ly-front/src/components/myForm.vue

721 lines
23 KiB
Vue
Raw Normal View History

2025-07-08 14:33:58 +00:00
my-form
2025-03-31 15:26:29 +00:00
<template>
<div class="my-form">
<el-form
:model="ruleForm"
ref="ruleForm"
:label-width="labelWidth"
class="el-row demo-ruleForm"
:label-position="labelPosition"
>
<el-form-item
v-for="(item, index) in formList"
:key="index"
:label="item.label"
:prop="item.model"
:class="item.colWidth"
:rules="detail ? false : item.rules"
2025-04-02 14:38:27 +00:00
v-show="item.show === undefined ? true : item.show"
2025-03-31 15:26:29 +00:00
>
<!-- 输入框 -->
<template v-if="item.type === 'input'">
<el-input
:placeholder="item.placeholder"
clearable
:disabled="disabled || item.disabled"
v-model="ruleForm[`${item.model}`]"
:show-password="item.showPassword"
/>
</template>
<!-- 数字输入框 -->
<template v-if="item.type === 'inputNum'">
<el-input
v-model="ruleForm[`${item.model}`]"
:disabled="disabled || item.disabled"
onkeyup="this.value=this.value.replace(/\D/g,'')"
onafterpaste="this.value=this.value.replace(/\D/g,'')"
></el-input>
</template>
<!-- 下拉框 -->
<template v-if="item.type === 'select'">
<el-select
v-model="ruleForm[`${item.model}`]"
:placeholder="item.placeholder"
:disabled="disabled || item.disabled"
clearable
:collapse-tags="item.collapseTags"
:multiple="item.multiple"
style="width: 100%"
@change="changeSelect"
>
<el-option
v-for="(element, i) in item.options"
:label="element.label"
2025-06-25 18:07:23 +00:00
:value="element.key"
2025-03-31 15:26:29 +00:00
:key="i"
/>
</el-select>
</template>
<!-- 下拉框带图片 -->
<template v-if="item.type === 'selectImg'">
<el-select
v-model="ruleForm[`${item.model}`]"
:placeholder="item.placeholder"
:disabled="disabled || item.disabled"
clearable
:collapse-tags="item.collapseTags"
:multiple="item.multiple"
style="width: 100%"
@change="changeSelectImg(item)"
>
<el-option
v-for="(element, i) in item.options"
:label="element.label"
:value="`${element.key}`"
:key="i"
>
<template #default>
<img
:src="element.imageUrl"
style="width: 30px; height: 30px; vertical-align: middle"
/>
<span style="margin-left: 10px">{{ element.label }}</span>
</template>
</el-option>
</el-select>
</template>
<!-- 设备搜索下拉框 -->
<template v-if="item.type === 'selectDevice'">
<el-select
v-model="ruleForm[`${item.model}`]"
:placeholder="item.placeholder"
:disabled="disabled || item.disabled"
clearable
:collapse-tags="item.collapseTags"
:multiple="item.multiple"
style="width: 100%"
@change="changeSelectdevice(item.options)"
>
<el-option
v-for="(element, i) in item.options"
:label="element.label"
:value="`${element.key}`"
:key="i"
/>
</el-select>
</template>
<!-- 单选框 -->
<template v-if="item.type === 'radio'">
<el-radio-group
v-model="ruleForm[`${item.model}`]"
:disabled="disabled || item.disabled"
>
<el-radio
v-for="(element, i) in item.options"
:key="i"
:label="`${element.value}`"
>
{{ element.label }}
</el-radio>
</el-radio-group>
</template>
<!-- 级联选择(不跳转地图) -->
<template v-if="item.type === 'radiogroupQuery'">
<el-cascader
v-model="ruleForm[`${item.model}`]"
:options="item.options"
:props="item.props"
clearable
:show-all-levels="false"
popper-class="popper"
ref="cascader"
:disabled="disabled || item.disabled"
@change="changeCascaderQuery(item.options)"
>
<span
slot-scope="{ node, data }"
style="margin-left: -10px; padding-left: 10px; display: block"
@click="clickNode($event, node)"
>
{{ data.name }}
</span>
</el-cascader>
</template>
<!-- 级联和下拉联动 -->
<template v-if="item.type === 'radiogroupSelct'">
<el-cascader
v-model="ruleForm[`${item.model}`]"
:options="item.options"
:props="item.props"
clearable
:show-all-levels="false"
popper-class="popper"
ref="cascader"
:disabled="disabled || item.disabled"
@change="changeCascaderSelct(item.options)"
>
<span
slot-scope="{ node, data }"
style="margin-left: -10px; padding-left: 10px; display: block"
@click="clickNode($event, node)"
>
{{ data.name }}
</span>
</el-cascader>
</template>
<!-- 级联选择 -->
<template v-if="item.type === 'radiogroup'">
<el-cascader
v-model="ruleForm[`${item.model}`]"
:options="item.options"
:props="item.props"
clearable
:show-all-levels="false"
popper-class="popper"
ref="cascader"
@change="changeCascader"
:disabled="disabled || item.disabled"
>
<span
slot-scope="{ node, data }"
style="margin-left: -10px; padding-left: 10px; display: block"
@click="clickNode($event, node)"
>
{{ data.name }}
</span>
</el-cascader>
</template>
<!-- 复选框 -->
<template v-if="item.type === 'checkbox'">
<el-checkbox-group
v-model="ruleForm[`${item.model}`]"
:disabled="disabled || item.disabled"
>
<el-checkbox
v-for="(city, i) in item.cities"
:label="city"
:name="city"
:key="i"
>
{{ city }}
</el-checkbox>
</el-checkbox-group>
</template>
<!-- 开关 -->
<template v-if="item.type === 'switch'">
<el-switch
v-model="ruleForm[`${item.model}`]"
:disabled="disabled || item.disabled"
>
</el-switch>
</template>
<!--文本框 -->
<template v-if="item.type === 'textarea'">
<el-input
:maxlength="item.maxLenght"
type="textarea"
:rows="item.rowsHeight"
:disabled="disabled || item.disabled"
:placeholder="item.placeholder"
v-model="ruleForm[`${item.model}`]"
/>
</template>
<!-- 日期时间范围选择 -->
<template v-if="item.type === 'datetimerange'">
<el-date-picker
v-model="ruleForm[`${item.model}`]"
:value-format="item.format ? item.format : 'yyyy-MM-dd HH:mm:ss'"
:picker-options="item.pickerOptions"
:disabled="disabled || item.disabled"
type="datetimerange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
>
</el-date-picker>
</template>
<!-- 日期选择 -->
<template v-if="item.type === 'date'">
<el-date-picker
v-model="ruleForm[`${item.model}`]"
align="right"
style="width: 100%"
:value-format="item.format ? item.format : 'yyyy-MM-dd HH:mm:ss'"
:disabled="disabled || item.disabled"
type="date"
placeholder="选择日期"
:picker-options="item.pickerOptions"
>
</el-date-picker>
</template>
<!-- 日期范围选择 -->
<template v-if="item.type === 'daterange'">
<el-date-picker
v-model="ruleForm[`${item.model}`]"
:value-format="item.format ? item.format : 'yyyy-MM-dd HH:mm:ss'"
:picker-options="item.pickerOptions"
:disabled="disabled || item.disabled"
type="daterange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
>
</el-date-picker>
</template>
<!-- 时间选择 -->
<template v-if="item.type === 'datetime'">
<el-time-picker
v-model="ruleForm[`${item.model}`]"
:value-format="item.format ? item.format : 'HH:mm:ss'"
:disabled="disabled || item.disabled"
placeholder="选择时间"
:picker-options="item.pickerOptions"
>
</el-time-picker>
</template>
<!-- 时间范围选择 -->
<template v-if="item.type === 'datetimeisrange'">
<el-time-picker
is-range
v-model="ruleForm[`${item.model}`]"
:value-format="item.format ? item.format : 'HH:mm:ss'"
format="HH:mm"
:disabled="disabled || item.disabled"
:picker-options="item.pickerOptions"
range-separator="至"
start-placeholder="开始时间"
end-placeholder="结束时间"
placeholder="选择时间范围"
>
</el-time-picker>
</template>
<!-- 上传图片 -->
<template v-if="item.type === 'img'">
<el-upload
class="avatar-uploader"
2025-04-02 14:40:04 +00:00
:action="uploadUrl + '/api/uploadImg'"
2025-03-31 15:26:29 +00:00
:show-file-list="false"
:headers="{ Authorization: 'Bearer ' + token }"
:on-success="handleAvatarSuccess"
:before-upload="beforeAvatarUpload"
:on-change="handleAvatarChange"
:disabled="disabled || item.disabled"
>
<img
v-if="ruleForm.img"
2025-04-04 14:10:52 +00:00
:src="uploadUrl + ruleForm.img"
2025-03-31 15:26:29 +00:00
class="avatar"
id="imgAdaption"
/>
<i v-else class="el-icon-plus"></i>
</el-upload>
</template>
<!-- 地图轨迹查看 -->
<template v-if="item.type === 'marsMap'">
<my-mars></my-mars>
</template>
<!-- 地图区域选择 -->
<template v-if="item.type === 'marsMapmap'">
<my-marsmap
ref="myMarsmap"
:mapgeoJson="geojson.regionJson"
:mapData="geojson"
:typeOf="item.model"
:disabled="disabled || item.disabled"
></my-marsmap>
</template>
<template v-if="item.type === 'button'"> </template>
<template>
<div class="slot">
<slot :name="item.slot" :form="item" />
</div>
</template>
</el-form-item>
</el-form>
<div class="demo-drawer__footer" v-if="dialogImageUrl">
<el-button @click="handleClose('ruleForm')"> </el-button>
<el-button type="primary" v-if="!disabled" @click="determine('ruleForm')">
</el-button>
</div>
</div>
</template>
<script>
export default {
name: "myForm",
props: {
detail: {
type: Boolean,
default: () => false
},
disabled: {
type: Boolean,
default: () => false
},
//回显数据
fromItem: {
type: Object,
default: () => {}
},
labelWidth: {
type: String,
default: () => "85px"
},
// 表单数据
formNewList: {
type: Array,
default: () => []
},
labelPosition: {
type: String,
default: () => "right"
},
search: {
type: Boolean,
default: () => false
},
searchTwo: {
type: Boolean,
default: () => false
},
searchTree: {
type: Boolean,
default: () => false
},
searchFour: {
type: Boolean,
default: () => false
},
searchFive: {
type: Boolean,
default: () => false
},
dialogImageUrl: {
type: Boolean,
default: () => false
},
testConnectIon: {
type: Boolean,
default: () => false
},
// 设备点位数据
devicePositionData: {
type: Object,
default: () => {}
}
},
watch: {
formNewList: {
immediate: true, // 立即触发监听函数
handler() {
this.formList = this.formNewList;
// this.defaultFun();
}
},
fromItem: {
immediate: true,
deep: true,
handler(newVal) {
if (newVal) {
this.ruleForm = { ...newVal };
this.geojson = newVal;
if (this.$refs.ruleForm) {
this.$refs.ruleForm.resetFields();
}
if (newVal.geojson === undefined) {
if (this.$refs.myMarsmap !== undefined) {
2025-07-08 14:33:58 +00:00
// this.$refs.myMarsmap[0].clearDraw();
2025-03-31 15:26:29 +00:00
}
}
}
}
},
devicePositionData: {
handler(newVal) {
if (newVal) {
const intervalId = setInterval(() => {
if (this.$refs.myMarsmap) {
clearInterval(intervalId);
// this.$refs.myMarsmap[0].mapDevice(newVal.lon, newVal.lat);
}
}, 1000);
}
},
immediate: true,
deep: true
}
},
data() {
return {
2025-04-04 14:06:14 +00:00
url: process.env.VUE_APP_API_URL,
2025-03-31 15:26:29 +00:00
ruleForm: {},
formList: [],
languageList: [],
languageIds: [],
uploadUrl: "",
deviceManufacturer: [],
geojson: {},
token: ""
};
},
mounted() {
this.token = localStorage.getItem("token");
let wPath = window.document.location.href;
let pathName = this.$route.path;
let pos = wPath.indexOf(pathName);
let localhostPath = wPath.substring(0, pos);
this.uploadUrl =
process.env.VUE_APP_API_URL === "/"
? localhostPath
: process.env.VUE_APP_API_URL;
console.log(this.$refs.ruleForm, "this.$refs.ruleForm");
this.$emit("onMounted", this.ruleForm, this.$refs.ruleForm);
},
methods: {
clickNode($event) {
$event.target.parentElement.parentElement.firstElementChild.click();
},
// 下拉框下拉事件
changeSelect(row) {
this.$emit("changeSelect", row, this.ruleForm);
},
// 下拉框图片下拉事件
changeSelectImg(row) {
this.$emit("changeSelectImg", row, this.ruleForm);
},
// 下拉查询地图(设备)
changeSelectdevice(row) {
row.forEach((item) => {
if (item.key === this.ruleForm.deviceId) {
this.$refs.myMarsmap[0].mapJump(item.lon, item.lat);
// this.$refs.myMarsmap[0].mapDevice(item.lon, item.lat);
}
});
this.$emit(
"changeSelectdevice",
row,
this.ruleForm,
this.$refs.myMarsmap[0]
);
},
// 级联下拉联动
changeCascaderSelct(row) {
this.$emit("changeCascaderSelct", row, this.ruleForm);
this.$refs.cascader[0].dropDownVisible = false;
},
// 级联选择
changeCascaderQuery(row) {
console.log(row);
this.deviceManufacturer = row;
this.$emit("changeCascaderQuery", row, this.ruleForm);
if (this.$refs.cascader.length > 1) {
this.$refs.cascader.forEach((item) => {
item.dropDownVisible = false;
});
} else {
this.$refs.cascader[0].dropDownVisible = false;
}
},
deviceManufacturerQuery() {
return this.deviceManufacturer;
},
2025-04-12 15:15:32 +00:00
2025-03-31 15:26:29 +00:00
changeCascader(row) {
console.log(row);
},
// from表单点击事件
submitForm(value) {
this.$emit("headdenForm", this.ruleForm, value);
},
determine(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
let geojson = {
type: "MultiPolygon",
coordinates: []
};
if (this.$refs.myMarsmap) {
let toJson = this.$refs.myMarsmap[0].toGeoJSON();
2025-07-08 14:33:58 +00:00
if (toJson.features.length !== 0) {
console.log(toJson, "toJson");
// 多边形 首尾相同
geojson.coordinates = toJson.features.map((item) => {
if (item.properties.type === "polygon") {
// 确保 coordinates 是有效的多边形坐标(三维数组)
if (
Array.isArray(item.geometry.coordinates) &&
Array.isArray(item.geometry.coordinates[0]) &&
Array.isArray(item.geometry.coordinates[0][0])
) {
let coords = item.geometry.coordinates[0];
if (coords.length > 1) {
let firstCoord = coords[0];
let lastCoord = coords[coords.length - 1];
// 清理重复的尾坐标
while (
coords.length > 1 &&
lastCoord[0] === firstCoord[0] &&
lastCoord[1] === firstCoord[1]
) {
coords.pop();
// 更新 lastCoord
if (coords.length > 1) {
lastCoord = coords[coords.length - 1];
}
}
// 检查首尾坐标是否相同
if (
coords.length > 1 &&
(firstCoord[0] !== lastCoord[0] ||
firstCoord[1] !== lastCoord[1])
) {
// 如果不同,将首坐标添加到尾部
item.geometry.coordinates[0].push(firstCoord);
}
}
}
} else if (item.properties.type === "pointP") {
console.log(item.geometry.coordinates, "112233");
} else if (item.properties.type === "circle") {
const positions = mars3d.PolyUtil.getEllipseOuterPositions({
position: item.geometry.coordinates,
radius: item.properties.style.radius
});
item.geometry.coordinates = [[]];
positions.forEach((el) => {
2025-03-31 15:26:29 +00:00
item.geometry.coordinates[0].push(
mars3d.LngLatPoint.fromCartesian(el).toArray()
);
2025-07-08 14:33:58 +00:00
});
console.log(item.geometry.coordinates);
// 为圆添加首坐标到尾部以闭合
if (item.geometry.coordinates[0].length > 0) {
item.geometry.coordinates[0].push(
item.geometry.coordinates[0][0]
);
}
}
return item.geometry.coordinates;
});
2025-07-09 01:49:42 +00:00
// 清理 geojson.coordinates只保留第一个有效元素
if (
Array.isArray(geojson.coordinates) &&
geojson.coordinates.length > 1
) {
// 寻找第一个有效元素(点、多边形或圆)
const firstValidItem = geojson.coordinates.find(
(item) =>
Array.isArray(item) &&
// 点坐标:[lng, lat]
((item.length === 2 &&
typeof item[0] === "number" &&
typeof item[1] === "number") ||
// 多边形或圆坐标:三维数组
(Array.isArray(item[0]) && Array.isArray(item[0][0])))
);
// 如果找到有效元素,只保留它;否则置为空数组
geojson.coordinates = firstValidItem ? [firstValidItem] : [];
}
2025-07-08 14:33:58 +00:00
}
2025-03-31 15:26:29 +00:00
this.ruleForm.geoJson = geojson;
2025-04-13 08:23:44 +00:00
this.ruleForm.center = [
this.$refs.myMarsmap[0].lonData,
this.$refs.myMarsmap[0].latData
];
2025-03-31 15:26:29 +00:00
}
this.$emit("determine", this.ruleForm);
} else {
console.log("error submit!!");
return false;
}
});
},
handleClose() {
// this.$refs[formName].resetFields();
this.$emit("handleClose", false);
},
UnfoldAndfold(value) {
this.$emit("UnfoldAndfold", value);
},
toJson() {
return this.$refs.myMarsmap.toGeoJson();
},
handleAvatarSuccess(response, uploadFile) {
console.log(response, uploadFile);
if (response.code === 0) {
// this.ruleForm.imageBriefUrl = URL.createObjectURL(uploadFile.raw);
this.ruleForm.img = response.data;
}
},
beforeAvatarUpload(file) {
console.log(file);
let upload = document.querySelector(".el-upload");
// const MAX_WIDTH = upload.offsetWidth || 500; // 设置最大宽度
// const MAX_HEIGHT = upload.offsetHeight || 500; // 设置最大高度
const reader = new FileReader();
reader.onload = (e) => {
// const img = new Image();
// console.log(e.target.result, "e.target.result");
// img.src = e.target.result;
this.ruleForm.img = e.target.result;
// img.onload = () => {
// const canvas = document.createElement("canvas");
// const ctx = canvas.getContext("2d");
// // 计算缩放比例
// let width = img.width;
// let height = img.height;
// const aspectRatio = width / height;
// if (width > MAX_WIDTH) {
// width = MAX_WIDTH;
// height = width / aspectRatio;
// }
// if (height > MAX_HEIGHT) {
// height = MAX_HEIGHT;
// width = height * aspectRatio;
// }
// // 设置canvas尺寸
// canvas.width = width;
// canvas.height = height;
// // 绘制图片到canvas
// ctx.drawImage(img, 0, 0, width, height);
// // 将canvas转换为数据URL
// const dataUrl = canvas.toDataURL();
// // 在这里可以将dataUrl用于后续处理如上传等
// const imgAdaption = document.getElementById("imgAdaption");
// imgAdaption.style.width = "";
// imgAdaption.style.height = "";
// imgAdaption.style.width = `${width}px`;
// imgAdaption.style.height = `${height}px`;
// imgAdaption.src = dataUrl; // 更新img元素的src属性
// console.log(`图片宽度: ${width}, 图片高度: ${height}`);
// };
};
// 读取文件内容
reader.readAsDataURL(file);
return true;
},
handleAvatarChange(file) {
// this.ruleForm.imageBriefUrl = URL.createObjectURL(file.raw);
this.$forceUpdate();
}
}
};
</script>