A.6 盤點:全站寫入類按鈕 → API → 權限

卡片 CM-2561(FR-132 T-6.2),子母卡 CM-2559。只讀盤點,不改程式。FE 讀的是 feature/azure-iam 當下的工作樹(含平行 session 未 commit 的 access/user-manage 改動)。

§1

怎麼讀

  • 範圍:FE src/views、src/components 下有 <Button> 的 188 支,加上同目錄裡只用 @click/<button> 的 16 支、src/layout/AppLayout.vue 的 AI 小幫手浮鈕,以及順著 emit 追到的上層頁,共 239 支全部逐檔讀完(清單見文末「附錄:逐檔清單」,含 0 顆的檔與原因)。
  • 列什麼:會打寫入 API,或打 export/下載這類有獨立權限 API 的按鈕與選單項。純導覽、開 dialog(送出鈕在同檔時)、本地篩選、查詢鈕不列。開 dialog 的入口鈕若送出在別支檔,入口鈕照列,備註寫「入口鈕;送出在 X」——實作棒在入口隱藏即可。
  • 權限欄:只照 a1-capability-tree.md 附錄 A 查(API → 新名);附錄 C 豁免的寫「免判斷」。428 顆全部查得到,沒有附錄 A 查不到的 API。
  • 一顆按鈕多個權限時,備註會寫清楚是哪一種,實作棒不要一律套 hasAny:
    • 需 hasAny:多支寫入 API 打不同權限,有任一就讓它出現。
    • 兩者都要:按一下會連打兩支,缺一支會做一半(例:退回任務=流程 API+留言 API)。這種實作要 has(a) && has(b)。
    • 依情境擇一(通則 C):同一元件服務專案 SSP 與資源庫兩條路,看 apiBase/mode/source_type 決定用哪個權限——判斷要由頁面傳 props 進來,元件內不可寫死。
  • 專案層條件:今天決定按鈕出不出現的既有條件照抄(專案角色、輪次狀態、BE 回的旗標)。D-A6-4:公司層權限+這欄都過才顯示,實作棒保留這欄、外面再 AND 公司層權限。writeDisabled() 是授權唯讀反灰(FR-062),不是權限判斷,不算在這欄。
  • 現況五種:無判斷/已有且正確/已有但名不對/已有但粗(用較粗的權限或旗標遮寫入鈕)/免判斷。
§2

統計

模組 顆數 檔數 無判斷 已有但名不對 已有但粗 已有且正確 免判斷 要改(扣死碼) 有專案層條件
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
  • 「模組」依按鈕打的 API 的權限前綴分,不依檔案目錄。所以 views/project/ProjectPlanningView.vue 的 SSP 匯出鈕歸 oscal、views/survey-v2/SurveyPreview.vue 的填答鈕歸 grc(通則 A)。同一支檔可能分在兩個模組。
  • 「要改」=無判斷+已有但名不對+已有但粗,扣掉 14 顆死碼按鈕(見下方「死碼」)。
  • 「有專案層條件」222 顆:實作時要和公司層權限 AND,不可取代。
§3

已有判斷但名不對或太粗(19 顆)

檔案 按鈕 今天用的 該用的(附錄 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 一起改)
§4

建議拆棒(T-6.3 起)

前提照母卡: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 棒。死碼按鈕不必改,建議另開一張清理卡刪檔(見下方)。

§5

實作棒要注意

  1. 隱藏 vs 反灰:掃描設定檔管理頁的寫入項今天是「disabled+label 寫無權限」,AoTaskSection 新增任務鈕是「反灰+原因提示」。D-A6-1 裁定隱藏,實作棒改這兩處時要知道是在推翻既有設計。
  2. 同一份 SSP 兩套專案層判準:SspTabPanel 的 canEdit 要 planning 且 role==='manager';ProjectPlanningView 給 DocumentPoolPanel/ReferenceDocumentList/SspImportDialog 的 canEditSsp 只看 apStatus==='planning',非 manager 的參與者也看得到上傳/刪除/匯入。照 D-A6-4 只照抄、不修,但實作棒要知道。
  3. 入口有擋、目的頁沒擋:ProjectCreateView 的送出鈕無判斷,只靠列表頁入口;直接打網址進得去。JobImportDialog、AssignmentDialog、RoleImportDialog 同理(入口已擋或將擋)。目的頁也要補。
  4. 半套寫入:退回任務、完成任務先打 flow-engine 再補打留言 API——有 grc.job.update 沒有 grc.job-comment.create 的角色,任務已退回但留言 403。摘要報告匯出鈕先 PUT 再匯出——只有 export 的角色會先跳一次 403。這些「兩者都要」的按鈕已在備註標出(共 21 顆標了 hasAny/兩者都要,逐顆看備註)。
  5. 權限繞道(記錄,不屬本系列修):問卷設計頁與建立問卷 dialog 的「發布狀態」開關經 PUT /survey/<uid>(update)或 POST /survey(create)送出,等於不用 survey.survey.publish 就能發布;帳號列表「刪除」走 POST /user/change-status(iam.user.update),iam.user.delete 守的 DELETE /user/<uid> 前端沒人呼叫。
  6. 連 writeDisabled 都沒掛的:SystemMenuManage、TenantManage、SurveyManage 與問卷子元件、RemoteAgentManage 列內啟用開關、ToolPluginManage 測試連線、各設定頁的「測試」鈕。補 has() 時順手補授權唯讀(不補也不違反本系列,但同一行)。
  7. 檢測執行全部歸 grc.job.update(通則 A),無法單獨遮檢測操作;這是附錄 A 的設計,不是漏。
  8. SurveyPreview 一檔兩用:preview 模式從問卷管理進來(survey),fill 模式從任務抽屜開新分頁(grc.job),判斷要依 route.query.mode 分流。逐題作答走 socket /socket/fill-survey,不在 REST 表,只靠 canEdit。
§6

待首腦決定

  1. WorkflowSetupEditor 在專案情境的存檔:PUT /module-frame/item/xml/<uid> 附錄 A 只對到 oscal.resource-library.update,但這支編輯器也會從 ProjectPlanningView(專案 SSP 情境)開啟。照通則 C 專案情境該看專案權限,附錄 A 沒有另列——表上照附錄 A 寫,未自行改判。
  2. 合規框架頁的 isPlatformAdmin:四頁今天只靠「作用租戶是否單層」遮寫入鈕;後端另有 require_platform_admin()。改成 has() 後要不要保留 isPlatformAdmin 當第二層?(附錄 A 原廠層通則是「兩道都過」,傾向保留,但這幾支 route 不在 platform 模組。)
  3. Excel 批次建立帳號:POST /user/batch-upload 每列會帶角色,附錄 A 只標 iam.user.create;單筆新增帶角色則另要 iam.user.assign。兩者不對稱,要不要也要求 assign?
  4. AI 小幫手「清除對話」:DELETE /ai-chatbot 在 src/stores/aiChatStore.js:57 呼叫,附錄 A 守 ai.assistant.read。觸發它的按鈕沒有在本次盤點的檔案裡找到,實作棒動 ChatBox 時順手確認。
§7

死碼(不必改,建議另開清理卡)

全專案無引用或無路由,按鈕照列在表內(備註標「死碼」),不算進「要改」:

  • components/grc/ssp/SspLeveragedSection.vue
  • components/grc/ssp-excel-import/ 的 SheetPreviewDevices、SheetPreviewInfoSystems、SheetPreviewLeveraged,以及只被它們用的 RowActionMenu → EntityPickerDialog、InlineCreateDialog
  • components/grc/ 的 DepartmentsPanel、RoundSwitcherBar、SspSystemForm、ModuleFramePartiesDialog、ModuleFrameSspResourcesDialog(連帶 ModuleFrameSspDevicesPanel、ModuleFrameSspInfoSystemsPanel 不會出現在畫面)
  • components/grc/ssp-docx-import-v2/sections/ 的 MetadataSection、UnmatchedSection
  • components/compliance-framework/import/ 三支 ListEditor
  • views/module_frame/ModuleFrameItems.vue(路由 module-frame-items 指到 ModuleFrameDetail)
  • views/workflow/BpmnProcess/ 整包四支(外來樣板殘骸,依賴的 @/api、@/designer 目錄不存在)
  • 檔內死函式(無 template 引用):ProjectAuditorOverview 的 handleCloseRound、RoundApAuthoringView 的 saveControls/saveSubjects、RoundAuditReviewView 的 linkFindings、ModuleFrame 的 onCloneClick、ModuleFrameTemplateEditView 的 editMenuItems
§8

按鈕明細(依模組)

欄位:檔案:行號|按鈕(i18n key 或文字)|打的 API|權限(附錄 A 新名)|專案層條件(今天的既有條件,照抄)|現況|備註。檔案路徑相對 FE repo 根目錄。

grc(96 顆)

檔案:行號 按鈕 打的 API 權限 專案層條件 現況 備註
src/components/grc/AuditControlRef.vue:548 證據檔案下載(icon) GET /file/access-token/ → GET /file/download/ 免判斷(附錄 C) — 免判斷 僅 RoundAuditReviewView.vue 用
src/components/grc/FlowPhaseBanner.vue:63 前往下一階段(主按鈕,非審核階段經 confirm 後送出;審核階段改開同檔 Dialog) POST /project//audit-round//stage/advance 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//audit-round//stage/advance(帶 decision) grc.audit.update showAdvanceButton && isReviewStage(current_stage.code==='review');退回時 comment 必填 無判斷 入口為同檔 :43 退回鈕與 :63 主按鈕;共用(同上 5 頁),判斷放 props 由頁面傳入
src/components/grc/FlowPhaseBanner.vue:160 返回上一階段 Dialog 確認 POST /project//audit-round//stage/rollback 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//execute 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//cancel 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//assignments//start-now grc.job.update canEdit && !history && canStartNowAssignment(group, exec)(exec.status==='scheduled') 無判斷 共用(同上)
src/components/grc/JobExecutionDrawer.vue:2526 重跑此組 POST /detection-tools/execution-groups//assignments//rerun grc.job.update canEdit && !history && canRerunAssignment(group, exec)(failed/cancelled) 無判斷 共用(同上)
src/components/grc/JobExecutionDrawer.vue:2539 取消執行(單組) POST /detection-tools/execution-groups//assignments//cancel grc.job.update canEdit && !history && canCancelAssignment(group, exec)(running/scheduled) 無判斷 共用(同上)
src/components/grc/JobExecutionDrawer.vue:2546 取消執行(legacy 舊資料) POST /detection-tools/jobs//cancel grc.job.update canEdit && !history && group.is_legacy && exec.status === 'running' 無判斷 共用(同上)
src/components/grc/JobExecutionDrawer.vue:2555 下載報告(單份) GET /file/access-token/ → GET /file/download/ 免判斷(附錄 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/ → GET /file/download/ 免判斷(附錄 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//comments grc.job-comment.create canComment(canEdit || props.canReturn) 無判斷 共用(同上)
src/components/grc/JobExecutionDrawer.vue:2893 退回任務(footer) POST /flow-engine/task/revert/;接著 POST /grc/job-executions//comments 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/;有填留言時再 POST /grc/job-executions//comments grc.job.update、grc.job-comment.create canEdit;disabled 條件 !canCompleteJob(問卷全完成/至少一份附件) 無判斷 入口鈕;送出在 JobActionCommentDialog.vue;需 hasAny(留言那支僅在有填時打);共用(同上)
src/components/grc/JobExecutionDrawer.vue:2947 排程確認框「確認執行」 POST /detection-tools/jobs//execute grc.job.update canEdit(同 :2286 入口) 無判斷 共用(同上),判斷放 props 由頁面傳入
src/components/grc/JobExecutionDrawer.vue:3058 執行詳情視窗逐份下載報告 GET /file/access-token/ → GET /file/download/ 免判斷(附錄 C) — 免判斷 共用(同上)
src/components/grc/JobImportDialog.vue:258 上傳任務匯入檔(步驟 1) POST /grc/project//ap//jobs/import grc.job.import — 無判斷 僅 ProjectPlanningView.vue 用;入口在 ProjectPlanningView.vue:242 批次維護選單(disabled: writeDisabled(!canEditSsp),canEditSsp=apStatus==='planning')
src/components/grc/JobImportDialog.vue:387 重新驗證 POST /grc/project//ap//jobs/import/validate grc.job.import — 無判斷 僅 ProjectPlanningView.vue 用
src/components/grc/JobImportDialog.vue:392 確認匯入 POST /grc/project//ap//jobs/import/confirm grc.job.import — 無判斷 僅 ProjectPlanningView.vue 用
src/components/grc/ap-docx-import/ApDocxImportDialog.vue:565 lang.ap_docx_import.choose_file(選擇檔案;選檔即上傳解析,含 :555 dropzone 點擊) POST /ap//docx-imports/parse 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//confirm 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//ar-imports/parse 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//confirm 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/ + POST /grc/job-executions//comments 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/(有留言再 POST comments) 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//job//force-start(useJobForceStart) 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//job/ 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//job/ grc.job.update canEdit(同上) 無判斷 僅 AoTaskSection.vue 用;分派列增刪/複製/排程皆為本地狀態,隨此鈕一併送出
src/components/grc/planning/AoTaskSection.vue:176 新增任務 emit create → ProjectPlanningView.vue createTask:POST /grc/project//assessment-object//jobs 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//assessment-object//jobs 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/(之前先 PUT /project-summary-report/ 存檔) 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//jobs/batch-complete 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//launch-reverify 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//audit-rounds 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//start-task-execution 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//ap//control//review 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//ap//control//ao//review 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//ap//control//review grc.audit.approve canReviewControl(selectionGroupId, selectedControl.id);disabled 同 1711 無判斷 頁面(route view)
src/views/project/ProjectAuditorOverview.vue:2009 AO 審閱 icon 鈕(控制項詳情內 AO 列) POST/DELETE /grc/project//ap//control//ao//review 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//ap//jobs/import(+/import/validate、/import/confirm) 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//ap//jobs/export 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//start-task-execution 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//job/(逐筆) 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//docx-imports/parse grc.audit.import canImportDocx(= !readonly && apUid;readonly = !(roundStatus==='audit_planning' && (isAuditor || isProjectManager))) 無判斷 入口鈕;送出在 ApDocxImportDialog.vue(後續 GET/DELETE /ap-docx-import/、POST /ap-docx-import//confirm 同為 grc.audit.import);ApDocxImportDialog 僅 RoundApAuthoringView 用
src/views/project/RoundApAuthoringView.vue:685 lang.ap_authoring.btn_save_tasks(儲存行程) PUT /ap//tasks grc.audit.update !readonly(readonly 定義同上) 無判斷 同區「新增行程」:683、刪除行程 :745 只改本地陣列,靠此鈕存檔;僅 RoundApAuthoringView 用
src/views/project/RoundApAuthoringView.vue:871 lang.common.confirm(新增稽核人員 dialog 確定) POST /ap//parties 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//ar-imports/parse grc.audit.import canImportAr(= roundStatus==='auditing' && (isAuditor || isProjectManager) && stats.total > 0) 無判斷 入口鈕;送出在 ArImportDialog.vue(後續 POST /ar-import//confirm、DELETE /ar-import/ 同為 grc.audit.import);ArImportDialog 僅 RoundAuditReviewView 用
src/views/project/RoundAuditReviewView.vue:779 lang.round_audit.save(儲存控制項判定) PUT /audit-round//ar/finding/;判定 not_met 時另打 PUT /audit-round//ar/risk/ 或 POST /audit-round//ar/risks + PUT /audit-round//ar/risk//findings 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//ar/observation/ grc.audit.update canEdit(入口鉛筆 :819 掛 v-if="canEdit") 無判斷 入口鉛筆 :819 只切編輯模式
src/views/project/RoundAuditReviewView.vue:822 icon pi-trash(刪除觀察) DELETE /audit-round//ar/observation/ grc.audit.delete canEdit 無判斷
src/views/project/RoundAuditReviewView.vue:869 lang.round_audit.obs_save(新增觀察) POST /audit-round//ar/observations + PUT /audit-round//ar/finding/ 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//ar/risks + PUT /audit-round//ar/risk//findings grc.audit.create、grc.audit.update canEdit 無判斷 需 hasAny(選了來源控制項才打 PUT findings)
src/views/project/RoundAuditReviewView.vue:904 icon pi-trash(刪除風險) DELETE /audit-round//ar/risk/ grc.audit.delete canEdit 無判斷
src/views/project/RoundAuditReviewView.vue:922 lang.round_audit.save(風險編輯儲存) PUT /audit-round//ar/risk/ + PUT /audit-round//ar/risk//findings grc.audit.update canEdit(入口鉛筆 :903) 無判斷 入口鉛筆 :903 只切編輯模式
src/views/project/RoundPoamView.vue:558 icon pi-trash(刪除改善計畫) DELETE /audit-round//remediation/ grc.audit.delete rem.lifecycle !== 'recommendation' && canEdit(canEdit = roundStatus==='remediation') 無判斷 僅 RoundPoamView 用
src/views/project/RoundPoamView.vue:577 里程碑狀態 chip(點擊循環切換狀態,即存) PUT /audit-round//poam-milestone/ grc.audit.update canEdit(寫在 @click 守門與 class,非 v-if) 無判斷 chip 永遠顯示,canEdit 為 false 時點了不送
src/views/project/RoundPoamView.vue:583 icon pi-trash(刪除里程碑) DELETE /audit-round//poam-milestone/ grc.audit.delete canEdit 無判斷
src/views/project/RoundPoamView.vue:604 lang.round_poam.btn_save_ms(里程碑編輯儲存) PUT /audit-round//poam-milestone/ grc.audit.update canEdit(入口鉛筆 :582) 無判斷 入口鉛筆 :582 只切編輯模式
src/views/project/RoundPoamView.vue:619 lang.round_poam.add_milestone(新增里程碑,就地卡片送出) POST /audit-round//remediation//milestones grc.audit.create canEdit(入口 :569) 無判斷 入口 :569 只長出草稿卡
src/views/project/RoundPoamView.vue:702 lang.round_poam.add_plan/btn_save_ms(改善計畫 dialog 新增/編輯送出) POST /audit-round//ar/risk//remediations 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//checkpoint 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//checkpoint(status=2) 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//checkpoint(status=3) grc.job.update mode 為 fill/review && taskSurvey.status === 2 && canVerify 無判斷 canVerify=本人是 approver 且狀態待審
src/views/survey-v2/SurveyPreview.vue:1296 提交補充 POST /project-survey/answers//checkpoint(status=2) grc.job.update mode 為 fill/review && taskSurvey.status === 3 && canEdit 無判斷
src/views/survey-v2/SurveyPreview.vue:1301 結案 POST /project-survey/answers//checkpoint(status=9) 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)

oscal(114 顆)

檔案:行號 按鈕 打的 API 權限 專案層條件 現況 備註
src/components/compliance-framework/article/FrameworkArticleContent.vue:140 群組名稱 inline「儲存」 emit update:group → 由 FrameworkVersionEditView 處理:PUT /oscal-catalog-group/;FrameworkImportPage 處理:只寫本地草稿,最後由「確認匯入」POST /oscal-framework-parse-jobs//confirm 送出 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/;FrameworkImportPage:本地標記刪除,送出走 confirm 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/;FrameworkImportPage:本地草稿,送出走 confirm oscal.framework.update(編輯頁)/oscal.framework.import(匯入頁) !readOnly 無判斷 共用,同上
src/components/compliance-framework/article/FrameworkArticleContent.vue:197 刪除控制項(icon) emit delete:control → FrameworkVersionEditView:DELETE /oscal-catalog-control/;FrameworkImportPage:本地標記刪除,送出走 confirm 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/;FrameworkImportPage:本地草稿,送出走 confirm 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/;FrameworkImportPage:本地草稿,送出走 confirm 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/;FrameworkImportPage:本地草稿,送出走 confirm 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/;FrameworkImportPage:本地標記刪除,送出走 confirm 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//components/<item_uid> 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//leveraged/<item_uid> 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//inventory/<item_uid> 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//inventory/<item_uid> 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//components、PUT /module-frame//components/<item_uid> 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//leveraged、PUT /module-frame//leveraged/<item_uid> 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//inventory、PUT /module-frame//inventory/<item_uid> 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//inventory、PUT /module-frame//inventory/<item_uid> oscal.resource-library.update — 無判斷 共用,MF 情境
src/components/grc/DocumentLinkDialog.vue:343 「確認關聯」(程序書勾選送出) POST /ssp/<ssp_uid>/control-implementation//document-mappings、DELETE /ssp/<ssp_uid>.../document-mapping/<doc_uid>(AO 層改打 .../objective//document-mappings、.../objective//document-mapping/<doc_uid>) 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/ → GET /file/download/ 免判斷(附錄 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//reference-documents oscal.resource-library.update — 無判斷 僅 ModuleFrameTemplateEditView.vue 用(api-base=module-frame;ssp 分支無人使用);FileUpload 拖拉區 :291 同一路徑
src/components/grc/ModuleFrameDocumentPoolPanel.vue:378 參考文件「下載」 GET /file/access-token/ → GET /file/download/ 免判斷(附錄 C) — 免判斷 僅 ModuleFrameTemplateEditView.vue 用
src/components/grc/ModuleFrameDocumentPoolPanel.vue:386 參考文件「刪除」 DELETE /module-frame//reference-documents/<doc_uid> 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//parties/<party_uid> 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//parties、PUT /module-frame//parties/<party_uid> oscal.resource-library.update — 無判斷 共用,MF 情境
src/components/grc/ModuleFrameSspDevicesPanel.vue:282 設備列「刪除」 DELETE /module-frame//ssp-resources/items/<item_uid> 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//ssp-resources/items、PUT /module-frame//ssp-resources/items/<item_uid> oscal.resource-library.update — 無判斷 同上(死碼)
src/components/grc/ModuleFrameSspInfoSystemsPanel.vue:278 資訊系統列「刪除」 DELETE /module-frame//ssp-resources/items/<item_uid> oscal.resource-library.update — 無判斷 同 DevicesPanel,僅掛在無人引用的 ModuleFrameSspResourcesDialog.vue(死碼)
src/components/grc/ModuleFrameSspInfoSystemsPanel.vue:328 資訊系統 dialog「儲存」 POST /module-frame//ssp-resources/items、PUT /module-frame//ssp-resources/items/<item_uid> oscal.resource-library.update — 無判斷 同上(死碼)
src/components/grc/ModuleFrameTemplateImportDialog.vue:390 import_upload_btn(上傳並驗證 Excel) POST /file/upload → POST /module-frame//control-defaults/import/template → POST /module-frame//control-defaults/import/verify oscal.resource-library.update — 無判斷 /file/upload 屬附錄 C 豁免,取後兩支(同權限);僅 ModuleFrameTemplateEditView.vue 用(入口在該檔 menu command:227)
src/components/grc/ModuleFrameTemplateImportDialog.vue:533 import_btn_revalidate(重新驗證) POST /module-frame//control-defaults/import/validate oscal.resource-library.update hasEdited(有改過才可按) 無判斷 只驗證不寫入,但 BE 以 update 守;僅 ModuleFrameTemplateEditView.vue 用
src/components/grc/ModuleFrameTemplateImportDialog.vue:538 import_btn_confirm_all(確認匯入) POST /module-frame//control-defaults/import 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//document-mappings(AO 層 .../objective//document-mappings) 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/ → GET /file/download/ 免判斷(附錄 C) — 免判斷 共用(同上)
src/components/grc/ReferenceDocumentList.vue:221 「取消關聯」 emit unlink → 由 ProjectPlanningView.vue handleUnlinkRefDoc 處理 → DELETE /ssp/<ssp_uid>.../document-mapping/<doc_uid>(AO 層 .../objective//document-mapping/<doc_uid>) 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//system-characteristic 或 PUT /module-frame//system-characteristic(依 apiBase) 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//leveraged/<item_uid> 或 DELETE /module-frame//leveraged/<item_uid>(依 apiBase) oscal.ssp.update、oscal.resource-library.update !readonly(操作欄 v-if) 無判斷 依 apiBase 擇一;全專案無任何檔 import 此元件(死碼)
src/components/grc/ssp/SspLeveragedSection.vue:374 dialog common.save(新增/編輯外部利用服務) POST /ssp//leveraged、PUT /ssp//leveraged/<item_uid>(module-frame 版同形) 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//token → 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//confirm 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/,但本頁開 dialog 一律清空 currFrame,實際只走新增
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/(編輯)、POST /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//control-defaults/import(另打 import/template、import/verify、import/validate) 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//control-defaults/ oscal.resource-library.update isControlDirty(selectedControl.code) 才可按(disabled) 無判斷 只有 writeDisabled 授權唯讀判斷
src/views/module_frame/ModuleFrameTemplateEditView.vue:1693 控制項程序書 chip › 解除掛載(×) DELETE /module-frame//control-defaults//reference-documents/<doc_uid> oscal.resource-library.update — 無判斷
src/views/module_frame/ModuleFrameTemplateEditView.vue:1877 檢查項目程序書 chip › 解除掛載(×) DELETE /module-frame//objective-defaults/<od_uid>/reference-documents/<doc_uid> oscal.resource-library.update — 無判斷
src/views/module_frame/ModuleFrameTemplateEditView.vue:1910 檢查項目 › 儲存 PUT /module-frame//objective-defaults oscal.resource-library.update isAODirty(...) 才可按(disabled) 無判斷
src/views/module_frame/ModuleFrameTemplateEditView.vue:2083 從程序書池選用 dialog › 確認 POST /module-frame//control-defaults//reference-documents、DELETE .../control-defaults//reference-documents/<doc_uid>、POST .../objective-defaults/<od_uid>/reference-documents、DELETE .../objective-defaults/<od_uid>/reference-documents/<doc_uid> 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//objective/ oscal.ssp.update canEditSsp 無判斷 經 SspService.updateObjective;另有 writeDisabled()
src/views/workflow/WorkflowSetupEditor.vue:472 分享給子租戶開關(InputSwitch) PUT /module-frame/item/xml/(帶 scope) 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 開啟)

flow(11 顆)

檔案:行號 按鈕 打的 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//publish(dirty 時先 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//unpublish 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/(帶 scope) 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//publish flow.template.publish canWriteRow(data) && data.status === 'draft' 已有但名不對 實際用 flow.template.update(canUpdate)
src/views/flow-template/FlowTemplateManageView.vue:464 列內撤銷發布 PUT /flow-engine/flow-templates//unpublish 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//duplicate flow.template.create — 已有且正確 判斷在入口 :437(disabled: !canCreate),dialog 鈕本身沒判

detection(40 顆)

檔案:行號 按鈕 打的 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//new-version 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//source 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//fork 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//copy-to-tool 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//deactivate 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//extraction 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//extraction 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//extraction detection.profile.create isStaleSnapshot && canRetry 無判斷 同上
src/views/detection-profile/components/ProfileCopyDialog.vue:143 btn_fork/btn_copy_to_tool(複製送出) POST /detection-tool-profiles//fork(fork 模式);POST /detection-tool-profiles//copy-to-tool(copy 模式) detection.profile.create — 無判斷 兩模式同權限;只有 writeDisabled;僅 DetectionProfileManageView 用
src/views/detection-profile/components/ProfileFormDialog.vue:448 btn_save(新增/編輯送出) POST /detection-tool-profiles(create 模式);PUT /detection-tool-profiles/(edit 模式) 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//new-version detection.profile.create — 無判斷 只有 writeDisabled;入口在父層已判;僅 DetectionProfileManageView 用
src/views/detection-profile/components/ProfileSourceDialog.vue:201 btn_fix_source(修正來源送出) PATCH /detection-tool-profile-versions//source detection.profile.create — 無判斷 只有 writeDisabled;主列表與版本子表兩入口共用;僅 DetectionProfileManageView 用
src/views/detection-profile/components/ProfileVersionTable.vue:328 選單項 controls_btn_extract(開始解析) POST /detection-tool-profile-versions//extraction 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//source 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//reset detection.plugin.update tool.config 無判斷 Menu item;只有 writeDisabled
src/views/plugin/ToolPluginManage.vue:595 test_connection(卡片測試連線) POST /detection-tools/configs//test-connection 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//test-connection 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//approve 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//reconcile detection.remote-agent.update — 無判斷 只有 writeDisabled
src/views/remote-agent/RemoteAgentManage.vue:624 unrevoke_agent(復用 icon 鈕) POST /remote-agents//unrevoke detection.remote-agent.update data.status === 'revoked' 無判斷 只有 writeDisabled
src/views/remote-agent/RemoteAgentManage.vue:628 revoke_agent(撤銷 icon 鈕) POST /remote-agents//revoke 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

evidence(17 顆)

檔案:行號 按鈕 打的 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//retry 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//state evidence.classification.update — 無判斷 僅 EvidenceClassificationReview 用;完全沒有角色條件(稽核員也看得到可按),只有 disabled 於 !hasUnsavedChanges || saving;判斷需由 Review 頁傳 prop

survey(39 顆)

檔案:行號 按鈕 打的 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//release、DELETE /survey//release 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/(跳題精靈另打 PUT /survey-page/) 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//clone 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//clone 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/(+ 精靈 PUT /survey-page/、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//clone survey.survey.create — 無判斷 非按鈕但會直接觸發寫入,隱藏確認鈕時要一起擋;僅 SurveyManage.vue 用
src/views/survey-v2/components/SurveyPickerDialog.vue:291 範本/複製確認鈕 emit confirm → 由 SurveyManage.vue 處理:POST /survey//clone survey.survey.create — 無判斷 僅 SurveyManage.vue 用;開啟時另打 GET /surveys/menu(讀)

system(23 顆)

檔案:行號 按鈕 打的 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 用

iam(40 顆)

檔案:行號 按鈕 打的 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/(送出在 AssignmentDialog.vue:210) 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/(mode EDIT) 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/(status 1/-1) 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/(status -2) iam.user.update data.login_name !== 'admin' && data.login_name !== userInfo.login_name 無判斷 刪除走 change-status 不走 DELETE /user/,故後端守的是 iam.user.update 而非 iam.user.delete(見觀察)
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 免判斷 只驗登入:本人

platform(12 顆)

檔案:行號 按鈕 打的 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//ceiling 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//unlock platform.license.update data.has_license && data.suspended_at 無判斷
src/views/license/LicenseManage.vue:524 停權(dialog 確認) POST /license/tenants//lock platform.license.update data.has_license && !data.suspended_at(入口 :432) 無判斷 入口 :432 同檔
src/views/license/LicenseManage.vue:571 指派/換發儲存(dialog) POST /license/tenants//assign platform.license.assign — 無判斷 入口 :417 同檔
src/views/license/LicenseManage.vue:663 向 LC 請照儲存(新簽/展延 dialog) POST /license/tenants//lc-issue 或 POST /license/tenants//lc-extend 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 用

asset(11 顆)

檔案:行號 按鈕 打的 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 同檔,皆無判斷

ai(12 顆)

檔案:行號 按鈕 打的 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

workspace(13 顆)

檔案:行號 按鈕 打的 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//<file_uid> 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) — 免判斷
§9

附錄:逐檔清單

每支讀過的檔與寫入類按鈕數;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//ssp-import-template,屬 read,未列
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 有掛(側邊選單已關,四支頁面以程式開啟)
§10

附錄:各批盤點的觀察原文

  • 三支檔都沒有任何公司層權限判斷(無 hasCap/can/useMyCapabilities),寫入鈕只靠專案角色(isProjectManager/canEdit/canReviewControl)+輪次狀態+writeDisabled(授權唯讀)。
  • ProjectPlanningView 批次維護選單的三種匯出(SSP Excel、任務 Excel、SSP 文件)完全無條件,只要 sspUid 存在即顯示,任何專案參與者含 viewer 都看得到。
  • ProjectPlanningView 的子元件 emit 由本檔處理的寫入(給子元件批次核對):AoTaskSection @create → POST /grc/project//assessment-object//jobs(grc.job.create);@save → PUT /grc/project//job/(grc.job.update);@delete → DELETE /grc/project//job/(grc.job.delete);@force-start → useJobForceStart POST /grc/project//job//force-start(grc.job.assign);ReferenceDocumentList @unlink → DELETE /ssp/<ssp_uid>/.../document-mapping/(oscal.ssp.update)。AoTaskSection 的 can-edit 傳 canEdit、can-force-start 傳 isProjectManager。
  • ProjectPlanningView 傳給設定類 panel 的 can-edit=isProjectManager;ProjectSettingsView 傳的 canEdit 是自己算的同義 computed(participants 中 role==='manager')。
  • 死碼:ProjectAuditorOverview 的 handleCloseRound(打 POST /grc/project//ap//close-round,附錄 A 只有 /audit-round//close-round)、handleSaveSspDesc(mock)、downloadEvidence 都沒有 template 引用;ProjectPlanningView 的 handleUploadRefDoc、handleRemoveRefDoc、showDocPoolDialog 入口也無引用。
  • ProjectAuditorOverview 的審閱 POST/DELETE 打 /grc/project//ap//control/... 舊 AP 路徑格式(round 模式下 ap 段塞 roundUid),附錄 A 有登記此路徑。
  • ProjectListView 是本批唯一有公司層判斷的頁,但走 getUserPagePermissions('/project/projects') 比 action 字串(CREATE/UPDATE/DELETE),不是 hasCap;Drive 同步鈕完全沒判斷。
  • ProjectCreateView 送出鈕無判斷,只靠 ProjectListView 入口擋;直接打網址進建立頁可看到送出鈕。
  • RoundApAuthoringView 的 saveControls(PUT /ap//reviewed-controls)、saveSubjects(PUT /ap//assessment-subjects)與 RoundAuditReviewView 的 linkFindings 有定義但 template 沒有任何按鈕呼叫,是死碼。
  • RoundAuditReviewView 判定/新增觀察/新增風險一顆鈕連打 create+update 兩種權限,實際需兩者都有才不會半途失敗(比 hasAny 嚴),實作棒請留意。
  • RoundPoamView 改善計畫「編輯」走 upsert POST,權限表歸 grc.audit.create;只給 update 的角色會編輯不了。
  • 三支輪次頁(ApAuthoring/AuditReview/Poam)都嵌 FlowPhaseBanner(推進/退回階段鈕),該元件不在本批,需由涵蓋 components/grc 的批次處理。
  • 12 支檔案全部沒有任何公司層權限判斷(無 hasCap/can/useMyCapabilities),寫入鈕一律只靠專案角色、props(canExecute/canReturn/canForceStart/canEdit)或 BE 回的旗標(user_can_advance、user_role、can_revert_job)。
  • FlowPhaseBanner 的推進/退回顯示完全由 BE stage/info 的 user_can_advance/user_role 決定,FE 沒有自己的角色判斷;實作棒若加 hasCap 需與該旗標 AND。
  • 抽屜「完成/退回」先打 flow-engine 再補打留言 API:若角色有 grc.job.update 但無 grc.job-comment.create,任務已完成/退回但留言 403,屬半套寫入(退回必帶留言,必中)。
  • 檢測執行全部動作(執行、取消、重跑、立即開始、刪紀錄、整組刪)都對到 grc.job.update,與「完成任務」同一權限,無法單獨遮檢測操作。
  • JobImportDialog 本身無任何條件,入口在 ProjectPlanningView.vue:242(不在本批),且入口用的是 canEditSsp 而非任務相關條件。
  • AoTaskSection 新增任務鈕無 v-if,只用 disabled 呈現原因;若改「照權限隱藏」需留意與既有「反灰+原因提示」設計的取捨。
  • 源碼壓縮包上傳與報告/證據下載走 /file/* 豁免端點,前端不需判斷(真正守門在後續 execute)。
  • 本批 24 支沒有任何一支用 hasCap/can/useMyCapabilities;writeDisabled() 全是 FR-062 授權唯讀反灰,不是權限判斷。
  • 同一份 SSP 內容兩種專案層判準不一致:SspTabPanel 的 canEdit 要 planning 且 role==='manager';ProjectPlanningView 給 DocumentPoolPanel/ReferenceDocumentList/SspImportDialog 的 canEditSsp 只看 apStatus==='planning',不看專案角色——非 manager 參與者在規劃期也看得到上傳/刪除/關聯/匯入鈕。
  • 共用 panel 一律以 apiBase 切 SSP(oscal.ssp.)或資源庫(oscal.resource-library.),實作棒需由頁面依情境傳入對應權限,元件內不可寫死單一名字。
  • 死碼候選:DepartmentsPanel、RoundSwitcherBar、SspSystemForm、ModuleFramePartiesDialog、ModuleFrameSspResourcesDialog(連帶 Devices/InfoSystems 兩 panel 實際上不會出現在畫面)。
  • ReferenceDocumentList 的「刪除」(:217)與上傳區(:236)只在 !linkMode 時顯示,但現有三個使用端不是 linkMode 就是 readonly,這兩條 emit(remove/upload)無父層處理,實際不會出現。
  • ModuleFrameDocumentPoolPanel 的 ssp 分支(POST /ssp//document-pool)目前無人以 api-base="ssp" 掛載。
  • 三支匯入精靈本身都只有 writeDisabled(授權唯讀反灰),沒有任何公司層權限判斷;AP/AR 靠宿主頁的專案角色+輪次狀態決定是否出現入口鈕,實作棒可直接在宿主入口鈕(RoundApAuthoringView.vue:664、RoundAuditReviewView.vue:721,屬 02 批)加 grc.audit.import 判斷。
  • SspDocxImportPage 的權限依 mode 而變(同一元件服務專案 SSP 與資源庫兩條路),建議由兩個宿主頁依 mode 傳入「可匯入」旗標;兩支 views/*/ImportDocxPage.vue 目前也都沒有任何權限判斷(路由頁本身,屬 07 批與 project 批)。
  • PartyLinkDialog 搜尋候選打 POST /users(iam.user.read)、POST /org-units(iam.org-unit.read)——只有 oscal.ssp.import 而無 iam 讀權限的人在 Step 3 連結人員時會搜不到(403),實作棒可能要注意這個相依。
  • MetadataSection.vue、UnmatchedSection.vue 已無任何檔案引用,屬死碼。
  • 本批 19 支沒有任何一支有 hasCap/can/useMyCapabilities;所見 writeDisabled() 全是 FR-062 授權唯讀(license),不是能力點。
  • 死碼:SspLeveragedSection、SheetPreviewDevices/InfoSystems/Leveraged,以及只被它們用的 RowActionMenu → EntityPickerDialog/InlineCreateDialog,全專案無 import;實作棒可略過或順手刪。
  • SSP Excel 預覽子元件全部只寫 sspExcelImportStore,唯一送出點是 ImportExcelPreviewPage.vue(不在本批)的確認鈕 POST /ssp-excel-import/<parse_uid>/confirm,權限依匯入來源(通則 C)三擇一;建議在該頁整頁/確認鈕判斷,子元件不逐顆判。
  • SspTabPanel 下載已填 Excel(oscal.ssp.export)完全沒條件,非 manager 也看得到。
  • SspBasicSection 在 MF 樣板頁(ModuleFrameTemplateEditView)不傳 readonly,儲存鈕對任何能進頁的人都顯示;需由頁面傳入 oscal.resource-library.update 判斷結果。
  • 七支檔全部沒有 hasCap/can/useMyCapabilities,寫入鈕只靠 useLicenseReadonly 的 writeDisabled(授權唯讀反灰);實作棒要另加能力點判斷。
  • ModuleFrame.vue 的 onCloneClick(POST /module-frame/clone)與 cloneDialog 沒有任何 template 掛載,是死碼。
  • ModuleFrameTemplateEditView 的 editMenuItems(:1207)沒有 Menu 渲染,也是死碼。
  • ModuleFrameItems.vue 沒有路由指向(module-frame-items 路由指到 ModuleFrameDetail.vue),整支疑似孤兒頁;BE item DELETE 今天守 oscal.resource-library.delete、POST 守 .create,與附錄 A 的 update 不一致(附錄 A 已裁)。
  • ModuleFrame 列動作「編輯預設值與內容」(:221) 只是導到 template-edit 頁(名稱連結 :1092 也會導過去),未列;該頁本身可讀,寫入鈕已在該頁逐顆列出。
  • ImportExcelPreviewPage 確認匯入的權限要依 source_type 決定(同一顆鈕可能是資源庫新增/更新/專案 SSP 匯入),實作時判斷式要吃 parseResult.source_type。
  • 合規框架三頁(管理/版本/匯入/編輯)今天全用 isPlatformAdmin(作用租戶 path 是否單層)遮寫入鈕,完全沒看能力點;實作棒要改成 hasCap 時需決定 isPlatformAdmin 是否保留為第二層條件(後端另有 require_platform_admin())。
  • 流程範本兩頁的發布/撤銷發布都用 flow.template.update 判斷,附錄 A 新名是 flow.template.publish。
  • 編輯器「發布」在 dirty 時會先自動存檔(PUT update),只有 publish 沒有 update 的人會在存檔那步被擋。
  • WorkflowSetupEditor 會從 ProjectPlanningView(專案情境)開啟,但存檔 PUT /module-frame/item/xml/ 在附錄 A 只對到 oscal.resource-library.update;專案情境的存檔權限(通則 C)附錄 A 沒有另列,實作棒需確認。
  • views/workflow/BpmnProcess/ 整包四支是外來樣板殘骸(Element Plus、依賴的 @/api、@/hooks、@/store、@/designer 目錄都不存在),無路由無引用,可列刪除候選。
  • compliance-framework/import/ 三支 ListEditor 無人引用,疑似兩階段匯入改文章式後遺留。
  • ComplianceFrameworkManage 存檔用 currFramework.id 判新增/編輯,但編輯 PUT 用的是 uid;若列表資料不帶 id,編輯會誤走 POST 新增(非本卡範圍,順帶記錄)。
  • 掃描設定檔管理頁與版本子表的 hasCap 名稱全部對得上附錄 A(create/update/delete 三顆),寫入項是 disabled+label 後綴「無權限」而非隱藏,實作棒若改成隱藏要先跟 CM-1078 的「不可做成不能做就不放」設計對齊。
  • ScanZoneManage 所有寫入都只看 detection.scan-zone.update,但附錄 A 把新增/產生建議改守 create、刪除改守 delete(A.2 拆分),三顆要換名;常數檔 src/config/scanZone.js 目前只有 READ/UPDATE 兩個。
  • ControlExtractionSummary 已預留 canRetry prop,父層 DetectionProfileControlsView 沒傳,直接傳 hasCap('detection.profile.create') 即可。
  • ToolPluginManage、RemoteAgentManage 完全沒有任何公司層判斷(只有授權唯讀 writeDisabled);RemoteAgentManage 列內的啟用開關 :608 與 ToolPluginManage 卡片測試連線 :595 連 writeDisabled 都沒掛。
  • RemoteAgentManage 的「健康檢查」(:511)與 dialog「測試連線」(:662)打的是 POST /remote-agents/health-check,附錄 A 守 detection.remote-agent.read,屬查詢性質未列入表。
  • 工具卡片測試連線附錄 A 守 detection.plugin.update(寫入級),只有 read 權限的人看得到這顆但按了會 403。
  • 自動分類審閱頁的「儲存」(PUT /classification-run//state)前端完全沒有角色或能力點判斷,稽核員進第③步也能編輯並按儲存;歸檔鈕卻有 canArchive。實作棒隱藏儲存時,FileDetail/PlacementTable/AOPickerDialog 那批只改本地 state 的編輯鈕應一併收(否則能改不能存)。
  • GoogleDriveIntegrationCard 錯誤橫幅內的「重新授權」(:341) 漏了 canCreate,同動作的⋯選單版本有。
  • 專案調查結果三頁只用 writeDisabled()(授權唯讀反灰),沒有任何 grc.summary-report.* 判斷;匯出鈕先 PUT 再匯出,只有 export 權限的人按下會先跳一次 403 錯誤再成功匯出。
  • 自動分類三頁的寫入鈕全靠 isProjectManager(專案層),公司層 evidence.classification.* 一律沒判。
  • 十支檔沒有任何 hasCap/can/useMyCapabilities 判斷;部分寫入鈕只掛 FR-062 的 writeDisabled(授權唯讀反灰),與能力點無關。SurveyManage、FolderTree、SurveyPickerDialog、AddPageDialog、AddQuestionDialog、PageTreeNode 連 writeDisabled 都沒有。
  • SurveyDesigner「問卷設定」dialog 的「發布狀態」InputSwitch 改的是本地 survey.isPublished,再經儲存選單 PUT /survey/ 送出——等於用 survey.survey.update 繞過 survey.survey.publish,實作棒要決定該開關是否也要照 publish 判斷或移除。
  • 建立問卷 dialog(SurveyManage:863)也有「發布狀態」開關,經 POST /survey(survey.survey.create)一併設定發布,同樣繞過 publish。
  • SurveyPreview 填答模式的逐題作答是走 socket /socket/fill-survey 的 update 事件即時寫入(不在附錄 A 的 REST 表內),只靠 canEdit 決定能不能動欄位;檔案題的刪除鈕(:1698/:1931)只是本地清空答案,靠後續存檔落地,未列表。
  • SurveyPreview 的 preview 模式從 SurveyManage/SurveyDesigner 進入(屬 survey),fill 模式從 JobExecutionDrawer 開新分頁(屬 grc.job);同一支元件兩種歸屬,實作棒判斷要依 route.query.mode 分流。
  • 部門管理(OrgUnitManage)與租戶管理(TenantManage)所有寫入鈕都沒有能力點判斷;TenantManage 連授權唯讀 writeDisabled 也沒接。
  • 帳號列表「刪除」走 POST /user/change-status/(status -2),後端守 iam.user.update;iam.user.delete 守的 DELETE /user/ 前端沒有任何呼叫者,兩者語意不一致待決策。
  • 帳號管理整組(UserManage/UserManageUpload/UserMTRBACForm)只靠授權唯讀反灰,沒有 hasCap/can;UserMTRBACForm 的「變更密碼」開關用 userInfo.is_admin 判,不是能力點。
  • 新增帳號時的「新增角色列」不看 iam.user.assign,沒權限的人照樣能加列,存檔才被後端擋;對照編輯模式的 UserRoleSummary 已用 myCaps 擋。
  • AssignmentDialog/RoleImportDialog 本身不判,靠入口鈕擋;三個指派入口都已正確用 iam.user.assign。
  • AssignmentManage 讀用 hasCap('iam.user.read')(選單權限)、寫用 useMyCapabilities;RoleList/RoleDetail 一律用 useRoleCaps(=useMyCapabilities)。
  • Excel 批次建立(POST /user/batch-upload)會帶每列角色,附錄 A 未註明是否另要 iam.user.assign,與單筆 POST /user 不對稱,建議首腦確認。
  • AI 服務設定、Google Drive 應用程式設定、支援頁三頁今天都只靠「平台管理員」程式常數旗標(canManage/isPlatformAdmin)擋整頁,按鈕本身完全沒有能力點判斷。
  • 只有 LogForwardingForm、SecurityPolicyForm 已用 hasCap 遮寫入鈕,且名字正確;其餘設定頁只掛 FR-062 授權唯讀的 writeDisabled。
  • 多顆「測試」鈕(LDAP 測試連線、通知測試、SMTP 測試信、Drive 驗證)連 writeDisabled 都沒掛。
  • SystemMenuManage 完全沒有任何判斷(無 hasCap、無 writeDisabled),批次刪除是前端迴圈打單筆 DELETE。
  • SmtpConfigForm 的儲存鈕與 SystemMenuManage 的 dialog 儲存鈕都是新增/修改共用一顆,實作棒要依模式(uid/id 有無)選 create 或 update,不是 hasAny。
  • SMTP 列表刪除與編輯走 /system/config/<uid>,群組由後端依該列推得(SMTP);前端不帶群組代碼。
  • 對話紀錄的「清除對話」打 DELETE /ai-chatbot(ai.assistant.read),在 src/stores/aiChatStore.js:57,不在本批清單內,觸發它的按鈕需由別批確認。
  • 回饋頁沿用舊的「選單動作」機制(getUserPagePermissions 回 CREATE/UPDATE/READ),SystemFeedbackManage 的 view 模式是「選單能力點只有一個」推出來的,實作棒換 hasCap 時要連 FeedbackManageTabView.vue:69 一起改。
  • 設備匯入入口用 asset.device.create 擋,但三支匯入 API 的新名是 asset.device.import;匯出入口用 asset.device.read 擋,新名是 asset.device.export。
  • 設備新增鈕(DeviceManage.vue:364)與資訊系統新增/編輯/刪除全部沒有能力點判斷,只有 writeDisabled() 授權唯讀反灰。
  • LicenseManage.vue 整頁沒有任何能力點判斷,完全靠選單過濾擋人。
  • POST /bulletins 依能力點分讀者/管理模式,BulletinManage 前端用 props.view 區分;BulletinList 傳 view=true,寫入鈕已藏。