This commit is contained in:
2025-03-25 23:25:59 +08:00
parent ddda5c1c68
commit 9579987fd9
61 changed files with 3902 additions and 1 deletions
+1
View File
@@ -0,0 +1 @@
通用的函数式组件
+194
View File
@@ -0,0 +1,194 @@
import { h, reactive, renderList } from "vue"
import { createSharedComposable } from "@vueuse/core"
import ActionDialog from "@/components/calc/action-dialog/index.vue"
import type { JSX } from "vue/jsx-runtime"
export type ConfirmDialogOption = Omit<ShowDialogOption, "okButton" | "cancelButton">
export type AlertDialogOption = Omit<ShowDialogOption, "okButton" | "cancelButton">
export type ElementLike = JSX.Element | string | ElementLike[]
function randomId() {
return Symbol(Math.random().toString(36).slice(2, 9))
}
type DialogID = string | number | symbol
export interface ShowDialogOption {
id?: DialogID
/**
* 标题
*/
header?: string | boolean
/**
* 内容插槽
*/
content?: (() => ElementLike) | ElementLike
/**
* 操作区插槽
*/
action?: (() => ElementLike) | ElementLike
cache?: boolean
/**
* 超时自动关闭
*/
timeout?: number
/**
* 关闭时的延迟动画
*/
duration?: number
/**
* 确定按钮文本 为false时不显示
*/
okButton?: string | boolean
/**
* 取消按钮文本 为false时不显示
*/
cancelButton?: string | boolean
/**
* 拒绝按钮文本 为false时不显示
*/
rejectButton?: string | boolean
// 默认状态
defaultStatus?: DialogResult["status"]
mask?: boolean
scale?: number
}
export interface DialogInstance {
id?: DialogID
visible: boolean
render: (visible?: boolean) => ElementLike
resolve: (result: DialogResult) => void
}
export interface DialogResult {
id: DialogID
status: "ok" | "reject" | "cancel" | "none"
data?: any
readonly isOk: boolean
readonly isCancel: boolean
readonly isReject: boolean
}
export const useDialog = createSharedComposable(() => {
const dialogs = reactive(new Map<DialogID, DialogInstance>())
// 注册窗口
function open(dialog: DialogInstance) {
const id = dialog.id ?? randomId()
dialogs.set(id, dialog)
return id
}
function close(id: DialogID, status = "cancel", duration = 400) {
const dialog = dialogs.get(id)
if (!dialog) {
return
}
dialog.resolve({
id,
status: "ok",
get isOk() {
return status == "ok"
},
get isCancel() {
return status == "cancel"
},
get isReject() {
return status == "reject"
}
})
dialog.visible = false
const fn = () => dialogs.delete(id)
if (duration > 0) {
setTimeout(fn, duration)
} else {
fn()
}
}
function render() {
return renderList(dialogs.values(), e => e.render(e.visible))
}
async function show(option: ShowDialogOption = {}) {
return new Promise<DialogResult>(resolve => {
const { id = randomId(), header = true, content, action, timeout, okButton = false, cancelButton = false, rejectButton = false, defaultStatus = "none", duration = 400, mask = false, scale = 1.0 } = option
const onClose = (status: DialogResult["status"] = defaultStatus) => {
close(id, status, duration)
}
open({
id,
visible: true,
resolve,
render(visible = true) {
return h(
ActionDialog,
{
header,
okButton,
cancelButton,
rejectButton,
visible,
onClose,
mask,
scale
},
{
default() {
switch (typeof content) {
case "function":
return content()
case "string":
return h("div", { class: "w-full justify-center text-hex-d4d6b6 text-center", style: "margin:10px" }, content)
default:
return content
}
},
action: typeof action == "function" || !action ? action : () => action
}
)
}
})
if (timeout) {
setTimeout(onClose, timeout)
}
})
}
async function alert(option: AlertDialogOption = {}) {
return show({
...option,
defaultStatus: "ok",
okButton: true,
cancelButton: false
})
}
async function confirm(option: ConfirmDialogOption = {}) {
return show({
...option,
defaultStatus: "cancel",
okButton: true,
cancelButton: true
})
}
return {
confirm,
alert,
render,
show,
close,
randomId
}
})
+29
View File
@@ -0,0 +1,29 @@
import type { Fn } from "@vueuse/core"
import type { InjectionKey, Ref } from "vue";
import { inject, provide, ref, watch } from "vue"
const IsOpenSymbol: InjectionKey<Ref<boolean>> = Symbol("is-open")
export function isShow() {
return inject(IsOpenSymbol, ref(false))
}
interface OnShowOption {
immediate?: boolean
}
export function onShow(fn: Fn, { immediate = false }: OnShowOption = {}) {
return watch(
isShow(),
val => {
if (val) {
fn()
}
},
{ immediate }
)
}
export function provideShow(ref: Ref<boolean>) {
provide(IsOpenSymbol, ref)
}
+11
View File
@@ -0,0 +1,11 @@
import type { PropType } from "vue"
export type ClassType = string | string[]
export type BaseType = string | number | boolean | object | symbol | undefined
export const valuePropType = [String, Number, Object, Boolean, Array] as PropType<BaseType>
export const classPropType = [String, Array] as PropType<string | string[]>
export const labelPropType = [String, Function] as PropType<string | ((val?: BaseType) => string) | null>