| @ -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> | |||||
| @ -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> --> | |||||
| @ -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> | |||||
| @ -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> | |||||
| @ -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> | |||||
| @ -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> | |||||
| @ -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> | |||||