Commit e122d857 authored by fisherdaddy's avatar fisherdaddy

feature: 新增导航栏

parent b60729a2
...@@ -6,10 +6,14 @@ import Footer from './components/Footer'; ...@@ -6,10 +6,14 @@ import Footer from './components/Footer';
import NotFound from './pages/NotFound'; import NotFound from './pages/NotFound';
import Login from './pages/Login'; import Login from './pages/Login';
const DevTools = lazy(() => import('./pages/DevTools'));
const ImageTools = lazy(() => import('./pages/ImageTools'));
const Blog = lazy(() => import('./pages/Blog'));
const AIProduct = lazy(() => import('./pages/AIProduct'));
const JsonFormatter = lazy(() => import('./components/JsonFormatter')); const JsonFormatter = lazy(() => import('./components/JsonFormatter'));
const TextToImage = lazy(() => import('./components/TextToImage')); const TextToImage = lazy(() => import('./components/TextToImage'));
const UrlDecode = lazy(() => import('./components/UrlDecode')); const UrlEnDecode = lazy(() => import('./components/UrlEnDecode'));
const UrlEncode = lazy(() => import('./components/UrlEncode'));
const About = lazy(() => import('./pages/About')); const About = lazy(() => import('./pages/About'));
const OpenAITimeline = lazy(() => import('./components/OpenAITimeline')); const OpenAITimeline = lazy(() => import('./components/OpenAITimeline'));
const PricingCharts = lazy(() => import('./components/PricingCharts')); const PricingCharts = lazy(() => import('./components/PricingCharts'));
...@@ -27,11 +31,16 @@ function App() { ...@@ -27,11 +31,16 @@ function App() {
<Routes> <Routes>
<Route path="/" element={<Home />} /> <Route path="/" element={<Home />} />
<Route path="/login" element={<Login />} /> <Route path="/login" element={<Login />} />
<Route path="/about" element={<About />} />
<Route path="/dev-tools" element={<DevTools />} />
<Route path="/image-tools" element={<ImageTools />} />
<Route path="/ai-products" element={<AIProduct />} />
<Route path="/blog" element={<Blog />} />
<Route path="/text2image" element={<TextToImage />} /> <Route path="/text2image" element={<TextToImage />} />
<Route path="/json-formatter" element={<JsonFormatter />} /> <Route path="/json-formatter" element={<JsonFormatter />} />
<Route path="/url-decode" element={<UrlDecode />} /> <Route path="/url-encode-and-decode" element={<UrlEnDecode />} />
<Route path="/url-encode" element={<UrlEncode />} />
<Route path="/about" element={<About />} />
<Route path="/openai-timeline" element={<OpenAITimeline />} /> <Route path="/openai-timeline" element={<OpenAITimeline />} />
<Route path="/llm-model-price" element={<PricingCharts />} /> <Route path="/llm-model-price" element={<PricingCharts />} />
<Route path="/handwriting" element={<HandwriteGen />} /> <Route path="/handwriting" element={<HandwriteGen />} />
......
// src/components/Header.jsx // src/components/Header.jsx
import React, { useState, useEffect, useRef } from 'react'; import React, { useState, useEffect, useRef } from 'react';
import { Link, useNavigate } from 'react-router-dom'; import { NavLink, useNavigate } from 'react-router-dom';
import LanguageSelector from './LanguageSelector'; import LanguageSelector from './LanguageSelector';
import { useTranslation } from '../js/i18n'; import { useTranslation } from '../js/i18n';
import '../styles/Header.css'; import '../styles/Header.css';
...@@ -22,7 +22,6 @@ function Header() { ...@@ -22,7 +22,6 @@ function Header() {
setMenuOpen((prev) => !prev); setMenuOpen((prev) => !prev);
}; };
// 点击菜单外部时关闭菜单
useEffect(() => { useEffect(() => {
const handleClickOutside = (event) => { const handleClickOutside = (event) => {
if (menuRef.current && !menuRef.current.contains(event.target)) { if (menuRef.current && !menuRef.current.contains(event.target)) {
...@@ -39,17 +38,30 @@ function Header() { ...@@ -39,17 +38,30 @@ function Header() {
<header> <header>
<nav> <nav>
<div className="logo-title-container"> <div className="logo-title-container">
<Link to="/" className="title no-underline"> <NavLink to="/" className="title no-underline">
<img src={logo} alt="Logo" className="logo" /> <img src={logo} alt="Logo" className="logo" />
{t('title')} {t('title')}
</Link> </NavLink>
</div>
<div className="menu-items">
<NavLink to="/dev-tools" className={({ isActive }) => (isActive ? 'active' : '')}>
{t('dev-tools')}
</NavLink>
<NavLink to="/image-tools" className={({ isActive }) => (isActive ? 'active' : '')}>
{t('image-tools')}
</NavLink>
<NavLink to="/blog" className={({ isActive }) => (isActive ? 'active' : '')}>
{t('blog')}
</NavLink>
<NavLink to="/ai-products" className={({ isActive }) => (isActive ? 'active' : '')}>
{t('ai-products')}
</NavLink>
</div> </div>
<div className="right-container"> <div className="right-container">
<LanguageSelector /> <LanguageSelector />
<div className="auth-container"> <div className="auth-container">
{user ? ( {user ? (
<div className="user-info"> <div className="user-info">
{/* 头像容器 */}
<div className="avatar-container" ref={menuRef}> <div className="avatar-container" ref={menuRef}>
<img <img
src={user.picture} src={user.picture}
...@@ -57,7 +69,6 @@ function Header() { ...@@ -57,7 +69,6 @@ function Header() {
className="avatar" className="avatar"
onClick={toggleMenu} onClick={toggleMenu}
/> />
{/* 下拉菜单 */}
{menuOpen && ( {menuOpen && (
<div className="dropdown-menu"> <div className="dropdown-menu">
<button onClick={handleLogout}>{t('logout')}</button> <button onClick={handleLogout}>{t('logout')}</button>
...@@ -66,7 +77,7 @@ function Header() { ...@@ -66,7 +77,7 @@ function Header() {
</div> </div>
</div> </div>
) : ( ) : (
<Link to="/login">{t('login')}</Link> <NavLink to="/login">{t('login')}</NavLink>
)} )}
</div> </div>
</div> </div>
......
import React, { useState, useCallback } from 'react';
import { Title, Wrapper, Container, InputText, Preview } from '../js/SharedStyles';
import styled from 'styled-components';
import { useTranslation } from '../js/i18n';
import SEO from '../components/SEO';
const DecoderContainer = styled(Container)`
flex-direction: column;
`;
const StyledInputText = styled(InputText)`
height: 100px;
margin-bottom: 20px;
@media (min-width: 768px) {
height: 100px;
width: 100%;
}
`;
const PreviewWrapper = styled.div`
width: 100%;
`;
const Label = styled.label`
font-weight: 500;
font-size: 14px;
color: #5f6368;
margin-bottom: 8px;
display: block;
letter-spacing: 0.1px;
`;
const StyledPreview = styled(Preview)`
background-color: #f8f9fa;
padding: 12px 40px 12px 12px; // 增加右侧 padding 为按钮留出空间
border-radius: 8px;
border: 1px solid #dadce0;
font-size: 14px;
color: #202124;
min-height: 24px; // 确保即使内容为空,也有足够的高度容纳按钮
`;
const ResultContainer = styled.div`
position: relative;
width: 100%;
`;
const CopyButton = styled.button`
position: absolute;
top: 8px;
right: 8px;
background-color: transparent;
border: none;
cursor: pointer;
padding: 4px;
display: flex;
align-items: center;
justify-content: center;
opacity: 0.6;
transition: opacity 0.3s, color 0.3s;
&:hover {
opacity: 1;
}
svg {
width: 16px;
height: 16px;
}
&.copied {
color: #34a853; // Google green color for success feedback
}
`;
function UrlDecoder() {
const { t } = useTranslation();
const [input, setInput] = useState('');
const [decodedText, setDecodedText] = useState('');
const [isCopied, setIsCopied] = useState(false);
const handleInputChange = (e) => {
const inputValue = e.target.value;
setInput(inputValue);
try {
const decoded = decodeURIComponent(inputValue);
setDecodedText(decoded);
} catch (error) {
setDecodedText('Invalid URL encoding');
}
};
const handleCopy = useCallback(() => {
navigator.clipboard.writeText(decodedText).then(() => {
setIsCopied(true);
setTimeout(() => setIsCopied(false), 2000);
});
}, [decodedText]);
return (
<>
<SEO
title={t('tools.urlDecode.title')}
description={t('tools.urlDecode.description')}
/>
<Wrapper>
<Title>{t('tools.urlDecode.title')}</Title>
<DecoderContainer>
<StyledInputText
id="urlInput"
placeholder={t('tools.urlDecode.inputLabel')}
value={input}
onChange={handleInputChange}
/>
<PreviewWrapper>
<Label>{t('tools.urlDecode.resultLabel')}</Label>
<ResultContainer>
<StyledPreview>{decodedText}</StyledPreview>
<CopyButton onClick={handleCopy} className={isCopied ? 'copied' : ''}>
{isCopied ? (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
<path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/>
</svg>
) : (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
<path d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z"/>
</svg>
)}
</CopyButton>
</ResultContainer>
</PreviewWrapper>
</DecoderContainer>
</Wrapper>
</>
);
}
export default UrlDecoder;
\ No newline at end of file
...@@ -4,7 +4,7 @@ import styled from 'styled-components'; ...@@ -4,7 +4,7 @@ import styled from 'styled-components';
import { useTranslation } from '../js/i18n'; import { useTranslation } from '../js/i18n';
import SEO from './SEO'; import SEO from './SEO';
const EncoderContainer = styled(Container)` const EncoderDecoderContainer = styled(Container)`
flex-direction: column; flex-direction: column;
`; `;
...@@ -69,53 +69,85 @@ const CopyButton = styled.button` ...@@ -69,53 +69,85 @@ const CopyButton = styled.button`
} }
&.copied { &.copied {
color: #34a853; // 成功复制后的绿色反馈 color: #34a853; // 成功复制后的反馈颜色
} }
`; `;
function UrlEncoder() { const ModeSwitcher = styled.div`
margin-bottom: 20px;
select {
padding: 8px;
border-radius: 4px;
border: 1px solid #dadce0;
font-size: 14px;
color: #202124;
}
`;
function UrlEncoderDecoder() {
const { t } = useTranslation(); const { t } = useTranslation();
const [input, setInput] = useState(''); const [input, setInput] = useState('');
const [encodedText, setEncodedText] = useState(''); const [resultText, setResultText] = useState('');
const [isCopied, setIsCopied] = useState(false); const [isCopied, setIsCopied] = useState(false);
const [mode, setMode] = useState('decode'); // 'encode' 或 'decode'
const handleModeChange = (e) => {
setMode(e.target.value);
// 当模式切换时,清空输入和输出
setInput('');
setResultText('');
};
const handleInputChange = (e) => { const handleInputChange = (e) => {
const inputValue = e.target.value; const inputValue = e.target.value;
setInput(inputValue); setInput(inputValue);
try { try {
const encoded = encodeURIComponent(inputValue); let result;
setEncodedText(encoded); if (mode === 'decode') {
result = decodeURIComponent(inputValue);
} else {
result = encodeURIComponent(inputValue);
}
setResultText(result);
} catch (error) { } catch (error) {
setEncodedText('编码出错'); setResultText('Invalid input');
} }
}; };
const handleCopy = useCallback(() => { const handleCopy = useCallback(() => {
navigator.clipboard.writeText(encodedText).then(() => { navigator.clipboard.writeText(resultText).then(() => {
setIsCopied(true); setIsCopied(true);
setTimeout(() => setIsCopied(false), 2000); setTimeout(() => setIsCopied(false), 2000);
}); });
}, [encodedText]); }, [resultText]);
return ( return (
<> <>
<SEO <SEO
title={t('tools.urlEncode.title')} title={t('tools.urlEncodeDecode.title')}
description={t('tools.urlEncode.description')} description={t('tools.urlEncodeDecode.description')}
/> />
<Wrapper> <Wrapper>
<Title>{t('tools.urlEncode.title')}</Title> <Title>{t('tools.urlEncodeDecode.title')}</Title>
<EncoderContainer> <EncoderDecoderContainer>
<ModeSwitcher>
<Label>{t('tools.urlEncodeDecode.modeLabel')}</Label>
<select value={mode} onChange={handleModeChange}>
<option value="encode">{t('tools.urlEncodeDecode.encode')}</option>
<option value="decode">{t('tools.urlEncodeDecode.decode')}</option>
</select>
</ModeSwitcher>
<StyledInputText <StyledInputText
id="urlInput" id="urlInput"
placeholder={t('tools.urlEncode.inputLabel')} placeholder={mode === 'decode' ? t('tools.urlDecode.inputLabel') : t('tools.urlEncode.inputLabel')}
value={input} value={input}
onChange={handleInputChange} onChange={handleInputChange}
/> />
<PreviewWrapper> <PreviewWrapper>
<Label>{t('tools.urlEncode.resultLabel')}</Label> <Label>{mode === 'decode' ? t('tools.urlDecode.resultLabel') : t('tools.urlEncode.resultLabel')}</Label>
<ResultContainer> <ResultContainer>
<StyledPreview>{encodedText}</StyledPreview> <StyledPreview>{resultText}</StyledPreview>
<CopyButton onClick={handleCopy} className={isCopied ? 'copied' : ''}> <CopyButton onClick={handleCopy} className={isCopied ? 'copied' : ''}>
{isCopied ? ( {isCopied ? (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
...@@ -129,10 +161,10 @@ function UrlEncoder() { ...@@ -129,10 +161,10 @@ function UrlEncoder() {
</CopyButton> </CopyButton>
</ResultContainer> </ResultContainer>
</PreviewWrapper> </PreviewWrapper>
</EncoderContainer> </EncoderDecoderContainer>
</Wrapper> </Wrapper>
</> </>
); );
} }
export default UrlEncoder; export default UrlEncoderDecoder;
...@@ -7,6 +7,10 @@ ...@@ -7,6 +7,10 @@
"welcome": "Welcome", "welcome": "Welcome",
"login": "Login", "login": "Login",
"logout": "Logout", "logout": "Logout",
"dev-tools": "Development Tools",
"image-tools": "Image Tools",
"blog": "Blog",
"ai-products": "AI Products",
"tools": { "tools": {
"text2image": { "text2image": {
"title": "Text to Image Card", "title": "Text to Image Card",
...@@ -22,21 +26,20 @@ ...@@ -22,21 +26,20 @@
"copyButton": "Copy", "copyButton": "Copy",
"copiedMessage": "Copied" "copiedMessage": "Copied"
}, },
"urlDecode": { "urlEncodeDecode": {
"title": "URL Decoder", "title": "URL Encode/Decode Tool",
"description": "Decode URL-encoded strings", "description": "Encode or decode a URL online.",
"inputLabel": "Enter URL to decode", "modeLabel": "Select Mode",
"resultLabel": "Decoded result", "encode": "Encode",
"copyButton": "Copy", "decode": "Decode"
"copiedMessage": "Copied"
}, },
"urlEncode": { "urlEncode": {
"title": "URL Encoder", "inputLabel": "Enter text to encode",
"description": "Encode URL strings", "resultLabel": "Encoded Result"
"inputLabel": "Enter URL to encode", },
"resultLabel": "Encoded result", "urlDecode": {
"copyButton": "Copy", "inputLabel": "Enter URL to decode",
"copiedMessage": "Copied" "resultLabel": "Decoded Result"
}, },
"openAITimeline": { "openAITimeline": {
"title": "OpenAI Product Release", "title": "OpenAI Product Release",
...@@ -62,7 +65,7 @@ ...@@ -62,7 +65,7 @@
"download": "Download Image" "download": "Download Image"
}, },
"fisherai": { "fisherai": {
"title": "One-Click Summary Plugin", "title": "FisherAI",
"description": "The Best Summary Extension for Chrome Browser" "description": "The Best Summary Extension for Chrome Browser"
}, },
"quotecard": { "quotecard": {
...@@ -99,6 +102,10 @@ ...@@ -99,6 +102,10 @@
"welcome": "欢迎", "welcome": "欢迎",
"login": "登录", "login": "登录",
"logout": "退出登录", "logout": "退出登录",
"dev-tools": "开发工具",
"image-tools": "图片工具",
"blog": "博客",
"ai-products": "AI 产品",
"tools": { "tools": {
"text2image": { "text2image": {
"title": "文字卡片", "title": "文字卡片",
...@@ -114,21 +121,20 @@ ...@@ -114,21 +121,20 @@
"copyButton": "复制", "copyButton": "复制",
"copiedMessage": "已复制" "copiedMessage": "已复制"
}, },
"urlDecode": { "urlEncodeDecode": {
"title": "URL 解码器", "title": "URL 编码/解码工具",
"description": "解码 URL 编码的字符串", "description": "在线编码或解码 URL。",
"inputLabel": "输入需要解码的 URL", "modeLabel": "选择模式",
"resultLabel": "解码结果", "encode": "编码",
"copyButton": "复制", "decode": "解码"
"copiedMessage": "已复制"
}, },
"urlEncode": { "urlEncode": {
"title": "URL 编码器", "inputLabel": "输入要编码的文本",
"description": "编码 URL 字符串", "resultLabel": "编码结果"
"inputLabel": "输入需要编码的 URL", },
"resultLabel": "编码结果", "urlDecode": {
"copyButton": "复制", "inputLabel": "输入要解码的 URL",
"copiedMessage": "已复制" "resultLabel": "解码结果"
}, },
"openAITimeline": { "openAITimeline": {
"title": "OpenAI 产品发布", "title": "OpenAI 产品发布",
...@@ -143,7 +149,7 @@ ...@@ -143,7 +149,7 @@
"description": "生成和纸上书写相媲美的效果" "description": "生成和纸上书写相媲美的效果"
}, },
"imageBase64Converter": { "imageBase64Converter": {
"title": "图片与 Base64 转换器", "title": "Base64与图片转换",
"description": "图片与 Base64 的互相转换", "description": "图片与 Base64 的互相转换",
"imageToBase64": "图片转 Base64", "imageToBase64": "图片转 Base64",
"base64Result": "Base64 结果", "base64Result": "Base64 结果",
...@@ -154,7 +160,7 @@ ...@@ -154,7 +160,7 @@
"download": "下载图片" "download": "下载图片"
}, },
"fisherai": { "fisherai": {
"title": "一键摘要插件", "title": "FisherAI",
"description": "最好用的 Chrome 浏览器摘要插件" "description": "最好用的 Chrome 浏览器摘要插件"
}, },
"quotecard": { "quotecard": {
...@@ -191,6 +197,10 @@ ...@@ -191,6 +197,10 @@
"welcome": "ようこそ", "welcome": "ようこそ",
"login": "ログイン", "login": "ログイン",
"logout": "ログアウト", "logout": "ログアウト",
"dev-tools": "開発ツール",
"image-tools": "画像ツール",
"blog": "ブログ",
"ai-products": "AI製品",
"tools": { "tools": {
"text2image": { "text2image": {
"title": "テキストから画像", "title": "テキストから画像",
...@@ -206,21 +216,20 @@ ...@@ -206,21 +216,20 @@
"copyButton": "コピー", "copyButton": "コピー",
"copiedMessage": "コピーしました" "copiedMessage": "コピーしました"
}, },
"urlDecode": { "urlEncodeDecode": {
"title": "URLデコーダー", "title": "URL エンコード/デコード ツール",
"description": "URLエンコードされた文字列をデコード", "description": "URL をオンラインでエンコードまたはデコードします。",
"inputLabel": "デコードするURLを入力", "modeLabel": "モードを選択",
"resultLabel": "デコード結果", "encode": "エンコード",
"copyButton": "コピー", "decode": "デコード"
"copiedMessage": "コピーしました"
}, },
"urlEncode": { "urlEncode": {
"title": "URLエンコーダー", "inputLabel": "エンコードするテキストを入力",
"description": "URL文字列をエンコード", "resultLabel": "エンコード結果"
"inputLabel": "エンコードするURLを入力", },
"resultLabel": "エンコード結果", "urlDecode": {
"copyButton": "コピー", "inputLabel": "デコードする URL を入力",
"copiedMessage": "コピーしました" "resultLabel": "デコード結果"
}, },
"openAITimeline": { "openAITimeline": {
"title": "OpenAI 製品リリース", "title": "OpenAI 製品リリース",
...@@ -246,7 +255,7 @@ ...@@ -246,7 +255,7 @@
"download": "画像をダウンロード" "download": "画像をダウンロード"
}, },
"fisherai": { "fisherai": {
"title": "ワンクリック要約プラグイン", "title": "FisherAI",
"description": "最高のChromeブラウザ用要約プラグイン" "description": "最高のChromeブラウザ用要約プラグイン"
}, },
"quotecard": { "quotecard": {
...@@ -283,6 +292,10 @@ ...@@ -283,6 +292,10 @@
"welcome": "환영합니다", "welcome": "환영합니다",
"login": "로그인", "login": "로그인",
"logout": "로그아웃", "logout": "로그아웃",
"dev-tools": "개발 도구",
"image-tools": "이미지 도구",
"blog": "블로그",
"ai-products": "AI 제품",
"tools": { "tools": {
"text2image": { "text2image": {
"title": "텍스트를 이미지로", "title": "텍스트를 이미지로",
...@@ -298,21 +311,20 @@ ...@@ -298,21 +311,20 @@
"copyButton": "복사", "copyButton": "복사",
"copiedMessage": "복사됨" "copiedMessage": "복사됨"
}, },
"urlDecode": { "urlEncodeDecode": {
"title": "URL 디코더", "title": "URL 인코딩/디코딩 도구",
"description": "URL 인코딩된 문자열 디코딩", "description": "URL을 온라인으로 인코딩하거나 디코딩합니다.",
"inputLabel": "디코딩할 URL 입력", "modeLabel": "모드 선택",
"resultLabel": "디코딩 결과", "encode": "인코딩",
"copyButton": "복사", "decode": "디코딩"
"copiedMessage": "복사됨"
}, },
"urlEncode": { "urlEncode": {
"title": "URL 인코더", "inputLabel": "인코딩할 텍스트 입력",
"description": "URL 문자열 인코딩", "resultLabel": "인코딩 결과"
"inputLabel": "인코딩할 URL을 입력", },
"resultLabel": "인코딩 결과", "urlDecode": {
"copyButton": "복사", "inputLabel": "디코딩할 URL 입력",
"copiedMessage": "복사됨" "resultLabel": "디코딩 결과"
}, },
"openAITimeline": { "openAITimeline": {
"title": "OpenAI 제품 출시", "title": "OpenAI 제품 출시",
...@@ -338,7 +350,7 @@ ...@@ -338,7 +350,7 @@
"download": "이미지 다운로드" "download": "이미지 다운로드"
}, },
"fisherai": { "fisherai": {
"title": "원클릭 요약 플러그인", "title": "FisherAI",
"description": "가장 유용한 Chrome 브라우저 요약 확장 프로그램" "description": "가장 유용한 Chrome 브라우저 요약 확장 프로그램"
}, },
"quotecard": { "quotecard": {
......
...@@ -4,133 +4,133 @@ ...@@ -4,133 +4,133 @@
"providers": [ "providers": [
{ {
"name": "Doubao-lite-32k", "name": "Doubao-lite-32k",
"logo": "/assets/doubao.png", "logo": "/assets/icon/doubao.png",
"inputPrice": 0.3, "inputPrice": 0.3,
"outputPrice": 0.6 "outputPrice": 0.6
}, },
{ {
"name": "yi-lightning\n-16k", "name": "yi-lightning\n-16k",
"logo": "/assets/yi_small.jpg", "logo": "/assets/icon/yi_small.jpg",
"inputPrice": 0.99, "inputPrice": 0.99,
"outputPrice": 0.99 "outputPrice": 0.99
}, },
{ {
"name": "GLM-4-Air\n-128k", "name": "GLM-4-Air\n-128k",
"logo": "/assets/glm_small.svg", "logo": "/assets/icon/glm_small.svg",
"inputPrice": 1, "inputPrice": 1,
"outputPrice": 1 "outputPrice": 1
}, },
{ {
"name": "gemini 1.5 Flash", "name": "gemini 1.5 Flash",
"logo": "/assets/google_small.svg", "logo": "/assets/icon/google_small.svg",
"inputPrice": 0.53, "inputPrice": 0.53,
"outputPrice": 2.1 "outputPrice": 2.1
}, },
{ {
"name": "Doubao-pro-32k", "name": "Doubao-pro-32k",
"logo": "/assets/doubao.png", "logo": "/assets/icon/doubao.png",
"inputPrice": 0.8, "inputPrice": 0.8,
"outputPrice": 2 "outputPrice": 2
}, },
{ {
"name": "qwen-plus\n-128k", "name": "qwen-plus\n-128k",
"logo": "/assets/ali_small.svg", "logo": "/assets/icon/ali_small.svg",
"inputPrice": 0.8, "inputPrice": 0.8,
"outputPrice": 2 "outputPrice": 2
}, },
{ {
"name": "ERNIE 3.5", "name": "ERNIE 3.5",
"logo": "/assets/wenxin_small.png", "logo": "/assets/icon/wenxin_small.png",
"inputPrice": 0.8, "inputPrice": 0.8,
"outputPrice": 2 "outputPrice": 2
}, },
{ {
"name": "deepseek-\nchat-128k", "name": "deepseek-\nchat-128k",
"logo": "/assets/deepseek_small.jpg", "logo": "/assets/icon/deepseek_small.jpg",
"inputPrice": 1, "inputPrice": 1,
"outputPrice": 2 "outputPrice": 2
}, },
{ {
"name": "gpt-4o-mini", "name": "gpt-4o-mini",
"logo": "/assets/openai_small.svg", "logo": "/assets/icon/openai_small.svg",
"inputPrice": 1.05, "inputPrice": 1.05,
"outputPrice": 4.2 "outputPrice": 4.2
}, },
{ {
"name": "qwen-math-plus", "name": "qwen-math-plus",
"logo": "/assets/ali_small.svg", "logo": "/assets/icon/ali_small.svg",
"inputPrice": 4, "inputPrice": 4,
"outputPrice": 12 "outputPrice": 12
}, },
{ {
"name": "claude 3.5\nHaiku", "name": "claude 3.5\nHaiku",
"logo": "/assets/anthropic_small.svg", "logo": "/assets/icon/anthropic_small.svg",
"inputPrice": 8.75, "inputPrice": 8.75,
"outputPrice": 35 "outputPrice": 35
}, },
{ {
"name": "yi-large-32k", "name": "yi-large-32k",
"logo": "/assets/yi_small.jpg", "logo": "/assets/icon/yi_small.jpg",
"inputPrice": 20, "inputPrice": 20,
"outputPrice": 20 "outputPrice": 20
}, },
{ {
"name": "mooonshot-v1-32k", "name": "mooonshot-v1-32k",
"logo": "/assets/moonshot_small.svg", "logo": "/assets/icon/moonshot_small.svg",
"inputPrice": 24, "inputPrice": 24,
"outputPrice": 24 "outputPrice": 24
}, },
{ {
"name": "qwen-max-32k", "name": "qwen-max-32k",
"logo": "/assets/ali_small.svg", "logo": "/assets/icon/ali_small.svg",
"inputPrice": 20, "inputPrice": 20,
"outputPrice": 60 "outputPrice": 60
}, },
{ {
"name": "ERNIE 4.0\nTurbo", "name": "ERNIE 4.0\nTurbo",
"logo": "/assets/wenxin_small.png", "logo": "/assets/icon/wenxin_small.png",
"inputPrice": 20, "inputPrice": 20,
"outputPrice": 60 "outputPrice": 60
}, },
{ {
"name": "gpt-4o", "name": "gpt-4o",
"logo": "/assets/openai_small.svg", "logo": "/assets/icon/openai_small.svg",
"inputPrice": 17.5, "inputPrice": 17.5,
"outputPrice": 70 "outputPrice": 70
}, },
{ {
"name": "GLM-4-Plus-128k", "name": "GLM-4-Plus-128k",
"logo": "/assets/glm_small.svg", "logo": "/assets/icon/glm_small.svg",
"inputPrice": 50, "inputPrice": 50,
"outputPrice": 50 "outputPrice": 50
}, },
{ {
"name": "o1-mini", "name": "o1-mini",
"logo": "/assets/openai_small.svg", "logo": "/assets/icon/openai_small.svg",
"inputPrice": 21, "inputPrice": 21,
"outputPrice": 84 "outputPrice": 84
}, },
{ {
"name": "gemini 1.5 Pro", "name": "gemini 1.5 Pro",
"logo": "/assets/google_small.svg", "logo": "/assets/icon/google_small.svg",
"inputPrice": 87.5, "inputPrice": 87.5,
"outputPrice": 35 "outputPrice": 35
}, },
{ {
"name": "claude 3.5\nSonnet", "name": "claude 3.5\nSonnet",
"logo": "/assets/anthropic_small.svg", "logo": "/assets/icon/anthropic_small.svg",
"inputPrice": 26.3, "inputPrice": 26.3,
"outputPrice": 105 "outputPrice": 105
}, },
{ {
"name": "o1-preview", "name": "o1-preview",
"logo": "/assets/openai_small.svg", "logo": "/assets/icon/openai_small.svg",
"inputPrice": 105, "inputPrice": 105,
"outputPrice": 420 "outputPrice": 420
}, },
{ {
"name": "claude 3.5\nOpus", "name": "claude 3.5\nOpus",
"logo": "/assets/anthropic_small.svg", "logo": "/assets/icon/anthropic_small.svg",
"inputPrice": 131, "inputPrice": 131,
"outputPrice": 525 "outputPrice": 525
} }
......
...@@ -4,49 +4,49 @@ ...@@ -4,49 +4,49 @@
"providers": [ "providers": [
{ {
"name": "gpt-4o-mini", "name": "gpt-4o-mini",
"logo": "/assets/openai_small.svg", "logo": "/assets/icon/openai_small.svg",
"inputPrice": 0.15, "inputPrice": 0.15,
"outputPrice": 0.6 "outputPrice": 0.6
}, },
{ {
"name": "gpt-4o", "name": "gpt-4o",
"logo": "/assets/openai_small.svg", "logo": "/assets/icon/openai_small.svg",
"inputPrice": 2.5, "inputPrice": 2.5,
"outputPrice": 10 "outputPrice": 10
}, },
{ {
"name": "gpt-4o-latest", "name": "gpt-4o-latest",
"logo": "/assets/openai_small.svg", "logo": "/assets/icon/openai_small.svg",
"inputPrice": 5, "inputPrice": 5,
"outputPrice": 15 "outputPrice": 15
}, },
{ {
"name": "gpt-4-turbo", "name": "gpt-4-turbo",
"logo": "/assets/openai_small.svg", "logo": "/assets/icon/openai_small.svg",
"inputPrice": 10, "inputPrice": 10,
"outputPrice": 30 "outputPrice": 30
}, },
{ {
"name": "o1-mini", "name": "o1-mini",
"logo": "/assets/openai_small.svg", "logo": "/assets/icon/openai_small.svg",
"inputPrice": 3, "inputPrice": 3,
"outputPrice": 12 "outputPrice": 12
}, },
{ {
"name": "o1-preview", "name": "o1-preview",
"logo": "/assets/openai_small.svg", "logo": "/assets/icon/openai_small.svg",
"inputPrice": 15, "inputPrice": 15,
"outputPrice": 60 "outputPrice": 60
}, },
{ {
"name": "gpt-4o-audio-preview", "name": "gpt-4o-audio-preview",
"logo": "/assets/openai_small.svg", "logo": "/assets/icon/openai_small.svg",
"inputPrice": 2.5, "inputPrice": 2.5,
"outputPrice": 10 "outputPrice": 10
}, },
{ {
"name": "gpt-4o-realtime-preview", "name": "gpt-4o-realtime-preview",
"logo": "/assets/openai_small.svg", "logo": "/assets/icon/openai_small.svg",
"inputPrice": 5, "inputPrice": 5,
"outputPrice": 20 "outputPrice": 20
} }
......
...@@ -4,37 +4,37 @@ ...@@ -4,37 +4,37 @@
"providers": [ "providers": [
{ {
"name": "gpt-4o-mini", "name": "gpt-4o-mini",
"logo": "/assets/openai_small.svg", "logo": "/assets/icon/openai_small.svg",
"inputPrice": 1.05, "inputPrice": 1.05,
"outputPrice": 4.2 "outputPrice": 4.2
}, },
{ {
"name": "yi-vision-16k", "name": "yi-vision-16k",
"logo": "/assets/yi_small.jpg", "logo": "/assets/icon/yi_small.jpg",
"inputPrice": 6, "inputPrice": 6,
"outputPrice": 6 "outputPrice": 6
}, },
{ {
"name": "GLM-4V-Plus", "name": "GLM-4V-Plus",
"logo": "/assets/glm_small.svg", "logo": "/assets/icon/glm_small.svg",
"inputPrice": 10, "inputPrice": 10,
"outputPrice": 10 "outputPrice": 10
}, },
{ {
"name": "qwen-vl-max", "name": "qwen-vl-max",
"logo": "/assets/ali_small.svg", "logo": "/assets/icon/ali_small.svg",
"inputPrice": 20, "inputPrice": 20,
"outputPrice": 20 "outputPrice": 20
}, },
{ {
"name": "gpt-4o", "name": "gpt-4o",
"logo": "/assets/openai_small.svg", "logo": "/assets/icon/openai_small.svg",
"inputPrice": 17.5, "inputPrice": 17.5,
"outputPrice": 70 "outputPrice": 70
}, },
{ {
"name": "GLM-4V", "name": "GLM-4V",
"logo": "/assets/glm_small.svg", "logo": "/assets/icon/glm_small.svg",
"inputPrice": 50, "inputPrice": 50,
"outputPrice": 50 "outputPrice": 50
} }
......
import React from 'react';
import { Link } from 'react-router-dom';
import { useTranslation } from '../js/i18n';
import SEO from '../components/SEO';
const tools = [
{
id: 'fisherai',
icon: '/assets/icon/fisherai.png',
path: 'https://chromewebstore.google.com/detail/fisherai-your-best-summar/ipfiijaobcenaibdpaacbbpbjefgekbj',
external: true
},
// 其他工具...
];
const AIProduct = () => {
const { t } = useTranslation();
return (
<>
<SEO
title={t('title')}
description={t('slogan')}
/>
<main>
<section className="tools-section">
<div className="tools-grid">
{tools.map(tool => (
tool.external ? (
<a
href={tool.path}
key={tool.id}
className="tool-card"
target="_blank"
rel="noopener noreferrer"
>
<img
src={tool.icon}
alt={`${t(`tools.${tool.id}.title`)} icon`}
className="tool-icon"
loading="lazy"
/>
<div className="tool-content">
<h3 className="tool-title">{t(`tools.${tool.id}.title`)}</h3>
<p className="tool-description">{t(`tools.${tool.id}.description`)}</p>
</div>
</a>
) : (
<Link
to={tool.path}
key={tool.id}
className="tool-card"
>
<img
src={tool.icon}
alt={`${t(`tools.${tool.id}.title`)} icon`}
className="tool-icon"
loading="lazy"
/>
<div className="tool-content">
<h3 className="tool-title">{t(`tools.${tool.id}.title`)}</h3>
<p className="tool-description">{t(`tools.${tool.id}.description`)}</p>
</div>
</Link>
)
))}
</div>
</section>
</main>
</>
);
};
export default AIProduct;
import React from 'react';
import { Link } from 'react-router-dom';
import { useTranslation } from '../js/i18n';
import SEO from '../components/SEO';
const tools = [
{ id: 'openAITimeline', icon: '/assets/icon/openai_small.svg', path: '/openai-timeline' },
{ id: 'modelPrice', icon: '/assets/icon/openai_small.svg', path: '/llm-model-price' },
];
const Home = () => {
const { t } = useTranslation();
return (
<>
<SEO
title={t('title')}
description={t('slogan')}
/>
<main>
<section className="tools-section">
<div className="tools-grid">
{tools.map(tool => (
<Link to={tool.path} key={tool.id} className="tool-card">
<img
src={tool.icon}
alt={`${t(`tools.${tool.id}.title`)} icon`}
className="tool-icon"
loading="lazy"
/>
<div className="tool-content">
<h3 className="tool-title">{t(`tools.${tool.id}.title`)}</h3>
<p className="tool-description">{t(`tools.${tool.id}.description`)}</p>
</div>
</Link>
))}
</div>
</section>
</main>
</>
);
};
export default Home;
import React from 'react';
import { Link } from 'react-router-dom';
import { useTranslation } from '../js/i18n';
import SEO from '../components/SEO';
const tools = [
{ id: 'jsonFormatter', icon: '/assets/icon/json-format.png', path: '/json-formatter' },
{ id: 'urlEncodeDecode', icon: '/assets/icon/url-endecode.png', path: '/url-encode-and-decode' },
{ id: 'imageBase64Converter', icon: '/assets/icon/image-base64.png', path: '/image-base64' },
];
const DevTools = () => {
const { t } = useTranslation();
return (
<>
<SEO
title={t('title')}
description={t('slogan')}
/>
<main>
<section className="tools-section">
<div className="tools-grid">
{tools.map(tool => (
<Link to={tool.path} key={tool.id} className="tool-card">
<img
src={tool.icon}
alt={`${t(`tools.${tool.id}.title`)} icon`}
className="tool-icon"
loading="lazy"
/>
<div className="tool-content">
<h3 className="tool-title">{t(`tools.${tool.id}.title`)}</h3>
<p className="tool-description">{t(`tools.${tool.id}.description`)}</p>
</div>
</Link>
))}
</div>
</section>
</main>
</>
);
};
export default DevTools;
...@@ -4,16 +4,15 @@ import { useTranslation } from '../js/i18n'; ...@@ -4,16 +4,15 @@ import { useTranslation } from '../js/i18n';
import SEO from '../components/SEO'; import SEO from '../components/SEO';
const tools = [ const tools = [
{ id: 'handwrite', icon: 'fa-handwrite', path: '/handwriting' }, { id: 'handwrite', icon: '/assets/icon/handwrite.png', path: '/handwriting' },
{ id: 'quotecard', icon: 'fa-quotecard', path: '/quote-card' }, { id: 'quotecard', icon: '/assets/icon/quotecard.png', path: '/quote-card' },
{ id: 'text2image', icon: 'fa-image', path: '/text2image' }, { id: 'text2image', icon: '/assets/icon/text2image.png', path: '/text2image' },
{ id: 'jsonFormatter', icon: 'fa-jsonformat', path: '/json-formatter' }, { id: 'jsonFormatter', icon: '/assets/icon/json-format.png', path: '/json-formatter' },
{ id: 'urlDecode', icon: 'fa-decode', path: '/url-decode' }, { id: 'urlEncodeDecode', icon: '/assets/icon/url-endecode.png', path: '/url-encode-and-decode' },
{ id: 'urlEncode', icon: 'fa-encode', path: '/url-encode' }, { id: 'imageBase64Converter', icon: '/assets/icon/image-base64.png', path: '/image-base64' },
{ id: 'imageBase64Converter', icon: 'fa-image-base64', path: '/image-base64' }, { id: 'openAITimeline', icon: '/assets/icon/openai_small.svg', path: '/openai-timeline' },
{ id: 'openAITimeline', icon: 'fa-openai-timeline', path: '/openai-timeline' }, { id: 'modelPrice', icon: '/assets/icon/openai_small.svg', path: '/llm-model-price' },
{ id: 'modelPrice', icon: 'fa-model-price', path: '/llm-model-price' }, { id: 'fisherai', icon: '/assets/icon/fisherai.png', path: 'https://chromewebstore.google.com/detail/fisherai-your-best-summar/ipfiijaobcenaibdpaacbbpbjefgekbj', external: true } // 新增外部链接
{ id: 'fisherai', icon: 'fa-fisherai', path: 'https://chromewebstore.google.com/detail/fisherai-your-best-summar/ipfiijaobcenaibdpaacbbpbjefgekbj', external: true } // 新增外部链接
]; ];
const Home = () => { const Home = () => {
...@@ -26,17 +25,20 @@ const Home = () => { ...@@ -26,17 +25,20 @@ const Home = () => {
description={t('slogan')} description={t('slogan')}
/> />
<main> <main>
<section className="hero">
<h1>{t('title')}</h1>
<p className="slogan">{t('slogan')}</p>
</section>
<section className="tools-section"> <section className="tools-section">
<div className="tools-grid"> <div className="tools-grid">
{tools.map(tool => ( {tools.map(tool => (
<Link to={tool.path} key={tool.id} className="tool-card"> <Link to={tool.path} key={tool.id} className="tool-card">
<i className={`fas ${tool.icon} tool-icon`}></i> <img
<h3 className="tool-title">{t(`tools.${tool.id}.title`)}</h3> src={tool.icon}
<p className="tool-description">{t(`tools.${tool.id}.description`)}</p> alt={`${t(`tools.${tool.id}.title`)} icon`}
className="tool-icon"
loading="lazy"
/>
<div className="tool-content">
<h3 className="tool-title">{t(`tools.${tool.id}.title`)}</h3>
<p className="tool-description">{t(`tools.${tool.id}.description`)}</p>
</div>
</Link> </Link>
))} ))}
</div> </div>
......
import React from 'react';
import { Link } from 'react-router-dom';
import { useTranslation } from '../js/i18n';
import SEO from '../components/SEO';
const tools = [
{ id: 'handwrite', icon: '/assets/icon/handwrite.png', path: '/handwriting' },
{ id: 'quotecard', icon: '/assets/icon/quotecard.png', path: '/quote-card' },
{ id: 'text2image', icon: '/assets/icon/text2image.png', path: '/text2image' },
];
const ImageTools = () => {
const { t } = useTranslation();
return (
<>
<SEO
title={t('title')}
description={t('slogan')}
/>
<main>
<section className="tools-section">
<div className="tools-grid">
{tools.map(tool => (
<Link to={tool.path} key={tool.id} className="tool-card">
<img
src={tool.icon}
alt={`${t(`tools.${tool.id}.title`)} icon`}
className="tool-icon"
loading="lazy"
/>
<div className="tool-content">
<h3 className="tool-title">{t(`tools.${tool.id}.title`)}</h3>
<p className="tool-description">{t(`tools.${tool.id}.description`)}</p>
</div>
</Link>
))}
</div>
</section>
</main>
</>
);
};
export default ImageTools;
...@@ -53,14 +53,39 @@ ...@@ -53,14 +53,39 @@
header nav { header nav {
display: flex; display: flex;
justify-content: space-between;
align-items: center; align-items: center;
padding: 10px 20px; padding: 10px 20px;
} }
.menu-items {
display: flex;
align-items: center;
margin-left: 40px; /* 调整导航菜单与标题之间的距离 */
}
.menu-items a {
color: hsl(205deg, 25%, 23%);
text-decoration: none;
padding: 0 15px;
font-size: 16px; /* 增大字体大小 */
font-weight: 500; /* 调整字体粗细 */
transition: color 0.2s ease;
}
.menu-items a:hover {
color: #4F46E5; /* 悬停时的颜色,稍深的紫色 */
}
.menu-items a.active {
color: #6366F1; /* 高亮颜色,与网站主题一致 */
font-weight: bold;
border-bottom: 2px solid #6366F1; /* 添加下划线高亮 */
}
.right-container { .right-container {
display: flex; display: flex;
align-items: center; align-items: center;
margin-left: auto; /* 将右侧容器推到最右边 */
} }
.right-container > * { .right-container > * {
...@@ -138,4 +163,17 @@ header nav { ...@@ -138,4 +163,17 @@ header nav {
.dropdown-menu button:hover { .dropdown-menu button:hover {
color: #007bff; color: #007bff;
} }
\ No newline at end of file
/* 调整导航栏布局 */
header nav {
display: flex;
align-items: center;
padding: 10px 20px;
}
.right-container {
margin-left: auto; /* 将右侧容器推到最右边 */
display: flex;
align-items: center;
}
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
.timeline-title { .timeline-title {
text-align: center; text-align: center;
color: #7c4dff; color: #4560f5;
margin-bottom: 40px; margin-bottom: 40px;
font-size: 60px; font-size: 60px;
} }
...@@ -28,7 +28,7 @@ ...@@ -28,7 +28,7 @@
left: 50%; left: 50%;
width: 2px; width: 2px;
height: 100%; height: 100%;
background: #7c4dff; background: #4560f5;
} }
.event { .event {
...@@ -43,7 +43,7 @@ ...@@ -43,7 +43,7 @@
left: 50%; left: 50%;
width: 20px; width: 20px;
height: 20px; height: 20px;
background: #7c4dff; background: #4560f5;
border-radius: 50%; border-radius: 50%;
transform: translateX(-50%); transform: translateX(-50%);
} }
...@@ -67,7 +67,7 @@ ...@@ -67,7 +67,7 @@
.event-date { .event-date {
font-weight: bold; font-weight: bold;
color: #7c4dff; color: #4560f5;
margin-bottom: 5px; margin-bottom: 5px;
} }
......
...@@ -59,35 +59,6 @@ main { ...@@ -59,35 +59,6 @@ main {
padding: 3rem 1rem 1rem; padding: 3rem 1rem 1rem;
} }
.hero {
text-align: center;
margin-bottom: 2rem;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
padding: 0.1rem 0.1rem;
border-radius: 20px;
color: white;
}
h1 {
font-size: 4rem;
margin-bottom: 1.5rem;
font-weight: 700;
letter-spacing: -0.015em;
}
.slogan {
font-size: 1.8rem;
margin-bottom: 3.5rem;
font-weight: 400;
opacity: 0.9;
}
.search-container {
position: relative;
max-width: 680px;
margin: 0 auto;
}
.tools-section h2 { .tools-section h2 {
text-align: center; text-align: center;
font-size: 3rem; font-size: 3rem;
...@@ -105,33 +76,48 @@ h1 { ...@@ -105,33 +76,48 @@ h1 {
.tool-card { .tool-card {
background-color: var(--card-background); background-color: var(--card-background);
border-radius: 20px; border-radius: 20px;
padding: 2.5rem; padding: 1rem; /* 减少内边距以增加内容紧凑度 */
box-shadow: var(--card-shadow); box-shadow: var(--card-shadow);
transition: all 0.3s ease; transition: all 0.3s ease;
text-align: center; display: flex;
align-items: center; /* 垂直居中对齐 */
text-decoration: none;
height: 100%;
} }
.tool-card:hover { .tool-card:hover {
transform: translateY(-5px); transform: translateY(-5px);
box-shadow: 0 12px 24px rgba(0, 0, 0, 0.15); box-shadow: 0 12px 24px rgba(0, 0, 0, 0.15);
text-decoration: none;
} }
.tool-icon { .tool-icon {
font-size: 4rem; width: 40px; /* 调整图标宽度 */
color: var(--secondary-color); height: 40px; /* 调整图标高度 */
margin-bottom: 2rem; object-fit: contain;
margin-right: 1rem; /* 缩小图标与文本之间的间距 */
}
.tool-content {
display: flex;
flex-direction: column;
} }
.tool-title { .tool-title {
font-size: 1.6rem; font-size: 1.6rem;
font-weight: 600; font-weight: 600;
margin-bottom: 1rem; margin: 0;
margin-bottom: 0.25rem; /* 减少标题与描述之间的间距 */
color: var(--primary-color);
text-decoration: none;
} }
.tool-description { .tool-description {
font-size: 1.1rem; font-size: 1.1rem;
color: #86868b; color: #86868b;
line-height: 1.5; line-height: 1.4; /* 适当调整行高 */
margin: 0; /* 移除描述的默认 margin */
text-decoration: none;
} }
footer { footer {
...@@ -146,12 +132,24 @@ footer { ...@@ -146,12 +132,24 @@ footer {
grid-template-columns: 1fr; grid-template-columns: 1fr;
} }
h1 { .tool-card {
font-size: 3rem; flex-direction: column; /* 在小屏幕上堆叠图标和文本 */
align-items: center; /* 居中对齐 */
text-align: center;
padding: 1rem;
} }
.slogan { .tool-icon {
font-size: 1.5rem; margin-right: 0;
margin-bottom: 1rem;
}
.tool-title {
margin-bottom: 0.25rem; /* 减少标题与描述之间的间距 */
}
.tool-description {
line-height: 1.4; /* 保持一致的行高 */
} }
} }
...@@ -212,14 +210,13 @@ footer { ...@@ -212,14 +210,13 @@ footer {
.footer-link { .footer-link {
text-decoration: none; text-decoration: none;
color: inherit; /* 保持与父元素相同的颜色,可以根据需要调整 */ color: inherit;
} }
.footer-link:hover { .footer-link:hover {
text-decoration: none; /* 悬停时也不显示下划线 */ text-decoration: none;
} }
/* 如果需要进一步去除所有链接的下划线,可以添加以下全局样式 */
.footer a { .footer a {
text-decoration: none; text-decoration: none;
color: inherit; color: inherit;
...@@ -227,4 +224,5 @@ footer { ...@@ -227,4 +224,5 @@ footer {
.footer a:hover { .footer a:hover {
text-decoration: none; text-decoration: none;
} }
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment