feat: 添加外部应用卡片和可复制优化
- 新增'📱 外部应用'卡片,显示飞书应用信息 * 应用名称和图标 * 应用 ID(等宽字体显示) * 一键复制按钮 * 未配置应用显示友好提示 - 优化工作区路径显示 * 代码样式(等宽字体 + 背景色) * 添加'📋 复制'按钮 * 复制成功有提示消息 - 后端添加龙虾应用配置 * 飞行侠:IT 项目推广运营平台 (cli_a92413cfb0791bce) * 道童:道德经新解 (cli_a9439b614f38dbd2) * 其他龙虾:未配置 - 样式优化 * 绿色渐变复制按钮 * 悬停动画效果 * 响应式布局 📱 让重要信息一键可复制
This commit is contained in:
@@ -10,12 +10,12 @@ import re
|
|||||||
|
|
||||||
# 龙虾配置
|
# 龙虾配置
|
||||||
LOBSTERS = [
|
LOBSTERS = [
|
||||||
{'id': 1, 'name': '飞行侠', 'emoji': '🦸', 'port': 18789, 'specialty': '主力/通用', 'container': 'openclaw-instance2'},
|
{'id': 1, 'name': '飞行侠', 'emoji': '🦸', 'port': 18789, 'specialty': '主力/通用', 'container': 'openclaw-instance2', 'app_name': 'IT 项目推广运营平台', 'app_id': 'cli_a92413cfb0791bce'},
|
||||||
{'id': 2, 'name': '道童', 'emoji': '☯️', 'port': 18889, 'specialty': '道德经注解', 'container': 'openclaw-gateway-2'},
|
{'id': 2, 'name': '道童', 'emoji': '☯️', 'port': 18889, 'specialty': '道德经注解', 'container': 'openclaw-gateway-2', 'app_name': '道德经新解', 'app_id': 'cli_a9439b614f38dbd2'},
|
||||||
{'id': 3, 'name': '墨子', 'emoji': '🔧', 'port': 18689, 'specialty': '代码专家', 'container': 'openclaw-coder'},
|
{'id': 3, 'name': '墨子', 'emoji': '🔧', 'port': 18689, 'specialty': '代码专家', 'container': 'openclaw-coder', 'app_name': '未配置', 'app_id': ''},
|
||||||
{'id': 4, 'name': '织网者', 'emoji': '🕸️', 'port': 18589, 'specialty': '网站制作', 'container': 'openclaw-web'},
|
{'id': 4, 'name': '织网者', 'emoji': '🕸️', 'port': 18589, 'specialty': '网站制作', 'container': 'openclaw-web', 'app_name': '未配置', 'app_id': ''},
|
||||||
{'id': 5, 'name': '费曼', 'emoji': '⚛️', 'port': 18989, 'specialty': '物理研究', 'container': 'openclaw-physics'},
|
{'id': 5, 'name': '费曼', 'emoji': '⚛️', 'port': 18989, 'specialty': '物理研究', 'container': 'openclaw-physics', 'app_name': '未配置', 'app_id': ''},
|
||||||
{'id': 6, 'name': '守望者', 'emoji': '👁️', 'port': 18080, 'specialty': '舰队监控', 'container': 'openclaw-watcher'},
|
{'id': 6, 'name': '守望者', 'emoji': '👁️', 'port': 18080, 'specialty': '舰队监控', 'container': 'openclaw-watcher', 'app_name': '未配置', 'app_id': ''},
|
||||||
]
|
]
|
||||||
|
|
||||||
@api_view(['GET'])
|
@api_view(['GET'])
|
||||||
|
|||||||
@@ -103,9 +103,21 @@ function LobsterDetail() {
|
|||||||
<span className="info-value code">{lobster.container}</span>
|
<span className="info-value code">{lobster.container}</span>
|
||||||
</div>
|
</div>
|
||||||
{lobster.workspace && (
|
{lobster.workspace && (
|
||||||
<div className="info-row">
|
<div className="info-row workspace-row">
|
||||||
<span className="info-label">工作区</span>
|
<span className="info-label">工作区</span>
|
||||||
<span className="info-value code">{lobster.workspace}</span>
|
<div className="workspace-value">
|
||||||
|
<code>{lobster.workspace}</code>
|
||||||
|
<button
|
||||||
|
className="copy-btn"
|
||||||
|
onClick={() => {
|
||||||
|
navigator.clipboard.writeText(lobster.workspace);
|
||||||
|
alert('工作区路径已复制到剪贴板!📋');
|
||||||
|
}}
|
||||||
|
title="复制路径"
|
||||||
|
>
|
||||||
|
📋 复制
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="info-row">
|
<div className="info-row">
|
||||||
@@ -150,6 +162,41 @@ function LobsterDetail() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 状态历史卡片 */}
|
{/* 状态历史卡片 */}
|
||||||
|
<div className="info-card">
|
||||||
|
<div className="card-header">
|
||||||
|
<h2>📱 外部应用</h2>
|
||||||
|
</div>
|
||||||
|
<div className="card-body">
|
||||||
|
{lobster.app_name && lobster.app_name !== '未配置' ? (
|
||||||
|
<div className="app-info">
|
||||||
|
<div className="app-icon">🪵</div>
|
||||||
|
<div className="app-details">
|
||||||
|
<div className="app-name">{lobster.app_name}</div>
|
||||||
|
<div className="app-id">
|
||||||
|
<code>{lobster.app_id}</code>
|
||||||
|
<button
|
||||||
|
className="copy-btn small"
|
||||||
|
onClick={() => {
|
||||||
|
navigator.clipboard.writeText(lobster.app_id);
|
||||||
|
alert('应用 ID 已复制到剪贴板!📋');
|
||||||
|
}}
|
||||||
|
title="复制应用 ID"
|
||||||
|
>
|
||||||
|
📋 复制
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="app-empty">
|
||||||
|
<p>😕 暂无外部应用</p>
|
||||||
|
<p className="app-hint">这只龙虾还没有关联外部应用哦~</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 运行统计卡片 */}
|
||||||
<div className="info-card">
|
<div className="info-card">
|
||||||
<div className="card-header">
|
<div className="card-header">
|
||||||
<h2>📊 运行统计</h2>
|
<h2>📊 运行统计</h2>
|
||||||
@@ -282,6 +329,54 @@ function LobsterDetail() {
|
|||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.workspace-row {
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-value {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
flex: 1;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-value code {
|
||||||
|
font-family: 'Courier New', monospace;
|
||||||
|
font-size: 0.85em;
|
||||||
|
background: #f7fafc;
|
||||||
|
padding: 6px 10px;
|
||||||
|
border-radius: 4px;
|
||||||
|
color: #2d3748;
|
||||||
|
word-break: break-all;
|
||||||
|
max-width: 400px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.copy-btn {
|
||||||
|
background: linear-gradient(135deg, #48bb78 0%, #38a169 100%);
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
padding: 6px 12px;
|
||||||
|
border-radius: 6px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 0.85em;
|
||||||
|
font-weight: 600;
|
||||||
|
white-space: nowrap;
|
||||||
|
transition: all 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.copy-btn:hover {
|
||||||
|
opacity: 0.9;
|
||||||
|
transform: translateY(-1px);
|
||||||
|
box-shadow: 0 4px 8px rgba(72, 187, 120, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.copy-btn:active {
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
|
||||||
.status-badge {
|
.status-badge {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -362,6 +457,67 @@ function LobsterDetail() {
|
|||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.app-info {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 15px;
|
||||||
|
padding: 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-icon {
|
||||||
|
font-size: 2.5em;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-details {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-name {
|
||||||
|
font-size: 1.1em;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #2d3748;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-id {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-id code {
|
||||||
|
font-family: 'Courier New', monospace;
|
||||||
|
font-size: 0.85em;
|
||||||
|
background: #f7fafc;
|
||||||
|
padding: 6px 10px;
|
||||||
|
border-radius: 4px;
|
||||||
|
color: #4a5568;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
|
||||||
|
.copy-btn.small {
|
||||||
|
padding: 4px 10px;
|
||||||
|
font-size: 0.8em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-empty {
|
||||||
|
text-align: center;
|
||||||
|
padding: 30px 20px;
|
||||||
|
color: #a0aec0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-empty p {
|
||||||
|
margin: 8px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-hint {
|
||||||
|
font-size: 0.9em;
|
||||||
|
color: #cbd5e0;
|
||||||
|
}
|
||||||
|
|
||||||
.detail-loading, .detail-error {
|
.detail-loading, .detail-error {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|||||||
Reference in New Issue
Block a user