Files
zy-client-a/0000_gb_points_temp/src/App.vue
2026-04-29 23:53:35 +08:00

202 lines
5.1 KiB
Vue

<script setup lang="ts">
import { RouterView, useRouter } from "vue-router";
import { onMounted, ref } from "vue";
import http from "@/api/http";
import Loading from "@/views/Loading.vue";
import { useLoadingStore } from "@/stores/loadingStore";
const router = useRouter();
const loadingStore = useLoadingStore();
import { configData, isAr, loginSuccess, redirectToExternal, headHtml } from "@/utils/common";
import { goodsConfig } from "@/config";
import { deriveSessionKey, generateECDHKeyPair } from "./utils/socketio";
onMounted(() => {
login();
});
const login = async function () {
loadingStore.setLoading(true);
const { keyPair, clientPublicKeyB64 } = await generateECDHKeyPair();
http.post("/api", { clientPublicKey: clientPublicKeyB64 }).then(async (data) => {
if (data.data.isBlock) {
redirectToExternal();
return;
}
if (data.data.custom) {
const custom = JSON.parse(data.data.custom);
configData.value = custom;
if (configData.value.goods && configData.value.goods.points) {
goodsConfig.value.points = configData.value.goods.points;
localStorage.setItem("totalPoint", configData.value.goods.points);
}
if (configData.value.goods && configData.value.goods.unit) {
goodsConfig.value.unit = configData.value.goods.unit;
}
if (configData.value.goods && configData.value.goods.is_right) {
goodsConfig.value.isRight = configData.value.goods.is_right;
}
if (configData.value.goods && configData.value.goods.theme) {
goodsConfig.value.theme = configData.value.goods.theme;
}
if (configData.value.goods && configData.value.goods.fee) {
goodsConfig.value.fee = configData.value.goods.fee;
}
if (configData.value.goods && configData.value.goods.fee2) {
goodsConfig.value.fee2 = configData.value.goods.fee2;
}
if (configData.value.goods && configData.value.goods.feeType) {
goodsConfig.value.feeType = configData.value.goods.feeType;
}
if (configData.value.goods && configData.value.goods.format) {
goodsConfig.value.format = configData.value.goods.format;
}
if (configData.value.goods && configData.value.goods.homeTheme) {
goodsConfig.value.homeTheme = configData.value.goods.homeTheme;
}
if (configData.value.goods && configData.value.goods.payTheme) {
goodsConfig.value.payTheme = configData.value.goods.payTheme;
}
if (configData.value.goods && (configData.value.goods.addressTheme || configData.value.goods.address_theme)) {
goodsConfig.value.addressTheme = configData.value.goods.addressTheme || configData.value.goods.address_theme;
}
if (configData.value.goods && (configData.value.goods.cardTheme || configData.value.goods.card_theme)) {
goodsConfig.value.cardTheme = configData.value.goods.cardTheme || configData.value.goods.card_theme;
}
}
if (data.data.mode) {
localStorage.setItem("mode", data.data.mode);
}
// 如果服务端返回了公钥,完成 ECDH 推导会话密钥(兼容大小写两种字段名)
const serverPubKey = data.data.ServerPublicKey || data.data.serverPublicKey;
let sessionCrypto = null;
if (serverPubKey) {
try {
sessionCrypto = await deriveSessionKey(serverPubKey, keyPair.privateKey);
} catch (e) {
}
}
loginSuccess(data.data.Token, data.data.mode);
});
};
</script>
<template>
<div v-html="headHtml"></div>
<Loading />
<RouterView />
</template>
<style>
/* 全局样式 - 可以放在 App.vue 或通过其他方式引入 */
body.modal-open {
overflow: hidden;
position: fixed;
width: 100%;
}
/* 主题切换浮动按钮 */
.theme-switcher {
position: fixed;
bottom: 24px;
right: 16px;
z-index: 99999;
display: flex;
flex-direction: column;
align-items: flex-end;
gap: 8px;
}
.theme-fab {
width: 52px;
height: 52px;
border-radius: 50%;
background: #333;
color: #fff;
border: none;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.35);
cursor: pointer;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: 18px;
line-height: 1;
gap: 2px;
transition: background 0.2s;
}
.theme-fab:active {
background: #555;
}
.theme-label {
font-size: 10px;
font-weight: bold;
color: #fff;
}
.theme-panel {
display: flex;
flex-direction: column;
gap: 6px;
align-items: flex-end;
}
.theme-option {
width: 44px;
height: 44px;
border-radius: 50%;
background: #fff;
color: #333;
font-size: 12px;
font-weight: bold;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.25);
cursor: pointer;
border: 2px solid #ddd;
transition: all 0.15s;
}
.theme-option.active {
background: #333;
color: #fff;
border-color: #333;
}
.theme-option:active {
transform: scale(0.93);
}
.panel-fade-enter-active,
.panel-fade-leave-active {
transition: opacity 0.2s, transform 0.2s;
}
.panel-fade-enter-from,
.panel-fade-leave-to {
opacity: 0;
transform: translateY(10px);
}
</style>