目次
BuildTree (フラット データの親子レベルへの変換を完了)
ResponseUtil (データをエコー用の json 形式に変換します)
I.はじめに
このケースを完了する前に、まずツリー メニューとは何かを理解しましょう
1. ツリーメニューとは
ツリー メニューは、大量の情報の階層を整理して表示するためによく使用されるユーザー インターフェイスのデザイン パターンです。この名前は、メニューがツリーの枝やノードに似た構造を表すという事実に由来しています。ツリー メニューは通常、メイン ノードと複数の子ノードで構成され、各子ノードは独自の子ノードを持つことができ、親子関係の階層構造を形成します。
ツリー メニューでは、メイン ノードは最上位のオプションまたはカテゴリを表し、子ノードはメイン ノードに関連付けられた特定のオプションまたはサブカテゴリを表します。ユーザーは、子ノードを展開または折りたたむことで、より詳細なレベルを参照できます。通常、リーフ ノードは最下位のノードで、最も具体的なオプションまたはコンテンツを表します。
ツリー メニューは、複雑なデータや複数レベルのディレクトリを整理して表示する必要があるアプリケーションや Web サイトに適しています。これらにより、ユーザーは簡単にナビゲートして必要なオプションを選択できるようになり、情報を参照してアクセスするためのクリーンで直感的な方法が提供されます。
2. ツリーメニューの利用シーン
ツリー メニューはさまざまなアプリケーションや Web サイトで広く使用されており、一般的な使用シナリオのいくつかを次に示します。
- 1. ファイル リソースの管理: 特にファイルが複数レベルのディレクトリ構造に編成されている場合、ツリー メニューを使用してファイル リソースを参照および管理できます。ユーザーはディレクトリを展開したり折りたたんだりして、目的のファイルやフォルダにアクセスできます。
- 2. 組織構造と人事管理:企業や組織の組織構造をツリーメニューで可視化できます。各ノードは部門、チーム、または個人を表し、ユーザーはノードを展開することでより詳細なレベルと情報を表示できます。
- 3. ナビゲーション メニュー: Web サイトまたはアプリケーションのメイン ナビゲーション メニューは、さまざまなページや機能オプションを表示するためにツリー構造を使用することがよくあります。ユーザーは必要に応じてメニュー項目を展開したり折りたたんだりして、目的の機能やコンテンツをすばやく見つけることができます。
- 4. 製品カテゴリとカタログ ナビゲーション: オンライン ストアや電子商取引サイトでは、製品カテゴリやカテゴリを整理するためにツリー メニューを使用することがよくあります。ユーザーは、さまざまなカテゴリを展開したり折りたたんだりすることで、製品を参照して選択できます。
- 5. コンテンツ管理システム: ツリー メニューは、Web サイトのページ、記事、カテゴリ、タグなどのコンテンツを整理および管理するためにコンテンツ管理システムで広く使用されています。
全体として、ツリー メニューは、階層データを表示および整理して、明確でナビゲートしやすいユーザー インターフェイスを提供する必要があるあらゆるシナリオに適しています。
2. 事例の実現
1. 需要分析
書籍管理システムの左側のリスト表示を完成させたいのですが、肝心なのはテーブルのデザインです。テーブルのデザインはJSPページの表示に関係します。以下は私のテーブルのデザインです。参考までに。 。
テーブルの設計にもデータが必要ですが、むやみにテーブルのデータを書き込むとエラーが発生する可能性がありますので注意してください。!
ここでのデータは非常に特殊です。通常、dao レイヤーを介して取得するデータはフラット データであり、要件を満たしていません。必要なのは親子レベルのデータです。フラット データから親はどうなるかに変換する方法は次のとおりです。子供のデータ?? このとき、テーブルの設計が重要な役割を果たしており、親ノードの pid がすべて 0 であり、子ノードの pid が親ノードの id に対応していることがわかります。
必要なメソッドは 2 つだけであるという結論に達しました。1 つはすべてのメソッドをクエリする (並列データを取得する)、もう 1 つは同じレベルのデータに対して 2 つの走査を実行し、それらを新しいコレクション (親子) に追加することです。最初のトラバーサルは、pid 番号が最上位ノード、つまり親ノードであるデータをコンテナに追加します。2 番目のトラバーサルは、pid と新しいコレクションの id を比較します。それらが同じであれば、それは新しいコレクションの子供たち。
2. 準備作業
注: 詳細については、私が書いたカスタム MVC 三部作を参照してください。
①依存関係をインポートする
必要な依存関係を WebContent➡WEB-INF にインポートします
js/cssを格納するディレクトリを作成し、必要なファイルを配置します。
複数のページで共有されているファイルを公開ファイルにパッケージ化する
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html "> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title></title> <!-- 引入layui.css--> <link rel="stylesheet" href="${pageContext.request.contextPath}/static/js/layui/css/layui.css"> <!-- 引入layui.js--> <script type="text/javascript" src="${pageContext.request.contextPath}/static/js/layui/layui.js"></script> </head> </html>
②道具
ここではツールを使用する必要があります
BaseDao (一般的な CRUD)
package com.zking.util; import java.lang.reflect.Field; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * 所有Dao层的父类 BookDao UserDao OrderDao ... * * @author Administrator * * @param <T> */ public class BaseDao<T> { /** * 适合多表联查的数据返回 * @param sql * @param pageBean * @return * @throws SQLException * @throws InstantiationException * @throws IllegalAccessException */ public List<Map<String, Object>> executeQuery(String sql, PageBean pageBean) throws SQLException, InstantiationException, IllegalAccessException { List<Map<String, Object>> list = new ArrayList<>(); Connection con = DBAccess.getConnection(); PreparedStatement pst = null; ResultSet rs = null; /* * 是否需要分页? 无需分页(项目中的下拉框,查询条件教员下拉框,无须分页) 必须分页(项目中列表类需求、订单列表、商品列表、学生列表...) */ if (pageBean != null && pageBean.isPagination()) { // 必须分页(列表需求) String countSQL = getCountSQL(sql); pst = con.prepareStatement(countSQL); rs = pst.executeQuery(); if (rs.next()) { pageBean.setTotal(String.valueOf(rs.getObject(1))); } // 挪动到下面,是因为最后才处理返回的结果集 // -- sql=SELECT * FROM t_mvc_book WHERE bname like '%圣墟%' // -- pageSql=sql limit (page-1)*rows,rows 对应某一页的数据 // -- countSql=select count(1) from (sql) t 符合条件的总记录数 String pageSQL = getPageSQL(sql, pageBean);// 符合条件的某一页数据 pst = con.prepareStatement(pageSQL); rs = pst.executeQuery(); } else { // 不分页(select需求) pst = con.prepareStatement(sql);// 符合条件的所有数据 rs = pst.executeQuery(); } // 获取源数据 ResultSetMetaData md = rs.getMetaData(); int count = md.getColumnCount(); Map<String, Object> map = null; while (rs.next()) { map = new HashMap<>(); for (int i = 1; i <= count; i++) { // map.put(md.getColumnName(i), rs.getObject(i)); map.put(md.getColumnLabel(i), rs.getObject(i)); } list.add(map); } return list; } /** * * @param sql * @param attrs * map中的key * @param paMap * jsp向后台传递的参数集合 * @return * @throws SQLException * @throws NoSuchFieldException * @throws SecurityException * @throws IllegalArgumentException * @throws IllegalAccessException */ public int executeUpdate(String sql, String[] attrs, Map<String, String[]> paMap) throws SQLException, NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { Connection con = DBAccess.getConnection(); PreparedStatement pst = con.prepareStatement(sql); for (int i = 0; i < attrs.length; i++) { pst.setObject(i + 1, JsonUtils.getParamVal(paMap, attrs[i])); } return pst.executeUpdate(); } /** * 批处理 * @param sqlLst * @return */ public static int executeUpdateBatch(String[] sqlLst) { Connection conn = null; PreparedStatement stmt = null; try { conn = DBAccess.getConnection(); // 设置不自动提交 conn.setAutoCommit(false); for (String sql : sqlLst) { stmt = conn.prepareStatement(sql); stmt.executeUpdate(); } conn.commit(); } catch (Exception e) { try { conn.rollback(); } catch (SQLException e1) { e1.printStackTrace(); throw new RuntimeException(e1); } e.printStackTrace(); throw new RuntimeException(e); } finally { DBAccess.close(conn, stmt, null); } return sqlLst.length; } /** * 通用的增删改方法 * * @param book * @throws Exception */ public int executeUpdate(String sql, T t, String[] attrs) throws Exception { // String[] attrs = new String[] {"bid", "bname", "price"}; Connection con = DBAccess.getConnection(); PreparedStatement pst = con.prepareStatement(sql); // pst.setObject(1, book.getBid()); // pst.setObject(2, book.getBname()); // pst.setObject(3, book.getPrice()); /* * 思路: 1.从传进来的t中读取属性值 2.往预定义对象中设置了值 * * t->book f->bid */ for (int i = 0; i < attrs.length; i++) { Field f = t.getClass().getDeclaredField(attrs[i]); f.setAccessible(true); pst.setObject(i + 1, f.get(t)); } return pst.executeUpdate(); } /** * 通用分页查询 * * @param sql * @param clz * @return * @throws Exception */ public List<T> executeQuery(String sql, Class<T> clz, PageBean pageBean) throws Exception { List<T> list = new ArrayList<T>(); Connection con = DBAccess.getConnection(); ; PreparedStatement pst = null; ResultSet rs = null; /* * 是否需要分页? 无需分页(项目中的下拉框,查询条件教员下拉框,无须分页) 必须分页(项目中列表类需求、订单列表、商品列表、学生列表...) */ if (pageBean != null && pageBean.isPagination()) { // 必须分页(列表需求) String countSQL = getCountSQL(sql); pst = con.prepareStatement(countSQL); rs = pst.executeQuery(); if (rs.next()) { pageBean.setTotal(String.valueOf(rs.getObject(1))); } // 挪动到下面,是因为最后才处理返回的结果集 // -- sql=SELECT * FROM t_mvc_book WHERE bname like '%圣墟%' // -- pageSql=sql limit (page-1)*rows,rows 对应某一页的数据 // -- countSql=select count(1) from (sql) t 符合条件的总记录数 String pageSQL = getPageSQL(sql, pageBean);// 符合条件的某一页数据 pst = con.prepareStatement(pageSQL); rs = pst.executeQuery(); } else { // 不分页(select需求) pst = con.prepareStatement(sql);// 符合条件的所有数据 rs = pst.executeQuery(); } while (rs.next()) { T t = clz.newInstance(); Field[] fields = clz.getDeclaredFields(); for (Field f : fields) { f.setAccessible(true); f.set(t, rs.getObject(f.getName())); } list.add(t); } return list; } /** * 将原生SQL转换成符合条件的总记录数countSQL * * @param sql * @return */ private String getCountSQL(String sql) { // -- countSql=select count(1) from (sql) t 符合条件的总记录数 return "select count(1) from (" + sql + ") t"; } /** * 将原生SQL转换成pageSQL * * @param sql * @param pageBean * @return */ private String getPageSQL(String sql, PageBean pageBean) { // (this.page - 1) * this.rows // pageSql=sql limit (page-1)*rows,rows return sql + " limit " + pageBean.getStartIndex() + "," + pageBean.getRows(); } }
BuildTree (フラット データの親子レベルへの変換を完了)
package com.zking.util; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; public class BuildTree { /** * 默认-1为顶级节点 * @param nodes * @param <T> * @return */ public static <T> TreeVo<T> build(List<TreeVo<T>> nodes) { if (nodes == null) { return null; } List<TreeVo<T>> topNodes = new ArrayList<TreeVo<T>>(); for (TreeVo<T> children : nodes) { String pid = children.getParentId(); if (pid == null || "-1".equals(pid)) { topNodes.add(children); continue; } for (TreeVo<T> parent : nodes) { String id = parent.getId(); if (id != null && id.equals(pid)) { parent.getChildren().add(children); children.setHasParent(true); parent.setChildren(true); continue; } } } TreeVo<T> root = new TreeVo<T>(); if (topNodes.size() == 1) { root = topNodes.get(0); } else { root.setId("000"); root.setParentId("-1"); root.setHasParent(false); root.setChildren(true); root.setChecked(true); root.setChildren(topNodes); root.setText("顶级节点"); Map<String, Object> state = new HashMap<>(16); state.put("opened", true); root.setState(state); } return root; } /** * 指定idparam为顶级节点 * @param nodes * @param idParam * @param <T> * @return */ public static <T> List<TreeVo<T>> buildList(List<TreeVo<T>> nodes, String idParam) { if (nodes == null) { return null; } List<TreeVo<T>> topNodes = new ArrayList<TreeVo<T>>(); for (TreeVo<T> children : nodes) { String pid = children.getParentId(); if (pid == null || idParam.equals(pid)) { topNodes.add(children); continue; } for (TreeVo<T> parent : nodes) { String id = parent.getId(); if (id != null && id.equals(pid)) { parent.getChildren().add(children); children.setHasParent(true); parent.setChildren(true); continue; } } } return topNodes; } }
ResponseUtil (データをエコー用の json 形式に変換します)
package com.zking.util; import java.io.PrintWriter; import javax.servlet.http.HttpServletResponse; import com.fasterxml.jackson.databind.ObjectMapper; public class ResponseUtil { public static void write(HttpServletResponse response,Object o)throws Exception{ response.setContentType("text/html;charset=utf-8"); PrintWriter out=response.getWriter(); out.println(o.toString()); out.flush(); out.close(); } public static void writeJson(HttpServletResponse response,Object o)throws Exception{ ObjectMapper om = new ObjectMapper(); // om.writeValueAsString(o)代表了json串 write(response, om.writeValueAsString(o)); } }
③エンティティの書き込み
権限(データテーブルエンティティ)
package com.xw.entity; /**书籍管理系统实体 * @author 索隆 * */ public class Permission { private long id; private String name; private String description; private String url; private long pid; private int ismenu; private long displayno; public Permission() { // TODO Auto-generated constructor stub } public Permission(long id, String name, String description, String url, long pid, int ismenu, long displayno) { super(); this.id = id; this.name = name; this.description = description; this.url = url; this.pid = pid; this.ismenu = ismenu; this.displayno = displayno; } public long getId() { return id; } public void setId(long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public long getPid() { return pid; } public void setPid(long pid) { this.pid = pid; } public int getIsmenu() { return ismenu; } public void setIsmenu(int ismenu) { this.ismenu = ismenu; } public long getDisplayno() { return displayno; } public void setDisplayno(long displayno) { this.displayno = displayno; } @Override public String toString() { return "Permission [id=" + id + ", name=" + name + ", description=" + description + ", url=" + url + ", pid=" + pid + ", ismenu=" + ismenu + ", displayno=" + displayno + "]"; } }
注: mysql の bigint 型は Java では long 型です。
3.daoレイヤー書き込み
package com.xw.dao; import java.util.ArrayList; import java.util.List; import com.fasterxml.jackson.databind.ObjectMapper; import com.xw.entity.Permission; import com.zking.util.BaseDao; import com.zking.util.BuildTree; import com.zking.util.TreeVo; /**书记管理 * @author 索隆 * */ public class PermissionDao extends BaseDao<Permission>{ /**获取书籍管理的所有信息(平级数据) * @return * @throws Exception */ public List<Permission> list() throws Exception{ String sql="select * from t_easyui_permission"; return super.executeQuery(sql, Permission.class, null); } /**将平级数据变成我们需要的父子级数据 * @return * @throws Exception */ public List<TreeVo<Permission>> menu() throws Exception{ //存放父子级的容器 List<TreeVo<Permission>> menu=new ArrayList<TreeVo<Permission>>(); //拿到平级数据 List<Permission> list = this.list(); //遍历平级数据 for (Permission permission : list) { //工具类帮助我们完成父子级关系 TreeVo<Permission> vo=new TreeVo<Permission>(); vo.setId(permission.getId()+""); vo.setParentId(permission.getPid()+""); vo.setText(permission.getName()); menu.add(vo); } //通过工具类筛选父级菜单的儿子,pid为0是父级菜单 return BuildTree.buildList(menu, "0"); } public static void main(String[] args) throws Exception { //测试数据 PermissionDao d=new PermissionDao(); List<TreeVo<Permission>> menu = d.menu(); ObjectMapper om =new ObjectMapper(); System.out.println(om.writeValueAsString(menu)); } }
2 つのデータセットの違いを比較できます。
- フラットデータ
- 親子データ
4. サーブレット層の記述
package com.xw.web; import java.util.List; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.xw.dao.PermissionDao; import com.xw.entity.Permission; import com.zking.framework.ActionSupport; import com.zking.framework.ModelDriver; import com.zking.util.ResponseUtil; import com.zking.util.TreeVo; /**书籍管理的servlet处理 * @author 索隆 * */ public class PermissionAction extends ActionSupport implements ModelDriver<Permission>{ private Permission Permission=new Permission(); private PermissionDao pdao=new PermissionDao(); /**初始化书籍管理系统的动态树 * @param req * @param resp * @throws Exception */ public void listmenu(HttpServletRequest req, HttpServletResponse resp) throws Exception { //查询父子级数据 List<TreeVo<Permission>> menu = pdao.menu(); //将集合转换成json格式进行回显 ResponseUtil.writeJson(resp, menu); } @Override public Permission getModel() { return Permission; } }
サーブレットを作成した後は、設定ファイルを作成することを忘れないでください。
<?xml version="1.0" encoding="UTF-8"?> <config> <action path="/blog" type="com.zking.web.BlogAction"> <forward name="list" path="/blogList.jsp" redirect="false" /> <forward name="toList" path="/blog.action?methodName=list" redirect="true" /> <forward name="toEdit" path="/blogEdit.jsp" redirect="false" /> </action> <!--用户--> <action path="/user" type="com.xw.web.Useraction"> </action> <!-- 书籍管理——树 --> <action path="/Permission" type="com.xw.web.PermissionAction"> </action> </config>
5. jspページの構築
現時点では、オンラインの Layui ウェブサイトにアクセスして、満足のいく JSP ページを見つけることができます。
Layui の開発および使用に関するドキュメント - スタート ガイド
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE> <html> <head> <%@ include file="common/static.jsp"%> <script type="text/javascript" src="js/index.js"></script> </head> <body> <div class="layui-layout layui-layout-admin"> <div class="layui-header"> <div class="layui-logo layui-hide-xs layui-bg-black">书籍管理系统</div> <!-- 头部区域(可配合layui 已有的水平导航) --> <ul class="layui-nav layui-layout-left"> <!-- 移动端显示 --> <li class="layui-nav-item layui-show-xs-inline-block layui-hide-sm" lay-header-event="menuLeft"><i class="layui-icon layui-icon-spread-left"></i></li> <!-- Top导航栏 --> <li class="layui-nav-item layui-hide-xs"><a href="">nav 1</a></li> <li class="layui-nav-item layui-hide-xs"><a href="">nav 2</a></li> <li class="layui-nav-item layui-hide-xs"><a href="">nav 3</a></li> <li class="layui-nav-item"><a href="javascript:;">nav groups</a> <dl class="layui-nav-child"> <dd> <a href="">menu 11</a> </dd> <dd> <a href="">menu 22</a> </dd> <dd> <a href="">menu 33</a> </dd> </dl></li> </ul> <!-- 个人头像及账号操作 --> <ul class="layui-nav layui-layout-right"> <li class="layui-nav-item layui-hide layui-show-md-inline-block"> <a href="javascript:;"> <img src="//tva1.sinaimg.cn/crop.0.0.118.118.180/5db11ff4gw1e77d3nqrv8j203b03cweg.jpg" class="layui-nav-img"> tester </a> <dl class="layui-nav-child"> <dd> <a href="">Your Profile</a> </dd> <dd> <a href="">Settings</a> </dd> <dd> <a href="login.jsp">Sign out</a> </dd> </dl> </li> <li class="layui-nav-item" lay-header-event="menuRight" lay-unselect> <a href="javascript:;"> <i class="layui-icon layui-icon-more-vertical"></i> </a> </li> </ul> </div> <div class="layui-side layui-bg-black"> <div class="layui-side-scroll"> <!-- 左侧导航区域(可配合layui已有的垂直导航) --> <ul id="menu" class="layui-nav layui-nav-tree" lay-filter="menu"> <!-- <li class="layui-nav-item layui-nav-itemed"> <a class="" href="javascript:;">menu group 1</a> <dl class="layui-nav-child"> <dd><a href="javascript:;">menu 1</a></dd> <dd><a href="javascript:;">menu 2</a></dd> <dd><a href="javascript:;">menu 3</a></dd> <dd><a href="">the links</a></dd> </dl> </li> <li class="layui-nav-item"> <a href="javascript:;">menu group 2</a> <dl class="layui-nav-child"> <dd><a href="javascript:;">list 1</a></dd> <dd><a href="javascript:;">list 2</a></dd> <dd><a href="">超链接</a></dd> </dl> </li> <li class="layui-nav-item"><a href="javascript:;">click menu item</a></li> <li class="layui-nav-item"><a href="">the links</a></li> --> </ul> </div> </div> <div class="layui-body"> <!-- 内容主体区域 --> <div style="padding: 15px;">内容主体区域。记得修改 layui.css 和 js 的路径</div> </div> <div class="layui-footer"> <!-- 底部固定区域 --> 底部固定区域 </div> </div> <script> //JS layui.use(['element', 'layer', 'util'], function(){ var element = layui.element ,layer = layui.layer ,util = layui.util ,$ = layui.$; $.ajax({ url: "${pageContext.request.contextPath}/Permission.action?methodName=listmenu", type: 'post', dataType: 'json', success: function(data) { //定义一个变量将回显的数据进行拼接,最终追加到指定标签上 var str=''; $.each(data,function(i,n){ str+='<li class="layui-nav-item layui-nav-itemed">'; str+=' <a class="" href="javascript:;">'+n.text+'</a>'; //判断有无children节点有就遍历 if(n.hasChildren){ //有children节点拿到children节点 var children=n.children; str+='<dl class="layui-nav-child">'; $.each(children,function(idx,node){ str+='<dd><a href="javascript:;">'+node.text+'</a></dd>'; }) str+='</dl>'; } str+='</li>'; }) //将拼接内容追加到指定ul标签 $("#menu").html(str); element.render('menu'); } }) }); </script> </body> </html>
チップ:
element.render() の有用性:
特定のフロントエンド フレームワークまたはライブラリを使用しているとします。ここで、`element` は対応するコンポーネントまたはオブジェクトであり、`render()` メソッドを呼び出します。一般に、`render()` メソッドは、Web ページ内の特定の位置にコンポーネントまたは要素をレンダリングするために使用されます。具体的な機能は以下の通りです。
- 1. コンポーネントのレンダリング: `render()` メソッドは、特定のコンポーネントをページにレンダリングするために使用されます。コンポーネントのコンテンツとスタイルを解析して、対応する HTML 構造を生成し、ブラウザーで表示できるようにします。
- 2. ページを更新します。コンポーネントまたは要素がすでに存在し、ページに表示されている場合は、`render()` を再度呼び出すと、ページの更新をトリガーできます。これは、データの変更に基づいてページを更新する必要がある場合に便利です。
- 3. イベントのバインド: コンポーネントをレンダリングするとき、通常、`render()` メソッドはコンポーネントのイベントを対応する DOM 要素にバインドします。これにより、ユーザーがコンポーネントと対話するときに、コンポーネントが適切に応答することが保証されます。
- `render()` メソッドの具体的な実装と使用法は、使用されるフロントエンド フレームワークまたはライブラリに依存し、追加のパラメーターやオプションがある場合があることに注意してください。「render()」メソッドが何を行うのかを正確に理解するには、それぞれのフレームワークまたはライブラリのドキュメントと使用ガイドを参照してください。
6. 事例紹介
私の共有はここで終わりです。議論やコミュニケーションのためにコメントエリアへようこそ!!
役に立ったと思ったら、いいね、愛をお願いします♥ ♥