@ -1 +0,0 @@ |
|||||||
Subproject commit e9d7f5bf799bf2d3d3df068f35290eba414237ad |
|
||||||
|
After Width: | Height: | Size: 330 KiB |
|
After Width: | Height: | Size: 330 KiB |
|
After Width: | Height: | Size: 323 KiB |
|
After Width: | Height: | Size: 358 KiB |
|
After Width: | Height: | Size: 198 KiB |
|
After Width: | Height: | Size: 218 KiB |
|
After Width: | Height: | Size: 206 KiB |
|
After Width: | Height: | Size: 246 KiB |
|
After Width: | Height: | Size: 280 KiB |
|
After Width: | Height: | Size: 258 KiB |
|
After Width: | Height: | Size: 194 KiB |
|
After Width: | Height: | Size: 312 KiB |
|
After Width: | Height: | Size: 222 KiB |
|
After Width: | Height: | Size: 305 KiB |
|
After Width: | Height: | Size: 214 KiB |
|
After Width: | Height: | Size: 188 KiB |
|
After Width: | Height: | Size: 250 KiB |
|
After Width: | Height: | Size: 206 KiB |
|
After Width: | Height: | Size: 273 KiB |
|
After Width: | Height: | Size: 300 KiB |
|
After Width: | Height: | Size: 318 KiB |
|
After Width: | Height: | Size: 347 KiB |
|
After Width: | Height: | Size: 424 KiB |
|
After Width: | Height: | Size: 353 KiB |
|
After Width: | Height: | Size: 354 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 990 B |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
@ -1,21 +1,167 @@ |
|||||||
<template> |
<template> |
||||||
<!-- 模板不变,只需: --> |
<div class="smile-emoticon-box" :class="{ elder: !!elder }"> |
||||||
<!-- $elder → elder --> |
<div class="emoticon-panel"> |
||||||
<!-- v-lazy="..." → 改用 DynamicImage 组件或 @error 处理 --> |
<div |
||||||
|
class="emoticon-pages" |
||||||
|
:style="{ width: scrollWidth + 'px', transform: `translateX(-${scrollPosition}px)` }" |
||||||
|
> |
||||||
|
<div |
||||||
|
class="emoticon-page" |
||||||
|
v-for="(page, pageIndex) in faces" |
||||||
|
:key="pageIndex" |
||||||
|
:style="{ width: pageWidth + 'px' }" |
||||||
|
> |
||||||
|
<button |
||||||
|
type="button" |
||||||
|
class="emoticon-item" |
||||||
|
v-for="(face, faceIndex) in page" |
||||||
|
:key="`${pageIndex}-${faceIndex}-${face.id}`" |
||||||
|
@click.stop="chooseFace(face)" |
||||||
|
> |
||||||
|
<img :src="publicPath + face.src" :alt="face.title" /> |
||||||
|
</button> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="emoticon-dots" v-if="faces.length > 1"> |
||||||
|
<span |
||||||
|
v-for="(_, pageIndex) in faces" |
||||||
|
:key="pageIndex" |
||||||
|
class="dot" |
||||||
|
:class="{ active: active === pageIndex }" |
||||||
|
@click.stop="active = pageIndex" |
||||||
|
></span> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
</template> |
</template> |
||||||
|
|
||||||
<script setup> |
<script setup> |
||||||
import { ref, computed, inject } from 'vue' |
import { ref, computed, inject, onMounted, onBeforeUnmount } from 'vue' |
||||||
import faceJSON from '@/mock/face.json' |
import faceJSON from '@/mock/face.json' |
||||||
|
|
||||||
const elder = inject('elder', false) |
defineOptions({ name: 'EmoticonBox' }) |
||||||
|
|
||||||
|
const emit = defineEmits(['onFaceChoose']) |
||||||
|
|
||||||
const publicPath = ref(import.meta.env.BASE_URL) |
const elder = inject('elder', ref(false)) |
||||||
|
const publicPath = import.meta.env.BASE_URL || '/' |
||||||
const active = ref(0) |
const active = ref(0) |
||||||
const faceData = ref(faceJSON) |
const pageWidth = ref(typeof window !== 'undefined' ? window.innerWidth : 375) |
||||||
const faceIndex = ref(0) |
const pageSize = computed(() => (elder?.value ? 16 : 20)) |
||||||
|
|
||||||
|
const flatFaces = computed(() => { |
||||||
|
const list = [] |
||||||
|
;(faceJSON || []).forEach((group) => { |
||||||
|
;(group.faceList || []).forEach((face) => { |
||||||
|
if (face?.title && face?.src) list.push(face) |
||||||
|
}) |
||||||
|
}) |
||||||
|
return list |
||||||
|
}) |
||||||
|
|
||||||
|
const faces = computed(() => { |
||||||
|
const size = pageSize.value || 20 |
||||||
|
const pages = [] |
||||||
|
const source = flatFaces.value |
||||||
|
for (let i = 0; i < source.length; i += size) { |
||||||
|
pages.push(source.slice(i, i + size)) |
||||||
|
} |
||||||
|
return pages.length ? pages : [[]] |
||||||
|
}) |
||||||
|
|
||||||
|
const scrollWidth = computed(() => faces.value.length * pageWidth.value) |
||||||
|
const scrollPosition = computed(() => active.value * pageWidth.value) |
||||||
|
|
||||||
|
function chooseFace(face) { |
||||||
|
if (!face?.title) return |
||||||
|
emit('onFaceChoose', `[${face.title}]`) |
||||||
|
} |
||||||
|
|
||||||
|
function updatePageWidth() { |
||||||
|
pageWidth.value = window.innerWidth || document.documentElement.clientWidth || 375 |
||||||
|
if (active.value > faces.value.length - 1) active.value = 0 |
||||||
|
} |
||||||
|
|
||||||
|
onMounted(() => { |
||||||
|
updatePageWidth() |
||||||
|
window.addEventListener('resize', updatePageWidth) |
||||||
|
}) |
||||||
|
|
||||||
|
onBeforeUnmount(() => { |
||||||
|
window.removeEventListener('resize', updatePageWidth) |
||||||
|
}) |
||||||
|
</script> |
||||||
|
|
||||||
|
<style scoped> |
||||||
|
.smile-emoticon-box { |
||||||
|
width: 100%; |
||||||
|
background-color: #efefef; |
||||||
|
overflow: hidden; |
||||||
|
} |
||||||
|
|
||||||
|
.emoticon-panel { |
||||||
|
padding: 12px 8px 8px; |
||||||
|
box-sizing: border-box; |
||||||
|
} |
||||||
|
|
||||||
|
.emoticon-pages { |
||||||
|
display: flex; |
||||||
|
transition: transform 0.25s ease; |
||||||
|
} |
||||||
|
|
||||||
|
.emoticon-page { |
||||||
|
flex-shrink: 0; |
||||||
|
display: flex; |
||||||
|
flex-wrap: wrap; |
||||||
|
align-content: flex-start; |
||||||
|
padding: 0 4px; |
||||||
|
box-sizing: border-box; |
||||||
|
} |
||||||
|
|
||||||
|
.emoticon-item { |
||||||
|
width: 20%; |
||||||
|
height: 48px; |
||||||
|
border: 0; |
||||||
|
background: transparent; |
||||||
|
display: flex; |
||||||
|
align-items: center; |
||||||
|
justify-content: center; |
||||||
|
padding: 0; |
||||||
|
} |
||||||
|
|
||||||
|
.smile-emoticon-box.elder .emoticon-item { |
||||||
|
width: 25%; |
||||||
|
height: 56px; |
||||||
|
} |
||||||
|
|
||||||
|
.emoticon-item img { |
||||||
|
width: 28px; |
||||||
|
height: 28px; |
||||||
|
object-fit: contain; |
||||||
|
} |
||||||
|
|
||||||
|
.smile-emoticon-box.elder .emoticon-item img { |
||||||
|
width: 34px; |
||||||
|
height: 34px; |
||||||
|
} |
||||||
|
|
||||||
|
.emoticon-dots { |
||||||
|
display: flex; |
||||||
|
justify-content: center; |
||||||
|
align-items: center; |
||||||
|
gap: 6px; |
||||||
|
padding-top: 6px; |
||||||
|
} |
||||||
|
|
||||||
|
.dot { |
||||||
|
width: 6px; |
||||||
|
height: 6px; |
||||||
|
border-radius: 50%; |
||||||
|
background: #c7c7c7; |
||||||
|
} |
||||||
|
|
||||||
const faces = computed(() => { /* ... 逻辑不变 ... */ }) |
.dot.active { |
||||||
const scrollWidth = computed(() => faces.value.length * document.body.scrollWidth) |
background: #025cea; |
||||||
const scrollPosition = computed(() => active.value * document.body.scrollWidth) |
} |
||||||
</script> |
</style> |
||||||
|
|||||||