修改列印呈現方式

This commit is contained in:
2025-10-12 03:35:50 +08:00
parent fbb423243a
commit ef640e9bdb
2 changed files with 707 additions and 7 deletions

View File

@@ -82,6 +82,7 @@ function AdminResultDetailContent() {
const [detailData, setDetailData] = useState<DetailData | null>(null)
const [isLoading, setIsLoading] = useState(true)
const [error, setError] = useState<string | null>(null)
const [showDetailedResults, setShowDetailedResults] = useState(true)
useEffect(() => {
const loadDetailData = async () => {
@@ -341,7 +342,7 @@ function AdminResultDetailContent() {
const displayScorePercentage = creativeScorePercentage > 0 ? creativeScorePercentage : result.score
return (
<div className="min-h-screen bg-background">
<div className="min-h-screen bg-background admin-result-print">
{/* Header */}
<header className="border-b bg-card/50 backdrop-blur-sm">
<div className="container mx-auto px-3 sm:px-4 py-3 sm:py-4">
@@ -390,7 +391,532 @@ function AdminResultDetailContent() {
<span className="hidden sm:inline"></span>
</Button>
<Button
onClick={() => window.print()}
onClick={() => {
// 創建一個新的列印窗口
const printWindow = window.open('', '_blank', 'width=800,height=600')
if (printWindow) {
// 獲取當前頁面的數據
const userInfo = `
<div class="card">
<h3>
<svg class="icon icon-user" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"></path>
</svg>
用戶資訊
</h3>
<div class="info-grid">
<div class="info-label">姓名:</div>
<div class="info-value">${user.name}</div>
<div class="info-label">電子郵件:</div>
<div class="info-value">${user.email}</div>
<div class="info-label">部門:</div>
<div class="info-value">${user.department}</div>
<div class="info-label">角色:</div>
<div class="info-value">${user.role}</div>
</div>
</div>
`
const testResult = `
<div class="card" style="text-align: center;">
<div class="score-circle">
<span>${result.score}</span>
</div>
<h3>綜合測試${result.isTimeout ? '(時間到)' : ''}完成!</h3>
<div class="level-badge">${getScoreLevel(result.score, result.type).level}</div>
${result.isTimeout ? '<div class="timeout-badge">時間到強制提交</div>' : ''}
<div class="description">${getScoreLevel(result.score, result.type).description}</div>
</div>
`
let logicTest = ''
let creativeTest = ''
if (result.type === 'combined' && result.details) {
logicTest = `
<div class="card">
<h4>
<svg class="icon icon-brain" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9.663 17h4.673M12 3v1m6.364 1.636l-.707.707M21 12h-1M4 12H3m3.343-5.657l-.707-.707m2.828 9.9a5 5 0 117.072 0l-.548.547A3.374 3.374 0 0014 18.469V19a2 2 0 11-4 0v-.531c0-.895-.356-1.754-.988-2.386l-.548-.547z"></path>
</svg>
邏輯思維測試
</h4>
<div class="stats">
<div class="stat-item">
<div class="stat-number">${result.details.logicScore || 0}</div>
<div class="stat-label">得分</div>
</div>
<div class="stat-item">
<div class="stat-number">${result.details.logicCorrectAnswers || 0}</div>
<div class="stat-label">答對題數</div>
</div>
<div class="stat-item">
<div class="stat-number">${result.details.logicTotalQuestions || 0}</div>
<div class="stat-label">總題數</div>
</div>
</div>
<div class="level-badge">${getScoreLevel(result.details.logicScore || 0, 'logic').level}</div>
<div class="description">${getScoreLevel(result.details.logicScore || 0, 'logic').description}</div>
</div>
`
creativeTest = `
<div class="card">
<h4>
<svg class="icon icon-lightbulb" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9.663 17h4.673M12 3v1m6.364 1.636l-.707.707M21 12h-1M4 12H3m3.343-5.657l-.707-.707m2.828 9.9a5 5 0 117.072 0l-.548.547A3.374 3.374 0 0014 18.469V19a2 2 0 11-4 0v-.531c0-.895-.356-1.754-.988-2.386l-.548-.547z"></path>
</svg>
創意能力測試
</h4>
<div class="stats">
<div class="stat-item">
<div class="stat-number">${result.details.creativeScore || 0}</div>
<div class="stat-label">得分</div>
</div>
<div class="stat-item">
<div class="stat-number">${result.details.creativeScore || 0}</div>
<div class="stat-label">原始得分</div>
</div>
<div class="stat-item">
<div class="stat-number">${result.details.creativeMaxScore || 0}</div>
<div class="stat-label">滿分</div>
</div>
</div>
<div class="level-badge">${getScoreLevel(result.details.creativeScore || 0, 'creative').level}</div>
<div class="description">${getScoreLevel(result.details.creativeScore || 0, 'creative').description}</div>
</div>
`
}
// 創建一頁 A4 的緊湊列印內容
const printContent = `
<!DOCTYPE html>
<html>
<head>
<title>測試結果列印</title>
<style>
* { box-sizing: border-box; }
body {
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif;
margin: 0;
padding: 0.5rem;
background: #f8fafc;
color: #0f172a;
line-height: 1.4;
font-size: 0.75rem;
}
.header {
text-align: center;
margin-bottom: 0.75rem;
padding-bottom: 0.5rem;
border-bottom: 1px solid #e2e8f0;
}
.header h1 {
margin: 0 0 0.25rem 0;
font-size: 1.125rem;
font-weight: 700;
color: #0f172a;
}
.header .subtitle {
color: #64748b;
font-size: 0.7rem;
}
.row {
display: flex;
gap: 0.5rem;
margin-bottom: 0.5rem;
align-items: flex-start;
}
.card {
flex: 1;
background: white;
border: 1px solid #e2e8f0;
border-radius: 0.375rem;
padding: 0.75rem;
box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
}
.card h3 {
margin: 0 0 0.5rem 0;
font-size: 0.875rem;
font-weight: 600;
color: #0f172a;
border-bottom: 1px solid #e2e8f0;
padding-bottom: 0.25rem;
display: flex;
align-items: center;
gap: 0.25rem;
}
.card h4 {
margin: 0 0 0.5rem 0;
font-size: 0.8rem;
font-weight: 600;
color: #374151;
display: flex;
align-items: center;
gap: 0.25rem;
}
.icon {
width: 0.875rem;
height: 0.875rem;
display: inline-block;
}
.icon-user { color: #3b82f6; }
.icon-brain { color: #3b82f6; }
.icon-lightbulb { color: #f59e0b; }
.icon-target { color: #10b981; }
.icon-trending { color: #8b5cf6; }
.icon-search { color: #ef4444; }
.info-grid {
display: grid;
grid-template-columns: auto 1fr;
gap: 0.25rem 0.5rem;
margin-bottom: 0.5rem;
}
.info-label {
font-weight: 500;
color: #6b7280;
font-size: 0.7rem;
}
.info-value {
color: #0f172a;
font-size: 0.7rem;
}
.score-circle {
width: 2.5rem;
height: 2.5rem;
border-radius: 50%;
background: linear-gradient(135deg, #3b82f6, #1d4ed8);
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto 0.5rem;
box-shadow: 0 2px 4px -1px rgb(0 0 0 / 0.1);
}
.score-circle span {
color: white;
font-size: 1rem;
font-weight: bold;
}
.level-badge {
display: inline-block;
background: #f1f5f9;
color: #475569;
padding: 0.125rem 0.375rem;
border-radius: 9999px;
font-size: 0.65rem;
font-weight: 500;
margin-bottom: 0.25rem;
}
.timeout-badge {
background: #fef2f2;
color: #dc2626;
border: 1px solid #fecaca;
padding: 0.25rem;
border-radius: 0.25rem;
font-size: 0.65rem;
font-weight: 500;
text-align: center;
margin-bottom: 0.5rem;
}
.description {
color: #4b5563;
font-size: 0.65rem;
margin-bottom: 0.5rem;
}
.stats {
display: flex;
gap: 0.5rem;
margin-bottom: 0.5rem;
}
.stat-item {
flex: 1;
text-align: center;
padding: 0.375rem;
background: #f8fafc;
border-radius: 0.25rem;
border: 1px solid #e2e8f0;
}
.stat-number {
font-size: 0.875rem;
font-weight: bold;
color: #0f172a;
}
.stat-label {
font-size: 0.6rem;
color: #64748b;
margin-top: 0.125rem;
}
.ability-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 0.5rem;
margin-bottom: 0.75rem;
}
.ability-card {
background: white;
border: 1px solid #e2e8f0;
border-radius: 0.375rem;
padding: 0.5rem;
text-align: center;
box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
}
.ability-icon {
width: 1.25rem;
height: 1.25rem;
margin: 0 auto 0.25rem;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
}
.ability-title {
font-size: 0.65rem;
font-weight: 600;
color: #374151;
margin-bottom: 0.25rem;
}
.ability-score {
font-size: 1rem;
font-weight: bold;
margin-bottom: 0.125rem;
}
.ability-score.logic { color: #dc2626; }
.ability-score.creative { color: #f59e0b; }
.ability-score.balance { color: #f59e0b; }
.ability-status {
font-size: 0.6rem;
color: #6b7280;
margin-bottom: 0.25rem;
}
.progress-bar {
width: 100%;
height: 0.25rem;
background: #e5e7eb;
border-radius: 9999px;
overflow: hidden;
}
.progress-fill {
height: 100%;
background: #3b82f6;
border-radius: 9999px;
}
.suggestions {
background: white;
border: 1px solid #e2e8f0;
border-radius: 0.375rem;
padding: 0.75rem;
box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
}
.suggestions h3 {
margin: 0 0 0.5rem 0;
font-size: 0.875rem;
font-weight: 600;
color: #0f172a;
border-bottom: 1px solid #e2e8f0;
padding-bottom: 0.25rem;
display: flex;
align-items: center;
gap: 0.25rem;
}
.suggestion-item {
display: flex;
align-items: flex-start;
gap: 0.375rem;
margin-bottom: 0.375rem;
padding: 0.375rem;
background: #f8fafc;
border-radius: 0.25rem;
border: 1px solid #e2e8f0;
}
.suggestion-number {
width: 1rem;
height: 1rem;
background: #3b82f6;
color: white;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 0.6rem;
font-weight: bold;
flex-shrink: 0;
}
.suggestion-text {
color: #374151;
font-size: 0.65rem;
line-height: 1.3;
}
.no-print {
position: fixed;
top: 0.5rem;
right: 0.5rem;
z-index: 1000;
}
.no-print button {
background: #3b82f6;
color: white;
border: none;
padding: 0.375rem 0.75rem;
border-radius: 0.25rem;
margin-left: 0.25rem;
cursor: pointer;
font-weight: 500;
font-size: 0.7rem;
}
.no-print button:hover {
background: #2563eb;
}
.no-print button.secondary {
background: #6b7280;
}
.no-print button.secondary:hover {
background: #4b5563;
}
@media print {
body {
background: white;
margin: 0.3cm;
padding: 0;
font-size: 0.7rem;
}
.no-print { display: none; }
.card, .ability-card, .suggestions {
box-shadow: none;
border: 1px solid #d1d5db;
}
@page {
size: A4;
margin: 0.3cm;
}
}
</style>
</head>
<body>
<div class="header">
<h1>${user.name} - 綜合能力測試結果</h1>
<div class="subtitle">完成時間: ${new Date(result.completedAt).toLocaleString("zh-TW")}</div>
</div>
<!-- 第一列:用戶資料 + 測驗結果 -->
<div class="row">
${userInfo}
${testResult}
</div>
<!-- 第二列:邏輯測驗 + 創意測驗 -->
${logicTest && creativeTest ? `
<div class="row">
${logicTest}
${creativeTest}
</div>
` : ''}
<!-- 第三列:能力分析 -->
${result.type === 'combined' && result.details ? `
<div class="card">
<h3>
<svg class="icon icon-trending" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 7h8m0 0v8m0-8l-8 8-4-4-6 6"></path>
</svg>
能力分析
</h3>
<div class="ability-grid">
<div class="ability-card">
<div class="ability-icon" style="background: #dbeafe;">
<svg class="icon icon-brain" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9.663 17h4.673M12 3v1m6.364 1.636l-.707.707M21 12h-1M4 12H3m3.343-5.657l-.707-.707m2.828 9.9a5 5 0 117.072 0l-.548.547A3.374 3.374 0 0014 18.469V19a2 2 0 11-4 0v-.531c0-.895-.356-1.754-.988-2.386l-.548-.547z"></path>
</svg>
</div>
<div class="ability-title">邏輯思維</div>
<div class="ability-score logic">${result.details.logicScore || 0}</div>
<div class="ability-status">需要加強</div>
<div class="progress-bar">
<div class="progress-fill" style="width: ${Math.min((result.details.logicScore || 0) / 100 * 100, 100)}%"></div>
</div>
</div>
<div class="ability-card">
<div class="ability-icon" style="background: #fef3c7;">
<svg class="icon icon-lightbulb" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9.663 17h4.673M12 3v1m6.364 1.636l-.707.707M21 12h-1M4 12H3m3.343-5.657l-.707-.707m2.828 9.9a5 5 0 117.072 0l-.548.547A3.374 3.374 0 0014 18.469V19a2 2 0 11-4 0v-.531c0-.895-.356-1.754-.988-2.386l-.548-.547z"></path>
</svg>
</div>
<div class="ability-title">創意能力</div>
<div class="ability-score creative">${result.details.creativeScore || 0}</div>
<div class="ability-status">需要提升</div>
<div class="progress-bar">
<div class="progress-fill" style="width: ${Math.min((result.details.creativeScore || 0) / 100 * 100, 100)}%"></div>
</div>
</div>
<div class="ability-card">
<div class="ability-icon" style="background: #d1fae5;">
<svg class="icon icon-target" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
</div>
<div class="ability-title">能力平衡</div>
<div class="ability-score balance">${Math.round(((result.details.logicScore || 0) + (result.details.creativeScore || 0)) / 2)}</div>
<div class="ability-status">相對均衡</div>
<div class="progress-bar">
<div class="progress-fill" style="width: ${Math.min(((result.details.logicScore || 0) + (result.details.creativeScore || 0)) / 2 / 100 * 100, 100)}%"></div>
</div>
</div>
</div>
</div>
<!-- 第四列:發展建議 -->
<div class="suggestions">
<h3>
<svg class="icon icon-search" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path>
</svg>
發展建議
</h3>
<div class="suggestion-item">
<div class="suggestion-number">1</div>
<div class="suggestion-text">建議加強邏輯思維訓練,多做推理題和數學題</div>
</div>
<div class="suggestion-item">
<div class="suggestion-number">2</div>
<div class="suggestion-text">學習系統性思維方法,如思維導圖、流程圖等</div>
</div>
<div class="suggestion-item">
<div class="suggestion-number">3</div>
<div class="suggestion-text">建議參與更多創意活動,如頭腦風暴、設計思維工作坊</div>
</div>
<div class="suggestion-item">
<div class="suggestion-number">4</div>
<div class="suggestion-text">培養好奇心,多接觸不同領域的知識和經驗</div>
</div>
<div class="suggestion-item">
<div class="suggestion-number">5</div>
<div class="suggestion-text">您的創意能力較強,建議平衡發展邏輯思維</div>
</div>
</div>
` : ''}
<div class="no-print">
<button onclick="window.print()">列印</button>
<button onclick="window.close()" class="secondary">關閉</button>
</div>
</body>
</html>
`
printWindow.document.write(printContent)
printWindow.document.close()
// 等待內容載入後自動列印
printWindow.onload = () => {
setTimeout(() => {
printWindow.print()
}, 500)
}
} else {
// 如果無法打開新窗口,使用原始方法
window.print()
}
}}
variant="outline"
size="sm"
className="print:hidden flex-shrink-0"
@@ -403,10 +929,11 @@ function AdminResultDetailContent() {
</div>
</header>
<div className="container mx-auto px-4 py-8">
<div className="max-w-4xl mx-auto space-y-8">
{/* User Info */}
<Card>
<Card className="print-user-info">
<CardHeader>
<CardTitle></CardTitle>
</CardHeader>
@@ -434,7 +961,7 @@ function AdminResultDetailContent() {
{/* Logic Test Overview */}
{result.type === 'logic' && (
<Card className="text-center mb-6">
<Card className="text-center mb-6 print-logic-test">
<CardHeader>
<div
className={`w-24 h-24 ${scoreLevel.color} rounded-full flex items-center justify-center mx-auto mb-4`}
@@ -486,7 +1013,7 @@ function AdminResultDetailContent() {
{/* Creative Test Overview */}
{result.type === 'creative' && (
<Card className="text-center mb-6">
<Card className="text-center mb-6 print-creative-test">
<CardHeader>
<div
className={`w-24 h-24 ${scoreLevel.color} rounded-full flex items-center justify-center mx-auto mb-4`}
@@ -574,8 +1101,8 @@ function AdminResultDetailContent() {
)}
{/* Detailed Results */}
{questions.length > 0 && (
<Card>
{questions.length > 0 && showDetailedResults && (
<Card id="detailed-results-card" className="hide-in-print">
<CardHeader>
<CardTitle></CardTitle>
</CardHeader>

View File

@@ -19,6 +19,179 @@
padding: 0 !important;
}
/* 管理員結果頁面專用樣式 - 並排佈局 */
.admin-result-print {
/* 整體佈局調整 */
.container {
max-width: none !important;
padding: 0 !important;
margin: 0 !important;
}
/* 主容器調整 */
.admin-result-print .max-w-4xl {
max-width: none !important;
margin: 0 !important;
padding: 0 !important;
}
/* 主頁面並排佈局(非列印時) */
.admin-result-print .print-user-info {
width: 48% !important;
display: inline-block !important;
vertical-align: top !important;
margin: 0 !important;
margin-right: 2% !important;
margin-bottom: 1rem !important;
}
.admin-result-print .print-logic-test {
width: 48% !important;
display: inline-block !important;
vertical-align: top !important;
margin: 0 !important;
margin-right: 2% !important;
margin-bottom: 1rem !important;
}
.admin-result-print .print-creative-test {
width: 48% !important;
display: inline-block !important;
vertical-align: top !important;
margin: 0 !important;
margin-left: 2% !important;
margin-bottom: 1rem !important;
}
/* 測驗結果卡片(綜合測試) */
.admin-result-print .text-center.mb-6:not(.print-logic-test):not(.print-creative-test) {
width: 48% !important;
display: inline-block !important;
vertical-align: top !important;
margin: 0 !important;
margin-left: 2% !important;
margin-bottom: 1rem !important;
}
/* 其他卡片保持全寬度 */
.admin-result-print .card:not(.print-user-info):not(.print-logic-test):not(.print-creative-test) {
width: 100% !important;
display: block !important;
margin: 1rem 0 !important;
}
/* 卡片樣式調整 */
.card {
margin: 0 !important;
padding: 0.3rem !important;
border: 1px solid #ccc !important;
page-break-inside: avoid !important;
}
.card-header,
.card-content {
padding: 0.2rem !important;
margin: 0 !important;
}
/* 分數圓圈縮小 */
.w-24.h-24,
.w-20.h-20,
.w-16.h-16 {
width: 2rem !important;
height: 2rem !important;
}
/* 文字大小調整 */
.text-3xl {
font-size: 1rem !important;
}
.text-2xl {
font-size: 0.875rem !important;
}
.text-xl {
font-size: 0.75rem !important;
}
.text-lg {
font-size: 0.7rem !important;
}
.text-base {
font-size: 0.65rem !important;
}
.text-sm {
font-size: 0.6rem !important;
}
.text-xs {
font-size: 0.55rem !important;
}
/* 網格佈局緊湊 */
.grid {
gap: 0.2rem !important;
}
.grid-cols-3 {
grid-template-columns: repeat(3, 1fr) !important;
}
.grid-cols-2 {
grid-template-columns: repeat(2, 1fr) !important;
}
/* 進度條縮小 */
.h-3,
.h-2 {
height: 0.3rem !important;
}
/* 徽章縮小 */
.badge {
font-size: 0.5rem !important;
padding: 0.1rem 0.2rem !important;
}
/* 按鈕隱藏 */
.button,
button {
display: none !important;
}
/* 間距調整 */
.space-y-8 > * + * {
margin-top: 0 !important;
}
.space-y-6 > * + * {
margin-top: 0.1rem !important;
}
.space-y-4 > * + * {
margin-top: 0.05rem !important;
}
/* 列印時隱藏不需要的元素 */
@media print {
header,
.print\\:hidden,
.hide-in-print,
#detailed-results-card {
display: none !important;
}
@page {
size: A4;
margin: 0.8cm;
}
}
}
.space-y-8 > * + * {
margin-top: 1.5rem !important;
}