【C#MVC4】菜单管理和访问权限分配(四)

【C#MVC4】菜单管理和访问权限分配(四)

这一篇,介绍菜单的管理功能。
上一篇中,我们完成了过渡,明白了本例是通过读取xml文件的方式加载菜单到easyui的tree控件上的。也自己模拟了而一个菜单(手动创建),菜单最终肯定是要从数据库中加载而得,由于数据库目前是空的,暂时不做这一块的东西。等到项目最后再回头做。这里使用这个模拟的菜单做 菜单的创建工作。

按照习惯,从后往前书写,做菜单管理就是对Menu_list的正删改查:

Service层:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Arise.Model;
using Arise.Common;

namespace Arise.Service
{
    public class Menu_List_Service
    {
        public IList<Menu_List> GetAllMenuList() 
        {
            using(AriseEntities ariseEntities = new AriseEntities())
            {
                return ariseEntities.Menu_List.ToList(); 
            }
        }

        public Menu_List GetMenuListById(Guid MenuListId)
        {
            using(AriseEntities ariseEntities = new AriseEntities())
            {
                return ariseEntities.Menu_List.Where(ml => ml.Menu_List_Id == MenuListId).FirstOrDefault();
            }
        }

        public void SaveMenuList(Menu_List menuList,string saveType) 
        {
            using(AriseEntities ariseEntities = new AriseEntities())
            {
                try 
                {
                    if(saveType.ToUpper() == "ADD")
                    {
                        ariseEntities.Menu_List.Add(menuList);
                    }
                    else
                    {
                        ariseEntities.Entry(menuList).State = System.Data.Entity.EntityState.Modified;
                    }
                    ariseEntities.SaveChanges();
                }
                catch(Exception ex)
                {
                    throw ex;
                }
            }
        }

        public void DeleteMenuList(Guid menuListId) 
        {
            using(AriseEntities ariseEntities = new AriseEntities())
            {
                //这里最好加上当前删除的链接是否有用户在使用的业务
                //....
                Menu_List menuList = ariseEntities.Menu_List.Where(ml => ml.Menu_List_Id == menuListId).FirstOrDefault();
                ariseEntities.Entry(menuList).State = System.Data.Entity.EntityState.Deleted;
                ariseEntities.SaveChanges();
            }
        }
    }
}

上面的方法很明显了, GetMenuListById():通过Id获取Menu对象。 DeleteMenuList():删除Menu,这里做的不是很完善,当期按钮若存在用户使用时,不应该删除成功的,但是这里没做这个业务,有兴趣的可以自己判断一下,就是去关联查询Menu_List_Access,若存在则不允许删除即可。

Manager层:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Arise.Model;
using Arise.Service;

namespace Arise.Manager
{
    public class Menu_List_Manager
    {
        private Menu_List_Service menuListService = new Menu_List_Service();

        public IList<Menu_List> GetAllMenuList()
        {
            return menuListService.GetAllMenuList();
        }

        public Menu_List GetMenuListById(string MenuListId)
        {
            if (MenuListId == "")
            {
                return null;
            }
            return menuListService.GetMenuListById(Guid.Parse(MenuListId));
        }

        public void SaveMenuList(Menu_List menuList, string saveType)
        {
            try 
            {
                menuListService.SaveMenuList(menuList,saveType);
            }
            catch(Exception ex)
            {
                throw ex;
            }
        }

        public void DeleteMenuList(string menuListId)
        {
            if (menuListId != "")
            {
                menuListService.DeleteMenuList(Guid.Parse(menuListId));
            }
        }
    }
}

这里什么赘述的,就是简单的调用,值得一提的是,因为数据库Id字段为uniqueidentifier,这里需要使用Guid。

重点来了,一大波代码正在前进:
我先将Controller的代码和View的发出来,两相结合,讲解:

Controller层:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Arise.Common;
using Arise.Model;
using Arise.Manager;

namespace Arise.Web.Controllers
{
    public class AdminController : BaseController
    {
        //
        // GET: /Admin/

        public ActionResult Home()
        {
            User_Master userMaster = (User_Master)Session["UserMaster"];
            ViewBag.User = userMaster.User_Name;
            ViewBag.Date = DateTime.Now;
            string userMasterId = userMaster.User_Master_Id.ToString();
            string userMenuPath = System.Configuration.ConfigurationManager.AppSettings["UserMenuPath"].ToString().Trim();
            string xmlPath = userMenuPath + userMasterId + ".xml";
            ViewBag.MenuTreeList = MenuXMLHelper.LoadMenuXML(xmlPath);
            return View();
        }
        public ActionResult Index()
        {
            return View();
        }

        #region Menu List Page

        public ActionResult MenuList() 
        {
            return View();
        }

        /*
         *获取菜单信息,并将菜单信息拼接成tree能够识别的字符串 
         * */
        public string GeneratePermission() 
        {
            string type = Request.Params["Type"].ToString().Trim();
            Menu_List_Manager menuListManager = new Menu_List_Manager();
            List<Menu_List> listMenu = menuListManager.GetAllMenuList().ToList();
            string permissionList = "[";
            //若menu_Level为空则表示为菜单父分类
            var parentlist = listMenu.Where(p => p.Menu_Level.ToString() == "").OrderBy(p => p.Serial).ToList();
            for (int i = 0; i < parentlist.Count; i++)
            {
                if (type != "")//表示为菜单管理页面
                {
                    permissionList += "{\"id\":\"" + parentlist[i].Menu_List_Id.ToString().Trim() + "\",\"text\":\"" + parentlist[i].Menu_Name.ToString().Trim() + " /Serial:" + parentlist[i].Serial.ToString().Trim() + "\",\"iconCls\":\"" + parentlist[i].Icon_Name.ToString().Trim() + "\",\"attributes\":{\"level\":\"1\"}";
                }
                else//表示为菜单权限分配页面
                {
                    permissionList += "{\"id\":\"" + parentlist[i].Menu_List_Id.ToString().Trim() + "\",\"text\":\"" + parentlist[i].Menu_Name.ToString().Trim() + "\",\"iconCls\":\"" + parentlist[i].Icon_Name.ToString().Trim() + "\",\"attributes\":{\"level\":\"1\"}";
                }
                //若menu_list不为空,则为父分类的ID,
                var childlist = listMenu.Where(p => p.Menu_Level == parentlist[i].Menu_List_Id.ToString()).OrderBy(p => p.Serial).ToList();
                if (childlist.Count != 0)
                {
                    permissionList += ",\"children\":[";
                }
                for (int j = 0; j < childlist.Count; j++)
                {
                    if (type != "")
                    {
                        permissionList += "{\"id\":\"" + childlist[j].Menu_List_Id.ToString().Trim() + "\",\"text\":\"" + childlist[j].Menu_Name.ToString().Trim() + " /Serial:" + childlist[j].Serial.ToString().Trim() + "\",\"iconCls\":\"" + parentlist[i].Icon_Name.ToString().Trim() + "\",\"attributes\":{\"level\":\"2\"}},";
                    }
                    else
                    {
                        permissionList += "{\"id\":\"" + childlist[j].Menu_List_Id.ToString().Trim() + "\",\"text\":\"" + childlist[j].Menu_Name.ToString().Trim() + "\",\"iconCls\":\"" + parentlist[i].Icon_Name.ToString().Trim() + "\",\"attributes\":{\"level\":\"2\"}},";
                    }
                    if (childlist.Count != 0 && j == childlist.Count - 1)
                    {
                        permissionList = permissionList.Substring(0, permissionList.Length - 1);
                        permissionList += "]";
                    }
                }
                permissionList += "},";
            }
            permissionList = permissionList.Substring(0, permissionList.Length - 1);
            permissionList += "]";

            return permissionList;
        }

        public JsonResult SaveMenuList() 
        {
            try 
            {
                string saveType = Request.Params["SaveType"].ToString().Trim();
                string menuListId = Request.Params["MenuListId"].ToString().Trim();
                string requestURL = Request.Params["RequestURL"].ToString().Trim();
                string menuName = Request.Params["MenuName"].ToString().Trim();
                int serial = Utility.ConvertToInt32(Request.Params["Serial"].ToString().Trim());
                string menuLevel = Request.Params["MenuLevel"].ToString().Trim();
                string iconName = "icon-system";

                Menu_List_Manager menuListManager = new Menu_List_Manager();
                Menu_List menuList = null;
                if (menuListId != null)
                {
                    menuList = menuListManager.GetMenuListById(menuListId);
                }
                if (saveType.ToUpper() == "ADD")
                {
                    if (menuList != null)
                    {
                        Utility.ReturnJsonResult("0", UserMessageShow.Invaild);
                    }
                    menuList = new Menu_List();
                    menuList.Menu_List_Id = Guid.NewGuid();
                    menuList.Request_URL = requestURL;
                    menuList.Menu_Name = menuName;
                    menuList.Menu_Level = menuLevel;
                    menuList.Serial = serial;
                    menuList.Icon_Name = iconName;
                }
                else
                {
                    if (menuList == null)
                    {
                        Utility.ReturnJsonResult("0", UserMessageShow.Invaild);
                    }
                    menuList.Request_URL = requestURL;
                    menuList.Menu_Name = menuName;
                    menuList.Menu_Level = menuLevel;
                    menuList.Serial = serial;
                    menuList.Icon_Name = iconName;
                }
                menuListManager.SaveMenuList(menuList, saveType);
                return Utility.ReturnJsonResult("1", UserMessageShow.SaveSuccess);
            }
            catch(Exception ex)
            {
                return Utility.ReturnJsonResult("0",ex.Message);
            }
        }

        public JsonResult GetMenuByMenuId() 
        {
            try 
            {
                string menuListId = Request.Params["MenuListId"].ToString().Trim();
                Menu_List_Manager menuListManager = new Menu_List_Manager();
                Menu_List menuList = menuListManager.GetMenuListById(menuListId);
                return Utility.ReturnJsonResult<Menu_List>(menuList);
            }
            catch(Exception ex)
            {
                return Utility.ReturnJsonResult("0",ex.Message);
            }
        }

        public JsonResult DeleteMenuByMenuId() 
        {
            string menuListId = Request.Params["MenuListId"].ToString().Trim();
            Menu_List_Manager menuListManager = new Menu_List_Manager();
            menuListManager.DeleteMenuList(menuListId);
            return Utility.ReturnJsonResult("0", UserMessageShow.DeleteSuccess);
        }
        #endregion
    }
}

在代码段#region Menu List Page下为MenuList页面对应的action。

View层:

@{
    ViewBag.Title = "Menu List";
    Layout = "~/Views/Shared/_Layout.cshtml";
}
<script type="text/javascript">
    function AddParentMenu()
    {<!-- -->
        $("#AddMenuInformation").dialog({
            title: 'Add Menu Information',
            width: 500,
            height: 250
        });

        $('#trParentName').css("display", "none");
        $('#trRequestURL').css("display", "none");
        $('#hfSaveType').val("ADD");
        $('#hfParentId').val("");

        $("#txtMenuName").focus();
        $('#txtParentMenuName').val("");
        $('#hfMenuId').val("");
        $('#txtMenuName').val('');
        $('#txtMenuRequestURL').val('');
        $('#txtSerialNumber').val('');
        $("#AddMenuInformation").dialog('open');
    }
    function AddMenu() {<!-- -->
        var node = $('#tt').tree('getSelected');
        if (node == null) {
            alert("Please select a menu Item!");
        }
        if (node.attributes.level == "2") {
            alert("This menu can not be added child menu!");
            return;
        }
        $('#hfParentId').val(node.id);
        $("#AddMenuInformation").dialog({
            title: 'Add Menu Information',
            width: 500,
            height: 250
        });
        $('#trParentName').css("display", "");
        $('#trRequestURL').css("display", "");
        $('#hfSaveType').val("ADD");

        $("#txtMenuName").focus();
        $('#txtParentMenuName').val(node.text.substring(0, node.text.indexOf("/")));
        $('#hfMenuId').val("");
        $('#txtMenuName').val('');
        $('#txtMenuRequestURL').val('');
        $('#txtSerialNumber').val('');
        $("#AddMenuInformation").dialog('open');
    }
    function SaveMenuInfo() {<!-- -->
        var type = $('#hfSaveType').val();
        var menuId = $('#hfMenuId').val();
        var parentMenuId = $('#hfParentId').val();
        var menuName = $('#txtMenuName').val();
        var menuRequestURL = $('#txtMenuRequestURL').val();
        var serialNumber = $('#txtSerialNumber').val();
        $.ajax({
            url: '/Admin/SaveMenuList',
            data: {
                SaveType: type, MenuListId: menuId, MenuLevel: parentMenuId, MenuName: menuName, RequestURL: menuRequestURL, Serial: serialNumber
            },
            type: 'post',
            dataType: 'json',
            success: function (data) {<!-- -->
                if (data.result == "0") {
                    alert(data.message);
                } else {
                    $("#AddMenuInformation").dialog('close');
                    alert(data.message);
                    $('#tt').tree('reload');
                }
            }
        });
    }
    function DeleteMenu() {<!-- -->
        var node = $('#tt').tree('getSelected');
        if (node.attributes.level == "1") {
            alert("This menu can not be deleted!");
            return;
        }
        if (confirm("Are you sure delete this menu?")) {
            $.ajax({
                url: '/Admin/DeleteMenuByMenuId',
                data: {
                    MenuListId: node.id
                },
                type: 'post',
                dataType: 'json',
                success: function (data) {<!-- -->
                    if (data.result == "0") {
                        alert(data.message);
                        $('#tt').tree('reload');
                    } else {
                        alert(data.message);
                    }
                }
            });
        }
    }

    function EditMenu() {<!-- -->
        var node = $('#tt').tree('getSelected');
        if (node == null) {
            alert("Please select a menu Item!");
        }
        if (node.attributes.level == "1") {
            return;
        }
        var nodeParent = $('tt').tree('getParent', node.target)
        $('#hfMenuId').val(node.id);
        $('#hfParentId').val(nodeParent.id);
        $.ajax({
            url: '/Admin/GetMenuByMenuId',
            data: {
                MenuListId: node.id
            },
            type: 'post',
            dataType: 'json',
            success: function (data) {<!-- -->
                if (data.result == "1") {
                    alert(data.message);
                } else {
                    $("#AddMenuInformation").dialog({
                        title: 'Edit Menu Information',
                        width: 500,
                        height: 250
                    });
                    $('#trParentName').css("display", "");
                    $('#trRequestURL').css("display", "");
                    $('#hfSaveType').val("Update");

                    $('#txtParentMenuName').val(nodeParent.text.substring(0, nodeParent.text.indexOf("/")));

                    $('#txtMenuName').val(data.Menu_Name);
                    $('#txtMenuRequestURL').val(data.Request_URL);
                    $('#txtSerialNumber').val(data.Serial);
                    $("#AddMenuInformation").dialog('open');
                }
            }
        });
    }

</script>
<div style="padding-top: 2px; padding-left: 2px;font-size:8px;">
    <div id="dQueryPermissionList" class="easyui-panel" style="float: left; width: 360px; height: 'auto';
        padding: 10px; background-color: #FFFFFF;" title="Menu List">

        <div style="width: 330px; height: 450px; overflow: auto;">
            <ul id="tt" class="easyui-tree" data-options="url:'/Admin/GeneratePermission?Type=S',method:'get',animate:true"></ul>
        </div>

    </div>
    <div style="margin-top:15px;">
        <a href="#" class="easyui-linkbutton" data-options="iconCls:'icon-add'" onclick="AddMenu();" style="margin-right: 18px;">Add</a>
        <a href="#" class="easyui-linkbutton" data-options="iconCls:'icon-edit'" onclick="EditMenu();" style="margin-right: 18px;">Edit</a>
        <a href="#" class="easyui-linkbutton" data-options="iconCls:'icon-cancel'" onclick="DeleteMenu();" style="margin-right: 18px;">Delete</a>
        <a href="#" class="easyui-linkbutton" data-options="iconCls:'icon-cancel'" onclick="AddParentMenu();" style="margin-right: 18px;">AddParentMenu</a>
    </div>
    <div id="AddMenuInformation" class="easyui-dialog" closed="true" buttons="#AddMenuInformation-Buttons"
         style="padding: 10px 20px">
        <table>
            <tr id="trParentName">
                <td>
                    Parent Menu Name:
                </td>
                <td>
                    <input type="text" id="txtParentMenuName" name="txtParentMenuName" class="easyui-textbox" style="width: 238px" readonly="readonly" />
                    <input type="hidden" id="hfParentId" name="hfParentId" />
                    <input type="hidden" id="hfMenuId" name="hfMenuId" />
                    <input type="hidden" id="hfSaveType" name="hfSaveType" />
                </td>
            </tr>
            <tr>
                <td>
                    Menu Name:
                </td>
                <td>
                    <input type="text" id="txtMenuName" name="txtMenuName" class="easyui-textbox" style="width: 238px" />
                </td>
            </tr>
            <tr id="trRequestURL">
                <td>
                    Menu Request URL:
                </td>
                <td>
                    <input type="text" id="txtMenuRequestURL" name="txtMenuRequestURL" class="easyui-textbox" style="width: 238px" />
                </td>
            </tr>
            <tr>
                <td>
                    Serial Number:
                </td>
                <td>
                    <input type="text" id="txtSerialNumber" name="txtSerialNumber" class="easyui-textbox" style="width: 238px" onkeyup="onlyNumber(this)" onafterpaste="onlyNumber(this)" />
                </td>
            </tr>
        </table>
    </div>
    <div id="AddMenuInformation-Buttons">
        <a href="#" class="easyui-linkbutton" iconcls="icon-ok" onclick="SaveMenuInfo();">
            Save
        </a> <a href="#" class="easyui-linkbutton" iconcls="icon-cancel" onclick="javascript: $('#AddMenuInformation').dialog('close');">
            Close
        </a>
    </div>
</div>

页面显示:

这里写图片描述

这里写图片描述

好了,代码贴完了,讲解开始,
在页面我们看到tree是通过下面这个代码加载的:

 <div style="width: 330px; height: 400px; overflow: auto;">
            <ul id="tt" class="easyui-tree" data-options="url:'/Admin/GeneratePermission?Type=S',method:'get',animate:true"></ul>
        </div>

在页面easyui通过tree控价访问url获取菜单信息,最终展示,这个返回的数据easyui是有规范的,他给提供一些可选参数,大致类似这种:

[{
    "id":"5686e272-cc26-4039-8519-705baa2ab88d",
    "text":"Setting /Serial:1",
    "iconCls":"icon-system",
    "children":[{
            "id":"9c40bb25-92cb-40c8-a7cf-59f2e11c89ba",
            "text":"Test /Serial:1",
            "iconCls":"icon-system",
            "attributes":{"level":"2"}},
            {"id":"dc686259-11dc-4dbf-997f-c823c55387fd",
            "text":"Test2 /Serial:2",
            "iconCls":"icon-system",
            "attributes":{"level":"2"}}]
}]

一串json,这个参数标准是easyui提供的,不是我们随意定义的,这个具体还有那些参数可选呢?
每个节点可以包括下列属性:(这个是easyui官网提供的可选参数)

id:节点的 id,它对于加载远程数据很重要。 state:节点状态,’open’ 或 ‘closed’,默认是 ‘open’。当设置为 ‘closed’ 时,该节点有子节点,并且将从远程站点加载它们。 attributes:给一个节点添加的自定义属性。
children:定义了一些子节点的节点数组

官网提供的一个Demo:

    [{
        "id":1,
        "text":"Folder1",
        "iconCls":"icon-save",
        "children":[{
            "text":"File1",
            "checked":true
        },{
            "text":"Books",
            "state":"open",
            "attributes":{
                "url":"/demo/book/abc",
                "price":100
            },
            "children":[{
                "text":"PhotoShop",
                "checked":true
            },{
                "id": 8,
                "text":"Sub Bookds",
                "state":"closed"
            }]
        }]
    },{
        "text":"Languages",
        "state":"closed",
        "children":[{
            "text":"Java"
        },{
            "text":"C#"
        }]
    }]

根据你项目的需要你可以选择性的添加或是删除某些不重要的属性。所以这里你应该知道了,服务器端有两种实现方式,一种是你建立对应的Model,然后使用Utility里面的Object直接转成json;一种是自己拼接字符串。
第二种看似很傻,但是对于这种复用不是很强的业务,是没必要建立Model的,所以这里直接拼接字符串。

获取所有列表,并将列表信息,转换成easyui的tree等够识别的json字符:

    public string GeneratePermission() 
        {
            string type = Request.Params["Type"].ToString().Trim();
            Menu_List_Manager menuListManager = new Menu_List_Manager();
            List<Menu_List> listMenu = menuListManager.GetAllMenuList().ToList();
            string permissionList = "[";
            //若menu_Level为空则表示为菜单父分类
            var parentlist = listMenu.Where(p => p.Menu_Level.ToString() == "").OrderBy(p => p.Serial).ToList();
            for (int i = 0; i < parentlist.Count; i++)
            {
                if (type != "")//表示为菜单管理页面
                {
                    permissionList += "{\"id\":\"" + parentlist[i].Menu_List_Id.ToString().Trim() + "\",\"text\":\"" + parentlist[i].Menu_Name.ToString().Trim() + " /Serial:" + parentlist[i].Serial.ToString().Trim() + "\",\"iconCls\":\"" + parentlist[i].Icon_Name.ToString().Trim() + "\",\"attributes\":{\"level\":\"1\"}";
                }
                else//表示为菜单权限分配页面
                {
                    permissionList += "{\"id\":\"" + parentlist[i].Menu_List_Id.ToString().Trim() + "\",\"text\":\"" + parentlist[i].Menu_Name.ToString().Trim() + "\",\"iconCls\":\"" + parentlist[i].Icon_Name.ToString().Trim() + "\",\"attributes\":{\"level\":\"1\"}";
                }
                //若menu_list不为空,则为父分类的ID,
                var childlist = listMenu.Where(p => p.Menu_Level == parentlist[i].Menu_List_Id.ToString()).OrderBy(p => p.Serial).ToList();
                if (childlist.Count != 0)
                {
                    permissionList += ",\"children\":[";
                }
                for (int j = 0; j < childlist.Count; j++)
                {
                    if (type != "")
                    {
                        permissionList += "{\"id\":\"" + childlist[j].Menu_List_Id.ToString().Trim() + "\",\"text\":\"" + childlist[j].Menu_Name.ToString().Trim() + " /Serial:" + childlist[j].Serial.ToString().Trim() + "\",\"iconCls\":\"" + parentlist[i].Icon_Name.ToString().Trim() + "\",\"attributes\":{\"level\":\"2\"}},";
                    }
                    else
                    {
                        permissionList += "{\"id\":\"" + childlist[j].Menu_List_Id.ToString().Trim() + "\",\"text\":\"" + childlist[j].Menu_Name.ToString().Trim() + "\",\"iconCls\":\"" + parentlist[i].Icon_Name.ToString().Trim() + "\",\"attributes\":{\"level\":\"2\"}},";
                    }
                    if (childlist.Count != 0 && j == childlist.Count - 1)
                    {
                        permissionList = permissionList.Substring(0, permissionList.Length - 1);
                        permissionList += "]";
                    }
                }
                permissionList += "},";
            }
            permissionList = permissionList.Substring(0, permissionList.Length - 1);
            permissionList += "]";

            return permissionList;
        }

这里有两个关键的点,需要略作解释:

  var parentlist = listMenu.Where(p => p.Menu_Level.ToString() == "").OrderBy(p => p.Serial).ToList();

取出所有父节点,并根据Serial排序。
这里是使用linq将集合中符合条件的对象筛选出来,所以以后不要别人问你linq,你就只知道linq to ef。这是不全面的。linq不止有跟EF连用哦,功能强大着呢。

 var childlist = listMenu.Where(p => p.Menu_Level == parentlist[i].Menu_List_Id.ToString()).OrderBy(p => p.Serial).ToList();

这里是将当前父节点的所有子节点取出来。

这列的type参数你可能会有点迷惑,这个是添加用来区分做权限分配时候显示的那个tree的。因为做权限分配的时候也需要显示一个tree结构,重写一个方法又太麻烦了,所以都在这个方法里写了,这样可能会对你现在造成一些麻烦,这样,你看着太乱,就现将判断type的那部分删了,这样就清晰了。

显示讲完之后,好像就没什么好讲的了,添加和修改公用的同一个方法,添加父节点,跟添加子节点不太一下,所以就专门开了一个按钮,这里你可能遇到的问题就是easyui的dialg控件了。这个是easyui固定的写法格式,自己多看两遍熟悉一下就好了,基本用法都在代码中了,这里不过多解释,希望可以自己钻研一番。谢谢大家

posted @ 2021-01-04 13:49  不要摸我的腰  阅读(350)  评论(0编辑  收藏  举报