refactor: migrate all dialogs to a centralized shared dialog management system using useSharedDialog composable

This commit is contained in:
jxxghp
2026-05-17 22:54:17 +08:00
parent 30a4c55050
commit 48e6fc4466
88 changed files with 8300 additions and 6283 deletions

View File

@@ -0,0 +1,96 @@
import { markRaw, shallowRef, type Component } from 'vue'
export type SharedDialogEventHandler = (...args: any[]) => unknown
export interface SharedDialogOpenOptions {
closeOn?: string[] | false
events?: Record<string, SharedDialogEventHandler>
props?: Record<string, unknown>
replace?: boolean
}
export interface SharedDialogEntry {
closeOn: string[]
component: Component
events: Record<string, SharedDialogEventHandler>
id: number
props: Record<string, unknown>
visible: boolean
}
const DEFAULT_CLOSE_EVENTS = ['close']
const dialogStack = shallowRef<SharedDialogEntry[]>([])
let dialogSeed = 0
// 规范化弹窗关闭事件,避免每个调用方重复处理关闭约定。
function normalizeCloseEvents(closeOn: SharedDialogOpenOptions['closeOn']) {
if (closeOn === false) return []
return closeOn ?? DEFAULT_CLOSE_EVENTS
}
// 更新弹窗栈引用,确保 Host 能响应数组内容变化。
function setDialogStack(entries: SharedDialogEntry[]) {
dialogStack.value = entries
}
// 打开一个共享弹窗,并返回当前弹窗的控制器。
export function openSharedDialog(
component: Component,
props: Record<string, unknown> = {},
events: Record<string, SharedDialogEventHandler> = {},
options: Omit<SharedDialogOpenOptions, 'props' | 'events'> = {},
) {
const id = ++dialogSeed
const entry: SharedDialogEntry = {
closeOn: normalizeCloseEvents(options.closeOn),
component: markRaw(component),
events,
id,
props,
visible: true,
}
setDialogStack(options.replace ? [entry] : [...dialogStack.value, entry])
return {
id,
close: () => closeSharedDialog(id),
updateProps: (nextProps: Record<string, unknown>) => updateSharedDialogProps(id, nextProps),
}
}
// 使用对象参数打开共享弹窗,适合调用方需要传入更多选项的场景。
export function openSharedDialogWithOptions(component: Component, options: SharedDialogOpenOptions = {}) {
return openSharedDialog(component, options.props ?? {}, options.events ?? {}, {
closeOn: options.closeOn,
replace: options.replace,
})
}
// 关闭指定弹窗;未传 id 时关闭最上层弹窗。
export function closeSharedDialog(id?: number) {
if (id === undefined) {
setDialogStack(dialogStack.value.slice(0, -1))
return
}
setDialogStack(dialogStack.value.filter(entry => entry.id !== id))
}
// 合并更新指定弹窗的 props供进度弹窗等需要刷新内容的场景使用。
export function updateSharedDialogProps(id: number, props: Record<string, unknown>) {
setDialogStack(
dialogStack.value.map(entry => (entry.id === id ? { ...entry, props: { ...entry.props, ...props } } : entry)),
)
}
// 提供共享弹窗的响应式状态和命令式操作方法。
export function useSharedDialog() {
return {
dialogs: dialogStack,
openDialog: openSharedDialog,
openDialogWithOptions: openSharedDialogWithOptions,
closeDialog: closeSharedDialog,
updateDialogProps: updateSharedDialogProps,
}
}

View File

@@ -0,0 +1,177 @@
import { computed, ref } from 'vue'
export interface TransparencySettings {
backgroundBlur: number
backgroundPosterOpacity: number
blur: number
level: string
opacity: number
}
export const transparencyPresets = {
low: { opacity: 0.1, blur: 5 },
medium: { opacity: 0.3, blur: 10 },
high: { opacity: 0.6, blur: 15 },
}
/** 将数值限制在指定范围内。 */
function clamp(value: number, min: number, max: number) {
return Math.min(max, Math.max(min, value))
}
/** 从本地存储读取透明主题设置。 */
export function readTransparencySettings(): TransparencySettings {
return {
opacity: parseFloat(localStorage.getItem('transparency-opacity') || '0.3'),
blur: parseFloat(localStorage.getItem('transparency-blur') || '10'),
backgroundPosterOpacity: parseFloat(localStorage.getItem('transparency-background-poster-opacity') || '0'),
backgroundBlur: parseFloat(localStorage.getItem('transparency-background-blur') || '16'),
level: localStorage.getItem('transparency-level') || 'medium',
}
}
/** 应用透明主题设置并写入本地存储。 */
export function applyTransparencySettings(settings: TransparencySettings) {
const normalized: TransparencySettings = {
opacity: Number.isFinite(settings.opacity) ? clamp(settings.opacity, 0, 1) : 0.3,
blur: Number.isFinite(settings.blur) ? clamp(settings.blur, 0, 30) : 10,
backgroundPosterOpacity: Number.isFinite(settings.backgroundPosterOpacity)
? clamp(settings.backgroundPosterOpacity, 0, 1)
: 0,
backgroundBlur: Number.isFinite(settings.backgroundBlur) ? clamp(settings.backgroundBlur, 0, 30) : 16,
level: settings.level,
}
const root = document.documentElement
root.style.setProperty('--transparent-opacity', normalized.opacity.toString())
root.style.setProperty('--transparent-opacity-light', (normalized.opacity * 0.67).toString())
root.style.setProperty('--transparent-opacity-heavy', (normalized.opacity * 1.67).toString())
root.style.setProperty('--transparent-blur', `${normalized.blur}px`)
root.style.setProperty('--transparent-blur-light', `${normalized.blur * 0.6}px`)
root.style.setProperty('--transparent-blur-heavy', `${normalized.blur * 1.6}px`)
root.style.setProperty('--transparent-background-poster-opacity', (1 - normalized.backgroundPosterOpacity).toString())
root.style.setProperty('--transparent-background-blur', `${normalized.backgroundBlur}px`)
localStorage.setItem('transparency-opacity', normalized.opacity.toString())
localStorage.setItem('transparency-blur', normalized.blur.toString())
localStorage.setItem('transparency-background-poster-opacity', normalized.backgroundPosterOpacity.toString())
localStorage.setItem('transparency-background-blur', normalized.backgroundBlur.toString())
localStorage.setItem('transparency-level', normalized.level)
return normalized
}
/** 按本地存储中的最新值应用透明主题设置。 */
export function applyStoredTransparencySettings() {
return applyTransparencySettings(readTransparencySettings())
}
/** 提供透明主题设置的响应式状态和操作方法。 */
export function useTransparencySettings() {
const storedSettings = readTransparencySettings()
const transparencyOpacity = ref(storedSettings.opacity)
const transparencyBlur = ref(storedSettings.blur)
const backgroundPosterOpacity = ref(storedSettings.backgroundPosterOpacity)
const backgroundBlur = ref(storedSettings.backgroundBlur)
const transparencyLevel = ref(storedSettings.level)
const currentPresetLevel = computed(() => {
for (const [level, preset] of Object.entries(transparencyPresets)) {
if (
Math.abs(transparencyOpacity.value - preset.opacity) < 0.01 &&
Math.abs(transparencyBlur.value - preset.blur) < 0.1
) {
return level
}
}
return null
})
/** 同步当前响应式状态到 CSS 变量和本地存储。 */
function syncTransparencySettings() {
const normalized = applyTransparencySettings({
opacity: transparencyOpacity.value,
blur: transparencyBlur.value,
backgroundPosterOpacity: backgroundPosterOpacity.value,
backgroundBlur: backgroundBlur.value,
level: transparencyLevel.value,
})
transparencyOpacity.value = normalized.opacity
transparencyBlur.value = normalized.blur
backgroundPosterOpacity.value = normalized.backgroundPosterOpacity
backgroundBlur.value = normalized.backgroundBlur
transparencyLevel.value = normalized.level
}
/** 按预设级别调整透明度和模糊度。 */
function adjustTransparency(level: string) {
transparencyLevel.value = level
switch (level) {
case 'low':
transparencyOpacity.value = transparencyPresets.low.opacity
transparencyBlur.value = transparencyPresets.low.blur
break
case 'medium':
transparencyOpacity.value = transparencyPresets.medium.opacity
transparencyBlur.value = transparencyPresets.medium.blur
break
case 'high':
transparencyOpacity.value = transparencyPresets.high.opacity
transparencyBlur.value = transparencyPresets.high.blur
break
}
syncTransparencySettings()
}
/** 处理手动调整面板透明度。 */
function onOpacityChange() {
transparencyLevel.value = ''
syncTransparencySettings()
}
/** 处理手动调整面板模糊度。 */
function onBlurChange() {
transparencyLevel.value = ''
syncTransparencySettings()
}
/** 处理背景海报透明度变化。 */
function onBackgroundPosterOpacityChange() {
syncTransparencySettings()
}
/** 处理背景磨砂变化。 */
function onBackgroundBlurChange() {
syncTransparencySettings()
}
/** 重置透明主题设置为默认值。 */
function resetTransparencySettings() {
transparencyOpacity.value = transparencyPresets.medium.opacity
transparencyBlur.value = transparencyPresets.medium.blur
backgroundPosterOpacity.value = 0
backgroundBlur.value = 16
transparencyLevel.value = 'medium'
syncTransparencySettings()
}
return {
adjustTransparency,
backgroundBlur,
backgroundPosterOpacity,
currentPresetLevel,
onBackgroundBlurChange,
onBackgroundPosterOpacityChange,
onBlurChange,
onOpacityChange,
resetTransparencySettings,
syncTransparencySettings,
transparencyBlur,
transparencyOpacity,
transparencyLevel,
}
}