vue3+te项目正式开发
1.样式初始化
1.1 normalize.css 对项目样式进行初始化
npm i normalize.css import 'normalize.css'(main.js)
1.2 自定义样式文件
./assets/css/index.less 定义自定义的样式
- body {
- padding: 0;
- margin: 0;
- }
-
- html, body, #app {
- width: 100%;
- height: 100%;
- }
本项目用到的公共组件-components
所有项目的公共组件-base-ui
解决刷新页面vuex中数据丢失的问题
在/store/index.ts 中导出一个函数setupStore,函数中执行各个模块自定义的从localStorage中加载数据的函数,判断当localStorage有值时,则保存到store中,在main.ts中导入setupStore并调用
通过导航守卫,每次进行路由跳转前,判断如果要跳转去的路由不是登录界面,则去localStorage中读取token,如果token没有值,则跳转去login页面
/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)
在页面中使用useStore()对TS的支持不好,得到的store的类型是any,无法对store中的数据做类型检测,可以在./store.index.ts中自己定义一个useStore(),规定返回值的类型是IRootState&IModuleState,在函数内部返回vuex的useState()
3.1 将账号登录-获取用户信息-获取用户菜单权限封装在vuex中login模块
3.2 登录成功后的处理流程
3.2.1 用户登录成功后,后端会返回token用于验证用户身份
3.2.2 根据userId请求用户详细信息
将获取的用户信息保存到vuex和localStorage
3.2.3 根据用户角色信息获取用户菜单权限
将获取的用户菜单保存到vuex和localStorage
3.2.4 跳转到首页
路由根路径redirect->登录界面
缺点:新增角色或者修改角色对应的权限时,只能修改前端代码
- import { RouteRecordRaw } from 'vue-router'
- export default function (menu: any[]): RouteRecordRaw[] {
- const routes: RouteRecordRaw[] = []
-
- // 1.获取所有已经定义过的路由映射对象
- const allRoutes: RouteRecordRaw[] = []
- const routeFiles = require.context('../router/main', true, /\.ts/)
- routeFiles.keys().forEach((item) => {
- const route = require(`../router/main${item.split('.')[1]}`)
- allRoutes.push(route.default)
- })
-
- // 2.根据菜单获取需要添加的routes
- // userMenus:
- // type === 1 -> children -> type === 1
- // type === 2 -> url -> route
- const _recurseGetRoute = (menu: any[]) => {
- for (const menuItem of menu) {
- if (menuItem.type === 2) {
- const route = allRoutes.find((routeItem) => {
- return routeItem.path === menuItem.url
- })
- if (route) {
- routes.push(route)
- }
- } else {
- _recurseGetRoute(menuItem.children)
- }
- }
- }
- _recurseGetRoute(menu)
- return routes
- }
- setMenus(state, payload: any) {
- console.log(payload)
- state.userMenus = payload
- const route = MenuToRouteMap(payload)
- route.forEach((item) => {
- router.addRoute('main', item)
- })
- }
折叠按钮,icon,用户点击控制是否折叠-》main(控制宽度)->nav-nenu是否折叠()
nav-menu-index.ts 统一导出
在template中使用别名:~@
刷新页面时,定位到刷新之前的菜单
获取当前route->path,用path匹配userMenu中的url,匹配的那个menu的id赋值给default-active
同时可以获取当前页面的breadcrumb
匹配的函数封装在utils/pathMapToMenu.ts
- export function pathMapToMenu(
- userMenus: any[],
- currentPath: string,
- breadcrumb?: IBreadcrumb[]
- ): any {
- for (const menu of userMenus) {
- if (menu.type === 1) {
- const findMenu = pathMapToMenu(menu.children ?? [], currentPath)
- if (findMenu) {
- breadcrumb?.push({ name: menu.name })
- breadcrumb?.push({ name: findMenu.name })
- return findMenu
- }
- } else if (menu.type === 2 && menu.url === currentPath) {
- return menu
- }
- }
根路径/:redirect:/home
/home->home.vue只显示侧边栏导航和头部,内容是由路由匹配到的子组件展示的,但是当用户第一次进入页面,或者在/main刷新页面时,main的内容部分就是空的,这就需要把/main重定向到第一个侧边导航,但是Home的子路由是动态生成的,要怎么实现/main的重定向呢?
setUserMenus()中根据导航生成路由时,保存第一个有路由页面的导航firstMenu,并导出
在路由守卫中判断,如果to='home',则读取firstMenu.url,跳转到该页面
父组件通过传递一个对象给form组件,form组件通过IFormItem.field属性绑定数据
缺点:子组件修改了父组件传递的数据,违背了单向数据流传递
父组件通过v-model=formData传递一个const formData= ref()类型的对象
子组件let data = ref(...formData),子组件中绑定data,再监听data的变化,当变化是,emit('update:formData'),把数据传递给父组件,这种方式子组件并没有直接修改父组件的数据
在封装的form组件中,提供两个插槽,分别是头部的插槽header(标题)和底部的插槽footer(可以放重置和查询按钮等)
components/page-search:封装一个新的组件,在内部使用form,并将搜索的头部和底部信息传递给form中的插槽,在组件中使用page-search,并把搜索框配置数据searchFormConfig传递给page-search
搜索与重置按钮在
3个组件中的formData需要同步,思路如下:
- // 如果直接把modelValue绑定到form上,则当用户输入时,会直接修改modelValue的值,违背了单向数据流
- const formData = ref({ ...prop.modelValue })
-
- // 当用户输入的数据发送变化,把最新的数据发送到page-search
- watch(
- formData,
- (newValue) => {
- emit('update:modelValue', newValue)
- },
- { deep: true }
- )
当用户点击重置时,
当用户点击查询时,
基础的table封装在base-ui,多个项目都可以用,接收的数据如下:
- for="item in propData" :key="item.prop">
- <el-table-column v-bind="item">
- <template #default="scope">
- <slot :name="item.slotName" :row="scope.row">
- {{ scope.row[item.prop] }}
- slot>
- template>
- el-table-column>
7.2.2 头部的插槽
7.2.3 底部的插槽
7.1各个模块的数据保存到store中,获取各模块数据的方法放到actions
app.config.globalProperties:定义在这上面的变量在全局任意组件中都可以访问
app.config.perpories.$filters={} :定义全局的filter
$filter.xx:页面中使用
/global/register-properties/中导出配置变量
6.封装面包屑
1.将面包屑封装成一个组件
2.使用时,根据当前path生成数据传入面包屑组件,