演示 Demo
地址: https://shop.fed.lagou.com/admin/
测试账号: lagou / 123456 zce wanglei
一、使用 Vite 创建项目 参考 Vite 官方指南
npm init vite@latest√ Project name: ... lagou-shop-admin
√ Select a framework: ? vue
√ Select a variant: ? vue-tsScaffolding project in C:\Users\lpz\Projects\lagou-shop-admin...Done. Now run:cd lagou-shop-admin
npm install
npm run dev
初始目录结构说明
.
├── public
│└── favicon.ico
├── src
│├── assets
││└── logo.png
│├── components
││└── HelloWorld.vue
│├── App.vue
│├── main.ts
│├── shims-vue.d.ts
│└── vite-env.d.ts
├── .gitignore
├── README.md
├── index.html
├── package-lock.json
├── package.json
├── tsconfig.json
└── vite.config.ts
在安装了 Vite 的项目中,可以在 npm scripts 中使用 vite 可执行文件,或者直接使用 npx vite 运行它。下面是通过脚手架创建的 Vite 项目中默认的 npm scripts:
{
"scripts": {
"dev": "vite", // 启动开发服务器
"build": "vite build", // 为生产环境构建产物
"serve": "vite preview" // 本地预览生产构建产物
}
}
可以指定额外的命令行选项,如 --port 或 --https。运行 npx vite --help 获得完整的命令行选项列表
二、代码规范和 ESLint 基础配置
1、安装 ESLint 到项目中
npm install eslint --save-dev
2、初始化 ESLint 配置
npx eslint --init? How would you like to use ESLint? ...
To check syntax only
To check syntax and find problems
> To check syntax, find problems, and enforce code style? What type of modules does your project use? ...
> JavaScript modules (import/export)
CommonJS (require/exports)
None of these ? Which framework does your project use? ...
React
> Vue.js
None of these? Does your project use TypeScript? ? No / Yes? Where does your code run? ...(Pressto select,to toggle all, to invert selection)
√ Browser
√ Node? How would you like to define a style for your project? ...
> Use a popular style guide
Answer questions about your style
Inspect your JavaScript file(s)? Which style guide do you want to follow? ...
Airbnb: https://github.com/airbnb/javascript
> Standard: https://github.com/standard/standard
Google: https://github.com/google/eslint-config-google
XO: https://github.com/xojs/eslint-config-xo ? What format do you want your config file to be in? ...
> JavaScript
YAML
JSON Checking peerDependencies of eslint-config-standard@latest
The config that you've selected requires the following dependencies:eslint-plugin-vue@latest @typescript-eslint/eslint-plugin@latest eslint-config-standard@latest eslint@^7.12.1 eslint-plugin-import@^2.22.1 eslint-plugin-node@^11.1.0 eslint-plugin-promise@^4.2.1 || ^5.0.0 @typescript-eslint/parser@latest
? Would you like to install them now with npm?+ eslint-plugin-import@2.23.4
+ eslint-plugin-node@11.1.0
+ eslint-config-standard@16.0.3
+ eslint-plugin-vue@7.11.1
+ eslint@7.29.0
+ @typescript-eslint/parser@4.27.0
+ @typescript-eslint/eslint-plugin@4.27.0
+ eslint-plugin-promise@5.1.0
3、ESLint 配置文件
文章图片
这里改成 vue3-strongly-recommended
// .eslintrc.js
module.exports = {
env: {
browser: true,
es2021: true
},
extends: [
// 'plugin:vue/essential',// 使用 Vue 3 规则
// https://eslint.vuejs.org/user-guide/#bundle-configurations
'plugin:vue/vue3-strongly-recommended',
'standard'
],
parserOptions: {
ecmaVersion: 12,
parser: '@typescript-eslint/parser',
sourceType: 'module'
},
plugins: [
'vue',
'@typescript-eslint'
],
rules: {}
}
4、在 npm scripts 中添加验证脚本
"scripts": {
...
"lint": "eslint src/**/*.{js,jsx,vue,ts,tsx} --fix",
}
注意:eslint 后面的路径最好加上引号,否则在类 Unix 系统(比如 macOS)中会报错说找不到资源。
vue-eslint-plugin
https://eslint.vuejs.org/
编译器宏和 defineProps、defineEmits、no-undef 规则警告
您需要定义全局变量 (打开新窗口)在您的 ESLint 配置文件中。
如果您不想定义全局变量,请使用
import { defineProps, defineEmits } from 'vue'
示例
.eslintrc.js:
module.exports = {
globals: {
defineProps: "readonly",
defineEmits: "readonly",
defineExpose: "readonly",
withDefaults: "readonly"
}
}
另请参阅 ESLint - 指定全局变量 > 使用配置文件。
三、编辑器集成 ● 禁用 Vetur
● 安装 eslint 插件
● 安装 volar 插件
使用dbaeumer.vscode-eslint (打开新窗口)微软官方提供的扩展。
您必须配置eslint.validate扩展的选项来检查.vue文件,因为扩展默认只针对*.js或*.jsx文件。
示例**.vscode/settings.json:**
{
"eslint.validate": [
"javascript",
"javascriptreact",
"vue"
]
}
如果您使用该 Vetur 插件,请设置 “vetur.validation.template”: false 为避免默认 Vetur 模板验证。查看vetur 文档 (打开新窗口)了解更多信息
1、在 vscode 中使用 ESLint 规则格式化代码
1)安装 vscode 扩展 ESLint
文章图片
2)在 vscode 配置文件中找到 ESLint 启用该选项
文章图片
3)重启 vscode
4)打开带有 ESLint 配置文件的项目中任意的 .js 或是 .vue 文件
文章图片
右键选择 文档格式设置方式
文章图片
选择 配置默认格式化程序
文章图片
选择 ESLint
6)如果你喜欢保存文件的时候自动格式化代码,也可以开启这个功能
文章图片
7) 如果你修改了项目中 ESLint 的校验规则,一定要重启 vscode 才能生效。
四、配置 git commit hook ● https://github.com/okonet/lint-staged
安装:
npx mrm@2 lint-staged
// package.json
{
"version": "0.0.0",
"scripts": {
"dev": "vite",
"build": "vue-tsc --noEmit && vite build",
"serve": "vite preview",
"tsc": "vue-tsc --noEmit",
"lint": "eslint ./src/**/*.ts ./src/**/*.vue --cache --fix",
"prepare": "husky install"
},
"dependencies": {
"@form-create/element-ui": "^2.5.7",
"axios": "^0.21.1",
"element-plus": "^1.0.2-beta.48",
"nprogress": "^0.2.0",
"path-to-regexp": "^6.2.0",
"utility-types": "^3.10.0",
"vue": "^3.1.1",
"vue-router": "^4.0.8",
"vuex": "^4.0.1",
"vxe-table": "^4.0.22",
"xe-utils": "^3.3.0"
},
"devDependencies": {
"@types/node": "^15.12.2",
"@types/nprogress": "^0.2.0",
"@typescript-eslint/eslint-plugin": "^4.27.0",
"@typescript-eslint/parser": "^4.27.0",
"@vitejs/plugin-vue": "^1.2.3",
"@vue/compiler-sfc": "^3.1.1",
"eslint": "^7.29.0",
"eslint-config-standard": "^16.0.3",
"eslint-plugin-import": "^2.23.4",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^5.1.0",
"eslint-plugin-vue": "^7.11.1",
"husky": "^6.0.0",
"lint-staged": "^11.0.0",
"sass": "^1.34.1",
"typescript": "^4.1.3",
"vite": "^2.3.5",
"vue-tsc": "^0.0.24"
},
"lint-staged": {
"*.{js,jsx,vue,ts,tsx}": [
"npm run lint",
// "git add" 之前的版本需要手动把 lint 过程中修改的代码手动 add,新版本不需要了
]
}
}
1、在开发和构建中进行代码规范校验
● https://github.com/vitejs/awesome-vite#plugins
● https://github.com/gxmari007/vite-plugin-eslint
npm install vite-plugin-eslint --save-dev
vite.config.ts里面做下配置
文章图片
效果:
文章图片
五、Git commit 提交规范 ● Commit message 和 Change log 编写指南
● Git 使用规范流程
● Git 工作流程
统一团队 Git commit 日志标准,便于后续代码 review,版本发布以及日志自动化生成等等。
● commitlint:验证 git commit 日志是否符合规范
● Commitizen:辅助编写符合 git commit 规范的工具
六、Vite中得TS环境说明 ● TS 环境说明
● shimes-vue.d.ts 文件的作用
● vite-env.d.ts 文件的作用
● vue-tsc 和 tsc
○ tsc 只能验证 ts 代码类型
○ vue-tsc 可以验证 ts + Vue Template 中的类型(基于 Volar)
建议在 package.json 中新增一个 scripts 脚本用来单独执行 TS 类型验证:
"scripts": {
...
"build": "npm run tsc && vite build",
"tsc": "vue-tsc -noEmit"
},
-noEmit 表示只验证类型,不输出编译结果。
跳过第三方包类型检查
{
"compilerOptions": {
...
"baseUrl": "./",
"skipLibCheck": true
}
}
1、Vue 3 中的 TS 支持
建议参考:
● https://v3.cn.vuejs.org/guide/typescript-support.html
Vue 3 中的
Vue 3 支持三种写法:
● Option API
● Composition API
● .el-container { height: 100vh; }.el-header { background-color: #fff; color: #333; display: flex; justify-content: space-between; align-items: center; }.el-aside { background-color: #304156; color: #333; }.el-main { background-color: #E9EEF3; color: #333; }
菜单栏
首页
商品
商品列表
商品规格
导航二
导航三
导航四
导航一
分组一
选项1
选项2
选项3
.el-menu {
border-right: none;
}
头部
i {
font-size: 19px;
cursor: pointer;
}
ToggleSidebar
Breadcrumb
{{ item.meta.title }}
MenuSearch
FullScreen
Notification
UserInfo
admin
个人中心
退出登录
十五、配置基础路由页面
import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router'
import AppLayout from '@/layout/AppLayout.vue'
import productRoutes from './modules/product'
import orderRoutes from './modules/order'
import permissionRoutes from './modules/permission'
import mediaRoutes from './modules/media'const routes: RouteRecordRaw[] = [
{
path: '/',
component: AppLayout,
children: [
{
path: '', // 默认子路由
name: 'home',
component: () => import('../views/home/index.vue')
},
productRoutes,
orderRoutes,
permissionRoutes,
mediaRoutes
]
},
{
path: '/login',
name: 'login',
component: () => import('../views/login/index.vue')
}
]const router = createRouter({
history: createWebHashHistory(), // 路由模式
routes // 路由规则
})export default router
// src\router\modules\order.tsimport { RouteRecordRaw, RouterView } from 'vue-router'const routes: RouteRecordRaw = {
path: '/order',
name: 'order',
component: RouterView,
children: [
{
path: 'list',
name: 'order_list',
component: () => import('@/views/order/list/index.vue')
},
{
path: 'offline',
name: 'order-offline',
component: () => import('@/views/order/offline/index.vue')
}
]
}export default routes
十六、页面加载进度条 知识点:
● 路由拦截器
● 加载进度条
安装 nprogress
npm i nprogress# 如果是 TS 需要补充安装它的类型补充包
npm i -D @types/nprogress
配置
// src\router\index.ts
import nprogress from 'nprogress'
import 'nprogress/nprogress.css'// 进度条的配置
nprogress.configure({})// VueRouter 4 中可以不写 next 了,默认就是通过状态
router.beforeEach((to, from) => {
nprogress.start()
})router.afterEach(() => {
nprogress.done()
})
十七、页面标题处理 ● https://github.com/nuxt/vue-meta
● https://github.com/nuxt/vue-meta/tree/next
npm install vue-meta@next --save
十八、面包屑导航
{{ item.meta.title }}
十九、全屏切换
二十、侧边栏展开/收起
二十一、用户登录和身份认证 1、登录页面布局
文章图片
文章图片
登录
.login-container {
min-width: 400px;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
background-color: #2d3a4b;
}.login-form {
padding: 30px;
border-radius: 6px;
background: #fff;
min-width: 350px;
.login-form__header {
display: flex;
justify-content: center;
align-items: center;
padding-bottom: 30px;
}.el-form-item:last-child {
margin-bottom: 0;
}.login__form-title {
display: flex;
justify-content: center;
color: #fff;
}.submit-button {
width: 100%;
}.login-logo {
width: 271px;
height: 74px;
}
.imgcode-wrap {
display: flex;
align-items: center;
.imgcode {
height: 37px;
}
}
}
2、处理图片验证码
const captchaSrc = https://www.it610.com/article/ref('')onMounted(() => {
loadCaptcha()
})const loadCaptcha = async () => {
const data = https://www.it610.com/article/await getCaptcha()
captchaSrc.value = URL.createObjectURL(data)
}
export const getCaptcha = () => {
return request({
method: 'GET',
url: '/captcha_pro',
params: {
stamp: Date.now()
},
responseType: 'blob' // 请求获取图片数据
})
}
3、处理登录逻辑
const handleSubmit = async () => {
// 表单验证
const valid = await form.value?.validate()
if (!valid) {
return false
}// 验证通过,展示 loading
loading.value = https://www.it610.com/article/true// 请求登录
const data = await login(user).finally(() => {
loading.value = https://www.it610.com/article/false
})// 存储登录用户信息
store.commit('setUser', {
...data.user_info,
token: data.token
})// 跳转回原来页面
let redirect = route.query.redirect || '/'
if (typeof redirect !== 'string') {
redirect = '/'
}
router.replace(redirect)// 路由跳转不想被记录
}
4、统一处理接口请求失败
request.interceptors.response.use(
response => {
const { status } = response.data// 请求成功
if (!status || status === 200) {
return response
}// 处理 Token 过期// 其它错误给出提示即可,比如 400 参数错误之类的
ElMessage({
type: 'error',
message: response.data.msg,
duration: 5 * 1000
})
return Promise.reject(response)
},
err => {
ElMessage({
type: 'error',
message: err.message,
duration: 5 * 1000
})
return Promise.reject(err)
}
)
5、封装 element-plus 类型
// src\types\element-plus.tsimport { ElForm } from 'element-plus'
import { FormItemRule } from 'element-plus/packages/form/src/form.type'export type IElForm = InstanceTypeexport type IFormRule = Record
// src\utils\storage.ts
export const getItem = (key: string) => {
const data = https://www.it610.com/article/window.localStorage.getItem(key)
if (!data) return null
try {
return JSON.parse(data) as T
} catch (err) {
return null
}
}export const setItem = (key: string, value: object | string | null) => {
if (typeof value =https://www.it610.com/article/=='object') {
value = https://www.it610.com/article/JSON.stringify(value)
}
window.localStorage.setItem(key, value)
}export const removeItem = (key: string) => {
window.localStorage.removeItem(key)
}
6、统一设置用户 Token
request.interceptors.request.use(
config => {
// 容错:防止请求地址中有空格
config.url = config.url?.trim()// 统一设置用户 token
const { user } = store.state
if (user && user.token) {
config.headers.Authorization = `Bearer ${user.token}`
}
return config
},
error => {
return Promise.reject(error)
}
)
7、未登录不允许访问
router.beforeEach((to, from) => {
nprogress.start() // 开始加载进度条
if (to.meta.requiresAuth && !store.state.user) {
// 此路由需要授权,请检查是否已登录
// 如果没有,则重定向到登录页面
return {
path: '/login',
// 保存我们所在的位置,以便以后再来
query: { redirect: to.fullPath }
}
}
})
8、统一处理 Token 失效
// 控制登录过期的锁
let isRefreshing = false
request.interceptors.response.use(
response => {
const { status } = response.data// 请求成功
if (status === 200 || response.config.responseType === 'blob') {
return response
}// 登录过期
if (status === 410000) {
if (isRefreshing) return Promise.reject(response)
isRefreshing = true
ElMessageBox.confirm('您的登录已过期,您可以取消停留在此页面,或确认重新登录', '登录过期', {
confirmButtonText: '确认',
cancelButtonText: '取消'
}).then(
() => {
// 清除登录状态并跳转到登录页
store.commit('setUser', null)
router.push({
name: 'login',
query: {
redirect: router.currentRoute.value.fullPath
}
})
}
).finally(() => {
isRefreshing = false
})return Promise.reject(response)
}// 其它错误给出提示即可,比如 400 参数错误之类的
ElMessage({
type: 'error',
message: response.data.msg,
duration: 5 * 1000
})
return Promise.reject(response)
},
err => {
ElMessage({
type: 'error',
message: err.message,
duration: 5 * 1000
})
return Promise.reject(err)
}
)
二十二、权限管理 1、管理员
开启
关闭
element 组件库的表格树有性能问题,这里推荐另一个第三方表格组件。
● https://github.com/x-extends/vxe-table
二十三、Excel 导出
npm install xlsx
import XLSX from 'xlsx'
【typescript|Vue.js 3 + Vite + TypeScript 实战项目开发】性能优化 import异步加载,没必要一开始就加载xlsx资源
const handleExportExcel = async () => {
if (!selectionItems.value.length) {
return ElMessage.warning('请选择商品')
}
exportExcelLoading.value = https://www.it610.com/article/true
try {
const { jsonToExcel } = await import('@/utils/export-to-excel')
jsonToExcel({
data: selectionItems.value,
header: {
id: '编号',
store_name: '商品名称',
price: '价格'
},
fileName: '测试.xlsx',
bookType: 'xlsx'
})
} catch (err) {
console.error(err)
}
exportExcelLoading.value = https://www.it610.com/article/false
}
二十四、富文本编辑器
二十五、拖拽
:deep(.el-tag) {
margin-right: 5px;
}
:deep(.el-tag) {
margin-right: 5px;
}
推荐阅读
- 技术分享|使用 SpringBoot + Redis + Vue3 + ArcoPro 开发管理系统
- javascript|Vue3 + Vite2 项目实战复盘总结(干货!)
- vue.js|vue3+antd实现table中点击具体某一列展示弹窗
- vue|Vue+element-ui+ts封装table业务组件
- 前端|axios在vue中的使用
- vue单文件组件(SFC)规范
- 笔记|require.context()的用法详解
- vue|vue实时获取时间
- element|element ui日期组件设置默认时间