diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 00000000..c10f7c72
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,36 @@
+# Node modules
+InfoGenie-frontend/node_modules
+InfoGenie-frontend/build
+
+# Python cache
+InfoGenie-backend/__pycache__
+InfoGenie-backend/**/__pycache__
+InfoGenie-backend/*.pyc
+InfoGenie-backend/**/*.pyc
+
+# Git
+.git
+.gitignore
+
+# IDE
+.vscode
+.idea
+*.swp
+*.swo
+
+# Logs
+*.log
+
+# OS
+.DS_Store
+Thumbs.db
+
+# Test files
+InfoGenie-backend/test
+
+# Documentation
+*.md
+
+# Backup files
+*.backup
+*.bak
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 00000000..ea2b41a4
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,57 @@
+# InfoGenie 统一 Docker 镜像
+# 多阶段构建:前端构建 + 后端 + Nginx
+
+# 阶段1: 前端构建
+FROM node:18-alpine AS frontend-builder
+
+WORKDIR /frontend
+COPY InfoGenie-frontend/package*.json ./
+RUN npm install --legacy-peer-deps
+COPY InfoGenie-frontend/ ./
+RUN npm run build
+
+# 阶段2: 最终镜像
+FROM python:3.10-slim
+
+# 安装 Nginx 和必要的工具
+RUN apt-get update && apt-get install -y \
+ nginx \
+ supervisor \
+ && rm -rf /var/lib/apt/lists/*
+
+# 设置工作目录
+WORKDIR /app
+
+# 复制后端代码
+COPY InfoGenie-backend/ ./backend/
+
+# 安装 Python 依赖
+RUN pip install --no-cache-dir -r ./backend/requirements.txt gunicorn
+
+# 复制前端构建产物到 Nginx 目录
+COPY --from=frontend-builder /frontend/build /usr/share/nginx/html
+
+# 创建持久化数据目录
+RUN mkdir -p /app/data/logs
+
+# 复制 Nginx 配置
+COPY docker/nginx.conf /etc/nginx/nginx.conf
+COPY docker/default.conf /etc/nginx/conf.d/default.conf
+
+# 复制 Supervisor 配置
+COPY docker/supervisord.conf /etc/supervisor/conf.d/supervisord.conf
+
+# 复制启动脚本
+COPY docker/entrypoint.sh /entrypoint.sh
+RUN chmod +x /entrypoint.sh
+
+# 暴露端口
+EXPOSE 2323
+
+# 设置环境变量
+ENV FLASK_APP=app.py
+ENV FLASK_ENV=production
+ENV PYTHONUNBUFFERED=1
+
+# 使用 supervisor 管理多进程
+ENTRYPOINT ["/entrypoint.sh"]
diff --git a/InfoGenie-backend/modules/__pycache__/aimodelapp.cpython-313.pyc b/InfoGenie-backend/modules/__pycache__/aimodelapp.cpython-313.pyc
index fff27464..68c49b55 100755
Binary files a/InfoGenie-backend/modules/__pycache__/aimodelapp.cpython-313.pyc and b/InfoGenie-backend/modules/__pycache__/aimodelapp.cpython-313.pyc differ
diff --git a/InfoGenie-backend/modules/__pycache__/auth.cpython-313.pyc b/InfoGenie-backend/modules/__pycache__/auth.cpython-313.pyc
index d34a4a37..71487c3c 100755
Binary files a/InfoGenie-backend/modules/__pycache__/auth.cpython-313.pyc and b/InfoGenie-backend/modules/__pycache__/auth.cpython-313.pyc differ
diff --git a/InfoGenie-backend/modules/__pycache__/email_service.cpython-313.pyc b/InfoGenie-backend/modules/__pycache__/email_service.cpython-313.pyc
index e992185e..8c30c86a 100755
Binary files a/InfoGenie-backend/modules/__pycache__/email_service.cpython-313.pyc and b/InfoGenie-backend/modules/__pycache__/email_service.cpython-313.pyc differ
diff --git a/InfoGenie-backend/modules/__pycache__/user_management.cpython-313.pyc b/InfoGenie-backend/modules/__pycache__/user_management.cpython-313.pyc
index 1ad337d1..7d164dd7 100755
Binary files a/InfoGenie-backend/modules/__pycache__/user_management.cpython-313.pyc and b/InfoGenie-backend/modules/__pycache__/user_management.cpython-313.pyc differ
diff --git a/InfoGenie-backend/modules/aimodelapp.py b/InfoGenie-backend/modules/aimodelapp.py
index 5b52d554..7de7c36c 100755
--- a/InfoGenie-backend/modules/aimodelapp.py
+++ b/InfoGenie-backend/modules/aimodelapp.py
@@ -855,6 +855,56 @@ def linux_command_generator():
except Exception as e:
return jsonify({'error': f'Linux命令生成失败: {str(e)}'}), 500
+#AI文章排版(Markdown格式化)接口
+@aimodelapp_bp.route('/markdown_formatting', methods=['POST'])
+@verify_user_coins
+def markdown_formatting():
+ """AI文章排版(Markdown格式化)接口"""
+ try:
+ data = request.get_json()
+ article_text = data.get('article_text', '').strip()
+ emoji_style = data.get('emoji_style', 'balanced').strip()
+ markdown_option = data.get('markdown_option', 'standard').strip()
+
+ if not article_text:
+ return jsonify({'error': '文章内容不能为空'}), 400
+
+ # 构建Markdown排版的提示词
+ prompt = f"""你是一位专业的文档排版助手。请将用户提供的全文按“标准Markdown格式”进行排版,并在不改变任何原文内容的前提下进行结构化呈现。严格遵守以下规则:
+
+1) 保留所有原始内容,严禁改写、删减或添加新内容。
+2) 使用合理的Markdown结构(标题、分节、段落、列表、引用、表格如有必要、代码块仅当原文包含)。
+3) 智能添加适量Emoji以增强可读性({emoji_style}),在标题、关键句、列表项等处点缀;避免过度使用,保持专业。
+4) 保持语言与语气不变,只优化排版和表现形式。
+5) 输出“纯Markdown文本”,不要包含任何JSON、HTML、XML、解释文字、或代码块围栏标记(例如不要在最外层使用```)。
+
+如果原文本较长,可在开头自动生成简洁的“目录”以便阅读。
+
+原文如下:
+{article_text}
+"""
+
+ messages = [{"role": "user", "content": prompt}]
+
+ # 使用DeepSeek进行排版生成
+ content, error = call_deepseek_api(messages)
+
+ if error:
+ return jsonify({'error': error}), 500
+
+ # 返回AI生成的Markdown文本
+ return jsonify({
+ 'success': True,
+ 'formatted_markdown': content,
+ 'source_text': article_text,
+ 'emoji_style': emoji_style,
+ 'markdown_option': markdown_option,
+ 'timestamp': datetime.now().isoformat()
+ })
+
+ except Exception as e:
+ return jsonify({'error': f'文章排版失败: {str(e)}'}), 500
+
#获取用户萌芽币余额
@aimodelapp_bp.route('/coins', methods=['GET'])
def get_user_coins():
@@ -951,4 +1001,87 @@ def get_available_models():
})
except Exception as e:
- return jsonify({'error': f'获取模型列表失败: {str(e)}'}), 500
\ No newline at end of file
+ return jsonify({'error': f'获取模型列表失败: {str(e)}'}), 500
+
+#中国亲戚称呼计算器接口(普通话版 + 方言)
+@aimodelapp_bp.route('/kinship-calculator', methods=['POST'])
+@verify_user_coins
+def kinship_calculator():
+ """中国亲戚称呼计算器接口"""
+ try:
+ data = request.get_json() or {}
+ relation_chain = (data.get('relation_chain') or '').strip()
+ dialects = data.get('dialects') # 可选,指定方言列表
+
+ if not relation_chain:
+ return jsonify({'error': '亲属关系链不能为空'}), 400
+
+ # 组装提示词:要求严格JSON输出
+ requested_dialects = dialects if isinstance(dialects, list) and dialects else [
+ '粤语', '闽南语', '上海话', '四川话', '东北话', '客家话'
+ ]
+
+ prompt = f"""你是一位中国亲属称呼专家。请解析下面的亲属关系链,给出最终的亲属称呼。
+输入的关系链会用“的”连接,如“妈妈的爸爸”“爸爸的姐姐的儿子”。
+
+请遵循:
+1) 以中国大陆通行的标准普通话称呼为准,给出最常用、规范的最终称呼。
+2) 同时给出若干方言的对应称呼:{', '.join(requested_dialects)}。
+3) 如存在地区差异或性别歧义,请在notes中说明,但最终给出一个最常用称呼。
+4) 不要展示推理过程;只输出JSON。
+
+严格按以下JSON结构输出:
+{{
+ "mandarin_title": "标准普通话称呼",
+ "dialect_titles": {{
+ "粤语": {{"title": "称呼", "romanization": "粤拼或发音", "notes": "可选说明"}},
+ "闽南语": {{"title": "称呼", "romanization": "白话字或发音", "notes": "可选说明"}},
+ "上海话": {{"title": "称呼", "romanization": "拟音或IPA", "notes": "可选说明"}},
+ "四川话": {{"title": "称呼", "romanization": "拟音或IPA", "notes": "可选说明"}},
+ "东北话": {{"title": "称呼", "romanization": "拟音或IPA", "notes": "可选说明"}},
+ "客家话": {{"title": "称呼", "romanization": "客家话拟音", "notes": "可选说明"}}
+ }},
+ "notes": "总体说明(如地区差异、辈分方向、父系/母系等提示)"
+}}
+
+关系链:
+{relation_chain}
+"""
+
+ messages = [{"role": "user", "content": prompt}]
+ content, error = call_deepseek_api(messages)
+
+ if error:
+ return jsonify({'error': error}), 500
+
+ # 解析AI返回的JSON
+ try:
+ result = json.loads(content)
+ except json.JSONDecodeError:
+ import re
+ m = re.search(r'\{[\s\S]*\}', content)
+ if not m:
+ return jsonify({'error': 'AI返回的数据中未找到有效JSON'}), 500
+ try:
+ result = json.loads(m.group())
+ except Exception:
+ return jsonify({'error': 'AI返回的JSON格式无法解析'}), 500
+
+ mandarin_title = result.get('mandarin_title')
+ dialect_titles = result.get('dialect_titles', {})
+ notes = result.get('notes', '')
+
+ if not mandarin_title:
+ return jsonify({'error': '未获得标准普通话称呼'}), 500
+
+ return jsonify({
+ 'success': True,
+ 'relation_chain': relation_chain,
+ 'mandarin_title': mandarin_title,
+ 'dialect_titles': dialect_titles,
+ 'notes': notes,
+ 'timestamp': datetime.now().isoformat()
+ })
+
+ except Exception as e:
+ return jsonify({'error': f'亲戚称呼计算失败: {str(e)}'}), 500
\ No newline at end of file
diff --git a/InfoGenie-backend/modules/auth.py b/InfoGenie-backend/modules/auth.py
index 2e074970..a62f99c7 100755
--- a/InfoGenie-backend/modules/auth.py
+++ b/InfoGenie-backend/modules/auth.py
@@ -70,6 +70,8 @@ def validate_password(password):
"""验证密码格式(6-20位)"""
return 6 <= len(password) <= 20
+
+#==========================对外暴露的HTTP接口==========================
#发送验证码邮件
@auth_bp.route('/send-verification', methods=['POST'])
def send_verification():
@@ -450,3 +452,4 @@ def check_login():
'success': False,
'message': f'服务器错误: {str(e)}'
}), 500
+#==========================对外暴露的HTTP接口==========================
\ No newline at end of file
diff --git a/InfoGenie-backend/modules/user_management.py b/InfoGenie-backend/modules/user_management.py
index 4d4af285..db252c48 100755
--- a/InfoGenie-backend/modules/user_management.py
+++ b/InfoGenie-backend/modules/user_management.py
@@ -51,6 +51,8 @@ def login_required(f):
return decorated_function
return decorated_function
+
+#==========================对外暴露的HTTP接口==========================
# 获取用户资料
@user_bp.route('/profile', methods=['GET'])
@login_required
@@ -102,6 +104,127 @@ def get_profile():
'message': f'服务器错误: {str(e)}'
}), 500
+# 为指定账号增加萌芽币
+@user_bp.route('/add-coins', methods=['POST'])
+@login_required
+def add_coins():
+ """为指定账号增加萌芽币(支持email或username指定账号,amount为正整数)"""
+ try:
+ data = request.get_json() or {}
+ email = (data.get('email') or '').strip()
+ username = (data.get('username') or '').strip()
+ amount = data.get('amount')
+
+ # 参数校验
+ if not email and not username:
+ return jsonify({
+ 'success': False,
+ 'message': '请提供email或username其中之一'
+ }), 400
+
+ if amount is None:
+ return jsonify({
+ 'success': False,
+ 'message': 'amount不能为空'
+ }), 400
+
+ try:
+ amount_int = int(amount)
+ except Exception:
+ return jsonify({
+ 'success': False,
+ 'message': 'amount必须为整数'
+ }), 400
+
+ if amount_int <= 0:
+ return jsonify({
+ 'success': False,
+ 'message': 'amount必须为正整数'
+ }), 400
+
+ users_collection = current_app.mongo.db.userdata
+ query = {'邮箱': email} if email else {'用户名': username}
+ user = users_collection.find_one(query)
+ if not user:
+ return jsonify({
+ 'success': False,
+ 'message': '用户不存在'
+ }), 404
+
+ before_coins = user.get('萌芽币', 0)
+ update_result = users_collection.update_one(query, {'$inc': {'萌芽币': amount_int}})
+
+ if update_result.modified_count == 0:
+ return jsonify({
+ 'success': False,
+ 'message': '更新失败,请稍后重试'
+ }), 500
+
+ updated = users_collection.find_one({'_id': user['_id']})
+ new_coins = updated.get('萌芽币', before_coins)
+
+ return jsonify({
+ 'success': True,
+ 'message': f"已为账户{email or username}增加{amount_int}萌芽币",
+ 'data': {
+ 'before_coins': before_coins,
+ 'added': amount_int,
+ 'new_coins': new_coins,
+ 'user': {
+ 'id': str(updated.get('_id')),
+ 'email': updated.get('邮箱'),
+ 'username': updated.get('用户名'),
+ 'avatar': updated.get('头像'),
+ 'register_time': updated.get('注册时间'),
+ 'last_login': updated.get('最后登录'),
+ 'login_count': updated.get('登录次数', 0),
+ 'status': updated.get('用户状态', 'active'),
+ 'level': updated.get('等级', 0),
+ 'experience': updated.get('经验', 0),
+ 'coins': new_coins
+ }
+ }
+ }), 200
+ except Exception as e:
+ return jsonify({
+ 'success': False,
+ 'message': f'服务器错误: {str(e)}'
+ }), 500
+
+# 列出所有用户
+@user_bp.route('/list', methods=['GET'])
+@login_required
+def list_users():
+ """列出所有用户(不返回密码)"""
+ try:
+ users_collection = current_app.mongo.db.userdata
+ cursor = users_collection.find({}, {'密码': 0})
+ users = []
+ for u in cursor:
+ users.append({
+ 'id': str(u.get('_id')),
+ 'email': u.get('邮箱'),
+ 'username': u.get('用户名'),
+ 'avatar': u.get('头像'),
+ 'register_time': u.get('注册时间'),
+ 'last_login': u.get('最后登录'),
+ 'login_count': u.get('登录次数', 0),
+ 'status': u.get('用户状态', 'active'),
+ 'level': u.get('等级', 0),
+ 'experience': u.get('经验', 0),
+ 'coins': u.get('萌芽币', 0)
+ })
+ return jsonify({
+ 'success': True,
+ 'count': len(users),
+ 'data': users
+ }), 200
+ except Exception as e:
+ return jsonify({
+ 'success': False,
+ 'message': f'服务器错误: {str(e)}'
+ }), 500
+
# 修改密码
@user_bp.route('/change-password', methods=['POST'])
@login_required
@@ -424,3 +547,4 @@ def delete_account():
'success': False,
'message': f'服务器错误: {str(e)}'
}), 500
+#==========================对外暴露的HTTP接口==========================
\ No newline at end of file
diff --git a/InfoGenie-backend/test/test_add_coins.py b/InfoGenie-backend/test/test_add_coins.py
new file mode 100644
index 00000000..aa3c51e6
--- /dev/null
+++ b/InfoGenie-backend/test/test_add_coins.py
@@ -0,0 +1,100 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+"""
+测试为指定账号增加萌芽币接口 (/api/user/add-coins)
+"""
+
+import os
+import sys
+import json
+from datetime import datetime
+
+# 加入后端根目录到路径,导入create_app
+sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+
+from app import create_app
+from modules.auth import generate_token
+from werkzeug.security import generate_password_hash
+
+
+def run_test():
+ """运行加币接口测试,打印真实响应并断言结果"""
+ app = create_app()
+
+ with app.app_context():
+ db = app.mongo.db
+ users = db.userdata
+
+ # 构造一个临时测试用户(真实写库,测试结束删除)
+ test_email = "infogenie.test.addcoins@foxmail.com"
+ users.delete_many({'邮箱': test_email})
+ test_user = {
+ '邮箱': test_email,
+ '用户名': '测试用户_加币',
+ '密码': generate_password_hash('AddCoins123!'),
+ '头像': None,
+ '注册时间': datetime.now().isoformat(),
+ '最后登录': None,
+ '登录次数': 0,
+ '用户状态': 'active',
+ '等级': 0,
+ '经验': 0,
+ '萌芽币': 0,
+ '签到系统': {
+ '连续签到天数': 0,
+ '今日是否已签到': False,
+ '签到时间': datetime.now().strftime('%Y-%m-%d')
+ }
+ }
+ insert_result = users.insert_one(test_user)
+ test_user_id = str(insert_result.inserted_id)
+
+ # 生成有效JWT用于认证
+ token = generate_token({
+ 'user_id': test_user_id,
+ 'email': test_email,
+ 'username': test_user['用户名']
+ })
+
+ client = app.test_client()
+
+ # 第一次加币: +500
+ resp1 = client.post(
+ '/api/user/add-coins',
+ headers={'Authorization': f'Bearer {token}'},
+ json={'email': test_email, 'amount': 500}
+ )
+ print('第一次加币 状态码:', resp1.status_code)
+ data1 = resp1.get_json()
+ print('第一次加币 响应:')
+ print(json.dumps(data1, ensure_ascii=False, indent=2))
+ assert resp1.status_code == 200
+ assert data1.get('success') is True
+ assert data1['data']['before_coins'] == 0
+ assert data1['data']['added'] == 500
+ assert data1['data']['new_coins'] == 500
+
+ # 第二次加币: +200
+ resp2 = client.post(
+ '/api/user/add-coins',
+ headers={'Authorization': f'Bearer {token}'},
+ json={'email': test_email, 'amount': 200}
+ )
+ print('第二次加币 状态码:', resp2.status_code)
+ data2 = resp2.get_json()
+ print('第二次加币 响应:')
+ print(json.dumps(data2, ensure_ascii=False, indent=2))
+ assert resp2.status_code == 200
+ assert data2.get('success') is True
+ assert data2['data']['before_coins'] == 500
+ assert data2['data']['added'] == 200
+ assert data2['data']['new_coins'] == 700
+
+ # 清理临时测试用户
+ users.delete_many({'邮箱': test_email})
+
+
+if __name__ == '__main__':
+ print('🔧 开始测试 /api/user/add-coins 接口...')
+ run_test()
+ print('✅ 测试完成!')
\ No newline at end of file
diff --git a/InfoGenie-backend/test/test_user_list.py b/InfoGenie-backend/test/test_user_list.py
new file mode 100644
index 00000000..26df9d80
--- /dev/null
+++ b/InfoGenie-backend/test/test_user_list.py
@@ -0,0 +1,81 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+"""
+测试列出所有用户的HTTP接口 (/api/user/list)
+"""
+
+import os
+import sys
+import json
+from datetime import datetime
+
+# 将后端根目录加入路径,便于导入app
+sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+
+from app import create_app
+from modules.auth import generate_token
+from werkzeug.security import generate_password_hash
+
+
+def run_test():
+ """运行用户列表接口测试,输出真实数据"""
+ # 使用.env中的真实Mongo配置,不造假
+ app = create_app()
+
+ with app.app_context():
+ db = app.mongo.db
+ users = db.userdata
+
+ # 插入一个测试用户(真实写入后再删除),确保可验证接口输出
+ test_email = "infogenie.test.user@foxmail.com"
+ users.delete_many({'邮箱': test_email})
+ test_user = {
+ '邮箱': test_email,
+ '用户名': '测试用户_列表',
+ '密码': generate_password_hash('TestPass123!'),
+ '头像': None,
+ '注册时间': datetime.now().isoformat(),
+ '最后登录': None,
+ '登录次数': 0,
+ '用户状态': 'active',
+ '等级': 0,
+ '经验': 0,
+ '萌芽币': 0,
+ '签到系统': {
+ '连续签到天数': 0,
+ '今日是否已签到': False,
+ '签到时间': datetime.now().strftime('%Y-%m-%d')
+ }
+ }
+ insert_result = users.insert_one(test_user)
+ test_user_id = str(insert_result.inserted_id)
+
+ # 生成有效JWT,满足认证要求
+ token = generate_token({
+ 'user_id': test_user_id,
+ 'email': test_email,
+ 'username': test_user['用户名']
+ })
+
+ client = app.test_client()
+ resp = client.get('/api/user/list', headers={'Authorization': f'Bearer {token}'})
+
+ print("状态码:", resp.status_code)
+ data = resp.get_json()
+ print("响应内容:")
+ print(json.dumps(data, ensure_ascii=False, indent=2))
+
+ # 基本断言,确保返回真实列表数据且包含刚插入的测试用户
+ assert resp.status_code == 200
+ assert data.get('success') is True
+ assert isinstance(data.get('data'), list)
+ assert any(u.get('email') == test_email for u in data['data'])
+
+ # 清理测试数据
+ users.delete_many({'邮箱': test_email})
+
+
+if __name__ == '__main__':
+ print('🔎 开始测试 /api/user/list 接口...')
+ run_test()
+ print('✅ 测试完成!')
\ No newline at end of file
diff --git a/InfoGenie-frontend/.env.development b/InfoGenie-frontend/.env.development
new file mode 100644
index 00000000..bf4bf9b4
--- /dev/null
+++ b/InfoGenie-frontend/.env.development
@@ -0,0 +1,11 @@
+# React 开发环境变量
+
+# API URL - 开发环境使用本地后端
+REACT_APP_API_URL=http://127.0.0.1:5002
+
+# 应用信息
+REACT_APP_NAME=InfoGenie
+REACT_APP_VERSION=1.0.0
+
+# 调试模式
+REACT_APP_DEBUG=true
diff --git a/InfoGenie-frontend/.env.production b/InfoGenie-frontend/.env.production
new file mode 100644
index 00000000..41a7dfa9
--- /dev/null
+++ b/InfoGenie-frontend/.env.production
@@ -0,0 +1,13 @@
+# React 构建时环境变量
+# 用于 Docker 构建
+
+# API URL - 在 Docker 环境下,前端和后端在同一个容器
+# 使用相对路径,这样前端会自动使用当前域名
+REACT_APP_API_URL=
+
+# 应用信息
+REACT_APP_NAME=InfoGenie
+REACT_APP_VERSION=1.0.0
+
+# 调试模式(生产环境关闭)
+REACT_APP_DEBUG=false
diff --git a/InfoGenie-frontend/public/60sapi/娱乐消遣/随机一言/css/background.css b/InfoGenie-frontend/public/60sapi/娱乐消遣/随机一言/css/background.css
index 3a10cfda..7bdb6f75 100755
--- a/InfoGenie-frontend/public/60sapi/娱乐消遣/随机一言/css/background.css
+++ b/InfoGenie-frontend/public/60sapi/娱乐消遣/随机一言/css/background.css
@@ -4,11 +4,11 @@
body {
background: linear-gradient(
135deg,
- #fff8dc 0%,
- #ffeaa7 25%,
- #fdcb6e 50%,
- #e17055 75%,
- #d63031 100%
+ #f1f8e9 0%,
+ #dcedc8 25%,
+ #c8e6c8 50%,
+ #a5d6a7 75%,
+ #81c784 100%
);
background-size: 400% 400%;
animation: gradientShift 15s ease infinite;
@@ -24,9 +24,9 @@ body::before {
width: 100%;
height: 100%;
background:
- radial-gradient(circle at 20% 80%, rgba(255, 215, 0, 0.1) 0%, transparent 50%),
- radial-gradient(circle at 80% 20%, rgba(255, 223, 0, 0.1) 0%, transparent 50%),
- radial-gradient(circle at 40% 40%, rgba(212, 175, 55, 0.05) 0%, transparent 50%);
+ radial-gradient(circle at 20% 80%, rgba(129, 199, 132, 0.1) 0%, transparent 50%),
+ radial-gradient(circle at 80% 20%, rgba(165, 214, 167, 0.1) 0%, transparent 50%),
+ radial-gradient(circle at 40% 40%, rgba(102, 187, 106, 0.05) 0%, transparent 50%);
pointer-events: none;
z-index: 1;
}
@@ -40,11 +40,11 @@ body::after {
width: 100%;
height: 100%;
background-image:
- radial-gradient(2px 2px at 20px 30px, rgba(255, 215, 0, 0.3), transparent),
- radial-gradient(2px 2px at 40px 70px, rgba(255, 223, 0, 0.2), transparent),
- radial-gradient(1px 1px at 90px 40px, rgba(212, 175, 55, 0.4), transparent),
- radial-gradient(1px 1px at 130px 80px, rgba(255, 215, 0, 0.2), transparent),
- radial-gradient(2px 2px at 160px 30px, rgba(255, 223, 0, 0.3), transparent);
+ radial-gradient(2px 2px at 20px 30px, rgba(129, 199, 132, 0.3), transparent),
+ radial-gradient(2px 2px at 40px 70px, rgba(165, 214, 167, 0.2), transparent),
+ radial-gradient(1px 1px at 90px 40px, rgba(102, 187, 106, 0.4), transparent),
+ radial-gradient(1px 1px at 130px 80px, rgba(129, 199, 132, 0.2), transparent),
+ radial-gradient(2px 2px at 160px 30px, rgba(165, 214, 167, 0.3), transparent);
background-repeat: repeat;
background-size: 200px 100px;
animation: sparkle 20s linear infinite;
@@ -106,8 +106,8 @@ body::after {
body::before {
background:
- radial-gradient(circle at 30% 70%, rgba(255, 215, 0, 0.08) 0%, transparent 40%),
- radial-gradient(circle at 70% 30%, rgba(255, 223, 0, 0.08) 0%, transparent 40%);
+ radial-gradient(circle at 30% 70%, rgba(129, 199, 132, 0.08) 0%, transparent 40%),
+ radial-gradient(circle at 70% 30%, rgba(165, 214, 167, 0.08) 0%, transparent 40%);
}
body::after {
@@ -121,9 +121,9 @@ body::after {
body {
background: linear-gradient(
135deg,
- #fff8dc 0%,
- #ffeaa7 50%,
- #fdcb6e 100%
+ #f1f8e9 0%,
+ #dcedc8 50%,
+ #c8e6c8 100%
);
background-size: 150% 150%;
}
@@ -138,18 +138,18 @@ body::after {
body {
background: linear-gradient(
135deg,
- #2c1810 0%,
- #3d2914 25%,
- #4a3319 50%,
- #5c3e1f 75%,
- #6b4423 100%
+ #1b2e1b 0%,
+ #2e4a2e 25%,
+ #3e5e3e 50%,
+ #4e6e4e 75%,
+ #5e7e5e 100%
);
}
body::before {
background:
- radial-gradient(circle at 20% 80%, rgba(255, 215, 0, 0.05) 0%, transparent 50%),
- radial-gradient(circle at 80% 20%, rgba(255, 223, 0, 0.05) 0%, transparent 50%);
+ radial-gradient(circle at 20% 80%, rgba(129, 199, 132, 0.05) 0%, transparent 50%),
+ radial-gradient(circle at 80% 20%, rgba(165, 214, 167, 0.05) 0%, transparent 50%);
}
}
@@ -162,6 +162,6 @@ body::after {
}
body {
- background: linear-gradient(135deg, #fff8dc 0%, #ffeaa7 50%, #fdcb6e 100%);
+ background: linear-gradient(135deg, #f1f8e9 0%, #dcedc8 50%, #c8e6c8 100%);
}
}
\ No newline at end of file
diff --git a/InfoGenie-frontend/public/60sapi/娱乐消遣/随机一言/css/style.css b/InfoGenie-frontend/public/60sapi/娱乐消遣/随机一言/css/style.css
index b84b1125..618808d2 100755
--- a/InfoGenie-frontend/public/60sapi/娱乐消遣/随机一言/css/style.css
+++ b/InfoGenie-frontend/public/60sapi/娱乐消遣/随机一言/css/style.css
@@ -8,7 +8,7 @@
body {
font-family: 'Microsoft YaHei', 'PingFang SC', 'Helvetica Neue', Arial, sans-serif;
line-height: 1.6;
- color: #2c1810;
+ color: #2e7d32;
overflow-x: hidden;
}
@@ -33,20 +33,20 @@ body {
.title {
font-size: 3rem;
font-weight: 700;
- color: #d4af37;
+ color: #2e7d32;
text-shadow:
- 0 0 10px rgba(212, 175, 55, 0.8),
- 0 0 20px rgba(212, 175, 55, 0.6),
- 0 0 30px rgba(212, 175, 55, 0.4);
+ 0 0 10px rgba(129, 199, 132, 0.8),
+ 0 0 20px rgba(129, 199, 132, 0.6),
+ 0 0 30px rgba(129, 199, 132, 0.4);
margin-bottom: 10px;
animation: titleGlow 3s ease-in-out infinite alternate;
}
.subtitle {
font-size: 1.2rem;
- color: #b8860b;
+ color: #388e3c;
opacity: 0.9;
- text-shadow: 0 0 5px rgba(184, 134, 11, 0.5);
+ text-shadow: 0 0 5px rgba(102, 187, 106, 0.5);
}
/* 主内容区域 */
@@ -58,14 +58,14 @@ body {
/* 一言容器 */
.quote-container {
- background: linear-gradient(135deg, rgba(255, 215, 0, 0.1), rgba(255, 223, 0, 0.05));
- border: 2px solid rgba(212, 175, 55, 0.3);
+ background: linear-gradient(135deg, rgba(129, 199, 132, 0.1), rgba(165, 214, 167, 0.05));
+ border: 2px solid rgba(102, 187, 106, 0.3);
border-radius: 20px;
padding: 40px;
margin-bottom: 30px;
backdrop-filter: blur(10px);
box-shadow:
- 0 8px 32px rgba(212, 175, 55, 0.2),
+ 0 8px 32px rgba(102, 187, 106, 0.2),
inset 0 1px 0 rgba(255, 255, 255, 0.1);
position: relative;
overflow: hidden;
@@ -78,7 +78,7 @@ body {
left: -2px;
right: -2px;
bottom: -2px;
- background: linear-gradient(45deg, #ffd700, #ffed4e, #ffd700, #ffed4e);
+ background: linear-gradient(45deg, #81c784, #a5d6a7, #81c784, #a5d6a7);
border-radius: 22px;
z-index: -1;
animation: borderGlow 4s linear infinite;
@@ -88,7 +88,7 @@ body {
.loading {
display: none;
text-align: center;
- color: #d4af37;
+ color: #2e7d32;
}
.loading.show {
@@ -98,8 +98,8 @@ body {
.loading-spinner {
width: 40px;
height: 40px;
- border: 4px solid rgba(212, 175, 55, 0.3);
- border-top: 4px solid #d4af37;
+ border: 4px solid rgba(102, 187, 106, 0.3);
+ border-top: 4px solid #2e7d32;
border-radius: 50%;
margin: 0 auto 15px;
animation: spin 1s linear infinite;
@@ -118,15 +118,15 @@ body {
.quote-text {
font-size: 1.8rem;
line-height: 1.8;
- color: #2c1810;
+ color: #2e7d32;
margin-bottom: 20px;
- text-shadow: 0 1px 2px rgba(212, 175, 55, 0.1);
+ text-shadow: 0 1px 2px rgba(102, 187, 106, 0.1);
font-weight: 500;
}
.quote-index {
font-size: 0.9rem;
- color: #b8860b;
+ color: #388e3c;
opacity: 0.8;
}
@@ -134,7 +134,7 @@ body {
.error-message {
display: none;
text-align: center;
- color: #cd853f;
+ color: #66bb6a;
}
.error-message.show {
@@ -157,20 +157,20 @@ body {
}
.refresh-btn {
- background: linear-gradient(135deg, #ffd700, #ffed4e);
+ background: linear-gradient(135deg, #81c784, #a5d6a7);
border: none;
border-radius: 50px;
padding: 15px 30px;
font-size: 1.1rem;
font-weight: 600;
- color: #2c1810;
+ color: #2e7d32;
cursor: pointer;
display: inline-flex;
align-items: center;
gap: 10px;
transition: all 0.3s ease;
box-shadow:
- 0 4px 15px rgba(212, 175, 55, 0.3),
+ 0 4px 15px rgba(102, 187, 106, 0.3),
inset 0 1px 0 rgba(255, 255, 255, 0.3);
position: relative;
overflow: hidden;
@@ -179,7 +179,7 @@ body {
.refresh-btn:hover {
transform: translateY(-2px);
box-shadow:
- 0 6px 20px rgba(212, 175, 55, 0.4),
+ 0 6px 20px rgba(102, 187, 106, 0.4),
inset 0 1px 0 rgba(255, 255, 255, 0.3);
}
@@ -206,7 +206,7 @@ body {
.footer {
margin-top: 40px;
text-align: center;
- color: #b8860b;
+ color: #388e3c;
opacity: 0.8;
font-size: 0.9rem;
}
@@ -215,15 +215,15 @@ body {
@keyframes titleGlow {
0% {
text-shadow:
- 0 0 10px rgba(212, 175, 55, 0.8),
- 0 0 20px rgba(212, 175, 55, 0.6),
- 0 0 30px rgba(212, 175, 55, 0.4);
+ 0 0 10px rgba(129, 199, 132, 0.8),
+ 0 0 20px rgba(129, 199, 132, 0.6),
+ 0 0 30px rgba(129, 199, 132, 0.4);
}
100% {
text-shadow:
- 0 0 15px rgba(212, 175, 55, 1),
- 0 0 25px rgba(212, 175, 55, 0.8),
- 0 0 35px rgba(212, 175, 55, 0.6);
+ 0 0 15px rgba(129, 199, 132, 1),
+ 0 0 25px rgba(129, 199, 132, 0.8),
+ 0 0 35px rgba(129, 199, 132, 0.6);
}
}
diff --git a/InfoGenie-frontend/public/60sapi/娱乐消遣/随机搞笑段子/css/background.css b/InfoGenie-frontend/public/60sapi/娱乐消遣/随机搞笑段子/css/background.css
index 7f1aa01c..38d40031 100755
--- a/InfoGenie-frontend/public/60sapi/娱乐消遣/随机搞笑段子/css/background.css
+++ b/InfoGenie-frontend/public/60sapi/娱乐消遣/随机搞笑段子/css/background.css
@@ -6,9 +6,9 @@ body {
transition: background 0.5s ease;
}
-/* Hand-drawn Comic Theme Background - NEW VIBRANT VERSION */
+/* Hand-drawn Comic Theme Background - FRESH GREEN VERSION */
body.theme-comic {
- background: linear-gradient(-45deg, #ff7e5f, #feb47b, #ffcc80, #ffecb3);
+ background: linear-gradient(-45deg, #c8e6c9, #dcedc8, #f1f8e9, #e8f5e8);
background-size: 400% 400%;
animation: gradientBG 15s ease infinite;
}
diff --git a/InfoGenie-frontend/public/60sapi/娱乐消遣/随机搞笑段子/css/style.css b/InfoGenie-frontend/public/60sapi/娱乐消遣/随机搞笑段子/css/style.css
index 5dccdee4..d8e4fa1e 100755
--- a/InfoGenie-frontend/public/60sapi/娱乐消遣/随机搞笑段子/css/style.css
+++ b/InfoGenie-frontend/public/60sapi/娱乐消遣/随机搞笑段子/css/style.css
@@ -33,7 +33,7 @@
border: 2px solid transparent;
}
.theme-icon.active {
- border-color: #ff7043;
+ border-color: #66bb6a;
transform: scale(1.1);
}
@@ -41,26 +41,26 @@
.theme-comic header h1 {
font-family: 'Zhi Mang Xing', cursive;
font-size: 4em;
- color: #d84315; /* Deep Orange */
+ color: #2e7d32; /* Fresh Green */
text-shadow: 2px 2px 0 #fff;
margin: 0.2em 0;
}
.theme-comic .divider {
height: 3px;
- background: linear-gradient(90deg, #ffca28, #ff7043, #29b6f6, #66bb6a);
+ background: linear-gradient(90deg, #81c784, #a5d6a7, #c8e6c9, #66bb6a);
border-radius: 3px;
margin: 20px auto;
width: 80%;
}
.theme-comic .joke-card {
- background: rgba(255, 255, 255, 0.85); /* White with transparency */
+ background: rgba(248, 255, 248, 0.9); /* Light green tinted white */
backdrop-filter: blur(5px);
border-radius: 15px;
padding: 40px;
min-height: 200px;
- box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
+ box-shadow: 0 8px 25px rgba(102, 187, 106, 0.15);
display: flex;
justify-content: center;
align-items: center;
@@ -68,6 +68,7 @@
margin-bottom: 20px;
transform: rotate(-1deg);
transition: transform 0.2s ease;
+ border: 1px solid rgba(129, 199, 132, 0.3);
}
.theme-comic .joke-card:hover {
transform: rotate(1deg) scale(1.02);
@@ -77,11 +78,11 @@
font-family: 'Zhi Mang Xing', cursive;
font-size: 2em;
line-height: 1.6;
- color: #5d4037;
+ color: #1b5e20;
}
.theme-comic .new-joke-btn {
- background: #1e88e5; /* Vibrant Blue */
+ background: linear-gradient(135deg, #66bb6a, #81c784); /* Fresh Green Gradient */
color: white;
font-family: 'Zhi Mang Xing', cursive;
font-size: 2.5em;
@@ -89,7 +90,7 @@
border-radius: 50px;
padding: 10px 30px;
cursor: pointer;
- box-shadow: 0 5px 0 #1565c0; /* Darker Blue */
+ box-shadow: 0 5px 0 #388e3c; /* Darker Green */
transition: all 0.1s ease-in-out;
}
.theme-comic .new-joke-btn:active {
@@ -120,8 +121,8 @@
margin-top: -27.5px;
}
.book-page {
- background: #ffca28;
- border: 1px solid #ff7043;
+ background: #a5d6a7;
+ border: 1px solid #66bb6a;
border-radius: 3px;
transform-origin: left;
}
diff --git a/InfoGenie-frontend/public/60sapi/娱乐消遣/随机搞笑段子/index.html b/InfoGenie-frontend/public/60sapi/娱乐消遣/随机搞笑段子/index.html
index e443b252..833f79da 100755
--- a/InfoGenie-frontend/public/60sapi/娱乐消遣/随机搞笑段子/index.html
+++ b/InfoGenie-frontend/public/60sapi/娱乐消遣/随机搞笑段子/index.html
@@ -11,8 +11,6 @@
diff --git a/InfoGenie-frontend/public/60sapi/娱乐消遣/随机答案之书/css/style.css b/InfoGenie-frontend/public/60sapi/娱乐消遣/随机答案之书/css/style.css
new file mode 100644
index 00000000..c218a495
--- /dev/null
+++ b/InfoGenie-frontend/public/60sapi/娱乐消遣/随机答案之书/css/style.css
@@ -0,0 +1,163 @@
+/* 随机答案之书 - 淡绿色清新风格样式(与随机唱歌音频一致) */
+
+/* 重置样式 */
+* {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+}
+
+body {
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
+ background: linear-gradient(135deg, #a8e6cf 0%, #dcedc1 50%, #ffd3a5 100%);
+ min-height: 100vh;
+ color: #2d5016;
+ line-height: 1.6;
+ overflow-x: hidden;
+}
+
+.container {
+ max-width: 900px;
+ margin: 0 auto;
+ padding: 20px;
+}
+
+/* 头部 */
+.header {
+ text-align: center;
+ margin-bottom: 20px;
+ background: rgba(255, 255, 255, 0.85);
+ border-radius: 20px;
+ padding: 24px;
+ box-shadow: 0 8px 25px rgba(45, 80, 22, 0.08);
+ backdrop-filter: blur(10px);
+}
+
+.header h1 {
+ font-size: 2rem;
+ color: #2d5016;
+ margin-bottom: 10px;
+ font-weight: 700;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 12px;
+}
+
+.header p {
+ color: #5a7c65;
+ font-size: 1rem;
+}
+
+/* 按钮 */
+.btn {
+ background: linear-gradient(135deg, #81c784 0%, #66bb6a 100%);
+ color: white;
+ border: none;
+ padding: 10px 18px;
+ border-radius: 10px;
+ font-size: 0.95rem;
+ font-weight: 600;
+ cursor: pointer;
+ transition: all 0.25s ease;
+ box-shadow: 0 4px 12px rgba(129, 199, 132, 0.35);
+ text-decoration: none;
+}
+
+.btn:hover {
+ transform: translateY(-2px);
+ box-shadow: 0 6px 18px rgba(129, 199, 132, 0.45);
+}
+
+/* 加载与错误 */
+.loading, .error {
+ text-align: center;
+ padding: 30px;
+ background: rgba(255, 255, 255, 0.85);
+ border-radius: 15px;
+ box-shadow: 0 5px 20px rgba(45, 80, 22, 0.08);
+}
+
+.spinner {
+ width: 36px;
+ height: 36px;
+ border: 4px solid #e8f5e8;
+ border-top: 4px solid #81c784;
+ border-radius: 50%;
+ animation: spin 1s linear infinite;
+ margin: 0 auto 18px;
+}
+
+@keyframes spin {
+ 0% { transform: rotate(0deg); }
+ 100% { transform: rotate(360deg); }
+}
+
+/* 动画 */
+.fade-in {
+ animation: fadeIn 0.5s ease-in-out;
+}
+
+@keyframes fadeIn {
+ from { opacity: 0; transform: translateY(10px); }
+ to { opacity: 1; transform: translateY(0); }
+}
+
+/* 答案卡片 */
+.answer-card {
+ background: rgba(255, 255, 255, 0.9);
+ padding: 16px;
+ border-radius: 15px;
+ box-shadow: 0 4px 18px rgba(45, 80, 22, 0.08);
+ margin-bottom: 15px;
+ text-align: center;
+}
+
+.answer-text {
+ font-size: 1.4rem;
+ font-weight: 700;
+ margin-bottom: 8px;
+ color: #1b5e20;
+ word-break: break-word;
+}
+
+.answer-en {
+ color: #5a7c65;
+ font-size: 1rem;
+ margin-bottom: 10px;
+}
+
+.meta {
+ color: #5a7c65;
+ font-size: 0.95rem;
+}
+
+.actions {
+ display: flex;
+ gap: 12px;
+ align-items: center;
+ justify-content: center;
+ margin-top: 12px;
+}
+
+/* 手机端优先优化 */
+@media (max-width: 767px) {
+ .container { padding: 12px; }
+ .header { padding: 18px; }
+ .header h1 { font-size: 1.6rem; gap: 8px; }
+
+ .answer-card { padding: 16px; }
+ .answer-text { font-size: 1.3rem; }
+ .answer-en { font-size: 0.95rem; }
+
+ .actions {
+ flex-direction: column;
+ gap: 15px;
+ }
+
+ .btn {
+ width: 100%;
+ max-width: 220px;
+ text-align: center;
+ }
+}
\ No newline at end of file
diff --git a/InfoGenie-frontend/public/60sapi/娱乐消遣/随机答案之书/index.html b/InfoGenie-frontend/public/60sapi/娱乐消遣/随机答案之书/index.html
new file mode 100644
index 00000000..aedfd38c
--- /dev/null
+++ b/InfoGenie-frontend/public/60sapi/娱乐消遣/随机答案之书/index.html
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
📘真理之道
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
+
编号:-
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/InfoGenie-frontend/public/60sapi/娱乐消遣/随机答案之书/js/script.js b/InfoGenie-frontend/public/60sapi/娱乐消遣/随机答案之书/js/script.js
new file mode 100644
index 00000000..3f27cd9f
--- /dev/null
+++ b/InfoGenie-frontend/public/60sapi/娱乐消遣/随机答案之书/js/script.js
@@ -0,0 +1,224 @@
+// 随机答案之书 页面脚本
+(function () {
+ 'use strict';
+
+ const API = {
+ endpoints: [],
+ currentIndex: 0,
+ params: {
+ encoding: 'json'
+ },
+ localFallback: '返回接口.json',
+ // 初始化API接口列表
+ async init() {
+ try {
+ const res = await fetch('./接口集合.json');
+ const endpoints = await res.json();
+ this.endpoints = endpoints.map(endpoint => `${endpoint}/v2/answer`);
+ } catch (e) {
+ // 如果无法加载接口集合,使用默认接口
+ this.endpoints = ['https://60s.api.shumengya.top/v2/answer'];
+ }
+ },
+ // 获取当前接口URL
+ getCurrentUrl() {
+ if (this.endpoints.length === 0) return null;
+ const url = new URL(this.endpoints[this.currentIndex]);
+ Object.entries(this.params).forEach(([k, v]) => url.searchParams.append(k, v));
+ return url.toString();
+ },
+ // 切换到下一个接口
+ switchToNext() {
+ this.currentIndex = (this.currentIndex + 1) % this.endpoints.length;
+ return this.currentIndex < this.endpoints.length;
+ },
+ // 重置到第一个接口
+ reset() {
+ this.currentIndex = 0;
+ }
+ };
+
+ // DOM 元素引用
+ const els = {
+ loading: null,
+ error: null,
+ container: null,
+ answer: null,
+ answerEn: null,
+ indexEl: null,
+ refreshBtn: null,
+ copyBtn: null,
+ };
+
+ function initDom() {
+ els.loading = document.getElementById('loading');
+ els.error = document.getElementById('error');
+ els.container = document.getElementById('content');
+
+ els.answer = document.getElementById('answer');
+ els.answerEn = document.getElementById('answer-en');
+ els.indexEl = document.getElementById('index');
+ els.refreshBtn = document.getElementById('refresh-btn');
+ els.copyBtn = document.getElementById('copy-btn');
+ }
+
+ function showLoading() {
+ els.loading.style.display = 'block';
+ els.error.style.display = 'none';
+ els.container.style.display = 'none';
+ }
+
+ function showError(msg) {
+ els.loading.style.display = 'none';
+ els.error.style.display = 'block';
+ els.container.style.display = 'none';
+ els.error.querySelector('p').textContent = msg || '获取数据失败,请稍后重试';
+ }
+
+ function showContent() {
+ els.loading.style.display = 'none';
+ els.error.style.display = 'none';
+ els.container.style.display = 'block';
+ }
+
+ function safeText(text) {
+ const div = document.createElement('div');
+ div.textContent = text == null ? '' : String(text);
+ return div.innerHTML;
+ }
+
+ async function fetchFromAPI() {
+ // 初始化API接口列表
+ await API.init();
+
+ // 重置API索引到第一个接口
+ API.reset();
+
+ // 尝试所有API接口
+ for (let i = 0; i < API.endpoints.length; i++) {
+ try {
+ const url = API.getCurrentUrl();
+ console.log(`尝试接口 ${i + 1}/${API.endpoints.length}: ${url}`);
+
+ const resp = await fetch(url, {
+ cache: 'no-store',
+ timeout: 10000 // 10秒超时(兼容同目录页面风格)
+ });
+
+ if (!resp.ok) {
+ throw new Error(`HTTP ${resp.status}: ${resp.statusText}`);
+ }
+
+ const data = await resp.json();
+
+ if (data && data.code === 200) {
+ console.log(`接口 ${i + 1} 请求成功`);
+ return data;
+ }
+
+ throw new Error(data && data.message ? data.message : '接口返回异常');
+
+ } catch (e) {
+ console.warn(`接口 ${i + 1} 失败:`, e.message);
+
+ // 如果不是最后一个接口,切换到下一个
+ if (i < API.endpoints.length - 1) {
+ API.switchToNext();
+ continue;
+ }
+
+ // 所有接口都失败了
+ console.warn('所有远程接口都失败,尝试本地数据');
+ return null;
+ }
+ }
+ }
+
+ async function fetchFromLocal() {
+ try {
+ const resp = await fetch(API.localFallback + `?t=${Date.now()}`);
+ if (!resp.ok) throw new Error(`本地文件HTTP ${resp.status}`);
+ const data = await resp.json();
+ return data;
+ } catch (e) {
+ console.error('读取本地返回接口.json失败:', e);
+ return null;
+ }
+ }
+
+ function render(data) {
+ const d = data?.data || {};
+
+ const cn = d.answer || '';
+ const en = d.answer_en || '';
+ const idx = d.index != null ? d.index : d.id != null ? d.id : '-';
+
+ els.answer.innerHTML = safeText(cn || '-');
+
+ if (en) {
+ els.answerEn.style.display = 'block';
+ els.answerEn.innerHTML = safeText(en);
+ } else {
+ els.answerEn.style.display = 'none';
+ els.answerEn.innerHTML = '';
+ }
+
+ els.indexEl.textContent = idx;
+ showContent();
+ }
+
+ async function load() {
+ showLoading();
+ try {
+ // 先尝试远程API
+ const data = await fetchFromAPI();
+ if (data) {
+ render(data);
+ return;
+ }
+
+ // 远程API失败,尝试本地数据
+ const localData = await fetchFromLocal();
+ if (localData) {
+ render(localData);
+ return;
+ }
+
+ // 都失败了
+ showError('获取数据失败,请稍后重试');
+ } catch (e) {
+ console.error('加载数据时发生错误:', e);
+ showError('获取数据失败,请稍后重试');
+ }
+ }
+
+ function bindEvents() {
+ if (els.refreshBtn) {
+ els.refreshBtn.addEventListener('click', load);
+ }
+ if (els.copyBtn) {
+ els.copyBtn.addEventListener('click', async () => {
+ const textParts = [];
+ const cn = els.answer?.textContent?.trim();
+ const en = els.answerEn?.textContent?.trim();
+ if (cn) textParts.push(cn);
+ if (en) textParts.push(en);
+ const finalText = textParts.join('\n');
+ try {
+ await navigator.clipboard.writeText(finalText);
+ const old = els.copyBtn.textContent;
+ els.copyBtn.textContent = '已复制';
+ setTimeout(() => { els.copyBtn.textContent = old; }, 1200);
+ } catch (e) {
+ alert('复制失败,请手动选择文本复制');
+ }
+ });
+ }
+ }
+
+ document.addEventListener('DOMContentLoaded', () => {
+ initDom();
+ bindEvents();
+ load();
+ });
+})();
\ No newline at end of file
diff --git a/InfoGenie-frontend/public/60sapi/娱乐消遣/随机答案之书/接口集合.json b/InfoGenie-frontend/public/60sapi/娱乐消遣/随机答案之书/接口集合.json
new file mode 100644
index 00000000..8ec2d2ec
--- /dev/null
+++ b/InfoGenie-frontend/public/60sapi/娱乐消遣/随机答案之书/接口集合.json
@@ -0,0 +1,3 @@
+[
+ "https://60s.api.shumengya.top"
+]
\ No newline at end of file
diff --git a/InfoGenie-frontend/public/60sapi/娱乐消遣/随机答案之书/返回接口.json b/InfoGenie-frontend/public/60sapi/娱乐消遣/随机答案之书/返回接口.json
new file mode 100644
index 00000000..73cbc640
--- /dev/null
+++ b/InfoGenie-frontend/public/60sapi/娱乐消遣/随机答案之书/返回接口.json
@@ -0,0 +1,10 @@
+{
+ "code": 200,
+ "message": "获取成功。数据来自官方/权威源头,以确保稳定与实时。开源地址 https://github.com/vikiboss/60s,反馈群 595941841",
+ "data": {
+ "id": "63",
+ "answer": "那不值得纠结",
+ "answer_en": "It's not worth worrying about",
+ "index": 62
+ }
+}
\ No newline at end of file
diff --git a/InfoGenie-frontend/public/60sapi/娱乐消遣/随机运势/css/background.css b/InfoGenie-frontend/public/60sapi/娱乐消遣/随机运势/css/background.css
index d18a8699..92d35e3c 100755
--- a/InfoGenie-frontend/public/60sapi/娱乐消遣/随机运势/css/background.css
+++ b/InfoGenie-frontend/public/60sapi/娱乐消遣/随机运势/css/background.css
@@ -1,8 +1,8 @@
body {
- background: linear-gradient(-45deg, #0a021a, #2a0d3f, #4a1a6c, #7b2f8f);
+ background: linear-gradient(-45deg, #f1f8e9, #e8f5e8, #c8e6c9, #dcedc8);
background-size: 400% 400%;
animation: gradientBG 20s ease infinite;
- color: #ffffff;
+ color: #2e7d32;
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
margin: 0;
padding: 0;
diff --git a/InfoGenie-frontend/public/60sapi/娱乐消遣/随机运势/css/style.css b/InfoGenie-frontend/public/60sapi/娱乐消遣/随机运势/css/style.css
index 7792be02..c3bec4c0 100755
--- a/InfoGenie-frontend/public/60sapi/娱乐消遣/随机运势/css/style.css
+++ b/InfoGenie-frontend/public/60sapi/娱乐消遣/随机运势/css/style.css
@@ -8,14 +8,14 @@
header h1 {
font-size: 2.8em;
- color: #f0e6ff;
- text-shadow: 0 0 10px #d1a9ff, 0 0 20px #d1a9ff;
+ color: #2e7d32;
+ text-shadow: 0 0 10px #81c784, 0 0 20px #a5d6a7;
margin-bottom: 0.2em;
}
header p {
font-size: 1.2em;
- color: #e0c8ff;
+ color: #388e3c;
margin-bottom: 40px;
}
@@ -27,11 +27,11 @@ header p {
.crystal-ball {
width: 200px;
height: 200px;
- background: radial-gradient(circle at 30% 30%, rgba(255, 255, 255, 0.6), rgba(200, 180, 255, 0.1));
+ background: radial-gradient(circle at 30% 30%, rgba(255, 255, 255, 0.6), rgba(200, 230, 201, 0.3));
border-radius: 50%;
margin: 0 auto;
position: relative;
- box-shadow: 0 0 30px #c390ff, 0 0 60px #a060e0, inset 0 0 20px rgba(255, 220, 255, 0.3);
+ box-shadow: 0 0 30px #81c784, 0 0 60px #66bb6a, inset 0 0 20px rgba(220, 255, 220, 0.3);
animation: float 6s ease-in-out infinite;
transform-style: preserve-3d;
}
@@ -54,7 +54,7 @@ header p {
left: 50%;
width: 120%;
height: 120%;
- background: linear-gradient(45deg, rgba(255, 192, 203, 0.1), rgba(128, 0, 128, 0.2));
+ background: linear-gradient(45deg, rgba(200, 230, 201, 0.2), rgba(129, 199, 132, 0.3));
border-radius: 50%;
animation: swirl 10s linear infinite;
transform: translate(-50%, -50%);
@@ -71,7 +71,7 @@ header p {
}
.fortune-card {
- background: rgba(255, 255, 255, 0.05);
+ background: rgba(248, 255, 248, 0.8);
border-radius: 15px;
padding: 30px;
margin-bottom: 30px;
@@ -80,8 +80,8 @@ header p {
justify-content: center;
align-items: center;
backdrop-filter: blur(10px);
- border: 1px solid rgba(255, 255, 255, 0.1);
- box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.37);
+ border: 1px solid rgba(129, 199, 132, 0.3);
+ box-shadow: 0 8px 32px 0 rgba(102, 187, 106, 0.2);
transition: opacity 0.5s ease-in-out;
}
@@ -96,13 +96,13 @@ header p {
#luck-desc {
font-size: 2em;
- color: #ffc0cb;
+ color: #2e7d32;
margin: 0 0 10px;
}
#luck-tip {
font-size: 1.1em;
- color: #e0e0e0;
+ color: #388e3c;
margin: 0;
padding-bottom: 20px; /* Add some space before the new details */
}
@@ -121,7 +121,7 @@ header p {
.detail-item h3 {
font-size: 0.9em;
- color: #ffc0cb;
+ color: #66bb6a;
margin: 0 0 5px;
font-weight: normal;
}
@@ -151,8 +151,8 @@ header p {
.tarot-container h2 {
font-size: 1.5em;
- color: #f0e6ff;
- text-shadow: 0 0 8px #d1a9ff;
+ color: #2e7d32;
+ text-shadow: 0 0 8px #81c784;
margin-bottom: 20px;
}
@@ -188,23 +188,23 @@ header p {
}
.tarot-card-back {
- background: linear-gradient(135deg, #4a1a6c, #2a0d3f);
- border: 2px solid #d1a9ff;
+ background: linear-gradient(135deg, #66bb6a, #388e3c);
+ border: 2px solid #81c784;
display: flex;
justify-content: center;
align-items: center;
font-size: 3em;
- color: #d1a9ff;
+ color: #c8e6c9;
}
.tarot-card-back::after {
content: '✧'; /* A simple star symbol */
- text-shadow: 0 0 10px #f0e6ff;
+ text-shadow: 0 0 10px #e8f5e8;
}
.tarot-card-front {
- background: linear-gradient(135deg, #3e165b, #592883);
- border: 2px solid #d1a9ff;
+ background: linear-gradient(135deg, #4caf50, #66bb6a);
+ border: 2px solid #81c784;
color: white;
transform: rotateY(180deg);
padding: 20px;
@@ -217,7 +217,7 @@ header p {
#tarot-name {
font-size: 1.4em;
- color: #ffc0cb;
+ color: #e8f5e8;
margin: 0 0 10px;
}
@@ -248,8 +248,8 @@ header p {
.decor-symbol {
position: absolute;
- color: rgba(209, 169, 255, 0.5);
- text-shadow: 0 0 10px rgba(240, 230, 255, 0.7);
+ color: rgba(129, 199, 132, 0.5);
+ text-shadow: 0 0 10px rgba(200, 230, 201, 0.7);
animation: floatSymbol 20s infinite ease-in-out;
}
@@ -268,7 +268,7 @@ header p {
}
#get-fortune-btn {
- background: linear-gradient(45deg, #da70d6, #8a2be2);
+ background: linear-gradient(45deg, #66bb6a, #4caf50);
color: white;
border: none;
border-radius: 50px;
@@ -276,12 +276,12 @@ header p {
font-size: 1.1em;
cursor: pointer;
transition: transform 0.2s, box-shadow 0.2s;
- box-shadow: 0 0 15px #c390ff;
+ box-shadow: 0 0 15px #81c784;
}
#get-fortune-btn:hover {
transform: scale(1.05);
- box-shadow: 0 0 25px #d1a9ff;
+ box-shadow: 0 0 25px #a5d6a7;
}
#get-fortune-btn:active {
@@ -289,8 +289,8 @@ header p {
}
.loading-spinner {
- border: 4px solid rgba(255, 255, 255, 0.2);
- border-left-color: #ffc0cb;
+ border: 4px solid rgba(129, 199, 132, 0.3);
+ border-left-color: #66bb6a;
border-radius: 50%;
width: 40px;
height: 40px;
@@ -308,7 +308,7 @@ header p {
footer {
margin-top: 40px;
- color: rgba(255, 255, 255, 0.6);
+ color: rgba(46, 125, 50, 0.7);
}
/* Responsive Design */
diff --git a/InfoGenie-frontend/public/60sapi/实用功能/公网IP地址/css/background.css b/InfoGenie-frontend/public/60sapi/实用功能/公网IP地址/css/background.css
index 74f65ebc..0da5d4ac 100755
--- a/InfoGenie-frontend/public/60sapi/实用功能/公网IP地址/css/background.css
+++ b/InfoGenie-frontend/public/60sapi/实用功能/公网IP地址/css/background.css
@@ -6,7 +6,7 @@ body::before {
left: 0;
width: 100%;
height: 100%;
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+ background: linear-gradient(135deg, #f0f8e8 0%, #e8f5e8 50%, #d4f4dd 100%);
z-index: -2;
}
@@ -18,9 +18,9 @@ body::after {
width: 100%;
height: 100%;
background:
- radial-gradient(circle at 20% 80%, rgba(120, 119, 198, 0.3) 0%, transparent 50%),
- radial-gradient(circle at 80% 20%, rgba(255, 119, 198, 0.3) 0%, transparent 50%),
- radial-gradient(circle at 40% 40%, rgba(120, 219, 255, 0.3) 0%, transparent 50%);
+ radial-gradient(circle at 20% 80%, rgba(144, 238, 144, 0.2) 0%, transparent 50%),
+ radial-gradient(circle at 80% 20%, rgba(173, 255, 173, 0.2) 0%, transparent 50%),
+ radial-gradient(circle at 40% 40%, rgba(152, 251, 152, 0.2) 0%, transparent 50%);
z-index: -1;
animation: backgroundMove 20s ease-in-out infinite;
}
@@ -28,27 +28,27 @@ body::after {
@keyframes backgroundMove {
0%, 100% {
background:
- radial-gradient(circle at 20% 80%, rgba(120, 119, 198, 0.3) 0%, transparent 50%),
- radial-gradient(circle at 80% 20%, rgba(255, 119, 198, 0.3) 0%, transparent 50%),
- radial-gradient(circle at 40% 40%, rgba(120, 219, 255, 0.3) 0%, transparent 50%);
+ radial-gradient(circle at 20% 80%, rgba(144, 238, 144, 0.2) 0%, transparent 50%),
+ radial-gradient(circle at 80% 20%, rgba(173, 255, 173, 0.2) 0%, transparent 50%),
+ radial-gradient(circle at 40% 40%, rgba(152, 251, 152, 0.2) 0%, transparent 50%);
}
25% {
background:
- radial-gradient(circle at 60% 30%, rgba(120, 119, 198, 0.3) 0%, transparent 50%),
- radial-gradient(circle at 30% 70%, rgba(255, 119, 198, 0.3) 0%, transparent 50%),
- radial-gradient(circle at 80% 80%, rgba(120, 219, 255, 0.3) 0%, transparent 50%);
+ radial-gradient(circle at 60% 30%, rgba(144, 238, 144, 0.2) 0%, transparent 50%),
+ radial-gradient(circle at 30% 70%, rgba(173, 255, 173, 0.2) 0%, transparent 50%),
+ radial-gradient(circle at 80% 80%, rgba(152, 251, 152, 0.2) 0%, transparent 50%);
}
50% {
background:
- radial-gradient(circle at 80% 60%, rgba(120, 119, 198, 0.3) 0%, transparent 50%),
- radial-gradient(circle at 20% 30%, rgba(255, 119, 198, 0.3) 0%, transparent 50%),
- radial-gradient(circle at 60% 70%, rgba(120, 219, 255, 0.3) 0%, transparent 50%);
+ radial-gradient(circle at 80% 60%, rgba(144, 238, 144, 0.2) 0%, transparent 50%),
+ radial-gradient(circle at 20% 30%, rgba(173, 255, 173, 0.2) 0%, transparent 50%),
+ radial-gradient(circle at 60% 70%, rgba(152, 251, 152, 0.2) 0%, transparent 50%);
}
75% {
background:
- radial-gradient(circle at 40% 90%, rgba(120, 119, 198, 0.3) 0%, transparent 50%),
- radial-gradient(circle at 70% 10%, rgba(255, 119, 198, 0.3) 0%, transparent 50%),
- radial-gradient(circle at 20% 60%, rgba(120, 219, 255, 0.3) 0%, transparent 50%);
+ radial-gradient(circle at 40% 90%, rgba(144, 238, 144, 0.2) 0%, transparent 50%),
+ radial-gradient(circle at 70% 10%, rgba(173, 255, 173, 0.2) 0%, transparent 50%),
+ radial-gradient(circle at 20% 60%, rgba(152, 251, 152, 0.2) 0%, transparent 50%);
}
}
diff --git a/InfoGenie-frontend/public/60sapi/实用功能/公网IP地址/css/style.css b/InfoGenie-frontend/public/60sapi/实用功能/公网IP地址/css/style.css
index 30d4768f..4ef98078 100755
--- a/InfoGenie-frontend/public/60sapi/实用功能/公网IP地址/css/style.css
+++ b/InfoGenie-frontend/public/60sapi/实用功能/公网IP地址/css/style.css
@@ -11,6 +11,22 @@ body {
color: #333;
min-height: 100vh;
overflow-x: hidden;
+ /* 隐藏滚动条但保留滚动功能 */
+ scrollbar-width: none; /* Firefox */
+ -ms-overflow-style: none; /* IE and Edge */
+}
+
+/* 隐藏 Webkit 浏览器的滚动条 */
+body::-webkit-scrollbar,
+html::-webkit-scrollbar,
+*::-webkit-scrollbar {
+ display: none;
+}
+
+/* 全局隐藏滚动条但保留滚动功能 */
+html {
+ scrollbar-width: none; /* Firefox */
+ -ms-overflow-style: none; /* IE and Edge */
}
/* 容器样式 */
@@ -26,7 +42,7 @@ body {
.header {
text-align: center;
padding: 3rem 2rem 2rem;
- background: linear-gradient(135deg, rgba(74, 144, 226, 0.1), rgba(80, 200, 120, 0.1));
+ background: linear-gradient(135deg, rgba(144, 238, 144, 0.15), rgba(152, 251, 152, 0.15));
backdrop-filter: blur(10px);
border-bottom: 1px solid rgba(255, 255, 255, 0.2);
}
@@ -34,7 +50,7 @@ body {
.header h1 {
font-size: 2.5rem;
font-weight: 700;
- background: linear-gradient(135deg, #4a90e2, #50c878);
+ background: linear-gradient(135deg, #228B22, #32CD32);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
@@ -44,7 +60,7 @@ body {
.header h1 i {
margin-right: 0.5rem;
- background: linear-gradient(135deg, #4a90e2, #50c878);
+ background: linear-gradient(135deg, #228B22, #32CD32);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
@@ -72,7 +88,7 @@ body {
}
.query-btn {
- background: linear-gradient(135deg, #4a90e2, #50c878);
+ background: linear-gradient(135deg, #228B22, #32CD32);
color: white;
border: none;
padding: 1rem 2rem;
@@ -81,7 +97,7 @@ body {
border-radius: 50px;
cursor: pointer;
transition: all 0.3s ease;
- box-shadow: 0 4px 15px rgba(74, 144, 226, 0.3);
+ box-shadow: 0 4px 15px rgba(34, 139, 34, 0.3);
display: inline-flex;
align-items: center;
gap: 0.5rem;
@@ -91,8 +107,8 @@ body {
.query-btn:hover {
transform: translateY(-2px);
- box-shadow: 0 6px 20px rgba(74, 144, 226, 0.4);
- background: linear-gradient(135deg, #3a7bc8, #40a868);
+ box-shadow: 0 6px 20px rgba(34, 139, 34, 0.4);
+ background: linear-gradient(135deg, #1e7e1e, #2eb82e);
}
.query-btn:active {
diff --git a/InfoGenie-frontend/public/60sapi/实用功能/公网IP地址/index.html b/InfoGenie-frontend/public/60sapi/实用功能/公网IP地址/index.html
index 875fb9f0..88d7ae88 100755
--- a/InfoGenie-frontend/public/60sapi/实用功能/公网IP地址/index.html
+++ b/InfoGenie-frontend/public/60sapi/实用功能/公网IP地址/index.html
@@ -51,11 +51,6 @@
查询时间:
--
-
-
- 数据来源:
- 60s.viki.moe
-
位置信息:
@@ -129,9 +124,6 @@
-
diff --git a/InfoGenie-frontend/public/60sapi/实用功能/农历信息/css/background.css b/InfoGenie-frontend/public/60sapi/实用功能/农历信息/css/background.css
index 3454ea71..4b346b4f 100755
--- a/InfoGenie-frontend/public/60sapi/实用功能/农历信息/css/background.css
+++ b/InfoGenie-frontend/public/60sapi/实用功能/农历信息/css/background.css
@@ -1,18 +1,33 @@
-/* 农历主题背景样式 - 柔和版本 */
+/* 全局滚动条隐藏样式 */
+html, body {
+ scrollbar-width: none; /* Firefox */
+ -ms-overflow-style: none; /* IE/Edge */
+}
+
+html::-webkit-scrollbar,
+body::-webkit-scrollbar,
+*::-webkit-scrollbar {
+ display: none; /* Webkit浏览器 */
+}
+
+/* 农历主题背景样式 - 淡黄绿色到淡绿色清新渐变 */
body {
background: linear-gradient(135deg,
- #f8f9fa 0%, /* 浅灰白 */
- #fff3e0 20%, /* 淡橙色 */
- #fef7e0 40%, /* 极淡黄 */
- #f3e5ab 60%, /* 柔和金色 */
- #e8dcc6 80%, /* 米色 */
- #f8f9fa 100% /* 浅灰白 */
+ #f0f8e8 0%, /* 淡黄绿色 */
+ #e8f5e8 20%, /* 浅绿色 */
+ #d4f4dd 40%, /* 淡绿色 */
+ #c8f2d4 60%, /* 清新绿色 */
+ #b8f0c8 80%, /* 柔和绿色 */
+ #e8f5e8 100% /* 浅绿色 */
);
background-size: 400% 400%;
animation: gentleShift 30s ease infinite;
background-attachment: fixed;
min-height: 100vh;
position: relative;
+ /* 隐藏滚动条但保留滚动功能 */
+ scrollbar-width: none; /* Firefox */
+ -ms-overflow-style: none; /* IE/Edge */
}
@keyframes gentleShift {
@@ -23,7 +38,7 @@ body {
100% { background-position: 0% 50%; }
}
-/* 动态颜色调节系统 - 柔和版本 */
+/* 动态颜色调节系统 - 绿色主题版本 */
.adaptive-overlay {
position: fixed;
top: 0;
@@ -31,9 +46,9 @@ body {
width: 100%;
height: 100%;
background:
- radial-gradient(circle at 20% 30%, rgba(255, 255, 255, 0.3) 0%, transparent 50%),
- radial-gradient(circle at 80% 70%, rgba(255, 255, 255, 0.25) 0%, transparent 50%),
- linear-gradient(45deg, rgba(255, 255, 255, 0.2) 0%, rgba(255, 255, 255, 0.3) 100%);
+ radial-gradient(circle at 20% 30%, rgba(200, 242, 212, 0.3) 0%, transparent 50%),
+ radial-gradient(circle at 80% 70%, rgba(184, 240, 200, 0.25) 0%, transparent 50%),
+ linear-gradient(45deg, rgba(232, 245, 232, 0.2) 0%, rgba(212, 244, 221, 0.3) 100%);
pointer-events: none;
z-index: 1;
animation: adaptiveShift 60s ease infinite;
@@ -42,33 +57,33 @@ body {
@keyframes adaptiveShift {
0% {
background:
- radial-gradient(circle at 20% 30%, rgba(255, 255, 255, 0.1) 0%, transparent 50%),
- radial-gradient(circle at 80% 70%, rgba(255, 255, 255, 0.15) 0%, transparent 50%),
- linear-gradient(45deg, rgba(255, 255, 255, 0.1) 0%, rgba(255, 255, 255, 0.2) 100%);
+ radial-gradient(circle at 20% 30%, rgba(232, 245, 232, 0.1) 0%, transparent 50%),
+ radial-gradient(circle at 80% 70%, rgba(212, 244, 221, 0.15) 0%, transparent 50%),
+ linear-gradient(45deg, rgba(240, 248, 232, 0.1) 0%, rgba(232, 245, 232, 0.2) 100%);
}
25% {
background:
- radial-gradient(circle at 70% 20%, rgba(255, 255, 255, 0.2) 0%, transparent 50%),
- radial-gradient(circle at 30% 80%, rgba(255, 255, 255, 0.1) 0%, transparent 50%),
- linear-gradient(135deg, rgba(255, 255, 255, 0.15) 0%, rgba(255, 255, 255, 0.25) 100%);
+ radial-gradient(circle at 70% 20%, rgba(200, 242, 212, 0.2) 0%, transparent 50%),
+ radial-gradient(circle at 30% 80%, rgba(184, 240, 200, 0.1) 0%, transparent 50%),
+ linear-gradient(135deg, rgba(212, 244, 221, 0.15) 0%, rgba(200, 242, 212, 0.25) 100%);
}
50% {
background:
- radial-gradient(circle at 50% 50%, rgba(255, 255, 255, 0.15) 0%, transparent 50%),
- radial-gradient(circle at 10% 90%, rgba(255, 255, 255, 0.12) 0%, transparent 50%),
- linear-gradient(225deg, rgba(255, 255, 255, 0.12) 0%, rgba(255, 255, 255, 0.22) 100%);
+ radial-gradient(circle at 50% 50%, rgba(220, 246, 228, 0.15) 0%, transparent 50%),
+ radial-gradient(circle at 10% 90%, rgba(232, 245, 232, 0.12) 0%, transparent 50%),
+ linear-gradient(225deg, rgba(240, 248, 232, 0.12) 0%, rgba(212, 244, 221, 0.22) 100%);
}
75% {
background:
- radial-gradient(circle at 90% 60%, rgba(255, 255, 255, 0.18) 0%, transparent 50%),
- radial-gradient(circle at 40% 10%, rgba(255, 255, 255, 0.08) 0%, transparent 50%),
- linear-gradient(315deg, rgba(255, 255, 255, 0.1) 0%, rgba(255, 255, 255, 0.2) 100%);
+ radial-gradient(circle at 90% 60%, rgba(184, 240, 200, 0.18) 0%, transparent 50%),
+ radial-gradient(circle at 40% 10%, rgba(240, 248, 232, 0.08) 0%, transparent 50%),
+ linear-gradient(315deg, rgba(232, 245, 232, 0.1) 0%, rgba(200, 242, 212, 0.2) 100%);
}
100% {
background:
- radial-gradient(circle at 20% 30%, rgba(255, 255, 255, 0.1) 0%, transparent 50%),
- radial-gradient(circle at 80% 70%, rgba(255, 255, 255, 0.15) 0%, transparent 50%),
- linear-gradient(45deg, rgba(255, 255, 255, 0.1) 0%, rgba(255, 255, 255, 0.2) 100%);
+ radial-gradient(circle at 20% 30%, rgba(232, 245, 232, 0.1) 0%, transparent 50%),
+ radial-gradient(circle at 80% 70%, rgba(212, 244, 221, 0.15) 0%, transparent 50%),
+ linear-gradient(45deg, rgba(240, 248, 232, 0.1) 0%, rgba(232, 245, 232, 0.2) 100%);
}
}
diff --git a/InfoGenie-frontend/public/60sapi/实用功能/农历信息/css/style.css b/InfoGenie-frontend/public/60sapi/实用功能/农历信息/css/style.css
index 38abe022..4004990d 100755
--- a/InfoGenie-frontend/public/60sapi/实用功能/农历信息/css/style.css
+++ b/InfoGenie-frontend/public/60sapi/实用功能/农历信息/css/style.css
@@ -266,12 +266,12 @@ body {
.date-input:focus {
outline: none;
- border-color: rgba(255, 255, 255, 0.5);
- background: rgba(255, 255, 255, 0.15);
+ border-color: #228B22;
+ background: rgba(255, 255, 255, 0.95);
box-shadow:
- 0 6px 20px rgba(31, 38, 135, 0.2),
- inset 0 1px 0 rgba(255, 255, 255, 0.3),
- 0 0 0 3px rgba(255, 255, 255, 0.1);
+ 0 6px 20px rgba(34, 139, 34, 0.2),
+ inset 0 1px 0 rgba(255, 255, 255, 0.8),
+ 0 0 0 3px rgba(34, 139, 34, 0.1);
transform: translateY(-2px);
}
@@ -282,11 +282,11 @@ body {
}
.query-btn {
- background: linear-gradient(135deg, #f0f0f0, #e0e0e0);
+ background: linear-gradient(135deg, #228B22 0%, #32CD32 100%);
backdrop-filter: blur(15px);
-webkit-backdrop-filter: blur(15px);
- color: #1a1a1a;
- border: 1px solid rgba(0, 0, 0, 0.2);
+ color: #ffffff;
+ border: 1px solid rgba(34, 139, 34, 0.3);
padding: 12px 28px;
border-radius: 20px;
cursor: pointer;
@@ -298,10 +298,10 @@ body {
gap: 8px;
position: relative;
overflow: hidden;
- text-shadow: 0 1px 3px rgba(255, 255, 255, 0.8);
+ text-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
box-shadow:
- 0 4px 15px rgba(0, 0, 0, 0.1),
- inset 0 1px 0 rgba(255, 255, 255, 0.8);
+ 0 4px 15px rgba(34, 139, 34, 0.3),
+ inset 0 1px 0 rgba(255, 255, 255, 0.2);
}
/* 移除按钮颜色动画,保持稳定的可读性 */
@@ -326,12 +326,12 @@ body {
}
.query-btn:hover {
- background: linear-gradient(135deg, #e8e8e8, #d8d8d8);
- border-color: rgba(0, 0, 0, 0.3);
+ background: linear-gradient(135deg, #1e7e1e, #2eb82e);
+ border-color: rgba(34, 139, 34, 0.5);
transform: translateY(-2px);
box-shadow:
- 0 8px 25px rgba(0, 0, 0, 0.15),
- inset 0 1px 0 rgba(255, 255, 255, 0.9);
+ 0 8px 25px rgba(34, 139, 34, 0.4),
+ inset 0 1px 0 rgba(255, 255, 255, 0.3);
}
.btn-icon {
diff --git a/InfoGenie-frontend/public/60sapi/实用功能/农历信息/index.html b/InfoGenie-frontend/public/60sapi/实用功能/农历信息/index.html
index 6744ddec..d66d4d7c 100755
--- a/InfoGenie-frontend/public/60sapi/实用功能/农历信息/index.html
+++ b/InfoGenie-frontend/public/60sapi/实用功能/农历信息/index.html
@@ -3,7 +3,7 @@
- 🌙 农历信息查询
+ 🌙农历信息查询
@@ -48,8 +48,7 @@
diff --git a/InfoGenie-frontend/public/60sapi/实用功能/哈希解压压缩/css/style.css b/InfoGenie-frontend/public/60sapi/实用功能/哈希解压压缩/css/style.css
index 4b94a03d..98183afc 100755
--- a/InfoGenie-frontend/public/60sapi/实用功能/哈希解压压缩/css/style.css
+++ b/InfoGenie-frontend/public/60sapi/实用功能/哈希解压压缩/css/style.css
@@ -7,10 +7,26 @@
body {
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+ background: linear-gradient(135deg, #f0f8e8 0%, #e8f5e8 50%, #d4f4dd 100%);
min-height: 100vh;
color: #333;
overflow-x: hidden;
+ /* 隐藏滚动条但保留滚动功能 */
+ scrollbar-width: none; /* Firefox */
+ -ms-overflow-style: none; /* IE and Edge */
+}
+
+/* 隐藏 Webkit 浏览器的滚动条 */
+body::-webkit-scrollbar,
+html::-webkit-scrollbar,
+*::-webkit-scrollbar {
+ display: none;
+}
+
+/* 全局隐藏滚动条但保留滚动功能 */
+html {
+ scrollbar-width: none; /* Firefox */
+ -ms-overflow-style: none; /* IE and Edge */
}
.container {
@@ -65,7 +81,7 @@ body {
.logo i {
font-size: 48px;
- background: linear-gradient(45deg, #ff6b6b, #4ecdc4);
+ background: linear-gradient(45deg, #228B22, #32CD32);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
@@ -174,7 +190,7 @@ body {
.card-header i {
font-size: 24px;
- color: #667eea;
+ color: #228B22;
}
.card-header h2 {
@@ -202,8 +218,8 @@ body {
#inputText:focus {
outline: none;
- border-color: #667eea;
- box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
+ border-color: #228B22;
+ box-shadow: 0 0 0 3px rgba(34, 139, 34, 0.1);
background: rgba(255, 255, 255, 0.95);
}
@@ -247,14 +263,14 @@ body {
}
.btn-primary {
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+ background: linear-gradient(135deg, #228B22 0%, #32CD32 100%);
color: white;
- box-shadow: 0 4px 15px rgba(102, 126, 234, 0.3);
+ box-shadow: 0 4px 15px rgba(34, 139, 34, 0.3);
}
.btn-primary:hover {
transform: translateY(-2px);
- box-shadow: 0 8px 25px rgba(102, 126, 234, 0.4);
+ box-shadow: 0 8px 25px rgba(34, 139, 34, 0.4);
}
.btn-secondary {
@@ -306,7 +322,7 @@ body {
left: 0;
right: 0;
height: 4px;
- background: linear-gradient(90deg, #ff6b6b, #4ecdc4, #45b7d1, #96ceb4);
+ background: linear-gradient(90deg, #228B22, #32CD32, #90EE90, #98FB98);
}
.result-card:hover {
@@ -356,7 +372,7 @@ body {
.result-value:hover {
background: rgba(248, 250, 252, 0.95);
- border-color: #667eea;
+ border-color: #228B22;
}
.result-value .placeholder {
@@ -367,7 +383,7 @@ body {
.copy-btn {
background: none;
border: none;
- color: #667eea;
+ color: #228B22;
cursor: pointer;
padding: 8px;
border-radius: 6px;
@@ -377,8 +393,8 @@ body {
}
.copy-btn:hover {
- background: rgba(102, 126, 234, 0.1);
- color: #5a67d8;
+ background: rgba(34, 139, 34, 0.1);
+ color: #1e7e1e;
}
/* Loading Overlay */
@@ -429,7 +445,7 @@ body {
position: fixed;
bottom: 30px;
right: 30px;
- background: linear-gradient(135deg, #4ecdc4, #44a08d);
+ background: linear-gradient(135deg, #228B22, #32CD32);
color: white;
padding: 16px 24px;
border-radius: 12px;
diff --git a/InfoGenie-frontend/public/60sapi/实用功能/密码强度检测/css/style.css b/InfoGenie-frontend/public/60sapi/实用功能/密码强度检测/css/style.css
index 858e4bea..563b0930 100755
--- a/InfoGenie-frontend/public/60sapi/实用功能/密码强度检测/css/style.css
+++ b/InfoGenie-frontend/public/60sapi/实用功能/密码强度检测/css/style.css
@@ -5,12 +5,27 @@
box-sizing: border-box;
}
+/* 隐藏滚动条但保留滚动功能 */
+html {
+ scrollbar-width: none;
+ -ms-overflow-style: none;
+}
+
+body::-webkit-scrollbar,
+html::-webkit-scrollbar,
+*::-webkit-scrollbar {
+ display: none;
+}
+
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', sans-serif;
line-height: 1.6;
color: #2c3e50;
min-height: 100vh;
overflow-x: hidden;
+ background: linear-gradient(135deg, #f0f8e8 0%, #e8f5e8 50%, #d4f4dd 100%);
+ scrollbar-width: none;
+ -ms-overflow-style: none;
}
/* 容器布局 */
@@ -28,9 +43,9 @@ body {
text-align: center;
margin-bottom: 40px;
padding: 40px 20px;
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+ background: linear-gradient(135deg, #228B22 0%, #32CD32 100%);
border-radius: 20px;
- box-shadow: 0 10px 30px rgba(102, 126, 234, 0.3);
+ box-shadow: 0 10px 30px rgba(34, 139, 34, 0.3);
color: white;
}
@@ -95,9 +110,9 @@ body {
.password-input:focus {
outline: none;
- border-color: #667eea;
+ border-color: #228B22;
background: #ffffff;
- box-shadow: 0 0 0 4px rgba(102, 126, 234, 0.1);
+ box-shadow: 0 0 0 4px rgba(34, 139, 34, 0.1);
}
.password-input::placeholder {
@@ -140,7 +155,7 @@ body {
/* 检测按钮 */
.check-btn {
width: 100%;
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+ background: linear-gradient(135deg, #228B22 0%, #32CD32 100%);
color: white;
border: none;
padding: 18px 32px;
@@ -149,7 +164,7 @@ body {
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
- box-shadow: 0 4px 20px rgba(102, 126, 234, 0.3);
+ box-shadow: 0 4px 20px rgba(34, 139, 34, 0.3);
display: flex;
align-items: center;
justify-content: center;
@@ -160,7 +175,7 @@ body {
.check-btn:hover {
transform: translateY(-2px);
- box-shadow: 0 6px 25px rgba(102, 126, 234, 0.4);
+ box-shadow: 0 6px 25px rgba(34, 139, 34, 0.4);
}
.check-btn:active {
@@ -284,7 +299,7 @@ body {
.bar-fill {
height: 100%;
- background: linear-gradient(90deg, #ef4444, #f97316, #eab308, #22c55e);
+ background: linear-gradient(90deg, #90EE90, #98FB98, #32CD32, #228B22);
border-radius: 6px;
width: 0%;
transition: width 0.8s ease;
@@ -383,13 +398,13 @@ body {
}
.char-type.has-type {
- background: #dcfce7;
- border-color: #bbf7d0;
- color: #166534;
+ background: #f0f8e8;
+ border-color: #d4f4dd;
+ color: #1e7e1e;
}
.char-type.has-type .type-icon {
- color: #22c55e;
+ color: #228B22;
}
.type-icon {
@@ -555,11 +570,11 @@ body {
position: fixed;
top: 20px;
right: 20px;
- background: #22c55e;
+ background: #228B22;
color: white;
padding: 16px 24px;
border-radius: 10px;
- box-shadow: 0 4px 20px rgba(34, 197, 94, 0.3);
+ box-shadow: 0 4px 20px rgba(34, 139, 34, 0.3);
z-index: 1000;
animation: toastSlide 0.3s ease-out;
font-weight: 500;
@@ -586,11 +601,11 @@ body {
}
.strength-strong {
- color: #059669 !important;
+ color: #228B22 !important;
}
.strength-very-strong {
- color: #047857 !important;
+ color: #1e7e1e !important;
}
/* 分数圆圈颜色 */
@@ -603,11 +618,11 @@ body {
}
.score-strong {
- background: conic-gradient(from 0deg, #059669 0deg, #059669 var(--score-deg), #e2e8f0 var(--score-deg), #e2e8f0 360deg) !important;
+ background: conic-gradient(from 0deg, #228B22 0deg, #228B22 var(--score-deg), #e2e8f0 var(--score-deg), #e2e8f0 360deg) !important;
}
.score-very-strong {
- background: conic-gradient(from 0deg, #047857 0deg, #047857 var(--score-deg), #e2e8f0 var(--score-deg), #e2e8f0 360deg) !important;
+ background: conic-gradient(from 0deg, #1e7e1e 0deg, #1e7e1e var(--score-deg), #e2e8f0 var(--score-deg), #e2e8f0 360deg) !important;
}
/* 平板端适配 (768px - 1024px) */
diff --git a/InfoGenie-frontend/public/aimodelapp/AI中国亲戚称呼计算器/env.js b/InfoGenie-frontend/public/aimodelapp/AI中国亲戚称呼计算器/env.js
new file mode 100644
index 00000000..95bfb73d
--- /dev/null
+++ b/InfoGenie-frontend/public/aimodelapp/AI中国亲戚称呼计算器/env.js
@@ -0,0 +1,25 @@
+// 环境配置文件 - AI中国亲戚称呼计算器
+// 复用 InfoGenie 的全局 ENV_CONFIG,支持独立打开的回退地址
+
+const DEFAULT_API = (window.ENV_CONFIG && window.ENV_CONFIG.API_URL) || 'http://127.0.0.1:5002';
+
+window.API_CONFIG = {
+ baseUrl: window.parent?.ENV_CONFIG?.API_URL || DEFAULT_API,
+ endpoints: {
+ kinshipCalculator: '/api/aimodelapp/kinship-calculator'
+ }
+};
+
+window.AUTH_CONFIG = {
+ tokenKey: 'token',
+ getToken: () => localStorage.getItem('token'),
+ isAuthenticated: () => !!localStorage.getItem('token')
+};
+
+window.APP_CONFIG = {
+ name: 'InfoGenie 中国亲戚称呼计算器',
+ version: '1.0.0',
+ debug: false
+};
+
+console.log('中国亲戚称呼计算器 环境配置已加载');
\ No newline at end of file
diff --git a/InfoGenie-frontend/public/aimodelapp/AI中国亲戚称呼计算器/index.html b/InfoGenie-frontend/public/aimodelapp/AI中国亲戚称呼计算器/index.html
new file mode 100644
index 00000000..9e7bf811
--- /dev/null
+++ b/InfoGenie-frontend/public/aimodelapp/AI中国亲戚称呼计算器/index.html
@@ -0,0 +1,59 @@
+
+
+
+
+
+ 中国亲戚称呼计算器
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/InfoGenie-frontend/public/aimodelapp/AI中国亲戚称呼计算器/script.js b/InfoGenie-frontend/public/aimodelapp/AI中国亲戚称呼计算器/script.js
new file mode 100644
index 00000000..a71c1bd4
--- /dev/null
+++ b/InfoGenie-frontend/public/aimodelapp/AI中国亲戚称呼计算器/script.js
@@ -0,0 +1,135 @@
+// 环境与认证在 env.js 中定义
+
+const relationInput = document.getElementById('relationInput');
+const calcBtn = document.getElementById('calcBtn');
+const loadingDiv = document.getElementById('loading');
+const errorDiv = document.getElementById('error');
+const resultSection = document.getElementById('resultSection');
+const mandarinTitleEl = document.getElementById('mandarinTitle');
+const dialectListEl = document.getElementById('dialectList');
+const copyMandarinBtn = document.getElementById('copyMandarinBtn');
+const notesBlock = document.getElementById('notesBlock');
+const notesEl = document.getElementById('notes');
+
+function setExample(text) {
+ relationInput.value = text;
+}
+window.setExample = setExample;
+
+function showLoading(show) {
+ loadingDiv.style.display = show ? 'block' : 'none';
+ calcBtn.disabled = show;
+}
+
+function showError(msg) {
+ errorDiv.textContent = msg || '';
+ errorDiv.style.display = msg ? 'block' : 'none';
+}
+
+function clearResults() {
+ resultSection.style.display = 'none';
+ mandarinTitleEl.textContent = '';
+ dialectListEl.innerHTML = '';
+ notesBlock.style.display = 'none';
+ notesEl.textContent = '';
+}
+
+async function callKinshipAPI(relationChain) {
+ const token = window.AUTH_CONFIG.getToken();
+ if (!token) throw new Error('未登录,请先登录后使用AI功能');
+
+ const url = `${window.API_CONFIG.baseUrl}${window.API_CONFIG.endpoints.kinshipCalculator}`;
+ const resp = await fetch(url, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'Authorization': `Bearer ${token}`
+ },
+ body: JSON.stringify({ relation_chain: relationChain })
+ });
+
+ if (!resp.ok) {
+ if (resp.status === 402) throw new Error('您的萌芽币余额不足,无法使用此功能');
+ const err = await resp.json().catch(() => ({}));
+ throw new Error(err.error || `API请求失败: ${resp.status} ${resp.statusText}`);
+ }
+
+ const data = await resp.json();
+ if (!data.success) throw new Error(data.error || 'API响应异常');
+ return data;
+}
+
+function renderDialects(dialectTitles) {
+ dialectListEl.innerHTML = '';
+ const order = ['粤语','闽南语','上海话','四川话','东北话','客家话'];
+ const names = order.concat(Object.keys(dialectTitles || {}).filter(k => !order.includes(k)));
+
+ names.forEach(name => {
+ const info = dialectTitles?.[name];
+ if (!info || (!info.title && !info.romanization && !info.notes)) return;
+ const item = document.createElement('div');
+ item.className = 'dialect-item';
+ const title = (info.title || '').toString();
+ const roman = (info.romanization || '').toString();
+ const notes = (info.notes || '').toString();
+ item.innerHTML = `
+ ${name}
+ ${title}
+ ${roman ? `${roman}
` : ''}
+ ${notes ? `${notes}
` : ''}
+ `;
+ dialectListEl.appendChild(item);
+ });
+}
+
+async function doCalculate() {
+ const relation = (relationInput.value || '').trim();
+ if (!relation) {
+ showError('请输入亲属关系链');
+ return;
+ }
+ showError('');
+ showLoading(true);
+ clearResults();
+
+ try {
+ const data = await callKinshipAPI(relation);
+ mandarinTitleEl.textContent = data.mandarin_title || '';
+ renderDialects(data.dialect_titles || {});
+ if (data.notes) {
+ notesEl.textContent = data.notes;
+ notesBlock.style.display = 'block';
+ }
+ resultSection.style.display = 'block';
+ } catch (e) {
+ console.error('计算失败:', e);
+ showError(`计算失败: ${e.message}`);
+ } finally {
+ showLoading(false);
+ }
+}
+
+function copyText(text) {
+ try {
+ navigator.clipboard.writeText(text);
+ } catch (e) {
+ const ta = document.createElement('textarea');
+ ta.value = text;
+ document.body.appendChild(ta);
+ ta.select();
+ document.execCommand('copy');
+ document.body.removeChild(ta);
+ }
+}
+
+copyMandarinBtn.addEventListener('click', () => {
+ const t = mandarinTitleEl.textContent || '';
+ if (!t) return;
+ copyText(t);
+});
+
+calcBtn.addEventListener('click', doCalculate);
+
+document.addEventListener('DOMContentLoaded', () => {
+ showError('');
+});
\ No newline at end of file
diff --git a/InfoGenie-frontend/public/aimodelapp/AI中国亲戚称呼计算器/styles.css b/InfoGenie-frontend/public/aimodelapp/AI中国亲戚称呼计算器/styles.css
new file mode 100644
index 00000000..c63f012e
--- /dev/null
+++ b/InfoGenie-frontend/public/aimodelapp/AI中国亲戚称呼计算器/styles.css
@@ -0,0 +1,189 @@
+/* 渐变背景与毛玻璃风格,参考 AI文章排版 */
+:root {
+ --green: #a8e6cf;
+ --lime: #dcedc1;
+ --dark: #2e7d32;
+ --text: #1b5e20;
+ --muted: #558b2f;
+ --white: #ffffff;
+ --shadow: rgba(0, 0, 0, 0.08);
+}
+
+html, body {
+ height: 100%;
+}
+
+body {
+ margin: 0;
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", "PingFang SC", "Microsoft YaHei", sans-serif;
+ color: var(--text);
+ background: linear-gradient(135deg, var(--green) 0%, var(--lime) 100%);
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+
+.app-container {
+ min-height: 100vh;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ padding: 16px;
+}
+
+.app-header {
+ text-align: center;
+ margin: 8px 0 16px;
+}
+
+.app-header h1 {
+ font-size: 22px;
+ margin: 0;
+ color: var(--dark);
+}
+
+.subtitle {
+ margin-top: 8px;
+ font-size: 13px;
+ color: var(--muted);
+}
+
+.card {
+ width: 100%;
+ max-width: 720px;
+ background: rgba(255,255,255,0.75);
+ backdrop-filter: blur(10px);
+ border-radius: 14px;
+ box-shadow: 0 8px 24px var(--shadow);
+ padding: 16px;
+}
+
+.label {
+ display: block;
+ font-size: 13px;
+ color: var(--muted);
+ margin-bottom: 6px;
+}
+
+.textarea {
+ width: 100%;
+ box-sizing: border-box;
+ border: 1px solid rgba(0,0,0,0.05);
+ border-radius: 10px;
+ padding: 10px 12px;
+ font-size: 14px;
+ color: var(--text);
+ outline: none;
+}
+.textarea:focus {
+ border-color: rgba(46,125,50,0.35);
+ box-shadow: 0 0 0 3px rgba(46,125,50,0.12);
+}
+
+.hint {
+ margin: 10px 0 12px;
+ font-size: 12px;
+ color: var(--muted);
+}
+.chip {
+ display: inline-block;
+ background: rgba(255,255,255,0.9);
+ border: 1px solid rgba(46,125,50,0.2);
+ color: var(--dark);
+ border-radius: 999px;
+ padding: 4px 10px;
+ margin-right: 6px;
+ cursor: pointer;
+ user-select: none;
+}
+.chip:hover { filter: brightness(0.98); }
+
+.button {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ border: none;
+ border-radius: 12px;
+ padding: 10px 14px;
+ font-size: 14px;
+ cursor: pointer;
+ background: rgba(46,125,50,0.15);
+ color: var(--dark);
+}
+.button.primary {
+ background: linear-gradient(135deg, #81c784, #aed581);
+ color: #fff;
+}
+.button:disabled { opacity: 0.6; cursor: not-allowed; }
+
+.loading {
+ margin-top: 10px;
+ font-size: 13px;
+ color: var(--muted);
+}
+.error {
+ margin-top: 8px;
+ padding: 8px 10px;
+ border-left: 3px solid #e53935;
+ background: rgba(229,57,53,0.08);
+ border-radius: 8px;
+ color: #c62828;
+ font-size: 13px;
+}
+
+.result-section { margin-top: 14px; }
+.result-section h2 {
+ font-size: 16px;
+ margin: 0 0 8px;
+ color: var(--dark);
+}
+
+.result-block {
+ background: rgba(255,255,255,0.9);
+ border: 1px solid rgba(0,0,0,0.05);
+ border-radius: 12px;
+ padding: 10px;
+ margin-bottom: 10px;
+}
+.result-title {
+ font-size: 13px;
+ color: var(--muted);
+ margin-bottom: 6px;
+}
+.result-value {
+ font-size: 18px;
+ color: var(--text);
+}
+.actions { margin-top: 8px; }
+
+.dialect-list {
+ display: grid;
+ grid-template-columns: 1fr;
+ gap: 8px;
+}
+@media (min-width: 540px) {
+ .dialect-list { grid-template-columns: 1fr 1fr; }
+}
+
+.dialect-item {
+ border: 1px solid rgba(46,125,50,0.18);
+ border-radius: 10px;
+ padding: 8px;
+ background: rgba(255,255,255,0.95);
+}
+.dialect-item .dialect-name {
+ font-weight: 600;
+ color: var(--dark);
+ margin-bottom: 4px;
+}
+.dialect-item .dialect-title { font-size: 15px; }
+.dialect-item .dialect-roman { font-size: 12px; color: var(--muted); }
+.dialect-item .dialect-notes { font-size: 12px; color: var(--muted); margin-top: 4px; }
+
+.notes { font-size: 13px; color: var(--muted); }
+
+.app-footer {
+ margin-top: 12px;
+ font-size: 12px;
+ color: var(--muted);
+ text-align: center;
+}
\ No newline at end of file
diff --git a/InfoGenie-frontend/public/aimodelapp/AI变量命名助手/styles.css b/InfoGenie-frontend/public/aimodelapp/AI变量命名助手/styles.css
index c6d4bd14..ce1673ca 100755
--- a/InfoGenie-frontend/public/aimodelapp/AI变量命名助手/styles.css
+++ b/InfoGenie-frontend/public/aimodelapp/AI变量命名助手/styles.css
@@ -8,13 +8,31 @@
/* 主体样式 - iOS风格 */
body {
font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Helvetica Neue', Arial, sans-serif;
- background: linear-gradient(135deg, #87CEEB 0%, #98FB98 100%);
+ background: linear-gradient(135deg, #F0FFF0 0%, #98FB98 50%, #90EE90 100%);
min-height: 100vh;
padding: 20px;
color: #1D1D1F;
line-height: 1.47;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
+ /* 隐藏滚动条但保留滚动功能 */
+ scrollbar-width: none; /* Firefox */
+ -ms-overflow-style: none; /* IE/Edge */
+}
+
+/* 隐藏Webkit浏览器的滚动条 */
+body::-webkit-scrollbar {
+ display: none;
+}
+
+/* 全局滚动条隐藏 */
+* {
+ scrollbar-width: none; /* Firefox */
+ -ms-overflow-style: none; /* IE/Edge */
+}
+
+*::-webkit-scrollbar {
+ display: none; /* Chrome, Safari, Opera */
}
/* 容器样式 - iOS毛玻璃效果 */
@@ -81,9 +99,9 @@ body {
.form-input:focus {
outline: none;
- border-color: #007AFF;
+ border-color: #32CD32;
background: rgba(255, 255, 255, 0.95);
- box-shadow: 0 0 0 4px rgba(0, 122, 255, 0.1);
+ box-shadow: 0 0 0 4px rgba(50, 205, 50, 0.1);
}
.textarea {
@@ -105,7 +123,7 @@ body {
.btn {
width: 100%;
padding: 16px;
- background: #007AFF;
+ background: #32CD32;
color: white;
border: none;
border-radius: 12px;
@@ -114,18 +132,18 @@ body {
cursor: pointer;
transition: all 0.2s ease;
margin-bottom: 24px;
- box-shadow: 0 2px 8px rgba(0, 122, 255, 0.25);
+ box-shadow: 0 2px 8px rgba(50, 205, 50, 0.25);
}
.btn:hover {
- background: #0056CC;
+ background: #228B22;
transform: translateY(-1px);
- box-shadow: 0 4px 16px rgba(0, 122, 255, 0.35);
+ box-shadow: 0 4px 16px rgba(50, 205, 50, 0.35);
}
.btn:active {
transform: translateY(0);
- background: #004499;
+ background: #006400;
}
.btn:disabled {
@@ -151,7 +169,7 @@ body {
.loading {
display: none;
text-align: center;
- color: #007AFF;
+ color: #32CD32;
font-style: normal;
padding: 24px;
font-weight: 500;
@@ -161,9 +179,12 @@ body {
background: rgba(255, 255, 255, 0.6);
border: 1px solid rgba(0, 0, 0, 0.08);
border-radius: 16px;
- padding: 24px;
+ padding: 20px;
min-height: 150px;
backdrop-filter: blur(10px);
+ display: grid;
+ gap: 16px;
+ grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
}
.placeholder {
@@ -176,15 +197,16 @@ body {
/* 分组标题样式 - iOS风格 */
.convention-group-title {
- font-size: 1.0625rem;
+ font-size: 1rem;
font-weight: 600;
color: white;
- margin: 20px 0 12px 0;
- padding: 12px 16px;
- background: #007AFF;
+ margin: 16px 0 12px 0;
+ padding: 10px 16px;
+ background: #32CD32;
border-radius: 12px;
text-align: center;
- box-shadow: 0 2px 8px rgba(0, 122, 255, 0.25);
+ box-shadow: 0 2px 8px rgba(50, 205, 50, 0.25);
+ grid-column: 1 / -1;
}
.convention-group-title:first-child {
@@ -196,17 +218,21 @@ body {
background: rgba(255, 255, 255, 0.9);
border: 1px solid rgba(0, 0, 0, 0.06);
border-radius: 12px;
- padding: 16px;
- margin-bottom: 12px;
+ padding: 12px;
+ margin-bottom: 0;
transition: all 0.2s ease;
cursor: pointer;
position: relative;
backdrop-filter: blur(10px);
+ min-height: 80px;
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
}
.suggestion-item:hover {
- border-color: rgba(0, 122, 255, 0.3);
- box-shadow: 0 4px 16px rgba(0, 122, 255, 0.1);
+ border-color: rgba(50, 205, 50, 0.3);
+ box-shadow: 0 4px 16px rgba(50, 205, 50, 0.1);
background: rgba(255, 255, 255, 0.95);
}
@@ -216,33 +242,35 @@ body {
.variable-name {
font-family: 'SF Mono', 'Monaco', 'Consolas', 'Courier New', monospace;
- font-size: 1.0625rem;
+ font-size: 1rem;
font-weight: 600;
color: #1D1D1F;
- margin-bottom: 6px;
+ margin-bottom: 4px;
+ word-break: break-all;
}
.variable-description {
- font-size: 0.9375rem;
+ font-size: 0.875rem;
color: #86868B;
- line-height: 1.47;
+ line-height: 1.4;
+ flex-grow: 1;
}
.copy-btn {
position: absolute;
- top: 12px;
- right: 12px;
- background: #007AFF;
+ top: 8px;
+ right: 8px;
+ background: #32CD32;
color: white;
border: none;
- border-radius: 8px;
- padding: 6px 12px;
- font-size: 0.8125rem;
+ border-radius: 6px;
+ padding: 4px 8px;
+ font-size: 0.75rem;
font-weight: 600;
cursor: pointer;
opacity: 0;
transition: all 0.2s ease;
- box-shadow: 0 2px 4px rgba(0, 122, 255, 0.25);
+ box-shadow: 0 1px 3px rgba(50, 205, 50, 0.25);
}
.suggestion-item:hover .copy-btn {
@@ -250,7 +278,7 @@ body {
}
.copy-btn:hover {
- background: #0056CC;
+ background: #228B22;
transform: translateY(-1px);
}
@@ -314,17 +342,30 @@ body {
.suggestions-container {
padding: 15px;
+ grid-template-columns: 1fr;
+ gap: 12px;
}
.suggestion-item {
- padding: 12px;
+ padding: 10px;
+ min-height: 70px;
}
.copy-btn {
position: static;
opacity: 1;
- margin-top: 8px;
+ margin-top: 6px;
width: 100%;
+ padding: 6px 8px;
+ font-size: 0.75rem;
+ }
+
+ .variable-name {
+ font-size: 0.9rem;
+ }
+
+ .variable-description {
+ font-size: 0.8rem;
}
}
@@ -342,16 +383,30 @@ body {
padding: 10px;
}
+ .suggestions-container {
+ padding: 12px;
+ gap: 10px;
+ }
+
.suggestion-item {
- padding: 10px;
+ padding: 8px;
+ min-height: 60px;
}
.variable-name {
- font-size: 1rem;
+ font-size: 0.85rem;
+ margin-bottom: 3px;
}
.variable-description {
- font-size: 0.85rem;
+ font-size: 0.75rem;
+ line-height: 1.3;
+ }
+
+ .convention-group-title {
+ font-size: 0.9rem;
+ padding: 8px 12px;
+ margin: 12px 0 8px 0;
}
}
diff --git a/InfoGenie-frontend/public/aimodelapp/AI文章排版/env.js b/InfoGenie-frontend/public/aimodelapp/AI文章排版/env.js
new file mode 100644
index 00000000..277c8d07
--- /dev/null
+++ b/InfoGenie-frontend/public/aimodelapp/AI文章排版/env.js
@@ -0,0 +1,29 @@
+// 环境配置文件 - AI文章排版
+// 复用 InfoGenie 的全局 ENV_CONFIG
+
+// 本地/独立打开页面的API回退地址(优先使用父窗口ENV_CONFIG)
+const DEFAULT_API = (window.ENV_CONFIG && window.ENV_CONFIG.API_URL) || 'http://127.0.0.1:5002';
+
+// API配置
+window.API_CONFIG = {
+ baseUrl: window.parent?.ENV_CONFIG?.API_URL || DEFAULT_API,
+ endpoints: {
+ markdownFormatting: '/api/aimodelapp/markdown_formatting'
+ }
+};
+
+// 认证配置
+window.AUTH_CONFIG = {
+ tokenKey: 'token',
+ getToken: () => localStorage.getItem('token'),
+ isAuthenticated: () => !!localStorage.getItem('token')
+};
+
+// 应用配置
+window.APP_CONFIG = {
+ name: 'InfoGenie AI文章排版',
+ version: '1.0.0',
+ debug: false
+};
+
+console.log('AI文章排版 环境配置已加载');
\ No newline at end of file
diff --git a/InfoGenie-frontend/public/aimodelapp/AI文章排版/index.html b/InfoGenie-frontend/public/aimodelapp/AI文章排版/index.html
new file mode 100644
index 00000000..6118e10d
--- /dev/null
+++ b/InfoGenie-frontend/public/aimodelapp/AI文章排版/index.html
@@ -0,0 +1,92 @@
+
+
+
+
+
+ AI文章排版助手
+
+
+
+
+
+
+
+
+
+
排版结果
+
正在排版中,请稍候...
+
+
输入文章后点击“开始排版”,AI将把原文转换为规范的Markdown,并智能添加合适的Emoji
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/InfoGenie-frontend/public/aimodelapp/AI文章排版/script.js b/InfoGenie-frontend/public/aimodelapp/AI文章排版/script.js
new file mode 100644
index 00000000..5a144a42
--- /dev/null
+++ b/InfoGenie-frontend/public/aimodelapp/AI文章排版/script.js
@@ -0,0 +1,152 @@
+// 配置已在 env.js 中定义
+
+// DOM元素
+const articleTextInput = document.getElementById('articleText');
+const emojiStyleSelect = document.getElementById('emojiStyle');
+const markdownOptionSelect = document.getElementById('markdownOption');
+const formatBtn = document.getElementById('formatBtn');
+const loadingDiv = document.getElementById('loading');
+const resultContainer = document.getElementById('resultContainer');
+const previewSection = document.getElementById('previewSection');
+const markdownPreview = document.getElementById('markdownPreview');
+const rawSection = document.getElementById('rawSection');
+const markdownRaw = document.getElementById('markdownRaw');
+const copyMdBtn = document.getElementById('copyMdBtn');
+const copyHtmlBtn = document.getElementById('copyHtmlBtn');
+
+// 加载器控制
+function showLoading(show) {
+ loadingDiv.style.display = show ? 'block' : 'none';
+ formatBtn.disabled = show;
+}
+
+// 错误提示
+function showErrorMessage(msg) {
+ resultContainer.innerHTML = `${msg}
`;
+}
+
+// 调用后端API
+async function callBackendAPI(articleText, emojiStyle, markdownOption) {
+ try {
+ const token = window.AUTH_CONFIG.getToken();
+ if (!token) throw new Error('未登录,请先登录后使用AI功能');
+
+ const url = `${window.API_CONFIG.baseUrl}${window.API_CONFIG.endpoints.markdownFormatting}`;
+ const response = await fetch(url, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'Authorization': `Bearer ${token}`
+ },
+ body: JSON.stringify({
+ article_text: articleText,
+ emoji_style: emojiStyle,
+ markdown_option: markdownOption
+ })
+ });
+
+ if (!response.ok) {
+ if (response.status === 402) throw new Error('您的萌芽币余额不足,无法使用此功能');
+ const errorData = await response.json().catch(() => ({}));
+ throw new Error(errorData.error || `API请求失败: ${response.status} ${response.statusText}`);
+ }
+
+ const data = await response.json();
+ if (data.success && data.formatted_markdown) return data.formatted_markdown;
+ throw new Error(data.error || 'API响应格式异常');
+ } catch (error) {
+ console.error('API调用错误:', error);
+ throw error;
+ }
+}
+
+// 显示结果
+function displayFormattingResult(markdownText) {
+ // 源Markdown
+ markdownRaw.textContent = markdownText || '';
+ rawSection.style.display = markdownText ? 'block' : 'none';
+
+ // 预览渲染(使用marked + DOMPurify)
+ let html = '';
+ try {
+ // 兼容新旧版本的marked库
+ if (typeof marked === 'function') {
+ // 旧版本marked直接调用
+ html = marked(markdownText || '');
+ } else if (marked && typeof marked.parse === 'function') {
+ // 新版本marked使用parse方法
+ html = marked.parse(markdownText || '');
+ } else {
+ throw new Error('marked库未正确加载');
+ }
+
+ // 使用DOMPurify清理HTML(如果可用)
+ const safeHtml = typeof DOMPurify !== 'undefined' ? DOMPurify.sanitize(html) : html;
+ markdownPreview.innerHTML = safeHtml;
+ } catch (error) {
+ console.error('Markdown渲染失败:', error);
+ markdownPreview.innerHTML = `Markdown渲染失败: ${error.message}
`;
+ }
+
+ previewSection.style.display = markdownText ? 'block' : 'none';
+
+ // 顶部结果容器状态
+ resultContainer.innerHTML = '';
+ resultContainer.classList.add('conversion-result');
+}
+
+// 复制功能
+function copyToClipboard(text) {
+ try {
+ navigator.clipboard.writeText(text);
+ } catch (e) {
+ const textarea = document.createElement('textarea');
+ textarea.value = text;
+ document.body.appendChild(textarea);
+ textarea.select();
+ document.execCommand('copy');
+ document.body.removeChild(textarea);
+ }
+}
+
+copyMdBtn.addEventListener('click', () => copyToClipboard(markdownRaw.textContent || ''));
+copyHtmlBtn.addEventListener('click', () => copyToClipboard(markdownPreview.innerHTML || ''));
+
+// 执行排版
+async function performFormatting() {
+ const articleText = articleTextInput.value.trim();
+ const emojiStyle = emojiStyleSelect.value;
+ const markdownOption = markdownOptionSelect.value;
+
+ if (!articleText) {
+ showErrorMessage('请输入需要排版的文章内容');
+ return;
+ }
+
+ showLoading(true);
+ resultContainer.innerHTML = '';
+ previewSection.style.display = 'none';
+ rawSection.style.display = 'none';
+
+ try {
+ const markdown = await callBackendAPI(articleText, emojiStyle, markdownOption);
+ displayFormattingResult(markdown);
+ } catch (error) {
+ console.error('排版失败:', error);
+ showErrorMessage(`排版失败: ${error.message}`);
+ } finally {
+ showLoading(false);
+ }
+}
+
+// 事件绑定
+formatBtn.addEventListener('click', performFormatting);
+
+// 页面初始化
+document.addEventListener('DOMContentLoaded', () => {
+ resultContainer.innerHTML = '请输入文章内容,选择Emoji风格与排版偏好,然后点击开始排版
';
+});
+
+// 导出函数供HTML调用
+window.performFormatting = performFormatting;
+window.copyToClipboard = copyToClipboard;
\ No newline at end of file
diff --git a/InfoGenie-frontend/public/aimodelapp/AI文章排版/styles.css b/InfoGenie-frontend/public/aimodelapp/AI文章排版/styles.css
new file mode 100644
index 00000000..83442a33
--- /dev/null
+++ b/InfoGenie-frontend/public/aimodelapp/AI文章排版/styles.css
@@ -0,0 +1,84 @@
+/* 全局样式重置 */
+* { margin: 0; padding: 0; box-sizing: border-box; }
+
+/* 主体样式 - 清新渐变 */
+body {
+ font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Helvetica Neue', Arial, sans-serif;
+ background: linear-gradient(135deg, #a8e6cf 0%, #dcedc8 50%, #f1f8e9 100%);
+ min-height: 100vh;
+ padding: 20px;
+ color: #2e7d32;
+ line-height: 1.47;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+
+/* 容器样式 - 毛玻璃效果 */
+.container {
+ max-width: 900px;
+ margin: 0 auto;
+ background: rgba(255, 255, 255, 0.85);
+ border-radius: 24px;
+ padding: 32px;
+ box-shadow: 0 8px 32px rgba(76, 175, 80, 0.15), 0 2px 8px rgba(76, 175, 80, 0.1);
+ backdrop-filter: blur(20px) saturate(180%);
+ border: 1px solid rgba(76, 175, 80, 0.2);
+}
+
+/* 头部样式 */
+.header { text-align: center; margin-bottom: 32px; }
+.title { font-size: 2.25rem; color: #1b5e20; margin-bottom: 8px; font-weight: 600; letter-spacing: -0.02em; }
+.subtitle { color: #4caf50; font-size: 1.0625rem; margin-bottom: 24px; font-weight: 400; }
+
+/* 表单区域 */
+.form-section { margin-bottom: 32px; }
+.form-group { margin-bottom: 24px; }
+.form-row { display: flex; gap: 16px; margin-bottom: 24px; }
+.half-width { flex: 1; }
+.form-label { display: block; margin-bottom: 8px; font-weight: 600; color: #2e7d32; }
+.form-input { width: 100%; border: 1px solid rgba(0, 0, 0, 0.08); border-radius: 12px; padding: 12px 14px; outline: none; background: rgba(255, 255, 255, 0.75); color: #1b5e20; font-size: 1rem; transition: all 0.2s ease; }
+.form-input:focus { border-color: rgba(76, 175, 80, 0.4); box-shadow: 0 0 0 4px rgba(76, 175, 80, 0.15); }
+.textarea { min-height: 160px; resize: vertical; line-height: 1.6; }
+.select { appearance: none; background-image: linear-gradient(135deg, #f1f8e9 0%, #e8f5e9 100%); }
+
+/* 操作按钮 */
+.btn { width: 100%; padding: 14px 18px; border: none; border-radius: 14px; font-weight: 600; font-size: 1.0625rem; color: #fff; background: linear-gradient(135deg, #43a047 0%, #66bb6a 50%, #81c784 100%); box-shadow: 0 4px 16px rgba(76, 175, 80, 0.3), 0 2px 8px rgba(76, 175, 80, 0.2); cursor: pointer; transition: all 0.2s ease; }
+.btn:hover { transform: translateY(-1px); box-shadow: 0 6px 20px rgba(76, 175, 80, 0.35); }
+.btn:active { transform: translateY(0); background: linear-gradient(135deg, #2e7d32 0%, #388e3c 100%); }
+.btn:disabled { opacity: 0.5; cursor: not-allowed; transform: none; background: #86868B; }
+
+/* 结果区域 */
+.result-section { margin-top: 32px; }
+.result-title { font-size: 1.25rem; color: #1b5e20; margin-bottom: 16px; text-align: center; font-weight: 600; }
+.loading { display: none; text-align: center; color: #4caf50; padding: 24px; font-weight: 500; }
+.conversion-container { background: rgba(255, 255, 255, 0.6); border: 1px solid rgba(0, 0, 0, 0.08); border-radius: 16px; padding: 24px; min-height: 140px; backdrop-filter: blur(10px); }
+.placeholder { text-align: center; color: #86868B; padding: 32px 20px; font-weight: 400; }
+.placeholder.error { color: #d32f2f; background: rgba(244, 67, 54, 0.1); border: 1px solid rgba(244, 67, 54, 0.2); border-radius: 8px; }
+.error { color: #d32f2f; background: rgba(244, 67, 54, 0.1); padding: 12px; border-radius: 8px; border: 1px solid rgba(244, 67, 54, 0.2); }
+
+/* 预览与原文区域 */
+.preview-section, .raw-section { margin-top: 24px; }
+.preview-header, .raw-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px; }
+.preview-header .label, .raw-header .label { font-weight: 600; color: #2e7d32; font-size: 1rem; }
+.copy-btn { padding: 6px 10px; border: none; border-radius: 10px; font-weight: 600; font-size: 0.9375rem; color: #fff; background: linear-gradient(135deg, #4caf50 0%, #81c784 100%); box-shadow: 0 2px 8px rgba(76, 175, 80, 0.25); cursor: pointer; }
+.copy-btn:hover { filter: brightness(1.05); }
+
+.markdown-preview { background: rgba(255, 255, 255, 0.9); border: 1px solid rgba(0, 0, 0, 0.06); border-radius: 12px; padding: 20px; color: #2e7d32; line-height: 1.8; }
+.markdown-raw { background: rgba(255, 255, 255, 0.85); border: 1px solid rgba(0, 0, 0, 0.06); border-radius: 12px; padding: 16px; color: #1b5e20; font-family: 'SF Mono', 'Monaco', 'Consolas', 'Courier New', monospace; font-size: 0.9375rem; white-space: pre-wrap; word-break: break-word; }
+
+/* Markdown渲染细节 */
+.markdown-preview h1, .markdown-preview h2, .markdown-preview h3 { color: #1b5e20; margin: 10px 0; }
+.markdown-preview p { margin: 10px 0; }
+.markdown-preview ul, .markdown-preview ol { padding-left: 24px; margin: 10px 0; }
+.markdown-preview blockquote { border-left: 4px solid rgba(76, 175, 80, 0.4); padding-left: 12px; color: #4caf50; background: rgba(76, 175, 80, 0.08); border-radius: 6px; }
+.markdown-preview code { background: rgba(0, 0, 0, 0.06); padding: 2px 6px; border-radius: 6px; font-family: 'SF Mono', 'Monaco', 'Consolas', 'Courier New', monospace; }
+
+/* 移动端优化 */
+@media (max-width: 480px) {
+ .container { padding: 18px; border-radius: 18px; }
+ .title { font-size: 1.75rem; }
+ .subtitle { font-size: 0.95rem; }
+ .form-row { flex-direction: column; gap: 12px; }
+ .textarea { min-height: 200px; }
+ .btn { font-size: 1rem; padding: 12px 16px; }
+}
\ No newline at end of file
diff --git a/InfoGenie-frontend/public/assets/fonts/kaiti1.ttf b/InfoGenie-frontend/public/assets/fonts/kaiti1.ttf
new file mode 100644
index 00000000..e3c7007c
Binary files /dev/null and b/InfoGenie-frontend/public/assets/fonts/kaiti1.ttf differ
diff --git a/InfoGenie-frontend/public/assets/圆角-logo.png b/InfoGenie-frontend/public/assets/圆角-logo.png
new file mode 100644
index 00000000..ef7d5fc2
Binary files /dev/null and b/InfoGenie-frontend/public/assets/圆角-logo.png differ
diff --git a/InfoGenie-frontend/public/index.html b/InfoGenie-frontend/public/index.html
index effd9954..45050624 100755
--- a/InfoGenie-frontend/public/index.html
+++ b/InfoGenie-frontend/public/index.html
@@ -323,9 +323,9 @@
- 🎨 一个跨平台的多功能聚合应用(´。• ω •。`) 💬
+ 🎨 一个跨平台的多功能聚合应用(´。• ω •。`) 💬
diff --git a/InfoGenie-frontend/public/smallgame/2048/game-logic.js b/InfoGenie-frontend/public/smallgame/2048/game-logic.js
index 910dce86..e68ffd92 100755
--- a/InfoGenie-frontend/public/smallgame/2048/game-logic.js
+++ b/InfoGenie-frontend/public/smallgame/2048/game-logic.js
@@ -30,6 +30,122 @@ class Game2048 {
// 开始计时
this.startTimer();
}
+
+ // 依据分数计算权重(0.1 ~ 0.95)
+ calculateWeightByScore(score) {
+ const w = score / 4000; // 4000分约接近满权重
+ return Math.max(0.1, Math.min(0.95, w));
+ }
+
+ // 按权重偏向生成0~10的随机整数,权重越高越偏向更大值
+ biasedRandomInt(maxInclusive, weight) {
+ const rand = Math.random();
+ const biased = Math.pow(rand, 1 - weight); // weight越大,biased越接近1
+ const val = Math.floor(biased * (maxInclusive + 1));
+ return Math.max(0, Math.min(maxInclusive, val));
+ }
+
+ // 附加结束信息到界面
+ appendEndInfo(text, type = 'info') {
+ const message = document.getElementById('game-message');
+ if (!message) return;
+ const info = document.createElement('div');
+ info.style.marginTop = '10px';
+ info.style.fontSize = '16px';
+ info.style.color = type === 'error' ? '#d9534f' : (type === 'success' ? '#28a745' : '#776e65');
+ info.textContent = text;
+ message.appendChild(info);
+ }
+
+ // 游戏结束时尝试给当前登录账户加“萌芽币”
+ async tryAwardCoinsOnGameOver() {
+ try {
+ const token = localStorage.getItem('token');
+ if (!token) {
+ this.appendEndInfo('未登录,无法获得萌芽币');
+ return;
+ }
+
+ let email = null;
+ try {
+ const userStr = localStorage.getItem('user');
+ if (userStr) {
+ const userObj = JSON.parse(userStr);
+ email = userObj && (userObj.email || userObj['邮箱']);
+ }
+ } catch (e) {
+ // 忽略解析错误
+ }
+
+ if (!email) {
+ this.appendEndInfo('未找到账户信息(email),无法加币', 'error');
+ return;
+ }
+
+ // 根据分数计算权重与概率
+ const weight = this.calculateWeightByScore(this.score);
+ let awardProbability = weight; // 默认用权重作为概率
+ let guaranteed = false;
+
+ // 分数≥500时必定触发奖励
+ if (this.score >= 500) {
+ awardProbability = 1;
+ guaranteed = true;
+ }
+
+ const roll = Math.random();
+ if (roll > awardProbability) {
+ this.appendEndInfo('本局未获得萌芽币');
+ return;
+ }
+
+ // 生成0~10随机萌芽币数量,权重越高越偏向更大值
+ let coins = this.biasedRandomInt(5, weight);
+ // 保底至少 1 个(仅当分数≥500时)
+ if (guaranteed) {
+ coins = Math.max(1, coins);
+ }
+ coins = Math.max(0, Math.min(10, coins));
+
+ if (coins <= 0) {
+ this.appendEndInfo('本局未获得萌芽币');
+ return;
+ }
+
+ // 后端 API base URL(从父窗口ENV_CONFIG获取,回退到本地默认)
+ const apiBase = (window.parent && window.parent.ENV_CONFIG && window.parent.ENV_CONFIG.API_URL)
+ ? window.parent.ENV_CONFIG.API_URL
+ : ((window.ENV_CONFIG && window.ENV_CONFIG.API_URL) ? window.ENV_CONFIG.API_URL : 'http://127.0.0.1:5002');
+
+ const resp = await fetch(`${apiBase}/api/user/add-coins`, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'Authorization': `Bearer ${token}`
+ },
+ body: JSON.stringify({ email, amount: coins })
+ });
+
+ if (!resp.ok) {
+ const err = await resp.json().catch(() => ({}));
+ const msg = err && (err.message || err.error) ? (err.message || err.error) : `请求失败(${resp.status})`;
+ this.appendEndInfo(`加币失败:${msg}`, 'error');
+ return;
+ }
+
+ const data = await resp.json();
+ if (data && data.success) {
+ const newCoins = data.data && data.data.new_coins;
+ this.appendEndInfo(`恭喜获得 ${coins} 个萌芽币!当前余额:${newCoins}`, 'success');
+ } else {
+ const msg = (data && (data.message || data.error)) || '未知错误';
+ this.appendEndInfo(`加币失败:${msg}`, 'error');
+ }
+ } catch (e) {
+ console.error('加币流程发生错误:', e);
+ this.appendEndInfo('加币失败:网络或系统错误', 'error');
+ }
+ }
initializeGrid() {
this.grid = [];
@@ -315,6 +431,16 @@ class Game2048 {
message.className = 'game-message game-won';
message.style.display = 'flex';
message.querySelector('p').textContent = '你赢了!';
+
+ // 胜利也尝试加币(异步,不阻塞UI)
+ this.tryAwardCoinsOnGameOver();
+
+ // 显示最终统计
+ setTimeout(() => {
+ if (window.gameStats) {
+ window.gameStats.showFinalStats();
+ }
+ }, 1000);
}
showGameOver() {
@@ -323,6 +449,16 @@ class Game2048 {
message.style.display = 'flex';
message.querySelector('p').textContent = '游戏结束!';
+ // 渲染排行榜
+ try {
+ this.renderLeaderboard();
+ } catch (e) {
+ console.error('渲染排行榜时发生错误:', e);
+ }
+
+ // 尝试加币(异步,不阻塞UI)
+ this.tryAwardCoinsOnGameOver();
+
// 显示最终统计
setTimeout(() => {
if (window.gameStats) {
@@ -377,6 +513,92 @@ class Game2048 {
}, 1000);
}
+ // 构建并渲染排行榜
+ renderLeaderboard() {
+ const container = document.getElementById('leaderboard');
+ if (!container) return;
+
+ // 生成当前玩家数据
+ const today = this.formatDate(new Date());
+ const currentPlayer = {
+ "名称": "我",
+ "账号": "guest-local",
+ "分数": this.score,
+ "时间": today,
+ _current: true
+ };
+
+ // 合并并排序数据(分数由高到低)
+ const baseData = (typeof playerdata !== 'undefined' && Array.isArray(playerdata)) ? playerdata : [];
+ const merged = [...baseData.map(d => ({...d})), currentPlayer]
+ .sort((a, b) => (b["分数"] || 0) - (a["分数"] || 0));
+
+ // 计算当前玩家排名
+ const currentIndex = merged.findIndex(d => d._current);
+ const rank = currentIndex >= 0 ? currentIndex + 1 : '-';
+
+ // 仅展示前10条
+ const topN = merged.slice(0, 10);
+
+ // 生成 HTML
+ const summaryHtml = `
+
+ 本局分数:${this.score}
+ 用时:${this.stats.gameTime} 秒
+ 你的排名:${rank}
+
+ `;
+
+ const headerHtml = `
+
+ `;
+
+ const rowsHtml = topN.map((d, i) => {
+ const isCurrent = !!d._current;
+ const rowClass = `leaderboard-row${isCurrent ? ' current' : ''}`;
+ return `
+
+
${i + 1}
+
${this.escapeHtml(d["名称"] || '未知')}
+
${d["分数"] ?? 0}
+
${this.escapeHtml(d["时间"] || '-')}
+
+ `;
+ }).join('');
+
+ container.innerHTML = `
+ 排行榜
+ ${summaryHtml}
+
+ ${headerHtml}
+
${rowsHtml}
+
+ `;
+ }
+
+ // 工具:日期格式化 YYYY-MM-DD
+ formatDate(date) {
+ const y = date.getFullYear();
+ const m = String(date.getMonth() + 1).padStart(2, '0');
+ const d = String(date.getDate()).padStart(2, '0');
+ return `${y}-${m}-${d}`;
+ }
+
+ // 工具:简单转义以避免 XSS
+ escapeHtml(str) {
+ return String(str)
+ .replace(/&/g, '&')
+ .replace(//g, '>')
+ .replace(/\"/g, '"')
+ .replace(/'/g, ''');
+ }
+
bindEvents() {
// 重试按钮
document.getElementById('retry-btn').addEventListener('click', () => {
diff --git a/InfoGenie-frontend/public/smallgame/2048/index.html b/InfoGenie-frontend/public/smallgame/2048/index.html
index 41e1a707..11a8f6c0 100755
--- a/InfoGenie-frontend/public/smallgame/2048/index.html
+++ b/InfoGenie-frontend/public/smallgame/2048/index.html
@@ -22,6 +22,8 @@