refactor: update artifact list style
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { memo } from "react";
|
||||
import { memo, useMemo, useState } from "react";
|
||||
|
||||
import { type ScheduleArtifact } from "../../services/api";
|
||||
import Icon from "../../components/common/Icon";
|
||||
@@ -29,10 +29,41 @@ function ArtifactListImpl({
|
||||
))
|
||||
: artifacts;
|
||||
|
||||
const [expandedScriptNames, setExpandedScriptNames] = useState<Set<string>>(new Set());
|
||||
|
||||
const groups = useMemo(() => {
|
||||
const map = new Map<string, ScheduleArtifact[]>();
|
||||
for (const item of filtered) {
|
||||
const list = map.get(item.script_name);
|
||||
if (list) list.push(item);
|
||||
else map.set(item.script_name, [item]);
|
||||
}
|
||||
// 组内按 created_at desc,version_label desc 兜底
|
||||
for (const list of map.values()) {
|
||||
list.sort((a, b) => {
|
||||
if (a.created_at !== b.created_at) return a.created_at < b.created_at ? 1 : -1;
|
||||
return a.version_label < b.version_label ? 1 : -1;
|
||||
});
|
||||
}
|
||||
return Array.from(map.entries()).sort(([a], [b]) => a.localeCompare(b));
|
||||
}, [filtered]);
|
||||
|
||||
function toggle(scriptName: string) {
|
||||
setExpandedScriptNames((prev) => {
|
||||
const next = new Set(prev);
|
||||
if (next.has(scriptName)) next.delete(scriptName);
|
||||
else next.add(scriptName);
|
||||
return next;
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<section className="schedule-panel schedule-artifacts-panel">
|
||||
<div className="schedule-panel__title">
|
||||
<div><strong>稳定版本脚本</strong><span>{artifacts.length}</span></div>
|
||||
<div>
|
||||
<strong>稳定版本脚本</strong>
|
||||
<span className="schedule-panel__count">{artifacts.length}</span>
|
||||
</div>
|
||||
</div>
|
||||
<label className="schedule-search">
|
||||
<Icon name="search" size={15} />
|
||||
@@ -42,45 +73,89 @@ function ArtifactListImpl({
|
||||
onChange={(event) => onKeywordChange(event.target.value)}
|
||||
/>
|
||||
</label>
|
||||
<p className="artifact-tip">拖动卡片到画布,节点将保存 versions_id</p>
|
||||
<div className="artifact-list">
|
||||
{filtered.length ? filtered.map((artifact) => (
|
||||
<div
|
||||
className="artifact-card"
|
||||
draggable
|
||||
key={artifact.versions_id}
|
||||
onDragStart={(event) => {
|
||||
event.dataTransfer.effectAllowed = "copy";
|
||||
event.dataTransfer.setData(
|
||||
"application/x-model-platform-version",
|
||||
artifact.versions_id,
|
||||
);
|
||||
event.dataTransfer.setData("text/plain", artifact.versions_id);
|
||||
}}
|
||||
onContextMenu={(event) => onContextMenu(event, artifact)}
|
||||
onDoubleClick={() => onDoubleClick(artifact)}
|
||||
>
|
||||
<span className={`artifact-card__icon artifact-card__icon--${artifact.script_type}`}>
|
||||
<Icon
|
||||
name={artifact.script_type === "notebook" ? "notebook" : "python"}
|
||||
size={17}
|
||||
/>
|
||||
</span>
|
||||
<span>
|
||||
<strong>{artifact.script_name}</strong>
|
||||
<small>
|
||||
{artifact.version_label} · {shortHash(artifact.content_hash)}
|
||||
</small>
|
||||
</span>
|
||||
</div>
|
||||
)) : (
|
||||
{groups.length === 0 ? (
|
||||
<p className="schedule-empty">
|
||||
暂无稳定版本,请先在"构建脚本"中发布
|
||||
</p>
|
||||
) : (
|
||||
groups.map(([scriptName, items]) => {
|
||||
const isExpanded = expandedScriptNames.has(scriptName);
|
||||
const scriptType = items[0].script_type;
|
||||
const iconName = scriptType === "notebook" ? "notebook" : "python";
|
||||
return (
|
||||
<div key={scriptName} className="artifact-group">
|
||||
<button
|
||||
type="button"
|
||||
className="artifact-group__header"
|
||||
aria-expanded={isExpanded}
|
||||
onClick={() => toggle(scriptName)}
|
||||
>
|
||||
<span className="artifact-group__main">
|
||||
<span className={`artifact-group__chevron${isExpanded ? " is-open" : ""}`}>
|
||||
<Icon name="chevron" size={14} />
|
||||
</span>
|
||||
<span className={`artifact-group__icon artifact-group__icon--${scriptType}`}>
|
||||
<Icon name={iconName} size={14} />
|
||||
</span>
|
||||
<span className="artifact-group__name" title={scriptName}>
|
||||
{scriptName}
|
||||
</span>
|
||||
</span>
|
||||
{items.length > 1 && (
|
||||
<span className="artifact-group__count">{items.length}</span>
|
||||
)}
|
||||
</button>
|
||||
{isExpanded && (
|
||||
<div className="artifact-versions">
|
||||
{items.map((artifact) => (
|
||||
<div
|
||||
className="artifact-version"
|
||||
draggable
|
||||
key={artifact.versions_id}
|
||||
onDragStart={(event) => {
|
||||
event.dataTransfer.effectAllowed = "copy";
|
||||
event.dataTransfer.setData(
|
||||
"application/x-model-platform-version",
|
||||
artifact.versions_id,
|
||||
);
|
||||
event.dataTransfer.setData("text/plain", artifact.versions_id);
|
||||
}}
|
||||
onContextMenu={(event) => onContextMenu(event, artifact)}
|
||||
onDoubleClick={() => onDoubleClick(artifact)}
|
||||
>
|
||||
<span className="artifact-version__main">
|
||||
<span className="artifact-version__handle">
|
||||
<Icon name="grip-vertical" size={13} />
|
||||
</span>
|
||||
<span className="artifact-version__name">
|
||||
{artifact.version_label}
|
||||
</span>
|
||||
<span className="artifact-version__commit">
|
||||
{shortHash(artifact.content_hash)}
|
||||
</span>
|
||||
</span>
|
||||
<span className="artifact-version__action">
|
||||
<Icon name="external" size={12} />
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})
|
||||
)}
|
||||
</div>
|
||||
<p className="artifact-tip artifact-tip--footer">
|
||||
<span>按住</span>
|
||||
<span className="artifact-tip__grip">
|
||||
<Icon name="grip-vertical" size={12} />
|
||||
</span>
|
||||
<span>拖拽卡片到画布即可绑定节点</span>
|
||||
</p>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
export const ArtifactList = memo(ArtifactListImpl);
|
||||
export const ArtifactList = memo(ArtifactListImpl);
|
||||
Reference in New Issue
Block a user