管理后台项目

文章目录

前端项目初始化

https://www.cnblogs.com/jimmy2019/p/13961509.html

创建项目
https://www.cnblogs.com/haitaoli/p/10304193.html

  1. 安装vue脚手架

  2. 脚手架创建项目(这就创建好vue项目了)

  • vue create **

  • 使用图形化界面 vue ui 命令
    以图形化界面创建和管理项目。上述命令会打开一个浏览器窗口,并以图形化界面将你引导至项目创建的流程。(d目录输入vue ui )

    利用babel就可以让我们在当前的项目中随意的使用这些新最新的es6,甚至es7的语法。说白了就是把各种javascript千奇百怪的语言统统专为浏览器可以认识的语言。

  1. 配置Element-UI组件库
  • 3.1可以通过element插件快速集成element-ui
//进入项目文件夹后执行
cd shop
vue add element

按如下操作进行全局引入( Fully import(全局引入))
在这里插入图片描述

  • 3.2从vue项目搭建的初始开始安装Element的步骤:

首先第一步是下载:打开命令行切换到vue项目根目录然后输入:npm i element-ui
安装完成后vue项目根目录下会多这个文件:package-lock.json 说明已经安装了element-ui插件
第二步骤:引入插件 在项目下的src下的main.js引入js和css如下:
import ElementUI from ‘element-ui’
import ‘element-ui/lib/theme-chalk/index.css’
第三步骤:让vue引用Element ui
Vue.use(ElementUI);
接下来测试能否使用 在src目录下的App.vue 文件template标签中随便写个组件 如图就代表安装且引用成功了

  • 3.3图形化界面
    点击右上角添加插件,输入插件名字:vue-cli-plugin-element
    点击第一个,点击安装vue-cli-plugin-element
    点击按需导入
    点击任务,选择server,再点击运行,可以查看输出以及控制台
  1. 配置axios库
    点击右上角添加依赖,输入名字:axios
    点击第一个
    运行时依赖不是开发依赖
  2. 项目托管到gitee
    简易的命令行入门教程:

Git 全局设置:

git config --global user.name " "
git config --global user.email " "

创建 git 仓库:

mkdir vue_shop 建项目文件夹
cd vue_shop 进入项目文件夹
git init
touch README.md
git add README.md
git commit -m “first commit”
git remote add origin https://gitee.com/libudding/vue_shop.git
git push -u origin master

已有仓库(可以在gitee上建好)

cd **(或者是去本地文件目录命令行执行以下)
git remote add origin https://gitee.com/libudding/vue_shop.git
git push -u origin master

  • 进到刚刚创建的项目,在项目中打开终端shift+右键,查看一下状态git status git add . git commit -m ’初始化项目‘ (只是在本地操作了仓库,如果需要把仓库上传到码云中需要)执行git remote add origin https://gitee.com/libudding/vue_shop.git
    git push -u origin master

后台项目的环境安装配置

安装MySQL数据库(之后导入数据库)
安装Node.js
配置项目相关信息(安装项目后台依赖包)(在项目中打开终端 npm install 安装所有的依赖包
启动项目(用node命令执行app.js)
使用postman测试后台项目接口是否正常

cls清屏
用node命令执行app.js node app.js

登录

登录技术点

  1. http无状态

跨域问题:前后端地址不一样,前后端分离必跨域。
跨域:域名 端口号 服务器名全一样叫同源,不一样就叫跨域
客户端服务器端就存在跨域问题了
客户端通过ajax访问服务器端数据

  • 通过cookie在客服端记录状态 通过session在服务器端记录状态(前端和后台接口不存在跨域问题)
  • 通过token维持登录状态(前端和服务器之间存在跨域问题)
  1. 登录-token原理(有点像session)
    ------------token就是记录服务器端和客服端身份校验的------
    用户输入用户名密码登录,
    服务器验证通过之后生成该用户的token并返回给客户端(token服务器生成,每个用户不一样),
    接下来客户端存储该token(token记录了客服端的登录状态),
    后续的所有请求都需要携带该token,发送请求,
    服务器根据提交过来的token去验证是哪个用户,根据用户操作返回结果。

登录页面布局

  • element-ui
    el-form 整个是个大表单
    el-form-item 用户密码登录重置都是
    el-input 用户密码
    el-button 登录重置
    字体图标

开发新功能前都先创建个分支,开发完成后再合并到master主分支上
创建分支 git checkout -b login(创建了一个子分支,并且通过 checkout 命令切换到了login分支上,-b分支的意思)
查看项目中所有分支 git branch
开发完成后再merge到master分支

删掉不要组件

main.js是入口文件
App.vue是根组件

main.js导入了vue,导入了App.vue根组件,导入了路由,导入了element对应的组件。

new了个vue的实例,通过render函数把App根组件渲染到了页面上,同时也把路由挂载到了vue实例上。

打开App.vue发现template里放的就是整个页面的结构,注意template里要放个根标签

再打开router.js(路由),将routes数组中的路由规则清除,然后将views删除,将components中的helloworld.vue删除

去导入的里面找对象,即去vue-router里面找对象,对象里面有install()方法,执行vue-router里面
import Vue from 'vue'
import Router from 'vue-router'

Vue.use(Router)

export default new Router({
  routes: [
    
  ]
})

router

import Vue from 'vue'
import VueRouter from 'vue-router'

Vue.use(VueRouter)

const routes = [

]

const router = new VueRouter({
  routes
})

export default router

import Vue from 'vue'
import VueRouter from 'vue-router'

Vue.use(VueRouter)

export default new VueRouter({
  routes:[] 里面每加一个对象就是个路由规则,当用户访问/login,我们通过component属性指定要展示的组件
  重定向/login
})

在app.vue中还要放个路由的占位符<router-view></router-view>这样我们通过路由匹配到的组件,都会被渲染到router-view里面去

目录

1、./是当前目录

2、…/是父级目录

3、/是根目录

vue实例

https://blog.youkuaiyun.com/l1996729/article/details/106297700

https://blog.youkuaiyun.com/qq_43672268/article/details/105134001?utm_medium=distribute.pc_relevant.none-task-blog-2defaultbaidujs_baidulandingword~default-0.no_search_link&spm=1001.2101.3001.4242.0

$mount(’#app’) :手动挂载到id为app的dom中的意思。需要注意的是:该方法是直接挂载到入口文件index.html 的 id=app 的dom 元素上的

https://blog.youkuaiyun.com/github_37516320/article/details/78321391

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
</head>

<body>
    <div id="app-1">
        {{msg}}
    </div>
    ================
    <div id="app-2">
        {{msg}}
    </div>
    =================
    <div id="app-3">
        {{msg}}
    </div>

    <script>
        //在vue.js中,可以使用template给元素添加模板,但是元素中只能有一个根元素,不能出现两个或两个以上的根同级元素。还可以在模板中绑定数据、表达式等。下面利用实例说明如何添加模板
        // 创建 Vue 实例,得到 ViewModel
        new Vue({
            el: '#app-1',
            data: {
                msg: '这是通过el属性获取挂载元素的outerHTML方式渲染'
            }
        });
        //结论:如果vue实例中有template属性,会将该属性值进行编译,将编译后的虚拟dom直接替换掉vue实例绑定的元素(即el绑定的那个元素);
        //注意:template属性中的dom结构只能有一个根元素,如果有多个根元素需要使用v-if、v-else、v-else-if设置成只显示其中一个根元素;
        new Vue({
            el: '#app-2',
            data: {
                msg: '这是通过el属性获取挂载元素的outerHTML方式渲染'
            },
            template: '<div>这是template属性模板渲染</div>'
        });
        //render 
        new Vue({
            el: '#app-3',
            data: {
                msg: '这是通过el属性获取挂载元素的outerHTML方式渲染'
            },
            template: '<div>这是template属性模板渲染</div>',
            render: function (createElement) {
                return createElement('div',
                    // 参数2、这里相当于给标签加属性 例如:<div style='color:red,font-size: 14px'></div>
                    {
                        //给div绑定样式
                        style: {
                            width: '300px',
                            height: '400px',
                            color: 'pink'
                        },
                        //给div绑定点击事件  
                        on: {
                            click: () => {
                                console.log('点击事件')
                            }
                        }
                    },
                    // 参数3、参数中渲染的标签的子元素数组(可选) 
                    // [
                    //    // 文本节点直接写就可以
                    //    'text'
                    // ]
                    '这是render属性方式渲染。'
                );
            }
        });
    </script>
</body>

</html>

箭头函数

https://blog.youkuaiyun.com/qq_32614411/article/details/80897256

类似于匿名函数
x=>xx
相当于
function (x){
return x
x
}

function fn2(a, b) {
    return a + b
}

var fn1 = function(a, b) {
    return a + b
}
 

var fn1 = (a, b) => {
    return a + b
}
 
(a, b) => {
    return a + b
}

h => h(App)

function (h){
  return h(App)
}

用less语法

必须配置less相关的loader,打开可视化工具安装依赖
在自己终端输入vue ui即可
或者在终端运行 npm install less-loader@4.1.0 --save-dev(开发依赖)不打dev是运行依赖
less-loader内部是依赖于less这个包的 ,所以 npm install less@3.9.0 --save-dev

全局样式表

main.js引入

登录样式

border-radius: 3px;

position: absolute;

border-radius: 50%;

box-shadow: h-shadow v-shadow blur spread color inset;
box-shadow: 10px 10px 5px #888888;
box-shadow: 0px 0px 10px #888888; 向外扩散10px

盒子里面放盒子再放图片

改这个图片的样式,可以改盒子2(可以在盒子1的样式里嵌套盒子2的样式)
盒子2 .avatar_box {
width: 130px;
height: 130px;
img {
width: 100%;
height: 100%;
}

element是按需导入的

需要哪些组件必须先导入才能正常使用
找到element.js,在这个文件中用到哪些组件就导入哪些组件(main.js入口文件中已经导入了element.js)

Vue.use(Button)把他们注册成全局可用的组件

按需导入并注册

.btns {
display: flex;
justify-content: flex-end;横轴上尾部对齐
}
icon element有提供,因为main.js引用了饿element所以直接用就行
如果觉得icon的图少,可以去阿里图标库下载,下载的css文件导入到main.js(入口文件中)

box-sizing

box-sizing: content-box|border-box
content-box默认值,说好宽多少就是多少,再设置padding值会撑的很大,破坏外部布局

v-model

v-model指令来实现表单元素和数据的双向绑定
input填的数据会自动同步到data中

使用Form Methods

拿到表单的实例对象(即form),直接访问resetFields这个函数resetFields()

如何拿到表单的实例对象:给<el-form>加个ref=“aaa”,现在就可以通过aaa调用resetFields了

ref

https://blog.youkuaiyun.com/qq_38128179/article/details/88876060

ref是vue的获取dom元素的方式
$refs指向ref写的名字,指向vue实例对象

在JavaScript中需要通过document.querySelector("#demo")来获取dom节点,然后再获取这个节点的值。在Vue中,我们不用获取dom节点,元素绑定ref之后,直接通过this.$refs即可调用,这样可以减少获取dom节点的消耗。

<p ref="p">Hello</p>
<children ref="children"></children>
this.$refs.p
this.$refs.children

methods中的this

this指向实例对象(就是那个new vue)
console.log(this) this里有$refs属性
这个aaa是当前元素的实例对象
this.$refs.aaa 指向当前aaa的dom
可以自己试试

111登录前的预验证

之前有个表单的数据验证,是为了验证是不是符合input写的名字字数是2-6

现在这个登录前的预验证:点击登录按钮不应该直接发起登录的网络请求,而是在发起请求之前,先对表单数据进行预验证,当验证通过之后才能发送请求。否则提示表单数据不合法。

发现这两种性质差不多

点击登录之后调用函数进行预验证 在element有 form methods中有个函数

return 和 return false

https://www.cnblogs.com/yalong/p/9811176.html

  1. return返回null,起到中断方法执行的效果,只要不return false事件处理函数将会继续执行,表单将提交

  2. return false,事件处理函数会取消事件,不再继续向下执行。比如表单将终止提交。

  3. return ; return false return true 都会在函数内部阻止程序的执行。

  4. 只有 return false 会阻止表单的提交。

<form action="index.jsp" method="post" onsubmit="return submitTest();">
    <INPUT value="www">
    <input type="submit" value="submit">
</form>

<script>
    function submitTest() {
        return;
    }    
</script>

如果return false 阻止表单发请求

https://www.jb51.net/article/69483.htm

return;
此情况就是单纯的将控制权转交给主调函数继续执行。

return语句的普通应用并没有特殊之处,最需要注意的是return false的使用。事件处理函数返回false可以阻止默认事件的发生。

if(!valid) return;这里如果valid是false,!valid是true,则会执行return ,会跳出这个函数,不执行下面的。
如果valid是true,则说明填的信息是对的,则不执行return,而会执行后面的。

login() {
this.$refs.loginFromRef.validate(valid => {
// console.log(valid);
if (!valid) return;
this.$http.post(“login”, this.username).then(res => {})
}

axios发请求

对axios进行全局的配置
在main.js中导入这个包import axios from ‘axios’
把这个包挂载到vue的原型对象上面Vue.prototype.$http=axios。这样的话,每个vue的组件都可以通过this直接访问到$http,从而去发起ajax请求。
挂载完这个原型的属性之后,尽量为axios设置请求的根路径

请求写好之后,要开启mysql,运行后台代码node app.js

axios请求返回来的数据会加个壳,所以要解构赋值

promise

如果某个方法的返回值是promise,我们可以用async和await来简化这个promise(不再是promise了,而是个具体的响应对象)。我们可以在这个返回之前加上await(注意await只能用在被async修饰的方法中),所以把紧挨着await的方法修饰成异步的async方法

如果某个方法的返回值是promise,我们加个then也行

// login() {
    //   this.$refs.loginFromRef.validate(async valid => {
    //     // console.log(valid);
    //     if (!valid) return;
    //     const result = await this.$http.post("login", this.username);
    //     console.log(result);
    //   });
    login() {
      this.$refs.loginFromRef.validate(valid => {
        // console.log(valid);
        if (!valid) return;
        this.$http.post("login", this.username).then(res => {
          console.log(res);
        });
      });

解构

解构赋值 mdn:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment
const result = await this.$http.post(“login”, this.username);
{data: {…}, status: 200, statusText: ‘OK’, headers: {…}, config: {…}, …}
返回的result是个对象,我们可以从result解构赋值出来

const result = await this.$http.post(“login”, this.username);
data属性重命名res
const {data:res} = await this.$http.post(“login”, this.username);

return 还能返回 log呢

if (res.meta.staus !== 200) return console.log(“登录失败”);
console.log(“登录成功”);
这句是满足条件返回登录失败,否则跳过这句执行登录成功

if语句

if (condition)
{
    当条件为 true 时执行的代码
}
if (time<20)
{
    x="Good day";
}
if (condition)
{
    当条件为 true 时执行的代码
}
else
{
    当条件不为 true 时执行的代码
}
if (time<20)
{
    x="Good day";
}
else
{
    x="Good evening";
}

message

这个element,和form有点不一样,除了导入,还需要进行全局挂载(即把message挂载到vue上)(即把 弹窗组件 挂载到vue的原型对象上)

登录成功后 token

  1. 将登录成功之后的token,保存到客户端的 sessionStorage
    1.1 项目中出了登录之外的其他API接口,必须在登录之后才能访问
    1.2 token 只应在当前网站打开期间生效,所以将 token 保存在 sessionStorage 中(肯定不是localStorage因为这个是长久的)
    window.sessionStorage.setItem(‘token’, res.data.token)
  2. 通过编程式导航跳转到后台主页,路由地址是 /home
    this.$router.push("/home");

为什么要保存token:
因为项目中除了登录之外的接口,其他接口必须在登录之后才能访问想访问其他api接口,就要告诉他我登录了。我们可以在访问这些接口的时候提供一下token,因为token是登录成功之后,服务器颁发给我们的登录的令牌(登录的身份认证的信息)。
我们只要在访问这些接口的时候,携带了token,那么一定能访问成功。
所以token有必要保存在客户端。

通过路由导航守卫控制访问权限

如果用户没有登录,不能访问/home(看不到页面内容),如果用户通过url地址直接访问,则强制跳转到登录页面 。
打开开发者工具 Application的session Storage,如果登录了是有token的。选中token,清除,此时就是未登录状态

// 挂载路由导航守卫
router.beforeEach((to, from, next) => {
  // to将要访问的路径
  // from代表从哪个路径跳转而来
  // next()放行 next('/login') 强制跳转
  if (to.path === '/login') return next()
  // 获取token
  const tokenStr = window.sessionStorage.getItem('token')
  if (!tokenStr) return next('/lohin')
  next()
})

return

执行完retunrn后面的语句之后跳出函数,就不执行后面了,因为后面可能是请求成功之后要做的事情
注意这个return是必须要加的,不能直接写next(),因为这样会不退出函数,不然好几种判断都做有问题

if (res.meta.status !== 200) return this.$message.error(res.meta.msg);

if语句 (!

变量)表示变量为空
if (!token) 表示token是空

实现退出功能

基于token实现退出简单,只要销毁本地token。这样后续请求不携带token,必须重新登录生成一个新的token之后才可能访问页面。

清空token
window.sessionStorage.clear();
跳转
this.$router.push('/login');

replace和push区别

this.$router.replace(’/login’);即使点击返回按钮也不会回到这个页面。

加上replace: true后,它不会向 history 添加新记录,而是跟它的方法名一样 —— 替换掉当前的 history 记录。

//声明式:

// 编程式:
router.replace(…)
//push方法也可以传replace
this.$router.push({path: ‘/home’, replace: true})

传参两种方式

https://www.jianshu.com/p/b3f4c1b3aab2
使用query:
使用params:

终端快捷键ctrl+~

git

新建一个项目终端,输入命令‘git status’查看修改过的与新增的文件内容
将所有文件添加到暂存区:git add .
将所有代码提交到本地仓库:git commit -m “添加登录功能以及/home的基本结构”
查看分支: git branch
发现所有代码都被提交到了login分支 将login分支代码合并到master主分支,
先切换到master:git checkout master
在master分支进行代码合并:git merge login
将本地的master推送到远端的码云:git push

推送本地的子分支到码云,先切换到子分支:git checkout 分支名
直接git push不行,因为gitee没有新建login分支
第一次把分支推送到码云:git push -u origin 远端分支名

开发的时候才发现我的新功能,用户权限的功能竟然是在login分支上开发的,不合适,应该放在单独的user分支上,所以我们应该统一的迁移到新分支user上
git checkeout -b user(-b是新建分支,checkout是切换分支)(这一句的意思是先新建个分支user,然后我们再切换到user分支上)
我们切换完毕后,当前代码的修改也切换到了user分支上
接下来把所有的修改操作添加到暂存区git add . git commit -m ‘完成用户列表功能的开发’
码云中并没有新分支的名字,第一次把分支推送到码云:git push -u origin 远端分支名
发现所有代码都被提交到了user分支 将user分支代码合并到master主分支,
先切换到master:git checkout master
在master分支进行代码合并:git merge user
将本地的master推送到远端的码云:git push

写新功能要创建个新分支git checkout -b rights
把本地的rights新分支推送到云端进行保存git push -u origin rights
git push 加不加-u取决于,在远端有没有这个分支,如果没有-u,需要加-u,如果有就不用加了直接git push
orign是云端仓储的别名
因为也要在云端新建一个分支rights,所以要加rights

如果你在本地处于right分支,同时远端也建了right分支,这时你想把代码推进分支right中,你只需要git push 即可

主页整体布局

使用element的Container 布局容器,进行整体布局。
这个有个<el-container>可以替代temeplate内的第一个div标签,实现根元素的效果

每个element提供的组件,组件名称就是类名。比如<el-container>这种element的标签名也是类名

开发者工具看一下,height看一下,分析为什么没撑开页面,heigh设置成100%

flex布局

  • justify-content属性
    justify-content属性定义了项目在主轴上的对齐方式。

    .box {
    justify-content: flex-start | flex-end | center | space-between | space-around;
    }

  • align-items属性
    align-items属性定义项目在交叉轴上如何对齐。

    .box {
    align-items: flex-start | flex-end | center | baseline | stretch;
    }

左右两侧有内容,想到flex布局,要分成两大类标签,flex使得这两类标签左右分开两边。比如用布局用的div包裹一下span和img。而另一边的el-button不需要包裹

子代选择器>

如果您不希望选择任意的后代元素,而是希望缩小范围,只选择某个元素的子元素,请使用子元素选择器(Child selector)。

例如,如果您希望选择只作为 h1 元素子元素的 strong 元素,可以这样写:

h1 > strong {color:red;}

.el-header {
color: #fff;
  font-size: 20px;
  >div{
      font-size: 12px;
  }
}

行内元素的margin-left是有效的

行内元素与块级元素的区别之一就是 对于行内元素来说margin只有margin-left和margin-right有效,padding只有padding-left和padding-right有效,《css权威指南》上有写

通过接口请求侧边栏菜单数据

需要授权的 API ,必须在请求头中使用 Authorization 字段提供 token 令牌。
即除了登陆接口,其他接口必须要授权才能正常调用。
怎么授权,即登录成功之后发的token令牌,这个token令牌就是权限认证的资格。

如何在每一个api的请求头中添加 Authorization 字段?
通过axios请求拦截器添加token,保证拥有获取数据的权限

在main.js中,在axios挂载到原型对象之前,设置拦截器。

两次token

一个是通过路由导航守卫判断每个路径的名字,看有没有token,如果没有,router(‘/login’)。若有就继续下一步next()。因为上面是to其他地址的时候遇到路由导航守卫,所以next()后面就是要继续访问其他页面,就是要发请求了。

但是真正发请求的时候

想方位另一个页面,发请求的时候还有一个请求拦截器,在请求发送之前把token放在请求头中,传递给服务器

登录之后返回个token,保存到sessionstorage。
有一次用,是取出来判断是不是空,如果不是空,可以尝试访问home地址
之后一次用,是放到请求头中

请求头中带token到服务器

开发者工具,打开network,输入正确密码点击登录,发起一个login的登录,看一下login的请求头中有没有包括Authorization,发现有,但是值是null,因为发起的是登录请求,在登录期间服务器肯定没有给你颁发令牌。
如果登录之后再去调用其他接口,再次查看请求,就会被发现Authorization的值是个真正的token令牌。
服务器在接收到这个请求,会先判断这个Authorization是否符合要求,如果符合要求才会真正的进行响应,如果不存在,或者不符合要求,会驳回这次请求。

//请求在到达服务器之前,先会调用use中的这个回调函数来添加请求头信息
axios.interceptors.request.use(config => {
  //为请求头对象,添加token验证的Authorization字段
  console.log(config)
  config.headers.Authorization = window.sessionStorage.getItem('token')
  return config
})
// axios有个interceptors属性
// request是请求拦截器,通过use函数为请求拦截器挂载个回调函数,config是请求对象
// 在最后必须retun config 固定写法,return表示对请求头做好处理,放出去,发送到服务器

router-view是占位符

<router-view>作为占位,之后组件渲染的位置。
在路由切换时, 切换的是<router-view>挂载的组件, 其他内容不会发生改变.
这个标签相当于占位的作用。之后路径一变,组件渲染出来,替代router-view位置

App.vue是main.js分出去的。现在main.js导入了App.vue,那么App.vue中的<router-view>也相当于在main.js。
rouer的页面 index.js里面导入Home.vue和Login.vue的目的是,建立组件和路由的联系,之后路由一变,相关的组件就渲染到router-view位置了

所以这里都没有用到components,即没有用到组件乱引用,乱设置父子组件的情况。

通过接口请求侧边栏菜单数据

请求侧边栏数据
在created阶段请求左侧菜单数据

created写在methods之前

请求数据虽然是created,但是created肯定不会存太复杂的运算,肯定created中只是个调用,真正请求的发生是在methods中的。

created里面调用methods中的方法也是要加this的 this.getMenuList()

methods方法的前面也是可以加上async的,神奇
methods: {
async getMenuList() {
const result = await this.$http.get(“menus”);
}}加完async之后,它内部的await的地方就能返回不是promise的数据了

if (res.meta.status == 200) this.menuList = res.data;
改为
if (res.meta.status !== 200) return this.$message.error(this.meta.msg);
this.menuList = res.data;

发现home.vue和login.vue算是子组件

虽然没注册,是路由跳转的时候,整个文件通过路由调取整个组件,渲染到占位符上。但是这些文件里面的data是函数形式的 data(){return{} }

字体图标

字体图标就导入个css就能用了,注意css是导入到main.js中了,这样其他组件在用的时候就不用一直导入了,直接用。直接把iconfont类和图标独有的类加到标签中就行了。 或者直接用element中的,也是就把类加到标签就行了。
直接通过设置类名为 el-icon-iconName 来使用即可

布尔值写在标签中必须加:

<el-menu
background-color="#333744"
text-color="#fff"
active-text-color="#409EFF"
v-for="item in menuList "
:key=“item.id”
:unique-opened=“true”
>
还可以简写unique-opened,这样代表,我们希望把unique-opened值,设置为true
不加:不行,是字符串不行的,加了才能识别出是布尔值

??直接unique-opened:true或1??

复合选择器

.el-aside {
.el-menu {
border-right: 0px;
}

letter-spacing

letter-spacing 属性增加或减少字符间的空白(字符间距)。

该属性定义了在文本字符框之间插入多少空间。由于字符字形通常比其字符框要窄,指定长度值时,会调整字母之间通常的间隔。因此,normal 就相当于值为 0。

注释:允许使用负值,这会让字母之间挤得更紧。

cursor

cursor: pointer 鼠标放上去变成小手

布尔值

<el-aside width=“200px’”>
根据一个布尔值确定宽度是多少,不用非要写成isToggle=’true’ 有点if的感觉
<el-aside :width=“isToggle?‘64px’:‘200px’”>

登录进到home之后,进到组件welcom

welcome这个路由设置成home路由的子路由,home这个页面中嵌套显示welcome的子组件
const routes = [
{ path: ‘/’, redirect: ‘/login’ },
{ path: ‘/login’, component: Login },
{ path: ‘/test’, component: Test },
{
path: ‘/home’, component: Home, children: [
{ path: ‘/’, redirect: ‘/welcome’ },
{ path: ‘/welcome’, component: Welcome },
{ path: ‘/users’, component: Users } http://localhost:8080/users 注意这里可以不是home/users
]
}
]
但是没有加component等,这算是啥子组件啊,疑惑
知道点击侧边栏,为啥会渲染到home.vue的<router-view>了吧,这路由就是写在home中的啊,<router-view>也是写在home.vue的
就像app.vue中的<router-view>

home路由里面包含个子路由

/home会直接跳成/welcome

sessionStorage

有些值要存在session中,不然刷新就全没了,比如可以通过sessionstorage存储确定当前高亮

box-shadow

box-shadow: h-shadow v-shadow blur spread color inset;

!important语法

选择器{样式:值!import;}
a{color:green!important;}中间不能加分号;

标签中写数字也要加:

<el-row :gutter=“20”>

路由和请求地址的地址不是一个地址

虽然都是user,但是axios请求地址是服务器地址,有根地址。
路由地址是根据路由确定跳到哪个页面。

发axios请求get、post 带参数区别

login中post
loginForm: {
username: “admin”,
password: “123456”
},
axios.post(“login”, this.loginForm);

user中的get
queryInfo: {
query: “”,
pagenum: 1,
pagesize: 2
}
axios.get(“users”, {params:this.queryInfo})

axios请求的老是这种promise的数据。为了简化数据可以加个then或者async await

发请求,参数拼接,地址是动态的,没请求参数

this.$http.put('users/:uId/state/:type')
this.$http.put(`users/${userInfo.id}/state/${userInfo.mg_state}` );

this.$http.get('users/:id')
this.$http.get("users/" + id);

作用域插槽

使用作用域插槽的目的就是把子数据传过来,由父组件这边决定什么形式渲染出来。

布尔值按需渲染成开关,用到作用域插槽,获取这一行的数据,就可以点出来状态具体的值,按需渲染状态的效果。
slot 插槽
https://blog.youkuaiyun.com/houyibing930920/article/details/89513246
调用组件内部放一个摸板template,写个slot-scope,值随便写,接收数据。上面有个固定属性*.row,就是当前这一行的数据。

<el-table-column prop="mg_state" label="状态">
   <template slot-scope="scope">{{scope.row.mg_state}}</template>
</el-table-column>

通过插槽接收scope,通过scope得出来row属性,得到这一行对应的数据

还有“操作”也是要拿到对应的id才能操作用户,修改删除用户,也是需要用这个作用域插槽

别的都是拿到数据,直接放进<el-table-column>里,如果需要进行样式的修改就需要借用template作用域插槽
注意:没有插槽的情况下,在组件标签内一些内容是不起任何作用的

methods

注意在created等中调用methods中的方法也是要加上this的

更新状态

更新状态的时候带着这一行去,因为需要根据这一行的id去数据库修改。
@change新状态的值,指的是这个状态改变之后的值。本地存的这个scope.row都变了。最新的状态
<el-table-column label="状态">
        <template slot-scope="scope">
          <!-- {{scope.row.mg_state}} -->
          <el-switch v-model="scope.row.mg_state" @change="userStateChanged(scope.row)"></el-switch>
        </template>
</el-table-column>

改失败,不更新,再搞回来
if (res.meta.status !== 200) {
userInfo.mg_state = !userInfo.mg_state;
return this.$message.error(“设置状态失败”);
}

实现搜索功能

输入的内容数据绑定,点击搜索发请求。

原来发请求的query那个就是关键字搜索,直接点击的时候调用发送用户请求的那个即可。

如果是false

if (!valid)

表单验证(包括自定义验证验证邮箱手机号)

data() {
var emailPass = (rule, value, callback) => {
var reg = /1+[@][0-9a-zA-Z_.-]+([.][a-zA-Z]+){1,2}KaTeX parse error: Expected 'EOF', got '}' at position 93: …入正确的邮箱")); }̲; var mobil…/;
if (reg.test(value)) return callback();
callback(new Error(“请输入正确的手机号”));
};
return {
queryInfo: {
query: “”,
pagenum: 1,
pagesize: 2
},
userList: [],
total: 0,
addDialogVisible: false,
addForm: {
username: “”,
passorword: “”,
email: “”,
mobile: “”
},
addRules: {
username: [
{ required: true, message: “请输入用户名”, trigger: “blur” },
{ min: 3, max: 15, message: “长度在 3 到 15 个字符”, trigger: “blur” }
],
password: [
{ required: true, message: “请输入密码”, trigger: “blur” },
{ min: 3, max: 15, message: “长度在 3 到 15 个字符”, trigger: “blur” }
],
email: [
{ required: true, message: “请输入邮箱”, trigger: “blur” },
{ validator: emailPass, trigger: “blur” }
],
mobile: [
{ required: true, message: “请输入手机号”, trigger: “blur” },
{ validator: mobilePass, trigger: “blur” }
]
}
};
},

预验证 整体表单验证

addUserSuccess() {
this.$refs.addFormRef.validate(valid => {
console.log(valid);
if (!valid) return;
});
}

箭头函数之前加aync,一个参数 回调函数前加async

addUserSuccess() {
this.KaTeX parse error: Expected '}', got 'EOF' at end of input: …} = await this.http.post(“users”, this.addForm);
if (res.meta.status !== 201) this. m e s s a g e . e r r o r ( " 添 加 用 户 失 败 " ) ; t h i s . message.error("添加用户失败"); this. message.error("");this.message.success(“添加用户成功”);
this.addDialogVisible = false;
this.getUserList();
});
}

修改用户通过id实现

修改用户不是通过scope.row.emile实现的,而是通过点击修改哪一个的时候,连带着把id传进去。即点击修改按钮的单击事件的时候传进去用户id。
修改按钮button的外面,通过作用域插槽接收到了scope这个数据对象。通过scope.row拿到了这一行对应的数据信息。

报错try catch

 deleteUser(id) {
      const res = await this.$confirm(
        "此操作将永久删除该用户, 是否继续?",
        "删除用户",
        {
          confirmButtonText: "确定",
          cancelButtonText: "取消",
          type: "warning"
        }
      ).catch(err => err);

如果一个句子报错了,就在后面.catch捕获前面的所有错误,然后接收错误消息,return出去

catch捕获错误消息,并且把错误消息返回(return)给res进行接收。try catch这个错误之后,返回值打印的是cancle

用户点击取消的时候,catch

箭头函数的简单

.catch(err=>err)
函数体中如果只有一行代码且是return的,可以简写
.catch(err=>{return err})
花括号去掉的同时return也必须去掉

width 为啥可以直接写

table中scope.row是每一列的所有的数据

template里面写的是每一列的数据,需要用template接收数据,重新以自己想要的格式处理。
[{},{},{}] 则scope.row是个对象,里面的每个对象

用id做v-for的key值

设置最小宽度min-width

发现伸缩屏幕时,屏幕的布局被破坏了,为了防止发生这种事,在全局的css里面设置min-width

html,body,#app{
min-width: 800px;
}
当页面不足800时,会强制让页面宽是800,开始保持800的样子生成滚动条

设置flex属性,让他里面的元素在纵向上居中对齐align-items

.el-row {
display: flex;
align-items: center;
}

类的作用,定义上样式谁需要就给哪个标签加上这个类

pre

 <template slot-scope="scope">
     <pre>{{scope.row}}</pre>
<\template>


乱糟糟的数据
 <template slot-scope="scope">
     {{scope.row}}
<\template>

t-tag

t-tag 标签有两个事件,一个是点击时触发事件click,一个是关闭时触发的事件close(只要点击小叉就触发第二个事件,即close这个事件)
故可以在t-tag标签上搞个close事件 @close="。。。"

根据权限id删除对应的权限

服了层层嵌套也不管只要在template中,都能是使用scope.row

注意有两个变量的这种地址是用反语号表示的

服了层层嵌套也不管只要在template中,都能是使用scope.row

页面上有个问题就是删除某个权限后,这时如果调用 this.getRoleList()会刷新页面重新渲染,因为删除权限返回的是整个角色的所有权限,这时候我们就可以获取role.children = delr.data;这个role是指这一列的数据scope.row

scope.row是这一列的数据是个对象,你可以直接用scope.row。还可以通过点击事件把scope.row送出去

获取这一列三级权限的id,存在数组中,通过递归获取

最终的目的是获取到最后一项没有children

getRightCheckedId(node, arr) {
      if (!node.childre) return arr.push("node.id");
      node.children.forEach(item => getRightCheckedId(item, arr));
    }

 [
        {
            "id": 30,
            "roleName": "主管",
            "roleDesc": "技术负责人",
            "children": [
                {
                    "id": 101,
                    "authName": "商品管理",
                    "path": null,
                    "children": [
                        {
                            "id": 104,
                            "authName": "商品列表",
                            "path": null,
                            "children": [
                                {
                                    "id": 105,
                                    "authName": "添加商品",
                                    "path": null
                                }
                            ]
                        }
                    ]
                }
            ]
        }

push id的时候不能加“”

arr.push(node.id)

使用arr.push的时候注意

有时候这个arr是每点开这一列的请求来的数据放进arr里面的,这时我们需要先把arr清空之后再用,不然arr越来越多
监听对话框的关闭事件(注意对话框element本来就可以通过确认取消等关闭,故它关闭事件是专门的就是@close,而不是给每个东西添加监听事件)关闭之后把arr清空。即为arr重新赋值即可 arr=[]

调用element组件里的方法,发现要用ref

this.$refs.addFormRef.validate

两个数组合并成一个新数组—展开运算符

const arr3=[…arr1,…arr2]

数组拼接成以,分割的字符串

arr.join(’,’)

请求参数:通过 请求体 发送给后端

  • 请求路径:roles/:roleId/rights
  • 请求方法:post
  • 请求参数:通过 请求体 发送给后端
参数名参数说明备注
:roleId角色 ID不能为空携带在url中
rids权限 ID 列表(字符串), 分割的权限 ID 列表(获取所有被选中、叶子节点的key和半选中节点的key, 包括 1,2,3级节点)

请求体指的是放在地址后面,单独用个对象存的那种

this.$http.post(`roles/${this.roleId}/rights`, { rids: idStr });

例2

  • 请求路径:users/:id/role
  • 请求方法:put
  • 请求参数
参数名参数说明备注
id用户 ID不能为空参数是url参数:id
rid角色 id不能为空参数body参数
this.$http.put(
        `users/${this.userInfo.id}/role`,
        {
          rid: this.selectRoleId
        }
  }

如果一个id发现是传不过来的

传不过来指,比如一个table里面,一个按钮弹出对话框里面,一个对话框点击确定的时候获取用户id

所以我们应该在弹对话框的时候(点击按钮的时候能把scope.row拿到角色id),直接把角色的id保存到data中,供后续使用

这个id不一定是最新的我看,但是也不管是不是老的,我也觉得不会变的

比如后面有个例子是点开分配角色按钮,展示用户角色,数据存到userInfo里去,最后在这个对话框确认分配的角色之后要发送请求,需要用到用户的id,这时候你就可以用那个userInfo里中的id

获取table这一列的数据,之后展示在对话框中

scope.row即可获取这一列数据,可以点击分配用户的时候,把这个数据传递给对话框
获取到这个数据要存到data中,之后展示在对话框页面中
在点击事件中设置这个值存到data中,之后对话框展示可以直接用

element select框

<el-select v-model="selectRoleId" placeholder="请选择">
              <el-option
                v-for="item in roleList"
                :key="item.id"
                :label="item.roleName" 这是表现出来的名字 主管
                :value="item.id"  这是实际选中主管的话,其实选中的是主管的value值
              ></el-option>
</el-select>

roleList: [], 这是从服务器端请求来存在这的全部的角色列表
selectRoleId: "" 这就是你真实选中的那一个的值,比如你在框子里选中主管,那么这里存的就是主管的id

对话框关闭之后重置选择项

每次对话框打开都要显示请选择select让我选,而不是是我本身的角色
应该是在对话框关闭时每次清空一下对话框的选择 this.**=’’
@close=" "
还有用户信息,也是关闭对话框之后重置为空对象
this.selectRoleId = “”;
this.userInfo = {};

slot-scope接收这一行的数据

<template slot-scope=“scope”>

分页

关于分页一般参数里面要有两个数据,一个是当前的页码值,一个是当前每页显示多少条数据

<el-pagination
        @size-change="handleSizeChange" 切换每页显示多少条的时候会触发并且把最新的数据传进去
        @current-change="handleCurrentChange" 当前页数改变时触发,并且把最新的数据传进去
        :current-page="queryInfo.pagenum" 当前的页码值
        :page-sizes="[1, 2, 3, 5]" 数组,为了供pagesize的切换
        :page-size="queryInfo.pagesize" 是每页显示多少条数据
        layout="total, sizes, prev, pager, next, jumper"
        :total="this.total"
      ></el-pagination>

data:{ 
  return {
      queryInfo: {
        query: "",
        pagenum: 1,当前的页码值
        pagesize: 2 是每页显示多少条数据
      },
      }

为了显示分页效果,刚开始的时候必须要指定当前的页码值和每页显示多少条数据

扩展功能

扩展功能可以vue可视化面板,安装依赖插件,点击查看详情里面有用法示例
vue-table-with-tree-grid

v-if和v-else

发现字体图标的颜色是可以变的

<template slot="isok" slot-scope="scope">
          <i class="el-icon-success" v-if="scope.row.cat_deleted===false" style="color:lightgreen"></i>
          <i class="el-icon-error" v-else style="color:red"></i>
</template>

v-else后面啥也不跟哈,不跟条件的

添加分类对话框

分类名称
父级分类

这个添加分类注意一下,上面是必填项目,是自己填的。
如果下面什么也不填的话,就是默认将上面填的作为父级
如果上面填了,同时下面也填了的话,就是将上面的作为下面选择的子级
下面可以选择两个,如果你选择只选择一个家电,那么上面填的就是二级。如果你选择两个家电->彩色电视机,那么上面填的就是三级
如果下面什么也不选就是填的是一级

如果用get发请求,要用params参数

this.$http.get(“categories”, { params:{type: 2 }})

分析一下

<el-cascader
              v-model="selectKeys"  里面存的是你选中的父级,是个数据。存的是id值,根据下面的props确定的
              :options="parentCartList"里面存的是父级的所用分类数据列表,是你请求过来的
              :props="cascaderProps"
              @change="parentCartChange"每次下面的复选框一发生改变就触发change事件,同时也会把最新的数据selectKeys存在change事件中。
              clearable
></el-cascader>

parentCartChange() {
      console.log(this.selectKeys);
    }

之前为了展示复选框,获取了2级父级分类,故数据parentCartList里面存的是父级的数据。

selectKeys里面存的是你选中的父级,是个数据,里面存的是父级分类的id值,每次下面的复选框一发生改变就触发change事件,同时也会把数据存在change事件中。

再看一下想发送添加数据分类请求需要用到参数,建一个数据对象

// 添加分类的表单数据对象
addCartForm: {
// 将要添加的分类的名称(就是上面输入的名称是必填的)
cat_name: “”,
// 父级分类的id(若没选的话,默认应该是0)
cat_pid: 0,
// 分类的等级,默认要添加的是一级分类(若没选的话,对应的level也是0)
cat_level: 0
}

如果下面选中一项大家电,则父级分类id是大家电的id
同时你填的那个数据也不是一级分类了,是二级分类,归属于大家电

所以我们就该监听变化的change事件,selectKeys存在change事件中。这个监听的目的就是更新addCartForm,之后好发送请求

只要选中项发生了变化,那么selectKeys数组肯定会变化,在里面判断数组长度如果是1个,如果是2个 即>0 说明有选中数据分类,如果等于0说明没有选中数据分类

cat_pid
选中大家电一个,那么selectKeys是个[1]
选中大家电/电视两个,那么selectKeys是个[1,3] 即无论如何,永远选择最后一项作为父级分类的id

cat_level
如果selectKeys数组是一项,说明选中一个,那么数组包含一项,那么level是1
如果selectKeys数组是两项,说明选中两个,那么数组包含两项,那么level是2
与数组的length是一致的

如果没选的话,那个父级数组肯定是空数组

注意注意这个addCartForm的改变时间是在那个复选框改变的时候触发改变的,而不是点击确认的时候触发改变的。注意close事件应该是点击叉,确认,取消都能触发

数组最后一项的索引

等于数组长度减一
arr[arr.length-1]

监听对话框关闭事件,把里面的数据清空

el-cascader关闭清空

这个通过this.$refs.addCartRef.resetFields()是没办法清空的,需要把这个级联选择器绑定到的数据清空,同时需要把addCartForm这个里面的数据也清空,下一次再选择的时候再重新填。
v-model=“selectKeys” 这个selectKeys清空,不然打开还是显示数据啊

<el-form :model="addCartForm" :rules="addCartRules" ref="addCartRef" label-width="100px">
          <el-form-item label="分类名称" prop="cat_name">
            <el-input v-model="addCartForm.cat_name"></el-input>
          </el-form-item>
          <el-form-item label="父级分类">
            <el-cascader
              v-model="selectKeys"
              :options="parentCartList"
              :props="cascaderProps"
              @change="parentCartChange"
              clearable
            ></el-cascader>
          </el-form-item>
        </el-form>

其实 <el-cascader>不用放在el-form中,也可以。

表单验证是否为空

this.$refs.addFormRef.validate(async valid => {
        // console.log(valid);
        if (!valid) return;
        const { data: res } = await this.$http.post("users", this.addForm);
        if (res.meta.status !== 201) this.$message.error("添加用户失败");

        this.$message.success("添加用户成功");
        this.addDialogVisible = false;
        this.getUserList();

表单关闭时清空的办法

注意close事件应该是点击叉,确认,取消都能触发
1、把表单里面引用的数据清空
2、resetFields 对整个表单进行重置this.$refs.addCartRef.resetFields()

点击叉,确认,取消都触发close事件,如果确定的点击事件做的事情与触发close做的事情相违背呢,这时候会自动以确定事件为首要

本例中不相同,这是复选框的change事件触发的原始数据的修改
是close触发的把数据再修改为原始状态

通过计算属性禁用和启用按钮

计算属性

methods中可直接用计算属性,this.cartId 注意不加()

computed{
cateID() {
      // 证明选中了三级分类
      if (this.selectKeys == 3) {
        return this.selectKeys[2];
      }
      //   如果没有return出去,证明没有选中三级分类
      return null;
      证明没有这个id值;
    }
}    

计算属性还可以直接在methods方法中调用,比如一个请求的发送就需要这个处理后的数据,那么就可以直接调用this.cateID
想不到的是在这里调用的时候也是不需要加()

disabled

1.HTML写法
禁止某input控件。

【注意】
【1】disabled包含字母d
【2】“disabled”而不是true或false。

2.javascript操作
【读取】
document.getElementById(“testButton”).disabled;
返回结果禁止时为true,使能时为false。
【设置】
document.getElementById(“testButton”).disabled = false;
设置禁止为true,使能为false。

 <el-button
    type="primary"
    size="mini"
    :disabled="isCheckedCate"
     @click="addDialogVisible = true"
>添加参数</el-button>

表单重置

只要触发了关闭对话框,就将表单重置一下,防止下次打开还是上一次验证的情况

表单验证

点确定-预检验-通过发请求

发请求的格式总结

post和put不写params,get要写

this.$http.post{'baidu',{ }}
this.$http.put{'baidu',{ }}

this.$http.get{'baidu',{params:{a:b}}

数据变

vue中数据变化,渲染表格的页面会自动变

错误必须要return出去,不能不处理

element中警告提示删除框,如果按了取消删除,要捕获错误的 …catch(err=>err)
捕获之后return出去

错误处理.catch(err=>err)

将错误return出去

可以return同时提示用户

if (res.meta.status !== 200)
        return this.$message.error("获取分类数据失败");

按我的思路这个return和这句也能写到一起,不清楚

handleChange(value) {
      if (this.addForm.goods_cat.length !== 3) {
        this.addForm.goods_cat = [];
        return;
      }
    }

res.data已经不是从服务器请求来的res.data

从服务器请求数据,之后可对这个数据修改,再存到data中

     // console.log(res.data);
      res.data.forEach(item => {
        item.attr_vals = item.attr_vals ? item.attr_vals.split(" ") : [];
        // 这个相当于把从服务器获取的数据修改后,还放到服务器请求的数据 的原数据里面
        item.inputVisible = false;
        item.inputValue = "";
        // 控制按钮的文本框切换显示与内容 是每个框子单独控制的
        //这就是把请求来的数据给改了,添加新属性了
      });
      this.paramsList = res.data;
      //把服务器请求的数据,做过以上修改之后,存到data中。
      //这是的res.data已经不是从服务器请求来的res.data了
      if (this.activeName == "many") {
        this.manyTableData = res.data;
        // console.log(this.manyTableData);
      } else {
        this.onlyTableData = res.data;
        // console.log(this.onlyTableData);
      }

表格中修改商品名称

表格中点了编辑按钮,首先会把原商品名反映在对话框中。
我们如何取出原商品名,并且展示在对话框中的呢,不是scope.row.good_name,即不要用渲染整个表格的那个旧数据。
而是把id传出去,根据id查询最新的参数名,然后获取的整个响应信息放在data的变量中,因为可能之后发编辑后的信息时会需要。

          <el-table-column label="操作" width="180">
              <template slot-scope="scope">
                <el-button type="primary" @click="showEditDialog(scope.row.attr_id)">编辑</el-button>
                <el-button type="danger" @click="removeParams(scope.row.attr_id)">删除</el-button>
              </template>
            </el-table-column>

三元表达式

item.attr_vals = item.attr_vals ? item.attr_vals.split(" ") : []
有点那种if(!item.attr_vals) 的感觉

这里如果item.attr_vals是空字符串,那么"".split(" “) 空字符串用空格分割,结果返回[” "]一个空字符串数组,这个空字符数组之后也会尝试渲染的,这是不对的,所以我们要把它变成空数组。所以借助三元表达式

v-if v-else

点按钮, e-tag消失,文本框展示
文本框失去焦点或者按enter(keyup.enter),文本框消失,el-tag展示
v-if v-else布尔值控制两者切换

v-model

v-model实现数据双向绑定,文本框的值绑定到v-model后面的变量上

获取焦点

              <el-input
                 v-if="scope.row.inputVisible"
                 v-model="scope.row.inputValue"
                 ref="saveTagInput"
                 @keyup.enter.native="handleInputConfirm(scope.row)"
                 @blur="handleInputConfirm(scope.row)"
               ></el-input>
               <el-button
                 v-else
                 @click="showInput(scope.row)"
               >+ New Tag</el-button>
               
showInput(row) {
     row.inputVisible = true;
     this.$nextTick(_ => {
       this.$refs.saveTagInput.$refs.input.focus();
     });
   }

$nextTick

$nextTick 方法的作用,当页面元素被重新渲染之后(以上点了显示input之后),才会调用回调函数的代码

比如你想让文本框获得焦点,就必须在页面重新渲染之后才可以执行这行代码。否则,你点击按钮时,文本框的显示被设为true,但是页面上文本框元素还没有渲染出来,这是没有获取到input

native

               <el-input
                  class="input-new-tag"
                  v-if="scope.row.inputVisible"
                  v-model="scope.row.inputValue"
                  ref="saveTagInput"
                  size="small"
                  @keyup.enter.native="handleInputConfirm(scope.row)"
                  @blur="handleInputConfirm(scope.row)"
                ></el-input>

数据获取-格式不行-格式修改-修改后直接存到item.good_attrs-再放到data-再展示

数据修改data-改data格式-发到服务器

从服务器获取参数,获取的是个字符串形式的,有空格,我们取到之后想要分开展示每条。先split分隔开,之后我们把分成数组的,添加到data中。

之后我们修改,修改data,但是data只是本地渲染的,服务器刷新添加不上,这时候还需要我们把我们添加的,合并之后发送到服务器请添加图片描述

不合法字体清除

if (row.inputValue.trim().length === 0) {
        row.inputValue = "";

根据索引删除数组中的一项元素

根据v-for的索引,从数组中把对应的那一项删除。同时通过scope把删除后变了的数组一并修改到福区群

               <el-tag
                  v-for="(item,i) in scope.row.attr_vals"
                  :key="i"
                  closable
                  @close="handleClose(i,scope.row)"
                >{{item}}</el-tag>

 handleClose(i, row) {
      row.attr_vals.splice(i, 1);
      this.saveAttrVals(row);
      这个函数就是将对attrVals的操作保存到数据库中 row row
 }

根据索引删除数组中的一项元素
splice会修改原数组的,这样row.attr_vals数组就少了一个元素,这时表格中绑定的变量那里也会少这个元素,之后服务器发起请求就行

total初值为0

v-for

数组 i 是索引,item是内容

<el-tag
                  v-for="(item,i) in scope.row.attr_vals"
                  :key="i"
                  closable
                  @close="handleClose(i,scope.row)"
                >{{item}}</el-tag>

console

想演示单独的代码,可以在控制台用console

表格中日期格式自定义处理 过滤器

从数据库取出来的数据是毫秒的形式,想显示标准格式。在main.js注册一个全局的过滤器,通过构造函数vue定义

Vue.filter('dataFormat', function (originaVal) {
 const dt = new Date(originaVal)

 const y = dt.getFullYear()
 const m = (dt.getMonth() + 1 + '').padStart(2, '0')
 const d = (dt.getDay() + '').padStart(2, '0')
 const h = (dt.getHours() + '').padStart(2, '0')
 const mm = (dt.getMinutes() + '').padStart(2, '0')
 const s = (dt.getSeconds() + '').padStart(2, '0')

 // return `yyyy-mm-dd hh:mm:ss`
 return `${y}-${m}-${d} ${h}:${mm}:${s}`
})

过滤器的调用挺搞笑的

<el-table-column prop="add_time" width="140px" label="创建时间">
  <template slot-scope="scope">
     {{scope.row.add_time | dataFormat}}
  </template>
</el-table-column>

请求参数时可以在data准备好

设置请求参数时可以在data中完全设好,之后请求时{params:this.queryInfo}

 data() {
    return {
      queryInfo: {
        query: "",
        pagenum: 1,
        pagesize: 10
      }

this.$confirm调用返回promise

单击添加 通过路由导航的形式跳转到添加商品页面

跳转到指定的路由页面

this.$router.push("/goods/add");

现在这个路由并没有被监听 我们也没有创建指定组件显示对应的界面

字符串转化成数字

想要将一个长得像数字的字符串转化为数字 ‘0’—>0 '1—>1
最简单的方式就是减0 active-0
:active=“active - 0”

需要字符串的地方和需要数字的地方,需要用到一个变量,最好把这个变量设置成字符串,之后那个可以通过:active="active - 0"转化为数字

表单和面板

一个商品的信息拆分成了五个面板,每个面板里面只维护当前商品的部分信息,只有将五个面板合起来才是完整的商品信息数据。所以,应该在这五个面板外面统一的用一个form进行包裹

好多嵌套关系是不可分割的

不能中间突然加一个其他的标签,比如

<el-tabs>
   <el-tab-pane>

设置间距margin及字体

发现设置间距margin一般直接通过组件,在element中,组件名就是类,直接用这个类。

而设置字体,如果在这个组件名类里设置不成功的话,可以去到控制台看看详细的类是什么,然后设置字体。

return null

如果没选中三级分类,就返回null

 computed: {
    cartId() {
      if (this.addForm.goods_cat === 3) return this.addForm.goods_cat[2];
      return null;
    }
  }

forEach 大写

v-for的item

item很厉害[{attr_name:’’,attr_vals:[]},{},{}] 你v-for的话,item的每一项就是{},他会自动搞三个el-form-item 每一项都差不多。下面再去遍历每一项的attr_vals数组里面的东西,根据个数渲染出多少个复选框

           <el-form-item v-for="item in manyTableData" :key="item.attr_id" :label="item.attr_name">
              <el-checkbox-group v-model="item.attr_vals">
                <el-checkbox
                  :label="itemCheck"
                  v-for="(itemCheck,i) in item.attr_vals"
                  :key="i"
                  border
                ></el-checkbox>
              </el-checkbox-group>
            </el-form-item>

属性important

/* margin: 0 !important; */
margin: 0 1px 0 0 !important;

if(){}else if(){}

图片上传地址

action是要上传的后台的地址

如果地址只写一个upload,代表说,你要把图片上传到8080这个服务器里面的goods/add/upload中 。
这样是不对的,因为我们后台服务器的接口不是8080,而是8888。

去main.js找到之前配置的请求的根路径,那个才是后台服务器的根路径http://127.0.0.1:8888/api/private/v1/。

之后在这个根路径后面补全上传图片的地址http://127.0.0.1:8888/api/private/v1/upload

当前页面网页 http://localhost:8080/goods/add
<el-upload action="upload">不对

<el-upload action="http://127.0.0.1:8888/api/private/v1/upload">

on-preview是处理图片预览效果的事件。在这个事件里面处理预览的业务逻辑。
功能:1、点上传图片按钮,会出现图片文件选择对话框,选中图片之后,会出现你选中的图片的缩略图及名字。
2、图片名称会弹出选中图片的预览窗口,你点击名称的时候会触发一个事件,就是on-preview事件,在这个事件中处理预览的业务逻辑。
3、点击叉号移除上传好的图片时,触发on-remove

token

除了登录接口之外,其他接口都是有权限的,都需要在调用这些接口的同时提供token。之前在main.js中通过axios.request拦截器,为咱们的请求头都挂载了Authorization字段,字段的值都是token。用axios发请求都自己加token。

token在sessionStorage中存着呢。 config.headers.Authorization = window.sessionStorage.getItem(‘token’)

el-upload发请求

el-upload没有用axios发ajax请求,所以el-upload请求出错,出错原因,没带token。
el-upload内部自己封装了一套ajax,没携带Authorization字段,所以报了无效token的错误。
我们要想办法为el-upload指定token。

图片上传

图片上传完成后,并不代表你的操作就结束了,只是代表服务器存储了这张图,但是图片相关信息还要存储到添加表单中才算完事。

图片上传参数

on-success 文件上传成功时的钩子 function(response, file, fileList)

:on-success=“handleSuccess”

handleSuccess(response) {}

需要用到的请求参数,都写到一个对象,到时候直接用

这个对象里面还存着表格用的参数

addForm: {
        goods_name: "",
        goods_price: 0,
        goods_weight: 0,
        goods_number: 0,
        goods_cat: [],
        pics: []
      }

服务器里的东西获取后,变成对象,再push进数组里

handleSuccess(response) {
      console.log(response);
      const picInfo = { pic: response.data.tmp_path };
      this.addForm.pics.push(picInfo);
      console.log(this.addForm);
    }

删除操作

1、获取将要删除图片的临时路径
2、从pics数组中,找到这个图片对应的索引值(根据临时路径获取索引)findIndex
3、调用数组的splice方法,把图片信息对象,从pics数组中移除

网页图片显示

只知道图片的网络路径,想让他显示在页面上,只需要

<img :src="previewPath" class="previewImg" />

想让图片刚好充满全屏,可以加个class类,之后给类加上样式,width:100%

注意注意这个src是动态的

安装依赖

  • -S,–save 安装包信息将加到dependencies(生产阶段的依赖)打包计算在内
    npm install --save 或
    npm install -S
  • -D, –save-dev 安装包信息将加到devDependencies(开发阶段的依赖)打包不计算在内
    npm install --save-dev
    npm install -D

vue.use(…)注册为全局可用的组件

min-height和height

.ql-editor{
min-height: 300px;
}

深拷贝

问题引出

这里需要的goods_cat是数组形式的,请注意这里是v-model,
一直一直双向绑定的,所以你想要把add时数据变成数组形式是不正常的
<el-cascader
                v-model="addForm.goods_cat"
                :options="cateList"
                :props="cateProps"
                @change="handleChange"
                clearable
></el-cascader>

而添加商品的button按钮这里需要的是字符串形式的数据,
下面这样肯定不对,你直接把数据改成字符串形式,上面怎么用
this.addForm.goods_cat=this.addForm.goods_cat.join(',)

一个希望这个数据是数组,一个希望是字符串,所以我们用深拷贝。在操作goods_cat之前,我们把addForm这个对象做一下深拷贝。

深拷贝指的是,把这个addForm对象,原封不动的拷贝出新的一份,和原对象之间是互不相干的。

在下面拼接之前,先把addForm深拷贝一份。

方法:用到包lodash,包上边提供一个很好用的方法cloneDeep(obj) obj是要拷贝的对象

join拼接的时候用的是复制的一项

 //将addForm进行深拷贝,避免goods_cat数组转换字符串之后导致级联选择器报错
    const form = _.cloneDeep(this.addForm)
    //将goods_cat从数组转换为"1,2,3"字符串形式
    form.goods_cat = form.goods_cat.join(",")

  1. 0-9a-zA-Z_.- ↩︎

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值