vue3脚手架项目基础准备
vue create vue3_cli_project
cd vue3_cli_project
npm install
npm run serve
关闭eslint代码检查
vue.config.js(vue-cli创建的项目)
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
transpileDependencies: true,
lintOnSave: false, //关闭代码格式化校验工具
devServer: {
port: 8080, //修改启动,
proxy: {
'/api': {
target: 'http://localhost:10029',//后端接口地址
changeOrigin: true,//是否允许跨越
pathRewrite: {
'^/api': '/api'//重写,
}
}
}
},
configureWebpack: {
//关闭 webpack 的性能提示
performance: {
hints: false
}
},
})
常用依赖安装
element-plus包括其图标库
npm install element-plus @element-plus/icons-vue
css底板 该css文件引入到main.js中
* {
box-sizing: border-box;
font-family: PingFang SC, tahoma, arial, 'Microsoft Yahei', 'Hiragino Sans GB', '\5b8b\4f53', sans-serif;
}
body,
h1,
h2,
h3,
h4,
h5,
h6,
hr,
p,
blockquote,
dl,
dt,
dd,
ul,
ol,
li,
pre,
form,
fieldset,
legend,
button,
input,
textarea,
th,
td {
margin: 0;
padding: 0;
}
body,
button,
input,
select,
textarea {
font: 12px/1.5tahoma, arial, \5b8b\4f53;
}
h1,
h2,
h3,
h4,
h5,
h6 {
font-size: 100%;
}
address,
cite,
dfn,
em,
var {
font-style: normal;
}
code,
kbd,
pre,
samp {
font-family: couriernew, courier, monospace;
}
small {
font-size: 12px;
}
ul,
ol {
list-style: none;
}
a {
text-decoration: none;
outline: none;
}
sup {
vertical-align: text-top;
}
sub {
vertical-align: text-bottom;
}
legend {
color: #000;
}
fieldset,
img {
border: 0;
}
button,
input,
select,
textarea {
font-size: 100%;
outline: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
body {
color: #000;
background-color: #fff;
}
body, html {
width: 100%;
height: 100%;
}
#app {
height: 100%;
}
/* a,a:hover,a:active,a:visited,a:link,a:focus{
color: #000;
} */
全量导入element-plus模块和icon图标组件
//main.js
import './assets/main.css'
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import * as ElementPlusIconsVue from '@element-plus/icons-vue' //element-icons
import zhCn from 'element-plus/dist/locale/zh-cn.mjs' //国际化
const app = createApp(App)
app.use(router)
app.use(ElementPlus, { locale: zhCn })
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component)
}
app..mount('#app')
scss
scss看个人使用习惯,酌情添加
npm install sass sass-loader node-sass --save-dev vue-style-loader --sava-dev
echarts
npm install echarts --save
axios
npm install axios --save
App.vue
<template>
<router-view />
</template>
<style lang="scss">
* {
margin: 0px;
padding: 0px;
box-sizing: border-box;
list-style: none;
text-decoration: none;
outline: none;
min-height: 100vh;
}
</style>
router
router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import { ElMessage } from 'element-plus'
import { h } from 'vue';
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: '/',
name: 'home',
component: () => import('../views/LayoutView.vue')
},
{
path: '/login',
name: '登录页',
component: () => import('../views/LoginView.vue')
},
{
path: '/',
name: "框架页",
component: () => import('../views/LayoutView.vue'),
redirect: '/home',
children: [
{
path: 'home',
name: "定位",
component: () => import('../components/Map.vue')
},
{
path: "ais",
name: "AIS船只信息",
component: () => import('../components/Ais.vue')
},
{
path: "echarts",
name: "Echarts图形绘制",
component: () => import('../components/Chart.vue')
},
]
}
]
})
//登录状态时间的检查
// 设置登录过期时间(一天)86400000
let expire = 43200000;
//路由守卫
//全局守卫,登录拦截
//进行路由拦截:当没有登陆标识,直接打回登陆页面,如何避免退回到 登陆页呢?
router.beforeEach((to, from, next) => {
// 从本地缓存中获取保存的 token 信息
const tokenObj = JSON.parse(window.localStorage.getItem('isLogin'))
if (to.path === "/login") {
next()
} else {
// 如果没有token,强制跳转到登录页面;如果有,则判断token时间是否过期
if (!tokenObj || !tokenObj.token) {
next('/login')
} else {
let date = new Date().getTime();
// 当前时间 - token中的登录时间 > 设置的过期时间,为过期;则清除token,并强制跳转至登录页
// 反之为有效期,则放行
if (date - tokenObj.startTime > expire) {
window.localStorage.removeItem('isLogin');
next('/login')
ElMessage({
message: h('p', null, [
h('span', null, '登录状态过期'),
h('i', { style: 'color: teal' }, '请重新登录!'),
]),
})
} else {
next();
}
}
}
});
export default router
封装axios api层
该部分转载于程序员青戈
基础登录页面
<template>
<div class="login-body">
<div class="login-panel">
<div class="login-title">用户登录</div>
<el-form :model="formData" :rules="rules" ref="formDataRef">
<el-form-item prop="username">
<el-input placeholder="请输入账号" v-model="formData.username" size="large" type="text">
<template #prefix>
<el-icon>
<User />
</el-icon>
</template>
</el-input>
</el-form-item>
<el-form-item prop="password">
<el-input placeholder="请输入密码" v-model="formData.password" size="large" type="password"
@keyup.enter.native="login()">
<template #prefix>
<el-icon>
<Lock />
</el-icon>
</template>
</el-input>
</el-form-item>
<el-form-item label="">
<el-button type="primary" style="width: 100%;" @click="login()" size="large">登录</el-button>
</el-form-item>
</el-form>
</div>
</div>
</template>
<script setup>
import { ref, reactive, } from 'vue'
import { ElMessage } from 'element-plus';
import request from '@/utils/request';
import { useRouter } from 'vue-router';
// const checkCodeUrl = "api/checkCode?" + new Date().getTime();
//表单
const formDataRef = ref();
let formData = reactive({
username: "",
password: ""
});
const rules = {
username: [{
required: true,
message: "请输入用户名"
}],
password: [{
required: true,
message: "请输入密码"
}],
}
const router = useRouter();
const login = () => {
var form_obj = JSON.parse(JSON.stringify(formData));
// console.log(form_obj);
request.get("/login/"+form_obj.username + "/" + form_obj.password).then(res => {
if (res) {
ElMessage({
message: '登录成功',
type: 'success',
})
let tokenObj = { token: " isLogin", startTime: new Date().getTime() };
window.localStorage.setItem("isLogin", JSON.stringify(tokenObj));
localStorage.setItem("username", JSON.parse(JSON.stringify(formData.username)));
router.push("/home");
} else {
ElMessage.error('账号或密码错误!!!登录失败!!!')
}
});
};
</script>
<style lang="scss" scoped >
.login-body {
background: url("../assets/images/三门峡.png") no-repeat center center;
// background: url("../assets/zqn01.jpg") no-repeat center center;
height: 100%;
width: 100%;
background-size: cover;
position: absolute;
left: 0;
top: 0;
.login-panel {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: auto;
padding: 25px;
width: 26%;
min-width: 460px;
height: 30%;
min-height: 300px;
background: rgba(255, 255, 255, 0.6);
border-radius: 5%;
box-shadow: 2px 2px 10px #ddd;
.login-title {
font-size: 22px;
text-align: center;
margin-bottom: 22px;
}
}
}
</style>
框架页模板
<script setup >
import { h, ref } from 'vue';
import { useRouter, RouterView, useRoute } from 'vue-router';
import { ArrowRight } from '@element-plus/icons-vue';
import { ElNotification, ElMessageBox, ElMessage } from 'element-plus';
import screenfull from 'screenfull'
const SetFullScreen = () => {
if (!screenfull.isEnabled) {
return false;
}
screenfull.toggle()
}
const router = useRouter();
const route = useRoute();
let local_username = localStorage.getItem("username");
const outLogin = () => {
ElMessageBox.confirm(
'确定退出当前登录状态吗?', 'Warning',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}
).then(() => {
router.push("/login");
ElNotification.success({
title: 'Success',
message: h('i', { style: 'color: teal' }, '成功退出当前用户登录状态'),
offset: 100,
})
window.localStorage.removeItem('isLogin');
window.localStorage.removeItem('username');
}).catch(() => {
ElMessage({
type: 'info',
message: '已取消退出操作',
})
})
}
</script>
<template>
<el-container class="layout-container-demo" style="min-height: 100vh;">
<el-aside style="width: 14%;">
<el-menu :default-openeds="['1', '2']"
style="min-height: 100%; overflow: hidden; box-shadow:2px 0 6px rgb(0 21 41 / 35%);"
background-color="rgb(48,65,86)" :collapse-transition="false" text-color="white" active-text-color="#ffd04b"
:router="true" :default-active="route.path">
<el-menu-item index="/home">
<el-icon>
<Location />
</el-icon>
<span>首页</span>
</el-menu-item>
<el-menu-item index="/ais">
<el-icon>
<Ship />
</el-icon>
<span>Ais</span>
</el-menu-item>
<el-menu-item index="/echarts">
<el-icon>
<Histogram />
</el-icon>
<span>Echarts</span>
</el-menu-item>
</el-menu>
</el-aside>
<el-container>
<el-header style="font-size: 12px;border-bottom: 1px solid #ccc; text-align: right;display: flex;">
<el-breadcrumb :separator-icon="ArrowRight" style="flex: 1; padding-top:25px;">
<el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
<el-breadcrumb-item :to="{ path: '/ais' }">AIS船舶信息</el-breadcrumb-item>
<el-breadcrumb-item :to="{ path: '/echarts' }">Echarts</el-breadcrumb-item>
</el-breadcrumb>
<el-icon @click="SetFullScreen()" size="25" style="margin-top: 18px;cursor: pointer;">
<FullScreen />
</el-icon>
<div class="toolbar" style="width: 80px;">
<el-dropdown>
<el-avatar src="https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png" :size="40"
style="margin-top: 5px; "></el-avatar>
<template #dropdown>
<el-dropdown-menu>
<div style="text-align: center; padding: 10px 10px;"><b><i>{{ local_username }}</i></b>
</div>
<el-dropdown-item @click="outLogin()">退出登录</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</el-header>
<el-main>
<RouterView></RouterView>
</el-main>
</el-container>
</el-container>
</template>
<style lang='scss' scoped>
.layout-container-demo .el-header {
position: relative;
color: var(--el-text-color-primary);
}
.layout-container-demo .el-aside {
color: var(--el-text-color-primary);
background: var(--el-color-primary-light-8);
}
.layout-container-demo .el-menu {
border-right: none;
}
.layout-container-demo .el-main {
padding: 0;
}
.layout-container-demo .toolbar {
display: inline-flex;
align-items: center;
justify-content: center;
height: 100%;
right: 20px;
}
</style>