A:design
This commit is contained in:
1 parent
1491757e7f
commit
74962ebd8b
2 files changed
-2316
No files matched your search
@@ -1,1083 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>防火墙管理面板</title>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
:root {
|
||||
--bg-primary: #0a0f1c;
|
||||
--bg-secondary: #131a2a;
|
||||
--bg-card: rgba(20, 27, 45, 0.8);
|
||||
--text-primary: #e4e8f1;
|
||||
--text-secondary: #8b92a8;
|
||||
--accent: #3b82f6;
|
||||
--accent-hover: #2563eb;
|
||||
--success: #10b981;
|
||||
--danger: #ef4444;
|
||||
--warning: #f59e0b;
|
||||
--border: rgba(255, 255, 255, 0.1);
|
||||
--glass: rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
||||
background: var(--bg-primary);
|
||||
color: var(--text-primary);
|
||||
line-height: 1.6;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
/* 背景动画 */
|
||||
body::before {
|
||||
content: '';
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: radial-gradient(circle at 20% 50%, rgba(59, 130, 246, 0.1) 0%, transparent 50%),
|
||||
radial-gradient(circle at 80% 80%, rgba(16, 185, 129, 0.1) 0%, transparent 50%);
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
/* 主容器 */
|
||||
.container {
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
/* 头部 */
|
||||
.header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 30px;
|
||||
padding: 20px 0;
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
font-size: 2.5rem;
|
||||
font-weight: 700;
|
||||
background: linear-gradient(135deg, var(--accent) 0%, var(--success) 100%);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
}
|
||||
|
||||
/* 统计卡片 */
|
||||
.stats-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||
gap: 20px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
background: var(--bg-card);
|
||||
backdrop-filter: blur(10px);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 16px;
|
||||
padding: 24px;
|
||||
transition: all 0.3s ease;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.stat-card::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 4px;
|
||||
background: linear-gradient(90deg, var(--accent) 0%, var(--success) 100%);
|
||||
}
|
||||
|
||||
.stat-card:hover {
|
||||
transform: translateY(-4px);
|
||||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
font-size: 2.5rem;
|
||||
font-weight: 700;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
color: var(--text-secondary);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
/* 过滤和操作栏 */
|
||||
.action-bar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
flex-wrap: wrap;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.filter-group {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.btn {
|
||||
padding: 10px 20px;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
font-size: 0.9rem;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: var(--accent);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background: var(--accent-hover);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 5px 15px rgba(59, 130, 246, 0.3);
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background: var(--glass);
|
||||
color: var(--text-primary);
|
||||
border: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.btn-danger {
|
||||
background: var(--danger);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.search-box {
|
||||
position: relative;
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
.search-box input {
|
||||
width: 100%;
|
||||
padding: 10px 40px 10px 15px;
|
||||
background: var(--glass);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 0.9rem;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.search-box input:focus {
|
||||
outline: none;
|
||||
border-color: var(--accent);
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
/* 规则表格 */
|
||||
.rules-table {
|
||||
background: var(--bg-card);
|
||||
backdrop-filter: blur(10px);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 16px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
th {
|
||||
background: var(--bg-secondary);
|
||||
padding: 15px;
|
||||
text-align: left;
|
||||
font-weight: 600;
|
||||
font-size: 0.9rem;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
td {
|
||||
padding: 15px;
|
||||
border-top: 1px solid var(--border);
|
||||
}
|
||||
|
||||
tr {
|
||||
transition: all 0.3s ease;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
tr:hover {
|
||||
background: rgba(255, 255, 255, 0.03);
|
||||
}
|
||||
|
||||
.status-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 4px 12px;
|
||||
border-radius: 20px;
|
||||
font-size: 0.85rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.status-enabled {
|
||||
background: rgba(16, 185, 129, 0.2);
|
||||
color: var(--success);
|
||||
}
|
||||
|
||||
.status-disabled {
|
||||
background: rgba(239, 68, 68, 0.2);
|
||||
color: var(--danger);
|
||||
}
|
||||
|
||||
.direction-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
padding: 4px 10px;
|
||||
border-radius: 6px;
|
||||
font-size: 0.85rem;
|
||||
background: var(--glass);
|
||||
border: 1px solid var(--border);
|
||||
}
|
||||
|
||||
/* 模态框 */
|
||||
.modal {
|
||||
display: none;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
backdrop-filter: blur(5px);
|
||||
z-index: 1000;
|
||||
animation: fadeIn 0.3s ease;
|
||||
}
|
||||
|
||||
.modal.active {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
background: var(--bg-secondary);
|
||||
border-radius: 20px;
|
||||
width: 90%;
|
||||
max-width: 800px;
|
||||
max-height: 90vh;
|
||||
overflow-y: auto;
|
||||
animation: slideUp 0.3s ease;
|
||||
border: 1px solid var(--border);
|
||||
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.modal-header {
|
||||
padding: 30px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.modal-header h2 {
|
||||
font-size: 1.8rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.close-btn {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 50%;
|
||||
background: var(--glass);
|
||||
border: 1px solid var(--border);
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.close-btn:hover {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
padding: 30px;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.form-group label {
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
font-weight: 500;
|
||||
color: var(--text-secondary);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.form-control {
|
||||
width: 100%;
|
||||
padding: 12px 15px;
|
||||
background: var(--glass);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 0.95rem;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.form-control:focus {
|
||||
outline: none;
|
||||
border-color: var(--accent);
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
}
|
||||
|
||||
select.form-control {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.form-row {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.tabs {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
margin-bottom: 30px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.tab {
|
||||
padding: 12px 24px;
|
||||
background: none;
|
||||
border: none;
|
||||
color: var(--text-secondary);
|
||||
cursor: pointer;
|
||||
font-weight: 500;
|
||||
position: relative;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.tab.active {
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.tab.active::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: -1px;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 2px;
|
||||
background: var(--accent);
|
||||
}
|
||||
|
||||
.tab-content {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.tab-content.active {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.checkbox-group {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.checkbox-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.checkbox-item input[type="checkbox"] {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.modal-footer {
|
||||
padding: 20px 30px;
|
||||
border-top: 1px solid var(--border);
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
/* 动画 */
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
|
||||
@keyframes slideUp {
|
||||
from { transform: translateY(50px); opacity: 0; }
|
||||
to { transform: translateY(0); opacity: 1; }
|
||||
}
|
||||
|
||||
/* 服务选择器 */
|
||||
.service-selector {
|
||||
max-height: 200px;
|
||||
overflow-y: auto;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 8px;
|
||||
padding: 10px;
|
||||
background: var(--glass);
|
||||
}
|
||||
|
||||
.service-item {
|
||||
padding: 8px;
|
||||
margin: 2px 0;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.service-item:hover {
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
.service-item input {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
/* 响应式 */
|
||||
@media (max-width: 768px) {
|
||||
.header h1 {
|
||||
font-size: 1.8rem;
|
||||
}
|
||||
|
||||
.action-bar {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.search-box {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.form-row {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
width: 95%;
|
||||
max-height: 95vh;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<!-- 头部 -->
|
||||
<div class="header">
|
||||
<h1>防火墙管理中心</h1>
|
||||
<div class="header-actions">
|
||||
<button class="btn btn-secondary" onclick="exportRules()">
|
||||
<span>📤</span> 导出规则
|
||||
</button>
|
||||
<button class="btn btn-secondary" onclick="importRules()">
|
||||
<span>📥</span> 导入规则
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 统计卡片 -->
|
||||
<div class="stats-grid">
|
||||
<div class="stat-card">
|
||||
<div class="stat-label">总规则数</div>
|
||||
<div class="stat-value" id="total-rules">42</div>
|
||||
<div class="stat-label">入站: 24 | 出站: 18</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-label">已启用规则</div>
|
||||
<div class="stat-value" style="color: var(--success);">35</div>
|
||||
<div class="stat-label">占比 83.3%</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-label">已禁用规则</div>
|
||||
<div class="stat-value" style="color: var(--danger);">7</div>
|
||||
<div class="stat-label">占比 16.7%</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-label">安全等级</div>
|
||||
<div class="stat-value" style="color: var(--success);">高</div>
|
||||
<div class="stat-label">系统防护良好</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 过滤和操作栏 -->
|
||||
<div class="action-bar">
|
||||
<div class="filter-group">
|
||||
<button class="btn btn-secondary" onclick="filterRules('all')">全部</button>
|
||||
<button class="btn btn-secondary" onclick="filterRules('inbound')">入站</button>
|
||||
<button class="btn btn-secondary" onclick="filterRules('outbound')">出站</button>
|
||||
<button class="btn btn-secondary" onclick="filterRules('enabled')">已启用</button>
|
||||
<button class="btn btn-secondary" onclick="filterRules('disabled')">已禁用</button>
|
||||
</div>
|
||||
<div class="search-box">
|
||||
<input type="text" placeholder="搜索规则名称、程序、端口..." onkeyup="searchRules(this.value)">
|
||||
</div>
|
||||
<button class="btn btn-primary" onclick="openAddModal()">
|
||||
<span>➕</span> 新建规则
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- 规则表格 -->
|
||||
<div class="rules-table">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>状态</th>
|
||||
<th>名称</th>
|
||||
<th>方向</th>
|
||||
<th>程序</th>
|
||||
<th>协议</th>
|
||||
<th>端口</th>
|
||||
<th>远程地址</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="rules-tbody">
|
||||
<!-- 动态生成的规则行 -->
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 添加/编辑规则模态框 -->
|
||||
<div class="modal" id="rule-modal">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h2 id="modal-title">新建防火墙规则</h2>
|
||||
<button class="close-btn" onclick="closeModal()">✕</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<!-- 标签页 -->
|
||||
<div class="tabs">
|
||||
<button class="tab active" onclick="switchTab('general')">常规</button>
|
||||
<button class="tab" onclick="switchTab('program')">程序和服务</button>
|
||||
<button class="tab" onclick="switchTab('protocol')">协议和端口</button>
|
||||
<button class="tab" onclick="switchTab('scope')">作用域</button>
|
||||
<button class="tab" onclick="switchTab('advanced')">高级</button>
|
||||
</div>
|
||||
|
||||
<!-- 常规标签页 -->
|
||||
<div class="tab-content active" id="general-tab">
|
||||
<div class="form-group">
|
||||
<label>规则名称 *</label>
|
||||
<input type="text" class="form-control" id="rule-name" placeholder="输入规则名称">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>描述</label>
|
||||
<textarea class="form-control" id="rule-description" rows="3" placeholder="输入规则描述"></textarea>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label>方向 *</label>
|
||||
<select class="form-control" id="rule-direction">
|
||||
<option value="inbound">入站</option>
|
||||
<option value="outbound">出站</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>操作 *</label>
|
||||
<select class="form-control" id="rule-action">
|
||||
<option value="allow">允许连接</option>
|
||||
<option value="block">阻止连接</option>
|
||||
<option value="allow-secure">允许安全连接</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>
|
||||
<input type="checkbox" id="rule-enabled" checked>
|
||||
启用此规则
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 程序和服务标签页 -->
|
||||
<div class="tab-content" id="program-tab">
|
||||
<div class="form-group">
|
||||
<label>程序路径</label>
|
||||
<input type="text" class="form-control" id="program-path" placeholder="例如: C:\Program Files\App\app.exe">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>应用于以下服务</label>
|
||||
<div class="service-selector">
|
||||
<div class="service-item">
|
||||
<label><input type="checkbox" value="BITS"> 后台智能传输服务 (BITS)</label>
|
||||
</div>
|
||||
<div class="service-item">
|
||||
<label><input type="checkbox" value="Dnscache"> DNS 客户端 (Dnscache)</label>
|
||||
</div>
|
||||
<div class="service-item">
|
||||
<label><input type="checkbox" value="Dhcp"> DHCP 客户端 (Dhcp)</label>
|
||||
</div>
|
||||
<div class="service-item">
|
||||
<label><input type="checkbox" value="EventLog"> Windows 事件日志 (EventLog)</label>
|
||||
</div>
|
||||
<div class="service-item">
|
||||
<label><input type="checkbox" value="MpsSvc"> Windows 防火墙 (MpsSvc)</label>
|
||||
</div>
|
||||
<div class="service-item">
|
||||
<label><input type="checkbox" value="RpcSs"> 远程过程调用 (RPC)</label>
|
||||
</div>
|
||||
<div class="service-item">
|
||||
<label><input type="checkbox" value="Spooler"> 打印后台处理程序</label>
|
||||
</div>
|
||||
<div class="service-item">
|
||||
<label><input type="checkbox" value="W32Time"> Windows 时间</label>
|
||||
</div>
|
||||
<div class="service-item">
|
||||
<label><input type="checkbox" value="WinRM"> Windows 远程管理</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 协议和端口标签页 -->
|
||||
<div class="tab-content" id="protocol-tab">
|
||||
<div class="form-group">
|
||||
<label>协议类型</label>
|
||||
<select class="form-control" id="protocol-type" onchange="togglePortFields()">
|
||||
<option value="any">任意</option>
|
||||
<option value="tcp">TCP</option>
|
||||
<option value="udp">UDP</option>
|
||||
<option value="icmpv4">ICMPv4</option>
|
||||
<option value="icmpv6">ICMPv6</option>
|
||||
<option value="custom">自定义</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group" id="custom-protocol-group" style="display: none;">
|
||||
<label>协议号</label>
|
||||
<input type="number" class="form-control" id="protocol-number" min="0" max="255" placeholder="0-255">
|
||||
</div>
|
||||
<div class="form-row" id="port-fields">
|
||||
<div class="form-group">
|
||||
<label>本地端口</label>
|
||||
<input type="text" class="form-control" id="local-ports" placeholder="例如: 80, 443, 1000-2000">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>远程端口</label>
|
||||
<input type="text" class="form-control" id="remote-ports" placeholder="例如: 80, 443, 1000-2000">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 作用域标签页 -->
|
||||
<div class="tab-content" id="scope-tab">
|
||||
<div class="form-group">
|
||||
<label>本地 IP 地址</label>
|
||||
<div class="checkbox-group">
|
||||
<div class="checkbox-item">
|
||||
<input type="radio" name="local-ip" value="any" checked>
|
||||
<label>任何 IP 地址</label>
|
||||
</div>
|
||||
<div class="checkbox-item">
|
||||
<input type="radio" name="local-ip" value="custom">
|
||||
<label>指定 IP 地址</label>
|
||||
</div>
|
||||
</div>
|
||||
<input type="text" class="form-control" id="local-ip-list" placeholder="例如: 192.168.1.0/24, 10.0.0.1" style="margin-top: 10px; display: none;">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>远程 IP 地址</label>
|
||||
<div class="checkbox-group">
|
||||
<div class="checkbox-item">
|
||||
<input type="radio" name="remote-ip" value="any" checked>
|
||||
<label>任何 IP 地址</label>
|
||||
</div>
|
||||
<div class="checkbox-item">
|
||||
<input type="radio" name="remote-ip" value="custom">
|
||||
<label>指定 IP 地址</label>
|
||||
</div>
|
||||
</div>
|
||||
<input type="text" class="form-control" id="remote-ip-list" placeholder="例如: 192.168.1.0/24, 10.0.0.1" style="margin-top: 10px; display: none;">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 高级标签页 -->
|
||||
<div class="tab-content" id="advanced-tab">
|
||||
<div class="form-group">
|
||||
<label>配置文件</label>
|
||||
<div class="checkbox-group">
|
||||
<div class="checkbox-item">
|
||||
<input type="checkbox" id="profile-domain" checked>
|
||||
<label>域</label>
|
||||
</div>
|
||||
<div class="checkbox-item">
|
||||
<input type="checkbox" id="profile-private" checked>
|
||||
<label>专用</label>
|
||||
</div>
|
||||
<div class="checkbox-item">
|
||||
<input type="checkbox" id="profile-public" checked>
|
||||
<label>公用</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>用户和组</label>
|
||||
<input type="text" class="form-control" id="users-groups" placeholder="例如: DOMAIN\User, Everyone">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>边缘遍历</label>
|
||||
<select class="form-control" id="edge-traversal">
|
||||
<option value="block">阻止边缘遍历</option>
|
||||
<option value="allow">允许边缘遍历</option>
|
||||
<option value="defer-to-user">延迟到用户</option>
|
||||
<option value="defer-to-app">延迟到应用程序</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-secondary" onclick="closeModal()">取消</button>
|
||||
<button class="btn btn-primary" onclick="saveRule()">保存规则</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// 模拟规则数据
|
||||
let rules = [
|
||||
{
|
||||
id: 1,
|
||||
name: "允许 Chrome 浏览器",
|
||||
enabled: true,
|
||||
direction: "outbound",
|
||||
program: "C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe",
|
||||
protocol: "TCP",
|
||||
localPort: "任意",
|
||||
remotePort: "80, 443",
|
||||
remoteAddress: "任意",
|
||||
action: "allow",
|
||||
description: "允许 Chrome 浏览器访问网络"
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: "远程桌面 - 用户模式 (TCP-In)",
|
||||
enabled: true,
|
||||
direction: "inbound",
|
||||
program: "系统",
|
||||
protocol: "TCP",
|
||||
localPort: "3389",
|
||||
remotePort: "任意",
|
||||
remoteAddress: "任意",
|
||||
action: "allow",
|
||||
description: "允许远程桌面连接"
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: "阻止 SMB 445 端口",
|
||||
enabled: true,
|
||||
direction: "inbound",
|
||||
program: "任意",
|
||||
protocol: "TCP",
|
||||
localPort: "445",
|
||||
remotePort: "任意",
|
||||
remoteAddress: "任意",
|
||||
action: "block",
|
||||
description: "阻止 SMB 文件共享端口"
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: "Windows 更新服务",
|
||||
enabled: true,
|
||||
direction: "outbound",
|
||||
program: "C:\\Windows\\System32\\svchost.exe",
|
||||
protocol: "TCP/UDP",
|
||||
localPort: "任意",
|
||||
remotePort: "80, 443",
|
||||
remoteAddress: "任意",
|
||||
action: "allow",
|
||||
description: "允许 Windows 更新服务"
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
name: "SSH 服务器",
|
||||
enabled: false,
|
||||
direction: "inbound",
|
||||
program: "C:\\Program Files\\OpenSSH\\sshd.exe",
|
||||
protocol: "TCP",
|
||||
localPort: "22",
|
||||
remotePort: "任意",
|
||||
remoteAddress: "192.168.1.0/24",
|
||||
action: "allow",
|
||||
description: "SSH 服务器访问规则"
|
||||
}
|
||||
];
|
||||
|
||||
let currentEditId = null;
|
||||
|
||||
// 渲染规则表格
|
||||
function renderRules() {
|
||||
const tbody = document.getElementById('rules-tbody');
|
||||
tbody.innerHTML = '';
|
||||
|
||||
rules.forEach(rule => {
|
||||
const tr = document.createElement('tr');
|
||||
tr.onclick = () => editRule(rule.id);
|
||||
tr.innerHTML = `
|
||||
<td>
|
||||
<span class="status-badge ${rule.enabled ? 'status-enabled' : 'status-disabled'}">
|
||||
<span style="font-size: 0.8rem;">${rule.enabled ? '●' : '○'}</span>
|
||||
${rule.enabled ? '启用' : '禁用'}
|
||||
</span>
|
||||
</td>
|
||||
<td>${rule.name}</td>
|
||||
<td>
|
||||
<span class="direction-badge">
|
||||
${rule.direction === 'inbound' ? '⬅️ 入站' : '➡️ 出站'}
|
||||
</span>
|
||||
</td>
|
||||
<td>${rule.program.length > 30 ? '...' + rule.program.substr(-30) : rule.program}</td>
|
||||
<td>${rule.protocol}</td>
|
||||
<td>${rule.localPort}</td>
|
||||
<td>${rule.remoteAddress}</td>
|
||||
<td>
|
||||
<span style="color: ${rule.action === 'allow' ? 'var(--success)' : 'var(--danger)'}">
|
||||
${rule.action === 'allow' ? '✓ 允许' : '✗ 阻止'}
|
||||
</span>
|
||||
</td>
|
||||
`;
|
||||
tbody.appendChild(tr);
|
||||
});
|
||||
}
|
||||
|
||||
// 打开添加模态框
|
||||
function openAddModal() {
|
||||
currentEditId = null;
|
||||
document.getElementById('modal-title').textContent = '新建防火墙规则';
|
||||
document.getElementById('rule-modal').classList.add('active');
|
||||
resetForm();
|
||||
}
|
||||
|
||||
// 编辑规则
|
||||
function editRule(id) {
|
||||
currentEditId = id;
|
||||
const rule = rules.find(r => r.id === id);
|
||||
if (!rule) return;
|
||||
|
||||
document.getElementById('modal-title').textContent = '编辑防火墙规则';
|
||||
document.getElementById('rule-modal').classList.add('active');
|
||||
|
||||
// 填充表单
|
||||
document.getElementById('rule-name').value = rule.name;
|
||||
document.getElementById('rule-description').value = rule.description || '';
|
||||
document.getElementById('rule-direction').value = rule.direction;
|
||||
document.getElementById('rule-action').value = rule.action;
|
||||
document.getElementById('rule-enabled').checked = rule.enabled;
|
||||
document.getElementById('program-path').value = rule.program === '任意' ? '' : rule.program;
|
||||
|
||||
// 这里可以继续填充其他字段...
|
||||
}
|
||||
|
||||
// 关闭模态框
|
||||
function closeModal() {
|
||||
document.getElementById('rule-modal').classList.remove('active');
|
||||
}
|
||||
|
||||
// 切换标签页
|
||||
function switchTab(tabName) {
|
||||
// 更新标签按钮状态
|
||||
document.querySelectorAll('.tab').forEach(tab => {
|
||||
tab.classList.remove('active');
|
||||
});
|
||||
event.target.classList.add('active');
|
||||
|
||||
// 显示对应内容
|
||||
document.querySelectorAll('.tab-content').forEach(content => {
|
||||
content.classList.remove('active');
|
||||
});
|
||||
document.getElementById(`${tabName}-tab`).classList.add('active');
|
||||
}
|
||||
|
||||
// 切换端口字段显示
|
||||
function togglePortFields() {
|
||||
const protocol = document.getElementById('protocol-type').value;
|
||||
const portFields = document.getElementById('port-fields');
|
||||
const customProtocolGroup = document.getElementById('custom-protocol-group');
|
||||
|
||||
if (protocol === 'tcp' || protocol === 'udp') {
|
||||
portFields.style.display = 'grid';
|
||||
customProtocolGroup.style.display = 'none';
|
||||
} else if (protocol === 'custom') {
|
||||
portFields.style.display = 'none';
|
||||
customProtocolGroup.style.display = 'block';
|
||||
} else {
|
||||
portFields.style.display = 'none';
|
||||
customProtocolGroup.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
// 保存规则
|
||||
function saveRule() {
|
||||
const name = document.getElementById('rule-name').value.trim();
|
||||
if (!name) {
|
||||
alert('请输入规则名称');
|
||||
return;
|
||||
}
|
||||
|
||||
const rule = {
|
||||
id: currentEditId || Date.now(),
|
||||
name: name,
|
||||
enabled: document.getElementById('rule-enabled').checked,
|
||||
direction: document.getElementById('rule-direction').value,
|
||||
program: document.getElementById('program-path').value || '任意',
|
||||
protocol: document.getElementById('protocol-type').value.toUpperCase(),
|
||||
localPort: document.getElementById('local-ports').value || '任意',
|
||||
remotePort: document.getElementById('remote-ports').value || '任意',
|
||||
remoteAddress: '任意', // 简化处理
|
||||
action: document.getElementById('rule-action').value,
|
||||
description: document.getElementById('rule-description').value
|
||||
};
|
||||
|
||||
if (currentEditId) {
|
||||
// 更新现有规则
|
||||
const index = rules.findIndex(r => r.id === currentEditId);
|
||||
if (index !== -1) {
|
||||
rules[index] = rule;
|
||||
}
|
||||
} else {
|
||||
// 添加新规则
|
||||
rules.unshift(rule);
|
||||
}
|
||||
|
||||
renderRules();
|
||||
closeModal();
|
||||
updateStats();
|
||||
}
|
||||
|
||||
// 重置表单
|
||||
function resetForm() {
|
||||
document.getElementById('rule-name').value = '';
|
||||
document.getElementById('rule-description').value = '';
|
||||
document.getElementById('rule-direction').value = 'inbound';
|
||||
document.getElementById('rule-action').value = 'allow';
|
||||
document.getElementById('rule-enabled').checked = true;
|
||||
document.getElementById('program-path').value = '';
|
||||
document.getElementById('protocol-type').value = 'any';
|
||||
document.getElementById('local-ports').value = '';
|
||||
document.getElementById('remote-ports').value = '';
|
||||
|
||||
// 重置到第一个标签页
|
||||
document.querySelectorAll('.tab').forEach((tab, index) => {
|
||||
tab.classList.toggle('active', index === 0);
|
||||
});
|
||||
document.querySelectorAll('.tab-content').forEach((content, index) => {
|
||||
content.classList.toggle('active', index === 0);
|
||||
});
|
||||
}
|
||||
|
||||
// 过滤规则
|
||||
function filterRules(type) {
|
||||
// 实际实现中应该过滤规则
|
||||
console.log('过滤规则:', type);
|
||||
}
|
||||
|
||||
// 搜索规则
|
||||
function searchRules(keyword) {
|
||||
// 实际实现中应该搜索规则
|
||||
console.log('搜索规则:', keyword);
|
||||
}
|
||||
|
||||
// 更新统计
|
||||
function updateStats() {
|
||||
document.getElementById('total-rules').textContent = rules.length;
|
||||
}
|
||||
|
||||
// 导出规则
|
||||
function exportRules() {
|
||||
const dataStr = JSON.stringify(rules, null, 2);
|
||||
const dataBlob = new Blob([dataStr], {type: 'application/json'});
|
||||
const url = URL.createObjectURL(dataBlob);
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.download = 'firewall_rules.json';
|
||||
link.click();
|
||||
}
|
||||
|
||||
// 导入规则
|
||||
function importRules() {
|
||||
const input = document.createElement('input');
|
||||
input.type = 'file';
|
||||
input.accept = '.json';
|
||||
input.onchange = function(e) {
|
||||
const file = e.target.files[0];
|
||||
if (file) {
|
||||
const reader = new FileReader();
|
||||
reader.onload = function(e) {
|
||||
try {
|
||||
const importedRules = JSON.parse(e.target.result);
|
||||
rules = importedRules;
|
||||
renderRules();
|
||||
updateStats();
|
||||
alert('规则导入成功!');
|
||||
} catch (err) {
|
||||
alert('导入失败:文件格式错误');
|
||||
}
|
||||
};
|
||||
reader.readAsText(file);
|
||||
}
|
||||
};
|
||||
input.click();
|
||||
}
|
||||
|
||||
// 处理本地/远程IP选择
|
||||
document.querySelectorAll('input[name="local-ip"]').forEach(radio => {
|
||||
radio.addEventListener('change', function() {
|
||||
document.getElementById('local-ip-list').style.display =
|
||||
this.value === 'custom' ? 'block' : 'none';
|
||||
});
|
||||
});
|
||||
|
||||
document.querySelectorAll('input[name="remote-ip"]').forEach(radio => {
|
||||
radio.addEventListener('change', function() {
|
||||
document.getElementById('remote-ip-list').style.display =
|
||||
this.value === 'custom' ? 'block' : 'none';
|
||||
});
|
||||
});
|
||||
|
||||
// 页面加载时初始化
|
||||
window.onload = function() {
|
||||
renderRules();
|
||||
updateStats();
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,1233 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>防火墙管理看板</title>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/3.3.4/vue.global.prod.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/lucide/0.263.1/lucide.min.js"></script>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', sans-serif;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
min-height: 100vh;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.app-container {
|
||||
padding: 20px;
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.header {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 16px;
|
||||
padding: 24px 32px;
|
||||
margin-bottom: 24px;
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
.header-content {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
color: #2d3748;
|
||||
font-size: 2rem;
|
||||
font-weight: 700;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.header-stats {
|
||||
display: flex;
|
||||
gap: 24px;
|
||||
}
|
||||
|
||||
.stat-item {
|
||||
text-align: center;
|
||||
padding: 12px 20px;
|
||||
background: linear-gradient(135deg, #4f46e5, #7c3aed);
|
||||
color: white;
|
||||
border-radius: 12px;
|
||||
min-width: 80px;
|
||||
}
|
||||
|
||||
.stat-number {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size: 0.875rem;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.controls {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 16px;
|
||||
padding: 20px 32px;
|
||||
margin-bottom: 24px;
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.filter-group {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.filter-select, .search-input {
|
||||
padding: 10px 16px;
|
||||
border: 2px solid #e2e8f0;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
transition: all 0.3s ease;
|
||||
background: white;
|
||||
}
|
||||
|
||||
.filter-select:focus, .search-input:focus {
|
||||
outline: none;
|
||||
border-color: #4f46e5;
|
||||
box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.1);
|
||||
}
|
||||
|
||||
.search-input {
|
||||
min-width: 250px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
padding: 12px 24px;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: linear-gradient(135deg, #4f46e5, #7c3aed);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 8px 25px rgba(79, 70, 229, 0.3);
|
||||
}
|
||||
|
||||
.btn-success {
|
||||
background: linear-gradient(135deg, #10b981, #059669);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-danger {
|
||||
background: linear-gradient(135deg, #ef4444, #dc2626);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background: #f1f5f9;
|
||||
color: #64748b;
|
||||
border: 1px solid #e2e8f0;
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.rules-container {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.rules-header {
|
||||
padding: 20px 32px;
|
||||
background: linear-gradient(135deg, #f8fafc, #f1f5f9);
|
||||
border-bottom: 1px solid #e2e8f0;
|
||||
}
|
||||
|
||||
.rules-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 120px 100px 150px 120px 100px 140px;
|
||||
gap: 16px;
|
||||
font-weight: 600;
|
||||
color: #475569;
|
||||
}
|
||||
|
||||
.rule-item {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 120px 100px 150px 120px 100px 140px;
|
||||
gap: 16px;
|
||||
padding: 20px 32px;
|
||||
border-bottom: 1px solid #f1f5f9;
|
||||
transition: all 0.3s ease;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.rule-item:hover {
|
||||
background: rgba(79, 70, 229, 0.05);
|
||||
transform: translateX(4px);
|
||||
}
|
||||
|
||||
.rule-name {
|
||||
font-weight: 600;
|
||||
color: #2d3748;
|
||||
}
|
||||
|
||||
.rule-program {
|
||||
font-size: 0.875rem;
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
.status-badge {
|
||||
padding: 4px 12px;
|
||||
border-radius: 20px;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.status-enabled {
|
||||
background: #dcfce7;
|
||||
color: #166534;
|
||||
}
|
||||
|
||||
.status-disabled {
|
||||
background: #fee2e2;
|
||||
color: #991b1b;
|
||||
}
|
||||
|
||||
.action-badge {
|
||||
padding: 4px 12px;
|
||||
border-radius: 20px;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.action-allow {
|
||||
background: #dcfce7;
|
||||
color: #166534;
|
||||
}
|
||||
|
||||
.action-block {
|
||||
background: #fee2e2;
|
||||
color: #991b1b;
|
||||
}
|
||||
|
||||
.direction-badge {
|
||||
padding: 4px 12px;
|
||||
border-radius: 20px;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.direction-inbound {
|
||||
background: #dbeafe;
|
||||
color: #1e40af;
|
||||
}
|
||||
|
||||
.direction-outbound {
|
||||
background: #fef3c7;
|
||||
color: #92400e;
|
||||
}
|
||||
|
||||
.rule-actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.icon-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.icon-btn:hover {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
.icon-btn.edit {
|
||||
background: #f0f9ff;
|
||||
color: #0369a1;
|
||||
}
|
||||
|
||||
.icon-btn.delete {
|
||||
background: #fef2f2;
|
||||
color: #dc2626;
|
||||
}
|
||||
|
||||
.icon-btn.toggle {
|
||||
background: #f7fee7;
|
||||
color: #65a30d;
|
||||
}
|
||||
|
||||
.modal {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
backdrop-filter: blur(4px);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 1000;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.modal.show {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
background: white;
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
|
||||
max-width: 800px;
|
||||
width: 90%;
|
||||
max-height: 90vh;
|
||||
overflow-y: auto;
|
||||
transform: scale(0.9);
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.modal.show .modal-content {
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
.modal-header {
|
||||
padding: 24px 32px;
|
||||
border-bottom: 1px solid #e2e8f0;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.modal-title {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 700;
|
||||
color: #2d3748;
|
||||
}
|
||||
|
||||
.close-btn {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border: none;
|
||||
background: #f1f5f9;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.close-btn:hover {
|
||||
background: #e2e8f0;
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
padding: 32px;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.form-row {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 20px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.form-label {
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
font-weight: 600;
|
||||
color: #374151;
|
||||
}
|
||||
|
||||
.form-input, .form-select, .form-textarea {
|
||||
width: 100%;
|
||||
padding: 12px 16px;
|
||||
border: 2px solid #e2e8f0;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.form-input:focus, .form-select:focus, .form-textarea:focus {
|
||||
outline: none;
|
||||
border-color: #4f46e5;
|
||||
box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.1);
|
||||
}
|
||||
|
||||
.form-textarea {
|
||||
resize: vertical;
|
||||
min-height: 80px;
|
||||
}
|
||||
|
||||
.checkbox-group {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.checkbox {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
.service-list {
|
||||
max-height: 200px;
|
||||
overflow-y: auto;
|
||||
border: 2px solid #e2e8f0;
|
||||
border-radius: 8px;
|
||||
padding: 12px;
|
||||
background: #f8fafc;
|
||||
}
|
||||
|
||||
.service-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 8px;
|
||||
border-radius: 6px;
|
||||
transition: background 0.2s ease;
|
||||
}
|
||||
|
||||
.service-item:hover {
|
||||
background: #e2e8f0;
|
||||
}
|
||||
|
||||
.modal-footer {
|
||||
padding: 24px 32px;
|
||||
border-top: 1px solid #e2e8f0;
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.tabs {
|
||||
display: flex;
|
||||
border-bottom: 2px solid #e2e8f0;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.tab {
|
||||
padding: 12px 24px;
|
||||
cursor: pointer;
|
||||
border-bottom: 3px solid transparent;
|
||||
transition: all 0.3s ease;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.tab.active {
|
||||
color: #4f46e5;
|
||||
border-bottom-color: #4f46e5;
|
||||
}
|
||||
|
||||
.tab-content {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.tab-content.active {
|
||||
display: block;
|
||||
}
|
||||
|
||||
@media (max-width: 1200px) {
|
||||
.rules-grid, .rule-item {
|
||||
grid-template-columns: 1fr 100px 80px 120px 100px 120px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.controls {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.filter-group {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.rules-grid, .rule-item {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.rule-item {
|
||||
padding: 16px 20px;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
width: 95%;
|
||||
margin: 20px;
|
||||
}
|
||||
|
||||
.form-row {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app" class="app-container">
|
||||
<!-- Header -->
|
||||
<div class="header">
|
||||
<div class="header-content">
|
||||
<h1>
|
||||
<i data-lucide="shield"></i>
|
||||
防火墙管理看板
|
||||
</h1>
|
||||
<div class="header-stats">
|
||||
<div class="stat-item">
|
||||
<div class="stat-number">{{ enabledRules }}</div>
|
||||
<div class="stat-label">已启用</div>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<div class="stat-number">{{ disabledRules }}</div>
|
||||
<div class="stat-label">已禁用</div>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<div class="stat-number">{{ totalRules }}</div>
|
||||
<div class="stat-label">总规则</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Controls -->
|
||||
<div class="controls">
|
||||
<div class="filter-group">
|
||||
<select v-model="filterDirection" class="filter-select">
|
||||
<option value="">所有方向</option>
|
||||
<option value="inbound">入站</option>
|
||||
<option value="outbound">出站</option>
|
||||
</select>
|
||||
<select v-model="filterAction" class="filter-select">
|
||||
<option value="">所有操作</option>
|
||||
<option value="allow">允许</option>
|
||||
<option value="block">阻止</option>
|
||||
</select>
|
||||
<select v-model="filterStatus" class="filter-select">
|
||||
<option value="">所有状态</option>
|
||||
<option value="enabled">已启用</option>
|
||||
<option value="disabled">已禁用</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="filter-group">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="搜索规则名称..."
|
||||
v-model="searchText"
|
||||
class="search-input"
|
||||
>
|
||||
<button class="btn btn-primary" @click="openModal()">
|
||||
<i data-lucide="plus"></i>
|
||||
新增规则
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Rules Container -->
|
||||
<div class="rules-container">
|
||||
<div class="rules-header">
|
||||
<div class="rules-grid">
|
||||
<div>规则名称</div>
|
||||
<div>方向</div>
|
||||
<div>操作</div>
|
||||
<div>协议/端口</div>
|
||||
<div>状态</div>
|
||||
<div>作用域</div>
|
||||
<div>操作</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-for="rule in filteredRules" :key="rule.id" class="rule-item">
|
||||
<div>
|
||||
<div class="rule-name">{{ rule.name }}</div>
|
||||
<div class="rule-program">{{ rule.program }}</div>
|
||||
</div>
|
||||
<div>
|
||||
<span :class="'direction-badge direction-' + rule.direction">
|
||||
{{ rule.direction === 'inbound' ? '入站' : '出站' }}
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<span :class="'action-badge action-' + rule.action">
|
||||
{{ rule.action === 'allow' ? '允许' : '阻止' }}
|
||||
</span>
|
||||
</div>
|
||||
<div>{{ rule.protocol }}/{{ rule.ports }}</div>
|
||||
<div>
|
||||
<span :class="'status-badge status-' + rule.status">
|
||||
{{ rule.status === 'enabled' ? '已启用' : '已禁用' }}
|
||||
</span>
|
||||
</div>
|
||||
<div>{{ rule.scope }}</div>
|
||||
<div class="rule-actions">
|
||||
<button class="icon-btn edit" @click="editRule(rule)" title="编辑">
|
||||
<i data-lucide="edit"></i>
|
||||
</button>
|
||||
<button class="icon-btn toggle" @click="toggleRule(rule)" title="切换状态">
|
||||
<i :data-lucide="rule.status === 'enabled' ? 'pause' : 'play'"></i>
|
||||
</button>
|
||||
<button class="icon-btn delete" @click="deleteRule(rule.id)" title="删除">
|
||||
<i data-lucide="trash-2"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal -->
|
||||
<div class="modal" :class="{ show: showModal }" @click.self="closeModal">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h2 class="modal-title">{{ editingRule ? '编辑规则' : '新增规则' }}</h2>
|
||||
<button class="close-btn" @click="closeModal">
|
||||
<i data-lucide="x"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="modal-body">
|
||||
<div class="tabs">
|
||||
<div class="tab" :class="{ active: activeTab === 'general' }" @click="activeTab = 'general'">
|
||||
常规
|
||||
</div>
|
||||
<div class="tab" :class="{ active: activeTab === 'programs' }" @click="activeTab = 'programs'">
|
||||
程序和服务
|
||||
</div>
|
||||
<div class="tab" :class="{ active: activeTab === 'protocols' }" @click="activeTab = 'protocols'">
|
||||
协议和端口
|
||||
</div>
|
||||
<div class="tab" :class="{ active: activeTab === 'scope' }" @click="activeTab = 'scope'">
|
||||
作用域
|
||||
</div>
|
||||
<div class="tab" :class="{ active: activeTab === 'advanced' }" @click="activeTab = 'advanced'">
|
||||
高级
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 常规选项卡 -->
|
||||
<div class="tab-content" :class="{ active: activeTab === 'general' }">
|
||||
<div class="form-group">
|
||||
<label class="form-label">规则名称</label>
|
||||
<input type="text" v-model="formData.name" class="form-input" placeholder="输入规则名称">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label">描述</label>
|
||||
<textarea v-model="formData.description" class="form-textarea" placeholder="规则描述(可选)"></textarea>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label class="form-label">方向</label>
|
||||
<select v-model="formData.direction" class="form-select">
|
||||
<option value="inbound">入站</option>
|
||||
<option value="outbound">出站</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">操作</label>
|
||||
<select v-model="formData.action" class="form-select">
|
||||
<option value="allow">允许连接</option>
|
||||
<option value="block">阻止连接</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="checkbox-group">
|
||||
<input type="checkbox" v-model="formData.enabled" class="checkbox" id="enabled">
|
||||
<label for="enabled">启用此规则</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 程序和服务选项卡 -->
|
||||
<div class="tab-content" :class="{ active: activeTab === 'programs' }">
|
||||
<div class="form-group">
|
||||
<label class="form-label">应用程序</label>
|
||||
<div class="checkbox-group">
|
||||
<input type="radio" v-model="formData.programType" value="all" name="programType" id="all-programs">
|
||||
<label for="all-programs">所有程序</label>
|
||||
</div>
|
||||
<div class="checkbox-group">
|
||||
<input type="radio" v-model="formData.programType" value="specific" name="programType" id="specific-program">
|
||||
<label for="specific-program">此程序路径:</label>
|
||||
</div>
|
||||
<input
|
||||
type="text"
|
||||
v-model="formData.programPath"
|
||||
class="form-input"
|
||||
placeholder="C:\Program Files\..."
|
||||
:disabled="formData.programType !== 'specific'"
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label">服务</label>
|
||||
<div class="checkbox-group">
|
||||
<input type="radio" v-model="formData.serviceType" value="all" name="serviceType" id="all-services">
|
||||
<label for="all-services">应用于所有程序和服务</label>
|
||||
</div>
|
||||
<div class="checkbox-group">
|
||||
<input type="radio" v-model="formData.serviceType" value="specific" name="serviceType" id="specific-services">
|
||||
<label for="specific-services">仅应用于这些服务:</label>
|
||||
</div>
|
||||
|
||||
<div v-if="formData.serviceType === 'specific'" class="service-list">
|
||||
<div v-for="service in availableServices" :key="service.name" class="service-item">
|
||||
<input
|
||||
type="checkbox"
|
||||
:value="service.name"
|
||||
v-model="formData.selectedServices"
|
||||
:id="'service-' + service.name"
|
||||
>
|
||||
<label :for="'service-' + service.name">{{ service.displayName }}</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 协议和端口选项卡 -->
|
||||
<div class="tab-content" :class="{ active: activeTab === 'protocols' }">
|
||||
<div class="form-group">
|
||||
<label class="form-label">协议类型</label>
|
||||
<select v-model="formData.protocolType" class="form-select">
|
||||
<option value="any">任何</option>
|
||||
<option value="tcp">TCP</option>
|
||||
<option value="udp">UDP</option>
|
||||
<option value="icmpv4">ICMPv4</option>
|
||||
<option value="icmpv6">ICMPv6</option>
|
||||
<option value="custom">自定义</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div v-if="formData.protocolType === 'custom'" class="form-group">
|
||||
<label class="form-label">协议号</label>
|
||||
<input type="number" v-model="formData.protocolNumber" class="form-input" placeholder="0-255">
|
||||
</div>
|
||||
|
||||
<div v-if="['tcp', 'udp'].includes(formData.protocolType)" class="form-row">
|
||||
<div class="form-group">
|
||||
<label class="form-label">本地端口</label>
|
||||
<div class="checkbox-group">
|
||||
<input type="radio" v-model="formData.localPortType" value="all" name="localPortType" id="all-local-ports">
|
||||
<label for="all-local-ports">所有端口</label>
|
||||
</div>
|
||||
<div class="checkbox-group">
|
||||
<input type="radio" v-model="formData.localPortType" value="specific" name="localPortType" id="specific-local-ports">
|
||||
<label for="specific-local-ports">特定端口:</label>
|
||||
</div>
|
||||
<input
|
||||
type="text"
|
||||
v-model="formData.localPorts"
|
||||
class="form-input"
|
||||
placeholder="80,443,8000-8080"
|
||||
:disabled="formData.localPortType !== 'specific'"
|
||||
>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">远程端口</label>
|
||||
<div class="checkbox-group">
|
||||
<input type="radio" v-model="formData.remotePortType" value="all" name="remotePortType" id="all-remote-ports">
|
||||
<label for="all-remote-ports">所有端口</label>
|
||||
</div>
|
||||
<div class="checkbox-group">
|
||||
<input type="radio" v-model="formData.remotePortType" value="specific" name="remotePortType" id="specific-remote-ports">
|
||||
<label for="specific-remote-ports">特定端口:</label>
|
||||
</div>
|
||||
<input
|
||||
type="text"
|
||||
v-model="formData.remotePorts"
|
||||
class="form-input"
|
||||
placeholder="80,443,8000-8080"
|
||||
:disabled="formData.remotePortType !== 'specific'"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 作用域选项卡 -->
|
||||
<div class="tab-content" :class="{ active: activeTab === 'scope' }">
|
||||
<div class="form-group">
|
||||
<label class="form-label">本地IP地址</label>
|
||||
<div class="checkbox-group">
|
||||
<input type="radio" v-model="formData.localAddressType" value="any" name="localAddressType" id="any-local-address">
|
||||
<label for="any-local-address">任何IP地址</label>
|
||||
</div>
|
||||
<div class="checkbox-group">
|
||||
<input type="radio" v-model="formData.localAddressType" value="specific" name="localAddressType" id="specific-local-address">
|
||||
<label for="specific-local-address">这些IP地址:</label>
|
||||
</div>
|
||||
<textarea
|
||||
v-model="formData.localAddresses"
|
||||
class="form-textarea"
|
||||
placeholder="192.168.1.1 10.0.0.0/8 172.16.0.0-172.31.255.255"
|
||||
:disabled="formData.localAddressType !== 'specific'"
|
||||
></textarea>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label">远程IP地址</label>
|
||||
<div class="checkbox-group">
|
||||
<input type="radio" v-model="formData.remoteAddressType" value="any" name="remoteAddressType" id="any-remote-address">
|
||||
<label for="any-remote-address">任何IP地址</label>
|
||||
</div>
|
||||
<div class="checkbox-group">
|
||||
<input type="radio" v-model="formData.remoteAddressType" value="specific" name="remoteAddressType" id="specific-remote-address">
|
||||
<label for="specific-remote-address">这些IP地址:</label>
|
||||
</div>
|
||||
<textarea
|
||||
v-model="formData.remoteAddresses"
|
||||
class="form-textarea"
|
||||
placeholder="8.8.8.8 192.168.0.0/16 203.0.113.0-203.0.113.255"
|
||||
:disabled="formData.remoteAddressType !== 'specific'"
|
||||
></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 高级选项卡 -->
|
||||
<div class="tab-content" :class="{ active: activeTab === 'advanced' }">
|
||||
<div class="form-group">
|
||||
<label class="form-label">配置文件</label>
|
||||
<div class="checkbox-group">
|
||||
<input type="checkbox" v-model="formData.profiles.domain" id="profile-domain">
|
||||
<label for="profile-domain">域</label>
|
||||
</div>
|
||||
<div class="checkbox-group">
|
||||
<input type="checkbox" v-model="formData.profiles.private" id="profile-private">
|
||||
<label for="profile-private">专用</label>
|
||||
</div>
|
||||
<div class="checkbox-group">
|
||||
<input type="checkbox" v-model="formData.profiles.public" id="profile-public">
|
||||
<label for="profile-public">公用</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label">边缘遍历</label>
|
||||
<select v-model="formData.edgeTraversal" class="form-select">
|
||||
<option value="no">阻止边缘遍历</option>
|
||||
<option value="allow">允许边缘遍历</option>
|
||||
<option value="defer">延迟到用户</option>
|
||||
<option value="defer-app">延迟到应用程序</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label">授权用户</label>
|
||||
<div class="checkbox-group">
|
||||
<input type="radio" v-model="formData.userType" value="any" name="userType" id="any-user">
|
||||
<label for="any-user">任何用户</label>
|
||||
</div>
|
||||
<div class="checkbox-group">
|
||||
<input type="radio" v-model="formData.userType" value="specific" name="userType" id="specific-users">
|
||||
<label for="specific-users">仅这些用户:</label>
|
||||
</div>
|
||||
<input
|
||||
type="text"
|
||||
v-model="formData.authorizedUsers"
|
||||
class="form-input"
|
||||
placeholder="DOMAIN\Username, Everyone, ..."
|
||||
:disabled="formData.userType !== 'specific'"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-secondary" @click="closeModal">取消</button>
|
||||
<button class="btn btn-success" @click="saveRule">
|
||||
{{ editingRule ? '保存' : '创建' }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const { createApp } = Vue;
|
||||
|
||||
createApp({
|
||||
data() {
|
||||
return {
|
||||
showModal: false,
|
||||
editingRule: null,
|
||||
activeTab: 'general',
|
||||
searchText: '',
|
||||
filterDirection: '',
|
||||
filterAction: '',
|
||||
filterStatus: '',
|
||||
|
||||
// 表单数据
|
||||
formData: {
|
||||
name: '',
|
||||
description: '',
|
||||
direction: 'inbound',
|
||||
action: 'allow',
|
||||
enabled: true,
|
||||
programType: 'all',
|
||||
programPath: '',
|
||||
serviceType: 'all',
|
||||
selectedServices: [],
|
||||
protocolType: 'any',
|
||||
protocolNumber: '',
|
||||
localPortType: 'all',
|
||||
localPorts: '',
|
||||
remotePortType: 'all',
|
||||
remotePorts: '',
|
||||
localAddressType: 'any',
|
||||
localAddresses: '',
|
||||
remoteAddressType: 'any',
|
||||
remoteAddresses: '',
|
||||
profiles: {
|
||||
domain: true,
|
||||
private: true,
|
||||
public: false
|
||||
},
|
||||
edgeTraversal: 'no',
|
||||
userType: 'any',
|
||||
authorizedUsers: ''
|
||||
},
|
||||
|
||||
// 可用服务列表
|
||||
availableServices: [
|
||||
{ name: 'http', displayName: 'HTTP (80)' },
|
||||
{ name: 'https', displayName: 'HTTPS (443)' },
|
||||
{ name: 'ftp', displayName: 'FTP (21)' },
|
||||
{ name: 'ssh', displayName: 'SSH (22)' },
|
||||
{ name: 'telnet', displayName: 'Telnet (23)' },
|
||||
{ name: 'smtp', displayName: 'SMTP (25)' },
|
||||
{ name: 'dns', displayName: 'DNS (53)' },
|
||||
{ name: 'dhcp', displayName: 'DHCP (67-68)' },
|
||||
{ name: 'pop3', displayName: 'POP3 (110)' },
|
||||
{ name: 'imap', displayName: 'IMAP (143)' },
|
||||
{ name: 'snmp', displayName: 'SNMP (161)' },
|
||||
{ name: 'ldap', displayName: 'LDAP (389)' },
|
||||
{ name: 'smb', displayName: 'SMB (445)' },
|
||||
{ name: 'rdp', displayName: 'RDP (3389)' }
|
||||
],
|
||||
|
||||
// 防火墙规则数据
|
||||
rules: [
|
||||
{
|
||||
id: 1,
|
||||
name: 'Core Networking - DNS (UDP-Out)',
|
||||
program: 'svchost.exe',
|
||||
direction: 'outbound',
|
||||
action: 'allow',
|
||||
protocol: 'UDP',
|
||||
ports: '53',
|
||||
status: 'enabled',
|
||||
scope: '任何'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: 'Windows Remote Management (HTTP-In)',
|
||||
program: 'svchost.exe',
|
||||
direction: 'inbound',
|
||||
action: 'allow',
|
||||
protocol: 'TCP',
|
||||
ports: '5985',
|
||||
status: 'enabled',
|
||||
scope: '本地子网'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: 'File and Printer Sharing (SMB-In)',
|
||||
program: 'System',
|
||||
direction: 'inbound',
|
||||
action: 'allow',
|
||||
protocol: 'TCP',
|
||||
ports: '445',
|
||||
status: 'disabled',
|
||||
scope: '专用'
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: 'Remote Desktop - User Mode (TCP-In)',
|
||||
program: 'svchost.exe',
|
||||
direction: 'inbound',
|
||||
action: 'allow',
|
||||
protocol: 'TCP',
|
||||
ports: '3389',
|
||||
status: 'enabled',
|
||||
scope: '任何'
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
name: 'Block Suspicious Outbound',
|
||||
program: 'malware.exe',
|
||||
direction: 'outbound',
|
||||
action: 'block',
|
||||
protocol: 'TCP',
|
||||
ports: '任何',
|
||||
status: 'enabled',
|
||||
scope: '任何'
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
name: 'Web Browser (HTTP/HTTPS-Out)',
|
||||
program: 'chrome.exe',
|
||||
direction: 'outbound',
|
||||
action: 'allow',
|
||||
protocol: 'TCP',
|
||||
ports: '80,443',
|
||||
status: 'enabled',
|
||||
scope: '任何'
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
name: 'Email Client (SMTP-Out)',
|
||||
program: 'outlook.exe',
|
||||
direction: 'outbound',
|
||||
action: 'allow',
|
||||
protocol: 'TCP',
|
||||
ports: '25,587',
|
||||
status: 'disabled',
|
||||
scope: '任何'
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
name: 'Windows Update (HTTP-Out)',
|
||||
program: 'svchost.exe',
|
||||
direction: 'outbound',
|
||||
action: 'allow',
|
||||
protocol: 'TCP',
|
||||
ports: '80,443',
|
||||
status: 'enabled',
|
||||
scope: '任何'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
filteredRules() {
|
||||
let filtered = this.rules;
|
||||
|
||||
if (this.searchText) {
|
||||
filtered = filtered.filter(rule =>
|
||||
rule.name.toLowerCase().includes(this.searchText.toLowerCase()) ||
|
||||
rule.program.toLowerCase().includes(this.searchText.toLowerCase())
|
||||
);
|
||||
}
|
||||
|
||||
if (this.filterDirection) {
|
||||
filtered = filtered.filter(rule => rule.direction === this.filterDirection);
|
||||
}
|
||||
|
||||
if (this.filterAction) {
|
||||
filtered = filtered.filter(rule => rule.action === this.filterAction);
|
||||
}
|
||||
|
||||
if (this.filterStatus) {
|
||||
filtered = filtered.filter(rule => rule.status === this.filterStatus);
|
||||
}
|
||||
|
||||
return filtered;
|
||||
},
|
||||
|
||||
totalRules() {
|
||||
return this.rules.length;
|
||||
},
|
||||
|
||||
enabledRules() {
|
||||
return this.rules.filter(rule => rule.status === 'enabled').length;
|
||||
},
|
||||
|
||||
disabledRules() {
|
||||
return this.rules.filter(rule => rule.status === 'disabled').length;
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
openModal(rule = null) {
|
||||
this.editingRule = rule;
|
||||
this.activeTab = 'general';
|
||||
|
||||
if (rule) {
|
||||
// 编辑模式 - 填充表单数据
|
||||
this.formData = {
|
||||
name: rule.name,
|
||||
description: '',
|
||||
direction: rule.direction,
|
||||
action: rule.action,
|
||||
enabled: rule.status === 'enabled',
|
||||
programType: 'all',
|
||||
programPath: rule.program,
|
||||
serviceType: 'all',
|
||||
selectedServices: [],
|
||||
protocolType: rule.protocol.toLowerCase(),
|
||||
protocolNumber: '',
|
||||
localPortType: 'specific',
|
||||
localPorts: rule.ports,
|
||||
remotePortType: 'all',
|
||||
remotePorts: '',
|
||||
localAddressType: 'any',
|
||||
localAddresses: '',
|
||||
remoteAddressType: 'any',
|
||||
remoteAddresses: '',
|
||||
profiles: {
|
||||
domain: true,
|
||||
private: true,
|
||||
public: false
|
||||
},
|
||||
edgeTraversal: 'no',
|
||||
userType: 'any',
|
||||
authorizedUsers: ''
|
||||
};
|
||||
} else {
|
||||
// 新增模式 - 重置表单
|
||||
this.resetForm();
|
||||
}
|
||||
|
||||
this.showModal = true;
|
||||
},
|
||||
|
||||
closeModal() {
|
||||
this.showModal = false;
|
||||
this.editingRule = null;
|
||||
this.resetForm();
|
||||
},
|
||||
|
||||
resetForm() {
|
||||
this.formData = {
|
||||
name: '',
|
||||
description: '',
|
||||
direction: 'inbound',
|
||||
action: 'allow',
|
||||
enabled: true,
|
||||
programType: 'all',
|
||||
programPath: '',
|
||||
serviceType: 'all',
|
||||
selectedServices: [],
|
||||
protocolType: 'any',
|
||||
protocolNumber: '',
|
||||
localPortType: 'all',
|
||||
localPorts: '',
|
||||
remotePortType: 'all',
|
||||
remotePorts: '',
|
||||
localAddressType: 'any',
|
||||
localAddresses: '',
|
||||
remoteAddressType: 'any',
|
||||
remoteAddresses: '',
|
||||
profiles: {
|
||||
domain: true,
|
||||
private: true,
|
||||
public: false
|
||||
},
|
||||
edgeTraversal: 'no',
|
||||
userType: 'any',
|
||||
authorizedUsers: ''
|
||||
};
|
||||
},
|
||||
|
||||
saveRule() {
|
||||
if (!this.formData.name.trim()) {
|
||||
alert('请输入规则名称');
|
||||
return;
|
||||
}
|
||||
|
||||
const ruleData = {
|
||||
name: this.formData.name,
|
||||
program: this.formData.programType === 'specific' ? this.formData.programPath : 'System',
|
||||
direction: this.formData.direction,
|
||||
action: this.formData.action,
|
||||
protocol: this.formData.protocolType.toUpperCase(),
|
||||
ports: this.formData.localPortType === 'specific' ? this.formData.localPorts : '任何',
|
||||
status: this.formData.enabled ? 'enabled' : 'disabled',
|
||||
scope: '任何'
|
||||
};
|
||||
|
||||
if (this.editingRule) {
|
||||
// 编辑现有规则
|
||||
const index = this.rules.findIndex(rule => rule.id === this.editingRule.id);
|
||||
if (index !== -1) {
|
||||
this.rules[index] = { ...this.rules[index], ...ruleData };
|
||||
}
|
||||
} else {
|
||||
// 新增规则
|
||||
const newRule = {
|
||||
id: Math.max(...this.rules.map(r => r.id)) + 1,
|
||||
...ruleData
|
||||
};
|
||||
this.rules.push(newRule);
|
||||
}
|
||||
|
||||
this.closeModal();
|
||||
},
|
||||
|
||||
editRule(rule) {
|
||||
this.openModal(rule);
|
||||
},
|
||||
|
||||
toggleRule(rule) {
|
||||
rule.status = rule.status === 'enabled' ? 'disabled' : 'enabled';
|
||||
},
|
||||
|
||||
deleteRule(ruleId) {
|
||||
if (confirm('确定要删除这个规则吗?')) {
|
||||
this.rules = this.rules.filter(rule => rule.id !== ruleId);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
// 初始化Lucide图标
|
||||
lucide.createIcons();
|
||||
},
|
||||
|
||||
updated() {
|
||||
// 更新时重新创建图标
|
||||
lucide.createIcons();
|
||||
}
|
||||
}).mount('#app');
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in new issue
Block a user