mirror of
https://github.com/dotnetcore/BootstrapBlazor.git
synced 2025-12-20 10:26:41 +08:00
!2829 feat: add cherry-markdown
* chore: 更新依赖包 * refactor: 更新组件到 0.7.3 * 升级cherrymarkdown版本到0.7.3 * doc: 更新示例文档 * doc: 更新参数注释 * doc: 更新菜单 * chore: 更新资源文件 * refactor: 更新组件 logo * Merge branch 'main' into dev-CherryMarkdown * chore: 更新打包文件 * feat: 增加客户端文件键值防止大文件并行上传键值覆盖问题 * chore: 格式化脚本更改缓存键值 * fix: 增加 using 释放文件流 * refactor: 重构代码增加兼容 NET5 代码 * chore: 更新项目配置文件 * chore: 更新打包文件 * Merge branch 'main' into dev-CherryMarkdown * update * update * update * update * update * update * update
This commit is contained in:
@@ -120,6 +120,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootstrapBlazor.AzureSpeech
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootstrapBlazor.BaiduSpeech", "src\Extensions\Components\BootstrapBlazor.BaiduSpeech\BootstrapBlazor.BaiduSpeech.csproj", "{4ED606D8-D252-4573-8F0F-B69502ADB7ED}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootstrapBlazor.CherryMarkdown", "src\Extensions\Components\BootstrapBlazor.CherryMarkdown\BootstrapBlazor.CherryMarkdown.csproj", "{018091B3-1E3A-41F3-87B2-5F285C2C3B73}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootstrapBlazor.SummerNote", "src\Extensions\Components\BootstrapBlazor.SummerNote\BootstrapBlazor.SummerNote.csproj", "{2FFC1564-EF75-454B-9D8E-A437A1737CEC}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnitTestEditor", "test\UnitTestEditor\UnitTestEditor.csproj", "{9552B649-17E2-4BCA-8774-664C83A960CB}"
|
||||
@@ -200,6 +202,10 @@ Global
|
||||
{4ED606D8-D252-4573-8F0F-B69502ADB7ED}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{4ED606D8-D252-4573-8F0F-B69502ADB7ED}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{4ED606D8-D252-4573-8F0F-B69502ADB7ED}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{018091B3-1E3A-41F3-87B2-5F285C2C3B73}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{018091B3-1E3A-41F3-87B2-5F285C2C3B73}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{018091B3-1E3A-41F3-87B2-5F285C2C3B73}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{018091B3-1E3A-41F3-87B2-5F285C2C3B73}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{2FFC1564-EF75-454B-9D8E-A437A1737CEC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{2FFC1564-EF75-454B-9D8E-A437A1737CEC}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{2FFC1564-EF75-454B-9D8E-A437A1737CEC}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
@@ -244,6 +250,7 @@ Global
|
||||
{C28717F8-9D2B-4296-9CC4-94882338F370} = {CD062AB6-244D-402A-8F33-C37DAC5856CC}
|
||||
{42108A8A-C773-4F35-A870-3673BDD383E4} = {CD062AB6-244D-402A-8F33-C37DAC5856CC}
|
||||
{4ED606D8-D252-4573-8F0F-B69502ADB7ED} = {CD062AB6-244D-402A-8F33-C37DAC5856CC}
|
||||
{018091B3-1E3A-41F3-87B2-5F285C2C3B73} = {CD062AB6-244D-402A-8F33-C37DAC5856CC}
|
||||
{2FFC1564-EF75-454B-9D8E-A437A1737CEC} = {CD062AB6-244D-402A-8F33-C37DAC5856CC}
|
||||
{9552B649-17E2-4BCA-8774-664C83A960CB} = {7C1D79F1-87BC-42C1-BD5A-CDE4044AC1BD}
|
||||
{6312863E-771D-4EFE-9B9D-071A01222E7A} = {CD062AB6-244D-402A-8F33-C37DAC5856CC}
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BootstrapBlazor.BaiduSpeech" Version="6.*" />
|
||||
<PackageReference Include="BootstrapBlazor.Chart" Version="6.*" />
|
||||
<PackageReference Include="BootstrapBlazor.CherryMarkdown" Version="6.*" />
|
||||
<PackageReference Include="BootstrapBlazor.Markdown" Version="6.*" />
|
||||
<PackageReference Include="BootstrapBlazor.OnScreenKeyboard" Version="6.*" />
|
||||
<PackageReference Include="BootstrapBlazor.SignaturePad" Version="6.*" />
|
||||
|
||||
@@ -2735,6 +2735,7 @@
|
||||
"InputNumber": "InputNumber",
|
||||
"Ip": "IpAddress",
|
||||
"Markdown": "Markdown",
|
||||
"CherryMarkdown": "Cherry-Markdown",
|
||||
"MultiSelect": "MultiSelect",
|
||||
"Radio": "Radio",
|
||||
"Rate": "Rate",
|
||||
|
||||
@@ -2744,6 +2744,7 @@
|
||||
"InputGroup": "输入组 InputGroup",
|
||||
"Ip": "IP 地址 IpAddress",
|
||||
"Markdown": "富文本框 Markdown",
|
||||
"CherryMarkdown": "腾讯文本框 Markdown",
|
||||
"MultiSelect": "多项选择器 MultiSelect",
|
||||
"Radio": "单选框 Radio",
|
||||
"Rate": "评分 Rate",
|
||||
|
||||
39
src/BootstrapBlazor.Shared/Samples/CherryMarkdowns.razor
Normal file
39
src/BootstrapBlazor.Shared/Samples/CherryMarkdowns.razor
Normal file
@@ -0,0 +1,39 @@
|
||||
@page "/cherry-markdowns"
|
||||
|
||||
<h3>CherryMarkdown 腾讯富文本框</h3>
|
||||
|
||||
<h4>基于 CherryMarkdown 的富文本框组件</h4>
|
||||
|
||||
<DemoBlock Title="基础用法" Introduction="使用双向绑定获取对应的 <code>html</code> 和 <code>markdown</code> 内容">
|
||||
<CherryMarkdown @bind-Value="MarkdownString" @bind-Html="HtmlString" style="height: 400px"></CherryMarkdown>
|
||||
<div class="mt-3">
|
||||
<textarea class="form-control" rows="6" disabled="disabled">
|
||||
@MarkdownString
|
||||
</textarea>
|
||||
</div>
|
||||
<div class="mt-3">
|
||||
<textarea class="form-control" rows="6" disabled="disabled">
|
||||
@HtmlString
|
||||
</textarea>
|
||||
</div>
|
||||
</DemoBlock>
|
||||
|
||||
<DemoBlock Title="自行处理文件上传事件" Introduction="使用<code>OnFileUpload</code>事件处理文件上传事件,支持直接粘贴图片到浏览器">
|
||||
<CherryMarkdown OnFileUpload="OnFileUpload"></CherryMarkdown>
|
||||
</DemoBlock>
|
||||
|
||||
<DemoBlock Title="自定义内容" Introduction="通过<code>ToolbarSettings</code>自定义工具栏,通过<code>EditorSettings</code>自定义编辑器样式">
|
||||
<CherryMarkdown ToolbarSettings="@ToolbarSettings" EditorSettings="@EditorSettings"></CherryMarkdown>
|
||||
</DemoBlock>
|
||||
|
||||
<DemoBlock Title="浏览模式" Introduction="纯浏览模式,没有编辑器">
|
||||
<CherryMarkdown @bind-Value="MarkdownString" @bind-Html="HtmlString" style="height: 400px" IsViewer="true"></CherryMarkdown>
|
||||
</DemoBlock>
|
||||
|
||||
<DemoBlock Title="外部控制组件" Introduction="使用CherryMarkdown的Api在外部控制内容">
|
||||
<CherryMarkdown @ref="@MarkdownElement"></CherryMarkdown>
|
||||
<Button OnClick="@(async () => { await MarkdownElement.DoMethodAsync("toolbar.toolbarHandlers.insert", "checklist");})">插入一个CheckList</Button>
|
||||
<Button OnClick="@(async () => { await MarkdownElement.DoMethodAsync("insert", "", false, false, true);})">插入一张图片</Button>
|
||||
</DemoBlock>
|
||||
|
||||
<AttributeTable Items="GetAttributes()"></AttributeTable>
|
||||
96
src/BootstrapBlazor.Shared/Samples/CherryMarkdowns.razor.cs
Normal file
96
src/BootstrapBlazor.Shared/Samples/CherryMarkdowns.razor.cs
Normal file
@@ -0,0 +1,96 @@
|
||||
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
// Website: https://www.blazor.zone or https://argozhang.github.io/
|
||||
|
||||
using BootstrapBlazor.Components;
|
||||
using BootstrapBlazor.Shared.Common;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace BootstrapBlazor.Shared.Samples;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public partial class CherryMarkdowns
|
||||
{
|
||||
private string? MarkdownString { get; set; }
|
||||
|
||||
private string? HtmlString { get; set; }
|
||||
|
||||
[NotNull]
|
||||
private CherryMarkdown? MarkdownElement { get; set; }
|
||||
|
||||
private EditorSettings EditorSettings { get; set; } = new EditorSettings() { DefaultModel = "editOnly" };
|
||||
|
||||
private ToolbarSettings ToolbarSettings { get; set; } =
|
||||
new ToolbarSettings() { Toolbar = new List<object> { "italic", new { insert = new List<string>() { "image" } } }, Bubble = new List<string>() { "bold" }, Float = new List<string>() { "h1" } };
|
||||
|
||||
[Inject]
|
||||
[NotNull]
|
||||
private IOptionsMonitor<WebsiteOptions>? SiteOptions { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// OnInitialized
|
||||
/// </summary>
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
base.OnInitialized();
|
||||
MarkdownString = "# test";
|
||||
}
|
||||
|
||||
private async Task<string> OnFileUpload(CherryMarkdownUploadFile arg)
|
||||
{
|
||||
var url = Path.Combine("images", "uploader",
|
||||
$"{Path.GetFileNameWithoutExtension(arg.FileName)}-{DateTimeOffset.Now:yyyyMMddHHmmss}{Path.GetExtension(arg.FileName)}");
|
||||
var fileName = Path.Combine(SiteOptions.CurrentValue.WebRootPath, url);
|
||||
var ret = await arg.SaveToFile(fileName);
|
||||
return ret ? url : "";
|
||||
}
|
||||
|
||||
private IEnumerable<AttributeItem> GetAttributes() => new AttributeItem[]
|
||||
{
|
||||
new AttributeItem(){
|
||||
Name = "EditorSettings",
|
||||
Description = "编辑器设置",
|
||||
Type = "EditorSettings",
|
||||
ValueList = " — ",
|
||||
DefaultValue = " — "
|
||||
},
|
||||
new AttributeItem(){
|
||||
Name = "ToolbarSettings",
|
||||
Description = "工具栏设置",
|
||||
Type = "ToolbarSettings",
|
||||
ValueList = " — ",
|
||||
DefaultValue = " — "
|
||||
},
|
||||
new AttributeItem(){
|
||||
Name = "Value",
|
||||
Description = "组件值",
|
||||
Type = "string",
|
||||
ValueList = " — ",
|
||||
DefaultValue = " — "
|
||||
},
|
||||
new AttributeItem(){
|
||||
Name = "Html",
|
||||
Description = "组件 Html 代码",
|
||||
Type = "string",
|
||||
ValueList = " — ",
|
||||
DefaultValue = " — "
|
||||
},
|
||||
new AttributeItem(){
|
||||
Name = "OnFileUpload",
|
||||
Description = "文件上传回调方法",
|
||||
Type = "Func<CherryMarkdownUploadFile, Task<string>>",
|
||||
ValueList = " — ",
|
||||
DefaultValue = " — "
|
||||
},
|
||||
new AttributeItem(){
|
||||
Name = "IsViewer",
|
||||
Description = "组件是否为浏览器模式",
|
||||
Type = "bool",
|
||||
ValueList = "true/false",
|
||||
DefaultValue = "false"
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -346,6 +346,12 @@ public sealed partial class NavMenu
|
||||
Url = "markdowns"
|
||||
},
|
||||
new()
|
||||
{
|
||||
IsNew = true,
|
||||
Text = Localizer["CherryMarkdown"],
|
||||
Url = "cherry-markdowns"
|
||||
},
|
||||
new()
|
||||
{
|
||||
Text = Localizer["MultiSelect"],
|
||||
Url = "multiselects"
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Razor">
|
||||
|
||||
<Import Project="..\..\..\bundleconfig.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<Version>6.0.1</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Remove="wwwroot\css\cherry-markdown.min.css" />
|
||||
<Content Remove="wwwroot\js\cherry-markdown.min.js" />
|
||||
<None Include="wwwroot\css\cherry-markdown.min.css" />
|
||||
<None Include="wwwroot\js\cherry-markdown.min.js" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="..\..\..\BootstrapBlazor\Extensions\JSRuntimeExtensions.cs" Link="JSRuntimeExtensions.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,79 @@
|
||||
export function bb_cherry_markdown(el, value, method, obj) {
|
||||
BootstrapBlazorModules.addLink('_content/BootstrapBlazor.CherryMarkdown/css/bootstrap.blazor.cherrymarkdown.min.css');
|
||||
var $el = $(el);
|
||||
if (method === "init") {
|
||||
if (value.toolbars.toolbar === null) {
|
||||
delete value.toolbars.toolbar;
|
||||
}
|
||||
if (value.toolbars.bubble === null) {
|
||||
delete value.toolbars.bubble;
|
||||
}
|
||||
if (value.toolbars.float === null) {
|
||||
delete value.toolbars.float;
|
||||
}
|
||||
var handler = window.setInterval(function () {
|
||||
|
||||
if ($el.is(':visible')) {
|
||||
window.clearInterval(handler);
|
||||
var editor = new Cherry({
|
||||
el: el,
|
||||
value: value.value,
|
||||
fileUpload(file, callback) {
|
||||
var id = $.getUID('md');
|
||||
if (window.cherryMarkdownUploadFiles === undefined) {
|
||||
window.cherryMarkdownUploadFiles = {};
|
||||
}
|
||||
window.cherryMarkdownUploadFiles[id] = file;
|
||||
obj.invokeMethodAsync('Upload', id, {
|
||||
fileName: file.name,
|
||||
fileSize: file.size,
|
||||
contentType: file.type,
|
||||
lastModified: new Date(file.lastModified).toISOString(),
|
||||
}).then(data => {
|
||||
if (data !== "") {
|
||||
callback(data);
|
||||
}
|
||||
})
|
||||
},
|
||||
editor: value.editor,
|
||||
toolbars: value.toolbars,
|
||||
callback: {
|
||||
afterChange: function (markdown, html) {
|
||||
obj.invokeMethodAsync('Update', [markdown, html]);
|
||||
}
|
||||
}
|
||||
});
|
||||
$.data(el, 'bb_cherry_md_editor', editor);
|
||||
}
|
||||
}, 100);
|
||||
} else if (method === 'setMarkdown') {
|
||||
var editor = $.data(el, 'bb_cherry_md_editor');
|
||||
editor.setMarkdown(value, true);
|
||||
}
|
||||
}
|
||||
|
||||
export function bb_cherry_markdown_file(id) {
|
||||
var file = window.cherryMarkdownUploadFiles[id];
|
||||
delete window.cherryMarkdownUploadFiles[id];
|
||||
return file
|
||||
}
|
||||
|
||||
export function bb_cherry_markdown_method(el, method, parameter, obj) {
|
||||
var md = $.data(el, 'bb_cherry_md_editor');
|
||||
if (md) {
|
||||
if (method.indexOf('.') < 0) {
|
||||
md[method](...parameter)
|
||||
} else {
|
||||
var methods = method.split('.');
|
||||
var m = md[methods[0]];
|
||||
for (let i = 1; i < methods.length; i++) {
|
||||
m = m[methods[i]]
|
||||
}
|
||||
m(...parameter);
|
||||
}
|
||||
var val = md.getMarkdown();
|
||||
var html = md.getHtml();
|
||||
obj.invokeMethodAsync('Update', [val, html]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export function bb_cherry_markdown(n,t,i,r){var u,f,e;BootstrapBlazorModules.addLink("_content/BootstrapBlazor.CherryMarkdown/css/bootstrap.blazor.cherrymarkdown.min.css");u=$(n);i==="init"?(t.toolbars.toolbar===null&&delete t.toolbars.toolbar,t.toolbars.bubble===null&&delete t.toolbars.bubble,t.toolbars.float===null&&delete t.toolbars.float,f=window.setInterval(function(){if(u.is(":visible")){window.clearInterval(f);var i=new Cherry({el:n,value:t.value,fileUpload(n,t){var i=$.getUID("md");window.cherryMarkdownUploadFiles===undefined&&(window.cherryMarkdownUploadFiles={});window.cherryMarkdownUploadFiles[i]=n;r.invokeMethodAsync("Upload",i,{fileName:n.name,fileSize:n.size,contentType:n.type,lastModified:new Date(n.lastModified).toISOString()}).then(n=>{n!==""&&t(n)})},editor:t.editor,toolbars:t.toolbars,callback:{afterChange:function(n,t){r.invokeMethodAsync("Update",[n,t])}}});$.data(n,"bb_cherry_md_editor",i)}},100)):i==="setMarkdown"&&(e=$.data(n,"bb_cherry_md_editor"),e.setMarkdown(t,!0))}export function bb_cherry_markdown_file(n){var t=window.cherryMarkdownUploadFiles[n];return delete window.cherryMarkdownUploadFiles[n],t}export function bb_cherry_markdown_method(n,t,i,r){var u=$.data(n,"bb_cherry_md_editor"),f,e,o,s;if(u){if(t.indexOf(".")<0)u[t](...i);else{f=t.split(".");e=u[f[0]];for(let n=1;n<f.length;n++)e=e[f[n]];e(...i)}o=u.getMarkdown();s=u.getHtml();r.invokeMethodAsync("Update",[o,s])}}
|
||||
@@ -0,0 +1,6 @@
|
||||
@namespace BootstrapBlazor.Components
|
||||
@inherits BootstrapComponentBase
|
||||
|
||||
<div @attributes="@AdditionalAttributes" @ref="MarkdownElement">
|
||||
|
||||
</div>
|
||||
@@ -0,0 +1,212 @@
|
||||
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
// Website: https://www.blazor.zone or https://argozhang.github.io/
|
||||
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Components.Forms;
|
||||
using Microsoft.JSInterop;
|
||||
|
||||
namespace BootstrapBlazor.Components;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public partial class CherryMarkdown : IAsyncDisposable
|
||||
{
|
||||
private readonly CherryMarkdownOption _option = new();
|
||||
|
||||
/// <summary>
|
||||
/// 获得/设置 编辑器设置
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public EditorSettings? EditorSettings { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获得/设置 工具栏设置
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public ToolbarSettings? ToolbarSettings { get; set; }
|
||||
|
||||
private string? _value;
|
||||
/// <summary>
|
||||
/// 获得/设置 组件值
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public string? Value
|
||||
{
|
||||
get => _value;
|
||||
set
|
||||
{
|
||||
if (_value != value)
|
||||
{
|
||||
_value = value;
|
||||
IsRender = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获得/设置 组件值回调
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public EventCallback<string?> ValueChanged { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获得/设置 组件 Html 代码
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public string? Html { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获得/设置 组件 Html 代码回调
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public EventCallback<string?> HtmlChanged { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获得/设置 Markdown组件内上传文件时回调此方法
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public Func<CherryMarkdownUploadFile, Task<string>>? OnFileUpload { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取/设置 组件是否为浏览器模式
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public bool? IsViewer { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获得/设置 DOM 元素实例
|
||||
/// </summary>
|
||||
private ElementReference MarkdownElement { get; set; }
|
||||
|
||||
private bool IsRender { get; set; }
|
||||
|
||||
[NotNull]
|
||||
private JSModule<CherryMarkdown>? Module { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// OnInitialized 方法
|
||||
/// </summary>
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
base.OnInitialized();
|
||||
_option.Value = _value;
|
||||
_option.Editor = EditorSettings ?? new EditorSettings();
|
||||
_option.Toolbars = ToolbarSettings ?? new ToolbarSettings();
|
||||
if (IsViewer == true)
|
||||
{
|
||||
_option.Editor.DefaultModel = "previewOnly";
|
||||
_option.Toolbars.Toolbar = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OnAfterRenderAsync 方法
|
||||
/// </summary>
|
||||
/// <param name="firstRender"></param>
|
||||
/// <returns></returns>
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
await base.OnAfterRenderAsync(firstRender);
|
||||
if (firstRender)
|
||||
{
|
||||
IsRender = false;
|
||||
Module = await JSRuntime.LoadModule<CherryMarkdown>("./_content/BootstrapBlazor.CherryMarkdown/js/bootstrap.blazor.cherrymarkdown.min.js", this, false);
|
||||
await Module.InvokeVoidAsync("bb_cherry_markdown", MarkdownElement, _option, "init");
|
||||
}
|
||||
|
||||
if (IsRender)
|
||||
{
|
||||
await Module.InvokeVoidAsync("bb_cherry_markdown", MarkdownElement, Value ?? "", "setMarkdown");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 文件上传回调
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <param name="uploadFile"></param>
|
||||
[JSInvokable]
|
||||
public async Task<string> Upload(string id, CherryMarkdownUploadFile uploadFile)
|
||||
{
|
||||
#if NET6_0_OR_GREATER
|
||||
var stream = await Module.InvokeAsync<IJSStreamReference>("bb_cherry_markdown_file", id);
|
||||
using var data = await stream.OpenReadStreamAsync();
|
||||
uploadFile.UploadStream = data;
|
||||
var ret = "";
|
||||
if (OnFileUpload != null)
|
||||
{
|
||||
ret = await OnFileUpload.Invoke(uploadFile);
|
||||
}
|
||||
return ret;
|
||||
#else
|
||||
await Task.Yield();
|
||||
throw new NotSupportedException();
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新组件值方法
|
||||
/// </summary>
|
||||
/// <param name="vals"></param>
|
||||
/// <returns></returns>
|
||||
[JSInvokable]
|
||||
public async Task Update(string[] vals)
|
||||
{
|
||||
if (vals.Length == 2)
|
||||
{
|
||||
var hasChanged = !EqualityComparer<string>.Default.Equals(vals[0], Value);
|
||||
if (hasChanged)
|
||||
{
|
||||
_value = vals[0];
|
||||
if (ValueChanged.HasDelegate)
|
||||
{
|
||||
await ValueChanged.InvokeAsync(Value);
|
||||
}
|
||||
}
|
||||
|
||||
hasChanged = !EqualityComparer<string>.Default.Equals(vals[1], Html);
|
||||
if (hasChanged)
|
||||
{
|
||||
Html = vals[1];
|
||||
if (HtmlChanged.HasDelegate)
|
||||
{
|
||||
await HtmlChanged.InvokeAsync(Html);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 执行方法
|
||||
/// </summary>
|
||||
/// <param name="method"></param>
|
||||
/// <param name="parameters"></param>
|
||||
/// <returns></returns>
|
||||
public ValueTask DoMethodAsync(string method, params object[] parameters) => Module.InvokeVoidAsync("bb_cherry_markdown_method", MarkdownElement, method, parameters);
|
||||
|
||||
/// <summary>
|
||||
/// Dispose 方法
|
||||
/// </summary>
|
||||
/// <param name="disposing"></param>
|
||||
protected virtual async ValueTask DisposeAsync(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
if (Module != null)
|
||||
{
|
||||
await Module.DisposeAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dispose 方法
|
||||
/// </summary>
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
await DisposeAsync(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
// Website: https://www.blazor.zone or https://argozhang.github.io/
|
||||
|
||||
namespace BootstrapBlazor.Components;
|
||||
|
||||
/// <summary>
|
||||
/// 配置信息
|
||||
/// </summary>
|
||||
internal class CherryMarkdownOption
|
||||
{
|
||||
/// <summary>
|
||||
/// 初始值
|
||||
/// </summary>
|
||||
public string? Value { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 编辑器选项
|
||||
/// </summary>
|
||||
public EditorSettings? Editor { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 工具栏选项
|
||||
/// </summary>
|
||||
public ToolbarSettings? Toolbars { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
// Website: https://www.blazor.zone or https://argozhang.github.io/
|
||||
|
||||
namespace BootstrapBlazor.Components;
|
||||
|
||||
/// <summary>
|
||||
/// 文件信息
|
||||
/// </summary>
|
||||
public class CherryMarkdownUploadFile
|
||||
{
|
||||
/// <summary>
|
||||
/// 文件名
|
||||
/// </summary>
|
||||
public string? FileName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 文件大小
|
||||
/// </summary>
|
||||
public long FileSize { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 最后修改日期
|
||||
/// </summary>
|
||||
public string? LastModified { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 文件类型
|
||||
/// </summary>
|
||||
public string? ContentType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 上传的文件流
|
||||
/// </summary>
|
||||
public Stream? UploadStream { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 返回码,0为成功,非0失败
|
||||
/// </summary>
|
||||
public int Code { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 错误信息
|
||||
/// </summary>
|
||||
public string? Error { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 保存到文件
|
||||
/// </summary>
|
||||
/// <param name="fileName"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<bool> SaveToFile(string fileName, CancellationToken token = default)
|
||||
{
|
||||
var ret = false;
|
||||
if (UploadStream != null)
|
||||
{
|
||||
// 文件保护,如果文件存在则先删除
|
||||
if (System.IO.File.Exists(fileName))
|
||||
{
|
||||
try
|
||||
{
|
||||
System.IO.File.Delete(fileName);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Code = 1002;
|
||||
Error = ex.Message;
|
||||
}
|
||||
}
|
||||
|
||||
var folder = Path.GetDirectoryName(fileName);
|
||||
if (!string.IsNullOrEmpty(folder) && !Directory.Exists(folder))
|
||||
{
|
||||
Directory.CreateDirectory(folder);
|
||||
}
|
||||
|
||||
if (Code == 0)
|
||||
{
|
||||
using var uploadFile = File.OpenWrite(fileName);
|
||||
|
||||
try
|
||||
{
|
||||
// 打开文件流
|
||||
var stream = UploadStream;
|
||||
|
||||
var buffer = new byte[4 * 1096];
|
||||
int bytesRead = 0;
|
||||
|
||||
// 开始读取文件
|
||||
while ((bytesRead = await stream.ReadAsync(buffer, token)) > 0)
|
||||
{
|
||||
await uploadFile.WriteAsync(buffer.AsMemory(0, bytesRead), token);
|
||||
|
||||
}
|
||||
ret = true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Code = 1003;
|
||||
Error = ex.Message;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
// Website: https://www.blazor.zone or https://argozhang.github.io/
|
||||
|
||||
namespace BootstrapBlazor.Components;
|
||||
|
||||
/// <summary>
|
||||
/// 编辑器设置
|
||||
/// </summary>
|
||||
public class EditorSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// CodeMirror主题,默认为 default
|
||||
/// </summary>
|
||||
public string Theme { get; set; } = "default";
|
||||
|
||||
/// <summary>
|
||||
/// 编辑器高度,默认为100%
|
||||
/// </summary>
|
||||
public string Height { get; set; } = "100%";
|
||||
|
||||
/// <summary>
|
||||
/// 编辑器显示模式
|
||||
/// edit&preview: 双栏编辑预览模式,默认值
|
||||
/// editOnly: 只显示编辑器
|
||||
/// previewOnly: 预览模式
|
||||
/// </summary>
|
||||
public string DefaultModel { get; set; } = "edit&preview";
|
||||
|
||||
/// <summary>
|
||||
/// 粘贴Html时自动转换为Markdown格式
|
||||
/// </summary>
|
||||
public bool ConvertWhenPaste { get; set; } = true;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
// Website: https://www.blazor.zone or https://argozhang.github.io/
|
||||
|
||||
namespace BootstrapBlazor.Components;
|
||||
|
||||
/// <summary>
|
||||
/// 状态栏设置
|
||||
/// </summary>
|
||||
public class ToolbarSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// 主题,light 、dark(默认值)
|
||||
/// </summary>
|
||||
public string Theme { get; set; } = "dark";
|
||||
|
||||
/// <summary>
|
||||
/// 自定义工具栏按钮,null则为默认工具栏,false则不显示工具栏
|
||||
/// </summary>
|
||||
public object? Toolbar { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 选中后的悬浮菜单,null为默认悬浮菜单,false则不显示悬浮菜单
|
||||
/// </summary>
|
||||
public object? Bubble { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 新行的悬浮菜单,null为默认悬浮菜单,false则不显示悬浮菜单
|
||||
/// </summary>
|
||||
public object? Float { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
[
|
||||
{
|
||||
"outputFileName": "wwwroot/css/bootstrap.blazor.cherrymarkdown.min.css",
|
||||
"inputFiles": [
|
||||
"wwwroot/css/cherry-markdown.min.css",
|
||||
"Components/**/*.css"
|
||||
]
|
||||
},
|
||||
{
|
||||
"outputFileName": "Components/CherryMarkdown/CherryMarkdown.min.js",
|
||||
"inputFiles": [
|
||||
"Components/CherryMarkdown/CherryMarkdown.js"
|
||||
],
|
||||
"minify": {
|
||||
"enabled": true,
|
||||
"renameLocals": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"outputFileName": "wwwroot/js/bootstrap.blazor.cherrymarkdown.min.js",
|
||||
"inputFiles": [
|
||||
"wwwroot/js/cherry-markdown.min.js",
|
||||
"Components/**/*.min.js"
|
||||
],
|
||||
"minify": {
|
||||
"enabled": false,
|
||||
"renameLocals": true
|
||||
},
|
||||
"sourceMap": false
|
||||
}
|
||||
]
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 6.3 KiB |
File diff suppressed because one or more lines are too long
1
src/Extensions/Components/BootstrapBlazor.CherryMarkdown/wwwroot/css/cherry-markdown.min.css
vendored
Normal file
1
src/Extensions/Components/BootstrapBlazor.CherryMarkdown/wwwroot/css/cherry-markdown.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 197 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
1
src/Extensions/Components/BootstrapBlazor.CherryMarkdown/wwwroot/js/cherry-markdown.min.js
vendored
Normal file
1
src/Extensions/Components/BootstrapBlazor.CherryMarkdown/wwwroot/js/cherry-markdown.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user