完成了文章的管理

This commit is contained in:
expdsn 2025-02-07 19:02:22 +08:00
parent df92d99a51
commit fbf3c4102d
25 changed files with 1920 additions and 147 deletions

View File

@ -40,7 +40,7 @@ export async function login(state: FormState, formData: FormData) {
await createSession(user._id)
// 5. Redirect user
redirect('/admin')p
redirect('/admin')
}
export async function logout() {

45
app/_lib/data/article.ts Normal file
View File

@ -0,0 +1,45 @@
import { ObjectId } from "mongodb";
import { getCollection } from "../mongodb";
export type ArticleType = {
title: string;
description: string;
link: string;
_id: string;
content: string;
priority: number;
cover: string;
addTime: number;
}
export async function getArticleList({ page = 1, pageSize = 9999 }: {
page?: number;
pageSize?: number;
}) {
const collection = await getCollection('article');
const startIndex = (page - 1) * pageSize;
// 构建查询条件对象
const pipeline = [
// 根据构建好的查询条件筛选文档
{ $sort: { priority: 1 } },
{ $skip: startIndex },
{ $limit: pageSize },
{
$addFields: {
_id: { $toString: "$_id" }
}
}
];
const cursor = collection.aggregate<ArticleType>(pipeline);
const list = await cursor.toArray();
// 获取符合查询条件的文档总数
const total = await collection.countDocuments();
return {
total,
list
}
}
export async function getArticle(id: string) {
const collection = await getCollection('article');
return collection.findOne<ArticleType>({ _id: new ObjectId(id) })
}

View File

@ -8,7 +8,7 @@ export type LinkType = {
iconElement?: ReactNode;
_id: string;
href?: string;
priority: number;
priority?: number;
location?: string;
}

View File

@ -4,7 +4,7 @@ import { getCollection } from "../mongodb";
export type SearchTypeItem = {
name: string;
key: string;
key?: string;
includes: string[];
_id: string;
priority: number;
@ -63,7 +63,7 @@ export async function getSearchWayList({ page = 1, pageSize = 9999 }: {
const list = await cursor.toArray();
// 计算总数量
const total = (await collection.find<SearchWayItemType>({}).toArray()).length
const total = await collection.countDocuments()
return {
total,
list

View File

@ -16,12 +16,15 @@ export async function encrypt(payload: SessionPayload) {
export async function decrypt(session: string | undefined = '') {
try {
if (!session) {
throw new Error('Session is empty or undefined');
}
const { payload } = await jwtVerify(session, encodedKey, {
algorithms: ['HS256'],
})
return payload
} catch (error) {
console.log('Failed to verify session', error)
console.log('Failed to verify session')
}
}
export async function createSession(userId: string) {
@ -55,8 +58,8 @@ export async function updateSession() {
sameSite: 'lax',
path: '/',
})
}
export async function deleteSession() {
}
export async function deleteSession() {
const cookieStore = await cookies()
cookieStore.delete('session')
}
}

5
app/_lib/utils.ts Normal file
View File

@ -0,0 +1,5 @@
export function doSearch(url: string, keyword: string) {
window.open(url.replace(/%s/g, keyword), "_blank")
}

View File

@ -3,84 +3,51 @@
import { faSearch } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import clsx from "clsx";
import { useMemo, useState } from "react";
import { useEffect, useMemo, useState } from "react";
import Logo from "./Logo";
export type SearchTypeItem = {
name: string;
key: string;
includes: string[];
priority: number;
import { useRequest } from "ahooks";
import { mRequest } from "../_lib/request";
import { SearchTypeItem, SearchWayItemType } from "../_lib/data/search";
import { doSearch } from "../_lib/utils";
import { log } from "node:console";
}
export type SearchWayItemType = {
label: string;
value: string;
fullName: string;
key: string;
}
export const SearchTypeList: SearchTypeItem[] = [
{
name: '常用',
key: 'custom',
includes: ['inside', 'bing'],
priority: 0
},
{
name: '搜索',
key: 'search',
includes: ['101'],
priority: 0
},
{
name: '社区',
key: 'community',
includes: ['101'],
priority: 0
},
{
name: '图片',
key: 'picture',
includes: ['101'],
priority: 0
},
{
name: '生活',
key: 'life',
includes: ['101'],
priority: 0
}
]
export const SearchWayItem: SearchWayItemType[] = [
{
label: '站内',
value: '',
fullName: '站内资源',
key: 'inside',
},
{
label: 'Bing',
fullName: '站内资源',
value: 'https://bing.com',
key: 'bing'
}
]
export default function Search() {
const [selectKey, setSelectKey] = useState(SearchTypeList[0].key)
const { data: searchWayList = [] } = useRequest(() => mRequest<{ list: SearchWayItemType[] }>('GET', '/api/search?page=1&pageSize=999').then(res => res.list))
const { data: searchTypeList = [] } = useRequest(() => mRequest<{ list: SearchTypeItem[] }>('GET', '/api/searchType?page=1&pageSize=999').then(res => res.list))
const [selectKey, setSelectKey] = useState<string | null>(null)
const [activeSearchKey, setActiveSearchKey] = useState<string | null>(null)
const [inputStr, setInputStr] = useState('')
const nowSelectConfig = useMemo(() => {
const idx = SearchTypeList.findIndex(val => val.key === selectKey)
if (idx !== -1) return SearchTypeList[idx]
const idx = searchTypeList.findIndex(val => val._id === selectKey)
if (idx !== -1) return searchTypeList[idx]
else return null
}, [selectKey])
const [activeSearchKey, setActiveSearchKey] = useState(nowSelectConfig?.includes[0])
}, [selectKey, searchTypeList])
const filterSeachList = useMemo(() => {
return searchWayList.filter(val => nowSelectConfig?.includes.includes(val._id))
}, [searchWayList, nowSelectConfig])
const activeSearch = useMemo(() => {
const idx = SearchWayItem.findIndex(val => val.key === activeSearchKey)
const idx = searchWayList.findIndex(val => val._id === activeSearchKey)
if (idx !== -1) {
return SearchWayItem[idx]
return searchWayList[idx]
} else {
return null
}
}, [activeSearchKey])
}, [activeSearchKey, searchWayList])
useEffect(() => {
if (searchTypeList.length > 0) {
setSelectKey(searchTypeList[0]._id)
}
}, [searchTypeList])
useEffect(() => {
if (filterSeachList.length > 0) {
setActiveSearchKey(filterSeachList[0]._id)
}
}, [selectKey])
return (
<div className="w-full flex justify-center flex-col items-center py-10 h-[500px]">
@ -90,12 +57,12 @@ export default function Search() {
<div className="w-full lg:w-[800px] flex flex-col gap-y-2">
<div className="flex w-full justify-center gap-x-5 text-[#666666]">
{
SearchTypeList.map(item => (
<div key={item.key}
searchTypeList.map(item => (
<div key={item._id}
onClick={() => {
setSelectKey(item.key)
setSelectKey(item._id)
}}
className={clsx(item.key === selectKey ?
className={clsx(item._id === selectKey ?
"text-[#333]" : "text-[#999] cursor-pointer")}>
{item.name}
</div>
@ -103,15 +70,28 @@ export default function Search() {
}
</div>
<div className="w-full relative">
<input type="text" placeholder={`${activeSearch?.label}搜索`} className="w-full bg-[#C4C2C6] px-4 h-[50px] rounded-3xl outline-none" />
<input
onKeyDown={(e) => {
if (!activeSearch?.value) return
if (e.key === 'Enter') {
doSearch(activeSearch?.value, inputStr)
}
}}
type="text"
onChange={e => {
setInputStr(e.target.value)
}}
placeholder={`${activeSearch?.label}搜索`} className="w-full bg-[#C4C2C6] px-4 h-[50px] rounded-3xl outline-none" />
<FontAwesomeIcon className="text-white absolute top-1/2 -translate-y-1/2 right-6 text-xl" icon={faSearch}></FontAwesomeIcon>
</div>
<div className="w-full flex justify-center gap-x-4 ">
{
SearchWayItem.filter(val => nowSelectConfig?.includes.includes(val.key)).map(item => (
<div key={item.key} onClick={() => {
setActiveSearchKey(item.key)
}} className={clsx(activeSearchKey === item.key ?
filterSeachList.map(item => (
<div key={item._id} onClick={() => {
setActiveSearchKey(item._id)
}} className={clsx(activeSearchKey === item._id ?
"text-[#333]" : "text-[#999] cursor-pointer")}>{item.label}</div>
))
}

View File

@ -1,5 +1,36 @@
"use client";
import { Button, Card, Space, Table } from "antd";
export default function Page() {
return (
<>ad</>
<Card title="广告管理" extra={<Button type="primary">广</Button>}>
<Table
columns={[
{
title: '标题',
dataIndex: 'title',
},
{
title: '链接',
dataIndex: 'link',
},
{
title: '图片',
dataIndex: 'image',
},
{
title: '操作',
render: (_, row) => (
<Space>
<Button type="primary"></Button>
<Button type="primary"></Button>
</Space>
)
}
]}
>
</Table>
</Card>
)
}

View File

@ -0,0 +1,72 @@
"use client"
import { ArticleType } from '@/app/_lib/data/article';
import ImageUpload from '@/app/_ui/ImageUpload';
import { faArrowLeft, faRibbon } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { BackTop, Button, Card, Col, Form, Grid, Input, Row, Upload } from 'antd';
import dynamic from 'next/dynamic';
import { useRouter } from 'next/navigation'
import { useState } from 'react';
const MDEditor = dynamic(() => import('@uiw/react-md-editor'), { ssr: false });
export default function Page() {
const router = useRouter()
return <>
<div className='w-full bg-white p-2 flex gap-x-1 items-center rounded shadow'>
<div className='flex gap-x-1 items-center text-[#666] hover:text-[#333] cursor-pointer '
onClick={() => {
router?.back()
}}
>
<FontAwesomeIcon icon={faArrowLeft}></FontAwesomeIcon>
</div>
</div>
<Card title="新增文章" className='mt-2'>
<Form<ArticleType>>
<Row gutter={24}>
<Col span={12}>
<Form.Item label="标题" name={"title"} >
<Input />
</Form.Item>
<Form.Item label="副标题" name={"description"}>
<Input />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item label="链接" name={"link"}>
<Input />
</Form.Item>
<Form.Item label="封面" name={"cover"}>
<ImageUpload
accept="image/*"
width={80}
height={80}
></ImageUpload>
</Form.Item>
</Col>
</Row>
<Form.Item label="内容" name={"content"}>
<MDEditor extraCommands={[
{
name: 'image',
icon: <span>1</span>, // 可以使用图标库的图标
execute: () => {
}, // 点击时调用插入图片的函数
},
]} />
</Form.Item>
</Form>
</Card>
</>
}

View File

@ -1,5 +1,42 @@
"use client"
import { Button, Card, Space, Table } from "antd";
import '@ant-design/v5-patch-for-react-19';
import { useRouter } from "next/navigation";
export default function Page() {
const router = useRouter()
return (
<></>
<Card title="文章管理" extra={<Button type="primary" onClick={() => {
router.push("/admin/dashboard/article/add")
}} >
</Button>}>
<Table
columns={[
{
title: '标题',
dataIndex: 'title',
},
{
title: '作者',
dataIndex: 'author',
},
{
title: '内容',
dataIndex: 'content',
},
{
title: '操作',
render: (_, row) => (
<Space>
<Button type="primary"></Button>
<Button type="primary"></Button>
</Space>
)
}
]}
></Table>
</Card>
)
}

View File

@ -1,7 +1,6 @@
"use client";
import { Button, Card, Drawer, Form, Image, Input, InputNumber, message, Modal, Popconfirm, Space, Table } from "antd";
import { useRef, useState } from "react";
import '@ant-design/v5-patch-for-react-19';
import { useAntdTable } from "ahooks";
import { mRequest } from "@/app/_lib/request";
import { LinkTypeItem } from "@/app/_lib/types";
@ -9,6 +8,7 @@ import LinkTable from "./LinkTable";
import ImageUpload from "@/app/_ui/ImageUpload";
import { useForm } from "antd/es/form/Form";
import { LinkType } from "@/app/_lib/data/linkType";
import '@ant-design/v5-patch-for-react-19';
export default function Page() {
const { tableProps, refresh } = useAntdTable(

View File

@ -0,0 +1,25 @@
import { SearchWayItemType } from "@/app/_lib/data/search";
import { mRequest } from "@/app/_lib/request";
import { useRequest } from "ahooks";
import { Checkbox, Space } from "antd";
import { useMemo } from "react";
export default function SearchSelect({ value, onChange }: { value?: string[]; onChange?: (e: any) => void }) {
const { data } = useRequest(() => mRequest<{
list: SearchWayItemType[]
}>('GET', '/api/search?page=1&pageSize=999'))
const options = useMemo(() => {
return data?.list.map(val => ({
label: val.fullName,
value: val._id
}))
}, [data])
return (
<Checkbox.Group
value={value}
options={options}
onChange={onChange}
/>
)
}

View File

@ -1,38 +1,41 @@
"use client";
import { SearchTypeItem, SearchWayItemType } from "@/app/_lib/data/search";
import { mRequest } from "@/app/_lib/request";
import { useAntdTable, useRequest } from "ahooks";
import { Button, Card, Space, Table } from "antd";
import { useAntdTable } from "ahooks";
import { Button, Card, Drawer, Form, Input, InputNumber, message, Popconfirm, Space, Table } from "antd";
import { useState } from "react";
import '@ant-design/v5-patch-for-react-19';
import SearchSelect from "./SearchSelect";
export default function Page() {
const searchWayList = useRequest(async () => mRequest('GET', "/api/search"))
const { data } = useAntdTable(async () => mRequest<{
const { tableProps, refresh } = useAntdTable(async ({ current, pageSize }) => mRequest<{
total: number;
list: SearchWayItemType[]
}>('GET', `/api/search?page=${current}&pageSize=${pageSize}`))
const { tableProps: typeTableProps, refresh: refreshTypeList } = useAntdTable(async ({ current, pageSize }) => mRequest<{
total: number;
list: SearchTypeItem[]
}>('GET', "/api/"))
return <Space>
{/*
<Card title="搜索分类管理" extra={<Button type="primary"></Button>}>
<Table<SearchTypeItem>
columns={[
{
title: '分类名称',
dataIndex: 'name'
},
{
title: '标示符',
dataIndex: 'key',
}>('GET', `/api/searchType?page=${current}&pageSize=${pageSize}`))
const [selected, setSelected] = useState<SearchWayItemType | null | undefined>()
const [selectedType, setSelectedType] = useState<SearchTypeItem | null | undefined>()
const [viewMode, setViewMode] = useState(false)
return <>
<Card title="搜索引擎管理"
extra={
<Space>
<Button onClick={() => {
refresh()
}}></Button>
<Button type="primary" onClick={() => {
setSelected(null)
}}></Button>
</Space>
},
{
title:
}
]}
>
</Table>
</Card> */}
<Card title="搜索引擎管理">
}>
<Table<SearchWayItemType>
{...tableProps}
rowKey={'_id'}
columns={[
{
title: '名称',
@ -46,6 +49,30 @@ export default function Page() {
{
title: '搜索链接',
dataIndex: 'value'
},
{
title: '操作',
render: (_, row) => (
<Space>
<Button type="primary" onClick={() => {
setSelected(row)
}}></Button>
<Popconfirm
title="确定删除"
description="是否删除该项?"
okText="确定"
cancelText="取消"
onConfirm={() => {
mRequest('DELETE', `/api/search/${row._id}`).then(res => {
refresh()
})
}}
>
<Button danger ></Button>
</Popconfirm>
</Space>
)
}
]}
>
@ -53,6 +80,214 @@ export default function Page() {
</Table>
</Card>
<Card
className="mt-2"
title="搜索分类管理"
extra={
<Space>
<Button onClick={() => {
refreshTypeList()
}}></Button>
<Button type="primary" onClick={() => {
setSelectedType(null)
}}></Button>
</Space>
}>
<Table<SearchTypeItem>
{...typeTableProps}
rowKey={'_id'}
columns={[
{
title: '名称',
dataIndex: 'name',
width: 400
},
{
title: '优先级',
dataIndex: 'priority'
},
{
title: '操作',
render: (_, row) => (
<Space>
<Button onClick={() => {
console.log(row);
setViewMode(true)
setSelectedType(row)
}}></Button>
<Button type="primary" onClick={() => {
setSelectedType(row)
}}></Button>
<Popconfirm
title="确定删除"
description="是否删除该项?"
okText="确定"
cancelText="取消"
onConfirm={() => {
mRequest('DELETE', `/api/searchType/${row._id}`).then(res => {
refreshTypeList()
message.success('删除成功')
})
}}
>
<Button danger ></Button>
</Popconfirm>
</Space>
)
}
]}
>
</Table>
</Card>
<Drawer
destroyOnClose
title={selected !== undefined ? selected === null ? "添加搜索引擎" : "修改搜索引擎" : ""}
open={selected !== undefined}
width={500}
onClose={() => {
setSelected(undefined)
}}
>
<Form
labelCol={{ span: 4 }}
initialValues={selected ?
selected
: {
}
}
onFinish={async (res) => {
if (selected) {
await mRequest("PUT", "/api/search", {
_id: selected._id, ...res
})
} else {
await mRequest("POST", "/api/search", {
...res,
})
}
refresh()
setSelected(undefined)
message.success("操作成功")
}}
>
<Form.Item
name="label"
label="简称"
rules={[{ required: true, message: "请输入名称" }]}
>
<Input />
</Form.Item>
<Form.Item
name="fullName"
label="全称"
rules={[{ required: true, message: "请输入名称" }]}
>
<Input />
</Form.Item>
<Form.Item
name="value"
label="链接"
rules={[{ required: true, message: "请输入名称" }]}
>
<Input placeholder="关键词以%s代替" />
</Form.Item>
<Form.Item className="flex justify-end">
<Space>
<Button>
</Button>
<Button type="primary" htmlType="submit">
</Button>
</Space>
</Form.Item>
</Form>
</Drawer>
<Drawer
destroyOnClose
title={viewMode ? '查看详请' : (selectedType !== undefined ? selectedType === null ? "添加搜索分类" : "修改搜索分类" : "")}
open={selectedType !== undefined}
width={500}
onClose={() => {
setSelectedType(undefined)
setViewMode(false)
}}
>
<Form
disabled={viewMode}
labelCol={{ span: 4 }}
initialValues={selectedType ?
selectedType
: {
priority: 0
}
}
onFinish={async (res) => {
if (selectedType) {
await mRequest("PUT", "/api/searchType", {
_id: selectedType._id, ...res
})
} else {
await mRequest("POST", "/api/searchType", {
...res,
})
}
refreshTypeList()
setSelectedType(undefined)
message.success("操作成功")
}}
>
<Form.Item
name="name"
label="分类名"
rules={[{ required: true, message: "请输入名称" }]}
>
<Input />
</Form.Item>
<Form.Item
name="includes"
label="启用引擎"
>
<SearchSelect ></SearchSelect>
</Form.Item>
<Form.Item
name="priority"
label="优先级"
>
<InputNumber ></InputNumber>
</Form.Item>
{
!viewMode &&
<Form.Item className="flex justify-end">
<Space>
<Button>
</Button>
<Button type="primary" htmlType="submit">
</Button>
</Space>
</Form.Item>
}
</Form>
</Drawer >
</>
}

View File

@ -1,6 +1,6 @@
import LoginState from "@/app/_ui/LoginState";
import SiderNav from "../../_ui/SiderNav";
import { faAd, faArrowAltCircleLeft, faMagnet, faPenClip } from "@fortawesome/free-solid-svg-icons"
import { faAd, faMagnet, faPenClip, faSearch } from "@fortawesome/free-solid-svg-icons"
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
export default function Layout({
@ -9,10 +9,10 @@ export default function Layout({
children: React.ReactNode;
}>) {
return (
<div className="flex flex-col">
<SiderNav
linkList={[
{
label: '链接管理',
@ -20,6 +20,12 @@ export default function Layout({
_id: 'addLink',
href: '/admin/dashboard'
},
{
label: '搜索管理',
iconElement: <FontAwesomeIcon icon={faSearch}></FontAwesomeIcon>,
_id: 'searchMenagement',
href: '/admin/dashboard/search'
},
{
label: '广告管理',
iconElement: <FontAwesomeIcon icon={faAd}></FontAwesomeIcon>,

View File

@ -0,0 +1,25 @@
import { verifySession } from "@/app/_lib/dal";
import { getCollection } from "@/app/_lib/mongodb";
import { ObjectId } from "mongodb";
import { NextRequest } from "next/server";
export async function DELETE(req: NextRequest, { params }: { params: Promise<{ id: string }> }) {
try {
const session = await verifySession()
// Check if the user is authenticated
if (!session) {
// User is not authenticated
return new Response(null, { status: 401 })
}
// 获取路径参数
const slug = (await params).id
const collection = await getCollection('article')
collection.deleteOne({
_id: new ObjectId(slug)
})
return Response.json({ message: '删除成功' })
} catch (e) {
return Response.error()
}
}

32
app/api/article/route.ts Normal file
View File

@ -0,0 +1,32 @@
import { getArticleList } from "@/app/_lib/data/article";
import { getCollection } from "@/app/_lib/mongodb";
import { NextRequest } from "next/server";
export async function GET(request: NextRequest) {
const page = parseInt(request.nextUrl.searchParams.get('page') || '1') || 1;
const pageSize = parseInt(request.nextUrl.searchParams.get('pageSize') || '10') || 10;
// 计算起始索引和结束索引
const res = getArticleList({ page, pageSize })
return Response.json(res)
}
export async function POST(request: NextRequest) {
try {
const body = await request.json();
const collection = await getCollection('article');
const data = await collection.insertOne(body);
return Response.json(data)
} catch (e) {
return Response.error()
}
}
export async function PUT(request: NextRequest) {
try {
const body = await request.json();
const collection = await getCollection('article');
const data = await collection.updateOne({ _id: body._id }, { $set: body });
return Response.json(data)
} catch (e) {
return Response.error()
}
}

View File

@ -1,8 +1,8 @@
import { getCollection, getDb } from "@/app/_lib/mongodb";
import { ObjectId } from "mongodb";
import { NextRequest } from "next/server";
import { Link } from "../route";
import { verifySession } from "@/app/_lib/dal";
import { Link } from "@/app/_lib/data/link";
export async function GET(req: NextRequest) {
try {

View File

@ -17,6 +17,7 @@ export async function GET(req: NextRequest) {
return Response.error()
}
}
export async function DELETE(req: NextRequest, { params }: { params: Promise<{ id: string }> }) {
try {
const session = await verifySession()
@ -28,7 +29,7 @@ export async function DELETE(req: NextRequest, { params }: { params: Promise<{ i
}
// 获取路径参数
const slug = (await params).id
const collection = await getCollection('link')
const collection = await getCollection('search')
collection.deleteOne({
_id: new ObjectId(slug)
})

View File

@ -1,10 +1,7 @@
import { verifySession } from "@/app/_lib/dal";
import { Link } from "@/app/_lib/data/link";
import { getSearchWayList } from "@/app/_lib/data/search";
import { User } from "@/app/_lib/data/user";
import { getCollection, getDb } from "@/app/_lib/mongodb";
import { message } from "antd";
import bcrypt from 'bcrypt';
import { ObjectId } from "mongodb";
import { NextRequest } from "next/server";
@ -15,7 +12,7 @@ export async function GET(req: NextRequest) {
const page = parseInt(req.nextUrl.searchParams.get('page') || '1') || 1;
const pageSize = parseInt(req.nextUrl.searchParams.get('pageSize') || '10') || 10;
const res = getSearchWayList({ page, pageSize })
const res = await getSearchWayList({ page, pageSize })
return Response.json(res)
} catch (e) {
return Response.error()
@ -50,11 +47,15 @@ export async function PUT(req: NextRequest) {
// User is not authenticated
return new Response(null, { status: 401 })
}
// 获取待更新的对象
const link = await req.json() as Link
const collection = await getCollection('link')
const collection = await getCollection('search')
await collection.replaceOne({ _id: new ObjectId(link._id) }, { ...link, _id: new ObjectId(link._id) })
return Response.json({ message: '成功' })
} catch (e) {
return Response.error()
}

View File

@ -11,7 +11,7 @@ export async function GET(req: NextRequest) {
const page = parseInt(req.nextUrl.searchParams.get('page') || '1') || 1;
const pageSize = parseInt(req.nextUrl.searchParams.get('pageSize') || '10') || 10;
const res = getSearchTypeList({ page, pageSize })
const res = await getSearchTypeList({ page, pageSize })
return Response.json(res)
} catch (e) {
return Response.error()
@ -27,7 +27,7 @@ export async function POST(req: NextRequest) {
return new Response(null, { status: 401 })
}
const collection = await getCollection('search-type')
const item = req.json()
const item = await req.json()
const res = collection.insertOne(item)
return Response.json(res)

View File

@ -19,3 +19,12 @@ body {
background: var(--background);
font-family: Arial, Helvetica, sans-serif;
}
.ant-input-disabled,
.ant-select-disabled .ant-select-selector {
background-color: transparent !important;
color: rgba(0, 0, 0, 0.85) !important;
}
.ant-checkbox-disabled+span{
background-color: transparent !important;
color: rgba(0, 0, 0, 0.85) !important;
}

View File

@ -3,7 +3,6 @@ import "./globals.css";
import '@fortawesome/fontawesome-svg-core/styles.css'
import '@ant-design/v5-patch-for-react-19';
export const metadata: Metadata = {
title: "Create Next App",
description: "Generated by create next app",

View File

@ -1,15 +1,5 @@
import HeaderNav from "./_ui/HeaderNav";
import SiderNav from "./_ui/SiderNav";
import Search from "./_ui/Search";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faArrowRotateBack, faDeafness, faImage, faMagnet, faMessage, faPenClip, faSearch, faThumbsUp, faVideo } from '@fortawesome/free-solid-svg-icons'
import { LinkTypeItem } from "./_lib/types";
import PosterBox from "./_ui/PosterBox";
import { mRequest } from "./_lib/request";
import { LinkType } from "./api/linkType/route";
import { getCollection } from "./_lib/mongodb";
import { Link as _Link } from "./api/link/route";
import Link from "next/link";
import LinkListBox from "./_ui/LinkListBox";
import { getLinkTypeList } from "./_lib/data/linkType";
import { getLinkList } from "./_lib/data/link";

View File

@ -13,6 +13,7 @@
"@fortawesome/fontawesome-svg-core": "^6.7.2",
"@fortawesome/free-solid-svg-icons": "^6.7.2",
"@fortawesome/react-fontawesome": "^0.2.2",
"@uiw/react-md-editor": "^4.0.5",
"ahooks": "^3.8.4",
"ali-oss": "^6.22.0",
"antd": "^5.23.2",

File diff suppressed because it is too large Load Diff