1. 弹幕功能
2. 点赞 3. 导航引导 4. ai弹幕、审核
This commit is contained in:
111
vite-tailwindcss/src/components/AppToast.vue
Normal file
111
vite-tailwindcss/src/components/AppToast.vue
Normal file
@@ -0,0 +1,111 @@
|
||||
<template>
|
||||
<Teleport to="body">
|
||||
<div class="app-toast-host" aria-live="polite" aria-relevant="additions">
|
||||
<TransitionGroup name="app-toast">
|
||||
<div
|
||||
v-for="t in toasts"
|
||||
:key="t.id"
|
||||
class="app-toast"
|
||||
:class="`is-${t.type}`"
|
||||
role="status"
|
||||
>
|
||||
<span class="app-toast-icon" aria-hidden="true">
|
||||
<i v-if="t.type === 'success'" class="fa-solid fa-check" />
|
||||
<i v-else-if="t.type === 'error'" class="fa-solid fa-exclamation" />
|
||||
<i v-else class="fa-solid fa-info" />
|
||||
</span>
|
||||
<p class="app-toast-msg">{{ t.message }}</p>
|
||||
<button type="button" class="app-toast-close" aria-label="关闭" @click="dismiss(t.id)">✕</button>
|
||||
</div>
|
||||
</TransitionGroup>
|
||||
</div>
|
||||
</Teleport>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { useToast } from '@/composables/useToast'
|
||||
|
||||
const { toasts, dismiss } = useToast()
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.app-toast-host {
|
||||
position: fixed;
|
||||
top: 18%;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
z-index: 9999;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
width: min(86vw, 340px);
|
||||
pointer-events: none;
|
||||
}
|
||||
.app-toast {
|
||||
pointer-events: auto;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 10px;
|
||||
width: 100%;
|
||||
padding: 12px 14px;
|
||||
border-radius: 16px;
|
||||
background: rgba(253, 251, 247, 0.96);
|
||||
border: 0.5px solid rgba(168, 140, 107, 0.35);
|
||||
box-shadow: 0 12px 32px rgba(92, 74, 53, 0.16);
|
||||
backdrop-filter: blur(8px);
|
||||
color: #5c4a35;
|
||||
}
|
||||
.app-toast.is-success {
|
||||
border-color: rgba(168, 140, 107, 0.5);
|
||||
}
|
||||
.app-toast.is-error {
|
||||
border-color: rgba(196, 120, 110, 0.55);
|
||||
background: rgba(255, 248, 246, 0.97);
|
||||
}
|
||||
.app-toast-icon {
|
||||
flex: 0 0 auto;
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
border-radius: 999px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-top: 1px;
|
||||
font-size: 11px;
|
||||
background: rgba(168, 140, 107, 0.14);
|
||||
color: #a88c6b;
|
||||
}
|
||||
.app-toast.is-error .app-toast-icon {
|
||||
background: rgba(196, 120, 110, 0.16);
|
||||
color: #c4786e;
|
||||
}
|
||||
.app-toast-msg {
|
||||
flex: 1;
|
||||
margin: 0;
|
||||
font-size: 13px;
|
||||
line-height: 1.55;
|
||||
letter-spacing: 0.04em;
|
||||
word-break: break-word;
|
||||
}
|
||||
.app-toast-close {
|
||||
flex: 0 0 auto;
|
||||
margin-top: -2px;
|
||||
padding: 2px 4px;
|
||||
font-size: 12px;
|
||||
color: #9a9084;
|
||||
line-height: 1;
|
||||
}
|
||||
.app-toast-enter-active,
|
||||
.app-toast-leave-active {
|
||||
transition: opacity 0.28s ease, transform 0.28s ease;
|
||||
}
|
||||
.app-toast-enter-from,
|
||||
.app-toast-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateY(-8px) scale(0.98);
|
||||
}
|
||||
.app-toast-move {
|
||||
transition: transform 0.28s ease;
|
||||
}
|
||||
</style>
|
||||
@@ -77,12 +77,17 @@ const buildRow = (item, { force = false } = {}) => {
|
||||
const delay = force ? 0 : Math.random() * 0.4
|
||||
const id = ++uid
|
||||
|
||||
// 发送者刚插入的弹幕强制「刚刚」,避免服务端时间解析偏差
|
||||
const timeLabel = force
|
||||
? '刚刚'
|
||||
: formatRelativeTime(item.created_at || Date.now())
|
||||
|
||||
return {
|
||||
id,
|
||||
name: item.name,
|
||||
content: item.content,
|
||||
color: resolveDanmakuColor(item.color),
|
||||
timeLabel: formatRelativeTime(item.created_at || Date.now()),
|
||||
timeLabel,
|
||||
style: {
|
||||
top: `${8 + track * 12}%`,
|
||||
animationDuration: `${duration}s`,
|
||||
@@ -231,9 +236,9 @@ defineExpose({ refresh: fetchList, pushItem })
|
||||
animation-timing-function: linear;
|
||||
animation-fill-mode: forwards;
|
||||
backdrop-filter: blur(6px);
|
||||
max-width: min(80vw, 420px);
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
/* 不裁切宽度:长祝福时末尾相对时间需完整露出并随整条飞过 */
|
||||
width: max-content;
|
||||
max-width: none;
|
||||
}
|
||||
.danmaku-name { font-weight: 600; }
|
||||
.danmaku-sep { opacity: 0.9; }
|
||||
|
||||
@@ -276,7 +276,14 @@ function getRevealConfig(imgIdx) {
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
.overlap-wrap { height: clamp(320px, 54vh, 460px); }
|
||||
.overlap-wrap {
|
||||
width: 100%;
|
||||
max-width: 420px;
|
||||
margin-inline: auto;
|
||||
height: clamp(360px, 56vh, 520px);
|
||||
padding: 12px 16px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.g-masonry { column-count: 2; column-gap: 10px; }
|
||||
.g-masonry-item { break-inside: avoid; margin-bottom: 10px; border-radius: 6px; }
|
||||
|
||||
Reference in New Issue
Block a user