840 lines
26 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!--
* @Author: SunTao 328867980@qq.com
* @Date: 2024-11-20 11:58:33
* @LastEditors: SunTao 328867980@qq.com
* @LastEditTime: 2025-02-12 13:16:22
* @FilePath: \znxjxt-ui\src\views\xj\document\maintenance-notice\components\edit-dialog.vue
* @Description: 养护通知单编辑弹窗
-->
<template>
<div class="edit-dialog">
<el-form class="editForm" ref="editForm" :model="editForm" :rules="rules" label-width="7rem">
<el-row :gutter="24">
<el-col :span="6">
<el-form-item label="病害成因:" prop="value2">
<el-select v-model="editForm.value2" placeholder="请选择病害成因" clearable>
<el-option v-for="item in value2List" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="路段类型:" prop="value1">
<el-select v-model="editForm.value1" placeholder="请选择路段类型" clearable>
<el-option v-for="item in value1List" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="工区:" prop="value3">
<el-select v-model="editForm.value3" placeholder="请选择工区" clearable>
<el-option v-for="item in value3List" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="病害来源:" prop="value4">
<el-select v-model="editForm.value4" placeholder="请选择病害来源" clearable>
<el-option v-for="item in value4List" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="24">
<el-col :span="6">
<el-form-item label="工作站:" prop="value5">
<el-select v-model="editForm.value5" placeholder="请选择工作站" clearable>
<el-option v-for="item in value5List" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="车道:" prop="value7">
<el-select v-model="editForm.value7" placeholder="请选择车道" clearable>
<el-option v-for="item in value7List" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="桥梁:" prop="value8">
<el-select v-model="editForm.value8" placeholder="请选择方向" clearable>
<el-option v-for="item in value8List" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="隧道:" prop="value9">
<el-select v-model="editForm.value9" placeholder="请选择方向" clearable>
<el-option v-for="item in value9List" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="24">
<el-col :span="6">
<el-form-item label="道路名称:" prop="segmentId">
<el-select v-model="editForm.segmentId" placeholder="请选择路段" clearable>
<el-option v-for="item in segmentList" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="开始桩号:" prop="stakeStart">
<el-input v-model="editForm.stakeStart" placeholder="K0000+000" />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="结束桩号:" prop="stakeEnd">
<el-input v-model="editForm.stakeEnd" placeholder="K0000+000" />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="方向:" prop="value6">
<el-select v-model="editForm.value6" placeholder="请选择方向" clearable>
<el-option v-for="item in value6List" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="24">
<el-col :span="6">
<el-form-item label="收费站:" prop="value10">
<el-select v-model="editForm.value10" placeholder="请选择路段" clearable>
<el-option v-for="item in value10List" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="收费站位置:" prop="value11">
<el-select v-model="editForm.value11" placeholder="请选择路段" clearable>
<el-option v-for="item in value11List" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="匝道类型:" prop="value12">
<el-select v-model="editForm.value12" placeholder="请选择匝道类型" clearable>
<el-option v-for="item in value12List" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="匝道立交:" prop="value13">
<el-select v-model="editForm.value13" placeholder="请选择匝道立交" clearable>
<el-option v-for="item in value13List" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="匝道立交方向:" prop="value14">
<el-select v-model="editForm.value14" placeholder="请选择匝道立交方向" clearable>
<el-option v-for="item in value14List" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
</el-col>
</el-row>
</el-form>
<el-card shadow="never">
<div slot="header" class="clearfix">
<span style="font-size: 1.2rem">选择病害</span>
<el-button style="float: right; padding: 3px 0" type="text" @click="viewSelected">选择病害</el-button>
<el-button style="float: right; padding: 3px 1rem" type="text" v-if="mapTip"
@click="viewMapSelected(false)">地图查看</el-button>
<el-button style="float: right; padding: 3px 1rem" type="text" v-if="!mapTip"
@click="viewMapSelected(true)">列表查看</el-button>
</div>
<div style="height: 33rem;">
<!-- 列表 -->
<el-table v-if="mapTip" ref="roadTable" :data="checkedDiseaseList" :row-key="getRowKey" height="33rem"
style="width: 100%">
<el-table-column type="selection" :reserve-selection="true" width="55" align="center" />
<el-table-column label="路段名称" align="center" prop="segmentName" />
<el-table-column label="采集时间" align="center" prop="createdTimeLocal">
</el-table-column>
<el-table-column label="病害类型" align="center" prop="defectTypeName">
</el-table-column>
<!-- <el-table-column label="病害状态" align="center" prop="state">
<template slot-scope="scope">
<span>{{ filterState(scope.row.state) }}</span>
</template>
</el-table-column> -->
<el-table-column label="开始桩号" align="center" prop="stakeStart" />
<el-table-column label="结束桩号" align="center" prop="stakeEnd" />
<el-table-column label="病害图片" align="center" prop="media">
<template slot-scope="scope">
<img @click="showScreenImg(scope.row)" height="50" :src="scope.row.mediaUrl" />
</template>
</el-table-column>
<el-table-column label="病害长度" align="center" prop="targetLen">
<template slot-scope="scope">
{{ scope.row.targetLen * 1 <= 0 ? "" : `${scope.row.targetLen}` }} </template>
</el-table-column>
<el-table-column label="病害面积" align="center" prop="targetArea" />
<el-table-column label="道路方向" align="center" prop="inspectDirection">
<template slot-scope="scope">
<span>{{
scope.row.inspectDirection === "0" ? "上行" : "下行"
}}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.$index)">删除
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 地图 -->
<fssm-map ref="diseaseMap" v-if="!mapTip" @map-zoom="getZoom" @feature-select="featureSelect"></fssm-map>
</div>
</el-card>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
<!-- 查看图片大图 -->
<el-dialog title="查看图片" :visible.sync="showImageDialog" width="85rem" append-to-body destroy-on-close
@close="imgCancel">
<div class="image-container" ref="imageContainer">
<img :src="currentImageItem.mediaUrl" alt="Main Image" ref="mainImage" @load="updateRects" />
<div v-for="(rect, index) in rects" :key="index" class="rect-overlay" :style="getRectStyle(rect)"></div>
<div class="rect-image">
采集时间:
{{ new Date(currentImageItem.createdTime).toLocaleString() }}
起始桩号 {{ currentImageItem.stakeStart || "暂无数据" }} 终止桩号
{{ currentImageItem.stakeEnd || "暂无数据" }}
病害类型
{{ currentImageItem.defectTypeName || "暂无数据" }}
病害面积{{ currentImageItem.targetArea }}平方米 病害长度{{
currentImageItem.targetLen * 1 <= 0 ? "暂无数据" : `${currentImageItem.targetLen}` }} </div>
</div>
</el-dialog>
<!-- 选择病害弹窗 -->
<el-dialog title="选择病害" :visible.sync="diseaseVisible" width="90%" append-to-body :close-on-click-modal="false"
destroy-on-close @close="diseaseCancel">
<disease-dialog :checkedDiseaseList="checkedDiseaseList" :segmentList="segmentList" :tableDefect="tableDefect"
v-if="diseaseVisible" @cancel="diseaseCancel" @checkedList="getCheckedList" />
</el-dialog>
</div>
</template>
<script>
import FssmScroll from "@/components/scroll/fssm-scroll.vue";
import { addMaintenanceNotice } from "@/api/xj/document";
import DiseaseDialog from "./disease-dialog.vue";
import FssmMap from '@/components/map/fssm-map.vue';
import { Feature } from "ol";
import { Point } from "ol/geom";
import { Style, Icon, Fill, Text } from "ol/style";
import VectorSource from "ol/source/Vector";
import VectorLayer from "ol/layer/Vector";
import Cluster from "ol/source/Cluster";
export default {
name: "EditDialog",
components: { FssmScroll, DiseaseDialog, FssmMap },
props: {
dialogItem: {
type: Array,
default: () => [],
},
tableDefect: {
type: Array,
default: () => [],
},
segmentList: {
type: Array,
default: () => [],
},
},
data() {
return {
// 编辑表单
editForm: {
// 病害成因
value2: "",
// 路段类型
value1: "",
// 工区id
value3: "",
// 病害来源
value4: "",
// 工作站id
value5: "",
// 方向
value6: "",
// 车道
value7: "",
// 桥梁
value8: "",
// 隧道
value9: "",
// 收费站
value10: "",
// 收费站位置
value11: "",
// 匝道类型
value12: "",
// 匝道立交
value13: "",
// 匝道立交方向
value14: "",
// 病害id
defectId: "",
// 道路名称
segmentId: "",
// 开始桩号
stakeStart: "",
// 结束桩号
stakeEnd: "",
// 养护单位项目负责人
receiver: "",
// 养护内容
// taskDescription: [""],
},
// 病害成因下拉数据
value2List: [],
// 路段类型下拉数据
value1List: [],
// 工区下拉数据
value3List: [],
// 病害来源下拉数据
value4List: [],
// 工作站下拉数据
value5List: [],
// 方向下拉
value6List: [],
// 车道下拉
value7List: [],
// 桥梁下拉
value8List: [],
// 隧道下拉
value9List: [],
// 收费站下拉
value10List: [],
// 收费站位置下拉
value11List: [],
// 匝道下拉
value12List: [],
// 匝道立交下拉
value13List: [],
// 匝道立交方向下拉
value14List: [],
// 表单验证规则
rules: {
value2: [
{
required: true,
message: "请选择病害成因",
trigger: "change",
},
],
value1: [
{ required: true, message: "请选择路段类型", trigger: "change" },
],
value3: [{ required: true, message: "请选择工区", trigger: "change" }],
value4: [
{ required: true, message: "请选择病害来源", trigger: "change" },
],
value5: [
{ required: true, message: "请选择工作站", trigger: "change" },
],
value6: [{ required: true, message: "请选择方向", trigger: "change" }],
value7: [{ required: true, message: "请选择车道", trigger: "change" }],
value8: [{ required: true, message: "请选择桥梁", trigger: "change" }],
value9: [{ required: true, message: "请选择隧道", trigger: "change" }],
value10: [
{ required: true, message: "请选择收费站", trigger: "change" },
],
value11: [
{ required: true, message: "请选择收费站位置", trigger: "change" },
],
value12: [{ required: true, message: "请选择匝道", trigger: "change" }],
value13: [
{ required: true, message: "请选择匝道立交", trigger: "change" },
],
value14: [
{ required: true, message: "请选择匝道立交方向", trigger: "change" },
],
segmentId: [
{ required: true, message: "请选择路段", trigger: "change" },
],
stakeStart: [
{
required: true,
message: "请输入开始桩号",
trigger: "blur",
},
{
pattern: /^K\d{4}\+\d{3}$/,
message: "请输入正确的桩号格式",
trigger: "blur",
},
],
stakeEnd: [
{
required: true,
message: "请输入结束桩号",
trigger: "blur",
},
{
pattern: /^K\d{4}\+\d{3}$/,
message: "请输入正确的桩号格式",
trigger: "blur",
},
],
receiver: [
{ required: true, message: "请输入负责人名称", trigger: "blur" },
],
},
// 表格是否显示控制
mapTip: true,
// 已选病害列表
checkedDiseaseList: [],
// 点位图层
clusters: null,
// 地图图层层级
mapZoom: "",
// 查看图片大图弹窗显隐控制
showImageDialog: false,
// 查看大图片路径
currentImageItem: {},
// 图片病害位置信息
rects: [],
// 选择病害弹窗显隐控制
diseaseVisible: false,
};
},
watch: {
dialogItem: {
handler(val) {
// this.editForm = {
// ...val,
// segmentId: val.segmentId === "999999999" ? "" : val.segmentId,
// id: null,
// defectId: val.id,
// createdTime: null,
// };
this.checkedDiseaseList = val
},
deep: true,
immediate: true,
},
},
mounted() {
},
methods: {
/**
* @description: 列表行绑定id
* @param {*} row
* @return {*}
*/
getRowKey(row) {
return row.snapshotId;
},
/**
* @description: 列表行删除事件
* @param {*} row
* @return {*}
*/
handleDelete(index) {
this.checkedDiseaseList.splice(index, 1);
},
/**
* @description: 取消
*/
cancel() {
this.$emit("cancel");
},
/**
* @description: 提交表单
*/
submitForm() {
this.$refs.editForm.validate((valid) => {
if (valid) {
addMaintenanceNotice(this.editForm).then(({ code, data }) => {
if (code === 200) {
this.$message.success("生成成功");
this.$emit("cancel");
} else {
this.$message.error(data);
}
});
}
});
},
/**
* @description: 查看地图的已选中数据
* @param {*}
* @return {*}
*/
viewMapSelected(tip) {
this.mapTip = tip
// 地图打点
if (!tip && this.checkedDiseaseList.length > 0) {
// 修改地图中心点位
const pointArray = this.checkedDiseaseList.map((item) => {
if (item.coordinates) {
return item.coordinates;
}
return [];
});
this.$nextTick(() => {
this.fitMapToPoints(pointArray);
this.drawPoint();
})
}
},
/**
* @description: 根据点位计算地图中心点
* @return {void}
*/
fitMapToPoints(points) {
if (points.length > 0) {
// 创建一个空的 extent
let extent = [Infinity, Infinity, -Infinity, -Infinity];
// 计算所有点的边界框extent
points.forEach((point) => {
extent = [
Math.min(extent[0], point[0]),
Math.min(extent[1], point[1]),
Math.max(extent[2], point[0]),
Math.max(extent[3], point[1]),
];
});
// 获取地图实例
const map = this.$refs.diseaseMap.instance.get("map");
// 使用 fit 方法根据边界框计算最佳缩放级别
map.getView().fit(extent, {
duration: 500, // 动画持续时间
padding: [10, 10, 10, 10], // 边缘填充
});
}
},
/**
* @description: 传回来的地图图层
* @param {number} zoom
* @return {void}
*/
getZoom(zoom) {
this.mapZoom = zoom;
if (this.clusters && zoom * 1 > 15) {
this.clusters.getSource().setDistance(0);
} else if (this.clusters && zoom * 1 <= 15) {
this.clusters.getSource().setDistance(100);
}
},
/**
* @description: 绘制地图点位
* @return {void}
*/
drawPoint() {
const features = [];
this.checkedDiseaseList.forEach((element) => {
// 修改坐标样式
const point = new Point(element.coordinates);
const feature = new Feature({
geometry: point,
data: element,
// 自己设置一个标识
type: "icon",
});
features.push(feature);
});
const clusterSource = new Cluster({
distance: this.mapZoom > 15 ? 0 : 100,
minDistance: this.mapZoom > 15 ? 0 : 100,
source: new VectorSource({
features,
}),
type: "Cluster",
});
this.clusters = new VectorLayer({
source: clusterSource,
name: "clusterLayer",
zIndex: 1,
style: (feature) => {
const count = feature.get("features").length;
if (count > 1) {
return new Style({
image: new Icon({
crossOrigin: "anonymous",
src: require(`@/assets/screen/index/龟裂.png`),
// 图标缩放比例
scale: 0.5,
displacement: [0, 30],
// 0.3为30度
// rotation: 0.3,
}),
text: new Text({
textAlign: "center", //位置
textBaseline: "middle",
font: "normal 14px 微软雅黑",
offsetY: -40,
fill: new Fill({
color: "black",
}),
text: count.toString(),
}),
});
} else {
return new Style({
image: new Icon({
crossOrigin: "anonymous",
src: require(`@/assets/screen/index/${feature.getProperties().features[0].get("data").defectTypeName
|| "龟裂"
}.png`),
// 图标缩放比例
scale: 0.5,
displacement: [0, 30],
// 0.3为30度
// rotation: 0.3,
}),
text: new Text({
textAlign: "center", //位置
textBaseline: "middle",
font: "normal 14px 微软雅黑",
offsetY: -40,
fill: new Fill({
color: "#ffffff",
}),
text: "",
}),
});
}
},
});
this.$nextTick(() => {
const map = this.$refs.diseaseMap.instance.get("map");
map.addLayer(this.clusters);
});
},
/**
* @description: 点击地图图标事件
* @param {*}
* @return {*}
*/
featureSelect(e) {
const map = this.$refs.diseaseMap.instance.get("map");
const selectedFeatures = e.selected;
if (selectedFeatures.length > 0) {
let feature = selectedFeatures[0];
let features = feature.get("features");
if (features.length === 1) {
// 单个点位
// 执行之前的业务逻辑
// 获取点击的图层信息
const selectFeature = feature.getProperties().features[0];
console.log(selectFeature, "点位");
// 获取点位数据
this.currentImageItem = selectFeature.get("data");
this.showImageDialog = true;
// console.log(selectFeature.get("data"));
} else {
// 聚合点
// 放大地图层级
map.getView().animate({
center: feature.getGeometry().getCoordinates(),
zoom: map.getView().getZoom() + 1,
});
}
}
},
/**
* @description: 选择病害弹窗
* @param {*}
* @return {*}
*/
viewSelected() {
this.diseaseVisible = true
},
/**
* @description: 获取选择的病害列表
* @param {*}
* @return {*}
*/
getCheckedList(value) {
this.checkedDiseaseList = value;
this.diseaseVisible = false;
},
/**
* @description: 选择病害弹窗关闭方法
* @param {*}
* @return {*}
*/
diseaseCancel() {
this.diseaseVisible = false
},
/**
* @description: 打开查看图片弹窗
* @param {*} item
* @return {*}
*/
showScreenImg(item) {
this.currentImageItem = item;
this.showImageDialog = true;
},
/**
* @description: 图片位置信息获取
* @param {*} val
* @return {*}
*/
updateRects() {
this.rects = [];
this.rectsItem = {};
const rects = this.currentImageItem?.rect?.split(",").map(Number) || [];
this.rects = [
{
left: rects[0],
top: rects[1],
width: rects[2],
height: rects[3],
},
];
},
/**
* @description: 图片红框位置
* @param {*} left
* @param {*} top
* @param {*} width
* @param {*} height
* @return {*}
*/
getRectStyle({ left, top, width, height }) {
const image = this.$refs.mainImage;
const container = this.$refs.imageContainer;
if (!image || !container) return {};
const scaleX = container.clientWidth / image.naturalWidth;
const scaleY = container.clientHeight / image.naturalHeight;
const scale = Math.min(scaleX, scaleY);
const renderedWidth = image.naturalWidth * scale;
const renderedHeight = image.naturalHeight * scale;
const offsetX = (container.clientWidth - renderedWidth) / 2;
const offsetY = (container.clientHeight - renderedHeight) / 2;
return {
position: "absolute",
left: `${left * scale + offsetX}px`,
top: `${top * scale + offsetY}px`,
width: `${width * scale}px`,
height: `${height * scale}px`,
border: "2px solid #FF0000",
boxSizing: "border-box",
};
},
/**
* @description: 关闭查看图片弹窗
* @param {*} val
* @return {*}
*/
imgCancel() {
if (this.$refs.diseaseMap) {
this.$refs.diseaseMap.removeSelectClick();
}
this.showImageDialog = false;
this.currentImageItem = {};
this.rects = [];
},
},
};
</script>
<style scoped lang="scss">
.edit-dialog {
width: 100%;
height: 100%;
}
.editForm {
.el-select {
width: 100%;
}
.content-box {
display: flex;
span {
font-size: 1.5rem;
line-height: 3rem;
}
.el-icon-circle-plus-outline {
color: #08ce29;
}
.el-icon-delete {
color: #f01515;
}
>div {
margin: 0.5rem 0;
}
}
}
/* 页脚 */
.dialog-footer {
padding: 1rem 0;
display: flex;
justify-content: flex-end;
}
/* 查看大图弹窗 */
.image-container {
position: relative;
width: 100%;
height: 80%;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
}
.image-container img {
max-width: 100%;
max-height: 100%;
object-fit: contain;
}
.rect-overlay {
position: absolute;
pointer-events: none;
border: 2px solid red;
}
// 图片信息
.rect-image {
width: 90%;
position: absolute;
top: 0;
font-size: 1.2rem;
color: #ffffff;
background-color: rgba(0, 0, 0, 0.5);
}
</style>