目录

    js常用API&方法-API&方法

    // 元素距离浏览器顶部的位置
    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)
            }
          })