vue中一些功能的实现

112 阅读1分钟

一,封装表单组件

1,移动端

<template>
  <div class="from">
    <van-form>
      <van-field
        v-for="item in FromLabel"
        :key="item.label"
        v-model="from[item.model]"
        :label="item.label"
        :type="item.type"
        :placeholder="`请输入${item.label}`"
        :rules="[
          {
            required: true,
            trigger: 'onBlur',
            message: item.label+'不能为空',
          },
          {
            pattern: item.rule,
            message: item.message,
            trigger: 'onBlur',
          },
        ]"
      />
    </van-form>
    <slot></slot>
  </div>
</template>
// 传入的数据
      FromLabel: [
        {
          model: "username",
          label: "账号",
          type: "text",
          rule: /^.{6,16}$/,
          message: "请填写6到16位字符",
        },
        {
          model: "password",
          label: "密码",
          type: "password",
          rule: /^.{6,16}$/,
          message: "请填写6到16位字符",
        },
      ],
      from: {
        username: "",
        password: "",
      },

2,vue2PC端

<template>
  <el-form ref="form" :model="form" label-width="100px" :inline="inline" >
    <el-form-item
      v-for="item in formLabel"
      :key="item.label"
      :label="item.label"
    >
      <el-input
        v-if="item.type == 'input'"
        v-model="form[item.model]"
        :placeholder="'请输入' + item.label"
      ></el-input>
      <el-switch
        v-if="item.type == 'switch'"
        v-model="form[item.model]"
      ></el-switch>
      <el-date-picker
        v-if="item.type == 'date'"
        type="date"
        value-format="yyyy-MM-dd"
        v-model="form[item.model]"
        placeholder="选择日期"
      >
      </el-date-picker>
      <el-select
        v-if="item.type === 'select'"
        placeholder="请选择"
        v-model="form[item.model]"
      >
        <el-option
          v-for="item in item.opts"
          :key="item.value"
          :label="item.label"
          :value="item.value"
        >
        </el-option>
      </el-select>
    </el-form-item>
    <el-form-item>
 <slot></slot>
</el-form-item>
  </el-form>
  operateFormLabel: [
        {
          model: "name",
          label: "姓名",
          type: "input",
        },
        {
          model: "age",
          label: "年龄",
          type: "input",
        },
        {
          model: "sex",
          label: "性别",
          type: "select",
          opts: [
            {
              label: "男",
              value: 1,
            },
            {
              label: "女",
              value: 0,
            },
          ],
        },
        {
          model: "birth",
          label: "出生日期",
          type: "date",
        },
        {
          model: "addr",
          label: "地址",
          type: "input",
        },
      ],
      operateForm: {
        name: "",
        addr: "",
        age: "",
        birth: "",
        sex: "",
      },

二,封装table组件

    <el-table :data="tableData" >
      <el-table-column
        show-overflow-tooltip
        v-for="item in tableLable"
        :key="item.prop"
        :label="item.label"
        :width="item.width ? item.width : 125"
      >
        <template slot-scope="scope">
          {{ scope.row[item.prop] }}
        </template>
      </el-table-column>
      <el-table-column label="操作" min-width="100">
        <template slot-scope="scope">
          <el-button size="mini" @click="handleEdit(scope.row)">编辑</el-button>
          <el-button size="mini" @click="handleDelete(scope.row)" type="danger"
            >删除</el-button
          >
        </template>
      </el-table-column>
    </el-table>
   tableData: [],
      tableLable: [
        {
          prop: "name",
          label: "姓名",
          width: 100,
        },
        {
          prop: "age",
          label: "年龄",
          width: 100,
        },
        {
          prop: "sex",
          label: "性别",
          width: 100,
        },
        {
          prop: "birth",
          label: "出生日期",
          width: 100,
        },
        {
          prop: "addr",
          label: "地址",
          width: 220,
        },
      ],

三,vue3,vite中动态获取图片

// 模板使用
<img :src="getImg()" alt="">
// js
const getImg = ()=> new URL('../assets/images/avter4.webp',import.meta.url).href

四,二次封装axiosaxiosmock的灵活使用

1,在src/config/index.js配置不同开发环境下的apimockApi

/**
 * 环境配置文件
 * 一般企业级项目有三个环境
 * 开发环境
 * 测试环境
 * 生产环境
 *  */// 当前的环境
const env = import.meta.env.MODE || 'prod'const EnvConfig = {
    development:{
        baseApi:'/api',
        mockApi:'https://www.fastmock.site/mock/f44a3a709364126cbd8dac2abb5fc00a/api'
    },
    test:{
        baseApi:'//test.full.com/api',
        mockApi:'https://www.fastmock.site/mock/f44a3a709364126cbd8dac2abb5fc00a/api'
    },
    prod:{
        baseApi:'//full.com/api',
        mockApi:'https://www.fastmock.site/mock/f44a3a709364126cbd8dac2abb5fc00a/api'
    }
}
​
export default {
    env,
    // mock的总开关
    mock:true,
    // 获取到当前环境的api
    ...EnvConfig[env]
}

2,在src/api/request.js配置请求,响应拦截器,并封装一个api关键函数

import axios from 'axios'
import {
    ElMessage
} from 'element-plus'
import config from '../config/index'

// 网络请求异常消息提示
const NETWORK_Message = '网络请求异常'
// 创建一个axios对象
const http = axios.create({
    baseURL: 'config.baseApi'
})

// 在请求之前做一些事情
// http.interceptors.request.use()

// 在响应之后做一些处理
http.interceptors.response.use(
    res => {
        // 把响应的数据结果出来
        console.log(res.data);
        const {
            code,
            data,
            msg
        } = res.data
        console.log(data);
        // 视开发情况决定
        if (code == 200) {
            return data
        } else {
            // 网络错误
            ElMessage.error(msg || NETWORK_Message)
            return Promise.reject(msg || NETWORK_Message)
        }
    }
)

// 封装的接口核心函数
function request(options) {
    // 如果是get请求,把data赋值给perams
    options.method = options.method || 'get'
    if (options.method.toLowerCase() == 'get') {
        options.params = options.data
    }

    // 对mock进行处理
    // 获取到mock的总开关
    let isMock = config.mock
    // 如果接口设置了mock,则把isMock的值变为自己设置的mock值,由接口自己控制是否使用mockApi
    if (typeof options.mock != 'undefined') {
        isMock = options.mock
    }

    // 对线上环境做处理
    if (config.env == 'prod') {
        http.defaults.baseURL = config.baseURL
    } else {
        http.defaults.baseURL = isMock ? config.mockApi : config.baseApi
    }

    return http(options)
}

export default request

3,封装接口,并挂载到全局

import request from "./request"
export default {
    // 获取home页面的表格数据
    getHomeData(params){
        return request({
            url:'/home/getHomeData',
            method:'get',
            data:params,
            // 自己的开关,是否使用mockAPI
            mock:true
        })
    }
}
import api from './api/api';
app.config.globalProperties.$api = api
// 声明类型(必须)
declare module '@vue/runtime-core'{
    export interface ComponentCustomProperties{
        $api:Object
    }
}

4,在组件中使用axios

import {getCurrentInstance } from "vue"
// 获取当前实例
const app = getCurrentInstance()
const getTableData = async ()=>{

  const res =await app?.proxy?.$api.getHomeData();
  tableData.value = res
}

五,vue3使用面包屑

1,在pinia添加字段current存储面包屑路由数据

 state:()=>{
        return { 
            current:null
        }
    }

2,跳转路由,把路由的数据添加到pinia

const push = (item:any)=>{
    router.push(item.path)
    test.$patch({
    	//如果跳转页面为主页,则值为null,否则值为当前的路由数据
        current:item.path=='home'?'null':item
    })
    
}

3,组件中使用面包屑

 <el-breadcrumb class="bread" separator="/">
            <el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
            <el-breadcrumb-item :to="current['path']" v-if="current">{{ current['label'] }}</el-breadcrumb-item>
        </el-breadcrumb>

<script>
    // 结构pinia中把current结构出来
const {current} = storeToRefs(test)
</script>

六,vue3使用tag标签

1,在pinia中配置tag数据

state: () => {
        return {
            // 首页tag标签是一直存在的,默认有数据
            tableList: [
                {
                    path: 'home',
                    name: 'home',
                    label: '首页',
                    icon: 'House',
                    url: 'Home/index'

                }
            ]
        }

2,当点击菜单栏,添加菜单数据到tableList

const push = (item:any) => {
    router.push(item.path)
    test.$patch((state)=>{
        if(item.name=='home'){
            //state.current=null
            // 如果点击home页面,不会添加
        }else{
            //state.current=item
            // 如果菜单在tablist不存在,则添加
            const bol = state.tableList.some((val)=>val.name===item.name)
            if(!bol){
                state.tableList.push(item)
            }
        }
    })
}

3,配置components/CommonTab.vue组件

<template>
  <div class="tabs">
    <el-tag
    class="tab"
    v-for="(tag,index) in tags"
    :key="tag.name"
    // 当前路由的name==当前tag的name为drak色,路由跳转到哪,哪个tag变色
    :effect="tag.name==route.name?'dark':'plain'"
    // 除了home的tag,其他都能删除
    :closable="tag.name!='home'"
    // 取消动画效果
    :disable-transitions="false"
    @click="clickTab(tag)"
    @close="closeTab(tag,index)"
  >
    {{ tag.label }}
  </el-tag>
  </div>
</template>

<script setup lang = 'ts'>
import {useRoute,useRouter} from "vue-router"
import {useStore} from '../store'
const route = useRoute()
const router = useRouter()
const test = useStore()

const tags = test.tableList

const clickTab = (item:any)=>{
    // 点击跳转页面
    router.push(item.path)
}
// 删除tab标签
const closeTab = (item:any,index:number)=>{
    // 当前路由name(点击的tag)不等于要删除tag的name,直接删除
    if(route.name!==item.name){
        test.tableList.splice(index,1)
        //要删除当前路由name(点击的tag),他在最后面,他前面的tag变色
    }else if(index===test.tableList.length-1){
        test.tableList.pop()
        router.push(test.tableList[index-1].path)
       // test.tableList[index-1].name==='home'?test.current=null:test.current=test.tableList[index-1]
    
    }else{
        //要删除当前路由name(点击的tag),不在最后,他后面的tag变色
        test.tableList.splice(index,1)
        router.push(test.tableList[index].path)
        test.tableList[index-1].name==='home'?test.current=null:test.current=test.tableList[index-1]
    }
}
</script>

4,把组件放在指定位置

七,element plus表单提交进行验证、格式化表单数据

1,在form标签上添加ref属性

2,进行验证

//获取当前实例
import { getCurrentInstance } from "vue"
const app = getCurrentInstance()
//进行验证
app?.proxy?.$refs.formRef.validate(async (valid:any) => {
      if (valid) {
        const res = await app?.proxy?.$api.addUserData(formData)
        if(res){
          formatForm()
          getUserDatas(config)

        }
    }

3,退出格式化表单

  app?.proxy?.$refs.formRef.resetFields()

八,新增,编辑使用同一个弹出框,修改操作的resetFields()格式化表单失效

修改操作获取数据使用$nextTick

const editUser = (row:any): void => {
  dialogVisible.value = true
  app?.proxy?.$nextTick(()=>{
    Object.assign(formData, row)
  })
}

九,权限设计,不同用户登录,展示不同的菜单栏,路由

1,设置pinia的方法

import {defineStore} from 'pinia'
export const useStore = defineStore('test', {
    state: () => {
        return {
            loginRouters: []
        }
    },
    actions: {
        // 把后端返回的菜单数据添加到pinia
        setRoutes(val:any) {
            this.loginRouters = val
            localStorage.setItem('menu', JSON.stringify(val))
        },
        // 把菜单数据重置为路由格式,动态添加到router
        addMenu(router:any) {
            if (!localStorage.getItem("menu")) {
            return
            }
            // 数据持久化
            const stateRoutes = JSON.parse(localStorage.getItem('menu'))
            this.loginRouters = stateRoutes
            let menuRouter = stateRoutes
            let menus:any = []
            // 对数据做处理
            menuRouter.forEach((item:any) => {
                if (item.children) {
                    item.children.map((item:any) => {
                        let url = `../view/${item.url}.vue`
                        item.component = () => import(url)
                        return item
                    })
                    menus.push(...item.children)
                } else {
                    let url = `../view/${item.url}.vue`
                    item.component = () => import(url)
                    menus.push(item)
                }
            })
            // 动态添加到路由
            menus.forEach((item:any) => {
                router.addRoute('home1', item)
            })
            // console.log(router.hasRoute('user'));
            
        }
    }
})

2,登录时,调用pinia的方法,动态添加,菜单,路由

const login = ()=>{
    app?.proxy?.$refs.loginRef.validate( async (valid:any)=>{
        if(valid){
            const res = await app.proxy?.$api.login(FormData)
            if(res){
                test.setRoutes(res.menu)
                test.addMenu(router)
                router.push('/')
            }
        }
    })
}

3,菜单组件获取菜单

const asyncList:Array<object> = test.loginRouters;

4,页面刷新,页面会丢失,在main.ts调用

const test = useStore()
test.addMenu(router)