- 需要引入body-parser
// express 不能解析post请求体,需要安装中间件 body-parser
var bodyParser = require('body-parser')// parse application/x-www-form-urlencoded (表单传输)
app.use(bodyParser.urlencoded({ extended: false }))
// parse application/json (json传输)
app.use(bodyParser.json())
然后才能用request.body解析
- 拦截到的response是一个他包好的对象
- 有xhr请求自带的状态码,在响应拦截器中处理掉这一块,给他脱一层,把response.data传下去
- response.data是请求获取到的数据,这里面往往后端人还会加一个code,如果拿到这个code,说明请求已经到后端了,看后端向怎么处理
this.$API.trademark.delete
this.$API.attr.delete
xport {default as trademark} from './product/trademark'
入并暴露
以这么理解
一步:引入
import {default as trademark} from './product/trademark'
引入之后 就把这个对象引过来,并且给这个对象赋值给trademark
trademark = {
getPageList(){},
delete(){},
addOrUpdate
}
二步:并暴露(部分暴露)
export trademark = {
getPageList(){},
delete(){},
addOrUpdate
}
export attr = {
// getPageList(){},
// delete(){},
// addOrUpdate
}
最终从index.js暴露出去的就是
{
trademark,
attr
}
main里面就可以 import * as $API from '@/api' 拿到暴露出去的整个对象
$API = {
trademark,
attr
}
index.js其实就把不同的模块当中接口请求函数整合到同一个文件进行一次性暴露,这样我们就可以直接
获取到所有的接口请求函数,进行统一的处理
<!--
table写法
先写eltable 回车
有几列就复制几个列,复制几个el-table-column
先把动态数据属性data干掉
有边框得添加table的border属性
每个列的width控制列的宽度
每个列的label控制这个列的名称
想让那个列居中,那么哪个列需要添加align="center"
那个列是序号,需要添加type = "index"
-->
<el-table border :data="trademarkList" style="width: 100%; margin: 20px 0">
<!-- data是要展示的动态数据,必须是一个数组
table当中展示数据的时候,每个列内部都暗含了一个vfor,都在遍历data的数组当中的每个品牌对象
每一个列内部都有展示数据的功能,只不过展示的是数据的哪个属性,我们可以通过prop去告知
没个列展示数据的时候都会给你留有作用域插槽,那么如果你需要修改展示时候的结构,那么你就得去完善作用域插槽
如果不需要修改展示数据的结构,就不需要关心作用域插槽
-->
<!-- 序号这一列type成index,才能自动添加序号 -->
<!-- prop是跟和data联系起来的,显示的数据字段。data是列表,prop是列表中每一项对象的属性名 -->
<el-table-column
type="index"
prop="prop"
label="序号"
align="center"
width="80"
>
</el-table-column>
<el-table-column label="品牌名称" width="width" prop="tmName">
<!-- width不写数值表示等分 -->
</el-table-column>
<el-table-column label="品牌logo" width="width" prop="logoUrl">
<!-- 要修改子组件的结构,所以要用作用域插槽 -->
<!-- <template slot-scope="{ row, $index }"> row是data中被遍历数组的每一项,$index是下标-->
<template slot-scope="{ row }">
<img :src="row.logoUrl" alt="#" style="width: 80px; height: 60px" />
</template>
</el-table-column>
<el-table-column label="操作" width="width">
<!-- <template slot-scope="{row, $index}"> 既然不需要从子里面拿每一条的数据,就不用取出来,省的报警告-->
<template>
<el-button type="warning" icon="el-icon-edit" size="mini"
>修改</el-button
>
<el-button type="danger" icon="el-icon-delete" size="mini"
>删除</el-button
>
</template>
</el-table-column>
</el-table>
- Multer 会添加一个 body 对象 以及 file 或 files 对象 到 express 的 request 对象中。
- body 对象包含表单的文本域信息,file 或 files 对象包含对象表单上传的文件信息。
const multer = require('multer'); // 引入处理文件上传的中间件
var storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, './assets/images/trademarklogo/') //这里是图片存储路径,注意啊这里最后的那个/
},
filFilter: function (req, file, cb) {
var typeArray = file.mimetype.split('/');
var fileType = typeArray[1];
if (fileType == 'jpg' || fileType == 'png') {
cb(null, true);
} else {
cb(null, false)
}
},
//fieldname 为文件域的名称
/*
filename 用于确定文件夹中的文件名的确定。 如果没有设置 filename,每个文件将设置为一个随机文件名,并且是没有扩展名的。
注意: Multer 不会为你添加任何扩展名,你的程序应该返回一个完整的文件名。
每个函数都传递了请求对象 (req) 和一些关于这个文件的信息 (file),有助于你的决定。
这里的file.filename是保存在 destination 中的文件名
*/
filename: function (req, file, cb) {
// 获取被上传文件的扩展名
var nameArray = file.originalname.split('.');
var type = nameArray[nameArray.length-1];
// 设置回调的内容,参数1:错误信息,参数2:图片新的名字
cb(null, file.fieldname + '_' + Date.now() + '.'+type)
}
})
// 使用storage引擎
var upload = multer({
storage: storage
});
路由
// 用户上传品牌logo,发送到服务器,然后服务器存下来,并把实际路径返回给前端
// 接受一个以 fieldname 命名的文件。这个文件的信息保存在 request.file
// sigle上传单个文件,file是前端上传图像的input标签的name值
app.post('/api/admin/product/fileUpload', upload.single('file'), (request, response) => {
console.log('upload');
// 这里的request.file.filaname是保存下来的文件的全名
var url = `http://localhost:8000/images/trademarklogo/${request.file.filename}`
response.send({
"code": 200,
"logoUrl": url
})
})
<!--
dialogFormVisible是决定对话框显示和隐藏
:model="from",model=对象 指定收集的数据最终放在哪 表单收集的数据在from对象中,在data中创一个对象from{}
form-item当中 是收集数据的每一项 每一项都可以有label指定这一项名称,label-width="100px"指定名称宽度
每一项当中都可以对应收集数据,收集的数据一般都是放在我们指定的对象当中
-->
<el-dialog
:title="toForm.id ? '修改品牌' : '添加品牌'"
:visible.sync="dialogFormVisible"
>
<el-form :model="toForm" :rules="rules" ref="ruleForm" style="width: 80%">
<el-form-item label="品牌名称" label-width="100px" prop="tmName">
<el-input v-model="toForm.tmName" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="品牌Logo" label-width="100px" prop="logoUrl">
<!-- upload里面的action就是让你自己去写真实的上传接口,对应上传到哪里 -->
<!-- show-file-list指定上传的是不是照片墙,现在上传的是一张不是照片墙 -->
<!-- :before-upload是上传前的回调,这里用来验证上传文件的格式是否符合要求 -->
<!-- :on-success是上传成功后的回调,这里用来把请求回来的数据做显示或其他处理 -->
<el-upload
class="avatar-uploader"
action="/api/admin/product/fileUpload"
:show-file-list="false"
:on-success="handleAvatarSuccess"
:before-upload="beforeAvatarUpload"
>
<img v-if="toForm.logoUrl" :src="toForm.logoUrl" class="avatar" />
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
<div class="el-upload__tip" slot="tip">
只能上传jpg/png文件,且不超过500kb
</div>
</el-upload>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="dialogFormVisible = false">取 消</el-button>
<el-button type="primary" @click="addOrUpdate">确 定</el-button>
</div>
</el-dialog>
- 添加rules属性,ref属性
rules是要被效验的规则
rules是一个对象,每一个属性也是对象,属性名是被效验的数据名,对象的内容是改数据的规则
可自定义规则
ref是在效验时,找到这一块东西
let validateTmName = (rule, value, callback) => { //value就是你要校验的数据 //callback代表校验成功还是失败的回调 //如果传递了一个错误对象,那么就代表验证失败 //如果没有传递任何参数就代表验证成功 if (value.length < 2 || value.length > 10) { callback(new Error("长度必须是2到10之间")); } else { callback(); } }; // 表单验证对象 rules: { //这个对象代表是表单验证的规则对象 //每个字段都是一个数组,数组里面放的就是我们验证的规则对象,几个规则就是几个对象 //每个规则对象都可以规定规则名称,消息名称 和 触发时机 //触发时机 有三种情况:失去焦点的时候 输入框改变的时候 整体校验的时候 tmName: [ { required: true, message: "请输入活动名称", trigger: "blur" }, // 必须,错误提示时候'请输入活动名称',触发时机是失去焦点后 // 自定义校验规则 { validator: validateTmName, trigger: "change" }, // { // min: 3, // max: 10, // message: "长度在 3 到 10 个字符", // trigger: "blur", // }, ], // 这个触发写什么都无所谓,upload触发时机必须是整体校验才会触发 logoUrl: [ { required: true, message: "请选择上传图片", trigger: "change" }, ], },
this.$refs.ruleForm.validate(async (valid) => { if (valid) { let trademark = this.toForm; try { const result = await this.$api.trademark.addOrUpdate(trademark);
if (result.code === 200) {
// 如果成功,提示成功,然后要跳到哪一页呢?
this.$message.success(
trademark.id ? "修改品牌成功" : "添加品牌成功"
);
// 返回列表页
this.dialogFormVisible = false;
// 如果是添加的就跳到第一页,修改的就跳到本页
this.getTrademarkList(trademark.id ? this.page : 1);
}
} catch {
// 失败干啥
this.$message.success(toForm.id ? "修改品牌失败" : "添加品牌失败");
}
} else{
console.log('校验失败不提交');
return false; 返回false既是不通过
}
});
},
### this.$confirm是基于Promise的
* .then处理成确定的回调
* .catch是处理取消的回调
### 注意请求中的是数字还是字符串
### Array.prototype.splice(star,num,add)
* 如果只有两个参数,代表从star开始删错num个
* 如果第二个参数为0,有第三个、四个、五个参数,那就从index开始插入第三个、四个、五个参数。arr[star]位置上是第三个然后依次...
* 后台中添加的id不要用length决定,如果修改了数组,length就变了,用Data.now()比较好
### el-upload中
list-type="picture-card" 开启照片墙
:file-list="spuImageList" 被遍历的数组
这里被遍历的数组的项中必须包含name,id
```js
spuImageList.forEach((item) => {
item.name = item.imgName;
item.url = item.imgUrl;
});
所有的销售属性除去自身的,剩余的销售属性列表
unUsedSpuSaleAttrList(){
//从所有的销售属性列表当中去过滤,过滤出销售属性名称和自己的销售属性列表当中每个销售属性名称都不相同的
return this.baseSaleAttrList.filter(baseSaleAttr => {
//从baseSaleAttrList拿一项,就要去和自己已有的数组每个去对比,如果都不相等,就拿走,有相等就不要
return this.spuForm.spuSaleAttrList.every(spuSaleAttr => {
return baseSaleAttr.name !== spuSaleAttr.saleAttrName
})
})
},
closable表示可以删除
@close绑定删除的回调函数
<el-tag
v-for="(saleValue, index) in row.spuSaleAttrValueList"
:key="saleValue.id"
closable
@close="row.spuSaleAttrValueList.splice(index, 1)"
>
{{ saleValue.saleAttrvalueName }}
</el-tag>
<el-popconfirm title="这是一段内容确定删除吗?">
<el-button slot="reference">删除</el-button>
</el-popconfirm>
title是点击时弹出的文本
slot="reference"必须加
onConfirm才是确认的回调,官网上是错的
- 因为在spu的子组件spuForm中书写新增与修改
- 什么时候需要判断它是增加还是修改然后再操作呢?
- 因本页面中有分页器,所以那边保存跳回父组件这里时,分页器展示哪一页?
- 因为对添加与修改按钮父亲这里有不同的回调,在修改按钮的回调中,在this加flag
- 因为分页器页码操作在父亲这里,所以在this加flag
- 怎么判断跳转回来了?这里是组件,所以在跳回来之前,子spuForm那里可以利用自定义事件调用父这里的方法
这个initialValue
默认值可以是数组类型,比如
- 要注意很重要的一点,prop必须与data中的需要验证的数据名字一样
- 注意v-model要收集的数据类型,比如v-model.number
-
scoped不写,那么当前组件的样式会影响其它组件
-
scoped写上,把样式作用在当前组件内部及子组件的根元素身上
-
scoped如何把样式作用在本组件和子组件根元素身上 加了scoped就会有唯一的一个标识值,而这个标识数据会作为被影响到的元素的属性 这个元素的样式会在选择器的最右侧添加这个属性选择器。
不加scoped
h2{ color:hotpink } 加了scoped h2[data-v-6c1c67aa]{ color:hotpink } -
scoped中有些元素直接添加样式就会生效,而有些元素直接添加样式就不会生效? 一句话:scoped只能把样式作用延长到自身元素还有子组件的根元素身上 如果在scoped书写的样式,刚好是作用在子组件的根元素身上,就会生效 如果在scoped书写的样式,不是作用在子组件根元素而是子组件根元素内部元素身上,就不会生效
-
加了scoped,还想让子组件根元素内部元素的样式生效(使用深度作用选择器) 1、把子组件内部元素的样式重新写一个style写,不加scoped,用的不多 2、深度作用选择器的写法 如果是原生css 深度作用选择器
父元素 >>> 选中的元素 如果是less scss 预编译的css文件 /deep/ 用于less ::v-deep 都行 -
添加深度作用选择器css怎么处理的 不加添加深度作用选择器的时候,scoped的唯一标识会作为属性选择器添加在css选择器最右侧选中的元素身上,去限制 添加了深度作用选择器的时候,scoped的唯一标识会作为属性选择器添加在css选择器最左侧元素身上,限制不了选中的元素
- 在mounted里面初始化
- 内置的有多种主题,在theme文件夹下
- 在页面宽度变化时,图表重新加载
- 在汉堡包变化时,图表重新加载
Vue脚手架中,$el指向当前组件template模板中的根标签,注意什么时候根标签才被解析成DOM元素
- 请求用户信息时就有一个属性routes,里面携带此用户可访问的路由组件
- 路由组件划分三类,常量路由(任何用户都能访问)、异步路由(根据用户的权限确定哪些被访问)、任意路由(用户随意输入的非法路径,被转到404)
- 要在所有的异步路由中根据用户信息携带的routes过滤可以被访问的组件然后注册
- 这里具体的操作是在vuex里,创建了一个mutations以及几个states,过滤出来的路由组件不是字符串,是真正的路由(对象)
- 然后动态给路由器添加路由
router.addRoutes([...asyncRoutes, anyRoute])
getPageList(page, limit, searchObj) {
return request({
url: `${api_name}/${page}/${limit}`,
method: 'get',
params: searchObj // url查询字符串或表单键值对
})
},
- params属性表示query参数众所周知,对象也可以直接丢进去,因为他是键值对
- 类似params:{roleName:'dadada'}
{
path: '/acl',
name: 'Acl',
component: Layout, // 组件是一级路由Layout,Layout里面的AppMain组件里面才是这里写的二级路由
redirect: '/acl/user/list',
meta: { title: '权限管理', icon: 'password' },
children: [
{
path: '/user/list', // 带/时 浏览器上的地址中直接就是/#/user/list
name: 'User',
component: () => import("@/views/acl/user/List.vue"),
meta: { title: '用户管理' }
},
{
path: 'role/list', // 不带/ 浏览器上的地址栏中直接就是/#/acl/role/list
name: 'Role',
component: () => import("@/views/acl/role/List.vue"),
meta: { title: '角色管理' },
},
]
最上面的path带/,他的子不带/
<el-tree
:props="props"
:data="allPermissions"
node-key="id" 每个树节点用来作为唯一标识的属性,整棵树应该是唯一的
show-checkbox
@check-change="handleCheckChange"
ref="tree"
>
</el-tree>
``
```js
props指定了将要解析成树结构的数据中的配置,label指定要显示的文本从data的label属性中拿,children指的是data结构中的子树名字
props: {
label: "label",
children: "children",
},
this.$refs.tree.setCheckedKeys(checkedIds) 通过 keys 设置目前勾选的节点,使用此方法必须设置 node-key 属性
this.$refs.tree.getCheckedKeys() 若节点可被选择(即 show-checkbox 为 true),则返回目前被选中的节点的 key 所组成的数组
window.location.reload()