优化列表

This commit is contained in:
2025-10-20 16:14:59 +08:00
parent 1b45e61daf
commit 0cddf99456
4 changed files with 96 additions and 60 deletions

View File

@@ -5,53 +5,53 @@
<el-form-item v-for="col in filterableColumns" :key="col.dataIndex" :label="col.title"> <el-form-item v-for="col in filterableColumns" :key="col.dataIndex" :label="col.title">
<template v-if="col.filterType === 'text'"> <template v-if="col.filterType === 'text'">
<el-input <el-input
v-model="filters[col.dataIndex]" v-model="filters[col.dataIndex]"
:placeholder="`搜索 ${col.title}`" :placeholder="`搜索 ${col.title}`"
clearable clearable
@change="handleFilterChange(col.dataIndex, filters[col.dataIndex])" @change="handleFilterChange(col.dataIndex, filters[col.dataIndex])"
></el-input> ></el-input>
</template> </template>
<template v-else-if="col.filterType === 'number'"> <template v-else-if="col.filterType === 'number'">
<el-input-number <el-input-number
v-model="filters[col.dataIndex]" v-model="filters[col.dataIndex]"
:placeholder="`搜索 ${col.title}`" :placeholder="`搜索 ${col.title}`"
:controls="false" :controls="false"
clearable clearable
@change="handleFilterChange(col.dataIndex, filters[col.dataIndex])" @change="handleFilterChange(col.dataIndex, filters[col.dataIndex])"
></el-input-number> ></el-input-number>
</template> </template>
<template v-else-if="col.filterType === 'dateRange'"> <template v-else-if="col.filterType === 'dateRange'">
<el-date-picker <el-date-picker
v-model="filters[col.dataIndex]" v-model="filters[col.dataIndex]"
type="datetimerange" type="datetimerange"
range-separator="" range-separator=""
start-placeholder="开始日期" start-placeholder="开始日期"
end-placeholder="结束日期" end-placeholder="结束日期"
value-format="YYYY-MM-DD HH:mm:ss" value-format="YYYY-MM-DD HH:mm:ss"
@change="handleFilterChange(col.dataIndex, filters[col.dataIndex])" @change="handleFilterChange(col.dataIndex, filters[col.dataIndex])"
></el-date-picker> ></el-date-picker>
</template> </template>
<template v-else-if="col.filterType === 'select'"> <template v-else-if="col.filterType === 'select'">
<el-select <el-select
v-model="filters[col.dataIndex]" v-model="filters[col.dataIndex]"
:placeholder="`选择 ${col.title}`" :placeholder="`选择 ${col.title}`"
clearable clearable
@change="handleFilterChange(col.dataIndex, filters[col.dataIndex])" @change="handleFilterChange(col.dataIndex, filters[col.dataIndex])"
> >
<el-option <el-option
v-for="option in col.filterOptions" v-for="option in col.filterOptions"
:key="option.value" :key="option.value"
:label="option.text" :label="option.text"
:value="option.value" :value="option.value"
></el-option> ></el-option>
</el-select> </el-select>
</template> </template>
<template v-else-if="col.filterType === 'boolean'"> <template v-else-if="col.filterType === 'boolean'">
<el-select <el-select
v-model="filters[col.dataIndex]" v-model="filters[col.dataIndex]"
:placeholder="`选择 ${col.title}`" :placeholder="`选择 ${col.title}`"
clearable clearable
@change="handleFilterChange(col.dataIndex, filters[col.dataIndex])" @change="handleFilterChange(col.dataIndex, filters[col.dataIndex])"
> >
<el-option label="是" :value="true"></el-option> <el-option label="是" :value="true"></el-option>
<el-option label="否" :value="false"></el-option> <el-option label="否" :value="false"></el-option>
@@ -65,44 +65,46 @@
</el-form> </el-form>
<el-table <el-table
:data="data" :data="data"
v-loading="loading" v-loading="loading"
border border
style="width: 100%" stripe
@sort-change="handleSortChange" style="width: 100%"
@sort-change="handleSortChange"
> >
<el-table-column <el-table-column
v-for="col in tableColumns" v-for="col in tableColumns"
:key="col.key" :key="col.key"
:prop="col.prop" :prop="col.prop"
:label="col.title" :label="col.title"
:sortable="col.sorter ? 'custom' : false" :sortable="col.sorter ? 'custom' : false"
:formatter="col.formatter" :formatter="col.formatter"
:min-width="col.minWidth"
> >
<template v-if="col.render" #default="{ row }"> <template v-if="col.render" #default="{ row }">
<component :is="col.render(row)" /> <component :is="col.render(row)"/>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<el-pagination <el-pagination
@size-change="handleSizeChange" @size-change="handleSizeChange"
@current-change="handleCurrentChange" @current-change="handleCurrentChange"
:current-page="pagination.currentPage" :current-page="pagination.currentPage"
:page-sizes="[10, 20, 50, 100]" :page-sizes="[10, 20, 50, 100]"
:page-size="pagination.pageSize" :page-size="pagination.pageSize"
layout="total, sizes, prev, pager, next, jumper" layout="total, sizes, prev, pager, next, jumper"
:total="pagination.total" :total="pagination.total"
background background
style="margin-top: 20px; text-align: right;" style="margin-top: 20px; text-align: right;"
></el-pagination> ></el-pagination>
</el-card> </el-card>
</div> </div>
</template> </template>
<script setup> <script setup>
import { ref, reactive, onMounted, watch, computed } from 'vue'; import {ref, reactive, onMounted, watch, computed} from 'vue';
import { ElMessage } from 'element-plus'; import {ElMessage} from 'element-plus';
const props = defineProps({ const props = defineProps({
fetchData: { fetchData: {
@@ -134,7 +136,7 @@ const filterableColumns = computed(() => {
const tableColumns = computed(() => { const tableColumns = computed(() => {
return props.columnsConfig.map(col => { return props.columnsConfig.map(col => {
const newCol = { ...col }; const newCol = {...col};
newCol.prop = Array.isArray(col.dataIndex) ? col.dataIndex.join('.') : col.dataIndex; newCol.prop = Array.isArray(col.dataIndex) ? col.dataIndex.join('.') : col.dataIndex;
return newCol; return newCol;
}); });
@@ -182,7 +184,7 @@ const handleCurrentChange = (val) => {
loadData(); loadData();
}; };
const handleSortChange = ({ prop, order }) => { const handleSortChange = ({prop, order}) => {
sortOrder.prop = prop; sortOrder.prop = prop;
sortOrder.order = order; sortOrder.order = order;
loadData(); loadData();
@@ -213,10 +215,12 @@ onMounted(() => {
.generic-monitor-list { .generic-monitor-list {
padding: 20px; padding: 20px;
} }
.filter-form { .filter-form {
margin-bottom: 20px; margin-bottom: 20px;
} }
.el-card { .el-card {
border: none; border: none;
} }
</style> </style>

View File

@@ -2,6 +2,7 @@ import { createApp } from 'vue';
import { createRouter, createWebHistory } from 'vue-router'; import { createRouter, createWebHistory } from 'vue-router';
import ElementPlus from 'element-plus'; import ElementPlus from 'element-plus';
import 'element-plus/dist/index.css'; import 'element-plus/dist/index.css';
import zhCn from 'element-plus/dist/locale/zh-cn.mjs'; // 导入 Element Plus 中文语言包
import App from './App.vue'; import App from './App.vue';
import Home from './components/Home.vue'; import Home from './components/Home.vue';
@@ -47,8 +48,8 @@ router.beforeEach((to, from, next) => {
// 创建Vue应用实例 // 创建Vue应用实例
const app = createApp(App); const app = createApp(App);
// 使用Element Plus组件库 // 全局配置 Element Plus 为中文
app.use(ElementPlus); app.use(ElementPlus, { locale: zhCn });
// 使用路由 // 使用路由
app.use(router); app.use(router);

22
src/utils/format.js Normal file
View File

@@ -0,0 +1,22 @@
/**
* 将 RFC3339 格式的日期时间字符串格式化为 'YYYY-MM-DD HH:mm:ss'
* @param {string | null | undefined} rfc3339String - 后端返回的日期时间字符串
* @returns {string} - 格式化后的字符串,如果输入无效则返回空字符串或提示
*/
export function formatRFC3339(rfc3339String) {
if (!rfc3339String) {
return '--'; // 或者返回空字符串 ''
}
try {
// 例如: "2025-10-17T14:57:01.570169+08:00"
// 替换 'T' 为空格,并截取前 19 个字符即可得到 'YYYY-MM-DD HH:mm:ss'
return rfc3339String.replace('T', ' ').substring(0, 19);
} catch (error) {
console.error('Error formatting date:', rfc3339String, error);
return rfc3339String; // 格式化失败时返回原始值
}
}
// 你未来还可以添加其他全局格式化函数
// export function formatCurrency(number) { ... }

View File

@@ -1,15 +1,16 @@
<template> <template>
<div class="device-command-log-view"> <div class="device-command-log-view">
<GenericMonitorList <GenericMonitorList
:fetchData="fetchDeviceCommandLogs" :fetchData="fetchDeviceCommandLogs"
:columnsConfig="deviceCommandLogColumns" :columnsConfig="deviceCommandLogColumns"
/> />
</div> </div>
</template> </template>
<script setup> <script setup>
import GenericMonitorList from '../../components/GenericMonitorList.vue'; import GenericMonitorList from '../../components/GenericMonitorList.vue';
import { getDeviceCommandLogs } from '../../api/monitor.js'; import {getDeviceCommandLogs} from '../../api/monitor.js';
import {formatRFC3339} from '../../utils/format.js'; // 导入全局格式化函数
// 适配通用组件的 fetchData prop // 适配通用组件的 fetchData prop
const fetchDeviceCommandLogs = async (params) => { const fetchDeviceCommandLogs = async (params) => {
@@ -26,12 +27,15 @@ const deviceCommandLogColumns = [
key: 'device_id', key: 'device_id',
sorter: true, sorter: true,
filterType: 'number', // 设置筛选类型为数字 filterType: 'number', // 设置筛选类型为数字
minWidth: 100, // 设置最小宽度
}, },
{ {
title: '消息ID', title: '消息ID',
dataIndex: 'message_id', dataIndex: 'message_id',
key: 'message_id', key: 'message_id',
filterType: 'text', // 设置筛选类型为文本 // 后端不支持此字段筛选,暂时移除
// filterType: 'text',
minWidth: 300, // UUID较长设置较宽的最小宽度
}, },
{ {
title: '发送时间', title: '发送时间',
@@ -39,6 +43,8 @@ const deviceCommandLogColumns = [
key: 'sent_at', key: 'sent_at',
sorter: true, sorter: true,
filterType: 'dateRange', // 设置筛选类型为日期范围 filterType: 'dateRange', // 设置筛选类型为日期范围
formatter: (row, column, cellValue) => formatRFC3339(cellValue), // 使用全局格式化函数
minWidth: 180, // 设置最小宽度
}, },
{ {
title: '接收成功', title: '接收成功',
@@ -46,12 +52,15 @@ const deviceCommandLogColumns = [
key: 'received_success', key: 'received_success',
filterType: 'boolean', // 设置筛选类型为布尔值 filterType: 'boolean', // 设置筛选类型为布尔值
formatter: (row, column, cellValue) => (cellValue ? '是' : '否'), formatter: (row, column, cellValue) => (cellValue ? '是' : '否'),
minWidth: 100, // 设置最小宽度
}, },
{ {
title: '确认时间', title: '确认时间',
dataIndex: 'acknowledged_at', dataIndex: 'acknowledged_at',
key: 'acknowledged_at', key: 'acknowledged_at',
sorter: true, sorter: true,
formatter: (row, column, cellValue) => formatRFC3339(cellValue), // 使用全局格式化函数
minWidth: 180, // 设置最小宽度
}, },
]; ];
</script> </script>