【发布时间】:2021-03-06 16:04:50
【问题描述】:
对于 Piranha 和 Vue 来说非常陌生,但对于 .Net Core 来说却不是。试图让我了解如何创建自定义块。我创建了一个新块,试图将 HtmlBlock 和 ImageBlock 结合起来:
using Piranha.Extend;
using Piranha.Extend.Blocks;
using Piranha.Extend.Fields;
namespace YR.Models.Piranha.Blocks
{
[BlockType(Name = "Card", Category = "Content", Icon = "fas fa-address-card", Component = "card-block")]
public class CardBlock : Block
{
public ImageField ImgBody { get; set; }
public SelectField<ImageAspect> Aspect { get; set; } = new SelectField<ImageAspect>();
public HtmlField HtmlBody { get; set; }
public override string GetTitle()
{
if (ImgBody != null && ImgBody.Media != null)
{
return ImgBody.Media.Filename;
}
return "No image selected";
}
}
}
如果我在 BlockTypeAttribute 中省略了 Component 属性,则该块可以工作,将图像和内容保存到草稿中,并在发布时完美地更新站点。为了获得管理器的完整体验,我还尝试构建一个 Vue 组件,它只是结合了 html-block.vue 和 image-block.vue 组件。
这是 Vue 组件的内容:
Vue.component("card-block", {
props: ["uid", "toolbar", "model"],
data: function () {
return {
imgBody: this.model.imgBody.value,
htmlBody: this.model.htmlBody.value
};
},
methods: {
clear: function () {
// clear media from block
},
onBlur: function (e) {
this.model.htmlBody.value = e.target.innerHTML;
},
onChange: function (data) {
this.model.htmlBody.value = data;
},
remove: function () {
this.model.imgBody.id = null;
this.model.imgBody.media = null;
},
select: function () {
if (this.model.imgBody.media != null) {
piranha.mediapicker.open(this.update, "Image", this.model.imgBody.media.folderId);
} else {
piranha.mediapicker.openCurrentFolder(this.update, "Image");
}
},
update: function (media) {
if (media.type === "Image") {
this.model.imgBody.id = media.id;
this.model.imgBody.media = media;
// Tell parent that title has been updated
this.$emit('update-title', {
uid: this.uid,
title: this.model.imgBody.media.filename
});
} else {
console.log("No image was selected");
}
}
},
computed: {
isEmpty: function () {
return {
htmlBody: piranha.utils.isEmptyHtml(this.model.htmlBody.value),
imgBody: this.model.imgBody.media == null
}
},
mediaUrl: function () {
if (this.model.imgBody.media != null) {
return piranha.utils.formatUrl(this.model.imgBody.media.publicUrl);
} else {
return piranha.utils.formatUrl("~/manager/assets/img/empty-image.png");
}
}
},
mounted: function () {
piranha.editor.addInline(this.uid, this.toolbar, this.onChange);
this.model.imgBody.getTitle = function () {
if (this.model.imgBody.media != null) {
return this.model.imgBody.media.filename;
} else {
return "No image selected";
}
};
},
beforeDestroy: function () {
piranha.editor.remove(this.uid);
},
template:
"<div class='block-body has-media-picker rounded' :class='{ empty: isEmpty }'>" +
" <img class='rounded' :src='mediaUrl'>" +
" <div class='media-picker'>" +
" <div class='btn-group float-right'>" +
" <button :id='uid' class='btn btn-info btn-aspect text-center' data-toggle='dropdown' aria-haspopup='true' aria-expanded='false'>" +
" <i v-if='model.aspect.value === 0' class='fas fa-cog'></i>" +
" <img v-else :src='iconUrl'>" +
" </button>" +
" <div class='dropdown-menu aspect-menu' :aria-labelledby='uid'>" +
" <label class='mb-0'>{{ piranha.resources.texts.aspectLabel }}</label>" +
" <div class='dropdown-divider'></div>" +
" <a v-on:click.prevent='selectAspect(0)' class='dropdown-item' :class='{ active: isAspectSelected(0) }' href='#'>" +
" <img :src='piranha.utils.formatUrl('~/manager/assets/img/icons/img-original.svg')'><span>{{ piranha.resources.texts.aspectOriginal }}</span>" +
" </a>" +
" <a v-on:click.prevent='selectAspect(1)' class='dropdown-item' :class='{ active: isAspectSelected(1) }' href='#'>" +
" <img :src='piranha.utils.formatUrl('~/manager/assets/img/icons/img-original.svg')'><span>{{ piranha.resources.texts.aspectOriginal }}</span>" +
" </a>" +
" <a v-on:click.prevent='selectAspect(2)' class='dropdown-item' :class='{ active: isAspectSelected(2) }' href='#'>" +
" <img :src='piranha.utils.formatUrl('~/manager/assets/img/icons/img-original.svg')'><span>{{ piranha.resources.texts.aspectOriginal }}</span>" +
" </a>" +
" <a v-on:click.prevent='selectAspect(3)' class='dropdown-item' :class='{ active: isAspectSelected(3) }' href='#'>" +
" <img :src='piranha.utils.formatUrl('~/manager/assets/img/icons/img-original.svg')'><span>{{ piranha.resources.texts.aspectOriginal }}</span>" +
" </a>" +
" <a v-on:click.prevent='selectAspect(4)' class='dropdown-item' :class='{ active: isAspectSelected(4) }' href='#'>" +
" <img :src='piranha.utils.formatUrl('~/manager/assets/img/icons/img-original.svg')'><span>{{ piranha.resources.texts.aspectOriginal }}</span>" +
" </a>" +
" </div>" +
" <button v-on:click.prevent='select' class='btn btn-primary text-center'>" +
" <i class='fas fa-plus'></i>" +
" </button>" +
" <button v-on:click.prevent='remove' class='btn btn-danger text-center'>" +
" <i class='fas fa-times'></i>" +
" </button>" +
" </div>" +
" <div class='card text-left'>" +
" <div class='card-body' v-if='isEmpty'>" +
" " +
" </div>" +
" <div class='card-body' v-else>" +
" {{ model.body.media.filename }}" +
" </div>" +
" </div>" +
"</div>" +
" <div contenteditable='true' :id='uid' spellcheck='false' v-html='htmlBody' v-on:blur='onBlur'></div>" +
"</div>"
});
它基本上是我在 GitHub 上的 repo 中找到的两个 Vue 组件的融合,但我已经对其进行了一些调整,以使其不会在 DevTools 控制台中出现错误。如果我能通过保存这些项目,我会重新审视这些项目。
所以,这是我对@tidyui 或任何成功实现类似这样的人的问题:
-
我这样做的方式是否正确?我只想要三列,每列将包含我的 CardBlock,它下面有一张图片和一个内容简介,但我希望 CardBlock 是一个单元(有点像引导卡)。有没有办法在不创建我自己的块的情况下做到这一点?我研究了嵌套 BlockGroup,但很快发现这是不可能的。
-
如果我在正确的轨道上,我需要帮助来解决我在尝试保存草稿时遇到的错误。该错误与Save ImageBlock error #1117 相同,似乎已在 8.2 中修复。我在 8.4.2。
我非常喜欢为 .Net Core 构建 CMS 的想法,而 Piranha 让我为自己创建的 CMS 大吃一惊。拜托,我只需要朝正确的方向一点点推动,我大部分时间都在这样做。
提前致谢, D
【问题讨论】:
-
在 SO 上找到另一个解决保存问题的帖子,但暂时让这个问题悬而未决,看看我的方法是否得到验证,或者我是否偏离了轨道。另外,只是想提请注意这个问题似乎已经从 8.2 到 8.4.2 重新出现,除非我弄错了。我找到的帖子是:stackoverflow.com/questions/61922839/…
标签: piranha-cms