1.添加样式配置页面,添加相关业务逻辑,未连接接口;

This commit is contained in:
zhangquan 2025-05-30 17:27:47 +08:00
parent 46885a917f
commit aa2b55ac01
23 changed files with 1914 additions and 0 deletions

View File

@ -0,0 +1,85 @@
import request from '@/utils/request'
import { parseStrEmpty } from "@/utils/ruoyi";
import { createUniqueString } from "@/utils/index";
const tempTypeList = [
{ value: 'point', label: '点' },
{ value: 'polyline', label: '线' },
{ value: 'polygon', label: '面' },
{ value: 'billboard', label: '图标' },
{ value: 'label', label: '标签' },
]
const tempList = []
// 查询样式类型信息
export function getStyleType() {
return Promise.resolve(tempTypeList)
}
// 查询样式列表
export function listStyle(query) {
const list = tempList.filter(item => {
if(query.styleName && item.styleName.indexOf(query.styleName) === -1) {
return false
}
if(query.styleType && item.styleType.indexOf(query.styleType) === -1) {
return false
}
return true
})
return Promise.resolve({
rows: list,
total: list.length,
})
}
// 查询样式详细
export function getStyle(styleId) {
return Promise.resolve({
data: tempList.find(item => item.id === parseStrEmpty(styleId))
})
}
// 新增样式
export function addStyle(data) {
data.id = createUniqueString()
tempList.push(data)
return Promise.resolve({
code: 200,
msg: '新增成功',
data: data
})
}
// 修改样式
export function updateStyle(data) {
for (const v of tempList) {
if (v.id === data.id) {
v.styleName = data.styleName
v.styleType = data.styleType
v.style = data.style
break
}
}
return Promise.resolve({
code: 200,
msg: '修改成功',
})
}
// 删除样式
export function delStyle(styleId) {
let idList = styleId
if(typeof styleId === 'string') {
idList = [styleId]
}
for (let index = tempList.length - 1; index >= 0; index--) {
if(idList.includes(tempList[index].id)) {
tempList.splice(index, 1)
}
}
return Promise.resolve({
code: 200,
msg: '删除成功',
})
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 866 B

View File

@ -16,6 +16,7 @@ const props = defineProps({
}
}
})
const emits = defineEmits(['init'])
let viewer = null;
@ -91,6 +92,8 @@ const initMap = async () => {
//
viewer.cesiumWidget.screenSpaceEventHandler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK);
emits('init', viewer);
}
//
@ -177,6 +180,10 @@ const mapSetView = (options) => {
//
const setModel = async (options) => {
if (!options.modelList || options.modelList.length === 0) {
return;
}
// 3d
const tilesetOption = {
maximumScreenSpaceError: 1, //

View File

@ -0,0 +1,32 @@
import * as Cesium from "cesium";
const getCenterPosition = (positions) => {
if(positions.length === 0) {
console.warn("positions is empty");
return Cesium.Cartesian3.ZERO;
}
let maxX = Cesium.Math.toRadians(-180),
minX = Cesium.Math.toRadians(180);
let maxY = Cesium.Math.toRadians(-90),
minY = Cesium.Math.toRadians(90);
for (let i = 0; i < positions.length; i++) {
const cartographic = Cesium.Cartographic.fromCartesian(positions[i]);
if (cartographic.longitude < minX) {
minX = cartographic.longitude;
} else if (cartographic.longitude > maxX) {
maxX = cartographic.longitude;
}
if (cartographic.latitude < minY) {
minY = cartographic.latitude;
} else if (cartographic.latitude > maxY) {
maxY = cartographic.latitude;
}
}
return Cesium.Cartesian3.fromRadians((minX + maxX) / 2, (minY + maxY) / 2)
};
export {
getCenterPosition
}

View File

@ -0,0 +1,167 @@
import * as Cesium from "cesium";
import { getCenterPosition } from "./calcFn";
const createFeature = (style, position, text, layer) => {
if (!style) {
return null;
}
switch (style.properties?.type) {
case "point":
case "billboard":
case "label":
return createPointFeature(style, position, text, layer);
case "polyline":
return createPolylineFeature(style, position, text, layer);
case "polygon":
return createPolygonFeature(style, position, text, layer);
default:
return null;
}
};
/**
* 生成点要素
* 目前图标标签没有额外操作故共享该方法
* @param {Object} style 处理后的样式对象
* @param {Cesium.Cartesian3} position 点位置
* @param {String} text 标签文本可选
* @param {Cesium.DataSource} layer 图层
* @returns {Cesium.Entity} 点对象
*/
const createPointFeature = (style, position, text, layer) => {
if (!position) {
return null;
}
const entity = new Cesium.Entity({
position: position,
...cloneStyle(style),
});
// 添加标签文本
// if(text !== null && text !== undefined) {
// if(Cesium.defined(entity.label)) {
// entity.label.text = new Cesium.ConstantProperty(text)
// } else {
// entity.label = new Cesium.LabelGraphics({
// text,
// font: '16px sans-serif',
// })
// }
// }
if (Cesium.defined(entity.label) && text !== null && text !== undefined) {
entity.label.text = new Cesium.ConstantProperty(text);
}
// 加入图层
if (layer) {
layer.entities.add(entity);
}
return entity;
};
/**
* 生成线要素
* @param {Object} style 处理后的样式对象
* @param {Array<Cesium.Cartesian3>} position 位置数组
* @param {String} text 标签文本可选
* @param {Cesium.DataSource} layer 图层
* @returns {Cesium.Entity} 线对象
*/
const createPolylineFeature = (style, position, text, layer) => {
if (!position || position.length < 2) {
return null;
}
const entity = new Cesium.Entity(cloneStyle(style));
// 添加线坐标
if (Cesium.defined(style.polyline)) {
entity.polyline.positions = new Cesium.ConstantProperty(
style.properties.closed ? [...position, position[0]] : position
);
} else {
entity.polyline = new Cesium.PolylineGraphics({
positions: position,
});
}
// 添加标签文本
if (Cesium.defined(entity.label) && text !== null && text !== undefined) {
entity.label.text = new Cesium.ConstantProperty(text)
// 添加label坐标
entity.position = getCenterPosition(position)
}
// 加入图层
if (layer) {
layer.entities.add(entity);
}
return entity;
};
/**
* 生成面要素
* @param {Object} style 处理后的样式对象
* @param {Array<Cesium.Cartesian3>} position 位置数组
* @param {String} text 标签文本可选
* @param {Cesium.DataSource} layer 图层
* @returns {Cesium.Entity} 面对象
*/
const createPolygonFeature = (style, position, text, layer) => {
if (!position || position.length < 3) {
return null;
}
const entity = new Cesium.Entity(cloneStyle(style));
// 添加面坐标
if (Cesium.defined(style.polygon)) {
entity.polygon.hierarchy = new Cesium.PolygonHierarchy(position);
} else {
entity.polyline = new Cesium.PolygonGraphics({
hierarchy: new Cesium.PolygonHierarchy(position),
});
}
// 添加轮廓线坐标
// 这里消极的判断仅当存在polyline时才添加且必定是闭合线
if (Cesium.defined(style.polyline)) {
entity.polyline.positions = new Cesium.ConstantProperty([
...position,
position[0],
]);
}
// 添加标签文本
if (Cesium.defined(entity.label) && text !== null && text !== undefined) {
entity.label.text = new Cesium.ConstantProperty(text)
// 添加label坐标
entity.position = getCenterPosition(position)
}
// 加入图层
if (layer) {
layer.entities.add(entity)
}
return entity;
};
const cloneStyle = (style) => {
const clone = {};
for (let key in style) {
console.log(key);
if (key === "id") {
clone[key] = style[key];
} else if (key === "properties") {
// 浅拷贝属性
clone[key] = {};
for (let prop in style[key]) {
clone[key][prop] = style[key][prop];
}
} else {
// 用cesium提供方法拷贝
clone[key] = style[key].clone();
}
}
return clone;
};
export {
createFeature,
createPointFeature,
createPolylineFeature,
createPolygonFeature,
};

View File

@ -0,0 +1,330 @@
import * as Cesium from 'cesium';
let _clampToGround = false;
const getStyle = (options) => {
// 将配置项转为cesium内部类提供复用
const _options = {}
// 判断类型option提供了则套用未提供测推断
if(options.type) {
_options.type = options.type
} else if(options.point) {
_options.type = 'point'
} else if(options.polygon) {
_options.type = 'polygon'
} else if(options.polyline) {
_options.type = 'polyline'
} else if(options.billboard) {
_options.type = 'billboard'
} else if(options.label) {
_options.type = 'label'
} else {
// 什么配置都没提供,则返回空对象
return _options
}
// 转通用配置
const common = parseCommonOptions(_options.type, options.common);
// 属性配置项
const property = options.property || {};
property.type = _options.type;
switch (_options.type) {
case 'point':
return parsePointOptions(options, common, property)
case 'polyline':
return parsePolylineOptions(options, common, property)
case 'polygon':
return parsePolygonOptions(options, common, property)
case 'billboard':
return parseBillboardOptions(options, common, property)
case 'label':
return parseLabelOptions(options, common, property)
default:
console.warn('不支持的样式类型:', _options.type);
return {}
}
}
// 解析通用配置
const parseCommonOptions = (type, options) => {
const _options = {}
if(!options) {
return _options
}
// 贴地
if(options.clampToGround) {
_clampToGround = true;
}
// 按视距缩放
// 线和面没有按视距缩放
if(options.scaleByDistance && type !== 'polyline' && type !== 'polygon') {
_options.scaleByDistance = new Cesium.NearFarScalar(
options.scaleNear,
options.scaleNearValue,
options.scaleFar,
options.scaleFarValue
);
}
return _options
}
/**
* 解析点相关配置为对象
* @param {Object} options 总配置项
* @param {Object} common 通用配置对象
* @param {Object} property 属性配置对象
* @returns 点样式对象
*/
const parsePointOptions = (options, common, property) => {
const style = {
point: createPointByOptions(options.point, common),
properties: property || {}
}
// 添加图标、标签
if(options.billboard) {
style.billboard = createBillboardByOptions(options.billboard, common)
}
if(options.label) {
style.label = createLabelByOptions(options.label, common)
}
return style
}
/**
* 解析点配置项
* @param {Object} options 点配置项
* @param {Object} common 通用配置对象
* @returns cesium点对象
*/
const createPointByOptions = (options, common) => {
if(!options) {
return undefined
}
const _options = {
pixelSize: parseFloat(options.pixelSize) || 10,
color: Cesium.Color.fromCssColorString(options.color) || Cesium.Color.WHITE,
...common
}
if(options.showOutline) {
_options.outlineWidth = parseFloat(options.outlineWidth) || 1
_options.outlineColor = Cesium.Color.fromCssColorString(options.outlineColor) || Cesium.Color.BLACK
}
if(_clampToGround) {
_options.heightReference = Cesium.HeightReference.CLAMP_TO_GROUND
}
return new Cesium.PointGraphics(_options)
}
/**
* 解析线相关配置为对象
* @param {Object} options 总配置项
* @param {Object} common 通用配置对象
* @param {Object} property 属性配置对象
* @returns 线样式对象
*/
const parsePolylineOptions = (options, common, property) => {
const style = {
polyline: createPolylineByOptions(options.polyline, common),
properties: property || {}
}
// 添加闭合属性
if(options.polyline.closed) {
property.closed = true
}
// 添加标签
if(options.label) {
style.label = createLabelByOptions(options.label, common)
}
return style
}
/**
* 解析线配置项
* @param {Object} options 线配置项
* @param {Object} common 通用配置对象
* @param {Object} property 属性配置对象
* @returns cesium线对象
*/
const createPolylineByOptions = (options, common) => {
if(!options) {
return undefined
}
const _options = {
width: parseFloat(options.width) || 1,
material: Cesium.Color.fromCssColorString(options.color) || Cesium.Color.WHITE,
...common
}
if(_clampToGround) {
_options.clampToGround = true
}
return new Cesium.PolylineGraphics(_options)
}
/**
* 解析面相关配置为对象
* @param {Object} options 总配置项
* @param {Object} common 通用配置对象
* @param {Object} property 属性配置对象
* @returns 面样式对象
*/
const parsePolygonOptions = (options, common, property) => {
const style = {
polygon: createPolygonByOptions(options.polygon, common),
properties: property || {}
}
// 添加面的轮廓线
if(options.polygon.showOutline) {
property.closed = true
style.polyline = createPolygonOutlineByOptions(options.polygon, common)
}
// 添加标签
if(options.label) {
style.label = createLabelByOptions(options.label, common)
}
return style
}
/**
* 解析面的轮廓线配置项
* 因面的自带相关属性不支持贴地形所以生成cesium线对象
* @param {Object} options 面配置项
* @param {Object} common 通用配置对象
* @returns cesium线对象
*/
const createPolygonOutlineByOptions = (options, common) => {
if(!options) {
return undefined
}
const _options = {
width: parseFloat(options.outlineWidth) ?? 1,
material: Cesium.Color.fromCssColorString(options.outlineColor) || Cesium.Color.BLACK,
...common
}
if(_clampToGround) {
_options.clampToGround = true
}
return new Cesium.PolylineGraphics(_options)
}
/**
* 解析面配置项
* @param {Object} options 面配置项
* @param {Object} common 通用配置对象
* @returns cesium面对象
*/
const createPolygonByOptions = (options, common) => {
if(!options) {
return undefined
}
const _options = {
material: Cesium.Color.fromCssColorString(options.color) || Cesium.Color.WHITE,
...common
}
if(_clampToGround) {
_options.heightReference = Cesium.HeightReference.CLAMP_TO_GROUND
}
return new Cesium.PolygonGraphics(_options)
}
/**
* 解析图标相关配置为对象
* @param {Object} options 总配置项
* @param {Object} common 通用配置对象
* @param {Object} property 属性配置对象
* @returns 图标样式对象
*/
const parseBillboardOptions = (options, common, property) => {
const style = {
billboard: createBillboardByOptions(options.billboard, common),
properties: property || {}
}
// 添加标签
if(options.label) {
style.label = createLabelByOptions(options.label, common)
}
return style
}
/**
* 解析图标配置项
* @param {Object} options 图标配置项
* @param {Object} common 通用配置对象
* @returns cesium图标对象
*/
const createBillboardByOptions = (options, common) => {
if(!options) {
return undefined
}
const _options = {
image: options.image,
scale: parseFloat(options.scale) ?? 1,
rotation: Cesium.Math.toRadians(parseFloat(options.rotation) || 0),
horizontalOrigin: parseInt(options.horizontalOrigin) ?? Cesium.HorizontalOrigin.CENTER,
verticalOrigin: parseInt(options.verticalOrigin) ?? Cesium.VerticalOrigin.BOTTOM,
pixelOffset: new Cesium.Cartesian2(parseFloat(options.horizontalOffset) || 0, parseFloat(options.verticalOffset) || 0),
...common
}
if(_clampToGround) {
_options.heightReference = Cesium.HeightReference.CLAMP_TO_GROUND
}
return new Cesium.BillboardGraphics(_options)
}
/**
* 解析标签相关配置为对象
* @param {Object} options 总配置项
* @param {Object} common 通用配置对象
* @param {Object} property 属性配置对象
* @returns 标签样式对象
*/
const parseLabelOptions = (options, common, property) => {
return {
label: createLabelByOptions(options.label, common),
properties: property || {}
}
}
/**
* 解析标签配置项
* @param {Object} options 标签配置项
* @param {Object} common 通用配置对象
* @returns cesium标签对象
*/
const createLabelByOptions = (options, common) => {
if(!options) {
return undefined
}
const _options = {
font: `${options.font ?? 16}px sans-serif`,
fillColor: Cesium.Color.fromCssColorString(options.fillColor) || Cesium.Color.WHITE,
horizontalOrigin: parseInt(options.horizontalOrigin) ?? Cesium.HorizontalOrigin.CENTER,
verticalOrigin: parseInt(options.verticalOrigin) ?? Cesium.VerticalOrigin.BOTTOM,
pixelOffset: new Cesium.Cartesian2(parseFloat(options.horizontalOffset) ?? 0, parseFloat(options.verticalOffset) ?? 0),
...common
}
if(options.showOutline) {
_options.style = Cesium.LabelStyle.FILL_AND_OUTLINE
_options.outlineWidth = parseFloat(options.outlineWidth) || 1
_options.outlineColor = Cesium.Color.fromCssColorString(options.outlineColor) || Cesium.Color.BLACK
}
if(options.showBackground) {
_options.showBackground = true
_options.backgroundColor = Cesium.Color.fromCssColorString(options.backgroundColor) || new Cesium.Color(0.165, 0.165, 0.165, 0.8)
_options.backgroundPadding = new Cesium.Cartesian2(parseFloat(options.horizontalPadding) ?? 7, parseFloat(options.verticalPadding) ?? 5)
}
if(_clampToGround) {
_options.heightReference = Cesium.HeightReference.CLAMP_TO_GROUND
}
return new Cesium.LabelGraphics(_options)
}
export {
getStyle
}

View File

@ -99,6 +99,12 @@ export const constantRoutes = [
component: () => import('@/views/gisManagement/configSetting/index.vue'),
name: 'configSetting',
meta: { title: '地图配置' }
},
{
path: 'configStyle',
component: () => import('@/views/gisManagement/configStyle/index.vue'),
name: 'configStyle',
meta: { title: '样式配置' }
}
]
}

View File

@ -0,0 +1,170 @@
<template>
<el-dialog v-model="show" title="选择图标" width="600px">
<!-- <el-button type="primary">上传本地图片</el-button> -->
<div v-loading="loading">
<el-upload action="#" :auto-upload="false" :show-file-list="false" :on-change="handleFileChange"
accept="image/*">
<el-button type="primary">上传本地图片</el-button>
</el-upload>
<div class="icon-container">
<div :class="{active: icon === selectedItem}" class="icon-item" v-for="(icon, index) in icons" :key="index" @click="selectIcon(icon)">
<el-image style="width: 48px; height: 48px" :src="icon" fit="contain" />
</div>
</div>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="handleCancel">取消</el-button>
<el-button type="primary" @click="handleConfirm" :disabled="!selectedItem">
确定
</el-button>
</span>
</template>
</el-dialog>
</template>
<script setup>
import analysisAroundIcon from '@/assets/icons/example/analysis_around.png'
import analysisCoordinationLocationIcon from '@/assets/icons/example/analysis_coordination_location.png'
import analysisMapLocationIcon from '@/assets/icons/example/analysis_map_location.png'
import analysisSandboxIcon from '@/assets/icons/example/analysis_sandbox.png'
import analysisSpreadIcon from '@/assets/icons/example/analysis_spread.png'
import checkPointIcon from '@/assets/icons/example/check_point.png'
import sandboxExplosiveIcon from '@/assets/icons/example/sandbox_explosive.png'
import sandboxFireIcon from '@/assets/icons/example/sandbox_fire.png'
import sandboxFirefighterIcon from '@/assets/icons/example/sandbox_firefighter.png'
import sandboxFirepointIcon from '@/assets/icons/example/sandbox_firepoint.png'
import sandboxFlameIcon from '@/assets/icons/example/sandbox_flame.png'
import sandboxTrappedIcon from '@/assets/icons/example/sandbox_trapped.png'
import sandboxWaterIcon from '@/assets/icons/example/sandbox_water.png'
const show = defineModel()
const props = defineProps({
current: {
type: String,
default: ''
}
})
const emits = defineEmits(['update:current'])
// const icons = ref([
// { url: analysisAroundIcon },
// { url: analysisCoordinationLocationIcon },
// { url: analysisMapLocationIcon },
// { url: analysisSandboxIcon },
// { url: analysisSpreadIcon },
// { url: checkPointIcon },
// { url: sandboxExplosiveIcon },
// { url: sandboxFireIcon },
// { url: sandboxFirefighterIcon },
// { url: sandboxFirepointIcon },
// { url: sandboxFlameIcon },
// { url: sandboxTrappedIcon },
// { url: sandboxWaterIcon }
// ])
const icons = ref([
analysisAroundIcon,
analysisCoordinationLocationIcon,
analysisMapLocationIcon,
analysisSandboxIcon,
analysisSpreadIcon,
checkPointIcon,
sandboxExplosiveIcon,
sandboxFireIcon,
sandboxFirefighterIcon,
sandboxFirepointIcon,
sandboxFlameIcon,
sandboxTrappedIcon,
sandboxWaterIcon
])
const loading = ref(false)
const selectedItem = ref(props.current)
/* 上传图片 */
const handleFileChange = (uploadFile) => {
loading.value = true;
const file = uploadFile.raw;
if (!file) return;
//
if (!file.type.startsWith('image/')) {
ElMessage.error('只能上传图片文件!');
return;
}
// ()
// const isLt2M = file.size / 1024 / 1024 < 2;
// if (!isLt2M) {
// ElMessage.error(' 2MB!');
// return;
// }
const reader = new FileReader();
reader.onloadend = () => {
const base64String = reader.result;
if(icons.value.includes(base64String)) {
ElMessage.error('该图片已存在,请选择其他图片!');
loading.value = false;
return;
}
icons.value.push(base64String);
loading.value = false;
};
reader.onerror = () => {
ElMessage.error('读取图片时出错!');
loading.value = false;
};
reader.readAsDataURL(file);
}
/* 图片点击 */
const selectIcon = (icon) => {
selectedItem.value = icon
}
/* 确认 */
const handleConfirm = () => {
emits('update:current', selectedItem.value)
show.value = false
}
/* 取消 */
const handleCancel = () => {
selectedItem.value = props.current
show.value = false
}
</script>
<style lang="scss" scoped>
.icon-container {
margin-top: 20px;
display: flex;
flex-wrap: wrap;
border-left: 1px solid var(--el-border-color);
.icon-item {
width: 60px;
height: 60px;
// background-color: #f0f0f0;
border-right: 1px solid var(--el-border-color);
border-bottom: 1px solid var(--el-border-color);
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
&:nth-child(-n+10) {
border-top: 1px solid var(--el-border-color);
}
&:hover {
// background-color: #e0e0e0;
background-color: var(--el-border-color-extra-light);
color: var(--brand-color-light);
}
&.active {
background-color: var(--el-color-primary-light-6);
}
}
}
</style>

View File

@ -0,0 +1,234 @@
<template>
<el-dialog v-model="dialogVisible" title="选择图标/图片" width="600px" @close="handleCancel">
<div class="image-library-content">
<!-- 上传本地图片 -->
<div class="upload-area">
<el-upload action="#" :auto-upload="false" :show-file-list="false" :on-change="handleFileChange"
accept="image/*">
<el-button type="primary">上传本地图片</el-button>
</el-upload>
</div>
<!-- 图标/图片列表 -->
<div class="item-list">
<div v-for="item in items" :key="item.id" :class="['item', { 'is-selected': selectedItem?.id === item.id }]"
@click="selectItem(item)">
<template v-if="item.type === 'system' && item.component">
<!-- Element Plus Icon -->
<el-icon :size="30">
<component :is="item.component" />
</el-icon>
</template>
<template v-else-if="item.type === 'system' && item.display">
<!-- System Image URL -->
<img :src="item.display" alt="system image" class="item-image" />
</template>
<template v-else-if="item.type === 'local' && item.display">
<!-- Local Base64 Image -->
<img :src="item.display" alt="local image" class="item-image" />
</template>
<div v-else class="item-placeholder">无预览</div>
</div>
</div>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="handleCancel">取消</el-button>
<el-button type="primary" @click="handleConfirm" :disabled="!selectedItem">
确定
</el-button>
</span>
</template>
</el-dialog>
</template>
<script setup>
import { ref, watch, onMounted } from 'vue';
import { ElDialog, ElButton, ElUpload, ElIcon, ElMessage } from 'element-plus';
// Element Plus Icons ()
//
//
import * as ElementPlusIconsVue from '@element-plus/icons-vue';
// props
const props = defineProps({
visible: {
type: Boolean,
default: false
},
initialValue: {
type: String, // (URL Base64)
default: null
}
});
// emits
const emit = defineEmits(['update:visible', 'confirm']);
//
const dialogVisible = ref(props.visible);
// /
const items = ref([]);
//
const selectedItem = ref(null);
//
const defaultSystemItems = [
// Element Plus Icons ()
{ id: 'icon-star', type: 'system', display: 'Star', value: 'ElIconStarFilled', component: 'ElIconStarFilled' },
{ id: 'icon-picture', type: 'system', display: 'Picture', value: 'ElIconPicture', component: 'ElIconPicture' },
{ id: 'icon-folder', type: 'system', display: 'Folder', value: 'ElIconFolder', component: 'ElIconFolder' },
{ id: 'icon-setting', type: 'system', display: 'Setting', value: 'ElIconSetting', component: 'ElIconSetting' },
{ id: 'icon-user', type: 'system', display: 'User', value: 'ElIconUser', component: 'ElIconUser' },
// System Images ( URLs)
{ id: 'img-placeholder-1', type: 'system', display: 'https://via.placeholder.com/50x50?text=Img1', value: 'https://via.placeholder.com/50x50?text=Img1' },
{ id: 'img-placeholder-2', type: 'system', display: 'https://via.placeholder.com/50x50?text=Img2', value: 'https://via.placeholder.com/50x50?text=Img2' },
];
//
// onMounted(() => {
// items.value = [...defaultSystemItems];
// });
// visible prop
// watch(() => props.visible, (newVal) => {
// dialogVisible.value = newVal;
// if (newVal) {
// // initialValue
// selectedItem.value = items.value.find(item => item.value === props.initialValue) || null;
// } else {
// //
// selectedItem.value = null;
// }
// });
//
const handleFileChange = (uploadFile) => {
const file = uploadFile.raw;
if (!file) return;
//
if (!file.type.startsWith('image/')) {
ElMessage.error('只能上传图片文件!');
return;
}
// ()
// const isLt2M = file.size / 1024 / 1024 < 2;
// if (!isLt2M) {
// ElMessage.error(' 2MB!');
// return;
// }
const reader = new FileReader();
reader.onloadend = () => {
const base64String = reader.result;
const newItem = {
id: `local-${Date.now()}-${Math.random().toString(16).slice(2)}`, // ID
type: 'local',
display: base64String, // Base64
value: base64String // Base64
};
items.value.push(newItem);
// ()
// selectedItem.value = newItem;
};
reader.readAsDataURL(file);
};
//
const selectItem = (item) => {
selectedItem.value = item;
};
//
const handleConfirm = () => {
if (selectedItem.value) {
// value (URL Base64)
emit('confirm', selectedItem.value.value);
//
emit('update:visible', false);
}
};
//
const handleCancel = () => {
emit('update:visible', false);
};
// Element Plus Icons 使 ()
//
const icons = ElementPlusIconsVue;
</script>
<style scoped>
.image-library-content {
padding: 0 20px 20px 20px;
/* 调整内边距 */
}
.upload-area {
margin-bottom: 20px;
text-align: right;
/* 让上传按钮靠右 */
}
.item-list {
display: flex;
flex-wrap: wrap;
gap: 10px;
/* 项之间的间距 */
max-height: 300px;
/* 限制列表高度并添加滚动条 */
overflow-y: auto;
padding-right: 10px;
/* 为滚动条留出空间 */
}
.item {
width: 60px;
/* 项的固定宽度 */
height: 60px;
/* 项的固定高度 */
border: 1px solid #dcdfe6;
border-radius: 4px;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
transition: border-color 0.3s;
overflow: hidden;
/* 防止图片溢出 */
position: relative;
}
.item:hover {
border-color: #409eff;
}
.item.is-selected {
border-color: #409eff;
box-shadow: 0 0 5px rgba(64, 158, 255, 0.5);
}
.item-image {
max-width: 100%;
max-height: 100%;
object-fit: contain;
/* 保持图片比例 */
}
.item-placeholder {
font-size: 12px;
color: #909399;
text-align: center;
}
/* 调整 Element Plus Icon 的样式 */
.item .el-icon {
font-size: 30px;
/* 确保图标大小合适 */
}
</style>

View File

@ -0,0 +1,155 @@
<template>
<div class="app-container">
<el-card style="margin-bottom: 12px;" shadow="always" v-show="showSearch">
<el-form :model="queryParams" ref="queryRef" :inline="true" label-width="68px">
<el-form-item label="样式名称" prop="styleName">
<el-input v-model="queryParams.styleName" placeholder="请输入样式名称" clearable></el-input>
</el-form-item>
<el-form-item label="样式类型" prop="styleType">
<el-select clearable v-model="queryParams.styleType" style="width: 200px" placeholder="请选择样式类型">
<el-option v-for="type in styleTypeList" :key="type.value" :label="type.label" :value="type.value" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
</el-card>
<el-card shadow="always" class="list-table-section">
<template #header>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="formDialogRef.show()">新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="success" plain icon="Edit" :disabled="single" @click="formDialogRef.show(ids[0])">修改</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete">删除</el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
</template>
<el-table v-loading="loading" :data="styleList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="样式名称" align="center" prop="styleName" />
<el-table-column label="样式类型" align="center" prop="styleType">
<template #default="{ row }">
<!-- <el-tag type="primary" v-for="(type, index) in row.type.split(',')" :key="index">
{{ styleTypeList.find(item => item.id === type) }}
</el-tag> -->
<el-tag v-for="(dict, index) in styleTypeList"
v-show="row.styleType === dict.value" :key="index">{{ dict.label }}</el-tag>
</template>
</el-table-column>
<el-table-column label="描述信息" align="center" prop="description" />
<el-table-column label="操作" width="300" align="center" class-name="small-padding fixed-width">
<template #default="{ row }">
<el-tooltip content="修改" placement="top">
<el-button link type="primary" icon="Edit" @click="formDialogRef.show(row.id)"></el-button>
</el-tooltip>
<el-tooltip content="删除" placement="top">
<el-button link type="primary" icon="Delete" @click="handleDelete(row)"></el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
</el-card>
<div class="page-wrap">
<pagination class="list-page" v-show="total > 0" :total="total" v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize" @pagination="getList" />
</div>
</div>
<style-dialog ref="formDialogRef" @refresh="getList()" />
</template>
<script setup name="configStyle">
import { getStyleType, listStyle, delStyle } from "@/api/gisManagement/configStyle"
import styleDialog from "./styleDialog.vue"
const { proxy } = getCurrentInstance()
const styleTypeList = ref([])
const styleList = ref([])
const loading = ref(true)
const showSearch = ref(true)
const ids = ref([])
const single = ref(true)
const multiple = ref(true)
const total = ref(0)
const formDialogRef = ref()
const queryParams = ref({
pageNum: 1,
pageSize: 10,
styleName: '',
styleType: null
})
/* 初始化 */
function init() {
//
getStyleType().then(response => {
styleTypeList.value = response
})
getList()
}
/** 查询用户信息列表 */
function getList() {
loading.value = true
listStyle(queryParams.value).then(response => {
styleList.value = response.rows
total.value = response.total
loading.value = false
})
}
/** 搜索按钮操作 */
function handleQuery() {
queryParams.value.pageNum = 1
getList()
}
/** 重置按钮操作 */
function resetQuery() {
proxy.resetForm("queryRef")
handleQuery()
}
//
function handleSelectionChange(selection) {
ids.value = selection.map(item => item.id)
single.value = selection.length != 1
multiple.value = !selection.length
}
/** 删除按钮操作 */
function handleDelete(row) {
const _userIds = row.id || ids.value
proxy.$modal.confirm('是否确认删除勾选的数据项?').then(function () {
return delStyle(_userIds)
}).then(() => {
getList()
proxy.$modal.msgSuccess("删除成功")
}).catch(() => { })
}
init()
</script>
<style lang="scss" scoped>
.app-container {
position: relative;
height: calc(100vh - 84px);
overflow: hidden;
display: flex;
flex-direction: column;
}
.list-table-section {
flex: auto;
}
</style>

View File

@ -0,0 +1,728 @@
<template>
<el-dialog v-model="showDialog" :title="title" width="800px" destroy-on-close>
<div class="form-wrapper">
<div class="base-container">
<el-form class="base-info" :model="form" ref="formRef" :label-width="labelWidth">
<el-form-item label="样式名称" prop="styleName">
<el-input v-model="form.styleName" placeholder="请输入样式名称" clearable></el-input>
</el-form-item>
<el-form-item label="样式类型" prop="styleType">
<el-select clearable v-model="form.styleType" placeholder="请选择样式类型" @change="handleTypeChange">
<el-option v-for="type in styleTypeList" :key="type.value" :label="type.label" :value="type.value" />
</el-select>
</el-form-item>
<el-form-item label="描述" prop="description">
<el-input v-model="form.description" type="textarea" :autosize="{ minRows: 4, maxRows: 4 }" resize="none"
placeholder="请输入描述信息"></el-input>
</el-form-item>
<el-row :gutter="20">
<el-col :span="12" v-if="form.styleType === 'point'">
<el-form-item label="显示图标" prop="showBillboard">
<el-switch v-model="showBillboard" @change="refreshMap" />
</el-form-item>
</el-col>
<el-col :span="12" v-if="['point', 'polyline', 'polygon', 'billboard'].includes(form.styleType)">
<el-form-item label="显示标签" prop="showLabel">
<el-switch v-model="showLabel" @change="refreshMap" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<div class="map-preview">
<cesium-map :options="mapOptions" @init="initMap" />
</div>
</div>
<div class="sub-container">
<!-- 点样式 -->
<el-form v-if="showPoint" :model="formPoint" ref="formPointRef" :label-width="labelWidth">
<el-divider content-position="left" class="style-title">点样式配置</el-divider>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="像素大小" prop="pixelSize">
<!-- <el-input v-model="form.styleName" placeholder="请输入像素大小" clearable></el-input> -->
<el-input-number v-model="formPoint.pixelSize" :min="1" style="width: 100%;" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="颜色" prop="color">
<el-color-picker v-model="formPoint.color" />
</el-form-item>
</el-col>
</el-row>
<el-form-item label="轮廓线" prop="showOutline">
<el-switch v-model="formPoint.showOutline" />
</el-form-item>
<el-row v-if="formPoint.showOutline" :gutter="20">
<el-col :span="12">
<el-form-item label="宽度" prop="outlineWidth">
<!-- <el-input v-model="form.styleName" placeholder="请输入像素大小" clearable></el-input> -->
<el-input-number v-model="formPoint.outlineWidth" :min="1" style="width: 100%;" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="颜色" prop="outlineColor">
<el-color-picker v-model="formPoint.outlineColor" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<!-- 线样式 -->
<el-form v-if="showPolyline" :model="formPolyline" ref="formPolylineRef" :label-width="labelWidth">
<el-divider content-position="left" class="style-title">线样式配置</el-divider>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="宽度" prop="width">
<!-- <el-input v-model="form.styleName" placeholder="请输入像素大小" clearable></el-input> -->
<el-input-number v-model="formPolyline.width" :min="1" style="width: 100%;" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="颜色" prop="color">
<el-color-picker v-model="formPolyline.color" />
</el-form-item>
</el-col>
</el-row>
<el-form-item label="闭合" prop="closed">
<el-switch v-model="formPolyline.closed" />
</el-form-item>
</el-form>
<!-- 面样式 -->
<el-form v-if="showPolygon" :model="formPolygon" ref="formPolygonRef" :label-width="labelWidth">
<el-divider content-position="left" class="style-title">面样式配置</el-divider>
<el-form-item label="填充颜色" prop="color">
<el-color-picker v-model="formPolygon.color" />
</el-form-item>
<el-form-item label="轮廓线" prop="showOutline">
<el-switch v-model="formPolygon.showOutline" />
</el-form-item>
<el-row v-if="formPolygon.showOutline" :gutter="20">
<el-col :span="12">
<el-form-item label="宽度" prop="outlineWidth">
<!-- <el-input v-model="form.styleName" placeholder="请输入像素大小" clearable></el-input> -->
<el-input-number v-model="formPolygon.outlineWidth" :min="1" style="width: 100%;" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="颜色" prop="outlineColor">
<el-color-picker v-model="formPolygon.outlineColor" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<!-- 图标样式 -->
<el-form v-if="showBillboard" :model="formBillboard" ref="formBillboardRef" :label-width="labelWidth">
<el-divider content-position="left" class="style-title">图标样式配置</el-divider>
<el-form-item label="图片" prop="image">
<el-image class="style-billboard-image" :src="formBillboard.image" fit="contain"
@click="handleIconSelect" />
</el-form-item>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="缩放" prop="scale">
<el-slider v-model="formBillboard.scale" :max="5" :min="0.1" :step="0.1" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="旋转" prop="rotation">
<el-input-number v-model="formBillboard.rotation" :min="0" :max="360" style="width: 100%;" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="横向定位" prop="horizontalOrigin">
<el-select v-model="formBillboard.horizontalOrigin" placeholder="请选择横向定位">
<el-option v-for="item in horizontalOriginTypeList" :key="item.value" :label="item.label"
:value="item.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="纵向定位" prop="verticalOrigin">
<el-select v-model="formBillboard.verticalOrigin" placeholder="请选择纵向定位">
<el-option v-for="item in verticalOriginTypeList" :key="item.value" :label="item.label"
:value="item.value" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="横向偏移" prop="horizontalOffset">
<el-input-number v-model="formBillboard.horizontalOffset" style="width: calc(100% - 60px);" />
<span style="margin-left: 8px;">(右为正)</span>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="纵向偏移" prop="verticalOffset">
<el-input-number v-model="formBillboard.verticalOffset" style="width: calc(100% - 60px);" />
<span style="margin-left: 8px;">(下为正)</span>
</el-form-item>
</el-col>
</el-row>
</el-form>
<!-- 标签样式 -->
<el-form v-if="showLabel" :model="formLabel" ref="formLabelRef" :label-width="labelWidth">
<el-divider content-position="left" class="style-title">标签样式配置</el-divider>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="字号" prop="font">
<el-input-number v-model="formLabel.font" :min="12" style="width: 100%;" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="颜色" prop="fillColor">
<el-color-picker v-model="formLabel.fillColor" />
</el-form-item>
</el-col>
</el-row>
<el-form-item label="轮廓线" prop="showOutline">
<el-switch v-model="formLabel.showOutline" />
</el-form-item>
<el-row v-if="formLabel.showOutline" :gutter="20">
<el-col :span="12">
<el-form-item label="宽度" prop="outlineWidth">
<el-input-number v-model="formLabel.outlineWidth" :min="1" style="width: 100%;" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="颜色" prop="outlineColor">
<el-color-picker v-model="formLabel.outlineColor" />
</el-form-item>
</el-col>
</el-row>
<el-form-item label="背景" prop="showBackground">
<el-switch v-model="formLabel.showBackground" />
</el-form-item>
<el-row v-if="formLabel.showBackground" :gutter="20">
<el-col :span="12">
<el-form-item label="横向内边距" prop="horizontalPadding">
<el-input-number v-model="formLabel.horizontalPadding" :min="0" style="width: 100%;" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="纵向内边距" prop="verticalPadding">
<el-input-number v-model="formLabel.verticalPadding" :min="0" style="width: 100%;" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="颜色" prop="backgroundColor">
<el-color-picker v-model="formLabel.backgroundColor" show-alpha />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="横向定位" prop="horizontalOrigin">
<el-select v-model="formLabel.horizontalOrigin" placeholder="请选择横向定位">
<el-option v-for="item in horizontalOriginTypeList" :key="item.value" :label="item.label"
:value="item.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="纵向定位" prop="verticalOrigin">
<el-select v-model="formLabel.verticalOrigin" placeholder="请选择纵向定位">
<el-option v-for="item in verticalOriginTypeList" :key="item.value" :label="item.label"
:value="item.value" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="横向偏移" prop="horizontalOffset">
<el-input-number v-model="formLabel.horizontalOffset" style="width: calc(100% - 60px);" />
<span style="margin-left: 8px;">(右为正)</span>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="纵向偏移" prop="verticalOffset">
<el-input-number v-model="formLabel.verticalOffset" style="width: calc(100% - 60px);" />
<span style="margin-left: 8px;">(下为正)</span>
</el-form-item>
</el-col>
</el-row>
</el-form>
<!-- 通用样式 -->
<el-form :model="formCommon" ref="formCommonRef" :label-width="labelWidth">
<el-divider content-position="left" class="style-title">通用样式配置</el-divider>
<el-form-item label="贴地" prop="clampToGround">
<el-switch v-model="formCommon.clampToGround" />
</el-form-item>
<div v-if="['point', 'billboard', 'label'].includes(form.styleType)">
<el-form-item label="按视距缩放" prop="scaleByDistance">
<el-switch v-model="formCommon.scaleByDistance" />
</el-form-item>
<el-row v-if="formCommon.scaleByDistance" :gutter="20">
<el-col :span="12">
<el-form-item label="缩放近视距" prop="scaleNear">
<el-input-number v-model="formCommon.scaleNear" :min="0" style="width: 100%;" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="缩放比例" prop="scaleNearValue">
<el-input-number v-model="formCommon.scaleNearValue" style="width: 100%;" />
</el-form-item>
</el-col>
</el-row>
<el-row v-if="formCommon.scaleByDistance" :gutter="20">
<el-col :span="12">
<el-form-item label="缩放远视距" prop="scaleFar">
<el-input-number v-model="formCommon.scaleFar" :min="1" style="width: 100%;" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="缩放比例" prop="scaleFarValue">
<el-input-number v-model="formCommon.scaleFarValue" style="width: 100%;" />
</el-form-item>
</el-col>
</el-row>
</div>
</el-form>
</div>
</div>
<icon-dialog ref="iconDialog" v-model:current="formBillboard.image" v-model="showIconDialog" />
<template #footer>
<div class="dialog-footer">
<el-button @click="handleCancel">取消</el-button>
<el-button type="primary" @click="handleConfirm">保存</el-button>
</div>
</template>
</el-dialog>
</template>
<script setup name="styleDialog">
import * as Cesium from 'cesium'
import CesiumMap from '@/components/CesiumMap/index.vue';
import { getStyle } from "@/components/CesiumMap/utils/styleFn";
import { createFeature } from "@/components/CesiumMap/utils/layerFn";
import IconDialog from "./iconDialog.vue";
import { getStyleType, getStyle as getStyleApi, addStyle, updateStyle } from "@/api/gisManagement/configStyle"
import checkPointIcon from '@/assets/icons/example/check_point.png'
const emits = defineEmits(['refresh'])
const { proxy } = getCurrentInstance()
const showDialog = ref(false)
const title = ref('');
const labelWidth = '85px'
let viewer = null
let entity = null
const defaultPoint = [102, 25]
const defaultPosition = Cesium.Cartesian3.fromDegrees(...defaultPoint)
const defaultPolyline = Cesium.Cartesian3.fromDegreesArray([102.1, 24.9, 102.1, 25.1, 101.9, 24.9, 101.9, 25.2])
const defaultPolygon = Cesium.Cartesian3.fromDegreesArray([102.1, 24.9, 102.1, 25.1, 101.9, 25.1, 101.9, 24.9])
const mapOptions = {
setView: {
x: defaultPoint[0],
y: defaultPoint[1],
z: 5000,
heading: 0,
pitch: -90,
roll: 0
}
}
const styleTypeList = ref([])
const horizontalOriginTypeList = ref([
{ label: '左', value: 1 },
{ label: '中', value: 0 },
{ label: '右', value: -1 }
])
const verticalOriginTypeList = ref([
{ label: '上', value: -1 },
{ label: '中', value: 0 },
{ label: '下', value: 1 }
])
const form = ref({
id: null,
styleName: '',
styleType: [],
description: '',
config: '',
})
const showPoint = ref(false)
const formPoint = ref({
pixelSize: 10,
color: '#ffffff',
showOutline: false,
outlineWidth: 1,
outlineColor: '#000000',
})
const showPolyline = ref(false)
const formPolyline = ref({
width: 1,
color: '#ffffff',
closed: false,
})
const showPolygon = ref(false)
const formPolygon = ref({
color: '#ffffff',
showOutline: false,
outlineWidth: 1,
outlineColor: '#000000',
})
const showBillboard = ref(false)
const formBillboard = ref({
image: checkPointIcon,
scale: 1,
rotation: 0,
horizontalOrigin: 0,
verticalOrigin: 1,
horizontalOffset: 0,
verticalOffset: 0,
})
const showIconDialog = ref(false)
const showLabel = ref(false)
const formLabel = ref({
font: 12,
fillColor: '#ffffff',
showOutline: false,
outlineWidth: 1,
outlineColor: '#000000',
showBackground: false,
backgroundColor: 'rgba(42, 42, 42, 0.8)',
horizontalPadding: 0,
verticalPadding: 0,
horizontalOrigin: 0,
verticalOrigin: 1,
horizontalOffset: 0,
verticalOffset: 0,
})
const formCommon = ref({
clampToGround: true,
scaleByDistance: false,
scaleNear: 0,
scaleNearValue: 0,
scaleFar: 1,
scaleFarValue: 0,
})
/**
* 打开弹窗的方法
* @param id 样式id
*/
const show = async (id) => {
reset()
if (id) {
title.value = '修改样式信息'
getStyleApi(id).then(res => {
// const data = response.data
// form.value.styleName = data.styleName
// form.value.styleType = data.styleType
// form.value.description = data.description
form.value = res.data
const config = JSON.parse(form.value.config)
if (config.point) {
showPoint.value = true
formPoint.value = config.point
}
if (config.polyline) {
showPolyline.value = true
formPolyline.value = config.polyline
}
if (config.polygon) {
showPolygon.value = true
formPolygon.value = config.polygon
}
if (config.billboard) {
showBillboard.value = true
formBillboard.value = config.billboard
}
if (config.label) {
showLabel.value = true
formLabel.value = config.label
}
formCommon.value = config.common
})
} else {
title.value = '新增样式信息'
form.value.id = null
}
//
getStyleType().then(response => {
styleTypeList.value = response
})
showDialog.value = true;
}
/**
* 初始化地图
*/
const initMap = (viewerInstance) => {
//
viewer = viewerInstance
// entity = viewer.entities.add({
// id: 'stylePreview',
// position: defaultPosition, //
// })
//
viewer.camera.lookAt(
defaultPosition, //
new Cesium.HeadingPitchRange(Cesium.Math.toRadians(0), Cesium.Math.toRadians(-90), 5000) //
);
refreshMap()
}
/**
* 样式类型改变, 切换配置项显示
* @param {string} val 类型值
*/
const handleTypeChange = (val) => {
if (val === 'point') {
showPoint.value = true
showPolyline.value = false
showPolygon.value = false
showBillboard.value = false
showLabel.value = false
} else if (val === 'polyline') {
showPoint.value = false
showPolyline.value = true
showPolygon.value = false
showBillboard.value = false
showLabel.value = false
} else if (val === 'polygon') {
showPoint.value = false
showPolyline.value = false
showPolygon.value = true
showBillboard.value = false
showLabel.value = false
} else if (val === 'billboard') {
showPoint.value = false
showPolyline.value = false
showPolygon.value = false
showBillboard.value = true
showLabel.value = false
} else if (val === 'label') {
showPoint.value = false
showPolyline.value = false
showPolygon.value = false
showBillboard.value = false
showLabel.value = true
} else {
//
showPoint.value = false
showPolyline.value = false
showPolygon.value = false
showBillboard.value = false
showLabel.value = false
}
}
/**
* 打开图标选择对话框
*/
const handleIconSelect = () => {
showIconDialog.value = true;
}
/**
* 刷新地图
* 更新样式后触发
*/
const refreshMap = () => {
if(!viewer) {
return
}
const options = {
type: form.value.styleType,
}
if (showPoint.value) options.point = formPoint.value
if (showPolyline.value) options.polyline = formPolyline.value
if (showPolygon.value) options.polygon = formPolygon.value
if (showBillboard.value) options.billboard = formBillboard.value
if (showLabel.value) options.label = formLabel.value
options.common = formCommon.value
console.log('更新样式配置:', JSON.stringify(options))
form.value.config = JSON.stringify(options)
if (entity) {
viewer.entities.remove(entity)
}
const style = getStyle(options)
style.id = 'stylePreview'
let position = defaultPosition
if (style.properties?.type === 'polyline') {
position = defaultPolyline
} else if (style.properties?.type === 'polygon') {
position = defaultPolygon
}
entity = createFeature(style, position, '测试')
if (entity) {
viewer.entities.add(entity)
}
}
const reset = () => {
viewer = null
entity = null
form.value = {
id: null,
styleName: '',
styleType: [],
description: '',
config: '',
}
showPoint.value = false
formPoint.value = {
pixelSize: 10,
color: '#ffffff',
showOutline: false,
outlineWidth: 1,
outlineColor: '#000000',
}
showPolyline.value = false
formPolyline.value = {
width: 1,
color: '#ffffff',
closed: false,
}
showPolygon.value = false
formPolygon.value = {
color: '#ffffff',
showOutline: false,
outlineWidth: 1,
outlineColor: '#000000',
}
showBillboard.value = false
formBillboard.value = {
image: checkPointIcon,
scale: 1,
rotation: 0,
horizontalOrigin: 0,
verticalOrigin: 1,
horizontalOffset: 0,
verticalOffset: 0,
}
showLabel.value = false
formLabel.value = {
font: 12,
fillColor: '#ffffff',
showOutline: false,
outlineWidth: 1,
outlineColor: '#000000',
showBackground: false,
backgroundColor: 'rgba(42, 42, 42, 0.8)',
horizontalPadding: 0,
verticalPadding: 0,
horizontalOrigin: 0,
verticalOrigin: 1,
horizontalOffset: 0,
verticalOffset: 0,
}
formCommon.value = {
clampToGround: true,
scaleByDistance: false,
scaleNear: 0,
scaleNearValue: 0,
scaleFar: 1,
scaleFarValue: 0,
}
}
/**
* 确定
*/
const handleConfirm = () => {
// proxy.$refs["styleRef"].validate(valid => {
// if (valid) {
// if (form.value.userId != null) {
// updateUser(form.value).then(response => {
// proxy.$modal.msgSuccess("")
// open.value = false
// getList()
// })
// } else {
// addUser(form.value).then(response => {
// proxy.$modal.msgSuccess("")
// open.value = false
// getList()
// })
// }
// }
// })
proxy.$refs["formRef"].validate(valid => {
if (valid) {
if (form.value.id) {
updateStyle(form.value).then(response => {
proxy.$modal.msgSuccess("修改成功")
emits('refresh', response)
showDialog.value = false
})
} else {
addStyle(form.value).then(response => {
proxy.$modal.msgSuccess("新增成功")
emits('refresh', response)
showDialog.value = false
})
}
}
})
}
/**
* 取消
*/
const handleCancel = () => {
showDialog.value = false
}
watch(form.value.styleType, (val) => {
if (val === 'polyline' || val === 'polygon') {
formCommon.value.scaleByDistance = false
}
})
watch(() => [form.value.styleType, formPoint, formPolyline, formPolygon, formBillboard, formLabel, formCommon], refreshMap, {
deep: true
})
defineExpose({
show
})
</script>
<style lang="scss" scoped>
.base-container {
display: flex;
.base-info {
flex: auto;
}
.map-preview {
width: 40%;
min-width: 400px;
height: 250px;
margin-left: 20px;
border: 1px solid var(--el-border-color);
}
}
.sub-container {
margin-top: 12px;
max-height: calc(70vh - 290px);
overflow-x: hidden;
overflow-y: auto;
}
.style-title {
margin: 32px 0;
}
.style-billboard-image {
width: 48px;
height: 48px;
border: 1px solid var(--el-border-color);
border-radius: 4px;
cursor: pointer;
}
</style>