text-to-chart.js 70 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193
  1. console.log('text-to-chart');
  2. var accessToken = localStorage.getItem("access_token");
  3. var usernameEmail = localStorage.getItem("username");
  4. let username = usernameEmail.split('@')[0];
  5. console.log(username);
  6. console.log(accessToken);
  7. Chart.register(ChartDataLabels);
  8. var userNameSpan = document.getElementById("userName");
  9. userNameSpan.textContent = username;
  10. var inputField = document.getElementById("keyword_data");
  11. var sendButton = document.getElementById("send_data");
  12. var canvasPng = document.getElementById("textToChart");
  13. var img_box = document.getElementById("img_box");
  14. var sidebar = document.getElementById('style_setting');
  15. var input_text_value;
  16. var chartType = 'line';
  17. var unit = '';
  18. var myChart = null;
  19. var chartColor = '#912B2B';
  20. var chartColorBar = '#'
  21. var displayXaxes = true;
  22. var ctx;
  23. var chartlabels;
  24. var chartdata;
  25. var XfontSizeValue = 18;
  26. var YfontSizeValue = 18;
  27. var TitlefontSizeValue = 32;
  28. var chart_bgimg_url = "url(./img/bg06.webp)";
  29. var vocab = [];
  30. var chartDiv = document.getElementById('chartdiv');
  31. document.addEventListener('DOMContentLoaded', function () {
  32. axios.get("https://cmm.ai:8080/vocab")
  33. .then(response => {
  34. // apiHideLoading();
  35. console.log(response);
  36. vocab = response.data;
  37. })
  38. .catch(error => {
  39. console.error('發生錯誤:', error);
  40. });
  41. });
  42. inputField.addEventListener('click', function () {
  43. // 確保輸入框處於焦點狀態
  44. inputField.focus();
  45. // 在控制台中顯示焦點狀態
  46. console.log('Input field focused');
  47. });
  48. document.addEventListener('DOMContentLoaded', function () {
  49. // const input = document.getElementById('stockInput');
  50. const suggestions = document.getElementById('suggestions');
  51. inputField.addEventListener('input', function () {
  52. const query = this.value.trim();
  53. suggestions.innerHTML = ''; // 清空之前的建議
  54. console.log('query', query);
  55. if (query) {
  56. // const filteredVocab = vocab.filter(item => item.toLowerCase().includes(query));
  57. const queryTerms = query.split(/\s+/); // 分割輸入的內容
  58. const lastQueryTerm = queryTerms[queryTerms.length - 1];
  59. const filteredVocab = vocab.filter(item => {
  60. // 對每個分割後的單詞進行匹配,只要其中有一個匹配成功就返回 true
  61. return item.toLowerCase().includes(lastQueryTerm.toLowerCase());
  62. });
  63. if (filteredVocab.length > 0) {
  64. suggestions.style.border = '1px solid #ccc';
  65. suggestions.style.display = 'block'; // 顯示建議容器
  66. } else {
  67. suggestions.style.border = 'none';
  68. suggestions.style.display = 'none'; // 隱藏建議容器
  69. }
  70. console.log(queryTerms[queryTerms.length - 1])
  71. filteredVocab.forEach(item => {
  72. const div = document.createElement('div');
  73. div.classList.add('autocomplete-suggestion');
  74. div.textContent = item;
  75. // div.addEventListener('click', function () {
  76. // inputField.value = item;
  77. // suggestions.innerHTML = ''; // 清空建議
  78. // suggestions.style.border = 'none';
  79. // suggestions.style.display = 'none'; // 隱藏建議容器
  80. // });
  81. console.log('queryTerms', queryTerms);
  82. // console.log('filteredVocab', filteredVocab);
  83. div.addEventListener('click', function () {
  84. queryTerms.forEach((term, index) => {
  85. const regex = new RegExp(term, 'gi');
  86. const match = regex.exec(inputField.value);
  87. // console.log(queryTerms[queryTerms.length - 1]);
  88. // console.log(filteredVocab, queryTerms[queryTerms.length - 1])
  89. if (index === queryTerms.length - 1) {
  90. if (match) {
  91. var start = match.index;
  92. var end = start + match[0].length;
  93. console.log(start, end)
  94. const textBefore = inputField.value.substring(0, start);
  95. const textAfter = inputField.value.substring(end, inputField.value.length);
  96. console.log('start', start, 'end', end, 'textBefore', textBefore, 'textAfter', textAfter);
  97. // 捕獲被選中的部分並替換它
  98. const selectedText = inputField.value.substring(start, end);
  99. console.log('selectedText', selectedText);
  100. const newText = textBefore + item + textAfter.replace(selectedText, '');
  101. console.log('newText', newText);
  102. inputField.value = newText;
  103. // inputField.value = textBefore + item.replace(selectedText, '') + textAfter;
  104. suggestions.innerHTML = ''; // 清空建議
  105. suggestions.style.border = 'none';
  106. }
  107. }
  108. });
  109. // const start = inputField.selectionStart;
  110. // const end = inputField.selectionEnd;
  111. });
  112. suggestions.appendChild(div);
  113. });
  114. } else {
  115. suggestions.style.border = 'none';
  116. suggestions.style.display = 'none'; // 隱藏建議容器
  117. }
  118. });
  119. });
  120. // 監聽輸入框的鍵盤事件
  121. // document.getElementById("keyword_data").addEventListener("keyup", function (event) {
  122. // // 判斷是否按下 Enter 鍵 (key code: 13)
  123. // if (event.keyCode === 13) {
  124. // // 觸發送出按鈕的點擊事件
  125. // document.getElementById("send_data").click();
  126. // }
  127. // });
  128. // 定義按鈕點擊事件處理函數
  129. function sendButtonClickHandler() {
  130. console.log(inputField.value);
  131. var input_text_value = inputField.value;
  132. $('#chartdiv').hide();
  133. sidebar.classList.remove('show');
  134. sidebar.classList.add('hidden');
  135. // 在這裡添加你希望在按下按鈕時執行的其他代碼
  136. get_data(input_text_value);
  137. }
  138. sendButton.addEventListener("click", sendButtonClickHandler);
  139. // sendButton.addEventListener("click", function () {
  140. // console.log(inputField.value);
  141. // input_text_value = inputField.value;
  142. // // if (myChart) {
  143. // // console.log('已存在')
  144. // // data = [];
  145. // // labels = [];
  146. // // myChart.removePlugin(Chart.pluginService.getPlugin('afterDraw'));
  147. // // myChart.destroy();
  148. // // }
  149. // get_data(input_text_value);
  150. // });
  151. var lastKeyPressTime = 0;
  152. var enterCount = 0;
  153. var ENTER_THRESHOLD = 500; // 設定連續按下 Enter 的時間閾值(毫秒)
  154. inputField.addEventListener("keyup", function (event) {
  155. // 判斷是否按下 Enter 鍵 (key code: 13)
  156. if (event.key === "Enter") {
  157. var currentTime = new Date().getTime();
  158. // 計算與上一次按鍵按下時間的差值
  159. var timeDiff = currentTime - lastKeyPressTime;
  160. lastKeyPressTime = currentTime;
  161. // 如果兩次 Enter 鍵按下的時間差小於閾值,則增加計數器
  162. if (timeDiff <= ENTER_THRESHOLD) {
  163. enterCount++;
  164. // 如果計數器為2,則觸發 API 請求並重置計數器
  165. if (enterCount === 2) {
  166. sendButtonClickHandler();
  167. enterCount = 0;
  168. }
  169. } else {
  170. // 如果時間差大於閾值,則重置計數器
  171. enterCount = 0;
  172. }
  173. } else {
  174. // 如果按下的不是 Enter 鍵,則重置計數器
  175. enterCount = 0;
  176. }
  177. });
  178. function number_format(number, decimals, dec_point, thousands_sep) {
  179. // * example: number_format(1234.56, 2, ',', ' ');
  180. // * return: '1 234,56'
  181. number = (number + '').replace(',', '').replace(' ', '');
  182. var n = !isFinite(+number) ? 0 : +number,
  183. prec = !isFinite(+decimals) ? 0 : Math.abs(decimals),
  184. sep = (typeof thousands_sep === 'undefined') ? ',' : thousands_sep,
  185. dec = (typeof dec_point === 'undefined') ? '.' : dec_point,
  186. s = '',
  187. toFixedFix = function (n, prec) {
  188. var k = Math.pow(10, prec);
  189. return '' + Math.round(n * k) / k;
  190. };
  191. // Fix for IE parseFloat(0.55).toFixed(0) = 0;
  192. s = (prec ? toFixedFix(n, prec) : '' + Math.round(n)).split('.');
  193. if (s[0].length > 3) {
  194. s[0] = s[0].replace(/\B(?=(?:\d{3})+(?!\d))/g, sep);
  195. }
  196. if ((s[1] || '').length < prec) {
  197. s[1] = s[1] || '';
  198. s[1] += new Array(prec - s[1].length + 1).join('0');
  199. }
  200. return s.join(dec);
  201. }
  202. var labels = [];
  203. var data = [];
  204. function apiLoading() {
  205. document.getElementById('chartDataLoading').style.display = 'block';
  206. }
  207. function apiHideLoading() {
  208. document.getElementById('chartDataLoading').style.display = 'none';
  209. }
  210. const DISPLAY = true;
  211. const BORDER = true;
  212. const CHART_AREA = true;
  213. const TICKS = true;
  214. var labels = [];
  215. var data = [];
  216. var dataArray;
  217. function generateChart(dataArray) {
  218. // 创建一个空数组来存储 labels 和 data
  219. console.log(dataArray)
  220. // 遍历数据数组
  221. for (var i = 0; i < dataArray.length; i++) {
  222. var item = dataArray[i];
  223. // 遍历当前项的属性
  224. for (var key in item) {
  225. // 如果属性名不在 labels 数组中,并且属性值不是对象,则将属性名添加到 labels 数组中
  226. if (typeof item[key] === 'string') {
  227. labels.push(item[key]);
  228. }
  229. // 如果属性值是数值类型,则将其添加到 data 数组中
  230. if (typeof item[key] === 'number') {
  231. data.push(item[key]);
  232. }
  233. }
  234. }
  235. console.log(data);
  236. if (myChart) {
  237. console.log('已存在')
  238. myChart.destroy();
  239. }
  240. if (chartType === "doughnut") {
  241. // createChartDoughnut(chartType, data, labels);
  242. // createChartDoughnut2(data, labels)
  243. } else {
  244. createChart(chartType, data, labels);
  245. }
  246. // console.log(data);
  247. console.log(labels);
  248. }
  249. var amChartInstance;
  250. function extractAndGenerateChart(dataArray) {
  251. for (var i = 0; i < dataArray.length; i++) {
  252. var item = dataArray[i];
  253. // 遍历当前项的属性
  254. for (var key in item) {
  255. // 如果属性名不在 labels 数组中,并且属性值不是对象,则将属性名添加到 labels 数组中
  256. if (typeof item[key] === 'string') {
  257. labels.push(item[key]);
  258. }
  259. // 如果属性值是数值类型,则将其添加到 data 数组中
  260. if (typeof item[key] === 'number') {
  261. data.push(item[key]);
  262. }
  263. }
  264. }
  265. // console.log('key', key);
  266. generateBarChart(key, dataArray);
  267. }
  268. // 圓餅圖
  269. function generatePieChart(dataArray) {
  270. $('#chartdiv').show();
  271. bgImgelement.style.backgroundImage = chart_bgimg_url;
  272. // chartDiv.style.width = "100%"
  273. chartWhitelement.style.background = "rgba(255,255,255,0.5)"
  274. // Themes begin
  275. am4core.useTheme(am4themes_animated);
  276. // Themes end
  277. // Create chart instance
  278. var chart = am4core.create("chartdiv", am4charts.PieChart);
  279. chart.data = dataArray;
  280. chart.innerRadius = am4core.percent(50);
  281. // Add and configure Series
  282. var pieSeries = chart.series.push(new am4charts.PieSeries());
  283. pieSeries.dataFields.value = "y";
  284. pieSeries.dataFields.category = "x";
  285. pieSeries.slices.template.stroke = am4core.color("#fff");
  286. pieSeries.slices.template.strokeWidth = 2;
  287. pieSeries.slices.template.strokeOpacity = 1;
  288. // Configure labels
  289. pieSeries.labels.template.wrap = true;
  290. pieSeries.labels.template.maxWidth = 100;
  291. pieSeries.labels.template.truncate = false;
  292. pieSeries.labels.template.fontSize = 14;
  293. // Configure label text to wrap and show percentages
  294. pieSeries.labels.template.adapter.add("textOutput", function (text, target) {
  295. if (target.dataItem && target.dataItem.values.value.percent) {
  296. return "[font-size: 14px]" + target.dataItem.category + ": " + target.dataItem.values.value.percent.toFixed(1) + "%";
  297. }
  298. return text;
  299. });
  300. pieSeries.colors.list = [
  301. am4core.color('rgb(171, 51, 49)'),
  302. am4core.color('rgb(34, 83, 149)'),
  303. am4core.color('rgb(79, 148, 65)'),
  304. am4core.color('rgb(217, 195, 105)'),
  305. am4core.color('rgb(142, 124, 180)'),
  306. am4core.color('rgb(211, 183, 144)'),
  307. am4core.color('rgb(83, 84, 84)'),
  308. am4core.color('rgb(229, 147, 152)')
  309. ];
  310. // Ensure labels have background
  311. // pieSeries.labels.template.background.fillOpacity = 1;
  312. console.log(pieSeries.labels.template)
  313. // Set label background color to match corresponding slice color
  314. pieSeries.labels.template.adapter.add("background.fill", function (fill, target) {
  315. return target.dataItem.slice.fill;
  316. });
  317. // This creates initial animation
  318. pieSeries.hiddenState.properties.opacity = 1;
  319. pieSeries.hiddenState.properties.endAngle = -90;
  320. pieSeries.hiddenState.properties.startAngle = -90;
  321. // console.log('調位置')
  322. downloadButton.style.display = "inline-block";
  323. }
  324. // var colorEven='#288D97';
  325. // var colorOdd='#427D7E'
  326. function adjustColorBrightness(color, amount) {
  327. let usePound = false;
  328. if (color[0] == "#") {
  329. color = color.slice(1);
  330. usePound = true;
  331. }
  332. let num = parseInt(color, 16);
  333. let r = (num >> 16) + amount;
  334. let g = ((num >> 8) & 0x00FF) + amount;
  335. let b = (num & 0x0000FF) + amount;
  336. if (r > 255) r = 255;
  337. else if (r < 0) r = 0;
  338. if (g > 255) g = 255;
  339. else if (g < 0) g = 0;
  340. if (b > 255) b = 255;
  341. else if (b < 0) b = 0;
  342. return (usePound ? "#" : "") + (r << 16 | g << 8 | b).toString(16).padStart(6, '0');
  343. }
  344. // 柱狀圖
  345. function generateBarChart(key, dataArray) {
  346. $('#chartdiv').show();
  347. // var colorEven = document.getElementById('colorEven').value;
  348. // var colorOdd = document.getElementById('colorOdd').value;
  349. var baseColor = document.getElementById('borderColorInputBar').value;
  350. var categoryAxisFzValue = document.getElementById('XfontSizeBar').value;
  351. var valueAxisFzValue = document.getElementById('YfontSizeBar').value;
  352. var colorEven = am4core.color(baseColor);
  353. var colorOdd = adjustColorBrightness(baseColor, -20);
  354. chartDiv.style.fontFamily = "Arial, sans-serif";
  355. chartDiv.style.fontWeight = "900";
  356. bgImgelement.style.backgroundImage = chart_bgimg_url;
  357. chartWhitelement.style.background = "rgba(255,255,255,0.5)"
  358. console.log('labelDependent', labelDependent);
  359. // Themes begin
  360. am4core.useTheme(am4themes_animated);
  361. // Themes end
  362. // Create chart instance
  363. var chart = am4core.create("chartdiv", am4charts.XYChart3D);
  364. console.log('判斷資料', dataArray);
  365. chart.data = dataArray;
  366. chart.angle = 30; // 圖表角度
  367. chart.depth = 25; // 圖表深度
  368. let categoryAxis = chart.xAxes.push(new am4charts.CategoryAxis());
  369. categoryAxis.dataFields.category = "x";
  370. categoryAxis.renderer.labels.template.rotation = 0;
  371. categoryAxis.renderer.labels.template.hideOversized = false;
  372. categoryAxis.renderer.minGridDistance = 20; // 間距
  373. // categoryAxis.renderer.labels.template.horizontalCenter = "left";
  374. // categoryAxis.renderer.labels.template.verticalCenter = "middle";
  375. // categoryAxis.tooltip.label.rotation = 90;
  376. // categoryAxis.tooltip.label.horizontalCenter = "right";
  377. // categoryAxis.tooltip.label.verticalCenter = "middle";
  378. // label 換行
  379. let label = categoryAxis.renderer.labels.template;
  380. label.wrap = true;
  381. if (dataArray.length > 5) {
  382. console.log('資料長度5')
  383. label.maxWidth = 50;
  384. } else {
  385. label.maxWidth = 100;
  386. console.log('資料長度<5')
  387. }
  388. // x軸字體大小
  389. categoryAxis.renderer.labels.template.fontSize = categoryAxisFzValue;
  390. categoryAxis.renderer.cellEndLocation = 0.5; // 減少值可以增加間距
  391. let valueAxis = chart.yAxes.push(new am4charts.ValueAxis());
  392. valueAxis.title.text = labelDependent; // Y軸
  393. valueAxis.title.fontWeight = "bold";
  394. // y軸字體大小
  395. valueAxis.renderer.labels.template.fontSize = valueAxisFzValue;
  396. // Create series
  397. var series = chart.series.push(new am4charts.ColumnSeries3D());
  398. series.dataFields.valueY = 'y';
  399. series.dataFields.categoryX = "x";
  400. series.name = "y";
  401. series.tooltipText = "{categoryX}: [bold]{valueY}[/]";
  402. series.columns.template.fillOpacity = .8;
  403. let columnTemplate = series.columns.template;
  404. columnTemplate.strokeWidth = 2;
  405. columnTemplate.strokeOpacity = 1;
  406. columnTemplate.stroke = am4core.color("#FFFFFF");
  407. // columnTemplate.adapter.add("fill", function (fill, target) {
  408. // return chart.colors.getIndex(target.dataItem.index);
  409. // })
  410. // 設置基數和偶數顏色
  411. columnTemplate.adapter.add("fill", function (fill, target) {
  412. return target.dataItem.index % 2 === 0 ? am4core.color(colorEven) : am4core.color(colorOdd);
  413. });
  414. columnTemplate.adapter.add("stroke", function (stroke, target) {
  415. return chart.colors.getIndex(target.dataItem.index);
  416. })
  417. // // 加上白色線條
  418. // series.columns.template.events.on("validated", function (event) {
  419. // let column = event.target;
  420. // let whiteLine = column.createChild(am4core.Line);
  421. // whiteLine.stroke = am4core.color("#ffffff");
  422. // whiteLine.strokeWidth = 1.3; // 線條寬度
  423. // whiteLine.strokeOpacity = 0.5;// 透明度
  424. // whiteLine.x1 = column.pixelWidth / 1;
  425. // whiteLine.y1 = 0;
  426. // whiteLine.x2 = column.pixelWidth / 1;
  427. // whiteLine.y2 = column.pixelHeight;
  428. // });
  429. chart.cursor = new am4charts.XYCursor();
  430. chart.cursor.lineX.strokeOpacity = 0;
  431. chart.cursor.lineY.strokeOpacity = 0;
  432. categoryAxis.renderer.grid.template.strokeOpacity = 0; // 隱藏 X 軸
  433. // valueAxis.renderer.grid.template.strokeOpacity = 0; // 隱藏 Y 軸
  434. // valueAxis.renderer.line.strokeOpacity = 0;
  435. // LineSeries 底部線條
  436. var lineSeries = chart.series.push(new am4charts.LineSeries());
  437. lineSeries.dataFields.valueY = "bottom";
  438. lineSeries.dataFields.categoryX = "0";
  439. lineSeries.stroke = am4core.color("#FFFFFF")
  440. lineSeries.strokeWidth = 2;
  441. lineSeries.strokeOpacity = 1
  442. var bottomData = [];
  443. for (var i = 0; i < dataArray.length; i++) {
  444. bottomData.push({ "x": dataArray[i].x, "bottom": 0 });
  445. }
  446. lineSeries.data = bottomData;
  447. downloadButton.style.display = "inline-block";
  448. // 底部線條
  449. // let rectElements = document.querySelectorAll("rect");
  450. // // let widthValue = 0;
  451. // let fifthRectElement = rectElements[3];
  452. // // fifthRectElement.style.position = "relative";
  453. // setTimeout(() => {
  454. // let width = fifthRectElement.getAttribute("width");
  455. // // widthValue = parseFloat(width);
  456. // console.log('rectElements', rectElements);
  457. // console.log('fifthRectElement', fifthRectElement);
  458. // console.log('width', width);
  459. // let line = document.createElement("div");
  460. // line.style.position = "absolute";
  461. // line.style.bottom = "162px";
  462. // line.style.right = "40px";
  463. // line.style.width = `${parseFloat(width)}px`;
  464. // line.style.height = "2px";
  465. // line.style.backgroundColor = "#000000";
  466. // document.getElementById("chartdiv").appendChild(line);
  467. // }, 10)
  468. // end am4core.ready()
  469. }
  470. var downloadButton = document.getElementById("downloadButton");
  471. // downloadButton.style.display = 'none';
  472. // 添加點擊事件監聽器
  473. downloadButton.addEventListener('click', function () {
  474. setTimeout(function () {
  475. html2canvas(document.getElementById('data_chart_box')).then(function (canvas) {
  476. // // 獲取圖片資料 URL
  477. // var imageData = canvasPng.toDataURL("image/png");
  478. // // 創建下載連結
  479. // var link = document.createElement('a');
  480. // link.download = 'chart.png'; // 下載的檔案名稱
  481. // link.href = imageData; // 圖片資料 URL
  482. // // 模擬點擊下載連結
  483. // link.click();
  484. // 創建一個&lt;a&gt;元素
  485. const link = document.createElement('a');
  486. // 將 Canvas 轉換為 Data URL
  487. link.href = canvas.toDataURL('image/png');
  488. // 設置下載文件名
  489. link.download = 'chart.png';
  490. // 模擬點擊下載
  491. link.click();
  492. });
  493. }, 1000); // 等待 1 秒钟
  494. });
  495. var pieChartColors = [
  496. 'rgb(171, 51, 49)',
  497. 'rgb(34, 83, 149)',
  498. 'rgb(79, 148, 65)',
  499. 'rgb(217, 195, 105)',
  500. 'rgb(142, 124, 180)',
  501. 'rgb(211, 183, 144)',
  502. 'rgb(83, 84, 84)',
  503. 'rgb(229, 147, 152)'
  504. ];
  505. function createChartDoughnut2(data, labels) {
  506. const chartData = {
  507. type: 'outlabeledPie',
  508. data: {
  509. labels: labels,
  510. datasets: [
  511. {
  512. data: data,
  513. borderWidth: borderWidthValue,
  514. fill: false,
  515. backgroundColor: pieChartColors,
  516. },
  517. ],
  518. },
  519. options: {
  520. plugins: {
  521. legend: false,
  522. outlabels: {
  523. text: '%l %p',
  524. color: 'white',
  525. stretch: 35,
  526. font: {
  527. resizable: true,
  528. family: 'Arial',
  529. size: 48,
  530. weight: 'bold',
  531. style: 'italic',
  532. },
  533. },
  534. },
  535. },
  536. };
  537. const devicePixelRatio = 4;
  538. var chartUrl = `https://quickchart.io/chart?c=${encodeURIComponent(JSON.stringify(chartData))}&devicePixelRatio=${devicePixelRatio}&f=png&fontsize=20`;
  539. // 获取目标 div 元素
  540. const targetDiv = document.getElementById('data_chartJs_box');
  541. // 创建一个 img 元素来显示图表
  542. const chartImg = document.createElement('img');
  543. chartImg.src = chartUrl;
  544. chartImg.classList.add('outlabeled_img');
  545. // 清空目标 div 元素
  546. targetDiv.innerHTML = '';
  547. // 将图表 img 元素插入到目标 div 中
  548. targetDiv.appendChild(chartImg);
  549. console.log(chart_bgimg_url);
  550. bgImgelement.style.backgroundImage = chart_bgimg_url;
  551. chartWhitelement.style.background = "rgba(255,255,255,0.5)"
  552. }
  553. // 版型二源餅圖
  554. // // 创建 Chart.js 图表
  555. function createChartDoughnut(chartType, data, labels) {
  556. $('#textToChart').show();
  557. canvasPng.style.maxWidth = '450px';
  558. canvasPng.style.maxHeight = '450px';
  559. var windowWidth = window.innerWidth || document.documentElement.clientWidth;
  560. var canvasWidth = Math.min(500, windowWidth); // 限制最大宽度为 500px
  561. canvasPng.style.width = canvasWidth + 'px'; // 设置 canvas 宽度
  562. canvasPng.style.marginLeft = 'auto'; // 左外边距自动计算
  563. canvasPng.style.marginRight = 'auto'; // 右外边距自动计算
  564. var ctx = document.getElementById("textToChart").getContext('2d');
  565. myChart = new Chart(ctx, {
  566. type: chartType,
  567. data: {
  568. labels: labels,
  569. datasets: [{
  570. strokeColor: "rgba(220,220,220,1)",
  571. StrokeWidth: 5,
  572. data: data,
  573. borderWidth: borderWidthValue,
  574. pointRadius: 0,
  575. pointBorderColor: '#fff',
  576. pointBorderWidth: 3,
  577. pointHoverRadius: 0,
  578. fill: false,
  579. backgroundColor: pieChartColors,
  580. // barPercentage: 0.5,
  581. // categoryPercentage: 0.5
  582. }]
  583. },
  584. options: {
  585. // responsive: true, // 啟用響應式
  586. maintainAspectRatio: true,
  587. responsive: true,
  588. aspectRatio: 1,
  589. plugins: {
  590. legend: {
  591. display: false,
  592. },
  593. datalabels: {
  594. color: '#fff', // 数据标签的颜色
  595. anchor: 'end', // 数据标签的位置
  596. align: 'start', // 数据标签的对齐方式
  597. font: {
  598. size: 14, // 数据标签的字体大小
  599. },
  600. }
  601. },
  602. scales: {
  603. x: {
  604. display: false, // 显示 x 轴
  605. grid: {
  606. display: false,
  607. lineWidth: 5,
  608. color: '#584B3D'
  609. },
  610. ticks: {
  611. textStrokeColor: '#fff',
  612. textStrokeWidth: 5,
  613. font: {
  614. display: false,
  615. family: 'Arial', // 设置字体
  616. size: XfontSizeValue, // 设置字体大小
  617. weight: 'bold', // 设置字体粗细
  618. fontColor: 'rgba(255,255,255,0.8)', // X-axis font color
  619. shadowColor: 'rgba(0, 0, 0, 0.5)', // Shadow color
  620. shadowBlur: 10, // Shadow blur level
  621. shadowOffsetX: 5, // Horizontal shadow offset
  622. shadowOffsetY: 5
  623. },
  624. },
  625. },
  626. y: {
  627. display: false,
  628. beginAtZero: false,
  629. grid: {
  630. display: false,
  631. lineWidth: 5,
  632. color: '#584B3D'
  633. },
  634. ticks: {
  635. textStrokeColor: '#fff',
  636. textStrokeWidth: 5,
  637. font: {
  638. family: 'Arial', // 設置字體
  639. size: XfontSizeValue, // 設置字體大小
  640. weight: 'bold', // 設置字體粗細
  641. color: 'rgba(255,255,255,0.8)', // Y-axis 字體顏色
  642. shadowColor: 'rgba(0, 0, 0, 0.5)', // 陰影顏色
  643. shadowBlur: 10, // 陰影模糊級別
  644. shadowOffsetX: 5, // 水平陰影偏移
  645. shadowOffsetY: 5 // 垂直陰影偏移
  646. },
  647. }
  648. }
  649. },
  650. layout: {
  651. padding: {
  652. left: 50, // 调整图表左边距
  653. right: 50, // 调整图表右边距
  654. top: 10,
  655. bottom: 10,
  656. }
  657. },
  658. },
  659. });
  660. console.log(chart_bgimg_url);
  661. bgImgelement.style.backgroundImage = chart_bgimg_url;
  662. chartWhitelement.style.background = "rgba(255,255,255,0.5)"
  663. }
  664. var chartJsBox = document.getElementById('data_chartJs_box');
  665. function createTable(dataArray, unit, Label_dependent_variable, Label_independent_variable,) {
  666. $('#chartdiv').hide();
  667. $('#textToChart').hide();
  668. var table = document.createElement('table');
  669. table.classList.add('table', 'table_template', 'dynamic-table'); // 添加 Bootstrap 的 table 样式,如果你在使用 Bootstrap
  670. // 创建表头
  671. var thead = table.createTHead();
  672. var headerRow = thead.insertRow();
  673. // Object.keys(dataArray[0]).forEach(function (key) {
  674. // var header = headerRow.insertCell();
  675. // header.textContent = key;
  676. // });
  677. var yearHeader = headerRow.insertCell();
  678. yearHeader.textContent = Label_independent_variable;
  679. var avgPriceHeader = headerRow.insertCell();
  680. avgPriceHeader.textContent = Label_dependent_variable;
  681. // 创建表格主体
  682. var tbody = table.createTBody();
  683. dataArray.forEach(function (item) {
  684. console.log(item)
  685. var row = tbody.insertRow();
  686. // var cell1 = row.insertCell();
  687. // cell1.textContent = item.year;
  688. // cell1.classList.add('year-class'); // 为年份单元格添加类
  689. // var cell2 = row.insertCell();
  690. Object.keys(item).forEach(function (key, index) {
  691. var cell = row.insertCell();
  692. if (index === 0) { // If it's the first cell, add 'year-class'
  693. cell.textContent = item[key];
  694. cell.classList.add('year-class');
  695. } else { // Otherwise, add 'average-class'
  696. if (key === 'Average_Close') {
  697. cell.textContent = item[key].toFixed(2) + unit;
  698. } else {
  699. // cell.textContent = item[key];
  700. cell.textContent = item[key].toFixed(2) + unit;
  701. }
  702. cell.classList.add('average-class');
  703. }
  704. });
  705. // console.log(item.avg_price.toFixed(2))
  706. // cell.textContent = item[key].toFixed(2) + unit;
  707. // cell2.classList.add('average-class'); // 为年份单元格添加类
  708. });
  709. // 获取包含表格的 div 元素
  710. bgImgelement.style.backgroundImage = chart_bgimg_url;
  711. img_box.style.bottom = '60px';
  712. img_box.style.right = '5px';
  713. document.getElementById('img_box_url').style.width = "180px";
  714. // chartWhitelement.style.background = "rgba(255,255,255,0.5)"
  715. // 将表格添加到指定的 div 元素中
  716. chartJsBox.appendChild(table);
  717. }
  718. // 版型一折線圖
  719. // // 创建 Chart.js 图表
  720. function createChart(chartType, data, labels) {
  721. $('#textToChart').show();
  722. canvasPng.style.maxWidth = '';
  723. canvasPng.style.maxHeight = '';
  724. ctx = document.getElementById("textToChart");
  725. myChart = new Chart(ctx, {
  726. type: chartType,
  727. data: {
  728. labels: labels,
  729. datasets: [{
  730. strokeColor: "rgba(220,220,220,1)",
  731. StrokeWidth: 5,
  732. data: data,
  733. borderWidth: borderWidthValue,
  734. pointRadius: 0,
  735. pointBorderColor: '#fff',
  736. pointBorderWidth: 3,
  737. pointHoverRadius: 0,
  738. fill: false,
  739. backgroundColor: chartColor,
  740. borderColor: chartColor,
  741. // barPercentage: 0.5,
  742. // categoryPercentage: 0.5
  743. }]
  744. },
  745. options: {
  746. // responsive: true, // 啟用響應式
  747. maintainAspectRatio: false,
  748. plugins: {
  749. chart3d: {
  750. enabled: true, // 啟用 3D 插件
  751. alpha: 25, // 圖表繞 x 軸的旋轉角度
  752. beta: 25, // 圖表繞 y 軸的旋轉角度
  753. depth: 15 // 圖表的深度
  754. },
  755. // datalabels: {
  756. // formatter: function (value, context) {
  757. // // 添加单位
  758. // return value + ' ' + unit;
  759. // },
  760. // textStrokeColor: '#fff',
  761. // textStrokeWidth: 5,
  762. // color: '#584B3D',
  763. // font: {
  764. // size: YfontSizeValue,
  765. // },
  766. // anchor: 'end',
  767. // align: 'end',
  768. // },
  769. datalabels: null,
  770. // datalabels: data.length <= 10 ? {
  771. // formatter: function (value, context) {
  772. // // 添加单位
  773. // return value + ' ' + unit;
  774. // },
  775. // textStrokeColor: '#fff',
  776. // textStrokeWidth: 5,
  777. // color: '#584B3D',
  778. // font: {
  779. // size: YfontSizeValue,
  780. // },
  781. // anchor: 'end',
  782. // align: 'end',
  783. // } : null,
  784. customLabels: null,
  785. legend: {
  786. display: false,
  787. },
  788. // subtitle: {
  789. // display: true,
  790. // align: 'left',
  791. // position: 'top',
  792. // text: 'Custom Chart Subtitle'
  793. // },
  794. // afterDatasetsDraw(chart) {
  795. // var ctx = chart.ctx;
  796. // var xAxis = chart.scales.x;
  797. // var yAxis = chart.scales.y;
  798. // var dataj = myChart.data.datasets[0].data;
  799. // console.log('after', dataj)
  800. // ctx.save();
  801. // ctx.strokeStyle = '#000'; // 虚线颜色
  802. // ctx.lineWidth = 1; // 虚线宽度
  803. // ctx.setLineDash([5, 5]); // 设置虚线样式,[虚线段长度, 间隔长度]
  804. // dataj.forEach(function (value, index) {
  805. // var xPos = xAxis.getPixelForValue(labels[index]); // 获取 x 轴位置
  806. // var yPos = yAxis.getPixelForValue(value); // 获取 y 轴位置
  807. // // console.log(xPos)
  808. // // 绘制虚线
  809. // // ctx.beginPath();
  810. // // ctx.moveTo(xPos, yPos);
  811. // // ctx.lineTo(xPos, xAxis.bottom);
  812. // // ctx.stroke();
  813. // var xLineEnd = Math.min(yPos, yAxis.bottom);
  814. // // console.log('第' + index + '條線', xPos, yPos, 'xLineEnd', xLineEnd, 'yAxis.getPixelForValue(value)', yAxis.getPixelForValue(value));
  815. // // console.log('yAxis.getPixelForValue(value)', yAxis.getPixelForValue(value))
  816. // // 绘制虚线
  817. // ctx.beginPath();
  818. // ctx.moveTo(xPos, yAxis.getPixelForValue(value));
  819. // ctx.lineTo(xPos, xAxis.top);
  820. // ctx.stroke();
  821. // // 添加日期标签
  822. // ctx.fillStyle = '#000'; // 文字颜色
  823. // ctx.textAlign = 'center';
  824. // // ctx.fillText(labels[index], xPos, yAxis.bottom + 20); // 将日期标签绘制在虚线下方
  825. // });
  826. // ctx.restore();
  827. // }
  828. },
  829. scales: {
  830. x: {
  831. display: displayXaxes, // 显示 x 轴
  832. grid: {
  833. display: false,
  834. lineWidth: 5,
  835. color: '#584B3D'
  836. },
  837. ticks: {
  838. textStrokeColor: '#fff',
  839. textStrokeWidth: 5,
  840. font: {
  841. display: false,
  842. family: 'Arial', // 设置字体
  843. size: XfontSizeValue, // 设置字体大小
  844. weight: 'bold', // 设置字体粗细
  845. fontColor: 'rgba(255,255,255,0.8)', // X-axis font color
  846. shadowColor: 'rgba(0, 0, 0, 0.5)', // Shadow color
  847. shadowBlur: 10, // Shadow blur level
  848. shadowOffsetX: 5, // Horizontal shadow offset
  849. shadowOffsetY: 5
  850. },
  851. },
  852. },
  853. y: {
  854. display: true,
  855. beginAtZero: false,
  856. grid: {
  857. grid: {
  858. display: true, // 显示 y 轴网格线
  859. lineWidth: 1, // 网格线宽度
  860. color: '#C0C5CC' // 网格线颜色
  861. },
  862. },
  863. ticks: {
  864. textStrokeColor: '#fff',
  865. textStrokeWidth: 5,
  866. font: {
  867. family: 'Arial', // 設置字體
  868. size: XfontSizeValue, // 設置字體大小
  869. weight: 'bold', // 設置字體粗細
  870. color: 'rgba(255,255,255,0.8)', // Y-axis 字體顏色
  871. shadowColor: 'rgba(0, 0, 0, 0.5)', // 陰影顏色
  872. shadowBlur: 10, // 陰影模糊級別
  873. shadowOffsetX: 5, // 水平陰影偏移
  874. shadowOffsetY: 5 // 垂直陰影偏移
  875. },
  876. }
  877. }
  878. },
  879. layout: {
  880. padding: {
  881. left: 50, // 调整图表左边距
  882. right: 50, // 调整图表右边距
  883. top: 40,
  884. bottom: 10,
  885. }
  886. },
  887. },
  888. });
  889. // 注册 afterDraw 事件
  890. // Chart.register({
  891. // id: 'afterDraw',
  892. // afterDraw: function (chart) {
  893. // // console.log('處發', chart)
  894. // // 在这里添加你的绘制代码
  895. // var ctx = chart.ctx;
  896. // var xAxis = chart.scales.x;
  897. // var yAxis = chart.scales.y;
  898. // var dataj = myChart.data.datasets[0].data;
  899. // console.log('after', dataj)
  900. // ctx.save();
  901. // ctx.strokeStyle = '#000'; // 虚线颜色
  902. // ctx.lineWidth = 1; // 虚线宽度
  903. // ctx.setLineDash([5, 5]); // 设置虚线样式,[虚线段长度, 间隔长度]
  904. // dataj.forEach(function (value, index) {
  905. // var xPos = xAxis.getPixelForValue(labels[index]); // 获取 x 轴位置
  906. // var yPos = yAxis.getPixelForValue(value); // 获取 y 轴位置
  907. // // console.log(xPos)
  908. // // 绘制虚线
  909. // // ctx.beginPath();
  910. // // ctx.moveTo(xPos, yPos);
  911. // // ctx.lineTo(xPos, xAxis.bottom);
  912. // // ctx.stroke();
  913. // var xLineEnd = Math.min(yPos, yAxis.bottom);
  914. // // console.log('第' + index + '條線', xPos, yPos, 'xLineEnd', xLineEnd, 'yAxis.getPixelForValue(value)', yAxis.getPixelForValue(value));
  915. // // console.log('yAxis.getPixelForValue(value)', yAxis.getPixelForValue(value))
  916. // // 绘制虚线
  917. // ctx.beginPath();
  918. // ctx.moveTo(xPos, yAxis.getPixelForValue(value));
  919. // ctx.lineTo(xPos, xAxis.top);
  920. // ctx.stroke();
  921. // // 添加日期标签
  922. // ctx.fillStyle = '#000'; // 文字颜色
  923. // ctx.textAlign = 'center';
  924. // // ctx.fillText(labels[index], xPos, yAxis.bottom + 20); // 将日期标签绘制在虚线下方
  925. // });
  926. // ctx.restore();
  927. // }
  928. // });
  929. console.log(chart_bgimg_url);
  930. bgImgelement.style.backgroundImage = chart_bgimg_url;
  931. chartWhitelement.style.background = "rgba(255,255,255,0.5)";
  932. downloadButton.style.display = "inline-block";
  933. }
  934. // createChart(chartType);
  935. // createChart(chartType)
  936. const ChartOptions = document.querySelectorAll('input[name="ChartOptions"]');
  937. ChartOptions.forEach(button => {
  938. button.addEventListener('click', function () {
  939. // console.log(this.value);
  940. chartType = this.value;
  941. console.log(chartType);
  942. if (myChart !== null) {
  943. myChart.destroy();
  944. data = [];
  945. labels = [];
  946. }
  947. if (chartType === 'line') {
  948. $('#chartdiv').hide();
  949. generateChart(dataArray);
  950. } else {
  951. $('#textToChart').hide();
  952. extractAndGenerateChart(dataArray);
  953. }
  954. // createChart(chartType, data, labels);
  955. });
  956. });
  957. // 表格字體
  958. var fontSizeInput = document.getElementById('table_fontSize');
  959. // 添加事件监听器
  960. fontSizeInput.addEventListener('input', function () {
  961. // 获取输入框的值
  962. var fontSize = fontSizeInput.value;
  963. // 获取动态创建的表格元素
  964. var dynamicTable = document.querySelector('.dynamic-table');
  965. // 如果表格存在,则调整其字体大小
  966. if (dynamicTable) {
  967. dynamicTable.style.fontSize = fontSize + 'px';
  968. }
  969. });
  970. // 是否顯示point
  971. var togglePoints = document.getElementById('togglePoints');
  972. togglePoints.addEventListener('change', function () {
  973. console.log('checked')
  974. if (this.checked) {
  975. myChart.data.datasets[0].pointRadius = 5;
  976. myChart.data.datasets[0].pointHoverRadius = 8;
  977. } else {
  978. myChart.data.datasets[0].pointRadius = 0;
  979. myChart.data.datasets[0].pointHoverRadius = 0;
  980. }
  981. myChart.update();
  982. });
  983. // 單位標題
  984. // =========================
  985. var unitInput = document.getElementById('unit_data');
  986. unitInput.addEventListener('input', function () {
  987. unit = unitInput.value;
  988. // 重新渲染图表
  989. myChart.update();
  990. });
  991. // ==========================
  992. // y軸字體大小-折線圖
  993. // ==========================
  994. var YfontSize = document.getElementById('YfontSize');
  995. YfontSize.addEventListener('input', function () {
  996. YfontSizeValue = YfontSize.value;
  997. if (myChart) {
  998. myChart.destroy();
  999. }
  1000. createChart(chartType, data, labels);
  1001. });
  1002. // ==========================
  1003. // x軸字體大小-折線圖
  1004. // ==========================
  1005. var XfontSize = document.getElementById('XfontSize');
  1006. XfontSize.addEventListener('input', function () {
  1007. XfontSizeValue = XfontSize.value;
  1008. console.log(XfontSizeValue)
  1009. if (myChart) {
  1010. myChart.destroy();
  1011. }
  1012. createChart(chartType, data, labels);
  1013. });
  1014. // ==========================
  1015. // x軸字體大小-柱狀圖
  1016. // ==========================
  1017. var XfontSizeBar = document.getElementById('XfontSizeBar');
  1018. XfontSizeBar.addEventListener('input', function () {
  1019. extractAndGenerateChart(dataArray);
  1020. });
  1021. // ==========================
  1022. // y軸字體大小-柱狀圖
  1023. // ==========================
  1024. var YfontSizeBar = document.getElementById('YfontSizeBar');
  1025. YfontSizeBar.addEventListener('input', function () {
  1026. extractAndGenerateChart(dataArray);
  1027. });
  1028. // ==========================
  1029. // 線條顏色-柱狀圖
  1030. // =========================
  1031. const colorInputBar = document.getElementById('borderColorInputBar');
  1032. // 添加事件监听器,当颜色选择发生变化时触发
  1033. colorInputBar.addEventListener('input', function () {
  1034. extractAndGenerateChart(dataArray);
  1035. });
  1036. // =========================
  1037. // 線條粗細
  1038. // =========================
  1039. var rangeInput = document.getElementById('borderWidthRange');
  1040. var rangeInputSpan = document.getElementById('borderWidthRangeValue');
  1041. var borderWidthValue;
  1042. rangeInputSpan.textContent = rangeInput.value;
  1043. // 添加事件监听器,当滑动条的值发生改变时更新显示的值
  1044. rangeInput.addEventListener('input', function () {
  1045. // 获取滑动条的当前值
  1046. borderWidthValue = rangeInput.value;
  1047. console.log(borderWidthValue);
  1048. rangeInputSpan.textContent = borderWidthValue;
  1049. if (myChart) {
  1050. myChart.destroy();
  1051. }
  1052. createChart(chartType, data, labels);
  1053. // myChart.data.datasets[0].borderWidth = borderWidthValue;
  1054. });
  1055. // =========================
  1056. // 線條顏色-折線圖
  1057. // =========================
  1058. const colorInput = document.getElementById('borderColorInput');
  1059. // 添加事件监听器,当颜色选择发生变化时触发
  1060. colorInput.addEventListener('input', function () {
  1061. // 获取当前选择的颜色值
  1062. chartColor = colorInput.value;
  1063. if (myChart) {
  1064. myChart.destroy();
  1065. }
  1066. console.log(chartColor);
  1067. createChart(chartType, data, labels);
  1068. });
  1069. // =========================
  1070. // 模板樣式
  1071. // =========================
  1072. // JavaScript
  1073. document.addEventListener("DOMContentLoaded", function () {
  1074. // 獲取所有帶有 templateImg class 的圖像元素
  1075. var templateImgs = document.querySelectorAll('.templateImg');
  1076. // 為每個圖像元素添加點擊事件監聽器
  1077. templateImgs.forEach(function (img) {
  1078. img.addEventListener('click', function () {
  1079. // 在點擊時印出圖像元素的 value 屬性值
  1080. console.log(this.getAttribute('value'));
  1081. });
  1082. });
  1083. });
  1084. // =========================
  1085. var bgImgelement = document.getElementById('data_chart_box');
  1086. var chartWhitelement = document.getElementById('data_chartJs_box');
  1087. // 背景樣式
  1088. // =========================
  1089. // JavaScript
  1090. document.addEventListener("DOMContentLoaded", function () {
  1091. // 獲取所有帶有 templateImg class 的圖像元素
  1092. var bgImgs = document.querySelectorAll('.bgImg');
  1093. // 為每個圖像元素添加點擊事件監聽器
  1094. bgImgs.forEach(function (img) {
  1095. img.addEventListener('click', function () {
  1096. // 在點擊時印出圖像元素的 value 屬性值
  1097. console.log(this.getAttribute('value'));
  1098. var bgImgUrl = this.getAttribute('value');
  1099. chart_bgimg_url = 'url(' + bgImgUrl + ')';
  1100. if (bgImgUrl === './img/bg07.webp') {
  1101. chartTitle.style.color = "#ffff";
  1102. } else {
  1103. chartTitle.style.color = "#1c2d6d";
  1104. }
  1105. bgImgelement.style.backgroundImage = chart_bgimg_url;
  1106. // bgImgelement.style.backgroundSize = 'cover'; // 調整背景圖片大小
  1107. // bgImgelement.style.backgroundPosition = 'center'; // 調整背景圖片位置
  1108. // bgImgelement.style.backgroundRepeat = 'no-repeat'; // 調整背景圖片重複
  1109. });
  1110. });
  1111. });
  1112. // =========================
  1113. const radioButtons = document.querySelectorAll('input[name="inlineRadioOptions"]');
  1114. var chartTitle = document.querySelector('.chart_title');
  1115. radioButtons.forEach(button => {
  1116. button.addEventListener('click', function () {
  1117. if (this.value === '0') {
  1118. chartTitle.style.textAlign = 'left';
  1119. } else if (this.value === '1') {
  1120. chartTitle.style.textAlign = 'center';
  1121. } else if (this.value === '2') {
  1122. chartTitle.style.textAlign = 'right';
  1123. }
  1124. });
  1125. });
  1126. var keywordInput = document.getElementById('title_data');
  1127. // 初始时将输入框的值设置为标题的文本内容
  1128. keywordInput.value = chartTitle.textContent;
  1129. // 监听输入框的输入事件,实现内容同步更新
  1130. keywordInput.addEventListener('input', function () {
  1131. chartTitle.textContent = this.value;
  1132. });
  1133. // 標題字體大小
  1134. // ==========================
  1135. var TitlefontSize = document.getElementById('TitlefontSize');
  1136. TitlefontSize.addEventListener('input', function () {
  1137. var TitlefontSizeValue = TitlefontSize.value;
  1138. chartTitle.style.fontSize = TitlefontSizeValue + 'px';;
  1139. });
  1140. // 標題字體顏色
  1141. // ==========================
  1142. let labelDependent = ""; // 圖表左側 Label
  1143. function get_data(input_text_value) {
  1144. inputField.value = "";
  1145. apiLoading();
  1146. var existingTable = chartJsBox.querySelector('table');
  1147. downloadButton.style.display = "none";
  1148. if (chartDiv.childElementCount != 0) {
  1149. data = [];
  1150. labels = [];
  1151. console.log('已存在')
  1152. chartTitle.textContent = "";
  1153. bgImgelement.style.backgroundImage = "";
  1154. chartWhitelement.style.background = "";
  1155. document.getElementById('img_box_url').src = "";
  1156. }
  1157. if (existingTable) {
  1158. chartJsBox.removeChild(existingTable);
  1159. chartTitle.textContent = "";
  1160. bgImgelement.style.backgroundImage = "";
  1161. chartWhitelement.style.background = "";
  1162. document.getElementById('img_box_url').src = "";
  1163. }
  1164. if (myChart) {
  1165. data = [];
  1166. labels = [];
  1167. chartTitle.textContent = "";
  1168. bgImgelement.style.backgroundImage = "";
  1169. chartWhitelement.style.background = "";
  1170. myChart.destroy();
  1171. document.getElementById('img_box_url').src = "";
  1172. }
  1173. axios.get('https://cmm.ai:8080/answer_with_token?token=' + accessToken + '&question=' + input_text_value)
  1174. .then(response => {
  1175. apiHideLoading();
  1176. console.log(response);
  1177. if (response.data.data == "Failed to generate chart!") {
  1178. alert('無法生成圖表')
  1179. return
  1180. } else {
  1181. var chart_info = response.data.chart_info.Title;
  1182. chartType = response.data.chart_info.Chart_type;
  1183. var finance_img_url = response.data.imageUrl_info.imageUrl;
  1184. if (response.data.imageUrl_info != null) {
  1185. console.log(finance_img_url);
  1186. document.getElementById('img_box_url').src = finance_img_url;
  1187. }
  1188. keywordInput.value = chart_info;
  1189. chartTitle.textContent = chart_info;
  1190. unit = response.data.chart_info.Unit_of_dependent_variable;
  1191. var Label_dependent_variable = response.data.chart_info.Label_dependent_variable;
  1192. var Label_independent_variable = response.data.chart_info.Label_independent_variable;
  1193. unit_data.value = response.data.chart_info.Unit_of_dependent_variable;
  1194. dataArray = response.data.data;
  1195. if (chartType === "table") {
  1196. createTable(dataArray, unit, Label_dependent_variable, Label_independent_variable);
  1197. // document.getElementById('img_box_url').src = "";
  1198. } else if (chartType === "bar") {
  1199. console.log('圖表類型bar')
  1200. $('#textToChart').hide();
  1201. extractAndGenerateChart(dataArray);
  1202. } else if (chartType === "doughnut") {
  1203. $('#textToChart').hide();
  1204. generatePieChart(dataArray)
  1205. } else {
  1206. $('#chartdiv').hide();
  1207. generateChart(dataArray);
  1208. }
  1209. // }
  1210. // 在這裡處理成功獲取 JSON 的情況
  1211. // console.log(response);
  1212. // switch (chartType) {
  1213. // case "line":
  1214. // document.getElementById("inlineRadio4").checked = true; // 折线图
  1215. // break;
  1216. // case "bar":
  1217. // document.getElementById("inlineRadio5").checked = true; // 柱状图
  1218. // labelDependent = response.data.chart_info.Label_dependent_variable;
  1219. // break;
  1220. // case "pie":
  1221. // document.getElementById("inlineRadio6").checked = true; // 圆饼图
  1222. // break;
  1223. // case "table":
  1224. // document.getElementById("inlineRadio7").checked = true; // 表格
  1225. // break;
  1226. // default:
  1227. // // 默认情况
  1228. }
  1229. // generateChart(dataArray);
  1230. })
  1231. .catch(error => {
  1232. apiHideLoading();
  1233. // 在這裡處理請求失敗的情況
  1234. console.error('發生錯誤:', error);
  1235. });
  1236. }
  1237. $(document).ready(function () {
  1238. $('#dataTable').DataTable();
  1239. });
  1240. function resizeChart() {
  1241. if (myChart) {
  1242. myChart.resize();
  1243. }
  1244. }
  1245. document.addEventListener('visibilitychange', function () {
  1246. console.log('Visibility changed:', document.visibilityState);
  1247. if (document.visibilityState === 'visible') {
  1248. resizeChart();
  1249. }
  1250. });
  1251. // 监听窗口resize事件
  1252. window.addEventListener('resize', resizeChart);
  1253. var setting_button = document.getElementById('chart_seeting_button');
  1254. var closeButton = document.getElementById('style_setting_close');
  1255. var closeButton_table = document.getElementById('style_setting_close_table');
  1256. // var borderSetting = document.getElementById('borderSetting');
  1257. var lineChartSetting = document.getElementById('lineChartSetting');
  1258. var barChartSetting = document.getElementById('barChartSetting');
  1259. var table_setting = document.getElementById('table_setting');
  1260. var chart_category = document.getElementById('chart_category');
  1261. // 获取 chartDiv 元素
  1262. setting_button.addEventListener('click', () => {
  1263. console.log('setting_button')
  1264. if (myChart === null && !chartJsBox.querySelector('table') && chartDiv.childElementCount === 0) {
  1265. alert('請先生成圖表');
  1266. return
  1267. }
  1268. if (chartType === 'table') {
  1269. console.log('表格');
  1270. sidebar.classList.toggle('show');
  1271. sidebar.classList.toggle('hidden');
  1272. barChartSetting.style.display = 'none';
  1273. lineChartSetting.style.display = 'none';
  1274. table_setting.style.display = 'block';
  1275. chart_category.style.display = 'none';
  1276. } else if (chartType === "bar") {
  1277. console.log('柱狀圖')
  1278. sidebar.classList.toggle('show');
  1279. sidebar.classList.toggle('hidden');
  1280. barChartSetting.style.display = 'block';
  1281. lineChartSetting.style.display = 'none';
  1282. table_setting.style.display = 'none';
  1283. } else if (chartType === "doughnut") {
  1284. console.log('快捷功能圓餅圖')
  1285. sidebar.classList.toggle('show');
  1286. sidebar.classList.toggle('hidden');
  1287. barChartSetting.style.display = 'none';
  1288. lineChartSetting.style.display = 'none';
  1289. table_setting.style.display = 'none';
  1290. chart_category.style.display = 'none';
  1291. } else {
  1292. sidebar.classList.toggle('show');
  1293. sidebar.classList.toggle('hidden');
  1294. table_setting.style.display = 'none';
  1295. barChartSetting.style.display = 'none';
  1296. lineChartSetting.style.display = 'block';
  1297. }
  1298. });
  1299. closeButton.addEventListener('click', () => {
  1300. sidebar.classList.remove('show');
  1301. sidebar.classList.add('hidden');
  1302. });
  1303. const finance_options = [
  1304. { name: "原油油桶", imageUrl: "./img/finance/01.webp" },
  1305. { name: "黃金", imageUrl: "./img/finance/02.webp" },
  1306. { name: "房屋", imageUrl: "./img/finance/03.webp" },
  1307. { name: "車", imageUrl: "./img/finance/04.webp" },
  1308. { name: "股市", imageUrl: "./img/finance/05.webp" },
  1309. { name: "晶圓", imageUrl: "./img/finance/06.webp" },
  1310. { name: "TSMC 台積電", imageUrl: "./img/finance/07.webp" },
  1311. { name: "台幣", imageUrl: "./img/finance/08.webp" },
  1312. { name: "美金", imageUrl: "./img/finance/09.webp" },
  1313. { name: "購物車", imageUrl: "./img/finance/10.webp" },
  1314. { name: "美中貿易", imageUrl: "./img/finance/11.webp" },
  1315. { name: "世界地圖", imageUrl: "./img/finance/12.webp" },
  1316. { name: "小麥", imageUrl: "./img/finance/13.webp" },
  1317. { name: "玉米", imageUrl: "./img/finance/14.webp" },
  1318. { name: "智慧產線", imageUrl: "./img/finance/15.webp" }
  1319. ];
  1320. const selectOptions = document.getElementById('finance_options');
  1321. const selectOptions_table = document.getElementById('finance_options_table');
  1322. finance_options.forEach((option, index) => {
  1323. const optionElement = document.createElement('option');
  1324. optionElement.value = index + 1; // 值从 1 开始
  1325. optionElement.textContent = option.name;
  1326. optionElement.setAttribute('data-image-url', option.imageUrl); // 存储图片 URL
  1327. selectOptions.appendChild(optionElement);
  1328. });
  1329. selectOptions.addEventListener('change', function () {
  1330. const selectedOption = selectOptions.options[selectOptions.selectedIndex];
  1331. const imageUrl = selectedOption.getAttribute('data-image-url');
  1332. document.getElementById('img_box_url').src = imageUrl;
  1333. });
  1334. // finance_options.forEach((option, index) => {
  1335. // const optionElement = document.createElement('option');
  1336. // optionElement.value = index + 1; // 值从 1 开始
  1337. // optionElement.textContent = option.name;
  1338. // optionElement.setAttribute('data-image-url', option.imageUrl); // 存储图片 URL
  1339. // selectOptions_table.appendChild(optionElement);
  1340. // });
  1341. // selectOptions_table.addEventListener('change', function () {
  1342. // const selectedOption = selectOptions_table.options[selectOptions_table.selectedIndex];
  1343. // const imageUrl = selectedOption.getAttribute('data-image-url');
  1344. // document.getElementById('img_box_url').src = imageUrl;
  1345. // });
  1346. document.querySelectorAll('input[name="ChartOptions"]').forEach(function (radio) {
  1347. radio.addEventListener('change', function () {
  1348. // 檢查哪個單選按鈕被選中
  1349. if (this.value === 'line') {
  1350. // 如果選擇了折線圖,執行相應的操作
  1351. console.log("選擇了折線圖");
  1352. // 在這裡執行顯示折線圖的相關代碼
  1353. } else if (this.value === 'doughnut') {
  1354. // 如果選擇了柱狀圖,執行相應的操作
  1355. console.log("選擇了柱狀圖");
  1356. // 在這裡執行顯示柱狀圖的相關代碼
  1357. }
  1358. });
  1359. });
  1360. function tokencheck() {
  1361. if (!localStorage.getItem("access_token")) {
  1362. window.location.href = "./login.html";
  1363. } else {
  1364. console.log("存在");
  1365. }
  1366. }
  1367. tokencheck();
  1368. function logout() {
  1369. localStorage.removeItem("access_token");
  1370. }
  1371. $(document).on("click", "#logout", function (event) {
  1372. alert("登出成功");
  1373. logout();
  1374. location.reload();
  1375. });
  1376. // speech_to_text
  1377. var closeRecord = document.getElementById('recording_block_close');
  1378. var recording_block = document.getElementById('recording_block');
  1379. var PageLM = "2024-05-07 18:33";
  1380. var recording_button = document.getElementById('recording_button');
  1381. var stopButton = document.getElementById('stop');
  1382. var audio = document.getElementById('audio');
  1383. var recorder, audioBlob;
  1384. closeRecord.addEventListener('click', () => {
  1385. recPause();
  1386. console.log('record-click')
  1387. recording_block.classList.remove('show');
  1388. recording_block.classList.add('hidden');
  1389. });
  1390. function reclog(s, color) {
  1391. var now = new Date();
  1392. var t = ("0" + now.getHours()).substr(-2)
  1393. + ":" + ("0" + now.getMinutes()).substr(-2)
  1394. + ":" + ("0" + now.getSeconds()).substr(-2);
  1395. var div = document.createElement("div");
  1396. var elem = document.querySelector(".reclog");
  1397. elem.insertBefore(div, elem.firstChild);
  1398. div.innerHTML = '<div style="color:' + (!color ? "" : color == 1 ? "red" : color == 2 ? "#0b1" : color) + '">[' + t + ']' + s + '</div>';
  1399. };
  1400. window.onerror = function (message, url, lineNo, columnNo, error) {
  1401. //https://www.cnblogs.com/xianyulaodi/p/6201829.html
  1402. reclog('<span style="color:red">【Uncaught Error】' + message + '<pre>' + "at:" + lineNo + ":" + columnNo + " url:" + url + "\n" + (error && error.stack || Html_$T("kBaF::不能获得错误堆栈")) + '</pre></span>');
  1403. };
  1404. if (!window.Html_$T) {//没有提供本页面用的国际化多语言支持时 返回中文文本
  1405. window.Html_$T = function () {
  1406. var a = arguments, txt = a[0].replace(/^.+?::/, ""), n = 0;
  1407. for (var i = 0; i < a.length; i++) { if (typeof a[i] == "number") { n = i; break } }
  1408. txt = txt.replace(/\{(\d+)\}/g, function (v, f) { v = a[+f + n]; return v == null ? "" : v });
  1409. return txt;
  1410. }
  1411. window.Html_xT = function (v) { return v }
  1412. }
  1413. if (window.Recorder) {
  1414. // reclog(Html_$T('BL9u::頁面已準備好,請先點擊打開錄音,然後點擊錄製'), 2);
  1415. console.log('頁面已準備好,請先點擊打開錄音,然後點擊錄製')
  1416. } else {
  1417. reclog(Html_$T("YzPd::js文件加载失败,请刷新重试!"), "#f00;font-size:50px");
  1418. console.log('js文件加载失敗,请刷新重试!')
  1419. }
  1420. recording_button.addEventListener('click', async () => {
  1421. recording_block.classList.toggle('show');
  1422. recording_block.classList.toggle('hidden');
  1423. recStart();
  1424. });
  1425. function recStart() {//打开了录音后才能进行start、stop调用
  1426. rec.start();
  1427. };
  1428. stopButton.addEventListener('click', async () => {
  1429. // recorder.stop();
  1430. // uploadAudio(audioBlob);
  1431. recStop();
  1432. });
  1433. window.onload = function () {
  1434. recOpen();
  1435. };
  1436. // 錄音程式
  1437. // =======================
  1438. var rec, wave, recBlob;
  1439. /**调用open打开录音请求好录音权限 Call open to open the recording and request the recording permission**/
  1440. var recOpen = function () {//一般在显示出录音按钮或相关的录音界面时进行此方法调用,后面用户点击开始录音时就能畅通无阻了
  1441. rec = null;
  1442. wave = null;
  1443. recBlob = null;
  1444. var newRec = Recorder({
  1445. type: "mp3", sampleRate: 16000, bitRate: 16 //mp3格式,指定采样率hz、比特率kbps,其他参数使用默认配置;注意:是数字的参数必须提供数字,不要用字符串;需要使用的type类型,需提前把格式支持文件加载进来,比如使用wav格式需要提前加载wav.js编码引擎
  1446. , onProcess: function (buffers, powerLevel, bufferDuration, bufferSampleRate, newBufferIdx, asyncEnd) {
  1447. // // //录音实时回调,大约1秒调用12次本回调
  1448. document.querySelector(".recpowerx").style.width = powerLevel + "%";
  1449. document.querySelector(".recpowert").innerText = formatMs(bufferDuration, 1) + " / " + powerLevel;
  1450. // //可视化图形绘制
  1451. // wave.input(buffers[buffers.length - 1], powerLevel, bufferSampleRate);
  1452. }
  1453. });
  1454. newRec.open(function () {//打开麦克风授权获得相关资源
  1455. rec = newRec;
  1456. //此处创建这些音频可视化图形绘制浏览器支持妥妥的
  1457. // wave = Recorder.FrequencyHistogramView({ elem: ".recwave" });
  1458. reclog(Html_$T("GVCa::已打開錄音,可以點擊錄制開始錄音"), 2);
  1459. }, function (msg, isUserNotAllow) {//用户拒绝未授权或不支持
  1460. reclog((isUserNotAllow ? "UserNotAllow, " : "") + Html_$T("TOOV::打開錄音失敗:") + msg, 1);
  1461. });
  1462. };
  1463. /**关闭录音,释放资源 Close recording, release resources**/
  1464. function recClose() {
  1465. if (rec) {
  1466. rec.close();
  1467. reclog(Html_$T("jqOs::已关闭"));
  1468. } else {
  1469. reclog(Html_$T("VOOw::未打开录音"), 1);
  1470. };
  1471. };
  1472. /**开始录音 Start recording**/
  1473. function recStart() {//打开了录音后才能进行start、stop调用
  1474. if (rec && Recorder.IsOpen()) {
  1475. recBlob = null;
  1476. rec.start();
  1477. reclog(Html_$T("CGdy::已經開始錄音...") + " " + rec.set.type + " " + rec.set.sampleRate + " " + rec.set.bitRate + "kbps");
  1478. } else {
  1479. reclog(Html_$T("ajKR::未打开錄音"), 1);
  1480. };
  1481. };
  1482. /**暂停录音 Passing recording**/
  1483. function recPause() {
  1484. if (rec && Recorder.IsOpen()) {
  1485. rec.pause();
  1486. reclog(Html_$T("GvCy::已暂停"));
  1487. } else {
  1488. reclog(Html_$T("gCAR::未打開錄音"), 1);
  1489. };
  1490. };
  1491. /**恢复录音 Resume recording**/
  1492. function recResume() {
  1493. if (rec && Recorder.IsOpen()) {
  1494. rec.resume();
  1495. reclog(Html_$T("5q1K::继续录音中..."));
  1496. } else {
  1497. reclog(Html_$T("Ob6S::未打开录音"), 1);
  1498. };
  1499. };
  1500. /**结束录音,得到音频文件 Stop recording and get audio files**/
  1501. function recStop() {
  1502. if (!(rec && Recorder.IsOpen())) {
  1503. reclog(Html_$T("5JuL::未打開錄音"), 1);
  1504. return;
  1505. };
  1506. rec.stop(function (blob, duration) {
  1507. console.log(blob, (window.URL || webkitURL).createObjectURL(blob), Html_xT(Html_$T("gOix::時長:{1}ms", 0, duration)));
  1508. recBlob = blob;
  1509. var file = new File([blob], "recording.mp3", { type: "audio/mp3" });
  1510. console.log(file);
  1511. handleAudioToText(file);
  1512. reclog(Html_$T("0LHf::已錄製mp3:{1}ms {2}字節,可以點擊播放、上傳、本地下载了", 0, formatMs(duration), blob.size), 2);
  1513. }, function (msg) {
  1514. reclog(Html_$T("kGZO::錄音失敗:") + msg, 1);
  1515. });
  1516. };
  1517. /**播放 Play**/
  1518. function recPlay() {
  1519. if (!recBlob) {
  1520. reclog(Html_$T("tIke::请先錄音,然后停止後再播放"), 1);
  1521. return;
  1522. };
  1523. var cls = ("a" + Math.random()).replace(".", "");
  1524. reclog(Html_$T('GlWb::播放中: ') + '<span class="' + cls + '"></span>');
  1525. var audio = document.createElement("audio");
  1526. audio.controls = true;
  1527. document.querySelector("." + cls).appendChild(audio);
  1528. //简单利用URL生成播放地址,注意不用了时需要revokeObjectURL,否则霸占内存
  1529. audio.src = (window.URL || webkitURL).createObjectURL(recBlob);
  1530. audio.play();
  1531. setTimeout(function () {
  1532. (window.URL || webkitURL).revokeObjectURL(audio.src);
  1533. }, 5000);
  1534. };
  1535. /**上传 Upload**/
  1536. function recUpload() {
  1537. var blob = recBlob;
  1538. if (!blob) {
  1539. reclog(Html_$T("DUTn::请先录音,然后停止后再上传"), 1);
  1540. return;
  1541. };
  1542. //本例子假设使用原始XMLHttpRequest请求方式,实际使用中自行调整为自己的请求方式
  1543. //录音结束时拿到了blob文件对象,可以用FileReader读取出内容,或者用FormData上传
  1544. var api = "http://127.0.0.1:9528";
  1545. var onreadystatechange = function (xhr, title) {
  1546. return function () {
  1547. if (xhr.readyState == 4) {
  1548. if (xhr.status == 200) {
  1549. reclog(title + Html_$T("G2MU::上传成功") + ' <span style="color:#999">response: ' + xhr.responseText + '</span>', 2);
  1550. } else {
  1551. reclog(title + Html_$T("TUdi::没有完成上传,演示上传地址无需关注上传结果,只要浏览器控制台内Network面板内看到的请求数据结构是预期的就ok了。"), "#d8c1a0");
  1552. console.error(Html_xT(title + Html_$T("HjDi::上传失败")), xhr.status, xhr.responseText);
  1553. };
  1554. };
  1555. };
  1556. };
  1557. reclog(Html_$T("QnSI::开始上传到{1},请稍候... (你可以先到源码 /assets/node-localServer 目录内执行 npm run start 来运行本地测试服务器)", 0, api));
  1558. /***方式一:将blob文件转成base64纯文本编码,使用普通application/x-www-form-urlencoded表单上传***/
  1559. var reader = new FileReader();
  1560. reader.onloadend = function () {
  1561. var postData = "";
  1562. postData += "mime=" + encodeURIComponent(blob.type);//告诉后端,这个录音是什么格式的,可能前后端都固定的mp3可以不用写
  1563. postData += "&upfile_b64=" + encodeURIComponent((/.+;\s*base64\s*,\s*(.+)$/i.exec(reader.result) || [])[1]) //录音文件内容,后端进行base64解码成二进制
  1564. //...其他表单参数
  1565. var xhr = new XMLHttpRequest();
  1566. xhr.open("POST", api + "/uploadBase64");
  1567. xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
  1568. xhr.onreadystatechange = onreadystatechange(xhr, Html_$T("gG1f::上传方式一【Base64】"));
  1569. xhr.send(postData);
  1570. };
  1571. reader.readAsDataURL(blob);
  1572. /***方式二:使用FormData用multipart/form-data表单上传文件***/
  1573. var form = new FormData();
  1574. form.append("upfile", blob, "recorder.mp3"); //和普通form表单并无二致,后端接收到upfile参数的文件,文件名为recorder.mp3
  1575. //...其他表单参数
  1576. var xhr = new XMLHttpRequest();
  1577. xhr.open("POST", api + "/upload");
  1578. xhr.onreadystatechange = onreadystatechange(xhr, Html_$T("vDzB::上传方式二【FormData】"));
  1579. xhr.send(form);
  1580. };
  1581. /**本地下载 Local download**/
  1582. function recLocalDown() {
  1583. if (!recBlob) {
  1584. reclog(Html_$T("M86h::请先录音,然后停止后再下载"), 1);
  1585. return;
  1586. };
  1587. var cls = ("a" + Math.random()).replace(".", "");
  1588. recdown64.lastCls = cls;
  1589. reclog(Html_$T('vJPl::点击 ') + '<span class="' + cls + '"></span>' + Html_$T('Whtc:: 下载,或复制文本')
  1590. + '<button onclick="recdown64(\'' + cls + '\')">' + Html_$T('XK4l::生成Base64文本') + '</button><span class="' + cls + '_b64"></span>');
  1591. var fileName = "recorder-" + Date.now() + ".mp3";
  1592. var downA = document.createElement("A");
  1593. downA.innerHTML = Html_$T("g8Fy::下载 ") + fileName;
  1594. downA.href = (window.URL || webkitURL).createObjectURL(recBlob);
  1595. downA.download = fileName;
  1596. document.querySelector("." + cls).appendChild(downA);
  1597. if (/mobile/i.test(navigator.userAgent)) {
  1598. alert(Html_xT(Html_$T("DIEK::因移动端绝大部分国产浏览器未适配Blob Url的下载,所以本demo代码在移动端未调用downA.click()。请尝试点击日志中显示的下载链接下载")));
  1599. } else {
  1600. downA.click();
  1601. }
  1602. //不用了时需要revokeObjectURL,否则霸占内存
  1603. //(window.URL||webkitURL).revokeObjectURL(downA.href);
  1604. };
  1605. function recdown64(cls) {
  1606. var el = document.querySelector("." + cls + "_b64");
  1607. if (recdown64.lastCls != cls) {
  1608. el.innerHTML = '<span style="color:red">' + Html_$T("eKKx::老的数据没有保存,只支持最新的一条") + '</span>';
  1609. return;
  1610. }
  1611. var reader = new FileReader();
  1612. reader.onloadend = function () {
  1613. el.innerHTML = '<textarea></textarea>';
  1614. el.querySelector("textarea").value = reader.result;
  1615. };
  1616. reader.readAsDataURL(recBlob);
  1617. };
  1618. var formatMs = function (ms, all) {
  1619. var ss = ms % 1000; ms = (ms - ss) / 1000;
  1620. var s = ms % 60; ms = (ms - s) / 60;
  1621. var m = ms % 60; ms = (ms - m) / 60;
  1622. var h = ms;
  1623. var t = (h ? h + ":" : "")
  1624. + (all || h + m ? ("0" + m).substr(-2) + ":" : "")
  1625. + (all || h + m + s ? ("0" + s).substr(-2) + "″" : "")
  1626. + ("00" + ss).substr(-3);
  1627. return t;
  1628. };
  1629. // =======================
  1630. let mediaRecorder;
  1631. let audioChunks = [];
  1632. async function handleAudioToText(file) {
  1633. console.log('tts')
  1634. let audioLang = "cmn-Hant-TW"; // 音訊語言
  1635. // let lang = localStorage.getItem("lang");
  1636. let lang = "zh-tw";
  1637. console.log("lang", lang);
  1638. switch (lang) {
  1639. case "zh-tw":
  1640. audioLang = "cmn-Hant-TW";
  1641. break;
  1642. case "en-us":
  1643. audioLang = "en-US";
  1644. break;
  1645. case "ja-jp":
  1646. audioLang = "ja-JP";
  1647. break;
  1648. case "ko-kr":
  1649. audioLang = "ko-KR";
  1650. break;
  1651. default:
  1652. break;
  1653. }
  1654. let url = `https://bf18-61-230-0-215.ngrok-free.app/gcp/speech-to-text?language_code=${audioLang}`;
  1655. const formData = new FormData();
  1656. formData.append("file", file);
  1657. try {
  1658. const response = await axios.post(url, formData);
  1659. console.log("response", response);
  1660. input_text_value = response.data;
  1661. console.log(input_text_value[0]);
  1662. recording_block.classList.remove('show');
  1663. recording_block.classList.add('hidden');
  1664. get_data(input_text_value);
  1665. } catch (error) {
  1666. console.log("error", error);
  1667. }
  1668. }