v3.5.3版本更新1
CI / verify (pull_request) Has been cancelled

This commit is contained in:
2026-08-20 18:54:15 +08:00
parent ee493823bf
commit fc2e5b65de
65 changed files with 2297 additions and 875 deletions
@@ -17,6 +17,11 @@ type Props = {
accept?: string;
/** 计量单位文案,如「张」「个」 */
unit?: string;
/**
* stack:缩略图 + 下方独立上传按钮(合同等)
* grid:九宫格,末尾「+」格上传,无独立大按钮(环境图)
*/
variant?: 'stack' | 'grid';
};
function isCancelError(msg: string): boolean {
@@ -42,6 +47,7 @@ export default function MultiOssUploadField({
label,
accept = 'image/*',
unit = '张',
variant = 'stack',
}: Props) {
const inputRef = useRef<HTMLInputElement>(null);
const pickingRef = useRef(false);
@@ -53,6 +59,7 @@ export default function MultiOssUploadField({
const onChangeRef = useRef(onChange);
const remaining = Math.max(0, maxCount - urls.length);
const inWechat = isWechatEnv();
const isGrid = variant === 'grid';
useEffect(() => {
urlsRef.current = urls;
@@ -126,11 +133,78 @@ export default function MultiOssUploadField({
}
}
function openPicker() {
if (inWechat) void pickWechat();
else inputRef.current?.click();
}
function removeAt(index: number) {
if (disabled) return;
onChange?.(urls.filter((_, i) => i !== index));
}
const thumb = (url: string, index: number) => (
<div key={`${url}-${index}`} className={isGrid ? 'partner-upload-grid-thumb' : undefined} style={isGrid ? undefined : { position: 'relative', width: 88, height: 88 }}>
{isPdf(url) ? (
<a
href={url}
target="_blank"
rel="noreferrer"
style={{
display: 'flex',
width: isGrid ? '100%' : 88,
height: isGrid ? '100%' : 88,
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
gap: 2,
borderRadius: 8,
border: '1px solid rgba(0,0,0,0.08)',
background: '#f7f7f7',
fontSize: 12,
}}
>
<span className="material-symbols-outlined text-primary" style={{ fontSize: 26 }}>
description
</span>
<span className="text-muted">PDF</span>
</a>
) : (
<img
src={url}
alt=""
style={{
width: isGrid ? '100%' : 88,
height: isGrid ? '100%' : 88,
objectFit: 'cover',
borderRadius: isGrid ? 12 : 8,
display: 'block',
}}
/>
)}
{!disabled ? (
<button
type="button"
className="partner-packages-remove"
style={{
position: 'absolute',
top: 2,
right: 2,
margin: 0,
padding: '2px 6px',
fontSize: 12,
background: 'rgba(0,0,0,0.55)',
color: '#fff',
borderRadius: 4,
}}
onClick={() => removeAt(index)}
>
</button>
) : null}
</div>
);
return (
<div className="partner-oss-upload">
<input
@@ -146,86 +220,49 @@ export default function MultiOssUploadField({
}}
/>
{urls.length > 0 ? (
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 8, marginBottom: 8 }}>
{urls.map((url, index) => (
<div key={`${url}-${index}`} style={{ position: 'relative', width: 88, height: 88 }}>
{isPdf(url) ? (
<a
href={url}
target="_blank"
rel="noreferrer"
style={{
display: 'flex',
width: 88,
height: 88,
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
gap: 2,
borderRadius: 8,
border: '1px solid rgba(0,0,0,0.08)',
background: '#f7f7f7',
fontSize: 12,
}}
>
<span className="material-symbols-outlined text-primary" style={{ fontSize: 26 }}>
description
</span>
<span className="text-muted">PDF</span>
</a>
) : (
<img
src={url}
alt=""
style={{ width: 88, height: 88, objectFit: 'cover', borderRadius: 8 }}
/>
)}
{!disabled ? (
<button
type="button"
className="partner-packages-remove"
style={{
position: 'absolute',
top: 2,
right: 2,
margin: 0,
padding: '2px 6px',
fontSize: 12,
background: 'rgba(0,0,0,0.55)',
color: '#fff',
borderRadius: 4,
}}
onClick={() => removeAt(index)}
>
</button>
) : null}
</div>
))}
{isGrid ? (
<div className="partner-upload-grid">
{urls.map((url, index) => thumb(url, index))}
{remaining > 0 && !disabled ? (
<button
type="button"
className="partner-upload-dashed partner-upload-dashed--compact"
disabled={uploading}
onClick={openPicker}
aria-label={uploading ? '上传中' : `添加${unit}${urls.length}/${maxCount}`}
>
<span className="material-symbols-outlined text-primary" style={{ fontSize: 28 }}>
{uploading ? 'hourglass_top' : 'add'}
</span>
</button>
) : null}
</div>
) : null}
<button
type="button"
className="partner-upload-dashed partner-upload-dashed--compact"
disabled={disabled || uploading || remaining <= 0}
onClick={() => {
if (inWechat) void pickWechat();
else inputRef.current?.click();
}}
>
<span className="material-symbols-outlined text-primary" style={{ fontSize: 28 }}>
{uploading ? 'hourglass_top' : 'add_a_photo'}
</span>
<span className="text-primary" style={{ fontWeight: 500 }}>
{uploading
? '上传中…'
: remaining <= 0
? `已达上限 ${maxCount}${unit}`
: label ?? `批量上传(${urls.length}/${maxCount}`}
</span>
</button>
) : (
<>
{urls.length > 0 ? (
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 8, marginBottom: 8 }}>
{urls.map((url, index) => thumb(url, index))}
</div>
) : null}
<button
type="button"
className="partner-upload-dashed partner-upload-dashed--compact"
disabled={disabled || uploading || remaining <= 0}
onClick={openPicker}
>
<span className="material-symbols-outlined text-primary" style={{ fontSize: 28 }}>
{uploading ? 'hourglass_top' : 'add_a_photo'}
</span>
<span className="text-primary" style={{ fontWeight: 500 }}>
{uploading
? '上传中…'
: remaining <= 0
? `已达上限 ${maxCount}${unit}`
: label ?? `批量上传(${urls.length}/${maxCount}`}
</span>
</button>
</>
)}
{error ? (
<p className="partner-form-error" role="alert">
{error}