Skip to content

Commit d113fc5

Browse files
committed
feat: 标签和图片列表
1 parent e9210d3 commit d113fc5

File tree

5 files changed

+296
-34
lines changed

5 files changed

+296
-34
lines changed

app/admin/list/page.tsx

+117-13
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,126 @@
1-
import { Card, CardBody } from '@nextui-org/card'
1+
'use client'
2+
3+
import React from 'react'
4+
import { Card, CardHeader } from '@nextui-org/card'
5+
import { Button, Dropdown, DropdownItem, DropdownMenu, DropdownTrigger } from '@nextui-org/react'
6+
import { Table } from 'antd'
7+
import type { TableProps } from 'antd'
8+
import useSWR from 'swr'
9+
import { fetcher } from '~/utils/fetcher'
10+
import { toast } from 'sonner'
11+
import { DeleteDocumentBulkIcon, EditDocumentBulkIcon, SendFilledIcon, LinkIcon } from '@nextui-org/shared-icons'
12+
13+
interface DataType {
14+
id: number;
15+
tag: string;
16+
url: string;
17+
exif: any;
18+
rating: number;
19+
detail: string;
20+
show: number;
21+
sort: number;
22+
}
23+
24+
const iconClasses = 'text-xl text-default-500 pointer-events-none flex-shrink-0'
25+
26+
const columns: TableProps<DataType>['columns'] = [
27+
{
28+
title: '标签',
29+
dataIndex: 'tag',
30+
},
31+
{
32+
title: '评分',
33+
dataIndex: 'rating',
34+
},
35+
{
36+
title: '说明',
37+
dataIndex: 'detail',
38+
},
39+
{
40+
title: '显示',
41+
dataIndex: 'show',
42+
},
43+
{
44+
title: '排序',
45+
dataIndex: 'sort',
46+
},
47+
{
48+
title: '操作',
49+
key: 'action',
50+
render: (_, record) => (
51+
<Dropdown>
52+
<DropdownTrigger>
53+
<Button
54+
isIconOnly
55+
variant="bordered"
56+
>
57+
<SendFilledIcon />
58+
</Button>
59+
</DropdownTrigger>
60+
<DropdownMenu variant="faded" aria-label="图片操作选项卡">
61+
<DropdownItem
62+
key="edit"
63+
description="编辑图片信息"
64+
startContent={<EditDocumentBulkIcon className={iconClasses} />}
65+
onClick={() => toast.warning('还没写!')}
66+
>
67+
编辑
68+
</DropdownItem>
69+
<DropdownItem
70+
key="edit"
71+
description="同步图片地址"
72+
startContent={<LinkIcon className={iconClasses} />}
73+
onClick={() => toast.warning('还没写!')}
74+
>
75+
同步
76+
</DropdownItem>
77+
<DropdownItem
78+
key="delete"
79+
description="删除图片"
80+
startContent={<DeleteDocumentBulkIcon className={iconClasses + ' text-red-500'} />}
81+
onClick={() => toast.warning('还没写!')}
82+
>
83+
删除
84+
</DropdownItem>
85+
</DropdownMenu>
86+
</Dropdown>
87+
),
88+
},
89+
];
290

391
export default async function List() {
92+
const { data, error, isLoading, isValidating, mutate } = useSWR('/api/get-images', fetcher)
93+
94+
if (error) {
95+
toast.error('数据获取失败!')
96+
}
97+
498
return (
599
<div className="flex flex-col space-y-2 h-full flex-1">
6100
<Card>
7-
<CardBody>
8-
<p>
9-
图片维护
10-
</p>
11-
</CardBody>
12-
</Card>
13-
<Card className="flex-1">
14-
<CardBody>
15-
<p>
16-
图片维护页面
17-
</p>
18-
</CardBody>
101+
<CardHeader className="justify-between">
102+
<div className="flex gap-5">
103+
<div className="flex flex-col gap-1 items-start justify-center">
104+
<h4 className="text-small font-semibold leading-none text-default-600 select-none">标签管理</h4>
105+
</div>
106+
</div>
107+
<Button
108+
color="primary"
109+
radius="full"
110+
size="sm"
111+
isLoading={isValidating}
112+
onClick={() => mutate()}
113+
>
114+
刷新
115+
</Button>
116+
</CardHeader>
19117
</Card>
118+
<Table
119+
bordered
120+
columns={columns}
121+
dataSource={data}
122+
loading={isValidating}
123+
/>
20124
</div>
21125
)
22126
}

app/admin/tag/page.tsx

+103-14
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,111 @@
1-
import { Card, CardBody } from '@nextui-org/card'
1+
'use client'
2+
3+
import React from 'react'
4+
import { Card, CardHeader } from '@nextui-org/card'
5+
import { Button, Dropdown, DropdownItem, DropdownMenu, DropdownTrigger } from '@nextui-org/react'
6+
import { Table } from 'antd'
7+
import type { TableProps } from 'antd'
8+
import useSWR from 'swr'
9+
import { fetcher } from '~/utils/fetcher'
10+
import { toast } from 'sonner'
11+
import { DeleteDocumentBulkIcon, EditDocumentBulkIcon, SendFilledIcon } from '@nextui-org/shared-icons'
12+
13+
interface DataType {
14+
id: number;
15+
name: string;
16+
tag_value: string;
17+
detail: string;
18+
show: number;
19+
}
20+
21+
const iconClasses = 'text-xl text-default-500 pointer-events-none flex-shrink-0'
22+
23+
const columns: TableProps<DataType>['columns'] = [
24+
{
25+
title: '标签名称',
26+
dataIndex: 'name',
27+
},
28+
{
29+
title: '标签值',
30+
dataIndex: 'tag_value',
31+
},
32+
{
33+
title: '说明',
34+
dataIndex: 'detail',
35+
},
36+
{
37+
title: '显示',
38+
dataIndex: 'show',
39+
},
40+
{
41+
title: '操作',
42+
key: 'action',
43+
render: (_, record) => (
44+
<Dropdown>
45+
<DropdownTrigger>
46+
<Button
47+
isIconOnly
48+
variant="bordered"
49+
>
50+
<SendFilledIcon />
51+
</Button>
52+
</DropdownTrigger>
53+
<DropdownMenu variant="faded" aria-label="标签操作选项卡">
54+
<DropdownItem
55+
key="edit"
56+
description="编辑标签信息"
57+
startContent={<EditDocumentBulkIcon className={iconClasses} />}
58+
onClick={() => toast.warning('还没写!')}
59+
>
60+
编辑
61+
</DropdownItem>
62+
<DropdownItem
63+
key="delete"
64+
description="删除标签"
65+
startContent={<DeleteDocumentBulkIcon className={iconClasses + ' text-red-500'} />}
66+
onClick={() => toast.warning('还没写!')}
67+
>
68+
删除
69+
</DropdownItem>
70+
</DropdownMenu>
71+
</Dropdown>
72+
),
73+
},
74+
];
75+
76+
export default function List(callback: any, deps: React.DependencyList) {
77+
const { data, error, isLoading, isValidating, mutate } = useSWR('/api/get-tags', fetcher)
78+
79+
if (error) {
80+
toast.error('数据获取失败!')
81+
}
282

3-
export default async function List() {
483
return (
584
<div className="flex flex-col space-y-2 h-full flex-1">
685
<Card>
7-
<CardBody>
8-
<p>
9-
标签管理
10-
</p>
11-
</CardBody>
12-
</Card>
13-
<Card className="flex-1">
14-
<CardBody>
15-
<p>
16-
标签管理页面
17-
</p>
18-
</CardBody>
86+
<CardHeader className="justify-between">
87+
<div className="flex gap-5">
88+
<div className="flex flex-col gap-1 items-start justify-center">
89+
<h4 className="text-small font-semibold leading-none text-default-600 select-none">标签管理</h4>
90+
</div>
91+
</div>
92+
<Button
93+
color="primary"
94+
radius="full"
95+
size="sm"
96+
isLoading={isValidating}
97+
onClick={() => mutate()}
98+
>
99+
刷新
100+
</Button>
101+
</CardHeader>
19102
</Card>
103+
<Table
104+
bordered
105+
columns={columns}
106+
dataSource={data}
107+
loading={isValidating}
108+
/>
20109
</div>
21110
)
22111
}

app/admin/upload/page.tsx

+19-6
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,31 @@
1-
import { Card, CardBody } from '@nextui-org/card'
1+
import { Card, CardBody, CardHeader } from '@nextui-org/card'
22
import FileUpload from '~/components/FileUpload'
3+
import { Button } from '@nextui-org/react'
4+
import UploadSelect from '~/components/UploadSelect'
35

46
export default async function Upload() {
57

8+
69
return (
710
<div className="flex flex-col space-y-2 h-full flex-1">
811
<Card>
9-
<CardBody>
10-
<p>
11-
上传
12-
</p>
13-
</CardBody>
12+
<CardHeader className="justify-between">
13+
<div className="flex gap-5">
14+
<div className="flex flex-col gap-1 items-start justify-center">
15+
<h4 className="text-small font-semibold leading-none text-default-600 select-none">上传</h4>
16+
</div>
17+
</div>
18+
<Button
19+
color="primary"
20+
radius="full"
21+
size="sm"
22+
>
23+
提交
24+
</Button>
25+
</CardHeader>
1426
</Card>
1527
<Card className="flex-1">
28+
<UploadSelect />
1629
<CardBody>
1730
<FileUpload />
1831
</CardBody>

components/AListTabs.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ export default function AListTabs() {
4343
data.map((item: any) => (
4444
<TableRow key={item.id}>
4545
<TableCell>{item.config_key}</TableCell>
46-
<TableCell>{item.config_value || 'N&A'}</TableCell>
46+
<TableCell className="truncate max-w-60">{item.config_value || 'N&A'}</TableCell>
4747
</TableRow>
4848
))
4949
}

components/UploadSelect.tsx

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
'use client'
2+
3+
import { Button, Select, SelectItem } from '@nextui-org/react'
4+
import {CardHeader} from "@nextui-org/card";
5+
6+
export default async function UploadSelect() {
7+
const animals = [
8+
{label: "Cat", value: "cat", description: "The second most popular pet in the world"},
9+
{label: "Dog", value: "dog", description: "The most popular pet in the world"},
10+
{label: "Elephant", value: "elephant", description: "The largest land animal"},
11+
];
12+
13+
const storages = [
14+
{
15+
label: 'S3',
16+
value: 's3',
17+
},
18+
{
19+
label: 'AList',
20+
value: 'alist',
21+
}
22+
]
23+
24+
return (
25+
<CardHeader className="justify-between space-x-1">
26+
<Select
27+
isRequired
28+
color="primary"
29+
variant="bordered"
30+
label="存储"
31+
placeholder="请选择存储"
32+
defaultSelectedKeys={["cat"]}
33+
>
34+
{storages.map((storage) => (
35+
<SelectItem key={storage.value} value={storage.value}>
36+
{storage.label}
37+
</SelectItem>
38+
))}
39+
</Select>
40+
<Select
41+
isRequired
42+
color="secondary"
43+
variant="bordered"
44+
label="标签"
45+
placeholder="请选择标签"
46+
defaultSelectedKeys={["cat"]}
47+
>
48+
{animals.map((animal) => (
49+
<SelectItem key={animal.value} value={animal.value}>
50+
{animal.label}
51+
</SelectItem>
52+
))}
53+
</Select>
54+
</CardHeader>
55+
)
56+
}

0 commit comments

Comments
 (0)