万民优选项目
一、git
1、简易流程
- git status 查看状态
- git add . 添加
- git commit -m “消息” 提交
- git pull origin develop 拉代码 —解决冲突
- 解决完冲突后重复2、3
- git push origin HEAD:分支名(yyDevelop)提交自己的代码到远程分支
- 提pr,提交之前看一下自己的文件改动是否正确
- git stash save “信息” 放在缓存区
- git stash apply 释放缓存后,不会删掉stash 把缓存区东西和拉下来的代码进行合并,解决冲突
- git stash pop 释放暂存后,会删掉stash
- git push origin -d 分支名 删除远程分支
2、warning: remote HEAD refers to nonexistent ref, unable to checkout.问题解决
当我git clone ssh地址时出现warning: remote HEAD refers to nonexistent ref, unable to checkout
解决方式:
- 在.git文件夹Git Bash Here
- 查看分支 git branch -a
- 切换分支即可 git checkout develop
3、Merge branch ‘develop’ of e.coding.net:marchsoft/wanminyouxue/wmyx-admin into develop # Please enter a commit message to explain why this merge is necessary,
# especially if it merges an updated upstream into a topic branch. # # Lines starting with ‘#’
- 只需 shift + ! 输入 wq
- 问题的意思是:请解释为什么合并代码 shift + !(进入编辑状态) wq (保存退出)
二、vue
1、状态(报错码)
401 token值–拦截器 没有权限
500 网络不好、服务器异常(后端)
404 路径和文件名的匹配问题
2、上传图片
<template>
<el-upload
ref="upload"
class="upload-demo"
action="/api/resource"
:auto-upload="true"
multiple
:file-list="form.resourceList"
:limit="9"
:on-remove="handleRemove"
:on-exceed="handleExceed"
:http-request="handleFileSuccess"
>
<el-button
slot="trigger"
size="small"
type="primary"
>选取图片</el-button>
</el-upload>
</template>
<script>
import { compressImage } from '@/utils/compress' // 图片压缩方法
import { upload } from '@/utils/upload.js'
export default {
methods: {
// 删除图片
handleRemove(file) {
// console.log(file, 'file--remove')
// console.log(this.form.resourceList, 'this.form.resourceList')
this.form.deleteIds.push(file.id)
for (let i = 0; i < this.form.resourceList.length; i++) {
if (this.form.resourceList[i].uid === file.uid) {
this.form.resourceList.splice(i, 1)
}
}
},
// 文件个数超出指定个数
handleExceed() {
this.$message.warning('最多选择9个图片')
},
// 上传图片
handleFileSuccess(file) {
// console.log(file, 'file')
const config = {
width: 100, // 压缩后图片的宽
height: 100, // 压缩后图片的高
quality: 0.6 // 压缩后图片的清晰度,取值0-1,值越小,所绘制出的图像越模糊
}
compressImage(file.file, config).then((result) => {
// console.log(result,"11")
// result 为压缩后二进制文件
upload('/api/resource', result)
.then(res => {
this.form.resourceList.push(res.data.data)
// console.log(this.form.resourceList, 'resourceList')
this.$message.success('上传图片成功')
})
.catch((err) => {
this.$message.error('上传图片失败')
console.log('返回错误', err)
})
})
}
}
}
</script>
<style lang="scss" scoped>
//解决图片上传闪动问题
::v-deep .el-upload-list__item {
transition: none !important;
}
</style>
-
el-upload的属性:
- action 请求路径
- auto-upload=“true” 自动上传
- multiple 支持多选图片
- file-list 获取数据
- limit 限制最多几张图片
-
el-upload的方法:
- on-remove 删除图片相应方法
- on-exceed 超过限制图片相应方法
- http-request上传图片相应方法
-
上传图片的方法
- handleRemove 删除图片 把删除的图片id放入到deleteIds数组里,找到删除图片的uid,把它从form.resourceList删除
- handleExceed 图片超过limit设定的个数时,进行提醒“最多选择9个图片”
- handleFileSuccess 上传图片
- config设置了压缩的图片的宽、高、清晰度
- 调用压缩图片方法
compressImage
,传入参数图片的信息file.file和压缩图片条件config - 产生的result是压缩后的图片文件
- 调用上传图片的方法upload,把result作为参数传入
- 把接口/api/resource传给的res.data.data放入form.resourceList数组里
-
图片上传闪动问题 加一个css样式
::v-deep .el-upload-list__item { transition: none !important; }
3、父子组件传值
-
父传子
父组件:
<template> <eForm :cate-options="cateOptions" /> </template> <script> import eForm from './module/form' export default { data() { return { cateOptions: [] } }, methods: { // 获取商品分类 getCate() { this.cateOptions = [{id:1,tname:"wuwu"},{id:2,tname:"yuyu"}] }, }, created(){ this.getCate() } } </script>
子组件
<template> <el-select v-model="form.cateId" placeholder="请选择" style="width: 120px" > <el-option v-for="item in cateOptions" :key="item.id" :label="item.name" :value="item.id" /> </el-select> </template> <script> export default { props: { cateOptions: { type: Array, required: true } } } </script>
- 父组件要传给子组件的值cateOptions,在data中声明,在子组件中赋值:
<eForm :cate-options="cateOptions" />
- 在子组件中,用props接收cateOptions。然后就可以在子组件中用cateOptions
- 父组件要传给子组件的值cateOptions,在data中声明,在子组件中赋值:
-
子传父
-
$emit
子组件
子组件: <span>{{childValue}}</span> <!-- 定义一个子组件传值的方法 --> <input type="button" value="点击触发" @click="childClick"> export default { data () { return { childValue: '我是子组件的数据' } }, methods: { childClick () { this.$emit('childByValue', this.childValue) } } }
父组件
<!-- 引入子组件 定义一个on的方法监听子组件的状态--> <child v-on:childByValue="childByValue"></child> methods: { childByValue: function (childValue) { // childValue就是子组件传过来的值 this.name = childValue } } }
-
父组件调用子组件的方法通过ref
在DOM元素上使用 r e f s 可 以 迅 速 进 行 d o m 定 位 , 类 似 于 refs可以迅速进行dom定位,类似于 refs可以迅速进行dom定位,类似于(“selectId”)
使用this.$refs.paramsName能更快的获取操作子组件属性值或函数
//子组件: methods:{ childMethods() { alert("I am child's methods") } }
//父组件: 在子组件中加上ref即可通过this.$refs.method调用 <template> <div @click="parentMethod"> <children ref="c1"></children> </div> </template> <script> import children from 'components/children/children.vue' export default { data(){ return { } }, computed: { }, components: { 'children': children }, methods:{ parentMethod() { console.log(this.$refs.c1) //返回的是一个vue对象,可以看到所有添加ref属性的元素 this.$refs.c1.childMethods(); } }, created(){ } } </script>
可以通过
$parent和$children
获取父子组件的参数
我们可以使用$children[i].paramsName
来获取某个子组件的属性值或函数,$children
返回的是一个子组件数组父组件
<template> <div class="parent"> <children1></children1> <children2></children2> <button @click="getChildren()">使用$children来获取子组件的数据</button> <p>这是从children1获取到的数据:{{msgFromChild1}}</p> <p>这是从children2获取到的数据:{{msgFromChild2}}</p> </div> </template> <script> import children1 from "./children1.vue" import children2 from "./children2.vue" export default { name:"parent", components:{ children1, children2 }, data(){ return{ msgFromChild1:"", msgFromChild2:"" } }, methods:{ getChildren(){ this.msgFromChild1=this.$children[0].childMsg//获取第一个子组件(children1)里的childMsg this.msgFromChild2=this.$children[1].childMsg//获取第二个子组件(children2)里的childMsg } } } </script> <style> </style>
子组件(children1)
<template> <div class="children1"> children1 </div> </template> <script> export default { name:"children1", data(){ return{ childMsg:"我是children1" } } } </script> <style> </style>
子组件(children2)
<template> <div class="children2"> children2 </div> </template> <script> export default { name:"children2", data(){ return{ childMsg:"我是children2" } } } </script> <style> </style>
效果:
$parent
父组件:
<template> <div> 子组件:<children/> </div> </template> <script> import children from "./children.vue" export default { components:{children}, data(){ return{ parentMsg:"我是父组件的信息" } } } </script> <style> </style>
子组件:
<template> <div> <button @click="getParent()">通过$parent获取父组件信息</button> <p>{{msg}}</p> </div> </template> <script> export default { data(){ return{ msg:"" } }, methods:{ getParent(){ this.msg=this.$parent.parentMsg } } } </script> <style> </style>
效果:
-
-
vue 全局事件(eventBus)
(1)在main.js里:
window.eventBus = new Vue();//注册全局事件对象
(2)一个组件
methods:{ down(name){ eventBus.$emit("eventBusName",name) } }
(3)另一个组件
eventBus.$on("eventBusName",function(data){ console.log(data,'data00000000000000') })
- 兄弟间传值(用vuex)
4、Web Storage API的介绍和使用
1、介绍
web Storage为浏览器提供了方便的key value存储,是一种比cookie更加方便简洁的存储方式。
2、浏览器的本地存储技术
Web Storage有两种存储方式:
-
sessionStorage:
- 对于每一个访问源,都会维持一个独立的存储区域。
- 只要浏览器不关闭,这些数据都不会消失。所以这种存储叫做session存储。
- 注意,这里的session和服务器端的session的意思是不一样的,这里的sessionStorage只是本地的存储,并不会将数据传输到服务器端。
- sessionStorage的存储容量要比cookie大得多,可以达到5MB。
-
localStorage:
- 和sessionStorage类似,也是用来做数据存储的,不同的是localStorage存储的数据不会随着浏览器的关闭而消失。
- 我们可以通过设置过期时间,使用javascript手动删除或者清除浏览器缓存来清除localStorage。
-
这两种存储方式是通过Window.sessionStorage 和 Window.localStorage来使用的。事实上我们看下Window的定义:
interface Window extends EventTarget, AnimationFrameProvider, GlobalEventHandlers, WindowEventHandlers, WindowLocalStorage, WindowOrWorkerGlobalScope, WindowSessionStorage
- window继承 WindowLocalStorage和WindowSessionStorage ,所以我们可以直接从Window来获取sessionStorage和localStorage。
- 对于每一个origin源来说,Window.sessionStorage 和 Window.localStorage 都会创建一个新的Storage对象,用来进行数据的读取。
-
存储token,我们用的是localStorage,存储用户信息,我们用的是sessionStorage.
3、Web Storage相关接口
-
window
- 我们可以通过window获取sessionStorage和localStorage
-
storage对象
-
interface Storage { readonly length: number; clear(): void; getItem(key: string): string | null; key(index: number): string | null; removeItem(key: string): void; setItem(key: string, value: string): void; [name: string]: any; }
-
-
StorageEvent
- 当storage发现变化的时候就会触发StorageEvent事件
-
判断Storage是否被浏览器有效的支持
function storageAvailable(type) { var storage; try { storage = window[type]; var x = '__storage_test__'; storage.setItem(x, x); storage.removeItem(x); return true; } catch(e) { return e instanceof DOMException && ( // everything except Firefox e.code === 22 || // Firefox e.code === 1014 || // test name field too, because code might not be present // everything except Firefox e.name === 'QuotaExceededError' || // Firefox e.name === 'NS_ERROR_DOM_QUOTA_REACHED') && // acknowledge QuotaExceededError only if there's something already stored (storage && storage.length !== 0); } }
其中的type就是localStorage或者sessionStorage,我们通过捕获异常来判断是否存在有效的Storge对象。
看下我们怎么使用:
if (storageAvailable('localStorage')) { // Yippee! We can use localStorage awesomeness } else { // Too bad, no localStorage for us }
4、隐身模式
1. 概念:隐身模式下,将不会存储浏览历史记录和Cookie等数据隐私选项。与Web Storage是不兼容的。
2. 解决方法:
- 隐身模式下Web Storage虽然是可用的,但是不会存储任何东西。
- 有些浏览器会选择在浏览器关闭的时候清空之前的所有存储
3. 我们在开发的过程中,一定要注意不同浏览器的不同处理方式
5、使用Web Storage API
1. Storage.getItem() 访问属性
2. Storage.setItem() 设置属性
注意Storage对象中的key value都是string类型的,即使你输入的是integer,也会被转换成为String。
下面的例子,都可以设置一个colorSetting属性:
localStorage.colorSetting = '#a4509b';
localStorage['colorSetting'] = '#a4509b';
localStorage.setItem('colorSetting', '#a4509b');
虽然三种方式都可以实现存取的功能,但是我们推荐使用Web Storage API:setItem, getItem, removeItem, key, length等。
除了对Storage中的值进行设置之外,我们还可以触发和监听StorageEvent。
先看一下StorageEvent的定义:
interface StorageEvent extends Event {
readonly key: string | null;
readonly newValue: string | null;
readonly oldValue: string | null;
readonly storageArea: Storage | null;
readonly url: string;
}
每当Storage对象发送变化的时候,就出触发StorageEvent事件。
注意,如果是sessionStorage的变化,则不会被触发。
如果一个domain中的某个页面发生了Storage的变化,那么这个domain的其他页面都会监听到这个变化。当然,如果是其他domain的话,是监听不到这个StorageEvent的。
我们可以通过window的addEventListener方法,来添加storage事件,如下所示:
window.addEventListener('storage', function(e) {
document.querySelector('.my-key').textContent = e.key;
document.querySelector('.my-old').textContent = e.oldValue;
document.querySelector('.my-new').textContent = e.newValue;
document.querySelector('.my-url').textContent = e.url;
document.querySelector('.my-storage').textContent = JSON.stringify(e.storageArea);
});
上面的例子中,我们从StorageEvent中获取了key,oldValue,newValue,url和Storage对象。
小程序:
- 获取存于storage里的数据:
uni.getStorageSync('字段名字')
- 把数据存在storage里:
uni.setStorageSync('字段名字')
- 清除本地缓存的数据:
uni.removeStorageSync('字段名字')
5、轮播图片显示第几张
<template>
<swiper @change="changeImg">
<swiper-item v-for="(item,index) in detailList.resourceList" :key="item.id">
<urlImage :url="item.path" class="top_pic"></urlImage>
</swiper-item>
</swiper>
<span class="page" v-if="detailList.resourceList.length>1">
<custom-text slot="body" class="slot-text imgPage" :content="currentImg+1 + '/' + detailList.resourceList.length"></custom-text>
</span>
</template>
<script>
export default {
data() {
return {
detailList:{},
currentImg: 0
}
},
methods:{
//通过id获取商品详情列表
async getGoodsList(id){
// 通过id获取商品详情列表
details(id).then(res => {
// console.log(res,"detail")
this.detailList=res
this.flag=true
}).catch(err => {
console.log('返回错误', err)
})
},
//滑动图片时获取对当前图片页数
changeImg(e) {
this.currentImg = e.detail.current
}
},
onLoad (option) {
//路由跳转拿到活动详情的id
this.getGoodsList(option.goodId)
}
}
</script>
<style lang="scss" scoped>
swiper{
width: 750rpx;
height: 560rpx;
opacity: 1;
.top_pic /deep/ .images{
width: 750rpx;
height: 560rpx;
}
}
.page{
z-index:100;
width:100rpx;
height:50rpx;
line-height:50rpx;
text-align:center;
float:right;
position:relative;
right:0rpx;
bottom:53rpx;
background-color: rgba(28, 28, 28, 0.8);
border-radius:20px;
.imgPage{
color:#e9e9e9;
font-weight: 400;
}
}
</style>
显示结果:
三、CSS
1、换行、不换行
white-space:nowrap;
//强制不换行word-wrap: break-word;
//换行
2、超出部分用引号表示
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
四、总结和反思
这是我第二次做项目,主要做的是后台,前台就写了一个页面(商品详情)。
1.这次的后台是基于SMPE框架写成的,使用框架有优点,也有缺点。
- SMPE框架用的是比较方便,后台关于样式的东西不大多,用的框架直接复制粘贴,然后进行修改(主要是功能的不同)。
- 但是也会有一点限制(比如商品价格后端传过来的价格显示出来就要除于100,传回给后端的时候乘于100,在这里的时候需要改框架)
- 框架是层层嵌套的,除非万不得已,最好不要改框架的内容。但是如果一个功能通过其他的方法无法实现,那么就要去改框架(要懂得变通)。不是说框架必须不能改,框架就是为了方便开发使用的,但是如果影响到了实际使用,就可以进行部分的修改。
- 使用一个框架,真的需要去熟悉一下框架里面有什么东西,比如说钩子函数,比如一些工具类,这样对于后来的开发会简便不少。我上一个项目没有用到框架,然后在这次就有这个烦恼,因为不熟悉这个框架所以踩了很多坑。
-
我总是想要等后端接口写好了,才能继续去写功能实现。其实这样不对,应该自己先写自己这个功能的逻辑,写假数据或者有软件可以模拟。其实只要逻辑跑通了,接口连接一下就可以了。
-
规范性错误:
错误正确时的提示(风格需要保持一致,要不然用户体验感不好) 超过三个属性需要换行。(代码规范问题)
代码规范问题说大也大说小也小,但是要形成一种习惯,这是一个程序员的基本素养,同时也是方便他人看懂自己的代码、方便迭代。
-
与后端的交流方面不大好,这是最大的一个错误。前后端分离的项目需要后端和前端的共同合作才能更好地完成,我沟通能力不大好,有时候给后端描述不清楚我的问题。比如字段的名字、格式,比如后端代码的提交与否等问题,都需要多沟通交流才能更有效率完成任务。
-
虽然这次已经是第二次做项目了,但是自己的不足还有好多。学习永无止境,加油!!