text-to-chart.js 92 KB

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