feat: ✨️ 新增图片上传类型校验及错误提示
This commit is contained in:
parent
116c171249
commit
a2b5df22f8
12
.prettierignore
Normal file
12
.prettierignore
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
/node_modules
|
||||||
|
package*.json
|
||||||
|
.gitignore
|
||||||
|
*.local
|
||||||
|
*_local
|
||||||
|
__test__
|
||||||
|
.ide
|
||||||
|
.vscode
|
||||||
|
.idea
|
||||||
|
test
|
||||||
|
dist
|
||||||
|
public
|
7
.prettierrc
Normal file
7
.prettierrc
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"useTabs": true,
|
||||||
|
"tabWidth": 2,
|
||||||
|
"singleQuote": true,
|
||||||
|
"trailingComma": "none",
|
||||||
|
"printWidth": 100
|
||||||
|
}
|
@ -2,12 +2,14 @@ import React, {useState} from "react";
|
|||||||
import {Input, Popconfirm, Spin, Upload, UploadProps} from "antd";
|
import {Input, Popconfirm, Spin, Upload, UploadProps} from "antd";
|
||||||
import {CloseOutlined} from "@ant-design/icons";
|
import {CloseOutlined} from "@ant-design/icons";
|
||||||
import {clsx} from "clsx";
|
import {clsx} from "clsx";
|
||||||
|
import {useTranslation} from "react-i18next";
|
||||||
|
|
||||||
import styles from './article.module.scss'
|
import styles from './article.module.scss'
|
||||||
import {getOssPolicy} from "@/service/api/common.ts";
|
import {getOssPolicy} from "@/service/api/common.ts";
|
||||||
import {showToast} from "@/components/message.ts";
|
import {showToast} from "@/components/message.ts";
|
||||||
import {IconAddImage, IconWarningCircle} from "@/components/icons";
|
import {IconAddImage} from "@/components/icons";
|
||||||
import {useTranslation} from "react-i18next";
|
import {ModalWarningIcon, ModalWarningTitle} from "@/components/icons/ModalWarning.tsx";
|
||||||
|
import { BizError } from '@/service/types.ts';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
@ -37,6 +39,10 @@ export function BlockImage({data, editable, onChange, onlyUpload, onRemove}: Ima
|
|||||||
});
|
});
|
||||||
const beforeUpload = async (file: any) => {
|
const beforeUpload = async (file: any) => {
|
||||||
try {
|
try {
|
||||||
|
// 判断文件类型
|
||||||
|
if (!MimeTypes.includes(file.type)) {
|
||||||
|
throw new Error('upload_file_type_error')
|
||||||
|
}
|
||||||
// 因为有超时问题,所以每次上传都重新获取参数
|
// 因为有超时问题,所以每次上传都重新获取参数
|
||||||
Data.uploadConfig = await getOssPolicy();
|
Data.uploadConfig = await getOssPolicy();
|
||||||
const suffix = file.name.slice(file.name.lastIndexOf('.'));
|
const suffix = file.name.slice(file.name.lastIndexOf('.'));
|
||||||
@ -52,17 +58,22 @@ export function BlockImage({data, editable, onChange, onlyUpload, onRemove}: Ima
|
|||||||
const onUploadChange = async (info) => {
|
const onUploadChange = async (info) => {
|
||||||
if (info.fileList.length == 0) return;
|
if (info.fileList.length == 0) return;
|
||||||
const file = info.fileList[0];
|
const file = info.fileList[0];
|
||||||
console.log('onChange', file);
|
console.log('onUploadChange', file);
|
||||||
if (file.status == 'done') {
|
if (file.status == 'done') {
|
||||||
setLoading(-1)
|
setLoading(-1);
|
||||||
onChange?.({type: 'image', content: Data.uploadConfig?.host + '/' + file.url})
|
onChange?.({ type: 'image', content: Data.uploadConfig?.host + '/' + file.url });
|
||||||
} else if (file.status == 'error') {
|
} else if (file.status == 'error') {
|
||||||
setLoading(-1)
|
|
||||||
showToast(t('upload.upload_failed'), 'warning')
|
if (!MimeTypes.includes(file.type)) {
|
||||||
|
showToast(t('upload.upload_file_type_error'), 'warning');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setLoading(-1);
|
||||||
|
showToast(t('upload.upload_failed'), 'warning');
|
||||||
} else if (file.status == 'uploading') {
|
} else if (file.status == 'uploading') {
|
||||||
setLoading(file.percent)
|
setLoading(file.percent);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
//
|
//
|
||||||
return <div className={styles.image}>
|
return <div className={styles.image}>
|
||||||
{editable && onlyUpload ? <div className={'relative'}>
|
{editable && onlyUpload ? <div className={'relative'}>
|
||||||
@ -104,8 +115,9 @@ export function BlockImage({data, editable, onChange, onlyUpload, onRemove}: Ima
|
|||||||
rootClassName={'popconfirm-main'}
|
rootClassName={'popconfirm-main'}
|
||||||
placement={'right'}
|
placement={'right'}
|
||||||
arrow={false}
|
arrow={false}
|
||||||
icon={<IconWarningCircle/>}
|
icon={<ModalWarningIcon/>}
|
||||||
title={<div style={{minWidth: 150}}><span>{t('upload.delete_confirm')}</span></div>}
|
title={<ModalWarningTitle/>}
|
||||||
|
description={<div style={{minWidth: 150}}><span>{t('upload.delete_confirm')}</span></div>}
|
||||||
onConfirm={onRemove}
|
onConfirm={onRemove}
|
||||||
okText={t('delete')}
|
okText={t('delete')}
|
||||||
cancelText={t('cancel')}
|
cancelText={t('cancel')}
|
||||||
|
@ -185,6 +185,7 @@
|
|||||||
"upload": {
|
"upload": {
|
||||||
"delete_confirm": "Are you sure delete the picture?",
|
"delete_confirm": "Are you sure delete the picture?",
|
||||||
"upload_failed": "Upload failed",
|
"upload_failed": "Upload failed",
|
||||||
|
"upload_file_type_error": "Only support upload image",
|
||||||
"upload_image": "Upload Image"
|
"upload_image": "Upload Image"
|
||||||
},
|
},
|
||||||
"user": {
|
"user": {
|
||||||
|
@ -185,6 +185,7 @@
|
|||||||
"upload": {
|
"upload": {
|
||||||
"delete_confirm": "请确认删除此图片?",
|
"delete_confirm": "请确认删除此图片?",
|
||||||
"upload_failed": "上传图片失败,请重试",
|
"upload_failed": "上传图片失败,请重试",
|
||||||
|
"upload_file_type_error": "仅支持上传图片",
|
||||||
"upload_image": "上传图片"
|
"upload_image": "上传图片"
|
||||||
},
|
},
|
||||||
"user": {
|
"user": {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import {Checkbox, Popconfirm, Space} from "antd";
|
import {Checkbox, Space} from "antd";
|
||||||
|
|
||||||
import React, {useRef, useState} from "react";
|
import React, {useRef, useState} from "react";
|
||||||
import {useRequest} from "ahooks";
|
import {useRequest} from "ahooks";
|
||||||
@ -10,13 +10,12 @@ import ButtonPush2Video, {ProcessResult} from "@/pages/news/components/button-pu
|
|||||||
|
|
||||||
import styles from './components/style.module.scss'
|
import styles from './components/style.module.scss'
|
||||||
import InfiniteScroller, {InfiniteScrollerRef} from "@/components/scoller/infinite-scroller.tsx";
|
import InfiniteScroller, {InfiniteScrollerRef} from "@/components/scoller/infinite-scroller.tsx";
|
||||||
import {IconDelete, IconEdit, IconWarningCircle} from "@/components/icons";
|
import {IconDelete, IconEdit} from "@/components/icons";
|
||||||
import {clsx} from "clsx";
|
import {clsx} from "clsx";
|
||||||
import ButtonToTop from "@/components/scoller/button-to-top.tsx";
|
import ButtonToTop from "@/components/scoller/button-to-top.tsx";
|
||||||
import ButtonDeleteBatch from "@/pages/news/components/button-delete-batch.tsx";
|
import ButtonDeleteBatch from "@/pages/news/components/button-delete-batch.tsx";
|
||||||
import {showErrorToast, showToast} from "@/components/message.ts";
|
import {showErrorToast, showToast} from "@/components/message.ts";
|
||||||
import {useTranslation} from "react-i18next";
|
import {useTranslation} from "react-i18next";
|
||||||
import {ModalWarningTitle,ModalWarningIcon} from "@/components/icons/ModalWarning.tsx";
|
|
||||||
import {DeleteItemPopoverConfirm} from "@/components/message/confirm.tsx";
|
import {DeleteItemPopoverConfirm} from "@/components/message/confirm.tsx";
|
||||||
|
|
||||||
const FilterCache: Partial<ApiArticleSearchParams> = {
|
const FilterCache: Partial<ApiArticleSearchParams> = {
|
||||||
@ -125,12 +124,12 @@ export default function NewEdit() {
|
|||||||
...prev,
|
...prev,
|
||||||
pagination: {page, limit: 10}
|
pagination: {page, limit: 10}
|
||||||
}))
|
}))
|
||||||
}} onScroll={(top) => setState({showToTop: top > 30})} loading={loading}
|
}} onScroll={(top) => setState(s=>({...s,showToTop: top > 30}))} loading={loading}
|
||||||
pagination={data?.pagination}>
|
pagination={data?.pagination}>
|
||||||
<div className="body">
|
<div className="body">
|
||||||
{data?.list?.map((item, i) => {
|
{data?.list?.map((item, i) => {
|
||||||
const checked = selectedRowKeys.includes(item.id)
|
const checked = selectedRowKeys.includes(item.id)
|
||||||
return <div key={i} className={clsx("row flex", {checked})}>
|
return <div key={item.id} className={clsx("row flex", {checked})}>
|
||||||
<div className="col title cursor-pointer" onClick={() => setEditId(item.id)}>
|
<div className="col title cursor-pointer" onClick={() => setEditId(item.id)}>
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<div className="text-base line-clamp-1">{item.title}</div>
|
<div className="text-base line-clamp-1">{item.title}</div>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import React, {useMemo, useRef, useState} from "react";
|
import React, {useMemo, useRef, useState} from "react";
|
||||||
import {Checkbox, Divider, Empty, Modal, Space} from "antd";
|
import {Checkbox, Divider, Empty, Modal, Space} from "antd";
|
||||||
import {useRequest} from "ahooks";
|
import { useRequest, useSetState } from 'ahooks';
|
||||||
import {CloseOutlined} from "@ant-design/icons"
|
import {CloseOutlined} from "@ant-design/icons"
|
||||||
import {clsx} from "clsx";
|
import {clsx} from "clsx";
|
||||||
|
|
||||||
@ -30,7 +30,7 @@ export default function NewsIndex() {
|
|||||||
|
|
||||||
const [activeNews, setActiveNews] = useState<NewsInfo>()
|
const [activeNews, setActiveNews] = useState<NewsInfo>()
|
||||||
|
|
||||||
const [state, setState] = useState<{
|
const [state, setState] = useSetState<{
|
||||||
checkAll?: boolean;
|
checkAll?: boolean;
|
||||||
showToTop?: boolean;
|
showToTop?: boolean;
|
||||||
}>({})
|
}>({})
|
||||||
@ -43,10 +43,10 @@ export default function NewsIndex() {
|
|||||||
FilterCache.tag_level_2_id = params.tag_level_2_id;
|
FilterCache.tag_level_2_id = params.tag_level_2_id;
|
||||||
FilterCache.title = params.title;
|
FilterCache.title = params.title;
|
||||||
FilterCache.time_flag = params.time_flag;
|
FilterCache.time_flag = params.time_flag;
|
||||||
setCheckedId([])
|
|
||||||
if (params.pagination.page === 1) {
|
if (params.pagination.page === 1) {
|
||||||
|
setCheckedId([])
|
||||||
setData(_data)
|
setData(_data)
|
||||||
setState({checkAll: checkedId && _data.list && checkedId.length === _data.list.length})
|
setState({checkAll: false,showToTop: false})
|
||||||
} else {
|
} else {
|
||||||
setData({
|
setData({
|
||||||
pagination: _data.pagination,
|
pagination: _data.pagination,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user