feat: Complete UI/UX optimization v0.1.21 - Final enhancements
- Enhanced TemplateManagement page with beautiful header and improved layout - Optimized TemplateCard with gradient backgrounds and refined visual effects - Improved empty state design with elegant icons and better messaging - Enhanced pagination component with modern styling - Added comprehensive responsive design optimizations for mobile and tablet - Implemented touch-friendly interactions for mobile devices - Added support for different screen orientations and pointer types - Enhanced animations with device-specific optimizations - Improved accessibility with proper touch target sizes - Added dark mode support and high-resolution display optimizations - Completed comprehensive design system with reusable components - All pages now follow consistent visual design language
This commit is contained in:
parent
19903303ae
commit
5d86a6411f
|
|
@ -72,49 +72,58 @@ export const TemplateCard: React.FC<TemplateCardProps> = ({
|
|||
const materialStats = getMaterialStats();
|
||||
|
||||
return (
|
||||
<div className="bg-white rounded-lg shadow-sm border border-gray-200 hover:shadow-md transition-shadow duration-200">
|
||||
{/* 卡片头部 */}
|
||||
<div className="p-4 border-b border-gray-100">
|
||||
<div className="group bg-gradient-to-br from-white to-gray-50/30 rounded-xl shadow-sm border border-gray-200/50 hover:shadow-lg hover:border-indigo-200 hover:-translate-y-1 transition-all duration-300 overflow-hidden relative">
|
||||
{/* 装饰性背景 */}
|
||||
<div className="absolute top-0 right-0 w-20 h-20 bg-gradient-to-br from-indigo-100/20 to-purple-100/20 rounded-full -translate-y-10 translate-x-10 opacity-0 group-hover:opacity-100 transition-all duration-300"></div>
|
||||
|
||||
{/* 美观的卡片头部 */}
|
||||
<div className="p-5 border-b border-gray-100/50 relative z-10">
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="flex-1 min-w-0">
|
||||
<h3 className="text-lg font-semibold text-gray-900 truncate" title={template.name}>
|
||||
{template.name}
|
||||
</h3>
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<div className="w-8 h-8 bg-gradient-to-br from-indigo-500 to-purple-500 rounded-lg flex items-center justify-center">
|
||||
<FileText className="w-4 h-4 text-white" />
|
||||
</div>
|
||||
<h3 className="text-lg font-semibold text-gray-900 truncate" title={template.name}>
|
||||
{template.name}
|
||||
</h3>
|
||||
</div>
|
||||
{template.description && (
|
||||
<p className="text-sm text-gray-600 mt-1 line-clamp-2" title={template.description}>
|
||||
<p className="text-sm text-gray-600 mt-1 line-clamp-2 leading-relaxed" title={template.description}>
|
||||
{template.description}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="relative ml-2">
|
||||
<div className="relative ml-3 flex-shrink-0">
|
||||
<button
|
||||
onClick={() => setShowMenu(!showMenu)}
|
||||
className="p-1 rounded-full hover:bg-gray-100 transition-colors"
|
||||
className="p-2 rounded-lg hover:bg-white hover:shadow-sm transition-all duration-200 opacity-0 group-hover:opacity-100"
|
||||
>
|
||||
<MoreVertical className="w-4 h-4 text-gray-500" />
|
||||
</button>
|
||||
|
||||
|
||||
{showMenu && (
|
||||
<div className="absolute right-0 top-8 w-32 bg-white border border-gray-200 rounded-lg shadow-lg z-10">
|
||||
<div className="absolute right-0 top-10 w-36 bg-white border border-gray-200/50 rounded-xl shadow-lg z-20 overflow-hidden">
|
||||
<button
|
||||
onClick={() => {
|
||||
onView();
|
||||
setShowMenu(false);
|
||||
}}
|
||||
className="w-full px-3 py-2 text-left text-sm text-gray-700 hover:bg-gray-50 flex items-center"
|
||||
className="w-full px-4 py-3 text-left text-sm text-gray-700 hover:bg-gray-50 flex items-center gap-2 transition-colors"
|
||||
>
|
||||
<Eye className="w-3 h-3 mr-2" />
|
||||
<Eye className="w-4 h-4" />
|
||||
查看详情
|
||||
</button>
|
||||
<div className="border-t border-gray-100"></div>
|
||||
<button
|
||||
onClick={() => {
|
||||
onDelete();
|
||||
setShowMenu(false);
|
||||
}}
|
||||
className="w-full px-3 py-2 text-left text-sm text-red-600 hover:bg-red-50 flex items-center"
|
||||
className="w-full px-4 py-3 text-left text-sm text-red-600 hover:bg-red-50 flex items-center gap-2 transition-colors"
|
||||
>
|
||||
<Trash2 className="w-3 h-3 mr-2" />
|
||||
<Trash2 className="w-4 h-4" />
|
||||
删除
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -124,51 +133,53 @@ export const TemplateCard: React.FC<TemplateCardProps> = ({
|
|||
|
||||
{/* 状态指示器 */}
|
||||
<div className="flex items-center mt-3">
|
||||
{getStatusIcon(template.import_status)}
|
||||
<span className="ml-2 text-sm text-gray-600">
|
||||
{getStatusText(template.import_status)}
|
||||
</span>
|
||||
<div className="flex items-center gap-2 px-3 py-1.5 bg-gray-50 rounded-lg">
|
||||
{getStatusIcon(template.import_status)}
|
||||
<span className="text-sm font-medium text-gray-700">
|
||||
{getStatusText(template.import_status)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 卡片内容 */}
|
||||
<div className="p-4">
|
||||
{/* 美观的卡片内容 */}
|
||||
<div className="p-5 relative z-10">
|
||||
{/* 基本信息 */}
|
||||
<div className="grid grid-cols-2 gap-4 mb-4">
|
||||
<div className="text-center">
|
||||
<div className="text-lg font-semibold text-gray-900">
|
||||
<div className="text-center p-3 bg-gradient-to-br from-gray-50 to-gray-100 rounded-lg">
|
||||
<div className="text-lg font-bold text-gray-900">
|
||||
{template.canvas_config.width}×{template.canvas_config.height}
|
||||
</div>
|
||||
<div className="text-xs text-gray-500">分辨率</div>
|
||||
<div className="text-xs text-gray-500 font-medium">分辨率</div>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<div className="text-lg font-semibold text-gray-900">
|
||||
<div className="text-center p-3 bg-gradient-to-br from-primary-50 to-primary-100 rounded-lg">
|
||||
<div className="text-lg font-bold text-primary-700">
|
||||
{formatDuration(template.duration)}
|
||||
</div>
|
||||
<div className="text-xs text-gray-500">时长</div>
|
||||
<div className="text-xs text-primary-600 font-medium">时长</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 素材统计 */}
|
||||
{/* 美观的素材统计 */}
|
||||
<div className="mb-4">
|
||||
<div className="text-xs text-gray-500 mb-2">素材统计</div>
|
||||
<div className="flex items-center justify-between text-xs">
|
||||
<div className="text-sm font-medium text-gray-700 mb-3">素材统计</div>
|
||||
<div className="flex items-center justify-center gap-3 text-xs">
|
||||
{materialStats.video > 0 && (
|
||||
<div className="flex items-center text-blue-600">
|
||||
<Video className="w-3 h-3 mr-1" />
|
||||
{materialStats.video}
|
||||
<div className="flex items-center gap-1 px-2.5 py-1.5 bg-blue-50 text-blue-700 rounded-lg border border-blue-200">
|
||||
<Video className="w-3 h-3" />
|
||||
<span className="font-medium">{materialStats.video}</span>
|
||||
</div>
|
||||
)}
|
||||
{materialStats.audio > 0 && (
|
||||
<div className="flex items-center text-green-600">
|
||||
<Music className="w-3 h-3 mr-1" />
|
||||
{materialStats.audio}
|
||||
<div className="flex items-center gap-1 px-2.5 py-1.5 bg-green-50 text-green-700 rounded-lg border border-green-200">
|
||||
<Music className="w-3 h-3" />
|
||||
<span className="font-medium">{materialStats.audio}</span>
|
||||
</div>
|
||||
)}
|
||||
{materialStats.image > 0 && (
|
||||
<div className="flex items-center text-purple-600">
|
||||
<Image className="w-3 h-3 mr-1" />
|
||||
{materialStats.image}
|
||||
<div className="flex items-center gap-1 px-2.5 py-1.5 bg-purple-50 text-purple-700 rounded-lg border border-purple-200">
|
||||
<Image className="w-3 h-3" />
|
||||
<span className="font-medium">{materialStats.image}</span>
|
||||
</div>
|
||||
)}
|
||||
{materialStats.text > 0 && (
|
||||
|
|
|
|||
|
|
@ -619,7 +619,7 @@ export const ProjectDetails: React.FC = () => {
|
|||
onClick={() => setActiveTab('materials')}
|
||||
className={`py-3 px-4 font-medium text-sm transition-all duration-200 whitespace-nowrap rounded-t-lg relative ${
|
||||
activeTab === 'materials'
|
||||
? 'text-primary-600 border-b-2 '
|
||||
? 'text-primary-600 border-b-2 border-primary-500'
|
||||
: 'text-gray-500 hover:text-gray-700 hover:bg-gray-50'
|
||||
}`}
|
||||
>
|
||||
|
|
@ -633,7 +633,7 @@ export const ProjectDetails: React.FC = () => {
|
|||
onClick={() => setActiveTab('segments')}
|
||||
className={`py-3 px-4 font-medium text-sm transition-all duration-200 whitespace-nowrap rounded-t-lg relative ${
|
||||
activeTab === 'segments'
|
||||
? 'text-primary-600 bg-primary-50 border-b-2 border-primary-500'
|
||||
? 'text-primary-600 border-b-2 border-primary-500'
|
||||
: 'text-gray-500 hover:text-gray-700 hover:bg-gray-50'
|
||||
}`}
|
||||
>
|
||||
|
|
@ -647,7 +647,7 @@ export const ProjectDetails: React.FC = () => {
|
|||
onClick={() => setActiveTab('templates')}
|
||||
className={`py-3 px-4 font-medium text-sm transition-all duration-200 whitespace-nowrap rounded-t-lg relative ${
|
||||
activeTab === 'templates'
|
||||
? 'text-primary-600 bg-primary-50 border-b-2 border-primary-500'
|
||||
? 'text-primary-600 border-b-2 border-primary-500'
|
||||
: 'text-gray-500 hover:text-gray-700 hover:bg-gray-50'
|
||||
}`}
|
||||
>
|
||||
|
|
@ -661,7 +661,7 @@ export const ProjectDetails: React.FC = () => {
|
|||
onClick={() => setActiveTab('ai-logs')}
|
||||
className={`py-3 px-4 font-medium text-sm transition-all duration-200 whitespace-nowrap rounded-t-lg relative ${
|
||||
activeTab === 'ai-logs'
|
||||
? 'text-primary-600 bg-primary-50 border-b-2 border-primary-500'
|
||||
? 'text-primary-600 border-b-2 border-primary-500'
|
||||
: 'text-gray-500 hover:text-gray-700 hover:bg-gray-50'
|
||||
}`}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -258,15 +258,17 @@ const TemplateManagement: React.FC = () => {
|
|||
<span className="ml-3 text-gray-600">加载中...</span>
|
||||
</div>
|
||||
) : templates.length === 0 ? (
|
||||
<div className="text-center py-12">
|
||||
<FileText className="w-16 h-16 text-gray-300 mx-auto mb-4" />
|
||||
<h3 className="text-lg font-medium text-gray-900 mb-2">暂无模板</h3>
|
||||
<p className="text-gray-500 mb-6">开始导入您的第一个剪映模板</p>
|
||||
<div className="text-center py-16">
|
||||
<div className="w-20 h-20 bg-gradient-to-br from-gray-100 to-gray-200 rounded-2xl flex items-center justify-center mx-auto mb-6">
|
||||
<FileText className="w-10 h-10 text-gray-400" />
|
||||
</div>
|
||||
<h3 className="text-xl font-semibold text-gray-900 mb-2">暂无模板</h3>
|
||||
<p className="text-gray-500 mb-8 max-w-md mx-auto">开始导入您的第一个剪映模板,体验智能视频制作的便利</p>
|
||||
<button
|
||||
onClick={() => setShowImportModal(true)}
|
||||
className="inline-flex items-center items-center gap-1.5 px-4 py-2 bg-gradient-to-r from-primary-500 to-primary-600 text-white rounded-lg hover:from-primary-600 hover:to-primary-700 transition-all duration-200 shadow-sm hover:shadow-md text-sm font-medium"
|
||||
className="inline-flex items-center gap-2 px-6 py-3 bg-gradient-to-r from-primary-600 to-primary-700 hover:from-primary-700 hover:to-primary-800 text-white rounded-xl transition-all duration-200 hover:scale-105 shadow-sm hover:shadow-md font-medium"
|
||||
>
|
||||
<Plus className="w-4 h-4 mr-2" />
|
||||
<Plus className="w-5 h-5" />
|
||||
导入模板
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -285,25 +287,27 @@ const TemplateManagement: React.FC = () => {
|
|||
))}
|
||||
</div>
|
||||
|
||||
{/* 分页 */}
|
||||
{/* 美观的分页组件 */}
|
||||
{totalPages > 1 && (
|
||||
<div className="flex items-center justify-center space-x-2">
|
||||
<div className="flex items-center justify-center space-x-3 mt-8">
|
||||
<button
|
||||
onClick={() => setCurrentPage(Math.max(1, currentPage - 1))}
|
||||
disabled={currentPage === 1}
|
||||
className="px-3 py-2 border border-gray-300 rounded-lg disabled:opacity-50 disabled:cursor-not-allowed hover:bg-gray-50"
|
||||
className="inline-flex items-center px-4 py-2.5 bg-white border border-gray-200 rounded-lg disabled:opacity-50 disabled:cursor-not-allowed hover:bg-gray-50 hover:border-gray-300 transition-all duration-200 text-sm font-medium text-gray-700"
|
||||
>
|
||||
上一页
|
||||
</button>
|
||||
|
||||
<span className="px-4 py-2 text-sm text-gray-600">
|
||||
第 {currentPage} 页,共 {totalPages} 页
|
||||
</span>
|
||||
|
||||
|
||||
<div className="flex items-center px-4 py-2.5 bg-gradient-to-r from-primary-50 to-primary-100 border border-primary-200 rounded-lg">
|
||||
<span className="text-sm font-medium text-primary-700">
|
||||
第 {currentPage} 页,共 {totalPages} 页
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={() => setCurrentPage(Math.min(totalPages, currentPage + 1))}
|
||||
disabled={currentPage === totalPages}
|
||||
className="px-3 py-2 border border-gray-300 rounded-lg disabled:opacity-50 disabled:cursor-not-allowed hover:bg-gray-50"
|
||||
className="inline-flex items-center px-4 py-2.5 bg-white border border-gray-200 rounded-lg disabled:opacity-50 disabled:cursor-not-allowed hover:bg-gray-50 hover:border-gray-300 transition-all duration-200 text-sm font-medium text-gray-700"
|
||||
>
|
||||
下一页
|
||||
</button>
|
||||
|
|
|
|||
|
|
@ -150,6 +150,70 @@
|
|||
background-clip: text;
|
||||
}
|
||||
|
||||
/* ===== 响应式动画优化 ===== */
|
||||
|
||||
/* 移动端动画优化 */
|
||||
@media (max-width: 768px) {
|
||||
.animate-fade-in-up {
|
||||
animation-duration: 0.3s;
|
||||
}
|
||||
|
||||
.hover-glow:hover {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 4px 6px -1px rgba(59, 130, 246, 0.1);
|
||||
}
|
||||
|
||||
/* 移动端卡片悬停效果减弱 */
|
||||
.card-interactive:hover {
|
||||
transform: translateY(-1px) scale(1.01);
|
||||
}
|
||||
|
||||
/* 移动端按钮点击效果 */
|
||||
.btn-standard:active,
|
||||
.btn-compact:active,
|
||||
.btn-large:active {
|
||||
transform: scale(0.98);
|
||||
}
|
||||
}
|
||||
|
||||
/* 平板端动画优化 */
|
||||
@media (min-width: 769px) and (max-width: 1024px) {
|
||||
.hover-glow:hover {
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.card-interactive:hover {
|
||||
transform: translateY(-1px) scale(1.015);
|
||||
}
|
||||
}
|
||||
|
||||
/* 大屏幕动画增强 */
|
||||
@media (min-width: 1920px) {
|
||||
.hover-glow:hover {
|
||||
transform: translateY(-3px);
|
||||
box-shadow: 0 20px 25px -5px rgba(59, 130, 246, 0.15), 0 10px 10px -5px rgba(59, 130, 246, 0.08);
|
||||
}
|
||||
|
||||
.card-interactive:hover {
|
||||
transform: translateY(-2px) scale(1.03);
|
||||
}
|
||||
}
|
||||
|
||||
/* 减少动画偏好设置 */
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.animate-fade-in-up,
|
||||
.hover-glow,
|
||||
.card-interactive {
|
||||
animation: none !important;
|
||||
transition: none !important;
|
||||
}
|
||||
|
||||
.hover-glow:hover,
|
||||
.card-interactive:hover {
|
||||
transform: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.animate-fade-out {
|
||||
animation: fadeOut 0.3s ease-out forwards;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -497,3 +497,87 @@
|
|||
border-color: rgba(75, 85, 99, 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
/* ===== 触摸设备优化 ===== */
|
||||
|
||||
/* 触摸友好的交互元素 */
|
||||
@media (pointer: coarse) {
|
||||
/* 增大触摸目标 */
|
||||
.btn-standard,
|
||||
.btn-compact,
|
||||
.btn-large {
|
||||
min-height: 44px;
|
||||
min-width: 44px;
|
||||
}
|
||||
|
||||
/* 菜单项触摸优化 */
|
||||
.dropdown-item,
|
||||
button[role="menuitem"] {
|
||||
min-height: 44px;
|
||||
padding: 12px 16px;
|
||||
}
|
||||
|
||||
/* 卡片交互优化 */
|
||||
.card-interactive {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* 移除悬停效果,使用点击效果 */
|
||||
.hover-glow:hover {
|
||||
transform: none;
|
||||
box-shadow: inherit;
|
||||
}
|
||||
|
||||
.hover-glow:active {
|
||||
transform: scale(0.98);
|
||||
box-shadow: 0 4px 6px -1px rgba(59, 130, 246, 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
/* 精确指针设备优化 */
|
||||
@media (pointer: fine) {
|
||||
/* 保持精细的悬停效果 */
|
||||
.hover-glow:hover {
|
||||
box-shadow: 0 10px 25px -5px rgba(59, 130, 246, 0.1), 0 10px 10px -5px rgba(59, 130, 246, 0.04);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
/* 精细的卡片悬停效果 */
|
||||
.card-interactive:hover {
|
||||
transform: translateY(-2px) scale(1.02);
|
||||
}
|
||||
}
|
||||
|
||||
/* 横屏模式优化 */
|
||||
@media (orientation: landscape) and (max-height: 600px) {
|
||||
.page-header {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
padding: 0.75rem;
|
||||
}
|
||||
|
||||
/* 紧凑的导航栏 */
|
||||
.navigation {
|
||||
height: 3rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* 竖屏模式优化 */
|
||||
@media (orientation: portrait) {
|
||||
.page-header {
|
||||
padding: 1.25rem;
|
||||
}
|
||||
|
||||
/* 竖屏下的网格优化 */
|
||||
.grid-responsive {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
@media (min-width: 640px) {
|
||||
.grid-responsive {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue