香港公司秘書/公司治理專業資格考試 CGQP|《公司秘書實務與合規》模擬題庫

每位學員用自己的帳號登入,錯題本與練習記錄會分開存放,不會互相混淆。 帳號資料储存在您目前使用的這台裝置瀏覽器內。
📖 考點速覽|12 章重點蒸餾(開考前 5 分鐘過一遍)(點擊展開)
第1章 · 公司秘書角色
  • 公司秘書任命:上市發行人公司秘書須為 CS/CGP 或律師/會計師等認可人士
  • 雙重角色:公司條例下的法定職務 + 上市規則下的合規職務
  • 主要聯絡人:負責與聯交所的日常溝通
第2章 · 股東大會與決議
  • AGM 每年舉行(上市公司每財年結束後 6 個月內);EGM 按需召開
  • 通知期:AGM 至少 21 日、其他大會至少 14 日
  • 普通決議 >50%;特別決議 ≥75%(如修改章程、清盤)
第3章 · 董事會會議與公司紀錄
  • 會議通知、議程與會議紀錄的法定要求與保存
  • 董事會議紀錄一經簽署即為表面證據
  • 書面決議:私人公司全體成員合資格即可
第4章 · 法定申報與合規程序
  • 周年申報表 NAR1(42 日內交付);董事/秘書變更通知 ND2A
  • 重要控制人登記冊(SCR)與指定代表
  • 股本變動、押記登記等其他法定申報
第5章 · 上市合規與持續責任
  • 董事會會議每年至少 4 次;定期檢討內控
  • 須予公布交易的分類(股份交易/須予披露/主要交易等)與股東批准
  • 董事證券禁售期與年度股東大會前的交易限制
第6章 · 治理支援與實務
  • 為董事會提供治理與合規建議;確保決議符合章程與法規
  • 內幕消息:《證券及期貨條例》第 XIVA 部——盡快披露或保密
  • 法定紀錄保存與查冊配合
第7章 · 股份過戶與股東名冊實務
  • 股份過戶處(share registrar)與股東名冊的管理
  • 過戶文書、蓋印花的程序;股份證明書補發
  • 股份過戶須在名冊登記方可享有股東權利
第8章 · 股息與分配
  • 派息來源:可分派利潤;股份溢價帳一般不可派息
  • 中期股息(董事會宣布)vs 末期股息(股東批准)
  • 以股代息(scrip dividend)的程序
第9章 · 通函與文件披露
  • 上市規則下的通函要求與聯交所預審
  • 股東通函的內容標準:交易詳情、財務資料、責任聲明
  • 上市文件與通函的時間表管理
第10章 · 公司資訊與查冊
  • 公司註冊處電子查冊服務(e-Registry / CR eFiling)
  • 哪些文件公開查閱:章程、周年申報表、押記登記
  • 披露與私隱的平衡(住宅地址保護)
第11章 · 治理文件與授權
  • 董事授權書(POA)的簽署與限制
  • 公司印章、簽署授權的管理
  • 決議授權鏈:誰有權簽署、開戶、簽約
第12章 · 實務情景應用
  • 綜合情景:大會程序瑕疵、申報逾期、披露危機的處理
  • 公司秘書在治理事件中的應變角色

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

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

📊 各章節表現

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

📕 我的錯題本(0 題)

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

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

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

🗂 我的練習記錄

👤 我的帳號

🔑 修改密碼

const BANK = [[1,1,"1","香港上市發行人的公司秘書根據《上市規則》第3.28條,須為:","任何年滿18歲的香港居民","公會(HKCGI)會員、律師、大律師、專業會計師或聯交所認為具備同等經驗的人士","公司的最大股東","必須是執行董事","B","第3.28條列出認可資格:香港公司治理公會會員、《法律執業者條例》界定的律師或大律師、《專業會計師條例》界定的會計師,或聯交所按經驗個別評估認可者。"],[2,1,"1","香港私人公司委任公司秘書的資格要求是:","必須持有 HKCGI 資格","通常居住香港的個人即可,法律未規定專業資格","必須是執業律師","必須是審計師","B","《公司條例》第475條只要求私人公司秘書為通常居於香港的個人,或在香港有營業地點的法人團體,資格屬商業判斷。"],[3,1,"1","上市發行人的公司秘書每年須完成不少於多少小時的相關專業培訓?","5小時","15小時","40小時","100小時","B","《上市規則》第3.29條要求上市發行人的公司秘書每個財政年度完成不少於15小時相關專業培訓。"],[4,1,"1","公司秘書在公司治理中的定位最準確的是:","純粹的文書打字員","治理守護者與合規顧問,向董事會提供治理、法規及程序建議","行政總裁的下屬監督者","外部審計師的助理","B","公司秘書是治理架構的核心支援:確保會議程序合規、申報準時、董事知情,並就治理實務向董事會提出建議。"],[5,1,"1","香港公司治理公會(HKCGI)會員的兩大專業銜頭是:","特許秘書(Chartered Secretary)及公司治理師(Chartered Governance Professional)","註冊會計師及核數師","律師及大律師","董事及監事","A","完成 CGQP 或認可企業管治碩士課程可取得 HKCGI 會員資格,使用 CS 及 CGP 雙重銜頭,是上市規則認可的公司秘書資格。"],[6,1,"1","公司秘書與董事會的報告關係,最佳實踐是:","向行政總裁個人匯報治理事宜","直接向董事會及主席匯報,保障其治理職能的獨立性","向市場推廣部門匯報","無須向任何人匯報","B","公司秘書的治理職能須直接觸達董事會(尤其主席),確保治理意見不被管理層過濾,符合守則精神。"],[7,1,"1","公司秘書不得被視為董事會「決策者」,原因是:","公司秘書永遠不是公司僱員","其角色是支援治理程序與合規,決策責任由董事承擔","法例禁止公司秘書提出意見","公司秘書不識讀財務報表","B","公司秘書就程序與合規提供建議,但商業決策屬董事職權;公司秘書須清楚界定角色,避免實質上行使董事職能而未註冊。"],[8,1,"1","「影子董事」(shadow director)在公司法上的含義是:","以董事名義註冊的人","公司董事慣常依照其指示或指令行事的人,即使未獲正式委任","已辭任的董事","公司客戶代表","B","若董事習慣聽從某人的指示行事,法院可將該人視為影子董事,須承擔與正式董事相若的責任。"],[9,1,"1","公司秘書在風險管理治理中的典型參與是:","親自承擔全部業務風險","協助建立治理架構、統籌披露及確保風險匯報流程合規","代替審計委員會審查財務報表","直接決定保險購買","B","公司秘書統籌治理流程(委員會安排、紀錄、披露),支援風險治理架構運作,而非取代專業風險管理職能。"],[10,1,"1","新任上市公司公司秘書上任後的首要任務之一是:","立即重組公司架構","確認法定申報時間表(年報、年結申報、權益披露)及治理日曆無遺漏","取消所有委員會","終止與核數師的聯繫","B","公司秘書須先盤點合規時間表:年報/ESG 匯報、股東大會、公司註冊處年報(NAR1)、權益披露、委員會日曆等,確保無違規風險。"],[11,2,"2","香港私人公司書面決議(written resolution)不適用於:","罷免審計師","在董事任期屆滿前罷免董事","批准股息分配","修改公司名稱","B","《公司條例》第590條規定,罷免董事或其任期屆滿前的相關決議不得以書面決議方式通過。"],[12,2,"2","上市發行人以投票方式就一般事項通過的普通決議(ordinary resolution)所需的多數是:","全體一致","出席投票的過半數表決權","三分之二多數","75% 多數","B","普通決議須獲出席(親自或委派代表)並有權投票的股東過半數表決權支持;特別決議則須不少於75%。"],[13,2,"2","香港《公司條例》下特別決議(special resolution)的多數要求是:","過半數","不低於75%的表決權","80%","全體董事同意","B","第564條規定特別決議須獲不少於75%的表決權支持,適用於修改章程、更改名稱、撤銷註冊等重要事項。"],[14,2,"2","股份上市發行人召開股東週年大會的通知期,一般須不少於:","14天","21天","28天","7天","B","《公司條例》第571條規定公眾公司周年大會須發出不少於21天的通知,除非經特定多數同意縮短。"],[15,2,"2","股東大會法定人數(quorum)不足時,會議:","照常開會","不能有效地開始或進行,須另行召開","由主席單獨決定一切事項","自動延期一年","B","法定人數是會議有效的先決條件,章程細則通常規定出席人數底線,不足則須延期或重召。"],[16,2,"2","委派代表(proxy)出席股東大會的規則,以下正確的是:","任何股東不得委派代表","成員有權委派他人代為出席及投票,並須按章程及法例在指定時間前遞交委託書","代表必須是公司董事","代表票只在平局時計算","B","《公司條例》第596條保障成員委派代表的權利,委託書須在會議或表決前指定時間送達公司方為有效。"],[17,2,"2","上市發行人的股東大會通告須載明的內容不包括:","會議時間地點及議程","決議的確切措辭","股東委派代表的安排及權利","公司未來十年的盈利保證","D","通告須載明會議詳情、決議文本、代表安排等程序資訊;法例並不要求(也不應)作出盈利保證。"],[18,2,"2","投票表決結果公布的要求,上市發行人應:","於會後合理時間內經披露易(HKEXnews)公告表決結果","永遠保密","只通知贊成方股東","在一年後年報中提及","A","聯交所要求發行人在股東大會後按規定時限(一般當日)公布按股數表決的結果,包括贊成、反對及棄權票數。"],[19,2,"2","公司秘書在股東大會中的核心職責是:","代替主席發言","確保通知、法定人數、表決程序及紀錄符合法例與章程","代表全體股東投票","決定股息金額","B","公司秘書負責會議的籌備、程序合規(通知、法定人數、表決)、紀錄及後續申報,是股東大會程序正確性的守門人。"],[20,2,"2","股東大會紀錄(minutes)的法律意義是:","僅供內部傳閱","屬《公司條例》要求保存的紀錄,經簽署確認後為會議程序的證據","可以隨意銷毀","僅在訴訟時才編製","B","第622章要求公司備存成員大會紀錄,經主席簽署的紀錄是會議程序的表面證據,須妥善保存。"],[21,3,"3","董事會會議通知的基本要求是:","口頭通知即可,無論章程如何規定","須按章程細則規定的通知期向全體董事發出,並附議程及會議文件","只通知出席過上次會議的董事","只在報紙刊登","B","會議通知須送達全體董事並符合章程的通知期,配合議程與董事會文件(board papers)讓董事有充分準備。"],[22,3,"3","董事會會議紀錄應記錄的重點是:","逐字記錄所有討論","決定、決議、主要論點、利益申報及董事異議","與會者的私人閒談","公司未來十年商業機密","B","紀錄應聚焦決定與理由、利益申報、重要異議,作為董事履行職責的證據;逐字稿既無必要亦可能抑制坦率討論。"],[23,3,"3","董事以傳閱方式通過決議(resolution by circulation)的風險主要是:","文件太多","缺乏充分討論,董事未獲全面資訊即表決,或授權範圍被擴大使用","費用過高","須報稅務局批准","B","傳閱決議程序雖有效率優勢,但須限定適用事項(非重大或緊急),確保董事獲得完整資料及合理時間考慮。"],[24,3,"3","公司紀錄(statutory books)通常包括:","成員登記冊、董事及公司秘書登記冊、押記登記冊等","客戶私人資料庫","競爭對手定價表","員工朋友圈","A","法定登記冊包括成員、董事及公司秘書、押記、董事披露等,須按《公司條例》備存並供查閱。"],[25,3,"3","董事權益登記(register of directors' interests)的主要目的:","公開董事的個人隱私","讓公司及市場了解董事在公司股份及相關權益上的持倉,支援利益衝突管理","取代財務報表","供獵頭公司使用","B","登記董事權益有助識別潛在利益衝突,配合《證券及期貨條例》第XV部權益披露制度維持市場透明。"],[26,3,"3","《證券及期貨條例》第XV部規定,上市法團的董事須在何時披露其股份權益變動?","無須披露","於產生須披露事件後3個營業日內向聯交所及公司申報","每年一次","只在辭任時","B","董事(及主要股東)在權益性質改變或達到披露門檻後,須於3個營業日內提交權益披露表格(Form 3A 等)。"],[27,3,"3","會議紀錄保存期限方面,香港私人公司成員大會及董事會紀錄建議保存:","1年","至少10年(配合《公司條例》紀錄保存要求)","3個月","會後即銷毀","B","《公司條例》第622部要求公司備存會議紀錄等紀錄不少於10年,作為程序合規的證據。"],[28,3,"3","董事會文件(board papers)的理想發送時間是:","會前盡早(一般不少於3至7天)讓董事有時間審閱","會議開始前5分鐘","會後補發","永不發送","B","董事須在會前獲得充分資料及時間,才能有效履行謹慎與技能職責;倉促發送文件會削弱決策質素及問責基礎。"],[29,3,"3","董事缺席會議的恰當處理是:","直接從登記冊刪除其名字","記錄缺席及告假(apology),重要決議須注意缺席對法定人數的影響","由公司秘書代簽名","自動喪失董事資格","B","缺席及告假須記錄;若缺席影響法定人數或重要決議,主席與秘書須判斷會議能否有效處理該事項。"],[30,3,"3","公司秘書主持董事會議程設計時,應遵循的原則是:","議程保密至會議當天","重要策略事項優先、常規事項後排,並確保各委員會匯報及風險事項定期覆蓋","全部由行政總裁臨場決定","只列管理層喜歡的題目","B","議程應突出策略與治理事項、預留討論時間、覆蓋委員會匯報與風險議題,並提前連同文件派發。"],[31,4,"4","香港公司每年向公司註冊處提交的周年申報表是:","NAR1 表格","Form 1A","Form IR56B","Form 3A","A","私人公司須在周年大會後42天內(或會計期結束後按規定時限)提交周年申報表 NAR1,列明股本、股東、董事等資料。"],[32,4,"4","公司更改註冊地址須在更改後多少天內通知公司註冊處?","無須通知","15天","90天","3年","B","《公司條例》第658條要求公司在地址更改後15天內提交 NR1 表格通知註冊處。"],[33,4,"4","董事或公司秘書變更時,公司須提交的表格是:","ND2A/ND2B","Form IRD1","Form SEHK1","Form M&A","A","委任董事及公司秘書用 ND2A,停任用 ND2B,須在更改後15天內提交公司註冊處。"],[34,4,"4","公司押記(charge)須於創設後一個月內向公司註冊處登記,逾期後果是:","無任何影響","押記對清盤人及其他債權人無效,擔保債權淪為普通債權人","自動順延一年","須加倍繳費即可","B","未在時限內登記的押記就公司財產而言屬無效(對清盤人及債權人),這是擔保融資交易的關鍵時限風險。"],[35,4,"4","股東名冊(register of members)的重要作用是:","作為股份所有權的法定證據","純粹作為公司裝飾","代替股票交易","用於稅務稽查","A","成員登記冊是誰屬成員的表面證據(prima facie evidence),轉讓登記與否直接影響權利行使。"],[36,4,"4","上市公司的披露易(HKEXnews)系統主要用於:","發布公告及上市文件等監管資訊","員工內部溝通","客戶售後服務","會計軟件操作","A","披露易是聯交所指定的資訊披露平台,發行人的公告、通函、年報及權益披露均須經此發布。"],[37,4,"4","公司在發行新股後,公司秘書須完成的關鍵申報不包括:","提交股份配發申報(如 NB1 類表格)及更新股本資料","向聯交所披露(如適用)","更新成員登記冊及發出股票","替股東申報個人入息稅","D","股東的個人稅務由其自行處理;公司須完成股份配發申報、登記冊更新及(上市時)市場披露。"],[38,4,"4","《公司(披露董事身份資料)條例》要求公司在對外文件中披露董事的:","完整身份證號碼及地址","董事的全名及任職身分(如董事),若干文件還須載明通訊地址及身分證號碼的規定部分","家庭成員名單","薪金數額","B","公司須在信件、通知等指明文件中顯示董事全名及董事身分;公司註冊處登記亦須載有董事通訊地址及身分證件號碼(部分不公開)。"],[39,4,"4","公司違反《公司條例》備存紀錄要求,可能導致:","董事及授權人員被檢控或罰款","無任何後果","自動獲得延期","股價上升","A","備存法定登記冊及紀錄是公司及每名授權人員(含董事、秘書)的持續責任,違者可被罰款甚至檢控。"],[40,4,"4","確認公司繼續經營的「 Business Registration 」每年須向哪個部門辦理續期?","商業登記署(稅務局)","公司註冊處","證監會","聯交所","A","商業登記證由稅務局商業登記署管理,須每年續期;公司註冊處則負責公司設立及周年申報表。"],[41,5,"5","上市發行人的年度報告須在會計年度結束後多少個月內刊發?","2個月","3個月","6個月","12個月","B","《上市規則》第13.49條規定年度報告須在會計年度結束後3個月內刊發,中期報告則在2個月內。"],[42,5,"5","上市發行人刊發業績公告的時限是:","會計年度結束後1個月內","會計年度結束後3個月內(初步業績公告)","6個月內","無須公告","B","初步業績公告須於年度結束後3個月內(中期則2個月內)經披露易發布,早於完整年報的刊發時限。"],[43,5,"5","關連交易(connected transaction)申報的核心要求是:","無須理會","按交易規模及性質分級,遵守通知、公告及獨立股東批准要求","只通知大股東","一律終止交易","B","《上市規則》第14A章按交易類別(如完全豁免、部分豁免、須公告及獨立股東批准)設定分級合規義務,防止利益輸送。"],[44,5,"5","須予公布的交易(notifiable transactions)分級的依據主要是:","交易的顏色","按百分比率(如資產比率、代價比率、盈利比率)分為股份交易、須披露交易、主要交易、非常重大出售或收購","交易日期","董事人數","B","第14章以五項百分比率測試計算最高值,決定交易的分級及對應的申報、公告與股東批准要求。"],[45,5,"5","停牌與復牌的治理責任主要涉及:","公司秘書與董事會確保及時披露足以讓市場公平交易的資訊,並與聯交所溝通復牌條件","財務部的獎金分配","客戶服務熱線安排","人力資源招聘計劃","A","停牌涉及資訊披露不足、舞弊調查等情況,董事會須評估披露義務、委任顧問並跟進復牌指引,公司秘書統籌程序。"],[46,5,"5","內幕消息披露義務的主體是:","上市公司及相關高級人員","只限董事長","只限核數師","只限股東","A","《證券及期貨條例》第307條規定上市法團及其高級人員(在知情範圍內)均負有披露內幕消息的法定義務。"],[47,5,"5","收購合併守則(《公司收購、合併及股份回購守則》)由哪個機構執行?","證監會(SFC)企業融資部執行人員","公司註冊處","稅務局","銀行公會","A","《收購守則》由證監會企業融資部執行人員執行,規範要約收購的強制全面要約、資訊揭露與股東公平對待。"],[48,5,"5","發行人委任核數師須經哪個程序?","行政總裁自行決定","由審計委員會建議、董事會提名,並須經股東大會委任","公司秘書直接聘任","毋須股東批准","B","核數師由股東於周年大會委任,審計委員會就聘任、費用及辭退提出建議,保障審計獨立性。"],[49,5,"5","董事離任時,上市公司的治理程序包括:","公布離任及確認無任何有待解決的事項須提請股東關注","立即刪除全部紀錄","不予公告","由其自行宣布","B","《上市規則》第13.51條要求披露董事變動;董事離任時有權要求說明離任背景,公司須按規定公告並披露應提請股東注意的事項。"],[50,5,"5","持續上市義務的核心文件基礎包括:","《上市規則》《證券及期貨條例》《公司條例》及《收購守則》","僅公司內部守則","僅僱傭合同","僅會計政策手冊","A","上市合規是多重法規框架疊加:聯交所《上市規則》、證監會法規、《公司條例》及收購守則等,公司秘書須統籌整合。"],[51,6,"6","公司秘書協助董事會建立「治理框架」的第一步通常是:","制定股息政策","檢視章程細則、董事會職權範圍、授權結構及內部治理文件的完備性","購買辦公設備","裁減員工","B","治理框架檢視始於基礎文件:章程、職權範圍、授權矩陣、行為守則等,確保權責清晰、程序完備。"],[52,6,"6","董事就任時,公司秘書應向其提供的「就任套件」通常不包括:","《上市規則》及相關法規指引","公司章程、董事會職權範圍","董事責任保險資料","公司未來十年未經審計的盈利保證","D","就任培訓涵蓋法定責任、治理文件、業務概況及保險安排等;不應提供任何業績保證。"],[53,6,"6","公司秘書在 ESG 治理中的角色是:","親自撰寫全部環境數據","統籌 ESG 匯報流程、確保披露準則合規,並協助董事會履行 ESG 治理職責","代替審計師核證 ESG 數據","拒絕任何 ESG 披露","B","ESG 匯報已成上市規則強制範疇,公司秘書統籌跨部門數據收集、報告編製與董事會審批,並協助設定治理架構。"],[54,6,"6","董事與公司秘書之間出現合規意見分歧時,最恰當的處理是:","公司秘書立即辭職","以書面記錄專業意見,提請董事會或相關委員會討論,必要時尋求外部法律意見","直接向媒體爆料","默不作聲照辦","B","公司秘書的專業意見應有書面紀錄;分歧應循治理途徑(董事會、委員會、外部意見)解決,避免程序或法律風險。"],[55,6,"6","處理股東查詢及投訴時,公司秘書應:","一律拒絕回覆","按既定通訊政策及時、準確、平等地回覆,並注意內幕消息限制","只回覆友好股東","將查詢轉交社交媒體","B","股東溝通須遵循公平及時原則,並確保回覆不構成選擇性披露或洩露內幕消息。"],[56,6,"6","年報中「企業管治報告」的編製責任通常落在:","公司秘書(統籌)與董事會(審批)","印刷廠","客戶服務部","核數師個人","A","企業管治報告由公司秘書統籌起草、董事會審核批准,是治理文件鏈的關鍵環節;核數師僅按規定覆核部分披露。"],[57,6,"6","企業管治報告須披露董事會會議出席記錄的目的:","供市場評估董事的參與度與投入程度","作為薪酬計算的唯一依據","滿足稅務要求","無實際作用","A","披露出席率讓股東評估董事是否投入足夠時間履行職責,是問責與選舉決策的重要參考。"],[58,6,"6","公司秘書安排董事責任保險(D&O Insurance)時,須注意:","保單條款及保障範圍須向董事會匯報並定期檢討","保單金額保密","保險可取代董事誠信職責","無須任何披露","A","守則要求發行人為董事購買適當的責任保險並定期檢討;保險不會免除董事的信義職責,保障範圍亦有限制。"],[59,6,"6","公司秘書在併購交易中的職責主要是:","親自談判收購價格","確保程序合規(董事會決議、公告、股東批准)及文件完備","代替財務顧問估值","承擔交易全部盈虧","B","併購涉及複雜的公告、通函、獨立股東批准及法定程序,公司秘書統籌程序合規與文件管理,估值及談判由專業顧問負責。"],[60,6,"6","若公司秘書懷疑公司涉及內幕交易,應採取的首要步驟是:","立即私下買入公司股票","提醒相關人員嚴守內幕消息限制,將事項提請董事會/審計委員會,必要時尋求法律意見","向朋友透露消息","保持沉默並刪除紀錄","B","公司秘書須確保內幕消息管制(受限制名單、停牌考慮)落實,並循治理途徑處理懷疑違規,絕不可自行交易或洩露。"],[121,7,"7","香港上市公司股份過戶登記的法定時間限制是:","過戶文件妥備後,登記處須在規定時間內完成登記(一般10個營業日內)","無時限","一年內","即時","A","《公司條例》第632條要求公眾公司在提交轉讓文件後規定時間內完成登記或發出通知。"],[122,7,"7","股份過戶文件(轉讓契據)的必備內容包括:","轉讓人與承讓人資料、股份數目及簽名(上市股份用標準表格)","口頭描述","無格式","律師意見","A","上市股份過戶用指定格式轉讓書,並須貼足印花稅。"],[123,7,"7","股份印花稅由哪個機構徵收?","稅務局印花稅署","公司註冊處","聯交所","證監會","A","股份轉讓須繳納印花稅(現行為代價或價值0.1%×2,買賣雙方各付),未貼印花不可登記。"],[124,7,"7","股票遺失的補發程序是:","登報聲明、提供彌償保證後由登記處補發新股票","直接印新股票","報警即可","無須程序","A","遺失股票補發須登報公告及辦理賠償保證,保障舊股票持有人風險。"],[125,7,"7","「股東名冊暫停辦理過戶」(closure of register)的常用場景是:","決定股息派發對象或股東大會投票資格前","裝修辦公室","年終放假","無此制度","A","公司可暫停過戶(上市公司每次不超過30日、全年不超過40日)以確定有權股東名單。"],[126,7,"7","「記名股東」與「實益擁有人」的區別是:","登記冊上的股東才可行使法定股東權利;實益擁有人透過名義持有人持有","完全相同","實益擁有人優先","登記無意義","A","CCASS 等託管架構下,實益權益人透過中介行使權利,法定權利屬登記股東。"],[127,7,"7","公司秘書核對股東名冊時須警惕的異常是:","同一地址大量分散戶口、頻繁過戶等可能涉及操縱的跡象","股東很多","名冊太長","無異常","A","異常過戶模式可能涉及市場操縱或違規持股,須留意並必要時上報。"],[128,7,"7","「H 股公司」的股東名冊管理特點是:","須同時符合內地與香港的登記要求,名冊備存於香港過戶處","只在內地登記","無特點","用人民幣結算","A","H 股公司名冊須存於香港的股份過戶登記分處。"],[129,7,"7","「股份合併或拆細」的程序是:","股東批准(及如適用上市規則程序)、更改章程與股票換發","董事會口頭決定","無須程序","登記處決定","A","資本重組須經決議並按規定更新文件、換發股票及通知交易所。"],[130,7,"7","「無紙化證券」趨勢對過戶處的影響是:","電子登記與名冊直接更新,減少實體股票流轉","增加紙張","無影響","取消登記","A","香港推動無紙化證券市場,登記與轉讓逐步電子化。"],[131,8,"8","宣布中期股息的權限一般屬於:","董事會(如章程授權)","股東大會必須批准","公司秘書","核數師","A","中期股息通常由董事會宣布;末期股息建議須股東批准。"],[132,8,"8","末期股息宣布的程序是:","董事會建議→股東大會批准→宣派並支付","董事會直接支付","股東要求即付","公司秘書決定","A","末期股息須經股東批准後成為公司債務,再按股東名冊派發。"],[133,8,"8","派息須滿足的法定條件是:","有可分派利潤並經適當決議,且不違反資本維持原則","有現金即可","股東多數意願即可","無條件","A","第291條規範可分派利潤的計算,違反分派須返還。"],[134,8,"8","「以股代息」(scrip dividend)的機制是:","股東可選擇以新股代替現金股息,通常按參考價折讓發行","以實物派息","延遲派息","取消股息","A","以股代息節省現金並擴大股本,須符合發行程序與上市規則。"],[135,8,"8","特別股息宣布後股價調整的原因是:","除淨後每股資產減少,市場自動調整股價","無原因","監管要求","主觀預期","A","除淨日股價下調反映派息導致的淨資產變動。"],[136,8,"8","公司回購股份後通常:","可註銷或作庫存股處理(香港允許持有庫存股但權利受限)","必須立即註銷","轉售員工","轉為負債","A","香港《公司條例》允許庫存股(treasury shares),但無投票權且不獲派息。"],[137,8,"8","「股息政策」披露的最佳實踐是:","說明派息理念、派息率範圍與決策因素","不披露","只寫酌情","隨意變更","A","透明的股息政策降低不確定性,符合投資者關係最佳實踐。"],[138,8,"8","預扣非香港居民股東股息稅的責任:","香港不徵收股息預扣稅,但股東須按其所在地納稅","公司須代扣","無稅務","由登記處扣","A","香港無股息預扣稅;跨境股東的稅務按其居留地規則處理。"],[139,8,"8","「可變股息」掛鈎業績的披露要求是:","清晰說明計算基準與上限,避免誤導","口頭承諾","無須披露","任意調整","A","變動股息安排須透明披露計算方法,防範誤導投資者。"],[140,8,"8","公司秘書在派息流程中的職責是:","核對股東名冊、編製派息時間表(除淨/暫停過戶/派發日)並公告","決定派息金額","簽發支票","代替董事決議","A","秘書統籌派息程序與時間表,確保公告及時、名冊準確。"],[141,9,"9","上市公司的「通函」(circular)與「公告」的區別是:","通函是寄發股東的正式文件(附函件陳述),公告是市場資訊發布","完全相同","公告更正式","無區別","A","關連交易、供股等須寄發通函並可能附獨立財務顧問意見。"],[142,9,"9","通函的「時間表」(timetable)通常包括:","截止登記日、投票日、完成日等關鍵時點","無時表","只寫日期","由股東定","A","時間表讓股東與市場清楚流程,公司秘書須確保各時點達成。"],[143,9,"9","「獨立財務顧問」(IFA)意見須在什麼交易中提供?","關連交易或須獨立股東批准的交易(如主要交易、收購守則下的交易)","所有公告","年報","無須意見","A","須獨立股東投票的交易通常須委任 IFA 就條款公平性發表意見。"],[144,9,"9","「獨立董事委員會」的功能是:","就關連交易向獨立股東提供意見,確保利益衝突交易公平處理","批准交易","執行交易","代替核數師","A","由無利益衝突的 INED 組成,審視交易條款並建議投票方向。"],[145,9,"9","「投票代理表格」(proxy form)的必備欄位包括:","委託人/代表資料、議案、投票指示與簽署","無格式","自由撰寫","只需簽名","A","標準委託書格式須清楚反映議案與指示,方便統計。"],[146,9,"9","「迴避投票」(abstention)在關連交易中的意義是:","有利益衝突的股東不得投票,其持股不計入贊成或反對","自願放棄","計入反對","計入贊成","A","《上市規則》要求關連方迴避,議案由獨立股東過半通過。"],[147,9,"9","「投票門檻75%」的典型適用場景是:","修改章程、清洗豁免等特別決議事項","派發股息","更換辦公地址","日常採購","A","特別決議事項須75%多數,反映其對股東權利的重大影響。"],[148,9,"9","「股份計劃」(share scheme)的治理審批要求是:","獨立股東批准計劃及相關限額(如授出上限、個人上限)","董事會即可","無須批准","由薪酬委員會決定","A","《上市規則》第17章規範股份計劃的批准、限額與披露。"],[149,9,"9","「資本重組」的公告披露要點是:","重組結構、對股東權益的影響、時間表及批准狀態","只提結果","無須披露","事後通知","A","重組涉及股東權益變化,須充分披露讓股東知情決策。"],[150,9,"9","通函中的「責任聲明」由誰作出?","董事對通函內容承擔共同及個別責任","公司秘書個人","核數師","登記處","A","董事須確認通函所載資料真實無誤導,是發行文件責任的核心。"],[151,10,"10","公司註冊處「電子查冊服務」可取得的資料包括:","公司基本資料、周年申報表、章程、押記登記等文件影像","董事個人稅單","客戶名單","審計底稿","A","公眾可付費查閱法定文件,是盡職調查的基礎工具。"],[152,10,"10","董事「身分證號碼」在公開查冊中的處理是:","只顯示部分(如 XXXX123(X)),全號受保護","全部公開","完全保密","由公司決定","A","2021年起分階段實施保護董事個人資料的查冊安排。"],[153,10,"10","公司「重要控制人登記冊」(SCR)的要求是:","私人公司須備存重要控制人登記冊並供執法人員查閱","只適用上市公司","須公開上網","無此要求","A","2018年起新《公司條例》要求私人公司備存 SCR,透明化股權控制。"],[154,10,"10","「 designated representative 」(指定代表)的職責是:","就 SCR 向執法人員提供聯絡與協助","管理公司賬戶","簽發股票","決定戰略","A","公司須指定至少一名代表(通常為秘書或會計/法律專業人士)配合查冊。"],[155,10,"10","商業登記證與公司註冊證書的區別是:","前者是稅務局的營業登記,後者是法人地位證明","完全相同","前者證明法人","無區別","A","公司成立後須同時辦理商業登記(BR)方能營業。"],[156,10,"10","「公司名稱被盜用」的處理是:","向公司註冊處投訴並要求調查,必要時法律行動","無法處理","只換名字","由銀行處理","A","註冊處及警方可調查冒名行為,受害者可民事追償。"],[157,10,"10","「實益擁有權」透明度對公司秘書的工作影響是:","須維護 SCR 準確性,及時更新控制權變化","無影響","增加客戶","減少工作","A","控制權變動須於規定時間更新 SCR,否則違規。"],[158,10,"10","「破產查冊」在董事盡職調查中的用途是:","核查董事候選人是否被破產或取消資格限制","無用途","核實收入","查詢房產","A","委任董事前查冊可避免違反取消資格法例。"],[159,10,"10","「公司印章」備存與使用的內控是:","印章登記冊記錄使用情況,授權使用並雙人核對","自由使用","只由主席保管","無需登記","A","印章管控是防範未經授權簽立的基礎內控。"],[160,10,"10","「電子註冊服務」(e-Registry)的好處是:","24小時提交、快速確認、費用優惠","無好處","只限上市公司","增加費用","A","電子提交已成主流,公司秘書日常申報多透過 e-Registry 完成。"],[161,11,"11","「授權矩陣」應包含的關鍵內容是:","事項類別、不同層級的審批權限與金額門檻","員工姓名","客戶名單","無格式","A","清晰的權限層級是內控與董事會授權管理的基礎。"],[162,11,"11","「授權書」(power of attorney)的使用要點是:","明確授權範圍與期限、按公司章程正式簽立並登記備案","口頭授權即可","無須範圍","永久有效","A","授權書須具體明確,避免過度授權帶來的風險。"],[163,11,"11","「公司章程修訂」後的跟進工作是:","向公司註冊處提交章程修訂文本、更新內部文件與通知相關方","只通知主席","無須跟進","銷毀舊版","A","章程修訂須登記並同步到治理文件體系。"],[164,11,"11","「董事會職權範圍」(terms of reference)應每年:","檢討其與法規及公司需要的適切性","永不修改","只看格式","由秘書定稿","A","職權範圍須動態更新,反映法規與治理架構變化。"],[165,11,"11","「公司治理章程」(governance charter)的內容通常包括:","董事會職能、委員會架構、授權與行為準則","產品清單","客戶資料","薪金表","A","治理章程整合治理架構,是內部治理的總綱文件。"],[166,11,"11","「政策與程序手冊」定期更新的負責人通常是:","公司秘書/合規職能統籌,經管理層與董事會批准","每個員工","無人負責","外部顧問","A","政策管理須有統籌者與批准程序,確保一致性。"],[167,11,"11","「會議授權」與「決議授權」的區別是:","前者為會議程序授權,後者為具體事項的權限授予","完全相同","無區別","後者無法律效力","A","兩種授權工具性質不同,須分別管理。"],[168,11,"11","「委任狀」(letter of appointment)對獨立董事的重要性是:","明確任期、職責、時間投入與獨立性要求","無必要","僅作紀念","由秘書自定","A","委任條款清晰化是董事問責與期望管理的基礎。"],[169,11,"11","「治理文件衝突」的解決原則是:","法例優先於章程,章程優先於內部政策","內部政策優先","章程優先於法例","由秘書決定","A","文件層級清晰才能避免執行混亂。"],[170,11,"11","「電子簽署」在治理文件中的接受度是:","《電子交易條例》下多數文件可電子簽署(特定文件除外)","一律無效","須公證","只限郵件","A","電子簽署已被廣泛接受,但契據等特定文件仍有形式要求。"],[171,12,"12","股東要求查閱公司會議紀錄,公司秘書應:","按法定權利與章程界定可查閱範圍,提供合理協助","一律拒絕","全部公開","收取天價費用","A","秘書須依法平衡查閱權與商業機密保護。"],[172,12,"12","董事會在會議前一天才收到重大交易議案文件,公司秘書應:","提醒主席及管理層程序風險,建議延期或調整議程","照常通過","自行否決","銷毀文件","A","資訊不足影響決策質素,秘書須提示程序風險。"],[173,12,"12","發現公告中的財務數字錯誤,應:","立即與財務及主席確認後發澄清公告更正","刪除公告","通知好友","拖延處理","A","及時更正是披露責任的核心。"],[174,12,"12","股東大會當日法定人數不足,公司秘書應:","宣布流會並按章程安排重開或延期會議","照常表決","由主席代決","當場改章程","A","人數不足則會議無效,須依程序延期。"],[175,12,"12","獨立非執行董事突然辭任致 INED 不足法定人數,應:","盡快委任補足並按規定公告,期間避免依賴需 INED 決議的程序","無須理會","解散董事會","由 CEO 代行","A","INED 不足影響合規(如審計委員會組成),須盡快補任。"],[176,12,"12","收到媒體查詢涉及未公布的重組事項,應:","按內幕消息管制回應,不確認不否認並評估披露義務","確認細節","否認一切","私下透露","A","涉內幕消息的查詢須依披露政策處理,防止選擇性披露。"],[177,12,"12","大股東要求提前知道業績,公司秘書應:","拒絕並解釋公平披露與內幕消息限制","提前提供","口頭暗示","部分透露","A","公平披露原則禁止選擇性提供重大未公布資訊。"],[178,12,"12","章程與股東協議就轉讓限制不一致,應:","檢視兩者法律效力,建議修訂對齊,避免日後爭議","以協議為準","以章程為準","置之不理","A","不一致會造成執行爭議,應透過治理修訂對齊。"],[179,12,"12","公司秘書發現年度申報即將逾期而董事未決議,應:","立即提醒主席與相關董事,必要時提出緊急會議/書面決議","逾期算了","自行決議","偽造簽名","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_cscc'; const K_ACCOUNTS = 'sunai_accounts_cgqp_cscc'; const K_SESSION = 'sunai_session_cgqp_cscc'; function wrongKey(){ return 'sunai_wrong_cgqp_cscc::' + (ME ? ME.uid : 'guest'); } function recKey(){ return 'sunai_records_cgqp_cscc::' + (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_cscc', []); 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();