33 KiB
33 KiB
常见表单模式示例
模式 A:简单信息录入表单
场景: 员工信息登记(姓名、手机、邮箱、部门、备注)
import urllib.request
import json
import time
import random
API_BASE = 'https://boot3.jeecg.com/jeecgboot'
TOKEN = 'your-jwt-token-here'
def api_request(path, data=None, method='POST'):
url = f'{API_BASE}{path}'
headers = {
'X-Access-Token': TOKEN,
'X-Sign': '00000000000000000000000000000000',
'X-Tenant-Id': '1',
'X-Timestamp': str(int(time.time() * 1000)),
'Content-Type': 'application/json; charset=UTF-8'
}
if data is not None:
json_data = json.dumps(data, ensure_ascii=False).encode('utf-8')
req = urllib.request.Request(url, data=json_data, headers=headers, method=method)
else:
req = urllib.request.Request(url, headers=headers, method=method)
resp = urllib.request.urlopen(req)
return json.loads(resp.read().decode('utf-8'))
def gen_ids(widget_type):
"""生成 key 和 model(type 中的 - 转为 _)"""
ts = int(time.time() * 1000)
rnd1 = random.randint(100000, 999999)
rnd2 = random.randint(100000, 999999)
rnd3 = random.randint(100000, 999999)
key = f"{ts}_{rnd1}"
safe_type = widget_type.replace('-', '_')
model = f"{safe_type}_{ts}_{rnd2}"
card_key = f"{ts + 1}_{rnd3}"
card_model = f"card_{ts + 1}_{rnd3}"
return key, model, card_key, card_model
def make_card_widget(widget_type, name, class_name, icon, options, required=False, extra_fields=None):
"""创建一个带 card 容器的控件"""
key, model, card_key, card_model = gen_ids(widget_type)
time.sleep(0.002) # 确保时间戳不同
widget = {
"type": widget_type,
"name": name,
"className": class_name,
"icon": icon,
"hideTitle": False,
"options": options,
"advancedSetting": {
"defaultValue": {
"type": "compose",
"value": "",
"format": "string",
"allowFunc": True,
"valueSplit": "",
"customConfig": False
}
},
"remoteAPI": {"url": "", "executed": False},
"key": key,
"model": model,
"modelType": "main",
"rules": [{"required": True, "message": "${title}必须填写"}] if required else [],
"isSubItem": False
}
if extra_fields:
widget.update(extra_fields)
card = {
"key": card_key,
"type": "card",
"isAutoGrid": True,
"isContainer": True,
"list": [widget],
"options": {},
"model": card_model
}
return card, model
# ---- 构建表单字段 ----
widgets = []
used_types = set(["card"])
title_model = None
# 1. 姓名(input,必填,标题字段)
card, model = make_card_widget("input", "姓名", "form-input", "icon-input", {
"width": "100%", "defaultValue": "", "required": True,
"dataType": None, "pattern": "", "patternMessage": "",
"placeholder": "", "clearable": False, "readonly": False,
"disabled": False, "fillRuleCode": "", "showPassword": False,
"unique": False, "hidden": False, "hiddenOnAdd": False,
"fieldNote": "", "autoWidth": 100
}, required=True)
widgets.append(card)
used_types.add("input")
title_model = model
# 2. 手机(phone)
card, _ = make_card_widget("phone", "手机", "form-input-phone", "icon-mobile-phone", {
"width": "300px", "defaultValue": "", "required": False,
"placeholder": "", "readonly": False, "disabled": False,
"unique": False, "hidden": False, "showVerifyCode": False,
"hiddenOnAdd": False, "fieldNote": "", "autoWidth": 100
}, extra_fields={
"defaultRules": [
{"type": "phone", "message": "请输入正确的手机号码"},
{"type": "validator", "message": "", "trigger": "blur"}
]
})
widgets.append(card)
used_types.add("phone")
# 3. 邮箱(email)
card, _ = make_card_widget("email", "邮箱", "form-input-email", "icon-email", {
"width": "300px", "defaultValue": "", "required": False,
"placeholder": "", "readonly": False, "disabled": False,
"unique": False, "hidden": False, "showVerifyCode": False,
"hiddenOnAdd": False, "fieldNote": "", "autoWidth": 100
}, extra_fields={
"defaultRules": [
{"type": "email", "message": "请输入正确的邮箱地址"},
{"type": "validator", "message": "", "trigger": "blur"}
]
})
widgets.append(card)
used_types.add("email")
# 4. 部门(select-depart)
card, _ = make_card_widget("select-depart", "部门", "form-select-depart", "icon-depart", {
"keyMaps": [], "defaultValue": "", "defaultLogin": False,
"placeholder": "", "width": "100%", "multiple": False,
"disabled": False, "customReturnField": "id",
"hidden": False, "dataAuthType": "member",
"hiddenOnAdd": False, "required": False, "fieldNote": "", "autoWidth": 100
})
widgets.append(card)
used_types.add("select-depart")
# 5. 备注(textarea)
card, _ = make_card_widget("textarea", "备注", "form-textarea", "icon-textarea", {
"width": "100%", "defaultValue": "", "required": False,
"disabled": False, "pattern": "", "patternMessage": "",
"placeholder": "", "readonly": False, "unique": False,
"hidden": False, "hiddenOnAdd": False, "fieldNote": "", "autoWidth": 100
})
widgets.append(card)
used_types.add("textarea")
# ---- 构建完整 JSON ----
design_json = {
"list": widgets,
"config": {
"formStyle": "normal",
"titleField": title_model,
"showHeaderTitle": True,
"labelWidth": 100,
"labelPosition": "top",
"size": "small",
"dialogOptions": {
"top": 20, "width": 1000,
"padding": {"top": 25, "right": 25, "bottom": 30, "left": 25}
},
"disabledAutoGrid": False,
"designMobileView": False,
"enableComment": True,
"hasWidgets": sorted(list(used_types)),
"defaultLoadLargeControls": False,
"expand": {"js": "", "css": "", "url": {"js": "", "css": ""}},
"transactional": True,
"customRequestURL": [{"url": ""}],
"disableMobileCss": True,
"allowExternalLink": False,
"externalLinkShowData": False,
"headerImgUrl": "",
"externalTitle": "",
"enableNotice": False,
"noticeMode": "external",
"noticeType": "system",
"noticeReceiver": "",
"allowPrint": False,
"allowJmReport": False,
"jmReportURL": "",
"bizRuleConfig": [],
"bigDataMode": False
}
}
# ---- 创建或查询表单 ----
form_name = "员工信息登记"
form_code = "yuan_gong_xin_xi_deng_ji"
# 先检查是否已存在(避免重复创建报错)
try:
query_result = api_request(f'/desform/queryByCode?desformCode={form_code}', method='GET')
if query_result.get('success') and query_result.get('result'):
form_id = query_result['result']['id']
update_count = query_result['result'].get('updateCount', 1)
print(f'表单已存在,ID: {form_id},将更新设计...')
else:
raise Exception('not found')
except:
# 创建新表单(注意:add 返回的 result 为 null,不能直接取 ID)
add_result = api_request('/desform/add', {
'desformName': form_name,
'desformCode': form_code
})
if not add_result.get('success'):
print('创建失败!', add_result)
exit(1)
# 必须通过 queryByCode 获取表单 ID
query_result = api_request(f'/desform/queryByCode?desformCode={form_code}', method='GET')
form_id = query_result['result']['id']
update_count = query_result['result'].get('updateCount', 1)
print(f'表单ID: {form_id}')
# ---- 保存设计(updateCount 传当前值,后端自动递增) ----
edit_result = api_request('/desform/edit', {
'id': form_id,
'desformDesignJson': json.dumps(design_json, ensure_ascii=False),
'updateCount': update_count,
'autoNumberDesignConfig': {'update': {}, 'current': {}},
'refTableDefaultValDbSync': {'changes': {}, 'removeKeys': []}
}, method='PUT')
print('保存结果:', json.dumps(edit_result, ensure_ascii=False, indent=2))
if edit_result.get('success'):
print(f'\n表单创建成功!')
print(f'表单ID: {form_id}')
print(f'表单名称: {form_name}')
print(f'表单编码: {form_code}')
else:
print('保存失败!')
模式 B:带选项的审批表单
场景: 请假申请(请假类型单选、日期范围、天数、原因、附件)
在模式 A 基础上,字段构建部分替换为:
# 请假类型(radio,必填)
card, _ = make_card_widget("radio", "请假类型", "form-radio", "icon-radio-active", {
"inline": True, "matrixWidth": 120, "defaultValue": "",
"showType": "default", "showLabel": False, "useColor": False,
"colorIteratorIndex": 3,
"options": [
{"value": "事假", "itemColor": "#2196F3"},
{"value": "病假", "itemColor": "#08C9C9"},
{"value": "年假", "itemColor": "#00C345"},
{"value": "婚假", "itemColor": "#FF9800"}
],
"required": True, "width": "", "remote": False,
"remoteOptions": [], "props": {"value": "value", "label": "label"},
"remoteFunc": "", "disabled": False, "hidden": False,
"hiddenOnAdd": False, "fieldNote": "", "autoWidth": 100
}, required=True)
# radio 的 advancedSetting 需要特殊设置
card["list"][0]["advancedSetting"]["defaultValue"]["valueSplit"] = ","
card["list"][0]["advancedSetting"]["defaultValue"]["customConfig"] = True
widgets.append(card)
used_types.add("radio")
# 开始日期(date,必填)
card, _ = make_card_widget("date", "开始日期", "form-date", "icon-date", {
"defaultValue": "", "defaultValueType": 1,
"readonly": False, "disabled": False, "editable": True,
"clearable": True, "placeholder": "", "startPlaceholder": "",
"endPlaceholder": "", "designType": "date", "type": "date",
"format": "yyyy-MM-dd", "timestamp": True, "required": True,
"width": "", "hidden": False, "hiddenOnAdd": False,
"fieldNote": "", "autoWidth": 50 # 半行
}, required=True)
widgets.append(card)
used_types.add("date")
模式 C:半行布局(一行两字段)
一个 card 内放两个控件,每个 autoWidth 设为 50:
ts = int(time.time() * 1000)
card = {
"key": f"{ts + 1}_{random.randint(100000, 999999)}",
"type": "card",
"isAutoGrid": True,
"isContainer": True,
"list": [
{
"type": "input",
"name": "姓名",
"className": "form-input",
"icon": "icon-input",
"hideTitle": False,
"options": {
"width": "100%", "defaultValue": "", "required": True,
"dataType": None, "pattern": "", "patternMessage": "",
"placeholder": "", "clearable": False, "readonly": False,
"disabled": False, "fillRuleCode": "", "showPassword": False,
"unique": False, "hidden": False, "hiddenOnAdd": False,
"fieldNote": "", "autoWidth": 50 # 半行
},
"advancedSetting": {"defaultValue": {"type": "compose", "value": "", "format": "string", "allowFunc": True, "valueSplit": "", "customConfig": False}},
"remoteAPI": {"url": "", "executed": False},
"key": f"{ts}_{random.randint(100000, 999999)}",
"model": f"input_{ts}_{random.randint(100000, 999999)}",
"modelType": "main",
"rules": [{"required": True, "message": "${title}必须填写"}],
"isSubItem": False
},
{
"type": "phone",
"name": "手机",
"className": "form-input-phone",
"icon": "icon-mobile-phone",
"hideTitle": False,
"options": {
"width": "300px", "defaultValue": "", "required": False,
"placeholder": "", "readonly": False, "disabled": False,
"unique": False, "hidden": False, "showVerifyCode": False,
"hiddenOnAdd": False, "fieldNote": "", "autoWidth": 50 # 半行
},
"defaultRules": [
{"type": "phone", "message": "请输入正确的手机号码"},
{"type": "validator", "message": "", "trigger": "blur"}
],
"advancedSetting": {"defaultValue": {"type": "compose", "value": "", "format": "string", "allowFunc": True, "valueSplit": "", "customConfig": False}},
"remoteAPI": {"url": "", "executed": False},
"key": f"{ts}_{random.randint(100000, 999999)}",
"model": f"phone_{ts}_{random.randint(100000, 999999)}",
"modelType": "main",
"rules": [],
"isSubItem": False
}
],
"options": {},
"model": f"card_{ts + 1}_{random.randint(100000, 999999)}"
}
模式 D:带子表的表单
场景: 采购单主表 + 采购明细子表
子表控件直接放在 list 顶层(不需要 card):
sub_table_key = f"{int(time.time() * 1000)}_{random.randint(100000, 999999)}"
sub_table_model = f"sub_table_design_{int(time.time() * 1000)}_{random.randint(100000, 999999)}"
sub_table = {
"type": "sub-table-design",
"name": "采购明细",
"className": "form-sub-table",
"icon": "icon-table",
"hideTitle": False,
"class": ["data-j-editable-design"],
"isContainer": True,
"columns": [
{
"span": 12,
"list": [
{
"type": "input",
"name": "物品名称",
"className": "form-input",
"icon": "icon-input",
"hideTitle": False,
"options": {
"width": "100%", "defaultValue": "", "required": True,
"dataType": None, "pattern": "", "patternMessage": "",
"placeholder": "", "clearable": False, "readonly": False,
"disabled": False, "fillRuleCode": "", "showPassword": False,
"unique": False, "hidden": False, "hiddenOnAdd": False, "fieldNote": ""
},
"advancedSetting": {"defaultValue": {"type": "compose", "value": "", "format": "string", "allowFunc": True, "valueSplit": "", "customConfig": False}},
"remoteAPI": {"url": "", "executed": False},
"key": f"{int(time.time() * 1000)}_{random.randint(100000, 999999)}",
"model": f"input_{int(time.time() * 1000)}_{random.randint(100000, 999999)}",
"modelType": "main",
"rules": [{"required": True, "message": "${title}必须填写"}],
"isSubItem": True,
"subOptions": {"width": "200px", "parentKey": sub_table_key}
},
{
"type": "number",
"name": "数量",
"className": "form-number",
"icon": "icon-number",
"hideTitle": False,
"options": {
"width": "", "required": False, "defaultValue": 0,
"placeholder": "", "controls": False,
"min": 0, "minUnlimited": True, "max": 100, "maxUnlimited": True,
"step": 1, "disabled": False, "controlsPosition": "right",
"unitText": "", "unitPosition": "suffix", "showPercent": False,
"align": "left", "hidden": False, "hiddenOnAdd": False, "fieldNote": ""
},
"advancedSetting": {"defaultValue": {"type": "compose", "value": "", "format": "number", "allowFunc": True, "valueSplit": "", "customConfig": False}},
"remoteAPI": {"url": "", "executed": False},
"key": f"{int(time.time() * 1000)}_{random.randint(100000, 999999)}",
"model": f"number_{int(time.time() * 1000)}_{random.randint(100000, 999999)}",
"modelType": "main",
"rules": [],
"isSubItem": True,
"subOptions": {"width": "200px", "parentKey": sub_table_key}
}
]
},
{
"span": 12,
"list": [
{
"type": "money",
"name": "单价",
"className": "form-money",
"icon": "icon-money",
"hideTitle": False,
"options": {
"width": "180px", "placeholder": "请输入金额",
"required": False, "unitText": "元", "unitPosition": "suffix",
"precision": 2, "hidden": False, "disabled": False,
"hiddenOnAdd": False, "fieldNote": ""
},
"advancedSetting": {"defaultValue": {"type": "compose", "value": "", "format": "number", "allowFunc": True, "valueSplit": "", "customConfig": False}},
"remoteAPI": {"url": "", "executed": False},
"key": f"{int(time.time() * 1000)}_{random.randint(100000, 999999)}",
"model": f"money_{int(time.time() * 1000)}_{random.randint(100000, 999999)}",
"modelType": "main",
"rules": [],
"isSubItem": True,
"subOptions": {"width": "200px", "parentKey": sub_table_key}
}
]
}
],
"options": {
"isWordStyle": False, "isWordInnerGrid": False, "gutter": 0,
"columnNumber": 2, "operationMode": 1, "justify": "start", "align": "top",
"defaultValue": [], "subTableName": "", "defaultRows": 0,
"showCheckbox": True, "showNumber": True, "showRowButton": False,
"allowAdd": True, "autoHeight": True, "defaultValType": "none",
"hidden": False, "hiddenOnAdd": False, "required": False, "fieldNote": ""
},
"advancedSetting": {"defaultValue": {"type": "compose", "value": "", "format": "string", "allowFunc": True, "valueSplit": "", "customConfig": True}},
"key": sub_table_key,
"model": sub_table_model,
"modelType": "main",
"rules": [],
"isSubItem": False
}
# 直接加入 list 顶层(不需要 card 容器)
widgets.append(sub_table)
used_types.add("sub-table-design")
子表控件要点:
- 子控件
isSubItem: True - 子控件有
subOptions: { "width": "200px", "parentKey": "子表的key" } - 子控件的 options 没有
autoWidth字段 columns数组中每个元素有span(栅格宽度)和list(控件列表)
模式 E:多子表订单表单
场景: 订单表(主表 + 商品明细 + 收款记录 + 发货记录 三个子表)
在模式 A 基础上,使用 make_widget + make_card + make_sub_table 分离构建:
def make_widget(widget_type, name, class_name, icon, options, required=False, is_sub=False, parent_key=None, extra=None):
"""创建控件(支持主表和子表控件,type 中的 - 自动转 _)"""
ts = int(time.time() * 1000)
rnd1 = random.randint(100000, 999999)
rnd2 = random.randint(100000, 999999)
key = f"{ts}_{rnd1}"
safe_type = widget_type.replace('-', '_')
model = f"{safe_type}_{ts}_{rnd2}"
time.sleep(0.003)
fmt = "number" if widget_type in ("number", "integer", "money", "slider") else "string"
custom = widget_type in ("radio", "checkbox", "select", "link-record", "sub-table-design")
w = {
"type": widget_type, "name": name, "className": class_name, "icon": icon,
"hideTitle": False, "options": options,
"remoteAPI": {"url": "", "executed": False},
"key": key, "model": model, "modelType": "main",
"rules": [{"required": True, "message": "${title}必须填写"}] if required else [],
"isSubItem": is_sub
}
# link-field 不需要 advancedSetting(其他控件都需要)
if widget_type != "link-field":
w["advancedSetting"] = {"defaultValue": {
"type": "compose", "value": "", "format": fmt,
"allowFunc": True, "valueSplit": "," if custom else "", "customConfig": custom
}}
if is_sub and parent_key:
w["subOptions"] = {"width": "200px", "parentKey": parent_key}
if extra:
w.update(extra)
return w, key, model
def make_card(*widgets_list):
"""创建 card 容器,支持放入 1~2 个控件"""
ts = int(time.time() * 1000)
rnd = random.randint(100000, 999999)
time.sleep(0.003)
return {
"key": f"{ts}_{rnd}", "type": "card", "isAutoGrid": True,
"isContainer": True, "list": list(widgets_list),
"options": {}, "model": f"card_{ts}_{rnd}"
}
def make_sub_table(name, sub_widgets):
"""创建子表,sub_widgets 为子控件列表"""
ts = int(time.time() * 1000)
rnd1 = random.randint(100000, 999999)
rnd2 = random.randint(100000, 999999)
st_key = f"{ts}_{rnd1}"
st_model = f"sub_table_design_{ts}_{rnd2}"
time.sleep(0.003)
return {
"type": "sub-table-design", "name": name,
"className": "form-sub-table", "icon": "icon-table",
"hideTitle": False, "class": ["data-j-editable-design"],
"isContainer": True,
"columns": [{"span": 24, "list": sub_widgets}],
"options": {
"isWordStyle": False, "isWordInnerGrid": False, "gutter": 0,
"columnNumber": 2, "operationMode": 1, "justify": "start", "align": "top",
"defaultValue": [], "subTableName": "", "defaultRows": 0,
"showCheckbox": True, "showNumber": True, "showRowButton": False,
"allowAdd": True, "autoHeight": True, "defaultValType": "none",
"hidden": False, "hiddenOnAdd": False, "required": False, "fieldNote": ""
},
"advancedSetting": {"defaultValue": {"type": "compose", "value": "", "format": "string", "allowFunc": True, "valueSplit": "", "customConfig": True}},
"key": st_key, "model": st_model, "modelType": "main",
"rules": [], "isSubItem": False
}, st_key
# 构建子表(先创建子表获取 key,再创建子控件绑定 parentKey)
sub_widgets = []
# 先占位获取子表 key
sub, sub_key = make_sub_table("商品明细", [])
# 创建子控件
w, _, _ = make_widget("input", "商品名称", "form-input", "icon-input",
{...}, required=True, is_sub=True, parent_key=sub_key)
sub_widgets.append(w)
w, _, _ = make_widget("integer", "数量", "form-integer", "icon-integer",
{...}, required=True, is_sub=True, parent_key=sub_key)
sub_widgets.append(w)
# 更新子表的 columns
sub["columns"] = [{"span": 24, "list": sub_widgets}]
all_widgets.append(sub)
多子表要点:
- 每个子表独立调用
make_sub_table()获取st_key - 子控件创建时传
is_sub=True, parent_key=st_key - 子控件的 options 没有
autoWidth字段 - 多个子表按顺序追加到
all_widgets(顶层 list) config.hasWidgets中只需加一次"sub-table-design"
执行模板(完整脚本框架)
生成脚本时遵循此框架:
1. 导入依赖 (urllib, json, time, random)
2. 配置 API_BASE 和 TOKEN
3. 定义工具函数 (api_request, make_widget, make_card, make_sub_table)
4. 构建各字段控件(主表 + 子表)
5. 组装 design_json (list + config)
6. 检查表单是否已存在(GET /desform/queryByCode)
7. 不存在则创建(POST /desform/add),再查询获取 ID
8. 保存设计(PUT /desform/edit),updateCount 传当前值
9. 输出结果
关键注意事项:
time.sleep(0.003)确保每个控件的时间戳不同config.hasWidgets必须包含所有使用到的控件 type(包括card)config.titleField必须指向一个实际存在的控件 model- 必填字段需要同时设置
options.required = True和rules = [{"required": True, ...}] - 数字类控件的 advancedSetting.defaultValue.format 应为
"number" POST /desform/add返回result: null,必须用GET /desform/queryByCode获取 IDupdateCount传当前数据库值(不是 +1),后端自动递增- 先检查表单是否存在,避免重复创建报
该code已存在错误
实战踩坑清单(className / icon 易错汇总)
生成表单时最容易出错的是 className 和 icon,以下为实测验证的正确值:
| 控件 type | className | icon | 特殊说明 |
|---|---|---|---|
link-record |
form-link-record |
icon-link |
不是 icon-link-record! |
link-field |
form-link-field |
icon-field |
不是 icon-link-field! |
sub-table-design |
form-sub-table |
icon-table |
不是 form-sub-table-design / icon-sub-table-design! |
divider |
form-divider |
icon-fengexian |
link-record 踩坑要点
advancedSetting.defaultValue.customConfig必须为trueallowView、allowEdit、allowAdd、allowSelect必须全部设为true(4 个操作选项默认全部勾选)titleField必须填源表的真实标题字段 model(如input_xxx),不能留空showFields建议填入源表中需要展示的字段 model 列表- 跨表单关联时,必须先创建被引用的表单,然后查询获取其字段 model,再构建引用方的 link-record
link-field 踩坑要点
- link-field 没有
advancedSetting— 与其他控件不同 linkRecordKey填的是 link-record 的 key(如1773457559119_461003),不是 modelfieldType必须填来源字段的真实控件类型(如"input","select-user","money"),不能一律写"input"fieldOptions需包含源字段类型相关的 options 子集,例如:- select-user:
{"multiple": false, "customReturnField": "username"} - select (多选):
{"multiple": true} - 普通 input/money 等:
{}即可
- select-user:
sub-table-design 踩坑要点
- options 必须包含
allowAdd: true,否则子表没有"添加"按钮 - 完整 options 字段(缺一不可):
{ "isWordStyle": false, "isWordInnerGrid": false, "gutter": 0, "columnNumber": 2, "operationMode": 1, "justify": "start", "align": "top", "defaultValue": [], "subTableName": "", "defaultRows": 0, "showCheckbox": true, "showNumber": true, "showRowButton": false, "allowAdd": true, "autoHeight": true, "defaultValType": "none", "hidden": false, "hiddenOnAdd": false, "required": false, "fieldNote": "" } - 子表内可以放 link-record + link-field,实现子表行级关联选择
跨表单关联的正确流程(多表单批量创建)
1. 先创建基础表单(如商品表、仓库表、供应商表)
2. 查询基础表单的 designJson,提取字段 model 和 titleField
3. 构建业务表单(如入库单)时:
a. link-record.options.sourceCode = 基础表单的 desformCode
b. link-record.options.titleField = 基础表单的 titleField model
c. link-record.options.showFields = [基础表单中需要展示的字段 model 列表]
d. link-field.options.linkRecordKey = 同表单中 link-record 控件的 KEY
e. link-field.options.showField = 基础表单中要自动填充的字段 MODEL
f. link-field.options.fieldType = 该字段的实际控件类型
Python 中查询基础表单字段的方法
def get_form_fields(form_code):
"""查询表单设计JSON,提取字段 model/key/type"""
# 注意:queryByCode 不可靠,推荐用 queryByIdOrCode 或 desform_utils.py 中的 get_form_fields
q = api_request(f'/desform/queryByIdOrCode?desformCode={form_code}', method='GET')
design = json.loads(q['result']['desformDesignJson'])
title_field = design['config']['titleField']
fields = {}
def extract(items):
for item in items:
if item.get('type') == 'card' and 'list' in item:
extract(item['list'])
elif 'model' in item and item.get('type') != 'card':
fields[item['name']] = {
'model': item['model'],
'key': item['key'],
'type': item['type']
}
extract(design.get('list', []))
return title_field, fields
模式 F:使用 desform_utils.py 的跨表单 CRM 系统(推荐)
场景: CRM 客户管理系统(4 个关联表单),使用共通工具库 desform_utils.py
命名规则: 模块名作为前缀,如
crm_customer(不是customer_crm)
"""CRM系统 - 4个关联表单"""
import sys, time
sys.path.insert(0, r'{后端项目根目录}')
from desform_utils import *
init_api('https://boot3.jeecg.com/jeecgboot', 'your-token')
# ---- 1/4 客户信息(基础表单,被其他表单引用)----
fid1, uc1 = find_or_create_form('客户信息', 'crm_customer')
w0, _, m0 = INPUT('客户名称', required=True)
w1, _, _ = AUTONUMBER('客户编号', prefix='CRM')
w2, _, _ = SELECT('客户类型', ['企业客户', '个人客户'], required=True)
w3, _, _ = SELECT('所属行业', ['IT互联网', '金融', '制造业', '教育', '医疗', '房地产', '零售', '其他'])
w4, _, _ = SELECT('客户来源', ['官网', '转介绍', '电话营销', '展会', '广告', '社交媒体', '其他'])
w5, _, _ = RADIO('客户等级', ['A-重要', 'B-普通', 'C-一般'])
w6, _, _ = RADIO('客户状态', ['潜在客户', '意向客户', '成交客户', '流失客户'])
w7, _, _ = PHONE('联系电话')
w8, _, _ = EMAIL('邮箱')
w9, _, _ = AREA('所在地区')
w10, _, _ = INPUT('详细地址')
w11, _, _ = USER('负责销售', required=True)
w12, _, _ = DEPART('所属部门')
w13, _, _ = TEXTAREA('备注')
save_design(fid1, 'crm_customer', [w0,w1,w2,w3,w4,w5,w6,w7,w8,w9,w10,w11,w12,w13], m0, uc1)
# 查询客户表单字段,供后续表单关联引用
time.sleep(1)
cust_tf, cust_fields = get_form_fields('crm_customer')
cust_show = [cust_fields['客户编号']['model'], cust_fields['联系电话']['model']]
# ---- 2/4 联系人(关联客户)----
fid2, uc2 = find_or_create_form('联系人', 'crm_contact')
w0, _, m0 = INPUT('联系人姓名', required=True)
w1, _, _ = LINK_RECORD('所属客户', 'crm_customer', cust_tf, cust_show, required=True)
lr_key = w1['list'][0]['key'] # 获取 link-record 的 key,供 link-field 引用
w2, _, _ = LINK_FIELD('客户编号', lr_key, cust_fields['客户编号']['model'], field_type='auto-number')
w3, _, _ = INPUT('职务')
w4, _, _ = INPUT('部门')
w5, _, _ = PHONE('手机号码', required=True)
w6, _, _ = EMAIL('邮箱')
w7, _, _ = SWITCH('是否决策人')
w8, _, _ = TEXTAREA('备注')
save_design(fid2, 'crm_contact', [w0,w1,w2,w3,w4,w5,w6,w7,w8], m0, uc2)
# ---- 3/4 商机管理(关联客户 + 分隔符 + 金额 + 滑块)----
fid3, uc3 = find_or_create_form('商机管理', 'crm_opportunity')
w0, _, m0 = INPUT('商机名称', required=True)
w1, _, _ = AUTONUMBER('商机编号', prefix='BIZ')
w2, _, _ = LINK_RECORD('关联客户', 'crm_customer', cust_tf, cust_show, required=True)
lr_key = w2['list'][0]['key']
w3, _, _ = LINK_FIELD('客户编号', lr_key, cust_fields['客户编号']['model'], field_type='auto-number')
w4, _, _ = DIVIDER('商机详情')
w5, _, _ = SELECT('商机阶段', ['初步接触', '需求确认', '方案报价', '商务谈判', '赢单', '输单'], required=True)
w6, _, _ = MONEY('预计金额', required=True)
w7, _, _ = DATE('预计成交日期')
w8, _, _ = USER('负责人', required=True)
w9, _, _ = USER('协作人', multiple=True)
w10, _, _ = SLIDER('赢单概率', show_input=True)
w11, _, _ = TEXTAREA('备注')
save_design(fid3, 'crm_opportunity', [w0,w1,w2,w3,w4,w5,w6,w7,w8,w9,w10,w11], m0, uc3)
# ---- 4/4 跟进记录(关联客户 + 默认当前用户)----
fid4, uc4 = find_or_create_form('跟进记录', 'crm_follow_up')
w0, _, m0 = INPUT('跟进主题', required=True)
w1, _, _ = LINK_RECORD('关联客户', 'crm_customer', cust_tf, cust_show, required=True)
lr_key = w1['list'][0]['key']
w2, _, _ = LINK_FIELD('客户名称', lr_key, cust_fields['客户名称']['model'], field_type='input')
w3, _, _ = RADIO('跟进方式', ['电话', '拜访', '微信', '邮件', '会议', '其他'], required=True)
w4, _, _ = DATE('跟进日期', required=True)
w5, _, _ = DATE('下次跟进日期')
w6, _, _ = USER('跟进人', required=True, default_login=True)
w7, _, _ = TEXTAREA('跟进内容', required=True)
w8, _, _ = FILE('附件')
save_design(fid4, 'crm_follow_up', [w0,w1,w2,w3,w4,w5,w6,w7,w8], m0, uc4)
# ---- 输出菜单 + 角色授权 SQL ----
print(gen_menu_sql('crm_menu', 'CRM客户管理', [
('crm_customer_menu', '客户信息', 'crm_customer', 1),
('crm_contact_menu', '联系人', 'crm_contact', 2),
('crm_opportunity_menu', '商机管理', 'crm_opportunity', 3),
('crm_follow_up_menu', '跟进记录', 'crm_follow_up', 4),
]))
跨表单关联要点:
- 先创建被引用的基础表单 →
save_design→time.sleep(1)→get_form_fields获取字段信息 LINK_RECORD需要源表编码、titleField、showFieldsLINK_FIELD需要 link-record 的 key(通过w['list'][0]['key']获取)和源字段的 model + field_type- link-record 的 4 个操作选项(allowView/allowEdit/allowAdd/allowSelect)已在 desform_utils.py 中默认全部开启