尚硅谷-硅谷甄选项目记录

一、Vue3

1 基础配置
1.1 @ 路径别名

vite.config.ts

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

// 引入path,node提供的模块,可以获取文件或文件夹的路径
import path from 'path'

export default defineConfig({
  	plugins: [vue()],
    resolve: {
		alias: {
			'@': path.resolve('./src')
		}
    }
})

tsconfig.json

{
    "compilerOptions": {
        // 配置路径别名
        "baseUrl": "./",
        "paths": {
        	"@/*": ["src/*"]
        }
   	}
}
1.2 SCSS 全局变量

vite.config.ts

export default defineConfig({
  	// scss全局变量一个配置
    css: {
        preprocessorOptions: {
            scss: {
                javascriptEnabled: true,
                additionalData: '@import "./src/styles/variable.scss";',
            },
        },
    },
})
1.3 SVG 使用方式

vite.config.ts

// 配置svg:引入插件
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'

export default defineConfig({
    plugins: [
        createSvgIconsPlugin({
            // 配置svg:指定svg资源目录
            iconDirs: [path.resolve(process.cwd(), 'src/asstes/icons')],
            // 配置svg:指定svg资源name
            symbolId: 'icon-[dir]-[name]'
        })
    ]
}

main.ts

// svg插件需要配置代码
import 'virtual:svg-icons-register'

使用

<svg style="width:30px; height:30px">
	<use xlink:href="#icon-xxx" fill="red"></use>
</svg>
1.4 SVG 全局组件
<svg-icon name="home" color="pihk"></svg-icon>
<!-- @/components/SvgIcon/index.vue -->

<template>
    <svg :style="{width, height}">
        <use :xlink:href="prefix + name"></use>
    </svg>
</template>

<script setup>
import { ref } from 'vue';
const prefix = ref('#icon-')
defineProps({
    // 名字
    name: String,
    // 颜色
    color:{
        type: String,
        defaule: ''
    },
    // 宽高
    width: {
        type: String,
        default: '20px'
    },
    height: {
        type: String,
        default: '20px'
    }
})
</script>
  • 1、单独引入注册

    main.ts

    import SvgIcon from '@/components/SvgIcon/index.vue'
    app.components('SvgIcon', SvgIcon)
    
  • 2、插件注册,引入components下组件遍历注册

    @/components/index.ts

    import SvgIcon from './SvgIcon/index.vue'
    
    const allGloablComponent = { SvgIcon }
    export default{
        install(app){
            Object.keys(allGloablComponent).forEach(key => {
                app.component(key, allGloablComponent[key])
            })
        }
    }
    

    main.ts

    //引入自定义插件对象:注册整个项目全局组件
    import gloalComponent from '@/components'
    //安装自定义插件
    app.use(gloalComponent)
    
1.5 EL 全局图标

@/components/index.ts

// 引入全部elmentPlus的图标
import * as ElementPlusIconsVue from '@element-plus/icons-vue'

const allGloablComponent = { SvgIcon }
export default{
    install(app){
        // 将element-plus的图标全部注册
        for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
            app.component(key, component)
        }
    }
}

main.ts

// 引入自定义插件对象:注册整个项目全局组件
import gloalComponent from '@/components'
// 安装自定义插件
app.use(gloalComponent)

使用方式

<el-icon> </Edit> </el-icon>

<el-icon> <component is="Edit"> </el-icon>
2 vue-router路由
2.1 路由跳转
  • 编程式导航

    <script setup lang="ts">
        import { useRouter } from 'vue-router';
        const $router = useRouter()
        const toRoutes = (path) => {
            $router.push(path)
        }
    </script>
    
2.2 路由过度
<template>
    <router-view v-slot="{ Component }">
        <transition name="fade">
            <component :is="Component" />
        </transition>
    </router-view>
</template>

<script setup lang="ts"> </script>

<style lang="scss" scoped>
// 此为vue3写法,与vue2类名略有不同,根据需求自定义过渡动画
.fade-enter-from {
    opacity: 0;
    transform: scale(0);
}

.fade-enter-active {
    transition: all .3s;
}

.fade-enter-to {
    opacity: 1;
    transform: scale(1);
}
</style>
2.3 路由鉴权

项目当中的各个路由在什么条件下可以访问,什么条件下不可以访问

2.3 element-plus 组件
2.3.1 Menu 菜单折叠图标

问题:在使用 el-menu 菜单的折叠功能时,因为自定义路由和Menu组件,导致图标没有正常显示

解决:将图标组件移出 template 外边

<el-menu-item >
    <template #title>
        <el-icon> <component :is="meta.icon"></component> </el-icon>
        <span>{{item.children[0].meta.title}}</span>
    </template>
</el-menu-item>
<el-menu-item >
	<el-icon> <component :is="item.meta.icon"></component> </el-icon>
    <template #title>
        <span>{{item.meta.title}}</span>
    </template>
</el-menu-item>
2.3.2 Menu刷新默认菜单

问题:使用 el-menu 菜单在页面刷新时,已展开的菜单会关闭,

解决:配置 el-menudefault-active 属性,通过 router 将页面路径赋值给他

备注: route 获取页面路径, router 页面跳转,现 $route 以改为宏函数,可以直接在 template 使用,无需引入,若想在 script 中使用,还是得引入

<el-menu :default-active="$route.path"></el-menu>

import { useRoute } from 'vue-router'
$route = useRoute()
3 父子组件交互
3.1 defineEmits

vue3 内置 defineEmitsdefinProps 可无需引入直接使用

// son.vue
let $emit = defineEmits(['fn1', 'fn2', ...])

const updata = () => {
	$emit('fn1', { name: '张三', age: '23' })
}

// father.vue
<son @fn1="fn1"></son>

const fn1 = (name='', age='') => { ... }
3.2 definProps
// father.vue
<son :name="name" :data="data"></son>

// son.vue
let props = defineProps({
    name: {
        type: String,
        default: ''
    },
    data:{
    	type:Object,
    	default:() => ({ ... })
    }
})
    
// 或

defineProps({
    name:{
        type:String,
        default:''
    },
    data:{
    	type:Object,
    	default:() => ({ ... })
    }
})
3.3 ref
// father.vue
<son ref="son"/>

let son = ref(null)
const fn = () => {
	son.value.fnSon
}

// son.vue
const fnSon = () => {
	// ...
}
defineExpose({ fnSon })

二、TypeScript

1 类型定义
  • @/api/user/index.js

    import request from '@/utils/request'
    
    import type {
        loginFormData,
        loginResponseData,
        userInfoReponseData,
    } from './type'
    
    enum API {
        // 登录
        LOGIN_URL = '/admin/acl/index/login',
        // 用户信息
        USERINFO_URL = '/admin/acl/index/info',
        // 退出登录
        LOGOUT_URL = '/admin/acl/index/logout',
    }
    
    // 登录接口
    export const reqLogin = (data: loginFormData) => 
        request.post<any, loginResponseData>(API.LOGIN_URL, data)
    
    // 获取用户信息
    export const reqUserInfo = () =>
        request.get<any, userInfoReponseData>(API.USERINFO_URL)
    
    // 退出登录
    export const reqLogout = () => request.post<any, any>(API.LOGOUT_URL)
    
  • @/api/user/type.ts

    // 请求携带参数类型
    export interface loginFormData {
        username: string,
        password: string
    }
    
    // 基础接口返回数据类型
    export interface Response {
        code: number|string,
        message?: string,
        ok?: boolean
    }
    
    // 登录接口 返回数据类型
    export interface loginResponseData extends Response {
        data: string
    }
    
    // 用户信息接口 返回数据类型
    export interface userInfoReponseData extends Response {
        data: {
            routes: string[]
            buttons: string[]
            roles: string[]
            name: string
            avatar: string
        }
    }
    

三、JavaScrpt

1 对象数组过滤对象
let objectsArray = [  
    { a: 1, b: 'one' },  
    { a: 2, b: 'two' },  
    { a: 3, b: 'three' },  
    { a: 2, b: 'anotherTwo' }
];  
  
let valueToRemove = 2; // 这是你想从对象中过滤掉的属性 a 的值  
  
objectsArray = objectsArray.filter(obj => obj.a !== valueToRemove);  
  
console.log(objectsArray);  
// 输出: [ { a: 1, b: 'one' }, { a: 3, b: 'three' } ]
2 对象数组的对象的属性名修改
imgList = res.data.map(item => {
	return{
		name: item.imgName,
		url: item.imgUrl
	}
})
3 Object.assign

Object.assign() 将所有可枚举的属性的值从一个或多个源对象复制到目标对象(第一个参数),具有相同属性的话,会按顺序进行覆盖,并返回目标对象

let a = reactive({ x: 1, y: 2 })
  
let b = { y: 3, z: 4 }

let c = { z: 3 }
  
let newA = Object.assign(a, b, c)   
  
console.log(a, a===newA) 	// { x: 1, y: 3, z: 3 }  true

// 在vue3中,如使用解构赋值的方法,会产生新的对象,会导致失去数据代理
a = {...a, ...b, ...c}
4 request.ts

@/utils/request.ts

import axios from 'axios'
import { ElMessage } from 'element-plus'
//引入用户相关的仓库
import useUserStore from '@/store/modules/user'

const request = axios.create({
    //基础路径(在项目根目录下设置)
    baseURL: import.meta.env.VITE_APP_BASE_API,
    //超时的时间的设置
    timeout: 5000,
})

// 请求拦截器
request.interceptors.request.use((config) => {
    if(useUserStore().token){
        config.headers.token = useUserStore().token
    }
    return config
})

// 响应拦截器
request.interceptors.response.use(
    (response) => {
        // 成功回调,简化数据
        return response.data
    },
    (error) => {
        // 失败回调:处理http网络错误
        let message = ''
        // http状态码
        const status = error.response.status
        switch (status) {
            case 401:
                message = 'TOKEN过期'
                break
            case 403:
                message = '无权访问'
                break
            case 404:
                message = '请求地址错误'
                break
            case 500:
                message = '服务器出现问题'
                break
            default:
                message = '网络出现问题'
                break
        }
        //提示错误信息
        ElMessage({
            type: 'error',
            message,
        })
        return Promise.reject(error)
    },
)

export default request

四、业务实现

  • 左侧菜单缩放通过仓库变量(true/false)进行管理

  • 刷新:通过v-ifnextTick仓库 实现刷新(销毁重建)

  • 全屏状态:通过document.fullscreenElement设置全屏状态

  • 自动刷新:window.location.reload(),用户修改自身账号或密码之后,调用方法自动刷新

  • 展示角色权限:例,后端返回全部权限,其中该角色拥有的权限的select:true,修改时需要把新的权限对象给后端(父级的id和权限值 + 子级的id和权限值 + …), <el-tree> 提供获取选中的节点的数组,修改完调用刷新方法

    一级二级三级四级
    1、全部数据
    1.1、权限管理
    1.1.1、用户管理
    1.1.1.1、添加用户
    1.1.1.2、删除用户
    1.1.1.3、修改用户
    1.1.2、菜单管理
    1.2、商品管理
    {
        "id": 1,
        "name": "全部数据",
        "level": 1,
        "children": [
            {
                "id": 7,
                "name": "权限管理",
                "level": 2,
                "children": [
                    {
                        "id": 8,
                        "name": "用户管理",
                        "level": 3,
                        "children": [
                            {
                                "id": 11,
                                "name": "添加用户",
                                "level": 4,
                                "children": [],
                                "select": false
                            },
                            {
                                "id": 12,
                                "name": "删除用户",
                                "level": 4,
                                "children": [],
                                "select": false
                            }
                        ],
                        "select": false
                    },
                    {
                        "id": 10,
                        "name": "菜单管理",
                        "level": 3,
                        "children": [],
                        "select": false
                    }
                ],
                "select": false
            }
        ],
        "select": true
    }
    
    const setPermisstion = async (id) => {
        let result = await reqAllMenuList(id);
        if (result.code == 200) {
            // 全部权限
            menuArr.value = result.data;
            // 拥有的权限
            selectArr.value = filterSelectArr(menuArr.value, []);
        }
    }
    
    const filterSelectArr = (allData, initArr) => {
        allData.forEach((item) => {
            // 如最后一级的权限的是第四级(遍历完其最低级的权限,即可通过<el-tree>自动判断)
            if (item.select && item.level == 4) {
                initArr.push(item.id);
            }
            if (item.children && item.children.length > 0) {
                filterSelectArr(item.children, initArr);
            }
        })
        return initArr;
    }
    

五、备注

  • 在平时写代码的时候,为实现某个功能,感觉自己写的代码很冗余,可以试试将代码交于 ai ,让其优化一下,说不定 JS官方 已经提供了便捷语法糖(如:ES6+新增的语法糖),或者 AI 有更加便捷的实现思路

  • 背景图片

    .contaiter{
    	background: url('@/xxx/xxx.jpg') no-repeat;
    	backgorund-size: cover;
    }
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值