香港公司秘書/公司治理專業資格考試 CGQP|《戰略管理》模擬題庫

每位學員用自己的帳號登入,錯題本與練習記錄會分開存放,不會互相混淆。 帳號資料储存在您目前使用的這台裝置瀏覽器內。
📖 考點速覽|12 章重點蒸餾(開考前 5 分鐘過一遍)(點擊展開)
第1章 · 戰略概念與層級
  • 戰略層級:公司層(where)、業務層(how to compete)、職能層
  • 戰略 = 分析—選擇—實施的循環
  • 使命、願景、價值觀與戰略的關系
第2章 · 外部環境分析
  • PESTEL:政治、經濟、社會、科技、環境、法律
  • Porter 五力:現有競爭、新進入者、替代品、買方與賣方議價力
  • 戰略群組圖與競爭定位
第3章 · 內部資源與能力
  • 資源基礎觀(RBV):VRIO——有價值、稀缺、難模仿、組織利用
  • 價值鏈分析:主要活動與支援活動
  • SWOT:內部(S/W)× 外部(O/T)的組合策略
第4章 · 戰略選擇與評估
  • Ansoff 矩陣:市場滲透、市場開發、產品開發、多元化
  • Porter 通用戰略:成本領先、差異化、聚焦
  • 評估準則 SAFE:可接受性、合適性、可行性
第5章 · 戰略實施
  • McKinsey 7S:戰略、結構、制度、風格、人員、技能、共同價值
  • Kotter 八步變革:從營造緊迫感到鞏固成果
  • 結構跟隨戰略(Chandler)
第6章 · 戰略控制與變革
  • 平衡計分卡(BSC)四維度:財務、客戶、內部流程、學習成長
  • KPI 與 OKR 的設計與聯動
  • 戰略控制:反饋循環與偏差修正
第7章 · 行業動態
  • 行業生命周期:導入、成長、成熟、衰退期的戰略含義
  • 顛覆式創新(Christensen):低端切入→向上蠶食
  • 龍頭與追隨者的不同打法
第8章 · 國際化戰略
  • CAGE 距離框架:文化、行政、地理、經濟距離
  • 進入模式:出口、授權、特許、合資、全資附屬
  • Uppsala 模型:漸進式國際化
第9章 · 數字戰略
  • 平台經濟與網絡效應:用戶越多價值越大
  • 數字化轉型:業務流程→商業模式→生態重構
  • 數據作為戰略資產
第10章 · 創新與商業模式
  • 商業模式畫布九要素:客戶細分、價值主張、渠道、收入流…
  • 藍海戰略:價值創新、四步動作框架(剔除-減少-增加-創造)
  • 免費/訂閱等新模式的收入邏輯
第11章 · 組織設計
  • 集權 vs 分權的取捨;矩陣結構的雙線匯報
  • 敏捷組織:小團隊、快速迭代
  • 組織設計須配合戰略與規模
第12章 · 情景應用
  • 綜合情景:競爭策略選擇、變革落地、跨國擴張推演

※ 以上為按課程大綱整理的自編備考重點,並非官方考試內容;系統學習請參閱 HKCGI 官方課程資料。

120題庫總題數
50%合格線
75%優秀線
第 1 / 100 題 模擬練習
0%
合格

📊 各章節表現

❌ 錯題回顧(已加入錯題庫)

📕 我的錯題本(0 題)

每次答錯的題目會自動記錄到這裡;在「錯題重做」中答對後會自動移出。

⚠️ 我的異議清單(0 條)

做題時點「⚠️ 質疑此題」即可記錄。確認後請一次過傳送給我們跟進核實。

🗂 我的練習記錄

👤 我的帳號

🔑 修改密碼

const BANK = [[1,1,"1","公司戰略的三個層級是:","公司層(在哪裏競爭)、業務層(如何競爭)、職能層(如何支援)","年度、季度、月度","生產、銷售、財務","高、中、低","A","三層戰略架構確保資源配置、競爭方式與日常運營相互銜接。"],[2,1,"1","願景(vision)與使命(mission)的區別是:","願景描繪長期想要達到的狀態;使命說明存在目的與服務對象","兩者相同","使命比願景更長遠","願景無須公開","A","願景面向未來圖景,使命界定宗旨與價值,共同構成戰略的起點。"],[3,1,"1","「戰略管理過程」的基本順序是:","戰略分析→戰略選擇→戰略實施→戰略控制","實施→分析→選擇","選擇→分析→實施","控制→分析→選擇","A","戰略管理是循環過程:分析內外部環境、評估並選擇方案、執行與檢討。"],[4,1,"1","「湧現戰略」(emergent strategy)的含義是:","戰略部分透過實踐中逐步形成的模式自然演化,而非全部由頂層規劃","完全無計劃","只由基層決定","書面即戰略","A","Mintzberg 指出實際戰略=刻意設計+湧現調整,管理層須保持學習與調適。"],[5,1,"1","利益相關者分析在戰略制定中的目的是:","識別各方期望與權力,評估戰略的支援與阻力","減少股東人數","取代競爭分析","純屬形式","A","門德洛矩陣等工具按權力與利益分類持份者,指導溝通與管理策略。"],[6,1,"1","董事會在戰略管理中的角色是:","批准戰略方向、質詢並監督執行,而非親自制定所有細節","親自執行全部計劃","完全放手不干預","只簽署文件","A","董事會負責戰略把關(核准、資源、監督),管理層負責起草與執行。"],[7,1,"1","「商業模式」的定義是:","企業創造、交付並獲取價值的邏輯與架構","盈利的口號","組織架構圖","會計準則","A","商業模式畫布等工具描繪價值主張、客戶、渠道、收入與成本結構。"],[8,1,"1","戰略管理中的「清晰度」之所以重要,是因為:","清晰戰略使全組織資源與決策方向一致,便於問責","讓文件更長","滿足審計要求","減少溝通","A","戰略須被各層級理解並轉化為目標與行動,模糊戰略導致資源分散。"],[9,1,"1","企業社會責任與 ESG 融入戰略的方式是:","將可持續發展考量嵌入戰略分析、目標與績效指標","只在年報提及","交給公益部門","與戰略無關","A","ESG 已影響監管、融資與消費者選擇,須作為戰略變數而非附屬活動。"],[10,1,"1","數碼化對戰略的影響主要體現在:","改變價值創造方式、競爭邊界與進入壁壘","只影響 IT 部門","無實質影響","只降低成本","A","數碼平台、數據網絡效應重構行業結構,戰略分析須納入數碼生態視角。"],[11,2,"2","PESTEL 分析的六個維度是:","政治、經濟、社會、科技、環境、法律","產品、價格、渠道、促銷、人員、流程","優勢、劣勢、機會、威脅、風險、回報","規劃、執行、檢查、行動、報告、改進","A","PESTEL 是宏觀環境掃描框架,識別外部驅動因素及其戰略影響。"],[12,2,"2","波特五力不包括:","員工流失率","現有競爭者、新進入者、替代品、買方議價力、賣方議價力","五力即前述五項","以上皆是","A","五力模型分析行業盈利結構;員工流失屬內部人力因素,不在五力之中。"],[13,2,"2","戰略集團(strategic group)的概念是指:","同一行業內採用相似戰略特徵的企業集合","行業協會","競爭對手的董事會","跨行業聯盟","A","戰略集團分析協助定位競爭距離與流動壁壘。"],[14,2,"2","「關鍵成功因素」(CSF)是指:","在特定行業取得成功必須做好的少數關鍵領域","所有任務清單","KPI 的別稱","財務指標","A","CSF 引導資源聚焦,如零售的選址與供應鏈、製藥的研發管線。"],[15,2,"2","市場進入壁壘的來源包括:","規模經濟、品牌、監管牌照、網絡效應與轉換成本","競爭者利潤低","天氣","公司成立時間","A","壁壘決定新進入者威脅強度,是行業結構分析的核心變數。"],[16,2,"2","「機會與威脅」的評估應基於:","外部環境變化對企業目標的潛在正面或負面影響","內部能力清單","員工意見","財務報表數字","A","機會威脅屬外部維度,與內部優劣勢(SWOT 的另兩項)區分。"],[17,2,"2","競爭者分析應涵蓋的要素是:","競爭者的戰略、目標、能力與假設(戰略假設圖)","只看價格","只看廣告","無須分析","A","理解競爭者的下一步行動邏輯,有助預判競爭動態。"],[18,2,"2","宏觀趨勢轉化為戰略議題的過程稱為:","環境掃描與議題分析(issue analysis)","內控測試","審計抽樣","預算編製","A","將 PESTEL 驅動因素評估為機率×影響,轉為戰略風險與議題清單。"],[19,2,"2","新興市場與成熟市場的戰略差異通常表現在:","基礎設施、渠道結構、監管與消費行為不同,須調整進入模式與定價","戰略完全相同","只須降價","無須調研","A","國際化須做 CAGE 距離等分析,選擇出口、合資、併購等進入模式。"],[20,2,"2","監管變化作為戰略變數的示例是:","ESG 披露要求、數據法規或市場准入政策改變競爭規則與成本結構","只影響會計","只影響人事","無戰略意義","A","監管既是合規成本也是競爭壁壘來源,前瞻性監察可轉為先發優勢。"],[21,3,"3","資源基礎觀(RBV)認為持續競爭優勢來自:","有價值、稀缺、難以模仿且組織化利用(VRIO)的資源與能力","規模最大","廣告最多","價格最低","A","VRIO 標準判斷資源能否支撐持續優勢,引導核心能力建設。"],[22,3,"3","價值鏈分析(Porter)將企業活動分為:","主要活動與支援活動","高層與基層活動","內部與外部審計","收入與支出","A","主要活動(進料、生產、出貨、營銷、服務)+支援活動(採購、技術、人力、基建)定位優勢來源。"],[23,3,"3","核心競爭力(core competence)的特徵是:","跨業務的獨特能力,能開拓多個市場並難以複製","單一產品技術","一次性專案","外包能力","A","Prahalad 與 Hamel 強調以核心能力為本位的多元化與競爭邏輯。"],[24,3,"3","SWOT 分析的正確用法是:","內部優劣勢與外部機會威脅匹配,形成 SO/WO/ST/WT 策略組合","四格羅列即完成","只談優勢","用於審計抽樣","A","SWOT 的價值在於交叉配對轉化為可行策略,而非靜態列表。"],[25,3,"3","Benchmarking(標杆管理)的目的是:","對照最佳實務識別績效差距與改進方向","抄襲競爭對手","滿足審計要求","減少披露","A","以行業領先者或跨行業最佳實踐為參照,驅動流程與績效改進。"],[26,3,"3","無形資產(品牌、數據、人才、組織能力)在戰略中的地位是:","往往是現代企業價值與優勢的主要來源","不重要","只計入折舊","可隨意複製","A","無形資產難以模仿且規模效應強,是估值溢價與護城河的基礎。"],[27,3,"3","組織能力評估常見的框架是:","7S 模型(戰略、結構、制度、風格、人員、技能、共同價值)","三張報表","PESTEL","五力","A","McKinsey 7S 強調硬性(戰略結構制度)與軟性要素的匹配。"],[28,3,"3","「動態能力」(dynamic capabilities)是指:","感知機會、抓住機會並重構資源以適應變化的能力","靜態資產規模","維持現狀的能力","成本控制","A","Teece 的動態能力理論解釋快速變化環境中企業持續優勢的來源。"],[29,3,"3","內部財務分析對戰略選擇的約束體現在:","可用資源與融資能力決定可行戰略的邊界","財務與戰略無關","戰略由財務部單獨決定","只看歷史利潤","A","資源限制與投資回報要求是篩選戰略選項的基本約束條件。"],[30,3,"3","「能力缺口分析」的下一步通常是:","制定發展、收購、合作或外包等縮缺口的方案","忽略缺口","立即裁員","維持現狀","A","識別缺口後以建設/購買/借用的路徑組合獲取所需能力。"],[31,4,"4","波特的三種通用競爭戰略是:","成本領先、差異化、集中化","增長、穩定、收縮","多元化、一體化、國際化","進攻、防守、撤退","A","卡在中間(stuck in the middle)通常難以建立持續優勢,須清晰選擇。"],[32,4,"4","安索夫矩陣的四個增長選項是:","市場滲透、市場開發、產品開發、多元化","買、賣、合併、清盤","降價、促銷、廣告、折扣","計劃、執行、檢查、行動","A","以產品×市場組合定位增長路徑,風險由左上向右下遞增。"],[33,4,"4","BCG 矩陣按哪兩個維度分類業務?","市場增長率與相對市場份額","利潤與成本","規模與年限","風險與回報","A","明星、金牛、問題、瘦狗四象限指導資源配置組合管理。"],[34,4,"4","垂直一體化(vertical integration)的含義是:","向供應鏈上游(後向)或下游(前向)延伸業務","擴大產品品類","進入新地區市場","增加廣告","A","一體化以控制供應/渠道換取確定性與利潤,代價是資本與靈活性。"],[35,4,"4","戰略聯盟相對併購的優勢是:","投入較少、靈活性高、共享能力與風險,但控制與整合弱","無任何風險","控制權最強","成本最高","A","聯盟適合互補能力與不確定市場,須以清晰協議治理。"],[36,4,"4","評估戰略選項的常用標準是:","適合性(suitability)、可接受性(acceptability)、可行性(feasibility)","顏色、形狀、大小","過去、現在、未來","收入、成本、利潤","A","SAF 三準則過濾戰略選項:是否應對議題、回報風險可否接受、資源能力是否可行。"],[37,4,"4","「藍海戰略」的核心思想是:","透過價值創新開創無直接競爭的新市場空間","在紅海降價競爭","模仿領先者","專注細分價格戰","A","以剔除-減少-增加-創造四步重構價值曲線,跳出零和競爭。"],[38,4,"4","數位化轉型作為戰略選擇時,董事會最應關注:","與核心價值主張的匹配、投資回報、組織能力與風險","只買設備","一次性專案","與治理無關","A","轉型是系統性戰略工程,涉及商業模式、流程、人才與網絡安全。"],[39,4,"4","退出戰略(divestment)的合理情形是:","業務長期無法達到要求回報且無改善空間,資源應重新配置","一律永不退出","情緒化決策","由競爭對手決定","A","有序退出(出售、關閉、分拆)是組合管理的重要紀律。"],[40,4,"4","「低成本航空公司」案例體現的戰略邏輯是:","重新設計活動組合以實現結構性成本優勢","只靠裁員","只靠低薪","只靠政府補貼","A","競爭優勢源於活動系統的整體一致性,單點措施難以複製。"],[41,5,"5","戰略與結構的關係(Chandler):","結構追隨戰略——組織結構須為戰略服務並隨之調整","結構決定一切","戰略無須考慮結構","兩者無關","A","實施時須檢視結構、流程與授權是否支撐所選戰略。"],[42,5,"5","平衡計分卡(BSC)的四個維度是:","財務、客戶、內部流程、學習與成長","收入、成本、利潤、現金","戰略、結構、制度、文化","計劃、執行、檢查、行動","A","BSC 將戰略轉化為四維度目標與 KPI,實現財務與先導指標平衡。"],[43,5,"5","OKR 與 KPI 的區別在於:","OKR 強調雄心目標與關鍵結果的對齊,KPI 側重持續監控既定指標","完全相同","OKR 不用於企業","KPI 不能量化","A","兩者可互補:KPI 保運營底線,OKR 驅動突破性目標。"],[44,5,"5","變革管理(Kotter 八步)的起點是:","建立緊迫感(sense of urgency)","宣布勝利","解散團隊","修改願景","A","變革須先讓組織理解為何改變,再建立聯盟、溝通願景並鞏固成果。"],[45,5,"5","戰略實施中「組織文化」的作用是:","影響行為模式與變革接受度,是實施成敗的隱性變數","只影響裝修","無實質作用","只在招聘重要","A","文化與激勵若與戰略相悖,計劃再好也難落地。"],[46,5,"5","戰略專案組合管理(portfolio management)的目的是:","以統一標準排序與監察專案,確保資源投向戰略優先級","增加會議","取代預算","分散責任","A","專案是戰略落地的載體,組合管理確保優先級、資源與風險平衡。"],[47,5,"5","激勵與戰略一致性的要點是:","考核與獎酬須導向戰略目標(長短期平衡),避免只獎短期利潤","獎金越高越好","與戰略無關","只獎銷售額","A","激勵錯位是戰略執行走樣的常見根因,須設計平衡的指標組合。"],[48,5,"5","「中層管理者」在戰略實施中的作用是:","承上啟下轉化目標、協調資源並回饋一線資訊","只傳達命令","可完全繞過","只做行政","A","中層是戰略轉譯與回饋的樞紐,忽視中層是實施失敗的常見原因。"],[49,5,"5","戰略溝通的基本原則是:","分層次、多渠道、持續地傳達「為何、是什麼、如何做」","一次公告即完成","只向高層溝通","保密到底","A","有效的戰略溝通讓員工理解意義與自身角色,支撐執行一致性。"],[50,5,"5","併購整合(PMI)中最常見的失敗原因是:","文化衝突與關鍵人才流失,整合計劃滯後","價格太低","法規限制","媒體報道","A","交易後100天的整合規劃(文化、人才、流程、系統)決定併購成敗。"],[51,6,"6","戰略控制與營運控制的區別在於:","戰略控制關注「做的方向是否正確」,營運控制關注「執行是否符合計劃」","完全相同","戰略控制只看成本","營運控制更重要","A","兩者層級不同:戰略控制檢視假設與環境變化,營運控制監察效率。"],[52,6,"6","「領先指標」與「滯後指標」的差異是:","領先指標預示未來結果(如管線、培訓時數),滯後指標反映已發生結果(如利潤)","無區別","滯後指標更有預測性","領先指標等於財務報表","A","平衡的計分系統須組合兩類指標,才能既監察結果又預警風險。"],[53,6,"6","環境監察(environmental scanning)在控制階段的作用是:","持續追蹤假設是否失效,觸發戰略調整","只做年報","代替審計","無須進行","A","戰略建立時的關鍵假設(市場、監管、技術)須定期覆核。"],[54,6,"6","組織慣性(organisational inertia)的表現是:","結構、流程、文化與既有投資使變革難以推動","員工過於積極","變革太容易","戰略太靈活","A","克服慣性須領導承諾、快速的小勝、資源重新配置與文化干預。"],[55,6,"6","危機對戰略的啟示是:","危機暴露戰略脆弱點,須納入韌性設計與情景規劃","無須復盤","回到原狀即可","只怪運氣","A","危機後復盤(post-mortem)將經驗轉為戰略調整與能力建設。"],[56,6,"6","「戰略復盤」會議的良好實踐是:","基於事實與數據檢討假設、執行與結果,形成明確的調整決議","互相表揚","追究個人責任為主","無議程閒聊","A","復盤的目的是學習與修正,須有結構化議程與後續行動跟蹤。"],[57,6,"6","董事會評估戰略執行的資訊來源應包括:","管理層匯報、內審與風險報告、外部市場數據及獨立顧問意見","只聽管理層","只看新聞","無須資訊","A","多元資訊來源降低「管理層過濾」風險,支撐獨立判斷。"],[58,6,"6","「轉型中的員工參與」之所以重要是因為:","參與提升認同與執行質素,減少阻力並釋放基層洞察","純屬形式","拖延進度","增加成本","A","變革的成功率與參與度高度相關,溝通之外須創造貢獻機會。"],[59,6,"6","ESG 目標納入戰略控制的方式是:","設定可量化的 ESG KPI 並與薪酬及匯報機制掛鈎","只寫在願景","與考核無關","交由外部","A","ESG 須如財務指標一樣納入目標體系與問責機制,方有執行力。"],[60,6,"6","戰略敏捷性(strategic agility)的內涵是:","快速感知變化並調整資源與方向的能力,兼顧穩定與靈活","朝令夕改","拒絕變化","只求速度","A","敏捷組織在核心穩定(使命、治理)與邊緣靈活(試驗、迭代)之間平衡。"],[121,7,"7","行業生命週期的階段是:","萌芽、成長、成熟、衰退","計劃執行檢查行動","高中低端","內外部","A","不同階段的競爭重點與戰略(增長vs效率)不同。"],[122,7,"7","「顛覆式創新」(disruptive innovation)的特徵是:","從低端或新市場切入,逐步改善至取代在位者","一定更先進","無威脅","只屬科技業","A","Christensen 模型解釋在位者為何被「不夠好」的方案取代。"],[123,7,"7","「價格戰」的戰略後果通常是:","行業利潤整體受損,須以結構性優勢而非降價競爭","必然利好","可持續","無影響","A","價格戰消耗價值,除非有成本結構支撐。"],[124,7,"7","「平台經濟」的競爭邏輯是:","網絡效應使規模與生態成為護城河","成本最低","傳統廣告","單點技術","A","平台競爭是多邊市場與生態鎖定的競爭。"],[125,7,"7","「監管套利」的戰略風險是:","政策收緊時業務模式可能一夜失效","無風險","長期穩定","鼓勵違規","A","依賴監管空隙的商業模式須評估政策韌性。"],[126,7,"7","「戰略承諾」(strategic commitment)與靈活性的權衡是:","重大投資傳遞承諾可威懾競爭,但降低轉身空間","無權衡","越大越靈活","無關係","A","博弈論視角:承諾價值與選擇權價值此消彼長。"],[127,7,"7","「互補者」(complementor)在生態中的角色是:","提升本產品價值的合作方(如開發者生態)","純競爭者","無角色","供應商唯一","A","六力模型納入互補者,生態協同是價值來源。"],[128,7,"7","「在位者反應」的典型障礙是:","既得利益、渠道衝突與資源被舊業務綁定","反應太快","無障礙","新技術必勝","A","創新者窘境:理性管理在位業務反而錯失轉型。"],[129,7,"7","「行業整合期」的常見戰略動作是:","併購整合、聚焦規模與成本曲線","擴產降價","多元化突進","退出行業","A","成熟期價值來自整合效率與市場份額。"],[130,7,"7","「地緣政治」對跨國戰略的影響是:","供應鏈區域化、市場准入與合規成本重構","無影響","只影響出口","無關戰略","A","戰略須納入地緣情景與韌性設計。"],[131,8,"8","CAGE 距離框架衡量的是:","文化、行政、地理與經濟距離","物理運輸","時區","貨幣","A","Ghemawat 框架協助評估跨境經營的摩擦成本。"],[132,8,"8","「進入模式」由低控制到高控制排序是:","出口→授權→合資→獨資","獨資→出口","合資→出口","無順序","A","控制與風險投入同步遞增,須按市場與資源選擇。"],[133,8,"8","「全球化-本土化」權衡(glocalization)指:","全球規模效益與本地適應的平衡","完全標準化","完全本地","無平衡","A","價值鏈配置決定哪些環節全球統一、哪些本地調適。"],[134,8,"8","「新興市場」的治理挑戰包括:","產權保護、合規環境與合作夥伴盡調","無挑戰","只語言問題","市場太小","A","新興市場須強化治理與風控以補制度缺口。"],[135,8,"8","「跨國公司治理」的特殊議題是:","多司法轄區合規、外派治理與集團控制","無議題","只語言","單一監管","A","集團治理架構須兼顧本地法規與集團標準。"],[136,8,"8","「合資夥伴選擇」的關鍵標準是:","戰略互補、治理文化契合與退出機制","只看規模","最便宜","親友介紹","A","夥伴錯配是國際合資失敗的主因。"],[137,8,"8","「外匯轉移風險」的管理是:","評估資本與利潤匯出限制,設計結構對沖","無風險","只靠保險","忽略","A","資本管制市場須預設現金回流路徑。"],[138,8,"8","「全球品牌與本地品牌」組合的策略是:","按市場成熟度與消費者認知分配品牌架構","一律統一","一律分開","無策略","A","品牌架構是國際化戰略的重要執行工具。"],[139,8,"8","「跨境合規」的基本設置是:","制裁、反腐(FCPA類)與數據跨境的集團政策","無設置","只總部適用","事後補","A","集團合規標準須落地到各法域並培訓監察。"],[140,8,"8","「撤出市場」的決策訊號是:","結構性虧損、監管風險失控或戰略不匹配","短期虧損即退","永不退出","競爭者建議","A","退出是組合管理的一部分,須基於結構性判斷。"],[141,9,"9","「數據網絡效應」的商業價值在於:","用戶越多數據越多,產品與壁壘同步增強","無價值","只省成本","監管要求","A","數據驅動型學習循環是數字平台的護城河。"],[142,9,"9","「平台治理」的議題包括:","開放度、規則公平與生態夥伴利益分配","無議題","只管技術","放任發展","A","平台規則設計直接影響生態健康與監管風險。"],[143,9,"9","「數字化轉型失敗」的常見原因是:","技術項目化而非戰略驅動,組織與流程未變","技術太貴","無失敗","太成功","A","轉型是組織變革,工具只是載體。"],[144,9,"9","「首席數字官/首席數據官」的角色是:","統籌數字戰略、數據治理與跨部門落地","只管機房","無角色","代替 CIO","A","數字職能須有治理層級與預算支撐。"],[145,9,"9","「AI 戰略」的評估框架應包括:","用例價值、數據基礎、治理與倫理風險","只買模型","無框架","只看成本","A","AI 須從價值與風險雙側納入治理。"],[146,9,"9","「網絡安全」作為戰略議題的原因是:","安全事件影響信任、監管與持續經營","只是 IT 事務","無關戰略","外包即可","A","董事會須將網安納入風險治理與投資優先級。"],[147,9,"9","「平台監管」趨勢(如反壟斷、數據法)的戰略影響是:","互操作性要求與數據壁壘削弱可能重構競爭","無影響","有利大平台","永遠不變","A","監管改變平台規模邏輯,須前置應對。"],[148,9,"9","「數據變現」的合規邊界是:","私隱同意、最小化與跨境限制","無邊界","自由買賣","由銷售定","A","數據商業化須在私隱法框架內設計。"],[149,9,"9","「數字渠道衝突」的管理是:","價格與服務區隔、夥伴激勵重新設計","禁止線上","忽視經銷商","無管理","A","全渠道轉型須管理傳統渠道的利益衝突。"],[150,9,"9","「IT 與戰略對齊」的衡量是:","IT 投資組合對戰略目標的覆蓋與回報","伺服器數量","預算大小","無衡量","A","技術投資須以戰略貢獻與風險為評估標準。"],[151,10,"10","「探索與利用」(explore vs exploit)的平衡是:","兼顧新機會探索與現有業務效率","只探索","只利用","無平衡","A","組織須雙元(ambidextrous)運營才能持續。"],[152,10,"10","「精益創業」(lean startup)的核心是:","快速假設驗證與迭代(MVP→學習→調整)","大規模規劃","完美產品後上市","無實驗","A","以最小成本驗證需求,降低不確定性。"],[153,10,"10","「開放式創新」的機制包括:","外部合作、眾包、收購早期技術","全部內部","封閉研發","禁止合作","A","創新邊界外移擴大知識來源。"],[154,10,"10","「商業模式創新」與產品創新的區別是:","重構價值創造/交付/獲取邏輯,而非只改產品","完全相同","更簡單","無區別","A","模式創新(如訂閱制)改變收入結構與客戶關係。"],[155,10,"10","「知識產權」在創新治理中的作用是:","保護並變現創新成果,支撐競爭地位","無作用","只法律文件","阻礙創新","A","IP 策略(申請、授權、防禦)須與創新戰略對齊。"],[156,10,"10","「創新組合」的管理原則是:","按風險與時間跨度分配資源(核心/相鄰/轉型)","全部押注轉型","只做核心","無組合","A","70-20-10 等配置平衡當期與未來。"],[157,10,"10","「失敗管理」的文化要素是:","安全試錯、快速復盤與知識沉澱","懲罰失敗","隱藏失敗","無管理","A","學習型組織將失敗轉化為能力。"],[158,10,"10","「內部創業」(intrapreneurship)的支撐條件是:","資源、自主權與激勵設計","只有口號","無支援","增加流程","A","內部創新單位須脫離日常考核束縛。"],[159,10,"10","「企業創投」(CVC)的戰略價值是:","接觸前沿技術與生態,作為併購管道","純財務回報","無價值","替代研發","A","CVC 以戰略為主、財務為輔。"],[160,10,"10","「技術路線圖」(technology roadmap)的作用是:","對齊技術演進與產品/市場時間表","裝飾文件","無作用","只給 IT","A","路線圖讓創新投資與戰略節奏同步。"],[161,11,"11","「集權與分權」的決定因素是:","規模、地理分佈、市場差異與風險控制需要","主席偏好","無因素","一律集權","A","結構選擇須匹配戰略與風險特徵。"],[162,11,"11","「矩陣式結構」的優點與代價是:","靈活調配專業能力,但雙重匯報易致權責模糊","無代價","只省人","無優點","A","矩陣須配清晰的衝突解決機制。"],[163,11,"11","「共享服務中心」的價值是:","集中後台職能提升效率與標準化","增加成本","無價值","分散管理","A","SSC 常見於財務、HR 與 IT 流程集中。"],[164,11,"11","「扁平化」的適用條件是:","知識型團隊、快速決策需要與成熟的人才密度","任何公司","人多部門多","無條件","A","扁平化依賴高素質人才與授權文化。"],[165,11,"11","「總部角色」的類型包括:","戰略控制、財務控制與戰略規劃","無角色","只行政","僅審批","A","Goold & Campbell 的總部角色框架指導管控模式。"],[166,11,"11","「附屬公司治理」的集團要點是:","法定董事履責、集團政策落地與本地合規","只看報表","無治理","全部由總部代管","A","附屬公司董事仍負個人法定責任,須兼顧集團與本地。"],[167,11,"11","「文化整合」在重組中的要點是:","先診斷文化差異、設計融合路徑與溝通","只合併制度","無需整合","裁員解決","A","文化診斷前置可大幅降低整合風險。"],[168,11,"11","「人才與戰略對齊」的工具是:","繼任規劃、關鍵崗位地圖與能力模型","無工具","只靠招聘","隨機調動","A","組織能力落實到關鍵崗位與人才梯隊。"],[169,11,"11","「流程再造」(BPR)的風險是:","過度裁減損害能力與士氣","無風險","一定成功","只省錢","A","再造須以端到端價值與能力保全為前提。"],[170,11,"11","「治理與管理界限」在組織設計中的意義是:","董事會定方向與監督,管理層執行,避免越界","混同即可","管理層定戰略不受監督","無界限","A","清晰的治理界限是問責與效率的基礎。"],[171,12,"12","新競爭者以低價進入,管理層建議全面降價,董事會應質詢:","成本結構能否支撐、是否損害品牌與長期定位","直接批准","直接否決","無須質詢","A","價格回應須與成本與定位策略一致。"],[172,12,"12","公司連續達成短期目標但市場份額下降,可能的問題是:","KPI 設計偏離戰略(重利潤輕市場)","無問題","目標太低","市場無關","A","指標與戰略錯位是執行走樣的典型。"],[173,12,"12","併購後協同遲遲未實現,董事會應:","檢視整合計劃、留任關鍵人才與整合治理機制","增加商譽","出售公司","無作為","A","協同實現須有專責整合組織與里程碑。"],[174,12,"12","新興市場擴張提案的盡調重點是:","合作夥伴合規背景、資金回流與退出安排","只看市場規模","只看匯率","無盡調","A","新興市場風險集中在制度與夥伴層面。"],[175,12,"12","創新項目連續失敗,管理層要求全部停止,董事會應:","區分項目組合狀態,保留高潛力探索並改善治理","全部停止","全部保留","無評估","A","創新組合須按學習價值與剩餘潛力管理。"],[176,12,"12","核心技術人員集體離職風險浮現,應:","啟動留任方案、知識文件化與繼任加速","只加薪","忽略","報警","A","人才風險須以組合措施(激勵、繼任、知識管理)應對。"],[177,12,"12","董事會收到「戰略執行度儀表板」,應關注:","領先指標趨勢、偏差原因與糾正行動","只看結果","無須關注","只看顏色","A","儀表板的價值在於驅動糾偏對話。"],[178,12,"12","供應鏈中斷事件後,戰略層面的調整是:","重新評估供應鏈韌性與區域配置","只加庫存","無調整","等下次","A","事件驅動的戰略復盤提升長期韌性。"],[179,12,"12","ESG 投資者質詢轉型計劃,管理層無量化路徑,應:","要求制定可量化轉型計劃並納入治理監督","口頭承諾","忽略質詢","購買評級","A","轉型計劃須具可驗證性,否則構成漂綠風險。"],[180,12,"12","年度戰略會議的產出應包括:","戰略選擇、資源配置決議、目標分解與檢視機制","只開會","無決議","旅遊團建","A","戰略會議的價值在於可執行的決議與跟蹤安排。"]]; const IDS = BANK.map(q => q[0]); const QMAP = {}; BANK.forEach(q => { QMAP[q[0]] = q; }); const PASS = 50, EXC = 75; let quiz = [], idx = 0, score = 0, answers = [], mode = 'normal'; let ME = null, quizStart = 0; /* ==================== 本地账号系统 ==================== */ const PAPER = 'cgqp_sm'; const K_ACCOUNTS = 'sunai_accounts_cgqp_sm'; const K_SESSION = 'sunai_session_cgqp_sm'; function wrongKey(){ return 'sunai_wrong_cgqp_sm::' + (ME ? ME.uid : 'guest'); } function recKey(){ return 'sunai_records_cgqp_sm::' + (ME ? ME.uid : 'guest'); } function lsGet(k, d){ try { const v = localStorage.getItem(k); return v == null ? d : JSON.parse(v); } catch(e){ return d; } } function lsSet(k, v){ try { localStorage.setItem(k, JSON.stringify(v)); } catch(e){} } function val(id){ const el = document.getElementById(id); return el ? el.value : ''; } function esc(s){ const d = document.createElement('div'); d.textContent = s == null ? '' : s; return d.innerHTML; } /* SHA-256:在 file:// 打开时 crypto.subtle 不可用,用它做兜底 */ function sha256hex(msg){ function rotr(x, n){ return (x >>> n) | (x << (32 - n)); } const K = [ 0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5, 0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174, 0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da, 0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967, 0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85, 0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070, 0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3, 0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2 ]; let H = [0x6a09e667,0xbb67ae85,0x3c6ef372,0xa54ff53a,0x510e527f,0x9b05688c,0x1f83d9ab,0x5be0cd19]; const bytes = []; const u = unescape(encodeURIComponent(msg)); for (let i = 0; i < u.length; i++) bytes.push(u.charCodeAt(i)); const l = bytes.length; bytes.push(0x80); while (bytes.length % 64 !== 56) bytes.push(0); const bitLen = l * 8; bytes.push(0, 0, 0, 0, (bitLen >>> 24) & 255, (bitLen >>> 16) & 255, (bitLen >>> 8) & 255, bitLen & 255); const w = new Array(64); for (let i = 0; i < bytes.length; i += 64){ for (let j = 0; j < 16; j++){ w[j] = (bytes[i+j*4] << 24) | (bytes[i+j*4+1] << 16) | (bytes[i+j*4+2] << 8) | bytes[i+j*4+3]; } for (let j = 16; j < 64; j++){ const s0 = rotr(w[j-15],7) ^ rotr(w[j-15],18) ^ (w[j-15] >>> 3); const s1 = rotr(w[j-2],17) ^ rotr(w[j-2],19) ^ (w[j-2] >>> 10); w[j] = (w[j-16] + s0 + w[j-7] + s1) | 0; } let a=H[0],b=H[1],c=H[2],d=H[3],e=H[4],f=H[5],g=H[6],h=H[7]; for (let j = 0; j < 64; j++){ const S1 = rotr(e,6) ^ rotr(e,11) ^ rotr(e,25); const ch = (e & f) ^ (~e & g); const t1 = (h + S1 + ch + K[j] + w[j]) | 0; const S0 = rotr(a,2) ^ rotr(a,13) ^ rotr(a,22); const maj = (a & b) ^ (a & c) ^ (b & c); const t2 = (S0 + maj) | 0; h=g; g=f; f=e; e=(d+t1)|0; d=c; c=b; b=a; a=(t1+t2)|0; } H = [(H[0]+a)|0,(H[1]+b)|0,(H[2]+c)|0,(H[3]+d)|0,(H[4]+e)|0,(H[5]+f)|0,(H[6]+g)|0,(H[7]+h)|0]; } let out = ''; for (let i = 0; i < 8; i++) out += ('00000000' + (H[i] >>> 0).toString(16)).slice(-8); return out; } function bytesToHex(b){ let s = ''; for (let i = 0; i < b.length; i++) s += ('0' + b[i].toString(16)).slice(-2); return s; } function hexToBytes(h){ const o = new Uint8Array(h.length / 2); for (let i = 0; i < o.length; i++) o[i] = parseInt(h.substr(i*2, 2), 16); return o; } function randSalt(){ const b = new Uint8Array(16); if (window.crypto && crypto.getRandomValues) crypto.getRandomValues(b); else for (let i = 0; i < 16; i++) b[i] = Math.floor(Math.random() * 256); return bytesToHex(b); } /* 有 WebCrypto 时用 PBKDF2(10万次),否则用迭代 SHA-256 */ async function makeHash(pwd, salt, algo){ if (algo !== 'sha256x3000' && window.crypto && crypto.subtle && window.TextEncoder){ try { const enc = new TextEncoder(); const key = await crypto.subtle.importKey('raw', enc.encode(pwd), 'PBKDF2', false, ['deriveBits']); const bits = await crypto.subtle.deriveBits( { name: 'PBKDF2', salt: hexToBytes(salt), iterations: 100000, hash: 'SHA-256' }, key, 256); return { algo: 'pbkdf2', hash: bytesToHex(new Uint8Array(bits)) }; } catch(e) { /* 落到下面 */ } } let h = salt + '|' + pwd; for (let i = 0; i < 3000; i++) h = sha256hex(h); return { algo: 'sha256x3000', hash: h }; } function getAccounts(){ return lsGet(K_ACCOUNTS, []); } function saveAccounts(a){ lsSet(K_ACCOUNTS, a); } function showMsg(id, text, kind){ const el = document.getElementById(id); if (!el) return; el.className = 'auth-msg show ' + (kind || ''); el.textContent = text; } function hideMsg(id){ const el = document.getElementById(id); if (el) el.className = 'auth-msg'; } function authMsg(text, kind){ const onReg = document.getElementById('rg-box').style.display !== 'none'; showMsg(onReg ? 'rg-msg' : 'lg-msg', text, kind); } async function doRegister(){ const login = val('rg-login').trim(); const p1 = val('rg-pwd'), p2 = val('rg-pwd2'); if (!login) return authMsg('請填寫登入帳號', 'bad'); if (login.length < 3) return authMsg('帳號至少 3 個字元', 'bad'); if (!p1) return authMsg('請設定密碼', 'bad'); if (p1.length < 4) return authMsg('密碼至少 4 位', 'bad'); if (p1 !== p2) return authMsg('兩次輸入的密碼不一致', 'bad'); const accs = getAccounts(); if (accs.some(a => a.login.toLowerCase() === login.toLowerCase())) return authMsg('此帳號已被使用,請換一個或直接登入', 'bad'); const salt = randSalt(); const h = await makeHash(p1, salt); const acc = { uid: 'u_' + Date.now().toString(36) + Math.random().toString(36).slice(2, 7), login: login, salt: salt, algo: h.algo, hash: h.hash, name: login, cls: '', createdAt: Date.now() }; accs.push(acc); saveAccounts(accs); loginAs(acc); } async function doLogin(){ const login = val('lg-login').trim(); const pwd = val('lg-pwd'); if (!login || !pwd) return showMsg('lg-msg', '請輸入帳號與密碼', 'bad'); const acc = getAccounts().find(a => a.login.toLowerCase() === login.toLowerCase()); if (!acc) return showMsg('lg-msg', '帳號或密碼不正確', 'bad'); const h = await makeHash(pwd, acc.salt, acc.algo); if (h.hash !== acc.hash) return showMsg('lg-msg', '帳號或密碼不正確', 'bad'); loginAs(acc); } function loginAs(acc){ ME = acc; lsSet(K_SESSION, acc.uid); // 首次登入时,继承旧版(未分账号)的错题本 const legacy = lsGet('sunai_wrong_cgqp_sm', []); if (legacy && legacy.length){ const mine = loadWrong(); if (!mine.length) saveWrong(legacy.slice()); } hideMsg('lg-msg'); hideMsg('rg-msg'); document.getElementById('lg-pwd').value = ''; document.getElementById('rg-pwd').value = ''; document.getElementById('rg-pwd2').value = ''; renderUserBar(); goHome(); } function doLogout(){ if (!confirm('確定登出?登出後可用其他帳號登入。')) return; ME = null; try { localStorage.removeItem(K_SESSION); } catch(e){} renderUserBar(); document.getElementById('lg-login').value = ''; document.getElementById('lg-pwd').value = ''; show('sc-auth'); } function renderUserBar(){ const bar = document.getElementById('userbar'); if (!bar) return; if (ME){ bar.style.display = 'flex'; document.getElementById('ub-name').textContent = ME.name || ME.login; document.getElementById('ub-cls').textContent = ME.cls ? '(' + ME.cls + ')' : ''; } else { bar.style.display = 'none'; } } function updateMe(){ if (!ME) return; document.getElementById('me-name').value = ME.name || ''; document.getElementById('me-class').value = ME.cls || ''; document.getElementById('me-login').value = ME.login || ''; hideMsg('me-msg'); hideMsg('pw-msg'); document.getElementById('pw-old').value = ''; document.getElementById('pw-new').value = ''; } function saveProfile(){ const accs = getAccounts(); const acc = accs.find(a => a.uid === ME.uid); if (!acc) return showMsg('me-msg', '找不到帳號,請重新登入', 'bad'); acc.name = val('me-name').trim() || acc.login; acc.cls = val('me-class').trim(); saveAccounts(accs); ME = acc; renderUserBar(); showMsg('me-msg', '已儲存', 'good'); } async function changePwd(){ const o = val('pw-old'), n = val('pw-new'); if (!o || !n) return showMsg('pw-msg', '請填寫目前密碼與新密碼', 'bad'); if (n.length < 4) return showMsg('pw-msg', '新密碼至少 4 位', 'bad'); const accs = getAccounts(); const acc = accs.find(a => a.uid === ME.uid); if (!acc) return showMsg('pw-msg', '找不到帳號,請重新登入', 'bad'); const ho = await makeHash(o, acc.salt, acc.algo); if (ho.hash !== acc.hash) return showMsg('pw-msg', '目前密碼不正確', 'bad'); const salt = randSalt(); const hn = await makeHash(n, salt); acc.salt = salt; acc.algo = hn.algo; acc.hash = hn.hash; saveAccounts(accs); ME = acc; document.getElementById('pw-old').value = ''; document.getElementById('pw-new').value = ''; showMsg('pw-msg', '密碼已修改', 'good'); } /* ==================== 错题本 / 记录(按账号) ==================== */ function loadWrong(){ return lsGet(wrongKey(), []); } function wcKey(){ return 'sunai_wrongcnt_' + PAPER + '::' + (ME ? ME.uid : 'guest'); } function loadWC(){ return lsGet(wcKey(), {}); } function saveWC(c){ lsSet(wcKey(), c); } function bumpWrong(id){ const c = loadWC(); c[id] = (c[id] || 0) + 1; saveWC(c); } function clearWrongCnt(id){ const c = loadWC(); if (c[id]) { delete c[id]; saveWC(c); } } function loadFreq(min){ const c = loadWC(); return Object.keys(c) .filter(id => QMAP[id] && c[id] >= min) .sort((a, b) => c[b] - c[a]).map(id => +id); } function saveWrong(w){ lsSet(wrongKey(), w); } function loadRecords(){ return lsGet(recKey(), []); } function addRecord(rec){ const r = loadRecords(); r.unshift(rec); if (r.length > 300) r.length = 300; lsSet(recKey(), r); } /* ==================== 異議回報 ==================== */ function rpKey(){ return 'sunai_reports_' + PAPER + '::' + (ME ? ME.uid : 'guest'); } function loadReports(){ return lsGet(rpKey(), []); } function toggleReport(){ const box = document.getElementById('rp-box'); const show = box.style.display === 'none'; box.style.display = show ? 'block' : 'none'; document.getElementById('rp-msg').style.display = 'none'; document.getElementById('rp-text').value = ''; } function submitReport(){ const q = quiz[idx]; const reason = document.getElementById('rp-text').value.trim(); if (!reason) { alert('請先填寫你的疑問。'); return; } const a = answers[answers.length - 1]; const r = loadReports(); r.push({ paper: PAPER, qid: q.id, ch: q.ch, ref: q.ref, q: q.q.slice(0, 120), myAns: a ? a.pick : '-', correctAns: q.ans, reason: reason.slice(0, 500), at: Date.now() }); lsSet(rpKey(), r); document.getElementById('rp-msg').style.display = 'block'; document.getElementById('rp-msg').textContent = '✅ 已記錄你的回報,感謝!你可以在「異議清單」中一次過傳送給我們。'; setTimeout(()=>{ document.getElementById('rp-box').style.display = 'none'; }, 1800); } function reportTextAll(){ const r = loadReports(); if (!r.length) return ''; return '【IIQE 題目異議回報 - ' + PAPER + '】\n' + r.map((x, i) => (i + 1) + '. 題號#' + x.qid + '(第' + x.ch + '章 ' + (x.ref || '') + ')\n' + '題目:' + x.q + '…\n' + '我的答案:' + x.myAns + '|現有答案:' + x.correctAns + '\n' + '疑問:' + x.reason ).join('\n\n'); } function copyReports(){ const txt = reportTextAll(); if (!txt) { alert('目前沒有異議回報。'); return; } (navigator.clipboard ? navigator.clipboard.writeText(txt) : Promise.reject()) .then(()=>alert('已複製,可貼上給我們。')) .catch(()=>{ const ta = document.createElement('textarea'); ta.value = txt; document.body.appendChild(ta); ta.select(); document.execCommand('copy'); document.body.removeChild(ta); alert('已複製,可貼上給我們。'); }); } function waReports(){ const txt = reportTextAll(); if (!txt) { alert('目前沒有異議回報。'); return; } window.open('https://wa.me/85268751658?text=' + encodeURIComponent(txt), '_blank'); } function openReports(){ const r = loadReports(); const box = document.getElementById('rp-list'); document.getElementById('rp-n').textContent = r.length; document.getElementById('rp-copy').disabled = r.length === 0; document.getElementById('rp-wa').disabled = r.length === 0; box.innerHTML = r.length ? r.slice().reverse().map(x => { const d = new Date(x.at); const pad = n => (n < 10 ? '0' + n : '' + n); return '
' + '
題號#' + x.qid + '(第' + x.ch + '章 ' + (x.ref || '') + ')
' + '
' + esc(x.q) + '…
' + '
我的答案 ' + x.myAns + '|現有答案 ' + x.correctAns + '
' + '
💬 ' + esc(x.reason) + '
' + '
' + d.getFullYear() + '-' + pad(d.getMonth()+1) + '-' + pad(d.getDate()) + '
' + '
'; }).join('') : '
目前沒有異議回報。
'; show('sc-reports'); } function clearWrongBank(){ if (!confirm('確定清空錯題本?此動作無法復原。')) return; saveWrong([]); openWrong(); } function openWrong(){ const w = loadWrong(); document.getElementById('wb-n').textContent = w.length; document.getElementById('wb-redo').disabled = w.length === 0; const fq = loadFreq(2); const wf = document.getElementById('wb-freq'); if (wf) wf.disabled = fq.length === 0; const box = document.getElementById('wb-list'); if (!w.length){ box.innerHTML = '
目前沒有錯題,繼續保持!
'; } else { box.innerHTML = w.map(id => { const q = QMAP[id]; if (!q) return ''; const ai = ['A','B','C','D'].indexOf(q[8]); const at = ai >= 0 ? q[4 + ai] : ''; const wc = loadWC()[id] || 1; const hot = wc >= 2 ? '❌ 錯 ' + wc + ' 次 ' : ''; return '
' + '
' + hot + esc(q[3].slice(0, 90)) + (q[3].length > 90 ? '…' : '') + '
' + '
正確答案:' + q[8] + '. ' + esc(at) + '
' + (q[9] ? '
💡 ' + esc(q[9]) + '
' : '') + '
參考章節 ' + esc(q[2] || '—') + '
' + '
'; }).join(''); } show('sc-wrong'); } function openRecords(){ const r = loadRecords(); const box = document.getElementById('rc-list'); if (!r.length){ box.innerHTML = '
還沒有練習記錄,先做一份模擬練習吧!
'; } else { box.innerHTML = r.map(x => { const d = new Date(x.at); const pad = n => (n < 10 ? '0' + n : '' + n); const when = d.getFullYear() + '-' + pad(d.getMonth()+1) + '-' + pad(d.getDate()) + ' ' + pad(d.getHours()) + ':' + pad(d.getMinutes()); const tone = x.pct >= EXC ? 'var(--ok)' : (x.pct >= PASS ? 'var(--navy2)' : 'var(--bad)'); return '
' + '
' + x.mode + '' + '' + x.pct + '% ' + x.grade + '
' + '
答對 ' + x.correct + ' / ' + x.total + ' 題 · ' + when + '
' + '
'; }).join(''); } show('sc-records'); } function openMe(){ updateMe(); show('sc-me'); } /* ==================== 首页 / 抽题 / 答题 ==================== */ function refreshHome(){ document.getElementById('st-bank').textContent = BANK.length; const w = loadWrong(); document.getElementById('wrong-n').textContent = w.length; document.getElementById('btn-wrong').disabled = w.length === 0; const fn = loadFreq(2); const fe = document.getElementById('freq-n'); if (fe) fe.textContent = fn.length; const bf = document.getElementById('btn-freq'); if (bf) bf.disabled = fn.length === 0; const rn = loadReports().length; const rh = document.getElementById('rp-home-n'); if (rh) rh.textContent = rn; renderUserBar(); } let COUNT = 100; document.querySelectorAll('#seg-count button').forEach(b=>{ b.onclick = ()=>{ document.querySelectorAll('#seg-count button').forEach(x=>x.classList.remove('on')); b.classList.add('on'); COUNT = b.dataset.n === 'all' ? BANK.length : +b.dataset.n; }; }); (function(){ const ab = document.querySelector('#seg-count button[data-n="all"]'); if(ab) ab.textContent = '全部題庫 ' + BANK.length + ' 題'; })(); document.querySelectorAll('#seg-auth button').forEach(b=>{ b.onclick = ()=>{ document.querySelectorAll('#seg-auth button').forEach(x=>x.classList.remove('on')); b.classList.add('on'); const isLogin = b.dataset.tab === 'login'; document.getElementById('lg-box').style.display = isLogin ? 'block' : 'none'; document.getElementById('rg-box').style.display = isLogin ? 'none' : 'block'; hideMsg('lg-msg'); hideMsg('rg-msg'); }; }); document.addEventListener('keydown', function(e){ if (e.key !== 'Enter') return; if (document.getElementById('sc-auth').classList.contains('on')){ if (document.getElementById('rg-box').style.display === 'none') doLogin(); else doRegister(); } }); function show(id){ document.querySelectorAll('.screen').forEach(s=>s.classList.remove('on')); document.getElementById('sc-'+id.replace('sc-','')).classList.add('on'); window.scrollTo(0,0); } function goHome(){ refreshHome(); document.querySelectorAll('.screen').forEach(s=>s.classList.remove('on')); document.getElementById('sc-home').classList.add('on'); window.scrollTo(0,0); } function pickQuestions(){ const w = loadWrong(); if(mode === 'freq'){ const set = new Set(loadFreq(2)); return BANK.filter(q=>set.has(q[0])).sort((a,b)=>{ const c = loadWC(); return (c[b.id]||0)-(c[a.id]||0); }).map(q=>{ const letters = ['A','B','C','D']; const opts = [q[4],q[5],q[6],q[7]]; const ansIdx = letters.indexOf(q[8]); const idxs = [0,1,2,3].sort(()=>Math.random()-.5); const newOpts = idxs.map(j=>opts[j]); const newAns = letters[idxs.indexOf(ansIdx)]; return {id:q[0], ch:q[1], ref:q[2], q:q[3], opts:newOpts, ans:newAns, expl:q[9]}; }); } if(mode === 'wrong'){ const set = new Set(w); return BANK.filter(q=>set.has(q[0])).sort(()=>Math.random()-.5).map(q=>{ const letters = ['A','B','C','D']; const opts = [q[4],q[5],q[6],q[7]]; const ansIdx = letters.indexOf(q[8]); const idxs = [0,1,2,3].sort(()=>Math.random()-.5); const newOpts = idxs.map(j=>opts[j]); const newAns = letters[idxs.indexOf(ansIdx)]; return {id:q[0], ch:q[1], ref:q[2], q:q[3], opts:newOpts, ans:newAns, expl:q[9]}; }); } const byCh = {}; BANK.forEach(q=>{ (byCh[q[1]] = byCh[q[1]]||[]).push(q); }); Object.values(byCh).forEach(a=>a.sort(()=>Math.random()-.5)); const total = BANK.length; const picks = []; const counts = {}; let assigned = 0; for(const ch in byCh){ counts[ch] = Math.floor(byCh[ch].length/total*COUNT); assigned += counts[ch]; } let rem = COUNT - assigned; const order = Object.keys(byCh).sort((a,b)=>byCh[b].length-byCh[a].length); let i = 0; while(rem > 0){ counts[order[i%order.length]]++; rem--; i++; } for(const ch in counts){ counts[ch] = Math.min(counts[ch], byCh[ch].length); picks.push(...byCh[ch].slice(0, counts[ch])); } if(picks.length < COUNT){ const used = new Set(picks.map(q=>q[0])); for(const q of BANK){ if(picks.length>=COUNT) break; if(!used.has(q[0])) picks.push(q); } } return picks.sort(()=>Math.random()-.5).map(q=>{ const letters = ['A','B','C','D']; const opts = [q[4],q[5],q[6],q[7]]; const ansIdx = letters.indexOf(q[8]); const idxs = [0,1,2,3].sort(()=>Math.random()-.5); const newOpts = idxs.map(j=>opts[j]); const newAns = letters[idxs.indexOf(ansIdx)]; return {id:q[0], ch:q[1], ref:q[2], q:q[3], opts:newOpts, ans:newAns, expl:q[9]}; }); } function startQuiz(isWrong, isFreq){ if(!ME){ show('sc-auth'); return; } mode = isWrong ? 'wrong' : (isFreq ? 'freq' : 'normal'); quiz = pickQuestions(); if(!quiz.length){ alert(mode==='wrong' ? '錯題庫是空的,先去練習吧!' : (mode==='freq' ? '暫時沒有常錯題(答錯 2 次以上才會收錄)。' : '沒有可用題目')); return; } idx = 0; score = 0; answers = []; quizStart = Date.now(); document.getElementById('q-mode').textContent = mode==='wrong' ? '錯題重做' : (mode==='freq' ? '🔥 常錯題重做' : '模擬練習'); document.querySelectorAll('.screen').forEach(s=>s.classList.remove('on')); document.getElementById('sc-quiz').classList.add('on'); window.scrollTo(0,0); renderQ(); } let curLocked = false; function renderQ(){ const q = quiz[idx]; curLocked = false; document.getElementById('q-idx').textContent = '第 ' + (idx+1) + ' / ' + quiz.length + ' 題'; document.getElementById('pbar').style.width = (idx/quiz.length*100)+'%'; document.getElementById('q-text').textContent = q.q; const box = document.getElementById('q-opts'); box.innerHTML = ''; ['A','B','C','D'].forEach(L=>{ const b = document.createElement('button'); b.className = 'opt'; b.innerHTML = '' + L + '.' + esc(q.opts[['A','B','C','D'].indexOf(L)]) + ''; b.onclick = ()=>answer(L, b); box.appendChild(b); }); const ex = document.getElementById('q-expl'); ex.className = 'expl'; ex.innerHTML = ''; document.getElementById('btn-next').disabled = true; document.getElementById('btn-next').textContent = (idx === quiz.length-1) ? '查看成績 🏁' : '下一題 →'; } function answer(L, btn){ if(curLocked) return; curLocked = true; const q = quiz[idx]; const correct = (L === q.ans); const btns = document.querySelectorAll('#q-opts .opt'); btns.forEach(b=>{ b.disabled = true; }); const ansIdx = ['A','B','C','D'].indexOf(q.ans); btns[ansIdx].classList.add('correct'); if(!correct){ btn.classList.add('wrong'); } btns.forEach(b=>{ if(b!==btns[ansIdx] && b!==btn) b.classList.add('dim'); }); if(correct){ score++; clearWrongCnt(q.id); } else { const w = loadWrong(); if(!w.includes(q.id)){ w.push(q.id); saveWrong(w); } bumpWrong(q.id); } answers.push({q, pick:L, correct}); const ex = document.getElementById('q-expl'); ex.className = 'expl show ' + (correct ? 'good' : 'bad'); const expl = q.expl && q.expl.length > 3 ? q.expl : '建議重溫本章節課程內容,加深理解。'; ex.innerHTML = (correct ? '\u2705 答對了!' : '\u274C 答錯了。正確答案:' + q.ans + '
') + '
💡 ' + esc(expl) + '
' + '參考章節 ' + esc(q.ref||'—') + ''; document.getElementById('btn-next').disabled = false; } function nextQ(){ if(idx < quiz.length-1){ idx++; renderQ(); } else showResult(); } function quitQuiz(){ if(confirm('確定離開本次練習?作答紀錄將不會保存。')) goHome(); } function showResult(){ const pct = Math.round(score/quiz.length*100); document.getElementById('score-num').textContent = pct; const ring = document.getElementById('ring'); ring.style.background = 'conic-gradient(' + (pct>=PASS?'var(--navy2)':'var(--bad)') + ' ' + (pct*3.6) + 'deg, #ece7db 0deg)'; const g = document.getElementById('grade-badge'); if(pct >= EXC){ g.className='grade exc'; g.textContent='🏆 優秀'; } else if(pct >= PASS){ g.className='grade pass'; g.textContent='\u2705 合格'; } else { g.className='grade fail'; g.textContent='\u274C 不合格'; } document.getElementById('score-detail').textContent = '答對 ' + score + ' / ' + quiz.length + ' 題(合格線 ' + PASS + '%,優秀線 ' + EXC + '%)'; const byCh = {}; answers.forEach(a=>{ const ch = a.q.ch; byCh[ch] = byCh[ch]||[0,0]; byCh[ch][1]++; if(a.correct) byCh[ch][0]++; }); document.getElementById('ch-stats').innerHTML = Object.keys(byCh).sort((a,b)=>a-b).map(ch=>{ const c = byCh[ch][0], t = byCh[ch][1]; return '
第 ' + ch + ' 章:答對 ' + c + ' / ' + t + ' 題(' + Math.round(c/t*100) + '%)
'; }).join(''); const wrongs = answers.filter(a=>!a.correct); document.getElementById('wrong-review').innerHTML = wrongs.length ? wrongs.map(a=> '
答錯' + esc(a.q.q.slice(0,60)) + '…
' + '正確答案:' + a.q.ans + '. ' + esc(a.q.opts[['A','B','C','D'].indexOf(a.q.ans)].slice(0,50)) + '
' ).join('') : '
🎉 全部答對,沒有錯題!
'; if(mode === 'wrong'){ let w = loadWrong(); const corrected = answers.filter(a=>a.correct).map(a=>a.q.id); w = w.filter(id=>!corrected.includes(id)); saveWrong(w); } if(ME){ const gradeTxt = pct >= EXC ? '優秀' : (pct >= PASS ? '合格' : '不合格'); addRecord({ at: Date.now(), mode: mode === 'wrong' ? '錯題重做' : ('模擬練習 ' + quiz.length + ' 題'), total: quiz.length, correct: score, pct: pct, grade: gradeTxt, sec: Math.round((Date.now() - quizStart)/1000) }); } document.querySelectorAll('.screen').forEach(s=>s.classList.remove('on')); document.getElementById('sc-result').classList.add('on'); window.scrollTo(0,0); } /* ==================== 启动 ==================== */ (function init(){ const sid = lsGet(K_SESSION, null); if (sid){ const acc = getAccounts().find(a => a.uid === sid); if (acc) ME = acc; } renderUserBar(); if (ME){ document.querySelectorAll('.screen').forEach(s=>s.classList.remove('on')); document.getElementById('sc-home').classList.add('on'); refreshHome(); } else { document.querySelectorAll('.screen').forEach(s=>s.classList.remove('on')); document.getElementById('sc-auth').classList.add('on'); } })(); refreshHome();