dianlixunjian
徐景良 1 year ago
parent 683824e42e
commit a75ab14d47

@ -12,6 +12,9 @@
<link rel="icon" href="/favicon.ico" />
<link rel="stylesheet" href="hc/hc.css">
<link href="/public/mapboxgl/mapbox-gl.css" rel="stylesheet">
<script src="/public/mapboxgl/mapbox-gl.js"></script>
</head>
<body>
<div id="app">
@ -147,7 +150,7 @@
</style>
<div class="app-loading">
<div class="app-loading-wrap">
<img src="<%= VITE_GLOB_APP_LOGO %>" class="app-loading-logo" alt="Logo" />
<div class="app-loading-dots">
<span class="dot dot-spin"><i></i><i></i><i></i><i></i></span>
</div>

@ -1328,10 +1328,8 @@ e.setDisabled&&e.setDisabled(!1)}})},resetForm(){Object.keys(this.subFormRefList
submitForm() {
this.$refs.vFormRef.getFormData().then( (formData) => {
// Form Validation OK
alert( JSON.stringify(formData) )
}).catch( function(error) {
// Form Validation Failed
alert(error)
})
}
}
@ -1361,7 +1359,6 @@ e.setDisabled&&e.setDisabled(!1)}})},resetForm(){Object.keys(this.subFormRefList
const submitForm = () => {
vFormRef.value.getFormData().then(formData => {
// Form Validation OK
alert( JSON.stringify(formData) )
}).catch(error => {
// Form Validation failed
ElMessage.error(error)

@ -109,6 +109,7 @@
"mapbox-extensions":"^1.3.38",
"terraformer-wkt-parser": "^1.2.1",
"@terraformer/wkt":"2.1.2",
"proj4":"2.11.0",
"vue3-print-nb":"0.1.4",
"ceel-json-editor":"^0.0.3",
"@originjs/vite-plugin-commonjs":"^1.0.3",

@ -520,7 +520,6 @@
$(document).ready(function () {
var IMAGE_BASE_URL = "http://192.168.10.102:9023";
function lookImage() {
alert(1);
}
$('.pop-close').click(function () {
$('.bgPop2,.pop2').hide();

File diff suppressed because one or more lines are too long

@ -3054,7 +3054,6 @@
}), this.geolocationControl.addEventListener("locationSuccess", (function(e) {
r.getAddrByPoint(e.point)
})), this.geolocationControl.addEventListener("locationError", (function(e) {
alert(e.message)
})), this.map.addControl(this.geolocationControl), this.map.addEventListener("click", (function(e) {
r.getAddrByPoint(e.point)
})), this.map.enableScrollWheelZoom(!0), this.loading = !1, this.lng && this.lat && (i = new window.BMap.Point(this.lng, this.lat), this.setPoint(i));

Binary file not shown.

After

Width:  |  Height:  |  Size: 431 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 542 B

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -37,4 +37,5 @@
);
// Listening to page changes and dynamically changing site titles
useTitle();
</script>

@ -55,7 +55,8 @@ import { on } from '@/utils/domUtils';
let map:Map;
onMounted(() => {
mapboxgl.accessToken = "pk.eyJ1Ijoic2hpY2hhbzEyMyIsImEiOiJja3FobnI1aDEwNGF6Mm9vOXVhNnBzZmFhIn0.2fZKiMqCQHxVY74QShMEGQ"
// mapboxgl.accessToken = "pk.eyJ1Ijoic2hpY2hhbzEyMyIsImEiOiJja3FobnI1aDEwNGF6Mm9vOXVhNnBzZmFhIn0.2fZKiMqCQHxVY74QShMEGQ"
mapboxgl.accessToken = "123"
map = initMap();
map.on("load",function(){
refreshLocation();

@ -300,7 +300,7 @@
])
function handleBackRows(ModuleId) {
alert(ModuleId);
}
function dataBaseClick() {

@ -21,7 +21,7 @@
<!-- empty -->
<a-empty
v-if="dataList.length==0"
v-if="props.layerList.length==0"
image="https://gw.alipayobjects.com/mdn/miniapp_social/afts/img/A*pevERLJC9v0AAAAAAAAAAABjAQAAAQ/original"
:image-style="{
height: '60px',
@ -37,7 +37,7 @@
</a-empty>
<!-- layer list -->
<a-card hoverable style="width: 326px;margin-bottom:15px;" v-for="(item,index) in dataList" :key="index">
<a-card hoverable style="width: 326px;margin-bottom:15px;" v-for="(item,index) in props.layerList" :key="index">
<!-- <template #cover>
<img
alt="example"
@ -46,12 +46,12 @@
</template> -->
<template #actions>
<setting-outlined key="setting" />
<TableOutlined />
<DeleteOutlined />
<TableOutlined @click="getTableList(item)" />
<DeleteOutlined @click="removeLayer(item)" />
</template>
<a-card-meta :title="item.name" :description="item.description">
<template #avatar>
<a-checkbox v-model:checked="item.checked" />
<a-checkbox v-model:checked="item.checked" @change="handlerLayerShowChange(item)" />
<!-- <a-avatar src="https://joeschmoe.io/api/v1/random" /> -->
</template>
</a-card-meta>
@ -102,7 +102,7 @@
const isSelected = ref();
const emits = defineEmits(['changeOpenModal', 'changeOpenInsertShpModal','clearLayerList']);
const emits = defineEmits(['changeOpenModal', 'changeOpenInsertShpModal','clearLayerList','handlerLayerShowChange','handlerGetTableList']);
const changeClick = () => {
emits('changeOpenModal', true);
};
@ -116,6 +116,24 @@
const handlerClearLayerList = ()=>{
emits("clearLayerList")
}
const getTableList = (layer)=> {
emits("handlerGetTableList");
}
const removeLayer = (layer)=>{
layer.checked = false;
emits("handlerLayerShowChange",layer)
props.layerList?.forEach((item,index)=>{
if(item.id==layer.id){
props.layerList.splice(index,1);
}
})
}
const handlerLayerShowChange = (layer)=>{
emits("handlerLayerShowChange",layer)
}
</script>
<style lang="scss" scoped>

@ -25,6 +25,38 @@
<div class="draw-btn" @click="handlerCancleDraw"> </div>
<div class="draw-btn" @click="handlerDrawComplete"> </div>
</div>
<!-- 根据输入坐标定位 -->
<div class="position-by-lnglat">
<div class="to-location">
</div>
<div class="draw-polygon" @click="LocationShow = true">
</div>
</div>
<!-- -->
<div class="to-location-input" v-if="LocationShow">
<div class="location-operation">
<a-button type="default" size="small" @click="handlerPushLocationItem"><PlusOutlined />添加</a-button>
&nbsp;
<a-button type="default" size="small" @click="handlerClearLocationItem"><ClearOutlined />清空</a-button>
<span style="float:right;">
<CloseOutlined @click="handlerLocationClose" />
</span>
</div>
<a-empty v-if="locationArrays.length==0" />
<div class="location-item" v-for="(item,index) in locationArrays" :key="index">
<a-button type="default" size="small" @click="handlerLocationFlyTo(item)"><EnvironmentOutlined /></a-button>
<a-input v-model:value="item.lng" @chagne="handlerLocationChange" addon-before="" size="small" style="width:140px;margin:0px 12px;" />
<a-input v-model:value="item.lat" @chagne="handlerLocationChange" addon-before="" size="small" style="width:140px;margin-right:12px;" />
<a-button type="default" size="small" @click="handlerLocationRemove(index)"><DeleteOutlined /></a-button>
</div>
</div>
</div>
</template>
@ -40,8 +72,8 @@
} from 'vue';
import { useMessage } from '@/hooks/web/useMessage';
import mapboxgl, { Map, Popup } from 'mapbox-gl';
import {CloseOutlined,EnvironmentOutlined,DeleteOutlined,CopyOutlined,PlusOutlined,ClearOutlined} from "@ant-design/icons-vue"
import mapboxgl,{ Map, Popup } from 'mapbox-gl';
//
import MapboxDraw from '@mapbox/mapbox-gl-draw';
@ -356,7 +388,7 @@
language: 'zh-cmn',
projection: 'equirectangular', // wgs84
style: MapboxDefaultStyle,
maxZoom: props.mapConfig.maxZoom ? props.mapConfig.maxZoom:18,
// maxZoom: props.mapConfig.maxZoom ? props.mapConfig.maxZoom:18,
minZoom: props.mapConfig.minZoom ? props.mapConfig.minZoom:1,
zoom: props.mapConfig.zoom ? props.mapConfig.zoom:12,
pitch:props.mapConfig.angle ? props.mapConfig.angle:0,
@ -773,7 +805,204 @@
//
//
interface LocationItem {
lng:string;
lat:string;
}
const LocationShow = ref<Boolean>(false);
const locationArrays = ref<LocationItem[]>([]);
const locationGeoJson = reactive({
point:{
"type": "FeatureCollection",
"features": []
},
polyline:{
"type": "FeatureCollection",
"features": []
},
polygon:{
"type": "FeatureCollection",
"features": []
}
})
const handlerPushLocationItem = ()=>{
let item:LocationItem = {
lng:"",
lat:"",
}
locationArrays.value.push(item);
}
const handlerClearLocationItem = ()=>{
locationArrays.value = [];
locationGeoJson.point.features = [];
locationGeoJson.polygon.features = [];
locationGeoJson.polyline.features = [];
handlerLocationLoadLayer();
}
const handlerLocationClose = ()=>{
LocationShow.value = false;
}
const handlerLocationRemove = (index:number)=>{
locationArrays.value.splice(index,1);
handlerLocationGeoJson();
}
const handlerLocationChange = (e)=>{
}
//
const handlerLocationFlyTo = (item:LocationItem)=>{
if(item.lng&&item.lat){
handlerLocation([item.lng,item.lat]);
handlerLocationGeoJson();
}
}
// geojson
const handlerLocationGeoJson = ()=>{
locationGeoJson.point.features = [];
locationGeoJson.polyline.features = [];
locationGeoJson.polygon.features = [];
locationArrays.value?.forEach((item,index)=>{
if(item.lng && item.lat){
let feature = {
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [parseFloat(item.lng), parseFloat(item.lat)]
}
}
locationGeoJson.point.features.push(feature);
}
})
if(locationArrays.value?.length>=2){
let feature = {
"type": "Feature",
"geometry": {
"type": "LineString",
"coordinates": []
}
}
locationArrays.value?.forEach((item,index)=>{
if(item.lng && item.lat){
let coor = [parseFloat(item.lng), parseFloat(item.lat)];
feature.geometry.coordinates.push(coor);
}
})
feature.geometry.coordinates.push([parseFloat(locationArrays.value[0].lng),parseFloat(locationArrays.value[0].lat)]);
locationGeoJson.polyline.features[0] = feature
}
if(locationArrays.value?.length>=3){
let feature = {
"type": "Feature",
"geometry": {
"type": "MultiLineString",
"coordinates": [[]]
}
}
locationArrays.value?.forEach((item,index)=>{
if(item.lng && item.lat){
let coor = [parseFloat(item.lng), parseFloat(item.lat)];
feature.geometry.coordinates[0].push(coor);
}
})
feature.geometry.coordinates[0].push([parseFloat(locationArrays.value[0].lng),parseFloat(locationArrays.value[0].lat)]);
locationGeoJson.polygon.features[0] = feature
}
handlerLocationLoadLayer();
console.log("locationGeoJson",locationGeoJson);
}
// 线
const handlerLocationLoadLayer = ()=>{
//
if(map.getSource("LocationPointSource")){
map.getSource("LocationPointSource").setData(locationGeoJson.point);
}else{
map.addSource("LocationPointSource",{
type:"geojson",
data:locationGeoJson.point
})
map.addLayer(
{
"id": "LocationPointLayer",
"type": "circle",
"source": "LocationPointSource",
paint: {
'circle-radius': 5,
'circle-color': '#408eff' //
}
}
)
}
// 线
if(map.getSource("LocationPolylineSource")){
map.getSource("LocationPolylineSource").setData(locationGeoJson.polyline);
}else{
map.addSource("LocationPolylineSource",{
type:"geojson",
data:locationGeoJson.polyline
})
map.addLayer(
{
"id": "LocationLineLayer",
"type": "line",
"source": "LocationPolylineSource",
"paint": {
'line-color': '#408eff', // 线
'line-width': 2 // 线
}
}
)
}
//
if(map.getSource("LocationPolygonSource")){
map.getSource("LocationPolygonSource").setData(locationGeoJson.polygon);
}else{
map.addSource("LocationPolygonSource",{
type:"geojson",
data:locationGeoJson.polygon
})
map.addLayer(
{
"id": "LocationPolygonLayer",
"type": "fill",
"source": "LocationPolygonSource",
"paint": {
'fill-color': '#408eff', //
'fill-opacity': 0.5 //
}
}
)
}
}
//
const handlerLocationDrawPoint = ()=>{
}
// 线
const handlerLocationDrawLine = ()=>{
}
//
const handlerLocationDrawPolygon = ()=>{
}
defineExpose({
handlerDraw,
@ -930,5 +1159,62 @@
::v-deep .jas-ctrl-extend-desktop-container {
width:320px!important;
}
.position-by-lnglat{
width:29px;
height:29px;
background:#fff;
position:absolute;
top:10px;
right:131px;
border-radius:3px;
.to-location{
display:none;
width:29px;
height:29px;
float:left;
background:url(/public/map/location.png);
background-size:20px 20px;
background-repeat: no-repeat;
background-position: 4px 5px;
&:hover{
cursor:pointer;
}
}
.draw-polygon{
width:29px;
height:29px;
float:left;
background:url(/public/map/draw_polygon.png);
background-size:20px 20px;
background-repeat: no-repeat;
background-position: 4px 5px;
&:hover{
cursor:pointer;
}
}
}
.to-location-input{
padding:16px;
width:408px;
min-height:60px;
background: #fff;
position:absolute;
top:48px;
right:10px;
border-radius:3px;
.location-operation{
width:100%;
height:40px;
border-bottom:1px solid #f1f1f1;
margin-bottom:12px;
}
.location-item{
line-height:26px;
margin-bottom:6px;
}
}
</style>

@ -19,6 +19,8 @@
@changeOpenInsertShpModal="changeOpenInsertShpModal"
:layerList="layerList"
@clearLayerList="handlerClearLayerList"
@handlerLayerShowChange="handlerLayerControler"
@handlerGetTableList="handlerGetTableList"
/>
<div class="close-layer-workspace" @click="changeLayerList">
<MenuFoldOutlined v-if="showLayerList" />
@ -33,21 +35,23 @@
<!-- <AddLayer v-model:openModal="insertShpModal" /> -->
<!-- table data list -->
<DataListComponent v-if="false" />
<DataListComponent v-if="DataTableList" />
<!-- attribute -->
<RightShowInfo v-model:openModal="openRightInfo" />
<RightShowInfo :openModal="openRightInfo" />
</div>
</template>
<script lang="ts" setup>
import { onMounted, onUnmounted, defineProps, reactive, ref, defineExpose } from 'vue';
import { useMessage } from '@/hooks/web/useMessage';
import mapboxgl, { Map, Popup } from 'mapbox-gl';
import { Map, Popup } from 'mapbox-gl';
//
import MapboxDraw from '@mapbox/mapbox-gl-draw';
import U from 'mapbox-gl-utils';
import 'mapbox-gl/dist/mapbox-gl.css';
import './src/index.less';
@ -69,6 +73,16 @@
import AddLayer from './AddLayer/index.vue';
import DataListComponent from './DataListComponent/index.vue';
import RightShowInfo from './RightShowInfo/index.vue';
import proj4 from 'proj4';
// let fromProjection = "+proj=tmerc +lat_0=0 +lon_0=117 +k=1 +x_0=500000 +y_0=0 +ellps=GRS80 +units=m +no_defs +type=crs";
let fromProjection = "+proj=tmerc +lat_0=0 +lon_0=117 +k=1 +x_0=39500000 +y_0=0 +ellps=GRS80 +units=m +no_defs +type=crs"
// let toProjection = "+proj=longlat +ellps=GRS80 +no_defs +type=crs"
let toProjection = "+proj=longlat +datum=WGS84 +no_defs +type=crs";
// let coordinates = [39591762.54875996,3908085.3169043385];
let coordinates = [39592654.90645946,3909001.5344353705]
const converted = proj4(fromProjection, toProjection, coordinates);
const openModal = ref(false);
const insertShpModal = ref(false);
@ -81,7 +95,7 @@
};
// layer list
const showLayerList = ref<boolean>(true);
const showLayerList = ref<boolean>(false);
const changeLayerList = ()=>{
showLayerList.value = !showLayerList.value;
}
@ -93,9 +107,7 @@
}
const handlerClearLayerList = () => {
layerList.value?.forEach((item,index)=>{
layerList.value?.pop();
})
layerList.value = [];
}
// map
@ -130,8 +142,69 @@
onMounted(() => {
mapboxgl.accessToken = MapboxConfig.ACCESS_TOKEN;
map = initMap();
map.on('load', () => {
map.addLayer({
'id': 'wms-test-layer',
'type': 'raster',
'source': {
'type': 'raster',
'tiles': [
// "http://175.27.168.120:8080/geoserver/feixian/wms?SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&FORMAT=image%2Fjpeg&TRANSPARENT=true&LAYERS=feixian%3Ayingxiang_17&exceptions=application%2Fvnd.ogc.se_inimage&SRS=EPSG%3A4548&STYLES=&WIDTH=728&HEIGHT=768&BBOX=586801.5388788335%2C3908183.1422136417%2C590270.7583843566%2C3911848.0123377703",
// "http://175.27.168.120:8080/geoserver/feixian/wms?service=WMS&version=1.1.0&request=GetMap&layers=feixian:yingxiang_17&styles=&bbox={bbox-epsg-3857}&width=256&height=256&srs=EPSG:3857&format=image/png&TRANSPARENT=TRUE"
"http://60.213.14.14:8060/geoserver/feixian/wms?service=WMS&version=1.1.0&request=GetMap&layers=feixian:yingxiang&styles=&bbox={bbox-epsg-3857}&width=256&height=256&srs=EPSG:3857&format=image/png&TRANSPARENT=TRUE"
],
'tileSize': 256
},
'paint': {}
});
map.addSource('radar', {
'type': 'image',
// 'url': 'http://175.27.168.120:8080/geoserver/feixian/wms?SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&FORMAT=image%2Fjpeg&TRANSPARENT=true&LAYERS=feixian%3Ayingxiang_17&exceptions=application%2Fvnd.ogc.se_inimage&SRS=EPSG%3A4548&STYLES=&WIDTH=728&HEIGHT=768&BBOX=586801.5388788335%2C3908183.1422136417%2C590270.7583843566%2C3911848.0123377703',
// 'url':"http://175.27.168.120:8080/geoserver/feixian/wms?SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&FORMAT=image%2Fjpeg&TRANSPARENT=true&LAYERS=feixian%3Ayingxiang_17&exceptions=application%2Fvnd.ogc.se_inimage&SRS=EPSG%3A4548&STYLES=&WIDTH=727&HEIGHT=768&BBOX=591035.9133569683%2C3908525.08342436%2C591469.2675472646%2C3908982.893941982",
'url':"http://60.213.14.14:8060/geoserver/feixian/wms?SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&FORMAT=image%2Fjpeg&TRANSPARENT=true&LAYERS=feixian%3Ayingxiang&exceptions=application%2Fvnd.ogc.se_inimage&SRS=EPSG%3A4527&STYLES=&WIDTH=1024&HEIGHT=1024&BBOX=39591762.54875996%2C3908085.3169043385%2C39592654.90645946%2C3909001.5344353705",
'coordinates': [
// EPSG:4548
// [118.00090749819051,35.30579563806707],
// [118.0057224793592,35.30579563806707],
// [118.0057224793592,35.30170937770571],
// [118.00090749819051,35.30170937770571],
// EPSG:4527
// [118.00884630303432,35.3058545162185],
// [118.01875909202998,35.3058545162185],
// [118.01875909202998,35.29767983003392],
// [118.00884630303432,35.29767983003392],
[118.00884630303432,35.30585451621851],
[118.01875909202998,35.30585451621851],
[118.01875909202998,35.297679830033914],
[118.00884630303432,35.297679830033914],
// [39591762.54875996,3909001.5344353705],
// [39592654.90645946,3909001.5344353705],
// [39592654.90645946,3908085.3169043385],
// [39591762.54875996,3908085.3169043385],
]
});
map.addLayer({
id: 'radar-layer',
'type': 'raster',
'source': 'radar',
'paint': {
'raster-fade-duration': 0
}
});
//mapbox-gl-utils
// U.init(map);
mp = new MP(map);
@ -271,6 +344,39 @@
defineExpose({
handlerRenderLayer,
});
const handlerLayerControler = (layerInfo) => {
layerInfo.layer = layerInfo.layer? layerInfo.layer : JSON.parse(layerInfo.style);
if (map.getSource(layerInfo.layer.id)) {
if (layerInfo.checked) {
map.setLayoutProperty(layerInfo.layer.id, 'visibility', 'visible');
} else {
map.setLayoutProperty(layerInfo.layer.id, 'visibility', 'none');
}
} else {
map.addLayer(layerInfo.layer);
map.on('click', layerInfo.layer.id, function (e) {
handlerPreviewFeatureInfo(e);
});
}
};
//
const handlerPreviewFeatureInfo = (e) => {
if (e.features) {
openRightInfo.value = true;
}
};
const DataTableList = ref<Boolean>(false);
const handlerGetTableList = ()=>{
DataTableList.value = true;
}
</script>
<style scoped>

@ -1,5 +1,5 @@
<template>
<a-drawer class="right-show-info" placement="right" :open="open" @close="() => (open = false)">
<a-drawer class="right-show-info" placement="right" :open="props.openModal" @close="props.openModal = false">
<div class="title">
<div class="tag"></div>
<div class="title-text">操作</div>

@ -1,5 +1,6 @@
export enum MapboxConfig {
ACCESS_TOKEN = 'pk.eyJ1Ijoic2hpY2hhbzEyMyIsImEiOiJja3FobnI1aDEwNGF6Mm9vOXVhNnBzZmFhIn0.2fZKiMqCQHxVY74QShMEGQ',
// ACCESS_TOKEN = 'pk.eyJ1Ijoic2hpY2hhbzEyMyIsImEiOiJja3FobnI1aDEwNGF6Mm9vOXVhNnBzZmFhIn0.2fZKiMqCQHxVY74QShMEGQ',
ACCESS_TOKEN = "1234",
TDT_TOKEN = 'b6585bc41ee16251dbe6b1af64f375d9',
// add more config options here
}

@ -4,12 +4,8 @@
<div class="video-delete-btn" @click="handlerDelete(index)">
<CloseOutlined/>
</div>
<a-image
width="100px"
:src="item.url"
></a-image>
<img :src="item.url" width="100px" alt="" @click="handlerPreviewImage(item.url)" >
</div>
<Upload
v-bind="$attrs"
@ -36,8 +32,9 @@
</div>
</template>
<script lang="ts" setup>
import { ref, toRefs, watch,reactive, onMounted } from 'vue';
<script setup lang="ts">
import { ref, toRefs, watch,reactive, onMounted,provide,inject } from 'vue';
import { PlusOutlined,CloseOutlined } from '@ant-design/icons-vue';
import type { UploadFile, UploadProps,UploadChangeParam } from 'ant-design-vue';
import { Modal, Upload } from 'ant-design-vue';
@ -52,6 +49,11 @@
import { UploadResultStatus } from '@/components/Upload/src/types/typing';
import { actions } from '@/views/demo/page/account/center/data';
import { fileUploadApi } from '@/api/sys/upload';
import ImagePreview from "@/components/Upload/src/components/image_preview.vue";
import { userFormFileStore } from '@/store/modules/formFileUrl';
const formFileStore = userFormFileStore();
defineOptions({ name: 'ImageUpload' });
@ -264,6 +266,15 @@
// });
// return props.multiple ? list : list.length > 0 ? list[0] : '';
}
const globalImagePreviewUrl = ref<String>("");
function handlerPreviewImage(url):void{
globalImagePreviewUrl.value = url+"?"+Math.random();
formFileStore.setUrl(url+"?"+Math.random());
}
</script>
<style lang="less">
@ -326,4 +337,14 @@
width:500px;
margin:10px;
}
.image-preview{
width:100%;
height:100%;
position:absolute;
top:50%;
left:50%;
transform: translate(-50%,-50%);
z-index:999;
}
</style>

@ -208,7 +208,6 @@
async function handleStartUpload() {
const { maxNumber,action } = props;
alert(action);
if ((fileListRef.value.length + props.previewFileList?.length ?? 0) > maxNumber) {
return createMessage.warning(t('component.upload.maxNumber', [maxNumber]));
}

@ -0,0 +1,237 @@
<template>
<div class="image-preview-container" id="imagePreviewContainer">
<img id="zoomableImage" draggable="false" :src="imageUrl" />
<div class="operation-container">
<div>
<UndoOutlined />
</div>
<div>
<ZoomInOutlined @click="bigScale" />
</div>
<div>
<ZoomOutOutlined @click="smallScale"/>
</div>
<div>
<RedoOutlined @click="transform" />
</div>
<div>
<CloseOutlined @click="closePreview"/>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { ZoomInOutlined, ZoomOutOutlined, RedoOutlined, UndoOutlined, CloseOutlined } from '@ant-design/icons-vue';
import {ref,onMounted,defineEmits,inject,defineProps} from 'vue';
const emits = defineEmits(["closeImagePreview"])
const props = defineProps({
globalImagePreviewUrl:{
type:String
}
})
const imageUrl = props.globalImagePreviewUrl
let isDragging = false;
let initialMouseX;
let initialMouseY;
let initialImageX;
let initialImageY;
onMounted(()=>{
const draggableImage = document.getElementById('zoomableImage');
const imageContainer = document.getElementById('imagePreviewContainer');
const zoomableImage = document.getElementById("zoomableImage");
// zoomableImage.addEventListener('wheel', handleWheel);
var scale = 1;
var minScale = 0.5;
var maxScale = 3;
var scaleFactor = 0.1;
var containerRect = imageContainer.getBoundingClientRect();
imageContainer.addEventListener('wheel', function(event) {
event.preventDefault();
var mouseX = event.clientX - containerRect.left; // X
var mouseY = event.clientY - containerRect.top; // Y
var delta = -Math.max(-1, Math.min(1, (event.deltaY || -event.detail)));
var zoomFactor = Math.exp(delta * scaleFactor);
console.log("delta",delta,zoomFactor);
var oldWidth = zoomableImage.clientWidth;
var oldHeight = zoomableImage.clientHeight;
var newWidth = Math.min(Math.max(oldWidth * zoomFactor, containerRect.width * minScale), containerRect.width * maxScale);
var newHeight = Math.min(Math.max(oldHeight * zoomFactor, containerRect.height * minScale), containerRect.height * maxScale);
var widthDiff = newWidth - oldWidth;
var heightDiff = newHeight - oldHeight;
var mouseXPercent = mouseX / containerRect.width;
var mouseYPercent = mouseY / containerRect.height;
var translateX = widthDiff * mouseXPercent;
var translateY = heightDiff * mouseYPercent;
scale *= zoomFactor;
zoomableImage.style.transformOrigin = mouseXPercent * 100 + '% ' + mouseYPercent * 100 + '%';
zoomableImage.style.transform = 'scale(' + scale + ') translate(' + translateX + 'px, ' + translateY + 'px)';
});
draggableImage.addEventListener('mousedown', function(event) {
isDragging = true;
initialMouseX = event.clientX;
initialMouseY = event.clientY;
initialImageX = draggableImage.offsetLeft;
initialImageY = draggableImage.offsetTop;
draggableImage.style.cursor = 'grabbing';
});
document.addEventListener('mousemove', function(event) {
if (isDragging) {
const deltaX = event.clientX - initialMouseX;
const deltaY = event.clientY - initialMouseY;
draggableImage.style.left = initialImageX + deltaX + 'px';
draggableImage.style.top = initialImageY + deltaY + 'px';
}
});
document.addEventListener('mouseup', function() {
isDragging = false;
draggableImage.style.cursor = 'default';
});
})
function handleWheel(event) {
// event.preventDefault();
// const delta = Math.sign(event.deltaY);
// if (delta > 0) {
// if(scale > 0.2){
// scale -= scaleStep;
// }else if(scale>1.6){
// scale -= 0.2;
// }
// } else {
// if(scale>1.6){
// scale += 0.2;
// }else{
// scale += scaleStep;
// }
// }
// zoomableImage.style.transform = `scale(${scale})`;
const imageContainer = document.getElementById('imagePreviewContainer');
let containerRect = imageContainer.getBoundingClientRect();
event.preventDefault();
var mouseX = event.clientX - containerRect.left; // X
var mouseY = event.clientY - containerRect.top; // Y
var delta = Math.max(-1, Math.min(1, (event.deltaY || -event.detail)));
var zoomFactor = Math.exp(delta * scaleFactor);
var oldWidth = zoomableImage.clientWidth;
var oldHeight = zoomableImage.clientHeight;
var newWidth = Math.min(Math.max(oldWidth * zoomFactor, containerRect.width * minScale), containerRect.width * maxScale);
var newHeight = Math.min(Math.max(oldHeight * zoomFactor, containerRect.height * minScale), containerRect.height * maxScale);
var widthDiff = newWidth - oldWidth;
var heightDiff = newHeight - oldHeight;
var mouseXPercent = mouseX / containerRect.width;
var mouseYPercent = mouseY / containerRect.height;
var translateX = widthDiff * mouseXPercent;
var translateY = heightDiff * mouseYPercent;
scale *= zoomFactor;
zoomableImage.style.transformOrigin = mouseXPercent * 100 + '% ' + mouseYPercent * 100 + '%';
zoomableImage.style.transform = 'scale(' + scale + ') translate(' + translateX + 'px, ' + translateY + 'px)';
}
function smallScale():void {
if(scale>0.4){
scale-=0.4;
}
zoomableImage.style.transform = `scale(${scale})`;
}
function bigScale():void {
scale+=0.4;
zoomableImage.style.transform = `scale(${scale})`;
}
function closePreview():void{
emits("closeImagePreview");
}
</script>
<style type="less" scoped>
.image-preview-container{
position:absolute;
z-index:99999999;
top:0px;
left:0px;
width:100%;
height: 100%;
margin:0px auto;
background:rgba(0,0,0,0.4);
overflow: hidden;
#zoomableImage {
position:relative;
top:0px;
left:20%;
width:60%;
cursor:default;
display: block;
max-width: none;
max-height: none;
transition: transform 0.2s ease-out; /* 添加平滑过渡效果 */
}
.operation-container{
padding:0px 100px;
width: calc( 100% - 0px);
height:60px;
position:absolute;
bottom:0px;
left:0px;
background:rgba(0,0,0,0.3);
display: flex;
line-height: 60px;
color:#fff;
div {
flex:1;
text-align: center;
cursor:pointer;
}
}
}
</style>

@ -0,0 +1,145 @@
<template>
<div class="image-preview-container" id="imagePreviewContainer">
<img id="zoomableImage" draggable="false" src="http://192.168.10.102:9023//20240629\2024062914291178850041.jpg" />
<div class="operation-container">
<div>
<UndoOutlined />
</div>
<div>
<ZoomInOutlined @click="bigScale" />
</div>
<div>
<ZoomOutOutlined @click="smallScale"/>
</div>
<div>
<RedoOutlined @click="transform" />
</div>
<div>
<CloseOutlined @click="closePreview"/>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { ZoomInOutlined, ZoomOutOutlined, RedoOutlined, UndoOutlined, CloseOutlined } from '@ant-design/icons-vue';
import {ref,onMounted,defineEmits,} from 'vue';
const emits = defineEmits(["defineEmits"])
let scale = 1.0;
const scaleStep = 0.15;
let isDragging = false;
let initialMouseX;
let initialMouseY;
let initialImageX;
let initialImageY;
onMounted(()=>{
const zoomableImage = document.getElementById("zoomableImage");
zoomableImage.addEventListener('wheel', handleWheel);
const draggableImage = document.getElementById('zoomableImage');
const imageContainer = document.getElementById('imagePreviewContainer');
draggableImage.addEventListener('mousedown', function(event) {
isDragging = true;
initialMouseX = event.clientX;
initialMouseY = event.clientY;
initialImageX = draggableImage.offsetLeft;
initialImageY = draggableImage.offsetTop;
draggableImage.style.cursor = 'grabbing';
});
document.addEventListener('mousemove', function(event) {
if (isDragging) {
const deltaX = event.clientX - initialMouseX;
const deltaY = event.clientY - initialMouseY;
draggableImage.style.left = initialImageX + deltaX + 'px';
draggableImage.style.top = initialImageY + deltaY + 'px';
}
});
document.addEventListener('mouseup', function() {
isDragging = false;
draggableImage.style.cursor = 'default';
});
})
function handleWheel(event) {
event.preventDefault();
const delta = Math.sign(event.deltaY);
if (delta > 0) {
if(scale > 0.2){
scale -= scaleStep;
}else if(scale>1.6){
scale -= 0.2;
}
} else {
if(scale>1.6){
scale += 0.2;
}else{
scale += scaleStep;
}
}
zoomableImage.style.transform = `scale(${scale})`;
}
function smallScale():void {
if(scale>0.4){
scale-=0.4;
}
zoomableImage.style.transform = `scale(${scale})`;
}
function bigScale():void {
scale+=0.4;
zoomableImage.style.transform = `scale(${scale})`;
}
function closePreview():void{
emits("defineEmits");
}
</script>
<style type="less" scoped>
.image-preview-container{
position:absolute;
z-index:99999;
width:500px;
height:500px;
margin:0px auto;
background:rgba(0,0,0,0.1);
overflow: hidden;
}
#zoomableImage{
width:100%;
position:absolute;
cursor:default;
}
.operation-container{
padding:0px 100px;
width: calc( 100% - 200px);
height:60px;
position:absolute;
bottom:0px;
left:0px;
background:rgba(0,0,0,0.2);
display: flex;
line-height: 60px;
color:#fff;
}
.operation-container div{
flex:1;
text-align: center;
cursor:pointer;
}
</style>

@ -0,0 +1,46 @@
import type {
ProjectConfig,
HeaderSetting,
MenuSetting,
TransitionSetting,
MultiTabsSetting,
} from '#/config';
import type { BeforeMiniState, ApiAddress } from '#/store';
import { defineStore } from 'pinia';
import { store } from '@/store';
import { resetRouter } from '@/router';
import { deepMerge } from '@/utils';
interface FormFile {
url:String
}
export const userFormFileStore = defineStore({
id: 'formfileurl',
state: (): FormFile => ({
url:""
}),
getters: {
getUrl(state): boolean {
return state.url;
},
},
actions: {
setUrl(url:string): void {
this.url = url;
},
},
mutations:{
setUrl(url:string): void {
this.url = url;
},
}
});
// Need to be used outside the setup
export function useAppStoreWithOut() {
return userFormFileStore(store);
}

@ -46,6 +46,7 @@ interface PermissionState {
changeMenu: number;
}
export const usePermissionStore = defineStore({
id: 'app-permission',
state: (): PermissionState => ({

@ -4,12 +4,17 @@
-->
<template>
<div class="form-panel v-form-container">
<ImagePreview v-if="isShowImagePreview" :globalImagePreviewUrl="globalImagePreviewUrl" @closeImagePreview="closeImagePreview"></ImagePreview>
<Empty
class="empty-text"
v-show="formConfig.schemas.length === 0"
description="从左侧选择控件添加"
/>
<Form v-bind="formConfig">
<div class="image-preview" v-if="false">
<ImagePreview ></ImagePreview>
</div>
<div class="draggable-box">
<draggable
class="list-main ant-row"
@ -39,10 +44,14 @@
<script lang="ts">
import draggable from 'vuedraggable';
import LayoutItem from '../components/LayoutItem.vue';
import { defineComponent, computed } from 'vue';
import { defineComponent, computed,ref,inject,watch } from 'vue';
import { storeToRefs } from 'pinia';
import { cloneDeep } from 'lodash-es';
import { useFormDesignState } from '../../../hooks/useFormDesignState';
import { Form, Empty } from 'ant-design-vue';
import ImagePreview from "@/components/Upload/src/components/image_preview.vue";
import { userFormFileStore } from '@/store/modules/formFileUrl';
export default defineComponent({
name: 'FormComponentPanel',
@ -51,6 +60,7 @@
draggable,
Form,
Empty,
ImagePreview
},
emits: ['handleSetSelectItem'],
setup(_, { emit }) {
@ -83,11 +93,28 @@
return formConfig.value.layout === 'horizontal' ? 'Col' : 'div';
});
//
const isShowImagePreview = ref<Boolean>(false);
const closeImagePreview = ()=>{
isShowImagePreview.value = false;
}
const globalImagePreviewUrl=ref<String>();
const formFileStore = userFormFileStore();
const formFileState = storeToRefs(formFileStore);
watch(formFileState.url, (newValue, oldValue) => {
isShowImagePreview.value = true;
globalImagePreviewUrl.value = newValue;
});
return {
addItem,
handleDragStart,
formConfig,
layoutTag,
closeImagePreview,
isShowImagePreview,
globalImagePreviewUrl
};
},
});

@ -7,6 +7,15 @@
<a-button type="primary" @click="closePage" class="ml-2" danger>关闭 </a-button>
</div>
<div class="maper-container" v-if="mapConfig?.isShowMap">
<MapboxMap
:mapConfig="mapConfig"
@handlerDrawComplete="handlerDrawComplete"
@mapOnLoad="onMapboxLoad"
ref="MapboxComponent"
/>
</div>
<div :class="mapConfig?.isShowMap ? 'form-container' : ''">
<a-layout>
<a-layout>
@ -22,6 +31,8 @@
v-if="formVisble"
>
<div>
<ImagePreview style="width:100%;height:" v-if="isShowImagePreview" :globalImagePreviewUrl="globalImagePreviewUrl" @closeImagePreview="closeImagePreview"></ImagePreview>
<FormViewer
ref="formBoxRef"
:formConfig="formConfig"
@ -85,7 +96,7 @@
</a-tab-pane>
</a-tabs>
</a-layout-content>
<a-layout-footer :style="footerStyle">
<div :style="footerStyle">
<a-tabs v-if="props.isRead == 0" v-model:activeKey="auditName">
<a-tab-pane key="audit" :tab="auditTitleVal">
<div class="approval-column">
@ -121,20 +132,10 @@
</div>
</a-tab-pane>
</a-tabs>
</a-layout-footer>
</div>
</a-layout>
</a-layout>
</div>
<div class="maper-container" v-if="mapConfig?.isShowMap">
<MapboxMap
:mapConfig="mapConfig"
@handlerDrawComplete="handlerDrawComplete"
@mapOnLoad="onMapboxLoad"
ref="MapboxComponent"
/>
</div>
<!-- 节点记录信息 -->
<div class="info-box" v-if="designerData.nodeLogs.length > 0">
<a-drawer v-model:open="infoOpen" class="custom-class" title="记录信息" placement="right">
@ -199,6 +200,10 @@
import { flowStore } from '@/store/modules/flow';
import { functionsaveForm, LoadFormScheme } from '@/api/demo/formScheme';
import { FormViewer } from '@/components/FormViewer';
import { storeToRefs } from 'pinia';
import ImagePreview from "@/components/Upload/src/components/image_preview.vue";
import { userFormFileStore } from '@/store/modules/formFileUrl';
import {
designerDataType,
logsType,
@ -314,11 +319,11 @@
overFlow: 'auto',
color: '#fff',
backgroundColor: '#ffffff',
zIndex: '9999999999',
zIndex: '123',
padding: '0px 100px',
position: 'fixed',
bottom: '0px',
left: '0px',
right: '0px',
});
function changeActive(activeKey) {
@ -873,6 +878,19 @@
}
}
}
//
const isShowImagePreview = ref<Boolean>(false);
const closeImagePreview = ()=>{
isShowImagePreview.value = false;
}
const globalImagePreviewUrl=ref<String>();
const formFileStore = userFormFileStore();
const formFileState = storeToRefs(formFileStore);
watch(formFileState.url, (newValue, oldValue) => {
isShowImagePreview.value = true;
globalImagePreviewUrl.value = newValue;
});
onBeforeMount(() => {
getTaskInfo();
});
@ -981,4 +999,6 @@
.l-from-body {
padding: 10px 30px;
}
</style>

@ -9,101 +9,107 @@
>关闭
</a-button>
</div> -->
<a-layout>
<div class="maper-container" v-if="mapConfig?.isShowMap">
<MapboxMap
:mapConfig="mapConfig"
@handlerDrawComplete="handlerDrawComplete"
@mapOnLoad="onMapboxLoad"
ref="MapboxComponent"
/>
</div>
<div :class="mapConfig?.isShowMap ? 'form-container' : ''">
<a-layout>
<a-layout-content>
<a-tabs v-model:activeKey="activeName" @change="changeActive" type="card" >
<a-tab-pane
key="form"
:tab="
designerData.formCurrentNode.formTitle
? designerData.formCurrentNode.formTitle
: '表单信息'
"
v-if="formVisble"
>
<div :class="mapConfig?.isShowMap ? 'form-container' : ''">
<FormViewer
ref="formBoxRef"
:formConfig="formConfig"
:processId="designerData.process.id"
:formVerison="designerData.formCurrentNode.formVerison"
:formRelationId="designerData.formCurrentNode.formRelationId"
:instanceInfo="designerData.process.instanceInfo"
:issueId="designerData.formCurrentNode.issueId"
:isDetail="true"
@getFormSuccess="getFormSuccess"
v-if="formVisble"
/>
</div>
<div class="maper-container" v-if="mapConfig?.isShowMap">
<MapboxMap
:mapConfig="mapConfig"
@handlerDrawComplete="handlerDrawComplete"
@mapOnLoad="onMapboxLoad"
ref="MapboxComponent"
/>
</div>
</a-tab-pane>
<a-tab-pane key="flow" tab="流程信息" force-render>
<div class="process-design" :style="'display: flex; height:' + designerData.height">
<process-viewer
v-if="processVisble"
:key="`designer-${id}`"
:events="['element.click']"
@element-click="elementClick"
:xml="flowContent"
:flowViewer="flowViewer"
/>
</div>
</a-tab-pane>
<a-tab-pane key="record" tab="流转记录" force-render>
<div :style="'padding:10px 0 40px 0;overflow:auto; height:' + designerData.height">
<a-timeline>
<a-timeline-item
v-for="(item, index) in designerData.logs"
:key="index"
:color="item.type"
>
<div class="title">{{ item.time }}</div>
<a-card hoverable size="small">
<div class="type-title">{{ item.name }}</div>
<div class="content">
<span
class="link"
v-for="(userName, index2) in item.userNames"
:key="index2"
>{{ userName }}</span
>
{{ item.des }}
</div>
</a-card>
</a-timeline-item>
</a-timeline>
</div>
</a-tab-pane>
</a-tabs>
</a-layout-content>
<a-layout-footer v-if="designerData.userLogs.length > 0">
<a-tabs v-model:activeKey="auditName">
<a-tab-pane key="audit" tab="审批信息">
<div class="approval-info">
<auditInfo :data="designerData.userLogs" />
</div>
</a-tab-pane>
</a-tabs>
</a-layout-footer>
<!-- <a-layout-sider :style="footerStyle" v-if="designerData.userLogs.length > 0">
<a-tabs v-model:activeKey="auditName">
<a-tab-pane key="audit" tab="审批信息">
<div class="approval-info">
<auditInfo :data="designerData.userLogs" />
</div>
</a-tab-pane>
</a-tabs>
</a-layout-sider> -->
<a-layout>
<a-layout-content>
<a-tabs v-model:activeKey="activeName" @change="changeActive" type="card" >
<a-tab-pane
key="form"
:tab="
designerData.formCurrentNode.formTitle
? designerData.formCurrentNode.formTitle
: '表单信息'
"
v-if="formVisble"
>
<div>
<ImagePreview v-if="isShowImagePreview" :globalImagePreviewUrl="globalImagePreviewUrl" @closeImagePreview="closeImagePreview"></ImagePreview>
<FormViewer
ref="formBoxRef"
:formConfig="formConfig"
:processId="designerData.process.id"
:formVerison="designerData.formCurrentNode.formVerison"
:formRelationId="designerData.formCurrentNode.formRelationId"
:instanceInfo="designerData.process.instanceInfo"
:issueId="designerData.formCurrentNode.issueId"
:isDetail="true"
@getFormSuccess="getFormSuccess"
v-if="formVisble"
/>
</div>
</a-tab-pane>
<a-tab-pane key="flow" tab="流程信息" force-render>
<div class="process-design" :style="'display: flex; height:' + designerData.height">
<process-viewer
v-if="processVisble"
:key="`designer-${id}`"
:events="['element.click']"
@element-click="elementClick"
:xml="flowContent"
:flowViewer="flowViewer"
/>
</div>
</a-tab-pane>
<a-tab-pane key="record" tab="流转记录" force-render>
<div :style="'padding:10px 0 40px 0;overflow:auto; height:' + designerData.height">
<a-timeline>
<a-timeline-item
v-for="(item, index) in designerData.logs"
:key="index"
:color="item.type"
>
<div class="title">{{ item.time }}</div>
<a-card hoverable size="small">
<div class="type-title">{{ item.name }}</div>
<div class="content">
<span
class="link"
v-for="(userName, index2) in item.userNames"
:key="index2"
>{{ userName }}</span
>
{{ item.des }}
</div>
</a-card>
</a-timeline-item>
</a-timeline>
</div>
</a-tab-pane>
</a-tabs>
</a-layout-content>
<div v-if="designerData.userLogs.length > 0" :style="footerStyle">
<a-tabs v-model:activeKey="auditName">
<a-tab-pane key="audit" tab="审批信息">
<div class="approval-info">
<auditInfo :data="designerData.userLogs" />
</div>
</a-tab-pane>
</a-tabs>
</div>
<!-- <a-layout-sider :style="footerStyle" v-if="designerData.userLogs.length > 0">
<a-tabs v-model:activeKey="auditName">
<a-tab-pane key="audit" tab="审批信息">
<div class="approval-info">
<auditInfo :data="designerData.userLogs" />
</div>
</a-tab-pane>
</a-tabs>
</a-layout-sider> -->
</a-layout>
</a-layout>
</a-layout>
</div>
<!-- 节点记录信息 -->
<div class="info-box" v-if="designerData.nodeLogs.length > 0">
@ -132,7 +138,7 @@
</template>
<script lang="ts" setup>
import { ref, reactive, onBeforeMount, defineAsyncComponent } from 'vue';
import { ref, reactive, onBeforeMount, defineAsyncComponent,watch } from 'vue';
import { FormViewer } from '@/components/FormViewer';
import { ProcessViewer } from '@/components/ProcessViewer';
import { PageWrapper } from '@/components/Page';
@ -143,6 +149,9 @@
import { auditInfo } from './page';
import { getGeom } from '@/api/sys/layerManagement';
import { useMessage } from '@/hooks/web/useMessage';
import { storeToRefs } from 'pinia';
import ImagePreview from "@/components/Upload/src/components/image_preview.vue";
import { userFormFileStore } from '@/store/modules/formFileUrl';
const { createMessage } = useMessage();
const MapboxMap = defineAsyncComponent(() => import('@/components/MapboxMaps/MapComponent.vue'));
@ -206,6 +215,19 @@
});
const activeName = ref('form');
const auditName = ref('audit');
const footerStyle = ref({
height: '220px',
width: '100%',
overFlow: 'auto',
color: '#fff',
backgroundColor: '#ffffff',
zIndex: '123',
padding: '0px 0px',
position: 'fixed',
bottom: '0px',
right: '0px',
});
function changeActive(activeKey) {
if (activeKey == 'flow') {
processVisble.value = true;
@ -248,10 +270,12 @@
if (auditNode.isInherit) {
currentNode = wfData.find((t) => t.type == 'bpmn:StartEvent');
mapConfig.value = currentNode.mapConfig;
footerStyle.value.width = mapConfig.value?.isShowMap ? '60%' : '100%';
} else {
currentNode = auditNode;
var currentMapNode = wfData.find((t) => t.type == 'bpmn:StartEvent');
mapConfig.value = currentMapNode.mapConfig;
footerStyle.value.width = mapConfig.value?.isShowMap ? '60%' : '100%';
}
}
if (currentNode.authFields.length > 0) {
@ -488,6 +512,20 @@
function onMapboxLoad() {
}
//
const isShowImagePreview = ref<Boolean>(false);
const closeImagePreview = ()=>{
isShowImagePreview.value = false;
}
const globalImagePreviewUrl=ref<String>();
const formFileStore = userFormFileStore();
const formFileState = storeToRefs(formFileStore);
watch(formFileState.url, (newValue, oldValue) => {
isShowImagePreview.value = true;
globalImagePreviewUrl.value = newValue;
});
onBeforeMount(() => {
getDetailInfo();
});

Loading…
Cancel
Save