text-to-chart.js 92 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026
  1. console.log('text-to-chart');
  2. var accessToken = localStorage.getItem("access_token");
  3. console.log(accessToken);
  4. if (accessToken === null) {
  5. alert('請先登入')
  6. window.location.href = "./login.html";
  7. }
  8. var usernameEmail = localStorage.getItem("username");
  9. var unit_table_value
  10. var obj = {
  11. _text: ''
  12. };
  13. // let username = usernameEmail.split('@')[0];
  14. Chart.register(ChartDataLabels);
  15. var userNameSpan = document.getElementById("userName");
  16. var lineYfontSize = document.getElementById("lineYfontSize");
  17. userNameSpan.textContent = usernameEmail;
  18. var inputField = document.getElementById("keyword_data");
  19. var inputField_compare = document.getElementById("keyword_data_compare");
  20. var compare_box = document.getElementById("compare");
  21. var sendButton = document.getElementById("send_data");
  22. var send_data_compare = document.getElementById("data_compare");
  23. var canvasPng = document.getElementById("textToChart");
  24. var img_box = document.getElementById("img_box");
  25. var sidebar = document.getElementById('style_setting');
  26. var input_text_value;
  27. var chartType = 'line';
  28. var unit = '';
  29. var myChart = null;
  30. var chartColor = '#912B2B';
  31. var chartColorBar = '#'
  32. var displayXaxes = true;
  33. var ctx;
  34. var chartlabels;
  35. var chartdata;
  36. var XfontSizeValue = 16;
  37. var YfontSizeValue = 18;
  38. var TitlefontSizeValue = 32;
  39. var pieFontSize = 14;
  40. var PieMaxWidth = 100;
  41. var pointRadiusValue = 0;
  42. var pointHoverRadiusValue = 0;
  43. var chart_bgimg_url = "url(./img/bg06.webp)";
  44. var vocab = [];
  45. var vocab_compare = [];
  46. var chartDiv = document.getElementById('chartdiv');
  47. var dataFirstValue;
  48. var dataLastValue;
  49. let cancelToken;
  50. let source;
  51. $('#chartdiv').hide();
  52. var clickExample = document.getElementById('example_box')
  53. document.addEventListener('DOMContentLoaded', function () {
  54. axios.get("https://cmm.ai:8080/vocab")
  55. .then(response => {
  56. // apiHideLoading();
  57. console.log(response);
  58. vocab = response.data;
  59. })
  60. .catch(error => {
  61. console.error('發生錯誤:', error);
  62. });
  63. });
  64. // 比較提示詞
  65. document.addEventListener('DOMContentLoaded', function () {
  66. axios.get("https://cmm.ai:8080/vocab_comparision")
  67. .then(response => {
  68. // apiHideLoading();
  69. console.log(response);
  70. vocab_compare = response.data;
  71. })
  72. .catch(error => {
  73. console.error('發生錯誤:', error);
  74. });
  75. });
  76. inputField.addEventListener('click', function () {
  77. // 確保輸入框處於焦點狀態
  78. inputField.focus();
  79. // 在控制台中顯示焦點狀態
  80. console.log('Input field focused');
  81. });
  82. document.addEventListener('DOMContentLoaded', function () {
  83. // const input = document.getElementById('stockInput');
  84. const suggestions = document.getElementById('suggestions');
  85. inputField.addEventListener('input', function () {
  86. const query = this.value.trim();
  87. suggestions.innerHTML = ''; // 清空之前的建議
  88. console.log('query', query);
  89. if (query) {
  90. // const filteredVocab = vocab.filter(item => item.toLowerCase().includes(query));
  91. const queryTerms = query.split(/\s+/); // 分割輸入的內容
  92. const lastQueryTerm = queryTerms[queryTerms.length - 1];
  93. const filteredVocab = vocab.filter(item => {
  94. // 對每個分割後的單詞進行匹配,只要其中有一個匹配成功就返回 true
  95. return item.toLowerCase().includes(lastQueryTerm.toLowerCase());
  96. });
  97. if (filteredVocab.length > 0) {
  98. suggestions.style.border = '1px solid #ccc';
  99. suggestions.style.display = 'block'; // 顯示建議容器
  100. } else {
  101. suggestions.style.border = 'none';
  102. suggestions.style.display = 'none'; // 隱藏建議容器
  103. }
  104. console.log(queryTerms[queryTerms.length - 1])
  105. filteredVocab.forEach(item => {
  106. const div = document.createElement('div');
  107. div.classList.add('autocomplete-suggestion');
  108. div.textContent = item;
  109. // div.addEventListener('click', function () {
  110. // inputField.value = item;
  111. // suggestions.innerHTML = ''; // 清空建議
  112. // suggestions.style.border = 'none';
  113. // suggestions.style.display = 'none'; // 隱藏建議容器
  114. // });
  115. // console.log('queryTerms', queryTerms);
  116. // console.log('filteredVocab', filteredVocab);
  117. div.addEventListener('click', function () {
  118. queryTerms.forEach((term, index) => {
  119. const regex = new RegExp(term, 'gi');
  120. const match = regex.exec(inputField.value);
  121. // console.log(queryTerms[queryTerms.length - 1]);
  122. // console.log(filteredVocab, queryTerms[queryTerms.length - 1])
  123. if (index === queryTerms.length - 1) {
  124. if (match) {
  125. var start = match.index;
  126. var end = start + match[0].length;
  127. console.log(start, end)
  128. const textBefore = inputField.value.substring(0, start);
  129. const textAfter = inputField.value.substring(end, inputField.value.length);
  130. console.log('start', start, 'end', end, 'textBefore', textBefore, 'textAfter', textAfter);
  131. // 捕獲被選中的部分並替換它
  132. const selectedText = inputField.value.substring(start, end);
  133. console.log('selectedText', selectedText);
  134. const newText = textBefore + item + textAfter.replace(selectedText, '');
  135. console.log('newText', newText);
  136. inputField.value = newText;
  137. // inputField.value = textBefore + item.replace(selectedText, '') + textAfter;
  138. suggestions.innerHTML = ''; // 清空建議
  139. suggestions.style.border = 'none';
  140. }
  141. }
  142. });
  143. // const start = inputField.selectionStart;
  144. // const end = inputField.selectionEnd;
  145. });
  146. suggestions.appendChild(div);
  147. });
  148. } else {
  149. suggestions.style.border = 'none';
  150. suggestions.style.display = 'none'; // 隱藏建議容器
  151. }
  152. });
  153. });
  154. document.addEventListener('DOMContentLoaded', function () {
  155. // const input = document.getElementById('stockInput');
  156. const suggestions_compare = document.getElementById('suggestions_compare');
  157. inputField_compare.addEventListener('input', function () {
  158. const query = this.value.trim();
  159. suggestions_compare.innerHTML = ''; // 清空之前的建議
  160. console.log('query', query);
  161. if (query) {
  162. // const filteredVocab = vocab.filter(item => item.toLowerCase().includes(query));
  163. const queryTerms = query.split(/\s+/); // 分割輸入的內容
  164. const lastQueryTerm = queryTerms[queryTerms.length - 1];
  165. const filteredVocab = vocab_compare.filter(item => {
  166. // 對每個分割後的單詞進行匹配,只要其中有一個匹配成功就返回 true
  167. return item.toLowerCase().includes(lastQueryTerm.toLowerCase());
  168. });
  169. if (filteredVocab.length > 0) {
  170. suggestions_compare.style.border = '1px solid #ccc';
  171. suggestions_compare.style.display = 'block'; // 顯示建議容器
  172. } else {
  173. suggestions_compare.style.border = 'none';
  174. suggestions_compare.style.display = 'none'; // 隱藏建議容器
  175. }
  176. console.log(queryTerms[queryTerms.length - 1])
  177. filteredVocab.forEach(item => {
  178. const div = document.createElement('div');
  179. div.classList.add('autocomplete-suggestion');
  180. div.textContent = item;
  181. // div.addEventListener('click', function () {
  182. // inputField.value = item;
  183. // suggestions.innerHTML = ''; // 清空建議
  184. // suggestions.style.border = 'none';
  185. // suggestions.style.display = 'none'; // 隱藏建議容器
  186. // });
  187. // console.log('queryTerms', queryTerms);
  188. // console.log('filteredVocab', filteredVocab);
  189. div.addEventListener('click', function () {
  190. queryTerms.forEach((term, index) => {
  191. const regex = new RegExp(term, 'gi');
  192. const match = regex.exec(inputField_compare.value);
  193. // console.log(queryTerms[queryTerms.length - 1]);
  194. // console.log(filteredVocab, queryTerms[queryTerms.length - 1])
  195. if (index === queryTerms.length - 1) {
  196. if (match) {
  197. var start = match.index;
  198. var end = start + match[0].length;
  199. console.log(start, end)
  200. const textBefore = inputField_compare.value.substring(0, start);
  201. const textAfter = inputField_compare.value.substring(end, inputField_compare.value.length);
  202. console.log('start', start, 'end', end, 'textBefore', textBefore, 'textAfter', textAfter);
  203. // 捕獲被選中的部分並替換它
  204. const selectedText = inputField_compare.value.substring(start, end);
  205. console.log('selectedText', selectedText);
  206. const newText = textBefore + item + textAfter.replace(selectedText, '');
  207. console.log('newText', newText);
  208. inputField_compare.value = newText;
  209. // inputField.value = textBefore + item.replace(selectedText, '') + textAfter;
  210. suggestions_compare.innerHTML = ''; // 清空建議
  211. suggestions_compare.style.border = 'none';
  212. }
  213. }
  214. });
  215. // const start = inputField.selectionStart;
  216. // const end = inputField.selectionEnd;
  217. });
  218. suggestions_compare.appendChild(div);
  219. });
  220. } else {
  221. suggestions_compare.style.border = 'none';
  222. suggestions_compare.style.display = 'none'; // 隱藏建議容器
  223. }
  224. });
  225. });
  226. // 監聽輸入框的鍵盤事件
  227. // document.getElementById("keyword_data").addEventListener("keyup", function (event) {
  228. // // 判斷是否按下 Enter 鍵 (key code: 13)
  229. // if (event.keyCode === 13) {
  230. // // 觸發送出按鈕的點擊事件
  231. // document.getElementById("send_data").click();
  232. // }
  233. // });
  234. // 定義按鈕點擊事件處理函數
  235. function sendButtonClickHandler() {
  236. // inputField.value = "";
  237. console.log(inputField.value);
  238. var input_text_value = inputField.value;
  239. // input_text_value = '台積電近兩週收盤價'
  240. compare_box.style.display = "none";
  241. $('#chartdiv').hide();
  242. sidebar.classList.remove('show');
  243. sidebar.classList.add('hidden');
  244. // 在這裡添加你希望在按下按鈕時執行的其他代碼
  245. get_data(input_text_value);
  246. }
  247. sendButton.addEventListener("click", function () {
  248. sendButtonClickHandler();
  249. inputField.value = ''
  250. unit_value_box.style.display = "none"
  251. chartTypeBtn.style.display = "none";
  252. });
  253. let labels2 = [];
  254. let data2 = [];
  255. // 漲幅or單價
  256. const checkbox1 = document.getElementById('checkbox1');
  257. const checkbox2 = document.getElementById('checkbox2');
  258. var compare_type_input = document.querySelector('.checkbox-group')
  259. var compare_type = "";
  260. checkbox1.addEventListener('change', () => {
  261. if (checkbox1.checked) {
  262. checkbox2.checked = false;
  263. compare_type = '漲幅'
  264. console.log(compare_type);
  265. }
  266. });
  267. checkbox2.addEventListener('change', () => {
  268. if (checkbox2.checked) {
  269. checkbox1.checked = false;
  270. compare_type = '單價'
  271. console.log(compare_type);
  272. }
  273. });
  274. send_data_compare.addEventListener("click", function () {
  275. // let compare_url=`https://cmm.ai:8080/get_data_from_date?start_date=${dataFirstValue}&end_date=${dataLastValue}&stock_name=${inputField_compare.value}`
  276. let compare_url = `https://cmm.ai:8080/get_data_from_date?target=${inputField_compare.value}`
  277. console.log('比較', inputField_compare.value);
  278. const stockName = inputField_compare.value;
  279. axios
  280. // .get(compare_url)
  281. .post(`https://cmm.ai:8080/get_comparision_data?target=${inputField_compare.value}`, dataArray)
  282. .then((response) => {
  283. console.log(response);
  284. var compare_data = response.data.data;
  285. labels2 = []; // Clear previous labels
  286. data2 = []; // Clear previous data
  287. if (compare_type === "") {
  288. alert('請輸入比較類型');
  289. return
  290. }
  291. // console.log('比較送出後 ', chartType)
  292. if (response.data.data === "無法產生圖表") {
  293. alert('無法產生圖表')
  294. return
  295. }
  296. if (chartType === 'line') {
  297. const colors = [
  298. '#82a33f', // rgba already
  299. '#2f487b', // rgba already
  300. '#df8c49', // hex #df8c49
  301. '#72598f', // hex #72598f
  302. '#489fb6', // hex #489fb6
  303. '#99b0d5' // hex #99b0d5
  304. ];
  305. let colorIndex = 0;
  306. for (var i = 0; i < compare_data.length; i++) {
  307. var item = compare_data[i];
  308. // 遍历当前项的属性
  309. for (var key in item) {
  310. // 如果属性名不在 labels 数组中,并且属性值不是对象,则将属性名添加到 labels 数组中
  311. if (typeof item[key] === 'string') {
  312. labels2.push(item[key]);
  313. }
  314. // 如果属性值是数值类型,则将其添加到 data 数组中
  315. if (typeof item[key] === 'number') {
  316. data2.push(item[key]);
  317. }
  318. }
  319. }
  320. function getRandomColor() {
  321. const randomIndex = Math.floor(Math.random() * colors.length);
  322. const selectedColor = colors[randomIndex];
  323. colors.splice(randomIndex, 1); // 从数组中移除已选颜色
  324. return selectedColor;
  325. }
  326. const normalize = (prices) => {
  327. const initialPrice = prices[0];
  328. // return prices.map(price => (price - initialPrice) / initialPrice * 100);
  329. return prices.map(price => {
  330. // Calculate the normalized price
  331. const normalized = (price - initialPrice) / initialPrice * 100;
  332. // Round to 2 decimal places and convert back to a number
  333. return parseFloat(normalized.toFixed(2));
  334. });
  335. };
  336. var stock2Normalized;
  337. var stock1Normalized;
  338. if (compare_type === '漲幅') {
  339. console.log('漲幅')
  340. stock2Normalized = normalize(data2);
  341. stock1Normalized = normalize(data);
  342. unitInput.value = '%';
  343. unit_value.textContent = '%'
  344. } else {
  345. console.log('單價')
  346. unitInput.value = unitInput.value;
  347. unit_value.textContent = unitInput.value;
  348. stock2Normalized = data2;
  349. stock1Normalized = data;
  350. }
  351. const color = getRandomColor();
  352. if (response.data.data.length < 15) {
  353. pointRadiusValue = 5;
  354. pointHoverRadiusValue = 0;
  355. togglePoints.checked = true;
  356. }
  357. const newDataset = {
  358. label: stockName,
  359. backgroundColor: color,
  360. pointRadius: pointRadiusValue,
  361. pointHoverRadius: 0,
  362. pointBorderColor: '#fff',
  363. pointBorderWidth: 3,
  364. pointHoverRadiusValue: 8,
  365. borderColor: color,
  366. data: stock2Normalized,
  367. fill: false
  368. };
  369. colorIndex = (colorIndex + 1) % colors.length;
  370. myChart.data.datasets.push(newDataset);
  371. myChart.data.datasets[0].data = stock1Normalized;
  372. console.log('多筆資料', myChart.data.datasets);
  373. // console.log('比較', myChart.options.plugins.legend);
  374. myChart.options.plugins.legend.display = true;
  375. // myChart.defaults.global.legend = {
  376. // display: true,
  377. // position: 'top', // 默认图例位置为顶部
  378. // labels: {
  379. // fontColor: 'rgb(255, 99, 132)', // 默认图例标签颜色
  380. // fontSize: 14 // 默认图例标签字体大小
  381. // }
  382. // };
  383. myChart.update();
  384. inputField_compare.value = '';
  385. compare_type_input.style.display = 'none'
  386. } else if (chartType === 'bar') {
  387. }
  388. })
  389. .catch((error) =>
  390. console.log(error)
  391. );
  392. });
  393. // sendButton.addEventListener("click", function () {
  394. // console.log(inputField.value);
  395. // input_text_value = inputField.value;
  396. // // if (myChart) {
  397. // // console.log('已存在')
  398. // // data = [];
  399. // // labels = [];
  400. // // myChart.removePlugin(Chart.pluginService.getPlugin('afterDraw'));
  401. // // myChart.destroy();
  402. // // }
  403. // get_data(input_text_value);
  404. // });
  405. var lastKeyPressTime = 0;
  406. var enterCount = 0;
  407. var ENTER_THRESHOLD = 500; // 設定連續按下 Enter 的時間閾值(毫秒)
  408. inputField.addEventListener("keyup", function (event) {
  409. // 判斷是否按下 Enter 鍵 (key code: 13)
  410. if (event.key === "Enter") {
  411. var currentTime = new Date().getTime();
  412. // 計算與上一次按鍵按下時間的差值
  413. var timeDiff = currentTime - lastKeyPressTime;
  414. lastKeyPressTime = currentTime;
  415. // 如果兩次 Enter 鍵按下的時間差小於閾值,則增加計數器
  416. if (timeDiff <= ENTER_THRESHOLD) {
  417. enterCount++;
  418. // 如果計數器為2,則觸發 API 請求並重置計數器
  419. if (enterCount === 2) {
  420. sendButtonClickHandler();
  421. enterCount = 0;
  422. }
  423. } else {
  424. // 如果時間差大於閾值,則重置計數器
  425. enterCount = 0;
  426. }
  427. } else {
  428. // 如果按下的不是 Enter 鍵,則重置計數器
  429. enterCount = 0;
  430. }
  431. });
  432. function number_format(number, decimals, dec_point, thousands_sep) {
  433. // * example: number_format(1234.56, 2, ',', ' ');
  434. // * return: '1 234,56'
  435. number = (number + '').replace(',', '').replace(' ', '');
  436. var n = !isFinite(+number) ? 0 : +number,
  437. prec = !isFinite(+decimals) ? 0 : Math.abs(decimals),
  438. sep = (typeof thousands_sep === 'undefined') ? ',' : thousands_sep,
  439. dec = (typeof dec_point === 'undefined') ? '.' : dec_point,
  440. s = '',
  441. toFixedFix = function (n, prec) {
  442. var k = Math.pow(10, prec);
  443. return '' + Math.round(n * k) / k;
  444. };
  445. // Fix for IE parseFloat(0.55).toFixed(0) = 0;
  446. s = (prec ? toFixedFix(n, prec) : '' + Math.round(n)).split('.');
  447. if (s[0].length > 3) {
  448. s[0] = s[0].replace(/\B(?=(?:\d{3})+(?!\d))/g, sep);
  449. }
  450. if ((s[1] || '').length < prec) {
  451. s[1] = s[1] || '';
  452. s[1] += new Array(prec - s[1].length + 1).join('0');
  453. }
  454. return s.join(dec);
  455. }
  456. var labels = [];
  457. var data = [];
  458. function apiLoading() {
  459. document.getElementById('chartDataLoading').style.display = 'block';
  460. }
  461. function apiHideLoading() {
  462. document.getElementById('chartDataLoading').style.display = 'none';
  463. }
  464. const DISPLAY = true;
  465. const BORDER = true;
  466. const CHART_AREA = true;
  467. const TICKS = true;
  468. var labels = [];
  469. var data = [];
  470. var dataArray;
  471. // get_data('2330近兩週收盤價');
  472. function generateChart(dataArray) {
  473. if (dataArray.length > 15) {
  474. lineYfontSize.style.display = 'none'
  475. } else {
  476. lineYfontSize.style.display = 'block'
  477. }
  478. // 创建一个空数组来存储 labels 和 data
  479. console.log(dataArray)
  480. // 遍历数据数组
  481. for (var i = 0; i < dataArray.length; i++) {
  482. var item = dataArray[i];
  483. // 遍历当前项的属性
  484. for (var key in item) {
  485. // 如果属性名不在 labels 数组中,并且属性值不是对象,则将属性名添加到 labels 数组中
  486. if (typeof item[key] === 'string') {
  487. labels.push(item[key]);
  488. } else {
  489. if (key === 'x') {
  490. labels.push(String(item[key]));
  491. }
  492. }
  493. if (key === 'y') {
  494. data.push(item[key]);
  495. }
  496. // 如果属性值是数值类型,则将其添加到 data 数组中
  497. // if (typeof item[key] === 'number') {
  498. // data.push(item[key]);
  499. // }
  500. }
  501. }
  502. console.log(data);
  503. dataFirstValue = labels[0];
  504. dataLastValue = labels[labels.length - 1]
  505. console.log('dataFirstValue', dataFirstValue);
  506. console.log('dataLastValue', dataLastValue);
  507. if (myChart) {
  508. console.log('已存在')
  509. myChart.destroy();
  510. }
  511. if (chartType === "doughnut") {
  512. // createChartDoughnut(chartType, data, labels);
  513. // createChartDoughnut2(data, labels)
  514. } else {
  515. createChart(chartType, data, labels);
  516. }
  517. // console.log(data);
  518. console.log(labels);
  519. }
  520. var amChartInstance;
  521. function extractAndGenerateChart(dataArray) {
  522. for (var i = 0; i < dataArray.length; i++) {
  523. var item = dataArray[i];
  524. // 遍历当前项的属性
  525. for (var key in item) {
  526. // 如果属性名不在 labels 数组中,并且属性值不是对象,则将属性名添加到 labels 数组中
  527. if (typeof item[key] === 'string') {
  528. labels.push(item[key]);
  529. }
  530. // 如果属性值是数值类型,则将其添加到 data 数组中
  531. if (typeof item[key] === 'number') {
  532. data.push(item[key]);
  533. }
  534. }
  535. }
  536. // console.log('key', key);
  537. generateBarChart(key, dataArray);
  538. }
  539. // 圓餅圖
  540. function generatePieChart(dataArray, pieFontSize, PieMaxWidth) {
  541. compare_box.style.display = "none";
  542. $('#chartdiv').show();
  543. bgImgelement.style.backgroundImage = chart_bgimg_url;
  544. // chartDiv.style.width = "100%"
  545. chartWhitelement.style.background = "rgba(255,255,255,0.5)"
  546. // Themes begin
  547. am4core.useTheme(am4themes_animated);
  548. // Themes end
  549. // Create chart instance
  550. var chart = am4core.create("chartdiv", am4charts.PieChart);
  551. chart.data = dataArray;
  552. chart.innerRadius = am4core.percent(50);
  553. // Add and configure Series
  554. var pieSeries = chart.series.push(new am4charts.PieSeries());
  555. pieSeries.dataFields.value = "y";
  556. pieSeries.dataFields.category = "x";
  557. pieSeries.slices.template.stroke = am4core.color("#fff");
  558. pieSeries.slices.template.strokeWidth = 2;
  559. pieSeries.slices.template.strokeOpacity = 1;
  560. // Configure labels
  561. pieSeries.labels.template.wrap = true;
  562. console.log('PieMaxWidth', PieMaxWidth);
  563. pieSeries.labels.template.maxWidth = PieMaxWidth;
  564. pieSeries.labels.template.truncate = false;
  565. pieSeries.labels.template.fontSize = 14;
  566. if (typeof PieMaxWidth === 'string') {
  567. console.log('The variable is a string.');
  568. } else {
  569. console.log('The variable is a number.');
  570. }
  571. // Configure label text to wrap and show percentages
  572. // pieSeries.labels.template.adapter.add("textOutput", function (text, target) {
  573. // if (target.dataItem && target.dataItem.values.value.percent) {
  574. // return "[font-size: 14px]" + target.dataItem.category + ": " + target.dataItem.values.value.percent.toFixed(1) + "%";
  575. // }
  576. // return text;
  577. // });
  578. pieSeries.labels.template.adapter.add("textOutput", function (text, target) {
  579. if (target.dataItem && target.dataItem.values.value.percent) {
  580. return "[font-size: " + pieFontSize + "px]" + target.dataItem.category + ": " + target.dataItem.values.value.percent.toFixed(1) + "%";
  581. }
  582. return text;
  583. });
  584. pieSeries.colors.list = [
  585. am4core.color('rgb(171, 51, 49)'),
  586. am4core.color('rgb(34, 83, 149)'),
  587. am4core.color('rgb(79, 148, 65)'),
  588. am4core.color('rgb(217, 195, 105)'),
  589. am4core.color('rgb(142, 124, 180)'),
  590. am4core.color('rgb(211, 183, 144)'),
  591. am4core.color('rgb(83, 84, 84)'),
  592. am4core.color('rgb(229, 147, 152)')
  593. ];
  594. // Ensure labels have background
  595. // pieSeries.labels.template.background.fillOpacity = 1;
  596. console.log(pieSeries.labels.template)
  597. // Set label background color to match corresponding slice color
  598. pieSeries.labels.template.adapter.add("background.fill", function (fill, target) {
  599. return target.dataItem.slice.fill;
  600. });
  601. // This creates initial animation
  602. pieSeries.hiddenState.properties.opacity = 1;
  603. pieSeries.hiddenState.properties.endAngle = -90;
  604. pieSeries.hiddenState.properties.startAngle = -90;
  605. // console.log('調位置')
  606. downloadButton.style.display = "inline-block";
  607. chartTypeBtn.style.display = "inline-block";
  608. }
  609. // var colorEven='#288D97';
  610. // var colorOdd='#427D7E'
  611. function adjustColorBrightness(color, amount) {
  612. let usePound = false;
  613. if (color[0] == "#") {
  614. color = color.slice(1);
  615. usePound = true;
  616. }
  617. let num = parseInt(color, 16);
  618. let r = (num >> 16) + amount;
  619. let g = ((num >> 8) & 0x00FF) + amount;
  620. let b = (num & 0x0000FF) + amount;
  621. if (r > 255) r = 255;
  622. else if (r < 0) r = 0;
  623. if (g > 255) g = 255;
  624. else if (g < 0) g = 0;
  625. if (b > 255) b = 255;
  626. else if (b < 0) b = 0;
  627. return (usePound ? "#" : "") + (r << 16 | g << 8 | b).toString(16).padStart(6, '0');
  628. }
  629. // 柱狀圖
  630. function generateBarChart(key, dataArray) {
  631. console.log('generateBarChart', dataArray)
  632. chartTypeBtn.style.display = "inline-block";
  633. compare_box.style.display = "none";
  634. $('#chartdiv').show();
  635. // var colorEven = document.getElementById('colorEven').value;
  636. // var colorOdd = document.getElementById('colorOdd').value;
  637. var baseColor = document.getElementById('borderColorInputBar').value;
  638. var categoryAxisFzValue = document.getElementById('XfontSizeBar').value;
  639. var valueAxisFzValue = document.getElementById('YfontSizeBar').value;
  640. var colorEven = am4core.color(baseColor);
  641. var colorOdd = adjustColorBrightness(baseColor, -20);
  642. chartDiv.style.fontFamily = "Arial, sans-serif";
  643. chartDiv.style.fontWeight = "900";
  644. bgImgelement.style.backgroundImage = chart_bgimg_url;
  645. chartWhitelement.style.background = "rgba(255,255,255,0.5)"
  646. console.log('labelDependent', labelDependent);
  647. // Themes begin
  648. am4core.useTheme(am4themes_animated);
  649. // Themes end
  650. // Create chart instance
  651. var chart = am4core.create("chartdiv", am4charts.XYChart3D);
  652. chart.data = dataArray;
  653. chart.angle = 30; // 圖表角度
  654. chart.depth = 25; // 圖表深度
  655. let categoryAxis = chart.xAxes.push(new am4charts.CategoryAxis());
  656. categoryAxis.dataFields.category = "x";
  657. categoryAxis.renderer.labels.template.rotation = 0;
  658. categoryAxis.renderer.labels.template.hideOversized = false;
  659. categoryAxis.renderer.minGridDistance = 20; // 間距
  660. // categoryAxis.renderer.labels.template.horizontalCenter = "left";
  661. // categoryAxis.renderer.labels.template.verticalCenter = "middle";
  662. // categoryAxis.tooltip.label.rotation = 90;
  663. // categoryAxis.tooltip.label.horizontalCenter = "right";
  664. // categoryAxis.tooltip.label.verticalCenter = "middle";
  665. // label 換行
  666. let label = categoryAxis.renderer.labels.template;
  667. label.wrap = true;
  668. if (dataArray.length > 8) {
  669. console.log('資料長度5')
  670. label.maxWidth = 50;
  671. }
  672. else if (dataArray.length < 7) {
  673. label.maxWidth = 80;
  674. } else {
  675. label.maxWidth = 100;
  676. console.log('資料長度<5')
  677. }
  678. // x軸字體大小
  679. categoryAxis.renderer.labels.template.fontSize = categoryAxisFzValue;
  680. categoryAxis.renderer.cellEndLocation = 0.5; // 減少值可以增加間距
  681. let valueAxis = chart.yAxes.push(new am4charts.ValueAxis());
  682. valueAxis.title.text = labelDependent; // Y軸
  683. valueAxis.title.fontWeight = "bold";
  684. // y軸字體大小
  685. valueAxis.renderer.labels.template.fontSize = valueAxisFzValue;
  686. // Create series
  687. var series = chart.series.push(new am4charts.ColumnSeries3D());
  688. series.dataFields.valueY = 'y';
  689. series.dataFields.categoryX = "x";
  690. series.name = "y";
  691. series.tooltipText = "{categoryX}: [bold]{valueY}[/]";
  692. series.columns.template.fillOpacity = .8;
  693. let columnTemplate = series.columns.template;
  694. columnTemplate.strokeWidth = 2;
  695. columnTemplate.strokeOpacity = 1;
  696. columnTemplate.stroke = am4core.color("#FFFFFF");
  697. // columnTemplate.adapter.add("fill", function (fill, target) {
  698. // return chart.colors.getIndex(target.dataItem.index);
  699. // })
  700. // 設置基數和偶數顏色
  701. columnTemplate.adapter.add("fill", function (fill, target) {
  702. return target.dataItem.index % 2 === 0 ? am4core.color(colorEven) : am4core.color(colorOdd);
  703. });
  704. columnTemplate.adapter.add("stroke", function (stroke, target) {
  705. return chart.colors.getIndex(target.dataItem.index);
  706. })
  707. // // 加上白色線條
  708. // series.columns.template.events.on("validated", function (event) {
  709. // let column = event.target;
  710. // let whiteLine = column.createChild(am4core.Line);
  711. // whiteLine.stroke = am4core.color("#ffffff");
  712. // whiteLine.strokeWidth = 1.3; // 線條寬度
  713. // whiteLine.strokeOpacity = 0.5;// 透明度
  714. // whiteLine.x1 = column.pixelWidth / 1;
  715. // whiteLine.y1 = 0;
  716. // whiteLine.x2 = column.pixelWidth / 1;
  717. // whiteLine.y2 = column.pixelHeight;
  718. // });
  719. chart.cursor = new am4charts.XYCursor();
  720. chart.cursor.lineX.strokeOpacity = 0;
  721. chart.cursor.lineY.strokeOpacity = 0;
  722. categoryAxis.renderer.grid.template.strokeOpacity = 0; // 隱藏 X 軸
  723. // valueAxis.renderer.grid.template.strokeOpacity = 0; // 隱藏 Y 軸
  724. // valueAxis.renderer.line.strokeOpacity = 0;
  725. // LineSeries 底部線條
  726. var lineSeries = chart.series.push(new am4charts.LineSeries());
  727. lineSeries.dataFields.valueY = "bottom";
  728. lineSeries.dataFields.categoryX = "0";
  729. lineSeries.stroke = am4core.color("#FFFFFF")
  730. lineSeries.strokeWidth = 2;
  731. lineSeries.strokeOpacity = 1
  732. var bottomData = [];
  733. for (var i = 0; i < dataArray.length; i++) {
  734. bottomData.push({ "x": dataArray[i].x, "bottom": 0 });
  735. }
  736. lineSeries.data = bottomData;
  737. downloadButton.style.display = "inline-block";
  738. chartTypeBtn.style.display = "inline-block";
  739. chartTypeBtn.style.opacity = "1";
  740. // 底部線條
  741. // let rectElements = document.querySelectorAll("rect");
  742. // // let widthValue = 0;
  743. // let fifthRectElement = rectElements[3];
  744. // // fifthRectElement.style.position = "relative";
  745. // setTimeout(() => {
  746. // let width = fifthRectElement.getAttribute("width");
  747. // // widthValue = parseFloat(width);
  748. // console.log('rectElements', rectElements);
  749. // console.log('fifthRectElement', fifthRectElement);
  750. // console.log('width', width);
  751. // let line = document.createElement("div");
  752. // line.style.position = "absolute";
  753. // line.style.bottom = "162px";
  754. // line.style.right = "40px";
  755. // line.style.width = `${parseFloat(width)}px`;
  756. // line.style.height = "2px";
  757. // line.style.backgroundColor = "#000000";
  758. // document.getElementById("chartdiv").appendChild(line);
  759. // }, 10)
  760. // end am4core.ready()
  761. }
  762. function handleClick(element) {
  763. const Clickvalue = element.dataset.value;
  764. console.log(Clickvalue); // 123
  765. get_data(Clickvalue);
  766. }
  767. // 圖表類型
  768. var chartTypeBtn = document.getElementById("chartType");
  769. chartTypeBtn.addEventListener('change', function () {
  770. const selectedValue = this.value;
  771. console.log('選擇的圖表類型:', selectedValue);
  772. obj.text = selectedValue;
  773. // 在此处添加处理选定图表类型的代码
  774. if (selectedValue === 'line') {
  775. // 处理折线图
  776. console.log('處理折線圖', dataArray);
  777. $('#chartdiv').hide();
  778. chartType = 'line';
  779. if (myChart) {
  780. data = [];
  781. labels = [];
  782. myChart.destroy();
  783. }
  784. sidebar.classList.add('hidden');
  785. generateChart(dataArray);
  786. } else if (selectedValue === 'bar') {
  787. chartType = 'bar';
  788. $('#textToChart').hide();
  789. // 处理柱状图
  790. console.log('處理柱狀圖', dataArray);
  791. sidebar.classList.add('hidden');
  792. extractAndGenerateChart(dataArray);
  793. } else if (selectedValue === 'doughnut') {
  794. chartType = 'doughnut';
  795. console.log('處理圓餅圖', dataArray);
  796. unit_value.textContent = "";
  797. $('#textToChart').hide();
  798. sidebar.classList.add('hidden');
  799. generatePieChart(dataArray, pieFontSize, PieMaxWidth)
  800. // 处理圆饼图
  801. }
  802. });
  803. var downloadButton = document.getElementById("downloadButton");
  804. // downloadButton.style.display = 'none';
  805. // 添加點擊事件監聽器
  806. downloadButton.addEventListener('click', function () {
  807. setTimeout(function () {
  808. // html2canvas(document.getElementById('data_chart_box')).then(function (canvas) {
  809. // // // 獲取圖片資料 URL
  810. // // var imageData = canvasPng.toDataURL("image/png");
  811. // // // 創建下載連結
  812. // // var link = document.createElement('a');
  813. // // link.download = 'chart.png'; // 下載的檔案名稱
  814. // // link.href = imageData; // 圖片資料 URL
  815. // // // 模擬點擊下載連結
  816. // // link.click();
  817. // // 創建一個&lt;a&gt;元素
  818. // const link = document.createElement('a');
  819. // // 將 Canvas 轉換為 Data URL
  820. // link.href = canvas.toDataURL('image/png');
  821. // // 設置下載文件名
  822. // link.download = 'chart.png';
  823. // // 模擬點擊下載
  824. // link.click();
  825. // });
  826. html2canvas(document.getElementById('data_chart_box'), {
  827. backgroundColor: null, // 確保背景透明
  828. scale: 2,
  829. useCORS: true,
  830. scrollX: 0,
  831. scrollY: 0
  832. }).then(function (canvas) {
  833. // 創建一個 <a> 元素
  834. const link = document.createElement('a');
  835. // 將 Canvas 轉換為 Data URL
  836. link.href = canvas.toDataURL('image/png');
  837. // 設置下載文件名
  838. link.download = 'chart.png';
  839. // 模擬點擊下載
  840. link.click();
  841. });
  842. }, 1000); // 等待 1 秒钟
  843. });
  844. var pieChartColors = [
  845. 'rgb(171, 51, 49)',
  846. 'rgb(34, 83, 149)',
  847. 'rgb(79, 148, 65)',
  848. 'rgb(217, 195, 105)',
  849. 'rgb(142, 124, 180)',
  850. 'rgb(211, 183, 144)',
  851. 'rgb(83, 84, 84)',
  852. 'rgb(229, 147, 152)'
  853. ];
  854. function createChartDoughnut2(data, labels) {
  855. const chartData = {
  856. type: 'outlabeledPie',
  857. data: {
  858. labels: labels,
  859. datasets: [
  860. {
  861. data: data,
  862. borderWidth: borderWidthValue,
  863. fill: false,
  864. backgroundColor: pieChartColors,
  865. },
  866. ],
  867. },
  868. options: {
  869. plugins: {
  870. legend: false,
  871. outlabels: {
  872. text: '%l %p',
  873. color: 'white',
  874. stretch: 35,
  875. font: {
  876. resizable: true,
  877. family: 'Arial',
  878. size: 48,
  879. weight: 'bold',
  880. style: 'italic',
  881. },
  882. },
  883. },
  884. },
  885. };
  886. const devicePixelRatio = 4;
  887. var chartUrl = `https://quickchart.io/chart?c=${encodeURIComponent(JSON.stringify(chartData))}&devicePixelRatio=${devicePixelRatio}&f=png&fontsize=20`;
  888. // 获取目标 div 元素
  889. const targetDiv = document.getElementById('data_chartJs_box');
  890. // 创建一个 img 元素来显示图表
  891. const chartImg = document.createElement('img');
  892. chartImg.src = chartUrl;
  893. chartImg.classList.add('outlabeled_img');
  894. // 清空目标 div 元素
  895. targetDiv.innerHTML = '';
  896. // 将图表 img 元素插入到目标 div 中
  897. targetDiv.appendChild(chartImg);
  898. console.log(chart_bgimg_url);
  899. bgImgelement.style.backgroundImage = chart_bgimg_url;
  900. chartWhitelement.style.background = "rgba(255,255,255,0.5)"
  901. }
  902. // 版型二源餅圖
  903. // // 创建 Chart.js 图表
  904. function createChartDoughnut(chartType, data, labels) {
  905. $('#textToChart').show();
  906. canvasPng.style.maxWidth = '450px';
  907. canvasPng.style.maxHeight = '450px';
  908. var windowWidth = window.innerWidth || document.documentElement.clientWidth;
  909. var canvasWidth = Math.min(500, windowWidth); // 限制最大宽度为 500px
  910. canvasPng.style.width = canvasWidth + 'px'; // 设置 canvas 宽度
  911. canvasPng.style.marginLeft = 'auto'; // 左外边距自动计算
  912. canvasPng.style.marginRight = 'auto'; // 右外边距自动计算
  913. var ctx = document.getElementById("textToChart").getContext('2d');
  914. myChart = new Chart(ctx, {
  915. type: chartType,
  916. data: {
  917. labels: labels,
  918. datasets: [{
  919. strokeColor: "rgba(220,220,220,1)",
  920. StrokeWidth: 5,
  921. data: data,
  922. borderWidth: borderWidthValue,
  923. pointRadius: 0,
  924. pointBorderColor: '#fff',
  925. pointBorderWidth: 3,
  926. pointHoverRadius: 0,
  927. fill: false,
  928. backgroundColor: pieChartColors,
  929. // barPercentage: 0.5,
  930. // categoryPercentage: 0.5
  931. }]
  932. },
  933. options: {
  934. // responsive: true, // 啟用響應式
  935. maintainAspectRatio: true,
  936. responsive: true,
  937. aspectRatio: 1,
  938. plugins: {
  939. legend: {
  940. display: false,
  941. },
  942. datalabels: {
  943. color: '#fff', // 数据标签的颜色
  944. anchor: 'end', // 数据标签的位置
  945. align: 'start', // 数据标签的对齐方式
  946. font: {
  947. size: 14, // 数据标签的字体大小
  948. },
  949. }
  950. },
  951. scales: {
  952. x: {
  953. display: false, // 显示 x 轴
  954. grid: {
  955. display: false,
  956. lineWidth: 5,
  957. color: '#584B3D'
  958. },
  959. ticks: {
  960. textStrokeColor: '#fff',
  961. textStrokeWidth: 5,
  962. font: {
  963. display: false,
  964. family: 'Arial', // 设置字体
  965. size: XfontSizeValue, // 设置字体大小
  966. weight: 'bold', // 设置字体粗细
  967. fontColor: 'rgba(255,255,255,0.8)', // X-axis font color
  968. shadowColor: 'rgba(0, 0, 0, 0.5)', // Shadow color
  969. shadowBlur: 10, // Shadow blur level
  970. shadowOffsetX: 5, // Horizontal shadow offset
  971. shadowOffsetY: 5
  972. },
  973. },
  974. },
  975. y: {
  976. display: false,
  977. beginAtZero: false,
  978. grid: {
  979. display: false,
  980. lineWidth: 5,
  981. color: '#584B3D'
  982. },
  983. ticks: {
  984. textStrokeColor: '#fff',
  985. textStrokeWidth: 5,
  986. font: {
  987. family: 'Arial', // 設置字體
  988. size: XfontSizeValue, // 設置字體大小
  989. weight: 'bold', // 設置字體粗細
  990. color: 'rgba(255,255,255,0.8)', // Y-axis 字體顏色
  991. shadowColor: 'rgba(0, 0, 0, 0.5)', // 陰影顏色
  992. shadowBlur: 10, // 陰影模糊級別
  993. shadowOffsetX: 5, // 水平陰影偏移
  994. shadowOffsetY: 5 // 垂直陰影偏移
  995. },
  996. }
  997. }
  998. },
  999. layout: {
  1000. padding: {
  1001. left: 50, // 调整图表左边距
  1002. right: 50, // 调整图表右边距
  1003. top: 10,
  1004. bottom: 10,
  1005. }
  1006. },
  1007. },
  1008. });
  1009. console.log(chart_bgimg_url);
  1010. bgImgelement.style.backgroundImage = chart_bgimg_url;
  1011. chartWhitelement.style.background = "rgba(255,255,255,0.5)"
  1012. }
  1013. var chartJsBox = document.getElementById('data_chartJs_box');
  1014. function createTable(dataArray, unit, Label_dependent_variable, Label_independent_variable,) {
  1015. $('#chartdiv').hide();
  1016. $('#textToChart').hide();
  1017. chartTitle.style.padding = "15px 0px 30px 30px";
  1018. bgImgelement.style.paddingTop = "10px";
  1019. var table = document.createElement('table');
  1020. table.classList.add('table', 'table_template', 'dynamic-table'); // 添加 Bootstrap 的 table 样式,如果你在使用 Bootstrap
  1021. // 创建表头
  1022. var thead = table.createTHead();
  1023. var headerRow = thead.insertRow();
  1024. // Object.keys(dataArray[0]).forEach(function (key) {
  1025. // var header = headerRow.insertCell();
  1026. // header.textContent = key;
  1027. // });
  1028. var yearHeader = headerRow.insertCell();
  1029. yearHeader.textContent = Label_independent_variable;
  1030. var avgPriceHeader = headerRow.insertCell();
  1031. avgPriceHeader.textContent = Label_dependent_variable;
  1032. // 创建表格主体
  1033. var tbody = table.createTBody();
  1034. dataArray.forEach(function (item) {
  1035. console.log(item)
  1036. var row = tbody.insertRow();
  1037. // var cell1 = row.insertCell();
  1038. // cell1.textContent = item.year;
  1039. // cell1.classList.add('year-class'); // 为年份单元格添加类
  1040. // var cell2 = row.insertCell();
  1041. Object.keys(item).forEach(function (key, index) {
  1042. var cell = row.insertCell();
  1043. if (index === 0) { // If it's the first cell, add 'year-class'
  1044. cell.textContent = item[key];
  1045. cell.classList.add('year-class');
  1046. } else { // Otherwise, add 'average-class'
  1047. if (key === 'Average_Close') {
  1048. // cell.textContent = item[key].toFixed(2) + unit;
  1049. cell.innerHTML = item[key].toFixed(2) + '<span class="unit_table">' + unit + '</span>';
  1050. } else {
  1051. // cell.textContent = item[key];
  1052. // cell.textContent = item[key].toFixed(2) + unit;
  1053. cell.innerHTML = item[key].toFixed(2) + '<span class="unit_table">' + unit + '</span>';
  1054. }
  1055. cell.classList.add('average-class');
  1056. }
  1057. });
  1058. // console.log(item.avg_price.toFixed(2))
  1059. // cell.textContent = item[key].toFixed(2) + unit;
  1060. // cell2.classList.add('average-class'); // 为年份单元格添加类
  1061. });
  1062. // 获取包含表格的 div 元素
  1063. bgImgelement.style.backgroundImage = chart_bgimg_url;
  1064. img_box.style.bottom = '60px';
  1065. img_box.style.right = '5px';
  1066. document.getElementById('img_box_url').style.width = "180px";
  1067. // chartWhitelement.style.background = "rgba(255,255,255,0.5)"
  1068. // 将表格添加到指定的 div 元素中
  1069. chartJsBox.appendChild(table);
  1070. unit_table_value = document.querySelectorAll('.unit_table');
  1071. downloadButton.style.display = "inline-block";
  1072. chartTypeBtn.style.display = "inline-block";
  1073. chartTypeBtn.style.opacity = "0";
  1074. }
  1075. function getDatalabelsConfig(dataLength) {
  1076. if (dataLength < 10) {
  1077. return {
  1078. formatter: function (value, context) {
  1079. return value + ' unit'; // Add unit if data length is less than 10
  1080. },
  1081. textStrokeColor: '#fff',
  1082. textStrokeWidth: 5,
  1083. color: '#584B3D',
  1084. font: {
  1085. size: YfontSizeValue,
  1086. },
  1087. anchor: 'end',
  1088. align: 'end',
  1089. };
  1090. } else {
  1091. return null; // No datalabels if data length is 10 or more
  1092. }
  1093. }
  1094. function getDatalabelsConfig(isEnabled) {
  1095. if (!isEnabled) {
  1096. return null;
  1097. }
  1098. return data.length <= 15 ? {
  1099. formatter: function (value, context) {
  1100. return value;
  1101. },
  1102. textStrokeColor: '#fff',
  1103. textStrokeWidth: 5,
  1104. color: '#584B3D',
  1105. font: {
  1106. size: YfontSizeValue,
  1107. },
  1108. anchor: 'end',
  1109. align: 'top'
  1110. } : null;
  1111. }
  1112. // 版型一折線圖
  1113. // // 创建 Chart.js 图表
  1114. function createChart(chartType, data, labels) {
  1115. compare_type_input.style.display = 'block'
  1116. $('#textToChart').show();
  1117. canvasPng.style.maxWidth = '';
  1118. canvasPng.style.maxHeight = '';
  1119. console.log('繪製圖表', data, labels);
  1120. if (data.length < 15) {
  1121. pointRadiusValue = 5;
  1122. pointHoverRadiusValue = 0;
  1123. togglePoints.checked = true;
  1124. toggleValue.checked = true;
  1125. } else {
  1126. pointRadiusValue = 0;
  1127. togglePoints.checked = false;
  1128. toggleValue.checked = false;
  1129. }
  1130. const datalabelsConfig = data.length <= 15 ? {
  1131. formatter: function (value, context) {
  1132. // if (context.dataIndex === 0) {
  1133. // return '';
  1134. // }
  1135. // if (typeof value === 'number' && value.toString().includes('.')) {
  1136. // value = value.toFixed(4);
  1137. // }
  1138. // return value;
  1139. return value;
  1140. },
  1141. textStrokeColor: '#fff',
  1142. textStrokeWidth: 5,
  1143. color: '#584B3D',
  1144. font: {
  1145. size: YfontSizeValue,
  1146. },
  1147. anchor: 'end',
  1148. align: 'end',//固定
  1149. offset: 5,
  1150. } : null;
  1151. ctx = document.getElementById("textToChart");
  1152. myChart = new Chart(ctx, {
  1153. type: chartType,
  1154. data: {
  1155. labels: labels,
  1156. datasets: [{
  1157. label: stocknumber,
  1158. strokeColor: "rgba(220,220,220,1)",
  1159. StrokeWidth: 5,
  1160. data: data,
  1161. borderWidth: borderWidthValue,
  1162. pointRadius: pointRadiusValue,
  1163. pointBorderColor: '#fff',
  1164. pointBorderWidth: 3,
  1165. // pointHoverRadius: 10,
  1166. fill: false,
  1167. backgroundColor: chartColor,
  1168. borderColor: chartColor,
  1169. hitRadius: 10,
  1170. // barPercentage: 0.5,
  1171. // categoryPercentage: 0.5
  1172. }]
  1173. },
  1174. options: {
  1175. responsive: true, // 啟用響應式
  1176. maintainAspectRatio: false,
  1177. plugins: {
  1178. datalabels: datalabelsConfig,
  1179. backgroundColor: 'transparent',
  1180. // datalabels: {
  1181. // formatter: function (value, context) {
  1182. // // 添加单位
  1183. // return value;
  1184. // },
  1185. // textStrokeColor: '#fff',
  1186. // textStrokeWidth: 5,
  1187. // color: '#584B3D',
  1188. // font: {
  1189. // size: YfontSizeValue,
  1190. // },
  1191. // anchor: 'end',
  1192. // align: 'end',
  1193. // },
  1194. // datalabels: null,
  1195. // datalabels: data.length <= 10 ? {
  1196. // formatter: function (value, context) {
  1197. // // 添加单位
  1198. // return value + ' ' + unit;
  1199. // },
  1200. // textStrokeColor: '#fff',
  1201. // textStrokeWidth: 5,
  1202. // color: '#584B3D',
  1203. // font: {
  1204. // size: YfontSizeValue,
  1205. // },
  1206. // anchor: 'end',
  1207. // align: 'end',
  1208. // } : null,
  1209. // customLabels: null,
  1210. legend: {
  1211. display: false,
  1212. fullSize: true,
  1213. position: 'bottom',
  1214. onClick: function (e, legendItem, legend) {
  1215. e.native.stopImmediatePropagation();
  1216. // 獲取被點擊的數據集索引
  1217. selectedDatasetIndex = legendItem.datasetIndex;
  1218. const dataset_color = legend.chart.data.datasets[selectedDatasetIndex].borderColor;
  1219. console.log(dataset_color)
  1220. colorInput.value = dataset_color;
  1221. // // 手動觸發 Chart.js 的默認圖例點擊事件
  1222. // const defaultClickHandler = Chart.defaults.plugins.legend.onClick;
  1223. // defaultClickHandler.call(this, e, legendItem, legend);
  1224. }
  1225. },
  1226. },
  1227. scales: {
  1228. x: {
  1229. display: displayXaxes, // 显示 x 轴
  1230. grid: {
  1231. display: false,
  1232. lineWidth: 5,
  1233. color: '#584B3D'
  1234. },
  1235. ticks: {
  1236. textStrokeColor: '#fff',
  1237. textStrokeWidth: 5,
  1238. font: {
  1239. display: false,
  1240. family: 'Arial', // 设置字体
  1241. size: XfontSizeValue, // 设置字体大小
  1242. weight: 'bold', // 设置字体粗细
  1243. fontColor: 'rgba(255,255,255,0.8)', // X-axis font color
  1244. shadowColor: 'rgba(0, 0, 0, 0.5)', // Shadow color
  1245. shadowBlur: 10, // Shadow blur level
  1246. shadowOffsetX: 5, // Horizontal shadow offset
  1247. shadowOffsetY: 5
  1248. },
  1249. },
  1250. },
  1251. y: {
  1252. display: true,
  1253. beginAtZero: false,
  1254. grid: {
  1255. grid: {
  1256. display: true, // 显示 y 轴网格线
  1257. lineWidth: 1, // 网格线宽度
  1258. color: '#C0C5CC' // 网格线颜色
  1259. },
  1260. },
  1261. ticks: {
  1262. padding: 10,
  1263. textStrokeColor: '#fff',
  1264. textStrokeWidth: 5,
  1265. font: {
  1266. family: 'Arial', // 設置字體
  1267. size: XfontSizeValue, // 設置字體大小
  1268. weight: 'bold', // 設置字體粗細
  1269. color: 'rgba(255,255,255,0.8)', // Y-axis 字體顏色
  1270. shadowColor: 'rgba(0, 0, 0, 0.5)', // 陰影顏色
  1271. shadowBlur: 10, // 陰影模糊級別
  1272. shadowOffsetX: 5, // 水平陰影偏移
  1273. shadowOffsetY: 5 // 垂直陰影偏移
  1274. },
  1275. }
  1276. }
  1277. },
  1278. layout: {
  1279. padding: {
  1280. left: 50, // 调整图表左边距
  1281. right: 50, // 调整图表右边距
  1282. top: 40,
  1283. bottom: 10,
  1284. }
  1285. },
  1286. onClick: function (event, elements) {
  1287. if (elements.length > 0) {
  1288. const element = elements[0];
  1289. selectedDatasetIndex = element.datasetIndex;
  1290. const currentColor = myChart.data.datasets[selectedDatasetIndex].borderColor;
  1291. const colorValue = currentColor;
  1292. colorInput.value = colorValue;
  1293. // 获取当前线条颜色并更新 colorPicker
  1294. // colorInput.value = rgbToHex(currentColor);
  1295. // console.log('當前顏色', colorValue);
  1296. colorInput.disabled = false; // 启用 colorPicker
  1297. }
  1298. },
  1299. },
  1300. });
  1301. console.log('第一次', myChart.options.legend)
  1302. console.log(chart_bgimg_url);
  1303. bgImgelement.style.backgroundImage = chart_bgimg_url;
  1304. chartWhitelement.style.background = "rgba(255,255,255,0.5)";
  1305. downloadButton.style.display = "inline-block";
  1306. chartTypeBtn.style.display = "inline-block";
  1307. chartTypeBtn.style.opacity = "1";
  1308. compare_box.style.display = "flex";
  1309. }
  1310. // createChart(chartType);
  1311. // createChart(chartType)
  1312. const ChartOptions = document.querySelectorAll('input[name="ChartOptions"]');
  1313. ChartOptions.forEach(button => {
  1314. button.addEventListener('click', function () {
  1315. // console.log(this.value);
  1316. chartType = this.value;
  1317. console.log(chartType);
  1318. if (myChart !== null) {
  1319. myChart.destroy();
  1320. data = [];
  1321. labels = [];
  1322. }
  1323. if (chartType === 'line') {
  1324. $('#chartdiv').hide();
  1325. generateChart(dataArray);
  1326. } else {
  1327. $('#textToChart').hide();
  1328. extractAndGenerateChart(dataArray);
  1329. }
  1330. // createChart(chartType, data, labels);
  1331. });
  1332. });
  1333. // 表格字體
  1334. var fontSizeInput = document.getElementById('table_fontSize');
  1335. // 添加事件监听器
  1336. fontSizeInput.addEventListener('input', function () {
  1337. // 获取输入框的值
  1338. var fontSize = fontSizeInput.value;
  1339. // 获取动态创建的表格元素
  1340. var dynamicTable = document.querySelector('.dynamic-table');
  1341. // 如果表格存在,则调整其字体大小
  1342. if (dynamicTable) {
  1343. dynamicTable.style.fontSize = fontSize + 'px';
  1344. }
  1345. });
  1346. // 圓餅圖字體
  1347. var pieFontSizeInput = document.getElementById('pie_fontSize');
  1348. // 添加事件监听器
  1349. pieFontSizeInput.addEventListener('input', function () {
  1350. // 获取输入框的值
  1351. pieFontSize = pieFontSizeInput.value;
  1352. console.log('圓餅圖字體', pieFontSize);
  1353. if (pieFontSize) {
  1354. generatePieChart(dataArray, pieFontSize, PieMaxWidth);
  1355. } else {
  1356. alert("Please enter a valid font size.");
  1357. }
  1358. // 如果表格存在,则调整其字体大小
  1359. // if (dynamicTable) {
  1360. // dynamicTable.style.fontSize = fontSize + 'px';
  1361. // }
  1362. });
  1363. document.getElementById('PieMaxWidthRange').addEventListener('input', function () {
  1364. PieMaxWidth = Number(document.getElementById('PieMaxWidthRange').value);
  1365. console.log('圓餅圖', PieMaxWidth)
  1366. generatePieChart(dataArray, pieFontSize, PieMaxWidth);
  1367. });
  1368. // 是否顯示point
  1369. var togglePoints = document.getElementById('togglePoints');
  1370. togglePoints.addEventListener('change', function () {
  1371. console.log('checked', "change")
  1372. // 遍历所有数据集,应用相应的pointRadius和pointHoverRadius
  1373. myChart.data.datasets.forEach(function (dataset) {
  1374. if (togglePoints.checked) {
  1375. dataset.pointRadius = 5;
  1376. dataset.pointHoverRadius = 8;
  1377. } else {
  1378. dataset.pointRadius = 0;
  1379. dataset.pointHoverRadius = 0;
  1380. }
  1381. });
  1382. myChart.update();
  1383. });
  1384. // 是否顯示數值
  1385. var toggleValue = document.getElementById('toggleValue');
  1386. let datalabelsConfig = getDatalabelsConfig(toggleValue.checked);
  1387. toggleValue.addEventListener('change', function () {
  1388. console.log('切換數值')
  1389. // 根据复选框状态更新 datalabelsConfig
  1390. datalabelsConfig = getDatalabelsConfig(this.checked);
  1391. // 更新图表
  1392. myChart.options.plugins.datalabels = datalabelsConfig;
  1393. myChart.update();
  1394. });
  1395. // 是否顯示x軸
  1396. const toggleXAxis = document.getElementById('toggleXAxis');
  1397. toggleXAxis.addEventListener('change', function () {
  1398. // 根据复选框的状态来显示或隐藏 X 轴
  1399. myChart.options.scales.x.ticks.color = this.checked ? 'rgba(88,75,61,1)' : 'rgba(88,75,61,0)';
  1400. myChart.options.scales.x.ticks.textStrokeColor = this.checked ? 'rgba(255, 255, 255, 1)' : 'rgba(255, 255, 255, 0)';
  1401. // 更新图表
  1402. myChart.update();
  1403. });
  1404. // 是否顯示單位
  1405. var toggleUnit = document.getElementById('toggleUnit');
  1406. toggleUnit.addEventListener('change', function () {
  1407. if (this.checked) {
  1408. unit_value_box.style.display = 'block';
  1409. } else {
  1410. unit_value_box.style.display = 'none';
  1411. }
  1412. });
  1413. // 單位
  1414. // =========================
  1415. var unitInput = document.getElementById('unit_data');
  1416. var unit_value = document.querySelector('.unit');
  1417. var unit_value_box = document.querySelector('#unit_box');
  1418. // console.log('單位', unitInput)
  1419. unitInput.addEventListener('input', function () {
  1420. // unitInput.value = unit_value.textContent;
  1421. if (chartType === 'table') {
  1422. // unit_table_value.textContent = this.value;
  1423. unit_table_value.forEach(span => {
  1424. span.textContent = this.value;
  1425. });
  1426. } else {
  1427. unit_value.textContent = this.value;
  1428. }
  1429. });
  1430. // ==========================
  1431. // y軸字體大小-折線圖
  1432. // ==========================
  1433. var YfontSize = document.getElementById('YfontSize');
  1434. YfontSize.addEventListener('input', function () {
  1435. YfontSizeValue = YfontSize.value;
  1436. const newSize = parseInt(YfontSizeValue);
  1437. // 更新 datalabelsConfig 的字体大小
  1438. myChart.options.plugins.datalabels.font.size = newSize;
  1439. // 更新图表
  1440. myChart.update();
  1441. // createChart(chartType, data, labels);
  1442. });
  1443. // ==========================
  1444. // x軸字體大小-折線圖
  1445. // ==========================
  1446. var XfontSize = document.getElementById('XfontSize');
  1447. XfontSize.addEventListener('input', function () {
  1448. XfontSizeValue = XfontSize.value;
  1449. // console.log(XfontSizeValue)
  1450. // if (myChart) {
  1451. // myChart.destroy();
  1452. // }
  1453. // 修改X轴标签字体大小
  1454. myChart.options.scales.x.ticks.font.size = parseInt(XfontSizeValue);
  1455. myChart.options.scales.y.ticks.font.size = parseInt(XfontSizeValue);
  1456. myChart.update();
  1457. // createChart(chartType, data, labels);
  1458. });
  1459. // ==========================
  1460. // x軸字體大小-柱狀圖
  1461. // ==========================
  1462. var XfontSizeBar = document.getElementById('XfontSizeBar');
  1463. XfontSizeBar.addEventListener('input', function () {
  1464. extractAndGenerateChart(dataArray);
  1465. });
  1466. // ==========================
  1467. // y軸字體大小-柱狀圖
  1468. // ==========================
  1469. var YfontSizeBar = document.getElementById('YfontSizeBar');
  1470. YfontSizeBar.addEventListener('input', function () {
  1471. extractAndGenerateChart(dataArray);
  1472. });
  1473. // ==========================
  1474. // 線條顏色-柱狀圖
  1475. // =========================
  1476. const colorInputBar = document.getElementById('borderColorInputBar');
  1477. // 添加事件监听器,当颜色选择发生变化时触发
  1478. colorInputBar.addEventListener('input', function () {
  1479. extractAndGenerateChart(dataArray);
  1480. });
  1481. // =========================
  1482. // 線條粗細
  1483. // =========================
  1484. function updateLineWidth(borderWidthValue) {
  1485. myChart.data.datasets.forEach(dataset => {
  1486. dataset.borderWidth = parseFloat(borderWidthValue);
  1487. });
  1488. myChart.update();
  1489. }
  1490. var rangeInput = document.getElementById('borderWidthRange');
  1491. // var rangeInputSpan = document.getElementById('borderWidthRangeValue');
  1492. var borderWidthValue;
  1493. // rangeInputSpan.textContent = rangeInput.value;
  1494. // 添加事件监听器,当滑动条的值发生改变时更新显示的值
  1495. rangeInput.addEventListener('input', function () {
  1496. // 获取滑动条的当前值
  1497. borderWidthValue = rangeInput.value;
  1498. console.log(borderWidthValue);
  1499. // rangeInputSpan.textContent = borderWidthValue;
  1500. updateLineWidth(borderWidthValue);
  1501. // myChart.data.datasets[0].borderWidth = borderWidthValue;
  1502. });
  1503. // =========================
  1504. // 線條顏色-折線圖
  1505. // =========================
  1506. const colorInput = document.getElementById('borderColorInput');
  1507. let selectedDatasetIndex = null;
  1508. // 添加事件监听器,当颜色选择发生变化时触发
  1509. colorInput.addEventListener('input', function () {
  1510. // 获取当前选择的颜色值
  1511. // chartColor = colorInput.value;
  1512. // if (myChart) {
  1513. // myChart.destroy();
  1514. // }
  1515. if (selectedDatasetIndex !== null) {
  1516. const newColor = colorInput.value;
  1517. myChart.data.datasets[selectedDatasetIndex].borderColor = newColor;
  1518. myChart.data.datasets[selectedDatasetIndex].backgroundColor = newColor;
  1519. myChart.update(); // 更新图表
  1520. // createChart(chartType, data, labels);
  1521. }
  1522. // console.log(chartColor);
  1523. // createChart(chartType, data, labels);
  1524. });
  1525. function rgbToHex(rgb) {
  1526. const rgbArr = rgb.match(/\d+/g);
  1527. return `#${((1 << 24) + (parseInt(rgbArr[0]) << 16) + (parseInt(rgbArr[1]) << 8) + parseInt(rgbArr[2])).toString(16).slice(1)}`;
  1528. }
  1529. function hexToRgba(hex, alpha = 1) {
  1530. // 移除 HEX 颜色值的 # 符号
  1531. hex = hex.replace(/^#/, '');
  1532. // 处理 3 位 HEX 颜色值(如 #fff)
  1533. if (hex.length === 3) {
  1534. hex = hex.split('').map(function (hex) {
  1535. return hex + hex;
  1536. }).join('');
  1537. }
  1538. // 解析 HEX 颜色值
  1539. const r = parseInt(hex.substring(0, 2), 16);
  1540. const g = parseInt(hex.substring(2, 4), 16);
  1541. const b = parseInt(hex.substring(4, 6), 16);
  1542. // 返回 RGBA 颜色值
  1543. return `rgba(${r}, ${g}, ${b}, ${alpha})`;
  1544. }
  1545. // =========================
  1546. // 模板樣式
  1547. // =========================
  1548. // JavaScript
  1549. document.addEventListener("DOMContentLoaded", function () {
  1550. // 獲取所有帶有 templateImg class 的圖像元素
  1551. var templateImgs = document.querySelectorAll('.templateImg');
  1552. // 為每個圖像元素添加點擊事件監聽器
  1553. templateImgs.forEach(function (img) {
  1554. img.addEventListener('click', function () {
  1555. // 在點擊時印出圖像元素的 value 屬性值
  1556. console.log(this.getAttribute('value'));
  1557. });
  1558. });
  1559. });
  1560. // =========================
  1561. var bgImgelement = document.getElementById('data_chart_box');
  1562. var chartWhitelement = document.getElementById('data_chartJs_box');
  1563. // 背景樣式
  1564. // =========================
  1565. // JavaScript
  1566. document.addEventListener("DOMContentLoaded", function () {
  1567. // 獲取所有帶有 templateImg class 的圖像元素
  1568. var bgImgs = document.querySelectorAll('.bgImg');
  1569. // 為每個圖像元素添加點擊事件監聽器
  1570. bgImgs.forEach(function (img) {
  1571. img.addEventListener('click', function () {
  1572. // 在點擊時印出圖像元素的 value 屬性值
  1573. console.log(this.getAttribute('value'));
  1574. var bgImgUrl = this.getAttribute('value');
  1575. chart_bgimg_url = 'url(' + bgImgUrl + ')';
  1576. if (bgImgUrl === './img/bg07.webp') {
  1577. chartTitle.style.color = "#ffff";
  1578. chartTitle.style.padding = '0px'
  1579. }
  1580. else {
  1581. chartTitle.style.color = "#1c2d6d";
  1582. }
  1583. if (bgImgUrl === "") {
  1584. chartWhitelement.style.background = "";
  1585. console.log('無背景');
  1586. bgImgelement.style.backgroundImage = "";
  1587. chartWhitelement.style.background = 'rgba(255, 255, 255, 0) !important;'
  1588. } else {
  1589. bgImgelement.style.backgroundImage = chart_bgimg_url;
  1590. chartWhitelement.style.background = "rgba(255, 255, 255, 0.5)"
  1591. }
  1592. // bgImgelement.style.backgroundSize = 'cover'; // 調整背景圖片大小
  1593. // bgImgelement.style.backgroundPosition = 'center'; // 調整背景圖片位置
  1594. // bgImgelement.style.backgroundRepeat = 'no-repeat'; // 調整背景圖片重複
  1595. });
  1596. });
  1597. });
  1598. // =========================
  1599. const radioButtons = document.querySelectorAll('input[name="inlineRadioOptions"]');
  1600. var chartTitle = document.querySelector('.chart_title');
  1601. radioButtons.forEach(button => {
  1602. button.addEventListener('click', function () {
  1603. if (this.value === '0') {
  1604. chartTitle.style.textAlign = 'left';
  1605. } else if (this.value === '1') {
  1606. chartTitle.style.textAlign = 'center';
  1607. } else if (this.value === '2') {
  1608. chartTitle.style.textAlign = 'right';
  1609. }
  1610. });
  1611. });
  1612. // 圖表標題
  1613. var keywordInput = document.getElementById('title_data');
  1614. // 初始时将输入框的值设置为标题的文本内容
  1615. keywordInput.value = chartTitle.textContent;
  1616. // 监听输入框的输入事件,实现内容同步更新
  1617. keywordInput.addEventListener('input', function () {
  1618. chartTitle.textContent = this.value;
  1619. });
  1620. // 標題字體大小
  1621. // ==========================
  1622. var TitlefontSize = document.getElementById('TitlefontSize');
  1623. TitlefontSize.addEventListener('input', function () {
  1624. var TitlefontSizeValue = TitlefontSize.value;
  1625. chartTitle.style.fontSize = TitlefontSizeValue + 'px';;
  1626. });
  1627. // 標題字體顏色
  1628. var stocknumber;
  1629. // ==========================
  1630. let labelDependent = ""; // 圖表左側 Label
  1631. function get_data(input_text_value) {
  1632. clickExample.style.display = 'none'
  1633. // 重新创建取消令牌和源
  1634. cancelToken = axios.CancelToken;
  1635. source = cancelToken.source();
  1636. // inputField.value = "";
  1637. apiLoading();
  1638. var existingTable = chartJsBox.querySelector('table');
  1639. downloadButton.style.display = "none";
  1640. if (chartDiv.childElementCount != 0) {
  1641. data = [];
  1642. labels = [];
  1643. console.log('已存在')
  1644. chartTitle.textContent = "";
  1645. unit_value.textContent = "";
  1646. bgImgelement.style.backgroundImage = "";
  1647. chartWhitelement.style.background = "";
  1648. document.getElementById('img_box_url').src = "";
  1649. }
  1650. if (existingTable) {
  1651. unit_value.textContent = "";
  1652. chartJsBox.removeChild(existingTable);
  1653. chartTitle.textContent = "";
  1654. bgImgelement.style.backgroundImage = "";
  1655. chartWhitelement.style.background = "";
  1656. document.getElementById('img_box_url').src = "";
  1657. }
  1658. if (myChart) {
  1659. unit_value_box.style.display = 'none';
  1660. unit_value.textContent = "";
  1661. data = [];
  1662. labels = [];
  1663. chartTitle.textContent = "";
  1664. bgImgelement.style.backgroundImage = "";
  1665. chartWhitelement.style.background = "";
  1666. myChart.destroy();
  1667. document.getElementById('img_box_url').src = "";
  1668. }
  1669. const apiUrl = `https://cmm.ai:8080/answer_with_token?token=${accessToken}&question=${input_text_value}`;
  1670. axios.get(apiUrl, {
  1671. cancelToken: source.token // 将取消令牌传递给 Axios 请求
  1672. })
  1673. .then(response => {
  1674. apiHideLoading();
  1675. console.log(response);
  1676. // console.log(response.data.chart_info === undefined);
  1677. if (response.data.chart_info === undefined) {
  1678. console.log(response.data.data)
  1679. alert(response.data.data)
  1680. return
  1681. } else {
  1682. unit_value_box.style.display = "block";
  1683. var chart_info = response.data.chart_info.Title;
  1684. chartType = response.data.chart_info.Chart_type;
  1685. var finance_img_url = response.data.imageUrl_info.imageUrl;
  1686. if (response.data.imageUrl_info != null) {
  1687. console.log(finance_img_url);
  1688. document.getElementById('img_box_url').src = finance_img_url;
  1689. }
  1690. keywordInput.value = chart_info;
  1691. chartTitle.textContent = chart_info;
  1692. unit = response.data.chart_info.Unit_of_dependent_variable;
  1693. var Label_dependent_variable = response.data.chart_info.Label_dependent_variable;
  1694. var Label_independent_variable = response.data.chart_info.Label_independent_variable;
  1695. stocknumber = response.data.chart_info.args;
  1696. chartTypeBtn.value = chartType;
  1697. unit_data.value = response.data.chart_info.Unit_of_dependent_variable;
  1698. if (response.data.chart_info.Unit_of_dependent_variable == "") {
  1699. unit_value.textContent = ""
  1700. } else {
  1701. unit_value.textContent = response.data.chart_info.Unit_of_dependent_variable;
  1702. }
  1703. obj.text = chartType;
  1704. dataArray = response.data.data;
  1705. if (chartType === "table") {
  1706. unit_value.textContent = "";
  1707. unit_value_box.style.display = "none"
  1708. createTable(dataArray, unit, Label_dependent_variable, Label_independent_variable);
  1709. // document.getElementById('img_box_url').src = "";
  1710. } else if (chartType === "bar") {
  1711. unit_value_box.style.display = 'block';
  1712. console.log('圖表類型bar')
  1713. $('#textToChart').hide();
  1714. extractAndGenerateChart(dataArray);
  1715. } else if (chartType === "doughnut") {
  1716. unit_value.textContent = "";
  1717. $('#textToChart').hide();
  1718. unit_value_box.style.display = 'none';
  1719. generatePieChart(dataArray, pieFontSize, PieMaxWidth)
  1720. } else {
  1721. unit_value_box.style.display = "block"
  1722. $('#chartdiv').hide();
  1723. generateChart(dataArray);
  1724. }
  1725. // }
  1726. // 在這裡處理成功獲取 JSON 的情況
  1727. // console.log(response);
  1728. // switch (chartType) {
  1729. // case "line":
  1730. // document.getElementById("inlineRadio4").checked = true; // 折线图
  1731. // break;
  1732. // case "bar":
  1733. // document.getElementById("inlineRadio5").checked = true; // 柱状图
  1734. // labelDependent = response.data.chart_info.Label_dependent_variable;
  1735. // break;
  1736. // case "pie":
  1737. // document.getElementById("inlineRadio6").checked = true; // 圆饼图
  1738. // break;
  1739. // case "table":
  1740. // document.getElementById("inlineRadio7").checked = true; // 表格
  1741. // break;
  1742. // default:
  1743. // // 默认情况
  1744. }
  1745. // generateChart(dataArray);
  1746. })
  1747. .catch(error => {
  1748. // 错误处理逻辑,包括取消请求的情况
  1749. apiHideLoading();
  1750. if (axios.isCancel(error)) {
  1751. console.log('请求被取消:', error.message);
  1752. } else {
  1753. console.error('发生错误:', error);
  1754. }
  1755. });
  1756. }
  1757. // 取消请求的函数
  1758. function cancelRequest() {
  1759. if (source) {
  1760. source.cancel('取消了 API 请求');
  1761. apiHideLoading(); // 隐藏加载状态
  1762. }
  1763. }
  1764. document.getElementById('cancel_send_data').addEventListener('click', cancelRequest);
  1765. $(document).ready(function () {
  1766. $('#dataTable').DataTable();
  1767. });
  1768. function resizeChart() {
  1769. console.log(myChart)
  1770. if (myChart != null) {
  1771. myChart.resize();
  1772. }
  1773. }
  1774. document.addEventListener('visibilitychange', function () {
  1775. console.log('Visibility changed:', document.visibilityState);
  1776. if (document.visibilityState === 'visible') {
  1777. resizeChart();
  1778. }
  1779. });
  1780. // 监听窗口resize事件
  1781. window.addEventListener('resize', resizeChart);
  1782. var setting_button = document.getElementById('chart_seeting_button');
  1783. var closeButton = document.getElementById('style_setting_close');
  1784. var closeButton_table = document.getElementById('style_setting_close_table');
  1785. // var borderSetting = document.getElementById('borderSetting');
  1786. var lineChartSetting = document.getElementById('lineChartSetting');
  1787. var barChartSetting = document.getElementById('barChartSetting');
  1788. var table_setting = document.getElementById('table_setting');
  1789. var chart_category = document.getElementById('chart_category');
  1790. var pie_setting = document.getElementById('pie_setting');
  1791. // 获取 chartDiv 元素
  1792. setting_button.addEventListener('click', () => {
  1793. console.log('setting_button')
  1794. if (myChart === null && !chartJsBox.querySelector('table') && chartDiv.childElementCount === 0) {
  1795. alert('請先生成圖表');
  1796. return
  1797. }
  1798. if (chartType === 'table') {
  1799. console.log('表格');
  1800. sidebar.classList.toggle('show');
  1801. sidebar.classList.toggle('hidden');
  1802. barChartSetting.style.display = 'none';
  1803. lineChartSetting.style.display = 'none';
  1804. table_setting.style.display = 'block';
  1805. chart_category.style.display = 'none';
  1806. pie_setting.style.display = 'none';
  1807. } else if (chartType === "bar") {
  1808. console.log('柱狀圖')
  1809. sidebar.classList.toggle('show');
  1810. sidebar.classList.toggle('hidden');
  1811. barChartSetting.style.display = 'block';
  1812. lineChartSetting.style.display = 'none';
  1813. table_setting.style.display = 'none';
  1814. pie_setting.style.display = 'none';
  1815. } else if (chartType === "doughnut") {
  1816. console.log('快捷功能圓餅圖')
  1817. sidebar.classList.toggle('show');
  1818. sidebar.classList.toggle('hidden');
  1819. barChartSetting.style.display = 'none';
  1820. lineChartSetting.style.display = 'none';
  1821. pie_setting.style.display = 'block';
  1822. table_setting.style.display = 'none';
  1823. chart_category.style.display = 'none';
  1824. document.getElementById('unit_input').style.display = 'none';
  1825. } else {
  1826. sidebar.classList.toggle('show');
  1827. sidebar.classList.toggle('hidden');
  1828. table_setting.style.display = 'none';
  1829. barChartSetting.style.display = 'none';
  1830. lineChartSetting.style.display = 'block';
  1831. pie_setting.style.display = 'none';
  1832. }
  1833. });
  1834. closeButton.addEventListener('click', () => {
  1835. sidebar.classList.remove('show');
  1836. sidebar.classList.add('hidden');
  1837. });
  1838. const finance_options = [
  1839. { name: "原油油桶", imageUrl: "./img/finance/01.webp" },
  1840. { name: "黃金", imageUrl: "./img/finance/02.webp" },
  1841. { name: "房屋", imageUrl: "./img/finance/03.webp" },
  1842. { name: "車", imageUrl: "./img/finance/04.webp" },
  1843. { name: "股市", imageUrl: "./img/finance/05.webp" },
  1844. { name: "晶圓", imageUrl: "./img/finance/06.webp" },
  1845. { name: "TSMC 台積電", imageUrl: "./img/finance/07.webp" },
  1846. { name: "台幣", imageUrl: "./img/finance/08.webp" },
  1847. { name: "美金", imageUrl: "./img/finance/09.webp" },
  1848. { name: "購物車", imageUrl: "./img/finance/10.webp" },
  1849. { name: "美中貿易", imageUrl: "./img/finance/11.webp" },
  1850. { name: "世界地圖", imageUrl: "./img/finance/12.webp" },
  1851. { name: "小麥", imageUrl: "./img/finance/13.webp" },
  1852. { name: "玉米", imageUrl: "./img/finance/14.webp" },
  1853. { name: "智慧產線", imageUrl: "./img/finance/15.webp" }
  1854. ];
  1855. const selectOptions = document.getElementById('finance_options');
  1856. const selectOptions_table = document.getElementById('finance_options_table');
  1857. finance_options.forEach((option, index) => {
  1858. const optionElement = document.createElement('option');
  1859. optionElement.value = index + 1; // 值从 1 开始
  1860. optionElement.textContent = option.name;
  1861. optionElement.setAttribute('data-image-url', option.imageUrl); // 存储图片 URL
  1862. selectOptions.appendChild(optionElement);
  1863. });
  1864. selectOptions.addEventListener('change', function () {
  1865. const selectedOption = selectOptions.options[selectOptions.selectedIndex];
  1866. const imageUrl = selectedOption.getAttribute('data-image-url');
  1867. document.getElementById('img_box_url').src = imageUrl;
  1868. });
  1869. // finance_options.forEach((option, index) => {
  1870. // const optionElement = document.createElement('option');
  1871. // optionElement.value = index + 1; // 值从 1 开始
  1872. // optionElement.textContent = option.name;
  1873. // optionElement.setAttribute('data-image-url', option.imageUrl); // 存储图片 URL
  1874. // selectOptions_table.appendChild(optionElement);
  1875. // });
  1876. // selectOptions_table.addEventListener('change', function () {
  1877. // const selectedOption = selectOptions_table.options[selectOptions_table.selectedIndex];
  1878. // const imageUrl = selectedOption.getAttribute('data-image-url');
  1879. // document.getElementById('img_box_url').src = imageUrl;
  1880. // });
  1881. document.querySelectorAll('input[name="ChartOptions"]').forEach(function (radio) {
  1882. radio.addEventListener('change', function () {
  1883. // 檢查哪個單選按鈕被選中
  1884. if (this.value === 'line') {
  1885. // 如果選擇了折線圖,執行相應的操作
  1886. console.log("選擇了折線圖");
  1887. // 在這裡執行顯示折線圖的相關代碼
  1888. } else if (this.value === 'doughnut') {
  1889. // 如果選擇了柱狀圖,執行相應的操作
  1890. console.log("選擇了柱狀圖");
  1891. // 在這裡執行顯示柱狀圖的相關代碼
  1892. }
  1893. });
  1894. });
  1895. function tokencheck() {
  1896. if (!localStorage.getItem("access_token")) {
  1897. window.location.href = "./login.html";
  1898. } else {
  1899. console.log("存在");
  1900. }
  1901. }
  1902. tokencheck();
  1903. function logout() {
  1904. localStorage.removeItem("access_token");
  1905. }
  1906. $(document).on("click", "#logout", function (event) {
  1907. alert("登出成功");
  1908. logout();
  1909. location.reload();
  1910. });
  1911. // speech_to_text
  1912. var closeRecord = document.getElementById('recording_block_close');
  1913. var recording_block = document.getElementById('recording_block');
  1914. var PageLM = "2024-05-07 18:33";
  1915. var recording_button = document.getElementById('recording_button');
  1916. var stopButton = document.getElementById('stop');
  1917. var audio = document.getElementById('audio');
  1918. var recorder, audioBlob;
  1919. closeRecord.addEventListener('click', () => {
  1920. recPause();
  1921. console.log('record-click')
  1922. recording_block.classList.remove('show');
  1923. recording_block.classList.add('hidden');
  1924. });
  1925. function reclog(s, color) {
  1926. var now = new Date();
  1927. var t = ("0" + now.getHours()).substr(-2)
  1928. + ":" + ("0" + now.getMinutes()).substr(-2)
  1929. + ":" + ("0" + now.getSeconds()).substr(-2);
  1930. var div = document.createElement("div");
  1931. var elem = document.querySelector(".reclog");
  1932. elem.insertBefore(div, elem.firstChild);
  1933. div.innerHTML = '<div style="color:' + (!color ? "" : color == 1 ? "red" : color == 2 ? "#0b1" : color) + '">[' + t + ']' + s + '</div>';
  1934. };
  1935. window.onerror = function (message, url, lineNo, columnNo, error) {
  1936. //https://www.cnblogs.com/xianyulaodi/p/6201829.html
  1937. reclog('<span style="color:red">【Uncaught Error】' + message + '<pre>' + "at:" + lineNo + ":" + columnNo + " url:" + url + "\n" + (error && error.stack || Html_$T("kBaF::不能获得错误堆栈")) + '</pre></span>');
  1938. };
  1939. if (!window.Html_$T) {//没有提供本页面用的国际化多语言支持时 返回中文文本
  1940. window.Html_$T = function () {
  1941. var a = arguments, txt = a[0].replace(/^.+?::/, ""), n = 0;
  1942. for (var i = 0; i < a.length; i++) { if (typeof a[i] == "number") { n = i; break } }
  1943. txt = txt.replace(/\{(\d+)\}/g, function (v, f) { v = a[+f + n]; return v == null ? "" : v });
  1944. return txt;
  1945. }
  1946. window.Html_xT = function (v) { return v }
  1947. }
  1948. if (window.Recorder) {
  1949. // reclog(Html_$T('BL9u::頁面已準備好,請先點擊打開錄音,然後點擊錄製'), 2);
  1950. alert('錄音已準備好,請先點擊右下角打開錄音,即可開始錄製')
  1951. } else {
  1952. reclog(Html_$T("YzPd::js文件加载失败,请刷新重试!"), "#f00;font-size:50px");
  1953. console.log('js文件加载失敗,请刷新重试!')
  1954. }
  1955. recording_button.addEventListener('click', async () => {
  1956. recording_block.classList.toggle('show');
  1957. recording_block.classList.toggle('hidden');
  1958. recStart();
  1959. });
  1960. function recStart() {//打开了录音后才能进行start、stop调用
  1961. rec.start();
  1962. };
  1963. stopButton.addEventListener('click', async () => {
  1964. // recorder.stop();
  1965. // uploadAudio(audioBlob);
  1966. recStop();
  1967. });
  1968. window.onload = function () {
  1969. recOpen();
  1970. };
  1971. // 錄音程式
  1972. // =======================
  1973. var rec, wave, recBlob;
  1974. /**调用open打开录音请求好录音权限 Call open to open the recording and request the recording permission**/
  1975. var recOpen = function () {//一般在显示出录音按钮或相关的录音界面时进行此方法调用,后面用户点击开始录音时就能畅通无阻了
  1976. rec = null;
  1977. wave = null;
  1978. recBlob = null;
  1979. var newRec = Recorder({
  1980. type: "mp3", sampleRate: 16000, bitRate: 16 //mp3格式,指定采样率hz、比特率kbps,其他参数使用默认配置;注意:是数字的参数必须提供数字,不要用字符串;需要使用的type类型,需提前把格式支持文件加载进来,比如使用wav格式需要提前加载wav.js编码引擎
  1981. , onProcess: function (buffers, powerLevel, bufferDuration, bufferSampleRate, newBufferIdx, asyncEnd) {
  1982. // // //录音实时回调,大约1秒调用12次本回调
  1983. document.querySelector(".recpowerx").style.width = powerLevel + "%";
  1984. document.querySelector(".recpowert").innerText = formatMs(bufferDuration, 1) + " / " + powerLevel;
  1985. // //可视化图形绘制
  1986. // wave.input(buffers[buffers.length - 1], powerLevel, bufferSampleRate);
  1987. }
  1988. });
  1989. newRec.open(function () {//打开麦克风授权获得相关资源
  1990. rec = newRec;
  1991. //此处创建这些音频可视化图形绘制浏览器支持妥妥的
  1992. // wave = Recorder.FrequencyHistogramView({ elem: ".recwave" });
  1993. reclog(Html_$T("GVCa::已打開錄音,可以點擊錄制開始錄音"), 2);
  1994. }, function (msg, isUserNotAllow) {//用户拒绝未授权或不支持
  1995. reclog((isUserNotAllow ? "UserNotAllow, " : "") + Html_$T("TOOV::打開錄音失敗:") + msg, 1);
  1996. });
  1997. };
  1998. /**关闭录音,释放资源 Close recording, release resources**/
  1999. function recClose() {
  2000. if (rec) {
  2001. rec.close();
  2002. reclog(Html_$T("jqOs::已关闭"));
  2003. } else {
  2004. reclog(Html_$T("VOOw::未打开录音"), 1);
  2005. };
  2006. };
  2007. /**开始录音 Start recording**/
  2008. function recStart() {//打开了录音后才能进行start、stop调用
  2009. if (rec && Recorder.IsOpen()) {
  2010. recBlob = null;
  2011. rec.start();
  2012. reclog(Html_$T("CGdy::已經開始錄音...") + " " + rec.set.type + " " + rec.set.sampleRate + " " + rec.set.bitRate + "kbps");
  2013. } else {
  2014. reclog(Html_$T("ajKR::未打开錄音"), 1);
  2015. };
  2016. };
  2017. /**暂停录音 Passing recording**/
  2018. function recPause() {
  2019. if (rec && Recorder.IsOpen()) {
  2020. rec.pause();
  2021. reclog(Html_$T("GvCy::已暂停"));
  2022. } else {
  2023. reclog(Html_$T("gCAR::未打開錄音"), 1);
  2024. };
  2025. };
  2026. /**恢复录音 Resume recording**/
  2027. function recResume() {
  2028. if (rec && Recorder.IsOpen()) {
  2029. rec.resume();
  2030. reclog(Html_$T("5q1K::继续录音中..."));
  2031. } else {
  2032. reclog(Html_$T("Ob6S::未打开录音"), 1);
  2033. };
  2034. };
  2035. /**结束录音,得到音频文件 Stop recording and get audio files**/
  2036. function recStop() {
  2037. if (!(rec && Recorder.IsOpen())) {
  2038. reclog(Html_$T("5JuL::未打開錄音"), 1);
  2039. return;
  2040. };
  2041. rec.stop(function (blob, duration) {
  2042. console.log(blob, (window.URL || webkitURL).createObjectURL(blob), Html_xT(Html_$T("gOix::時長:{1}ms", 0, duration)));
  2043. recBlob = blob;
  2044. var file = new File([blob], "recording.mp3", { type: "audio/mp3" });
  2045. // console.log(file);
  2046. handleAudioToText(file);
  2047. reclog(Html_$T("0LHf::已錄製mp3:{1}ms {2}字節,可以點擊播放、上傳、本地下载了", 0, formatMs(duration), blob.size), 2);
  2048. }, function (msg) {
  2049. reclog(Html_$T("kGZO::錄音失敗:") + msg, 1);
  2050. });
  2051. };
  2052. /**播放 Play**/
  2053. function recPlay() {
  2054. if (!recBlob) {
  2055. reclog(Html_$T("tIke::请先錄音,然后停止後再播放"), 1);
  2056. return;
  2057. };
  2058. var cls = ("a" + Math.random()).replace(".", "");
  2059. reclog(Html_$T('GlWb::播放中: ') + '<span class="' + cls + '"></span>');
  2060. var audio = document.createElement("audio");
  2061. audio.controls = true;
  2062. document.querySelector("." + cls).appendChild(audio);
  2063. //简单利用URL生成播放地址,注意不用了时需要revokeObjectURL,否则霸占内存
  2064. audio.src = (window.URL || webkitURL).createObjectURL(recBlob);
  2065. audio.play();
  2066. setTimeout(function () {
  2067. (window.URL || webkitURL).revokeObjectURL(audio.src);
  2068. }, 5000);
  2069. };
  2070. /**上传 Upload**/
  2071. function recUpload() {
  2072. var blob = recBlob;
  2073. if (!blob) {
  2074. reclog(Html_$T("DUTn::请先录音,然后停止后再上传"), 1);
  2075. return;
  2076. };
  2077. //本例子假设使用原始XMLHttpRequest请求方式,实际使用中自行调整为自己的请求方式
  2078. //录音结束时拿到了blob文件对象,可以用FileReader读取出内容,或者用FormData上传
  2079. var api = "http://127.0.0.1:9528";
  2080. var onreadystatechange = function (xhr, title) {
  2081. return function () {
  2082. if (xhr.readyState == 4) {
  2083. if (xhr.status == 200) {
  2084. reclog(title + Html_$T("G2MU::上传成功") + ' <span style="color:#999">response: ' + xhr.responseText + '</span>', 2);
  2085. } else {
  2086. reclog(title + Html_$T("TUdi::没有完成上传,演示上传地址无需关注上传结果,只要浏览器控制台内Network面板内看到的请求数据结构是预期的就ok了。"), "#d8c1a0");
  2087. console.error(Html_xT(title + Html_$T("HjDi::上传失败")), xhr.status, xhr.responseText);
  2088. };
  2089. };
  2090. };
  2091. };
  2092. reclog(Html_$T("QnSI::开始上传到{1},请稍候... (你可以先到源码 /assets/node-localServer 目录内执行 npm run start 来运行本地测试服务器)", 0, api));
  2093. /***方式一:将blob文件转成base64纯文本编码,使用普通application/x-www-form-urlencoded表单上传***/
  2094. var reader = new FileReader();
  2095. reader.onloadend = function () {
  2096. var postData = "";
  2097. postData += "mime=" + encodeURIComponent(blob.type);//告诉后端,这个录音是什么格式的,可能前后端都固定的mp3可以不用写
  2098. postData += "&upfile_b64=" + encodeURIComponent((/.+;\s*base64\s*,\s*(.+)$/i.exec(reader.result) || [])[1]) //录音文件内容,后端进行base64解码成二进制
  2099. //...其他表单参数
  2100. var xhr = new XMLHttpRequest();
  2101. xhr.open("POST", api + "/uploadBase64");
  2102. xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
  2103. xhr.onreadystatechange = onreadystatechange(xhr, Html_$T("gG1f::上传方式一【Base64】"));
  2104. xhr.send(postData);
  2105. };
  2106. reader.readAsDataURL(blob);
  2107. /***方式二:使用FormData用multipart/form-data表单上传文件***/
  2108. var form = new FormData();
  2109. form.append("upfile", blob, "recorder.mp3"); //和普通form表单并无二致,后端接收到upfile参数的文件,文件名为recorder.mp3
  2110. //...其他表单参数
  2111. var xhr = new XMLHttpRequest();
  2112. xhr.open("POST", api + "/upload");
  2113. xhr.onreadystatechange = onreadystatechange(xhr, Html_$T("vDzB::上传方式二【FormData】"));
  2114. xhr.send(form);
  2115. };
  2116. /**本地下载 Local download**/
  2117. function recLocalDown() {
  2118. if (!recBlob) {
  2119. reclog(Html_$T("M86h::请先录音,然后停止后再下载"), 1);
  2120. return;
  2121. };
  2122. var cls = ("a" + Math.random()).replace(".", "");
  2123. recdown64.lastCls = cls;
  2124. reclog(Html_$T('vJPl::点击 ') + '<span class="' + cls + '"></span>' + Html_$T('Whtc:: 下载,或复制文本')
  2125. + '<button onclick="recdown64(\'' + cls + '\')">' + Html_$T('XK4l::生成Base64文本') + '</button><span class="' + cls + '_b64"></span>');
  2126. var fileName = "recorder-" + Date.now() + ".mp3";
  2127. var downA = document.createElement("A");
  2128. downA.innerHTML = Html_$T("g8Fy::下载 ") + fileName;
  2129. downA.href = (window.URL || webkitURL).createObjectURL(recBlob);
  2130. downA.download = fileName;
  2131. document.querySelector("." + cls).appendChild(downA);
  2132. if (/mobile/i.test(navigator.userAgent)) {
  2133. alert(Html_xT(Html_$T("DIEK::因移动端绝大部分国产浏览器未适配Blob Url的下载,所以本demo代码在移动端未调用downA.click()。请尝试点击日志中显示的下载链接下载")));
  2134. } else {
  2135. downA.click();
  2136. }
  2137. //不用了时需要revokeObjectURL,否则霸占内存
  2138. //(window.URL||webkitURL).revokeObjectURL(downA.href);
  2139. };
  2140. function recdown64(cls) {
  2141. var el = document.querySelector("." + cls + "_b64");
  2142. if (recdown64.lastCls != cls) {
  2143. el.innerHTML = '<span style="color:red">' + Html_$T("eKKx::老的数据没有保存,只支持最新的一条") + '</span>';
  2144. return;
  2145. }
  2146. var reader = new FileReader();
  2147. reader.onloadend = function () {
  2148. el.innerHTML = '<textarea></textarea>';
  2149. el.querySelector("textarea").value = reader.result;
  2150. };
  2151. reader.readAsDataURL(recBlob);
  2152. };
  2153. var formatMs = function (ms, all) {
  2154. var ss = ms % 1000; ms = (ms - ss) / 1000;
  2155. var s = ms % 60; ms = (ms - s) / 60;
  2156. var m = ms % 60; ms = (ms - m) / 60;
  2157. var h = ms;
  2158. var t = (h ? h + ":" : "")
  2159. + (all || h + m ? ("0" + m).substr(-2) + ":" : "")
  2160. + (all || h + m + s ? ("0" + s).substr(-2) + "″" : "")
  2161. + ("00" + ss).substr(-3);
  2162. return t;
  2163. };
  2164. // =======================
  2165. let mediaRecorder;
  2166. let audioChunks = [];
  2167. var recordText = document.getElementById('record_text');
  2168. async function handleAudioToText(file) {
  2169. console.log('tts')
  2170. let audioLang = "cmn-Hant-TW"; // 音訊語言
  2171. // let lang = localStorage.getItem("lang");
  2172. let lang = "zh-tw";
  2173. console.log("lang", lang);
  2174. switch (lang) {
  2175. case "zh-tw":
  2176. audioLang = "cmn-Hant-TW";
  2177. break;
  2178. case "en-us":
  2179. audioLang = "en-US";
  2180. break;
  2181. case "ja-jp":
  2182. audioLang = "ja-JP";
  2183. break;
  2184. case "ko-kr":
  2185. audioLang = "ko-KR";
  2186. break;
  2187. default:
  2188. break;
  2189. }
  2190. let url = `https://cmm.ai:9001/gcp/speech-to-text?language_code=${audioLang}`;
  2191. const formData = new FormData();
  2192. formData.append("file", file);
  2193. try {
  2194. const response = await axios.post(url, formData);
  2195. console.log("response", response);
  2196. input_text_value = response.data;
  2197. recording_block.classList.remove('show');
  2198. recording_block.classList.add('hidden');
  2199. if (input_text_value.length === 0) {
  2200. alert('請再說一次')
  2201. return
  2202. }
  2203. console.log(input_text_value[0]);
  2204. inputField.value = input_text_value[0];
  2205. // Show the result for a short time before executing get_data
  2206. get_data(input_text_value);
  2207. // get_data(input_text_value);
  2208. } catch (error) {
  2209. console.log("error", error);
  2210. }
  2211. }
  2212. Object.defineProperty(obj, 'text', {
  2213. get() {
  2214. return this._text;
  2215. },
  2216. set(newValue) {
  2217. console.log(`字符串从 "${this._text}" 变为 "${newValue}"`);
  2218. this._text = newValue;
  2219. }
  2220. });
  2221. // sendButtonClickHandler(input_text_value);