门户页面

Frappe 框架不仅通过 Desk 提供了一个功能丰富的管理界面(这是一个单页应用),还提供了静态的服务器渲染网页。这些页面通常是为您的网站访客构建的。它们可以是公开的,也可以需要登录才能访问。

添加页面

每个 frappe 应用(包括 frappe 本身)都带有一个 www 文件夹,它直接映射到网站 URL。以下是目录结构的样子:

frappe/www
├── about.html
├── about.py
├── contact.html
├── contact.py
├── desk.html
├── desk.py
├── login.html
├── login.py
├── me.html
└── me.py

这个结构启用了以下路由:/about、/contact、/desk、/login 和 /me。

要添加您自己的页面,只需在您应用的 www 文件夹中添加一个 HTML 文件。有多种方式可以组织这些门户页面。例如,

custom_app/www
├── custom_page.html
└── custom_page.py

将在路由 /custom_page 上渲染。

要添加子页面,您可以将主页面转换为文件夹,并将其内容添加到 index 文件中。例如,

custom_app/www
└── custom_page
 ├── index.html
 ├── index.py
 ├── subpage.html
 └── subpage.py

仍将在路由 /custom_page 上渲染,同时 /custom_page/subpage 也将可用。

对于像文档这样的简单静态页面,您可以编写 .md 文件来代替 .html。您正在阅读的这份文档就是以 markdown 文件的形式编写的。

覆盖标准页面

Frappe 还允许您通过自定义应用覆盖标准页面。例如,要覆盖 frappe 提供的标准 /about,只需在您应用的 www 文件夹中添加一个名为 about.html 的文件,它就会优先生效。

模板

您可以使用 Jinja 模板为门户页面添加动态内容。所有门户页面都继承自基础模板 frappe/templates/web.html,而该模板本身又继承自 frappe/templates/base.html。

以下是一个示例页面的样子:


{% extends "templates/web.html" %}

{% block title %}{{ _("About Us") }}{% endblock %}

{% block page_content %}
{{ _("About Us") }}
====================

 We believe that great companies are driven by excellence,
 and add value to both its customers and society.
 You will find our team embibes these values.

{% endblock %}

如果您想使用默认的基础模板,也可以省略 extend 和 block。


{{ _("About Us") }}
====================

 We believe that great companies are driven by excellence,
 and add value to both its customers and society.
 You will find our team embibes these values.

上下文

每个门户页面都可以有一个 Python 控制器,用于为该页面构建 context。控制器应与 .html 或 .md 文件同名,但扩展名为 .py。

custom_app/www
├── custom_page.html
└── custom_page.py

控制器应有一个 get_context 方法,它接收一个 context 字典,向其中添加数据,然后返回它。以下是一个示例页面控制器的样子:

# about.py
import frappe

def get_context(context):
 context.about_us_settings = frappe.get_doc('About Us Settings')
 return context

在模板中的用法


{{ _("About Us") }}
====================

 We believe that great companies are driven by excellence,
 and add value to both its customers and society.
 You will find our team embibes these values.

 {% if about_us_settings.show_contact_us %}
 [Contact Us](/contact)
 {% endif %}

由于门户页面是使用 Jinja 构建的,frappe 提供了一个标准 API 供在 Jinja 模板中使用。

标准上下文键列表

以下是可以设置在 context 中的所有标准键及其功能的列表。

上下文键 功能
add_breadcrumbs 向页面添加面包屑导航
no_breadcrumbs 从页面移除面包屑导航
show_sidebar 显示网页侧边栏
safe_render 切换 safe_render
no_header 隐藏页头
no_cache 禁用此页面的缓存
sitemap 在站点地图中包含/排除页面
add_next_prev_links 添加下一页和上一页导航按钮
title 设置页面标题
safe_render

frappe.render_template 不会渲染包含字符串 .__ 的模板,以防止运行任何非法的 Python 表达式。如果您确信内容是安全的,可以禁用此行为。为此,您需要在上下文中将 safe_render 键的值设置为 False 来关闭安全渲染。

通过 frontmatter 设置上下文

您还可以使用 frontmatter 块在上下文中设置值。Frontmatter 块可用于设置特定于页面的静态值,例如元标签。

请看以下示例:

---
title: Introduction
metatags:
 description: This is description for the introduction page
---

# Introduction
This is an introduction page

上述 frontmatter 块将使用以下值更新 context 字典:

{
 'title': 'Introduction',
 'metatags': {
 'description': 'This is description for the introduction page'
 }
}

通过注释设置上下文

您还可以通过在页面中添加 HTML 注释来在上下文中设置一些值。

例如,在您的 .html 或 .md 文件中添加 “,context.add_breadcrumbs 将被设置为 True,并且它将根据文件夹结构自动生成面包屑导航。


{{ _("About Us") }}
====================

 We believe that great companies are driven by excellence,
 and add value to both its customers and society.
 You will find our team embibes these values.

 {% if about_us_settings.show_contact_us %}
 [Contact Us](/contact)
 {% endif %}

以下是您可以设置的键及其上下文值的列表:

注释 上下文值
“ | add_breadcrumbs = 1
“ | no_breadcrumbs = 1
“ | show_sidebar = 1
“ | no_header = 1
“ | no_cache = 1
“ | sitemap = 0
“ | sitemap = 1
“ | add_next_prev_links = 1
“ | title = 'Custom Title'
“ | base_template = 'custom_app/path/to/custom_base.html'

自定义 CSS 和 JS

你可以通过放置一个同名的 .css 或 .js 文件
来为你的页面添加自定义 CSS 和 JS。

custom_app/www
├── custom_page.html
├── custom_page.css
├── custom_page.js
└── custom_page.py

主页

你的门户主页可以在以下位置定义

  1. 角色
  2. 门户设置(这将适用于已登录用户)
  3. 通过 Hook get_website_user_home_page
  4. 网站设置(这也将适用于未登录用户——即访客)

讨论

现在,您可以在门户的任何网页上添加讨论组件。讨论组件允许已登录的用户输入消息并与其他用户分享。该组件可用于讨论,或用于搭建一个用户互相帮助解答问题的门户。

设置讨论组件的步骤

  1. 打开您想要添加讨论组件的网页文档,或者新建一个网页并保存。
  2. 滚动到“页面构建块”表格。
  3. 在“网页模板”下选择“讨论”。
  4. 点击“编辑值”。
  5. 输入标题、CTA 标题,并将“网页”选择为当前网页名称。
  6. 如果您希望讨论为单线程模式,可以勾选“单线程”。
  7. 保存页面。

Excel (XLSX) 导出样式

版本 16 中引入

get_xlsx_styles 模块方法允许你为报表导出定义自定义 Excel 样式。

注意: 此方法仅在将标准脚本报表或查询报表导出为 Excel 格式(非 CSV)时被调用。

基本用法

在报表的 Python 文件中定义一个 get_xlsx_styles 函数:

from frappe.utils.xlsxutils import XLSXMetadata, XLSXStyleBuilder

def get_xlsx_styles(metadata: XLSXMetadata) -> dict:
    builder = XLSXStyleBuilder(metadata)

    # Add your custom styles here...
    return builder.result

注意: 当 XLSXStyleBuilder 初始化时,默认样式(表头、筛选器、字段类型格式、货币)会自动应用。

要禁用此功能,请传入 default_styling=False:

builder = XLSXStyleBuilder(metadata, default_styling=False)

XLSXMetadata

metadata 对象提供对报表数据和辅助方法的访问。

属性

属性 类型 描述
report_name str 报表名称
filters dict 原始筛选值
column_map dict[int, dict] 列索引 → 列定义
row_map dict[int, dict \| list] 行索引 → 行数据
applied_filters_map dict[int, list] 筛选行索引 → [标签, 值]
has_total_row bool 最后一行是否为合计行
has_indentation bool 是否应用缩进样式

注意: 所有索引必须从 0 开始,遵循 XlsxWriter 的索引规则。

XLSXStyleBuilder

该构建器提供注册和应用样式的方法。

注册样式

# Register a style and get its ID
style_id = builder.register_style({"bold": True, "bg_color": "#FFFF00"})

应用样式

# Style an entire column
builder.style_column(col_idx, style_id)

# Style an entire row
builder.style_row(row_idx, style_id)

# Style a specific cell
builder.style_cell(row_idx, col_idx, style_id)

样式属性

字体样式

builder.register_style({
    "bold": True,
    "italic": True,
    "underline": True,
    "font_size": 12,
    "font_color": "#FF0000",
    "font_name": "Arial",
})

背景颜色

builder.register_style({
    "bg_color": "#FFFF00",
})

边框

# Border thickness: 1 = thin, 2 = medium, 3 = thick
builder.register_style({
    "border": 1,
    "border_color": "#000000",
})

# Individual borders
builder.register_style({
    "left": 2,
    "right": 2,
    "top": 1,
    "bottom": 3,
})

对齐方式

builder.register_style({
    "align": "center",           # left, center, right
    "valign": "vcenter",         # top, vcenter, bottom
    "text_wrap": True,
    "indent": 2,

})

数字格式

builder.register_style({
    "num_format": "#,##0.00",    # Custom number format

})

完整示例

from frappe.utils.xlsxutils import XLSXMetadata, XLSXStyleBuilder

def get_xlsx_styles(metadata: XLSXMetadata) -> dict:
    builder = XLSXStyleBuilder(metadata)

    # --- Register Styles (do this once, reuse style_id) --- #
    success_style = builder.register_style({"font_color": "#006100", "bg_color": "#C6EFCE"})
    warning_style = builder.register_style({"font_color": "#9C5700", "bg_color": "#FFEB9C"})
    error_style = builder.register_style({"font_color": "#9C0006", "bg_color": "#FFC7CE"})
    highlight_style = builder.register_style({"bold": True, "bg_color": "#DDEBF7"})
    total_row_border = builder.register_style({"border": 3, "border_color": "#15137e"})
    right_align = builder.register_style({"align": "right"})

    # --- Style entire column --- #
    amount_col_idx = builder.field_index_map.get("net_amount")

    if amount_col_idx is not None:
        builder.style_column(amount_col_idx, right_align)

    # --- Style total row with border --- #
    if metadata.has_total_row:
        builder.style_row(metadata.get_last_row_index(), total_row_border)

    # --- Conditional Cell Styling --- #
    status_col_idx = builder.field_index_map.get("status")

    for row_idx, row_data in metadata.row_map.items():
        if not isinstance(row_data, dict):
            continue

        # Status-based styling
        if status_col_idx is not None:
            status = row_data.get("status")

            if status == "Completed":
                builder.style_cell(row_idx, status_col_idx, success_style)

            elif status == "Pending":
                builder.style_cell(row_idx, status_col_idx, warning_style)

            elif status == "Cancelled":
                builder.style_cell(row_idx, status_col_idx, error_style)

        # High value highlighting
        if amount_col_idx is not None:
            amount = row_data.get("net_amount")

            if amount and amount > 10000:
                builder.style_cell(row_idx, amount_col_idx, highlight_style)

    return builder.result

示例输出


带样式的导出示例

建议

  • 注册一次样式并重复使用 — 在开始时注册样式,并在整个过程中使用返回的 style_id,以避免创建重复样式。

参考

  • 有关所有可用的样式属性,请访问 xlsxwriter 格式文档。

报表生成器

报表生成器是最简单的报表类型,无需编写任何代码即可创建。
它只能显示单个 DocType 的记录以及子表的子记录。

保存报表

默认情况下,所有 DocType 都会生成一个报表视图。用户还可以根据不同的筛选条件、排序方式、列等组合保存多个报表。


报表生成器

更新或删除自定义报表

用户使用报表生成器创建的任何自定义报表,都可以在报表视图中进行更新或删除。


更新或删除自定义报表

打印

报表可以通过菜单进行打印。


报表生成器打印

分组

你还可以对列应用分组条件,并使用计数、求和、平均值等聚合函数。


报表生成器分组

键盘导航与编辑

由于报表生成器是单个 DocType 的视图,因此也可以进行编辑。


报表生成器编辑

查询报表

查询报表是可以通过单条 SQL 查询生成的报表。
只要查询能生成列和记录,查询可以简单也可以复杂。
这些报表只能由系统管理员创建,并存储在数据库中。

要创建查询报表,请在快捷搜索栏中输入“新建报表”并按回车。

  1. 将报表类型设置为“查询报表”
  2. 设置参考文档类型 – 有权访问该参考文档类型的用户将有权访问该报表
  3. 设置模块 – 报表将显示在该模块的“自定义报表”部分。
  4. 编写你的查询

如果你将“标准”设置为“是”并且启用了开发者模式,那么将生成一个 JSON 文件,
你需要将其签入到你的版本控制中。只有当你想要将查询报表与你的应用一起打包时,
才应该这样做。模块将决定 JSON 文件存放的位置。

列和筛选器

在版本 13 中添加

你可以在报表文档中配置列和筛选器。在这里,你可以为列和筛选器设置标签、宽度、格式(字段类型)。

筛选器可以在查询中用作格式化变量。例如,类型为 customer 的筛选器可以在查询中用作 %(customer)s。

示例

SELECT
 name, creation, production_item, qty, produced_qty, company
FROM
 `tabWork Order`
WHERE
 docstatus=1
 AND ifnull(produced_qty,0) < qty

格式化列(旧式)

或者,也可以通过以特定格式指定列的标签来格式化列:{label}:{optional fieldtype}{optional /}{optional options}:{optional width}

如果你已经在报表本身中配置了字段和列,则无需使用这种样式。

示例(旧式)

下面是一个查询可能的样子:

SELECT
 `tabWork Order`.name as "Work Order:Link/Work Order:200",
 `tabWork Order`.creation as "Date:Date:120",
 `tabWork Order`.production_item as "Item:Link/Item:150",
 `tabWork Order`.qty as "To Produce:Int:100",
 `tabWork Order`.produced_qty as "Produced:Int:100",
 `tabWork Order`.company as "Company:Link/Company:"
FROM
 `tabWork Order`
WHERE
 `tabWork Order`.docstatus=1
 AND ifnull(`tabWork Order`.produced_qty,0) < `tabWork Order`.qty
 AND NOT EXISTS (SELECT name from `tabStock Entry` where work_order =`tabWork Order`.name)

如果你注意到每一列都有一种特殊语法,我们会使用这些信息来格式化报表视图。

例如:第一列 Work Order:Link/Work Order:200 将渲染为文档类型为“工单”的链接字段,列宽将为 200px。


查询报表视图

脚本报表

任何无法通过报表构建器或查询报表实现的功能,都可以通过脚本报表来实现。顾名思义,这些报表是用 Python 脚本构建的。由于这些报表通过 Python 脚本为您提供了不受限制的访问权限,因此只能由管理员创建。这些报表是在开发过程中编写的,并且是您应用的一部分。

要创建脚本报表,您必须启用开发者模式。

要创建脚本报表,请在快捷搜索栏中输入”new report”并按回车。

  1. 将报表类型设置为”脚本报表”
  2. 将”是否标准”设置为”是”
  3. 选择您要添加此报表的模块
  4. 在模块文件夹中(例如,如果是 ERPNext 中的 Accounts,文件夹
    将是 erpnext/accounts/report/[report-name]),您会看到模板
    或报表文件将被创建。
  5. 在生成的 {report-name}.py 文件中编写您的 Python 脚本。
  6. 您可以通过将筛选器添加到 {report-name}.js 来为报表添加筛选器。


脚本报表

标准报表和自定义报表

在版本 12 中添加

从版本 12 开始,您可以在 Frappe 框架中创建自定义查询报表和脚本报表。在自定义报表中,脚本可以直接添加到报表本身中,您可以使用 Frappe 框架的脚本 API 函数。

列和筛选器

在版本 13 中添加

您可以在报表文档中配置列和筛选器。在这里,您可以为列和筛选器设置标签、宽度、格式(字段类型)。

筛选器可以在查询中用作格式化变量。例如,类型为 customer 的筛选器可以在查询中用作 %(customer)s。

编写脚本

自定义报表

在自定义报表中,您可以使用脚本 API 并直接在代码部分编写脚本。

return frappe.db.get_all('User', ['first_name', 'last_name'], filters = filters)

标准报表

生成的 .py 文件附带了一个报表的样板代码。其中有一个名为 execute 的方法,它接收 filters 并返回 columns 和 data。您可以使用任何 Python 模块和 SQL 查询的组合来生成报表。execute 函数如下所示

from __future__ import unicode_literals
# import frappe

def execute(filters=None):
 columns, data = [], []
 return columns, data

execute 函数应返回默认在报表中显示的 columns 和 data。开发者可以选择性地返回一些参数,如 message、chart、report_summary、skip_total_rows。

以下是 execute 函数可以返回的参数

列

这是一个字典列表。它按顺序保存了要在数据表中显示的所有列。

注意:只有在报表中未指定列时,您才需要返回列

示例:

columns = [
 {
 'fieldname': 'account',
 'label': _('Account'),
 'fieldtype': 'Link',
 'options': 'Account'
 },
 {
 'fieldname': 'currency',
 'label': _('Currency'),
 'fieldtype': 'Link',
 'options': 'Currency'
 },
 {
 'fieldname': 'balance',
 'label': _('Balance'),
 'fieldtype': 'Currency',
 'options': 'currency'
 }
 ]

结果

这可以是一个列表的列表或字典的列表。它保存了要在报表中显示的数据

示例:

data = [
 {
 'account': 'Application of Funds (Assets)',
 'currency': 'INR',
 'balance': '15182212.738'
 },
 {
 'account': 'Current Assets - GTPL',
 'currency': 'INR',
 'balance': '17117932.738'
 },
 ...
 ]

图表

包含报表中默认显示的图表的配置。

报表摘要

这是一个字典列表,用于存储报表中的重要数值,并在界面顶部区域单独显示。

示例:

[{
 "value": profit,
 "indicator": "Green" if profit > 0 else "Red",
 "label": _("Total Profit This Year"),
 "datatype": "Currency",
 "currency": "INR"
}]

注意:这些参数需要按照以下特定顺序返回

以下是来自 ERPNext 的一个脚本报表:资产负债表

添加筛选器

要在报表中添加筛选器,请在
{report-name}.js 文件中定义字段及其字段类型。筛选器的值将在 execute
方法中以字典形式提供。

frappe.query_reports['Balance Sheet'] = {
 filters: [
 {
 fieldname: 'company',
 label: __('Company'),
 fieldtype: 'Link',
 options: 'Company',
 default: frappe.defaults.get_user_default('company')
 },
 {
 fieldname: 'periodicity',
 label: __('Periodicity'),
 fieldtype: 'Select',
 options: [
 'Monthly',
 'Quarterly',
 'Half-Yearly',
 'Yearly'
 ],
 default: 'Yearly',
 depends_on: 'eval:doc.company=="Gadget Technologies Pvt. Ltd."'
 }
 ]
}

类似于控制字段显示的 depends_on 属性,在
版本 13 中我们为脚本报表筛选器引入了 depends_on。它可以
用于根据 depends_on 中条件的值来确定筛选器是否可见。


资产负债表


资产负债表

专业提示:要直接导航到上述任意类型的报表,请在快捷搜索栏中输入其
名称并按回车。

快照报表

一种高级报表生成引擎。它需要手动重写报表以使用 OLAP 数据库。

  1. 在系统设置 -> 高级中启用”快照报表”。这会设置一个后台任务,将数据从 MariaDB 同步到 DuckDB
  2. 在报表主数据中勾选”快照报表”复选框
  3. 在报表模块中使用 execute_snapshot_report 方法重写报表。
def execute_snapshot_report(filters: dict | None = None):
    """Return columns and data for the report.

    This is the main entry point for snapshot report. When 'Synced
    Report' is enabled in report, framework will call this method 
    every time the report is refreshed or a filter is updated. It 
    accepts the same filters as normal execute. But a utility method - 
    get_latest_sync, is also imported.
    """

    from frappe.database.duckdb.database import get_latest_sync
    columns = get_columns()
    data = get_data()

    return columns, data

访问权限

工作区可以根据模块和角色进行限制。

模块

用户可以访问不同的模块,标准工作区会根据对这些模块的访问权限来显示。

在下面的 GIF 中可以看到,当用户 John Doe 的网站模块访问权限被移除后,他无法看到网站工作区。

角色

用户可以拥有模块的访问权限,但如果你仍然不希望这些用户看到该模块的标准工作区呢?那么你可以根据分配给该用户的角色来限制访问。

请看下面的示例:

Jack Doe 是一位拥有工作区管理员角色的经理。他只希望拥有网站管理员角色的用户才能看到网站工作区。因此,进行了如下配置:

为了测试这一点,Jane Doe 被授予了网站模块的访问权限,但没有被赋予网站管理员角色。由于上面所做的配置,她将无法看到网站工作区,如下所示。而 John Doe 既拥有网站模块访问权限又拥有网站管理员角色,他将能够看到网站工作区。

默认工作区

用户首次登录时显示的默认工作区可以为每个用户单独配置。默认情况下,用户会看到他们上次所在的工作区。默认工作区可以通过用户设置表单中的“默认工作区”字段来选择,该表单可以通过头像菜单中的“我的设置”选项访问。

块

区块,顾名思义,是工作区的构建模块。
你可以添加不同类型的区块,编辑它们,并根据需要排列它们,以构建一个完全可定制的工作区。

目前有 9 个区块。我们将在未来继续添加新的区块。

让我们逐一了解它们:

标题

标题区块用作任何部分或段落的标题。

文本

文本区块用作任何部分的段落或描述。

标题和文本都有内联工具栏。
目前,我们有一些有限的工具,如从 H1 到 H6 的标题大小、粗体、斜体和链接。

卡片

卡片包含多个链接,可用于快速导航。
链接项可以是 DocType、报表或页面。

图表

你可以使用此区块在工作区上添加任何仪表板图表。

快捷方式

快捷方式区块用于快速导航到任何 DocType、报表或页面。

快捷方式也可以自定义。
你可以选择它应该打开的视图,并可以添加在打开所选视图时应用的筛选条件。
你可以以药丸形式查看基于快捷方式上应用的筛选条件的记录数量。

间隔

它用于在两个区块之间添加一些空间。

它也可以用于通过在区块之前或之后添加空白空间来定位其他区块。

入门引导

入门引导区块用于在首次使用的用户访问你的页面时引导他们。
它有学习特定模块的步骤。
对于每个步骤,右侧部分有一些描述,或者可以有一个视频链接。

快速列表

在快速列表区块中,我们可以看到所选 DocType 最近更新的记录。
我们还可以添加一些筛选条件。
在只读模式下,我们可以选择刷新列表、更新已应用的筛选条件、创建新记录和打开 DocType 的列表视图。

数字卡片

你可以使用此区块在工作区上添加任何现有的数字卡片。

定制

工作区有两种模式:只读模式和编辑模式。你可以点击编辑按钮开始自定义你的工作区。
自定义工作区和自定义工作区页面是两件不同的事情。

##自定义工作区
除了创建工作区之外,所有对工作区的自定义操作都可以通过侧边栏完成。

让我们看看可以做哪些自定义操作:

###编辑工作区详情
你可以更新标题和图标,将其设为父级或子级,还可以将其设为公开或私有。

###创建副本
你可以创建现有工作区的副本,并根据需要添加或移除其中的一些区块。

###删除工作区
删除现有的工作区。

###隐藏工作区
隐藏你的私有工作区。

只有 Workspace Manager 可以隐藏公开工作区。

###调整位置:
你可以从侧边栏拖放工作区来改变其位置,或将其设为子级/父级工作区。

保存/放弃按钮仅用于保存工作区页面的自定义内容。所有从侧边栏进行的工作区更改都会在你操作时自动保存。

##自定义工作区页面
自定义特定工作区中的区块属于这一类。

让我们看看可以做哪些自定义操作:

###添加新区块

###编辑现有区块

###移除或删除现有区块

###调整区块宽度

###调整区块位置

工作区

工作区是你登录后看到的第一个页面。
它还有一个专属的侧边栏,你可以根据自己的方便重新调整工作区的位置。

工作区本身是一个用来构建用户专属页面的工具。
它自带一些标准工作区,这些工作区列在侧边栏的 PUBLIC 区域中,对所有用户可见。
你也可以创建私有工作区,这些工作区只会在 MY WORKSPACES 区域中对当前登录的用户(所有者)可见。

让我们来创建一个,并看看一些很酷的功能。

###创建新工作区

  1. 点击 创建工作区 按钮。
  2. 输入标题,然后点击创建。

###创建子工作区

  1. 点击 创建工作区 按钮。
  2. 输入标题。
  3. 在父级字段中选择“主页”,然后点击创建。

###工作区区块
工作区区块是构建工作区页面的基本单元。
这些区块可以通过不同的组合方式来摆放,从而为特定使用场景构建理想的工作区。

了解更多 →


###侧边栏

侧边栏中有两个区域:MY WORKSPACES 和 PUBLIC。

你可以创建任意数量的私有工作区。
它们只会在 MY WORKSPACES 区域中对当前登录的用户(所有者)可见。

所有标准工作区都位于 PUBLIC 区域下。
你也可以创建更多公共工作区。这些工作区将对所有用户可见。

更多 →

只有拥有 工作区管理员角色 的用户才能创建、编辑或删除公共工作区

###自定义
工作区有两种模式:只读模式 和 编辑模式。你可以点击 编辑 按钮开始自定义你的工作区。
自定义工作区和自定义工作区页面是两件不同的事。

从工作区文档中添加新区块是行不通的,因为新工作区并不知道该把那个区块加到哪里(在哪个位置)。你可以从工作区文档中更新现有区块,但也不推荐这样做。最佳做法是使用新的工作区构建器界面来完成各种自定义操作。