yuang-framework-ui-pc
Version:
yuang-framework-ui-pc Library
734 lines (733 loc) • 25.4 kB
JavaScript
;
const vue = require("vue");
const AMapLoader = require("@amap/amap-jsapi-loader");
const elementPlus = require("element-plus");
const icons = require("../../icons");
const EleLoading = require("../../ele-loading/index");
const props = require("../props");
const ICON_CLASS = "ele-map-view-body-icon";
const _sfc_main = vue.defineComponent({
name: "MapView",
components: {
ElAutocomplete: elementPlus.ElAutocomplete,
ElInput: elementPlus.ElInput,
ElButton: elementPlus.ElButton,
ElIcon: elementPlus.ElIcon,
ElEmpty: elementPlus.ElEmpty,
EleLoading,
PlusOutlined: icons.PlusOutlined,
EnvironmentOutlined: icons.EnvironmentOutlined,
CheckCircleOutlined: icons.CheckCircleOutlined,
SearchOutlined: icons.SearchOutlined
},
props: props.mapProps,
emits: {
mapDone: (_ins) => true,
select: (_selected) => true,
done: (_result) => true
},
setup(props2, { emit }) {
const state = {};
const mapRef = vue.ref(null);
const autocompleteRef = vue.ref(null);
const listRef = vue.ref(null);
const loading = vue.ref(true);
const poiLoading = vue.ref(false);
const confirmLoading = vue.ref(false);
const data = vue.ref([]);
const suggestionData = vue.ref([]);
const centerIconClass = vue.ref([ICON_CLASS]);
const keywords = vue.ref("");
const current = vue.ref(null);
const confirmDisabled = vue.computed(() => props2.required && !current.value);
const keywordMode = vue.computed(() => "keyword" === props2.mode);
const poiMode = vue.computed(() => "poi" === props2.mode);
const getInitSelected = () => {
if (props2.selected != null) {
const { lng, lat } = props2.selected;
if (lat != null && lng != null) {
return props2.selected;
}
}
};
const isSamePOI = (poi, item) => {
return item.lat === poi.lat && item.lng === poi.lng && item.name === poi.name && item.address === poi.address;
};
const addFirstPOI = (poi, list) => {
const index = list.findIndex((d) => isSamePOI(poi, d));
if (index == -1) {
return [poi, ...list];
}
const result = [...list];
result[index] = poi;
return result;
};
const isNullCity = (city) => {
if (city == null || typeof city !== "object" || city.province == null && city.city == null && city.district == null && city.citycode == null) {
return true;
}
return false;
};
const formatPOI = (d) => {
const name = d.name || "";
const addr = Array.isArray(d.address) ? d.address[0] : d.address;
const address = addr || "";
const { lat, lng } = d.location;
const city = isNullCity(d.city) ? void 0 : d.city;
const key = [name, address, lat, lng].join(",");
return { ...d, name, address, lat, lng, city, key };
};
const scrollToTop = () => {
const listEl = listRef.value;
if (listEl) {
listEl.scrollTop = 0;
}
};
const scrollToActive = () => {
const listEl = listRef.value;
if (listEl) {
const el = listEl.querySelector(".ele-map-view-item.is-active");
if (el) {
if (typeof el["scrollIntoViewIfNeeded"] === "function") {
el.scrollIntoViewIfNeeded(true);
} else {
el.scrollIntoView({ behavior: "smooth", block: "center" });
}
}
}
};
const setSelected = (item, trigger) => {
if (current.value !== item) {
current.value = item;
if (trigger) {
emit("select", current.value);
}
}
};
const searchKeywords = (keyword) => {
return new Promise((resolve, reject) => {
if (!state.autoCompleteIns) {
reject(new Error("AutoComplete instance is null"));
return;
}
state.autoCompleteIns.search(keyword, (status, result) => {
if (status === "error") {
const msg = status + " " + (result ? JSON.stringify(result) : "");
reject(new Error(msg));
return;
}
if (!(result == null ? void 0 : result.tips)) {
resolve([]);
return;
}
const tips = result.tips.filter((d) => !!d.location);
resolve(tips.map((d) => formatPOI(d)));
});
});
};
const searchNearBy = (lng, lat) => {
return new Promise((resolve, reject) => {
if (!state.placeSearchIns) {
reject(new Error("PlaceSearch instance is null"));
return;
}
state.placeSearchIns.searchNearBy(
props2.poiKeywords,
[lng, lat],
props2.poiRadius,
(status, result) => {
var _a;
if (status === "complete" && ((_a = result == null ? void 0 : result.poiList) == null ? void 0 : _a.pois)) {
const ps = result.poiList.pois.filter((d) => !!d.location);
resolve(ps.map((d) => formatPOI(d)));
return;
}
if (status === "no_data") {
resolve([]);
return;
}
const msg = status + " " + (result ? JSON.stringify(result) : "");
reject(new Error(msg));
}
);
});
};
const searchPOI = (lng, lat) => {
loading.value = true;
poiLoading.value = true;
searchNearBy(lng, lat).then((result) => {
poiLoading.value = false;
loading.value = false;
if (!state.selectedSuggestion) {
data.value = result;
setSelected(null, true);
scrollToTop();
return;
}
data.value = addFirstPOI(state.selectedSuggestion, result);
setSelected(state.selectedSuggestion, true);
state.selectedSuggestion = null;
vue.nextTick(() => {
});
}).catch((e) => {
console.error(e);
poiLoading.value = false;
loading.value = false;
const selectedSuggestion = state.selectedSuggestion;
data.value = selectedSuggestion ? [selectedSuggestion] : [];
setSelected(selectedSuggestion ? selectedSuggestion : null, true);
});
};
const bounceIcon = () => {
centerIconClass.value = [ICON_CLASS];
vue.nextTick(() => {
setTimeout(() => {
centerIconClass.value = [ICON_CLASS, "ele-map-view-anim-bounce"];
}, 0);
});
};
const removeCenterMarker = () => {
if (state.centerMarker && state.mapIns) {
state.mapIns.remove(state.centerMarker);
}
};
const showCenterMarker = (lng, lat) => {
if (!state.centerMarker) {
console.error("centerMarker is null");
return;
}
if (!state.mapIns) {
console.error("map instance is null");
return;
}
removeCenterMarker();
if (lng != null && lat != null) {
state.centerMarker.setPosition([lng, lat]);
state.mapIns.add(state.centerMarker);
}
};
const setMapCenter = (lng, lat, zoom) => {
if (state.mapIns && lng != null && lat != null) {
if (zoom == null) {
state.mapIns.setCenter([lng, lat]);
} else {
state.mapIns.setZoomAndCenter(zoom, [lng, lat]);
}
}
};
const getMapCenter = (returnRegions) => {
return new Promise((resolve, reject) => {
if (!state.mapIns) {
reject(new Error("map instance is null"));
return;
}
const result = state.mapIns.getCenter();
if (!returnRegions) {
resolve(result || {});
return;
}
state.mapIns.getCity((city) => {
resolve({ ...result || {}, city });
});
});
};
const changeMapStyle = (style) => {
if (state.mapIns) {
if (typeof style === "boolean") {
if (style) {
state.mapIns.setMapStyle("amap://styles/dark");
} else {
state.mapIns.setMapStyle("amap://styles/normal");
}
} else if (style) {
state.mapIns.setMapStyle(style);
}
}
};
const destroyMap = () => {
state.mapIns && state.mapIns.destroy();
state.centerMarker = null;
state.placeSearchIns = null;
state.autoCompleteIns = null;
state.mapIns = null;
};
const destroyAll = () => {
destroyMap();
state.lastSuggestion = "";
state.selectedSuggestion = null;
state.isItemClickMove = false;
data.value = [];
suggestionData.value = [];
keywords.value = "";
setSelected(null);
};
const renderMap = () => {
if (!props2.mapKey || state.mapIns) {
return;
}
AMapLoader.load({
key: props2.mapKey,
version: props2.mapVersion,
plugins: ["AMap.PlaceSearch", "AMap.AutoComplete"]
}).then((AMap) => {
destroyAll();
state.centerMarker = new AMap.Marker({
icon: new AMap.Icon({
image: props2.markerSrc,
size: new AMap.Size(26, 36.5),
imageSize: new AMap.Size(26, 36.5)
}),
offset: new AMap.Pixel(-13, -36.5)
});
state.autoCompleteIns = new AMap.AutoComplete({
city: props2.suggestionCity
});
state.placeSearchIns = new AMap.PlaceSearch({
type: props2.poiType,
pageSize: props2.poiLimit,
pageIndex: 1
});
const selected = getInitSelected();
const selCenter = selected ? [selected.lng, selected.lat] : void 0;
const defaultStyle = props2.darkMode ? "amap://styles/dark" : void 0;
state.mapIns = new AMap.Map(mapRef.value, {
zoom: selected == null ? props2.zoom : props2.selectedZoom,
// 初缩放级别
center: selCenter || props2.center,
// 初始中心点
resizeEnable: true,
// 监控地图容器尺寸变化
mapStyle: props2.mapStyle || defaultStyle
});
state.mapIns.on("complete", () => {
const selected2 = getInitSelected();
if (selected2 != null) {
setSelected(selected2);
if (!poiMode.value) {
const { lng, lat } = selected2;
showCenterMarker(lng, lat);
}
}
if (poiMode.value || keywordMode.value) {
state.selectedSuggestion = selected2;
const { lng, lat } = state.mapIns.getCenter();
searchPOI(lng, lat);
return;
}
loading.value = false;
});
state.mapIns.on("moveend", () => {
if (state.isItemClickMove || !poiMode.value) {
state.isItemClickMove = false;
return;
}
bounceIcon();
const { lng, lat } = state.mapIns.getCenter();
searchPOI(lng, lat);
});
state.mapIns.on("click", (e) => {
if (poiMode.value || keywordMode.value) {
return;
}
if (e.lnglat == null || typeof e.lnglat !== "object") {
console.error(e);
return;
}
const { lng, lat } = e.lnglat;
setSelected(formatPOI({ location: { lat, lng } }), true);
setMapCenter(lng, lat, props2.selectedZoom);
showCenterMarker(lng, lat);
});
emit("mapDone", state.mapIns);
}).catch((e) => {
console.error(e);
});
};
const getMapIns = () => {
return state.mapIns;
};
const showInitSelected = () => {
const selected = getInitSelected();
if (state.mapIns == null || selected == null) {
return;
}
setSelected(selected);
const { lng, lat } = selected;
state.isItemClickMove = true;
setMapCenter(lng, lat, props2.selectedZoom);
if (!poiMode.value) {
showCenterMarker(lng, lat);
}
if ((keywordMode.value || poiMode.value) && !data.value.includes(selected)) {
data.value = addFirstPOI(selected, data.value);
}
vue.nextTick(() => {
scrollToActive();
});
};
const handleSearch = (keyword, callback) => {
if (!keyword || state.lastSuggestion === keyword) {
callback && callback(suggestionData.value);
return;
}
state.lastSuggestion = keyword;
if (keywordMode.value) {
poiLoading.value = true;
}
searchKeywords(keyword).then((result) => {
if (keywordMode.value) {
data.value = result;
poiLoading.value = false;
setSelected(null, true);
removeCenterMarker();
scrollToTop();
return;
}
suggestionData.value = result;
callback && callback(suggestionData.value);
}).catch((e) => {
console.error(e);
poiLoading.value = false;
callback && callback(suggestionData.value);
});
};
const handleSearchSelect = (item) => {
autocompleteRef.value && autocompleteRef.value.blur();
if (!item) {
return;
}
const { lng, lat } = item;
if (lng == null || lat == null) {
return;
}
if (!poiMode.value) {
showCenterMarker(lng, lat);
setMapCenter(lng, lat, props2.selectedZoom);
setSelected(item, true);
return;
}
loading.value = true;
state.selectedSuggestion = item;
state.isItemClickMove = true;
setMapCenter(lng, lat, props2.selectedZoom);
bounceIcon();
searchPOI(lng, lat);
};
const handleItemClick = (item) => {
state.isItemClickMove = true;
setSelected(item, true);
const { lng, lat } = item;
setMapCenter(lng, lat, props2.selectedZoom);
if (poiMode.value) {
bounceIcon();
return;
}
showCenterMarker(lng, lat);
};
const handleConfirm = () => {
if (!current.value) {
confirmLoading.value = true;
getMapCenter(props2.returnRegions).then((result) => {
confirmLoading.value = false;
emit("done", result);
}).catch((e) => {
console.error(e);
confirmLoading.value = false;
emit("done", {});
});
return;
}
if (!props2.returnRegions || !isNullCity(current.value.city)) {
emit("done", current.value);
return;
}
confirmLoading.value = true;
state.isItemClickMove = true;
setMapCenter(current.value.lng, current.value.lat);
getMapCenter(true).then(({ city }) => {
confirmLoading.value = false;
emit("done", { ...current.value || {}, city });
}).catch((e) => {
console.error(e);
confirmLoading.value = false;
emit("done", current.value || {});
});
};
vue.watch(
() => props2.selected,
() => {
showInitSelected();
}
);
vue.watch(
() => props2.darkMode,
(darkMode) => {
if (!props2.mapStyle) {
changeMapStyle(darkMode);
}
}
);
vue.watch(
() => props2.mapStyle,
(mapStyle) => {
if (mapStyle) {
changeMapStyle(mapStyle);
}
}
);
vue.watch(
() => props2.mode,
(mode) => {
keywords.value = "";
suggestionData.value = [];
state.selectedSuggestion = null;
state.lastSuggestion = "";
removeCenterMarker();
if (mode !== "poi" && current.value) {
const { lng, lat } = current.value.location;
showCenterMarker(lng, lat);
}
if (!data.value.length && (mode === "poi" || mode === "keyword")) {
if (current.value) {
const { lng, lat } = current.value.location;
searchPOI(lng, lat);
} else {
const { lng, lat } = state.mapIns.getCenter();
searchPOI(lng, lat);
}
}
}
);
vue.watch(
() => props2.mapKey,
() => {
destroyAll();
renderMap();
}
);
vue.onMounted(() => {
renderMap();
});
vue.onBeforeUnmount(() => {
destroyAll();
});
return {
SearchOutlined: icons.SearchOutlined,
mapRef,
autocompleteRef,
listRef,
loading,
poiLoading,
confirmLoading,
data,
suggestionData,
centerIconClass,
keywords,
current,
confirmDisabled,
keywordMode,
poiMode,
getMapIns,
showInitSelected,
handleSearch,
handleSearchSelect,
handleItemClick,
handleConfirm
};
}
});
const _export_sfc = (sfc, props2) => {
const target = sfc.__vccOpts || sfc;
for (const [key, val] of props2) {
target[key] = val;
}
return target;
};
const _hoisted_1 = { class: "ele-map-view-body" };
const _hoisted_2 = {
ref: "mapRef",
style: { "height": "100%" }
};
const _hoisted_3 = ["src"];
const _hoisted_4 = { class: "ele-map-suggest-item" };
const _hoisted_5 = { class: "ele-map-suggest-title" };
const _hoisted_6 = { class: "ele-map-suggest-text" };
const _hoisted_7 = {
key: 0,
class: "ele-map-view-message"
};
const _hoisted_8 = {
ref: "listRef",
class: "ele-map-view-list"
};
const _hoisted_9 = ["onClick"];
const _hoisted_10 = { class: "ele-map-view-item-body" };
const _hoisted_11 = { class: "ele-map-view-item-title" };
const _hoisted_12 = {
key: 0,
class: "ele-map-view-item-text"
};
const _hoisted_13 = {
key: 0,
class: "ele-map-view-empty"
};
const _hoisted_14 = { class: "ele-map-view-extra" };
const _hoisted_15 = {
key: 0,
class: "ele-map-view-message"
};
function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
const _component_PlusOutlined = vue.resolveComponent("PlusOutlined");
const _component_ElIcon = vue.resolveComponent("ElIcon");
const _component_SearchOutlined = vue.resolveComponent("SearchOutlined");
const _component_ElAutocomplete = vue.resolveComponent("ElAutocomplete");
const _component_ElButton = vue.resolveComponent("ElButton");
const _component_ElInput = vue.resolveComponent("ElInput");
const _component_EnvironmentOutlined = vue.resolveComponent("EnvironmentOutlined");
const _component_CheckCircleOutlined = vue.resolveComponent("CheckCircleOutlined");
const _component_ElEmpty = vue.resolveComponent("ElEmpty");
const _component_EleLoading = vue.resolveComponent("EleLoading");
return vue.openBlock(), vue.createBlock(_component_EleLoading, {
loading: _ctx.loading,
class: vue.normalizeClass(["ele-map-view", { "is-poi-mode": _ctx.poiMode }]),
style: vue.normalizeStyle({ height: _ctx.height })
}, {
default: vue.withCtx(() => [
vue.createElementVNode("div", _hoisted_1, [
vue.createElementVNode("div", _hoisted_2, null, 512),
_ctx.poiMode ? (vue.openBlock(), vue.createElementBlock(vue.Fragment, { key: 0 }, [
vue.createVNode(_component_ElIcon, { class: "ele-map-view-icon-plus" }, {
default: vue.withCtx(() => [
vue.createVNode(_component_PlusOutlined)
]),
_: 1
}),
vue.createElementVNode("img", {
src: _ctx.markerSrc,
class: vue.normalizeClass(_ctx.centerIconClass)
}, null, 10, _hoisted_3)
], 64)) : vue.createCommentVNode("", true),
_ctx.filterable && !_ctx.keywordMode ? (vue.openBlock(), vue.createElementBlock("div", {
key: 1,
class: "ele-map-view-search",
style: vue.normalizeStyle(_ctx.searchStyle)
}, [
vue.createVNode(_component_ElAutocomplete, {
ref: "autocompleteRef",
valueKey: "name",
clearable: true,
modelValue: _ctx.keywords,
"onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => _ctx.keywords = $event),
placeholder: _ctx.searchPlaceholder,
popperClass: "ele-map-suggest-popper",
fetchSuggestions: _ctx.handleSearch,
onSelect: _ctx.handleSearchSelect
}, {
prefix: vue.withCtx(() => [
vue.createVNode(_component_ElIcon, { class: "el-input__icon" }, {
default: vue.withCtx(() => [
vue.createVNode(_component_SearchOutlined)
]),
_: 1
})
]),
default: vue.withCtx(({ item }) => [
vue.createElementVNode("div", _hoisted_4, [
vue.createElementVNode("div", _hoisted_5, vue.toDisplayString(item.name), 1),
vue.createElementVNode("div", _hoisted_6, vue.toDisplayString(item.district), 1)
])
]),
_: 1
}, 8, ["modelValue", "placeholder", "fetchSuggestions", "onSelect"])
], 4)) : vue.createCommentVNode("", true),
!_ctx.poiMode && !_ctx.keywordMode ? (vue.openBlock(), vue.createElementBlock(vue.Fragment, { key: 2 }, [
_ctx.confirmDisabled && _ctx.clickMessage ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_7, vue.toDisplayString(_ctx.clickMessage), 1)) : (vue.openBlock(), vue.createBlock(_component_ElButton, {
key: 1,
type: "primary",
loading: _ctx.confirmLoading,
disabled: _ctx.confirmDisabled,
class: "ele-map-view-btn-ok",
onClick: _ctx.handleConfirm
}, {
default: vue.withCtx(() => [
vue.createTextVNode(vue.toDisplayString(_ctx.okText), 1)
]),
_: 1
}, 8, ["loading", "disabled", "onClick"]))
], 64)) : vue.createCommentVNode("", true)
]),
_ctx.keywordMode || _ctx.poiMode ? (vue.openBlock(), vue.createBlock(_component_EleLoading, {
key: 0,
loading: _ctx.poiLoading,
style: vue.normalizeStyle(_ctx.sideStyle),
class: "ele-map-view-side"
}, {
default: vue.withCtx(() => [
_ctx.keywordMode ? (vue.openBlock(), vue.createElementBlock("div", {
key: 0,
class: "ele-map-view-search",
style: vue.normalizeStyle(_ctx.searchStyle)
}, [
vue.createVNode(_component_ElInput, {
clearable: true,
modelValue: _ctx.keywords,
"onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => _ctx.keywords = $event),
prefixIcon: _ctx.SearchOutlined,
placeholder: _ctx.searchPlaceholder,
validateEvent: false,
onChange: _ctx.handleSearch
}, null, 8, ["modelValue", "prefixIcon", "placeholder", "onChange"])
], 4)) : vue.createCommentVNode("", true),
vue.createElementVNode("div", _hoisted_8, [
(vue.openBlock(true), vue.createElementBlock(vue.Fragment, null, vue.renderList(_ctx.data, (item) => {
return vue.openBlock(), vue.createElementBlock("div", {
key: item.key,
class: vue.normalizeClass(["ele-map-view-item", { "is-active": item === _ctx.current }]),
onClick: ($event) => _ctx.handleItemClick(item)
}, [
vue.createVNode(_component_ElIcon, { class: "ele-map-view-item-icon" }, {
default: vue.withCtx(() => [
vue.createVNode(_component_EnvironmentOutlined)
]),
_: 1
}),
vue.createElementVNode("div", _hoisted_10, [
vue.createElementVNode("div", _hoisted_11, vue.toDisplayString(item.name), 1),
item.address ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_12, vue.toDisplayString(item.address), 1)) : vue.createCommentVNode("", true)
]),
vue.createVNode(_component_ElIcon, { class: "ele-map-view-item-radio" }, {
default: vue.withCtx(() => [
vue.createVNode(_component_CheckCircleOutlined)
]),
_: 1
})
], 10, _hoisted_9);
}), 128)),
!_ctx.data || !_ctx.data.length ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_13, [
vue.createVNode(_component_ElEmpty, vue.mergeProps({ imageSize: 80 }, _ctx.emptyProps || {}), null, 16)
])) : vue.createCommentVNode("", true)
], 512),
vue.createElementVNode("div", _hoisted_14, [
_ctx.confirmDisabled && _ctx.message ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_15, vue.toDisplayString(_ctx.message), 1)) : (vue.openBlock(), vue.createBlock(_component_ElButton, {
key: 1,
type: "primary",
loading: _ctx.confirmLoading,
disabled: _ctx.confirmDisabled,
class: "ele-map-view-btn-ok",
onClick: _ctx.handleConfirm
}, {
default: vue.withCtx(() => [
vue.createTextVNode(vue.toDisplayString(_ctx.okText), 1)
]),
_: 1
}, 8, ["loading", "disabled", "onClick"]))
])
]),
_: 1
}, 8, ["loading", "style"])) : vue.createCommentVNode("", true)
]),
_: 1
}, 8, ["loading", "class", "style"]);
}
const mapView = /* @__PURE__ */ _export_sfc(_sfc_main, [["render", _sfc_render]]);
module.exports = mapView;