如何为小约翰可汗的通辽宇宙制作一副地图

前言

作为小约翰可汗的歌迷,他的视频我都看过了至少一遍。每次听到这句因为那个敢于为劳动者说话的超级强权已经不在了 都会感到震撼,每次重看布基纳法索,也就是上沃尔特的故事都会感到惋惜。历史的发展有其客观规律,但是荒诞的事情也时有发生,以史为鉴可知兴替,但是人们时常忘记过去的经验教训,容易好了伤疤忘了疼。

预览

很显然,小约翰可汗的"通辽宇宙"还没有一幅地图。为了直观的感受通辽宇宙,也为了记住一些发生的事,我做了一幅"通辽宇宙地图 (lishiyuan.cn)"。点击链接可以查看,预览如下:

地图标出了已经被小约翰讲过的奇葩小国,以及通辽宇宙的基本面积单位------通辽市和基本人口单位------天通苑与回龙观,点击地图区域可以查看该地区已经发生的故事。目前奇葩小国集中在非洲大区与南美洲大区,硬核狠人最多的国家是英国与美国。

设计

在最初的设计中,我只想标识出已经讲过的奇葩小国,后面加上了单位、神奇组织和硬核狠人的信息。 人们有时会为了学习某项技术而实现某个项目,有时是为了实现某个项目而学习的某些技能。本项目属于后后面的情况,项目是自己想写,然后为了实现这个项目而学习了很多东西。

技术选型

本项目使用Python写爬虫爬取小约翰的投稿数据与信息数据,使用Echarts绘制地图,使用vue + antdv构建前端界面。

实现

主要包括两个部分:数据部分与界面部分。

数据部分

数据部分主要是数据的爬取与整理。这里使用写python爬取B站的合集信息(小约翰创建了奇葩小国、硬核狠人与神奇组织三个合集)。这里使用到了requests与json库。

python 复制代码
# 合集信息 https://api.bilibili.com/x/polymer/web-space/seasons_archives_list?mid=23947287&season_id=665&sort_reverse=false&page_num=1&page_size=30
def seasons_archives_list(mid,seasonId,pageNum):
    url = 'https://api.bilibili.com/x/polymer/web-space/seasons_archives_list'
    headers = {
        'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36 Edg/116.0.1938.69'
    }
    params = {
        "mid":mid,
        "season_id":seasonId,
        "page_num":pageNum,
        "sort_reverse": True,
        "page_size":30
    }
    json = requests.get(url,params=params,headers=headers).json()
    return json['data']

同时,这里也爬取了小约翰的一些账户信息。

python 复制代码
# 获取用户信息
# https://api.bilibili.com/x/space/wbi/acc/info?mid=23947287&token=&platform=web&web_location=1550101&w_rid=76d46ebbed24101710fdcb030df91d61&wts=1693982211
def upinfo(mid):
    url = 'https://api.bilibili.com/x/space/wbi/acc/info'
    headers = {
        'cookie':'buvid3=C1571E59-20C1-B15C-9FBD-41C0001894EB67936infoc;' ,
        'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36 Edg/116.0.1938.69',
    }
    params = {
        "mid":mid,
    }
    json = requests.get(url,params=params,headers=headers).json()
    return json['data']

# up主统计数据  https://api.bilibili.com/x/relation/stat?vmid=23947287&w_rid=e6a6e31cc15fa5b806f9f1ad6ce548bb&wts=1695312456&web_location=333.999
def up_stat(mid):
    url = 'https://api.bilibili.com/x/relation/stat'
    headers = {
        'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36 Edg/116.0.1938.69'
    }
    params = {
        "vmid":mid,
    }
    json = requests.get(url,params=params,headers=headers).json()
    return json['data']

数据整理

  • 小约翰信息数据
python 复制代码
def john_info():
    mid = 23947287
    up_stat_info= up_stat(mid)
    upinfo_json= upinfo(mid)

    john ={
        "mid": mid,
        "name": upinfo_json['name'],
        "sign": upinfo_json['sign'],
        "avatar": upinfo_json['face'], 
        "birthday": upinfo_json['birthday'], 
        "sex": upinfo_json['sex'],
        "title": upinfo_json['official']['title'],
        "follower": up_stat_info['follower'],
        "space":"https://space.bilibili.com/{0}".format(mid),
        "live_room": upinfo_json["live_room"]["url"],
    }

    path= saveFile(john['avatar'])

    john['avatar'] = path

    saveInfo(john,"john.json")
    return
  • 稿件信息数据
python 复制代码
def season(mid,seasonId,fileName):
    pageNum = 1
    bvBathPath = "https://www.bilibili.com/video/{bvid}"
    durationInfo = "{m}分{s}秒"
    
    all_archives=[]

    dataInFile = readFile(fileName=fileName)
    
    while True :
        seasons_archives= seasons_archives_list(mid,seasonId,pageNum)
        page = seasons_archives['page']
        archives = seasons_archives['archives']
        for item in archives :
            
            data = None

            for f in dataInFile:
                if f['bvid'] == item['bvid']:
                    data = f

            all_archives.append({
                "countryName":data['countryName'] if data != None and 'countryName' in data else ['#'],
                "leader":data['leader'] if data != None and 'leader' in data else ['#'],
                "personName":data['personName'] if data != None and 'personName' in data else ['#'],
                "organizationName":data['organizationName'] if data != None and 'organizationName' in data else ['#'],
                "name": item['title'],
                "bvid": item['bvid'],
                "aid": item['aid'],
                "cover": item['pic'],
                "view": item['stat']['view'],
                'duration': durationInfo.format(m=math.floor( item['duration'] / 60 ),s=item['duration'] % 60),
                "pub_date": time.strftime("%Y-%m-%d", time.localtime(item['pubdate'])) ,
                "url": bvBathPath.format(bvid=item['bvid'])
            })

        if(page['page_num'] * page['page_size'] >= page['total']):
            break
        pageNum+=1

    # 下载封面
    

    for item in all_archives:
        path= saveFile(item['cover'])
        item['cover'] = path

    saveInfo(all_archives,fileName)
    return

注意

  • 接口校验规则可能会变化,爬取数据可能会失败。
  • 人物信息、地区信息和组织信息虽然在有些视频简介会标出来,但是有一部分是没有的,这里选择手动补全。这里最省力的方式应该是提取音频转成文字然后通过AI提取我们需要的关键信息。
  • 世界地图数据可以在其他开源项目中找到。
  • 完整代码请查看文章末尾的github项目地址。

前端界面

前端界面核心两部分,地图渲染与事件处理。

地图渲染

首先引入Echarts相关组件:

ts 复制代码
import * as echarts from 'echarts/core';

import {
    TitleComponent,
    VisualMapComponent,
    GeoComponent,
} from 'echarts/components';

import type {
    TitleComponentOption,
    VisualMapComponentOption,
    GeoComponentOption,

} from 'echarts/components'

import { MapChart, EffectScatterChart, CustomChart } from 'echarts/charts';
import type { MapSeriesOption, EffectScatterSeriesOption, CustomSeriesOption } from 'echarts/charts';
import { CanvasRenderer } from 'echarts/renderers';

导入世界地图数据与奇葩小国数据

ts 复制代码
import usaJson from '@/assets/world.geo.json'
import country from '@/assets/country.json'

渲染基本地图信息

ts 复制代码
echarts.use([
    TitleComponent,
    VisualMapComponent,
    GeoComponent,
    MapChart,
    EffectScatterChart,
    CustomChart,
    CanvasRenderer
]);

type EChartsOption = echarts.ComposeOption<
    | TitleComponentOption
    | VisualMapComponentOption
    | GeoComponentOption
    | MapSeriesOption
    | EffectScatterSeriesOption
    | CustomSeriesOption
>;

let mapChart: any

let option: EChartsOption

let countrySet = new Set<string>(country.flatMap(item => item.countryName))

let countryList = Array.from(countrySet).map(item => {
    return {name:item,value:1}
})

onMounted(() => {

    const chartDom = document.getElementById('map')!

    resize()

    mapChart = echarts.init(chartDom, 'dark');

    mapChart.showLoading();

    const axios = inject<Axios>("axios")

    mapChart.hideLoading();

    echarts.registerMap('tongliao-world', usaJson as any, {
        // 此处需要将回龙观、天通苑与通辽标出
    });
    option = {
        title: {
            show: true,
            text: '通辽宇宙地图',
            borderColor: "#FFF",
            // 殷红
            textStyle: {
                color: "#82111f"
            },
            subtextStyle: {
                color: "#82111f"
            },
            subtext: 'the map of tongliao universe',
            top: 64,
            right: 64
        },
        // 藏花红、金黄、姜红
        visualMap: [
            {
                type: 'piecewise',
                show: true,
                hoverLink: false,
                itemWidth: 32,
                id: 'color',
                seriesIndex: 0,
                pieces: [
                    { gte: 1, lte: 1, label: "奇葩小国", color: "#ec2d7a" },
                    // { gte: 2, lte: 2, label: "硬核狠人", color: "#4d1018" },
                    // { gte: 3, lte: 3, label: "神奇组织", color: "#45b787" },
                    { gte: 4, lte: 4, label: "新大陆", color: "#eeb8c3" },
                    { gte: 5, lte: 5, label: "计量单位", color: "#f26b1f" },
                ],
                inverse: true,
                orient: 'vertical',
                left: 64,
                top: 64,
                selectedMode: false,
                textStyle: {
                    color: "#FFF"
                }
            },
        ],
        geo: [
            {
                name: '奇葩小国',
                type: 'map',
                roam: true,
                zoom: 1.1,
                selectedMode: false,
                map: 'tongliao-world',
                label: {
                    color: "#FFF",
                },
                itemStyle: {
                    areaColor: "#eeb8c3",
                    borderWidth: 0
                },
                emphasis: {
                    label: {
                        color: "#FFF",
                        show: true
                    },
                    itemStyle: {
                        areaColor: "#1661ab",
                        shadowColor: 'rgba(0, 0, 0, 0.7)',
                        shadowBlur: 10
                    }
                }
            }
        ],
        series: [
            {
                name: '奇葩小国',
                type: 'map',
                geoIndex: 0,
                selectedMode: false,
                map: 'tongliao-world',
                data: countryList
            }
        ]
    };

})

面积单位与人口单位需要添加新的serie渲染出来,其中通辽需要自定义渲染器才能展示出来。

ts 复制代码
series: [
            {
                name: '奇葩小国',
                type: 'map',
                geoIndex: 0,
                selectedMode: false,
                map: 'tongliao-world',
                data: countryList
            },
            {
                name: '人口单位',
                type: 'effectScatter',
                geoIndex: 0,
                selectedMode: false,
                coordinateSystem: 'geo',
                data: [
                    {
                        name: "天通苑",
                        value: [116.420862, 40.061552],
                        itemStyle: {
                            color: "#f26b1f"
                        }
                    },
                    {
                        name: "回龙观",
                        value: [116.324618, 40.064687],
                        itemStyle: {
                            color: "#f26b1f"
                        },
                    }
                ],
                
                symbolSize: 4,
                label: {
                    formatter: '{b}',
                    color:'#fff',
                    textBorderColor: '#000',
                    textBorderWidth: 2,
                    position: 'right',
                    show: true
                },
            },
            {
                name: "面积单位",
                type: 'custom',
                geoIndex: 0,
                coordinateSystem: 'geo',
                selectedMode: false,
                data: [['通辽']],
                renderItem: (params: any, api: any)=> {
                    let coords = [
                        [123.087539, 44.522239],
                        [123.399602, 44.139572],
                        [123.577924, 43.646975],
                        [123.355022, 43.528576],
                        [123.741386, 43.301896],
                        [123.488763, 43.02007],
                        [123.280721, 42.802395],
                        [122.626874, 42.824197],
                        [122.374251, 42.682347],
                        [121.973026, 42.660495],
                        [121.586662, 42.474443],
                        [121.111137, 42.243849],
                        [120.858514, 42.232847],
                        [120.427569, 43.052654],
                        [120.769353, 43.366749],
                        [120.48701, 43.388351],
                        [120.992255, 43.603948],
                        [120.813933, 43.7866],
                        [120.769353, 44.075551],
                        [120.368128, 44.363098],
                        [119.253616, 45.269626],
                        [119.283336, 45.447136],
                        [119.550819, 45.634481],
                        [119.996624, 45.457561],
                        [120.011484, 45.634481],
                        [120.293827, 45.572102],
                        [120.947675, 45.196371],
                        [121.958166, 44.320587],
                        [122.463412, 44.235473],
                        [123.087539, 44.522239]
                    ]
                    let points = []
                    for (let i = 0; i < coords.length; i++) {
                        points.push(api.coord(coords[i]))
                    }
                    
                    return {
                        type: 'polygon',
                        shape: {
                            points: points
                        },
                        style: {
                            fill: '#f26b1f'
                        },
                        emphasis: {
                            style: {
                                shadowColor: 'rgba(0, 0, 0, 0.7)',
                                shadowBlur: 10
                            }
                        },
                        focus:'self',
                        textContent: {
                            type:'text',
                            style: {
                                textStroke: '#000',
                                lineWidth:2,
                                text: api.value(0)
                            }
                        },
                        textConfig: {
                            insideFill:'#FFF',
                            position: 'inside',
                        },
                    }
                }
            }
            
        ]
            

为了响应事件,需要向外发送区域点击事件,需要响应resize操作

ts 复制代码
const clickArea = defineEmits(['clickArea'])

mapChart.on("click", (params: any) => {
        clickArea('clickArea', params)
    })

window.onresize = () => {
    resize()
    mapChart.resize()
};

function resize() {
    chartDom.style.width = window.innerWidth + 'px'
    chartDom.style.height = window.innerHeight + 'px'
}

function mapResize() {
    console.log('mapResize')
    mapChart.setOption(option, true)
}
defineExpose({
    mapResize,
})

完整代码

html 复制代码
<script setup lang="ts">
// 地图主页
import * as echarts from 'echarts/core';

import {
    TitleComponent,
    VisualMapComponent,
    GeoComponent,
} from 'echarts/components';

import type {
    TitleComponentOption,
    VisualMapComponentOption,
    GeoComponentOption,

} from 'echarts/components'

import { MapChart, EffectScatterChart, CustomChart } from 'echarts/charts';
import type { MapSeriesOption, EffectScatterSeriesOption, CustomSeriesOption } from 'echarts/charts';
import { CanvasRenderer } from 'echarts/renderers';
import { inject, onMounted } from 'vue';
import type { Axios } from 'axios';
import usaJson from '@/assets/world.geo.json'
import country from '@/assets/country.json'

const clickArea = defineEmits(['clickArea'])

echarts.use([
    TitleComponent,
    VisualMapComponent,
    GeoComponent,
    MapChart,
    EffectScatterChart,
    CustomChart,
    CanvasRenderer
]);

type EChartsOption = echarts.ComposeOption<
    | TitleComponentOption
    | VisualMapComponentOption
    | GeoComponentOption
    | MapSeriesOption
    | EffectScatterSeriesOption
    | CustomSeriesOption
>;

let mapChart: any

let option: EChartsOption

let countrySet = new Set<string>(country.flatMap(item => item.countryName))

let countryList = Array.from(countrySet).map(item => {
    return {name:item,value:1}
})

onMounted(() => {

    const chartDom = document.getElementById('map')!

    resize()

    mapChart = echarts.init(chartDom, 'dark');

    mapChart.showLoading();

    const axios = inject<Axios>("axios")

    mapChart.hideLoading();

    echarts.registerMap('tongliao-world', usaJson as any, {
        // 此处需要将回龙观、天通苑与通辽标出
    });
    option = {
        title: {
            show: true,
            text: '通辽宇宙地图',
            borderColor: "#FFF",
            // 殷红
            textStyle: {
                color: "#82111f"
            },
            subtextStyle: {
                color: "#82111f"
            },
            subtext: 'the map of tongliao universe',
            top: 64,
            right: 64
        },
        // 藏花红、金黄、姜红
        visualMap: [
            {
                type: 'piecewise',
                show: true,
                hoverLink: false,
                itemWidth: 32,
                id: 'color',
                seriesIndex: 0,
                pieces: [
                    { gte: 1, lte: 1, label: "奇葩小国", color: "#ec2d7a" },
                    // { gte: 2, lte: 2, label: "硬核狠人", color: "#4d1018" },
                    // { gte: 3, lte: 3, label: "神奇组织", color: "#45b787" },
                    { gte: 4, lte: 4, label: "新大陆", color: "#eeb8c3" },
                    { gte: 5, lte: 5, label: "计量单位", color: "#f26b1f" },
                ],
                inverse: true,
                orient: 'vertical',
                left: 64,
                top: 64,
                selectedMode: false,
                textStyle: {
                    color: "#FFF"
                }
            },
        ],
        geo: [
            {
                name: '奇葩小国',
                type: 'map',
                roam: true,
                zoom: 1.1,
                selectedMode: false,
                map: 'tongliao-world',
                label: {
                    color: "#FFF",
                },
                itemStyle: {
                    areaColor: "#eeb8c3",
                    borderWidth: 0
                },
                emphasis: {
                    label: {
                        color: "#FFF",
                        show: true
                    },
                    itemStyle: {
                        areaColor: "#1661ab",
                        shadowColor: 'rgba(0, 0, 0, 0.7)',
                        shadowBlur: 10
                    }
                }
            }
        ],
        series: [
            {
                name: '奇葩小国',
                type: 'map',
                geoIndex: 0,
                selectedMode: false,
                map: 'tongliao-world',
                data: countryList
            },
            {
                name: '人口单位',
                type: 'effectScatter',
                geoIndex: 0,
                selectedMode: false,
                coordinateSystem: 'geo',
                data: [
                    {
                        name: "天通苑",
                        value: [116.420862, 40.061552],
                        itemStyle: {
                            color: "#f26b1f"
                        }
                    },
                    {
                        name: "回龙观",
                        value: [116.324618, 40.064687],
                        itemStyle: {
                            color: "#f26b1f"
                        },
                    }
                ],
                
                symbolSize: 4,
                label: {
                    formatter: '{b}',
                    color:'#fff',
                    textBorderColor: '#000',
                    textBorderWidth: 2,
                    position: 'right',
                    show: true
                },
            },
            {
                name: "面积单位",
                type: 'custom',
                geoIndex: 0,
                coordinateSystem: 'geo',
                selectedMode: false,
                data: [['通辽']],
                renderItem: (params: any, api: any)=> {
                    let coords = [
                        [123.087539, 44.522239],
                        [123.399602, 44.139572],
                        [123.577924, 43.646975],
                        [123.355022, 43.528576],
                        [123.741386, 43.301896],
                        [123.488763, 43.02007],
                        [123.280721, 42.802395],
                        [122.626874, 42.824197],
                        [122.374251, 42.682347],
                        [121.973026, 42.660495],
                        [121.586662, 42.474443],
                        [121.111137, 42.243849],
                        [120.858514, 42.232847],
                        [120.427569, 43.052654],
                        [120.769353, 43.366749],
                        [120.48701, 43.388351],
                        [120.992255, 43.603948],
                        [120.813933, 43.7866],
                        [120.769353, 44.075551],
                        [120.368128, 44.363098],
                        [119.253616, 45.269626],
                        [119.283336, 45.447136],
                        [119.550819, 45.634481],
                        [119.996624, 45.457561],
                        [120.011484, 45.634481],
                        [120.293827, 45.572102],
                        [120.947675, 45.196371],
                        [121.958166, 44.320587],
                        [122.463412, 44.235473],
                        [123.087539, 44.522239]
                    ]
                    let points = []
                    for (let i = 0; i < coords.length; i++) {
                        points.push(api.coord(coords[i]))
                    }
                    
                    return {
                        type: 'polygon',
                        shape: {
                            points: points
                        },
                        style: {
                            fill: '#f26b1f'
                        },
                        emphasis: {
                            style: {
                                shadowColor: 'rgba(0, 0, 0, 0.7)',
                                shadowBlur: 10
                            }
                        },
                        focus:'self',
                        textContent: {
                            type:'text',
                            style: {
                                textStroke: '#000',
                                lineWidth:2,
                                text: api.value(0)
                            }
                        },
                        textConfig: {
                            insideFill:'#FFF',
                            position: 'inside',
                        },
                    }
                }
            }
        ]
    };

    mapChart.setOption(option);

    mapChart.on("click", (params: any) => {
        clickArea('clickArea', params)
    })

    window.onresize = () => {
        resize()
        mapChart.resize()
    };

    function resize() {
        chartDom.style.width = window.innerWidth + 'px'
        chartDom.style.height = window.innerHeight + 'px'
    }

})



function mapResize() {
    console.log('mapResize')
    mapChart.setOption(option, true)
}

defineExpose({
    mapResize,
})

</script>

<template>
    <div id="map"></div>
</template>

<style scoped></style>

事件处理

主要处理区域点击、界面重置与打开关于页

ts 复制代码
function clickArea(params: any) {
  // 如果data有数据则打开弹窗
  console.log(params)
  
  let data= params.data

  let name = '';
  if(data instanceof Array){
    name = data[0]
  }else{
    if(data==undefined){
      name = params.name
    }else{
      name = data?.name
    }
    
  }

  // 通辽,回龙观,天通苑
  if('通辽' == name){
    activeUnit.value = unitInfos.tongliao
    unitInfoVisible.value = true
  }else if('回龙观' == name){
    activeUnit.value = unitInfos.huilongguang
    unitInfoVisible.value = true
  }else if('天通苑' == name){
    activeUnit.value = unitInfos.tiantongyuan
    unitInfoVisible.value = true
  }else{
    // 从json筛选数据
    activeTitle.value = name
    personInfo.value = []
    countryInfo.value = []
    organizationInfo.value = []
    personInfo.value = person.filter(it=>it.countryName.includes(name))
    countryInfo.value = country.filter(it=>it.countryName.includes(name))
    organizationInfo.value = organization.filter(it=>it.countryName.includes(name))
    aeraInfoVisible.value = true
  }

}


function toAbout() {
  router.push({ name: "about" })

}

function resize() {
  map.value.mapResize()
}

完整代码

html 复制代码
<script setup lang="ts">
import MapView from './map/MapView.vue';
import {
  QuestionCircleOutlined,
  SyncOutlined,
  ShareAltOutlined,
  EyeOutlined,
  CopyOutlined
} from '@ant-design/icons-vue';
import { ref } from 'vue';
import { useRouter } from 'vue-router';

import john from '@/assets/john.json'
import liganma from '@/assets/liganma.json'
import ClipboardJS from 'clipboard'
import { message } from 'ant-design-vue';
import Card from '@/components/Card.vue';
import List from '@/components/List.vue';

import country from '@/assets/country.json'
import person from '@/assets/person.json'
import organization from '@/assets/organization.json'


const [messageApi, contextHolder] = message.useMessage();

const map = ref()

const qrcodeVisible = ref<boolean>(false);

const shareVisible = ref<boolean>(false);

const aeraInfoVisible = ref<boolean>(false);

const unitInfoVisible = ref<boolean>(false)

const activeTitle = ref<string>();

const personInfo = ref<Array<{
  
}>>([]);

const countryInfo = ref<Array<{
  
}>>([]);

const organizationInfo = ref<Array<{

}>>([]);

const href = window.location.href

const followInfo = {
  john: john,
  liganma: liganma,
}
const router = useRouter()

function toAbout() {
  router.push({ name: "about" })

}

function resize() {
  map.value.mapResize()
}

function share() {
  console.log(href)
  // 打开弹窗展示二维码
  shareVisible.value = true
  new ClipboardJS("#copyShareLink")
}

function follow() {
  // 打开李干嘛与小约翰的弹窗
  qrcodeVisible.value = true
}

const unitInfos= {
  tongliao:{
    title: "内蒙古通辽市",
    sign: "通辽,简写T,通辽宇宙基本面积单位。总面积59535平方千米。截至2022年10月,全市辖1个区、1个县和5个旗,代管1个县级市。"
  },
  huilongguang:{
    title:"北京回龙观社区",
    sign: "回龙观,通辽宇宙基本人口小单位。回龙观是具有850万平方米的超大规模社区,常驻人口将达到30万人。"
  },
  tiantongyuan:{
    title:"北京天通苑社区",
    sign: "天通苑,通辽宇宙基本人口大单位。天通苑是亚洲最大社区,人口约50万人。"
  }
}

const activeUnit = ref<{
  title?:string,
  sign?:string
}>()

function clickArea(params: any) {
  // 如果data有数据则打开弹窗
  console.log(params)
  
  let data= params.data

  let name = '';
  if(data instanceof Array){
    name = data[0]
  }else{
    if(data==undefined){
      name = params.name
    }else{
      name = data?.name
    }
    
  }

  // 通辽,回龙观,天通苑
  if('通辽' == name){
    activeUnit.value = unitInfos.tongliao
    unitInfoVisible.value = true
  }else if('回龙观' == name){
    activeUnit.value = unitInfos.huilongguang
    unitInfoVisible.value = true
  }else if('天通苑' == name){
    activeUnit.value = unitInfos.tiantongyuan
    unitInfoVisible.value = true
  }else{
    // 从json筛选数据
    activeTitle.value = name
    personInfo.value = []
    countryInfo.value = []
    organizationInfo.value = []
    personInfo.value = person.filter(it=>it.countryName.includes(name))
    countryInfo.value = country.filter(it=>it.countryName.includes(name))
    organizationInfo.value = organization.filter(it=>it.countryName.includes(name))
    aeraInfoVisible.value = true
  }

}

</script>

<template>
  <div class="content">
    <MapView ref="map" @click-area="clickArea"></MapView>

    <!-- <Card></Card> -->
    <a-float-button-group shape="square" :style="{ right: '64px' }">
      <!-- 关于  -->
      <a-float-button @click="toAbout" tooltip="关于">
        <template #icon>
          <QuestionCircleOutlined />
        </template>
      </a-float-button>
      <!-- 同步  -->
      <a-float-button @click="resize" tooltip="重置地图">
        <template #icon>
          <SyncOutlined />
        </template>
      </a-float-button>
      <!-- 分享  -->
      <a-float-button @click="share" tooltip="分享">
        <template #icon>
          <ShareAltOutlined />
        </template>
      </a-float-button>
      <!-- 关注  -->
      <a-float-button @click="follow" tooltip="关注">
        <template #icon>
          <EyeOutlined />
        </template>
      </a-float-button>
    </a-float-button-group>

    <div class="models">
      <a-modal v-model:open="qrcodeVisible" title="感谢关注" centered @ok="qrcodeVisible = false">
        <a-row :gutter="[16, 24]" justify="space-around" >
          <a-col :span="11" justify="space-around" align="middle" class="space-box">
            
            <Card :title="followInfo.john.name" :avatar="'image/'+ followInfo.john.avatar" :qrcode="followInfo.john.space" :sign="followInfo.john.sign"></Card>

          </a-col>
          <a-col :span="11" justify="space-around" align="middle" class="space-box">
            <Card :title="followInfo.liganma.name" :avatar="'image/'+ followInfo.liganma.avatar" :qrcode="followInfo.liganma.space" :sign="followInfo.liganma.sign"></Card>

          </a-col>
        </a-row>

        <template #footer>
        </template>
      </a-modal>

      <a-modal v-model:open="shareVisible" title="分享给朋友" centered @ok="shareVisible = false">
        <a-row :gutter="[16, 24]" justify="space-around" align="middle">
          <a-col :span="24" justify="space-around" align="middle">

            <a-space direction="vertical" style="width: 100%;">
              <a-input-group compact>
                <a-button>
                  链接
                </a-button>
                <a-input :value="href" style="width: calc(100% - 200px)" id="shareLink" />
                <a-tooltip title="复制链接">
                  <a-button id="copyShareLink" data-clipboard-action="copy" data-clipboard-target="#shareLink">
                    <template #icon>
                      <CopyOutlined />
                    </template>
                  </a-button>
                </a-tooltip>
              </a-input-group>
              <a-qrcode error-level="H" :value="href" icon="favicon.ico" />
            </a-space>

          </a-col>
        </a-row>

        <template #footer></template>
      </a-modal>

      <a-modal v-model:open="aeraInfoVisible" :title="activeTitle" centered @ok="aeraInfoVisible = false" width="1080px" >
        <a-row justify="space-around" >
          <a-col :span="7" justify="space-around" align="middle" class="space-box">
            
            <List title="奇葩小国" :list="countryInfo"></List>

          </a-col>

          <a-col :span="7" justify="space-around" align="middle" class="space-box">
            
            <List title="硬核狠人" :list="personInfo"></List>

          </a-col>

          <a-col :span="7" justify="space-around" align="middle" class="space-box">
            <List title="神奇组织" :list="organizationInfo"></List>
          </a-col>
        </a-row>

        <template #footer></template>
      </a-modal>

      <a-modal v-model:open="unitInfoVisible" title="单位信息" centered @ok="unitInfoVisible = false" >
        <a-row justify="space-around" >
          <a-col :span="24" justify="space-around" align="middle">
            
            <Card :title="activeUnit?.title" :sign="activeUnit?.sign" ></Card>

          </a-col>
        </a-row>

        <template #footer></template>
      </a-modal>
    </div>
  </div>
</template>

<style scoped>
.content {
  position: relative;
}

.space-box{
  box-shadow: rgba(99, 99, 99, 0.2) 0px 2px 8px 0px; 
  border-radius: 15px; 
  padding: 10px;
}
</style>

其他部分

dockerfile

dockerfile 复制代码
FROM nginx:mainline-alpine3.18-slim
COPY ./dist /usr/share/nginx/html

打包

npm run build-only

打包镜像

docker build -t tongliao-universe .

docker部署

docker run -p 8080:80 --name tongliao-universe -d tongliao-universe

注意

相关推荐
qq_589568101 小时前
Echarts+vue电商平台数据可视化——后台实现笔记
vue.js·信息可视化·echarts
5hand2 小时前
Element-ui的使用教程 基于HBuilder X
前端·javascript·vue.js·elementui
GDAL2 小时前
vue3入门教程:ref能否完全替代reactive?
前端·javascript·vue.js
z千鑫3 小时前
【前端】详解前端三大主流框架:React、Vue与Angular的比较与选择
前端·vue.js·react.js
苹果醋34 小时前
React系列(八)——React进阶知识点拓展
运维·vue.js·spring boot·nginx·课程设计
王小王和他的小伙伴5 小时前
解决 vue3 中 echarts图表在el-dialog中显示问题
javascript·vue.js·echarts
好名字08215 小时前
前端取Content-Disposition中的filename字段与解码(vue)
前端·javascript·vue.js·前端框架
隐形喷火龙6 小时前
element ui--下拉根据拼音首字母过滤
前端·vue.js·ui
Simaoya7 小时前
【vue】css模拟玻璃球体效果(带高光)
前端·css·vue.js
ekskef_sef7 小时前
前端Vue框架基础介绍
前端·javascript·vue.js