<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12
      Fork me on GitHub

      Knockout JS實現任務管理應用程序

      1.1.1 摘要

      在博文《Ember.js實現單頁面應用程序》中,我們介紹了使用Ember JS實現一個單頁應用程序 (SPA),這使我想起了幾年前寫過一個任務管理程序,通過選擇日期,然后編輯時間來增加任務信息。

      當時,我們是使用ASP.NET和jQuery實現了任務管理程序的,通過ajax調用ASP.NET的Webservice方法來訪問數據庫。

      今天,我們將通過任務管理程序的實現,來介紹使用ASP.NET Web API和Knockout JS的結合使用,想必許多人都有使用過任務管理程序,其中我覺得Google日歷是一個不錯的任務管理器

      taskcalendar1

      圖1 Google日歷

      目錄

      1.1.2 正文

      通過圖1Google日歷,我們發現它使用一個Date Picker,讓用戶選擇編輯的日期、還有一個24小時的表格,當用戶點擊表上的一個時間區域就顯示一個彈出式窗口,讓用戶編輯任務的內容,現在大概了解了基本的界面設計了,接下來我們將通過ASP.NET Web API作為服務端,開放API讓Knockout JS調用接口獲取數據。

      創建ASP.NET MVC 項目

      首先,我們在VS2012中創建一個ASP.NET MVC 4 Web項目。

      然后,我們打開Package Manager Console,添加Package引用,要使用的庫如下:

      • PM> install-package jQuery
      • PM> install-package KnockoutJS
      • PM> install-package Microsoft.AspNet.Web.Optimization
      • PM> update-package Micrsoft.AspNet.WebApi
      • PM> install-package EntityFramework

      taskcalendar2

      圖2 ASP.NET MVC 4 Web Application

      創建數據表

      接著,我們在數據庫中添加表TaskDays和TaskDetails,TaskDays保存所有任務的日期,那么一個日期只有一行記錄保存該表中,它包含了Id(自增)和Day字段,TaskDetails保存不同時間短任務信息,它包含Id(自增)、Title、Details、Starts、Ends和ParentTaskId等字段,其中ParentTaskId保存任務的日期的Id值。

       taskcalendar4

       taskcalendar5

       taskcalendar3

      圖3 表TaskDays和TaskDetails

      數據傳輸對象

      前面,我們已經定義了數據表TaskDays和TaskDetails并且通過ParentTaskId建立了表之間的關系,接下來,我們將根表定義數據傳輸對象,具體定義如下:

          /// <summary>
          /// Defines a DTO TaskCalendar.
          /// </summary>
          public class TaskDay
          {
              public TaskDay()
              {
                  Tasks = new List<TaskDetail>();
              }
              public int Id { get; set; }
              public DateTime Day { get; set; }
              public List<TaskDetail> Tasks { get; set; }
          }
      
      
          /// <summary>
          /// Defines a DTO TaskDetail.
          /// </summary>
          public class TaskDetail
          {
              public int Id { get; set; }
              public string Title { get; set; }
              public string Details { get; set; }
              public DateTime Starts { get; set; }
              public DateTime Ends { get; set; }
      
              [ForeignKey("ParentTaskId")]
              [ScriptIgnore]
              public TaskDay ParentTask { get; set; }
              public int ParentTaskId { get; set; }
          }

       

      上面,我們定義了數據傳輸對象TaskDays和TaskDetails,在TaskDays類中,我們定義了一個List<TaskDetail>類型的字段并且在構造函數中實例化該字段,通過保持TaskDetail類型的強對象引用,從而建立起TaskDays和TaskDetails之間的聚合關系,也就是TaskDay和TaskDetails是一對多的關系。

      創建控制器

      這里我們的ASP.NET MVC程序作為服務端向客戶端開放API接口,所以我們創建控制器CalendarController并且提供數據庫操作方法,具體實現如下:

          /// <summary>
          /// The server api controller.
          /// </summary>
          public class CalendarController : ApiController
          {
      
              /// <summary>
              /// Gets the task details.
              /// </summary>
              /// <param name="id">The identifier.</param>
              /// <returns>A list of task detail.</returns>
              /// /api/Calendar/GetTaskDetails?id
              [HttpGet]
              public List<TaskDetail> GetTaskDetails(DateTime id)
              {
      
              }
      
              /// <summary>
              /// Saves the task.
              /// </summary>
              /// <param name="taskDetail">The task detail.</param>
              /// <returns></returns>
              /// /api/Calendar/SaveTask?taskDetail
              [HttpPost]
              public bool SaveTask(TaskDetail taskDetail)
              {
              }
      
              /// <summary>
              /// Deletes the task.
              /// </summary>
              /// <param name="id">The identifier.</param>
              /// <returns></returns>
              /// /api/Calendar/DeleteTask?id
              [HttpDelete]
              public bool DeleteTask(int id)
              {
      
              }
          }

      在控制器CalendarController中我們定義了三個方法分別是SaveTask()、DeleteTask()和GetTaskDetails(),想必大家一看都知道這三個方法的作用,沒錯就是傳統的增刪查API,但我們這里并沒有給出具體數據庫操作代碼,因為我們將使用Entity Framework替代傳統ADO.NET操作。

      Entity Framework數據庫操作

      接下來,我們定義類TaskDayRepository和TaskDetailRepository,它們使用Entity Framework對數據庫進行操作,具體定義如下:

          /// <summary>
          /// Task day repository
          /// </summary>
          public class TaskDayRepository : ITaskDayRepository
          {
              readonly TaskCalendarContext _context = new TaskCalendarContext();
      
              /// <summary>
              /// Gets all tasks.
              /// </summary>
              public IQueryable<TaskDay> All
              {
                  get { return _context.TaskDays.Include("Tasks"); }
              }
      
              /// <summary>
              /// Alls the including tasks.
              /// </summary>
              /// <param name="includeProperties">The include properties.</param>
              /// <returns></returns>
              public IQueryable<TaskDay> AllIncluding(params Expression<Func<TaskDay, object>>[] includeProperties)
              {
                  IQueryable<TaskDay> query = _context.TaskDays;
                  foreach (var includeProperty in includeProperties)
                  {
                      query = query.Include(includeProperty);
                  }
                  return query;
              }
      
              /// <summary>
              /// Finds the specified identifier.
              /// </summary>
              /// <param name="id">The identifier.</param>
              /// <returns></returns>
              public TaskDay Find(int id)
              {
                  return _context.TaskDays.Find(id);
              }
      
              /// <summary>
              /// Inserts the or update.
              /// </summary>
              /// <param name="taskday">The taskday.</param>
              public void InsertOrUpdate(TaskDay taskday)
              {
                  if (taskday.Id == default(int))
                  {
                      _context.TaskDays.Add(taskday);
                  }
                  else
                  {
                      _context.Entry(taskday).State = EntityState.Modified;
                  }
              }
      
              /// <summary>
              /// Saves this instance.
              /// </summary>
              public void Save()
              {
                  _context.SaveChanges();
              }
      
              /// <summary>
              /// Deletes the specified identifier.
              /// </summary>
              /// <param name="id">The identifier.</param>
              public void Delete(int id)
              {
                  var taskDay = _context.TaskDays.Find(id);
                  _context.TaskDays.Remove(taskDay);
              }
      
              public void Dispose()
              {
                  _context.Dispose();
              }
          }
      
          public interface ITaskDayRepository : IDisposable
          {
              IQueryable<TaskDay> All { get; }
              IQueryable<TaskDay> AllIncluding(params Expression<Func<TaskDay, object>>[] includeProperties);
              TaskDay Find(int id);
              void InsertOrUpdate(TaskDay taskday);
              void Delete(int id);
              void Save();
          }

      上面,我們定義類TaskDayRepository,它包含具體的數據庫操作方法:Save()、Delete()和Find(),TaskDetailRepository的實現和TaskDayRepository基本相似,所以我們很快就可以使用TaskDetailRepository,具體定義如下:

      /// <summary>
      /// Task detail repository
      /// </summary>
      public class TaskDetailRepository : ITaskDetailRepository
      {
          readonly TaskCalendarContext _context = new TaskCalendarContext();
      
          /// <summary>
          /// Gets all.
          /// </summary>
          public IQueryable<TaskDetail> All
          {
              get { return _context.TaskDetails; }
          }
      
          /// <summary>
          /// Alls the including task details.
          /// </summary>
          /// <param name="includeProperties">The include properties.</param>
          /// <returns></returns>
          public IQueryable<TaskDetail> AllIncluding(params Expression<Func<TaskDetail, object>>[] includeProperties)
          {
              IQueryable<TaskDetail> query = _context.TaskDetails;
              foreach (var includeProperty in includeProperties)
              {
                  query = query.Include(includeProperty);
              }
              return query;
          }
      
          /// <summary>
          /// Finds the specified identifier.
          /// </summary>
          /// <param name="id">The identifier.</param>
          /// <returns></returns>
          public TaskDetail Find(int id)
          {
              return _context.TaskDetails.Find(id);
          }
      
          /// <summary>
          /// Saves this instance.
          /// </summary>
          public void Save()
          {
              _context.SaveChanges();
          }
      
          /// <summary>
          /// Inserts the or update.
          /// </summary>
          /// <param name="taskdetail">The taskdetail.</param>
          public void InsertOrUpdate(TaskDetail taskdetail)
          {
              if (default(int) == taskdetail.Id)
              {
                  _context.TaskDetails.Add(taskdetail);
              }
              else
              {
                  _context.Entry(taskdetail).State = EntityState.Modified;
              }
          }
      
          /// <summary>
          /// Deletes the specified identifier.
          /// </summary>
          /// <param name="id">The identifier.</param>
          public void Delete(int id)
          {
              var taskDetail = _context.TaskDetails.Find(id);
              _context.TaskDetails.Remove(taskDetail);
          }
      
          public void Dispose()
          {
              _context.Dispose();
          }
      }
      
      public interface ITaskDetailRepository : IDisposable
      {
          IQueryable<TaskDetail> All { get; }
          IQueryable<TaskDetail> AllIncluding(params Expression<Func<TaskDetail, object>>[] includeProperties);
          TaskDetail Find(int id);
          void InsertOrUpdate(TaskDetail taskdetail);
          void Delete(int id);
          void Save();
      }

       

      上面我們通過Entity Framework實現了數據的操作,接下來,讓我們控制器CalendarController的API的方法吧!

       

          /// <summary>
          /// The server api controller.
          /// </summary>
          public class CalendarController : ApiController
          {
              readonly ITaskDayRepository _taskDayRepository = new TaskDayRepository();
              readonly TaskDetailRepository _taskDetailRepository = new TaskDetailRepository();
      
              /// <summary>
              /// Gets the task details.
              /// </summary>
              /// <param name="id">The identifier.</param>
              /// <returns>A list of task detail.</returns>
              /// /api/Calendar/GetTaskDetails?id
              [HttpGet]
              public List<TaskDetail> GetTaskDetails(DateTime id)
              {
                  var taskDay = _taskDayRepository.All.FirstOrDefault<TaskDay>(_ => _.Day == id);
                  return taskDay != null ? taskDay.Tasks : new List<TaskDetail>();
              }
      
              /// <summary>
              /// Saves the task.
              /// </summary>
              /// <param name="taskDetail">The task detail.</param>
              /// <returns></returns>
              /// /api/Calendar/SaveTask?taskDetail
              [HttpPost]
              public bool SaveTask(TaskDetail taskDetail)
              {
                  var targetDay = new DateTime(
                      taskDetail.Starts.Year,
                      taskDetail.Starts.Month,
                      taskDetail.Starts.Day);
      
                  // Check new task or not.
                  var day = _taskDayRepository.All.FirstOrDefault<TaskDay>(_ => _.Day == targetDay);
                  
                  if (null == day)
                  {
                      day = new TaskDay
                          {
                              Day = targetDay,
                              Tasks = new List<TaskDetail>()
                          };
                      _taskDayRepository.InsertOrUpdate(day);
                      _taskDayRepository.Save();
                      taskDetail.ParentTaskId = day.Id;
                  }
                  else
                  {
                      taskDetail.ParentTaskId = day.Id;
                      taskDetail.ParentTask = null;
                  }
                  _taskDetailRepository.InsertOrUpdate(taskDetail);
                  _taskDetailRepository.Save();
                  return true;
              }
      
              /// <summary>
              /// Deletes the task.
              /// </summary>
              /// <param name="id">The identifier.</param>
              /// <returns></returns>
              /// /api/Calendar/DeleteTask?id
              [HttpDelete]
              public bool DeleteTask(int id)
              {
                  try
                  {
                      _taskDetailRepository.Delete(id);
                      _taskDetailRepository.Save();
                      return true;
                  }
                  catch (Exception)
                  {
                      return false;
                  }
      
              }
          }

       

      Knockout JS

      上面,我們通過APS.NET MVC實現了服務端,接下來,我們通過Knockout JS實現客戶端訪問服務端,首先,我們在Script文件中創建day-calendar.js和day-calendar.knockout.bindinghandlers.js文件。

      // The viem model type.
      var ViewModel = function () {
          var $this = this,
              d = new Date();
          
          // Defines observable object, when the selectedDate value changed, will
          // change data bind in the view.
          $this.selectedDate = ko.observable(new Date(d.getFullYear(), d.getMonth(), d.getDate()));
          $this.selectedTaskDetails = ko.observable(new TaskDetails(d));
      
          // A serial of observable object observableArray.
          $this.dateDetails = ko.observableArray();
          $this.appointments = ko.observableArray();
      
          
          // Init date details list.
          $this.initializeDateDetails = function () {
              $this.dateDetails.removeAll();
              for (var i = 0; i < 24; i++) {
                  var dt = $this.selectedDate();
                  $this.dateDetails.push({
                      count: i,
                      TaskDetails: new GetTaskHolder(i, dt)
                  });
              }
          };
          
          // Call api to get task details.
          $this.getTaskDetails = function (date) {
              var dt = new Date(date.getFullYear(), date.getMonth(), date.getDate()),
                  uri = "/api/Calendar/GetTaskDetails";
              
              // Deprecation Notice: The jqXHR.success(), jqXHR.error(), and jqXHR.complete() callbacks are deprecated as of jQuery 1.8.
              // To prepare your code for their eventual removal, use jqXHR.done(), jqXHR.fail(), and jqXHR.always() instead.
              // Reference: https://api.jquery.com/jQuery.ajax/
              $.get(uri, 'id=' + dt.getFullYear() + '-' + (dt.getMonth() + 1) + '-' + dt.getDate()
              ).done(function(data) {
                  $this.appointments.removeAll();
                  $(data).each(function(i, item) {
                      $this.appointments.push(new Appointment(item, i));
                  });
              }).error(function(data) {
                  alert("Failed to retrieve tasks from server.");
              });
          };
      };

      上面,我們定義了ViewModel類型,并且在其中定義了一系列的方法。

      • selectedDate:獲取用戶在日歷控件中選擇的日期。
      • selectedTaskDetails:獲取用戶選擇中TaskDetail對象。
      • dateDetails:定義監控數組,它保存了24個時間對象。
      • appointments:定義監控數組保存每個TaskDetail對象。

      接下來,需要獲取用戶點擊日歷控件的操作,我們通過方法ko.bindingHandlers()自定義事件處理方法,具體定義如下:

      // Binding event handler with date picker.
      ko.bindingHandlers.datepicker = {
          init: function (element, valueAccessor, allBindingsAccessor) {
              
              // initialize datepicker with some optional options
              var options = allBindingsAccessor().datepickerOptions || {};
              $(element).datepicker(options);
      
              // when a user changes the date, update the view model
              ko.utils.registerEventHandler(element, "changeDate", function (event) {
                  var value = valueAccessor();
                  
                  // Determine if an object property is ko.observable
                  if (ko.isObservable(value)) {
                      value(event.date);
                  }
              });
          },
          update: function (element, valueAccessor) {
              var widget = $(element).data("datepicker");
              //when the view model is updated, update the widget
              if (widget) {
                  widget.date = ko.utils.unwrapObservable(valueAccessor());
                  widget.setValue();
              }
          }
      
      };

      上面,我們定義了日歷控件的事件處理方法,當用戶選擇日歷中的日期時,我們獲取當前選擇的日期綁定到界面上,具體定義如下:

      <!-- Selected time control -->
      <input id="selectStartDate" data-bind="datepicker: Starts" type="text" class="span12" />

      上面,我們在Html元素中綁定了datepicker事件處理方法并且把Starts值顯示到input元素中。

      taskcalendar6

      圖4 日歷控件

      接下來,我們定義Time picker事件處理方法,當用戶時間時獲取當前選擇的時間綁定到界面上,具體定義如下:

      // Binding event handler with time picker.
      ko.bindingHandlers.timepicker = {
          init: function (element, valueAccessor, allBindingsAccessor) {
              //initialize timepicker 
              var options = $(element).timepicker();
      
              //when a user changes the date, update the view model        
              ko.utils.registerEventHandler(element, "changeTime.timepicker", function (event) {
                  var value = valueAccessor();
                  if (ko.isObservable(value)) {
                      value(event.time.value);
                  }
              });
          },
          update: function (element, valueAccessor) {
              var widget = $(element).data("timepicker");
              //when the view model is updated, update the widget
              if (widget) {
                  var time = ko.utils.unwrapObservable(valueAccessor());
                  widget.setTime(time);
              }
          }
      };

      同樣,我們把時間值綁定頁面元素中,具體定義如下:

      <!-- Time picker value-->
      <input id="selectStartTime" data-bind="timepicker: StartTime" class="span8" type="text" />

      現在,我們已經實現獲取用戶的輸入,接下來需要把用戶輸入的任務信息數據保存到數據庫中,那么我們將通過$.ajax()方法調用API接口,首先我們在day-calendar.js文件中定義類型TaskDetails,具體定義如下:

      // TaskDetails type.
      var TaskDetails = function (date) {
          var $this = this;
          $this.Id = ko.observable();
          $this.ParentTask = ko.observable();
          $this.Title = ko.observable("New Task");
          $this.Details = ko.observable();
          $this.Starts = ko.observable(new Date(new Date(date).setMinutes(0)));
          $this.Ends = ko.observable(new Date(new Date(date).setMinutes(59)));
          
          // Gets start time when starts changed.
          $this.StartTime = ko.computed({
              read: function () {
                  return $this.Starts().toLocaleTimeString("en-US");
              },
              write: function (value) {
                  if (value) {
                      var dt = new Date($this.Starts().toDateString() + " " + value);
                      $this.Starts(new Date($this.Starts().getFullYear(), $this.Starts().getMonth(), $this.Starts().getDate(), dt.getHours(), dt.getMinutes()));
                  }
              }
          });
      
          // Gets end time when ends changed.
          $this.EndTime = ko.computed({
              read: function () {
                  return $this.Ends().toLocaleTimeString("en-US");
              },
              write: function (value) {
                  if (value) {
                      var dt = new Date($this.Ends().toDateString() + " " + value);
                      $this.Ends(new Date($this.Ends().getFullYear(), $this.Ends().getMonth(), $this.Ends().getDate(), dt.getHours(), dt.getMinutes()));
                  }
              }
          });
      
          $this.btnVisibility = ko.computed(function () {
              if ($this.Id() > 0) {
                  return "visible";
              }
              else {
                  return "hidden";
              }
          });
      
          $this.Save = function (data) {
      
              // http://knockoutjs.com/documentation/plugins-mapping.html
              var taskDetails = ko.mapping.toJS(data);
              taskDetails.Starts = taskDetails.Starts.toDateString();
              taskDetails.Ends = taskDetails.Ends.toDateString();
              $.ajax({
                  url: "/api/Calendar/SaveTask",
                  type: "POST",
                  contentType: "text/json",
                  data: JSON.stringify(taskDetails)
      
              }).done(function () {
                  $("#currentTaskModal").modal("toggle");
                  vm.getTaskDetails(vm.selectedDate());
              }).error(function () {
                  alert("Failed to Save Task");
              });
          };
      
          $this.Delete = function (data) {
              $.ajax({
                  url: "/api/Calendar/" + data.Id(),
                  type: "DELETE",
      
              }).done(function () {
                  $("#currentTaskModal").modal("toggle");
                  vm.getTaskDetails(vm.selectedDate());
              }).error(function () {
                  alert("Failed to Delete Task");
              });
          };
      
          $this.Cancel = function (data) {
              $("#currentTaskModal").modal("toggle");
          };
      };

      我們在TaskDetails類型中定義方法Save()和Delete(),我們看到Save()方法通過$.ajax()調用接口“/api/Calendar/SaveTask” 保存數據,這里要注意的是我們把TaskDetails對象序列化成JSON格式數據,然后調用SaveTask()保存數據。

      現在,我們已經實現了頁面綁定用戶的輸入,然后由Knockout JS訪問Web API接口,對數據庫進行操作;接著需要對程序界面進行調整,我們在項目中添加bootstrap-responsive.css和bootstrap.css文件引用,接下來,我們在的BundleConfig中指定需要加載的Javascript和CSS文件,具體定義如下:

          /// <summary>
          /// Compress JS and CSS file.
          /// </summary>
          public class BundleConfig
          {
              /// <summary>
              /// Registers the bundles.
              /// </summary>
              /// <param name="bundles">The bundles.</param>
              public static void RegisterBundles(BundleCollection bundles)
              {
                  bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
                  "~/Scripts/jquery-{version}.js"));
      
                  bundles.Add(new ScriptBundle("~/bundles/bootstrap").Include(
                              "~/Scripts/bootstrap.js",
                              "~/Scripts/html5shiv.js"));
      
                  bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include(
                              "~/Scripts/jquery.unobtrusive*",
                              "~/Scripts/jquery.validate*"));
      
                  bundles.Add(new ScriptBundle("~/bundles/knockout").Include(
                              "~/Scripts/knockout-{version}.js"));
      
                  bundles.Add(new StyleBundle("~/Styles/bootstrap/css").Include(
                      "~/Content/bootstrap-responsive.css",
                      "~/Content/bootstrap.css"));
      
                  bundles.Add(new ScriptBundle("~/bundles/jquerydate").Include(
                              "~/Scripts/datepicker/bootstrap-datepicker.js",
                              //"~/Scripts/datepicker/locales/bootstrap-datepicker.zh-CN.js",
                              "~/Scripts/timepicker/bootstrap-timepicker.min.js",
                              "~/Scripts/moment.js"));
      
                  bundles.Add(new ScriptBundle("~/bundles/app").Include(
                             "~/Scripts/app/day-calendar*"));
      
                  bundles.Add(new StyleBundle("~/Styles/jquerydate").Include(
                      "~/Content/datepicker/datepicker.css",
                      "~/Content/timepicker/bootstrap-timepicker.css"));
              }
          }

       

      taskcalendar7

      圖5 任務管理器Demo

       

      1.1.3 總結

      我們通過一個任務管理程序的實現介紹了Knockout JS和Web API的結合使用,服務端我們通過Entity Framework對數據進行操作,然后使用API控制器開放接口讓客戶端訪問,然后我們使用Knockout JS的數據綁定和事件處理方法實現了動態的頁面顯示,最后,我們使用了BootStrap CSS美化了一下程序界面。

      本文僅僅是介紹了Knockout JS和Web API的結合使用,如果大家想進一步學習,參考如下鏈接。

      參考

      posted @ 2014-02-28 22:07  JK_Rush  閱讀(5603)  評論(1)    收藏  舉報
      主站蜘蛛池模板: 亚洲国产日韩a在线播放| 国产成人无码aa片免费看| 亚洲一区二区三区黄色片| 性色av极品无码专区亚洲| 国产成人高清精品亚洲一区| 亚洲精品免费一二三区| 中文字幕少妇人妻精品| 午夜AAAAA级岛国福利在线| 国产永久免费高清在线观看| 久久丁香五月天综合网| 丝袜人妖av在线一区二区| 中文字幕无码不卡一区二区三区 | 91老熟女老人国产老太| 这里只有精品在线播放| 欧洲无码一区二区三区在线观看| 潮喷失禁大喷水无码| 成人一区二区三区久久精品| 呼伦贝尔市| 国产成人精品中文字幕| 亚洲中文字幕伊人久久无码| 亚洲免费成人av一区| 国产超碰人人做人人爱| 55夜色66夜色国产精品视频| 久久精品免视看国产成人| 亚洲真人无码永久在线| 开心一区二区三区激情| 一区二区三区av天堂| 久久久亚洲欧洲日产国码606 | 国产超高清麻豆精品传媒麻豆精品 | 久久人妻精品国产| 免费看欧美日韩一区二区三区| 国产做a爱片久久毛片a片| 国产成人高清亚洲综合| 伊人久久大香线蕉av色婷婷色 | 国精品无码人妻一区二区三区| 精品尤物国产尤物在线看| 中文字幕国产精品资源| 毛片大全真人在线| 精品一区二区三区日韩版| 国产精品久久久一区二区三区| 国产成人无码免费视频麻豆|