fix: 显示调研草稿发布反馈
This commit is contained in:
parent
89878b7880
commit
1859d03760
@ -104,6 +104,16 @@ function jsonResponse(data: unknown) {
|
||||
} as unknown as Response
|
||||
}
|
||||
|
||||
function jsonErrorResponse(error: string, status = 409) {
|
||||
return {
|
||||
ok: false,
|
||||
status,
|
||||
headers: { get: () => 'application/json' },
|
||||
json: async () => ({ success: false, error }),
|
||||
text: async () => '',
|
||||
} as unknown as Response
|
||||
}
|
||||
|
||||
describe('考核调研顶层导航保护', () => {
|
||||
const originalFetch = global.fetch
|
||||
|
||||
@ -241,4 +251,67 @@ describe('考核调研顶层导航保护', () => {
|
||||
expect.anything(),
|
||||
))
|
||||
})
|
||||
|
||||
test('我发起的草稿发布失败时展示发布进度和后端原因', async () => {
|
||||
mockSearchParams = new URLSearchParams('tab=personal&workspace=created')
|
||||
let resolvePublish: (response: Response) => void = () => {}
|
||||
const publishResponse = new Promise<Response>((resolve) => {
|
||||
resolvePublish = resolve
|
||||
})
|
||||
global.fetch = jest.fn(async (input, init) => {
|
||||
const url = String(input)
|
||||
if (url.endsWith('/me')) {
|
||||
return jsonResponse({
|
||||
user: { id: 'user-admin', name: '管理员', email: 'admin@example.com' },
|
||||
employee: { id: 'employee-admin', displayName: '管理员' },
|
||||
permissions: {
|
||||
canCreate: true,
|
||||
isSystemAdmin: true,
|
||||
isDepartmentAdmin: true,
|
||||
isLeadership: true,
|
||||
},
|
||||
})
|
||||
}
|
||||
if (url.endsWith('/surveys') && (!init?.method || init.method === 'GET')) {
|
||||
return jsonResponse({
|
||||
summary: {
|
||||
pendingCount: 0,
|
||||
submittedCount: 0,
|
||||
createdCount: 1,
|
||||
openCreatedCount: 0,
|
||||
},
|
||||
pending: [],
|
||||
submitted: [],
|
||||
created: [{
|
||||
id: 'survey-draft',
|
||||
title: '2026年7月 · AI作品评审',
|
||||
status: 'draft',
|
||||
departmentName: '综合支持部',
|
||||
dueAt: null,
|
||||
counts: { targets: 1, assignments: 3, responses: 0 },
|
||||
}],
|
||||
})
|
||||
}
|
||||
if (url.endsWith('/surveys/survey-draft') && init?.method === 'PATCH') {
|
||||
return publishResponse
|
||||
}
|
||||
return jsonResponse([])
|
||||
}) as jest.Mock
|
||||
|
||||
render(<PerformanceSurveyPage />)
|
||||
|
||||
const publishButtons = await screen.findAllByRole('button', { name: '发布调研:2026年7月 · AI作品评审' })
|
||||
fireEvent.click(publishButtons[0])
|
||||
|
||||
expect(await screen.findAllByText('发布中...')).not.toHaveLength(0)
|
||||
resolvePublish(jsonErrorResponse('视觉评分作品尚未上传完整:无邪(0/3)'))
|
||||
|
||||
expect(await screen.findAllByRole('alert')).not.toHaveLength(0)
|
||||
expect(await screen.findAllByText('发布失败:视觉评分作品尚未上传完整:无邪(0/3)')).not.toHaveLength(0)
|
||||
expect(screen.getAllByRole('link', { name: '继续上传作品' })[0]).toHaveAttribute(
|
||||
'href',
|
||||
'/performance-survey/survey/survey-draft',
|
||||
)
|
||||
expect(screen.getAllByRole('button', { name: '发布调研:2026年7月 · AI作品评审' })[0]).toBeEnabled()
|
||||
})
|
||||
})
|
||||
|
||||
@ -2335,15 +2335,29 @@ function CreatedList({
|
||||
resultBackHref?: string
|
||||
}) {
|
||||
const [busyId, setBusyId] = useState<string | null>(null)
|
||||
const [actionErrors, setActionErrors] = useState<Record<string, string>>({})
|
||||
|
||||
const changeStatus = async (id: string, status: string) => {
|
||||
const actionLabel = status === 'open' ? '发布' : '关闭'
|
||||
setBusyId(id)
|
||||
setActionErrors((current) => {
|
||||
if (!current[id]) return current
|
||||
const next = { ...current }
|
||||
delete next[id]
|
||||
return next
|
||||
})
|
||||
try {
|
||||
await apiRequest(`/surveys/${id}`, {
|
||||
method: 'PATCH',
|
||||
body: JSON.stringify({ status }),
|
||||
})
|
||||
await onRefresh()
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : `${actionLabel}失败,请稍后重试`
|
||||
setActionErrors((current) => ({
|
||||
...current,
|
||||
[id]: `${actionLabel}失败:${message}`,
|
||||
}))
|
||||
} finally {
|
||||
setBusyId(null)
|
||||
}
|
||||
@ -2382,6 +2396,8 @@ function CreatedList({
|
||||
const progressText = assignmentCount > 0
|
||||
? `已提交 ${responseCount}/${assignmentCount}`
|
||||
: '尚未生成评价任务'
|
||||
const actionError = actionErrors[item.id]
|
||||
const needsWorkItemUpload = actionError?.includes('作品尚未上传完整')
|
||||
|
||||
return (
|
||||
<article
|
||||
@ -2411,7 +2427,7 @@ function CreatedList({
|
||||
aria-label={`发布调研:${item.title}`}
|
||||
className="min-h-11 rounded border border-[#b8d0ff] bg-[#edf4ff] px-4 text-sm font-bold text-[#0050cb] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[#0050cb] focus-visible:ring-offset-2 disabled:opacity-50"
|
||||
>
|
||||
发布
|
||||
{busyId === item.id ? '发布中...' : '发布'}
|
||||
</button>
|
||||
)}
|
||||
{item.status !== 'closed' && item.status !== 'cancelled' && (
|
||||
@ -2422,7 +2438,7 @@ function CreatedList({
|
||||
aria-label={`关闭调研:${item.title}`}
|
||||
className="min-h-11 rounded border border-[#c2c6d8] bg-white px-4 text-sm font-bold text-[#1a1c1c] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[#0050cb] focus-visible:ring-offset-2 disabled:opacity-50"
|
||||
>
|
||||
关闭
|
||||
{busyId === item.id ? '处理中...' : '关闭'}
|
||||
</button>
|
||||
)}
|
||||
{item.canCancel && (
|
||||
@ -2446,6 +2462,22 @@ function CreatedList({
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
{actionError && (
|
||||
<div
|
||||
role="alert"
|
||||
className="mt-3 rounded border border-[#f0b8b8] bg-[#fff5f5] px-3 py-2 text-[13px] font-medium leading-5 text-[#b42318]"
|
||||
>
|
||||
{actionError}
|
||||
{needsWorkItemUpload && (
|
||||
<Link
|
||||
href={`/performance-survey/survey/${item.id}`}
|
||||
className="ml-2 inline-flex min-h-8 items-center rounded border border-[#f0b8b8] bg-white px-2.5 font-bold text-[#b42318] underline-offset-2 hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[#b42318] focus-visible:ring-offset-2"
|
||||
>
|
||||
继续上传作品
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</article>
|
||||
)
|
||||
})}
|
||||
@ -2467,6 +2499,8 @@ function CreatedList({
|
||||
const targetCount = Number(item.counts?.targets || 0)
|
||||
const assignmentCount = Number(item.counts?.assignments || 0)
|
||||
const responseCount = Number(item.counts?.responses || 0)
|
||||
const actionError = actionErrors[item.id]
|
||||
const needsWorkItemUpload = actionError?.includes('作品尚未上传完整')
|
||||
|
||||
return (
|
||||
<tr key={item.id} className="text-sm text-[#1a1c1c] hover:bg-[#fcfcfc]">
|
||||
@ -2492,7 +2526,7 @@ function CreatedList({
|
||||
aria-label={`发布调研:${item.title}`}
|
||||
className="min-h-9 rounded border border-[#b8d0ff] bg-[#edf4ff] px-3 text-[13px] font-bold text-[#0050cb] hover:bg-[#dae8ff] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[#0050cb] focus-visible:ring-offset-2 disabled:opacity-50"
|
||||
>
|
||||
发布
|
||||
{busyId === item.id ? '发布中...' : '发布'}
|
||||
</button>
|
||||
)}
|
||||
{item.status !== 'closed' && item.status !== 'cancelled' && (
|
||||
@ -2503,7 +2537,7 @@ function CreatedList({
|
||||
aria-label={`关闭调研:${item.title}`}
|
||||
className="min-h-9 rounded border border-[#c2c6d8] bg-white px-3 text-[13px] font-bold text-[#1a1c1c] hover:border-[#0050cb] hover:text-[#0050cb] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[#0050cb] focus-visible:ring-offset-2 disabled:opacity-50"
|
||||
>
|
||||
关闭
|
||||
{busyId === item.id ? '处理中...' : '关闭'}
|
||||
</button>
|
||||
)}
|
||||
{item.canCancel && (
|
||||
@ -2527,6 +2561,22 @@ function CreatedList({
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
{actionError && (
|
||||
<div
|
||||
role="alert"
|
||||
className="mt-2 rounded border border-[#f0b8b8] bg-[#fff5f5] px-3 py-2 text-left text-[12px] font-medium leading-5 text-[#b42318]"
|
||||
>
|
||||
{actionError}
|
||||
{needsWorkItemUpload && (
|
||||
<Link
|
||||
href={`/performance-survey/survey/${item.id}`}
|
||||
className="ml-2 inline-flex min-h-8 items-center rounded border border-[#f0b8b8] bg-white px-2.5 font-bold text-[#b42318] underline-offset-2 hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[#b42318] focus-visible:ring-offset-2"
|
||||
>
|
||||
继续上传作品
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user