feat: 通行记录管理
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
import { requestClient } from '#/api/request';
|
||||
import type { PageResult } from '#/api/common';
|
||||
|
||||
/**
|
||||
* 通行记录列表
|
||||
* @param data
|
||||
* @returns void
|
||||
*/
|
||||
export function getVisitorList(params?: any) {
|
||||
return requestClient.get<PageResult<any>>('/sis/visitor/list', { params });
|
||||
}
|
@@ -0,0 +1,151 @@
|
||||
import type { FormSchemaGetter } from '#/adapter/form';
|
||||
import type { VxeGridProps } from '#/adapter/vxe-table';
|
||||
import { getDictOptions } from '#/utils/dict';
|
||||
import { renderDict } from '#/utils/render';
|
||||
|
||||
export const querySchema: FormSchemaGetter = () => [
|
||||
{
|
||||
component: 'Input',
|
||||
fieldName: 'actionTime',
|
||||
label: '通行时间',
|
||||
},
|
||||
{
|
||||
component: 'Input',
|
||||
fieldName: 'customerName',
|
||||
label: '人员姓名',
|
||||
},
|
||||
{
|
||||
component: 'Input',
|
||||
fieldName: 'organFullPath',
|
||||
label: '组织机构',
|
||||
},
|
||||
{
|
||||
component: 'Input',
|
||||
fieldName: 'doorName',
|
||||
label: '门/电梯名称',
|
||||
},
|
||||
{
|
||||
component: 'Input',
|
||||
fieldName: 'deviceName',
|
||||
label: '设备名称',
|
||||
},
|
||||
{
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
options: getDictOptions('wy_txjllx'),
|
||||
},
|
||||
fieldName: 'recordType',
|
||||
label: '记录类型',
|
||||
},
|
||||
];
|
||||
|
||||
export const columns: VxeGridProps['columns'] = [
|
||||
// { type: 'checkbox', width: 60 },
|
||||
{
|
||||
title: '序号',
|
||||
field: 'id',
|
||||
slots: {
|
||||
default: ({ rowIndex }) => {
|
||||
return (rowIndex + 1).toString();
|
||||
},
|
||||
},
|
||||
width: 60,
|
||||
},
|
||||
{
|
||||
title: '订单编号',
|
||||
field: 'orderId',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '车场名称',
|
||||
field: 'plName',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '停车类型',
|
||||
field: 'carBusiType',
|
||||
slots: {
|
||||
default: ({ row }) => {
|
||||
return renderDict(row.deviceType, 'wy_txjlsblb');
|
||||
},
|
||||
},
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '车牌号',
|
||||
field: 'carNumber',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '车辆类型',
|
||||
field: 'carType',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '应收',
|
||||
field: 'orderTotalFee',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '实收',
|
||||
field: 'orderActFee',
|
||||
slots: {
|
||||
default: ({ row }) => {
|
||||
return renderDict(row.cardType, 'wy_txjlmklb');
|
||||
},
|
||||
},
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '停车时长',
|
||||
field: 'parkingDuration',
|
||||
slots: {
|
||||
default: ({ row }) => {
|
||||
return renderDict(row.gatewayType, 'wy_txjlcrlx');
|
||||
},
|
||||
},
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '进场时间',
|
||||
field: 'parkInTime',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '出场时间',
|
||||
field: 'parkOutTime',
|
||||
slots: {
|
||||
default: ({ row }) => {
|
||||
return renderDict(row.recordType, 'wy_txjllx');
|
||||
},
|
||||
},
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '备注',
|
||||
field: 'remark',
|
||||
slots: {
|
||||
default: ({ row }) => {
|
||||
return renderDict(row.recordType, 'wy_txjllx');
|
||||
},
|
||||
},
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
field: 'parkState',
|
||||
slots: {
|
||||
default: ({ row }) => {
|
||||
return renderDict(row.recordType, 'wy_txjllx');
|
||||
},
|
||||
},
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
field: 'action',
|
||||
fixed: 'right',
|
||||
slots: { default: 'action' },
|
||||
title: '操作',
|
||||
minWidth: 180,
|
||||
},
|
||||
];
|
@@ -0,0 +1,127 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted,ref } from 'vue';
|
||||
import { Page, useVbenModal, type VbenFormProps } from '@vben/common-ui';
|
||||
import { getVxePopupContainer } from '@vben/utils';
|
||||
|
||||
import { Modal, Popconfirm, Space } from 'ant-design-vue';
|
||||
|
||||
import {
|
||||
useVbenVxeGrid,
|
||||
vxeCheckboxChecked,
|
||||
type VxeGridProps,
|
||||
} from '#/adapter/vxe-table';
|
||||
|
||||
import { commonDownloadExcel } from '#/utils/file/download';
|
||||
|
||||
import { columns, querySchema } from './data';
|
||||
import { getVisitorList } from '#/api/property/resident/passRecordManagement';
|
||||
const token = ref('')
|
||||
const formOptions: VbenFormProps = {
|
||||
commonConfig: {
|
||||
labelWidth: 120,
|
||||
componentProps: {
|
||||
allowClear: true,
|
||||
},
|
||||
},
|
||||
schema: querySchema(),
|
||||
wrapperClass: 'grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4',
|
||||
};
|
||||
|
||||
const gridOptions: VxeGridProps = {
|
||||
checkboxConfig: {
|
||||
// 高亮
|
||||
highlight: true,
|
||||
// 翻页时保留选中状态
|
||||
reserve: true,
|
||||
// 点击行选中
|
||||
// trigger: 'row',
|
||||
},
|
||||
columns,
|
||||
height: 'auto',
|
||||
keepSource: true,
|
||||
pagerConfig: {},
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
query: async ({ page }, formValues = {}) => {
|
||||
return await getVisitorList({
|
||||
pageNum: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
...formValues,
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
rowConfig: {
|
||||
keyField: 'id',
|
||||
},
|
||||
// 表格全局唯一表示 保存列配置需要用到
|
||||
id: 'property-unit-index',
|
||||
};
|
||||
|
||||
const [BasicTable, tableApi] = useVbenVxeGrid({
|
||||
formOptions,
|
||||
gridOptions,
|
||||
});
|
||||
|
||||
function handleDownloadExcel() {
|
||||
// commonDownloadExcel(
|
||||
// resident_unitExport,
|
||||
// '入驻单位数据',
|
||||
// tableApi.formApi.form.values,
|
||||
// {
|
||||
// fieldMappingTime: formOptions.fieldMappingTime,
|
||||
// },
|
||||
// );
|
||||
}
|
||||
|
||||
// 外部登录接口:页面加载时请求
|
||||
async function externalLoginOnLoad() {
|
||||
try {
|
||||
const response = await fetch('https://server.cqnctc.com:6081/web/oAuth/login', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
loginCode: 'zhfwzx',
|
||||
loginPassword: 'nc123456',
|
||||
}),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Request failed: ${response.status}`);
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
if (result?.data?.token) {
|
||||
token.value = result.data.token;
|
||||
}
|
||||
console.log('external login result:', result);
|
||||
} catch (error) {
|
||||
console.error('external login error:', error);
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
externalLoginOnLoad();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page :auto-content-height="true">
|
||||
<BasicTable table-title="通行记录列表">
|
||||
<template #toolbar-tools>
|
||||
<Space>
|
||||
<a-button @click="handleDownloadExcel">
|
||||
{{ $t('pages.common.export') }}
|
||||
</a-button>
|
||||
</Space>
|
||||
</template>
|
||||
<template #action="{ row }">
|
||||
<Space>
|
||||
<ghost-button> 查看 </ghost-button>
|
||||
</Space>
|
||||
</template>
|
||||
</BasicTable>
|
||||
</Page>
|
||||
</template>
|
@@ -0,0 +1,131 @@
|
||||
import type { FormSchemaGetter } from '#/adapter/form';
|
||||
import type { VxeGridProps } from '#/adapter/vxe-table';
|
||||
import { getDictOptions } from '#/utils/dict';
|
||||
import { renderDict } from '#/utils/render';
|
||||
|
||||
export const querySchema: FormSchemaGetter = () => [
|
||||
{
|
||||
component: 'Input',
|
||||
fieldName: 'actionTime',
|
||||
label: '通行时间',
|
||||
},
|
||||
{
|
||||
component: 'Input',
|
||||
fieldName: 'customerName',
|
||||
label: '人员姓名',
|
||||
},
|
||||
{
|
||||
component: 'Input',
|
||||
fieldName: 'organFullPath',
|
||||
label: '组织机构',
|
||||
},
|
||||
{
|
||||
component: 'Input',
|
||||
fieldName: 'doorName',
|
||||
label: '门/电梯名称',
|
||||
},
|
||||
{
|
||||
component: 'Input',
|
||||
fieldName: 'deviceName',
|
||||
label: '设备名称',
|
||||
},
|
||||
{
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
options: getDictOptions('wy_txjllx'),
|
||||
},
|
||||
fieldName: 'recordType',
|
||||
label: '记录类型',
|
||||
},
|
||||
];
|
||||
|
||||
export const columns: VxeGridProps['columns'] = [
|
||||
// { type: 'checkbox', width: 60 },
|
||||
{
|
||||
title: '序号',
|
||||
field: 'id',
|
||||
slots: {
|
||||
default: ({ rowIndex }) => {
|
||||
return (rowIndex + 1).toString();
|
||||
},
|
||||
},
|
||||
width: 60,
|
||||
},
|
||||
{
|
||||
title: '门/电梯名称',
|
||||
field: 'doorName',
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
title: '设备名称',
|
||||
field: 'deviceName',
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
title: '设备类别',
|
||||
field: 'deviceType',
|
||||
slots: {
|
||||
default: ({ row }) => {
|
||||
return renderDict(row.deviceType, 'wy_txjlsblb');
|
||||
},
|
||||
},
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
title: '读头名称',
|
||||
field: 'readerName',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '人员姓名',
|
||||
field: 'customerName',
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
title: '所属组织',
|
||||
field: 'organFullPath',
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
title: '门卡类别',
|
||||
field: 'cardType',
|
||||
slots: {
|
||||
default: ({ row }) => {
|
||||
return renderDict(row.cardType, 'wy_txjlmklb');
|
||||
},
|
||||
},
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '出入类型',
|
||||
field: 'gatewayType',
|
||||
slots: {
|
||||
default: ({ row }) => {
|
||||
return renderDict(row.gatewayType, 'wy_txjlcrlx');
|
||||
},
|
||||
},
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '通行时间',
|
||||
field: 'actionTime',
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
title: '记录类型',
|
||||
field: 'recordType',
|
||||
slots: {
|
||||
default: ({ row }) => {
|
||||
return renderDict(row.recordType, 'wy_txjllx');
|
||||
},
|
||||
},
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
field: '',
|
||||
fixed: 'right',
|
||||
slots: { default: 'action' },
|
||||
title: '抓拍图片',
|
||||
minWidth: 180,
|
||||
},
|
||||
];
|
@@ -0,0 +1,102 @@
|
||||
<script setup lang="ts">
|
||||
import { Page, useVbenModal, type VbenFormProps } from '@vben/common-ui';
|
||||
import { getVxePopupContainer } from '@vben/utils';
|
||||
|
||||
import { Modal, Popconfirm, Space } from 'ant-design-vue';
|
||||
|
||||
import {
|
||||
useVbenVxeGrid,
|
||||
vxeCheckboxChecked,
|
||||
type VxeGridProps,
|
||||
} from '#/adapter/vxe-table';
|
||||
|
||||
import { commonDownloadExcel } from '#/utils/file/download';
|
||||
|
||||
import { columns, querySchema } from './data';
|
||||
import { getVisitorList } from '#/api/property/resident/passRecordManagement';
|
||||
import recordDetailModal from './record-detail-modal.vue';
|
||||
const formOptions: VbenFormProps = {
|
||||
commonConfig: {
|
||||
labelWidth: 120,
|
||||
componentProps: {
|
||||
allowClear: true,
|
||||
},
|
||||
},
|
||||
schema: querySchema(),
|
||||
wrapperClass: 'grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4',
|
||||
};
|
||||
|
||||
const gridOptions: VxeGridProps = {
|
||||
checkboxConfig: {
|
||||
// 高亮
|
||||
highlight: true,
|
||||
// 翻页时保留选中状态
|
||||
reserve: true,
|
||||
// 点击行选中
|
||||
// trigger: 'row',
|
||||
},
|
||||
columns,
|
||||
height: 'auto',
|
||||
keepSource: true,
|
||||
pagerConfig: {},
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
query: async ({ page }, formValues = {}) => {
|
||||
return await getVisitorList({
|
||||
pageNum: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
...formValues,
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
rowConfig: {
|
||||
keyField: 'id',
|
||||
},
|
||||
// 表格全局唯一表示 保存列配置需要用到
|
||||
id: 'property-unit-index',
|
||||
};
|
||||
|
||||
const [BasicTable, tableApi] = useVbenVxeGrid({
|
||||
formOptions,
|
||||
gridOptions,
|
||||
});
|
||||
|
||||
function handleDownloadExcel() {
|
||||
// commonDownloadExcel(
|
||||
// resident_unitExport,
|
||||
// '入驻单位数据',
|
||||
// tableApi.formApi.form.values,
|
||||
// {
|
||||
// fieldMappingTime: formOptions.fieldMappingTime,
|
||||
// },
|
||||
// );
|
||||
}
|
||||
const [RecordDetailModal, recordDetailModalApi] = useVbenModal({
|
||||
connectedComponent: recordDetailModal,
|
||||
});
|
||||
async function handleInfo(row: any) {
|
||||
recordDetailModalApi.setData({ data: row });
|
||||
recordDetailModalApi.open();
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page :auto-content-height="true">
|
||||
<BasicTable table-title="通行记录列表">
|
||||
<template #toolbar-tools>
|
||||
<Space>
|
||||
<a-button @click="handleDownloadExcel">
|
||||
{{ $t('pages.common.export') }}
|
||||
</a-button>
|
||||
</Space>
|
||||
</template>
|
||||
<template #action="{ row }">
|
||||
<Space>
|
||||
<ghost-button @click.stop="handleInfo(row)"> 查看 </ghost-button>
|
||||
</Space>
|
||||
</template>
|
||||
</BasicTable>
|
||||
<RecordDetailModal/>
|
||||
</Page>
|
||||
</template>
|
@@ -0,0 +1,157 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
import { $t } from '@vben/locales';
|
||||
import { cloneDeep, handleNode } from '@vben/utils';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
import {
|
||||
resident_unitAdd,
|
||||
resident_unitInfo,
|
||||
resident_unitUpdate,
|
||||
} from '#/api/property/resident/unit';
|
||||
import { defaultFormValueGetter, useBeforeCloseDiff } from '#/utils/popup';
|
||||
|
||||
import { communityTree } from '#/api/property/community';
|
||||
|
||||
const emit = defineEmits<{ reload: [] }>();
|
||||
|
||||
const isUpdate = ref(false);
|
||||
const title = computed(() => {
|
||||
return isUpdate.value ? $t('pages.common.edit') : $t('pages.common.add');
|
||||
});
|
||||
const picture1 = ref('')
|
||||
// const { onBeforeClose, markInitialized, resetInitialized } = useBeforeCloseDiff(
|
||||
// {
|
||||
// initializedGetter: defaultFormValueGetter(formApi),
|
||||
// currentGetter: defaultFormValueGetter(formApi),
|
||||
// },
|
||||
// );
|
||||
|
||||
const [BasicModal, modalApi] = useVbenModal({
|
||||
// 在这里更改宽度
|
||||
class: 'w-[70%]',
|
||||
fullscreenButton: false,
|
||||
onClosed: handleClosed,
|
||||
onConfirm: handleConfirm,
|
||||
onOpenChange: async (isOpen) => {
|
||||
if (!isOpen) {
|
||||
return null;
|
||||
}
|
||||
const {data} = modalApi.getData() as { data:any };
|
||||
console.log(data)
|
||||
picture1.value = data.pictureUrl;
|
||||
// detail.value = await meetInfo(id);
|
||||
// if (detail.value?.picture) {
|
||||
// const res = await ossInfo([conferenceSettingsDetail.value?.picture]);
|
||||
// if (res) {
|
||||
// let imgArr = [] as string[];
|
||||
// res.forEach(item => {
|
||||
// imgArr.push(item.url)
|
||||
// })
|
||||
// conferenceSettingsDetail.value.pictureArr = imgArr;
|
||||
// }
|
||||
// }
|
||||
modalApi.modalLoading(true);
|
||||
|
||||
modalApi.modalLoading(false);
|
||||
},
|
||||
});
|
||||
|
||||
async function handleConfirm() {
|
||||
try {
|
||||
modalApi.lock(true);
|
||||
emit('reload');
|
||||
modalApi.close();
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
} finally {
|
||||
modalApi.lock(false);
|
||||
}
|
||||
}
|
||||
async function handleClosed() {}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<BasicModal title="抓拍图片">
|
||||
<div class="detail-wrapper">
|
||||
<div class="detail-grid">
|
||||
<div class="detail-card">
|
||||
<img class="detail-thumb" :src="picture1"></img>
|
||||
<div class="detail-caption">人脸凭证照片</div>
|
||||
</div>
|
||||
<div class="detail-card">
|
||||
<img class="detail-thumb" :src="picture1"></img>
|
||||
<div class="detail-caption">监控设备抓拍</div>
|
||||
</div>
|
||||
<div class="detail-card">
|
||||
<img class="detail-thumb" :src="picture1"></img>
|
||||
<div class="detail-caption">人脸门禁抓拍</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</BasicModal>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.detail-wrapper {
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.detail-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 24px;
|
||||
}
|
||||
|
||||
.detail-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06), 0 1px 2px rgba(0, 0, 0, 0.08);
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.detail-thumb {
|
||||
width: 220px;
|
||||
height: 260px;
|
||||
background-color: #f5f7fa;
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 1px solid #e5e7eb;
|
||||
}
|
||||
|
||||
.detail-thumb img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.detail-caption {
|
||||
margin-top: 12px;
|
||||
font-size: 14px;
|
||||
color: #606266;
|
||||
}
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
.detail-grid {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.detail-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
.detail-thumb {
|
||||
width: 100%;
|
||||
height: 240px;
|
||||
}
|
||||
}
|
||||
</style>
|
Reference in New Issue
Block a user