|
|
@@ -3,7 +3,7 @@
|
|
|
ref="mapRef"
|
|
|
:center="center"
|
|
|
:api-key="googleKey"
|
|
|
- mapId="DEMO_MAP_ID"
|
|
|
+ mapId="japan_4dkankan"
|
|
|
:map-type-control="true"
|
|
|
:disable-default-ui="false"
|
|
|
:language="lang"
|
|
|
@@ -20,53 +20,72 @@
|
|
|
:loading-tip="t('common.loadingText')"
|
|
|
>
|
|
|
<MarkerCluster>
|
|
|
- <AdvancedMarker
|
|
|
- v-for="(location, i) in locations"
|
|
|
- :key="i"
|
|
|
- :options="{ position: location }"
|
|
|
- @click="handleMarkerClick(location)"
|
|
|
- >
|
|
|
- <!-- <InfoWindow>
|
|
|
- <div id="content">
|
|
|
- <div id="siteNotice"></div>
|
|
|
- <h1 id="firstHeading" class="firstHeading">{{ location?.num }}</h1>
|
|
|
- <div id="bodyContent">
|
|
|
- <p>
|
|
|
- <a :href="location?.webSite"> {{ location?.webSite }}</a>
|
|
|
- </p>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </InfoWindow> -->
|
|
|
- </AdvancedMarker>
|
|
|
+ <template v-if="form.type === 0">
|
|
|
+ <CustomMarker
|
|
|
+ v-for="(location, i) in locations"
|
|
|
+ :key="i"
|
|
|
+ :options="{
|
|
|
+ position: { lat: location.lat, lng: location.lng },
|
|
|
+ title: location.title,
|
|
|
+ anchorPoint: 'TOP_CENTER',
|
|
|
+ }"
|
|
|
+ @click="handleMarkerClick(location)"
|
|
|
+ >
|
|
|
+ <div class="custom-marker">{{ location.title }}</div>
|
|
|
+ </CustomMarker>
|
|
|
+ </template>
|
|
|
+ <template v-else>
|
|
|
+ <AdvancedMarker
|
|
|
+ v-for="(location, i) in locations"
|
|
|
+ :key="i"
|
|
|
+ :options="{
|
|
|
+ position: { lat: location.lat, lng: location.lng },
|
|
|
+ title: location.title,
|
|
|
+ }"
|
|
|
+ @click="handleMarkerClick(location)"
|
|
|
+ />
|
|
|
+ </template>
|
|
|
</MarkerCluster>
|
|
|
|
|
|
<CustomControl position="TOP_LEFT">
|
|
|
<div class="top_left_control">
|
|
|
<div>
|
|
|
- <!-- <a-input
|
|
|
- type="text"
|
|
|
- style="width: 220px"
|
|
|
- v-model:value="form.searchValue"
|
|
|
- size="large"
|
|
|
- :placeholder="t('common.inputText')"
|
|
|
- >
|
|
|
- <template #prefix><SearchOutlined /></template>
|
|
|
- </a-input> -->
|
|
|
-
|
|
|
- <AutoComplete
|
|
|
- v-model:value="form.searchValue"
|
|
|
- :options="options"
|
|
|
+ <ApiSelect
|
|
|
+ :api="projectFetch"
|
|
|
size="large"
|
|
|
- allowClear
|
|
|
+ numberToString
|
|
|
+ resultField="list"
|
|
|
+ labelField="title"
|
|
|
+ valueField="id"
|
|
|
+ optionFilterProp="label"
|
|
|
+ v-model:value="form.projectValue"
|
|
|
+ immediate
|
|
|
+ show-search
|
|
|
+ allow-clear
|
|
|
+ v-if="form.type === 0"
|
|
|
+ @select="handleSelect"
|
|
|
style="width: 300px"
|
|
|
- :placeholder="t('common.inputText')"
|
|
|
+ :params="{
|
|
|
+ type: 0,
|
|
|
+ projectId: projectId,
|
|
|
+ searchKey: form.searchValue,
|
|
|
+ }"
|
|
|
+ />
|
|
|
+ <ApiSelect
|
|
|
+ :api="sceneFetch"
|
|
|
+ v-if="form.type === 1"
|
|
|
+ size="large"
|
|
|
+ resultField="list"
|
|
|
+ labelField="title"
|
|
|
+ valueField="num"
|
|
|
+ optionFilterProp="label"
|
|
|
+ immediate
|
|
|
+ show-search
|
|
|
+ allow-clear
|
|
|
@select="handleSelect"
|
|
|
- @search="onSearch"
|
|
|
- >
|
|
|
- <template #option="{ title, value: num }">
|
|
|
- <span style="font-weight: bold" :value="num">{{ title }}</span>
|
|
|
- </template>
|
|
|
- </AutoComplete>
|
|
|
+ style="width: 300px"
|
|
|
+ :params="{ type: 1, searchKey: form.searchValue }"
|
|
|
+ />
|
|
|
</div>
|
|
|
<div>
|
|
|
<Select
|
|
|
@@ -74,60 +93,90 @@
|
|
|
v-model:value="form.type"
|
|
|
style="width: 120px"
|
|
|
size="large"
|
|
|
+ @select="handleTypeSelect"
|
|
|
:placeholder="t('common.chooseText')"
|
|
|
>
|
|
|
<SelectOption :value="0">全部项目</SelectOption>
|
|
|
<SelectOption :value="1">全部场景</SelectOption>
|
|
|
</Select>
|
|
|
</div>
|
|
|
-
|
|
|
- <div>
|
|
|
- <!-- <ApiTreeSelect size="large" style="width: 120px" /> -->
|
|
|
- </div>
|
|
|
</div>
|
|
|
</CustomControl>
|
|
|
|
|
|
- <CustomControl position="LEFT_CENTER">
|
|
|
- <button class="custom-btn">👋</button>
|
|
|
+ <CustomControl position="BOTTOM_CENTER">
|
|
|
+ <div class="share-container">
|
|
|
+ <Popover trigger="click" v-model:open="shareOpen">
|
|
|
+ <template #content>
|
|
|
+ <div class="share-content">
|
|
|
+ <p>扫码分享</p>
|
|
|
+ <QrCode :value="qrCodeShareUrl" :width="250" :height="250" />
|
|
|
+ <a-button text @click="handleCopy">复制链接</a-button>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ <!-- <a-button type="primary">Hover me</a-button> -->
|
|
|
+ <a-button class="custom-btn" @click="handleShare">分享</a-button>
|
|
|
+ </Popover>
|
|
|
+ </div>
|
|
|
</CustomControl>
|
|
|
</GoogleMap>
|
|
|
</template>
|
|
|
|
|
|
<script lang="ts" setup>
|
|
|
// import { SearchOutlined } from '@ant-design/icons-vue';
|
|
|
- import { ref, onMounted, watch, computed } from 'vue';
|
|
|
+ import { ref, onMounted, watch, computed, watchEffect } from 'vue';
|
|
|
import {
|
|
|
GoogleMap,
|
|
|
AdvancedMarker,
|
|
|
MarkerCluster,
|
|
|
+ CustomMarker,
|
|
|
// InfoWindow,
|
|
|
CustomControl,
|
|
|
} from 'vue3-google-map';
|
|
|
import { useRouteQuery } from '@vueuse/router';
|
|
|
- // import { Loading } from '/@/components/Loading';
|
|
|
- import { Select, SelectOption, AutoComplete } from 'ant-design-vue';
|
|
|
- // import { ApiTreeSelect } from '/@/components/Form';
|
|
|
- // import { all } from './test';
|
|
|
+ import { Select, SelectOption, Popover } from 'ant-design-vue';
|
|
|
+ import ApiSelect from '/@/components/Form/src/components/ApiSelect.vue';
|
|
|
+ import { useRouter } from 'vue-router';
|
|
|
const loadingRef = ref(true);
|
|
|
import { useI18n } from '/@/hooks/web/useI18n';
|
|
|
+ import { AllGpsApi, CreateShareMapApi } from '/@/api/mapOpt/list';
|
|
|
+ import { QrCode } from '/@/components/Qrcode/index';
|
|
|
+ import { useCopyToClipboard } from '/@/hooks/web/useCopyToClipboard';
|
|
|
+ import { useMessage } from '/@/hooks/web/useMessage';
|
|
|
|
|
|
- import { AllGpsApi } from '/@/api/mapOpt/list';
|
|
|
const { t } = useI18n();
|
|
|
+ const router = useRouter();
|
|
|
const googleKey = computed(() => import.meta.env.VITE_GOOGLE_KEY);
|
|
|
const center = { lat: 35.717, lng: 139.731 };
|
|
|
- const options = ref([]);
|
|
|
+ const { createMessage } = useMessage();
|
|
|
+ // const options = ref([]);
|
|
|
// const pinOptions = { background: '#FBBC04' };
|
|
|
+ const shareOpen = ref(false);
|
|
|
+ const qrCodeShareUrl = ref('');
|
|
|
+ const { clipboardRef, isSuccessRef } = useCopyToClipboard(qrCodeShareUrl.value);
|
|
|
const lang = useRouteQuery('lang', 'ja');
|
|
|
- // console.log('lang', lang);
|
|
|
-
|
|
|
- const form = ref({
|
|
|
+ const projectId = useRouteQuery('projectId');
|
|
|
+ const type = useRouteQuery('type', 0);
|
|
|
+ const form = ref<{
|
|
|
+ searchValue: null | string;
|
|
|
+ projectValue: null | string;
|
|
|
+ sceneValue: null | string;
|
|
|
+ type: number;
|
|
|
+ }>({
|
|
|
searchValue: '',
|
|
|
- type: 1,
|
|
|
- orgId: undefined,
|
|
|
+ projectValue: '',
|
|
|
+ sceneValue: null,
|
|
|
+ type: 0,
|
|
|
});
|
|
|
|
|
|
const mapRef = ref();
|
|
|
- const locations = ref([]);
|
|
|
+ const locations = ref<
|
|
|
+ {
|
|
|
+ lat: string;
|
|
|
+ lng: string;
|
|
|
+ title: string;
|
|
|
+ webSite: string;
|
|
|
+ }[]
|
|
|
+ >([]);
|
|
|
// locations.value = locations.value.concat(all);
|
|
|
console.log('total', locations.value.length);
|
|
|
|
|
|
@@ -137,7 +186,7 @@
|
|
|
if (!ready) return;
|
|
|
console.log('ready', ready, lo.value.length);
|
|
|
loadingRef.value = false;
|
|
|
- if (ready && lo.value.length > 2) {
|
|
|
+ if (ready && lo.value.length > 0) {
|
|
|
// debugger;
|
|
|
mapFitBounds(mapRef, locations.value);
|
|
|
}
|
|
|
@@ -146,37 +195,25 @@
|
|
|
deep: true,
|
|
|
},
|
|
|
);
|
|
|
- const getGps = () => {
|
|
|
- return AllGpsApi({ searchKey: form.value.searchValue, type: form.value.type });
|
|
|
- };
|
|
|
+
|
|
|
onMounted(async () => {
|
|
|
- const allGps = await getGps();
|
|
|
-
|
|
|
- console.log('allGps', allGps);
|
|
|
- let data = allGps.map((item) => {
|
|
|
- const mapper = {
|
|
|
- lat: Number(item.lat),
|
|
|
- lng: Number(item.lon),
|
|
|
- webSite: item.webSite,
|
|
|
- num: item.num,
|
|
|
- };
|
|
|
- return mapper;
|
|
|
+ watchEffect(() => {
|
|
|
+ console.log(projectId.value);
|
|
|
+ if (projectId.value) {
|
|
|
+ form.value.projectValue = String(projectId.value);
|
|
|
+ }
|
|
|
+ if (type.value) {
|
|
|
+ form.value.type = Number(type.value);
|
|
|
+ }
|
|
|
});
|
|
|
- locations.value = data;
|
|
|
});
|
|
|
|
|
|
- watch(
|
|
|
- () => form,
|
|
|
- async () => {
|
|
|
- // const allGps = await getGps();
|
|
|
- // // const opts = allGps.map
|
|
|
- // options.value = allGps;
|
|
|
- // console.log('allGps', allGps);
|
|
|
- },
|
|
|
- {
|
|
|
- deep: true,
|
|
|
- },
|
|
|
- );
|
|
|
+ function reloadWithParams() {
|
|
|
+ console.log('reloadWithParams');
|
|
|
+ const routeData = router.resolve({ name: 'Map', query: { type: form.value.type } });
|
|
|
+ location.replace(routeData.href);
|
|
|
+ location.reload();
|
|
|
+ }
|
|
|
|
|
|
function mapFitBounds(mapRef, markers) {
|
|
|
let bounds;
|
|
|
@@ -198,23 +235,61 @@
|
|
|
const handleMarkerClick = (data) => {
|
|
|
// const { lat, lng } = event.latLng;
|
|
|
console.log('handleMarkerClick', data);
|
|
|
- window.open(data.webSite);
|
|
|
- // infowindow.value = true;
|
|
|
- // infowindowPosition.value.lat = lat();
|
|
|
- // infowindowPosition.value.lng = lng();
|
|
|
- };
|
|
|
-
|
|
|
- const onSearch = async (item) => {
|
|
|
- console.log('onSearch', item);
|
|
|
- const allGps = await getGps();
|
|
|
- // const opts = allGps.map
|
|
|
- options.value = allGps;
|
|
|
- // debugger;
|
|
|
+ data.webSite && window.open(data.webSite);
|
|
|
};
|
|
|
|
|
|
const handleSelect = (item: any) => {
|
|
|
console.log('onSelect', item);
|
|
|
};
|
|
|
+
|
|
|
+ const handleShare = async () => {
|
|
|
+ const res = await CreateShareMapApi(form.value.type);
|
|
|
+ console.log('handleShare', res);
|
|
|
+ const routeData = router.resolve({
|
|
|
+ name: 'Map',
|
|
|
+ query: { ciphertext: res.ciphertext, type: form.value.type },
|
|
|
+ });
|
|
|
+ const url = location.protocol + '//' + location.hostname + '/' + routeData.href;
|
|
|
+
|
|
|
+ qrCodeShareUrl.value = url;
|
|
|
+ };
|
|
|
+
|
|
|
+ const getMarkerData = (data) => {
|
|
|
+ return data.map((item) => {
|
|
|
+ const mapper = {} as any;
|
|
|
+ mapper.lat = Number(item.lat);
|
|
|
+ mapper.lng = Number(item.lon);
|
|
|
+ mapper.title = item.title;
|
|
|
+ mapper.webSite = item.webSite;
|
|
|
+ return mapper;
|
|
|
+ });
|
|
|
+ };
|
|
|
+
|
|
|
+ const projectFetch = async (params) => {
|
|
|
+ const res = await AllGpsApi(params);
|
|
|
+ const data = getMarkerData(res);
|
|
|
+ console.log('result', data.length);
|
|
|
+ locations.value = data;
|
|
|
+ return res;
|
|
|
+ };
|
|
|
+ const sceneFetch = async (params) => {
|
|
|
+ const res = await AllGpsApi(params);
|
|
|
+ const data = getMarkerData(res);
|
|
|
+ console.log('result', data.length);
|
|
|
+ locations.value = data;
|
|
|
+ return res;
|
|
|
+ };
|
|
|
+
|
|
|
+ const handleTypeSelect = () => {
|
|
|
+ reloadWithParams();
|
|
|
+ };
|
|
|
+
|
|
|
+ const handleCopy = () => {
|
|
|
+ clipboardRef.value = qrCodeShareUrl.value;
|
|
|
+ if (isSuccessRef.value) {
|
|
|
+ createMessage.success(t('routes.scenes.copyInfi.ok'));
|
|
|
+ }
|
|
|
+ };
|
|
|
</script>
|
|
|
<style lang="less">
|
|
|
// @import './dark.less';
|
|
|
@@ -228,4 +303,20 @@
|
|
|
flex-direction: row;
|
|
|
gap: 25px;
|
|
|
}
|
|
|
+ .share-container {
|
|
|
+ padding-bottom: 30px;
|
|
|
+ }
|
|
|
+ .custom-marker {
|
|
|
+ background: rgba(0, 0, 0, 0.6);
|
|
|
+ padding: 8px;
|
|
|
+ border-radius: 10px;
|
|
|
+ color: white;
|
|
|
+ }
|
|
|
+ .share-content {
|
|
|
+ min-width: 300px;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ justify-content: center;
|
|
|
+ align-items: center;
|
|
|
+ }
|
|
|
</style>
|