手写科技感弹窗,流程样式:

布局代码:
javascript
// index.tsx
import scrollstyles from './index.less';
<div
className={`${scrollstyles.detailModalOverlay} ${scrollstyles.detailModalVisible}`}
onClick={handleCloseModal}
>
<div
className={scrollstyles.detailModalContent}
onClick={(e) => e.stopPropagation()}
>
{/* 装饰边框 */}
<div className={scrollstyles.detailModalBorder}>
<div className={scrollstyles.detailCornerTopLeft}></div>
<div className={scrollstyles.detailCornerTopRight}></div>
<div className={scrollstyles.detailCornerBottomLeft}></div>
<div className={scrollstyles.detailCornerBottomRight}></div>
<div className={scrollstyles.detailBorderLineTop}></div>
<div className={scrollstyles.detailBorderLineBottom}></div>
<div className={scrollstyles.detailBorderLineLeft}></div>
<div className={scrollstyles.detailBorderLineRight}></div>
</div>
{/* 弹窗头部 */}
<div className={scrollstyles.detailModalHeader}>
<div className={scrollstyles.detailModalTitle}>
<span className={scrollstyles.detailTitleIcon}>◆</span>
异常详情 - {selectedData.id}
</div>
<div
className={scrollstyles.detailModalClose}
onClick={handleCloseModal}
>
✕
</div>
</div>
{/* 弹窗主体 - 时间线布局 */}
<div className={scrollstyles.detailModalBody}>
<div className={scrollstyles.detailModalInfo}>
<div className={scrollstyles.detailInfoItem}>
<span className={scrollstyles.detailInfoLabel}>异常ID</span>
<span className={scrollstyles.detailInfoValue}>
{selectedData.id}
</span>
</div>
<div className={scrollstyles.detailInfoItem}>
<span className={scrollstyles.detailInfoLabel}>类型</span>
<span className={scrollstyles.detailInfoValue}>
{selectedData.type}
</span>
</div>
<div className={scrollstyles.detailInfoItem}>
<span className={scrollstyles.detailInfoLabel}>异常详情</span>
<span className={scrollstyles.detailInfoValue}>
{selectedData.detail}
</span>
</div>
</div>
{/* 时间线 */}
<div className={scrollstyles.detailTimeline}>
{selectedData.stages.map((stage, index) => (
<div key={index} className={scrollstyles.detailTimelineItem}>
<div className={scrollstyles.detailTimelineDot}>
<span className={scrollstyles.detailDotInner}></span>
</div>
{index < selectedData.stages.length - 1 && (
<div className={scrollstyles.detailTimelineLine}></div>
)}
<div className={scrollstyles.detailTimelineContent}>
<div className={scrollstyles.detailTimelineHeader}>
<span className={scrollstyles.detailTimelineName}>
{stage.name}
</span>
<span className={scrollstyles.detailTimelineStatus}>
{stage.status && (
<span className={scrollstyles.detailStatusTag}>
{stage.status}
</span>
)}
</span>
</div>
<div className={scrollstyles.detailTimelineDesc}>
{stage.description}
</div>
<div className={scrollstyles.detailTimelineTime}>
{stage.time}
</div>
</div>
</div>
))}
</div>
{/* 底部操作按钮 */}
<div className={scrollstyles.detailModalFooter}>
<div
className={scrollstyles.detailReportBtn}
onClick={handleGenerateReport}
>
<span className={scrollstyles.detailBtnIcon}>📄</span>
生成处置报告
<span className={scrollstyles.detailBtnGlow}></span>
</div>
</div>
</div>
</div>
</div>
javascript
// index/less
// 弹窗遮罩层
.detailModalOverlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 70%);
backdrop-filter: blur(8px);
display: flex;
align-items: center;
justify-content: center;
z-index: 9998;
opacity: 0;
visibility: hidden;
transition: all 0.3s ease;
&.detailModalVisible {
opacity: 1;
visibility: visible;
}
}
// 弹窗内容
.detailModalContent {
position: relative;
width: 600px;
max-height: 80vh;
background: linear-gradient(
145deg,
rgba(0, 20, 50, 95%),
rgba(0, 10, 30, 98%)
);
border: 1px solid rgba(0, 180, 255, 30%);
border-radius: 12px;
padding: 24px 28px 28px;
box-shadow: 0 0 60px rgba(0, 100, 255, 15%),
inset 0 0 60px rgba(0, 100, 255, 5%);
overflow-y: auto;
transform: scale(0.95) translateY(20px);
transition: all 0.3s ease;
.detailModalOverlay.detailModalVisible & {
transform: scale(1) translateY(0);
}
&::-webkit-scrollbar {
width: 4px;
}
&::-webkit-scrollbar-track {
background: rgba(0, 40, 80, 30%);
border-radius: 2px;
}
&::-webkit-scrollbar-thumb {
background: rgba(0, 180, 255, 40%);
border-radius: 2px;
}
}
// ===== 装饰边框 =====
.detailModalBorder {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
pointer-events: none;
border-radius: 12px;
overflow: hidden;
.detailCornerTopLeft,
.detailCornerTopRight,
.detailCornerBottomLeft,
.detailCornerBottomRight {
position: absolute;
width: 20px;
height: 20px;
border-color: rgba(0, 180, 255, 60%);
border-style: solid;
border-width: 0;
}
.detailCornerTopLeft {
top: 6px;
left: 6px;
border-top-width: 2px;
border-left-width: 2px;
}
.detailCornerTopRight {
top: 6px;
right: 6px;
border-top-width: 2px;
border-right-width: 2px;
}
.detailCornerBottomLeft {
bottom: 6px;
left: 6px;
border-bottom-width: 2px;
border-left-width: 2px;
}
.detailCornerBottomRight {
bottom: 6px;
right: 6px;
border-bottom-width: 2px;
border-right-width: 2px;
}
.detailBorderLineTop,
.detailBorderLineBottom,
.detailBorderLineLeft,
.detailBorderLineRight {
position: absolute;
background: linear-gradient(
90deg,
transparent,
rgba(0, 180, 255, 30%),
transparent
);
}
.detailBorderLineTop {
top: 6px;
left: 30px;
right: 30px;
height: 1px;
}
.detailBorderLineBottom {
bottom: 6px;
left: 30px;
right: 30px;
height: 1px;
}
.detailBorderLineLeft {
left: 6px;
top: 30px;
bottom: 30px;
width: 1px;
background: linear-gradient(
180deg,
transparent,
rgba(0, 180, 255, 30%),
transparent
);
}
.detailBorderLineRight {
right: 6px;
top: 30px;
bottom: 30px;
width: 1px;
background: linear-gradient(
180deg,
transparent,
rgba(0, 180, 255, 30%),
transparent
);
}
}
// ===== 弹窗头部 =====
.detailModalHeader {
display: flex;
justify-content: space-between;
align-items: center;
padding-bottom: 16px;
border-bottom: 1px solid rgba(0, 180, 255, 20%);
margin-bottom: 20px;
.detailModalTitle {
color: #00d4ff;
font-size: 18px;
font-weight: 600;
text-shadow: 0 0 20px rgba(0, 180, 255, 30%);
letter-spacing: 2px;
.detailTitleIcon {
color: #00d4ff;
margin-right: 10px;
font-size: 14px;
animation: pulse 2s ease-in-out infinite;
}
}
.detailModalClose {
background: none;
border: 1px solid rgba(0, 180, 255, 30%);
color: #00d4ff;
width: 32px;
height: 32px;
border-radius: 50%;
cursor: pointer;
font-size: 16px;
transition: all 0.3s ease;
display: flex;
align-items: center;
justify-content: center;
&:hover {
background: rgba(0, 180, 255, 15%);
border-color: rgba(0, 180, 255, 60%);
transform: rotate(90deg);
}
}
}
// ===== 弹窗主体 =====
.detailModalBody {
.detailModalInfo {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
gap: 12px;
margin-bottom: 20px;
padding: 16px;
background: rgba(0, 40, 80, 30%);
border-radius: 8px;
border: 1px solid rgba(0, 180, 255, 10%);
.detailInfoItem {
display: flex;
flex-direction: column;
.detailInfoLabel {
color: rgba(0, 180, 255, 60%);
font-size: 11px;
text-transform: uppercase;
letter-spacing: 1px;
margin-bottom: 8px;
}
.detailInfoValue {
color: #b8d4e8;
font-size: 14px;
font-weight: 500;
}
}
}
// ===== 时间线 =====
.detailTimeline {
position: relative;
padding-left: 28px;
.detailTimelineItem {
position: relative;
padding-bottom: 24px;
&:last-child {
padding-bottom: 0;
}
.detailTimelineDot {
position: absolute;
left: -28px;
top: 4px;
width: 16px;
height: 16px;
background: rgba(0, 180, 255, 15%);
border: 2px solid rgba(0, 180, 255, 40%);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
.detailDotInner {
width: 6px;
height: 6px;
background: #00d4ff;
border-radius: 50%;
box-shadow: 0 0 12px rgba(0, 180, 255, 50%);
}
}
.detailTimelineLine {
position: absolute;
left: -21px;
top: 20px;
width: 2px;
height: calc(100% - 4px);
background: linear-gradient(
180deg,
rgba(0, 180, 255, 30%),
transparent
);
}
.detailTimelineContent {
padding-left: 12px;
.detailTimelineHeader {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 4px;
.detailTimelineName {
color: #00d4ff;
font-size: 14px;
font-weight: 500;
}
.detailTimelineStatus {
.detailStatusTag {
display: inline-block;
padding: 1px 10px;
border-radius: 10px;
font-size: 11px;
background: rgba(255, 200, 0, 15%);
color: #fc0;
border: 1px solid rgba(255, 200, 0, 30%);
}
}
}
.detailTimelineDesc {
color: #b8d4e8;
font-size: 13px;
margin-bottom: 8px;
margin-top: 8px;
}
.detailTimelineTime {
color: rgba(0, 180, 255, 50%);
font-size: 12px;
font-family: 'Courier New', monospace;
}
}
}
}
// ===== 底部操作按钮 =====
.detailModalFooter {
display: flex;
justify-content: center;
align-items: center;
padding-top: 20px;
margin-top: 16px;
border-top: 1px solid rgba(0, 180, 255, 15%);
position: relative;
.detailReportBtn {
position: relative;
display: flex;
align-items: center;
justify-content: center;
gap: 10px;
padding: 10px 36px;
background: linear-gradient(
135deg,
rgba(0, 180, 255, 15%),
rgba(0, 100, 255, 15%)
);
border: 1px solid rgba(0, 180, 255, 40%);
border-radius: 8px;
color: #00d4ff;
font-size: 15px;
font-weight: 500;
cursor: pointer;
transition: all 0.35s ease;
letter-spacing: 2px;
text-shadow: 0 0 20px rgba(0, 180, 255, 20%);
overflow: hidden;
user-select: none;
.detailBtnIcon {
font-size: 18px;
position: relative;
z-index: 1;
}
.detailBtnGlow {
position: absolute;
top: -50%;
left: -50%;
width: 200%;
height: 200%;
background: radial-gradient(
circle at center,
rgba(0, 180, 255, 10%) 0%,
transparent 60%
);
opacity: 0;
transition: opacity 0.5s ease;
pointer-events: none;
}
&::before {
content: '';
position: absolute;
inset: -1px;
border-radius: 8px;
padding: 1px;
background: linear-gradient(
135deg,
rgba(0, 180, 255, 30%),
rgba(0, 100, 255, 10%),
rgba(0, 180, 255, 30%)
);
mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
mask-composite: xor;
mask-composite: exclude;
opacity: 0;
transition: opacity 0.4s ease;
}
&:hover {
background: linear-gradient(
135deg,
rgba(0, 180, 255, 25%),
rgba(0, 100, 255, 25%)
);
border-color: rgba(0, 180, 255, 70%);
box-shadow: 0 0 30px rgba(0, 180, 255, 15%),
inset 0 0 30px rgba(0, 180, 255, 5%);
transform: translateY(-2px) scale(1.02);
&::before {
opacity: 1;
}
.detailBtnGlow {
opacity: 1;
animation: glow-pulse 1.5s ease-in-out infinite;
}
}
&:active {
transform: translateY(0) scale(0.98);
}
}
}
}