Aller au contenu principal

Guide de développement des plugins de type de champ personnalisé Spark Workbench

Ce guide s'adresse aux développeurs qui veulent étendre les types de champs de tâche Spark Workbench. Un plugin tiers doit pouvoir livrer, comme le plugin Spark par défaut, les définitions de types de champs, les définitions de champs système, la logique de configuration, les opérateurs TQL et les composants Vue correspondants.

Le plugin de champs par défaut se trouve ici :

spark-plugins/spark-workbench-default-plugin/

Les types de champs intégrés Spark, par exemple Text, Rich Text, Markdown, User et Attachment, ainsi que les champs système comme Title, Assignee et Description, sont enregistrés par ce plugin. Le host de la plateforme ne consomme que les points d'extension ; les types de champs concrets ne sont plus codés en dur dans le concepteur de tâches.

1. Modèle de plugin de champ​

Un plugin de type de champ comporte généralement deux parties :

  • Extension backend : retourne les types de champs, les champs système, les formulaires de configuration, les opérateurs TQL supportés et le protocole de filtre via une Lattice Ability.
  • Composant frontend : rend les contrôles de création / édition avec Vue, et peut aussi fournir des composants en lecture seule ou pour la page de détail de tâche.

Structure recommandée :

my-task-field-plugin/
pom.xml
src/main/java/
.../MyTaskFieldPlugin.java
.../MyFieldRegistryExt.java
.../PriorityFieldTypeProvider.java
src/main/frontend/
package.json
vite.config.js
src/
index.js
PriorityField.vue
PriorityDetailField.vue

Le jar du plugin doit contenir à la fois les extensions Java et les ressources frontend statiques. Spark les charge au runtime.

2. Identité métier​

Le plugin doit déclarer sa propre identité métier Lattice :

@Business(code = "my.task.field.plugin", name = "My Task Field Plugin")
public class MyTaskFieldPlugin extends BusinessTemplate {
}

Lors de l'enregistrement des champs, Spark parcourt toutes les identités métier Lattice enregistrées et appelle la Field Registry Ability pour chacune. Un plugin tiers ne doit pas rattacher @Realization à un code intégré de la plateforme ; il doit utiliser sa propre identité métier.

3. Enregistrement des types de champs côté backend​

Le point d'extension d'enregistrement est SparkEraTaskFieldRegistryExt. En général, il suffit d'étendre l'implémentation vide :

@Realization(codes = "my.task.field.plugin")
public class MyFieldRegistryExt extends BlankSparkEraTaskFieldRegistryExt {

private static final String FRONTEND_RESOURCE =
"/spark-gadgets/my-task-field-plugin/index.umd.js";

@Override
public List<SparkEraTaskCustomFieldTypeProvider> listCustomFieldTypeProviders(
SparkEraTaskFieldRegistryContext context) {
return List.of(new PriorityFieldTypeProvider(FRONTEND_RESOURCE));
}
}

Exemple de provider de type de champ :

public class PriorityFieldTypeProvider implements SparkEraTaskCustomFieldTypeProvider {

private final String frontendResource;

public PriorityFieldTypeProvider(String frontendResource) {
this.frontendResource = frontendResource;
}

@Override
public String getCode() {
return "priority_score";
}

@Override
public String getTitle() {
return "Priority Score";
}

@Override
public SparkEraTaskFieldCategory getCategory() {
return SparkEraTaskFieldCategory.BASIC;
}

@Override
public String getDescription() {
return "A numeric priority score with visual emphasis.";
}

@Override
public String getComponentType() {
return "priorityScore";
}

@Override
public String getTaskFieldType() {
return "number";
}

@Override
public String getPreviewIcon() {
return "gauge";
}

@Override
public boolean isSortable() {
return true;
}

@Override
public List<String> supportOperators() {
return List.of("eq", "neq", "gt", "ge", "lt", "le");
}

@Override
public String getFrontendResource() {
return frontendResource;
}

@Override
public String getEditComponent() {
return "my$field-priority-score";
}

@Override
public String getDetailComponent() {
return "my$task-detail-priority-score-field";
}
}

Champs importants :

  • code : code de type de champ, idéalement unique globalement.
  • componentType : sémantique du contrôle runtime.
  • taskFieldType : type de valeur sous-jacent, utilisé par la recherche, l'indexation et l'affichage.
  • previewIcon : icône dans la palette de champs du concepteur de tâches.
  • supportOperators : opérateurs TQL disponibles pour ce champ. Ne retournez pas l'ensemble complet par défaut.
  • frontendResource : entrée UMD frontend du plugin.
  • editComponent : clé du composant Vue pour les formulaires de création / édition.
  • readonlyComponent : clé du composant Vue pour les formulaires en lecture seule ; optionnelle.
  • detailComponent : clé du composant du protocole de rendu de détail de tâche.

4. Enregistrement des champs système​

Les champs système utilisent SparkEraTaskSystemFieldProvider :

@Override
public List<SparkEraTaskSystemFieldProvider> listSystemFieldProviders(
SparkEraTaskFieldRegistryContext context) {
return List.of(new EscalationLevelSystemFieldProvider(FRONTEND_RESOURCE));
}

Un provider de champ système doit retourner un code stable, le label d'affichage, le type de champ, l'affichabilité en liste, la recherchabilité, la triabilité et les opérateurs supportés. Un champ système peut également déclarer frontendResource et les clés de composants.

5. Enregistrement des composants frontend​

L'entrée frontend du plugin enregistre les composants via window.__SPARK_TASK_FIELDS__ exposé par le host :

import PriorityField from "./PriorityField.vue";
import PriorityDetailField from "./PriorityDetailField.vue";

const api = typeof window === "undefined" ? null : window.__SPARK_TASK_FIELDS__;

if (api?.registerFieldComponent) {
api.registerFieldComponent("my$field-priority-score", PriorityField);
}

if (api?.registerDetailFieldComponent) {
api.registerDetailFieldComponent("my$task-detail-priority-score-field", PriorityDetailField);
}

Les composants de création / édition reçoivent principalement ces props :

defineProps({
modelValue: [String, Number, Boolean, Array, Object],
field: Object,
definition: Object,
placeholder: String,
disabled: Boolean,
context: Object
});

Utilisez emit("update:modelValue", value) pour écrire la valeur du champ. context contient des capacités du host comme t, apiFetch, openUserSelect et chooseAttachmentFiles. Les composants tiers doivent réutiliser ces capacités au lieu de réimplémenter les interactions communes de la plateforme.

Les composants de détail de tâche utilisent le protocole de rendu Spark et reçoivent componentModel, renderContext et t :

<template>
<div class="priority-detail">
<span>{{ field.label }}</span>
<strong>{{ field.value }}</strong>
</div>
</template>

<script setup>
import { computed } from "vue";

const props = defineProps({
componentModel: Object
});

const field = computed(() => props.componentModel?.props?.field || {});
</script>

6. Build Vite​

Le frontend d'un plugin de champ utilise le mode library de Vite comme les plugins Gadget, avec Vue marqué comme external :

import { resolve } from "node:path";
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";

export default defineConfig({
plugins: [vue()],
build: {
outDir: resolve(__dirname, "../../../target/classes/META-INF/resources/spark-gadgets/my-task-field-plugin"),
lib: {
entry: resolve(__dirname, "src/index.js"),
name: "MyTaskFieldPlugin",
formats: ["umd"],
fileName: () => "index.umd.js",
cssFileName: "index"
},
rollupOptions: {
external: ["vue"],
output: {
globals: {
vue: "__SPARK_VUE__"
}
}
}
}
});

Après le build, l'URL runtime de la ressource est :

/spark-gadgets/my-task-field-plugin/index.umd.js

7. Checklist de développement​

  • Les codes de types de champs et de champs système doivent être stables.
  • @Realization(codes = "...") doit utiliser l'identité métier du plugin.
  • Les opérateurs de recherche doivent correspondre au type de valeur du champ ; ne retournez pas tous les opérateurs.
  • La triabilité doit être explicitement déclarée par le type de champ. Rich text, pièces jointes et textes longs sont généralement de mauvais candidats au tri.
  • Les composants frontend ne doivent pas embarquer un runtime Vue séparé.
  • Les styles de composants doivent utiliser les variables de thème Spark plutôt que des couleurs codées en dur.
  • Les labels, descriptions et textes de configuration des champs doivent être retournés via le mécanisme i18n backend.