互动信箱功能
文档:https://yxw7ds3a1o.apifox.cn/
优先确认是否拥有权限

如图,类似反馈提交的表单功能:

后台配置

API:新增 反馈
https://yxw7ds3a1o.apifox.cn/292181455e0
| 请求类型 | API 地址 |
|---|---|
| POST | /interactive-api/interaction/mailbox/web/add/{appId} |
json
[
{ "field": "id_Fcldm96uw5l2acc", "title": "姓名", "value": "1" },
{ "field": "id_Fts4m96uw6ctafc", "title": "电子邮箱", "value": "1" },
{ "field": "id_F1w8m9i3zfaoakc", "title": "手机号码", "value": "1" },
{ "field": "id_Fcfmmdy7ypduacc", "title": "意见标题", "value": "1" },
{ "field": "id_Flxsm9i40ddpanc", "title": "您的意见内容", "value": "1" }
]提示
示例代码仅做参考
ts
function getAppId() {
return 30007;
}
// @ts-ignore
window.handleSubmitFeedback = function () {
const name = document.getElementById("feedback-name") as HTMLInputElement;
const email = document.getElementById("feedback-email") as HTMLInputElement;
const gender = document.querySelector(
'input[name="gender"]:checked'
) as HTMLInputElement;
const address = document.getElementById(
"feedback-address"
) as HTMLInputElement;
const age = document.getElementById("feedback-age") as HTMLInputElement;
const letterTheme = document.getElementById(
"feedback-letter-theme"
) as HTMLInputElement;
const phone = document.getElementById("feedback-phone") as HTMLInputElement;
const letterType = document.querySelector(
'input[name="letter-type"]:checked'
) as HTMLInputElement;
const content = document.getElementById(
"feedback-content"
) as HTMLTextAreaElement | null;
const checkList: [HTMLInputElement | null, string][] = [
[name, "姓名"],
[email, "电子邮件"],
[gender, "性别"],
[address, "联系地址"],
[age, "年龄"],
[letterTheme, "来信主题"],
[phone, "电话"],
[letterType, "信件类型"],
];
for (const [element, label] of checkList) {
if (!element) {
return showError(`${label} 元素异常`);
}
if (!element.value || element.value.trim() === "") {
return showError(`${label} 不能为空`);
}
}
// content 可以为空
const contentValue = (content && content.value) || "";
const body = [
{ field: "id_Fcldm96uw5l2acc", title: "姓名", value: name.value },
{ field: "id_Fts4m96uw6ctafc", title: "电子邮件", value: email.value },
{ field: "id_Frmgmgj59hmbacc", title: "性别", value: gender.value },
{ field: "id_Fv7smgj5bk9vafc", title: "联系地址", value: address.value },
{ field: "id_Fkhrmgj5bvjqaic", title: "年龄", value: age.value },
{
field: "id_F0o2mgj5d0hpaoc",
title: "来信主题",
value: letterTheme.value,
},
{ field: "id_Fjhomgj5crk1alc", title: "电话", value: phone.value },
{ field: "id_F5zxmgj5e216axc", title: "信件类型", value: letterType.value },
{ field: "id_Fdp8mgj5ddmmauc", title: "具体内容", value: contentValue },
];
addFeedback(getAppId(), body, {
onSuccess: (data) => {
const res = JSON.parse(data);
if (res.code === 200) {
showError(res.message || "提交成功");
handleClearFeedback();
} else {
showInfo(res.message || "提交失败");
}
},
onError: (error) => {
showError("提交失败,请稍后尝试");
console.error(error);
},
});
};ts
import { HttpRequestDataType, post } from "../util/http";
interface CallBack {
onSuccess?: (data: string) => void;
onError?: (error: any) => void;
}
export function addFeedback(
APPID: string | number = 30007,
data: HttpRequestDataType,
callback?: CallBack
) {
const url = `/interactive-api/interaction/mailbox/web/add/${APPID}`;
post(url, data, {
onSuccess: (data) => {
callback?.onSuccess?.(data);
},
onError: (error) => {
callback?.onError?.(error);
},
});
}ts
type IHttpHeaders = Record<string, string>;
interface JSONType {
[key: string]: string | number | boolean | null | undefined | JSONType;
}
export type HttpRequestDataType =
| JSONType
| FormData
| undefined
| Array<JSONType>;
interface IHttpOptions {
method?: "POST" | "GET" | "PUT" | "DELETE";
headers?: IHttpHeaders;
onSuccess: (data: string) => void;
onError: (error: HttpError) => void;
}
class HttpError extends Error {
public status: number;
public responseText?: string;
constructor(options: {
message?: string;
status: number;
responseText?: string;
}) {
super(options.message);
this.status = options.status;
this.responseText = options.responseText;
}
}
export const request = (
url: string,
data: HttpRequestDataType,
config: IHttpOptions
) => {
const method =
config.method && typeof config.method === "string"
? config.method.toLocaleUpperCase()
: "GET";
const http = new XMLHttpRequest();
let url2 = url;
if (method === "GET") {
if (data instanceof FormData) {
throw new Error("GET 的 data **不** 应该是 FormData");
}
const queryString = Array.isArray(data) ? "" : dataToQueryString(data);
url2 = queryString ? url + "?" + queryString : url;
}
http.open(method, url2, true);
const headers = combinationHeader(config.headers, method);
// 除了 Content Type 需要单独处理,其他直接设置 Request Header
let contentType = "";
for (const key in headers) {
if (key.toLocaleUpperCase() === "CONTENT-TYPE") {
contentType = headers[key];
} else {
http.setRequestHeader(key, headers[key]);
}
}
if (method === "POST" || method === "PUT") {
const __data__ = data || {};
const isFromData = data instanceof FormData;
if (contentType === "application/json") {
onPostByJson(http, __data__ as JSONType);
} else if (contentType === "application/x-www-form-urlencoded") {
onPostByUrlEncoded(http, __data__ as JSONType);
} else if (contentType === "multipart/form-data") {
if (isFromData) {
onPostByFormData(http, data);
} else {
throw new Error("data 不是 FormData");
}
} else {
throw new Error("未实现的 content type: " + contentType);
}
} else {
http.send();
}
http.onreadystatechange = function () {
// readyState
// 0: 未初始化
// 1: 已打开
// 2: 已发送
// 3: 接收中
// 4: 完成
if (http.readyState === 4) {
if (http.status === 200) {
config.onSuccess(http.responseText);
} else {
config.onError(
new HttpError({
message: "请求失败",
status: http.status,
responseText: http.responseText,
})
);
}
}
};
};
function combinationHeader(header: IHttpHeaders = {}, method: string) {
const defaultOptions: IHttpHeaders = {
"Content-Type": "application/json",
};
let headers: Record<string, string> = {};
if (method === "POST" || method === "PUT") {
headers = { ...defaultOptions };
}
headers = { ...headers, ...header };
return headers;
}
export const get = (
url: string,
data: JSONType | undefined,
config: IHttpOptions
) => {
return request(url, data, { ...config, method: "GET" });
};
export const post = (
url: string,
data: HttpRequestDataType,
config: IHttpOptions
) => {
return request(url, data, { ...config, method: "POST" });
};
// ------------------------------------------------------------
function onPostByJson(xhr: XMLHttpRequest, data: JSONType) {
xhr.setRequestHeader("Content-Type", "application/json");
const body = JSON.stringify(data);
xhr.send(body);
}
function onPostByUrlEncoded(xhr: XMLHttpRequest, data: JSONType) {
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
const queryString = dataToQueryString(data);
xhr.send(queryString);
}
function onPostByFormData(xhr: XMLHttpRequest, data: FormData) {
if (!data) throw new Error("data is required");
if (data instanceof FormData === false) {
throw new Error("data must be a FormData instance");
}
xhr.setRequestHeader("Content-Type", "multipart/form-data");
xhr.send(data);
}
function dataToQueryString(data?: JSONType): string {
if (!data) {
return "";
}
const params: string[] = [];
for (const key in data) {
const value = data[key];
params.push(`${key}=${value}`);
}
return params.join("&");
}注意!field 字段怎么来?

field 并不是“配置表单”时候出现的字段!
field 并不是“配置表单”时候出现的字段!
field 并不是“配置表单”时候出现的字段!
API:获取 form 配置
https://yxw7ds3a1o.apifox.cn/292178118e0
| 请求类型 | API 地址 |
|---|---|
| GET | /interactive-api/interaction/mailbox/web/mailboxConfig/{appId} |
提示
示例代码仅做参考
ts
window.addEventListener("DOMContentLoaded", function () {
getFeedbackFormConfiguration(getAppId(), {
onSuccess(data) {
// console.log(data);
},
});
});ts
import { get, HttpRequestDataType, post } from "../util/http";
export function getFeedbackFormConfiguration(
APPID: string | number = 30007,
callback?: CallBack
) {
const url = `/interactive-api/interaction/mailbox/web/mailboxConfig/${APPID}`;
get(url, undefined, {
onSuccess: (data) => {
callback?.onSuccess?.(data);
},
onError: (error) => {
callback?.onError?.(error);
},
});
}
interface CallBack {
onSuccess?: (data: string) => void;
onError?: (error: any) => void;
}ts
type IHttpHeaders = Record<string, string>;
interface JSONType {
[key: string]: string | number | boolean | null | JSONType;
}
export type HttpRequestDataType =
| JSONType
| FormData
| undefined
| Array<JSONType>;
interface IHttpOptions {
method?: "POST" | "GET" | "PUT" | "DELETE";
headers?: IHttpHeaders;
onSuccess: (data: string) => void;
onError: (error: any) => void;
}
class HttpError extends Error {
constructor(message: string, public status: number) {
super(message);
}
}
export const request = (
url: string,
data: HttpRequestDataType,
config: IHttpOptions
) => {
const method =
config.method && typeof config.method === "string"
? config.method.toLocaleUpperCase()
: "GET";
const http = new XMLHttpRequest();
let url2 = url;
if (method === "GET") {
if (data instanceof FormData) {
throw new Error("GET 的 data **不** 应该是 FormData");
}
const queryString = Array.isArray(data) ? "" : dataToQueryString(data);
url2 = queryString ? url + "?" + queryString : url;
}
http.open(method, url2, true);
const headers = combinationHeader(config.headers, method);
// 除了 Content Type 需要单独处理,其他直接设置 Request Header
let contentType = "";
for (const key in headers) {
if (key.toLocaleUpperCase() === "CONTENT-TYPE") {
contentType = headers[key];
} else {
http.setRequestHeader(key, headers[key]);
}
}
if (method === "POST" || method === "PUT") {
const __data__ = data || {};
const isFromData = data instanceof FormData;
if (contentType === "application/json") {
onPostByJson(http, __data__ as JSONType);
} else if (contentType === "application/x-www-form-urlencoded") {
onPostByUrlEncoded(http, __data__ as JSONType);
} else if (contentType === "multipart/form-data") {
if (isFromData) {
onPostByFormData(http, data);
} else {
throw new Error("data 不是 FormData");
}
} else {
throw new Error("未实现的 content type: " + contentType);
}
} else {
http.send();
}
http.onreadystatechange = function () {
// readyState
// 0: 未初始化
// 1: 已打开
// 2: 已发送
// 3: 接收中
// 4: 完成
if (http.readyState === 4) {
if (http.status === 200) {
config.onSuccess(http.responseText);
} else {
config.onError(new HttpError("请求失败", http.status));
}
}
};
};
function combinationHeader(header: IHttpHeaders = {}, method: string) {
const defaultOptions: IHttpHeaders = {
"Content-Type": "application/json",
};
let headers: Record<string, string> = {};
if (method === "POST" || method === "PUT") {
headers = { ...defaultOptions };
}
headers = { ...headers, ...header };
return headers;
}
export const get = (
url: string,
data: JSONType | undefined,
config: IHttpOptions
) => {
return request(url, data, { ...config, method: "GET" });
};
export const post = (
url: string,
data: HttpRequestDataType,
config: IHttpOptions
) => {
return request(url, data, { ...config, method: "POST" });
};
// ------------------------------------------------------------
function onPostByJson(xhr: XMLHttpRequest, data: JSONType) {
xhr.setRequestHeader("Content-Type", "application/json");
const body = JSON.stringify(data);
xhr.send(body);
}
function onPostByUrlEncoded(xhr: XMLHttpRequest, data: JSONType) {
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
const queryString = dataToQueryString(data);
xhr.send(queryString);
}
function onPostByFormData(xhr: XMLHttpRequest, data: FormData) {
if (!data) throw new Error("data is required");
if (data instanceof FormData === false) {
throw new Error("data must be a FormData instance");
}
xhr.setRequestHeader("Content-Type", "multipart/form-data");
xhr.send(data);
}
function dataToQueryString(data?: JSONType): string {
if (!data) {
return "";
}
const params: string[] = [];
for (const key in data) {
const value = data[key];
params.push(`${key}=${value}`);
}
return params.join("&");
}响应示例
json
{
"traceId": "1976221376620797952",
"code": 200,
"msg": "操作成功",
"message": "操作成功",
"data": [
{
"type": "input",
"field": "F291m96uw5l2abc",
"title": "姓名",
"info": "",
"$required": true,
"_fc_id": "id_Fcldm96uw5l2acc",
"name": "ref_Fkcfm96uw5l3adc",
"display": true,
"hidden": false,
"_fc_drag_tag": "input",
"props": {
"placeholder": "请输入",
"columnShow": true
}
},
{
"type": "input",
"field": "Fyalm96uw6ctaec",
"title": "电子邮件",
"info": "",
"$required": true,
"_fc_id": "id_Fts4m96uw6ctafc",
"name": "ref_F2f2m96uw6ctagc",
"display": true,
"hidden": false,
"_fc_drag_tag": "input",
"props": {
"placeholder": "请输入",
"columnShow": true
}
},
{
"type": "radio",
"field": "F0homgj59hmbabc",
"title": "性别",
"info": "",
"effect": {
"fetch": ""
},
"$required": true,
"props": {
"columnShow": true
},
"options": [
{
"label": "男",
"value": "male"
},
{
"label": "女",
"value": "female"
}
],
"_fc_id": "id_Frmgmgj59hmbacc",
"name": "ref_Fzy9mgj59hmbadc",
"display": true,
"hidden": false,
"_fc_drag_tag": "radio"
},
{
"type": "input",
"field": "Fgvhmgj5bk9vaec",
"title": "联系地址",
"info": "",
"$required": true,
"props": {
"columnShow": true
},
"_fc_id": "id_Fv7smgj5bk9vafc",
"name": "ref_F4xumgj5bk9vagc",
"display": true,
"hidden": false,
"_fc_drag_tag": "input"
},
{
"type": "input",
"field": "Fe8tmgj5bvjqahc",
"title": "年龄",
"info": "",
"$required": true,
"props": {
"columnShow": true
},
"_fc_id": "id_Fkhrmgj5bvjqaic",
"name": "ref_Fs66mgj5bvjqajc",
"display": true,
"hidden": false,
"_fc_drag_tag": "input"
},
{
"type": "input",
"field": "F9lvmgj5crk1akc",
"title": "电话",
"info": "",
"$required": true,
"props": {
"columnShow": true
},
"_fc_id": "id_Fjhomgj5crk1alc",
"name": "ref_Fjwxmgj5crk1amc",
"display": true,
"hidden": false,
"_fc_drag_tag": "input"
},
{
"type": "input",
"field": "Fjltmgj5d0hpanc",
"title": "来信主题",
"info": "",
"$required": true,
"props": {
"columnShow": true
},
"_fc_id": "id_F0o2mgj5d0hpaoc",
"name": "ref_Fh0rmgj5d0hpapc",
"display": true,
"hidden": false,
"_fc_drag_tag": "input"
},
{
"type": "radio",
"field": "Ftbemgj5e215awc",
"title": "信件类型",
"info": "",
"effect": {
"fetch": ""
},
"$required": true,
"props": {
"columnShow": true
},
"options": [
{
"label": "建议",
"value": "suggestion"
},
{
"label": "求助",
"value": "help"
},
{
"label": "投诉",
"value": "complaint"
},
{
"label": "咨询",
"value": "consultation"
},
{
"label": "其它",
"value": "other"
}
],
"_fc_id": "id_F5zxmgj5e216axc",
"name": "ref_Fz6hmgj5e216ayc",
"display": true,
"hidden": false,
"_fc_drag_tag": "radio"
},
{
"type": "input",
"field": "Flbnmgj5ddmmatc",
"title": "具体内容",
"info": "",
"$required": false,
"props": {
"type": "textarea",
"columnShow": true
},
"_fc_id": "id_Fdp8mgj5ddmmauc",
"name": "ref_Fg3gmgj5ddmmavc",
"display": true,
"hidden": false,
"_fc_drag_tag": "textarea"
}
],
"timestamp": "2025-10-09 17:41:07"
}API:回复列表
https://yxw7ds3a1o.apifox.cn/292178357e0
应该用不上
其他信息
参考已经上线的站点:
长寿人大-公众意见征集:https://www.cqcsrd.gov.cn/web/column/col5000843.html
重庆妇女网-维权咨询:https://www.cqwomen.org.cn/web/column/col1827873.html
todo
统一战线-部长直通车:https://www.cqtzb.gov.cn/kuai/Message/index
武隆区人大-人大信箱:http://www.cqwlrd.gov.cn/content/rdxx/
