專案

一般

配置概況

工作單 #199 » P9802001-Merchant and Device Transaction Statistics-Business Text.json

marlboro chu, 2025-06-30 02:03

 
{
"result" : {
"id" : 631,
"orgId" : 1,
"folderId" : 136,
"folderUid" : "bemeqa2prz7k0d",
"uid" : "eenxdwupe52wwd",
"name" : "P9802001-Merchant and Device Transaction Statistics-Business Text",
"kind" : 1,
"type" : "marcusolsson-dynamictext-panel",
"description" : "",
"model" : {
"datasource" : {
"type" : "yesoreyeram-infinity-datasource",
"uid" : "aegaeyjq187b4e"
},
"description" : "",
"fieldConfig" : {
"defaults" : {
"thresholds" : {
"mode" : "absolute",
"steps" : [ {
"color" : "green",
"value" : null
}, {
"color" : "red",
"value" : 80
} ]
},
"unit" : "locale"
},
"overrides" : [ {
"matcher" : {
"id" : "byName",
"options" : "txAmount"
},
"properties" : [ {
"id" : "decimals",
"value" : 2
} ]
}, {
"matcher" : {
"id" : "byName",
"options" : "deviceDataCollection"
},
"properties" : [ {
"id" : "unit",
"value" : "percentunit"
} ]
} ]
},
"gridPos" : {
"h" : 11,
"w" : 24,
"x" : 0,
"y" : 29
},
"id" : 9802001,
"options" : {
"afterRender" : "let currentHighTxSortField = 'txCount';\r\nlet currentHighTxOrder = 'desc';\r\n\r\n\r\nconst highTxHeaders = [\r\n { text: 'Merchant', field: 'merchantName', type: 'text' },\r\n { text: 'Transactions', field: 'txCount', type: 'number' },\r\n { text: 'Device-Linked Transactions', field: 'txCountDevice', type: 'number' },\r\n { text: 'Total Amount (USD)', field: 'txAmount', type: 'number' },\r\n { text: 'Unique Devices', field: 'txCountUniDevice', type: 'number' },\r\n { text: 'Device Data Collection%', field: 'deviceDataCollection', type: 'text' },\r\n { text: 'High-Risk', field: 'decisionHight', type: 'number' },\r\n { text: 'Mid-Risk', field: 'decisionMid', type: 'number' },\r\n];\r\n\r\n// 加總欄位\r\nconst highTxTotals = {\r\n txCount: 0,\r\n txCountDevice: 0,\r\n txAmount: 0,\r\n txCountUniDevice: 0,\r\n decisionHight: 0,\r\n decisionMid:0\r\n};\r\n\r\n\r\n\r\nfunction setupHighTxTableHeader(data) {\r\n const tableHeader = document.getElementById('p98020301-table-header');\r\n tableHeader.innerHTML = '';\r\n\r\n const headerRow = document.createElement('tr');\r\n\r\n highTxHeaders.forEach(header => {\r\n const th = document.createElement('th');\r\n th.textContent = header.text;\r\n th.dataset.field = header.field;\r\n\r\n if (header.field !== 'countryName') {\r\n th.addEventListener('click', () => {\r\n if (currentHighTxSortField === header.field) {\r\n currentHighTxOrder = currentHighTxOrder === 'asc' ? 'desc' : 'asc';\r\n } else {\r\n currentHighTxSortField = header.field;\r\n currentHighTxOrder = 'asc';\r\n }\r\n generateHighTxItems(data, currentHighTxSortField, currentHighTxOrder);\r\n updateTableHeaderSortIndicator(headers);\r\n });\r\n }\r\n\r\n headerRow.appendChild(th);\r\n });\r\n\r\n tableHeader.appendChild(headerRow);\r\n}\r\n\r\nfunction generateHighTxItems(data, sortField = '', sortOrder = 'desc') {\r\n\r\n const tableBody = document.getElementById('p98020301-table-body');\r\n const tableFooter = document.getElementById('p98020301-table-footer');\r\n tableBody.innerHTML = '';\r\n tableFooter.innerHTML = '';\r\n\r\n // 排序\r\n data.sort((a, b) => {\r\n const valA = a[sortField];\r\n const valB = b[sortField];\r\n\r\n const numA = Number(String(valA).replace(/,/g, ''));\r\n const numB = Number(String(valB).replace(/,/g, ''));\r\n\r\n if (!isNaN(numA) && !isNaN(numB)) {\r\n return sortOrder === 'asc' ? numA - numB : numB - numA;\r\n } else {\r\n return sortOrder === 'asc'\r\n ? String(valA).localeCompare(String(valB))\r\n : String(valB).localeCompare(String(valA));\r\n }\r\n });\r\n\r\n resetHighTxTotals();\r\n\r\n data.forEach(item => {\r\n\r\n const row = document.createElement('tr');\r\n\r\n highTxHeaders.forEach(header => {\r\n\r\n let value = item[header.field];\r\n\r\n if (header.field === 'countryName') {\r\n value = value;\r\n } else if (header.type === 'number') {\r\n const numericValue = Number(String(value).replace(/,/g, '')) || 0;\r\n value = numericValue.toLocaleString();\r\n\r\n if (highTxTotals.hasOwnProperty(header.field)) {\r\n highTxTotals[header.field] += numericValue;\r\n }\r\n }\r\n row.appendChild(createCell(value));\r\n });\r\n\r\n tableBody.appendChild(row);\r\n\r\n });\r\n\r\n // 表尾加總列\r\n const footerRow = document.createElement('tr');\r\n highTxHeaders.forEach(header => {\r\n\r\n if (highTxTotals.hasOwnProperty(header.field)) {\r\n footerRow.appendChild(createCell(highTxTotals[header.field].toLocaleString(), true));\r\n } else if (header.field === 'merchantName') {\r\n footerRow.appendChild(createCell('Total', true));\r\n }\r\n else {\r\n footerRow.appendChild(createCell('', true));\r\n }\r\n\r\n });\r\n tableFooter.appendChild(footerRow);\r\n}\r\n\r\nfunction createCell(text, isFooter = false) {\r\n const cell = document.createElement('td');\r\n cell.textContent = text;\r\n if (isFooter) cell.style.fontWeight = 'bold';\r\n return cell;\r\n}\r\n\r\nfunction updateTableHeaderSortIndicator(headers) {\r\n const ths = document.querySelectorAll('#p98020301-table-header th');\r\n ths.forEach(th => {\r\n const field = th.dataset.field;\r\n const header = headers.find(h => h.field === field);\r\n if (!header) return;\r\n\r\n if (field === currentHighTxSortField) {\r\n th.textContent = `${header.text} ${currentHighTxOrder === 'asc' ? '▲' : '▼'}`;\r\n } else {\r\n th.textContent = header.text;\r\n }\r\n });\r\n}\r\n\r\nfunction resetHighTxTotals() {\r\n for (const key in highTxTotals) {\r\n if (Object.hasOwn(highTxTotals, key)) highTxTotals[key] = 0;\r\n }\r\n}\r\n\r\n// 初始化\r\nif (Array.isArray(context.data) && context.data.length > 0 && Array.isArray(context.data[0])) {\r\n const tableData = context.data[0];\r\n setupHighTxTableHeader(tableData);\r\n generateHighTxItems(tableData);\r\n}\r\n\r\n",
"content" : "<div class=\"p98020301-table-container\">\n <table class=\"p98020301-table\">\n <thead id=\"p98020301-table-header\"></thead> \n <tbody id=\"p98020301-table-body\"></tbody>\n </table>\n <p>\n <table class=\"p98020301-table-footer p98020301-table\">\n <tfoot id=\"p98020301-table-footer\"></tfoot>\n </table>\n</div>",
"contentPartials" : [ ],
"defaultContent" : "The query didn't return any results.",
"editor" : {
"format" : "auto",
"language" : "markdown"
},
"editors" : [ "styles", "afterRender" ],
"externalStyles" : [ ],
"helpers" : "",
"renderMode" : "data",
"styles" : "<style>\r\n.p98020301-table-container {\r\n width: 100%;\r\n margin: 0 auto;\r\n max-height: 180px; /* 必須設定 */\r\n overflow-y: auto; /* 讓內容可滾動 */\r\n position: relative; \r\n}\r\n\r\n/* 表格本體 */\r\n.p98020301-table {\r\n border-collapse: collapse;\r\n width: 100%;\r\n table-layout: fixed;\r\n}\r\n\r\n.p98020301-table-footer {\r\n position: sticky;\r\n bottom: 0;\r\n z-index: 3;\r\n pointer-events: none; /* 若不需互動 */\r\n backdrop-filter: blur(15px);\r\n}\r\n\r\n/* 固定 header 和 footer */\r\n.p98020301-table thead th, .p98020301-table tfoot td {\r\n position: sticky;\r\n z-index: 2;\r\n}\r\n\r\n/* 表頭固定在頂部 */\r\n.p98020301-table thead th {\r\n top: 0;\r\n text-align: center;\r\n}\r\n\r\n/* 表尾固定在底部 */\r\n.p98020301-table tfoot td {\r\n bottom: 0;\r\n}\r\n\r\n.p98020301-table td:first-child,\r\n.p98020301-table th:first-child {\r\n width: 16%;\r\n}\r\n\r\n.p98020301-table td:nth-child(7),\r\n.p98020301-table th:nth-child(7),\r\n.p98020301-table td:nth-child(8),\r\n.p98020301-table th:nth-child(8) {\r\n width: 10%;\r\n}\r\n\r\n/* 表格內容欄位 */\r\n.p98020301-table tbody td {\r\n padding: 8px;\r\n}\r\n\r\n/* 筆數與百分比靠右 */\r\n.p98020301-table tbody td, .p98020301-table-footer tbody td, {\r\n text-align: right;\r\n font-variant-numeric: tabular-nums;\r\n}\r\n\r\n.p98020301-table tbody td:first-child {\r\n text-align: left;\r\n}\r\n\r\n.p98020301-table tfoot td:first-child {\r\n text-align: left;\r\n}\r\n.p98020301-table-footer, .p98020301-table-footer td {\r\n border: none;\r\n}\r\n.p98020301-table-footer {\r\n border-top: 1px solid rgba(204, 204, 220, 0.2);;\r\n}\r\n\r\n/* 滑鼠提示效果 */\r\n.p98020301-table thead th {\r\n cursor: pointer;\r\n}\r\n.p98020301-table td {\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n white-space: nowrap;\r\n}\r\n</style>",
"wrap" : true
},
"pluginVersion" : "5.7.0",
"targets" : [ {
"columns" : [ ],
"datasource" : {
"type" : "yesoreyeram-infinity-datasource",
"uid" : "aegaeyjq187b4e"
},
"filters" : [ ],
"format" : "table",
"global_query_id" : "",
"parser" : "backend",
"refId" : "A",
"root_selector" : "",
"source" : "url",
"type" : "json",
"url" : "/smartfds-adm-web/report/api/store-aggs",
"url_options" : {
"data" : "",
"method" : "GET",
"params" : [ {
"key" : "operator_id",
"value" : "${operator_id}"
}, {
"key" : "institute_id",
"value" : "${institute_id}"
}, {
"key" : "connector_id",
"value" : "${connector_id}"
}, {
"key" : "from_time",
"value" : "${__from}"
}, {
"key" : "to_time",
"value" : "${__to}"
}, {
"key" : "auth_token",
"value" : "${auth_token}"
}, {
"key" : "merchant_id",
"value" : "${merchant_id}"
} ]
}
} ],
"title" : "B-03 Risk Summary by Merchant(Device Linkage & Risk Levels)",
"transformations" : [ {
"id" : "calculateField",
"options" : {
"alias" : "deviceDataCollection",
"binary" : {
"left" : {
"matcher" : {
"id" : "byName",
"options" : "txCountDevice"
}
},
"operator" : "/",
"right" : {
"matcher" : {
"id" : "byName",
"options" : "txCount"
}
}
},
"mode" : "binary",
"reduce" : {
"reducer" : "sum"
},
"replaceFields" : false
}
}, {
"id" : "organize",
"options" : {
"excludeByName" : {
"txRealAmount" : true
},
"includeByName" : { },
"indexByName" : {
"TransactionVolume" : 5,
"decisionHight" : 6,
"decisionMid" : 7,
"merchantName" : 0,
"txAmount" : 3,
"txCount" : 1,
"txCountDevice" : 2,
"txCountUniDevice" : 4,
"txRealAmount" : 8
},
"renameByName" : {
"Transaction Volume" : "Device Data Collection%",
"Transaction Volume (with Device Info)" : "Device Data Collection%",
"TransactionVolume" : "",
"decisionHight" : "",
"decisionMid" : "",
"merchantName" : "",
"txAmount" : "",
"txCount" : "",
"txCountDevice" : "",
"txCountUniDevice" : ""
}
}
} ],
"type" : "marcusolsson-dynamictext-panel"
},
"version" : 3,
"meta" : {
"folderName" : "R98-Library",
"folderUid" : "bemeqa2prz7k0d",
"connectedDashboards" : 2,
"created" : "2025-06-04T16:46:16+08:00",
"updated" : "2025-06-26T13:55:37+08:00",
"createdBy" : {
"avatarUrl" : "/avatar/ce6412d58e966caaa26cac12eb99734b",
"id" : 1,
"name" : "admin"
},
"updatedBy" : {
"avatarUrl" : "/avatar/ce6412d58e966caaa26cac12eb99734b",
"id" : 1,
"name" : "admin"
}
}
}
}
(19-19/30)