Files
hr-position-system/add_csv_buttons.py
DonaldFang 方士碩 d17af39bf4 feat: 新增多項功能 v2.1
- 新增 CSV 匯入匯出功能(所有頁籤)
- 新增崗位清單頁籤(含欄位排序)
- 新增管理者頁面(使用者 CRUD)
- 新增事業體選項(SBU/MBU/HQBU/ITBU/HRBU/ACCBU)
- 新增組織單位欄位(處級/部級/課級)
- 崗位描述/備注改為條列式說明
- 新增 README.md 文件
- 新增開發指令記錄檔

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-04 10:06:50 +08:00

282 lines
13 KiB
Python
Raw Permalink 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.

"""
為每個模組加入 CSV 匯入匯出按鈕
"""
import sys
import codecs
# 設置 UTF-8 編碼Windows 編碼修正)
if sys.platform == 'win32':
sys.stdout = codecs.getwriter('utf-8')(sys.stdout.buffer, 'strict')
sys.stderr = codecs.getwriter('utf-8')(sys.stderr.buffer, 'strict')
with open('index.html', 'r', encoding='utf-8') as f:
content = f.read()
# 備份
with open('index.html.backup_csv', 'w', encoding='utf-8') as f:
f.write(content)
# 1. 在 <head> 中加入 csv_utils.js
if '<script src="csv_utils.js"></script>' not in content:
head_insertion = ' <script src="csv_utils.js"></script>\n</head>'
content = content.replace('</head>', head_insertion)
print("[OK] Added csv_utils.js reference")
# 2. 為崗位資料模組加入 CSV 按鈕
# 在 action-buttons 區域前加入 CSV 按鈕
position_csv_buttons = ''' <!-- CSV 匯入匯出按鈕 -->
<div class="csv-buttons" style="margin-bottom: 15px; display: flex; gap: 10px;">
<button type="button" class="btn btn-secondary" onclick="exportPositionsCSV()">
<svg viewBox="0 0 24 24" style="width: 18px; height: 18px; fill: currentColor;"><path d="M14 2H6c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V8l-6-6zm4 18H6V4h7v5h5v11z"/></svg>
匯出 CSV
</button>
<button type="button" class="btn btn-secondary" onclick="importPositionsCSV()">
<svg viewBox="0 0 24 24" style="width: 18px; height: 18px; fill: currentColor;"><path d="M9 16h6v-6h4l-7-7-7 7h4zm-4 2h14v2H5z"/></svg>
匯入 CSV
</button>
<input type="file" id="positionCSVInput" accept=".csv" style="display: none;" onchange="handlePositionCSVImport(event)">
</div>
'''
# 找到崗位資料模組的 action-buttons
old_position_section = ''' <div class="action-buttons">
<button type="button" class="btn btn-primary" onclick="savePositionAndExit()">'''
new_position_section = position_csv_buttons + ''' <div class="action-buttons">
<button type="button" class="btn btn-primary" onclick="savePositionAndExit()">'''
if old_position_section in content and position_csv_buttons not in content:
content = content.replace(old_position_section, new_position_section)
print("[OK] Added CSV buttons to Position module")
# 3. 為職務資料模組加入 CSV 按鈕
job_csv_buttons = ''' <!-- CSV 匯入匯出按鈕 -->
<div class="csv-buttons" style="margin-bottom: 15px; display: flex; gap: 10px;">
<button type="button" class="btn btn-secondary" onclick="exportJobsCSV()">
<svg viewBox="0 0 24 24" style="width: 18px; height: 18px; fill: currentColor;"><path d="M14 2H6c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V8l-6-6zm4 18H6V4h7v5h5v11z"/></svg>
匯出 CSV
</button>
<button type="button" class="btn btn-secondary" onclick="importJobsCSV()">
<svg viewBox="0 0 24 24" style="width: 18px; height: 18px; fill: currentColor;"><path d="M9 16h6v-6h4l-7-7-7 7h4zm-4 2h14v2H5z"/></svg>
匯入 CSV
</button>
<input type="file" id="jobCSVInput" accept=".csv" style="display: none;" onchange="handleJobCSVImport(event)">
</div>
'''
# 找到職務資料模組的 action-buttons注意有不同的函數名
old_job_section = ''' <div class="action-buttons">
<button type="button" class="btn btn-primary" onclick="saveJobAndExit()">'''
new_job_section = job_csv_buttons + ''' <div class="action-buttons">
<button type="button" class="btn btn-primary" onclick="saveJobAndExit()">'''
if old_job_section in content and job_csv_buttons not in content:
content = content.replace(old_job_section, new_job_section)
print("[OK] Added CSV buttons to Job module")
# 4. 為崗位描述模組加入 CSV 按鈕
desc_csv_buttons = ''' <!-- CSV 匯入匯出按鈕 -->
<div class="csv-buttons" style="margin-bottom: 15px; display: flex; gap: 10px;">
<button type="button" class="btn btn-secondary" onclick="exportDescriptionsCSV()">
<svg viewBox="0 0 24 24" style="width: 18px; height: 18px; fill: currentColor;"><path d="M14 2H6c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V8l-6-6zm4 18H6V4h7v5h5v11z"/></svg>
匯出 CSV
</button>
<button type="button" class="btn btn-secondary" onclick="importDescriptionsCSV()">
<svg viewBox="0 0 24 24" style="width: 18px; height: 18px; fill: currentColor;"><path d="M9 16h6v-6h4l-7-7-7 7h4zm-4 2h14v2H5z"/></svg>
匯入 CSV
</button>
<input type="file" id="descCSVInput" accept=".csv" style="display: none;" onchange="handleDescCSVImport(event)">
</div>
'''
# 找到崗位描述模組的 action-buttons
old_desc_section = ''' <div class="action-buttons">
<button type="button" class="btn btn-primary" onclick="saveDescAndExit()">'''
new_desc_section = desc_csv_buttons + ''' <div class="action-buttons">
<button type="button" class="btn btn-primary" onclick="saveDescAndExit()">'''
if old_desc_section in content and desc_csv_buttons not in content:
content = content.replace(old_desc_section, new_desc_section)
print("[OK] Added CSV buttons to Description module")
# 5. 加入 CSV 處理函數
csv_functions = '''
// ==================== CSV 匯入匯出函數 ====================
// 崗位資料 CSV 匯出
function exportPositionsCSV() {
// 收集所有崗位資料(這裡簡化為當前表單資料)
const data = [{
positionCode: getFieldValue('positionCode'),
positionName: getFieldValue('positionName'),
positionCategory: getFieldValue('positionCategory'),
positionNature: getFieldValue('positionNature'),
headcount: getFieldValue('headcount'),
positionLevel: getFieldValue('positionLevel'),
effectiveDate: getFieldValue('effectiveDate'),
positionDesc: getFieldValue('positionDesc'),
positionRemark: getFieldValue('positionRemark'),
minEducation: getFieldValue('minEducation'),
salaryRange: getFieldValue('salaryRange'),
workExperience: getFieldValue('workExperience'),
minAge: getFieldValue('minAge'),
maxAge: getFieldValue('maxAge')
}];
const headers = ['positionCode', 'positionName', 'positionCategory', 'positionNature',
'headcount', 'positionLevel', 'effectiveDate', 'positionDesc', 'positionRemark',
'minEducation', 'salaryRange', 'workExperience', 'minAge', 'maxAge'];
CSVUtils.exportToCSV(data, 'positions.csv', headers);
showToast('崗位資料已匯出!');
}
// 崗位資料 CSV 匯入觸發
function importPositionsCSV() {
document.getElementById('positionCSVInput').click();
}
// 處理崗位 CSV 匯入
function handlePositionCSVImport(event) {
const file = event.target.files[0];
if (!file) return;
CSVUtils.importFromCSV(file, (data) => {
if (data && data.length > 0) {
const firstRow = data[0];
// 填充表單
Object.keys(firstRow).forEach(key => {
const element = document.getElementById(key);
if (element) {
element.value = firstRow[key];
}
});
showToast(`已匯入 ${data.length} 筆崗位資料(顯示第一筆)`);
}
});
// 重置 input
event.target.value = '';
}
// 職務資料 CSV 匯出
function exportJobsCSV() {
const data = [{
jobCategoryCode: getFieldValue('jobCategoryCode'),
jobCategoryName: getFieldValue('jobCategoryName'),
jobCode: getFieldValue('jobCode'),
jobName: getFieldValue('jobName'),
jobNameEn: getFieldValue('jobNameEn'),
jobEffectiveDate: getFieldValue('jobEffectiveDate'),
jobHeadcount: getFieldValue('jobHeadcount'),
jobSortOrder: getFieldValue('jobSortOrder'),
jobRemark: getFieldValue('jobRemark'),
jobLevel: getFieldValue('jobLevel'),
hasAttendanceBonus: document.getElementById('hasAttendanceBonus')?.checked,
hasHousingAllowance: document.getElementById('hasHousingAllowance')?.checked
}];
const headers = ['jobCategoryCode', 'jobCategoryName', 'jobCode', 'jobName', 'jobNameEn',
'jobEffectiveDate', 'jobHeadcount', 'jobSortOrder', 'jobRemark', 'jobLevel',
'hasAttendanceBonus', 'hasHousingAllowance'];
CSVUtils.exportToCSV(data, 'jobs.csv', headers);
showToast('職務資料已匯出!');
}
// 職務資料 CSV 匯入觸發
function importJobsCSV() {
document.getElementById('jobCSVInput').click();
}
// 處理職務 CSV 匯入
function handleJobCSVImport(event) {
const file = event.target.files[0];
if (!file) return;
CSVUtils.importFromCSV(file, (data) => {
if (data && data.length > 0) {
const firstRow = data[0];
Object.keys(firstRow).forEach(key => {
const element = document.getElementById(key);
if (element) {
if (element.type === 'checkbox') {
element.checked = firstRow[key] === 'true';
} else {
element.value = firstRow[key];
}
}
});
showToast(`已匯入 ${data.length} 筆職務資料(顯示第一筆)`);
}
});
event.target.value = '';
}
// 崗位描述 CSV 匯出
function exportDescriptionsCSV() {
const data = [{
descPositionCode: getFieldValue('descPositionCode'),
descPositionName: getFieldValue('descPositionName'),
descEffectiveDate: getFieldValue('descEffectiveDate'),
jobDuties: getFieldValue('jobDuties'),
requiredSkills: getFieldValue('requiredSkills'),
workEnvironment: getFieldValue('workEnvironment'),
careerPath: getFieldValue('careerPath'),
descRemark: getFieldValue('descRemark')
}];
const headers = ['descPositionCode', 'descPositionName', 'descEffectiveDate', 'jobDuties',
'requiredSkills', 'workEnvironment', 'careerPath', 'descRemark'];
CSVUtils.exportToCSV(data, 'job_descriptions.csv', headers);
showToast('崗位描述已匯出!');
}
// 崗位描述 CSV 匯入觸發
function importDescriptionsCSV() {
document.getElementById('descCSVInput').click();
}
// 處理崗位描述 CSV 匯入
function handleDescCSVImport(event) {
const file = event.target.files[0];
if (!file) return;
CSVUtils.importFromCSV(file, (data) => {
if (data && data.length > 0) {
const firstRow = data[0];
Object.keys(firstRow).forEach(key => {
const element = document.getElementById(key);
if (element) {
element.value = firstRow[key];
}
});
showToast(`已匯入 ${data.length} 筆崗位描述資料(顯示第一筆)`);
}
});
event.target.value = '';
}
'''
# 在 </script> 前加入函數
if 'function exportPositionsCSV()' not in content:
content = content.replace(' </script>\n</body>', csv_functions + '\n </script>\n</body>')
print("[OK] Added CSV handler functions")
# 寫回
with open('index.html', 'w', encoding='utf-8') as f:
f.write(content)
print("\n" + "="*60)
print("[OK] CSV Integration Complete!")
print("="*60)
print("\nCompleted tasks:")
print("1. Added csv_utils.js reference in <head>")
print("2. Added CSV buttons to Position module")
print("3. Added CSV buttons to Job module")
print("4. Added CSV buttons to Description module")
print("5. Added all CSV handler functions")
print("\nPlease reload the page (Ctrl+F5) to test CSV features!")