Browse Source

增加点位管理和巡视方案的界面

master
douyage 1 month ago
parent
commit
8ffa5885de
33 changed files with 11449 additions and 3 deletions
  1. BIN
      public/plugins/jessibucaPlayer/bg.jpg
  2. +31
    -0
      src/api/basedata/area/area.js
  3. +23
    -0
      src/api/basedata/patrolpointmnt/patrolpoint.js
  4. BIN
      src/assets/images/pay.png
  5. BIN
      src/assets/images/r1.png
  6. BIN
      src/assets/images/r2.png
  7. BIN
      src/assets/images/r3.png
  8. BIN
      src/assets/images/r4.png
  9. BIN
      src/assets/images/r5.png
  10. BIN
      src/assets/images/r6.png
  11. BIN
      src/assets/images/r7.png
  12. BIN
      src/assets/images/r8.png
  13. BIN
      src/assets/images/renwuzhanbi.png
  14. BIN
      src/assets/images/yuntai1.png
  15. +383
    -0
      src/components/ImagePreviewDialog/index.vue
  16. +1
    -0
      src/layout/components/Sidebar/Item.vue
  17. +3
    -0
      src/layout/components/Sidebar/SidebarItem.vue
  18. +1
    -0
      src/plugins/modal.js
  19. +32
    -0
      src/router/index.js
  20. +42
    -0
      src/utils/index.js
  21. BIN
      src/views/cards/assets/card_full_bg.png
  22. BIN
      src/views/cards/assets/collect.png
  23. +173
    -0
      src/views/cards/assets/styles/index.scss
  24. +717
    -0
      src/views/cards/patrolPlan/notstart.vue
  25. +1536
    -0
      src/views/cards/patrolPlan/summary_editTask.vue
  26. +1442
    -0
      src/views/cards/patrolPlan/summary_newTask.vue
  27. +607
    -0
      src/views/cards/site_management/algorithm.vue
  28. +534
    -0
      src/views/cards/site_management/components/ImageAnnotation.vue
  29. +837
    -0
      src/views/cards/site_management/components/chooseAlg.vue
  30. +222
    -0
      src/views/cards/site_management/components/chooseDevices.vue
  31. +171
    -0
      src/views/cards/site_management/components/uploadDialog.vue
  32. +4686
    -0
      src/views/cards/site_management/site_management.vue
  33. +8
    -3
      src/views/cards/taskSummary/inspectionTask.vue

BIN
public/plugins/jessibucaPlayer/bg.jpg View File

Before After
Width: 1000  |  Height: 599  |  Size: 145 KiB

+ 31
- 0
src/api/basedata/area/area.js View File

@ -68,6 +68,14 @@ export function editSave (data) {
data: data
})
}
// 修改巡视任务(绍兴)
export function shaoxingEditSave (data) {
return request({
url: '/patrol/task/shaoxing/save',
method: 'post',
data: data
})
}
//主设备下巡检点和巡检点巡检设备树(巡视任务回显)
export function editList (query) {
return request({
@ -83,6 +91,13 @@ export function editTask (taskId) {
method: 'get',
})
}
// 巡视任务编辑回显(绍兴)
export function editTaskShaoxing (taskId) {
return request({
url: '/patrol/task/shaoxing/' + taskId,
method: 'get',
})
}
// 复制巡检任务
export function copyTask (query) {
return request({
@ -136,6 +151,14 @@ export function addPatrol (data) {
data: data
})
}
// 绍兴站新增/修改巡视任务
export function shaoxingAddPatrol (data) {
return request({
url: '/patrol/task/shaoxing/add',
method: 'post',
data: data
})
}
// 巡视任务管理列表
export function taskPatrol (query) {
return request({
@ -659,3 +682,11 @@ export function downAreas (ids) {
}
})
}
export function proDotManTableRelation (params) {
return request({
url: '/basedata/patrolpointmnt/patrolpoint/list/monitor/relation',
method: 'get',
params: params
})
}

+ 23
- 0
src/api/basedata/patrolpointmnt/patrolpoint.js View File

@ -149,4 +149,27 @@ export function getBdconfigMainsystem() {
}
// 获取点位管理右侧设备树
export function treeAreaDeviceSelectFilterByPresetRight(query) {
return request({
url: '/basedata/prodevmnt/device/treeAreaDeviceSelectFilterByPreset/relation/right',
method: 'get',
params: query
})
}
export function treeAreaDeviceSelectFilterByPreset() {
return request({
url: '/basedata/prodevmnt/device/treeAreaDeviceSelectFilterByPreset',
method: 'get'
})
}
export function treeAreaDeviceSelectFilterByPresetRelation() {
return request({
url: '/basedata/prodevmnt/device/treeAreaDeviceSelectFilterByPreset/relation',
method: 'get'
})
}

BIN
src/assets/images/pay.png View File

Before After
Width: 900  |  Height: 539  |  Size: 137 KiB

BIN
src/assets/images/r1.png View File

Before After
Width: 55  |  Height: 24  |  Size: 1.2 KiB Width: 55  |  Height: 24  |  Size: 1.5 KiB

BIN
src/assets/images/r2.png View File

Before After
Width: 55  |  Height: 24  |  Size: 1.5 KiB Width: 55  |  Height: 24  |  Size: 1.6 KiB

BIN
src/assets/images/r3.png View File

Before After
Width: 55  |  Height: 24  |  Size: 1.6 KiB Width: 55  |  Height: 24  |  Size: 1.6 KiB

BIN
src/assets/images/r4.png View File

Before After
Width: 55  |  Height: 24  |  Size: 1.5 KiB Width: 55  |  Height: 24  |  Size: 1.4 KiB

BIN
src/assets/images/r5.png View File

Before After
Width: 55  |  Height: 24  |  Size: 1.9 KiB Width: 55  |  Height: 24  |  Size: 2.2 KiB

BIN
src/assets/images/r6.png View File

Before After
Width: 23  |  Height: 23  |  Size: 1.4 KiB Width: 24  |  Height: 24  |  Size: 1.0 KiB

BIN
src/assets/images/r7.png View File

Before After
Width: 23  |  Height: 23  |  Size: 1.4 KiB Width: 24  |  Height: 24  |  Size: 1.1 KiB

BIN
src/assets/images/r8.png View File

Before After
Width: 23  |  Height: 23  |  Size: 1.4 KiB Width: 22  |  Height: 24  |  Size: 1.1 KiB

BIN
src/assets/images/renwuzhanbi.png View File

Before After
Width: 32  |  Height: 32  |  Size: 3.0 KiB

BIN
src/assets/images/yuntai1.png View File

Before After
Width: 334  |  Height: 334  |  Size: 24 KiB Width: 332  |  Height: 332  |  Size: 16 KiB

+ 383
- 0
src/components/ImagePreviewDialog/index.vue View File

@ -0,0 +1,383 @@
<template>
<el-dialog
:title="title"
:visible.sync="visible"
:close-on-click-modal="false"
:close-on-press-escape="false"
:destroy-on-close="true"
:before-close="handleClose"
width="70%"
append-to-body
>
<div class="flex-row" v-loading="loading">
<div style="flex: 1; height: 100%; display: flex; flex-direction: column">
<div class="img-wrap">
<AsyncImage
style="width: 100%; height: 100%"
fit="contain"
:src="selectImgUrl"
></AsyncImage>
<DrawShape
ref="drawShapeRef"
:brokenLineCount="1"
@drawComplete="
(shape) => {
handleDrawComplete(shape);
}
"
/>
<!-- 上一个图片 -->
<div
v-if="bShowBackBtn"
@click="() => selectIndex--"
class="img-handle-btn img-handle-btn-left"
>
<i class="el-icon-arrow-left"></i>
</div>
<!-- 下一个图片 -->
<div
v-if="bShowNextBtn"
@click="() => selectIndex++"
class="img-handle-btn img-handle-btn-right"
>
<i class="el-icon-arrow-right"></i>
</div>
</div>
<!-- 开始测温 -->
<div
v-if="showTemperatureTool && imageUrls.length > 0"
class="measure-temperature-btn"
>
<div>
<el-button
@click="measureTemperature(ShapeEnum.point)"
type="primary"
>点测温</el-button
>
<el-button
@click="measureTemperature(ShapeEnum.brokenline)"
type="primary"
>线测温</el-button
>
<el-button
@click="measureTemperature(ShapeEnum.rect)"
type="primary"
>区域测温</el-button
>
<el-button
v-if="measuring"
@click="cancelMeasureTemperature"
type="primary"
>取消测温</el-button
>
</div>
<div style="flex: 1; text-align: right">
<span style="padding-left: 10px"
>平均温度{{
temperatureInfo.averageT ? temperatureInfo.averageT + "℃" : "-"
}}</span
>
<span style="padding-left: 10px"
>最大温度{{
temperatureInfo.maxT ? temperatureInfo.maxT + "℃" : "-"
}}</span
>
<span style="padding-left: 10px"
>最小温度{{
temperatureInfo.maxT ? temperatureInfo.minT + "℃" : "-"
}}</span
>
<span style="padding-left: 10px"
>环境温度{{
temperatureInfo.weatherT ? temperatureInfo.weatherT + "℃" : "-"
}}</span
>
</div>
</div>
</div>
<div class="img-list-wrap" v-if="imageUrls.length > 1">
<ul ref="imgUlRef">
<li
v-for="(item, index) in imageUrls"
:key="index"
@click="onSelectImage(item, index)"
>
<div
:style="imgListItemStyle"
:class="{ 'img-list-item-selected': index === selectIndex }"
>
<AsyncImage
style="width: 100%; height: 100%"
fit="contain"
:src="item"
></AsyncImage>
</div>
</li>
</ul>
</div>
</div>
</el-dialog>
</template>
<script>
import { getTemperatureValue } from "@/api/device";
import DrawShape, { ShapeEnum } from "../Canvas/DrawShape.vue";
// item
const IMG_LIST_ITEM_HEIGHT = 170;
export default {
name: "ImagePreviewDialog",
components: { DrawShape },
props: {
//
title: {
type: String,
default: "图片",
},
//
visible: {
type: Boolean,
default: false,
},
//
showTemperatureTool: {
type: Boolean,
default: false,
},
//
imageUrls: {
type: Array,
default: [],
},
//
selectImgPosition: {
type: Number,
default: 1,
},
},
data() {
return {
// url
selectIndex: 0,
// item使便js
imgListItemStyle: {
width: "300px",
height: `${IMG_LIST_ITEM_HEIGHT}px`,
margin: "5px 0",
},
ShapeEnum: ShapeEnum,
//
measuring: false,
temperatureInfo: {},
loading: false,
};
},
computed: {
selectImgUrl() {
return this.imageUrls.length > this.selectIndex
? this.imageUrls[this.selectIndex]
: "";
},
//
bShowNextBtn() {
return this.selectIndex + 1 < this.imageUrls.length;
},
//
bShowBackBtn() {
return this.selectIndex > 0;
},
},
watch: {
visible(value) {
//
if (value) {
this.reset();
}
},
selectImgPosition() {
if (this.selectImgPosition < this.imageUrls.length) {
this.selectIndex = this.selectImgPosition;
}
},
selectIndex(newValue) {
//
if (this.$refs.imgUlRef) {
this.$refs.imgUlRef.scrollTo({
top: IMG_LIST_ITEM_HEIGHT * newValue,
behavior: "smooth",
});
}
},
},
methods: {
reset() {
this.selectIndex = 0;
this.hideMeasureBox();
},
handleClose(done) {
this.$emit("update:visible", false);
},
//
onSelectImage(item, index) {
this.selectIndex = index;
},
//
showMeasureBox() {
if (this.$refs.drawShapeRef) {
this.temperatureInfo = {};
this.measuring = true;
this.$refs.drawShapeRef.show();
}
},
//
hideMeasureBox() {
this.measuring = false;
this.temperatureInfo = {};
if (this.$refs.drawShapeRef) {
this.$refs.drawShapeRef.hide();
}
},
//
measureTemperature(type) {
this.showMeasureBox();
this.$nextTick(() => {
this.$refs.drawShapeRef.init();
this.$refs.drawShapeRef.startDraw(type);
});
},
cancelMeasureTemperature() {
this.hideMeasureBox();
},
handleDrawComplete(shape) {
const info = shape.getData();
this.loading = true;
getTemperatureValue(info.data, info.type, this.selectImgUrl)
.then((res) => {
this.temperatureInfo = res.data;
})
.finally(() => {
this.loading = false;
});
},
},
};
</script>
<style lang="scss" scoped>
::v-deep .el-dialog {
height: 80%;
display: flex;
flex-direction: column;
}
::v-deep .el-dialog__header {
background-color: #001946;
color: #fff;
.el-dialog__headerbtn .el-dialog__close {
color: #fff;
}
.el-dialog__title {
color: #fff;
}
}
::v-deep .el-dialog__body {
background-color: #001946;
padding: 0px 20px 20px;
flex: 1;
//
height: 0;
}
.flex-row {
display: flex;
height: 100%;
}
.img-wrap {
flex: 1;
// height: 100%;
height: 0;
position: relative;
.img-handle-btn {
position: absolute;
top: 45%;
color: rgba(255, 255, 255, 0.5);
font-size: 25px;
width: 50px;
height: 50px;
border: rgba(255, 255, 255, 0.5) solid 2px;
border-radius: 25px;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
}
.img-handle-btn-left {
left: 15px;
}
.img-handle-btn-right {
right: 15px;
}
}
.measure-temperature-btn {
padding: 10px 0;
display: flex;
}
.img-list-wrap {
width: 300px;
height: 100%;
padding: 0 0 0 10px;
ul {
width: 100%;
height: 100%;
padding: 0;
overflow-x: hidden;
overflow-y: auto;
margin: 0;
/* 解决火狐浏览器不隐藏滚动条的问题 */
scrollbar-width: none;
.img-list-item {
width: 300px;
height: 170px;
margin: 5px 0;
}
.img-list-item-selected {
border: #1d86e1 solid 3px;
}
}
}
/* 设置滚动条的样式 */
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
/* 滚动槽 */
::-webkit-scrollbar-track {
box-shadow: inset006pxrgba(0, 0, 0, 0.3);
border-radius: 10px;
}
/* 滚动条滑块 */
::-webkit-scrollbar-thumb {
border-radius: 10px;
background: rgba(64, 229, 240, 0.15);
box-shadow: inset006pxrgba(0, 0, 0, 0.5);
}
</style>

+ 1
- 0
src/layout/components/Sidebar/Item.vue View File

@ -10,6 +10,7 @@ const navIconMap = {
'巡视任务': require('@/assets/images/nav/task.png'),
'设备管理': require('@/assets/images/nav/device.png'),
'点位管理': require('@/assets/images/nav/point.png'),
'点位管理(实施用)': require('@/assets/images/nav/point.png'),
'系统管理': require('@/assets/images/nav/system.png'),
'区域管理': require('@/assets/images/nav/area.png'),
'系统监控': require('@/assets/images/nav/monitor.png'),


+ 3
- 0
src/layout/components/Sidebar/SidebarItem.vue View File

@ -159,6 +159,9 @@ export default {
str = "首页";
}
}
if (str === "点位管理" && this.basePath !== "/site-management") {
str = "点位管理(实施用)";
}
return str;
}
},


+ 1
- 0
src/plugins/modal.js View File

@ -57,6 +57,7 @@ export default {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: "warning",
customClass: "card-page card-message-box",
})
},
// 提交内容


+ 32
- 0
src/router/index.js View File

@ -101,6 +101,38 @@ export const constantRoutes = [
meta: { title: '巡视任务', icon: 'task', activeMenu: '/task-summary' }
}
]
},
{
path: '/patrol-plan',
component: Layout,
redirect: '/patrol-plan/notstart',
meta: { title: '巡视方案', icon: 'task' },
children: [
{
path: 'notstart',
component: () => import('@/views/cards/patrolPlan/notstart'),
name: 'PatrolPlan',
meta: { title: '巡视方案', icon: 'task', activeMenu: '/patrol-plan' }
}
]
},
{
path: '/site-management',
component: Layout,
redirect: '/site-management/index',
meta: { title: '点位管理', icon: 'point' },
children: [
{
path: 'index',
component: () => import('@/views/cards/site_management/site_management'),
name: 'SiteManagementCard',
meta: {
title: '点位管理',
icon: 'point',
activeMenu: '/site-management'
}
}
]
}
]


+ 42
- 0
src/utils/index.js View File

@ -388,3 +388,45 @@ export function isNumberStr(str) {
return /^[+-]?(0|([1-9]\d*))(\.\d+)?$/g.test(str)
}
export function getWeekdaysInRange(startDate, endDate) {
const start = new Date(startDate)
const end = new Date(endDate)
const days = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六']
const totalDays = Math.ceil((end - start) / (1000 * 60 * 60 * 24))
if (totalDays >= 7) {
return days
}
const result = []
const current = new Date(start)
while (current <= end) {
result.push(days[current.getDay()])
current.setDate(current.getDate() + 1)
}
return result
}
export function getDaysInRange(startDate, endDate) {
const start = new Date(startDate)
const end = new Date(endDate)
const totalDays = Math.ceil((end - start) / (1000 * 60 * 60 * 24))
if (totalDays > 365) {
return Array.from({ length: 31 }, (_, index) => `${index + 1}`)
}
const result = []
const current = new Date(start)
while (current <= end) {
const dayOfMonth = current.getDate()
if (!result.includes(dayOfMonth)) {
result.push(`${dayOfMonth}`)
}
current.setDate(current.getDate() + 1)
}
return result
}

BIN
src/views/cards/assets/card_full_bg.png View File

Before After
Width: 1862  |  Height: 47  |  Size: 47 KiB

BIN
src/views/cards/assets/collect.png View File

Before After
Width: 17  |  Height: 100  |  Size: 998 B

+ 173
- 0
src/views/cards/assets/styles/index.scss View File

@ -546,6 +546,159 @@
}
}
.site-point-add-dialog {
height: 92vh;
max-height: 92vh;
margin-top: 4vh;
display: flex;
flex-direction: column;
border: 0;
box-shadow: none;
.el-dialog__body {
flex: 1 1 auto;
min-height: 0;
height: auto;
max-height: calc(92vh - 135px);
overflow-y: auto;
}
.el-dialog__footer {
flex-shrink: 0;
border-top: 0;
}
.site-point-cancel-btn {
border: 1px solid #3988ff !important;
background: #174d9b !important;
color: #43c0ff !important;
&:hover,
&:focus {
border-color: #75c9ff !important;
background: #256fcc !important;
color: #ffffff !important;
}
}
}
.monitor-config-dialog {
height: 88vh;
max-height: 88vh;
margin-bottom: 0;
display: flex;
flex-direction: column;
&,
* {
scrollbar-width: none;
-ms-overflow-style: none;
}
&::-webkit-scrollbar,
*::-webkit-scrollbar {
width: 0;
height: 0;
display: none;
}
.el-dialog__body {
height: calc(88vh - 135px) !important;
flex: 1 1 auto;
overflow: auto;
padding: 24px;
box-sizing: border-box;
}
.horizontal-tabs-container {
min-height: 650px;
}
.el-dialog__footer {
flex-shrink: 0;
}
.el-button {
border: 1px solid #3988ff !important;
background: rgba(3, 81, 198, 0.78) !important;
color: #43c0ff !important;
&:hover,
&:focus {
border-color: #43c0ff !important;
background: rgba(57, 136, 255, 0.86) !important;
color: #ffffff !important;
}
&.is-disabled,
&.is-disabled:hover,
&.is-disabled:focus {
border-color: rgba(57, 136, 255, 0.35) !important;
background: rgba(3, 81, 198, 0.28) !important;
color: rgba(67, 192, 255, 0.45) !important;
}
}
.el-button--text {
border-color: transparent !important;
background: transparent !important;
color: #43c0ff !important;
&:hover,
&:focus {
background: rgba(57, 136, 255, 0.14) !important;
color: #43c0ff !important;
}
}
}
.monitor-config-popover {
&,
* {
scrollbar-width: none;
-ms-overflow-style: none;
}
&::-webkit-scrollbar,
*::-webkit-scrollbar {
width: 0;
height: 0;
display: none;
}
.el-button {
border: 1px solid #3988ff !important;
background: rgba(3, 81, 198, 0.78) !important;
color: #43c0ff !important;
&:hover,
&:focus {
border-color: #43c0ff !important;
background: rgba(57, 136, 255, 0.86) !important;
color: #ffffff !important;
}
}
}
.bind-thermal-dialog .bind-thermal-secondary-btn {
border: 1px solid transparent !important;
background:
linear-gradient(#1f6fe8, #2f8df2) padding-box,
linear-gradient(135deg, #75c9ff, #1c5fd8) border-box !important;
color: #ffffff !important;
box-shadow: 0 0 0 1px rgba(117, 201, 255, 0.35);
&:hover,
&:focus {
border-color: transparent !important;
background:
linear-gradient(#2f8df2, #4aa3ff) padding-box,
linear-gradient(135deg, #9cdbff, #3988ff) border-box !important;
color: #ffffff !important;
box-shadow: 0 0 0 1px rgba(156, 219, 255, 0.55);
}
}
.card-message-box {
border: none;
padding-bottom: 0;
@ -632,6 +785,26 @@
}
}
.el-button,
.el-button--primary {
border: 1px solid transparent !important;
background:
linear-gradient(#1f6fe8, #2f8df2) padding-box,
linear-gradient(135deg, #75c9ff, #1c5fd8) border-box !important;
color: #ffffff !important;
box-shadow: 0 0 0 1px rgba(117, 201, 255, 0.35);
&:hover,
&:focus {
border-color: transparent !important;
background:
linear-gradient(#2f8df2, #4aa3ff) padding-box,
linear-gradient(135deg, #9cdbff, #3988ff) border-box !important;
color: #ffffff !important;
box-shadow: 0 0 0 1px rgba(156, 219, 255, 0.55);
}
}
}
}


+ 717
- 0
src/views/cards/patrolPlan/notstart.vue View File

@ -0,0 +1,717 @@
<template>
<CardBox
class="card-page"
:showTitle="false"
>
<div class="page-container">
<el-form
:model="queryParams"
ref="queryForm"
:inline="true"
label-width="68px"
>
<el-form-item label="方案名称" prop="taskName">
<el-input
v-model="queryParams.taskName"
placeholder="请输入方案名称"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="区域名称" prop="areaName">
<el-select
v-model="queryParams.areaName"
filterable
clearable
placeholder="请选择区域"
@change="handleQuery"
>
<el-option
v-for="(item, index) in areaList"
:key="index"
:label="item.dictLabel"
:value="item.dictValue"
>
</el-option>
</el-select>
</el-form-item>
<!-- <el-form-item label="区域名称" prop="areaName">
<el-input
v-model="queryParams.areaName"
placeholder="请输入区域名称"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item> -->
<!--
<el-form-item label="状态">
<el-select v-model="queryParams.isenable" placeholder="请选择" clearable>
<el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="方案有效期" label-width="100px">
<el-date-picker v-model="dateRange" size="small" style="width: 240px" value-format="yyyy-MM-dd"
type="daterange" :picker-options="dateRangePickerOptions" range-separator="-" start-placeholder="开始日期"
end-placeholder="结束日期" :clearable="true"></el-date-picker>
</el-form-item>
<el-form-item label="创建时间">
<el-date-picker v-model="dateRange2" size="small" style="width: 240px" value-format="yyyy-MM-dd"
type="daterange" :picker-options="dateRangePickerOptions2" range-separator="-" start-placeholder="开始日期"
end-placeholder="结束日期" :clearable="true"></el-date-picker>
</el-form-item> -->
<el-form-item>
<el-button
class="task-toolbar-btn"
type="primary"
icon="el-icon-search"
size="mini"
@click="handleQuery"
>搜索</el-button
>
<el-button
class="task-toolbar-btn"
type="primary"
icon="el-icon-plus"
size="mini"
@click="linkUrl"
>添加</el-button
>
<!-- <el-button icon="el-icon-refresh" size="mini" @click="resetQuery"
>重置</el-button
> -->
</el-form-item>
</el-form>
<div class="table-card" ref="tableCardRef">
<el-table
v-loading="loading"
:data="dataList"
class="card-table"
ref="tableRef"
border
stripe
:height="tableHeight"
>
<el-table-column
label="序号"
type="index"
width="60"
align="center"
:index="table_index"
/>
<el-table-column label="方案名称" align="left" prop="taskName" width="700" header-align="center" />
<el-table-column label="点位数量" align="center" prop="totalPoints" />
<el-table-column label="创建时间" align="center" prop="createTime" />
<el-table-column label="区域名称" align="center" prop="areaName" />
<el-table-column
label="任务策略"
align="center"
prop="executionStatus"
:formatter="stateFormat"
>
</el-table-column>
<!-- <el-table-column
label="执行任务周期"
align="center"
prop="taskCycle"
:formatter="stateTask"
/> -->
<el-table-column label="操作" align="center">
<template slot-scope="scope">
<el-tooltip content="立即执行" placement="bottom">
<i
class="el-icon-finished fss"
@click="controlChange(scope.row)"
></i>
</el-tooltip>
<el-tooltip content="编辑" placement="bottom">
<i class="el-icon-edit fss" @click="editClick(scope.row)"></i>
</el-tooltip>
<el-tooltip content="删除" placement="bottom">
<i class="el-icon-delete fss" @click="delClick(scope.row)"></i>
</el-tooltip>
</template>
</el-table-column>
</el-table>
<CardPagination
v-show="total > 0"
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
</div>
<el-dialog
title="新建方案"
:close-on-click-modal="false"
:close-on-press-escape="false"
:destroy-on-close="true"
:visible.sync="addShow"
width="1800px"
custom-class="card-dialog card-page patrol-plan-dialog"
>
<summaryNewTask
@hideDialog="addShow = false"
v-if="addShow"
@addSuccess="addSuccess"
>
</summaryNewTask>
</el-dialog>
<el-dialog
title="编辑任务"
:close-on-click-modal="false"
:close-on-press-escape="false"
:destroy-on-close="true"
:visible.sync="editShow"
width="1800px"
custom-class="card-dialog card-page patrol-plan-dialog"
>
<summaryEditTask
@hideDialog="editShow = false"
:editid="editid"
@editSuccess="editSuccess"
v-if="editShow"
>
</summaryEditTask>
</el-dialog>
</div>
</CardBox>
</template>
<script>
import {
taskPatrol,
changeStatus,
delTask,
controlChange,
} from "@/api/basedata/area/area";
import { listData } from "@/api/system/dict/data";
import summaryNewTask from "./summary_newTask"
import summaryEditTask from "./summary_editTask"
import debounce from "lodash.debounce";
import CardBox from "../components/cardBox.vue";
import CardPagination from "../components/CardPagination/index.vue";
export default {
name: "InspectionReporter",
dicts: ["patrol_type", "device_type"],
components: { summaryNewTask, summaryEditTask, CardBox, CardPagination },
data() {
return {
editShow: false,
editid: "",
addShow: false,
options: [
{
value: "0",
label: "开启",
},
{
value: "1",
label: "不开启",
},
],
//
loading: true,
//
ids: [],
//
single: true,
//
multiple: true,
//
total: 0,
//
dataList: [],
//
dateRange: [],
dateRangePickerOptions: {
disabledDate(selectDate) {
return new Date().getTime() < selectDate.getTime();
},
},
dateRange2: [],
dateRangePickerOptions2: {
disabledDate(selectDate) {
return new Date().getTime() < selectDate.getTime();
},
},
//
queryParams: {
pageNum: 1,
pageSize: 10,
taskName: null,
areaName: null,
isenable: null,
},
//
showReport: false,
//
reportInfo: {},
// table
tableHeight: null,
areaList: [
],
};
},
created() {
//
this.setTableHeight = debounce(this.setTableHeight, 300);
window.addEventListener("resize", this.setTableHeight);
this.getList();
window.addEventListener("message", (e) => {
let datasList = JSON.parse(e.data);
if (datasList.id == "editTask") {
this.getList();
}
});
},
mounted() {
this.setTableHeight();
this.getAreaList()
},
destroyed() {
this.setTableHeight.cancel();
window.removeEventListener("resize", this.setTableHeight);
},
methods: {
//
getAreaList() {
var data = {"dictType":"sys_area_list"}
listData(data).then((res) => {
this.areaList = res.rows;
});
},
table_index(index) {
return (
(this.queryParams.pageNum - 1) * this.queryParams.pageSize + index + 1
);
},
delClick(row) {
let taskIds = row.taskId;
this.$modal
.confirm('是否确认删除方案名称为"' + row.taskName + '"的数据项?')
.then(function () {
return delTask(taskIds);
})
.then(() => {
this.getList();
this.$modal.msgSuccess("删除成功");
})
.catch(() => {});
},
linkUrl() {
this.addShow = true;
},
addSuccess() {
this.addShow = false;
this.$modal.msgSuccess("添加成功");
this.getList();
},
editSuccess() {
this.editShow = false;
this.getList();
},
changeSwitch(row) {
const data = {
taskId: row.taskId,
isenable: row.isenable,
};
changeStatus(data).then((response) => {
if (response.code == 200) {
this.getList();
}
});
},
controlChange(row) {
let result = 1;
let that = this;
this.loading = true;
controlChange(row.taskId)
.then((res) => {
sessionStorage.setItem("ShuanXing", JSON.stringify(result));
that.getList();
this.$modal.msgSuccess("指令下发成功");
//
var data = {
id: "notstart",
sourceId: "",
targetId: "main_frame_global_modal", // ID
event: {
type: "PING",
data: {},
},
tm: new Date().getTime(), //
};
window.top.postMessage(JSON.stringify(data), "*");
})
.finally(() => {
this.loading = false;
});
},
stateFormat(row, column) {
if (row.executionStatus == 0) {
return "立即执行";
} else if (row.executionStatus == 1) {
return "周期执行";
} else if (row.executionStatus == 2) {
return "定时执行";
} else if (row.executionStatus == 3) {
return "已执行";
} else if (row.executionStatus == 4) {
return "间隔执行";
}
},
stateTask(row, column) {
if (row.executionStatus == 1) {
// if (row.taskCycle == 0) {
// // return "";
// } else if (row.taskCycle == 1) {
// return "";
// }
return "";
}
},
setTableHeight() {
if (!this.$refs.tableCardRef) {
return;
}
this.tableHeight = Math.max(this.$refs.tableCardRef.clientHeight - 48, 160);
},
/** 获取数据列表 */
getList() {
this.loading = true;
// const params = {};
// for (const key in this.queryParams) {
// if (Object.hasOwnProperty.call(this.queryParams, key)) {
// const element = this.queryParams[key];
// if (element) {
// params[key] = element;
// }
// }
// }
if (this.dateRange && this.dateRange.length === 2) {
params.cycleStartTime = this.dateRange[0];
params.cycleEndTime = this.dateRange[1];
}
if (this.dateRange2 && this.dateRange.length2 === 2) {
params.beginTime = this.dateRange2[0];
params.endTime = this.dateRange2[1];
}
taskPatrol(this.queryParams).then((response) => {
this.dataList = response.rows;
this.total = response.total;
this.loading = false;
});
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.dateRange = [];
this.resetForm("queryForm");
this.handleQuery();
},
//
handleSelectionChange(selection) {
this.ids = selection.map((item) => item.id);
this.single = selection.length !== 1;
this.multiple = !selection.length;
},
editClick(row) {
this.editid = row.taskId;
sessionStorage.setItem("reportLineId", JSON.stringify(this.editid));
this.editShow = true;
// this.$router.push({ path: "/edit_task", query: { taskId: row.taskId } });
// this.$router.push({name:'edit_task',params:{taskId:row.taskId}});
},
},
};
</script>
<style lang="scss" scoped>
.JiShu {
background-color: RGBA(23, 37, 51, 1);
}
.OuShu {
background-color: RGBA(32, 47, 62, 1);
}
.page-container {
height: 100%;
display: flex;
flex-direction: column;
position: relative;
overflow: hidden;
min-height: 0;
}
.card-page {
height: 100%;
overflow: hidden;
}
.fss {
font-size: 20px;
margin: 0 15px;
cursor: pointer;
}
.table-card {
flex: 1;
height: 0;
min-height: 0;
overflow: hidden;
::v-deep .el-card__body {
height: 100%;
}
}
.card-page .el-table td .cell {
color: #000000;
}
.card-page ::v-deep .card-table,
.card-page ::v-deep .el-table {
width: 100% !important;
background: transparent !important;
color: #000000;
border-color: rgba(64, 146, 255, 0.95) !important;
}
.card-page ::v-deep .el-table::before,
.card-page ::v-deep .el-table::after,
.card-page ::v-deep .el-table--border::after,
.card-page ::v-deep .el-table--group::after {
background-color: rgba(64, 146, 255, 0.95) !important;
}
.card-page ::v-deep .el-table__header-wrapper,
.card-page ::v-deep .el-table__fixed-header-wrapper {
background: #dbe7fb !important;
}
.card-page ::v-deep .el-table__body-wrapper,
.card-page ::v-deep .el-table__fixed,
.card-page ::v-deep .el-table__fixed-right,
.card-page ::v-deep .el-table__empty-block {
background: transparent !important;
background-color: transparent !important;
}
.card-page ::v-deep .el-table th.el-table__cell {
background: #06347f !important;
border-color: rgba(64, 146, 255, 0.95) !important;
}
.card-page ::v-deep .el-table th.el-table__cell .cell {
color: #ffffff !important;
}
.card-page ::v-deep .el-table td.el-table__cell {
background: transparent !important;
border-color: #c7d7f4 !important;
color: #000000 !important;
}
.card-page ::v-deep .el-table td.el-table__cell .cell {
color: #000000 !important;
font-weight: 500;
}
.card-page ::v-deep .el-table__row {
background: #e4edfc !important;
}
.card-page ::v-deep .el-table__row--striped td.el-table__cell {
background: #d7e4f8 !important;
}
.card-page ::v-deep .el-table__body tr:hover > td.el-table__cell {
background: #c5d9f6 !important;
}
.card-page ::v-deep .el-table__body-wrapper {
overflow-y: auto;
overflow-x: hidden;
scrollbar-width: none;
-ms-overflow-style: none;
}
.card-page ::v-deep .el-table__body-wrapper::-webkit-scrollbar {
width: 0;
height: 0;
display: none;
}
.task-toolbar-btn {
border: 1px solid transparent !important;
background:
linear-gradient(#01016b, #01016b) padding-box,
linear-gradient(135deg, #3988ff, #0351c6) border-box !important;
color: #43c0ff !important;
}
.task-toolbar-btn:hover,
.task-toolbar-btn:focus {
border: 1px solid transparent !important;
background:
linear-gradient(#01016b, #01016b) padding-box,
linear-gradient(135deg, #3988ff, #0351c6) border-box !important;
color: #43c0ff !important;
opacity: 0.9;
}
::v-deep .patrol-plan-dialog .el-dialog__body {
height: auto !important;
max-height: calc(100vh - 160px);
padding: 0 24px 24px !important;
overflow-y: auto !important;
}
.table1 {
width: 100%;
border-collapse: collapse;
text-align: center;
table-layout: fixed;
}
.table1 > thead > tr > th {
width: 200px;
border: 1px solid RGBA(64, 72, 106, 1);
border-bottom: none;
background-color: RGBA(16, 29, 41, 1);
height: 38px;
}
.Tbody {
height: 400px;
overflow: scroll;
}
.table1 > tbody > tr > td {
width: 211px;
border: 1px solid RGBA(64, 72, 106, 1);
height: 45px;
}
.HeadXuHao {
width: 50px !important;
}
.TbodyXuHao {
width: 52px !important;
}
.HeadCaoZuo {
width: 160px !important;
}
.TbodyCaoZuo {
width: 169px !important;
}
.HeadChuanJian {
width: 300px !important;
}
.TbodyChuanJian {
width: 317px !important;
}
.el-scrollbar {
background-color: RGBA(32, 47, 62, 1) !important;
}
.table-box {
flex: 1;
height: 0;
overflow-y: auto;
}
</style>
<!--
<style lang="scss" scoped>
.title_img {
position: absolute;
top: 0px;
left: 0px;
z-index: 0;
width: 1848px;
}
.el-dialog__header {
background-color: #222f3d !important;
}
.el-dialog__body {
background-color: #222f3d !important;
}
.el-input__inner {
height: 28px !important;
line-height: 28px !important;
}
.el-select .el-input.is-focus .el-input__inner {
border-color: #1890ff !important;
}
.el-select-dropdown {
border: 1px solid RGBA(32, 47, 62, 1) !important;
}
.el-card.is-always-shadow {
border: none !important;
box-shadow: none !important;
}
.el-select-dropdown__list {
background-color: #202f3e !important;
border: 1px solid #40486a !important;
border-radius: 4px !important;
}
.el-select-dropdown__item.hover,
.el-select-dropdown__item:hover {
background-color: RGBA(23, 37, 51, 1) !important;
}
.el-form-item__label {
width: 82px !important;
}
.el-card__body {
padding: 0 0 15px 0 !important;
}
.content {
padding: 15px 15px 6px 15px !important;
}
.pagination-container {
margin-top: -2px !important;
}
.el-table {
height: 144px !important;
overflow: scroll !important;
}
.card-page .el-table::before {
background-color: rgb(0, 1, 1, 0) !important;
}
.el-table__header,
.el-table__body,
.el-table__footer {
border-left: 1px solid #40486a !important;
}
.el-table__header {
border-top: 1px solid #40486a !important;
border-left: 1px solid #40486a !important;
}
.el-table--border {
border: none;
}
.el-table__header-wrapper,
.el-table__body-wrapper,
.el-table__footer-wrapper {
border-right: 1px solid #40486a !important;
}
.el-table--border::after {
background-color: rgb(0, 1, 1, 0) !important;
}
</style> -->

+ 1536
- 0
src/views/cards/patrolPlan/summary_editTask.vue
File diff suppressed because it is too large
View File


+ 1442
- 0
src/views/cards/patrolPlan/summary_newTask.vue
File diff suppressed because it is too large
View File


+ 607
- 0
src/views/cards/site_management/algorithm.vue View File

@ -0,0 +1,607 @@
<template>
<div>
<div class="page-container">
<el-form
:model="queryParams"
ref="queryForm"
:inline="true"
label-width="75px"
>
<el-form-item label="算法类型" prop="taskType">
<el-select
style="width: 150px;"
v-model="queryParams.taskType"
placeholder="全部"
filterable
clearable
popper-class="card-select-dropdown"
>
<el-option
v-for="item in listPatroltype"
:key="item.algId"
:label="item.algName"
:value="item.algName"
/>
</el-select>
</el-form-item>
<el-form-item label="算法名称" prop="devType">
<el-select
style="width: 150px;"
v-model="queryParams.devType"
placeholder="全部"
filterable
clearable
popper-class="card-select-dropdown"
>
<el-option label="表记识别算法" value="0" />
<el-option label="机器人" value="1" />
<el-option label="视频" value="2" />
</el-select>
</el-form-item>
<el-form-item label="算法状态" prop="fileStatus">
<el-select
style="width: 150px;"
v-model="queryParams.fileStatus"
placeholder="全部"
filterable
clearable
popper-class="card-select-dropdown"
>
<el-option label="未归档" value="0" />
<el-option label="已归档" value="1" />
</el-select>
</el-form-item>
<el-form-item label="时间">
<el-date-picker
style="width: 200px;border-radius: 5px;border:1px"
v-model="timeValue"
type="date"
clearable
placeholder="选择时间"
value-format="yyyy-MM-dd HH:mm:ss"
:picker-options="pickerOptions0"
>
</el-date-picker>
</el-form-item>
<el-form-item>
<el-button
type="primary"
icon="el-icon-search"
size="mini"
@click="handleQuery"
>查询</el-button
>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery"
>重置</el-button
>
</el-form-item>
</el-form>
<div class="table-card" ref="tableCardRef">
<el-table
class="card-table algorithm-table"
v-loading="loading"
:data="dataList"
height="500"
border
stripe
>
<el-table-column
label="序号"
type="index"
width="50"
align="center"
/>
<el-table-column label="算法标识" align="center" prop="algSubtypeCode" />
<el-table-column label="算法名称" align="center" prop="algSubtypeName" />
<el-table-column label="算法类型" align="center" prop="algName" />
<!-- <el-table-column label="厂商" align="center" prop="createTime" >
<template slot-scope="scope">
<span>**********</span>
</template>
</el-table-column> -->
<el-table-column label="时间" align="center" prop="createTime" />
<el-table-column label="操作" align="center">
<template slot-scope="scope">
<!-- 只有未归档才能修正 -->
<el-button v-if="scope.row.delFlag !=1"
size="mini"
type="primary"
@click="archiveInfo(scope.row)"
>启用
</el-button>
<el-button v-else
style="background-color: #909399;"
size="mini"
type="info"
@click="lookDetail(scope.row)"
>停用</el-button
>
</template>
</el-table-column>
</el-table>
<CardPagination
v-show="total > 0"
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
</div>
</div>
<el-dialog
title="归档"
id="dialoga"
:visible.sync="showContrastEdit"
:close-on-click-modal="false"
:close-on-press-escape="false"
:destroy-on-close="true"
append-to-body
custom-class="card-dialog card-page card-dialog-height-auto"
>
<el-form
ref="contrastForm"
:model="contrastForm"
:rules="contrastRules"
label-width="100px"
label-position="top"
>
<el-form-item label="审核人" prop="chechkPerson">
<el-input
v-model="contrastForm.chechkPerson"
placeholder="请输入审核人"
/>
</el-form-item>
<el-form-item label="巡视结论" prop="taskResult">
<el-input
style="resize: none"
type="textarea"
:rows="3"
v-model="contrastForm.taskResult"
placeholder="请输入巡视结论"
/>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button
class="card-dialog-cancel-btn"
@click="() => (showContrastEdit = false)"
> </el-button
>
<el-button
type="primary"
class="card-dialog-sure-btn"
:loading="isArchiving"
:disable="isLock"
@click="handleArchiveClicked"
> </el-button
>
</div>
</el-dialog>
</div>
</template>
<script>
import {
listPartrolResult,
} from "@/api/algorithmConfiguration/index";
import {
partrolResultArchive,
} from "@/api/inspectionDataManage/inspectionArchive";
import debounce from "lodash.debounce";
import { listPatroltype } from "@/api/algorithmConfiguration/index";
import { openDialog } from "../common.js";
import CardPagination from "../components/CardPagination/index.vue";
import CardBox from "../components/cardBox.vue";
export default {
name: "InspectionArchive",
components: { CardPagination, CardBox },
dicts: [
"patrol_result_value_type",
"patrol_result_recognition_type",
"patrol_result_valid_type",
// "patrol_type",
"device_type",
],
data() {
return {
pickerOptions0: {
disabledDate(time) {
return time.getTime() < Date.now() - 8.64e7;
}
},
timeValue:'',
//
loading: true,
isArchiving: false, //
isLock: false,
//
total: 0,
//
dataList: [],
//
dateRange: [],
dateRangePickerOptions: {
disabledDate(selectDate) {
return new Date().getTime() < selectDate.getTime();
},
},
//
queryParams: {
pageNum: 1,
pageSize: 10,
taskType: null,
devType: null,
fileStatus: null,
},
//
showContrast: false,
//
showContrastEdit: false,
contrastForm: {
chechkPerson: null,
taskResult: null,
},
contrastRules: {
chechkPerson: [
{ required: true, message: "请输入审核人", trigger: "change" },
],
taskResult: [
{ required: true, message: "请输入巡视结论", trigger: "change" },
],
},
//
newInfo: null,
//
originalInfo: null,
listPatroltype: [],
};
},
created() {
//
this.setTableHeight = debounce(this.setTableHeight, 300);
window.addEventListener("resize", this.setTableHeight);
this.getlistPatroltype();
this.getList();
},
mounted() {
this.setTableHeight();
},
destroyed() {
this.setTableHeight.cancel();
window.removeEventListener("resize", this.setTableHeight);
},
methods: {
getlistPatroltype() {
listPatroltype().then((res) => {
this.listPatroltype = res.rows;
});
},
//
getPatrolTypeStr(type) {
const element = this.listPatroltype.find(
(item) => item.patrolTypeCode == type
);
if (element) {
return element.patrolTypeName;
}
return "";
},
setTableHeight() {
this.tableHeight = this.$refs.tableCardRef.clientHeight - 40;
},
/** 获取数据列表 */
getList() {
this.loading = true;
const params = { ...this.queryParams };
//
if (this.dateRange && this.dateRange.length === 2) {
params.beginTime = this.dateRange[0];
params.endTime = this.dateRange[1];
}
listPartrolResult(params).then((response) => {
this.dataList = response.rows;
this.total = response.total;
this.loading = false;
});
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.dateRange = [];
this.resetForm("queryForm");
this.handleQuery();
},
//
getDeviceTypeStr(dataType) {
switch (dataType) {
case "0":
return "无人机";
case "1":
return "机器人";
case "2":
return "视频";
}
},
//
archiveInfo(info) {
this.contrastForm = {
lineId: info.lineId,
chechkPerson: null,
taskResult: null,
};
this.resetForm("contrastForm");
this.showContrastEdit = true;
},
//
lookDetail(info) {
this.$router.push({
name: "cardInspectionArchivePointInfo",
params: {
id: info.lineId,
},
});
// //
// if (process.env.VUE_APP_DIALOG == "true") {
// this.$router.push({
// name: "cardInspectionArchivePointInfo",
// params: {
// id: info.lineId,
// },
// });
// } else {
// sessionStorage.setItem("archiveLineId", JSON.stringify(info.lineId));
// openDialog({
// sourceCardId: "wuhan@videoMonitor-card-45",
// targetCardId: "wuhan@videoMonitor-card-46",
// dialogTitle: "",
// });
// }
},
//
handleArchiveClicked() {
this.$refs["contrastForm"].validate((valid) => {
if (valid) {
const params = { ...this.contrastForm };
this.isArchiving = true;
this.isLock = true;
partrolResultArchive(params)
.then((res) => {
this.$modal.msgSuccess("归档成功");
this.showContrastEdit = false;
this.getList();
})
.finally(() => {
this.isArchiving = false;
this.isLock = false;
});
}
});
},
},
};
</script>
<style lang="scss" scoped>
.page-content {
width: 100%;
height: 856px;
}
.page-container {
height: 100%;
display: flex;
flex-direction: column;
}
.query-card {
margin-bottom: 15px;
::v-deep .el-form-item {
margin-bottom: 0;
}
}
.table-card {
flex: 1;
height: 0;
::v-deep .el-card__body {
height: 100%;
}
}
.algorithm-table {
background: #222f3d !important;
border-color: rgba(64, 146, 255, 0.95) !important;
color: #000000;
::v-deep .el-table__header-wrapper,
::v-deep .el-table__fixed-header-wrapper {
background: #06347f !important;
}
::v-deep .el-table__body-wrapper,
::v-deep .el-table__fixed,
::v-deep .el-table__fixed-right,
::v-deep .el-table__empty-block,
::v-deep .el-table__append-wrapper {
background: #222f3d !important;
}
::v-deep th.el-table__cell {
background: #06347f !important;
border-color: rgba(64, 146, 255, 0.95) !important;
color: #ffffff !important;
}
::v-deep th.el-table__cell .cell {
color: #ffffff !important;
}
::v-deep td.el-table__cell {
border-bottom: 1px solid #c7d7f4 !important;
border-right-color: #c7d7f4 !important;
color: #000000 !important;
}
::v-deep td.el-table__cell .cell {
color: #000000 !important;
font-weight: 500;
}
::v-deep .el-table__body tbody tr:nth-child(odd) td.el-table__cell {
background: #e4edfc !important;
}
::v-deep .el-table__body tbody tr:nth-child(even) td.el-table__cell,
::v-deep .el-table__row--striped td.el-table__cell {
background: #d7e4f8 !important;
}
::v-deep .el-table__body tr:hover > td.el-table__cell {
background: #c5d9f6 !important;
}
::v-deep .el-table__empty-text {
color: #5f7394 !important;
}
}
.algorithm-table::before,
.algorithm-table::after {
background-color: rgba(64, 146, 255, 0.95) !important;
}
// #dialoga {
// height: 39vh;
// overflow: auto;
// }
</style>
<style>
.vue-treeselect__control {
background-color: rgb(0, 1, 1, 0) !important;
border: 1px solid #40486a !important;
}
.el-tree-node__content:hover {
background: RGBA(23, 37, 51, 1) !important;
}
.el-tree-node:focus > .el-tree-node__content {
background-color: RGBA(23, 37, 51, 1) !important;
}
.el-picker-panel {
background: #202f3e !important;
border: 1px solid #40486a !important;
}
.el-picker-panel__footer {
background: #202f3e !important;
}
.el-date-picker__time-header {
border-bottom: 1px solid #40486a !important;
}
.el-date-table th {
border-bottom: 1px solid #40486a !important;
}
.el-picker-panel__footer {
border-top: 1px solid #40486a !important;
}
.el-popper[x-placement^="top"] .popper__arrow::after {
border-top-color: #40486a !important;
}
.el-time-panel__footer {
border-top: 1px solid #40486a !important;
}
.el-button--medium,
.el-button.is-plain {
background-color: #247382 !important;
border: 1px solid #247382 !important;
}
.el-time-panel {
background-color: #202f3e !important;
border: 1px solid #40486a !important;
}
.el-date-table td.disabled div {
background: RGBA(23, 37, 51, 1) !important;
}
.el-popper[x-placement^="bottom"] .popper__arrow::after {
border-bottom-color: #40486a !important;
}
.el-input-number--medium .el-input-number__increase {
background-color: rgb(1, 1, 1, 0) !important;
}
.el-transfer-panel {
background-color: #202f3e !important;
border-color: #40486a !important;
/* border: 1px solid red; */
width: 300px !important;
}
.el-transfer-panel__header {
background-color: #202f3e !important;
border-color: #40486a !important;
}
.el-step__head.is-finish {
color: #37cbd6 !important;
border-color: #37cbd6 !important;
}
.el-message-box {
background-color: #202f3e !important;
border: 1px solid #40486a !important;
}
.el-button--small {
background-color: #247382 !important;
border: none !important;
}
.el-step__title.is-finish {
color: #37cbd6 !important;
}
.el-input__inner {
border-color: #40486a !important;
}
.el-select-dropdown {
background-color: #202f3e !important;
border: 1px solid #40486a !important;
}
.el-select-dropdown__item.hover,
.el-select-dropdown__item:hover {
background-color: RGBA(23, 37, 51, 1) !important;
}
</style>

+ 534
- 0
src/views/cards/site_management/components/ImageAnnotation.vue View File

@ -0,0 +1,534 @@
<template>
<div class="image-annotation-container">
<!-- 控制面板 -->
<div class="control-panel">
<button @click="setMode('point')" :class="{ active: mode === 'point' }">
绘制点
</button>
<button @click="setMode('rect')" :class="{ active: mode === 'rect' }">
绘制矩形
</button>
<button @click="clearAll" class="clear-btn">清除所有</button>
<div class="config-options" v-if="mode">
<label v-if="mode === 'point'">
点大小:
<input type="range" v-model.number="pointSize" min="3" max="15" />
</label>
<label v-if="mode === 'rect'">
线宽:
<input type="range" v-model.number="lineWidth" min="1" max="10" />
</label>
<!-- <label>
颜色: <input type="color" v-model="drawColor">
</label> -->
</div>
</div>
<!-- 图片和画布容器 -->
<div class="image-wrapper" ref="imageWrapper">
<img
:src="imageSrc"
alt="标注图片"
ref="image"
@load="initCanvas"
@mousedown="handleMouseDown"
@mousemove="handleMouseMove"
@mouseup="handleMouseUp"
@mouseleave="handleMouseUp"
/>
<canvas ref="canvas"></canvas>
</div>
<!-- 标注信息面板 -->
<!-- <div class="annotation-panel">
<h3>标注信息 ({{ annotations.length }})</h3>
<div class="annotation-list">
<div
v-for="(annotation, index) in annotations"
:key="index"
class="annotation-item"
:class="annotation.type"
@click="highlightAnnotation(index)"
>
<div class="annotation-header">
<span class="annotation-type">
{{ annotation.type === 'point' ? '点' : '矩形' }} {{ index + 1 }}
</span>
<button @click.stop="removeAnnotation(index)" class="small-btn">
删除
</button>
</div>
</div>
</div>
</div> -->
</div>
</template>
<script>
export default {
name: "ImageAnnotation",
props: {
imageSrc: {
type: String,
required: true,
},
},
data() {
return {
mode: null, // : 'point' 'rect'
isDrawing: false, //
startX: 0, // X
startY: 0, // Y
annotations: [], //
drawColor: "red", //
pointSize: 6, //
lineWidth: 2, // 线
canvas: null, // canvas
ctx: null, // canvas
imgElement: null, // img
all_data: [],
};
},
watch: {
imageSrc() {
this.clearAll();
this.$nextTick(() => {
if (this.imgElement.complete) {
this.initCanvas();
}
});
},
},
mounted() {
// this.annotations = [];
// this.all_data = [];
this.imgElement = this.$refs.image;
this.canvas = this.$refs.canvas;
this.ctx = this.canvas.getContext("2d");
//
if (this.imgElement.complete) {
this.initCanvas();
}
},
methods: {
//
initCanvas() {
this.canvas.width = this.imgElement.width;
this.canvas.height = this.imgElement.height;
localStorage.setItem(
"imgElement",
JSON.stringify({
width: this.imgElement.width,
height: this.imgElement.height,
})
);
this.redrawAll();
},
//
setMode(mode) {
this.mode = mode;
this.imgElement.ondragstart = function () {
return false;
};
},
//
handleMouseDown(e) {
if (!this.mode || e.button !== 0) return; //
const rect = this.imgElement.getBoundingClientRect();
this.startX = e.clientX - rect.left;
this.startY = e.clientY - rect.top;
if (this.mode === "point") {
this.addPoint(this.startX, this.startY);
} else if (this.mode === "rect") {
this.isDrawing = true;
}
},
//
handleMouseMove(e) {
if (!this.isDrawing) return;
const rect = this.imgElement.getBoundingClientRect();
const currentX = e.clientX - rect.left;
const currentY = e.clientY - rect.top;
//
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
this.redrawAll();
//
this.drawRect(
this.startX,
this.startY,
currentX - this.startX,
currentY - this.startY,
this.drawColor,
this.lineWidth,
true //
);
},
//
handleMouseUp(e) {
if (!this.isDrawing) return;
this.isDrawing = false;
const rect = this.imgElement.getBoundingClientRect();
const endX = e.clientX - rect.left;
const endY = e.clientY - rect.top;
//
if (
Math.abs(endX - this.startX) > 5 &&
Math.abs(endY - this.startY) > 5
) {
this.addRectangle(
this.startX,
this.startY,
endX - this.startX,
endY - this.startY,
endX,
endY
);
}
this.redrawAll();
},
//
addPoint(x, y) {
var point_object = { firstX: Math.round(x), firstY: Math.round(y) };
this.all_data.push(point_object);
localStorage.setItem("drawData", JSON.stringify(this.all_data));
this.annotations.push({
type: "point",
x,
y,
color: this.drawColor,
size: this.pointSize,
});
this.redrawAll();
},
//
addRectangle(x, y, width, height, endX, endY) {
//
var point_object = {
firstX: Math.round(x),
firstY: Math.round(y),
secondX: Math.round(endX),
secondY: Math.round(endY),
};
this.all_data.push(point_object);
//
localStorage.setItem("drawData", JSON.stringify(this.all_data));
this.annotations.push({
type: "rect",
x,
y,
width,
height,
color: this.drawColor,
lineWidth: this.lineWidth,
});
},
//
drawPoint(x, y, color, size) {
this.ctx.fillStyle = color;
this.ctx.beginPath();
this.ctx.arc(x, y, size, 0, 2 * Math.PI);
this.ctx.fill();
//
this.ctx.fillStyle = "red";
this.ctx.beginPath();
this.ctx.arc(x, y, size / 2, 0, 2 * Math.PI);
this.ctx.fill();
},
//
drawRect(x, y, width, height, color, lineWidth, isTemporary = false) {
this.ctx.strokeStyle = color;
this.ctx.lineWidth = lineWidth;
this.ctx.strokeRect(x, y, width, height);
if (!isTemporary) {
//
this.ctx.fillStyle = "rgba(0, 0, 255, 0)"; //
// this.ctx.fillStyle = color.replace(')', ', 0)').replace('rgb', 'rgba');
this.ctx.fillRect(x, y, width, height);
}
},
//
redrawAll() {
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
this.annotations.forEach((annotation) => {
if (annotation.type === "point") {
this.drawPoint(
annotation.x,
annotation.y,
annotation.color,
annotation.size
);
} else {
this.drawRect(
annotation.x,
annotation.y,
annotation.width,
annotation.height,
annotation.color,
annotation.lineWidth
);
}
});
},
//
removeAnnotation(index) {
this.all_data = [];
this.annotations.splice(index, 1);
this.redrawAll();
},
//
highlightAnnotation(index) {
const annotation = this.annotations[index];
//
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
//
this.annotations.forEach((item, i) => {
if (i !== index) {
const alphaColor = item.color
.replace(")", ", 0.3)")
.replace("rgb", "rgba");
if (item.type === "point") {
this.drawPoint(item.x, item.y, alphaColor, item.size);
} else {
this.drawRect(
item.x,
item.y,
item.width,
item.height,
alphaColor,
item.lineWidth
);
}
}
});
//
if (annotation.type === "point") {
this.drawPoint(
annotation.x,
annotation.y,
annotation.color,
annotation.size * 1.5
);
} else {
this.drawRect(
annotation.x,
annotation.y,
annotation.width,
annotation.height,
annotation.color,
annotation.lineWidth * 2
);
}
// 3
setTimeout(() => {
this.redrawAll();
}, 3000);
},
//
clearAll() {
this.annotations = [];
this.all_data = [];
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
},
//
exportAnnotations() {
const data = {
imageWidth: this.imgElement.width,
imageHeight: this.imgElement.height,
annotations: this.annotations,
};
return data;
},
//
importAnnotations(data) {
if (
data.imageWidth === this.imgElement.width &&
data.imageHeight === this.imgElement.height
) {
this.annotations = data.annotations;
this.redrawAll();
return true;
}
return false;
},
},
};
</script>
<style scoped>
.image-annotation-container {
font-family: Arial, sans-serif;
max-width: 1000px;
height: 550px;
margin: 0 auto;
padding: 20px;
overflow: scroll;
}
.control-panel {
margin: 15px 0;
padding: 15px;
/* background: #f5f5f5; */
border-radius: 5px;
display: flex;
flex-wrap: wrap;
gap: 10px;
align-items: center;
border: 1px solid #555;
}
button {
padding: 8px 15px;
background: #4caf50;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
transition: background 0.2s;
}
button:hover {
background: #45a049;
}
button.active {
background: #2e7d32;
box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2);
}
.clear-btn {
background: #f44336;
}
.clear-btn:hover {
background: #d32f2f;
}
.small-btn {
padding: 3px 8px;
font-size: 12px;
background: #f44336;
}
.config-options {
display: flex;
gap: 15px;
align-items: center;
margin-left: auto;
}
.config-options label {
display: flex;
align-items: center;
gap: 5px;
font-size: 14px;
}
.image-wrapper {
position: relative;
display: inline-block;
margin-bottom: 20px;
border: 1px solid #555;
}
img {
display: block;
max-width: 100%;
max-height: 100%;
}
canvas {
position: absolute;
top: 0;
left: 0;
pointer-events: none;
/* width: 100%;
height: 100%; */
/* width:780px;
height:445px; */
}
.annotation-panel {
border: 1px solid #ddd;
padding: 15px;
border-radius: 5px;
background: #f9f9f9;
}
.annotation-list {
max-height: 300px;
overflow-y: auto;
margin-top: 10px;
}
.annotation-item {
padding: 10px;
margin-bottom: 8px;
background: white;
border-radius: 4px;
border-left: 4px solid #4caf50;
cursor: pointer;
transition: transform 0.2s, box-shadow 0.2s;
}
.annotation-item:hover {
transform: translateX(3px);
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
}
.annotation-item.point {
border-left-color: #2196f3;
}
.annotation-item.rect {
border-left-color: #ff9800;
}
.annotation-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 5px;
}
.annotation-type {
font-weight: bold;
}
.annotation-details {
font-size: 14px;
color: #555;
}
.empty-message {
color: #777;
font-style: italic;
text-align: center;
padding: 10px;
}
</style>

+ 837
- 0
src/views/cards/site_management/components/chooseAlg.vue View File

@ -0,0 +1,837 @@
<template>
<el-dialog
:title="alertTitle"
:visible.sync="visible"
width="55%"
@close="handleClose"
:close-on-click-modal="false"
append-to-body
custom-class="algorithm-dialog"
>
<el-row class="card-page">
<!-- 筛选条件区域 -->
<el-form
:model="queryParams"
ref="queryForm"
:inline="true"
label-width="75px"
class="filter-form"
>
<el-form-item label="算法类型" prop="algType">
<el-select
style="width: 150px"
v-model="queryParams.algType"
placeholder="全部"
filterable
clearable
popper-class="card-select-dropdown dark-select-dropdown"
@change="handleFilterChange"
>
<el-option
v-for="item in algTypeOptions"
:key="item"
:label="item"
:value="item"
/>
</el-select>
</el-form-item>
<el-form-item label="算法名称" prop="algName">
<el-select
style="width: 150px"
v-model="queryParams.algName"
placeholder="全部"
filterable
clearable
popper-class="card-select-dropdown dark-select-dropdown"
@change="handleFilterChange"
>
<el-option
v-for="item in algNameOptions"
:key="item"
:label="item"
:value="item"
/>
</el-select>
</el-form-item>
</el-form>
<!-- 添加提示文字 -->
<div class="selection-limit-tip" :class="{'warning': isNearLimit}">
<i class="el-icon-info"></i>
最多可选择10个算法已选择 {{ checkedCount }}
</div>
<el-form label-width="0px" :model="form" ref="ruleForm">
<el-col :span="24">
<el-table
v-loading="loading"
:data="filteredData"
row-key="id"
border
default-expand-all
height="400px"
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
class="dark-table"
ref="algorithmTable"
>
<el-table-column
align="center"
width="60"
v-if="!disableCheck"
>
<template #header>
<span></span>
</template>
<template slot-scope="scope">
<el-checkbox
:indeterminate="scope.row.isIndeterminate"
v-model="scope.row.checked"
:disabled="isCheckboxDisabled(scope.row)"
@change="handleRowCheckedChange(scope.row)"
class="dark-checkbox"
></el-checkbox>
</template>
</el-table-column>
<el-table-column
type="index"
align="center"
label="序号"
width="50"
/>
<el-table-column
prop="name"
header-align="center"
label="算法名称"
min-width="180"
>
<template slot-scope="scope">
<span :title="scope.row.name" class="dark-text">{{ scope.row.name }}</span>
</template>
</el-table-column>
<el-table-column label="算法类型" align="center" min-width="120">
<template slot-scope="scope">
<span v-if="scope.row.parent" class="dark-text">{{ scope.row.parent.name }}</span>
<span v-else class="dark-text">{{ scope.row.name }}</span>
</template>
</el-table-column>
<el-table-column label="时间" align="center" width="160">
<template slot-scope="scope">
<span v-if="scope.row.id && scope.row.id.startsWith('sub_')" class="dark-text">
{{ formatDateTime(scope.row.createTime || scope.row.updateTime) }}
</span>
<span v-else class="dark-text">--</span>
</template>
</el-table-column>
<el-table-column prop="upperValue" align="center" width="180">
<template #header>
<div class="dark-text">阈值上限</div>
</template>
<template slot-scope="scope">
<el-form-item
style="margin-top: 0; margin-bottom: 0"
v-if="scope.row.upperFlag"
>
<el-input
v-model="scope.row.upperValue"
:disabled="!scope.row.checked"
placeholder="请输入"
class="dark-table-input"
size="small"
></el-input>
</el-form-item>
<span v-else class="dark-text">--</span>
</template>
</el-table-column>
<el-table-column prop="lowerValue" align="center" width="180">
<template #header>
<div class="dark-text">阈值下限</div>
</template>
<template slot-scope="scope">
<el-form-item
style="margin-top: 0; margin-bottom: 0"
v-if="scope.row.lowerFlag"
>
<el-input
v-model="scope.row.lowerValue"
:disabled="!scope.row.checked"
placeholder="请输入"
class="dark-table-input"
size="small"
></el-input>
</el-form-item>
<span v-else class="dark-text">--</span>
</template>
</el-table-column>
</el-table>
</el-col>
</el-form>
</el-row>
<span slot="footer" class="dialog-footer card-page">
<el-button @click="eventOk" :disabled="loading" type="primary">确定</el-button>
<el-button @click="visible = false" class="dark-button">取消</el-button>
</span>
</el-dialog>
</template>
<script>
import { getAlgList, getAlgPartType } from "@/api/basedata/area/area";
import linq from "linq";
export default {
name: "ChooseAlg",
props: {
show: {
type: Boolean,
default: false,
},
algSubtypeIds: {
type: String,
default: "",
},
algSubtypeName: {
type: String,
default: "",
},
alarmThreshold: {
type: String,
default: "",
},
disableCheck: {
type: Boolean,
default: false,
},
filterCheckedAlgs: {
type: Boolean,
default: false,
},
},
watch: {
show(newVal) {
this.visible = newVal;
if (newVal === true) {
this.loadData();
}
},
},
data() {
return {
loading: false,
form: {
algList: [],
},
visible: false,
algList: [],
alertTitle: "设置智能识别类型",
queryParams: {
algType: null,
algName: null,
upperValue: null,
lowerValue: null,
timeRange: null,
},
savedAlgorithmConfig: new Map(),
MAX_SELECTION_LIMIT: 10, //
};
},
computed: {
//
checkedCount() {
if (!this.form.algList) return 0;
return this.form.algList.filter(item => item.checked && item.parent && item.id && item.id.startsWith('sub_')).length;
},
//
isNearLimit() {
return this.checkedCount >= this.MAX_SELECTION_LIMIT;
},
selectedIds() {
const ids = [];
// algSubtypeIds
if (this.algSubtypeIds && this.algSubtypeIds.length > 0) {
const parts = this.algSubtypeIds.split(',');
parts.forEach(part => {
const trimmed = part.trim();
if (trimmed) {
let cleanId = trimmed.replace('sub_', '');
ids.push(cleanId);
}
});
}
// algSubtypeIds alarmThreshold
if (ids.length === 0 && this.alarmThreshold && this.alarmThreshold.length > 0) {
try {
const alarmThreshold = JSON.parse(this.alarmThreshold);
if (Array.isArray(alarmThreshold)) {
alarmThreshold.forEach(item => {
if (item.id) {
let cleanId = String(item.id).replace('sub_', '');
if (!ids.includes(cleanId)) {
ids.push(cleanId);
}
}
});
}
} catch (e) {
// console.error("alarmThreshold:", e);
}
}
return ids;
},
selectedNames() {
if (this.algSubtypeName && this.algSubtypeName.length > 0) {
return this.algSubtypeName.split(',').map(name => name.trim());
}
return [];
},
hasFilter() {
return this.queryParams.algType ||
this.queryParams.algName ||
this.queryParams.upperValue ||
this.queryParams.lowerValue ||
(this.queryParams.timeRange && this.queryParams.timeRange.length > 0);
},
algTypeOptions() {
const types = new Set();
this.form.algList.forEach(item => {
if (item.parent) {
types.add(item.parent.name);
} else {
types.add(item.name);
}
});
return Array.from(types).sort();
},
algNameOptions() {
const names = new Set();
this.form.algList.forEach(item => {
names.add(item.name);
});
return Array.from(names).sort();
},
filteredData() {
if (!this.hasFilter) {
return this.algList;
}
return this.algList.filter(item => {
return this.filterNode(item);
}).map(item => {
if (item.children && item.children.length > 0) {
const filteredChildren = item.children.filter(child => this.filterNode(child));
if (filteredChildren.length > 0) {
return {
...item,
children: filteredChildren
};
}
}
return item;
});
},
},
methods: {
formatDateTime(dateTimeStr) {
if (!dateTimeStr) return '--';
return dateTimeStr;
},
handleFilterChange() {
this.$forceUpdate();
},
filterNode(node) {
let match = this.matchNode(node);
if (node.children && node.children.length > 0) {
const hasMatchingChild = node.children.some(child => this.filterNode(child));
return match || hasMatchingChild;
}
return match;
},
matchNode(node) {
const nodeType = node.parent ? node.parent.name : node.name;
const nodeName = node.name;
if (this.queryParams.algType && nodeType !== this.queryParams.algType) {
return false;
}
if (this.queryParams.algName) {
const searchName = this.queryParams.algName.toLowerCase();
if (!nodeName.toLowerCase().includes(searchName)) {
return false;
}
}
return true;
},
//
isCheckboxDisabled(row) {
//
if (row.checked) return false;
//
if (row.parent && this.checkedCount >= this.MAX_SELECTION_LIMIT) {
return true;
}
//
if (row.children && row.children.length > 0) {
const allChildrenDisabled = row.children.every(child => this.isCheckboxDisabled(child));
return allChildrenDisabled;
}
return false;
},
loadData() {
this.loading = true;
// alarmThreshold
this.parseSavedAlgorithmConfig();
Promise.all([getAlgPartType(), getAlgList()])
.then(([subTypeRes, mainTypeRes]) => {
const treeData = [];
mainTypeRes.rows.forEach((mainRow) => {
const mainNode = {
id: "alg_" + mainRow.algId,
name: mainRow.algName,
sourceId: mainRow.algId,
checked: false,
isIndeterminate: false,
upperValue: "",
lowerValue: "",
lowerFlag: mainRow.lowerFlag,
upperFlag: mainRow.upperFlag,
code: "",
parent: null,
children: [],
createTime: null,
updateTime: null
};
const children = linq
.from(subTypeRes.rows)
.where((x) => x.algId === mainRow.algId)
.select((sub) => {
const algId = String(sub.algSubtypeId);
const savedConfig = this.savedAlgorithmConfig.get(algId);
let upperValue = "";
let lowerValue = "";
if (savedConfig) {
upperValue = savedConfig.upperValue || "";
lowerValue = savedConfig.lowerValue || "";
} else {
// 使
upperValue = sub.upperValue || "";
lowerValue = sub.lowerValue || "";
}
return {
sourceId: sub.algSubtypeId,
id: "sub_" + sub.algSubtypeId,
name: sub.algSubtypeName,
checked: false,
isIndeterminate: false,
code: sub.algSubtypeCode,
upperValue: upperValue,
lowerValue: lowerValue,
lowerFlag: sub.lowerFlag,
upperFlag: sub.upperFlag,
parent: mainNode,
createTime: sub.createTime,
updateTime: sub.updateTime
};
})
.toArray();
mainNode.children = children;
if (!(this.filterCheckedAlgs && children.length === 0)) {
treeData.push(mainNode);
}
});
this.algList = treeData;
this.form.algList = this.treeToList(treeData);
this.$nextTick(() => {
this.renderData();
});
this.loading = false;
})
.catch((error) => {
this.loading = false;
});
},
parseSavedAlgorithmConfig() {
this.savedAlgorithmConfig.clear();
if (this.alarmThreshold && this.alarmThreshold.length > 0) {
try {
const alarmThreshold = JSON.parse(this.alarmThreshold);
if (Array.isArray(alarmThreshold)) {
alarmThreshold.forEach(item => {
if (item.id) {
const cleanId = String(item.id).replace('sub_', '');
const config = {
upperValue: item.upperValue || "",
lowerValue: item.lowerValue || ""
};
this.savedAlgorithmConfig.set(cleanId, config);
}
});
}
} catch (e) {
// console.error("alarmThreshold:", e);
}
}
},
renderData() {
const idsToSelect = this.selectedIds;
if (idsToSelect.length === 0) {
this.form.algList.forEach(item => {
item.checked = false;
if (!item.parent) {
item.isIndeterminate = false;
}
});
return;
}
//
this.form.algList.forEach(item => {
item.checked = false;
if (!item.parent) {
item.isIndeterminate = false;
}
});
// ID
idsToSelect.forEach((id) => {
const idStr = String(id).replace('sub_', '');
//
const matchedItem = this.form.algList.find(item => {
if (!item.parent) return false;
const itemSourceIdStr = String(item.sourceId).replace('sub_', '');
return itemSourceIdStr === idStr;
});
if (matchedItem) {
matchedItem.checked = true;
// savedAlgorithmConfig
const savedConfig = this.savedAlgorithmConfig.get(idStr);
if (savedConfig) {
if (savedConfig.upperValue && matchedItem.upperFlag) {
matchedItem.upperValue = savedConfig.upperValue;
}
if (savedConfig.lowerValue && matchedItem.lowerFlag) {
matchedItem.lowerValue = savedConfig.lowerValue;
}
} else {
// console.log(` 使: =${matchedItem.upperValue}, =${matchedItem.lowerValue}`);
}
//
if (matchedItem.parent) {
const parent = matchedItem.parent;
const allChecked = parent.children.every(child => child.checked);
const anyChecked = parent.children.some(child => child.checked);
parent.checked = allChecked;
parent.isIndeterminate = anyChecked && !allChecked;
}
} else {
// console.warn(` ID: ${idStr}`);
}
});
this.$forceUpdate();
if (this.$refs.algorithmTable) {
this.$refs.algorithmTable.doLayout();
}
//
const checkedItems = this.form.algList.filter(x => x.checked && x.parent);
// console.log(":", checkedItems.map(x => ({
// name: x.name,
// upperValue: x.upperValue,
// lowerValue: x.lowerValue
// })));
},
treeToList(nodes) {
let result = [];
nodes.forEach((node) => {
result.push(node);
if (node.children && node.children.length > 0) {
result = result.concat(this.treeToList(node.children));
}
});
return result;
},
handleRowCheckedChange(row) {
//
if (row.parent && !row.checked) {
//
if (this.checkedCount >= this.MAX_SELECTION_LIMIT) {
this.$message.warning(`最多只能选择 ${this.MAX_SELECTION_LIMIT} 个算法`);
return;
}
}
if (row.children) {
//
let willCheckCount = 0;
if (row.checked) {
//
willCheckCount = row.children.filter(child => !child.checked).length;
if (this.checkedCount + willCheckCount > this.MAX_SELECTION_LIMIT) {
this.$message.warning(`最多只能选择 ${this.MAX_SELECTION_LIMIT} 个算法,当前已选择 ${this.checkedCount}`);
row.checked = false;
return;
}
row.children.forEach((x) => {
x.checked = row.checked;
});
} else {
row.children.forEach((x) => {
x.checked = row.checked;
});
}
}
if (row.parent) {
//
const parent = row.parent;
const allChecked = parent.children.every(child => child.checked);
const anyChecked = parent.children.some(child => child.checked);
parent.checked = allChecked;
parent.isIndeterminate = anyChecked && !allChecked;
} else {
if (row.checked) {
row.isIndeterminate = false;
}
}
},
handleClose() {
if (this.$refs.ruleForm) {
this.$refs.ruleForm.resetFields();
}
this.visible = false;
this.$emit("update:show", false);
},
eventOk() {
const selectedItems = this.form.algList.filter(x =>
x.checked && x.parent && x.id && x.id.startsWith('sub_')
);
//
if (selectedItems.length > this.MAX_SELECTION_LIMIT) {
this.$message.error(`选择的算法不能超过 ${this.MAX_SELECTION_LIMIT}`);
return;
}
const result = selectedItems.map(item => ({
id: item.sourceId,
name: item.name,
upperValue: item.upperValue || "",
lowerValue: item.lowerValue || "",
lowerFlag: item.lowerFlag,
upperFlag: item.upperFlag,
code: item.code
}));
this.visible = false;
this.$emit("confirm", result);
},
resetQuery() {
this.queryParams = {
algType: null,
algName: null,
upperValue: null,
lowerValue: null,
timeRange: null,
};
this.$refs.queryForm?.resetFields();
this.handleFilterChange();
},
},
};
</script>
<style scoped>
.algorithm-dialog {
background-color: #1a2a3a !important;
}
.algorithm-dialog ::v-deep .el-dialog__title {
color: #ffffff;
}
.algorithm-dialog ::v-deep .el-dialog__header {
border-bottom: 1px solid #2a3a4a;
}
.algorithm-dialog ::v-deep .el-dialog__body {
background-color: #1a2a3a;
color: #ffffff;
}
.algorithm-dialog ::v-deep .el-dialog__footer {
border-top: 1px solid #2a3a4a;
background-color: #1a2a3a;
}
.filter-form {
background-color: #1a2a3a;
padding: 10px;
}
.filter-form ::v-deep .el-form-item__label {
color: #ffffff;
}
.filter-form ::v-deep .el-select .el-input__inner {
background-color: #2a3a4a;
border-color: #3a4a5a;
color: #ffffff;
}
/* 选择限制提示样式 */
.selection-limit-tip {
padding: 8px 12px;
margin-bottom: 10px;
background-color: #2a3a4a;
border-radius: 4px;
color: #a0b0c0;
font-size: 12px;
display: flex;
align-items: center;
gap: 6px;
}
.selection-limit-tip.warning {
background-color: #3a2a2a;
color: #ffaa66;
}
.selection-limit-tip i {
font-size: 14px;
}
.dark-table {
background-color: #222f3d !important;
border-color: rgba(64, 146, 255, 0.95) !important;
}
.dark-table ::v-deep .el-table__header th {
background-color: #06347f !important;
color: #ffffff !important;
border-color: rgba(64, 146, 255, 0.95) !important;
border-bottom: 2px solid rgba(64, 146, 255, 0.95) !important;
}
.dark-table ::v-deep .el-table__row td {
color: #000000 !important;
border-bottom: 1px solid #c7d7f4 !important;
border-right-color: #c7d7f4 !important;
}
.dark-table ::v-deep .el-table__body-wrapper,
.dark-table ::v-deep .el-table__fixed,
.dark-table ::v-deep .el-table__fixed-right,
.dark-table ::v-deep .el-table__empty-block {
background: #222f3d !important;
}
.dark-table ::v-deep .el-table__body tbody tr:nth-child(odd) td.el-table__cell {
background: #e4edfc !important;
}
.dark-table ::v-deep .el-table__body tbody tr:nth-child(even) td.el-table__cell,
.dark-table ::v-deep .el-table__row--striped td.el-table__cell {
background: #d7e4f8 !important;
}
.dark-table ::v-deep .el-table__row:hover td {
background-color: #c5d9f6 !important;
}
.dark-table-input ::v-deep .el-input__inner {
background-color: #edf4ff !important;
border-color: #7da8e8 !important;
color: #000000 !important;
-webkit-text-fill-color: #000000 !important;
}
.dark-table-input ::v-deep .el-input__inner::placeholder {
color: #6f7f96 !important;
-webkit-text-fill-color: #6f7f96 !important;
}
.dark-table-input.is-disabled ::v-deep .el-input__inner,
.dark-table-input ::v-deep .el-input.is-disabled .el-input__inner {
background-color: #d8e5f8 !important;
border-color: #7da8e8 !important;
color: #000000 !important;
-webkit-text-fill-color: #000000 !important;
}
/* 禁用复选框样式 */
.dark-checkbox ::v-deep .el-checkbox.is-disabled .el-checkbox__inner {
background-color: #2a3a4a;
border-color: #3a4a5a;
opacity: 0.5;
}
.dark-checkbox ::v-deep .el-checkbox__input.is-checked .el-checkbox__inner {
background-color: #1890ff;
border-color: #1890ff;
}
.dark-text {
color: #000000 !important;
}
.dark-table ::v-deep th.el-table__cell .dark-text {
color: #ffffff !important;
}
.dark-button {
background-color: #2a3a4a !important;
border-color: #3a4a5a !important;
color: #ffffff !important;
}
.dark-button:hover {
background-color: #3a4a5a !important;
border-color: #4a5a6a !important;
}
</style>

+ 222
- 0
src/views/cards/site_management/components/chooseDevices.vue View File

@ -0,0 +1,222 @@
<template>
<el-dialog title="选择巡检设备" :visible.sync="visible" width="55%" style="height:100%" @close="handleClose" :close-on-click-modal="false" append-to-body>
<el-row class="card-page">
<el-form label-width="0px" :model="form" ref="ruleForm">
<el-col :span="24">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" label-width="68px">
<el-form-item label="设备类型" prop="type">
<el-select v-model="queryParams.type" placeholder="请选择">
<el-option v-for="item in dict.type.device_type" :key="item.value" :label="item.label" :value="item.value">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="设备名称" prop="patroldeviceName">
<el-input v-model="queryParams.patroldeviceName" placeholder="请输入设备名称" clearable @keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="设备编码" prop="patroldeviceCode">
<el-input v-model="queryParams.patroldeviceCode" placeholder="请输入设备编码" clearable @keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="生产厂家" prop="manufacturer">
<el-input v-model="queryParams.manufacturer" placeholder="请输入生产厂家" clearable @keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<el-table v-loading="loading" :data="eqpbookList" @row-click="handleCurrentChange" :multiple="false" highlight-current-row>
<el-table-column label="设备名称" width="100" align="center" prop="patroldeviceName" fixed />
<el-table-column label="设备编码" width="100" align="center" prop="patroldeviceCode" fixed />
<el-table-column label="视频NVR编码" width="100" align="center" prop="videoNvrCode" fixed />
<el-table-column label="生产厂家" width="100" align="center" prop="manufacturer" fixed />
<el-table-column label="主系统编号" width="100" align="center" prop="mainSystemCode" fixed />
<el-table-column label="变电站名称" width="150" align="center" prop="stationName" />
<el-table-column label="变电站编码" width="100" align="center" prop="stationCode" />
<el-table-column label="区域名称" width="150" align="center" prop="areaName" />
<el-table-column label="设备类型" width="100" align="center" prop="type">
<template slot-scope="scope">
<dict-tag :options="dict.type.device_type" :value="scope.row.type" />
</template>
</el-table-column>
<el-table-column label="设备型号" width="100" align="center" prop="deviceModel" />
<el-table-column label="使用单位" width="100" align="center" prop="useUnit" />
<el-table-column label="设备来源" width="100" align="center" prop="deviceSource" />
<el-table-column label="生产日期" width="100" align="center" prop="productionDate" />
<el-table-column label="出厂编号" width="100" align="center" prop="productionCode" />
<el-table-column label="是否轮转" width="100" align="center" prop="istransport">
<template slot-scope="scope">
<dict-tag :options="dict.type.is_transport" :value="scope.row.istransport" />
</template>
</el-table-column>
<el-table-column label="使用类型" width="100" align="center" prop="useMode">
<template slot-scope="scope">
<dict-tag :options="dict.type.use_mode" :value="scope.row.useMode" />
</template>
</el-table-column>
<el-table-column label="视频类型" width="100" align="center" prop="videoMode">
<template slot-scope="scope">
<dict-tag :options="dict.type.video_mode" :value="scope.row.videoMode" />
</template>
</el-table-column>
<el-table-column label="所属机器人" width="100" align="center" prop="robotsCode" />
<el-table-column label="ip地址" width="200" align="center" prop="ipAddr" />
<el-table-column label="端口" width="100" align="center" prop="port" />
<el-table-column label="用户" width="100" align="center" prop="user" />
<el-table-column label="密码" width="100" align="center" prop="password" />
</el-table>
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList" />
</el-col>
</el-form>
</el-row>
<span slot="footer" class="dialog-footer card-page">
<el-button @click="eventOk" :disabled="loading" type="primary">确定</el-button>
<el-button @click="visible = false">取消</el-button>
</span>
</el-dialog>
</template>
<script>
import {
listEqpbook
} from "@/api/basedata/eqpbook/eqpbook";
import linq from 'linq'
export default {
dicts: ["device_type", "video_mode", "use_mode", "is_transport"],
props: {
show: {
type: Boolean,
default: false,
}
},
watch: {
show (newVal) {
this.visible = newVal
if (newVal === true) {
this.getList();
}
}
},
data () {
return {
currentRow: null,
total: 0,
loading: false,
// -
eqpbookList: [],
form: {
algList: []
},
//
queryParams: {
pageNum: 1,
pageSize: 10,
stationName: null,
stationCode: null,
areaName: null,
patroldeviceName: null,
patroldeviceCode: null,
deviceModel: null,
manufacturer: null,
useUnit: null,
deviceSource: null,
productionDate: null,
productionCode: null,
istransport: null,
useMode: null,
videoMode: null,
place: null,
positionX: null,
positionY: null,
positionZ: null,
type: null,
patroldeviceInfo: null,
robotsCode: null,
ipAddr: null,
port: null,
user: null,
password: null,
video_mode: null,
use_mode: null,
is_transport: null,
},
//
ids: [],
//
single: true,
//
multiple: true,
visible: false,
algList: [],/* 算法类型 */
alertTitle: "设置智能识别类型", /* 弹窗标题 */
rules: {
algId: [{ required: true, message: "必选", trigger: "blur" }],
algSubtypeId: [{ required: true, message: "必选", trigger: "blur" }],
upperValue: [{ required: true, message: "必选", trigger: "blur" }],
lowerValue: [{ required: true, message: "必选", trigger: "blur" }],
}
}
},
created () {
},
computed: {
},
methods: {
/** 重置按钮操作 */
resetQuery () {
this.resetForm("queryForm");
this.handleQuery();
},
/** 搜索按钮操作 */
handleQuery () {
this.queryParams.pageNum = 1;
this.getList();
},
handleCurrentChange (row) {
this.currentRow = row;
},
//
handleSelectionChange (selection) {
this.ids = selection.map((item) => item.eqpbookId);
this.single = selection.length !== 1;
this.multiple = !selection.length;
},
/** 查询基础数据-设备台账主列表 */
getList () {
this.loading = true;
listEqpbook(this.queryParams).then((response) => {
this.eqpbookList = response.rows;
this.total = response.total;
this.loading = false;
});
},
//
handleClose () {
//
this.resetForm('queryForm');
this.visible = false;
this.$emit('update:show', false)
},
//
eventOk () {
const me = this;
if (this.currentRow === null) {
this.$message({
type: 'warning',
message: '请选中一行数据。'
})
return;
}
me.visible = false;
this.$emit('confirm', this.currentRow)
}
}
}
</script>

+ 171
- 0
src/views/cards/site_management/components/uploadDialog.vue View File

@ -0,0 +1,171 @@
<template>
<el-dialog :title="title" @close="handleClose" :visible.sync="visible" width="40%" append-to-body
:close-on-click-modal="false">
<el-upload :data="updata" :accept="accept" class="upload-demo" :headers="headers" v-loading="loading"
:with-credential="true" :action="action" :on-success="handleUploadSuccess" :on-error="handleUploadError"
:on-preview="handlePreview" :on-remove="handleRemove" :before-remove="beforeRemove" :limit="1"
:on-exceed="handleExceed" :file-list="fileList">
<div class="card-page">
<el-button size="small" type="primary">点击上传</el-button>
</div>
<!-- list-type="picture" -->
</el-upload>
<AsyncImage v-if="pendding" ref="asyncImage" class="xg_image_viewer" @imgload="handleImageLoad"
@imgloaderror="handleImageLoadError" :src="currentSrc" :srcList="currentSrcList" />
<span slot="footer" class="dialog-footer card-page">
<el-button type="primary" v-if="showConfirmBtn" @click="handleConfirm"> </el-button>
<el-button @click="visible = false"> </el-button>
</span>
</el-dialog>
</template>
<script>
import { getToken } from '@/utils/auth'
import AsyncImage from '@/components/AsyncImage'
import linq from 'linq'
export default {
components: {
AsyncImage
},
props: {
show: {
type: Boolean,
default: false
},
filePath: {
type: String,
default: ""
},
title: {
type: String,
default: "判别图片上传"
},
action: {
type: String,
default: process.env.VUE_APP_BASE_API + '/patrol/partrolresult/lasupload'
},
accept: {
type: String,
default: '*'
},
updata: {
type: Object,
default: function () {
return {}
}
},
showConfirmBtn: {
type: Boolean,
default: true
}
},
watch: {
show: {
handler (newVal) {
this.loading = false
this.visible = newVal
this.fileList = []
if (this.filePath && this.filePath.length > 0) {
this.fileList = linq.from(this.filePath.split(',')).select(x => { return { name: '缺陷判别图片.jpg', url: x } }).toArray()
} else {
this.fileList = []
}
}
}
},
data () {
return {
visible: false,
fileList: [],
loading: false,
currentSrc: "",
currentSrcList: [],
pendding: false,
headers: { 'Authorization': 'Bearer ' + getToken() }
};
},
methods: {
handleConfirm () {
this.visible = false
this.$emit('confirm', this.fileList)
},
//
handleUploadSuccess (response, file, fileList) {
this.fileList = []
if (typeof (file.response) !== 'string') {
if (file.response.code != 200) {
this.$message.error(`上传失败。消息:${file.response.msg}`)
return;
}
this.fileList.push({
name: '机器人点位文件.xml'
})
return;
}
this.fileList.push({
name: file.name,
url: file.response
// name: "_20220520095815.png"
// percentage: 100
// raw: File
// response: "/home/xgftp/_20220520095815.png"
// size: 699595
// status: "success"
// uid: 1653012695833
})
console.log(this.fileList)
// this.$emit('uploadSuccess', response)
},
//
handleClose () {
this.visible = false
this.fileList = []
this.$emit('update:show', false)
},
//
handleUploadError (err, file, fileList) {
this.$message.error(`上传失败,请检查文件或网络。`)
},
handleRemove (file, fileList) {
this.fileList = []
console.log(file, fileList);
},
handlePreview (file) {
this.pendding = false
this.$nextTick(() => {
this.pendding = true
this.currentSrc = file.url
this.currentSrcList = [file.url]
this.loading = true
})
},
handleImageLoad () {
this.loading = false;
this.$refs.asyncImage.$el.querySelector("img").click();
},
handleImageLoadError () {
this.loading = false;
},
handleExceed (files, fileList) {
this.$message.warning(`当前限制选择 1 个文件,本次选择了 ${files.length} 个文件,共选择了 ${files.length + fileList.length} 个文件`);
},
beforeRemove (file, fileList) {
return this.$confirm(`确定移除 ${file.name}`);
}
}
}
</script>
<style lang="scss" scoped>
.xg_image_viewer {
position: fixed;
bottom: 0;
left: 0;
width: 50px;
height: 50px;
display: none;
}
</style>

+ 4686
- 0
src/views/cards/site_management/site_management.vue
File diff suppressed because it is too large
View File


+ 8
- 3
src/views/cards/taskSummary/inspectionTask.vue View File

@ -1120,11 +1120,16 @@ export default {
}
.card-page ::v-deep .el-table__header-wrapper,
.card-page ::v-deep .el-table__fixed-header-wrapper,
.card-page ::v-deep .el-table__fixed-header-wrapper {
background: #dbe7fb !important;
}
.card-page ::v-deep .el-table__body-wrapper,
.card-page ::v-deep .el-table__fixed,
.card-page ::v-deep .el-table__fixed-right {
background: #dbe7fb !important;
.card-page ::v-deep .el-table__fixed-right,
.card-page ::v-deep .el-table__empty-block {
background: transparent !important;
background-color: transparent !important;
}
.card-page ::v-deep .el-table th.el-table__cell {


Loading…
Cancel
Save