feat: 實作三個錦囊 AI 功能

- 新增 AI 錦囊 CSS 樣式到 components.css
- 創建 js/ai-bags.js 模組,包含:
  * 5個模組各3個錦囊的預設 prompt 模板
  * executeAIBag() - 執行 AI 生成並填充表單
  * editBagPrompt() - 編輯自定義 prompt
  * LocalStorage 管理自定義 prompt
- 更新 index.html:
  * 替換 5 處 AI 按鈕為三個錦囊(崗位基礎、招聘要求、職務、部門職責、崗位描述)
  * 新增 Prompt 編輯模態框
- 更新 main.js 引入 ai-bags.js 並初始化
- 新增設計文檔:三個錦囊設計.md
- 新增欄位對照文檔:表單欄位清單.md、更新欄位名稱.md、ID重命名對照表.md

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-12-06 01:19:54 +08:00
parent 12ceccc3d3
commit 8069f1b628
8 changed files with 1925 additions and 21 deletions

286
ID重命名對照表.md Normal file
View File

@@ -0,0 +1,286 @@
# HTML ID 重命名對照表
## 變更概覽
- **總計需變更72 個**
- **無需變更13 個**
- **總欄位數85 個**
---
## 模組 1: 崗位基礎資料 - 基礎資料頁籤
**需變更15 / 15**
| 舊 ID | 新 ID | 欄位名稱 | 備註 |
|-------|-------|---------|------|
| `businessUnit` | `pos_businessUnit` | 事業體 (Business Unit) | |
| `division` | `pos_division` | 處級單位 (Division) | |
| `department` | `pos_department` | 部級單位 (Department) | |
| `section` | `pos_section` | 課級單位 (Section) | |
| `positionCode` | `pos_code` | 崗位編號 | 必填欄位 |
| `effectiveDate` | `pos_effectiveDate` | 生效日期 | |
| `positionName` | `pos_name` | 崗位名稱 | 必填欄位 |
| `positionLevel` | `pos_level` | 崗位級別 | |
| `positionCategory` | `pos_category` | 崗位類別 | onchange 事件 |
| `positionCategoryName` | `pos_categoryName` | 崗位類別名稱 | readonly |
| `positionNature` | `pos_type` | 崗位性質 | onchange 事件,注意:資料庫改為 positionType |
| `positionNatureName` | `pos_typeName` | 崗位性質名稱 | readonly資料庫改為 positionTypeName |
| `headcount` | `pos_headcount` | 編制人數 | |
| `positionDesc` | `pos_desc` | 崗位描述 | 資料庫改為 description |
| `positionRemark` | `pos_remark` | 崗位備注 | 資料庫改為 remark |
---
## 模組 2: 崗位基礎資料 - 招聘要求頁籤
**需變更18 / 18**
| 舊 ID | 新 ID | 欄位名稱 | 備註 |
|-------|-------|---------|------|
| `minEducation` | `rec_eduLevel` | 最低學歷 | 資料庫改為 educationLevel |
| `requiredGender` | `rec_gender` | 要求性別 | |
| `salaryRange` | `rec_salaryRange` | 薪酬范圍 | |
| `workExperience` | `rec_expYears` | 工作經驗 | 資料庫改為 experienceYears |
| `minAge` | `rec_minAge` | 最小年齡 | |
| `maxAge` | `rec_maxAge` | 最大年齡 | |
| `jobType` | `rec_jobType` | 工作性質 | |
| `recruitPosition` | `rec_position` | 招聘職位 | 資料庫改為 recruitPosition |
| `jobTitle` | `rec_jobTitle` | 職位名稱(對外) | |
| `superiorPosition` | `rec_superiorCode` | 上級崗位編號 | 資料庫改為 superiorPositionCode |
| `jobDesc` | `rec_jobDesc` | 職位描述(JD) | 資料庫改為 recruitJobDesc |
| `positionReq` | `rec_positionReq` | 崗位要求(Req) | 資料庫改為 recruitRequirements |
| `titleReq` | `rec_certReq` | 職稱要求 | 語義更正為「證照要求」,資料庫改為 certRequirements |
| `majorReq` | `rec_majorReq` | 專業要求 | 資料庫改為 majorRequirements |
| `skillReq` | `rec_skillReq` | 技能要求 | 資料庫改為 skillRequirements |
| `langReq` | `rec_langReq` | 語言要求 | 資料庫改為 langRequirements |
| `otherReq` | `rec_otherReq` | 其他要求 | 資料庫改為 otherRequirements |
| `recruitRemark` | `rec_remark` | 招聘備注 | 資料庫改為 recruitRemark |
---
## 模組 3: 職務基礎資料
**需變更12 / 12**
| 舊 ID | 新 ID | 欄位名稱 | 備註 |
|-------|-------|---------|------|
| `jobCategoryCode` | `job_category` | 職務類別編號 | 必填欄位onchange 事件 |
| `jobCategoryName` | `job_categoryName` | 職務類別名稱 | readonly |
| `jobCode` | `job_code` | 職務編號 | 必填欄位 |
| `jobName` | `job_name` | 職務名稱 | 必填欄位 |
| `jobNameEn` | `job_nameEn` | 職務英文 | |
| `jobEffectiveDate` | `job_effectiveDate` | 生效日期 | 資料庫改為 effectiveDate |
| `jobLevel` | `job_level` | 職務層級 | 敏感欄位 |
| `jobHeadcount` | `job_headcount` | 編制人數 | 資料庫改為 headcount |
| `jobSortOrder` | `job_sortOrder` | 排列順序 | 資料庫改為 sortOrder |
| `hasAttendanceBonus` | `job_hasAttBonus` | 全勤獎金 | Toggle Switch |
| `hasHousingAllowance` | `job_hasHouseAllow` | 住房補貼 | Toggle Switch |
| `jobRemark` | `job_remark` | 職務備注 | 資料庫改為 remark |
---
## 模組 4: 部門職責
**需變更19 / 19**
| 舊 ID | 新 ID | 欄位名稱 | 備註 |
|-------|-------|---------|------|
| `deptFunctionCode` | `df_code` | 部門職責編號 | 必填欄位,資料庫改為 dfCode |
| `deptFunctionName` | `df_name` | 部門職責名稱 | 必填欄位,資料庫改為 dfName |
| `deptFunctionBU` | `df_businessUnit` | 事業體 (第1個) | 必填欄位,已合併重複欄位 |
| `deptFunc_businessUnit` | `df_businessUnit` | 事業體 (第2個) | **合併為同一欄位** |
| `deptFunc_division` | `df_division` | 處級單位 | 必填欄位 |
| `deptFunc_department` | `df_department` | 部級單位 | 必填欄位 |
| `deptFunc_section` | `df_section` | 課級單位 | |
| `deptFunc_positionTitle` | `df_posTitle` | 崗位名稱 | 必填欄位,資料庫改為 positionTitle |
| `deptFunc_positionLevel` | `df_posLevel` | 崗位級別 | 資料庫保持 positionLevel |
| `deptManager` | `df_managerTitle` | 部門主管職稱 | 資料庫改為 managerTitle |
| `deptFunctionEffectiveDate` | `df_effectiveDate` | 生效日期 | 必填欄位,資料庫改為 effectiveDate |
| `deptHeadcount` | `df_headcountLimit` | 部門人數上限 | 資料庫改為 headcountLimit |
| `deptStatus` | `df_status` | 部門狀態 | 資料庫改為 status |
| `deptMission` | `df_mission` | 部門使命 | 資料庫改為 mission |
| `deptVision` | `df_vision` | 部門願景 | 資料庫改為 vision |
| `deptCoreFunctions` | `df_coreFunc` | 核心職責 | 必填欄位,資料庫改為 coreFunctions |
| `deptKPIs` | `df_kpis` | 關鍵績效指標 | 資料庫改為 kpis |
| `deptCollaboration` | `df_collab` | 協作部門 | 資料庫改為 collaboration |
| `deptFunctionRemark` | `df_remark` | 備注 | 資料庫改為 remark |
**特別注意:** 原本有兩個事業體欄位 (`deptFunctionBU``deptFunc_businessUnit`),新規範已合併為單一欄位 `df_businessUnit`
---
## 模組 5: 崗位描述 - 基本信息
**需變更3 / 4**
| 舊 ID | 新 ID | 欄位名稱 | 備註 |
|-------|-------|---------|------|
| `jd_empNo` | `jd_empNo` | 工號 | ✓ 無需變更 |
| `jd_empName` | `jd_empName` | 姓名 | ✓ 無需變更 |
| `jd_positionCode` | `jd_posCode` | 崗位代碼 | |
| `jd_versionDate` | `jd_versionDate` | 版本更新日期 | ✓ 無需變更 |
---
## 模組 6: 崗位描述 - 崗位基本信息
**需變更10 / 14**
| 舊 ID | 新 ID | 欄位名稱 | 備註 |
|-------|-------|---------|------|
| `jd_positionName` | `jd_posName` | 崗位名稱 | |
| `jd_businessUnit` | `jd_businessUnit` | 事業體 | ✓ 無需變更 |
| `jd_division` | `jd_division` | 處級單位 | ✓ 無需變更 |
| `jd_department` | `jd_department` | 部級單位 | ✓ 無需變更 |
| `jd_section` | `jd_section` | 課級單位 | ✓ 無需變更 |
| `jd_positionTitle` | `(刪除)` | 崗位名稱 (重複) | 與 positionName 重複,建議刪除 |
| `jd_positionLevel` | `jd_posLevel` | 崗位級別 | |
| `jd_positionEffectiveDate` | `jd_posEffDate` | 崗位生效日期 | |
| `jd_directSupervisor` | `jd_supervisor` | 直接領導職務 | |
| `jd_positionGradeJob` | `jd_gradeJob` | 崗位職等&職務 | Modal 選擇器 |
| `jd_reportTo` | `jd_reportTo` | 匯報對象職務 | ✓ 無需變更 |
| `jd_directReports` | `jd_directReports` | 直接下級 | ✓ 無需變更 |
| `jd_workLocation` | `jd_location` | 任職地點 | |
| `jd_empAttribute` | `jd_empAttr` | 員工屬性 | |
---
## 模組 7: 崗位描述 - 部門職責資訊
**需變更3 / 5**
| 舊 ID | 新 ID | 欄位名稱 | 備註 |
|-------|-------|---------|------|
| `jd_deptFunctionCode` | `jd_dfCode` | 部門職責編號 | readonly |
| `jd_deptFunctionBU` | `(保留)` | 事業體 | 建議保留或改為 jd_dfBU |
| `jd_deptMission` | `jd_deptMission` | 部門使命 | ✓ 無需變更 |
| `jd_deptCoreFunctions` | `jd_deptCoreFunctions` | 部門核心職責 | ✓ 無需變更 |
| `jd_deptKPIs` | `jd_deptKPIs` | 部門 KPIs | ✓ 無需變更 |
**說明:** 這些是 readonly 欄位,自動帶入,可保持現有命名以維持與來源資料的一致性。
---
## 模組 8: 崗位描述 - 職責描述
**需變更1 / 2**
| 舊 ID | 新 ID | 欄位名稱 | 備註 |
|-------|-------|---------|------|
| `jd_positionPurpose` | `jd_purpose` | 崗位設置目的 | |
| `jd_mainResponsibilities` | `jd_mainResp` | 主要崗位職責 | Numbered textarea |
---
## 模組 9: 崗位描述 - 崗位要求
**需變更5 / 5**
| 舊 ID | 新 ID | 欄位名稱 | 備註 |
|-------|-------|---------|------|
| `jd_education` | `jd_eduLevel` | 教育程度 | |
| `jd_basicSkills` | `jd_basicSkills` | 基本技能 | ✓ 無需變更 |
| `jd_professionalKnowledge` | `jd_proKnowledge` | 專業知識 | |
| `jd_workExperienceReq` | `jd_expReq` | 工作經驗 | |
| `jd_otherRequirements` | `jd_otherReq` | 其他 | |
---
## 重要變更說明
### 1. 前綴標準化
所有欄位統一採用模組前綴:
- `pos_` - 崗位基礎資料
- `rec_` - 招聘要求
- `job_` - 職務基礎資料
- `df_` - 部門職責
- `jd_` - 崗位描述
### 2. 命名簡化
- `positionCode``pos_code`
- `positionName``pos_name`
- `positionCategory``pos_category`
- `positionNature``pos_type` (語義更精確)
- `hasAttendanceBonus``job_hasAttBonus` (縮寫簡化)
### 3. 語義改善
- `titleReq``rec_certReq` (職稱要求 → 證照要求)
- `positionNature``pos_type` (性質 → 類型,更符合英文語義)
- `workExperience``rec_expYears` (經驗年數更明確)
### 4. 資料庫欄位同步變更
部分 HTML ID 變更時,資料庫欄位名稱也需要對應修改:
- `positionNature``positionType`
- `positionDesc``description`
- `positionRemark``remark`
- `minEducation``educationLevel`
### 5. 重複欄位合併
部門職責模組中的兩個事業體欄位:
- 原:`deptFunctionBU``deptFunc_businessUnit`
- 新:統一為 `df_businessUnit`
---
## 實施建議
### 階段 1: 資料庫遷移
1. 執行資料庫欄位重命名 SQL 腳本
2. 更新所有 ORM Model 定義
3. 更新 API 回應的欄位名稱
### 階段 2: 前端更新
1. 全局搜尋替換 HTML ID
2. 更新所有 JavaScript 選擇器
3. 更新 CSS 樣式選擇器
4. 更新表單驗證邏輯
### 階段 3: 測試驗證
1. 單元測試:驗證所有表單欄位綁定
2. 整合測試:驗證資料儲存與讀取
3. E2E 測試:完整流程測試
4. 回歸測試:確保無遺漏欄位
### 階段 4: 文檔更新
1. API 文檔更新
2. 資料字典更新
3. 開發指南更新
4. 使用者手冊更新(如有影響)
---
## 風險評估
### 高風險變更 (必填欄位)
- `positionCode``pos_code`
- `positionName``pos_name`
- `jobCode``job_code`
- `jobName``job_name`
- `deptFunctionCode``df_code`
**建議:** 優先測試這些欄位的儲存與驗證邏輯。
### 中風險變更 (聯動欄位)
- 組織結構欄位:`businessUnit`, `division`, `department`, `section`
- 自動帶出欄位:`positionCategoryName`, `positionNatureName`
**建議:** 仔細測試聯動邏輯和 onchange 事件。
### 低風險變更 (選填欄位)
- 備注類欄位
- 描述類欄位
---
## 檢查清單
- [ ] 資料庫遷移腳本已準備
- [ ] Model 定義已更新
- [ ] HTML 表單 ID 已更新
- [ ] JavaScript 選擇器已更新
- [ ] CSS 樣式已更新
- [ ] API 端點已更新
- [ ] 驗證邏輯已更新
- [ ] 單元測試已通過
- [ ] 整合測試已通過
- [ ] E2E 測試已通過
- [ ] 文檔已更新
- [ ] 程式碼審查已完成
---
**文檔版本:** v1.0
**建立日期:** 2025-12-06
**最後更新:** 2025-12-06
**維護者:** 系統開發團隊

View File

@@ -98,10 +98,27 @@
<form id="positionForm">
<div class="tab-content active" id="tab-position-basic">
<button type="button" class="ai-generate-btn" onclick="generatePositionBasic()">
<svg viewBox="0 0 24 24"><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>
<span>✨ I'm feeling lucky</span>
</button>
<!-- AI 錦囊:三個可自定義的 AI 生成按鈕 -->
<div class="ai-bags-container">
<div class="ai-bag" data-module="positionBasic" data-bag="1" onclick="executeAIBag('positionBasic', 1)">
<div class="bag-icon">🎁</div>
<div class="bag-title">錦囊一</div>
<div class="bag-subtitle">簡化版</div>
<button class="bag-edit-btn" onclick="editBagPrompt(event, 'positionBasic', 1)">⚙️</button>
</div>
<div class="ai-bag" data-module="positionBasic" data-bag="2" onclick="executeAIBag('positionBasic', 2)">
<div class="bag-icon">🎁</div>
<div class="bag-title">錦囊二</div>
<div class="bag-subtitle">標準版</div>
<button class="bag-edit-btn" onclick="editBagPrompt(event, 'positionBasic', 2)">⚙️</button>
</div>
<div class="ai-bag" data-module="positionBasic" data-bag="3" onclick="executeAIBag('positionBasic', 3)">
<div class="bag-icon">🎁</div>
<div class="bag-title">錦囊三</div>
<div class="bag-subtitle">詳細版</div>
<button class="bag-edit-btn" onclick="editBagPrompt(event, 'positionBasic', 3)">⚙️</button>
</div>
</div>
<div class="form-grid">
<!-- 事業體 -->
<div class="form-group">
@@ -212,10 +229,27 @@
</div>
<div class="tab-content" id="tab-position-recruit">
<button type="button" class="ai-generate-btn" onclick="generatePositionRecruit()">
<svg viewBox="0 0 24 24"><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>
<span>✨ I'm feeling lucky</span>
</button>
<!-- AI 錦囊:三個可自定義的 AI 生成按鈕 -->
<div class="ai-bags-container">
<div class="ai-bag" data-module="positionRecruit" data-bag="1" onclick="executeAIBag('positionRecruit', 1)">
<div class="bag-icon">🎁</div>
<div class="bag-title">錦囊一</div>
<div class="bag-subtitle">基本需求</div>
<button class="bag-edit-btn" onclick="editBagPrompt(event, 'positionRecruit', 1)">⚙️</button>
</div>
<div class="ai-bag" data-module="positionRecruit" data-bag="2" onclick="executeAIBag('positionRecruit', 2)">
<div class="bag-icon">🎁</div>
<div class="bag-title">錦囊二</div>
<div class="bag-subtitle">標準需求</div>
<button class="bag-edit-btn" onclick="editBagPrompt(event, 'positionRecruit', 2)">⚙️</button>
</div>
<div class="ai-bag" data-module="positionRecruit" data-bag="3" onclick="executeAIBag('positionRecruit', 3)">
<div class="bag-icon">🎁</div>
<div class="bag-title">錦囊三</div>
<div class="bag-subtitle">完整需求</div>
<button class="bag-edit-btn" onclick="editBagPrompt(event, 'positionRecruit', 3)">⚙️</button>
</div>
</div>
<div class="form-grid">
<div class="form-group">
<label>最低學歷</label>
@@ -402,10 +436,27 @@
<form id="jobForm">
<div class="tab-content active" id="tab-job-basic">
<button type="button" class="ai-generate-btn" onclick="generateJobBasic()">
<svg viewBox="0 0 24 24"><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>
<span>✨ I'm feeling lucky</span>
</button>
<!-- AI 錦囊:三個可自定義的 AI 生成按鈕 -->
<div class="ai-bags-container">
<div class="ai-bag" data-module="jobBasic" data-bag="1" onclick="executeAIBag('jobBasic', 1)">
<div class="bag-icon">🎁</div>
<div class="bag-title">錦囊一</div>
<div class="bag-subtitle">簡化版</div>
<button class="bag-edit-btn" onclick="editBagPrompt(event, 'jobBasic', 1)">⚙️</button>
</div>
<div class="ai-bag" data-module="jobBasic" data-bag="2" onclick="executeAIBag('jobBasic', 2)">
<div class="bag-icon">🎁</div>
<div class="bag-title">錦囊二</div>
<div class="bag-subtitle">標準版</div>
<button class="bag-edit-btn" onclick="editBagPrompt(event, 'jobBasic', 2)">⚙️</button>
</div>
<div class="ai-bag" data-module="jobBasic" data-bag="3" onclick="executeAIBag('jobBasic', 3)">
<div class="bag-icon">🎁</div>
<div class="bag-title">錦囊三</div>
<div class="bag-subtitle">完整版</div>
<button class="bag-edit-btn" onclick="editBagPrompt(event, 'jobBasic', 3)">⚙️</button>
</div>
</div>
<div class="form-grid">
<div class="form-group">
<label><span class="required">*</span> 職務類別編號</label>
@@ -546,10 +597,27 @@
<div class="form-card">
<form id="deptFunctionForm">
<div class="tab-content active">
<button type="button" class="ai-generate-btn" onclick="generateDeptFunction()">
<svg viewBox="0 0 24 24"><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>
<span>✨ I'm feeling lucky</span>
</button>
<!-- AI 錦囊:三個可自定義的 AI 生成按鈕 -->
<div class="ai-bags-container">
<div class="ai-bag" data-module="deptFunction" data-bag="1" onclick="executeAIBag('deptFunction', 1)">
<div class="bag-icon">🎁</div>
<div class="bag-title">錦囊一</div>
<div class="bag-subtitle">基本版</div>
<button class="bag-edit-btn" onclick="editBagPrompt(event, 'deptFunction', 1)">⚙️</button>
</div>
<div class="ai-bag" data-module="deptFunction" data-bag="2" onclick="executeAIBag('deptFunction', 2)">
<div class="bag-icon">🎁</div>
<div class="bag-title">錦囊二</div>
<div class="bag-subtitle">標準版</div>
<button class="bag-edit-btn" onclick="editBagPrompt(event, 'deptFunction', 2)">⚙️</button>
</div>
<div class="ai-bag" data-module="deptFunction" data-bag="3" onclick="executeAIBag('deptFunction', 3)">
<div class="bag-icon">🎁</div>
<div class="bag-title">錦囊三</div>
<div class="bag-subtitle">完整版</div>
<button class="bag-edit-btn" onclick="editBagPrompt(event, 'deptFunction', 3)">⚙️</button>
</div>
</div>
<div class="csv-buttons" style="margin-bottom: 15px;">
<button type="button" class="btn btn-secondary" onclick="importDeptFunctionCSV()">匯入 CSV</button>
@@ -732,10 +800,27 @@
<form id="jobDescForm">
<div class="tab-content active" id="tab-jobdesc-basic">
<button type="button" class="ai-generate-btn" onclick="generateJobDesc()">
<svg viewBox="0 0 24 24"><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>
<span>✨ I'm feeling lucky</span>
</button>
<!-- AI 錦囊:三個可自定義的 AI 生成按鈕 -->
<div class="ai-bags-container">
<div class="ai-bag" data-module="jobDesc" data-bag="1" onclick="executeAIBag('jobDesc', 1)">
<div class="bag-icon">🎁</div>
<div class="bag-title">錦囊一</div>
<div class="bag-subtitle">基本版</div>
<button class="bag-edit-btn" onclick="editBagPrompt(event, 'jobDesc', 1)">⚙️</button>
</div>
<div class="ai-bag" data-module="jobDesc" data-bag="2" onclick="executeAIBag('jobDesc', 2)">
<div class="bag-icon">🎁</div>
<div class="bag-title">錦囊二</div>
<div class="bag-subtitle">標準版</div>
<button class="bag-edit-btn" onclick="editBagPrompt(event, 'jobDesc', 2)">⚙️</button>
</div>
<div class="ai-bag" data-module="jobDesc" data-bag="3" onclick="executeAIBag('jobDesc', 3)">
<div class="bag-icon">🎁</div>
<div class="bag-title">錦囊三</div>
<div class="bag-subtitle">完整版</div>
<button class="bag-edit-btn" onclick="editBagPrompt(event, 'jobDesc', 3)">⚙️</button>
</div>
</div>
<!-- 頂部區域 -->
<div class="form-grid">
<div class="form-group">
@@ -4024,5 +4109,40 @@ ${contextInfo}
</script>
<!-- ==================== Prompt 編輯模態框 ==================== -->
<div class="modal-overlay" id="promptEditModal">
<div class="modal" style="max-width: 700px;">
<div class="modal-header">
<h3 id="promptModalTitle">編輯 Prompt</h3>
<button class="modal-close" onclick="closePromptEditModal()"></button>
</div>
<div class="modal-body">
<div class="form-group">
<label>錦囊標題</label>
<input type="text" id="promptTitle" placeholder="例如:簡化版、標準版、詳細版">
</div>
<div class="form-group" style="margin-top: 16px;">
<label>副標題(選填)</label>
<input type="text" id="promptSubtitle" placeholder="例如:僅必填欄位、常用欄位">
</div>
<div class="form-group" style="margin-top: 16px;">
<label>Prompt 內容</label>
<textarea id="promptContent" rows="12" placeholder="請輸入 AI Prompt 內容&#10;&#10;可使用的變數:&#10;{existingData} - 當前表單已填寫的資料&#10;{positionName} - 崗位名稱"></textarea>
</div>
<div style="margin-top: 12px; padding: 12px; background: #f8fafc; border-radius: 6px; font-size: 0.85rem; color: #64748b;">
<strong>提示:</strong>Prompt 會自動替換以下變數:<br>
<code>{existingData}</code> → 當前表單已填寫的資料JSON 格式)<br>
<code>{positionName}</code> → 崗位名稱(用於招聘要求頁籤)
</div>
</div>
<div class="modal-footer">
<button class="btn btn-secondary" onclick="resetToDefaultPrompt()">重置為預設</button>
<button class="btn btn-cancel" onclick="closePromptEditModal()">取消</button>
<button class="btn btn-primary" onclick="savePromptEdit()">保存</button>
</div>
</div>
</div>
</body>
</html>

628
js/ai-bags.js Normal file
View File

@@ -0,0 +1,628 @@
/**
* AI Bags - 三個錦囊功能
* 提供可自定義 prompt 的 AI 生成按鈕
*/
import { callClaudeAPI } from './api.js';
import { showToast, fillIfEmpty } from './utils.js';
import { getPositionFormData, getJobFormData, getJobDescFormData, getDeptFunctionFormData } from './ui.js';
// ==================== 預設 Prompt 模板 ====================
const DEFAULT_PROMPTS = {
positionBasic: {
bag1: {
title: '簡化版',
subtitle: '僅必填欄位',
prompt: `你是專業人資顧問,熟悉半導體製造業。請生成崗位基礎資料(僅必填欄位)。
已填寫的資料:{existingData}
需要生成的欄位positionCode, positionName
請用繁體中文,返回 JSON 格式,不要有任何其他文字。`
},
bag2: {
title: '標準版',
subtitle: '常用欄位',
prompt: `你是專業人資顧問,熟悉半導體製造業。請生成崗位基礎資料(標準版)。
已填寫的資料:{existingData}
需要生成的欄位positionCode, positionName, positionCategory, positionLevel, headcount
欄位說明:
- positionCode: 崗位編號(格式如 ENG-001
- positionName: 崗位名稱
- positionCategory: 崗位類別代碼01=技術職, 02=管理職, 03=業務職, 04=行政職)
- positionLevel: 崗位級別L1-L7
- headcount: 編制人數1-10
請用繁體中文,返回 JSON 格式,不要有任何其他文字。`
},
bag3: {
title: '詳細版',
subtitle: '所有欄位',
prompt: `你是專業人資顧問,熟悉半導體製造業的人資所有流程。請生成完整的崗位基礎資料。
已填寫的資料:{existingData}
需要生成的欄位positionCode, positionName, positionCategory, positionNature, headcount, positionLevel, positionDesc, positionRemark
欄位說明:
- positionCode: 崗位編號(格式如 ENG-001, MGR-002
- positionName: 崗位名稱
- positionCategory: 崗位類別代碼01=技術職, 02=管理職, 03=業務職, 04=行政職)
- positionNature: 崗位性質代碼FT=全職, PT=兼職, CT=約聘, IN=實習)
- headcount: 編制人數1-10之間的數字字串
- positionLevel: 崗位級別L1到L7
- positionDesc: 崗位描述(條列式,用換行分隔)
- positionRemark: 崗位備注(條列式,用換行分隔)
請用繁體中文,返回 JSON 格式,不要有任何其他文字。`
}
},
positionRecruit: {
bag1: {
title: '基本需求',
subtitle: '核心要求',
prompt: `請生成「{positionName}」的基本招聘要求。
已填寫的資料:{existingData}
需要生成的欄位minEducation, workExperience, jobType, jobTitle
請用繁體中文,返回 JSON 格式,不要有任何其他文字。`
},
bag2: {
title: '標準需求',
subtitle: '完整資訊',
prompt: `請生成「{positionName}」的標準招聘要求。
已填寫的資料:{existingData}
需要生成的欄位minEducation, salaryRange, workExperience, jobType, recruitPosition, jobTitle, positionReq, skillReq
欄位說明:
- minEducation: 最低學歷代碼HS=高中職, JC=專科, BA=大學, MA=碩士, PHD=博士)
- salaryRange: 薪酬范圍代碼A=30000以下, B=30000-50000, C=50000-80000, D=80000-120000, E=120000以上, N=面議)
- workExperience: 工作經驗年數0=不限, 1, 3, 5, 10
- jobType: 工作性質代碼FT=全職, PT=兼職, CT=約聘, DP=派遣)
- recruitPosition: 招聘職位代碼ENG=工程師, MGR=經理, AST=助理, OP=作業員, SAL=業務)
- positionReq: 崗位要求(條列式,用換行分隔)
- skillReq: 技能要求(條列式,用換行分隔)
請用繁體中文,返回 JSON 格式,不要有任何其他文字。`
},
bag3: {
title: '完整需求',
subtitle: '18個欄位',
prompt: `請生成「{positionName}」的完整招聘要求資料。
已填寫的資料:{existingData}
需要生成所有空白欄位。
欄位說明:
- minEducation: 最低學歷代碼HS=高中職, JC=專科, BA=大學, MA=碩士, PHD=博士)
- requiredGender: 性別要求代碼M=限男性, F=限女性, N=不限)
- salaryRange: 薪酬范圍代碼A=30000以下, B=30000-50000, C=50000-80000, D=80000-120000, E=120000以上, N=面議)
- workExperience: 工作經驗年數0=不限, 1, 3, 5, 10
- minAge: 最低年齡18-65
- maxAge: 最高年齡18-65
- jobType: 工作性質代碼FT=全職, PT=兼職, CT=約聘, DP=派遣)
- recruitPosition: 招聘職位代碼ENG=工程師, MGR=經理, AST=助理, OP=作業員, SAL=業務)
- jobTitle: 職位名稱
- jobDesc: 工作內容(條列式,用換行分隔)
- positionReq: 崗位要求(條列式,用換行分隔)
- titleReq: 職稱要求(條列式,用換行分隔)
- majorReq: 科系要求(多個科系用逗號分隔)
- skillReq: 技能要求(條列式,用換行分隔)
- langReq: 語言要求(條列式,用換行分隔)
- otherReq: 其他要求(條列式,用換行分隔)
- superiorPosition: 直屬主管職位
- recruitRemark: 招聘備注
請用繁體中文,返回 JSON 格式,不要有任何其他文字。`
}
},
jobBasic: {
bag1: {
title: '簡化版',
subtitle: '核心欄位',
prompt: `請生成職務基礎資料(簡化版)。
已填寫的資料:{existingData}
需要生成的欄位jobCode, jobName, jobCategoryCode
請用繁體中文,返回 JSON 格式,不要有任何其他文字。`
},
bag2: {
title: '標準版',
subtitle: '常用欄位',
prompt: `請生成職務基礎資料(標準版)。
已填寫的資料:{existingData}
需要生成的欄位jobCode, jobName, jobNameEn, jobCategoryCode, jobLevel, jobHeadcount
欄位說明:
- jobCode: 職務編號(格式如 J001
- jobName: 職務名稱(繁體中文)
- jobNameEn: 職務名稱英文
- jobCategoryCode: 職務類別代碼01=技術類, 02=管理類, 03=業務類, 04=行政類)
- jobLevel: 職務級別J1-J7
- jobHeadcount: 職務人數1-100
請用繁體中文,返回 JSON 格式,不要有任何其他文字。`
},
bag3: {
title: '完整版',
subtitle: '所有欄位',
prompt: `請生成完整的職務基礎資料。
已填寫的資料:{existingData}
需要生成所有空白欄位。
欄位說明:
- jobCode: 職務編號(格式如 J001
- jobName: 職務名稱(繁體中文)
- jobNameEn: 職務名稱英文
- jobCategoryCode: 職務類別代碼01=技術類, 02=管理類, 03=業務類, 04=行政類)
- jobLevel: 職務級別J1-J7
- jobHeadcount: 職務人數1-100
- jobEffectiveDate: 生效日期YYYY-MM-DD
- jobSortOrder: 排序順序1-999
- hasAttendanceBonus: 是否有全勤獎金true/false
- hasHousingAllowance: 是否有住宿津貼true/false
- jobRemark: 職務備註
請用繁體中文,返回 JSON 格式,不要有任何其他文字。`
}
},
deptFunction: {
bag1: {
title: '基本版',
subtitle: '核心資訊',
prompt: `請生成部門職責資料(基本版)。
已填寫的資料:{existingData}
需要生成的欄位deptFunctionCode, deptFunctionName, deptFunctionBU, deptFunctionDept
請用繁體中文,返回 JSON 格式,不要有任何其他文字。`
},
bag2: {
title: '標準版',
subtitle: '含職責描述',
prompt: `請生成部門職責資料(標準版)。
已填寫的資料:{existingData}
需要生成的欄位deptFunctionCode, deptFunctionName, deptFunctionBU, deptFunctionDept, deptManager, deptMission, deptCoreFunctions
欄位說明:
- deptFunctionCode: 部門職責編號(格式如 DF001
- deptFunctionName: 部門職責名稱
- deptFunctionBU: 事業單位代碼BU1-BU5
- deptFunctionDept: 部門代碼DEPT1-DEPT20
- deptManager: 部門主管
- deptMission: 部門使命(簡短描述)
- deptCoreFunctions: 核心職能(條列式,用換行分隔)
請用繁體中文,返回 JSON 格式,不要有任何其他文字。`
},
bag3: {
title: '完整版',
subtitle: '含KPI指標',
prompt: `請生成完整的部門職責資料。
已填寫的資料:{existingData}
需要生成所有空白欄位。
欄位說明:
- deptFunctionCode: 部門職責編號(格式如 DF001
- deptFunctionName: 部門職責名稱
- deptFunctionBU: 事業單位代碼BU1-BU5
- deptFunctionDept: 部門代碼DEPT1-DEPT20
- deptManager: 部門主管
- deptMission: 部門使命(簡短描述)
- deptVision: 部門願景(條列式,用換行分隔)
- deptCoreFunctions: 核心職能(條列式,用換行分隔)
- deptKPIs: KPI 指標(條列式,用換行分隔)
請用繁體中文,返回 JSON 格式,不要有任何其他文字。`
}
},
jobDesc: {
bag1: {
title: '基本版',
subtitle: '核心資訊',
prompt: `請生成崗位描述資料(基本版)。
已填寫的資料:{existingData}
需要生成的欄位positionName, department, positionPurpose
請用繁體中文,返回 JSON 格式,不要有任何其他文字。`
},
bag2: {
title: '標準版',
subtitle: '含職責說明',
prompt: `請生成崗位描述資料(標準版)。
已填寫的資料:{existingData}
需要生成的欄位positionName, department, directSupervisor, positionPurpose, mainResponsibilities, education, basicSkills
欄位說明:
- positionName: 崗位名稱
- department: 所屬部門
- directSupervisor: 直屬主管
- positionPurpose: 崗位目的(簡短描述)
- mainResponsibilities: 主要職責(條列式,用換行分隔)
- education: 學歷要求
- basicSkills: 基本技能(條列式,用換行分隔)
請用繁體中文,返回 JSON 格式,不要有任何其他文字。`
},
bag3: {
title: '完整版',
subtitle: '33個欄位',
prompt: `請生成完整的崗位描述資料。
已填寫的資料:{existingData}
需要生成所有空白欄位。
包含以下區塊:
1. 基本資訊empNo, empName, positionCode, versionDate
2. 崗位資訊positionName, department, positionEffectiveDate, directSupervisor, positionGradeJob, reportTo, directReports, workLocation, empAttribute
3. 職責與目的positionPurpose, mainResponsibilities
4. 任職要求education, basicSkills, professionalKnowledge, workExperienceReq, otherRequirements
請用繁體中文,返回 JSON 格式,不要有任何其他文字。`
}
}
};
// ==================== LocalStorage 管理 ====================
/**
* 獲取模組的 prompts優先使用自定義否則使用預設
* @param {string} module - 模組名稱
* @returns {Object} - 包含三個 bag 的 prompt 物件
*/
function getModulePrompts(module) {
try {
const saved = localStorage.getItem('aiPrompts');
const prompts = saved ? JSON.parse(saved) : {};
// 如果沒有保存的 prompts使用預設值
if (!prompts[module]) {
return DEFAULT_PROMPTS[module] || {};
}
return prompts[module];
} catch (e) {
console.error('讀取 prompts 失敗:', e);
return DEFAULT_PROMPTS[module] || {};
}
}
/**
* 保存模組的 prompts
* @param {string} module - 模組名稱
* @param {number} bagNumber - 錦囊編號 (1-3)
* @param {Object} bagData - 包含 title, subtitle, prompt 的物件
*/
function saveModulePrompt(module, bagNumber, bagData) {
try {
const saved = localStorage.getItem('aiPrompts');
const prompts = saved ? JSON.parse(saved) : {};
if (!prompts[module]) {
prompts[module] = {};
}
prompts[module][`bag${bagNumber}`] = bagData;
localStorage.setItem('aiPrompts', JSON.stringify(prompts));
return true;
} catch (e) {
console.error('保存 prompt 失敗:', e);
return false;
}
}
/**
* 初始化所有模組的預設 prompts如果尚未設定
*/
function initializeDefaultPrompts() {
const saved = localStorage.getItem('aiPrompts');
if (!saved) {
localStorage.setItem('aiPrompts', JSON.stringify(DEFAULT_PROMPTS));
}
}
// ==================== 執行 AI 錦囊 ====================
/**
* 執行 AI 錦囊
* @param {string} module - 模組名稱
* @param {number} bagNumber - 錦囊編號 (1-3)
*/
export async function executeAIBag(module, bagNumber) {
const bagElement = document.querySelector(`.ai-bag[data-module="${module}"][data-bag="${bagNumber}"]`);
if (!bagElement) return;
// 防止重複點擊
if (bagElement.classList.contains('loading')) return;
try {
// 顯示載入狀態
bagElement.classList.add('loading');
const icon = bagElement.querySelector('.bag-icon');
const originalIcon = icon.textContent;
icon.innerHTML = '<div class="spinner"></div>';
// 獲取當前表單資料
let existingData = {};
if (module === 'positionBasic' || module === 'positionRecruit') {
const positionData = getPositionFormData();
existingData = module === 'positionBasic' ? positionData.basicInfo : positionData.recruitInfo;
} else if (module === 'jobBasic') {
existingData = getJobFormData();
} else if (module === 'jobDesc') {
existingData = getJobDescFormData();
} else if (module === 'deptFunction') {
existingData = getDeptFunctionFormData();
}
// 獲取 prompt 模板
const prompts = getModulePrompts(module);
const bagPrompt = prompts[`bag${bagNumber}`];
if (!bagPrompt || !bagPrompt.prompt) {
throw new Error('Prompt 模板不存在');
}
// 替換 prompt 中的變數
let finalPrompt = bagPrompt.prompt
.replace('{existingData}', JSON.stringify(existingData, null, 2))
.replace('{positionName}', existingData.positionName || '此崗位');
// 調用 AI API
const result = await callClaudeAPI(finalPrompt);
// 填充表單
fillFormWithAIResult(module, result);
showToast('✨ AI 生成成功!');
} catch (error) {
console.error('AI 錦囊執行失敗:', error);
showToast('❌ AI 生成失敗: ' + error.message);
} finally {
// 恢復正常狀態
bagElement.classList.remove('loading');
const icon = bagElement.querySelector('.bag-icon');
icon.textContent = '🎁';
}
}
/**
* 根據 AI 結果填充表單
* @param {string} module - 模組名稱
* @param {Object} result - AI 返回的 JSON 結果
*/
function fillFormWithAIResult(module, result) {
if (module === 'positionBasic') {
// 崗位基礎資料 - 基礎資料頁籤
Object.entries(result).forEach(([key, value]) => {
fillIfEmpty(key, value);
});
} else if (module === 'positionRecruit') {
// 崗位基礎資料 - 招聘要求頁籤
Object.entries(result).forEach(([key, value]) => {
fillIfEmpty(key, value);
});
} else if (module === 'jobBasic') {
// 職務基礎資料
Object.entries(result).forEach(([key, value]) => {
if (key === 'hasAttendanceBonus' || key === 'hasHousingAllowance') {
const checkbox = document.getElementById(key);
if (checkbox) checkbox.checked = value;
} else {
fillIfEmpty(key, value);
}
});
} else if (module === 'deptFunction') {
// 部門職責
Object.entries(result).forEach(([key, value]) => {
fillIfEmpty('df_' + key, value);
});
} else if (module === 'jobDesc') {
// 崗位描述
if (result.basicInfo) {
Object.entries(result.basicInfo).forEach(([key, value]) => {
fillIfEmpty('jd_' + key, value);
});
}
if (result.positionInfo) {
Object.entries(result.positionInfo).forEach(([key, value]) => {
fillIfEmpty('jd_' + key, value);
});
}
if (result.responsibilities) {
Object.entries(result.responsibilities).forEach(([key, value]) => {
fillIfEmpty('jd_' + key, value);
});
}
if (result.requirements) {
Object.entries(result.requirements).forEach(([key, value]) => {
fillIfEmpty('jd_' + key, value);
});
}
}
}
// ==================== 編輯 Prompt ====================
/**
* 編輯錦囊 Prompt
* @param {Event} event - 點擊事件
* @param {string} module - 模組名稱
* @param {number} bagNumber - 錦囊編號 (1-3)
*/
export function editBagPrompt(event, module, bagNumber) {
event.stopPropagation(); // 防止觸發父元素的 click 事件
const prompts = getModulePrompts(module);
const bagPrompt = prompts[`bag${bagNumber}`];
if (!bagPrompt) {
showToast('❌ Prompt 不存在');
return;
}
// 顯示編輯對話框
showPromptEditModal(module, bagNumber, bagPrompt);
}
/**
* 顯示 Prompt 編輯對話框
* @param {string} module - 模組名稱
* @param {number} bagNumber - 錦囊編號 (1-3)
* @param {Object} bagData - 包含 title, subtitle, prompt 的物件
*/
function showPromptEditModal(module, bagNumber, bagData) {
const modal = document.getElementById('promptEditModal');
if (!modal) {
console.error('找不到編輯對話框');
return;
}
// 填充對話框內容
document.getElementById('promptModalTitle').textContent = `編輯錦囊 ${bagNumber} - ${bagData.title}`;
document.getElementById('promptTitle').value = bagData.title || '';
document.getElementById('promptSubtitle').value = bagData.subtitle || '';
document.getElementById('promptContent').value = bagData.prompt || '';
// 保存當前編輯的模組和錦囊編號
modal.dataset.module = module;
modal.dataset.bagNumber = bagNumber;
// 顯示對話框
modal.classList.add('show');
}
/**
* 保存編輯的 Prompt
*/
export function savePromptEdit() {
const modal = document.getElementById('promptEditModal');
const module = modal.dataset.module;
const bagNumber = parseInt(modal.dataset.bagNumber);
const title = document.getElementById('promptTitle').value.trim();
const subtitle = document.getElementById('promptSubtitle').value.trim();
const prompt = document.getElementById('promptContent').value.trim();
if (!title || !prompt) {
showToast('⚠️ 標題和 Prompt 內容不能為空');
return;
}
// 保存到 LocalStorage
const success = saveModulePrompt(module, bagNumber, { title, subtitle, prompt });
if (success) {
// 更新頁面上的錦囊標題
const bagElement = document.querySelector(`.ai-bag[data-module="${module}"][data-bag="${bagNumber}"]`);
if (bagElement) {
const titleElement = bagElement.querySelector('.bag-title');
const subtitleElement = bagElement.querySelector('.bag-subtitle');
if (titleElement) titleElement.textContent = title;
if (subtitleElement) {
if (subtitle) {
if (!subtitleElement.classList) {
const newSubtitle = document.createElement('div');
newSubtitle.className = 'bag-subtitle';
newSubtitle.textContent = subtitle;
bagElement.querySelector('.bag-title').after(newSubtitle);
} else {
subtitleElement.textContent = subtitle;
}
}
}
}
closePromptEditModal();
showToast('✅ Prompt 已保存');
} else {
showToast('❌ 保存失敗');
}
}
/**
* 關閉編輯對話框
*/
export function closePromptEditModal() {
const modal = document.getElementById('promptEditModal');
if (modal) {
modal.classList.remove('show');
}
}
/**
* 重置為預設 Prompt
*/
export function resetToDefaultPrompt() {
const modal = document.getElementById('promptEditModal');
const module = modal.dataset.module;
const bagNumber = parseInt(modal.dataset.bagNumber);
const defaultPrompt = DEFAULT_PROMPTS[module]?.[`bag${bagNumber}`];
if (!defaultPrompt) {
showToast('❌ 找不到預設 Prompt');
return;
}
if (confirm('確定要重置為預設 Prompt 嗎?')) {
document.getElementById('promptTitle').value = defaultPrompt.title || '';
document.getElementById('promptSubtitle').value = defaultPrompt.subtitle || '';
document.getElementById('promptContent').value = defaultPrompt.prompt || '';
showToast('✅ 已重置為預設值');
}
}
// ==================== 初始化 ====================
/**
* 初始化錦囊標題
*/
export function initializeBagTitles() {
document.querySelectorAll('.ai-bag').forEach(bag => {
const module = bag.dataset.module;
const bagNumber = parseInt(bag.dataset.bag);
const prompts = getModulePrompts(module);
const bagPrompt = prompts[`bag${bagNumber}`];
if (bagPrompt) {
const titleElement = bag.querySelector('.bag-title');
const subtitleElement = bag.querySelector('.bag-subtitle');
if (titleElement) titleElement.textContent = bagPrompt.title || `錦囊${bagNumber}`;
if (subtitleElement && bagPrompt.subtitle) {
subtitleElement.textContent = bagPrompt.subtitle;
}
}
});
}
// 頁面載入時初始化
document.addEventListener('DOMContentLoaded', () => {
initializeDefaultPrompts();
initializeBagTitles();
});
// ==================== 掛載到 window ====================
if (typeof window !== 'undefined') {
window.executeAIBag = executeAIBag;
window.editBagPrompt = editBagPrompt;
window.savePromptEdit = savePromptEdit;
window.closePromptEditModal = closePromptEditModal;
window.resetToDefaultPrompt = resetToDefaultPrompt;
}

View File

@@ -5,6 +5,7 @@
import { showToast } from './utils.js';
import { switchModule, updatePreview, updateCategoryName, updateNatureName, updateJobCategoryName } from './ui.js';
import { initializeBagTitles } from './ai-bags.js';
// ==================== 初始化 ====================
@@ -187,6 +188,9 @@ document.addEventListener('DOMContentLoaded', () => {
setupFormListeners();
setupKeyboardShortcuts();
// 初始化 AI 錦囊標題
initializeBagTitles();
// 初始化預覽
updatePreview();

View File

@@ -265,7 +265,87 @@ select {
fill: currentColor;
}
/* ==================== AI Button ==================== */
/* ==================== AI Bags (Three Fortune Bags) ==================== */
.ai-bags-container {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 16px;
margin-bottom: 24px;
}
.ai-bag {
position: relative;
padding: 20px;
background: linear-gradient(135deg, #9b59b6 0%, #8e44ad 100%);
border-radius: var(--radius);
cursor: pointer;
transition: all 0.3s ease;
box-shadow: 0 4px 15px rgba(155, 89, 182, 0.3);
text-align: center;
}
.ai-bag:hover {
transform: translateY(-4px);
box-shadow: 0 6px 20px rgba(155, 89, 182, 0.5);
}
.ai-bag:active {
transform: translateY(-2px);
}
.ai-bag .bag-icon {
font-size: 2rem;
margin-bottom: 8px;
}
.ai-bag .bag-title {
color: white;
font-weight: 600;
font-size: 0.95rem;
margin-bottom: 4px;
}
.ai-bag .bag-subtitle {
color: rgba(255, 255, 255, 0.8);
font-size: 0.75rem;
margin-top: 4px;
}
.ai-bag .bag-edit-btn {
position: absolute;
top: 8px;
right: 8px;
background: rgba(255, 255, 255, 0.2);
border: none;
border-radius: 4px;
padding: 4px 8px;
cursor: pointer;
font-size: 0.9rem;
transition: all 0.2s ease;
line-height: 1;
}
.ai-bag .bag-edit-btn:hover {
background: rgba(255, 255, 255, 0.3);
transform: scale(1.1);
}
.ai-bag.loading {
pointer-events: none;
opacity: 0.7;
}
.ai-bag .spinner {
width: 20px;
height: 20px;
border: 2px solid rgba(255, 255, 255, 0.3);
border-top-color: white;
border-radius: 50%;
animation: spin 0.8s linear infinite;
margin: 8px auto;
}
/* ==================== Old AI Button (Deprecated) ==================== */
.ai-generate-btn {
display: flex;
align-items: center;

263
三個錦囊設計.md Normal file
View File

@@ -0,0 +1,263 @@
# 三個錦囊功能設計
## 功能概述
將原本的單一 "I'm feeling lucky" 按鈕改為三個可自定義的 AI 生成按鈕("三個錦囊")。
## UI 設計
### 視覺效果
```
┌─────────────────────────────────────────────────────────┐
│ 🎁 錦囊一 🎁 錦囊二 🎁 錦囊三 │
│ [標題] [標題] [標題] │
│ ⚙️ 編輯 ⚙️ 編輯 ⚙️ 編輯 │
└─────────────────────────────────────────────────────────┘
```
### 互動流程
1. **點擊錦囊按鈕** → 使用預設或自定義的 prompt 調用 AI
2. **點擊編輯圖示** → 彈出對話框,顯示並可編輯當前 prompt
3. **Prompt 儲存** → 儲存至 localStorage按模組區分
## 資料結構
### LocalStorage 結構
```javascript
{
"prompts": {
"positionBasic": {
"bag1": { "title": "簡化版", "prompt": "..." },
"bag2": { "title": "標準版", "prompt": "..." },
"bag3": { "title": "詳細版", "prompt": "..." }
},
"positionRecruit": {
"bag1": { "title": "一般需求", "prompt": "..." },
"bag2": { "title": "高階需求", "prompt": "..." },
"bag3": { "title": "專業需求", "prompt": "..." }
},
"jobBasic": { ... },
"deptFunction": { ... },
"jobDesc": { ... }
}
}
```
## 預設 Prompt 模板
### 崗位基礎資料 - 基礎資料頁籤
#### 錦囊一:簡化版(僅填必填欄位)
```
你是專業人資顧問,熟悉半導體製造業。請生成崗位基礎資料(僅必填欄位)。
已填寫的資料:{existingData}
需要生成的欄位positionCode, positionName
請用繁體中文,返回 JSON 格式。
```
#### 錦囊二:標準版(常用欄位)
```
你是專業人資顧問,熟悉半導體製造業。請生成崗位基礎資料(標準版)。
已填寫的資料:{existingData}
需要生成的欄位positionCode, positionName, positionCategory, positionLevel, headcount
欄位說明:
- positionCode: 崗位編號(格式如 ENG-001
- positionName: 崗位名稱
- positionCategory: 崗位類別代碼01=技術職, 02=管理職, 03=業務職, 04=行政職)
- positionLevel: 崗位級別L1-L7
- headcount: 編制人數1-10
請用繁體中文,返回 JSON 格式。
```
#### 錦囊三:詳細版(所有欄位)
```
你是專業人資顧問,熟悉半導體製造業的人資所有流程。請生成完整的崗位基礎資料。
已填寫的資料:{existingData}
需要生成的欄位positionCode, positionName, positionCategory, positionNature, headcount, positionLevel, positionDesc, positionRemark
欄位說明:
- positionCode: 崗位編號(格式如 ENG-001, MGR-002
- positionName: 崗位名稱
- positionCategory: 崗位類別代碼01=技術職, 02=管理職, 03=業務職, 04=行政職)
- positionNature: 崗位性質代碼FT=全職, PT=兼職, CT=約聘, IN=實習)
- headcount: 編制人數1-10之間的數字字串
- positionLevel: 崗位級別L1到L7
- positionDesc: 崗位描述(條列式,用換行分隔)
- positionRemark: 崗位備注(條列式,用換行分隔)
請用繁體中文,返回 JSON 格式,不要有任何其他文字。
```
### 崗位基礎資料 - 招聘要求頁籤
#### 錦囊一:基本需求
```
請生成「{positionName}」的基本招聘要求。
已填寫的資料:{existingData}
需要生成的欄位minEducation, workExperience, jobType, jobTitle
請用繁體中文,返回 JSON 格式。
```
#### 錦囊二:標準需求
```
請生成「{positionName}」的標準招聘要求。
已填寫的資料:{existingData}
需要生成的欄位minEducation, salaryRange, workExperience, jobType, recruitPosition, jobTitle, positionReq, skillReq
欄位說明:
- minEducation: 最低學歷代碼HS=高中職, JC=專科, BA=大學, MA=碩士, PHD=博士)
- salaryRange: 薪酬范圍代碼A=30000以下, B=30000-50000, C=50000-80000, D=80000-120000, E=120000以上, N=面議)
- workExperience: 工作經驗年數0=不限, 1, 3, 5, 10
- jobType: 工作性質代碼FT=全職, PT=兼職, CT=約聘, DP=派遣)
- recruitPosition: 招聘職位代碼ENG=工程師, MGR=經理, AST=助理, OP=作業員, SAL=業務)
- positionReq: 崗位要求(條列式,用換行分隔)
- skillReq: 技能要求(條列式,用換行分隔)
請用繁體中文,返回 JSON 格式。
```
#### 錦囊三:完整需求
```
請生成「{positionName}」的完整招聘要求資料。
已填寫的資料:{existingData}
需要生成所有空白欄位。
(完整欄位說明同標準版,包含所有 18 個欄位)
請用繁體中文,返回 JSON 格式。
```
## 實作步驟
### 1. HTML 結構更新
將單一按鈕:
```html
<button type="button" class="ai-generate-btn" onclick="generatePositionBasic()">
<span>✨ I'm feeling lucky</span>
</button>
```
改為三個錦囊:
```html
<div class="ai-bags-container">
<div class="ai-bag" data-bag="1" onclick="executeAIBag('positionBasic', 1)">
<div class="bag-icon">🎁</div>
<div class="bag-title" id="bag1-title-positionBasic">錦囊一</div>
<button class="bag-edit-btn" onclick="editBagPrompt(event, 'positionBasic', 1)">⚙️</button>
</div>
<div class="ai-bag" data-bag="2" onclick="executeAIBag('positionBasic', 2)">
<div class="bag-icon">🎁</div>
<div class="bag-title" id="bag2-title-positionBasic">錦囊二</div>
<button class="bag-edit-btn" onclick="editBagPrompt(event, 'positionBasic', 2)">⚙️</button>
</div>
<div class="ai-bag" data-bag="3" onclick="executeAIBag('positionBasic', 3)">
<div class="bag-icon">🎁</div>
<div class="bag-title" id="bag3-title-positionBasic">錦囊三</div>
<button class="bag-edit-btn" onclick="editBagPrompt(event, 'positionBasic', 3)">⚙️</button>
</div>
</div>
```
### 2. CSS 樣式
```css
.ai-bags-container {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 16px;
margin-bottom: 24px;
}
.ai-bag {
position: relative;
padding: 20px;
background: linear-gradient(135deg, #9b59b6 0%, #8e44ad 100%);
border-radius: 8px;
cursor: pointer;
transition: all 0.3s ease;
box-shadow: 0 4px 15px rgba(155, 89, 182, 0.3);
}
.ai-bag:hover {
transform: translateY(-4px);
box-shadow: 0 6px 20px rgba(155, 89, 182, 0.5);
}
.ai-bag .bag-icon {
font-size: 2rem;
text-align: center;
margin-bottom: 8px;
}
.ai-bag .bag-title {
color: white;
font-weight: 600;
text-align: center;
font-size: 0.95rem;
}
.ai-bag .bag-edit-btn {
position: absolute;
top: 8px;
right: 8px;
background: rgba(255, 255, 255, 0.2);
border: none;
border-radius: 4px;
padding: 4px 8px;
cursor: pointer;
font-size: 0.9rem;
transition: all 0.2s ease;
}
.ai-bag .bag-edit-btn:hover {
background: rgba(255, 255, 255, 0.3);
transform: scale(1.1);
}
```
### 3. JavaScript 函式
#### 執行 AI 錦囊
```javascript
async function executeAIBag(module, bagNumber) {
const prompts = getModulePrompts(module);
const bagPrompt = prompts[`bag${bagNumber}`];
// 使用 prompt 調用 AI
await callClaudeAPI(bagPrompt.prompt);
}
```
#### 編輯 Prompt
```javascript
function editBagPrompt(event, module, bagNumber) {
event.stopPropagation();
// 顯示編輯對話框
showPromptEditModal(module, bagNumber);
}
```
## 優點
1.**靈活性高** - 用戶可自定義每個錦囊的用途
2.**效率提升** - 三個預設選項涵蓋不同需求場景
3.**學習友好** - 顯示 prompt 有助於理解 AI 運作
4.**可擴展** - 未來可增加更多錦囊或分享功能
## 待實作功能
- [ ] HTML 結構更新5 個模組)
- [ ] CSS 樣式新增
- [ ] JavaScript 函式實作
- [ ] LocalStorage 管理
- [ ] 編輯 Modal 對話框
- [ ] 預設 Prompt 初始化

176
更新欄位名稱.md Normal file
View File

@@ -0,0 +1,176 @@
# 系統表單欄位規範書 (Standardized Field Specifications)
## 1. 命名規範與前綴定義 (Naming Conventions)
為了確保系統一致性HTML 元素 ID 採用 `[模組前綴]_[標準欄位名]` 的命名方式。
| 模組名稱 | 模組前綴 (Prefix) | 說明 |
| :--- | :--- | :--- |
| **崗位管理 (Position)** | `pos_` | 崗位基礎資料 |
| **招聘條件 (Recruit)** | `rec_` | 崗位內的招聘頁籤 |
| **職務管理 (Job)** | `job_` | 全公司通用的職務定義 |
| **部門職責 (DeptFunc)** | `df_` | 部門功能與職責定義 |
| **崗位描述 (JobDesc)** | `jd_` | 最終的 JD 產出表單 |
---
## 2. 崗位基礎資料模組 (Position Module)
**表單代號**: `positionForm`
**資料表**: `Position`
### 2.1 基礎資料頁籤 (tab-position-basic)
| # | 欄位顯示名稱 | 標準化 HTML ID | 資料庫欄位名稱 | 類型 | 必填 | 預設 | 備註 |
|---|---|---|---|---|---|---|---|
| 1 | 事業體 | `pos_businessUnit` | `businessUnit` | select | 否 | - | SBU, MBU... (聯動L1) |
| 2 | 處級單位 | `pos_division` | `division` | select | 否 | - | (聯動L2) |
| 3 | 部級單位 | `pos_department` | `department` | select | 否 | - | (聯動L3) |
| 4 | 課級單位 | `pos_section` | `section` | text | 否 | - | - |
| 5 | **崗位編號** | `pos_code` | `positionCode` | text | **是** | - | 唯一識別碼 (PK) |
| 6 | 生效日期 | `pos_effectiveDate` | `effectiveDate` | date | 否 | Today | - |
| 7 | **崗位名稱** | `pos_name` | `positionName` | text | **是** | - | - |
| 8 | 崗位級別 | `pos_level` | `positionLevel` | select | 否 | - | L1-L7 |
| 9 | 崗位類別 | `pos_category` | `positionCategory` | select | 否 | - | onchange 觸發 |
| 10 | 崗位類別名稱 | `pos_categoryName` | `positionCategoryName` | text | 否 | - | readonly |
| 11 | 崗位性質 | `pos_type` | `positionType` | select | 否 | - | FT, PT, CT, IN |
| 12 | 崗位性質名稱 | `pos_typeName` | `positionTypeName` | text | 否 | - | readonly |
| 13 | 編制人數 | `pos_headcount` | `headcount` | number | 否 | 0 | min=0 |
| 14 | 崗位描述 | `pos_desc` | `description` | textarea | 否 | - | rows=6 |
| 15 | 崗位備注 | `pos_remark` | `remark` | textarea | 否 | - | rows=6 |
### 2.2 招聘要求資料頁籤 (tab-position-recruit)
| # | 欄位顯示名稱 | 標準化 HTML ID | 資料庫欄位名稱 | 類型 | 必填 | 預設 | 備註 |
|---|---|---|---|---|---|---|---|
| 1 | 最低學歷 | `rec_eduLevel` | `educationLevel` | select | 否 | - | HS, BA, MA, PHD |
| 2 | 要求性別 | `rec_gender` | `requiredGender` | select | 否 | Any | M, F, Any |
| 3 | 薪酬范圍 | `rec_salaryRange` | `salaryRange` | select | 否 | - | A-E, Negotiable |
| 4 | 工作經驗 | `rec_expYears` | `experienceYears` | select | 否 | - | 0, 1, 3, 5, 10+ |
| 5 | 最小年齡 | `rec_minAge` | `minAge` | number | 否 | - | min=18 |
| 6 | 最大年齡 | `rec_maxAge` | `maxAge` | number | 否 | - | max=65 |
| 7 | 工作性質 | `rec_jobType` | `jobType` | select | 否 | - | 招聘用性質分類 |
| 8 | 招聘職位 | `rec_position` | `recruitPosition` | select | 否 | - | ENG, MGR... |
| 9 | 職位名稱(對外) | `rec_jobTitle` | `jobTitle` | text | 否 | - | 對外招聘用Title |
| 10 | 上級崗位編號 | `rec_superiorCode` | `superiorPositionCode` | text | 否 | - | - |
| 11 | 職位描述(JD) | `rec_jobDesc` | `recruitJobDesc` | textarea | 否 | - | 招聘廣告用 |
| 12 | 崗位要求(Req) | `rec_positionReq` | `recruitRequirements` | textarea | 否 | - | 招聘廣告用 |
| 13 | 證照要求 | `rec_certReq` | `certRequirements` | select | 否 | - | - |
| 14 | 專業要求 | `rec_majorReq` | `majorRequirements` | text | 否 | - | Modal選擇 |
| 15 | 技能要求 | `rec_skillReq` | `skillRequirements` | text | 否 | - | Tags input |
| 16 | 語言要求 | `rec_langReq` | `langRequirements` | text | 否 | - | - |
| 17 | 其他要求 | `rec_otherReq` | `otherRequirements` | text | 否 | - | - |
| 18 | 招聘備注 | `rec_remark` | `recruitRemark` | textarea | 否 | - | - |
---
## 3. 職務基礎資料模組 (Job Module)
**表單代號**: `jobForm`
**資料表**: `Job`
| # | 欄位顯示名稱 | 標準化 HTML ID | 資料庫欄位名稱 | 類型 | 必填 | 預設 | 備註 |
|---|---|---|---|---|---|---|---|
| 1 | **職務類別編號** | `job_category` | `jobCategoryCode` | select | **是** | - | onchange 觸發 |
| 2 | 職務類別名稱 | `job_categoryName` | `jobCategoryName` | text | 否 | - | readonly |
| 3 | **職務編號** | `job_code` | `jobCode` | text | **是** | - | 唯一識別碼 |
| 4 | **職務名稱** | `job_name` | `jobName` | text | **是** | - | - |
| 5 | 職務英文 | `job_nameEn` | `jobNameEn` | text | 否 | - | - |
| 6 | 生效日期 | `job_effectiveDate`| `effectiveDate` | date | 否 | - | - |
| 7 | 職務層級 | `job_level` | `jobLevel` | text | 否 | *保密* | **敏感欄位** |
| 8 | 編制人數 | `job_headcount` | `headcount` | number | 否 | - | - |
| 9 | 排列順序 | `job_sortOrder` | `sortOrder` | number | 否 | - | - |
| 10 | 全勤獎金 | `job_hasAttBonus` | `hasAttendanceBonus` | checkbox| 否 | false| Toggle Switch |
| 11 | 住房補貼 | `job_hasHouseAllow`| `hasHousingAllowance` | checkbox| 否 | false| Toggle Switch |
| 12 | 職務備注 | `job_remark` | `remark` | textarea | 否 | - | - |
---
## 4. 部門職責模組 (DeptFunction Module)
**表單代號**: `deptFunctionForm`
**資料表**: `DeptFunction`
| # | 欄位顯示名稱 | 標準化 HTML ID | 資料庫欄位名稱 | 類型 | 必填 | 預設 | 備註 |
|---|---|---|---|---|---|---|---|
| 1 | **職責編號** | `df_code` | `dfCode` | text | **是** | - | DF-001 |
| 2 | **職責名稱** | `df_name` | `dfName` | text | **是** | - | - |
| 3 | **事業體** | `df_businessUnit` | `businessUnit` | select | **是** | - | (已合併重複欄位) |
| 4 | **處級單位** | `df_division` | `division` | select | **是** | - | - |
| 5 | **部級單位** | `df_department` | `department` | select | **是** | - | - |
| 6 | 課級單位 | `df_section` | `section` | text | 否 | - | - |
| 7 | **對應崗位** | `df_posTitle` | `positionTitle` | select | **是** | - | 關聯 Position |
| 8 | 崗位級別 | `df_posLevel` | `positionLevel` | select | 否 | - | 自動帶出或指定 |
| 9 | 部門主管職稱 | `df_managerTitle` | `managerTitle` | text | 否 | - | - |
| 10 | **生效日期** | `df_effectiveDate` | `effectiveDate` | date | **是** | - | - |
| 11 | 人數上限 | `df_headcountLimit`| `headcountLimit` | number | 否 | - | - |
| 12 | 狀態 | `df_status` | `status` | select | 否 | active | - |
| 13 | 部門使命 | `df_mission` | `mission` | textarea | 否 | - | - |
| 14 | 部門願景 | `df_vision` | `vision` | textarea | 否 | - | - |
| 15 | **核心職責** | `df_coreFunc` | `coreFunctions` | textarea | **是** | - | - |
| 16 | KPIs | `df_kpis` | `kpis` | textarea | 否 | - | - |
| 17 | 協作部門 | `df_collab` | `collaboration` | textarea | 否 | - | - |
| 18 | 備注 | `df_remark` | `remark` | textarea | 否 | - | - |
---
## 5. 崗位描述模組 (JobDescription Module)
**表單代號**: `jobDescForm`
**資料表**: `JobDescription` (部分欄位為 View)
### 5.1 基本信息 (Header)
| # | 欄位顯示名稱 | 標準化 HTML ID | 資料庫欄位名稱 | 類型 | 必填 | 備註 |
|---|---|---|---|---|---|---|
| 1 | 工號 | `jd_empNo` | `empNo` | text | 否 | Search Modal |
| 2 | 姓名 | `jd_empName` | `empName` | text | 否 | Readonly |
| 3 | 崗位代碼 | `jd_posCode` | `positionCode` | text | 否 | 關聯鍵 |
| 4 | 版本日期 | `jd_versionDate` | `versionDate` | date | 否 | - |
### 5.2 崗位資訊 (Position Info - Readonly/Derived)
| # | 欄位顯示名稱 | 標準化 HTML ID | 資料庫欄位名稱 | 類型 | 必填 | 備註 |
|---|---|---|---|---|---|---|
| 1 | 崗位名稱 | `jd_posName` | `positionName` | text | 否 | - |
| 2 | 事業體 | `jd_businessUnit` | `businessUnit` | select | 否 | - |
| 3 | 處級單位 | `jd_division` | `division` | select | 否 | - |
| 4 | 部級單位 | `jd_department` | `department` | select | 否 | - |
| 5 | 課級單位 | `jd_section` | `section` | text | 否 | - |
| 6 | 崗位級別 | `jd_posLevel` | `positionLevel` | select | 否 | - |
| 7 | 生效日期 | `jd_posEffDate` | `positionEffectiveDate`| date | 否 | - |
| 8 | **直接主管** | `jd_supervisor` | `directSupervisor` | text | 否 | - |
| 9 | 職等&職務 | `jd_gradeJob` | `positionGradeJob` | text | 否 | Modal |
| 10 | **匯報對象** | `jd_reportTo` | `reportTo` | text | 否 | Modal |
| 11 | 直接下屬 | `jd_directReports` | `directReports` | text | 否 | - |
| 12 | 任職地點 | `jd_location` | `workLocation` | select | 否 | - |
| 13 | 員工屬性 | `jd_empAttr` | `empAttribute` | select | 否 | - |
### 5.3 職責與要求 (Details)
| # | 欄位顯示名稱 | 標準化 HTML ID | 資料庫欄位名稱 | 類型 | 必填 | 備註 |
|---|---|---|---|---|---|---|
| 1 | 部門職責代碼 | `jd_dfCode` | `dfCode` | text | 否 | 關聯 DeptFunction |
| 2 | 崗位設置目的 | `jd_purpose` | `positionPurpose` | text | 否 | - |
| 3 | **主要職責** | `jd_mainResp` | `mainResponsibilities`| textarea | 否 | 編號清單 |
| 4 | 教育程度 | `jd_eduLevel` | `educationLevel` | text | 否 | - |
| 5 | 基本技能 | `jd_basicSkills` | `basicSkills` | textarea | 否 | - |
| 6 | 專業知識 | `jd_proKnowledge` | `professionalKnowledge` | textarea | 否 | - |
| 7 | 工作經驗 | `jd_expReq` | `experienceRequirements`| textarea | 否 | - |
| 8 | 其他要求 | `jd_otherReq` | `otherRequirements` | textarea | 否 | - |
---
## 6. 共用設定與資料字典
### 6.1 模態框 (Modals)
* `MajorModal` (專業要求)
* `EmpSearchModal` (員工搜索)
* `OrgSearchModal` (組織搜索)
* `GradeJobModal` (職等職務)
* `ReportToModal` (匯報對象)
### 6.2 特殊控件
* **Toggle Switch**: 用於所有布林值 (Boolean) 欄位。
* **Numbered Textarea**: 用於 `mainResponsibilities`,自動產生序號。
* **Cascading Selects**: 組織層級 (BU -> Div -> Dept -> Section) 統一使用標準聯動邏輯。

347
表單欄位清單.md Normal file
View File

@@ -0,0 +1,347 @@
# 表單欄位完整清單
## 1. 崗位基礎資料模組 - 基礎資料頁籤 (positionForm - tab-position-basic)
| # | 欄位顯示名稱 | HTML元素ID | 資料庫欄位名稱 | 資料類型 | 是否必填 | 預設值 | 備註 |
|---|-----------|---------|------------|------|------|------|------|
| 1 | 事業體 (Business Unit) | businessUnit | businessUnit | select | 否 | 空值 | SBU, MBU, HQBU, ITBU, HRBU, ACCBU |
| 2 | 處級單位 (Division) | division | division | select | 否 | 空值 | 根據事業體變動 |
| 3 | 部級單位 (Department) | department | department | select | 否 | 空值 | 根據處級單位變動 |
| 4 | 課級單位 (Section) | section | section | text | 否 | 選填 | - |
| 5 | 崗位編號 * | positionCode | positionCode | text | **是** | 空值 | 唯一識別碼,可更改 |
| 6 | 生效日期 | effectiveDate | effectiveDate | date | 否 | 2001-01-01 | - |
| 7 | 崗位名稱 * | positionName | positionName | text | **是** | 空值 | - |
| 8 | 崗位級別 | positionLevel | positionLevel | select | 否 | 空值 | L1-L7 (基層至總經理) |
| 9 | 崗位類別 | positionCategory | positionCategory | select | 否 | 空值 | 01, 02, 03, 04有onchange事件 |
| 10 | 崗位類別名稱 | positionCategoryName | positionCategoryName | text | 否 | 自動帶出 | readonly |
| 11 | 崗位性質 | positionNature | positionNature | select | 否 | 空值 | FT(全職), PT(兼職), CT(約聘), IN(實習)有onchange事件 |
| 12 | 崗位性質名稱 | positionNatureName | positionNatureName | text | 否 | 自動帶出 | readonly |
| 13 | 編制人數 | headcount | headcount | number | 否 | 空值 | min=0 |
| 14 | 崗位描述(條列式說明) | positionDesc | positionDesc | textarea | 否 | 空值 | rows=6有範例提示 |
| 15 | 崗位備注(條列式說明) | positionRemark | positionRemark | textarea | 否 | 空值 | rows=6有範例提示 |
---
## 2. 崗位基礎資料模組 - 招聘要求資料頁籤 (positionForm - tab-position-recruit)
| # | 欄位顯示名稱 | HTML元素ID | 資料庫欄位名稱 | 資料類型 | 是否必填 | 預設值 | 備註 |
|---|-----------|---------|------------|------|------|------|------|
| 1 | 最低學歷 | minEducation | minEducation | select | 否 | 空值 | HS, JC, BA, MA, PHD |
| 2 | 要求性別 | requiredGender | requiredGender | select | 否 | 不限 | M(男), F(女) |
| 3 | 薪酬范圍 | salaryRange | salaryRange | select | 否 | 空值 | A-E, N(面議) |
| 4 | 工作經驗 | workExperience | workExperience | select | 否 | 空值 | 0, 1, 3, 5, 10 (年) |
| 5 | 最小年齡 | minAge | minAge | number | 否 | 空值 | min=18, max=65 |
| 6 | 最大年齡 | maxAge | maxAge | number | 否 | 空值 | min=18, max=65 |
| 7 | 工作性質 | jobType | jobType | select | 否 | 空值 | FT, PT, CT, DP |
| 8 | 職位名稱 | jobTitle | jobTitle | text | 否 | 空值 | - |
| 9 | 招聘職位 | recruitPosition | recruitPosition | select | 否 | 空值 | ENG, MGR, AST, OP, SAL |
| 10 | 上級崗位編號 | superiorPosition | superiorPosition | text | 否 | 空值 | - |
| 11 | 職位描述 | jobDesc | jobDesc | textarea | 否 | 空值 | rows=3 |
| 12 | 崗位要求 | positionReq | positionReq | textarea | 否 | 空值 | rows=3 |
| 13 | 職稱要求 | titleReq | titleReq | select | 否 | 空值 | NONE, CERT, LIC |
| 14 | 專業要求 | majorReq | majorReq | text | 否 | 點擊選擇 | readonly有modal選擇器 |
| 15 | 技能要求 | skillReq | skillReq | text | 否 | 空值 | 例Excel, Python, SAP... |
| 16 | 語言要求 | langReq | langReq | text | 否 | 空值 | 例英文中級、日文N2... |
| 17 | 其他要求 | otherReq | otherReq | text | 否 | 空值 | - |
| 18 | 備注說明 | recruitRemark | recruitRemark | textarea | 否 | 空值 | rows=4 |
---
## 3. 職務基礎資料模組 (jobForm - tab-job-basic)
| # | 欄位顯示名稱 | HTML元素ID | 資料庫欄位名稱 | 資料類型 | 是否必填 | 預設值 | 備註 |
|---|-----------|---------|------------|------|------|------|------|
| 1 | 職務類別編號 * | jobCategoryCode | jobCategoryCode | select | **是** | 空值 | MGR, TECH, SALE, ADMIN, RD, PROD有onchange事件 |
| 2 | 職務類別名稱 | jobCategoryName | jobCategoryName | text | 否 | 自動帶出 | readonly |
| 3 | 職務編號 * | jobCode | jobCode | text | **是** | 空值 | 可更改職務編號 |
| 4 | 職務名稱 * | jobName | jobName | text | **是** | 空值 | - |
| 5 | 職務英文 | jobNameEn | jobNameEn | text | 否 | 空值 | - |
| 6 | 生效日期 | jobEffectiveDate | jobEffectiveDate | date | 否 | 空值 | - |
| 7 | 編制人數 | jobHeadcount | jobHeadcount | number | 否 | 空值 | min=0 |
| 8 | 排列順序 | jobSortOrder | jobSortOrder | number | 否 | 空值 | min=0 |
| 9 | 備注說明 | jobRemark | jobRemark | textarea | 否 | 空值 | rows=4 |
| 10 | 職務層級 | jobLevel | jobLevel | text | 否 | 如:*保密* | 敏感信息欄位 |
| 11 | 是否有全勤 | hasAttendanceBonus | hasAttendanceBonus | checkbox | 否 | 否 | toggle-switch 控件 |
| 12 | 是否住房補貼 | hasHousingAllowance | hasHousingAllowance | checkbox | 否 | 否 | toggle-switch 控件 |
---
## 4. 部門職責模組 (deptFunctionForm)
| # | 欄位顯示名稱 | HTML元素ID | 資料庫欄位名稱 | 資料類型 | 是否必填 | 預設值 | 備註 |
|---|-----------|---------|------------|------|------|------|------|
| 1 | 部門職責編號 * | deptFunctionCode | deptFunctionCode | text | **是** | 空值 | 例如: DF-001 |
| 2 | 部門職責名稱 * | deptFunctionName | deptFunctionName | text | **是** | 空值 | 例如: 軟體研發部職責 |
| 3 | 事業體 (Business Unit) * (第1個) | deptFunctionBU | deptFunctionBU | select | **是** | 空值 | SBU, MBU, HQBU, ITBU, HRBU, ACCBU |
| 4 | 事業體 (Business Unit) * (第2個) | deptFunc_businessUnit | businessUnit | select | **是** | 空值 | 聯動選擇 |
| 5 | 處級單位 (Division) * | deptFunc_division | division | select | **是** | 空值 | 聯動選擇 |
| 6 | 部級單位 (Department) * | deptFunc_department | department | select | **是** | 空值 | 聯動選擇 |
| 7 | 課級單位 (Section) | deptFunc_section | section | text | 否 | 選填 | - |
| 8 | 崗位名稱 * | deptFunc_positionTitle | positionTitle | select | **是** | 空值 | 根據部級單位變動 |
| 9 | 崗位級別 | deptFunc_positionLevel | positionLevel | select | 否 | 空值 | E, M, S, D, VP, C |
| 10 | 部門主管職稱 | deptManager | deptManager | text | 否 | 例如: 部門經理 | - |
| 11 | 生效日期 * | deptFunctionEffectiveDate | deptFunctionEffectiveDate | date | **是** | 空值 | - |
| 12 | 部門人數上限 | deptHeadcount | deptHeadcount | number | 否 | 例如: 50 | min=1 |
| 13 | 部門狀態 | deptStatus | deptStatus | select | 否 | active | active, inactive, planning |
| 14 | 部門使命 (Mission) | deptMission | deptMission | textarea | 否 | 空值 | rows=3有範例提示 |
| 15 | 部門願景 (Vision) | deptVision | deptVision | textarea | 否 | 空值 | rows=3有範例提示 |
| 16 | 核心職責 (Core Functions) * | deptCoreFunctions | deptCoreFunctions | textarea | **是** | 空值 | rows=6有範例提示 |
| 17 | 關鍵績效指標 (KPIs) | deptKPIs | deptKPIs | textarea | 否 | 空值 | rows=4有範例提示 |
| 18 | 協作部門 | deptCollaboration | deptCollaboration | textarea | 否 | 空值 | rows=3有範例提示 |
| 19 | 備注 | deptFunctionRemark | deptFunctionRemark | textarea | 否 | 空值 | rows=3 |
---
## 5. 崗位描述模組 (jobDescForm - tab-jobdesc-basic)
### 5.1 基本信息區塊
| # | 欄位顯示名稱 | HTML元素ID | 資料庫欄位名稱 | 資料類型 | 是否必填 | 預設值 | 備註 |
|---|-----------|---------|------------|------|------|------|------|
| 1 | 工號 | jd_empNo | empNo | text | 否 | 空值 | 有員工搜索modal |
| 2 | 姓名 | jd_empName | empName | text | 否 | 自動帶出 | readonly |
| 3 | 崗位代碼 | jd_positionCode | positionCode | text | 否 | 空值 | - |
| 4 | 版本更新日期 | jd_versionDate | versionDate | date | 否 | 空值 | - |
### 5.2 崗位基本信息區塊
| # | 欄位顯示名稱 | HTML元素ID | 資料庫欄位名稱 | 資料類型 | 是否必填 | 預設值 | 備註 |
|---|-----------|---------|------------|------|------|------|------|
| 1 | 崗位名稱 | jd_positionName | positionName | text | 否 | 空值 | - |
| 2 | 事業體 (Business Unit) | jd_businessUnit | businessUnit | select | 否 | 空值 | 聯動選擇 |
| 3 | 處級單位 (Division) | jd_division | division | select | 否 | 空值 | 聯動選擇 |
| 4 | 部級單位 (Department) | jd_department | department | select | 否 | 空值 | 聯動選擇 |
| 5 | 課級單位 (Section) | jd_section | section | text | 否 | 選填 | - |
| 6 | 崗位名稱 (重複) | jd_positionTitle | positionTitle | select | 否 | 空值 | 根據部級單位變動 |
| 7 | 崗位級別 | jd_positionLevel | positionLevel | select | 否 | 空值 | E, M, S, D, VP, C |
| 8 | 崗位生效日期 | jd_positionEffectiveDate | positionEffectiveDate | date | 否 | 空值 | - |
| 9 | 直接領導職務 | jd_directSupervisor | directSupervisor | text | 否 | 空值 | - |
| 10 | 崗位職等&職務 | jd_positionGradeJob | positionGradeJob | text | 否 | 點擊選擇 | readonly有modal選擇器 |
| 11 | 匯報對象職務 | jd_reportTo | reportTo | text | 否 | 點擊選擇 | readonly有modal選擇器 |
| 12 | 直接下級(職位及人數) | jd_directReports | directReports | text | 否 | 例:工程師 x 5人 | - |
| 13 | 任職地點 | jd_workLocation | workLocation | select | 否 | 空值 | HQ, TPE, TYC, KHH, SH, SZ |
| 14 | 員工屬性 | jd_empAttribute | empAttribute | select | 否 | 空值 | FT, CT, PT, IN, DP |
### 5.3 部門職責資訊區塊 (自動帶入,隱藏顯示)
| # | 欄位顯示名稱 | HTML元素ID | 資料庫欄位名稱 | 資料類型 | 是否必填 | 預設值 | 備註 |
|---|-----------|---------|------------|------|------|------|------|
| 1 | 部門職責編號 | jd_deptFunctionCode | deptFunctionCode | text | 否 | 自動帶出 | readonlyid="deptFunctionInfoSection" |
| 2 | 事業體 | jd_deptFunctionBU | deptFunctionBU | text | 否 | 自動帶出 | readonly |
| 3 | 部門使命 | jd_deptMission | deptMission | textarea | 否 | 自動帶出 | readonlyrows=2 |
| 4 | 部門核心職責 | jd_deptCoreFunctions | deptCoreFunctions | textarea | 否 | 自動帶出 | readonlyrows=4 |
| 5 | 部門 KPIs | jd_deptKPIs | deptKPIs | textarea | 否 | 自動帶出 | readonlyrows=3 |
### 5.4 職責描述區塊
| # | 欄位顯示名稱 | HTML元素ID | 資料庫欄位名稱 | 資料類型 | 是否必填 | 預設值 | 備註 |
|---|-----------|---------|------------|------|------|------|------|
| 1 | 崗位設置目的 | jd_positionPurpose | positionPurpose | text | 否 | 空值 | 有展開編輯按鈕 |
| 2 | 主要崗位職責 | jd_mainResponsibilities | mainResponsibilities | textarea | 否 | 空值 | rows=8numbered-textarea有數字編號 |
### 5.5 崗位要求區塊
| # | 欄位顯示名稱 | HTML元素ID | 資料庫欄位名稱 | 資料類型 | 是否必填 | 預設值 | 備註 |
|---|-----------|---------|------------|------|------|------|------|
| 1 | 教育程度 | jd_education | education | text | 否 | 例:大學本科及以上 | - |
| 2 | 基本技能 | jd_basicSkills | basicSkills | textarea | 否 | 空值 | rows=2有展開編輯按鈕 |
| 3 | 專業知識 | jd_professionalKnowledge | professionalKnowledge | textarea | 否 | 空值 | rows=2有展開編輯按鈕 |
| 4 | 工作經驗 | jd_workExperienceReq | workExperienceReq | textarea | 否 | 空值 | rows=2有展開編輯按鈕 |
| 5 | 其他 | jd_otherRequirements | otherRequirements | textarea | 否 | 空值 | rows=3 |
---
## 6. 崗位清單模組 (positionListTable)
### 6.1 表格列(只讀顯示)
| # | 列標題 | 資料鍵值 | 資料類型 | 可排序 | 備註 |
|---|------|--------|------|------|------|
| 1 | 崗位編號 | positionCode | text | 是 | 可點擊排序 |
| 2 | 崗位名稱 | positionName | text | 是 | 可點擊排序 |
| 3 | 崗位類別 | positionCategory | text | 是 | 可點擊排序 |
| 4 | 崗位性質 | positionNature | text | 是 | 可點擊排序 |
| 5 | 編制人數 | headcount | number | 是 | 可點擊排序 |
| 6 | 崗位等級 | positionLevel | text | 是 | 可點擊排序 |
| 7 | 生效日期 | effectiveDate | date | 是 | 可點擊排序 |
| 8 | 操作 | - | button | 否 | 編輯/刪除按鈕 |
---
## 數據結構對應關係
### Position (崗位基礎資料)
```javascript
{
id: string,
basicInfo: {
positionCode: string,
positionName: string,
positionCategory: string,
positionCategoryName: string,
positionNature: string,
positionNatureName: string,
headcount: number,
positionLevel: string,
effectiveDate: date,
positionDesc: string,
positionRemark: string
},
recruitInfo: {
minEducation: string,
requiredGender: string,
salaryRange: string,
workExperience: number,
minAge: number,
maxAge: number,
jobType: string,
recruitPosition: string,
jobTitle: string,
jobDesc: string,
positionReq: string,
titleReq: string,
majorReq: string,
skillReq: string,
langReq: string,
otherReq: string,
superiorPosition: string,
recruitRemark: string
},
createdAt: datetime,
updatedAt: datetime
}
```
### Job (職務基礎資料)
```javascript
{
id: string,
jobCategoryCode: string,
jobCategoryName: string,
jobCode: string,
jobName: string,
jobNameEn: string,
jobEffectiveDate: date,
jobHeadcount: number,
jobSortOrder: number,
jobRemark: string,
jobLevel: string,
hasAttendanceBonus: boolean,
hasHousingAllowance: boolean,
createdAt: datetime,
updatedAt: datetime
}
```
### DeptFunction (部門職責)
```javascript
{
deptFunctionCode: string,
deptFunctionName: string,
deptFunctionBU: string,
businessUnit: string,
division: string,
department: string,
section: string,
positionTitle: string,
positionLevel: string,
deptManager: string,
deptFunctionEffectiveDate: date,
deptHeadcount: number,
deptStatus: string,
deptMission: string,
deptVision: string,
deptCoreFunctions: string,
deptKPIs: string,
deptCollaboration: string,
deptFunctionRemark: string
}
```
### JobDescription (崗位描述)
```javascript
{
basicInfo: {
empNo: string,
empName: string,
positionCode: string,
versionDate: date
},
positionInfo: {
positionName: string,
businessUnit: string,
division: string,
department: string,
section: string,
positionTitle: string,
positionLevel: string,
positionEffectiveDate: date,
directSupervisor: string,
positionGradeJob: string,
reportTo: string,
directReports: string,
workLocation: string,
empAttribute: string,
deptFunctionCode: string,
deptFunctionBU: string,
deptMission: string,
deptCoreFunctions: string,
deptKPIs: string
},
responsibilities: {
positionPurpose: string,
mainResponsibilities: string
},
requirements: {
education: string,
basicSkills: string,
professionalKnowledge: string,
workExperienceReq: string,
otherRequirements: string
}
}
```
---
## 關鍵特性總結
### 聯動選擇Cascading Select
- **事業體 → 處級單位 → 部級單位 → 課級單位**
- 在崗位基礎資料、部門職責、崗位描述中都有此聯動
- 觸發事件onchange="onXXXBusinessUnitChange/onXXXDivisionChange" 等
### 自動帶出欄位 (readonly)
- 崗位類別名稱 (根據崗位類別自動帶出)
- 崗位性質名稱 (根據崗位性質自動帶出)
- 職務類別名稱 (根據職務類別編號自動帶出)
- 員工姓名 (根據工號自動帶出)
- 部門職責相關資訊 (自動帶入到崗位描述)
### Modal/模態框
- 專業要求選擇 (Major Modal - majorModal)
- 員工搜索 (EmpSearchModal)
- 組織搜索 (OrgSearchModal)
- 職等職務選擇 (GradeJobModal)
- 匯報對象選擇 (ReportToModal)
### CSV 操作
- 職務基礎資料支援: 下載範本、匯出、匯入
- 部門職責支援: 匯入、匯出
### 特殊欄位
- **敏感信息欄位**: jobLevel (職務層級) - 標記為 *保密*
- **布林值(Checkbox)**: hasAttendanceBonus, hasHousingAllowance
- **Toggle Switch**: 用於布林值的友善UI
### 表單驗證
- 必填欄位用 `<span class="required">*</span>` 標記
- HTML5 required 屬性用於部分欄位
---
## 變更紀錄
| 日期 | 版本 | 變更內容 |
|------|------|--------|
| 2025-12-05 | v1.0 | 初版完整表單欄位清單 |