feat: 全局反馈系统 + 各模块底部统一动态提示
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
- 新增 components/RotatingFooterHint:统一文案+蓝色脉冲,4 秒轮换
- 新增 components/FeedbackFab:右下角悬浮按钮(渐变 + 心形信封 + 黄色脉冲点),
点击打开 4 步引导式弹窗
Step 1 选类型(💡新维度 / 🐛bug / 🎨界面 / 📝其他)
Step 2 描述需求 + 选当前板块(chip)
Step 3 留联系方式(可选)+ 提交概览
Step 4 ❤️ 成功页(弹簧 √ 动画)
顶部 spring 进度条,底部上一步/下一步,下拉手柄,背景点击或 X 关闭
- 后端 routes/feedback:bi_user_feedback 表(自动建表,含 status 字段)
POST /api/feedback/submit + GET /api/feedback/list
- Shell 全局挂载 FeedbackFab,自动从 hash 检测当前模块
- 各模块底部追加 RotatingFooterHint:
AssetsModule / MileageModule / SchedulingModule / EleImportPage
HydrogenOverview / HydrogenDaily / ElectricOverview / ElectricDaily
(HydrogenOverview 旧的内嵌实现已替换为共享组件)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
51
src/components/RotatingFooterHint.tsx
Normal file
51
src/components/RotatingFooterHint.tsx
Normal file
@@ -0,0 +1,51 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
const FOOTER_HINTS = [
|
||||
'想看哪个角度的数据?告诉我们一下嘛',
|
||||
'更多统计维度接入中,欢迎您的建议 ~',
|
||||
'下一个图表,可能就是您建议的那个',
|
||||
'数据科学家正在深夜挖掘新维度…',
|
||||
'维度灵感正在路上,钉一下产品同学也行',
|
||||
'数字背后还有故事,等下一次上线揭晓',
|
||||
];
|
||||
|
||||
interface Props {
|
||||
/** 自定义提示词集合,默认使用通用文案 */
|
||||
hints?: string[];
|
||||
/** 切换间隔,默认 4 秒 */
|
||||
intervalMs?: number;
|
||||
/** 额外类名 */
|
||||
className?: string;
|
||||
/** 点击时回调(一般用来打开反馈弹窗) */
|
||||
onClick?: () => void;
|
||||
}
|
||||
|
||||
export default function RotatingFooterHint({ hints = FOOTER_HINTS, intervalMs = 4000, className = '', onClick }: Props) {
|
||||
const [idx, setIdx] = useState(0);
|
||||
useEffect(() => {
|
||||
if (hints.length <= 1) return;
|
||||
const t = setInterval(() => setIdx(i => (i + 1) % hints.length), intervalMs);
|
||||
return () => clearInterval(t);
|
||||
}, [hints, intervalMs]);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`mt-1 flex items-center justify-center gap-1.5 text-[11px] text-slate-400 font-bold ${onClick ? 'cursor-pointer hover:text-blue-500 transition-colors' : ''} ${className}`}
|
||||
onClick={onClick}
|
||||
>
|
||||
<span className="inline-block w-1.5 h-1.5 rounded-full bg-blue-400 animate-pulse" />
|
||||
<span
|
||||
key={idx}
|
||||
style={{ animation: 'rotatingHintFade 0.5s ease' }}
|
||||
>
|
||||
{hints[idx]}
|
||||
</span>
|
||||
<style>{`
|
||||
@keyframes rotatingHintFade {
|
||||
from { opacity: 0; transform: translateY(2px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
`}</style>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user