'admin-21.01.27:升级最新依赖包、处理登录及文档等'

This commit is contained in:
lyt 2021-01-27 17:54:58 +08:00
parent a66f93d59d
commit 6f26e8f602
10 changed files with 330 additions and 62 deletions

View File

@ -108,7 +108,8 @@ module.exports = {
title: '配置',
collapsable: false,
children: [
''
'',
'other'
]
}
],

View File

@ -1,4 +1,4 @@
# 创建 vue3.x vite 项目
# 手把手创建 vue3.x vite 项目
## 安装 vite
@ -23,21 +23,31 @@ npm run dev
## 配置 vite
在项目根目录中创建一个 `vite.config.js``vite.config.ts` 文件与vue.config.js一样。如果在当前工作目录中找到 `Vite`,它将自动使用它。
官网 `config.ts` 配置参考:[https://github.com/vitejs/vite/blob/master/src/node/config.ts](https://github.com/vitejs/vite/blob/master/src/node/config.ts)
- github `config.ts` 配置参考(失效0.x 版本)[https://github.com/vitejs/vite/blob/master/src/node/config.ts](https://github.com/vitejs/vite/blob/master/src/node/config.ts)
- vite最新文档英文[https://vitejs.dev/index.html](https://vitejs.dev/index.html)
配置 `vite.config.ts`
```ts
import type { UserConfig } from 'vite'
const viteConfig: UserConfig = {
port: 8080, // 端口号
hostname: 'localhost', // 主机名
open: true // 运行自动打开浏览器
server: {
port: 8080, // 端口号g
hostname: 'localhost', // 主机名
open: true // 运行自动打开浏览器
}
}
export default viteConfig
```
:::tip vite 配置参考
- github[github/vite/config.ts](https://github.com/vitejs/vite/blob/73196e517643af88a790ab5222d3e6b68dbbf987/packages/vite/src/node/config.ts)
- issues[https://github.com/vitejs/vite/issues/1467](https://github.com/vitejs/vite/issues/1467)
- plugin-vue[@vitejs/plugin-vue](@vitejs/plugin-vue)
- plugins[alias#entries](https://github.com/rollup/plugins/tree/master/packages/alias#entries)
:::
## 安装 typescript
#### 1、安装

View File

@ -0,0 +1,101 @@
# 其它问题
## 批量更新 package.json
我们想用各个依赖包的最新版本。如果手动去修改 `dependencies、devDependencies` 中各个包的版本号,那就太麻烦了,借助 `npm-check-updates` 工具可以很方便的将 `package.json` 中的依赖包版本号更新为最新版本。
::: tip 提示
以下命令都是在 cmd 中执行:
:::
```bash
# 1、安装
cnpm install -g npm-check-updates
# 2、检查 package.json 中是否有更新
ncu
# 3、更新依赖到最新版本 or 更新全部 ncu -a
ncu -u
```
## 更新(升级) vite 2.0 后遇到的问题
- vite文档英文[https://vitejs.dev/index.html](https://vitejs.dev/index.html)
- vite文档中文非官方[https://vite-design.surge.sh/guide/chinese-doc.html](https://vite-design.surge.sh/guide/chinese-doc.html)
`vue.config.js` 配置改变:
#### 1、之前 1.x
```ts
import type { UserConfig } from 'vite'
import { resolve } from 'path'
import { loadEnv } from './build/utils'
const pathResolve = (dir: string): any => {
return resolve(__dirname, '.', dir)
}
const alias: Record<string, string> = {
'/@/': pathResolve('src')
}
const { VITE_PORT, VITE_PUBLIC_PATH, VITE_OPEN } = loadEnv()
const root: string = process.cwd()
const viteConfig: UserConfig = {
root,
alias,
outDir: 'dist',
minify: 'esbuild',
port: VITE_PORT,
open: VITE_OPEN,
base: process.env.NODE_ENV === "production" ? "./" : VITE_PUBLIC_PATH,
optimizeDeps: {
include: ['element-plus/lib/locale/lang/zh-cn']
}
}
export default viteConfig
```
#### 2、现在 2.x`alias、server、build`
:::tip 提示
需要安装 @vitejs/plugin-vue否则 `.vue` 文件报错。安装命令:`cnpm install @vitejs/plugin-vue --save-dev`
:::
```ts
import vue from '@vitejs/plugin-vue'
import type { UserConfig } from 'vite'
import { loadEnv } from './build/utils'
const { VITE_PORT, VITE_PUBLIC_PATH, VITE_OPEN } = loadEnv()
const viteConfig: UserConfig = {
plugins: [vue()],
root: process.cwd(),
alias: [
{
find: /^\/@\//,
replacement: '/src/'
}
],
base: process.env.NODE_ENV === "production" ? VITE_PUBLIC_PATH : './',
optimizeDeps: {
include: ['element-plus/lib/locale/lang/zh-cn']
},
server: {
port: VITE_PORT,
open: VITE_OPEN
},
build: {
outDir: 'dist',
minify: 'esbuild',
sourcemap: false
}
}
export default viteConfig
```

View File

@ -16,3 +16,5 @@ Each page generated by VuePress has its own pre-rendered static HTML, providing
- [vue-i18n-next]: https://vue-i18n-next.intlify.dev/
- [composition-api-vue-i18n-next]: https://vue-i18n-next.intlify.dev/advanced/composition.html#local-scope
- [https://vitepress.vuejs.org/]: https://vitepress.vuejs.org/
- [https://vitejs.dev/]: vite文档

View File

@ -6,19 +6,23 @@
"build": "vite build"
},
"dependencies": {
"element-plus": "^1.0.1-beta.27",
"element-plus": "^1.0.2-beta.30",
"mitt": "^2.1.0",
"sortablejs": "^1.10.2",
"nprogress": "^0.2.0",
"sortablejs": "^1.13.0",
"vue": "^3.0.5",
"vue-router": "^4.0.2",
"vuex": "^4.0.0-rc.2"
},
"devDependencies": {
"@types/node": "^14.14.22",
"@types/nprogress": "^0.2.0",
"@types/sortablejs": "^1.10.6",
"@vitejs/plugin-vue": "^1.1.2",
"@vue/compiler-sfc": "^3.0.5",
"sass": "^1.30.0",
"sass-loader": "^10.1.0",
"typescript": "^4.1.2",
"vite": "^1.0.0-rc.13"
"sass": "^1.32.5",
"sass-loader": "^10.1.1",
"typescript": "^4.1.3",
"vite": "^2.0.0-beta.50"
}
}

View File

@ -1,5 +1,8 @@
import { createRouter, createWebHashHistory, RouteRecordRaw } from "vue-router"
import NProgress from 'nprogress';
import 'nprogress/nprogress.css';
import { store } from "/@/store/index.ts"
import { getSession, clearSession } from "/@/utils/storage.ts"
// 定义动态路由
export const dynamicRoutes = [
@ -455,6 +458,7 @@ export const dynamicRoutes = [
const staticRoutes: Array<RouteRecordRaw> = [
{
path: '/login',
name: 'login',
component: () => import('/@/views/login/index.vue'),
meta: {
title: '登陆'
@ -462,6 +466,7 @@ const staticRoutes: Array<RouteRecordRaw> = [
},
{
path: '/404',
name: '404',
component: () => import('/@/views/error/404.vue'),
meta: {
title: '找不到此页面'
@ -469,6 +474,7 @@ const staticRoutes: Array<RouteRecordRaw> = [
},
{
path: '/401',
name: '401',
component: () => import('/@/views/error/401.vue'),
meta: {
title: '没有权限'
@ -476,6 +482,7 @@ const staticRoutes: Array<RouteRecordRaw> = [
},
{
path: '/:pathMatch(.*)',
name: 'pathMatch',
redirect: '/404',
meta: {
title: '页面找不到重定向'
@ -528,7 +535,6 @@ export function setCacheTagsViewRoutes() {
// 获取当前用户的权限去比对路由表,用于左侧菜单/横向菜单的显示
export function setFilterMenu() {
if (store.state.auths.length <= 0) store.dispatch('setAuths')
store.dispatch("setRoutes", setFilterMenuFun(dynamicRoutes[0].children, store.state.auths))
}
@ -538,7 +544,7 @@ export function hasAuth(auths: any, route: any) {
else return true
}
// 递归过滤权限的路由
// 递归过滤权限的路由
export function setFilterMenuFun(routes: any, auth: any) {
const menu: any = []
routes.map((route: any) => {
@ -553,7 +559,6 @@ export function setFilterMenuFun(routes: any, auth: any) {
// 获取当前用户的权限去比对路由表,用于动态路由的添加
export function setFilterRoute() {
if (store.state.auths.length <= 0) store.dispatch('setAuths')
let filterRoute: any = []
formatTwoStageRoutes(formatFlatteningRoutes(dynamicRoutes))[0].children.map((route: any) => {
route.meta.auth.map((metaAuth: any) => {
@ -587,14 +592,46 @@ export function resetRoute() {
})
}
// 初始化执行函数
setAddRoute()
setFilterMenu()
setCacheTagsViewRoutes()
// 初始化方法,防止刷新时丢失
export function initAllFun() {
const token = getSession('token')
if (!token) return false
store.dispatch('setAuths')
setAddRoute() // 添加动态路由
setFilterMenu() // 过滤权限菜单
setCacheTagsViewRoutes() // 添加 keepAlive 缓存
}
// 初始化方法执行
initAllFun()
// router.afterEach((to, from) => {
// 路由加载前
router.beforeEach((to, from, next) => {
document.title = `${to.meta.title} - vue-admin-wonderful-next` || 'vue-admin-wonderful-next'
NProgress.configure({ showSpinner: false })
NProgress.start()
const token = getSession('token')
if (to.path === '/login' && !token) {
next()
NProgress.done()
} else {
if (!token) {
next('/login')
clearSession()
resetRoute()
NProgress.done()
} else if (token && to.path === '/login') {
next('/home')
NProgress.done()
} else {
next();
}
}
})
// })
// 路由加载后
router.afterEach(() => {
NProgress.done()
})
export default router

View File

@ -1,6 +1,7 @@
import { InjectionKey } from 'vue'
import { createStore, useStore as baseUseStore, Store } from 'vuex'
import themeConfig from '/@/utils/themeConfig.ts'
import { getSession } from "/@/utils/storage.ts";
export interface RootStateTypes {
themeConfig: {
isDrawer: boolean,
@ -59,42 +60,57 @@ export const store = createStore<RootStateTypes>({
auths: []
},
mutations: {
// 设置布局配置
getThemeConfig(state: any, data: object) {
state.themeConfig = Object.assign({}, data)
},
// 设置路由,菜单中使用到
getRoutes(state: any, data: Array<object>) {
state.routes = data
},
// 设置缓存name字段
getCacheKeepAlive(state: any, data: Array<string>) {
state.caches = data
},
// 设置 TagsView 路由
getTagsViewRoutes(state: any, data: Array<string>) {
state.tagsViewRoutes = data
},
// 设置权限
getAuths(state: any, data: Array<string>) {
state.auths = data
}
},
actions: {
// 设置布局配置
setThemeConfig({ commit }, data: object) {
commit('getThemeConfig', data)
},
// 设置路由,菜单中使用到
async setRoutes({ commit }, data: any) {
commit('getRoutes', data)
},
// 设置缓存name字段
async setCacheKeepAlive({ commit }, data: Array<string>) {
commit('getCacheKeepAlive', data)
},
// 设置 TagsView 路由
async setTagsViewRoutes({ commit }, data: Array<string>) {
commit('getTagsViewRoutes', data)
},
setAuths({ commit }, data: Array<string>) {
const defaultAuthList: Array<string> = ['admin', 'btn.add', 'btn.del', 'btn.edit']
// 设置权限
async setAuths({ commit }, data: Array<string>) {
// 模拟权限,实际项目中,请通过直接走接口获取权限标识
let authList: Array<string> = []
if (data && data.length > 0) authList = data
else authList = defaultAuthList
if (getSession('defaultAuthList')) {
authList = getSession('defaultAuthList')
} else {
let defaultAuthList: Array<string> = ['admin', 'btn.add', 'btn.del', 'btn.edit']
if (data && data.length > 0) authList = data
else authList = defaultAuthList
}
commit('getAuths', authList)
}
},
}
})

View File

@ -8,7 +8,7 @@
</div>
<div class="layout-navbars-breadcrumb-user-icon"><i class="el-icon-bell" title="消息"></i></div>
<div class="layout-navbars-breadcrumb-user-icon mr10"><i class="icon-fullscreen iconfont" title="开全屏"></i></div>
<el-dropdown :show-timeout="70" :hide-timeout="50">
<el-dropdown :show-timeout="70" :hide-timeout="50" @command="onHandleCommandClick">
<span class="layout-navbars-breadcrumb-user-link">
<img src="https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=1813762643,1914315241&fm=26&gp=0.jpg"
class="layout-navbars-breadcrumb-user-link-photo mr5" /> small@小柒
@ -16,12 +16,11 @@
</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item>个人中心</el-dropdown-item>
<el-dropdown-item>首页</el-dropdown-item>
<el-dropdown-item>文档</el-dropdown-item>
<el-dropdown-item>404</el-dropdown-item>
<el-dropdown-item>401</el-dropdown-item>
<el-dropdown-item divided>退出登录</el-dropdown-item>
<el-dropdown-item command="/home">首页</el-dropdown-item>
<el-dropdown-item command="/personal">个人中心</el-dropdown-item>
<el-dropdown-item command="/404">404</el-dropdown-item>
<el-dropdown-item command="/401">401</el-dropdown-item>
<el-dropdown-item divided command="logOut">退出登录</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
@ -30,15 +29,18 @@
<script lang="ts">
import { ref, getCurrentInstance, computed } from "vue";
import { useRouter } from "vue-router";
import { ElMessageBox, ElMessage } from "element-plus";
import { resetRoute } from "/@/router/index.ts";
import { useStore } from "/@/store/index.ts";
import { clearSession } from "/@/utils/storage.ts";
export default {
name: "layoutBreadcrumbUser",
setup() {
const { proxy } = getCurrentInstance();
const router = useRouter();
const store = useStore();
const onLayoutSetingClick = () => {
proxy.mittBus.emit("openSetingsDrawer");
};
//
const setFlexAutoStyle = computed(() => {
if (
!store.state.themeConfig.isBreadcrumb &&
@ -47,9 +49,53 @@ export default {
)
return { flex: 1 };
});
// icon
const onLayoutSetingClick = () => {
proxy.mittBus.emit("openSetingsDrawer");
};
//
const onHandleCommandClick = (path) => {
if (path === "logOut") {
ElMessageBox({
closeOnClickModal: false,
closeOnPressEscape: false,
title: "提示",
message: "此操作将退出登录, 是否继续?",
showCancelButton: true,
confirmButtonText: "确定",
cancelButtonText: "取消",
beforeClose: (action, instance, done) => {
if (action === "confirm") {
instance.confirmButtonLoading = true;
instance.confirmButtonText = "退出中";
setTimeout(() => {
done();
setTimeout(() => {
instance.confirmButtonLoading = false;
}, 300);
}, 700);
} else {
done();
}
},
})
.then((action) => {
clearSession(); // /token
resetRoute(); // /
router.push("/login");
setTimeout(() => {
ElMessage.success("退出成功!记得回来哟~");
}, 300);
})
.catch(() => {});
} else {
router.push(path);
}
};
return {
onLayoutSetingClick,
setFlexAutoStyle,
onLayoutSetingClick,
onHandleCommandClick,
};
},
};

View File

@ -1,7 +1,7 @@
<template>
<el-form class="login-content-form">
<el-form-item>
<el-input type="text" placeholder="用户名 admin 或 test" prefix-icon="el-icon-user" v-model="ruleForm.userName"
<el-input type="text" placeholder="用户名 admin 或不输均为 test" prefix-icon="el-icon-user" v-model="ruleForm.userName"
clearable autocomplete="off">
</el-input>
</el-form-item>
@ -24,7 +24,7 @@
</el-row>
</el-form-item>
<el-form-item>
<el-button type="primary" class="login-content-submit" round>
<el-button type="primary" class="login-content-submit" round @click="onSignIn">
<span> </span>
</el-button>
</el-form-item>
@ -32,18 +32,68 @@
</template>
<script lang="ts">
import { toRefs, reactive, defineComponent } from "vue";
import { toRefs, reactive, defineComponent, computed } from "vue";
import { useRouter } from "vue-router";
import { ElMessage } from "element-plus";
import {
setAddRoute,
setFilterMenu,
setCacheTagsViewRoutes,
} from "/@/router/index.ts";
import { useStore } from "/@/store/index.ts";
import { setSession } from "/@/utils/storage.ts";
import { formatAxis } from "/@/utils/formatTime";
export default defineComponent({
name: "login",
setup() {
const store = useStore();
const router = useRouter();
const state = reactive({
ruleForm: {
userName: "",
password: "",
code: "",
userName: "admin",
password: "123456",
code: "1234",
},
});
//
const currentTime = computed(() => {
return formatAxis(new Date());
});
//
const initAllFun = () => {
setAddRoute();
setFilterMenu();
setCacheTagsViewRoutes();
};
//
const onSignIn = () => {
let currentTimeInfo = currentTime.value;
let defaultAuthList: Array<string> = [];
let adminAuthList: Array<string> = [
"admin",
"btn.add",
"btn.del",
"btn.edit",
];
let testAuthList: Array<string> = ["test", "btn.add"];
if (state.ruleForm.userName === "admin") defaultAuthList = adminAuthList;
else defaultAuthList = testAuthList;
store.dispatch("setAuths", defaultAuthList);
initAllFun();
setSession("defaultAuthList", defaultAuthList);
setSession("token", Math.random().toString(36).substr(0));
setSession("userInfo", {
userName: state.ruleForm.userName,
time: new Date().getTime(),
});
router.push("/");
setTimeout(() => {
ElMessage.success(`${currentTimeInfo},欢迎回来!`);
}, 300);
};
return {
currentTime,
onSignIn,
...toRefs(state),
};
},

View File

@ -1,29 +1,30 @@
import vue from '@vitejs/plugin-vue'
import type { UserConfig } from 'vite'
import { resolve } from 'path'
import { loadEnv } from './build/utils'
const pathResolve = (dir: string): any => {
return resolve(__dirname, '.', dir)
}
const alias: Record<string, string> = {
'/@/': pathResolve('src')
}
const { VITE_PORT, VITE_PUBLIC_PATH, VITE_OPEN } = loadEnv()
const root: string = process.cwd()
const viteConfig: UserConfig = {
root,
alias,
outDir: 'dist',
minify: 'esbuild',
port: VITE_PORT,
open: VITE_OPEN,
base: process.env.NODE_ENV === "production" ? "./" : VITE_PUBLIC_PATH,
plugins: [vue()],
root: process.cwd(),
alias: [
{
find: /^\/@\//,
replacement: '/src/'
}
],
base: process.env.NODE_ENV === "production" ? VITE_PUBLIC_PATH : './',
optimizeDeps: {
include: ['element-plus/lib/locale/lang/zh-cn']
},
server: {
port: VITE_PORT,
open: VITE_OPEN
},
build: {
outDir: 'dist',
minify: 'esbuild',
sourcemap: false
}
}