项目总结cms
需求:会议室预定,实现如下图的table表格,显示对应时间段的预定情况
问题1: 当会议室名称较长时,如何实现表格中,div的高度自适应,占满整个td?
实现思路,在完成了数据渲染之后,使用js重新设置高度
1 this.$nextTick( _=>{ 2 var eles = this.$el.querySelectorAll('tbody .au-table-cell') 3 4 eles.forEach( ele =>{ 5 ele.style.height = ele.parentElement.clientHeight+'px' 6 }) 7 8 })
需求: 实现图片的拖拽排序,可能出现换行的情况
实现思路:
1: 拖拽过程中的动画:对于之前实现表格的拖拽排序,不需要考虑换行,所以可以判断拖拽的tr距离顶部的高度以及drag的tr元素距离顶部的高度之差,来设定拖拽过程中的动画,比如dragover的元素向上移动或者向下移动。可是图片会有换行,所以不能根据高度来判断。所以动画可以换一种思路实现。空白占位符
2: 在页面初始化时,给ul绑定拖拽事件,(支持事件委托),在li标签上实现。
拖拽img时,冒泡到li标签上。
1 // 工具函数 2 // 参数: 指定的node元素 3 // 返回: 该node元素在列表中所占的位置角标(该indx值用于处理数据的删除插入) 4 5 getNodeIndex(node){ 6 return Array.apply(null, this.$refs.xinxiImgList.$el.children[0].querySelectorAll('li')).indexOf(node); 7 },
1 dragstart(event) { 2 if (event.target.parentNode.nodeName.toLowerCase() != 'ul') { 3 return; 4 } 5 event.dataTransfer.effectAllowed = 'move'; 6 this.startIndex = this.getNodeIndex(event.target) // 获取起始位置的索引 7 window.requestAnimationFrame(() => { 8 event.target.style.display = 'none'; 9 event.target.parentNode.insertBefore(this.placeholder, event.target); 10 }); 11 }, 12 dragend(event) { 13 if (event.target.parentNode.nodeName.toLowerCase() != 'ul') { 14 return; 15 } 16 this.endIndex = this.getNodeIndex(this.placeholder) // 获取结束位置的索引 17 window.requestAnimationFrame(() => { 18 event.target.style.display = ''; 19 this.placeholder.remove(); // 删除占位符 20 // 修改数据 21 this.pic.imgList.splice(this.endIndex, 0 , this.pic.imgList[this.startIndex]) 22 if (this.endIndex>this.startIndex) { 23 this.pic.imgList.splice(this.startIndex,1) 24 } else { 25 this.pic.imgList.splice(this.startIndex+1,1) 26 } 27 }) 28 }, 29 dragover(event) { 30 event.preventDefault(); 31 let $ele = event.target 32 let targetRect = $ele.getBoundingClientRect(); 33 let clientX = event.clientX; 34 let middle = targetRect.width / 2 + targetRect.left; 35 if($ele.nodeName.toLowerCase() == 'img'){ 36 $ele = $ele.parentNode 37 } else if ($ele.nodeName.toLowerCase() != 'li' || $ele.classList.contains('placeholder')){ 38 return 39 } 40 if (clientX < middle) { 41 $ele.parentNode.insertBefore(this.placeholder, $ele); 42 } else { 43 $ele.parentNode.insertBefore(this.placeholder, $ele.nextElementSibling); 44 } 45 return false; 46 }
// 在页面初始化的时候绑定事件
1 this.$nextTick(() => { 2 let ul = this.$refs.xinxiImgList.$el.children[0] 3 // 创建placeholder 4 var newNode = document.createElement("li"); 5 newNode.classList.add("placeholder") 6 this.placeholder = newNode 7 // 取消监听 8 ul.removeEventListener('dragstart', this.dragstart, false) 9 ul.removeEventListener("dragover", this.dragover, false) 10 ul.removeEventListener("dragend", this.dragend, false); 11 12 // 监听 13 ul.addEventListener("dragstart", this.dragstart, false) 14 ul.addEventListener("dragover", this.dragover, false) 15 ul.addEventListener("dragend", this.dragend, false); 16 })
tip: 关于window.requestAnimationFrame()
The window.requestAnimationFrame()
method tells the browser that you wish to perform an animation and requests that the browser call a specified function to update an animation before the next repaint. The method takes a callback as an argument to be invoked before the repaint.
window.requestAnimationFrame()方法告诉浏览器,你想要执行一个动画,并要求浏览器,在下一次重绘页面之前,执行一个特殊的函数去更新动画。这个方法有一个回调函数作为参数,他在下一次页面重绘之前执行。
3: 关于服务器时间和本地时间不同步的问题:
服务器和前端改成所在的时区必须相同,否则时间解析会有差异
4: 高德地图
自定义一个组件
1 export default { 2 template: ` <div> 3 <div id='gaodeMap' style='height:400px'>{{ value }}</div> 4 <div class ='panel' style=' background-color: #ddf;color: #333;border: 1px solid silver;box-shadow: 3px 4px 3px 0px silver;position: absolute;top: 100px;right: 50%; border-radius: 5px;overflow: hidden;line-height: 20px;'> 5 <div id = 'message'></div> 6 </div> 7 </div> 8 `, 9 props: { 10 value: String, 11 lnglatXY: Array, 12 }, 13 model: { 14 }, 15 data: () => ({ 16 map: '', 17 }), 18 watch: { 19 lnglatXY:function(val, oldval){ 20 if(val[1] == oldval[1] && val[0] == oldval[0]) return 21 this.map.setCenter(val) 22 } 23 }, 24 mounted: function(){ 25 this.init() 26 }, 27 methods: { 28 init(){ 29 var map = new AMap.Map('gaodeMap', { 30 mapStyle: 'amap://styles/normal', //样式URL 31 resizeEnable: true, 32 zoom: 10, 33 center: this.lnglatXY, 34 scrollWheel: false 35 }); 36 var message = document.getElementById('message'); 37 //加载PositionPicker,loadUI的路径参数为模块名中 'ui/' 之后的部分 38 AMapUI.loadUI(['misc/PositionPicker'], (PositionPicker) => { 39 var positionPicker = new PositionPicker({ 40 // mode:'dragMap',//'dragMap'、'dragMarker',默认为'dragMap' 41 map:map 42 }); 43 positionPicker.on('success', (positionResult) => { 44 message.innerHTML = '' 45 this.$emit('getPointZuobiao',positionResult.position) 46 }); 47 positionPicker.on('fail', function(positionResult) { 48 message.innerHTML = '无法获取地址' 49 }); 50 positionPicker.start(); 51 }); 52 53 AMap.plugin(['AMap.ToolBar','AMap.Scale','AMap.OverView'],function(){ 54 map.addControl(new AMap.ToolBar()); 55 map.addControl(new AMap.Scale()); 56 map.addControl(new AMap.OverView({isOpen:true})); 57 }); 58 this.map = map 59 } 60 } 61 }
参考: http://lbs.amap.com/fn/jsdemo_loader/?url=http%3A%2F%2Fwebapi.amap.com%2Fdemos%2Fgeocoder%2Fgeocoder-regeo.html
5: 遇到的问题: 在第一次初始化frame时,会获取到用户权限和用户信息,这些信息在 frame 部分显示。
用户退出系统并再次登陆时,由于并没有重新初始化frame,所以frame的SSPA_componentReady方法没有调用,因此不会重新调用权限和用户信息的方法,权限和信息还保留上次登陆用户的信息。
解决方案: 设置一个全局变量,例如 window.__reload , 默认为false。 当用户进入系统后退出时,设置window.__reload = true, 若是直接打开登陆界面,那么不需要处理。
在登陆界面判断,如果window.__reload为true,那么刷新,反之不刷新
1 if(window.__reload === true){ 2 location.reload() 3 return 4 } 5 // if(localStorage.getItem('TOKEN')){ 6 // dataHelper.userVerify(localStorage.getItem('TOKEN'),{forbidToast: true}).then(res=>{ 7 // location.replace('#welcome') 8 // }).catch(res=>{ 9 // localStorage.removeItem('TOKEN') 10 // }) 11 // }
6: 低版本的手机浏览器不支持es6的语法,所以字符串模版等写法不可用,
另外,对于transform属性的设置,应该写作ele.style.transform = 'scale(0.5)' , 而不应该用ele.style= 'transform: scale(0.5)'
7: nginx 配置
8: 金额和数字 输入框控件(vue指令)
1 Vue.directive('numberOnly', { // au-input 只能输入数字 2 bind: function (ele) { 3 ele = ele.querySelector('input') 4 this.handler = function () { 5 ele.value = ele.value.replace(/\D+/, '') 6 7 } 8 ele.addEventListener('input', this.handler) 9 } 10 }) 11 Vue.directive('money', { // au-input 只能输入金额 12 bind: function (ele) { 13 ele = ele.querySelector('input') 14 var exp =/[^0-9.]$/; 15 16 this.handler = function () { 17 if(exp.test(ele.value)){ 18 // 输入不正确 19 ele.style.borderColor = 'red' 20 return false; 21 } else { 22 ele.style.borderColor = '#DDD' 23 } 24 } 25 this.format = function(){ 26 if(!exp.test(ele.value)&&!!ele.value){ 27 ele.value = parseFloat(ele.value).toFixed(2) 28 } 29 } 30 ele.addEventListener('input', this.handler) 31 ele.addEventListener('blur', this.format) 32 } 33 })