// 元素距离浏览器顶部的位置
element.getBoundingClientRect().top
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
import { ISeo } from '~~/components/prouser/interfaces'
import { DETAIL_PAGE_SEO } from '~~/services/constants'
import { MIDDLEWARE_BASE_URL } from '~~/utils/article'
import { postApi } from '~~/utils/http'
export default defineNuxtRouteMiddleware(async () => {
const route = useRoute()
const options = {
method: 'POST',
body: JSON.stringify({
resource_type: 1,
resource_id: Number(route.params.uid),
sss: 1,
}),
headers: { 'Content-Type': 'application/json' },
}
try {
const a = await fetch(MIDDLEWARE_BASE_URL() + DETAIL_PAGE_SEO, options)
console.log('a.data:', a)
} catch (error) {
console.log('error:', error)
}
// const { data } = await postApi<ISeo>(DETAIL_PAGE_SEO, {
// body: { resource_type: 1, resource_id: Number(route.params.uid) },
// })
// if (data.value?.redirect === 1 && data.value?.status === 1) {
// return navigateTo({ path: `/${data.value?.seo}` })
// }
})
-
实现一个挂在全局的模态框
import CustomDrawer from '@/components/CustomDrawer.vue'; import { CSSProperties, createApp } from 'vue'; interface IBasicOption<T extends { onClose?: () => void }> { id?: string; // @ts-ignore wrapperComponent: any; props?: T; styles?: CSSProperties; } let container: Node | null; const createMount = (option: any) => { console.log('创建临时抽屉', option); if (container) { //确保只存在一个弹框 document.body.removeChild(container); container = null; } container = document.createElement('div'); document.body.appendChild(container); const drawerVisible = ref(true); const app = createApp( h( CustomDrawer, { drawerVisible, ...option?.props, styles: option.styles, onClose: (params?: any) => { // @ts-ignore if (typeof option.props?.onClose === 'function' && option.closeByIcon) { // @ts-ignore option.props?.onClose(params); } drawerVisible.value = false; }, onClosed() { // app.unmount(container); if (container) { document.body.removeChild(container); container = null; } }, }, h(option.wrapperComponent, { ...(option.props ?? {}), onClose: (params?: any) => { // @ts-ignore if (typeof option.props?.onClose === 'function') { // @ts-ignore option.props?.onClose(params); } drawerVisible.value = false; console.log('drawerVisible', drawerVisible.value); }, }), ), ); return app.mount(container as Element); }; // @ts-ignore function useDrawer<T>(paramsOptions: IBasicOption<T>) { paramsOptions.id = paramsOptions?.id || 'drawer_' + 1; //唯一id 删除组件时用于定位 const $inst = createMount(paramsOptions); return $inst; } export default useDrawer;
-
挂载窗口 新
-
import { type Component, type CSSProperties, render } from 'vue' import ModalEmpty from '~/components/Common/ModalEmpty.vue' interface IBasicOption<T> { onClose?: () => void id?: string wrapperComponent: Component props?: T styles?: CSSProperties banEmptyClose?: boolean disableAnimation?: boolean } const createMount = <T>(option: IBasicOption<T>) => { // if (container) { // //确保只存在一个弹框 // document.body.removeChild(container) // container = null // } let container: Element | null = document.createElement('div') const drawerVisible = ref(true) const vNode = h( ModalEmpty, { drawerVisible, ...option?.props, styles: option.styles, banEmptyClose: option.banEmptyClose, disableAnimation: option.disableAnimation, onClose: (params?: any) => { // @ts-ignore if (typeof option.props?.onClose === 'function' && option.closeByIcon) { // @ts-ignore option.props?.onClose(params) } drawerVisible.value = false }, onClosed() { if (container) { document.body.removeChild(container) container = null } }, }, h(option.wrapperComponent, { ...(option.props ?? {}), onClose: (params?: any) => { // @ts-ignore if (typeof option.props?.onClose === 'function') { // @ts-ignore option.props?.onClose(params) } drawerVisible.value = false }, }), ) // important const nuxtApp = useNuxtApp() // 获取 Nuxt 上下文 const appContext = nuxtApp.vueApp._context vNode.appContext = appContext if (container) { render(vNode, container) // render(vNode, container) document.body.appendChild(container) } return { close: () => { if (container) { document.body.removeChild(container) container = null } }, } } // @ts-ignore function useEmptyModal<T>(paramsOptions: IBasicOption<T>) { paramsOptions.id = paramsOptions?.id || 'empty_module_' + 1 //唯一id 删除组件时用于定位 return createMount<T>(paramsOptions) } export default useEmptyModal
-
requestAnimationsFrame
function scrollToAnimated(targetPosition: number, duration = 300) { const startPosition = navRefInfo.scrollLeft; const distance = targetPosition; const startTime = performance.now(); function step(currentTime: number) { const elapsedTime = currentTime - startTime; const scrollY = easeInOutCubic( elapsedTime, startPosition, distance, duration ); navRef.value && (navRef.value.scrollLeft = scrollY); navRefInfo.scrollLeft = navRef.value?.scrollLeft || 0; if (elapsedTime < duration) requestAnimationFrame(step); } function easeInOutCubic(t: number, b: number, c: number, d: number) { t /= d / 2; if (t < 1) return (c / 2) * t * t * t + b; t -= 2; return (c / 2) * (t * t * t + 2) + b; } requestAnimationFrame(step); }
-
获取当前系统主题
const isDarkTheme = window.matchMedia("(prefers-color-scheme: dark)"); // 是深色 if (isDarkTheme.matches) { // 是深色 // 主题设置为深色。 } else { // 不是深色 // 主题设置为浅色。 }
-
复制图片
<template> <div class=""> <div class="w-[100px]" v-for="(item, idx) in QRCodeList" :key="idx"> <QrcodeVue id="canvasDom" @click="(e) => htmlToCanvas(e.currentTarget)" :value="item.link" :size="68" class="m-auto bg-white" /> <div>{{ item.label }}</div> </div> <div class="absolute opacity-0 pointer-events-none"> <img :src="canvasImageUrl" ref="copyImage" /> </div> </div> </template> <script setup lang="ts"> import html2canvas from 'html2canvas'; const copyImage = ref(); const canvasImageUrl = ref<string>(); function htmlToCanvas(picture: HTMLElement) { return new Promise((resolve, reject) => { html2canvas(picture, { width: picture.offsetWidth, // 画布的宽 height: picture.offsetHeight, // 画布的高 scale: 4, // 处理模糊问题 useCORS: true, allowTaint: true, imageTimeout: 15000, logging: true, }) .then(async function (canvas: any) { canvasImageUrl.value = canvas.toDataURL('image/png', 1.0); await nextTick(); const selection = window.getSelection(); //清除选中 if (selection && selection.rangeCount > 0) selection.removeAllRanges(); if (!document.queryCommandSupported('copy')) return alert('浏览器暂不支持复制命令'); const range = document.createRange(); range.selectNode(copyImage.value); selection && selection.addRange(range); document.execCommand('copy'); selection && selection.removeAllRanges(); }) .catch((err: any) => reject(err)); }); } </script>
-
base64ToFile
-
const base64ToBlob = (base64: string, type: string): Blob => { const byteCharacters = atob(base64) // 解码 Base64 字符串 const byteNumbers = new Array(byteCharacters.length) for (let i = 0; i < byteCharacters.length; i++) { byteNumbers[i] = byteCharacters.charCodeAt(i) } const byteArray = new Uint8Array(byteNumbers) return new Blob([byteArray], { type: type }) // 创建 Blob 对象 } const base64ToFile = (base64: string, fileName: string): File => { const mimeType = base64.split(';')[0].split(':')[1] // 获取 MIME 类型 const base64Data = base64.split(',')[1] // 获取 Base64 数据部分 const blob = base64ToBlob(base64Data, mimeType) // 转换为 Blob return new File([blob], fileName, { type: mimeType }) // 创建 File 对象 }
-
-
file2Base64
-
const file2Base64 = async (file: File) => new Promise<string>((resolve, reject) => { const fileReader = new FileReader() fileReader.readAsDataURL(file) fileReader.onload = () => { resolve(fileReader.result as string) } fileReader.onerror = (error) => { reject(error) } })
-