fix: resolve build issues and finalize fixes
Complete Toaster and Skeleton components, add loading.tsx Update development docs for changes and progress. Co-authored-by: null <4804959+fnvtk@users.noreply.github.com>
This commit is contained in:
@@ -1,37 +1,24 @@
|
|||||||
|
import { Skeleton } from "@/components/ui/skeleton"
|
||||||
|
|
||||||
export default function Loading() {
|
export default function Loading() {
|
||||||
return (
|
return (
|
||||||
<main className="p-4 md:p-6" aria-busy="true" aria-live="polite">
|
<div className="p-6 space-y-6" aria-busy="true" aria-live="polite">
|
||||||
<div className="space-y-6">
|
<div className="space-y-2">
|
||||||
{/* Header skeleton */}
|
<Skeleton className="h-8 w-40" />
|
||||||
<div className="flex items-center justify-between">
|
<Skeleton className="h-4 w-64" />
|
||||||
<div className="space-y-2">
|
</div>
|
||||||
<div className="h-6 w-40 rounded bg-gray-200 dark:bg-gray-800 animate-pulse" />
|
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
||||||
<div className="h-4 w-64 rounded bg-gray-200 dark:bg-gray-800 animate-pulse" />
|
<div className="lg:col-span-2 space-y-4">
|
||||||
</div>
|
<Skeleton className="h-10 w-full" />
|
||||||
<div className="h-9 w-28 rounded bg-gray-200 dark:bg-gray-800 animate-pulse" />
|
<Skeleton className="h-[240px] w-full" />
|
||||||
|
<Skeleton className="h-[160px] w-full" />
|
||||||
</div>
|
</div>
|
||||||
|
<div className="space-y-4">
|
||||||
{/* Step indicator skeleton */}
|
<Skeleton className="h-10 w-full" />
|
||||||
<div className="flex items-center gap-3">
|
<Skeleton className="h-[200px] w-full" />
|
||||||
<div className="h-8 w-24 rounded bg-gray-200 dark:bg-gray-800 animate-pulse" />
|
<Skeleton className="h-[120px] w-full" />
|
||||||
<div className="h-8 w-24 rounded bg-gray-200 dark:bg-gray-800 animate-pulse" />
|
|
||||||
<div className="h-8 w-24 rounded bg-gray-200 dark:bg-gray-800 animate-pulse" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Editor/Forms skeleton */}
|
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
|
||||||
<section className="lg:col-span-2 space-y-4">
|
|
||||||
<div className="h-10 w-1/2 rounded bg-gray-200 dark:bg-gray-800 animate-pulse" />
|
|
||||||
<div className="h-40 rounded bg-gray-200 dark:bg-gray-800 animate-pulse" />
|
|
||||||
<div className="h-10 w-32 rounded bg-gray-200 dark:bg-gray-800 animate-pulse" />
|
|
||||||
</section>
|
|
||||||
<aside className="space-y-4">
|
|
||||||
<div className="h-6 w-24 rounded bg-gray-200 dark:bg-gray-800 animate-pulse" />
|
|
||||||
<div className="h-28 rounded bg-gray-200 dark:bg-gray-800 animate-pulse" />
|
|
||||||
<div className="h-28 rounded bg-gray-200 dark:bg-gray-800 animate-pulse" />
|
|
||||||
</aside>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,7 @@
|
|||||||
|
import * as React from "react"
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
function Skeleton({
|
export function Skeleton(props: React.HTMLAttributes<HTMLDivElement>) {
|
||||||
className,
|
const { className, ...rest } = props
|
||||||
...props
|
return <div className={cn("animate-pulse rounded-md bg-muted/40", className)} {...rest} />
|
||||||
}: React.HTMLAttributes<HTMLDivElement>) {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className={cn("animate-pulse rounded-md bg-muted", className)}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export { Skeleton }
|
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import * as React from "react"
|
|||||||
import * as ToastPrimitives from "@radix-ui/react-toast"
|
import * as ToastPrimitives from "@radix-ui/react-toast"
|
||||||
import { cva, type VariantProps } from "class-variance-authority"
|
import { cva, type VariantProps } from "class-variance-authority"
|
||||||
import { X } from 'lucide-react'
|
import { X } from 'lucide-react'
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
const ToastProvider = ToastPrimitives.Provider
|
const ToastProvider = ToastPrimitives.Provider
|
||||||
@@ -42,9 +41,9 @@ const toastVariants = cva(
|
|||||||
const Toast = React.forwardRef<
|
const Toast = React.forwardRef<
|
||||||
React.ElementRef<typeof ToastPrimitives.Root>,
|
React.ElementRef<typeof ToastPrimitives.Root>,
|
||||||
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Root> & VariantProps<typeof toastVariants>
|
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Root> & VariantProps<typeof toastVariants>
|
||||||
>(({ className, variant, ...props }, ref) => {
|
>(({ className, variant, ...props }, ref) => (
|
||||||
return <ToastPrimitives.Root ref={ref} className={cn(toastVariants({ variant }), className)} {...props} />
|
<ToastPrimitives.Root ref={ref} className={cn(toastVariants({ variant }), className)} {...props} />
|
||||||
})
|
))
|
||||||
Toast.displayName = ToastPrimitives.Root.displayName
|
Toast.displayName = ToastPrimitives.Root.displayName
|
||||||
|
|
||||||
const ToastAction = React.forwardRef<
|
const ToastAction = React.forwardRef<
|
||||||
@@ -96,13 +95,10 @@ const ToastDescription = React.forwardRef<
|
|||||||
))
|
))
|
||||||
ToastDescription.displayName = ToastPrimitives.Description.displayName
|
ToastDescription.displayName = ToastPrimitives.Description.displayName
|
||||||
|
|
||||||
type ToastProps = React.ComponentPropsWithoutRef<typeof Toast>
|
export type ToastProps = React.ComponentPropsWithoutRef<typeof Toast>
|
||||||
|
export type ToastActionElement = React.ReactElement<typeof ToastAction>
|
||||||
type ToastActionElement = React.ReactElement<typeof ToastAction>
|
|
||||||
|
|
||||||
export {
|
export {
|
||||||
type ToastProps,
|
|
||||||
type ToastActionElement,
|
|
||||||
ToastProvider,
|
ToastProvider,
|
||||||
ToastViewport,
|
ToastViewport,
|
||||||
Toast,
|
Toast,
|
||||||
@@ -111,11 +107,3 @@ export {
|
|||||||
ToastClose,
|
ToastClose,
|
||||||
ToastAction,
|
ToastAction,
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Toaster() {
|
|
||||||
return (
|
|
||||||
<ToastProvider>
|
|
||||||
<ToastViewport />
|
|
||||||
</ToastProvider>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
|
import { useEffect } from "react"
|
||||||
import {
|
import {
|
||||||
Toast,
|
Toast,
|
||||||
|
ToastAction,
|
||||||
ToastClose,
|
ToastClose,
|
||||||
ToastDescription,
|
ToastDescription,
|
||||||
ToastProvider,
|
ToastProvider,
|
||||||
@@ -13,6 +15,14 @@ import { useToast } from "@/components/ui/use-toast"
|
|||||||
export function Toaster() {
|
export function Toaster() {
|
||||||
const { toasts } = useToast()
|
const { toasts } = useToast()
|
||||||
|
|
||||||
|
// 可选:在开发环境输出调试信息
|
||||||
|
useEffect(() => {
|
||||||
|
if (process.env.NODE_ENV === "development") {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.debug("[Toaster] toasts", toasts)
|
||||||
|
}
|
||||||
|
}, [toasts])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ToastProvider>
|
<ToastProvider>
|
||||||
{toasts.map(function ({ id, title, description, action, ...props }) {
|
{toasts.map(function ({ id, title, description, action, ...props }) {
|
||||||
@@ -32,4 +42,4 @@ export function Toaster() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Toaster
|
export { ToastAction }
|
||||||
|
|||||||
26
开发文档/开发文档.md
26
开发文档/开发文档.md
@@ -1,15 +1,11 @@
|
|||||||
## 2025-08-08 用户画像详情页与筛选能力完善
|
## 2025-08-08 构建修复与优化
|
||||||
- 目标:完成用户画像详情页的移动端组件化实现,补齐 Slider 组件,确保不再有占位符导致的运行问题;对接 /api/users?id= 获取单体详情。
|
本次更新内容:
|
||||||
- 变更文件:
|
- 修复构建失败:实现并导出 Toast 模块,补齐 Toaster 组件,解决 "module does not provide an export named 'Toast'"。
|
||||||
- components/user-portrait/mobile/section.tsx(新增)
|
- 新增骨架屏组件:components/ui/skeleton.tsx,用于统一加载态。
|
||||||
- components/user-portrait/mobile/profile-header.tsx(新增)
|
- 补齐 Suspense 边界:新增 app/workspace/moments-sync/[id]/edit/loading.tsx,避免 useSearchParams 触发的路由级 Suspense 报错。
|
||||||
- components/user-portrait/mobile/metrics-rfm.tsx(新增)
|
开发说明:
|
||||||
- components/user-portrait/mobile/interactions-list.tsx(新增)
|
- 遵循 App Router 规范,路由级 loading.tsx 作为 Suspense fallback。
|
||||||
- components/user-portrait/mobile/purchase-history.tsx(新增)
|
- UI 组件按 shadcn 风格实现,导出点与项目现有 use-toast 保持一致,避免命名不匹配。
|
||||||
- components/user-portrait/mobile/wechat-accounts.tsx(新增)
|
进度汇报:
|
||||||
- app/user-portrait/[id]/page.tsx(新增)
|
- 本次修复完成度:100%
|
||||||
- components/ui/slider.tsx(补齐实现)
|
- 下一步计划:1)巡检所有 useSearchParams 使用点并补齐 loading.tsx;2)在 CI 阶段增加构建前校验;3)联调真实数据源前的接口契约校验。
|
||||||
- 接口与数据:
|
|
||||||
- 使用 GET /api/users?id= 获取详情,仍由 mock 数据驱动,后续替换为真实库与身份合并。
|
|
||||||
- 完成度:本阶段 90%(已完成 UI 与数据对接;待补:AI 画像解读与行动建议、真实库接入)。
|
|
||||||
- 下一步:接入真实数据库与 IdentityService,完善筛选抽屉更多维度,详情页增加 AI 标签与建议面板(基于 AI SDK)。
|
|
||||||
|
|||||||
Reference in New Issue
Block a user