feat:第二版

This commit is contained in:
dzc committed 2025-08-19 16:35:59 +08:00
1 parent 53bdab7f0c
commit bf4f9348df
21 files changed
+775 -221

No files matched your search

+4 -4
View File
@@ -1,8 +1,8 @@
# 网站主标题
VITE_GLOBAL_TITLE= 'PIGX ADMIN'
VITE_GLOBAL_TITLE= '智慧安防平台'
# footer
VITE_FOOTER_TITLE= '©2025 pig4cloud.com'
VITE_FOOTER_TITLE= ''
# 是否是微服务架构(重要)
VITE_IS_MICRO= true
@@ -38,13 +38,13 @@ VITE_VERIFY_IMAGE_ENABLE = false
VITE_WEBSOCKET_ENABLE = false
# 是否开启注册
VITE_REGISTER_ENABLE = true
VITE_REGISTER_ENABLE = false
# 是否开启租户自动选择 (根据租户域名)
VITE_AUTO_TENANT = false
# 是否开启多语言切换
VITE_I18N_ENABLE = true
VITE_I18N_ENABLE = false
# 是否开启暗黑模式切换
VITE_DARK_MODE_ENABLE = true
+2 -2
View File
@@ -8,10 +8,10 @@ VITE_OPEN = true
ENV = 'prod'
# ADMIN 服务地址
# VITE_ADMIN_PROXY_PATH = http://127.0.0.1:36588
VITE_ADMIN_PROXY_PATH = http://127.0.0.1:36588
# 本地
VITE_ADMIN_PROXY_PATH = http://172.20.10.2:36588
# VITE_ADMIN_PROXY_PATH = http://172.20.10.2:36588
# 图片访问基础地址
VITE_IMAGE_BASE_PATH = http://123.157.250.162:36588/api
Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

+22
View File
@@ -1,5 +1,27 @@
import request from "/@/utils/request"
/**
* 加入考勤的人员在位数量分页
*/
export function reignCount(data: any) {
return request({
url: '/admin/door/total/reignCount',
method: 'post',
data,
})
}
/**
* 迟到、早退数量统计
*/
export function beLateCount(data: any) {
return request({
url: '/admin/door/total/beLateCount',
method: 'post',
data,
})
}
export function fetchList(query?: Object) {
return request({
url: '/admin/door/doorRecord/page',
+6 -6
View File
@@ -1,29 +1,29 @@
<template>
<div class="layout-logo" v-if="setShowLogo" @click="onThemeConfigChange">
<span :style="{color:setFontColor}">{{ themeConfig.globalTitle }}</span>
<span :style="{ color: setFontColor }">{{ themeConfig.globalTitle }}</span>
</div>
<div class="layout-logo-size" v-else @click="onThemeConfigChange">
<img :src="logoMini" class="layout-logo-size-img"/>
<!-- <img :src="logoMini" class="layout-logo-size-img" /> -->
</div>
</template>
<script setup lang="ts" name="layoutLogo">
import {useThemeConfig} from '/@/stores/themeConfig';
import { useThemeConfig } from '/@/stores/themeConfig';
import logoMini from '/@/assets/logo-mini.svg';
// 定义变量内容
const storesThemeConfig = useThemeConfig();
const {themeConfig} = storeToRefs(storesThemeConfig);
const { themeConfig } = storeToRefs(storesThemeConfig);
// 设置 logo 的显示。classic 经典布局默认显示 logo
const setShowLogo = computed(() => {
let {isCollapse, layout} = themeConfig.value;
let { isCollapse, layout } = themeConfig.value;
return !isCollapse || layout === 'classic' || document.body.clientWidth < 1000;
});
// 设置 title 的显示颜色。根据布局模式自动显示
const setFontColor = computed(() => {
let {layout} = themeConfig.value;
let { layout } = themeConfig.value;
return layout === 'classic' || layout === 'transverse' ? `var(--next-bg-topBarColor)` : 'var(--el-color-primary)';
});
+144
View File
@@ -0,0 +1,144 @@
<template>
<el-form ref="dataFormRef" :model="form" :rules="dataRules" formDialogRef label-width="180px"
v-loading="state.loading">
<el-divider class="mb-12">人员高频次出入</el-divider>
<el-form-item label="车闸报警频次" prop="carRepeatCount">
<template #label>
<div class="flex items-center gap-1">
<span>车闸报警频次</span>
<el-tooltip class="box-item" :content="`每天进出${form.carRepeatCount}次就会报警`" placement="top">
<el-icon :size="16">
<InfoFilled />
</el-icon>
</el-tooltip>
</div>
</template>
<el-input v-model="form.carRepeatCount" placeholder="请输入道闸报警频次">
<template #append>次</template>
</el-input>
</el-form-item>
<el-divider class="my-12">车辆工作时间</el-divider>
<el-form-item prop="carPersonWorkForenoonTime">
<template #label>
<div class="flex items-center gap-1">
<span>车辆工作时间(上午)</span>
</div>
</template>
<el-time-picker format="HH:mm" value-format="HH:mm" v-model="form.carPersonWorkForenoonTime" is-range
placeholder="请选择车辆工作时间(上午)" />
</el-form-item>
<el-form-item prop="carPersonWorkAfternoonTime">
<template #label>
<div class="flex items-center gap-1">
<span>车辆工作时间(下午)</span>
</div>
</template>
<el-time-picker format="HH:mm" value-format="HH:mm" v-model="form.carPersonWorkAfternoonTime" is-range
placeholder="请选择车辆工作时间(下午)" />
</el-form-item>
<el-divider class="my-12">夜晚报警</el-divider>
<el-form-item prop="carNightAlarmTime">
<template #label>
<div class="flex items-center gap-1">
<span>车辆夜晚报警时间</span>
<el-tooltip class="box-item" :content="`每天${form.carNightAlarmTime}以后出入人闸报警`" placement="top">
<el-icon :size="16">
<InfoFilled />
</el-icon>
</el-tooltip>
</div>
</template>
<el-time-picker format="HH:mm" value-format="HH:mm" v-model="form.carNightAlarmTime" is-range
placeholder="请选择车辆夜晚报警时间" />
</el-form-item>
</el-form>
</template>
<script setup lang="ts" name="carSetting">
import { useMessage } from "/@/hooks/message"
import * as ComDhSettingApi from "/@/api/com/comDhSetting";
const dataFormRef = ref();
const state = reactive<any>({
loading: false,
})
// 提交表单数据
const form = reactive<any>({
// 道闸报警频次
carRepeatCount: 0,
// 工作时间上午
carPersonWorkForenoonTime: null,
// 工作时间下午
carPersonWorkAfternoonTime: null,
// 夜晚报警时间
carNightAlarmTime: [],
})
const dataRules = ref({
carRepeatCount: [{ required: true, message: '车辆报警频次不能为空', trigger: 'blur' }],
carPersonWorkForenoonTime: [{ required: true, message: '车辆工作时间(上午)不能为空', trigger: 'change' }],
carPersonWorkAfternoonTime: [{ required: true, message: '车辆工作时间(下午)不能为空', trigger: 'change' }],
carNightAlarmTime: [{ required: true, message: '车辆报警时间不能为空', trigger: 'change' }],
})
onMounted(() => {
getSetting()
})
async function getSetting() {
state.loading = true
try {
const { data } = await ComDhSettingApi.getObj()
if (data) {
if (data.carPersonWorkForenoonTime) {
data.carPersonWorkForenoonTime = data.carPersonWorkForenoonTime.split(',')
}
if (data.carPersonWorkAfternoonTime) {
data.carPersonWorkAfternoonTime = data.carPersonWorkAfternoonTime.split(',')
}
if (data.carNightAlarmTime) {
data.carNightAlarmTime = data.carNightAlarmTime.split(',')
}
Object.assign(form, data)
}
} catch (err: any) {
console.error(err);
} finally {
state.loading = false
}
}
async function onSubmit() {
const valid = await dataFormRef.value.validate().catch(() => { });
if (!valid) return false;
state.loading = true
try {
const _form = {
...form,
}
if (_form.carPersonWorkForenoonTime && Array.isArray(_form.carPersonWorkForenoonTime)) {
_form.carPersonWorkForenoonTime = _form.carPersonWorkForenoonTime.join(',')
}
if (_form.carPersonWorkAfternoonTime && Array.isArray(_form.carPersonWorkAfternoonTime)) {
_form.carPersonWorkAfternoonTime = _form.carPersonWorkAfternoonTime.join(',')
}
if (_form.carNightAlarmTime && Array.isArray(_form.carNightAlarmTime)) {
_form.carNightAlarmTime = _form.carNightAlarmTime.join(',')
}
await ComDhSettingApi.putObj(_form)
useMessage().success('修改成功');
} catch (err: any) {
console.error(err);
useMessage().error(err.msg);
} finally {
state.loading = false
}
}
defineExpose({
getSetting,
onSubmit
});
</script>
@@ -0,0 +1,173 @@
<template>
<el-form ref="dataFormRef" :model="form" :rules="dataRules" formDialogRef label-width="180px"
v-loading="state.loading">
<el-divider class="mb-12">人员高频次出入</el-divider>
<el-form-item prop="doorRepeatCount">
<template #label>
<div class="flex items-center gap-1">
<span>道闸报警频次</span>
<el-tooltip class="box-item" :content="`每天进出${form.doorRepeatCount}次就会报警`" placement="top">
<el-icon :size="16">
<InfoFilled />
</el-icon>
</el-tooltip>
</div>
</template>
<el-input v-model="form.doorRepeatCount" placeholder="请输入道闸报警频次">
<template #append>次</template>
</el-input>
</el-form-item>
<el-divider class="my-12">人员工作时间</el-divider>
<el-form-item prop="doorPersonWorkForenoonTime">
<template #label>
<div class="flex items-center gap-1">
<span>人员工作时间(上午)</span>
</div>
</template>
<el-time-picker format="HH:mm" value-format="HH:mm" v-model="form.doorPersonWorkForenoonTime" is-range
placeholder="请选择人员工作时间(上午)" />
</el-form-item>
<el-form-item prop="doorPersonWorkAfternoonTime">
<template #label>
<div class="flex items-center gap-1">
<span>人员工作时间(下午)</span>
</div>
</template>
<el-time-picker format="HH:mm" value-format="HH:mm" v-model="form.doorPersonWorkAfternoonTime" is-range
placeholder="请选择人员工作时间(下午)" />
</el-form-item>
<el-divider class="my-12">夜晚报警</el-divider>
<el-form-item prop="doorNightAlarmTime">
<template #label>
<div class="flex items-center gap-1">
<span>人员夜晚报警时间</span>
</div>
</template>
<el-time-picker format="HH:mm" value-format="HH:mm" v-model="form.doorNightAlarmTime" is-range
placeholder="请选择夜晚报警时间" />
</el-form-item>
<el-divider class="my-12">主楼配置</el-divider>
<el-form-item prop="doorMainFloorChannels">
<template #label>
<div class="flex items-center gap-1">
<span>主楼入口摄像头</span>
</div>
</template>
<select-tree-channel v-model="form.doorMainFloorChannels" placeholder="请选择主楼入口摄像头" />
</el-form-item>
<el-divider class="my-12">考勤配置</el-divider>
<el-form-item prop="doorCheckingChannels">
<template #label>
<div class="flex items-center gap-1">
<span>考勤摄像头</span>
</div>
</template>
<select-tree-channel v-model="form.doorCheckingChannels" placeholder="请选择考勤摄像头" />
</el-form-item>
</el-form>
</template>
<script setup lang="ts" name="doorSetting">
import { useMessage } from "/@/hooks/message"
import * as ComDhSettingApi from "/@/api/com/comDhSetting";
const dataFormRef = ref();
const state = reactive<any>({
loading: false,
})
// 提交表单数据
const form = reactive<any>({
// 道闸报警频次
doorRepeatCount: 0,
// 工作时间上午
doorPersonWorkForenoonTime: null,
// 工作时间下午
doorPersonWorkAfternoonTime: null,
// 夜晚报警时间
doorNightAlarmTime: [],
// 通道编号
doorMainFloorChannels: [],
// 考勤摄像头通道
doorCheckingChannels: [],
})
const dataRules = ref({
doorRepeatCount: [{ required: true, message: '道闸报警频次不能为空', trigger: 'blur' }],
doorPersonWorkForenoonTime: [{ required: true, message: '人员工作时间(上午)不能为空', trigger: 'change' }],
doorPersonWorkAfternoonTime: [{ required: true, message: '人员工作时间(下午)不能为空', trigger: 'change' }],
doorNightAlarmTime: [{ required: true, message: '夜晚报警时间不能为空', trigger: 'change' }],
})
onMounted(() => {
getSetting()
})
async function getSetting() {
state.loading = true
try {
const { data } = await ComDhSettingApi.getObj()
if (data) {
if (data.doorPersonWorkForenoonTime) {
data.doorPersonWorkForenoonTime = data.doorPersonWorkForenoonTime.split(',')
}
if (data.doorPersonWorkAfternoonTime) {
data.doorPersonWorkAfternoonTime = data.doorPersonWorkAfternoonTime.split(',')
}
if (data.doorMainFloorChannels) {
data.doorMainFloorChannels = data.doorMainFloorChannels.split(',')
}
if (data.doorCheckingChannels) {
data.doorCheckingChannels = data.doorCheckingChannels.split(',')
}
if (data.doorNightAlarmTime) {
data.doorNightAlarmTime = data.doorNightAlarmTime.split(',')
}
Object.assign(form, data)
}
} catch (err: any) {
console.error(err);
} finally {
state.loading = false
}
}
async function onSubmit() {
const valid = await dataFormRef.value.validate().catch(() => { });
if (!valid) return false;
state.loading = true
try {
const _form = {
...form,
}
if (_form.doorPersonWorkForenoonTime && Array.isArray(_form.doorPersonWorkForenoonTime)) {
_form.doorPersonWorkForenoonTime = _form.doorPersonWorkForenoonTime.join(',')
}
if (_form.doorPersonWorkAfternoonTime && Array.isArray(_form.doorPersonWorkAfternoonTime)) {
_form.doorPersonWorkAfternoonTime = _form.doorPersonWorkAfternoonTime.join(',')
}
if (_form.doorMainFloorChannels && Array.isArray(_form.doorMainFloorChannels)) {
_form.doorMainFloorChannels = _form.doorMainFloorChannels.join(',')
}
if (_form.doorCheckingChannels && Array.isArray(_form.doorCheckingChannels)) {
_form.doorCheckingChannels = _form.doorCheckingChannels.join(',')
}
if (_form.doorNightAlarmTime && Array.isArray(_form.doorNightAlarmTime)) {
_form.doorNightAlarmTime = _form.doorNightAlarmTime.join(',')
}
await ComDhSettingApi.putObj(_form)
useMessage().success('修改成功');
} catch (err: any) {
console.error(err);
useMessage().error(err.msg);
} finally {
state.loading = false
}
}
defineExpose({
getSetting,
onSubmit
});
</script>
+22 -113
View File
@@ -4,61 +4,14 @@
<el-tabs v-model="state.currentTab" class="demo-tabs" @tab-click="tabClick">
<el-tab-pane v-for="(v, i) in data.tabs" :key="v.name" :label="v.label" :name="v.name" />
</el-tabs>
<div class="flex-1 overflow-hidden mt-4">
<el-form ref="dataFormRef" :model="form" :rules="dataRules" formDialogRef label-width="150px"
v-loading="state.loading">
<el-row :gutter="24">
<div class="w-full" v-if="state.currentTab === 'PERSON'">
<el-col :span="24" class="mb20">
<el-form-item label="人闸报警频次" prop="doorRepeatCount">
<template #label>
<div class="flex items-center gap-1">
<span>人闸报警频次</span>
<el-tooltip class="box-item" :content="`每天进出${form.doorRepeatCount}次就会报警`"
placement="top">
<el-icon :size="16">
<InfoFilled />
</el-icon>
</el-tooltip>
</div>
</template>
<el-input v-model="form.doorRepeatCount" placeholder="请输入人闸报警频次">
<div class="flex items-center gap-1">
<span>请输入人闸报警频次</span>
<el-tooltip class="box-item" :content="`每天进出${form.doorRepeatCount}次就会报警`"
placement="top">
<el-icon :size="16">
<InfoFilled />
</el-icon>
</el-tooltip>
</div>
<template #append>次</template>
</el-input>
</el-form-item>
</el-col>
</div>
<div class="w-full" v-if="state.currentTab === 'DOOR'">
<el-col :span="24" class="mb20">
<el-form-item label="道闸报警频次" prop="carRepeatCount">
<template #label>
<div class="flex items-center gap-1">
<span>道闸报警频次</span>
<el-tooltip class="box-item" :content="`每天进出${form.carRepeatCount}次就会报警`"
placement="top">
<el-icon :size="16">
<InfoFilled />
</el-icon>
</el-tooltip>
</div>
</template>
<el-input v-model="form.carRepeatCount" placeholder="请输入道闸报警频次">
<template #append>次</template>
</el-input>
</el-form-item>
</el-col>
</div>
</el-row>
</el-form>
<div class="flex-1 overflow-y-scroll px-8">
<div class="w-full" v-if="state.currentTab === 'PERSON'">
<DoorSetting ref="doorSetting" />
</div>
<div class="w-full" v-if="state.currentTab === 'DOOR'">
<CarSetting ref="carSetting" />
</div>
<div class="h-20" />
</div>
<div class="flex justify-center">
<el-button type="primary" @click="onSubmit">保存配置</el-button>
@@ -68,16 +21,11 @@
</template>
<script setup lang="ts" name="deviceLog">
import { useMessage } from "/@/hooks/message";
import * as ComDhSettingApi from "/@/api/com/comDhSetting";
const CarSetting = defineAsyncComponent(() => import('./car/car-setting.vue'));
const DoorSetting = defineAsyncComponent(() => import('./door/door-setting.vue'));
const data = {
tabs: [
{
label: "车速预警配置",
name: "CAR_SPEED",
},
{
label: "人员预警配置",
name: "PERSON",
@@ -89,64 +37,25 @@ const data = {
]
}
const doorSetting = ref()
const carSetting = ref()
const state = reactive<any>({
currentTab: data.tabs[0].name,
loading: false,
})
const dataFormRef = ref();
// 提交表单数据
const form = reactive({
// 人闸报警频次
doorRepeatCount: null,
// 道闸报警频次
carRepeatCount: null,
})
// 定义校验规则
const dataRules = ref({
doorRepeatCount: [{ required: true, message: '人闸报警频次不能为空', trigger: 'blur' }],
carRepeatCount: [{ required: true, message: '道闸报警频次不能为空', trigger: 'blur' }],
})
onMounted(() => {
getSetting()
})
function tabClick(e: any) {
getSetting()
console.log("tabClick", e);
}
async function getSetting() {
state.loading = true
try {
const { data } = await ComDhSettingApi.getObj()
if (data) {
Object.assign(form, data)
}
} catch (err: any) {
console.error(err);
} finally {
state.loading = false
function onSubmit() {
if (state.currentTab == "PERSON") {
doorSetting.value.onSubmit()
return
}
if (state.currentTab == "DOOR") {
carSetting.value.onSubmit()
return
}
}
async function onSubmit() {
const valid = await dataFormRef.value.validate().catch(() => { });
if (!valid) return false;
state.loading = true
try {
const { data } = await ComDhSettingApi.putObj(form)
if (data) {
Object.assign(form, data)
}
useMessage().success('修改成功');
} catch (err: any) {
useMessage().error(err.msg);
} finally {
state.loading = false
}
}
</script>
+17 -4
View File
@@ -35,7 +35,7 @@
<el-table-column prop="carOwnerId" min-width="100" label="车主id" />
<el-table-column prop="ownerType" min-width="100" label="车辆类型" />
</el-table-column>
<el-table-column min-width="200" label="报警类型">
<el-table-column min-width="210" label="报警类型">
<template #default="{ row }">
<div class="flex flex-wrap gap-1">
<el-tag v-for="(v, i) in row.alarmList" key="v" type="danger">
@@ -84,7 +84,7 @@
</el-table-column>
<el-table-column label="人员信息">
<el-table-column prop="ownerId" width="120" label="人员ID" />
<el-table-column prop="ownerName" width="120" label="人员" />
<el-table-column prop="ownerCardId" width="120" label="证件号码" />
</el-table-column>
@@ -112,8 +112,8 @@
<el-table-column label="操作" width="100" fixed="right">
<template #default="{ row }">
<el-button v-if="row.alarmList && row.alarmList.includes('2')" icon="VideoCamera" text
type="primary" @click="replayVideoDialogRef.openDialog('channel', row.devChnId)">
<el-button v-if="row.devChnId" icon="VideoCamera" text type="primary"
@click="openReplayDialog(row)">
回放
</el-button>
</template>
@@ -195,6 +195,19 @@ function getDictByValue(value: any, tag: any) {
}
}
function openReplayDialog(row: any) {
const dateTime = row.capTime
if (!dateTime) return
// 获取dateTime前后30秒的时间
const baseTime = new Date(dateTime)
const startDateTime = new Date(baseTime.getTime() - 30 * 1000)
const endDateTime = new Date(baseTime.getTime() + 30 * 1000)
const startTime = parseTime(startDateTime, '{y}-{m}-{d} {h}:{i}:{s}')
const endTime = parseTime(endDateTime, '{y}-{m}-{d} {h}:{i}:{s}')
replayVideoDialogRef.value.openDialog('channel', row.devChnId, { startTime, endTime })
}
</script>
<style lang="scss" scoped></style>
+1 -1
View File
@@ -68,7 +68,7 @@
</el-table-column>
<el-table-column label="人员信息">
<el-table-column prop="ownerId" width="120" label="人员ID" />
<el-table-column prop="ownerName" width="120" label="人员" />
<el-table-column prop="ownerCardId" width="120" label="证件号码" />
</el-table-column>
@@ -51,7 +51,7 @@
</el-table-column>
<el-table-column label="人员信息">
<el-table-column prop="ownerId" width="120" label="人员ID" />
<el-table-column prop="ownerName" width="120" label="人员" />
<el-table-column prop="ownerCardId" width="120" label="证件号码" />
</el-table-column>
@@ -1,6 +1,6 @@
<!-- 人员出入次数统计详情 -->
<template>
<el-dialog title="车辆进去详情" v-model="visible" width="70%" :close-on-click-modal="false" draggable>
<el-dialog title="人员进出详情" v-model="visible" width="80%" :close-on-click-modal="false" draggable>
<el-row shadow="hover" class="ml10">
<el-form-item style="width: 250px;" label="进出门类型" prop="enterOrExit">
<dict-select v-model="state.queryForm.enterOrExit" :options="dh_enter_or_exit" placeholder="请选择进出门类型" />
@@ -147,8 +147,8 @@ const resetQuery = () => {
}
// 打开弹窗
function openDialog(row: any) {
state.queryForm.personCode = row.personCode
function openDialog(query: any) {
state.queryForm = query
visible.value = true
getDataList();
}
+5 -7
View File
@@ -10,8 +10,8 @@
<dict-select v-model="state.queryForm.enterOrExit" :options="dh_enter_or_exit"
placeholder="请选择进出门类型" />
</el-form-item>
<el-form-item style="width: 250px;" label="开门类型" prop="openType">
<dict-select v-model="state.queryForm.openType" :options="dh_door_open_type"
<el-form-item style="width: 250px;" label="报警类型" prop="door_alarm_type">
<dict-select v-model="state.queryForm.alarmType" :options="door_alarm_type"
placeholder="请选择进开门类型" />
</el-form-item>
<el-form-item style="width: 350px;" label="抓拍时间" prop="swingTimeList">
@@ -30,7 +30,6 @@
@sort-change="sortChangeHandle">
<el-table-column type="selection" width="40" align="center" />
<el-table-column type="index" label="#" width="80" />
<el-table-column min-width="200" label="报警类型">
<template #default="{ row }">
<div class="flex flex-wrap gap-1">
@@ -48,7 +47,7 @@
</div>
</template>
</el-table-column>
<el-table-column prop="swingTime" label="触发时间" min-width="180" />
<el-table-column min-width="100" label="人员">
<el-table-column prop="personCode" min-width="100" label="人员编号">
<template #default="{ row }">
@@ -112,11 +111,10 @@
</template>
</el-table-column>
<el-table-column prop="remark" label="开门失败原因" min-width="200" show-overflow-tooltip />
<el-table-column prop="swingTime" label="触发时间" min-width="180" />
<el-table-column label="操作" min-width="100" fixed="right">
<template #default="{ row }">
<el-button v-if="row.alarmList && (row.alarmList.includes('1') || row.alarmList.includes('2'))"
icon="VideoCamera" text type="primary" @click="openReplayDialog(row)">
<el-button v-if="row.channelCode" icon="VideoCamera" text type="primary"
@click="openReplayDialog(row)">
回放
</el-button>
</template>
+168
View File
@@ -0,0 +1,168 @@
<template>
<div class="layout-padding">
<div class="layout-padding-auto layout-padding-view">
<el-row shadow="hover" v-show="showSearch" class="ml10">
<el-form :model="state.queryForm" ref="queryRef" :inline="true" @keyup.enter="getDataList">
<el-form-item style="width: 250px;" label="统计时间" prop="type">
<dict-select v-model="state.queryForm.type" :options="tool_time_type" placeholder="请选择统计时间" />
</el-form-item>
<el-form-item>
<el-button icon="search" type="primary" @click="getDataList">
{{ $t('common.queryBtn') }}
</el-button>
<el-button icon="Refresh" @click="resetQuery">{{ $t('common.resetBtn') }}</el-button>
</el-form-item>
</el-form>
</el-row>
<el-row>
<div class="mb8" style="width: 100%">
<right-toolbar v-model:showSearch="showSearch" :export="false" @exportExcel="exportExcel"
class="ml10 mr20" style="float: right;" @queryTable="getDataList"></right-toolbar>
</div>
</el-row>
<el-table :data="state.dataList" v-loading="state.loading" border :cell-style="tableStyle.cellStyle"
:header-cell-style="tableStyle.headerCellStyle" @selection-change="selectionChangHandle"
@sort-change="sortChangeHandle" show-summary :summary-method="getSummaries">
<el-table-column type="selection" width="80" align="center" />
<el-table-column type="index" label="#" width="80" />
<el-table-column min-width="100" label="日期">
<template #default="{ row }">
<span>{{ row.time }}</span>
<span> - </span>
<span class="color-primary">{{ row.weekStr }}</span>
</template>
</el-table-column>
<el-table-column min-width="100" prop="morningPreCount" align="center" label="上午迟到、早退次数">
<template #default="{ row }">
<div class="color-success">
<span> {{ row.morningPreCount }}</span>
<span>次</span>
</div>
</template>
</el-table-column>
<el-table-column min-width="100" prop="afternoonPreCount" align="center" label="下午迟到、早退次数">
<template #default="{ row }">
<div class="color-success">
<span> {{ row.afternoonPreCount }}</span>
<span>人</span>
</div>
</template>
</el-table-column>
<el-table-column min-width="100" prop="timeCount" align="center" label="迟到、早退次数">
<template #default="{ row }">
<div class="color-success">
<span> {{ row.timeCount }}</span>
<span>次</span>
</div>
</template>
</el-table-column>
<el-table-column min-width="100" prop="preCount" align="center" label="迟到、早退人数">
<template #default="{ row }">
<div class="color-success">
<span> {{ row.preCount }}</span>
<span>人</span>
</div>
</template>
</el-table-column>
<el-table-column label="操作" width="150" fixed="right">
<template #default="{ row }">
<el-button icon="InfoFilled" text type="primary" @click="openDoorTableDialogRef(row)">
详情
</el-button>
</template>
</el-table-column>
</el-table>
</div>
<door-table-dialog ref="doorTableDialogRef" />
</div>
</template>
<script setup lang="ts" name="systemDoorRecord">
import { BasicTableProps, useTable } from "/@/hooks/table";
import * as doorRecordApi from "/@/api/door/doorRecord";
import { useDict } from '/@/hooks/dict';
// 引入组件
const DoorTableDialog = defineAsyncComponent(() => import("../components/doorTableDialog.vue"));
// 定义查询字典
const { tool_time_type } = useDict("tool_time_type")
// 定义变量内容
const formDialogRef = ref()
// 搜索变量
const queryRef = ref()
const showSearch = ref(true)
// 多选变量
const selectObjs = ref([]) as any
const multiple = ref(true)
const doorTableDialogRef = ref<any>(null)
const state: BasicTableProps = reactive<BasicTableProps>({
isPage: false,
queryForm: { type: '3' },
pageList: doorRecordApi.beLateCount
})
// table hook
const {
getDataList,
currentChangeHandle,
sizeChangeHandle,
sortChangeHandle,
downBlobFile,
tableStyle
} = useTable(state)
// 清空搜索条件
const resetQuery = () => {
// 清空搜索条件
queryRef.value?.resetFields()
// 清空多选
selectObjs.value = []
getDataList()
}
// 导出excel
const exportExcel = () => {
downBlobFile('/door/doorRecord/export', Object.assign(state.queryForm, { ids: selectObjs }), 'doorRecord.xlsx')
}
// 多选事件
const selectionChangHandle = (objs: { id: string }[]) => {
selectObjs.value = objs.map(({ id }) => id);
multiple.value = !objs.length;
};
const getSummaries = (param: any) => {
const { columns, data } = param
const totalColumns = ["morningPreCount", "afternoonPreCount", "timeCount", "preCount"]
const totalRow = Array.from({ length: columns.length })
totalRow[0] = "合计"
columns.forEach((i: any, index: number) => {
if (totalColumns.includes(i.property)) {
const total = data
.filter((v: any) => v[i.property] != null)
.map((v: any) => Number(v[i.property]))
.reduce((pren: number, next: number) => pren + next, 0)
totalRow[index] = `${total}次`
}
})
return totalRow
}
function openDoorTableDialogRef(row: any) {
const query = {
swingTimeList: [row.time, addDays(row.time, 1)],
openTypeList: ['61', '62'],
}
doorTableDialogRef.value.openDialog(query)
}
function addDays(dateStr: string, days: number) {
const date = new Date(dateStr);
date.setDate(date.getDate() + days);
return date.toISOString().split('T')[0];
};
</script>
+4 -4
View File
@@ -39,25 +39,25 @@
<el-table-column prop="personCode" min-width="100" label="人员编号">
<template #default="{ row }">
<span v-if="row.personCode">{{ row.personCode }}</span>
<span v-else class="color-danger">未知</span>
<span v-else class="color-danger">访客</span>
</template>
</el-table-column>
<el-table-column prop="personName" min-width="100" label="姓名">
<template #default="{ row }">
<span v-if="row.personName">{{ row.personName }}</span>
<span v-else class="color-danger">未知</span>
<span v-else class="color-danger">访客</span>
</template>
</el-table-column>
<el-table-column prop="paperNumber" min-width="100" label="证件号码" show-overflow-tooltip>
<template #default="{ row }">
<span v-if="row.paperNumber">{{ row.paperNumber }}</span>
<span v-else class="color-danger">未知</span>
<span v-else class="color-danger">访客</span>
</template>
</el-table-column>
<el-table-column prop="deptName" min-width="100" label="部门名称">
<template #default="{ row }">
<span v-if="row.deptName">{{ row.deptName }}</span>
<span v-else class="color-danger">未知</span>
<span v-else class="color-danger">访客</span>
</template>
</el-table-column>
</el-table-column>
+144
View File
@@ -0,0 +1,144 @@
<template>
<div class="layout-padding">
<div class="layout-padding-auto layout-padding-view">
<el-row shadow="hover" v-show="showSearch" class="ml10">
<el-form :model="state.queryForm" ref="queryRef" :inline="true" @keyup.enter="getDataList">
<el-form-item style="width: 250px;" label="统计时间" prop="type">
<dict-select v-model="state.queryForm.type" :options="tool_time_type" placeholder="请选择统计时间" />
</el-form-item>
<el-form-item>
<el-button icon="search" type="primary" @click="getDataList">
{{ $t('common.queryBtn') }}
</el-button>
<el-button icon="Refresh" @click="resetQuery">{{ $t('common.resetBtn') }}</el-button>
</el-form-item>
</el-form>
</el-row>
<el-row>
<div class="mb8" style="width: 100%">
<right-toolbar v-model:showSearch="showSearch" :export="false" @exportExcel="exportExcel"
class="ml10 mr20" style="float: right;" @queryTable="getDataList"></right-toolbar>
</div>
</el-row>
<el-table :data="state.dataList" v-loading="state.loading" border :cell-style="tableStyle.cellStyle"
:header-cell-style="tableStyle.headerCellStyle" @selection-change="selectionChangHandle"
@sort-change="sortChangeHandle" show-summary :summary-method="getSummaries">
<el-table-column type="selection" width="80" align="center" />
<el-table-column type="index" label="#" width="80" />
<el-table-column min-width="100" label="日期">
<template #default="{ row }">
<span>{{ row.time }}</span>
<span> - </span>
<span class="color-primary">{{ row.weekStr }}</span>
</template>
</el-table-column>
<el-table-column min-width="100" prop="preCount" align="center" label="在位人量">
<template #default="{ row }">
<div class="color-success">
<span> {{ row.preCount }}</span>
<span>次</span>
</div>
</template>
</el-table-column>
<el-table-column label="操作" width="150" fixed="right">
<template #default="{ row }">
<el-button icon="InfoFilled" text type="primary" @click="openDoorTableDialogRef(row)">
详情
</el-button>
</template>
</el-table-column>
</el-table>
</div>
<door-table-dialog ref="doorTableDialogRef" />
</div>
</template>
<script setup lang="ts" name="systemDoorRecord">
import { BasicTableProps, useTable } from "/@/hooks/table";
import * as doorRecordApi from "/@/api/door/doorRecord";
import { useDict } from '/@/hooks/dict';
// 引入组件
const DoorTableDialog = defineAsyncComponent(() => import("../components/doorTableDialog.vue"));
// 定义查询字典
const { tool_time_type } = useDict("tool_time_type")
// 定义变量内容
const formDialogRef = ref()
// 搜索变量
const queryRef = ref()
const showSearch = ref(true)
// 多选变量
const selectObjs = ref([]) as any
const multiple = ref(true)
const doorTableDialogRef = ref<any>(null)
const state: BasicTableProps = reactive<BasicTableProps>({
isPage: false,
queryForm: { type: '3' },
pageList: doorRecordApi.reignCount
})
// table hook
const {
getDataList,
currentChangeHandle,
sizeChangeHandle,
sortChangeHandle,
downBlobFile,
tableStyle
} = useTable(state)
// 清空搜索条件
const resetQuery = () => {
// 清空搜索条件
queryRef.value?.resetFields()
// 清空多选
selectObjs.value = []
getDataList()
}
// 导出excel
const exportExcel = () => {
downBlobFile('/door/doorRecord/export', Object.assign(state.queryForm, { ids: selectObjs }), 'doorRecord.xlsx')
}
// 多选事件
const selectionChangHandle = (objs: { id: string }[]) => {
selectObjs.value = objs.map(({ id }) => id);
multiple.value = !objs.length;
};
const getSummaries = (param: any) => {
const { columns, data } = param
const totalColumns = ["preCount"]
const totalRow = Array.from({ length: columns.length })
totalRow[0] = "合计"
columns.forEach((i: any, index: number) => {
if (totalColumns.includes(i.property)) {
const total = data
.filter((v: any) => v[i.property] != null)
.map((v: any) => Number(v[i.property]))
.reduce((pren: number, next: number) => pren + next, 0)
totalRow[index] = `${total}次`
}
})
return totalRow
}
function openDoorTableDialogRef(row: any) {
const query = {
swingTimeList: [row.time, addDays(row.time, 1)],
openTypeList: ['61', '62'],
}
doorTableDialogRef.value.openDialog(query)
}
function addDays(dateStr: string, days: number) {
const date = new Date(dateStr);
date.setDate(date.getDate() + days);
return date.toISOString().split('T')[0];
};
</script>
+8 -4
View File
@@ -71,7 +71,7 @@
<el-table-column label="操作" width="150" fixed="right">
<template #default="{ row }">
<el-button icon="InfoFilled" text type="primary"
@click="doorTimesTableDialogRef.openDialog(row)">详情</el-button>
@click="openDoorTableDialogRef(row)">详情</el-button>
</template>
</el-table-column>
</el-table>
@@ -79,7 +79,7 @@
v-bind="state.pagination" />
</div>
<door-times-table-dialog ref="doorTimesTableDialogRef" />
<door-table-dialog ref="doorTableDialogRef" />
</div>
</template>
@@ -91,9 +91,9 @@ import { useDict } from '/@/hooks/dict';
import * as DoorRecordApi from "/@/api/door/doorRecord";
// 引入组件
const DoorTimesTableDialog = defineAsyncComponent(() => import("./doorTimesTableDialog.vue"));
const DoorTableDialog = defineAsyncComponent(() => import("../components/doorTableDialog.vue"));
const doorTimesTableDialogRef = ref<any>(null)
const doorTableDialogRef = ref<any>(null)
// 搜索变量
const queryRef = ref()
@@ -152,6 +152,10 @@ const getSummaries = (param: any) => {
return totalRow
}
function openDoorTableDialogRef(row: any) {
doorTableDialogRef.value.openDialog({ personCode: row.personCode })
}
</script>
<style lang="scss" scoped></style>
+8 -13
View File
@@ -3,12 +3,9 @@
<!-- 语言切换 -->
<el-dropdown v-if="isI18nEnabled" trigger="click" @command="onLanguageChange">
<div
class="flex items-center justify-center transition-colors rounded-lg cursor-pointer w-9 h-9 bg-white/80 dark:bg-slate-800/80 hover:bg-gray-100 dark:hover:bg-slate-700 backdrop-blur-sm"
>
<i
class="text-lg text-gray-600 dark:text-slate-300 iconfont"
:class="state.disabledI18n === 'en' ? 'icon-fuhao-yingwen' : 'icon-fuhao-zhongwen'"
></i>
class="flex items-center justify-center transition-colors rounded-lg cursor-pointer w-9 h-9 bg-white/80 dark:bg-slate-800/80 hover:bg-gray-100 dark:hover:bg-slate-700 backdrop-blur-sm">
<i class="text-lg text-gray-600 dark:text-slate-300 iconfont"
:class="state.disabledI18n === 'en' ? 'icon-fuhao-yingwen' : 'icon-fuhao-zhongwen'"></i>
</div>
<template #dropdown>
<el-dropdown-menu>
@@ -20,10 +17,8 @@
<!-- 主题切换 -->
<el-tooltip v-if="isDarkModeEnabled" :content="getThemeConfig.isDark ? '切换亮色模式' : '切换暗色模式'" placement="bottom">
<div
class="flex items-center justify-center transition-colors rounded-lg cursor-pointer w-9 h-9 bg-white/80 dark:bg-slate-800/80 hover:bg-gray-100 dark:hover:bg-slate-700 backdrop-blur-sm"
@click="onThemeClick"
>
<div class="flex items-center justify-center transition-colors rounded-lg cursor-pointer w-9 h-9 bg-white/80 dark:bg-slate-800/80 hover:bg-gray-100 dark:hover:bg-slate-700 backdrop-blur-sm"
@click="onThemeClick">
<el-icon :size="18" class="text-gray-600 dark:text-slate-300">
<ele-Moon v-if="!getThemeConfig.isDark" />
<ele-Sunny v-else />
@@ -66,7 +61,7 @@ const onLanguageChange = (lang: string) => {
// 主题切换
const onThemeClick = () => {
if (!isDarkModeEnabled) return; // 如果未启用暗黑模式,直接返回
const body = document.documentElement as HTMLElement;
themeConfig.value.isDark = !themeConfig.value.isDark;
@@ -91,11 +86,11 @@ const getThemeConfig = computed(() => {
// 在 script setup 中添加
const initLanguage = () => {
if (!isI18nEnabled) return; // 如果未启用多语言,直接返回
if (Local.get('themeConfig')) {
state.disabledI18n = Local.get('themeConfig').globalI18n;
// 确保 i18n locale 与存储的语言保持一致
locale.value = state.disabledI18n;
} else {
// 如果没有存储的主题配置,设置默认语言
+23 -38
View File
@@ -1,14 +1,9 @@
<template>
<el-form size="large" class="login-content-form" ref="loginFormRef" :rules="loginRules" :model="state.ruleForm" @keyup.enter="handleVerify">
<el-form size="large" class="login-content-form" ref="loginFormRef" :rules="loginRules" :model="state.ruleForm"
@keyup.enter="handleVerify">
<el-form-item class="login-animation1" prop="username">
<el-input
text
:placeholder="$t('password.accountPlaceholder1')"
v-model="state.ruleForm.username"
clearable
autocomplete="off"
class="dark:bg-slate-700 dark:text-slate-200"
>
<el-input text :placeholder="$t('password.accountPlaceholder1')" v-model="state.ruleForm.username" clearable
autocomplete="off" class="dark:bg-slate-700 dark:text-slate-200">
<template #prefix>
<el-icon class="el-input__icon dark:text-slate-400">
<ele-User />
@@ -17,31 +12,26 @@
</el-input>
</el-form-item>
<el-form-item class="login-animation2" prop="password">
<el-input
:type="state.isShowPassword ? 'text' : 'password'"
:placeholder="$t('password.accountPlaceholder2')"
v-model="state.ruleForm.password"
class="dark:bg-slate-700 dark:text-slate-200"
autocomplete="off"
>
<el-input :type="state.isShowPassword ? 'text' : 'password'"
:placeholder="$t('password.accountPlaceholder2')" v-model="state.ruleForm.password"
class="dark:bg-slate-700 dark:text-slate-200" autocomplete="off">
<template #prefix>
<el-icon class="el-input__icon dark:text-slate-400">
<ele-Unlock />
</el-icon>
</template>
<template #suffix>
<i
class="iconfont el-input__icon login-content-password"
<i class="iconfont el-input__icon login-content-password"
:class="state.isShowPassword ? 'icon-yincangmima' : 'icon-xianshimima'"
@click="state.isShowPassword = !state.isShowPassword"
>
@click="state.isShowPassword = !state.isShowPassword">
</i>
</template>
</el-input>
</el-form-item>
<el-form-item class="login-animation2" prop="code" v-if="verifyImageEnable">
<el-col :span="15">
<el-input text maxlength="4" :placeholder="$t('mobile.placeholder2')" v-model="state.ruleForm.code" clearable autocomplete="off">
<el-input text maxlength="4" :placeholder="$t('mobile.placeholder2')" v-model="state.ruleForm.code"
clearable autocomplete="off">
<template #prefix>
<el-icon class="el-input__icon">
<ele-Position />
@@ -55,17 +45,19 @@
</el-col>
</el-form-item>
<el-form-item class="mt-4 login-animation4">
<el-button type="primary" class="rounded-lg login-content-submit" v-waves @click="handleVerify" :loading="loading">
<el-button type="primary" class="rounded-lg login-content-submit" v-waves @click="handleVerify"
:loading="loading">
<span class="font-semibold tracking-wide">{{ $t('password.accountBtnText') }}</span>
</el-button>
</el-form-item>
<div class="flex relative justify-between items-center">
<div v-if="false" class="flex relative justify-between items-center">
<div class="ml-auto text-sm">
<a href="#" class="text-blue-500" @click="emit('change', LoginTypeEnum.MOBILE)">
{{ $t('password.mobileLogin') }}
</a>
<a href="#" v-if="autoRegisterEnable" class="ml-2 text-blue-500" @click="emit('change', LoginTypeEnum.REGISTER)">
<a href="#" v-if="autoRegisterEnable" class="ml-2 text-blue-500"
@click="emit('change', LoginTypeEnum.REGISTER)">
{{ $t('password.createAccount') }}
</a>
</div>
@@ -73,14 +65,8 @@
<div class="font12 mt30 login-animation4 login-msg text-slate-400">{{ $t('browserMsgText') }}</div>
</el-form>
<Verify
@success="verifySuccess"
:mode="'pop'"
:captchaType="'blockPuzzle'"
v-if="verifyEnable"
:imgSize="{ width: '330px', height: '155px' }"
ref="verifyref"
/>
<Verify @success="verifySuccess" :mode="'pop'" :captchaType="'blockPuzzle'" v-if="verifyEnable"
:imgSize="{ width: '330px', height: '155px' }" ref="verifyref" />
</template>
<script setup lang="ts" name="password">
@@ -105,8 +91,8 @@ const state = reactive({
isShowPassword: false, // 是否显示密码
ruleForm: {
// 表单数据
username: 'admin', // 用户名
password: '123456', // 密码
username: '', // 用户名
password: '', // 密码
code: '', // 验证码
randomStr: 'blockPuzzle', // 验证码随机数
},
@@ -127,14 +113,13 @@ const imgSrc = ref('');
// 调用图形证码进行校验
const getVerifyImageCode = () => {
state.ruleForm.randomStr = generateUUID();
imgSrc.value = `${import.meta.env.VITE_API_URL}${import.meta.env.VITE_IS_MICRO == 'false' ? '/admin' : '/auth'}/code/image?randomStr=${
state.ruleForm.randomStr
}`;
imgSrc.value = `${import.meta.env.VITE_API_URL}${import.meta.env.VITE_IS_MICRO == 'false' ? '/admin' : '/auth'}/code/image?randomStr=${state.ruleForm.randomStr
}`;
};
// 调用滑块验证码进行校验
const handleVerify = async () => {
const valid = await loginFormRef.value.validate().catch(() => {}); // 表单校验
const valid = await loginFormRef.value.validate().catch(() => { }); // 表单校验
if (valid && verifyEnable.value) {
verifyref.value.show(); // 显示验证组件
+16 -17
View File
@@ -3,26 +3,22 @@
<!-- 右上角控制组件 -->
<Control />
<div
class="w-full overflow-hidden bg-white border border-gray-200 shadow-lg dark:bg-slate-800 rounded-3xl dark:border-slate-700"
style="max-width: 1000px"
>
<div class="w-full overflow-hidden bg-white border border-gray-200 shadow-lg dark:bg-slate-800 rounded-3xl dark:border-slate-700"
style="max-width: 1000px">
<div class="w-full md:flex">
<!-- 左侧插图区域 -->
<div
class="hidden w-[55%] bg-gradient-to-br from-blue-400 via-blue-300 to-blue-200 dark:from-slate-800 dark:via-slate-700 dark:to-slate-900/95 md:flex md:items-center md:justify-center"
>
<img
class="w-[85%] h-auto max-h-[650px] object-contain opacity-100 dark:opacity-75 transition-opacity duration-300 hover:opacity-90"
class="hidden w-[55%] bg-gradient-to-br from-blue-400 via-blue-300 to-blue-200 dark:from-slate-800 dark:via-slate-700 dark:to-slate-900/95 md:flex md:items-center md:justify-center">
<img class="w-[85%] h-auto max-h-[650px] object-contain opacity-100 dark:opacity-75 transition-opacity duration-300 hover:opacity-90"
:src="!themeConfig.background ? illustration : baseURL + themeConfig.background"
alt="Login illustration"
/>
alt="Login illustration" />
</div>
<!-- 右侧登录表单区域 -->
<div class="w-full px-5 py-12 md:w-[45%] md:px-12">
<div class="mb-12 text-center">
<h1 class="text-4xl font-bold text-gray-900 dark:text-white tracking-wide font-['Inter']">{{ getThemeConfig.globalTitle }}</h1>
<h1 class="text-4xl font-bold text-gray-900 dark:text-white tracking-wide font-['Inter']">{{
getThemeConfig.globalTitle }}</h1>
<!-- 租户选择 -->
<div class="mt-6">
<tenant class="shadow-sm" />
@@ -32,26 +28,29 @@
<div class="w-full px-5">
<!-- 登录表单组件 -->
<register v-if="loginType === LoginTypeEnum.REGISTER" @change="changeLoginType" />
<password v-if="loginType === LoginTypeEnum.PASSWORD" @signInSuccess="signInSuccess" @change="changeLoginType" />
<mobile v-if="loginType === LoginTypeEnum.MOBILE" @signInSuccess="signInSuccess" @change="changeLoginType" />
<expire v-if="loginType === LoginTypeEnum.EXPIRE" :username="username" @change="changeLoginType" />
<password v-if="loginType === LoginTypeEnum.PASSWORD" @signInSuccess="signInSuccess"
@change="changeLoginType" />
<mobile v-if="loginType === LoginTypeEnum.MOBILE" @signInSuccess="signInSuccess"
@change="changeLoginType" />
<expire v-if="loginType === LoginTypeEnum.EXPIRE" :username="username"
@change="changeLoginType" />
<!-- 分割线 -->
<div class="flex items-center justify-center my-6 space-x-3">
<div v-if="false" class="flex items-center justify-center my-6 space-x-3">
<span class="w-20 h-[1.5px] bg-gray-200 dark:bg-slate-600"></span>
<span class="text-gray-600 dark:text-slate-400">{{ $t('divider.or') }}</span>
<span class="w-20 h-[1.5px] bg-gray-200 dark:bg-slate-600"></span>
</div>
<!-- 社交登录 -->
<social @signInSuccess="signInSuccess" />
<social v-if="false" @signInSuccess="signInSuccess" />
</div>
</div>
</div>
</div>
<!-- 小程序二维码 -->
<Qrcode />
<Qrcode v-if="false" />
<!-- Footer 组件 -->
<Footer />
+4 -4
View File
@@ -46,28 +46,28 @@
:label="columnSetting['personCode'].label">
<template #default="{ row }">
<span v-if="row.personCode">{{ row.personCode }}</span>
<span v-else class="color-danger">未知</span>
<span v-else class="color-danger">访客</span>
</template>
</el-table-column>
<el-table-column v-if="columnSetting['personName'].visible" prop="personName" min-width="100"
:label="columnSetting['personName'].label">
<template #default="{ row }">
<span v-if="row.personName">{{ row.personName }}</span>
<span v-else class="color-danger">未知</span>
<span v-else class="color-danger">访客</span>
</template>
</el-table-column>
<el-table-column v-if="columnSetting['paperNumber'].visible" prop="paperNumber" min-width="100"
:label="columnSetting['paperNumber'].label" show-overflow-tooltip>
<template #default="{ row }">
<span v-if="row.paperNumber">{{ row.paperNumber }}</span>
<span v-else class="color-danger">未知</span>
<span v-else class="color-danger">访客</span>
</template>
</el-table-column>
<el-table-column v-if="columnSetting['deptName'].visible" prop="deptName" min-width="100"
:label="columnSetting['deptName'].label">
<template #default="{ row }">
<span v-if="row.deptName">{{ row.deptName }}</span>
<span v-else class="color-danger">未知</span>
<span v-else class="color-danger">访客</span>
</template>
</el-table-column>
<!-- end 人员 end -->