fix: 优化验收操作和图片去重

This commit is contained in:
yml2213
2026-08-15 14:27:12 +08:00
parent 55a658b4fe
commit 190445319b
3 changed files with 77 additions and 46 deletions
@@ -44,10 +44,15 @@ function normalizePreviewItems(files: UploadedFile[], imageUrls: string[]) {
mediumUrl: file.mediumUrl || file.url, mediumUrl: file.mediumUrl || file.url,
})) }))
.filter((item) => Boolean(item.url)) .filter((item) => Boolean(item.url))
const fileUrlSet = new Set(normalizedFiles.map((item) => item.url)) const fileUrlSet = new Set(normalizedFiles.map((item) => getImageIdentity(item.url)))
const normalizedUrls = imageUrls const normalizedUrls = imageUrls
.map((url) => String(url || '').trim()) .map((url) => String(url || '').trim())
.filter((url) => url && !fileUrlSet.has(url)) .filter((url) => url && !fileUrlSet.has(getImageIdentity(url)))
.map((url) => ({ url, thumbnailUrl: url, mediumUrl: url })) .map((url) => ({ url, thumbnailUrl: url, mediumUrl: url }))
return [...normalizedFiles, ...normalizedUrls] return [...normalizedFiles, ...normalizedUrls]
} }
/** 忽略临时签名参数,避免同一存储对象重复显示。 */
function getImageIdentity(value: string) {
return String(value || '').trim().split(/[?#]/, 1)[0]
}
@@ -22,6 +22,7 @@ import {
Table, Table,
Tabs, Tabs,
Tag, Tag,
Tooltip,
Typography, Typography,
} from 'antd' } from 'antd'
import type { TableColumnsType } from 'antd' import type { TableColumnsType } from 'antd'
@@ -167,20 +168,7 @@ export default function WorkerOrdersPage() {
const source = hasSubmittedAcceptance(order) const source = hasSubmittedAcceptance(order)
? getWorkerAcceptance(order) ? getWorkerAcceptance(order)
: getWorkerDraft(order) : getWorkerDraft(order)
setAcceptanceFiles([ setAcceptanceFiles(getUniqueAcceptanceFiles(source))
...getAcceptanceFilesFromSource(source),
...(Array.isArray(source.imageUrls) ? source.imageUrls : []).map(
(url): UploadedFile => ({
objectKey: '',
url: String(url || ''),
thumbnailUrl: String(url || ''),
mediumUrl: String(url || ''),
filename: '图片',
contentType: '',
size: 0,
}),
),
])
form.setFieldsValue({ note: String(source.note || '') }) form.setFieldsValue({ note: String(source.note || '') })
setDetailOrder(null) setDetailOrder(null)
} }
@@ -438,27 +426,33 @@ export default function WorkerOrdersPage() {
}, },
{ {
title: '操作', title: '操作',
width: 260, width: 190,
render: (_, row) => ( render: (_, row) => (
<Space wrap> <div className="worker-order-actions">
<Button icon={<EyeOutlined />} onClick={() => setDetailOrder(row)}> <Space size={8}>
<Tooltip title="查看详情">
</Button> <Button
aria-label="查看详情"
icon={<EyeOutlined />}
onClick={() => setDetailOrder(row)}
/>
</Tooltip>
{canEditAcceptanceImages(row) ? ( {canEditAcceptanceImages(row) ? (
<Button <Button
icon={<PictureOutlined />} icon={<PictureOutlined />}
type={canSubmitAcceptance(row) ? 'primary' : 'default'} type={canSubmitAcceptance(row) ? 'primary' : 'default'}
onClick={() => openAcceptanceModal(row)} onClick={() => openAcceptanceModal(row)}
> >
{hasSubmittedAcceptance(row) ? '补充图片' : '上传图片 / 提交验收'} {hasSubmittedAcceptance(row) ? '补充图片' : '验收'}
</Button>
) : null}
{canCancelOrder(row) ? (
<Button danger onClick={() => openCancelModal(row)}>
</Button> </Button>
) : null} ) : null}
</Space> </Space>
{canCancelOrder(row) ? (
<Button type="link" danger size="small" onClick={() => openCancelModal(row)}>
</Button>
) : null}
</div>
), ),
}, },
] ]
@@ -740,7 +734,7 @@ export default function WorkerOrdersPage() {
editingOrder editingOrder
? hasSubmittedAcceptance(editingOrder) ? hasSubmittedAcceptance(editingOrder)
? '补充验收图片' ? '补充验收图片'
: '提交验收' : '验收图片'
: '验收图片' : '验收图片'
} }
open={Boolean(editingOrder)} open={Boolean(editingOrder)}
@@ -762,10 +756,7 @@ export default function WorkerOrdersPage() {
> >
</Button> </Button>
<Button <Button onClick={() => void saveAcceptanceImages(form.getFieldsValue())}>
type="primary"
onClick={() => void saveAcceptanceImages(form.getFieldsValue())}
>
{hasSubmittedAcceptance(editingOrder) ? '保存修改' : '保存暂存'} {hasSubmittedAcceptance(editingOrder) ? '保存修改' : '保存暂存'}
</Button> </Button>
{canSubmitAcceptance(editingOrder) ? ( {canSubmitAcceptance(editingOrder) ? (
@@ -778,15 +769,7 @@ export default function WorkerOrdersPage() {
} }
> >
<Form form={form} layout="vertical" onFinish={submitAcceptance}> <Form form={form} layout="vertical" onFinish={submitAcceptance}>
<Form.Item <Form.Item label="完成说明" name="note">
label="完成说明"
name="note"
extra={
editingOrder && !hasSubmittedAcceptance(editingOrder)
? '图片和说明可先「保存暂存」,之后随时继续补充,不会丢失;确认完成后再「提交验收」。'
: undefined
}
>
<Input.TextArea <Input.TextArea
rows={3} rows={3}
placeholder="可补充代练结果、注意事项或截图说明。" placeholder="可补充代练结果、注意事项或截图说明。"
@@ -979,6 +962,38 @@ function getAcceptanceFilesFromSource(source: WorkerAcceptance): UploadedFile[]
return Array.isArray(source.files) ? source.files : [] return Array.isArray(source.files) ? source.files : []
} }
/** 合并草稿兼容字段,并按固定图片地址去重,避免同一图片重复回填。 */
function getUniqueAcceptanceFiles(source: WorkerAcceptance): UploadedFile[] {
const files = [
...getAcceptanceFilesFromSource(source),
...(Array.isArray(source.imageUrls) ? source.imageUrls : []).map(
(url): UploadedFile => ({
objectKey: '',
url: String(url || ''),
thumbnailUrl: String(url || ''),
mediumUrl: String(url || ''),
filename: '图片',
contentType: '',
size: 0,
}),
),
]
const identities = new Set<string>()
return files.filter((file) => {
const identity = getImageIdentity(file.url || file.mediumUrl || file.thumbnailUrl || file.objectKey)
if (!identity || identities.has(identity)) return false
identities.add(identity)
return true
})
}
/** 去除临时签名参数,确保同一个对象的不同访问链接能匹配。 */
function getImageIdentity(value: string): string {
const url = String(value || '').trim()
if (!url) return ''
return url.split(/[?#]/, 1)[0]
}
function getAcceptanceFiles(row: WorkOrder): UploadedFile[] { function getAcceptanceFiles(row: WorkOrder): UploadedFile[] {
return getAcceptanceFilesFromSource(getWorkerAcceptance(row)) return getAcceptanceFilesFromSource(getWorkerAcceptance(row))
} }
+11
View File
@@ -1519,6 +1519,17 @@ select {
gap: 16px; gap: 16px;
} }
.worker-order-actions {
display: grid;
justify-items: start;
gap: 4px;
}
.worker-order-actions .ant-btn-link {
height: auto;
padding: 0;
}
.worker-order-detail-value { .worker-order-detail-value {
white-space: pre-wrap; white-space: pre-wrap;
overflow-wrap: anywhere; overflow-wrap: anywhere;