{"version":3,"file":"quiz.js","names":["_regeneratorRuntime","exports","Op","Object","prototype","hasOwn","hasOwnProperty","defineProperty","obj","key","desc","value","$Symbol","Symbol","iteratorSymbol","iterator","asyncIteratorSymbol","asyncIterator","toStringTagSymbol","toStringTag","define","enumerable","configurable","writable","err","wrap","innerFn","outerFn","self","tryLocsList","protoGenerator","Generator","generator","create","context","Context","makeInvokeMethod","tryCatch","fn","arg","type","call","ContinueSentinel","GeneratorFunction","GeneratorFunctionPrototype","IteratorPrototype","getProto","getPrototypeOf","NativeIteratorPrototype","values","Gp","defineIteratorMethods","forEach","method","_invoke","AsyncIterator","PromiseImpl","invoke","resolve","reject","record","result","_typeof","__await","then","unwrapped","error","previousPromise","callInvokeWithMethodAndArg","state","Error","doneResult","delegate","delegateResult","maybeInvokeDelegate","sent","_sent","dispatchException","abrupt","done","methodName","undefined","TypeError","info","resultName","next","nextLoc","pushTryEntry","locs","entry","tryLoc","catchLoc","finallyLoc","afterLoc","tryEntries","push","resetTryEntry","completion","reset","iterable","iteratorMethod","isNaN","length","i","displayName","isGeneratorFunction","genFun","ctor","constructor","name","mark","setPrototypeOf","__proto__","awrap","async","Promise","iter","keys","val","object","reverse","pop","skipTempReset","prev","charAt","slice","stop","rootRecord","rval","exception","handle","loc","caught","hasCatch","hasFinally","finallyEntry","complete","finish","_catch","thrown","delegateYield","asyncGeneratorStep","gen","_next","_throw","_asyncToGenerator","args","arguments","apply","ownKeys","enumerableOnly","getOwnPropertySymbols","symbols","filter","sym","getOwnPropertyDescriptor","_objectSpread","target","source","_defineProperty","getOwnPropertyDescriptors","defineProperties","_toPropertyKey","_toPrimitive","String","input","hint","prim","toPrimitive","res","Number","_Vue","Vue","createApp","ref","reactive","onMounted","onUnmounted","computed","nextTick","BASE_SCORE","setup","sectionNumber","pageMode","averageAge","averageSalary","showQuestion","resultDataAll","resultHighChart","quizeQuestions","id","groupTitle","questions","title","note","category","multiple","matrix","layout","options","label","addition","data","position","subTotal","groupData","chartOrder","subTotalMax","categoryData","ratio","matrixData","q17age","q18salary","summary","quizeAnswers","averageAgeList","index","score","averageSalaryList","allQuestions","map","g","flat","questionByGroup","_","groupBy","gp","findQuestionById","find","q","onCheckboxChange","question","item","event","reverItem","without","includes","getMatrixValue","ans","concat","refAns","Math","max","questionScores","ansScores","qNo","parseInt","Array","isArray","sum","qq","qi","reduce","accumulator","currentValue","totalScores","questionInView","group","questionInViewRange","start","end","answersInView","emptyQuestions","groupingScores","groups","categoryMapper","scoreSummary","_g","current","percent","round","future","total","min","quizeResult","getResultDataAll","_ref","_callee","dataJson","_callee$","_context","fetch","json","questionPart","openQuestion","window","dataLayer","scrollIntoView","behavior","prevStep","scrollTo","top","nextStep","finalStep","showResult","_ref2","_callee2","categoryId","groupScores","_callee2$","_context2","console","debug","c","orderBy","chartData","handleScores","floor","drawHighChart","resultImgFixed","r","y","series","remove","addSeries","color","lineColor","lineWidth","scale","fillColor","marker","enabled","radius","pointPlacement","redraw","labelOffset","chartOptions","chart","polar","height","width","spacingTop","spacingLeft","spacingRighteft","spacingBottom","events","load","render","ticks","xAxis","offset","alignValue","labelWidth","getBBox","labelHeight","attr","translate","credits","exporting","text","tooltip","legend","categories","tickmarkPlacement","gridLineWidth","labels","align","distance","padding","x","style","fontSize","yAxis","gridLineInterpolation","startOnTick","tickInterval","showLastLabel","gridLineColor","quizeChart","Highcharts","quizeChartOptions","createDLImageKonva","_ref3","_callee4","canvas","ctx","TEXT_TEXT","initialMeasure","initialWidth","scaleRatio","isFontLoaded","textBaseStyle","loadImage","whenFontIsLoaded","stage","layer","backgroundShape","baseTemplate","generateResultChartBlock","generateScoreProgressBlock","resultChartBlock","ageAvgBlock","salaryAvgBlock","ageAvgBlockClientRect","salaryAvgBlockClientRect","url","_callee4$","_context4","_whenFontIsLoaded","callback","attemptCount","metrics","measureText","setTimeout","document","createElement","getContext","font","lineHeight","fontFamily","weight","imagePath","Konva","Image","fromURL","image","Stage","container","Layer","Rect","fill","add","zIndex","_ref4","_callee3","resultChartImg","gContent","resultScore","gContentClientRect","_callee3$","_context3","Group","getChartImage","Text","letterSpacing","getClientRect","_x","avgValue","progressGroup","titleGroup","getPosByValue","barGroup","base","cornerRadius","avgCircle","Circle","stroke","strokeWidth","shadowEnabled","shadowColor","shadowBlur","shadowOffset","shadowOpacity","circle","scoresTextStyle","avgText","scoreText","progressGroupClientRect","titleGroupClientRect","draw","toDataURL","chartSVG","querySelector","cloneNode","svgData","XMLSerializer","serializeToString","svgDataBase64","btoa","unescape","encodeURIComponent","svgDataUrl","resultBlock","downloadResult","_ref5","_callee5","imgUrl","link","filename","_callee5$","_context5","download","href","click","shareUrl","releasePath","baseUrl","urlForShare","copyUrl","navigator","clipboard","writeText","location","origin","pathname","mm","gsap","matchMedia","element","getElementById","windowHeight","innerHeight","offsetHeight","ScrollTrigger","trigger","pin","killScrollTrigger","triggers","getAll","kill","clearMatchMedia","_callee6","_callee6$","_context6","mount"],"sources":["quiz.js"],"sourcesContent":["const { createApp, ref, reactive, onMounted, onUnmounted, computed, nextTick } = Vue;\n\nconst BASE_SCORE = 2.2;\n\ncreateApp({\n setup() {\n const sectionNumber = ref(1);\n const pageMode = ref('');\n const averageAge = ref(0);\n const averageSalary = ref(0);\n const showQuestion = ref(false);\n const resultDataAll = ref(null);\n const resultHighChart = ref(null);\n\n /** Quize v2.0 */\n const quizeQuestions = reactive([\n {\n id: 1,\n groupTitle: 'Step 1',\n questions: [\n {\n id: 1,\n title: '你的年齡?',\n note: null,\n category: 0,\n multiple: false,\n matrix: null,\n layout: 'fixed',\n options: [\n { id: 1, label: '20歲以下', value: 0 },\n { id: 2, label: '20-24歲', value: 0 },\n { id: 3, label: '25-29歲', value: 0 },\n { id: 4, label: '30-34歲', value: 0 },\n { id: 5, label: '35-39歲', value: 0 },\n { id: 6, label: '40-44歲', value: 0 },\n { id: 7, label: '45-49歲', value: 0 },\n { id: 8, label: '50-54歲', value: 0 },\n { id: 9, label: '55-59歲', value: 0 },\n { id: 10, label: '60-64歲', value: 0 },\n { id: 11, label: '65-69歲', value: 0 },\n { id: 12, label: '70歲以上', value: 0 },\n ],\n },\n {\n id: 2,\n title: '你的年薪收入級距?',\n note: '年薪收入:包含正職、兼職收入,但不包含投資利得,單位:新台幣',\n category: 0,\n multiple: false,\n matrix: null,\n layout: 'fixed',\n options: [\n { id: 1, label: '40萬(含)以下', value: 0 },\n { id: 2, label: '41-60萬', value: 0 },\n { id: 3, label: '61-80萬', value: 0 },\n { id: 4, label: '81-100萬', value: 0 },\n { id: 5, label: '101-150萬', value: 0 },\n { id: 6, label: '151-200萬', value: 0 },\n { id: 7, label: '201-300萬', value: 0 },\n { id: 8, label: '301-500萬', value: 0 },\n { id: 9, label: '501萬-1000萬', value: 0 },\n { id: 10, label: '1001萬以上', value: 0 },\n ],\n },\n {\n id: 3,\n title: '你的婚姻狀態?',\n note: null,\n category: 0,\n multiple: false,\n matrix: null,\n layout: 'fixed',\n options: [\n { id: 1, label: '未婚', value: 0 },\n { id: 2, label: '已婚', value: 0 },\n { id: 3, label: '離婚', value: 0 },\n { id: 4, label: '喪偶', value: 0 },\n ],\n },\n {\n id: 4,\n title: '你撫養的子女數?',\n note: null,\n category: 0,\n multiple: false,\n matrix: null,\n layout: 'fixed',\n options: [\n { id: 1, label: '無子女', value: 0 },\n { id: 2, label: '一個', value: 0 },\n { id: 3, label: '二個', value: 0 },\n { id: 4, label: '三個', value: 0 },\n { id: 5, label: '四個(含以上)', value: 0 },\n ],\n },\n {\n id: 5,\n title: '哪一類型最接近你的「每月收入花費方式」?',\n note: '支出:包含必要支出、非必要支出、貸款金額',\n category: 1,\n multiple: false,\n matrix: null,\n layout: 'equal',\n options: [\n {\n id: 1,\n label: '支出 ≥ 收入,無法進行任何儲蓄或投資',\n value: 0,\n addition: {\n type: 'seesaw',\n value: 100,\n data: [\n {\n position: 0,\n label: '100% 支出',\n },\n { position: 100, label: '0% 儲蓄及投資' },\n ],\n },\n },\n {\n id: 2,\n label: '90%支出:10%儲蓄及投資',\n value: 1.36,\n addition: {\n type: 'seesaw',\n value: 90,\n data: [\n {\n position: 0,\n label: '90% 支出',\n },\n { position: 100, label: '10% 儲蓄及投資' },\n ],\n },\n },\n {\n id: 3,\n label: '80%支出:20%儲蓄及投資',\n value: 2.72,\n addition: {\n type: 'seesaw',\n value: 80,\n data: [\n {\n position: 0,\n label: '80% 支出',\n },\n { position: 100, label: '20% 儲蓄及投資' },\n ],\n },\n },\n {\n id: 4,\n label: '60%支出:40%儲蓄及投資',\n value: 4.08,\n addition: {\n type: 'seesaw',\n value: 60,\n data: [\n {\n position: 0,\n label: '60% 支出',\n },\n { position: 100, label: '40% 儲蓄及投資' },\n ],\n },\n },\n {\n id: 5,\n label: '50%支出:50%儲蓄及投資',\n value: 5.44,\n addition: {\n type: 'seesaw',\n value: 50,\n data: [\n {\n position: 0,\n label: '50% 支出',\n },\n { position: 100, label: '50% 儲蓄及投資' },\n ],\n },\n },\n {\n id: 6,\n label: '30%支出:50%儲蓄及投資',\n value: 6.8,\n addition: {\n type: 'seesaw',\n value: 30,\n data: [\n {\n position: 0,\n label: '30% 支出',\n },\n { position: 100, label: '70% 儲蓄及投資' },\n ],\n },\n },\n ],\n },\n ],\n },\n {\n id: 2,\n groupTitle: 'Step 2',\n questions: [\n {\n id: 6,\n title: '你平均每個月「休閒娛樂支出」佔收入佔比為?',\n note: '休閒娛樂支出:包含高級餐廳用餐、購買科技產品及精品、旅遊或醫美',\n category: 1,\n multiple: false,\n matrix: null,\n layout: 'full',\n options: [\n { id: 1, label: '休閒娛樂支出占總收入10%以下', value: 6.8 },\n { id: 2, label: '休閒娛樂支出占總收入10%至25%', value: 5.1 },\n { id: 3, label: '休閒娛樂支出占總收入25%至40%', value: 3.4 },\n { id: 4, label: '休閒娛樂支出占總收入40%至60%', value: 1.7 },\n { id: 5, label: '休閒娛樂支出占總收入60%以上', value: 0 },\n ],\n },\n {\n id: 7,\n title: '你「負擔生活費用狀況」為何?',\n note: null,\n category: 1,\n multiple: false,\n matrix: null,\n layout: 'full',\n options: [\n { id: 1, label: '非常辛苦,生活非常吃力且困難', value: 0 },\n { id: 2, label: '有點辛苦,支付生活基本支出有些許困難', value: 1.7 },\n { id: 3, label: '算不上辛苦但不到容易,生活支出打平', value: 3.4 },\n { id: 4, label: '還算容易,除生活基本支出外有多餘的錢做其他規劃', value: 5.1 },\n { id: 5, label: '非常容易,可以完全按照自己的想法享受生活', value: 6.8 },\n ],\n },\n {\n id: 8,\n title: '你目前的「債務償還狀況」最符合下列哪個描述?',\n note: null,\n category: 1,\n multiple: false,\n matrix: null,\n layout: 'full',\n options: [\n { id: 1, label: '債務不易償還,時常需籌錢償還債', value: 0 },\n { id: 2, label: '債務較多,為償還債務需減少日常支出', value: 1.7 },\n { id: 3, label: '債務不少,但可在不影響生活支出下償還債務', value: 3.4 },\n { id: 4, label: '債務不多,可輕鬆償還債務並兼顧生活品質', value: 5.1 },\n { id: 5, label: '無負債', value: 6.8 },\n ],\n },\n {\n id: 9,\n title: '你的帳單、信用卡繳款情形最符合哪個描述?',\n note: null,\n category: 1,\n multiple: false,\n matrix: null,\n layout: 'full',\n options: [\n { id: 1, label: '經常帳單欠繳,或是信用卡經常只繳最低應繳門檻', value: 0 },\n { id: 2, label: '有時帳單欠繳,或是信用卡有時只繳最低應繳門檻', value: 0.85 },\n { id: 3, label: '偶爾逾時繳交帳單,或是信用卡偶爾只繳部分金額', value: 1.7 },\n { id: 4, label: '偶爾逾時繳交帳單,但信用卡帳單皆全額準時繳交', value: 3.4 },\n { id: 5, label: '任何帳單皆全額準時繳交', value: 6.8 },\n ],\n },\n {\n id: 10,\n title: '以下何種情境最符合你的「休閒及社交生活狀態」?',\n note: null,\n category: 2,\n multiple: false,\n matrix: null,\n layout: 'full',\n options: [\n { id: 1, label: '非常滿意 - 能自由安排休閒及社交生活,並培養自己的興趣', value: 6.8 },\n { id: 2, label: '滿意 - 在一定的花費內,對於休閒時間都可透過事前規劃,盡可能地滿足自己需求', value: 5.1 },\n { id: 3, label: '普通 - 雖然休息時間不多,但偶爾有時間仍可犒賞自己,成為生活中的小確幸', value: 3.4 },\n { id: 4, label: '不滿意 - 受限於經濟或時間,少有休閒娛樂或社交,對目前生活感到不滿意', value: 1.7 },\n { id: 5, label: '非常不滿意 - 沒有餘力規畫休閒生活', value: 0 },\n ],\n },\n ],\n },\n {\n id: 3,\n groupTitle: 'Step 3',\n questions: [\n {\n id: 11,\n title: '以下陳述何者最能描述你的「儲蓄習慣」?',\n note: null,\n category: 3,\n multiple: false,\n matrix: null,\n layout: 'full',\n options: [\n { id: 1, label: '不儲蓄,通常花的⽐收⼊多', value: 0 },\n { id: 2, label: '⽉底剩下的就存起來,沒有固定計畫', value: 1.5 },\n { id: 3, label: '會盡量控制花費,但偶爾會超出預想', value: 3 },\n { id: 4, label: '僅花固定收⼊,儲蓄其他收⼊', value: 4.5 },\n { id: 5, label: '有定期儲蓄習慣與計畫', value: 6 },\n ],\n },\n {\n id: 12,\n title: '若收入突然中斷,你的儲蓄大概可維持你的基本生活多長的時間?',\n note: null,\n category: 3,\n multiple: false,\n matrix: null,\n layout: 'full',\n options: [\n { id: 1, label: '完全沒錢,須立刻有收入', value: 0 },\n { id: 2, label: '1個⽉以內', value: 0.75 },\n { id: 3, label: '1-3個⽉', value: 1.5 },\n { id: 4, label: '4-6個⽉', value: 3.0 },\n { id: 5, label: '6個⽉以上', value: 6 },\n ],\n },\n {\n id: 13,\n title: '假設你需要在一週內籌10萬,哪項陳述與情境最能描述你「支付這筆費用的能力」?',\n note: null,\n category: 3,\n multiple: false,\n matrix: null,\n layout: 'full',\n options: [\n { id: 1, label: '我可能籌不到任何錢', value: 0 },\n { id: 2, label: '我會選擇向第三方尋求協助 (例:父母親友、銀行)', value: 0.75 },\n { id: 3, label: '我會選擇減少支出、預支薪水、增加工作量等方式籌錢', value: 1.5 },\n { id: 4, label: '我自己有儲蓄可以支應一半費用 (5萬)', value: 3.0 },\n { id: 5, label: '我自己有儲蓄可以支應全部費用 (10萬)', value: 6 },\n ],\n },\n {\n id: 14,\n title: '你自我評估覺得你目前的保險保障足不足夠?',\n note: null,\n category: 3,\n multiple: false,\n matrix: null,\n layout: 'full',\n options: [\n { id: 1, label: '完全不足', value: 0 },\n { id: 2, label: '部分不足', value: 1.5 },\n { id: 3, label: '普通', value: 3 },\n { id: 4, label: '應該足夠', value: 4.5 },\n { id: 5, label: '完全足夠', value: 6 },\n ],\n },\n {\n id: 15,\n title: '若新冠疫情或全球金融波動等類似的大事件再次來臨,你已經做好相應準備而不致於發生財務困難?',\n note: null,\n category: 3,\n multiple: false,\n matrix: null,\n layout: 'full',\n options: [\n { id: 1, label: '完全不同意', value: 0 },\n { id: 2, label: '不同意', value: 1.5 },\n { id: 3, label: '中立', value: 3 },\n { id: 4, label: '同意', value: 4.5 },\n { id: 5, label: '完全同意', value: 6 },\n ],\n },\n ],\n },\n {\n id: 4,\n groupTitle: 'Step 4',\n questions: [\n {\n id: 16,\n title: '你「目前」財務規劃的表現為何?',\n note: null,\n category: 4,\n multiple: false,\n matrix: null,\n layout: 'full',\n options: [\n { id: 1, label: '對財務規劃及目標完全沒有想法,支出隨自己高興', value: 0 },\n { id: 2, label: '沒有財務目標,但有將部分收入規劃投資、儲蓄等用途', value: 1.5 },\n { id: 3, label: '雖有財務規劃,但離目標還有些差距', value: 3 },\n { id: 4, label: '為達財務目標,有時需要檢視並調整收支', value: 4.5 },\n { id: 5, label: '所有財務目標都依照自己的規劃持續進行', value: 6 },\n ],\n },\n {\n id: 17,\n title: '你自覺還需要多少時間才能退休?',\n note: null,\n category: 4,\n multiple: false,\n matrix: 'age',\n layout: 'full',\n options: [\n { id: 1, label: '完全沒有把握,也許要工作一輩子', value: 1 },\n { id: 2, label: '30年也許有機會達成,目前剛剛起步中', value: 2 },\n { id: 3, label: '20年左右達成,且已經開始逐步規劃', value: 3 },\n { id: 4, label: '10年有信心已經開始往目標執行', value: 4 },\n { id: 5, label: '5年內目標達成的機率很高,有非常完整的計劃', value: 5 },\n { id: 6, label: '已退休', value: 6 },\n ],\n },\n {\n id: 18,\n title: '你認為自己要存多少錢才能安心退休?',\n note: null,\n category: 4,\n multiple: false,\n matrix: 'salary',\n layout: 'full',\n options: [\n { id: 1, label: '400萬以下', value: 1 },\n { id: 2, label: '400萬~800萬', value: 2 },\n { id: 3, label: '800萬~1500萬', value: 3 },\n { id: 4, label: '1500萬~2000萬', value: 4 },\n { id: 5, label: '2000萬以上', value: 5 },\n { id: 6, label: '完全沒概念', value: 6 },\n ],\n },\n {\n id: 19,\n title: '請描述你對「金融及投資理財知識」與「投資意願」?',\n note: null,\n category: 5,\n multiple: false,\n matrix: null,\n layout: 'full',\n options: [\n { id: 1, label: '只有甚少或並無認識,且也不打算投資', value: 0 },\n { id: 2, label: '只有甚少或並無認識,但經常投資', value: 0 },\n { id: 3, label: '有稍微的了解,明白其中的優點及風險,但較少投資', value: 1.375 },\n { id: 4, label: '有稍微的了解,明白其中的優點及風險,且平時有投資', value: 2.75 },\n { id: 5, label: '熟悉並且有充足知識,但平時較少投資', value: 4.125 },\n { id: 6, label: '熟悉並且有充足知識,且平時經常投資', value: 5.5 },\n ],\n },\n {\n id: 20,\n title: '針對以下敘述,請選出符合你實際交易行為的選項?(可複選)',\n note: null,\n category: 5,\n multiple: true,\n subTotal: 5.5,\n matrix: null,\n layout: 'full',\n options: [\n { id: 1, label: '觸及停損點也無須理會,只要沒賣出投資標的就不算賠錢', value: -2.75 },\n { id: 2, label: '投資標的漲我就追,投資標的跌我就賣', value: -2.75 },\n { id: 3, label: '對於新聞報導與小道消息皆不太過濾,都列為重要參考資訊', value: -2.75 },\n { id: 4, label: '我通常自己判斷何時該買或賣,不需建立紀律投資', value: -2.75 },\n { id: 5, label: '以上皆非', value: 0 },\n ],\n },\n {\n id: 21,\n title: '請問你認為自己屬於以下哪一種類型的投資者?',\n note: null,\n category: 5,\n multiple: false,\n matrix: null,\n layout: 'full',\n options: [\n { id: 1, label: '風平浪靜型-追求資本保障,沒有賠就是賺', value: 0 },\n { id: 2, label: '滴水穿石型-以穩定的收益為主,每個月都穩穩賺一點最好', value: 0 },\n { id: 3, label: '細水長流型-穩定收益與資本增值,每個月穩穩領息的同時也求資產增值', value: 0 },\n { id: 4, label: '推波助瀾型-追求較高的資本增值,對每月領息或小漲小跌比較無感', value: 0 },\n { id: 5, label: '股海翻騰型-追求顯著的資本利得,想透過投資市場大賺一筆', value: 0 },\n ],\n },\n ],\n },\n ]);\n\n /** 測驗題組 */\n const groupData = [\n {\n id: 0,\n title: '基本資料',\n chartOrder: null,\n subTotalMax: 0,\n },\n {\n id: 1,\n title: '日常生活收支',\n chartOrder: 1,\n subTotalMax: 34,\n },\n {\n id: 2,\n title: '心理富足',\n chartOrder: 5,\n subTotalMax: 6.8,\n },\n {\n id: 3,\n title: '金融風險 抵抗力',\n chartOrder: 2,\n subTotalMax: 30,\n },\n {\n id: 4,\n title: '財務規劃',\n chartOrder: 3,\n subTotalMax: 18,\n },\n {\n id: 5,\n title: '財務信心',\n chartOrder: 4,\n subTotalMax: 11,\n },\n ];\n\n /** 測驗資料:結果類型 */\n const categoryData = [\n {\n id: 1,\n title: '財務資優生',\n ratio: 17,\n },\n {\n id: 2,\n title: '自律實踐者',\n ratio: 10,\n },\n {\n id: 3,\n title: '浪漫享樂者',\n ratio: 3,\n },\n {\n id: 4,\n title: '穩健計畫通',\n ratio: 10,\n },\n {\n id: 5,\n title: '理財潛力股',\n ratio: 19,\n },\n {\n id: 6,\n title: '隨興生活家',\n ratio: 11,\n },\n {\n id: 7,\n title: '熱血夢想家',\n ratio: 2,\n },\n {\n id: 8,\n title: '理財練習生',\n ratio: 11,\n },\n {\n id: 9,\n title: '財務躺平族',\n ratio: 17,\n },\n ];\n\n /** 測驗資料:分數矩陣 */\n const matrixData = {\n q17age: {\n ref: 1,\n data: [\n [0.0, 3.0, 6.0, 6.0, 6.0, 6.0],\n [0.0, 3.0, 6.0, 6.0, 6.0, 6.0],\n [0.0, 3.0, 5.0, 6.0, 6.0, 6.0],\n [0.0, 3.0, 5.0, 6.0, 6.0, 6.0],\n [0.0, 3.0, 4.0, 5.0, 6.0, 6.0],\n [0.0, 3.0, 4.0, 5.0, 6.0, 6.0],\n [0.0, 1.5, 3.0, 4.0, 5.0, 6.0],\n [0.0, 0.0, 3.0, 4.0, 5.0, 6.0],\n [0.0, 0.0, 1.5, 3.0, 4.5, 6.0],\n [0.0, 0.0, 0.0, 1.5, 3.0, 6.0],\n [0.0, 0.0, 0.0, 0.0, 3.0, 6.0],\n [0.0, 0.0, 0.0, 0.0, 3.0, 6.0],\n ],\n },\n q18salary: {\n ref: 2,\n data: [\n [3.6, 2.4, 1.25, 0.82, 0.72, 0.0],\n [4.5, 3.0, 1.57, 1.03, 0.9, 0.0],\n [6.0, 4.2, 2.19, 1.44, 1.26, 0.0],\n [6.0, 5.4, 2.82, 1.85, 1.62, 0.0],\n [6.0, 6.0, 3.91, 2.57, 2.25, 0.0],\n [6.0, 6.0, 5.48, 3.6, 3.15, 0.0],\n [6.0, 6.0, 6.0, 5.14, 4.5, 0.0],\n [6.0, 6.0, 6.0, 6.0, 6.0, 0.0],\n [6.0, 6.0, 6.0, 6.0, 6.0, 0.0],\n [6.0, 6.0, 6.0, 6.0, 6.0, 0.0],\n ],\n },\n summary: {\n data: [\n [1, 2, 3],\n [4, 5, 6],\n [7, 8, 9],\n ],\n },\n };\n\n /** 答題答案 */\n const quizeAnswers = reactive({\n 1: null,\n 2: null,\n 3: null,\n 4: null,\n 5: null,\n 6: null,\n 7: null,\n 8: null,\n 9: null,\n 10: null,\n 11: null,\n 12: null,\n 13: null,\n 14: null,\n 15: null,\n 16: null,\n 17: null,\n 18: null,\n 19: null,\n 20: [],\n 21: null,\n });\n\n /** 同年齡者的平均分數 */\n const averageAgeList = [\n { index: 1, score: 52.7 }, // 20歲以下\n { index: 2, score: 54.0 }, // 20-24歲\n { index: 3, score: 56.8 }, // 25-29歲\n { index: 4, score: 57.1 }, // 30-34歲\n { index: 5, score: 57.6 }, // 35-39歲\n { index: 6, score: 58.5 }, // 40-44歲\n { index: 7, score: 61.1 }, // 45-49歲\n { index: 8, score: 63.3 }, // 50-54歲\n { index: 9, score: 65.3 }, // 55-59歲\n { index: 10, score: 65.9 }, // 60-64歲\n { index: 11, score: 68.0 }, // 65-69歲\n { index: 12, score: 70.7 }, // 70歲以上\n ];\n\n /** 近似年收入者的平均分數 */\n const averageSalaryList = [\n { index: 1, score: 51.7 }, // 40萬(含)以下\n { index: 2, score: 56.8 }, // 41-60萬\n { index: 3, score: 60.5 }, // 61-80萬\n { index: 4, score: 62.8 }, // 81-100萬\n { index: 5, score: 66.3 }, // 101-150萬\n { index: 6, score: 69.1 }, // 151-200萬\n { index: 7, score: 72.9 }, // 201-300萬\n { index: 8, score: 75.6 }, // 301-500萬\n { index: 9, score: 78.2 }, // 501萬-1000萬\n { index: 10, score: 73.4 }, // 1001萬以上\n ];\n\n /** 題目清單 */\n const allQuestions = computed(() => {\n return quizeQuestions.map(g => g.questions).flat();\n });\n\n /** 題目分組清單 */\n const questionByGroup = computed(() => {\n const g = _.groupBy(allQuestions.value, 'category');\n return groupData.map(gp => ({\n ...gp,\n questions: g[gp.id],\n }));\n });\n\n /** 以題目編號取得題目內容 */\n const findQuestionById = id => {\n return allQuestions.value.find(q => q.id === id);\n };\n\n /** Checkbox 改變時 */\n const onCheckboxChange = (question, item, event) => {\n //in here you can check what ever condition before append to array.\n if (item.value === 0) {\n quizeAnswers[question.id] = [item.id];\n } else {\n // 以上皆非/以上皆是為單一反向選擇\n const reverItem = findQuestionById(question.id).options.find(i => i.value === 0);\n quizeAnswers[question.id] = _.without(quizeAnswers[question.id], reverItem.id);\n\n if (quizeAnswers[question.id].includes(item.id)) {\n quizeAnswers[question.id] = _.without(quizeAnswers[question.id], item.id);\n } else {\n quizeAnswers[question.id].push(item.id);\n }\n }\n };\n\n /** 取得矩陣對照值 */\n const getMatrixValue = (question, ans) => {\n const matrix = matrixData[`q${question.id}${question.matrix}`];\n const refAns = quizeAnswers[matrix.ref]; // 矩陣參考\n if (!refAns || !ans) {\n return 0;\n }\n return matrix.data[Math.max(refAns - 1, 0)][ans - 1];\n };\n\n /** 答題分數小計 */\n const questionScores = computed(() => {\n const ansScores = {};\n\n Object.keys(quizeAnswers).forEach(qNo => {\n const question = findQuestionById(parseInt(qNo));\n\n if (Array.isArray(quizeAnswers[qNo])) {\n const sum = quizeAnswers[qNo]\n .map(qq => {\n const qi = question.options.find(i => i.id === qq);\n return qi ? qi.value : 0;\n })\n .reduce((accumulator, currentValue) => accumulator + currentValue, 0);\n ansScores[qNo] = Math.max(question.subTotal + sum, 0);\n\n return;\n }\n\n if (question.matrix && matrixData[`q${question.id}${question.matrix}`]) {\n ansScores[qNo] = getMatrixValue(question, quizeAnswers[qNo]);\n\n return;\n }\n\n const qi = question.options.find(i => i.id === quizeAnswers[qNo]);\n ansScores[qNo] = qi ? qi.value : 0;\n });\n\n return ansScores;\n });\n\n /** 總分 */\n const totalScores = computed(() => {\n return Object.values(questionScores.value).reduce((accumulator, currentValue) => accumulator + currentValue, 0);\n });\n\n /** 當前分頁題目清單 */\n const questionInView = computed(() => {\n const group = quizeQuestions.find(g => g.id === sectionNumber.value);\n return group.questions;\n });\n\n /** 當前分頁題目編號範圍 */\n const questionInViewRange = computed(() => {\n return {\n start: questionInView.value[0].id,\n end: questionInView.value[questionInView.value.length - 1].id,\n };\n });\n\n /** 當前分頁答題狀態 */\n const answersInView = computed(() => {\n return questionInView.value.map(question => quizeAnswers[question.id]);\n });\n\n /** 當前分頁未答題數 */\n const emptyQuestions = computed(() => {\n return answersInView.value.filter(ans => ans === null).length;\n });\n\n /** 分組題目計分 */\n const groupingScores = computed(() => {\n const groups = {};\n questionByGroup.value\n .filter(group => group.chartOrder)\n .forEach(group => {\n groups[group.id] = group.questions.map(question => questionScores.value[question.id]);\n });\n return groups;\n });\n\n /** 九大類型現況、未來區間 */\n const categoryMapper = (type, value) => {\n if (type === 'current') {\n if (value >= 50.1) {\n return 0;\n } else if (value <= 38.5) {\n return 2;\n }\n return 1;\n }\n\n if (type === 'future') {\n if (value >= 15.8) {\n return 0;\n } else if (value <= 10.3) {\n return 2;\n }\n return 1;\n }\n };\n\n /** 分數總結 */\n const scoreSummary = computed(() => {\n const groups = {};\n Object.keys(groupingScores.value).forEach(group => {\n const g = questionByGroup.value.find(_g => _g.id === parseInt(group));\n const val = groupingScores.value[group].reduce((prev, current) => prev + current, 0);\n groups[group] = {\n value: val,\n percent: Math.round((val / g.subTotalMax) * 10000) / 100,\n };\n });\n\n const category = {\n current: {\n value: groups['1'].value + groups['2'].value + groups['3'].value,\n position: categoryMapper('current', groups['1'].value + groups['2'].value + groups['3'].value),\n },\n future: {\n value: groups['4'].value + groups['5'].value,\n position: categoryMapper('future', groups['4'].value + groups['5'].value),\n },\n };\n\n return {\n groups,\n category,\n total: Math.min(\n Math.max(\n Math.round(\n (Object.values(groupingScores.value)\n .flat()\n .reduce((prev, current) => prev + current, 0) +\n BASE_SCORE) *\n 1000,\n ) / 1000,\n 0,\n ),\n 100,\n ),\n };\n });\n\n const quizeResult = ref(null);\n\n const getResultDataAll = async () => {\n const data = await fetch('./utils/result.json');\n const dataJson = await data.json();\n resultDataAll.value = dataJson;\n };\n\n const result = ref({});\n const questionPart = ref();\n\n const openQuestion = () => {\n showQuestion.value = true;\n // gtm\n if (window.dataLayer) {\n window.dataLayer.push({\n event: 'fh_index_test_content_cta',\n });\n }\n nextTick(() => {\n const questions = questionPart.value;\n if (questions) {\n questions.scrollIntoView({ behavior: 'smooth' });\n }\n });\n };\n\n const prevStep = () => {\n sectionNumber.value--;\n\n nextTick(() => {\n window.scrollTo({\n top: 0,\n behavior: 'smooth',\n });\n });\n };\n\n const nextStep = () => {\n if (emptyQuestions.value) return;\n\n sectionNumber.value++;\n\n nextTick(() => {\n window.scrollTo({\n top: 0,\n behavior: 'smooth',\n });\n });\n // showErrortText.value = true;\n };\n\n const finalStep = () => {\n sectionNumber.value = quizeQuestions.length;\n showQuestion.value = true;\n\n showResult();\n\n nextTick(() => {\n window.scrollTo({\n top: 0,\n behavior: 'smooth',\n });\n });\n };\n\n const showResult = async () => {\n window.scrollTo({\n top: 0,\n behavior: 'instant',\n });\n console.debug('show result');\n\n showQuestion.value = false;\n pageMode.value = 'result';\n\n /** 準備資料 */\n // 總覽\n const categoryId =\n matrixData.summary.data[scoreSummary.value.category.current.position][\n scoreSummary.value.category.future.position\n ];\n const category = categoryData.find(c => c.id === categoryId);\n\n // 圖表用\n const groupScores = _.orderBy(\n questionByGroup.value.filter(group => group.chartOrder),\n ['chartOrder'],\n ).map(group => ({\n id: group.id,\n title: group.title,\n ...scoreSummary.value.groups[group.id],\n }));\n\n // 同年齡者的平均分數\n averageAge.value = averageAgeList.find(item => item.index === quizeAnswers['1']);\n averageSalary.value = averageSalaryList.find(item => item.index === quizeAnswers['2']);\n\n quizeResult.value = {\n category,\n chartData: groupScores.map(g => ({\n label: g.title,\n value: g.percent,\n })),\n totalScores: scoreSummary.value.total,\n handleScores: Math.floor(new Number(scoreSummary.value.total) * 10) / 10,\n };\n\n // 結果分析&建議方向\n if (resultDataAll.value && categoryId) {\n result.value = resultDataAll.value.find(item => item.id === categoryId);\n // gtm\n if (window.dataLayer) {\n window.dataLayer.push({\n event: 'financial-health-quiz',\n category: `result-${categoryId}`,\n });\n }\n }\n\n nextTick(() => {\n drawHighChart(quizeResult.value.chartData);\n resultImgFixed();\n // drawChart(quizeResult.value.chartData);\n\n // setTimeout(() => {\n // // createDLImage();\n // createDLImageKonva();\n // }, 1000);\n });\n };\n\n const drawHighChart = data => {\n const chartData = data.map(i => ({\n name: i.label,\n r: i.label,\n y: i.value * 0.5 + 50,\n value: i.value * 0.5 + 50,\n }));\n\n if (resultHighChart.value) {\n resultHighChart.value.series[0].remove();\n\n resultHighChart.value.addSeries({\n type: 'area',\n data: chartData,\n name: 'Scores',\n color: 'rgba(0, 144, 67, 0.7)',\n lineColor: 'rgba(0, 144, 67, 0.7)',\n lineWidth: 1 * scale,\n fillColor: 'rgba(127, 217, 167, 0.7)',\n marker: {\n enabled: true,\n radius: 4 * scale,\n fillColor: 'rgba(18, 172, 98, 1)',\n lineColor: 'rgba(255, 255, 255, 1)',\n lineWidth: 2 * scale,\n },\n pointPlacement: 'on',\n });\n resultHighChart.value.redraw();\n\n return;\n }\n\n const scale = 3;\n\n const labelOffset = [\n [-0.5, -0.75, 'center'],\n [0.5, -0.75, 'end'],\n [-0.5, 0.25, 'center'],\n [-0.5, 0.25, 'center'],\n [-0.5, -1, 'start'],\n ];\n\n const chartOptions = {\n chart: {\n polar: true,\n height: 260 * scale,\n width: 260 * scale,\n spacingTop: 20 * scale,\n spacingLeft: 10 * scale,\n spacingRighteft: 0 * scale,\n spacingBottom: 0 * scale,\n events: {\n load() {\n console.debug('chart on load');\n },\n render() {\n console.debug('chart on render');\n let chart = this;\n let ticks = chart.xAxis[0].ticks;\n\n for (let i in ticks) {\n if (!ticks[i].label) {\n return;\n }\n\n const offset = labelOffset[i];\n\n if (!offset) {\n return;\n }\n\n if (ticks[i].label.alignValue !== 'left') {\n let labelWidth = ticks[i].label.getBBox().width;\n let labelHeight = ticks[i].label.getBBox().height;\n\n ticks[i].label.attr({\n 'text-anchor': offset[2],\n });\n\n //set the center label on the middle\n if (ticks[i].label.alignValue === 'center') {\n ticks[i].label.translate(labelWidth * offset[0], labelHeight * offset[1]);\n } else {\n //move the left labels to the left\n ticks[i].label.translate(0, 0);\n }\n }\n }\n },\n },\n },\n credits: {\n enabled: false,\n },\n exporting: {\n enabled: false,\n },\n title: {\n text: undefined,\n },\n tooltip: {\n enabled: false,\n },\n legend: {\n enabled: false,\n },\n xAxis: {\n categories: chartData.map(i => i.name),\n tickmarkPlacement: 'on',\n lineWidth: 0,\n gridLineWidth: 0,\n labels: {\n align: 'center',\n distance: 10 * scale,\n padding: 0,\n x: 0,\n style: {\n fontSize: 12 * scale,\n },\n // formatter: function () {\n // const v = data[this.pos];\n\n // return v ? `