Files
hr-position-system/js/ui.js
DonaldFang 方士碩 a6af297623 backup: 完成 HR_position_ 表格前綴重命名與欄位對照表整理
變更內容:
- 所有資料表加上 HR_position_ 前綴
- 整理完整欄位顯示名稱與 ID 對照表
- 模組化 JS 檔案 (admin.js, ai.js, csv.js 等)
- 專案結構優化 (docs/, scripts/, tests/ 等)

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-09 12:05:20 +08:00

392 lines
12 KiB
JavaScript
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.

/**
* UI - UI 操作函式
* 包含模組切換、預覽更新、表單資料收集
*/
import { showToast } from './utils.js';
import { categoryMap, natureMap, jobCategoryMap } from './config.js';
// ==================== 模組切換 ====================
/**
* 切換頁面模組
* @param {string} moduleName - 模組名稱position/job/jobdesc/positionlist/deptfunction/admin
*/
export function switchModule(moduleName) {
document.querySelectorAll('.module-btn').forEach(b => {
b.classList.remove('active', 'job-active', 'desc-active');
});
document.querySelectorAll('.module-content').forEach(c => c.classList.remove('active'));
const targetBtn = document.querySelector(`.module-btn[data-module="${moduleName}"]`);
if (targetBtn) {
targetBtn.classList.add('active');
if (moduleName === 'job') targetBtn.classList.add('job-active');
if (moduleName === 'jobdesc') targetBtn.classList.add('desc-active');
}
const targetModule = document.getElementById('module-' + moduleName);
if (targetModule) {
targetModule.classList.add('active');
}
// 自動刷新崗位清單(需要從其他模組匯入)
if (moduleName === 'positionlist' && typeof window.loadPositionList === 'function') {
window.loadPositionList();
}
updatePreview();
}
// ==================== 表單資料收集 ====================
/**
* 收集崗位表單資料
* @returns {Object} - 崗位資料(分為 basicInfo 和 recruitInfo
*/
export function getPositionFormData() {
const form = document.getElementById('positionForm');
const formData = new FormData(form);
const data = { basicInfo: {}, recruitInfo: {} };
// 使用新的 pos_ prefix 欄位
const basicFieldMapping = {
'pos_code': 'positionCode',
'pos_name': 'positionName',
'pos_category': 'positionCategory',
'pos_categoryName': 'positionCategoryName',
'pos_type': 'positionType',
'pos_typeName': 'positionTypeName',
'pos_headcount': 'headcount',
'pos_level': 'positionLevel',
'pos_effectiveDate': 'effectiveDate',
'pos_desc': 'positionDesc',
'pos_remark': 'positionRemark'
};
// 使用新的 rec_ prefix 欄位
const recruitFieldMapping = {
'rec_eduLevel': 'minEducation',
'rec_gender': 'requiredGender',
'rec_salaryRange': 'salaryRange',
'rec_expYears': 'workExperience',
'rec_minAge': 'minAge',
'rec_maxAge': 'maxAge',
'rec_jobType': 'jobType',
'rec_position': 'recruitPosition',
'rec_jobTitle': 'jobTitle',
'rec_jobDesc': 'jobDesc',
'rec_positionReq': 'positionReq',
'rec_certReq': 'certReq',
'rec_majorReq': 'majorReq',
'rec_skillReq': 'skillReq',
'rec_langReq': 'langReq',
'rec_otherReq': 'otherReq',
'rec_superiorCode': 'superiorPosition',
'rec_remark': 'recruitRemark'
};
Object.entries(basicFieldMapping).forEach(([htmlId, dataKey]) => {
const el = document.getElementById(htmlId);
if (el && el.value) data.basicInfo[dataKey] = el.value;
});
Object.entries(recruitFieldMapping).forEach(([htmlId, dataKey]) => {
const el = document.getElementById(htmlId);
if (el && el.value) data.recruitInfo[dataKey] = el.value;
});
return data;
}
/**
* 收集職務表單資料
* @returns {Object} - 職務資料
*/
export function getJobFormData() {
const form = document.getElementById('jobForm');
const data = {};
// 使用新的 job_ prefix 欄位
const fieldMapping = {
'job_category': 'jobCategoryCode',
'job_categoryName': 'jobCategoryName',
'job_code': 'jobCode',
'job_name': 'jobName',
'job_nameEn': 'jobNameEn',
'job_level': 'jobLevel',
'job_effectiveDate': 'jobEffectiveDate',
'job_sortOrder': 'jobSortOrder',
'job_headcount': 'jobHeadcount',
'job_remark': 'jobRemark'
};
Object.entries(fieldMapping).forEach(([htmlId, dataKey]) => {
const el = document.getElementById(htmlId);
if (el && el.value) data[dataKey] = el.value;
});
const hasAttBonus = document.getElementById('job_hasAttBonus');
const hasHouseAllow = document.getElementById('job_hasHouseAllow');
data.hasAttendanceBonus = hasAttBonus ? hasAttBonus.checked : false;
data.hasHousingAllowance = hasHouseAllow ? hasHouseAllow.checked : false;
return data;
}
/**
* 收集崗位描述表單資料
* @returns {Object} - 崗位描述資料
*/
export function getJobDescFormData() {
const form = document.getElementById('jobDescForm');
if (!form) return {};
const data = { basicInfo: {}, positionInfo: {}, responsibilities: {}, requirements: {} };
// Basic Info - 使用新的 jd_ prefix
const basicMapping = {
'jd_empNo': 'empNo',
'jd_empName': 'empName',
'jd_posCode': 'positionCode',
'jd_versionDate': 'versionDate'
};
Object.entries(basicMapping).forEach(([htmlId, dataKey]) => {
const el = document.getElementById(htmlId);
if (el && el.value) data.basicInfo[dataKey] = el.value;
});
// Position Info - 使用新的 jd_ prefix
const posInfoMapping = {
'jd_posName': 'positionName',
'jd_department': 'department',
'jd_posLevel': 'positionLevel',
'jd_posEffDate': 'positionEffectiveDate',
'jd_supervisor': 'directSupervisor',
'jd_gradeJob': 'positionGradeJob',
'jd_reportTo': 'reportTo',
'jd_directReports': 'directReports',
'jd_location': 'workLocation',
'jd_empAttr': 'empAttribute'
};
Object.entries(posInfoMapping).forEach(([htmlId, dataKey]) => {
const el = document.getElementById(htmlId);
if (el && el.value) data.positionInfo[dataKey] = el.value;
});
// Purpose & Responsibilities - 使用新的 jd_ prefix
const purpose = document.getElementById('jd_purpose');
if (purpose && purpose.value) data.responsibilities.positionPurpose = purpose.value;
const mainResp = document.getElementById('jd_mainResp');
if (mainResp && mainResp.value) data.responsibilities.mainResponsibilities = mainResp.value;
// Requirements - 使用新的 jd_ prefix
const reqMapping = {
'jd_eduLevel': 'education',
'jd_basicSkills': 'basicSkills',
'jd_proKnowledge': 'professionalKnowledge',
'jd_expReq': 'workExperienceReq',
'jd_otherReq': 'otherRequirements'
};
Object.entries(reqMapping).forEach(([htmlId, dataKey]) => {
const el = document.getElementById(htmlId);
if (el && el.value) data.requirements[dataKey] = el.value;
});
return data;
}
/**
* 收集部門職責表單資料
* @returns {Object} - 部門職責資料
*/
export function getDeptFunctionFormData() {
const form = document.getElementById('deptFunctionForm');
if (!form) return {};
const data = {};
// 使用新的 df_ prefix 欄位
const fieldMapping = {
'df_code': 'dfCode',
'df_name': 'dfName',
'df_businessUnit': 'businessUnit',
'df_division': 'division',
'df_department': 'department',
'df_section': 'section',
'df_posTitle': 'positionTitle',
'df_posLevel': 'positionLevel',
'df_managerTitle': 'managerTitle',
'df_effectiveDate': 'effectiveDate',
'df_headcountLimit': 'headcountLimit',
'df_status': 'status',
'df_mission': 'mission',
'df_vision': 'vision',
'df_coreFunc': 'coreFunctions',
'df_kpis': 'kpis',
'df_collab': 'collaboration',
'df_remark': 'remark'
};
Object.entries(fieldMapping).forEach(([htmlId, dataKey]) => {
const el = document.getElementById(htmlId);
if (el && el.value) data[dataKey] = el.value;
});
return data;
}
// ==================== 預覽更新 ====================
/**
* 更新 JSON 預覽
*/
export function updatePreview() {
const activeBtn = document.querySelector('.module-btn.active');
if (!activeBtn) return;
const activeModule = activeBtn.dataset.module;
let data;
if (activeModule === 'position') {
data = { module: '崗位基礎資料', ...getPositionFormData() };
} else if (activeModule === 'job') {
data = { module: '職務基礎資料', ...getJobFormData() };
} else if (activeModule === 'jobdesc') {
data = { module: '崗位描述', ...getJobDescFormData() };
} else if (activeModule === 'deptfunction') {
data = { module: '部門職責', ...getDeptFunctionFormData() };
} else {
return; // 其他模組不顯示預覽
}
const previewEl = document.getElementById('jsonPreview');
if (previewEl) {
previewEl.textContent = JSON.stringify(data, null, 2);
}
}
// ==================== 表單邏輯輔助函式 ====================
/**
* 更新崗位類別中文名稱
*/
export function updateCategoryName() {
const categoryEl = document.getElementById('pos_category');
const categoryNameEl = document.getElementById('pos_categoryName');
if (categoryEl && categoryNameEl) {
categoryNameEl.value = categoryMap[categoryEl.value] || '';
}
updatePreview();
}
/**
* 更新崗位性質中文名稱
*/
export function updateNatureName() {
const typeEl = document.getElementById('pos_type');
const typeNameEl = document.getElementById('pos_typeName');
if (typeEl && typeNameEl) {
typeNameEl.value = natureMap[typeEl.value] || '';
}
updatePreview();
}
/**
* 更新職務類別中文名稱
*/
export function updateJobCategoryName() {
const categoryEl = document.getElementById('job_category');
const categoryNameEl = document.getElementById('job_categoryName');
if (categoryEl && categoryNameEl) {
categoryNameEl.value = jobCategoryMap[categoryEl.value] || '';
}
updatePreview();
}
/**
* 修改崗位編號
*/
export function changePositionCode() {
const codeEl = document.getElementById('pos_code');
if (!codeEl) return;
const currentCode = codeEl.value;
const newCode = prompt('請輸入新的崗位編號:', currentCode);
if (newCode && newCode !== currentCode) {
codeEl.value = newCode;
showToast('崗位編號已更改!');
updatePreview();
}
}
/**
* 修改職務編號
*/
export function changeJobCode() {
const codeEl = document.getElementById('job_code');
if (!codeEl) return;
const currentCode = codeEl.value;
const newCode = prompt('請輸入新的職務編號:', currentCode);
if (newCode && newCode !== currentCode) {
codeEl.value = newCode;
showToast('職務編號已更改!');
updatePreview();
}
}
// ==================== 模態框函式(待整合)====================
/**
* 開啟專業科目選擇模態框
*/
export function openMajorModal() {
const modal = document.getElementById('majorModal');
if (modal) {
modal.classList.add('show');
}
}
/**
* 關閉專業科目選擇模態框
*/
export function closeMajorModal() {
const modal = document.getElementById('majorModal');
if (modal) {
modal.classList.remove('show');
}
}
/**
* 確認選擇專業科目
*/
export function confirmMajor() {
const selected = [];
document.querySelectorAll('#majorModal input[type="checkbox"]:checked').forEach(cb => {
selected.push(cb.value);
});
const majorReqEl = document.getElementById('rec_majorReq');
if (majorReqEl) {
majorReqEl.value = selected.join(', ');
}
closeMajorModal();
updatePreview();
}
// 將函式掛載到 window 上以便內聯事件處理器使用
if (typeof window !== 'undefined') {
window.switchModule = switchModule;
window.updateCategoryName = updateCategoryName;
window.updateNatureName = updateNatureName;
window.updateJobCategoryName = updateJobCategoryName;
window.changePositionCode = changePositionCode;
window.changeJobCode = changeJobCode;
window.openMajorModal = openMajorModal;
window.closeMajorModal = closeMajorModal;
window.confirmMajor = confirmMajor;
window.updatePreview = updatePreview;
}