介绍
directus 支持extension扩展有多种方式
- display: 用于定制后台列表页面的某一列字段显示,对应数据库某个字段,使用vue3展示
- interface: 用于定制后台详细页面的某一字段显示,对应数据库某个字段,使用vue3展示
- operation: 在flow里面的框框,有输入和输出。可以做更细粒度的业务封装
- endpoint: 后台express的一个路由RESTful服务
- hook: 监听数据变化钩子函数
- layout: 后台列表页面的整体布局(列表或瀑布流)
- panel: 后台报表分析的模块图像/表格化显示
- module: 后台系统左边大菜单模块,可以定制更丰富的模块内容
- bundle: 自定义库,可以一次性把上面所有扩展都写到一个库中,方便不同模块之间共享数据方法
git demo
1. display
display:用于定制后台列表页面的某一列字段显示,对应数据库某个字段,使用vue3展示
我们用官方自带脚手架定制一个后台的display
cd extensions/displays #进入扩展路径
npx create-directus-extension@latest # 执行扩展安装
# 提示
Need to install the following packages:
create-directus-extension@10.0.15
Ok to proceed? (y) y # 输入y
This utility will walk you through creating a Directus extension.
# 选择 display
? Choose the extension type
panel
hook
endpoint
operation
bundle
interface
❯ display
# 定义名称
Choose a name for the extension mytest
# 选择语言
? Choose the language to use
javascript
❯ typescript
# 根据提示 我们进入进入项目运行调试或构建
To start developing, run:
cd mytest
npm run dev
and then to build for production, run:
npm run build
生成的代码
为了方便调整 我们打开项目的热更新
EXTENSIONS_AUTO_RELOAD=true
EXTENSIONS_PATH="./extensions"
启动服务,发现缺少index.js 因为自定义display打包路径是mytest/dist/index.js,而directus访问的路径是mytest/index.js
[17:41:44.367] WARN: Couldn't bundle App extensions
[17:41:44.368] WARN: Could not resolve "./extensions/displays/mytest/index.js" from "virtual:entry"
err: {
"type": "Error",
"message": "Could not resolve \"./extensions/displays/mytest/index.js\" from \"\u0000virtual:entry\"",
我们调整mytest的package.json输出路径
"directus:extension": {
"type": "display",
"path": "dist/index.js",
"source": "src/index.ts",
"host": "^10.1.14"
},
把 "path": "dist/index.js",调整为 "path": "./index.js",
再次启动,已经提示正常加载组件
测试一把
进入city表,选择name编辑
在显示拦新增了custom选项
我们对比选中自定义的name显示区别。
其实就是把字段通过自定义组件显示
代码分析
src/display.vue
生成的是vue3代码
<template>
<div>Value: {{ value }}</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
export default defineComponent({
props: {
value: {
type: String,
default: null,
},
},
});
</script>
src/index.ts
import { defineDisplay } from '@directus/extensions-sdk';
import DisplayComponent from './display.vue';
export default defineDisplay({
id: 'custom',
name: 'Custom',
icon: 'box',
description: 'This is my custom display!',
component: DisplayComponent,
options: null, //这里可以外部传入自定义的逻辑处理显示的交互
types: ['string'], //这里对应数据字段类型,只有匹配的类型才能选择该display
});
2. interface
interface: 用于定制后台详细页面的某一字段显示,对应数据库某个字段,使用vue3展示
和display相同操作,我们创建interface
cd extensions/interfaces #进入扩展路径
npx create-directus-extension@latest # 执行扩展安装
选择 interface 》 typescript
修改配置 我们调整mytest的package.json输出路径
"directus:extension": {
"type": "interface",
"path": "./index.js",
"source": "src/index.ts",
"host": "^10.1.14"
},
生成代码
测试一把
运行
cd ./extensions/interfaces/mytest
npm run dev
发现directus已经热加载最新interface插件
开始设置
在city -> name字段 接口栏发现 custom
对比详情页面差异
代码分析
src/interface.vue
生成的是vue3代码
<template>
<input :value="value" @input="handleChange($event.target.value)" />
</template>
<script lang="ts">
import { defineComponent } from 'vue';
export default defineComponent({
props: {
value: {
type: String,
default: null,
},
},
emits: ['input'],
setup(props, { emit }) {
return { handleChange };
function handleChange(value: string): void {
emit('input', value);
}
},
});
</script>
src/index.ts
import { defineInterface } from '@directus/extensions-sdk';
import InterfaceComponent from './interface.vue';
export default defineInterface({
id: 'custom',
name: 'Custom',
icon: 'box',
description: 'This is my custom interface!',
component: InterfaceComponent,
options: null,//这里可以外部传入自定义的逻辑处理显示的交互
types: ['string'],//这里对应数据字段类型,只有匹配的类型才能选择该display
});
3.operation
operation: 就是在flow里面的框框,有输入和输出。可以做更细粒度的业务封装
和上面相同操作,我们创建operation
cd extensions/operations #进入扩展路径
npx create-directus-extension@latest # 执行扩展安装
选择 operation 》 typescript
修改配置 我们调整mytest的package.json输出路径
"directus:extension": {
"type": "interface",
"path": "./index.js",
"source": "src/index.ts",
"host": "^10.1.14"
},
生成代码
测试一把
运行
cd ./extensions/operation/mytest
npm run dev
发现directus已经热加载最新operation插件
新建一个get请求的flow
创建一个operation,发现底部多了一个custom选项
选中并输入11111
访问get请求
http://127.0.0.1:8055/flows/trigger/ed2f22a8-ae4e-4ee8-9cbe-9b72a5195ba8
可以看到控制台输出 11111
代码分析
src/api.ts 运行时的代码
import { defineOperationApi } from '@directus/extensions-sdk';
type Options = {
text: string;
};
export default defineOperationApi<Options>({
id: 'custom',
handler: ({ text }) => {
console.log(text); //这里把接收的参数打印到控制台
},
});
src/app.ts 用于定义operation的配置项
import { defineOperationApp } from '@directus/extensions-sdk';
export default defineOperationApp({
id: 'custom',
name: 'Custom1',
icon: 'box',
description: 'This is my custom operation!', //描述信息
overview: ({ text }) => [
{
label: 'Text',
text: text,
},
],
options: [ //定义了一个文本输入框展示方式
{
field: 'text',
name: 'Text',
type: 'string',
meta: {
width: 'full',
interface: 'input',
},
},
],
});
4.endpoint
endpoint: 后台express的一个路由服务
和上面相同操作,我们创建endpoint
cd extensions/endpoints #进入扩展路径
npx create-directus-extension@latest # 执行扩展安装
选择 endpoint 》 typescript
修改配置 我们调整mytest的package.json输出路径
"directus:extension": {
"type": "interface",
"path": "./index.js",
"source": "src/index.ts",
"host": "^10.1.14"
},
生成代码
测试一把
运行
cd ./extensions/endpoint/mytest
npm run dev
发现directus已经热加载最新endpoint插件
在地址栏访问 127.0.0.1:8055/mytest/
输出hello word!
代码分析
src/index.ts
import { defineEndpoint } from '@directus/extensions-sdk';
export default defineEndpoint((router) => {
router.get('/', (_req, res) => res.send('Hello, World!'));
});
可以看到写法其实就是一个express的服务,默认路由前缀是 mytest
5.hook
hook: 监听数据变化钩子函数
和上面相同操作,我们创建hook
cd extensions/hooks #进入扩展路径
npx create-directus-extension@latest # 执行扩展安装
选择 hook 》 typescript
修改配置 我们调整mytest的package.json输出路径
"directus:extension": {
"type": "interface",
"path": "./index.js",
"source": "src/index.ts",
"host": "^10.1.14"
},
生成代码
测试一把
运行
cd ./extensions/hook/mytest
npm run dev
发现directus已经热加载最新hook插件
测试新增一条city数据
发现控制台打印
代码分析
src/index.ts
import { defineHook } from '@directus/extensions-sdk';
export default defineHook(({ filter, action }) => {
filter('items.create', () => {
console.log('Creating Item!');
});
action('items.create', () => {
console.log('Item created!');
});
});
可以看到在数据发生变化的时候,监听了数据的创建过滤事件和动作事件
优化代码
我们把入参打印
import { defineHook } from '@directus/extensions-sdk';
export default defineHook(({ filter, action }) => {
filter('items.create', (args) => {
console.log('Creating Item!',args);
});
action('items.create', (args) => {
console.log('Item created!',args);
});
});
新建一条rrrr数据
可以获取变化数据,如id 参数 进行具体的业务逻辑处理
6.panel
panel: 后台报表分析的模块图像/表格化显示
和上面相同操作,我们创建panel
cd extensions/panels #进入扩展路径
npx create-directus-extension@latest # 执行扩展安装
选择 panel 》 typescript
修改配置 我们调整mytest的package.json输出路径
"directus:extension": {
"type": "interface",
"path": "./index.js",
"source": "src/index.ts",
"host": "^10.1.14"
},
生成代码
测试一把
运行
cd ./extensions/panel/mytest
npm run dev
发现directus已经热加载最新panel插件
新建一个表盘
我们可以新增一个省份的列表对比
代码分析
src/panel.vue
<template>
<div class="text" :class="{ 'has-header': showHeader }">
{{ text }}
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
export default defineComponent({
props: {
showHeader: {
type: Boolean,
default: false,
},
text: {
type: String,
default: '',
},
},
});
</script>
<style scoped>
.text {
padding: 12px;
}
.text.has-header {
padding: 0 12px;
}
</style>
src/index.ts
import { definePanel } from '@directus/extensions-sdk';
import PanelComponent from './panel.vue';
export default definePanel({
id: 'custom',
name: 'Custom',
icon: 'box',
description: 'This is my custom panel!',
component: PanelComponent,
options: [
{
field: 'text',
name: 'Text',
type: 'string',
meta: {
interface: 'input',
width: 'full',
},
},
],
minWidth: 12,
minHeight: 8,
});
7.layout
layout: 后台列表页面的整体布局(列表或瀑布流)
和上面相同操作,我们创建layout
cd extensions/layouts #进入扩展路径
npx create-directus-extension@latest # 执行扩展安装
选择 layout 》 typescript
修改配置 我们调整mytest的package.json输出路径
"directus:extension": {
"type": "interface",
"path": "./index.js",
"source": "src/index.ts",
"host": "^10.1.14"
},
生成代码
测试一把
运行
cd ./extensions/layout/mytest
npm run dev
发现directus已经热加载最新layout插件
我们在列表页面可以选择自定义的布局效果
由于我们没有重写对应列表逻辑。所以显示了默认的文本提示内容
代码分析
src/layout.vue
<template>
<div>
<p>Name: {{ name }}</p>
<p>Collection: {{ collection }}</p>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
export default defineComponent({
inheritAttrs: false,
props: {
collection: {
type: String,
required: true,
},
name: {
type: String,
required: true,
},
},
});
</script>
src/index.ts
import { ref } from 'vue';
import { defineLayout } from '@directus/extensions-sdk';
import LayoutComponent from './layout.vue';
export default defineLayout({
id: 'custom',
name: 'Custom',
icon: 'box',
component: LayoutComponent,
slots: {
options: () => null,
sidebar: () => null,
actions: () => null,
},
setup() {
const name = ref('Custom Layout');
return { name };
},
});
8.module
module: 后台系统左边大菜单模块,可以定制更丰富的模块内容
和上面相同操作,我们创建module
cd extensions/modules #进入扩展路径
npx create-directus-extension@latest # 执行扩展安装
选择 module 》 typescript
修改配置 我们调整mytest的package.json输出路径
"directus:extension": {
"type": "interface",
"path": "./index.js",
"source": "src/index.ts",
"host": "^10.1.14"
},
生成代码
测试一把
运行
cd ./extensions/module/mytest
npm run dev
发现directus已经热加载最新module插件
进入设置页面可以发现设置模块里面多出来一个custom
勾选+保存
左侧菜单多出了对应的新菜单模块选项
9.bundle
bundle: 自定义库,可以一次性把上面所有扩展都写到一个库中,方便不同模块之间共享数据方法
和上面相同操作,我们创建bundle
mkdir bundles # 新建bundles文件夹
cd extensions/ #进入扩展路径
npx create-directus-extension@latest # 执行扩展安装
? Choose the extension type bundle #选择 bundle
? Choose a name for the extension directus-extension-bundle # 名称命名为 directus-extension-bundle
注意 : 名称必须是directus-extension-xxxxx 否则不能正常识别,并且是在extension路径下,不用嵌套两级目录
生成代码
我们看到其实只是生成的package.json和安装了一些依赖,并没有具体的代码
测试一把
运行
cd mytest
npm run dev
发现directus已经热加载最新directus-extension-bundle自定义库
这里我们看到他生成的代码输出在 dist下的 app.js和api.js
分析生成代码
api.js
const hooks = [];const endpoints = [];const operations = [];
export { endpoints, hooks, operations };
app.js
const hooks = [];const endpoints = [];const operations = [];
export { endpoints, hooks, operations };
生成的api.js 和 app.js 其实就是把当前bundle里面定义的库再往外面再导出一遍,由于当前没有实际的extension代码。导出为空。directus启动的时候会去查找所有满足directus-extension-xx文件夹,并且加载dist/app.js和dist/api.js
创建子扩展代码
cd ./extensions/directus-extension-bundle/ # 进入bundle文件夹
sudo npm run add # 执行directus自带的 添加自定义库
Choose the extension type endpoint # 选择创建一个endpoint
? Choose a name for the entry myendpoint # 命名 myendpoint
? Choose the language to use typescript # 语言typescript
可以看到新增了src文件夹,并且创建了endpoint项目,默认输出hello word
测试第二把
cd directus-extension-bundle
npm run dev # 启动
控制台打印库已经热更新
访问路径 127.0.0.1:8055/myendpoint
请求正常返回Hello, World