若是你想開發一個應用(1-17)

數據模型

mvvm是數據驅動的,數據模型佔了舉足輕重的地位,因此,在作首頁最終要的todo列表組件的時候,先暫時在客戶端使用數據模型進行開發。而既然已經想到了這些數據須要經過交互從服務端獲取,因此這個模型直接放入vuex中,數據模型的代碼上一章已經分析過,因此這裏直接複製過來:css

indexTodos:[
    {
        month:0,              //月份
        default:1,            //正在顯示的月份
        todos:[{
            createTime:new Date(),   //記錄時間
            item:'',                 //標題
            content:'',              //內容
            weather:0,               //天氣
            mood:0,                  //心情
            bookmark:0,              //標記
            groupid:0,               //所屬組
        }]
    }
]

這裏使用了兩個數組,即月份組,每月是一個項,而月分內呢,記事也是一個數組,每一個記事項就是一個項。html

引用vuex

具體到頁面中怎麼用呢?也就是說,頁面如何使用vuex庫中的值呢?前端

這裏使用computed關鍵字,在vue中computed關鍵字的意思是實時計算,或者說監控值的變化,具體到代碼中,首先須要咱們須要的vuex組件,這裏須要狀態:vue

import { mapState } from 'vuex'

而後使用computed來引用組件中的值:java

computed: mapState({
        groupId: state=>state.groupId,
        items:state=>state.indexTodos
}),

這樣,Index.vue就能夠像是使用data節點那樣,經過this引用這兩個值了。git

組件化

這裏須要思考一下,head有三個item,每一個item對應的panel都須要在內容部分顯示,那麼,該如何控制具體到每一個panel的顯示或者加載呢?首先pass掉的確定是作三個相同head和foot的頁,這樣的很明顯不符合單頁的需求,第二個被pass掉的應該是在這個頁建立三個div,而後經過tabitem來控制div的隱藏顯示了,那麼,第三種方法應該是第二種的升級版,建立三個組件,經過tabitem來選擇不一樣的組件加載。github

這裏咱們先建立一個components,用來放咱們所須要的組件。vuex

首先,咱們至少須要三個組件,也就是對應tabitem的三個,分別爲:vue-cli

  • DiaryPanel.vue 記錄項(爲防止與日記記錄組相混淆,這裏統一改成記錄,標題爲點滴,略微文青些)
  • Calendar.vue 日曆項
  • Mine.vue 個人項

反正組件文件已經創建,那麼咱們先將他們一股腦的在Index頁面中引用。json

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

謝謝觀看

相關文章
相關標籤/搜索