跳转到正文
开发者文档/第三方集成

外部系统嵌入组件

在自有业务系统中通过 iframe 免登录调用 MuseDAM 素材、文件夹、成员选择与上传组件。

最近更新
在业务系统中嵌入组件
01管理员开启嵌入
02后端用 API Key 签发 code
03iframe 打开 /outer/embed
04postMessage 调用组件

API Key 只保存在后端;code 5 分钟内有效且只能使用一次。

适用场景

企业员工已经登录了你的业务系统(OA、CMS、电商后台等),需要在其中直接使用 MuseDAM 的素材选择、文件夹选择、成员选择、上传和标签选择组件。

  • 员工打开组件时免登录,不会跳转 MuseDAM 登录页,也不需要逐个员工授权。
  • 是否开放由团队管理员统一决定,可随时开启、关闭或撤销。
  • 凭证只能代表本团队的正常成员,组件内的操作沿用该成员在 MuseDAM 中的权限。

本文适用于「在你的系统中嵌入 MuseDAM 组件」。如果是将你的应用嵌入 MuseDAM 内部运行,请参阅嵌入应用与组件。

接入流程

  1. 团队管理员为一个团队 API Key 开启嵌入组件,并配置允许嵌入的域名。
  2. 员工在你的系统中触发操作时,你的后端使用该 API Key 调用签发接口,为该员工获取一次性 code。
  3. 你的前端用 iframe 打开 https://{MuseDAM 域名}/outer/embed?code=...。
  4. 嵌入页完成鉴权后,向父页面发送 musedam-embed-ready 事件。
  5. 前端通过 postMessage 发送 action 打开组件,并按 dispatchId 接收 action_result 结果。
text
你的后端 ──(Bearer 团队 API Key + 员工标识)──► POST /api/muse/embed-code ──► { code, expiresIn }
│
▼ 把 code 交给前端
你的前端 ──► <iframe src="https://musedam.ai/outer/embed?code=...">
◄── musedam-embed-ready
──► action: assets-selector-modal-open
◄── action_result: { success: true, data: { selectedAssets } }

开启嵌入组件

管理员开启嵌入

  1. 系统管理员登录 MuseDAM,进入「团队设置」→「API 账号」。
  2. 选择一个团队 API Key(如果还没有,请先按快速开始创建)。
  3. 在 API Key 详情的「嵌入组件」区域打开开关,填写允许嵌入的域名并保存。
  4. 开启后,API Key 列表会显示「已开启嵌入」标识。

只有团队 API Key 能签发嵌入凭证;个人 API Key、第三方应用凭证均会被拒绝。关闭开关或撤销 API Key 后,立即无法再签发新的 code。

配置允许嵌入的域名

「允许嵌入的域名」用于限制哪些父页面可以加载嵌入页。配置后,嵌入页只在父页面 origin 匹配时进入可用状态,并且只接收、只发送给该 origin 的消息。

规则说明
格式http(s)://host[:port],只填写 origin,不能带路径,例如 https://oa.example.com
通配符支持一级子域名通配,例如 https://*.example.com
数量最多 20 条
留空不限制父页面来源(不建议在生产环境使用)

签发嵌入凭证

由你的后端调用,使用开启了嵌入的团队 API Key 认证。每次打开组件前签发一个新的 code。

签发请求

text
POST https://open.musedam.ai/api/muse/embed-code
Authorization: Bearer your_api_key
Content-Type: application/json

请求体用于指定员工,三个字段任选其一;同时传入时优先级为 userId > loginEmail > loginPhone。

字段类型必填说明
userIdstring否MuseDAM 用户 ID,可通过团队与成员接口查询
loginEmailstring否员工的 MuseDAM 登录邮箱
loginPhonestring否员工的 MuseDAM 登录手机号

请求示例:

bash
curl --location --request POST 'https://open.musedam.ai/api/muse/embed-code' \
--header 'Authorization: Bearer your_api_key' \
--header 'Content-Type: application/json' \
--data-raw '{
"loginEmail": "zhangsan@example.com"
}'

签发响应

json
{
"code": "0",
"result": {
"code": "c3f1a9...",
"expiresIn": 300,
"userId": "1538720494056386560",
"orgId": "123"
}
}
字段类型说明
result.codestring一次性嵌入凭证,用于拼接嵌入页地址
result.expiresInnumber有效期(秒),默认 300
result.userIdstring凭证对应的 MuseDAM 用户 ID
result.orgIdstring凭证对应的团队 ID

code 5 分钟内有效,且只能使用一次,兑换后立即作废。请在打开组件时实时签发,不要缓存或复用。

签发失败原因

code错误说明处理方法
50078EMBED_API_KEY_NOT_ALLOWED只有团队 API Key 才能签发嵌入凭证改用团队 API Key,不要使用个人 API Key 或应用凭证
50079EMBED_NOT_ENABLED该 API Key 未开启嵌入组件请管理员在 API Key 详情中开启嵌入组件
50080EMBED_MEMBER_NOT_FOUND指定的用户不是本团队的有效成员有效成员指状态为正常、管理员或待激活且未退出团队的成员,离职成员不可用;签发接口不会自动创建账号

API Key 无效或已撤销时,按错误处理中的认证错误处理。其他相关错误码:

code错误出现场景
50081EMBED_CODE_INVALID嵌入页兑换 code 时,code 无效、已过期或已被使用;嵌入页会发送 musedam-embed-auth-failed
50082EMBED_ORIGIN_INVALID管理员保存「允许嵌入的域名」时格式不正确,不会在签发或嵌入时出现

加载嵌入页

将签发得到的 code 拼接到嵌入页地址,用 iframe 打开:

text
https://musedam.ai/outer/embed?code={code}&theme=light
参数必填说明
code是签发接口返回的一次性 code,请使用 encodeURIComponent 编码
theme否light 或 dark,只作用于本次嵌入,不写入用户偏好

嵌入页域名与团队成员登录 MuseDAM 使用的域名一致,即 https://musedam.ai。

嵌入页本身是透明的空白页,只承载组件弹窗。建议 iframe 全屏覆盖,在组件返回结果后由你的页面隐藏或移除 iframe。code 兑换后会从嵌入页地址栏中移除。

测试页

联调时可以使用嵌入组件测试页:粘贴签发得到的 code 即可加载嵌入页,选择 action、编辑参数后发送,并查看生命周期事件和返回结果。

  • 测试页与 MuseDAM 同域,加载后当前浏览器的 MuseDAM 登录身份会切换为 code 对应的用户,建议在无痕窗口中使用。
  • 如果 API Key 配置了允许嵌入的域名,需要把 https://musedam.ai 也加进去,否则会鉴权失败。
  • 本页面用于测试「在你的系统中嵌入 MuseDAM 组件」;嵌入应用与组件中的组件测试页用于测试应用嵌入 MuseDAM 内部运行的场景。

生命周期事件

嵌入页通过 postMessage 向父页面发送以下事件,消息结构为 { source: 'musedam', type, timestamp, payload }:

type时机payload
musedam-embed-ready鉴权与初始化完成,可以开始发送 action{}
musedam-embed-auth-failedcode 缺失、无效、过期、已使用,或父页面域名不在允许列表中{ message }
musedam-embed-auth-expired使用过程中登录态失效{ code? }

收到 musedam-embed-auth-failed 或 musedam-embed-auth-expired 时,请让后端重新签发 code 并刷新 iframe 地址。嵌入页不会跳转到登录页。

调用组件

收到 musedam-embed-ready 后,通过 iframe.contentWindow.postMessage 发送 action。

消息格式

你的页面 → 嵌入页(请求)

typescript
{
source: "musedam-app",
target: "musedam",
type: "action",
timestamp: "2026-10-12T10:30:00.000Z",
dispatchId: "dispatch_1728700000000_abc123", // 唯一 ID,用于匹配结果
action: "assets-selector-modal-open",
args: { /* 各组件参数 */ }
}

嵌入页 → 你的页面(结果)

typescript
// 成功
{ type: "action_result", dispatchId, action, result: { success: true, data: { /* 结果 */ } } }
// 失败或取消
{
type: "action_result",
dispatchId,
action,
result: {
success: false,
code: "cancelled" | "forbidden" | "malformed_request" | "internal_server_error",
message: "..."
}
}
result.code含义
cancelled用户主动取消或关闭了组件
malformed_request请求参数错误或格式不正确
forbiddenaction 不在可调用列表中,或用户没有该操作权限
internal_server_error服务器内部错误或其他未捕获的异常

可调用的 action

组件action
素材选择器assets-selector-modal-open / assets-selector-modal-close
文件夹选择器(多选)folder-selector-modal-open / folder-selector-modal-close
文件夹选择器(单选)single-folder-selector-modal-open / single-folder-selector-modal-close
成员选择器member-selector-modal-open / member-selector-modal-close
上传upload-files / sub-project-upload-complete
标签选择openTagSelecter / closeTagSelecter / removeTag

以上之外的 action 会直接返回 result.code = "forbidden"。*-close 用于由你的页面主动关闭已打开的组件。嵌入会话锁定在 code 对应的团队,组件内不能切换团队。

素材选择器

素材选择器:浏览、筛选并选择资产库中的素材。
素材选择器:浏览、筛选并选择资产库中的素材。点击查看大图
  • action:assets-selector-modal-open
  • args:
typescript
{
initialSelectedAssetIds?: number[];
// 可选择的素材类型,不传则不限制
// 取值:image、video、audio、js、document、PDocument、excel、font、zip、dll、code、model、triD、url
allowedTypes?: string[];
// 各类型数量上限;limit 不传表示该类型不限数量
dataLimit?: Array<{ type: string; limit?: number }>;
// 打开时预置的筛选条件,用户仍可在组件内修改
filters?: {
types?: string[];
extensions?: string[];
size?: { min?: number; max?: number; unit?: 'KB' | 'MB' | 'GB' };
dimension?: { minWidth?: number; maxWidth?: number; minHeight?: number; maxHeight?: number };
};
}
  • result.data:{ selectedAssets: IAssetLite[] }。IAssetLite 包含 id、assetId、name、type、extension、size、width、height、downloadUrl、thumbnail、tags、folders 等字段,完整定义见嵌入应用与组件。

downloadUrl 等链接可能有时效。业务系统中请保存素材 ID,需要展示或下载时再通过素材管理接口获取有效链接。

文件夹选择器

文件夹选择器:选择一个或多个共享文件夹。
文件夹选择器:选择一个或多个共享文件夹。点击查看大图

多选:

  • action:folder-selector-modal-open
  • args:{ initialSelectedFolders?: Array<{ id: number; name: string }>; allMaterials?: boolean },allMaterials 控制是否显示「全部素材」快速选项
  • result.data:{ selectedFolders: Array<{ id: number; name: string }>; allMaterials: boolean }

单选:

  • action:single-folder-selector-modal-open
  • args:{ initialSelectedFolder?: number | { id: number; name?: string } }
  • result.data:{ id: number; name: string }

成员选择器

成员选择器:按成员、部门或群组选择。
成员选择器:按成员、部门或群组选择。点击查看大图
  • action:member-selector-modal-open
  • args:
typescript
{
selectedItems?: {
members?: Array<{ id: string; name: string }>;
departments?: Array<{ id: string; name: string }>;
groups?: Array<{ id: string; name: string }>;
};
}
  • result.data:
typescript
{
members: Array<{ id: string; name: string; departmentsName?: string; avatarUrl?: string }>;
departments: Array<{ id: string; name: string }>;
groups: Array<{ id: string; name: string }>;
}

文件上传

上传分两步:先用 upload-files 上传文件,业务处理完成后再用 sub-project-upload-complete 通知 MuseDAM 正式入库。

第一步:upload-files

typescript
// args
{
files: File[]; // 必填,可通过 <input type="file"> 获取
folderId?: number; // 目标文件夹 ID,可先用单选文件夹选择器获取
uploadSource?: string; // 上传来源标识
subProjectId?: string; // 推荐填写,用于区分不同业务的上传
}
// result.data
{
uploadedFiles: Array<{
key: string; // 文件唯一标识,第二步需要使用
fileName: string;
fileSize: number;
fileType: string;
uploadTime: string;
url?: string;
}>;
failedFiles?: Array<{ fileName: string; error: string }>;
}

第二步:sub-project-upload-complete

typescript
// args
{
subProjectId: string; // 与上传时一致
uploadKeys: Array<{ key: string; fileName: string; fileSize: number; fileType: string; uploadTime: string; url?: string }>;
}
// result.data
{
processedFiles: Array<{ key: string; fileName: string; status: 'success' | 'failed'; error?: string }>;
}

部分文件失败时 result.success 仍为 true,请逐个检查 failedFiles 和 processedFiles[].status。

标签选择器

标签选择器以浮层形式显示在指定位置,适合在你的页面中为内容打 MuseDAM 标签。

actionargs说明
openTagSelecter{ rect: { left: number; top: number; width?: number; height?: number } }在视口坐标 rect 旁打开标签浮层
closeTagSelecter无关闭标签浮层
removeTag{ tagId: number }从当前已选标签中移除一个标签

标签选择器不返回 action_result。用户每次修改标签,嵌入页都会发送 { source: 'musedam', type: 'action', action: 'updateTags', args: { tags } },请以最后一次收到的 tags 为准。

完整示例

服务端签发 code

以 Node.js 为例,API Key 只保存在服务端环境变量中:

javascript
// POST /api/musedam/embed-code —— 你自己的后端接口,需校验当前登录用户
app.post('/api/musedam/embed-code', requireLogin, async (req, res) => {
const response = await fetch('https://open.musedam.ai/api/muse/embed-code', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.MUSEDAM_API_KEY}`,
'Content-Type': 'application/json',
},
// 只为当前登录员工签发,不要直接使用前端传入的邮箱
body: JSON.stringify({ loginEmail: req.user.email }),
})
const body = await response.json()
if (String(body.code) !== '0') {
return res.status(400).json({ message: body.message })
}
res.json({ code: body.result.code })
})

前端打开组件

javascript
const MUSEDAM_ORIGIN = 'https://musedam.ai'
async function openMuseDAM(action, args) {
// 1. 每次打开都向自己的后端获取新的 code
const { code } = await fetch('/api/musedam/embed-code', { method: 'POST' }).then((r) => r.json())
// 2. 创建全屏 iframe
const iframe = document.createElement('iframe')
iframe.style.cssText = 'position:fixed;inset:0;width:100%;height:100%;border:0;z-index:9999'
iframe.src = `${MUSEDAM_ORIGIN}/outer/embed?code=${encodeURIComponent(code)}&theme=light`
document.body.appendChild(iframe)
const dispatchId = `dispatch_${Date.now()}_${Math.random().toString(36).slice(2)}`
return new Promise((resolve, reject) => {
function cleanup() {
window.removeEventListener('message', onMessage)
iframe.remove()
}
function onMessage(event) {
// 3. 只处理来自 MuseDAM 嵌入页的消息
if (event.origin !== MUSEDAM_ORIGIN || event.source !== iframe.contentWindow) return
const data = event.data || {}
switch (data.type) {
case 'musedam-embed-ready':
// 4. 初始化完成后发送 action
iframe.contentWindow.postMessage(
{
source: 'musedam-app',
target: 'musedam',
type: 'action',
timestamp: new Date().toISOString(),
dispatchId,
action,
args,
},
MUSEDAM_ORIGIN,
)
break
case 'musedam-embed-auth-failed':
case 'musedam-embed-auth-expired':
cleanup()
reject(new Error(data.payload?.message || data.type))
break
case 'action_result':
// 5. 按 dispatchId 匹配结果
if (data.dispatchId !== dispatchId) return
cleanup()
data.result?.success
? resolve(data.result.data)
: reject(Object.assign(new Error(data.result?.message), { code: data.result?.code }))
break
}
}
window.addEventListener('message', onMessage)
})
}
// 使用:最多选择 5 张图片
openMuseDAM('assets-selector-modal-open', { allowedTypes: ['image'], dataLimit: [{ type: 'image', limit: 5 }] })
.then(({ selectedAssets }) => console.log('已选择', selectedAssets))
.catch((error) => error.code !== 'cancelled' && console.error(error))

安全建议

  • API Key 只能保存在后端,不能出现在 iframe 地址、前端代码或浏览器存储中。
  • 后端只为当前已登录的员工签发 code,员工标识应取自你的登录会话,不要信任前端传入的邮箱或手机号。
  • code 5 分钟内有效且只能使用一次。不要记录到日志、埋点或分享链接中。
  • 在生产环境中配置「允许嵌入的域名」,只填写实际承载 iframe 的页面 origin。
  • 接收消息时校验 event.origin 和 event.source;发送消息时指定 MuseDAM 的 origin,不要使用 '*'。
  • 员工离职或被移出团队后,将无法再为其签发或兑换 code。不再需要嵌入时,请关闭开关或撤销 API Key。

常见问题

收到 musedam-embed-auth-failed 怎么办?

依次检查:code 是否超过 5 分钟、是否已被使用过(例如页面刷新后重复使用了同一个地址)、父页面 origin 是否在「允许嵌入的域名」中(注意协议和端口)。

可以把 iframe 预加载以加快打开速度吗?

可以在用户即将操作时提前签发 code 并加载 iframe,收到 musedam-embed-ready 后再显示。注意 code 5 分钟有效,过期后需要重新签发。

组件中能看到哪些素材和成员?

与员工本人登录 MuseDAM 时一致,按该员工的团队成员权限展示。嵌入会话固定在签发 code 的团队内。

MuseDAM Developer PlatformAPI · Integrations · MCP
    外部系统嵌入组件 | MuseDAM Developers