[关闭]
@heqinglin 2023-05-04T10:40:42.000000Z 字数 6564 阅读 33

Vue3-admin框架解析

文档 vue


关于layout

layout 模板选择由 状态管理控制 store/modules/app.ts layout,动态修改也是改这个,系统默认把layout的值加到了缓存里(wsCache封装的),如果想一套系统两套2布局需要修改。
新增布局需要在 src/layout/Layout.vue 在renderLayout()中增加stitch、然后在useRenderLayout.tsx中新增模板

关于wsCache 本地缓存

封装自web-storage-cache,可直接存储对象数组,位置在src/hooks/web/useCache.ts,其实就是webstorage,不需要JSON.stringify() JSON.parse()插件自动帮我们做了
使用

  1. import { useCache } from '@/hooks/web/useCache'
  2. const { wsCache } = useCache()
  3. wsCache.set('key', value)
  4. wsCache.get('key')
  5. wsCache.clear() //全部清除
  6. wsCache.set('username', 'wqteam', {exp : 100});//超时100s
  7. wsCache.deleteAllExpires();// 手工删除所有超时CacheItem,

登录状态token就是存在这里的

关于mitt 跨组件通信

全局的总线程类似vue2的EventBus,实现发布订阅

  1. mitt.emit('mymsg',data);
  2. mitt.on('mymsg',(data)=>{
  3. console.log("接收到的内容是:"+ data);
  4. });

使用

  1. import { useEmitt } from '@/hooks/web/useEmitt'
  2. const { emitter } = useEmitt()
  3. emitter.emit('getList', 'add') //发送数据
  4. //接收数据
  5. useEmitt({
  6. name: 'getList',
  7. callback: (type: string) => {
  8. console.log(type) //add
  9. }
  10. })

关于类名空间

封装在 src/hooks/web/userDesign.ts
getPrefixCls(scope)方法返回一个拼接的字符串 v-scope

  1. return `${lessVariables.namespace}-${scope}`

样式, 比如 scope = layout

  1. <style lang="less" scoped>
  2. @prefix-cls: ~'@{namespace}-layout';
  3. .@{prefix-cls} {}
  4. </style>

个人不喜欢这种马甲式调来调去的方式,顶层class根据页面命名,一目了然最好。

关于登录状态设计

登录成功, 把用户信息缓存wsCache.set(appStore.getUserInfo, res.data)appStore.getUserInfo是store中预设的key,默认为'userInfo'

缓存用于导航守卫在页面跳转时,判断是否登陆。导航守卫代码位于 src/permission.ts

关于动态路由

store/modules/app.ts中设置
dynamicRouter: wsCache.get('dynamicRouter') || false, // 是否动态路由

动态路由是根据账号来加载不同的路由,框架写死了2种账号权限,admin和test。
在mock/role/index.ts 分别维护两个list,模拟接口返回。
调用 store/modules/permission.tsgenerateRoutes 方法会根据账号类型,把动态路由与静态路由合并。
admin/test 合并之前,有个generateRoutesFn函数,对后端返回的路由,与asyncRouterMap 进行核对,将有权限访问的筛选出来,有权访问但项目缺失的路由也会打印错误提示。
详情见【动态路由完全解读】

关于环境变量

vite打包已经没有node环境下的process全局变量了,需要如下使用环境变量

  1. import.meta.env.VITE_APP_TITLE

关于await

await可以直接解出promise的返回值,相当于获取then()的值,如果遇到reject则什么也不会走,可以用catch劫持。

  1. async () => {
  2. const res = await loginOutApi().catch(() => {})
  3. }

关于 @vueuse/core

https://vueuse.org/guide/
VueUse 是一组基于Composition API的实用函数,也是尤雨溪团队出,旨在将复杂的功能实现简单化。
使用

  1. import { 具体方法 } from '@vueuse/core'

关于 require

vite不支持 require,需要安装插件
https://github.com/WarrenJones/vite-plugin-require-transform

  1. npm i vite-plugin-require-transform --save-dev
  2. // vite.config
  3. import requireTransform from 'vite-plugin-require-transform';
  4. export default defineConfig({
  5. plugins: [
  6. requireTransform({
  7. fileRegex: /.ts$|.tsx$ |.vue$/
  8. }),
  9. ],
  10. });

关于iframe

借助requre(.html?raw)
https://cn.vitejs.dev/config/shared-options.html#assetsinclude
vite.config.ts

  1. assetsInclude: ['**/*.html'],

完整

  1. <template>
  2. <div class="iframe_box">
  3. <iframe width="100%" height="800px" frameborder="0" id="myIframe"></iframe>
  4. </div>
  5. </template>
  6. <script setup lang="ts">
  7. import { onMounted, nextTick } from 'vue'
  8. const iframeData = require('/static/pdfjs-3.1.81-dist/web/viewer.html?raw')
  9. onMounted(() => {
  10. nextTick(() => {
  11. const myIframe: any = document.getElementById('myIframe')
  12. myIframe.contentDocument.documentElement.innerHTML = iframeData
  13. })
  14. })
  15. </script>

关于状态管理

  1. import { useAppStore } from '@/store/modules/app'
  2. const appStore = useAppStore()
  3. const footer = computed(() => appStore.getFooter) //取值

关于响应

src/components/ConfigGlobal.vue 中 watch width store.setLayout() actions实现切换layout
并且layout是存在wsCache

关于 windicss

https://windicss.org/utilities/layout/display.html

  1. flex display: flex;
  2. inline-block display: inline-block;
  3. justify-start justify-content: flex-start;
  4. justify-between justify-content: space-between;
  5. justify-around justify-content: space-around;
  6. items-center align-items: center;
  7. flex-col flex-direction: column;
  8. p-0 padding: 0px;
  9. px-0 padding-left: 0px;
  10. padding-right: 0px;
  11. py-0 padding-top: 0px;
  12. padding-bottom: 0px;
  13. pt-0 padding-top: 0px;
  14. pt-0 padding-right: 0px;
  15. pb-0 padding-bottom: 0px;
  16. pl-0 padding-left: 0px;
  17. m-0px margin: 0px;
  18. mb-30px margin-bottom: 30px;
  19. w-30px width: 30px;
  20. w-[100%] width100%;
  21. max-w-500px width: max-width:500px;
  22. h-30px height: 30px;
  23. leading-[35px] line-height:35px;
  24. text-16px font-size: 16px;
  25. line-height: 1;
  26. text-[#ff9f15] color:#ff9f15;
  27. text-center text-align:center;
  28. bg-[#ff9f15] background-color:#ff9f15;
  29. font-bold font-weight: 700;
  30. overflow-hidden overflow: hidden;
  31. rounded-full border-radius:50% //border-radius: 9999px;
  32. rounded boredr-radius:4px
  33. rounded-md boredr-radius:6px
  34. border-bottom-1 border-bottom-width: 1px;
  35. border-solid border-style: solid;

响应pc优先

  1. <sm:hidden @media (max-width: 640px){display:none;} //屏幕小于640执行
  2. <md @media (max-width: 768px)
  3. <lg @media (max-width: 1024px)
  4. <xl @media (max-width: 1280px)
  5. <2xl @media (max-width: 1536px)

关于::v-deep

vue3已弃用,改成 :deep(inner-select){}

Table组件增加选中监听

src/components/Table/src/Table.vue ,96

让element-table的 selection-change 事件可以从父组件监听

  1. const selectionChange = (selection: Recordable[]) => {
  2. selections.value = selection
  3. emit('seleChange',selection)
  4. }

使用

  1. <Table @sele-change="selectionChange">

只想取一下选中项可用ref方法,作者已通过暴露属性的方法给父组件使用
refTable.value?.selections

Form ref方式取表单的值

unref(formRef)?.formModel

git commit 时跳过husky校验

  1. git commit -m 'message' --no-verify

或者删除 根目录下 .husky 中的commit-msg、pre-commit

window等全局对象的类型

根目录/types/env.d.ts 或者 global.d.ts

  1. declare interface Window {
  2. BMapGL: any
  3. mapvgl: any
  4. BMapGLLib: any
  5. stylejson: any
  6. }

使用lodash

项目已经安装了lodash-es 支持摇树优化

  1. import {findKey} from 'lodash-es'
  2. //不推荐
  3. import * as _ from 'lodash-es'
  4. _.findKey(...)

使用websocket

封装了单例模式,用于跨组件操作
使用:直接 webSocket.instance.xxx 在实例上调用方法
注意:
只有send方法需要在init的回调中,确保链接成功才能发送。
组件卸载的回调里记得.instance.close()断开链接

  1. import ws from '@/utils/webSocket'
  2. //初始化
  3. ws.instance.init('ws://localhost:3000', () => {
  4. console.log('连接成功的回调:', ws.instance.getState())
  5. ws.instance.send('hello')
  6. })
  7. console.log(ws.instance.getState())
  8. //接收数据
  9. ws.instance.onMessage((res) => {
  10. console.log(res)
  11. })

设置主题是如何工作的

src/store/modules/app.ts 可以设置theme, 它其实是和action中的setCssVarTheme 一起工作的。
APP.vue主入口使用了一个自定义的ConfigGlobal组件,用来设置全局EL组件,在这里面也运行了设置主题的代码。

  1. onMounted(() => {
  2. appStore.setCssVarTheme()
  3. })

会把样式变量定义在document.documentElement也就是html上,然后就可以全局使用了。

关于组件的defineProps({})

'vue-types'进行类型约束,位置在 utils/propTypes.ts。
下面介绍下vue-types:
vue-types默认导出一个模拟 React prop-type 的 ES6 类对象

  1. import VueTypes from 'vue-types'
  2. export default {
  3. props: {
  4. message: VueTypes.string.isRequired,
  5. }
  6. }

也可以把这个类对象解构出来,每一个都是工厂函数。

  1. import { number, oneOf } from 'vue-types'
  2. export default {
  3. props: {
  4. id: number().isRequired,
  5. status: oneOf(['open', 'close']).def('open'),
  6. },
  7. }

vue-types还提供了一个createTypes()函数用来自定义命名空间。

  1. import { createTypes } from 'vue-types'
  2. const MyTypes = createTypes()
  3. export default {
  4. props: {
  5. message: MyTypes.string.isRequired
  6. }
  7. }

也可以给自定义的命名空间设置默认值,默认的默认值也是undefined

  1. const MyTypes = createTypes({
  2. string: undefined
  3. })
  1. import { propTypes } from '@/utils/propTypes'
  2. const props = defineProps({
  3. title: propTypes.string.def('xxx'),
  4. })

这个propTypes其实就是自定义的命名空间。

也可以用extend扩展自己的类型

  1. createTypes().extend([
  2. {
  3. name: 'style',
  4. getter: true,
  5. type: [String, Object],
  6. default: undefined
  7. }
  8. ])
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注