编辑
2024-03-07
我当开发
00

目录

1. 简介
2. 扩展控件
2.1 继承 Panel
2.2 扩展 UIExtension
2.3 增加方法和属性
3. 完整代码
c#部分
js部分
css部分

好久没有做内容了,手痒痒,找个控件说说,放点代码

1. 简介

昨天开发用到了步骤条 Step,长这样:

图片.png 时使用时代码 图片.png

2. 扩展控件

这里的 F.Step 是改造的 Panel 控件,Panel就当是个div,修改了渲染 OnFirstPreRender,抛出了自定义的html和js,就成步骤条了, 那么如何像 F.Panel 那样 写一个前台的控件呢

2.1 继承 Panel

第一步就是继承 FineUICore.Panel ,重写 OnFirstPreRender ,首先看一下 FineUICore 自己的 Panel怎么写的

namespace FineUICore; // // 摘要: // 面板控件 public class Panel : CollapsablePanel { // // 摘要: // 渲染 HTML 之前调用(页面第一次加载或者普通回发) protected override void OnFirstPreRender() { base.OnFirstPreRender(); string scriptContent = $"var {A()}=new F.Panel({d().ToString()});{GlobalConfig.B()}"; AddStartupScript(scriptContent); } }

这是反编译后的结果,代码很简单,就是 输出了一段js new F.Panel ,所以写的类 Step 只要重写 OnFirstPreRender 就行了

/// <summary> /// /// </summary> public class Step : Panel { /// <summary> /// 第一次绘制 /// </summary> protected override void OnFirstPreRender() { base.OnFirstPreRender(); } }

还需要一个类 继承 CollapsablePanelExtension,前台的 F.Panel 就是一个 PanelExtension 实例,继承的CollapsablePanelExtension,写法如下

/// <summary> /// 步骤条控件 /// </summary> public class StepExtension : CollapsablePanelExtension<Step, StepExtension> { /// <summary> /// 步骤条控件 /// </summary> private Step _source = null; /// <summary> /// 步骤条控件 /// </summary> /// <param name="source"></param> public StepExtension(Step source) : base(source) { _source = source; } }

2.2 扩展 UIExtension

现在 StepExtension 还不能用,我们要再包一下,在cshtml能点出来,需要给 FineUICore.UIExtension 扩展,这里也可以查看 F.Panel 不贴出来了,其实它是一个 IHtmlHelper,像这样

/// <summary> /// /// </summary> /// <param name="helperr"></param> /// <returns></returns> public static StepExtension Step(this UIExtension<dynamic, dynamic> helperr) { StepExtension r = new StepExtension(new FineUICore.Step()); return r; }

2.3 增加方法和属性

到这里 cshtml 就可以点出来了,F.Step() 它继承了Panel的所有属性和方法; 现在可以添加自己的属性了,这里来梳理下, ① 比如我要加一个 StepActive 方法,前端想这么写 F.Setp().StepActive(1),那其实就是 StepExtension ,给它加个方法就行了 StepActive(), ② StepExtension 中有个实例 _source ,将StepActive的参数值,赋值到实例 _source 中, ③ 最后在OnFirstPreRender进行前台的响应,

sequenceDiagram participant 前台 participant StepExtension participant Step 前台->>StepExtension: F.Setp().StepActive(1) StepExtension->>StepExtension: StepActive(index) StepExtension->>Step: _source.index = index Step->>前台: OnFirstPreRender(_source.index)
/// <summary> /// 步骤条控件 /// </summary> public class StepExtension : CollapsablePanelExtension<Step, StepExtension> { /// <summary> /// 步骤条控件 /// </summary> private Step _source = null; /// <summary> /// 步骤条控件 /// </summary> /// <param name="source"></param> public StepExtension(Step source) : base(source) { _source = source; } /// <summary> /// 步骤 /// </summary> /// <param name="index"></param> /// <returns></returns> public StepExtension StepActive(int index) { _source.stepActive = index; return this; } }
/// <summary> /// /// </summary> public class Step : Panel { /// <summary> /// 当前激活的序号 /// </summary> private int _stepActive = 0; /// <summary> /// 当前激活的序号 /// </summary> public int stepActive { get { return _stepActive; } set { _stepActive = value; } } /// <summary> /// 第一次绘制 /// </summary> protected override void OnFirstPreRender() { StringBuilder sb = new StringBuilder(); sb.Append($"this.stepActive = {stepActive};"); this.Listeners.Add(new FineUICore.Listener("render", sb.ToString())); base.OnFirstPreRender(); } }

注意这里的OnFirstPreRender 将输出的内容写在了render事件中,因为本身这里的this就是一个panel实例,所以可以这么写,这里还可以设置panel的默认参数

3. 完整代码

剩下就是前端的事了,这里放出完整的代码,

c#部分

#region Step步骤控件 基于Grid 卡片 /// <summary> /// 步骤条控件 /// </summary> public class StepExtension : CollapsablePanelExtension<Step, StepExtension> { /// <summary> /// 步骤条控件 /// </summary> private Step _source = null; /// <summary> /// 步骤条控件 /// </summary> /// <param name="source"></param> public StepExtension(Step source) : base(source) { _source = source; //默认值 _source.ShowBorder = false; _source.ShowHeader = false; //_source.CssClass = "f-step"; _source.stepActive = 0; } /// <summary> /// 数据源 /// </summary> /// <param name="data"></param> /// <returns></returns> public StepExtension DataSource(IEnumerable<StepItem> data) { _source.DataSource = data; return this; } /// <summary> /// 步骤 /// </summary> /// <param name="index"></param> /// <returns></returns> public StepExtension StepActive(int index) { _source.stepActive = index; return this; } /// <summary> /// 是否显示说明 /// </summary> /// <param name="b"></param> /// <returns></returns> public StepExtension ShowBody(bool b) { _source.showBody = b; return this; } } /// <summary> /// /// </summary> public class Step : Panel { /// <summary> /// 当前激活的序号 /// </summary> private int _stepActive = 0; /// <summary> /// 当前激活的序号 /// </summary> public int stepActive { get { return _stepActive; } set { _stepActive = value; } } /// <summary> /// 数据源 /// </summary> public IEnumerable<StepItem> DataSource { get; set; } = new List<StepItem>(); /// <summary> /// 是否显示明细 /// </summary> public bool showBody { get; set; } = true; /// <summary> /// 第一次绘制 /// <para>不能变更的属性在这里</para> /// </summary> protected override void OnFirstPreRender() { this.CssClass += " f-step"; StringBuilder sbel = new StringBuilder(); sbel.Append($"<ul>"); for (int i = 0; i < DataSource.Count(); i++) { var item = DataSource.ToList()[i]; sbel.Append($"<li class='f-grid-card-row' data-stepId='{item.Id}'>"); sbel.Append($"<div class='f-step-card' data-step='{i}'>"); sbel.Append($"<div class='f-step-head'>"); sbel.Append($" <div class='f-step-icon'>"); sbel.Append($" <div class='f-step-num'>"); sbel.Append($"{i + 1}"); sbel.Append($" </div>"); sbel.Append($" <div class='f-step-icon-finish f-iconfont-correct'>"); sbel.Append($" </div>"); sbel.Append($" </div>"); sbel.Append($" <div class='f-step-line'>"); sbel.Append($" </div>"); sbel.Append($"</div>"); sbel.Append($"<div class='f-step-main'>"); sbel.Append($" <div class='f-step-title'>"); sbel.Append($" {item.Title}"); sbel.Append($" </div>"); sbel.Append($" <div class='f-step-des'>"); sbel.Append($" {item.TextBody}"); sbel.Append($" </div>"); sbel.Append($"</div>"); sbel.Append($"</div>"); sbel.Append($"</li>"); } sbel.Append($"</ul>"); this.Content = sbel.ToString(); StringBuilder sb = new StringBuilder(); sb.Append($"this.stepActive = {stepActive};"); sb.Append($"this.showBody = {showBody.ToString().ToLower()};"); sb.Append($"this.data = {JArray.FromObject(DataSource).ToString()};"); this.Listeners.Add(new FineUICore.Listener("render", sb.ToString())); base.OnFirstPreRender(); } } /// <summary> /// 项 ID可忽略 /// </summary> public class StepItem { /// <summary> /// ID /// </summary> public string Id { get; set; } /// <summary> /// 标题 /// </summary> public string Title { get; set; } /// <summary> /// 内容 /// </summary> public string TextBody { get; set; } } #endregion Step步骤控件 基于Grid 卡片

js部分

//Panel控件 if (F.Panel) { //步骤条订制 F.Panel.prototype.__stepActive = -1; Object.defineProperty(F.Panel.prototype, "stepActive", { /** * 从0开始 * @@param {number} val */ set(val) { //设置当前激活的 序号 //无效的样式是 f-state-disabled //完成的样式是 f-step-finish //后面的 f-step-card 无效 //当前的 f-step-line 无效 //前面的 f-step-card 完成 const t = this; let el = t.el.find(`.f-step-card[data-step="${val}"]`); //一共几步 let count = t.el.find(`.f-step-card`).length; if (val < 0 || this.__stepActive == val) return; while (val > count) { val = val - count - 1; } //重置 t.el.find(`.f-step-finish`).removeClass("f-step-finish"); t.el.find(`.f-state-disabled`).removeClass("f-state-disabled"); for (let i = val + 1; i < count; i++) { let nextel = t.el.find(`.f-step-card[data-step="${i}"]`); nextel.addClass("f-state-disabled"); } for (let i = 0; i < val; i++) { t.el.find(`.f-step-card[data-step="${i}"]`).addClass("f-step-finish"); } el.find(".f-step-line").addClass("f-state-disabled"); this.__stepActive = val; this.trigger("stepChange", []); }, get() { return this.__stepActive; } }); //监听 showBody 控制是否显示明细 F.Panel.prototype.__showBody = true; Object.defineProperty(F.Panel.prototype, "showBody", { /** * @@param {boolean} val */ set(val) { const t = this; let el = t.el.find(`.f-step-card .f-step-des`); if (!val) { el.addClass("f-hidden"); } else { el.removeClass("f-hidden"); } this.__showBody = val; }, get() { return this.__showBody; } }); }

css部分

/*步骤条样式*/ .f-step ul { display: flex; counter-reset: index; padding: 20px; } .f-step li.f-grid-card-row { transition: all .3s; border: none; border-bottom: 0px solid; border-left: 0px solid; border-radius: 0px; margin: 0px; padding: 0px; flex: 1; min-width: unset; display: inline-block; position: relative; } .f-step li.f-grid-card-row.f-state-active, .f-step li.f-grid-card-row.f-grid-row-selected, .f-step li.f-grid-card-row.f-state-hover { background: unset; color: unset; } .f-step .f-step-card { transition: all .3s; } .f-step .f-step-head { display: flex; } /*编号*/ .f-step .f-step-head .f-step-num { font-size: 16px; line-height: 18px; text-align: center; height: 20px; width: 20px; border: 1px solid; border-radius: 50%; } .f-step .f-step-head .f-step-icon { height: 20px; width: 20px; margin: 0px 10px; } /*线*/ .f-step .f-step-line { border-top: 2px solid; margin: 0px; flex: 1; margin-top: 8px; transition: all .3s; } /*最后一个横线不显示*/ .f-step li:last-of-type .f-step-line { display: none; } /*身子*/ .f-step .f-step-main { margin-top: 8px; padding: 2px; } .f-step .f-step-main .f-step-title { font-size: 16px; font-weight: 600; line-height: 25px; } /*完成*/ .f-step .f-step-icon-finish { opacity: 0; position: absolute; transition: opacity .3s; display: inline-block; font: normal normal normal 16px/1 FontAwesome; text-rendering: auto; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-indent: 0; min-width: 16px; overflow: hidden; background-repeat: no-repeat; line-height: 20px; font-family: "iconfont"; } .f-step .f-step-num { opacity: 1; top: 0px; position: absolute; transition: all .3s; } .f-step .f-step-finish .f-step-num { opacity: 0; top: -10px; } .f-step .f-step-finish .f-step-icon-finish { opacity: 1; } /*步骤条样式 结束*/
如果对你有用的话,可以打赏哦
打赏
ali pay
wechat pay

本文作者:没想好

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!