SpringMVC_ < sf >表單標籤

在使用SpringMVC的時候我們可以使用Spring封裝的一系列表單標籤,這些標籤都可以訪問到ModelMap中的內容。下面將對這些標籤一一介紹。


1.引入標籤頭文件

在正式介紹SpringMVC的表單標籤之前,我們需要先在JSP中聲明使用的標籤,具體做法是在JSP文件的頂部加入以下指令:

<%@taglib prefix="sf" uri="http://www.springframework.org/tags/form" %>

2.form標籤

使用Spring的form標籤主要有兩個作用,第一是它會自動的綁定來自Model中的一個屬性值到當前form對應的實體對象,默認是command屬性(一般都用modelAttribute,兩者本質上沒有區別),這樣我們就可以在form表單體裏面方便的使用該對象的屬性了;第二是它支持我們在提交表單的時候使用除GET和POST之外的其他方法進行提交,包括DELETE和PUT等。

  • 代碼實例
        // 這裏使用modelAttribute換成commandName也是一樣的效果
 <sf:form action="formTag/form.do" method="post" modelAttribute="user">
    <table>
        <tr>
            <td>Name:</td><td><form:input path="name"/></td>
        </tr>
        <tr>
            <td>Age:</td><td><form:input path="age"/></td>
        </tr>
        <tr>
            <td colspan="2"><input type="submit" value="提交"/></td>
        </tr>
    </table>
</sf:form>

這個時候如果Model中存在一個屬性名稱爲user的javaBean,而且該javaBean擁有屬性name和age的時候,在渲染上面的代碼時就會取command的對應屬性值賦給對應標籤的值。如在上面的代碼中,假設Model中存在一個屬性名稱爲user的javaBean,且它的name和age屬性分別爲“Zhangsan”和“36”時,那麼它在渲染時就會生成如下一段代碼:

<form id="user" action="formTag/form.do" method="post">
    <table>
        <tr>
            <td>Name:</td><td><input id="name" name="name" type="text" value="ZhangSan"/></td>
        </tr>
        <tr>
            <td>Age:</td><td><input id="age" name="age" type="text" value="36"/></td>
        </tr>
        <tr>
            <td colspan="2"><input type="submit" value="提交"/></td>
        </tr>
    </table>
</form>

從上面生成的代碼中,我們可以看出,當沒有指定form標籤的id時它會自動獲取該form標籤綁定的Model中對應屬性名稱作爲id,而對於input標籤在沒有指定id的情況下它會自動獲取path指定的屬性作爲id和name。

2.支持隱藏域請求

看下面代碼,我們可以把form的method設爲delete

<form:form action="formTag/form.do" method="delete" modelAttribute="user">
    <table>
        <tr>
            <td>Name:</td><td><form:input path="name"/></td>
        </tr>
        <tr>
            <td>Age:</td><td><form:input path="age"/></td>
        </tr>
        <tr>
            <td colspan="2"><input type="submit" value="提交"/></td>
        </tr>
    </table>
</form:form>

在上面代碼中我們設定了該form的提交方法是delete,這樣在後臺我們就可以給對應的請求方法的RequestMapping加上method爲RequestMethod.DELETE的限制。我們來看一下上面的代碼在進行渲染的時候會生成怎樣的Html代碼,其生成的代碼如下所示:

<form id="user" action="formTag/form.do" method="post">
    <input type="hidden" name="_method" value="delete"/>
    <table>
        <tr>
            <td>Name:</td><td><input id="name" name="name" type="text" value="ZhangSan"/></td>
        </tr>
        <tr>
            <td>Age:</td><td><input id="age" name="age" type="text" value="36"/></td>
        </tr>
        <tr>
            <td colspan="2"><input type="submit" value="提交"/></td>
        </tr>
    </table>
</form>

從它生成的代碼我們可以看出,Spring在實現除GET和POST之外的請求方法時,還是使用的POST方法進行請求,然後給表單加上了一個隱藏域,用以表示真正的請求方法,這個隱藏域的名稱默認是“_method”。上面這樣定義之後是不是就意味着我們可以以delete方式訪問到“formTag/form.do”了呢?答案是不行的。這樣定義我們只是多加了一個用以表示請求方法的隱藏域而已,實際的請求方式還是POST。Spring爲我們提供了一個Filter——HiddenHttpMethodFilter,通過這個Filter我們可以把以POST方式傳遞過來的表示實際請求方式的參數轉換爲對應的真正的Http請求方法。所以這個時候我們還需要在web.xml中加上如下代碼:

    <filter>
       <filter-name>hiddenHttpMethodFilter</filter-name>
       <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
    </filter>
    <filter-mapping>
       <filter-name>hiddenHttpMethodFilter</filter-name>
       <url-pattern>/*</url-pattern>
    </filter-mapping>

注意:HiddenHttpMethodFilter只能對以POST方式進行傳遞的表示請求方式的隱藏域轉換爲真正的Http請求方式。當我們直接在form:form標籤的method中使用除GET和POST方法以外的其他方法時,Spring會自動生成以POST方式進行傳遞的表單以及對應的隱藏域。所以當我們需要手動的設置表示請求方法的隱藏域時,我們就需要指定表單的請求方式爲POST,爲GET將不會生效。

<form:form action="formTag/form.do" method="post" modelAttribute="user">
    <input type="hidden" name="_method" value="head"/>
    <table>
        <tr>
            <td>Name:</td><td><form:input path="name"/></td>
        </tr>
        <tr>
            <td>Age:</td><td><form:input path="age"/></td>
        </tr>
        <tr>
            <td colspan="2"><input type="submit" value="提交"/></td>
        </tr>
    </table>
</form:form>

上面代碼就是一個手動定義請求方式的隱藏域的示例。這裏表示請求方式的隱藏域的名稱默認是“_method”,如果不想使用這個默認值的話,我們也可以通過form:form標籤的methodParam屬性來指定。如下面這個示例:

<form:form action="formTag/form.do" method="post" methodParam="requestMethod" modelAttribute="user">
    <input type="hidden" name="requestMethod" value="head"/>
    <table>
        <tr>
            <td>Name:</td><td><form:input path="name"/></td>
        </tr>
        <tr>
            <td>Age:</td><td><form:input path="age"/></td>
        </tr>
        <tr>
            <td colspan="2"><input type="submit" value="提交"/></td>
        </tr>
    </table>
</form:form>

同時我們也要告訴HiddenHttpMethodFilter我們是使用哪個表單參數作爲methodParam,所以我們需要在配置HiddenHttpMethodFilter的時候指明methodParam對應的值。

    <filter>
       <filter-name>hiddenHttpMethodFilter</filter-name>
       <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
       <init-param>
           <param-name>methodParam</param-name>
           <param-value>requestMethod</param-value>
       </init-param>
    </filter>
    <filter-mapping>
       <filter-name>hiddenHttpMethodFilter</filter-name>
       <url-pattern>/*</url-pattern>
    </filter-mapping>

另外需要注意的是在有Multipart請求處理的時候HiddenHttpMethodFilter需要在Multipart處理之後執行,因爲在處理Multipart時需要從POST請求體中獲取參數。所以我們通常會在HiddenHttpMethodFilter之前設立一個MultipartFilter。MultipartFilter默認會去尋找一個名稱爲filterMultipartResolver的MultipartResolver bean對象來對當前的請求進行封裝。所以當你定義的MultipartResolver的名稱不爲filterMultipartResolver的時候就需要在定義MultipartFilter的時候通過參數multipartResolverBeanName來指定。

    <filter>
       <filter-name>multipartFilter</filter-name>
       <filter-class>org.springframework.web.multipart.support.MultipartFilter</filter-class>
       <init-param>
           <param-name>multipartResolverBeanName</param-name>
           <param-value>multipartResolver</param-value>
       </init-param>
    </filter>
    <filter-mapping>
       <filter-name>multipartFilter</filter-name>
       <url-pattern>/*</url-pattern>
    </filter-mapping>

    <filter>
       <filter-name>hiddenHttpMethodFilter</filter-name>
       <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
       <init-param>
           <param-name>methodParam</param-name>
           <param-value>requestMethod</param-value>
       </init-param>
    </filter>
    <filter-mapping>
       <filter-name>hiddenHttpMethodFilter</filter-name>
       <url-pattern>/*</url-pattern>
    </filter-mapping>

2.input標籤

SpringMVC的input標籤會被渲染爲一個type爲text的普通Html input標籤。使用SpringMVC的input標籤的唯一作用就是它能綁定表單數據。SpringMVC表單標籤最大的好處就是它支持數據綁定,當我們的表單標籤不需要綁定的數據的時候,我們應該使用普通的Html標籤。關於input標籤綁定表單數據的方法已經在介紹form標籤的時候順帶介紹過了,這裏就不再過多的贅述了。

<form:form action="formTag/form.do" method="head" modelAttribute="user" methodParam="requestMethod">
    <table>
        <tr>
            <td>Name:</td><td><form:input path="name"/></td>
        </tr>
        <tr>
            <td>Age:</td><td><form:input path="age"/></td>
        </tr>
        <tr>
            <td colspan="2"><input type="submit" value="提交"/></td>
        </tr>
    </table>
</form:form>

3.hidden標籤

hidden標籤會被渲染爲一個type爲hidden的普通Html input標籤。用法跟input標籤一樣,也能綁定表單數據,只是它生成的是一個隱藏域。


4.checkbox標籤

checkbox標籤會被渲染爲一個type爲checkbox的普通HTML input標籤。checkbox標籤也是支持綁定數據的。我們知道checkbox就是一個複選框,有選中和不選中兩種狀態,那麼我們在使用checkbox標籤的時候是如何來設定它的狀態的呢?checkbox標籤的選中與否狀態是根據它綁定的值來判斷的

  • 綁定boolean數據
<form:form action="formTag/form.do" method="post" commandName="user">
    <table>
        <tr>
            <td>Male:</td><td><form:checkbox path="male"/></td>
        </tr>
        <tr>
            <td colspan="2"><input type="submit" value="提交"/></td>
        </tr>
    </table>
</form:form>

看上面這段代碼,這個時候假設我們在渲染該視圖之前往ModelMap中添加了一個user屬性,並且該user對象有一個類型爲boolean的屬性male,那麼這個時候如果male屬性爲true則Male那一欄的複選框將會被選中。

  • 綁定列表數據
    這裏的列表數據包括數組、List和Set。下面將以List爲例講一下checkbox是如何根據綁定的列表數據來設定選中狀態的。現在假設有一個類User,其有一個類型爲List的屬性roles,如下所示:
public class User {

    private List<String> roles;

    public List<String> getRoles() {
       return roles;
    }

    public void setRoles(List<String> roles) {
       this.roles = roles;
    }
}

那麼當我們需要展現該User是否擁有某一個Role的時候,我們可以使用checkbox標籤來綁定roles數據進行展現。當checkbox標籤的value在我們綁定的列表數據中存在的時候該checkbox將爲選中狀態。來看下面一段代碼:

<form:form action="formTag/form.do" method="post" commandName="user">
    <table>
        <tr>
            <td>Roles:</td>
            <td>
               <form:checkbox path="roles" value="role1"/>Role1<br/>
               <form:checkbox path="roles" value="role2"/>Role2<br/>
               <form:checkbox path="roles" value="role3"/>Role3
            </td>
        </tr>
    </table>
</form:form>

就上面代碼而言就是當User擁有role1的時候對應的就會爲選中狀態,也就是說roles列表中包含role1的時候該checkbox就會爲選中狀態。

  • 綁定一個Object數據
    checkbox還支持綁定數據類型爲Object的數據,這種情況下Spring會拿所綁定對象數據的toString結果跟當前checkbox的value進行比較,如果能夠進行匹配則該checkbox將爲選中狀態。

5.Checkboxes標籤

相對於一個checkbox標籤只能生成一個對應的複選框而言,一個checkboxes標籤將根據其綁定的數據生成N個複選框。checkboxes綁定的數據可以是數組、集合和Map。在使用checkboxes時我們有兩個屬性是必須指定的,一個是path,另一個是items。Items表示當前要用來展現的項有哪些,而path所綁定的表單對象的屬性表示當前表單對象擁有的項,即在items所展現的所有項中表單對象擁有的項會被設定爲選中狀態。先來看以下一段代碼:

<form:form action="formTag/form.do" method="post" commandName="user">
    <table>
        <tr>
           <td>Roles:</td>
            <td>
               <form:checkboxes path="roles" items="${roleList}"/> 
            </td>
        </tr>
        <tr>
            <td colspan="2"><input type="submit" value="提交"/></td>
        </tr>
    </table>
</form:form>

上面的JSP視圖對應着如下的處理器方法:

    @RequestMapping(value="form", method=RequestMethod.GET)
    public String formTag(Map<String, Object> map) {
       User user = new User();
       List<String> roles = new ArrayList<String>();
       roles.add("role1");
       roles.add("role3");
       user.setRoles(roles);
       List<String> roleList = new ArrayList<String>();
       roleList.add("role1");
       roleList.add("role2");
       roleList.add("role3");
       map.put("user", user);
       map.put("roleList", roleList);
       return "formTag/form";
    }

從以上代碼中我們可以看到我們放在ModelMap中的roleList對象有三個元素,分別是role1、role2和role3,而我們的表單對象User的roles屬性只擁有兩個元素,分別是role1和role3,,所以當我們訪問該處理器方法返回如上所示的視圖頁面時,我們要展現的複選框項是roleList,也就是role1、role2和role3,而我們表單對象只擁有role1和role3,所以在頁面進行渲染的時候會展示3個複選框項,但只有role1和role3會被設定爲選中狀態。


上面介紹的這種情況是使用List作爲展現複選框項的數據源,這種情況我們已經看到了它所呈現出來的標籤Label和它的值是一樣的。使用Array和Set作爲數據源也是這種情況。那麼如果要讓checkboxes呈現出來的Label和實際上送的value不同的話應該怎麼做呢?這個時候我們就可以使用Map作爲數據源了。使用Map作爲checkboxes的items屬性的數據源時Key將作爲真正的複選框的value,而Map的value將作爲Label進行展示。當使用Map作爲checkboxes的items屬性的數據源時我們綁定的表單對象屬性的類型可以是Array、集合和Map,這種情況就是判斷items Map中是否含有對應的key來決定當前的複選框是否處於選中狀態。我們來看以下一個處理器方法以及其對應的視圖代碼。

    @RequestMapping(value="form", method=RequestMethod.GET)
    public String formTag(Map<String, Object> map) {
       User user = new User();
       List<String> roles = new ArrayList<String>();
       roles.add("role1");
       roles.add("role3");
       user.setRoles(roles);
       Map<String, String> roleMap = new HashMap<String, String>();
       roleMap.put("role1", "角色1");
       roleMap.put("role2", "角色2");
       roleMap.put("role3", "角色3");
       map.put("user", user);
       map.put("roleMap", roleMap);
       return "formTag/form";
    }

對應的視圖代碼:

<form:form action="formTag/form.do" method="post" commandName="user">
    <table>
        <tr>
            <td>Roles:</td>
            <td>
               <form:checkboxes path="roles" items="${roleMap}"/>  
            </td>
        </tr>
        <tr>
            <td colspan="2"><input type="submit" value="提交"/></td>
        </tr>
    </table>
</form:form>

這個時候我們知道會呈現出3個複選框,而checkboxes綁定的表單對象user的roles屬性是一個集合對象,其包含的兩個元素都能在checkboxes的items數據源中找到對應的Key,所以以這兩個元素爲value的checkbox將處於選中狀態

當我們使用Array或者集合作爲數據源,且裏面的元素都是一個一個POJO時,我們還可以使用checkboxes標籤的itemLabel和itemValue屬性來表示使用數組或者集合中元素對象的哪一個屬性作爲需要呈現的單選框的label和value。


6.radiobutton標籤

radiobutton標籤會被渲染爲一個type爲radio的普通HTML input標籤。radiobutton標籤也是可以綁定數據的。以下是一個radiobutton的簡單應用示例:

<form:form action="formTag/form.do" method="post" commandName="user">
    <table>
        <tr>
            <td>性別:</td>
            <td>
               <form:radiobutton path="sex" value="1"/><form:radiobutton path="sex" value="0"/></td>
        </tr>
        <tr>
            <td colspan="2"><input type="submit" value="提交"/></td>
        </tr>
    </table>
</form:form>

在上面代碼中我們的radiobutton標籤都是綁定了表單對象user的sex屬性,當sex爲1的時候就代表性別爲男,上面性別爲男的那一行就會被選中,當sex爲0的時候就代表性別爲女,上面性別爲女的那一行就會被選中。


7.radiobuttons標籤

radiobuttons標籤跟radiobutton標籤的區別如同checkbox標籤對checkboxes標籤的區別。使用radiobuttons標籤的時候將生成多個單選按鈕。使用radiobuttons有兩個屬性也是我們必須指定的,一個是path屬性,表示綁定的表單對象對應的屬性,另一個是items屬性,表示用於生成單選按鈕的數據源。跟checkboxes一樣,radiobuttons的items屬性和path屬性都可以是Array、集合或者是Map。現在我們假設user在籃球、足球、乒乓球、羽毛球和排球這5種運動中選擇一種作爲自己最喜歡的球類運動。處理器方法和返回的對應的視圖代碼如下:

    @RequestMapping(value="form", method=RequestMethod.GET)
    public String formTag(Map<String, Object> map) {
       User user = new User();
       user.setFavoriteBall(4);//設置我最喜愛的球類運動是4羽毛球
       Map<Integer, String> ballMap = new HashMap<Integer, String>();
       ballMap.put(1, "籃球");
       ballMap.put(2, "足球");
       ballMap.put(3, "乒乓球");
       ballMap.put(4, "羽毛球");
       ballMap.put(5, "排球");
       map.put("user", user);
       map.put("ballMap", ballMap);
       return "formTag/form";
    }

JSP視圖

<form:form action="formTag/form.do" method="post" commandName="user">
    <table>
        <tr>
            <td>最喜歡的球類:</td>
            <td>
               <form:radiobuttons path="favoriteBall" items="${ballMap}" delimiter="&nbsp;"/>
            </td>
        </tr>
        <tr>
            <td colspan="2"><input type="submit" value="提交"/></td>
        </tr>
    </table>
</form:form>

在上述代碼中我們可以看到我們使用了radiobuttons的delimiter屬性,該屬性表示進行展示的radiobutton之間的分隔符。這裏用的是一個空格。結果頁面如下所示:


8.password標籤

password標籤將會被渲染爲一個type爲password的普通HTML input標籤。


9.select標籤

select標籤將會被渲染爲一個普通的HTML select標籤。這裏還拿前面的user最喜歡的球類運動來做示例,有如下這樣一個處理器方法和對應的視圖頁面:

    @RequestMapping(value="form", method=RequestMethod.GET)
    public String formTag(Map<String, Object> map) {
       User user = new User();
       user.setFavoriteBall(4);//設置我最喜愛的球類運動是4羽毛球
       Map<Integer, String> ballMap = new HashMap<Integer, String>();
       ballMap.put(1, "籃球");
       ballMap.put(2, "足球");
       ballMap.put(3, "乒乓球");
       ballMap.put(4, "羽毛球");
       ballMap.put(5, "排球");
       map.put("user", user);
       map.put("ballMap", ballMap);
       return "formTag/form";
    }

JSP視圖

<form:form action="formTag/form.do" method="post" commandName="user">
    <table>
        <tr>
            <td>最喜歡的運動:</td>
            <td>
               <form:select path="favoriteBall" items="${ballMap}"/>
            </td>
        </tr>
       <tr>
            <td colspan="2"><input type="submit" value="提交"/></td>
        </tr>
    </table>
</form:form>

從上面示例我們可以看出,我們通過items屬性給select標籤指定了一個數據源,並且綁定了表單對象user的favoriteBall屬性。Items屬性是用於指定當前select的所有可選項的,但是它對於select標籤而言不是必須的,因爲我們還可以手動的在select標籤中間加上option標籤來指定select可選的option。Select標籤支持的items屬性的數據類型可以是Array、Collection和Map,當數據類型爲Array或Collection時且其中的元素爲一個POJO時,我們可以通過屬性itemLabel和itemValue來指定將用於呈現的option Label和Value,其他情況下Array和Collection數據源中的元素將既作爲可選項option的value又作爲它的Label。當items的數據類型爲Map時,Map的key將作爲可選項option的value,而Map的value將作爲option的Label標籤。


10.option標籤

option標籤會被渲染爲一個普通的HTML option標籤。當一個SpringMVC select標籤沒有通過items屬性指定自己的數據源的時候,我們就可以在select標籤中通過普通HTML option標籤或者SpringMVC option標籤來指定可以選擇的項。

<form:form action="formTag/form.do" method="post" commandName="user">
    <table>
        <tr>
            <td>最喜歡的運動:</td>
            <td>
               <form:select path="favoriteBall">
                   <option>請選擇</option>
                   <form:option value="1">籃球</form:option>
                   <option value="4">羽毛球</option>
               </form:select>
            </td>
        </tr>
       <tr>
            <td colspan="2"><input type="submit" value="提交"/></td>
        </tr>
    </table>
</form:form>

我們可以看到在上面代碼中我們是沒有指定select標籤的數據源的,而是通過在select標籤體裏面指定普通HTML option標籤和SpringMVC option標籤來指定可選項。


11.textarea標籤

SpringMVC textarea標籤將被渲染爲普通HTML textarea標籤。簡單示例如下:

<form:form action="formTag/form.do" method="post" commandName="user">
    <table>
        <tr>
            <td>自我介紹:</td>
            <td>
               <form:textarea path="introduction" cols="20" rows="10"/>
            </td>
        </tr>
       <tr>
            <td colspan="2"><input type="submit" value="提交"/></td>
        </tr>
    </table>
</form:form>

12.errors標籤

SpringMVC errors標籤是對應於SpringMVC的Errors對象的。它的作用就是用於展現Errors對象中包含的錯誤信息的。我們利用errors標籤來展現Errors的時候是通過errors標籤的path屬性來綁定一個錯誤信息的。我們可以通過path屬性來展現兩種類型的錯誤信息。
l 所有的錯誤信息,這個時候path的值應該置爲“*”
l 當前對象的某一個域的錯誤信息,這個時候path的值應爲所需展現的域的名稱
看下面這樣一個例子:
定義了一個UserValidator對象,專門用來對User對象進行驗證,其代碼如下:

import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;

public class UserValidator implements Validator {

    @Override
    public boolean supports(Class<?> clazz) {
       // TODO Auto-generated method stub
       return User.class.equals(clazz);
    }

    @Override
    public void validate(Object target, Errors errors) {
       // TODO Auto-generated method stub
       ValidationUtils.rejectIfEmpty(errors, "name", null, "Name Is Empty");
       ValidationUtils.rejectIfEmpty(errors, "username", null, "Username Is Empty.");
    }

}

然後我們有這樣一個控制器類:

 @Controller
@RequestMapping("formTag")
public class FormTagController {

    @RequestMapping(value="form", method=RequestMethod.GET)
    public String formTag(Map<String, Object> map) {
       User user = new User();
       map.put("user", user);
       return "formTag/form";
    }

    @InitBinder
    public void initBinder(DataBinder binder) {
       binder.setValidator(new UserValidator());
    }

    @RequestMapping(value="form", method=RequestMethod.POST)
    public String form(@Valid User user, Errors errors) {
       if (errors.hasFieldErrors())
           return "formTag/form";
       return "formTag/submit";
    }
}

我們可以看到我們在上述控制器類中通過DataBinder對象給該類設定了一個用於驗證的UserValidator,這樣當我們請求該控制器的時候UserValidator將生效。

我們有如下這樣一段表單代碼:

<form:form action="formTag/form.do" method="post" commandName="user">
    <table border="1px" bordercolor="blue">
        <tr align="center">
            <td width="100">姓名:</td>
            <td width="150"><form:input path="name"/></td>
        </tr>
        <tr align="center">
            <td>用戶名:</td>
            <td><form:input path="username"/></td>
        </tr>
        <tr>
            <td>所有錯誤信息:</td>
            <td><form:errors path="*"/></td>
        </tr>
        <tr>
            <td>Name的錯誤信息:</td>
            <td><form:errors path="name"/></td>
        </tr>
        <tr align="center">
            <td colspan="2"><input type="submit" value="提交"/></td>
        </tr>
    </table>
</form:form>

當我們提交上面的表單的時候會往Errors中注入兩個錯誤信息

原文鏈接:http://haohaoxuexi.iteye.com/blog/1807330


項目示例可以參考:
SSM框架整合: https://github.com/nl101531/JavaWEB

發佈了25 篇原創文章 · 獲贊 5 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章