text-to-chart.js 91 KB

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