修复门禁:安全化凭据下发与有效期来源

This commit is contained in:
wangzhibiao committed 2026-09-21 12:51:10 +08:00
1 parent a83a3a642e
commit 0d5df72d8e
10 files changed
+908 -142

No files matched your search

+16
View File
@@ -66,3 +66,19 @@ export function manualReconcile(enrollmentId) {
method: 'post'
})
}
// 管理员显式下发门禁凭据 (单次返回明文凭据)
export function issueAuth(authId) {
return request({
url: '/train/access/auth/' + authId + '/issue',
method: 'post'
})
}
// 管理员显式注销门禁授权
export function revokeAuth(authId) {
return request({
url: '/train/access/auth/' + authId + '/revoke',
method: 'post'
})
}
+2 -2
View File
@@ -107,14 +107,14 @@ export const dynamicRoutes = [
component: Layout,
hidden: true,
redirect: '/train/auth',
permissions: ['system:auth:list', 'train:accommodation:list'],
permissions: ['train:access:query', 'system:auth:list', 'train:accommodation:list'],
children: [
{
path: 'auth',
component: () => import('@/views/train/auth/index'),
name: 'DoorAuth',
meta: { title: '门禁授权', icon: 'lock' },
permissions: ['system:auth:list']
permissions: ['train:access:query', 'system:auth:list']
},
{
path: 'accommodation',
+55 -11
View File
@@ -67,6 +67,12 @@
</template>
</el-table-column>
<el-table-column label="学员学号" align="center" prop="studentId" width="120" />
<el-table-column label="手机号" align="center" width="125">
<template slot-scope="scope">
<span v-if="scope.row.studentPhone">{{ scope.row.studentPhone.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2') }}</span>
<el-tag v-else type="danger" size="mini">缺手机号</el-tag>
</template>
</el-table-column>
<el-table-column label="宿舍安排" align="center" min-width="130">
<template slot-scope="scope">
<span>{{ scope.row.dormBuilding || '' }} {{ scope.row.dormRoom || '' }} ({{ scope.row.bedNo || '-' }}床)</span>
@@ -108,7 +114,7 @@
</el-table-column>
<el-table-column label="重试次数" align="center" prop="retryCount" width="80" />
<el-table-column label="异常说明" align="center" prop="lastError" :show-overflow-tooltip="true" min-width="120" />
<el-table-column label="管理操作" align="center" width="160" class-name="small-padding fixed-width">
<el-table-column label="管理操作" align="center" width="280" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button
v-hasPermi="['train:access:query']"
@@ -124,9 +130,31 @@
icon="el-icon-refresh"
@click="handleManualReconcile(scope.row)"
>手动对账</el-button>
<span v-if="scope.row.authStatus === 'PENDING_GRANT' || scope.row.authStatus === 'ERROR'">
<el-tooltip v-if="!scope.row.studentPhone" content="缺手机号,暂无法下发门禁" placement="top">
<span style="display: inline-block; margin-left: 10px;">
<el-button
v-hasPermi="['train:access:manage']"
size="mini"
type="text"
icon="el-icon-key"
disabled
>下发门禁凭据</el-button>
</span>
</el-tooltip>
<el-button
v-else
v-hasPermi="['train:access:manage']"
size="mini"
type="text"
icon="el-icon-key"
style="color: #409EFF;"
@click="handleIssue(scope.row)"
>下发门禁凭据</el-button>
</span>
<el-button
v-if="scope.row.authStatus === 'ACTIVE' || scope.row.authStatus === 'GRANTING'"
v-hasPermi="['system:auth:edit']"
v-if="scope.row.authStatus === 'ACTIVE' || scope.row.authStatus === 'GRANTING' || scope.row.authStatus === 'PENDING_REVOKE'"
v-hasPermi="['train:access:manage']"
size="mini"
type="text"
icon="el-icon-circle-close"
@@ -196,7 +224,7 @@
<!-- 一次性临时凭据展示对话框 (ADMIN_ONE_TIME_DISPLAY) -->
<el-dialog
title="一次性临时凭据展示 (ADMIN_ONE_TIME_DISPLAY)"
title="一次性门禁凭据展示 (ADMIN_ONE_TIME_DISPLAY)"
:visible.sync="oneTimeDialogOpen"
width="550px"
append-to-body
@@ -205,7 +233,7 @@
:before-close="handleCloseOneTimeDialog"
>
<el-alert
title="安全合规提示:该临时凭据仅在本次操作创建成功后由内存短暂展示一次!关闭弹窗或刷新页面后将永久清除,系统不持久化亦无法再次查询。"
title="凭据已提交下发,设备确认后生效。安全合规提示:该临时凭据仅在本次操作下发成功后由内存短暂展示一次!关闭弹窗或刷新页面后将永久清除,系统不持久化亦无法再次查询旧明文。"
type="warning"
:closable="false"
show-icon
@@ -230,8 +258,7 @@
</template>
<script>
import { listAuth, getOperationHistory, manualReconcile } from '@/api/train/access'
import { revokeAuth } from '@/api/train/auth'
import { listAuth, getOperationHistory, manualReconcile, issueAuth, revokeAuth } from '@/api/train/access'
export default {
name: 'Auth',
@@ -308,18 +335,35 @@ export default {
this.getList()
if (res.data && res.data.oneTimePlaintextCredential) {
this.oneTimeCredential = res.data.oneTimePlaintextCredential
this.maskedMode = true
this.maskedMode = false
this.oneTimeDialogOpen = true
}
}).catch(() => {})
},
handleIssue(row) {
if (!row.studentPhone) {
this.$modal.msgWarning('缺手机号,暂无法下发门禁')
return
}
this.$modal.confirm(`确认向学员【${row.studentName || row.studentId}】下发门禁凭据?`).then(() => {
return issueAuth(row.authId)
}).then(res => {
this.$modal.msgSuccess(res.msg || '凭据已提交下发,设备确认后生效。')
this.getList()
if (res.data && res.data.oneTimePlaintextCredential) {
this.oneTimeCredential = res.data.oneTimePlaintextCredential
this.maskedMode = false
this.oneTimeDialogOpen = true
}
}).catch(() => {})
},
handleRevoke(row) {
const studentDesc = row.studentId || `报名ID:${row.enrollmentId}`
const studentDesc = row.studentName ? `${row.studentName} (${row.studentId || row.enrollmentId})` : (row.studentId || `报名ID:${row.enrollmentId}`)
const dormDesc = `${row.dormBuilding || ''} ${row.dormRoom || ''}`
this.$modal.confirm(`确定要注销学员【${studentDesc}】在宿舍【${dormDesc}】的门禁授权(授权ID: ${row.authId})吗?注销后该学员将无法通行该门禁。`).then(() => {
return revokeAuth(row.authId)
}).then(() => {
this.$modal.msgSuccess('注销成功')
}).then(res => {
this.$modal.msgSuccess(res.msg || '注销操作已提交')
this.getList()
}).catch(() => {})
}