diff --git a/components/admin/upload/FileUpload.tsx b/components/admin/upload/FileUpload.tsx
index 087a0ee..619c2a6 100644
--- a/components/admin/upload/FileUpload.tsx
+++ b/components/admin/upload/FileUpload.tsx
@@ -11,6 +11,10 @@ import { Button, Select, SelectItem, Input, Divider, Card, CardBody, CardHeader,
import ExifReader from 'exifreader'
import Compressor from 'compressorjs'
import { TagInput } from '@douyinfe/semi-ui'
+import { Select as ShadcnSelect, SelectContent, SelectItem as ShadcnSelectItem, SelectTrigger, SelectValue } from '~/components/ui/Select'
+import { useButtonStore } from '~/app/providers/button-store-Providers'
+import { CircleHelp } from 'lucide-react'
+import FileUploadHelpSheet from '~/components/admin/upload/FileUploadHelpSheet'
export default function FileUpload() {
const [alistStorage, setAlistStorage] = useState([])
@@ -29,6 +33,10 @@ export default function FileUpload() {
const [lon, setLon] = useState('')
const [detail, setDetail] = useState('')
const [imageLabels, setImageLabels] = useState([] as string[])
+ const [mode, setMode] = useState('singleton')
+ const { setUploadHelp } = useButtonStore(
+ (state) => state,
+ )
const { data, isLoading } = useSWR('/api/v1/get-tags', fetcher)
@@ -216,7 +224,104 @@ export default function FileUpload() {
}).then((res) => res.json())
}
- async function uploadPreviewImage(option: any, type: string) {
+ async function autoSubmit(file: any, url: string, previewUrl: string) {
+ try {
+ if (mode === 'multiple') {
+ const tagArray = Array.from(tag)
+ if (tagArray.length === 0 || tagArray[0] === '') {
+ toast.warning('请先选择相册!')
+ return
+ }
+ const tags = await ExifReader.load(file)
+ const exifObj = {
+ make: '',
+ model: '',
+ bits: '',
+ data_time: '',
+ exposure_time: '',
+ f_number: '',
+ exposure_program: '',
+ iso_speed_rating: '',
+ focal_length: '',
+ lens_specification: '',
+ lens_model: '',
+ exposure_mode: '',
+ cfa_pattern: '',
+ color_space: '',
+ white_balance: '',
+ } as ExifType
+ exifObj.make = tags?.Make?.description
+ exifObj.model = tags?.Model?.description
+ exifObj.bits = tags?.['Bits Per Sample']?.description
+ exifObj.data_time = tags?.DateTime?.description
+ exifObj.exposure_time = tags?.ExposureTime?.description
+ exifObj.f_number = tags?.FNumber?.description
+ exifObj.exposure_program = tags?.ExposureProgram?.description
+ exifObj.iso_speed_rating = tags?.ISOSpeedRatings?.description
+ exifObj.focal_length = tags?.FocalLength?.description
+ exifObj.lens_specification = tags?.LensSpecification?.description
+ exifObj.lens_model = tags?.LensModel?.description
+ exifObj.exposure_mode = tags?.ExposureMode?.description
+ exifObj.cfa_pattern = tags?.CFAPattern?.description
+ exifObj.color_space = tags?.ColorSpace?.description
+ exifObj.white_balance = tags?.WhiteBalance?.description
+ if (tags?.GPSLatitude?.description) {
+ setLat(tags?.GPSLatitude?.description)
+ } else {
+ setLat('')
+ }
+ if (tags?.GPSLongitude?.description) {
+ setLon(tags?.GPSLongitude?.description)
+ } else {
+ setLon('')
+ }
+ try {
+ const reader = new FileReader();
+ reader.onload = (e) => {
+ const img = new Image();
+ img.onload = async () => {
+ const data = {
+ tag: tagArray[0],
+ url: url,
+ title: '',
+ preview_url: previewUrl,
+ exif: exifObj,
+ labels: [],
+ detail: '',
+ width: Number(img.width),
+ height: Number(img.height),
+ lat: '',
+ lon: '',
+ } as ImageType
+ const res = await fetch('/api/v1/image-add', {
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ method: 'post',
+ // @ts-ignore
+ body: JSON.stringify(data),
+ }).then(res => res.json())
+ console.log(res)
+ if (res?.code === 200) {
+ toast.success('保存成功!')
+ } else {
+ toast.error('保存失败!')
+ }
+ };
+ // @ts-ignore
+ img.src = e.target.result;
+ };
+ reader.readAsDataURL(file);
+ } catch (e) {
+ console.log(e)
+ }
+ }
+ } catch (e) {
+ console.log(e)
+ }
+ }
+
+ async function uploadPreviewImage(option: any, type: string, url: string) {
new Compressor(option.file, {
quality: 0.3,
checkOrientation: false,
@@ -228,6 +333,7 @@ export default function FileUpload() {
toast.success('预览图片上传成功!')
option.onSuccess(option.file)
setPreviewUrl(res?.data)
+ await autoSubmit(option.file, url, res?.data)
} else {
toast.error('预览图片上传失败!')
}
@@ -240,6 +346,7 @@ export default function FileUpload() {
toast.success('预览图片上传成功!')
option.onSuccess(option.file)
setPreviewUrl(res?.data)
+ await autoSubmit(option.file, url, res?.data)
} else {
toast.error('预览图片上传失败!')
}
@@ -259,44 +366,52 @@ export default function FileUpload() {
toast.success('图片上传成功,尝试生成预览图片并上传!')
try {
if (tagArray[0] === '/') {
- await uploadPreviewImage(option, '/preview')
+ await uploadPreviewImage(option, '/preview', res?.data)
} else {
- await uploadPreviewImage(option, tagArray[0] + '/preview')
+ await uploadPreviewImage(option, tagArray[0] + '/preview', res?.data)
}
} catch (e) {
console.log(e)
option.onSuccess(option.file)
}
- await loadExif(option.file)
- setUrl(res?.data)
+ if (mode === 'singleton') {
+ await loadExif(option.file)
+ setUrl(res?.data)
+ }
} else {
option.onError(option.file)
toast.error('图片上传失败!')
}
}
+ async function onRemoveFile() {
+ setStorageSelect(false)
+ setStorage(new Set([] as string[]))
+ setTag(new Set([] as string[]))
+ setAlistMountPath(new Set([] as string[]))
+ setExif({} as ExifType)
+ setUrl('')
+ setTitle('')
+ setDetail('')
+ setWidth(0)
+ setHeight(0)
+ setLat('')
+ setLon('')
+ setPreviewUrl('')
+ setImageLabels([])
+ }
+
const props: UploadProps = {
listType: "picture",
name: 'file',
- multiple: false,
- maxCount: 1,
+ multiple: mode === 'multiple',
+ maxCount: mode === 'singleton' ? 1 : 5,
customRequest: (file) => onRequestUpload(file),
beforeUpload: async (file) => await onBeforeUpload(file),
- onRemove: (file) => {
- setStorageSelect(false)
- setStorage(new Set([] as string[]))
- setTag(new Set([] as string[]))
- setAlistMountPath(new Set([] as string[]))
- setExif({} as ExifType)
- setUrl('')
- setTitle('')
- setDetail('')
- setWidth(0)
- setHeight(0)
- setLat('')
- setLon('')
- setPreviewUrl('')
- setImageLabels([])
+ onRemove: async (file) => {
+ if (mode === 'singleton') {
+ await onRemoveFile()
+ }
}
}
@@ -305,20 +420,59 @@ export default function FileUpload() {
-
-
上传
-
+
{
+ setMode(value)
+ }}
+ >
+
+
+
+
+
+ 单文件上传
+
+
+ 多文件上传
+
+
+
+
+
+
+ {mode === 'singleton'
+ ?
+ :
+
+ }
-
@@ -416,7 +570,7 @@ export default function FileUpload() {
{
- url && url !== '' &&
+ url && url !== '' && mode === 'singleton' &&
+
)
}
\ No newline at end of file
diff --git a/components/admin/upload/FileUploadHelpSheet.tsx b/components/admin/upload/FileUploadHelpSheet.tsx
new file mode 100644
index 0000000..34a3e69
--- /dev/null
+++ b/components/admin/upload/FileUploadHelpSheet.tsx
@@ -0,0 +1,57 @@
+'use client'
+
+import { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle } from '~/components/ui/Sheet'
+import { useButtonStore } from '~/app/providers/button-store-Providers'
+
+export default function FileUploadHelpSheet() {
+ const {uploadHelp, setUploadHelp} = useButtonStore(
+ (state) => state,
+ )
+
+ return (
+ {
+ if (!open) {
+ setUploadHelp(false)
+ }
+ }}
+ modal={false}
+ >
+
+
+ 帮助
+
+
+ 您在当前页面可以上传图片。
+
+
+ 单文件上传模式:
+ 选择好存储和相册后,选择文件或拖入文件到上传框,会自动上传文件到对应的存储。
+ 同时以 0.3 倍率压缩为 webp 格式,生成一张预览用的图片。
+ 同时上传完毕后,您可以编辑图片的一些信息,最后点击保存入库。
+
+
+ 多文件上传模式:
+ 选择好存储和相册后,选择文件或拖入文件到上传框,会自动上传文件到对应的存储。
+ 多文件上传模式下,无法在数据入库之前进行编辑,多文件上传属于全自动化上传,无需手动入库。
+ 上传队列最大支持 5 个,上传完毕后您可以将图片从上传队列中删除。
+ 重置按钮会重置存储和相册等数据。
+
+
+ 注:文件上传时,会自动获取图片的宽高,请您勿随意更改,否则可能导致前端展示错位。
+ 非必填项您可以在图片数据入库后,去图片维护里面进行编辑。
+
+
+ 注:部分云平台,限制了上传请求的主体大小,比如 Vercel 的免费用户限制 6M。
+
+
+ 如您有更多疑问欢迎反馈!
+
+
+
+
+
+ )
+}
\ No newline at end of file
diff --git a/stores/buttonStores.ts b/stores/buttonStores.ts
index 4fe5a62..04d08f1 100644
--- a/stores/buttonStores.ts
+++ b/stores/buttonStores.ts
@@ -23,6 +23,7 @@ export type ButtonState = {
MasonryViewData: ImageType
tagHelp: boolean
imageHelp: boolean
+ uploadHelp: boolean
}
export type ButtonActions = {
@@ -46,6 +47,7 @@ export type ButtonActions = {
setMasonryViewData: (masonryViewData: ImageType) => void
setTagHelp: (tagHelp: boolean) => void
setImageHelp: (imageHelp: boolean) => void
+ setUploadHelp: (uploadHelp: boolean) => void
}
export type ButtonStore = ButtonState & ButtonActions
@@ -72,6 +74,7 @@ export const initButtonStore = (): ButtonState => {
MasonryViewData: {} as ImageType,
tagHelp: false,
imageHelp: false,
+ uploadHelp: false,
}
}
@@ -96,6 +99,7 @@ export const defaultInitState: ButtonState = {
MasonryViewData: {} as ImageType,
tagHelp: false,
imageHelp: false,
+ uploadHelp: false,
}
export const createButtonStore = (
@@ -165,6 +169,9 @@ export const createButtonStore = (
setImageHelp: (imageHelpValue) => set(() => ({
imageHelp: imageHelpValue,
})),
+ setUploadHelp: (uploadHelpValue) => set(() => ({
+ uploadHelp: uploadHelpValue,
+ })),
}),
{
name: 'pic-impact-button-storage', // name of the item in the storage (must be unique)