mcpField
F
frappe-frontend-development

frappe-frontend-development

github.com/lubusin开发69293 次安装12 周前更新
代码仓库

关于 frappe-frontend-development

Frappe Frontend Development 是用于构建 Frappe 应用前端的技能,涵盖 Frappe UI 和门户页面。内容包括 Vue 3、TailwindCSS 单页应用、数据资源及生产构建。

主要功能

  1. 01搭建 Frappe UI 单页应用
  2. 02使用 Jinja 和 HTML 构建门户页
  3. 03通过 Resource API 获取数据
  4. 04使用 Frappe UI 组件和工具
  5. 05配置 TailwindCSS 与生产构建

适用场景

  1. 01为 Frappe 应用创建自定义单页应用
  2. 02构建面向公众的门户页面
  3. 03添加分页 DocType 列表和文档操作
  4. 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

参考资料

防护措施

  • 自定义前端始终使用 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