fix: security remediation follow-up and OLT button layout improvements

- fix Casdoor JWT verification with correct PythonProject public key
- fix iMC TLS cert validation with custom SSL adapter (skip hostname check for non-DNS CN)
- add iMC CA cert and Casdoor public key to build context
- improve OLT manage page: unify button styles, fix mobile grid spacing, replace el-upload with native input for consistent alignment
- swap NTP sync button for duplicate MAC on mobile

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-07-30 15:05:52 +08:00
parent 26f6ca2d8d
commit f511e3e808
5 changed files with 135 additions and 35 deletions
+65 -33
View File
@@ -8,31 +8,22 @@
</div>
<div class="header-actions">
<el-button v-if="!isMobile && can('olt.manage')" type="primary" size="small" @click="openAddDialog">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" style="margin-right: 5px">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" class="btn-icon">
<line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/>
</svg>
添加 OLT
</el-button>
<el-upload
v-if="!isMobile && can('olt.manage')"
:auto-upload="false"
:show-file-list="false"
:on-change="handleImportFile"
accept=".xlsx,.xls"
style="display: inline-block"
>
<el-button type="success" size="small" :loading="importing">批量导入</el-button>
</el-upload>
<el-button v-if="!isMobile && can('olt.manage')" size="small" @click="downloadTemplate">下载模板</el-button>
<el-button v-if="!isMobile && can('olt.manage')" type="danger" plain size="small" @click="openDuplicateMacs">
重复 MAC
<el-badge v-if="duplicateCount > 0" :value="duplicateCount" style="margin-left: 4px" />
<el-button v-if="!isMobile && can('olt.manage')" type="success" plain size="small" :loading="importing" @click="triggerImport">
批量导入
</el-button>
<el-button v-if="!isMobile && can('olt.manage')" plain size="small" @click="downloadTemplate">下载模板</el-button>
<el-button v-if="can('olt.manage')" type="danger" plain size="small" @click="openDuplicateMacs">
重复 MAC<span v-if="duplicateCount > 0" class="btn-count danger">{{ duplicateCount }}</span>
</el-button>
<el-button v-if="can('olt.manage')" type="warning" plain size="small" @click="openNewDevices">
新增设备
<el-badge v-if="newDeviceCount > 0" :value="newDeviceCount" style="margin-left: 4px" />
新增设备<span v-if="newDeviceCount > 0" class="btn-count warning">{{ newDeviceCount }}</span>
</el-button>
<el-button v-if="can('olt.manage')" type="info" plain size="small" @click="openNtpSync" :loading="ntpSyncing">
<el-button v-if="!isMobile && can('olt.manage')" type="info" plain size="small" @click="openNtpSync" :loading="ntpSyncing">
同步NTP
</el-button>
<el-button v-if="can('olt.loopback')" type="danger" plain size="small" @click="runLoopbackDetection" :loading="loopDetecting">
@@ -41,6 +32,15 @@
<el-button v-if="can('olt.discover')" type="primary" plain size="small" @click="runQuickScan" :loading="quickScanning">
快速扫描
</el-button>
<!-- 隐藏的上传组件批量导入触发 -->
<input
v-if="!isMobile && can('olt.manage')"
ref="importInput"
type="file"
accept=".xlsx,.xls"
style="display:none"
@change="handleImportFile"
/>
</div>
</div>
@@ -604,6 +604,7 @@ const newDevList = ref([])
const newDeviceCount = ref(0)
const newDevVisible = ref(false)
const savingDev = ref(null)
const importInput = ref(null)
const loopDetecting = ref(false)
const loopVisible = ref(false)
const loopResults = ref([])
@@ -973,10 +974,16 @@ const showLoopDetail = (oltId) => {
loopDetailVisible.value = true
}
const handleImportFile = async (uploadFile) => {
const triggerImport = () => {
importInput.value?.click()
}
const handleImportFile = async (event) => {
const file = event.target?.files?.[0] || (event.raw || event)
if (!file) return
importing.value = true
const formData = new FormData()
formData.append('file', uploadFile.raw)
formData.append('file', file)
try {
const { data } = await request.post('/olt/import', formData, {
headers: { 'Content-Type': 'multipart/form-data' }
@@ -1077,8 +1084,35 @@ onMounted(() => {
.header-actions {
display: flex;
align-items: center;
gap: 8px;
flex-wrap: wrap;
gap: 8px;
}
.btn-icon {
margin-right: 5px;
vertical-align: -2px;
}
.btn-count {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 18px;
height: 18px;
padding: 0 5px;
margin-left: 4px;
border-radius: 9px;
font-size: 11px;
font-weight: 600;
line-height: 1;
flex-shrink: 0;
}
.btn-count.danger { background: var(--danger); color: #fff; }
.btn-count.warning { background: var(--warning); color: #fff; }
.header-actions .el-button {
white-space: nowrap;
}
/* 数据面板 */
@@ -1526,19 +1560,17 @@ onMounted(() => {
.header-actions {
width: 100%;
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 8px;
}
.header-actions .el-button,
.header-actions > * {
width: 100%;
min-height: 44px;
}
/* 移动端按钮区只显示2列(移动端只剩新增设备、环路检测、快速扫描) */
.header-actions {
grid-template-columns: repeat(2, 1fr);
gap: 8px;
justify-items: center;
}
.header-actions .el-button {
width: 95%;
min-height: 44px;
padding-left: 14px;
padding-right: 14px;
justify-content: center;
}
/* 端口管理对话框:限制高度支持滚动 */