gpt4 book ai didi

piranha-cms - Piranha CMS - 自定义 block 不会保存

转载 作者:行者123 更新时间:2023-12-04 08:31:54 33 4
gpt4 key购买 nike

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'>" +
" &nbsp;" +
" </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 是一个单元(有点像 Bootstrap Card)。有没有办法在不创建自己的块的情况下做到这一点?我研究了嵌套 BlockGroups,但很快发现这是不可能的。
  • 如果我在正确的轨道上,我需要帮助解决我在尝试保存草稿时遇到的错误。错误与 Save ImageBlock error #1117 相同,这似乎已在 8.2 中修复。我在 8.4.2。

  • 我非常喜欢为 .Net Core 构建的 CMS 的想法,而 Piranha 吹走了我为自己创建的 CMS。我只需要朝着正确的方向插入一点,拜托,我一天中的大部分时间都在这样做。
    提前致谢,
    D

    最佳答案

    以防万一这对某人有帮助。事实证明,我在将两个块合并在一起时做得很差。将此归结为对 Piranha 和 Vue.js 缺乏经验。我将文档中的代码与 repo 中的代码混合在一起。不要那样做 - 可以理解的是,文档仍然有点落后。我不是在向开发人员扔石头,我真的在挖掘他们创造的东西,并将继续努力熟练地使用它。
    下面是我为 Vue 组件设计的。可能仍然需要进行一些调整以更好地分离 Image-Block 和 Html-Block 代码,但它现在可以工作、保存并且不会在控制台中引发错误。

    /*global
    piranha
    */

    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;
    },
    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");
    }
    },
    remove: function () {
    this.model.imgBody.id = null;
    this.model.imgBody.media = null;
    },
    update: function (media) {
    if (media.type === "Image") {
    this.model.imgBody.id = media.id;
    this.model.imgBody.media = {
    id: media.id,
    folderId: media.folderId,
    type: media.type,
    filename: media.filename,
    contentType: media.contentType,
    publicUrl: media.publicUrl,
    };
    // 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");
    }
    },
    selectAspect: function (val) {
    this.model.aspect.value = val;
    },
    isAspectSelected(val) {
    return this.model.aspect.value === val;
    }
    },
    computed: {
    isImgEmpty: function (e) {
    return this.model.imgBody.media == null;
    },
    isHtmlEmpty: function () {
    return piranha.utils.isEmptyHtml(this.model.htmlBody.value);
    },
    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");
    }
    },
    iconUrl: function () {
    if (this.model.aspect.value > 0) {
    if (this.model.aspect.value === 1 || this.model.aspect.value === 3) {
    return piranha.utils.formatUrl("~/manager/assets/img/icons/img-landscape.svg");
    } else if (this.model.aspect.value == 2) {
    return piranha.utils.formatUrl("~/manager/assets/img/icons/img-portrait.svg");
    } else if (this.model.aspect.value == 4) {
    return piranha.utils.formatUrl("~/manager/assets/img/icons/img-square.svg");
    }
    }
    return null;
    }
    },
    mounted: function () {
    piranha.editor.addInline(this.uid, this.toolbar, this.onChange);
    this.model.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: isImgEmpty }'>" +
    " <div class='image-block'>" +
    " <img class='rounded' :src='mediaUrl'>" +
    " <div class='media-picker'>" +
    " <div class='btn-group float-right'>" +
    " <button :id='uid + \"-aspect\"' 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 + \"-aspect\"'>" +
    " <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-landscape.svg\")'><span>{{ piranha.resources.texts.aspectLandscape }}</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-portrait.svg\")'><span>{{ piranha.resources.texts.aspectPortrait }}</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-landscape.svg\")'><span>{{ piranha.resources.texts.aspectWidescreen }}</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-square.svg\")'><span>{{ piranha.resources.texts.aspectSquare }}</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='isImgEmpty'>" +
    " &nbsp;" +
    " </div>" +
    " <div class='card-body' v-else>" +
    " {{ model.imgBody.media.filename }}" +
    " </div>" +
    " </div>" +
    " </div>" +
    " </div>" +
    " <br />" +
    " <div class='html-block'>" +
    " <div class='block-body border rounded' :class='{ empty: isHtmlEmpty }' >" +
    " <div contenteditable='true' :id='uid' v-html='htmlBody' v-on:blur='onBlur'></div> " +
    " </div>" +
    " </div>" +
    "</div>"
    });
    我仍然很想得到一些确认,我没有不必要地做所有这些。如果有更好的方法来完成它,请不要退缩。

    关于piranha-cms - Piranha CMS - 自定义 block 不会保存,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64976918/

    33 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com