如果你想开发一个应用(1-17)
2018-01-09 16:05
351 查看
数据模型
mvvm是数据驱动的,数据模型占了举足轻重的地位,所以,在做首页最终要的todo列表组件的时候,先暂时在客户端使用数据模型进行开发。而既然已经想到了这些数据需要通过交互从服务端获取,所以这个模型直接放入vuex中,数据模型的代码上一章已经分析过,所以这里直接复制过来:indexTodos:[ { month:0, //月份 default:1, //正在显示的月份 todos:[{ createTime:new Date(), //记录时间 item:'', //标题 content:'', //内容 weather:0, //天气 mood:0, //心情 bookmark:0, //标记 groupid:0, //所属组 }] } ]
这里使用了两个数组,即月份组,每个月是一个项,而月份内呢,记事也是一个数组,每个记事项就是一个项。
引用vuex
具体到页面中怎么用呢?也就是说,页面如何使用vuex库中的值呢?这里使用computed关键字,在vue中computed关键字的意思是实时计算,或者说监控值的变化,具体到代码中,首先需要我们需要的vuex组件,这里需要状态:
import { mapState } from 'vuex'
然后使用computed来引用组件中的值:
computed: mapState({ groupId: state=>state.groupId, items:state=>state.indexTodos }),
这样,Index.vue就可以像是使用data节点那样,通过this引用这两个值了。
组件化
这里需要思考一下,head有三个item,每个item对应的panel都需要在内容部分显示,那么,该如何控制具体到每个panel的显示或者加载呢?首先pass掉的肯定是做三个相同head和foot的页,这样的很明显不符合单页的需求,第二个被pass掉的应该是在这个页创建三个div,然后通过tabitem来控制div的隐藏显示了,那么,第三种方法应该是第二种的升级版,创建三个组件,通过tabitem来选择不同的组件加载。这里我们先创建一个
components,用来放我们所需要的组件。
首先,我们至少需要三个组件,也就是对应tabitem的三个,分别为:
DiaryPanel.vue 记录项(为防止与日记记录组相混淆,这里统一改为记录,标题为点滴,略微文青些)
Calendar.vue 日历项
Mine.vue 我的项
反正组件文件已经建立,那么我们先将他们一股脑的在Index页面中引用。
import DiaryPanelComponents from '../components/DiaryPanel.vue' import CalendarComponents from '../components/Calendar.vue' import MineComponents from '../components/Mine.vue'
因为完成之后,紧接着就是要对它们进行注册:
components:{ DiaryPanelComponents, CalendarComponents, MineComponents },
这时,就可以和html标签一样使用了。
<div id="contentPanel"> <transition name="custom-classes-transition" enter-active-class="animated bounceInLeft" leave-active-class="animated fadeOut" :duration="{ enter: 700, leave: 200 }" > <DiaryPanelComponents></DiaryPanelComponents> </transition> </div>
但是,我们想想,这样的这个页面只能使用
DiaryPanelComponents这一个组件,其他组件怎么办,如果将三个组件一股脑的全写在这个div节点中,控制显示隐藏,岂不是又回到了老路上?
好在vue提供了动态绑定组件的功能,我们在data数据模型中新增一个表示组件名称的属性
currentView表示当前处于显示状态的组件:
data () { return { currentView:'DiaryPanelComponents', ... } },
然后修改组件部分的模板html:
<div id="contentPanel"> <transition name="custom-classes-transition" enter-active-class="animated bounceInLeft" leave-active-class="animated fadeOut" :duration="{ enter: 700, leave: 200 }" > <component v-bind:is="currentView"> </component> </transition> </div>
这样,tab的item选择操作,就变成了最基本的的字符串赋值操作:
tabChange:function(event){ ... var componentName = '' switch (event) { case 'tab1': componentName = 'DiaryPanelComponents' break case 'tab2': componentName = 'CalendarComponents' break case 'tab3': componentName = 'MineComponents' break } this.currentView = componentName }
组件嵌套
首页现在基本只起一个调度作用,具体的内容交给了组件来完成,下面打开DiaryPanel.vue,对这个组件进行开发。分析一下这个组件,这个组同样分为两部分,头部一个作为标题的月份,下边循环显示一个此月所有的记录项。
但无论开发哪个部分,我们都需要先从vuex中将数据取出来:
import { mapState } from 'vuex' export default { computed: mapState({ indexTodos: state=>state.indexTodos, }) }
剩下的就很简单了,先把显示的部分代码写完,这里用了museui的组件sub-header:
<div v-for="item in indexTodos" > <mu-sub-header class="day_title">{{ item.month }}</mu-sub-header> <DiaryListComponents></DiaryListComponents> </div>
然后根据实际情况修改css样式:
.day_title{ font-size: 50px; line-height: 55px; font-family: 'Microsoft YaHei',arial,tahoma,\5b8b\4f53,sans-serif; font-weight: 500; color: #5e35b1; text-align: center; padding: 0px; }
接下来就是循环显示记录列表了,想一下原型中,这个todo放到了一个面板块内,而面板块还是比较复杂的,并且每个月都要使用,所以,我们也把他提炼到一个组件中,嗯,就叫DiaryList,从这里也可以看出,vue的组件是支持嵌套的。接下来在components文件夹内创建DiaryList文件。
同时,由于用户会滑动页面,也就是说,这个组件内所需要的值,即todo数组,是与父组件联系紧密的,所以需要通过参数的方式,将父组件循环得来的值传送到子组件中,vue中传值也非常方便,在标签引用的地方绑定一下就行了:
<DiaryListComponents v-bind:todos="item.todos"></DiaryListComponents>
然后子组件获取更加简单:
DiaryList.vue:
export default { props:["todos"] }
这样就可以直接使用todos变量。
而面板使用museui的pager控件就可以了,还自带阴影效果,并且是在循环体内,使用todos变量的pager代码如下;
<mu-paper class="diaryitem" :zDepth="2" v-for="(item) in todos" > </mu-paper>
过滤器
接下来就是对这个组件的开发了。观察一下这个块的内容:首先四周都有个边框,所以用一个父级的mu-content-block包裹一下,然后看内容,是一个左中右的结构,刚好museui有个布局表格,就直接使用了,布局表格的权重,暂时就20-6-20吧,最终布局部分代码如下:
<mu-paper class="diaryitem" :zDepth="2" v-for="(item) in todos" > <mu-content-block> <mu-row gutter> <mu-col width="20"> </mu-col> <mu-col width="60"> </mu-col> <mu-col width="20" style="text-align:right"> </mu-col> </mu-row> </mu-content-block> </mu-paper>
剩下的内容,如果先不考虑样式的话,最简单应该就是标题和内容了,直接输入就好:
<mu-col width="60"> <div class="item_time">12:34</div> <div class="item_title">{{ item.item }}</div> <div class="item_content">{{item.content}}</div> </mu-col>
css的一会在完善,接下来就是时间了,其实时间虽然现实了这么多,但是具体到了数据项上,实际上只有一个,就是createtime,接下来要做的就是如何提取显示的问题了,这时候vue提供的过滤器就登场了,下面以日期为例介绍一下过滤器的用法.
过滤器其实就是一个通过filter标记的普通js的方法,然后我们先让他返回一个固定数字的写法:
filters: { getDay(time) { return 3; } }
调用方法为:
{{ item.createTime | getDay }}
其中item.createTime对应模型中的值和过滤器方法的参数,getDay很明显,就是我们过滤器的方法了。有了这些,完成过滤器就很简单了:
getDay(time) { var date = new Date(time); return date.getDate(); }
这时页面上就回只显示日期值的。
接下来我们想到,不只是日期需要,其他的需要的还有很多,比如月份,时间等,而且在可预见的地方,比如新增记录页,tag的列表页等,所以这个功能有必要提取复用一下,关于日期操作的js方法网上有很多,就不在叙述,这个作为工具类,通服务端代码一样,创建一个util文件夹,然后就叫Date.js文件,最终的代码如下:
export function formatDate(time, fmt) { var date = new Date(time); if (/(y+)/.test(fmt)) { fmt = fmt.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length)); } let o = { 'M+': date.getMonth() + 1, 'd+': date.getDate(), 'h+': date.getHours(), 'm+': date.getMinutes(), 's+': date.getSeconds(), 'w+':getWeek(date) }; for (let k in o) { if (new RegExp(`(${k})`).test(fmt)) { let str = o[k] + ''; fmt = fmt.replace(RegExp.$1, (RegExp.$1.length === 1) ? str : padLeftZero(str)); } } return fmt; }; function padLeftZero(str) { return ('00' + str).substr(str.length); } function getDay(time){ return formatDate(time,"dd"); } function getWeek(time){ var weekName=['星期日','星期一','星期二','星期三','星期四','星期五','星期六'] return weekName[time.getDay()]; } function getTime(time){ return formatDate("hh-mm-dd"); }
然后DirayList组件内引入,并完成剩余的几个过滤器方法:
<script> import { formatDate } from '../utils/date.js'; export default { props:["todos"], filters: { getDay(time) { return formatDate(time,"dd"); }, getWeek(time) { return formatDate(time,"w"); }, getTime(time) { return formatDate(time,"hh:mm"); } } } </script>
最后,是右边的三个icon图标,这三个db中存储的是int型,而页面显示需要一个String的name,所以,通date中的week一样,分别将int作为数组的下标,这里给出三个最简的形式:
mood.js
export function mood(num) { var moodValue=["mood",""] if(num==null) num=0; return moodValue[num]; }
weather.js
export function weather(num) { var weatherValue=["wb_sunny",""] if(num==null) num=0; return weatherValue[num]; }
bookmark.js
export function bookmark(num) { var bookmarkValue=["bookmark_border","bookmark"] if(num==null) num=0; return bookmarkValue[num]; }
最终标签内的代码如下:
<mu-paper class="diaryitem" :zDepth="2" v-for="(item) in todos" > <mu-content-block> <mu-row gutter> <mu-col width="20"> <div class="item_day">{{ item.createTime | getDay }}</div> <div class="item_week">{{ item.createTime | getWeek }}</div> </mu-col> <mu-col width="60"> <div class="item_time">{{ item.createTime | getTime }}</div> <div class="item_title">{{ item.item }}</div> <div class="item_content">{{item.content}}</div> </mu-col> <mu-col width="25" style="text-align:right"> <mu-icon :value=" item.mood | getMoodValue " :size="16"/> <mu-icon :value=" item.weather | getWeatherValue " :size="16"/> <mu-icon :value=" item.bookmark | getBookmarkValue " :size="16"/> </mu-col> </mu-row> </mu-content-block> </mu-paper>
js代码如下:
import { formatDate } from '../utils/date.js'; import { mood } from '../utils/mood.js'; import { weather } from '../utils/weather.js'; import { bookmark } from '../utils/bookmark.js'; export default { props:["todos"], filters: { getDay(time) { var date = new Date(time); console.log(date) return date.getDate(); //return formatDate(time,"dd"); }, getWeek(time) { return formatDate(time,"w"); }, getTime(time) { return formatDate(time,"hh:mm"); }, getMoodValue(num){ return mood(num); }, getWeatherValue(num){ return weather(num); }, getBookmarkValue(num){ return bookmark(num); } } }
css代码略,请自行查看源码
这时候跑起来,效果如图:
从当前的界面莱克,基本上符合原型的要求。
服务端数据
剩下的内容就简单了,只要解决数据来源的问题就清楚了,我们在贴一下要求的数据格式:indexTodos:[ { month:0, //月份 default:1, //正在显示的月份 todos:[{ createTime:new Date(), //记录时间 item:'', //标题 content:'', //内容 weather:0, //天气 mood:0, //心情 bookmark:0, //标记 groupid:0, //所属组 }] } ]
同时,还需要一个itemnumber,所以回到服务端的java代码,一步一步的完成这个api功能。
首先,为了和原有的代码区分,新创建一个ApiTodoController控制器,里边新增一个action,apiIndex,这个action除了token外,还需要一个月份作为参数,这个也很容易理解。然后我们需要根据月份查询todo列表,在之前还提到过,由于分多个组,需要设置一个默认组,首页显示默认组的todo,所以,服务层的方法名也就出来了,getTodosByDefaultGroup,参数有两个,用户Id(由token获取)和月份(参数传递)。
其实根据服务层的方法名,他的伪代码就都出来了,根据的《代码大全里》的方法,用说明注释写出来:
根据用户id查询此用户的默认记录组
查询此组此月的所有记录
注释简单,代码当然也就简单了:
public List<Todo> getTodoByDefaultGroup(int userId,int month) { TodoGroup todoGroup=todoGroupRepository.findByIsDefaultAndUserId(1,userId); DateBetween between=getDate(month); List<Todo> todos= todoRepository.getByGroupIdAndCreateTimeBetween(todoGroup.getId(),between.getFirstDate(),between.getEndDate()); return todos; }
repository层内只有方法名没有方法体,所以查看调用就能看到全部内容,不在叙述。
DateBetween类从名字就可以看出来,表示一个日期区间,具体到这个代码中,表示的是这个月的1号到这个月的最后一天,即31号(1月份),他的代码如下:
class DateBetween{ private Date firstDate; private Date endDate; //get set }
getDate就是获取参数月的起始和结束日期,代码如下:
private DateBetween getDate( int month ){ DateBetween between=new DateBetween(); SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd"); Calendar firstCalender = Calendar.getInstance(); // 获取前月的第一天 firstCalender = Calendar.getInstance(); firstCalender.add(Calendar.MONTH, 0); firstCalender.set(Calendar.DAY_OF_MONTH, 1); between.setFirstDate(firstCalender.getTime()); // 获取前月的最后一天 Calendar endCalender = Calendar.getInstance(); endCalender.add(Calendar.MONTH, 1); endCalender.set(Calendar.DAY_OF_MONTH, 0); between.setEndDate(endCalender.getTime()); return between; }
貌似有点啰嗦,先这样回头再慢慢重构吧,这个方法只有这个类用,是private的。
组装json
接下来回到Controller,这里没什么好说的,jackson库能直接将Map和类转成Json对象,所以直接把前端需要的数据通过map组装起来就好了,直接贴代码:
@RequestMapping(value = "/api/index",method = RequestMethod.POST) public Object apiIndex(HttpServletRequest request,@RequestBody Map map){ //获取首页数据 String userId=request.getAttribute("tokenId").toString(); Integer month=Integer.parseInt( map.get("month").toString()); List<Map<String,Object>> items=new ArrayList<Map<String,Object>>(); for (int i=0;i<1;i++) { List<Todo> todos = todoService.getTodoByDefaultGroup(Integer.parseInt(userId),month); //数据结构扩充接口 Map<String, Object> data = new HashMap<String, Object>(); data.put("month",month); data.put("todos",todos); data.put("default",1); items.add(data); } Map<String,Object> resutl=new HashMap<String,Object>(); resutl.put("items",items); resutl.put("itemnumber",items.size()); return result(resutl); }
注意这个for循环,现在只走一次,这是为了之后优化效率,一次性返回多个月而预留的代码,现在就直接当它是一个顺序结构即可.
到目前为止的代码:
前端vue
后端java
谢谢观看
相关文章推荐
- 如果你想开发一个应用(1-21)
- 如果你想开发一个应用(1-19)
- 如果你想开发一个应用(1-6)
- 如果你想开发一个应用(1-1)
- 如果你想开发一个应用(1-7)
- 如果你想开发一个应用(1-16)
- 如果你想开发一个应用(1-12)
- 如果你想开发一个应用(1-20)
- 如果你想开发一个应用(1-13)
- 如果你想开发一个应用(1-4)
- 如果你想开发一个应用(1-8)
- 如果你想开发一个应用(1-2)
- 如果你想开发一个应用(1-22)
- 如果你想开发一个应用(1-15)
- 如果你想开发一个应用(1-3)
- 如果你想开发一个应用(1-18)
- 如果你想开发一个应用(1-5)
- 如果你想开发一个应用(1-11)
- 如果你想开发一个应用(1-9)