# 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
默认
light
```
## 获取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: {}
}
})
```