mcpField
F
frontend-development

frontend-development

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

关于 frontend-development

为 Frappe 应用开发前端的技能,指导使用 Frappe UI、Vue 3 和 TailwindCSS 构建 SPA 或门户页面,面向 Frappe 开发者。

主要功能

  1. 01选择 SPA 或门户页面方案
  2. 02配置 Vue 3 前端工具
  3. 03使用资源接口获取数据
  4. 04采用 Frappe UI 组件

适用场景

  1. 01为 Frappe 应用构建自定义 SPA
  2. 02开发面向公众的门户页面
  3. 03通过资源接口读取文档和列表

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/lubusin/frappe-skills --skill frontend-development

安装后在 Claude Code 中输入/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 脚本(参见 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) 使用 Resources 获取数据

通用 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 脚本 → desk-customization
  • API 端点实现 → api-development
  • 应用架构 → app-development
  • 官方应用中的 UI/UX 模式 → ui-patterns

参考资料

防护原则

  • 自定义前端始终使用 Frappe UI:切勿在应用前端中使用原生 JS、jQuery 或自定义框架——Frappe UI(Vue 3 + TailwindCSS)是标准方案。这可确保与 CRM、Helpdesk 及其他官方 Frappe 应用保持一致。
  • 使用 FrappeUI 组件:优先使用 <Button>、<Input>、<FormControl>,而不是自定义 HTML,以保持一致性
  • 遵循 CRM/Helpdesk 应用外壳模式:对于 CRUD 应用,遵循 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