新增檔案: - js/ui.js - UI 操作、模組切換、預覽更新、表單資料收集 - js/main.js - 主程式初始化、事件監聽器設置、快捷鍵 更新檔案: - index.html - 引用 ES6 模組 (type="module") 功能: ✅ 模組切換功能 ✅ 標籤頁切換 ✅ 表單欄位監聽 ✅ JSON 預覽更新 ✅ 快捷鍵支援 (Ctrl+S, Ctrl+N) ✅ 用戶信息載入 ✅ 登出功能 注意: - 大部分 JavaScript 代碼仍在 HTML 中(約 2400 行) - 已建立核心模組架構,便於後續逐步遷移 - 使用 ES6 Modules,需要通過 HTTP Server 運行 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
472 lines
15 KiB
HTML
472 lines
15 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-TW">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>那都AI寫的,不要問我 - 登入</title>
|
||
<style>
|
||
* {
|
||
margin: 0;
|
||
padding: 0;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
body {
|
||
font-family: 'Microsoft JhengHei', 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||
min-height: 100vh;
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
padding: 20px;
|
||
}
|
||
|
||
.login-container {
|
||
background: white;
|
||
border-radius: 20px;
|
||
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
|
||
overflow: hidden;
|
||
max-width: 450px;
|
||
width: 100%;
|
||
animation: slideIn 0.5s ease-out;
|
||
}
|
||
|
||
@keyframes slideIn {
|
||
from {
|
||
opacity: 0;
|
||
transform: translateY(-30px);
|
||
}
|
||
to {
|
||
opacity: 1;
|
||
transform: translateY(0);
|
||
}
|
||
}
|
||
|
||
.login-header {
|
||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||
padding: 40px 30px;
|
||
text-align: center;
|
||
color: white;
|
||
}
|
||
|
||
.logo-container {
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.logo-container img {
|
||
width: 150px;
|
||
height: 150px;
|
||
animation: float 3s ease-in-out infinite;
|
||
}
|
||
|
||
@keyframes float {
|
||
0%, 100% {
|
||
transform: translateY(0px);
|
||
}
|
||
50% {
|
||
transform: translateY(-10px);
|
||
}
|
||
}
|
||
|
||
.system-title {
|
||
font-size: 28px;
|
||
font-weight: bold;
|
||
margin-bottom: 10px;
|
||
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.2);
|
||
}
|
||
|
||
.system-subtitle {
|
||
font-size: 14px;
|
||
opacity: 0.9;
|
||
font-style: italic;
|
||
}
|
||
|
||
.login-body {
|
||
padding: 40px 30px;
|
||
}
|
||
|
||
.form-group {
|
||
margin-bottom: 25px;
|
||
}
|
||
|
||
.form-group label {
|
||
display: block;
|
||
margin-bottom: 8px;
|
||
color: #333;
|
||
font-weight: 500;
|
||
font-size: 14px;
|
||
}
|
||
|
||
.form-group input {
|
||
width: 100%;
|
||
padding: 12px 15px;
|
||
border: 2px solid #e0e0e0;
|
||
border-radius: 8px;
|
||
font-size: 15px;
|
||
transition: all 0.3s;
|
||
}
|
||
|
||
.form-group input:focus {
|
||
outline: none;
|
||
border-color: #667eea;
|
||
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
|
||
}
|
||
|
||
.btn {
|
||
width: 100%;
|
||
padding: 14px;
|
||
border: none;
|
||
border-radius: 8px;
|
||
font-size: 16px;
|
||
font-weight: 600;
|
||
cursor: pointer;
|
||
transition: all 0.3s;
|
||
margin-bottom: 10px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 10px;
|
||
}
|
||
|
||
.btn-primary {
|
||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||
color: white;
|
||
}
|
||
|
||
.btn-primary:hover {
|
||
transform: translateY(-2px);
|
||
box-shadow: 0 10px 20px rgba(102, 126, 234, 0.3);
|
||
}
|
||
|
||
.btn-primary:active {
|
||
transform: translateY(0);
|
||
}
|
||
|
||
.divider {
|
||
text-align: center;
|
||
margin: 30px 0 20px 0;
|
||
position: relative;
|
||
}
|
||
|
||
.divider::before {
|
||
content: '';
|
||
position: absolute;
|
||
top: 50%;
|
||
left: 0;
|
||
right: 0;
|
||
height: 1px;
|
||
background: #e0e0e0;
|
||
}
|
||
|
||
.divider span {
|
||
background: white;
|
||
padding: 0 15px;
|
||
color: #999;
|
||
font-size: 14px;
|
||
position: relative;
|
||
z-index: 1;
|
||
}
|
||
|
||
.quick-login {
|
||
margin-top: 20px;
|
||
}
|
||
|
||
.quick-login-title {
|
||
font-size: 14px;
|
||
color: #666;
|
||
margin-bottom: 15px;
|
||
text-align: center;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.btn-test {
|
||
background: white;
|
||
color: #333;
|
||
border: 2px solid #e0e0e0;
|
||
font-size: 14px;
|
||
padding: 12px;
|
||
}
|
||
|
||
.btn-test:hover {
|
||
background: #f8f9fa;
|
||
border-color: #667eea;
|
||
color: #667eea;
|
||
}
|
||
|
||
.btn-test .role-badge {
|
||
display: inline-block;
|
||
padding: 3px 10px;
|
||
border-radius: 12px;
|
||
font-size: 12px;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.btn-test.user .role-badge {
|
||
background: #e3f2fd;
|
||
color: #1976d2;
|
||
}
|
||
|
||
.btn-test.admin .role-badge {
|
||
background: #fff3e0;
|
||
color: #f57c00;
|
||
}
|
||
|
||
.btn-test.superadmin .role-badge {
|
||
background: #fce4ec;
|
||
color: #c2185b;
|
||
}
|
||
|
||
.footer-note {
|
||
margin-top: 25px;
|
||
text-align: center;
|
||
font-size: 12px;
|
||
color: #999;
|
||
line-height: 1.6;
|
||
}
|
||
|
||
.icon {
|
||
width: 20px;
|
||
height: 20px;
|
||
fill: currentColor;
|
||
}
|
||
|
||
@media (max-width: 480px) {
|
||
.login-container {
|
||
margin: 10px;
|
||
}
|
||
|
||
.system-title {
|
||
font-size: 24px;
|
||
}
|
||
|
||
.login-body {
|
||
padding: 30px 20px;
|
||
}
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="login-container">
|
||
<div class="login-header">
|
||
<div class="logo-container">
|
||
<img src="logo.svg" alt="System Logo">
|
||
</div>
|
||
<h1 class="system-title">那都AI寫的,不要問我</h1>
|
||
<p class="system-subtitle">HR Position Management System</p>
|
||
</div>
|
||
|
||
<div class="login-body">
|
||
<form id="loginForm" onsubmit="handleLogin(event)">
|
||
<div class="form-group">
|
||
<label for="username">
|
||
<svg class="icon" viewBox="0 0 24 24" style="width: 16px; height: 16px; vertical-align: middle; margin-right: 5px;">
|
||
<path d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"/>
|
||
</svg>
|
||
工號 / 帳號
|
||
</label>
|
||
<input type="text" id="username" name="username" placeholder="請輸入工號" required>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label for="password">
|
||
<svg class="icon" viewBox="0 0 24 24" style="width: 16px; height: 16px; vertical-align: middle; margin-right: 5px;">
|
||
<path d="M18 8h-1V6c0-2.76-2.24-5-5-5S7 3.24 7 6v2H6c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V10c0-1.1-.9-2-2-2zm-6 9c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2zm3.1-9H8.9V6c0-1.71 1.39-3.1 3.1-3.1 1.71 0 3.1 1.39 3.1 3.1v2z"/>
|
||
</svg>
|
||
密碼
|
||
</label>
|
||
<input type="password" id="password" name="password" placeholder="請輸入密碼" required>
|
||
</div>
|
||
|
||
<button type="submit" class="btn btn-primary">
|
||
<svg class="icon" viewBox="0 0 24 24">
|
||
<path d="M10 17l5-5-5-5v10z" fill="currentColor"/>
|
||
</svg>
|
||
登入系統
|
||
</button>
|
||
</form>
|
||
|
||
<div class="divider">
|
||
<span>或使用測試帳號快速登入</span>
|
||
</div>
|
||
|
||
<div class="quick-login">
|
||
<p class="quick-login-title">選擇測試角色</p>
|
||
|
||
<button type="button" class="btn btn-test user" onclick="quickLogin('user')">
|
||
<svg class="icon" viewBox="0 0 24 24">
|
||
<path d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"/>
|
||
</svg>
|
||
<span class="role-badge">一般使用者</span>
|
||
<span style="flex: 1; text-align: left; margin-left: 10px; font-size: 13px; color: #666;">A003 / employee</span>
|
||
</button>
|
||
|
||
<button type="button" class="btn btn-test admin" onclick="quickLogin('admin')">
|
||
<svg class="icon" viewBox="0 0 24 24">
|
||
<path d="M12 1L3 5v6c0 5.55 3.84 10.74 9 12 5.16-1.26 9-6.45 9-12V5l-9-4zm0 10.99h7c-.53 4.12-3.28 7.79-7 8.94V12H5V6.3l7-3.11v8.8z"/>
|
||
</svg>
|
||
<span class="role-badge">管理者</span>
|
||
<span style="flex: 1; text-align: left; margin-left: 10px; font-size: 13px; color: #666;">A002 / hr_manager</span>
|
||
</button>
|
||
|
||
<button type="button" class="btn btn-test superadmin" onclick="quickLogin('superadmin')">
|
||
<svg class="icon" viewBox="0 0 24 24">
|
||
<path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/>
|
||
</svg>
|
||
<span class="role-badge">最高管理者</span>
|
||
<span style="flex: 1; text-align: left; margin-left: 10px; font-size: 13px; color: #666;">A001 / admin</span>
|
||
</button>
|
||
</div>
|
||
|
||
<div class="footer-note">
|
||
這個系統真的都是 AI 寫的<br>
|
||
如果有問題... 那就是 AI 的問題 ¯\_(ツ)_/¯
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
// 測試帳號資料
|
||
const testAccounts = {
|
||
'user': {
|
||
username: 'A003',
|
||
password: 'employee',
|
||
role: 'user',
|
||
name: '一般員工'
|
||
},
|
||
'admin': {
|
||
username: 'A002',
|
||
password: 'hr_manager',
|
||
role: 'admin',
|
||
name: '人資主管'
|
||
},
|
||
'superadmin': {
|
||
username: 'A001',
|
||
password: 'admin',
|
||
role: 'superadmin',
|
||
name: '系統管理員'
|
||
}
|
||
};
|
||
|
||
// 處理一般登入
|
||
function handleLogin(event) {
|
||
event.preventDefault();
|
||
|
||
const username = document.getElementById('username').value;
|
||
const password = document.getElementById('password').value;
|
||
|
||
// 簡單的驗證邏輯(實際應該調用後端 API)
|
||
let validUser = null;
|
||
for (const [key, account] of Object.entries(testAccounts)) {
|
||
if (account.username === username && account.password === password) {
|
||
validUser = account;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (validUser) {
|
||
// 儲存登入資訊
|
||
localStorage.setItem('currentUser', JSON.stringify(validUser));
|
||
|
||
// 顯示登入成功訊息
|
||
showLoginSuccess(validUser.name, validUser.role);
|
||
|
||
// 延遲跳轉到主頁面
|
||
setTimeout(() => {
|
||
window.location.href = 'index.html';
|
||
}, 1500);
|
||
} else {
|
||
alert('帳號或密碼錯誤!\n\n提示:您可以使用下方的測試帳號快速登入');
|
||
}
|
||
}
|
||
|
||
// 快速登入
|
||
function quickLogin(role) {
|
||
const account = testAccounts[role];
|
||
|
||
// 填入表單
|
||
document.getElementById('username').value = account.username;
|
||
document.getElementById('password').value = account.password;
|
||
|
||
// 儲存登入資訊
|
||
localStorage.setItem('currentUser', JSON.stringify(account));
|
||
|
||
// 顯示登入成功訊息
|
||
showLoginSuccess(account.name, account.role);
|
||
|
||
// 延遲跳轉到主頁面
|
||
setTimeout(() => {
|
||
window.location.href = 'index.html';
|
||
}, 1500);
|
||
}
|
||
|
||
// 顯示登入成功訊息
|
||
function showLoginSuccess(name, role) {
|
||
const roleNames = {
|
||
'user': '一般使用者',
|
||
'admin': '管理者',
|
||
'superadmin': '最高管理者'
|
||
};
|
||
|
||
// 創建成功訊息元素
|
||
const successDiv = document.createElement('div');
|
||
successDiv.style.cssText = `
|
||
position: fixed;
|
||
top: 20px;
|
||
right: 20px;
|
||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||
color: white;
|
||
padding: 20px 30px;
|
||
border-radius: 10px;
|
||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
|
||
z-index: 9999;
|
||
animation: slideInRight 0.5s ease-out;
|
||
font-size: 16px;
|
||
`;
|
||
successDiv.innerHTML = `
|
||
<div style="display: flex; align-items: center; gap: 15px;">
|
||
<svg viewBox="0 0 24 24" style="width: 32px; height: 32px; fill: white;">
|
||
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/>
|
||
</svg>
|
||
<div>
|
||
<div style="font-weight: 600; margin-bottom: 5px;">登入成功!</div>
|
||
<div style="font-size: 14px; opacity: 0.9;">${name} (${roleNames[role]})</div>
|
||
</div>
|
||
</div>
|
||
`;
|
||
|
||
// 添加動畫樣式
|
||
const style = document.createElement('style');
|
||
style.textContent = `
|
||
@keyframes slideInRight {
|
||
from {
|
||
transform: translateX(400px);
|
||
opacity: 0;
|
||
}
|
||
to {
|
||
transform: translateX(0);
|
||
opacity: 1;
|
||
}
|
||
}
|
||
`;
|
||
document.head.appendChild(style);
|
||
document.body.appendChild(successDiv);
|
||
}
|
||
|
||
// 頁面載入時檢查是否已登入
|
||
window.addEventListener('DOMContentLoaded', function() {
|
||
const currentUser = localStorage.getItem('currentUser');
|
||
if (currentUser) {
|
||
const user = JSON.parse(currentUser);
|
||
// 如果已經登入,詢問是否要繼續使用或重新登入
|
||
const confirm = window.confirm(`偵測到您已經以「${user.name}」身份登入。\n\n是否要繼續使用此帳號?\n(取消將重新登入)`);
|
||
if (confirm) {
|
||
window.location.href = 'index.html';
|
||
} else {
|
||
localStorage.removeItem('currentUser');
|
||
}
|
||
}
|
||
});
|
||
</script>
|
||
</body>
|
||
</html>
|