更新前后端配置并清理仓库内容

This commit is contained in:
2026-04-12 16:37:36 +08:00
parent dbd72610c1
commit d373ce1da9
20 changed files with 362 additions and 63 deletions

View File

@@ -38,8 +38,8 @@
<link rel="manifest" href="/manifest.json" />
<!-- Preload fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="preconnect" href="//fonts.googleapis.com">
<link rel="preconnect" href="//fonts.gstatic.com" crossorigin>
<title>万象口袋</title>

View File

@@ -1,7 +1,7 @@
{
"short_name": "万象口袋",
"name": "万象口袋 - 多功能聚合应用",
"description": "万象口袋 - 多功能聚合应用",
"name": "万象口袋",
"description": "万象口袋",
"icons": [
{
"src": "/icons/icon-192.png",
@@ -36,9 +36,9 @@
"categories": ["utilities", "productivity", "entertainment"],
"screenshots": [
{
"src": "data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 400 800'><rect width='400' height='800' fill='%23667eea'/><text x='200' y='400' text-anchor='middle' fill='white' font-size='32'>万象口袋</text></svg>",
"type": "image/svg+xml",
"sizes": "400x800",
"src": "/icons/icon-192.png",
"type": "image/png",
"sizes": "192x192",
"form_factor": "narrow"
}
]

View File

@@ -1,13 +1,15 @@
/**
* 60s API 聚合接口配置
* 基于 https://docs.60s-api.viki.moe/ 文档整理
* 基于 60s-api 官方文档整理
* 所有 endpoint 均为 /v2/ 前缀的相对路径,配合 apiBaseUrl 使用
*/
import { tlsHost } from './urlJoin.js';
// 可选的 API 数据源列表,前端可切换
export const API_SOURCES = [
{ id: 'self', label: '萌芽节点', baseUrl: 'https://60s.api.shumengya.top' },
{ id: 'official', label: '官方节点', baseUrl: 'https://60s.viki.moe' },
{ id: 'self', label: '萌芽节点', baseUrl: tlsHost('60s.api.shumengya.top') },
{ id: 'official', label: '官方节点', baseUrl: tlsHost('60s.viki.moe') },
];
// 默认 API 数据源
@@ -126,6 +128,9 @@ const TOOLBOX_IMAGE = [
{ id: 'tb-img-b64', title: '图片转 Base64', icon: '🔀', link: '/toolbox/图片处理/图片转Base64编码/index.html', desc: '编码转换', offlineOk: true },
{ id: 'tb-img-gif-split', title: 'GIF拆帧', icon: '🎞️', link: '/toolbox/图片处理/GIF拆帧/index.html', desc: 'GIF帧提取', offlineOk: true },
{ id: 'tb-img-webp', title: '图片转 WebP', icon: '🌐', link: '/toolbox/图片处理/图片转webp格式/index.html', desc: 'WebP格式转换', offlineOk: true },
{ id: 'tb-img-watermark', title: '图片加文字水印', icon: '✏️', link: '/toolbox/图片处理/图片加文字水印/图片加文字水印.html', desc: '为图片添加文字水印', offlineOk: true },
{ id: 'tb-img-compress', title: '图片压缩', icon: '🗜️', link: '/toolbox/图片处理/图片压缩处理/图片压缩处理.html', desc: '压缩图片体积', offlineOk: true },
{ id: 'tb-img-png-jpg', title: 'PNG / JPG 互转', icon: '🔁', link: '/toolbox/图片处理/png和jpg格式互转/png和jpg相互转化.html', desc: 'PNG 与 JPG 格式转换', offlineOk: true },
];
const TOOLBOX_UTIL = [
@@ -135,6 +140,7 @@ const TOOLBOX_UTIL = [
{ id: 'tb-json', title: 'JSON 编辑器', icon: '📑', link: '/toolbox/实用工具/Json编辑器/index.html', desc: '格式化与校验', offlineOk: true },
{ id: 'tb-markdown', title: 'Markdown', icon: '⌨️', link: '/toolbox/实用工具/Markdown解析器/index.html', desc: '实时预览', offlineOk: true },
{ id: 'tb-js', title: 'JavaScript', icon: '🟩', link: '/toolbox/实用工具/JavaScript编译器/index.html', desc: '本地运行脚本', offlineOk: true },
{ id: 'tb-c-lang', title: 'C 语言编译器', icon: '⚙️', link: '/toolbox/实用工具/C语言编译器/网页C语言编译器.html', desc: '网页端编写与运行 C', offlineOk: true },
{ id: 'tb-random-num', title: '随机数', icon: '🎲', link: '/toolbox/实用工具/随机数生成器/index.html', desc: '自定义范围', offlineOk: true },
{ id: 'tb-wheel', title: '做决定转盘', icon: '🎡', link: '/toolbox/实用工具/做决定转盘/index.html', desc: '随机选择', offlineOk: true },
{ id: 'tb-calc', title: '计算器', icon: '🔢', link: '/toolbox/实用工具/计算器/index.html', desc: '基础运算', offlineOk: true },

View File

@@ -1,14 +1,16 @@
import { tlsHost, loopbackPort } from './urlJoin.js';
const resolveApiUrl = () => {
const mode = import.meta.env.MODE;
const envApiUrl = import.meta.env.VITE_API_URL;
if (mode === 'production') {
if (envApiUrl && envApiUrl.trim() !== '') return envApiUrl;
return 'https://infogenie.api.shumengya.top';
return tlsHost('infogenie.api.shumengya.top');
}
if (envApiUrl && envApiUrl.trim() !== '') return envApiUrl;
return 'http://127.0.0.1:5002';
return loopbackPort('127.0.0.1', '5002');
};
const resolveAuthUrl = () => {
@@ -17,11 +19,11 @@ const resolveAuthUrl = () => {
if (mode === 'production') {
if (envAuthUrl && envAuthUrl.trim() !== '') return envAuthUrl;
return 'https://auth.shumengya.top';
return tlsHost('auth.shumengya.top');
}
if (envAuthUrl && envAuthUrl.trim() !== '') return envAuthUrl;
return 'https://auth.shumengya.top';
return tlsHost('auth.shumengya.top');
};
const resolveAuthApiUrl = () => {
@@ -30,11 +32,11 @@ const resolveAuthApiUrl = () => {
if (mode === 'production') {
if (envAuthApiUrl && envAuthApiUrl.trim() !== '') return envAuthApiUrl;
return 'https://auth.api.shumengya.top';
return tlsHost('auth.api.shumengya.top');
}
if (envAuthApiUrl && envAuthApiUrl.trim() !== '') return envAuthApiUrl;
return 'https://auth.api.shumengya.top';
return tlsHost('auth.api.shumengya.top');
};
const config = {

View File

@@ -0,0 +1,12 @@
/** 避免源码中出现 https/http 连续字符,否则 Tailwind 扫描会误生成无效任意属性类 */
const c = String.fromCharCode(58);
const tlsScheme = String.fromCharCode(104, 116, 116, 112, 115);
const plainScheme = String.fromCharCode(104, 116, 116, 112);
export function tlsHost(host) {
return `${tlsScheme}${c}//${host}`;
}
export function loopbackPort(host, port) {
return `${plainScheme}${c}//${host}${c}${port}`;
}

View File

@@ -11,6 +11,10 @@ import { ENV_CONFIG } from '../config/env';
import { API_CATEGORIES, TOOLBOX_ITEMS, API_SOURCES } from '../config/Api60sConfig';
import { AI_MODEL_APPS } from '../config/StaticPageConfig';
const gridOverviewStats = { gridTemplateColumns: 'repeat(auto-fit, minmax(200px, 1fr))' };
const gridQuickActions = { gridTemplateColumns: 'repeat(auto-fit, minmax(180px, 1fr))' };
const gridAdminItems = { gridTemplateColumns: 'repeat(auto-fill, minmax(210px, 1fr))' };
const InfoCard = ({ children }) => (
<div className="bg-white/95 rounded-[14px] p-6 shadow-[0_4px_12px_rgba(0,0,0,0.08)] mb-6">{children}</div>
);
@@ -458,7 +462,7 @@ const AdminPage = () => {
{adminSection === 'overview' && (
<>
<div className="grid grid-cols-[repeat(auto-fit,minmax(200px,1fr))] gap-3 mb-6">
<div className="grid gap-3 mb-6" style={gridOverviewStats}>
{[
{ icon: FiGrid, bg: 'linear-gradient(135deg,#4ade80,#22c55e)', label: '60sAPI', value: apiCount },
{ icon: FiBox, bg: 'linear-gradient(135deg,#60a5fa,#3b82f6)', label: '休闲游戏', value: gameCount },
@@ -496,7 +500,7 @@ const AdminPage = () => {
</InfoCard>
<h3 className="text-base text-white mb-3 mt-2 [text-shadow:0_1px_4px_rgba(0,0,0,0.12)]">快捷操作</h3>
<div className="grid grid-cols-[repeat(auto-fit,minmax(180px,1fr))] gap-3 mb-2">
<div className="grid gap-3 mb-2" style={gridQuickActions}>
<ActionBtn onClick={fetchSysInfo} disabled={loading}><FiRefreshCw size={16} /> 刷新后端状态</ActionBtn>
<ActionBtn onClick={handleClearBuildCache}><FiMonitor size={16} /> 清除 SW 缓存</ActionBtn>
<ActionBtn onClick={handleClearLocalStorage}><FiBox size={16} /> 清理本地存储</ActionBtn>
@@ -733,7 +737,7 @@ const AdminPage = () => {
) : (
<>
{[
{ id: 'ai-base', label: 'API Base URL', type: 'text', value: aiBase, onChange: setAiBase, placeholder: 'https://api.deepseek.com' },
{ id: 'ai-base', label: 'API Base URL', type: 'text', value: aiBase, onChange: setAiBase, placeholder: '例如 api.deepseek.com' },
{ id: 'ai-key', label: `API Key${aiKeySet ? `(已配置:${aiKeyHint}` : ''}`, type: 'password', value: aiKey, onChange: setAiKey, placeholder: aiKeySet ? '留空保留原密钥;填写则覆盖' : '必填,保存后仅显示脱敏尾号', autoComplete: 'new-password' },
{ id: 'ai-model', label: '默认模型 ID', type: 'text', value: aiModel, onChange: setAiModel, placeholder: 'deepseek-chat' },
].map(({ id, label, type, value, onChange, placeholder, autoComplete }) => (
@@ -756,11 +760,11 @@ const AdminPage = () => {
<p className="text-sm text-gray-500 leading-[1.55] mb-3">
选择 60s 类接口的<strong>上游根地址</strong> <code className="text-xs bg-gray-100 px-1.5 py-0.5 rounded text-gray-700">Api60sConfig.API_SOURCES</code>
</p>
<div className="grid grid-cols-[repeat(auto-fill,minmax(210px,1fr))] gap-[8px_14px] max-w-[520px]">
<div className="grid gap-[8px_14px] max-w-[520px]" style={gridAdminItems}>
{API_SOURCES.map((s) => (
<SixtyItem key={s.id} htmlFor={`admin-sixty-${s.id}`}>
<input type="radio" id={`admin-sixty-${s.id}`} name="admin_sixty_src" checked={sixtySrcId === s.id} onChange={() => setSixtySrcId(s.id)} />
<span>{s.label} <span className="text-gray-400 text-[11px] font-medium">({s.baseUrl.replace(/^https?:\/\//, '')})</span></span>
<span>{s.label} <span className="text-gray-400 text-[11px] font-medium">({s.baseUrl.replace(/^[^/]+\/\//, '')})</span></span>
</SixtyItem>
))}
</div>
@@ -783,7 +787,7 @@ const AdminPage = () => {
{API_CATEGORIES.map((cat) => (
<div key={cat.id}>
<h3 className="text-[15px] text-[#166534] mt-[18px] mb-2.5 first:mt-1">{cat.icon} {cat.title}</h3>
<div className="grid grid-cols-[repeat(auto-fill,minmax(210px,1fr))] gap-[8px_14px]">
<div className="grid gap-[8px_14px]" style={gridAdminItems}>
{cat.items.map((item) => (
<SixtyItem key={item.id}>
<input type="checkbox" checked={!disabled60sSet.has(item.id)} onChange={(e) => setItemVisible(item.id, e.target.checked)} />
@@ -811,7 +815,7 @@ const AdminPage = () => {
<p className="text-sm text-gray-500">正在加载配置</p>
) : (
<>
<div className="grid grid-cols-[repeat(auto-fill,minmax(210px,1fr))] gap-[8px_14px]">
<div className="grid gap-[8px_14px]" style={gridAdminItems}>
{AI_MODEL_APPS.map((app) => (
<SixtyItem key={app.id}>
<input type="checkbox" checked={!disabledAIModelsSet.has(app.id)} onChange={(e) => setAIModelVisible(app.id, e.target.checked)} />

View File

@@ -187,9 +187,9 @@ export function accentFromGradient(gradient) {
export const ModuleGrid = ({ children, className = '' }) => (
<div
className={clsx(
'grid grid-cols-1 min-[520px]:grid-cols-2',
'gap-3 min-[520px]:gap-3.5',
'max-w-[900px] mx-auto px-3 min-[520px]:px-4',
'grid grid-cols-1 sm:grid-cols-2',
'gap-3 sm:gap-3.5',
'max-w-[900px] mx-auto px-3 sm:px-4',
className,
)}
>

View File

@@ -7,6 +7,8 @@ import { fileURLToPath } from 'url';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
export default defineConfig({
// 若为 trueesbuild 会对 Tailwind 误生成的 gri 等无效规则告警;可改回 true 并接受提示
build: { cssMinify: false },
plugins: [
// Allow JSX in .js files (all files under src/)
{