English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
Prefazione: Nei due precedenti articoli ho encapsulato alcuni componenti di base dei moduli. Questo articolo continua a encapsulare altri componenti basati su Bootstrap. A differenza del precedente, alcuni componenti di questo articolo richiedono il supporto di alcuni file js.
Table of Contents of BootstrapHelper Series Articles
C# Advanced Series - Step by Step Encapsulation of Your Own HtmlHelper Component: BootstrapHelper
C# Advanced Series - Step by Step Encapsulation of Your Own HtmlHelper Component: BootstrapHelper (Part 2)
C# Advanced Series - Step by Step Encapsulation of Your Own HtmlHelper Component: BootstrapHelper (Part 3: Source Code)
1. NumberBoxExtensions
NumberBoxExtensions è una casella di testo numerica basata sullo stile Bootstrap, creata attraverso l'encapsulazione del componente numerico incrementale spinner descritto dal blogger in precedenza. Chi non conosce il componente spinner può leggere il secondo componente descritto all'indirizzo https://it.oldtoolbag.com/article/88490.htm.
Come abbiamo scoperto attraverso le precedenti presentazioni, l'inizializzazione del componente incrementale spinner non richiede la scrittura di alcun codice js, può essere realizzata direttamente attraverso la configurazione dell'attributo data dell'html. Questo ha reso molto più facile la nostra encapsulazione, poiché possiamo passare come parametri dell'estensione i parametri di inizializzazione comuni e poi trasformarli in attributi data da restituire all'utente finale.
Non facciamo troppe chiacchiere, mettiamo subito in evidenza il codice封装.
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; namespace BootstrapExtensions } public static class NumberBoxExtensions } // <summary> /// Genera la casella di testo numerica // </summary> // <param name="html">扩展方法实例</param> /// <param name="id">id</param> /// <returns>Restituisce il testo numerico della casella di testo</returns> public static MvcHtmlString NumberTextBox(this BootstrapHelper html, string id) } return NumberTextBox(html, id, null, null, null, null, null); } // <summary> /// Genera la casella di testo numerica // </summary> // <param name="html">扩展方法实例</param> /// <param name="id">id</param> /// <param name="value">Valore della casella di testo</param> /// <returns>Restituisce il testo numerico della casella di testo</returns> public static MvcHtmlString NumberTextBox(this BootstrapHelper html, string id, object value) } return NumberTextBox(html, id, value, null, null, null, null); } // <summary> /// Genera la casella di testo numerica // </summary> // <param name="html">扩展方法实例</param> /// <param name="value">Valore della casella di testo</param> /// <param name="min">Valore minimo di aumento automatico</param> /// <param name="max">Valore massimo di aumento automatico</param> /// <returns>Restituisce il testo numerico della casella di testo</returns> public static MvcHtmlString NumberTextBox(this BootstrapHelper html, object value, int? min, int? max) } return NumberTextBox(html, null, value, min, max, null, null); } // <summary> /// Genera la casella di testo numerica // </summary> // <param name="html">扩展方法实例</param> /// <param name="id">id</param> /// <param name="value">Valore della casella di testo</param> /// <param name="min">Valore minimo di aumento automatico</param> /// <param name="max">Valore massimo di aumento automatico</param> /// <returns>Restituisce il testo numerico della casella di testo</returns> public static MvcHtmlString NumberTextBox(this BootstrapHelper html, string id, object value, int? min, int? max) } return NumberTextBox(html, id, value, min, max, null, null); } // <summary> /// Genera la casella di testo numerica // </summary> // <param name="html">扩展方法实例</param> /// <param name="id">id</param> /// <param name="value">Valore della casella di testo</param> /// <param name="min">Valore minimo di aumento automatico</param> /// <param name="max">Valore massimo di aumento automatico</param> /// <param name="step">Numero aumentato ogni volta</param> /// <returns>Restituisce il testo numerico della casella di testo</returns> public static MvcHtmlString NumberTextBox(this BootstrapHelper html, string id, object value, int? min, int? max, int? step) } return NumberTextBox(html, id, value, min, max, step, null); } // <summary> /// Genera la casella di testo numerica // </summary> // <param name="html">扩展方法实例</param> /// <param name="id">id</param> /// <param name="value">Valore della casella di testo</param> /// <param name="min">Valore minimo di aumento automatico</param> /// <param name="max">Valore massimo di aumento automatico</param> /// <param name="step">Numero aumentato ogni volta</param> /// <param name="rule">Regola di aumento automatico</param> /// <returns>Restituisce il testo numerico della casella di testo</returns> public static MvcHtmlString NumberTextBox(this BootstrapHelper html, string id, object value, int? min, int? max, int? step, SpinningRule? rule) } TagBuilder tag = new TagBuilder("div"); tag.MergeAttribute("class", "input-group spinner"); tag.MergeAttribute("data-trigger", "spinner"); System.Text.StringBuilder sb = new System.Text.StringBuilder(); //sb.Append("<input type='text' class='form-control text-center' value='1' data-min='-10' data-max='10' data-step='2' data-rule='quantity'>"); sb.Append("<input type='text' class='form-control text-center' "); if (!string.IsNullOrEmpty(id)) } sb.Append("id='").Append(id).Append("' "); } if (value != null) } sb.Append("value='").Append(value.ToString()).Append("' "); } else } sb.Append("value='1' "); } if (min != null) } sb.Append("data-min='").Append(min).Append("' "); } if (max != null) } sb.Append("data-max='").Append(max).Append("' "); } if (step != null) } sb.Append("data-step='").Append(step).Append("' "); } if (rule != null) } sb.Append("data-rule='").Append(rule.ToString()).Append("' "); } else } sb.Append("data-rule='quantity' "); } sb.Append("/>"); sb.Append("<span class='input-group-addon'>"); sb.Append("<a href='javascript;;' class='spin-up' data-spin='up'><i class='fa fa-caret-up'></i></a>"); sb.Append("<a href='javascript:;' class='spin-down' data-spin='down'><i class='fa fa-caret-down'></i></a>"); sb.Append("</span>"); tag.InnerHtml = sb.ToString(); return MvcHtmlString.Create(tag.ToString()); } } public enum SpinningRule } defaults, currency, quantity, percent, month, day, hour, minute, second, } }
Oltre a id e value, i parametri necessari per l'inizializzazione del componente auto-incrementale sono principalmente min, max, step, rule, che corrispondono rispettivamente ai parametri data-min, data-max, data-step, data-rule del componente.
È molto semplice da usare, prima di tutto, includi i file js e css correlati nella pagina corrispondente, quindi chiama così nel cshtml:
@Bootstrap.NumberTextBox(null, "1", 1, 10, 2, null)
Risultato ottenuto:
Questo è molto più conveniente rispetto a copiare ogni volta un pezzo di codice HTML lungo, non hai un po' di emozione?~~
2、DateTimeBoxExtensions
Con il componente numerico sopra come base, ora dobbiamo incapsulare il componente temporale. Il blogger ha intenzione di utilizzare l'attributo data per l'inizializzazione, ma dopo aver cercato a lungo nel documento, non c'è un modo di inizializzazione con l'attributo data nel datatimepicker, non c'è altra scelta, quindi il blogger deve fare l'inizializzazione dell'attributo data da solo.
1、Piano iniziale
Creiamo un nuovo file: bootstrap-datetimepicker-helper.js. Il codice all'interno è il seguente
$(function () { var datetimedefault = { locale: 'zh-CN', //中文化 }); $.each($(".date"), function (index, item) { var data = $(item).data(); var param = $.extend({}, datetimedefault, data || {}); $(item).datetimepicker(param); }); });
Poi segue così il codice HTML
<div class='input-group date' data-format="YYYY-MM-DD" data-maxDate="2017-01-10" data-minDate="2010-01-10"> <input type='text' class="form-control" /> <span class="input-group-addon"> <span class="glyphicon glyphicon-calendar"></span> </span> </div>
Sembra che non ci siano problemi, e all'inizio anche l'autore credeva che non ci fossero problemi. Tuttavia, le cose non sono andate come previsto! C'è qualcosa che l'autore non ha considerato, ovvero che il metodo data() di jQuery trasforma in minuscolo tutti i nomi delle proprietà. Quindi, se scriviamo data-maxDate nell'HTML, il risultato ottenuto tramite il metodo data() diventa maxdate, come mostrato nell'immagine di seguito:
Poi, durante l'inizializzazione di datetimepicker, si è verificato un eccezione JavaScript. Questo metodo non funziona.
2、改进方案
Poiché il metodo sopra descritto non funziona, dobbiamo migliorare. Esiste un qualche parametro per il metodo data() che può evitarne la trasformazione in minuscolo? Dopo aver cercato un po', non ho trovato nessuna risposta. Alla fine, non avendo alternative, l'autore ha deciso di fare un miglioramento in proprio, e quindi il codice JavaScript è diventato così:
$(function () { var datetimedefault = { format: 'YYYY-MM-DD',//日期格式化,只显示日期 locale: 'zh-CN', //中文化 maxDate: '2017-01-01',//最大日期 minDate: '2010-01-01', //最小日期 viewMode: 'days', defaultDate: false, disabledDates: false, enabledDates: false, }); $.each($(".date"), function (index, item) { var data = $(item).data(); $.each(data, function (key, value) { for (i in datetimedefault) { if (key == i.toLowerCase()) { datetimedefault[i] = value; break; } } }); //var param = $.extend({}, datetimedefault, data || {}); $(item).datetimepicker(datetimedefault); }); });
Il principio è confrontare il risultato del metodo data() con il nome dell'attributo datetimedefault convertito in minuscolo. Se sono uguali, viene sovrascritto l'attributo predefinito all'interno di html. Dopo aver debuggato diverse volte, non ho trovato problemi significativi.
Il codice è scritto in questo modo, di fatto può risolvere i problemi che abbiamo menzionato sopra, ma è necessario che il nostro variabile datetimedefault contenga un numero sufficiente di parametri predefiniti, in modo da coprire l'obiettivo. Naturalmente, nel progetto di solito si modificano solo pochi parametri, qui è sufficiente aggiungere alcune proprietà predefinite che cambiano spesso.
Bene, con la teoria di cui sopra, il nostro DataTimeBox sarà facile da encapsulare. Passiamo direttamente al codice.
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; namespace BootstrapExtensions } public static class DateTimeBoxExtensions } // <summary> /// 生成日期控件 // </summary> // <param name="html">扩展方法实例</param> /// <param name="id">文本框标签的id</param> /// <returns>Restituisce l'etichetta HTML del controllo di data presentato</returns> public static MvcHtmlString DateTimeBox(this BootstrapHelper html, string id) } return DateTimeBox(html, id, null, null, null, null, null, null); } // <summary> /// 生成日期控件 // </summary> // <param name="html">扩展方法实例</param> /// <param name="id">文本框标签的id</param> /// <param name="value">文本框标签的默认值</param> /// <returns>Restituisce l'etichetta HTML del controllo di data presentato</returns> public static MvcHtmlString DateTimeBox(this BootstrapHelper html, string id, object value) } return DateTimeBox(html, id, value, null, null, null, null, null); } // <summary> /// 生成日期控件 // </summary> // <param name="html">扩展方法实例</param> /// <param name="id">文本框标签的id</param> /// <param name="value">文本框标签的默认值</param> /// <param name="format">显示日期的格式</param> /// <param name="maxDate">Valore minimo della data</param> /// <param name="minDate">Valore massimo della data</param> /// <returns>Restituisce l'etichetta HTML del controllo di data presentato</returns> public static MvcHtmlString DateTimeBox(this BootstrapHelper html, string id, object value, string format, string maxDate, string minDate) } return DateTimeBox(html, id, value, format, maxDate, minDate, null, null); } // <summary> /// 生成日期控件 // </summary> // <param name="html">扩展方法实例</param> /// <param name="id">文本框标签的id</param> /// <param name="value">文本框标签的默认值</param> /// <param name="format">显示日期的格式</param> /// <param name="maxDate">Valore minimo della data</param> /// <param name="minDate">Valore massimo della data</param> /// <param name="viewMode">Modalità di navigazione del controllo di data</param> /// <param name="showClear">Mostra pulsante di pulizia</param> /// <returns>Restituisce l'etichetta HTML del controllo di data presentato</returns> public static MvcHtmlString DateTimeBox(this BootstrapHelper html, string id, object value, string format, string maxDate, string minDate, string viewMode, bool? showClear) } TagBuilder tag = new TagBuilder("div"); tag.MergeAttribute("class", "input-group date"); if (!string.IsNullOrEmpty(format)) } tag.MergeAttribute("data-format", format); } if (!string.IsNullOrEmpty(maxDate)) } tag.MergeAttribute("data-maxDate", maxDate); } if (!string.IsNullOrEmpty(minDate)) } tag.MergeAttribute("data-minDate", minDate); } if (!string.IsNullOrEmpty(viewMode)) } tag.MergeAttribute("data-viewMode", viewMode); } if (showClear!=null) } tag.MergeAttribute("data-showClear", showClear.ToString()); } System.Text.StringBuilder sb = new System.Text.StringBuilder(); sb.Append("<input type='text' class='form-control'") if(!string.IsNullOrEmpty(id)) } sb.Append("id='").Append(id).Append("' "); } if (value != null) } sb.Append("value='").Append(value.ToString()).Append("' "); } sb.Append("/>").Append("<span class='input-group-addon'>") .Append("<span class='glyphicon glyphicon-calendar'></span>") .Append("</span>"); tag.InnerHtml = sb.ToString(); return MvcHtmlString.Create(tag.ToString()); } } }
Poi la nostra pagina cshtml deve solo richiamare i nostri js e css
<link href="~/Content/bootstrap-datetimepicker/css/bootstrap-datetimepicker.css" rel="stylesheet" /> <script src="~/Content/bootstrap-datetimepicker/js/moment-with-locales.js"></script> <script src="~/Content/bootstrap-datetimepicker/js/bootstrap-datetimepicker.min.js"></script> <script src="~/Content/bootstrap-datetimepicker/js/bootstrap-datetimepicker-helper.js"></script>
Poi utilizza direttamente
<div class="col-xs-3"> @Bootstrap.DateTimeBox("starttime", null, null, null, null, null, null) </div> <div class="col-xs-3"> @Bootstrap.DateTimeBox("endtime", null, null, null, null, null, null) </div>
Ottenere il risultato
Tre, TextareExtensions
La confezione del campo textarea è relativamente semplice, poiché la sua struttura è molto simile a quella di TextBox, forniamo direttamente il codice sorgente della confezione.
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; namespace BootstrapExtensions } public static class TextareExtensions } // <summary> /// textarea文本域 // </summary> // <param name="html">扩展方法实例</param> /// <param name="id">id</param> /// <returns>html标签</returns> public static MvcHtmlString TextAreaBox(this BootstrapHelper html, string id) } return TextAreaBox(html, id, null, null, null, null); } // <summary> /// textarea文本域 // </summary> // <param name="html">扩展方法实例</param> /// <param name="id">id</param> /// <param name="value">value</param> /// <param name="cssClass">样式</param> /// <returns>html标签</returns> public static MvcHtmlString TextAreaBox(this BootstrapHelper html, string id, object value, string cssClass) } return TextAreaBox(html, id, value, cssClass, null, null); } // <summary> /// textarea文本域 // </summary> // <param name="html">扩展方法实例</param> /// <param name="id">id</param> /// <param name="value">value</param> /// <param name="cssClass">样式</param> /// <param name="rows">行数</param> /// <returns>html标签</returns> public static MvcHtmlString TextAreaBox(this BootstrapHelper html, string id, object value, string cssClass, int? rows) } return TextAreaBox(html, id, value, cssClass, rows, null); } // <summary> /// textarea文本域 // </summary> // <param name="html">扩展方法实例</param> /// <param name="id">id</param> /// <param name="value">value</param> /// <param name="cssClass">样式</param> /// <param name="rows">行数</param> /// <param name="cols">列数</param> /// <returns>html标签</returns> public static MvcHtmlString TextAreaBox(this BootstrapHelper html, string id, object value, string cssClass, int? rows, int? cols) } TagBuilder tag = new TagBuilder("textarea"); tag.AddCssClass("form-control"); if (!string.IsNullOrEmpty(id)) } tag.MergeAttribute("id", id); } if (value != null) } tag.MergeAttribute("value", value.ToString()); } if (!string.IsNullOrEmpty(cssClass)) } tag.AddCssClass(cssClass); } if (rows != null) } tag.MergeAttribute("rows", rows.ToString()); } if (cols != null) } tag.MergeAttribute("cols", cols.ToString()); } return MvcHtmlString.Create(tag.ToString()); } } }
Supporta solo alcuni parametri semplici come rows e cols. Se il tuo progetto ha esigenze speciali, come l'inizializzazione di un editor di testo arricchito, puoi anche migliorarlo tu stesso.
Metodo di utilizzo
<div> @Bootstrap.TextAreaBox("id", "", "", 3, 5) </div>
Ecco un problema da menzionare, ovvero scrivendo direttamente così, il risultato è:
Sembra che l'attributo cols non funzioni, in realtà, se il tuo tag ha la classe di stile 'form-control', si espanderà per riempire l'intero div. Il metodo di soluzione è anche molto semplice, ad esempio possiamo fare qualcosa di più nel div:
<div> @Bootstrap.TextAreaBox("", "", "", 3, 5) </div>
Poiché l'attributo cols non ha funzionato, in realtà puoi considerare di rimuovere il parametro cols.
Quarto, SelectExtensions
È arrivato il momento di affrontare la selettore a discesa纠结. Perché si dice che è纠结? Perché, quando lo encapsuli, devi considerare molti problemi, come:
Come gestire gli <optgroup> e <option> per passare valori statici agli option al server? Encapsulare l'originale select o crearne uno basato su某些组件(ad esempio select2)? Come gestire i metodi ed eventi?
Poi ripensai, a cosa serve l'encapsulation? Non è per rendere più facile l'uso? Se è troppo rigorosa, l'uso può essere conveniente? Fortunatamente, ho deciso di encapsulare direttamente in un select il più semplice possibile. Il blogger intende fare così:
Se l'opzione è statica, scrivi direttamente il tag select nativo; se è dinamica, passa l'URL corrispondente al server, ottieni i dati e genera l'opzione. Ecco il codice encapsulato:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; namespace BootstrapExtensions } public static class SelectExtensions } // <summary> // 返回select标签 // </summary> // <param name="html">扩展方法实例</param> // <param name="id">标签id</param> /// <returns>select etichetta</returns> public static MvcHtmlString SelectBox(this BootstrapHelper html, string id) } return SelectBox(html, id, null, null, null, null, null, null); } // <summary> // 返回select标签 // </summary> // <param name="html">扩展方法实例</param> // <param name="id">标签id</param> // <param name="value">标签选中值</param> /// <returns>select etichetta</returns> public static MvcHtmlString SelectBox(this BootstrapHelper html, string id, object value) } return SelectBox(html, id, value, null, null, null, null, null); } // <summary> // 返回select标签 // </summary> // <param name="html">扩展方法实例</param> // <param name="id">标签id</param> // <param name="value">标签选中值</param> // <param name="cssClass">标签样式</param> /// <returns>select etichetta</returns> public static MvcHtmlString SelectBox(this BootstrapHelper html, string id, object value, string cssClass) } return SelectBox(html, id, value, cssClass, null, null, null, null); } // <summary> // 返回select标签 // </summary> // <param name="html">扩展方法实例</param> // <param name="id">标签id</param> // <param name="value">标签选中值</param> // <param name="cssClass">标签样式</param> // <param name="url">请求数据的url</param> /// <param name="textField">campo visualizzazione</param> /// <param name="valueField">campo valore</param> /// <returns>select etichetta</returns> public static MvcHtmlString SelectBox(this BootstrapHelper html, string id, object value, string cssClass, string url, string textField, string valueField) } return SelectBox(html, id, value, cssClass, url, null, textField, valueField); } // <summary> // 返回select标签 // </summary> // <param name="html">扩展方法实例</param> // <param name="id">标签id</param> // <param name="value">标签选中值</param> // <param name="cssClass">标签样式</param> // <param name="url">请求数据的url</param> // <param name="param">请求的参数</param> /// <param name="textField">campo visualizzazione</param> /// <param name="valueField">campo valore</param> /// <param name="multiple">se multipla scelta</param> /// <returns>select etichetta</returns> public static MvcHtmlString SelectBox(this BootstrapHelper html, string id, object value, string cssClass, string url, string param, string textField, string valueField, bool multiple = false) } TagBuilder tag = new TagBuilder("select"); tag.AddCssClass("form-control"); if (!string.IsNullOrEmpty(id)) } tag.MergeAttribute("id", id); } if (value != null) } tag.MergeAttribute("value", value.ToString()); } if (!string.IsNullOrEmpty(cssClass)) } tag.AddCssClass(cssClass); } if (!string.IsNullOrEmpty(url)) } tag.MergeAttribute("data-url", url); } if (!string.IsNullOrEmpty(param)) } tag.MergeAttribute("data-param", param); } if (!string.IsNullOrEmpty(valueField)) } tag.MergeAttribute("data-value-field", valueField); } if (!string.IsNullOrEmpty(textField)) } tag.MergeAttribute("data-text-field", textField); } if (multiple) } tag.MergeAttribute("multiple", "multiple"); } return MvcHtmlString.Create(tag.ToString()); } } }
poiante前端使用js去初始化,需要有一个js文件utility.combobox.js:
(function ($) { //1.定义jquery的扩展方法combobox $.fn.combobox = function (options, param) { if (typeof options == 'string') { return $.fn.combobox.methods[options](this, param); } //2.将调用时候传过来的参数和default参数合并 options = $.extend({}, $.fn.combobox.defaults, options || {}); //3.添加默认值 var target = $(this); target.attr('valuefield', options.valueField); target.attr('textfield', options.textField); target.empty(); var option = $('<option></option>'); option.attr('value', ''); option.text(options.placeholder); target.append(option); //4.判断用户传过来的参数列表里面是否包含数据data数据集,如果包含,不用发ajax从后台取,否则否送ajax从后台取数据 if (options.data) { init(target, options.data); } else { //var param = {}; options.onBeforeLoad.call(target, options.param); if (!options.url) return; if (typeof options.param == "string") { options.param = JSON.parse(options.param); } $.getJSON(options.url, options.param, function (data) { init(target, data); }); } function init(target, data) { $.each(data, function (i, item) { var option = $('<option></option>'); option.attr('value', item[options.valueField]); option.text(item[options.textField]); target.append(option); }); options.onLoadSuccess.call(target); } target.unbind("change"); target.on("change", function (e) { if (options.onChange) return options.onChange(target.val()); }); } //5. Se il valore passato è una stringa, rappresenta una chiamata al metodo. $.fn.combobox.methods = { getValue: function (jq) { return jq.val(); }, setValue: function (jq, param) { jq.val(param); }, load: function (jq, url) { $.getJSON(url, function (data) { jq.empty(); var option = $('<option></option>'); option.attr('value', ''); option.text('Scegli predefinito'); jq.append(option); $.each(data, function (i, item) { var option = $('<option></option>'); option.attr('value', item[jq.attr('valuefield')]); option.text(item[jq.attr('textField')]); jq.append(option); }); }); } }); //6.Elenco dei parametri predefiniti $.fn.combobox.defaults = { url: null, param: null, data: null, valueField: 'valore', textField: 'testo', placeholder: 'Scegli predefinito', onBeforeLoad: function (param) { }, onLoadSuccess: function () { }, onChange: function (value) { } }); // Questo pezzo è stato aggiunto, viene chiamato il metodo di inizializzazione dopo che la pagina è stata inizializzata $.document.ready(function () { $('select').each(function () { var $combobox = $(this); $.fn.combobox.call($combobox, $combobox.data()); }); }); )(jQuery);
Questo file JavaScript è derivato da un precedente articolo dell'autore https://it.oldtoolbag.com/article/92595.htm
Poi il frontend chiama
<div class="col-xs-2"> @Bootstrap.SelectBox("sel", null, null, "/Home/GetDept", null, "Name", "Id") </div>
V. Conclusione
Fino a questo punto, la prima versione di BootstrapHelper è praticamente completata, i componenti principali encapsulati sono i seguenti:
Certo, ci sono anche le condivisioni di codice sorgente che preferite.Indirizzo del codice sorgente
Se pensi che questo articolo ti possa aiutare, per favore aiutami a promuoverlo.
Questo è tutto il contenuto dell'articolo, spero che sia utile per la tua apprendimento, e ti prego di supportare e urlare il tutorial.
Dichiarazione: il contenuto di questo articolo è stato tratto da Internet, il copyright spetta ai rispettivi proprietari, il contenuto è stato contribuito e caricato autonomamente dagli utenti di Internet, questo sito non detiene i diritti di proprietà, non è stato editato manualmente e non assume responsabilità legali correlate. Se trovi contenuti sospetti di violazione del copyright, ti preghiamo di inviare una e-mail a notice#oldtoolbag.com (sostituisci # con @) per segnalare il problema e fornire prove pertinenti. Una volta verificata, questo sito eliminerà immediatamente il contenuto sospetto di violazione del copyright.