mirror of
https://github.com/qingchencloud/clawpanel.git
synced 2026-05-30 04:40:18 +08:00
feat: Docker 集群增强 — Gateway 通讯API、像素兵种系统、互动组件、UI 优化
This commit is contained in:
@@ -48,6 +48,22 @@ const PATHS = {
|
||||
'globe': '<circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 014 10 15.3 15.3 0 01-4 10 15.3 15.3 0 01-4-10 15.3 15.3 0 014-10z"/>',
|
||||
'shield': '<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/>',
|
||||
'list': '<line x1="8" y1="6" x2="21" y2="6"/><line x1="8" y1="12" x2="21" y2="12"/><line x1="8" y1="18" x2="21" y2="18"/><line x1="3" y1="6" x2="3.01" y2="6"/><line x1="3" y1="12" x2="3.01" y2="12"/><line x1="3" y1="18" x2="3.01" y2="18"/>',
|
||||
|
||||
// 军事主题图标
|
||||
'swords': '<path d="M14.5 17.5L3 6V3h3l11.5 11.5"/><path d="M13 19l6-6"/><path d="M16 16l4 4"/><path d="M14.5 6.5L18 3l3 3-3.5 3.5"/><path d="M20 4L8.5 15.5"/>',
|
||||
'castle': '<path d="M3 21h18M5 21V10l2-2V4h2v4h2V4h2v4h2V4h2v4l2 2v11"/><path d="M10 21v-4h4v4"/>',
|
||||
'tent': '<path d="M19 20L12 4 5 20"/><path d="M3 20h18"/><path d="M12 4v16"/>',
|
||||
'scroll': '<path d="M8 21h12a2 2 0 002-2v-2H10v2a2 2 0 11-4 0V5a2 2 0 012-2h12v16"/>',
|
||||
'play': '<polygon points="5 3 19 12 5 21 5 3"/>',
|
||||
'stop': '<rect x="6" y="6" width="12" height="12" rx="1"/>',
|
||||
'refresh-cw': '<polyline points="23 4 23 10 17 10"/><polyline points="1 20 1 14 7 14"/><path d="M3.51 9a9 9 0 0114.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0020.49 15"/>',
|
||||
'rocket': '<path d="M4.5 16.5c-1.5 1.26-2 5-2 5s3.74-.5 5-2c.71-.84.7-2.13-.09-2.91a2.18 2.18 0 00-2.91-.09z"/><path d="M12 15l-3-3a22 22 0 012-3.95A12.88 12.88 0 0122 2c0 2.72-.78 7.5-6 11a22.35 22.35 0 01-4 2z"/><path d="M9 12H4s.55-3.03 2-4c1.62-1.08 5 0 5 0M12 15v5s3.03-.55 4-2c1.08-1.62 0-5 0-5"/>',
|
||||
'eye': '<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/>',
|
||||
'pen-tool': '<path d="M12 19l7-7 3 3-7 7-3-3z"/><path d="M18 13l-1.5-7.5L2 2l3.5 14.5L13 18l5-5zM2 2l7.586 7.586"/><circle cx="11" cy="11" r="2"/>',
|
||||
'gear': '<circle cx="12" cy="12" r="3"/><path d="M12 1v2M12 21v2M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M1 12h2M21 12h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42"/>',
|
||||
'plus-circle': '<circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="16"/><line x1="8" y1="12" x2="16" y2="12"/>',
|
||||
'crown': '<path d="M2 20h20L19 9l-5 5-2-7-2 7-5-5-3 11z"/><path d="M2 20h20v2H2z"/>',
|
||||
'send': '<line x1="22" y1="2" x2="11" y2="13"/><polygon points="22 2 15 22 11 13 2 9 22 2"/>',
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
204
src/lib/pixel-roles.js
Normal file
204
src/lib/pixel-roles.js
Normal file
@@ -0,0 +1,204 @@
|
||||
// 像素风格龙虾兵角色图
|
||||
// 每个角色是一个 16x16 像素画,用 SVG rects 渲染
|
||||
// 调色板:0=制服主色 1=制服亮色 2=制服暗色 3=虾红(头/钳) 4=深色(眼) 5=白 6=装备色 .=透明
|
||||
// 制服用兵种色 → 每个角色外观差异明显
|
||||
|
||||
const PIXEL_CHARS = {
|
||||
// 步兵 — 灰蓝制服 + 钢盔
|
||||
general: {
|
||||
palette: ['#64748b', '#94a3b8', '#475569', '#e74c3c', '#1e293b', '#ffffff', '#cbd5e1'],
|
||||
pixels: [
|
||||
'................',
|
||||
'......0022......',
|
||||
'.....006622.....',
|
||||
'....00000000....',
|
||||
'..3..044440..3..',
|
||||
'..3..055550..3..',
|
||||
'.33..044440..33.',
|
||||
'.....000000.....',
|
||||
'....00011000....',
|
||||
'....00000000....',
|
||||
'.....000000.....',
|
||||
'.....000000.....',
|
||||
'......0000......',
|
||||
'.....00..00.....',
|
||||
'.....00..00.....',
|
||||
'.....22..22.....',
|
||||
],
|
||||
},
|
||||
// 突击兵(coder) — 金黄制服 + 护目镜 + 闪电
|
||||
coder: {
|
||||
palette: ['#f59e0b', '#fbbf24', '#b45309', '#e74c3c', '#1e293b', '#ffffff', '#fef3c7'],
|
||||
pixels: [
|
||||
'.......66.......',
|
||||
'......6666......',
|
||||
'.....666666.....',
|
||||
'....00066000....',
|
||||
'..3..044440..3..',
|
||||
'..3..055550..3..',
|
||||
'.33..044440..33.',
|
||||
'.....000000.....',
|
||||
'....00011000....',
|
||||
'....00000000....',
|
||||
'.....000000.....',
|
||||
'.6...000000...6.',
|
||||
'.66...0000...66.',
|
||||
'.....00..00.....',
|
||||
'.....00..00.....',
|
||||
'.....22..22.....',
|
||||
],
|
||||
},
|
||||
// 翻译官 — 青色制服 + 贝雷帽 + 卷轴
|
||||
translator: {
|
||||
palette: ['#06b6d4', '#22d3ee', '#0e7490', '#e74c3c', '#1e293b', '#ffffff', '#ecfeff'],
|
||||
pixels: [
|
||||
'....1100........',
|
||||
'...011000.......',
|
||||
'...0000000......',
|
||||
'....000000......',
|
||||
'..3..044440..3..',
|
||||
'..3..055550..3..',
|
||||
'.33..044440..33.',
|
||||
'.....000000.....',
|
||||
'....00011000....',
|
||||
'....00000000..66',
|
||||
'.....000000..646',
|
||||
'.....000000..646',
|
||||
'......0000...66.',
|
||||
'.....00..00.....',
|
||||
'.....00..00.....',
|
||||
'.....22..22.....',
|
||||
],
|
||||
},
|
||||
// 文书官 — 紫色制服 + 文人帽 + 羽毛笔
|
||||
writer: {
|
||||
palette: ['#8b5cf6', '#a78bfa', '#6d28d9', '#e74c3c', '#1e293b', '#ffffff', '#ede9fe'],
|
||||
pixels: [
|
||||
'..............6.',
|
||||
'.....0011....6..',
|
||||
'....001100..6...',
|
||||
'....000000.6....',
|
||||
'..3..044440..3..',
|
||||
'..3..055550..3..',
|
||||
'.33..044440..33.',
|
||||
'.....000000.....',
|
||||
'....00011000....',
|
||||
'....00000000....',
|
||||
'.....000000.....',
|
||||
'.....000000.....',
|
||||
'......0000......',
|
||||
'.....00..00.....',
|
||||
'.....00..00.....',
|
||||
'.....22..22.....',
|
||||
],
|
||||
},
|
||||
// 参谋(analyst) — 绿色制服 + 眼镜 + 图表板
|
||||
analyst: {
|
||||
palette: ['#22c55e', '#4ade80', '#15803d', '#e74c3c', '#1e293b', '#ffffff', '#dcfce7'],
|
||||
pixels: [
|
||||
'................',
|
||||
'......4444......',
|
||||
'.....444444.....',
|
||||
'....44444444....',
|
||||
'..3.066.660.3...',
|
||||
'..3..055550..3..',
|
||||
'.33..044440..33.',
|
||||
'.....000000.....',
|
||||
'....00011000....',
|
||||
'....00000000.66.',
|
||||
'.....000000.626.',
|
||||
'.....000000.626.',
|
||||
'......0000..66..',
|
||||
'.....00..00.....',
|
||||
'.....00..00.....',
|
||||
'.....22..22.....',
|
||||
],
|
||||
},
|
||||
// 特种兵(custom) — 深红制服 + 战术面罩 + 扳手
|
||||
custom: {
|
||||
palette: ['#ef4444', '#f87171', '#b91c1c', '#dc2626', '#1e293b', '#ffffff', '#fee2e2'],
|
||||
pixels: [
|
||||
'......0022......',
|
||||
'.....020020.....',
|
||||
'....00000000....',
|
||||
'....04444400....',
|
||||
'..3..044440..3..',
|
||||
'..3..055550..3..',
|
||||
'.33..044440..33.',
|
||||
'.....000000.....',
|
||||
'....00011000....',
|
||||
'....00000000....',
|
||||
'.....000000.....',
|
||||
'.4...000000...4.',
|
||||
'.44...0000...44.',
|
||||
'.....00..00.....',
|
||||
'.....00..00.....',
|
||||
'.....22..22.....',
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
// 军营(Docker 节点)像素图
|
||||
const PIXEL_BARRACKS = {
|
||||
palette: ['#92400e', '#b45309', '#fbbf24', '#d97706', '#78350f', '#ffffff', '#f59e0b'],
|
||||
pixels: [
|
||||
'......6666......',
|
||||
'.....666666.....',
|
||||
'....33333333....',
|
||||
'...3333333333...',
|
||||
'..333333333333..',
|
||||
'..300053005300..',
|
||||
'..300053005300..',
|
||||
'..300053005300..',
|
||||
'..333333333333..',
|
||||
'..300053005300..',
|
||||
'..300053005300..',
|
||||
'..300053005300..',
|
||||
'..333333333333..',
|
||||
'..333300003333..',
|
||||
'..333300003333..',
|
||||
'..444444444444..',
|
||||
],
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成像素角色 SVG
|
||||
* @param {string} role - 兵种 key
|
||||
* @param {number} size - 显示尺寸 (px)
|
||||
* @returns {string} SVG HTML string
|
||||
*/
|
||||
function _renderPixelSvg(data, size) {
|
||||
const { palette, pixels } = data
|
||||
const grid = 16
|
||||
let rects = ''
|
||||
for (let y = 0; y < pixels.length; y++) {
|
||||
const row = pixels[y]
|
||||
for (let x = 0; x < row.length; x++) {
|
||||
const ch = row[x]
|
||||
if (ch === '.') continue
|
||||
const colorIdx = parseInt(ch)
|
||||
if (isNaN(colorIdx) || !palette[colorIdx]) continue
|
||||
rects += `<rect x="${x}" y="${y}" width="1" height="1" fill="${palette[colorIdx]}"/>`
|
||||
}
|
||||
}
|
||||
return `<svg viewBox="0 0 ${grid} ${grid}" width="${size}" height="${size}" xmlns="http://www.w3.org/2000/svg" style="image-rendering:pixelated">${rects}</svg>`
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成像素角色 SVG
|
||||
* @param {string} role - 兵种 key
|
||||
* @param {number} size - 显示尺寸 (px)
|
||||
* @returns {string} SVG HTML string
|
||||
*/
|
||||
export function pixelRole(role, size = 32) {
|
||||
return _renderPixelSvg(PIXEL_CHARS[role] || PIXEL_CHARS.general, size)
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成军营像素 SVG
|
||||
* @param {number} size - 显示尺寸 (px)
|
||||
* @returns {string} SVG HTML string
|
||||
*/
|
||||
export function pixelBarracks(size = 32) {
|
||||
return _renderPixelSvg(PIXEL_BARRACKS, size)
|
||||
}
|
||||
@@ -16,7 +16,7 @@ const NO_MOCK_CMDS = new Set([
|
||||
'auto_pair_device',
|
||||
'assistant_exec', 'assistant_write_file',
|
||||
'docker_create_container', 'docker_start_container', 'docker_stop_container',
|
||||
'docker_restart_container', 'docker_remove_container', 'docker_pull_image',
|
||||
'docker_restart_container', 'docker_remove_container', 'docker_container_exec', 'docker_gateway_chat', 'docker_pull_image',
|
||||
'docker_add_node', 'docker_remove_node',
|
||||
'instance_add', 'instance_remove', 'instance_set_active',
|
||||
])
|
||||
@@ -26,7 +26,7 @@ const WEB_ONLY_CMDS = new Set([
|
||||
'docker_test_endpoint',
|
||||
'docker_info', 'docker_list_containers', 'docker_create_container',
|
||||
'docker_start_container', 'docker_stop_container', 'docker_restart_container',
|
||||
'docker_remove_container', 'docker_container_logs', 'docker_pull_image',
|
||||
'docker_remove_container', 'docker_container_logs', 'docker_container_exec', 'docker_gateway_chat', 'docker_pull_image', 'docker_pull_status',
|
||||
'docker_list_images', 'docker_list_nodes', 'docker_add_node', 'docker_remove_node',
|
||||
'docker_cluster_overview',
|
||||
'instance_list', 'instance_add', 'instance_remove', 'instance_set_active',
|
||||
@@ -421,7 +421,10 @@ export const api = {
|
||||
dockerRestartContainer: (nodeId, containerId) => { invalidate('docker_cluster_overview', 'docker_list_containers'); return invoke('docker_restart_container', { nodeId, containerId }) },
|
||||
dockerRemoveContainer: (nodeId, containerId, force = false) => { invalidate('docker_cluster_overview', 'docker_list_containers'); return invoke('docker_remove_container', { nodeId, containerId, force }) },
|
||||
dockerContainerLogs: (nodeId, containerId, tail = 200) => invoke('docker_container_logs', { nodeId, containerId, tail }),
|
||||
dockerPullImage: (nodeId, image, tag) => invoke('docker_pull_image', { nodeId, image, tag }),
|
||||
dockerContainerExec: (nodeId, containerId, cmd) => invoke('docker_container_exec', { nodeId, containerId, cmd }),
|
||||
dockerGatewayChat: (nodeId, containerId, message) => invoke('docker_gateway_chat', { nodeId, containerId, message }),
|
||||
dockerPullImage: (nodeId, image, tag, requestId) => invoke('docker_pull_image', { nodeId, image, tag, requestId }),
|
||||
dockerPullStatus: (requestId) => invoke('docker_pull_status', { requestId }),
|
||||
dockerListImages: (nodeId) => invoke('docker_list_images', { nodeId }),
|
||||
dockerListNodes: () => cachedInvoke('docker_list_nodes', {}, 30000),
|
||||
dockerAddNode: (name, endpoint) => { invalidate('docker_list_nodes', 'docker_cluster_overview'); return invoke('docker_add_node', { name, endpoint }) },
|
||||
|
||||
Reference in New Issue
Block a user