yl-frontend/src/views/SecurityView.vue

567 lines
14 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="security-page">
<el-row :gutter="20">
<!-- 安全状态概览 -->
<el-col :span="16">
<el-card class="security-overview">
<template #header>
<div class="card-header">
<span>安全状态</span>
<el-button-group>
<el-button type="primary" @click="refreshStatus">
<el-icon><Refresh /></el-icon>
</el-button>
<el-button type="success" @click="startScan">
<el-icon><Monitor /></el-icon>
</el-button>
</el-button-group>
</div>
</template>
<div class="security-grid">
<div
v-for="item in securityStatus"
:key="item.id"
class="security-item"
>
<el-icon :size="40" :color="item.color">
<component :is="item.icon" />
</el-icon>
<div class="item-info">
<h3>{{ item.name }}</h3>
<p>{{ item.description }}</p>
<el-tag :type="item.status === 'safe' ? 'success' : 'danger'">
{{ item.status === 'safe' ? '安全' : '风险' }}
</el-tag>
</div>
</div>
</div>
</el-card>
<!-- 安全日志 -->
<el-card class="security-logs mt-20">
<template #header>
<div class="card-header">
<span>安全日志</span>
<el-button type="primary" text @click="exportLogs">
导出日志
</el-button>
</div>
</template>
<el-table :data="securityLogs" style="width: 100%">
<el-table-column prop="time" label="时间" width="180" />
<el-table-column prop="type" label="类型" width="120">
<template #default="{ row }">
<el-tag :type="getLogType(row.type)">{{ row.type }}</el-tag>
</template>
</el-table-column>
<el-table-column prop="description" label="描述" />
<el-table-column prop="ip" label="IP地址" width="140" />
<el-table-column prop="status" label="状态" width="100">
<template #default="{ row }">
<el-tag :type="row.status === 'success' ? 'success' : 'danger'">
{{ row.status === 'success' ? '成功' : '失败' }}
</el-tag>
</template>
</el-table-column>
</el-table>
</el-card>
</el-col>
<!-- 安全设置 -->
<el-col :span="8">
<!-- 身份认证 -->
<el-card class="auth-settings">
<template #header>
<div class="card-header">
<span>身份认证</span>
<el-switch
v-model="securitySettings.twoFactorAuth"
active-text="双因素认证"
@change="toggleTwoFactor"
/>
</div>
</template>
<div class="settings-list">
<div class="setting-item">
<span>登录密码</span>
<el-button link type="primary" @click="changePassword">
修改密码
</el-button>
</div>
<div class="setting-item">
<span>安全手机</span>
<el-tag type="success" v-if="securitySettings.phone">
{{ maskPhone(securitySettings.phone) }}
</el-tag>
<el-button v-else link type="warning" @click="bindPhone">
绑定手机
</el-button>
</div>
<div class="setting-item">
<span>人脸识别</span>
<el-button
link
:type="securitySettings.faceId ? 'success' : 'primary'"
@click="manageFaceId"
>
{{ securitySettings.faceId ? '已开启' : '去开启' }}
</el-button>
</div>
</div>
</el-card>
<!-- 隐私设置 -->
<el-card class="privacy-settings mt-20">
<template #header>
<div class="card-header">
<span>隐私设置</span>
</div>
</template>
<el-form :model="privacySettings" label-width="120px">
<el-form-item label="个人信息可见">
<el-select v-model="privacySettings.infoVisibility">
<el-option label="所有人可见" value="public" />
<el-option label="仅家人可见" value="family" />
<el-option label="仅自己可见" value="private" />
</el-select>
</el-form-item>
<el-form-item label="位置信息">
<el-switch v-model="privacySettings.locationSharing" />
</el-form-item>
<el-form-item label="健康数据">
<el-switch v-model="privacySettings.healthDataSharing" />
</el-form-item>
<el-form-item label="活动记录">
<el-switch v-model="privacySettings.activitySharing" />
</el-form-item>
</el-form>
</el-card>
<!-- 应急处理 -->
<el-card class="emergency-settings mt-20">
<template #header>
<div class="card-header">
<span>应急处理</span>
<el-button type="danger" @click="showEmergencyPanel">
应急面板
</el-button>
</div>
</template>
<div class="emergency-contacts">
<h4>紧急联系人</h4>
<div
v-for="contact in emergencyContacts"
:key="contact.id"
class="contact-item"
>
<div class="contact-info">
<span>{{ contact.name }}</span>
<span>{{ contact.phone }}</span>
</div>
<el-button type="danger" circle @click="callEmergency(contact)">
<el-icon><Phone /></el-icon>
</el-button>
</div>
</div>
</el-card>
</el-col>
</el-row>
<!-- 修改密码对话框 -->
<el-dialog v-model="passwordDialogVisible" title="修改密码" width="30%">
<el-form :model="passwordForm" label-width="100px">
<el-form-item label="当前密码">
<el-input v-model="passwordForm.oldPassword" type="password" show-password />
</el-form-item>
<el-form-item label="新密码">
<el-input v-model="passwordForm.newPassword" type="password" show-password />
</el-form-item>
<el-form-item label="确认新密码">
<el-input v-model="passwordForm.confirmPassword" type="password" show-password />
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="passwordDialogVisible = false">取消</el-button>
<el-button type="primary" @click="submitPasswordChange">确定</el-button>
</span>
</template>
</el-dialog>
<!-- 应急面板对话框 -->
<el-dialog v-model="emergencyDialogVisible" title="应急处理面板" width="50%">
<div class="emergency-panel">
<div class="emergency-actions">
<el-button type="danger" size="large" @click="lockAccount">
<el-icon><Lock /></el-icon>
紧急锁定账号
</el-button>
<el-button type="warning" size="large" @click="resetDevice">
<el-icon><Delete /></el-icon>
远程重置设备
</el-button>
<el-button type="info" size="large" @click="backupData">
<el-icon><Download /></el-icon>
紧急数据备份
</el-button>
</div>
<el-alert
title="注意:应急操作不可撤销,请谨慎操作!"
type="warning"
:closable="false"
show-icon
/>
</div>
</el-dialog>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import {
Refresh,
Monitor,
Lock,
Unlock,
Phone,
Delete,
Download,
Warning,
Key,
Message,
Connection
} from '@element-plus/icons-vue'
import { ElMessage, ElMessageBox } from 'element-plus'
// 安全状态
const securityStatus = ref([
{
id: 1,
name: '账号安全',
description: '密码强度良好双因素认证已开启',
icon: 'Lock',
color: '#67C23A',
status: 'safe'
},
{
id: 2,
name: '设备安全',
description: '当前设备已认证无异常登录',
icon: 'Monitor',
color: '#409EFF',
status: 'safe'
},
{
id: 3,
name: '数据安全',
description: '数据加密存储已开启自动备份',
icon: 'Connection',
color: '#E6A23C',
status: 'safe'
},
{
id: 4,
name: '支付安全',
description: '支付密码已设置交易验证正常',
icon: 'Key',
color: '#F56C6C',
status: 'safe'
}
])
// 安全日志
const securityLogs = ref([
{
time: '2024-03-20 10:30:00',
type: '登录',
description: '账号登录成功',
ip: '192.168.1.100',
status: 'success'
},
{
time: '2024-03-20 09:15:00',
type: '修改密码',
description: '密码修改成功',
ip: '192.168.1.100',
status: 'success'
},
{
time: '2024-03-19 15:20:00',
type: '异常登录',
description: '检测到异地登录尝试',
ip: '10.0.0.1',
status: 'failed'
}
])
// 安全设置
const securitySettings = ref({
twoFactorAuth: true,
phone: '13800138000',
faceId: true
})
// 隐私设置
const privacySettings = ref({
infoVisibility: 'family',
locationSharing: true,
healthDataSharing: false,
activitySharing: true
})
// 紧急联系人
const emergencyContacts = ref([
{ id: 1, name: '张小明', phone: '13800138001' },
{ id: 2, name: '李医生', phone: '13900139000' }
])
// 对话框控制
const passwordDialogVisible = ref(false)
const emergencyDialogVisible = ref(false)
const passwordForm = ref({
oldPassword: '',
newPassword: '',
confirmPassword: ''
})
// 方法
const refreshStatus = () => {
ElMessage.success('安全状态已更新')
}
const startScan = () => {
ElMessage.info('正在进行安全扫描...')
}
const exportLogs = () => {
ElMessage.success('日志导出成功')
}
const getLogType = (type: string) => {
const types: Record<string, string> = {
'登录': 'primary',
'修改密码': 'success',
'异常登录': 'danger'
}
return types[type] || 'info'
}
const toggleTwoFactor = (value: boolean) => {
if (value) {
ElMessage.success('双因素认证已开启')
} else {
ElMessage.warning('双因素认证已关闭')
}
}
const changePassword = () => {
passwordDialogVisible.value = true
}
const submitPasswordChange = () => {
if (passwordForm.value.newPassword !== passwordForm.value.confirmPassword) {
ElMessage.error('两次输入的密码不一致')
return
}
ElMessage.success('密码修改成功')
passwordDialogVisible.value = false
passwordForm.value = {
oldPassword: '',
newPassword: '',
confirmPassword: ''
}
}
const bindPhone = () => {
ElMessage.info('请完成手机号绑定')
}
const manageFaceId = () => {
if (securitySettings.value.faceId) {
ElMessage.info('人脸识别已开启')
} else {
ElMessage.info('请完成人脸识别设置')
}
}
const maskPhone = (phone: string) => {
return phone.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2')
}
const showEmergencyPanel = () => {
emergencyDialogVisible.value = true
}
const callEmergency = (contact: any) => {
ElMessageBox.confirm(
`确定要拨打紧急联系人 ${contact.name} 的电话吗?`,
'提示',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}
)
}
const lockAccount = () => {
ElMessageBox.confirm(
'确定要锁定账号吗?此操作将导致所有设备退出登录。',
'警告',
{
confirmButtonText: '确定锁定',
cancelButtonText: '取消',
type: 'warning'
}
).then(() => {
ElMessage.success('账号已锁定')
emergencyDialogVisible.value = false
})
}
const resetDevice = () => {
ElMessageBox.confirm(
'确定要远程重置设备吗?此操作将清除设备上的所有数据。',
'警告',
{
confirmButtonText: '确定重置',
cancelButtonText: '取消',
type: 'warning'
}
).then(() => {
ElMessage.success('设备重置指令已发送')
emergencyDialogVisible.value = false
})
}
const backupData = () => {
ElMessage.success('正在执行紧急数据备份...')
emergencyDialogVisible.value = false
}
</script>
<style scoped>
.security-page {
padding: 20px;
}
.mt-20 {
margin-top: 20px;
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
}
/* 安全状态网格 */
.security-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 20px;
}
.security-item {
display: flex;
align-items: flex-start;
padding: 20px;
background: #f5f7fa;
border-radius: 8px;
transition: all 0.3s;
}
.security-item:hover {
transform: translateY(-2px);
box-shadow: 0 2px 12px 0 rgba(0,0,0,0.1);
}
.item-info {
margin-left: 15px;
flex: 1;
}
.item-info h3 {
margin: 0 0 10px 0;
font-size: 18px;
}
.item-info p {
margin: 0 0 10px 0;
color: #666;
font-size: 14px;
}
/* 设置列表样式 */
.settings-list {
display: flex;
flex-direction: column;
gap: 15px;
}
.setting-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px 0;
border-bottom: 1px solid #eee;
}
/* 紧急联系人样式 */
.emergency-contacts h4 {
margin: 0 0 15px 0;
color: #606266;
}
.contact-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px;
background: #f5f7fa;
border-radius: 4px;
margin-bottom: 10px;
}
.contact-info {
display: flex;
flex-direction: column;
gap: 5px;
}
.contact-info span:last-child {
color: #666;
font-size: 14px;
}
/* 应急面板样式 */
.emergency-panel {
padding: 20px;
}
.emergency-actions {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20px;
margin-bottom: 20px;
}
.emergency-actions .el-button {
height: 100px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 10px;
}
.emergency-actions .el-icon {
font-size: 24px;
}
.dialog-footer {
display: flex;
justify-content: flex-end;
gap: 10px;
}
</style>