• vue3+te项目正式开发


    vue3+te项目正式开发

    1.样式初始化

    1.1  normalize.css 对项目样式进行初始化

    npm i normalize.css
    import 'normalize.css'(main.js)

    1.2 自定义样式文件

    ./assets/css/index.less 定义自定义的样式

    1. body {
    2. padding: 0;
    3. margin: 0;
    4. }
    5. html, body, #app {
    6. width: 100%;
    7. height: 100%;
    8. }

    本项目用到的公共组件-components

    所有项目的公共组件-base-ui 

    2.封装公共模块

    2.1 网络请求模块

    解决刷新页面vuex中数据丢失的问题

    在/store/index.ts 中导出一个函数setupStore,函数中执行各个模块自定义的从localStorage中加载数据的函数,判断当localStorage有值时,则保存到store中,在main.ts中导入setupStore并调用

    2.2 utils公共工具模块

    2.3 路由模块

    2.3.1 导航守卫

            通过导航守卫,每次进行路由跳转前,判断如果要跳转去的路由不是登录界面,则去localStorage中读取token,如果token没有值,则跳转去login页面

    2.3.2 setupStore() 刷新页面vuex数据不丢失

    /store/index.ts 导出setupStore(),从localStorage中读取数据,如果数据不为空,则赋值给store.state,main.ts导入setupStore()并执行,则可以解决页面刷新vuex中数据丢失的问题。

    注意:setupStore()需要在use(store)之前执行,否则页面刷新导航会出错

    原因:刷新页面时,会重新加载页面执行main.ts,从上往下执行,执行use(store),会执行store中的install()函数,该函数会读取当前url,并从路由配置对象中进行匹配,若当前url对应的路由对象是动态加载的,则动态加载路由的操作是在执行setupStore(),从缓存中读取userMenu后,执行setMenu()之后进行的,因此setupStore()必须先执行,将动态路由加载好之后,在执行use(store)

    2.4 vuex模块

    在页面中使用useStore()对TS的支持不好,得到的store的类型是any,无法对store中的数据做类型检测,可以在./store.index.ts中自己定义一个useStore(),规定返回值的类型是IRootState&IModuleState,在函数内部返回vuex的useState()

    3.登录模块

    3.1 将账号登录-获取用户信息-获取用户菜单权限封装在vuex中login模块

    3.2 登录成功后的处理流程

    3.2.1 用户登录成功后,后端会返回token用于验证用户身份

    • 将token保存到store中
    • 并且保存到localStorage(避免页面刷新store中的数据丢失)
    • 在service封装的网络模块中,在interceptors.request.use的请求拦截中当存在token时,为每一个请求头添加token config.header.authorization = window.localStorage.getItem('token')

    3.2.2 根据userId请求用户详细信息 

    将获取的用户信息保存到vuex和localStorage

    3.2.3 根据用户角色信息获取用户菜单权限

    将获取的用户菜单保存到vuex和localStorage

    3.2.4 跳转到首页

    路由根路径redirect->登录界面 

    4.首页 main/main.vue

    4.1 结构设计

    • /main/main.vue:放各个组件的首页根组件
    • /main/xx/xx: 放首页的各模块组件
    • components/nav-nenu/src:侧边导航的组件
    • components/nav-header/src:头部的内容
    • components/nav-header/index.ts:对nav-header中的组件统一导出

    4.2 动态路由

    4.2.1 提前写好不同角色对应的路由数组,根据用户角色加载对应的数组

    缺点:新增角色或者修改角色对应的权限时,只能修改前端代码

    3.2.2 根据菜单动态生成路由映射

    • npm i coderwhy  --coderwhy老师自己写的库
    • coderwhy add3page user -d src/views/main/system/user --自动生成页面和router中的路由映射
    • 在src/views/main/system/user下生成user.vue文件,并且router/main/system/user/user.ts中生成映射关系
    • /utils/map-menu.ts 定义根据menu生成对应的路由配置对象的函数MenuToRouteMap()并导出
    1. import { RouteRecordRaw } from 'vue-router'
    2. export default function (menu: any[]): RouteRecordRaw[] {
    3. const routes: RouteRecordRaw[] = []
    4. // 1.获取所有已经定义过的路由映射对象
    5. const allRoutes: RouteRecordRaw[] = []
    6. const routeFiles = require.context('../router/main', true, /\.ts/)
    7. routeFiles.keys().forEach((item) => {
    8. const route = require(`../router/main${item.split('.')[1]}`)
    9. allRoutes.push(route.default)
    10. })
    11. // 2.根据菜单获取需要添加的routes
    12. // userMenus:
    13. // type === 1 -> children -> type === 1
    14. // type === 2 -> url -> route
    15. const _recurseGetRoute = (menu: any[]) => {
    16. for (const menuItem of menu) {
    17. if (menuItem.type === 2) {
    18. const route = allRoutes.find((routeItem) => {
    19. return routeItem.path === menuItem.url
    20. })
    21. if (route) {
    22. routes.push(route)
    23. }
    24. } else {
    25. _recurseGetRoute(menuItem.children)
    26. }
    27. }
    28. }
    29. _recurseGetRoute(menu)
    30. return routes
    31. }
    • /store/login 的setMenus()中,调用MenuToRouteMap(state)获取该用户对应的路由映射对象,通过addRoute()添加到/main的children
    1. setMenus(state, payload: any) {
    2. console.log(payload)
    3. state.userMenus = payload
    4. const route = MenuToRouteMap(payload)
    5. route.forEach((item) => {
    6. router.addRoute('main', item)
    7. })
    8. }

    4.3 侧边栏折叠

    折叠按钮,icon,用户点击控制是否折叠-》main(控制宽度)->nav-nenu是否折叠()

    nav-menu-index.ts 统一导出

    在template中使用别名:~@

    4.4 刷新页面定位到原来选中的目录

    4.4.1 根据路径定位菜单

    刷新页面时,定位到刷新之前的菜单

    获取当前route->path,用path匹配userMenu中的url,匹配的那个menu的id赋值给default-active

    同时可以获取当前页面的breadcrumb

    匹配的函数封装在utils/pathMapToMenu.ts

    1. export function pathMapToMenu(
    2. userMenus: any[],
    3. currentPath: string,
    4. breadcrumb?: IBreadcrumb[]
    5. ): any {
    6. for (const menu of userMenus) {
    7. if (menu.type === 1) {
    8. const findMenu = pathMapToMenu(menu.children ?? [], currentPath)
    9. if (findMenu) {
    10. breadcrumb?.push({ name: menu.name })
    11. breadcrumb?.push({ name: findMenu.name })
    12. return findMenu
    13. }
    14. } else if (menu.type === 2 && menu.url === currentPath) {
    15. return menu
    16. }
    17. }

    4.4.2 /main重定向

    根路径/:redirect:/home

    /home->home.vue只显示侧边栏导航和头部,内容是由路由匹配到的子组件展示的,但是当用户第一次进入页面,或者在/main刷新页面时,main的内容部分就是空的,这就需要把/main重定向到第一个侧边导航,但是Home的子路由是动态生成的,要怎么实现/main的重定向呢?

    setUserMenus()中根据导航生成路由时,保存第一个有路由页面的导航firstMenu,并导出

    在路由守卫中判断,如果to='home',则读取firstMenu.url,跳转到该页面

    5.搜索框封装

    5.1 样式

    • 定义IForm,IFormItem类型
    • /base-ui/form/src/form中使用props接受IForm,并设置默认值
    • /base-ui/types/index.ts 声明所以类型
    • /base-ui/form/index.ts 对该组件所有内容进行统一导出,导入/types/index.ts和/src/form,并导出
    • 其他组件导入/base-ui/form,根据IForm定义搜索框数据

    5.2 数据传递

    5.2.1 form-item通过v-model直接修改父组件数据

    父组件通过传递一个对象给form组件,form组件通过IFormItem.field属性绑定数据

    缺点:子组件修改了父组件传递的数据,违背了单向数据流传递

    5.2.2 真正的双向数据绑定

    父组件通过v-model=formData传递一个const formData= ref()类型的对象

    子组件let data = ref(...formData),子组件中绑定data,再监听data的变化,当变化是,emit('update:formData'),把数据传递给父组件,这种方式子组件并没有直接修改父组件的数据

    5.3 form头部和底部的插槽与search-form

    在封装的form组件中,提供两个插槽,分别是头部的插槽header(标题)和底部的插槽footer(可以放重置和查询按钮等)

    components/page-search:封装一个新的组件,在内部使用form,并将搜索的头部和底部信息传递给form中的插槽,在组件中使用page-search,并把搜索框配置数据searchFormConfig传递给page-search

    5.4 搜索与重置

    搜索与重置按钮在,中使用中使用

    3个组件中的formData需要同步,思路如下:

    • 通过v-model=‘formData’双向数据绑定,中定义一个数据接受formData的拷贝,在页面中绑定到formData,再监听formData的变化,当发生变化时,将最新的数据发送到
    1. // 如果直接把modelValue绑定到form上,则当用户输入时,会直接修改modelValue的值,违背了单向数据流
    2. const formData = ref({ ...prop.modelValue })
    3. // 当用户输入的数据发送变化,把最新的数据发送到page-search
    4. watch(
    5. formData,
    6. (newValue) => {
    7. emit('update:modelValue', newValue)
    8. },
    9. { deep: true }
    10. )

    当用户点击重置时, 需要重置formData,并且重新查询用户列表数据

    当用户点击查询时,需要携带最新的formData数据,并且重新查询用户列表数据

    7.封装table

    7.1 基础table的封装

    基础的table封装在base-ui,多个项目都可以用,接收的数据如下:

    • 接受后端返回的原始数据listData
    • 表格样式数据contentTableConfig,包括title(表格标题),propList用来控制表格每一项数据的展示,包括label(表头)、prop(展示的数据),min-width,soltName等,showIndexColumn控制序号是否展示,showSelectColumn控制select是否展示

    7.2 table中的插槽

    7.2.1 表格数据中展示的内容

    7.2.2 头部的插槽

    7.2.3 底部的插槽

    lot,作用域插槽,序号优化,选择,操作,header,分页

    7.1各个模块的数据保存到store中,获取各模块数据的方法放到actions

    8.全局属性

    app.config.globalProperties:定义在这上面的变量在全局任意组件中都可以访问

    app.config.perpories.$filters={}  :定义全局的filter

    $filter.xx:页面中使用 

    /global/register-properties/中导出配置变量

    6.封装面包屑

    1.将面包屑封装成一个组件

    2.使用时,根据当前path生成数据传入面包屑组件,

  • 相关阅读:
    kafka-- kafka集群环境搭建
    在线客服系统代码_h5客服_对接公众号_支持APP_支持多语言
    Webpack 复习小结
    如何使用jenkins、ant、selenium、testng搭建自动化测试框架
    containerd的安装和使用
    技术学习:Python(08)|操作MySQL
    redis 分布式锁
    算法时空复杂度分析
    docker 镜像内执行命令显示:You requested GPUs: [0] But your machine only has: []
    Sentinel
  • 原文地址:https://blog.csdn.net/ICanWin_lll/article/details/126410437