如何在 Keystone 的管理中重新排序字段 UI

How to reorder fields in Keystone's Admin UI

我正在尝试将 Keystone 制作成 CMS。所以,我需要 Article、Category、ImagePage、AttachmentPage 等模型。我提到的每个模型都有一个公共字段的子集,例如:标题、内容、元:{标题、描述、关键字}等等。

在 Keystone 中,模型是这样构建的:

Article.add(fieldsCollectionObject)

所以我在外部文件中定义了公共字段:

var T = require('keystone').Field.Types;

module.exports = {
    title: { type: T.Text, required: true },
    content: { type: T.Html, wysiwyg: true, height: 400 },
    meta: {
        title: { type: T.Text },
        desc: { type: T.Textarea, height: 50 },
        keywords: { type: T.Text },
    },
    publishedDate: { type: T.Date, index: true, dependsOn: { state: 'published' } },
    state: { type: T.Select, options: 'draft, published, archived', default: 'draft', index: true },
};

并在模型文件中要求它:

const _ = require('lodash');
const pageDef = require('./common/Page.js');
const keystone = require('keystone');
const T = keystone.Field.Types;

<...>

Article.add(_.defaultsDeep({
    brief: { type: T.Html, wysiwyg: true, height: 150 },
    category: { type: T.Relationship, ref: 'Category', many: false, collapse: true },
    tags: { type: T.Relationship, ref: 'Tag', many: true },
}, defs.authored, pageDef));

现在,问题出在 Admin UI 中的字段顺序 - 不出所料,briefcategorytags 排在 [=17 的字段之前=].有什么办法可以强加我想要的命令吗?喜欢 titlebriefcontent<the rest>?

defaults and defaultsDeep 改变作为参数传递给它的第一个对象(Keystone 字段的初始对象)。要拥有自己的顺序,您需要按照您希望它们在对象中出现的顺序将对象传递给 _.defaultsDeep,因此它们在 Admin UI 中出现的顺序也是如此。

有用的是,结果对象中不会包含重复项。所以你会有这样的东西:

const _ = require('lodash');
const pageDef = require('./common/Page.js');
const keystone = require('keystone');
const T = keystone.Field.Types;

//....

let articleDef = {
    brief: { type: T.Html, wysiwyg: true, height: 150 },
    category: { type: T.Relationship, ref: 'Category', many: false, collapse: true },
    tags: { type: T.Relationship, ref: 'Tag', many: true };
};

Article.add(_.defaultsDeep({
    title: pageDef.title,
    brief: articleDef.brief, 
    content: pageDef.content},
pageDef, articleDef));

上面的答案证明是可行的。所以我扩展并构建了它:

lib/util.js

const _ = require('lodash');

class Util {
    static sourceFields (fields, ...sources) {
        const source = _.defaultsDeep(...sources);
        const result = [];
        for (let fieldSet of fields) {
            result.push(_.isArray(fieldSet) ? _.pick(source, fieldSet) : fieldSet);
        }
        return result;
    }
}

module.exports = Util;

models/common/traits.js

var T = require('keystone').Field.Types;

module.exports = {
    title: { type: T.Text, required: true },
    content: { type: T.Html, wysiwyg: true, height: 400 },
    indexImage: { type: T.CloudinaryImage },
    meta: {
        title: { type: T.Text },
        desc: { type: T.Textarea, height: 50 },
        keywords: { type: T.Text },
    },
// <...>
}

models/Article.js

const util = require('../lib/utils.js');
const defs = require('./common/traits.js');
const keystone = require('keystone');
const T = keystone.Field.Types;

// < var Article declaration... >

const ownDef = {
    brief: { type: T.Html, wysiwyg: true, height: 150 },
    category: { type: T.Relationship, ref: 'Category', many: false, collapse: true },
    tags: { type: T.Relationship, ref: 'Tag', many: true },
    images: { type: T.Relationship, ref: 'Image', many: true, collapse: true },
    attachments: { type: T.Relationship, ref: 'Attachment', many: true, collapse: true },
};

Article.add(...util.sourceFields([
    'Content', ['title', 'brief', 'content', 'indexImage', 'category', 'tags'],
    'Media', ['images', 'attachments'],
    'SEO', ['meta'],
    'Status', ['pageType', 'author', 'state', 'publishedDate'],
], ownDef, defs));

因此,在 traits.js 中我定义了公共字段,在 Article.js 中 - 我仅在文章模型中使用的字段。然后,在文章模型中,我借助 sourceFields() 函数将字段添加到列表中。 sourceFields() 获取字段集数组和未指定数量的字段定义 objects(如 ownDefdefs)。

字段集是字符串或字段名称数组(定义中的键 objects)。如果它是字符串,它将是 Admin UI 中的 header,如果它是数组,那么它将是一组字段,就像数组中的字段名称一样排序 - 该函数基本上将字段定义插入到"slot" 在字段集中指定。