Compare commits

...

2 Commits

Author SHA1 Message Date
fish e7cd30b30d 侧边栏增加 admin 用户管理入口
admin 用户可在侧边栏底部进入用户管理页,
支持新增/删除用户。

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-06 15:05:35 +08:00
fish c1fa0461c1 复盘页添加日期选择功能
Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-06 15:04:01 +08:00
2 changed files with 24 additions and 5 deletions
+18
View File
@@ -28,6 +28,8 @@ import {
Moon,
Sun,
User,
Users,
Shield,
LogOut,
} from 'lucide-react'
import { Logo } from './Logo'
@@ -92,6 +94,7 @@ export function Layout() {
})
const username = authData?.username
const role = authData?.role
const navigate = useNavigate()
const qc = useQueryClient()
const [darkMode, setDarkMode] = useState(() => {
@@ -209,6 +212,21 @@ export function Layout() {
</div>
)}
{/* 用户管理 (admin only) */}
{role === 'admin' && (
<NavLink
to="/settings/users"
className={({ isActive }) =>
'flex items-center gap-2 px-3 py-1.5 text-xs text-foreground/70 hover:bg-elevated hover:text-foreground transition-colors' +
(isActive ? ' bg-accent/10 text-accent' : '')
}
>
<Users className="h-3.5 w-3.5" />
<Shield className="h-3 w-3 text-amber-400" />
<span className="flex-1"></span>
</NavLink>
)}
{/* 暗夜模式 */}
<div className="px-1">
<button
+6 -5
View File
@@ -19,6 +19,7 @@ import { api, type OverviewMarket, type AiReviewReport } from '@/lib/api'
import { QK } from '@/lib/queryKeys'
import { cn } from '@/lib/cn'
import { fmtBigNum } from '@/lib/format'
import { DatePicker } from '@/components/DatePicker'
import { PageHeader } from '@/components/PageHeader'
import { MarkdownRenderer } from '@/components/financials/MarkdownRenderer'
import { toast } from '@/components/Toast'
@@ -67,8 +68,7 @@ function fmtArchivedAt(iso: string): string {
export function Review() {
const qc = useQueryClient()
// 复盘日期:当前固定取最新交易日(后续如需日期选择可改回 useState)
const asOf: string | undefined = undefined
const [asOf, setAsOf] = useState('')
const [focus, setFocus] = useState('')
// 生成状态走全局 store:切走页面流不中断,回来可恢复
const { phase, content, error, meta } = useReviewState()
@@ -77,8 +77,8 @@ export function Review() {
// 看板数据(与总览页同源)
const marketQuery = useQuery<OverviewMarket>({
queryKey: QK.overviewMarket(asOf),
queryFn: () => api.overviewMarket(asOf),
queryKey: QK.overviewMarket(asOf || undefined),
queryFn: () => api.overviewMarket(asOf || undefined),
staleTime: 5_000,
placeholderData: (prev) => prev,
})
@@ -138,7 +138,7 @@ export function Review() {
if (isReviewGenerating()) return
setViewing(null)
resetReview()
startReviewGeneration(asOf, focus, (full, doneMeta) => {
startReviewGeneration(asOf || undefined, focus, (full, doneMeta) => {
onGenerationDone(full, doneMeta).catch(() => { /* 静默 */ })
})
}, [asOf, focus, onGenerationDone])
@@ -190,6 +190,7 @@ export function Review() {
subtitle={`${displayDate}${data?.emotion ? ` · 情绪 ${data.emotion.label}` : ''}`}
right={
<div className="flex items-center gap-1">
<DatePicker value={asOf} onChange={setAsOf} className="w-28" />
<button
onClick={() => { marketQuery.refetch() }}
disabled={marketQuery.isFetching}