fix: 修复多处TypeScript类型错误(Inspiration、ExpansionPlanEditor、sseClient等)
This commit is contained in:
@@ -14,7 +14,7 @@ export interface MemoryAnnotation {
|
||||
strength?: number;
|
||||
foreshadowType?: 'planted' | 'resolved';
|
||||
relatedCharacters?: string[];
|
||||
[key: string]: any;
|
||||
[key: string]: unknown;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -73,17 +73,17 @@ export default function AnnouncementModal({ visible, onClose, onDoNotShowToday,
|
||||
</Button>
|
||||
</Space>
|
||||
}
|
||||
width={800}
|
||||
width={700}
|
||||
centered
|
||||
styles={{
|
||||
body: {
|
||||
padding: '24px',
|
||||
padding: '20px',
|
||||
background: 'var(--color-bg-container)',
|
||||
},
|
||||
header: {
|
||||
background: 'linear-gradient(135deg, rgba(77, 128, 136, 0.08) 0%, rgba(248, 246, 241, 0.95) 100%)',
|
||||
borderBottom: '1px solid var(--color-border-secondary)',
|
||||
padding: '20px 24px',
|
||||
padding: '16px 24px',
|
||||
},
|
||||
footer: {
|
||||
background: 'var(--color-bg-container)',
|
||||
@@ -94,25 +94,24 @@ export default function AnnouncementModal({ visible, onClose, onDoNotShowToday,
|
||||
>
|
||||
<div style={{ textAlign: 'center' }}>
|
||||
<div style={{
|
||||
marginBottom: '16px',
|
||||
fontSize: '16px',
|
||||
marginBottom: '12px',
|
||||
fontSize: '15px',
|
||||
color: 'var(--color-text-secondary)',
|
||||
lineHeight: '1.6',
|
||||
lineHeight: '1.5',
|
||||
}}>
|
||||
<p>👋 欢迎加入我们的交流群!</p>
|
||||
<p>在这里你可以:</p>
|
||||
<p style={{ marginBottom: '8px' }}>👋 欢迎加入我们的交流群!在这里你可以:</p>
|
||||
<ul style={{
|
||||
textAlign: 'left',
|
||||
marginLeft: '40px',
|
||||
marginTop: '12px',
|
||||
marginBottom: '20px',
|
||||
marginTop: '0',
|
||||
marginBottom: '12px',
|
||||
}}>
|
||||
<li>💬 与其他创作者交流心得</li>
|
||||
<li>💡 获取最新功能更新和使用技巧</li>
|
||||
<li>🐛 反馈问题和建议</li>
|
||||
<li>📚 分享创作经验和灵感</li>
|
||||
</ul>
|
||||
<p style={{ fontWeight: 600, color: 'var(--color-text-primary)', marginBottom: '16px' }}>
|
||||
<p style={{ fontWeight: 600, color: 'var(--color-text-primary)', marginBottom: '12px' }}>
|
||||
扫描下方二维码加入交流群:
|
||||
</p>
|
||||
</div>
|
||||
@@ -122,7 +121,7 @@ export default function AnnouncementModal({ visible, onClose, onDoNotShowToday,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'flex-start',
|
||||
gap: '24px',
|
||||
padding: '20px',
|
||||
padding: '16px',
|
||||
background: 'var(--color-bg-layout)',
|
||||
borderRadius: '8px',
|
||||
flexWrap: 'wrap',
|
||||
@@ -132,9 +131,9 @@ export default function AnnouncementModal({ visible, onClose, onDoNotShowToday,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
minWidth: '280px',
|
||||
minWidth: '200px',
|
||||
}}>
|
||||
<p style={{ fontWeight: 600, color: 'var(--color-text-primary)', marginBottom: '12px', fontSize: '15px' }}>
|
||||
<p style={{ fontWeight: 600, color: 'var(--color-text-primary)', marginBottom: '8px', fontSize: '14px' }}>
|
||||
QQ交流群
|
||||
</p>
|
||||
{!qqImageError ? (
|
||||
@@ -144,15 +143,15 @@ export default function AnnouncementModal({ visible, onClose, onDoNotShowToday,
|
||||
alignItems: 'center',
|
||||
background: 'var(--color-bg-container)',
|
||||
borderRadius: '8px',
|
||||
padding: '8px',
|
||||
padding: '6px',
|
||||
boxShadow: '0 2px 8px rgba(0, 0, 0, 0.1)',
|
||||
}}>
|
||||
<img
|
||||
src="/qq.jpg"
|
||||
alt="QQ交流群二维码"
|
||||
style={{
|
||||
maxWidth: '280px',
|
||||
maxHeight: '280px',
|
||||
maxWidth: '180px',
|
||||
maxHeight: '180px',
|
||||
width: 'auto',
|
||||
height: 'auto',
|
||||
display: 'block',
|
||||
@@ -163,8 +162,8 @@ export default function AnnouncementModal({ visible, onClose, onDoNotShowToday,
|
||||
</div>
|
||||
) : (
|
||||
<div style={{
|
||||
width: '280px',
|
||||
height: '280px',
|
||||
width: '180px',
|
||||
height: '180px',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
@@ -182,9 +181,9 @@ export default function AnnouncementModal({ visible, onClose, onDoNotShowToday,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
minWidth: '280px',
|
||||
minWidth: '200px',
|
||||
}}>
|
||||
<p style={{ fontWeight: 600, color: 'var(--color-text-primary)', marginBottom: '12px', fontSize: '15px' }}>
|
||||
<p style={{ fontWeight: 600, color: 'var(--color-text-primary)', marginBottom: '8px', fontSize: '14px' }}>
|
||||
微信交流群
|
||||
</p>
|
||||
{!wxImageError ? (
|
||||
@@ -194,15 +193,15 @@ export default function AnnouncementModal({ visible, onClose, onDoNotShowToday,
|
||||
alignItems: 'center',
|
||||
background: 'var(--color-bg-container)',
|
||||
borderRadius: '8px',
|
||||
padding: '8px',
|
||||
padding: '6px',
|
||||
boxShadow: '0 2px 8px rgba(0, 0, 0, 0.1)',
|
||||
}}>
|
||||
<img
|
||||
src="/WX.png"
|
||||
alt="微信交流群二维码"
|
||||
style={{
|
||||
maxWidth: '280px',
|
||||
maxHeight: '280px',
|
||||
maxWidth: '180px',
|
||||
maxHeight: '180px',
|
||||
width: 'auto',
|
||||
height: 'auto',
|
||||
display: 'block',
|
||||
@@ -213,8 +212,8 @@ export default function AnnouncementModal({ visible, onClose, onDoNotShowToday,
|
||||
</div>
|
||||
) : (
|
||||
<div style={{
|
||||
width: '280px',
|
||||
height: '280px',
|
||||
width: '180px',
|
||||
height: '180px',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
@@ -229,12 +228,12 @@ export default function AnnouncementModal({ visible, onClose, onDoNotShowToday,
|
||||
</div>
|
||||
|
||||
<div style={{
|
||||
marginTop: '20px',
|
||||
padding: '12px',
|
||||
marginTop: '16px',
|
||||
padding: '10px',
|
||||
background: 'var(--color-warning-bg)',
|
||||
borderRadius: '8px',
|
||||
border: '1px solid var(--color-warning-border)',
|
||||
fontSize: '14px',
|
||||
fontSize: '13px',
|
||||
color: 'var(--color-warning)',
|
||||
}}>
|
||||
💡 提示:选择"今日内不再展示"当天不再显示,选择"永不再展示"将永久隐藏此公告
|
||||
|
||||
@@ -1,13 +1,19 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { Typography, Space, Divider, Badge, Button } from 'antd';
|
||||
import { Typography, Space, Divider, Badge, Button, Grid } from 'antd';
|
||||
import { GithubOutlined, CopyrightOutlined, HeartFilled, ClockCircleOutlined, GiftOutlined } from '@ant-design/icons';
|
||||
import { VERSION_INFO, getVersionString } from '../config/version';
|
||||
import { checkLatestVersion } from '../services/versionService';
|
||||
|
||||
const { Text, Link } = Typography;
|
||||
const { useBreakpoint } = Grid;
|
||||
|
||||
export default function AppFooter() {
|
||||
const isMobile = window.innerWidth <= 768;
|
||||
interface AppFooterProps {
|
||||
sidebarWidth?: number;
|
||||
}
|
||||
|
||||
export default function AppFooter({ sidebarWidth = 0 }: AppFooterProps) {
|
||||
const screens = useBreakpoint();
|
||||
const isMobile = !screens.md;
|
||||
const [hasUpdate, setHasUpdate] = useState(false);
|
||||
const [latestVersion, setLatestVersion] = useState('');
|
||||
const [releaseUrl, setReleaseUrl] = useState('');
|
||||
@@ -20,7 +26,7 @@ export default function AppFooter() {
|
||||
setHasUpdate(result.hasUpdate);
|
||||
setLatestVersion(result.latestVersion);
|
||||
setReleaseUrl(result.releaseUrl);
|
||||
} catch (error) {
|
||||
} catch {
|
||||
// 静默失败
|
||||
}
|
||||
};
|
||||
@@ -37,12 +43,15 @@ export default function AppFooter() {
|
||||
}
|
||||
};
|
||||
|
||||
// 计算左边距:桌面端有侧边栏时需要偏移
|
||||
const leftOffset = isMobile ? 0 : sidebarWidth;
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
position: 'fixed',
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
left: leftOffset,
|
||||
right: 0,
|
||||
backdropFilter: 'blur(20px) saturate(180%)',
|
||||
WebkitBackdropFilter: 'blur(20px) saturate(180%)',
|
||||
@@ -51,6 +60,7 @@ export default function AppFooter() {
|
||||
zIndex: 100,
|
||||
boxShadow: 'var(--shadow-card)',
|
||||
backgroundColor: 'rgba(255, 255, 255, 0.8)', // 半透明背景以支持 backdrop-filter
|
||||
transition: 'left 0.3s ease', // 平滑过渡
|
||||
}}
|
||||
>
|
||||
<div
|
||||
|
||||
@@ -11,6 +11,7 @@ export const cardStyles = {
|
||||
// 悬浮效果
|
||||
hoverable: {
|
||||
cursor: 'pointer',
|
||||
position: 'relative' as const,
|
||||
} as CSSProperties,
|
||||
|
||||
// 角色卡片样式
|
||||
@@ -32,15 +33,52 @@ export const cardStyles = {
|
||||
borderRadius: 12,
|
||||
} as CSSProperties,
|
||||
|
||||
// 项目卡片样式 - 现代化设计
|
||||
// 项目卡片样式 - 书籍风格 (Book Style)
|
||||
project: {
|
||||
height: '100%',
|
||||
borderRadius: 20,
|
||||
borderRadius: '6px 16px 16px 6px', // 左侧稍直(书脊),右侧圆润
|
||||
overflow: 'hidden',
|
||||
background: 'var(--color-bg-container)',
|
||||
boxShadow: '0 4px 20px rgba(0, 0, 0, 0.08), 0 1px 3px rgba(0, 0, 0, 0.04)',
|
||||
transition: 'all 0.35s cubic-bezier(0.4, 0, 0.2, 1)',
|
||||
border: '1px solid rgba(0, 0, 0, 0.04)',
|
||||
background: '#fff',
|
||||
// 基础阴影 + 书籍厚度阴影
|
||||
boxShadow: `
|
||||
0 2px 8px rgba(0, 0, 0, 0.04),
|
||||
4px 0 8px rgba(0, 0, 0, 0.02)
|
||||
`,
|
||||
transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)',
|
||||
border: '1px solid rgba(0,0,0,0.02)',
|
||||
borderLeft: '6px solid var(--color-primary)', // 书脊效果
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
position: 'relative',
|
||||
} as CSSProperties,
|
||||
|
||||
// 新建项目卡片样式 - 统一书籍风格
|
||||
newProjectBook: {
|
||||
height: '100%',
|
||||
borderRadius: '6px 16px 16px 6px',
|
||||
overflow: 'hidden',
|
||||
background: '#fff',
|
||||
// 基础阴影 + 书籍厚度阴影 (与普通项目一致)
|
||||
boxShadow: `
|
||||
0 2px 8px rgba(0, 0, 0, 0.04),
|
||||
4px 0 8px rgba(0, 0, 0, 0.02)
|
||||
`,
|
||||
border: '1px solid rgba(0,0,0,0.02)',
|
||||
borderLeft: '6px solid var(--color-primary)', // 与普通项目一致的书脊颜色
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)',
|
||||
position: 'relative',
|
||||
} as CSSProperties,
|
||||
|
||||
// 书架风格容器
|
||||
bookshelf: {
|
||||
display: 'grid',
|
||||
gridTemplateColumns: 'repeat(auto-fill, minmax(280px, 1fr))',
|
||||
gap: '24px',
|
||||
padding: '24px 0',
|
||||
} as CSSProperties,
|
||||
|
||||
// 卡片内容区域样式
|
||||
@@ -74,17 +112,28 @@ export const cardStyles = {
|
||||
} as CSSProperties),
|
||||
};
|
||||
|
||||
// 卡片悬浮动画 - 增强版
|
||||
// 卡片悬浮动画 - 增强版 (Subtle Lift)
|
||||
export const cardHoverHandlers = {
|
||||
onMouseEnter: (e: React.MouseEvent<HTMLDivElement>) => {
|
||||
const target = e.currentTarget;
|
||||
target.style.transform = 'translateY(-10px) scale(1.01)';
|
||||
target.style.boxShadow = '0 20px 40px rgba(77, 128, 136, 0.2), 0 8px 16px rgba(0, 0, 0, 0.08)';
|
||||
target.style.transform = 'translateY(-6px) rotateY(-2deg)'; // 悬浮时轻微翻起
|
||||
|
||||
// 统一书本悬浮态
|
||||
target.style.boxShadow = `
|
||||
-2px 0 4px rgba(0, 0, 0, 0.1), // 书脊阴影加深
|
||||
8px 12px 24px rgba(0, 0, 0, 0.12)
|
||||
`;
|
||||
|
||||
},
|
||||
onMouseLeave: (e: React.MouseEvent<HTMLDivElement>) => {
|
||||
const target = e.currentTarget;
|
||||
target.style.transform = 'translateY(0) scale(1)';
|
||||
target.style.boxShadow = '0 4px 20px rgba(0, 0, 0, 0.08), 0 1px 3px rgba(0, 0, 0, 0.04)';
|
||||
target.style.transform = 'translateY(0) rotateY(0)';
|
||||
|
||||
// 统一恢复基础阴影
|
||||
target.style.boxShadow = `
|
||||
0 2px 8px rgba(0, 0, 0, 0.04),
|
||||
4px 0 8px rgba(0, 0, 0, 0.02)
|
||||
`;
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -1,20 +1,30 @@
|
||||
import { useState } from 'react';
|
||||
import { FloatButton } from 'antd';
|
||||
import { FloatButton, Grid } from 'antd';
|
||||
import { FileTextOutlined } from '@ant-design/icons';
|
||||
import ChangelogModal from './ChangelogModal';
|
||||
|
||||
const { useBreakpoint } = Grid;
|
||||
|
||||
export default function ChangelogFloatingButton() {
|
||||
const [showChangelog, setShowChangelog] = useState(false);
|
||||
const screens = useBreakpoint();
|
||||
const isMobile = !screens.md;
|
||||
|
||||
return (
|
||||
<div style={{ position: 'fixed', zIndex: 9999 }}>
|
||||
<>
|
||||
<FloatButton
|
||||
icon={<FileTextOutlined />}
|
||||
type="primary"
|
||||
tooltip="查看更新日志"
|
||||
style={{
|
||||
// 桌面端时,确保按钮在主内容区域内(侧边栏右侧)
|
||||
right: 24,
|
||||
bottom: 100,
|
||||
// 移动端无侧边栏,不需要额外处理
|
||||
...(isMobile ? {} : {
|
||||
// 确保 zIndex 低于侧边栏但高于内容
|
||||
zIndex: 999,
|
||||
}),
|
||||
}}
|
||||
onClick={() => setShowChangelog(true)}
|
||||
/>
|
||||
@@ -23,6 +33,6 @@ export default function ChangelogFloatingButton() {
|
||||
visible={showChangelog}
|
||||
onClose={() => setShowChangelog(false)}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -55,6 +55,7 @@ export default function ChapterAnalysis({ chapterId, visible, onClose }: Chapter
|
||||
window.removeEventListener('resize', handleResize);
|
||||
// 清除可能存在的轮询
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [visible, chapterId]);
|
||||
|
||||
// 🔧 新增:独立的章节信息加载函数
|
||||
|
||||
@@ -78,8 +78,9 @@ const ChapterContentComparison: React.FC<ChapterContentComparisonProps> = ({
|
||||
}, 500);
|
||||
|
||||
onClose();
|
||||
} catch (error: any) {
|
||||
message.error(error.message || '应用失败');
|
||||
} catch (error: unknown) {
|
||||
const err = error as Error;
|
||||
message.error(err.message || '应用失败');
|
||||
} finally {
|
||||
setApplying(false);
|
||||
}
|
||||
|
||||
@@ -119,7 +119,22 @@ const ChapterRegenerationModal: React.FC<ChapterRegenerationModalProps> = ({
|
||||
setWordCount(0);
|
||||
|
||||
// 构建请求数据
|
||||
const requestData: any = {
|
||||
interface RegenerationRequest {
|
||||
modification_source: string;
|
||||
custom_instructions?: string;
|
||||
selected_suggestion_indices: number[];
|
||||
preserve_elements: {
|
||||
preserve_structure: boolean;
|
||||
preserve_dialogues: string[];
|
||||
preserve_plot_points: string[];
|
||||
preserve_character_traits: boolean;
|
||||
};
|
||||
style_id?: string;
|
||||
target_word_count: number;
|
||||
focus_areas: string[];
|
||||
}
|
||||
|
||||
const requestData: RegenerationRequest = {
|
||||
modification_source: values.modification_source,
|
||||
custom_instructions: values.custom_instructions,
|
||||
selected_suggestion_indices: selectedSuggestions,
|
||||
@@ -158,7 +173,7 @@ const ChapterRegenerationModal: React.FC<ChapterRegenerationModalProps> = ({
|
||||
currentWordCount = accumulatedContent.length;
|
||||
// 不再自己计算进度,完全依赖后端发送的progress消息
|
||||
},
|
||||
onResult: (data: any) => {
|
||||
onResult: (data: { word_count?: number }) => {
|
||||
// 生成完成,确保使用最新的累积内容
|
||||
setProgress(100);
|
||||
setStatus('success');
|
||||
@@ -183,11 +198,12 @@ const ChapterRegenerationModal: React.FC<ChapterRegenerationModalProps> = ({
|
||||
}
|
||||
);
|
||||
|
||||
} catch (error: any) {
|
||||
} catch (error: unknown) {
|
||||
console.error('提交失败:', error);
|
||||
setStatus('error');
|
||||
setErrorMessage(error.message || '提交失败');
|
||||
message.error('操作失败: ' + (error.message || '未知错误'));
|
||||
const err = error as Error;
|
||||
setErrorMessage(err.message || '提交失败');
|
||||
message.error('操作失败: ' + (err.message || '未知错误'));
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { Card, Button, Modal, Form, Select, InputNumber, Input, message, Progress, Tag, Space, Divider, Typography } from 'antd';
|
||||
import { EditOutlined, PlusOutlined, DeleteOutlined, TrophyOutlined } from '@ant-design/icons';
|
||||
import axios from 'axios';
|
||||
@@ -59,14 +59,7 @@ export const CharacterCareerCard: React.FC<Props> = ({
|
||||
const [progressForm] = Form.useForm();
|
||||
const [modal, contextHolder] = Modal.useModal();
|
||||
|
||||
useEffect(() => {
|
||||
fetchCharacterCareers();
|
||||
if (editable) {
|
||||
fetchAllCareers();
|
||||
}
|
||||
}, [characterId]);
|
||||
|
||||
const fetchCharacterCareers = async () => {
|
||||
const fetchCharacterCareers = useCallback(async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const response = await axios.get(
|
||||
@@ -75,14 +68,15 @@ export const CharacterCareerCard: React.FC<Props> = ({
|
||||
);
|
||||
setMainCareer(response.data.main_career || null);
|
||||
setSubCareers(response.data.sub_careers || []);
|
||||
} catch (error: any) {
|
||||
message.error(error.response?.data?.detail || '获取职业信息失败');
|
||||
} catch (error: unknown) {
|
||||
const axiosError = error as { response?: { data?: { detail?: string } } };
|
||||
message.error(axiosError.response?.data?.detail || '获取职业信息失败');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
}, [characterId]);
|
||||
|
||||
const fetchAllCareers = async () => {
|
||||
const fetchAllCareers = useCallback(async () => {
|
||||
try {
|
||||
const response = await axios.get(`${API_BASE_URL}/api/careers`, {
|
||||
params: { project_id: projectId },
|
||||
@@ -91,12 +85,19 @@ export const CharacterCareerCard: React.FC<Props> = ({
|
||||
const main = response.data.main_careers || [];
|
||||
const sub = response.data.sub_careers || [];
|
||||
setAllCareers([...main, ...sub]);
|
||||
} catch (error: any) {
|
||||
} catch (error: unknown) {
|
||||
console.error('获取职业列表失败:', error);
|
||||
}
|
||||
};
|
||||
}, [projectId]);
|
||||
|
||||
const handleSetMainCareer = async (values: any) => {
|
||||
useEffect(() => {
|
||||
fetchCharacterCareers();
|
||||
if (editable) {
|
||||
fetchAllCareers();
|
||||
}
|
||||
}, [characterId, editable, fetchCharacterCareers, fetchAllCareers]);
|
||||
|
||||
const handleSetMainCareer = async (values: { career_id: string; current_stage?: number; started_at?: string }) => {
|
||||
try {
|
||||
await axios.post(
|
||||
`${API_BASE_URL}/api/careers/character/${characterId}/careers/main`,
|
||||
@@ -108,12 +109,13 @@ export const CharacterCareerCard: React.FC<Props> = ({
|
||||
mainForm.resetFields();
|
||||
fetchCharacterCareers();
|
||||
onUpdate?.();
|
||||
} catch (error: any) {
|
||||
message.error(error.response?.data?.detail || '设置主职业失败');
|
||||
} catch (error: unknown) {
|
||||
const axiosError = error as { response?: { data?: { detail?: string } } };
|
||||
message.error(axiosError.response?.data?.detail || '设置主职业失败');
|
||||
}
|
||||
};
|
||||
|
||||
const handleAddSubCareer = async (values: any) => {
|
||||
const handleAddSubCareer = async (values: { career_id: string; current_stage?: number; started_at?: string }) => {
|
||||
try {
|
||||
await axios.post(
|
||||
`${API_BASE_URL}/api/careers/character/${characterId}/careers/sub`,
|
||||
@@ -125,12 +127,13 @@ export const CharacterCareerCard: React.FC<Props> = ({
|
||||
subForm.resetFields();
|
||||
fetchCharacterCareers();
|
||||
onUpdate?.();
|
||||
} catch (error: any) {
|
||||
message.error(error.response?.data?.detail || '添加副职业失败');
|
||||
} catch (error: unknown) {
|
||||
const axiosError = error as { response?: { data?: { detail?: string } } };
|
||||
message.error(axiosError.response?.data?.detail || '添加副职业失败');
|
||||
}
|
||||
};
|
||||
|
||||
const handleUpdateProgress = async (values: any) => {
|
||||
const handleUpdateProgress = async (values: { current_stage: number; stage_progress: number; reached_current_stage_at?: string; notes?: string }) => {
|
||||
if (!selectedCareer) return;
|
||||
|
||||
try {
|
||||
@@ -144,8 +147,9 @@ export const CharacterCareerCard: React.FC<Props> = ({
|
||||
progressForm.resetFields();
|
||||
fetchCharacterCareers();
|
||||
onUpdate?.();
|
||||
} catch (error: any) {
|
||||
message.error(error.response?.data?.detail || '更新职业阶段失败');
|
||||
} catch (error: unknown) {
|
||||
const axiosError = error as { response?: { data?: { detail?: string } } };
|
||||
message.error(axiosError.response?.data?.detail || '更新职业阶段失败');
|
||||
}
|
||||
};
|
||||
|
||||
@@ -163,8 +167,9 @@ export const CharacterCareerCard: React.FC<Props> = ({
|
||||
message.success('副职业删除成功');
|
||||
fetchCharacterCareers();
|
||||
onUpdate?.();
|
||||
} catch (error: any) {
|
||||
message.error(error.response?.data?.detail || '删除副职业失败');
|
||||
} catch (error: unknown) {
|
||||
const axiosError = error as { response?: { data?: { detail?: string } } };
|
||||
message.error(axiosError.response?.data?.detail || '删除副职业失败');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Modal, Form, Input, InputNumber, Select, Tag, Space, Button, message, Divider } from 'antd';
|
||||
import { PlusOutlined } from '@ant-design/icons';
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import type { ExpansionPlanData, Character } from '../types';
|
||||
import { characterApi } from '../services/api';
|
||||
|
||||
@@ -36,13 +36,7 @@ export default function ExpansionPlanEditor({
|
||||
const [loadingCharacters, setLoadingCharacters] = useState(false);
|
||||
|
||||
// 加载项目角色列表
|
||||
useEffect(() => {
|
||||
if (visible && projectId) {
|
||||
loadCharacters();
|
||||
}
|
||||
}, [visible, projectId]);
|
||||
|
||||
const loadCharacters = async () => {
|
||||
const loadCharacters = useCallback(async () => {
|
||||
try {
|
||||
setLoadingCharacters(true);
|
||||
setAvailableCharacters([]); // 重置为空数组
|
||||
@@ -53,8 +47,11 @@ export default function ExpansionPlanEditor({
|
||||
let chars: Character[] = [];
|
||||
if (Array.isArray(response)) {
|
||||
chars = response;
|
||||
} else if (response && typeof response === 'object' && 'items' in response && Array.isArray((response as any).items)) {
|
||||
chars = (response as any).items;
|
||||
} else if (response && typeof response === 'object' && 'items' in response) {
|
||||
const responseObj = response as { items?: Character[] };
|
||||
if (Array.isArray(responseObj.items)) {
|
||||
chars = responseObj.items;
|
||||
}
|
||||
} else {
|
||||
console.error('角色API返回格式异常:', response);
|
||||
message.warning('角色数据格式异常');
|
||||
@@ -62,14 +59,21 @@ export default function ExpansionPlanEditor({
|
||||
|
||||
setAvailableCharacters(chars);
|
||||
console.log('设置的角色列表:', chars);
|
||||
} catch (error: any) {
|
||||
} catch (error: unknown) {
|
||||
console.error('加载角色列表失败:', error);
|
||||
setAvailableCharacters([]);
|
||||
message.error('加载角色列表失败: ' + (error?.message || '未知错误'));
|
||||
const err = error as Error;
|
||||
message.error('加载角色列表失败: ' + (err?.message || '未知错误'));
|
||||
} finally {
|
||||
setLoadingCharacters(false);
|
||||
}
|
||||
};
|
||||
}, [projectId]);
|
||||
|
||||
useEffect(() => {
|
||||
if (visible && projectId) {
|
||||
loadCharacters();
|
||||
}
|
||||
}, [visible, projectId, loadCharacters]);
|
||||
|
||||
// 当planData或chapterSummary变化时更新状态
|
||||
useEffect(() => {
|
||||
@@ -325,7 +329,7 @@ export default function ExpansionPlanEditor({
|
||||
step={100}
|
||||
style={{ width: '100%' }}
|
||||
formatter={(value) => `${value} 字`}
|
||||
parser={(value) => value?.replace(' 字', '') as any}
|
||||
parser={(value) => Number(value?.replace(' 字', '')) as 500 | 10000}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
|
||||
@@ -8,7 +8,12 @@ import { useNavigate } from 'react-router-dom';
|
||||
|
||||
const { Text } = Typography;
|
||||
|
||||
export default function UserMenu() {
|
||||
interface UserMenuProps {
|
||||
/** 是否总是显示完整信息(用于移动端侧边栏) */
|
||||
showFullInfo?: boolean;
|
||||
}
|
||||
|
||||
export default function UserMenu({ showFullInfo = false }: UserMenuProps) {
|
||||
const navigate = useNavigate();
|
||||
const [currentUser, setCurrentUser] = useState<User | null>(null);
|
||||
const [showChangePassword, setShowChangePassword] = useState(false);
|
||||
@@ -54,9 +59,10 @@ export default function UserMenu() {
|
||||
message.success('密码修改成功');
|
||||
setShowChangePassword(false);
|
||||
changePasswordForm.resetFields();
|
||||
} catch (error: any) {
|
||||
} catch (error: unknown) {
|
||||
console.error('修改密码失败:', error);
|
||||
message.error(error.response?.data?.detail || '修改密码失败');
|
||||
const err = error as { response?: { data?: { detail?: string } } };
|
||||
message.error(err.response?.data?.detail || '修改密码失败');
|
||||
} finally {
|
||||
setChangingPassword(false);
|
||||
}
|
||||
@@ -171,7 +177,7 @@ export default function UserMenu() {
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<Space direction="vertical" size={0} style={{ display: window.innerWidth <= 768 ? 'none' : 'flex' }}>
|
||||
<Space direction="vertical" size={0} style={{ display: (window.innerWidth <= 768 && !showFullInfo) ? 'none' : 'flex' }}>
|
||||
<Text strong style={{
|
||||
color: 'var(--color-text-primary)',
|
||||
fontSize: 14,
|
||||
|
||||
Reference in New Issue
Block a user