Vue3常用方法和组件

VUE3+TS

【方法】文件下载

export enum FileType {
  XLSX = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
  XLS = 'application/vnd.ms-excel',
  ZIP = 'application/x-zip-compressed',
  PDF = 'application/pdf',
  PNG = 'image/png',
  JPEG = 'image/jpeg',
  DOC = 'application/msword',
  DOCX = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
}
export const mimeMap = {
  xlsx: FileType.XLSX,
  xls: FileType.XLS,
  zip: FileType.ZIP,
};
/**
 * 解析blob响应内容并下载
 * @param {*} res blob响应内容
 * @param {String} mimeType MIME类型
 */
export function resolveBlob(res: any, mimeType: string) {
  const aLink = document.createElement('a');
  const blob = new Blob([res.data], { type: mimeType });
  // //从response的headers中获取filename, 后端response.setHeader("Content-disposition", "attachment; filename=xxxx.docx") 设置的文件名;
  const patt = new RegExp('filename=([^;]+\\.[^\\.;]+);*');
  const contentDisposition = decodeURI(res.headers['content-disposition']);
  const result = patt.exec(contentDisposition);
  let fileName = result ? result[1] : '';
  fileName = fileName.replace(/"/g, '');
  aLink.style.display = 'none';
  aLink.href = URL.createObjectURL(blob);
  aLink.setAttribute('download', decodeURI(fileName)); // 设置下载文件名称
  document.body.appendChild(aLink);
  aLink.click();
  URL.revokeObjectURL(aLink.href); //清除引用
  document.body.removeChild(aLink);
}

【组件】二次封装ElMessageBox

import { ElMessageBox } from 'element-plus';
import { App } from 'vue';

/**
 * @description 全局的弹窗对话函数,element messagebox二次封装
 * @export
 * @param {string} message
 * @param {boolean} [html=false]
 * @param {string} [title='提示']
 * @return {*}
 */
export function messageBox(message: string, html = false, title = '提示') {
  return new Promise(resovle => {
    ElMessageBox.confirm(message, title, {
      confirmButtonText: '确认',
      cancelButtonText: '取消',
      type: 'warning',
      dangerouslyUseHTMLString: html
    })
      .then(() => {
        resovle(0);
      })
      .catch(() => {});
  });
}
/**
 * @description 弹窗确认框
 * @export
 * @param {string} message
 * @param {boolean} [html=false]
 * @param {string} [title='请确认']
 * @return {*}
 */
export function messagePrompt(message: string, html = false, title = '请确认') {
  return new Promise(resovle => {
    ElMessageBox.prompt(message, title, {
      confirmButtonText: '确认',
      cancelButtonText: '取消',
      dangerouslyUseHTMLString: html
    })
      .then(({ value }) => {
        resovle(value);
      })
      .catch(() => {});
  });
}

export default function (app: App) {
  /**
   * @description 弹窗对话
   */
  app.config.globalProperties.messageBox = messageBox;
  app.config.globalProperties.messagePrompt = messagePrompt;
}

【方法】格式化时间

/**
 * @description 格式化时间戳
 * @param {number} time
 * @param {string} type yyyy-MM-dd/yyyy-MM-dd hh:mm:ss/hh:mm:ss
 */
export function dateFormat(time: number, type: string) {
  if (!time) {
    return;
  }
  let formatTime;
  let date;
  if (time === 0) {
    date = new Date();
  } else {
    date = new Date(time);
  }
  const Year =
    date.getFullYear() < 10 ? '0' + date.getFullYear() : date.getFullYear();
  const month =
    date.getMonth() + 1 < 10
      ? '0' + (date.getMonth() + 1)
      : date.getMonth() + 1;
  const day = date.getDate() < 10 ? '0' + date.getDate() : date.getDate();
  const Hour = date.getHours() < 10 ? '0' + date.getHours() : date.getHours();
  const Minute =
    date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes();
  const Second =
    date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds();
  if (type === 'yyyy-MM-dd') {
    formatTime = Year + '-' + month + '-' + day;
    return formatTime;
  } else if (type === 'yyyy-MM-dd hh:mm:ss') {
    formatTime =
      Year + '-' + month + '-' + day + ' ' + Hour + ':' + Minute + ':' + Second;
    return formatTime;
  } else if (type === 'hh:mm:ss') {
    formatTime = Hour + ':' + Minute + ':' + Second;
    return formatTime;
  } else {
    return 'error type!';
  }
}

【组件】导入单个文件,再次导入可替换,可回显

<template>
  <el-dialog
    v-model="state.visible"
    :title="props.title"
    center
    :close-on-click-modal="false"
    @close="close"
    width="700"
  >
    <el-form
      :model="state.form"
      label-width="100px"
      :rules="state.rules"
      ref="formRef"
    >
      <el-form-item label="文件" prop="file">
        <el-upload
          ref="fileRef"
          class="upload-demo"
          :auto-upload="false"
          drag
          :data="props.data"
          :limit="props.limit"
          :action="url"
          :on-change="changeFile"
          :on-exceed="handleExceed"
          :on-success="onSuccess"
          :on-error="onError"
          :on-remove="onRemove"
          :headers="uploadHeader()"
          accept="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.ms-excel"
        >
          <el-icon class="el-icon--upload"><upload-filled /></el-icon>
          <div class="el-upload__text">
            拖拽文件到这里 或者<em>点击上传</em>
          </div>
          <template #tip>
            <div class="el-upload__tip">请上传Excel文件,大小不超过2M</div>
          </template>
        </el-upload>
      </el-form-item>
    </el-form>
    <template #footer>
      <span class="dialog-footer">
        <el-button type="primary" @click="submit(formRef)"> 确认 </el-button>
        <el-button @click="cancel">取消</el-button>
      </span>
    </template>
  </el-dialog>
</template>

<script lang="ts">
export default { name: 'ImportDialog' };
</script>
<script lang="ts" setup>
import {
  ElMessage,
  UploadFile,
  UploadFiles,
  UploadInstance,
  UploadProps,
  UploadRawFile,
  genFileId,
  FormInstance
} from 'element-plus';
import { reactive, ref } from 'vue';
import { useUpload } from '@/hooks/useUpload';

const props = defineProps({
  title: {
    type: String,
    default: '文件上传'
  },
  limit: {
    type: Number,
    default: 1
  },
  url: {
    type: String,
    required: true
  },
  data: {
    type: Object,
    default: () => {}
  }
});

const emit = defineEmits(['success']);
const state = reactive({
  visible: false,
  form: {
    file: ''
  },
  rules: {
    file: [{ required: true, message: '请选择上传文件!', trigger: 'change' }]
  }
});
const fileRef = ref<UploadInstance>();
const formRef = ref<FormInstance>();
const { uploadHeader, validateFile } = useUpload();

/**
 * @description 文件上传
 */
const submit = async (formEl: FormInstance | undefined) => {
  if (!formEl) return;
  await formEl.validate((valid, fields) => {
    if (valid) {
      fileRef.value?.submit();
    }
  });
};
/**
 * @description 取消文件上传
 */
const cancel = () => {
  close();
};
/**
 * @description 关闭
 */
const close = () => {
  clearFile();
  state.visible = false;
};
/**
 * @description 文件上传之前验证
 */
const changeFile: UploadProps['onChange'] = (
  uploadFile: UploadFile,
  uploadFiles: UploadFiles
) => {
  const type = [
    'application/vnd.ms-excel',
    'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
  ];
  validateFile(
    uploadFile,
    type,
    2,
    () => {
      state.form.file = uploadFile.name;
      formRef.value?.validateField('file');
    },
    () => {
      clearFile();
    }
  );
};
/**
 * @description 覆盖前一个文件
 */
const handleExceed: UploadProps['onExceed'] = files => {
  fileRef.value!.clearFiles();
  const file = files[0] as UploadRawFile;
  file.uid = genFileId();
  fileRef.value!.handleStart(file);
};
/**
 * @description 清空数据
 */
const clearFile = () => {
  fileRef.value?.clearFiles();
  state.form.file = '';
  formRef.value?.resetFields();
};
/**
 * @description 上传成功
 */
const onSuccess: UploadProps['onSuccess'] = (response: any) => {
  if (response.code === 500 || response.code === 2) {
    ElMessage.error(response.msg || '上传失败!');
    clearFile();
  } else {
    emit('success');
    ElMessage.success('上传成功!');
    close();
  }
};
/**
 * @description 上传失败
 */
const onError: UploadProps['onError'] = () => {
  ElMessage.error('上传失败!');
  clearFile();
};
/**
 * @description 文件删除
 */
const onRemove: UploadProps['onRemove'] = () => {
  state.form.file = '';
};
const open = () => {
  state.visible = true;
};
defineExpose({
  open
});
</script>

<style lang="scss" scoped>
.upload-demo {
  width: 90%;
}
</style>

【组件】同上 单个上传组件

<template>
  <el-upload
    ref="fileRef"
    style="width: 100%"
    :auto-upload="true"
    :data="props.data"
    :limit="props.limit"
    :action="url"
    :on-exceed="handleExceed"
    :on-success="onSuccess"
    :on-error="onError"
    :on-remove="onRemove"
    :before-upload="beforeAvatarUpload"
    :headers="uploadHeader()"
    :accept="accept"
    :file-list="props.fileList"
  >
    <slot><el-button type="primary">上传附件</el-button></slot>
    <template #tip>
      <div class="el-upload__tip">{{ tips }}</div>
    </template>
  </el-upload>
</template>

<script lang="ts">
export default { name: 'UploadButton' };
</script>
<script lang="ts" setup>
import {
  ElMessage,
  UploadInstance,
  UploadProps,
  UploadRawFile,
  UploadUserFile,
  genFileId
} from 'element-plus';
import { reactive, ref } from 'vue';
import { useUpload } from '@/hooks/useUpload';
import { baseSettingUrl, baseUrl } from '@/constant';
import { FileType } from '@/types/common';

const props = defineProps({
  title: {
    type: String,
    default: '文件上传'
  },
  limit: {
    type: Number,
    default: 1
  },
  url: {
    type: String,
    default: `${baseUrl}${baseSettingUrl}/file/upload`
  },
  data: {
    type: Object,
    default: () => {}
  },
  accept: {
    type: String,
    default: () => {
      return `${FileType.XLS},${FileType.XLSX},${FileType.ZIP}`;
    }
  },
  tips: {
    type: String,
    default: '文件大小不超过2M'
  },
  fileList: {
    type: Array<UploadUserFile>,
    default: () => [] as UploadUserFile[]
  }
});

const emits = defineEmits(['success', 'update:file-list']);
const state = reactive({
  file: ''
});
const fileRef = ref<UploadInstance>();
const { uploadHeader } = useUpload();

/**
 * @description 文件上传之前验证
 */
const beforeAvatarUpload: UploadProps['beforeUpload'] = rawFile => {
  if (
    !(
      rawFile.type === FileType.XLS ||
      rawFile.type === FileType.XLSX ||
      rawFile.type === FileType.ZIP
    )
  ) {
    ElMessage.warning('请上传EXCLE表格或者ZIP压缩包!');
    return false;
  } else if (rawFile.size / 1024 / 1024 > 2) {
    ElMessage.warning('文件大小不超过2M!');
    return false;
  }
  return true;
};

/**
 * @description 覆盖前一个文件
 */
const handleExceed: UploadProps['onExceed'] = files => {
  fileRef.value!.clearFiles();
  const file = files[0] as UploadRawFile;
  file.uid = genFileId();
  fileRef.value!.handleStart(file);
  fileRef.value?.submit();
};
/**
 * @description 清空数据
 */
const clearFile = () => {
  fileRef.value?.clearFiles();
  state.file = '';
};
/**
 * @description 上传成功
 */
const onSuccess: UploadProps['onSuccess'] = (response: any) => {
  if (response.code === 500) {
    ElMessage.error('上传失败!');
    clearFile();
  } else {
    emits('success', response);
  }
};
/**
 * @description 上传失败
 */
const onError: UploadProps['onError'] = () => {
  ElMessage.error('上传失败!');
  clearFile();
};
/**
 * @description 文件删除
 */
const onRemove: UploadProps['onRemove'] = () => {
  state.file = '';
};

defineExpose({
  clearFile
});
</script>

<style lang="scss" scoped></style>

【组件】封装展示表格和可修改表格

仅做展示

<template>
  <el-table
    :data="props.data"
    border
    :max-height="props.maxHeight"
    :row-key="rowKey"
    :highlight-current-row="true"
    ref="tableRef"
    @row-click="rowClick"
    :span-method="props.spanMethod"
  >
    <slot name="index">
      <el-table-column
        type="index"
        label="序号"
        width="60"
        align="center"
      ></el-table-column>
    </slot>
    <el-table-column
      v-for="col in props.column"
      :key="col.prop"
      :prop="col.prop"
      :label="col.label"
      :show-overflow-tooltip="col.showOverflowTooltip === false ? false : true"
      align="center"
      :width="col.width ? col.width : 'auto'"
    >
      <!-- 多级表头 -->
      <template v-if="col.children">
        <el-table-column
          v-for="child in col.children"
          :key="child.prop"
          :prop="child.prop"
          :label="child.label"
          :show-overflow-tooltip="true"
          align="center"
          :width="child.width ? child.width : 'auto'"
        >
          <!-- 多级表头并且有父子级关系 子级字段相同手动处理数据后展示 -->
          <template #default="{ row }" v-if="col.template === 'children'">
            <span
              v-if="
                child.prop === 'exceedEstimate' || child.prop === 'exceedRange'
              "
              :class="[getDangerClass(row[col.prop][child.prop])]"
              >{{ row[col.prop][child.prop] }}</span
            >
            <span v-else>{{ row[col.prop][child.prop] }}</span>
          </template>
          <!-- 没有父子级关系 字段不同 手动加children处理成表头格式展示 -->
          <template #default="{ row }" v-else>
            <span
              v-if="
                child.prop === 'exceedEstimate' || child.prop === 'exceedRange'
              "
              :class="[getDangerClass(row[child.prop])]"
              >{{ row[child.prop] }}</span
            >
            <span v-else>{{ row[child.prop] }}</span>
          </template>
        </el-table-column>
      </template>
      <!-- 处理时间格式 -->
      <template v-if="col.template === 'date'" #default="{ row }">{{
        dateFormat(row[col.prop], 'yyyy-MM-dd')
      }}</template>
      <!-- 处理 0/1 -->
      <template v-else-if="col.template === 'trueOrFalse'" #default="{ row }">
        <el-tag :type="row[col.prop] === 0 ? 'success' : 'info'" size="small">
          {{ row[col.prop] === 0 ? '是' : '否' }}
        </el-tag>
      </template>
      <!-- 处理状态 例如el-tag对应不同颜色 -->
      <template v-else-if="col.template === 'status'" #default="{ row }">
        <slot name="status" :row="row"></slot>
      </template>
      <!-- 对齐方式 -->
      <template
        v-else-if="col.template === 'left' || col.template === 'right'"
        #default="{ row }"
      >
        <div :style="{ textAlign: col.template }" class="text-overflow">
          {{ row[col.prop] }}
        </div>
      </template>
      <!-- 下载 -->
      <template v-else-if="col.template === 'download'" #default="{ row }">
        <DownloadButton
          v-if="row[col.prop]"
          :id="row[getDownloadId(col.prop)]"
          :mime="getFileType(row[col.prop])"
          >{{ row[col.prop] }}</DownloadButton
        >
      </template>
      <template v-else-if="col.prop === 'evalType'" #default="{ row }">{{
        row.evalTyp === 1 ? '油藏' : '气藏'
      }}</template>
      <template v-else-if="col.prop === 'afterYield'" #default="{ row }">{{
        row.afterYield ? row.afterYield + '%' : ''
      }}</template>
    </el-table-column>
  </el-table>
</template>

<script lang="ts">
export default { name: 'SimpleTable' };
</script>
<script lang="ts" setup>
import { FileType, TableColumn } from '@/types/common';
import { dateFormat } from '@/utils/filter';
import { useTableClick } from '@/utils/tools';
import { TableColumnCtx, TableInstance } from 'element-plus';
import { computed, ref } from 'vue';
import DownloadButton from '@/components/Upload/DownloadButton/index.vue';

const tableRef = ref<TableInstance>();
const emit = defineEmits(['rowClick']);
const props = defineProps<{
  data: object[];
  column: TableColumn[];
  rowKey?: string;
  maxHeight: number | string;
  spanMethod?: (data: {
    row: any;
    rowIndex: number;
    column: TableColumnCtx<any>;
    columnIndex: number;
  }) =>
    | number[]
    | {
        rowspan: number;
        colspan: number;
      }
    | undefined;
}>();
const rowKey = computed(() => {
  return props.rowKey ? props.rowKey : 'id';
});
const { tableRowData, tableRowClick, clearTableClick } = useTableClick(
  rowKey.value
);
const rowClick = (row: any) => {
  tableRowClick(row, tableRef.value);
  emit('rowClick', tableRowData.value);
};
const getDangerClass = (value: string | number) => {
  return Number(value) > 0 ? 'danger' : '';
};
type type = keyof typeof FileType;
/**
 * @description 获取文件后缀
 */
const getFileType = (name: string) => {
  if (name) {
    const typeArr = name.split('.');
    const realType = typeArr[typeArr.length - 1].toUpperCase() as type;
    return FileType[realType];
  }
  return;
};
/**
 * @description 获取文件Id
 */
const getDownloadId = (key: string) => {
  if (key) {
    return key.replace(/(Url)/, 'Id');
  }
  return;
};
defineExpose({
  clearTableClick: () => clearTableClick(tableRef.value)
});
</script>

<style lang="scss" scoped>
.danger {
  color: var(--el-color-danger);
  font-weight: bold;
}
</style>

可修改表格值

<template>
  <el-table
    :data="props.data"
    border
    :max-height="props.maxHeight"
    :highlight-current-row="true"
    ref="tableRef"
    :row-key="rowKey"
    @row-click="rowClick"
    :span-method="props.spanMethod"
  >
    <slot name="index"> </slot>
    <el-table-column
      v-for="col in props.column"
      :key="col.prop"
      :prop="col.prop"
      :label="col.label"
      :show-overflow-tooltip="false"
      :align="col.align ? col.align : 'center'"
      :width="col.width ? col.width : 'auto'"
    >
      <template #default="{ row }">
        <div v-show="props.isEdit">
          <div v-if="row.isEditor === 1 && col.canEdit" @click.stop>
            <el-input-number
              v-model="row[col.prop]"
              :controls="false"
              :min="0"
              :max="9999999999"
              v-if="col.isNumber"
              @blur="changeValue(row)"
            ></el-input-number>
            <el-input
              v-model="row[col.prop]"
              v-else
              maxlength="10"
              @blur="changeValue(row)"
            ></el-input>
          </div>
          <div v-else-if="row.isEditor === 2 && col.canEditOther" @click.stop>
            <el-input-number
              v-model="row[col.prop]"
              :controls="false"
              :min="0"
              :max="9999999999"
              v-if="col.isNumber"
              @blur="changeValue(row)"
            ></el-input-number>
            <el-input
              v-model="row[col.prop]"
              v-else
              maxlength="10"
              @blur="changeValue(row)"
            ></el-input>
          </div>
          <div v-else>
            <div v-if="col.template === 'left' || col.template === 'right'">
              <div :style="{ textAlign: col.template }" class="text-overflow">
                {{ row[col.prop] }}
              </div>
            </div>
            <div v-else>{{ row[col.prop] }}</div>
          </div>
        </div>
        <div v-show="!props.isEdit">
          <div v-if="col.template === 'left' || col.template === 'right'">
            <div :style="{ textAlign: col.template }" class="text-overflow">
              {{ row[col.prop] }}
            </div>
          </div>
          <div v-else>{{ row[col.prop] }}</div>
        </div>
      </template>
    </el-table-column>
  </el-table>
</template>

<script lang="ts">
export default { name: 'SimpleTable' };
</script>
<script lang="ts" setup>
import { TableColumn } from '@/types/common';
import { useTableClick } from '@/utils/tools';
import { TableColumnCtx, TableInstance } from 'element-plus';
import { computed, ref } from 'vue';

const props = defineProps<{
  data: object[];
  column: TableColumn[];
  maxHeight: number | string;
  isEdit: boolean;
  rowKey?: string;
  spanMethod?: (data: {
    row: any;
    rowIndex: number;
    column: TableColumnCtx<any>;
    columnIndex: number;
  }) =>
    | number[]
    | {
        rowspan: number;
        colspan: number;
      }
    | undefined;
}>();
const tableRef = ref<TableInstance>();
const emits = defineEmits(['change', 'rowClick']);
const changeValue = (row: any) => {
  emits('change', row);
};
const rowKey = computed(() => {
  return props.rowKey ? props.rowKey : 'id';
});
const { tableRowData, tableRowClick, clearTableClick } = useTableClick(
  rowKey.value
);
const rowClick = (row: any) => {
  tableRowClick(row, tableRef.value);
  emits('rowClick', tableRowData.value);
};
defineExpose({
  clearTableClick: () => clearTableClick(tableRef.value)
});
</script>

<style lang="scss" scoped></style>

可通过简单的column配置进行展示

/**
 * @description 表格
 * @export
 * @interface TableColumn
 */
export interface TableColumn {
  prop: string;
  label: string;
  template?: string;
  align?: string;
  width?: string;
  isNumber?: boolean;
  canEdit?: boolean;
  canEditOther?: boolean;
  children?: TableColumn[];
  showOverflowTooltip?: boolean;
}
export const preDrillingEngineeringColumn: TableColumn[] = [
  {
    prop: 'year',
    label: '年度',
    width: '70'
  },
  {
    prop: 'projectName',
    label: '项目名称',
    template: 'left'
  },
  {
    prop: 'startDate',
    label: '开工日期',
    template: 'date',
    width: '100'
  },
  {
    prop: 'amount',
    label: '金额',
    canEdit: true,
    isNumber: true
  },
  {
    prop: 'costFileUrl',
    label: '造价文件',
    template: 'download',
    width: '120'
  },
  {
    prop: 'landFee',
    label: '土地费(万元)',
    template: 'children',
    children: [
      {
        prop: 'landFeeEstimate',
        label: '概算',
        width: '80'
      },
      {
        prop: 'landFeeSettlement',
        label: '结算',
        width: '80'
      },
      {
        prop: 'landFeeDetermine',
        label: '决算',
        width: '80'
      }
    ]
  },
]

【方法】【问题】解决大屏全屏 手动触发/F11/ESC冲突的问题

全屏方法

import { ref } from 'vue';

const useFullScreen = () => {
  const isFullscreen = ref(false);
  const enterFullscreen = (element: HTMLElement) => {
    if (element.requestFullscreen) {
      element
        .requestFullscreen()
        .then(() => {
          isFullscreen.value = true;
        })
        .catch(() => {
          // iframe下全屏报错 新开窗口
          window.open(
            'http://xxx',
            '_blank'
          );
        });
    }
    // else if (element.mozRequestFullScreen) { /* Firefox */
    //     element.mozRequestFullScreen();
    // } else if (element.webkitRequestFullscreen) { /* Chrome, Safari & Opera */
    //     element.webkitRequestFullscreen();
    // } else if (element.msRequestFullscreen) { /* IE/Edge */
    //     element.msRequestFullscreen();
    // }
  };
  const exitFullscreen = () => {
    if (document.exitFullscreen) {
      document
        .exitFullscreen()
        .then(() => {
          isFullscreen.value = false;
        })
        .catch(() => {
          // iframe下全屏报错 新开窗口
          window.open(
            'http://xxx',
            '_blank'
          );
        });
    }
    // else if (document.mozCancelFullScreen) { /* Firefox */
    //     document.mozCancelFullScreen();
    // } else if (document.webkitExitFullscreen) { /* Chrome, Safari and Opera */
    //     document.webkitExitFullscreen();
    // } else if (document.msExitFullscreen) { /* IE/Edge */
    //     document.msExitFullscreen();
    // }
  };
  return {
    isFullscreen,
    enterFullscreen,
    exitFullscreen
  };
};
export default useFullScreen;

监听全屏和键盘事件,注意F11进入全屏后,再次F11无法触发keydown,需要在fullscreenchange中处理

const fullscreenHandler = () => {
  if (document.fullscreenElement) {
    // 浏览器已进入全屏模式
    nextTick(() => {
      state.width = window.screen.width;
      state.height = window.screen.height;
      // 全屏动画结束后设置宽高比例
      requestAnimationFrame(setStyle);
    });
  } else {
    // 浏览器已退出全屏模式
    nextTick(() => {
      state.width = window.innerWidth;
      state.height = window.innerHeight;
      // 全屏动画结束后设置宽高比例
      requestAnimationFrame(setStyle);
      isFullscreen.value = false; //这里设置是因为全屏之后按F11无法触发keydown,也就无法赋值
    });
  }
};
const keydownHandler = (event: any) => {
  if (event.keyCode === 122) {
    // F11 key
    event.preventDefault();
    enterFullscreen(document.getElementById('fullscreen')!);
  }

  if (event.keyCode === 27) {
    // ESC key
    event.preventDefault();
    exitFullscreen();
  }
};
onMounted(() => {
  document.addEventListener('fullscreenchange', fullscreenHandler);
  document.addEventListener('keydown', keydownHandler);
});
onDeactivated(() => {
  document.removeEventListener('fullscreenchange', fullscreenHandler);
  document.removeEventListener('keydown', keydownHandler);
});
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值