diff --git a/admin-frontend/index.html b/admin-frontend/index.html
index 1339ae8..443dc49 100644
--- a/admin-frontend/index.html
+++ b/admin-frontend/index.html
@@ -2,7 +2,36 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
TradeMate 管理后台
diff --git a/admin-frontend/public/robots.txt b/admin-frontend/public/robots.txt
new file mode 100644
index 0000000..349f334
--- /dev/null
+++ b/admin-frontend/public/robots.txt
@@ -0,0 +1,7 @@
+User-agent: *
+Allow: /
+Disallow: /api/
+Disallow: /static/
+Disallow: /assets/
+
+Sitemap: https://trade.yuzhiran.com/sitemap.xml
\ No newline at end of file
diff --git a/tests/browser_automation.py b/tests/browser_automation.py
new file mode 100644
index 0000000..b40c959
--- /dev/null
+++ b/tests/browser_automation.py
@@ -0,0 +1,347 @@
+#!/usr/bin/env python3
+"""
+TradeMate 前端 SEO 与浏览器自动化测试脚本
+使用 requests + BeautifulSoup 进行 SEO 审计
+"""
+
+import json
+import re
+import sys
+from pathlib import Path
+from urllib.parse import urljoin, urlparse
+
+try:
+ import requests
+ from bs4 import BeautifulSoup
+ HAS_REQUESTS = True
+except ImportError:
+ HAS_REQUESTS = False
+ print("⚠️ requests/bs4 未安装,跳过网络测试")
+
+BASE_URL = "https://trade.yuzhiran.com"
+TEST_RESULTS = []
+
+def run_test(name: str, test_func):
+ """运行单个测试并记录结果"""
+ try:
+ result = test_func()
+ TEST_RESULTS.append({"name": name, "status": "PASS", "detail": result})
+ print(f"✅ {name}")
+ except Exception as e:
+ TEST_RESULTS.append({"name": name, "status": "FAIL", "detail": str(e)})
+ print(f"❌ {name}: {e}")
+
+def get_page(url: str, timeout: int = 15) -> tuple:
+ """获取页面内容"""
+ if not HAS_REQUESTS:
+ raise RuntimeError("requests not available")
+ response = requests.get(url, timeout=timeout, headers={
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
+ })
+ response.raise_for_status()
+ return response
+
+def parse_html(html: str) -> BeautifulSoup:
+ """解析 HTML"""
+ return BeautifulSoup(html, 'html.parser', from_encoding='utf-8')
+
+def test_homepage_seo():
+ """测试首页 SEO 优化"""
+ response = get_page(BASE_URL)
+ soup = parse_html(response.text)
+
+ # 检查 title
+ title = soup.title.string if soup.title else ""
+ assert "TradeMate" in title, f"Title should contain TradeMate, got: {title}"
+
+ # 检查 meta description
+ desc_tag = soup.find('meta', attrs={'name': 'description'})
+ desc = desc_tag.get('content', '') if desc_tag else ""
+ assert len(desc) > 50, f"Meta description too short: {desc}"
+
+ # 检查 viewport
+ viewport_tag = soup.find('meta', attrs={'name': 'viewport'})
+ viewport = viewport_tag.get('content', '') if viewport_tag else ""
+ assert "width=device-width" in viewport, f"Viewport missing: {viewport}"
+
+ # 检查 canonical
+ canonical_tag = soup.find('link', attrs={'rel': 'canonical'})
+ canonical = canonical_tag.get('href', '') if canonical_tag else ""
+ assert canonical == BASE_URL + "/", f"Canonical incorrect: {canonical}"
+
+ # 检查 Open Graph
+ og_title_tag = soup.find('meta', attrs={'property': 'og:title'})
+ og_title = og_title_tag.get('content', '') if og_title_tag else ""
+ assert "TradeMate" in og_title, f"OG title missing: {og_title}"
+
+ # 检查结构化数据
+ ld_scripts = soup.find_all('script', attrs={'type': 'application/ld+json'})
+ assert len(ld_scripts) > 0, "No structured data found"
+
+ return {
+ "title": title,
+ "description": desc[:100] + "...",
+ "canonical": canonical,
+ "og_title": og_title,
+ "structured_data_count": len(ld_scripts)
+ }
+
+def test_homepage_performance():
+ """测试首页性能(简化版)"""
+ response = get_page(BASE_URL)
+
+ # 检查页面大小
+ html_size = len(response.text.encode('utf-8'))
+ assert html_size < 500000, f"HTML too large: {html_size} bytes"
+
+ # 检查 HTTP 状态
+ assert response.status_code == 200, f"Status code: {response.status_code}"
+
+ # 检查压缩
+ content_encoding = response.headers.get('Content-Encoding', '')
+
+ return {
+ "html_size": html_size,
+ "status_code": response.status_code,
+ "content_encoding": content_encoding,
+ "load_time_ms": response.elapsed.total_seconds() * 1000
+ }
+
+def test_workspace_seo():
+ """测试工作台 SEO"""
+ response = get_page(BASE_URL + "/workspace/")
+ soup = parse_html(response.text)
+
+ title = soup.title.string if soup.title else ""
+ assert "工作台" in title or "TradeMate" in title, f"Workspace title incorrect: {title}"
+
+ desc_tag = soup.find('meta', attrs={'name': 'description'})
+ desc = desc_tag.get('content', '') if desc_tag else ""
+ assert len(desc) > 30, f"Workspace description too short: {desc}"
+
+ # 检查 robots
+ robots_tag = soup.find('meta', attrs={'name': 'robots'})
+ robots = robots_tag.get('content', '') if robots_tag else ""
+ assert "noindex" in robots, f"Workspace should have noindex: {robots}"
+
+ return {"title": title, "description": desc[:100] + "...", "robots": robots}
+
+def test_admin_seo():
+ """测试管理后台 SEO"""
+ response = get_page(BASE_URL + "/admin/")
+ soup = parse_html(response.text)
+
+ title = soup.title.string if soup.title else ""
+ assert "管理后台" in title or "TradeMate" in title, f"Admin title incorrect: {title}"
+
+ # 检查 robots
+ robots_tag = soup.find('meta', attrs={'name': 'robots'})
+ robots = robots_tag.get('content', '') if robots_tag else ""
+ assert "noindex" in robots, f"Admin should have noindex: {robots}"
+
+ return {"title": title, "robots": robots}
+
+def test_app_seo():
+ """测试移动端 App SEO"""
+ response = get_page(BASE_URL + "/app/")
+ soup = parse_html(response.text)
+
+ title = soup.title.string if soup.title else ""
+ # Accept both full title and short title
+ assert "TradeMate" in title or "外贸" in title or "小助手" in title, f"App title incorrect: {title}"
+
+ desc_tag = soup.find('meta', attrs={'name': 'description'})
+ desc = desc_tag.get('content', '') if desc_tag else ""
+ assert len(desc) > 30, f"App description too short: {desc}"
+
+ # 检查 PWA meta
+ apple_capable_tag = soup.find('meta', attrs={'name': 'apple-mobile-web-app-capable'})
+ apple_capable = apple_capable_tag.get('content', '') if apple_capable_tag else ""
+ assert apple_capable == "yes", f"Apple web app capable missing: {apple_capable}"
+
+ # 检查 theme-color
+ theme_color_tag = soup.find('meta', attrs={'name': 'theme-color'})
+ theme_color = theme_color_tag.get('content', '') if theme_color_tag else ""
+ assert theme_color, f"Theme color missing: {theme_color}"
+
+ return {"title": title, "description": desc[:100] + "...", "apple_capable": apple_capable, "theme_color": theme_color}
+
+def test_robots_txt():
+ """测试 robots.txt"""
+ response = get_page(BASE_URL + "/robots.txt")
+ assert response.status_code == 200, f"robots.txt returned {response.status_code}"
+
+ content = response.text
+ assert "User-agent" in content, "robots.txt missing User-agent"
+ assert "Disallow" in content, "robots.txt missing Disallow"
+ assert "Sitemap" in content, "robots.txt missing Sitemap"
+
+ return {"status": response.status_code, "has_user_agent": True, "has_disallow": True, "has_sitemap": True}
+
+def test_sitemap_xml():
+ """测试 sitemap.xml"""
+ response = get_page(BASE_URL + "/sitemap.xml")
+ assert response.status_code == 200, f"sitemap.xml returned {response.status_code}"
+
+ content = response.text
+ assert "" in content, "sitemap.xml missing url entries"
+ assert BASE_URL in content, "sitemap.xml missing base URL"
+
+ return {"status": response.status_code, "has_urlset": True, "has_urls": True}
+
+def test_image_optimization():
+ """测试图片优化"""
+ response = get_page(BASE_URL)
+ soup = parse_html(response.text)
+
+ images = soup.find_all('img')
+ results = []
+
+ for img in images:
+ src = img.get('src', '')
+ alt = img.get('alt', '')
+ width = img.get('width')
+ height = img.get('height')
+
+ result = {"src": src, "has_alt": bool(alt), "has_dimensions": bool(width or height)}
+ if src:
+ result["is_webp"] = src.endswith('.webp')
+ result["is_lazy"] = img.get('loading') == 'lazy'
+ results.append(result)
+
+ # 检查是否有图片缺少 alt
+ missing_alt = [r for r in results if not r.get('has_alt')]
+
+ return {"total_images": len(results), "missing_alt": len(missing_alt), "images": results[:5]}
+
+def test_links():
+ """测试链接有效性"""
+ response = get_page(BASE_URL)
+ soup = parse_html(response.text)
+
+ links = soup.find_all('a', href=True)
+ broken_links = []
+
+ for link in links:
+ href = link['href']
+ if href.startswith('#') or href.startswith('mailto:'):
+ continue
+
+ full_url = urljoin(BASE_URL, href)
+ try:
+ link_response = requests.get(full_url, timeout=5, allow_redirects=True)
+ if link_response.status_code >= 400:
+ broken_links.append({"href": href, "status": link_response.status_code})
+ except:
+ broken_links.append({"href": href, "status": "timeout"})
+
+ return {"total_links": len(links), "broken_links": broken_links}
+
+def test_semantic_html():
+ """测试语义化 HTML"""
+ response = get_page(BASE_URL)
+ soup = parse_html(response.text)
+
+ # 检查语义化标签
+ semantic_tags = ['header', 'nav', 'main', 'section', 'article', 'aside', 'footer']
+ found_tags = []
+
+ for tag in semantic_tags:
+ if soup.find(tag):
+ found_tags.append(tag)
+
+ # 检查 heading 层级
+ headings = soup.find_all(['h1', 'h2', 'h3', 'h4', 'h5', 'h6'])
+ h1_count = len(soup.find_all('h1'))
+
+ return {
+ "semantic_tags_found": found_tags,
+ "heading_count": len(headings),
+ "h1_count": h1_count,
+ "has_main": 'main' in found_tags
+ }
+
+def test_mobile_meta():
+ """测试移动端 meta 标签"""
+ response = get_page(BASE_URL + "/app/")
+ soup = parse_html(response.text)
+
+ viewport_tag = soup.find('meta', attrs={'name': 'viewport'})
+ viewport = viewport_tag.get('content', '') if viewport_tag else ""
+
+ # 检查移动端相关 meta
+ mobile_meta = {
+ "viewport": viewport,
+ "apple_capable": soup.find('meta', attrs={'name': 'apple-mobile-web-app-capable'}) is not None,
+ "apple_status_bar": soup.find('meta', attrs={'name': 'apple-mobile-web-app-status-bar-style'}) is not None,
+ "mobile_capable": soup.find('meta', attrs={'name': 'mobile-web-app-capable'}) is not None,
+ "theme_color": soup.find('meta', attrs={'name': 'theme-color'}) is not None
+ }
+
+ return mobile_meta
+
+def main():
+ """主测试函数"""
+ print("=" * 60)
+ print("TradeMate 前端 SEO 与浏览器自动化测试")
+ print("=" * 60)
+
+ if not HAS_REQUESTS:
+ print("\n❌ requests/bs4 未安装,无法运行测试")
+ print(" 请运行: pip install requests beautifulsoup4")
+ return 1
+
+ # 首页测试
+ print("\n📄 首页测试")
+ run_test("首页 SEO 优化", test_homepage_seo)
+ run_test("首页性能", test_homepage_performance)
+
+ # 子页面测试
+ print("\n📊 子页面测试")
+ run_test("工作台 SEO", test_workspace_seo)
+ run_test("管理后台 SEO", test_admin_seo)
+ run_test("移动端 App SEO", test_app_seo)
+
+ # SEO 文件测试
+ print("\n🔍 SEO 文件测试")
+ run_test("robots.txt", test_robots_txt)
+ run_test("sitemap.xml", test_sitemap_xml)
+
+ # 其他测试
+ print("\n🖼️ 其他测试")
+ run_test("图片优化", test_image_optimization)
+ run_test("链接有效性", test_links)
+ run_test("语义化 HTML", test_semantic_html)
+ run_test("移动端 meta", test_mobile_meta)
+
+ # 输出结果
+ print("\n" + "=" * 60)
+ print("测试结果汇总")
+ print("=" * 60)
+
+ passed = sum(1 for r in TEST_RESULTS if r["status"] == "PASS")
+ failed = sum(1 for r in TEST_RESULTS if r["status"] == "FAIL")
+
+ for result in TEST_RESULTS:
+ status_icon = "✅" if result["status"] == "PASS" else "❌"
+ detail = result['detail']
+ if isinstance(detail, dict):
+ detail = json.dumps(detail, ensure_ascii=False, indent=2)
+ print(f"{status_icon} {result['name']}")
+ if result['status'] == 'FAIL':
+ print(f" → {detail}")
+
+ print(f"\n总计: {len(TEST_RESULTS)} 个测试, {passed} 通过, {failed} 失败")
+
+ # 保存结果到 JSON
+ output_path = Path(__file__).parent / "test_results.json"
+ with open(output_path, 'w', encoding='utf-8') as f:
+ json.dump(TEST_RESULTS, f, ensure_ascii=False, indent=2)
+ print(f"\n结果已保存到: {output_path}")
+
+ return 0 if failed == 0 else 1
+
+if __name__ == "__main__":
+ exit_code = main()
+ sys.exit(exit_code)
\ No newline at end of file
diff --git a/uni-app/index.html b/uni-app/index.html
index cef7e67..bc1c9a8 100644
--- a/uni-app/index.html
+++ b/uni-app/index.html
@@ -2,10 +2,57 @@
-
- 外贸小助手 - TradeMate
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ TradeMate 外贸小助手 - AI 驱动的外贸智能工作台
diff --git a/uni-app/public/robots.txt b/uni-app/public/robots.txt
new file mode 100644
index 0000000..a9b85b9
--- /dev/null
+++ b/uni-app/public/robots.txt
@@ -0,0 +1,24 @@
+User-agent: *
+Allow: /
+Allow: /app/
+Allow: /workspace/
+Disallow: /admin/
+Disallow: /api/
+Disallow: /static/
+Disallow: /assets/
+
+User-agent: Googlebot
+Allow: /
+Allow: /app/
+Allow: /workspace/
+Disallow: /admin/
+Disallow: /api/
+
+User-agent: Baiduspider
+Allow: /
+Allow: /app/
+Allow: /workspace/
+Disallow: /admin/
+Disallow: /api/
+
+Sitemap: https://trade.yuzhiran.com/sitemap.xml
\ No newline at end of file
diff --git a/uni-app/public/sitemap.xml b/uni-app/public/sitemap.xml
new file mode 100644
index 0000000..5268493
--- /dev/null
+++ b/uni-app/public/sitemap.xml
@@ -0,0 +1,34 @@
+
+
+
+ https://trade.yuzhiran.com/
+ 2026-06-29
+ weekly
+ 1.0
+
+
+ https://trade.yuzhiran.com/app/
+ 2026-06-29
+ daily
+ 0.9
+
+
+ https://trade.yuzhiran.com/workspace/
+ 2026-06-29
+ daily
+ 0.8
+
+
+ https://trade.yuzhiran.com/app/pages/agreement/privacy
+ 2026-06-29
+ monthly
+ 0.5
+
+
+ https://trade.yuzhiran.com/app/pages/agreement/terms
+ 2026-06-29
+ monthly
+ 0.5
+
+
\ No newline at end of file
diff --git a/user-frontend/index.html b/user-frontend/index.html
index d2a039e..975ec00 100644
--- a/user-frontend/index.html
+++ b/user-frontend/index.html
@@ -2,7 +2,36 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
TradeMate 工作台
diff --git a/user-frontend/public/robots.txt b/user-frontend/public/robots.txt
new file mode 100644
index 0000000..349f334
--- /dev/null
+++ b/user-frontend/public/robots.txt
@@ -0,0 +1,7 @@
+User-agent: *
+Allow: /
+Disallow: /api/
+Disallow: /static/
+Disallow: /assets/
+
+Sitemap: https://trade.yuzhiran.com/sitemap.xml
\ No newline at end of file