How Formily Revolutionized Our Store‑Headquarters Form Workflow

This article details how Guming's frontend team evolved from simple page‑per‑work‑order implementations to a high‑performance, low‑code Formily ecosystem, covering the technical stack, form configuration platform, custom expression parser, and future visual builder for dynamic business workflows.

Goodme Frontend Team
Goodme Frontend Team
Goodme Frontend Team
How Formily Revolutionized Our Store‑Headquarters Form Workflow

Background

Guming is a tea‑chain brand with many franchisee‑related processes such as supply chain, ordering, finance, and equipment. "Upload and dispatch" refers to the communication method between headquarters and stores. This article explains the technical evolution and implementation of that workflow.

Upload and Dispatch

Upload and dispatch consist of two parts:

Store upload: stores send messages, apply for after‑sale service, etc.

Headquarters dispatch: headquarters send messages, tasks, announcements to stores.

Store uploads are mainly work orders, though they can also originate from manual customer service. Headquarters dispatches are similar to work orders on the front‑end.

Work‑order System Evolution

Initially each work order had a dedicated page, which became inefficient as the number of orders grew.

We then tried a form‑model‑description plus rendering approach using the third‑party platform Jiandaoyun. The model was maintained there and rendered on our pages, but it showed several drawbacks:

Redundant fields – the model contained dozens of fields while only a few were needed.

Complex validation.

Inability to express field linkage (e.g., show/hide based on selections).

Front‑end rendering required many if/else statements.

We needed a solution that supported linkage, high performance, strong validation, and elegant implementation. Formily met these requirements, so we adopted it as the core of Guming’s work‑order ecosystem.

Formily Work‑order Ecosystem

Formily

Formily is a data‑and‑protocol‑driven form solution that provides high‑performance, low‑code capabilities. JSON Schema exists independently and is consumed by UI bridge layers, ensuring consistent behavior across frameworks without duplicated parsing logic. Its cross‑framework component ecosystem improves developer efficiency.

Core Advantages

High performance

Out‑of‑the‑box

Efficient linkage logic

Cross‑platform reuse

Dynamic rendering

Core Disadvantage

Steep learning curve, although 2.x has reduced complexity.

Technology Stack Selection

We chose React for the UI bridge and Ant Design for the component library in the admin console. For B‑end mini‑programs we initially used Taro UI, but later built our own component library Gudesion and migrated Formily’s extensions to it.

Integrating Extension Library (Gudesign)

Formily documentation: https://react.formilyjs.org/zh-CN/api/shared/connect

import { connect, mapReadPretty, mapProps } from '@formily/react';
import { getPrefixCls } from '@guming/formily-shared';
import { Input as GudInput, InputProps, TextareaProps } from '@guming/gudesign';
import cls from 'classnames';
import React from 'react';
import { PreviewText } from '../preview-text';
import { getFinallyLayout } from '../shared';

import './index.less';

export type FGInputProps = Omit<InputProps, 'required' | 'label' | 'defaultValue' | 'value'>;
export type FGTextAreaProps = Omit<TextareaProps, 'required' | 'defaultValue' | 'value'>;

export const Input: React.FC<FGInputProps> & { TextArea?: React.FC<FGTextAreaProps> } = connect(
  GudInput,
  mapProps((props, field) => {
    return {
      ...(getFinallyLayout(field.componentType, field.decoratorProps.layout) === 'column'
        ? { align: 'left' }
        : { align: 'right' }),
      placeholder: '请输入',
      ...props,
      className: cls(`${getPrefixCls('comp-input')}`, props.className),
      // not needed
      required: undefined,
      label: undefined,
      defaultValue: undefined,
    } as InputProps;
  }),
  mapReadPretty(PreviewText.Input)
);

export default Input;

Form Configuration Platform

We built a platform to configure JSON Schema for forms. The configuration method follows Formily’s official documentation.

Business Scenario Implementation

Example: headquarters dispatch a task to collect store façade dimensions. The process includes JSON Schema configuration, rendering the form, submitting data, and workflow nodes such as store, supervisor, finance. Nodes can hide or show fields based on the current step.

Task data structure includes:

Form JSON Schema for rendering.

Form data for initialization and submission.

Node definitions (e.g., SHOP_TASK, SUPERVISOR_TASK, FINANCE_TASK).

Node attributes (processing logic, custom hooks, etc.).

Form Data Structure

{
  jsonSchema // form description
  fromData // form data
  effects: {
    nodeA: { // node, store/supervisor
      hook // custom logic
      action // workflow handling
      scope // fields needed by js block
      beforeSubmit // hook before submit
      afterSubmit // hook after submit
    }
  }
}

effects Configuration

hook

Execution Flow

Parser

Effects contain custom logic expressed as strings. Mini‑programs forbid eval and new Function, so we explored safe execution methods.

Old solution used Acorn to parse simple expressions.

Old Scheme

Parsing a single‑line conditional expression. $deps[0] === '3' ? 'visible' : 'none' Parser implementation snippet:

import { Parser } from 'acorn';

export function CustomCompiler(input, scope) {
  const parseNode = Parser.parse(input, { ecmaVersion: 'latest', locations: false });
  const targetNode = parseNode?.body?.[0];
  if (!targetNode) return false;
  const customParser = new CustomParse({ node: targetNode, scope, input });
  const result = customParser.execute();
  return result;
}

Configuration mapping node types to parser methods:

export const PARSE_FN_MAP = {
  Identifier: 'parseIdentifier',
  MemberExpression: 'parseMember',
  LogicalExpression: 'parseLogical',
  BinaryExpression: 'parseBinary',
  Literal: 'parseLiteral',
  ConditionalExpression: 'parseConditional',
  UnaryExpression: 'parseUnary',
  CallExpression: 'parseCall',
  ChainExpression: 'parseChain',
};

Current Scheme

For complex expressions we adopted an open‑source sandbox compiler.

export function customCompiler(input, scope) {
  const sandbox = new Sandbox();
  const exec = sandbox.compile(`return ${input}`);
  const result = exec({ ...scope, dayjs }).run();
  return result;
}

Register the compiler with Formily:

Schema.registerCompiler(customCompiler);

Use it for effects hooks:

customCompiler(effects?.[nodeId]?.hook, {
  form: f,
  ...basicEffectsParams,
  ...(effects?.[nodeId]?.scope || {}),
});

We are still seeking a more stable parser solution.

Recommendations

Articles on writing AST parsers:

https://github.com/peakchen90/how-to-write-an-ast-parser/blob/master/README.md

https://github.com/jamiebuilds/the-super-tiny-compiler/blob/master/README.md

Preview

Form rendering screenshot.

Future

The current form configuration lacks a visual builder; developers manually write JSON Schema, which is time‑consuming and error‑prone. We are building a visual configuration platform that generates schema from component metadata, to be released soon.

Conclusion

Formily provides strong performance, low‑code, cross‑platform, and linkage capabilities that greatly help our business, though it introduces learning cost and higher maintenance for large schemas. Our current solution fits our needs and will evolve as the business grows.

Final

Follow the Goodme frontend team public account for more practical sharing.

Dynamic FormsReActForm Architecture
Goodme Frontend Team
Written by

Goodme Frontend Team

Regularly sharing the team's insights and expertise in the frontend field

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.