init
@@ -0,0 +1 @@
|
||||
无样式的基础组件
|
||||
@@ -0,0 +1 @@
|
||||
export { default as NTooltip } from "./tooltip"
|
||||
@@ -0,0 +1,150 @@
|
||||
import { syncRefs, useVModel } from "@vueuse/core"
|
||||
import type { CSSProperties, PropType } from "vue";
|
||||
import { Teleport, Transition, computed, defineComponent, nextTick, reactive, ref, renderSlot, watch } from "vue"
|
||||
import { provideShow } from "../hooks/show"
|
||||
|
||||
export const tooltipProps = {
|
||||
popClass: {
|
||||
type: String
|
||||
},
|
||||
position: {
|
||||
type: String as PropType<"bottom" | "top" | "left" | "right" | "auto">,
|
||||
default: () => "auto"
|
||||
},
|
||||
offset: {
|
||||
type: Number,
|
||||
default: () => 8
|
||||
},
|
||||
lazy: {
|
||||
type: Boolean,
|
||||
default: () => false
|
||||
},
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: () => false
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: () => false
|
||||
}
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
name: "ITooltip",
|
||||
props: tooltipProps,
|
||||
emits: ["change", "update:visible"],
|
||||
setup(props, { slots, emit }) {
|
||||
const isOpen = ref(props.visible)
|
||||
|
||||
syncRefs(useVModel(props, "visible"), isOpen)
|
||||
|
||||
provideShow(isOpen)
|
||||
|
||||
let timer: NodeJS.Timeout
|
||||
|
||||
const triggerRef = ref<HTMLElement>()
|
||||
const popupRef = ref<HTMLElement>()
|
||||
|
||||
const dropdownPosition = reactive({ x: 0, y: 0 })
|
||||
|
||||
const isShow = computed(() => isOpen.value && triggerRef.value && popupRef.value && dropdownPosition.x + dropdownPosition.y > 0)
|
||||
|
||||
const dropdownStyle = computed<CSSProperties>(() => {
|
||||
return {
|
||||
left: `${dropdownPosition.x}px`,
|
||||
top: `${dropdownPosition.y}px`,
|
||||
visibility: isShow.value ? "visible" : "hidden"
|
||||
}
|
||||
})
|
||||
|
||||
const mounted = ref(false)
|
||||
|
||||
function onMouseover() {
|
||||
if (props.disabled) {
|
||||
return
|
||||
}
|
||||
timer = setTimeout(
|
||||
() => {
|
||||
isOpen.value = true
|
||||
emit("change", true)
|
||||
},
|
||||
props.lazy ? 800 : 100
|
||||
)
|
||||
}
|
||||
|
||||
function onMouseout() {
|
||||
if (props.disabled) {
|
||||
return
|
||||
}
|
||||
clearTimeout(timer)
|
||||
isOpen.value = false
|
||||
emit("change", false)
|
||||
}
|
||||
|
||||
function resize() {
|
||||
const trigger = triggerRef.value
|
||||
const popup = popupRef.value
|
||||
|
||||
if (!!trigger && !!popup) {
|
||||
const { width, height, left, top } = trigger.getBoundingClientRect()
|
||||
|
||||
const { clientWidth: pWidth, clientHeight: pHeight } = popup
|
||||
|
||||
let x = 0
|
||||
let y = 0
|
||||
|
||||
const offset = props.offset
|
||||
|
||||
switch (props.position) {
|
||||
case "auto":
|
||||
case "bottom":
|
||||
x = left + width / 2
|
||||
y = top + height + offset
|
||||
break
|
||||
case "top":
|
||||
x = left
|
||||
y = top - pHeight - offset
|
||||
break
|
||||
case "left":
|
||||
x = left - pWidth - offset
|
||||
y = top
|
||||
break
|
||||
case "right":
|
||||
x = left + width + offset
|
||||
y = top
|
||||
break
|
||||
}
|
||||
if (props.position == "auto") {
|
||||
x = window.innerWidth - x > 500 ? x + 20 : x - 20 - 310
|
||||
y = window.innerHeight - y > popup.offsetHeight + 50 ? y : window.innerHeight - (popup.offsetHeight + 50)
|
||||
}
|
||||
dropdownPosition.x = x
|
||||
dropdownPosition.y = y
|
||||
}
|
||||
}
|
||||
|
||||
watch(isOpen, val => {
|
||||
if (val) {
|
||||
mounted.value = true
|
||||
}
|
||||
nextTick(resize)
|
||||
})
|
||||
|
||||
return () => {
|
||||
return (
|
||||
<div {...{ onMouseout, onMouseover }} ref={triggerRef}>
|
||||
{renderSlot(slots, "default")}
|
||||
<Teleport to="body">
|
||||
<Transition name="dropdown" mode="out-in">
|
||||
{mounted.value && !props.disabled && (
|
||||
<div class={["i-popper z-999", props.popClass]} style={dropdownStyle.value} ref={popupRef}>
|
||||
{renderSlot(slots, "popper")}
|
||||
</div>
|
||||
)}
|
||||
</Transition>
|
||||
</Teleport>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1 @@
|
||||
DNF风格定制组件
|
||||
@@ -0,0 +1,207 @@
|
||||
<script lang="tsx">
|
||||
import { onKeyStroke, syncRef, useVModel } from "@vueuse/core"
|
||||
import type { PropType, RendererElement } from "vue"
|
||||
import { defineComponent, ref, renderSlot } from "vue"
|
||||
|
||||
import CalcDialog from "@/components/calc/dialog/index.vue"
|
||||
import CalcButton from "@/components/calc/button/index.vue"
|
||||
import type { JSX } from "vue/jsx-runtime"
|
||||
|
||||
export type ActionDialogResult = "ok" | "reject" | "cancel" | "none"
|
||||
|
||||
export default defineComponent({
|
||||
name: "IActionDialog",
|
||||
props: {
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: () => false
|
||||
},
|
||||
class: {
|
||||
type: String
|
||||
},
|
||||
mask: {
|
||||
type: Boolean,
|
||||
default: () => false
|
||||
},
|
||||
// 模态框
|
||||
modal: {
|
||||
type: Boolean,
|
||||
default: () => false
|
||||
},
|
||||
header: {
|
||||
type: [String, Boolean] as PropType<string | boolean>,
|
||||
default: () => true
|
||||
},
|
||||
drag: {
|
||||
type: String as PropType<"header" | "all" | "none">,
|
||||
default: () => "header"
|
||||
},
|
||||
okButton: {
|
||||
type: [String, Boolean],
|
||||
default: "确定"
|
||||
},
|
||||
rejectButton: {
|
||||
type: [String, Boolean],
|
||||
default: false
|
||||
},
|
||||
cancelButton: {
|
||||
type: [String, Boolean],
|
||||
default: "取消"
|
||||
},
|
||||
closeOnYes: {
|
||||
type: Boolean,
|
||||
default: () => true
|
||||
},
|
||||
closeOnCancel: {
|
||||
type: Boolean,
|
||||
default: () => true
|
||||
},
|
||||
closeOnReject: {
|
||||
type: Boolean,
|
||||
default: () => true
|
||||
},
|
||||
closeOnEsc: {
|
||||
type: Boolean,
|
||||
default: () => true
|
||||
},
|
||||
cache: {
|
||||
// 维持窗口状态
|
||||
type: Boolean,
|
||||
default: () => true
|
||||
},
|
||||
defaultStatus: {
|
||||
type: String as PropType<ActionDialogResult>,
|
||||
default: () => "cancel"
|
||||
},
|
||||
appendTo: {
|
||||
type: [Object, String] as PropType<string | RendererElement | null | undefined>,
|
||||
default: "body"
|
||||
},
|
||||
scale: {
|
||||
type: Number,
|
||||
default: 1.0
|
||||
}
|
||||
},
|
||||
emits: ["close", "ok", "cancel", "action", "update:visible"],
|
||||
setup(props, { emit, slots }) {
|
||||
const visible = ref(props.visible)
|
||||
|
||||
syncRef(useVModel(props, "visible"), visible, { direction: "both" })
|
||||
|
||||
const result = ref<ActionDialogResult>(props.defaultStatus)
|
||||
|
||||
function close() {
|
||||
visible.value = false
|
||||
emit("close", result.value)
|
||||
}
|
||||
|
||||
onKeyStroke("ESC", (e: Event) => {
|
||||
if (visible.value && props.closeOnEsc) {
|
||||
e.preventDefault()
|
||||
close()
|
||||
}
|
||||
})
|
||||
|
||||
function onOkClick() {
|
||||
result.value = "ok"
|
||||
if (props.closeOnYes) {
|
||||
close()
|
||||
}
|
||||
}
|
||||
|
||||
function onRejectClick() {
|
||||
result.value = "reject"
|
||||
if (props.closeOnReject) {
|
||||
close()
|
||||
}
|
||||
}
|
||||
|
||||
function onCancelClick() {
|
||||
result.value = "cancel"
|
||||
if (props.closeOnCancel) {
|
||||
close()
|
||||
}
|
||||
}
|
||||
|
||||
function onCloseClick() {
|
||||
close()
|
||||
}
|
||||
|
||||
function renderAction() {
|
||||
if ((props.okButton || props.cancelButton || props.rejectButton) && !slots.action) {
|
||||
const buttons: JSX.Element[] = []
|
||||
if (props.okButton) {
|
||||
buttons.push(
|
||||
<CalcButton class="mx-2px" type="primary" onClick={onOkClick}>
|
||||
{props.okButton === true ? "确定" : props.okButton}
|
||||
</CalcButton>
|
||||
)
|
||||
}
|
||||
if (props.rejectButton) {
|
||||
buttons.push(
|
||||
<CalcButton class="mx-2px" type="primary" onClick={onRejectClick}>
|
||||
{props.rejectButton === true ? "拒绝" : props.rejectButton}
|
||||
</CalcButton>
|
||||
)
|
||||
}
|
||||
if (props.cancelButton) {
|
||||
buttons.push(
|
||||
<CalcButton class="mx-2px" onClick={onCancelClick}>
|
||||
{props.cancelButton === true ? "取消" : props.cancelButton}
|
||||
</CalcButton>
|
||||
)
|
||||
}
|
||||
|
||||
return <div class="flex pb-2 items-center justify-center">{buttons}</div>
|
||||
}
|
||||
return renderSlot(slots, "action")
|
||||
}
|
||||
|
||||
return () => {
|
||||
return (
|
||||
<CalcDialog
|
||||
onClose={onCloseClick}
|
||||
v-model:visible={visible.value}
|
||||
drag={props.drag}
|
||||
mask={props.mask}
|
||||
modal={props.modal}
|
||||
header={props.header == true ? "提示" : props.header}
|
||||
appendTo={props.appendTo}
|
||||
scale={props.scale}
|
||||
>
|
||||
<div class={["flex items-center justify-center"]}> {renderSlot(slots, "default")}</div>
|
||||
{renderAction()}
|
||||
</CalcDialog>
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.i-button {
|
||||
text-shadow: none;
|
||||
|
||||
color: #a89679;
|
||||
background: linear-gradient(#12396b, #081f3c);
|
||||
border: 1px solid #554a3d;
|
||||
|
||||
&:hover {
|
||||
color: #fee97d;
|
||||
background: linear-gradient(#2d4e9f, #153051);
|
||||
// border: 1px solid #e3c18a;
|
||||
color: #d4d4b6;
|
||||
background: linear-gradient(#195cab, #0b3260);
|
||||
border: 1px solid #867760;
|
||||
}
|
||||
|
||||
&:active {
|
||||
color: #e9c556;
|
||||
background: linear-gradient(#2d4e9f, #122438);
|
||||
border: 1px solid #e3c18a;
|
||||
color: #d4d4b6;
|
||||
background: linear-gradient(#195cab, #0b3260);
|
||||
border: 1px solid #918168;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,222 @@
|
||||
<script lang="tsx">
|
||||
import { debouncedWatch, onClickOutside, useVModel } from "@vueuse/core"
|
||||
import type { CSSProperties, PropType } from "vue";
|
||||
import { Teleport, Transition, computed, defineComponent, onDeactivated, ref, renderList, watch } from "vue"
|
||||
|
||||
type HandleChangeFunction = (value: string | number) => string[] | Promise<string[]>
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
modelValue: {
|
||||
type: [String, Number] as PropType<string | number>,
|
||||
default: () => ""
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: ""
|
||||
},
|
||||
handleChange: {
|
||||
type: Function as PropType<HandleChangeFunction>,
|
||||
default: () => () => []
|
||||
},
|
||||
delay: {
|
||||
type: Number,
|
||||
default: 300
|
||||
}
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
const modelValue = useVModel(props, "modelValue")
|
||||
|
||||
const isOpen = ref(false)
|
||||
const triggerRef = ref<HTMLElement>()
|
||||
|
||||
watch(isOpen, onResize)
|
||||
|
||||
// 下拉框位置
|
||||
const dropdownPosition = ref({ x: 0, y: 0, w: 0 })
|
||||
// 下拉框位置
|
||||
const dropdownStyle = computed<CSSProperties>(() => {
|
||||
return {
|
||||
left: `${dropdownPosition.value.x}px`,
|
||||
top: `${dropdownPosition.value.y}px`,
|
||||
width: `${dropdownPosition.value.w}px`
|
||||
}
|
||||
})
|
||||
|
||||
function onResize() {
|
||||
if (triggerRef.value) {
|
||||
const { width, height, left, top } = triggerRef.value.getBoundingClientRect()
|
||||
dropdownPosition.value = {
|
||||
w: width,
|
||||
x: left,
|
||||
y: top + height + 2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onClickOutside(triggerRef, () => (isOpen.value = false))
|
||||
|
||||
window.addEventListener("resize", onResize)
|
||||
window.addEventListener("scroll", onResize)
|
||||
|
||||
onDeactivated(() => {
|
||||
window.removeEventListener("resize", onResize)
|
||||
window.removeEventListener("scroll", onResize)
|
||||
})
|
||||
|
||||
function selectItem(item: string) {
|
||||
return () => {
|
||||
modelValue.value = item
|
||||
}
|
||||
}
|
||||
|
||||
const delay = computed(() => props.delay)
|
||||
|
||||
const droplist = ref<string[]>([])
|
||||
|
||||
debouncedWatch(modelValue, async value => (droplist.value = await props.handleChange(value)), {
|
||||
debounce: delay
|
||||
})
|
||||
|
||||
function handleInput() {
|
||||
isOpen.value = droplist.value.length > 0
|
||||
}
|
||||
|
||||
function handleKeyPress(event: KeyboardEvent) {
|
||||
if (event.code == "Enter") {
|
||||
emit("enter", modelValue.value)
|
||||
}
|
||||
}
|
||||
|
||||
return () => {
|
||||
return (
|
||||
<div class="i-autocomplete">
|
||||
<div class="i-autocomplete-trigger" ref={triggerRef}>
|
||||
<input onKeypress={handleKeyPress} class="i-autocomplete-input" onFocus={handleInput} onInput={handleInput} type="text" v-model={modelValue.value} placeholder={props.placeholder} />
|
||||
</div>
|
||||
<Teleport to="body">
|
||||
<Transition name="dropdown" mode="out-in">
|
||||
<div class="i-autocomplete-dropdown" style={dropdownStyle.value} v-show={isOpen.value}>
|
||||
{renderList(droplist.value, item => (
|
||||
<div class="i-autocomplete-dropdown-item" onClick={selectItem(item)}>
|
||||
{item}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</Transition>
|
||||
</Teleport>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
$text-color: #937639;
|
||||
.i-autocomplete {
|
||||
min-width: 80px;
|
||||
width: 160px;
|
||||
user-select: none;
|
||||
outline: none;
|
||||
height: 24px;
|
||||
min-height: 16px;
|
||||
line-height: 100%;
|
||||
// font-size: 12px;
|
||||
color: $text-color;
|
||||
background-color: rgba(0, 0, 0, 1);
|
||||
border: 1px solid #5b472a;
|
||||
border-radius: 2px;
|
||||
display: block;
|
||||
|
||||
.i-autocomplete-trigger {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
opacity: 0.8;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.i-autocomplete-input {
|
||||
padding-left: 8px;
|
||||
width: 100%;
|
||||
border: none;
|
||||
outline: none;
|
||||
color: inherit;
|
||||
// font-size: 12px;
|
||||
background-color: black;
|
||||
font-family: SimSun;
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
color: gray;
|
||||
|
||||
.i-select-down-icon {
|
||||
background-image: url("./img/select_down_disabled.png");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.i-autocomplete-trigger:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.i-autocomplete-dropdown {
|
||||
position: fixed;
|
||||
max-height: 160px;
|
||||
overflow-y: auto;
|
||||
background: black;
|
||||
// font-size: 12px;
|
||||
z-index: 888;
|
||||
|
||||
$hoverColor: #002947;
|
||||
$activeColor: lighten($hoverColor, 5%);
|
||||
color: $text-color;
|
||||
|
||||
.i-autocomplete-dropdown-item {
|
||||
// font-size: 12px;
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
margin: 0;
|
||||
padding: 0 8px;
|
||||
border: none;
|
||||
outline: none;
|
||||
appearance: none;
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
|
||||
&.active {
|
||||
background-color: $activeColor;
|
||||
}
|
||||
|
||||
&:hover:not(.active) {
|
||||
// font-size: 12px;
|
||||
background-color: $hoverColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-active {
|
||||
animation: dropdown-enter 0.2s ease-in;
|
||||
}
|
||||
|
||||
.dropdown-active {
|
||||
animation: dropdown-leave 0.2s ease-in;
|
||||
}
|
||||
|
||||
.dropdown-enter-active {
|
||||
animation: dropdown-enter 0.2s ease-in;
|
||||
}
|
||||
|
||||
.dropdown-leave-active {
|
||||
animation: dropdown-leave 0.2s ease-in;
|
||||
}
|
||||
|
||||
.dropdown-enter-active {
|
||||
animation: dropdown-enter 0.2s ease-in;
|
||||
}
|
||||
|
||||
.dropdown-leave-active {
|
||||
animation: dropdown-leave 0.2s ease-in;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,100 @@
|
||||
<script lang="tsx">
|
||||
import { defineComponent, renderSlot } from "vue"
|
||||
import { RouterLink } from "vue-router"
|
||||
|
||||
export default defineComponent({
|
||||
name: "CalcButton",
|
||||
props: {
|
||||
small: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
to: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
class: {
|
||||
type: [String],
|
||||
default: ""
|
||||
},
|
||||
icon: {
|
||||
type: String
|
||||
}
|
||||
},
|
||||
setup(props, { slots }) {
|
||||
return () => {
|
||||
const proerpties = {
|
||||
to: props.to,
|
||||
class: {
|
||||
[props.class]: !!props.class,
|
||||
"i-button cursor-pointer outline-none text-shadow": true,
|
||||
"small": props.small,
|
||||
[`icon-${props.icon}`]: !!props.icon
|
||||
}
|
||||
}
|
||||
const content = renderSlot(slots, "default")
|
||||
return props.to ? <RouterLink {...proerpties}>{content}</RouterLink> : <button {...proerpties}>{content}</button>
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.i-button {
|
||||
min-width: 56px;
|
||||
height: 24px;
|
||||
// line-height: 20px;
|
||||
|
||||
// font-size: 12px;
|
||||
color: #e9c556;
|
||||
background: linear-gradient(#223768, #122438);
|
||||
border: 1px solid #755f44;
|
||||
border-radius: 2px;
|
||||
display: block;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
transition: all 200ms ease-in-out;
|
||||
|
||||
&[class*="icon-"] {
|
||||
min-width: 1px;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
padding: 0;
|
||||
border: none !important;
|
||||
transition: all 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: #fee97d;
|
||||
background: linear-gradient(#2d4e9f, #153051);
|
||||
// border: 1px solid #e3c18a;
|
||||
}
|
||||
|
||||
&:active {
|
||||
color: #e9c556;
|
||||
background: linear-gradient(#2d4e9f, #122438);
|
||||
border: 1px solid #e3c18a;
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
color: #696969;
|
||||
background: linear-gradient(#353535, #1c1c1c);
|
||||
border: 1px solid #414141;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
&.small {
|
||||
min-width: 28px;
|
||||
height: 20px;
|
||||
// line-height: 20px;
|
||||
font-size: 12px;
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.i-href {
|
||||
display: block;
|
||||
text-decoration: none;
|
||||
}
|
||||
</style>
|
||||
|
After Width: | Height: | Size: 205 B |
|
After Width: | Height: | Size: 205 B |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 400 B |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 215 B |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 400 B |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 215 B |
@@ -0,0 +1,271 @@
|
||||
<script lang="tsx">
|
||||
import { selectionListEmits, selectionListProps } from "hoci"
|
||||
import type { CSSProperties, PropType } from "vue";
|
||||
import { Teleport, Transition, computed, defineComponent, onDeactivated, ref, watch } from "vue"
|
||||
|
||||
import { onClickOutside, useVModel } from "@vueuse/core"
|
||||
interface ICascaderItem {
|
||||
label: string
|
||||
id?: string | number
|
||||
value?: any
|
||||
children?: ICascaderItem[]
|
||||
onSelect?: () => void
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
name: "ICascader",
|
||||
props: {
|
||||
...selectionListProps,
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
width: {
|
||||
type: Number,
|
||||
default: 120
|
||||
},
|
||||
emptyLabel: {
|
||||
type: String
|
||||
},
|
||||
highlight: {
|
||||
type: String,
|
||||
// warn remind
|
||||
default: ""
|
||||
},
|
||||
items: {
|
||||
type: Array as PropType<ICascaderItem[]>,
|
||||
default: () => []
|
||||
}
|
||||
},
|
||||
emits: [...selectionListEmits, "select"],
|
||||
setup(props, { emit }) {
|
||||
const modelValue = useVModel(props, "modelValue")
|
||||
|
||||
const isOpen = ref(false)
|
||||
const triggerRef = ref<HTMLElement>()
|
||||
const selectRef = ref<HTMLElement>()
|
||||
const dropdownRef = ref<HTMLElement>()
|
||||
|
||||
watch(isOpen, onResize)
|
||||
|
||||
function collapse() {
|
||||
isOpen.value = !isOpen.value && !props.disabled
|
||||
}
|
||||
|
||||
// 下拉框位置
|
||||
const dropdownPosition = ref({ x: 0, y: 0, w: 0, z: 0 })
|
||||
// 下拉框位置
|
||||
const dropdownStyle = computed<CSSProperties>(() => {
|
||||
if (dropdownPosition.value.z == 0) {
|
||||
return {
|
||||
left: `${dropdownPosition.value.x}px`,
|
||||
top: `${dropdownPosition.value.y}px`,
|
||||
width: `${dropdownPosition.value.w}px`
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
left: `${dropdownPosition.value.x}px`,
|
||||
bottom: `${dropdownPosition.value.z}px`,
|
||||
width: `${dropdownPosition.value.w}px`
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
function onResize() {
|
||||
if (triggerRef.value) {
|
||||
const { width, height, left, top } = triggerRef.value.getBoundingClientRect()
|
||||
if (window.innerHeight - top > 160) {
|
||||
dropdownPosition.value = {
|
||||
w: width,
|
||||
x: left,
|
||||
y: top + height + 2,
|
||||
z: 0
|
||||
}
|
||||
} else {
|
||||
dropdownPosition.value = {
|
||||
w: width,
|
||||
x: left,
|
||||
z: window.innerHeight - top + 2,
|
||||
y: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
window.addEventListener("resize", onResize)
|
||||
window.addEventListener("scroll", onResize)
|
||||
|
||||
onDeactivated(() => {
|
||||
window.removeEventListener("resize", onResize)
|
||||
window.removeEventListener("scroll", onResize)
|
||||
})
|
||||
|
||||
const selectItem = ref<ICascaderItem>()
|
||||
|
||||
function onSelect(item: ICascaderItem) {
|
||||
if (!item.children?.length) {
|
||||
isOpen.value = false
|
||||
}
|
||||
selectItem.value = item
|
||||
emit("select", item.value)
|
||||
}
|
||||
|
||||
function loadNode(item: ICascaderItem) {
|
||||
const value = props.modelValue ?? props.defaultValue
|
||||
if ((selectItem.value == null || selectItem.value == undefined) && value == item.value) {
|
||||
selectItem.value = item
|
||||
}
|
||||
}
|
||||
|
||||
onClickOutside(triggerRef, () => (isOpen.value = false), { ignore: [dropdownRef] })
|
||||
|
||||
return () => {
|
||||
return (
|
||||
<div class={["min-w-20 w-40 i-cascader"].concat(props.highlight)} onClick={collapse} ref={selectRef}>
|
||||
<div
|
||||
class={{
|
||||
"i-cascader-trigger": true,
|
||||
"disabled": props.disabled
|
||||
}}
|
||||
ref={triggerRef}
|
||||
>
|
||||
<span class="i-cascader-label">{selectItem.value?.label}</span>
|
||||
<calc-button icon="dropdown"></calc-button>
|
||||
</div>
|
||||
<Teleport to="body">
|
||||
<Transition name="dropdown" mode="out-in">
|
||||
<calc-tree
|
||||
{...props}
|
||||
v-model={modelValue.value}
|
||||
class="py-1 i-cascader-dropdown"
|
||||
style={dropdownStyle.value}
|
||||
v-show={isOpen.value}
|
||||
ref={dropdownRef}
|
||||
onLoad={loadNode}
|
||||
onSelect={onSelect}
|
||||
data={props.items}
|
||||
/>
|
||||
</Transition>
|
||||
</Teleport>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
$text-color: #937639;
|
||||
.i-cascader {
|
||||
min-width: 80px;
|
||||
width: 160px;
|
||||
user-select: none;
|
||||
outline: none;
|
||||
height: 16px;
|
||||
min-height: 16px;
|
||||
line-height: 100%;
|
||||
// font-size: 12px;
|
||||
color: $text-color;
|
||||
background-color: rgba(0, 0, 0, 1);
|
||||
border: 1px solid #5b472a;
|
||||
border-radius: 2px;
|
||||
padding: 0;
|
||||
padding-right: 3px;
|
||||
margin: 0;
|
||||
display: block;
|
||||
|
||||
.i-cascader-trigger {
|
||||
padding: 0;
|
||||
display: flex;
|
||||
height: 100%;
|
||||
opacity: 0.9;
|
||||
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.i-cascader-label {
|
||||
padding-left: 5px;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
max-width: calc(100% - 20px);
|
||||
}
|
||||
|
||||
.i-cascader-down-icon {
|
||||
background-image: url("./img/select_down.png");
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
border-radius: 2px;
|
||||
width: 15px;
|
||||
height: 14px;
|
||||
}
|
||||
|
||||
.i-cascader-down-icon:active {
|
||||
background-image: url("./img/select_down_clicked.png");
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
border-radius: 2px;
|
||||
width: 15px;
|
||||
height: 14px;
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
color: gray;
|
||||
|
||||
.i-cascader-down-icon {
|
||||
background-image: url("./img/select_down_disabled.png");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.i-cascader-trigger:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.remind {
|
||||
color: #32e432 !important;
|
||||
border: 1px solid #aa8651;
|
||||
}
|
||||
|
||||
.i-cascader-dropdown {
|
||||
position: fixed;
|
||||
max-height: 160px;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
background: black;
|
||||
// font-size: 12px;
|
||||
z-index: 888;
|
||||
|
||||
$hoverColor: #002947;
|
||||
$activeColor: lighten($hoverColor, 5%);
|
||||
color: $text-color;
|
||||
}
|
||||
|
||||
.dropdown-active {
|
||||
animation: dropdown-enter 0.2s ease-in;
|
||||
}
|
||||
|
||||
.dropdown-active {
|
||||
animation: dropdown-leave 0.2s ease-in;
|
||||
}
|
||||
|
||||
.dropdown-enter-active {
|
||||
animation: dropdown-enter 0.2s ease-in;
|
||||
}
|
||||
|
||||
.dropdown-leave-active {
|
||||
animation: dropdown-leave 0.2s ease-in;
|
||||
}
|
||||
|
||||
.dropdown-enter-active {
|
||||
animation: dropdown-enter 0.2s ease-in;
|
||||
}
|
||||
|
||||
.dropdown-leave-active {
|
||||
animation: dropdown-leave 0.2s ease-in;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,73 @@
|
||||
<script lang="tsx">
|
||||
import { defineComponent, provide } from "vue"
|
||||
import { selectionListEmits, selectionListProps, useSelectionList } from "hoci"
|
||||
import { CHECKBOX_GROUP } from "../shared"
|
||||
|
||||
export default defineComponent({
|
||||
name: "ICheckboxGroup",
|
||||
components: {},
|
||||
props: {
|
||||
...selectionListProps,
|
||||
activeClass: {
|
||||
type: String,
|
||||
default: "checked"
|
||||
},
|
||||
unactiveClass: {
|
||||
type: String,
|
||||
default: "unchecked"
|
||||
},
|
||||
multiple: {
|
||||
type: [Boolean, Number],
|
||||
default: true
|
||||
}
|
||||
},
|
||||
emits: [...selectionListEmits],
|
||||
|
||||
setup(props, context) {
|
||||
provide(CHECKBOX_GROUP, true)
|
||||
|
||||
const { render } = useSelectionList(props, context)
|
||||
|
||||
return () => render()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.i-checkbox {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
&.checked {
|
||||
.i-checkbox-icon {
|
||||
background-image: url("@/assets/img/control/checkbox_checked.png");
|
||||
}
|
||||
}
|
||||
|
||||
.i-checkbox-icon {
|
||||
width: 12px;
|
||||
min-width: 12px;
|
||||
height: 12px;
|
||||
margin-right: 4px;
|
||||
background-image: url("@/assets/img/control/checkbox_uncheck.png");
|
||||
}
|
||||
|
||||
.i-checkbox-icon-disable {
|
||||
width: 12px;
|
||||
min-width: 12px;
|
||||
height: 12px;
|
||||
margin-right: 4px;
|
||||
background-image: url("@/assets/img/control/checkbox_disable.png");
|
||||
}
|
||||
|
||||
&:hover:not(.checked) {
|
||||
.i-checkbox-icon {
|
||||
background-image: url("@/assets/img/control/checkbox_hover.png");
|
||||
}
|
||||
}
|
||||
|
||||
.i-checkbox-label {
|
||||
line-height: 24px;
|
||||
color: #937639;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,211 @@
|
||||
<script lang="tsx">
|
||||
import type { EmitsOptions, SetupContext } from "vue";
|
||||
import { computed, defineComponent, inject, nextTick, renderSlot } from "vue"
|
||||
import { selectionItemProps, switchEmits, switchProps, useSelectionItem, useSwitch } from "hoci"
|
||||
import { CHECKBOX_GROUP } from "../shared"
|
||||
|
||||
export default defineComponent({
|
||||
name: "ICheckbox",
|
||||
components: {},
|
||||
props: {
|
||||
...switchProps,
|
||||
...selectionItemProps,
|
||||
label: {
|
||||
type: String
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
round: {
|
||||
type: Boolean,
|
||||
default: () => false
|
||||
}
|
||||
},
|
||||
emits: [...switchEmits],
|
||||
|
||||
setup(props, context) {
|
||||
const inGroup = computed(() => {
|
||||
return inject(CHECKBOX_GROUP, false)
|
||||
})
|
||||
|
||||
const { toggle, switchClass } = useSwitch(
|
||||
() => ({
|
||||
...props,
|
||||
class: props.round ? "i-switch text-xs h-auto cursor-pointer" : "i-checkbox text-xs h-auto cursor-pointer",
|
||||
activeClass: "checked"
|
||||
}),
|
||||
context
|
||||
)
|
||||
|
||||
const { activate, itemClass } = useSelectionItem(props, context as SetupContext<EmitsOptions>)
|
||||
|
||||
const baseClass = computed(() => (props.round ? "i-switch" : "i-checkbox"))
|
||||
|
||||
const { slots } = context
|
||||
|
||||
async function onClick() {
|
||||
if (inGroup.value) {
|
||||
activate()
|
||||
} else {
|
||||
toggle()
|
||||
}
|
||||
await nextTick()
|
||||
// console.log(props.modelValue)
|
||||
}
|
||||
|
||||
const className = computed(() => {
|
||||
return inGroup.value ? itemClass.value : switchClass.value
|
||||
})
|
||||
|
||||
return () => (
|
||||
<div onClick={onClick} class={["flex", baseClass.value, className.value]}>
|
||||
<span class={!props.disabled ? "i-checkbox-icon" : "i-checkbox-icon-disable"}></span>
|
||||
<div class={["i-checkbox-label h-auto"]}>{props.label ?? renderSlot(slots, "default")}</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.i-checkbox {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: baseline;
|
||||
&.checked {
|
||||
.i-checkbox-icon {
|
||||
background-image: url("@/assets/img/control/checkbox_checked.png");
|
||||
}
|
||||
}
|
||||
|
||||
.i-checkbox-icon {
|
||||
width: 12px;
|
||||
min-width: 12px;
|
||||
height: 12px;
|
||||
margin-right: 4px;
|
||||
background-image: url("@/assets/img/control/checkbox_uncheck.png");
|
||||
}
|
||||
|
||||
.i-checkbox-icon-disable {
|
||||
width: 12px;
|
||||
min-width: 12px;
|
||||
height: 12px;
|
||||
margin-right: 4px;
|
||||
background-image: url("@/assets/img/control/checkbox_disable.png");
|
||||
}
|
||||
|
||||
&:hover:not(.checked) {
|
||||
.i-checkbox-icon {
|
||||
background-image: url("@/assets/img/control/checkbox_hover.png");
|
||||
}
|
||||
}
|
||||
|
||||
.i-checkbox-label {
|
||||
line-height: 24px;
|
||||
color: #937639;
|
||||
}
|
||||
}
|
||||
|
||||
.i-switch {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
// justify-content: center;
|
||||
|
||||
&.checked {
|
||||
.i-checkbox-icon {
|
||||
background-image: linear-gradient(to right, #8f6e29, #8f6e29), linear-gradient(rgb(110, 89, 61), rgb(60, 46, 30));
|
||||
}
|
||||
|
||||
.i-checkbox-icon::after {
|
||||
left: 100%;
|
||||
margin-left: -14.8px;
|
||||
}
|
||||
}
|
||||
|
||||
.i-checkbox-icon {
|
||||
margin: 0;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
width: 30px;
|
||||
height: 18px;
|
||||
|
||||
outline: none;
|
||||
border-radius: 10px;
|
||||
box-sizing: border-box;
|
||||
background: #dcdfe6;
|
||||
cursor: pointer;
|
||||
transition: border-color 0.3s, background-color 0.3s;
|
||||
vertical-align: middle;
|
||||
// border-radius: 16px;
|
||||
border: 1.5px solid transparent;
|
||||
background-clip: padding-box, border-box;
|
||||
background-origin: padding-box, border-box;
|
||||
background-image: linear-gradient(to right, #141414, #141414), linear-gradient(rgb(110, 89, 61), rgb(60, 46, 30));
|
||||
box-shadow: inset 0 0 2px 1.5px black;
|
||||
}
|
||||
|
||||
.i-checkbox-icon::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 1.6px;
|
||||
left: 1px;
|
||||
border-radius: 100%;
|
||||
transition: all 0.3s;
|
||||
width: 13px;
|
||||
height: 13px;
|
||||
outline: 0.8px solid black;
|
||||
background: linear-gradient(rgb(15, 61, 116), rgb(8, 37, 71));
|
||||
box-shadow: inset 0 0 2px 0.5px rgb(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.i-checkbox-icon-disable {
|
||||
margin: 0;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
width: 30px;
|
||||
height: 18px;
|
||||
|
||||
outline: none;
|
||||
border-radius: 10px;
|
||||
box-sizing: border-box;
|
||||
background: #dcdfe6;
|
||||
cursor: pointer;
|
||||
transition: border-color 0.3s, background-color 0.3s;
|
||||
vertical-align: middle;
|
||||
// border-radius: 16px;
|
||||
border: 1.5px solid transparent;
|
||||
background-clip: padding-box, border-box;
|
||||
background-origin: padding-box, border-box;
|
||||
background-image: linear-gradient(to right, #dcdfe6, #dcdfe6), linear-gradient(rgb(110, 89, 61), rgb(60, 46, 30));
|
||||
box-shadow: inset 0 0 2px 1.5px black;
|
||||
}
|
||||
|
||||
.i-checkbox-icon-disable::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 1.6px;
|
||||
left: 1px;
|
||||
border-radius: 100%;
|
||||
transition: all 0.3s;
|
||||
width: 13px;
|
||||
height: 13px;
|
||||
// outline: 0.8px solid black;
|
||||
background: white;
|
||||
// box-shadow: inset 0 0 2px 0.5px rgb(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.i-checkbox-icon::after {
|
||||
background: linear-gradient(#15549f, #0e3c72);
|
||||
}
|
||||
}
|
||||
|
||||
.i-checkbox-label {
|
||||
line-height: 18px;
|
||||
height: 18px;
|
||||
padding-left: 2px;
|
||||
color: #937639;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,33 @@
|
||||
<script lang="tsx">
|
||||
import { useToggle, useVModel } from "@vueuse/core"
|
||||
import { defineComponent, renderSlot } from "vue"
|
||||
|
||||
export default defineComponent({
|
||||
name: "ICollapse",
|
||||
props: {
|
||||
title: {
|
||||
type: String
|
||||
},
|
||||
modelValue: {
|
||||
type: Boolean,
|
||||
default: () => true
|
||||
}
|
||||
},
|
||||
setup(props, { slots, emit }) {
|
||||
const modelValue = useVModel(props, "modelValue", emit, { passive: true })
|
||||
|
||||
const toggle = useToggle(modelValue)
|
||||
|
||||
return () => {
|
||||
return (
|
||||
<div class="rounded h-auto bg-hex-00000078 border-1 border-hex-ffffff28 my-1 overflow-hidden">
|
||||
<div onClick={() => toggle()} v-text={props.title} class="bg-gradient-to-b flex from-hex-244281 to-hex-122438 h-6 text-xs text-center w-full items-center justify-center"></div>
|
||||
<div class={["transition-all", "ease-out-in", "h-auto"].concat(modelValue.value ? ["max-h-200"] : ["max-h-0"])}>
|
||||
<div class="p-2">{renderSlot(slots, "default")}</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,170 @@
|
||||
<script lang="tsx">
|
||||
import { onClickOutside, onKeyDown, syncRef, useDraggable, useVModel } from "@vueuse/core"
|
||||
import type { PropType, RendererElement } from "vue"
|
||||
import { Teleport, Transition, computed, defineComponent, provide, ref, renderSlot, watch } from "vue"
|
||||
import { provideShow } from "@/components/hooks/show"
|
||||
|
||||
export default defineComponent({
|
||||
name: "IDialog",
|
||||
props: {
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: () => false
|
||||
},
|
||||
class: {
|
||||
type: String
|
||||
},
|
||||
mask: {
|
||||
type: Boolean,
|
||||
default: () => false
|
||||
},
|
||||
// 模态框
|
||||
modal: {
|
||||
type: Boolean,
|
||||
default: () => false
|
||||
},
|
||||
header: {
|
||||
type: [String, Boolean],
|
||||
default: () => "提示"
|
||||
},
|
||||
closeOnEsc: {
|
||||
type: Boolean,
|
||||
default: () => true
|
||||
},
|
||||
closeButton: {
|
||||
type: Boolean,
|
||||
default: () => true
|
||||
},
|
||||
cache: {
|
||||
// 维持窗口状态
|
||||
type: Boolean,
|
||||
default: () => true
|
||||
},
|
||||
lazy: {
|
||||
type: Boolean,
|
||||
default: () => false
|
||||
},
|
||||
drag: {
|
||||
type: String as PropType<"header" | "all" | "none">,
|
||||
default: () => "header"
|
||||
},
|
||||
appendTo: {
|
||||
type: [Object, String] as PropType<string | RendererElement | null | undefined>,
|
||||
default: "body"
|
||||
},
|
||||
scale: {
|
||||
type: Number,
|
||||
default: 1
|
||||
}
|
||||
},
|
||||
emits: ["close", "update:visible"],
|
||||
setup(props, { emit, slots }) {
|
||||
const dialogRef = ref<HTMLElement | null>(null)
|
||||
const headerRef = ref<HTMLElement | null>(null)
|
||||
|
||||
const visible = ref(props.visible)
|
||||
|
||||
provideShow(visible)
|
||||
|
||||
provide("dialogRef", dialogRef)
|
||||
|
||||
const loaded = ref(!props.lazy)
|
||||
|
||||
const stopLoadWatch = watch(visible, val => {
|
||||
if (val) {
|
||||
loaded.value = true
|
||||
stopLoadWatch()
|
||||
}
|
||||
})
|
||||
|
||||
syncRef(useVModel(props, "visible"), visible, { direction: "both" })
|
||||
|
||||
onClickOutside(dialogRef, () => {
|
||||
if (props.modal) {
|
||||
close()
|
||||
}
|
||||
})
|
||||
|
||||
function close() {
|
||||
visible.value = false
|
||||
emit("close")
|
||||
}
|
||||
|
||||
onKeyDown(
|
||||
"Escape",
|
||||
() => {
|
||||
if (visible.value && props.closeOnEsc) {
|
||||
close()
|
||||
}
|
||||
},
|
||||
{ target: document }
|
||||
)
|
||||
|
||||
const init = ref(true)
|
||||
|
||||
const { x, y, style } = useDraggable(
|
||||
computed(() => {
|
||||
switch (props.drag) {
|
||||
case "header":
|
||||
return headerRef.value
|
||||
case "all":
|
||||
return dialogRef.value
|
||||
case "none":
|
||||
default:
|
||||
return null
|
||||
}
|
||||
}),
|
||||
{
|
||||
initialValue: { x: 0, y: 0 }
|
||||
}
|
||||
)
|
||||
|
||||
const isFixed = computed(() => x.value + y.value > 0)
|
||||
|
||||
const appendToRef = computed(() => {
|
||||
return props.appendTo || "body"
|
||||
})
|
||||
|
||||
return () => {
|
||||
return (
|
||||
<Teleport to={appendToRef.value}>
|
||||
<Transition name="dialog" mode="out-in">
|
||||
{loaded.value && (props.cache || visible.value) && (
|
||||
<div v-show={visible.value} class={["dialog-mask w-full h-full fixed top-0 left-0 z-999 flex justify-center items-center "].concat(props.mask ? "bg-hex-000 bg-opacity-66" : "")} style={`transform: scale(${props.scale});${x.value == 0 && y.value == 0 ?"transform-origin:center":"transform-origin:left top"}`}>
|
||||
<div ref={dialogRef} class={["h-auto border-1 border-hex-2b2b2b border-solid shadow-sm round-1 dialog min-w-48", isFixed.value ? "fixed" : "", props.class]} style={`top:${y.value/props.scale}px;left:${x.value/props.scale}px`}>
|
||||
{props.header && (
|
||||
<div ref={headerRef} class="flex h-4 px-1 leading-4 z-9999 justify-center relative app-header py-2px">
|
||||
<div class="text-xs text-shadow ">{props.header}</div>
|
||||
{props.closeButton && (
|
||||
<div class="flex top-0 right-0 bottom-0 items-center absolute">
|
||||
<div onClick={close} class="cursor-pointer flex h-4 text-center w-4 items-center close-icon"></div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<div class="bg-hex-000 bg-opacity-80 text-white text-xs">{renderSlot(slots, "default")}</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</Transition>
|
||||
</Teleport>
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.dialog-enter-active {
|
||||
animation: fade-in 400ms;
|
||||
.dialog {
|
||||
animation: zoom-in 400ms;
|
||||
}
|
||||
}
|
||||
.dialog-leave-active {
|
||||
animation: fade-in reverse 400ms;
|
||||
.dialog {
|
||||
animation: zoom-out 400ms;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,242 @@
|
||||
<script lang="tsx">
|
||||
import { selectionListEmits, selectionListProps, useSelectionList } from "hoci"
|
||||
import type { CSSProperties, PropType, RendererElement } from "vue";
|
||||
import { Teleport, Transition, computed, defineComponent, onDeactivated, ref, renderSlot, watch } from "vue"
|
||||
|
||||
import { onClickOutside } from "@vueuse/core"
|
||||
|
||||
export default defineComponent({
|
||||
name: "IIconselect",
|
||||
props: {
|
||||
...selectionListProps,
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
width: {
|
||||
type: Number,
|
||||
default: 28
|
||||
},
|
||||
emptyLabel: {
|
||||
type: String
|
||||
},
|
||||
columnNum: {
|
||||
type: Number,
|
||||
default: 1
|
||||
},
|
||||
appendTo: {
|
||||
type: [Object, String] as PropType<string | RendererElement | null | undefined>,
|
||||
default: "body"
|
||||
}
|
||||
},
|
||||
emits: selectionListEmits,
|
||||
setup(props, context) {
|
||||
// props.itemClass = "i-select-dropdown-item"
|
||||
// const { active } = useSelectionList({ ...props, itemClass: "i-select-dropdown-item" }, context)
|
||||
const { renderItem } = useSelectionList(() => {
|
||||
return {
|
||||
...props,
|
||||
itemClass: "i-option",
|
||||
activeClass: "active",
|
||||
unactiveClass: "unactive"
|
||||
}
|
||||
}, context)
|
||||
|
||||
// const { active } = useSelectionList(props, context)
|
||||
const columnNum = computed(() => props.columnNum || 1)
|
||||
// console.log({ ...props })
|
||||
|
||||
const isOpen = ref(false)
|
||||
const triggerRef = ref<HTMLElement>()
|
||||
|
||||
watch(isOpen, onResize)
|
||||
|
||||
const appendToRef = computed(() => {
|
||||
return props.appendTo || "body"
|
||||
})
|
||||
|
||||
function collapse() {
|
||||
isOpen.value = !isOpen.value && !props.disabled
|
||||
}
|
||||
|
||||
// 下拉框位置
|
||||
const dropdownPosition = ref({ x: 0, y: 0, w: 0 })
|
||||
// 下拉框位置
|
||||
const dropdownStyle = computed<CSSProperties>(() => {
|
||||
return {
|
||||
left: `${dropdownPosition.value.x}px`,
|
||||
top: `${dropdownPosition.value.y}px`,
|
||||
width: `${columnNum.value * 32 + 11}px`
|
||||
}
|
||||
})
|
||||
|
||||
function onResize() {
|
||||
if (triggerRef.value) {
|
||||
const { width, height, left, top } = triggerRef.value.getBoundingClientRect()
|
||||
dropdownPosition.value = {
|
||||
w: width,
|
||||
x: left - 2,
|
||||
y: top + height + 2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onClickOutside(triggerRef, () => (isOpen.value = false))
|
||||
|
||||
window.addEventListener("resize", onResize)
|
||||
window.addEventListener("scroll", onResize)
|
||||
|
||||
onDeactivated(() => {
|
||||
window.removeEventListener("resize", onResize)
|
||||
window.removeEventListener("scroll", onResize)
|
||||
})
|
||||
|
||||
const { slots } = context
|
||||
// console.log(active.value?.render())
|
||||
// console.log(active.value)
|
||||
// console.log(active.value?.render() ?? props.emptyLabel)
|
||||
|
||||
return () => {
|
||||
return (
|
||||
<div class="i-icon-select" onClick={collapse}>
|
||||
<div
|
||||
class={{
|
||||
"i-select-trigger": true,
|
||||
"disabled": props.disabled
|
||||
}}
|
||||
ref={triggerRef}
|
||||
>
|
||||
<span class="i-select-label">{renderItem() ?? props.emptyLabel}</span>
|
||||
</div>
|
||||
<Teleport to={appendToRef.value}>
|
||||
<Transition name="dropdown" mode="out-in">
|
||||
<div class="i-icon-select-dropdown" style={dropdownStyle.value} v-show={isOpen.value}>
|
||||
{renderSlot(slots, "default")}
|
||||
</div>
|
||||
</Transition>
|
||||
</Teleport>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
$text-color: #e9c556;
|
||||
.i-icon-select {
|
||||
width: 28px;
|
||||
min-width: 28px;
|
||||
user-select: none;
|
||||
outline: none;
|
||||
height: 28px;
|
||||
line-height: 100%;
|
||||
// font-size: 12px;
|
||||
color: $text-color;
|
||||
background-color: rgba(0, 0, 0, 1);
|
||||
border: 1px solid #5b472a;
|
||||
border-radius: 2px;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
display: block;
|
||||
|
||||
.i-select-trigger {
|
||||
padding: 0;
|
||||
display: flex;
|
||||
height: 100%;
|
||||
opacity: 1;
|
||||
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
&.disabled {
|
||||
color: gray;
|
||||
}
|
||||
}
|
||||
|
||||
.i-select-trigger:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.i-icon-select-dropdown {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
position: fixed;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
max-height: 480px;
|
||||
// background: rgba(255, 255, 255, 0.1);
|
||||
// font-size: 12px;
|
||||
z-index: 888;
|
||||
min-height: 28px;
|
||||
|
||||
$hoverColor: #002947;
|
||||
$activeColor: lighten($hoverColor, 5%);
|
||||
color: $text-color;
|
||||
|
||||
.i-option {
|
||||
// font-size: 12px;
|
||||
height: 28px;
|
||||
line-height: 28px;
|
||||
margin: 1px;
|
||||
border: none;
|
||||
outline: none;
|
||||
appearance: none;
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
border: gray 1px solid;
|
||||
|
||||
// &.active {
|
||||
// background-color: $activeColor;
|
||||
// }
|
||||
|
||||
&.active::before {
|
||||
content: "";
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
position: absolute;
|
||||
background-image: url("/src/assets/img/control/item_checked.png");
|
||||
background-size: 80% 80%;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
z-index: 6;
|
||||
}
|
||||
|
||||
&:hover:not(.active) ::before {
|
||||
content: "";
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
position: absolute;
|
||||
background-image: url("@/assets/img/control/item_hover.png");
|
||||
background-size: 100% 100%;
|
||||
z-index: 6;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-active {
|
||||
animation: dropdown-enter 0.2s ease-in;
|
||||
}
|
||||
|
||||
.dropdown-active {
|
||||
animation: dropdown-leave 0.2s ease-in;
|
||||
}
|
||||
|
||||
.dropdown-enter-active {
|
||||
animation: dropdown-enter 0.2s ease-in;
|
||||
}
|
||||
|
||||
.dropdown-leave-active {
|
||||
animation: dropdown-leave 0.2s ease-in;
|
||||
}
|
||||
|
||||
.dropdown-enter-active {
|
||||
animation: dropdown-enter 0.2s ease-in;
|
||||
}
|
||||
|
||||
.dropdown-leave-active {
|
||||
animation: dropdown-leave 0.2s ease-in;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,39 @@
|
||||
import type { App } from "vue"
|
||||
import Button from "./button/index.vue"
|
||||
import Tabs from "./tabs/index.vue"
|
||||
import Tab from "./tab.vue"
|
||||
import Select from "./select/index.vue"
|
||||
import Option from "./option/index.vue"
|
||||
import Menu from "./menu/menu.vue"
|
||||
import Checkbox from "./checkbox/index.vue"
|
||||
import Collapse from "./collapse/index.vue"
|
||||
import Dialog from "./dialog/index.vue"
|
||||
import Tooltip from "./tooltip/index.vue"
|
||||
import Iconselct from "./iconselct/index.vue"
|
||||
import Selection from "./selection/index.vue"
|
||||
|
||||
|
||||
export const components = {
|
||||
Button,
|
||||
Tabs,
|
||||
Option,
|
||||
Tab,
|
||||
Select,
|
||||
Menu,
|
||||
Checkbox,
|
||||
Collapse,
|
||||
Dialog,
|
||||
Tooltip,
|
||||
Iconselct,
|
||||
Selection
|
||||
}
|
||||
|
||||
export type CalcComponents = typeof components
|
||||
|
||||
export function install(app: App) {
|
||||
for (const [key, value] of Object.entries(components)) {
|
||||
app.component(`calc-${key.toLocaleLowerCase()}`, value)
|
||||
}
|
||||
}
|
||||
|
||||
export default components
|
||||
@@ -0,0 +1,44 @@
|
||||
<script lang="tsx">
|
||||
import { useVModel } from "@vueuse/core"
|
||||
import { defineComponent } from "vue"
|
||||
|
||||
export default defineComponent({
|
||||
name: "IInput",
|
||||
props: {
|
||||
modelValue: {
|
||||
type: [String, Number],
|
||||
default: ""
|
||||
}
|
||||
},
|
||||
setup(props) {
|
||||
const modelValue = useVModel(props, "modelValue")
|
||||
return () => <input v-model={modelValue.value} class="i-input" />
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.i-input {
|
||||
min-width: 80px;
|
||||
width: 178px;
|
||||
user-select: none;
|
||||
outline: none;
|
||||
height: 20px;
|
||||
min-height: 16px;
|
||||
line-height: 100%;
|
||||
// font-size: 12px;
|
||||
color: #937639;
|
||||
background-color: rgba(0, 0, 0, 1);
|
||||
border: 1px solid #5b472a;
|
||||
border-radius: 2px;
|
||||
margin: 0;
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
input::-webkit-outer-spin-button,
|
||||
input::-webkit-inner-spin-button {
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
margin: 0;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,4 @@
|
||||
<script lang="tsx">
|
||||
import { HiItem } from "hoci"
|
||||
export default HiItem
|
||||
</script>
|
||||
@@ -0,0 +1,69 @@
|
||||
<script lang="tsx">
|
||||
import { computed, defineComponent, renderSlot } from "vue"
|
||||
import { classPropType } from "@/components/hooks/types"
|
||||
export default defineComponent({
|
||||
props: {
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: () => false
|
||||
},
|
||||
class: {
|
||||
type: classPropType,
|
||||
default: () => ""
|
||||
}
|
||||
},
|
||||
setup(props, { slots }) {
|
||||
const loading = computed(() => props.loading)
|
||||
return () => {
|
||||
return (
|
||||
<div class="relative">
|
||||
<div v-show={loading.value} class="flex h-full w-full z-99 absolute items-center justify-center">
|
||||
<svg
|
||||
class="i-loading-spin"
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
width="200px"
|
||||
height="200px"
|
||||
viewBox="0 0 200 200"
|
||||
xml:space="preserve"
|
||||
>
|
||||
<g>
|
||||
<linearGradient id="right" gradientUnits="userSpaceOnUse" x1="150" y1="20" x2="150" y2="180">
|
||||
<stop offset="0" style="stop-color:transparent" />
|
||||
<stop offset="1" style="stop-color:currentColor" />
|
||||
</linearGradient>
|
||||
<path fill="url(#right)" d="M100,0v20c44.1,0,80,35.9,80,80c0,44.1-35.9,80-80,80v20c55.2,0,100-44.8,100-100S155.2,0,100,0z" />
|
||||
<linearGradient id="left" gradientUnits="userSpaceOnUse" x1="50" y1="0" x2="50" y2="180">
|
||||
<stop offset="0" style="stop-color:currentColor" />
|
||||
<stop offset="1" style="stop-color:transparent" />
|
||||
</linearGradient>
|
||||
<path fill="url(#left)" d="M20,100c0-44.1,35.9-80,80-80V0C44.8,0,0,44.8,0,100s44.8,100,100,100v-20C55.9,180,20,144.1,20,100z" />
|
||||
<circle fill="currentColor" cx="100" cy="10" r="10" />
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
<div class={[props.class].concat("duration-200").concat(loading.value ? "opacity-0" : "")}>{renderSlot(slots, "default", {}, () => [])}</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.i-loading-spin {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
animation: spin infinite 1s linear;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1639282812376" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2304" width="16" height="16" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><style type="text/css"></style></defs><path d="M85.312 85.312v853.376h853.376V85.312H85.312zM0 0h1024v1024H0V0z m554.624 213.312v256h256v85.376h-256v256H469.312v-256h-256V469.376h256v-256h85.312z" fill="currentColor" p-id="2305"></path></svg>
|
||||
|
After Width: | Height: | Size: 557 B |
@@ -0,0 +1,71 @@
|
||||
<script lang="tsx">
|
||||
import { useToggle } from "@vueuse/core"
|
||||
import { defineComponent, ref, renderSlot } from "vue"
|
||||
|
||||
export default defineComponent({
|
||||
name: "CalcMenu",
|
||||
props: ["label"],
|
||||
setup(props, { slots }) {
|
||||
const expand = ref(false)
|
||||
|
||||
const toggle = useToggle(expand)
|
||||
|
||||
return () => {
|
||||
return (
|
||||
<div class="i-menu">
|
||||
<div class="i-menu-content">
|
||||
<div onClick={() => toggle()} class="i-menu-expand-icon">
|
||||
<svg viewBox="0 0 1024 1024" width="100%" height="100%">
|
||||
<path
|
||||
d="M85.312 85.312v853.376h853.376V85.312H85.312zM0 0h1024v1024H0V0z m554.624 213.312v256h256v85.376h-256v256H469.312v-256h-256V469.376h256v-256h85.312z"
|
||||
fill="currentColor"
|
||||
></path>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="i-menu-label" v-trans={props.label}></div>
|
||||
</div>
|
||||
<div class={{ "i-menu-items": true, "expand": expand.value }}>{renderSlot(slots, "default")}</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.i-menu {
|
||||
min-width: 120px;
|
||||
width: 200px;
|
||||
transition: all 0.3s ease;
|
||||
|
||||
.i-menu-content {
|
||||
width: 100%;
|
||||
height: 16px;
|
||||
line-height: 16px;
|
||||
display: flex;
|
||||
background-color: rgba(37, 37, 37, 0.4);
|
||||
padding: 4px;
|
||||
|
||||
.i-menu-expand-icon {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
color: rgb(255, 247, 148);
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.i-menu-label {
|
||||
width: auto;
|
||||
// font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.i-menu-items {
|
||||
height: 0;
|
||||
overflow: hidden;
|
||||
|
||||
&.expand {
|
||||
height: 32px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,4 @@
|
||||
<script lang="tsx">
|
||||
import { HiItem } from "hoci"
|
||||
export default HiItem
|
||||
</script>
|
||||
@@ -0,0 +1,86 @@
|
||||
<script lang="tsx">
|
||||
import { onKeyStroke } from "@vueuse/core"
|
||||
import type { PropType } from "vue";
|
||||
import { computed, defineComponent, onUnmounted } from "vue"
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
page: {
|
||||
type: Number,
|
||||
default: () => 0
|
||||
},
|
||||
totalPage: {
|
||||
type: Number,
|
||||
default: () => 0
|
||||
},
|
||||
minPage: {
|
||||
type: Number,
|
||||
default: () => 1
|
||||
},
|
||||
parent: {
|
||||
type: Object as PropType<HTMLElement>,
|
||||
default: () => document
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
const totalPage = computed(() => (Number.isNaN(props.totalPage) ? props.minPage : props.totalPage))
|
||||
|
||||
const page = computed({
|
||||
get() {
|
||||
if (Number.isNaN(props.page)) {
|
||||
return props.minPage
|
||||
}
|
||||
return Math.max(Math.min(props.page, totalPage.value), props.minPage)
|
||||
},
|
||||
set(val) {
|
||||
emit("update:page", val)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
function prev(e: Event) {
|
||||
e.preventDefault()
|
||||
if (page.value > 1 && !props.disabled) {
|
||||
page.value--
|
||||
emit("prev")
|
||||
emit("change", page.value)
|
||||
}
|
||||
}
|
||||
|
||||
function next(e: Event) {
|
||||
e.preventDefault()
|
||||
if (page.value < totalPage.value && !props.disabled) {
|
||||
page.value = page.value + 1
|
||||
emit("next")
|
||||
emit("change", page.value)
|
||||
}
|
||||
}
|
||||
|
||||
const removePrev = onKeyStroke(["F1", "PageUp"], prev, { target: props.parent })
|
||||
const removeNext = onKeyStroke(["F2", "PageDown"], next, { target: props.parent })
|
||||
|
||||
onUnmounted(() => {
|
||||
removePrev()
|
||||
removeNext()
|
||||
})
|
||||
|
||||
return () => {
|
||||
return (
|
||||
<div class="flex space-x-1 h-10 w-full items-center justify-center">
|
||||
<calc-button icon="prev" disabled={page.value <= 1 || props.disabled} onClick={prev}></calc-button>
|
||||
<span class="text-center w-8">
|
||||
{page.value}/{totalPage.value}
|
||||
</span>
|
||||
<calc-button icon="next" disabled={page.value >= totalPage.value || props.disabled} onClick={next}></calc-button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss"></style>
|
||||
@@ -0,0 +1,39 @@
|
||||
<script lang="tsx">
|
||||
import { computed, defineComponent } from "vue"
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
value: {
|
||||
type: [Number, String],
|
||||
default: 0
|
||||
},
|
||||
|
||||
total: {
|
||||
type: [Number, String],
|
||||
default: 100
|
||||
}
|
||||
},
|
||||
setup(props) {
|
||||
const progress = computed(() => {
|
||||
if (props.value == 0) {
|
||||
return 0
|
||||
}
|
||||
return (Number(props.value) / Number(props.total)) * 100
|
||||
})
|
||||
|
||||
return () => (
|
||||
<div class="border-solid bg-hex-171717 border-hex-272521 border-2">
|
||||
<div class="border-transparent border-solid border-2">
|
||||
<div style={progress.value == 0 ? "width:0" : `width:${progress.value.toFixed(2)}%`} class="h-2 duration-200 calc-progress"></div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.calc-progress {
|
||||
background: linear-gradient(to bottom, #48d0eb 0%, #6cf4fe 15%, #4fbdfe 30%, #3080fe 40%, #1d5bfe 55%, #3283fd 70%, #000456 100%);
|
||||
}
|
||||
</style>
|
||||
|
After Width: | Height: | Size: 205 B |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 400 B |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 215 B |
@@ -0,0 +1,304 @@
|
||||
<script lang="tsx">
|
||||
import { selectionListEmits, selectionListProps, useSelectionList } from "hoci"
|
||||
import type { CSSProperties, PropType, RendererElement } from "vue";
|
||||
import { Teleport, Transition, computed, defineComponent, onDeactivated, ref, renderList, renderSlot, watch } from "vue"
|
||||
|
||||
import { onClickOutside, useVModel } from "@vueuse/core"
|
||||
import { classPropType, labelPropType } from "@/components/hooks/types"
|
||||
|
||||
export default defineComponent({
|
||||
name: "ISelect",
|
||||
props: {
|
||||
...selectionListProps,
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
width: {
|
||||
type: Number,
|
||||
default: 120
|
||||
},
|
||||
emptyLabel: {
|
||||
type: labelPropType
|
||||
},
|
||||
highlight: {
|
||||
type: String,
|
||||
// warn remind
|
||||
default: ""
|
||||
},
|
||||
labelClass: {
|
||||
type: String,
|
||||
default: ""
|
||||
},
|
||||
inputClass: {
|
||||
type: classPropType,
|
||||
default: ""
|
||||
},
|
||||
appendTo: {
|
||||
type: [Object, String] as PropType<string | RendererElement | null | undefined>,
|
||||
default() {
|
||||
return "body"
|
||||
}
|
||||
}
|
||||
},
|
||||
emits: [...selectionListEmits],
|
||||
setup(props, context) {
|
||||
const modelValue = useVModel(props, "modelValue", context.emit, { passive: true })
|
||||
|
||||
const { renderItem } = useSelectionList(() => {
|
||||
return {
|
||||
...props,
|
||||
modelValue: modelValue.value,
|
||||
itemClass: "i-select-dropdown-item",
|
||||
activeClass: "active",
|
||||
unactiveClass: "unactive"
|
||||
}
|
||||
}, context)
|
||||
|
||||
const isOpen = ref(false)
|
||||
const triggerRef = ref<HTMLElement>()
|
||||
const selectRef = ref<HTMLElement>()
|
||||
const dropdownRef = ref<HTMLElement>()
|
||||
|
||||
watch(isOpen, onResize)
|
||||
|
||||
function collapse() {
|
||||
isOpen.value = !isOpen.value && !props.disabled
|
||||
}
|
||||
|
||||
// 下拉框位置
|
||||
const dropdownPosition = ref({ x: 0, y: 0, w: 0, z: 0 })
|
||||
// 下拉框位置
|
||||
const dropdownStyle = computed<CSSProperties>(() => {
|
||||
if (dropdownPosition.value.z == 0) {
|
||||
return {
|
||||
left: `${dropdownPosition.value.x}px`,
|
||||
top: `${dropdownPosition.value.y}px`,
|
||||
width: `${dropdownPosition.value.w}px`
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
left: `${dropdownPosition.value.x}px`,
|
||||
bottom: `${dropdownPosition.value.z}px`,
|
||||
width: `${dropdownPosition.value.w}px`
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
function onResize() {
|
||||
if (triggerRef.value) {
|
||||
const { width, height, left, top } = triggerRef.value.getBoundingClientRect()
|
||||
if (window.innerHeight - top > 160) {
|
||||
dropdownPosition.value = {
|
||||
w: width,
|
||||
x: left,
|
||||
y: top + height + 2,
|
||||
z: 0
|
||||
}
|
||||
} else {
|
||||
dropdownPosition.value = {
|
||||
w: width,
|
||||
x: left,
|
||||
z: window.innerHeight - top + 2,
|
||||
y: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const appendToRef = computed(() => {
|
||||
return props.appendTo || "body"
|
||||
})
|
||||
|
||||
|
||||
onClickOutside(triggerRef, () => (isOpen.value = false))
|
||||
|
||||
window.addEventListener("resize", onResize)
|
||||
window.addEventListener("scroll", onResize)
|
||||
|
||||
onDeactivated(() => {
|
||||
window.removeEventListener("resize", onResize)
|
||||
window.removeEventListener("scroll", onResize)
|
||||
})
|
||||
|
||||
const { slots } = context
|
||||
|
||||
function renderLabel() {
|
||||
const children = renderItem()
|
||||
if (children) {
|
||||
if (!Array.isArray(children)) {
|
||||
return <span class={["i-select-input"].concat(props.inputClass)}>{children}</span>
|
||||
} else if (children.length > 0) {
|
||||
return (
|
||||
<span class={["i-select-input space-x-1"].concat(props.inputClass)}>
|
||||
{renderList(children, item => (
|
||||
<span>{item}</span>
|
||||
))}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
}
|
||||
const emptyLabel = typeof props.emptyLabel == "function" ? props.emptyLabel() : props.emptyLabel
|
||||
return <span class="w-full pl-2 i-select-input i-select-empty-label">{emptyLabel}</span>
|
||||
}
|
||||
|
||||
return () => {
|
||||
return (
|
||||
<div class={["min-w-20 w-40 i-select "].concat(props.highlight)} onClick={collapse} ref={selectRef}>
|
||||
<div
|
||||
class={{
|
||||
"i-select-trigger": true,
|
||||
"disabled": props.disabled
|
||||
}}
|
||||
ref={triggerRef}
|
||||
>
|
||||
{renderLabel()}
|
||||
<calc-button disabled={props.disabled} icon="dropdown" class="cursor-pointer"></calc-button>
|
||||
</div>
|
||||
<Teleport to={appendToRef.value}>
|
||||
<Transition name="dropdown" mode="out-in">
|
||||
<div class="i-select-dropdown" style={dropdownStyle.value} v-show={isOpen.value} ref={dropdownRef}>
|
||||
{renderSlot(slots, "default")}
|
||||
</div>
|
||||
</Transition>
|
||||
</Teleport>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
$text-color: #937639;
|
||||
.i-select {
|
||||
min-width: 80px;
|
||||
width: 160px;
|
||||
user-select: none;
|
||||
outline: none;
|
||||
height: 16px;
|
||||
min-height: 16px;
|
||||
line-height: 100%;
|
||||
// font-size: 12px;
|
||||
color: $text-color;
|
||||
background-color: rgba(0, 0, 0, 1);
|
||||
border: 1px solid #5b472a;
|
||||
border-radius: 2px;
|
||||
padding: 0;
|
||||
padding-right: 3px;
|
||||
margin: 0;
|
||||
display: block;
|
||||
|
||||
.i-select-trigger {
|
||||
padding: 0;
|
||||
display: flex;
|
||||
height: 100%;
|
||||
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.i-select-input {
|
||||
padding-left: 8px;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
max-width: calc(100% - 20px);
|
||||
}
|
||||
|
||||
.i-select-empty-label {
|
||||
color: #5e5e5e;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.i-select-empty-label {
|
||||
color: #757575;
|
||||
}
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
color: gray;
|
||||
}
|
||||
}
|
||||
|
||||
.i-select-trigger:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.warn {
|
||||
color: red !important;
|
||||
border: 1px solid #aa8651;
|
||||
}
|
||||
|
||||
.remind {
|
||||
color: #32e432 !important;
|
||||
border: 1px solid #aa8651;
|
||||
}
|
||||
|
||||
.i-select-dropdown {
|
||||
position: fixed;
|
||||
max-height: 160px;
|
||||
overflow-y: auto;
|
||||
background: black;
|
||||
// font-size: 12px;
|
||||
z-index: 888;
|
||||
|
||||
$hoverColor: #002947;
|
||||
$activeColor: lighten($hoverColor, 5%);
|
||||
color: $text-color;
|
||||
|
||||
.i-select-dropdown-item {
|
||||
// font-size: 12px;
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
margin: 0;
|
||||
padding: 0 5px;
|
||||
border: none;
|
||||
outline: none;
|
||||
appearance: none;
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
&.active {
|
||||
// background-color: $activeColor;
|
||||
color: #e9c556;
|
||||
}
|
||||
&.active::after {
|
||||
content: "✓";
|
||||
position: absolute;
|
||||
right: 5px;
|
||||
}
|
||||
|
||||
&:hover:not(.active) {
|
||||
// font-size: 12px;
|
||||
background-color: $hoverColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-active {
|
||||
animation: dropdown-enter 0.2s ease-in;
|
||||
}
|
||||
|
||||
.dropdown-active {
|
||||
animation: dropdown-leave 0.2s ease-in;
|
||||
}
|
||||
|
||||
.dropdown-enter-active {
|
||||
animation: dropdown-enter 0.2s ease-in;
|
||||
}
|
||||
|
||||
.dropdown-leave-active {
|
||||
animation: dropdown-leave 0.2s ease-in;
|
||||
}
|
||||
|
||||
.dropdown-enter-active {
|
||||
animation: dropdown-enter 0.2s ease-in;
|
||||
}
|
||||
|
||||
.dropdown-leave-active {
|
||||
animation: dropdown-leave 0.2s ease-in;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,5 @@
|
||||
<script lang="tsx">
|
||||
import { HiSelection } from "hoci"
|
||||
|
||||
export default HiSelection
|
||||
</script>
|
||||
@@ -0,0 +1,3 @@
|
||||
import type { InjectionKey } from "vue";
|
||||
|
||||
export const CHECKBOX_GROUP: InjectionKey<boolean> = Symbol("checkbox-group")
|
||||
@@ -0,0 +1,16 @@
|
||||
<script lang="tsx">
|
||||
import { defineComponent, renderSlot } from "vue"
|
||||
import { HiItem, selectionItemProps } from "hoci"
|
||||
|
||||
export default defineComponent({
|
||||
name: "CalcTab",
|
||||
props: {
|
||||
...selectionItemProps
|
||||
},
|
||||
setup(props, { slots }) {
|
||||
return () => {
|
||||
return <HiItem {...props}>{renderSlot(slots, "default")}</HiItem>
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,5 @@
|
||||
export const AciveSymbol = Symbol("[i-tabs]active")
|
||||
|
||||
export const AciveClassSymbol = Symbol("[i-tabs]active-class")
|
||||
|
||||
export const ChangeActiveSymbol = Symbol("[i-tabs]change-active")
|
||||
@@ -0,0 +1,123 @@
|
||||
<script lang="tsx">
|
||||
import { HiSelection, selectionListProps } from "hoci"
|
||||
import type { PropType } from "vue";
|
||||
import { computed, defineComponent, renderSlot } from "vue"
|
||||
import type { LocationQuery } from "vue-router";
|
||||
import { parseQuery, useRouter } from "vue-router"
|
||||
import type { BaseType } from "@/components/hooks/types"
|
||||
|
||||
export default defineComponent({
|
||||
name: "CalcTabs",
|
||||
props: {
|
||||
...selectionListProps,
|
||||
vertical: {
|
||||
type: Boolean
|
||||
},
|
||||
route: {
|
||||
type: Boolean
|
||||
},
|
||||
query: {
|
||||
type: [Object, String] as PropType<string | LocationQuery>
|
||||
}
|
||||
},
|
||||
setup(props, { emit, slots }) {
|
||||
const router = useRouter()
|
||||
|
||||
const routeQuery = computed(() => {
|
||||
if (typeof props.query == "string") {
|
||||
return parseQuery(props.query)
|
||||
}
|
||||
return props.query ?? {}
|
||||
})
|
||||
|
||||
const modelValue = computed<BaseType>({
|
||||
get() {
|
||||
let val = props.modelValue
|
||||
if (props.route) {
|
||||
val = val ?? router.currentRoute.value.path
|
||||
}
|
||||
return val
|
||||
},
|
||||
set(val) {
|
||||
if (props.route && val) {
|
||||
val = decodeURIComponent(val.toString())
|
||||
router.push({ path: val, query: routeQuery.value })
|
||||
}
|
||||
emit("update:modelValue", val)
|
||||
emit("change", val)
|
||||
}
|
||||
})
|
||||
|
||||
return () => {
|
||||
return (
|
||||
<HiSelection v-model={modelValue.value} class={{ "i-tabs": true, "vertical": !!props.vertical }} activeClass="active" itemClass={["i-tab cursor-pointer"].concat(props.itemClass)}>
|
||||
{renderSlot(slots, "default")}
|
||||
</HiSelection>
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.i-tabs {
|
||||
display: flex;
|
||||
list-style: none;
|
||||
align-items: flex-end;
|
||||
border-bottom: 1px solid #4f4838;
|
||||
width: 100%;
|
||||
|
||||
font-size: 14px;
|
||||
|
||||
color: #c6b083;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: #c6b083;
|
||||
&:visited {
|
||||
color: currentColor;
|
||||
}
|
||||
}
|
||||
|
||||
&.vertical {
|
||||
flex-direction: column;
|
||||
|
||||
.i-tab {
|
||||
border-radius: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.i-tab {
|
||||
// font-size: 12px;
|
||||
margin-left: 1px;
|
||||
|
||||
width: 120px;
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
|
||||
&:visited {
|
||||
color: currentColor;
|
||||
}
|
||||
|
||||
border: #4f4838 1px solid;
|
||||
border-bottom: none;
|
||||
border-radius: 5px 5px 0 0;
|
||||
background: linear-gradient(to top, rgba(6, 6, 6, 0.9), rgba(52, 52, 52, 0.9));
|
||||
transition: all 0.4s ease-in-out;
|
||||
|
||||
&:hover {
|
||||
background: linear-gradient(#4a4b4e, #1d1e22);
|
||||
}
|
||||
|
||||
&.active {
|
||||
background: linear-gradient(#574d38, #25221d);
|
||||
border-image: url("@/assets/img/control/active_top.png") 1 fill stretch;
|
||||
// color: #ffb400;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,25 @@
|
||||
<script lang="tsx">
|
||||
import { defineComponent } from "vue"
|
||||
import { NTooltip } from "@/components/base"
|
||||
|
||||
import { tooltipProps } from "@/components/base/tooltip"
|
||||
|
||||
export default defineComponent({
|
||||
name: "ITooltip",
|
||||
components: { NTooltip },
|
||||
props: {
|
||||
...tooltipProps
|
||||
},
|
||||
setup(props, { slots }) {
|
||||
return () => {
|
||||
return (
|
||||
<n-tooltip {...props} pop-class="i-popper z-999 fixed top-0 left-0 bg-black bg-opacity-90 border-1.5 border-hex-272928 rounded-2px border-solid">
|
||||
{slots}
|
||||
</n-tooltip>
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss"></style>
|
||||
|
After Width: | Height: | Size: 353 B |
|
After Width: | Height: | Size: 332 B |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 280 B |
|
After Width: | Height: | Size: 322 B |
@@ -0,0 +1,92 @@
|
||||
<script lang="tsx">
|
||||
import { HiSelection, selectionListProps } from "hoci"
|
||||
import { useVModel } from "@vueuse/core"
|
||||
import type { PropType } from "vue";
|
||||
import { computed, defineComponent, renderList, watch } from "vue"
|
||||
import CalcTreeNode from "./node.vue"
|
||||
import type { TreeNode } from "./types"
|
||||
import { labelPropType, valuePropType } from "@/components/hooks/types"
|
||||
|
||||
const Tree = defineComponent({
|
||||
name: "CalcTree",
|
||||
props: {
|
||||
...selectionListProps,
|
||||
modelValue: {
|
||||
type: valuePropType,
|
||||
default: () => null
|
||||
},
|
||||
activeItem: {
|
||||
type: Object as PropType<TreeNode>,
|
||||
default: () => null
|
||||
},
|
||||
idKey: {
|
||||
type: String,
|
||||
default: "id"
|
||||
},
|
||||
label: {
|
||||
type: labelPropType
|
||||
},
|
||||
data: {
|
||||
type: Array as PropType<TreeNode[]>,
|
||||
default: () => []
|
||||
},
|
||||
depth: {
|
||||
type: [Number, String] as PropType<number>,
|
||||
default: () => 0
|
||||
},
|
||||
depthStyle: {
|
||||
type: Function as PropType<(depth: number) => string>
|
||||
}
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
const modelValue = useVModel(props, "modelValue")
|
||||
|
||||
function onSelect(item?: TreeNode) {
|
||||
if (item) {
|
||||
emit("select", item)
|
||||
item.onSelect?.()
|
||||
}
|
||||
}
|
||||
|
||||
function loadItem(total: TreeNode[], items: TreeNode[]) {
|
||||
items?.forEach(item => {
|
||||
if (item.children) {
|
||||
loadItem(total, item.children)
|
||||
}
|
||||
total.push(item)
|
||||
})
|
||||
return total
|
||||
}
|
||||
|
||||
const children = computed(() => {
|
||||
return loadItem([], props.data)
|
||||
})
|
||||
|
||||
watch(modelValue, val => {
|
||||
val = val ?? props.defaultValue
|
||||
const item = children.value.find(item => item.value === val)
|
||||
onSelect(item)
|
||||
})
|
||||
|
||||
return () => {
|
||||
return (
|
||||
<HiSelection {...props} activeClass="i-tree-node-active" v-model={modelValue.value} class="i-tree">
|
||||
{renderList(props.data, item => {
|
||||
return <CalcTreeNode {...item} onSelect={onSelect} depth-style={props.depthStyle} depth={props.depth} />
|
||||
})}
|
||||
</HiSelection>
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
export default Tree
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.i-tree {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,159 @@
|
||||
<script lang="tsx">
|
||||
import { HiItem } from "hoci"
|
||||
import type { PropType } from "vue";
|
||||
import { Transition, computed, defineComponent, ref, renderList } from "vue"
|
||||
import type { TreeNode } from "./types"
|
||||
|
||||
const NTreeNode = defineComponent({
|
||||
props: {
|
||||
label: {
|
||||
type: String,
|
||||
default: ""
|
||||
},
|
||||
value: {
|
||||
type: [String, Number, Boolean, Object, Array] as PropType<any>,
|
||||
default: () => null
|
||||
},
|
||||
children: {
|
||||
type: Array as PropType<TreeNode[]>,
|
||||
default: () => []
|
||||
},
|
||||
depth: {
|
||||
type: [String, Number] as PropType<number>,
|
||||
default: 0
|
||||
},
|
||||
depthStyle: {
|
||||
type: Function as PropType<(depth: number) => string>
|
||||
},
|
||||
depthOffset: {
|
||||
type: String,
|
||||
default: "12px"
|
||||
}
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
const isOpen = ref(false)
|
||||
|
||||
function open() {
|
||||
isOpen.value = !isOpen.value
|
||||
onSelect(props)
|
||||
}
|
||||
|
||||
function onSelect(item: TreeNode) {
|
||||
return emit("select", item)
|
||||
}
|
||||
|
||||
const depthStyle = computed(() => {
|
||||
if (props.depthStyle) {
|
||||
return props.depthStyle(props.depth)
|
||||
}
|
||||
return `padding-left: calc(${props.depth}*${props.depthOffset} + 12px);`
|
||||
})
|
||||
|
||||
return () => (
|
||||
<div class="w-full i-tree-item relative">
|
||||
<HiItem label={props.label} value={props.value} style={depthStyle.value} class="flex i-tree-item-label items-center" onClick={open}>
|
||||
<div class="top-0 right-0 bottom-0 left-0 absolute i-tree-item-active-mask"></div>
|
||||
<div class="top-0 right-0 bottom-0 left-0 absolute i-tree-item-mask"></div>
|
||||
<calc-button v-show={props.children?.length} icon={isOpen.value ? "collapse-up" : "collapse-down"}></calc-button>
|
||||
<div class="pl-1">{props.label}</div>
|
||||
</HiItem>
|
||||
<Transition name="dropdown" mode="out-in">
|
||||
<div v-show={isOpen.value}>
|
||||
{renderList(props.children, item => {
|
||||
return <NTreeNode onSelect={onSelect} {...item} depth={Number(props.depth) + 1} />
|
||||
})}
|
||||
</div>
|
||||
</Transition>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
export default NTreeNode
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.i-tree-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
cursor: pointer;
|
||||
|
||||
border: 1px solid transparent;
|
||||
background-color: black;
|
||||
}
|
||||
|
||||
.i-tree-item-label {
|
||||
flex: 1;
|
||||
height: 16px;
|
||||
min-height: 16px;
|
||||
color: #ddc593;
|
||||
position: relative;
|
||||
&:hover {
|
||||
.i-tree-item-mask {
|
||||
background: url("./img/item_hover.png") no-repeat;
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
}
|
||||
|
||||
&.i-tree-node-active {
|
||||
.i-tree-item-active-mask {
|
||||
background: url("./img/item_active.png") no-repeat;
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.icon-collapse-up {
|
||||
background-image: url("./img/collapse_up.png");
|
||||
background-size: 100% 100%;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
min-width: 13px !important;
|
||||
width: 13px !important;
|
||||
height: 13px !important;
|
||||
}
|
||||
|
||||
.icon-collapse-down {
|
||||
background-image: url("./img/collapse_down.png");
|
||||
background-size: 100% 100%;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
min-width: 13px !important;
|
||||
width: 13px !important;
|
||||
height: 13px !important;
|
||||
}
|
||||
|
||||
.i-tree-dropdown {
|
||||
width: 100%;
|
||||
max-height: 0;
|
||||
overflow: hidden;
|
||||
background-color: #fff;
|
||||
border-radius: 0.5rem;
|
||||
box-shadow: 0 0 0.5rem 0 rgba(0, 0, 0, 0.1);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.dropdown-active {
|
||||
animation: dropdown-enter 0.2s ease-in;
|
||||
}
|
||||
|
||||
.dropdown-active {
|
||||
animation: dropdown-leave 0.2s ease-in;
|
||||
}
|
||||
|
||||
.dropdown-enter-active {
|
||||
animation: dropdown-enter 0.2s ease-in;
|
||||
}
|
||||
|
||||
.dropdown-leave-active {
|
||||
animation: dropdown-leave 0.2s ease-in;
|
||||
}
|
||||
|
||||
.dropdown-enter-active {
|
||||
animation: dropdown-enter 0.2s ease-in;
|
||||
}
|
||||
|
||||
.dropdown-leave-active {
|
||||
animation: dropdown-leave 0.2s ease-in;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,10 @@
|
||||
export interface TreeNode {
|
||||
label: string
|
||||
id?: string | number
|
||||
value?: any
|
||||
children?: TreeNode[]
|
||||
onSelect?: () => void
|
||||
[key: string]: any
|
||||
}
|
||||
|
||||
export interface TreeData {}
|
||||
@@ -0,0 +1 @@
|
||||
通用的函数式组件
|
||||
@@ -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
|
||||
}
|
||||
})
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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>
|
||||
@@ -0,0 +1,100 @@
|
||||
import type { MaybeRef } from "@vueuse/core"
|
||||
import querystring from "query-string"
|
||||
import { h, isRef } from "vue"
|
||||
import { useRouter } from "vue-router"
|
||||
import { useDialog } from "@/components/hooks/dialog"
|
||||
|
||||
interface UseOpenOption {
|
||||
/*
|
||||
|
||||
*/
|
||||
width?: number
|
||||
height?: number
|
||||
scale?: number
|
||||
query?: Record<string, string | string[] | undefined>
|
||||
|
||||
/**
|
||||
* 是否打开新页面
|
||||
*/
|
||||
target?: "_blank" | "_self" | false
|
||||
|
||||
title?: string
|
||||
}
|
||||
|
||||
export function useOpenWindow(opt?: UseOpenOption & { immediate?: boolean }) {
|
||||
const { show, close, randomId } = useDialog()
|
||||
const router = useRouter()
|
||||
|
||||
return async (maybeUrl: MaybeRef<string> | (() => string), { width = 0, height = 0, target = "_self", query = {}, title = "DNF计算器 & Colg", scale = 1.0 }: UseOpenOption = {}) => {
|
||||
let url = isRef(maybeUrl) ? maybeUrl.value : typeof maybeUrl === "function" ? maybeUrl() : maybeUrl
|
||||
if (!url) {
|
||||
return
|
||||
}
|
||||
|
||||
if (url.startsWith("/")) {
|
||||
url = router.resolve(url).href
|
||||
}
|
||||
query.r = `${Math.random()}`
|
||||
url = querystring.stringifyUrl({ url, query })
|
||||
try {
|
||||
let _target = target
|
||||
if (!_target) {
|
||||
const rs = await show({
|
||||
content: "请确认打开新页面的方式",
|
||||
okButton: "当前窗口",
|
||||
rejectButton: "新窗口",
|
||||
cancelButton: true,
|
||||
defaultStatus: "cancel"
|
||||
})
|
||||
if (rs.isOk) {
|
||||
_target = "_self"
|
||||
} else if (rs.isReject) {
|
||||
_target = "_blank"
|
||||
}
|
||||
}
|
||||
|
||||
if (_target == "_blank" || width * height < 1) {
|
||||
window.open(url, _target as string)
|
||||
|
||||
} else if (_target == "_self") {
|
||||
|
||||
const id = randomId()
|
||||
const onClose = (e: MessageEvent) => {
|
||||
if (e.data == "close") {
|
||||
close(id)
|
||||
}
|
||||
}
|
||||
window.addEventListener("message", onClose)
|
||||
const temp = await show({
|
||||
id,
|
||||
header: title,
|
||||
mask: true,
|
||||
scale,
|
||||
content() {
|
||||
return h("iframe", {
|
||||
name: Math.random().toString(36).slice(2),
|
||||
referrerpolicy: "no-referrer",
|
||||
frameborder: 0,
|
||||
src: url,
|
||||
style: {
|
||||
width: `${width}px`,
|
||||
height: `${height}px`
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
window.removeEventListener("message", onClose)
|
||||
return temp
|
||||
}
|
||||
} catch (err) {
|
||||
// let routerURL = router?.resolve({
|
||||
// path: url
|
||||
// })
|
||||
// window.open(routerURL?.href)
|
||||
// router.push(url)
|
||||
// 网页端打开
|
||||
router?.push(url)
|
||||
console.error(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@
|
||||
"compilerOptions": {
|
||||
"noEmit": true,
|
||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
|
||||
"jsx": "preserve",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Bundler",
|
||||
"types": ["node"]
|
||||
|
||||
@@ -8,6 +8,7 @@ import Components from 'unplugin-vue-components/vite'
|
||||
import AutoImport from 'unplugin-auto-import/vite'
|
||||
import RekaResolver from 'reka-ui/resolver'
|
||||
import UnoCSS from 'unocss/vite'
|
||||
import uncomponents from "unplugin-vue-components/vite"
|
||||
|
||||
// https://vite.dev/config/
|
||||
export default defineConfig({
|
||||
@@ -16,6 +17,12 @@ export default defineConfig({
|
||||
vueJsx(),
|
||||
// vueDevTools(),
|
||||
UnoCSS(),
|
||||
uncomponents({
|
||||
dts: true,
|
||||
dirs: ["src/components/"],
|
||||
allowOverrides: true,
|
||||
directoryAsNamespace: true
|
||||
}),
|
||||
Components({
|
||||
dts: 'src/types/components.d.ts',
|
||||
resolvers: [RekaResolver()],
|
||||
|
||||
@@ -10,5 +10,9 @@
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC"
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"hoci": "^0.6.0",
|
||||
"query-string": "^9.1.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,773 @@
|
||||
lockfileVersion: '9.0'
|
||||
|
||||
settings:
|
||||
autoInstallPeers: true
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
importers:
|
||||
|
||||
.:
|
||||
dependencies:
|
||||
hoci:
|
||||
specifier: ^0.6.0
|
||||
version: 0.6.0(@babel/parser@7.27.0)(@vueuse/core@9.0.0(vue@3.5.13))(vue@3.5.13)
|
||||
query-string:
|
||||
specifier: ^9.1.1
|
||||
version: 9.1.1
|
||||
|
||||
packages:
|
||||
|
||||
'@antfu/utils@0.7.10':
|
||||
resolution: {integrity: sha512-+562v9k4aI80m1+VuMHehNJWLOFjBnXn3tdOitzD0il5b7smkSBal4+a3oKiQTbrwMmN/TBUMDvbdoWDehgOww==}
|
||||
|
||||
'@babel/helper-string-parser@7.25.9':
|
||||
resolution: {integrity: sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
|
||||
'@babel/helper-validator-identifier@7.25.9':
|
||||
resolution: {integrity: sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
|
||||
'@babel/parser@7.27.0':
|
||||
resolution: {integrity: sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==}
|
||||
engines: {node: '>=6.0.0'}
|
||||
hasBin: true
|
||||
|
||||
'@babel/types@7.27.0':
|
||||
resolution: {integrity: sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
|
||||
'@hoci/components@0.6.0':
|
||||
resolution: {integrity: sha512-FKWh6JmV5iezDGdQq/UOQ6PfJNRzWfjrN6C6s8Q2QoQMJTC3wAQXJjsKP+OsO2tdizYA1NBBwfow4Oi2X6GZcg==}
|
||||
peerDependencies:
|
||||
'@vueuse/core': '>=9.0.0'
|
||||
vue: ^3.0.0-0
|
||||
|
||||
'@hoci/core@0.6.0':
|
||||
resolution: {integrity: sha512-lFV5gFNNzdH5cTmXHonKRaRKt0FDZX9kQ18xwWP89yoDAXrpXbRt5ZK8oQgqCHdWYtwDcVrAMZ+IZmhkCBiO6g==}
|
||||
peerDependencies:
|
||||
vue: ^3.0.0-0
|
||||
|
||||
'@hoci/shared@0.6.0':
|
||||
resolution: {integrity: sha512-XreSiu+AFisDKorgugX8zjNEsv889U6SbbkTLPHlYIcLvSYk33TmwQ5jHpHe1RAE2KE2Xxn9fBqYRlOCAn8ujg==}
|
||||
peerDependencies:
|
||||
'@vueuse/core': '>=9.0.0'
|
||||
vue: ^3.0.0-0
|
||||
|
||||
'@jridgewell/sourcemap-codec@1.5.0':
|
||||
resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==}
|
||||
|
||||
'@nodelib/fs.scandir@2.1.5':
|
||||
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
|
||||
engines: {node: '>= 8'}
|
||||
|
||||
'@nodelib/fs.stat@2.0.5':
|
||||
resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==}
|
||||
engines: {node: '>= 8'}
|
||||
|
||||
'@nodelib/fs.walk@1.2.8':
|
||||
resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
|
||||
engines: {node: '>= 8'}
|
||||
|
||||
'@rollup/pluginutils@5.1.4':
|
||||
resolution: {integrity: sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
peerDependencies:
|
||||
rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0
|
||||
peerDependenciesMeta:
|
||||
rollup:
|
||||
optional: true
|
||||
|
||||
'@types/estree@1.0.7':
|
||||
resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==}
|
||||
|
||||
'@types/web-bluetooth@0.0.15':
|
||||
resolution: {integrity: sha512-w7hEHXnPMEZ+4nGKl/KDRVpxkwYxYExuHOYXyzIzCDzEZ9ZCGMAewulr9IqJu2LR4N37fcnb1XVeuZ09qgOxhA==}
|
||||
|
||||
'@vue/compiler-core@3.5.13':
|
||||
resolution: {integrity: sha512-oOdAkwqUfW1WqpwSYJce06wvt6HljgY3fGeM9NcVA1HaYOij3mZG9Rkysn0OHuyUAGMbEbARIpsG+LPVlBJ5/Q==}
|
||||
|
||||
'@vue/compiler-dom@3.5.13':
|
||||
resolution: {integrity: sha512-ZOJ46sMOKUjO3e94wPdCzQ6P1Lx/vhp2RSvfaab88Ajexs0AHeV0uasYhi99WPaogmBlRHNRuly8xV75cNTMDA==}
|
||||
|
||||
'@vue/compiler-sfc@3.5.13':
|
||||
resolution: {integrity: sha512-6VdaljMpD82w6c2749Zhf5T9u5uLBWKnVue6XWxprDobftnletJ8+oel7sexFfM3qIxNmVE7LSFGTpv6obNyaQ==}
|
||||
|
||||
'@vue/compiler-ssr@3.5.13':
|
||||
resolution: {integrity: sha512-wMH6vrYHxQl/IybKJagqbquvxpWCuVYpoUJfCqFZwa/JY1GdATAQ+TgVtgrwwMZ0D07QhA99rs/EAAWfvG6KpA==}
|
||||
|
||||
'@vue/reactivity@3.5.13':
|
||||
resolution: {integrity: sha512-NaCwtw8o48B9I6L1zl2p41OHo/2Z4wqYGGIK1Khu5T7yxrn+ATOixn/Udn2m+6kZKB/J7cuT9DbWWhRxqixACg==}
|
||||
|
||||
'@vue/runtime-core@3.5.13':
|
||||
resolution: {integrity: sha512-Fj4YRQ3Az0WTZw1sFe+QDb0aXCerigEpw418pw1HBUKFtnQHWzwojaukAs2X/c9DQz4MQ4bsXTGlcpGxU/RCIw==}
|
||||
|
||||
'@vue/runtime-dom@3.5.13':
|
||||
resolution: {integrity: sha512-dLaj94s93NYLqjLiyFzVs9X6dWhTdAlEAciC3Moq7gzAc13VJUdCnjjRurNM6uTLFATRHexHCTu/Xp3eW6yoog==}
|
||||
|
||||
'@vue/server-renderer@3.5.13':
|
||||
resolution: {integrity: sha512-wAi4IRJV/2SAW3htkTlB+dHeRmpTiVIK1OGLWV1yeStVSebSQQOwGwIq0D3ZIoBj2C2qpgz5+vX9iEBkTdk5YA==}
|
||||
peerDependencies:
|
||||
vue: 3.5.13
|
||||
|
||||
'@vue/shared@3.5.13':
|
||||
resolution: {integrity: sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ==}
|
||||
|
||||
'@vueuse/core@9.0.0':
|
||||
resolution: {integrity: sha512-hMMc2ajuVknkL7Z39JdP9gFFND2OgnDTSS5mmuinWGAE1Vxy1AwDvTHm3+juyk+GzJjYRAktnBIPy7Fq53iOnw==}
|
||||
|
||||
'@vueuse/metadata@9.0.0':
|
||||
resolution: {integrity: sha512-79YVIsAP1bbWm5GdQuG7jDVF/9uuExzhkO0Sd4/TLuSfzH2uZOrHvGwy+ZNJHjbyRn3uf56rKINWLJdBuTLSqQ==}
|
||||
|
||||
'@vueuse/shared@9.0.0':
|
||||
resolution: {integrity: sha512-WRCyr/wIz5e/2gR/+qFucbCUcGMyJKkQZAzlECl3e71ebQQ9X/w3aBWT9FbnogJX+DNZ/t3Pj+TqPbC7TH1Yog==}
|
||||
|
||||
acorn@8.14.1:
|
||||
resolution: {integrity: sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==}
|
||||
engines: {node: '>=0.4.0'}
|
||||
hasBin: true
|
||||
|
||||
anymatch@3.1.3:
|
||||
resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
|
||||
engines: {node: '>= 8'}
|
||||
|
||||
balanced-match@1.0.2:
|
||||
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
|
||||
|
||||
binary-extensions@2.3.0:
|
||||
resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
brace-expansion@2.0.1:
|
||||
resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==}
|
||||
|
||||
braces@3.0.3:
|
||||
resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
chokidar@3.6.0:
|
||||
resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==}
|
||||
engines: {node: '>= 8.10.0'}
|
||||
|
||||
confbox@0.1.8:
|
||||
resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==}
|
||||
|
||||
csstype@3.1.3:
|
||||
resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
|
||||
|
||||
debug@4.4.0:
|
||||
resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==}
|
||||
engines: {node: '>=6.0'}
|
||||
peerDependencies:
|
||||
supports-color: '*'
|
||||
peerDependenciesMeta:
|
||||
supports-color:
|
||||
optional: true
|
||||
|
||||
decode-uri-component@0.4.1:
|
||||
resolution: {integrity: sha512-+8VxcR21HhTy8nOt6jf20w0c9CADrw1O8d+VZ/YzzCt4bJ3uBjw+D1q2osAB8RnpwwaeYBxy0HyKQxD5JBMuuQ==}
|
||||
engines: {node: '>=14.16'}
|
||||
|
||||
defu@6.1.4:
|
||||
resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==}
|
||||
|
||||
entities@4.5.0:
|
||||
resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
|
||||
engines: {node: '>=0.12'}
|
||||
|
||||
estree-walker@2.0.2:
|
||||
resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
|
||||
|
||||
fast-glob@3.3.3:
|
||||
resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==}
|
||||
engines: {node: '>=8.6.0'}
|
||||
|
||||
fastq@1.19.1:
|
||||
resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==}
|
||||
|
||||
fill-range@7.1.1:
|
||||
resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
filter-obj@5.1.0:
|
||||
resolution: {integrity: sha512-qWeTREPoT7I0bifpPUXtxkZJ1XJzxWtfoWWkdVGqa+eCr3SHW/Ocp89o8vLvbUuQnadybJpjOKu4V+RwO6sGng==}
|
||||
engines: {node: '>=14.16'}
|
||||
|
||||
fsevents@2.3.3:
|
||||
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
|
||||
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
|
||||
os: [darwin]
|
||||
|
||||
glob-parent@5.1.2:
|
||||
resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
|
||||
engines: {node: '>= 6'}
|
||||
|
||||
hoci@0.6.0:
|
||||
resolution: {integrity: sha512-PtSPRtQm5/OUKxSU8lVelhBc+3TVW2cB2hGlG9pfzQ0rbSBFgLHWSLImBgLZnau72wKOrMNjBxzprQ941+rTWw==}
|
||||
peerDependencies:
|
||||
vue: ^3.0.0
|
||||
|
||||
is-binary-path@2.1.0:
|
||||
resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
is-extglob@2.1.1:
|
||||
resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
is-glob@4.0.3:
|
||||
resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
is-number@7.0.0:
|
||||
resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
|
||||
engines: {node: '>=0.12.0'}
|
||||
|
||||
local-pkg@0.5.1:
|
||||
resolution: {integrity: sha512-9rrA30MRRP3gBD3HTGnC6cDFpaE1kVDWxWgqWJUN0RvDNAo+Nz/9GxB+nHOH0ifbVFy0hSA1V6vFDvnx54lTEQ==}
|
||||
engines: {node: '>=14'}
|
||||
|
||||
magic-string@0.30.17:
|
||||
resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==}
|
||||
|
||||
maybe-types@0.2.0:
|
||||
resolution: {integrity: sha512-4ul+RHPcxNznI+Ke6xUT2/mcQTl54fYUdRZQ3QNkQsC4USfAxXkPTh+wEOx4UBsXYg214867v2ZeirQDJT7uOA==}
|
||||
|
||||
merge2@1.4.1:
|
||||
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
|
||||
engines: {node: '>= 8'}
|
||||
|
||||
micromatch@4.0.8:
|
||||
resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
|
||||
engines: {node: '>=8.6'}
|
||||
|
||||
minimatch@9.0.5:
|
||||
resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==}
|
||||
engines: {node: '>=16 || 14 >=14.17'}
|
||||
|
||||
mlly@1.7.4:
|
||||
resolution: {integrity: sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==}
|
||||
|
||||
ms@2.1.3:
|
||||
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
|
||||
|
||||
nanoid@3.3.11:
|
||||
resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==}
|
||||
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
|
||||
hasBin: true
|
||||
|
||||
normalize-path@3.0.0:
|
||||
resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
pathe@2.0.3:
|
||||
resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==}
|
||||
|
||||
picocolors@1.1.1:
|
||||
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
|
||||
|
||||
picomatch@2.3.1:
|
||||
resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
|
||||
engines: {node: '>=8.6'}
|
||||
|
||||
picomatch@4.0.2:
|
||||
resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
pkg-types@1.3.1:
|
||||
resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==}
|
||||
|
||||
postcss@8.5.3:
|
||||
resolution: {integrity: sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==}
|
||||
engines: {node: ^10 || ^12 || >=14}
|
||||
|
||||
query-string@9.1.1:
|
||||
resolution: {integrity: sha512-MWkCOVIcJP9QSKU52Ngow6bsAWAPlPK2MludXvcrS2bGZSl+T1qX9MZvRIkqUIkGLJquMJHWfsT6eRqUpp4aWg==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
queue-microtask@1.2.3:
|
||||
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
|
||||
|
||||
readdirp@3.6.0:
|
||||
resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
|
||||
engines: {node: '>=8.10.0'}
|
||||
|
||||
reusify@1.1.0:
|
||||
resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==}
|
||||
engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
|
||||
|
||||
run-parallel@1.2.0:
|
||||
resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
|
||||
|
||||
source-map-js@1.2.1:
|
||||
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
split-on-first@3.0.0:
|
||||
resolution: {integrity: sha512-qxQJTx2ryR0Dw0ITYyekNQWpz6f8dGd7vffGNflQQ3Iqj9NJ6qiZ7ELpZsJ/QBhIVAiDfXdag3+Gp8RvWa62AA==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
to-regex-range@5.0.1:
|
||||
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
|
||||
engines: {node: '>=8.0'}
|
||||
|
||||
tslx@0.1.1:
|
||||
resolution: {integrity: sha512-MYh2I7iw8owRx3xFVq8DXp3JWoJ7G7oMA5uUrCwGvuOipV6w9XvlJKo3yVJMh75Y+5caIaYhCsUZ1WTnxQRk7A==}
|
||||
|
||||
type-fest@4.38.0:
|
||||
resolution: {integrity: sha512-2dBz5D5ycHIoliLYLi0Q2V7KRaDlH0uWIvmk7TYlAg5slqwiPv1ezJdZm1QEM0xgk29oYWMCbIG7E6gHpvChlg==}
|
||||
engines: {node: '>=16'}
|
||||
|
||||
ufo@1.5.4:
|
||||
resolution: {integrity: sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==}
|
||||
|
||||
unplugin-vue-components@0.27.5:
|
||||
resolution: {integrity: sha512-m9j4goBeNwXyNN8oZHHxvIIYiG8FQ9UfmKWeNllpDvhU7btKNNELGPt+o3mckQKuPwrE7e0PvCsx+IWuDSD9Vg==}
|
||||
engines: {node: '>=14'}
|
||||
peerDependencies:
|
||||
'@babel/parser': ^7.15.8
|
||||
'@nuxt/kit': ^3.2.2
|
||||
vue: 2 || 3
|
||||
peerDependenciesMeta:
|
||||
'@babel/parser':
|
||||
optional: true
|
||||
'@nuxt/kit':
|
||||
optional: true
|
||||
|
||||
unplugin@1.16.1:
|
||||
resolution: {integrity: sha512-4/u/j4FrCKdi17jaxuJA0jClGxB1AvU2hw/IuayPc4ay1XGaJs/rbb4v5WKwAjNifjmXK9PIFyuPiaK8azyR9w==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
|
||||
vue-demi@0.14.10:
|
||||
resolution: {integrity: sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==}
|
||||
engines: {node: '>=12'}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
'@vue/composition-api': ^1.0.0-rc.1
|
||||
vue: ^3.0.0-0 || ^2.6.0
|
||||
peerDependenciesMeta:
|
||||
'@vue/composition-api':
|
||||
optional: true
|
||||
|
||||
vue@3.5.13:
|
||||
resolution: {integrity: sha512-wmeiSMxkZCSc+PM2w2VRsOYAZC8GdipNFRTsLSfodVqI9mbejKeXEGr8SckuLnrQPGe3oJN5c3K0vpoU9q/wCQ==}
|
||||
peerDependencies:
|
||||
typescript: '*'
|
||||
peerDependenciesMeta:
|
||||
typescript:
|
||||
optional: true
|
||||
|
||||
webpack-virtual-modules@0.6.2:
|
||||
resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==}
|
||||
|
||||
snapshots:
|
||||
|
||||
'@antfu/utils@0.7.10':
|
||||
optional: true
|
||||
|
||||
'@babel/helper-string-parser@7.25.9': {}
|
||||
|
||||
'@babel/helper-validator-identifier@7.25.9': {}
|
||||
|
||||
'@babel/parser@7.27.0':
|
||||
dependencies:
|
||||
'@babel/types': 7.27.0
|
||||
|
||||
'@babel/types@7.27.0':
|
||||
dependencies:
|
||||
'@babel/helper-string-parser': 7.25.9
|
||||
'@babel/helper-validator-identifier': 7.25.9
|
||||
|
||||
'@hoci/components@0.6.0(@vueuse/core@9.0.0(vue@3.5.13))(vue@3.5.13)':
|
||||
dependencies:
|
||||
'@hoci/core': 0.6.0(vue@3.5.13)
|
||||
'@hoci/shared': 0.6.0(@vueuse/core@9.0.0(vue@3.5.13))(vue@3.5.13)
|
||||
'@vueuse/core': 9.0.0(vue@3.5.13)
|
||||
maybe-types: 0.2.0
|
||||
tslx: 0.1.1
|
||||
vue: 3.5.13
|
||||
transitivePeerDependencies:
|
||||
- '@vue/composition-api'
|
||||
|
||||
'@hoci/core@0.6.0(vue@3.5.13)':
|
||||
dependencies:
|
||||
'@hoci/shared': 0.6.0(@vueuse/core@9.0.0(vue@3.5.13))(vue@3.5.13)
|
||||
'@vueuse/core': 9.0.0(vue@3.5.13)
|
||||
maybe-types: 0.2.0
|
||||
tslx: 0.1.1
|
||||
vue: 3.5.13
|
||||
transitivePeerDependencies:
|
||||
- '@vue/composition-api'
|
||||
|
||||
'@hoci/shared@0.6.0(@vueuse/core@9.0.0(vue@3.5.13))(vue@3.5.13)':
|
||||
dependencies:
|
||||
'@vueuse/core': 9.0.0(vue@3.5.13)
|
||||
defu: 6.1.4
|
||||
maybe-types: 0.2.0
|
||||
type-fest: 4.38.0
|
||||
vue: 3.5.13
|
||||
|
||||
'@jridgewell/sourcemap-codec@1.5.0': {}
|
||||
|
||||
'@nodelib/fs.scandir@2.1.5':
|
||||
dependencies:
|
||||
'@nodelib/fs.stat': 2.0.5
|
||||
run-parallel: 1.2.0
|
||||
optional: true
|
||||
|
||||
'@nodelib/fs.stat@2.0.5':
|
||||
optional: true
|
||||
|
||||
'@nodelib/fs.walk@1.2.8':
|
||||
dependencies:
|
||||
'@nodelib/fs.scandir': 2.1.5
|
||||
fastq: 1.19.1
|
||||
optional: true
|
||||
|
||||
'@rollup/pluginutils@5.1.4':
|
||||
dependencies:
|
||||
'@types/estree': 1.0.7
|
||||
estree-walker: 2.0.2
|
||||
picomatch: 4.0.2
|
||||
optional: true
|
||||
|
||||
'@types/estree@1.0.7':
|
||||
optional: true
|
||||
|
||||
'@types/web-bluetooth@0.0.15': {}
|
||||
|
||||
'@vue/compiler-core@3.5.13':
|
||||
dependencies:
|
||||
'@babel/parser': 7.27.0
|
||||
'@vue/shared': 3.5.13
|
||||
entities: 4.5.0
|
||||
estree-walker: 2.0.2
|
||||
source-map-js: 1.2.1
|
||||
|
||||
'@vue/compiler-dom@3.5.13':
|
||||
dependencies:
|
||||
'@vue/compiler-core': 3.5.13
|
||||
'@vue/shared': 3.5.13
|
||||
|
||||
'@vue/compiler-sfc@3.5.13':
|
||||
dependencies:
|
||||
'@babel/parser': 7.27.0
|
||||
'@vue/compiler-core': 3.5.13
|
||||
'@vue/compiler-dom': 3.5.13
|
||||
'@vue/compiler-ssr': 3.5.13
|
||||
'@vue/shared': 3.5.13
|
||||
estree-walker: 2.0.2
|
||||
magic-string: 0.30.17
|
||||
postcss: 8.5.3
|
||||
source-map-js: 1.2.1
|
||||
|
||||
'@vue/compiler-ssr@3.5.13':
|
||||
dependencies:
|
||||
'@vue/compiler-dom': 3.5.13
|
||||
'@vue/shared': 3.5.13
|
||||
|
||||
'@vue/reactivity@3.5.13':
|
||||
dependencies:
|
||||
'@vue/shared': 3.5.13
|
||||
|
||||
'@vue/runtime-core@3.5.13':
|
||||
dependencies:
|
||||
'@vue/reactivity': 3.5.13
|
||||
'@vue/shared': 3.5.13
|
||||
|
||||
'@vue/runtime-dom@3.5.13':
|
||||
dependencies:
|
||||
'@vue/reactivity': 3.5.13
|
||||
'@vue/runtime-core': 3.5.13
|
||||
'@vue/shared': 3.5.13
|
||||
csstype: 3.1.3
|
||||
|
||||
'@vue/server-renderer@3.5.13(vue@3.5.13)':
|
||||
dependencies:
|
||||
'@vue/compiler-ssr': 3.5.13
|
||||
'@vue/shared': 3.5.13
|
||||
vue: 3.5.13
|
||||
|
||||
'@vue/shared@3.5.13': {}
|
||||
|
||||
'@vueuse/core@9.0.0(vue@3.5.13)':
|
||||
dependencies:
|
||||
'@types/web-bluetooth': 0.0.15
|
||||
'@vueuse/metadata': 9.0.0
|
||||
'@vueuse/shared': 9.0.0(vue@3.5.13)
|
||||
vue-demi: 0.14.10(vue@3.5.13)
|
||||
transitivePeerDependencies:
|
||||
- '@vue/composition-api'
|
||||
- vue
|
||||
|
||||
'@vueuse/metadata@9.0.0': {}
|
||||
|
||||
'@vueuse/shared@9.0.0(vue@3.5.13)':
|
||||
dependencies:
|
||||
vue-demi: 0.14.10(vue@3.5.13)
|
||||
transitivePeerDependencies:
|
||||
- '@vue/composition-api'
|
||||
- vue
|
||||
|
||||
acorn@8.14.1:
|
||||
optional: true
|
||||
|
||||
anymatch@3.1.3:
|
||||
dependencies:
|
||||
normalize-path: 3.0.0
|
||||
picomatch: 2.3.1
|
||||
optional: true
|
||||
|
||||
balanced-match@1.0.2:
|
||||
optional: true
|
||||
|
||||
binary-extensions@2.3.0:
|
||||
optional: true
|
||||
|
||||
brace-expansion@2.0.1:
|
||||
dependencies:
|
||||
balanced-match: 1.0.2
|
||||
optional: true
|
||||
|
||||
braces@3.0.3:
|
||||
dependencies:
|
||||
fill-range: 7.1.1
|
||||
optional: true
|
||||
|
||||
chokidar@3.6.0:
|
||||
dependencies:
|
||||
anymatch: 3.1.3
|
||||
braces: 3.0.3
|
||||
glob-parent: 5.1.2
|
||||
is-binary-path: 2.1.0
|
||||
is-glob: 4.0.3
|
||||
normalize-path: 3.0.0
|
||||
readdirp: 3.6.0
|
||||
optionalDependencies:
|
||||
fsevents: 2.3.3
|
||||
optional: true
|
||||
|
||||
confbox@0.1.8:
|
||||
optional: true
|
||||
|
||||
csstype@3.1.3: {}
|
||||
|
||||
debug@4.4.0:
|
||||
dependencies:
|
||||
ms: 2.1.3
|
||||
optional: true
|
||||
|
||||
decode-uri-component@0.4.1: {}
|
||||
|
||||
defu@6.1.4: {}
|
||||
|
||||
entities@4.5.0: {}
|
||||
|
||||
estree-walker@2.0.2: {}
|
||||
|
||||
fast-glob@3.3.3:
|
||||
dependencies:
|
||||
'@nodelib/fs.stat': 2.0.5
|
||||
'@nodelib/fs.walk': 1.2.8
|
||||
glob-parent: 5.1.2
|
||||
merge2: 1.4.1
|
||||
micromatch: 4.0.8
|
||||
optional: true
|
||||
|
||||
fastq@1.19.1:
|
||||
dependencies:
|
||||
reusify: 1.1.0
|
||||
optional: true
|
||||
|
||||
fill-range@7.1.1:
|
||||
dependencies:
|
||||
to-regex-range: 5.0.1
|
||||
optional: true
|
||||
|
||||
filter-obj@5.1.0: {}
|
||||
|
||||
fsevents@2.3.3:
|
||||
optional: true
|
||||
|
||||
glob-parent@5.1.2:
|
||||
dependencies:
|
||||
is-glob: 4.0.3
|
||||
optional: true
|
||||
|
||||
hoci@0.6.0(@babel/parser@7.27.0)(@vueuse/core@9.0.0(vue@3.5.13))(vue@3.5.13):
|
||||
dependencies:
|
||||
'@hoci/components': 0.6.0(@vueuse/core@9.0.0(vue@3.5.13))(vue@3.5.13)
|
||||
'@hoci/core': 0.6.0(vue@3.5.13)
|
||||
vue: 3.5.13
|
||||
optionalDependencies:
|
||||
unplugin-vue-components: 0.27.5(@babel/parser@7.27.0)(vue@3.5.13)
|
||||
transitivePeerDependencies:
|
||||
- '@babel/parser'
|
||||
- '@nuxt/kit'
|
||||
- '@vue/composition-api'
|
||||
- '@vueuse/core'
|
||||
- rollup
|
||||
- supports-color
|
||||
|
||||
is-binary-path@2.1.0:
|
||||
dependencies:
|
||||
binary-extensions: 2.3.0
|
||||
optional: true
|
||||
|
||||
is-extglob@2.1.1:
|
||||
optional: true
|
||||
|
||||
is-glob@4.0.3:
|
||||
dependencies:
|
||||
is-extglob: 2.1.1
|
||||
optional: true
|
||||
|
||||
is-number@7.0.0:
|
||||
optional: true
|
||||
|
||||
local-pkg@0.5.1:
|
||||
dependencies:
|
||||
mlly: 1.7.4
|
||||
pkg-types: 1.3.1
|
||||
optional: true
|
||||
|
||||
magic-string@0.30.17:
|
||||
dependencies:
|
||||
'@jridgewell/sourcemap-codec': 1.5.0
|
||||
|
||||
maybe-types@0.2.0: {}
|
||||
|
||||
merge2@1.4.1:
|
||||
optional: true
|
||||
|
||||
micromatch@4.0.8:
|
||||
dependencies:
|
||||
braces: 3.0.3
|
||||
picomatch: 2.3.1
|
||||
optional: true
|
||||
|
||||
minimatch@9.0.5:
|
||||
dependencies:
|
||||
brace-expansion: 2.0.1
|
||||
optional: true
|
||||
|
||||
mlly@1.7.4:
|
||||
dependencies:
|
||||
acorn: 8.14.1
|
||||
pathe: 2.0.3
|
||||
pkg-types: 1.3.1
|
||||
ufo: 1.5.4
|
||||
optional: true
|
||||
|
||||
ms@2.1.3:
|
||||
optional: true
|
||||
|
||||
nanoid@3.3.11: {}
|
||||
|
||||
normalize-path@3.0.0:
|
||||
optional: true
|
||||
|
||||
pathe@2.0.3:
|
||||
optional: true
|
||||
|
||||
picocolors@1.1.1: {}
|
||||
|
||||
picomatch@2.3.1:
|
||||
optional: true
|
||||
|
||||
picomatch@4.0.2:
|
||||
optional: true
|
||||
|
||||
pkg-types@1.3.1:
|
||||
dependencies:
|
||||
confbox: 0.1.8
|
||||
mlly: 1.7.4
|
||||
pathe: 2.0.3
|
||||
optional: true
|
||||
|
||||
postcss@8.5.3:
|
||||
dependencies:
|
||||
nanoid: 3.3.11
|
||||
picocolors: 1.1.1
|
||||
source-map-js: 1.2.1
|
||||
|
||||
query-string@9.1.1:
|
||||
dependencies:
|
||||
decode-uri-component: 0.4.1
|
||||
filter-obj: 5.1.0
|
||||
split-on-first: 3.0.0
|
||||
|
||||
queue-microtask@1.2.3:
|
||||
optional: true
|
||||
|
||||
readdirp@3.6.0:
|
||||
dependencies:
|
||||
picomatch: 2.3.1
|
||||
optional: true
|
||||
|
||||
reusify@1.1.0:
|
||||
optional: true
|
||||
|
||||
run-parallel@1.2.0:
|
||||
dependencies:
|
||||
queue-microtask: 1.2.3
|
||||
optional: true
|
||||
|
||||
source-map-js@1.2.1: {}
|
||||
|
||||
split-on-first@3.0.0: {}
|
||||
|
||||
to-regex-range@5.0.1:
|
||||
dependencies:
|
||||
is-number: 7.0.0
|
||||
optional: true
|
||||
|
||||
tslx@0.1.1:
|
||||
dependencies:
|
||||
csstype: 3.1.3
|
||||
|
||||
type-fest@4.38.0: {}
|
||||
|
||||
ufo@1.5.4:
|
||||
optional: true
|
||||
|
||||
unplugin-vue-components@0.27.5(@babel/parser@7.27.0)(vue@3.5.13):
|
||||
dependencies:
|
||||
'@antfu/utils': 0.7.10
|
||||
'@rollup/pluginutils': 5.1.4
|
||||
chokidar: 3.6.0
|
||||
debug: 4.4.0
|
||||
fast-glob: 3.3.3
|
||||
local-pkg: 0.5.1
|
||||
magic-string: 0.30.17
|
||||
minimatch: 9.0.5
|
||||
mlly: 1.7.4
|
||||
unplugin: 1.16.1
|
||||
vue: 3.5.13
|
||||
optionalDependencies:
|
||||
'@babel/parser': 7.27.0
|
||||
transitivePeerDependencies:
|
||||
- rollup
|
||||
- supports-color
|
||||
optional: true
|
||||
|
||||
unplugin@1.16.1:
|
||||
dependencies:
|
||||
acorn: 8.14.1
|
||||
webpack-virtual-modules: 0.6.2
|
||||
optional: true
|
||||
|
||||
vue-demi@0.14.10(vue@3.5.13):
|
||||
dependencies:
|
||||
vue: 3.5.13
|
||||
|
||||
vue@3.5.13:
|
||||
dependencies:
|
||||
'@vue/compiler-dom': 3.5.13
|
||||
'@vue/compiler-sfc': 3.5.13
|
||||
'@vue/runtime-dom': 3.5.13
|
||||
'@vue/server-renderer': 3.5.13(vue@3.5.13)
|
||||
'@vue/shared': 3.5.13
|
||||
|
||||
webpack-virtual-modules@0.6.2:
|
||||
optional: true
|
||||