修改concept

This commit is contained in:
2025-12-28 13:42:57 +08:00
parent d083ab3aa2
commit 75db6901c0
7 changed files with 202 additions and 7 deletions

View File

@@ -2,6 +2,195 @@
* 文本处理工具函数 * 文本处理工具函数
*/ */
/**
* 简单的 MD5 实现(与 Python hashlib.md5 兼容)
* 用于生成概念链接的哈希值
*/
const md5 = (string) => {
function rotateLeft(lValue, iShiftBits) {
return (lValue << iShiftBits) | (lValue >>> (32 - iShiftBits));
}
function addUnsigned(lX, lY) {
const lX8 = lX & 0x80000000;
const lY8 = lY & 0x80000000;
const lX4 = lX & 0x40000000;
const lY4 = lY & 0x40000000;
const lResult = (lX & 0x3FFFFFFF) + (lY & 0x3FFFFFFF);
if (lX4 & lY4) return lResult ^ 0x80000000 ^ lX8 ^ lY8;
if (lX4 | lY4) {
if (lResult & 0x40000000) return lResult ^ 0xC0000000 ^ lX8 ^ lY8;
return lResult ^ 0x40000000 ^ lX8 ^ lY8;
}
return lResult ^ lX8 ^ lY8;
}
function f(x, y, z) { return (x & y) | (~x & z); }
function g(x, y, z) { return (x & z) | (y & ~z); }
function h(x, y, z) { return x ^ y ^ z; }
function i(x, y, z) { return y ^ (x | ~z); }
function ff(a, b, c, d, x, s, ac) {
a = addUnsigned(a, addUnsigned(addUnsigned(f(b, c, d), x), ac));
return addUnsigned(rotateLeft(a, s), b);
}
function gg(a, b, c, d, x, s, ac) {
a = addUnsigned(a, addUnsigned(addUnsigned(g(b, c, d), x), ac));
return addUnsigned(rotateLeft(a, s), b);
}
function hh(a, b, c, d, x, s, ac) {
a = addUnsigned(a, addUnsigned(addUnsigned(h(b, c, d), x), ac));
return addUnsigned(rotateLeft(a, s), b);
}
function ii(a, b, c, d, x, s, ac) {
a = addUnsigned(a, addUnsigned(addUnsigned(i(b, c, d), x), ac));
return addUnsigned(rotateLeft(a, s), b);
}
function convertToWordArray(str) {
let lWordCount;
const lMessageLength = str.length;
const lNumberOfWordsTemp1 = lMessageLength + 8;
const lNumberOfWordsTemp2 = (lNumberOfWordsTemp1 - (lNumberOfWordsTemp1 % 64)) / 64;
const lNumberOfWords = (lNumberOfWordsTemp2 + 1) * 16;
const lWordArray = Array(lNumberOfWords - 1);
let lBytePosition = 0;
let lByteCount = 0;
while (lByteCount < lMessageLength) {
lWordCount = (lByteCount - (lByteCount % 4)) / 4;
lBytePosition = (lByteCount % 4) * 8;
lWordArray[lWordCount] = lWordArray[lWordCount] | (str.charCodeAt(lByteCount) << lBytePosition);
lByteCount++;
}
lWordCount = (lByteCount - (lByteCount % 4)) / 4;
lBytePosition = (lByteCount % 4) * 8;
lWordArray[lWordCount] = lWordArray[lWordCount] | (0x80 << lBytePosition);
lWordArray[lNumberOfWords - 2] = lMessageLength << 3;
lWordArray[lNumberOfWords - 1] = lMessageLength >>> 29;
return lWordArray;
}
function wordToHex(lValue) {
let wordToHexValue = '', wordToHexValueTemp = '', lByte, lCount;
for (lCount = 0; lCount <= 3; lCount++) {
lByte = (lValue >>> (lCount * 8)) & 255;
wordToHexValueTemp = '0' + lByte.toString(16);
wordToHexValue = wordToHexValue + wordToHexValueTemp.substr(wordToHexValueTemp.length - 2, 2);
}
return wordToHexValue;
}
function utf8Encode(str) {
str = str.replace(/\r\n/g, '\n');
let utftext = '';
for (let n = 0; n < str.length; n++) {
const c = str.charCodeAt(n);
if (c < 128) {
utftext += String.fromCharCode(c);
} else if (c > 127 && c < 2048) {
utftext += String.fromCharCode((c >> 6) | 192);
utftext += String.fromCharCode((c & 63) | 128);
} else {
utftext += String.fromCharCode((c >> 12) | 224);
utftext += String.fromCharCode(((c >> 6) & 63) | 128);
utftext += String.fromCharCode((c & 63) | 128);
}
}
return utftext;
}
const x = convertToWordArray(utf8Encode(string));
let a = 0x67452301, b = 0xEFCDAB89, c = 0x98BADCFE, d = 0x10325476;
const S11 = 7, S12 = 12, S13 = 17, S14 = 22;
const S21 = 5, S22 = 9, S23 = 14, S24 = 20;
const S31 = 4, S32 = 11, S33 = 16, S34 = 23;
const S41 = 6, S42 = 10, S43 = 15, S44 = 21;
for (let k = 0; k < x.length; k += 16) {
const AA = a, BB = b, CC = c, DD = d;
a = ff(a, b, c, d, x[k], S11, 0xD76AA478);
d = ff(d, a, b, c, x[k + 1], S12, 0xE8C7B756);
c = ff(c, d, a, b, x[k + 2], S13, 0x242070DB);
b = ff(b, c, d, a, x[k + 3], S14, 0xC1BDCEEE);
a = ff(a, b, c, d, x[k + 4], S11, 0xF57C0FAF);
d = ff(d, a, b, c, x[k + 5], S12, 0x4787C62A);
c = ff(c, d, a, b, x[k + 6], S13, 0xA8304613);
b = ff(b, c, d, a, x[k + 7], S14, 0xFD469501);
a = ff(a, b, c, d, x[k + 8], S11, 0x698098D8);
d = ff(d, a, b, c, x[k + 9], S12, 0x8B44F7AF);
c = ff(c, d, a, b, x[k + 10], S13, 0xFFFF5BB1);
b = ff(b, c, d, a, x[k + 11], S14, 0x895CD7BE);
a = ff(a, b, c, d, x[k + 12], S11, 0x6B901122);
d = ff(d, a, b, c, x[k + 13], S12, 0xFD987193);
c = ff(c, d, a, b, x[k + 14], S13, 0xA679438E);
b = ff(b, c, d, a, x[k + 15], S14, 0x49B40821);
a = gg(a, b, c, d, x[k + 1], S21, 0xF61E2562);
d = gg(d, a, b, c, x[k + 6], S22, 0xC040B340);
c = gg(c, d, a, b, x[k + 11], S23, 0x265E5A51);
b = gg(b, c, d, a, x[k], S24, 0xE9B6C7AA);
a = gg(a, b, c, d, x[k + 5], S21, 0xD62F105D);
d = gg(d, a, b, c, x[k + 10], S22, 0x2441453);
c = gg(c, d, a, b, x[k + 15], S23, 0xD8A1E681);
b = gg(b, c, d, a, x[k + 4], S24, 0xE7D3FBC8);
a = gg(a, b, c, d, x[k + 9], S21, 0x21E1CDE6);
d = gg(d, a, b, c, x[k + 14], S22, 0xC33707D6);
c = gg(c, d, a, b, x[k + 3], S23, 0xF4D50D87);
b = gg(b, c, d, a, x[k + 8], S24, 0x455A14ED);
a = gg(a, b, c, d, x[k + 13], S21, 0xA9E3E905);
d = gg(d, a, b, c, x[k + 2], S22, 0xFCEFA3F8);
c = gg(c, d, a, b, x[k + 7], S23, 0x676F02D9);
b = gg(b, c, d, a, x[k + 12], S24, 0x8D2A4C8A);
a = hh(a, b, c, d, x[k + 5], S31, 0xFFFA3942);
d = hh(d, a, b, c, x[k + 8], S32, 0x8771F681);
c = hh(c, d, a, b, x[k + 11], S33, 0x6D9D6122);
b = hh(b, c, d, a, x[k + 14], S34, 0xFDE5380C);
a = hh(a, b, c, d, x[k + 1], S31, 0xA4BEEA44);
d = hh(d, a, b, c, x[k + 4], S32, 0x4BDECFA9);
c = hh(c, d, a, b, x[k + 7], S33, 0xF6BB4B60);
b = hh(b, c, d, a, x[k + 10], S34, 0xBEBFBC70);
a = hh(a, b, c, d, x[k + 13], S31, 0x289B7EC6);
d = hh(d, a, b, c, x[k], S32, 0xEAA127FA);
c = hh(c, d, a, b, x[k + 3], S33, 0xD4EF3085);
b = hh(b, c, d, a, x[k + 6], S34, 0x4881D05);
a = hh(a, b, c, d, x[k + 9], S31, 0xD9D4D039);
d = hh(d, a, b, c, x[k + 12], S32, 0xE6DB99E5);
c = hh(c, d, a, b, x[k + 15], S33, 0x1FA27CF8);
b = hh(b, c, d, a, x[k + 2], S34, 0xC4AC5665);
a = ii(a, b, c, d, x[k], S41, 0xF4292244);
d = ii(d, a, b, c, x[k + 7], S42, 0x432AFF97);
c = ii(c, d, a, b, x[k + 14], S43, 0xAB9423A7);
b = ii(b, c, d, a, x[k + 5], S44, 0xFC93A039);
a = ii(a, b, c, d, x[k + 12], S41, 0x655B59C3);
d = ii(d, a, b, c, x[k + 3], S42, 0x8F0CCC92);
c = ii(c, d, a, b, x[k + 10], S43, 0xFFEFF47D);
b = ii(b, c, d, a, x[k + 1], S44, 0x85845DD1);
a = ii(a, b, c, d, x[k + 8], S41, 0x6FA87E4F);
d = ii(d, a, b, c, x[k + 15], S42, 0xFE2CE6E0);
c = ii(c, d, a, b, x[k + 6], S43, 0xA3014314);
b = ii(b, c, d, a, x[k + 13], S44, 0x4E0811A1);
a = ii(a, b, c, d, x[k + 4], S41, 0xF7537E82);
d = ii(d, a, b, c, x[k + 11], S42, 0xBD3AF235);
c = ii(c, d, a, b, x[k + 2], S43, 0x2AD7D2BB);
b = ii(b, c, d, a, x[k + 9], S44, 0xEB86D391);
a = addUnsigned(a, AA);
b = addUnsigned(b, BB);
c = addUnsigned(c, CC);
d = addUnsigned(d, DD);
}
return (wordToHex(a) + wordToHex(b) + wordToHex(c) + wordToHex(d)).toLowerCase();
};
/**
* 根据概念名称生成概念详情页面的 URL
* @param {string} conceptName - 概念名称
* @returns {string} 概念详情页面的完整 URL
*/
export const getConceptHtmlUrl = (conceptName) => {
if (!conceptName) return '';
const conceptHash = md5(conceptName);
return `https://valuefrontier.cn/htmls/concept/${conceptHash}/`;
};
/** /**
* 处理包含<br>标签的文本转换为适合在React中显示的格式 * 处理包含<br>标签的文本转换为适合在React中显示的格式
* @param {string} text - 包含<br>标签的文本 * @param {string} text - 包含<br>标签的文本

View File

@@ -29,6 +29,7 @@ import { AlertCircle, Clock, TrendingUp, Info, RefreshCw } from 'lucide-react';
import ReactECharts from 'echarts-for-react'; import ReactECharts from 'echarts-for-react';
import { logger } from '@utils/logger'; import { logger } from '@utils/logger';
import { getApiBase } from '@utils/apiConfig'; import { getApiBase } from '@utils/apiConfig';
import { getConceptHtmlUrl } from '@utils/textUtils';
import { useIndexQuote } from '@hooks/useIndexQuote'; import { useIndexQuote } from '@hooks/useIndexQuote';
import conceptStaticService from '@services/conceptStaticService'; import conceptStaticService from '@services/conceptStaticService';
import { GLASS_BLUR } from '@/constants/glassConfig'; import { GLASS_BLUR } from '@/constants/glassConfig';
@@ -450,7 +451,7 @@ const FlowingConcepts = () => {
}; };
const handleClick = (name) => { const handleClick = (name) => {
window.open(`https://valuefrontier.cn/htmls/${name}.html`, '_blank'); window.open(getConceptHtmlUrl(name), '_blank');
}; };
if (loading) { if (loading) {

View File

@@ -44,6 +44,7 @@ import {
ArrowLeft, ArrowLeft,
} from 'lucide-react'; } from 'lucide-react';
import { logger } from '../../../utils/logger'; import { logger } from '../../../utils/logger';
import { getConceptHtmlUrl } from '../../../utils/textUtils';
import { GLASS_BLUR } from '@/constants/glassConfig'; import { GLASS_BLUR } from '@/constants/glassConfig';
// 极光动画 - 黑金色主题 // 极光动画 - 黑金色主题
@@ -942,7 +943,7 @@ const ForceGraphView = ({
if (data.level === 'concept') { if (data.level === 'concept') {
// 跳转到概念详情页 // 跳转到概念详情页
const htmlPath = `https://valuefrontier.cn/htmls/${encodeURIComponent(params.name)}.html`; const htmlPath = getConceptHtmlUrl(params.name);
window.open(htmlPath, '_blank'); window.open(htmlPath, '_blank');
return; return;
} }

View File

@@ -62,6 +62,7 @@ import {
ExternalLink, ExternalLink,
} from 'lucide-react'; } from 'lucide-react';
import { logger } from '../../../utils/logger'; import { logger } from '../../../utils/logger';
import { getConceptHtmlUrl } from '../../../utils/textUtils';
import { GLASS_BLUR } from '@/constants/glassConfig'; import { GLASS_BLUR } from '@/constants/glassConfig';
// 一级分类图标映射 // 一级分类图标映射
@@ -685,7 +686,7 @@ const HierarchyView = ({
]); ]);
} else if (item.level === 'concept') { } else if (item.level === 'concept') {
// 跳转到概念详情页 // 跳转到概念详情页
const htmlPath = `https://valuefrontier.cn/htmls/${encodeURIComponent(item.name)}.html`; const htmlPath = getConceptHtmlUrl(item.name);
window.open(htmlPath, '_blank'); window.open(htmlPath, '_blank');
} }
}, [currentLv1, currentLv2]); }, [currentLv1, currentLv2]);

View File

@@ -1,6 +1,7 @@
import React, { useState, useEffect, useCallback } from 'react'; import React, { useState, useEffect, useCallback } from 'react';
import { useSearchParams, useNavigate } from 'react-router-dom'; import { useSearchParams, useNavigate } from 'react-router-dom';
import { logger } from '../../utils/logger'; import { logger } from '../../utils/logger';
import { getConceptHtmlUrl } from '../../utils/textUtils';
import defaultEventImage from '../../assets/img/default-event.jpg'; import defaultEventImage from '../../assets/img/default-event.jpg';
import { import {
Box, Box,
@@ -674,7 +675,7 @@ const ConceptCenter = () => {
trackConceptClicked(concept, position); trackConceptClicked(concept, position);
} }
const htmlPath = `https://valuefrontier.cn/htmls/${encodeURIComponent(conceptName)}.html`; const htmlPath = getConceptHtmlUrl(conceptName);
window.open(htmlPath, '_blank'); window.open(htmlPath, '_blank');
}; };

View File

@@ -34,6 +34,7 @@ import dayjs from 'dayjs';
import tradingDayUtils from '../../../utils/tradingDayUtils'; // 引入交易日工具 import tradingDayUtils from '../../../utils/tradingDayUtils'; // 引入交易日工具
import { logger } from '../../../utils/logger'; import { logger } from '../../../utils/logger';
import { getApiBase } from '../../../utils/apiConfig'; import { getApiBase } from '../../../utils/apiConfig';
import { getConceptHtmlUrl } from '../../../utils/textUtils';
import { PROFESSIONAL_COLORS } from '../../../constants/professionalTheme'; import { PROFESSIONAL_COLORS } from '../../../constants/professionalTheme';
// 增强版 ConceptCard 组件 - 展示更多数据细节 // 增强版 ConceptCard 组件 - 展示更多数据细节
@@ -61,7 +62,7 @@ const ConceptCard = ({ concept, tradingDate, onViewDetails }) => {
// 处理概念点击 // 处理概念点击
const handleConceptClick = () => { const handleConceptClick = () => {
window.open(`https://valuefrontier.cn/htmls/${encodeURIComponent(concept.concept)}.html`, '_blank'); window.open(getConceptHtmlUrl(concept.concept), '_blank');
}; };
return ( return (
@@ -758,7 +759,7 @@ const RelatedConcepts = ({ eventTitle, eventTime, eventId, loading: externalLoad
size="lg" size="lg"
flex={1} flex={1}
onClick={() => { onClick={() => {
window.open(`https://valuefrontier.cn/htmls/${encodeURIComponent(selectedConcept.concept)}.html`, '_blank'); window.open(getConceptHtmlUrl(selectedConcept.concept), '_blank');
}} }}
leftIcon={<Icon as={ExternalLink} boxSize={4} />} leftIcon={<Icon as={ExternalLink} boxSize={4} />}
> >

View File

@@ -55,6 +55,7 @@ import HotspotOverview from './components/HotspotOverview';
import FlexScreen from './components/FlexScreen'; import FlexScreen from './components/FlexScreen';
import { echarts } from '@lib/echarts'; import { echarts } from '@lib/echarts';
import { logger } from '../../utils/logger'; import { logger } from '../../utils/logger';
import { getConceptHtmlUrl } from '../../utils/textUtils';
import tradingDays from '../../data/tradingDays.json'; import tradingDays from '../../data/tradingDays.json';
import { useStockOverviewEvents } from './hooks/useStockOverviewEvents'; import { useStockOverviewEvents } from './hooks/useStockOverviewEvents';
import { GLASS_BLUR } from '@/constants/glassConfig'; import { GLASS_BLUR } from '@/constants/glassConfig';
@@ -534,7 +535,7 @@ const StockOverview = () => {
// 🎯 追踪概念点击 // 🎯 追踪概念点击
trackConceptClicked(concept, rank); trackConceptClicked(concept, rank);
const htmlPath = `/htmls/${concept.concept_name}.html`; const htmlPath = getConceptHtmlUrl(concept.concept_name);
window.open(htmlPath, '_blank'); window.open(htmlPath, '_blank');
}; };