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.innerHeight3、當display: none; 與 display: flex; 相遇,其一作用失效。
解決辦法:在子元素的外層,原來父元素的內層套一個盒子,比如div,display: 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月前端学习笔记的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【UE4笔记】C++游戏控制的摄像机
- 下一篇: 汽车ECU AUTOSAR 开发