具体场景

以私钥的输入为例,用户既可以直接输入私钥内容,也可以上传指定的文件。 双重输入1 双重输入2

如何实现

用 tabs 或 select 控件自由切换输入/上传方式,是 AMis 推荐的模式。

              {
                "type": "button",
                "label": "新增",
                "level": "primary",
                "className": "m-r-xs",
                "behavior": "Insert",
                "onEvent": {
                  "click": {
                    "actions": [
                      {
                        "actionType": "dialog",
                        "dialog": {
                          "type": "dialog",
                          "title": "新增数据",
                          "body": [
                            {
                              "id": "u:2b9e1961e997",
                              "type": "form",
                              "title": "新增数据",
                              "mode": "flex",
                              "labelAlign": "top",
                              "dsType": "api",
                              "feat": "Insert",
                              "body": [
                                {
                                  "name": "name",
                                  "label": "name",
                                  "row": 0,
                                  "type": "input-text",
                                  "id": "u:649fb9d93c2e"
                                },
                                {
                                  "type": "select",
                                  "label": "pem私钥输入方式",
                                  "name": "input_mode",
                                  "row": 1,
                                  "options": [
                                    {
                                      "label": "直接输入",
                                      "value": "text"
                                    },
                                    {
                                      "label": "上传文件",
                                      "value": "file"
                                    }
                                  ],
                                  "id": "u:edd5de2054f4",
                                  "multiple": false,
                                  "size": "sm",
                                  "selectFirst": true
                                },
                                {
                                  "type": "textarea",
                                  "label": "pem私钥",
                                  "name": "private_key_pem",
                                  "row": 2,
                                  "colSize": "1",
                                  "id": "u:2bb518c4e0cc",
                                  "minRows": 3,
                                  "maxRows": 20,
                                  "visibleOn": "${input_mode=== 'text'}"
                                },
                                {
                                  "type": "input-file",
                                  "label": "pem私钥文件上传",
                                  "autoUpload": true,
                                  "proxy": true,
                                  "uploadType": "asForm",
                                  "name": "private_key_pem_file_asbase64",
                                  "row": 3,
                                  "colSize": "1",
                                  "id": "u:5ba0338375fe",
                                  "btnLabel": "文件上传",
                                  "multiple": false,
                                  "useChunk": false,
                                  "accept": "",
                                  "drag": false,
                                  "asBlob": false,
                                  "asBase64": true,
                                  "formType": "asBase64",
                                  "onEvent": {},
                                  "visibleOn": "${input_mode==='file'}"
                                },
                                {
                                  "name": "description",
                                  "label": "描述",
                                  "row": 4,
                                  "type": "input-text",
                                  "id": "u:be1465a0b2ee"
                                }
                              ],
                              "resetAfterSubmit": true,
                              "actions": [
                                {
                                  "type": "button",
                                  "actionType": "cancel",
                                  "label": "取消"
                                },
                                {
                                  "type": "button",
                                  "actionType": "submit",
                                  "label": "提交",
                                  "level": "primary"
                                }
                              ],
                              "onEvent": {
                                "submitSucc": {
                                  "actions": [
                                    {
                                      "actionType": "search",
                                      "groupType": "component",
                                      "componentId": "u:86eeb4c2dc51"
                                    }
                                  ]
                                }
                              }
                            }
                          ],
                          "size": "md",
                          "actions": [
                            {
                              "type": "button",
                              "actionType": "cancel",
                              "label": "取消",
                              "id": "u:f4d7b414e7b1"
                            },
                            {
                              "type": "button",
                              "actionType": "submit",
                              "label": "提交",
                              "level": "primary",
                              "id": "u:3cd978b191bd",
                              "onEvent": {
                                "click": {
                                  "weight": 0,
                                  "actions": [
                                    {
                                      "ignoreError": false,
                                      "outputVar": "responseResult",
                                      "actionType": "ajax",
                                      "options": {},
                                      "api": {
                                        "url": "/admin/cloud/v1/rsa_add",
                                        "method": "post",
                                        "requestAdaptor": "if (!(api.data.private_key_pem) && api.data.private_key_pem_file_asbase64) {\n  console.log(api.data.private_key_pem_file_asbase64);\n  base64 = api.data.private_key_pem_file_asbase64.split(',')[1];\n  api.data['private_key_pem'] = atob(base64);\n}\n\nreturn api;",
                                        "adaptor": "",
                                        "messages": {},
                                        "dataType": "json",
                                        "data": {
                                          "name": "${name}",
                                          "private_key_pem": "${private_key_pem}",
                                          "private_key_pem_file_asbase64": "${private_key_pem_file_asbase64}",
                                          "description": "${description}"
                                        }
                                      }
                                    }
                                  ]
                                }
                              }
                            }
                          ],
                          "actionType": "dialog",
                          "id": "u:af133b5ba74c",
                          "showCloseButton": true,
                          "closeOnOutside": false,
                          "closeOnEsc": false,
                          "showErrorMsg": true,
                          "showLoading": true,
                          "draggable": false
                        }
                      }
                    ]
                  }
                },
                "id": "u:5bd82685f6b5"
              }

Textarea 多行文本输入框

InputFile 文件上传: 作为表单项上传


实现细节

  1. 通用流程

AMis 表单设计成不管用户上传文件还是直接输入,最终都将 SSH 的内容(文本)作为字符串字段提交给后端,那么后端只需要接受一个字符串参数就可以(比如 JSON 字段 ssh_private_key 或类似)。这种方式最简单,后端无需区分来源。

通用流程如下:

  • 用户上传文件 → 前端用 JS 读取文件内容为字符串 → 填充到表单的字符串字段
  • 用户直接输入 → 也是同一个表单字符串字段
  • 提交时都是同一个参数

服务端只需接收字符串,只要能处理好 SSH 密钥的内容即可,不额外修改上传文件相关逻辑。


  1. 发送适配器
function(api, context) {
  if (!(api.data.private_key_pem) && api.data.private_key_pem_file_asbase64) {
    base64 = api.data.private_key_pem_file_asbase64.split(',')[1];  // 获取 base64
    api.data['private_key_pem'] = atob(base64);
  }

  return api;
}

作为表单项上传,配置数据格式为 asBase64 ,实际上得到的是Data URL格式(而不是只包含纯base64字符串),一般形如:

data:MIME类型;编码,data

举个例子:

data:application/x-x509-ca-cert;base64,CgotLS0tLUJFR0l...
  • data: 说明后面这是一个 Data URL
  • application/x-x509-ca-cert: MIME 类型,指明数据的文件类型,本例是 x509 CA 证书
  • base64: 后面的数据使用了 base64 编码
  • , 分隔,后面是真正的 base64 字符串
  • CgotLS0tLUJFR0l... 这是真正的原始数据经过 base64 编码后的部分

这种格式的目的是为了让浏览器在某些场合,可以直接把 base64 数据作为图片、文件、Blob、下载资源等直接使用,而不需要另外上传到服务器。

为什么不是直接输出 Base64?

如果只返回纯 base64 内容,那么你拿到的字符串只有:

CgotLS0tLUJFR0l...

这样你无法得知这是什么文件、什么类型,也难以直接用在 HTML 的 src/href 属性等需要完整 Data URL 格式的地方。

有了前缀(Data URL)之后你可以:

  • 直接赋值给 <img src="这里">
  • 立即 start 下载,比如 <a download href="这里">
  • 方便前端使用 fileType 或内容类型解析

总结

带上这些前缀是为了让数据格式自描述,更方便前端使用和处理!

Last modified: May 26, 2025

Comments

Write a Reply or Comment

Your email address will not be published.