console.log('text-to-chart'); var accessToken = localStorage.getItem("access_token"); console.log(accessToken); if (accessToken === null) { alert('請先登入') window.location.href = "./login.html"; } var usernameEmail = localStorage.getItem("username"); var unit_table_value var obj = { _text: '' }; // let username = usernameEmail.split('@')[0]; Chart.register(ChartDataLabels); var userNameSpan = document.getElementById("userName"); var lineYfontSize = document.getElementById("lineYfontSize"); userNameSpan.textContent = usernameEmail; var inputField = document.getElementById("keyword_data"); var inputField_compare = document.getElementById("keyword_data_compare"); var compare_box = document.getElementById("compare"); var sendButton = document.getElementById("send_data"); var send_data_compare = document.getElementById("data_compare"); var canvasPng = document.getElementById("textToChart"); var img_box = document.getElementById("img_box"); var sidebar = document.getElementById('style_setting'); var input_text_value; var chartType = 'line'; var unit = ''; var myChart = null; var chartColor = '#912B2B'; var chartColorBar = '#' var displayXaxes = true; var ctx; var chartlabels; var chartdata; var XfontSizeValue = 16; var YfontSizeValue = 18; var TitlefontSizeValue = 32; var pieFontSize = 14; var PieMaxWidth = 100; var pointRadiusValue = 0; var pointHoverRadiusValue = 0; var chart_bgimg_url = "url(./img/bg06.webp)"; var vocab = []; var vocab_compare = []; var chartDiv = document.getElementById('chartdiv'); var dataFirstValue; var dataLastValue; let cancelToken; let source; $('#chartdiv').hide(); var clickExample = document.getElementById('example_box') document.addEventListener('DOMContentLoaded', function () { axios.get("https://cmm.ai:8080/vocab") .then(response => { // apiHideLoading(); console.log(response); vocab = response.data; }) .catch(error => { console.error('發生錯誤:', error); }); }); // 比較提示詞 document.addEventListener('DOMContentLoaded', function () { axios.get("https://cmm.ai:8080/vocab_comparision") .then(response => { // apiHideLoading(); console.log(response); vocab_compare = response.data; }) .catch(error => { console.error('發生錯誤:', error); }); }); inputField.addEventListener('click', function () { // 確保輸入框處於焦點狀態 inputField.focus(); // 在控制台中顯示焦點狀態 console.log('Input field focused'); }); document.addEventListener('DOMContentLoaded', function () { // const input = document.getElementById('stockInput'); const suggestions = document.getElementById('suggestions'); inputField.addEventListener('input', function () { const query = this.value.trim(); suggestions.innerHTML = ''; // 清空之前的建議 console.log('query', query); if (query) { // const filteredVocab = vocab.filter(item => item.toLowerCase().includes(query)); const queryTerms = query.split(/\s+/); // 分割輸入的內容 const lastQueryTerm = queryTerms[queryTerms.length - 1]; const filteredVocab = vocab.filter(item => { // 對每個分割後的單詞進行匹配,只要其中有一個匹配成功就返回 true return item.toLowerCase().includes(lastQueryTerm.toLowerCase()); }); if (filteredVocab.length > 0) { suggestions.style.border = '1px solid #ccc'; suggestions.style.display = 'block'; // 顯示建議容器 } else { suggestions.style.border = 'none'; suggestions.style.display = 'none'; // 隱藏建議容器 } console.log(queryTerms[queryTerms.length - 1]) filteredVocab.forEach(item => { const div = document.createElement('div'); div.classList.add('autocomplete-suggestion'); div.textContent = item; // div.addEventListener('click', function () { // inputField.value = item; // suggestions.innerHTML = ''; // 清空建議 // suggestions.style.border = 'none'; // suggestions.style.display = 'none'; // 隱藏建議容器 // }); // console.log('queryTerms', queryTerms); // console.log('filteredVocab', filteredVocab); div.addEventListener('click', function () { queryTerms.forEach((term, index) => { const regex = new RegExp(term, 'gi'); const match = regex.exec(inputField.value); // console.log(queryTerms[queryTerms.length - 1]); // console.log(filteredVocab, queryTerms[queryTerms.length - 1]) if (index === queryTerms.length - 1) { if (match) { var start = match.index; var end = start + match[0].length; console.log(start, end) const textBefore = inputField.value.substring(0, start); const textAfter = inputField.value.substring(end, inputField.value.length); console.log('start', start, 'end', end, 'textBefore', textBefore, 'textAfter', textAfter); // 捕獲被選中的部分並替換它 const selectedText = inputField.value.substring(start, end); console.log('selectedText', selectedText); const newText = textBefore + item + textAfter.replace(selectedText, ''); console.log('newText', newText); inputField.value = newText; // inputField.value = textBefore + item.replace(selectedText, '') + textAfter; suggestions.innerHTML = ''; // 清空建議 suggestions.style.border = 'none'; } } }); // const start = inputField.selectionStart; // const end = inputField.selectionEnd; }); suggestions.appendChild(div); }); } else { suggestions.style.border = 'none'; suggestions.style.display = 'none'; // 隱藏建議容器 } }); }); document.addEventListener('DOMContentLoaded', function () { // const input = document.getElementById('stockInput'); const suggestions_compare = document.getElementById('suggestions_compare'); inputField_compare.addEventListener('input', function () { const query = this.value.trim(); suggestions_compare.innerHTML = ''; // 清空之前的建議 console.log('query', query); if (query) { // const filteredVocab = vocab.filter(item => item.toLowerCase().includes(query)); const queryTerms = query.split(/\s+/); // 分割輸入的內容 const lastQueryTerm = queryTerms[queryTerms.length - 1]; const filteredVocab = vocab_compare.filter(item => { // 對每個分割後的單詞進行匹配,只要其中有一個匹配成功就返回 true return item.toLowerCase().includes(lastQueryTerm.toLowerCase()); }); if (filteredVocab.length > 0) { suggestions_compare.style.border = '1px solid #ccc'; suggestions_compare.style.display = 'block'; // 顯示建議容器 } else { suggestions_compare.style.border = 'none'; suggestions_compare.style.display = 'none'; // 隱藏建議容器 } console.log(queryTerms[queryTerms.length - 1]) filteredVocab.forEach(item => { const div = document.createElement('div'); div.classList.add('autocomplete-suggestion'); div.textContent = item; // div.addEventListener('click', function () { // inputField.value = item; // suggestions.innerHTML = ''; // 清空建議 // suggestions.style.border = 'none'; // suggestions.style.display = 'none'; // 隱藏建議容器 // }); // console.log('queryTerms', queryTerms); // console.log('filteredVocab', filteredVocab); div.addEventListener('click', function () { queryTerms.forEach((term, index) => { const regex = new RegExp(term, 'gi'); const match = regex.exec(inputField_compare.value); // console.log(queryTerms[queryTerms.length - 1]); // console.log(filteredVocab, queryTerms[queryTerms.length - 1]) if (index === queryTerms.length - 1) { if (match) { var start = match.index; var end = start + match[0].length; console.log(start, end) const textBefore = inputField_compare.value.substring(0, start); const textAfter = inputField_compare.value.substring(end, inputField_compare.value.length); console.log('start', start, 'end', end, 'textBefore', textBefore, 'textAfter', textAfter); // 捕獲被選中的部分並替換它 const selectedText = inputField_compare.value.substring(start, end); console.log('selectedText', selectedText); const newText = textBefore + item + textAfter.replace(selectedText, ''); console.log('newText', newText); inputField_compare.value = newText; // inputField.value = textBefore + item.replace(selectedText, '') + textAfter; suggestions_compare.innerHTML = ''; // 清空建議 suggestions_compare.style.border = 'none'; } } }); // const start = inputField.selectionStart; // const end = inputField.selectionEnd; }); suggestions_compare.appendChild(div); }); } else { suggestions_compare.style.border = 'none'; suggestions_compare.style.display = 'none'; // 隱藏建議容器 } }); }); // 監聽輸入框的鍵盤事件 // document.getElementById("keyword_data").addEventListener("keyup", function (event) { // // 判斷是否按下 Enter 鍵 (key code: 13) // if (event.keyCode === 13) { // // 觸發送出按鈕的點擊事件 // document.getElementById("send_data").click(); // } // }); // 定義按鈕點擊事件處理函數 function sendButtonClickHandler() { // inputField.value = ""; console.log(inputField.value); var input_text_value = inputField.value; // input_text_value = '台積電近兩週收盤價' compare_box.style.display = "none"; $('#chartdiv').hide(); sidebar.classList.remove('show'); sidebar.classList.add('hidden'); // 在這裡添加你希望在按下按鈕時執行的其他代碼 get_data(input_text_value); } sendButton.addEventListener("click", function () { sendButtonClickHandler(); inputField.value = '' unit_value_box.style.display = "none" chartTypeBtn.style.display = "none"; }); let labels2 = []; let data2 = []; // 漲幅or單價 const checkbox1 = document.getElementById('checkbox1'); const checkbox2 = document.getElementById('checkbox2'); var compare_type_input = document.querySelector('.checkbox-group') var compare_type = ""; checkbox1.addEventListener('change', () => { if (checkbox1.checked) { checkbox2.checked = false; compare_type = '漲幅' console.log(compare_type); } }); checkbox2.addEventListener('change', () => { if (checkbox2.checked) { checkbox1.checked = false; compare_type = '單價' console.log(compare_type); } }); send_data_compare.addEventListener("click", function () { console.log('比較', inputField_compare.value); const stockName = inputField_compare.value; axios .get(`https://cmm.ai:8080/get_data_from_date?start_date=${dataFirstValue}&end_date=${dataLastValue}&stock_name=${inputField_compare.value}`) .then((response) => { console.log(response); var compare_data = response.data.data; labels2 = []; // Clear previous labels data2 = []; // Clear previous data if (compare_type === "") { alert('請輸入比較類型'); return } // console.log('比較送出後 ', chartType) if (response.data.data === "無法產生圖表") { alert('無法產生圖表') return } if (chartType === 'line') { const colors = [ '#82a33f', // rgba already '#2f487b', // rgba already '#df8c49', // hex #df8c49 '#72598f', // hex #72598f '#489fb6', // hex #489fb6 '#99b0d5' // hex #99b0d5 ]; let colorIndex = 0; for (var i = 0; i < compare_data.length; i++) { var item = compare_data[i]; // 遍历当前项的属性 for (var key in item) { // 如果属性名不在 labels 数组中,并且属性值不是对象,则将属性名添加到 labels 数组中 if (typeof item[key] === 'string') { labels2.push(item[key]); } // 如果属性值是数值类型,则将其添加到 data 数组中 if (typeof item[key] === 'number') { data2.push(item[key]); } } } function getRandomColor() { const randomIndex = Math.floor(Math.random() * colors.length); const selectedColor = colors[randomIndex]; colors.splice(randomIndex, 1); // 从数组中移除已选颜色 return selectedColor; } const normalize = (prices) => { const initialPrice = prices[0]; // return prices.map(price => (price - initialPrice) / initialPrice * 100); return prices.map(price => { // Calculate the normalized price const normalized = (price - initialPrice) / initialPrice * 100; // Round to 2 decimal places and convert back to a number return parseFloat(normalized.toFixed(2)); }); }; var stock2Normalized; var stock1Normalized; if (compare_type === '漲幅') { console.log('漲幅') stock2Normalized = normalize(data2); stock1Normalized = normalize(data); unitInput.value = '%'; unit_value.textContent = '%' } else { console.log('單價') unitInput.value = unitInput.value; unit_value.textContent = unitInput.value; stock2Normalized = data2; stock1Normalized = data; } const color = getRandomColor(); if (response.data.data.length < 15) { pointRadiusValue = 5; pointHoverRadiusValue = 0; togglePoints.checked = true; } const newDataset = { label: stockName, backgroundColor: color, pointRadius: pointRadiusValue, pointHoverRadius: 0, pointBorderColor: '#fff', pointBorderWidth: 3, pointHoverRadiusValue: 8, borderColor: color, data: stock2Normalized, fill: false }; colorIndex = (colorIndex + 1) % colors.length; myChart.data.datasets.push(newDataset); myChart.data.datasets[0].data = stock1Normalized; console.log('多筆資料', myChart.data.datasets); // console.log('比較', myChart.options.plugins.legend); myChart.options.plugins.legend.display = true; // myChart.defaults.global.legend = { // display: true, // position: 'top', // 默认图例位置为顶部 // labels: { // fontColor: 'rgb(255, 99, 132)', // 默认图例标签颜色 // fontSize: 14 // 默认图例标签字体大小 // } // }; myChart.update(); inputField_compare.value = ''; compare_type_input.style.display = 'none' } else if (chartType === 'bar') { } }) .catch((error) => console.log(error) ); }); // sendButton.addEventListener("click", function () { // console.log(inputField.value); // input_text_value = inputField.value; // // if (myChart) { // // console.log('已存在') // // data = []; // // labels = []; // // myChart.removePlugin(Chart.pluginService.getPlugin('afterDraw')); // // myChart.destroy(); // // } // get_data(input_text_value); // }); var lastKeyPressTime = 0; var enterCount = 0; var ENTER_THRESHOLD = 500; // 設定連續按下 Enter 的時間閾值(毫秒) inputField.addEventListener("keyup", function (event) { // 判斷是否按下 Enter 鍵 (key code: 13) if (event.key === "Enter") { var currentTime = new Date().getTime(); // 計算與上一次按鍵按下時間的差值 var timeDiff = currentTime - lastKeyPressTime; lastKeyPressTime = currentTime; // 如果兩次 Enter 鍵按下的時間差小於閾值,則增加計數器 if (timeDiff <= ENTER_THRESHOLD) { enterCount++; // 如果計數器為2,則觸發 API 請求並重置計數器 if (enterCount === 2) { sendButtonClickHandler(); enterCount = 0; } } else { // 如果時間差大於閾值,則重置計數器 enterCount = 0; } } else { // 如果按下的不是 Enter 鍵,則重置計數器 enterCount = 0; } }); function number_format(number, decimals, dec_point, thousands_sep) { // * example: number_format(1234.56, 2, ',', ' '); // * return: '1 234,56' number = (number + '').replace(',', '').replace(' ', ''); var n = !isFinite(+number) ? 0 : +number, prec = !isFinite(+decimals) ? 0 : Math.abs(decimals), sep = (typeof thousands_sep === 'undefined') ? ',' : thousands_sep, dec = (typeof dec_point === 'undefined') ? '.' : dec_point, s = '', toFixedFix = function (n, prec) { var k = Math.pow(10, prec); return '' + Math.round(n * k) / k; }; // Fix for IE parseFloat(0.55).toFixed(0) = 0; s = (prec ? toFixedFix(n, prec) : '' + Math.round(n)).split('.'); if (s[0].length > 3) { s[0] = s[0].replace(/\B(?=(?:\d{3})+(?!\d))/g, sep); } if ((s[1] || '').length < prec) { s[1] = s[1] || ''; s[1] += new Array(prec - s[1].length + 1).join('0'); } return s.join(dec); } var labels = []; var data = []; function apiLoading() { document.getElementById('chartDataLoading').style.display = 'block'; } function apiHideLoading() { document.getElementById('chartDataLoading').style.display = 'none'; } const DISPLAY = true; const BORDER = true; const CHART_AREA = true; const TICKS = true; var labels = []; var data = []; var dataArray; // get_data('2330近兩週收盤價'); function generateChart(dataArray) { if (dataArray.length > 15) { lineYfontSize.style.display = 'none' } else { lineYfontSize.style.display = 'block' } // 创建一个空数组来存储 labels 和 data console.log(dataArray) // 遍历数据数组 for (var i = 0; i < dataArray.length; i++) { var item = dataArray[i]; // 遍历当前项的属性 for (var key in item) { // 如果属性名不在 labels 数组中,并且属性值不是对象,则将属性名添加到 labels 数组中 if (typeof item[key] === 'string') { labels.push(item[key]); } else { if (key === 'x') { labels.push(String(item[key])); } } if (key === 'y') { data.push(item[key]); } // 如果属性值是数值类型,则将其添加到 data 数组中 // if (typeof item[key] === 'number') { // data.push(item[key]); // } } } console.log(data); dataFirstValue = labels[0]; dataLastValue = labels[labels.length - 1] console.log('dataFirstValue', dataFirstValue); console.log('dataLastValue', dataLastValue); if (myChart) { console.log('已存在') myChart.destroy(); } if (chartType === "doughnut") { // createChartDoughnut(chartType, data, labels); // createChartDoughnut2(data, labels) } else { createChart(chartType, data, labels); } // console.log(data); console.log(labels); } var amChartInstance; function extractAndGenerateChart(dataArray) { for (var i = 0; i < dataArray.length; i++) { var item = dataArray[i]; // 遍历当前项的属性 for (var key in item) { // 如果属性名不在 labels 数组中,并且属性值不是对象,则将属性名添加到 labels 数组中 if (typeof item[key] === 'string') { labels.push(item[key]); } // 如果属性值是数值类型,则将其添加到 data 数组中 if (typeof item[key] === 'number') { data.push(item[key]); } } } // console.log('key', key); generateBarChart(key, dataArray); } // 圓餅圖 function generatePieChart(dataArray, pieFontSize, PieMaxWidth) { compare_box.style.display = "none"; $('#chartdiv').show(); bgImgelement.style.backgroundImage = chart_bgimg_url; // chartDiv.style.width = "100%" chartWhitelement.style.background = "rgba(255,255,255,0.5)" // Themes begin am4core.useTheme(am4themes_animated); // Themes end // Create chart instance var chart = am4core.create("chartdiv", am4charts.PieChart); chart.data = dataArray; chart.innerRadius = am4core.percent(50); // Add and configure Series var pieSeries = chart.series.push(new am4charts.PieSeries()); pieSeries.dataFields.value = "y"; pieSeries.dataFields.category = "x"; pieSeries.slices.template.stroke = am4core.color("#fff"); pieSeries.slices.template.strokeWidth = 2; pieSeries.slices.template.strokeOpacity = 1; // Configure labels pieSeries.labels.template.wrap = true; console.log('PieMaxWidth', PieMaxWidth); pieSeries.labels.template.maxWidth = PieMaxWidth; pieSeries.labels.template.truncate = false; pieSeries.labels.template.fontSize = 14; if (typeof PieMaxWidth === 'string') { console.log('The variable is a string.'); } else { console.log('The variable is a number.'); } // Configure label text to wrap and show percentages // pieSeries.labels.template.adapter.add("textOutput", function (text, target) { // if (target.dataItem && target.dataItem.values.value.percent) { // return "[font-size: 14px]" + target.dataItem.category + ": " + target.dataItem.values.value.percent.toFixed(1) + "%"; // } // return text; // }); pieSeries.labels.template.adapter.add("textOutput", function (text, target) { if (target.dataItem && target.dataItem.values.value.percent) { return "[font-size: " + pieFontSize + "px]" + target.dataItem.category + ": " + target.dataItem.values.value.percent.toFixed(1) + "%"; } return text; }); pieSeries.colors.list = [ am4core.color('rgb(171, 51, 49)'), am4core.color('rgb(34, 83, 149)'), am4core.color('rgb(79, 148, 65)'), am4core.color('rgb(217, 195, 105)'), am4core.color('rgb(142, 124, 180)'), am4core.color('rgb(211, 183, 144)'), am4core.color('rgb(83, 84, 84)'), am4core.color('rgb(229, 147, 152)') ]; // Ensure labels have background // pieSeries.labels.template.background.fillOpacity = 1; console.log(pieSeries.labels.template) // Set label background color to match corresponding slice color pieSeries.labels.template.adapter.add("background.fill", function (fill, target) { return target.dataItem.slice.fill; }); // This creates initial animation pieSeries.hiddenState.properties.opacity = 1; pieSeries.hiddenState.properties.endAngle = -90; pieSeries.hiddenState.properties.startAngle = -90; // console.log('調位置') downloadButton.style.display = "inline-block"; chartTypeBtn.style.display = "inline-block"; } // var colorEven='#288D97'; // var colorOdd='#427D7E' function adjustColorBrightness(color, amount) { let usePound = false; if (color[0] == "#") { color = color.slice(1); usePound = true; } let num = parseInt(color, 16); let r = (num >> 16) + amount; let g = ((num >> 8) & 0x00FF) + amount; let b = (num & 0x0000FF) + amount; if (r > 255) r = 255; else if (r < 0) r = 0; if (g > 255) g = 255; else if (g < 0) g = 0; if (b > 255) b = 255; else if (b < 0) b = 0; return (usePound ? "#" : "") + (r << 16 | g << 8 | b).toString(16).padStart(6, '0'); } // 柱狀圖 function generateBarChart(key, dataArray) { console.log('generateBarChart', dataArray) chartTypeBtn.style.display = "inline-block"; compare_box.style.display = "none"; $('#chartdiv').show(); // var colorEven = document.getElementById('colorEven').value; // var colorOdd = document.getElementById('colorOdd').value; var baseColor = document.getElementById('borderColorInputBar').value; var categoryAxisFzValue = document.getElementById('XfontSizeBar').value; var valueAxisFzValue = document.getElementById('YfontSizeBar').value; var colorEven = am4core.color(baseColor); var colorOdd = adjustColorBrightness(baseColor, -20); chartDiv.style.fontFamily = "Arial, sans-serif"; chartDiv.style.fontWeight = "900"; bgImgelement.style.backgroundImage = chart_bgimg_url; chartWhitelement.style.background = "rgba(255,255,255,0.5)" console.log('labelDependent', labelDependent); // Themes begin am4core.useTheme(am4themes_animated); // Themes end // Create chart instance var chart = am4core.create("chartdiv", am4charts.XYChart3D); chart.data = dataArray; chart.angle = 30; // 圖表角度 chart.depth = 25; // 圖表深度 let categoryAxis = chart.xAxes.push(new am4charts.CategoryAxis()); categoryAxis.dataFields.category = "x"; categoryAxis.renderer.labels.template.rotation = 0; categoryAxis.renderer.labels.template.hideOversized = false; categoryAxis.renderer.minGridDistance = 20; // 間距 // categoryAxis.renderer.labels.template.horizontalCenter = "left"; // categoryAxis.renderer.labels.template.verticalCenter = "middle"; // categoryAxis.tooltip.label.rotation = 90; // categoryAxis.tooltip.label.horizontalCenter = "right"; // categoryAxis.tooltip.label.verticalCenter = "middle"; // label 換行 let label = categoryAxis.renderer.labels.template; label.wrap = true; if (dataArray.length > 8) { console.log('資料長度5') label.maxWidth = 50; } else if (dataArray.length < 7) { label.maxWidth = 80; } else { label.maxWidth = 100; console.log('資料長度<5') } // x軸字體大小 categoryAxis.renderer.labels.template.fontSize = categoryAxisFzValue; categoryAxis.renderer.cellEndLocation = 0.5; // 減少值可以增加間距 let valueAxis = chart.yAxes.push(new am4charts.ValueAxis()); valueAxis.title.text = labelDependent; // Y軸 valueAxis.title.fontWeight = "bold"; // y軸字體大小 valueAxis.renderer.labels.template.fontSize = valueAxisFzValue; // Create series var series = chart.series.push(new am4charts.ColumnSeries3D()); series.dataFields.valueY = 'y'; series.dataFields.categoryX = "x"; series.name = "y"; series.tooltipText = "{categoryX}: [bold]{valueY}[/]"; series.columns.template.fillOpacity = .8; let columnTemplate = series.columns.template; columnTemplate.strokeWidth = 2; columnTemplate.strokeOpacity = 1; columnTemplate.stroke = am4core.color("#FFFFFF"); // columnTemplate.adapter.add("fill", function (fill, target) { // return chart.colors.getIndex(target.dataItem.index); // }) // 設置基數和偶數顏色 columnTemplate.adapter.add("fill", function (fill, target) { return target.dataItem.index % 2 === 0 ? am4core.color(colorEven) : am4core.color(colorOdd); }); columnTemplate.adapter.add("stroke", function (stroke, target) { return chart.colors.getIndex(target.dataItem.index); }) // // 加上白色線條 // series.columns.template.events.on("validated", function (event) { // let column = event.target; // let whiteLine = column.createChild(am4core.Line); // whiteLine.stroke = am4core.color("#ffffff"); // whiteLine.strokeWidth = 1.3; // 線條寬度 // whiteLine.strokeOpacity = 0.5;// 透明度 // whiteLine.x1 = column.pixelWidth / 1; // whiteLine.y1 = 0; // whiteLine.x2 = column.pixelWidth / 1; // whiteLine.y2 = column.pixelHeight; // }); chart.cursor = new am4charts.XYCursor(); chart.cursor.lineX.strokeOpacity = 0; chart.cursor.lineY.strokeOpacity = 0; categoryAxis.renderer.grid.template.strokeOpacity = 0; // 隱藏 X 軸 // valueAxis.renderer.grid.template.strokeOpacity = 0; // 隱藏 Y 軸 // valueAxis.renderer.line.strokeOpacity = 0; // LineSeries 底部線條 var lineSeries = chart.series.push(new am4charts.LineSeries()); lineSeries.dataFields.valueY = "bottom"; lineSeries.dataFields.categoryX = "0"; lineSeries.stroke = am4core.color("#FFFFFF") lineSeries.strokeWidth = 2; lineSeries.strokeOpacity = 1 var bottomData = []; for (var i = 0; i < dataArray.length; i++) { bottomData.push({ "x": dataArray[i].x, "bottom": 0 }); } lineSeries.data = bottomData; downloadButton.style.display = "inline-block"; chartTypeBtn.style.display = "inline-block"; chartTypeBtn.style.opacity = "1"; // 底部線條 // let rectElements = document.querySelectorAll("rect"); // // let widthValue = 0; // let fifthRectElement = rectElements[3]; // // fifthRectElement.style.position = "relative"; // setTimeout(() => { // let width = fifthRectElement.getAttribute("width"); // // widthValue = parseFloat(width); // console.log('rectElements', rectElements); // console.log('fifthRectElement', fifthRectElement); // console.log('width', width); // let line = document.createElement("div"); // line.style.position = "absolute"; // line.style.bottom = "162px"; // line.style.right = "40px"; // line.style.width = `${parseFloat(width)}px`; // line.style.height = "2px"; // line.style.backgroundColor = "#000000"; // document.getElementById("chartdiv").appendChild(line); // }, 10) // end am4core.ready() } function handleClick(element) { const Clickvalue = element.dataset.value; console.log(Clickvalue); // 123 get_data(Clickvalue); } // 圖表類型 var chartTypeBtn = document.getElementById("chartType"); chartTypeBtn.addEventListener('change', function () { const selectedValue = this.value; console.log('選擇的圖表類型:', selectedValue); obj.text = selectedValue; // 在此处添加处理选定图表类型的代码 if (selectedValue === 'line') { // 处理折线图 console.log('處理折線圖', dataArray); $('#chartdiv').hide(); chartType = 'line'; if (myChart) { data = []; labels = []; myChart.destroy(); } sidebar.classList.add('hidden'); generateChart(dataArray); } else if (selectedValue === 'bar') { chartType = 'bar'; $('#textToChart').hide(); // 处理柱状图 console.log('處理柱狀圖', dataArray); sidebar.classList.add('hidden'); extractAndGenerateChart(dataArray); } else if (selectedValue === 'doughnut') { chartType = 'doughnut'; console.log('處理圓餅圖', dataArray); unit_value.textContent = ""; $('#textToChart').hide(); sidebar.classList.add('hidden'); generatePieChart(dataArray, pieFontSize, PieMaxWidth) // 处理圆饼图 } }); var downloadButton = document.getElementById("downloadButton"); // downloadButton.style.display = 'none'; // 添加點擊事件監聽器 downloadButton.addEventListener('click', function () { setTimeout(function () { // html2canvas(document.getElementById('data_chart_box')).then(function (canvas) { // // // 獲取圖片資料 URL // // var imageData = canvasPng.toDataURL("image/png"); // // // 創建下載連結 // // var link = document.createElement('a'); // // link.download = 'chart.png'; // 下載的檔案名稱 // // link.href = imageData; // 圖片資料 URL // // // 模擬點擊下載連結 // // link.click(); // // 創建一個<a>元素 // const link = document.createElement('a'); // // 將 Canvas 轉換為 Data URL // link.href = canvas.toDataURL('image/png'); // // 設置下載文件名 // link.download = 'chart.png'; // // 模擬點擊下載 // link.click(); // }); html2canvas(document.getElementById('data_chart_box'), { backgroundColor: null, // 確保背景透明 scale: 2, useCORS: true, scrollX: 0, scrollY: 0 }).then(function (canvas) { // 創建一個 元素 const link = document.createElement('a'); // 將 Canvas 轉換為 Data URL link.href = canvas.toDataURL('image/png'); // 設置下載文件名 link.download = 'chart.png'; // 模擬點擊下載 link.click(); }); }, 1000); // 等待 1 秒钟 }); var pieChartColors = [ 'rgb(171, 51, 49)', 'rgb(34, 83, 149)', 'rgb(79, 148, 65)', 'rgb(217, 195, 105)', 'rgb(142, 124, 180)', 'rgb(211, 183, 144)', 'rgb(83, 84, 84)', 'rgb(229, 147, 152)' ]; function createChartDoughnut2(data, labels) { const chartData = { type: 'outlabeledPie', data: { labels: labels, datasets: [ { data: data, borderWidth: borderWidthValue, fill: false, backgroundColor: pieChartColors, }, ], }, options: { plugins: { legend: false, outlabels: { text: '%l %p', color: 'white', stretch: 35, font: { resizable: true, family: 'Arial', size: 48, weight: 'bold', style: 'italic', }, }, }, }, }; const devicePixelRatio = 4; var chartUrl = `https://quickchart.io/chart?c=${encodeURIComponent(JSON.stringify(chartData))}&devicePixelRatio=${devicePixelRatio}&f=png&fontsize=20`; // 获取目标 div 元素 const targetDiv = document.getElementById('data_chartJs_box'); // 创建一个 img 元素来显示图表 const chartImg = document.createElement('img'); chartImg.src = chartUrl; chartImg.classList.add('outlabeled_img'); // 清空目标 div 元素 targetDiv.innerHTML = ''; // 将图表 img 元素插入到目标 div 中 targetDiv.appendChild(chartImg); console.log(chart_bgimg_url); bgImgelement.style.backgroundImage = chart_bgimg_url; chartWhitelement.style.background = "rgba(255,255,255,0.5)" } // 版型二源餅圖 // // 创建 Chart.js 图表 function createChartDoughnut(chartType, data, labels) { $('#textToChart').show(); canvasPng.style.maxWidth = '450px'; canvasPng.style.maxHeight = '450px'; var windowWidth = window.innerWidth || document.documentElement.clientWidth; var canvasWidth = Math.min(500, windowWidth); // 限制最大宽度为 500px canvasPng.style.width = canvasWidth + 'px'; // 设置 canvas 宽度 canvasPng.style.marginLeft = 'auto'; // 左外边距自动计算 canvasPng.style.marginRight = 'auto'; // 右外边距自动计算 var ctx = document.getElementById("textToChart").getContext('2d'); myChart = new Chart(ctx, { type: chartType, data: { labels: labels, datasets: [{ strokeColor: "rgba(220,220,220,1)", StrokeWidth: 5, data: data, borderWidth: borderWidthValue, pointRadius: 0, pointBorderColor: '#fff', pointBorderWidth: 3, pointHoverRadius: 0, fill: false, backgroundColor: pieChartColors, // barPercentage: 0.5, // categoryPercentage: 0.5 }] }, options: { // responsive: true, // 啟用響應式 maintainAspectRatio: true, responsive: true, aspectRatio: 1, plugins: { legend: { display: false, }, datalabels: { color: '#fff', // 数据标签的颜色 anchor: 'end', // 数据标签的位置 align: 'start', // 数据标签的对齐方式 font: { size: 14, // 数据标签的字体大小 }, } }, scales: { x: { display: false, // 显示 x 轴 grid: { display: false, lineWidth: 5, color: '#584B3D' }, ticks: { textStrokeColor: '#fff', textStrokeWidth: 5, font: { display: false, family: 'Arial', // 设置字体 size: XfontSizeValue, // 设置字体大小 weight: 'bold', // 设置字体粗细 fontColor: 'rgba(255,255,255,0.8)', // X-axis font color shadowColor: 'rgba(0, 0, 0, 0.5)', // Shadow color shadowBlur: 10, // Shadow blur level shadowOffsetX: 5, // Horizontal shadow offset shadowOffsetY: 5 }, }, }, y: { display: false, beginAtZero: false, grid: { display: false, lineWidth: 5, color: '#584B3D' }, ticks: { textStrokeColor: '#fff', textStrokeWidth: 5, font: { family: 'Arial', // 設置字體 size: XfontSizeValue, // 設置字體大小 weight: 'bold', // 設置字體粗細 color: 'rgba(255,255,255,0.8)', // Y-axis 字體顏色 shadowColor: 'rgba(0, 0, 0, 0.5)', // 陰影顏色 shadowBlur: 10, // 陰影模糊級別 shadowOffsetX: 5, // 水平陰影偏移 shadowOffsetY: 5 // 垂直陰影偏移 }, } } }, layout: { padding: { left: 50, // 调整图表左边距 right: 50, // 调整图表右边距 top: 10, bottom: 10, } }, }, }); console.log(chart_bgimg_url); bgImgelement.style.backgroundImage = chart_bgimg_url; chartWhitelement.style.background = "rgba(255,255,255,0.5)" } var chartJsBox = document.getElementById('data_chartJs_box'); function createTable(dataArray, unit, Label_dependent_variable, Label_independent_variable,) { $('#chartdiv').hide(); $('#textToChart').hide(); chartTitle.style.padding = "15px 0px 30px 30px"; bgImgelement.style.paddingTop = "10px"; var table = document.createElement('table'); table.classList.add('table', 'table_template', 'dynamic-table'); // 添加 Bootstrap 的 table 样式,如果你在使用 Bootstrap // 创建表头 var thead = table.createTHead(); var headerRow = thead.insertRow(); // Object.keys(dataArray[0]).forEach(function (key) { // var header = headerRow.insertCell(); // header.textContent = key; // }); var yearHeader = headerRow.insertCell(); yearHeader.textContent = Label_independent_variable; var avgPriceHeader = headerRow.insertCell(); avgPriceHeader.textContent = Label_dependent_variable; // 创建表格主体 var tbody = table.createTBody(); dataArray.forEach(function (item) { console.log(item) var row = tbody.insertRow(); // var cell1 = row.insertCell(); // cell1.textContent = item.year; // cell1.classList.add('year-class'); // 为年份单元格添加类 // var cell2 = row.insertCell(); Object.keys(item).forEach(function (key, index) { var cell = row.insertCell(); if (index === 0) { // If it's the first cell, add 'year-class' cell.textContent = item[key]; cell.classList.add('year-class'); } else { // Otherwise, add 'average-class' if (key === 'Average_Close') { // cell.textContent = item[key].toFixed(2) + unit; cell.innerHTML = item[key].toFixed(2) + '' + unit + ''; } else { // cell.textContent = item[key]; // cell.textContent = item[key].toFixed(2) + unit; cell.innerHTML = item[key].toFixed(2) + '' + unit + ''; } cell.classList.add('average-class'); } }); // console.log(item.avg_price.toFixed(2)) // cell.textContent = item[key].toFixed(2) + unit; // cell2.classList.add('average-class'); // 为年份单元格添加类 }); // 获取包含表格的 div 元素 bgImgelement.style.backgroundImage = chart_bgimg_url; img_box.style.bottom = '60px'; img_box.style.right = '5px'; document.getElementById('img_box_url').style.width = "180px"; // chartWhitelement.style.background = "rgba(255,255,255,0.5)" // 将表格添加到指定的 div 元素中 chartJsBox.appendChild(table); unit_table_value = document.querySelectorAll('.unit_table'); downloadButton.style.display = "inline-block"; chartTypeBtn.style.display = "inline-block"; chartTypeBtn.style.opacity = "0"; } function getDatalabelsConfig(dataLength) { if (dataLength < 10) { return { formatter: function (value, context) { return value + ' unit'; // Add unit if data length is less than 10 }, textStrokeColor: '#fff', textStrokeWidth: 5, color: '#584B3D', font: { size: YfontSizeValue, }, anchor: 'end', align: 'end', }; } else { return null; // No datalabels if data length is 10 or more } } function getDatalabelsConfig(isEnabled) { if (!isEnabled) { return null; } return data.length <= 15 ? { formatter: function (value, context) { return value; }, textStrokeColor: '#fff', textStrokeWidth: 5, color: '#584B3D', font: { size: YfontSizeValue, }, anchor: 'end', align: 'top' } : null; } // 版型一折線圖 // // 创建 Chart.js 图表 function createChart(chartType, data, labels) { $('#textToChart').show(); canvasPng.style.maxWidth = ''; canvasPng.style.maxHeight = ''; console.log('繪製圖表', data, labels); if (data.length < 15) { pointRadiusValue = 5; pointHoverRadiusValue = 0; togglePoints.checked = true; toggleValue.checked = true; } else { pointRadiusValue = 0; togglePoints.checked = false; toggleValue.checked = false; } const datalabelsConfig = data.length <= 15 ? { formatter: function (value, context) { // if (context.dataIndex === 0) { // return ''; // } // if (typeof value === 'number' && value.toString().includes('.')) { // value = value.toFixed(4); // } // return value; return value; }, textStrokeColor: '#fff', textStrokeWidth: 5, color: '#584B3D', font: { size: YfontSizeValue, }, anchor: 'end', align: 'end',//固定 offset: 5, } : null; ctx = document.getElementById("textToChart"); myChart = new Chart(ctx, { type: chartType, data: { labels: labels, datasets: [{ label: stocknumber, strokeColor: "rgba(220,220,220,1)", StrokeWidth: 5, data: data, borderWidth: borderWidthValue, pointRadius: pointRadiusValue, pointBorderColor: '#fff', pointBorderWidth: 3, // pointHoverRadius: 10, fill: false, backgroundColor: chartColor, borderColor: chartColor, hitRadius: 10, // barPercentage: 0.5, // categoryPercentage: 0.5 }] }, options: { responsive: true, // 啟用響應式 maintainAspectRatio: false, plugins: { datalabels: datalabelsConfig, backgroundColor: 'transparent', // datalabels: { // formatter: function (value, context) { // // 添加单位 // return value; // }, // textStrokeColor: '#fff', // textStrokeWidth: 5, // color: '#584B3D', // font: { // size: YfontSizeValue, // }, // anchor: 'end', // align: 'end', // }, // datalabels: null, // datalabels: data.length <= 10 ? { // formatter: function (value, context) { // // 添加单位 // return value + ' ' + unit; // }, // textStrokeColor: '#fff', // textStrokeWidth: 5, // color: '#584B3D', // font: { // size: YfontSizeValue, // }, // anchor: 'end', // align: 'end', // } : null, // customLabels: null, legend: { display: false, fullSize: true, position: 'bottom', onClick: function (e, legendItem, legend) { e.native.stopImmediatePropagation(); // 獲取被點擊的數據集索引 selectedDatasetIndex = legendItem.datasetIndex; const dataset_color = legend.chart.data.datasets[selectedDatasetIndex].borderColor; console.log(dataset_color) colorInput.value = dataset_color; // // 手動觸發 Chart.js 的默認圖例點擊事件 // const defaultClickHandler = Chart.defaults.plugins.legend.onClick; // defaultClickHandler.call(this, e, legendItem, legend); } }, }, scales: { x: { display: displayXaxes, // 显示 x 轴 grid: { display: false, lineWidth: 5, color: '#584B3D' }, ticks: { textStrokeColor: '#fff', textStrokeWidth: 5, font: { display: false, family: 'Arial', // 设置字体 size: XfontSizeValue, // 设置字体大小 weight: 'bold', // 设置字体粗细 fontColor: 'rgba(255,255,255,0.8)', // X-axis font color shadowColor: 'rgba(0, 0, 0, 0.5)', // Shadow color shadowBlur: 10, // Shadow blur level shadowOffsetX: 5, // Horizontal shadow offset shadowOffsetY: 5 }, }, }, y: { display: true, beginAtZero: false, grid: { grid: { display: true, // 显示 y 轴网格线 lineWidth: 1, // 网格线宽度 color: '#C0C5CC' // 网格线颜色 }, }, ticks: { padding: 10, textStrokeColor: '#fff', textStrokeWidth: 5, font: { family: 'Arial', // 設置字體 size: XfontSizeValue, // 設置字體大小 weight: 'bold', // 設置字體粗細 color: 'rgba(255,255,255,0.8)', // Y-axis 字體顏色 shadowColor: 'rgba(0, 0, 0, 0.5)', // 陰影顏色 shadowBlur: 10, // 陰影模糊級別 shadowOffsetX: 5, // 水平陰影偏移 shadowOffsetY: 5 // 垂直陰影偏移 }, } } }, layout: { padding: { left: 50, // 调整图表左边距 right: 50, // 调整图表右边距 top: 40, bottom: 10, } }, onClick: function (event, elements) { if (elements.length > 0) { const element = elements[0]; selectedDatasetIndex = element.datasetIndex; const currentColor = myChart.data.datasets[selectedDatasetIndex].borderColor; const colorValue = currentColor; colorInput.value = colorValue; // 获取当前线条颜色并更新 colorPicker // colorInput.value = rgbToHex(currentColor); // console.log('當前顏色', colorValue); colorInput.disabled = false; // 启用 colorPicker } }, }, }); console.log('第一次', myChart.options.legend) console.log(chart_bgimg_url); bgImgelement.style.backgroundImage = chart_bgimg_url; chartWhitelement.style.background = "rgba(255,255,255,0.5)"; downloadButton.style.display = "inline-block"; chartTypeBtn.style.display = "inline-block"; chartTypeBtn.style.opacity = "1"; compare_box.style.display = "flex"; } // createChart(chartType); // createChart(chartType) const ChartOptions = document.querySelectorAll('input[name="ChartOptions"]'); ChartOptions.forEach(button => { button.addEventListener('click', function () { // console.log(this.value); chartType = this.value; console.log(chartType); if (myChart !== null) { myChart.destroy(); data = []; labels = []; } if (chartType === 'line') { $('#chartdiv').hide(); generateChart(dataArray); } else { $('#textToChart').hide(); extractAndGenerateChart(dataArray); } // createChart(chartType, data, labels); }); }); // 表格字體 var fontSizeInput = document.getElementById('table_fontSize'); // 添加事件监听器 fontSizeInput.addEventListener('input', function () { // 获取输入框的值 var fontSize = fontSizeInput.value; // 获取动态创建的表格元素 var dynamicTable = document.querySelector('.dynamic-table'); // 如果表格存在,则调整其字体大小 if (dynamicTable) { dynamicTable.style.fontSize = fontSize + 'px'; } }); // 圓餅圖字體 var pieFontSizeInput = document.getElementById('pie_fontSize'); // 添加事件监听器 pieFontSizeInput.addEventListener('input', function () { // 获取输入框的值 pieFontSize = pieFontSizeInput.value; console.log('圓餅圖字體', pieFontSize); if (pieFontSize) { generatePieChart(dataArray, pieFontSize, PieMaxWidth); } else { alert("Please enter a valid font size."); } // 如果表格存在,则调整其字体大小 // if (dynamicTable) { // dynamicTable.style.fontSize = fontSize + 'px'; // } }); document.getElementById('PieMaxWidthRange').addEventListener('input', function () { PieMaxWidth = Number(document.getElementById('PieMaxWidthRange').value); console.log('圓餅圖', PieMaxWidth) generatePieChart(dataArray, pieFontSize, PieMaxWidth); }); // 是否顯示point var togglePoints = document.getElementById('togglePoints'); togglePoints.addEventListener('change', function () { console.log('checked', "change") // 遍历所有数据集,应用相应的pointRadius和pointHoverRadius myChart.data.datasets.forEach(function (dataset) { if (togglePoints.checked) { dataset.pointRadius = 5; dataset.pointHoverRadius = 8; } else { dataset.pointRadius = 0; dataset.pointHoverRadius = 0; } }); myChart.update(); }); // 是否顯示數值 var toggleValue = document.getElementById('toggleValue'); let datalabelsConfig = getDatalabelsConfig(toggleValue.checked); toggleValue.addEventListener('change', function () { console.log('切換數值') // 根据复选框状态更新 datalabelsConfig datalabelsConfig = getDatalabelsConfig(this.checked); // 更新图表 myChart.options.plugins.datalabels = datalabelsConfig; myChart.update(); }); // 是否顯示x軸 const toggleXAxis = document.getElementById('toggleXAxis'); toggleXAxis.addEventListener('change', function () { // 根据复选框的状态来显示或隐藏 X 轴 myChart.options.scales.x.ticks.display = this.checked; // 更新图表 myChart.update(); }); // 是否顯示單位 var toggleUnit = document.getElementById('toggleUnit'); toggleUnit.addEventListener('change', function () { if (this.checked) { unit_value_box.style.display = 'block'; } else { unit_value_box.style.display = 'none'; } }); // 單位 // ========================= var unitInput = document.getElementById('unit_data'); var unit_value = document.querySelector('.unit'); var unit_value_box = document.querySelector('#unit_box'); // console.log('單位', unitInput) unitInput.addEventListener('input', function () { // unitInput.value = unit_value.textContent; if (chartType === 'table') { // unit_table_value.textContent = this.value; unit_table_value.forEach(span => { span.textContent = this.value; }); } else { unit_value.textContent = this.value; } }); // ========================== // y軸字體大小-折線圖 // ========================== var YfontSize = document.getElementById('YfontSize'); YfontSize.addEventListener('input', function () { YfontSizeValue = YfontSize.value; const newSize = parseInt(YfontSizeValue); // 更新 datalabelsConfig 的字体大小 myChart.options.plugins.datalabels.font.size = newSize; // 更新图表 myChart.update(); // createChart(chartType, data, labels); }); // ========================== // x軸字體大小-折線圖 // ========================== var XfontSize = document.getElementById('XfontSize'); XfontSize.addEventListener('input', function () { XfontSizeValue = XfontSize.value; // console.log(XfontSizeValue) // if (myChart) { // myChart.destroy(); // } // 修改X轴标签字体大小 myChart.options.scales.x.ticks.font.size = parseInt(XfontSizeValue); myChart.options.scales.y.ticks.font.size = parseInt(XfontSizeValue); myChart.update(); // createChart(chartType, data, labels); }); // ========================== // x軸字體大小-柱狀圖 // ========================== var XfontSizeBar = document.getElementById('XfontSizeBar'); XfontSizeBar.addEventListener('input', function () { extractAndGenerateChart(dataArray); }); // ========================== // y軸字體大小-柱狀圖 // ========================== var YfontSizeBar = document.getElementById('YfontSizeBar'); YfontSizeBar.addEventListener('input', function () { extractAndGenerateChart(dataArray); }); // ========================== // 線條顏色-柱狀圖 // ========================= const colorInputBar = document.getElementById('borderColorInputBar'); // 添加事件监听器,当颜色选择发生变化时触发 colorInputBar.addEventListener('input', function () { extractAndGenerateChart(dataArray); }); // ========================= // 線條粗細 // ========================= function updateLineWidth(borderWidthValue) { myChart.data.datasets.forEach(dataset => { dataset.borderWidth = parseFloat(borderWidthValue); }); myChart.update(); } var rangeInput = document.getElementById('borderWidthRange'); // var rangeInputSpan = document.getElementById('borderWidthRangeValue'); var borderWidthValue; // rangeInputSpan.textContent = rangeInput.value; // 添加事件监听器,当滑动条的值发生改变时更新显示的值 rangeInput.addEventListener('input', function () { // 获取滑动条的当前值 borderWidthValue = rangeInput.value; console.log(borderWidthValue); // rangeInputSpan.textContent = borderWidthValue; updateLineWidth(borderWidthValue); // myChart.data.datasets[0].borderWidth = borderWidthValue; }); // ========================= // 線條顏色-折線圖 // ========================= const colorInput = document.getElementById('borderColorInput'); let selectedDatasetIndex = null; // 添加事件监听器,当颜色选择发生变化时触发 colorInput.addEventListener('input', function () { // 获取当前选择的颜色值 // chartColor = colorInput.value; // if (myChart) { // myChart.destroy(); // } if (selectedDatasetIndex !== null) { const newColor = colorInput.value; myChart.data.datasets[selectedDatasetIndex].borderColor = newColor; myChart.data.datasets[selectedDatasetIndex].backgroundColor = newColor; myChart.update(); // 更新图表 // createChart(chartType, data, labels); } // console.log(chartColor); // createChart(chartType, data, labels); }); function rgbToHex(rgb) { const rgbArr = rgb.match(/\d+/g); return `#${((1 << 24) + (parseInt(rgbArr[0]) << 16) + (parseInt(rgbArr[1]) << 8) + parseInt(rgbArr[2])).toString(16).slice(1)}`; } function hexToRgba(hex, alpha = 1) { // 移除 HEX 颜色值的 # 符号 hex = hex.replace(/^#/, ''); // 处理 3 位 HEX 颜色值(如 #fff) if (hex.length === 3) { hex = hex.split('').map(function (hex) { return hex + hex; }).join(''); } // 解析 HEX 颜色值 const r = parseInt(hex.substring(0, 2), 16); const g = parseInt(hex.substring(2, 4), 16); const b = parseInt(hex.substring(4, 6), 16); // 返回 RGBA 颜色值 return `rgba(${r}, ${g}, ${b}, ${alpha})`; } // ========================= // 模板樣式 // ========================= // JavaScript document.addEventListener("DOMContentLoaded", function () { // 獲取所有帶有 templateImg class 的圖像元素 var templateImgs = document.querySelectorAll('.templateImg'); // 為每個圖像元素添加點擊事件監聽器 templateImgs.forEach(function (img) { img.addEventListener('click', function () { // 在點擊時印出圖像元素的 value 屬性值 console.log(this.getAttribute('value')); }); }); }); // ========================= var bgImgelement = document.getElementById('data_chart_box'); var chartWhitelement = document.getElementById('data_chartJs_box'); // 背景樣式 // ========================= // JavaScript document.addEventListener("DOMContentLoaded", function () { // 獲取所有帶有 templateImg class 的圖像元素 var bgImgs = document.querySelectorAll('.bgImg'); // 為每個圖像元素添加點擊事件監聽器 bgImgs.forEach(function (img) { img.addEventListener('click', function () { // 在點擊時印出圖像元素的 value 屬性值 console.log(this.getAttribute('value')); var bgImgUrl = this.getAttribute('value'); chart_bgimg_url = 'url(' + bgImgUrl + ')'; if (bgImgUrl === './img/bg07.webp') { chartTitle.style.color = "#ffff"; chartTitle.style.padding = '0px' } else { chartTitle.style.color = "#1c2d6d"; } if (bgImgUrl === "") { chartWhitelement.style.background = ""; console.log('無背景'); bgImgelement.style.backgroundImage = ""; chartWhitelement.style.background = 'rgba(255, 255, 255, 0) !important;' } else { bgImgelement.style.backgroundImage = chart_bgimg_url; chartWhitelement.style.background = "rgba(255, 255, 255, 0.5)" } // bgImgelement.style.backgroundSize = 'cover'; // 調整背景圖片大小 // bgImgelement.style.backgroundPosition = 'center'; // 調整背景圖片位置 // bgImgelement.style.backgroundRepeat = 'no-repeat'; // 調整背景圖片重複 }); }); }); // ========================= const radioButtons = document.querySelectorAll('input[name="inlineRadioOptions"]'); var chartTitle = document.querySelector('.chart_title'); radioButtons.forEach(button => { button.addEventListener('click', function () { if (this.value === '0') { chartTitle.style.textAlign = 'left'; } else if (this.value === '1') { chartTitle.style.textAlign = 'center'; } else if (this.value === '2') { chartTitle.style.textAlign = 'right'; } }); }); // 圖表標題 var keywordInput = document.getElementById('title_data'); // 初始时将输入框的值设置为标题的文本内容 keywordInput.value = chartTitle.textContent; // 监听输入框的输入事件,实现内容同步更新 keywordInput.addEventListener('input', function () { chartTitle.textContent = this.value; }); // 標題字體大小 // ========================== var TitlefontSize = document.getElementById('TitlefontSize'); TitlefontSize.addEventListener('input', function () { var TitlefontSizeValue = TitlefontSize.value; chartTitle.style.fontSize = TitlefontSizeValue + 'px';; }); // 標題字體顏色 var stocknumber; // ========================== let labelDependent = ""; // 圖表左側 Label function get_data(input_text_value) { clickExample.style.display = 'none' // 重新创建取消令牌和源 cancelToken = axios.CancelToken; source = cancelToken.source(); // inputField.value = ""; apiLoading(); var existingTable = chartJsBox.querySelector('table'); downloadButton.style.display = "none"; if (chartDiv.childElementCount != 0) { data = []; labels = []; console.log('已存在') chartTitle.textContent = ""; unit_value.textContent = ""; bgImgelement.style.backgroundImage = ""; chartWhitelement.style.background = ""; document.getElementById('img_box_url').src = ""; } if (existingTable) { unit_value.textContent = ""; chartJsBox.removeChild(existingTable); chartTitle.textContent = ""; bgImgelement.style.backgroundImage = ""; chartWhitelement.style.background = ""; document.getElementById('img_box_url').src = ""; } if (myChart) { unit_value_box.style.display = 'none'; unit_value.textContent = ""; data = []; labels = []; chartTitle.textContent = ""; bgImgelement.style.backgroundImage = ""; chartWhitelement.style.background = ""; myChart.destroy(); document.getElementById('img_box_url').src = ""; } const apiUrl = `https://cmm.ai:8080/answer_with_token?token=${accessToken}&question=${input_text_value}`; axios.get(apiUrl, { cancelToken: source.token // 将取消令牌传递给 Axios 请求 }) .then(response => { apiHideLoading(); console.log(response); // console.log(response.data.chart_info === undefined); if (response.data.chart_info === undefined) { console.log(response.data.data) alert(response.data.data) return } else { unit_value_box.style.display = "block"; var chart_info = response.data.chart_info.Title; chartType = response.data.chart_info.Chart_type; var finance_img_url = response.data.imageUrl_info.imageUrl; if (response.data.imageUrl_info != null) { console.log(finance_img_url); document.getElementById('img_box_url').src = finance_img_url; } keywordInput.value = chart_info; chartTitle.textContent = chart_info; unit = response.data.chart_info.Unit_of_dependent_variable; var Label_dependent_variable = response.data.chart_info.Label_dependent_variable; var Label_independent_variable = response.data.chart_info.Label_independent_variable; stocknumber = response.data.chart_info.args; chartTypeBtn.value = chartType; unit_data.value = response.data.chart_info.Unit_of_dependent_variable; if (response.data.chart_info.Unit_of_dependent_variable == "") { unit_value.textContent = "" } else { unit_value.textContent = response.data.chart_info.Unit_of_dependent_variable; } obj.text = chartType; dataArray = response.data.data; if (chartType === "table") { unit_value.textContent = ""; unit_value_box.style.display = "none" createTable(dataArray, unit, Label_dependent_variable, Label_independent_variable); // document.getElementById('img_box_url').src = ""; } else if (chartType === "bar") { unit_value_box.style.display = 'block'; console.log('圖表類型bar') $('#textToChart').hide(); extractAndGenerateChart(dataArray); } else if (chartType === "doughnut") { unit_value.textContent = ""; $('#textToChart').hide(); unit_value_box.style.display = 'none'; generatePieChart(dataArray, pieFontSize, PieMaxWidth) } else { unit_value_box.style.display = "block" $('#chartdiv').hide(); generateChart(dataArray); } // } // 在這裡處理成功獲取 JSON 的情況 // console.log(response); // switch (chartType) { // case "line": // document.getElementById("inlineRadio4").checked = true; // 折线图 // break; // case "bar": // document.getElementById("inlineRadio5").checked = true; // 柱状图 // labelDependent = response.data.chart_info.Label_dependent_variable; // break; // case "pie": // document.getElementById("inlineRadio6").checked = true; // 圆饼图 // break; // case "table": // document.getElementById("inlineRadio7").checked = true; // 表格 // break; // default: // // 默认情况 } // generateChart(dataArray); }) .catch(error => { // 错误处理逻辑,包括取消请求的情况 apiHideLoading(); if (axios.isCancel(error)) { console.log('请求被取消:', error.message); } else { console.error('发生错误:', error); } }); } // 取消请求的函数 function cancelRequest() { if (source) { source.cancel('取消了 API 请求'); apiHideLoading(); // 隐藏加载状态 } } document.getElementById('cancel_send_data').addEventListener('click', cancelRequest); $(document).ready(function () { $('#dataTable').DataTable(); }); function resizeChart() { console.log(myChart) if (myChart != null) { myChart.resize(); } } document.addEventListener('visibilitychange', function () { console.log('Visibility changed:', document.visibilityState); if (document.visibilityState === 'visible') { resizeChart(); } }); // 监听窗口resize事件 window.addEventListener('resize', resizeChart); var setting_button = document.getElementById('chart_seeting_button'); var closeButton = document.getElementById('style_setting_close'); var closeButton_table = document.getElementById('style_setting_close_table'); // var borderSetting = document.getElementById('borderSetting'); var lineChartSetting = document.getElementById('lineChartSetting'); var barChartSetting = document.getElementById('barChartSetting'); var table_setting = document.getElementById('table_setting'); var chart_category = document.getElementById('chart_category'); var pie_setting = document.getElementById('pie_setting'); // 获取 chartDiv 元素 setting_button.addEventListener('click', () => { console.log('setting_button') if (myChart === null && !chartJsBox.querySelector('table') && chartDiv.childElementCount === 0) { alert('請先生成圖表'); return } if (chartType === 'table') { console.log('表格'); sidebar.classList.toggle('show'); sidebar.classList.toggle('hidden'); barChartSetting.style.display = 'none'; lineChartSetting.style.display = 'none'; table_setting.style.display = 'block'; chart_category.style.display = 'none'; pie_setting.style.display = 'none'; } else if (chartType === "bar") { console.log('柱狀圖') sidebar.classList.toggle('show'); sidebar.classList.toggle('hidden'); barChartSetting.style.display = 'block'; lineChartSetting.style.display = 'none'; table_setting.style.display = 'none'; pie_setting.style.display = 'none'; } else if (chartType === "doughnut") { console.log('快捷功能圓餅圖') sidebar.classList.toggle('show'); sidebar.classList.toggle('hidden'); barChartSetting.style.display = 'none'; lineChartSetting.style.display = 'none'; pie_setting.style.display = 'block'; table_setting.style.display = 'none'; chart_category.style.display = 'none'; document.getElementById('unit_input').style.display = 'none'; } else { sidebar.classList.toggle('show'); sidebar.classList.toggle('hidden'); table_setting.style.display = 'none'; barChartSetting.style.display = 'none'; lineChartSetting.style.display = 'block'; pie_setting.style.display = 'none'; } }); closeButton.addEventListener('click', () => { sidebar.classList.remove('show'); sidebar.classList.add('hidden'); }); const finance_options = [ { name: "原油油桶", imageUrl: "./img/finance/01.webp" }, { name: "黃金", imageUrl: "./img/finance/02.webp" }, { name: "房屋", imageUrl: "./img/finance/03.webp" }, { name: "車", imageUrl: "./img/finance/04.webp" }, { name: "股市", imageUrl: "./img/finance/05.webp" }, { name: "晶圓", imageUrl: "./img/finance/06.webp" }, { name: "TSMC 台積電", imageUrl: "./img/finance/07.webp" }, { name: "台幣", imageUrl: "./img/finance/08.webp" }, { name: "美金", imageUrl: "./img/finance/09.webp" }, { name: "購物車", imageUrl: "./img/finance/10.webp" }, { name: "美中貿易", imageUrl: "./img/finance/11.webp" }, { name: "世界地圖", imageUrl: "./img/finance/12.webp" }, { name: "小麥", imageUrl: "./img/finance/13.webp" }, { name: "玉米", imageUrl: "./img/finance/14.webp" }, { name: "智慧產線", imageUrl: "./img/finance/15.webp" } ]; const selectOptions = document.getElementById('finance_options'); const selectOptions_table = document.getElementById('finance_options_table'); finance_options.forEach((option, index) => { const optionElement = document.createElement('option'); optionElement.value = index + 1; // 值从 1 开始 optionElement.textContent = option.name; optionElement.setAttribute('data-image-url', option.imageUrl); // 存储图片 URL selectOptions.appendChild(optionElement); }); selectOptions.addEventListener('change', function () { const selectedOption = selectOptions.options[selectOptions.selectedIndex]; const imageUrl = selectedOption.getAttribute('data-image-url'); document.getElementById('img_box_url').src = imageUrl; }); // finance_options.forEach((option, index) => { // const optionElement = document.createElement('option'); // optionElement.value = index + 1; // 值从 1 开始 // optionElement.textContent = option.name; // optionElement.setAttribute('data-image-url', option.imageUrl); // 存储图片 URL // selectOptions_table.appendChild(optionElement); // }); // selectOptions_table.addEventListener('change', function () { // const selectedOption = selectOptions_table.options[selectOptions_table.selectedIndex]; // const imageUrl = selectedOption.getAttribute('data-image-url'); // document.getElementById('img_box_url').src = imageUrl; // }); document.querySelectorAll('input[name="ChartOptions"]').forEach(function (radio) { radio.addEventListener('change', function () { // 檢查哪個單選按鈕被選中 if (this.value === 'line') { // 如果選擇了折線圖,執行相應的操作 console.log("選擇了折線圖"); // 在這裡執行顯示折線圖的相關代碼 } else if (this.value === 'doughnut') { // 如果選擇了柱狀圖,執行相應的操作 console.log("選擇了柱狀圖"); // 在這裡執行顯示柱狀圖的相關代碼 } }); }); function tokencheck() { if (!localStorage.getItem("access_token")) { window.location.href = "./login.html"; } else { console.log("存在"); } } tokencheck(); function logout() { localStorage.removeItem("access_token"); } $(document).on("click", "#logout", function (event) { alert("登出成功"); logout(); location.reload(); }); // speech_to_text var closeRecord = document.getElementById('recording_block_close'); var recording_block = document.getElementById('recording_block'); var PageLM = "2024-05-07 18:33"; var recording_button = document.getElementById('recording_button'); var stopButton = document.getElementById('stop'); var audio = document.getElementById('audio'); var recorder, audioBlob; closeRecord.addEventListener('click', () => { recPause(); console.log('record-click') recording_block.classList.remove('show'); recording_block.classList.add('hidden'); }); function reclog(s, color) { var now = new Date(); var t = ("0" + now.getHours()).substr(-2) + ":" + ("0" + now.getMinutes()).substr(-2) + ":" + ("0" + now.getSeconds()).substr(-2); var div = document.createElement("div"); var elem = document.querySelector(".reclog"); elem.insertBefore(div, elem.firstChild); div.innerHTML = '
[' + t + ']' + s + '
'; }; window.onerror = function (message, url, lineNo, columnNo, error) { //https://www.cnblogs.com/xianyulaodi/p/6201829.html reclog('【Uncaught Error】' + message + '
' + "at:" + lineNo + ":" + columnNo + " url:" + url + "\n" + (error && error.stack || Html_$T("kBaF::不能获得错误堆栈")) + '
'); }; if (!window.Html_$T) {//没有提供本页面用的国际化多语言支持时 返回中文文本 window.Html_$T = function () { var a = arguments, txt = a[0].replace(/^.+?::/, ""), n = 0; for (var i = 0; i < a.length; i++) { if (typeof a[i] == "number") { n = i; break } } txt = txt.replace(/\{(\d+)\}/g, function (v, f) { v = a[+f + n]; return v == null ? "" : v }); return txt; } window.Html_xT = function (v) { return v } } if (window.Recorder) { // reclog(Html_$T('BL9u::頁面已準備好,請先點擊打開錄音,然後點擊錄製'), 2); alert('錄音已準備好,請先點擊右下角打開錄音,即可開始錄製') } else { reclog(Html_$T("YzPd::js文件加载失败,请刷新重试!"), "#f00;font-size:50px"); console.log('js文件加载失敗,请刷新重试!') } recording_button.addEventListener('click', async () => { recording_block.classList.toggle('show'); recording_block.classList.toggle('hidden'); recStart(); }); function recStart() {//打开了录音后才能进行start、stop调用 rec.start(); }; stopButton.addEventListener('click', async () => { // recorder.stop(); // uploadAudio(audioBlob); recStop(); }); window.onload = function () { recOpen(); }; // 錄音程式 // ======================= var rec, wave, recBlob; /**调用open打开录音请求好录音权限 Call open to open the recording and request the recording permission**/ var recOpen = function () {//一般在显示出录音按钮或相关的录音界面时进行此方法调用,后面用户点击开始录音时就能畅通无阻了 rec = null; wave = null; recBlob = null; var newRec = Recorder({ type: "mp3", sampleRate: 16000, bitRate: 16 //mp3格式,指定采样率hz、比特率kbps,其他参数使用默认配置;注意:是数字的参数必须提供数字,不要用字符串;需要使用的type类型,需提前把格式支持文件加载进来,比如使用wav格式需要提前加载wav.js编码引擎 , onProcess: function (buffers, powerLevel, bufferDuration, bufferSampleRate, newBufferIdx, asyncEnd) { // // //录音实时回调,大约1秒调用12次本回调 document.querySelector(".recpowerx").style.width = powerLevel + "%"; document.querySelector(".recpowert").innerText = formatMs(bufferDuration, 1) + " / " + powerLevel; // //可视化图形绘制 // wave.input(buffers[buffers.length - 1], powerLevel, bufferSampleRate); } }); newRec.open(function () {//打开麦克风授权获得相关资源 rec = newRec; //此处创建这些音频可视化图形绘制浏览器支持妥妥的 // wave = Recorder.FrequencyHistogramView({ elem: ".recwave" }); reclog(Html_$T("GVCa::已打開錄音,可以點擊錄制開始錄音"), 2); }, function (msg, isUserNotAllow) {//用户拒绝未授权或不支持 reclog((isUserNotAllow ? "UserNotAllow, " : "") + Html_$T("TOOV::打開錄音失敗:") + msg, 1); }); }; /**关闭录音,释放资源 Close recording, release resources**/ function recClose() { if (rec) { rec.close(); reclog(Html_$T("jqOs::已关闭")); } else { reclog(Html_$T("VOOw::未打开录音"), 1); }; }; /**开始录音 Start recording**/ function recStart() {//打开了录音后才能进行start、stop调用 if (rec && Recorder.IsOpen()) { recBlob = null; rec.start(); reclog(Html_$T("CGdy::已經開始錄音...") + " " + rec.set.type + " " + rec.set.sampleRate + " " + rec.set.bitRate + "kbps"); } else { reclog(Html_$T("ajKR::未打开錄音"), 1); }; }; /**暂停录音 Passing recording**/ function recPause() { if (rec && Recorder.IsOpen()) { rec.pause(); reclog(Html_$T("GvCy::已暂停")); } else { reclog(Html_$T("gCAR::未打開錄音"), 1); }; }; /**恢复录音 Resume recording**/ function recResume() { if (rec && Recorder.IsOpen()) { rec.resume(); reclog(Html_$T("5q1K::继续录音中...")); } else { reclog(Html_$T("Ob6S::未打开录音"), 1); }; }; /**结束录音,得到音频文件 Stop recording and get audio files**/ function recStop() { if (!(rec && Recorder.IsOpen())) { reclog(Html_$T("5JuL::未打開錄音"), 1); return; }; rec.stop(function (blob, duration) { console.log(blob, (window.URL || webkitURL).createObjectURL(blob), Html_xT(Html_$T("gOix::時長:{1}ms", 0, duration))); recBlob = blob; var file = new File([blob], "recording.mp3", { type: "audio/mp3" }); // console.log(file); handleAudioToText(file); reclog(Html_$T("0LHf::已錄製mp3:{1}ms {2}字節,可以點擊播放、上傳、本地下载了", 0, formatMs(duration), blob.size), 2); }, function (msg) { reclog(Html_$T("kGZO::錄音失敗:") + msg, 1); }); }; /**播放 Play**/ function recPlay() { if (!recBlob) { reclog(Html_$T("tIke::请先錄音,然后停止後再播放"), 1); return; }; var cls = ("a" + Math.random()).replace(".", ""); reclog(Html_$T('GlWb::播放中: ') + ''); var audio = document.createElement("audio"); audio.controls = true; document.querySelector("." + cls).appendChild(audio); //简单利用URL生成播放地址,注意不用了时需要revokeObjectURL,否则霸占内存 audio.src = (window.URL || webkitURL).createObjectURL(recBlob); audio.play(); setTimeout(function () { (window.URL || webkitURL).revokeObjectURL(audio.src); }, 5000); }; /**上传 Upload**/ function recUpload() { var blob = recBlob; if (!blob) { reclog(Html_$T("DUTn::请先录音,然后停止后再上传"), 1); return; }; //本例子假设使用原始XMLHttpRequest请求方式,实际使用中自行调整为自己的请求方式 //录音结束时拿到了blob文件对象,可以用FileReader读取出内容,或者用FormData上传 var api = "http://127.0.0.1:9528"; var onreadystatechange = function (xhr, title) { return function () { if (xhr.readyState == 4) { if (xhr.status == 200) { reclog(title + Html_$T("G2MU::上传成功") + ' response: ' + xhr.responseText + '', 2); } else { reclog(title + Html_$T("TUdi::没有完成上传,演示上传地址无需关注上传结果,只要浏览器控制台内Network面板内看到的请求数据结构是预期的就ok了。"), "#d8c1a0"); console.error(Html_xT(title + Html_$T("HjDi::上传失败")), xhr.status, xhr.responseText); }; }; }; }; reclog(Html_$T("QnSI::开始上传到{1},请稍候... (你可以先到源码 /assets/node-localServer 目录内执行 npm run start 来运行本地测试服务器)", 0, api)); /***方式一:将blob文件转成base64纯文本编码,使用普通application/x-www-form-urlencoded表单上传***/ var reader = new FileReader(); reader.onloadend = function () { var postData = ""; postData += "mime=" + encodeURIComponent(blob.type);//告诉后端,这个录音是什么格式的,可能前后端都固定的mp3可以不用写 postData += "&upfile_b64=" + encodeURIComponent((/.+;\s*base64\s*,\s*(.+)$/i.exec(reader.result) || [])[1]) //录音文件内容,后端进行base64解码成二进制 //...其他表单参数 var xhr = new XMLHttpRequest(); xhr.open("POST", api + "/uploadBase64"); xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); xhr.onreadystatechange = onreadystatechange(xhr, Html_$T("gG1f::上传方式一【Base64】")); xhr.send(postData); }; reader.readAsDataURL(blob); /***方式二:使用FormData用multipart/form-data表单上传文件***/ var form = new FormData(); form.append("upfile", blob, "recorder.mp3"); //和普通form表单并无二致,后端接收到upfile参数的文件,文件名为recorder.mp3 //...其他表单参数 var xhr = new XMLHttpRequest(); xhr.open("POST", api + "/upload"); xhr.onreadystatechange = onreadystatechange(xhr, Html_$T("vDzB::上传方式二【FormData】")); xhr.send(form); }; /**本地下载 Local download**/ function recLocalDown() { if (!recBlob) { reclog(Html_$T("M86h::请先录音,然后停止后再下载"), 1); return; }; var cls = ("a" + Math.random()).replace(".", ""); recdown64.lastCls = cls; reclog(Html_$T('vJPl::点击 ') + '' + Html_$T('Whtc:: 下载,或复制文本') + ''); var fileName = "recorder-" + Date.now() + ".mp3"; var downA = document.createElement("A"); downA.innerHTML = Html_$T("g8Fy::下载 ") + fileName; downA.href = (window.URL || webkitURL).createObjectURL(recBlob); downA.download = fileName; document.querySelector("." + cls).appendChild(downA); if (/mobile/i.test(navigator.userAgent)) { alert(Html_xT(Html_$T("DIEK::因移动端绝大部分国产浏览器未适配Blob Url的下载,所以本demo代码在移动端未调用downA.click()。请尝试点击日志中显示的下载链接下载"))); } else { downA.click(); } //不用了时需要revokeObjectURL,否则霸占内存 //(window.URL||webkitURL).revokeObjectURL(downA.href); }; function recdown64(cls) { var el = document.querySelector("." + cls + "_b64"); if (recdown64.lastCls != cls) { el.innerHTML = '' + Html_$T("eKKx::老的数据没有保存,只支持最新的一条") + ''; return; } var reader = new FileReader(); reader.onloadend = function () { el.innerHTML = ''; el.querySelector("textarea").value = reader.result; }; reader.readAsDataURL(recBlob); }; var formatMs = function (ms, all) { var ss = ms % 1000; ms = (ms - ss) / 1000; var s = ms % 60; ms = (ms - s) / 60; var m = ms % 60; ms = (ms - m) / 60; var h = ms; var t = (h ? h + ":" : "") + (all || h + m ? ("0" + m).substr(-2) + ":" : "") + (all || h + m + s ? ("0" + s).substr(-2) + "″" : "") + ("00" + ss).substr(-3); return t; }; // ======================= let mediaRecorder; let audioChunks = []; var recordText = document.getElementById('record_text'); async function handleAudioToText(file) { console.log('tts') let audioLang = "cmn-Hant-TW"; // 音訊語言 // let lang = localStorage.getItem("lang"); let lang = "zh-tw"; console.log("lang", lang); switch (lang) { case "zh-tw": audioLang = "cmn-Hant-TW"; break; case "en-us": audioLang = "en-US"; break; case "ja-jp": audioLang = "ja-JP"; break; case "ko-kr": audioLang = "ko-KR"; break; default: break; } let url = `https://cmm.ai:9001/gcp/speech-to-text?language_code=${audioLang}`; const formData = new FormData(); formData.append("file", file); try { const response = await axios.post(url, formData); console.log("response", response); input_text_value = response.data; recording_block.classList.remove('show'); recording_block.classList.add('hidden'); if (input_text_value.length === 0) { alert('請再說一次') return } console.log(input_text_value[0]); inputField.value = input_text_value[0]; // Show the result for a short time before executing get_data get_data(input_text_value); // get_data(input_text_value); } catch (error) { console.log("error", error); } } Object.defineProperty(obj, 'text', { get() { return this._text; }, set(newValue) { console.log(`字符串从 "${this._text}" 变为 "${newValue}"`); this._text = newValue; } }); // sendButtonClickHandler(input_text_value);