Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e7cd30b30d | |||
| c1fa0461c1 |
@@ -28,6 +28,8 @@ import {
|
|||||||
Moon,
|
Moon,
|
||||||
Sun,
|
Sun,
|
||||||
User,
|
User,
|
||||||
|
Users,
|
||||||
|
Shield,
|
||||||
LogOut,
|
LogOut,
|
||||||
} from 'lucide-react'
|
} from 'lucide-react'
|
||||||
import { Logo } from './Logo'
|
import { Logo } from './Logo'
|
||||||
@@ -92,6 +94,7 @@ export function Layout() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const username = authData?.username
|
const username = authData?.username
|
||||||
|
const role = authData?.role
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const qc = useQueryClient()
|
const qc = useQueryClient()
|
||||||
const [darkMode, setDarkMode] = useState(() => {
|
const [darkMode, setDarkMode] = useState(() => {
|
||||||
@@ -209,6 +212,21 @@ export function Layout() {
|
|||||||
</div>
|
</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">
|
<div className="px-1">
|
||||||
<button
|
<button
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import { api, type OverviewMarket, type AiReviewReport } from '@/lib/api'
|
|||||||
import { QK } from '@/lib/queryKeys'
|
import { QK } from '@/lib/queryKeys'
|
||||||
import { cn } from '@/lib/cn'
|
import { cn } from '@/lib/cn'
|
||||||
import { fmtBigNum } from '@/lib/format'
|
import { fmtBigNum } from '@/lib/format'
|
||||||
|
import { DatePicker } from '@/components/DatePicker'
|
||||||
import { PageHeader } from '@/components/PageHeader'
|
import { PageHeader } from '@/components/PageHeader'
|
||||||
import { MarkdownRenderer } from '@/components/financials/MarkdownRenderer'
|
import { MarkdownRenderer } from '@/components/financials/MarkdownRenderer'
|
||||||
import { toast } from '@/components/Toast'
|
import { toast } from '@/components/Toast'
|
||||||
@@ -67,8 +68,7 @@ function fmtArchivedAt(iso: string): string {
|
|||||||
|
|
||||||
export function Review() {
|
export function Review() {
|
||||||
const qc = useQueryClient()
|
const qc = useQueryClient()
|
||||||
// 复盘日期:当前固定取最新交易日(后续如需日期选择可改回 useState)
|
const [asOf, setAsOf] = useState('')
|
||||||
const asOf: string | undefined = undefined
|
|
||||||
const [focus, setFocus] = useState('')
|
const [focus, setFocus] = useState('')
|
||||||
// 生成状态走全局 store:切走页面流不中断,回来可恢复
|
// 生成状态走全局 store:切走页面流不中断,回来可恢复
|
||||||
const { phase, content, error, meta } = useReviewState()
|
const { phase, content, error, meta } = useReviewState()
|
||||||
@@ -77,8 +77,8 @@ export function Review() {
|
|||||||
|
|
||||||
// 看板数据(与总览页同源)
|
// 看板数据(与总览页同源)
|
||||||
const marketQuery = useQuery<OverviewMarket>({
|
const marketQuery = useQuery<OverviewMarket>({
|
||||||
queryKey: QK.overviewMarket(asOf),
|
queryKey: QK.overviewMarket(asOf || undefined),
|
||||||
queryFn: () => api.overviewMarket(asOf),
|
queryFn: () => api.overviewMarket(asOf || undefined),
|
||||||
staleTime: 5_000,
|
staleTime: 5_000,
|
||||||
placeholderData: (prev) => prev,
|
placeholderData: (prev) => prev,
|
||||||
})
|
})
|
||||||
@@ -138,7 +138,7 @@ export function Review() {
|
|||||||
if (isReviewGenerating()) return
|
if (isReviewGenerating()) return
|
||||||
setViewing(null)
|
setViewing(null)
|
||||||
resetReview()
|
resetReview()
|
||||||
startReviewGeneration(asOf, focus, (full, doneMeta) => {
|
startReviewGeneration(asOf || undefined, focus, (full, doneMeta) => {
|
||||||
onGenerationDone(full, doneMeta).catch(() => { /* 静默 */ })
|
onGenerationDone(full, doneMeta).catch(() => { /* 静默 */ })
|
||||||
})
|
})
|
||||||
}, [asOf, focus, onGenerationDone])
|
}, [asOf, focus, onGenerationDone])
|
||||||
@@ -190,6 +190,7 @@ export function Review() {
|
|||||||
subtitle={`${displayDate}${data?.emotion ? ` · 情绪 ${data.emotion.label}` : ''}`}
|
subtitle={`${displayDate}${data?.emotion ? ` · 情绪 ${data.emotion.label}` : ''}`}
|
||||||
right={
|
right={
|
||||||
<div className="flex items-center gap-1">
|
<div className="flex items-center gap-1">
|
||||||
|
<DatePicker value={asOf} onChange={setAsOf} className="w-28" />
|
||||||
<button
|
<button
|
||||||
onClick={() => { marketQuery.refetch() }}
|
onClick={() => { marketQuery.refetch() }}
|
||||||
disabled={marketQuery.isFetching}
|
disabled={marketQuery.isFetching}
|
||||||
|
|||||||
Reference in New Issue
Block a user