refactor(Phase 3): merge SystemUpdate + AboutUs into Settings page
- New Settings page combines: system update check, version info, auth/copyright - Remove standalone AboutUs.tsx and SystemUpdate.tsx - Route: 'about-us' + 'system-update' → 'settings' - Profile menu: merge '系统设置' + '关于我们' into single '设置' entry - Sidebar dropdown: add '设置' back to user menu (besides '我的账户')
This commit is contained in:
@@ -9,10 +9,7 @@ import Login from './pages/Login/Login';
|
||||
import VideoCreation from './pages/VideoCreation';
|
||||
import MyWorks from './pages/ContentManagement/MyWorks';
|
||||
import VoiceMaterialLibrary from './pages/ContentManagement/VoiceMaterialLibrary';
|
||||
import AboutUs from './pages/Settings/AboutUs';
|
||||
import SystemUpdate from './pages/Settings/SystemUpdate';
|
||||
|
||||
|
||||
import Settings from './pages/Settings/Settings';
|
||||
import Profile from './pages/Profile/Profile';
|
||||
import UsageDetail from './pages/Profile/UsageDetail';
|
||||
import ToastContainer from './components/Toast/ToastContainer';
|
||||
@@ -33,8 +30,7 @@ type PageType =
|
||||
| 'video-creation'
|
||||
| 'voice-material'
|
||||
| 'my-works'
|
||||
| 'about-us'
|
||||
| 'system-update'
|
||||
| 'settings'
|
||||
| 'profile'
|
||||
| 'usage-detail';
|
||||
|
||||
@@ -43,8 +39,7 @@ const pages: Record<PageType, React.ComponentType> = {
|
||||
'video-creation': VideoCreation,
|
||||
'voice-material': VoiceMaterialLibrary,
|
||||
'my-works': MyWorks,
|
||||
'about-us': AboutUs,
|
||||
'system-update': SystemUpdate,
|
||||
settings: Settings,
|
||||
profile: Profile,
|
||||
'usage-detail': UsageDetail,
|
||||
};
|
||||
|
||||
@@ -35,6 +35,7 @@ interface SidebarProps {
|
||||
|
||||
const userMenuItems = [
|
||||
{ id: 'profile', label: '我的账户' },
|
||||
{ id: 'settings', label: '设置' },
|
||||
];
|
||||
|
||||
export default function Sidebar({ currentPath, onNavigate }: SidebarProps) {
|
||||
|
||||
@@ -33,12 +33,6 @@ const SettingsIcon = () => (
|
||||
</svg>
|
||||
);
|
||||
|
||||
const InfoIcon = () => (
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||
<circle cx="12" cy="12" r="10" /><line x1="12" y1="16" x2="12" y2="12" /><line x1="12" y1="8" x2="12.01" y2="8" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
const ChevronRightIcon = ({ className }: { className?: string }) => (
|
||||
<svg className={className} width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||
<polyline points="9 18 15 12 9 6" />
|
||||
@@ -129,8 +123,7 @@ export default function Profile() {
|
||||
|
||||
const menuItems = [
|
||||
{ label: '使用明细', icon: <FileTextIcon />, onClick: () => navigate('usage-detail') },
|
||||
{ label: '系统设置', icon: <SettingsIcon />, onClick: () => navigate('system-update') },
|
||||
{ label: '关于我们', icon: <InfoIcon />, onClick: () => navigate('about-us') },
|
||||
{ label: '设置', icon: <SettingsIcon />, onClick: () => navigate('settings') },
|
||||
];
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,113 +0,0 @@
|
||||
import { useState, useRef, useCallback } from 'react';
|
||||
import '../ContentManagement/ContentManagement.css';
|
||||
import AppHeader from '../../components/Layout/AppHeader';
|
||||
import EnvironmentSwitchModal from '../../components/Modal/EnvironmentSwitchModal';
|
||||
import { useNavigation } from '../../contexts/NavigationContext';
|
||||
import { saveAppConfig } from '../../api/modules/config';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { toast } from '../../store/uiStore';
|
||||
|
||||
const CURRENT_VERSION = __APP_VERSION__;
|
||||
|
||||
export default function AboutUs() {
|
||||
const { navigate, appEnvironment } = useNavigation();
|
||||
const [showModal, setShowModal] = useState(false);
|
||||
|
||||
// 版本号连击检测
|
||||
const clickCountRef = useRef(0);
|
||||
const clickTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||
|
||||
const handleVersionClick = useCallback(() => {
|
||||
clickCountRef.current += 1;
|
||||
|
||||
if (clickCountRef.current === 5) {
|
||||
setShowModal(true);
|
||||
clickCountRef.current = 0;
|
||||
if (clickTimerRef.current) {clearTimeout(clickTimerRef.current);}
|
||||
return;
|
||||
}
|
||||
|
||||
if (clickTimerRef.current) {clearTimeout(clickTimerRef.current);}
|
||||
clickTimerRef.current = setTimeout(() => {
|
||||
clickCountRef.current = 0;
|
||||
}, 2000);
|
||||
}, []);
|
||||
|
||||
const handleSave = async (env: string) => {
|
||||
try {
|
||||
await saveAppConfig(env);
|
||||
|
||||
// 开发模式下 Vite dev server 重启后无法自动恢复,改用刷新页面
|
||||
if (import.meta.env.DEV) {
|
||||
toast.success('配置已保存,即将刷新');
|
||||
setTimeout(() => {
|
||||
window.location.reload();
|
||||
}, 500);
|
||||
return;
|
||||
}
|
||||
|
||||
toast.success('配置已保存,应用即将重启');
|
||||
setTimeout(() => {
|
||||
invoke('restart_app');
|
||||
}, 800);
|
||||
} catch {
|
||||
toast.error('保存配置失败');
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="settings-page">
|
||||
<AppHeader title="关于我们" showBack onBack={() => navigate('profile')} />
|
||||
<div className="settings-section">
|
||||
<div className="card" style={{ padding: 0, overflow: 'hidden' }}>
|
||||
<div className="settings-row">
|
||||
<span className="settings-row-label">应用名称</span>
|
||||
<span className="settings-row-value">美家卡 智影</span>
|
||||
</div>
|
||||
<div className="settings-row" style={{ borderTop: '1px solid var(--border-light)' }}>
|
||||
<span className="settings-row-label">版本号</span>
|
||||
<span
|
||||
className="settings-row-value"
|
||||
onClick={handleVersionClick}
|
||||
style={{ cursor: 'default', userSelect: 'none' }}
|
||||
title={appEnvironment !== 'production' ? `当前环境: ${appEnvironment}` : undefined}
|
||||
>
|
||||
v{CURRENT_VERSION}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="settings-section">
|
||||
<h2>授权信息</h2>
|
||||
<div className="card" style={{ padding: 'var(--spacing-xl)' }}>
|
||||
<p style={{ lineHeight: 1.8, margin: 0 }}>
|
||||
本软件由美家卡团队开发维护。授权用户可在授权范围内使用本软件进行视频创作。
|
||||
如需商业授权或有任何疑问,请联系我们的支持团队。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="settings-section">
|
||||
<h2>版权声明</h2>
|
||||
<div className="card" style={{ padding: 'var(--spacing-xl)' }}>
|
||||
<p style={{ lineHeight: 1.8, margin: 0 }}>
|
||||
Copyright 2025 美家卡 (meijiaka.cn). All rights reserved.
|
||||
</p>
|
||||
<p style={{ lineHeight: 1.8, marginTop: 'var(--spacing-md)', marginBottom: 0 }}>
|
||||
本软件及其相关文档的所有权利均归美家卡所有。
|
||||
未经授权,不得复制、修改、分发或以其他方式使用本软件。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<EnvironmentSwitchModal
|
||||
key={appEnvironment}
|
||||
open={showModal}
|
||||
currentEnv={appEnvironment}
|
||||
onSave={handleSave}
|
||||
onCancel={() => setShowModal(false)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
+118
-31
@@ -1,21 +1,20 @@
|
||||
/**
|
||||
* 系统更新页面
|
||||
* ============
|
||||
*
|
||||
* 支持手动检查更新、下载并安装。
|
||||
*/
|
||||
|
||||
import { useState } from 'react';
|
||||
import { useState, useRef, useCallback } from 'react';
|
||||
import { useNavigation } from '../../contexts/NavigationContext';
|
||||
import AppHeader from '../../components/Layout/AppHeader';
|
||||
import EnvironmentSwitchModal from '../../components/Modal/EnvironmentSwitchModal';
|
||||
import { check, type Update, type DownloadEvent } from '@tauri-apps/plugin-updater';
|
||||
import { relaunch } from '@tauri-apps/plugin-process';
|
||||
import { saveAppConfig } from '../../api/modules/config';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { toast } from '../../store/uiStore';
|
||||
import '../ContentManagement/ContentManagement.css';
|
||||
|
||||
const CURRENT_VERSION = __APP_VERSION__;
|
||||
|
||||
export default function SystemUpdate() {
|
||||
const { navigate } = useNavigation();
|
||||
export default function Settings() {
|
||||
const { navigate, appEnvironment } = useNavigation();
|
||||
|
||||
// ── 系统更新状态 ──
|
||||
const [checking, setChecking] = useState(false);
|
||||
const [checkResult, setCheckResult] = useState<'none' | 'latest' | 'available'>('none');
|
||||
const [updateInfo, setUpdateInfo] = useState<Update | null>(null);
|
||||
@@ -24,7 +23,12 @@ export default function SystemUpdate() {
|
||||
const [progress, setProgress] = useState(0);
|
||||
const [downloadedBytes, setDownloadedBytes] = useState(0);
|
||||
const [totalBytes, setTotalBytes] = useState(0);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [updateError, setUpdateError] = useState<string | null>(null);
|
||||
|
||||
// ── 环境切换 ──
|
||||
const [showEnvModal, setShowEnvModal] = useState(false);
|
||||
const clickCountRef = useRef(0);
|
||||
const clickTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||
|
||||
const formatBytes = (bytes: number) => {
|
||||
if (bytes === 0) return '0 B';
|
||||
@@ -37,7 +41,7 @@ export default function SystemUpdate() {
|
||||
const handleCheck = async () => {
|
||||
setChecking(true);
|
||||
setCheckResult('none');
|
||||
setError(null);
|
||||
setUpdateError(null);
|
||||
setUpdateInfo(null);
|
||||
|
||||
try {
|
||||
@@ -49,8 +53,8 @@ export default function SystemUpdate() {
|
||||
setCheckResult('latest');
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('[SystemUpdate] 检查更新失败:', err);
|
||||
setError(err instanceof Error ? err.message : '检查更新失败');
|
||||
console.error('[Settings] 检查更新失败:', err);
|
||||
setUpdateError(err instanceof Error ? err.message : '检查更新失败');
|
||||
setCheckResult('none');
|
||||
} finally {
|
||||
setChecking(false);
|
||||
@@ -64,9 +68,8 @@ export default function SystemUpdate() {
|
||||
setProgress(0);
|
||||
setDownloadedBytes(0);
|
||||
setTotalBytes(0);
|
||||
setError(null);
|
||||
setUpdateError(null);
|
||||
|
||||
// 用局部变量保存总大小,避免 Progress 回调里的闭包问题
|
||||
let totalSize = 0;
|
||||
|
||||
try {
|
||||
@@ -94,8 +97,8 @@ export default function SystemUpdate() {
|
||||
setDownloading(false);
|
||||
setInstalling(true);
|
||||
} catch (err) {
|
||||
console.error('[SystemUpdate] 下载安装失败:', err);
|
||||
setError(err instanceof Error ? err.message : '下载安装失败');
|
||||
console.error('[Settings] 下载安装失败:', err);
|
||||
setUpdateError(err instanceof Error ? err.message : '下载安装失败');
|
||||
setDownloading(false);
|
||||
}
|
||||
};
|
||||
@@ -104,22 +107,57 @@ export default function SystemUpdate() {
|
||||
try {
|
||||
await relaunch();
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : '重启失败');
|
||||
setUpdateError(err instanceof Error ? err.message : '重启失败');
|
||||
}
|
||||
};
|
||||
|
||||
// ── 版本号连击 ──
|
||||
const handleVersionClick = useCallback(() => {
|
||||
clickCountRef.current += 1;
|
||||
|
||||
if (clickCountRef.current === 5) {
|
||||
setShowEnvModal(true);
|
||||
clickCountRef.current = 0;
|
||||
if (clickTimerRef.current) { clearTimeout(clickTimerRef.current); }
|
||||
return;
|
||||
}
|
||||
|
||||
if (clickTimerRef.current) { clearTimeout(clickTimerRef.current); }
|
||||
clickTimerRef.current = setTimeout(() => {
|
||||
clickCountRef.current = 0;
|
||||
}, 2000);
|
||||
}, []);
|
||||
|
||||
const handleSaveEnv = async (env: string) => {
|
||||
try {
|
||||
await saveAppConfig(env);
|
||||
|
||||
if (import.meta.env.DEV) {
|
||||
toast.success('配置已保存,即将刷新');
|
||||
setTimeout(() => { window.location.reload(); }, 500);
|
||||
return;
|
||||
}
|
||||
|
||||
toast.success('配置已保存,应用即将重启');
|
||||
setTimeout(() => { invoke('restart_app'); }, 800);
|
||||
} catch {
|
||||
toast.error('保存配置失败');
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="settings-page">
|
||||
<AppHeader title="系统更新" showBack onBack={() => navigate('profile')} />
|
||||
<AppHeader title="设置" showBack onBack={() => navigate('profile')} />
|
||||
|
||||
{/* 系统更新 */}
|
||||
<div className="settings-section">
|
||||
<h2>系统更新</h2>
|
||||
<div className="card" style={{ padding: 0, overflow: 'hidden' }}>
|
||||
{/* 当前版本 */}
|
||||
<div className="settings-row">
|
||||
<span className="settings-row-label">当前版本</span>
|
||||
<span className="settings-row-value">v{CURRENT_VERSION}</span>
|
||||
</div>
|
||||
|
||||
{/* 检查更新 */}
|
||||
<div className="settings-row" style={{ borderTop: '1px solid var(--border-light)' }}>
|
||||
<span className="settings-row-label">版本更新</span>
|
||||
<div className="settings-row-value" style={{ display: 'flex', alignItems: 'center', gap: 'var(--spacing-md)', justifyContent: 'flex-end' }}>
|
||||
@@ -141,7 +179,6 @@ export default function SystemUpdate() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 更新详情 & 操作 */}
|
||||
{checkResult === 'available' && updateInfo && (
|
||||
<div style={{ padding: '16px 20px', borderTop: '1px solid var(--border-light)', background: 'var(--bg-input)' }}>
|
||||
{updateInfo.body && (
|
||||
@@ -151,7 +188,6 @@ export default function SystemUpdate() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 下载进度 */}
|
||||
{downloading && (
|
||||
<div style={{ marginBottom: 12 }}>
|
||||
<div style={{ height: 6, background: 'var(--border-light)', borderRadius: 3, overflow: 'hidden' }}>
|
||||
@@ -164,12 +200,9 @@ export default function SystemUpdate() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 按钮 */}
|
||||
<div style={{ display: 'flex', gap: 8, justifyContent: 'flex-end' }}>
|
||||
{installing ? (
|
||||
<button className="btn btn-primary btn-sm" onClick={handleRelaunch}>
|
||||
立即重启
|
||||
</button>
|
||||
<button className="btn btn-primary btn-sm" onClick={handleRelaunch}>立即重启</button>
|
||||
) : (
|
||||
<button className="btn btn-primary btn-sm" onClick={handleDownloadAndInstall} disabled={downloading}>
|
||||
{downloading ? '下载中...' : '立即更新'}
|
||||
@@ -179,14 +212,68 @@ export default function SystemUpdate() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 错误 */}
|
||||
{error && (
|
||||
{updateError && (
|
||||
<div style={{ padding: '12px 20px', borderTop: '1px solid var(--border-light)', background: '#fef2f2', color: '#dc2626', fontSize: 'var(--font-sm)' }}>
|
||||
{error}
|
||||
{updateError}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 关于我们 */}
|
||||
<div className="settings-section">
|
||||
<h2>关于我们</h2>
|
||||
<div className="card" style={{ padding: 0, overflow: 'hidden' }}>
|
||||
<div className="settings-row">
|
||||
<span className="settings-row-label">应用名称</span>
|
||||
<span className="settings-row-value">美家卡 智影</span>
|
||||
</div>
|
||||
<div className="settings-row" style={{ borderTop: '1px solid var(--border-light)' }}>
|
||||
<span className="settings-row-label">版本号</span>
|
||||
<span
|
||||
className="settings-row-value"
|
||||
onClick={handleVersionClick}
|
||||
style={{ cursor: 'default', userSelect: 'none' }}
|
||||
title={appEnvironment !== 'production' ? `当前环境: ${appEnvironment}` : undefined}
|
||||
>
|
||||
v{CURRENT_VERSION}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 授权信息 */}
|
||||
<div className="settings-section">
|
||||
<h2>授权信息</h2>
|
||||
<div className="card" style={{ padding: 'var(--spacing-xl)' }}>
|
||||
<p style={{ lineHeight: 1.8, margin: 0 }}>
|
||||
本软件由美家卡团队开发维护。授权用户可在授权范围内使用本软件进行视频创作。
|
||||
如需商业授权或有任何疑问,请联系我们的支持团队。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 版权声明 */}
|
||||
<div className="settings-section">
|
||||
<h2>版权声明</h2>
|
||||
<div className="card" style={{ padding: 'var(--spacing-xl)' }}>
|
||||
<p style={{ lineHeight: 1.8, margin: 0 }}>
|
||||
Copyright 2025 美家卡 (meijiaka.cn). All rights reserved.
|
||||
</p>
|
||||
<p style={{ lineHeight: 1.8, marginTop: 'var(--spacing-md)', marginBottom: 0 }}>
|
||||
本软件及其相关文档的所有权利均归美家卡所有。
|
||||
未经授权,不得复制、修改、分发或以其他方式使用本软件。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<EnvironmentSwitchModal
|
||||
key={appEnvironment}
|
||||
open={showEnvModal}
|
||||
currentEnv={appEnvironment}
|
||||
onSave={handleSaveEnv}
|
||||
onCancel={() => setShowEnvModal(false)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user