在需要处理很多分类以及导航的时候,树形导航菜单就比较适合。例如在汽车之家上:

页面主要分两部分,左边是导航菜单,右边显示对应的内容。现在,我们就在ASP.NET MVC 4 下临摹一个,如下:

实现的效果包括:
1、点击导航菜单上的品牌,左侧显示该品牌下的所有车型。
2、点击导航菜单上的车系,左侧显示该车系下的所有车型。
3、点击左侧上方的字母导航,锚点跳到导航菜单的对应部分。
4、页面加载完毕,显示所有品牌和车系,即树形导航完全展开。
5、点击导航菜单上的品牌,收缩或展开对应的车系,收缩时,品牌前面图标为+号,展开时,品牌前面的图片为-号。
......

源码部分,在这里

思路呢?

页面分成左右2部分,使用Bootstrap轻松实现:

<div class="row">
    <div class="col-md-2 col-lg-2 col-sm-2">
    </div>
    <div class="col-md-10 col-lg-10 col-sm-10">
    </div>
</div>

左侧最上方的字母导航,被放在一个div中,页面加载的时候向控制器动态请求。

品牌上方的字母归类,比如奥迪上方的字母A,实际上是一个div。

品牌和车系放在了ul中,比如奥迪品牌以及奥迪下的奥迪A4和奥迪A6车系。车系被放在了dl中。

树形菜单采用模版比较合适,先把数据填充到模版,再把模版追加到页面元素。

当点击左侧树形导航上的品牌或车系,右侧通过iframe来呈现对应的内容。

领域先行。有关品牌和车系就抽象成如下的类:

    public class CarCategory
    {
        public int Id { get; set; }
        public int ParentId { get; set; }
        public string Name { get; set; }
        public string FirstLetter { get; set; }
        public string AnchorName { get; set; }
        public int Level { get; set; }
        public short DelFlag { get; set; }
    }

有关车型就抽象成如下的类:

    public class Car
    {
        public int Id { get; set; }
        public int PinPaiId { get; set; }
        public int CheXiId { get; set; }
        public string Name { get; set; }
    }

页面左侧呈现树形导航需要向控制器请求json数据,大致格式是:

首字母
锚点名称
所有品牌
    品牌编号
    品牌名称
    所有车系
        车系编号
        车系名称
        车系下车型的总数量

貌似有3层,那就从最里面这层开始建模。有关车系在树形导航中的显示:

    public class CheXiForDisplay
    {
        public int CheXiId { get; set; }
        public int TotalCount { get; set; }
        public string CheXiName { get; set; }
    }

有关品牌在树形导航中的显示:

    public class PinPaiForDisplay
    {
        public int PinPaiId { get; set; }
        public string PinPaiName { get; set; }
        public List<CheXiForDisplay> CheXis { get; set; }
    }

有关品牌车系分组的:

    public class PinPaiCheXiForDisplay
    {
        public string FirstLetter { get; set; }
        public string Anchor { get; set; }
        public List<PinPaiForDisplay> PinPais { get; set; }
    }

数据源从哪里来?模拟了一个:

    public class Database
    {
        public static IEnumerable<CarCategory> GetAllCarCategories()
        {
            return new List<CarCategory>
            {
                new CarCategory(){Id = 1, ParentId = 0, Name = "奥迪",FirstLetter = "A", AnchorName = "AA", Level = 1, DelFlag = 0},
                new CarCategory(){Id = 2, ParentId = 0, Name = "宝马",FirstLetter = "B", AnchorName = "BB", Level = 1, DelFlag = 0},
                new CarCategory(){Id = 3, ParentId = 0, Name = "保时捷",FirstLetter = "B", AnchorName = "BB", Level = 1, DelFlag = 0},
                new CarCategory(){Id = 4, ParentId = 0, Name = "长安",FirstLetter = "C", AnchorName = "CC", Level = 1, DelFlag = 0},
                new CarCategory(){Id = 5, ParentId = 0, Name = "大众",FirstLetter = "D", AnchorName = "DD", Level = 1, DelFlag = 0},
                new CarCategory(){Id = 6, ParentId = 0, Name = "东风",FirstLetter = "D", AnchorName = "DD", Level = 1, DelFlag = 0},
                new CarCategory(){Id = 7, ParentId = 0, Name = "丰田",FirstLetter = "F", AnchorName = "FF", Level = 1, DelFlag = 0},
                new CarCategory(){Id = 8, ParentId = 0, Name = "福特",FirstLetter = "F", AnchorName = "FF", Level = 1, DelFlag = 0},

                new CarCategory(){Id = 9, ParentId = 1, Name = "奥迪A4",FirstLetter = "A", AnchorName = "AA", Level = 2, DelFlag = 0},
                new CarCategory(){Id = 10, ParentId = 1, Name = "奥迪A6",FirstLetter = "A", AnchorName = "AA", Level = 2, DelFlag = 0},

                new CarCategory(){Id = 11, ParentId = 2, Name = "宝马1",FirstLetter = "B", AnchorName = "BB", Level = 2, DelFlag = 0},
                new CarCategory(){Id = 12, ParentId = 2, Name = "宝马2",FirstLetter = "B", AnchorName = "BB", Level = 2, DelFlag = 0},

                new CarCategory(){Id = 13, ParentId = 3, Name = "保时捷1",FirstLetter = "B", AnchorName = "BB", Level = 2, DelFlag = 0},
                new CarCategory(){Id = 14, ParentId = 3, Name = "保时捷2",FirstLetter = "B", AnchorName = "BB", Level = 2, DelFlag = 0},

                new CarCategory(){Id = 15, ParentId = 4, Name = "长安1",FirstLetter = "C", AnchorName = "CC", Level = 2, DelFlag = 0},
                new CarCategory(){Id = 16, ParentId = 4, Name = "长安2",FirstLetter = "C", AnchorName = "CC", Level = 2, DelFlag = 0},

                new CarCategory(){Id = 17, ParentId = 5, Name = "大众1",FirstLetter = "D", AnchorName = "DD", Level = 2, DelFlag = 0},
                new CarCategory(){Id = 18, ParentId = 5, Name = "大众2",FirstLetter = "D", AnchorName = "DD", Level = 2, DelFlag = 1},

                new CarCategory(){Id = 19, ParentId = 6, Name = "东风1",FirstLetter = "D", AnchorName = "DD", Level = 2, DelFlag = 0},
                new CarCategory(){Id = 20, ParentId = 6, Name = "东风2",FirstLetter = "D", AnchorName = "DD", Level = 2, DelFlag = 0},

                new CarCategory(){Id = 21, ParentId = 7, Name = "丰田1",FirstLetter = "F", AnchorName = "FF", Level = 2, DelFlag = 0},
                new CarCategory(){Id = 22, ParentId = 7, Name = "丰田2",FirstLetter = "F", AnchorName = "FF", Level = 2, DelFlag = 0},

                new CarCategory(){Id = 23, ParentId = 8, Name = "福特1",FirstLetter = "F", AnchorName = "AFF", Level = 2, DelFlag = 0},
                new CarCategory(){Id = 24, ParentId = 8, Name = "福特2",FirstLetter = "F", AnchorName = "AFF", Level = 2, DelFlag = 0}
            };
        }

        public static IEnumerable<Car> GetAllCars()
        {
            return new List<Car>
            {
                new Car(){Id = 1, PinPaiId = 1, CheXiId = 9, Name = "奥迪A401"},
                new Car(){Id = 2, PinPaiId = 1, CheXiId = 9, Name = "奥迪A402"},
                new Car(){Id = 3, PinPaiId = 1, CheXiId = 10, Name = "奥迪A601"},
                new Car(){Id = 4, PinPaiId = 1, CheXiId = 10, Name = "奥迪A602"},

                new Car(){Id = 5, PinPaiId = 2, CheXiId = 11, Name = "宝马101"},
                new Car(){Id = 6, PinPaiId = 2, CheXiId = 11, Name = "宝马102"},
                new Car(){Id = 7, PinPaiId = 2, CheXiId = 12, Name = "宝马201"},
                new Car(){Id = 8, PinPaiId = 2, CheXiId = 12, Name = "宝马202"},

                new Car(){Id = 9, PinPaiId = 3, CheXiId = 13, Name = "保时捷101"},
                new Car(){Id = 10, PinPaiId = 3, CheXiId = 13, Name = "保时捷102"},
                new Car(){Id = 11, PinPaiId = 3, CheXiId = 14, Name = "保时捷201"},
                new Car(){Id = 12, PinPaiId = 3, CheXiId = 14, Name = "保时捷202"},

                new Car(){Id = 13, PinPaiId = 4, CheXiId = 15, Name = "长安101"},
                new Car(){Id = 14, PinPaiId = 4, CheXiId = 15, Name = "长安102"},
                new Car(){Id = 15, PinPaiId = 4, CheXiId = 16, Name = "长安201"},
                new Car(){Id = 16, PinPaiId = 4, CheXiId = 16, Name = "长安202"},

                new Car(){Id = 17, PinPaiId = 5, CheXiId = 17, Name = "大众101"},
                new Car(){Id = 18, PinPaiId = 5, CheXiId = 17, Name = "大众102"},
                new Car(){Id = 19, PinPaiId = 5, CheXiId = 18, Name = "大众201"},
                new Car(){Id = 20, PinPaiId = 5, CheXiId = 18, Name = "大众202"},

                new Car(){Id = 21, PinPaiId = 6, CheXiId = 19, Name = "东风101"},
                new Car(){Id = 22, PinPaiId = 6, CheXiId = 19, Name = "东风102"},
                new Car(){Id = 23, PinPaiId = 6, CheXiId = 20, Name = "东风201"},
                new Car(){Id = 24, PinPaiId = 6, CheXiId = 20, Name = "东风202"},

                new Car(){Id = 25, PinPaiId = 7, CheXiId = 21, Name = "丰田101"},
                new Car(){Id = 26, PinPaiId = 7, CheXiId = 21, Name = "丰田102"},
                new Car(){Id = 27, PinPaiId = 7, CheXiId = 22, Name = "丰田201"},
                new Car(){Id = 28, PinPaiId = 7, CheXiId = 22, Name = "丰田202"},

                new Car(){Id = 29, PinPaiId = 8, CheXiId = 23, Name = "福特101"},
                new Car(){Id = 30, PinPaiId = 8, CheXiId = 23, Name = "福特102"},
                new Car(){Id = 31, PinPaiId = 8, CheXiId = 24, Name = "福特201"},
                new Car(){Id = 32, PinPaiId = 8, CheXiId = 24, Name = "福特202"}
            };
        }
    }

好,现在可以向控制器要数据了。

   public class HomeController : Controller
    {

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

        //获取所有首字母以及锚点的json
        public ActionResult GetFirstLettersJson()
        {
            var allCarCategories = Database.GetAllCarCategories();
            var result = from l in allCarCategories
                group l by l.FirstLetter
                into g
                select new {firstletter = g.Key, anchor=g.ToList()[0].AnchorName};
            return Json(result, JsonRequestBehavior.AllowGet);
        }

        //获取按首字母分组后的品牌车系json
        public ActionResult GetPinPaiCheXiJson()
        {
            var allPinPais = Database.GetAllCarCategories().Where(c => c.Level == 1).OrderBy(c => c.FirstLetter);
            var allPinPaisGroup = from p in allPinPais
                group p by new
                {
                    p.FirstLetter,
                    p.AnchorName
                };

            List<PinPaiCheXiForDisplay> result1 = new List<PinPaiCheXiForDisplay>();
            foreach (var item in allPinPaisGroup)
            {
                //品牌车系
                PinPaiCheXiForDisplay pinPaiCheXiForDisplay = new PinPaiCheXiForDisplay();
                pinPaiCheXiForDisplay.FirstLetter = item.Key.FirstLetter;
                pinPaiCheXiForDisplay.Anchor = item.Key.AnchorName;

                //品牌
                List<PinPaiForDisplay> pinPaiForDisplays = new List<PinPaiForDisplay>();
                foreach (var pinpai in item.ToList())
                {
                    PinPaiForDisplay pinPaiForDisplay = new PinPaiForDisplay();
                    pinPaiForDisplay.PinPaiId = pinpai.Id;
                    pinPaiForDisplay.PinPaiName = pinpai.Name;

                    //车系
                    List<CheXiForDisplay> cheXiForDisplays = new List<CheXiForDisplay>();
                    var cheXis = Database.GetAllCarCategories().Where(c => c.ParentId == pinpai.Id).OrderBy(c => c.Id);
                    foreach (var chexi in cheXis)
                    {
                        CheXiForDisplay cheXiForDisplay = new CheXiForDisplay();
                        cheXiForDisplay.CheXiId = chexi.Id;
                        cheXiForDisplay.CheXiName = chexi.Name;
                        cheXiForDisplay.TotalCount = cheXis.Count();

                        cheXiForDisplays.Add(cheXiForDisplay);
                    }
                    pinPaiForDisplay.CheXis = cheXiForDisplays;

                    pinPaiForDisplays.Add(pinPaiForDisplay);
                }
                pinPaiCheXiForDisplay.PinPais = pinPaiForDisplays;

                result1.Add(pinPaiCheXiForDisplay);
            }

            return Json(result1, JsonRequestBehavior.AllowGet);
        }

        //根据品牌Id显示车型
        public ActionResult GetCheXingsByPId(int pid)
        {
            var cars = Database.GetAllCars().Where(c => c.PinPaiId == pid);
            return View(cars);
        }

        //根据车系Id显示车型
        public ActionResult GetCheXingsByChexiId(int cxid)
        {
            var cars = Database.GetAllCars().Where(c => c.CheXiId == cxid);
            return View(cars);
        }

    }


在Shared/_Layout.cshtml中,该引用的css,js都要引用上。

<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width" />
    <title>@ViewBag.Title</title>
    <link href="~/bootstrap/css/bootstrap.min.css" rel="stylesheet" />
    @Styles.Render("~/Content/css")
    @RenderSection("styles", required: false)
    @Scripts.Render("~/bundles/jquery")
    <script src="~/bootstrap/js/bootstrap.min.js"></script>
</head>
<body>
    @RenderBody()


    @RenderSection("scripts", required: false)
</body>  

Home/Index.cshtml就负责显示就行了。

@{
    ViewBag.Title = "Index";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

@section styles
{
    <link href="~/Content/sidemenu.css" rel="stylesheet" />
}

<div class="row">
    <div class="col-md-2 col-lg-2 col-sm-2">
        <!--字母导航开始-->
        <div id="lDaoHang">
        </div>
        <!--字母导航结束-->
        <!--树开始-->
        <div id="cTreeDiv" style="overflow-x: hidden; overflow-y: scroll; height: 550px; width: 99%;">
        </div>
        <!--树结束-->
        <div>
            <dl id="test"></dl>
        </div>
    </div>
    <div class="col-md-10 col-lg-10 col-sm-10">
        <div class="carContent" id="carContent">
            <iframe id="frameCar" src="" scrolling="no" frameborder="0" height="100%" width="100%" onload="this.height=this.contentWindow.document.documentElement.scrollHeight"></iframe>
        </div>
    </div>
</div>

@section scripts
{
    <script src="~/Scripts/jquery.tmpl.min.js"></script>
    <script type="text/javascript">
        $(function () {

            //加载首字母
            $.getJSON('@Url.Action("GetFirstLettersJson", "Home")', function (data) {
                $('#firstLetterTemplate').tmpl(data).appendTo('#lDaoHang');
            });

            //加载所有品牌车系
            $.getJSON('@Url.Action("GetPinPaiCheXiJson", "Home")', function (data) {
                $('#pinpaiTemplate').tmpl(data).appendTo('#cTreeDiv');

                $('.pLink').each(function () {
                    pinPaiInitialState($(this));
                });
            });

            //隐藏ifame所在div
            $("#carContent").css("display", "none");

            //点击品牌
            $('#cTreeDiv').on("click", ".pLink", function () {
                //切换
                togglePinPaiState($(this));

                //显示右边区域
                var url = "/Home/GetCheXingsByPId?pid=" + $(this).attr('id');
                $("#frameCar").attr("src", url);
                $("#carContent").css("display", "block");
            });

            //点击车系
            $('#cTreeDiv').on("click", ".cxLink", function () {
                //显示右边区域
                var url = "/Home/GetCheXingsByChexiId?cxid=" + $(this).attr('id');
                $("#frameCar").attr("src", url);
                $("#carContent").css("display", "block");
            });
        });

        //品牌的初始状态,即把车系隐藏和品牌前面的图标为+号
        var pstate = 0;

        //品牌只有2种状态:所有车系隐藏,品牌前面的图标变为为+号;要么显示品牌下的所有车系,品牌前面的图标变为-号
        //把车系隐藏和品牌前面的图标为+号,记为状态0
        //把车系隐藏和品牌前面的图标为-号,记为状态1
        function togglePinPaiState($pinpai) {


            if (pstate == 0) {
                var $i = $pinpai.parent().find("i");
                var attr = $i.attr('class');
                if (typeof attr !== typeof undefined && attr !== false) {
                    $i.removeClass("iconHide");
                }

                $i.addClass("iconShow");
                $pinpai.parent().parent().find("dl").show();
                pstate = 1;
            } else {
                var $j = $pinpai.parent().find("i");
                var attr1 = $j.attr('class');
                if (typeof attr1 !== typeof undefined && attr1 !== false) {
                    $j.removeClass("iconShow");
                }
                $j.addClass("iconHide");
                $pinpai.parent().parent().find("dl").hide();
                pstate = 0;
            }
        }

        function pinPaiInitialState($pinpai) {
            pstate = 0;
            togglePinPaiState($pinpai);
        }

    </script>

    <!--首字母模版-->
    <script id="firstLetterTemplate" type="text/x-jQuery-tmpl">
        <div class="lWrapper">
            <a href="#${anchor}" class="lLink">${firstletter}</a>
        </div>
    </script>

    <!--品牌模版-->
    <script id="pinpaiTemplate" type="text/x-jQuery-tmpl">
        <div class="lHeader" id="${Anchor}">${FirstLetter}</div>
        <ul class="uTree">
            {{if PinPais}}
            {{each PinPais}}
            <li>
                <h5 class="font-bold">
                    <a href="javascript:void(0)" class="pLink" id="${$value.PinPaiId}"><i></i>${$value.PinPaiName}</a>
                </h5>
                <dl>
                    {{tmpl(CheXis) "#chexiTemplate"}}
                </dl>
            </li>
            {{/each}}
            {{else}}
            <li>没有对应品牌</li>
            {{/if}}

        </ul>
    </script>

    <!--车系模版-->
    <script id="chexiTemplate" type="text/x-jQuery-tmpl">
        <dd><a id="${CheXiId}" href="javascript:void(0)" class="cxLink">${CheXiName}<em>(${TotalCount})</em></a></dd>
    </script>
}

以上,
○ 从控制器返回的有关树形菜单的json数据,先填充到jquery.tmpl.min.js模版中,然后追加到页面上。
○ 树形菜单的展开或收缩,通过全局变量pstate在0和1之间的切换来实现,页面初次加载给变量pstate一个初始值。

另外,点击树形导航上的品牌,iframe加载的视图是Home/GetCheXingsByPId.cshtml

@model IEnumerable<MvcApplication1.Models.Car>

@{
    ViewBag.Title = "GetCheXingsByPId";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<h2>GetCheXingsByPId</h2>

<div>
    @foreach (var item in Model)
    {
        <p>@item.Name </p>

    }
</div>

点击树形导航上的车系,iframe加载的视图是Home/GetCheXingsByChexiId.cshtml

@model IEnumerable<MvcApplication1.Models.Car>

@{
    ViewBag.Title = "GetCheXingsByChexiId";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<h2>GetCheXingsByChexiId</h2>

<div>
    @foreach (var item in Model)
    {
        <p>@item.Name </p>

    }
</div

就这样。

在ASP.NET MVC下实现树形导航菜单的更多相关文章

  1. ASP.NET MVC下的四种验证编程方式[续篇]

    在<ASP.NET MVC下的四种验证编程方式>一文中我们介绍了ASP.NET MVC支持的四种服务端验证的编程方式("手工验证"."标注Validation ...

  2. ASP.NET MVC下的四种验证编程方式

    ASP.NET MVC采用Model绑定为目标Action生成了相应的参数列表,但是在真正执行目标Action方法之前,还需要对绑定的参数实施验证以确保其有效性,我们将针对参数的验证成为Model绑定 ...

  3. Response.End()在Webform和ASP.NET MVC下的表现差异

    前几天在博问中看到一个问题--Response.End()后,是否停止执行?MVC与WebForm不一致.看到LZ的描述后,虽然奇怪于为何用Response.End()而不用return方式去控制流程 ...

  4. ASP.NET MVC下的四种验证编程方式[续篇]【转】

    在<ASP.NET MVC下的四种验证编程方式> 一文中我们介绍了ASP.NET MVC支持的四种服务端验证的编程方式(“手工验证”.“标注ValidationAttribute特性”.“ ...

  5. ASP.NET MVC下的四种验证编程方式【转】

    ASP.NET MVC采用Model绑定为目标Action生成了相应的参数列表,但是在真正执行目标Action方法之前,还需要对绑定的参数实施验证以确保其有效 性,我们将针对参数的验证成为Model绑 ...

  6. ASP.NET MVC下使用AngularJs语言(六):获取下拉列表的value和Text

    前面Insus.NET有在Angularjs实现DropDownList的下拉列表的功能.但是没有实现怎样获取下拉列表的value和text功能. 下面分别使用ng-click和ng-change来实 ...

  7. ASP.NET MVC下使用AngularJs语言(五):ng-selected

    这次学习ng-selected语法,这个是为DropDownList下拉列表显示默认选项. 演示从下面步骤开始 1,新建一个model: 上面#14行代码的property,数据类型为bool.即是存 ...

  8. ASP.NET MVC下使用AngularJs语言(二):ng-click事件

    程序用户交互,用户使用mouse点击,这是一个普通的功能. 在angularjs的铵钮点击命令是ng-click. 创建Angularjs的app使用前一篇<ASP.NET MVC下使用Angu ...

  9. ASP.NET MVC下使用AngularJs语言(一):Hello your name

    新春节后,分享第一个教程. 是教一位新朋友全新学习ASP.NET MVC下使用AngularJs语言. 一,新建一个空的Web项目.使用NuGet下载AngularJs和jQuery.二,配置Bund ...

随机推荐

  1. C# UDP广播消息

    首先是发送端: /// <summary> /// 发送UDP消息 /// </summary> /// <param name="msg">消 ...

  2. Apache+Tomcat+mod_jk实现负载均衡

    最近公司提出了负载均衡的新需求,以减轻网站的高峰期的服务器负担.趁空闲时间我就准备了一下这方面的知识,都说有备无患嘛.网上相关资料很多,但是太散.我希望可以通过这篇随笔,系统的总结. 一.Tomcat ...

  3. 001_chrome工具详解

    一.chrome https://segmentfault.com/a/1190000000683599

  4. JS文件上传神器bootstrap fileinput详解

    Bootstrap FileInput插件功能如此强大,完全没有理由不去使用,但是国内很少能找到本插件完整的使用方法,于是本人去其官网翻译了一下英文说明文档放在这里供英文不好的同学勉强查阅.另外附上一 ...

  5. Springbatch Miscellanea Notes

    1.scope="step",如下图,这是一种后绑定的方式,生成Step的时候,才去创建bean <bean id="testTasklet" paren ...

  6. rabbitmq route

    AMQP AMQP协议是一个高级抽象层消息通信协议,RabbitMQ是AMQP协议的实现.它主要包括以下组件: 1.Server(broker): 接受客户端连接,实现AMQP消息队列和路由功能的进程 ...

  7. Java第三阶段学习(十二、HttpServletRequest与HttpServletResponse)

    一.HttpServletRequest 1.概述: 我们在创建Servlet时会覆盖service()方法,或doGet()/doPost(),这些方法都有两个参数,一个为代表请求的request和 ...

  8. CSS 浮动和清除

    CSS 浮动和清除浮动 在写页面布局的过程中,浮动是大家经常用的属性.在好多的排版布局中都是用的的浮动比如说下面这些地方都是应用到了浮动. 在我学习浮动的时候可是熬坏了脑筋,在这里我分享一下我对浮动这 ...

  9. 【LOJ】#2509. 「AHOI / HNOI2018」排列

    题解 虽然要求一个dfs序,但是不是从根开始贪心 从最小的点开始贪心,最小的点显然是父亲选了之后马上就选它 那么我们每次把最小的点和父亲合并,两个联通块之间也是如此 对于两个联通块,他们合并的顺序应该 ...

  10. 【AtCoder】ARC093

    C - Traveling Plan 相当于一个环,每次删掉i点到两边的距离,加上新相邻的两个点的距离 代码 #include <bits/stdc++.h> #define fi fir ...