商品二维码上传改为黏贴上传

This commit is contained in:
db_huang 2026-09-18 18:08:02 +08:00
parent 68b59d2eab
commit 86a1a33693
3 changed files with 74 additions and 3 deletions

View File

@ -79,6 +79,7 @@ export default function CreateAskTasksPage({ api, librarySelection = [], onLibra
const [addDialog, setAddDialog] = useState(null); const [addDialog, setAddDialog] = useState(null);
const [publishOpen, setPublishOpen] = useState(false); const [publishOpen, setPublishOpen] = useState(false);
const [linkCopied, setLinkCopied] = useState(false); const [linkCopied, setLinkCopied] = useState(false);
const productQrPasteRef = useRef(null);
const processingLibrarySelection = useRef(false); const processingLibrarySelection = useRef(false);
const consumedLibrarySelectionKey = useRef(""); const consumedLibrarySelectionKey = useRef("");
useEffect(() => { useEffect(() => {
@ -160,6 +161,35 @@ export default function CreateAskTasksPage({ api, librarySelection = [], onLibra
if (productQrInputRef.current) productQrInputRef.current.value = ""; if (productQrInputRef.current) productQrInputRef.current.value = "";
} }
useEffect(() => {
if (!publishOpen) return undefined;
const pasteProductQr = (event) => {
const imageItem = Array.from(event.clipboardData?.items || []).find((item) =>
item.kind === "file" && item.type.startsWith("image/"),
);
if (!imageItem) return;
const blob = imageItem.getAsFile?.();
if (!blob) {
setError("读取剪贴板图片失败,请重新复制后再试");
return;
}
const extension = blob.type === "image/jpeg" ? "jpg" : blob.type.split("/")[1] || "png";
const pastedFile = new File(
[blob],
`product-qr-${Date.now()}.${extension}`,
{ type: blob.type, lastModified: Date.now() },
);
event.preventDefault();
selectProductQr(pastedFile);
};
window.addEventListener("paste", pasteProductQr);
const focusTimer = window.setTimeout(() => productQrPasteRef.current?.focus(), 0);
return () => {
window.clearTimeout(focusTimer);
window.removeEventListener("paste", pasteProductQr);
};
}, [publishOpen]);
function clearAll() { function clearAll() {
if (busy) return; if (busy) return;
clearPageCache("create-ask-task"); clearPageCache("create-ask-task");
@ -814,12 +844,21 @@ export default function CreateAskTasksPage({ api, librarySelection = [], onLibra
{form.product_qr_image ? ( {form.product_qr_image ? (
<div className="ask-product-qr-preview"> <div className="ask-product-qr-preview">
<img src={form.product_qr_image} alt="商品二维码预览" /> <img src={form.product_qr_image} alt="商品二维码预览" />
<div><strong>{form.product_qr_image_name || "商品二维码"}</strong><small>已上传商品二维码</small><button type="button" className="ghost-button" onClick={clearProductQr}>移除二维码</button></div> <div><strong>{form.product_qr_image_name || "商品二维码"}</strong><small>已上传商品二维码,可粘贴图片替换</small><button type="button" className="ghost-button" onClick={clearProductQr}>移除二维码</button></div>
</div> </div>
) : ( ) : (
<button type="button" className="ask-product-qr-upload" onClick={() => productQrInputRef.current?.click()}><span>+ 上传商品二维码</span><small>支持 JPG、PNG、WEBP,单张不超过 5MB</small></button> <button
ref={productQrPasteRef}
type="button"
className="ask-product-qr-upload"
onClick={() => productQrInputRef.current?.click()}
aria-label="粘贴或选择商品二维码"
>
<span>+ 粘贴或上传商品二维码</span>
<small>复制图片后按 Ctrl+V 粘贴,也可选择 JPG、PNG、WEBP,单张不超过 5MB</small>
</button>
)} )}
<small className="ask-product-token-hint">淘口令和二维码至少填写一种,也可以两者都填写</small> <small className="ask-product-token-hint">淘口令和二维码至少填写一种,也可以两者都填写;复制二维码图片后可直接按 Ctrl+V 粘贴</small>
</div> </div>
<label> <label>
每个用户可领取问题数 * 每个用户可领取问题数 *

View File

@ -84,6 +84,10 @@ function TaskDetail({ task, close, onConfirmRefund, onCreateRefundUpload, onPoll
setRefundFileError(""); setRefundFileError("");
return true; return true;
} }
function clearRefundProofFile() {
setRefundFile(null);
setRefundFileError("");
}
useEffect(() => { useEffect(() => {
if (!refundOpen || refundSession || !task.refund_endpoint) return undefined; if (!refundOpen || refundSession || !task.refund_endpoint) return undefined;
let cancelled = false; let cancelled = false;
@ -422,6 +426,15 @@ function TaskDetail({ task, close, onConfirmRefund, onCreateRefundUpload, onPoll
<span>剪贴板粘贴上传(可选)</span> <span>剪贴板粘贴上传(可选)</span>
<strong>{refundFile ? "图片已粘贴,可直接确认" : "复制图片后,按 Ctrl+V 粘贴"}</strong> <strong>{refundFile ? "图片已粘贴,可直接确认" : "复制图片后,按 Ctrl+V 粘贴"}</strong>
<small>{refundFile ? `${refundFile.name} · ${(refundFile.size / 1024).toFixed(0)}KB` : "支持 JPG、PNG、WEBP,最大 10MB"}</small> <small>{refundFile ? `${refundFile.name} · ${(refundFile.size / 1024).toFixed(0)}KB` : "支持 JPG、PNG、WEBP,最大 10MB"}</small>
{refundFile && (
<button
type="button"
className="refund-proof-remove"
onClick={clearRefundProofFile}
>
移除图片
</button>
)}
{refundFileError && <em>{refundFileError}</em>} {refundFileError && <em>{refundFileError}</em>}
</div> </div>
<label className="refund-proof-fallback"> <label className="refund-proof-fallback">

View File

@ -2929,6 +2929,20 @@
font-size: 12px; font-size: 12px;
font-style: normal; font-style: normal;
} }
.refund-proof-remove {
justify-self: start;
padding: 3px 7px;
border: 1px solid #f0b7c0;
border-radius: 6px;
background: #fff5f6;
color: #d23850;
font-size: 11px;
cursor: pointer;
}
.refund-proof-remove:hover {
border-color: #d23850;
background: #ffecef;
}
.refund-proof-fallback { .refund-proof-fallback {
display: inline-flex; display: inline-flex;
justify-content: flex-start; justify-content: flex-start;
@ -5148,6 +5162,11 @@
text-align: left; text-align: left;
cursor: pointer; cursor: pointer;
} }
.ask-product-qr-upload:focus {
border-color: #1764e8;
outline: 0;
box-shadow: 0 0 0 3px rgba(23, 100, 232, .1);
}
.ask-product-qr-upload small { .ask-product-qr-upload small {
color: #8296ad; color: #8296ad;
font-size: 10px; font-size: 10px;