lint
This commit is contained in:
@@ -41,8 +41,8 @@ export function BlogCommentTool({ blogId, onInsertComment, replayTarget, handleC
|
|||||||
onInsertComment(res);
|
onInsertComment(res);
|
||||||
handleClearReplayTarget();
|
handleClearReplayTarget();
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error) {
|
||||||
if (error.statusCode === 429) {
|
if ((error as { statusCode: number }).statusCode === 429) {
|
||||||
return toast.error('操作太频繁了,稍后再试吧')
|
return toast.error('操作太频繁了,稍后再试吧')
|
||||||
}
|
}
|
||||||
toast.error('发布失败')
|
toast.error('发布失败')
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ export function BlogComments({ blogId }: { blogId: string }) {
|
|||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
{
|
{
|
||||||
data.filter(d => !d.parentId)
|
data.filter(d => !d.parentId)
|
||||||
.map((d, dIndex) => (
|
.map((d) => (
|
||||||
<div key={d.id} className="border-b border-zinc-300 py-2 last:border-none">
|
<div key={d.id} className="border-b border-zinc-300 py-2 last:border-none">
|
||||||
<h1 className="text-zinc-500">{d.user ? d.user.nickname : '匿名'}</h1>
|
<h1 className="text-zinc-500">{d.user ? d.user.nickname : '匿名'}</h1>
|
||||||
<div className="whitespace-pre-wrap break-all">{d.content}</div>
|
<div className="whitespace-pre-wrap break-all">{d.content}</div>
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import 'react-photo-view/dist/react-photo-view.css';
|
|||||||
import rehypeRaw from 'rehype-raw'
|
import rehypeRaw from 'rehype-raw'
|
||||||
import { Skeleton } from "@/components/ui/skeleton";
|
import { Skeleton } from "@/components/ui/skeleton";
|
||||||
import { BlogComments } from "./components/BlogComments";
|
import { BlogComments } from "./components/BlogComments";
|
||||||
|
import Image from "next/image";
|
||||||
|
|
||||||
export default function Blog() {
|
export default function Blog() {
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
@@ -50,29 +51,28 @@ export default function Blog() {
|
|||||||
<ReactMarkdown
|
<ReactMarkdown
|
||||||
remarkPlugins={[remarkGfm]}
|
remarkPlugins={[remarkGfm]}
|
||||||
rehypePlugins={[rehypeRaw, rehypeHighlight]}
|
rehypePlugins={[rehypeRaw, rehypeHighlight]}
|
||||||
children={data.content}
|
|
||||||
components={{
|
components={{
|
||||||
h1: ({ node, ...props }) => <h1 className="text-3xl font-bold py-2" {...props} />,
|
h1: ({ ...props }) => <h1 className="text-3xl font-bold py-2" {...props} />,
|
||||||
h2: ({ node, ...props }) => <h2 className="text-2xl font-bold py-1" {...props} />,
|
h2: ({ ...props }) => <h2 className="text-2xl font-bold py-1" {...props} />,
|
||||||
h3: ({ node, ...props }) => <h3 className="text-xl font-bold py-0.5" {...props} />,
|
h3: ({ ...props }) => <h3 className="text-xl font-bold py-0.5" {...props} />,
|
||||||
h4: ({ node, ...props }) => <h4 className="text-lg font-bold" {...props} />,
|
h4: ({ ...props }) => <h4 className="text-lg font-bold" {...props} />,
|
||||||
h5: ({ node, ...props }) => <h5 className="text-md font-bold" {...props} />,
|
h5: ({ ...props }) => <h5 className="text-md font-bold" {...props} />,
|
||||||
p: ({ node, ...props }) => <p className="py-1 text-zinc-700" {...props} />,
|
p: ({ ...props }) => <p className="py-1 text-zinc-700" {...props} />,
|
||||||
img: ({ node, src, ...props }) => (
|
img: ({ src }) => (
|
||||||
<PhotoProvider>
|
<PhotoProvider>
|
||||||
<PhotoView src={src as string}>
|
<PhotoView src={src as string}>
|
||||||
<img src={src} {...props} className="cursor-pointer" />
|
<Image src={src as string} alt="加载失败" />
|
||||||
</PhotoView>
|
</PhotoView>
|
||||||
</PhotoProvider>
|
</PhotoProvider>
|
||||||
),
|
),
|
||||||
th: ({ node, ...props }) => <th className="text-ellipsis text-nowrap border border-zinc-300 p-2" {...props} />,
|
th: ({ ...props }) => <th className="text-ellipsis text-nowrap border border-zinc-300 p-2" {...props} />,
|
||||||
td: ({ node, ...props }) => <td className="border border-zinc-300 p-1" {...props} />,
|
td: ({ ...props }) => <td className="border border-zinc-300 p-1" {...props} />,
|
||||||
table: ({ node, ...props }) => <div className="overflow-x-auto"><table {...props} /></div>,
|
table: ({ ...props }) => <div className="overflow-x-auto"><table {...props} /></div>,
|
||||||
pre: ({ node, ...props }) => <pre className="rounded-sm overflow-hidden shadow" {...props} />,
|
pre: ({ ...props }) => <pre className="rounded-sm overflow-hidden shadow" {...props} />,
|
||||||
blockquote: ({ node, ...props }) => <blockquote className="pl-3 border-l-5" {...props} />,
|
blockquote: ({ ...props }) => <blockquote className="pl-3 border-l-5" {...props} />,
|
||||||
a: ({ node, ...props }) => <a className="hover:underline" {...props} />,
|
a: ({ ...props }) => <a className="hover:underline" {...props} />,
|
||||||
}}
|
}}
|
||||||
/>
|
>{data.content}</ReactMarkdown>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import {
|
|||||||
DrawerFooter,
|
DrawerFooter,
|
||||||
DrawerHeader,
|
DrawerHeader,
|
||||||
DrawerTitle,
|
DrawerTitle,
|
||||||
DrawerTrigger,
|
|
||||||
} from "@/components/ui/drawer"
|
} from "@/components/ui/drawer"
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { AdminApi } from "@/lib/api";
|
import { AdminApi } from "@/lib/api";
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ import { Label } from "@/components/ui/label"
|
|||||||
import { useUser } from "@/hooks/admin/user/use-user";
|
import { useUser } from "@/hooks/admin/user/use-user";
|
||||||
import { User } from "@/lib/types/user";
|
import { User } from "@/lib/types/user";
|
||||||
import { Skeleton } from "@/components/ui/skeleton";
|
import { Skeleton } from "@/components/ui/skeleton";
|
||||||
import { updateUser } from "@/lib/api/admin/user";
|
|
||||||
import { AdminApi } from "@/lib/api";
|
import { AdminApi } from "@/lib/api";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import {
|
import {
|
||||||
@@ -42,10 +41,15 @@ export function UserInfoEditor({
|
|||||||
}) {
|
}) {
|
||||||
const { user, isLoading, error } = useUser(userId);
|
const { user, isLoading, error } = useUser(userId);
|
||||||
|
|
||||||
const [saveLoading, setSaveLoading] = React.useState(false);
|
// const [saveLoading, setSaveLoading] = React.useState(false);
|
||||||
const handleSave = async (user: updateUser) => {
|
const handleSave = async (user: {
|
||||||
|
username: string;
|
||||||
|
nickname: string;
|
||||||
|
email: string | null;
|
||||||
|
phone: string | null;
|
||||||
|
}) => {
|
||||||
try {
|
try {
|
||||||
setSaveLoading(true);
|
// setSaveLoading(true);
|
||||||
const res = await AdminApi.user.update(userId, user);
|
const res = await AdminApi.user.update(userId, user);
|
||||||
if (res) {
|
if (res) {
|
||||||
toast.success("保存成功");
|
toast.success("保存成功");
|
||||||
@@ -57,14 +61,14 @@ export function UserInfoEditor({
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
toast.error((error as Error).message || "保存失败");
|
toast.error((error as Error).message || "保存失败");
|
||||||
} finally {
|
} finally {
|
||||||
setSaveLoading(false);
|
// setSaveLoading(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const [removeLoading, setRemoveLoading] = React.useState(false);
|
// const [removeLoading, setRemoveLoading] = React.useState(false);
|
||||||
const handleRemove = async (userId: string) => {
|
const handleRemove = async (userId: string) => {
|
||||||
try {
|
try {
|
||||||
setRemoveLoading(true);
|
// setRemoveLoading(true);
|
||||||
await AdminApi.user.remove(userId, true);
|
await AdminApi.user.remove(userId, true);
|
||||||
toast.success("注销成功");
|
toast.success("注销成功");
|
||||||
onUserSoftDelete(userId);
|
onUserSoftDelete(userId);
|
||||||
@@ -72,22 +76,22 @@ export function UserInfoEditor({
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
toast.error((error as Error).message || "注销失败");
|
toast.error((error as Error).message || "注销失败");
|
||||||
} finally {
|
} finally {
|
||||||
setRemoveLoading(false);
|
// setRemoveLoading(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const [passwordDialogOpen, setPasswordDialogOpen] = React.useState(false);
|
const [passwordDialogOpen, setPasswordDialogOpen] = React.useState(false);
|
||||||
const [setPasswordLoading, setSetPasswordLoading] = React.useState(false);
|
// const [setPasswordLoading, setSetPasswordLoading] = React.useState(false);
|
||||||
const handleSetPassword = async (userId: string, password: string) => {
|
const handleSetPassword = async (userId: string, password: string) => {
|
||||||
try {
|
try {
|
||||||
setSetPasswordLoading(true);
|
// setSetPasswordLoading(true);
|
||||||
await AdminApi.user.setPassword(userId, password);
|
await AdminApi.user.setPassword(userId, password);
|
||||||
toast.success("密码修改成功");
|
toast.success("密码修改成功");
|
||||||
setPasswordDialogOpen(false);
|
setPasswordDialogOpen(false);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
toast.error((error as Error).message || "密码修改失败");
|
toast.error((error as Error).message || "密码修改失败");
|
||||||
} finally {
|
} finally {
|
||||||
setSetPasswordLoading(false);
|
// setSetPasswordLoading(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,8 +113,8 @@ export function UserInfoEditor({
|
|||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
const formData = new FormData(e.currentTarget);
|
const formData = new FormData(e.currentTarget);
|
||||||
handleSave({
|
handleSave({
|
||||||
username: formData.get("username")?.toString()!,
|
username: formData.get("username")?.toString() as unknown as string,
|
||||||
nickname: formData.get("nickname")?.toString()!,
|
nickname: formData.get("nickname")?.toString() as unknown as string,
|
||||||
email: formData.get("email")?.toString() || null,
|
email: formData.get("email")?.toString() || null,
|
||||||
phone: formData.get("phone")?.toString() || null,
|
phone: formData.get("phone")?.toString() || null,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -8,14 +8,14 @@ import { useState } from "react";
|
|||||||
import { UserInfoEditor } from "./components/user-info-editor";
|
import { UserInfoEditor } from "./components/user-info-editor";
|
||||||
import { User } from "@/lib/types/user";
|
import { User } from "@/lib/types/user";
|
||||||
import { CreateUserEditor } from "./components/create-user-editor";
|
import { CreateUserEditor } from "./components/create-user-editor";
|
||||||
import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger } from "@/components/ui/alert-dialog";
|
import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle } from "@/components/ui/alert-dialog";
|
||||||
import { AdminApi } from "@/lib/api";
|
import { AdminApi } from "@/lib/api";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import { ApiError } from "next/dist/server/api-utils";
|
import { ApiError } from "next/dist/server/api-utils";
|
||||||
|
|
||||||
|
|
||||||
export default function Page() {
|
export default function Page() {
|
||||||
const { users, isLoading, error, total, page, pageSize, mutate, refresh } = useUserList();
|
const { users, isLoading, error, mutate, refresh } = useUserList();
|
||||||
const [editorUserId, setEditorUserId] = useState("");
|
const [editorUserId, setEditorUserId] = useState("");
|
||||||
|
|
||||||
const handleUserUpdateLocal = async (newUser: User) => {
|
const handleUserUpdateLocal = async (newUser: User) => {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import React, { use, useState } from "react"
|
import React, { useState } from "react"
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
@@ -13,25 +13,9 @@ import { Input } from "@/components/ui/input"
|
|||||||
import { Label } from "@/components/ui/label"
|
import { Label } from "@/components/ui/label"
|
||||||
import { Button } from "@/components/ui/button"
|
import { Button } from "@/components/ui/button"
|
||||||
import { toast } from "sonner"
|
import { toast } from "sonner"
|
||||||
import { Textarea } from "@/components/ui/textarea"
|
|
||||||
import { Plus } from "lucide-react"
|
|
||||||
import { Resource } from "@/lib/types/resource"
|
|
||||||
import { AdminApi } from "@/lib/api"
|
import { AdminApi } from "@/lib/api"
|
||||||
import useSWR from "swr"
|
import useSWR from "swr"
|
||||||
import { ApiError } from "next/dist/server/api-utils"
|
import { ApiError } from "next/dist/server/api-utils"
|
||||||
import { Skeleton } from "@/components/ui/skeleton"
|
|
||||||
import {
|
|
||||||
AlertDialog,
|
|
||||||
AlertDialogAction,
|
|
||||||
AlertDialogCancel,
|
|
||||||
AlertDialogContent,
|
|
||||||
AlertDialogDescription,
|
|
||||||
AlertDialogFooter,
|
|
||||||
AlertDialogHeader,
|
|
||||||
AlertDialogTitle,
|
|
||||||
AlertDialogTrigger,
|
|
||||||
} from "@/components/ui/alert-dialog"
|
|
||||||
|
|
||||||
|
|
||||||
interface BlogEditProps {
|
interface BlogEditProps {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -41,7 +25,7 @@ interface BlogEditProps {
|
|||||||
|
|
||||||
export default function BlogEdit({ id, children, onRefresh }: BlogEditProps) {
|
export default function BlogEdit({ id, children, onRefresh }: BlogEditProps) {
|
||||||
const [open, setOpen] = useState(false)
|
const [open, setOpen] = useState(false)
|
||||||
const { data: blog, error, isLoading, mutate } = useSWR(
|
const { data: blog, mutate } = useSWR(
|
||||||
open ? `/api/admin/web/blog/${id}` : null,
|
open ? `/api/admin/web/blog/${id}` : null,
|
||||||
() => AdminApi.web.blog.get(id),
|
() => AdminApi.web.blog.get(id),
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import {
|
|||||||
TableBody,
|
TableBody,
|
||||||
TableCaption,
|
TableCaption,
|
||||||
TableCell,
|
TableCell,
|
||||||
TableFooter,
|
|
||||||
TableHead,
|
TableHead,
|
||||||
TableHeader,
|
TableHeader,
|
||||||
TableRow,
|
TableRow,
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import AddBlog from "./components/AddBlog";
|
|||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
|
|
||||||
export default function Page() {
|
export default function Page() {
|
||||||
const { blogs, error, isLoading, refresh } = useBlogList();
|
const { blogs, refresh } = useBlogList();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -153,7 +153,7 @@ export default function AddResource({ children, refresh }: AddResourceProps) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<DialogFooter>
|
<DialogFooter>
|
||||||
<Button type="submit" onClick={handleSubmit}>保存</Button>
|
<Button type="submit" onClick={handleSubmit} disabled={loading}>保存</Button>
|
||||||
</DialogFooter>
|
</DialogFooter>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ interface ResourceEditProps {
|
|||||||
export default function ResourceEdit({ children, id, onRefresh }: ResourceEditProps) {
|
export default function ResourceEdit({ children, id, onRefresh }: ResourceEditProps) {
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
|
|
||||||
const { data: resource, error, isLoading, mutate } = useSWR<Resource>(
|
const { data: resource, isLoading, mutate } = useSWR<Resource>(
|
||||||
open ? [`/api/admin/web/resource/${id}`] : null,
|
open ? [`/api/admin/web/resource/${id}`] : null,
|
||||||
() => AdminApi.web.resource.get(id),
|
() => AdminApi.web.resource.get(id),
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { Button } from "@/components/ui/button";
|
|||||||
import AddResource from "./components/AddResource";
|
import AddResource from "./components/AddResource";
|
||||||
|
|
||||||
export default function Page() {
|
export default function Page() {
|
||||||
const { resources, error, isLoading, mutate, refresh } = useResourceList();
|
const { resources, refresh } = useResourceList();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { usePathname, useRouter } from "next/navigation";
|
import { usePathname, useRouter } from "next/navigation";
|
||||||
import { useCallback, useState } from "react";
|
import { useState } from "react";
|
||||||
import {
|
import {
|
||||||
Drawer,
|
Drawer,
|
||||||
DrawerContent,
|
DrawerContent,
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ export function NavUser({ user, isUserLoading }: { user: User | undefined, isUse
|
|||||||
localStorage.removeItem(UserApi.USER_ME_CACHE_KEY)
|
localStorage.removeItem(UserApi.USER_ME_CACHE_KEY)
|
||||||
toast.success('登出成功');
|
toast.success('登出成功');
|
||||||
router.replace('/console/login');
|
router.replace('/console/login');
|
||||||
} catch (error) {
|
} catch {
|
||||||
toast.error('登出失败,请稍后再试');
|
toast.error('登出失败,请稍后再试');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { SeparatorProps } from "@radix-ui/react-separator";
|
|
||||||
import { Button } from "@/components/ui/button"
|
import { Button } from "@/components/ui/button"
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
@@ -9,7 +8,6 @@ import {
|
|||||||
DialogFooter,
|
DialogFooter,
|
||||||
DialogHeader,
|
DialogHeader,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
DialogTrigger,
|
|
||||||
} from "@/components/ui/dialog"
|
} from "@/components/ui/dialog"
|
||||||
import { Input } from "@/components/ui/input"
|
import { Input } from "@/components/ui/input"
|
||||||
import { Label } from "@/components/ui/label"
|
import { Label } from "@/components/ui/label"
|
||||||
|
|||||||
@@ -10,11 +10,7 @@ import {
|
|||||||
} from "@/components/ui/dialog"
|
} from "@/components/ui/dialog"
|
||||||
import { Button } from "../ui/button";
|
import { Button } from "../ui/button";
|
||||||
|
|
||||||
interface UserProfileProps {
|
export default function UserProfile({ onOpenChange, ...props }: React.ComponentProps<FC<DialogProps>>) {
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function ({ onOpenChange, ...props }: UserProfileProps & React.ComponentProps<FC<DialogProps>>) {
|
|
||||||
return (
|
return (
|
||||||
<Dialog onOpenChange={onOpenChange} {...props}>
|
<Dialog onOpenChange={onOpenChange} {...props}>
|
||||||
<DialogContent className="sm:max-w-[425px]">
|
<DialogContent className="sm:max-w-[425px]">
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
import fetcher from "@/lib/api/fetcher";
|
import fetcher from "@/lib/api/fetcher";
|
||||||
|
|
||||||
type CreateBlogParams = {
|
type CreateBlogParams = {
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
contentUrl: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function create(data: CreateBlogParams) {
|
export async function create(data: CreateBlogParams) {
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { Blog } from "@/lib/types/blog";
|
|
||||||
import fetcher from "../fetcher";
|
import fetcher from "../fetcher";
|
||||||
|
|
||||||
export async function get(id: string) {
|
export async function get(id: string) {
|
||||||
|
|||||||
Reference in New Issue
Block a user