mirror of
https://github.com/truewhile/MeBox.git
synced 2026-09-30 11:46:36 +08:00
TG Bot 命令交互 + UI 圆角色差深度优化 + 通知配置修复
## TG Bot 交互命令系统 (新增) - telegram_bot.go: 命令路由 + /start /help /status /search /downloads /stats - telegram_webhook.go: Webhook 接收 + Polling 管理端点 - Polling 模式: 无需公网 HTTPS, 服务器启动自动轮询 - HTML parse_mode 统一消息格式 ## 通知配置 BUG 修复 - validateChannel 添加 email 类型支持 - 前后端字段统一: channel_type -> type - ListByType 新增仓库方法 ## UI 深度优化 - glass-panel: 添加 rounded-2xl + 可见边框 + padding - card: 添加 p-4 sm:p-6 - 全局 rounded-md -> rounded-xl, 裸 rounded -> rounded-lg - 500+ 深色硬编码类名清零 - text-gray-400 -> gray-500 色差提升 - 按钮组 flex-wrap 防溢出 - data-table 表格溢出截断
This commit is contained in:
@@ -40,7 +40,7 @@ export function APIConfigsPanel() {
|
||||
{!loading && (
|
||||
<div className="glass-panel overflow-hidden">
|
||||
<table className="w-full text-left text-sm">
|
||||
<thead className="border-b border-white/5 text-xs uppercase tracking-wider text-sand-500">
|
||||
<thead className="border-b border-gray-200 text-xs uppercase tracking-wider text-sand-500">
|
||||
<tr>
|
||||
<th className="px-4 py-3">服务</th>
|
||||
<th className="px-4 py-3">密钥</th>
|
||||
@@ -63,7 +63,7 @@ export function APIConfigsPanel() {
|
||||
) : (
|
||||
<tr
|
||||
key={item.id}
|
||||
className="border-t border-white/5 transition hover:bg-white/[0.02]"
|
||||
className="border-t border-gray-200 transition hover:bg-gray-50"
|
||||
>
|
||||
<td className="px-4 py-3">
|
||||
<p className="font-medium text-ink-600">{item.provider}</p>
|
||||
@@ -75,7 +75,7 @@ export function APIConfigsPanel() {
|
||||
{item.has_key ? (
|
||||
<span className="text-brand-500">{item.masked_key}</span>
|
||||
) : (
|
||||
<span className="text-sand-400">未配置</span>
|
||||
<span className="text-gray-500">未配置</span>
|
||||
)}
|
||||
</td>
|
||||
<td className="px-4 py-3">
|
||||
@@ -93,7 +93,7 @@ export function APIConfigsPanel() {
|
||||
<div className="flex items-center justify-end gap-1">
|
||||
<button
|
||||
onClick={() => setEditing(item.provider)}
|
||||
className="rounded p-1.5 text-ink-50 transition hover:bg-white/5 hover:text-white"
|
||||
className="rounded-lg p-1.5 text-ink-50 transition hover:bg-gray-50 hover:text-white"
|
||||
title="编辑"
|
||||
>
|
||||
<Save size={14} />
|
||||
@@ -104,7 +104,7 @@ export function APIConfigsPanel() {
|
||||
`已配置 ${item.has_key ? '✓' : '✗'} 密钥 (在线测试请用对应功能页面)`,
|
||||
)
|
||||
}}
|
||||
className="rounded p-1.5 text-ink-50 transition hover:bg-white/5 hover:text-ink-200"
|
||||
className="rounded-lg p-1.5 text-ink-50 transition hover:bg-gray-50 hover:text-ink-200"
|
||||
title="查看状态"
|
||||
>
|
||||
<Eye size={14} />
|
||||
@@ -117,7 +117,7 @@ export function APIConfigsPanel() {
|
||||
toast.success('已清除')
|
||||
refresh()
|
||||
}}
|
||||
className="rounded p-1.5 text-ink-50 transition hover:bg-red-400/10 hover:text-red-400"
|
||||
className="rounded-lg p-1.5 text-ink-50 transition hover:bg-red-400/10 hover:text-red-400"
|
||||
title="清除密钥"
|
||||
>
|
||||
<Trash2 size={14} />
|
||||
@@ -170,7 +170,7 @@ function EditingRow({
|
||||
}
|
||||
|
||||
return (
|
||||
<tr className="border-t border-white/5 bg-primary-400/5">
|
||||
<tr className="border-t border-gray-200 bg-primary-400/5">
|
||||
<td colSpan={4} className="px-4 py-3">
|
||||
<form onSubmit={submit} className="flex flex-wrap items-end gap-3">
|
||||
<span className="text-sm font-medium text-ink-600">{item.provider}</span>
|
||||
@@ -207,7 +207,7 @@ function EditingRow({
|
||||
<button
|
||||
type="button"
|
||||
onClick={onCancel}
|
||||
className="rounded border border-sand-400/30 px-2 py-1.5 text-xs text-ink-50 hover:text-white"
|
||||
className="rounded-lg border border-sand-400/30 px-2 py-1.5 text-xs text-ink-50 hover:text-white"
|
||||
>
|
||||
<X size={12} />
|
||||
</button>
|
||||
|
||||
@@ -16,7 +16,7 @@ export function AppFooter({ className = '' }: { className?: string }) {
|
||||
href={link.href}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="flex items-center gap-1.5 rounded-lg px-2 py-1.5 text-xs text-sand-500 transition-colors hover:bg-sand-200 hover:text-ink-50"
|
||||
className="flex items-center gap-1.5 rounded-xl px-2 py-1.5 text-xs text-sand-500 transition-colors hover:bg-sand-200 hover:text-ink-50"
|
||||
title={link.label}
|
||||
>
|
||||
<link.icon size={14} />
|
||||
|
||||
@@ -78,7 +78,7 @@ export function Layout() {
|
||||
{/* Toggle Collapse Button for Large Screen */}
|
||||
<button
|
||||
onClick={() => setIsSidebarOpen(!isSidebarOpen)}
|
||||
className="rounded-lg p-1.5 text-gray-400 hover:bg-gray-100 hover:text-gray-900 transition-colors hidden lg:block"
|
||||
className="rounded-xl p-1.5 text-gray-500 hover:bg-gray-100 hover:text-gray-900 transition-colors hidden lg:block"
|
||||
>
|
||||
<Menu size={18} />
|
||||
</button>
|
||||
@@ -86,7 +86,7 @@ export function Layout() {
|
||||
{/* Mobile Drawer Close Button */}
|
||||
<button
|
||||
onClick={() => setIsMobileDrawerOpen(false)}
|
||||
className="rounded-lg p-1.5 text-gray-400 hover:bg-gray-100 hover:text-gray-900 transition-colors block lg:hidden"
|
||||
className="rounded-xl p-1.5 text-gray-500 hover:bg-gray-100 hover:text-gray-900 transition-colors block lg:hidden"
|
||||
>
|
||||
<X size={18} />
|
||||
</button>
|
||||
@@ -114,7 +114,7 @@ export function Layout() {
|
||||
<SectionHeader label="媒体片库" visible={isSidebarOpen || isMobileDrawerOpen} />
|
||||
<div className="space-y-1">
|
||||
{libraries.length === 0 && (isSidebarOpen || isMobileDrawerOpen) && (
|
||||
<div className="px-4 py-2 text-xs text-gray-400 italic">暂无媒体片库</div>
|
||||
<div className="px-4 py-2 text-xs text-gray-500 italic">暂无媒体片库</div>
|
||||
)}
|
||||
{libraries.map((lib) => (
|
||||
<SidebarLink
|
||||
@@ -180,7 +180,7 @@ export function Layout() {
|
||||
onClick={() => { logout(); navigate('/login') }}
|
||||
className={clsx(
|
||||
"flex items-center gap-3.5 rounded-xl px-4 py-3 text-sm font-semibold transition-all duration-300 w-full group/logout",
|
||||
(isSidebarOpen || isMobileDrawerOpen) ? "justify-start text-gray-500 hover:bg-red-50 hover:text-red-600" : "justify-center text-gray-400 hover:text-red-600"
|
||||
(isSidebarOpen || isMobileDrawerOpen) ? "justify-start text-gray-500 hover:bg-red-50 hover:text-red-600" : "justify-center text-gray-500 hover:text-red-600"
|
||||
)}
|
||||
title={`安全登出 (${user?.username})`}
|
||||
>
|
||||
@@ -248,7 +248,7 @@ export function Layout() {
|
||||
<form onSubmit={handleSearchSubmit} className="relative w-full hidden sm:block">
|
||||
<span className={clsx(
|
||||
"absolute left-4 top-1/2 -translate-y-1/2 transition-colors duration-200",
|
||||
searchFocused ? "text-brand-600" : "text-gray-400"
|
||||
searchFocused ? "text-brand-600" : "text-gray-500"
|
||||
)}>
|
||||
<Search size={16} />
|
||||
</span>
|
||||
@@ -259,10 +259,10 @@ export function Layout() {
|
||||
onFocus={() => setSearchFocused(true)}
|
||||
onBlur={() => setSearchFocused(false)}
|
||||
placeholder="搜索电影、电视剧、演员、种子站点..."
|
||||
className="w-full rounded-full border border-gray-200 bg-gray-50/50 py-2.5 pl-11 pr-12 text-sm text-gray-900 placeholder-gray-400 outline-none transition-all duration-300 focus:border-brand-500 focus:bg-white focus:ring-4 focus:ring-brand-100/40"
|
||||
className="w-full rounded-full border border-gray-200 bg-gray-50/50 py-2.5 pl-11 pr-12 text-sm text-gray-900 placeholder-gray-500 outline-none transition-all duration-300 focus:border-brand-500 focus:bg-white focus:ring-4 focus:ring-brand-100/40"
|
||||
/>
|
||||
<div className="absolute right-4 top-1/2 -translate-y-1/2 pointer-events-none">
|
||||
<span className="rounded-md border border-gray-200 bg-white px-1.5 py-0.5 text-[9px] font-bold text-gray-400 uppercase tracking-wider">
|
||||
<span className="rounded-xl border border-gray-200 bg-white px-1.5 py-0.5 text-[9px] font-bold text-gray-500 uppercase tracking-wider">
|
||||
Enter
|
||||
</span>
|
||||
</div>
|
||||
@@ -274,7 +274,7 @@ export function Layout() {
|
||||
{/* Quick search button shown ONLY on Mobile screens */}
|
||||
<Link
|
||||
to="/search"
|
||||
className="rounded-xl border border-gray-200 p-2.5 text-gray-400 hover:bg-gray-100 hover:text-gray-900 sm:hidden"
|
||||
className="rounded-xl border border-gray-200 p-2.5 text-gray-500 hover:bg-gray-100 hover:text-gray-900 sm:hidden"
|
||||
>
|
||||
<Search size={18} />
|
||||
</Link>
|
||||
@@ -289,7 +289,7 @@ export function Layout() {
|
||||
</Link>
|
||||
|
||||
{/* Notification alert bubble */}
|
||||
<button className="relative rounded-xl border border-gray-200 p-2.5 text-gray-400 hover:bg-gray-100 hover:text-gray-900 transition-all">
|
||||
<button className="relative rounded-xl border border-gray-200 p-2.5 text-gray-500 hover:bg-gray-100 hover:text-gray-900 transition-all">
|
||||
<Bell size={18} />
|
||||
<span className="absolute top-1.5 right-1.5 h-2 w-2 rounded-full bg-brand-500 ring-2 ring-white animate-pulse" />
|
||||
</button>
|
||||
@@ -310,7 +310,7 @@ export function Layout() {
|
||||
<p className="text-xs font-bold text-gray-900 leading-none">{user?.username}</p>
|
||||
<p className="text-[9px] text-gray-500 font-bold uppercase tracking-wider mt-0.5 leading-none">{user?.role}</p>
|
||||
</div>
|
||||
<ChevronDown size={14} className="text-gray-400" />
|
||||
<ChevronDown size={14} className="text-gray-500" />
|
||||
</button>
|
||||
|
||||
<AnimatePresence>
|
||||
@@ -389,7 +389,7 @@ export function Layout() {
|
||||
</main>
|
||||
|
||||
{/* Absolute Footer Frame */}
|
||||
<AppFooter className="border-t border-gray-200/50 bg-white py-5 text-center text-xs text-gray-400" />
|
||||
<AppFooter className="border-t border-gray-200/50 bg-white py-5 text-center text-xs text-gray-500" />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
@@ -399,7 +399,7 @@ function SectionHeader({ label, visible }: { label: string; visible: boolean })
|
||||
if (!visible) return <div className="h-5" />;
|
||||
return (
|
||||
<div className="px-4 mb-2 mt-4">
|
||||
<span className="text-[10px] font-bold uppercase tracking-[0.25em] text-gray-400">
|
||||
<span className="text-[10px] font-bold uppercase tracking-[0.25em] text-gray-500">
|
||||
{label}
|
||||
</span>
|
||||
</div>
|
||||
@@ -432,7 +432,7 @@ function SidebarLink({ to, icon, label, end, collapsed }: SidebarLinkProps) {
|
||||
<>
|
||||
<span className={clsx(
|
||||
"flex shrink-0 items-center justify-center w-5 h-5 transition-transform duration-300 group-hover:scale-110",
|
||||
isActive ? "text-[#c9954a]" : "text-gray-400 group-hover:text-gray-700"
|
||||
isActive ? "text-[#c9954a]" : "text-gray-500 group-hover:text-gray-700"
|
||||
)}>
|
||||
{icon}
|
||||
</span>
|
||||
@@ -446,7 +446,7 @@ function SidebarLink({ to, icon, label, end, collapsed }: SidebarLinkProps) {
|
||||
</motion.span>
|
||||
)}
|
||||
{collapsed && (
|
||||
<div className="absolute left-full ml-3 px-2.5 py-1.5 rounded-lg bg-gray-900 text-white text-xs font-semibold opacity-0 pointer-events-none group-hover:opacity-100 group-hover:pointer-events-auto transition-opacity shadow-lg z-50 whitespace-nowrap">
|
||||
<div className="absolute left-full ml-3 px-2.5 py-1.5 rounded-xl bg-gray-900 text-white text-xs font-semibold opacity-0 pointer-events-none group-hover:opacity-100 group-hover:pointer-events-auto transition-opacity shadow-lg z-50 whitespace-nowrap">
|
||||
{label}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -32,7 +32,7 @@ export const MediaCard = ({
|
||||
referrerPolicy="no-referrer"
|
||||
/>
|
||||
) : (
|
||||
<div className="flex h-full w-full flex-col items-center justify-center gap-2 text-gray-400 bg-gray-50">
|
||||
<div className="flex h-full w-full flex-col items-center justify-center gap-2 text-gray-500 bg-gray-50">
|
||||
<Film size={28} className="stroke-[1.5]" />
|
||||
<span className="text-[10px] uppercase tracking-wider font-bold">No Poster</span>
|
||||
</div>
|
||||
@@ -40,7 +40,7 @@ export const MediaCard = ({
|
||||
|
||||
{/* Episode count badge */}
|
||||
{count !== undefined && count > 1 && (
|
||||
<span className="absolute right-3 top-3 inline-flex items-center gap-1 rounded-lg bg-[#111827]/90 px-2 py-1 text-[10px] font-bold text-white shadow-sm border border-white/10">
|
||||
<span className="absolute right-3 top-3 inline-flex items-center gap-1 rounded-xl bg-[#111827]/90 px-2 py-1 text-[10px] font-bold text-white shadow-sm border border-gray-200">
|
||||
<Layers size={10} className="text-[#c9954a]" />
|
||||
<span>{count} 集</span>
|
||||
</span>
|
||||
@@ -48,7 +48,7 @@ export const MediaCard = ({
|
||||
|
||||
{/* Rating Badge */}
|
||||
{(rating || (media as any).rating) && (
|
||||
<span className="absolute left-3 top-3 inline-flex items-center gap-0.5 rounded-lg bg-[#111827]/90 px-2 py-1 text-[10px] font-bold text-[#c9954a] shadow-sm border border-white/10">
|
||||
<span className="absolute left-3 top-3 inline-flex items-center gap-0.5 rounded-xl bg-[#111827]/90 px-2 py-1 text-[10px] font-bold text-[#c9954a] shadow-sm border border-gray-200">
|
||||
<Star size={10} fill="currentColor" />
|
||||
<span>{(rating || (media as any).rating).toFixed(1)}</span>
|
||||
</span>
|
||||
@@ -66,7 +66,7 @@ export const MediaCard = ({
|
||||
<Play size={10} fill="currentColor" className="text-white" />
|
||||
<span>立即观影</span>
|
||||
</span>
|
||||
<p className="text-[10px] text-gray-300 font-semibold line-clamp-2 leading-relaxed">
|
||||
<p className="text-[10px] text-gray-500 font-semibold line-clamp-2 leading-relaxed">
|
||||
{media.overview || "暂无简介内容"}
|
||||
</p>
|
||||
</motion.div>
|
||||
@@ -91,7 +91,7 @@ export const MediaCard = ({
|
||||
<div className="flex items-center justify-between text-[11px] text-gray-500 font-bold uppercase tracking-wider">
|
||||
<span>{media.year > 0 ? media.year : "未知年份"}</span>
|
||||
{media.video_codec && (
|
||||
<span className="px-1.5 py-0.5 rounded-md bg-gray-50 text-gray-600 border border-gray-100">
|
||||
<span className="px-1.5 py-0.5 rounded-xl bg-gray-50 text-gray-600 border border-gray-100">
|
||||
{media.video_codec}
|
||||
</span>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user