优化:全面重构任务节点查询弹窗UI设计,统一使用网站设计风格
This commit is contained in:
parent
0bb9975f31
commit
d987fb6016
@ -2,25 +2,34 @@
|
||||
<a-modal
|
||||
v-model:open="dialogVisible"
|
||||
title="任务节点查询"
|
||||
width="720px"
|
||||
width="680px"
|
||||
:footer="null"
|
||||
:maskClosable="false"
|
||||
@cancel="handleClose"
|
||||
class="task-flow-query-modal"
|
||||
centered
|
||||
>
|
||||
<!-- 搜索框 -->
|
||||
<div class="search-box">
|
||||
<a-input-search
|
||||
v-model:value="searchInput"
|
||||
placeholder="请输入任务编号或任务标题关键字"
|
||||
size="large"
|
||||
enter-button="查询"
|
||||
:loading="loading"
|
||||
@search="handleSearch"
|
||||
>
|
||||
<template #prefix>
|
||||
<Icon name="search" size="sm" />
|
||||
</template>
|
||||
</a-input-search>
|
||||
<div class="search-section">
|
||||
<div class="search-input-wrapper">
|
||||
<Icon name="search" size="sm" class="search-prefix-icon" />
|
||||
<input
|
||||
v-model="searchInput"
|
||||
type="text"
|
||||
class="search-input"
|
||||
placeholder="请输入任务编号或任务标题关键字"
|
||||
@keyup.enter="handleSearch"
|
||||
/>
|
||||
<button
|
||||
class="search-btn"
|
||||
:class="{ loading: loading }"
|
||||
:disabled="loading"
|
||||
@click="handleSearch"
|
||||
>
|
||||
<span v-if="!loading">查询</span>
|
||||
<span v-else class="loading-spinner"></span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="search-tip">
|
||||
支持输入完整任务编号(如 TASK202601221029343146)或任务标题关键字搜索
|
||||
</div>
|
||||
@ -29,30 +38,38 @@
|
||||
<!-- 查询结果 -->
|
||||
<div v-if="result" class="result-container">
|
||||
<!-- 基本信息卡片 -->
|
||||
<a-card class="info-card" size="small">
|
||||
<template #title>
|
||||
<div class="card-header">
|
||||
<span>任务信息</span>
|
||||
<a-tag v-if="result.task.isPrimaryTask" color="warning">一级需求任务</a-tag>
|
||||
<a-tag v-else>普通任务</a-tag>
|
||||
<a-tag v-if="result.task.isPipeline" color="processing">流水线</a-tag>
|
||||
<div class="info-section">
|
||||
<div class="section-header">
|
||||
<span class="section-title">任务信息</span>
|
||||
<div class="task-tags">
|
||||
<span v-if="result.task.isPrimaryTask" class="tag tag-warning">一级需求任务</span>
|
||||
<span v-else class="tag tag-default">普通任务</span>
|
||||
<span v-if="result.task.isPipeline" class="tag tag-info">流水线</span>
|
||||
</div>
|
||||
</template>
|
||||
<a-descriptions :column="2" bordered size="small">
|
||||
<a-descriptions-item label="任务编号">
|
||||
<a-typography-text copyable>{{ result.task.taskNo }}</a-typography-text>
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="任务标题">{{ result.task.title }}</a-descriptions-item>
|
||||
<a-descriptions-item label="任务积分">
|
||||
<span class="score-text">{{ result.task.score }} 分</span>
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="创建时间">{{ formatTime(result.task.createTime) }}</a-descriptions-item>
|
||||
</a-descriptions>
|
||||
</a-card>
|
||||
</div>
|
||||
<div class="info-grid">
|
||||
<div class="info-item">
|
||||
<span class="info-label">任务编号</span>
|
||||
<span class="info-value task-no">{{ result.task.taskNo }}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-label">任务标题</span>
|
||||
<span class="info-value">{{ result.task.title }}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-label">任务积分</span>
|
||||
<span class="info-value score">{{ result.task.score }} 分</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-label">创建时间</span>
|
||||
<span class="info-value">{{ formatTime(result.task.createTime) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 当前节点卡片(重点展示) -->
|
||||
<div class="current-node-card">
|
||||
<div class="current-node">
|
||||
<div class="current-node-section">
|
||||
<div class="current-node-content">
|
||||
<div class="node-icon">📍</div>
|
||||
<div class="node-info">
|
||||
<div class="node-status">当前节点:{{ result.currentNode.statusName }}</div>
|
||||
@ -71,70 +88,88 @@
|
||||
</div>
|
||||
|
||||
<!-- 流程时间线 -->
|
||||
<a-card class="timeline-card" size="small" title="流程进度">
|
||||
<a-timeline>
|
||||
<a-timeline-item
|
||||
v-for="(node, index) in result.flowTimeline"
|
||||
<div class="timeline-section">
|
||||
<div class="section-header">
|
||||
<span class="section-title">流程进度</span>
|
||||
</div>
|
||||
<div class="timeline">
|
||||
<div
|
||||
v-for="(node, index) in result.flowTimeline"
|
||||
:key="index"
|
||||
:color="getTimelineColor(node.status)"
|
||||
class="timeline-item"
|
||||
:class="[`status-${node.status}`]"
|
||||
>
|
||||
<div class="timeline-node">
|
||||
<div class="node-name">
|
||||
{{ node.nodeName }}
|
||||
<a-tag v-if="node.status === 'pending'" color="warning" size="small">当前</a-tag>
|
||||
<div class="timeline-dot"></div>
|
||||
<div class="timeline-line" v-if="index < result.flowTimeline.length - 1"></div>
|
||||
<div class="timeline-content">
|
||||
<div class="timeline-header">
|
||||
<span class="timeline-title">{{ node.nodeName }}</span>
|
||||
<span v-if="node.status === 'pending'" class="tag tag-current">当前</span>
|
||||
</div>
|
||||
<div class="node-time">
|
||||
<div class="timeline-time">
|
||||
{{ node.time ? formatTime(node.time) : (node.status === 'pending' ? '进行中...' : '等待中') }}
|
||||
</div>
|
||||
<div class="node-operator" v-if="node.operatorName">
|
||||
<div class="timeline-operator" v-if="node.operatorName">
|
||||
<Icon name="user" size="xs" />
|
||||
{{ node.operatorName }}
|
||||
<span v-if="node.operatorDept">({{ node.operatorDept }})</span>
|
||||
<span>{{ node.operatorName }}</span>
|
||||
<span v-if="node.operatorDept" class="operator-dept">({{ node.operatorDept }})</span>
|
||||
</div>
|
||||
<div class="node-comment" v-if="node.comment">
|
||||
<div class="timeline-comment" v-if="node.comment">
|
||||
<Icon name="info" size="xs" />
|
||||
{{ node.comment }}
|
||||
<span>{{ node.comment }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</a-timeline-item>
|
||||
</a-timeline>
|
||||
</a-card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 相关人员 -->
|
||||
<a-card class="users-card" size="small" title="相关人员">
|
||||
<div class="users-section">
|
||||
<div class="section-header">
|
||||
<span class="section-title">相关人员</span>
|
||||
</div>
|
||||
<div class="users-grid">
|
||||
<div class="user-item" v-if="result.relatedUsers.publisher">
|
||||
<div class="user-card" v-if="result.relatedUsers.publisher">
|
||||
<div class="user-role">发起人</div>
|
||||
<div class="user-name">{{ result.relatedUsers.publisher.name }}</div>
|
||||
<div class="user-dept">{{ result.relatedUsers.publisher.dept }}</div>
|
||||
</div>
|
||||
<div class="user-item" v-if="result.relatedUsers.detailCompleter">
|
||||
<div class="user-card" v-if="result.relatedUsers.detailCompleter">
|
||||
<div class="user-role">明细完善人</div>
|
||||
<div class="user-name">{{ result.relatedUsers.detailCompleter.name }}</div>
|
||||
<div class="user-dept">{{ result.relatedUsers.detailCompleter.dept }}</div>
|
||||
</div>
|
||||
<div class="user-item" v-if="result.relatedUsers.receiver">
|
||||
<div class="user-card" v-if="result.relatedUsers.receiver">
|
||||
<div class="user-role">接单人</div>
|
||||
<div class="user-name">{{ result.relatedUsers.receiver.name }}</div>
|
||||
<div class="user-dept">{{ result.relatedUsers.receiver.dept }}</div>
|
||||
</div>
|
||||
<div class="user-item" v-if="result.relatedUsers.currentApprover">
|
||||
<div class="user-card" v-if="result.relatedUsers.currentApprover">
|
||||
<div class="user-role">当前审批人</div>
|
||||
<div class="user-name">{{ result.relatedUsers.currentApprover.name }}</div>
|
||||
<div class="user-dept">{{ result.relatedUsers.currentApprover.dept }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</a-card>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 空状态 -->
|
||||
<a-empty v-else-if="searched && !result && !loading" description="未找到相关任务" />
|
||||
<div v-else-if="searched && !result && !loading" class="empty-state">
|
||||
<Icon name="inbox" size="xl" class="empty-icon" />
|
||||
<p>未找到相关任务</p>
|
||||
</div>
|
||||
|
||||
<!-- 初始状态 -->
|
||||
<div v-else-if="!searched" class="initial-state">
|
||||
<Icon name="search" size="xl" class="search-icon" />
|
||||
<div v-else-if="!searched && !loading" class="initial-state">
|
||||
<Icon name="search" size="xl" class="initial-icon" />
|
||||
<p>输入任务编号或标题关键字查询任务流程状态</p>
|
||||
</div>
|
||||
|
||||
<!-- 加载状态 -->
|
||||
<div v-if="loading && !result" class="loading-state">
|
||||
<div class="loading-spinner large"></div>
|
||||
<p>正在查询...</p>
|
||||
</div>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
@ -210,18 +245,6 @@ const handleClose = () => {
|
||||
searched.value = false
|
||||
}
|
||||
|
||||
// 获取时间线颜色
|
||||
const getTimelineColor = (status: string) => {
|
||||
switch (status) {
|
||||
case 'completed':
|
||||
return 'green'
|
||||
case 'pending':
|
||||
return 'orange'
|
||||
default:
|
||||
return 'gray'
|
||||
}
|
||||
}
|
||||
|
||||
// 格式化时间
|
||||
const formatTime = (time: string | undefined) => {
|
||||
if (!time) return ''
|
||||
@ -241,228 +264,37 @@ defineExpose({
|
||||
<style scoped lang="scss">
|
||||
@import "@/styles/variables.scss";
|
||||
|
||||
.search-box {
|
||||
margin-bottom: 20px;
|
||||
// 搜索区域
|
||||
.search-section {
|
||||
margin-bottom: $space-lg;
|
||||
}
|
||||
|
||||
.search-tip {
|
||||
margin-top: 8px;
|
||||
font-size: 12px;
|
||||
.search-input-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: $bg-tertiary;
|
||||
border: 1px solid $border-primary;
|
||||
border-radius: $radius-lg;
|
||||
padding: 0 $space-md;
|
||||
transition: all $transition-base $ease-out;
|
||||
|
||||
&:focus-within {
|
||||
border-color: $brand-primary;
|
||||
box-shadow: 0 0 0 2px rgba($brand-primary, 0.1);
|
||||
}
|
||||
|
||||
.search-prefix-icon {
|
||||
color: $text-tertiary;
|
||||
}
|
||||
}
|
||||
|
||||
.result-container {
|
||||
max-height: 60vh;
|
||||
overflow-y: auto;
|
||||
|
||||
.info-card {
|
||||
margin-bottom: 16px;
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.score-text {
|
||||
color: $brand-primary;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
.current-node-card {
|
||||
margin-bottom: 16px;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
|
||||
.current-node {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
color: #fff;
|
||||
|
||||
.node-icon {
|
||||
font-size: 40px;
|
||||
margin-right: 16px;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.node-info {
|
||||
flex: 1;
|
||||
|
||||
.node-status {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.node-waiting {
|
||||
font-size: 14px;
|
||||
margin-bottom: 6px;
|
||||
opacity: 0.95;
|
||||
|
||||
strong {
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
.node-desc {
|
||||
font-size: 13px;
|
||||
opacity: 0.85;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.timeline-card {
|
||||
margin-bottom: 16px;
|
||||
|
||||
.timeline-node {
|
||||
.node-name {
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
color: $text-primary;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.node-time {
|
||||
font-size: 12px;
|
||||
color: $text-tertiary;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.node-operator {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
color: $brand-primary;
|
||||
font-size: 13px;
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.node-comment {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
color: $color-success;
|
||||
font-size: 12px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.users-card {
|
||||
.users-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 16px;
|
||||
|
||||
@media (max-width: 600px) {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
|
||||
.user-item {
|
||||
text-align: center;
|
||||
padding: 12px;
|
||||
background: $bg-tertiary;
|
||||
border-radius: 8px;
|
||||
|
||||
.user-role {
|
||||
font-size: 12px;
|
||||
color: $text-tertiary;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.user-name {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: $text-primary;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.user-dept {
|
||||
font-size: 12px;
|
||||
color: $text-secondary;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.initial-state {
|
||||
text-align: center;
|
||||
padding: 60px 0;
|
||||
color: $text-tertiary;
|
||||
|
||||
.search-icon {
|
||||
font-size: 48px;
|
||||
margin-bottom: 16px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
// 暗色模式下的 Modal 样式
|
||||
:deep(.ant-modal-content) {
|
||||
background: $bg-secondary;
|
||||
|
||||
.ant-modal-header {
|
||||
background: $bg-secondary;
|
||||
border-bottom-color: $border-primary;
|
||||
|
||||
.ant-modal-title {
|
||||
color: $text-primary;
|
||||
}
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.ant-modal-close-x {
|
||||
color: $text-secondary;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.ant-card) {
|
||||
background: $bg-card;
|
||||
border-color: $border-primary;
|
||||
|
||||
.ant-card-head {
|
||||
border-bottom-color: $border-primary;
|
||||
color: $text-primary;
|
||||
}
|
||||
|
||||
.ant-card-body {
|
||||
color: $text-primary;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.ant-descriptions) {
|
||||
.ant-descriptions-item-label {
|
||||
background: $bg-tertiary;
|
||||
color: $text-secondary;
|
||||
}
|
||||
|
||||
.ant-descriptions-item-content {
|
||||
background: $bg-card;
|
||||
color: $text-primary;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.ant-timeline-item-content) {
|
||||
color: $text-primary;
|
||||
}
|
||||
|
||||
:deep(.ant-empty-description) {
|
||||
color: $text-tertiary;
|
||||
}
|
||||
|
||||
:deep(.ant-input-search) {
|
||||
.ant-input {
|
||||
background: $bg-card;
|
||||
border-color: $border-primary;
|
||||
.search-input {
|
||||
flex: 1;
|
||||
background: transparent;
|
||||
border: none;
|
||||
outline: none;
|
||||
padding: $space-md $space-sm;
|
||||
font-size: $font-size-base;
|
||||
color: $text-primary;
|
||||
|
||||
&::placeholder {
|
||||
@ -470,9 +302,445 @@ defineExpose({
|
||||
}
|
||||
}
|
||||
|
||||
.ant-input-group-addon {
|
||||
.search-btn {
|
||||
flex-shrink: 0;
|
||||
background: $brand-primary;
|
||||
color: $bg-primary;
|
||||
border: none;
|
||||
border-radius: $radius-md;
|
||||
padding: $space-sm $space-lg;
|
||||
font-size: $font-size-base;
|
||||
font-weight: $font-weight-medium;
|
||||
cursor: pointer;
|
||||
transition: all $transition-base $ease-out;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-width: 72px;
|
||||
height: 36px;
|
||||
|
||||
&:hover:not(:disabled) {
|
||||
background: $brand-hover;
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
opacity: 0.7;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.search-tip {
|
||||
margin-top: $space-sm;
|
||||
font-size: $font-size-sm;
|
||||
color: $text-tertiary;
|
||||
}
|
||||
|
||||
// 加载动画
|
||||
.loading-spinner {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border: 2px solid transparent;
|
||||
border-top-color: currentColor;
|
||||
border-radius: 50%;
|
||||
animation: spin 0.8s linear infinite;
|
||||
|
||||
&.large {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-width: 3px;
|
||||
border-top-color: $brand-primary;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
// 结果容器
|
||||
.result-container {
|
||||
max-height: 55vh;
|
||||
overflow-y: auto;
|
||||
padding-right: $space-xs;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
border-color: $border-primary;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: $border-primary;
|
||||
border-radius: 3px;
|
||||
|
||||
&:hover {
|
||||
background: $text-tertiary;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 通用区块样式
|
||||
.section-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: $space-md;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: $font-size-base;
|
||||
font-weight: $font-weight-semibold;
|
||||
color: $text-primary;
|
||||
}
|
||||
|
||||
// 标签样式
|
||||
.tag {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 2px 8px;
|
||||
font-size: $font-size-xs;
|
||||
font-weight: $font-weight-medium;
|
||||
border-radius: $radius-small;
|
||||
margin-left: $space-xs;
|
||||
|
||||
&.tag-default {
|
||||
background: rgba($text-secondary, 0.15);
|
||||
color: $text-secondary;
|
||||
}
|
||||
|
||||
&.tag-warning {
|
||||
background: rgba($color-warning, 0.15);
|
||||
color: $color-warning;
|
||||
}
|
||||
|
||||
&.tag-info {
|
||||
background: rgba($brand-primary, 0.15);
|
||||
color: $brand-primary;
|
||||
}
|
||||
|
||||
&.tag-current {
|
||||
background: $color-warning;
|
||||
color: $bg-primary;
|
||||
}
|
||||
}
|
||||
|
||||
.task-tags {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
// 任务信息区域
|
||||
.info-section {
|
||||
background: $bg-tertiary;
|
||||
border-radius: $radius-lg;
|
||||
padding: $space-md;
|
||||
margin-bottom: $space-md;
|
||||
}
|
||||
|
||||
.info-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: $space-md $space-xl;
|
||||
}
|
||||
|
||||
.info-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: $space-xs;
|
||||
}
|
||||
|
||||
.info-label {
|
||||
font-size: $font-size-sm;
|
||||
color: $text-tertiary;
|
||||
}
|
||||
|
||||
.info-value {
|
||||
font-size: $font-size-base;
|
||||
color: $text-primary;
|
||||
|
||||
&.task-no {
|
||||
font-family: $font-family-mono;
|
||||
font-size: $font-size-sm;
|
||||
}
|
||||
|
||||
&.score {
|
||||
color: $brand-primary;
|
||||
font-weight: $font-weight-semibold;
|
||||
}
|
||||
}
|
||||
|
||||
// 当前节点区域
|
||||
.current-node-section {
|
||||
background: linear-gradient(135deg, rgba($brand-primary, 0.9) 0%, rgba(#764ba2, 0.9) 100%);
|
||||
border-radius: $radius-lg;
|
||||
padding: $space-lg;
|
||||
margin-bottom: $space-md;
|
||||
}
|
||||
|
||||
.current-node-content {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: $space-md;
|
||||
|
||||
.node-icon {
|
||||
font-size: 32px;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.node-info {
|
||||
flex: 1;
|
||||
color: $text-white;
|
||||
}
|
||||
|
||||
.node-status {
|
||||
font-size: $font-size-lg;
|
||||
font-weight: $font-weight-semibold;
|
||||
margin-bottom: $space-sm;
|
||||
}
|
||||
|
||||
.node-waiting {
|
||||
font-size: $font-size-base;
|
||||
margin-bottom: $space-xs;
|
||||
opacity: 0.95;
|
||||
|
||||
strong {
|
||||
font-weight: $font-weight-semibold;
|
||||
}
|
||||
}
|
||||
|
||||
.node-desc {
|
||||
font-size: $font-size-sm;
|
||||
opacity: 0.85;
|
||||
}
|
||||
}
|
||||
|
||||
// 时间线区域
|
||||
.timeline-section {
|
||||
background: $bg-tertiary;
|
||||
border-radius: $radius-lg;
|
||||
padding: $space-md;
|
||||
margin-bottom: $space-md;
|
||||
}
|
||||
|
||||
.timeline {
|
||||
position: relative;
|
||||
padding-left: $space-md;
|
||||
}
|
||||
|
||||
.timeline-item {
|
||||
position: relative;
|
||||
padding-left: $space-lg;
|
||||
padding-bottom: $space-md;
|
||||
|
||||
&:last-child {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.timeline-dot {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 4px;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 50%;
|
||||
background: $text-tertiary;
|
||||
border: 2px solid $bg-tertiary;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.timeline-line {
|
||||
position: absolute;
|
||||
left: 4px;
|
||||
top: 14px;
|
||||
width: 2px;
|
||||
height: calc(100% - 10px);
|
||||
background: $border-primary;
|
||||
}
|
||||
|
||||
&.status-completed {
|
||||
.timeline-dot {
|
||||
background: $brand-primary;
|
||||
}
|
||||
}
|
||||
|
||||
&.status-pending {
|
||||
.timeline-dot {
|
||||
background: $color-warning;
|
||||
box-shadow: 0 0 0 4px rgba($color-warning, 0.2);
|
||||
}
|
||||
}
|
||||
|
||||
&.status-waiting {
|
||||
.timeline-dot {
|
||||
background: $text-disabled;
|
||||
}
|
||||
|
||||
.timeline-content {
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.timeline-content {
|
||||
.timeline-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: $space-sm;
|
||||
margin-bottom: $space-xs;
|
||||
}
|
||||
|
||||
.timeline-title {
|
||||
font-size: $font-size-base;
|
||||
font-weight: $font-weight-medium;
|
||||
color: $text-primary;
|
||||
}
|
||||
|
||||
.timeline-time {
|
||||
font-size: $font-size-sm;
|
||||
color: $text-tertiary;
|
||||
margin-bottom: $space-xs;
|
||||
}
|
||||
|
||||
.timeline-operator {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: $space-xs;
|
||||
font-size: $font-size-sm;
|
||||
color: $brand-primary;
|
||||
margin-top: $space-xs;
|
||||
|
||||
.operator-dept {
|
||||
color: $text-secondary;
|
||||
}
|
||||
}
|
||||
|
||||
.timeline-comment {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: $space-xs;
|
||||
font-size: $font-size-sm;
|
||||
color: $color-success;
|
||||
margin-top: $space-xs;
|
||||
}
|
||||
}
|
||||
|
||||
// 相关人员区域
|
||||
.users-section {
|
||||
background: $bg-tertiary;
|
||||
border-radius: $radius-lg;
|
||||
padding: $space-md;
|
||||
}
|
||||
|
||||
.users-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: $space-sm;
|
||||
|
||||
@media (max-width: 600px) {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
.user-card {
|
||||
background: $bg-card;
|
||||
border-radius: $radius-md;
|
||||
padding: $space-md;
|
||||
text-align: center;
|
||||
|
||||
.user-role {
|
||||
font-size: $font-size-xs;
|
||||
color: $text-tertiary;
|
||||
margin-bottom: $space-xs;
|
||||
}
|
||||
|
||||
.user-name {
|
||||
font-size: $font-size-base;
|
||||
font-weight: $font-weight-semibold;
|
||||
color: $text-primary;
|
||||
margin-bottom: $space-xxs;
|
||||
}
|
||||
|
||||
.user-dept {
|
||||
font-size: $font-size-xs;
|
||||
color: $text-secondary;
|
||||
}
|
||||
}
|
||||
|
||||
// 空状态和初始状态
|
||||
.empty-state,
|
||||
.initial-state,
|
||||
.loading-state {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: $space-xxl 0;
|
||||
color: $text-tertiary;
|
||||
|
||||
.empty-icon,
|
||||
.initial-icon {
|
||||
font-size: 48px;
|
||||
margin-bottom: $space-md;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: $font-size-base;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.loading-state {
|
||||
p {
|
||||
margin-top: $space-md;
|
||||
}
|
||||
}
|
||||
|
||||
// Modal 样式覆盖
|
||||
:deep(.ant-modal) {
|
||||
.ant-modal-content {
|
||||
background: $bg-secondary;
|
||||
border: 1px solid $border-primary;
|
||||
border-radius: $radius-xl;
|
||||
box-shadow: $shadow-strong;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.ant-modal-header {
|
||||
background: transparent;
|
||||
border-bottom: 1px solid $border-primary;
|
||||
padding: $space-lg $space-xl;
|
||||
margin: 0;
|
||||
|
||||
.ant-modal-title {
|
||||
font-size: $font-size-lg;
|
||||
font-weight: $font-weight-semibold;
|
||||
color: $text-primary;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-modal-body {
|
||||
padding: $space-xl;
|
||||
}
|
||||
|
||||
.ant-modal-close {
|
||||
top: $space-lg;
|
||||
right: $space-lg;
|
||||
|
||||
.ant-modal-close-x {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
font-size: 16px;
|
||||
color: $text-secondary;
|
||||
transition: color $transition-base $ease-out;
|
||||
|
||||
&:hover {
|
||||
color: $text-primary;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user