|
"afterRender": "let currentSortField = 'merchantName'; // 預設排序欄位\r\nlet currentSortOrder = 'asc'; // 預設排序方向\r\n\r\nfunction generateRelationItems(data, sortField = 'txCount', sortOrder = 'desc') {\r\n const tableBody = document.getElementById('relation-table-body');\r\n tableBody.innerHTML = ''; // 清空舊資料\r\n\r\n // 排序\r\n data.sort((a, b) => {\r\n const rawA = a[sortField];\r\n const rawB = b[sortField];\r\n\r\n // 嘗試把逗號移除,轉成數字\r\n const numA = typeof rawA === 'string' ? Number(rawA.replace(/,/g, '')) : rawA;\r\n const numB = typeof rawB === 'string' ? Number(rawB.replace(/,/g, '')) : rawB;\r\n\r\n if (!isNaN(numA) && !isNaN(numB)) {\r\n // 都是有效數字,就用數字比較\r\n return sortOrder === 'asc' ? numA - numB : numB - numA;\r\n } else {\r\n // 有不是數字,就當字串比\r\n return sortOrder === 'asc'\r\n ? String(rawA).localeCompare(String(rawB))\r\n : String(rawB).localeCompare(String(rawA));\r\n }\r\n });\r\n\r\n // 產生每一列\r\n data.forEach(item => {\r\n const row = document.createElement('tr');\r\n\r\n // 商店名稱\r\n const merchantNameCell = document.createElement('td');\r\n merchantNameCell.className = 'kpi-merchant-name';\r\n merchantNameCell.textContent = item.create_datetime;\r\n row.appendChild(merchantNameCell);\r\n\r\n // 其他欄位\r\n const kpiFields = [\r\n item.diiaInfo__deviceInfo__udid,\r\n item.diiaInfo__deviceInfo__ip_request,\r\n item.veriid_trans_id,\r\n item.txInfo__acctNumber,\r\n ];\r\n\r\n kpiFields.forEach(value => {\r\n const cell = document.createElement('td');\r\n cell.className = 'kpi-value';\r\n cell.textContent = value;\r\n row.appendChild(cell);\r\n });\r\n\r\n tableBody.appendChild(row);\r\n });\r\n}\r\n\r\nfunction setupRelationTableHeader(data) {\r\n const tableHeader = document.getElementById('relation-table-header');\r\n tableHeader.innerHTML = ''; // 清空舊表頭\r\n\r\n // 中文表頭設定\r\n const headers = [\r\n { text: 'Date Time', field: 'create_datetime' },\r\n { text: 'UDID', field: 'diiaInfo__deviceInfo__udid' },\r\n { text: 'IP', field: 'diiaInfo__deviceInfo__ip_request' },\r\n { text: 'Transaction ID', field: 'veriid_trans_id' },\r\n { text: 'Account Number', field: 'txInfo__acctNumber' },\r\n ];\r\n\r\n const headerRow = document.createElement('tr');\r\n\r\n headers.forEach(header => {\r\n const th = document.createElement('th');\r\n th.textContent = header.text;\r\n th.style.cursor = 'pointer'; // 鼠標提示可以點\r\n th.addEventListener('click', () => {\r\n if (currentSortField === header.field) {\r\n currentSortOrder = currentSortOrder === 'asc' ? 'desc' : 'asc';\r\n } else {\r\n currentSortField = header.field;\r\n currentSortOrder = 'asc';\r\n }\r\n generateRelationItems(data, currentSortField, currentSortOrder);\r\n updateTableHeaderSortIndicator(headers);\r\n });\r\n th.dataset.field = header.field; // 記錄欄位屬性 (方便加箭頭)\r\n headerRow.appendChild(th);\r\n });\r\n\r\n tableHeader.appendChild(headerRow);\r\n}\r\n\r\nfunction updateTableHeaderSortIndicator(headers) {\r\n const thElements = document.querySelectorAll('#relation-table-header th');\r\n thElements.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 === currentSortField) {\r\n th.textContent = `${header.text} ${currentSortOrder === 'asc' ? '▲' : '▼'}`;\r\n } else {\r\n th.textContent = header.text;\r\n }\r\n });\r\n}\r\n\r\n\r\n\r\n\r\n// 初始化\r\nif (Array.isArray(context.data) && context.data.length > 0) {\r\n const tableData = context.data[0];\r\n setupRelationTableHeader(tableData);\r\n generateRelationItems(tableData);\r\n}\r\n",
|
|
"styles": "<style>\r\n/* 基本字型與排版 */\r\n.kpi-grid {\r\n display: grid;\r\n grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));\r\n gap: 24px;\r\n padding: 20px;\r\n font-family: 'Segoe UI', 'Roboto Mono', 'Noto Sans TC', monospace;\r\n}\r\n\r\n/* KPI 項目 */\r\n.kpi-item {\r\n border: 1px solid rgba(100, 181, 246, 0.4);\r\n border-radius: 12px;\r\n padding: 16px 20px;\r\n background: linear-gradient(160deg, rgba(30, 30, 30, 0.9), rgba(40, 40, 40, 0.6));\r\n box-shadow: 0 0 12px rgba(66, 165, 245, 0.2);\r\n transition: transform 0.2s ease;\r\n color: #fff;\r\n}\r\n.kpi-item:hover {\r\n transform: scale(1.02);\r\n box-shadow: 0 0 20px rgba(66, 165, 245, 0.4);\r\n}\r\n\r\n/* 表格樣式 */\r\n.relation-table {\r\n margin: auto;\r\n border-collapse: collapse;\r\n table-layout: fixed;\r\n border: 1px solid rgba(100, 181, 246, 0.2);\r\n font-family: 'Segoe UI', 'Roboto Mono', 'Noto Sans TC', monospace;\r\n // color: #fff;\r\n}\r\n\r\n/* Sticky 表頭和表尾 */\r\n.relation-table thead th,\r\n.relation-table tfoot td {\r\n position: sticky;\r\n z-index: 1;\r\n color: #fff; \r\n background: rgba(30, 30, 30, 0.95);\r\n}\r\n\r\n.relation-table thead th {\r\n top: 0;\r\n font-size: 12px;\r\n text-align: center;\r\n border-bottom: 1px solid rgba(100, 181, 246, 0.4);\r\n}\r\n\r\n.relation-table tfoot td {\r\n bottom: 0;\r\n font-weight: bold;\r\n border-top: 1px solid rgba(100, 181, 246, 0.4);\r\n}\r\n\r\n/* 表格內容 */\r\n.relation-table th,\r\n.relation-table td {\r\n padding: 8px 12px;\r\n min-width: 160px;\r\n text-align: right;\r\n white-space: nowrap;\r\n width: auto;\r\n}\r\n\r\n.relation-table td:last-child {\r\n text-align: left;\r\n}\r\n\r\n.relation-table td.kpi-merchant-name {\r\n font-size: 14px;\r\n letter-spacing: 1px;\r\n text-transform: uppercase;\r\n text-align: center;\r\n}\r\n\r\n.relation-table td.kpi-value {\r\n font-size: 14px;\r\n color: rgb(44, 132, 198);\r\n font-variant-numeric: tabular-nums;\r\n}\r\n\r\n.relation-table td.kpi-label {\r\n font-size: 14px;\r\n letter-spacing: 1px;\r\n text-transform: uppercase;\r\n text-align: left;\r\n}\r\n\r\n.relation-table tbody tr:hover {\r\n transform: scale(1.02);\r\n box-shadow: 0 0 20px rgba(66, 165, 245, 0.4);\r\n transition: all 0.2s ease;\r\n}\r\n\r\n\r\n",
|