使用到的资源
vue官网: https://cn.vuejs.org/
element-ui : https://element.eleme.cn/#/zh-CN/component/icon
ESLint
TS
SCSS
源码地址: https://github.com/qifutian/learngit/tree/main/TSvue-DEMO/edu-boss-fed
一 、 搭建项目架构
使用vue-cli创建项目
- vue create edu-boss-fed
- 使用自定义选项,选择babel,ts,route,vuex,css预处理,linter
- 使用class风格描述组件
- 选择ts和babel编译
- 不选择history路由模式
- 选择预处理器Sass,建议选择 dart-sass
- 选择ESlint
- 单独存放个自的配置文件
- 进入项目中,npm run serve 启动,默认打开8080地址
加入git 版本管理
- 创建远程仓库
- 本地代码提交远程仓库
- 生成git本地仓库 git init
- git status 查看当前状态
- git add . 将所有文件放到暂存区
- git status 查看暂存区内容,是否选中
- git commit -m "init " 记录提交日志
- git log 查看历史记录
- git remote add origin 远程地址 关联远程地址
- git remote -v 查看远程地址
- git push -u origin master 第一次记录当前origin中的master分支
目录结构
main.ts — 整个系统的入口
public — 纯静态资源文件,放index.html,不被webpack打包的
App.vue — 项目中的根组件,router-view是根路由出口
shims.tsx.d.ts 和 shims-vue.d.ts — 都是整个项目中ts的配置
Home.vue — home组件,组件中是ts规范
store — vuex目录,存放vuex的配置
route — 放置路由表,存放对应的ts路由,严格描述
components — 放置公共的组件文件夹
assets — 放置静态资源
node_modules — 第三方包
.gitignore — 放置不会被git 提交的文件
babel.config.js — babel 的配置
tsconfig.json – ts的配置
调整初始目录结构
App.vue 删除样式,删除多余模板
route/index.ts 清空路由表
删除Home.vue 和 about.vue
删除Hello.vue,删除log.png
创建utils目录,存放一些功能模块,请求等
创建styles目录,全局样式
创建services,放接口部分
环境说明
在Vue中如何启动 typescript 支持
两种方式: 1. 在使用vue-cli创建项目中选择typescript
2. 已有项目,添加vue官方配置的ts适配插件
vue add @vue/typescript
使用TS开发,编辑器推荐VS Code
如果是vue项目,推荐安装 vetur插件
相关配置说明
ts相关的配置
使用Options APIs
定义组件的方式
要让 TypeScript 正确推断 Vue 组件选项中的类型,您需要使用 Vue.component 或 Vue.extend 定义组件:
import Vue from 'vue'
const Component = Vue.extend({
// 类型推断已启用
})
const Component = {
// 这里不会有类型推断,
// 因为 TypeScript 不能确认这是 Vue 组件的选项
}
基于类的 Vue 组件
import Vue from 'vue'
import Component from 'vue-class-component'
// @Component 修饰符注明了此类为一个 Vue 组件
@Component({
// 所有的组件选项都可以放在这里
template: '<button @click="onClick">Click!</button>'
})
export default class MyComponent extends Vue {
// 初始数据可以直接声明为实例的 property
message: string = 'Hello!'
// 组件方法也可以直接声明为实例的方法
onClick (): void {
window.alert(this.message)
}
}
装饰器语法
代码格式规范
良好的代码格式有利于维护,多人协作,阅读
约束代码规范
默认阅读不可靠,需要工具强制执行
- JSLint
- JSHint
- ESLint 等等
项目中的代码规范
module.exports = {
root: true,
env: {
node: true
},
// 使用插件的编码校验规则
extends: [
'plugin:vue/essential',
'@vue/standard',
'@vue/typescript/recommended'
],
parserOptions: {
ecmaVersion: 2020
},
// 自定义编码校验规则
rules: {
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
// 'semi': ['error', 'always']
'@typescript-eslint/member-delimiter-style': ['error', {
"multiline": {
"delimiter": "none",
"requireLast": true
}
}]
}
}
自定义的校验规则
// 自定义编码校验规则
rules: {
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
// 'semi': ['error', 'always']
'@typescript-eslint/member-delimiter-style': ['error', {
"multiline": {
"delimiter": "none",
"requireLast": true
}
}]
}
导入element
安装: npm i element-ui -S
引入element-ui
两种方式: 1.全部引入 2. 按需引入
在 main.js 中写入以下内容:
import Vue from 'vue';
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import App from './App.vue';
Vue.use(ElementUI);
new Vue({
el: '#app',
render: h => h(App)
});
样式处理
在使用element,进行组件定制
在src/styles中新增
index.scss 全局样式,在入口模块被加载
mixin.scss 公共的mixin混入
reset.scss 重置默认样式
variables.scss 公共样式变量
index.scss
@import './variables.scss';
// globals
html {
font-family: $font-family;
-webkit-text-size-adjust: 100%;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
// better Font Rendering
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
body {
margin: 0;
background-color: $body-bg;
}
// custom element theme
$--color-primary: $primary-color;
$--color-success: $success-color;
$--color-warning: $warning-color;
$--color-danger: $danger-color;
$--color-info: $info-color;
// change font path, required
$--font-path: '~element-ui/lib/theme-chalk/fonts';
// import element default theme
@import '~element-ui/packages/theme-chalk/src/index';
// node_modules/element-ui/packages/theme-chalk/src/common/var.scss
// overrides
// .el-menu-item, .el-submenu__title {
// height: 50px;
// line-height: 50px;
// }
.el-pagination {
color: #868e96;
}
// components
.status {
display: inline-block;
cursor: pointer;
width: .875rem;
height: .875rem;
vertical-align: middle;
border-radius: 50%;
&-primary {
background: $--color-primary;
}
&-success {
background: $--color-success;
}
&-warning {
background: $--color-warning;
}
&-danger {
background: $--color-danger;
}
&-info {
background: $--color-info;
}
}
variables.scss
$primary-color: #40586F;
$success-color: #51cf66;
$warning-color: #fcc419;
$danger-color: #ff6b6b;
$info-color: #868e96; // #22b8cf;
$body-bg: #E9EEF3; // #f5f5f9;
$sidebar-bg: #F8F9FB;
$navbar-bg: #F8F9FB;
$font-family: system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
样式处理 – 共享全局样式变量
在全局配置,节省组件加载,注入到全局
增加vue.config.js
// vue.config.js
module.exports = {
css: {
loaderOptions: {
// 默认情况下 `sass` 选项会同时对 `sass` 和 `scss` 语法同时生效
// 因为 `scss` 语法在内部也是由 sass-loader 处理的
// 但是在配置 `prependData` 选项的时候
// `scss` 语法会要求语句结尾必须有分号,`sass` 则要求必须没有分号
// 在这种情况下,我们可以使用 `scss` 选项,对 `scss` 语法进行单独配置
scss: {
prependData: `@import "~@/styles/variables.scss";`
}
}
},
}
接口处理
使用proxy设置代理接口地址
在vue.config.js 在添加
devServer: {
proxy: {
'/boss': {
target: 'http://eduboss.lagou.com',
changeOrigin: true // 把请求头中的 host 配置为 target
},
'/front': {
target: 'http://edufront.lagou.com',
changeOrigin: true
}
}
}
封装请求模块
安装 axios
npm i axios
创建请求模块,src/utils/request.js
import axios from 'axios'
const request = axios.create({
// 配置选项
// baseURL,
// timeout
})
// 请求拦截器
// 响应拦截器
export default request
初始化路由组件
view下新创建对应的文件
在route/index.ts 下配置 路由懒加载,进行lauout布局,路由嵌套
layout 布局,src下新建layout文件夹,将除了登录页和404页面外全部是layout的子路由
import Vue from 'vue'
import VueRouter, {
RouteConfig } from 'vue-router'
import Layout from '@/layout/index.vue'
Vue.use(VueRouter)
// 路由配置规则
const routes: Array<RouteConfig> = [
{
path: '/login',
name: 'login',
component: () => import(/* webpackChunkName: 'login' */ '@/views/login/index.vue')
},
{
path: '/',
component: Layout,
children: [
{
path: '', // 默认子路由
name: 'home',
component: () => import(/* webpackChunkName: 'home' */ '@/views/home/index.vue')
},
{
path: '/role',
name: 'role',
component: () => import(/* webpackChunkName: 'role' */ '@/views/role/index.vue')
},
{
path: '/menu',
name: 'menu',
component: () => import(/* webpackChunkName: 'menu' */ '@/views/menu/index.vue')
},
{
path: '/resource',
name: 'resource',
component: () => import(/* webpackChunkName: 'resource' */ '@/views/resource/index.vue')
},
{
path: '/course',
name: 'course',
component: () => import(/* webpackChunkName: 'course' */ '@/views/course/index.vue')
},
{
path: '/user',
name: 'user',
component: () => import(/* webpackChunkName: 'user' */ '@/views/user/index.vue')
},
{
path: '/advert',
name: 'advert',
component: () => import(/* webpackChunkName: 'advert' */ '@/views/advert/index.vue')
},
{
path: '/advert-space',
name: 'advert-space',
component: () => import(/* webpackChunkName: 'advert-space' */ '@/views/advert-space/index.vue')
}
]
},
{
path: '*',
name: '404',
component: () => import(/* webpackChunkName: '404' */ '@/views/error-page/404.vue')
}
]
const router = new VueRouter({
routes
})
export default router
侧边栏组件
使用container布局容器
在app-aside.vue中使用el-menu标签,加入route进入导航资源
<template>
<div class="aside">
<el-menu
default-active="4"
@open="handleOpen"
@close="handleClose"
background-color="#545c64"
text-color="#fff"
active-text-color="#ffd04b"
router
>
<el-submenu index="1">
<template slot="title">
<i class="el-icon-location"></i>
<span>权限管理</span>
</template>
<el-menu-item index="/role">
<i class="el-icon-setting"></i>
<span slot="title">角色管理</span>
</el-menu-item>
<el-menu-item index="/menu">
<i class="el-icon-setting"></i>
<span slot="title">菜单管理</span>
</el-menu-item>
<el-menu-item index="/resource">
<i class="el-icon-setting"></i>
<span slot="title">资源管理</span>
</el-menu-item>
</el-submenu>
<el-menu-item index="/course">
<i class="el-icon-menu"></i>
<span slot="title">课程管理</span>
</el-menu-item>
<el-menu-item index="/user">
<i class="el-icon-document"></i>
<span slot="title">用户管理</span>
</el-menu-item>
<el-submenu index="4">
<template slot="title">
<i class="el-icon-location"></i>
<span>广告管理</span>
</template>
<el-menu-item index="/advert">
<i class="el-icon-setting"></i>
<span slot="title">广告列表</span>
</el-menu-item>
<el-menu-item index="/advert-space">
<i class="el-icon-setting"></i>
<span slot="title">广告位列表</span>
</el-menu-item>
</el-submenu>
</el-menu>
</div>
</template>
<script lang="ts">
import Vue from 'vue'
export default Vue.extend({
name: 'AppAside',
methods: {
handleOpen (key: string, keyPath: string): void {
console.log(key, keyPath)
},
handleClose (key: string, keyPath: string): void {
console.log(key, keyPath)
}
}
})
</script>
<style lang="scss" scoped>
.aside {
.el-menu {
min-height: 100vh;
}
}
</style>
头部导航
采用面包屑导航,新建app-header.vue组件
采用下拉菜单作为用户信息
<template>
<div class="header">
<el-breadcrumb separator-class="el-icon-arrow-right">
<el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
<el-breadcrumb-item>活动管理</el-breadcrumb-item>
<el-breadcrumb-item>活动列表</el-breadcrumb-item>
<el-breadcrumb-item>活动详情</el-breadcrumb-item>
</el-breadcrumb>
<el-dropdown>
<span class="el-dropdown-link">
<el-avatar
shape="square"
:size="40"
src="https://cube.elemecdn.com/9/c2/f0ee8a3c7c9638a54940382568c9dpng.png"
></el-avatar>
<i class="el-icon-arrow-down el-icon--right"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item>用户ID</el-dropdown-item>
<el-dropdown-item divided>退出</el-dropdown-item>
</el