关于 frappe-frontend-development
Frappe Frontend Development 是用于构建 Frappe 应用前端的技能,涵盖 Frappe UI 和门户页面。内容包括 Vue 3、TailwindCSS 单页应用、数据资源及生产构建。
主要功能
- 01搭建 Frappe UI 单页应用
- 02使用 Jinja 和 HTML 构建门户页
- 03通过 Resource API 获取数据
- 04使用 Frappe UI 组件和工具
- 05配置 TailwindCSS 与生产构建
适用场景
- 01为 Frappe 应用创建自定义单页应用
- 02构建面向公众的门户页面
- 03添加分页 DocType 列表和文档操作
- 04配置前端身份验证与 API 访问
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ lubusin/ frappe-skills --skill frappe-frontend-development安装后在 Claude Code 中输入/frappe-frontend-development确认已加载。
附带文件references/frappe-ui.mdreferences/portal-development.md
详细介绍
Frappe 前端开发
使用 Frappe UI(Vue 3 + TailwindCSS)和门户页面构建现代前端应用。
适用场景
- 为 Frappe 应用构建自定义 SPA 前端
- 使用 Frappe UI 组件(Button、Dialog、ListView 等)
- 使用 Resource、ListResource、DocumentResource 获取数据
- 创建门户/面向公众的页面
- 在 Frappe 应用中配置 Vue 3 前端工具链
所需输入
- 应用名称,以及是否已有前端
- 前端类型(通过 Frappe UI 构建完整 SPA,或门户页面)
- 身份验证要求(登录用户、访客访问)
- 所需的关键组件和数据资源
流程
0) 选择前端方案
| 方案 | 适用场景 | 技术栈 |
|---|---|---|
| Frappe UI SPA | 自定义应用前端 | Vue 3、TailwindCSS、Vite |
| 门户页面 | 简单的公共页面 | Jinja + HTML、少量 JS |
| Desk 扩展 | 管理界面增强 | Form/List 脚本(参见 frappe-desk-customization) |
1) 搭建 Frappe UI 前端
# Inside your Frappe app directory
cd apps/my_app
npx degit frappe/frappe-ui-starter frontend
# Install dependencies
cd frontend
yarn
# Start dev server
yarn dev
2) 配置 main.js
import { createApp } from 'vue'
import {
FrappeUI,
setConfig,
frappeRequest,
resourcesPlugin,
pageMetaPlugin
} from 'frappe-ui'
import App from './App.vue'
import './index.css'
let app = createApp(App)
// Register FrappeUI plugin (components + directives)
app.use(FrappeUI)
// Enable Frappe response parsing
setConfig('resourceFetcher', frappeRequest)
// Optional: Options API resource support
app.use(resourcesPlugin)
// Optional: Reactive page titles
app.use(pageMetaPlugin)
app.mount('#app')
3) 使用 Resource 获取数据
通用 Resource — 用于自定义 API 调用:
import { createResource } from 'frappe-ui'
let stats = createResource({
url: 'my_app.api.get_dashboard_stats',
params: { period: 'monthly' },
auto: true,
cache: 'dashboard-stats',
transform(data) {
return { ...data, formatted_total: format_currency(data.total) }
},
onSuccess(data) { console.log('Loaded:', data) },
onError(error) { console.error('Failed:', error) }
})
// Properties
stats.data // Response data
stats.loading // Boolean: request in progress
stats.error // Error object if failed
stats.fetched // Boolean: data fetched at least once
// Methods
stats.fetch() // Trigger request
stats.reload() // Re-fetch
stats.submit({ period: 'weekly' }) // Fetch with new params
stats.reset() // Reset state
List Resource — 用于带分页的 DocType 列表:
import { createListResource } from 'frappe-ui'
let todos = createListResource({
doctype: 'ToDo',
fields: ['name', 'description', 'status'],
filters: { status: 'Open' },
orderBy: 'creation desc',
pageLength: 20,
auto: true,
cache: 'open-todos'
})
// List-specific API
todos.data // Array of records
todos.hasNextPage // Boolean: more pages
todos.next() // Load next page
todos.reload() // Refresh list
// CRUD operations
todos.insert.submit({ description: 'New task' })
todos.setValue.submit({ name: 'TODO-001', status: 'Closed' })
todos.delete.submit('TODO-001')
todos.runDocMethod.submit({ method: 'send_email', name: 'TODO-001' })
Document Resource — 用于单个文档操作:
import { createDocumentResource } from 'frappe-ui'
let todo = createDocumentResource({
doctype: 'ToDo',
name: 'TODO-001',
whitelistedMethods: {
sendEmail: 'send_email',
markComplete: 'mark_complete'
},
onSuccess(doc) { console.log('Loaded:', doc.name) }
})
// Document API
todo.doc // Full document object
todo.reload() // Refresh document
// Update fields
todo.setValue.submit({ status: 'Closed' })
// Debounced update (coalesces rapid changes)
todo.setValueDebounced.submit({ description: 'Updated' })
// Call whitelisted methods
todo.sendEmail.submit({ email: '[email protected]' })
// Delete
todo.delete.submit()
4) 使用 Frappe UI 组件
<template>
<div class="p-4">
<Button variant="solid" theme="blue" @click="showDialog = true">
Add Todo
</Button>
<ListView :columns="columns" :rows="todos.data">
<template #cell="{ column, row, value }">
<Badge v-if="column.key === 'status'" :theme="value === 'Open' ? 'orange' : 'green'">
{{ value }}
</Badge>
<span v-else>{{ value }}</span>
</template>
</ListView>
<Dialog v-model="showDialog" :options="{ title: 'New Todo' }">
<template #body-content>
<TextInput v-model="newDescription" placeholder="Description" />
</template>
<template #actions>
<Button variant="solid" @click="addTodo">Save</Button>
</template>
</Dialog>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { Button, ListView, Badge, Dialog, TextInput, createListResource } from 'frappe-ui'
const showDialog = ref(false)
const newDescription = ref('')
const todos = createListResource({
doctype: 'ToDo',
fields: ['name', 'description', 'status'],
auto: true
})
const columns = [
{ label: 'Description', key: 'description' },
{ label: 'Status', key: 'status', width: 100 }
]
function addTodo() {
todos.insert.submit(
{ description: newDescription.value },
{ onSuccess() { showDialog.value = false; newDescription.value = '' } }
)
}
</script>
可用的组件类别:
| 类别 | 组件 |
|---|---|
| 输入 | TextInput、Textarea、Select、Combobox、MultiSelect、Checkbox、Switch、DatePicker、TimePicker、Slider、Password、Rating |
| 显示 | Alert、Avatar、Badge、Breadcrumbs、Progress、Tooltip、ErrorMessage、LoadingText |
| 导航 | Button、Dropdown、Tabs、Sidebar、Popover |
| 布局 | Dialog、ListView、Calendar、Tree |
| 富内容 | TextEditor(TipTap)、Charts、FileUploader |
5) 添加指令和实用工具
<script setup>
import { onOutsideClickDirective, visibilityDirective, debounce } from 'frappe-ui'
const vOnOutsideClick = onOutsideClickDirective
const vVisibility = visibilityDirective
const debouncedSearch = debounce((query) => {
// Search logic
}, 500)
</script>
<template>
<div v-on-outside-click="closeDropdown">...</div>
<div v-visibility="onVisible">Lazy loaded content</div>
</template>
6) 配置 TailwindCSS
// tailwind.config.js
module.exports = {
presets: [
require('frappe-ui/src/utils/tailwind.config')
],
content: [
'./index.html',
'./src/**/*.{vue,js,ts}',
'./node_modules/frappe-ui/src/components/**/*.{vue,js,ts}'
]
}
7) 构建生产版本
# Build frontend assets
cd frontend && yarn build
# Assets are served at /frontend by Frappe
8) 门户页面(替代方案)
无需完整 SPA 的简单公共页面:
# In your app's website/ or www/ directory
# my_app/www/my_page.html
{% extends "templates/web.html" %}
{% block page_content %}
<h1>{{ title }}</h1>
<p>Welcome, {{ frappe.session.user }}</p>
{% endblock %}
# my_app/www/my_page.py
def get_context(context):
context.title = "My Page"
context.data = frappe.get_all("ToDo", filters={"owner": frappe.session.user})
验证
-
yarn dev启动时没有错误 - 组件能够正确渲染
- 数据资源能够获取并显示数据
- CRUD 操作正常(插入、更新、删除)
- 身份验证正常(登录重定向、会话处理)
-
yarn build成功完成 - Frappe 能够正确提供生产资源文件
故障排查
- CORS 错误:本地开发时设置
ignore_csrf;确保生产环境中使用正确的 CSRF 令牌 - API 调用返回 404:检查方法路径;确认存在
@frappe.whitelist()装饰器 - 找不到组件:确保导入路径正确;检查
frappe-ui版本 - 样式异常:确认 TailwindCSS 配置包含
frappe-ui组件路径 - 身份验证问题:检查会话 Cookie;确保开发代理配置中的站点 URL 匹配
转交处理
- Desk UI 脚本编写 →
frappe-desk-customization - API 端点实现 →
frappe-api-development - 应用架构 →
frappe-app-development - 官方应用的 UI/UX 模式 →
frappe-ui-patterns
参考资料
- references/frappe-ui.md — Frappe UI 框架参考
- references/portal-development.md — 门户页面概览
防护措施
- 自定义前端始终使用 Frappe UI:切勿在应用前端中使用原生 JS、jQuery 或自定义框架——Frappe UI(Vue 3 + TailwindCSS)是标准方案。这样可确保与 CRM、Helpdesk 及其他官方 Frappe 应用保持一致。
- 使用 FrappeUI 组件:优先使用
<Button>、<Input>、<FormControl>,而不是自定义 HTML,以保持一致性。 - 遵循 CRM/Helpdesk 应用外壳模式:对于 CRUD 应用,请遵循
frappe-ui-patterns技能,其中记录了官方 Frappe 应用的侧边栏导航、列表视图、表单布局和路由模式。 - 处理加载状态:API 调用期间始终显示加载指示器;使用
resource.loading。 - 验证 API 响应:访问数据前检查是否有错误;处理
exc响应。 - 正确配置代理:开发服务器必须将 API 调用代理到 Frappe 后端。
- 处理身份验证:检查
$session.user,并在需要时重定向到登录页面。
常见错误
| 错误 | 失败原因 | 修复方法 |
|---|---|---|
| 缺少 CORS/代理设置 | API 调用在开发环境中失败 | 配置 Vite 代理,将 /api 转发到 Frappe 站点 |
| 未处理身份验证状态 | 已登出的用户会导致应用崩溃 | 在挂载时检查 call('frappe.auth.get_logged_user') |
| 资源 URL 错误 | API 调用出现 404 错误 | 使用 createResource 并设置正确的方法路径 |
| 硬编码站点 URL | 在不同环境中无法正常工作 | 使用相对 URL 或环境变量 |
| 未包含 CSRF 令牌 | POST 请求失败 | 使用 frappe.csrf_token 或正确配置会话 |
| 缺少 TailwindCSS 配置 | Frappe UI 样式失效 | 在 Tailwind 内容路径中包含 frappe-ui |
| 使用原生 JS/jQuery | 用户体验不一致,维护成本高 | 自定义前端始终使用 Frappe UI |
| 自定义应用外壳设计 | 与生态系统不一致 | 遵循 CRM/Helpdesk 的导航、列表和表单模式 |
评论
支持 Markdown