|
@@ -120,6 +120,13 @@
|
|
|
@click="wxOpen(scope.row.id)"
|
|
@click="wxOpen(scope.row.id)"
|
|
|
v-hasPermi="['system:companyVoiceRobotic:list']"
|
|
v-hasPermi="['system:companyVoiceRobotic:list']"
|
|
|
>加微管理</el-button>
|
|
>加微管理</el-button>
|
|
|
|
|
+ <el-button
|
|
|
|
|
+ size="mini"
|
|
|
|
|
+ type="text"
|
|
|
|
|
+ icon="el-icon-document"
|
|
|
|
|
+ @click="showExecLogs(scope.row)"
|
|
|
|
|
+ v-hasPermi="['system:companyVoiceRobotic:list']"
|
|
|
|
|
+ >执行日志</el-button>
|
|
|
<el-button
|
|
<el-button
|
|
|
size="mini"
|
|
size="mini"
|
|
|
type="text"
|
|
type="text"
|
|
@@ -317,6 +324,147 @@
|
|
|
@pagination="getWxList"
|
|
@pagination="getWxList"
|
|
|
/>
|
|
/>
|
|
|
</el-drawer>
|
|
</el-drawer>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 执行日志对话框 -->
|
|
|
|
|
+ <el-drawer
|
|
|
|
|
+ title="任务执行日志"
|
|
|
|
|
+ size="75%"
|
|
|
|
|
+ :visible.sync="execLogs.show"
|
|
|
|
|
+ append-to-body
|
|
|
|
|
+ class="exec-logs-drawer">
|
|
|
|
|
+ <div class="exec-logs-container" v-loading="execLogs.loading">
|
|
|
|
|
+ <div v-if="execLogs.list.length === 0" class="empty-logs">
|
|
|
|
|
+ <i class="el-icon-document"></i>
|
|
|
|
|
+ <p>暂无执行记录</p>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <div v-else>
|
|
|
|
|
+ <!-- 统计信息卡片 -->
|
|
|
|
|
+ <el-card class="stats-card" shadow="never">
|
|
|
|
|
+ <div class="stats-grid">
|
|
|
|
|
+ <div class="stat-item">
|
|
|
|
|
+ <i class="el-icon-user" style="color: #1890ff;"></i>
|
|
|
|
|
+ <div class="stat-content">
|
|
|
|
|
+ <div class="stat-label">客户总数</div>
|
|
|
|
|
+ <div class="stat-value">{{ execLogs.list.length }}</div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="stat-item">
|
|
|
|
|
+ <i class="el-icon-phone" style="color: #52c41a;"></i>
|
|
|
|
|
+ <div class="stat-content">
|
|
|
|
|
+ <div class="stat-label">已打电话</div>
|
|
|
|
|
+ <div class="stat-value">{{ execLogs.stats.callDone }}</div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="stat-item">
|
|
|
|
|
+ <i class="el-icon-chat-line-square" style="color: #faad14;"></i>
|
|
|
|
|
+ <div class="stat-content">
|
|
|
|
|
+ <div class="stat-label">已加微信</div>
|
|
|
|
|
+ <div class="stat-value">{{ execLogs.stats.addWxDone }}</div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="stat-item">
|
|
|
|
|
+ <i class="el-icon-message" style="color: #722ed1;"></i>
|
|
|
|
|
+ <div class="stat-content">
|
|
|
|
|
+ <div class="stat-label">已发短信</div>
|
|
|
|
|
+ <div class="stat-value">{{ execLogs.stats.sendMsgDone }}</div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </el-card>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 执行记录列表 -->
|
|
|
|
|
+ <div class="logs-list">
|
|
|
|
|
+ <el-collapse v-model="execLogs.activeNames" accordion>
|
|
|
|
|
+ <el-collapse-item
|
|
|
|
|
+ v-for="(record, index) in execLogs.list"
|
|
|
|
|
+ :key="index"
|
|
|
|
|
+ :name="index">
|
|
|
|
|
+ <template slot="title">
|
|
|
|
|
+ <div class="record-header">
|
|
|
|
|
+ <div class="record-left">
|
|
|
|
|
+ <el-avatar :size="48" icon="el-icon-user-solid" :style="{background: getAvatarColor(index)}"></el-avatar>
|
|
|
|
|
+ <div class="customer-detail">
|
|
|
|
|
+ <div class="customer-name-row">
|
|
|
|
|
+ <span class="customer-name">{{ record.customerName }}</span>
|
|
|
|
|
+ <el-tag
|
|
|
|
|
+ :type="getWorkflowStatusType(record.workflowStatus)"
|
|
|
|
|
+ size="small"
|
|
|
|
|
+ class="status-tag">
|
|
|
|
|
+ {{ record.workflowStatusName }}
|
|
|
|
|
+ </el-tag>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="customer-meta">
|
|
|
|
|
+ <span class="meta-item phone">
|
|
|
|
|
+ <i class="el-icon-phone-outline"></i>
|
|
|
|
|
+ {{ record.customerPhone }}
|
|
|
|
|
+ </span>
|
|
|
|
|
+ <span class="meta-divider">|</span>
|
|
|
|
|
+ <span class="meta-item node">
|
|
|
|
|
+ <i class="el-icon-s-operation"></i>
|
|
|
|
|
+ 当前节点:<strong>{{ record.currentNodeTypeName }}</strong>
|
|
|
|
|
+ </span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="record-right">
|
|
|
|
|
+ <div class="progress-info">
|
|
|
|
|
+ <span class="progress-label">执行进度</span>
|
|
|
|
|
+ <span class="progress-value">{{ getProgress(record) }}%</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <el-progress
|
|
|
|
|
+ :percentage="getProgress(record)"
|
|
|
|
|
+ :color="getProgressColor(record)"
|
|
|
|
|
+ :stroke-width="10"
|
|
|
|
|
+ :show-text="false"></el-progress>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </template>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 节点执行日志 -->
|
|
|
|
|
+ <div class="node-logs">
|
|
|
|
|
+ <el-timeline>
|
|
|
|
|
+ <el-timeline-item
|
|
|
|
|
+ v-for="(log, logIndex) in record.nodeLogs"
|
|
|
|
|
+ :key="logIndex"
|
|
|
|
|
+ :timestamp="log.startTime"
|
|
|
|
|
+ :color="getNodeStatusColor(log.statusName)"
|
|
|
|
|
+ placement="top">
|
|
|
|
|
+ <el-card class="node-log-card">
|
|
|
|
|
+ <div class="node-log-header">
|
|
|
|
|
+ <div class="node-info">
|
|
|
|
|
+ <i :class="getNodeIcon(log.nodeKey)" class="node-icon"></i>
|
|
|
|
|
+ <span class="node-name">{{ log.nodeName }}</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <el-tag
|
|
|
|
|
+ :type="getStatusTagType(log.statusName)"
|
|
|
|
|
+ size="mini">
|
|
|
|
|
+ {{ log.statusName }}
|
|
|
|
|
+ </el-tag>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="node-log-body" v-if="log.errorMsg">
|
|
|
|
|
+ <div class="error-msg">
|
|
|
|
|
+ <i class="el-icon-warning"></i>
|
|
|
|
|
+ {{ log.errorMsg }}
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="node-log-footer">
|
|
|
|
|
+ <span class="duration" v-if="log.duration">
|
|
|
|
|
+ <i class="el-icon-time"></i>
|
|
|
|
|
+ 耗时: {{ formatDuration(log.duration) }}
|
|
|
|
|
+ </span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </el-card>
|
|
|
|
|
+ </el-timeline-item>
|
|
|
|
|
+ </el-timeline>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </el-collapse-item>
|
|
|
|
|
+ </el-collapse>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </el-drawer>
|
|
|
|
|
+
|
|
|
<el-drawer size="75%" title="客户详情" :visible.sync="customerDetailShow" append-to-body>
|
|
<el-drawer size="75%" title="客户详情" :visible.sync="customerDetailShow" append-to-body>
|
|
|
<customer-details ref="customerDetails" />
|
|
<customer-details ref="customerDetails" />
|
|
|
</el-drawer>
|
|
</el-drawer>
|
|
@@ -340,7 +488,8 @@ import {
|
|
|
taskRun,
|
|
taskRun,
|
|
|
getTypes,
|
|
getTypes,
|
|
|
getSmsTempList,
|
|
getSmsTempList,
|
|
|
- getCIDGroupList
|
|
|
|
|
|
|
+ getCIDGroupList,
|
|
|
|
|
+ getExecRecords
|
|
|
} from "@/api/company/companyVoiceRobotic";
|
|
} from "@/api/company/companyVoiceRobotic";
|
|
|
import draggable from 'vuedraggable'
|
|
import draggable from 'vuedraggable'
|
|
|
import { listAll } from '@/api/company/wxDialog';
|
|
import { listAll } from '@/api/company/wxDialog';
|
|
@@ -447,6 +596,17 @@ export default {
|
|
|
pageSize: 10,
|
|
pageSize: 10,
|
|
|
},
|
|
},
|
|
|
},
|
|
},
|
|
|
|
|
+ execLogs: {
|
|
|
|
|
+ show: false,
|
|
|
|
|
+ loading: false,
|
|
|
|
|
+ list: [],
|
|
|
|
|
+ activeNames: [],
|
|
|
|
|
+ stats: {
|
|
|
|
|
+ callDone: 0,
|
|
|
|
|
+ addWxDone: 0,
|
|
|
|
|
+ sendMsgDone: 0
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
// 表单校验
|
|
// 表单校验
|
|
|
rules: {
|
|
rules: {
|
|
|
name: [
|
|
name: [
|
|
@@ -732,6 +892,99 @@ export default {
|
|
|
},
|
|
},
|
|
|
removeQwUser(index){
|
|
removeQwUser(index){
|
|
|
this.form.qwUser.splice(index, 1)
|
|
this.form.qwUser.splice(index, 1)
|
|
|
|
|
+ },
|
|
|
|
|
+ // 打开执行日志
|
|
|
|
|
+ async showExecLogs(row) {
|
|
|
|
|
+ this.execLogs.show = true;
|
|
|
|
|
+ this.execLogs.loading = true;
|
|
|
|
|
+ this.execLogs.list = [];
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ const res = await getExecRecords(row.id);
|
|
|
|
|
+ this.execLogs.list = res.data || [];
|
|
|
|
|
+
|
|
|
|
|
+ // 计算统计数据
|
|
|
|
|
+ this.execLogs.stats = {
|
|
|
|
|
+ callDone: this.execLogs.list.reduce((sum, r) => sum + (r.callPhoneDone || 0), 0),
|
|
|
|
|
+ addWxDone: this.execLogs.list.reduce((sum, r) => sum + (r.addWxDone || 0), 0),
|
|
|
|
|
+ sendMsgDone: this.execLogs.list.reduce((sum, r) => sum + (r.sendMsgDone || 0), 0)
|
|
|
|
|
+ };
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ console.error('获取执行日志失败:', error);
|
|
|
|
|
+ this.$message.error('获取执行日志失败');
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ this.execLogs.loading = false;
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ // 获取工作流状态类型
|
|
|
|
|
+ getWorkflowStatusType(status) {
|
|
|
|
|
+ const typeMap = {
|
|
|
|
|
+ 1: '',
|
|
|
|
|
+ 2: 'success',
|
|
|
|
|
+ 3: 'warning',
|
|
|
|
|
+ 4: 'danger'
|
|
|
|
|
+ };
|
|
|
|
|
+ return typeMap[status] || 'info';
|
|
|
|
|
+ },
|
|
|
|
|
+ // 获取进度
|
|
|
|
|
+ getProgress(record) {
|
|
|
|
|
+ if (!record.nodeLogs || record.nodeLogs.length === 0) return 0;
|
|
|
|
|
+ const total = record.nodeLogs.length;
|
|
|
|
|
+ const completed = record.nodeLogs.filter(log => log.statusName === '执行成功').length;
|
|
|
|
|
+ return Math.round((completed / total) * 100);
|
|
|
|
|
+ },
|
|
|
|
|
+ // 获取进度条颜色
|
|
|
|
|
+ getProgressColor(record) {
|
|
|
|
|
+ const progress = this.getProgress(record);
|
|
|
|
|
+ if (progress === 100) return '#52c41a';
|
|
|
|
|
+ if (progress >= 50) return '#1890ff';
|
|
|
|
|
+ return '#faad14';
|
|
|
|
|
+ },
|
|
|
|
|
+ // 获取节点状态颜色
|
|
|
|
|
+ getNodeStatusColor(statusName) {
|
|
|
|
|
+ const colorMap = {
|
|
|
|
|
+ '执行成功': '#52c41a',
|
|
|
|
|
+ '执行中': '#1890ff',
|
|
|
|
|
+ '执行失败': '#f5222d',
|
|
|
|
|
+ '等待执行': '#d9d9d9'
|
|
|
|
|
+ };
|
|
|
|
|
+ return colorMap[statusName] || '#d9d9d9';
|
|
|
|
|
+ },
|
|
|
|
|
+ // 获取状态标签类型
|
|
|
|
|
+ getStatusTagType(statusName) {
|
|
|
|
|
+ const typeMap = {
|
|
|
|
|
+ '执行成功': 'success',
|
|
|
|
|
+ '执行中': 'warning',
|
|
|
|
|
+ '执行失败': 'danger',
|
|
|
|
|
+ '等待执行': 'info'
|
|
|
|
|
+ };
|
|
|
|
|
+ return typeMap[statusName] || 'info';
|
|
|
|
|
+ },
|
|
|
|
|
+ // 获取节点图标
|
|
|
|
|
+ getNodeIcon(nodeKey) {
|
|
|
|
|
+ const iconMap = {
|
|
|
|
|
+ 'node_start': 'el-icon-video-play',
|
|
|
|
|
+ 'node_addwx': 'el-icon-chat-line-square',
|
|
|
|
|
+ 'node_call': 'el-icon-phone',
|
|
|
|
|
+ 'node_sms': 'el-icon-message',
|
|
|
|
|
+ 'node_end': 'el-icon-circle-check'
|
|
|
|
|
+ };
|
|
|
|
|
+ return iconMap[nodeKey] || 'el-icon-document';
|
|
|
|
|
+ },
|
|
|
|
|
+ // 格式化时长
|
|
|
|
|
+ formatDuration(ms) {
|
|
|
|
|
+ if (!ms) return '-';
|
|
|
|
|
+ if (ms < 1000) return ms + 'ms';
|
|
|
|
|
+ const seconds = Math.floor(ms / 1000);
|
|
|
|
|
+ if (seconds < 60) return seconds + 's';
|
|
|
|
|
+ const minutes = Math.floor(seconds / 60);
|
|
|
|
|
+ const remainSeconds = seconds % 60;
|
|
|
|
|
+ return minutes + 'm' + remainSeconds + 's';
|
|
|
|
|
+ },
|
|
|
|
|
+ // 获取头像颜色
|
|
|
|
|
+ getAvatarColor(index) {
|
|
|
|
|
+ const colors = ['#1890ff', '#52c41a', '#faad14', '#722ed1', '#eb2f96'];
|
|
|
|
|
+ return colors[index % colors.length];
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
};
|
|
};
|
|
@@ -770,7 +1023,260 @@ export default {
|
|
|
position: absolute;
|
|
position: absolute;
|
|
|
}
|
|
}
|
|
|
.sortable-ghost{
|
|
.sortable-ghost{
|
|
|
- //background: #FFF !important;
|
|
|
|
|
|
|
+ /* background: #FFF !important; */
|
|
|
background: rgb(217, 236, 255) !important;
|
|
background: rgb(217, 236, 255) !important;
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+/* 执行日志样式 */
|
|
|
|
|
+.exec-logs-container {
|
|
|
|
|
+ padding: 20px;
|
|
|
|
|
+ background: #f5f7fa;
|
|
|
|
|
+ min-height: calc(100vh - 60px);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.exec-logs-container ::v-deep .el-card {
|
|
|
|
|
+ border: none;
|
|
|
|
|
+ box-shadow: none;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.empty-logs {
|
|
|
|
|
+ text-align: center;
|
|
|
|
|
+ padding: 80px 20px;
|
|
|
|
|
+ color: #909399;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.empty-logs i {
|
|
|
|
|
+ font-size: 64px;
|
|
|
|
|
+ margin-bottom: 16px;
|
|
|
|
|
+ color: #dcdfe6;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.empty-logs p {
|
|
|
|
|
+ font-size: 16px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* 统计卡片 */
|
|
|
|
|
+.stats-card {
|
|
|
|
|
+ margin-bottom: 20px;
|
|
|
|
|
+ border-radius: 8px;
|
|
|
|
|
+ border: none;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.stats-card ::v-deep .el-card__body {
|
|
|
|
|
+ padding: 20px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.stats-grid {
|
|
|
|
|
+ display: grid;
|
|
|
|
|
+ grid-template-columns: repeat(4, 1fr);
|
|
|
|
|
+ gap: 20px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.stat-item {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ gap: 12px;
|
|
|
|
|
+ padding: 12px;
|
|
|
|
|
+ background: #f9fafb;
|
|
|
|
|
+ border-radius: 6px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.stat-item i {
|
|
|
|
|
+ font-size: 32px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.stat-content {
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.stat-label {
|
|
|
|
|
+ font-size: 13px;
|
|
|
|
|
+ color: #909399;
|
|
|
|
|
+ margin-bottom: 4px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.stat-value {
|
|
|
|
|
+ font-size: 24px;
|
|
|
|
|
+ font-weight: 600;
|
|
|
|
|
+ color: #303133;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* 日志列表 */
|
|
|
|
|
+.logs-list {
|
|
|
|
|
+ background: #fff;
|
|
|
|
|
+ border-radius: 8px;
|
|
|
|
|
+ padding: 16px;
|
|
|
|
|
+ border: none;
|
|
|
|
|
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
|
|
|
|
+}
|
|
|
|
|
+.el-collapse-item__header{
|
|
|
|
|
+ height: auto !important;
|
|
|
|
|
+}
|
|
|
|
|
+/* 记录头部 */
|
|
|
|
|
+.record-header {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ justify-content: space-between;
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+ padding: 20px 16px;
|
|
|
|
|
+ gap: 32px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.record-left {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ gap: 16px;
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+ min-width: 0;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.customer-detail {
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+ min-width: 0;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ gap: 8px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.customer-name-row {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ gap: 12px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.customer-name {
|
|
|
|
|
+ font-size: 16px;
|
|
|
|
|
+ font-weight: 600;
|
|
|
|
|
+ color: #303133;
|
|
|
|
|
+ line-height: 1.4;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.status-tag {
|
|
|
|
|
+ flex-shrink: 0;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.customer-meta {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ gap: 12px;
|
|
|
|
|
+ font-size: 13px;
|
|
|
|
|
+ color: #606266;
|
|
|
|
|
+ line-height: 1.4;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.meta-item {
|
|
|
|
|
+ display: inline-flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ gap: 6px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.meta-item i {
|
|
|
|
|
+ font-size: 14px;
|
|
|
|
|
+ color: #909399;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.meta-item.node strong {
|
|
|
|
|
+ color: #1890ff;
|
|
|
|
|
+ font-weight: 600;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.meta-divider {
|
|
|
|
|
+ color: #dcdfe6;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.record-right {
|
|
|
|
|
+ width: 200px;
|
|
|
|
|
+ flex-shrink: 0;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.progress-info {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ justify-content: space-between;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ margin-bottom: 8px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.progress-label {
|
|
|
|
|
+ font-size: 13px;
|
|
|
|
|
+ color: #909399;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.progress-value {
|
|
|
|
|
+ font-size: 16px;
|
|
|
|
|
+ font-weight: 600;
|
|
|
|
|
+ color: #303133;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* 节点日志 */
|
|
|
|
|
+.node-logs {
|
|
|
|
|
+ padding: 20px;
|
|
|
|
|
+ background: #fafafa;
|
|
|
|
|
+ border-radius: 6px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.node-log-card {
|
|
|
|
|
+ margin-bottom: 0;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.node-log-header {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ justify-content: space-between;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ margin-bottom: 8px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.node-info {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ gap: 8px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.node-icon {
|
|
|
|
|
+ font-size: 18px;
|
|
|
|
|
+ color: #1890ff;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.node-name {
|
|
|
|
|
+ font-size: 14px;
|
|
|
|
|
+ font-weight: 600;
|
|
|
|
|
+ color: #303133;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.node-log-body {
|
|
|
|
|
+ margin: 12px 0;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.error-msg {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ gap: 8px;
|
|
|
|
|
+ padding: 8px 12px;
|
|
|
|
|
+ background: #fff2e8;
|
|
|
|
|
+ border-left: 3px solid #faad14;
|
|
|
|
|
+ border-radius: 4px;
|
|
|
|
|
+ font-size: 13px;
|
|
|
|
|
+ color: #d46b08;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.error-msg i {
|
|
|
|
|
+ font-size: 16px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.node-log-footer {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ gap: 16px;
|
|
|
|
|
+ font-size: 13px;
|
|
|
|
|
+ color: #909399;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.duration {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ gap: 4px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.duration i {
|
|
|
|
|
+ font-size: 14px;
|
|
|
|
|
+}
|
|
|
</style>
|
|
</style>
|