VUE项目-第十二天
01-反馈
姓名 意见或建议
*** Promise和async同为发送请求的异步操作是如何区分以及使用的?萌
*** 不知道axios.defaults.baseURL的值和接口文档中的接口基准地址是什么关系?
- Promise async await 处理异步操作
- 只是写法不一样
- Promise 在处理异步操作的时候 还是需要使用 then catch 还不是特别方便
- async await 方便我们在做异步操作的时候 写法可以是同步写法 逻辑清晰
- axios.defaults.baseURL
- baseURL 当前请求的接口地址在开头有很多重复的字符串 http://localhost:8888/v1/private/
- 如果没有跨域 /v1/private/
02-回顾
- 写代码
03-商品添加-组件基础布局
-
面包屑
-
卡片
-
警告条
-
步骤条
-
tab标签页
-
效果:切换tab的时候同时去切换步骤条
基本信息 商品参数 商品属性 商品图片 商品内容changeTab (tab) { // 根据当去的tab的位置 去切换步骤条的位置 // tab 当请点击的tab的实例对象 包含一些信息 // console.log(tab) tab.index 就是索引正好对应 步骤条的active数据 this.active = +tab.index }
data () { return { // 是当前步骤的索引 active: 0 } },
04-商品添加-表单准备及验证
表单的基础布局:
<el-form ref="form" :model="form" :rules="rules" label-width="200px" autocomplete="off">
<el-form-item label="商品名称" prop="goods_name">
<el-input v-model="form.goods_name" style="width: 400px"></el-input>
</el-form-item>
<el-form-item label="商品分类" prop="goods_cat">
<el-input v-model="form.goods_cat" style="width: 200px"></el-input>
</el-form-item>
<el-form-item label="商品价格" prop="goods_price">
<el-input v-model="form.goods_price" style="width: 200px"></el-input>
</el-form-item>
<el-form-item label="商品数量" prop="goods_number">
<el-input v-model="form.goods_number" style="width: 200px"></el-input>
</el-form-item>
<el-form-item label="商品重量" prop="goods_weight">
<el-input v-model="form.goods_weight" style="width: 200px"></el-input>
</el-form-item>
</el-form>
// 添加商品表单数据
form: {
goods_name: '',
goods_cat: '',
goods_price: '',
goods_number: '',
goods_weight: '',
goods_introduce: '',
pics: [],
attrs: []
},
rules: {
goods_name: [
{required: true, message: '商品名称必填', trigger: 'blur'}
],
goods_cat: [
{required: true, message: '商品分类必填', trigger: 'blur'}
],
goods_price: [
{required: true, message: '商品价格必填', trigger: 'blur'}
],
goods_number: [
{required: true, message: '商品数量必填', trigger: 'blur'}
],
goods_weight: [
{required: true, message: '商品重量必填', trigger: 'blur'}
]
}
实现分类级联功能:
<el-cascader
clearable
style="width: 200px"
expand-trigger="hover"
:options="categoryList"
v-model="categoryValues"
:props="{value:'cat_id',label:'cat_name'}"
@change="handleChange">
</el-cascader>
// 级联相关的数据
categoryList: [],
categoryValues: []
handleChange () {
},
async getData () {
// 获取三级分类数据 且赋值给级联组件
const {data: {data, meta}} = await this.$http.get('categories')
if (meta.status !== 200) return this.$message.error('获取分类数据失败')
this.categoryList = data
}
实现表单校验:
-
级联校验需要加上
-
改成 change 事件去触发校验 文字提示该了一下
-
因为校验的是 字段 form.goods_cat 而在选择级联的时候没有去修改这个字段
-
选择级联的时候去修改这个字段 怎么去修改???
-
思路:通过绑定事件去做 可以
-
使用的方式:通过侦听器去监听 categoryValues 字段值改变 去修改 form.goods_cat
watch: { categoryValues (now, old) { // 当 categoryValues 值改变的时候 // form.goods_cat 赋值 // 如果 categoryValues 的长度不等于3 就不赋值清空 if (now.length === 3) { // 以为','分割的分类列表 this.form.goods_cat = now.join(',') } else { this.form.goods_cat = '' // this.categoryValues = [] } } }
-
-
离开第一个选项卡就该 对整个表单进行校验
-
如果校验失败 阻止切换选项卡
-
如果成功 放行
changeTabBefore (activeName, oldActiveName) { console.log(activeName, oldActiveName) // 对整个表单进行校验 // 如果校验失败 阻止切换 // return false 即可阻止 必须在当前函数的作用域下有效 // return Promise 对象 执行 reject 阻止 if (oldActiveName === '0') { // 如果离开的是第一个选项卡 去做校验
return new Promise((resolve, reject) => { this.$refs.form.validate(valid => { if (valid) { // 校验成功 随着tab的索引去切步骤条 this.active = +activeName resolve() } else { // 阻止切换
reject(new Error('校验表单失败')) } }) }) } else { // 如果不是第一个选项 随着tab的索引去切步骤条 this.active = +activeName } }
-
05-商品添加-渲染参数及属性
-
参数及属性数据 需要请求两次去获取不同的属性列表数据
// id 当前选中的第三级分类的ID const {data: {data, meta}} = await this.
{id}/attributes`, { params: {sel: ‘many|only’} })
使用的代码:
async getParams (type) {
const id = this.categoryValues[2]
const {data: {data, meta}} = await this.$http.get(`categories/${id}/attributes`, {
params: {sel: type}
})
if (meta.status !== 200) return this.$message.error('获取参数数据失败')
this[type + 'Attrs'] = data
}
校验成功后使用:
// 获取第二个和第三个选项卡的数据
this.getParams('many')
this.getParams('only')
html渲染
<el-tab-pane label="商品参数">
<el-form label-width="200px">
<el-form-item v-if="item.attr_vals" v-for="(item,i) in manyAttrs" :key="i" :label="item.attr_name">
<el-tag v-for="(tag,i) in item.attr_vals.split(',')" :key="i">{{tag}}</el-tag>
</el-form-item>
</el-form>
</el-tab-pane>
<el-tab-pane label="商品属性">
<el-form label-width="200px">
<el-form-item v-if="item.attr_vals" v-for="(item,i) in onlyAttrs" :key="i" :label="item.attr_name">
<el-tag style="width: 300px">{{item.attr_vals}}</el-tag>
</el-form-item>
</el-form>
</el-tab-pane>
06-商品添加-完成图片上传
使用组件:
<el-upload
:on-success="handleSuccess"
:action="action"
:headers="headers"
list-type="picture-card"
:on-preview="handlePictureCardPreview"
:on-remove="handleRemove">
<i class="el-icon-plus"></i>
</el-upload>
action 是当前的上传地址 且 全路径
headers 请求的时候 不是通过axios 所以你需要添加token
// 上传图片
dialogImageUrl: '',
dialogVisible: false,
action: this.$http.defaults.baseURL + '/upload/',
headers: {
Authorization: sessionStorage.getItem('token')
}
// 上传成功后需要给 form.pics 数组追加图片
handleSuccess (res) {
// 图片地址? 在上传成功后获取响应数据 才有图片地址
this.form.pics.push({pic: res.data.tmp_path})
},
handleRemove (file, fileList) {
// 删除图片后台触发的事件
console.log(file, fileList)
// 移除 form.pics 中对应的图片对象
// 根据索引删除一条即可
// 在file中可以获取当前删除图片的临时路径
// 根据路径去 form.pics 获取对应的索引
const tmpPath = file.response.data.tmp_path
const index = this.form.pics.findIndex(item => item.pic === tmpPath)
this.form.pics.splice(index, 1)
},
handlePictureCardPreview (file) {
// 预览图片
this.dialogImageUrl = file.url
this.dialogVisible = true
},
07-商品添加-使用富文本插件
-
vue-quill-editor 安装
-
npm i vue-quill-editor
import VueQuillEditor from 'vue-quill-editor'
// require styles import 'quill/dist/quill.core.css' import 'quill/dist/quill.snow.css' import 'quill/dist/quill.bubble.css'
Vue.use(VueQuillEditor, /* { default global options } */)
组件:
<quill-editor v-model="form.goods_introduce"></quill-editor>
08-商品添加-商品的录入
- 准备数据
- 数据在 form 中 attrs 的数据没有准备
- 其他数据都可以
- 提交即可
- 成功:去列表页展示
09-订单列表-组件基础布局
10-订单列表-列表渲染
- 几乎copy
- 搜索的时候 接口有问题 请忽略
11-订单列表-收货地址对话框
- 实现省市区联动
- 准备 city.js 模块 包含数据
- 组件 导入 city 模块
- options 赋值
12-订单列表-物流查询对话框
<!--时间线-->
<el-timeline>
<el-timeline-item
v-for="(item, i) in wlList"
:key="i"
:timestamp="item.time">
{{item.context}}
</el-timeline-item>
</el-timeline>
数据:
wlList: [
{
'time': '2018-05-10 09:39:00',
'ftime': '2018-05-10 09:39:00',
'context': '已签收,感谢使用顺丰,期待再次为您服务',
'location': ''
},
{
'time': '2018-05-10 08:23:00',
'ftime': '2018-05-10 08:23:00',
'context': '[北京市]北京海淀育新小区营业点派件员 顺丰速运 95338正在为您派件',
'location': ''
},
{
'time': '2018-05-10 07:32:00',
'ftime': '2018-05-10 07:32:00',
'context': '快件到达 [北京海淀育新小区营业点]',
'location': ''
},
{
'time': '2018-05-10 02:03:00',
'ftime': '2018-05-10 02:03:00',
'context': '快件在[北京顺义集散中心]已装车,准备发往 [北京海淀育新小区营业点]',
'location': ''
},
{
'time': '2018-05-09 23:05:00',
'ftime': '2018-05-09 23:05:00',
'context': '快件到达 [北京顺义集散中心]',
'location': ''
},
{
'time': '2018-05-09 21:21:00',
'ftime': '2018-05-09 21:21:00',
'context': '快件在[北京宝胜营业点]已装车,准备发往 [北京顺义集散中心]',
'location': ''
},
{
'time': '2018-05-09 13:07:00',
'ftime': '2018-05-09 13:07:00',
'context': '顺丰速运 已收取快件',
'location': ''
},
{
'time': '2018-05-09 12:25:03',
'ftime': '2018-05-09 12:25:03',
'context': '卖家发货',
'location': ''
},
{
'time': '2018-05-09 12:22:24',
'ftime': '2018-05-09 12:22:24',
'context': '您的订单将由HLA(北京海淀区清河中街店)门店安排发货。',
'location': ''
},
{
'time': '2018-05-08 21:36:04',
'ftime': '2018-05-08 21:36:04',
'context': '商品已经下单',
'location': ''
}
]
13-数据报表-echarts使用
/* 1. 引入 echarts 插件 */
/* 2. 准备一个容器 */
/* 3. 实例化echarts对象 需要容器dom */
/* 4. 需要符合echarts规则的配置项 */
/* 5. 设置配置项给实例 */
async getData () {
const {data: {data, meta}} = await this.$http.get('reports/type/1')
if (meta.status !== 200) return this.$message.error('获取报表数据失败')
// 后台给的配置项 和 现在的options 进行合并 然后给图表使用
const myEcharts = echarts.init(this.$refs.box)
// Object.assign(o1,o2) 注意:如果有相同key 后面的生效
const options = {...this.options, ...data}
myEcharts.setOption(options)
}
14-项目打包
- 把项目开发是需要依赖的资源合并
- npm run build
- 是让webpack进行打包
- 在 dist 目录
- 发现:
- app.xxx.css 较大 (用自己的css 第三方的css)
- vendor.xxx.js 较大 第三方的依赖
- app.xxx.js 自己写的js代码 组件代码
- 发现:
15-项目打包优化-路由懒加载
- 按需加载组件
- 路由懒加载 按照现在当前的路由去加载需要的组件
-
const Foo = () => import('./Foo.vue')const Login = () => import('@/components/Login') const Home = () => import('@/components/home/Home') const Welcome = () => import('@/components/home/Welcome') const Users = () => import('@/components/users/Users') const Rights = () => import('@/components/auth/Rights') const Roles = () => import('@/components/auth/Roles') const Categories = () => import('@/components/goods/Categories') const Params = () => import('@/components/goods/Params') const Goods = () => import('@/components/goods/Goods') const GoodsAdd = () => import('@/components/goods/Goods-Add') const Orders = () => import('@/components/orders/Orders') const Reports = () => import('@/components/reports/Reports')
-
16-项目打包优化-cdn配置
- 把第三方的资源 使用 外链的方式来加载
- cdn 服务 提供前端的资源 通过地址就可以访问
- 第一步 找到 cnd 地址
- 第二步 在index页面的最底部
- 第三步 告诉webpack 那些模块不需要打包
- 是靠配置文件
- 加配置项 排除打包模块的配置
-
// 使用cdn的js包 externals: { // key (包的名称) value (js文件暴露在全局的对象名称) // 注意 导包后的 接受包的变量和 暴露在全局的对象名称 一致 'vue': 'Vue', 'element-ui': 'ELEMENT', 'echarts': 'echarts' }, module:{}
VUE扩展
17-组件传值-父向子
- 在子组件的自定义标签上写 动态属性 :data='数据'
- 在子组件中定义 props 的选项 [‘data’]
18-组件传值-子向父
- 自定义事件 概念
- 绑定事件 <child-a :say="msg" @toParent="fn">
- 触发事件 this.$emit('toParent', this.msg)
- 步骤
- 你需要在 子组件的自定义标签上绑定一个自定义事件。
- 如果需要传值给父组件 让子组件去触发这个事件 触发的同时是可以传参的。
19-组件传值-非父子关系
-
事件总结
- 统一控制事件的绑定和触发
- 这是一个模块 暴露vue实例的模块
-
在A组件 导入 事件总线模块 得到一个实例
- 用这实例 绑定 一个自定义事件
-
在B组件 导入 事件总线模块 得到一个实例
- 用这个实例 触发你在 A 绑定的自定义事件
-
然后通过事件的回调函数可以传参
// 事件总线 // 1. 依赖一个VUE实例 import Vue from 'vue' const vm = new Vue() export default vm // 2. 在两个组件中导入这个实例 // 3. 绑定事件 // 4. 在实例中绑定的事件 只有当前实例可以去触发
20-vuex基础-介绍
当我们的应用遇到多个组件共享状态时,单向数据流的简洁性很容易被破坏。
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。
- Vuex 可以帮助我们管理共享状态,并附带了更多的概念和框架。
- 这需要对短期和长期效益进行权衡。
- 如果您不打算开发大型单页应用,使用 Vuex 可能是繁琐冗余的。
- 确实是如此——如果您的应用够简单,您最好不要使用 Vuex。
总结:项目复杂使用vuex可以简化逻辑,但是如果项目简单使用vuex则会增加逻辑。
结论:
- Actions 发送请求 响应成功 把数据提交给 Mutations
- Mutations 接收到数据后 去更新State中的数据
- State管理的数据是响应式的 当数据改变时渲染视图