feat: 新建流程首次进入展示引导提示,5 秒后自动关闭
This commit is contained in:
1 parent
3a1c176f4d
commit
37f2c8977b
2 files changed
+88
-1
No files matched your search
@@ -1,5 +1,5 @@
|
|||||||
import { useCallback, useEffect, useState } from "react";
|
import { useCallback, useEffect, useState } from "react";
|
||||||
import { ArrowLeft, Check, CircleAlert, CirclePlay, Loader2, Redo2, Save, Square, Undo2, X } from "lucide-react";
|
import { ArrowLeft, Check, CircleAlert, CirclePlay, Lightbulb, Loader2, Redo2, Save, Square, Undo2, X } from "lucide-react";
|
||||||
import type { Catalog } from "@shared/types";
|
import type { Catalog } from "@shared/types";
|
||||||
import { useEditorStore } from "../store/editorStore";
|
import { useEditorStore } from "../store/editorStore";
|
||||||
import { useRunStore } from "../store/runStore";
|
import { useRunStore } from "../store/runStore";
|
||||||
@@ -37,6 +37,23 @@ export function EditorPage({ catalog, onToast, onBack }: EditorPageProps) {
|
|||||||
const [saveConflict, setSaveConflict] = useState(false);
|
const [saveConflict, setSaveConflict] = useState(false);
|
||||||
const [savedAt, setSavedAt] = useState<string | null>(null);
|
const [savedAt, setSavedAt] = useState<string | null>(null);
|
||||||
const [validationIssues, setValidationIssues] = useState<FlowValidationIssue[] | null>(null);
|
const [validationIssues, setValidationIssues] = useState<FlowValidationIssue[] | null>(null);
|
||||||
|
// 新建流程首访引导:仅第一次进入空流程页面时展示,5 秒后自动关闭
|
||||||
|
const [showGuide, setShowGuide] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!flow || flow.nodes.length > 1) return;
|
||||||
|
const seen = window.localStorage.getItem("rpa.editor.guide.seen");
|
||||||
|
if (seen) return;
|
||||||
|
setShowGuide(true);
|
||||||
|
window.localStorage.setItem("rpa.editor.guide.seen", "1");
|
||||||
|
const timer = window.setTimeout(() => setShowGuide(false), 5000);
|
||||||
|
return () => window.clearTimeout(timer);
|
||||||
|
}, [flow?.id]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// 引导展示期间用户已拖入节点(不再是空流程)则立即收起
|
||||||
|
if (showGuide && flow && flow.nodes.length > 1) setShowGuide(false);
|
||||||
|
}, [flow?.nodes.length, showGuide]);
|
||||||
|
|
||||||
const save = useCallback(async (opts?: { interactive?: boolean }) => {
|
const save = useCallback(async (opts?: { interactive?: boolean }) => {
|
||||||
const current = useEditorStore.getState().flow;
|
const current = useEditorStore.getState().flow;
|
||||||
@@ -213,6 +230,16 @@ export function EditorPage({ catalog, onToast, onBack }: EditorPageProps) {
|
|||||||
|
|
||||||
<FlowCanvas catalog={catalog} onAddAt={(componentId, position) => addNode(componentId, position)} />
|
<FlowCanvas catalog={catalog} onAddAt={(componentId, position) => addNode(componentId, position)} />
|
||||||
|
|
||||||
|
{showGuide && (
|
||||||
|
<div className="editor-guide" role="status">
|
||||||
|
<span className="editor-guide-icon"><Lightbulb className="icon" /></span>
|
||||||
|
<div className="editor-guide-text">
|
||||||
|
<strong>从左侧「组件库」拖入能力节点,开始搭建流程</strong>
|
||||||
|
<span>拖拽到画布任意位置,或单击组件快速追加;支持按名称 / ID / 标签搜索。</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
<div className="canvas-badge">
|
<div className="canvas-badge">
|
||||||
<Check className="icon" />
|
<Check className="icon" />
|
||||||
可执行 {catalog.components.filter((item) => item.implemented).length} / {catalog.counts.total} 项
|
可执行 {catalog.components.filter((item) => item.implemented).length} / {catalog.counts.total} 项
|
||||||
|
|||||||
@@ -2257,6 +2257,66 @@ textarea:focus-visible {
|
|||||||
margin-top: 2px;
|
margin-top: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 新建流程首访引导:顶部浮出,5 秒后自动淡出,不接收鼠标事件 */
|
||||||
|
.editor-guide {
|
||||||
|
position: absolute;
|
||||||
|
left: 50%;
|
||||||
|
top: 52px;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
z-index: 5;
|
||||||
|
max-width: 460px;
|
||||||
|
padding: 11px 16px;
|
||||||
|
display: flex;
|
||||||
|
gap: 11px;
|
||||||
|
align-items: center;
|
||||||
|
border: 1px solid var(--line);
|
||||||
|
border-radius: 13px;
|
||||||
|
background: rgba(255, 255, 255, 0.94);
|
||||||
|
box-shadow: var(--shadow-panel);
|
||||||
|
backdrop-filter: blur(8px);
|
||||||
|
pointer-events: none;
|
||||||
|
animation: guide-life 5s ease forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-guide-icon {
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
flex: none;
|
||||||
|
display: grid;
|
||||||
|
place-items: center;
|
||||||
|
border-radius: 9px;
|
||||||
|
background: var(--blue-soft);
|
||||||
|
color: var(--blue-dark);
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-guide-icon .icon {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-guide-text strong {
|
||||||
|
display: block;
|
||||||
|
font-size: 12.5px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--ink);
|
||||||
|
line-height: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-guide-text span {
|
||||||
|
display: block;
|
||||||
|
font-size: 11px;
|
||||||
|
color: var(--muted);
|
||||||
|
line-height: 16px;
|
||||||
|
margin-top: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes guide-life {
|
||||||
|
0% { opacity: 0; transform: translateX(-50%) translateY(-10px); }
|
||||||
|
6% { opacity: 1; transform: translateX(-50%) translateY(0); }
|
||||||
|
90% { opacity: 1; }
|
||||||
|
100% { opacity: 0; transform: translateX(-50%) translateY(-6px); }
|
||||||
|
}
|
||||||
|
|
||||||
.mono {
|
.mono {
|
||||||
font-family: var(--mono);
|
font-family: var(--mono);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user