安装
1 | npm install pinia |
相关推荐

2024-02-26
Vue后台系统
项目由 Vue3 + Pina + JavaScript + Ant Design Vue 创建项目12345# 直接创建npm create vite@latest my-vue-app -- --template vue# 如果要创建ts或者自定义配置的项目就用这个npm create vite 引入Ant Design Vue1npm i --save [email protected] mian.js 删掉自带的style.css文件 12345678import {createApp} from 'vue'import Antd from 'ant-design-vue';import 'ant-design-vue/dist/reset.css';import App from './App.vue'const app = createApp(App);app.use(Antd)app.mount('#app'); 安装router1npm install vue-router@4 创建src/router/index.js文件 12345678910111213141516171819202122232425262728293031323334353637383940414243444546import { createWebHistory, createRouter } from 'vue-router'/** * Note: 路由配置项 * * hidden: true // 当设置 true 的时候该路由不会再侧边栏出现 如401,login等页面,或者如一些编辑页面/edit/1 * alwaysShow: true // 当你一个路由下面的 children 声明的路由大于1个时,自动会变成嵌套的模式--如组件页面 * // 只有一个时,会将那个子路由当做根路由显示在侧边栏--如引导页面 * // 若你想不管路由下面的 children 声明的个数都显示你的根路由 * // 你可以设置 alwaysShow: true,这样它就会忽略之前定义的规则,一直显示根路由 * redirect: noRedirect // 当设置 noRedirect 的时候该路由在面包屑导航中不可被点击 * name:'router-name' // 设定路由的名字,一定要填写不然使用<keep-alive>时会出现各种问题 * query: '{"id": 1, "name": "ry"}' // 访问路由的默认传递参数 * roles: ['admin', 'common'] // 访问路由的角色权限 * permissions: ['a:a:a', 'b:b:b'] // 访问路由的菜单权限 * meta : { noCache: true // 如果设置为true,则不会被 <keep-alive> 缓存(默认 false) title: 'title' // 设置该路由在侧边栏和面包屑中展示的名字 icon: 'svg-name' // 设置该路由的图标,对应路径src/assets/icons/svg breadcrumb: false // 如果设置为false,则不会在breadcrumb面包屑中显示 activeMenu: '/system/user' // 当路由设置了该属性,则会高亮相对应的侧边栏。 } */// 公共路由export const constantRoutes = [ { path: '/login', component: () => import('@/views/login'), hidden: true },]const router = createRouter({ history: createWebHistory(), routes: constantRoutes, scrollBehavior(to, from, savedPosition) { if (savedPosition) { return savedPosition } else { return { top: 0 } } },});export default router; 配置开发环境编辑vite.config.js 123456789101112131415161718192021import { defineConfig, loadEnv } from 'vite'import path from 'path'import vue from '@vitejs/plugin-vue'// https://vitejs.dev/config/export default defineConfig(({ mode, command }) => { return { plugins: [vue()], resolve: { // https://cn.vitejs.dev/config/#resolve-alias alias: { // 设置路径 '~': path.resolve(__dirname, './'), // 设置别名 '@': path.resolve(__dirname, './src') }, // https://cn.vitejs.dev/config/#resolve-extensions extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue'] } }}) 项目根目录创建环境配置文件 .env.development .env.prod 12345678# 页面标题VITE_APP_TITLE = 若依管理系统# 开发环境配置VITE_APP_ENV = 'development'# 若依管理系统/开发环境VITE_APP_BASE_API = '/dev-api' 安装sass12345npm install -D sass# 直接使用就可以<style lang="scss" scoped></style> 全局样式创建src/style/index.scss文件 123456789101112131415161718@import './demo.scss';html { height: 100vh; box-sizing: border-box;}body { height: 100vh; margin: 0; //-moz-osx-font-smoothing: grayscale; //-webkit-font-smoothing: antialiased; //text-rendering: optimizeLegibility; //font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif;}#app { height: 100vh;} 创建src/style/demo.scss文件 1// 这个文件是用来写css的,前面导入的这个文件,所以后面只需要写就行了 导入scss,编辑main.js 1import './style/index.scss' Ant Design Vue Icon的使用123456789101112131415<template> <a-form class="login-from"> <a-form-item size> <a-input placeholder="请输入用户名" size="large"> <template #prefix> <UserOutlined/> </template> </a-input> </a-form-item> </a-form></template><script setup> import {UserOutlined, LockOutlined} from '@ant-design/icons-vue'</script> 安装Axios1npm install axios 创建 src/utils/request.js 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556import axios from 'axios'axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8'// 创建axios实例const service = axios.create({ // axios中请求配置有baseURL选项,表示请求URL公共部分 baseURL: import.meta.env.VITE_APP_BASE_API, // 超时 timeout: 10000})// request拦截器service.interceptors.request.use(config => { return config}, error => { console.log(error) Promise.reject(error)})// 响应拦截器service.interceptors.response.use(res => { // 未设置状态码则默认成功状态 const code = res.data.code || 200; if (code === 401) { return Promise.reject('无效的会话,或者会话已过期,请重新登录。') } else if (code === 500) { ElMessage({ message: msg, type: 'error' }) return Promise.reject(new Error(msg)) } else if (code === 601) { ElMessage({ message: msg, type: 'warning' }) return Promise.reject(new Error(msg)) } else if (code !== 200) { ElNotification.error({ title: msg }) return Promise.reject('error') } else { return Promise.resolve(res.data) } }, error => { console.log('err' + error) let { message } = error; if (message == "Network Error") { message = "后端接口连接异常"; } else if (message.includes("timeout")) { message = "系统接口请求超时"; } else if (message.includes("Request failed with status code")) { message = "系统接口" + message.substr(message.length - 3) + "异常"; } ElMessage({ message: message, type: 'error', duration: 5 * 1000 }) return Promise.reject(error) })export default service main.js 1import router from './router'

2022-11-19
Element-UI
表单验证简单版123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899<template> <div class="login"> <el-card class="box-card"> <div slot="header" class="clearfix"> <span>通用后台管理系统</span> </div> <el-form label-width="80px" :model="form" ref="form"> <el-form-item label="用户名" prop="username" :rules="[ {required:true,message:'请输入用户名',trigger:'blur'}, {min:6,max:12,message:'长度在6-12位字符',trigger:'blur'}, ]"> <el-input v-model="form.username"></el-input> </el-form-item> <el-form-item label="密码" prop="password" :rules="[ {required:true,message:'请输入密码',trigger:'blur'}, {min:6,max:12,message:'长度在6-12位字符',trigger:'blur'}, ]"> <el-input type="password" v-model="form.password"></el-input> </el-form-item> <el-form-item> <el-button type="primary" @click="login('form')">登录</el-button> </el-form-item> </el-form> </el-card> </div></template><script>export default { data() { return { form: { username: "", password: "" } } }, methods:{ login(form){ this.$refs[form].validate((valid)=>{ if(valid){ console.log("验证通过") this.axios.post("http://localhost",this.form).then( res=>{ console.log(res) if(res.data.status === 200){ localStorage.setItem("username",res.data.username) this.$message({ message:res.data.message, type:'success' }) this.$router.push('/home') } } ) .catch(err=>{ console.error(err) }) }else { console.error("验证不通过") } }) } }}</script><style scoped lang="scss">.login { width: 100%; height: 100%; position: absolute; background: cadetblue; .box-card { width: 450px; margin: 200px auto; .el-button { width: 100%; } }}</style> 自定义校验123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119<template> <div class="login"> <el-card class="box-card"> <div slot="header" class="clearfix"> <span>通用后台管理系统</span> </div> <el-form label-width="80px" :model="form" :rules="rules" ref="form"> <el-form-item label="用户名" prop="username"> <el-input v-model="form.username"></el-input> </el-form-item> <el-form-item label="密码" prop="password"> <el-input type="password" v-model="form.password"></el-input> </el-form-item> <el-form-item> <el-button type="primary" @click="login('form')">登录</el-button> </el-form-item> </el-form> </el-card> </div></template><script>export default { data() { // 验证用户名 const validateName = (rule, value, callback) => { if (value === '') { callback(new Error("请输入用户名")) } else if (value === '123456') { console.log("11111111111") callback(new Error('换个用户名,猜到啦')) } else { callback() } } const validatePassword = (rule, value, callback) => { if (value === '') { callback(new Error("请输入密码")) } else if (!value === '123456') { callback(new Error('换个密码,猜到啦')) } else { callback() } } return { form: { username: "", password: "" }, rules: { username: [{validator: validateName, trigger: 'blur'}], password: [{validator: validatePassword, trigger: 'blur'}], } } }, methods: { login(form) { this.$refs[form].validate((valid) => { if (valid) { console.log("验证通过") // this.axios.post("http://localhost", this.form).then( // res => { // console.log(res) // if (res.data.status === 200) { // localStorage.setItem("username", res.data.username) // this.$message({ // message: res.data.message, // type: 'success' // }) // this.$router.push('/home') // } // } // ) // .catch(err => { // console.error(err) // }) } else { console.error("验证不通过") } }) } }}</script><style scoped lang="scss">.login { width: 100%; height: 100%; position: absolute; background: cadetblue; .box-card { width: 450px; margin: 200px auto; .el-button { width: 100%; } }}</style> 自定义校验封装新建一个vaildate.js 123456789101112131415161718192021// 用户名匹配export function nameRule(rule,value,callback){ if (value === '') { callback(new Error("请输入用户名")) } else if (value === '123456') { console.log("11111111111") callback(new Error('换个用户名,猜到啦')) } else { callback() }}export function passwordRule(rule,value,callback){ if (value === '') { callback(new Error("请输入密码")) } else if (value === '123456') { callback(new Error('换个密码,猜到啦')) } else { callback() }} 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697<template> <div class="login"> <el-card class="box-card"> <div slot="header" class="clearfix"> <span>通用后台管理系统</span> </div> <el-form label-width="80px" :model="form" :rules="rules" ref="form"> <el-form-item label="用户名" prop="username"> <el-input v-model="form.username"></el-input> </el-form-item> <el-form-item label="密码" prop="password"> <el-input type="password" v-model="form.password"></el-input> </el-form-item> <el-form-item> <el-button type="primary" @click="login('form')">登录</el-button> </el-form-item> </el-form> </el-card> </div></template><script>import {nameRule,passwordRule} from "@/utils/vaildate";export default { data() { return { form: { username: "", password: "" }, rules: { username: [{validator: nameRule, trigger: 'blur'}], password: [{validator: passwordRule, trigger: 'blur'}], } } }, methods: { login(form) { this.$refs[form].validate((valid) => { if (valid) { console.log("验证通过") // this.axios.post("http://localhost", this.form).then( // res => { // console.log(res) // if (res.data.status === 200) { // localStorage.setItem("username", res.data.username) // this.$message({ // message: res.data.message, // type: 'success' // }) // this.$router.push('/home') // } // } // ) // .catch(err => { // console.error(err) // }) } else { console.error("验证不通过") } }) } }}</script><style scoped lang="scss">.login { width: 100%; height: 100%; position: absolute; background: cadetblue; .box-card { width: 450px; margin: 200px auto; .el-button { width: 100%; } }}</style> 遍历路由渲染菜单栏src>common>menu.vue 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869<template> <div class="menu"> <el-aside width="200px"> <el-menu router default-active="2" class="el-menu-vertical-demo" background-color="cornflowerblue" text-color="#fff" active-text-color="#ffd04b"> <template v-for="(item,index) in menus"> <div> <el-submenu :index="index + '' " :key="index" v-if="!item.hidden"> <template slot="title"> <i :class="item.iconClass"></i> <span>{{ item.name }}</span> </template> <el-menu-item-group v-for="(child,index) in item.children" :key="index"> <el-menu-item :index="child.path"> <i :class="child.iconClass"></i> {{ child.name }} </el-menu-item> </el-menu-item-group> </el-submenu> </div> </template> </el-menu> </el-aside> </div></template><script>export default { data(){ return{ menus:[] } }, created() { console.log(this.$router.options.routes) this.menus=this.$router.options.routes }, methods: { }}</script><style scoped lang="scss">.menu{ .el-aside{ height: 100%; .el-menu{ height: 100%; .fa{ margin-right: 10px; } } .el-submenu .el-menu-item{ min-width: 0; } }}</style> route.js 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136import Vue from 'vue'import Router from 'vue-router'// import Home from '../components/Home.vue'Vue.use(Router)export default new Router({ routes:[ { path:'/', // 重定向 redirect:'/login', // 路由懒加载 hidden:true, component:()=>import('@/components/Login.vue') // 异步组件 }, { path:'/login', name:'Login', hidden:true, // 路由懒加载 component:()=>import('@/components/Login.vue') // 异步组件 }, { path:'/home', // component:Home hidden:true, // 路由懒加载 component:()=>import('@/components/Home.vue') // 异步组件 }, { path:'/home2', // component:Home hidden:true, // 路由懒加载 component:resolve =>require(['@/components/Home.vue'],resolve) // 异步组件 }, { path:'*', hidden:true, // 路由懒加载 component:()=>import('@/components/404.vue') }, { path:"/home", name:"学生管理", iconClass:'fa fa-user', // 默认重定向 redirect:'/home/student', component:()=>import('@/components/Home.vue'), children:[ { path:'/home/student', name:'学生列表', iconClass:"fa fa-list", component:()=>import("@/components/students/StudentList.vue") }, { path:'/home/info', name:'信息列表', iconClass:"fa fa-list", component:()=>import("@/components/students/InfoList.vue") }, { path:'/home/infos', name:'信息管理', iconClass:"fa fa-list-alt", component:()=>import("@/components/students/InfoLists.vue") }, { path:'/home/work', name:'作业列表', iconClass:"fa fa-list-ul", component:()=>import("@/components/students/WorkList.vue") }, { path:'/home/workd', name:'作业管理', iconClass:"fa fa-th-list", component:()=>import("@/components/students/WorkMent.vue") } ] }, { path:"/home", name:"数据分析", iconClass:'fa fa-bar-chart', component:()=>import('@/components/Home.vue'), children:[ { path:'/home/dataview', name:'数据概览', iconClass:"fa fa-list-alt", component:()=>import("@/components/dataAnalysis/DataView.vue") }, { path:'/home/mapview', name:'地图概览', iconClass:"fa fa-list-alt", component:()=>import("@/components/dataAnalysis/MapView.vue") }, { path:'/home/travel', name:'信息管理', iconClass:"fa fa-list-alt", component:()=>import("@/components/dataAnalysis/TraveMap.vue") }, { path:'/home/score', name:'分数地图', iconClass:"fa fa-list-alt", component:()=>import("@/components/dataAnalysis/ScoreMap.vue") } ] }, { path:"/users", name:"用户中心", iconClass:'fa fa-user', component:()=>import('@/components/Home.vue'), children:[ { path:'/users/user', name:'权限管理', iconClass:"fa fa-user", component:()=>import("@/components/users/User.vue") } ] }, ], mode:'history'}) 面包屑的使用不同的路由,面包屑显示的信息会自动变化bread.vue 12345678910111213141516171819202122<template> <div> <el-card> <el-breadcrumb separator="/"> <el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item> <el-breadcrumb-item v-for="(item,index) in $route.matched" :key="index" >{{item.name}}</el-breadcrumb-item> </el-breadcrumb> </el-card> </div></template><script>export default {}</script><style scoped></style> home.vue 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859<template> <div class="home"> <Header></Header> <el-container class="content"> <Menu>menu</Menu> <el-container> <el-main> <Breadcrumb/> <div class="cont"> <router-view></router-view> </div> </el-main> <el-footer> <Footer></Footer> </el-footer> </el-container> </el-container> </div></template><script>import Header from "@/components/common/Header.vue";import Footer from "@/components/common/Footer.vue";import Menu from "@/components/common/Menu.vue";import Breadcrumb from '@/components/common/Breadcrumb.vue'export default { components: { Header, Footer, Breadcrumb, Menu, }, data() { return {} }}</script><!-- Add "scoped" attribute to limit CSS to this component only --><style scoped lang="less">.home { width: 100%; height: 100%; .content{ position: absolute; width: 100%; top: 60px; bottom: 0; .cont{ margin: 20px 0; } }}</style> Card 卡片12345<!-- body-style的使用 --><el-card :body-style="{padding:'0px'}"> <div style="padding-top: 4px;display: flex"> </div</div></el-card> 级联选择器新版本props的使用级联选择器太高可以在全局样式里给.el-cascader-panel设置高度为200px:props=”{ expandTrigger: ‘hover’, value: ‘cat_id’, label: ‘cat_name’, children: ‘children’ }” 多选框 1234567891011121314151617<div> <el-checkbox-group v-model="checkboxGroup1"> <el-checkbox-button v-for="city in cities" :label="city" :key="city">{{city}}</el-checkbox-button> </el-checkbox-group></div>const cityOptions = ['上海', '北京', '广州', '深圳']; export default { data () { return { checkboxGroup1: ['上海'], checkboxGroup2: ['上海'], checkboxGroup3: ['上海'], checkboxGroup4: ['上海'], cities: cityOptions }; } Avatar 头像12<el-avatar shape="square" size="small" src="https://baidu.com/logo"></el-avatar> Table 表格el-table怎样隐藏某一列el-table-column上添加v-if="false" 1234567891011121314151617181920212223<el-table v-loading="loading" :data="bczglList" @selection-change="handleSelectionChange"> <el-table-column label="id" align="center" prop="id" v-if="false" /> <el-table-column label="班次组编号" align="center" prop="bczbh" /> <el-table-column label="操作" align="center" class-name="small-padding fixed-width"> <template slot-scope="scope"> <el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)" v-hasPermi="['kqgl:bczgl:edit']" >修改</el-button> <el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)" v-hasPermi="['kqgl:bczgl:remove']" >删除</el-button> </template> </el-table-column></el-table> 先记着Vue同一个dom元素绑定多个点击事件如何绑定 1<el-button @click="dialogVisible = false;clearTempData()">取 消</el-button> 模板获取值 123<template slot-scope="scope"> <el-tag type="warning" v-for="ids in scope.row.typeID.split(',')">{{getGameType(ids)}}</el-tag></template> Store 数据 1agentId: _this.$store.getters.name.agentId,

2022-11-19
Vue项目
安装Vue局部安装局部安装并使用vue-cli 4.x版本,和创建项目 12345678910111213141516171819202122# 先创建一个文件夹# 初始化一个node项目npm init -y# 局部安装vuenpm i -D @vue/cli# 查看vue的版本# 局部安装要用npx命令C:\Users\mmzcg\WebstormProjects>npx vue -V@vue/cli 5.0.8# 创建一个项目# 为啥不能用npm运行npx vue create project-one# 进入项目并且运行cd project-onenpm run serve# 也可以用uinpx vue ui 全局安装12345npm install -g @vue/clivue create my-projectvue ui 安装Element-UI全局安装12# 安装npm i element-ui -S 1234// main.js 使用import ElementUI from 'element-ui'import 'element-ui/lib/theme-chalk/index.css'Vue.use(ElementUI) 按需引入123# 安装npm i element-ui -Snpm install babel-plugin-component -D 123456789101112131415161718192021222324252627// main.js// 全局引入// import ElementUI from 'element-ui'// import 'element-ui/lib/theme-chalk/index.css'// Vue.use(ElementUI)// 按需引入import {Button} from 'element-ui'Vue.use(Button)// babel.config.jsmodule.exports = { // 自动生成的 presets: [ '@vue/cli-plugin-babel/preset' ], // 从官网上复制的 "plugins": [ [ "component", { "libraryName": "element-ui", "styleLibraryName": "theme-chalk" } ] ]} 如果没有找到.babelirc文件,找找babel.config.js文件 一个一个import 按需引入有点麻烦,在根目录下面创建一个plugins文件夹,在这个文件夹下面创建element.js 123import Vue from 'vue'import {Button} from 'element-ui'Vue.use(Button) 在main.js引入element.js 1import '../element.js' 安装CSS预处理器Sass安装的是sass,使用的时候是scss…. 留意 12npm install --save-dev node-sassnpm install --save-dev sass-loader 12345678# 注意这里是scss<style scoped lang="scss">.hello{ background: yellow; .el-button{ color: red }} Less12npm i less less-loader --save-devnpm i less less-loader --save-dev 1234567<style scoped lang="less">.hello{ background: yellow; .el-button{ color: red }} 重置样式下面的代码可以搜索reset.css在src/assets 下创建css文件夹,在这个文件下创建reset.css文件,粘贴 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748/* http://meyerweb.com/eric/tools/css/reset/ v2.0 | 20110126 License: none (public domain)*/html, body, div, span, applet, object, iframe,h1, h2, h3, h4, h5, h6, p, blockquote, pre,a, abbr, acronym, address, big, cite, code,del, dfn, em, img, ins, kbd, q, s, samp,small, strike, strong, sub, sup, tt, var,b, u, i, center,dl, dt, dd, ol, ul, li,fieldset, form, label, legend,table, caption, tbody, tfoot, thead, tr, th, td,article, aside, canvas, details, embed,figure, figcaption, footer, header, hgroup,menu, nav, output, ruby, section, summary,time, mark, audio, video { margin: 0; padding: 0; border: 0; font-size: 100%; font: inherit; vertical-align: baseline;}/* HTML5 display-role reset for older browsers */article, aside, details, figcaption, figure,footer, header, hgroup, menu, nav, section { display: block;}body { line-height: 1;}ol, ul { list-style: none;}blockquote, q { quotes: none;}blockquote:before, blockquote:after,q:before, q:after { content: ''; content: none;}table { border-collapse: collapse; border-spacing: 0;} 在App.vue或者其他的组件中使用 1234567891011<style lang="scss">@import url('./assets/css/reset.css');#app { font-family: Avenir, Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; color: #2c3e50; margin-top: 60px;}</style> 安装图标库12345678# 安装npm i -D font-awesomemain.jsimport 'font-awesome/css/font-awesome.min.css'# 使用<i class="fa fa-user"></i> 安装axios123456npm i axios -Smain.jsimport axios from "axios";# 挂在之后,就可以全局使用了Vue.prototype.axios = axios 安装vue-router12# vue-router3 和 4 的配置不一样npm install [email protected] 创建文件和文件夹src->router->index.js 1234567891011121314151617181920212223import Vue from 'vue'import Router from 'vue-router'import Home from '../components/Home.vue'Vue.use(Router)export default new Router({ routes:[ { path:'/home', component:Home } ], mode:'history'})main.jsimport router from './router'new Vue({ render: h => h(App), router,}).$mount('#app') 路由懒加载和异步组件123456789101112131415export default new Router({ routes:[ { path:'/home', // 路由懒加载 component:()=>import('@/components/Home.vue') }, { path:'/home2', // 异步组件 component:resolve =>require(['@/components/Home.vue'],resolve) } ], mode:'history'}) setToken封装src→utils→settoken.js 1234567891011export function setToken(key,token){ return localStorage.setItem(key,token)}export function getToken(key){ return localStorage.getItem(key)}export function delToken(key){ return localStorage.removeItem(key)} 使用 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647<script>import {nameRule,passwordRule} from "@/utils/vaildate";import {setToken} from "@/utils/setToken";export default { data() { return { form: { username: "", password: "" }, rules: { username: [{validator: nameRule, trigger: 'blur'}], password: [{validator: passwordRule, trigger: 'blur'}], } } }, methods: { login(form) { this.$refs[form].validate((valid) => { if (valid) { console.log("验证通过") this.axios.post("http://localhost", this.form).then( res => { console.log(res) if (res.data.status === 200) { setToken("username", res.data.username) this.$message({ message: res.data.message, type: 'success' }) this.$router.push('/home') } } ) .catch(err => { console.error(err) }) } else { console.error("验证不通过") } }) } }}</script> 配置代理服务器123456789101112131415161718192021const { defineConfig } = require('@vue/cli-service')module.exports = defineConfig({ transpileDependencies: true, lintOnSave: false, devServer:{ // 运行自动打开浏览器, open:true, // 如果不配置这个,自动打开浏览器,就是0.0.0.0:8080 host:"localhost", proxy:{ '/api':{ target:'http://127.0.0.1', changeOrigin:true, pathRewrite:{ '^/api':'' } } } }}) 40412345{ path:'*', // 路由懒加载 component:()=>import('@/components/404.vue')}

2022-11-19
Vue
按照官方文档的教程步骤来,大部分的代码Demo,都是通过脚手架创建的项目的基础上创建的,用的版本的Vue3,Typescript,组合式的写法 基础创建一个应用多个应用实例 没什么用,现在项目都是从头开始都是vue项目,不会用来一步一步替换,没有想到应用场景 1234567891011121314151617181920212223242526272829303132333435363738394041424344<!DOCTYPE html><html><head> <title>Vue Multiple Instances Example</title> <script src="https://cdn.jsdelivr.net/npm/vue@2"></script></head><body> <div id="app1"> <p>{{ message }}</p> <button @click="updateMessage">更新消息</button> </div> <div id="app2"> <p>{{ message }}</p> <button @click="updateMessage">更新消息</button> </div> <script> var app1 = new Vue({ el: '#app1', data: { message: '这是第一个 Vue 实例的消息' }, methods: { updateMessage() { this.message = '第一个实例的消息已更新'; } } }); var app2 = new Vue({ el: '#app2', data: { message: '这是第二个 Vue 实例的消息' }, methods: { updateMessage() { this.message = '第二个实例的消息已更新'; } } }); </script></body></html> 模板语法文本插值 双大括号标签会被替换为msg 属性的值。同时每次 msg 属性更改时它也会同步更新。 12345678<script setup lang="ts">import {ref} from "vue";const text = ref<string>("文本插值语法");</script><template> {{text}}</template> 原始 HTML 有的时候想想渲染源码,有的时候不想渲染源码 v-html就是指令语法 123456789<script setup lang="ts">import {ref} from "vue";const rawHtml = ref<string>("<span style='color: red'>This should be red.</span>");</script><template> <p>Using text interpolation: {{ rawHtml }}</p> <p>Using v-html directive: <span v-html="rawHtml"></span></p></template> Attribute 绑定 如果想给html的属性绑定数据要怎么操作呢,下面以给div的id和class属性绑定做演示 123456789101112131415161718192021222324252627282930313233343536373839404142<script setup lang="ts">import {ref} from "vue";const myId = ref<string>("myId");const showButton = ref<boolean>(false); type objectOfAttrs={ id: string, class:string} const objectOfAttrsObj=ref<objectOfAttrs>({ id: "myId4", class:"demo"}); </script><template> <!-- ❎错误做法 --> <div id={{myId}}></div> <!-- ✅正确做法,如果绑定的值是 null 或者 undefined,那么该 attribute 将会从渲染的元素上移除。 --> <div v-bind:id="myId"></div> <!-- ✅正确做法,简写写法 --> <div :id="myId"></div> <!-- ✅正确做法,同名简写写法,就是属性(Id),和等号对应的值是一样的话 --> <div :id></div> <!-- ✅正确做法,同名简写写法,第二种写法 --> <div v-bind:id></div> <!-- -------------------------------------------------------- --> <!-- 绑定布尔类型 --> <button :disabled="showButton">按钮</button> <!-- 动态绑定多个值 --> <div v-bind="objectOfAttrsObj">动态绑定多个值,只是需要写v-bind</div></template> 响应式基础这个有点底层,不去了解了,大概意思是简单的数据类型就用ref,对象或者复杂嵌套的结构就用reactive 计算属性基础示例 Vue 的计算属性会自动追踪响应式依赖。它会检测到 authorComputed 依赖于 author.books,所以当 author.books 改变时,任何依赖于 authorComputed 的绑定都会同时更新。 计算属性会被缓存,并且只调用一次,方法是调用一次执行一次,在例子中,触发了一次页面渲染(页面的数据有变动),方法就会被执行一次 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061<script setup lang="ts">import {computed, reactive, ref} from "vue";type authorType={ name:string, books:string[]}const author = reactive<authorType>({ name: 'John Doe', books: [ 'Vue 2 - Advanced Guide', 'Vue 3 - Basic Guide', 'Vue 4 - The Mystery' ]})// 计算属性,使用泛型,返回值必须是字符串const authorComputed = computed<string>(()=>{ return author.books.length >0?author.books.length.toString():"没有书"})function add(){ author.books.push("Vue 5 - Advanced Guide")}// 计算属性,使用泛型,返回值必须是字符串const myDateTimeComputed = computed<number>(() => { return getDateTime();});function getDateTime(){ console.log("方法被调用") return Date.now();}function myDateTimeMethod(){ return getDateTime();}// 定义一个用于触发重新渲染的状态const renderCounter = ref(0);function forceRerender() { console.log("12321") renderCounter.value++;}</script><template> <!-- 计算属性 --> <span>通过计算属性计算出来的结果:{{ authorComputed }}</span> <br> <button @click="add()">添加数据</button> <br> <!-- 计算属性和方法的区别 --> <button @click="forceRerender">触发重新渲染</button> 计算属性和方法的区别-计算属性:{{ myDateTimeComputed }} 计算属性和方法的区别-方法:{{renderCounter}}-{{ myDateTimeMethod() }}</template> 可写计算属性 计算属性默认是只读的。当你尝试修改一个计算属性时,你会收到一个运行时警告。只在某些特殊场景中你可能才需要用到“可写”的属性,你可以通过同时提供 getter 和 setter 来创建 1234567891011121314151617181920212223242526272829303132333435363738394041<script setup lang="ts">import {computed, reactive, ref} from "vue";const firstName = ref('Anthony')/** * 错误的写法 */// const fullName = computed(()=>{// return firstName.value + ' ' + lastName.value// })/** * 正确的写法 */const fullName = computed({ // getter get() { return firstName.value }, // setter set(newValue) { // 注意:我们这里使用的是解构赋值语法 firstName.value = newValue }})/** * 这样是不对的 */function showWarn(){ console.log("修改计算属性") fullName.value="DiDiDi"}</script><template> 演示修改计算属性,控制台报错:{{fullName}} <button @click="showWarn">尝试修改计算属性</button></template> Class 与 Style 绑定太麻烦了,不看了 条件渲染v-if 可以单独使用 v-else-if 要跟v-if 一起使用 v-else 要跟v-if 或者 v-else-if 一起使用 12345678910111213<script setup lang="ts">import {computed, reactive, ref} from "vue";const season = ref<number>(6);</script><template> <h1 v-if="season >=1 && season <=3">春季</h1> <h1 v-else-if="season >=4 && season <=6">不是春季</h1> <h1 v-else>数据错误</h1></template> 列表渲染123456789101112131415161718192021222324<script setup lang="ts">import {computed, reactive, ref} from "vue";const items = ref([{ message: 'Foo' }, { message: 'Bar' }])</script><template> for循环 <li v-for="(item,index) in items"> {{index}}-{{ item.message }} </li> 解构函数 <li v-for="{ message } in items"> {{ message }} </li> 解构函数,有索引的时候 <li v-for="({message},index) in items"> {{index}}-{{ message }} </li></template> 事件处理我们可以使用 v-on 指令 (简写为 @) 来监听 DOM 事件,并在事件触发时执行对应的 JavaScript。用法:v-on:click="handler" 或 @click="handler"。 内联事件处理器 12345678910<script setup lang="ts">import {computed, reactive, ref} from "vue";const count = ref(0)</script><template> <button @click="count++">Add 1</button> <p>Count is: {{ count }}</p></template> 方法事件处理器 1234567891011<script setup lang="ts">function greet() { console.log("方法事件处理器")}</script><template> <!-- `greet` 是上面定义过的方法名 --> <button @click="greet">Greet</button></template> 表单输入绑定主要是看框架了,先不学了,太多细节了 生命周期钩子这个API好多个,以后慢慢看 侦听器每次修改被监听的对象,就会打印旧值和新值 基本示例 12345678910111213141516<script setup lang="ts">import {ref, watch} from 'vue'/*被监听的对象*/const question = ref<number>(1);// 可以直接侦听一个 refwatch(question, async (newQuestion, oldQuestion) => { console.log("老值:", oldQuestion) console.log("新值:", newQuestion)})</script><template> <input v-model="question"/></template> 侦听不同的数据源类型 watch 的第一个参数可以是不同形式的“数据源”:它可以是一个 ref (包括计算属性)、一个响应式对象、一个 getter 函数、或多个数据源组成的数组: 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455<script setup lang="ts">import {reactive, ref, watch} from 'vue'const x = ref<number>(0)const y = ref<number>(0)type authorType={ name: string,}const z = reactive<authorType>({ name:"anthony"})// refwatch(x, (newX,oldValue) => { console.log(`监听单个ref,x的旧值是:${oldValue},新值是:${newX}`)})// getter 函数watch( () => x.value + y.value, (newSum, oldSum) => { console.log(`监听getter函数, 原来的和值是:${oldSum},新值是: ${newSum}`) })// 多个来源组成的数组watch([x, () => y.value], ([newX, newY],[oldX, oldY]) => { console.log(`监听多个数据源:X的旧值是:${oldX},X的新值是:${newX},Y的旧值是:${oldY},Y的新值是:${newY}`)})// 监听reactive// 错误,因为 watch() 得到的参数是一个 number// 错误的写法,会提示报错,// MyClick.vue:35 [Vue warn]: Invalid watch source: anthony A watch source can only be a getter/effect function, a ref, a reactive object, or an array of these types.// at <MyClick>// at <App>// watch(z.name, (count) => {// console.log(`name is: ${count}`)// })// 正确的写法watch( () => z.name, (nameNew,oldName) => { console.log(`name is: ${nameNew},${oldName}`) })</script><template> <input v-model="x" type="number"/> <input v-model="y" type="number"/> <input v-model="z.name" /></template> 模板引用进入到页面会自动聚焦到输入框中 123456789101112131415<script setup>import { ref, onMounted } from 'vue'// 声明一个 ref 来存放该元素的引用// 必须和模板里的 ref 同名const input = ref(null)onMounted(() => { input.value.focus()})</script><template> <input ref="input" /></template> 组件基础定义组件 123456789<script setup>import { ref } from 'vue'const count = ref(0)</script><template> <button @click="count++">You clicked me {{ count }} times.</button></template> 使用组件 在父组件中引入 1234567<script setup lang="ts">import MyClick from "@/components/MyClick.vue";</script><template> <MyClick/></template> 传递 props(父传子) 1234567891011121314151617181920212223// 子组件<script setup lang="ts">import {ref} from 'vue'const count = ref<number>(0)defineProps(['title'])</script><template> <button @click="count++">{{title}},You clicked me {{ count }} times.</button></template>// 父组件<script setup lang="ts">import MyClick from "@/components/MyClick.vue";</script><template> <MyClick title="计算器1"/> <MyClick title="计算器2"/> <MyClick title="计算器3"/> <MyClick title="计算器4"/></template> emit(子传父) 父组件 123456789101112131415161718192021222324<script setup lang="ts">import { ref } from 'vue'import MyEmit from "@/components/MyEmit.vue";type Person = { name: string, age: number}const myName = ref<string>('')const myAge = ref<number>('')const getSubmit = (data: Person) => { myName.value = data.name myAge.value = data.age}</script><template> <MyEmit @myEmit="getSubmit" /> 从子组件接收的数据:{{ myName }} {{ myAge }}</template> 子组件 123456789101112131415<script setup lang="ts">type Person = { name: string, age: number}const me = { name: 'anthony', age: 23}const emit = defineEmits<{ (e: 'myEmit', payload: Person): void }>();emit('myEmit', me)</script> $bus 消息总线main.js 123456789101112131415//引入Vueimport Vue from 'vue'//引入Appimport App from './App.vue'//关闭Vue的生产提示Vue.config.productionTip = false//创建vmnew Vue({ el:'#app', render: h => h(App), beforeCreate() { Vue.prototype.$bus = this //安装全局事件总线 },}) App.vue 1234567891011121314151617181920212223242526272829<template> <div class="app"> <h1>{{msg}}</h1> <School/> <Student/> </div></template><script> import Student from './components/Student' import School from './components/School' export default { name:'App', components:{School,Student}, data() { return { msg:'你好啊!', } } }</script><style scoped> .app{ background-color: gray; padding: 5px; }</style> Student.vue 12345678910111213141516171819202122232425262728293031323334<template> <div class="school"> <h2>学校名称:{{name}}</h2> <h2>学校地址:{{address}}</h2> </div></template><script> export default { name:'School', data() { return { name:'尚硅谷', address:'北京', } }, mounted() { // console.log('School',this) this.$bus.$on('hello',(data)=>{ console.log('我是School组件,收到了数据',data) }) }, beforeDestroy() { this.$bus.$off('hello') }, }</script><style scoped> .school{ background-color: skyblue; padding: 5px; }</style> School.vue 1234567891011121314151617181920212223242526272829303132<template> <div class="student"> <h2>学生姓名:{{name}}</h2> <h2>学生性别:{{sex}}</h2> <button @click="sendStudentName">把学生名给School组件</button> </div></template><script> export default { name:'Student', data() { return { name:'张三', sex:'男', } }, methods: { sendStudentName(){ this.$bus.$emit('hello',this.name) } }, }</script><style lang="less" scoped> .student{ background-color: pink; padding: 5px; margin-top: 30px; }</style> $set$nextTick深入组件插槽插槽内容与出口 主文件 1234567891011121314151617181920212223242526272829<script setup lang="ts">import { ref, provide, reactive } from 'vue'import First from "@/components/First.vue";import MySlotWithName from "@/components/MySlotWithName.vue";import MySlotWithOutName from "@/components/MySlotWithOutName.vue";</script><template> <h1>插槽演示</h1> <!-- 匿名插槽 --> <MySlotWithOutName> <a href="https://baidu.com">跳转到baidu</a> </MySlotWithOutName> <br> <MySlotWithName> <!-- 具名插槽 --> <!-- <template v-slot:url> --> <!-- 简写的方式 --> <template #url> <a href="https://google.com">跳转到谷歌</a> </template> </MySlotWithName></template> MySlotWithOutName.vue 1234<template> 匿名插槽 <slot /></template> 1234<template> 具名插槽 <slot name="url" /></template> 作用域插槽 12345678910111213<script setup lang="ts">import MySlotWithName from "@/components/MySlotWithName.vue";</script><template> <MySlotWithName> <template #url="data"> {{ data.title }}- {{ data.age }} <a href="https://google.com">跳转到谷歌</a> </template> </MySlotWithName></template> 1234<template> 具名插槽 <slot name="url" title="anthony" age="12" /></template> 依赖注入给所有的下级组件,包括直接子组件传递数据和方法 这个例子的最上层组件修改数据之后,可以修改所有组件显示的数据 这个例子的下层组件修改数据之后,也是可以修改所有组件显示的数据 最上层组件 12345678910111213141516171819202122232425262728293031323334353637<script setup lang="ts">import { ref, provide, reactive } from 'vue'import First from "@/components/First.vue";type Person = { name: string, age: number}const me: Person = reactive({ name: 'anthony', age: 23})provide("provideDemo", me)const update = () => { me.age = me.age + 1 console.log(me);}const updateTwo = () => { me.age = me.age + 2 console.log(me);}provide("provideUpdateTwo", updateTwo)</script><template> 最顶层组件,接收到的数据:{{ me.name }},{{ me.age }}<button @click="update">修改所有层数据+1</button> <br> <First /></template> 第一层组件 1234567891011121314151617<script setup lang="ts">import { inject } from 'vue'import Second from "./Second.vue";type Person = { name: string, age: number}const me = inject<Person>("provideDemo")</script><template> 这是第一层,接收到的数据:{{ me.name }},{{ me.age }} <br> <Second /></template> 第二层组件 12345678910111213141516<script setup lang="ts">import { inject } from 'vue'import Third from "./Third.vue";type Person = { name: string, age: number}const me = inject<Person>("provideDemo")</script><template> 这是第二层,接收到的数据:{{ me.name }},{{ me.age }} <br> <Third /></template> 第三层组件 123456789101112131415<script setup lang="ts">import { inject } from 'vue'type Person = { name: string, age: number}const me = inject<Person>("provideDemo")const provideUpdateTwo = inject("provideUpdateTwo")</script><template> 这是第三层,接收到的数据:{{ me.name }},{{ me.age }}<button @click="provideUpdateTwo">修改最顶层数据+2</button></template> yarnyarn的安装 1npm install -g yarn yarn常用命令 12345678910111213141516// 初始化yarn init // 添加包yarn add [package]yarn add [package]@[version]yarn add [package]@[tag]// 添加到不同依赖项yarn add [package] --devyarn add [package] --peeryarn add [package] --optional// 升级包yarn upgrade [package]// 移除依赖包yarn remove [package]// 安装所有依赖yarn 或 yarn install 报错1.node新版本引起的报错 1234this[kHandle] = new _Hash(algorithm, xofLen);^Error: error:0308010C:digital envelope routines::unsupported 解决方法1: 123456推荐:修改package.json,在相关构建命令之前加入SET NODE_OPTIONS=--openssl-legacy-provider"scripts": { "serve": "SET NODE_OPTIONS=--openssl-legacy-provider && vue-cli-service serve", "build": "SET NODE_OPTIONS=--openssl-legacy-provider && vue-cli-service build"}, 解决方法2: 降低node版本 前端Vue父组件点击按钮打开子组件弹窗案例父组件 12345678910111213141516171819202122232425<template> <div> # 需要定义ref=child <indexChild ref="child"></indexChild> <el-button @click="open">打开弹窗</el-button> </div></template><script>import indexChild from "../../components/indexChild.vue";export default { components: { indexChild }, data () { return { }; }, methods: { open () { this.$refs.child.open(); // 这样可以直接访问子组件方法,用ref拿子组件方法 } }}</script> 子组件 123456789101112131415161718192021222324252627<template> <div> <el-dialog title="收货地址" :visible.sync="dialogFormVisible"> <span>这是一段信息</span> <div slot="footer" class="dialog-footer"> <el-button @click="dialogFormVisible = false">取 消</el-button> <el-button type="primary" @click="dialogFormVisible = false">确 定</el-button> </div> </el-dialog> </div></template><script>export default { data () { return { dialogFormVisible: false, }; }, methods: { open () { // 在父组件调用打开 this.dialogFormVisible = true } }};</script> 参考:前端Vue父组件点击按钮打开子组件弹窗案例
评论