引言
目前,主流的前端开发框架为Vue3+TypeScript
。本文将以Vue3+TypeScript
为基础,构建一个简单的管理系统,旨在帮助了解Vue3与Vue2之间的区别,熟悉Vue3的开发方式。
项目地址:xxx
搭建
前提环境
安装了 包管理器npm
与 脚本运行环境
node.js
下面两种创建方式推荐脚手架cli
cli方式
创建项目
选择手动创建 Manually select features
,
回车---> 确认 , 空格---> 选择上

选择依赖,按下回车

选择3.x , 按下回车

按照下图,选择后,按下回车

等待项目创建成功

项目的基本结构

启动项目
项目效果

vite方式
据说 vite
没有脚手架,router、vuex、axios、element-plus、eslint等全要自己手动一个个引入,还各种报错的问题.
参考
api管理
request/api.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| import axios from 'axios'
const service = axios.create({ baseURL: "http://localhost:8724/", timeout: 5000, headers: { "Content-Type": "application/json;charset=utf-8" } })
service.interceptors.request.use((config) => { config.headers.token = config.headers.token || {} console.log('config',config) if (localStorage.getItem('token')) { config.headers.token = localStorage.getItem('token') || "" } return config; })
service.interceptors.response.use((res) => { const code: number = res.data.code console.log('code',code) if (code != 0) { return Promise.reject(res.data) } return res; }, (error) => { console.log(error); })
export default service
|
路由
/router.ts
此处为静态,后续需要根据系统当前角色配置资源菜单
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
| import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router' import HomeView from '../views/HomeView.vue'
const routes: Array<RouteRecordRaw> = [ { path: '/', name: 'home', component: HomeView, children: [ { path: "/pbill", name: 'pbill', meta: { isShow: true, title: "账务管理" }, children: [ { path: "/list", name: 'list', meta: { title: "账单列表" }, component: () => import( '../views/bill/BillListView.vue') }, { path: "/edit", name: 'edit', meta: { title: "账单编辑" }, component: () => import( '../views/bill/BillEditView.vue') }, { path: "/statistics", name: 'statistics', meta: { title: "账单统计" }, component: () => import( '../views/bill/BillStatisticsView.vue') }, ] }, { path: "/test", name: 'test', meta: { isShow: true, title: "测试" }, children: [ { path: "/goods", name: 'goods', meta: { title: "商品列表" }, component: () => import( '../views/GoodsView.vue') } ]
}
] }, { path: '/about', name: 'about', component: () => import( '../views/AboutView.vue') }, { path: '/login', name: 'login', component: () => import( '../views/LoginView.vue') },
]
const router = createRouter({ history: createWebHistory(process.env.BASE_URL), routes })
export default router
|
组件库
选择 elementui-plus
作为组件库
安装
1
| npm install element-plus --save
|
检验:package.json 中出现
1 2 3 4 5
| "dependencies": { ...... "element-plus": "^2.3.12", ..... },
|
模块
登陆模块
定义接口
/request/api.ts
1 2 3 4 5 6 7 8 9 10 11
| export function login(data:loginData){ return service({ url:"/hw/login/user/login", method:"post", data, headers: { "token": localStorage.getItem("token") } }) }
|
定义路由
/router/index.ts
1 2 3 4 5 6 7
| ... { path: '/login', name: 'login', component: () => import( '../views/LoginView.vue') }, ...
|
声明登陆数据类型
type/login.ts
1 2 3 4 5 6 7 8 9 10
| export interface LoginFormInt { username: string password: string } export class LoginData { ruleForm:LoginFormInt={ username:"", password:"" } }
|
页面组件
views/LoginView.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
| <template> <div class="login-box"> <el-form ref="ruleFormRef" :model="ruleForm" status-icon :rules="rules" label-width="80px" class="demo-ruleForm"> <h2>后台管理系统</h2> <el-form-item label="账号" prop="username"> <el-input v-model="ruleForm.username" autocomplete="off" /> </el-form-item> <el-form-item label="密码" prop="password"> <el-input v-model="ruleForm.password" type="password" autocomplete="off" /> </el-form-item> <el-form-item> <el-button class="loginBtn" type="primary" @click="submitForm(ruleFormRef)">登陆</el-button> <el-button class="loginBtn" @click="resetForm(ruleFormRef)">重置</el-button> </el-form-item> </el-form> </div> </template>
<script lang="ts"> import { defineComponent, reactive, toRefs, ref } from 'vue' import { LoginData } from '../type/login' import type { FormInstance, FormRules } from 'element-plus' import { login } from '../request/api' import {useRouter} from 'vue-router'
export default defineComponent({ setup() { const data = reactive(new LoginData()); const rules = { username: [ { required: true, message: '请输入账号', trigger: 'blur' }, { min: 3, max: 5, message: 'Length should be 3 to 5', trigger: 'blur' }, ], password: [ { required: true, message: '请输入密码', trigger: 'blur' }, { min: 3, max: 5, message: 'Length should be 3 to 5', trigger: 'blur' }, ], }
const ruleFormRef = ref<FormInstance>() const router = useRouter() const submitForm = (formEl: FormInstance | undefined) => { if (!formEl) return formEl.validate((valid) => { if (valid) { console.log('submit!') login(data.ruleForm).then((res) => { console.log("登陆成功-----------",res.data.data.token) localStorage.setItem('token',res.data.data.token) console.log('准备跳转.......') router.push('/') }).catch((e)=>{ console.log('异常',e) router.push('/login') }); router.push('/') } else { console.log('error submit!') return false } }) }
const resetForm = (formEl: FormInstance | undefined) => { if (!formEl) return formEl.resetFields() }
return { ...toRefs(data), rules, ruleFormRef, submitForm, resetForm } } }) </script>
<style lnag='scss' scoped> .login-box { width: 100%; height: 100%; Background: url("../assets/loginbj.jpg"); padding: 1px; text-align: center;
.demo-ruleForm { width: 500px; margin: 200px auto; background-color: white; padding: 30px; border-radius: 20px; }
;
.loginBtn { width: 40%; }
;
h2 { margin-bottom: 10px; } } </style>
|
商品模块
定义接口
request/api.ts
1 2 3 4 5 6 7
| export function getGoodsList(){ return service({ url: "/test/getGoodsList", method: "get" }) }
|
定义路由
router/index.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| { path: "/test", name: 'test', meta: { isShow: true, title: "测试" }, children: [ { path: "/goods", name: 'goods', meta: { title: "商品列表" }, component: () => import( '../views/GoodsView.vue') } ] }
|
声明商品数据类型
商品查询的对象
的属性类型
商品列表对象
的属性类型
good/goods.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| export interface ListInt { userId: number id: number, title: string, introduce: string }
interface selectDataInt{ title:string, introduce:string, page:number, count:number, pagesize:number, }
export class InitData { selectData:selectDataInt = { title:'', introduce:'', page:1, count:0, pagesize:5, } list:ListInt[] = [] }
|
页面组件
views/GoodsView.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
| <template> <div> <div> <el-form :inline="true" :model="selectData" class="demo-form-inline"> <el-form-item label="标题"> <el-input v-model="selectData.title" placeholder="请输入关键字" clearable /> </el-form-item> <el-form-item label="详情"> <el-input v-model="selectData.introduce" placeholder="请输入关键字" clearable/> </el-form-item> <el-form-item> <el-button type="primary" @click="onSubmit">查询</el-button> </el-form-item> </el-form> </div> <div> <el-table :data="dataList.comList" style="width: 100%"> <el-table-column prop="userId" label="用户id" width="180" /> <el-table-column prop="title" label="标题" width="180" /> <el-table-column prop="introduce" label="详情" width="180" /> </el-table> <el-pagination @size-change="sizeChange" @current-change="currentChange" layout="prev, pager, next" :total="selectData.count" /> </div> </div> </template>
<script lang="ts"> import { computed,defineComponent, reactive, toRefs, } from 'vue' import { getGoodsList } from '../request/api'; import {InitData} from '../type/good/goods'
export default defineComponent({ setup() {
getGoodsList().then(res => { console.log("res", res.data.data); data.list = res.data.data; data.selectData.count = res.data.data.length; })
const dataList = reactive({ comList:computed(()=>{ const startIndex = (data.selectData.page - 1) * data.selectData.pagesize; const endIndex = data.selectData.page * data.selectData.pagesize; return data.list.slice(startIndex, endIndex); }) })
const currentChange = (page:number)=>{ console.log("触发了currentChange") data.selectData.page = page } const sizeChange = (size:number)=>{ console.log("触发了sizeChange") data.selectData.pagesize = size }
const data = reactive(new InitData()); return { ...toRefs(data), currentChange, sizeChange, dataList } } }) </script>
<style scoped></style>
|