Translate all Chinese comments and strings to English
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -57,17 +57,17 @@ export async function saveFilter(addFilterId, next = null) {
|
||||
cancelButtonColor: '#94a3b8',
|
||||
customClass: customClass,
|
||||
});
|
||||
// 透過回傳值判斷要不要轉址
|
||||
if(isConfirmed) { // 存檔成功
|
||||
// Determine whether to redirect based on the return value
|
||||
if(isConfirmed) { // Save succeeded
|
||||
await addFilterId(fileName);
|
||||
// 顯示儲存完成
|
||||
// Show save complete notification
|
||||
if (value) { // Example of value: yes
|
||||
savedSuccessfully(value);
|
||||
}
|
||||
// 清空欄位
|
||||
// Clear the input field
|
||||
fileName = '';
|
||||
return true;
|
||||
} else { // 點擊取消或空白處,為存檔失敗。
|
||||
} else { // Clicked cancel or outside the dialog; save failed.
|
||||
pageAdminStore.keepPreviousPage();
|
||||
|
||||
// Not every time we have nontrivial next value
|
||||
@@ -88,7 +88,7 @@ export async function savedSuccessfully(value) {
|
||||
await Swal.fire({
|
||||
title: 'SAVE COMPLETE',
|
||||
html: `<span class="text-primary">${escapeHtml(value)}</span> has been saved in Lucia.`,
|
||||
timer: 3000, // 停留 3 秒後自動關閉
|
||||
timer: 3000, // Auto-close after 3 seconds
|
||||
showConfirmButton: false,
|
||||
icon: 'success',
|
||||
iconColor: '#0099FF',
|
||||
@@ -188,15 +188,15 @@ export async function saveConformance(addConformanceCreateCheckId) {
|
||||
cancelButtonColor: '#94a3b8',
|
||||
customClass: customClass,
|
||||
});
|
||||
// 透過回傳值判斷要不要轉址
|
||||
if(isConfirmed) { // 存檔成功
|
||||
// Determine whether to redirect based on the return value
|
||||
if(isConfirmed) { // Save succeeded
|
||||
await addConformanceCreateCheckId(fileName);
|
||||
// 顯示儲存完成
|
||||
// Show save complete notification
|
||||
if (value) savedSuccessfully(value);
|
||||
// 清空欄位
|
||||
// Clear the input field
|
||||
fileName = '';
|
||||
return true;
|
||||
} else { // 點擊取消或空白處,為存檔失敗。
|
||||
} else { // Clicked cancel or outside the dialog; save failed.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -329,7 +329,7 @@ export async function uploadFailedFirst(failureType, failureMsg, failureLoc) {
|
||||
await Swal.fire({
|
||||
title: 'IMPORT FAILED',
|
||||
html: value,
|
||||
timer: 3000, // 停留 3 秒後自動關閉
|
||||
timer: 3000, // Auto-close after 3 seconds
|
||||
showConfirmButton: false,
|
||||
icon: 'error',
|
||||
iconColor: '#FF3366',
|
||||
@@ -390,7 +390,7 @@ export async function uploadFailedSecond(detail) {
|
||||
await Swal.fire({
|
||||
title: 'IMPORT FAILED',
|
||||
html: `<div class="text-left mx-3 space-y-1"><p>Error(s) detected:</p><ul class="list-disc ml-6">${srt}</ul><p>${manySrt} Please check.</p></div>`,
|
||||
timer: 3000, // 停留 3 秒後自動關閉
|
||||
timer: 3000, // Auto-close after 3 seconds
|
||||
showConfirmButton: false,
|
||||
icon: 'error',
|
||||
iconColor: '#FF3366',
|
||||
@@ -405,7 +405,7 @@ export async function uploadFailedSecond(detail) {
|
||||
export async function uploadSuccess() {
|
||||
await Swal.fire({
|
||||
title: 'IMPORT COMPLETED',
|
||||
timer: 3000, // 停留 3 秒後自動關閉
|
||||
timer: 3000, // Auto-close after 3 seconds
|
||||
showConfirmButton: false,
|
||||
icon: 'success',
|
||||
iconColor: '#0099FF',
|
||||
@@ -494,9 +494,9 @@ export async function renameModal(rename, type, id, baseName) {
|
||||
}
|
||||
});
|
||||
|
||||
// 改名成功
|
||||
// Rename succeeded
|
||||
if(isConfirmed) await rename(type, id, value);
|
||||
// 清空欄位 fileName = '';
|
||||
// Clear the field: fileName = ''
|
||||
}
|
||||
/**
|
||||
* Shows a confirmation dialog for deleting a file and its dependents.
|
||||
@@ -544,7 +544,7 @@ export async function deleteFileModal(files, type, id, name) {
|
||||
export async function deleteSuccess() {
|
||||
await Swal.fire({
|
||||
title: 'FILE(S) DELETED',
|
||||
timer: 3000, // 停留 3 秒後自動關閉
|
||||
timer: 3000, // Auto-close after 3 seconds
|
||||
showConfirmButton: false,
|
||||
icon: 'success',
|
||||
iconColor: '#0099FF',
|
||||
|
||||
@@ -34,12 +34,12 @@ const composeFreqTypeText = (baseText, dataLayerOption, optionValue) => {
|
||||
let text = baseText;
|
||||
const textInt = dataLayerOption === 'rel_freq' ? baseText + optionValue * 100 + "%" : baseText + optionValue;
|
||||
const textFloat = dataLayerOption === 'rel_freq' ? baseText + (optionValue * 100).toFixed(2) + "%" : baseText + optionValue.toFixed(2);
|
||||
// 判斷是否為整數,若非整數要取小數點後面兩個值。
|
||||
// Check if the value is an integer; if not, round to 2 decimal places.
|
||||
text = Math.trunc(optionValue) === optionValue ? textInt : textFloat;
|
||||
return text;
|
||||
};
|
||||
|
||||
// 註冊布局演算法
|
||||
// Register layout algorithms
|
||||
cytoscape.use(dagre);
|
||||
cytoscape.use(spread);
|
||||
cytoscape.use(fcose);
|
||||
@@ -65,7 +65,7 @@ cytoscape.use(cola);
|
||||
* @returns {cytoscape.Core} The configured Cytoscape instance.
|
||||
*/
|
||||
export default function cytoscapeMap(mapData, dataLayerType, dataLayerOption, curveStyle, rank, graphId) {
|
||||
// 設定每個 node, edges 的顏色與樣式
|
||||
// Set the color and style for each node and edge
|
||||
let nodes = mapData.nodes;
|
||||
let edges = mapData.edges;
|
||||
|
||||
@@ -73,15 +73,15 @@ export default function cytoscapeMap(mapData, dataLayerType, dataLayerOption, cu
|
||||
let cy = cytoscape({
|
||||
container: graphId,
|
||||
elements: {
|
||||
nodes: nodes, //nodes, // 節點的資料
|
||||
edges: edges, //edges, // 關係線的資料
|
||||
nodes: nodes, // Node data
|
||||
edges: edges, // Edge data
|
||||
},
|
||||
layout: {
|
||||
name: 'dagre',
|
||||
rankDir: rank, // 直向 TB | 橫向 LR, 'cytoscape-dagre' 套件裡的變數
|
||||
rankDir: rank, // Vertical TB | Horizontal LR, variable from 'cytoscape-dagre' plugin
|
||||
},
|
||||
style: [
|
||||
// 點擊 node 後改變的樣式
|
||||
// Style changes when a node is selected
|
||||
{
|
||||
selector: 'node:selected',
|
||||
style: {
|
||||
@@ -89,27 +89,27 @@ export default function cytoscapeMap(mapData, dataLayerType, dataLayerOption, cu
|
||||
'border-width': '3',
|
||||
},
|
||||
},
|
||||
// node 節點的樣式
|
||||
// Node styling
|
||||
{
|
||||
selector: 'node',
|
||||
style: {
|
||||
'label':
|
||||
function (node) { // 節點要顯示的文字
|
||||
// node.data(this.dataLayerType+"."+this.dataLayerOption) 為原先陣列 node.data.key.value
|
||||
function (node) { // Text to display on the node
|
||||
// node.data(this.dataLayerType+"."+this.dataLayerOption) accesses the original array value at node.data.key.value
|
||||
let optionValue = node.data(`${dataLayerType}.${dataLayerOption}`);
|
||||
let text = '';
|
||||
const STRING_LIMIT = 8;
|
||||
if (node.data('label').length > STRING_LIMIT) {
|
||||
// 若文字超過 STRING_LIMIT長度,則字尾巴要加「...」,style 要換兩行(\n 換行符號)
|
||||
// 使用 data() 是因為在 cytoscape 中從陣列轉為 function
|
||||
// If text exceeds STRING_LIMIT, append "..." and add line breaks (\n)
|
||||
// Using data() because Cytoscape converts array data to function calls
|
||||
text = `${node.data('label').substr(0, STRING_LIMIT)}...\n\n`;
|
||||
} else { // 補空白直到撐寬label的寬度,這是為了統一所有label的寬度
|
||||
} else { // Pad with spaces to match the label width for consistent sizing
|
||||
text = `${node.data('label').padEnd(STRING_LIMIT, ' ')}\n\n`
|
||||
}
|
||||
|
||||
// 在 element 中 activity 歸類在 default,所以要先判斷 node 是否為 activity 才裝入文字。
|
||||
// 可使用 parseInt(整數) parseFloat(浮點數) 將字串轉為數字
|
||||
// Relative 要轉為百分比 %
|
||||
// In elements, activity is categorized as default, so check if the node is an activity before adding text.
|
||||
// Use parseInt (integer) or parseFloat (float) to convert strings to numbers.
|
||||
// Relative values need to be converted to percentages (%)
|
||||
if (node.data('type') === 'activity') {
|
||||
let textDurRel;
|
||||
let timeLabelInt;
|
||||
@@ -120,14 +120,14 @@ export default function cytoscapeMap(mapData, dataLayerType, dataLayerOption, cu
|
||||
case 'freq': // Frequency
|
||||
text = composeFreqTypeText(text, dataLayerOption, optionValue);
|
||||
break;
|
||||
case 'duration': // Duration 除了 Relative 為百分比 % ,其他要轉變時間單位。
|
||||
case 'duration': // Duration: Relative is percentage %, others need time unit conversion.
|
||||
// Relative %
|
||||
textDurRel = text + (optionValue * 100).toFixed(2) + "%";
|
||||
// Timelabel
|
||||
timeLabelInt = text + getTimeLabel(optionValue);
|
||||
timeLabelFloat = text + getTimeLabel(optionValue.toFixed(2));
|
||||
|
||||
// 判斷是否為整數,若非整數要取小數點後面兩個值。
|
||||
// Check if the value is an integer; if not, round to 2 decimal places.
|
||||
textTimeLabel = Math.trunc(optionValue) === optionValue ? timeLabelInt : timeLabelFloat;
|
||||
|
||||
text = dataLayerOption === 'rel_duration' ? textDurRel : textTimeLabel;
|
||||
@@ -145,12 +145,12 @@ export default function cytoscapeMap(mapData, dataLayerType, dataLayerOption, cu
|
||||
return node.data('type') === 'activity' ? '1' : '2';
|
||||
},
|
||||
'background-image': 'data(nodeImageUrl)',
|
||||
'background-opacity': 'data(backgroundOpacity)', // 透明背景
|
||||
'border-opacity': 'data(borderOpacity)', // 透明邊框
|
||||
'background-opacity': 'data(backgroundOpacity)', // Transparent background
|
||||
'border-opacity': 'data(borderOpacity)', // Transparent border
|
||||
'shape': 'data(shape)',
|
||||
'text-wrap': 'wrap',
|
||||
'text-max-width': 'data(width)', // 在 div 內換行
|
||||
'text-overflow-wrap': 'anywhere', // 在 div 內換行
|
||||
'text-max-width': 'data(width)', // Wrap text within the node
|
||||
'text-overflow-wrap': 'anywhere', // Allow wrapping at any position
|
||||
'text-margin-x': function (node) {
|
||||
return node.data('type') === 'activity' ? -5 : 0;
|
||||
},
|
||||
@@ -173,11 +173,11 @@ export default function cytoscapeMap(mapData, dataLayerType, dataLayerOption, cu
|
||||
},
|
||||
},
|
||||
},
|
||||
// edge 關係線的樣式
|
||||
// Edge styling
|
||||
{
|
||||
selector: 'edge',
|
||||
style: {
|
||||
'content': function (edge) { // 關係線顯示的文字
|
||||
'content': function (edge) { // Text displayed on the edge
|
||||
let optionValue = edge.data(`${dataLayerType}.${dataLayerOption}`);
|
||||
let result = '';
|
||||
let edgeInt;
|
||||
@@ -193,11 +193,11 @@ export default function cytoscapeMap(mapData, dataLayerType, dataLayerOption, cu
|
||||
edgeInt = dataLayerOption === 'rel_freq' ? optionValue * 100 + "%" : optionValue;
|
||||
edgeFloat = dataLayerOption === 'rel_freq' ? (optionValue * 100).toFixed(2) + "%" : optionValue.toFixed(2);
|
||||
|
||||
// 判斷是否為整數,若非整數要取小數點後面兩個值。
|
||||
// Check if the value is an integer; if not, round to 2 decimal places.
|
||||
result = Math.trunc(optionValue) === optionValue ? edgeInt : edgeFloat;
|
||||
break;
|
||||
|
||||
case 'duration': // Duration 除了 Relative 為百分比 % ,其他要轉變時間單位。
|
||||
case 'duration': // Duration: Relative is percentage %, others need time unit conversion.
|
||||
// Relative %
|
||||
edgeDurRel = (optionValue * 100).toFixed(2) + "%";
|
||||
// Timelabel
|
||||
@@ -211,10 +211,10 @@ export default function cytoscapeMap(mapData, dataLayerType, dataLayerOption, cu
|
||||
return result;
|
||||
},
|
||||
'curve-style': curveStyle, // unbundled-bezier | taxi
|
||||
'overlay-opacity': 0, // 將overlay-opacity設置為0,移除灰色陰影
|
||||
'target-arrow-shape': 'triangle', // 指向目標的箭頭形狀: 三角形
|
||||
'overlay-opacity': 0, // Set overlay-opacity to 0 to remove the gray shadow
|
||||
'target-arrow-shape': 'triangle', // Arrow shape pointing to target: triangle
|
||||
'color': 'gray', //#0066cc
|
||||
//'control-point-step-size':100, // 從點到點的垂直線,指定貝茲取線邊緣間的距離
|
||||
//'control-point-step-size':100, // Distance between Bezier curve control points
|
||||
'width': 'data(lineWidth)',
|
||||
'line-style': 'data(edgeStyle)',
|
||||
"text-margin-y": "0.7rem",
|
||||
@@ -237,30 +237,30 @@ export default function cytoscapeMap(mapData, dataLayerType, dataLayerOption, cu
|
||||
'overlay-padding': '5px',
|
||||
},
|
||||
}, {
|
||||
selector: 'edge[source = target]', // 選擇 self-loop 的邊
|
||||
selector: 'edge[source = target]', // Select self-loop edges
|
||||
style: {
|
||||
'loop-direction': '0deg', // 控制 loop 的方向
|
||||
'loop-sweep': '-60deg', // 控制 loop 的弧度,這裡可以調整弧度以改變大小
|
||||
'control-point-step-size': 50 // 控制 loop 的半徑大小,增加這個值可以增大 loop
|
||||
'loop-direction': '0deg', // Control the loop direction
|
||||
'loop-sweep': '-60deg', // Control the loop arc; adjust to change size
|
||||
'control-point-step-size': 50 // Control the loop radius; increase to enlarge the loop
|
||||
}
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
|
||||
// 按下線條,線條及線條上數字有光暈效果
|
||||
// When an edge is clicked, apply glow effect to the edge and its label
|
||||
cy.on('tap', 'edge', function (event) {
|
||||
cy.edges().removeClass('highlight-edge');
|
||||
event.target.addClass('highlight-edge');
|
||||
});
|
||||
|
||||
|
||||
// 按下節點光暈效果與鄰邊光暈效果
|
||||
// When a node is clicked, apply glow effect to the node and adjacent edges
|
||||
cy.on('tap, mousedown', 'node', function (event) {
|
||||
useMapPathStore().onNodeClickHighlightEdges(event.target);
|
||||
});
|
||||
|
||||
// 按下線段光暈效果與兩端點光暈效果
|
||||
// When an edge is clicked, apply glow effect to the edge and both endpoint nodes
|
||||
cy.on('tap, mousedown', 'edge', function (event) {
|
||||
useMapPathStore().onEdgeClickHighlightNodes(event.target);
|
||||
});
|
||||
@@ -288,12 +288,12 @@ export default function cytoscapeMap(mapData, dataLayerType, dataLayerOption, cu
|
||||
const cytoscapeStore = useCytoscapeStore();
|
||||
cy.ready(() => {
|
||||
cytoscapeStore.loadPositionsFromStorage(rank);
|
||||
// 判斷localStorage是否儲存過拜訪資訊
|
||||
// 若曾經儲存過拜訪後的座標位置,則restore位置來渲染出來
|
||||
// Check if localStorage has previously saved visit data.
|
||||
// If saved node positions exist, restore them for rendering.
|
||||
if (localStorage.getItem(SAVE_KEY_NAME) && JSON.parse(localStorage.getItem(SAVE_KEY_NAME))) {
|
||||
const allGraphsRemembered = JSON.parse(localStorage.getItem(SAVE_KEY_NAME));
|
||||
const currentGraphNodesRemembered =
|
||||
allGraphsRemembered[cytoscapeStore.currentGraphId] ? allGraphsRemembered[cytoscapeStore.currentGraphId][rank] : null; // 可能是undefined
|
||||
allGraphsRemembered[cytoscapeStore.currentGraphId] ? allGraphsRemembered[cytoscapeStore.currentGraphId][rank] : null; // May be undefined
|
||||
if (currentGraphNodesRemembered) {
|
||||
currentGraphNodesRemembered.forEach(nodeRemembered => {
|
||||
const nodeToDecide = cy.getElementById(nodeRemembered.id);
|
||||
@@ -303,14 +303,14 @@ export default function cytoscapeMap(mapData, dataLayerType, dataLayerOption, cu
|
||||
});
|
||||
}
|
||||
}
|
||||
//存下此刻剛進入畫面時當前所有節點的座標位置
|
||||
// Save the current positions of all nodes when the view is first entered
|
||||
const allNodes = cy.nodes();
|
||||
allNodes.forEach(nodeFirstlySave => {
|
||||
cytoscapeStore.saveNodePosition(nodeFirstlySave.id(), nodeFirstlySave.position(), rank);
|
||||
});
|
||||
|
||||
// 在改變節點位置後,盡可能地記錄節點線條的位置情報
|
||||
// rank 代表現在使用者切換的是水平方向還是垂直方向模式
|
||||
// After node positions change, save the updated positions.
|
||||
// rank represents whether the user is in horizontal or vertical layout mode.
|
||||
cy.on('dragfree', 'node', (event) => {
|
||||
const nodeToSave = event.target;
|
||||
cytoscapeStore.saveNodePosition(nodeToSave.id(), nodeToSave.position(), rank);
|
||||
|
||||
@@ -31,23 +31,23 @@ export default function cytoscapeMapTrace(nodes, edges, graphId) {
|
||||
let cy = cytoscape({
|
||||
container: graphId,
|
||||
elements: {
|
||||
nodes: nodes, // 節點的資料
|
||||
edges: edges, // 關係線的資料
|
||||
nodes: nodes, // Node data
|
||||
edges: edges, // Edge data
|
||||
},
|
||||
layout: {
|
||||
name: 'dagre',
|
||||
rankDir: 'LR' // 直向 TB | 橫向 LR, 'cytoscape-dagre' 套件裡的變數
|
||||
rankDir: 'LR' // Vertical TB | Horizontal LR, variable from 'cytoscape-dagre' plugin
|
||||
},
|
||||
style: [
|
||||
// node 節點的樣式
|
||||
// Node styling
|
||||
{
|
||||
selector: 'node',
|
||||
style: {
|
||||
'label':
|
||||
function(node) { // 節點要顯示的文字
|
||||
function(node) { // Text to display on the node
|
||||
let text = '';
|
||||
|
||||
// node.data('label') 為原先陣列 node.data.label
|
||||
// node.data('label') accesses the original array value at node.data.label
|
||||
text = node.data('label').length > 18 ? `${node.data('label').substr(0,15)}...` : `${node.data('label')}`;
|
||||
|
||||
return text
|
||||
@@ -67,18 +67,18 @@ export default function cytoscapeMapTrace(nodes, edges, graphId) {
|
||||
'font-size': 14,
|
||||
}
|
||||
},
|
||||
// edge 關係線的樣式
|
||||
// Edge styling
|
||||
{
|
||||
selector: 'edge',
|
||||
style: {
|
||||
'curve-style': 'taxi', // unbundled-bezier | taxi
|
||||
'target-arrow-shape': 'triangle', // 指向目標的箭頭形狀: 三角形
|
||||
'target-arrow-shape': 'triangle', // Arrow shape pointing to target: triangle
|
||||
'color': 'gray', //#0066cc
|
||||
'width': 'data(lineWidth)',
|
||||
'line-style': 'data(style)',
|
||||
}
|
||||
},
|
||||
// 點擊 node 後改變的樣式
|
||||
// Style changes when a node is selected
|
||||
{
|
||||
selector: 'node:selected',
|
||||
style:{
|
||||
|
||||
@@ -24,22 +24,22 @@ import getMoment from 'moment';
|
||||
* with boundary points.
|
||||
*/
|
||||
export function setLineChartData(baseData, xMax, xMin, isPercent, yMax, yMin) {
|
||||
// 將 baseData 轉換為包含 x 和 y 屬性的物件陣列
|
||||
// Convert baseData to an array of objects with x and y properties
|
||||
let data = baseData.map(i => ({ x: i.x, y: i.y }));
|
||||
|
||||
// 計算 y 軸最小值
|
||||
// Calculate the Y-axis minimum value
|
||||
let b = calculateYMin(baseData, isPercent, yMin, yMax);
|
||||
|
||||
// 計算 y 軸最大值
|
||||
// Calculate the Y-axis maximum value
|
||||
let mf = calculateYMax(baseData, isPercent, yMin, yMax);
|
||||
|
||||
// 添加最小值
|
||||
// Prepend the minimum value
|
||||
data.unshift({
|
||||
x: xMin,
|
||||
y: b,
|
||||
});
|
||||
|
||||
// 添加最大值
|
||||
// Append the maximum value
|
||||
data.push({
|
||||
x: xMax,
|
||||
y: mf,
|
||||
@@ -140,11 +140,11 @@ export function setBarChartData(baseData) {
|
||||
* values in seconds.
|
||||
*/
|
||||
export function timeRange(minTime, maxTime, amount) {
|
||||
// x 軸(時間軸)的範圍是最大-最小,從最小值按照 index 累加間距到最大值
|
||||
// The X-axis (time axis) range is max - min; accumulate intervals from min to max by index
|
||||
const startTime = minTime;
|
||||
const endTime = maxTime;
|
||||
let timeRange = []; // return數據初始化
|
||||
const timeGap = (endTime - startTime) / (amount - 1); // 切分成多少段
|
||||
let timeRange = []; // Initialize the return data array
|
||||
const timeGap = (endTime - startTime) / (amount - 1); // Divide into segments
|
||||
|
||||
for (let i = 0; i < amount; i++) {
|
||||
timeRange.push(startTime + timeGap * i);
|
||||
@@ -165,7 +165,7 @@ export function yTimeRange(data, yAmount, yMax) {
|
||||
const yRange = [];
|
||||
const yGap = (1/ (yAmount-1));
|
||||
|
||||
// 貝茲曲線公式
|
||||
// Cubic Bezier curve formula
|
||||
const threebsr = function (t, a1, a2, a3, a4) {
|
||||
return (
|
||||
(1 - t) * (1 - t) * (1 - t) * a1 +
|
||||
@@ -204,8 +204,8 @@ export function yTimeRange(data, yAmount, yMax) {
|
||||
* @returns {number} The index of the closest value in the array.
|
||||
*/
|
||||
export function getXIndex(data, xValue) {
|
||||
let closestIndex = xValue; // 假定第一个元素的索引是 0
|
||||
let smallestDifference = Math.abs(xValue - data[0]); // 初始差值设为第一个元素与目标数的差值
|
||||
let closestIndex = xValue; // Assume the first element index is 0
|
||||
let smallestDifference = Math.abs(xValue - data[0]); // Initialize difference to the gap between the first element and target
|
||||
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
let difference = Math.abs(xValue - data[i]);
|
||||
@@ -244,7 +244,7 @@ export function formatTime(seconds) {
|
||||
}
|
||||
result += `${remainingSeconds}s`;
|
||||
|
||||
return result.trim(); // 去除最后一个空格
|
||||
return result.trim(); // Remove trailing whitespace
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
@@ -258,12 +258,12 @@ export function formatTime(seconds) {
|
||||
export function formatMaxTwo(times) {
|
||||
const formattedTimes = [];
|
||||
for (let time of times) {
|
||||
// 匹配數字和單位(天、小時、分鐘、秒), 假設數字不可能大於10位數
|
||||
// Match numbers and units (days, hours, minutes, seconds); assume numbers have at most 10 digits
|
||||
let units = time.match(/\d{1,10}[dhms]/g);
|
||||
let formattedTime = '';
|
||||
let count = 0;
|
||||
|
||||
// 只保留最大的兩個單位
|
||||
// Keep only the two largest units
|
||||
for (let unit of units) {
|
||||
if (count >= 2) {
|
||||
break;
|
||||
@@ -271,7 +271,7 @@ export function formatMaxTwo(times) {
|
||||
formattedTime += unit + ' ';
|
||||
count++;
|
||||
}
|
||||
formattedTimes.push(formattedTime.trim()); // 去除末尾的空格
|
||||
formattedTimes.push(formattedTime.trim()); // Remove trailing whitespace
|
||||
}
|
||||
return formattedTimes;
|
||||
}
|
||||
|
||||
@@ -95,8 +95,8 @@ export function getTimeLabel(second, fixedNumber = 0) {
|
||||
const hour = 60 * 60;
|
||||
const minutes = 60;
|
||||
|
||||
// 取餘數的操作會把 second 限制在一天之內的範圍(即從0到86399之間),
|
||||
// 意思是過了多少天後剩下多少秒。
|
||||
// The modulo operation limits the value to within one day (0 to 86399),
|
||||
// representing the remaining seconds after full days.
|
||||
const dd = Math.floor(second / day);
|
||||
const hh = Math.floor((second % day) / hour);
|
||||
const mm = Math.floor((second % hour) / minutes);
|
||||
@@ -226,15 +226,15 @@ export const setTimeStringFormatBaseOnTimeDifference = (minTimeStamp, maxTimeSta
|
||||
|
||||
let dateFormat;
|
||||
if (timeDifferenceInSeconds < 60) {
|
||||
dateFormat = 'HH:mm:ss'; // 秒
|
||||
dateFormat = 'HH:mm:ss'; // Seconds range
|
||||
} else if (timeDifferenceInSeconds < 3600) {
|
||||
dateFormat = 'MM/DD HH:mm'; // 分鐘
|
||||
} else if (timeDifferenceInSeconds < 86400) { // 86400 秒 = 24 小時
|
||||
dateFormat = 'MM/DD HH:mm'; // 小時
|
||||
} else if (timeDifferenceInSeconds < 2592000) { // 2592000 秒 = 30 天
|
||||
dateFormat = 'YYYY/MM/DD'; // 天
|
||||
dateFormat = 'MM/DD HH:mm'; // Minutes range
|
||||
} else if (timeDifferenceInSeconds < 86400) { // 86400 seconds = 24 hours
|
||||
dateFormat = 'MM/DD HH:mm'; // Hours range
|
||||
} else if (timeDifferenceInSeconds < 2592000) { // 2592000 seconds = 30 days
|
||||
dateFormat = 'YYYY/MM/DD'; // Days range
|
||||
} else {
|
||||
dateFormat = 'YYYY/MM/DD'; // 月
|
||||
dateFormat = 'YYYY/MM/DD'; // Months range
|
||||
}
|
||||
|
||||
return dateFormat;
|
||||
|
||||
Reference in New Issue
Block a user