# vue3-ts-admin **Repository Path**: wjkfree/vue3-ts-admin ## Basic Information - **Project Name**: vue3-ts-admin - **Description**: vue3+ts+pinia - **Primary Language**: JavaScript - **License**: MIT - **Default Branch**: master - **Homepage**: https://gitee.com/wjkfree/vue3-ts-admin - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2022-10-10 - **Last Updated**: 2024-05-20 ## Categories & Tags **Categories**: Uncategorized **Tags**: vue3, TypeScript ## README # vue3-ts-admin vue3 + ts elelmentPlus:https://element-plus.gitee.io/zh-CN/component/config-provider.html vue3-admin-plus:https://github.com/jzfai/vue3-admin-plus ## 目标功能 - [x] 国际化语言 - [x] 动态路由 - [x] 主题风格 - [x] 引导页 - [ ] 通知 - [ ] 全屏 - [ ] 角色权限 - [ ] 个人信息 - [ ] tagsview - [ ] 锁屏技术 - [ ] echarts图表 - [ ] 水流图 - [ ] 文件上传下载 - [ ] 表格 - [ ] canvas - [ ] threejs - [ ] cesium - [ ] countTo - [ ] 第三方地图 - [ ] mapboxgl - [ ] mockjs - [ ] 第三方组件 - [ ] 富文本编辑器 - [ ] markdown - [ ] 拖拽 dialog - [ ] 列表拖拽 - [ ] 可拖拽看板 - [ ] 卡片拖拽 - [ ] Table - [ ] 动态Table - [ ] 拖拽Table - [ ] Table内编辑 - [ ] 创建文章/文章列表 - [ ] 性能监控 - [ ] 文件导出 - [ ] 可选择文件后缀 - [ ] 导出已选择项 - [ ] 导出多级表头 - [ ] 上传excel,点击选择文件/直接拖到区域内 - [ ] 导出zip - [ ] 表单 - [ ] 可校验 - [ ] 分布表单 - [ ] 日历 - [ ] 一键复制 - [ ] 工作流 - [ ] 时间轴(提交记录) - [ ] 水印 - [ ] 生成二维码 - [ ] 第三方登录 - [ ] 实时聊天 - [ ] 大文件上传(断点续传) - [ ] websocket测试 - [ ] 瀑布流 - [ ] 图片懒加载 - [ ] 虚拟滚动 - [ ] 服务部署 ## vscode 设置注释模板 1. vscode 里面按下 `shift+ctrl+p`, 2. 输入`snippets` 3. 自定义文件名称,然后回车,就进入到了文件 4. 代码模板 ```json { "Print to jsnote": { "prefix": "jsnote", //这里是快捷键方法 "body": [ "/**", " * @Description:", " * @Author: wjk", " * @Date: $CURRENT_YEAR/$CURRENT_MONTH/$CURRENT_DATE $CURRENT_HOUR:$CURRENT_MINUTE:$CURRENT_SECOND", " * @LastEditors: wjk", " * @LastEditTime: $CURRENT_YEAR/$CURRENT_MONTH/$CURRENT_DATE $CURRENT_HOUR:$CURRENT_MINUTE:$CURRENT_SECOND", " */" ], "description": "create jsnote info" }, "Print to htmlnote": { "prefix": "htmlnote", //这里是快捷键方法 "body": [ "" ], "description": "create htmlnote info" } } ``` 5. 在代码输出 `jsnote` 然后回车 ```js /** * @Description: * @Author: wjk * @Date: 2022/10/10 18:40:38 * @LastEditors: wjk * @LastEditTime: 2022/10/10 18:40:38 */ ``` ## vscode 设置注释模板2 安装插件 koro1FileHeader [快速上手](https://github.com/OBKoro1/koro1FileHeader/wiki/%E5%AE%89%E8%A3%85%E5%92%8C%E5%BF%AB%E9%80%9F%E4%B8%8A%E6%89%8B) ## 无法找到模块 xxx 的声明文件 无法找到模块“element-plus/dist/locale/en”的声明文件。 如果“element-plus”包实际公开了此模块,请尝试添加包含 `declare module‘element-plus/dist/locale/en';` 的新声明(.d.ts)文件 解决方案: 新建 `src/shims-vue.d.ts`(使用 vue-cli 创建的项目会自动创建此文件) ```js declare module 'element-plus/dist/locale/zh-cn' declare module 'element-plus/dist/locale/en' ``` ## vue-i18n 告警 原因:在 `vue-i18n/index.js` 中 ```js 'use strict' if (process.env.NODE_ENV === 'production') { module.exports = require('./dist/vue-i18n.cjs.prod.js') } else { module.exports = require('./dist/vue-i18n.cjs.js') } ``` 解决:在 `vue.config.js` 中添加 ```js module.exports = defineConfig({ transpileDependencies: true, configureWebpack: () => { return { ... resolve: { alias: { 'vue-i18n': 'vue-i18n/dist/vue-i18n.cjs' } } } } }) ``` ## 全局引入scss文件 [style-resources-loader](https://www.npmjs.com/package/vue-cli-plugin-style-resources-loader) ```shell vue add style-resources-loader ``` Example ```js const path = require('path') module.exports = { pluginOptions: { 'style-resources-loader': { 'preProcessor': 'scss', 'patterns': [ path.resolve(__dirname, 'src/styles/_variables.scss'), ] } } } ``` ## 在 ts 中使用 webpack.ProvidePlugin 定义的全局变量 ```js const webpack = require('webpack') module.exports = defineConfig({ configureWebpack: () => { return { plugins: [ AutoImport({ resolvers: [ElementPlusResolver()] }), Components({ resolvers: [ElementPlusResolver()] }), new webpack.ProvidePlugin({ dayjs: 'dayjs', _: [path.resolve(__dirname, 'src/libs/lodash.ts'), 'default'], echarts: [path.resolve(__dirname, './src/libs/echarts.ts'), 'default'] }) ] } } }) ``` `src/libs/echarts.ts` ```js // 加载echarts,注意引入文件的路径 import * as echarts from 'echarts/lib/echarts' // 再引入你需要使用的图表类型,标题,提示信息等 import 'echarts/lib/chart/line' import 'echarts/lib/chart/bar' import 'echarts/lib/chart/pie' import 'echarts/lib/chart/sankey' import 'echarts/lib/chart/gauge' import 'echarts/lib/chart/radar' import { GridComponent, TitleComponent, TooltipComponent, LegendComponent, ToolboxComponent, DataZoomComponent, VisualMapComponent } from 'echarts/components' echarts.use([ GridComponent, TitleComponent, TooltipComponent, LegendComponent, ToolboxComponent, DataZoomComponent, VisualMapComponent ]) export default echarts ``` `src/libs/lodash.ts` ```js import { cloneDeep, chunk, debounce } from 'lodash' export default { cloneDeep, chunk, debounce } ``` `.eslintrc` ```js { ... "globals": { "dayjs": true, "echarts": true, "_": true } } ``` `src/shims-vue.d.ts` ```js declare const _ declare const dayjs declare const echarts ``` ## 动态路由 路由列表由后端接口返回,保存到本地(前端定义好格式,发给后端) 前台在路由导航守卫 `router.beforeEach` 中进行操作 先声明一个变量 `routerCache`,值为 `undefined`,判断`routerCache` 值是否为 `false`,如果为 `false `,将本地保存的路由赋值给 `routerCache`,并且添加到路由中 后端返回的路由数据 ```js const routeList = [ { path: '/about', name: 'about', component: 'Layout', children: [ { path: '/about', component: 'about/index.vue', meta: { title: 'about', icon: 'about' } } ] }, { path: '/permission', name: 'permission', component: 'Layout', meta: { title: 'permission', icon: 'permission' }, children: [ { path: 'user', name: 'User', component: 'permission/user/index.vue', meta: { title: 'user' } }, { path: 'group', name: 'Group', component: 'permission/group/index.vue', meta: { title: 'group' } } ] } ] ``` 在路由导航守卫中进行添加 ```js import router from './router' import NProgress from 'nprogress' import { RouteLocationNormalized, NavigationGuardNext } from 'vue-router' import Layout from '@/layout/index.vue' import { getRoutes, getToken } from './utils/session' const whiteList = ['/login'] const handleChild = (child: any) => { child.forEach(async (c: any) => { const component = c.component c.component = () => import(`@/views/${component}`) }) } let routerCache: undefined router.beforeEach((to: RouteLocationNormalized, from: RouteLocationNormalized, next: NavigationGuardNext) => { NProgress.start() if (getToken()) { if (!routerCache) { routerCache = getRoutes() // 缓存路由数据,避免页面在不刷新时,每次都执行路由添加的方法 // 加载动态路由 getRoutes().forEach((item: any) => { handleChild(item.children) router.addRoute({ path: item.path, name: item.name, component: Layout, children: item.children }) }) next({ ...to, replace: true }) } else { const routes = router.getRoutes().map(r => r.path) if (!routes.includes(to.path)) { next('/404') } else { next() } } } else { if (whiteList.includes(to.path)) { next() } else { next('/login') } } }) router.afterEach(() => { NProgress.done() }) ``` ## 主题风格 通过给 `html` 添加 `class` 类名,来进行切换 新建 `theme` 文件夹 `theme/index.scss` ```scss @import './base/index.scss'; @import './light/index.scss'; :root { --transition-linear: all .3s linear; // 规定以相同速度开始至结束的过渡效果(等于 cubic-bezier(0,0,1,1)) --transition-ease: all .3s ease; // 规定慢速开始,然后变快,然后慢速结束的过渡效果(cubic-bezier(0.25,0.1,0.25,1)) --transition-ease-in: all .3s ease-in; // 规定以慢速开始的过渡效果(等于 cubic-bezier(0.42,0,1,1)) --transition-ease-out: all .3s ease-out; // 规定以慢速结束的过渡效果(等于 cubic-bezier(0,0,0.58,1)) --transition-ease-in-out: all .3s ease-in-out; // 规定以慢速开始和结束的过渡效果(等于 cubic-bezier(0.42,0,0.58,1)) } ``` `theme/base/index.scss` ```scss html.base-theme { --bg-color: #a0cfff; --text-color: #bfcbd9; --hover-text-color: #ff6700; } ``` `theme/light/index.scss` ```scss html.light-theme { --bg-color: #e8e8e8; } ``` 在 `main.ts` 中引入 `import './theme/index.scss'` `html` 中添加类名 ```html ... ``` 给 `button` 添加点击事件,切换类名即可 ```vue ``` ## 获取git提交记录 使用 [nodegit](https://github.com/nodegit/nodegit) ```bash npm i nodegit@next ``` 具体实现方法(结合 koa-router 使用) ```js const Git = require("nodegit"); homeRouter.get('/git/log', async (ctx: any) => { const data = await getCommits() ctx.body = { code: 200, message: '成功', data } }) async function getCommits () { const commitList:any = [] const repo = await Git.Repository.open('../') const commit = await repo.getBranchCommit('master') const history = await commit.history(); history.start() return new Promise(resolve => { history.on("commit", function(c: any) { const author = c.author() commitList.push({ sha: c.sha(), author: author.name() + " <" + author.email() + ">", data: c.date(), message: c.message(), summary: c.summary(), }) if (c.date().getTime() === 1665394031000) { resolve(commitList) } }) }) } ``` ## 文件上传 前端: ```js const uploadFile = () => { const input = document.createElement('input') input.type = 'file' input.accept = '.png,.jpg,.jpeg,.gif' input.click() input.addEventListener('change', async (e: any) => { const file = e.target.files[0] console.dir(file) const formData = new FormData() formData.append('file', file) formData.append('type', 'image') const res = await fileUpload(formData) console.log(res) }) } ``` 后端: 需要结合 [koa-body](https://www.npmjs.com/package/koa-body) 使用 ```js const Koa = require('koa') const router = require('koa-router')() const { koaBody } = require('koa-body') const app = new Koa() const port = 5100 // 跨域 app.use(cors()) // 参数解析 app.use(koaBody({ multipart: true, formidable: { keepExtensions: true, } })) router.post('/file/upload', (ctx: any) => { const file = ctx.request.files.file; // 获取上传文件 // 创建可读流 const reader = fs.createReadStream(file.filepath); const filePath = path.join(__dirname, '../upload/') + `/${file.originalFilename}` // 创建可写流 const upStream = fs.createWriteStream(filePath) // 可读流通过管道写入可写流 reader.pipe(upStream); ctx.body = { code: 200, message: '上传成功', data: {} } }) ```