feat:webui支持更加优化的模型配置,优化多处UI体验,支持设置视觉和cache价格,修复多重表达不生效的问题,修复表情包路径错误

This commit is contained in:
SengokuCola
2026-05-04 22:52:41 +08:00
parent 14b7bc78a2
commit eea95c1961
38 changed files with 1188 additions and 454 deletions

View File

@@ -7,6 +7,8 @@ import { useTour } from '@/components/tour'
import { MODEL_ASSIGNMENT_TOUR_ID, modelAssignmentTourSteps, STEP_ROUTE_MAP } from '@/components/tour/tours/model-assignment-tour'
interface UseModelTourOptions {
/** 打开模型编辑对话框回调 */
onOpenEditDialog?: () => void
/** 关闭编辑对话框回调 */
onCloseEditDialog?: () => void
}
@@ -24,13 +26,33 @@ interface UseModelTourReturn {
* Model 配置页面 Tour 引导 Hook
*/
export function useModelTour(options: UseModelTourOptions = {}): UseModelTourReturn {
const { onCloseEditDialog } = options
const { onOpenEditDialog, onCloseEditDialog } = options
const navigate = useNavigate()
const { registerTour, startTour: startTourFn, state: tourState, goToStep } = useTour()
// 用于追踪前一个步骤
const prevTourStepRef = useRef(tourState.stepIndex)
const didClickTourTarget = useCallback((event: MouseEvent, selector: string) => {
const target = event.target instanceof Element ? event.target : null
if (target?.closest(selector)) {
return true
}
const element = document.querySelector(selector)
if (!element) {
return false
}
const rect = element.getBoundingClientRect()
return (
event.clientX >= rect.left &&
event.clientX <= rect.right &&
event.clientY >= rect.top &&
event.clientY <= rect.bottom
)
}, [])
// 注册 Tour
useEffect(() => {
registerTour(MODEL_ASSIGNMENT_TOUR_ID, modelAssignmentTourSteps)
@@ -67,34 +89,59 @@ export function useModelTour(options: UseModelTourOptions = {}): UseModelTourRet
if (tourState.activeTourId !== MODEL_ASSIGNMENT_TOUR_ID || !tourState.isRunning) return
const handleTourClick = (e: MouseEvent) => {
const target = e.target as HTMLElement
const currentStep = tourState.stepIndex
// Step 3 (index 2): 点击添加提供商按钮
if (currentStep === 2 && target.closest('[data-tour="add-provider-button"]')) {
if (currentStep === 2 && didClickTourTarget(e, '[data-tour="add-provider-button"]')) {
setTimeout(() => goToStep(3), 300)
}
// Step 10 (index 9): 点击取消按钮(关闭提供商弹窗)
else if (currentStep === 9 && target.closest('[data-tour="provider-cancel-button"]')) {
else if (currentStep === 9 && didClickTourTarget(e, '[data-tour="provider-cancel-button"]')) {
setTimeout(() => goToStep(10), 300)
}
// Step 12 (index 11): 点击添加模型按钮
else if (currentStep === 11 && target.closest('[data-tour="add-model-button"]')) {
else if (currentStep === 11 && didClickTourTarget(e, '[data-tour="add-model-button"]')) {
onOpenEditDialog?.()
setTimeout(() => goToStep(12), 300)
}
// Step 18 (index 17): 点击取消按钮(关闭模型弹窗)
else if (currentStep === 17 && target.closest('[data-tour="model-cancel-button"]')) {
else if (currentStep === 17 && didClickTourTarget(e, '[data-tour="model-cancel-button"]')) {
setTimeout(() => goToStep(18), 300)
}
// Step 19 (index 18): 点击为模型分配功能标签页
else if (currentStep === 18 && target.closest('[data-tour="tasks-tab-trigger"]')) {
else if (currentStep === 18 && didClickTourTarget(e, '[data-tour="tasks-tab-trigger"]')) {
setTimeout(() => goToStep(19), 300)
}
}
document.addEventListener('click', handleTourClick, true)
return () => document.removeEventListener('click', handleTourClick, true)
}, [tourState, goToStep])
}, [tourState, goToStep, onOpenEditDialog, didClickTourTarget])
// Step 12 的 spotlight 点击在部分浏览器/布局下会被 Joyride 遮罩截获。
// 这里直接给目标按钮补一个原生监听,确保点中按钮时能打开模型弹窗。
useEffect(() => {
if (
tourState.activeTourId !== MODEL_ASSIGNMENT_TOUR_ID ||
!tourState.isRunning ||
tourState.stepIndex !== 11
) {
return
}
const addModelButton = document.querySelector('[data-tour="add-model-button"]')
if (!addModelButton) {
return
}
const handleAddModelButtonClick = () => {
onOpenEditDialog?.()
setTimeout(() => goToStep(12), 300)
}
addModelButton.addEventListener('click', handleAddModelButtonClick, true)
return () => addModelButton.removeEventListener('click', handleAddModelButtonClick, true)
}, [tourState.activeTourId, tourState.isRunning, tourState.stepIndex, goToStep, onOpenEditDialog])
// 开始引导
const handleStartTour = useCallback(() => {