This commit is contained in:
2026-08-04 17:57:01 +08:00
parent 768fd4a3a9
commit f77034edd7
641 changed files with 26137 additions and 0 deletions
@@ -0,0 +1,47 @@
<script setup lang="ts">
import type { HTMLAttributes } from 'vue'
import {
DialogContent,
type DialogContentEmits,
type DialogContentProps,
useForwardPropsEmits,
} from 'reka-ui'
import { cn } from '@/lib/utils'
const props = defineProps<DialogContentProps & { class?: HTMLAttributes['class'] }>()
const emits = defineEmits<DialogContentEmits>()
const delegatedProps = computed(() => {
const { class: _class, ...delegated } = props
return delegated
})
const forwarded = useForwardPropsEmits(delegatedProps, emits)
</script>
<template>
<DialogContent
v-bind="forwarded"
:class="
cn(
'ui-dialog-content fixed left-1/2 top-1/2 z-50 grid w-[min(92vw,620px)] -translate-x-1/2 -translate-y-1/2 outline-none data-[state=closed]:animate-[dialog-out_120ms_ease-in] data-[state=open]:animate-[dialog-in_160ms_ease-out]',
props.class,
)
"
>
<slot />
</DialogContent>
</template>
<style scoped>
.ui-dialog-content {
overflow: hidden;
border: 1px solid #6c5a37;
border-radius: 0;
background: #050606;
box-shadow:
0 18px 38px rgb(0 0 0 / 80%),
inset 0 0 0 1px #0e1418;
color: var(--ui-surface-foreground);
}
</style>
@@ -0,0 +1,20 @@
<script setup lang="ts">
import type { HTMLAttributes } from 'vue'
import { DialogDescription, type DialogDescriptionProps } from 'reka-ui'
import { cn } from '@/lib/utils'
const props = defineProps<DialogDescriptionProps & { class?: HTMLAttributes['class'] }>()
const delegatedProps = computed(() => {
const { class: _class, ...delegated } = props
return delegated
})
</script>
<template>
<DialogDescription
v-bind="delegatedProps"
:class="cn('text-xs text-muted-foreground', props.class)"
>
<slot />
</DialogDescription>
</template>
@@ -0,0 +1,18 @@
<template>
<div class="ui-dialog-footer">
<slot />
</div>
</template>
<style scoped>
.ui-dialog-footer {
display: flex;
min-height: 38px;
align-items: center;
justify-content: flex-end;
gap: 8px;
padding: 4px 8px;
border-top: 1px solid #332c22;
background: linear-gradient(#20201d, #11110f);
}
</style>
@@ -0,0 +1,140 @@
<script setup lang="ts">
import { onBeforeUnmount, onMounted, ref } from 'vue'
import { DialogClose } from 'reka-ui'
import closeIcon from '../../../../../design/src-web/dialog/img/sprite_interface.NPK/windowcommon_2x.img/6.PNG'
const props = withDefaults(
defineProps<{ closable?: boolean; closeLabel?: string; focused?: boolean }>(),
{ closable: true, closeLabel: '关闭窗口', focused: true },
)
const effectModules = import.meta.glob<string>(
'../../../../../design/src-web/dialog/img/sprite_interface.NPK/windowheadereffect.img/*.PNG',
{ eager: true, import: 'default', query: '?url' },
)
const effectFrames = Object.entries(effectModules)
.sort(([a], [b]) => Number(a.match(/(\d+)\.PNG$/)?.[1]) - Number(b.match(/(\d+)\.PNG$/)?.[1]))
.map(([, url]) => url)
const frameIndex = ref(0)
let timer: ReturnType<typeof setInterval> | undefined
onMounted(() => {
timer = setInterval(() => {
if (props.focused) frameIndex.value = (frameIndex.value + 1) % effectFrames.length
}, 66)
})
onBeforeUnmount(() => clearInterval(timer))
</script>
<template>
<div class="ui-dialog-header" :class="{ 'is-focused': focused, 'is-unfocused': !focused }">
<img
v-if="focused && effectFrames.length"
class="ui-dialog-header__effect"
:src="effectFrames[frameIndex]"
alt=""
aria-hidden="true"
/>
<div class="ui-dialog-header__title"><slot /></div>
<div class="ui-dialog-header__actions">
<slot name="actions" />
<DialogClose v-if="closable" class="ui-dialog-header__close" :aria-label="closeLabel">
<img :src="closeIcon" alt="" />
</DialogClose>
</div>
</div>
</template>
<style scoped>
.ui-dialog-header {
position: relative;
isolation: isolate;
display: grid;
grid-template-columns: 1fr auto 1fr;
height: 28px;
align-items: center;
padding: 0 8px;
overflow: hidden;
border-bottom: 1px solid #1d2735;
background: linear-gradient(#303b50, #263247 56%, #1d2839);
color: var(--ui-titlebar-foreground);
text-shadow: 0 1px 1px #000;
}
.ui-dialog-header.is-focused {
background: linear-gradient(#6079a5, #435a80 55%, #30425f);
color: #f4f6f2;
}
.ui-dialog-header__effect {
position: absolute;
z-index: 1;
top: 0;
left: 50%;
width: 1100px;
height: 100%;
max-width: none;
object-fit: fill;
opacity: 0.72;
mix-blend-mode: screen;
filter: brightness(1.55) contrast(1.06);
transform: translateX(-50%);
pointer-events: none;
}
.ui-dialog-header__title {
position: relative;
z-index: 2;
grid-column: 2;
}
.ui-dialog-header__actions {
position: relative;
z-index: 2;
grid-column: 3;
justify-self: end;
display: flex;
align-items: center;
gap: 8px;
}
.ui-dialog-header__close {
appearance: none;
width: 20px;
height: 20px;
padding: 0;
display: inline-flex;
align-items: center;
justify-content: center;
border: 0;
outline: none;
background: none;
cursor: pointer;
}
.ui-dialog-header__close img {
width: 10px;
height: 9px;
filter: brightness(1);
}
.ui-dialog-header__close:hover img,
.ui-dialog-header__close:focus-visible img {
filter: brightness(1.28);
}
.ui-dialog-header__close:focus-visible {
outline: 1px solid rgb(225 204 135 / 72%);
outline-offset: -2px;
}
.ui-dialog-header__close:active img {
filter: brightness(1.28);
transform: translateY(1px);
}
@media (prefers-reduced-motion: reduce) {
.ui-dialog-header__effect {
display: none;
}
}
</style>
@@ -0,0 +1,30 @@
<script setup lang="ts">
import type { HTMLAttributes } from 'vue'
import { DialogOverlay, type DialogOverlayProps } from 'reka-ui'
import { cn } from '@/lib/utils'
const props = defineProps<DialogOverlayProps & { class?: HTMLAttributes['class'] }>()
const delegatedProps = computed(() => {
const { class: _class, ...delegated } = props
return delegated
})
</script>
<template>
<DialogOverlay
v-bind="delegatedProps"
:class="
cn(
'ui-dialog-overlay fixed inset-0 z-50 data-[state=closed]:animate-[fade-out_120ms_ease-in] data-[state=open]:animate-[fade-in_140ms_ease-out]',
props.class,
)
"
/>
</template>
<style scoped>
.ui-dialog-overlay {
background: var(--ui-overlay);
}
</style>
@@ -0,0 +1,20 @@
<script setup lang="ts">
import type { HTMLAttributes } from 'vue'
import { DialogTitle, type DialogTitleProps } from 'reka-ui'
import { cn } from '@/lib/utils'
const props = defineProps<DialogTitleProps & { class?: HTMLAttributes['class'] }>()
const delegatedProps = computed(() => {
const { class: _class, ...delegated } = props
return delegated
})
</script>
<template>
<DialogTitle
v-bind="delegatedProps"
:class="cn('text-[13px] font-normal text-[#f0f2ef]', props.class)"
>
<slot />
</DialogTitle>
</template>
@@ -0,0 +1,7 @@
export { DialogClose, DialogPortal, DialogRoot, DialogTrigger } from 'reka-ui'
export { default as UiDialogContent } from './UiDialogContent.vue'
export { default as UiDialogDescription } from './UiDialogDescription.vue'
export { default as UiDialogFooter } from './UiDialogFooter.vue'
export { default as UiDialogHeader } from './UiDialogHeader.vue'
export { default as UiDialogOverlay } from './UiDialogOverlay.vue'
export { default as UiDialogTitle } from './UiDialogTitle.vue'