คำนำ: บทความก่อนหน้านี้แนะนำการห่อหุ้มของการเพิ่ม KO การลบการดัดแปลงและการค้นหาซึ่งบันทึกรหัส JS จำนวนมาก บล็อกเกอร์เป็นคนที่ชอบขี้เกียจ เขามักจะรู้สึกว่าการเพิ่มเติมพื้นฐานการลบการดัดแปลงและการตรวจสอบสามารถสร้างเอฟเฟกต์หน้าผ่านเครื่องมือได้โดยตรงและไม่จำเป็นต้องใช้รหัส นั่นจะเจ๋งมาก ดังนั้นฉันจึงศึกษาไวยากรณ์ของ T4 แม้ว่าฉันจะไม่เชี่ยวชาญอย่างเต็มที่ แต่ฉันก็มีความเข้าใจทั่วไป ดังนั้นบทความของวันนี้: สร้างหน้าผ่านเทมเพลต T4 อย่างรวดเร็ว
บทความซีรีส์ที่น่าพิศวง:
Bootstarpable และ KnockoutJs รวมกันเพื่อให้ได้ฟังก์ชั่นของการเพิ่มการลบการแก้ไขและการตรวจสอบ [1]
bootstary และ knockoutjs รวมกันเพื่อให้ได้ฟังก์ชั่นของการเพิ่ม, การลบ, การแก้ไขและการตรวจสอบ [2]
Bootstarpable + Knockoutjs รวมกันเพื่อรับรู้วิธีแก้ปัญหาของการเพิ่มการลบการแก้ไขและการตรวจสอบ (3) สอง ViewModels สามารถทำให้การเพิ่มการลบ, การแก้ไขและการตรวจสอบเสร็จสมบูรณ์
1. บทนำเกี่ยวกับการใช้ T4
เรารู้ว่าเมื่อเพิ่มมุมมองใน MVC เอฟเฟกต์หน้าของการเพิ่มการลบการแก้ไขและการตรวจสอบสามารถสร้างได้โดยอัตโนมัติ นั่นเป็นเพราะ MVC มีเทมเพลตพื้นฐานในตัวสำหรับการเพิ่มการลบการแก้ไขและการตรวจสอบ ไวยากรณ์ของเทมเพลตเหล่านี้คือการใช้ T4 ดังนั้นเทมเพลตเหล่านี้อยู่ที่ไหน หลังจากค้นหาบทความที่เกี่ยวข้องฉันพบว่าที่ตั้งของ MVC4 และเทมเพลตเวอร์ชันด้านล่างนั้นแตกต่างจาก MVC5 และสูงกว่ามาก
•ตำแหน่งเทมเพลตสำหรับ MVC4 และเวอร์ชันต่อไปนี้: VS Directory การติดตั้ง +/itemTemplates/CSHARP/WEB/MVC 2/CODETEMPLATES ตัวอย่างเช่นไฟล์ D:/โปรแกรมของบล็อกเกอร์ (x86)/Microsoft Visual Studio 12.0/Common7/IDE/ITEMTEMPLATES/CSHARP/WEB/MVC 4/CODETEMPLATES
ค้นหาเทมเพลตที่สอดคล้องกับ CSHTML และมีไฟล์ TT ที่เกี่ยวข้องที่เพิ่มลบแก้ไขและตรวจสอบ
• MVC5 และเหนือเทมเพลตตำแหน่ง: ให้ตำแหน่งเทมเพลตโดยตรงของบล็อกเกอร์ D:/ไฟล์โปรแกรม (x86)/Microsoft Visual Studio 12.0/Common7/IDE/EXTENSIONS/MICROSOFT/WEB/MVC/Scaffolding/Templates
เมื่อคุณรู้สิ่งนี้ขั้นตอนต่อไปคือการสร้างเทมเพลตใหม่และเพิ่มเนื้อหาที่สร้างขึ้นเอง คุณสามารถคัดลอกรายการและแก้ไขเทมเพลตโดยตรงไปยังการเปลี่ยนแปลงตนเอง แต่หลังจากคิดถึงมันแล้วมันจะดีกว่าที่จะไม่สัมผัส MVC ในตัว การสร้างแม่แบบของคุณเองไม่ดีกว่า
สร้างโฟลเดอร์ใหม่ภายใต้ไดเรกทอรีรูทของโครงการเว็บปัจจุบันตั้งชื่อมัน codetemplates จากนั้นคัดลอกโฟลเดอร์เทมเพลตสองโฟลเดอร์ MVCCONTROLLEREMPTY และ MVCView ในเทมเพลต MVC ไปยังโฟลเดอร์ codetemplates ลบเทมเพลตดั้งเดิมภายใน
ด้วยวิธีนี้เมื่อเราเพิ่มคอนโทรลเลอร์ใหม่และสร้างมุมมองใหม่เราสามารถดูเทมเพลตที่กำหนดเองของเรา:
2. การแนะนำรหัส T4
ข้างต้นแนะนำวิธีสร้างเทมเพลตของคุณเอง หลังจากสร้างเทมเพลตคุณควรเริ่มบรรจุเนื้อหาที่เกี่ยวข้องลงไป หากไวยากรณ์ของ T4 ขยายออกบทความนั้นจะไม่มีที่สิ้นสุด ชาวสวนที่สนใจสามารถค้นหาในสวน ยังมีบทความมากมาย มาดูเนื้อหาเทมเพลตสองสามรายการที่นี่ อีกสิ่งที่ควรทราบคือดูเหมือนว่าหลังจาก MVC5 ไฟล์คำต่อท้ายของเทมเพลตของ T4 เปลี่ยนเป็น T4 และเทมเพลตก่อนหน้านี้มักจะจบลงด้วย TT เสมอ โดยไม่ต้องดูความแตกต่างในไวยากรณ์ของพวกเขาคาดว่าจะมีความแตกต่างเล็กน้อย
1. Controller.cs.t4
ทำไมต้องเขียนเทมเพลตคอนโทรลเลอร์ที่ว่างเปล่านี้ใหม่ บล็อกเกอร์คิดว่าวิธีการเพิ่มจำนวนมากในการเพิ่มการลบการแก้ไขและการตรวจสอบจำเป็นต้องเขียนด้วยตนเองและเป็นปัญหามากมายในการเขียนเทมเพลตโดยตรง มาดูรหัสการใช้งานในเทมเพลตกันเถอะ:
<##@ template language = "c#" hostspecific = "true"#> <##@ extension output = "cs"#> <#@ parameter type = "system.string" name = "controllerName"#> <#@ parameter type = "system.string" name = "controllerrootname"#> name = "areaname"#> <#var index = controllerName.lastindexof ("คอนโทรลเลอร์"); var modelname = controllerName.substring (0, index);#> การใช้ระบบ; การใช้ system.collections.generic; การใช้ system.linq; การใช้ system# #>: คอนโทรลเลอร์ {public ActionResult index () {return view ();} public ActionResult Edit (< #= modelName #> รุ่น) {return view (รุ่น);} [httpget] สาธารณะ jsonResult รับ jsonResult add (<#= modelName#> odata) {<#= modelName#> model.add (odata); return json (ใหม่ {}, jsonrequestbehavior.allowget);} // update entity #> model.update (odata); return json (ใหม่ {}, jsonrequestbehavior.allowget);} // ลบเอนทิตี [httppost] สาธารณะ jsonresult ลบ (รายการ << #= modelname #> odata) {< #= modelname jsonRequestBehavior.allowget);}}}เนื้อหานี้ไม่ยากที่จะเข้าใจ เพียงตรวจสอบรหัสคอนโทรลเลอร์ที่สร้างขึ้น:
การใช้ระบบ; การใช้ System.collections.generic; การใช้ System.linq; การใช้ System.web; การใช้ System.web.mvc; การใช้ testko.models; namespace testko.controllers {คลาสสาธารณะ usercontroller: ตัวควบคุม {public ActionResult index jsonResult get (int limit, int offset) {return json (ใหม่ {}, jsonrequestbehavior.allowget);} // เพิ่มเอนทิตี [httppost] สาธารณะ jsonresult เพิ่ม (ผู้ใช้ odata) {usermodel.add (odata); return json (ใหม่ {}, jsonRequestBehavior.allowget);} // update entity [httppost] การอัปเดต JsonResult สาธารณะ (ผู้ใช้ ODATA) {USERMODEL.UPDATE (ODATA); return json (ใหม่ {}, jsonrequestbehavior.allowget);} // ลบเอนทิตี [httppost] JsonResult Public Public (รายการ <user> Odata) {usermodel.delete (odata); return json (ใหม่ {}, jsonrequestbehavior.allowget);}}}}2. koindex.cs.t4
เทมเพลตนี้ส่วนใหญ่จะใช้เพื่อสร้างหน้ารายการโดยมีรหัสทั่วไปดังนี้:
<##@ template language = "c#" hostspecific = "true"#> <##@ extension output = ". cshtml"#> <#@ include file = "imports.include.t4"#> <#// output If-statems if (iSlayOutPagesElected) {#>@{viewbag.title = "<#= viewName#>"; <#ถ้า (! string.isnullorEmpty (layoutpageFile)) {#> layout = "<#= layoutPageFile#>"; html> <html> <head> <meta name = "viewport" content = "width = device-width"/> <title> < #= viewName #> </title> <link href = "~/content/bootstrap/css/bootstrap.min.css href = "~/content/bootstrap-table/bootstrap-table.min.css" rel = "stylesheet"/> <script src = "~/scripts/jQuery-1.9.1.min.js"> </script> src = "~/content/bootstrap-table/bootstrap-table.min.js"> </script> <script src = "~/content/bootstrap-table/locale/bootstrap-table-zh-cn.js"> </script> <script src = "~/scripts/scripts/script src = "~/scripts/knockout/extensions/knockout.mapping-latest.js"> </script> <script src = "~/scripts/extensions/knockout.index.js"> </script> <script src = "scripts/scripts src = "~/scripts/extensions/knockout.index.js"> </script> <script src = "~/scripts/extensions/knockout.bootstraptable.js"> </script> <script type = "text/javaScript"> $ (ฟังก์ชัน () "/<#= viewDatatypeshortName#>/get", pageize: 2,}, urls: {del: "/<#= viewDatatypeshortName#>/ลบ", แก้ไข: "/<#= viewDatatypeshortName#>/edit" : {}}; ko.bindingViewModel (viewModel);}); </script> </head> <body> <#pushindent ("");}#> <div id = "แถบเครื่องมือ"> <button data-bind = "คลิก: addClick type = "ปุ่ม"> <span aria-hidden = "true"> </span> แก้ไข </button> <ปุ่ม data-bind = "คลิก: deleteClick" type = "ปุ่ม"> <span aria-hidden = "true"> </span> ลบ </button> </div> data-checkbox = "true"> </th> <#ienumerable <propertyMetadata> คุณสมบัติ = modelMetAdata.properties; foreach (คุณสมบัติ PropertyMetadata ในคุณสมบัติ) {ถ้า (property.scaffold &&! #> "> <#= getValueExpression (คุณสมบัติ)#> </th> <#}}#> </tr> </thead> </table> <#// รหัสต่อไปนี้ปิดแท็กที่ใช้ในกรณีของมุมมองโดยใช้หน้าเค้าโครง {clearIndent ();#> </body> </html> <#}#> <#@ include file = "modelMetAdatafunctions.cs.include.t4"#>เพิ่มดัชนีมุมมองและเลือกเทมเพลตนี้
เนื้อหาหน้าได้รับ
@{layout = null;} <! doctype html> <html> <head> <meta name = "viewport" content = "width = device-width"/> <title> ดัชนี </title> <link href = "/content/bootstrap/css/css/bootstrap.css href = "~/content/bootstrap-table/bootstrap-table.min.css" rel = "stylesheet"/> <script src = "~/scripts/jQuery-1.9.1.min.js"> </script> src = "~/content/bootstrap-table/bootstrap-table.min.js"> </script> <script src = "~/content/bootstrap-table/locale/bootstrap-table-zh-cn.js"> </script> <script src = "~/scripts/scripts/script src = "~/scripts/knockout/knockout-3.4.0.min.js"> </script> <script src = "~/scripts/knockout/extensions/knockout.mapping-latest.js"> </script> <script src = "~/scripts/scripts src = "~/scripts/extensions/knockout.bootStraptable.js"> </script> <script type = "text/javascript"> $ (function () {var viewmodel = {bindid: "div_index", tableParams: {url:/url:/url:/url "/ผู้ใช้/แก้ไข", เพิ่ม: "/ผู้ใช้/แก้ไข",}, queryCondition: {}}; ko.bindingViewModel (ViewModel);}); </script> </head> <body> <div id = "ปุ่ม adrid data-bind = "คลิก: editclick" type = "ปุ่ม"> <span aria-hidden = "true"> </span> แก้ไข </button> <button data-bind = "คลิก: deleteClick" type = "ปุ่ม"> <span aria-hidden = "true"> </span> data-checkbox = "true"> </th> <th data-field = "ชื่อ"> ชื่อ </th> <th data-field = "fullName"> fullName </th> <th data-field = "อายุ"> อายุ </th> <th data-field = "des"> des </th> data-field = "strCreateTime"> strCreateTime </th> </tr> </thead> </table> </body> </html> index.cshtmlเราย้าย ViewModel ที่กล่าวถึงในบทความก่อนหน้าไปยังหน้าเพื่อที่เราจะได้ไม่ต้องผ่านจากคอนโทรลเลอร์ทุกครั้ง เปลี่ยนชื่อคอลัมน์ของตารางเล็กน้อยและหน้าสามารถรันได้
นี่คือจุดสองสามจุดที่จะปรับให้เหมาะสม:
(1) เงื่อนไขการสืบค้นยังไม่ได้รับการสร้าง หากคุณศึกษาไวยากรณ์ของ T4 ลึกลงไปเล็กน้อยคุณสามารถเพิ่มคุณสมบัติให้กับฟิลด์ที่จำเป็นต้องสอบถามเพื่อระบุว่าจะต้องมีการสอบถามฟิลด์ใดจากนั้นจะสร้างเงื่อนไขการสืบค้นที่สอดคล้องกันโดยอัตโนมัติ
(2) ชื่อคอลัมน์ของตารางดูเหมือนจะถูกสร้างขึ้นผ่านคุณสมบัติของฟิลด์ของแอตทริบิวต์ สิ่งนี้คล้ายกับจุดแรกและทั้งคู่ต้องศึกษาไวยากรณ์ของ T4
3. Koedit.cs.t4
หน้าเทมเพลตที่สามคือเทมเพลตที่แก้ไขและรหัสคร่าวๆของมันมีดังนี้:
<##@ template language = "c#" hostspecific = "true"#> <##@ extension output = ". cshtml"#> <#@ include file = "imports.include.t4"#>@ model <#= viewDatatypename#> <#// ช่องทำเครื่องหมายซึ่งใช้ div ใน bootstrapstring booltype = "system.boolean"; เวอร์ชันที่ต้องการ mvcversion = เวอร์ชันใหม่ ("5.1.0.0"); bool iscontrolhtmlattributessupported = mvcversion> = markpversion; View.if ปกติ (isPartialView) {#> <#} อื่นถ้า (isLayOutPagesElected) {#>@{viewBag.title = "<#= viewName#>"; <#if (! string.isnullorempty ViewName#> </h2> <#} else {#>@{layout = null;} <! doctype html> <html> <head> <meta name = "viewport" content = "width = device-width"/> <title> {#> <#ถ้า (! islayoutpageselected && isbundleconfigpresent) {#>@scripts.render ("~/bundles/jQuery")@scripts.render ("~/bundles/jQueryVal") <#}#> <# src = "~/scripts/jQuery-<#= jQueryVerversion#>. min.js"> </script> <script src = "~/scripts/jQuery.validate.min.js"> </script> id = "formedit">@html.Hiddenfor (model => model.id) <div> <#ienumerable <propertyMetadata> คุณสมบัติ = modelMetAdata.properties; foreach (คุณสมบัติ PropertyMetadata ในคุณสมบัติ) {ถ้า (คุณสมบัติ {#> <div>@html.labelfor (model => model. <#= getValueExpression (คุณสมบัติ)#>, "<#= getValueexpression (คุณสมบัติ)#>" ใหม่ {@class = "collabel col-xs-2"}) @class = "form-control", data_bind = "value: editModel. <#= getValueExpression (คุณสมบัติ)#>"}) </div> </div> <#}}#> </div> <div> aria-hidden = "true"> </span> บันทึก </button> </div> </form> <#var index = viewDatatypename.lastindexof ("."); var modelName = viewDatatypename.substring (ดัชนี+1 src = "~/scripts/extensions/knockout.edit.js"> </script> <script type = "text/javascript"> $ (function () {var model = @html.raw model.id == 0? "/< #= modelName #>/add": "/< #= modelName #>/update"}, ผู้ตรวจสอบ: {ฟิลด์: {ชื่อ: {ผู้ตรวจสอบ: {notempty: {ข้อความ: 'ชื่อไม่ว่าง! }); </script> <#ถ้า (islayoutpageselected && referencescriptlibraries && isbundleconfigpresent) {#>@scripts ส่วน {@scripts.render ("~/bundles/jQueryVal")} <#}#> <# src = "~/scripts/jQuery-< #= jQueryVerversion #>. min.js"> </script> <script src = "~/scripts/jQuery.validate.min.js"> </script> <script src = "~/scripts/jQuery. src = "~/scripts/jQuery.validate.unoBtrive.min.js"> </script> <#}#> <#// รหัสต่อไปนี้ปิดแท็กที่ใช้ในกรณีของมุมมองโดยใช้หน้าเค้าโครง {clearIndent ();#> </body> </html> <#}#> <#@ include file = "modelMetAdatafunctions.cs.include.t4"#>รหัสที่สร้างขึ้น:
@model testko.models.user <form id = "formedit">@html.hiddenfor (model => model.id) <div> <div>@html.labelfor (model => model.name, "ชื่อ", {@class = "control-label-label-xs-2"} = "form-control", data_bind = "value: editModel.name"}) </div> </div> <div> @html.labelfor (model => model.fullname, "fullname", {@class = "control-label-xs-2"} "form-control", data_bind = "value: editmodel.fullname"}) </div> </div> <div> @html.labelfor (model => model.age, "อายุ", ใหม่ {@class = "control-label col-xs-2"} "form-control", data_bind = "value: editModel.age"}) </div> </div> <div> @html.labelfor (model => model.des, "DES", ใหม่ {@class = "control-label col-xs-2" data_bind = "value: editModel.des"}) </div> </div> <div> @html.labelfor (model => model.createTime, "createTime", ใหม่ {@class = "control-label col-xs-2"}) data_bind = "value: editModel.createTime"}) </div> </div> <div> @html.labelfor (model => model.strcreateTime, "strCreateTime", ใหม่ {@class = "control-label-label col-xs-2"}) "form-control", data_bind = "value: editModel.strCreateTime"}) </div> </div> <div> <div> <button type = "ปุ่ม" data-dismiss = "modal"> <span aria-hidden = "true"> </span> src = "~/scripts/extensions/knockout.edit.js"> </script> <script type = "text/javascript"> $ (function () {var model = @html.raw model.id == 0? "/ผู้ใช้/เพิ่ม": "/update/update"}, validator: {ฟิลด์: {ชื่อ: {ตัวตรวจสอบแน่นอนว่ารหัสยังต้องมีการแก้ไขเล็กน้อย ด้วยการเพิ่มหน้าเทมเพลตที่กำหนดเองตราบใดที่โมเดลเอนทิตีที่สอดคล้องกันในพื้นหลังถูกสร้างขึ้นคุณจะต้องสร้างมุมมองที่กำหนดเองใหม่สองมุมที่ด้านหน้าและการเพิ่มการลบการดัดแปลงและการค้นหาอย่างง่ายสามารถเสร็จสิ้นได้โดยไม่ต้องเขียนประโยคของรหัส JS
3. การผูกองค์ประกอบที่เลือก
ข้างต้นแนะนำไวยากรณ์ของการเพิ่มบรรจุภัณฑ์ T4 การลบการดัดแปลงและการค้นหา ส่วนประกอบทั้งหมดของหน้าเป็นกล่องข้อความโดยทั่วไป อย่างไรก็ตามในโครงการจริงหน้าเคียวรีและการแก้ไขจำนวนมากจะมีกล่องแบบเลื่อนลงเพื่อแสดง เราควรจัดการกับกล่องดรอปดาวน์อย่างไร? หากคุณไม่เก็บเป็นความลับเพียงแค่ให้ทางออก ตัวอย่างเช่นเราสามารถวางแหล่งข้อมูลของกล่องดรอปดาวน์ในพื้นหลังในหน้าแก้ไข
เอนทิตีของผู้ใช้
[DataContract] ผู้ใช้ระดับสาธารณะ {[Datamember] ID INT สาธารณะ {รับ; ชุด; } [Datamember] ชื่อสตริงสาธารณะ {get; ชุด; } [Datamember] Public String FullName {get; ชุด; } [Datamember] Public Int อายุ {รับ; ชุด; } [Datamember] Public String des {get; ชุด; } [Datamember] Public DateTime CreateTime {get; ชุด; } [DataMember] String Public StrCreateTime {get; ชุด; } [DataMember] Public String DepartmentID {get; ชุด; } [Datamember] แผนกวัตถุสาธารณะ {รับ; ชุด; -จากนั้นแก้ไขหน้า
Public ActionResult Edit (โมเดลผู้ใช้) {model.departments = DepartmentModel.getData (); Return View (รุ่น);}จากนั้นผูกปลายด้านหน้า
<div> <label for = "txt_des"> แผนก </label> <select id = "sel_dept" data-bind = "ตัวเลือก: editModel.departments, opptivestext: 'name', opotion
รหัส JS ไม่จำเป็นต้องแก้ไข เมื่อเพิ่มหรือแก้ไขเขตข้อมูลแผนกสามารถเพิ่มลงใน ViewModel โดยอัตโนมัติ
แน่นอนว่ากล่องแบบเลื่อนลงที่ใช้โดยโครงการของเราหลายโครงการไม่ได้เลือกเพียงเพราะสไตล์การเลือกแบบง่าย ๆ นั้นน่าเกลียดจริง ๆ ดังนั้นส่วนประกอบที่เลือกจำนวนมากจึงถูกสร้างขึ้นเช่น Select2, MultiSelect ฯลฯ ที่แชร์โดยบล็อกเกอร์มาก่อน เมื่อใช้ส่วนประกอบเหล่านี้เพื่อเริ่มต้นการเลือกคุณจะพบว่ากล่องดรอปดาวน์บนอินเทอร์เฟซไม่ใช่แท็ก Select ง่าย ๆ อีกต่อไป แต่ประกอบด้วยแท็กอื่น ๆ อีกมากมายที่ปรับแต่งโดยส่วนประกอบ ลองใช้องค์ประกอบ Select2 เป็นตัวอย่างเพื่อดูว่าเป็นไปได้ที่จะเริ่มต้นโดยตรงตามข้างต้นหรือไม่
เราเพิ่มประโยคสุดท้ายเพื่อแก้ไขรหัส JS ที่เริ่มต้นโดยหน้า:
<script type = "text/javascript"> $ (function () {var model = @html.raw (newtonsoft.json.jsonconvert.serializeObject (รุ่น)); var viewModel = {formId: "formEdit" "/ผู้ใช้/อัปเดต"}, ตัวตรวจสอบ: {ฟิลด์: {ชื่อ: {ตัวตรวจสอบ: {notempty: {ข้อความ: 'ชื่อไม่สามารถว่างเปล่า!'}}}}}}};ผ่านการเพิ่มเติมและการแก้ไขสิ่งนี้เป็นไปได้จริง! การวิเคราะห์เหตุผลแม้ว่าหน้า HTML จะเปลี่ยนแปลงหลังจากเริ่มต้นองค์ประกอบ Select2 แต่ส่วนประกอบจะนำเสนอค่าที่เลือกไว้ในตัวควบคุมการเลือกดั้งเดิมในที่สุด ฉันไม่ทราบว่าส่วนประกอบการเริ่มต้นที่เลือกอื่น ๆ จะเป็นแบบนี้ยกเว้น Select2 และพวกเขากำลังรอการตรวจสอบหรือไม่ อย่างไรก็ตามมีสิ่งหนึ่งที่จะอธิบายที่นี่ ก่อนที่จะเริ่มต้น Select2 ตัวเลือกในกล่องดรอปดาวน์จะต้องถูกผูกไว้กับค่านั่นคือการเริ่มต้นของส่วนประกอบจะต้องวางหลังจาก ko.applybinding ()
4. สรุป
ณ จุดนี้ KO รวมกับการสร้างเทมเพลต bootstable และการใช้งานการควบคุมที่เลือกนั้นมีอยู่โดยทั่วไปและแน่นอนว่ามันยังคงต้องมีการปรับปรุง หากคุณมีเวลาในภายหลังบล็อกเกอร์จะแยกแยะการรวมกันของส่วนประกอบส่วนหน้าอื่น ๆ และ KO เช่นการควบคุมวันที่ที่พบบ่อยที่สุดของเรา หากคุณมีคำถามใด ๆ โปรดฝากข้อความถึงฉันและบรรณาธิการจะตอบกลับทุกคนในเวลา ขอบคุณมากสำหรับการสนับสนุนเว็บไซต์ Wulin.com!