2023年1~3月前端学习笔记

2023.01.12

1、使用computed实时计算按钮的颜色

<div :class="className" @click="handleClick">去认证</div>

const className = computed(() => {
    const pattern =
        /^[1-9]\d{5}(18|19|20|(3\d))\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/
    const rerus = codeId.value === '000000000000000000'
    if ((pattern.test(codeId.value) && name.value && !twoToken.value) || rerus) {
        return 'custom-btn custom-btn-active'
    }
    return 'custom-btn'
})

2023.02.03

1、获取当前页面的路由

console.log(router.currentRoute.value)
console.log(router.currentRoute.value.fullPath)

2、解决vite+vue3项目中不能使用require导入图片的问题

在vite+vue3项目中是没有require的,使用require会报错。

解决方法1:

const tabLists = [
  {
    icon: new URL("../assets/accountManage.png", import.meta.url).href,
    name: "账号管理",
  }
];

解决方法2:在asset 前面加上src。

<img :src="'/src/assets/img/unbind_success.png'" alt="" />

3、vue3 在关闭弹窗之后暂停video或audio的播放

video与audio的写法一致。直接套用这个代码也没问题

<template>
    <el-dialog
      v-model="materialVisible"
      title=""
      :close-on-click-modal="false"
      @close="handlePause"
    >
        <audio
          controls
          ref="audio" //重要
          class="audioStyle"
          @play="isPlay = true"
          @pause="isPlay = false"
        >
          <source src="../../assets/Autumn morning.mp3" type="audio/ogg" />
          <source src="../../assets/Autumn morning.mp3" type="audio/mpeg" />
        </audio>
    </el-dialog>
</template>
<script lang="ts" setup>
    const isPlay = ref(false);
    const audio = ref(null) as any;
    const handlePause = () => {
      audio.value.pause();
    };
</script>

2023.02.09

1、隐藏html默认滚动条

.wrap {
  width: 100%;
  overflow-y: scroll;
}
::-webkit-scrollbar {
  display: none;
}

2、获取html一屏幕的窗口高度

window.innerHeight

3、当display: none; 与 display: flex; 相遇,其一作用失效。

解决办法:在子元素的外层,原来父元素的内层套一个盒子,比如divdisplay: none;就能作用了。

<div class="imgListFree">
  <div class="imgList">
    <img src="./img/imgList1.png" alt="" />
    <img src="./img/imgList2.png" alt="" />
    <img src="./img/imgList3.png" alt="" />
    <img src="./img/imgList4.png" alt="" />
    <img src="./img/imgList5.png" alt="" />
    <img src="./img/imgList6.png" alt="" />
  </div>
</div>
if (i === 0) {
  imgListFree.style.display = "block";
} else {
  imgListFree.style.display = "none";
}
.imgListFree {
  display: block;
}
.imgList {
  display: flex;
  justify-content: space-between;
  padding-top: 20px;
}
.imgList img {
  background: rgba(218, 219, 224, 1);
  width: 15.5%;
  border-radius: 12px;
}

2023.02.11

1、原生js Ajax请求接口数据

window.onload = function () {
  var xhr = new XMLHttpRequest();
  //建立连接
  xhr.open(
    "GET",
    "https://xxxx/api/article/detail",
    true
  );
  //参数1:请求的方式  post  get
  //参数2:url地址
  //参数3:是否异步  布尔值,默认为true,若为false,则为同步;

  xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
  //设置请求头信息
  xhr.send(null);
  xhr.onreadystatechange = function () {
    if (xhr.readyState === 4 && xhr.status === 200) {
      var data = JSON.parse(xhr.responseText);
      console.log(data);
    }
  };
};

2023.02.17

1、遇到的坑:点击删除按钮,删除按钮变蓝,其他不变还是灰色

在获取到后端接口数据时,循环数组添加icon_src字段,当点击删除图标时,更换该图标的src。

2、遇到的坑:一个页面怎么发送两次ajax请求

解决办法:把第二个请求放在第一个请求成功的里面(也就是回调套回调,但是真的不会地狱回调嘛?所以promise应运而生,避免层层的回调)

2023.02.20

1、遇到的坑:vue 视频播放 切换视频地址后还播放着前一个视频

问题描述:使用的是elementn plus的dialog弹窗,弹出弹窗播放视频。即使console.log出来的视频地址是对的,但是弹窗上播放的还是前一个视频。

解决办法:在dialog标签中添加 v-if 判断。

原理:v-if 与 v-show 都表示显示隐藏。但是v-if成立后会重新创建,不成立时就会消除代码块。换句话说就是成立时创建video标签,不成立则销毁video标签。而v-show 则只是控制其样式隐藏,并没有对其销毁。

2、element plus:table表格自定义列模板

vue3 + element plus的table表格。

表格自定义的东西都在父组件中书写。首先在父项定义加一个参数slot。

父组件:

<Table
  v-if="totalData > 0"
  :lists="lists"
  :table-data="tableData"
  :total="totalData"
  @prev-click.stop="prevClick"
  @next-click.stop="nextClick"
  @current-change="currentChange"
>
  <template #pay_status="{ row }">
    <-- 调用slot传过来的数据row -->
    <span class="status1">{{ row.pay_status }}</span>
    <el-button
      v-if="row.pay_status === '未支付'"
      link
      type="primary"
      class="operation1"
      size="small"
      @click="handlePay(row)"
    >
      去支付
    </el-button>
  </template>
</Table>

const lists = [
  { prop: "created_at", label: "订单时间", width: "130" },
  { prop: "pay_status", label: "状态", width: "100", slot: "pay_status" },
];

自定义模板,如果有slot参数,就可以调用。如果没有,就显示默认的。

对应的项的list.slot值是啥,自定义模板的slot插槽的name就是啥。

子组件:

<el-table
  :data="tableData"
  empty-text="暂无数据"
  style="width: 100%"
  border
>
  <el-table-column
    v-for="(list, index) in (lists as any)"
    :min-width="list.width"
    :height="150"
    :key="index"
    :prop="list.prop"
    :label="list.label"
  >
    <!-- 自定义模板 -->
    <template v-if="list.slot" #default="{ row, $index }">
      <slot :name="list.slot" :row="row" :index="$index"></slot>
    </template>
    <template v-else #default="{ row }">
      <span>{{ row[list.prop] || "" }}</span>
    </template>
  </el-table-column>
</el-table>

2023.03.02

1、改变element plus table表格的表头颜色

:deep(.el-table th.el-table__cell) {
    background-color: rgba(245, 245, 245, 1);
}

2023.03.08

1、element 重置form表单

1.将数据绑定在el-form表单的model

2.给el-form-item加上prop属性。

import type { FormInstance } from 'element-plus'

const ruleForm = ref<FormInstance>()

// 表单重置方法
const resetForm = () => {
  ruleForm.value?.resetFields()
}
// 或者是另一种方法
const resetForm = (formEl: FormInstance | undefined) => {
  if (!formEl) return;
  formEl.resetFields();
};

2023.03.17

1、二次封装element form表单

子组件:

<template>
  <div class="form">
    <el-form
      ref="ruleFormRef"
      :model="formData"
      :label-width="props.labelWidth"
      :label-position="labelPosition"
      status-icon
      :class="layoutClass"
    >
      <template v-for="item in props.formItems" :key="item.label">
        <el-form-item
          :label="item.label"
          :prop="item.prop"
          :rules="item.rules"
          :class="item.class"
        >
          <!-- 文本input框/密码框 -->
          <template
            v-if="
              item.type === 'text' ||
              item.type === 'textarea' ||
              item.type === 'password'
            "
          >
            <el-input
              :type="item.type"
              :placeholder="item.placeholder"
              :maxlength="item.maxLength"
              show-word-limit
              :rows="6"
              v-model="formData[`${item.field}`]"
            ></el-input>
            <div v-if="item.slot" class="addIcon" @click="handleAdd">
              <img src="@/assets/img/icon_add2.png" alt="" />
            </div>
          </template>
          <!-- 选择器 -->
          <template v-else-if="item.type === 'select'">
            <el-select
              :placeholder="item.placeholder"
              v-model="formData[`${item.field}`]"
            >
              <el-option
                v-for="option in item.options"
                :key="option.value"
                :value="option.value"
                :label="option.label"
              />
            </el-select>
          </template>
          <!-- 级联选择器 -->
          <template v-else-if="item.type === 'cascader'">
            <el-cascader
              v-model="formData[`${item.field}`]"
              :options="item.options"
              :show-all-levels="false"
              :placeholder="item.placeholder"
            />
          </template>
          <!-- 日期选择器 -->
          <template v-else-if="item.type === 'date'">
            <el-date-picker
              v-model="formData[`${item.field}`]"
              type="date"
              range-separator="至"
              :placeholder="item.placeholder"
              end-placeholder="End date"
            />
          </template>
          <!-- 日期范围选择器 -->
          <template v-else-if="item.type === 'daterange'">
            <el-date-picker
              v-model="formData[`${item.field}`]"
              type="daterange"
              range-separator="至"
              start-placeholder="开始时间"
              :placeholder="item.placeholder"
              end-placeholder="结束时间"
            />
          </template>
          <!-- 文件上传 -->
          <template v-else-if="item.type === 'upload'">
            <el-upload
              ref="item.prop"
              class="upload-demo"
              action="https://run.mocky.io/v3/9d059bf9-4660-45f2-925d-ce80ad6c4d15"
              :auto-upload="false"
            >
              <div class="uploadFile">
                <el-icon :size="33" color="rgba(196,196,196,1)">
                  <Plus />
                </el-icon>
                <div>上传文件</div>
              </div>
            </el-upload>
            <div class="tipBox" v-html="item.html"></div>
          </template>
          <!-- 单选框 -->
          <template v-else-if="item.type === 'radio'">
            <el-radio-group v-model="formData[`${item.field}`]">
              <el-radio
                v-for="option in item.options"
                :key="option.value"
                :label="option.label"
              /> </el-radio-group
          ></template>
          <!-- 单个多选框 -->
          <template v-else-if="item.type === 'checkbox'">
            <el-checkbox
              v-model="formData[`${item.field}`]"
              :label="item.placeholder"
            />
            <!-- <el-checkbox-group v-model="formData[`${item.field}`]">
              <el-checkbox
                v-for="option in item.options"
                :key="option.value"
                :label="option.label"
                :name="item.type"
              />
            </el-checkbox-group> -->
          </template>
        </el-form-item>
      </template>
      <el-form-item v-if="showSubmit">
        <div class="btn">
          <el-button v-show="showCancel" @click="handleCancel(ruleFormRef)"
            >取消</el-button
          >
          <el-button type="primary" @click="onSubmit(ruleFormRef)">{{
            confirmText
          }}</el-button>
        </div>
      </el-form-item>
    </el-form>
  </div>
</template>
<script setup lang="ts">
import { IFormItem } from "../store/types/form";
import { ref, watch } from "vue";
import type { FormInstance } from "element-plus";
import { Plus } from "@element-plus/icons-vue";

const ruleFormRef = ref<FormInstance>();

const props = defineProps({
  modelValue: {
    type: Object, // 双向绑定
    required: true,
  },
  formItems: {
    type: Array as () => IFormItem[], // 组件数据
    default: () => [],
  },
  labelWidth: {
    type: String,
    default: "210px",
  },
  labelPosition: {
    type: String,
    default: "left",
  },
  showSubmit: {
    type: Boolean,
    default: false,
  },
  layoutClass: {
    type: String,
    default: "formItem",
  },
  showCancel: {
    type: Boolean,
    default: false,
  },
  confirmText: {
    type: String,
    default: "确定",
  },
});

const emits = defineEmits([
  "update:modelValue",
  "handleAdd",
  "handleCancel",
  "onSubmit",
]);

const handleAdd = () => {
  emits("handleAdd", false);
};

const handleCancel = (formEl: FormInstance | undefined) => {
  if (!formEl) return;
  formEl.resetFields();
};

const onSubmit = async (formEl: FormInstance | undefined) => {
  if (!formEl) return;
  await formEl.validate((valid, fields) => {
    if (valid) {
      console.log(formData.value);
      emits("onSubmit", formData.value);
    } else {
      console.log("error submit!", fields);
    }
  });
};

// 获取值:{ ...props.modelValue} 进行浅拷贝,避免破坏props单向数据流。
// v-model="formData[`${item.field}`]"
const formData = ref({ ...props.modelValue });

// 进行深度监听,实现组件双向绑定
watch(
  formData,
  (newValue) => {
    emits("update:modelValue", newValue);
  },
  {
    deep: true,
  }
);
</script>
<style scoped lang="scss">
.form {
  margin-top: 20px;
  .formItem {
    :deep(.el-form-item) {
      width: 860px;
      margin-bottom: 22px;
    }
    :deep(.el-form-item__label) {
      font-size: 16px;
    }
  }
  .formDialog {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    :deep(.el-form-item) {
      width: 400px;
      margin-bottom: 22px;
    }
    :deep(.el-form-item__label) {
      font-size: 14px;
    }
    .btn {
      width: 200px;
      :deep(.el-button) {
        margin: 0;
      }
    }
  }
  :deep(.el-form-item__content) {
    align-items: flex-start;
  }
  :deep(.el-form-item__label) {
    font-weight: bold;
  }
  :deep(.el-input) {
    height: 38px;
  }
  .el-select {
    width: 100%;
  }
  :deep(.el-cascader) {
    width: 100%;
  }
  :deep(.el-date-editor.el-input) {
    width: 100%;
  }
  .uploadFile {
    width: 120px;
    height: 120px;
    border-radius: 4px;
    background: rgba(255, 255, 255, 1);
    border: 1px solid rgba(204, 204, 204, 1);
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    div {
      font-size: 14px;
      font-weight: 400;
      color: rgba(171, 174, 181, 1);
    }
  }
  .tipBox {
    margin-left: 30px;
    :deep(.redFont) {
      font-size: 14px;
      font-weight: 400;
      color: rgba(245, 108, 108, 1);
    }
    :deep(div) {
      font-size: 14px;
      font-weight: 400;
      line-height: 20px;
      color: rgba(171, 174, 181, 1);
    }
  }
  :deep(.el-radio__inner) {
    width: 24px;
    height: 24px;
  }
  :deep(.el-radio__inner::after) {
    width: 8px;
    height: 8px;
  }
  :deep(.el-radio__label) {
    font-size: 16px;
    font-weight: 700;
    color: rgba(85, 85, 85, 1);
    margin: 10px 0;
  }
  :deep(.el-radio) {
    margin-bottom: 20px;
    margin-right: 42px;
    &:last-child {
      margin-right: 0;
    }
  }
  :deep(.el-checkbox) {
    font-size: 14px;
    font-weight: 400;
    color: rgba(171, 174, 181, 1);
  }
  :deep(.el-checkbox__inner) {
    width: 22px;
    height: 22px;
  }
  :deep(.el-checkbox__inner::after) {
    width: 6px;
    height: 12px;
    left: 6px;
  }
  .addIcon {
    cursor: pointer;
    position: absolute;
    top: 1px;
    right: -35px;
    img {
      width: 24px;
      height: 24px;
    }
  }
  .btn {
    margin: 0 auto;
    :deep(.el-button) {
      width: 114px;
      height: 40px;
      margin: 0 15px;
    }
  }
}
</style>

父组件:

<template>
  <div id="addBox">
      <Form
        v-bind="addRightOwnerConfig"
        v-model="formData"
        :label-width="'140px'"
        :label-position="'right'"
        :layout-class="'formDialog'"
        :show-submit="true"
        @on-submit="onSubmit"
      ></Form>
  </div>
</template>
<script lang="ts" setup>
import Form from "./Form.vue";
import { ref } from "vue";
import { addRightOwnerConfig } from "@/store/types/addRightOwnerConfig";

// 获取数据:
const formItems = addRightOwnerConfig.formItems ?? [];
// 对数据进行初始化
const formOriginData: any = {};
for (const item of formItems) {
  formOriginData[item.field] = "";
}
// 把初始化的数据进行赋值
const formData = ref(formOriginData);

const emits = defineEmits([ "confirmAdd"]);

const onSubmit = (form: any) => {
  emits("confirmAdd", form);
};
</script>

配置文件:(addRightOwnerConfig.ts文件)

import { IForm } from "./form";
export const addRightOwnerConfig: IForm = {
  formItems: [
    {
      field: "type",
      type: "select",
      label: "类型:",
      prop: "type",
      placeholder: "请选择类型",
      options: [
        { label: "足球", value: "33" },
        { label: "篮球", value: "22" },
        { label: "排球", value: "11" },
      ],
      rules: [
        {
          required: true,
          message: "请选择类型",
        },
      ],
    },
    {
      field: "name",
      type: "text",
      label: "姓名或名称:",
      prop: "name",
      placeholder: "请输入姓名或名称",
      rules: [
        {
          required: true,
          message: "请输入姓名或名称",
        },
      ],
    },
    {
      field: "IDType",
      type: "select",
      label: "证件类型:",
      prop: "IDType",
      placeholder: "请选择证件类型",
      options: [
        { label: "足球", value: "33" },
        { label: "篮球", value: "22" },
        { label: "排球", value: "11" },
      ],
      rules: [
        {
          required: true,
          message: "请选择证件类型",
        },
      ],
    },
    {
      field: "identificationNumber",
      type: "text",
      label: "证件号码:",
      prop: "identificationNumber",
      placeholder: "请输入证件号码",
      rules: [
        {
          required: true,
          message: "请输入证件号码",
        },
      ],
    },
    {
      field: "phone",
      type: "text",
      label: "电话号码:",
      prop: "phone",
      placeholder: "请输入电话号码",
    },
  ],
};

表单类型:(form.d.ts文件)

// 表单中的组件类型
type IFormType =
  | "text"
  | "textarea"
  | "password"
  | "select"
  | "cascader"
  | "date"
  | "daterange"
  | "upload"
  | "radio"
  | "checkbox"
  | "submit";

/**
 * 表单所需要的数据类型
field:双向绑定关键字
type:表单中组件的类型(通过type进行匹配:比如:text是一个文字输入框,password则是密码框)
label  标签名称
prop
maxLength 输入框最大输入限制
placeholder 提醒文字
options  数据(比如select )
rules 验证规则
 */
export interface IFormItem {
  field: string;
  type: IFormType;
  label?: string;
  prop?: string;
  maxLength?: number;
  placeholder?: string | number;
  options?: any[];
  rules?: any[];
  html?: any;
  slot?: any;
}

// 表单的配置
export interface IForm {
  formItems: IFormItem[];
  labelWidth?: string;
}

2、router策略模式

要求:当刷新页面的时候,active的样式还是在原来的url上,而不是跑到url为 '/' 的tab上。

以下是原代码,又臭又长的,受不了了。

下面是用策略模式改过的:

2023.03.28

1、js使用对象解构删除对象属性

方法:使用对象解构。

应用场景:前端的表单对象不需要全部提交至后端时。

const ruleForm = reactive({
  name: "",
  phone: userInfo.value.mobile,
  email: "",
});

// 使用对象解构将对象中的phone属性除去
const { phone, ...newData } = ruleForm;
console.log(newData)// 新的对象

2、解决vue中v-html元素标签样式失效的问题

解决办法:使用deep scoped来实现对v-html的样式应用,并且不设置为全局

deep选择器在css中的写法为>>>

>>> p {
  font-size: 18px;
}

可惜>>>在sass/less中不生效,可以使用:deep()

:deep(p){
    font-size: 18px;
}

如果不生效,也可以试试 : :v-deep 或者 /deep/ 呢

::v-deep p{
    font-size: 18px;
}

// 或者
/deep/ p{
    font-size: 18px;
}

3、element plus:表格table+分页pagination组件

1.table组件

<el-table
  :data="tableData"
  empty-text="暂无数据"
  style="width: 100%"
  border
  @row-click="rowClick"
>
  <el-table-column
    v-for="(list, index) in (lists as any)"
    :min-width="list.width"
    :height="150"
    :key="index"
    :prop="list.prop"
    :label="list.label"
  >
    <!-- v-if 命名插槽 -->
    <template v-if="list.slot" #default="{ row, $index }">
      <slot :name="list.slot" :row="row" :index="$index"></slot>
    </template>
    <!-- v-else 显示默认内容 -->
    <template v-else #default="{ row }">
      <span>{{ row[list.prop] }}</span>
    </template>
  </el-table-column>
</el-table>
<div class="pagination">
  <el-pagination
    small
    hide-on-single-page
    v-model:current-page="currentPage"
    v-model:page-size="pageSize"
    layout="total, prev, pager, next, jumper"
    :total="total"
    @current-change="handleCurrentChange"
  />
</div>

2.页面中使用table组件

<Table
    v-if="totalData > 0"
    :lists="lists"
    :table-data="tableData"
    :total="totalData"
    @handle-current-change="currentChange"
    @row-click="rowClick"
    >
    <template #type="{ row }">
      <span>{{ typeOwnerObj[row.type] }}</span>
    </template>
</Table>
<Empty v-else :show="true"></Empty>
import Table from "../../components/Table.vue";
import Empty from "../../components/Empty.vue";

const currentChange = (val: number) => {
  parameter.page = val;
  store.dispatch("user/userList", parameter);
};

const rowClick = (row: any) => {
  router.push({
    path: "/detail",
    query: {
      oid: row.id,
    },
  });
};

2023.03.29

1、vue3下载后端返回的pdf(数据流)

请求的接口,返回的东西如下:

解决代码如下:

// 证书下载API
export const downloadCert = (cid: number) => {
  return request({
    url: `/api/xxxxxxxxxx/download?cid=${cid}`,
    responseType: "arraybuffer", //重点,否则下载的pdf会空白!!!
  });
};
// 页面中调用API接口
try {
  const res = await store
    .dispatch("copyright/downloadCert", detail.value.id)
    .then((response: any) => {
      downloadCert(response); //调用downloadCert()方法
    });
} catch (error: any) {
  console.log(error.message);
}
// downloadCert()方法
const downloadCert = (data: any) => {
  if (!data) {
    return;
  }
  // 创建a标签,设置download属性,插入到文档中并click
  let url = window.URL.createObjectURL(
    new Blob([data], {
      type: "application/pdf;chartset=UTF-8",
    })
  );
  let link = document.createElement("a");
  link.style.display = "none";
  link.href = url;
  link.setAttribute("download", "证书.pdf");
  document.body.appendChild(link);
  link.click();
};

2、vue3通过scrollIntoView来实现锚点定位

1.子组件

<div class="tabList">
  <div
    :class="tabActive === '#home' ? 'active' : ''"
    @click="goAnchor('#home')"
  >
    首页
  </div>
  <div
    :class="tabActive === '#introduce' ? 'active' : ''"
    @click="goAnchor('#introduce')"
  >
    介绍
  </div>
  <div
    :class="tabActive === '#superiority' ? 'active' : ''"
    @click="goAnchor('#superiority')"
  >
    优势
  </div>
  <div
    :class="tabActive === '#application' ? 'active' : ''"
    @click="goAnchor('#application')"
  >
    应用
  </div>
</div>
const tabActive = ref("#home");
const goAnchor = (selector: string) => {
  tabActive.value = selector;
  let anchor = document.querySelector(selector) as any;
  anchor.scrollIntoView({
    // 定义动画过渡效果, "auto"或 "smooth" 之一。默认为auto,"smooth"为平滑过渡。
    behavior: "smooth", 
    // 定义垂直方向的对齐, "start", "center", "end",默认值为start(上边框与视窗顶部平齐)。
    block: "start", 
  });
};
.tabList {
  display: flex;
  align-items: center;
  div {
    cursor: pointer;
    text-align: center;
    width: 100px;
    font-weight: bold;
  }
}
.active {
  color: rgba(59, 106, 246, 1);
}

2.父组件

<div id="home">
    <HeaderPage></HeaderPage>
    <div id="introduce">介绍</div>
    <div id="superiority">优势</div>
    <div id="application">应用</div>
</div>

import HeaderPage from "@/components/HeaderPage.vue";
#home {
  width: 100%;
  padding-top: 110px;
}
#introduce {
  width: 100%;
  height: 900px;
  background-color: lightcyan;
}
#superiority {
  width: 100%;
  height: 1500px;
  background-color: lightgray;
}
#application {
  width: 100%;
  height: 500px;
  background-color: lightcoral;
}

2023.03.31

1、vue3实现下滑页面隐藏头部导航栏,上滑显示。

父组件相关代码:

// 引入并使用子组件Header导航栏
<HeaderPage :class-name="topScroll"></HeaderPage>
// 滑动页面
const scrollTop = ref(0);
const topScroll = ref(false); //上移样式成立

onMounted(() => {
  window.addEventListener("scroll", handleScroll);
});

// 监听页面滚动
const handleScroll = () => {
  scrollTop.value =
    window.pageYOffset ||
    document.documentElement.scrollTop ||
    document.body.scrollTop;
};

// 监听top值的变化
watch(scrollTop, (newValue, oldValue) => {
  // 等新值大于100的时候再做变化(优化一下)
  if (newValue > 100) {
    if (newValue > oldValue) {
      topScroll.value = true;
    } else {
      topScroll.value = false;
    }
  }
});

子组件Header:

<div id="headerPage" :class="className ? 'top' : ''">
 ......
</div>
const props = defineProps({
  className: {
    type: Boolean,
    default: false,
  },
});
.top {
  transform: translateY(-90px);
}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值