128 lines
4.9 KiB
TypeScript
128 lines
4.9 KiB
TypeScript
'use client'
|
||
|
||
import { useEffect, useRef } from 'react'
|
||
import { createPortal } from 'react-dom'
|
||
|
||
export function PerformanceSurveyExportDialog({
|
||
open,
|
||
positionName,
|
||
monthLabel,
|
||
employeeSummary,
|
||
assessmentCount,
|
||
busy,
|
||
error,
|
||
onClose,
|
||
onExport,
|
||
}: {
|
||
open: boolean
|
||
positionName: string
|
||
monthLabel: string
|
||
employeeSummary: string
|
||
assessmentCount: number
|
||
busy: boolean
|
||
error: string | null
|
||
onClose: () => void
|
||
onExport: () => void
|
||
}) {
|
||
const exportButtonRef = useRef<HTMLButtonElement>(null)
|
||
|
||
useEffect(() => {
|
||
if (!open) return
|
||
const previousActiveElement = document.activeElement as HTMLElement | null
|
||
window.requestAnimationFrame(() => exportButtonRef.current?.focus())
|
||
const onKeyDown = (event: KeyboardEvent) => {
|
||
if (event.key === 'Escape' && !busy) onClose()
|
||
}
|
||
document.addEventListener('keydown', onKeyDown)
|
||
return () => {
|
||
document.removeEventListener('keydown', onKeyDown)
|
||
previousActiveElement?.focus()
|
||
}
|
||
}, [busy, onClose, open])
|
||
|
||
if (!open || typeof document === 'undefined') return null
|
||
|
||
return createPortal(
|
||
<div
|
||
className="fixed inset-0 z-[100] flex items-end justify-center bg-black/45 p-0 sm:items-center sm:p-5"
|
||
onMouseDown={(event) => {
|
||
if (event.target === event.currentTarget && !busy) onClose()
|
||
}}
|
||
>
|
||
<section
|
||
role="dialog"
|
||
aria-modal="true"
|
||
aria-labelledby="performance-survey-export-title"
|
||
aria-describedby="performance-survey-export-description"
|
||
className="w-full border border-[#d0d5dd] bg-white shadow-[0_18px_45px_rgba(15,23,42,0.18)] sm:max-w-lg sm:rounded-xl"
|
||
>
|
||
<div className="flex items-start justify-between gap-4 border-b border-[#e2e2e2] px-5 py-4">
|
||
<div className="min-w-0">
|
||
<h2 id="performance-survey-export-title" className="text-[18px] font-bold text-[#1a1c1c]">
|
||
导出公共看板
|
||
</h2>
|
||
<p id="performance-survey-export-description" className="mt-1 text-[13px] leading-5 text-[#5f5e5e]">
|
||
生成 WPS/Excel 可直接打开的 .xlsx 表格。
|
||
</p>
|
||
</div>
|
||
<button
|
||
type="button"
|
||
aria-label="关闭导出窗口"
|
||
disabled={busy}
|
||
onClick={onClose}
|
||
className="flex h-10 w-10 shrink-0 items-center justify-center rounded border border-transparent text-[22px] leading-none text-[#5f5e5e] transition-colors hover:border-[#d0d5dd] hover:bg-[#f7f7f7] focus:outline-none focus:ring-2 focus:ring-[#0050cb] disabled:opacity-40"
|
||
>
|
||
×
|
||
</button>
|
||
</div>
|
||
|
||
<dl className="grid grid-cols-2 border-b border-[#e2e2e2] bg-[#f8f9fb] sm:grid-cols-4">
|
||
{[
|
||
['岗位', positionName],
|
||
['月份', monthLabel],
|
||
['人员范围', employeeSummary],
|
||
['考核项', `${assessmentCount} 项`],
|
||
].map(([label, value]) => (
|
||
<div key={label} className="min-w-0 border-b border-[#e2e2e2] px-4 py-3 last:border-b-0 sm:border-b-0 sm:border-r sm:last:border-r-0">
|
||
<dt className="text-[11px] font-semibold text-[#727687]">{label}</dt>
|
||
<dd className="mt-1 truncate text-[13px] font-bold text-[#1a1c1c]" title={value}>{value}</dd>
|
||
</div>
|
||
))}
|
||
</dl>
|
||
|
||
<div className="px-5 py-4">
|
||
<p className="text-[13px] font-medium leading-6 text-[#475467]">
|
||
表格包含岗位、考核月份、姓名、工号、组织、职级及全部考核项得分。支持保存位置选择的浏览器会先打开本地路径窗口;其他浏览器将保存到默认下载目录。
|
||
</p>
|
||
{error && (
|
||
<div role="alert" className="mt-3 rounded border border-[#ffdad6] bg-[#fff6f4] px-3 py-2 text-[12px] font-semibold leading-5 text-[#93000a]">
|
||
{error}
|
||
</div>
|
||
)}
|
||
</div>
|
||
|
||
<div className="flex flex-col-reverse gap-2 border-t border-[#e2e2e2] px-5 py-4 sm:flex-row sm:justify-end">
|
||
<button
|
||
type="button"
|
||
disabled={busy}
|
||
onClick={onClose}
|
||
className="min-h-11 rounded border border-[#c9c9c9] bg-white px-4 text-[13px] font-bold text-[#1a1c1c] transition-colors hover:border-[#1a1c1c] hover:bg-[#f7f7f7] focus:outline-none focus:ring-2 focus:ring-[#0050cb] disabled:opacity-50"
|
||
>
|
||
取消
|
||
</button>
|
||
<button
|
||
ref={exportButtonRef}
|
||
type="button"
|
||
disabled={busy}
|
||
onClick={onExport}
|
||
className="min-h-11 rounded bg-[#0050cb] px-5 text-[13px] font-bold text-white transition-colors hover:bg-[#003f9f] focus:outline-none focus:ring-2 focus:ring-[#0050cb] focus:ring-offset-2 disabled:cursor-wait disabled:opacity-60"
|
||
>
|
||
{busy ? '正在生成表格…' : '选择保存位置并导出'}
|
||
</button>
|
||
</div>
|
||
</section>
|
||
</div>,
|
||
document.body,
|
||
)
|
||
}
|