Files
railwaysystem/build_ui/src/views/train/auth/index.vue
T

263 lines
12 KiB
Vue

<template>
<div class="app-container">
<el-alert
title="门禁授权管理规范:门禁授权受学员报到确认、住宿安排及排课计划自动驱动。根据当前安全策略,在线密码凭证待业务侧安全交付方案确认(密文脱敏),系统不持久化亦不展示任何密码明文、Token或密钥。"
type="info"
:closable="false"
show-icon
style="margin-bottom: 16px;"
/>
<el-form v-show="showSearch" ref="queryForm" :model="queryParams" size="small" :inline="true" label-width="80px">
<el-form-item label="学员学号" prop="studentId">
<el-input
v-model="queryParams.studentId"
placeholder="请输入学员学号"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="宿舍楼栋" prop="dormBuilding">
<el-input
v-model="queryParams.dormBuilding"
placeholder="请输入楼栋"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="房间号" prop="dormRoom">
<el-input
v-model="queryParams.dormRoom"
placeholder="请输入房间号"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="授权状态" prop="authStatus">
<el-select v-model="queryParams.authStatus" placeholder="全部状态" clearable>
<el-option label="待授权 (PENDING_GRANT)" value="PENDING_GRANT" />
<el-option label="授权中 (GRANTING)" value="GRANTING" />
<el-option label="生效中 (ACTIVE)" value="ACTIVE" />
<el-option label="待注销 (PENDING_REVOKE)" value="PENDING_REVOKE" />
<el-option label="注销中 (REVOKING)" value="REVOKING" />
<el-option label="已注销 (REVOKED)" value="REVOKED" />
<el-option label="异常 (ERROR)" value="ERROR" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<right-toolbar :show-search.sync="showSearch" @queryTable="getList" />
</el-row>
<el-table v-loading="loading" :data="authList">
<el-table-column label="授权ID" align="center" prop="authId" width="80" />
<el-table-column label="报名ID" align="center" prop="enrollmentId" width="90">
<template slot-scope="scope">
<span>{{ scope.row.enrollmentId || '-' }}</span>
</template>
</el-table-column>
<el-table-column label="学员姓名" align="center" width="110">
<template slot-scope="scope">
<span>{{ scope.row.studentName || scope.row.studentId || '-' }}</span>
</template>
</el-table-column>
<el-table-column label="学员学号" align="center" prop="studentId" width="120" />
<el-table-column label="宿舍安排" align="center" min-width="130">
<template slot-scope="scope">
<span>{{ scope.row.dormBuilding || '' }} {{ scope.row.dormRoom || '' }} ({{ scope.row.bedNo || '-' }}床)</span>
</template>
</el-table-column>
<el-table-column label="业务授权状态" align="center" width="140">
<template slot-scope="scope">
<el-tag v-if="scope.row.authStatus === 'ACTIVE'" type="success" effect="dark" size="small">生效中 (ACTIVE)</el-tag>
<el-tag v-else-if="scope.row.authStatus === 'PENDING_GRANT'" type="info" size="small">待授权 (PENDING_GRANT)</el-tag>
<el-tag v-else-if="scope.row.authStatus === 'GRANTING'" type="warning" size="small">授权中 (GRANTING)</el-tag>
<el-tag v-else-if="scope.row.authStatus === 'PENDING_REVOKE'" type="warning" size="small">待注销 (PENDING_REVOKE)</el-tag>
<el-tag v-else-if="scope.row.authStatus === 'REVOKING'" type="warning" size="small">注销中 (REVOKING)</el-tag>
<el-tag v-else-if="scope.row.authStatus === 'REVOKED'" type="info" size="small">已注销 (REVOKED)</el-tag>
<el-tag v-else-if="scope.row.authStatus === 'ERROR'" type="danger" size="small">异常 (ERROR)</el-tag>
<el-tag v-else size="small">{{ scope.row.authStatus || 'UNKNOWN' }}</el-tag>
</template>
</el-table-column>
<el-table-column label="凭据类型" align="center" width="130">
<template slot-scope="scope">
<el-tag size="mini" type="info">{{ scope.row.credentialType || 'ONLINE_PASSWORD' }}</el-tag>
</template>
</el-table-column>
<el-table-column label="安全凭据状态" align="center" min-width="160">
<template slot-scope="{ row }">
<el-tag size="mini" type="warning">{{ row.authStatus ? '凭证待业务侧安全交付方案确认' : '待确认' }}</el-tag>
</template>
</el-table-column>
<el-table-column label="权限生效时间" align="center" min-width="150">
<template slot-scope="scope">
<span v-if="scope.row.authStart">{{ parseTime(scope.row.authStart, '{y}-{m}-{d} {h}:{i}') }}</span>
<el-tag v-else size="mini" type="info">WAITING_FOR_PREREQUISITE</el-tag>
</template>
</el-table-column>
<el-table-column label="权限失效时间" align="center" min-width="150">
<template slot-scope="scope">
<span v-if="scope.row.authEnd">{{ parseTime(scope.row.authEnd, '{y}-{m}-{d} {h}:{i}') }}</span>
<el-tag v-else size="mini" type="info">WAITING_FOR_PREREQUISITE</el-tag>
</template>
</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">
<template slot-scope="scope">
<el-button
v-hasPermi="['train:access:query']"
size="mini"
type="text"
icon="el-icon-document"
@click="handleOperationHistory(scope.row)"
>操作流水</el-button>
<el-button
v-hasPermi="['train:access:reconcile']"
size="mini"
type="text"
icon="el-icon-refresh"
@click="handleManualReconcile(scope.row)"
>手动对账</el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total>0"
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
<!-- 外部操作流水历史对话框 -->
<el-dialog title="外部设备操作流水 (External Operation History)" :visible.sync="operationOpen" width="850px" append-to-body>
<el-alert
title="安全提示:操作流水记录了系统向外部门禁/门锁发起的调度记录,已执行全链路敏感信息脱敏(无明文密码、无AppSecret、无Token)。"
type="info"
:closable="false"
show-icon
style="margin-bottom: 15px;"
/>
<el-table v-loading="operationLoading" :data="operationList" size="small" border empty-text="当前暂无外部操作流水记录">
<el-table-column label="流水ID" prop="operationId" width="80" align="center" />
<el-table-column label="操作类型" prop="operationType" width="140" align="center">
<template slot-scope="scope">
<el-tag v-if="scope.row.operationType === 'GRANT_PASSCODE'" type="success" size="mini">下发密码 (GRANT)</el-tag>
<el-tag v-else-if="scope.row.operationType === 'REVOKE_PASSCODE'" type="danger" size="mini">注销密码 (REVOKE)</el-tag>
<el-tag v-else size="mini">{{ scope.row.operationType }}</el-tag>
</template>
</el-table-column>
<el-table-column label="执行状态" prop="operationStatus" width="100" align="center">
<template slot-scope="scope">
<el-tag v-if="scope.row.operationStatus === 'SUCCESS'" type="success" size="mini">成功</el-tag>
<el-tag v-else-if="scope.row.operationStatus === 'FAILED'" type="danger" size="mini">失败</el-tag>
<el-tag v-else-if="scope.row.operationStatus === 'RUNNING'" type="warning" size="mini">运行中</el-tag>
<el-tag v-else size="mini">{{ scope.row.operationStatus }}</el-tag>
</template>
</el-table-column>
<el-table-column label="尝试次数" prop="attemptCount" width="80" align="center" />
<el-table-column label="请求时间" align="center" width="150">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.requestedAt, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
</template>
</el-table-column>
<el-table-column label="完成时间" align="center" width="150">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.completedAt, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
</template>
</el-table-column>
<el-table-column label="外部流水引用" prop="externalOperationRef" align="center" min-width="120" :show-overflow-tooltip="true" />
<el-table-column label="错误说明" prop="sanitizedErrorMessage" align="center" min-width="120" :show-overflow-tooltip="true">
<template slot-scope="scope">
<span style="color: #F56C6C;">{{ scope.row.sanitizedErrorMessage || '-' }}</span>
</template>
</el-table-column>
</el-table>
<div slot="footer" class="dialog-footer">
<el-button @click="operationOpen = false">关 闭</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { listAuth, getOperationHistory, manualReconcile } from '@/api/train/access'
export default {
name: 'Auth',
data() {
return {
loading: true,
showSearch: true,
total: 0,
authList: [],
queryParams: {
pageNum: 1,
pageSize: 10,
studentId: null,
dormBuilding: null,
dormRoom: null,
authStatus: null
},
operationOpen: false,
operationLoading: false,
operationList: []
}
},
created() {
this.getList()
},
methods: {
getList() {
this.loading = true
listAuth(this.queryParams).then(response => {
this.authList = response.rows || []
this.total = response.total || 0
this.loading = false
}).catch(() => {
this.loading = false
})
},
handleQuery() {
this.queryParams.pageNum = 1
this.getList()
},
resetQuery() {
this.resetForm('queryForm')
this.handleQuery()
},
handleOperationHistory(row) {
this.operationOpen = true
this.operationLoading = true
getOperationHistory(row.authId).then(res => {
this.operationList = res.data || []
this.operationLoading = false
}).catch(() => {
this.operationLoading = false
})
},
handleManualReconcile(row) {
if (!row.enrollmentId) {
this.$modal.msgError('当前授权记录未关联报名信息,无法执行对账')
return
}
this.$modal.confirm(
'确认对报名编号 [' + row.enrollmentId + '] 执行门禁对账?系统将严格遵循业务前置校验规则。'
).then(() => {
return manualReconcile(row.enrollmentId)
}).then(res => {
this.$modal.msgSuccess(res.msg || '对账执行成功')
this.getList()
}).catch(() => {})
}
}
}
</script>