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