Pendahuluan: Artikel sebelumnya memperkenalkan enkapsulasi penambahan KO, penghapusan, modifikasi dan pencarian, yang memang menghemat banyak kode JS. Blogger adalah orang yang suka malas. Dia selalu merasa bahwa penambahan dasar, penghapusan, modifikasi, dan cek ini dapat secara langsung menghasilkan efek halaman melalui alat, dan tidak diperlukan kode. Itu akan sangat keren. Jadi saya mempelajari tata bahasa T4. Meskipun saya tidak sepenuhnya menguasainya, saya memiliki pemahaman umum. Jadi artikel hari ini: dengan cepat menghasilkan halaman melalui template T4.
Artikel Seri KnockoutJs:
Bootstraptable dan knockoutjs bergabung untuk mencapai fungsi menambahkan, menghapus, memodifikasi dan memeriksa [1]
Bootstraptable dan knockoutjs bergabung untuk mencapai fungsi menambahkan, menghapus, memodifikasi dan memeriksa [2]
Bootstraptable + KnockoutJs bergabung untuk mewujudkan solusi menambahkan, menghapus, memodifikasi, dan memeriksa (3) Dua viewmodels dapat menyelesaikan penambahan, menghapus, memodifikasi dan memeriksa
1. Pengantar Penggunaan T4
Kita tahu bahwa ketika menambahkan tampilan di MVC, efek halaman dari menambahkan, menghapus, memodifikasi dan memeriksa dapat dihasilkan secara otomatis. Itu karena MVC memiliki templat dasar bawaan untuk menambah, menghapus, memodifikasi, dan memeriksa. Sintaks dari templat ini adalah menggunakan T4, jadi di mana templat ini? Setelah mencari artikel terkait, saya menemukan bahwa lokasi template versi MVC4 dan di bawah ini sangat berbeda dari MVC5 dan di atas.
• Lokasi templat untuk MVC4 dan versi berikut: VS Direktori Instalasi +/ItemTemplates/CSharp/Web/MVC 2/Codetemplate. Misalnya, file D:/Program Blogger (x86)/Microsoft Visual Studio 12.0/Common7/IDE/ItemTemplates/Csharp/Web/MVC 4/Codetemplate.
Temukan templat yang sesuai dengan CSHTML, dan ada file TT yang sesuai yang menambah, menghapus, memodifikasi dan memeriksa
• Lokasi Templat MVC5 dan Di Atas: Langsung Berikan Lokasi Templat File Blogger D:/Program (x86)/Microsoft Visual Studio 12.0/Common7/IDE/Extensions/Microsoft/Web/MVC/SCAFFOLDING/Templat
Setelah Anda mengetahui hal ini, langkah selanjutnya adalah merombak templat dan menambahkan konten yang dihasilkan sendiri. Anda dapat secara langsung menyalin daftar dan mengedit templat ke transformasi diri, tetapi setelah memikirkannya, lebih baik tidak menyentuh MVC bawaan. Tidak lebih baik membangun templat Anda sendiri.
Buat folder baru di bawah direktori root dari proyek web saat ini, beri nama Codetemplates, dan kemudian salin dua folder templat MVCControllerEmpty dan MVCView di dalamnya MVC ke folder Codetemplates, hapus templat asli di dalamnya, dan kemudian buat beberapa templat baru, seperti yang ditunjukkan pada gambar di bawah ini:
Dengan cara ini, ketika kami menambahkan controller baru dan membuat tampilan baru, kami dapat melihat template khusus kami:
2. PENDAHULUAN KODE T4
Di atas memperkenalkan cara membuat template Anda sendiri. Setelah template dibangun, Anda harus mulai memasukkan konten yang sesuai ke dalamnya. Jika tata bahasa T4 diperluas, artikel itu tidak ada habisnya. Tukang kebun yang berminat dapat mencari di taman. Masih ada cukup banyak artikel. Mari kita lihat beberapa konten template di sini. Hal lain yang perlu diperhatikan adalah bahwa tampaknya setelah MVC5, akhiran file template dari T4 telah diubah menjadi T4, dan templat sebelumnya selalu berakhir dengan TT. Tanpa melihat perbedaan sintaksnya, diperkirakan harus ada sedikit perbedaan.
1. Controller.cs.t4
Mengapa menulis ulang template pengontrol kosong ini? Blogger berpikir bahwa banyak metode untuk menambahkan, menghapus, memodifikasi, dan memeriksa perlu ditulis secara manual dan sangat sulit untuk menulis templat secara langsung. Mari kita lihat kode implementasi di templat:
<#@ Templat Bahasa = "C#" HostSpecific = "true"#> <#@ output extension = "cs"#> <#@ parameter type = "System.string" name = "ControlName"#> <#@@ parameter type = "System.string" name = "controllerRootname"#> <##parameter = "System.string" name = "NOMACE"#<###@ parameter = "System" parameter "name =" name "###" name "##" name "name. type = "System.string" name = "AREAName"#> <#var index = controllERName.LastIndexOf ("controller"); var modelname = controllername.substring (0, index);#> menggunakan System; menggunakan System.collection.generic; menggunakan System.linq; menggunakan System.web; menggunakan System.web.web.collections; menggunakan test. #> {kelas publik < #= controllername #>: controller {public actionResult index () {return view ();} public actionResult edit (< #= modelname #> model) {return view (model);} [httpget] public jsonResult get (limit int, int offset) {return json (httpget] public jsonResult (batas int, int offset) {return json ({{}} {{{{{} {{{{{} {{{{{{{} {{{{New) public (ht. entity[HttpPost]public JsonResult Add(<#= ModelName #> oData){<#= ModelName #>Model.Add(oData);return Json(new { }, JsonRequestBehavior.AllowGet);}//Update entity [HttpPost]public JsonResult Update(<#= ModelName #> oData){<#= ModelName #> Model.update (odata); return json (baru {}, jsonrequestbehavior.allowget);} // hapus entitas [httppost] public JsonResult delete (daftar << #= modelName #> odata) {< #= modelName #> model.delete (odata) {< # #= modelName #> model.delete (odata) {< # #= modelName #> model.delete (odata) {< # #= modelName #> model.delete (odATa) Jsonrequestbehavior.allowget);}}}Konten ini tidak sulit untuk dipahami. Cukup periksa kode pengontrol yang dihasilkan:
Menggunakan Sistem; Menggunakan System.Collections.Generic; Menggunakan System.linq; Menggunakan System.Web; Menggunakan System.Web.MVC; Menggunakan TestKo.Models; Namespace Testko.Controllers {Public Class UserController: Controller {public ActionResult () {return view ();} public ActionResulul edit {public ActionResult () {return view ();} public ActionResult edit {public ActionResult () {return view ();} public ActionResult edit {public ActionResult () {return view ();} public ActionResult edit {public ActionResUl JsonResult get (batas int, int offset) {return json (baru {}, jsonrequestbehavior.allowget);} // tambahkan entitas [httppost] public jsonResult add (pengguna odata) {usermodel.add (odata); return json (baru {}, jsonRequestBehavior.allowget);} // pembaruan entitas [httppost] pembaruan public jsonResult (pengguna odata) {usermodel.update (odata); return json (baru {}, jsonrequestbehavior.allowget);} // hapus entitas [httppost] public JsonResult delete (daftar <user> odata) {userModel.delete (odata); kembalikan json (baru {}, jsonrequestbehavior.allowget);}}}2. Koindex.cs.t4
Templat ini terutama digunakan untuk menghasilkan halaman daftar, dengan kode umum sebagai berikut:
<#@ Templat Bahasa = "C#" hostSpecific = "true"#> <#@ output extension = ". Cshtml"#> <#@ include file = "imports.include.t4"#> <#// tontonan/ {eview {eview {eview {ever {ever o. if (isLayOutPageSelected) {#>@{viewbag.title = "<#= viewName#>"; <#if (! String.isnullorempty (LayoutPageFile)) {#> tata letak = "<#= LayOutPageFile#>"; <#}#> <##}} {#} {NOB layoutpageFile; html> <html> <head> <meta name = "viewport" content = "width = device-width"/> <itement> < #= viewName #> </iteme> <link href = "~/content/bootstrap/css/bootstrap.min.css" rel = "stylesheet"/css/bootstrap.min.css "rel =" stylesheet " href = "~/content/bootstrap-table/bootstrap-table.min.css" rel = "stylesheet"/> <script src = "~/scripts/jQuery-1..9.1.min.js"> </Script> <cript src = "~/content/bootstrap/js/bootstrap. src = "~/content/bootstrap-table/bootstrap-able.min.js"> </script> <script src = "~/content/bootstrap-table/locale/bootstrap-table-zh-cn.js"> </script> <script src = "/~/knockout/knockout-3.4 src = "~/skrip/knockout/extensions/knockout.mapping-latest.js"> </script> <script src = "~/skrip/ekstensi/knockout.index.js"> </script> <script src = "~/skrip/ekstensi/knockout.index.js"> </script> skrip> <cript> skrip> </skrip/skrip src = "~/skrip/ekstensi/knockout.index.js"> </script> <script src = "~/skrip/ekstensi/knockout.bootstraptable.js"> </script> <script type = "text/javascript"> $ () {var viewModel = {bindid: " "/<#= ViewDataTyPesHortName#>/get", pageSize: 2,}, URLS: {del: "/<#= viewDataTyPesHortName#>/delete", edit: "/<#= viewDataPeshortName#>/edit", add: "/<##= viewDatePeshortName#>/edit", add: "/<####nyCeshynition : {}}; ko.bindingviewModel (viewmodel);}); </script> </adept> <body> <#pushindent ("");}#> <div id = "toolbar"> <tombol-bind-bind = "klik: addClick" type = "Tombol"> <span aria-hidden = "tlik =" tlik = "tekan"> Tombol ADDCLICK "TOLUCK =" "Tombol"> <span-bak = "TOLK =" TOLK = "TOLK/TOLKLICK =" TOLPLICK "TOLKLICK =" TOLPLICK = "TOLK-BIND/TOLPLICK =" TOLPLICK = "TOLPLICK =" TOLP-BIND/TOLPLICK = type="button"><span aria-hidden="true"></span>Modify</button><button data-bind="click:deleteClick" type="button"><span aria-hidden="true"></span>Delete</button></div><table data-bind="bootstrapTable:bootstrapTable"><tr><th Data-checkbox = "true"> </th> <#ienumerable <VREASTYMetAdata> properties = ModelMetadata.properties; foreach (PropertiMetadata Properti di Properties) {if (Property.Scaffold & & Property. GetValueExpression (properti)#> </th> <#}}#> </tr> </thead> </able> <#// Kode berikut menutup tag yang digunakan dalam kasus tampilan menggunakan halaman tata letak dan tag html dalam kasus tampilan reguler#> <#if (! ISpartialview & &: iSpartial & & iSparliew & cera! {ClearIndent ();#> </body> </html> <#}#> <#@ include file = "ModelMetAdataFunctions.cs.include.t4"#>Tambahkan indeks tampilan dan pilih template ini
Konten halaman yang diperoleh
@{Tata letak = null;} <! Doctype html> <html> <head> <meta name = "viewport" content = "width = device-width"/> <iteme> index </iteme> <link href = "~/content/bootstrap/css/bootstrap href = "~/content/bootstrap-table/bootstrap-table.min.css" rel = "stylesheet"/> <script src = "~/scripts/jQuery-1..9.1.min.js"> </Script> <cript src = "~/content/bootstrap/js/bootstrap. src = "~/content/bootstrap-table/bootstrap-able.min.js"> </script> <script src = "~/content/bootstrap-table/locale/bootstrap-table-zh-cn.js"> </script> <script src = "/~/knockout/knockout-3.4 src = "~/skrip/knockout/knockout-3.4.0.min.js"> </script> <skrip src = "~/skrip/knockout/ekstensi/knockout.mapping-latest.js"> </script> <script src = "~/skrip/ekstensi/knockout.index.js"> </script src = "~/skrip/ekstensi/knockout.index.js"> </script> </script src = "~/skrip/ekstensi/knockout.bootStraptable.js"> </script> <script type = "text/javascript"> $ (function () {var viewmodel = {bindid: "div_index", tableParams: {url: "/user/get", ukur: 2, 2, 2, {{{URL: "/USER/get", ukur: 2, 2, 2, {{{URL: "/USERIT", {2, 2, {{URL: " "/User/Edit",add : "/User/Edit",},queryCondition :{}};ko.bindingViewModel(viewModel);});</script></head><body><div id="toolbar"><button data-bind="click:addClick" type="button"><span aria-hidden="true"></span>Add</button><button data-bind="click:editClick" type="button"><span aria-hidden="true"></span>Modify</button><button data-bind="click:deleteClick" type="button"><span aria-hidden="true"></span>Delete</button></div><table data-bind="bootstrapTable:bootstrapTable"><tr><th Data-checkbox = "true"> </t> <th-field = "name"> nama </th> <th-field = "fullname"> fullname </t> <th-field = "use"> usia </t> <-field = "des"> </th> <th-field = "createTime"> Data-field = "strcreateTime"> strcreateTime </t> </tr> </thead> </able> </body> </html> index.cshtmlKami memindahkan ViewModel yang disebutkan dalam artikel sebelumnya ke halaman, sehingga kami tidak perlu meneruskannya dari pengontrol setiap saat. Ubah nama kolom tabel sedikit dan halaman dapat berjalan.
Berikut adalah beberapa poin yang harus dioptimalkan:
(1) Kondisi kueri belum dihasilkan. Jika Anda mempelajari sintaks T4 sedikit lebih dalam, Anda dapat menambahkan karakteristik ke bidang yang perlu ditanya untuk mengidentifikasi bidang mana yang perlu ditanya, dan kemudian secara otomatis menghasilkan kondisi kueri yang sesuai.
(2) Nama kolom tabel tampaknya dihasilkan melalui sifat bidang atribut. Ini mirip dengan titik pertama, dan keduanya perlu mempelajari tata bahasa T4.
3. Koedit.cs.t4
Halaman template ketiga adalah templat yang diedit, dan kode kasarnya adalah sebagai berikut:
<#@ Template Bahasa = "C#" HostSpecific = "true"#> <#@ output extension = ". Cshtml"#> <#@ include file = "imports.include.t4"#>@ model <#= viewDataPename#> <#// "Form-Control" atribut "hanya didukung untuk semua editor untuk semua editor () <#//" Form-Control "Atribut hanya didukung untuk semua editor untuk semua editor. kotak centang, yang menggunakan div in bootstrapstring booltype = "system.boolean"; versi wajib Andavcversion = versi baru ("5.1.0.0"); bool isControlHtMlattributessupported = mvcversion a atau markuf a. view.if (isPartialView) {#> <#} else if (isLayOutPageSelected) {#>@{viewbag.title = "<#= viewName#>"; <#if (! string.isnullorempty (layoutpageFile)) {####"tata letak =" <##"<##" <##"tata letak = tata letak#" tata letak = "<#" <#"<#" <#"<#" <#" ViewName#></h2><#} else {#>@{Layout = null;}<!DOCTYPE html><html><head><meta name="viewport" content="width=device-width" /><title><#= ViewName #></title></head><body><#PushIndent(");}#><#if (ReferenceScriptLibraries) {#> <#if (! IsLayOutPageselected && isBundLeConfigPresent) {#>@scripts.render ("~/bundles/jQuery")@scripts.render ("~/bundel/jQueryVal") <#}#> <#lain if (islayout outpageSelected) {{{{{~ {~ script {~ ~ ~ ~ script/jQuery (~ ~ script/jQuery (~ ~ ~ script/jQuery { JQueryVersion#>. Min.js "> </script> <script src =" ~/scripts/jQuery.validate.min.js "> </script> <script src =" ~/skrip/jQuery.validate.unobtrusif.min.js "> </script> <#}#form =" HIDMIN.MIN "> </script> <#}#FORMLE.TMIN. Model.id) <div> <#ienumerable <VREASTYMETADATA> properties = ModelMetAdata.properties; foreach (properti propertymetadata di properti) {if (property.scaffold &&! Property.isprimaryKey &&! #>, "< #= GetValueExpression (properti) #>", baru {@class = "control-label col-xs-2"}) <verv> @html.textboxfor (model => model. < #= GetValueExpression (properti) #>, new {@class = "bentuk =", edit-control ", @ccontrol", form-control ", form-control", form-control ", @class =" {@class = "form-control" ne baru. GetValueExpression (properti)#> "}) </div> </div> <#}}#> </div> <v div> <tombol type =" tombol "data-dismiss =" modal "> <span aria-hidden =" true "</span> tutup </tombol> <tombol type =" kirim "> <span aria-hidden =" true-hidden = "rentang true" = ViewDataTypeName.LastIndexOf(".");var ModelName = ViewDataTypeName.Substring(index+1, ViewDataTypeName.Length-index-1);#><script src="~/Scripts/extensions/knockout.edit.js"></script><script type="text/javascript">$(function () {var model = @Html.raw (newTonsoft.json.jsonconvert.serializeObject (model)); var viewModel = {formid: "formedit", editmodel: model, urls: {formulds: {{{{{{{{{{Model, {validators: {notempty: {pesan: 'Nama tidak dapat kosong!'}}}}}}; ko.bindingeditViewModel (viewModel); {@Scripts.render ("~/bundles/jQueryVal")} <#}#> <#lain if (isLayOutPageSelected && ReferencesScripLibraries) {#> <skrip src = "~/scripts/jQuery-<#= jQueryVersion#>. src = "~/scripts/jQuery.validate.min.js"> </script> <skrip src = "~/skrip/jQuery.validate.unobtrusif.min.min.js"> </script//script screct = "~ ~/jQuery. Digunakan dalam kasus tampilan menggunakan halaman tata letak dan tag tubuh dan html dalam hal halaman tampilan reguler#> <#if (! isPartialView &&! Islayoutpages dipilih) {clearIndent ();#> </body> </html> <#}#> <#@ incled file = "modelSadu.Kode yang dihasilkan:
@model testko.models.user <formric id = "formedit">@html.hiddenfor (model => model.id) <div> <div>@html.lAbelfor (model => model.name, nama "@class =" control-label col-xs-2 "} {@class =" control-label-2 "{@class =" control-label-2 ")) @class = "Form-Control", data_bind = "value: editmodel.name"}) </div> </div> <verv> @html.labelfor (model => model.fullname, "fullname", hlm {@class = "control-label col-xs-2") <full div> @{@class = "control-label col-xs-2") <full div> @haLex. @class = "Form-Control", data_bind = "nilai: editmodel.fullname"}) </div> </div> <verv> @html.labelfor (model => model.age, "usia" {new @class = "control-label col-xs-2"}) <dv> {htclass = "control-label col-xs-2"}) <v> @html. "Form-Control", data_bind = "value: editmodel.age"}) </div> </div> <verv> @html.labelfor (model => model.des, "des", new {@class = "control-label col-xs-2") <v div> @html.textbox = col-label col-xs-2 ") <v div> @html.textbox = col-label col-xs-2") <v div> @html.textbox = col-label col-xs- "Form-Control", data_bind = "nilai: editmodel.des"}) </div> </div> <verv> @html.labelfor (model => model.createTime, "createTime", new {@class = "control-label col-xs-2"}) <dv> @htmlass = "control-label col-xs-2") <dv> @ht. "Form-Control", Data_Bind = "Nilai: EditModel.CreateTime"}) </div> </div> <verv>@html.labelfor (model => model.strCreateTime, "streatime," @class = "col-label (col-label," @class = "col-label col-xs-2"), he NewcreateTime, he NewcreateTime, he NewcreateTime, new @class = {@class = "Form-Control", Data_Bind = "Value: EditModel.StrCreateTime"}) </Div> </Div> <v> <Tombol type = "Tombol" Data-Dismiss = "Modal"> <span aria-hidden = "true"> <span> tutup </tombol </tombol type "> <span-hidden =" true "> </span> tutup </tombol </tombol type"> <span "> </rentang"> </span "> tutup </tombol> <button> <span"> <span "> </rentang aria-hidden = "true"> </span> save </button> </div> </form> <script src = "~/skrip/ekstensi/knockout.edit.js"> </script> <script type = "text/javascript"> $ (function () {var model = @html.raw (newtonsoft.json.json.json. = {FormId: "FormEdit", EditModel: Model, URLS: {Kirim: Model.id == 0? "/User/Add": "/User/Update"}, Validator: {Fields: {Name: {Validators: {notempty: {pesan: 'Nama itu tidak bisa kosong! '}}}}}}}; ko.bindingeditViewModel (viewModel);Tentu saja, kode juga perlu sedikit dimodifikasi. Dengan menambahkan halaman templat khusus, selama model entitas yang sesuai di latar belakang dibangun, Anda hanya perlu membuat dua tampilan khusus baru di ujung depan, dan penambahan sederhana, penghapusan, modifikasi dan pencarian dapat diselesaikan tanpa menulis kalimat kode JS.
3. Ikatan Komponen Pilih
Di atas memperkenalkan sintaks penambahan pengemasan T4, penghapusan, modifikasi dan pencarian. Semua komponen halaman pada dasarnya adalah kotak teks. Namun, dalam proyek aktual, banyak halaman kueri dan pengeditan akan memiliki kotak drop-down untuk ditampilkan. Bagaimana seharusnya kita menangani kotak drop-down? Jika Anda tidak merahasiakannya, berikan saja solusi. Misalnya, kita dapat menempatkan sumber data kotak drop-down di latar belakang di halaman pengeditan.
Entitas pengguna
[DataContract] pengguna kelas publik {[datamember] public int id {get; mengatur; } [Datamember] Nama string publik {get; mengatur; } [Datamember] string publik fullName {get; mengatur; } [Datamember] Usia int publik {get; mengatur; } [Datamember] public string des {get; mengatur; } [Datamember] public DateTime createTime {get; mengatur; } [Datamember] string publik strcreateTime {get; mengatur; } [Datamember] Public String DepartmentId {get; mengatur; } [Datamember] departemen objek publik {get; mengatur; }}Kemudian edit halaman
Public ActionResult Edit (Model Pengguna) {Model.Departments = DepartmentModel.getData (); return view (model);}Kemudian ikat ujung depan.
<div> <label for = "txt_des"> departemen </label> <pilih id = "sel_dept" data-bind = "Options: editmodel.departments, optionStext: 'name', optionsValue: 'ID', Nilai: EditModel.DepartmentId"> </Select> </div>
Kode JS tidak perlu dimodifikasi. Saat menambahkan atau mengedit, bidang departemen dapat secara otomatis ditambahkan ke ViewModel.
Tentu saja, kotak drop-down yang digunakan oleh banyak proyek kami tidak hanya memilih, karena gaya pilih yang sederhana benar-benar jelek, sehingga banyak komponen terpilih yang diproduksi, seperti Select2, Multiselect, dll. Dibagikan oleh blogger sebelumnya. Saat menggunakan komponen-komponen ini untuk menginisialisasi SELECT, Anda akan menemukan bahwa kotak drop-down pada antarmuka tidak lagi menjadi tag pilih yang sederhana, tetapi terdiri dari banyak tag lain yang dikustomisasi oleh komponen. Mari kita ambil komponen Select2 sebagai contoh untuk melihat apakah layak untuk diinisialisasi secara langsung sesuai dengan yang di atas.
Kami menambahkan kalimat terakhir untuk mengedit kode JS yang diinisialisasi oleh halaman:
<type skrip = "Text/JavaScript"> $ (function () {var model = @html.raw (newTonsoft.json.jsonConvert.serializeObject (model)); var viewModel = {formid: "FormedIt", editmodel: model, urls: {submit: Model: "id == ", editmodel: Model: edit: {{{{{Model: {edit editmodel: {edit. "/User/update"}, validator: {fields: {name: {validators: {notempty: {pesan: 'Nama tidak dapat kosong!Melalui penambahan dan pengeditan, ini memang layak! Analisis alasannya, meskipun halaman HTML berubah setelah menginisialisasi komponen Select2, komponen pada akhirnya akan menyajikan nilai yang dipilih pada kontrol pilih asli. Saya tidak tahu apakah komponen inisialisasi terpilih lainnya akan seperti ini kecuali Select2, dan mereka menunggu untuk diverifikasi. Namun, ada satu hal yang harus dijelaskan di sini. Sebelum menginisialisasi Select2, opsi dalam kotak drop-down harus terikat pada nilai, yaitu, inisialisasi komponen harus ditempatkan setelah ko.Applybinding ().
4. Ringkasan
Pada titik ini, KO dikombinasikan dengan pembuatan templat yang dapat dibooting dan penggunaan kontrol terpilih pada dasarnya tersedia, dan tentu saja, masih perlu ditingkatkan. Jika Anda punya waktu nanti, blogger akan memilah kombinasi komponen front-end lainnya dan KO, seperti kontrol tanggal kami yang paling umum. Jika Anda memiliki pertanyaan, silakan tinggalkan saya pesan dan editor akan membalas semua orang tepat waktu. Terima kasih banyak atas dukungan Anda ke situs web Wulin.com!