2
0

feat:白名单和告警界面中英适配

This commit is contained in:
zhongzm
2025-06-19 12:27:23 +08:00
parent e8c98e77d3
commit abda1ebe54
4 changed files with 167 additions and 68 deletions

View File

@@ -893,6 +893,54 @@ const local: any = {
deleteConfirmContent: 'Sure to delete portal {name} ', deleteConfirmContent: 'Sure to delete portal {name} ',
deleteSuccess: 'Delete success' deleteSuccess: 'Delete success'
}, },
alerts:{
alerts:'Alerts',
choosesite: 'Please select a site',
deletesuccess: 'Delete successful',
deletefail: 'Delete failed',
choserecord: 'Please select the record to operate on',
deletetitle: 'Confirm deletion',
deletebody: 'record(s)?',
suredelete: 'Are you sure you want to delete this alarm record?',
sure: 'Confirm',
cancel: 'Cancel',
unresolved: 'Unresolved',
resolved: 'Resolved',
all: 'All',
system: 'System',
device: 'Device',
batchdelete: 'Batch delete',
resovetitle: 'Mark as resolved',
delete: 'Delete',
type:'TYPE',
level:'LEVEL',
content:'CONTENT',
time:'TIME',
action:'ACTION',
},
access:{
title:'Access Control',
plesite:'please select site',
authaccess:'Pre-Authentication Access',
enable:'Enable',
authaccesslist:'Pre-Authentication Access List',
delete:'Delete',
authclient:'Authentication-Free Client',
authclientlist:'Authentication-Free Client List',
apply:'Apply',
cancel:'Cancel',
type:'TYPE',
pleurl:'Please select URL',
addauth:'Add Pre-Authentication Access',
addclient:'Add Authentication-Free Client',
add:'Add',
info:'INFORMATION',
action:'ACTION',
deletetitle:'Delete',
deletebody:'Are you sure you want to delete this record?',
ok:'confirm',
},
terminal:{ terminal:{
title:'Terminal', title:'Terminal',
total:'Total', total:'Total',

View File

@@ -894,6 +894,53 @@ const local:any = {
deleteConfirmContent: '确定要删除门户 {name} 吗?', deleteConfirmContent: '确定要删除门户 {name} 吗?',
deleteSuccess: '删除成功' deleteSuccess: '删除成功'
}, },
alerts:{
alerts:'告警',
chosesite:'请选择站点',
deletesuccess:'删除成功',
deletefail:'删除失败',
choserecord:'请选择需要操作的记录',
deletetitle:'确认删除',
deletebody:'条记录吗?',
suredelete:'确定要删除该条告警记录吗?',
sure:'确定',
cancel:'取消',
unresolved:'未解决',
resolved:'已解决',
all:'全部',
system:'系统',
device:'设备',
batchdelete:'批量删除',
resovetitle:'标记为已解决',
delete:'删除',
type:'类型',
level:'等级',
content:'说明',
time:'时间',
action:'操作',
},
access:{
title:'白名单控制',
plesite:'请选择站点',
authaccess:'预认证访问',
enable:'开启',
authaccesslist:'预认证访问列表',
delete:'删除',
authclient:'免认证客户端',
authclientlist:'免认证客户端列表',
apply:'提交',
cancel:'取消',
type:'类型',
pleurl:'请输入URL',
addauth:'添加预认证访问',
addclient:'添加免认证客户端',
add:'添加',
info:'信息',
action:'操作',
deletetitle:'确认删除',
deletebody:'确定要删除这条记录吗?',
ok:'确认',
},
terminal:{ terminal:{
title:'终端设备', title:'终端设备',
total:'共', total:'共',

View File

@@ -6,13 +6,13 @@
> >
<div> <div>
<!-- 标题 --> <!-- 标题 -->
<h2 style="font-weight: bold; font-size: 22px; margin-bottom: 8px;">Access Control</h2> <h2 style="font-weight: bold; font-size: 22px; margin-bottom: 8px;">{{ t('page.access.title') }}</h2>
<!-- 站点选择框 --> <!-- 站点选择框 -->
<div style="display: flex; align-items: center; margin-bottom: 24px;"> <div style="display: flex; align-items: center; margin-bottom: 24px;">
<a-select <a-select
v-model:value="selectedSiteId" v-model:value="selectedSiteId"
:loading="siteLoading" :loading="siteLoading"
placeholder="请选择站点" :placeholder="t('page.access.plesite')"
style="width: 200px; margin-right: 16px;" style="width: 200px; margin-right: 16px;"
@change="handleSiteChange" @change="handleSiteChange"
> >
@@ -24,17 +24,18 @@
<!-- Pre-Authentication Access --> <!-- Pre-Authentication Access -->
<div style="margin-bottom: 16px; display: flex; align-items: center; gap: 12px;"> <div style="margin-bottom: 16px; display: flex; align-items: center; gap: 12px;">
<span>Pre-Authentication Access</span> <span>{{ t('page.access.authaccess') }}</span>
<a-switch v-model:checked="preAuthEnabled" /> <a-switch v-model:checked="preAuthEnabled" />
<span>Enable</span> <span>{{ t('page.access.enable') }}</span>
<a-tooltip title="Pre-Authentication Access 说明"> <a-tooltip title="Pre-Authentication Access 说明">
<i class="anticon anticon-info-circle" style="color: #13c2c2; margin-left: 4px;"></i> <i class="anticon anticon-info-circle" style="color: #13c2c2; margin-left: 4px;"></i>
</a-tooltip> </a-tooltip>
</div> </div>
<div v-if="preAuthEnabled"> <div v-if="preAuthEnabled">
<div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px;"> <div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px;">
<span>Pre-Authentication Access List</span> <span>{{ t('page.access.authaccesslist') }}</span>
<a-button type="link" style="color: #13c2c2; font-weight: bold;" @click="onAddPreAuth"><i class="anticon anticon-plus-circle" /> Add</a-button> <a-button type="link" style="color: #13c2c2; font-weight: bold;" @click="onAddPreAuth"><i class="anticon anticon-plus-circle" />
{{ t('page.access.add') }}</a-button>
</div> </div>
<a-table <a-table
:columns="preAuthColumns" :columns="preAuthColumns"
@@ -46,7 +47,7 @@
> >
<template #bodyCell="{ column, record }"> <template #bodyCell="{ column, record }">
<template v-if="column.key === 'action'"> <template v-if="column.key === 'action'">
<a-tooltip title="删除"> <a-tooltip :title="t('page.access.delete')">
<a-button <a-button
type="link" type="link"
size="small" size="small"
@@ -65,17 +66,18 @@
<!-- Authentication-Free Client --> <!-- Authentication-Free Client -->
<div style="margin: 32px 0 16px 0; display: flex; align-items: center; gap: 12px;"> <div style="margin: 32px 0 16px 0; display: flex; align-items: center; gap: 12px;">
<span>Authentication-Free Client</span> <span>{{ t('page.access.authclient') }}</span>
<a-switch v-model:checked="freeClientEnabled" /> <a-switch v-model:checked="freeClientEnabled" />
<span>Enable</span> <span>{{ t('page.access.enable') }}</span>
<a-tooltip title="Authentication-Free Client 说明"> <a-tooltip title="Authentication-Free Client 说明">
<i class="anticon anticon-info-circle" style="color: #13c2c2; margin-left: 4px;"></i> <i class="anticon anticon-info-circle" style="color: #13c2c2; margin-left: 4px;"></i>
</a-tooltip> </a-tooltip>
</div> </div>
<div v-if="freeClientEnabled"> <div v-if="freeClientEnabled">
<div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px;"> <div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px;">
<span>Authentication-Free Client List</span> <span>{{ t('page.access.authclientlist') }}</span>
<a-button type="link" style="color: #13c2c2; font-weight: bold;" @click="onAddFreeClient"><i class="anticon anticon-plus-circle" /> Add</a-button> <a-button type="link" style="color: #13c2c2; font-weight: bold;" @click="onAddFreeClient"><i class="anticon anticon-plus-circle" />
{{ t('page.access.add') }}</a-button>
</div> </div>
<a-table <a-table
:columns="freeClientColumns" :columns="freeClientColumns"
@@ -87,7 +89,7 @@
> >
<template #bodyCell="{ column, record }"> <template #bodyCell="{ column, record }">
<template v-if="column.key === 'action'"> <template v-if="column.key === 'action'">
<a-tooltip title="删除"> <a-tooltip :title="t('page.access.delete')">
<a-button <a-button
type="link" type="link"
size="small" size="small"
@@ -106,15 +108,15 @@
<!-- 操作按钮 --> <!-- 操作按钮 -->
<div style="margin-top: 32px; display: flex; gap: 16px;"> <div style="margin-top: 32px; display: flex; gap: 16px;">
<a-button type="primary" @click="handleApply">Apply</a-button> <a-button type="primary" @click="handleApply">{{ t('page.access.apply') }}</a-button>
<a-button @click="handleCancel">Cancel</a-button> <a-button @click="handleCancel">{{ t('page.access.cancel') }}</a-button>
</div> </div>
</div> </div>
</a-card> </a-card>
<!-- 新增添加预认证白名单弹窗 --> <!-- 新增添加预认证白名单弹窗 -->
<a-modal <a-modal
v-model:visible="addPreAuthVisible" v-model:visible="addPreAuthVisible"
title="Add Pre-Authentication Access" :title="t('page.access.addauth')"
@ok="handleAddPreAuthOk" @ok="handleAddPreAuthOk"
@cancel="() => addPreAuthVisible = false" @cancel="() => addPreAuthVisible = false"
destroyOnClose destroyOnClose
@@ -150,7 +152,7 @@
<!-- 新增添加免认证白名单弹窗 --> <!-- 新增添加免认证白名单弹窗 -->
<a-modal <a-modal
v-model:visible="addFreeClientVisible" v-model:visible="addFreeClientVisible"
title="Add Authentication-Free Client" :title="t('page.access.addclient')"
@ok="handleAddFreeClientOk" @ok="handleAddFreeClientOk"
@cancel="() => addFreeClientVisible = false" @cancel="() => addFreeClientVisible = false"
destroyOnClose destroyOnClose
@@ -199,6 +201,8 @@ import { fetchSiteList, fetchAccessControl, updateAccessControl } from '@/servic
import { DeleteOutlined } from '@ant-design/icons-vue' import { DeleteOutlined } from '@ant-design/icons-vue'
import { message, Modal } from 'ant-design-vue' import { message, Modal } from 'ant-design-vue'
import { SimpleScrollbar } from '~/packages/materials/src'; import { SimpleScrollbar } from '~/packages/materials/src';
import { useI18n } from 'vue-i18n';
const { t } = useI18n();
const preAuthEnabled = ref(true) const preAuthEnabled = ref(true)
const freeClientEnabled = ref(true) const freeClientEnabled = ref(true)
const preAuthList = ref([]) // 预认证白名单列表 const preAuthList = ref([]) // 预认证白名单列表
@@ -223,7 +227,7 @@ const addFreeClientMacParts = ref(['', '', '', '', '', ''])
// 定义表格列配置 // 定义表格列配置
const preAuthColumns = [ const preAuthColumns = [
{ {
title: 'TYPE', title: t('page.access.type'),
dataIndex: 'type', dataIndex: 'type',
key: 'type', key: 'type',
customRender: ({ record }: { record: any }) => { customRender: ({ record }: { record: any }) => {
@@ -233,7 +237,7 @@ const preAuthColumns = [
} }
}, },
{ {
title: 'INFORMATION', title: t('page.access.info'),
dataIndex: 'url', dataIndex: 'url',
key: 'url', key: 'url',
customRender: ({ record }: { record: any }) => { customRender: ({ record }: { record: any }) => {
@@ -243,7 +247,7 @@ const preAuthColumns = [
} }
}, },
{ {
title: 'ACTION', title: t('page.access.action'),
key: 'action', key: 'action',
width: 120, width: 120,
fixed: 'right', fixed: 'right',
@@ -267,7 +271,7 @@ const preAuthColumns = [
const freeClientColumns = [ const freeClientColumns = [
{ {
title: 'TYPE', title: t('page.access.type'),
dataIndex: 'type', dataIndex: 'type',
key: 'type', key: 'type',
customRender: ({ record }: { record: any }) => { customRender: ({ record }: { record: any }) => {
@@ -277,7 +281,7 @@ const freeClientColumns = [
} }
}, },
{ {
title: 'INFORMATION', title: t('page.access.info'),
dataIndex: 'clientIp', dataIndex: 'clientIp',
key: 'clientIp', key: 'clientIp',
customRender: ({ record }: { record: any }) => { customRender: ({ record }: { record: any }) => {
@@ -287,7 +291,7 @@ const freeClientColumns = [
} }
}, },
{ {
title: 'ACTION', title: t('page.access.action'),
key: 'action', key: 'action',
width: 120, width: 120,
fixed: 'right', fixed: 'right',
@@ -423,10 +427,10 @@ const handleAddFreeClientOk = () => {
const handleDeletePreAuth = (record: any) => { const handleDeletePreAuth = (record: any) => {
Modal.confirm({ Modal.confirm({
title: '确认删除', title: t('page.access.deletetitle'),
content: '确定要删除该条记录吗?', content: t('page.access.deletebody'),
okText: '确认', okText: t('page.access.ok'),
cancelText: '取消', cancelText: t('page.access.cancel'),
onOk: () => { onOk: () => {
preAuthList.value = preAuthList.value.filter(item => item.key !== record.key) preAuthList.value = preAuthList.value.filter(item => item.key !== record.key)
} }
@@ -435,10 +439,10 @@ const handleDeletePreAuth = (record: any) => {
const handleDeleteFreeClient = (record: any) => { const handleDeleteFreeClient = (record: any) => {
Modal.confirm({ Modal.confirm({
title: '确认删除', title: t('page.access.deletetitle'),
content: '确定要删除该条记录吗?', content: t('page.access.deletebody'),
okText: '确认', okText: t('page.access.ok'),
cancelText: '取消', cancelText: t('page.access.cancel'),
onOk: () => { onOk: () => {
freeClientList.value = freeClientList.value.filter(item => item.key !== record.key) freeClientList.value = freeClientList.value.filter(item => item.key !== record.key)
} }
@@ -454,7 +458,7 @@ watch(selectedSiteId, (val) => {
// 在 script setup 中添加提交函数 // 在 script setup 中添加提交函数
const handleApply = async () => { const handleApply = async () => {
if (!selectedSiteId.value) { if (!selectedSiteId.value) {
message.warning('请先选择站点') // message.warning('请先选择站点')
return return
} }
@@ -478,31 +482,31 @@ const handleApply = async () => {
// 调用更新接口 // 调用更新接口
const { error } = await updateAccessControl(selectedSiteId.value, submitData) const { error } = await updateAccessControl(selectedSiteId.value, submitData)
if (error) { if (error) {
message.error('提交失败') // message.error('提交失败')
return return
} }
message.success('提交成功') // message.success('提交成功')
// 重新获取最新数据 // 重新获取最新数据
await getAccessControl() await getAccessControl()
} catch (e) { } catch (e) {
console.error('提交出错:', e) console.error('提交出错:', e)
message.error('提交失败') // message.error('提交失败')
} }
} }
// 在 script setup 中添加 Cancel 处理函数 // 在 script setup 中添加 Cancel 处理函数
const handleCancel = async () => { const handleCancel = async () => {
if (!selectedSiteId.value) { if (!selectedSiteId.value) {
message.warning('请先选择站点') // message.warning('请先选择站点')
return return
} }
try { try {
await getAccessControl() await getAccessControl()
message.success('已刷新数据') // message.success('已刷新数据')
} catch (e) { } catch (e) {
console.error('刷新数据出错:', e) console.error('刷新数据出错:', e)
message.error('刷新数据失败') // message.error('刷新数据失败')
} }
} }

View File

@@ -5,7 +5,8 @@ import dayjs from 'dayjs'
import { DeleteOutlined, ToolOutlined } from '@ant-design/icons-vue' import { DeleteOutlined, ToolOutlined } from '@ant-design/icons-vue'
// 假设API与portal页面一致 // 假设API与portal页面一致
import { fetchSiteList, fetchAlertList, deleteAlerts, markAlertsResolved } from '@/service/api/auth' import { fetchSiteList, fetchAlertList, deleteAlerts, markAlertsResolved } from '@/service/api/auth'
import { useI18n } from 'vue-i18n';
const { t } = useI18n();
const siteList = ref<{ siteId: string, name: string }[]>([]) const siteList = ref<{ siteId: string, name: string }[]>([])
const selectedSiteId = ref('') const selectedSiteId = ref('')
const siteLoading = ref(false) const siteLoading = ref(false)
@@ -63,7 +64,7 @@ const handleSiteChange = async (value: string) => {
const onResolve = async (id: string | number) => { const onResolve = async (id: string | number) => {
if (!selectedSiteId.value) { if (!selectedSiteId.value) {
message.warning('请先选择站点') message.warning(t('page.alerts.chosesite'))
return return
} }
const { error } = await markAlertsResolved(selectedSiteId.value, { const { error } = await markAlertsResolved(selectedSiteId.value, {
@@ -74,15 +75,14 @@ const onResolve = async (id: string | number) => {
}) })
if (!error) { if (!error) {
await getAlerts() await getAlerts()
message.success('已标记为已解决')
} else { } else {
message.error('标记失败') // message.error('标记失败')
} }
} }
const onDelete = async (ids: number | number[]) => { const onDelete = async (ids: number | number[]) => {
if (!selectedSiteId.value) { if (!selectedSiteId.value) {
message.warning('请先选择站点') message.warning(t('page.alerts.chosesite'))
return return
} }
@@ -97,23 +97,23 @@ const onDelete = async (ids: number | number[]) => {
}) })
if (!error) { if (!error) {
await getAlerts() await getAlerts()
message.success(`成功删除${deleteCount}条记录`) // message.success(`成功删除${deleteCount}条记录`)
} else { } else {
message.error('删除失败') message.error(t('page.alerts.deletefail'))
} }
} }
const onBatchDelete = () => { const onBatchDelete = () => {
if (selectedRowKeys.value.length === 0) { if (selectedRowKeys.value.length === 0) {
message.warning('请先选择要删除的记录') message.warning(t('page.alerts.choserecord'))
return return
} }
Modal.confirm({ Modal.confirm({
title: '确认删除', title: t('page.alerts.deletetitle'),
content: `确定要删除选中的 ${selectedRowKeys.value.length} 条记录吗?`, content: `${t('page.alerts.deletetitle')} ${selectedRowKeys.value.length} ${t('page.alerts.deletebody')}`,
okText: '确认', okText: t('page.alerts.sure'),
cancelText: '取消', cancelText: t('page.alerts.cancel'),
onOk: () => onDelete(selectedRowKeys.value) onOk: () => onDelete(selectedRowKeys.value)
}) })
} }
@@ -143,7 +143,7 @@ onMounted(async () => {
<div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px;"> <div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px;">
<div style="flex: 1; min-width: 0;"> <div style="flex: 1; min-width: 0;">
<a-tabs v-model:activeKey="activeMainTab" style="margin-bottom: 0;"> <a-tabs v-model:activeKey="activeMainTab" style="margin-bottom: 0;">
<a-tab-pane key="Alerts" tab="Alerts" /> <a-tab-pane key="Alerts" :tab="t('page.alerts.alerts')" />
</a-tabs> </a-tabs>
</div> </div>
<a-range-picker <a-range-picker
@@ -158,18 +158,18 @@ onMounted(async () => {
<div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 16px;"> <div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 16px;">
<div style="display: flex; align-items: center;"> <div style="display: flex; align-items: center;">
<a-radio-group v-model:value="statusTab" style="margin-right: 16px;"> <a-radio-group v-model:value="statusTab" style="margin-right: 16px;">
<a-radio-button value="Unresolved">Unresolved</a-radio-button> <a-radio-button value="Unresolved">{{ t('page.alerts.unresolved') }}</a-radio-button>
<a-radio-button value="Resolved">Resolved</a-radio-button> <a-radio-button value="Resolved">{{ t('page.alerts.resolved') }}</a-radio-button>
</a-radio-group> </a-radio-group>
<a-radio-group v-model:value="typeTab" style="margin-right: 16px;"> <a-radio-group v-model:value="typeTab" style="margin-right: 16px;">
<a-radio-button value="All">All</a-radio-button> <a-radio-button value="All">{{ t('page.alerts.all') }}</a-radio-button>
<a-radio-button value="System">System</a-radio-button> <a-radio-button value="System">{{ t('page.alerts.system') }}</a-radio-button>
<a-radio-button value="Device">Device</a-radio-button> <a-radio-button value="Device">{{ t('page.alerts.device') }}</a-radio-button>
</a-radio-group> </a-radio-group>
<a-select <a-select
v-model:value="selectedSiteId" v-model:value="selectedSiteId"
:loading="siteLoading" :loading="siteLoading"
placeholder="请选择站点" :placeholder="t('page.alerts.chosesite')"
style="width: 200px; margin-right: 16px;" style="width: 200px; margin-right: 16px;"
@change="handleSiteChange" @change="handleSiteChange"
> >
@@ -179,7 +179,7 @@ onMounted(async () => {
</a-select> </a-select>
</div> </div>
<div> <div>
<a-button @click="onBatchDelete">Batch Delete</a-button> <a-button @click="onBatchDelete">{{ t('page.alerts.batchdelete') }}</a-button>
</div> </div>
</div> </div>
@@ -192,8 +192,8 @@ onMounted(async () => {
bordered bordered
size="middle" size="middle"
> >
<a-table-column title="TYPE" dataIndex="key" key="key" /> <a-table-column :title="t('page.alerts.type')" dataIndex="key" key="key" />
<a-table-column title="LEVEL" dataIndex="level" key="level"> <a-table-column :title="t('page.alerts.level')" dataIndex="level" key="level">
<template #default="{ record }"> <template #default="{ record }">
<span> <span>
<span :style="{ <span :style="{
@@ -207,17 +207,17 @@ onMounted(async () => {
</span> </span>
</template> </template>
</a-table-column> </a-table-column>
<a-table-column title="CONTENT" dataIndex="content" key="content" /> <a-table-column :title="t('page.alerts.content')" dataIndex="content" key="content" />
<a-table-column title="TIME" dataIndex="time" key="time"> <a-table-column :title="t('page.alerts.time')" dataIndex="time" key="time">
<template #default="{ record }"> <template #default="{ record }">
<span>{{ record.time ? dayjs(record.time).format('YYYY-MM-DD HH:mm:ss') : '' }}</span> <span>{{ record.time ? dayjs(record.time).format('YYYY-MM-DD HH:mm:ss') : '' }}</span>
</template> </template>
</a-table-column> </a-table-column>
<a-table-column title="ACTION" key="action" width="120"> <a-table-column :title="t('page.alerts.action')" key="action" width="120">
<template #default="{ record }"> <template #default="{ record }">
<div style="display: flex; gap: 8px; justify-content: center;"> <div style="display: flex; gap: 8px; justify-content: center;">
<!-- 未解决状态显示解决按钮 --> <!-- 未解决状态显示解决按钮 -->
<a-tooltip v-if="statusTab === 'Unresolved'" title="标记为已解决"> <a-tooltip v-if="statusTab === 'Unresolved'" :title="t('page.alerts.resovetitle')">
<a-button <a-button
type="link" type="link"
size="small" size="small"
@@ -230,17 +230,17 @@ onMounted(async () => {
</a-button> </a-button>
</a-tooltip> </a-tooltip>
<!-- 所有状态都显示删除按钮 --> <!-- 所有状态都显示删除按钮 -->
<a-tooltip title="删除"> <a-tooltip :title="t('page.alerts.delete')">
<a-button <a-button
type="link" type="link"
size="small" size="small"
style="color: #ff4d4f" style="color: #ff4d4f"
@click="() => { @click="() => {
Modal.confirm({ Modal.confirm({
title: '确认删除', title: t('page.alerts.deletetitle'),
content: '确定要删除该条告警记录吗?', content: t('page.alerts.suredelete'),
okText: '确认', okText: t('page.alerts.sure'),
cancelText: '取消', cancelText: t('page.alerts.cancel'),
onOk: () => onDelete(record.id) onOk: () => onDelete(record.id)
}) })
}" }"