@nocobase/plugin-ai 2.2.0-beta.13 → 2.2.0-beta.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ai/docs/nocobase/ai-employees/scenarios/company-background-research.md +125 -0
- package/dist/ai/docs/nocobase/building-tips/operations-dashboard.md +513 -0
- package/dist/ai/docs/nocobase/file-manager/stable-url.md +87 -0
- package/dist/ai/docs/nocobase/get-started/deployment/production.md +24 -2
- package/dist/ai/docs/nocobase/get-started/installation/docker-caddy.mdx +3 -0
- package/dist/ai/docs/nocobase/get-started/installation/docker-nginx.mdx +3 -0
- package/dist/ai/docs/nocobase/get-started/installation/docker.mdx +27 -3
- package/dist/ai/docs/nocobase/get-started/installation/env.md +33 -0
- package/dist/ai/docs/nocobase/index.md +1 -1
- package/dist/ai/docs/nocobase/multi-app/multi-app-vs-multi-portal-vs-multi-space.md +159 -0
- package/dist/ai/docs/nocobase/multi-app/multi-portal/index.md +195 -0
- package/dist/ai/docs/nocobase/nocobase-cli/production/index.md +10 -0
- package/dist/ai/docs/nocobase/nocobase-cli/production/reverse-proxy/caddy.md +15 -2
- package/dist/ai/docs/nocobase/nocobase-cli/production/reverse-proxy/index.md +1 -1
- package/dist/ai/docs/nocobase/nocobase-cli/production/reverse-proxy/nginx.md +16 -2
- package/dist/ai/docs/nocobase/tutorials/index.md +20 -1
- package/dist/client/646.051c1437c9ca4fd4.js +10 -0
- package/dist/client/index.js +3 -3
- package/dist/externalVersion.js +16 -16
- package/dist/locale/en-US.json +2 -0
- package/dist/locale/zh-CN.json +2 -0
- package/dist/node_modules/@langchain/mistralai/package.json +1 -1
- package/dist/node_modules/@langchain/xai/package.json +1 -1
- package/dist/node_modules/fs-extra/package.json +1 -1
- package/dist/node_modules/jsonrepair/package.json +1 -1
- package/dist/node_modules/just-bash/package.json +1 -1
- package/dist/node_modules/nodejs-snowflake/package.json +1 -1
- package/dist/node_modules/openai/package.json +1 -1
- package/dist/node_modules/zod/package.json +1 -1
- package/dist/server/ai-employees/ai-employee.d.ts +1 -0
- package/dist/server/ai-employees/ai-employee.js +30 -1
- package/dist/server/attachments.d.ts +24 -0
- package/dist/server/attachments.js +204 -0
- package/dist/server/document-loader/cached.d.ts +1 -0
- package/dist/server/document-loader/cached.js +18 -9
- package/dist/server/document-loader/loader.d.ts +0 -1
- package/dist/server/document-loader/loader.js +25 -8
- package/dist/server/document-loader/types.d.ts +7 -0
- package/dist/server/llm-providers/anthropic.d.ts +2 -1
- package/dist/server/llm-providers/anthropic.js +1 -3
- package/dist/server/llm-providers/google-genai.d.ts +1 -1
- package/dist/server/llm-providers/google-genai.js +1 -5
- package/dist/server/llm-providers/provider.d.ts +3 -2
- package/dist/server/llm-providers/provider.js +26 -3
- package/dist/server/plugin.js +14 -0
- package/dist/server/resource/aiConversations.js +44 -1
- package/dist/server/utils.d.ts +5 -2
- package/dist/server/utils.js +11 -19
- package/dist/server/workflow/nodes/employee/files.d.ts +3 -1
- package/dist/server/workflow/nodes/employee/files.js +31 -5
- package/package.json +2 -2
- package/dist/client/646.2e395a514c511084.js +0 -10
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Workflow + AI for employees to complete company background research automation"
|
|
3
|
+
description: "Through company information forms, background investigation records, workflows, and AI employees, a company background investigation process can be automatically triggered, retained, and supported for manual review."
|
|
4
|
+
keywords: "NocoBase, AI employees, workflow, company background research, due diligence, automation, business practice"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Workflow + AI for employees to complete company background research automation
|
|
8
|
+
|
|
9
|
+
In NocoBase, you can turn company background research into a trackable automated task flow. Business staff still work in the familiar company information page, while workflow and AI staff are responsible for completing background information, recording the processing process, and saving each generated report.
|
|
10
|
+
|
|
11
|
+

|
|
12
|
+
|
|
13
|
+
This scenario is suitable for dealing with a common problem: company background information is not a static field that ends after being entered once. Public information will change, regulatory events will occur, and the status of cooperation will be constantly adjusted as business progresses. If you only rely on manual supplementary recording on a regular basis, it will be easy to miss; if you directly let AI cover company information, it will be difficult to explain "how this judgment came about." The approach here is to separate and save the current data and the research process - the company record saves the version being used by the business personnel, and the background check record saves the status, output and history of each AI survey.
|
|
14
|
+
|
|
15
|
+
## Let’s look at the two tables first
|
|
16
|
+
|
|
17
|
+
The company information form provides the basic information of the research object, and the background investigation record form is responsible for undertaking each research task. One saves the currently available information, and the other saves the processing process and historical results.
|
|
18
|
+
|
|
19
|
+
### `companies`: Company information table
|
|
20
|
+
|
|
21
|
+
| Core fields | effect |
|
|
22
|
+
| ---------------------- | -------------------------------------------------------------- |
|
|
23
|
+
| Company name | The main identifying information of the research object. |
|
|
24
|
+
| Website | Provide official website clues to reduce misjudgments caused by companies with the same name or abbreviation. |
|
|
25
|
+
| Address | Assist in determining regions, entities and business scope. |
|
|
26
|
+
| Company type | Mark business relationships such as customers, suppliers, partners, etc. to facilitate subsequent judgment and follow-up priorities. |
|
|
27
|
+
| Background information | Save the company background report you are currently using and use Markdown to render structured content. |
|
|
28
|
+
|
|
29
|
+
### `background_check_tasks`: Background check record form
|
|
30
|
+
|
|
31
|
+
| Core fields | effect |
|
|
32
|
+
| ------------------------- | ---------------------------------------------------------------------------------- |
|
|
33
|
+
| Company ID / Company name | Record which company this survey is for to facilitate task execution and historical review. |
|
|
34
|
+
| Status | The flow of marking tasks from `pending` to `processing` and `completed` is also the basis for preventing repeated triggering. |
|
|
35
|
+
| Research report | Save the complete research report generated by AI this time. |
|
|
36
|
+
| Summary | Save AI's summary of the research process, risk points, and information to be supplemented. |
|
|
37
|
+
| Previous background | Save the old version before writing back, supporting historical tracking and comparison of old and new reports. |
|
|
38
|
+
|
|
39
|
+

|
|
40
|
+
|
|
41
|
+
## Enter the research process from company information
|
|
42
|
+
|
|
43
|
+
The company list is the most familiar entrance for business people. You can see the company name, official website, company type, contact person, email and other information on the page. After entering a company, business personnel can view the current background report or initiate a new background investigation.
|
|
44
|
+
|
|
45
|
+
After entering the editing page, "Background information" is displayed using the Markdown editing component. The AI-generated content is not a short summary, but a structured report that can be read, copied, and continued to be maintained. Business personnel can still modify it manually, but each result generated by AI will leave a corresponding history in the background check record.
|
|
46
|
+
|
|
47
|
+

|
|
48
|
+
|
|
49
|
+
In this way, the page still looks like an ordinary company data maintenance interface, and the underlying processing method has become "current data + research history". The company table saves the current version, and the task table saves the process and evidence chain.
|
|
50
|
+
|
|
51
|
+
## Three triggering methods
|
|
52
|
+
|
|
53
|
+

|
|
54
|
+
|
|
55
|
+
Background research shouldn’t just rely on a manual button. In real business, you may want to automatically complete the information after adding a new company, you may also need to make up historical records regularly, and you may also take the initiative to re-investigate before signing a contract or reviewing.
|
|
56
|
+
|
|
57
|
+
The `New company background check` workflow handles automatic research after adding or updating a company. It listens to the data events of the company table and is triggered when the company name exists and the background information is empty. The AI will not be called immediately after triggering, but will first check whether there are any unfinished tasks for the same company; if not, a new background check record will be created.
|
|
58
|
+
|
|
59
|
+

|
|
60
|
+
|
|
61
|
+
The `Timing company background check` workflow is responsible for the continuous completion of historical data. It runs every 30 minutes, queries companies whose background information is still empty, and loops through batches. Inside the loop, we also check whether the task already exists, and then decide whether to create a new task. In this way, the scheduled task can be run repeatedly without creating multiple concurrently processed records due to repeated scanning.
|
|
62
|
+
|
|
63
|
+

|
|
64
|
+
|
|
65
|
+
The `Manual company background check` workflow is bound to the "Run background check" button on the company details page, which is suitable for business personnel to proactively initiate a survey before visiting, signing a contract, or reviewing. Manual triggering and automatic triggering use the same set of follow-up links: a background check record is created first, and then the task execution workflow takes over the AI investigation.
|
|
66
|
+
|
|
67
|
+

|
|
68
|
+
|
|
69
|
+
These three entrances solve problems at different points in time, and are ultimately merged into the same background investigation record form. New triggers, scheduled triggers, and manual triggers are only responsible for recording the "need to investigate", and the specific execution, status management, and result writing back are handed over to subsequent workflows for unified processing.
|
|
70
|
+
|
|
71
|
+
## Turn AI research into tasks
|
|
72
|
+
|
|
73
|
+
`Do company background check` is the workflow that actually performs research. It listens for the `pending` record in the background check record table. Once the previous automatic, scheduled or manual process creates a task, this workflow will be triggered asynchronously.
|
|
74
|
+
|
|
75
|
+
When executed, the workflow first queries whether the company still exists. If the company does not exist, the task will be closed and the description will be written; if the company exists, the task status will be switched to `processing`, and then the AI employee will be called to generate the report. The prompt word of the AI employee requires the output of two parts: a Markdown report that can be written directly into the company background field, and a summary for manual review.
|
|
76
|
+
|
|
77
|
+

|
|
78
|
+
|
|
79
|
+
After AI returns structured results, the workflow first writes the report, summary, and old background content into the background check record, and then writes the new report back to the company record. This order avoids the problem of "only the latest results, no process records": the company page keeps the latest available content, and the task records retain the context before this generation and writing back.
|
|
80
|
+
|
|
81
|
+

|
|
82
|
+
|
|
83
|
+
After tasking, batch processing will also become more natural. The scheduled workflow does not need to wait for each company's research to be completed, but is only responsible for creating multiple records to be processed; each record independently triggers the AI survey. Multiple companies can advance in parallel, and if a certain task fails or times out, other companies will not be blocked.
|
|
84
|
+
|
|
85
|
+
## Make AI results reviewable
|
|
86
|
+
|
|
87
|
+
AI-generated reports are organized according to a fixed structure: company profile, core business, development history and capital background, market position and competitive perspective, sales follow-up judgment, and citation links. Business personnel can see not only the "conclusion", but also the risk tips and additional information given by AI in the summary.
|
|
88
|
+
|
|
89
|
+
The background investigation record details page displays "Research report" and "Previous background" in tabs, and provides a "Copy" operation. In this way, you can quickly copy this report during discussion, review, or external communication, and you can also check changes against the old version.
|
|
90
|
+
|
|
91
|
+
The record details also configure two AI worker tasks. in:
|
|
92
|
+
|
|
93
|
+
- Improve the background research report: regenerate the report after adding information through dialogue, and write the results back to the company records
|
|
94
|
+
- Compare the old and new background research reports: Read the old and new reports and let AI explain the substantial differences brought about by this update
|
|
95
|
+
|
|
96
|
+
This allows AI not to stop at “generating text once” but to participate in the process of continuous maintenance, review and version comparison.
|
|
97
|
+
|
|
98
|
+

|
|
99
|
+
|
|
100
|
+
## How to combine workflow
|
|
101
|
+
|
|
102
|
+
Overall, this set of workflows can be divided into four layers.
|
|
103
|
+
|
|
104
|
+
The first layer is responsible for creating tasks. `New company background check` is for newly added or updated companies, `Timing company background check` is for historical data completion, and `Manual company background check` is for manual initiative. They will all check whether there are any unfinished records before creating a task, reducing duplicate processing from the source.
|
|
105
|
+
|
|
106
|
+
The second layer is responsible for performing tasks. `Do company background check` listens to the background check record, advances the pending task to processing, calls the AI employee, and writes the report, summary, and company's current background fields upon completion.
|
|
107
|
+
|
|
108
|
+
The third layer is responsible for providing controlled writeback capabilities to AI employees. As a tool-based workflow, `Update company background` restricts AI to only write specified records according to clear parameters to avoid over-exerting data modification permissions.
|
|
109
|
+
|
|
110
|
+
The fourth layer is responsible for exception cleaning. `Clean overtime processing background check` runs every 30 minutes to clean up non-completed tasks that have not been completed for more than 15 minutes to avoid long-term processing of tasks after abnormal interruption.
|
|
111
|
+
|
|
112
|
+

|
|
113
|
+
|
|
114
|
+
## What scenarios can be migrated to?
|
|
115
|
+
|
|
116
|
+
What this scene shows is not an isolated form or a separate AI button, but a combination of several capabilities in NocoBase: the data table is responsible for carrying business objects and historical records, the page is responsible for viewing and triggering by business personnel, the workflow is responsible for scheduling and writing back, and the AI staff is responsible for generating reviewable structured results.
|
|
117
|
+
|
|
118
|
+
Similar models can be migrated to scenarios such as supplier admission, customer due diligence, contract risk preliminary review, lead quality scoring, public opinion tracking, and preliminary screening of investment and financing targets. As long as there are several requirements in the business such as "data needs to be continuously completed", "AI results need to be left behind" and "historical versions cannot be overwritten", a runnable, trackable and scalable automated process can be built in a similar way.
|
|
119
|
+
|
|
120
|
+
## Reference documentation
|
|
121
|
+
|
|
122
|
+
- [NocoBase Workflow](/workflow/)
|
|
123
|
+
- [NocoBase AI Employee](/ai-employees/)
|
|
124
|
+
- [NocoBase Workflow AI Employee Node ](/ai-employees/workflow/nodes/employee/configuration)
|
|
125
|
+
- [NocoBase AI employee customization tool ](/ai-employees/features/tools)
|
|
@@ -0,0 +1,513 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Use NocoBase to build a linkable operational dashboard"
|
|
3
|
+
description: "Taking the work order operation dashboard as an example, the chart block, filter block and JS block are combined to achieve unified filtering, KPI, chart drill-down and custom styles."
|
|
4
|
+
keywords: "NocoBase, operational dashboard, data visualization, chart block, filter block, JS block, chart drill-down"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Use NocoBase to build a linkable operational dashboard
|
|
8
|
+
|
|
9
|
+
This article takes the operation dashboard of the "work order system" as an example to introduce how to use NocoBase's chart block, filter block and JS block in combination to build a data dashboard that supports filter linkage, chart drill-down and custom styles.
|
|
10
|
+
|
|
11
|
+
Although the examples are from work order scenarios, these methods are also applicable to business systems such as CRM, equipment operations, project management, approval flow, customer success, etc.
|
|
12
|
+
|
|
13
|
+
:::tip
|
|
14
|
+
What this article wants to introduce is not "how to use JS blocks to write a large screen", but how to combine NocoBase's native block capabilities and JS blocks: Let the native blocks be responsible for standard capabilities, and let the JS blocks complement the personalized experience.
|
|
15
|
+
:::
|
|
16
|
+
|
|
17
|
+

|
|
18
|
+
|
|
19
|
+
## scene target
|
|
20
|
+
|
|
21
|
+
We hope to build an Operations dashboard to help the operation or service team quickly determine the current workload:
|
|
22
|
+
|
|
23
|
+
- How many open work orders are there currently?
|
|
24
|
+
- Which work orders are at SLA risk?
|
|
25
|
+
- What is the trend in new work orders?
|
|
26
|
+
- What is the status and priority distribution of work orders?
|
|
27
|
+
- After clicking on a chart, you can view the corresponding details
|
|
28
|
+
|
|
29
|
+
The page can be roughly divided into four layers:
|
|
30
|
+
|
|
31
|
+
1. Top filter area: time, service group, request type, priority, SLA status
|
|
32
|
+
2. KPI statistics area: Open backlog, Unassigned, SLA warning, etc.
|
|
33
|
+
3. Chart analysis area: trend, status, SLA, priority distribution
|
|
34
|
+
4. Drill-down detail area: Click on the chart to display matching records
|
|
35
|
+
|
|
36
|
+
## First, clarify a construction idea
|
|
37
|
+
|
|
38
|
+
When many people make data dashboards, they tend to think of the problem as one of two options:
|
|
39
|
+
|
|
40
|
+
Either use all NocoBase's native blocks, which are simple to configure, but worry that the style and interaction are not flexible enough; or simply write a large JS block and control the query, chart, filtering, and drill-down by yourself, but this will lose the convenience brought by low-code configuration.
|
|
41
|
+
|
|
42
|
+
In fact, the more recommended way is to combine the two.
|
|
43
|
+
|
|
44
|
+
In this Operations dashboard, we did not write the entire page as a large JS screen, but split it according to responsibilities:
|
|
45
|
+
|
|
46
|
+
- Top filtering uses the filtering block that comes with the NocoBase system;
|
|
47
|
+
- Trend charts, status distribution, and SLA distribution use native chart blocks;
|
|
48
|
+
- KPI cards and drill-down details use JS blocks;
|
|
49
|
+
- Filter blocks affect both chart blocks and JS blocks;
|
|
50
|
+
- After the chart is clicked, the drill-down conditions are passed to the JS detail block below.
|
|
51
|
+
|
|
52
|
+
The advantage of this is that standard statistics and filtering still retain the configuration capabilities of NocoBase, while personalized display and complex interactions are completed by JS blocks. The page is neither "configurable only" nor "all code", but configuration and code each perform their own duties.
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## 1. How to customize the style of the chart block
|
|
57
|
+
|
|
58
|
+

|
|
59
|
+
|
|
60
|
+
The chart block of NocoBase can first use Query builder to define the statistical caliber, and then use the custom ECharts option to adjust the style.
|
|
61
|
+
|
|
62
|
+
Taking "work order status statistics" as an example, Query builder can be configured as:
|
|
63
|
+
|
|
64
|
+
- Datasheet: tickets
|
|
65
|
+
- Metrics: id count, alias ticketCount
|
|
66
|
+
- Dimensions: status
|
|
67
|
+
|
|
68
|
+
The key is that when customizing the style, you do not need to rewrite the query, you only need to process the chart display based on `ctx.data.objects`.
|
|
69
|
+
|
|
70
|
+
```javascript
|
|
71
|
+
const rows = Array.isArray(ctx.data?.objects) ? ctx.data.objects : [];
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
This line of code reads the chart query results. Then define status labels and colors:
|
|
75
|
+
|
|
76
|
+
```javascript
|
|
77
|
+
const labels = {
|
|
78
|
+
new: ctx.t('New'),
|
|
79
|
+
open: ctx.t('Open'),
|
|
80
|
+
pending_customer: ctx.t('Pending customer'),
|
|
81
|
+
resolved: ctx.t('Resolved'),
|
|
82
|
+
closed: ctx.t('Closed'),
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const colors = {
|
|
86
|
+
new: '#1677ff',
|
|
87
|
+
open: '#22a06b',
|
|
88
|
+
pending_customer: '#f59f00',
|
|
89
|
+
resolved: '#13c2c2',
|
|
90
|
+
closed: '#8c8c8c',
|
|
91
|
+
};
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
It is recommended that all visible copywriting use `ctx.t()` to facilitate subsequent multi-language support.
|
|
95
|
+
|
|
96
|
+
When generating chart data, you can attach drill-down information to each chart data point:
|
|
97
|
+
|
|
98
|
+
```javascript
|
|
99
|
+
const data = rows.map((row) => ({
|
|
100
|
+
value: Number(row.ticketCount || 0),
|
|
101
|
+
itemStyle: {
|
|
102
|
+
color: colors[row.status] || '#8c8c8c',
|
|
103
|
+
borderRadius: [6, 6, 0, 0],
|
|
104
|
+
},
|
|
105
|
+
ticketingDrilldown: {
|
|
106
|
+
label: ctx.t('Status') + ': ' + (labels[row.status] || row.status),
|
|
107
|
+
filter: { status: { $eq: row.status } },
|
|
108
|
+
},
|
|
109
|
+
}));
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
The most critical thing here is `ticketingDrilldown`. It is not a standard field of ECharts, but a business context that we put in ourselves, which will be used when clicking on the chart later.
|
|
113
|
+
|
|
114
|
+
Finally return to ECharts option:
|
|
115
|
+
|
|
116
|
+
```javascript
|
|
117
|
+
return {
|
|
118
|
+
grid: { top: 28, right: 22, bottom: 48, left: 42 },
|
|
119
|
+
tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } },
|
|
120
|
+
xAxis: {
|
|
121
|
+
type: 'category',
|
|
122
|
+
data: rows.map((row) => labels[row.status] || row.status),
|
|
123
|
+
},
|
|
124
|
+
yAxis: {
|
|
125
|
+
type: 'value',
|
|
126
|
+
minInterval: 1,
|
|
127
|
+
},
|
|
128
|
+
series: [
|
|
129
|
+
{
|
|
130
|
+
name: ctx.t('Tickets'),
|
|
131
|
+
type: 'bar',
|
|
132
|
+
barWidth: 36,
|
|
133
|
+
data,
|
|
134
|
+
},
|
|
135
|
+
],
|
|
136
|
+
};
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
The core idea of this part is:
|
|
140
|
+
|
|
141
|
+
- Query builder is responsible for statistics;
|
|
142
|
+
- Custom option is responsible for visual expression;
|
|
143
|
+
- Custom fields are responsible for carrying drill-down context.
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## 2. Let the system filter block become the observation scope of the entire page
|
|
148
|
+
|
|
149
|
+

|
|
150
|
+
|
|
151
|
+
The filter area in the operational dashboard should not be just an isolated form. It represents the current observation diameter of the entire page.
|
|
152
|
+
|
|
153
|
+
For example, if the user selects a service group, a request type, and a creation time, then KPIs, trend charts, status distribution, and drill-down details should all be displayed based on the same set of conditions. Otherwise, the numbers in different blocks on the page will fight with each other, and it will be difficult for users to judge which data is the result within the current range.
|
|
154
|
+
|
|
155
|
+
Here we directly use the filtering block that comes with the NocoBase system instead of writing a filtering component ourselves. Native filter blocks can be naturally bound to chart blocks, allowing the chart block to continue to use the Query builder, permissions, refresh and filter mechanisms.
|
|
156
|
+
|
|
157
|
+
Top `Dashboard scope` can configure these filter items:
|
|
158
|
+
|
|
159
|
+
- Created at
|
|
160
|
+
- Service group
|
|
161
|
+
- Request type
|
|
162
|
+
- Priority
|
|
163
|
+
- SLA status
|
|
164
|
+
|
|
165
|
+
For JS blocks, you only need to read the same set of filter conditions in the code and then convert them into query filters. In this way, KPIs and drill-down details can also be consistent with the native chart.
|
|
166
|
+
|
|
167
|
+
The combination of filter conditions can be encapsulated into a small function:
|
|
168
|
+
|
|
169
|
+
```javascript
|
|
170
|
+
function combineFilters(...filters) {
|
|
171
|
+
const parts = filters.filter(Boolean);
|
|
172
|
+
if (!parts.length) return undefined;
|
|
173
|
+
if (parts.length === 1) return parts[0];
|
|
174
|
+
return { $and: parts };
|
|
175
|
+
}
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
Count by filter:
|
|
179
|
+
|
|
180
|
+
```javascript
|
|
181
|
+
async function countTickets(filter) {
|
|
182
|
+
const resource = ctx.makeResource('MultiRecordResource');
|
|
183
|
+
resource.setResourceName('tickets');
|
|
184
|
+
resource.setPageSize(1);
|
|
185
|
+
|
|
186
|
+
if (filter) {
|
|
187
|
+
resource.setFilter(filter);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
await resource.refresh();
|
|
191
|
+
|
|
192
|
+
const meta = resource.getMeta?.() || {};
|
|
193
|
+
return Number(meta.count || meta.total || 0);
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
The key points here are:
|
|
198
|
+
|
|
199
|
+
```javascript
|
|
200
|
+
resource.setFilter(filter);
|
|
201
|
+
await resource.refresh();
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
The JS block queries business data through resources instead of writing SQL directly. This makes it easier to keep consistent with NocoBase's permissions, data sources, and page runtimes.
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
## 3. Use JS blocks to display KPI cards
|
|
209
|
+
|
|
210
|
+

|
|
211
|
+
|
|
212
|
+
KPIs are better suited to use JS blocks. Because KPI is usually not a single query, but a combination of multiple business calibers: unfinished, unassigned, SLA warning, SLA breached, new, resolved, etc.
|
|
213
|
+
|
|
214
|
+
The JS block can requery data based on the current filtering range and render it into a statistical card.
|
|
215
|
+
|
|
216
|
+
```javascript
|
|
217
|
+
const { Card, Col, Row, Statistic, Tag } = ctx.libs.antd;
|
|
218
|
+
|
|
219
|
+
const scopeFilter = getDashboardScopeFilter();
|
|
220
|
+
|
|
221
|
+
const openBacklog = await countTickets(
|
|
222
|
+
combineFilters(scopeFilter, {
|
|
223
|
+
status: { $notIn: ['resolved', 'closed', 'cancelled'] },
|
|
224
|
+
}),
|
|
225
|
+
);
|
|
226
|
+
|
|
227
|
+
ctx.render(
|
|
228
|
+
<Row gutter={[12, 12]}>
|
|
229
|
+
<Col span={6}>
|
|
230
|
+
<Card size="small">
|
|
231
|
+
<Tag color="blue">{ctx.t('Active')}</Tag>
|
|
232
|
+
<Statistic title={ctx.t('Open backlog')} value={openBacklog} />
|
|
233
|
+
</Card>
|
|
234
|
+
</Col>
|
|
235
|
+
</Row>,
|
|
236
|
+
);
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
The key points of JS blocks are:
|
|
240
|
+
|
|
241
|
+
- Use `ctx.makeResource()` to query data;
|
|
242
|
+
- Use `ctx.libs.antd` to render the interface;
|
|
243
|
+
- Use `ctx.render()` to output content;
|
|
244
|
+
- Re-render JS chunks after filtering changes.
|
|
245
|
+
|
|
246
|
+
In a real page, the filter button and reset button can configure the event flow so that they refresh the KPI JS block and drill-down JS block at the same time after completing the native filter action. In this way, the user clicks once to filter, and both charts and custom content will be updated based on the same range.
|
|
247
|
+
|
|
248
|
+
---
|
|
249
|
+
|
|
250
|
+
## 4. Chart linkage JS block for drill-down
|
|
251
|
+
|
|
252
|
+

|
|
253
|
+
|
|
254
|
+
Clicking on the chart to drill down is a very practical interaction in the dashboard.
|
|
255
|
+
|
|
256
|
+
In the work order scenario, the user clicks the "Status: Open" column, and all Open work orders are displayed in the detail area below; when the user clicks "SLA breached", all overtime work orders are displayed below.
|
|
257
|
+
|
|
258
|
+
The implementation idea is:
|
|
259
|
+
|
|
260
|
+
1. Chart data points carry `ticketingDrilldown`;
|
|
261
|
+
2. The chart event reads this drill-down information;
|
|
262
|
+
3. Write drill-down information into the target JS block context;
|
|
263
|
+
4. Trigger the target JS block to re-render.
|
|
264
|
+
|
|
265
|
+
The key code in the chart event is as follows. First find the drill-down JS block:
|
|
266
|
+
|
|
267
|
+
```javascript
|
|
268
|
+
const DRILLDOWN_TARGET_UID = 'v7mioopm6rm';
|
|
269
|
+
|
|
270
|
+
function getDrilldownTarget() {
|
|
271
|
+
if (typeof ctx.getModel === 'function') {
|
|
272
|
+
return ctx.getModel(DRILLDOWN_TARGET_UID);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const engine =
|
|
276
|
+
ctx.model?.flowEngine || ctx.model?.context?.flowEngine || ctx.engine;
|
|
277
|
+
return engine?.getModel?.(DRILLDOWN_TARGET_UID);
|
|
278
|
+
}
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
Then write the drill-down conditions obtained by clicking on the chart into the target block:
|
|
282
|
+
|
|
283
|
+
```javascript
|
|
284
|
+
function applyDrilldown(drilldown) {
|
|
285
|
+
if (!drilldown?.filter) return;
|
|
286
|
+
|
|
287
|
+
const target = getDrilldownTarget();
|
|
288
|
+
if (!target?.context?.defineProperty) return;
|
|
289
|
+
|
|
290
|
+
target.context.defineProperty('ticketingDashboardDrilldown', {
|
|
291
|
+
value: drilldown,
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
target.rerender?.();
|
|
295
|
+
}
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
The most critical are these two lines:
|
|
299
|
+
|
|
300
|
+
```javascript
|
|
301
|
+
target.context.defineProperty('ticketingDashboardDrilldown', {
|
|
302
|
+
value: drilldown,
|
|
303
|
+
});
|
|
304
|
+
target.rerender?.();
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
The first line passes the drill-down condition to the JS block, and the second line triggers the JS block refresh.
|
|
308
|
+
|
|
309
|
+
Finally bind the chart click event:
|
|
310
|
+
|
|
311
|
+
```javascript
|
|
312
|
+
const clickHandler = (params) => {
|
|
313
|
+
applyDrilldown(params?.data?.ticketingDrilldown);
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
chart.on('click', clickHandler);
|
|
317
|
+
|
|
318
|
+
return () => chart.off('click', clickHandler);
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
It is recommended here that you must return cleanup:
|
|
322
|
+
|
|
323
|
+
```javascript
|
|
324
|
+
return () => chart.off('click', clickHandler);
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
In this way, when the chart is reconfigured or re-rendered, old events can be cleaned up to avoid repeated binding.
|
|
328
|
+
|
|
329
|
+
The above click event-related code is applicable to [v2.2.0-beta.10](https://github.com/nocobase/nocobase/releases/tag/v2.2.0-beta.10) and above versions. Reference to the old version code:
|
|
330
|
+
|
|
331
|
+
```javascript
|
|
332
|
+
chart.off('click');
|
|
333
|
+
chart.on('click', clickHandler);
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
---
|
|
337
|
+
|
|
338
|
+
## 5. How to display details in drill-down JS blocks
|
|
339
|
+
|
|
340
|
+

|
|
341
|
+
|
|
342
|
+
Drill down into the JS block to read the `ticketingDashboardDrilldown` just written, and then query the data according to the filter in it.
|
|
343
|
+
|
|
344
|
+
```javascript
|
|
345
|
+
const drilldown = ctx.model?.context?.ticketingDashboardDrilldown;
|
|
346
|
+
|
|
347
|
+
if (!drilldown) {
|
|
348
|
+
ctx.render(
|
|
349
|
+
<Alert
|
|
350
|
+
type="info"
|
|
351
|
+
showIcon
|
|
352
|
+
message={ctx.t('Select a chart segment to inspect matching tickets')}
|
|
353
|
+
/>,
|
|
354
|
+
);
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
If the user has not clicked on the chart, display a prompt. After clicking, query the work order based on `drilldown.filter`:
|
|
360
|
+
|
|
361
|
+
```javascript
|
|
362
|
+
const resource = ctx.makeResource('MultiRecordResource');
|
|
363
|
+
resource.setResourceName('tickets');
|
|
364
|
+
resource.setFilter(drilldown.filter);
|
|
365
|
+
resource.setPageSize(10);
|
|
366
|
+
await resource.refresh();
|
|
367
|
+
|
|
368
|
+
const rows = resource.getData?.() || [];
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
Then render the table:
|
|
372
|
+
|
|
373
|
+
```javascript
|
|
374
|
+
const { Table, Typography } = ctx.libs.antd;
|
|
375
|
+
|
|
376
|
+
ctx.render(
|
|
377
|
+
<>
|
|
378
|
+
<Typography.Title level={5}>
|
|
379
|
+
{ctx.t('Drilldown')}: {drilldown.label}
|
|
380
|
+
</Typography.Title>
|
|
381
|
+
|
|
382
|
+
<Table
|
|
383
|
+
size="small"
|
|
384
|
+
rowKey="id"
|
|
385
|
+
dataSource={rows}
|
|
386
|
+
pagination={false}
|
|
387
|
+
columns={[
|
|
388
|
+
{ title: ctx.t('Ticket No'), dataIndex: 'ticketNo' },
|
|
389
|
+
{ title: ctx.t('Title'), dataIndex: 'title' },
|
|
390
|
+
{ title: ctx.t('Status'), dataIndex: 'status' },
|
|
391
|
+
{ title: ctx.t('Priority'), dataIndex: 'priority' },
|
|
392
|
+
]}
|
|
393
|
+
/>
|
|
394
|
+
</>,
|
|
395
|
+
);
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
If you need to clear drill-down conditions, you can refer to
|
|
399
|
+
|
|
400
|
+
```javascript
|
|
401
|
+
function clearChartDrilldown() {
|
|
402
|
+
if (ctx.model?.context?.defineProperty) {
|
|
403
|
+
ctx.model.context.defineProperty('ticketingDashboardDrilldown', {
|
|
404
|
+
value: null,
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
if (typeof ctx.model?.rerender === 'function') {
|
|
408
|
+
ctx.model.rerender();
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
The key points in this part are:
|
|
414
|
+
|
|
415
|
+
- The chart is only responsible for passing the filter;
|
|
416
|
+
- The JS block is responsible for querying and displaying details;
|
|
417
|
+
- Click on different charts to share the same drill-down block.
|
|
418
|
+
|
|
419
|
+
---
|
|
420
|
+
|
|
421
|
+
## Practical suggestions
|
|
422
|
+
|
|
423
|
+
### 1. Don’t rush to code the complex page as a whole
|
|
424
|
+
|
|
425
|
+
The most important lesson from this page is: don’t pit native capabilities against JS capabilities.
|
|
426
|
+
|
|
427
|
+
If a capability is already a native capability of NocoBase, such as filtering, chart query, table display, and permission control, the native block will be used first. In this way, when fields, filter conditions, and chart caliber are subsequently adjusted, they can still be configured on the interface.
|
|
428
|
+
|
|
429
|
+
JS blocks are more suitable for processing parts that native blocks are not good at, such as combining multiple indicators into a set of KPIs, special card styles, displaying a set of custom details after clicking on the chart, or passing business context between different blocks.
|
|
430
|
+
|
|
431
|
+
In other words, the native block is responsible for "configurable standard capabilities", and the JS block is responsible for "business-oriented personalized experience". This is also the most reusable construction idea for this dashboard.
|
|
432
|
+
|
|
433
|
+
### 2. For simple statistics, use the chart block Query builder first.
|
|
434
|
+
|
|
435
|
+
This preserves NocoBase's standard query, permissions, filtering and refreshing capabilities. Only when the default chart style cannot express the business focus, use the customized ECharts option for visual optimization.
|
|
436
|
+
|
|
437
|
+
### 3. KPI cards give priority to using JS blocks
|
|
438
|
+
|
|
439
|
+
KPIs often require multiple queries, condition combinations, and custom layouts, and JS blocks are more flexible. Especially when KPIs need to respond to the same set of system filter conditions, it will be clearer to use JS blocks to handle them uniformly.
|
|
440
|
+
|
|
441
|
+
### 4. Chart events should return cleanup
|
|
442
|
+
|
|
443
|
+
Recommended writing method:
|
|
444
|
+
|
|
445
|
+
```javascript
|
|
446
|
+
const handler = (params) => {
|
|
447
|
+
// handle click
|
|
448
|
+
};
|
|
449
|
+
|
|
450
|
+
chart.on('click', handler);
|
|
451
|
+
|
|
452
|
+
return () => chart.off('click', handler);
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
Do not directly use `chart.off('click')` to clear all click events, as this may accidentally delete the chart block or configure the panel's own monitoring.
|
|
456
|
+
|
|
457
|
+
---
|
|
458
|
+
|
|
459
|
+
## Let AI help you build it
|
|
460
|
+
|
|
461
|
+
This type of dashboard is very suitable for AI-assisted generation because it involves data models, statistical calibers, chart styles, and page interactions at the same time. You can hand it the content of this article and ask questions using the prompt words below.
|
|
462
|
+
|
|
463
|
+
You can ask questions like this:
|
|
464
|
+
|
|
465
|
+
```markdown
|
|
466
|
+
I am using NocoBase to build an operational dashboard for a work order system.
|
|
467
|
+
Please take the work order scenario as an example and help me design an Operations dashboard.
|
|
468
|
+
|
|
469
|
+
The data table tickets contains:
|
|
470
|
+
ticketNo、title、status、priority、slaStatus、
|
|
471
|
+
requestType、serviceGroup、assignee、createdAt、updatedAt。
|
|
472
|
+
|
|
473
|
+
The page requires:
|
|
474
|
+
|
|
475
|
+
1. Top filter: Created at, Service group, Request type, Priority, SLA status.
|
|
476
|
+
2. KPI cards: Open backlog, Unassigned, SLA warning, SLA breached, New tickets, Resolved tickets.
|
|
477
|
+
3. Chart: Created tickets trend, Ticket status, SLA status, Priority mix.
|
|
478
|
+
4. After clicking on the chart, the JS block below displays the matching Ticket drilldown table.
|
|
479
|
+
5. The chart style should be suitable for the operating market, with clear colors and compact layout.
|
|
480
|
+
6. Use ctx.t() for all JS copy.
|
|
481
|
+
7. Chart events use chart.on and return the cleanup function.
|
|
482
|
+
8. Prioritize using NocoBase’s native filter blocks and chart blocks. Only use JS blocks for KPIs, drill-down details, special styles, and cross-block interactions. Do not write the entire page as one large JS block.
|
|
483
|
+
|
|
484
|
+
Please give the configuration ideas for each block and mark the key JS code.
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
If you already have a page, you can also let AI help you optimize it:
|
|
488
|
+
|
|
489
|
+
```markdown
|
|
490
|
+
This is my current NocoBase dashboard design:
|
|
491
|
+
At the top is the filter area, in the middle are 4 charts, and below is the drill-down JS block.
|
|
492
|
+
Please help me optimize from the perspective of operator experience:
|
|
493
|
+
|
|
494
|
+
1. What indicators should the KPI display?
|
|
495
|
+
2. Whether there is a need for linkage between charts;
|
|
496
|
+
3. Which columns should be displayed in drill-down details;
|
|
497
|
+
4. How should JS block and chart events be organized;
|
|
498
|
+
5. Which code should be placed in the chart custom option and which should be placed in the JS block.
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
In this way, the content generated by AI will be closer to the real business, rather than just giving isolated code.
|
|
502
|
+
|
|
503
|
+
:::warning
|
|
504
|
+
If you choose to let AI help you build it, please use the backup manager to back up the project before starting.
|
|
505
|
+
:::
|
|
506
|
+
|
|
507
|
+
## Reference documentation
|
|
508
|
+
|
|
509
|
+
- [Chart configuration ](/data-visualization/guide/chart-options)
|
|
510
|
+
- [Frontend RunJS](/runjs/)
|
|
511
|
+
- [Filter form ](/interface-builder/blocks/filter-blocks/form)
|
|
512
|
+
- [AI Construction - Interface Construction ](/ai-builder/ui-builder)
|
|
513
|
+
- [ECharts Options](https://echarts.apache.org/en/option.html)
|