zoukankan      html  css  js  c++  java
  • 如果你想开发一个应用(1-17)

    数据模型

    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,5b8b4f53,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

    谢谢观看

  • 相关阅读:
    OnEraseBkgnd、OnPaint与画面重绘
    .编译ADO类DLL时报错的解决方案
    VC列表框样式
    Codeforces 131D. Subway 寻找环树的最短路径
    Codeforces 103B. Cthulhu 寻找奈亚子
    Codeforces 246D. Colorful Graph
    Codeforces 278C. Learning Languages 图的遍历
    Codeforces 217A. Ice Skating 搜索
    Codeforces 107A. Dorm Water Supply 搜图
    Codeforces 263 D. Cycle in Graph 环
  • 原文地址:https://www.cnblogs.com/jiangchao226/p/8251900.html
Copyright © 2011-2022 走看看