欢迎访问 生活随笔!

生活随笔

当前位置: 首页 > 前端技术 > HTML >内容正文

HTML

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

发布时间:2024/5/15 HTML 46 豆豆
生活随笔 收集整理的这篇文章主要介绍了 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-dialogv-model="materialVisible"title="":close-on-click-modal="false"@close="handlePause"><audiocontrolsref="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。

父组件:

<Tablev-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-buttonv-if="row.pay_status === '未支付'"linktype="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-columnv-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-formref="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框/密码框 --><templatev-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-optionv-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-cascaderv-model="formData[`${item.field}`]":options="item.options":show-all-levels="false":placeholder="item.placeholder"/></template><!-- 日期选择器 --><template v-else-if="item.type === 'date'"><el-date-pickerv-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-pickerv-model="formData[`${item.field}`]"type="daterange"range-separator="至"start-placeholder="开始时间":placeholder="item.placeholder"end-placeholder="结束时间"/></template><!-- 文件上传 --><template v-else-if="item.type === 'upload'"><el-uploadref="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-radiov-for="option in item.options":key="option.value":label="option.label"/> </el-radio-group></template><!-- 单个多选框 --><template v-else-if="item.type === 'checkbox'"><el-checkboxv-model="formData[`${item.field}`]":label="item.placeholder"/><!-- <el-checkbox-group v-model="formData[`${item.field}`]"><el-checkboxv-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"><Formv-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-columnv-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-paginationsmallhide-on-single-pagev-model:current-page="currentPage"v-model:page-size="pageSize"layout="total, prev, pager, next, jumper":total="total"@current-change="handleCurrentChange"/> </div>

2.页面中使用table组件

<Tablev-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属性,插入到文档中并clicklet 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); }

总结

以上是生活随笔为你收集整理的2023年1~3月前端学习笔记的全部内容,希望文章能够帮你解决所遇到的问题。

如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。