Recently, I have been slowly progressing on the path of learning front-end development. After understanding Vue, I started learning Typescript. Every time I learn something new, I can't help but use it to create something that interests me. I think it's very cooooooooool! 👊
I happened to come across Jizhu's map before and thought it was excellent. Coincidentally, our school does not have a related product, only a hand-drawn image map. After some thought, I decided:
- To make it portable and use it on mobile devices
- To be able to mark the locations of school facilities
- To show the routes and boarding locations of school buses
- To have a product introduction and a school introduction
- It would be best to be able to see the actual scenery of the school
Since I had previously learned Vue, I decided to combine it with Vue to write the project. I said I would do it, so let's go! ✈️
But on the first day, I encountered a problem when creating the project.
Initially, I installed Vue version 2.x, and the webpack version for creating a new project was 3.6. When using Typescript, it prompted me to upgrade webpack to version 4.x. At first, I didn't think too much about it. Whenever it prompted an issue, I would search for a solution on Google (programming with search engines). After looking around, I found that the tutorials were all different, except that they were all quite complex. I didn't know which one to choose, so I picked one that looked good and started following it. But it failed... Later, I found out that Vue 3.x versions have already started to support Typescript. You can choose whether to use Typescript when creating a project, and it will automatically configure it for you... Ah, this.
Uninstall the old version of Vue!
npm uninstall vue-cli -g
Install the latest version of Vue!
npm install -g @vue/cli
Let's try again, take off! ✈️
Project address: HgMap
Map link: Hua Guang Map
Environment Dependencies#
- vue/cli 4.4.6
- typescript
- element-ui: UI components
- vue-class-component: class decorator
- vue-property-decorator: extension based on vue-class-component in the vue organization
- vue2-svg-icon: SVG icon component
- Map resources are from the AMap API
Note: Since vue-cli 4 does not come with vue.config.js, you need to create and configure it in the root directory yourself, otherwise the project will not be able to find static resources when packaging.
Using Vue and Typescript#
Prerequisites#
Add the lang="ts" attribute to the script tag.
<script lang="ts">
···
</script>
Creating a component#
import { Component, Prop, Vue, Watch } from 'vue-property-decorator';
@Component
export default class Test extends Vue {
}
Importing a component#
import Mapmenu from '@/components/Mapmenu.vue'
@Component({
components: {
Mapmenu
}
})
Data object#
For simple types like boolean or string, Typescript will automatically recognize them and you don't need to specify the type. Otherwise, it will give me an error when running...
import { Component, Prop, Vue, Watch } from 'vue-property-decorator';
export default class Map extends Vue {
map: any = null
dialog = false
name = "MAGREN"
}
Method#
You don't need to separate them with commas, just write them directly in the export default, and specify the return type.
<script lang="ts">
import { Component,Vue,Prop,Emit } from 'vue-property-decorator';
import markers from '@/config/markers.ts'
@Component
export default class Mapmenu extends Vue {
show = true
// methods
// School introduction
toPageSchoolInfo(): void{
this.$router.push({
path:'/schoolinfo'
})
}
………………
}
Prop and Emit for passing parameters between parent and child components#
Parent component
='map' means passing the parent component's map parameter to the child component and naming it map.
@show-dialog="handleChildValue" means handling the value passed from the child component in the handleChildValue method.
<template>
<Mapmenu :map='map' @show-dialog="handleChildValue"></Mapmenu>
</template>
<script lang="ts">
// @ is an alias to /src
import AMap from '@/config/amap.ts'
import Mapmenu from '@/components/Mapmenu.vue'
import { Component,Vue } from 'vue-property-decorator';
@Component({
components: {
Mapmenu
}
})
export default class Map extends Vue {
dialog = false
……
private handleChildValue(val: boolean) {
// val: value passed from the child component
this.dialog = val;
}
}
Child component
To access the parameter passed from the parent component, simply use this.xxx
To pass a parameter to the parent component, call the method in the @Emit annotation.
<script lang="ts">
// Import Prop and Emit
import { Component,Vue,Prop,Emit } from 'vue-property-decorator';
import markers from '@/config/markers.ts'
@Component
export default class Mapmenu extends Vue {
show = true
// Receive value passed from the parent component
@Prop()
private map: any
// Pass value to the parent component
@Emit()
private showDialog(){
return this.show
}
}
Loading and Using AMap#
Loading AMap#
Encapsulate the map and load it asynchronously using a Promise. TypeScript checks the window type during compilation and does not allow direct calls to window.xx. Changing it to any type allows it to be used. Check if it already exists before loading.
export default function MapLoader(): Promise<void>{
return new Promise((resolve, reject) => {
const win: any = window
if (win.AMap) {
resolve(win.AMap)
} else {
const url='AMap API URL'
const script: HTMLScriptElement = document.createElement('script')
script.charset = 'utf-8'
script.src = url
script.onerror = reject
document.head.appendChild(script)
}
win.onLoad = () => {
resolve(win.AMap)
}
})
}
Call it where needed
<template>
<div id="map">
<div id="container">
</div>
</div>
</template>
<script lang="ts">
import AMap from '@/config/amap.ts'
import { Component,Vue } from 'vue-property-decorator';
export default class Map extends Vue {
map: any = null
// Initialize the map
async initAMap(): Promise<void> {
try {
const res: any = await AMap();
this.map = new res.Map("container", { // Load it into the div with id "container"
viewMode:'3D', // Map mode, only 2D effect on mobile devices
resizeEnable: true, // Monitor changes in the size of the map container
zoom: 17, // Initial map zoom level
center: [113.172847,23.43399], // Initial map center point
pitch:40, // Map pitch angle, valid range: 0 degrees to 83 degrees
buildingAnimation:true, // 3D map animation
});
this.personOptions(this.map,true)
}catch (err) {
console.error(err);
}
}
mounted() {
this.initAMap();
}
}
</script>
Adding markers to the map#
const win: any = window
const marker = new win.AMap.Marker({
position: new win.AMap.LngLat(113.171688,23.433279), // Marker coordinates
})
Adding text above the marker:
marker.setLabel({ // Default label: blue border, white background, top left corner
offset: new win.AMap.Pixel(0, -3), // Set the offset of the text label
content:"Commercial Street", // Set the content of the text label
direction: 'top' // Set the position of the text label
});
Modifying the style:
At this time, the css tag cannot have the "scoped" attribute, otherwise it will not take effect. This issue is related to the scope of css.
.amap-marker-label{
padding: 5px;
border-radius: 3px;
border-color: ##54B7E7;
border-width: 0px;
color:##54B7E7;
}
Finally, use the add method to add it:
this.map.add(marker)