fix(地图): 完成地图页面及其功能

This commit is contained in:
hzj 2025-09-05 22:35:59 +08:00
parent d962fe9102
commit f730e2461f
9 changed files with 219 additions and 20 deletions

View File

@ -19,6 +19,7 @@
},
"dependencies": {
"airwallex-payment-elements": "^1.131.0",
"pinia": "^3.0.3",
"vue": "^3.5.18",
"vue-router": "^4.5.1"
},

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -1,17 +1,17 @@
import './assets/main.css'
import { logEnvInfo } from './utils/env.js'
import { versionChecker } from './utils/versionChecker.js'
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import "./assets/main.css";
import { logEnvInfo } from "./utils/env.js";
import { versionChecker } from "./utils/versionChecker.js";
import { createPinia } from "pinia";
import { createApp } from "vue";
import App from "./App.vue";
import router from "./router";
// 初始化环境信息
logEnvInfo()
logEnvInfo();
// 版本检查
if (versionChecker.checkVersion()) {
console.log('检测到新版本,正在更新...')
console.log("检测到新版本,正在更新...");
// 可以选择显示一个更新提示
// if (confirm('检测到新版本,是否立即更新?')) {
// versionChecker.forceReload()
@ -19,10 +19,11 @@ if (versionChecker.checkVersion()) {
}
// 初始化权限系统
console.debug('🔐 Permission system initialized')
console.debug("🔐 Permission system initialized");
const pinia = createPinia();
const app = createApp(App);
const app = createApp(App)
app.use(router);
app.use(pinia);
app.use(router)
app.mount('#app')
app.mount("#app");

View File

@ -185,6 +185,12 @@ const router = createRouter({
component: () => import("../views/RechargeFreightAgent.vue"),
meta: { requiresAuth: true },
},
{
path: "/map",
name: "map",
component: () => import("../views/map.vue"),
meta: { requiresAuth: true },
},
{
path: "/pay-result",
name: "pay-result",

16
src/stores/country.js Normal file
View File

@ -0,0 +1,16 @@
import { defineStore } from "pinia";
export const useCountryStore = defineStore("country", {
state: () => ({
list: [],
selectedCountry: {},
}),
actions: {
setCountryList(data) {
this.list = data;
},
setCountry(country) {
this.selectedCountry = country;
},
},
});

View File

@ -30,6 +30,7 @@ export const PAGES = {
ADMIN_CENTER: "/admin-center",
RECHARGE_STANDARD: "/recharge-standard",
PAY_RESULT: "/pay-result",
MAP: "/map",
};
// 🎯 核心改变:基于身份的权限配置
@ -106,6 +107,7 @@ export const ROLE_PERMISSIONS = {
PUBLIC_PAGES: [
PAGES.NOT_APP, // 错误页面
PAGES.PAY_RESULT, // 支付成功页面
PAGES.MAP, // 地图选择页面
"/recharge",
"/recharge-freight-agent",
],

View File

@ -141,6 +141,7 @@ import MobileHeader from "../components/MobileHeader.vue";
import { onMounted, ref, computed } from "vue";
import { searchUser } from "@/api/userInfo";
// import { getUserIdentity } from "@/api/wallet";
import { showError, showWarning, showInfo, showSuccess } from "@/utils/toast.js";
import { useRouter } from "vue-router";
const router = useRouter();

View File

@ -1,5 +1,5 @@
<template>
<div>
<div class="fullPage">
<!-- 顶部 -->
<MobileHeader title="Recharge">
<template v-slot:extraFunction>
@ -72,7 +72,7 @@
<div style="width: 100%; display: flex; justify-content: space-between; margin: 10px 0">
<div style="font-weight: 600">Select a country</div>
<div style="font-weight: 500; color: rgba(0, 0, 0, 0.4)">
<div style="font-weight: 500; color: rgba(0, 0, 0, 0.4)" @click="goMap">
<img src="../assets/icon/Vector.png" alt="" style="width: 10px" />
{{ selectedCountry.countryName }}
</div>
@ -142,7 +142,11 @@ import { goAirwallex } from "@/utils/payment";
import { showError, showWarning, showInfo, showSuccess } from "@/utils/toast.js";
import { useRoute } from "vue-router";
import router from "@/router";
import { useCountryStore } from "@/stores/country";
import { storeToRefs } from "pinia";
const countryStore = useCountryStore();
const route = useRoute();
const languageList = ref([
@ -161,9 +165,10 @@ const listBt = () => {
const applicationId = ref("1963531459019739137");
const userProfile = ref({}); //
const type = ref(""); //
const countryList = ref([]); //
const regionId = ref(""); //id
const selectedCountry = ref({}); //
const { list: countryList, selectedCountry } = storeToRefs(countryStore); //
const channels = ref([]); //
const selectedChannelName = ref(""); //
@ -171,6 +176,11 @@ const selectedChannelName = ref(""); //选中的支付渠道
const commodityList = ref([]); //
const selectedCommodity = ref({}); //
//
const goMap = () => {
router.push({ path: "/map" });
};
const goPay = async (item, commodity) => {
selectedCommodity.value = commodity;
let payData = {
@ -230,8 +240,11 @@ onMounted(async () => {
if (userInfo.body && userInfo.body.regionId && userInfo.body.countryList.length != 0) {
userProfile.value = { ...userInfo.body.userProfile, type: "GOLD" };
countryList.value = userInfo.body.countryList;
selectedCountry.value = userInfo.body.countryList[0];
countryStore.setCountryList(userInfo.body.countryList);
if (JSON.stringify(selectedCountry.value) == "{}") {
countryStore.setCountry(userInfo.body.countryList[0]);
}
regionId.value = userInfo.body.regionId;
let appCommodityCardParams = {
@ -249,6 +262,14 @@ onMounted(async () => {
</script>
<style scoped>
.fullPage {
width: 100vw;
height: 100vh;
background-image: url(../assets/images/secondBg.png);
background-size: 100% 100%;
background-repeat: no-repeat;
}
.slide-fade-enter-active {
transition: all 0.3s ease-out;
}

151
src/views/map.vue Normal file
View File

@ -0,0 +1,151 @@
<template>
<div class="fullPage">
<div>
<!-- 顶部 -->
<MobileHeader title="Country&Region">
<template v-slot:extraFunction>
<div style="display: flex; justify-content: center; align-items: center; width: 10%">
<img src="../assets/icon/selected.png" alt="" style="width: 100%" />
</div>
</template>
</MobileHeader>
</div>
<!-- 内容 -->
<div style="padding: 10px">
<div style="font-weight: 510; color: rgba(0, 0, 0, 0.4)">Select your country</div>
<div v-for="(countries, gIndex) in groupedCountries" :key="gIndex">
<div style="font-weight: 400; color: rgba(0, 0, 0, 0.4)">{{ countries.letter }}</div>
<div
style="
margin: 10px 0;
border-radius: 12px;
box-shadow: 0 0 4px 0 #00000050;
padding: 10px;
background-color: white;
"
>
<div
v-for="(country, cIndex) in countries.countries"
:key="cIndex"
style="
display: flex;
justify-content: space-between;
align-items: center;
margin: 10px 0;
"
@click="selectC(gIndex, cIndex, country)"
>
<div style="display: flex; align-items: center">
<img
:src="country.country.nationalFlag"
alt=""
style="height: 16px; margin-right: 10px"
/>
{{ country.countryName }}
</div>
<div
style="
width: 16px;
height: 16px;
border: 1px solid rgba(0, 0, 0, 0.4);
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
"
>
<div
style="
width: 10px;
height: 10px;
border-radius: 50%;
background-color: rgba(187, 146, 255, 1);
"
v-if="groupIndex == gIndex && countryIndex == cIndex"
></div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { computed, onMounted, ref } from "vue";
import MobileHeader from "../components/MobileHeader.vue";
import { useRoute } from "vue-router";
import { useCountryStore } from "@/stores/country";
import { storeToRefs } from "pinia";
const route = useRoute();
const countryStore = useCountryStore();
const { list: countryList, selectedCountry } = storeToRefs(countryStore); //
const groupIndex = ref(-1); //
const countryIndex = ref(-1); //
const selectC = (gIndex, cIndex, country) => {
groupIndex.value = gIndex;
countryIndex.value = cIndex;
countryStore.setCountry(country);
}; //
const groupedCountries = computed(() => {
const groups = {};
countryList.value.forEach((item) => {
let firstLetter = item.countryName.charAt(0)?.toUpperCase() || "#"; //
const normalizedLetter = /[A-Z]/.test(firstLetter) ? firstLetter : "#"; //
if (!groups.hasOwnProperty(normalizedLetter)) {
groups[normalizedLetter] = [];
}
groups[normalizedLetter].push(item);
});
let resCountryList = Object.keys(groups)
.sort()
.map((letter) => ({
letter,
countries: groups[letter].sort((a, b) => a.countryName.localeCompare(b.countryName)),
}));
let firstLetter = selectedCountry.value.countryName.charAt(0)?.toUpperCase() || "#"; //
resCountryList.forEach((resitem, gIndex) => {
if (resitem.letter == firstLetter) {
resitem.countries.forEach((citem, cIndex) => {
if (citem.countryName == selectedCountry.value.countryName) {
groupIndex.value = gIndex;
countryIndex.value = cIndex;
}
});
}
});
return resCountryList;
});
onMounted(() => {
const countryListStr = sessionStorage.getItem("countryList");
selectedCountry.value = sessionStorage.getItem("selectedCountry");
if (countryListStr) {
countryList.value = JSON.parse(countryListStr);
//
// sessionStorage.removeItem('tempCountryList');
}
});
</script>
<style scoped>
.fullPage {
width: 100vw;
height: 100vh;
background-image: url(../assets/images/secondBg.png);
background-size: 100% 100%;
background-repeat: no-repeat;
}
</style>