@@ -553,6 +553,7 @@
< input type = "date" id = "alarmEndDate" style = "width:160px" >
< input type = "date" id = "alarmEndDate" style = "width:160px" >
< button class = "btn btn-primary" onclick = "loadAlarms()" > < i class = "fas fa-search" > < / i > 查询< / button >
< button class = "btn btn-primary" onclick = "loadAlarms()" > < i class = "fas fa-search" > < / i > 查询< / button >
< button class = "btn btn-secondary" onclick = "loadAlarms()" > < i class = "fas fa-sync-alt" > < / i > 刷新< / button >
< button class = "btn btn-secondary" onclick = "loadAlarms()" > < i class = "fas fa-sync-alt" > < / i > 刷新< / button >
< button class = "btn" style = "background:#b91c1c;color:#fff" id = "btnBatchDeleteAlarm" onclick = "batchDeleteSelectedAlarms()" disabled > < i class = "fas fa-trash-alt" > < / i > 删除选中 (< span id = "alarmSelCount" > 0< / span > )< / button >
< / div >
< / div >
< div class = "bg-gray-800 rounded-xl border border-gray-700 overflow-hidden relative" >
< div class = "bg-gray-800 rounded-xl border border-gray-700 overflow-hidden relative" >
< div id = "alarmsLoading" class = "loading-overlay" style = "display:none" > < div class = "spinner" > < / div > < / div >
< div id = "alarmsLoading" class = "loading-overlay" style = "display:none" > < div class = "spinner" > < / div > < / div >
@@ -560,6 +561,7 @@
< table >
< table >
< thead >
< thead >
< tr >
< tr >
< th style = "width:32px" > < input type = "checkbox" id = "alarmSelectAll" onchange = "toggleAllCheckboxes('alarm-sel-cb','alarmSelCount','btnBatchDeleteAlarm',this.checked)" title = "全选" > < / th >
< th > 设备ID< / th >
< th > 设备ID< / th >
< th > 类型< / th >
< th > 类型< / th >
< th > 来源< / th >
< th > 来源< / th >
@@ -572,7 +574,7 @@
< / tr >
< / tr >
< / thead >
< / thead >
< tbody id = "alarmsTableBody" >
< tbody id = "alarmsTableBody" >
< tr > < td colspan = "9 " class = "text-center text-gray-500 py-8" > 加载中...< / td > < / tr >
< tr > < td colspan = "10 " class = "text-center text-gray-500 py-8" > 加载中...< / td > < / tr >
< / tbody >
< / tbody >
< / table >
< / table >
< / div >
< / div >
@@ -635,6 +637,7 @@
< input type = "date" id = "attEndDate" style = "width:160px" >
< input type = "date" id = "attEndDate" style = "width:160px" >
< button class = "btn btn-primary" onclick = "loadAttendance()" > < i class = "fas fa-search" > < / i > 查询< / button >
< button class = "btn btn-primary" onclick = "loadAttendance()" > < i class = "fas fa-search" > < / i > 查询< / button >
< button class = "btn btn-secondary" onclick = "loadAttendance()" > < i class = "fas fa-sync-alt" > < / i > 刷新< / button >
< button class = "btn btn-secondary" onclick = "loadAttendance()" > < i class = "fas fa-sync-alt" > < / i > 刷新< / button >
< button class = "btn" style = "background:#b91c1c;color:#fff" id = "btnBatchDeleteAtt" onclick = "batchDeleteSelectedAttendance()" disabled > < i class = "fas fa-trash-alt" > < / i > 删除选中 (< span id = "attSelCount" > 0< / span > )< / button >
< / div >
< / div >
< div class = "bg-gray-800 rounded-xl border border-gray-700 overflow-hidden relative" >
< div class = "bg-gray-800 rounded-xl border border-gray-700 overflow-hidden relative" >
< div id = "attendanceLoading" class = "loading-overlay" style = "display:none" > < div class = "spinner" > < / div > < / div >
< div id = "attendanceLoading" class = "loading-overlay" style = "display:none" > < div class = "spinner" > < / div > < / div >
@@ -642,6 +645,7 @@
< table >
< table >
< thead >
< thead >
< tr >
< tr >
< th style = "width:32px" > < input type = "checkbox" id = "attSelectAll" onchange = "toggleAllCheckboxes('att-sel-cb','attSelCount','btnBatchDeleteAtt',this.checked)" title = "全选" > < / th >
< th > 设备ID< / th >
< th > 设备ID< / th >
< th > 类型< / th >
< th > 类型< / th >
< th > 来源< / th >
< th > 来源< / th >
@@ -653,7 +657,7 @@
< / tr >
< / tr >
< / thead >
< / thead >
< tbody id = "attendanceTableBody" >
< tbody id = "attendanceTableBody" >
< tr > < td colspan = "8 " class = "text-center text-gray-500 py-8" > 加载中...< / td > < / tr >
< tr > < td colspan = "9 " class = "text-center text-gray-500 py-8" > 加载中...< / td > < / tr >
< / tbody >
< / tbody >
< / table >
< / table >
< / div >
< / div >
@@ -701,6 +705,7 @@
< input type = "date" id = "btEndDate" style = "width:160px" >
< input type = "date" id = "btEndDate" style = "width:160px" >
< button class = "btn btn-primary" onclick = "loadBluetooth()" > < i class = "fas fa-search" > < / i > 查询< / button >
< button class = "btn btn-primary" onclick = "loadBluetooth()" > < i class = "fas fa-search" > < / i > 查询< / button >
< button class = "btn btn-secondary" onclick = "loadBluetooth()" > < i class = "fas fa-sync-alt" > < / i > 刷新< / button >
< button class = "btn btn-secondary" onclick = "loadBluetooth()" > < i class = "fas fa-sync-alt" > < / i > 刷新< / button >
< button class = "btn" style = "background:#b91c1c;color:#fff" id = "btnBatchDeleteBt" onclick = "batchDeleteSelectedBluetooth()" disabled > < i class = "fas fa-trash-alt" > < / i > 删除选中 (< span id = "btSelCount" > 0< / span > )< / button >
< / div >
< / div >
< div class = "bg-gray-800 rounded-xl border border-gray-700 overflow-hidden relative" >
< div class = "bg-gray-800 rounded-xl border border-gray-700 overflow-hidden relative" >
< div id = "bluetoothLoading" class = "loading-overlay" style = "display:none" > < div class = "spinner" > < / div > < / div >
< div id = "bluetoothLoading" class = "loading-overlay" style = "display:none" > < div class = "spinner" > < / div > < / div >
@@ -708,6 +713,7 @@
< table >
< table >
< thead >
< thead >
< tr >
< tr >
< th style = "width:32px" > < input type = "checkbox" id = "btSelectAll" onchange = "toggleAllCheckboxes('bt-sel-cb','btSelCount','btnBatchDeleteBt',this.checked)" title = "全选" > < / th >
< th > 设备ID< / th >
< th > 设备ID< / th >
< th > 类型< / th >
< th > 类型< / th >
< th > 信标MAC< / th >
< th > 信标MAC< / th >
@@ -719,7 +725,7 @@
< / tr >
< / tr >
< / thead >
< / thead >
< tbody id = "bluetoothTableBody" >
< tbody id = "bluetoothTableBody" >
< tr > < td colspan = "8 " class = "text-center text-gray-500 py-8" > 加载中...< / td > < / tr >
< tr > < td colspan = "9 " class = "text-center text-gray-500 py-8" > 加载中...< / td > < / tr >
< / tbody >
< / tbody >
< / table >
< / table >
< / div >
< / div >
@@ -906,6 +912,7 @@
< input type = "date" id = "logEndDate" style = "width:150px" >
< input type = "date" id = "logEndDate" style = "width:150px" >
< button class = "btn btn-primary" onclick = "loadDataLog()" > < i class = "fas fa-search" > < / i > 查询< / button >
< button class = "btn btn-primary" onclick = "loadDataLog()" > < i class = "fas fa-search" > < / i > 查询< / button >
< button class = "btn btn-secondary" onclick = "loadDataLog()" > < i class = "fas fa-sync-alt" > < / i > 刷新< / button >
< button class = "btn btn-secondary" onclick = "loadDataLog()" > < i class = "fas fa-sync-alt" > < / i > 刷新< / button >
< button class = "btn" style = "background:#b91c1c;color:#fff" id = "btnBatchDeleteLog" onclick = "batchDeleteSelectedDatalog()" disabled > < i class = "fas fa-trash-alt" > < / i > 删除选中 (< span id = "logSelCount" > 0< / span > )< / button >
< / div >
< / div >
< div class = "grid grid-cols-5 gap-3 mb-4" >
< div class = "grid grid-cols-5 gap-3 mb-4" >
< div class = "stat-card" style = "padding:14px;text-align:center;cursor:pointer" onclick = "document.getElementById('logTypeFilter').value='location';loadDataLog()" >
< div class = "stat-card" style = "padding:14px;text-align:center;cursor:pointer" onclick = "document.getElementById('logTypeFilter').value='location';loadDataLog()" >
@@ -935,6 +942,7 @@
< table >
< table >
< thead >
< thead >
< tr >
< tr >
< th style = "width:32px" > < input type = "checkbox" id = "logSelectAll" onchange = "toggleAllCheckboxes('log-sel-cb','logSelCount','btnBatchDeleteLog',this.checked)" title = "全选" > < / th >
< th > ID< / th >
< th > ID< / th >
< th > 类型< / th >
< th > 类型< / th >
< th > 设备ID< / th >
< th > 设备ID< / th >
@@ -946,7 +954,7 @@
< / tr >
< / tr >
< / thead >
< / thead >
< tbody id = "datalogTableBody" >
< tbody id = "datalogTableBody" >
< tr > < td colspan = "8 " class = "text-center text-gray-500 py-8" > 选择筛选条件后点击查询< / td > < / tr >
< tr > < td colspan = "9 " class = "text-center text-gray-500 py-8" > 选择筛选条件后点击查询< / td > < / tr >
< / tbody >
< / tbody >
< / table >
< / table >
< / div >
< / div >
@@ -1902,32 +1910,44 @@
}
}
}
}
function toggleAllLocCheckboxes ( checked ) {
// ==================== GENERIC BATCH DELETE HELPERS ====================
document . querySelectorAll ( '.loc-sel-cb' ) . forEach ( cb => { cb . checked = checked ; } ) ;
function toggleAllCheckboxes ( cbClass , countSpanId , btnId , checked ) {
updateLocSelCount ( ) ;
document . querySelectorAll ( '.' + cbClass ) . forEach ( cb => { cb . checked = checked ; } ) ;
updateSelCount ( cbClass , countSpanId , btnId ) ;
}
}
function updateSelCount ( cbClass , countSpanId , btnId ) {
function updateLocSelCount ( ) {
const count = document . querySelectorAll ( '.' + cbClass + ':checked' ) . length ;
const count = document . querySelectorAll ( '.loc-sel-cb:checked' ) . length ;
document . getElementById ( countSpanId ) . textContent = count ;
document . getElementById ( 'locSelCount' ) . textContent = count ;
document . getElementById ( btnId ) . disabled = count === 0 ;
document . getElementById ( 'btnBatchDeleteLoc' ) . disabled = count === 0 ;
}
}
// Location (compat wrappers)
function toggleAllLocCheckboxes ( checked ) { toggleAllCheckboxes ( 'loc-sel-cb' , 'locSelCount' , 'btnBatchDeleteLoc' , checked ) ; }
function updateLocSelCount ( ) { updateSelCount ( 'loc-sel-cb' , 'locSelCount' , 'btnBatchDeleteLoc' ) ; }
async function batchDeleteSelectedLocations ( ) {
async function _ batchDelete( cbClass , apiPath , idKey , label , reloadFn ) {
const ids = Array . from ( document . querySelectorAll ( '.loc-sel-cb :checked' ) ) . map ( cb => parseInt ( cb . value ) ) ;
const ids = Array . from ( document . querySelectorAll ( '.' + cbClass + ' :checked' ) ) . map ( cb => parseInt ( cb . value ) ) ;
if ( ids . length === 0 ) { showToast ( '请先勾选要删除的记录' , 'info' ) ; return ; }
if ( ids . length === 0 ) { showToast ( '请先勾选要删除的记录' , 'info' ) ; return ; }
if ( ! confirm ( ` 确定批量删除选中的 ${ ids . length } 条位置记录 ? ` ) ) return ;
if ( ! confirm ( ` 确定批量删除选中的 ${ ids . length } 条${ label } ? ` ) ) return ;
try {
try {
const result = await apiCall ( ` ${ API _BASE } /locations/batch-delete ` , {
const result = await apiCall ( ` ${ API _BASE } /${ apiPath } ` , {
method : 'POST' ,
method : 'POST' , body : JSON . stringify ( { [ idKey ] : ids } ) ,
body : JSON . stringify ( { location _ids : ids } ) ,
} ) ;
} ) ;
showToast ( ` 已删除 ${ result . deleted } 条记录 ` ) ;
showToast ( ` 已删除 ${ result . deleted } 条记录 ` ) ;
loadLocationRecords ( ) ;
re loadFn ( ) ;
} catch ( err ) {
} catch ( err ) {
showToast ( '批量删除失败: ' + err . message , 'error' ) ;
showToast ( '批量删除失败: ' + err . message , 'error' ) ;
}
}
}
}
function batchDeleteSelectedLocations ( ) { _batchDelete ( 'loc-sel-cb' , 'locations/batch-delete' , 'location_ids' , '位置记录' , loadLocationRecords ) ; }
function batchDeleteSelectedAlarms ( ) { _batchDelete ( 'alarm-sel-cb' , 'alarms/batch-delete' , 'alarm_ids' , '告警记录' , loadAlarms ) ; }
function batchDeleteSelectedAttendance ( ) { _batchDelete ( 'att-sel-cb' , 'attendance/batch-delete' , 'attendance_ids' , '考勤记录' , loadAttendance ) ; }
function batchDeleteSelectedBluetooth ( ) { _batchDelete ( 'bt-sel-cb' , 'bluetooth/batch-delete' , 'record_ids' , '蓝牙记录' , loadBluetooth ) ; }
function batchDeleteSelectedDatalog ( ) {
const logType = document . getElementById ( 'logTypeFilter' ) . value || 'location' ;
const apiMap = { location : [ 'locations/batch-delete' , 'location_ids' ] , alarm : [ 'alarms/batch-delete' , 'alarm_ids' ] , attendance : [ 'attendance/batch-delete' , 'attendance_ids' ] , bluetooth : [ 'bluetooth/batch-delete' , 'record_ids' ] , heartbeat : [ 'heartbeats/batch-delete' , 'record_ids' ] } ;
const [ path , key ] = apiMap [ logType ] || apiMap . location ;
_batchDelete ( 'log-sel-cb' , path , key , '记录' , loadDataLog ) ;
}
async function batchDeleteNoCoordLocations ( ) {
async function batchDeleteNoCoordLocations ( ) {
const deviceId = document . getElementById ( 'locDeviceSelect' ) . value || null ;
const deviceId = document . getElementById ( 'locDeviceSelect' ) . value || null ;
@@ -2608,10 +2628,11 @@
const tbody = document . getElementById ( 'alarmsTableBody' ) ;
const tbody = document . getElementById ( 'alarmsTableBody' ) ;
if ( items . length === 0 ) {
if ( items . length === 0 ) {
tbody . innerHTML = '<tr><td colspan="9 " class="text-center text-gray-500 py-8">没有告警记录</td></tr>' ;
tbody . innerHTML = '<tr><td colspan="10 " class="text-center text-gray-500 py-8">没有告警记录</td></tr>' ;
} else {
} else {
tbody . innerHTML = items . map ( a => `
tbody . innerHTML = items . map ( a => `
<tr>
<tr>
<td onclick="event.stopPropagation()"><input type="checkbox" class="alarm-sel-cb" value=" ${ a . id } " onchange="updateSelCount('alarm-sel-cb','alarmSelCount','btnBatchDeleteAlarm')"></td>
<td class="font-mono text-xs"> ${ escapeHtml ( a . device _id || '-' ) } </td>
<td class="font-mono text-xs"> ${ escapeHtml ( a . device _id || '-' ) } </td>
<td><span class=" ${ alarmTypeClass ( a . alarm _type ) } font-semibold"> ${ alarmTypeName ( a . alarm _type ) } </span></td>
<td><span class=" ${ alarmTypeClass ( a . alarm _type ) } font-semibold"> ${ alarmTypeName ( a . alarm _type ) } </span></td>
<td> ${ escapeHtml ( a . alarm _source || '-' ) } </td>
<td> ${ escapeHtml ( a . alarm _source || '-' ) } </td>
@@ -2629,10 +2650,11 @@
</tr>
</tr>
` ) . join ( '' ) ;
` ) . join ( '' ) ;
}
}
document . getElementById ( 'alarmSelectAll' ) . checked = false ;
renderPagination ( 'alarmsPagination' , data . total || 0 , data . page || p , data . page _size || ps , 'loadAlarms' ) ;
renderPagination ( 'alarmsPagination' , data . total || 0 , data . page || p , data . page _size || ps , 'loadAlarms' ) ;
} catch ( err ) {
} catch ( err ) {
showToast ( '加载告警失败: ' + err . message , 'error' ) ;
showToast ( '加载告警失败: ' + err . message , 'error' ) ;
document . getElementById ( 'alarmsTableBody' ) . innerHTML = '<tr><td colspan="9 " class="text-center text-red-400 py-8">加载失败</td></tr>' ;
document . getElementById ( 'alarmsTableBody' ) . innerHTML = '<tr><td colspan="10 " class="text-center text-red-400 py-8">加载失败</td></tr>' ;
} finally {
} finally {
hideLoading ( 'alarmsLoading' ) ;
hideLoading ( 'alarmsLoading' ) ;
}
}
@@ -2698,7 +2720,7 @@
const tbody = document . getElementById ( 'attendanceTableBody' ) ;
const tbody = document . getElementById ( 'attendanceTableBody' ) ;
if ( items . length === 0 ) {
if ( items . length === 0 ) {
tbody . innerHTML = '<tr><td colspan="8 " class="text-center text-gray-500 py-8">没有考勤记录</td></tr>' ;
tbody . innerHTML = '<tr><td colspan="9 " class="text-center text-gray-500 py-8">没有考勤记录</td></tr>' ;
} else {
} else {
tbody . innerHTML = items . map ( a => {
tbody . innerHTML = items . map ( a => {
const posStr = a . address || ( a . latitude != null ? ` ${ Number ( a . latitude ) . toFixed ( 6 ) } , ${ Number ( a . longitude ) . toFixed ( 6 ) } ` : '-' ) ;
const posStr = a . address || ( a . latitude != null ? ` ${ Number ( a . latitude ) . toFixed ( 6 ) } , ${ Number ( a . longitude ) . toFixed ( 6 ) } ` : '-' ) ;
@@ -2709,6 +2731,7 @@
const srcLabel = { 'device' : '<i class="fas fa-mobile-alt"></i> 设备' , 'bluetooth' : '<i class="fab fa-bluetooth-b"></i> 蓝牙' , 'fence' : '<i class="fas fa-draw-polygon"></i> 围栏' } [ a . attendance _source ] || a . attendance _source || '设备' ;
const srcLabel = { 'device' : '<i class="fas fa-mobile-alt"></i> 设备' , 'bluetooth' : '<i class="fab fa-bluetooth-b"></i> 蓝牙' , 'fence' : '<i class="fas fa-draw-polygon"></i> 围栏' } [ a . attendance _source ] || a . attendance _source || '设备' ;
const srcColor = { 'device' : '#9ca3af' , 'bluetooth' : '#818cf8' , 'fence' : '#34d399' } [ a . attendance _source ] || '#9ca3af' ;
const srcColor = { 'device' : '#9ca3af' , 'bluetooth' : '#818cf8' , 'fence' : '#34d399' } [ a . attendance _source ] || '#9ca3af' ;
return ` <tr>
return ` <tr>
<td onclick="event.stopPropagation()"><input type="checkbox" class="att-sel-cb" value=" ${ a . id } " onchange="updateSelCount('att-sel-cb','attSelCount','btnBatchDeleteAtt')"></td>
<td class="font-mono text-xs"> ${ escapeHtml ( a . device _id || '-' ) } </td>
<td class="font-mono text-xs"> ${ escapeHtml ( a . device _id || '-' ) } </td>
<td><span class=" ${ a . attendance _type === 'clock_in' ? 'text-green-400' : 'text-blue-400' } font-semibold"> ${ attendanceTypeName ( a . attendance _type ) } </span></td>
<td><span class=" ${ a . attendance _type === 'clock_in' ? 'text-green-400' : 'text-blue-400' } font-semibold"> ${ attendanceTypeName ( a . attendance _type ) } </span></td>
<td style="color: ${ srcColor } ;font-size:12px"> ${ srcLabel } </td>
<td style="color: ${ srcColor } ;font-size:12px"> ${ srcLabel } </td>
@@ -2720,10 +2743,11 @@
</tr> ` ;
</tr> ` ;
} ) . join ( '' ) ;
} ) . join ( '' ) ;
}
}
document . getElementById ( 'attSelectAll' ) . checked = false ;
renderPagination ( 'attendancePagination' , data . total || 0 , data . page || p , data . page _size || ps , 'loadAttendance' ) ;
renderPagination ( 'attendancePagination' , data . total || 0 , data . page || p , data . page _size || ps , 'loadAttendance' ) ;
} catch ( err ) {
} catch ( err ) {
showToast ( '加载考勤记录失败: ' + err . message , 'error' ) ;
showToast ( '加载考勤记录失败: ' + err . message , 'error' ) ;
document . getElementById ( 'attendanceTableBody' ) . innerHTML = '<tr><td colspan="8 " class="text-center text-red-400 py-8">加载失败</td></tr>' ;
document . getElementById ( 'attendanceTableBody' ) . innerHTML = '<tr><td colspan="9 " class="text-center text-red-400 py-8">加载失败</td></tr>' ;
} finally {
} finally {
hideLoading ( 'attendanceLoading' ) ;
hideLoading ( 'attendanceLoading' ) ;
}
}
@@ -2764,7 +2788,7 @@
const tbody = document . getElementById ( 'bluetoothTableBody' ) ;
const tbody = document . getElementById ( 'bluetoothTableBody' ) ;
if ( items . length === 0 ) {
if ( items . length === 0 ) {
tbody . innerHTML = '<tr><td colspan="8 " class="text-center text-gray-500 py-8">没有蓝牙记录</td></tr>' ;
tbody . innerHTML = '<tr><td colspan="9 " class="text-center text-gray-500 py-8">没有蓝牙记录</td></tr>' ;
} else {
} else {
tbody . innerHTML = items . map ( b => {
tbody . innerHTML = items . map ( b => {
const typeIcon = b . record _type === 'punch' ? '<i class="fas fa-fingerprint text-purple-400"></i>' : '<i class="fas fa-map-marker-alt text-cyan-400"></i>' ;
const typeIcon = b . record _type === 'punch' ? '<i class="fas fa-fingerprint text-purple-400"></i>' : '<i class="fas fa-map-marker-alt text-cyan-400"></i>' ;
@@ -2777,6 +2801,7 @@
const battStr = b . beacon _battery != null ? ` ${ Number ( b . beacon _battery ) . toFixed ( 2 ) } ${ b . beacon _battery _unit || 'V' } ` : '-' ;
const battStr = b . beacon _battery != null ? ` ${ Number ( b . beacon _battery ) . toFixed ( 2 ) } ${ b . beacon _battery _unit || 'V' } ` : '-' ;
const attStr = b . attendance _type ? ` <span class=" ${ b . attendance _type === 'clock_in' ? 'text-green-400' : 'text-blue-400' } "> ${ attendanceTypeName ( b . attendance _type ) } </span> ` : '-' ;
const attStr = b . attendance _type ? ` <span class=" ${ b . attendance _type === 'clock_in' ? 'text-green-400' : 'text-blue-400' } "> ${ attendanceTypeName ( b . attendance _type ) } </span> ` : '-' ;
return ` <tr>
return ` <tr>
<td onclick="event.stopPropagation()"><input type="checkbox" class="bt-sel-cb" value=" ${ b . id } " onchange="updateSelCount('bt-sel-cb','btSelCount','btnBatchDeleteBt')"></td>
<td class="font-mono text-xs"> ${ escapeHtml ( b . device _id || '-' ) } </td>
<td class="font-mono text-xs"> ${ escapeHtml ( b . device _id || '-' ) } </td>
<td> ${ typeIcon } <span class="font-semibold"> ${ typeName } </span></td>
<td> ${ typeIcon } <span class="font-semibold"> ${ typeName } </span></td>
<td class="font-mono text-xs"> ${ escapeHtml ( mac ) } </td>
<td class="font-mono text-xs"> ${ escapeHtml ( mac ) } </td>
@@ -2788,10 +2813,11 @@
</tr> ` ;
</tr> ` ;
} ) . join ( '' ) ;
} ) . join ( '' ) ;
}
}
document . getElementById ( 'btSelectAll' ) . checked = false ;
renderPagination ( 'bluetoothPagination' , data . total || 0 , data . page || p , data . page _size || ps , 'loadBluetooth' ) ;
renderPagination ( 'bluetoothPagination' , data . total || 0 , data . page || p , data . page _size || ps , 'loadBluetooth' ) ;
} catch ( err ) {
} catch ( err ) {
showToast ( '加载蓝牙记录失败: ' + err . message , 'error' ) ;
showToast ( '加载蓝牙记录失败: ' + err . message , 'error' ) ;
document . getElementById ( 'bluetoothTableBody' ) . innerHTML = '<tr><td colspan="8 " class="text-center text-red-400 py-8">加载失败</td></tr>' ;
document . getElementById ( 'bluetoothTableBody' ) . innerHTML = '<tr><td colspan="9 " class="text-center text-red-400 py-8">加载失败</td></tr>' ;
} finally {
} finally {
hideLoading ( 'bluetoothLoading' ) ;
hideLoading ( 'bluetoothLoading' ) ;
}
}
@@ -2919,7 +2945,7 @@
const tbody = document . getElementById ( 'datalogTableBody' ) ;
const tbody = document . getElementById ( 'datalogTableBody' ) ;
if ( items . length === 0 ) {
if ( items . length === 0 ) {
tbody . innerHTML = '<tr><td colspan="8 " class="text-center text-gray-500 py-8">暂无记录</td></tr>' ;
tbody . innerHTML = '<tr><td colspan="9 " class="text-center text-gray-500 py-8">暂无记录</td></tr>' ;
} else {
} else {
tbody . innerHTML = items . map ( r => {
tbody . innerHTML = items . map ( r => {
const detail = _logDetail ( type , r ) ;
const detail = _logDetail ( type , r ) ;
@@ -2930,6 +2956,7 @@
const time = r . recorded _at || r . alarm _time || r . heartbeat _time || r . created _at ;
const time = r . recorded _at || r . alarm _time || r . heartbeat _time || r . created _at ;
const typeBadge = _logTypeBadge ( type ) ;
const typeBadge = _logTypeBadge ( type ) ;
return ` <tr>
return ` <tr>
<td onclick="event.stopPropagation()"><input type="checkbox" class="log-sel-cb" value=" ${ r . id } " onchange="updateSelCount('log-sel-cb','logSelCount','btnBatchDeleteLog')"></td>
<td class="font-mono text-xs"> ${ r . id } </td>
<td class="font-mono text-xs"> ${ r . id } </td>
<td> ${ typeBadge } </td>
<td> ${ typeBadge } </td>
<td> ${ r . device _id || '-' } </td>
<td> ${ r . device _id || '-' } </td>
@@ -2941,9 +2968,10 @@
</tr> ` ;
</tr> ` ;
} ) . join ( '' ) ;
} ) . join ( '' ) ;
}
}
document . getElementById ( 'logSelectAll' ) . checked = false ;
renderPagination ( 'datalogPagination' , data . total || 0 , data . page || p , data . page _size || ps , 'loadDataLog' ) ;
renderPagination ( 'datalogPagination' , data . total || 0 , data . page || p , data . page _size || ps , 'loadDataLog' ) ;
} catch ( err ) {
} catch ( err ) {
document . getElementById ( 'datalogTableBody' ) . innerHTML = '<tr><td colspan="8 " class="text-center text-red-400 py-8">加载失败: ' + escapeHtml ( err . message ) + '</td></tr>' ;
document . getElementById ( 'datalogTableBody' ) . innerHTML = '<tr><td colspan="9 " class="text-center text-red-400 py-8">加载失败: ' + escapeHtml ( err . message ) + '</td></tr>' ;
} finally {
} finally {
hideLoading ( 'datalogLoading' ) ;
hideLoading ( 'datalogLoading' ) ;
}
}