事件上报
This commit is contained in:
69
src/components/AiAdd/AiAdd.vue
Normal file
69
src/components/AiAdd/AiAdd.vue
Normal file
@@ -0,0 +1,69 @@
|
||||
<template>
|
||||
<movable-area class="movableArea">
|
||||
<movable-view direction="all" x="300" y="500">
|
||||
<div class="AiAdd" @click.stop="add"></div>
|
||||
</movable-view>
|
||||
</movable-area>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "AiAdd",
|
||||
props: {},
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
methods: {
|
||||
add() {
|
||||
console.log(789)
|
||||
this.$emit("add")
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.movableArea {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
z-index: 999;
|
||||
|
||||
uni-movable-view {
|
||||
pointer-events: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.AiAdd {
|
||||
width: 96px;
|
||||
height: 96px;
|
||||
background: #1365DD;
|
||||
box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
|
||||
&:before, &:after {
|
||||
content: "";
|
||||
background: #FFFFFF;
|
||||
display: block;
|
||||
position: absolute;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
&:before {
|
||||
height: 48px;
|
||||
width: 4px;
|
||||
}
|
||||
|
||||
&:after {
|
||||
height: 4px;
|
||||
width: 48px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
118
src/components/AiCard/AiCard.vue
Normal file
118
src/components/AiCard/AiCard.vue
Normal file
@@ -0,0 +1,118 @@
|
||||
<template>
|
||||
<section class="AiCard">
|
||||
<div flex v-if="$slots.custom" class="start">
|
||||
<div class="fill">
|
||||
<slot name="custom"/>
|
||||
</div>
|
||||
<div v-if="$slots.menu" class="iconfont iconfont-iconMore" @tap.stop="handleMore"/>
|
||||
</div>
|
||||
<template v-else>
|
||||
<u-row>
|
||||
<div class="content">
|
||||
<slot/>
|
||||
</div>
|
||||
<div btn @tap="$emit('send')">发送</div>
|
||||
</u-row>
|
||||
<u-row justify="space-between">
|
||||
<slot v-if="$slots.title" name="title"/>
|
||||
<div v-else>{{ cardTitle }}</div>
|
||||
<div v-if="$slots.menu" class="iconfont iconfont-iconMore" @tap.stop="handleMore"/>
|
||||
</u-row>
|
||||
</template>
|
||||
<div v-if="menu" class="mask" @click.stop="handleClose">
|
||||
<div class="moreMenu" :style="menuPos">
|
||||
<slot name="menu"/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "AiCard",
|
||||
props: {
|
||||
cardTitle: String
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
menuPos: {},
|
||||
menu: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleMore({detail}) {
|
||||
this.menuPos = {
|
||||
left: detail.x - 10 + 'px',
|
||||
top: detail.y + 'px'
|
||||
}
|
||||
this.menu = !this.menu
|
||||
},
|
||||
handleClose() {
|
||||
this.menu = false
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.AiCard {
|
||||
width: 100%;
|
||||
background: #FFFFFF;
|
||||
box-shadow: 0 8px 16px 0 rgba(0, 0, 0, 0.02);
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
color: #999;
|
||||
font-size: 26px;
|
||||
flex-shrink: 0;
|
||||
|
||||
.content {
|
||||
background: #F9F9F9;
|
||||
border-radius: 4px;
|
||||
min-height: 120px;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
margin-bottom: 26px;
|
||||
padding: 20px;
|
||||
box-sizing: border-box;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.u-row {
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
|
||||
div[btn] {
|
||||
color: #1365DD;
|
||||
padding: 0 0 0 18px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.iconfont-iconMore {
|
||||
font-size: 38px;
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
.moreMenu {
|
||||
position: fixed;
|
||||
background: #FFFFFF;
|
||||
box-shadow: 0 8px 16px 0 rgba(0, 0, 0, 0.1);
|
||||
border-radius: 4px;
|
||||
transform: translate(-100%, -100%);
|
||||
min-width: 100px;
|
||||
min-height: 100px;
|
||||
z-index: 9;
|
||||
}
|
||||
|
||||
.mask {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 11;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
68
src/components/AiMap/AiMap.vue
Normal file
68
src/components/AiMap/AiMap.vue
Normal file
@@ -0,0 +1,68 @@
|
||||
<template>
|
||||
<section class="AiMap">
|
||||
<div ref="amap" class="map"/>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import AMapLoader from "@amap/amap-jsapi-loader";
|
||||
|
||||
export default {
|
||||
name: "AiMap",
|
||||
props: {
|
||||
plugins: {default: () => ['AMap.DistrictSearch']},
|
||||
map: Object,
|
||||
lib: Object
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
amap: null
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
initMap() {
|
||||
let {plugins} = this
|
||||
AMapLoader.load({
|
||||
key: '54a02a43d9828a8f9cd4f26fe281e74e',
|
||||
version: '2.0',
|
||||
plugins
|
||||
}).then(AMap => {
|
||||
this.amap = new AMap.Map(this.$refs.amap, {
|
||||
resizeEnable: true,
|
||||
zoom: 14,
|
||||
})
|
||||
this.$emit('update:lib', AMap)
|
||||
this.$emit('update:map', this.amap)
|
||||
})
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.initMap()
|
||||
},
|
||||
destroyed() {
|
||||
this.amap?.destroy()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.AiMap {
|
||||
.map {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
::v-deep .amap-logo, ::v-deep .amap-copyright {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
::v-deep .amap-icon {
|
||||
width: 40px !important;
|
||||
height: 40px !important;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
203
src/components/AiTMap/AiTMap.vue
Normal file
203
src/components/AiTMap/AiTMap.vue
Normal file
@@ -0,0 +1,203 @@
|
||||
<template>
|
||||
<section class="AiTMap">
|
||||
<div ref="tmap" class="map"/>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import {mapState} from 'vuex'
|
||||
|
||||
export default {
|
||||
name: "AiTMap",
|
||||
props: {
|
||||
/**
|
||||
* 地区id
|
||||
*/
|
||||
areaId: String,
|
||||
/**
|
||||
* 地图参数,详情见https://lbs.qq.com/webApi/javascriptGL/glDoc/docIndexMap
|
||||
*/
|
||||
ops: {default: () => ({})},
|
||||
/**
|
||||
* 加载三方库,以腾讯地图文档要求进行添加
|
||||
*/
|
||||
libraries: {default: () => ["service"]},
|
||||
/**
|
||||
* 地图实例,支持用.sync绑定获取
|
||||
*/
|
||||
map: Object,
|
||||
/**
|
||||
* 地图库,支持用.sync绑定获取
|
||||
*/
|
||||
lib: Object,
|
||||
},
|
||||
computed: {
|
||||
...mapState(['wxwork']),
|
||||
key() {
|
||||
return process.env.NODE_ENV == "production" ?
|
||||
"RWWBZ-64BEJ-MVLFJ-FTHLQ-JTR6J-SAB2S" :
|
||||
"3RZBZ-LZUCF-CT6J5-NWKZH-FCWOQ-UUFKY"
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
tmap: null,
|
||||
mapLib: null
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
injectLib(url, cb) {
|
||||
const script = document.createElement('script')
|
||||
script.type = 'text/javascript';
|
||||
script.id = "aitmap";
|
||||
script.src = url;
|
||||
script.addEventListener('load', () => {
|
||||
cb && cb()
|
||||
})
|
||||
document.body.appendChild(script);
|
||||
},
|
||||
initTMap() {
|
||||
let latLng = {}
|
||||
if (this.wxwork.config.lat) { //通用版
|
||||
latLng = this.wxwork.config
|
||||
this.mapLib = TMap
|
||||
this.tmap = new TMap.Map(this.$refs.tmap, {
|
||||
zoom: 11,
|
||||
center: new TMap.LatLng(latLng.lat, latLng.lng),
|
||||
...this.ops
|
||||
})
|
||||
this.$emit('update:lib', TMap)
|
||||
this.$emit('update:map', this.tmap)
|
||||
this.$emit('loaded')
|
||||
this.areaId && this.getMapArea()
|
||||
} else { //上架版
|
||||
uni.getLocation({
|
||||
type: "gcj02",
|
||||
success: res => {
|
||||
latLng = {
|
||||
lat: res.latitude,
|
||||
lng: res.longitude
|
||||
}
|
||||
if (latLng.lat) {
|
||||
// this.mapLib = TMap
|
||||
// this.tmap = new TMap.Map(this.$refs.tmap, {
|
||||
// zoom: 11,
|
||||
// center: new TMap.LatLng(latLng.lat, latLng.lng),
|
||||
// ...this.ops
|
||||
// })
|
||||
// this.$emit('update:lib', TMap)
|
||||
// this.$emit('update:map', this.tmap)
|
||||
// this.$emit('loaded')
|
||||
// this.areaId && this.getMapArea()
|
||||
}
|
||||
}
|
||||
})
|
||||
this.mapLib = TMap
|
||||
this.tmap = new TMap.Map(this.$refs.tmap, {
|
||||
zoom: 11,
|
||||
// center: new TMap.LatLng(latLng.lat, latLng.lng),
|
||||
...this.ops
|
||||
})
|
||||
this.$emit('update:lib', TMap)
|
||||
this.$emit('update:map', this.tmap)
|
||||
this.$emit('loaded')
|
||||
this.areaId && this.getMapArea()
|
||||
}
|
||||
},
|
||||
fitBounds(latLngList, count = 0) {
|
||||
// 由多边形顶点坐标数组计算能完整呈现该多边形的最小矩形范围
|
||||
let {mapLib: TMap} = this
|
||||
if (TMap) {
|
||||
if (latLngList.length === 0) {
|
||||
return null;
|
||||
}
|
||||
let boundsN = latLngList[0].getLat();
|
||||
let boundsS = boundsN;
|
||||
let boundsW = latLngList[0].getLng();
|
||||
let boundsE = boundsW;
|
||||
latLngList.forEach((point) => {
|
||||
point.getLat() > boundsN && (boundsN = point.getLat());
|
||||
point.getLat() < boundsS && (boundsS = point.getLat());
|
||||
point.getLng() > boundsE && (boundsE = point.getLng());
|
||||
point.getLng() < boundsW && (boundsW = point.getLng());
|
||||
});
|
||||
return new TMap.LatLngBounds(
|
||||
new TMap.LatLng(boundsS, boundsW),
|
||||
new TMap.LatLng(boundsN, boundsE)
|
||||
);
|
||||
} else {
|
||||
if (count < 5) {
|
||||
this.fitBounds(latLngList, ++count)
|
||||
}
|
||||
}
|
||||
},
|
||||
getMapArea() {
|
||||
let {mapLib, areaId, tmap: map} = this, keyword = areaId.substring(0, 6)
|
||||
let polygons = new TMap.MultiPolygon({map, geometries: []});
|
||||
new mapLib.service.District({
|
||||
polygon: 2,
|
||||
maxOffset: 100
|
||||
}).search({keyword}).then(res => {
|
||||
if (res?.result) {
|
||||
let center = res.result?.[0]?.[0]?.location
|
||||
this.tmap.setCenter(center)
|
||||
res.result.forEach((level) => {
|
||||
level.forEach((place) => {
|
||||
let bounds = [];
|
||||
let newGeometries = place.polygon.map((polygon, index) => {
|
||||
bounds.push(this.fitBounds(polygon)); // 计算能完整呈现行政区边界的最小矩形范围
|
||||
return {
|
||||
id: `${place.id}_${index}`,
|
||||
paths: polygon, // 将得到的行政区划边界用多边形标注在地图上
|
||||
};
|
||||
});
|
||||
bounds = bounds.reduce((a, b) => {
|
||||
return this.fitBounds([
|
||||
a.getNorthEast(),
|
||||
a.getSouthWest(),
|
||||
b.getNorthEast(),
|
||||
b.getSouthWest(),
|
||||
]);
|
||||
}); // 若一行政区有多个多边形边界,应计算能包含所有多边形边界的范围。
|
||||
polygons.updateGeometries(newGeometries);
|
||||
this.tmap.fitBounds(bounds);
|
||||
});
|
||||
});
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
if (!window?.TMap) {
|
||||
window.initTMap = this.initTMap
|
||||
this.injectLib(`https://map.qq.com/api/gljs?v=1.exp&key=${this.key}&libraries=${this.libraries.toString()}&callback=initTMap`)
|
||||
} else {
|
||||
this.initTMap()
|
||||
}
|
||||
},
|
||||
destroyed() {
|
||||
this.map.destroy()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.AiTMap {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
|
||||
::v-deep.map {
|
||||
height: 100%;
|
||||
|
||||
& > div > div {
|
||||
&:nth-of-type(2), &:nth-of-type(3) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user