卡片 CM-2561(FR-132 T-6.2),子母卡 CM-2559。只讀盤點,不改程式。FE 讀的是
feature/azure-iam當下的工作樹(含平行 session 未 commit 的 access/user-manage 改動)。
src/views、src/components 下有 <Button> 的 188 支,加上同目錄裡只用 @click/<button> 的 16 支、src/layout/AppLayout.vue 的 AI 小幫手浮鈕,以及順著 emit 追到的上層頁,共 239 支全部逐檔讀完(清單見文末「附錄:逐檔清單」,含 0 顆的檔與原因)。a1-capability-tree.md 附錄 A 查(API → 新名);附錄 C 豁免的寫「免判斷」。428 顆全部查得到,沒有附錄 A 查不到的 API。hasAny:
has(a) && has(b)。apiBase/mode/source_type 決定用哪個權限——判斷要由頁面傳 props 進來,元件內不可寫死。writeDisabled() 是授權唯讀反灰(FR-062),不是權限判斷,不算在這欄。| 模組 | 顆數 | 檔數 | 無判斷 | 已有但名不對 | 已有但粗 | 已有且正確 | 免判斷 | 要改(扣死碼) | 有專案層條件 |
|---|---|---|---|---|---|---|---|---|---|
| grc | 96 | 27 | 84 | 4 | 0 | 0 | 8 | 88 | 82 |
| oscal | 114 | 40 | 111 | 0 | 0 | 0 | 3 | 97 | 58 |
| flow | 11 | 2 | 1 | 4 | 0 | 6 | 0 | 5 | 8 |
| detection | 40 | 11 | 24 | 3 | 0 | 13 | 0 | 27 | 28 |
| evidence | 17 | 6 | 12 | 0 | 0 | 5 | 0 | 12 | 16 |
| survey | 39 | 8 | 39 | 0 | 0 | 0 | 0 | 39 | 4 |
| system | 23 | 10 | 19 | 0 | 0 | 3 | 1 | 19 | 3 |
| iam | 40 | 18 | 15 | 0 | 0 | 14 | 11 | 15 | 11 |
| platform | 12 | 6 | 9 | 0 | 1 | 0 | 2 | 10 | 2 |
| asset | 11 | 4 | 7 | 1 | 1 | 2 | 0 | 9 | 2 |
| ai | 12 | 10 | 10 | 0 | 0 | 2 | 0 | 10 | 1 |
| workspace | 13 | 5 | 6 | 1 | 4 | 0 | 2 | 11 | 7 |
| 合計 | 428 | 145 | 337 | 13 | 6 | 45 | 27 | 342 | 222 |
views/project/ProjectPlanningView.vue 的 SSP 匯出鈕歸 oscal、views/survey-v2/SurveyPreview.vue 的填答鈕歸 grc(通則 A)。同一支檔可能分在兩個模組。| 檔案 | 按鈕 | 今天用的 | 該用的(附錄 A) |
|---|---|---|---|
views/project/ProjectListView.vue :254 :259 :375 :414 |
批次刪除、新增、列內刪除、編輯儲存 | getUserPagePermissions('/project/projects') 比 CREATE/UPDATE/DELETE 字串(舊選單動作機制) |
grc.project.delete/.create/.update |
views/flow-template/FlowTemplateEditorView.vue :969 :979、FlowTemplateManageView.vue :450 :464 |
發布、撤銷發布 | flow.template.update |
flow.template.publish(編輯器 dirty 時先存檔,「發布」要 publish+update 兩者都有) |
views/scan-zone/ScanZoneManage.vue :136 :139 :158 |
產生建議、新增、刪除 | detection.scan-zone.update |
.create、.create、.delete(src/config/scanZone.js 常數只有 READ/UPDATE,要補) |
views/device/DeviceManage.vue :362、DeviceExportMenu.vue :39 |
匯入、匯出 | asset.device.create、asset.device.read |
asset.device.import、asset.device.export |
views/ai-quota/AiQuotaCeilingTable.vue :126 |
儲存額度上限 | 父層 isPlatformAdmin && isRoot 旗標 |
platform.ai-quota-ceiling.update |
views/feedback/system/SystemFeedbackForm.vue :455、SystemFeedbackManage.vue :266 :279 :282 :303 |
儲存、新增、編輯、刪除 | getUserPagePermissions 字串比對;Manage 的唯讀模式由 FeedbackManageTabView.vue:69 的「選單能力點只有一個」推出 |
workspace.feedback.create/.update/.delete(改時連 FeedbackManageTabView.vue:69 一起改) |
前提照母卡:T-6.1(單一來源 has())已合、CM-2556 補權限 migration 已套 DEV。數字是「要改」顆數。
| 棒 | 範圍 | 顆數 | 檔數 | 說明 |
|---|---|---|---|---|
| T-6.3 | grc 專案內頁(views/project 11 支+components/grc/project) |
38 | 12 | 輪次三頁(ApAuthoring/AuditReview/Poam)、規劃頁、稽核總覽、專案列表/建立。ProjectListView 換掉 getUserPagePermissions。專案層條件最多,逐顆 AND |
| T-6.4 | grc 任務與匯入(JobExecutionDrawer、FlowPhaseBanner、job-drawer、planning、JobImportDialog、AP/AR 匯入、SurveyPreview 填答、摘要報告三頁、資源庫列表的「啟動專案」) | 50 | 14 | 抽屜 19 顆集中一檔;FlowPhaseBanner 要和 BE 回的 user_can_advance AND;退回任務是「兩者都要」 |
| T-6.5 | oscal 專案 SSP 與共用 panel(components/grc 的 SSP/資源庫共用 panel、ssp-docx-import-v2、ssp-excel-import 活的那幾支、ProjectPlanningView 的 SSP 匯出) | 55 | 20 | 共用 panel 一律加 props 由頁面傳入,元件內不寫死名字;ComponentsLeveragedInventoryTab 16 顆集中 |
| T-6.6 | oscal 資源庫與合規框架(views/module_frame、views/compliance-framework、FrameworkArticleContent、WorkflowSetupEditor)+ flow 範本 5 顆 | 47 | 12 | 合規框架今天靠 isPlatformAdmin 遮,要決定是否保留成第二層(見待決 2);流程範本 update→publish |
| T-6.7 | detection+evidence+survey | 78 | 22 | 量大但都是管理頁、幾乎沒有專案層條件,機械性高;可再對半拆成 detection+evidence(39)與 survey(39) |
| T-6.8 | system+iam+platform+asset+ai+workspace | 74 | 37 | 設定頁多為一頁一兩顆,檔散但單純;ScanZone 以外的「名不對」大多在這棒(裝置、回饋、額度上限) |
合計 342 顆。每棒 38~78 顆;T-6.7、T-6.8 超過卡上「20~40 顆」的建議,建議各對半拆:T-6.7 拆 detection+evidence(39)與 survey(39),T-6.8 拆 system+platform+ai(39)與 iam+asset+workspace(35),共 8 棒。死碼按鈕不必改,建議另開一張清理卡刪檔(見下方)。
canEdit 要 planning 且 role==='manager';ProjectPlanningView 給 DocumentPoolPanel/ReferenceDocumentList/SspImportDialog 的 canEditSsp 只看 apStatus==='planning',非 manager 的參與者也看得到上傳/刪除/匯入。照 D-A6-4 只照抄、不修,但實作棒要知道。grc.job.update 沒有 grc.job-comment.create 的角色,任務已退回但留言 403。摘要報告匯出鈕先 PUT 再匯出——只有 export 的角色會先跳一次 403。這些「兩者都要」的按鈕已在備註標出(共 21 顆標了 hasAny/兩者都要,逐顆看備註)。PUT /survey/<uid>(update)或 POST /survey(create)送出,等於不用 survey.survey.publish 就能發布;帳號列表「刪除」走 POST /user/change-status(iam.user.update),iam.user.delete 守的 DELETE /user/<uid> 前端沒人呼叫。has() 時順手補授權唯讀(不補也不違反本系列,但同一行)。grc.job.update(通則 A),無法單獨遮檢測操作;這是附錄 A 的設計,不是漏。route.query.mode 分流。逐題作答走 socket /socket/fill-survey,不在 REST 表,只靠 canEdit。PUT /module-frame/item/xml/<uid> 附錄 A 只對到 oscal.resource-library.update,但這支編輯器也會從 ProjectPlanningView(專案 SSP 情境)開啟。照通則 C 專案情境該看專案權限,附錄 A 沒有另列——表上照附錄 A 寫,未自行改判。isPlatformAdmin:四頁今天只靠「作用租戶是否單層」遮寫入鈕;後端另有 require_platform_admin()。改成 has() 後要不要保留 isPlatformAdmin 當第二層?(附錄 A 原廠層通則是「兩道都過」,傾向保留,但這幾支 route 不在 platform 模組。)POST /user/batch-upload 每列會帶角色,附錄 A 只標 iam.user.create;單筆新增帶角色則另要 iam.user.assign。兩者不對稱,要不要也要求 assign?DELETE /ai-chatbot 在 src/stores/aiChatStore.js:57 呼叫,附錄 A 守 ai.assistant.read。觸發它的按鈕沒有在本次盤點的檔案裡找到,實作棒動 ChatBox 時順手確認。全專案無引用或無路由,按鈕照列在表內(備註標「死碼」),不算進「要改」:
components/grc/ssp/SspLeveragedSection.vuecomponents/grc/ssp-excel-import/ 的 SheetPreviewDevices、SheetPreviewInfoSystems、SheetPreviewLeveraged,以及只被它們用的 RowActionMenu → EntityPickerDialog、InlineCreateDialogcomponents/grc/ 的 DepartmentsPanel、RoundSwitcherBar、SspSystemForm、ModuleFramePartiesDialog、ModuleFrameSspResourcesDialog(連帶 ModuleFrameSspDevicesPanel、ModuleFrameSspInfoSystemsPanel 不會出現在畫面)components/grc/ssp-docx-import-v2/sections/ 的 MetadataSection、UnmatchedSectioncomponents/compliance-framework/import/ 三支 ListEditorviews/module_frame/ModuleFrameItems.vue(路由 module-frame-items 指到 ModuleFrameDetail)views/workflow/BpmnProcess/ 整包四支(外來樣板殘骸,依賴的 @/api、@/designer 目錄不存在)handleCloseRound、RoundApAuthoringView 的 saveControls/saveSubjects、RoundAuditReviewView 的 linkFindings、ModuleFrame 的 onCloneClick、ModuleFrameTemplateEditView 的 editMenuItems欄位:檔案:行號|按鈕(i18n key 或文字)|打的 API|權限(附錄 A 新名)|專案層條件(今天的既有條件,照抄)|現況|備註。檔案路徑相對 FE repo 根目錄。
| 檔案:行號 | 按鈕 | 打的 API | 權限 | 專案層條件 | 現況 | 備註 |
|---|---|---|---|---|---|---|
| src/components/grc/AuditControlRef.vue:548 | 證據檔案下載(icon) | GET /file/access-token/ |
免判斷(附錄 C) | — | 免判斷 | 僅 RoundAuditReviewView.vue 用 |
| src/components/grc/FlowPhaseBanner.vue:63 | 前往下一階段(主按鈕,非審核階段經 confirm 後送出;審核階段改開同檔 Dialog) | POST /project/ |
grc.audit.update | showAdvanceButton(stageInfo.current_stage 存在 && BE 回 user_can_advance===true);precondition 未過時改顯示 disabled 版 | 無判斷 | 共用(被 RoundAuditReviewView.vue、RoundApAuthoringView.vue、ProjectAuditorOverview.vue、RoundPoamView.vue、ProjectPlanningView.vue 用),判斷放 props 由頁面傳入 |
| src/components/grc/FlowPhaseBanner.vue:117 | 審核 Dialog 確定(核准/退回共用) | POST /project/ |
grc.audit.update | showAdvanceButton && isReviewStage(current_stage.code==='review');退回時 comment 必填 | 無判斷 | 入口為同檔 :43 退回鈕與 :63 主按鈕;共用(同上 5 頁),判斷放 props 由頁面傳入 |
| src/components/grc/FlowPhaseBanner.vue:160 | 返回上一階段 Dialog 確認 | POST /project/ |
grc.audit.update | isManager && canRollback(isManager=stageInfo.user_role==='manager';canRollback=current_stage.code ∈ ap_authoring/audit/poam) | 無判斷 | 入口為同檔 :36;共用(同上 5 頁),判斷放 props 由頁面傳入 |
| src/components/grc/JobExecutionDrawer.vue:2286 | 開始執行/重新執行(檢測工具) | POST /detection-tools/jobs/ |
grc.job.update | canEdit(= props.canExecute && status==='PROCESSING') | 無判斷 | 有排程列時改開同檔 :2947 確認框;共用(被 MyTasksView.vue、ProjectAuditorOverview.vue 用),判斷放 props 由頁面傳入 |
| src/components/grc/JobExecutionDrawer.vue:2325 | 替換源碼壓縮包 | POST /file/upload | 免判斷(附錄 C) | isUploadScanMode && canEdit | 免判斷 | 檔案 uid 之後隨 execute 送出;共用(同上) |
| src/components/grc/JobExecutionDrawer.vue:2336 | 上傳源碼壓縮包 | POST /file/upload | 免判斷(附錄 C) | isUploadScanMode && canEdit | 免判斷 | 同上;共用(同上) |
| src/components/grc/JobExecutionDrawer.vue:2382 | 整組取消 | POST /detection-tools/execution-groups/ |
grc.job.update | canEdit && canCancelGroup(group)(非 legacy、多列、有活著的筆) | 無判斷 | 共用(同上),判斷放 props 由頁面傳入 |
| src/components/grc/JobExecutionDrawer.vue:2392 | 整組刪除 | DELETE /detection-tools/execution-groups/ |
grc.job.update | canEdit && canDeleteGroup(group)(非 legacy、每一筆都 failed) | 無判斷 | 共用(同上) |
| src/components/grc/JobExecutionDrawer.vue:2516 | 立即開始(排程中的組) | POST /detection-tools/execution-groups/ |
grc.job.update | canEdit && !history && canStartNowAssignment(group, exec)(exec.status==='scheduled') | 無判斷 | 共用(同上) |
| src/components/grc/JobExecutionDrawer.vue:2526 | 重跑此組 | POST /detection-tools/execution-groups/ |
grc.job.update | canEdit && !history && canRerunAssignment(group, exec)(failed/cancelled) | 無判斷 | 共用(同上) |
| src/components/grc/JobExecutionDrawer.vue:2539 | 取消執行(單組) | POST /detection-tools/execution-groups/ |
grc.job.update | canEdit && !history && canCancelAssignment(group, exec)(running/scheduled) | 無判斷 | 共用(同上) |
| src/components/grc/JobExecutionDrawer.vue:2546 | 取消執行(legacy 舊資料) | POST /detection-tools/jobs/ |
grc.job.update | canEdit && !history && group.is_legacy && exec.status === 'running' | 無判斷 | 共用(同上) |
| src/components/grc/JobExecutionDrawer.vue:2555 | 下載報告(單份) | GET /file/access-token/ |
免判斷(附錄 C) | — | 免判斷 | 多份時改開詳情視窗;共用(同上) |
| src/components/grc/JobExecutionDrawer.vue:2567 | 刪除失敗執行紀錄(icon) | DELETE /detection-tools/executions/ |
grc.job.update | canEdit && canDeleteExecution(exec)(status==='failed') | 無判斷 | 共用(同上) |
| src/components/grc/JobExecutionDrawer.vue:2608 | 證據檔案上傳(瀏覽檔案;含 :2588 FileUpload 拖放區) | POST /job-evidences(multipart) | grc.job-evidence.create | canEdit | 無判斷 | 共用(同上),判斷放 props 由頁面傳入 |
| src/components/grc/JobExecutionDrawer.vue:2660 | 證據檔案下載(icon) | GET /file/access-token/ |
免判斷(附錄 C) | — | 免判斷 | 共用(同上) |
| src/components/grc/JobExecutionDrawer.vue:2661 | 刪除證據檔案(icon) | DELETE /job-evidence/ |
grc.job-evidence.delete | canEdit | 無判斷 | 共用(同上) |
| src/components/grc/JobExecutionDrawer.vue:2715 | 新增證據連結(inline 表單送出) | POST /job-evidence | grc.job-evidence.create | !evidenceLoading && canEdit && showAddLink | 無判斷 | 入口為同檔 :2680「新增連結」切換鈕(條件 !evidenceLoading && canEdit);共用(同上) |
| src/components/grc/JobExecutionDrawer.vue:2762 | 刪除證據連結(icon) | DELETE /job-evidence/ |
grc.job-evidence.delete | canEdit | 無判斷 | 共用(同上) |
| src/components/grc/JobExecutionDrawer.vue:2787 | 儲存證據連結(inline 編輯) | PUT /job-evidence/ |
grc.job-evidence.update | canEdit(進入編輯的 :2757 鈕條件) | 無判斷 | 共用(同上) |
| src/components/grc/JobExecutionDrawer.vue:2842 | 刪除留言(icon) | DELETE /grc/job/comment/ |
grc.job-comment.delete | canComment && comment.author.id === userInfo?.uid | 無判斷 | 共用(同上) |
| src/components/grc/JobExecutionDrawer.vue:2862 | 儲存留言(inline 編輯) | PUT /grc/job/comment/ |
grc.job-comment.update | canComment && comment.author.id === userInfo?.uid(canComment=canEdit || props.canReturn) | 無判斷 | 入口為同檔 :2837 編輯鈕;共用(同上) |
| src/components/grc/JobExecutionDrawer.vue:2877 | 送出留言 | POST /grc/job-executions/ |
grc.job-comment.create | canComment(canEdit || props.canReturn) | 無判斷 | 共用(同上) |
| src/components/grc/JobExecutionDrawer.vue:2893 | 退回任務(footer) | POST /flow-engine/task/revert/ |
grc.job.update、grc.job-comment.create | canRevert((props.canExecute || props.canReturn) && BE 回 can_revert_job) | 無判斷 | 入口鈕;送出在 JobActionCommentDialog.vue;需 hasAny(實際兩支都會打,缺 comment.create 會只做一半);共用(同上) |
| src/components/grc/JobExecutionDrawer.vue:2899 | 完成任務(footer) | POST /flow-engine/task/complete/ |
grc.job.update、grc.job-comment.create | canEdit;disabled 條件 !canCompleteJob(問卷全完成/至少一份附件) | 無判斷 | 入口鈕;送出在 JobActionCommentDialog.vue;需 hasAny(留言那支僅在有填時打);共用(同上) |
| src/components/grc/JobExecutionDrawer.vue:2947 | 排程確認框「確認執行」 | POST /detection-tools/jobs/ |
grc.job.update | canEdit(同 :2286 入口) | 無判斷 | 共用(同上),判斷放 props 由頁面傳入 |
| src/components/grc/JobExecutionDrawer.vue:3058 | 執行詳情視窗逐份下載報告 | GET /file/access-token/ |
免判斷(附錄 C) | — | 免判斷 | 共用(同上) |
| src/components/grc/JobImportDialog.vue:258 | 上傳任務匯入檔(步驟 1) | POST /grc/project/ |
grc.job.import | — | 無判斷 | 僅 ProjectPlanningView.vue 用;入口在 ProjectPlanningView.vue:242 批次維護選單(disabled: writeDisabled(!canEditSsp),canEditSsp=apStatus==='planning') |
| src/components/grc/JobImportDialog.vue:387 | 重新驗證 | POST /grc/project/ |
grc.job.import | — | 無判斷 | 僅 ProjectPlanningView.vue 用 |
| src/components/grc/JobImportDialog.vue:392 | 確認匯入 | POST /grc/project/ |
grc.job.import | — | 無判斷 | 僅 ProjectPlanningView.vue 用 |
| src/components/grc/ap-docx-import/ApDocxImportDialog.vue:565 | lang.ap_docx_import.choose_file(選擇檔案;選檔即上傳解析,含 :555 dropzone 點擊) |
POST /ap/ |
grc.audit.import | (宿主 RoundApAuthoringView 的 canImportDocx)roundStatus==='audit_planning' && (isAuditor || isProjectManager) && apUid | 無判斷 | 僅 RoundApAuthoringView.vue 用;dialog 本身只有 writeDisabled(授權唯讀);入口鈕在 RoundApAuthoringView.vue:664(不在本批) |
| src/components/grc/ap-docx-import/ApDocxImportDialog.vue:713 | lang.ap_docx_import.confirm_import(確認匯入) |
POST /ap-docx-import/ |
grc.audit.import | (宿主 canImportDocx)roundStatus==='audit_planning' && (isAuditor || isProjectManager) && apUid | 無判斷 | 僅 RoundApAuthoringView.vue 用;只有 writeDisabled;取消鈕會打 DELETE /ap-docx-import/ |
| src/components/grc/ar-import/ArImportDialog.vue:477 | lang.ar_import.choose_file(選擇檔案;選檔即上傳解析,含 :467 dropzone 點擊) |
POST /audit-round/ |
grc.audit.import | (宿主 RoundAuditReviewView 的 canImportAr)roundStatus==='auditing' && (isAuditor || isProjectManager) && stats.total > 0 | 無判斷 | 僅 RoundAuditReviewView.vue 用;dialog 本身只有 writeDisabled;入口鈕在 RoundAuditReviewView.vue:721(不在本批) |
| src/components/grc/ar-import/ArImportDialog.vue:636 | lang.ar_import.discard(捨棄並重來) |
DELETE /ar-import/ |
grc.audit.import | (宿主 canImportAr)roundStatus==='auditing' && (isAuditor || isProjectManager) && stats.total > 0 | 無判斷 | 僅 RoundAuditReviewView.vue 用;刪暫存解析結果後回 Step 1,與確認匯入同權限,隨 dialog 一起隱藏即可 |
| src/components/grc/ar-import/ArImportDialog.vue:640 | lang.ar_import.confirm_import(確認匯入) |
POST /ar-import/ |
grc.audit.import | (宿主 canImportAr)roundStatus==='auditing' && (isAuditor || isProjectManager) && stats.total > 0 | 無判斷 | 僅 RoundAuditReviewView.vue 用;只有 writeDisabled |
| src/components/grc/job-drawer/JobActionCommentDialog.vue:56 | 退回 Dialog 送出 | emit submit → 由 JobExecutionDrawer.vue handleReturnTask 處理:POST /flow-engine/task/revert/ |
grc.job.update、grc.job-comment.create | mode==='return'(入口條件見 JobExecutionDrawer.vue:2893 canRevert) | 無判斷 | 僅 JobExecutionDrawer.vue 用;需 hasAny |
| src/components/grc/job-drawer/JobActionCommentDialog.vue:60 | 完成 Dialog 送出 | emit submit → 由 JobExecutionDrawer.vue handleCompleteTask 處理:POST /flow-engine/task/complete/ |
grc.job.update、grc.job-comment.create | mode==='complete'(入口條件見 JobExecutionDrawer.vue:2899 canEdit) | 無判斷 | 僅 JobExecutionDrawer.vue 用;需 hasAny |
| src/components/grc/job-drawer/JobBlockedNotice.vue:81 | 強制開始(卡在合流閘道的任務) | emit force → 由 JobExecutionDrawer.vue handleForceStart/AoTaskForm→AoTaskSection→ProjectPlanningView.vue handleForceStartTask 處理:POST /grc/project/ |
grc.job.assign | blocked && canForce(canForce 由父傳:抽屜 = 父頁 isProjectManager;規劃頁 = ProjectPlanningView isProjectManager) | 無判斷 | 共用(被 JobExecutionDrawer.vue、AoTaskForm.vue 用),判斷放 props 由頁面傳入 |
| src/components/grc/planning/AoTaskForm.vue:654 | 刪除任務 | emit delete → AoTaskSection → ProjectPlanningView.vue deleteTask:DELETE /grc/project/ |
grc.job.delete | canEdit(父傳 ProjectPlanningView canEdit:AP 未關閉 && apStatus==='planning' && 專案/群組/控制項 manager);disabled 條件 deleteBlockedReason(mutationBlockedReason 或只剩一筆) | 無判斷 | 僅 AoTaskSection.vue 用(AoTaskSection 僅 ProjectPlanningView.vue 用) |
| src/components/grc/planning/AoTaskForm.vue:658 | 儲存任務 | emit save → AoTaskSection → ProjectPlanningView.vue saveTask:PUT /grc/project/ |
grc.job.update | canEdit(同上) | 無判斷 | 僅 AoTaskSection.vue 用;分派列增刪/複製/排程皆為本地狀態,隨此鈕一併送出 |
| src/components/grc/planning/AoTaskSection.vue:176 | 新增任務 | emit create → ProjectPlanningView.vue createTask:POST /grc/project/ |
grc.job.create | 無 v-if;disabled 條件 !!mutationBlockedReason(taskMutationBlockedReason:授權唯讀/AP 已關閉/apStatus!=='planning'/!canEdit) | 無判斷 | 僅 ProjectPlanningView.vue 用 |
| src/components/grc/planning/AoTaskSection.vue:205 | 新增任務(空狀態) | emit create → ProjectPlanningView.vue createTask:POST /grc/project/ |
grc.job.create | 同 :176(disabled 條件 !!mutationBlockedReason) | 無判斷 | 僅 ProjectPlanningView.vue 用 |
| src/components/grc/project/ProjectBasicInfoPanel.vue:139 | 專案基本資訊「儲存」 | PUT /grc/project/ |
grc.project.update | canEdit(ProjectSettingsView:本人在 participants 且 role==='manager';ProjectPlanningView 傳 isProjectManager,同義) |
無判斷 | 共用(被 ProjectSettingsView.vue、ProjectPlanningView.vue 用),判斷放 props 由頁面傳入 |
| src/components/grc/project/ProjectCloudIntegrationsPanel.vue:127 | Drive 資料夾「檢查並修復」確認鈕 | POST /integrations/google-drive/projects/ /verify-and-repair-folders |
grc.project.update | 入口鈕 :98 disabled=!canEdit(同上 manager 判斷) |
無判斷 | 共用(被 ProjectSettingsView.vue、ProjectPlanningView.vue〔v-if driveConnected〕用),判斷放 props 由頁面傳入;確認 dialog 內按鈕本身未再看 canEdit |
| src/components/grc/project/ProjectParticipantsPanel.vue:103 | 專案參與人員「儲存」 | PUT /grc/project/ |
grc.project.update | canEdit(同上) |
無判斷 | 共用(被 ProjectSettingsView.vue、ProjectPlanningView.vue 用),判斷放 props 由頁面傳入;ParticipantPicker 增刪改角色只改本地 v-model,靠此鈕送出 |
| src/views/module_frame/ModuleFrame.vue:199 | 列動作 › 啟動專案(Menu model,:1454 渲染) | POST /oscal-project/start | grc.project.create | rowData.status === 'published';disabled 另看 !rowData.enable |
無判斷 | 入口鈕;送出在 ProjectCreateView.vue(router push project-create 帶 profile_uid) |
| src/views/project-summary-report/ProjectSummaryReportForm.vue:319 | summary_report_manage.export_report(匯出 PDF) |
GET /project-summary-report/export/ |
grc.summary-report.export、grc.summary-report.update | — | 無判斷 | 一鍵打兩支:先 onSaveClick(false) 再匯出;PUT 失敗被 catch 吞掉仍續匯出(interceptor 仍會跳錯),故只有 export 也匯得出;僅 ProjectSummaryReportTabView 用 |
| src/views/project-summary-report/ProjectSummaryReportForm.vue:337 | common.save(儲存報告) |
PUT /project-summary-report/ |
grc.summary-report.update | — | 無判斷 | 只有 writeDisabled();僅 ProjectSummaryReportTabView 用 |
| src/views/project-summary-report/ProjectSummaryReportHistory.vue:145 | revert_this_version(還原此版本) |
POST /project-summary-report/history-revert | grc.summary-report.update | i != 0(非目前版本) | 無判斷 | 只有 writeDisabled();走 confirm.require |
| src/views/project-summary-report/ProjectSummaryReportManage.vue:243 | 列內 common.delete(刪除報告) |
DELETE /project-summary-report/ |
grc.summary-report.delete | — | 無判斷 | 只有 writeDisabled();走 confirm.require |
| src/views/project-summary-report/ProjectSummaryReportManage.vue:324 | common.save(新增報告 dialog 儲存) |
POST /project-summary-report | grc.summary-report.create | — | 無判斷 | 只有 writeDisabled()(授權唯讀)非能力點;入口鈕 :233 開同檔 dialog |
| src/views/project/MyTasksView.vue:649 | lang.my_tasks.batch_complete_btn(批次完成任務,dialog 確定) |
POST /grc/project/ |
grc.job.update | — | 無判斷 | 入口鈕 :427(同檔,selectedJobs.length > 0 才出現);可勾選列受 canComplete(PROCESSING 且有附件/問卷已完成)限制;依 project 分組逐支送;僅 MyTasksView 用 |
| src/views/project/ProjectApListView.vue:282 | lang.project_ap_list.btn_launch_reverify(發起覆核) |
POST /audit-round/ |
grc.audit.create | data.status === 'pending_reverify' && isProjectManager && !reverifyLaunched(data) |
無判斷 | isProjectManager=當前使用者在 project.participants 的 role==='manager';僅 ProjectApListView 用 |
| src/views/project/ProjectApListView.vue:331 | lang.project_ap_list.btn_new_round(建輪次 dialog 送出) |
POST /projects/ |
grc.audit.create | isProjectManager && ALLOW_MANUAL_ROUND_CREATE(入口 :210;入口另 disabled 於 hasActiveRound) |
無判斷 | 入口鈕 :215 同檔;ALLOW_MANUAL_ROUND_CREATE = false 常數寫死,目前整個入口隱藏 |
| src/views/project/ProjectAuditorOverview.vue:1152 | lang.project_planning.btn_start_task_execution(啟動證據蒐集任務) |
POST /grc/project/ |
grc.job.assign | isProjectManager && selectedApUid && selectedAp?.status === 'planning' | 無判斷 | 頁面(route view);另有 writeDisabled() 授權唯讀反灰;isProjectManager=當前使用者在 project.participants 中 role==='manager' |
| src/views/project/ProjectAuditorOverview.vue:1711 | lang.auditor_overview.btn_mark_reviewed/btn_reviewed(群組詳情內控制項列:標記已審閱/取消審閱) |
POST(標記)/DELETE(取消)/grc/project/ |
grc.audit.approve | canReviewControl(selectedGroup.id, ctrl.id)(輪次未關閉、非純 auditor、且在專案/群組/控制項層為 manager 或 reviewer);disabled:未審閱且控制項非 completed,或已審閱但非本人審閱 | 無判斷 | 頁面(route view);同一顆鈕依狀態切 POST/DELETE,同權限 |
| src/views/project/ProjectAuditorOverview.vue:1817 | AO 審閱 icon 鈕(群組詳情內 AO 列:標記已審閱/取消審閱) | POST/DELETE /grc/project/ |
grc.audit.approve | canReviewControl(selectedGroup.id, ctrl.id);disabled:未審閱且 AO 非 completed,或已審閱但非本人審閱 | 無判斷 | 頁面(route view);icon-only 鈕無 label |
| src/views/project/ProjectAuditorOverview.vue:1894 | lang.auditor_overview.btn_mark_reviewed/btn_reviewed(控制項詳情頁首:標記已審閱/取消審閱) |
POST/DELETE /grc/project/ |
grc.audit.approve | canReviewControl(selectionGroupId, selectedControl.id);disabled 同 1711 | 無判斷 | 頁面(route view) |
| src/views/project/ProjectAuditorOverview.vue:2009 | AO 審閱 icon 鈕(控制項詳情內 AO 列) | POST/DELETE /grc/project/ |
grc.audit.approve | canReviewControl(selectionGroupId, selectedControl.id);disabled 同 1817 | 無判斷 | 頁面(route view) |
| src/views/project/ProjectCreateView.vue:499 | lang.project_create.btn_create(建立專案) |
POST /oscal-project/start | grc.project.create | — | 無判斷 | 頁面入口在 ProjectListView :259(已用 pagePermission CREATE 擋);本頁送出鈕本身無判斷;僅 ProjectCreateView 用 |
| src/views/project/ProjectListView.vue:254 | lang.project_list.btn_batch_delete(批次刪除專案) |
POST /grc/projects/batch-delete | grc.project.delete | selectedProjects.length > 0 |
已有但名不對 | 用 getUserPagePermissions('/project/projects').includes('DELETE')(頁面節點 action 比對,對應舊名 project.delete) |
| src/views/project/ProjectListView.vue:259 | lang.project_list.btn_new_project(新增專案) |
POST /oscal-project/start | grc.project.create | — | 已有但名不對 | 入口鈕;送出在 ProjectCreateView.vue(路由跳頁)。用 pagePermission.includes('CREATE')(舊名 project.create) |
| src/views/project/ProjectListView.vue:364 | icon pi-google(同步到 Google Drive) | POST /integrations/google-drive/projects/ /init-folders |
grc.project.update | driveConnected && !data.drive_synced && (data.owner.id === userInfo.uid || isProjectManager(data)) |
無判斷 | isProjectManager(data)=該列 participants 有自己且 role==='manager';僅 ProjectListView 用 |
| src/views/project/ProjectListView.vue:375 | icon pi-trash(刪除專案,列內) | DELETE /grc/project/ |
grc.project.delete | data.owner.id === userInfo.uid || isProjectManager(data) |
已有但名不對 | 用 pagePermission.includes('DELETE')(舊名 project.delete) |
| src/views/project/ProjectListView.vue:414 | lang.common.save(編輯專案 dialog 儲存) |
PUT /grc/project/ |
grc.project.update | data.owner.id === userInfo.uid || isProjectManager(data)(掛在入口鉛筆 :370) |
已有但名不對 | 判斷掛在入口鉛筆鈕 :370 pagePermission.includes('UPDATE')(舊名 project.update),dialog 儲存鈕本身無判斷 |
| src/views/project/ProjectPlanningView.vue:241 | lang.project_planning.btn_import_jobs(批次維護選單:匯入任務 Excel) |
POST /grc/project/ |
grc.job.import | 選單鈕 v-if="sspUid";item disabled:writeDisabled(!canEditSsp) | 無判斷 | 入口鈕;送出在 JobImportDialog.vue;Menu model item |
| src/views/project/ProjectPlanningView.vue:247 | lang.project_planning.btn_export_jobs(批次維護選單:匯出任務 Excel) |
GET /grc/project/ |
grc.job.export | 選單鈕 v-if="sspUid" | 無判斷 | Menu model item;舊名 project.export(BE 今天有守、FE 沒判) |
| src/views/project/ProjectPlanningView.vue:1965 | lang.project_planning.btn_start_task_execution(啟動證據蒐集任務) |
POST /grc/project/ |
grc.job.assign | isProjectManager && canEditSsp(canEditSsp=輪次 apStatus==='planning') | 無判斷 | 頁面(route view);另有 writeDisabled() |
| src/views/project/ProjectPlanningView.vue:2462 | lang.task_setup.quick_config_confirm(快速配置 Dialog 確定:批次加指派人) |
PUT /grc/project/ |
grc.job.update | 入口鈕(1978)v-if="canEdit && canEditSsp"(canEdit=輪次未關閉且 planning,且為專案/群組/控制項層 manager) | 無判斷 | Dialog 在同檔;入口鈕 1978 另有 writeDisabled();語意是指派但打的是 job PUT |
| src/views/project/RoundApAuthoringView.vue:664 | lang.ap_docx_import.entry_button(匯入稽核計畫 docx) |
POST /ap/ |
grc.audit.import | canImportDocx(= !readonly && apUid;readonly = !(roundStatus==='audit_planning' && (isAuditor || isProjectManager))) |
無判斷 | 入口鈕;送出在 ApDocxImportDialog.vue(後續 GET/DELETE /ap-docx-import/ |
| src/views/project/RoundApAuthoringView.vue:685 | lang.ap_authoring.btn_save_tasks(儲存行程) |
PUT /ap/ |
grc.audit.update | !readonly(readonly 定義同上) |
無判斷 | 同區「新增行程」:683、刪除行程 :745 只改本地陣列,靠此鈕存檔;僅 RoundApAuthoringView 用 |
| src/views/project/RoundApAuthoringView.vue:871 | lang.common.confirm(新增稽核人員 dialog 確定) |
POST /ap/ |
grc.audit.update | !readonly(入口 :793 MultiSelect footer 以 writeDisabled(readonly) 反灰) |
無判斷 | 入口鈕 :793 同檔 |
| src/views/project/RoundAuditReviewView.vue:721 | lang.ar_import.entry_button(匯入稽核紀錄 xlsx) |
POST /audit-round/ |
grc.audit.import | canImportAr(= roundStatus==='auditing' && (isAuditor || isProjectManager) && stats.total > 0) |
無判斷 | 入口鈕;送出在 ArImportDialog.vue(後續 POST /ar-import/ |
| src/views/project/RoundAuditReviewView.vue:779 | lang.round_audit.save(儲存控制項判定) |
PUT /audit-round/ |
grc.audit.update、grc.audit.create | canEdit(= roundStatus==='auditing') |
無判斷 | 需 hasAny(not_met 且尚無風險時會新建風險=create);僅 RoundAuditReviewView 用 |
| src/views/project/RoundAuditReviewView.vue:811 | lang.round_audit.obs_update(更新觀察) |
PUT /audit-round/ |
grc.audit.update | canEdit(入口鉛筆 :819 掛 v-if="canEdit") |
無判斷 | 入口鉛筆 :819 只切編輯模式 |
| src/views/project/RoundAuditReviewView.vue:822 | icon pi-trash(刪除觀察) | DELETE /audit-round/ |
grc.audit.delete | canEdit |
無判斷 | |
| src/views/project/RoundAuditReviewView.vue:869 | lang.round_audit.obs_save(新增觀察) |
POST /audit-round/ |
grc.audit.create、grc.audit.update | canEdit |
無判斷 | 需 hasAny(建觀察後回寫 finding 掛 observation_uid);入口「+新增一筆觀察」:848 只展開表單 |
| src/views/project/RoundAuditReviewView.vue:890 | lang.round_audit.add_risk(新增風險) |
POST /audit-round/ |
grc.audit.create、grc.audit.update | canEdit |
無判斷 | 需 hasAny(選了來源控制項才打 PUT findings) |
| src/views/project/RoundAuditReviewView.vue:904 | icon pi-trash(刪除風險) | DELETE /audit-round/ |
grc.audit.delete | canEdit |
無判斷 | |
| src/views/project/RoundAuditReviewView.vue:922 | lang.round_audit.save(風險編輯儲存) |
PUT /audit-round/ |
grc.audit.update | canEdit(入口鉛筆 :903) |
無判斷 | 入口鉛筆 :903 只切編輯模式 |
| src/views/project/RoundPoamView.vue:558 | icon pi-trash(刪除改善計畫) | DELETE /audit-round/ |
grc.audit.delete | rem.lifecycle !== 'recommendation' && canEdit(canEdit = roundStatus==='remediation') |
無判斷 | 僅 RoundPoamView 用 |
| src/views/project/RoundPoamView.vue:577 | 里程碑狀態 chip(點擊循環切換狀態,即存) | PUT /audit-round/ |
grc.audit.update | canEdit(寫在 @click 守門與 class,非 v-if) |
無判斷 | chip 永遠顯示,canEdit 為 false 時點了不送 |
| src/views/project/RoundPoamView.vue:583 | icon pi-trash(刪除里程碑) | DELETE /audit-round/ |
grc.audit.delete | canEdit |
無判斷 | |
| src/views/project/RoundPoamView.vue:604 | lang.round_poam.btn_save_ms(里程碑編輯儲存) |
PUT /audit-round/ |
grc.audit.update | canEdit(入口鉛筆 :582) |
無判斷 | 入口鉛筆 :582 只切編輯模式 |
| src/views/project/RoundPoamView.vue:619 | lang.round_poam.add_milestone(新增里程碑,就地卡片送出) |
POST /audit-round/ |
grc.audit.create | canEdit(入口 :569) |
無判斷 | 入口 :569 只長出草稿卡 |
| src/views/project/RoundPoamView.vue:702 | lang.round_poam.add_plan/btn_save_ms(改善計畫 dialog 新增/編輯送出) |
POST /audit-round/ |
grc.audit.create | canEdit(入口 :628 新增 canEdit;:555 編輯 rem.lifecycle !== 'recommendation' && canEdit) |
無判斷 | 同一支 upsert API 兼新增與編輯(帶 uuid=編輯),編輯也落在 grc.audit.create |
| src/views/survey-v2/SurveyPreview.vue:1281 | 儲存草稿 | POST /project-survey/answers/ |
grc.job.update | mode 為 fill/review 且有 taskSurveyUid && taskSurvey.status !== 9 && (canEdit || canVerify) | 無判斷 | 填答頁(route survey-v2-fill,由 JobExecutionDrawer 開新分頁);canEdit/canVerify 由 task-assignees 的 is_approver+task_survey.status 推出;只有 writeDisabled |
| src/views/survey-v2/SurveyPreview.vue:1286 | 提交問卷 | POST /project-survey/answers/ |
grc.job.update | mode 為 fill/review && [0,1].includes(taskSurvey.status) && canEdit && taskSurvey.approval_required | 無判斷 | 同上 |
| src/views/survey-v2/SurveyPreview.vue:1291 | 提交回饋(審核退回) | POST /project-survey/answers/ |
grc.job.update | mode 為 fill/review && taskSurvey.status === 2 && canVerify | 無判斷 | canVerify=本人是 approver 且狀態待審 |
| src/views/survey-v2/SurveyPreview.vue:1296 | 提交補充 | POST /project-survey/answers/ |
grc.job.update | mode 為 fill/review && taskSurvey.status === 3 && canEdit | 無判斷 | |
| src/views/survey-v2/SurveyPreview.vue:1301 | 結案 | POST /project-survey/answers/ |
grc.job.update | mode 為 fill/review && ((taskSurvey.status === 2 && canVerify) || (!taskSurvey.approval_required && canEdit)) | 無判斷 | |
| src/views/survey-v2/SurveyPreview.vue:1706 | 檔案題上傳區(主題) | POST /file/upload | 免判斷(附錄 C) | !isDisabled(question.id)(fill 模式下=canEdit 且非他人編輯中) | 免判斷 | 上傳後答案經 socket update 即時寫入,非 REST;拖放(:1713)同路 |
| src/views/survey-v2/SurveyPreview.vue:1938 | 檔案題上傳區(子題) | POST /file/upload | 免判斷(附錄 C) | !isDisabled(sub.id) | 免判斷 | 同上 |
| src/views/survey-v2/SurveyPreview.vue:2080 | 歷史紀錄「還原此版本」 | POST /project-survey/history/revert | grc.job.update | 側欄入口(:1309)isInteractiveMode && histories.length > 0 && userRole !== 'reviewer' && taskSurvey.status !== 2;本鈕 i !== 0 && taskSurvey.status !== 9 | 無判斷 | |
| src/views/survey-v2/SurveyPreview.vue:2138 | 討論送出 | POST /survey-discussion | grc.job.update | 側欄入口(:1317)isInteractiveMode && taskSurveyUid | 無判斷 | InputText @keydown.enter 也會送出(:2137) |
| 檔案:行號 | 按鈕 | 打的 API | 權限 | 專案層條件 | 現況 | 備註 |
|---|---|---|---|---|---|---|
| src/components/compliance-framework/article/FrameworkArticleContent.vue:140 | 群組名稱 inline「儲存」 | emit update:group → 由 FrameworkVersionEditView 處理:PUT /oscal-catalog-group/ |
oscal.framework.update(編輯頁)/oscal.framework.import(匯入頁) | !readOnly && !isGroupDeleted(g.uid)(入口鉛筆鈕條件) |
無判斷 | 共用(被 FrameworkImportPage.vue 直接用、FrameworkVersionEditView.vue 經 FrameworkArticlePane 用),判斷放 props(既有 readOnly)由頁面傳入;編輯頁 readOnly 今天=!isPlatformAdmin || 非 draft |
| src/components/compliance-framework/article/FrameworkArticleContent.vue:156 | 刪除群組(icon) | emit delete:group → FrameworkVersionEditView:DELETE /oscal-catalog-group/ |
oscal.framework.update(編輯頁)/oscal.framework.import(匯入頁) | !readOnly && allowDelete && !isGroupDeleted(g.uid)(編輯頁 allowDelete=!readOnly && !tree.has_references) |
無判斷 | 共用,同上 |
| src/components/compliance-framework/article/FrameworkArticleContent.vue:186 | 控制項代碼/標題 inline「儲存」 | emit update:control → FrameworkVersionEditView:PUT /oscal-catalog-control/ |
oscal.framework.update(編輯頁)/oscal.framework.import(匯入頁) | !readOnly |
無判斷 | 共用,同上 |
| src/components/compliance-framework/article/FrameworkArticleContent.vue:197 | 刪除控制項(icon) | emit delete:control → FrameworkVersionEditView:DELETE /oscal-catalog-control/ |
oscal.framework.update(編輯頁)/oscal.framework.import(匯入頁) | !readOnly && allowDelete && !isControlDeleted(c.uid) |
無判斷 | 共用,同上 |
| src/components/compliance-framework/article/FrameworkArticleContent.vue:214 | 控制項描述 inline「儲存」 | emit update:control → FrameworkVersionEditView:PUT /oscal-catalog-control/ |
oscal.framework.update(編輯頁)/oscal.framework.import(匯入頁) | !readOnly |
無判斷 | 共用,同上 |
| src/components/compliance-framework/article/FrameworkArticleContent.vue:236 | 控制項 Guidance inline「儲存」 | emit update:control → FrameworkVersionEditView:PUT /oscal-catalog-control/ |
oscal.framework.update(編輯頁)/oscal.framework.import(匯入頁) | !readOnly |
無判斷 | 共用,同上 |
| src/components/compliance-framework/article/FrameworkArticleContent.vue:264 | 評估目標 inline「儲存」 | emit update:assessment → FrameworkVersionEditView:PUT /oscal-catalog-control-assessment/ |
oscal.framework.update(編輯頁)/oscal.framework.import(匯入頁) | !readOnly |
無判斷 | 共用,同上 |
| src/components/compliance-framework/article/FrameworkArticleContent.vue:276 | 刪除評估目標(icon) | emit delete:assessment → FrameworkVersionEditView:DELETE /oscal-catalog-control-assessment/ |
oscal.framework.update(編輯頁)/oscal.framework.import(匯入頁) | !readOnly && allowDelete && !isAssessmentDeleted(ao.uid) |
無判斷 | 共用,同上 |
| src/components/grc/ComponentsLeveragedInventoryTab.vue:890 | 元件列「刪除」(垃圾桶) | DELETE /ssp/<ssp_uid>/components/<item_uid> | oscal.ssp.update | !readonly(SspTabPanel 傳 !canEdit;canEdit=apStatus==='planning' && currentUserRole==='manager') |
無判斷 | 共用(被 SspTabPanel.vue[apiBase=ssp]、ModuleFrameTemplateEditView.vue[apiBase=module-frame] 用),判斷放 props 由頁面傳入;此列為 SSP 情境 |
| src/components/grc/ComponentsLeveragedInventoryTab.vue:890 | 元件列「刪除」(垃圾桶) | DELETE /module-frame/ |
oscal.resource-library.update | — | 無判斷 | 同上;此列為資源庫(MF)情境,MF 端不傳 readonly |
| src/components/grc/ComponentsLeveragedInventoryTab.vue:980 | 外部授權列「刪除」 | DELETE /ssp/<ssp_uid>/leveraged/<item_uid> | oscal.ssp.update | !readonly(同上 canEdit) |
無判斷 | 共用,SSP 情境 |
| src/components/grc/ComponentsLeveragedInventoryTab.vue:980 | 外部授權列「刪除」 | DELETE /module-frame/ |
oscal.resource-library.update | — | 無判斷 | 共用,MF 情境 |
| src/components/grc/ComponentsLeveragedInventoryTab.vue:1101 | 設備資產列「刪除」 | DELETE /ssp/<ssp_uid>/inventory-items/<item_uid> | oscal.ssp.update | !readonly(同上 canEdit) |
無判斷 | 共用,SSP 情境 |
| src/components/grc/ComponentsLeveragedInventoryTab.vue:1101 | 設備資產列「刪除」 | DELETE /module-frame/ |
oscal.resource-library.update | — | 無判斷 | 共用,MF 情境 |
| src/components/grc/ComponentsLeveragedInventoryTab.vue:1188 | 系統資產列「刪除」 | DELETE /ssp/<ssp_uid>/inventory-items/<item_uid> | oscal.ssp.update | !readonly(同上 canEdit) |
無判斷 | 共用,SSP 情境(系統資產與設備資產共用 inventory 端點) |
| src/components/grc/ComponentsLeveragedInventoryTab.vue:1188 | 系統資產列「刪除」 | DELETE /module-frame/ |
oscal.resource-library.update | — | 無判斷 | 共用,MF 情境 |
| src/components/grc/ComponentsLeveragedInventoryTab.vue:1278 | 元件 dialog「儲存」(新增/編輯) | POST /ssp/<ssp_uid>/components、PUT /ssp/<ssp_uid>/components/<item_uid> | oscal.ssp.update | 入口鈕 !readonly(同上 canEdit) |
無判斷 | 共用,SSP 情境;新增鈕 :813/編輯鈕 :886 開同檔 dialog |
| src/components/grc/ComponentsLeveragedInventoryTab.vue:1278 | 元件 dialog「儲存」(新增/編輯) | POST /module-frame/ |
oscal.resource-library.update | — | 無判斷 | 共用,MF 情境 |
| src/components/grc/ComponentsLeveragedInventoryTab.vue:1341 | 外部授權 dialog「儲存」 | POST /ssp/<ssp_uid>/leveraged、PUT /ssp/<ssp_uid>/leveraged/<item_uid> | oscal.ssp.update | 入口鈕 !readonly(同上 canEdit) |
無判斷 | 共用,SSP 情境;新增鈕 :907/編輯鈕 :976 開同檔 dialog |
| src/components/grc/ComponentsLeveragedInventoryTab.vue:1341 | 外部授權 dialog「儲存」 | POST /module-frame/ |
oscal.resource-library.update | — | 無判斷 | 共用,MF 情境 |
| src/components/grc/ComponentsLeveragedInventoryTab.vue:1437 | 設備資產 dialog「儲存」 | POST /ssp/<ssp_uid>/inventory-items、PUT /ssp/<ssp_uid>/inventory-items/<item_uid> | oscal.ssp.update | 入口鈕 !readonly(同上 canEdit) |
無判斷 | 共用,SSP 情境;新增鈕 :997/編輯鈕 :1097 開同檔 dialog |
| src/components/grc/ComponentsLeveragedInventoryTab.vue:1437 | 設備資產 dialog「儲存」 | POST /module-frame/ |
oscal.resource-library.update | — | 無判斷 | 共用,MF 情境 |
| src/components/grc/ComponentsLeveragedInventoryTab.vue:1533 | 系統資產 dialog「儲存」 | POST /ssp/<ssp_uid>/inventory-items、PUT /ssp/<ssp_uid>/inventory-items/<item_uid> | oscal.ssp.update | 入口鈕 !readonly(同上 canEdit) |
無判斷 | 共用,SSP 情境;新增鈕 :1118/編輯鈕 :1184 開同檔 dialog |
| src/components/grc/ComponentsLeveragedInventoryTab.vue:1533 | 系統資產 dialog「儲存」 | POST /module-frame/ |
oscal.resource-library.update | — | 無判斷 | 共用,MF 情境 |
| src/components/grc/DocumentLinkDialog.vue:343 | 「確認關聯」(程序書勾選送出) | POST /ssp/<ssp_uid>/control-implementation/ |
oscal.ssp.update | —(入口鈕在 ReferenceDocumentList,受 canEditSsp 控) | 無判斷 | 僅 ProjectPlanningView.vue 用 |
| src/components/grc/DocumentPoolPanel.vue:250 | 程序書池「上傳」 | POST /file/upload(豁免)+ POST /ssp/<ssp_uid>/document-pool | oscal.ssp.update | !readOnly(父層傳 !canEditSsp;canEditSsp=apStatus==='planning') |
無判斷 | 僅 ProjectPlanningView.vue 用(Tab+Dialog 兩個實例);FileUpload 拖拉區 :263 同一路徑 |
| src/components/grc/DocumentPoolPanel.vue:351 | 程序書「下載」 | GET /file/access-token/ |
免判斷(附錄 C) | — | 免判斷 | 僅 ProjectPlanningView.vue 用 |
| src/components/grc/DocumentPoolPanel.vue:359 | 程序書「刪除」 | DELETE /ssp/<ssp_uid>/document-pool/<doc_uid> | oscal.ssp.update | !readOnly(同上 canEditSsp) |
無判斷 | 僅 ProjectPlanningView.vue 用 |
| src/components/grc/DownloadSspBlankTemplateDialog.vue:131 | 「下載空白 SSP 匯入範本」 | GET /ssp-import-template?framework_version_uid=&mode=blank | oscal.resource-library.read | — | 無判斷 | 僅 ModuleFrame.vue 用(入口為列表頁 menu item :76) |
| src/components/grc/EditIncludeControlsDialog.vue:317 | 編輯納入控制項「儲存」 | PUT /module-frame/ |
oscal.resource-library.update | — | 無判斷 | 僅 ModuleFrameTemplateEditView.vue 用 |
| src/components/grc/ModuleFrameBasicInfoPanel.vue:198 | 「分享給子租戶」開關(InputSwitch) | PUT /module-frame/ |
oscal.resource-library.update | canShare(moduleFrame.tenant_id === currentTenant,資源所屬租戶才顯示) |
無判斷 | 共用(被 ModuleFrameBasicInfoDialog.vue〔ModuleFrame.vue 用〕、ModuleFrameTemplateEditView.vue 用),判斷放 props 由頁面傳入 |
| src/components/grc/ModuleFrameBasicInfoPanel.vue:211 | 基本資料「儲存」 | PUT /module-frame/ |
oscal.resource-library.update | — | 無判斷 | 共用(同上) |
| src/components/grc/ModuleFrameDocumentPoolPanel.vue:279 | 參考文件「上傳」 | POST /file/upload(豁免)+ POST /module-frame/ |
oscal.resource-library.update | — | 無判斷 | 僅 ModuleFrameTemplateEditView.vue 用(api-base=module-frame;ssp 分支無人使用);FileUpload 拖拉區 :291 同一路徑 |
| src/components/grc/ModuleFrameDocumentPoolPanel.vue:378 | 參考文件「下載」 | GET /file/access-token/ |
免判斷(附錄 C) | — | 免判斷 | 僅 ModuleFrameTemplateEditView.vue 用 |
| src/components/grc/ModuleFrameDocumentPoolPanel.vue:386 | 參考文件「刪除」 | DELETE /module-frame/ |
oscal.resource-library.update | — | 無判斷 | 僅 ModuleFrameTemplateEditView.vue 用 |
| src/components/grc/ModuleFramePartiesPanel.vue:440 | 責任單位/人員列「刪除」 | DELETE /ssp/<ssp_uid>/parties/<party_uid> | oscal.ssp.update | !readonly(SspTabPanel 傳 !canEdit;canEdit=apStatus==='planning' && currentUserRole==='manager') |
無判斷 | 共用(被 SspTabPanel.vue[ssp]、ModuleFrameTemplateEditView.vue[module-frame]、ModuleFramePartiesDialog.vue〔無人引用〕用),判斷放 props 由頁面傳入;SSP 情境 |
| src/components/grc/ModuleFramePartiesPanel.vue:440 | 責任單位/人員列「刪除」 | DELETE /module-frame/ |
oscal.resource-library.update | — | 無判斷 | 共用,MF 情境 |
| src/components/grc/ModuleFramePartiesPanel.vue:524 | 責任單位/人員 dialog「儲存」 | POST /ssp/<ssp_uid>/parties、PUT /ssp/<ssp_uid>/parties/<party_uid> | oscal.ssp.update | 入口鈕 !readonly(同上 canEdit) |
無判斷 | 共用,SSP 情境;新增鈕 :380/編輯鈕 :439 開同檔 dialog |
| src/components/grc/ModuleFramePartiesPanel.vue:524 | 責任單位/人員 dialog「儲存」 | POST /module-frame/ |
oscal.resource-library.update | — | 無判斷 | 共用,MF 情境 |
| src/components/grc/ModuleFrameSspDevicesPanel.vue:282 | 設備列「刪除」 | DELETE /module-frame/ |
oscal.resource-library.update | — | 無判斷 | 僅 ModuleFrameSspResourcesDialog.vue 用,而該 dialog 無人引用(死碼);ssp 分支對應 DELETE /ssp/<ssp_uid>/ssp-resources/items/<item_uid>=oscal.ssp.update,亦無人使用 |
| src/components/grc/ModuleFrameSspDevicesPanel.vue:328 | 設備 dialog「儲存」 | POST /module-frame/ |
oscal.resource-library.update | — | 無判斷 | 同上(死碼) |
| src/components/grc/ModuleFrameSspInfoSystemsPanel.vue:278 | 資訊系統列「刪除」 | DELETE /module-frame/ |
oscal.resource-library.update | — | 無判斷 | 同 DevicesPanel,僅掛在無人引用的 ModuleFrameSspResourcesDialog.vue(死碼) |
| src/components/grc/ModuleFrameSspInfoSystemsPanel.vue:328 | 資訊系統 dialog「儲存」 | POST /module-frame/ |
oscal.resource-library.update | — | 無判斷 | 同上(死碼) |
| src/components/grc/ModuleFrameTemplateImportDialog.vue:390 | import_upload_btn(上傳並驗證 Excel) |
POST /file/upload → POST /module-frame/ |
oscal.resource-library.update | — | 無判斷 | /file/upload 屬附錄 C 豁免,取後兩支(同權限);僅 ModuleFrameTemplateEditView.vue 用(入口在該檔 menu command:227) |
| src/components/grc/ModuleFrameTemplateImportDialog.vue:533 | import_btn_revalidate(重新驗證) |
POST /module-frame/ |
oscal.resource-library.update | hasEdited(有改過才可按) |
無判斷 | 只驗證不寫入,但 BE 以 update 守;僅 ModuleFrameTemplateEditView.vue 用 |
| src/components/grc/ModuleFrameTemplateImportDialog.vue:538 | import_btn_confirm_all(確認匯入) |
POST /module-frame/ |
oscal.resource-library.update | validItems.length > 0 && !hasEdited |
無判斷 | 既有 writeDisabled 是授權唯讀(license),非能力點;僅 ModuleFrameTemplateEditView.vue 用 |
| src/components/grc/ReferenceDocumentList.vue:187 | 「關聯程序書」 | emit link → 由 ProjectPlanningView.vue handleLinkRefDoc 開 DocumentLinkDialog → POST /ssp/<ssp_uid>/control-implementation/ |
oscal.ssp.update | linkMode && !readonly(ProjectPlanningView 傳 readonly=writeDisabled(!canEditSsp);canEditSsp=apStatus==='planning') |
無判斷 | 入口鈕;送出在 DocumentLinkDialog.vue。共用(被 ProjectPlanningView.vue、AuditControlRef.vue、ProjectAuditorOverview.vue 用;後兩者固定 readonly),判斷放 props 由頁面傳入 |
| src/components/grc/ReferenceDocumentList.vue:213 | 參考程序書「下載」 | GET /file/access-token/ |
免判斷(附錄 C) | — | 免判斷 | 共用(同上) |
| src/components/grc/ReferenceDocumentList.vue:221 | 「取消關聯」 | emit unlink → 由 ProjectPlanningView.vue handleUnlinkRefDoc 處理 → DELETE /ssp/<ssp_uid>.../document-mapping/<doc_uid>(AO 層 .../objective/ |
oscal.ssp.update | !readonly && linkMode(同上 canEditSsp) |
無判斷 | 共用(同上) |
| src/components/grc/SspImportDialog.vue:324 | 匯入實作描述「上傳」 | POST /ssp/<ssp_uid>/control-implementations/import | oscal.ssp.import | —(入口 menu item 在 ProjectPlanningView :226,disabled=writeDisabled(!canEditSsp)) |
無判斷 | 僅 ProjectPlanningView.vue 用 |
| src/components/grc/SspImportDialog.vue:450 | 「重新驗證」 | POST /ssp/<ssp_uid>/control-implementations/import/validate | oscal.ssp.import | —(同上) | 無判斷 | 僅 ProjectPlanningView.vue 用 |
| src/components/grc/SspImportDialog.vue:455 | 「確認匯入」 | POST /ssp/<ssp_uid>/control-implementations/import/confirm | oscal.ssp.import | —(同上) | 無判斷 | 僅 ProjectPlanningView.vue 用 |
| src/components/grc/ssp-docx-import-v2/SspDocxImportPage.vue:941 | lang.ssp_docx_import.confirm(確認匯入 (N)) |
create:POST /oscal/resource-libraries + POST /ssp-docx-import/<parse_uid>/confirm;update-mf/update-ssp:POST /ssp-docx-import/<parse_uid>/confirm | 依 props.mode(通則 C):update-ssp→oscal.ssp.import;update-mf→oscal.resource-library.update;create→oscal.resource-library.create | step===3 | 無判斷 | 共用(同上兩宿主),判斷放 props 由頁面傳入;create 模式打兩支寫入但同為 oscal.resource-library.create;只有 writeDisabled;所有 diff/sections 子元件的決策與編輯最終都經此鈕送出 |
| src/components/grc/ssp-docx-import-v2/SspDocxImportPage.vue:1023 | dropzone(lang.ssp_docx_import.dropzone_hint,點擊/拖放即上傳解析) |
POST /ssp-docx-imports/parse | 依 props.mode(通則 C):update-ssp→oscal.ssp.import;update-mf→oscal.resource-library.update;create→oscal.resource-library.create | — | 無判斷 | 共用(被 views/project/ImportDocxPage.vue〔mode=update-ssp〕、views/module_frame/ImportDocxPage.vue〔mode=create/update-mf〕用),判斷放 props 由頁面傳入;本身只有 canUpload(選版本)條件 |
| src/components/grc/ssp-excel-import/EntityPickerDialog.vue:143 | dialog common.confirm(選現有實體) |
emit pick → 由 RowActionMenu.vue onPick 寫 store → ImportExcelPreviewPage confirm 送出 |
oscal.ssp.import、oscal.resource-library.update、oscal.resource-library.create | — | 無判斷 | 依匯入來源擇一;僅 RowActionMenu.vue 用(死碼鏈) |
| src/components/grc/ssp-excel-import/InlineCreateDialog.vue:171 | dialog common.confirm(就地新建設備/系統/單位/使用者) |
emit save → 由 RowActionMenu.vue onCreate 寫 store.inlineCreates → ImportExcelPreviewPage confirm 送出(BE 確認時建實體) |
oscal.ssp.import、oscal.resource-library.update、oscal.resource-library.create | — | 無判斷 | 依匯入來源擇一;僅 RowActionMenu.vue 用(死碼鏈);若復活,確認時會順帶新建使用者/設備,可能需另判 iam/asset 權限 |
| src/components/grc/ssp-excel-import/RowActionMenu.vue:102 | Menu item skip_as_text/reset(script 55、61 行) |
本身不打 API:store.setDecision → 由 ImportExcelPreviewPage confirm 送 POST /ssp-excel-import/<parse_uid>/confirm | oscal.ssp.import、oscal.resource-library.update、oscal.resource-library.create | — | 無判斷 | 依匯入來源擇一;只被 SheetPreviewDevices/SheetPreviewInfoSystems 用(兩者皆死碼)→ 連帶死碼 |
| src/components/grc/ssp-excel-import/SheetPreviewComponents.vue:225 | 列內 pi-trash 移除/216 復原(元件清冊) |
本身不打 API:寫 store.removedRows → 由 ImportExcelPreviewPage.vue onConfirm 送 POST /ssp-excel-import/<parse_uid>/confirm | oscal.ssp.import、oscal.resource-library.update、oscal.resource-library.create | — | 無判斷 | 依匯入來源擇一(通則 C),非 hasAny;預覽暫存動作,整頁入口與確認鈕已需匯入權;僅 ImportExcelPreviewPage.vue 用 |
| src/components/grc/ssp-excel-import/SheetPreviewDevices.vue:137 | 列內 pi-trash 移除/114 復原(設備) |
同上(store → ImportExcelPreviewPage confirm) | oscal.ssp.import、oscal.resource-library.update、oscal.resource-library.create | — | 無判斷 | 死碼:ImportExcelPreviewPage 已不掛此元件,全專案無 import |
| src/components/grc/ssp-excel-import/SheetPreviewInfoSystems.vue:117 | 列內 pi-trash 移除/94 復原(資訊系統) |
同上(store → ImportExcelPreviewPage confirm) | oscal.ssp.import、oscal.resource-library.update、oscal.resource-library.create | — | 無判斷 | 死碼:全專案無 import |
| src/components/grc/ssp-excel-import/SheetPreviewInventoryItems.vue:226 | 列內 pi-trash 移除/217 復原(資產清冊) |
同上(store → ImportExcelPreviewPage confirm) | oscal.ssp.import、oscal.resource-library.update、oscal.resource-library.create | — | 無判斷 | 依匯入來源擇一;僅 ImportExcelPreviewPage.vue 用 |
| src/components/grc/ssp-excel-import/SheetPreviewLeveraged.vue:204 | 列內 pi-trash 移除/195 復原(外部利用服務) |
同上(store → ImportExcelPreviewPage confirm) | oscal.ssp.import、oscal.resource-library.update、oscal.resource-library.create | — | 無判斷 | 死碼:ImportExcelPreviewPage 已改用 SheetPreviewLeveragedAuth,全專案無 import |
| src/components/grc/ssp-excel-import/SheetPreviewLeveragedAuth.vue:205 | 列內 pi-trash 移除/196 復原(外部授權) |
同上(store → ImportExcelPreviewPage confirm) | oscal.ssp.import、oscal.resource-library.update、oscal.resource-library.create | — | 無判斷 | 依匯入來源擇一;僅 ImportExcelPreviewPage.vue 用 |
| src/components/grc/ssp-excel-import/SheetPreviewParties.vue:200 | 組織列 pi-trash 移除/191 復原 |
同上(store → ImportExcelPreviewPage confirm) | oscal.ssp.import、oscal.resource-library.update、oscal.resource-library.create | — | 無判斷 | 依匯入來源擇一;僅 ImportExcelPreviewPage.vue 用 |
| src/components/grc/ssp-excel-import/SheetPreviewParties.vue:324 | 人員列 pi-trash 移除/315 復原 |
同上(store → ImportExcelPreviewPage confirm) | oscal.ssp.import、oscal.resource-library.update、oscal.resource-library.create | — | 無判斷 | 依匯入來源擇一;僅 ImportExcelPreviewPage.vue 用 |
| src/components/grc/ssp-excel-import/SheetPreviewSystemAssets.vue:170 | 列內 pi-trash 移除/161 復原(系統資產) |
同上(store → ImportExcelPreviewPage confirm) | oscal.ssp.import、oscal.resource-library.update、oscal.resource-library.create | — | 無判斷 | 依匯入來源擇一;僅 ImportExcelPreviewPage.vue 用 |
| src/components/grc/ssp/SspBasicSection.vue:317 | common.save(儲存受評標的) |
PUT /ssp/ |
oscal.ssp.update、oscal.resource-library.update | !readonly(SspTabPanel 傳 !canEdit=apStatus==='planning' && currentUserRole==='manager';MF 頁不傳=永遠可見) |
無判斷 | 依 apiBase 擇一(ssp→oscal.ssp.update;module-frame→oscal.resource-library.update),非 hasAny;共用(被 SspTabPanel.vue、ModuleFrameTemplateEditView.vue 用),判斷放 props 由頁面傳入 |
| src/components/grc/ssp/SspLeveragedSection.vue:288 | 列內 pi-trash(刪除外部利用服務) |
DELETE /ssp/ |
oscal.ssp.update、oscal.resource-library.update | !readonly(操作欄 v-if) |
無判斷 | 依 apiBase 擇一;全專案無任何檔 import 此元件(死碼) |
| src/components/grc/ssp/SspLeveragedSection.vue:374 | dialog common.save(新增/編輯外部利用服務) |
POST /ssp/ |
oscal.ssp.update、oscal.resource-library.update | 入口「新增外部服務」235/列內編輯 287 皆 !readonly |
無判斷 | 依 apiBase 擇一;入口鈕在同檔不另列;死碼(無引用者) |
| src/components/grc/ssp/SspTabPanel.vue:315 | btn_download_ssp_import_template_filled(下載已填 SSP Excel) |
GET /ssp/<ssp_uid>/excel-template?mode=filled | oscal.ssp.export | —(僅 v-if="sspUid" 外層) |
無判斷 | 舊名 module-frame.read;非 manager 也看得到;僅 ProjectPlanningView.vue 用 |
| src/components/grc/ssp/SspTabPanel.vue:452 | dialog parse_button(解析檔案=上傳 SSP Excel) |
POST /ssp/<ssp_uid>/excel-import/upload | oscal.ssp.import | canEdit(入口鈕 322 v-if;canEdit=apStatus==='planning' && currentUserRole==='manager') |
無判斷 | 入口鈕 322 開同檔 dialog;成功後導 ImportExcelPreviewPage;僅 ProjectPlanningView.vue 用 |
| src/views/compliance-framework/ComplianceFrameworkManage.vue:412 | 批次刪除框架 | DELETE /oscal-framework/ |
oscal.framework.delete | selectedFrameworks.length > 0(published 自動剔除) |
無判斷 | 今用 isPlatformAdmin(租戶路徑判斷,非能力點);勾選欄 :423 同條件;僅 ComplianceFrameworkManage 用 |
| src/views/compliance-framework/ComplianceFrameworkManage.vue:476 | 列選單「刪除」(model 定義 :353) | DELETE /oscal-framework/ |
oscal.framework.delete | isDeletableFramework(data)(publish_status !== 'published') |
無判斷 | 今用 isPlatformAdmin;僅 ComplianceFrameworkManage 用 |
| src/views/compliance-framework/ComplianceFrameworkManage.vue:544 | 新增/編輯框架 Dialog「儲存」 | POST /oscal-framework(新增)/PUT /oscal-framework/ |
oscal.framework.create(新增)/oscal.framework.update(編輯) | — | 無判斷 | 依模式二擇一;入口「新增」:418 與選單「編輯」:348 今用 isPlatformAdmin;僅 ComplianceFrameworkManage 用 |
| src/views/compliance-framework/ComplianceFrameworkVersionManage.vue:600 | 批次刪除版本 | DELETE /oscal-framework-version/ |
oscal.framework.update | selectedVersions.length > 0(published 自動剔除) |
無判斷 | 今用 isPlatformAdmin;勾選欄 :612 同條件;僅 ComplianceFrameworkVersionManage 用 |
| src/views/compliance-framework/ComplianceFrameworkVersionManage.vue:607 | 匯入版本(入口鈕) | POST /oscal-framework-parse-jobs/parse | oscal.framework.import | — | 無判斷 | 入口鈕;送出在 FrameworkImportPage.vue(route 跳轉);今用 isPlatformAdmin |
| src/views/compliance-framework/ComplianceFrameworkVersionManage.vue:649 | 列選單「編輯內容」(model 定義 :465) | PUT/DELETE /oscal-catalog-group|control|control-assessment/ |
oscal.framework.update | — | 無判斷 | 入口鈕;送出在 FrameworkVersionEditView.vue(route 跳轉;該頁 readOnly 也看 isPlatformAdmin);今選單項以 isPlatformAdmin 決定是否出現 |
| src/views/compliance-framework/ComplianceFrameworkVersionManage.vue:649 | 列選單「刪除」(model 定義 :473) | DELETE /oscal-framework-version/ |
oscal.framework.update | isDeletableVersion(data)(publish_status !== 'published') |
無判斷 | 今用 isPlatformAdmin |
| src/views/compliance-framework/ComplianceFrameworkVersionManage.vue:649 | 列選單「OSCAL 下載」(model 定義 :488) | GET /oscal-framework-version/download/oscal/ |
oscal.framework.read | !data.children 時才出現(有 catalog 或葉節點) |
無判斷 | 下載類;權限是 read,與看頁同一顆,所有租戶可見 |
| src/views/compliance-framework/ComplianceFrameworkVersionManage.vue:698 | 新增/編輯版本 Dialog「儲存」 | POST /oscal-framework-version(新增)/PUT /oscal-framework-version/ |
oscal.framework.update | — | 無判斷 | 入口「新增版本」:606 與選單「編輯」:460 今用 isPlatformAdmin |
| src/views/compliance-framework/FrameworkImportPage.vue:477 | 捨棄草稿(Step 2) | DELETE /oscal-framework-parse-jobs/ |
oscal.framework.import | step === 2 |
無判斷 | 同上;無 parseUid 時只導回不打 API |
| src/views/compliance-framework/FrameworkImportPage.vue:481 | 確認匯入 | POST /oscal-framework-parse-jobs/ |
oscal.framework.import | step === 2 |
無判斷 | 同上 |
| src/views/compliance-framework/FrameworkImportPage.vue:533 | 上傳並解析文件 | POST /oscal-framework-parse-jobs/parse | oscal.framework.import | step === 1 |
無判斷 | 整頁 onMounted 以 !isPlatformAdmin 導回列表(縱深防護,非能力點);僅 FrameworkImportPage 用 |
| src/views/compliance-framework/FrameworkImportPage.vue:651 | 捨棄(解析失敗 Step 3) | DELETE /oscal-framework-parse-jobs/ |
oscal.framework.import | step === 3 |
無判斷 | 同上 |
| src/views/compliance-framework/FrameworkImportPage.vue:713 | 草稿管理 Dialog 列內「捨棄」 | DELETE /oscal-framework-parse-jobs/ |
oscal.framework.import | — | 無判斷 | 同上 |
| src/views/compliance-framework/FrameworkVersionEditView.vue:337 | 透過 PDF 更新內容 Dialog「上傳並解析」 | POST /oscal-framework-parse-jobs/parse(帶 target_version_uid 覆蓋模式) | oscal.framework.import | allowReupload(!readOnly && !tree.has_references;readOnly=!isPlatformAdmin || publish_status!=='draft') |
無判斷 | 入口 :269 同條件;僅 FrameworkVersionEditView 用 |
| src/views/module_frame/ImportExcelPage.vue:352 | 上傳區(點選/拖放 .xlsx 後自動上傳解析) | POST /ssp-excel-imports/parse | oscal.resource-library.create、oscal.resource-library.update | — | 無判斷 | 依 props.mode 擇一(create→oscal.resource-library.create;update-mf→oscal.resource-library.update),不是 hasAny;目前只有授權唯讀判斷 canUpload/無公司層判斷;頁面由 ModuleFrame.vue「從 Excel 建立」與 TemplateEditView「Excel 匯入」兩個入口進來 |
| src/views/module_frame/ImportExcelPreviewPage.vue:379 | 確認匯入 | POST /ssp-excel-import/<parse_uid>/confirm | oscal.resource-library.create、oscal.resource-library.update、oscal.ssp.import | parseResult.source_type(module_frame→覆蓋確認;ssp→更新確認;其他→直接匯入) |
無判斷 | 依 parseResult.source_type 擇一(framework_version→create;module_frame→update;ssp→oscal.ssp.import),不是 hasAny;本頁同時被專案 SSP 流程(SspTabPanel.vue 導入、return_to=ssp)使用;只有 writeDisabled 授權唯讀判斷 |
| src/views/module_frame/ModuleFrame.vue:61 | 新增選單 › 從 docx 建立(Menu model,:1069 渲染) | POST /ssp-docx-imports/parse、POST /ssp-docx-import/<parse_uid>/confirm | oscal.resource-library.create | — | 無判斷 | 入口鈕;送出在 SspDocxImportPage.vue(經 module_frame/ImportDocxPage.vue,mode=create) |
| src/views/module_frame/ModuleFrame.vue:66 | 新增選單 › 從 Excel 建立(Menu model,:1069 渲染) | POST /ssp-excel-imports/parse、POST /ssp-excel-import/<parse_uid>/confirm | oscal.resource-library.create | — | 無判斷 | 入口鈕;送出在 ImportExcelPage.vue / ImportExcelPreviewPage.vue(create 模式) |
| src/views/module_frame/ModuleFrame.vue:215 | 列動作 › 編輯(Menu model,:1454 渲染) | PUT /module-frame/ |
oscal.resource-library.update | — | 無判斷 | 入口鈕;送出在 ModuleFrameBasicInfoPanel.vue(經 ModuleFrameBasicInfoDialog.vue,該 Dialog 僅 ModuleFrame.vue 用) |
| src/views/module_frame/ModuleFrame.vue:230 | 列動作 › 刪除(Menu model,:1454 渲染) | DELETE /module-frame/ |
oscal.resource-library.delete | — | 無判斷 | 只有 writeDisabled 授權唯讀判斷 |
| src/views/module_frame/ModuleFrame.vue:1054 | 批次刪除(勾選後出現) | DELETE /module-frame/ |
oscal.resource-library.delete | selectedRows.length > 0 |
無判斷 | 只有 writeDisabled 授權唯讀判斷 |
| src/views/module_frame/ModuleFrame.vue:1440 | 新增資源庫 dialog › 建立 | POST /oscal/resource-libraries | oscal.resource-library.create | — | 無判斷 | dialog 由「新增選單 › 手動建立」(:55) 開啟;handler 另有 currFrame.uid 分支走 PUT /module-frame/ |
| src/views/module_frame/ModuleFrameItems.vue:354 | 列內 刪除 | DELETE /module-frame/item/ |
oscal.resource-library.update | — | 無判斷 | 本頁沒有任何路由指向(router 的 module-frame-items 指到 ModuleFrameDetail.vue),疑為孤兒頁;BE 守門今天寫的是 oscal.resource-library.delete,附錄 A 定 update |
| src/views/module_frame/ModuleFrameItems.vue:519 | 執行項目 dialog › 儲存 | PUT /module-frame/item/ |
oscal.resource-library.update | — | 無判斷 | dialog 由頁首新增鈕(:344)/列內編輯鈕(:352)開啟;孤兒頁同上;POST 帶 uid 的路徑對到附錄 A POST /module-frame/item(同一 resource 兩條 URL 註冊) |
| src/views/module_frame/ModuleFrameTemplateEditView.vue:186 | 批次維護選單 › 從 docx 匯入(Menu model,:1401 渲染) | POST /ssp-docx-imports/parse、POST /ssp-docx-import/<parse_uid>/confirm | oscal.resource-library.update | — | 無判斷 | 入口鈕;送出在 SspDocxImportPage.vue(經 module_frame/ImportDocxPage.vue,mode=update-mf) |
| src/views/module_frame/ModuleFrameTemplateEditView.vue:200 | 批次維護選單 › Excel 匯入(Menu model,:1401 渲染) | POST /ssp-excel-imports/parse、POST /ssp-excel-import/<parse_uid>/confirm | oscal.resource-library.update | — | 無判斷 | 入口鈕;送出在 ImportExcelPage.vue / ImportExcelPreviewPage.vue(update 模式) |
| src/views/module_frame/ModuleFrameTemplateEditView.vue:224 | 批次維護選單 › 匯入現況資料(Menu model,:1401 渲染) | POST /module-frame/ |
oscal.resource-library.update | — | 無判斷 | 入口鈕;送出在 ModuleFrameTemplateImportDialog.vue(僅本頁用);四支同權限 |
| src/views/module_frame/ModuleFrameTemplateEditView.vue:1424 | 編輯適用控制項 | PUT /module-frame/ |
oscal.resource-library.update | — | 無判斷 | 入口鈕;送出在 EditIncludeControlsDialog.vue(僅本頁用) |
| src/views/module_frame/ModuleFrameTemplateEditView.vue:1664 | 控制項 › 儲存 | PUT /module-frame/ |
oscal.resource-library.update | isControlDirty(selectedControl.code) 才可按(disabled) |
無判斷 | 只有 writeDisabled 授權唯讀判斷 |
| src/views/module_frame/ModuleFrameTemplateEditView.vue:1693 | 控制項程序書 chip › 解除掛載(×) | DELETE /module-frame/ |
oscal.resource-library.update | — | 無判斷 | |
| src/views/module_frame/ModuleFrameTemplateEditView.vue:1877 | 檢查項目程序書 chip › 解除掛載(×) | DELETE /module-frame/ |
oscal.resource-library.update | — | 無判斷 | |
| src/views/module_frame/ModuleFrameTemplateEditView.vue:1910 | 檢查項目 › 儲存 | PUT /module-frame/ |
oscal.resource-library.update | isAODirty(...) 才可按(disabled) |
無判斷 | |
| src/views/module_frame/ModuleFrameTemplateEditView.vue:2083 | 從程序書池選用 dialog › 確認 | POST /module-frame/ |
oscal.resource-library.update | — | 無判斷 | dialog 由控制項(:1713)/檢查項目(:1897)「+ 從程序書池選用」開啟;四支同權限 |
| src/views/project/ProjectPlanningView.vue:225 | lang.project_planning.btn_import_impl(批次維護選單:匯入現況說明 Excel) |
POST /ssp/<ssp_uid>/control-implementations/import(+/import/validate、/import/confirm) | oscal.ssp.import | 選單鈕 v-if="sspUid"(1973);item disabled:writeDisabled(!canEditSsp) | 無判斷 | 入口鈕;送出在 SspImportDialog.vue;Menu model item(script 行,Menu 在 template 1977) |
| src/views/project/ProjectPlanningView.vue:231 | lang.project_planning.btn_export_impl(批次維護選單:匯出現況說明 Excel) |
GET /ssp/<ssp_uid>/control-implementations/export | oscal.ssp.export | 選單鈕 v-if="sspUid" | 無判斷 | Menu model item;經 SspService.exportExcel |
| src/views/project/ProjectPlanningView.vue:258 | lang.project_planning.ssp_document_export_docx(匯出 SSP 文件 docx) |
GET /ssp/<ssp_uid>/export?format=docx | oscal.ssp.export | 選單鈕 v-if="sspUid" | 無判斷 | Menu model item |
| src/views/project/ProjectPlanningView.vue:263 | lang.project_planning.ssp_document_export_pdf(匯出 SSP 文件 pdf) |
GET /ssp/<ssp_uid>/export?format=pdf | oscal.ssp.export | 選單鈕 v-if="sspUid" | 無判斷 | Menu model item |
| src/views/project/ProjectPlanningView.vue:268 | lang.project_planning.ssp_document_export_odt(匯出 SSP 文件 odt) |
GET /ssp/<ssp_uid>/export?format=odt | oscal.ssp.export | 選單鈕 v-if="sspUid" | 無判斷 | Menu model item |
| src/views/project/ProjectPlanningView.vue:2242 | lang.project_planning.btn_save_ssp(控制項面板:儲存現況說明) |
PUT /ssp/<ssp_uid>/control-implementation/ |
oscal.ssp.update | canEditSsp(輪次 apStatus==='planning') | 無判斷 | 經 SspService.updateControlImplementation;另有 writeDisabled() |
| src/views/project/ProjectPlanningView.vue:2349 | lang.project_planning.btn_save_ssp(AO 面板:儲存現況說明) |
PUT /ssp/<ssp_uid>/control-implementation/ |
oscal.ssp.update | canEditSsp | 無判斷 | 經 SspService.updateObjective;另有 writeDisabled() |
| src/views/workflow/WorkflowSetupEditor.vue:472 | 分享給子租戶開關(InputSwitch) | PUT /module-frame/item/xml/ |
oscal.resource-library.update | canShare(有 processDefinitionId 且 processDefinition.tenant_id === currentTenant) |
無判斷 | 切換即打 API;只判資源所屬租戶 |
| src/views/workflow/WorkflowSetupEditor.vue:481 | 儲存流程圖 | PUT /module-frame/item/xml/ |
oscal.resource-library.update | !isReadonly(prop view 或 ?readonly=1) |
無判斷 | 只有授權唯讀 writeDisabled();僅 WorkflowSetupForm.vue 用(路由 /workflow/workflow-setup,由 ProjectPlanningView/ModuleFrameItems/ModuleFrameTemplateEditView/ModuleFrameDetail 開啟) |
| 檔案:行號 | 按鈕 | 打的 API | 權限 | 專案層條件 | 現況 | 備註 |
|---|---|---|---|---|---|---|
| src/views/flow-template/FlowTemplateEditorView.vue:956 | 儲存範本 | POST /flow-engine/flow-templates(新增)/PUT /flow-engine/flow-templates/ |
flow.template.create(新增)/flow.template.update(編輯) | !isReadonly(含 builtinWritable:非內建或平台管理員) |
已有且正確 | :disabled 用 isReadonly=新增看 hasCap('flow.template.create')、編輯看 hasCap('flow.template.update')+授權唯讀+內建 |
| src/views/flow-template/FlowTemplateEditorView.vue:969 | 發布 | PUT /flow-engine/flow-templates/ |
flow.template.publish、flow.template.update | builtinWritable && !isCreating && template.status !== 'published' |
已有但名不對 | 實際用 flow.template.update(isReadonly);dirty 時兩支都要打,需兩者皆有 |
| src/views/flow-template/FlowTemplateEditorView.vue:979 | 撤銷發布 | PUT /flow-engine/flow-templates/ |
flow.template.publish | builtinWritable && !isCreating && template.status === 'published' |
已有但名不對 | 實際用 flow.template.update(isReadonly) |
| src/views/flow-template/FlowTemplateEditorView.vue:1024 | 分享給子租戶開關(InputSwitch) | PUT /flow-engine/flow-templates/ |
flow.template.update | canShare(非新增且 template.tenant_id === currentTenant) |
無判斷 | 切換即打 API;只判資源所屬租戶,沒看能力點 |
| src/views/flow-template/FlowTemplateEditorView.vue:1239 | 另存為 Dialog「儲存」 | POST /flow-engine/flow-templates | flow.template.create | — | 已有且正確 | 判斷在入口 ⋮ 選單「另存為」(:681 disabled: !canCreate=hasCap('flow.template.create')),dialog 鈕本身沒判 |
| src/views/flow-template/FlowTemplateManageView.vue:269 | 新增範本(入口鈕) | POST /flow-engine/flow-templates | flow.template.create | — | 已有且正確 | 入口鈕;本頁 dialog「儲存」:544 只帶 query 跳編輯頁不打 API,真正送出在 FlowTemplateEditorView.vue:956;disabled 用 canCreate |
| src/views/flow-template/FlowTemplateManageView.vue:423 | 列內編輯(入口鈕) | PUT /flow-engine/flow-templates/ |
flow.template.update | canWriteRow(data)(非內建或平台管理員) |
已有且正確 | 入口鈕;送出在 FlowTemplateEditorView.vue;disabled 用 canUpdate |
| src/views/flow-template/FlowTemplateManageView.vue:450 | 列內發布 | PUT /flow-engine/flow-templates/ |
flow.template.publish | canWriteRow(data) && data.status === 'draft' |
已有但名不對 | 實際用 flow.template.update(canUpdate) |
| src/views/flow-template/FlowTemplateManageView.vue:464 | 列內撤銷發布 | PUT /flow-engine/flow-templates/ |
flow.template.publish | canWriteRow(data) && data.status === 'published' |
已有但名不對 | 實際用 flow.template.update(canUpdate) |
| src/views/flow-template/FlowTemplateManageView.vue:478 | 列內刪除 | DELETE /flow-engine/flow-templates/ |
flow.template.delete | canWriteRow(data) |
已有且正確 | disabled 用 canDelete=hasCap('flow.template.delete') |
| src/views/flow-template/FlowTemplateManageView.vue:595 | 複製範本 Dialog「複製」 | POST /flow-engine/flow-templates/ |
flow.template.create | — | 已有且正確 | 判斷在入口 :437(disabled: !canCreate),dialog 鈕本身沒判 |
| 檔案:行號 | 按鈕 | 打的 API | 權限 | 專案層條件 | 現況 | 備註 |
|---|---|---|---|---|---|---|
| src/views/detection-profile/DetectionProfileManageView.vue:427 | 選單項 btn_edit(編輯基本資料) |
PUT /detection-tool-profiles/ |
detection.profile.update | !readonly(rowReadonlyReason:授權唯讀/公版非 root/已停用) |
已有且正確 | Menu model item(template:895,kebab 鈕 :866);入口;送出在 ProfileFormDialog.vue:448(edit 模式)。canUpdate,disabled 不隱藏 |
| src/views/detection-profile/DetectionProfileManageView.vue:436 | 選單項 btn_new_version(版更) |
POST /detection-tool-profiles/ |
detection.profile.create | row.current_version && !readonly |
已有且正確 | Menu item;入口;送出在 ProfileNewVersionDialog.vue:169。canCreate |
| src/views/detection-profile/DetectionProfileManageView.vue:448 | 選單項 btn_fix_source(修正現行版來源) |
PATCH /detection-tool-profile-versions/ |
detection.profile.create | row.current_version && !frozen && !readonly(frozen=referencing-usage 判定已被使用) |
已有且正確 | Menu item;入口;送出在 ProfileSourceDialog.vue:201。canCreate |
| src/views/detection-profile/DetectionProfileManageView.vue:460 | 選單項 btn_fork(複製為自有) |
POST /detection-tool-profiles/ |
detection.profile.create | row.scope === 'SYSTEM' && row.is_active |
已有且正確 | Menu item;入口;送出在 ProfileCopyDialog.vue:143(fork 模式)。canCreate;刻意不受整列唯讀影響 |
| src/views/detection-profile/DetectionProfileManageView.vue:469 | 選單項 btn_copy_to_tool(複製到其他工具) |
POST /detection-tool-profiles/ |
detection.profile.create | !readonly |
已有且正確 | Menu item;入口;送出在 ProfileCopyDialog.vue:143(copy 模式)。canCreate |
| src/views/detection-profile/DetectionProfileManageView.vue:486 | 選單項 btn_delete(刪除整支基準) |
DELETE /detection-tool-profiles/ |
detection.profile.delete | usage && !frozen && !readonly(未被使用才出現,與停用互斥) |
已有且正確 | Menu item;confirm 後 handleDelete(:584)。canDelete |
| src/views/detection-profile/DetectionProfileManageView.vue:496 | 選單項 btn_deactivate(停用) |
PUT /detection-tool-profiles/ |
detection.profile.delete | !(usage && !frozen) && !readonly(已被使用或判定未回時出現) |
已有且正確 | Menu item;handleDeactivate(:552)。canDelete |
| src/views/detection-profile/DetectionProfileManageView.vue:630 | btn_new(新增掃描設定檔) |
POST /detection-tool-profiles | detection.profile.create | — | 已有且正確 | 入口鈕;送出在 ProfileFormDialog.vue:448。用 canCreate=hasCap('detection.profile.create'),disabled 不隱藏 |
| src/views/detection-profile/components/ControlExtractionSummary.vue:178 | controls_btn_retry(抽取失敗後重新解析) |
emit retry → 由 DetectionProfileControlsView.vue handleRetry(:329) → POST /detection-tool-profile-versions/ |
detection.profile.create | isFailed && canRetry |
無判斷 | 元件有 canRetry prop(預設 true)但父層沒傳;判斷放 props 由頁面傳入;僅 DetectionProfileControlsView 用 |
| src/views/detection-profile/components/ControlExtractionSummary.vue:200 | controls_btn_extract(尚未抽取→開始解析) |
emit retry → 由 DetectionProfileControlsView.vue handleRetry(:329) → POST /detection-tool-profile-versions/ |
detection.profile.create | isPending && canRetry |
無判斷 | 同上 |
| src/views/detection-profile/components/ControlExtractionSummary.vue:241 | controls_btn_retry(url 來源過期→重新解析) |
emit retry → 由 DetectionProfileControlsView.vue handleRetry(:329) → POST /detection-tool-profile-versions/ |
detection.profile.create | isStaleSnapshot && canRetry |
無判斷 | 同上 |
| src/views/detection-profile/components/ProfileCopyDialog.vue:143 | btn_fork/btn_copy_to_tool(複製送出) |
POST /detection-tool-profiles/ |
detection.profile.create | — | 無判斷 | 兩模式同權限;只有 writeDisabled;僅 DetectionProfileManageView 用 |
| src/views/detection-profile/components/ProfileFormDialog.vue:448 | btn_save(新增/編輯送出) |
POST /detection-tool-profiles(create 模式);PUT /detection-tool-profiles/ |
detection.profile.create、detection.profile.update | — | 無判斷 | 依 mode 二擇一(非 hasAny);只有 writeDisabled(授權唯讀);入口在父層已判;僅 DetectionProfileManageView 用 |
| src/views/detection-profile/components/ProfileNewVersionDialog.vue:169 | btn_new_version(版更送出) |
POST /detection-tool-profiles/ |
detection.profile.create | — | 無判斷 | 只有 writeDisabled;入口在父層已判;僅 DetectionProfileManageView 用 |
| src/views/detection-profile/components/ProfileSourceDialog.vue:201 | btn_fix_source(修正來源送出) |
PATCH /detection-tool-profile-versions/ |
detection.profile.create | — | 無判斷 | 只有 writeDisabled;主列表與版本子表兩入口共用;僅 DetectionProfileManageView 用 |
| src/views/detection-profile/components/ProfileVersionTable.vue:328 | 選單項 controls_btn_extract(開始解析) |
POST /detection-tool-profile-versions/ |
detection.profile.create | ['pending','failed'].includes(row.extraction_status) && !readonly |
已有且正確 | Menu item(template:573,kebab :559);canCreate=hasCap('detection.profile.create');僅 DetectionProfileManageView 用 |
| src/views/detection-profile/components/ProfileVersionTable.vue:341 | 選單項 btn_fix_source(修正此版來源) |
emit fix-source → 由 DetectionProfileManageView.vue openVersionFixSource 開 ProfileSourceDialog → PATCH /detection-tool-profile-versions/ |
detection.profile.create | !frozen && !readonly |
已有且正確 | Menu item;canCreate;僅 DetectionProfileManageView 用 |
| src/views/detection-profile/components/ProfileVersionTable.vue:352 | 選單項 btn_delete_version(刪除此版) |
DELETE /detection-tool-profile-versions/ |
detection.profile.delete | usage && !frozen && !readonly |
已有且正確 | Menu item;canDelete;僅 DetectionProfileManageView 用 |
| src/views/plugin/ToolPluginManage.vue:510 | 選單項 common.enable(啟用工具) |
PUT /detection-tools/configs/ |
detection.plugin.update | tool.config?.status === 'disabled' |
無判斷 | Menu model item(template:621,⋮ 鈕 :605);只有 writeDisabled |
| src/views/plugin/ToolPluginManage.vue:517 | 選單項 stop_use(停止使用) |
PUT /detection-tools/configs/ |
detection.plugin.update | tool.config?.status === 'enabled' |
無判斷 | Menu item;確認前另打 GET referencing-tasks-count(read);只有 writeDisabled |
| src/views/plugin/ToolPluginManage.vue:525 | 選單項 reset_key(重置憑證) |
POST /detection-tools/configs/ |
detection.plugin.update | tool.config |
無判斷 | Menu item;只有 writeDisabled |
| src/views/plugin/ToolPluginManage.vue:595 | test_connection(卡片測試連線) |
POST /detection-tools/configs/ |
detection.plugin.update | tool.config |
無判斷 | 不需前置時直接打;需主機/憑證組/多 agent 時改開同檔 dialog(送出在 :781)。連 writeDisabled 也沒掛;僅本頁用 |
| src/views/plugin/ToolPluginManage.vue:670 | common.save(工具設定 dialog 儲存) |
POST /detection-tools/configs(首次設定);PUT /detection-tools/configs/ |
detection.plugin.create、detection.plugin.update | — | 無判斷 | 依是否已有 config 二擇一(非 hasAny);開窗鈕 :586 同檔不另列;只有 writeDisabled |
| src/views/plugin/ToolPluginManage.vue:781 | test_connection(測試前置 dialog 送出) |
POST /detection-tools/configs/ |
detection.plugin.update | canRunTest |
無判斷 | 多台主機逐台並發各打一次 |
| src/views/remote-agent/RemoteAgentManage.vue:438 | generate/regenerate(產生註冊 Token) |
POST /agents/enroll-token | detection.remote-agent.create | — | 無判斷 | 只有 writeDisabled;僅本頁用 |
| src/views/remote-agent/RemoteAgentManage.vue:448 | revoke_token(撤銷註冊 Token) |
POST /agents/enroll-token/revoke | detection.remote-agent.delete | enrollToken?.exists |
無判斷 | 只有 writeDisabled |
| src/views/remote-agent/RemoteAgentManage.vue:490 | approve_agent(核准待核准機器) |
POST /remote-agents/ |
detection.remote-agent.update | status === 'pending'(待核准區) |
無判斷 | 只有 writeDisabled |
| src/views/remote-agent/RemoteAgentManage.vue:495 | 刪除(待核准區 icon 鈕) | DELETE /remote-agents/ |
detection.remote-agent.delete | status === 'pending'(待核准區) |
無判斷 | 只有 writeDisabled |
| src/views/remote-agent/RemoteAgentManage.vue:608 | InputSwitch 啟用/停用 | PUT /remote-agents/ |
detection.remote-agent.update | — | 無判斷 | 列內開關;連 writeDisabled 都沒掛 |
| src/views/remote-agent/RemoteAgentManage.vue:614 | reconcile(對帳 icon 鈕) |
POST /remote-agents/ |
detection.remote-agent.update | — | 無判斷 | 只有 writeDisabled |
| src/views/remote-agent/RemoteAgentManage.vue:624 | unrevoke_agent(復用 icon 鈕) |
POST /remote-agents/ |
detection.remote-agent.update | data.status === 'revoked' |
無判斷 | 只有 writeDisabled |
| src/views/remote-agent/RemoteAgentManage.vue:628 | revoke_agent(撤銷 icon 鈕) |
POST /remote-agents/ |
detection.remote-agent.update | data.status !== 'revoked'(v-else) |
無判斷 | 只有 writeDisabled |
| src/views/remote-agent/RemoteAgentManage.vue:632 | 刪除(已核准區 icon 鈕) | DELETE /remote-agents/ |
detection.remote-agent.delete | — | 無判斷 | 只有 writeDisabled |
| src/views/remote-agent/RemoteAgentManage.vue:677 | common.save(新增/編輯 agent dialog 儲存) |
POST /remote-agents(新增);PUT /remote-agents/ |
detection.remote-agent.create、detection.remote-agent.update | — | 無判斷 | 依有無 uid 二擇一(非 hasAny);編輯前先打 POST health-check(read);開窗鈕 :513/:618 同檔不另列 |
| src/views/scan-zone/ScanZoneDialog.vue:68 | common.save(網段儲存) |
POST /scan-zones(新增);PUT /scan-zones/ |
detection.scan-zone.create、detection.scan-zone.update | — | 無判斷 | 依 zone prop 二擇一(非 hasAny);入口在父層以 canEdit 擋;僅 ScanZoneManage 用 |
| src/views/scan-zone/ScanZoneManage.vue:136 | suggest(依 Agent 產生建議網段) |
POST /scan-zones/suggest-from-agents | detection.scan-zone.create | canEdit(hasCap(SCAN_ZONE_UPDATE),v-if :135) |
已有但名不對 | 實際用 detection.scan-zone.update;僅本頁用 |
| src/views/scan-zone/ScanZoneManage.vue:139 | create(新增授權網段) |
POST /scan-zones | detection.scan-zone.create | canEdit(v-if :135) |
已有但名不對 | 實際用 detection.scan-zone.update;入口鈕;送出在 ScanZoneDialog.vue:68 |
| src/views/scan-zone/ScanZoneManage.vue:153 | 確認授權(pending→active icon 鈕) | PUT /scan-zones/ |
detection.scan-zone.update | canEdit(欄 v-if :150)&& data.status === 'pending' |
已有且正確 | |
| src/views/scan-zone/ScanZoneManage.vue:156 | 編輯(icon 鈕) | PUT /scan-zones/ |
detection.scan-zone.update | canEdit(欄 v-if :150) |
已有且正確 | 入口鈕;送出在 ScanZoneDialog.vue:68 |
| src/views/scan-zone/ScanZoneManage.vue:158 | 刪除(icon 鈕) | DELETE /scan-zones/ |
detection.scan-zone.delete | canEdit(欄 v-if :150) |
已有但名不對 | 實際用 detection.scan-zone.update |
| 檔案:行號 | 按鈕 | 打的 API | 權限 | 專案層條件 | 現況 | 備註 |
|---|---|---|---|---|---|---|
| src/components/integrations/GoogleDriveIntegrationCard.vue:310 | ⋯選單 gdrive.reauth_btn(重新授權) |
POST /integrations/google-drive/auth-url | evidence.cloud-integration.create | isConnected(業務狀態) | 已有且正確 | Menu model item 定義在 script:235,以 canCreate 過濾;僅 CloudIntegrationsView 用 |
| src/components/integrations/GoogleDriveIntegrationCard.vue:325 | gdrive.connect_btn(連線 Google Drive) |
POST /integrations/google-drive/auth-url | evidence.cloud-integration.create | status.status==='DISCONNECTED' && !loading(業務狀態) | 已有且正確 | v-if="canCreate"(hasCap);僅 CloudIntegrationsView 用 |
| src/components/integrations/GoogleDriveIntegrationCard.vue:341 | gdrive.reauth_btn(連線異常區的重新授權) |
POST /integrations/google-drive/auth-url | evidence.cloud-integration.create | isError(status 為 REVOKED/EXPIRED,業務狀態) | 無判斷 | 同支 connect();⋯選單裡的重新授權有 canCreate,這顆錯誤橫幅內的沒有,沒權限者點了必 403;僅 CloudIntegrationsView 用 |
| src/components/integrations/GoogleDriveIntegrationCard.vue:396 | gdrive.disconnect_btn(中斷連線) |
DELETE /integrations/google-drive | evidence.cloud-integration.delete | isConnected(業務狀態) | 已有且正確 | v-if="canDelete";僅 CloudIntegrationsView 用 |
| src/components/integrations/GoogleDriveIntegrationCard.vue:436 | gdrive.rebuild_all_dialog_confirm_btn(重建全部 Drive 資料夾) |
POST /integrations/google-drive/tenant/rebuild-all | evidence.cloud-integration.update | isConnected;輸入 REBUILD 才可按 | 已有且正確 | dialog 內確認鈕本身沒判斷,判斷在入口 ⋯選單項 canUpdate(script:242);僅 CloudIntegrationsView 用 |
| src/components/integrations/SyncJobHistoryDialog.vue:144 | sync_jobs.retry(重試同步工作) |
POST /integrations/google-drive/sync-jobs/ |
evidence.cloud-integration.update | data.status==='FAILED'(業務狀態) | 已有且正確 | v-if 含 canRetry(hasCap update);僅 GoogleDriveIntegrationCard 用 |
| src/views/evidence-classification/AutoClassifyBatch.vue:529 | FileUpload 投遞區(上傳證據到批次) | POST /evidence-batches//files | evidence.classification.update | canWrite && batchEditable(canWrite=isProjectManager && !storageBlockedMsg;batchEditable=status 為 uploading/ready) | 無判斷 | 條件掛在外層 template :515;路由頁 auto-classify-batch |
| src/views/evidence-classification/AutoClassifyBatch.vue:647 | 列內 action_remove_file(移除批次檔) |
DELETE /evidence-batches//files/ |
evidence.classification.update | canWrite && batchEditable | 無判斷 | 走 confirm.require |
| src/views/evidence-classification/AutoClassifyBatch.vue:804 | archive.purge_button(第④步清理剩餘檔) |
POST /evidence-batches//purge | evidence.classification.delete | canWrite && purgeableCount && isArchived(第④步 v-else 區塊) | 無判斷 | 走 confirm.require |
| src/views/evidence-classification/AutoClassifyBatch.vue:894 | auto_classify.start_classify(分類設定 dialog 內開始分類) |
POST /evidence-batches//classify | evidence.classification.execute | 入口 :680 canWrite && batchEditable;入口 :702(重跑)canWrite && isFailed | 無判斷 | 附錄 A 新名欄註「只記錄」;入口兩顆開同檔 dialog |
| src/views/evidence-classification/AutoClassifyBatch.vue:928 | archive.confirm_accept(歸檔進任務,dialog 確認) |
POST /evidence-batches//archive | evidence.classification.update | canWrite(以 :can-archive 傳入 EvidenceClassificationReview 控制入口) | 無判斷 | 入口在 EvidenceClassificationReview.vue:391(emit archive) |
| src/views/evidence-classification/AutoClassifyList.vue:293 | auto_classify.create_batch(新增批次,頁首) |
POST /evidence-batches | evidence.classification.create | isProjectManager(目前使用者在 project.participants 的 role==='manager') | 無判斷 | disabled 於 creating || !roundUid || storageBlockedMsg;路由頁 auto-classify-list |
| src/views/evidence-classification/AutoClassifyList.vue:355 | auto_classify.create_batch(新增批次,空狀態) |
POST /evidence-batches | evidence.classification.create | isProjectManager | 無判斷 | 與 :293 同動作的第二入口 |
| src/views/evidence-classification/AutoClassifyList.vue:442 | 列內 action_purge(清理剩餘暫存檔) |
POST /evidence-batches//purge | evidence.classification.delete | canPurge(data)=isFinished(data)(已歸檔) && isProjectManager | 無判斷 | 走 confirm.require 後打 API |
| src/views/evidence-classification/AutoClassifyList.vue:506 | delete_batch_accept(刪除整批,dialog 確認) |
DELETE /evidence-batches/ | evidence.classification.delete | isProjectManager(入口鈕 :454);入口 disabled 於 isClassifying(data) || isFinished(data) | 無判斷 | 入口列內垃圾桶 :454 開同檔 dialog |
| src/views/evidence-classification/EvidenceClassificationReview.vue:391 | archive.archive_now / archive.rearchive(歸檔入口) |
emit archive → 由 AutoClassifyBatch.vue(onArchiveRequest→dialog :928→doArchive)處理:POST /evidence-batches//archive |
evidence.classification.update | byRunUid && embedded && canArchive(canArchive 由父層傳 canWrite=isProjectManager && !storageBlockedMsg);disabled 於 hasUnsavedChanges || archiving | 無判斷 | 入口鈕;送出在 AutoClassifyBatch.vue。本元件被 AutoClassifyBatch(嵌入)與路由 evidence-batch-review 用,獨立路由時不顯示此鈕,判斷放 props 由頁面傳入 |
| src/views/evidence-classification/components/ClassificationReviewHeader.vue:120 | actions.save_changes(儲存審閱結果) |
emit save → 由 EvidenceClassificationReview.vue onSave → useEvidenceClassification.save 處理:PUT /classification-run/ |
evidence.classification.update | — | 無判斷 | 僅 EvidenceClassificationReview 用;完全沒有角色條件(稽核員也看得到可按),只有 disabled 於 !hasUnsavedChanges || saving;判斷需由 Review 頁傳 prop |
| 檔案:行號 | 按鈕 | 打的 API | 權限 | 專案層條件 | 現況 | 備註 |
|---|---|---|---|---|---|---|
| src/views/survey-v2/SurveyDesigner.vue:885 | 儲存選單項「儲存」(saveMenuItems :237) | PUT /survey/ |
survey.survey.update | — | 無判斷 | :879 儲存鈕只展開選單;只有 writeDisabled(授權唯讀) |
| src/views/survey-v2/SurveyDesigner.vue:885 | 儲存選單項「另存新版本」(saveMenuItems :242) | POST /survey(as_new_version) | survey.survey.create | — | 無判斷 | 同上 |
| src/views/survey-v2/SurveyDesigner.vue:887 | 發布/取消發布 | PUT /survey/ |
survey.survey.publish | — | 無判斷 | 依 survey.isPublished 切換;只有 writeDisabled |
| src/views/survey-v2/SurveyDesigner.vue:927 | 題組樹「+」新增題組 | POST /survey-page | survey.survey.update | — | 無判斷 | 入口鈕;送出在 AddPageDialog.vue(emit submit → SurveyDesigner.onPageSubmit) |
| src/views/survey-v2/SurveyDesigner.vue:1017 | 空頁「新增題目」 | POST /survey-question | survey.survey.update | — | 無判斷 | 入口鈕;送出在 AddQuestionDialog.vue(emit submit → SurveyDesigner.onQuestionSubmit) |
| src/views/survey-v2/SurveyDesigner.vue:1028 | 題目間「插入題目」(第一題前) | POST /survey-question、POST /survey-questions/reorder | survey.survey.update | — | 無判斷 | 入口鈕;送出在 AddQuestionDialog.vue;原生 button |
| src/views/survey-v2/SurveyDesigner.vue:1114 | 子題上移 | POST /survey-questions/reorder | survey.survey.update | — | 無判斷 | |
| src/views/survey-v2/SurveyDesigner.vue:1120 | 子題下移 | POST /survey-questions/reorder | survey.survey.update | — | 無判斷 | |
| src/views/survey-v2/SurveyDesigner.vue:1128 | 子題編輯 | PUT /survey-question/ |
survey.survey.update | — | 無判斷 | 入口鈕;送出在 AddQuestionDialog.vue |
| src/views/survey-v2/SurveyDesigner.vue:1133 | 子題複製 | POST /survey-question | survey.survey.update | — | 無判斷 | |
| src/views/survey-v2/SurveyDesigner.vue:1138 | 子題刪除 | DELETE /survey-question/ |
survey.survey.update | — | 無判斷 | |
| src/views/survey-v2/SurveyDesigner.vue:1150 | 母題卡「+新增延伸題」 | POST /survey-question | survey.survey.update | — | 無判斷 | 入口鈕;送出在 AddQuestionDialog.vue |
| src/views/survey-v2/SurveyDesigner.vue:1161 | 題目上移 | POST /survey-questions/reorder | survey.survey.update | — | 無判斷 | |
| src/views/survey-v2/SurveyDesigner.vue:1167 | 題目下移 | POST /survey-questions/reorder | survey.survey.update | — | 無判斷 | |
| src/views/survey-v2/SurveyDesigner.vue:1175 | 題目編輯 | PUT /survey-question/ |
survey.survey.update | — | 無判斷 | 入口鈕;送出在 AddQuestionDialog.vue |
| src/views/survey-v2/SurveyDesigner.vue:1180 | 題目複製 | POST /survey-question | survey.survey.update | — | 無判斷 | |
| src/views/survey-v2/SurveyDesigner.vue:1185 | 題目刪除 | DELETE /survey-question/ |
survey.survey.update | — | 無判斷 | |
| src/views/survey-v2/SurveyDesigner.vue:1197 | 題目間「插入題目」(每題後) | POST /survey-question、POST /survey-questions/reorder | survey.survey.update | — | 無判斷 | 入口鈕;送出在 AddQuestionDialog.vue;原生 button |
| src/views/survey-v2/SurveyDesigner.vue:1205 | 底部虛線「新增題目」 | POST /survey-question | survey.survey.update | — | 無判斷 | 入口鈕;送出在 AddQuestionDialog.vue;原生 button |
| src/views/survey-v2/SurveyManage.vue:571 | 批次刪除問卷 | DELETE /survey/ |
survey.survey.delete | selectedSurveys.length > 0 | 無判斷 | 已填答(hasAnswer)的會被前端略過;僅 SurveyManage 用 |
| src/views/survey-v2/SurveyManage.vue:579 | SplitButton 項「從範本建立」(model 定義 :173) | POST /survey/ |
survey.survey.create | — | 無判斷 | 入口鈕;送出在 SurveyPickerDialog.vue(emit confirm → SurveyManage.onPickerConfirm) |
| src/views/survey-v2/SurveyManage.vue:579 | SplitButton 項「匯入問卷」(model 定義 :182) | POST /survey/excel-import | survey.survey.import | — | 無判斷 | 入口鈕;送出在 BatchImportDialog.vue;SplitButton 主鈕只展開選單、「建立空白問卷」項開本檔 dialog(送出見 :899) |
| src/views/survey-v2/SurveyManage.vue:676 | 卡片 ⋮ 選單項「複製」(getContextMenuItems :417) | POST /survey/ |
survey.survey.create | — | 無判斷 | 入口鈕;開 SurveyPickerDialog(copy 模式),送出由 SurveyManage.onPickerConfirm 打 clone |
| src/views/survey-v2/SurveyManage.vue:676 | 卡片 ⋮ 選單項「刪除」(getContextMenuItems :420) | DELETE /survey/ |
survey.survey.delete | — | 無判斷 | survey.hasAnswer 時前端擋下只跳 toast |
| src/views/survey-v2/SurveyManage.vue:775 | 列表模式列內刪除(pi-trash) | DELETE /survey/ |
survey.survey.delete | — | 無判斷 | 同 onDeleteSurvey |
| src/views/survey-v2/SurveyManage.vue:899 | 建立問卷 dialog「建立並設計」 | POST /survey | survey.survey.create | — | 無判斷 | 本檔 dialog 送出 |
| src/views/survey-v2/SurveyManage.vue:925 | 移動問卷 dialog「確定」 | POST /survey/move | survey.survey.update | — | 無判斷 | 入口為卡片選單「移動」(本檔 dialog,入口不列) |
| src/views/survey-v2/components/AddPageDialog.vue:248 | 題組 dialog「確定」 | emit submit → 由 SurveyDesigner.vue 處理:POST /survey-page(新增)或 PUT /survey-page/ |
survey.survey.update | — | 無判斷 | 兩種模式同一權限;僅 SurveyDesigner.vue 用 |
| src/views/survey-v2/components/AddQuestionDialog.vue:667 | 題目 dialog「確定/儲存」(第 3 步) | emit submit → 由 SurveyDesigner.vue 處理:POST /survey-question 或 PUT /survey-question/ |
survey.survey.update | — | 無判斷 | 僅 SurveyDesigner.vue 用 |
| src/views/survey-v2/components/BatchImportDialog.vue:221 | 下載匯入範本 | GET /survey/import-template/token(再導 GET /survey/import-template) | survey.survey.import | — | 無判斷 | 範本下載本體在附錄 C 豁免,但換憑證那支守 import;僅 SurveyManage.vue 用 |
| src/views/survey-v2/components/BatchImportDialog.vue:245 | 開始匯入 | POST /survey/excel-import | survey.survey.import | — | 無判斷 | 只有 writeDisabled(授權唯讀)反灰;僅 SurveyManage.vue 用 |
| src/views/survey-v2/components/FolderTree.vue:177 | 批次刪除資料夾 | emit folders-batch-deleted → 由 SurveyManage.vue 處理:DELETE /survey-folder/ |
survey.survey.delete | selectedKeys.length > 0 | 無判斷 | 僅 SurveyManage.vue 用 |
| src/views/survey-v2/components/FolderTree.vue:222 | 資料夾 ⋯ 選單項「刪除資料夾」(ctxMenuItems :80) | emit folder-deleted → 由 SurveyManage.vue 處理:DELETE /survey-folder/ |
survey.survey.delete | — | 無判斷 | 「編輯資料夾」項開本檔 dialog,送出見 :247;僅 SurveyManage.vue 用 |
| src/views/survey-v2/components/FolderTree.vue:247 | 資料夾 dialog「確定」(新增模式) | emit folder-created → 由 SurveyManage.vue 處理:POST /survey-folder |
survey.survey.create | !editMode | 無判斷 | 入口為 :162「+」(本檔 dialog);同一顆鈕依 editMode 打不同 API;僅 SurveyManage.vue 用 |
| src/views/survey-v2/components/FolderTree.vue:247 | 資料夾 dialog「確定」(編輯模式) | emit folder-updated → 由 SurveyManage.vue 處理:PUT /survey-folder/ |
survey.survey.update | editMode | 無判斷 | 入口為 :222 選單「編輯資料夾」;僅 SurveyManage.vue 用 |
| src/views/survey-v2/components/PageTreeNode.vue:71 | 題組 ⋮ 彈出選單「編輯」 | PUT /survey-page/ |
survey.survey.update | — | 無判斷 | 入口鈕;emit edit → SurveyDesigner 開 AddPageDialog,送出由 SurveyDesigner.onPageSubmit 打;僅 SurveyDesigner.vue 用 |
| src/views/survey-v2/components/PageTreeNode.vue:75 | 題組 ⋮ 彈出選單「刪除」 | emit delete → 由 SurveyDesigner.vue 處理:DELETE /survey-page/ |
survey.survey.update | — | 無判斷 | 僅 SurveyDesigner.vue 用 |
| src/views/survey-v2/components/SurveyPickerDialog.vue:235 | 清單項雙擊(等同確認) | emit confirm → 由 SurveyManage.vue 處理:POST /survey/ |
survey.survey.create | — | 無判斷 | 非按鈕但會直接觸發寫入,隱藏確認鈕時要一起擋;僅 SurveyManage.vue 用 |
| src/views/survey-v2/components/SurveyPickerDialog.vue:291 | 範本/複製確認鈕 | emit confirm → 由 SurveyManage.vue 處理:POST /survey/ |
survey.survey.create | — | 無判斷 | 僅 SurveyManage.vue 用;開啟時另打 GET /surveys/menu(讀) |
| 檔案:行號 | 按鈕 | 打的 API | 權限 | 專案層條件 | 現況 | 備註 |
|---|---|---|---|---|---|---|
| src/views/issue-integrate-config/IssueIntegrateConfigForm.vue:165 | 儲存(GitLab) | PUT /system/config/ISSUE_INTEGRATE_CONFIG/GITLAB | system.issue-integrate-config.update | — | 無判斷 | 群組 ISSUE_INTEGRATE_CONFIG;僅 router 用 |
| src/views/issue-integrate-config/IssueIntegrateConfigForm.vue:197 | 儲存(GitHub) | PUT /system/config/ISSUE_INTEGRATE_CONFIG/GITHUB | system.issue-integrate-config.update | — | 無判斷 | 群組 ISSUE_INTEGRATE_CONFIG;僅 router 用 |
| src/views/ldap-config/LdapConfigForm.vue:271 | 測試連線 | POST /ldap/connect-test | system.ldap-config.update | — | 無判斷 | 沒掛 writeDisabled;僅 router 用 |
| src/views/ldap-config/LdapConfigForm.vue:272 | 儲存 | PUT /system/config/THIRD_PARTY_LOGIN/LDAP | system.ldap-config.update | — | 無判斷 | 群組 THIRD_PARTY_LOGIN;另有非總部處理(useCompanyWideConfigAccess 接 GRC_403066,非能力點);僅 router 用 |
| src/views/log-forwarding/LogForwardingForm.vue:433 | 發送測試訊息 | POST /log-forwarding/test | system.log-forwarding.update | — | 已有且正確 | hasCap('system.log-forwarding.update')(canUpdate);僅 router 用 |
| src/views/log-forwarding/LogForwardingForm.vue:440 | 儲存 | PUT /log-forwarding | system.log-forwarding.update | — | 已有且正確 | hasCap('system.log-forwarding.update')(canUpdate);僅 router 用 |
| src/views/notify-config/NotifyConfigForm.vue:254 | 測試(Telegram) | POST /notify-config/TELEGRAM/test | system.notify-config.update | — | 無判斷 | 沒掛 writeDisabled;僅 router 用 |
| src/views/notify-config/NotifyConfigForm.vue:262 | 儲存(Telegram) | PUT /system/config/NOTIFY_CONFIG/TELEGRAM | system.notify-config.update | — | 無判斷 | 群組 NOTIFY_CONFIG;僅 router 用 |
| src/views/notify-config/NotifyConfigForm.vue:310 | 測試(Discord) | POST /notify-config/DISCORD/test | system.notify-config.update | — | 無判斷 | 沒掛 writeDisabled;僅 router 用 |
| src/views/notify-config/NotifyConfigForm.vue:318 | 儲存(Discord) | PUT /system/config/NOTIFY_CONFIG/DISCORD | system.notify-config.update | — | 無判斷 | 群組 NOTIFY_CONFIG;僅 router 用 |
| src/views/security-policy/SecurityPolicyForm.vue:372 | 儲存 | PUT /system/security-policy | system.security-policy.update | — | 已有且正確 | hasCap('system.security-policy.update')(canUpdate);另有非總部處理;僅 router 用 |
| src/views/setup/SetupWizard.vue:380 | 建立管理員帳號 | POST /setup/provision | 免判斷(附錄 C) | — | 免判斷 | 免登入、靠 X-Setup-Token;僅 router 用 |
| src/views/smtp-config/SmtpConfigForm.vue:341 | 儲存 | 無 uid:POST /system/config(group=SMTP);有 uid:PUT /system/config/ |
system.smtp-config.create、system.smtp-config.update | — | 無判斷 | 依 query.uid 決定新增或修改(同一顆鈕兩種權限,依模式擇一,非 hasAny);群組 SMTP;僅 router 用 |
| src/views/smtp-config/SmtpConfigForm.vue:365 | 寄送測試信(dialog 內「送出」) | POST /mail/test | system.smtp-config.update | — | 無判斷 | 入口鈕在同檔 :340(開 dialog,不列);沒掛 writeDisabled;僅 router 用 |
| src/views/smtp-config/SmtpConfigManage.vue:126 | 新增設定 | POST /system/config(group=SMTP) | system.smtp-config.create | — | 無判斷 | 入口鈕;送出在 SmtpConfigForm.vue(導頁 name=smtp-config);僅 router 用 |
| src/views/smtp-config/SmtpConfigManage.vue:133 | 編輯(列內 icon) | PUT /system/config/ |
system.smtp-config.update | — | 無判斷 | 入口鈕;送出在 SmtpConfigForm.vue(導頁帶 uid);僅 router 用 |
| src/views/smtp-config/SmtpConfigManage.vue:134 | 刪除(列內 icon) | DELETE /system/config/ |
system.smtp-config.delete | data.key != '*' | 無判斷 | 業務條件:預設網域 '*' 不可刪;僅 router 用 |
| src/views/storage-config/StorageConfigForm.vue:553 | 恢復系統內建儲存 | POST /system/config/storage/restore-default | system.storage-config.update | showRestoreButton(出廠快照存在且目前非內建儲存) | 無判斷 | 僅 router 用 |
| src/views/storage-config/StorageConfigForm.vue:561 | 儲存 | PUT /system/config/STORAGE_CONFIG/CONFIG | system.storage-config.update | — | 無判斷 | 群組 STORAGE_CONFIG;僅 router 用 |
| src/views/system-menu-manage/SystemMenuManage.vue:332 | 批次刪除 | DELETE /system-menu/ |
system.system-menu.delete | selectedMenus.length > 0 | 無判斷 | 僅 router 用 |
| src/views/system-menu-manage/SystemMenuManage.vue:348 | 編輯(列內 icon) | PUT /system-menu/ |
system.system-menu.update | — | 無判斷 | 開同檔 dialog(:401 送出);列出是因為實作棒要在入口隱藏,可與 :401 一併處理;僅 router 用 |
| src/views/system-menu-manage/SystemMenuManage.vue:349 | 刪除(列內 icon) | DELETE /system-menu/ |
system.system-menu.delete | — | 無判斷 | 僅 router 用 |
| src/views/system-menu-manage/SystemMenuManage.vue:401 | 儲存(新增/編輯 dialog) | 無 id:POST /system-menu;有 id:PUT /system-menu/ |
system.system-menu.create、system.system-menu.update | — | 無判斷 | 依 currentOption.id 決定新增或修改;新增入口在同檔 :338(開 dialog);僅 router 用 |
| 檔案:行號 | 按鈕 | 打的 API | 權限 | 專案層條件 | 現況 | 備註 |
|---|---|---|---|---|---|---|
| src/components/user-manage/UserInitialRolePicker.vue:56 | 新增角色列(新增帳號時直接指派角色) | POST /user(payload 帶 user_roles;送出在 UserMTRBACForm.vue:451) | iam.user.assign | — | 無判斷 | 入口鈕;對話框在 UserInitialRoleDialog.vue(只回填前端列,不打 API),角色列隨帳號存檔送出,後端 service 層另要 iam.user.assign。僅 UserMTRBACForm.vue 用(mode==='ADD') |
| src/components/user-manage/UserRoleSummary.vue:44 | 新增指派(帳號頁就地開指派對話框) | POST /role-assignment(送出在 AssignmentDialog.vue:210) | iam.user.assign | userUid 有值 |
已有且正確 | 入口鈕;用 myCaps.has('iam.user.assign')。僅 UserMTRBACForm.vue 用(編輯模式) |
| src/views/access/AssignmentDialog.vue:210 | 新增指派/儲存(編輯期間) | POST /role-assignment(新增,多人逐筆送);PUT /role-assignment/ |
iam.user.assign | — | 無判斷 | 共用(被 AssignmentManage.vue、RoleDetail.vue、UserRoleSummary.vue 用),判斷放 props 由頁面傳入;三個入口今天都已用 iam.user.assign 擋 |
| src/views/access/AssignmentManage.vue:140 | 新增指派 | POST /role-assignment(送出在 AssignmentDialog.vue:210) | iam.user.assign | — | 已有且正確 | 入口鈕;v-if="canAssign"=myCaps.has('iam.user.assign') |
| src/views/access/AssignmentManage.vue:199 | 列內編輯指派(鉛筆) | PUT /role-assignment/ |
iam.user.assign | — | 已有且正確 | 入口鈕;整欄 v-if="canAssign" |
| src/views/access/AssignmentManage.vue:201 | 列內刪除指派 | DELETE /role-assignment/ |
iam.user.assign | — | 已有且正確 | 整欄 v-if="canAssign";RoleAssignmentService.remove |
| src/views/access/RoleDetailHeader.vue:38 | 指派此角色 | emit assign → 由 RoleDetail.vue 處理(開 AssignmentDialog → POST /role-assignment) |
iam.user.assign | !isNew && role.enable === 1 |
已有且正確 | prop canAssign 由 RoleDetail.vue:39 算:!isNew && role.enable===1 && can('iam.user.assign')。僅 RoleDetail.vue 用 |
| src/views/access/RoleDetailHeader.vue:40 | 複製為新角色 | emit copy → 由 RoleDetail.vue 處理(導頁 access-role-new,存檔走 POST /role) |
iam.role.create | — | 已有且正確 | 入口鈕(導頁);prop canCopy=can('iam.role.create')。僅 RoleDetail.vue 用 |
| src/views/access/RoleDetailHeader.vue:42 | 匯出角色 | emit export → 由 RoleDetail.vue 處理(POST /roles/export) |
iam.role.export | — | 已有且正確 | prop canExport=can('iam.role.export')。僅 RoleDetail.vue 用 |
| src/views/access/RoleImportDialog.vue:104 | 匯入(送出角色 JSON) | POST /roles/import | iam.role.import | — | 無判斷 | 對話框本身不判;入口 RoleList.vue:180 已用 can('iam.role.import') 擋。僅 RoleList.vue 用 |
| src/views/access/RoleList.vue:177 | 匯出全部 | POST /roles/export(先 POST /roles 取 uid) | iam.role.export | — | 已有且正確 | v-if="can('iam.role.export')" |
| src/views/access/RoleList.vue:180 | 匯入 | POST /roles/import(送出在 RoleImportDialog.vue:104) | iam.role.import | — | 已有且正確 | 入口鈕;v-if="can('iam.role.import')" |
| src/views/access/RoleList.vue:183 | 新增角色 | POST /role(導頁 access-role-new,送出在 RoleSaveBar.vue:39) | iam.role.create | — | 已有且正確 | 入口鈕(導頁);v-if="can('iam.role.create')" |
| src/views/access/RoleList.vue:245 | 列內複製 | POST /role(導頁 access-role-new?template=,送出在 RoleSaveBar.vue:39) | iam.role.create | — | 已有且正確 | 入口鈕(導頁);v-if="can('iam.role.create')" |
| src/views/access/RoleList.vue:247 | 列內匯出 | POST /roles/export | iam.role.export | — | 已有且正確 | v-if="can('iam.role.export')" |
| src/views/access/RoleList.vue:250 | 列內刪除 | DELETE /role/ |
iam.role.delete | !data.is_system |
已有且正確 | v-if="!data.is_system && can('iam.role.delete')" |
| src/views/access/RoleSaveBar.vue:39 | 建立角色/儲存變更 | emit save → 由 RoleDetail.vue 處理(新增 POST /role;編輯 PUT /role/ |
iam.role.create、iam.role.update | !isSystem(內建角色唯讀) |
已有且正確 | 依模式擇一(isNew→create、否則 update);RoleDetail.vue:152 v-if="!readonly",readonly=isSystem || (isNew ? !can('iam.role.create') : !can('iam.role.update'))。僅 RoleDetail.vue 用 |
| src/views/auth/ChangePassword.vue:210 | 儲存新密碼 | POST /user/change-pwd-by-req | 免判斷(附錄 C) | — | 免判斷 | 免登入:忘記密碼/首登改密碼 |
| src/views/auth/ForgotPassword.vue:140 | 送出(寄重設信) | POST /forget-password | 免判斷(附錄 C) | — | 免判斷 | 免登入 |
| src/views/auth/Login.vue:386 | 登入 | POST /login | 免判斷(附錄 C) | — | 免判斷 | 免登入;成功後另打 GET /user/web-menu(附錄 C 本人) |
| src/views/auth/OtpVerification.vue:338 | 驗證 OTP | POST /otp-verify | 免判斷(附錄 C) | — | 免判斷 | 免登入:兩階段驗證 |
| src/views/auth/OtpVerification.vue:341 | 重新寄送驗證碼 | POST /otp-resend | 免判斷(附錄 C) | mfa_type === 'email' |
免判斷 | 免登入 |
| src/views/auth/OtpVerification.vue:344 | 改用 E-Mail 驗證 | POST /otp-resend(type=email) | 免判斷(附錄 C) | mfa_type !== 'email' |
免判斷 | 免登入 |
| src/views/org-unit/OrgUnitManage.vue:283 | 列內刪除部門 | DELETE /org-unit/ |
iam.org-unit.delete | — | 無判斷 | 只有授權唯讀反灰 writeDisabled |
| src/views/org-unit/OrgUnitManage.vue:381 | 部門對話框儲存 | POST /org-unit(新增);PUT /org-unit/ |
iam.org-unit.create、iam.org-unit.update | — | 無判斷 | 依模式擇一(currentData.uid 有值=編輯);入口在同檔 :275 新增、:282/:291 編輯(實作棒入口要分別用 create/update 擋) |
| src/views/tenant-manage/TenantManage.vue:263 | 列內刪除租戶 | DELETE /tenant/ |
iam.tenant.delete | — | 無判斷 | 連授權唯讀 writeDisabled 都沒有 |
| src/views/tenant-manage/TenantManage.vue:343 | 租戶對話框儲存 | POST /tenant(新增);PUT /tenant/ |
iam.tenant.create、iam.tenant.update | — | 無判斷 | 依模式擇一;入口在同檔 :255 新增、:262 編輯;全頁無 writeDisabled |
| src/views/user-manage/UserMTRBACForm.vue:451 | 帳號表單儲存 | POST /user(mode ADD);PUT /user/ |
iam.user.create、iam.user.update | mode === 'ADD' → create;否則 update |
無判斷 | 依模式擇一;新增帶 user_roles 時後端 service 層另要 iam.user.assign(入口見 UserInitialRolePicker.vue:56);只有 writeDisabled |
| src/views/user-manage/UserManage.vue:264 | 新增使用者 | POST /user(導頁 user-form,送出在 UserMTRBACForm.vue:451) | iam.user.create | — | 無判斷 | 入口鈕(導頁);只有 writeDisabled。僅 UserManageTabView.vue 用 |
| src/views/user-manage/UserManage.vue:276 | 列內停用/啟用帳號 | POST /user/change-status/ |
iam.user.update | data.login_name !== 'admin' && data.login_name !== userInfo.login_name |
無判斷 | 只有 writeDisabled。僅 UserManageTabView.vue 用 |
| src/views/user-manage/UserManage.vue:284 | 列內刪除帳號 | POST /user/change-status/ |
iam.user.update | data.login_name !== 'admin' && data.login_name !== userInfo.login_name |
無判斷 | 刪除走 change-status 不走 DELETE /user/ |
| src/views/user-manage/UserManageUpload.vue:260 | 下載 Excel 範本 | GET /user/download/sample | iam.user.import | — | 無判斷 | 僅 UserManageTabView.vue 用 |
| src/views/user-manage/UserManageUpload.vue:271 | 上傳區(點選/拖放選檔即上傳解析) | POST /users/upload | iam.user.import | — | 無判斷 | 原生 <button> dropzone;無 writeDisabled |
| src/views/user-manage/UserManageUpload.vue:312 | 建立勾選帳號 | POST /user/batch-upload | iam.user.import | — | 無判斷 | 只有 writeDisabled;payload 帶 Excel 的角色(後端是否另要 iam.user.assign 未見附錄 A 註記) |
| src/views/user-manage/UserManageUpload.vue:339 | 列編輯儲存(打勾 icon,重驗該列) | POST /user/validate | iam.user.import | — | 無判斷 | DataTable rowEditor 的 save icon(@row-edit-save) |
| src/views/user-manage/UserProfileForm.vue:549 | 個人資料儲存 | PUT /user-profile/ |
免判斷(附錄 C) | — | 免判斷 | 只驗登入:本人 |
| src/views/user-manage/UserProfileForm.vue:582 | 解除 LDAP 綁定 | DELETE /auth-provider/ |
iam.user.update(本人豁免) | isBindLdap |
免判斷 | 此頁一律操作本人,落在附錄 C 末段「本人免能力點」 |
| src/views/user-manage/UserProfileForm.vue:605 | 啟用/關閉 TOTP(InputSwitch,即時生效) | PUT /user-mfa-type(切到 totp 時先打 POST /totp-secret) | 免判斷(附錄 C) | — | 免判斷 | 只驗登入:本人;非 Button 但會直接寫入 |
| src/views/user-manage/UserProfileForm.vue:651 | LDAP 綁定對話框儲存 | POST /auth-provider | iam.user.update(本人豁免) | — | 免判斷 | user_uid=登入者本人;入口同檔 :578 |
| src/views/user-manage/UserProfileForm.vue:674 | 驗證並啟用 TOTP | POST /totp-verify+PUT /user-mfa-type | 免判斷(附錄 C) | totpSetupMode |
免判斷 | 只驗登入:本人 |
| 檔案:行號 | 按鈕 | 打的 API | 權限 | 專案層條件 | 現況 | 備註 |
|---|---|---|---|---|---|---|
| src/views/action-log/UserLogs.vue:192 | 匯出 | GET /log/api-logs/export | platform.api-log.export | — | 無判斷 | 僅 user-logs 路由用 |
| src/views/ai-quota/AiQuotaCeilingTable.vue:126 | 儲存上限(點列開窗編輯後送出) | PUT /system/ai-quota/tenants/ |
platform.ai-quota-ceiling.update | — | 已有但粗 | 父層 AiQuotaPage.vue:161 用 isPlatformAdmin && isRoot 旗標決定整塊顯示,不是 hasCap;僅 AiQuotaPage.vue 用 |
| src/views/google-drive-app-config/GoogleDriveAppConfigForm.vue:304 | 清除密鑰(垃圾桶 icon) | 無即時 API,僅標記清除;按「儲存」時以 PUT /system/config/GOOGLE_DRIVE_APP_CONFIG/CONFIG 帶 clear_client_secret 送出 | platform.drive-app-credential.update | — | 無判斷 | 群組 GOOGLE_DRIVE_APP_CONFIG;頁面另有 canManage=平台管理員旗標 DRIVE_APP_CONFIG_PLATFORM_ADMIN_ONLY(非能力點);僅 router 用 |
| src/views/google-drive-app-config/GoogleDriveAppConfigForm.vue:448 | 驗證憑證 | POST /integrations/google-drive/verify-credentials | platform.drive-app-credential.read | — | 無判斷 | 不存檔、只驗;附錄 A 取 read;僅 router 用 |
| src/views/google-drive-app-config/GoogleDriveAppConfigForm.vue:452 | 儲存 | PUT /system/config/GOOGLE_DRIVE_APP_CONFIG/CONFIG | platform.drive-app-credential.update | — | 無判斷 | 群組 GOOGLE_DRIVE_APP_CONFIG;僅 router 用 |
| src/views/license/LicenseActivation.vue:283 | 線上開通 | POST /license/activation/online | 免判斷(附錄 C) | — | 免判斷 | 只在 host 版顯示 |
| src/views/license/LicenseActivation.vue:327 | 開通(上傳授權檔) | POST /license/activation/upload | 免判斷(附錄 C) | — | 免判斷 | 只在 host 版顯示 |
| src/views/license/LicenseManage.vue:440 | 解除停權(列內,confirm 後送) | POST /license/tenants/ |
platform.license.update | data.has_license && data.suspended_at | 無判斷 | |
| src/views/license/LicenseManage.vue:524 | 停權(dialog 確認) | POST /license/tenants/ |
platform.license.update | data.has_license && !data.suspended_at(入口 :432) | 無判斷 | 入口 :432 同檔 |
| src/views/license/LicenseManage.vue:571 | 指派/換發儲存(dialog) | POST /license/tenants/ |
platform.license.assign | — | 無判斷 | 入口 :417 同檔 |
| src/views/license/LicenseManage.vue:663 | 向 LC 請照儲存(新簽/展延 dialog) | POST /license/tenants/ |
platform.license.assign、platform.license.update | — | 無判斷 | 依 lcMode:新簽=assign、展延=update;需 hasAny。入口 :411 同檔 |
| src/views/support/SupportPage.vue:226 | 匯出診斷包 | POST /support/diagnostic-bundle | platform.support.export | — | 無判斷 | 頁面另有 canManage=平台管理員旗標 SUPPORT_PAGE_PLATFORM_ADMIN_ONLY(非能力點);僅 router 用 |
| 檔案:行號 | 按鈕 | 打的 API | 權限 | 專案層條件 | 現況 | 備註 |
|---|---|---|---|---|---|---|
| src/views/device/DeviceExportMenu.vue:39 | 匯出(Menu 兩項 Excel/CSV,:33-34) | POST /devices/export | asset.device.export | — | 已有但粗 | 父層 DeviceManage.vue:361 用 v-if canRead=hasCap('asset.device.read');僅 DeviceManage.vue 用 |
| src/views/device/DeviceImportDialog.vue:120 | 下載範本 | GET /devices/import-template | asset.device.import | — | 無判斷 | dialog 內本身無判斷;入口 DeviceManage.vue:362 已用 asset.device.create 遮;僅 DeviceManage.vue 用 |
| src/views/device/DeviceImportDialog.vue:122 | 選擇檔案(選檔即上傳驗證) | POST /devices/import/verify | asset.device.import | — | 無判斷 | 同上 |
| src/views/device/DeviceImportDialog.vue:163 | 確認匯入 | POST /devices/import/confirm | asset.device.import | — | 無判斷 | 同上;「更新既有設備」勾選框(:154)另用 hasCap('asset.device.update') |
| src/views/device/DeviceManage.vue:354 | 批次刪除 | DELETE /device/ |
asset.device.delete | selectedDevices.length > 0 | 已有且正確 | v-if canDelete=hasCap('asset.device.delete') |
| src/views/device/DeviceManage.vue:362 | 匯入 | 入口 → POST /devices/import/confirm | asset.device.import | — | 已有但名不對 | 用 hasCap('asset.device.create');入口鈕,送出在 DeviceImportDialog.vue:163 |
| src/views/device/DeviceManage.vue:376 | 刪除(列內) | DELETE /device/ |
asset.device.delete | — | 已有且正確 | v-if canDelete=hasCap('asset.device.delete') |
| src/views/device/DeviceManage.vue:604 | 儲存(新增/編輯 dialog) | POST /device 或 PUT /device/ |
asset.device.create、asset.device.update | — | 無判斷 | 依 currentData.uid 決定;需 hasAny。新增入口 :364、編輯入口 :375/名稱連結 :387 都在同檔,皆無判斷 |
| src/views/information-system/InformationSystemManage.vue:397 | 批次停用(刪除) | DELETE /information-system/ |
asset.information-system.delete | selectedSystems.length > 0 | 無判斷 | 後端為軟刪除(is_active=false) |
| src/views/information-system/InformationSystemManage.vue:436 | 停用(刪除,列內) | DELETE /information-system/ |
asset.information-system.delete | — | 無判斷 | |
| src/views/information-system/InformationSystemManage.vue:782 | 儲存(新增/編輯 dialog) | POST /information-systems 或 PUT /information-system/ |
asset.information-system.create、asset.information-system.update | — | 無判斷 | 依 currentData.uid 決定;需 hasAny。新增入口 :411、編輯入口 :432/名稱連結 :447 同檔,皆無判斷 |
| 檔案:行號 | 按鈕 | 打的 API | 權限 | 專案層條件 | 現況 | 備註 |
|---|---|---|---|---|---|---|
| src/components/ChatBox.vue:184 | 送出訊息(↑ 鈕;Enter 鍵 onEnter 走同一條) | POST /ai-chatbot | ai.assistant.read | — | 無判斷 | 僅 AppLayout.vue 用 |
| src/layout/AppLayout.vue:129 | AI 小幫手浮鈕(開對話窗) | 入口 → POST /ai-chatbot | ai.assistant.read | — | 無判斷 | 入口鈕;送出在 ChatBox.vue:184。現在只看 writeDisabled()(授權唯讀反灰),沒有能力點判斷 |
| src/views/ai-call-log/AiCallLogList.vue:227 | 匯出 | POST /system/ai-call-logs/export | ai.call-log.export | — | 無判斷 | 僅 AiUsagePage.vue 用 |
| src/views/ai-call-log/AiUsageSummary.vue:182 | 匯出 | POST /system/ai-call-logs/usage/export | ai.call-log.export | — | 無判斷 | 僅 AiUsagePage.vue 用 |
| src/views/ai-quota/AiQuotaPage.vue:141 | 回復預設 | DELETE /system/ai-quota | ai.quota.update | — | 已有且正確 | 外層 v-if canEdit = hasCap('ai.quota.update') |
| src/views/ai-quota/AiQuotaPage.vue:143 | 儲存 | PUT /system/ai-quota | ai.quota.update | — | 已有且正確 | 外層 v-if canEdit = hasCap('ai.quota.update') |
| src/views/ai-service-config/AiServiceConfigForm.vue:231 | 清除金鑰(垃圾桶 icon) | 無即時 API,僅標記清除;按「儲存」時以 PUT /system/config/AI_PROVIDER_CONFIG/CONFIG 帶 clear:true 送出 | ai.ai-provider-config.update | — | 無判斷 | 群組 AI_PROVIDER_CONFIG;頁面另有 canManage=平台管理員旗標 AI_SERVICE_CONFIG_PLATFORM_ADMIN_ONLY(非能力點);僅 router 用 |
| src/views/ai-service-config/AiServiceConfigForm.vue:304 | 儲存 | PUT /system/config/AI_PROVIDER_CONFIG/CONFIG | ai.ai-provider-config.update | — | 無判斷 | 群組 AI_PROVIDER_CONFIG;同上平台管理員旗標;僅 router 用 |
| src/views/dynamic-dashboard/DynamicDashboard.vue:34 | 重新產生 | POST /ai-dashboard/auto-generate | ai.dashboard.read | — | 無判斷 | |
| src/views/dynamic-dashboard/components/DashboardErrorMessage.vue:15 | 重試 | emit retry → 由 DynamicDashboard.vue 處理 → POST /ai-dashboard/auto-generate |
ai.dashboard.read | error.canRetry | 無判斷 | 僅 DynamicDashboard.vue 用 |
| src/views/dynamic-dashboard/components/DashboardPromptBar.vue:23 | 產生 | emit generate → 由 DynamicDashboard.vue 處理(useDashboardGenerator.generateDashboard)→ POST /ai-dashboard/auto-generate |
ai.dashboard.read | — | 無判斷 | 僅 DynamicDashboard.vue 用;輸入框 Enter 走同一條 |
| src/views/dynamic-dashboard/components/DashboardTemplates.vue:10 | 快速模板卡片(點了直接產生) | emit select → 由 DynamicDashboard.vue 處理(useTemplate → generateDashboard)→ POST /ai-dashboard/auto-generate |
ai.dashboard.read | — | 無判斷 | 僅 DynamicDashboard.vue 用;是可點 Card 不是 Button |
| 檔案:行號 | 按鈕 | 打的 API | 權限 | 專案層條件 | 現況 | 備註 |
|---|---|---|---|---|---|---|
| src/views/bulletins/BulletinForm.vue:310 | 儲存 | POST /bulletin 或 PUT /bulletin/ |
workspace.bulletin.create、workspace.bulletin.update | — | 無判斷 | 依 query.uid 決定新增或修改;需 hasAny(或依模式各判一個) |
| src/views/bulletins/BulletinManage.vue:208 | 新增公告 | 入口 → POST /bulletin | workspace.bulletin.create | !props.view | 無判斷 | 入口鈕;送出在 BulletinForm.vue:310。共用(bulletin-manage 路由直掛、BulletinList.vue 以 view=true 用),判斷放 props 由頁面傳入;view 模式已隱藏 |
| src/views/bulletins/BulletinManage.vue:216 | 編輯公告(列內) | 入口 → PUT /bulletin/ |
workspace.bulletin.update | canEdit(data)(= !props.view) | 無判斷 | 入口鈕;送出在 BulletinForm.vue:310。共用(同上) |
| src/views/bulletins/BulletinManage.vue:218 | 刪除公告(列內) | DELETE /bulletin/ |
workspace.bulletin.delete | canEdit(data)(= !props.view) | 無判斷 | 共用(同上) |
| src/views/feedback/system/SystemFeedbackForm.vue:432 | 下載附件 | GET /file/download/ |
免判斷(附錄 C) | — | 免判斷 | |
| src/views/feedback/system/SystemFeedbackForm.vue:442 | 刪除已上傳附件 | DELETE /feedback/file/ |
workspace.feedback.update | — | 無判斷 | |
| src/views/feedback/system/SystemFeedbackForm.vue:455 | 儲存 | POST /feedback 或 PUT /feedback/ |
workspace.feedback.create、workspace.feedback.update | — | 已有但名不對 | v-if page_permissions.includes('CREATE') || page_permissions.includes('UPDATE'),來源是 getUserPagePermissions('/feedback/system-feedback-manage') 的選單動作,不是 hasCap;需 hasAny |
| src/views/feedback/system/SystemFeedbackManage.vue:265 | 匯出 | POST /feedback/export/excel | workspace.feedback.export | — | 無判斷 | 共用(被 FeedbackManageTabView.vue 用,mine/all 兩條路由),判斷放 props 由頁面傳入 |
| src/views/feedback/system/SystemFeedbackManage.vue:266 | 新增 | 入口 → POST /feedback | workspace.feedback.create | !view | 已有但粗 | view=FeedbackManageTabView.vue:69 傳入 permissions.length === 1(選單頁能力點只有一個就當唯讀);入口鈕,送出在 SystemFeedbackForm.vue:455;共用(同上) |
| src/views/feedback/system/SystemFeedbackManage.vue:279 | 編輯(列內) | 入口 → PUT /feedback/ |
workspace.feedback.update | !view && canEdit(data)(canEdit:is_admin 或本人建立) | 已有但粗 | 同上 view 來源;入口鈕,送出在 SystemFeedbackForm.vue:455 |
| src/views/feedback/system/SystemFeedbackManage.vue:282 | 刪除(列內) | DELETE /feedback/ |
workspace.feedback.delete | !view && canEdit(data)(canEdit:is_admin 或本人建立) | 已有但粗 | 同上 view 來源 |
| src/views/feedback/system/SystemFeedbackManage.vue:303 | 標題連結(可編輯者進編輯) | 入口 → PUT /feedback/ |
workspace.feedback.update | !view && canEdit(data) | 已有但粗 | 與 :279 同為編輯入口;送出在 SystemFeedbackForm.vue:455 |
| src/views/feedback/system/SystemFeedbackView.vue:127 | 下載附件 | GET /file/download/ |
免判斷(附錄 C) | — | 免判斷 |
每支讀過的檔與寫入類按鈕數;0 顆的寫明原因。
| 檔案 | 行數 | 寫入類按鈕數 | 說明 |
|---|---|---|---|
| src/components/BlockViewer.vue | 209 | 0 | 範例程式碼展示框,只有 Preview/Code 切換與複製到剪貼簿 |
| src/components/ChatBox.vue | 391 | 1 | 送出訊息;最小化鈕是 emit close 純導覽 |
| src/components/access/PermissionMenuTree.vue | 145 | 0 | 勾選與「只看頁面」都只改前端選取,不打 API |
| src/components/access/PermissionResourceMatrix.vue | 63 | 0 | 純前端勾選矩陣 |
| src/components/access/PermissionTree.vue | 124 | 0 | 展開收合、切視角、搜尋;元件不打 API |
| src/components/access/TriStateCheckbox.vue | 24 | 0 | 三態勾選元件,只 emit toggle |
| src/components/access/VisibleMenuPreview.vue | 104 | 0 | 「加入」emit add-gate → RoleDetail 只改前端勾選;其餘收合 |
| src/components/compliance-framework/article/FrameworkArticleContent.vue | 380 | 8 | 全是 emit 給父層;鉛筆鈕只開 inline 編輯不列;還原鈕(:159/:200/:279)只在匯入頁本地還原、編輯頁永不出現,不打 API 不列 |
| src/components/compliance-framework/article/FrameworkArticlePane.vue | 93 | 0 | 純轉手(Splitter+PDF iframe+把 FrameworkArticleContent 的 emit 原樣往上丟),按鈕已列在 Content;僅 FrameworkVersionEditView.vue 用 |
| src/components/compliance-framework/import/AssessmentListEditor.vue | 149 | 0 | 只改 draftKit 本地草稿不打 API;且全 repo 無人 import,疑似死元件 |
| src/components/compliance-framework/import/ControlListEditor.vue | 170 | 0 | 同上(本地草稿、無人 import) |
| src/components/compliance-framework/import/GroupTreeEditor.vue | 136 | 0 | 同上(本地草稿、無人 import) |
| src/components/grc/AuditControlRef.vue | 700 | 1 | 唯讀參考面板;僅證據下載(免判斷),其餘為預覽/展開收合/外部連結 |
| src/components/grc/AuditTreeNav.vue | 194 | 0 | 純導覽樹:搜尋、篩選待稽核、展開收合、emit select |
| src/components/grc/ComponentsLeveragedInventoryTab.vue | 1562 | 8 | 四區塊(元件/外部授權/設備資產/系統資產)各「刪除」+dialog「儲存」;SSP 與 MF 兩情境各列一行,共 16 列 |
| src/components/grc/DepartmentsPanel.vue | 99 | 0 | 只 emit add/remove、資料型別來自 grc-mock-data;全 repo 無人引用(死碼),無 API |
| src/components/grc/DocumentLinkDialog.vue | 378 | 1 | 確認關聯(增刪 mapping) |
| src/components/grc/DocumentPoolPanel.vue | 416 | 3 | 上傳、刪除、下載(下載免判斷);預覽為唯讀不列 |
| src/components/grc/DownloadSspBlankTemplateDialog.vue | 140 | 1 | 下載空白範本 |
| src/components/grc/EditIncludeControlsDialog.vue | 335 | 1 | 儲存 include_controls;展開/收合不列 |
| src/components/grc/EmptyState.vue | 20 | 0 | 純展示;action 按鈕由呼叫端傳入,現有兩個使用者(StageTransitionTimeline、EffectiveAccessView)都沒傳 action |
| src/components/grc/FlowPhaseBanner.vue | 627 | 3 | 階段推進/審核決策/返回上一階段 |
| src/components/grc/JobExecutionDrawer.vue | 3152 | 24 | 任務抽屜:檢測執行群組動作、證據檔案與連結、留言、完成/退回入口 |
| src/components/grc/JobImportDialog.vue | 425 | 3 | 任務批次匯入三步驟 |
| src/components/grc/ModuleFrameBasicInfoPanel.vue | 214 | 2 | 分享開關、儲存 |
| src/components/grc/ModuleFrameDocumentPoolPanel.vue | 442 | 3 | 上傳、刪除、下載(下載免判斷) |
| src/components/grc/ModuleFramePartiesPanel.vue | 529 | 2 | 刪除、dialog 儲存;SSP/MF 兩情境各列一行,共 4 列 |
| src/components/grc/ModuleFrameSspDevicesPanel.vue | 332 | 2 | 刪除、儲存;所在 dialog 無人引用 |
| src/components/grc/ModuleFrameSspInfoSystemsPanel.vue | 332 | 2 | 刪除、儲存;所在 dialog 無人引用 |
| src/components/grc/ModuleFrameTemplateImportDialog.vue | 580 | 3 | 上傳驗證/重新驗證/確認匯入;dropzone 內按鈕只開檔案選擇器、篩選 tab 為本地篩選不列 |
| src/components/grc/OpenInDriveButton.vue | 22 | 0 | 純 window.open 外連 Drive,屬導覽 |
| src/components/grc/ParticipantPicker.vue | 176 | 0 | 只改父層 v-model(新增/移除/換角色),不打 API;送出在 ProjectParticipantsPanel/ProjectCreateView |
| src/components/grc/PreviousFindingsPanel.vue | 153 | 0 | 唯讀展示,只有展開收合 |
| src/components/grc/ReferenceDocumentList.vue | 323 | 3 | 關聯程序書(入口)、取消關聯、下載;remove/upload 兩條 emit 無任何父層接(見觀察) |
| src/components/grc/RoundSwitcherBar.vue | 120 | 0 | 輪次切換 tab(導覽);全 repo 無人引用 |
| src/components/grc/SspImportDialog.vue | 501 | 3 | 上傳、重新驗證、確認匯入;篩選鈕/選檔鈕不列 |
| src/components/grc/SspSystemForm.vue | 201 | 0 | 儲存鈕 :194 是 TODO stub,只跳「尚未開放」toast 不打 API;全 repo 無人引用 |
| src/components/grc/StatCard.vue | 36 | 0 | 統計卡,只 emit click 開浮層 |
| src/components/grc/UnfilledListOverlay.vue | 68 | 0 | 浮層清單,點項目跳轉(導覽) |
| src/components/grc/ap-docx-import/ApDocxImportDialog.vue | 795 | 2 | 選檔上傳解析+確認匯入;取消/關閉為導覽 |
| src/components/grc/ar-import/ArImportDialog.vue | 718 | 3 | 選檔上傳解析+捨棄+確認匯入 |
| src/components/grc/job-drawer/JobActionCommentDialog.vue | 67 | 2 | 完成/退回送出鈕(emit 給抽屜) |
| src/components/grc/job-drawer/JobBlockedNotice.vue | 106 | 1 | 強制開始(emit 給父層) |
| src/components/grc/job-drawer/JobDrawerHeader.vue | 91 | 0 | 只有關閉鈕(導覽) |
| src/components/grc/planning/AoJobSection.vue | 149 | 0 | 任務列只 emit open(開抽屜),無寫入 |
| src/components/grc/planning/AoTaskForm.vue | 718 | 2 | 刪除/儲存(emit);分派列增刪複製、排程皆本地狀態不打 API |
| src/components/grc/planning/AoTaskSection.vue | 270 | 2 | 新增任務(兩處入口,emit) |
| src/components/grc/planning/PlanningStatsBar.vue | 106 | 0 | 統計卡+清單 overlay,只 emit 跳轉 |
| src/components/grc/project/ProjectBasicInfoPanel.vue | 144 | 1 | 儲存 |
| src/components/grc/project/ProjectCloudIntegrationsPanel.vue | 157 | 1 | 檢查並修復確認鈕;「開啟雲端資料夾」為外連導覽不列 |
| src/components/grc/project/ProjectParticipantsPanel.vue | 108 | 1 | 儲存 |
| src/components/grc/ssp-docx-import-v2/SspDocxImportPage.vue | 1344 | 2 | 上傳 dropzone+確認匯入;取消/上一步會順手 DELETE 暫存解析,屬導覽不列 |
| src/components/grc/ssp-docx-import-v2/diff/ComponentDiffCard.vue | 271 | 0 | SelectButton emit update:action → 只寫本地 diff store,最終由 SspDocxImportPage 確認鈕送出 |
| src/components/grc/ssp-docx-import-v2/diff/ComponentsDiffSection.vue | 104 | 0 | 只呼叫 store.setComponentDecision/bulkApply/setFilter(本地狀態) |
| src/components/grc/ssp-docx-import-v2/diff/ControlDiffCard.vue | 301 | 0 | SelectButton emit 決策,不打 API |
| src/components/grc/ssp-docx-import-v2/diff/ControlDiffSection.vue | 150 | 0 | 只寫本地 diff store |
| src/components/grc/ssp-docx-import-v2/diff/DiffResolutionStep.vue | 273 | 0 | 上一步/下一步 emit prev/next(導覽) |
| src/components/grc/ssp-docx-import-v2/diff/DiffSectionShell.vue | 130 | 0 | 「全部採用新值/全部保留現值」只 emit bulk-apply 改本地決策,不打 API |
| src/components/grc/ssp-docx-import-v2/diff/InventoryItemDiffCard.vue | 273 | 0 | SelectButton emit 決策,不打 API |
| src/components/grc/ssp-docx-import-v2/diff/InventoryItemsDiffSection.vue | 101 | 0 | 只寫本地 diff store |
| src/components/grc/ssp-docx-import-v2/diff/LeveragedAuthorizationDiffCard.vue | 266 | 0 | SelectButton emit 決策,不打 API |
| src/components/grc/ssp-docx-import-v2/diff/LeveragedAuthorizationsDiffSection.vue | 101 | 0 | 只寫本地 diff store |
| src/components/grc/ssp-docx-import-v2/diff/ObjectiveDiffRow.vue | 226 | 0 | SelectButton emit 決策,不打 API |
| src/components/grc/ssp-docx-import-v2/diff/PartiesDiffSection.vue | 106 | 0 | 只寫本地 diff store |
| src/components/grc/ssp-docx-import-v2/diff/PartyDiffCard.vue | 294 | 0 | SelectButton emit 決策,不打 API |
| src/components/grc/ssp-docx-import-v2/diff/SystemCharacteristicDiffSection.vue | 320 | 0 | 只呼叫 store.setScDecision(本地) |
| src/components/grc/ssp-docx-import-v2/sections/ControlImplSection.vue | 471 | 0 | 「編輯/儲存」只 emit update:override 寫入頁面 draft(localStorage),不打 API |
| src/components/grc/ssp-docx-import-v2/sections/LeveragedSection.vue | 247 | 0 | 表格內欄位編輯 emit update-la/update-comp 寫 draft,無按鈕 |
| src/components/grc/ssp-docx-import-v2/sections/MetadataSection.vue | 49 | 0 | 已不被 SspDocxImportPage 引用(import 已註解);只有輸入欄 |
| src/components/grc/ssp-docx-import-v2/sections/PartiesSection.vue | 387 | 0 | 連結/取消連結只改 draft,搜尋候選在 PartyLinkDialog |
| src/components/grc/ssp-docx-import-v2/sections/PartyLinkDialog.vue | 127 | 0 | 搜尋鈕打 POST /users、POST /org-units 屬查詢(iam.user.read/iam.org-unit.read),選中只 emit linked |
| src/components/grc/ssp-docx-import-v2/sections/SystemCharacteristicSection.vue | 115 | 0 | 只有輸入欄,emit update 寫 draft |
| src/components/grc/ssp-docx-import-v2/sections/UnmatchedSection.vue | 48 | 0 | 純顯示(且已無引用) |
| src/components/grc/ssp-excel-import/EntityPickerDialog.vue | 146 | 1 | 確認鈕 emit pick(死碼鏈) |
| src/components/grc/ssp-excel-import/InlineCreateDialog.vue | 174 | 1 | 確認鈕 emit save(死碼鏈) |
| src/components/grc/ssp-excel-import/MatchStatusCell.vue | 71 | 0 | 純顯示 Tag,無按鈕 |
| src/components/grc/ssp-excel-import/RowActionMenu.vue | 116 | 1 | Menu 兩項寫 store;pick/inline_new 兩項只開同檔子 dialog 不另列;⋯ 按鈕只開選單 |
| src/components/grc/ssp-excel-import/SheetPreviewBasic.vue | 161 | 0 | 只有表單欄位寫 store override,無按鈕 |
| src/components/grc/ssp-excel-import/SheetPreviewComponents.vue | 250 | 1 | 移除/復原 |
| src/components/grc/ssp-excel-import/SheetPreviewControls.vue | 567 | 0 | 只有 Checkbox(納入/群組全選)與欄位編輯;全部展開/收合為導覽 |
| src/components/grc/ssp-excel-import/SheetPreviewDevices.vue | 163 | 1 | 移除/復原(死碼) |
| src/components/grc/ssp-excel-import/SheetPreviewInfoSystems.vue | 143 | 1 | 移除/復原(死碼) |
| src/components/grc/ssp-excel-import/SheetPreviewInventoryItems.vue | 251 | 1 | 移除/復原 |
| src/components/grc/ssp-excel-import/SheetPreviewLeveraged.vue | 229 | 1 | 移除/復原(死碼) |
| src/components/grc/ssp-excel-import/SheetPreviewLeveragedAuth.vue | 230 | 1 | 移除/復原 |
| src/components/grc/ssp-excel-import/SheetPreviewParties.vue | 351 | 2 | 組織、人員各一組移除/復原 |
| src/components/grc/ssp-excel-import/SheetPreviewSystemAssets.vue | 196 | 1 | 移除/復原 |
| src/components/grc/ssp-excel-import/SheetPreviewSystemCharacteristic.vue | 262 | 0 | 只有表單欄位寫 store override,無按鈕 |
| src/components/grc/ssp/SspBasicSection.vue | 328 | 1 | 儲存 |
| src/components/grc/ssp/SspLeveragedSection.vue | 378 | 2 | 刪除、dialog 儲存(死碼);新增/編輯鈕只開同檔 dialog |
| src/components/grc/ssp/SspTabPanel.vue | 510 | 2 | 下載 Excel、dialog 解析上傳;「從 Excel 匯入」入口鈕開同檔 dialog 不另列 |
| src/components/integrations/GoogleDriveIntegrationCard.vue | 446 | 5 | 連線/錯誤區重新授權/⋯選單重新授權/重建全部/中斷連線;「查看同步紀錄」選單項只開 dialog 不列 |
| src/components/integrations/SyncJobHistoryDialog.vue | 157 | 1 | 重試;重新整理鈕為 GET 不列 |
| src/components/license/LicenseExpiryBanner.vue | 115 | 0 | 「續約」是 router.push 到開通頁的導覽連結 |
| src/components/user-manage/UserInitialRoleDialog.vue | 69 | 0 | 「加入」只 emit add 回填前端列,不打 API(實際寫入隨 POST /user) |
| src/components/user-manage/UserInitialRolePicker.vue | 81 | 1 | 刪除列為純前端 |
| src/components/user-manage/UserMembershipEditor.vue | 82 | 0 | 只有 MultiSelect,無按鈕 |
| src/components/user-manage/UserRoleSummary.vue | 58 | 1 | 「管理指派」為導頁 |
| src/components/user-manage/UserTenantGroupList.vue | 91 | 0 | 唯讀顯示 |
| src/layout/AppLayout.vue(只看 :129-140) | — | 1 | 主 session 指定加看的 AI 小幫手浮鈕 |
| src/views/access/AssignmentDialog.vue | 220 | 1 | 取消為導覽 |
| src/views/access/AssignmentFormBody.vue | 99 | 0 | 表單欄位+預覽,無送出鈕 |
| src/views/access/AssignmentManage.vue | 228 | 3 | 分組切換、篩選、顯示過期皆前端 |
| src/views/access/AssignmentMenuPreview.vue | 59 | 0 | 唯讀預覽 |
| src/views/access/CheckAccessBar.vue | 83 | 0 | 選人/清除只改網址 query |
| src/views/access/EffectiveAccessView.vue | 99 | 0 | 唯讀結果頁 |
| src/views/access/RoleDetail.vue | 174 | 0 | 按鈕都在子元件(Header/SaveBar);本檔處理 emit,已計在子元件列 |
| src/views/access/RoleDetailHeader.vue | 67 | 3 | 範本下拉只套前端勾選 |
| src/views/access/RoleImportDialog.vue | 111 | 1 | 選檔為前端解析 |
| src/views/access/RoleInfoCard.vue | 52 | 0 | 「查看指派」為導頁 |
| src/views/access/RoleList.vue | 265 | 6 | 角色名連結、檢視/編輯鈕為導頁 |
| src/views/access/RolePatternCard.vue | 29 | 0 | 唯讀規則清單 |
| src/views/access/RoleSaveBar.vue | 49 | 1 | 「捨棄」為前端還原 |
| src/views/action-log/UserLogs.vue | 234 | 1 | 匯出;其餘為查詢/重置 |
| src/views/ai-call-log/AiCallLogList.vue | 293 | 1 | 匯出;列點擊開明細只打 GET |
| src/views/ai-call-log/AiUsagePage.vue | 42 | 0 | 只是兩個分頁的外殼 |
| src/views/ai-call-log/AiUsageSummary.vue | 241 | 1 | 匯出;「明細 →」是切分頁下鑽、展開鈕是展開收合 |
| src/views/ai-quota/AiQuotaCeilingTable.vue | 138 | 1 | 儲存上限;點列開窗同檔 |
| src/views/ai-quota/AiQuotaForm.vue | 101 | 0 | 純輸入欄位元件,送出在父層 |
| src/views/ai-quota/AiQuotaPage.vue | 168 | 2 | 回復預設、儲存 |
| src/views/ai-quota/AiQuotaUsageCards.vue | 87 | 0 | 純顯示統計卡 |
| src/views/ai-service-config/AiServiceConfigForm.vue | 311 | 2 | 清除金鑰+儲存;「回復繼承」模型鈕是純前端重設不列 |
| src/views/auth/ChangePassword.vue | 243 | 1 | 返回登入為導覽 |
| src/views/auth/ForgotPassword.vue | 164 | 1 | 返回為導覽 |
| src/views/auth/Login.vue | 730 | 1 | 登入方式 tab、顯示密碼、忘記密碼連結、語言、主題切換皆前端 |
| src/views/auth/OtpVerification.vue | 363 | 3 | — |
| src/views/bulletins/BulletinForm.vue | 323 | 1 | 儲存;取消是返回 |
| src/views/bulletins/BulletinList.vue | 8 | 0 | 以 view 模式包 BulletinManage,本身無按鈕 |
| src/views/bulletins/BulletinManage.vue | 291 | 3 | 新增入口、編輯入口、刪除;瀏覽鈕是導覽 |
| src/views/bulletins/BulletinView.vue | 94 | 0 | 唯讀檢視頁,只有返回 |
| src/views/compliance-framework/ComplianceFrameworkManage.vue | 548 | 3 | 新增鈕/選單編輯只開同檔 dialog 不列;名稱連結與「檢視版本」為導覽 |
| src/views/compliance-framework/ComplianceFrameworkVersionManage.vue | 739 | 6 | 新增鈕/選單編輯開同檔 dialog 不列;子版本內容、版本連結為導覽;Excel 下載已註解停用 |
| src/views/compliance-framework/FrameworkImportPage.vue | 775 | 5 | 另內嵌 FrameworkArticleContent(:619,本地草稿編輯,已列在元件);看草稿、開啟草稿、重新上傳、選檔區為導覽/本地 |
| src/views/compliance-framework/FrameworkVersionEditView.vue | 346 | 1 | 條文的改/刪由 FrameworkArticlePane emit 到本頁 handler 打 API,已列在 FrameworkArticleContent 列;選擇檔案、回列表為本地/導覽 |
| src/views/default/NotFound.vue | 131 | 0 | 404 頁,只有回首頁 |
| src/views/default/SystemLocked.vue | 209 | 0 | 系統鎖定頁,只有複製到剪貼簿 |
| src/views/detection-profile/DetectionProfileControlsView.vue | 721 | 0 | 本檔只有返回、全部展開、篩選;重新解析鈕在子元件 ControlExtractionSummary(已列在該檔,API 由本檔 handleRetry:329 打) |
| src/views/detection-profile/DetectionProfileManageView.vue | 968 | 8 | 新增鈕+kebab 選單 7 項寫入(檢視/控制項為讀取不列) |
| src/views/detection-profile/components/ControlExtractionSummary.vue | 423 | 3 | 三顆重新解析/開始解析都 emit retry |
| src/views/detection-profile/components/ProfileCopyDialog.vue | 153 | 1 | fork/copy 共用送出鈕 |
| src/views/detection-profile/components/ProfileDetailDialog.vue | 170 | 0 | 唯讀詳細資料,只有關閉鈕 |
| src/views/detection-profile/components/ProfileFormDialog.vue | 458 | 1 | 新增/編輯共用送出鈕 |
| src/views/detection-profile/components/ProfileNewVersionDialog.vue | 179 | 1 | |
| src/views/detection-profile/components/ProfileSourceDialog.vue | 211 | 1 | |
| src/views/detection-profile/components/ProfileVersionTable.vue | 595 | 3 | 版本列 kebab 選單三項寫入(控制項為導覽不列) |
| src/views/device/DeviceExportMenu.vue | 42 | 1 | 匯出選單(兩種格式算一顆) |
| src/views/device/DeviceImportDialog.vue | 168 | 3 | 下載範本、選檔驗證、確認匯入 |
| src/views/device/DeviceManage.vue | 609 | 4 | 批次刪除、匯入入口、列內刪除、dialog 儲存(新增/編輯入口同檔不列) |
| src/views/dynamic-dashboard/DynamicDashboard.vue | 130 | 1 | 重新產生 |
| src/views/dynamic-dashboard/components/DashboardBattleRoom.vue | 80 | 0 | 只有展開/收合 |
| src/views/dynamic-dashboard/components/DashboardErrorMessage.vue | 36 | 1 | 重試(emit) |
| src/views/dynamic-dashboard/components/DashboardLoadingState.vue | 37 | 0 | 純顯示 |
| src/views/dynamic-dashboard/components/DashboardPromptBar.vue | 63 | 1 | 產生(emit) |
| src/views/dynamic-dashboard/components/DashboardTemplates.vue | 57 | 1 | 模板卡片(emit,點即產生) |
| src/views/evidence-classification/AutoClassifyBatch.vue | 1007 | 5 | 上傳/移除檔/開始分類/清理/歸檔確認;開 dialog 的入口鈕(:680、:702)與返回清單不列 |
| src/views/evidence-classification/AutoClassifyList.vue | 588 | 4 | 新增×2/清理/刪除確認;繼續、查看為導覽 |
| src/views/evidence-classification/EvidenceClassificationReview.vue | 523 | 1 | 歸檔入口(emit);「重跑」鈕為 router.back() 導覽 |
| src/views/evidence-classification/components/AOPickerDialog.vue | 287 | 0 | 確認只 emit diff 給 FileDetail 改本地 state,不打 API(由頁頭儲存鈕統一 PUT) |
| src/views/evidence-classification/components/ClassificationFileDetail.vue | 472 | 0 | 確認並下一份/標非證據/取消非證據/加入檢查點/刪除檔(更多選單)都只 emit 改本地 state,不打 API |
| src/views/evidence-classification/components/ClassificationFileList.vue | 265 | 0 | 只有預覽鈕(emit preview)與排序,只讀 |
| src/views/evidence-classification/components/ClassificationFilterBar.vue | 128 | 0 | 純前端篩選與重置 |
| src/views/evidence-classification/components/ClassificationReviewHeader.vue | 251 | 1 | 儲存(emit save);捨棄=重新 GET、門檻套用=本地重算,不列 |
| src/views/evidence-classification/components/EvidenceFilePreviewDialog.vue | 263 | 0 | 預覽為 GET;下載是把已取回的 blob 存檔,無獨立下載 API |
| src/views/evidence-classification/components/PlacementTable.vue | 237 | 0 | 移除/復原/加入只 emit 改本地 state |
| src/views/feedback/system/SystemFeedbackForm.vue | 469 | 3 | 下載附件、刪附件、儲存;FileUpload 選檔/清除只動前端暫存,隨儲存送出 |
| src/views/feedback/system/SystemFeedbackManage.vue | 343 | 5 | 匯出、新增入口、編輯入口×2、刪除;預覽鈕開新分頁是導覽 |
| src/views/feedback/system/SystemFeedbackView.vue | 157 | 1 | 下載附件;預覽、返回是導覽 |
| src/views/flow-template/FlowTemplateEditorView.vue | 1424 | 5 | 預覽、⋮ 選單的匯入/匯出 BPMN(純前端讀檔/產 blob)、屬性面板(改畫布)不打 API;topology validate 為自動呼叫非按鈕 |
| src/views/flow-template/FlowTemplateManageView.vue | 631 | 6 | 預覽、篩選、搜尋為只讀;新增 dialog 的「儲存」只跳頁不打 API |
| src/views/flow-template/components/FlowTemplatePreviewDialog.vue | 241 | 0 | 只讀預覽(GET stage-objects),唯一按鈕是關閉;共用(被 FlowTemplateManageView、FlowTemplateEditorView、ProjectCreateView 用) |
| src/views/google-drive-app-config/GoogleDriveAppConfigForm.vue | 471 | 3 | 清除密鑰、驗證、儲存;複製網址鈕純前端不列 |
| src/views/home/Home.vue | 60 | 0 | 舊流程設定頁,只寫 localStorage 再導頁,不打 API |
| src/views/information-system/InformationSystemManage.vue | 793 | 3 | 批次停用、列內停用、dialog 儲存(新增/編輯入口同檔不列) |
| src/views/issue-integrate-config/IssueIntegrateConfigForm.vue | 203 | 2 | GitLab/GitHub 各一顆儲存 |
| src/views/ldap-config/LdapConfigForm.vue | 278 | 2 | 測試連線、儲存 |
| src/views/license/LicenseActivation.vue | 338 | 2 | 線上開通、離線上傳開通(皆附錄 C 豁免);複製、重新登入不打寫入 API |
| src/views/license/LicenseManage.vue | 673 | 4 | 解除停權、停權送出、指派送出、LC 請照送出;時間線唯讀 |
| src/views/license/LicenseStatus.vue | 249 | 0 | 「上傳新授權照」是 router.push 到開通頁的導覽 |
| src/views/log-forwarding/LogForwardingForm.vue | 451 | 2 | 測試、儲存,已用 hasCap |
| src/views/module_frame/ImportDocxPage.vue | 70 | 0 | 純包裝,按鈕全在 SspDocxImportPage.vue(batch 05) |
| src/views/module_frame/ImportExcelPage.vue | 430 | 1 | 另有「下載空白樣板」鈕(:330) 打 GET /ssp-import-template 或 GET /module-frame/ |
| src/views/module_frame/ImportExcelPreviewPage.vue | 628 | 1 | 取消(:366)/上一步(:371) 會打 DELETE /ssp-excel-import/<parse_uid> 丟棄解析,屬導覽未列 |
| src/views/module_frame/ModuleFrame.vue | 1587 | 7 | 新增選單「下載匯入範本」(:74) 開 DownloadSspBlankTemplateDialog 打 GET /ssp-import-template 屬 read,未列 |
| src/views/module_frame/ModuleFrameDetail.vue | 547 | 0 | 唯讀逐層瀏覽群組/控制項/檢查項目,按鈕全是下鑽、返回、跳流程編輯器 |
| src/views/module_frame/ModuleFrameItems.vue | 523 | 2 | 疑孤兒頁(無路由) |
| src/views/module_frame/ModuleFrameTemplateEditView.vue | 2279 | 9 | 內嵌 ModuleFrameBasicInfoPanel/SspBasicSection/ModuleFramePartiesPanel/ComponentsLeveragedInventoryTab/ModuleFrameDocumentPoolPanel 的按鈕由 batch 04/06 盤;匯出類(已填樣板、匯出現況資料、SSP 文件匯出 docx/pdf/odt)皆 oscal.resource-library.read,未列 |
| src/views/notify-config/NotifyConfigForm.vue | 331 | 4 | Telegram/Discord 各測試+儲存 |
| src/views/org-unit/OrgUnitManage.vue | 385 | 2 | 新增/編輯開同檔 dialog 不另列;重置查詢為前端 |
| src/views/plugin/ToolPluginManage.vue | 927 | 6 | 卡片測試連線、選單三項、設定儲存、測試 dialog 送出(複製程式碼為純前端不列) |
| src/views/project-summary-report/ProjectSummaryReportForm.vue | 343 | 2 | 匯出/儲存;歷史、取消為導覽 |
| src/views/project-summary-report/ProjectSummaryReportHistory.vue | 158 | 1 | 還原;載入此版本為 GET |
| src/views/project-summary-report/ProjectSummaryReportManage.vue | 328 | 2 | 新增 dialog 儲存/刪除;編輯鈕為導覽、新增鈕開同檔 dialog |
| src/views/project-summary-report/ProjectSummaryReportTabView.vue | 52 | 0 | 只包一層 TabView 嵌 ProjectSummaryReportForm |
| src/views/project-summary-report/TipTapEditor.vue | 377 | 0 | 編輯器工具列全為前端操作;共用(被 ProjectSummaryReportForm、ProjectSummaryReportHistory、BulletinForm、SystemFeedbackForm 用) |
| src/views/project/DashboardView.vue | 505 | 0 | 只讀儀表板:篩選切換、「查看全部」導覽、卡片點擊導覽;各區塊已用 hasCap('grc.project.read'/'grc.audit.read') 控顯示 |
| src/views/project/MyAuditsView.vue | 229 | 0 | 只讀清單:狀態篩選、搜尋、「進入稽核/查看結果」皆為導覽 |
| src/views/project/MyTasksView.vue | 741 | 1 | 批次完成;任務抽屜 JobExecutionDrawer(列點擊開啟)的按鈕不在本批 |
| src/views/project/ProjectApListView.vue | 340 | 2 | 發起覆核、建輪次(建輪次入口目前常數寫死隱藏) |
| src/views/project/ProjectAuditorOverview.vue | 2161 | 5 | 啟動任務 1 顆+審閱切換 4 顆;其餘為導覽(規劃頁、稽核計畫、進入稽核、POA&M、自動分類、Drive 資料夾)與展開收合;任務執行在 JobExecutionDrawer、階段推進在 FlowPhaseBanner(皆別批) |
| src/views/project/ProjectCreateView.vue | 512 | 1 | 建立專案;其餘為步驟導覽、流程範本預覽(只讀 GET) |
| src/views/project/ProjectListView.vue | 430 | 5 | 批次刪除、新增入口、Drive 同步、列刪除、編輯儲存 |
| src/views/project/ProjectPlanningView.vue | 2613 | 11 | 頁首啟動任務、批次維護選單 7 項、快速配置確定、SSP 儲存 2 顆;任務新增/儲存/刪除/強制啟動走 AoTaskSection emit(別批,handler 在本檔);程序書關聯/取消走 ReferenceDocumentList emit+DocumentLinkDialog(別批) |
| src/views/project/ProjectSettingsView.vue | 125 | 0 | 只是 TabView 殼,三個 panel(ProjectBasicInfoPanel/ProjectParticipantsPanel/ProjectCloudIntegrationsPanel)在別批;本檔只有麵包屑導覽 |
| src/views/project/RoundApAuthoringView.vue | 1029 | 3 | docx 匯入入口、儲存行程、新增稽核人員;新增/刪除行程只動本地陣列 |
| src/views/project/RoundAuditReviewView.vue | 1077 | 8 | AR 匯入入口、判定、觀察增改刪、風險增改刪 |
| src/views/project/RoundPoamView.vue | 872 | 6 | 改善計畫新增/編輯/刪除、里程碑新增/改/刪/狀態循環;無匯出鈕 |
| src/views/remote-agent/RemoteAgentManage.vue | 709 | 10 | token 兩顆、待核准兩顆、列內開關+四顆、dialog 儲存(重新整理/健康檢查/測試連線/複製不列) |
| src/views/scan-zone/ScanZoneDialog.vue | 72 | 1 | |
| src/views/scan-zone/ScanZoneManage.vue | 186 | 5 | |
| src/views/security-policy/SecurityPolicyForm.vue | 383 | 1 | 儲存,已用 hasCap;「前往 SMTP 設定」為導覽不列 |
| src/views/setup/SetupWizard.vue | 580 | 1 | 建立帳號(附錄 C 免登入);「下一步」只打 GET /setup/status 驗碼、重試、複製、去登入皆不列 |
| src/views/sitemap/SiteMap.vue | 51 | 0 | 只有導覽鈕 |
| src/views/smtp-config/SmtpConfigForm.vue | 368 | 2 | 儲存(新增/修改兩用)、測試信 dialog 送出 |
| src/views/smtp-config/SmtpConfigManage.vue | 170 | 3 | 新增入口、編輯入口、刪除 |
| src/views/storage-config/StorageConfigForm.vue | 568 | 2 | 恢復內建儲存、儲存 |
| src/views/support/SupportPage.vue | 275 | 1 | 匯出診斷包;重新整理與複製請求編號不列 |
| src/views/survey-v2/SurveyDesigner.vue | 1659 | 19 | 問卷設計頁;「問卷設定」dialog 只有 v-model 欄位+關閉鈕,改動要靠儲存選單才落地 |
| src/views/survey-v2/SurveyManage.vue | 1133 | 8 | 問卷管理頁;SplitButton 主鈕/卡片選單「移動」「編輯」「預覽」為導覽或開本檔 dialog 不列 |
| src/views/survey-v2/SurveyPreview.vue | 2624 | 9 | 一支檔掛兩條 route:survey-v2-preview(設計者預覽,寫入鈕全被 mode 條件藏掉,0 顆)與 survey-v2-fill(任務填答/審核,歸 grc.job);2164 行後全是 style |
| src/views/survey-v2/components/AddPageDialog.vue | 253 | 1 | 僅 SurveyDesigner 用;「清除條件」為本地操作 |
| src/views/survey-v2/components/AddQuestionDialog.vue | 1098 | 1 | 僅 SurveyDesigner 用;新增選項/刪選項/精靈增刪目標皆本地操作;600 行後全是 style |
| src/views/survey-v2/components/BatchImportDialog.vue | 310 | 2 | 僅 SurveyManage 用 |
| src/views/survey-v2/components/FolderTree.vue | 286 | 4 | 僅 SurveyManage 用;全靠 emit 交父層打 API |
| src/views/survey-v2/components/FolderTreeNode.vue | 94 | 0 | 只 emit select/toggle/ctx-menu,無寫入;且全 repo 無任何檔 import(死元件) |
| src/views/survey-v2/components/PageTreeNode.vue | 200 | 2 | 僅 SurveyDesigner 用(遞迴自用) |
| src/views/survey-v2/components/SurveyPickerDialog.vue | 396 | 2 | 僅 SurveyManage 用;「清除篩選」為本地操作 |
| src/views/system-menu-manage/SystemMenuManage.vue | 405 | 4 | 批次刪除、列內編輯/刪除、dialog 儲存;新增鈕(:338)開同檔 dialog 不列、重置查詢不列 |
| src/views/tenant-manage/TenantManage.vue | 347 | 2 | 新增/編輯開同檔 dialog 不另列;重置查詢為前端 |
| src/views/user-manage/UserMTRBACForm.vue | 455 | 1 | 取消為導覽;角色區在子元件 |
| src/views/user-manage/UserManage.vue | 401 | 3 | 編輯鈕與帳號連結導到可看可改的同一張表單頁,不另列;展開 chip、重置查詢為前端 |
| src/views/user-manage/UserManageTabView.vue | 76 | 0 | 只有兩個 tab |
| src/views/user-manage/UserManageUpload.vue | 481 | 4 | 重新上傳、刪除勾選列、錯誤訊息鈕皆前端 |
| src/views/user-manage/UserProfileForm.vue | 684 | 5 | 取消、看 QR(GET)為非寫入;LDAP 綁定入口開同檔 dialog |
| src/views/workflow/BpmnProcess/components/BpmnProcessAddEdit/index.vue | 97 | 0 | 舊頁殘骸:src/router 無掛;Element Plus 寫法,handleOk 只 console.log |
| src/views/workflow/BpmnProcess/components/BpmnProcessDrawer/index.vue | 217 | 0 | 舊頁殘骸:src/router 無掛;import 的 @/api/bpmnProcess 不存在,刪除只彈訊息 |
| src/views/workflow/BpmnProcess/components/BpmnProcessModelAddEdit/index.vue | 118 | 0 | 舊頁殘骸:src/router 無掛;import 的 @/designer、@/store 不存在,handleOk 只 console.log |
| src/views/workflow/BpmnProcess/index.vue | 183 | 0 | 舊頁殘骸:src/router 無掛、全 repo 無人 import;import 的 @/api/bpmnProcess 不存在,新增/編輯/刪除皆無真 API |
| src/views/workflow/WorkflowSetupEditor.vue | 588 | 2 | 匯出 XML 為本地 blob、任務屬性面板「儲存」只改畫布不打 API、返回為導覽;僅 WorkflowSetupForm.vue 用 |
| src/views/workflow/WorkflowSetupForm.vue | 36 | 0 | 純外框,包 WorkflowSetupEditor;路由 /workflow/workflow-setup 有掛(側邊選單已關,四支頁面以程式開啟) |
getUserPagePermissions('/project/projects') 比 action 字串(CREATE/UPDATE/DELETE),不是 hasCap;Drive 同步鈕完全沒判斷。saveControls(PUT /ap/saveSubjects(PUT /ap/linkFindings 有定義但 template 沒有任何按鈕呼叫,是死碼。writeDisabled() 全是 FR-062 授權唯讀反灰,不是權限判斷。canEdit 要 planning 且 role==='manager';ProjectPlanningView 給 DocumentPoolPanel/ReferenceDocumentList/SspImportDialog 的 canEditSsp 只看 apStatus==='planning',不看專案角色——非 manager 參與者在規劃期也看得到上傳/刪除/關聯/匯入鈕。!linkMode 時顯示,但現有三個使用端不是 linkMode 就是 readonly,這兩條 emit(remove/upload)無父層處理,實際不會出現。writeDisabled(授權唯讀反灰),沒有任何公司層權限判斷;AP/AR 靠宿主頁的專案角色+輪次狀態決定是否出現入口鈕,實作棒可直接在宿主入口鈕(RoundApAuthoringView.vue:664、RoundAuditReviewView.vue:721,屬 02 批)加 grc.audit.import 判斷。mode 而變(同一元件服務專案 SSP 與資源庫兩條路),建議由兩個宿主頁依 mode 傳入「可匯入」旗標;兩支 views/*/ImportDocxPage.vue 目前也都沒有任何權限判斷(路由頁本身,屬 07 批與 project 批)。writeDisabled() 全是 FR-062 授權唯讀(license),不是能力點。module-frame-items 路由指到 ModuleFrameDetail.vue),整支疑似孤兒頁;BE item DELETE 今天守 oscal.resource-library.delete、POST 守 .create,與附錄 A 的 update 不一致(附錄 A 已裁)。isPlatformAdmin(作用租戶 path 是否單層)遮寫入鈕,完全沒看能力點;實作棒要改成 hasCap 時需決定 isPlatformAdmin 是否保留為第二層條件(後端另有 require_platform_admin())。flow.template.update 判斷,附錄 A 新名是 flow.template.publish。currFramework.id 判新增/編輯,但編輯 PUT 用的是 uid;若列表資料不帶 id,編輯會誤走 POST 新增(非本卡範圍,順帶記錄)。detection.scan-zone.update,但附錄 A 把新增/產生建議改守 create、刪除改守 delete(A.2 拆分),三顆要換名;常數檔 src/config/scanZone.js 目前只有 READ/UPDATE 兩個。canRetry prop,父層 DetectionProfileControlsView 沒傳,直接傳 hasCap('detection.profile.create') 即可。detection.remote-agent.read,屬查詢性質未列入表。detection.plugin.update(寫入級),只有 read 權限的人看得到這顆但按了會 403。/socket/fill-survey 的 update 事件即時寫入(不在附錄 A 的 REST 表內),只靠 canEdit 決定能不能動欄位;檔案題的刪除鈕(:1698/:1931)只是本地清空答案,靠後續存檔落地,未列表。userInfo.is_admin 判,不是能力點。hasCap('iam.user.read')(選單權限)、寫用 useMyCapabilities;RoleList/RoleDetail 一律用 useRoleCaps(=useMyCapabilities)。/system/config/<uid>,群組由後端依該列推得(SMTP);前端不帶群組代碼。