Files
hr-position-system/generate_review.py
DonaldFang 方士碩 12ceccc3d3 refactor: 新增 ui.js 和 main.js 模組,啟用 ES6 Modules
新增檔案:
- 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>
2025-12-05 17:18:28 +08:00

383 lines
11 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import json
# 讀取表格數據
with open('excel_table.md', 'r', encoding='utf-8') as f:
lines = f.readlines()
# 解析數據(跳過表頭和分隔線)
data = []
for line in lines[2:]: # 跳過表頭和分隔線
line = line.strip()
if not line or not line.startswith('|'):
continue
# 移除首尾的管道符號並分割
parts = [p.strip() for p in line[1:-1].split('|')]
if len(parts) >= 4:
data.append({
'事業體': parts[0],
'處級單位': parts[1],
'部級單位': parts[2],
'崗位名稱': parts[3]
})
# 生成 HTML
html_content = '''<!DOCTYPE html>
<html lang="zh-TW">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>組織架構預覽</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: "Microsoft JhengHei", "微軟正黑體", Arial, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
padding: 20px;
min-height: 100vh;
}
/* Header using Float */
.header {
background: rgba(255, 255, 255, 0.95);
padding: 20px;
border-radius: 10px;
margin-bottom: 20px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
overflow: hidden; /* Clear float */
}
.header h1 {
float: left;
color: #333;
font-size: 28px;
}
.header .stats {
float: right;
color: #666;
font-size: 14px;
padding-top: 8px;
}
.header::after {
content: "";
display: table;
clear: both;
}
/* Filter Section using Float */
.filters {
background: rgba(255, 255, 255, 0.95);
padding: 20px;
border-radius: 10px;
margin-bottom: 20px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
overflow: hidden;
}
.filter-group {
float: left;
margin-right: 20px;
margin-bottom: 10px;
}
.filter-group label {
display: block;
margin-bottom: 5px;
color: #555;
font-weight: bold;
font-size: 14px;
}
.filter-group select,
.filter-group input {
padding: 8px 12px;
border: 2px solid #ddd;
border-radius: 5px;
font-size: 14px;
width: 200px;
}
.filter-group select:focus,
.filter-group input:focus {
outline: none;
border-color: #667eea;
}
.filters::after {
content: "";
display: table;
clear: both;
}
/* Table Container using Float */
.table-container {
background: rgba(255, 255, 255, 0.95);
border-radius: 10px;
padding: 20px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
overflow-x: auto;
}
table {
width: 100%;
border-collapse: collapse;
font-size: 14px;
}
thead {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
}
th {
padding: 15px;
text-align: left;
font-weight: bold;
position: sticky;
top: 0;
z-index: 10;
}
tbody tr {
border-bottom: 1px solid #eee;
transition: background-color 0.2s;
}
tbody tr:hover {
background-color: #f5f5f5;
}
tbody tr:nth-child(even) {
background-color: #fafafa;
}
tbody tr:nth-child(even):hover {
background-color: #f0f0f0;
}
td {
padding: 12px 15px;
color: #333;
}
td:first-child {
font-weight: 600;
color: #667eea;
}
td:last-child {
color: #764ba2;
font-weight: 500;
}
/* Empty cells styling */
td:empty::before {
content: "";
color: #ccc;
}
/* Footer using Float */
.footer {
margin-top: 20px;
background: rgba(255, 255, 255, 0.95);
padding: 15px;
border-radius: 10px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
text-align: center;
color: #666;
font-size: 14px;
}
/* Responsive Design */
@media (max-width: 768px) {
.filter-group {
float: none;
width: 100%;
margin-right: 0;
}
.filter-group select,
.filter-group input {
width: 100%;
}
.header h1 {
float: none;
margin-bottom: 10px;
}
.header .stats {
float: none;
}
}
</style>
</head>
<body>
<div class="header">
<h1>📊 公司組織架構預覽</h1>
<div class="stats">總計: <span id="totalCount">''' + str(len(data)) + '''</span> 筆資料</div>
</div>
<div class="filters">
<div class="filter-group">
<label for="filterBusiness">事業體篩選</label>
<select id="filterBusiness">
<option value="">全部</option>
</select>
</div>
<div class="filter-group">
<label for="filterDepartment">處級單位篩選</label>
<select id="filterDepartment">
<option value="">全部</option>
</select>
</div>
<div class="filter-group">
<label for="filterDivision">部級單位篩選</label>
<select id="filterDivision">
<option value="">全部</option>
</select>
</div>
<div class="filter-group">
<label for="searchPosition">崗位搜尋</label>
<input type="text" id="searchPosition" placeholder="輸入崗位名稱...">
</div>
</div>
<div class="table-container">
<table>
<thead>
<tr>
<th>事業體</th>
<th>處級單位</th>
<th>部級單位</th>
<th>崗位名稱</th>
</tr>
</thead>
<tbody id="tableBody">
'''
# 添加表格行
for row in data:
html_content += f''' <tr>
<td>{row['事業體']}</td>
<td>{row['處級單位'] if row['處級單位'] else ''}</td>
<td>{row['部級單位'] if row['部級單位'] else ''}</td>
<td>{row['崗位名稱']}</td>
</tr>
'''
html_content += ''' </tbody>
</table>
</div>
<div class="footer">
<p>組織架構資料預覽系統 | 使用 CSS Float Layout 設計</p>
</div>
<script>
// 獲取所有數據
const allData = ''' + json.dumps(data, ensure_ascii=False) + ''';
// 獲取唯一的選項值
function getUniqueValues(key) {
const values = new Set();
allData.forEach(row => {
if (row[key]) {
values.add(row[key]);
}
});
return Array.from(values).sort();
}
// 填充下拉選單
function populateSelects() {
const businessSelect = document.getElementById('filterBusiness');
const deptSelect = document.getElementById('filterDepartment');
const divSelect = document.getElementById('filterDivision');
getUniqueValues('事業體').forEach(value => {
const option = document.createElement('option');
option.value = value;
option.textContent = value;
businessSelect.appendChild(option);
});
getUniqueValues('處級單位').forEach(value => {
const option = document.createElement('option');
option.value = value;
option.textContent = value;
deptSelect.appendChild(option);
});
getUniqueValues('部級單位').forEach(value => {
const option = document.createElement('option');
option.value = value;
option.textContent = value;
divSelect.appendChild(option);
});
}
// 過濾數據
function filterData() {
const businessFilter = document.getElementById('filterBusiness').value;
const deptFilter = document.getElementById('filterDepartment').value;
const divFilter = document.getElementById('filterDivision').value;
const positionSearch = document.getElementById('searchPosition').value.toLowerCase();
const filtered = allData.filter(row => {
const matchBusiness = !businessFilter || row['事業體'] === businessFilter;
const matchDept = !deptFilter || row['處級單位'] === deptFilter;
const matchDiv = !divFilter || row['部級單位'] === divFilter;
const matchPosition = !positionSearch || row['崗位名稱'].toLowerCase().includes(positionSearch);
return matchBusiness && matchDept && matchDiv && matchPosition;
});
renderTable(filtered);
document.getElementById('totalCount').textContent = filtered.length;
}
// 渲染表格
function renderTable(data) {
const tbody = document.getElementById('tableBody');
tbody.innerHTML = '';
data.forEach(row => {
const tr = document.createElement('tr');
tr.innerHTML = `
<td>${row['事業體'] || ''}</td>
<td>${row['處級單位'] || ''}</td>
<td>${row['部級單位'] || ''}</td>
<td>${row['崗位名稱'] || ''}</td>
`;
tbody.appendChild(tr);
});
}
// 事件監聽
document.getElementById('filterBusiness').addEventListener('change', filterData);
document.getElementById('filterDepartment').addEventListener('change', filterData);
document.getElementById('filterDivision').addEventListener('change', filterData);
document.getElementById('searchPosition').addEventListener('input', filterData);
// 初始化
populateSelects();
</script>
</body>
</html>'''
# 寫入文件
with open('review.html', 'w', encoding='utf-8') as f:
f.write(html_content)
print(f"預覽頁面已生成review.html")
print(f"共包含 {len(data)} 筆組織架構資料")