设备数据卡片列表展示

master
yangxiaozhong 3 weeks ago
parent 66fad8c586
commit 7aa2b3eedc

@ -1,303 +1,315 @@
<script setup lang="ts">
import type { VbenFormProps } from '@vben/common-ui';
import type { DeviceQuery, DeviceVO } from '#/api/hazard/device/model';
import type { VxeGridProps } from '#/adapter/vxe-table';
import type { DeviceForm } from '#/api/hazard/device/model';
import { onMounted, ref } from 'vue';
import { h, onMounted, ref } from 'vue';
import { useRouter } from 'vue-router';
import { useVbenModal } from '@vben/common-ui';
import {
DeleteOutlined,
EditOutlined,
EyeOutlined,
} from '@ant-design/icons-vue';
import {
Button,
Card,
Form,
Input,
Layout,
List,
Pagination,
Popconfirm,
RadioButton,
RadioGroup,
Tag,
} from 'ant-design-vue';
import { Page, useVbenModal } from '@vben/common-ui';
import { getVxePopupContainer } from '@vben/utils';
import {deviceExport, deviceList, deviceRemove} from '#/api/hazard/device';
import deviceModal from '#/views/hazard/device/device-modal.vue';
import {useRouter} from "vue-router";
import {commonDownloadExcel} from "#/utils/file/download";
//
const searchForm = ref<DeviceQuery>({ name: null, status: null });
const router = useRouter();
//
const current = ref<number>(1);
const currentSize = ref<number>(10);
const total = ref<number>(1);
const pageSizeOptions = ['5', '10', '20', '30', '40', '50'];
//
const listData = ref<DeviceVO[]>([]);
const [DeviceModal, modalApi] = useVbenModal({
connectedComponent: deviceModal,
});
//
const handleSearch = async () => {
// console.log(':', searchForm.keyword);
// API
const params = searchForm.value;
const response = await deviceList(params);
listData.value = response.rows;
console.log('handleSearch', listData.value);
};
import { Modal, Popconfirm, Space } from 'ant-design-vue';
//
const handleReset = async () => {
// searchForm.keyword = '';
console.log('重置搜索条件');
searchForm.value = {};
const response = await deviceList();
listData.value = response.rows;
console.log('handleSearch', listData.value);
};
import { useVbenVxeGrid, vxeCheckboxChecked } from '#/adapter/vxe-table';
import { deviceExport, deviceList, deviceRemove } from '#/api/hazard/device';
import { sensorQuery } from '#/api/hazard/sensor';
import { commonDownloadExcel } from '#/utils/file/download';
//
const handlePageChange = async (page: number) => {
console.log('当前页码:', page);
current.value = page;
const params = {
...searchForm.value,
pageNum: page,
pageSize: currentSize.value,
};
const response = await deviceList(params);
listData.value = response.rows;
total.value = response.total;
//
};
import { columns, querySchema } from './data';
import deviceModal from './device-modal.vue';
//
let sensors = ref<[]>([]);
const router = useRouter();
const exTarColumns: VxeGridProps['columns'] = [
{
title: '',
fixed: 'left',
width: 60,
slots: {
header: ({ _row }) => {
const selectArr = tableApi.grid.getCheckboxRecords();
const { visibleData } = tableApi.grid.getTableData();
return [
h('input', {
type: 'checkbox',
checked: selectArr.length === visibleData.length,
onChange: () => {
//
if (selectArr.length === visibleData.length) {
tableApi.grid.clearCheckboxRow();
} else {
tableApi.grid.toggleAllCheckboxRow();
}
},
style: {
height: '18px',
width: '18px',
cursor: 'pointer',
margin: 'auto',
display: 'block',
},
}),
];
},
default: ({ row, rowIndex }) => {
const isChecked = tableApi.grid.getCheckboxRecords().includes(row); //
const { pager } = tableApi.grid.getProxyInfo();
const pageSize = pager.pageSize;
const pageIndex = pager.currentPage;
return [
//
row.isHovered || isChecked
? h('input', {
type: 'checkbox',
checked: tableApi.grid.getCheckboxRecords().includes(row), // 使
onChange: () => {
tableApi.grid.toggleCheckboxRow(row); //
},
style: {
height: '18px',
width: '18px',
cursor: 'pointer',
margin: 'auto',
display: 'block',
},
})
: h('span', (pageIndex - 1) * pageSize + rowIndex + 1), //
];
},
},
},
];
//
const handlePageSizeChange = async (page: number, size: number) => {
console.log(`每页条数变为: ${size}`);
//
current.value = page;
currentSize.value = size;
//
const params = {
...searchForm.value,
pageNum: page,
pageSize: size,
};
const response = await deviceList(params);
listData.value = response.rows;
total.value = response.total;
};
//
const handleAdd = () => {
console.log('新增设备');
//
modalApi.setData({});
modalApi.open();
};
const handleView = async (item: DeviceVO) => {
await router.push(`/hazard/devicePreview/${item.id}`);
};
const handleEdit = async (item: DeviceVO) => {
console.log('编辑设备', item);
modalApi.setData({ id: item.id });
modalApi.open();
//
const response = await deviceList({ pageNum: 1, pageSize: 10 });
listData.value = response.rows;
total.value = response.total;
};
const formOptions: VbenFormProps = {
commonConfig: {
labelWidth: 80,
componentProps: {
allowClear: true,
},
},
schema: querySchema(),
wrapperClass: 'grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4',
// RangePicker /
//
// fieldMappingTime: [
// [
// 'createTime',
// ['params[beginTime]', 'params[endTime]'],
// ['YYYY-MM-DD 00:00:00', 'YYYY-MM-DD 23:59:59'],
// ],
// ],
const confirm = async (item: DeviceVO) => {
await handleDelete(item);
};
const gridOptions: VxeGridProps = {
checkboxConfig: {
//
highlight: true,
//
reserve: true,
//
// trigger: 'row',
trigger: 'cell',
},
// 使i18ngetter
// columns: columns(),
columns: [...exTarColumns, ...columns],
border: true,
height: 'auto',
keepSource: true,
pagerConfig: {},
proxyConfig: {
ajax: {
query: async ({ page }, formValues = {}) => {
return await deviceList({
pageNum: page.currentPage,
pageSize: page.pageSize,
...formValues,
});
},
},
},
rowConfig: {
isHover: true,
keyField: 'id',
},
//
// cellConfig: {
// //
// height: 200,
// },
//
id: 'hazard-device-index',
const cancel = () => {
};
//
const handleDelete = async (item: DeviceVO) => {
await deviceRemove(item.id);
const params = {
...searchForm.value,
pageNum: current.value,
pageSize: currentSize.value,
};
const response = await deviceList(params);
listData.value = response.rows;
total.value = response.total;
};
const checked = ref(false);
const [BasicTable, tableApi] = useVbenVxeGrid({
formOptions,
gridOptions,
gridEvents: {
cellMouseenter: (params) => {
const { row } = params;
row.isHovered = true;
},
cellMouseleave: (params) => {
const { row } = params;
row.isHovered = false;
},
},
});
const [DeviceModal, modalApi] = useVbenModal({
connectedComponent: deviceModal,
//
const handleExport = () => {
console.log('导出设备列表');
commonDownloadExcel(deviceExport, '设备数据', searchForm.value, {
fieldMappingTime: [], //
});
};
onMounted(async () => {
const response = await deviceList({ pageNum: 1, pageSize: 10 });
listData.value = response.rows;
total.value = response.total;
console.log(response.rows);
});
</script>
function handleAdd() {
modalApi.setData({});
modalApi.open();
<template>
<Layout>
<Layout.Content class="page-content">
<!-- 搜索条件区域 -->
<div class="search-container">
<div class="search-form-wrapper">
<Form layout="inline" :model="searchForm">
<div class="search-conditions">
<Form.Item label="设备名称">
<Input v-model:value="searchForm.name" placeholder="请输入" />
</Form.Item>
<Form.Item label="状态">
<RadioGroup
v-model:value="searchForm.status"
button-style="solid"
>
<RadioButton value="0">离线</RadioButton>
<RadioButton value="1">在线</RadioButton>
</RadioGroup>
</Form.Item>
</div>
<div class="search-buttons">
<Form.Item>
<Button style="margin-right: 8px" @click="handleReset">
重置
</Button>
<Button type="primary" @click="handleSearch"></Button>
</Form.Item>
</div>
</Form>
</div>
</div>
<!-- 列表和分页区域 -->
<div class="list-container">
<div class="list-actions">
<div class="list-title">设备列表</div>
<div class="action-buttons">
<Button @click="handleExport"></Button>
<Button style="margin-left: 8px" @click="handleAdd"></Button>
</div>
</div>
<List
:grid="{ gutter: 16, column: 4 }"
:data-source="listData"
:pagination="false"
class="list-content"
>
<template #renderItem="{ item }">
<List.Item>
<Card hoverable style="width: 100%">
<Card.Meta :title="item.name" />
<div style="margin-top: 18px">
<Tag :color="item.status === '1' ? 'green' : 'red'">
{{ item.status === '1' ? '在线' : '离线' }}
</Tag>
</div>
<template #actions>
<EyeOutlined key="view" @click="handleView(item)" />
<EditOutlined key="edit" @click="handleEdit(item)" />
<Popconfirm
title="确定要删除这个设备吗?"
ok-text="是"
cancel-text="否"
@confirm="confirm(item)"
@cancel="cancel"
>
<DeleteOutlined key="delete" @click="" />
</Popconfirm>
</template>
</Card>
</List.Item>
</template>
</List>
<!-- 分页组件 -->
<div class="pagination-wrapper">
<Pagination
v-model:current="current"
:total="total"
show-size-changer
:page-size-options="pageSizeOptions"
:show-total="(total) => `共 ${total} 条记录`"
@change="handlePageChange"
@show-size-change="handlePageSizeChange"
/>
</div>
</div>
</Layout.Content>
</Layout>
<DeviceModal @reload="handleReset()" />
</template>
<style scoped>
.page-content {
padding: 18px;
background-color: #f0f2f5;
min-height: calc(100vh - 100px);
}
async function handleEdit(row: Required<DeviceForm>) {
modalApi.setData({ id: row.id });
modalApi.open();
.search-container {
background: #fff;
padding: 18px;
margin-bottom: 16px;
border-radius: 4px;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.05);
}
async function handlePreview(row: Required<DeviceForm>) {
await router.push(`/hazard/devicePreview/${row.id}`);
.search-form-wrapper {
display: flex;
justify-content: space-between;
align-items: center;
}
async function handleDelete(row: Required<DeviceForm>) {
await deviceRemove(row.id);
await tableApi.query();
.search-conditions {
display: flex;
align-items: center;
}
function handleMultiDelete() {
const rows = tableApi.grid.getCheckboxRecords();
const ids = rows.map((row: Required<DeviceForm>) => row.id);
Modal.confirm({
title: '提示',
okType: 'danger',
content: `确认删除选中的${ids.length}条记录吗?`,
onOk: async () => {
await deviceRemove(ids);
await tableApi.query();
checked.value = false;
},
});
.search-buttons {
display: flex;
justify-content: flex-end;
}
function handleDownloadExcel() {
commonDownloadExcel(deviceExport, '设备数据', tableApi.formApi.form.values, {
fieldMappingTime: formOptions.fieldMappingTime,
});
.list-container {
background: #fff;
padding: 12px 24px;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.05);
min-height: calc(100vh - 200px);
display: flex;
flex-direction: column;
}
.list-content {
flex: 1;
}
.pagination-wrapper {
margin-top: 24px;
text-align: center;
}
//
onMounted(async () => {
try {
sensors.value = await sensorQuery();
} catch (error) {
console.error('获取传感器数据失败:', error);
}
});
</script>
:deep(.ant-layout-content) {
min-height: calc(100vh - 100px);
}
<template>
<Page :auto-content-height="true">
<BasicTable table-title="">
<template #toolbar-tools>
<Space>
<a-button
v-access:code="['hazard:device:export']"
@click="handleDownloadExcel"
>
{{ $t('pages.common.export') }}
</a-button>
<a-button
:disabled="!vxeCheckboxChecked(tableApi)"
danger
type="primary"
v-access:code="['hazard:device:remove']"
@click="handleMultiDelete"
>
{{ $t('pages.common.delete') }}
</a-button>
<a-button
type="primary"
v-access:code="['hazard:device:add']"
@click="handleAdd"
>
{{ $t('pages.common.add') }}
</a-button>
</Space>
</template>
<template #sensorCode="{ row }">
<div v-if="row.sensorCode">
{{
(() => {
// sensorCode
const sensorIds = Array.isArray(row.sensorCode)
? row.sensorCode
: typeof row.sensorCode === 'string'
? row.sensorCode.split(',').map(id => id.trim()).filter(id => id)
: [];
.list-actions {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
}
// ID
return sensorIds
.map(id => {
const sensor = sensors.find(s => s.id == id);
return sensor ? sensor.name : id;
})
.join('\n'); // 使
})()
}}
</div>
<div v-else>-</div>
</template>
<template #action="{ row }">
<Space>
<ghost-button
v-access:code="['hazard:device:edit']"
@click.stop="handlePreview(row)"
>
{{ $t('pages.common.preview') }}
</ghost-button>
<ghost-button
v-access:code="['hazard:device:edit']"
@click.stop="handleEdit(row)"
>
{{ $t('pages.common.edit') }}
</ghost-button>
<Popconfirm
:get-popup-container="getVxePopupContainer"
placement="left"
title="确认删除?"
@confirm="handleDelete(row)"
>
<ghost-button
danger
v-access:code="['hazard:device:remove']"
@click.stop=""
>
{{ $t('pages.common.delete') }}
</ghost-button>
</Popconfirm>
</Space>
</template>
</BasicTable>
<DeviceModal @reload="tableApi.query()" />
</Page>
</template>
.list-title {
font-size: 18px;
font-weight: 500;
color: #333;
}
.action-buttons {
display: flex;
align-items: center;
}
</style>

Loading…
Cancel
Save