飞布产品手册
官网B站Github
V1.0
V1.0
  • 序言
  • 更新日志
  • 产品简介
    • 什么是飞布?
    • 飞布的价值
    • 飞布的优势
    • 应用场景
    • 数据安全
    • 产品案例
  • 快速入门
    • 初识飞布
    • 快速上手
      • 图文版
    • 词汇概览
    • 工作原理
  • 基础-可视化开发
    • 概览
      • CLI
      • 控制台
        • 主功能区
    • 数据源
      • 数据库
        • 数据库连接
          • 高级设置
        • 数据建模
        • 数据预览
      • REST 数据源
      • GraphQL 数据源
      • 消息队列
    • API构建
      • 可视化构建
        • API规范
      • 批量新建
      • HTTP请求流程指令
      • 使用API
      • 实时查询
      • 实时推送
      • 关联查询
      • 数据缓存
      • 常见用例
    • 身份验证
      • 授权码模式
        • 身份验证(废弃)
      • 隐式模式
      • 数据权限控制
    • 身份授权
      • RBAC
        • 授权与访问控制(废弃)
      • 接口权限控制
      • 开放API
    • 文件存储
      • S3配置及使用
      • 文件管理面板
      • 高级配置:profile
  • 进阶-钩子机制
    • 钩子概览
    • 启动钩子
      • Node钩子
      • Golang钩子
      • Python钩子
      • Java钩子
    • OPERATION钩子
    • 身份验证钩子
    • graphql钩子
    • 函数钩子
      • functions(废弃)
      • proxys(废弃)
    • 文件上传钩子
    • 内部调用
  • 使用-部署上线
    • 部署运维
      • 手动部署
        • 流水线部署(废弃)
      • Docker部署
      • 飞布云
    • 接口安全
      • CSRF token 保护
      • 跨域访问
    • 客户端SDK
      • 微信小程序SDK
      • Flutter SDK
      • uniapp SDK
  • 环境准备
    • 文件存储 S3
    • 身份认证 OIDC
    • NodeJs环境
  • 实战案例
    • Fireboom Admin
      • 管理后台-refine(废弃)
    • 实时TODO LIST
    • 语音版ChatGPT
    • AI魔法师实战
    • 阿里低代码引擎
  • 路线图
  • 常见问题
  • 核心概念
    • GraphQL
    • 超图
    • 请求时序图
    • 服务端Operation
  • 二次开发
    • 钩子规范
      • 钩子规范bak
    • 模板规范
    • 自定义模板
    • 其它参考
由 GitBook 提供支持
在本页
  • Refine 介绍
  • 快速实现增删改查
  • 数据概览、统计
  • 用户角色菜单

这有帮助吗?

在GitHub上编辑
  1. 实战案例
  2. Fireboom Admin

管理后台-refine(废弃)

使用飞布开发管理后台

上一页Fireboom Admin下一页实时TODO LIST

最后更新于1年前

这有帮助吗?

因 Refine 文档较之前有较大变更,所以本文档可能不能适配最新的 Refine

Fireboom 非常适合增删改查类操作,因为 Fireboom 里可以直接对数据模型进行批量创建 API,快速实现增删改查业务,再配合上 Fireboom 自动生成的客户端 SDK,从而可以快速、类型安全的实现管理后台功能。

常见的中后台管理系统中,主要由数据概览、统计、业务数据管理、用户角色菜单配置等几个模块构成。要使用 Fireboom 实现这些功能,我们需要用到 Fireboom 的聚合查询或者rawQuery来进行统计查询,批量创建来生成增删改查的接口和 SDK,自定义的 Prisma schema 以及对应的 API 来实现用户角色菜单功能。下面分别介绍具体实现。

Refine 介绍

在这个示例中,我们使用 作为管理后台的开发基础。Refine 是一个基于 React 开发的完整的开发框架,它自带了多数据源管理、身份验证、访问控制、路由、网络管理、i18n 等中后台常用模块,同时对于 CRUD 应用提供快速的、通用的配置,非常适合中后台开发。

快速实现增删改查

  1. 在 Fireboom 中新建数据源,并添加一个 Pet 表

model Pet {
  id        Int      @id @default(autoincrement())
  name      String
  age       Int
  sex       Int
  createdAt DateTime @default(now())
}
  1. 在 Fireboom 控制台点击批量新建,勾选所有接口,生成多个接口,然后多选生成的 API,右键,点击“上线”完成批量上线。

npm create refine-app@latest my-antd-project

在命令行弹框中依次选择

✔ Choose a project template · refine-react
✔ What would you like to name your project?: · admin
✔ Choose your backend service to connect: · data-provider-custom-json-rest
✔ Do you want to use a UI Framework?: · antd
✔ Do you want to add example pages?: · no
✔ Do you need any Authentication logic?: · auth-provider-custom
✔ Do you need i18n (Internationalization) support?: · no
✔ Choose a package manager: · pnpm
✔ Would you mind sending us your choices so that we can improve superplate? · no

执行后会显示完成创建的提示

Success! Created admin at /path/to/your/workspace/admin
  1. 在 Refine 中添加一个 dataProvider

// dataProvider.ts
import { CrudOperators, DataProvider, LogicalFilter } from "@pankod/refine-core";
import { message } from "antd";
import axios, { AxiosError, AxiosResponse } from "axios";

async function resolveResp(respPromise: Promise<AxiosResponse<any, any>>, dataField = 'data') {
  let msg
  try {
    const resp = await respPromise
    if (resp.status < 300 && resp.status >= 200) {
      return resp.data[dataField]
    }
    if (resp.data) {
      msg = resp.data.message ?? resp.data
    } else {
      msg = resp.statusText
    }
  } catch (e) {
    const err = e as AxiosError
    msg = err.message
  }
  message.error(msg)
  return null
}

export type SimpleGraphqlQueryOperation = 'equals' | 'gt' | 'gte' | 'in' | 'lt' | 'lte' | 'notIn' | 'contains' | 'startsWith' | 'endsWith' | 'mode'

export type GraphqlQueryOperation = SimpleGraphqlQueryOperation | { not: SimpleGraphqlQueryOperation }

/**
 * 把 refine 定义的 operator 转换成 graph 的查询语句里的查询条件
 */
const simpleMatch: Record<Extract<CrudOperators, 'eq' | 'gt' | 'gte' | 'lt' | 'lte' | 'in' | 'startswith' | 'endswith' | 'contains'>, SimpleGraphqlQueryOperation> = {
  eq: 'equals',
  gt: 'gt',
  gte: 'gte',
  lt: 'lt',
  lte: 'lte',
  in: 'in',
  startswith: 'startsWith',
  endswith: 'endsWith',
  contains: 'contains'
}

type SimpleMatchKeys = keyof typeof simpleMatch

function isNull(v: any) {
  return v === null || v === undefined || v === ''
}

function parseOperatorToGraphQuery(operator: LogicalFilter) {
  if (isNull(operator.value) || (Array.isArray(operator.value) && (!operator.value.length || operator.value.every(isNull)))) {
    return
  }
  if (operator.operator in simpleMatch) {
    return {
      [simpleMatch[operator.operator as SimpleMatchKeys]]: operator.value
    }
  }
  switch (operator.operator) {
    case 'between':
      return {
        // 根据业务决定边界
        gte: operator.value[0],
        lte: operator.value[1]
      }
    default:
      break;
  }
}

export const OperationDataProvider = (apiUrl: string = '/operations'): DataProvider => {
  const client = axios.create({
    baseURL: apiUrl, paramsSerializer: {
      serialize(params) {
        return Object.keys(params).reduce<string[]>((arr, key) => {
          const curr = params[key]
          arr.push(`${key}=${JSON.stringify(curr)}`)
          return arr
        }, []).join('&')
      }
    }
  })

  client.interceptors.response.use((resp) => {
    let _message
    if (resp.data) {
      _message = resp.data.message
    } else {
      _message = resp.statusText
    }
    if (_message) {
      message.error(_message)
    }
    return resp
  })

  return {
    async getList({ resource, hasPagination, pagination, metaData, sort, filters }) {
      const params: Record<string, any> = {}
      if (hasPagination) {
        params.take = pagination!.pageSize
        params.skip = pagination!.pageSize! * (pagination!.current! - 1)
      }
      if (sort && sort.length) {
        params.orderBy = sort.map(item => ({ [item.field]: item.order }))
      }
      if (filters && filters.length) {
        params.query = filters.reduce<Record<string, any>>((map, cur) => {
          const filter = cur as LogicalFilter
          map[filter.field] = parseOperatorToGraphQuery(filter)
          return map
        }, {})
      }
      const data = await resolveResp(client.get(`/${resource}/Get${resource}List`, { params }))
      return data ? data : { total: 0, data: [] }
    },
    async getMany({ resource, metaData, ids }) {
      const data = await resolveResp(client.get(`/${resource}/GetMany${resource}`, { params: { ids } }))
      return data ? data.data : []
    },
    async getOne({ id, resource }) {
      const data = await resolveResp(client.get(`/${resource}/GetOne${resource}`, { params: { id: +id } }))
      return data.data
    },
    async create({ resource, variables, metaData }) {
      const data = await resolveResp(client.post(`/${resource}/CreateOne${resource}`, variables))
      return data
    },
    async update({ id, resource, variables, metaData }) {
      const data = await resolveResp(client.post(`/${resource}/UpdateOne${resource}`, { ...variables, id: +id }))
      return data
    },
    async deleteOne({ id, resource, variables }) {
      const data = await resolveResp(client.post(`/${resource}/DeleteOne${resource}`, { ...variables, id: +id }))
      return data
    },
    async custom({ url, method, query, headers, payload}) {
      const data = await resolveResp(client({
        url,
        method,
        headers,
        params: query,
        data: payload
      }))
      return data
    },
    getApiUrl() {
      return client.getUri()
    }
  }
}

该文件定义了一个适用于 Refine 的同时又符合 Fireboom 批量新建生成的 API 地址规则的数据源适配器,我们需要按照 Refine 的规则来设置数据源适配器。

// App.tsx
import { Refine } from "@refinedev/core";

import dataProvider from "./dataProvider";

const App: React.FC = () => {
    return <Refine dataProvider={dataProvider} />;
};
  1. 接下来开始添加 Pet 的增删改查页面。按照 Refine 的规则,我们创建Pet的resource,

import { Refine, ResourceProps } from '@pankod/refine-core'
import { PetCreate, PetEdit, PetList, PetShow } from './features/pet'
const resources: ResourceProps[] = [
  {
    name: 'Pet',
    options: { label: '宠物管理' },
    list: PetList,
    create: PetCreate,
    edit: PetEdit,
    show: PetShow
  }
]

const App: React.FC = () => {
    return <Refine dataProvider={dataProvider} resources={resources} />;
};
  1. 参考 Refine 文档实现前端增删改成页面,页面将根据定义的 dataProvider自动调用接口

数据概览、统计

使用 Fireboom 超图里的聚合方法,以及 NodeJs 数据源里的 rawQuery能力 WIP...

用户角色菜单

WIP...

按照 文档初始化一个 Refine 项目。

仓库地址:

Refine 官方快速开始
https://github.com/fireboomio/refine-admin
Refine