-
@@ -31,6 +30,11 @@ const props = defineProps({
default: true,
},
})
+const emit = defineEmits(['close'])
+
+const emitClose = () => {
+ emit('close')
+}
let lockCount = 0
let lockedScrollY = 0
diff --git a/src/main.js b/src/main.js
index de08db2..fb73f97 100644
--- a/src/main.js
+++ b/src/main.js
@@ -8,6 +8,7 @@ import { installRuntimeSecurity } from './utils/runtimeSecurity.js'
import { applyPerformanceModeClass } from './utils/performanceMode.js'
import { versionChecker } from './utils/versionChecker.js'
import { smartImage } from './directives/smartImage.js'
+import { vTouchTap } from './utils/touchTap.js'
import { createPinia } from 'pinia'
import { createApp } from 'vue'
import { useLangStore } from '@/stores/lang'
@@ -28,6 +29,7 @@ pinia.use(piniaPluginPersistedstate)
const app = createApp(App)
app.directive('smart-img', smartImage)
+app.directive('touch-tap', vTouchTap)
app.component('LoadingContent', LoadingContent)
app.use(router)
diff --git a/src/utils/touchTap.js b/src/utils/touchTap.js
new file mode 100644
index 0000000..f7984ed
--- /dev/null
+++ b/src/utils/touchTap.js
@@ -0,0 +1,167 @@
+const TAP_MOVE_LIMIT = 10
+const DUPLICATE_EVENT_LIMIT = 700
+const FOCUSABLE_SELECTOR =
+ 'a,button,input,textarea,select,[tabindex],[role="button"],[contenteditable="true"]'
+
+const getEventPoint = (event) => {
+ if (event.changedTouches?.length) return event.changedTouches[0]
+ if (event.touches?.length) return event.touches[0]
+ return event
+}
+
+const triggerBinding = (el, event) => {
+ const binding = el.__touchTapBinding
+ if (!binding) return
+
+ if (binding.modifiers.self && event.target !== el) {
+ return
+ }
+
+ if (binding.modifiers.stop) {
+ event.stopPropagation()
+ }
+ if (binding.modifiers.prevent) {
+ event.preventDefault()
+ }
+
+ if (typeof binding.value === 'function') {
+ binding.value(event)
+ }
+}
+
+export const vTouchTap = {
+ mounted(el, binding) {
+ el.__touchTapBinding = binding
+
+ let startX = 0
+ let startY = 0
+ let moved = false
+ let active = false
+ let lastTouchTime = 0
+ let lastActivationTime = 0
+
+ if (!el.matches(FOCUSABLE_SELECTOR)) {
+ el.setAttribute('role', 'button')
+ el.setAttribute('tabindex', '0')
+ el.__touchTapAddedA11y = true
+ }
+
+ const activate = (event) => {
+ lastActivationTime = Date.now()
+ triggerBinding(el, event)
+ }
+
+ // Pointer Events 同时覆盖移动端触摸和桌面鼠标;移动阈值用于避免滚动列表时误触。
+ const onStart = (event) => {
+ if (el.__touchTapBinding?.modifiers.self && event.target !== el) {
+ active = false
+ return
+ }
+ if (event.button !== undefined && event.button !== 0) return
+
+ const point = getEventPoint(event)
+ startX = point.clientX || 0
+ startY = point.clientY || 0
+ moved = false
+ active = true
+ }
+
+ const onMove = (event) => {
+ if (!active) return
+
+ const point = getEventPoint(event)
+ const deltaX = Math.abs((point.clientX || 0) - startX)
+ const deltaY = Math.abs((point.clientY || 0) - startY)
+ if (deltaX > TAP_MOVE_LIMIT || deltaY > TAP_MOVE_LIMIT) {
+ moved = true
+ }
+ }
+
+ const onEnd = (event) => {
+ if (!active) return
+
+ const point = getEventPoint(event)
+ const deltaX = Math.abs((point.clientX || 0) - startX)
+ const deltaY = Math.abs((point.clientY || 0) - startY)
+ active = false
+
+ if (moved || deltaX > TAP_MOVE_LIMIT || deltaY > TAP_MOVE_LIMIT) return
+ activate(event)
+ }
+
+ const onTouchEnd = (event) => {
+ lastTouchTime = Date.now()
+ onEnd(event)
+ }
+
+ const onMouseEnd = (event) => {
+ if (Date.now() - lastTouchTime < DUPLICATE_EVENT_LIMIT) return
+ onEnd(event)
+ }
+
+ const onClick = (event) => {
+ if (event.detail !== 0) return
+ if (Date.now() - lastActivationTime < DUPLICATE_EVENT_LIMIT) return
+ activate(event)
+ }
+
+ const onKeydown = (event) => {
+ if (event.key !== 'Enter' && event.key !== ' ') return
+ if (event.key === ' ') {
+ event.preventDefault()
+ }
+ activate(event)
+ }
+
+ const onCancel = () => {
+ active = false
+ moved = false
+ }
+
+ const hasPointerEvent = typeof window !== 'undefined' && typeof window.PointerEvent === 'function'
+ const pointerOrTouchEvents = hasPointerEvent
+ ? [
+ ['pointerdown', onStart, { passive: true }],
+ ['pointermove', onMove, { passive: true }],
+ ['pointerup', onEnd, { passive: false }],
+ ['pointercancel', onCancel, { passive: true }],
+ ]
+ : [
+ ['touchstart', onStart, { passive: true }],
+ ['touchmove', onMove, { passive: true }],
+ ['touchend', onTouchEnd, { passive: false }],
+ ['touchcancel', onCancel, { passive: true }],
+ ['mousedown', onStart, { passive: true }],
+ ['mousemove', onMove, { passive: true }],
+ ['mouseup', onMouseEnd, { passive: false }],
+ ]
+ const events = [
+ ...pointerOrTouchEvents,
+ ['click', onClick, { passive: false }],
+ ['keydown', onKeydown, { passive: false }],
+ ]
+
+ events.forEach(([name, handler, options]) => {
+ el.addEventListener(name, handler, options)
+ })
+
+ el.__touchTapCleanup = () => {
+ events.forEach(([name, handler, options]) => {
+ el.removeEventListener(name, handler, options)
+ })
+ }
+ },
+ updated(el, binding) {
+ el.__touchTapBinding = binding
+ },
+ beforeUnmount(el) {
+ el.__touchTapCleanup?.()
+ delete el.__touchTapBinding
+ delete el.__touchTapCleanup
+ if (el.__touchTapAddedA11y) {
+ el.removeAttribute('role')
+ el.removeAttribute('tabindex')
+ delete el.__touchTapAddedA11y
+ }
+ },
+}
diff --git a/src/views/AdminCenter/ItemDistribution.vue b/src/views/AdminCenter/ItemDistribution.vue
index 2fdf921..905ce65 100644
--- a/src/views/AdminCenter/ItemDistribution.vue
+++ b/src/views/AdminCenter/ItemDistribution.vue
@@ -1,7 +1,6 @@
-
+
-
-
-
-
- {{ t(item.name) }}
-
-
-
-
-
-
-
-
+
+
-
![]()
-
+
+
+
+
+
+
![]()
+
-
-
-
-
-
-
![]()
-
-
{{ t('validity') }}:
-
{{ validityPeriod }}{{ t('day') }}
+
+
+
![]()
+
+ {{ t('validity') }}:
+ {{ validityPeriod }}{{ t('day') }}
+
+
+
+
+
-
-
-
-
-
-
+
+
@@ -208,11 +127,12 @@ const underlineStyle = computed(() => {
if (tabItems.value.length === 0) return {} //没有这个元素
const activeTab = tabItems.value[activeIndex.value]
+ const underlineWidth = 24
console.log('选中元素的左边距', activeTab.offsetLeft)
return {
position: 'absolute',
- left: `${activeTab.offsetLeft + activeTab.offsetWidth / 2 - 15 / 2}px`,
+ left: `${activeTab.offsetLeft + activeTab.offsetWidth / 2 - underlineWidth / 2}px`,
bottom: '0',
transition: 'left 0.3s ease', // 平滑过渡
}
@@ -341,12 +261,18 @@ onMounted(() => {