diff --git a/CHANGELOG.md b/CHANGELOG.md index d1ad7e994325be4b56a73514554826db445a52f7..60773bcc49162c8a28e73d0ef4c1cb4b7c262416 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,8 @@ - 新增甘特图滑块的链接线绘制,新增部件参数linkdatasourcetype(链接数据获取模式)、linkappdataentityname(链接数据集应用实体名称)、linkappdedatasetname(链接应用实体结果集名称)、linknodedataname(链接节点数据属性名称)、fromdataitemname(链接起始数据项属性名称)、todataitemname(链接结束数据项属性名称) - 新增树节点数据拖拽状态属性 - 异步导出下载识别文件名属性 +- 地图支持下钻、返回、项样式、行政等级、激活模式 +- 地图内置导航区域导航添加行政编码与行政等级参数 ### Changed diff --git a/src/common/control-navigation/provider/map-navigation.provider.ts b/src/common/control-navigation/provider/map-navigation.provider.ts index 2387a1c59577f7ce1d9258c70c02e28ebcfaf826..58319ba1439632cd244568753607dab24f8b242b 100644 --- a/src/common/control-navigation/provider/map-navigation.provider.ts +++ b/src/common/control-navigation/provider/map-navigation.provider.ts @@ -1,4 +1,9 @@ -import { IMapData, INavViewMsg, MapController } from '@ibiz-template/runtime'; +import { + IMapData, + IMapEvent, + INavViewMsg, + MapController, +} from '@ibiz-template/runtime'; import { ISysMap } from '@ibiz/model-core'; import { NavgationBaseProvider } from './navigation-base.provider'; @@ -16,6 +21,20 @@ export class MapNavigationProvider extends NavgationBaseProvider { declare model: ISysMap; + /** + * @description 导航数据变化 + * @param {IMapEvent['onNavDataChange']['event']} event + * @memberof MapNavigationProvider + */ + onNavDataChange(event: IMapEvent['onNavDataChange']['event']): void { + const { navData } = event; + // 数据变更才重新导航 + if (this.navViewMsg.value?.key !== navData._id) { + this.navStack.unshift(navData[this.keyName]); + this.navViewMsg.value = this.getNavViewMsg(navData as IMapData); + } + } + onNavDataByStack(): void { const { items } = this.controller.state; const navData = @@ -30,16 +49,22 @@ export class MapNavigationProvider extends NavgationBaseProvider { } } - getNavViewMsg(item: IMapData): INavViewMsg { + getNavViewMsg(item: IData): INavViewMsg { const { sysMapItems } = this.model; + const { areaCode, areaLevel } = item; const itemModel = sysMapItems?.find(_item => _item.id === item._mapItemId); if (itemModel) { const { context, params } = this.prepareParams( itemModel, - item._deData, + { ...item._deData, areaCode, areaLevel }, this.controller.context, this.controller.params, ); + // 区域导航视图添加行政编码与行政等级 + if (itemModel.itemStyle?.startsWith('REGION')) { + params.srfareacode = areaCode; + params.srfarealevel = areaLevel; + } return { params, context, diff --git a/src/common/map-chart-user/map-chart-user.scss b/src/common/map-chart-user/map-chart-user.scss index 04a3d4108e70e6f4d7e9996425b5bd05f31fd7cc..f99339e0b746db22d9f8bb3961e94f7386cfafe7 100644 --- a/src/common/map-chart-user/map-chart-user.scss +++ b/src/common/map-chart-user/map-chart-user.scss @@ -21,7 +21,14 @@ cursor: pointer; } + @include e(fullscreen) { + position: absolute; + top: getCssVar(spacing, tight); + right: getCssVar(spacing, tight); + z-index: 9; + } + @include e(popper) { - padding: getCssVar(spacing, base); + padding: getCssVar(spacing, tight); } } \ No newline at end of file diff --git a/src/common/map-chart-user/map-chart-user.tsx b/src/common/map-chart-user/map-chart-user.tsx index 4145a57511a29777ab6427f73e2c1e94138cbdc5..1296c1e408793507adf51931fc1c81131456215d 100644 --- a/src/common/map-chart-user/map-chart-user.tsx +++ b/src/common/map-chart-user/map-chart-user.tsx @@ -1,8 +1,17 @@ /* eslint-disable eqeqeq */ -import { defineComponent, onMounted, PropType, computed } from 'vue'; +import { + defineComponent, + onMounted, + PropType, + computed, + ref, + onBeforeUnmount, +} from 'vue'; import { isNil, mergeDeepLeft, mergeDeepWithKey } from 'ramda'; import { useNamespace } from '@ibiz-template/vue3-util'; import { IMapData, MapController } from '@ibiz-template/runtime'; +import { listenJSEvent, NOOP } from '@ibiz-template/core'; +import { toNumber } from 'lodash-es'; import { defaultOpts, getAreaOption, @@ -38,6 +47,8 @@ export const IBizMapChartUser = defineComponent({ const ns = useNamespace('map-chart-user'); const c = props.controller; let option: IData = defaultOpts; + const mapRef = ref(); + const isFull = ref(false); if (c.controlParams.defaultopts) { const data = JSON.parse(c.controlParams.defaultopts); option = mergeDeepLeft(data, option); @@ -53,57 +64,148 @@ export const IBizMapChartUser = defineComponent({ ); }); - const { chartRef, historyNames, changeMap, getCityInfo, goBack } = - useMapManager(props.controller, options, mapName => { - const areaData = props.areaData || []; - const pointData = props.pointData || []; + let cleanup = NOOP; - const tooltip = getTooltip(); - const visualMap = getVisualMap(options.value); - const cityInfo = getCityInfo(); - const pointOption = { - ...getPointStaticOption(options.value), - ...getPointOption(pointData, areaData), - }; - const areaOption = { - ...getAreaStaticOption(options.value), - ...getAreaOption(mapName, pointData, areaData, cityInfo), - }; + const { + chartRef, + historyNames, + processing, + areaLevelMap, + changeMap, + getCityInfo, + goBack, + } = useMapManager(props.controller, options, mapName => { + const areaData = props.areaData || []; + const pointData = props.pointData || []; - const result: IData = { - geo: { - map: mapName, - }, - tooltip, - visualMap, - series: [ - // 地图区块序列 - areaOption, - // 地图散点序列 - pointOption, - ], - }; - return result; - }); + const tooltip = getTooltip(); + const visualMap = getVisualMap(options.value); + const cityInfo = getCityInfo(); + const pointOption = { + ...getPointStaticOption(options.value), + ...getPointOption(pointData, areaData), + }; + const { top, bottom } = options.value; + const areaOption = { + top, + bottom, + ...getAreaStaticOption(options.value), + ...getAreaOption(mapName, pointData, areaData, cityInfo), + }; + + const result: IData = { + geo: { + map: mapName, + top, + bottom, + }, + tooltip, + visualMap, + series: [ + // 地图区块序列 + areaOption, + // 地图散点序列 + pointOption, + ], + }; + return result; + }); onMounted(() => { const name = options.value.defaultAreaCode; const areaCode = c.state.strAreaCode ? `${name}` : Number(name); c.state.areaCode = areaCode; changeMap(name, areaCode, true); + + c.evt.on('onDrillDown', async (args: IData) => { + if (!processing.value) { + const { data } = args; + const code = data.areaCode; + const curAreaCode = c.state.strAreaCode ? `${code}` : Number(code); + const areaLevel = areaLevelMap.get(toNumber(curAreaCode)) || ''; + c.state.areaCode = curAreaCode; + c.state.areaLevel = areaLevel; + await changeMap(code, code); + } + }); + c.evt.on('onBackClick', () => { + if (!processing.value) { + goBack(); + } + }); + + cleanup = listenJSEvent(window, 'resize', () => { + if (isFull.value) { + isFull.value = ibiz.fullscreenUtil.isFullScreen; + } + }); + }); + + // 组件销毁前销毁监听 + onBeforeUnmount(() => { + if (cleanup !== NOOP) { + cleanup(); + } }); - return { ns, chartRef, historyNames, goBack }; + const onBack = async () => { + processing.value = true; + await c.evt.emit('onBackClick', undefined); + goBack(); + processing.value = false; + }; + + /** + * @description 切换全屏 + */ + const toggleFullScreen = () => { + if (mapRef.value) { + if (isFull.value) { + ibiz.fullscreenUtil.closeElementFullscreen(); + } else { + ibiz.fullscreenUtil.openElementFullscreen(mapRef.value); + } + isFull.value = !isFull.value; + } + }; + + return { + ns, + c, + mapRef, + chartRef, + historyNames, + isFull, + onBack, + toggleFullScreen, + }; }, render() { + const { enabledFullScreen } = this.c.state; return ( -
+
+ {enabledFullScreen ? ( + + + + ) : null}
{this.historyNames.length > 1 && (
{ - this.goBack(); + this.onBack(); }} > {ibiz.i18n.t('app.return')} diff --git a/src/common/map-chart-user/map-chart-user.util.ts b/src/common/map-chart-user/map-chart-user.util.ts index becc662ba6e04285b7b31ac77b6f8f9bcdd00753..33eef61cee5febbd5ccb8c9d06e0f8f63911386d 100644 --- a/src/common/map-chart-user/map-chart-user.util.ts +++ b/src/common/map-chart-user/map-chart-user.util.ts @@ -43,10 +43,39 @@ export const defaultOpts = { jsonBaseUrl: `${ibiz.env.assetsUrl}/json/map`, /** 默认打开的区域编码 */ defaultAreaCode: 100000 as string | number, + // 距离底部距离 + bottom: 20, + // 距离顶部距离 + top: 20, }; export type MapOptions = typeof defaultOpts; +/** + * @description 获取项样式 + * @param {IData} item + * @returns {*} {string} + */ +const getItemStyle = (item: IData): string => { + const itemStyle = []; + if (item._color) { + itemStyle.push(`color:${item._color}`); + } + if (item._bgcolor) { + itemStyle.push(`background:${item._bgcolor}`); + } + if (item._borderColor) { + itemStyle.push(`border-color:${item._borderColor}`); + } + if (item._borderWidth) { + itemStyle.push(`border-width:${item._borderWidth}px`); + } + if (item._borderWidth && item._borderColor) { + itemStyle.push(`border-style:solid`); + } + return itemStyle.join(';'); +}; + export const findData = ( id: string, type: 'area' | 'point', @@ -119,9 +148,21 @@ export const getPointOption = ( return; } const find = findData(params.data._id, 'point', pointData, areaData)!; - return `
${find?._tooltip}
`; + if (!find) { + return; + } + const { _deData: data } = find; + let text = data.srfmajortext; + if (find._value) { + text = `${data.srfmajortext}: ${find._value}`; + } + if (find._tooltip) { + text = find._tooltip; + } + const style = getItemStyle(find); + return `
${text}
`; }, padding: 0, }; @@ -189,9 +230,22 @@ export const getAreaOption = ( return; } const find = findData(params.data._id, 'area', pointData, areaData)!; - return `
${find?._tooltip}
`; + if (!find || (!find._tooltip && !find._value)) { + return; + } + const { _deData: data } = find; + let text = data.srfmajortext; + if (find._value) { + text = `${data.srfmajortext}: ${find._value}`; + } + if (find._tooltip) { + text = find._tooltip; + } + // 项样式作为弹框样式,弹框默认内容为主信息+值,可配置提示属性实体处理逻辑来调整提示框内容 + const style = getItemStyle(find); + return `
${text}
`; }, padding: 0, }; diff --git a/src/common/map-chart-user/map-user-manager.ts b/src/common/map-chart-user/map-user-manager.ts index 76cb9e17c1855c652f64523ab6488e0f62b0a4aa..c9212a0669e72cd685d0b5e7f78b4fbe1d68d0ae 100644 --- a/src/common/map-chart-user/map-user-manager.ts +++ b/src/common/map-chart-user/map-user-manager.ts @@ -3,6 +3,7 @@ import { registerMap as register, EChartsType, init } from 'echarts'; import { ComputedRef, onMounted, onUnmounted, ref } from 'vue'; import { IMapData, MapController } from '@ibiz-template/runtime'; import { listenJSEvent } from '@ibiz-template/core'; +import { toNumber } from 'lodash-es'; import { findData, MapOptions, getJsonUrl } from './map-chart-user.util'; /** @@ -26,6 +27,10 @@ export function useMapManager( const historyNames = ref([]); + const areaLevelMap = new Map(); + + const processing = ref(false); + let chart: EChartsType; const chartRef = ref(); @@ -38,8 +43,9 @@ export function useMapManager( noChild: json.features.length === 1, }; json.features.forEach((item: IData) => { - const { adcode, name } = item.properties; + const { adcode, name, level } = item.properties; info.cityNames[adcode] = name; + areaLevelMap.set(adcode, level); }); return info; }; @@ -76,16 +82,19 @@ export function useMapManager( areaCode: string | number, isInit: boolean = false, ) => { - if (!isInit) { - controller.onMapChange(areaCode); - } const strName = `${name}`; - if (!mapInfos.has(strName)) { await registerMap(strName); } - controller.state.mapInfo = getCityInfo()!; + if (!isInit) { + controller.onMapChange(areaCode); + } else { + // 初次加载设置默认行政等级 + const areaLevel = areaLevelMap.get(toNumber(areaCode)) || ''; + controller.state.areaLevel = areaLevel; + } currentName.value = strName; + controller.state.mapInfo = getCityInfo()!; historyNames.value.push(strName); refresh(); }; @@ -96,8 +105,9 @@ export function useMapManager( historyNames.value.pop(); // 先删除当前地图的name const name = historyNames.value.pop(); // 获取上一个地图的name const areaCode = opts.value.strAreaCode ? `${name}` : Number(name); + const areaLevel = areaLevelMap.get(toNumber(areaCode)) || ''; controller.state.areaCode = areaCode; - await controller.evt.emit('onBackClick', undefined); + controller.state.areaLevel = areaLevel; changeMap(name!, areaCode); } }; @@ -118,36 +128,80 @@ export function useMapManager( resizeObserver.observe(chartRef.value); } chart.on('click', (params: IData) => { - // 散点点击事件 if (params.componentType === 'series') { + // 散点点击事件 if (params.seriesType === 'scatter') { if (!params.data) { return; } + processing.value = true; const { pointData, areaData } = controller.state; const data = findData(params.data._id, 'point', pointData, areaData); if (data) { controller.onPointClick(data as IMapData); } + processing.value = false; return; } + // 区域点击事件 if (params.seriesType === 'map') { + processing.value = true; const areaCode = opts.value.strAreaCode ? `${params.name}` : Number(params.name); - controller.state.areaCode = areaCode; - const { pointData, areaData, enabledDrillDown } = controller.state; + const areaLevel = areaLevelMap.get(toNumber(areaCode)) || ''; + const { pointData, areaData, enabledDrillDown, mdctrlActiveMode } = + controller.state; if (params.data) { const data = findData(params.data._id, 'area', pointData, areaData); if (data) { - controller.onAreaClick(data as IMapData); + controller.onAreaClick(data as IMapData, areaCode, areaLevel); } } // 禁止切换同一个地图,到最下级的时候会出现这种情况 - if (params.name !== currentName.value && enabledDrillDown) { + if ( + params.name !== currentName.value && + enabledDrillDown && + mdctrlActiveMode !== 2 + ) { + // 行政编码大于当前编码即下钻 + if (toNumber(areaCode) > toNumber(controller.state.areaCode)) { + controller.evt.emit('onDrillDown', { data: { areaCode } }); + } + controller.state.areaCode = areaCode; + controller.state.areaLevel = areaLevel || ''; changeMap(params.name, areaCode); } + processing.value = false; + } + } + }); + chart.on('dblclick', (params: IData) => { + // 区域点击事件 + const { enabledDrillDown, mdctrlActiveMode } = controller.state; + if ( + params.componentType === 'series' && + params.seriesType === 'map' && + mdctrlActiveMode === 2 && + enabledDrillDown + ) { + processing.value = true; + const areaCode = opts.value.strAreaCode + ? `${params.name}` + : Number(params.name); + const areaLevel = areaLevelMap.get(toNumber(areaCode)) || ''; + // 行政编码大于当前编码即下钻 + if (toNumber(areaCode) > toNumber(controller.state.areaCode)) { + controller.evt.emit('onDrillDown', { data: { areaCode } }); + } + controller.state.areaCode = areaCode; + controller.state.areaLevel = areaLevel; + + // 禁止切换同一个地图,到最下级的时候会出现这种情况 + if (params.name !== currentName.value) { + changeMap(params.name, areaCode); } + processing.value = false; } }); @@ -193,6 +247,8 @@ export function useMapManager( chartRef, historyNames, currentName, + processing, + areaLevelMap, changeMap, getCityInfo, goBack,