3301 lines
126 KiB
HTML
3301 lines
126 KiB
HTML
|
|
<!DOCTYPE html>
|
|||
|
|
<html lang="zh-CN">
|
|||
|
|
<head>
|
|||
|
|
<meta charset="UTF-8">
|
|||
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|||
|
|
<title>智能低代码开发平台</title>
|
|||
|
|
<!-- Bootstrap CSS -->
|
|||
|
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
|||
|
|
<!-- Bootstrap Icons -->
|
|||
|
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.0/font/bootstrap-icons.css">
|
|||
|
|
<style>
|
|||
|
|
:root {
|
|||
|
|
--bg-primary: #0a0e27;
|
|||
|
|
--bg-secondary: #1a1f3a;
|
|||
|
|
--bg-panel: rgba(30, 41, 59, 0.6);
|
|||
|
|
--bg-card: rgba(51, 65, 85, 0.4);
|
|||
|
|
--border-color: rgba(59, 130, 246, 0.2);
|
|||
|
|
--border-hover: rgba(59, 130, 246, 0.4);
|
|||
|
|
--border-active: rgba(59, 130, 246, 0.6);
|
|||
|
|
--accent-blue: #3b82f6;
|
|||
|
|
--accent-cyan: #06b6d4;
|
|||
|
|
--text-primary: #e0e6ed;
|
|||
|
|
--text-secondary: #94a3b8;
|
|||
|
|
--text-highlight: #60a5fa;
|
|||
|
|
--success: #10b981;
|
|||
|
|
--warning: #f59e0b;
|
|||
|
|
--error: #ef4444;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
* {
|
|||
|
|
margin: 0;
|
|||
|
|
padding: 0;
|
|||
|
|
box-sizing: border-box;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
body {
|
|||
|
|
font-family: 'Microsoft YaHei', Arial, sans-serif;
|
|||
|
|
background: linear-gradient(135deg, var(--bg-primary) 0%, var(--bg-secondary) 100%);
|
|||
|
|
color: var(--text-primary);
|
|||
|
|
overflow-x: hidden;
|
|||
|
|
min-height: 100vh;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 顶部导航栏 */
|
|||
|
|
.top-navbar {
|
|||
|
|
background: rgba(15, 23, 42, 0.8);
|
|||
|
|
backdrop-filter: blur(10px);
|
|||
|
|
border-bottom: 2px solid var(--border-color);
|
|||
|
|
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5), 0 0 20px rgba(59, 130, 246, 0.3);
|
|||
|
|
padding: 15px 0;
|
|||
|
|
position: sticky;
|
|||
|
|
top: 0;
|
|||
|
|
z-index: 1000;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.logo {
|
|||
|
|
font-size: 24px;
|
|||
|
|
font-weight: bold;
|
|||
|
|
background: linear-gradient(135deg, var(--accent-blue) 0%, var(--accent-cyan) 100%);
|
|||
|
|
-webkit-background-clip: text;
|
|||
|
|
-webkit-text-fill-color: transparent;
|
|||
|
|
text-shadow: 0 0 30px rgba(59, 130, 246, 0.5);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.top-navbar .btn {
|
|||
|
|
color: var(--text-secondary);
|
|||
|
|
border: 1px solid var(--border-color);
|
|||
|
|
background: rgba(51, 65, 85, 0.5);
|
|||
|
|
transition: all 0.3s;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.top-navbar .btn:hover {
|
|||
|
|
color: var(--text-primary);
|
|||
|
|
background: rgba(51, 65, 85, 0.8);
|
|||
|
|
border-color: var(--border-hover);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.notification-badge {
|
|||
|
|
position: absolute;
|
|||
|
|
top: -5px;
|
|||
|
|
right: -5px;
|
|||
|
|
background: var(--error);
|
|||
|
|
color: white;
|
|||
|
|
border-radius: 50%;
|
|||
|
|
width: 18px;
|
|||
|
|
height: 18px;
|
|||
|
|
font-size: 10px;
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: center;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 面板样式 */
|
|||
|
|
.panel {
|
|||
|
|
background: var(--bg-panel);
|
|||
|
|
backdrop-filter: blur(15px);
|
|||
|
|
border: 1px solid var(--border-color);
|
|||
|
|
border-radius: 12px;
|
|||
|
|
padding: 20px;
|
|||
|
|
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4), inset 0 1px 0 rgba(255, 255, 255, 0.05);
|
|||
|
|
height: 100%;
|
|||
|
|
position: relative;
|
|||
|
|
transition: all 0.3s;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.panel::before {
|
|||
|
|
content: '';
|
|||
|
|
position: absolute;
|
|||
|
|
top: 0;
|
|||
|
|
left: 0;
|
|||
|
|
right: 0;
|
|||
|
|
height: 1px;
|
|||
|
|
background: linear-gradient(90deg, transparent, rgba(59, 130, 246, 0.5), transparent);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.panel-header {
|
|||
|
|
font-size: 16px;
|
|||
|
|
font-weight: bold;
|
|||
|
|
margin-bottom: 15px;
|
|||
|
|
padding-bottom: 10px;
|
|||
|
|
border-bottom: 1px solid var(--border-color);
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
gap: 10px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.panel-header::before {
|
|||
|
|
content: '';
|
|||
|
|
width: 4px;
|
|||
|
|
height: 18px;
|
|||
|
|
background: linear-gradient(180deg, var(--accent-blue), var(--accent-cyan));
|
|||
|
|
border-radius: 2px;
|
|||
|
|
box-shadow: 0 0 10px rgba(59, 130, 246, 0.5);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 按钮样式 */
|
|||
|
|
.btn-primary {
|
|||
|
|
background: linear-gradient(135deg, var(--accent-blue) 0%, #2563eb 100%);
|
|||
|
|
border: none;
|
|||
|
|
box-shadow: 0 4px 15px rgba(59, 130, 246, 0.4);
|
|||
|
|
transition: all 0.3s;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.btn-primary:hover {
|
|||
|
|
transform: translateY(-2px);
|
|||
|
|
box-shadow: 0 6px 20px rgba(59, 130, 246, 0.6);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.btn-primary:active {
|
|||
|
|
transform: scale(0.95);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.btn-secondary {
|
|||
|
|
background: rgba(51, 65, 85, 0.5);
|
|||
|
|
color: var(--text-secondary);
|
|||
|
|
border: 1px solid var(--border-color);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.btn-secondary:hover {
|
|||
|
|
background: rgba(51, 65, 85, 0.8);
|
|||
|
|
color: var(--text-primary);
|
|||
|
|
border-color: var(--border-hover);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.btn-success {
|
|||
|
|
background: linear-gradient(135deg, var(--success) 0%, #059669 100%);
|
|||
|
|
border: none;
|
|||
|
|
box-shadow: 0 4px 15px rgba(16, 185, 129, 0.4);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.btn-success:hover {
|
|||
|
|
transform: translateY(-2px);
|
|||
|
|
box-shadow: 0 6px 20px rgba(16, 185, 129, 0.6);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 项目列表 */
|
|||
|
|
.project-search {
|
|||
|
|
margin-bottom: 15px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.project-search input {
|
|||
|
|
background: rgba(51, 65, 85, 0.5);
|
|||
|
|
border: 1px solid var(--border-color);
|
|||
|
|
color: var(--text-primary);
|
|||
|
|
padding: 8px 12px;
|
|||
|
|
border-radius: 6px;
|
|||
|
|
width: 100%;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.project-search input:focus {
|
|||
|
|
outline: none;
|
|||
|
|
border-color: var(--accent-blue);
|
|||
|
|
box-shadow: 0 0 10px rgba(59, 130, 246, 0.3);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.project-item {
|
|||
|
|
padding: 12px;
|
|||
|
|
margin-bottom: 8px;
|
|||
|
|
background: var(--bg-card);
|
|||
|
|
border: 1px solid var(--border-color);
|
|||
|
|
border-radius: 8px;
|
|||
|
|
cursor: pointer;
|
|||
|
|
transition: all 0.3s;
|
|||
|
|
position: relative;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.project-item::before {
|
|||
|
|
content: '';
|
|||
|
|
position: absolute;
|
|||
|
|
left: 0;
|
|||
|
|
top: 0;
|
|||
|
|
bottom: 0;
|
|||
|
|
width: 3px;
|
|||
|
|
background: transparent;
|
|||
|
|
border-radius: 8px 0 0 8px;
|
|||
|
|
transition: all 0.3s;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.project-item:hover {
|
|||
|
|
background: rgba(59, 130, 246, 0.15);
|
|||
|
|
border-color: var(--border-hover);
|
|||
|
|
transform: translateX(5px);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.project-item.active {
|
|||
|
|
background: rgba(59, 130, 246, 0.2);
|
|||
|
|
border-color: var(--border-active);
|
|||
|
|
box-shadow: 0 0 20px rgba(59, 130, 246, 0.3);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.project-item.active::before {
|
|||
|
|
background: linear-gradient(180deg, var(--accent-blue), var(--accent-cyan));
|
|||
|
|
box-shadow: 0 0 10px rgba(59, 130, 246, 0.5);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.project-name {
|
|||
|
|
font-weight: bold;
|
|||
|
|
color: var(--text-highlight);
|
|||
|
|
margin-bottom: 5px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.project-desc {
|
|||
|
|
font-size: 12px;
|
|||
|
|
color: var(--text-secondary);
|
|||
|
|
margin-bottom: 5px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.project-meta {
|
|||
|
|
font-size: 11px;
|
|||
|
|
color: var(--text-secondary);
|
|||
|
|
display: flex;
|
|||
|
|
justify-content: space-between;
|
|||
|
|
align-items: center;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.project-status {
|
|||
|
|
padding: 2px 8px;
|
|||
|
|
border-radius: 4px;
|
|||
|
|
font-size: 10px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.status-active {
|
|||
|
|
background: rgba(16, 185, 129, 0.2);
|
|||
|
|
color: var(--success);
|
|||
|
|
border: 1px solid var(--success);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.status-completed {
|
|||
|
|
background: rgba(59, 130, 246, 0.2);
|
|||
|
|
color: var(--accent-blue);
|
|||
|
|
border: 1px solid var(--accent-blue);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.status-paused {
|
|||
|
|
background: rgba(245, 158, 11, 0.2);
|
|||
|
|
color: var(--warning);
|
|||
|
|
border: 1px solid var(--warning);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 标签页 */
|
|||
|
|
.nav-tabs {
|
|||
|
|
border-bottom: 1px solid var(--border-color);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.nav-tabs .nav-link {
|
|||
|
|
color: var(--text-secondary);
|
|||
|
|
background: var(--bg-card);
|
|||
|
|
border: 1px solid var(--border-color);
|
|||
|
|
border-radius: 6px 6px 0 0;
|
|||
|
|
margin-right: 5px;
|
|||
|
|
transition: all 0.3s;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.nav-tabs .nav-link:hover {
|
|||
|
|
background: rgba(59, 130, 246, 0.15);
|
|||
|
|
color: var(--text-primary);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.nav-tabs .nav-link.active {
|
|||
|
|
background: linear-gradient(135deg, var(--accent-blue) 0%, #2563eb 100%);
|
|||
|
|
color: white;
|
|||
|
|
border-color: var(--accent-blue);
|
|||
|
|
box-shadow: 0 0 15px rgba(59, 130, 246, 0.5);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 卡片 */
|
|||
|
|
.item-card {
|
|||
|
|
padding: 15px;
|
|||
|
|
margin-bottom: 10px;
|
|||
|
|
background: var(--bg-card);
|
|||
|
|
border: 1px solid var(--border-color);
|
|||
|
|
border-radius: 8px;
|
|||
|
|
transition: all 0.3s;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.item-card:hover {
|
|||
|
|
border-color: var(--border-hover);
|
|||
|
|
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
|
|||
|
|
transform: scale(1.02);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.item-title {
|
|||
|
|
font-weight: bold;
|
|||
|
|
color: var(--text-highlight);
|
|||
|
|
margin-bottom: 8px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.item-desc {
|
|||
|
|
font-size: 13px;
|
|||
|
|
color: var(--text-secondary);
|
|||
|
|
line-height: 1.5;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 文档列表 */
|
|||
|
|
.doc-item {
|
|||
|
|
padding: 12px;
|
|||
|
|
margin-bottom: 8px;
|
|||
|
|
background: var(--bg-card);
|
|||
|
|
border-left: 3px solid var(--accent-blue);
|
|||
|
|
border-radius: 4px;
|
|||
|
|
cursor: pointer;
|
|||
|
|
transition: all 0.3s;
|
|||
|
|
display: flex;
|
|||
|
|
justify-content: space-between;
|
|||
|
|
align-items: center;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.doc-item:hover {
|
|||
|
|
background: rgba(59, 130, 246, 0.15);
|
|||
|
|
transform: translateX(5px);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.doc-name {
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
gap: 10px;
|
|||
|
|
flex: 1;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.doc-meta {
|
|||
|
|
font-size: 11px;
|
|||
|
|
color: var(--text-secondary);
|
|||
|
|
margin-top: 5px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.doc-actions {
|
|||
|
|
display: flex;
|
|||
|
|
gap: 5px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.doc-actions .btn {
|
|||
|
|
padding: 4px 8px;
|
|||
|
|
font-size: 12px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 代码树 */
|
|||
|
|
.code-tree {
|
|||
|
|
font-family: 'Consolas', 'Monaco', monospace;
|
|||
|
|
font-size: 13px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.tree-node {
|
|||
|
|
padding: 5px 10px;
|
|||
|
|
cursor: pointer;
|
|||
|
|
transition: all 0.2s;
|
|||
|
|
border-radius: 4px;
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
gap: 8px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.tree-node:hover {
|
|||
|
|
background: rgba(59, 130, 246, 0.1);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.tree-folder {
|
|||
|
|
color: #fbbf24;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.tree-file {
|
|||
|
|
color: var(--text-highlight);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.tree-toggle {
|
|||
|
|
cursor: pointer;
|
|||
|
|
width: 16px;
|
|||
|
|
text-align: center;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 流水线 */
|
|||
|
|
.pipeline {
|
|||
|
|
display: flex;
|
|||
|
|
gap: 10px;
|
|||
|
|
align-items: center;
|
|||
|
|
position: relative;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.pipeline-step {
|
|||
|
|
flex: 1;
|
|||
|
|
text-align: center;
|
|||
|
|
padding: 20px 15px;
|
|||
|
|
background: var(--bg-card);
|
|||
|
|
border: 1px solid var(--border-color);
|
|||
|
|
border-radius: 8px;
|
|||
|
|
position: relative;
|
|||
|
|
transition: all 0.3s;
|
|||
|
|
cursor: pointer;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.pipeline-step:hover {
|
|||
|
|
background: rgba(59, 130, 246, 0.15);
|
|||
|
|
transform: translateY(-3px);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.pipeline-step.active {
|
|||
|
|
background: linear-gradient(135deg, rgba(59, 130, 246, 0.3) 0%, rgba(37, 99, 235, 0.3) 100%);
|
|||
|
|
border-color: var(--accent-blue);
|
|||
|
|
box-shadow: 0 0 20px rgba(59, 130, 246, 0.4);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.pipeline-step.success {
|
|||
|
|
border-color: var(--success);
|
|||
|
|
background: rgba(16, 185, 129, 0.1);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.pipeline-step.error {
|
|||
|
|
border-color: var(--error);
|
|||
|
|
background: rgba(239, 68, 68, 0.1);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.pipeline-step::after {
|
|||
|
|
content: '→';
|
|||
|
|
position: absolute;
|
|||
|
|
right: -15px;
|
|||
|
|
top: 50%;
|
|||
|
|
transform: translateY(-50%);
|
|||
|
|
color: var(--accent-blue);
|
|||
|
|
font-size: 20px;
|
|||
|
|
z-index: 1;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.pipeline-step:last-child::after {
|
|||
|
|
content: '';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.step-icon {
|
|||
|
|
font-size: 28px;
|
|||
|
|
margin-bottom: 10px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.step-name {
|
|||
|
|
font-size: 14px;
|
|||
|
|
font-weight: bold;
|
|||
|
|
margin-bottom: 5px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.step-status {
|
|||
|
|
font-size: 12px;
|
|||
|
|
color: var(--text-secondary);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 滚动条 */
|
|||
|
|
::-webkit-scrollbar {
|
|||
|
|
width: 8px;
|
|||
|
|
height: 8px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
::-webkit-scrollbar-track {
|
|||
|
|
background: rgba(30, 41, 59, 0.5);
|
|||
|
|
border-radius: 4px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
::-webkit-scrollbar-thumb {
|
|||
|
|
background: rgba(59, 130, 246, 0.4);
|
|||
|
|
border-radius: 4px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
::-webkit-scrollbar-thumb:hover {
|
|||
|
|
background: rgba(59, 130, 246, 0.6);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 模态框 */
|
|||
|
|
.modal-content {
|
|||
|
|
background: rgba(30, 41, 59, 0.95);
|
|||
|
|
border: 1px solid var(--border-color);
|
|||
|
|
color: var(--text-primary);
|
|||
|
|
backdrop-filter: blur(15px);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.modal-header {
|
|||
|
|
border-bottom: 1px solid var(--border-color);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.modal-footer {
|
|||
|
|
border-top: 1px solid var(--border-color);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.form-control, .form-select {
|
|||
|
|
background: rgba(51, 65, 85, 0.5);
|
|||
|
|
border: 1px solid var(--border-color);
|
|||
|
|
color: var(--text-primary);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.form-control:focus, .form-select:focus {
|
|||
|
|
background: rgba(51, 65, 85, 0.7);
|
|||
|
|
border-color: var(--accent-blue);
|
|||
|
|
color: var(--text-primary);
|
|||
|
|
box-shadow: 0 0 10px rgba(59, 130, 246, 0.3);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.form-control::placeholder {
|
|||
|
|
color: var(--text-secondary);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.form-label {
|
|||
|
|
color: var(--text-secondary);
|
|||
|
|
margin-bottom: 8px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 向导步骤 */
|
|||
|
|
.wizard-steps {
|
|||
|
|
display: flex;
|
|||
|
|
justify-content: space-between;
|
|||
|
|
margin-bottom: 30px;
|
|||
|
|
position: relative;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.wizard-steps::before {
|
|||
|
|
content: '';
|
|||
|
|
position: absolute;
|
|||
|
|
top: 20px;
|
|||
|
|
left: 0;
|
|||
|
|
right: 0;
|
|||
|
|
height: 2px;
|
|||
|
|
background: var(--border-color);
|
|||
|
|
z-index: 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.wizard-step {
|
|||
|
|
flex: 1;
|
|||
|
|
text-align: center;
|
|||
|
|
position: relative;
|
|||
|
|
z-index: 1;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.wizard-step-number {
|
|||
|
|
width: 40px;
|
|||
|
|
height: 40px;
|
|||
|
|
border-radius: 50%;
|
|||
|
|
background: rgba(51, 65, 85, 0.5);
|
|||
|
|
border: 2px solid var(--border-color);
|
|||
|
|
display: inline-flex;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: center;
|
|||
|
|
margin-bottom: 8px;
|
|||
|
|
font-weight: bold;
|
|||
|
|
transition: all 0.3s;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.wizard-step.active .wizard-step-number {
|
|||
|
|
background: linear-gradient(135deg, var(--accent-blue), #2563eb);
|
|||
|
|
border-color: var(--accent-blue);
|
|||
|
|
box-shadow: 0 0 20px rgba(59, 130, 246, 0.5);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.wizard-step.completed .wizard-step-number {
|
|||
|
|
background: rgba(16, 185, 129, 0.3);
|
|||
|
|
border-color: var(--success);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.wizard-step-label {
|
|||
|
|
font-size: 12px;
|
|||
|
|
color: var(--text-secondary);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 进度条 */
|
|||
|
|
.progress {
|
|||
|
|
background: rgba(51, 65, 85, 0.5);
|
|||
|
|
border-radius: 4px;
|
|||
|
|
height: 8px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.progress-bar {
|
|||
|
|
background: linear-gradient(90deg, var(--accent-blue), var(--accent-cyan));
|
|||
|
|
box-shadow: 0 0 10px rgba(59, 130, 246, 0.5);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.progress-bar-animated {
|
|||
|
|
animation: progress-stripes 1s linear infinite;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
@keyframes progress-stripes {
|
|||
|
|
0% { background-position: 0 0; }
|
|||
|
|
100% { background-position: 40px 0; }
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 高度控制 */
|
|||
|
|
.scrollable-panel {
|
|||
|
|
max-height: calc(100vh - 250px);
|
|||
|
|
overflow-y: auto;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.main-content {
|
|||
|
|
padding: 20px;
|
|||
|
|
min-height: calc(100vh - 80px);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 徽章 */
|
|||
|
|
.badge {
|
|||
|
|
padding: 4px 8px;
|
|||
|
|
border-radius: 4px;
|
|||
|
|
font-size: 11px;
|
|||
|
|
font-weight: normal;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 需求录入方式选择卡片 */
|
|||
|
|
.input-method-card {
|
|||
|
|
padding: 20px;
|
|||
|
|
border: 2px solid var(--border-color);
|
|||
|
|
border-radius: 8px;
|
|||
|
|
cursor: pointer;
|
|||
|
|
transition: all 0.3s;
|
|||
|
|
text-align: center;
|
|||
|
|
background: var(--bg-card);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.input-method-card:hover {
|
|||
|
|
border-color: var(--border-hover);
|
|||
|
|
background: rgba(59, 130, 246, 0.1);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.input-method-card.selected {
|
|||
|
|
border-color: var(--accent-blue);
|
|||
|
|
background: rgba(59, 130, 246, 0.2);
|
|||
|
|
box-shadow: 0 0 20px rgba(59, 130, 246, 0.3);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.input-method-icon {
|
|||
|
|
font-size: 48px;
|
|||
|
|
margin-bottom: 15px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.input-method-title {
|
|||
|
|
font-size: 16px;
|
|||
|
|
font-weight: bold;
|
|||
|
|
margin-bottom: 8px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.input-method-desc {
|
|||
|
|
font-size: 13px;
|
|||
|
|
color: var(--text-secondary);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 语音录制界面 */
|
|||
|
|
.voice-recorder {
|
|||
|
|
text-align: center;
|
|||
|
|
padding: 40px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.mic-button {
|
|||
|
|
width: 120px;
|
|||
|
|
height: 120px;
|
|||
|
|
border-radius: 50%;
|
|||
|
|
border: none;
|
|||
|
|
background: linear-gradient(135deg, var(--accent-blue), #2563eb);
|
|||
|
|
color: white;
|
|||
|
|
font-size: 48px;
|
|||
|
|
cursor: pointer;
|
|||
|
|
transition: all 0.3s;
|
|||
|
|
box-shadow: 0 8px 32px rgba(59, 130, 246, 0.4);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.mic-button:hover {
|
|||
|
|
transform: scale(1.05);
|
|||
|
|
box-shadow: 0 12px 40px rgba(59, 130, 246, 0.6);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.mic-button.recording {
|
|||
|
|
background: linear-gradient(135deg, var(--error), #dc2626);
|
|||
|
|
animation: pulse 1.5s ease-in-out infinite;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
@keyframes pulse {
|
|||
|
|
0%, 100% { transform: scale(1); }
|
|||
|
|
50% { transform: scale(1.1); }
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.recording-time {
|
|||
|
|
font-size: 24px;
|
|||
|
|
margin-top: 20px;
|
|||
|
|
color: var(--text-highlight);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.waveform {
|
|||
|
|
height: 60px;
|
|||
|
|
margin: 20px 0;
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: center;
|
|||
|
|
gap: 3px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.waveform-bar {
|
|||
|
|
width: 4px;
|
|||
|
|
background: var(--accent-blue);
|
|||
|
|
border-radius: 2px;
|
|||
|
|
animation: wave 1s ease-in-out infinite;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
@keyframes wave {
|
|||
|
|
0%, 100% { height: 10px; }
|
|||
|
|
50% { height: 40px; }
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.waveform-bar:nth-child(2) { animation-delay: 0.1s; }
|
|||
|
|
.waveform-bar:nth-child(3) { animation-delay: 0.2s; }
|
|||
|
|
.waveform-bar:nth-child(4) { animation-delay: 0.3s; }
|
|||
|
|
.waveform-bar:nth-child(5) { animation-delay: 0.4s; }
|
|||
|
|
|
|||
|
|
/* 工程类型卡片 */
|
|||
|
|
.project-type-card {
|
|||
|
|
padding: 20px;
|
|||
|
|
border: 2px solid var(--border-color);
|
|||
|
|
border-radius: 8px;
|
|||
|
|
cursor: pointer;
|
|||
|
|
transition: all 0.3s;
|
|||
|
|
background: var(--bg-card);
|
|||
|
|
height: 100%;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.project-type-card:hover {
|
|||
|
|
border-color: var(--border-hover);
|
|||
|
|
transform: translateY(-5px);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.project-type-card.selected {
|
|||
|
|
border-color: var(--accent-blue);
|
|||
|
|
background: rgba(59, 130, 246, 0.2);
|
|||
|
|
box-shadow: 0 0 20px rgba(59, 130, 246, 0.3);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.project-type-icon {
|
|||
|
|
font-size: 48px;
|
|||
|
|
margin-bottom: 15px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* Toast 通知 */
|
|||
|
|
.toast-container {
|
|||
|
|
position: fixed;
|
|||
|
|
top: 80px;
|
|||
|
|
right: 20px;
|
|||
|
|
z-index: 9999;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.toast {
|
|||
|
|
background: rgba(30, 41, 59, 0.95);
|
|||
|
|
border: 1px solid var(--border-color);
|
|||
|
|
color: var(--text-primary);
|
|||
|
|
backdrop-filter: blur(15px);
|
|||
|
|
min-width: 300px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.toast.success {
|
|||
|
|
border-color: var(--success);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.toast.error {
|
|||
|
|
border-color: var(--error);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.toast.warning {
|
|||
|
|
border-color: var(--warning);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 功能模块卡片 */
|
|||
|
|
.module-card {
|
|||
|
|
padding: 20px;
|
|||
|
|
background: var(--bg-card);
|
|||
|
|
border: 1px solid var(--border-color);
|
|||
|
|
border-radius: 8px;
|
|||
|
|
transition: all 0.3s;
|
|||
|
|
height: 100%;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.module-card:hover {
|
|||
|
|
border-color: var(--border-hover);
|
|||
|
|
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
|
|||
|
|
transform: translateY(-5px);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.module-icon {
|
|||
|
|
font-size: 36px;
|
|||
|
|
margin-bottom: 10px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.complexity-stars {
|
|||
|
|
color: var(--warning);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 下拉菜单 */
|
|||
|
|
.dropdown-menu {
|
|||
|
|
background: rgba(30, 41, 59, 0.95);
|
|||
|
|
border: 1px solid var(--border-color);
|
|||
|
|
backdrop-filter: blur(15px);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.dropdown-item {
|
|||
|
|
color: var(--text-primary);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.dropdown-item:hover {
|
|||
|
|
background: rgba(59, 130, 246, 0.2);
|
|||
|
|
color: var(--text-primary);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 右键菜单 */
|
|||
|
|
.context-menu {
|
|||
|
|
position: fixed;
|
|||
|
|
background: rgba(30, 41, 59, 0.95);
|
|||
|
|
border: 1px solid var(--border-color);
|
|||
|
|
border-radius: 8px;
|
|||
|
|
padding: 8px 0;
|
|||
|
|
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
|
|||
|
|
backdrop-filter: blur(15px);
|
|||
|
|
z-index: 9999;
|
|||
|
|
display: none;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.context-menu-item {
|
|||
|
|
padding: 8px 20px;
|
|||
|
|
cursor: pointer;
|
|||
|
|
transition: all 0.2s;
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
gap: 10px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.context-menu-item:hover {
|
|||
|
|
background: rgba(59, 130, 246, 0.2);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 文件上传区域 */
|
|||
|
|
.upload-area {
|
|||
|
|
border: 2px dashed var(--border-color);
|
|||
|
|
border-radius: 8px;
|
|||
|
|
padding: 40px;
|
|||
|
|
text-align: center;
|
|||
|
|
cursor: pointer;
|
|||
|
|
transition: all 0.3s;
|
|||
|
|
background: var(--bg-card);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.upload-area:hover {
|
|||
|
|
border-color: var(--accent-blue);
|
|||
|
|
background: rgba(59, 130, 246, 0.1);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.upload-area.dragover {
|
|||
|
|
border-color: var(--accent-blue);
|
|||
|
|
background: rgba(59, 130, 246, 0.2);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 标签输入 */
|
|||
|
|
.tag-input-container {
|
|||
|
|
display: flex;
|
|||
|
|
flex-wrap: wrap;
|
|||
|
|
gap: 8px;
|
|||
|
|
padding: 8px;
|
|||
|
|
background: rgba(51, 65, 85, 0.5);
|
|||
|
|
border: 1px solid var(--border-color);
|
|||
|
|
border-radius: 6px;
|
|||
|
|
min-height: 42px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.tag-item {
|
|||
|
|
background: rgba(59, 130, 246, 0.2);
|
|||
|
|
border: 1px solid var(--accent-blue);
|
|||
|
|
padding: 4px 10px;
|
|||
|
|
border-radius: 4px;
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
gap: 5px;
|
|||
|
|
font-size: 13px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.tag-remove {
|
|||
|
|
cursor: pointer;
|
|||
|
|
color: var(--error);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 响应式调整 */
|
|||
|
|
@media (max-width: 1200px) {
|
|||
|
|
.main-content {
|
|||
|
|
padding: 15px;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
@media (max-width: 768px) {
|
|||
|
|
.logo {
|
|||
|
|
font-size: 18px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.pipeline {
|
|||
|
|
flex-direction: column;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.pipeline-step::after {
|
|||
|
|
content: '↓';
|
|||
|
|
right: 50%;
|
|||
|
|
top: auto;
|
|||
|
|
bottom: -20px;
|
|||
|
|
transform: translateX(50%);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
</style>
|
|||
|
|
</head>
|
|||
|
|
<body>
|
|||
|
|
<!-- 顶部导航栏 -->
|
|||
|
|
<nav class="top-navbar">
|
|||
|
|
<div class="container-fluid">
|
|||
|
|
<div class="d-flex justify-content-between align-items-center">
|
|||
|
|
<div class="logo">
|
|||
|
|
<i class="bi bi-lightning-charge-fill"></i> 智能低代码开发平台
|
|||
|
|
</div>
|
|||
|
|
<div class="d-flex gap-2 align-items-center">
|
|||
|
|
<button class="btn btn-sm">
|
|||
|
|
<i class="bi bi-search"></i>
|
|||
|
|
</button>
|
|||
|
|
<button class="btn btn-sm">
|
|||
|
|
<i class="bi bi-gear"></i>
|
|||
|
|
</button>
|
|||
|
|
<button class="btn btn-sm">
|
|||
|
|
<i class="bi bi-question-circle"></i>
|
|||
|
|
</button>
|
|||
|
|
<button class="btn btn-sm position-relative">
|
|||
|
|
<i class="bi bi-bell"></i>
|
|||
|
|
<span class="notification-badge">3</span>
|
|||
|
|
</button>
|
|||
|
|
<div class="d-flex align-items-center gap-2 ms-2">
|
|||
|
|
<img src="https://via.placeholder.com/32" class="rounded-circle" alt="用户头像">
|
|||
|
|
<span style="color: var(--text-primary);">管理员</span>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</nav>
|
|||
|
|
|
|||
|
|
<!-- 主内容区 -->
|
|||
|
|
<div class="main-content">
|
|||
|
|
<div class="row g-3">
|
|||
|
|
<!-- 左侧:项目列表 -->
|
|||
|
|
<div class="col-lg-3">
|
|||
|
|
<div class="panel">
|
|||
|
|
<div class="panel-header">项目列表</div>
|
|||
|
|
<button class="btn btn-primary w-100 mb-3" data-bs-toggle="modal" data-bs-target="#newProjectModal">
|
|||
|
|
<i class="bi bi-plus-circle"></i> 新建项目
|
|||
|
|
</button>
|
|||
|
|
<div class="project-search">
|
|||
|
|
<input type="text" placeholder="搜索项目..." class="form-control form-control-sm">
|
|||
|
|
</div>
|
|||
|
|
<div class="scrollable-panel">
|
|||
|
|
<div class="project-item active" onclick="selectProject(this, 0)">
|
|||
|
|
<div class="project-name">智能监控系统</div>
|
|||
|
|
<div class="project-desc">基于AI的实时监控平台</div>
|
|||
|
|
<div class="project-meta">
|
|||
|
|
<span>2024-01-15</span>
|
|||
|
|
<span class="project-status status-active">进行中</span>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="project-item" onclick="selectProject(this, 1)">
|
|||
|
|
<div class="project-name">数据分析平台</div>
|
|||
|
|
<div class="project-desc">大数据可视化分析工具</div>
|
|||
|
|
<div class="project-meta">
|
|||
|
|
<span>2024-01-10</span>
|
|||
|
|
<span class="project-status status-completed">已完成</span>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="project-item" onclick="selectProject(this, 2)">
|
|||
|
|
<div class="project-name">物联网管理系统</div>
|
|||
|
|
<div class="project-desc">设备管理与数据采集</div>
|
|||
|
|
<div class="project-meta">
|
|||
|
|
<span>2024-01-05</span>
|
|||
|
|
<span class="project-status status-active">进行中</span>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="project-item" onclick="selectProject(this, 3)">
|
|||
|
|
<div class="project-name">电商管理系统</div>
|
|||
|
|
<div class="project-desc">订单、库存、用户管理</div>
|
|||
|
|
<div class="project-meta">
|
|||
|
|
<span>2023-12-28</span>
|
|||
|
|
<span class="project-status status-paused">已暂停</span>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- 中间:需求与功能 -->
|
|||
|
|
<div class="col-lg-6">
|
|||
|
|
<div class="row g-3">
|
|||
|
|
<!-- 需求板块 -->
|
|||
|
|
<div class="col-12">
|
|||
|
|
<div class="panel">
|
|||
|
|
<div class="panel-header">需求管理</div>
|
|||
|
|
<ul class="nav nav-tabs mb-3" role="tablist">
|
|||
|
|
<li class="nav-item" role="presentation">
|
|||
|
|
<button class="nav-link active" data-bs-toggle="tab" data-bs-target="#originalReq" type="button">
|
|||
|
|
原始需求
|
|||
|
|
</button>
|
|||
|
|
</li>
|
|||
|
|
<li class="nav-item" role="presentation">
|
|||
|
|
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#reqList" type="button">
|
|||
|
|
需求列表
|
|||
|
|
</button>
|
|||
|
|
</li>
|
|||
|
|
</ul>
|
|||
|
|
<div class="tab-content scrollable-panel" style="max-height: 300px;">
|
|||
|
|
<div class="tab-pane fade show active" id="originalReq">
|
|||
|
|
<div class="alert" style="background: rgba(59, 130, 246, 0.1); border-color: var(--border-color); color: var(--text-primary);">
|
|||
|
|
<div class="d-flex justify-content-between align-items-start mb-2">
|
|||
|
|
<div>
|
|||
|
|
<i class="bi bi-info-circle text-primary"></i>
|
|||
|
|
<strong class="ms-2">录入方式:</strong>
|
|||
|
|
<span class="badge bg-primary ms-2">文本输入</span>
|
|||
|
|
</div>
|
|||
|
|
<small class="text-secondary">2024-01-15 10:30</small>
|
|||
|
|
</div>
|
|||
|
|
<hr style="border-color: var(--border-color);">
|
|||
|
|
<p class="mb-0">
|
|||
|
|
需要开发一个智能监控系统,该系统应具备以下核心功能:<br><br>
|
|||
|
|
1. 用户登录认证:支持用户通过用户名和密码进行登录,并集成多因素认证机制,确保账户安全性。<br>
|
|||
|
|
2. 实时数据监控:平台需要实时采集设备数据,并通过可视化图表展示关键指标,支持异常告警。<br>
|
|||
|
|
3. 数据导出功能:支持将监控数据导出为Excel、CSV等格式,便于后续分析和报告生成。<br>
|
|||
|
|
4. 系统日志管理:记录所有系统操作日志,支持日志查询和审计。
|
|||
|
|
</p>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="tab-pane fade" id="reqList">
|
|||
|
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
|||
|
|
<div>
|
|||
|
|
<input type="checkbox" class="form-check-input me-2" id="selectAll">
|
|||
|
|
<label for="selectAll">全选</label>
|
|||
|
|
</div>
|
|||
|
|
<div class="btn-group btn-group-sm">
|
|||
|
|
<button class="btn btn-outline-primary">
|
|||
|
|
<i class="bi bi-pencil"></i> 批量编辑
|
|||
|
|
</button>
|
|||
|
|
<button class="btn btn-outline-danger">
|
|||
|
|
<i class="bi bi-trash"></i> 批量删除
|
|||
|
|
</button>
|
|||
|
|
<button class="btn btn-outline-success">
|
|||
|
|
<i class="bi bi-plus"></i> 添加需求
|
|||
|
|
</button>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="item-card">
|
|||
|
|
<div class="d-flex align-items-start gap-2">
|
|||
|
|
<input class="form-check-input mt-1" type="checkbox" checked>
|
|||
|
|
<div class="flex-grow-1">
|
|||
|
|
<div class="d-flex justify-content-between align-items-start mb-2">
|
|||
|
|
<div class="item-title">REQ-001 用户登录认证</div>
|
|||
|
|
<div>
|
|||
|
|
<span class="badge bg-success">高 95%</span>
|
|||
|
|
<span class="badge bg-warning text-dark ms-1">高优先级</span>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="item-desc">系统需要支持用户通过用户名和密码进行登录,并集成多因素认证机制,确保账户安全性。</div>
|
|||
|
|
<div class="mt-2">
|
|||
|
|
<button class="btn btn-sm btn-outline-primary">
|
|||
|
|
<i class="bi bi-pencil"></i>
|
|||
|
|
</button>
|
|||
|
|
<button class="btn btn-sm btn-outline-danger ms-1">
|
|||
|
|
<i class="bi bi-trash"></i>
|
|||
|
|
</button>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="item-card">
|
|||
|
|
<div class="d-flex align-items-start gap-2">
|
|||
|
|
<input class="form-check-input mt-1" type="checkbox" checked>
|
|||
|
|
<div class="flex-grow-1">
|
|||
|
|
<div class="d-flex justify-content-between align-items-start mb-2">
|
|||
|
|
<div class="item-title">REQ-002 实时数据监控</div>
|
|||
|
|
<div>
|
|||
|
|
<span class="badge bg-success">高 92%</span>
|
|||
|
|
<span class="badge bg-warning text-dark ms-1">高优先级</span>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="item-desc">平台需要实时采集设备数据,并通过可视化图表展示关键指标,支持异常告警。</div>
|
|||
|
|
<div class="mt-2">
|
|||
|
|
<button class="btn btn-sm btn-outline-primary">
|
|||
|
|
<i class="bi bi-pencil"></i>
|
|||
|
|
</button>
|
|||
|
|
<button class="btn btn-sm btn-outline-danger ms-1">
|
|||
|
|
<i class="bi bi-trash"></i>
|
|||
|
|
</button>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="item-card">
|
|||
|
|
<div class="d-flex align-items-start gap-2">
|
|||
|
|
<input class="form-check-input mt-1" type="checkbox">
|
|||
|
|
<div class="flex-grow-1">
|
|||
|
|
<div class="d-flex justify-content-between align-items-start mb-2">
|
|||
|
|
<div class="item-title">REQ-003 数据导出功能</div>
|
|||
|
|
<div>
|
|||
|
|
<span class="badge bg-warning text-dark">中 78%</span>
|
|||
|
|
<span class="badge bg-info ms-1">中优先级</span>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="item-desc">支持将监控数据导出为Excel、CSV等格式,便于后续分析和报告生成。</div>
|
|||
|
|
<div class="mt-2">
|
|||
|
|
<button class="btn btn-sm btn-outline-primary">
|
|||
|
|
<i class="bi bi-pencil"></i>
|
|||
|
|
</button>
|
|||
|
|
<button class="btn btn-sm btn-outline-danger ms-1">
|
|||
|
|
<i class="bi bi-trash"></i>
|
|||
|
|
</button>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- 功能板块 -->
|
|||
|
|
<div class="col-12">
|
|||
|
|
<div class="panel">
|
|||
|
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
|||
|
|
<div class="panel-header mb-0">功能模块</div>
|
|||
|
|
<div class="btn-group btn-group-sm">
|
|||
|
|
<button class="btn btn-outline-primary active">
|
|||
|
|
<i class="bi bi-grid-3x3-gap"></i>
|
|||
|
|
</button>
|
|||
|
|
<button class="btn btn-outline-primary">
|
|||
|
|
<i class="bi bi-list-ul"></i>
|
|||
|
|
</button>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="scrollable-panel" style="max-height: 300px;">
|
|||
|
|
<div class="row g-2">
|
|||
|
|
<div class="col-md-6">
|
|||
|
|
<div class="module-card">
|
|||
|
|
<div class="module-icon text-warning">
|
|||
|
|
<i class="bi bi-shield-lock"></i>
|
|||
|
|
</div>
|
|||
|
|
<div class="item-title">用户认证模块</div>
|
|||
|
|
<div class="mb-2">
|
|||
|
|
<span class="badge bg-primary">REQ-001</span>
|
|||
|
|
</div>
|
|||
|
|
<div class="item-desc mb-2">实现登录、注册、密码重置、多因素认证等功能</div>
|
|||
|
|
<div class="mb-2">
|
|||
|
|
<span class="badge bg-secondary">Spring Boot</span>
|
|||
|
|
<span class="badge bg-secondary ms-1">JWT</span>
|
|||
|
|
</div>
|
|||
|
|
<div class="progress mb-2" style="height: 6px;">
|
|||
|
|
<div class="progress-bar" style="width: 85%"></div>
|
|||
|
|
</div>
|
|||
|
|
<div class="d-flex gap-1">
|
|||
|
|
<button class="btn btn-sm btn-outline-primary flex-grow-1">
|
|||
|
|
<i class="bi bi-gear"></i> 配置
|
|||
|
|
</button>
|
|||
|
|
<button class="btn btn-sm btn-outline-info">
|
|||
|
|
<i class="bi bi-code"></i>
|
|||
|
|
</button>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="col-md-6">
|
|||
|
|
<div class="module-card">
|
|||
|
|
<div class="module-icon text-success">
|
|||
|
|
<i class="bi bi-graph-up"></i>
|
|||
|
|
</div>
|
|||
|
|
<div class="item-title">数据监控模块</div>
|
|||
|
|
<div class="mb-2">
|
|||
|
|
<span class="badge bg-primary">REQ-002</span>
|
|||
|
|
</div>
|
|||
|
|
<div class="item-desc mb-2">实时数据采集、可视化展示、告警管理</div>
|
|||
|
|
<div class="mb-2">
|
|||
|
|
<span class="badge bg-secondary">ECharts</span>
|
|||
|
|
<span class="badge bg-secondary ms-1">WebSocket</span>
|
|||
|
|
</div>
|
|||
|
|
<div class="progress mb-2" style="height: 6px;">
|
|||
|
|
<div class="progress-bar" style="width: 60%"></div>
|
|||
|
|
</div>
|
|||
|
|
<div class="d-flex gap-1">
|
|||
|
|
<button class="btn btn-sm btn-outline-primary flex-grow-1">
|
|||
|
|
<i class="bi bi-gear"></i> 配置
|
|||
|
|
</button>
|
|||
|
|
<button class="btn btn-sm btn-outline-info">
|
|||
|
|
<i class="bi bi-code"></i>
|
|||
|
|
</button>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="col-md-6">
|
|||
|
|
<div class="module-card">
|
|||
|
|
<div class="module-icon text-info">
|
|||
|
|
<i class="bi bi-folder"></i>
|
|||
|
|
</div>
|
|||
|
|
<div class="item-title">数据管理模块</div>
|
|||
|
|
<div class="mb-2">
|
|||
|
|
<span class="badge bg-primary">REQ-003</span>
|
|||
|
|
</div>
|
|||
|
|
<div class="item-desc mb-2">数据查询、导出、备份与恢复</div>
|
|||
|
|
<div class="mb-2">
|
|||
|
|
<span class="badge bg-secondary">POI</span>
|
|||
|
|
<span class="badge bg-secondary ms-1">MySQL</span>
|
|||
|
|
</div>
|
|||
|
|
<div class="progress mb-2" style="height: 6px;">
|
|||
|
|
<div class="progress-bar" style="width: 40%"></div>
|
|||
|
|
</div>
|
|||
|
|
<div class="d-flex gap-1">
|
|||
|
|
<button class="btn btn-sm btn-outline-primary flex-grow-1">
|
|||
|
|
<i class="bi bi-gear"></i> 配置
|
|||
|
|
</button>
|
|||
|
|
<button class="btn btn-sm btn-outline-info">
|
|||
|
|
<i class="bi bi-code"></i>
|
|||
|
|
</button>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- 右侧:文档与代码 -->
|
|||
|
|
<div class="col-lg-3">
|
|||
|
|
<div class="row g-3">
|
|||
|
|
<!-- 文档板块 -->
|
|||
|
|
<div class="col-12">
|
|||
|
|
<div class="panel">
|
|||
|
|
<div class="panel-header">项目文档</div>
|
|||
|
|
<div class="scrollable-panel" style="max-height: 300px;">
|
|||
|
|
<div class="doc-item">
|
|||
|
|
<div class="doc-name">
|
|||
|
|
<i class="bi bi-file-earmark-text fs-5"></i>
|
|||
|
|
<div>
|
|||
|
|
<div>需求规格说明书</div>
|
|||
|
|
<div class="doc-meta">2.3 MB · 2024-01-15</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="doc-actions">
|
|||
|
|
<button class="btn btn-sm btn-outline-primary" onclick="viewDocument('需求规格说明书')">
|
|||
|
|
<i class="bi bi-eye"></i>
|
|||
|
|
</button>
|
|||
|
|
<div class="btn-group">
|
|||
|
|
<button class="btn btn-sm btn-outline-success dropdown-toggle" data-bs-toggle="dropdown">
|
|||
|
|
<i class="bi bi-download"></i>
|
|||
|
|
</button>
|
|||
|
|
<ul class="dropdown-menu">
|
|||
|
|
<li><a class="dropdown-item" href="#" onclick="downloadDocument('需求规格说明书.docx')">Word (.docx)</a></li>
|
|||
|
|
<li><a class="dropdown-item" href="#" onclick="downloadDocument('需求规格说明书.pdf')">PDF (.pdf)</a></li>
|
|||
|
|
<li><a class="dropdown-item" href="#" onclick="downloadDocument('需求规格说明书.md')">Markdown (.md)</a></li>
|
|||
|
|
</ul>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="doc-item">
|
|||
|
|
<div class="doc-name">
|
|||
|
|
<i class="bi bi-file-earmark-code fs-5"></i>
|
|||
|
|
<div>
|
|||
|
|
<div>功能设计文档</div>
|
|||
|
|
<div class="doc-meta">1.8 MB · 2024-01-15</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="doc-actions">
|
|||
|
|
<button class="btn btn-sm btn-outline-primary" onclick="viewDocument('功能设计文档')">
|
|||
|
|
<i class="bi bi-eye"></i>
|
|||
|
|
</button>
|
|||
|
|
<div class="btn-group">
|
|||
|
|
<button class="btn btn-sm btn-outline-success dropdown-toggle" data-bs-toggle="dropdown">
|
|||
|
|
<i class="bi bi-download"></i>
|
|||
|
|
</button>
|
|||
|
|
<ul class="dropdown-menu">
|
|||
|
|
<li><a class="dropdown-item" href="#" onclick="downloadDocument('功能设计文档.docx')">Word (.docx)</a></li>
|
|||
|
|
<li><a class="dropdown-item" href="#" onclick="downloadDocument('功能设计文档.pdf')">PDF (.pdf)</a></li>
|
|||
|
|
<li><a class="dropdown-item" href="#" onclick="downloadDocument('功能设计文档.md')">Markdown (.md)</a></li>
|
|||
|
|
</ul>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="doc-item">
|
|||
|
|
<div class="doc-name">
|
|||
|
|
<i class="bi bi-diagram-3 fs-5"></i>
|
|||
|
|
<div>
|
|||
|
|
<div>软件概要设计文档</div>
|
|||
|
|
<div class="doc-meta">3.1 MB · 2024-01-15</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="doc-actions">
|
|||
|
|
<button class="btn btn-sm btn-outline-primary" onclick="viewDocument('软件概要设计文档')">
|
|||
|
|
<i class="bi bi-eye"></i>
|
|||
|
|
</button>
|
|||
|
|
<div class="btn-group">
|
|||
|
|
<button class="btn btn-sm btn-outline-success dropdown-toggle" data-bs-toggle="dropdown">
|
|||
|
|
<i class="bi bi-download"></i>
|
|||
|
|
</button>
|
|||
|
|
<ul class="dropdown-menu">
|
|||
|
|
<li><a class="dropdown-item" href="#" onclick="downloadDocument('软件概要设计文档.docx')">Word (.docx)</a></li>
|
|||
|
|
<li><a class="dropdown-item" href="#" onclick="downloadDocument('软件概要设计文档.pdf')">PDF (.pdf)</a></li>
|
|||
|
|
<li><a class="dropdown-item" href="#" onclick="downloadDocument('软件概要设计文档.md')">Markdown (.md)</a></li>
|
|||
|
|
</ul>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="doc-item">
|
|||
|
|
<div class="doc-name">
|
|||
|
|
<i class="bi bi-file-earmark-ruled fs-5"></i>
|
|||
|
|
<div>
|
|||
|
|
<div>软件详细设计文档</div>
|
|||
|
|
<div class="doc-meta">4.5 MB · 2024-01-15</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="doc-actions">
|
|||
|
|
<button class="btn btn-sm btn-outline-primary" onclick="viewDocument('软件详细设计文档')">
|
|||
|
|
<i class="bi bi-eye"></i>
|
|||
|
|
</button>
|
|||
|
|
<div class="btn-group">
|
|||
|
|
<button class="btn btn-sm btn-outline-success dropdown-toggle" data-bs-toggle="dropdown">
|
|||
|
|
<i class="bi bi-download"></i>
|
|||
|
|
</button>
|
|||
|
|
<ul class="dropdown-menu">
|
|||
|
|
<li><a class="dropdown-item" href="#" onclick="downloadDocument('软件详细设计文档.docx')">Word (.docx)</a></li>
|
|||
|
|
<li><a class="dropdown-item" href="#" onclick="downloadDocument('软件详细设计文档.pdf')">PDF (.pdf)</a></li>
|
|||
|
|
<li><a class="dropdown-item" href="#" onclick="downloadDocument('软件详细设计文档.md')">Markdown (.md)</a></li>
|
|||
|
|
</ul>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="doc-item">
|
|||
|
|
<div class="doc-name">
|
|||
|
|
<i class="bi bi-book fs-5"></i>
|
|||
|
|
<div>
|
|||
|
|
<div>接口文档</div>
|
|||
|
|
<div class="doc-meta">1.2 MB · 2024-01-15</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="doc-actions">
|
|||
|
|
<button class="btn btn-sm btn-outline-primary" onclick="viewDocument('接口文档')">
|
|||
|
|
<i class="bi bi-eye"></i>
|
|||
|
|
</button>
|
|||
|
|
<div class="btn-group">
|
|||
|
|
<button class="btn btn-sm btn-outline-success dropdown-toggle" data-bs-toggle="dropdown">
|
|||
|
|
<i class="bi bi-download"></i>
|
|||
|
|
</button>
|
|||
|
|
<ul class="dropdown-menu">
|
|||
|
|
<li><a class="dropdown-item" href="#" onclick="downloadDocument('接口文档.html')">HTML (.html)</a></li>
|
|||
|
|
<li><a class="dropdown-item" href="#" onclick="downloadDocument('接口文档.pdf')">PDF (.pdf)</a></li>
|
|||
|
|
</ul>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- 代码板块 -->
|
|||
|
|
<div class="col-12">
|
|||
|
|
<div class="panel">
|
|||
|
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
|||
|
|
<div class="panel-header mb-0">代码结构</div>
|
|||
|
|
<div class="btn-group btn-group-sm">
|
|||
|
|
<button class="btn btn-outline-success" onclick="downloadCode('all')">
|
|||
|
|
<i class="bi bi-download"></i> 全部
|
|||
|
|
</button>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="scrollable-panel code-tree" style="max-height: 300px;">
|
|||
|
|
<div class="tree-node tree-folder" onclick="toggleFolder(this)">
|
|||
|
|
<span class="tree-toggle">▼</span>
|
|||
|
|
<i class="bi bi-folder-fill"></i>
|
|||
|
|
<span>智能监控系统</span>
|
|||
|
|
</div>
|
|||
|
|
<div class="tree-children">
|
|||
|
|
<div class="tree-node tree-folder" onclick="toggleFolder(this)" style="padding-left: 30px;">
|
|||
|
|
<span class="tree-toggle">▼</span>
|
|||
|
|
<i class="bi bi-folder-fill"></i>
|
|||
|
|
<span>src</span>
|
|||
|
|
</div>
|
|||
|
|
<div class="tree-children">
|
|||
|
|
<div class="tree-node tree-file" style="padding-left: 50px;">
|
|||
|
|
<span class="tree-toggle"></span>
|
|||
|
|
<i class="bi bi-file-earmark-code"></i>
|
|||
|
|
<span>Application.java</span>
|
|||
|
|
<button class="btn btn-sm btn-outline-success ms-auto" onclick="downloadCode('Application.java')">
|
|||
|
|
<i class="bi bi-download"></i>
|
|||
|
|
</button>
|
|||
|
|
</div>
|
|||
|
|
<div class="tree-node tree-folder" onclick="toggleFolder(this)" style="padding-left: 50px;">
|
|||
|
|
<span class="tree-toggle">▼</span>
|
|||
|
|
<i class="bi bi-folder-fill"></i>
|
|||
|
|
<span>modules</span>
|
|||
|
|
</div>
|
|||
|
|
<div class="tree-children">
|
|||
|
|
<div class="tree-node tree-folder" onclick="toggleFolder(this)" style="padding-left: 70px;">
|
|||
|
|
<span class="tree-toggle">▼</span>
|
|||
|
|
<i class="bi bi-folder-fill"></i>
|
|||
|
|
<span>auth</span>
|
|||
|
|
</div>
|
|||
|
|
<div class="tree-children">
|
|||
|
|
<div class="tree-node tree-file" style="padding-left: 90px;">
|
|||
|
|
<span class="tree-toggle"></span>
|
|||
|
|
<i class="bi bi-file-earmark-code"></i>
|
|||
|
|
<span>AuthController.java</span>
|
|||
|
|
<button class="btn btn-sm btn-outline-success ms-auto" onclick="downloadCode('AuthController.java')">
|
|||
|
|
<i class="bi bi-download"></i>
|
|||
|
|
</button>
|
|||
|
|
</div>
|
|||
|
|
<div class="tree-node tree-file" style="padding-left: 90px;">
|
|||
|
|
<span class="tree-toggle"></span>
|
|||
|
|
<i class="bi bi-file-earmark-code"></i>
|
|||
|
|
<span>AuthService.java</span>
|
|||
|
|
<button class="btn btn-sm btn-outline-success ms-auto" onclick="downloadCode('AuthService.java')">
|
|||
|
|
<i class="bi bi-download"></i>
|
|||
|
|
</button>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="tree-node tree-folder" onclick="toggleFolder(this)" style="padding-left: 70px;">
|
|||
|
|
<span class="tree-toggle">▶</span>
|
|||
|
|
<i class="bi bi-folder-fill"></i>
|
|||
|
|
<span>monitor</span>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="tree-node tree-folder" onclick="toggleFolder(this)" style="padding-left: 50px;">
|
|||
|
|
<span class="tree-toggle">▶</span>
|
|||
|
|
<i class="bi bi-folder-fill"></i>
|
|||
|
|
<span>utils</span>
|
|||
|
|
</div>
|
|||
|
|
<div class="tree-node tree-folder" onclick="toggleFolder(this)" style="padding-left: 50px;">
|
|||
|
|
<span class="tree-toggle">▶</span>
|
|||
|
|
<i class="bi bi-folder-fill"></i>
|
|||
|
|
<span>config</span>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="tree-node tree-file" style="padding-left: 30px;">
|
|||
|
|
<span class="tree-toggle"></span>
|
|||
|
|
<i class="bi bi-file-earmark-text"></i>
|
|||
|
|
<span>pom.xml</span>
|
|||
|
|
<button class="btn btn-sm btn-outline-success ms-auto" onclick="downloadCode('pom.xml')">
|
|||
|
|
<i class="bi bi-download"></i>
|
|||
|
|
</button>
|
|||
|
|
</div>
|
|||
|
|
<div class="tree-node tree-file" style="padding-left: 30px;">
|
|||
|
|
<span class="tree-toggle"></span>
|
|||
|
|
<i class="bi bi-file-earmark-text"></i>
|
|||
|
|
<span>README.md</span>
|
|||
|
|
<button class="btn btn-sm btn-outline-success ms-auto" onclick="downloadCode('README.md')">
|
|||
|
|
<i class="bi bi-download"></i>
|
|||
|
|
</button>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- 底部:操作流水线 -->
|
|||
|
|
<div class="col-12">
|
|||
|
|
<div class="panel">
|
|||
|
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
|||
|
|
<div class="panel-header mb-0">流水线操作 </div>
|
|||
|
|
<div class="btn-group btn-group-sm">
|
|||
|
|
<button class="btn btn-success" onclick="startPipeline()">
|
|||
|
|
<i class="bi bi-play-fill"></i> 启动
|
|||
|
|
</button>
|
|||
|
|
<button class="btn btn-warning" onclick="stopPipeline()">
|
|||
|
|
<i class="bi bi-stop-fill"></i> 停止
|
|||
|
|
</button>
|
|||
|
|
<button class="btn btn-info" onclick="restartPipeline()">
|
|||
|
|
<i class="bi bi-arrow-clockwise"></i> 重新运行
|
|||
|
|
</button>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="pipeline">
|
|||
|
|
<div class="pipeline-step success" onclick="showPipelineDetail('scan')">
|
|||
|
|
<div class="step-icon"><i class="bi bi-search"></i></div>
|
|||
|
|
<div class="step-name">静态扫描</div>
|
|||
|
|
<div class="step-status">已完成 · 2分15秒</div>
|
|||
|
|
<small class="text-success">SonarQube</small>
|
|||
|
|
</div>
|
|||
|
|
<div class="pipeline-step active" onclick="showPipelineDetail('build')">
|
|||
|
|
<div class="step-icon"><i class="bi bi-hammer"></i></div>
|
|||
|
|
<div class="step-name">编译构建</div>
|
|||
|
|
<div class="step-status">进行中 · 65%</div>
|
|||
|
|
<div class="progress mt-2" style="height: 4px;">
|
|||
|
|
<div class="progress-bar progress-bar-striped progress-bar-animated" style="width: 65%"></div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="pipeline-step" onclick="showPipelineDetail('package')">
|
|||
|
|
<div class="step-icon"><i class="bi bi-box-seam"></i></div>
|
|||
|
|
<div class="step-name">打包</div>
|
|||
|
|
<div class="step-status">等待中</div>
|
|||
|
|
<small class="text-secondary">JAR</small>
|
|||
|
|
</div>
|
|||
|
|
<div class="pipeline-step" onclick="showPipelineDetail('deploy')">
|
|||
|
|
<div class="step-icon"><i class="bi bi-rocket-takeoff"></i></div>
|
|||
|
|
<div class="step-name">部署</div>
|
|||
|
|
<div class="step-status">等待中</div>
|
|||
|
|
<small class="text-secondary">测试环境</small>
|
|||
|
|
</div>
|
|||
|
|
<div class="pipeline-step" onclick="showPipelineDetail('verify')">
|
|||
|
|
<div class="step-icon"><i class="bi bi-check-circle"></i></div>
|
|||
|
|
<div class="step-name">验证测试</div>
|
|||
|
|
<div class="step-status">等待中</div>
|
|||
|
|
<small class="text-secondary">0/25 通过</small>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="mt-3 text-center">
|
|||
|
|
<small class="text-secondary">总耗时: 3分42秒 · 预计剩余: 2分18秒</small>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- 新建项目模态框 -->
|
|||
|
|
<div class="modal fade" id="newProjectModal" tabindex="-1" data-bs-backdrop="static">
|
|||
|
|
<div class="modal-dialog modal-xl modal-dialog-centered modal-dialog-scrollable">
|
|||
|
|
<div class="modal-content">
|
|||
|
|
<div class="modal-header">
|
|||
|
|
<h5 class="modal-title">
|
|||
|
|
<i class="bi bi-plus-circle"></i> 新建项目
|
|||
|
|
</h5>
|
|||
|
|
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
|
|||
|
|
</div>
|
|||
|
|
<div class="modal-body">
|
|||
|
|
<!-- 步骤导航 -->
|
|||
|
|
<div class="wizard-steps">
|
|||
|
|
<div class="wizard-step active" id="wizardStep1">
|
|||
|
|
<div class="wizard-step-number">1</div>
|
|||
|
|
<div class="wizard-step-label">基本信息</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="wizard-step" id="wizardStep2">
|
|||
|
|
<div class="wizard-step-number">2</div>
|
|||
|
|
<div class="wizard-step-label">需求分析</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="wizard-step" id="wizardStep3">
|
|||
|
|
<div class="wizard-step-number">3</div>
|
|||
|
|
<div class="wizard-step-label">功能生成</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="wizard-step" id="wizardStep4">
|
|||
|
|
<div class="wizard-step-number">4</div>
|
|||
|
|
<div class="wizard-step-label">工程生成</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- 步骤1: 基本信息 -->
|
|||
|
|
<div id="step1Content" class="wizard-content">
|
|||
|
|
<div class="mb-3">
|
|||
|
|
<label class="form-label">项目名称 <span class="text-danger">*</span></label>
|
|||
|
|
<input type="text" class="form-control" id="projectName" placeholder="请输入项目名称" maxlength="50">
|
|||
|
|
<small class="text-secondary">已输入 <span id="nameCount">0</span>/50 字符</small>
|
|||
|
|
</div>
|
|||
|
|
<div class="mb-3">
|
|||
|
|
<label class="form-label">项目描述</label>
|
|||
|
|
<textarea class="form-control" rows="3" placeholder="请输入项目描述"></textarea>
|
|||
|
|
</div>
|
|||
|
|
<div class="mb-3">
|
|||
|
|
<label class="form-label">项目标签</label>
|
|||
|
|
<div class="tag-input-container" id="tagContainer">
|
|||
|
|
<input type="text" class="border-0 bg-transparent text-white flex-grow-1" placeholder="输入标签后按回车" id="tagInput" style="outline: none;">
|
|||
|
|
</div>
|
|||
|
|
<small class="text-secondary">预设标签:</small>
|
|||
|
|
<div class="mt-2">
|
|||
|
|
<button class="btn btn-sm btn-outline-primary me-1" onclick="addTag('Web应用')">Web应用</button>
|
|||
|
|
<button class="btn btn-sm btn-outline-primary me-1" onclick="addTag('移动应用')">移动应用</button>
|
|||
|
|
<button class="btn btn-sm btn-outline-primary me-1" onclick="addTag('后台服务')">后台服务</button>
|
|||
|
|
<button class="btn btn-sm btn-outline-primary me-1" onclick="addTag('数据分析')">数据分析</button>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="mb-3">
|
|||
|
|
<label class="form-label">需求录入方式 <span class="text-danger">*</span></label>
|
|||
|
|
<div class="row g-3">
|
|||
|
|
<div class="col-md-4">
|
|||
|
|
<div class="input-method-card" onclick="selectInputMethod('text')">
|
|||
|
|
<div class="input-method-icon">📝</div>
|
|||
|
|
<div class="input-method-title">文本输入</div>
|
|||
|
|
<div class="input-method-desc">直接输入或粘贴需求文本</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="col-md-4">
|
|||
|
|
<div class="input-method-card" onclick="selectInputMethod('file')">
|
|||
|
|
<div class="input-method-icon">📁</div>
|
|||
|
|
<div class="input-method-title">文件导入</div>
|
|||
|
|
<div class="input-method-desc">上传需求文档文件</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="col-md-4">
|
|||
|
|
<div class="input-method-card" onclick="selectInputMethod('voice')">
|
|||
|
|
<div class="input-method-icon">🎤</div>
|
|||
|
|
<div class="input-method-title">语音输入</div>
|
|||
|
|
<div class="input-method-desc">通过语音描述需求</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- 文本输入区域 -->
|
|||
|
|
<div id="textInputArea" class="mb-3" style="display: none;">
|
|||
|
|
<label class="form-label">需求内容 <span class="text-danger">*</span></label>
|
|||
|
|
<div class="d-flex justify-content-between mb-2">
|
|||
|
|
<small class="text-secondary">建议 500-5000 字</small>
|
|||
|
|
<button class="btn btn-sm btn-outline-info" onclick="insertTemplate()">
|
|||
|
|
<i class="bi bi-file-text"></i> 插入模板
|
|||
|
|
</button>
|
|||
|
|
</div>
|
|||
|
|
<textarea class="form-control" rows="10" id="requirementText" placeholder="请输入需求描述..."></textarea>
|
|||
|
|
<small class="text-secondary">已输入 <span id="textCount">0</span> 字符</small>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- 文件上传区域 -->
|
|||
|
|
<div id="fileInputArea" class="mb-3" style="display: none;">
|
|||
|
|
<label class="form-label">上传需求文档 <span class="text-danger">*</span></label>
|
|||
|
|
<div class="upload-area" id="uploadArea">
|
|||
|
|
<i class="bi bi-cloud-upload fs-1 text-primary mb-3"></i>
|
|||
|
|
<p>拖拽文件到此处或点击选择文件</p>
|
|||
|
|
<small class="text-secondary">支持 .txt, .doc, .docx, .pdf, .md 格式,最大 10MB</small>
|
|||
|
|
<input type="file" id="fileInput" class="d-none" accept=".txt,.doc,.docx,.pdf,.md">
|
|||
|
|
</div>
|
|||
|
|
<div id="filePreview" style="display: none;" class="mt-3">
|
|||
|
|
<div class="alert alert-info d-flex justify-content-between align-items-center">
|
|||
|
|
<div>
|
|||
|
|
<i class="bi bi-file-earmark-text fs-4 me-2"></i>
|
|||
|
|
<span id="fileName"></span>
|
|||
|
|
<small class="text-secondary ms-2" id="fileSize"></small>
|
|||
|
|
</div>
|
|||
|
|
<div>
|
|||
|
|
<button class="btn btn-sm btn-outline-danger" onclick="removeFile()">
|
|||
|
|
<i class="bi bi-trash"></i> 删除
|
|||
|
|
</button>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- 语音输入区域 -->
|
|||
|
|
<div id="voiceInputArea" class="mb-3" style="display: none;">
|
|||
|
|
<label class="form-label">语音录制 <span class="text-danger">*</span></label>
|
|||
|
|
<div class="voice-recorder">
|
|||
|
|
<button class="mic-button" id="micButton" onclick="toggleRecording()">
|
|||
|
|
<i class="bi bi-mic-fill"></i>
|
|||
|
|
</button>
|
|||
|
|
<div class="recording-time" id="recordingTime">00:00</div>
|
|||
|
|
<div class="text-secondary mb-3">点击麦克风开始录入</div>
|
|||
|
|
<div class="waveform" id="waveform" style="display: none;">
|
|||
|
|
<div class="waveform-bar"></div>
|
|||
|
|
<div class="waveform-bar"></div>
|
|||
|
|
<div class="waveform-bar"></div>
|
|||
|
|
<div class="waveform-bar"></div>
|
|||
|
|
<div class="waveform-bar"></div>
|
|||
|
|
</div>
|
|||
|
|
<div class="mt-3">
|
|||
|
|
<button class="btn btn-warning me-2" id="pauseBtn" style="display: none;" onclick="pauseRecording()">
|
|||
|
|
<i class="bi bi-pause-fill"></i> 暂停
|
|||
|
|
</button>
|
|||
|
|
<button class="btn btn-danger me-2" id="stopBtn" style="display: none;" onclick="stopRecording()">
|
|||
|
|
<i class="bi bi-stop-fill"></i> 停止录入
|
|||
|
|
</button>
|
|||
|
|
<button class="btn btn-info" id="resetBtn" style="display: none;" onclick="resetRecording()">
|
|||
|
|
<i class="bi bi-arrow-clockwise"></i> 重新录制
|
|||
|
|
</button>
|
|||
|
|
</div>
|
|||
|
|
<div class="mt-4">
|
|||
|
|
<label class="form-label">实时转写文本</label>
|
|||
|
|
<textarea class="form-control" rows="6" id="transcriptText" placeholder="语音识别结果将显示在这里..."></textarea>
|
|||
|
|
<small class="text-secondary">识别置信度: <span id="confidence">--</span>%</small>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- 步骤2: 需求分析 -->
|
|||
|
|
<div id="step2Content" class="wizard-content" style="display: none;">
|
|||
|
|
<div class="text-center mb-4" id="analysisProgress">
|
|||
|
|
<div class="mb-3">
|
|||
|
|
<i class="bi bi-robot fs-1 text-primary"></i>
|
|||
|
|
</div>
|
|||
|
|
<h5 class="mb-3">AI 正在分析需求...</h5>
|
|||
|
|
<div class="progress mb-2" style="height: 12px;">
|
|||
|
|
<div class="progress-bar progress-bar-striped progress-bar-animated" id="analysisProgressBar" style="width: 0%"></div>
|
|||
|
|
</div>
|
|||
|
|
<div class="d-flex justify-content-between">
|
|||
|
|
<small class="text-secondary" id="analysisStage">正在解析需求文本...</small>
|
|||
|
|
<small class="text-primary" id="analysisPercent">0%</small>
|
|||
|
|
</div>
|
|||
|
|
<div class="mt-2">
|
|||
|
|
<small class="text-secondary">已识别需求: <span class="text-primary" id="identifiedCount">0</span> 个</small>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div id="requirementsList" style="display: none;">
|
|||
|
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
|||
|
|
<h6>AI 已识别 <span class="text-primary" id="totalReqCount">0</span> 个需求条目</h6>
|
|||
|
|
<div class="btn-group btn-group-sm">
|
|||
|
|
<button class="btn btn-outline-primary" onclick="selectAllRequirements()">
|
|||
|
|
<i class="bi bi-check-all"></i> 全选
|
|||
|
|
</button>
|
|||
|
|
<button class="btn btn-outline-secondary" onclick="deselectAllRequirements()">
|
|||
|
|
<i class="bi bi-x"></i> 全不选
|
|||
|
|
</button>
|
|||
|
|
<button class="btn btn-outline-success" onclick="addManualRequirement()">
|
|||
|
|
<i class="bi bi-plus"></i> 添加需求
|
|||
|
|
</button>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div id="requirementsContainer">
|
|||
|
|
<!-- 需求卡片将动态生成 -->
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- 步骤3: 功能生成 -->
|
|||
|
|
<div id="step3Content" class="wizard-content" style="display: none;">
|
|||
|
|
<div class="text-center mb-4" id="moduleProgress">
|
|||
|
|
<div class="mb-3">
|
|||
|
|
<i class="bi bi-gear-fill fs-1 text-primary"></i>
|
|||
|
|
</div>
|
|||
|
|
<h5 class="mb-3">AI 正在生成功能模块...</h5>
|
|||
|
|
<div class="progress mb-2" style="height: 12px;">
|
|||
|
|
<div class="progress-bar progress-bar-striped progress-bar-animated" id="moduleProgressBar" style="width: 0%"></div>
|
|||
|
|
</div>
|
|||
|
|
<div class="d-flex justify-content-between">
|
|||
|
|
<small class="text-secondary" id="moduleStage">正在分析需求关联...</small>
|
|||
|
|
<small class="text-primary" id="modulePercent">0%</small>
|
|||
|
|
</div>
|
|||
|
|
<div class="mt-2">
|
|||
|
|
<small class="text-secondary">已生成模块: <span class="text-primary" id="generatedModuleCount">0</span> 个</small>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div id="modulesList" style="display: none;">
|
|||
|
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
|||
|
|
<h6>AI 已生成 <span class="text-primary" id="totalModuleCount">0</span> 个功能模块</h6>
|
|||
|
|
<div class="btn-group btn-group-sm">
|
|||
|
|
<button class="btn btn-outline-primary active">
|
|||
|
|
<i class="bi bi-grid-3x3-gap"></i> 网格
|
|||
|
|
</button>
|
|||
|
|
<button class="btn btn-outline-primary">
|
|||
|
|
<i class="bi bi-list-ul"></i> 列表
|
|||
|
|
</button>
|
|||
|
|
<button class="btn btn-outline-success">
|
|||
|
|
<i class="bi bi-plus"></i> 添加模块
|
|||
|
|
</button>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="row g-3" id="modulesContainer">
|
|||
|
|
<!-- 功能模块卡片将动态生成 -->
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- 步骤4: 工程生成 -->
|
|||
|
|
<div id="step4Content" class="wizard-content" style="display: none;">
|
|||
|
|
<div class="mb-4">
|
|||
|
|
<label class="form-label">选择工程类型 <span class="text-danger">*</span></label>
|
|||
|
|
<div class="row g-3">
|
|||
|
|
<div class="col-md-4">
|
|||
|
|
<div class="project-type-card" onclick="selectProjectType('java')">
|
|||
|
|
<div class="project-type-icon">☕</div>
|
|||
|
|
<h6>Java Spring Boot</h6>
|
|||
|
|
<p class="text-secondary small mb-2">企业级 Java Web 应用</p>
|
|||
|
|
<small class="text-secondary">Spring Boot + Maven</small>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="col-md-4">
|
|||
|
|
<div class="project-type-card" onclick="selectProjectType('qt')">
|
|||
|
|
<div class="project-type-icon">🖥️</div>
|
|||
|
|
<h6>Qt C++ 工程</h6>
|
|||
|
|
<p class="text-secondary small mb-2">跨平台桌面应用</p>
|
|||
|
|
<small class="text-secondary">Qt 5/6 + CMake</small>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="col-md-4">
|
|||
|
|
<div class="project-type-card" onclick="selectProjectType('makefile')">
|
|||
|
|
<div class="project-type-icon">⚙️</div>
|
|||
|
|
<h6>Makefile C/C++</h6>
|
|||
|
|
<p class="text-secondary small mb-2">传统 C/C++ 项目</p>
|
|||
|
|
<small class="text-secondary">GCC + Makefile</small>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="col-md-4">
|
|||
|
|
<div class="project-type-card" onclick="selectProjectType('python')">
|
|||
|
|
<div class="project-type-icon">🐍</div>
|
|||
|
|
<h6>Python Flask</h6>
|
|||
|
|
<p class="text-secondary small mb-2">轻量级 Web 框架</p>
|
|||
|
|
<small class="text-secondary">Flask + pip</small>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="col-md-4">
|
|||
|
|
<div class="project-type-card" onclick="selectProjectType('nodejs')">
|
|||
|
|
<div class="project-type-icon">🟢</div>
|
|||
|
|
<h6>Node.js Express</h6>
|
|||
|
|
<p class="text-secondary small mb-2">JavaScript 后端框架</p>
|
|||
|
|
<small class="text-secondary">Express + npm</small>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="col-md-4">
|
|||
|
|
<div class="project-type-card" onclick="selectProjectType('vue')">
|
|||
|
|
<div class="project-type-icon">💚</div>
|
|||
|
|
<h6>Vue.js 前端</h6>
|
|||
|
|
<p class="text-secondary small mb-2">渐进式前端框架</p>
|
|||
|
|
<small class="text-secondary">Vue 3 + Vite</small>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="mb-3" id="projectConfig" style="display: none;">
|
|||
|
|
<h6 class="mb-3">工程配置</h6>
|
|||
|
|
<div class="row g-3">
|
|||
|
|
<div class="col-md-6">
|
|||
|
|
<label class="form-label">项目根目录名称</label>
|
|||
|
|
<input type="text" class="form-control" placeholder="my-project">
|
|||
|
|
</div>
|
|||
|
|
<div class="col-md-6">
|
|||
|
|
<label class="form-label">版本号</label>
|
|||
|
|
<input type="text" class="form-control" value="1.0.0">
|
|||
|
|
</div>
|
|||
|
|
<div class="col-md-6">
|
|||
|
|
<label class="form-label">包名/命名空间</label>
|
|||
|
|
<input type="text" class="form-control" placeholder="com.example.project">
|
|||
|
|
</div>
|
|||
|
|
<div class="col-md-6">
|
|||
|
|
<label class="form-label">作者信息</label>
|
|||
|
|
<input type="text" class="form-control" placeholder="Your Name">
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="mb-3">
|
|||
|
|
<h6 class="mb-3">文档生成选项</h6>
|
|||
|
|
<div class="row g-2">
|
|||
|
|
<div class="col-md-6">
|
|||
|
|
<div class="form-check">
|
|||
|
|
<input class="form-check-input" type="checkbox" checked id="doc1">
|
|||
|
|
<label class="form-check-label" for="doc1">需求规格说明书 (SRS)</label>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="col-md-6">
|
|||
|
|
<div class="form-check">
|
|||
|
|
<input class="form-check-input" type="checkbox" checked id="doc2">
|
|||
|
|
<label class="form-check-label" for="doc2">功能设计文档</label>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="col-md-6">
|
|||
|
|
<div class="form-check">
|
|||
|
|
<input class="form-check-input" type="checkbox" checked id="doc3">
|
|||
|
|
<label class="form-check-label" for="doc3">软件概要设计文档 (HLD)</label>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="col-md-6">
|
|||
|
|
<div class="form-check">
|
|||
|
|
<input class="form-check-input" type="checkbox" checked id="doc4">
|
|||
|
|
<label class="form-check-label" for="doc4">软件详细设计文档 (LLD)</label>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="col-md-6">
|
|||
|
|
<div class="form-check">
|
|||
|
|
<input class="form-check-input" type="checkbox" checked id="doc5">
|
|||
|
|
<label class="form-check-label" for="doc5">接口文档 (API Docs)</label>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="col-md-6">
|
|||
|
|
<div class="form-check">
|
|||
|
|
<input class="form-check-input" type="checkbox" checked id="doc6">
|
|||
|
|
<label class="form-check-label" for="doc6">数据库设计文档</label>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="col-md-6">
|
|||
|
|
<div class="form-check">
|
|||
|
|
<input class="form-check-input" type="checkbox" id="doc7">
|
|||
|
|
<label class="form-check-label" for="doc7">部署文档</label>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="col-md-6">
|
|||
|
|
<div class="form-check">
|
|||
|
|
<input class="form-check-input" type="checkbox" checked id="doc8">
|
|||
|
|
<label class="form-check-label" for="doc8">README.md</label>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="mt-2">
|
|||
|
|
<label class="form-label">文档格式</label>
|
|||
|
|
<div class="d-flex gap-3">
|
|||
|
|
<div class="form-check">
|
|||
|
|
<input class="form-check-input" type="checkbox" checked id="format1">
|
|||
|
|
<label class="form-check-label" for="format1">Word (.docx)</label>
|
|||
|
|
</div>
|
|||
|
|
<div class="form-check">
|
|||
|
|
<input class="form-check-input" type="checkbox" checked id="format2">
|
|||
|
|
<label class="form-check-label" for="format2">PDF (.pdf)</label>
|
|||
|
|
</div>
|
|||
|
|
<div class="form-check">
|
|||
|
|
<input class="form-check-input" type="checkbox" id="format3">
|
|||
|
|
<label class="form-check-label" for="format3">Markdown (.md)</label>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="mb-3">
|
|||
|
|
<h6 class="mb-3">代码生成选项</h6>
|
|||
|
|
<div class="row g-2">
|
|||
|
|
<div class="col-md-6">
|
|||
|
|
<div class="form-check">
|
|||
|
|
<input class="form-check-input" type="checkbox" checked id="code1">
|
|||
|
|
<label class="form-check-label" for="code1">生成单元测试代码</label>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="col-md-6">
|
|||
|
|
<div class="form-check">
|
|||
|
|
<input class="form-check-input" type="checkbox" id="code2">
|
|||
|
|
<label class="form-check-label" for="code2">生成 Docker 配置</label>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="col-md-6">
|
|||
|
|
<div class="form-check">
|
|||
|
|
<input class="form-check-input" type="checkbox" id="code3">
|
|||
|
|
<label class="form-check-label" for="code3">生成 CI/CD 配置</label>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="col-md-6">
|
|||
|
|
<label class="form-label">代码注释语言</label>
|
|||
|
|
<select class="form-select form-select-sm">
|
|||
|
|
<option selected>中文</option>
|
|||
|
|
<option>英文</option>
|
|||
|
|
</select>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div id="generationProgress" style="display: none;">
|
|||
|
|
<hr>
|
|||
|
|
<div class="text-center mb-4">
|
|||
|
|
<div class="mb-3">
|
|||
|
|
<div class="spinner-border text-primary" style="width: 4rem; height: 4rem;"></div>
|
|||
|
|
</div>
|
|||
|
|
<h5 class="mb-3">正在生成项目工程...</h5>
|
|||
|
|
<div class="progress mb-2" style="height: 12px;">
|
|||
|
|
<div class="progress-bar progress-bar-striped progress-bar-animated" id="genProgressBar" style="width: 0%"></div>
|
|||
|
|
</div>
|
|||
|
|
<div class="d-flex justify-content-between mb-3">
|
|||
|
|
<small class="text-secondary" id="genStage">创建项目结构...</small>
|
|||
|
|
<small class="text-primary" id="genPercent">0%</small>
|
|||
|
|
</div>
|
|||
|
|
<div class="row g-2 text-start">
|
|||
|
|
<div class="col-md-6">
|
|||
|
|
<small class="text-success">✅ 创建项目结构</small>
|
|||
|
|
</div>
|
|||
|
|
<div class="col-md-6">
|
|||
|
|
<small class="text-primary">🔄 生成代码文件...</small>
|
|||
|
|
</div>
|
|||
|
|
<div class="col-md-6">
|
|||
|
|
<small class="text-secondary">⏳ 生成文档...</small>
|
|||
|
|
</div>
|
|||
|
|
<div class="col-md-6">
|
|||
|
|
<small class="text-secondary">⏳ 配置构建脚本...</small>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="mt-3">
|
|||
|
|
<small class="text-secondary">
|
|||
|
|
已生成文件: <span class="text-primary">45</span> 个 ·
|
|||
|
|
代码行数: <span class="text-primary">3,250</span> 行 ·
|
|||
|
|
预计剩余: <span class="text-primary">1分30秒</span>
|
|||
|
|
</small>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div id="generationSuccess" style="display: none;">
|
|||
|
|
<hr>
|
|||
|
|
<div class="text-center">
|
|||
|
|
<div class="mb-3">
|
|||
|
|
<i class="bi bi-check-circle-fill text-success" style="font-size: 4rem;"></i>
|
|||
|
|
</div>
|
|||
|
|
<h4 class="text-success mb-3">项目生成成功!</h4>
|
|||
|
|
<div class="row g-2 mb-4">
|
|||
|
|
<div class="col-3">
|
|||
|
|
<div class="card bg-transparent border-success">
|
|||
|
|
<div class="card-body">
|
|||
|
|
<h3 class="text-success">156</h3>
|
|||
|
|
<small class="text-secondary">总文件数</small>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="col-3">
|
|||
|
|
<div class="card bg-transparent border-info">
|
|||
|
|
<div class="card-body">
|
|||
|
|
<h3 class="text-info">8,450</h3>
|
|||
|
|
<small class="text-secondary">代码行数</small>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="col-3">
|
|||
|
|
<div class="card bg-transparent border-warning">
|
|||
|
|
<div class="card-body">
|
|||
|
|
<h3 class="text-warning">8</h3>
|
|||
|
|
<small class="text-secondary">文档数量</small>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="col-3">
|
|||
|
|
<div class="card bg-transparent border-primary">
|
|||
|
|
<div class="card-body">
|
|||
|
|
<h3 class="text-primary">3:42</h3>
|
|||
|
|
<small class="text-secondary">总耗时</small>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="d-flex gap-2 justify-content-center">
|
|||
|
|
<button class="btn btn-primary" onclick="viewGeneratedProject()">
|
|||
|
|
<i class="bi bi-eye"></i> 查看项目
|
|||
|
|
</button>
|
|||
|
|
<button class="btn btn-success" onclick="downloadProject()">
|
|||
|
|
<i class="bi bi-download"></i> 下载项目
|
|||
|
|
</button>
|
|||
|
|
<button class="btn btn-info" onclick="openInIDE()">
|
|||
|
|
<i class="bi bi-code-square"></i> 在 IDE 中打开
|
|||
|
|
</button>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="modal-footer">
|
|||
|
|
<button type="button" class="btn btn-secondary" id="prevBtn" style="display: none;" onclick="previousStep()">
|
|||
|
|
<i class="bi bi-arrow-left"></i> 上一步
|
|||
|
|
</button>
|
|||
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
|
|||
|
|
<button type="button" class="btn btn-primary" id="nextBtn" onclick="nextStep()">
|
|||
|
|
下一步 <i class="bi bi-arrow-right"></i>
|
|||
|
|
</button>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- Toast 容器 -->
|
|||
|
|
<div class="toast-container"></div>
|
|||
|
|
|
|||
|
|
<!-- 右键菜单 -->
|
|||
|
|
<div class="context-menu" id="contextMenu">
|
|||
|
|
<div class="context-menu-item" onclick="handleContextMenu('rename')">
|
|||
|
|
<i class="bi bi-pencil"></i> 重命名
|
|||
|
|
</div>
|
|||
|
|
<div class="context-menu-item" onclick="handleContextMenu('delete')">
|
|||
|
|
<i class="bi bi-trash text-danger"></i> 删除
|
|||
|
|
</div>
|
|||
|
|
<div class="context-menu-item" onclick="handleContextMenu('export')">
|
|||
|
|
<i class="bi bi-box-arrow-up"></i> 导出
|
|||
|
|
</div>
|
|||
|
|
<div class="context-menu-item" onclick="handleContextMenu('copy')">
|
|||
|
|
<i class="bi bi-files"></i> 复制
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- Bootstrap JS -->
|
|||
|
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
|||
|
|
<script>
|
|||
|
|
let currentStep = 1;
|
|||
|
|
let selectedInputMethod = '';
|
|||
|
|
let isRecording = false;
|
|||
|
|
let recordingInterval = null;
|
|||
|
|
let recordingSeconds = 0;
|
|||
|
|
let selectedProjectType = '';
|
|||
|
|
|
|||
|
|
// 项目选择
|
|||
|
|
function selectProject(element, index) {
|
|||
|
|
document.querySelectorAll('.project-item').forEach(item => {
|
|||
|
|
item.classList.remove('active');
|
|||
|
|
});
|
|||
|
|
element.classList.add('active');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 项目右键菜单
|
|||
|
|
document.querySelectorAll('.project-item').forEach(item => {
|
|||
|
|
item.addEventListener('contextmenu', function(e) {
|
|||
|
|
e.preventDefault();
|
|||
|
|
const menu = document.getElementById('contextMenu');
|
|||
|
|
menu.style.display = 'block';
|
|||
|
|
menu.style.left = e.pageX + 'px';
|
|||
|
|
menu.style.top = e.pageY + 'px';
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
document.addEventListener('click', function() {
|
|||
|
|
document.getElementById('contextMenu').style.display = 'none';
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
function handleContextMenu(action) {
|
|||
|
|
showToast('info', `执行操作: ${action}`);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 文档查看
|
|||
|
|
function viewDocument(docName) {
|
|||
|
|
showToast('info', `正在打开文档: ${docName}`);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 文档下载
|
|||
|
|
function downloadDocument(fileName) {
|
|||
|
|
const content = `这是 ${fileName} 的内容\n\n生成时间: ${new Date().toLocaleString()}\n\n这是一个示例文档。`;
|
|||
|
|
const blob = new Blob([content], { type: 'text/plain' });
|
|||
|
|
const link = document.createElement('a');
|
|||
|
|
link.href = URL.createObjectURL(blob);
|
|||
|
|
link.download = fileName;
|
|||
|
|
document.body.appendChild(link);
|
|||
|
|
link.click();
|
|||
|
|
document.body.removeChild(link);
|
|||
|
|
showToast('success', `正在下载: ${fileName}`);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 代码下载
|
|||
|
|
function downloadCode(fileName) {
|
|||
|
|
showToast('success', `正在下载: ${fileName}`);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 树形结构折叠
|
|||
|
|
function toggleFolder(element) {
|
|||
|
|
const toggle = element.querySelector('.tree-toggle');
|
|||
|
|
const children = element.nextElementSibling;
|
|||
|
|
|
|||
|
|
if (children && children.classList.contains('tree-children')) {
|
|||
|
|
if (toggle.textContent === '▼') {
|
|||
|
|
toggle.textContent = '▶';
|
|||
|
|
children.style.display = 'none';
|
|||
|
|
} else {
|
|||
|
|
toggle.textContent = '▼';
|
|||
|
|
children.style.display = 'block';
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 流水线操作
|
|||
|
|
function startPipeline() {
|
|||
|
|
showToast('success', '流水线已启动');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function stopPipeline() {
|
|||
|
|
showToast('warning', '流水线已停止');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function restartPipeline() {
|
|||
|
|
showToast('info', '流水线重新运行中...');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function showPipelineDetail(stage) {
|
|||
|
|
showToast('info', `查看 ${stage} 详情`);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Toast 通知
|
|||
|
|
function showToast(type, message) {
|
|||
|
|
const container = document.querySelector('.toast-container');
|
|||
|
|
const iconMap = {
|
|||
|
|
success: 'check-circle-fill',
|
|||
|
|
error: 'exclamation-triangle-fill',
|
|||
|
|
info: 'info-circle-fill',
|
|||
|
|
warning: 'exclamation-circle-fill'
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const colorMap = {
|
|||
|
|
success: 'success',
|
|||
|
|
error: 'danger',
|
|||
|
|
info: 'primary',
|
|||
|
|
warning: 'warning'
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const toastEl = document.createElement('div');
|
|||
|
|
toastEl.className = `toast ${type}`;
|
|||
|
|
toastEl.setAttribute('role', 'alert');
|
|||
|
|
toastEl.innerHTML = `
|
|||
|
|
<div class="toast-body d-flex align-items-center">
|
|||
|
|
<i class="bi bi-${iconMap[type]} text-${colorMap[type]} me-2 fs-5"></i>
|
|||
|
|
<span class="flex-grow-1">${message}</span>
|
|||
|
|
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="toast"></button>
|
|||
|
|
</div>
|
|||
|
|
`;
|
|||
|
|
|
|||
|
|
container.appendChild(toastEl);
|
|||
|
|
const toast = new bootstrap.Toast(toastEl, { delay: 3000 });
|
|||
|
|
toast.show();
|
|||
|
|
|
|||
|
|
toastEl.addEventListener('hidden.bs.toast', function() {
|
|||
|
|
toastEl.remove();
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 向导步骤控制
|
|||
|
|
function nextStep() {
|
|||
|
|
if (currentStep === 1) {
|
|||
|
|
// 验证步骤1
|
|||
|
|
const projectName = document.getElementById('projectName').value.trim();
|
|||
|
|
if (!projectName) {
|
|||
|
|
showToast('error', '请输入项目名称');
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
if (!selectedInputMethod) {
|
|||
|
|
showToast('error', '请选择需求录入方式');
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 进入步骤2,开始AI分析
|
|||
|
|
document.getElementById('step1Content').style.display = 'none';
|
|||
|
|
document.getElementById('wizardStep1').classList.remove('active');
|
|||
|
|
document.getElementById('wizardStep1').classList.add('completed');
|
|||
|
|
|
|||
|
|
currentStep = 2;
|
|||
|
|
document.getElementById('step2Content').style.display = 'block';
|
|||
|
|
document.getElementById('wizardStep2').classList.add('active');
|
|||
|
|
document.getElementById('prevBtn').style.display = 'inline-block';
|
|||
|
|
|
|||
|
|
// 模拟AI分析
|
|||
|
|
simulateAnalysis();
|
|||
|
|
|
|||
|
|
} else if (currentStep === 2) {
|
|||
|
|
// 验证至少选中一个需求
|
|||
|
|
const checkedReqs = document.querySelectorAll('#requirementsContainer input[type="checkbox"]:checked');
|
|||
|
|
if (checkedReqs.length === 0) {
|
|||
|
|
showToast('error', '请至少选中一个需求');
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
document.getElementById('step2Content').style.display = 'none';
|
|||
|
|
document.getElementById('wizardStep2').classList.remove('active');
|
|||
|
|
document.getElementById('wizardStep2').classList.add('completed');
|
|||
|
|
|
|||
|
|
currentStep = 3;
|
|||
|
|
document.getElementById('step3Content').style.display = 'block';
|
|||
|
|
document.getElementById('wizardStep3').classList.add('active');
|
|||
|
|
|
|||
|
|
// 模拟功能生成
|
|||
|
|
simulateModuleGeneration();
|
|||
|
|
|
|||
|
|
} else if (currentStep === 3) {
|
|||
|
|
document.getElementById('step3Content').style.display = 'none';
|
|||
|
|
document.getElementById('wizardStep3').classList.remove('active');
|
|||
|
|
document.getElementById('wizardStep3').classList.add('completed');
|
|||
|
|
|
|||
|
|
currentStep = 4;
|
|||
|
|
document.getElementById('step4Content').style.display = 'block';
|
|||
|
|
document.getElementById('wizardStep4').classList.add('active');
|
|||
|
|
document.getElementById('nextBtn').innerHTML = '<i class="bi bi-check-circle"></i> 开始生成';
|
|||
|
|
|
|||
|
|
} else if (currentStep === 4) {
|
|||
|
|
if (!selectedProjectType) {
|
|||
|
|
showToast('error', '请选择工程类型');
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 开始生成
|
|||
|
|
document.getElementById('generationProgress').style.display = 'block';
|
|||
|
|
document.getElementById('nextBtn').style.display = 'none';
|
|||
|
|
document.getElementById('prevBtn').style.display = 'none';
|
|||
|
|
|
|||
|
|
simulateProjectGeneration();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function previousStep() {
|
|||
|
|
if (currentStep > 1) {
|
|||
|
|
document.getElementById(`step${currentStep}Content`).style.display = 'none';
|
|||
|
|
document.getElementById(`wizardStep${currentStep}`).classList.remove('active');
|
|||
|
|
|
|||
|
|
currentStep--;
|
|||
|
|
|
|||
|
|
document.getElementById(`step${currentStep}Content`).style.display = 'block';
|
|||
|
|
document.getElementById(`wizardStep${currentStep}`).classList.remove('completed');
|
|||
|
|
document.getElementById(`wizardStep${currentStep}`).classList.add('active');
|
|||
|
|
|
|||
|
|
if (currentStep === 1) {
|
|||
|
|
document.getElementById('prevBtn').style.display = 'none';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
document.getElementById('nextBtn').innerHTML = '下一步 <i class="bi bi-arrow-right"></i>';
|
|||
|
|
document.getElementById('nextBtn').style.display = 'inline-block';
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 需求录入方式选择
|
|||
|
|
function selectInputMethod(method) {
|
|||
|
|
selectedInputMethod = method;
|
|||
|
|
document.querySelectorAll('.input-method-card').forEach(card => {
|
|||
|
|
card.classList.remove('selected');
|
|||
|
|
});
|
|||
|
|
event.currentTarget.classList.add('selected');
|
|||
|
|
|
|||
|
|
// 隐藏所有输入区域
|
|||
|
|
document.getElementById('textInputArea').style.display = 'none';
|
|||
|
|
document.getElementById('fileInputArea').style.display = 'none';
|
|||
|
|
document.getElementById('voiceInputArea').style.display = 'none';
|
|||
|
|
|
|||
|
|
// 显示对应区域
|
|||
|
|
if (method === 'text') {
|
|||
|
|
document.getElementById('textInputArea').style.display = 'block';
|
|||
|
|
} else if (method === 'file') {
|
|||
|
|
document.getElementById('fileInputArea').style.display = 'block';
|
|||
|
|
} else if (method === 'voice') {
|
|||
|
|
document.getElementById('voiceInputArea').style.display = 'block';
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 标签管理
|
|||
|
|
const tags = [];
|
|||
|
|
document.getElementById('tagInput').addEventListener('keypress', function(e) {
|
|||
|
|
if (e.key === 'Enter' && this.value.trim()) {
|
|||
|
|
addTag(this.value.trim());
|
|||
|
|
this.value = '';
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
function addTag(tagText) {
|
|||
|
|
if (!tags.includes(tagText)) {
|
|||
|
|
tags.push(tagText);
|
|||
|
|
const tagEl = document.createElement('div');
|
|||
|
|
tagEl.className = 'tag-item';
|
|||
|
|
tagEl.innerHTML = `
|
|||
|
|
<span>${tagText}</span>
|
|||
|
|
<i class="bi bi-x tag-remove" onclick="removeTag('${tagText}')"></i>
|
|||
|
|
`;
|
|||
|
|
document.getElementById('tagContainer').insertBefore(tagEl, document.getElementById('tagInput'));
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function removeTag(tagText) {
|
|||
|
|
const index = tags.indexOf(tagText);
|
|||
|
|
if (index > -1) {
|
|||
|
|
tags.splice(index, 1);
|
|||
|
|
}
|
|||
|
|
event.target.parentElement.remove();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 项目名称字符计数
|
|||
|
|
document.getElementById('projectName').addEventListener('input', function() {
|
|||
|
|
document.getElementById('nameCount').textContent = this.value.length;
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 文本输入字符计数
|
|||
|
|
document.getElementById('requirementText')?.addEventListener('input', function() {
|
|||
|
|
document.getElementById('textCount').textContent = this.value.length;
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 插入模板
|
|||
|
|
function insertTemplate() {
|
|||
|
|
const template = `项目背景:
|
|||
|
|
请描述项目的背景和目标
|
|||
|
|
|
|||
|
|
核心功能:
|
|||
|
|
1. 功能一
|
|||
|
|
2. 功能二
|
|||
|
|
3. 功能三
|
|||
|
|
|
|||
|
|
用户角色:
|
|||
|
|
- 管理员
|
|||
|
|
- 普通用户
|
|||
|
|
|
|||
|
|
性能要求:
|
|||
|
|
- 响应时间
|
|||
|
|
- 并发用户数
|
|||
|
|
|
|||
|
|
安全要求:
|
|||
|
|
- 数据加密
|
|||
|
|
- 权限控制`;
|
|||
|
|
document.getElementById('requirementText').value = template;
|
|||
|
|
document.getElementById('textCount').textContent = template.length;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 文件上传
|
|||
|
|
const uploadArea = document.getElementById('uploadArea');
|
|||
|
|
const fileInput = document.getElementById('fileInput');
|
|||
|
|
|
|||
|
|
uploadArea.addEventListener('click', () => fileInput.click());
|
|||
|
|
|
|||
|
|
uploadArea.addEventListener('dragover', (e) => {
|
|||
|
|
e.preventDefault();
|
|||
|
|
uploadArea.classList.add('dragover');
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
uploadArea.addEventListener('dragleave', () => {
|
|||
|
|
uploadArea.classList.remove('dragover');
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
uploadArea.addEventListener('drop', (e) => {
|
|||
|
|
e.preventDefault();
|
|||
|
|
uploadArea.classList.remove('dragover');
|
|||
|
|
if (e.dataTransfer.files.length) {
|
|||
|
|
handleFileUpload(e.dataTransfer.files[0]);
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
fileInput.addEventListener('change', (e) => {
|
|||
|
|
if (e.target.files.length) {
|
|||
|
|
handleFileUpload(e.target.files[0]);
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
function handleFileUpload(file) {
|
|||
|
|
const maxSize = 10 * 1024 * 1024; // 10MB
|
|||
|
|
if (file.size > maxSize) {
|
|||
|
|
showToast('error', '文件大小超过 10MB 限制');
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
document.getElementById('fileName').textContent = file.name;
|
|||
|
|
document.getElementById('fileSize').textContent = (file.size / 1024).toFixed(2) + ' KB';
|
|||
|
|
document.getElementById('filePreview').style.display = 'block';
|
|||
|
|
uploadArea.style.display = 'none';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function removeFile() {
|
|||
|
|
document.getElementById('filePreview').style.display = 'none';
|
|||
|
|
uploadArea.style.display = 'block';
|
|||
|
|
fileInput.value = '';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 语音录制
|
|||
|
|
function toggleRecording() {
|
|||
|
|
if (!isRecording) {
|
|||
|
|
startRecording();
|
|||
|
|
} else {
|
|||
|
|
stopRecording();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function startRecording() {
|
|||
|
|
isRecording = true;
|
|||
|
|
recordingSeconds = 0;
|
|||
|
|
document.getElementById('micButton').classList.add('recording');
|
|||
|
|
document.getElementById('waveform').style.display = 'flex';
|
|||
|
|
document.getElementById('pauseBtn').style.display = 'inline-block';
|
|||
|
|
document.getElementById('stopBtn').style.display = 'inline-block';
|
|||
|
|
|
|||
|
|
recordingInterval = setInterval(() => {
|
|||
|
|
recordingSeconds++;
|
|||
|
|
const minutes = Math.floor(recordingSeconds / 60).toString().padStart(2, '0');
|
|||
|
|
const seconds = (recordingSeconds % 60).toString().padStart(2, '0');
|
|||
|
|
document.getElementById('recordingTime').textContent = `${minutes}:${seconds}`;
|
|||
|
|
|
|||
|
|
// 模拟实时转写
|
|||
|
|
if (recordingSeconds % 3 === 0) {
|
|||
|
|
const mockText = ['我想开发一个', '智能监控系统', '支持实时数据采集', '和可视化展示'];
|
|||
|
|
const currentText = document.getElementById('transcriptText').value;
|
|||
|
|
document.getElementById('transcriptText').value = currentText + ' ' + mockText[Math.floor(Math.random() * mockText.length)];
|
|||
|
|
document.getElementById('confidence').textContent = (85 + Math.random() * 10).toFixed(1);
|
|||
|
|
}
|
|||
|
|
}, 1000);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function pauseRecording() {
|
|||
|
|
if (recordingInterval) {
|
|||
|
|
clearInterval(recordingInterval);
|
|||
|
|
recordingInterval = null;
|
|||
|
|
}
|
|||
|
|
document.getElementById('micButton').classList.remove('recording');
|
|||
|
|
document.getElementById('waveform').style.display = 'none';
|
|||
|
|
showToast('info', '录制已暂停');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function stopRecording() {
|
|||
|
|
isRecording = false;
|
|||
|
|
if (recordingInterval) {
|
|||
|
|
clearInterval(recordingInterval);
|
|||
|
|
recordingInterval = null;
|
|||
|
|
}
|
|||
|
|
document.getElementById('micButton').classList.remove('recording');
|
|||
|
|
document.getElementById('waveform').style.display = 'none';
|
|||
|
|
document.getElementById('pauseBtn').style.display = 'none';
|
|||
|
|
document.getElementById('stopBtn').style.display = 'none';
|
|||
|
|
document.getElementById('resetBtn').style.display = 'inline-block';
|
|||
|
|
showToast('success', '录制已完成');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function resetRecording() {
|
|||
|
|
recordingSeconds = 0;
|
|||
|
|
document.getElementById('recordingTime').textContent = '00:00';
|
|||
|
|
document.getElementById('transcriptText').value = '';
|
|||
|
|
document.getElementById('confidence').textContent = '--';
|
|||
|
|
document.getElementById('resetBtn').style.display = 'none';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 模拟AI需求分析
|
|||
|
|
function simulateAnalysis() {
|
|||
|
|
let progress = 0;
|
|||
|
|
const stages = [
|
|||
|
|
'正在解析需求文本...',
|
|||
|
|
'正在识别功能点...',
|
|||
|
|
'正在提取需求条目...',
|
|||
|
|
'正在分类整理...'
|
|||
|
|
];
|
|||
|
|
let stageIndex = 0;
|
|||
|
|
|
|||
|
|
const interval = setInterval(() => {
|
|||
|
|
progress += 5;
|
|||
|
|
document.getElementById('analysisProgressBar').style.width = progress + '%';
|
|||
|
|
document.getElementById('analysisPercent').textContent = progress + '%';
|
|||
|
|
|
|||
|
|
if (progress % 25 === 0 && stageIndex < stages.length) {
|
|||
|
|
document.getElementById('analysisStage').textContent = stages[stageIndex];
|
|||
|
|
stageIndex++;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
document.getElementById('identifiedCount').textContent = Math.floor(progress / 25);
|
|||
|
|
|
|||
|
|
if (progress >= 100) {
|
|||
|
|
clearInterval(interval);
|
|||
|
|
setTimeout(() => {
|
|||
|
|
document.getElementById('analysisProgress').style.display = 'none';
|
|||
|
|
document.getElementById('requirementsList').style.display = 'block';
|
|||
|
|
generateRequirementsList();
|
|||
|
|
}, 500);
|
|||
|
|
}
|
|||
|
|
}, 100);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 生成需求列表
|
|||
|
|
function generateRequirementsList() {
|
|||
|
|
const requirements = [
|
|||
|
|
{ id: 'REQ-001', title: '用户登录认证', desc: '系统需要支持用户通过用户名和密码进行登录,并集成多因素认证机制', confidence: 95, priority: '高', type: '功能性' },
|
|||
|
|
{ id: 'REQ-002', title: '实时数据监控', desc: '平台需要实时采集设备数据,并通过可视化图表展示关键指标', confidence: 92, priority: '高', type: '功能性' },
|
|||
|
|
{ id: 'REQ-003', title: '数据导出功能', desc: '支持将监控数据导出为Excel、CSV等格式', confidence: 88, priority: '中', type: '功能性' },
|
|||
|
|
{ id: 'REQ-004', title: '系统日志管理', desc: '记录所有系统操作日志,支持日志查询和审计', confidence: 85, priority: '中', type: '非功能性' }
|
|||
|
|
];
|
|||
|
|
|
|||
|
|
document.getElementById('totalReqCount').textContent = requirements.length;
|
|||
|
|
const container = document.getElementById('requirementsContainer');
|
|||
|
|
container.innerHTML = '';
|
|||
|
|
|
|||
|
|
requirements.forEach(req => {
|
|||
|
|
const confidenceClass = req.confidence >= 90 ? 'success' : req.confidence >= 70 ? 'warning' : 'danger';
|
|||
|
|
const confidenceText = req.confidence >= 90 ? '高' : req.confidence >= 70 ? '中' : '低';
|
|||
|
|
|
|||
|
|
const card = document.createElement('div');
|
|||
|
|
card.className = 'item-card';
|
|||
|
|
card.innerHTML = `
|
|||
|
|
<div class="d-flex align-items-start gap-2">
|
|||
|
|
<input class="form-check-input mt-1" type="checkbox" checked>
|
|||
|
|
<div class="flex-grow-1">
|
|||
|
|
<div class="d-flex justify-content-between align-items-start mb-2">
|
|||
|
|
<div class="item-title">${req.id} ${req.title}</div>
|
|||
|
|
<div>
|
|||
|
|
<span class="badge bg-${confidenceClass}">${confidenceText} ${req.confidence}%</span>
|
|||
|
|
<span class="badge bg-${req.priority === '高' ? 'warning' : 'info'} text-dark ms-1">${req.priority}优先级</span>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="item-desc mb-2">${req.desc}</div>
|
|||
|
|
<div class="mb-2">
|
|||
|
|
<span class="badge bg-secondary">${req.type}</span>
|
|||
|
|
</div>
|
|||
|
|
<input type="text" class="form-control form-control-sm" placeholder="添加需求描述...">
|
|||
|
|
<div class="mt-2">
|
|||
|
|
<button class="btn btn-sm btn-outline-primary">
|
|||
|
|
<i class="bi bi-pencil"></i> 编辑
|
|||
|
|
</button>
|
|||
|
|
<button class="btn btn-sm btn-outline-danger ms-1">
|
|||
|
|
<i class="bi bi-trash"></i> 删除
|
|||
|
|
</button>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
`;
|
|||
|
|
container.appendChild(card);
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 需求全选/全不选
|
|||
|
|
function selectAllRequirements() {
|
|||
|
|
document.querySelectorAll('#requirementsContainer input[type="checkbox"]').forEach(cb => {
|
|||
|
|
cb.checked = true;
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function deselectAllRequirements() {
|
|||
|
|
document.querySelectorAll('#requirementsContainer input[type="checkbox"]').forEach(cb => {
|
|||
|
|
cb.checked = false;
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function addManualRequirement() {
|
|||
|
|
showToast('info', '添加手动需求功能');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 模拟功能模块生成
|
|||
|
|
function simulateModuleGeneration() {
|
|||
|
|
let progress = 0;
|
|||
|
|
const stages = [
|
|||
|
|
'正在分析需求关联...',
|
|||
|
|
'正在设计功能模块...',
|
|||
|
|
'正在规划模块架构...',
|
|||
|
|
'正在生成功能清单...'
|
|||
|
|
];
|
|||
|
|
let stageIndex = 0;
|
|||
|
|
|
|||
|
|
const interval = setInterval(() => {
|
|||
|
|
progress += 5;
|
|||
|
|
document.getElementById('moduleProgressBar').style.width = progress + '%';
|
|||
|
|
document.getElementById('modulePercent').textContent = progress + '%';
|
|||
|
|
|
|||
|
|
if (progress % 25 === 0 && stageIndex < stages.length) {
|
|||
|
|
document.getElementById('moduleStage').textContent = stages[stageIndex];
|
|||
|
|
stageIndex++;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
document.getElementById('generatedModuleCount').textContent = Math.floor(progress / 33);
|
|||
|
|
|
|||
|
|
if (progress >= 100) {
|
|||
|
|
clearInterval(interval);
|
|||
|
|
setTimeout(() => {
|
|||
|
|
document.getElementById('moduleProgress').style.display = 'none';
|
|||
|
|
document.getElementById('modulesList').style.display = 'block';
|
|||
|
|
generateModulesList();
|
|||
|
|
}, 500);
|
|||
|
|
}
|
|||
|
|
}, 100);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 生成功能模块列表
|
|||
|
|
function generateModulesList() {
|
|||
|
|
const modules = [
|
|||
|
|
{ name: '用户认证模块', icon: '🔐', reqs: ['REQ-001'], tech: ['Spring Boot', 'JWT'], progress: 0 },
|
|||
|
|
{ name: '数据监控模块', icon: '📊', reqs: ['REQ-002'], tech: ['ECharts', 'WebSocket'], progress: 0 },
|
|||
|
|
{ name: '数据管理模块', icon: '📁', reqs: ['REQ-003'], tech: ['POI', 'MySQL'], progress: 0 }
|
|||
|
|
];
|
|||
|
|
|
|||
|
|
document.getElementById('totalModuleCount').textContent = modules.length;
|
|||
|
|
const container = document.getElementById('modulesContainer');
|
|||
|
|
container.innerHTML = '';
|
|||
|
|
|
|||
|
|
modules.forEach(module => {
|
|||
|
|
const col = document.createElement('div');
|
|||
|
|
col.className = 'col-md-6';
|
|||
|
|
col.innerHTML = `
|
|||
|
|
<div class="module-card">
|
|||
|
|
<div class="module-icon">${module.icon}</div>
|
|||
|
|
<div class="item-title">${module.name}</div>
|
|||
|
|
<div class="mb-2">
|
|||
|
|
${module.reqs.map(req => `<span class="badge bg-primary">${req}</span>`).join(' ')}
|
|||
|
|
</div>
|
|||
|
|
<div class="item-desc mb-2">功能模块描述...</div>
|
|||
|
|
<div class="mb-2">
|
|||
|
|
${module.tech.map(tech => `<span class="badge bg-secondary">${tech}</span>`).join(' ')}
|
|||
|
|
</div>
|
|||
|
|
<div class="progress mb-2" style="height: 6px;">
|
|||
|
|
<div class="progress-bar" style="width: ${module.progress}%"></div>
|
|||
|
|
</div>
|
|||
|
|
<div class="d-flex gap-1">
|
|||
|
|
<button class="btn btn-sm btn-outline-primary flex-grow-1">
|
|||
|
|
<i class="bi bi-gear"></i> 配置
|
|||
|
|
</button>
|
|||
|
|
<button class="btn btn-sm btn-outline-danger">
|
|||
|
|
<i class="bi bi-trash"></i>
|
|||
|
|
</button>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
`;
|
|||
|
|
container.appendChild(col);
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 工程类型选择
|
|||
|
|
function selectProjectType(type) {
|
|||
|
|
selectedProjectType = type;
|
|||
|
|
document.querySelectorAll('.project-type-card').forEach(card => {
|
|||
|
|
card.classList.remove('selected');
|
|||
|
|
});
|
|||
|
|
event.currentTarget.classList.add('selected');
|
|||
|
|
document.getElementById('projectConfig').style.display = 'block';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 模拟项目生成
|
|||
|
|
function simulateProjectGeneration() {
|
|||
|
|
let progress = 0;
|
|||
|
|
const stages = [
|
|||
|
|
'创建项目结构...',
|
|||
|
|
'生成代码文件...',
|
|||
|
|
'生成文档...',
|
|||
|
|
'配置构建脚本...',
|
|||
|
|
'初始化版本控制...'
|
|||
|
|
];
|
|||
|
|
let stageIndex = 0;
|
|||
|
|
|
|||
|
|
const interval = setInterval(() => {
|
|||
|
|
progress += 2;
|
|||
|
|
document.getElementById('genProgressBar').style.width = progress + '%';
|
|||
|
|
document.getElementById('genPercent').textContent = progress + '%';
|
|||
|
|
|
|||
|
|
if (progress % 20 === 0 && stageIndex < stages.length) {
|
|||
|
|
document.getElementById('genStage').textContent = stages[stageIndex];
|
|||
|
|
stageIndex++;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (progress >= 100) {
|
|||
|
|
clearInterval(interval);
|
|||
|
|
setTimeout(() => {
|
|||
|
|
document.getElementById('generationProgress').style.display = 'none';
|
|||
|
|
document.getElementById('generationSuccess').style.display = 'block';
|
|||
|
|
}, 500);
|
|||
|
|
}
|
|||
|
|
}, 100);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 生成完成后的操作
|
|||
|
|
function viewGeneratedProject() {
|
|||
|
|
bootstrap.Modal.getInstance(document.getElementById('newProjectModal')).hide();
|
|||
|
|
showToast('success', '项目已添加到列表');
|
|||
|
|
resetWizard();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function downloadProject() {
|
|||
|
|
showToast('success', '正在打包下载项目...');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function openInIDE() {
|
|||
|
|
showToast('info', '正在打开 IDE...');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 重置向导
|
|||
|
|
function resetWizard() {
|
|||
|
|
currentStep = 1;
|
|||
|
|
selectedInputMethod = '';
|
|||
|
|
selectedProjectType = '';
|
|||
|
|
|
|||
|
|
for (let i = 1; i <= 4; i++) {
|
|||
|
|
document.getElementById(`step${i}Content`).style.display = i === 1 ? 'block' : 'none';
|
|||
|
|
document.getElementById(`wizardStep${i}`).classList.remove('active', 'completed');
|
|||
|
|
}
|
|||
|
|
document.getElementById('wizardStep1').classList.add('active');
|
|||
|
|
document.getElementById('prevBtn').style.display = 'none';
|
|||
|
|
document.getElementById('nextBtn').innerHTML = '下一步 <i class="bi bi-arrow-right"></i>';
|
|||
|
|
document.getElementById('nextBtn').style.display = 'inline-block';
|
|||
|
|
|
|||
|
|
// 重置表单
|
|||
|
|
document.getElementById('projectName').value = '';
|
|||
|
|
document.getElementById('nameCount').textContent = '0';
|
|||
|
|
|
|||
|
|
// 重置进度显示
|
|||
|
|
document.getElementById('analysisProgress').style.display = 'block';
|
|||
|
|
document.getElementById('requirementsList').style.display = 'none';
|
|||
|
|
document.getElementById('moduleProgress').style.display = 'block';
|
|||
|
|
document.getElementById('modulesList').style.display = 'none';
|
|||
|
|
document.getElementById('generationProgress').style.display = 'none';
|
|||
|
|
document.getElementById('generationSuccess').style.display = 'none';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 模态框关闭时重置
|
|||
|
|
document.getElementById('newProjectModal').addEventListener
|
|||
|
|
('hidden.bs.modal', function () {
|
|||
|
|
resetWizard();
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 快捷键支持
|
|||
|
|
document.addEventListener('keydown', function(e) {
|
|||
|
|
// Ctrl/Cmd + N: 新建项目
|
|||
|
|
if ((e.ctrlKey || e.metaKey) && e.key === 'n') {
|
|||
|
|
e.preventDefault();
|
|||
|
|
const modal = new bootstrap.Modal(document.getElementById('newProjectModal'));
|
|||
|
|
modal.show();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Ctrl/Cmd + K: 全局搜索
|
|||
|
|
if ((e.ctrlKey || e.metaKey) && e.key === 'k') {
|
|||
|
|
e.preventDefault();
|
|||
|
|
showToast('info', '全局搜索功能');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Esc: 关闭模态框
|
|||
|
|
if (e.key === 'Escape') {
|
|||
|
|
const modals = document.querySelectorAll('.modal.show');
|
|||
|
|
modals.forEach(modal => {
|
|||
|
|
bootstrap.Modal.getInstance(modal)?.hide();
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 页面加载动画
|
|||
|
|
window.addEventListener('load', function() {
|
|||
|
|
document.querySelectorAll('.panel').forEach((panel, index) => {
|
|||
|
|
panel.style.opacity = '0';
|
|||
|
|
panel.style.transform = 'translateY(20px)';
|
|||
|
|
setTimeout(() => {
|
|||
|
|
panel.style.transition = 'all 0.5s ease';
|
|||
|
|
panel.style.opacity = '1';
|
|||
|
|
panel.style.transform = 'translateY(0)';
|
|||
|
|
}, index * 100);
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 搜索功能
|
|||
|
|
document.querySelector('.project-search input')?.addEventListener('input', function(e) {
|
|||
|
|
const searchTerm = e.target.value.toLowerCase();
|
|||
|
|
document.querySelectorAll('.project-item').forEach(item => {
|
|||
|
|
const name = item.querySelector('.project-name').textContent.toLowerCase();
|
|||
|
|
const desc = item.querySelector('.project-desc').textContent.toLowerCase();
|
|||
|
|
if (name.includes(searchTerm) || desc.includes(searchTerm)) {
|
|||
|
|
item.style.display = 'block';
|
|||
|
|
} else {
|
|||
|
|
item.style.display = 'none';
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 模块配置弹窗
|
|||
|
|
function showModuleConfig(moduleName) {
|
|||
|
|
const modalHtml = `
|
|||
|
|
<div class="modal fade" id="moduleConfigModal" tabindex="-1">
|
|||
|
|
<div class="modal-dialog modal-lg">
|
|||
|
|
<div class="modal-content">
|
|||
|
|
<div class="modal-header">
|
|||
|
|
<h5 class="modal-title">配置模块: ${moduleName}</h5>
|
|||
|
|
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
|
|||
|
|
</div>
|
|||
|
|
<div class="modal-body">
|
|||
|
|
<ul class="nav nav-tabs mb-3" role="tablist">
|
|||
|
|
<li class="nav-item">
|
|||
|
|
<button class="nav-link active" data-bs-toggle="tab" data-bs-target="#basicInfo">基本信息</button>
|
|||
|
|
</li>
|
|||
|
|
<li class="nav-item">
|
|||
|
|
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#techStack">技术选型</button>
|
|||
|
|
</li>
|
|||
|
|
<li class="nav-item">
|
|||
|
|
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#apiDesign">接口设计</button>
|
|||
|
|
</li>
|
|||
|
|
<li class="nav-item">
|
|||
|
|
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#dataModel">数据模型</button>
|
|||
|
|
</li>
|
|||
|
|
</ul>
|
|||
|
|
<div class="tab-content">
|
|||
|
|
<div class="tab-pane fade show active" id="basicInfo">
|
|||
|
|
<div class="mb-3">
|
|||
|
|
<label class="form-label">模块名称</label>
|
|||
|
|
<input type="text" class="form-control" value="${moduleName}">
|
|||
|
|
</div>
|
|||
|
|
<div class="mb-3">
|
|||
|
|
<label class="form-label">模块描述</label>
|
|||
|
|
<textarea class="form-control" rows="3"></textarea>
|
|||
|
|
</div>
|
|||
|
|
<div class="mb-3">
|
|||
|
|
<label class="form-label">模块类型</label>
|
|||
|
|
<select class="form-select">
|
|||
|
|
<option>前端模块</option>
|
|||
|
|
<option selected>后端模块</option>
|
|||
|
|
<option>服务模块</option>
|
|||
|
|
<option>工具模块</option>
|
|||
|
|
</select>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="tab-pane fade" id="techStack">
|
|||
|
|
<div class="mb-3">
|
|||
|
|
<label class="form-label">编程语言</label>
|
|||
|
|
<select class="form-select">
|
|||
|
|
<option selected>Java</option>
|
|||
|
|
<option>Python</option>
|
|||
|
|
<option>JavaScript</option>
|
|||
|
|
<option>C++</option>
|
|||
|
|
</select>
|
|||
|
|
</div>
|
|||
|
|
<div class="mb-3">
|
|||
|
|
<label class="form-label">框架选择</label>
|
|||
|
|
<select class="form-select">
|
|||
|
|
<option selected>Spring Boot</option>
|
|||
|
|
<option>Spring MVC</option>
|
|||
|
|
<option>Micronaut</option>
|
|||
|
|
</select>
|
|||
|
|
</div>
|
|||
|
|
<div class="mb-3">
|
|||
|
|
<label class="form-label">数据库</label>
|
|||
|
|
<select class="form-select">
|
|||
|
|
<option selected>MySQL</option>
|
|||
|
|
<option>PostgreSQL</option>
|
|||
|
|
<option>MongoDB</option>
|
|||
|
|
<option>Oracle</option>
|
|||
|
|
</select>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="tab-pane fade" id="apiDesign">
|
|||
|
|
<button class="btn btn-sm btn-primary mb-3">
|
|||
|
|
<i class="bi bi-plus"></i> 添加接口
|
|||
|
|
</button>
|
|||
|
|
<div class="table-responsive">
|
|||
|
|
<table class="table table-dark table-hover">
|
|||
|
|
<thead>
|
|||
|
|
<tr>
|
|||
|
|
<th>接口路径</th>
|
|||
|
|
<th>方法</th>
|
|||
|
|
<th>描述</th>
|
|||
|
|
<th>操作</th>
|
|||
|
|
</tr>
|
|||
|
|
</thead>
|
|||
|
|
<tbody>
|
|||
|
|
<tr>
|
|||
|
|
<td>/api/auth/login</td>
|
|||
|
|
<td><span class="badge bg-success">POST</span></td>
|
|||
|
|
<td>用户登录</td>
|
|||
|
|
<td>
|
|||
|
|
<button class="btn btn-sm btn-outline-primary"><i class="bi bi-pencil"></i></button>
|
|||
|
|
<button class="btn btn-sm btn-outline-danger"><i class="bi bi-trash"></i></button>
|
|||
|
|
</td>
|
|||
|
|
</tr>
|
|||
|
|
</tbody>
|
|||
|
|
</table>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="tab-pane fade" id="dataModel">
|
|||
|
|
<button class="btn btn-sm btn-primary mb-3">
|
|||
|
|
<i class="bi bi-plus"></i> 添加数据表
|
|||
|
|
</button>
|
|||
|
|
<div class="accordion" id="tableAccordion">
|
|||
|
|
<div class="accordion-item bg-transparent border-secondary">
|
|||
|
|
<h2 class="accordion-header">
|
|||
|
|
<button class="accordion-button bg-transparent text-white" type="button" data-bs-toggle="collapse" data-bs-target="#table1">
|
|||
|
|
用户表 (user)
|
|||
|
|
</button>
|
|||
|
|
</h2>
|
|||
|
|
<div id="table1" class="accordion-collapse collapse show">
|
|||
|
|
<div class="accordion-body">
|
|||
|
|
<table class="table table-sm table-dark">
|
|||
|
|
<thead>
|
|||
|
|
<tr>
|
|||
|
|
<th>字段名</th>
|
|||
|
|
<th>类型</th>
|
|||
|
|
<th>约束</th>
|
|||
|
|
</tr>
|
|||
|
|
</thead>
|
|||
|
|
<tbody>
|
|||
|
|
<tr>
|
|||
|
|
<td>id</td>
|
|||
|
|
<td>BIGINT</td>
|
|||
|
|
<td>PRIMARY KEY</td>
|
|||
|
|
</tr>
|
|||
|
|
<tr>
|
|||
|
|
<td>username</td>
|
|||
|
|
<td>VARCHAR(50)</td>
|
|||
|
|
<td>NOT NULL, UNIQUE</td>
|
|||
|
|
</tr>
|
|||
|
|
<tr>
|
|||
|
|
<td>password</td>
|
|||
|
|
<td>VARCHAR(255)</td>
|
|||
|
|
<td>NOT NULL</td>
|
|||
|
|
</tr>
|
|||
|
|
</tbody>
|
|||
|
|
</table>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="modal-footer">
|
|||
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
|
|||
|
|
<button type="button" class="btn btn-primary" onclick="saveModuleConfig()">保存配置</button>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
`;
|
|||
|
|
|
|||
|
|
// 移除旧的模态框
|
|||
|
|
const oldModal = document.getElementById('moduleConfigModal');
|
|||
|
|
if (oldModal) {
|
|||
|
|
oldModal.remove();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 添加新模态框
|
|||
|
|
document.body.insertAdjacentHTML('beforeend', modalHtml);
|
|||
|
|
const modal = new bootstrap.Modal(document.getElementById('moduleConfigModal'));
|
|||
|
|
modal.show();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function saveModuleConfig() {
|
|||
|
|
showToast('success', '模块配置已保存');
|
|||
|
|
bootstrap.Modal.getInstance(document.getElementById('moduleConfigModal')).hide();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 代码预览弹窗
|
|||
|
|
function showCodePreview(fileName, code) {
|
|||
|
|
const modalHtml = `
|
|||
|
|
<div class="modal fade" id="codePreviewModal" tabindex="-1">
|
|||
|
|
<div class="modal-dialog modal-xl">
|
|||
|
|
<div class="modal-content">
|
|||
|
|
<div class="modal-header">
|
|||
|
|
<h5 class="modal-title">
|
|||
|
|
<i class="bi bi-file-earmark-code"></i> ${fileName}
|
|||
|
|
</h5>
|
|||
|
|
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
|
|||
|
|
</div>
|
|||
|
|
<div class="modal-body">
|
|||
|
|
<div class="d-flex justify-content-between mb-2">
|
|||
|
|
<div>
|
|||
|
|
<span class="badge bg-success">代码质量: A</span>
|
|||
|
|
<span class="badge bg-info ms-1">156 行</span>
|
|||
|
|
</div>
|
|||
|
|
<button class="btn btn-sm btn-outline-primary" onclick="copyCode()">
|
|||
|
|
<i class="bi bi-clipboard"></i> 复制代码
|
|||
|
|
</button>
|
|||
|
|
</div>
|
|||
|
|
<pre class="bg-dark p-3 rounded" style="max-height: 500px; overflow-y: auto;"><code class="language-java" id="codeContent">${code || `package com.example.auth;
|
|||
|
|
|
|||
|
|
import org.springframework.web.bind.annotation.*;
|
|||
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 用户认证控制器
|
|||
|
|
* @author AI Generator
|
|||
|
|
* @date 2024-01-15
|
|||
|
|
*/
|
|||
|
|
@RestController
|
|||
|
|
@RequestMapping("/api/auth")
|
|||
|
|
public class AuthController {
|
|||
|
|
|
|||
|
|
@Autowired
|
|||
|
|
private AuthService authService;
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 用户登录
|
|||
|
|
* @param loginRequest 登录请求
|
|||
|
|
* @return 登录响应
|
|||
|
|
*/
|
|||
|
|
@PostMapping("/login")
|
|||
|
|
public ResponseEntity<LoginResponse> login(@RequestBody LoginRequest loginRequest) {
|
|||
|
|
// 验证用户名和密码
|
|||
|
|
User user = authService.authenticate(
|
|||
|
|
loginRequest.getUsername(),
|
|||
|
|
loginRequest.getPassword()
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
if (user != null) {
|
|||
|
|
// 生成 JWT Token
|
|||
|
|
String token = jwtUtil.generateToken(user);
|
|||
|
|
return ResponseEntity.ok(new LoginResponse(token, user));
|
|||
|
|
} else {
|
|||
|
|
return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
|
|||
|
|
.body(new LoginResponse("认证失败"));
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 用户注册
|
|||
|
|
* @param registerRequest 注册请求
|
|||
|
|
* @return 注册响应
|
|||
|
|
*/
|
|||
|
|
@PostMapping("/register")
|
|||
|
|
public ResponseEntity<RegisterResponse> register(@RequestBody RegisterRequest registerRequest) {
|
|||
|
|
// 检查用户名是否已存在
|
|||
|
|
if (authService.userExists(registerRequest.getUsername())) {
|
|||
|
|
return ResponseEntity.status(HttpStatus.CONFLICT)
|
|||
|
|
.body(new RegisterResponse("用户名已存在"));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 创建新用户
|
|||
|
|
User newUser = authService.createUser(registerRequest);
|
|||
|
|
return ResponseEntity.ok(new RegisterResponse("注册成功", newUser));
|
|||
|
|
}
|
|||
|
|
}`}</code></pre>
|
|||
|
|
</div>
|
|||
|
|
<div class="modal-footer">
|
|||
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">关闭</button>
|
|||
|
|
<button type="button" class="btn btn-success" onclick="downloadCode('${fileName}')">
|
|||
|
|
<i class="bi bi-download"></i> 下载
|
|||
|
|
</button>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
`;
|
|||
|
|
|
|||
|
|
const oldModal = document.getElementById('codePreviewModal');
|
|||
|
|
if (oldModal) {
|
|||
|
|
oldModal.remove();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
document.body.insertAdjacentHTML('beforeend', modalHtml);
|
|||
|
|
const modal = new bootstrap.Modal(document.getElementById('codePreviewModal'));
|
|||
|
|
modal.show();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function copyCode() {
|
|||
|
|
const code = document.getElementById('codeContent').textContent;
|
|||
|
|
navigator.clipboard.writeText(code).then(() => {
|
|||
|
|
showToast('success', '代码已复制到剪贴板');
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 流水线日志查看
|
|||
|
|
function showPipelineLog(stage) {
|
|||
|
|
const logs = {
|
|||
|
|
scan: `[INFO] 开始静态代码扫描...
|
|||
|
|
[INFO] 扫描工具: SonarQube 9.9
|
|||
|
|
[INFO] 扫描文件: 156 个
|
|||
|
|
[INFO] 发现问题: 3 个
|
|||
|
|
[WARN] 代码复杂度较高: AuthService.java:45
|
|||
|
|
[WARN] 未使用的导入: UserController.java:12
|
|||
|
|
[INFO] 代码覆盖率: 85%
|
|||
|
|
[INFO] 扫描完成,耗时: 2分15秒`,
|
|||
|
|
|
|||
|
|
build: `[INFO] 开始编译构建...
|
|||
|
|
[INFO] 构建工具: Maven 3.8.6
|
|||
|
|
[INFO] JDK 版本: 17.0.5
|
|||
|
|
[INFO] 下载依赖...
|
|||
|
|
[INFO] 编译源代码...
|
|||
|
|
[INFO] 编译测试代码...
|
|||
|
|
[INFO] 运行单元测试...
|
|||
|
|
[INFO] 测试通过: 45/48
|
|||
|
|
[WARN] 测试失败: 3 个
|
|||
|
|
[INFO] 构建进度: 65%`,
|
|||
|
|
|
|||
|
|
package: `[INFO] 开始打包...
|
|||
|
|
[INFO] 打包格式: JAR
|
|||
|
|
[INFO] 包含资源文件...
|
|||
|
|
[INFO] 生成 MANIFEST.MF
|
|||
|
|
[INFO] 打包完成
|
|||
|
|
[INFO] 输出文件: target/smart-monitor-1.0.0.jar
|
|||
|
|
[INFO] 文件大小: 45.6 MB`,
|
|||
|
|
|
|||
|
|
deploy: `[INFO] 准备部署...
|
|||
|
|
[INFO] 目标环境: 测试环境
|
|||
|
|
[INFO] 服务器: 192.168.1.100:8080
|
|||
|
|
[INFO] 上传部署包...
|
|||
|
|
[INFO] 停止旧服务...
|
|||
|
|
[INFO] 启动新服务...
|
|||
|
|
[INFO] 健康检查...
|
|||
|
|
[INFO] 部署成功`,
|
|||
|
|
|
|||
|
|
verify: `[INFO] 开始验证测试...
|
|||
|
|
[INFO] 测试用例总数: 25
|
|||
|
|
[INFO] 运行接口测试...
|
|||
|
|
[INFO] 运行性能测试...
|
|||
|
|
[INFO] 运行安全测试...
|
|||
|
|
[INFO] 测试通过: 22/25
|
|||
|
|
[WARN] 测试失败: 3 个
|
|||
|
|
[INFO] 测试通过率: 88%`
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const modalHtml = `
|
|||
|
|
<div class="modal fade" id="logModal" tabindex="-1">
|
|||
|
|
<div class="modal-dialog modal-lg">
|
|||
|
|
<div class="modal-content">
|
|||
|
|
<div class="modal-header">
|
|||
|
|
<h5 class="modal-title">
|
|||
|
|
<i class="bi bi-terminal"></i> ${stage} 日志
|
|||
|
|
</h5>
|
|||
|
|
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
|
|||
|
|
</div>
|
|||
|
|
<div class="modal-body">
|
|||
|
|
<div class="d-flex justify-content-between mb-2">
|
|||
|
|
<div class="btn-group btn-group-sm">
|
|||
|
|
<button class="btn btn-outline-primary active">全部</button>
|
|||
|
|
<button class="btn btn-outline-info">INFO</button>
|
|||
|
|
<button class="btn btn-outline-warning">WARN</button>
|
|||
|
|
<button class="btn btn-outline-danger">ERROR</button>
|
|||
|
|
</div>
|
|||
|
|
<button class="btn btn-sm btn-outline-success" onclick="exportLog()">
|
|||
|
|
<i class="bi bi-download"></i> 导出日志
|
|||
|
|
</button>
|
|||
|
|
</div>
|
|||
|
|
<pre class="bg-dark p-3 rounded" style="max-height: 400px; overflow-y: auto; font-family: 'Consolas', monospace; font-size: 13px;">${logs[stage] || '暂无日志'}</pre>
|
|||
|
|
</div>
|
|||
|
|
<div class="modal-footer">
|
|||
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">关闭</button>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
`;
|
|||
|
|
|
|||
|
|
const oldModal = document.getElementById('logModal');
|
|||
|
|
if (oldModal) {
|
|||
|
|
oldModal.remove();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
document.body.insertAdjacentHTML('beforeend', modalHtml);
|
|||
|
|
const modal = new bootstrap.Modal(document.getElementById('logModal'));
|
|||
|
|
modal.show();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function exportLog() {
|
|||
|
|
showToast('success', '日志已导出');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 添加代码树点击事件
|
|||
|
|
document.querySelectorAll('.tree-file').forEach(node => {
|
|||
|
|
node.addEventListener('dblclick', function() {
|
|||
|
|
const fileName = this.querySelector('span').textContent;
|
|||
|
|
showCodePreview(fileName);
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 添加流水线步骤点击事件
|
|||
|
|
document.querySelectorAll('.pipeline-step').forEach(step => {
|
|||
|
|
const originalOnclick = step.getAttribute('onclick');
|
|||
|
|
step.setAttribute('onclick', originalOnclick + '; event.stopPropagation();');
|
|||
|
|
step.addEventListener('dblclick', function() {
|
|||
|
|
const stageName = this.querySelector('.step-name').textContent;
|
|||
|
|
const stageMap = {
|
|||
|
|
'静态扫描': 'scan',
|
|||
|
|
'编译构建': 'build',
|
|||
|
|
'打包': 'package',
|
|||
|
|
'部署': 'deploy',
|
|||
|
|
'验证测试': 'verify'
|
|||
|
|
};
|
|||
|
|
showPipelineLog(stageMap[stageName]);
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 初始化提示
|
|||
|
|
setTimeout(() => {
|
|||
|
|
showToast('info', '欢迎使用智能低代码开发平台!按 Ctrl+N 创建新项目');
|
|||
|
|
}, 1000);
|
|||
|
|
|
|||
|
|
// 自动保存功能
|
|||
|
|
let autoSaveTimer = null;
|
|||
|
|
function enableAutoSave() {
|
|||
|
|
const inputs = document.querySelectorAll('input[type="text"], textarea');
|
|||
|
|
inputs.forEach(input => {
|
|||
|
|
input.addEventListener('input', function() {
|
|||
|
|
clearTimeout(autoSaveTimer);
|
|||
|
|
autoSaveTimer = setTimeout(() => {
|
|||
|
|
console.log('自动保存...');
|
|||
|
|
// 这里可以添加实际的保存逻辑
|
|||
|
|
}, 30000); // 30秒后自动保存
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
enableAutoSave();
|
|||
|
|
|
|||
|
|
// 网络状态监听
|
|||
|
|
window.addEventListener('online', function() {
|
|||
|
|
showToast('success', '网络已连接');
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
window.addEventListener('offline', function() {
|
|||
|
|
showToast('warning', '网络已断开,数据将保存在本地');
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 页面可见性变化
|
|||
|
|
document.addEventListener('visibilitychange', function() {
|
|||
|
|
if (document.hidden) {
|
|||
|
|
console.log('页面已隐藏');
|
|||
|
|
} else {
|
|||
|
|
console.log('页面已显示');
|
|||
|
|
// 可以在这里刷新数据
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 防止意外关闭
|
|||
|
|
window.addEventListener('beforeunload', function(e) {
|
|||
|
|
if (currentStep > 1 && currentStep < 4) {
|
|||
|
|
e.preventDefault();
|
|||
|
|
e.returnValue = '您有未完成的项目创建,确定要离开吗?';
|
|||
|
|
return e.returnValue;
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 性能监控
|
|||
|
|
if ('PerformanceObserver' in window) {
|
|||
|
|
const observer = new PerformanceObserver((list) => {
|
|||
|
|
for (const entry of list.getEntries()) {
|
|||
|
|
if (entry.duration > 1000) {
|
|||
|
|
console.warn(`性能警告: ${entry.name} 耗时 ${entry.duration}ms`);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
observer.observe({ entryTypes: ['measure', 'navigation'] });
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
console.log('%c智能低代码开发平台', 'color: #3b82f6; font-size: 24px; font-weight: bold;');
|
|||
|
|
console.log('%c版本: 1.0.0', 'color: #94a3b8; font-size: 14px;');
|
|||
|
|
console.log('%c基于 Bootstrap 5 + 原生 JavaScript 构建', 'color: #94a3b8; font-size: 12px;');
|
|||
|
|
</script>
|
|||
|
|
</body>
|
|||
|
|
</html>
|