外部系统嵌入组件
在自有业务系统中通过 iframe 免登录调用 MuseDAM 素材、文件夹、成员选择与上传组件。
API Key 只保存在后端;code 5 分钟内有效且只能使用一次。
适用场景
企业员工已经登录了你的业务系统(OA、CMS、电商后台等),需要在其中直接使用 MuseDAM 的素材选择、文件夹选择、成员选择、上传和标签选择组件。
- 员工打开组件时免登录,不会跳转 MuseDAM 登录页,也不需要逐个员工授权。
- 是否开放由团队管理员统一决定,可随时开启、关闭或撤销。
- 凭证只能代表本团队的正常成员,组件内的操作沿用该成员在 MuseDAM 中的权限。
本文适用于「在你的系统中嵌入 MuseDAM 组件」。如果是将你的应用嵌入 MuseDAM 内部运行,请参阅嵌入应用与组件。
接入流程
- 团队管理员为一个团队 API Key 开启嵌入组件,并配置允许嵌入的域名。
- 员工在你的系统中触发操作时,你的后端使用该 API Key 调用签发接口,为该员工获取一次性
code。 - 你的前端用 iframe 打开
https://{MuseDAM 域名}/outer/embed?code=...。 - 嵌入页完成鉴权后,向父页面发送
musedam-embed-ready事件。 - 前端通过
postMessage发送action打开组件,并按dispatchId接收action_result结果。
你的后端 ──(Bearer 团队 API Key + 员工标识)──► POST /api/muse/embed-code ──► { code, expiresIn }│▼ 把 code 交给前端你的前端 ──► <iframe src="https://musedam.cc/outer/embed?code=...">◄── musedam-embed-ready──► action: assets-selector-modal-open◄── action_result: { success: true, data: { selectedAssets } }
开启嵌入组件
管理员开启嵌入
- 系统管理员登录 MuseDAM,进入「团队设置」→「API 账号」。
- 选择一个团队 API Key(如果还没有,请先按快速开始创建)。
- 在 API Key 详情的「嵌入组件」区域打开开关,填写允许嵌入的域名并保存。
- 开启后,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。
签发请求
POST https://open.musedam.cc/api/muse/embed-codeAuthorization: Bearer your_api_keyContent-Type: application/json
请求体用于指定员工,三个字段任选其一;同时传入时优先级为 userId > loginEmail > loginPhone。
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| userId | string | 否 | MuseDAM 用户 ID,可通过团队与成员接口查询 |
| loginEmail | string | 否 | 员工的 MuseDAM 登录邮箱 |
| loginPhone | string | 否 | 员工的 MuseDAM 登录手机号 |
请求示例:
curl --location --request POST 'https://open.musedam.cc/api/muse/embed-code' \--header 'Authorization: Bearer your_api_key' \--header 'Content-Type: application/json' \--data-raw '{"loginEmail": "zhangsan@example.com"}'
签发响应
{"code": "0","result": {"code": "c3f1a9...","expiresIn": 300,"userId": "1538720494056386560","orgId": "123"}}
| 字段 | 类型 | 说明 |
|---|---|---|
| result.code | string | 一次性嵌入凭证,用于拼接嵌入页地址 |
| result.expiresIn | number | 有效期(秒),默认 300 |
| result.userId | string | 凭证对应的 MuseDAM 用户 ID |
| result.orgId | string | 凭证对应的团队 ID |
code5 分钟内有效,且只能使用一次,兑换后立即作废。请在打开组件时实时签发,不要缓存或复用。
签发失败原因
| code | 错误 | 说明 | 处理方法 |
|---|---|---|---|
| 50078 | EMBED_API_KEY_NOT_ALLOWED | 只有团队 API Key 才能签发嵌入凭证 | 改用团队 API Key,不要使用个人 API Key 或应用凭证 |
| 50079 | EMBED_NOT_ENABLED | 该 API Key 未开启嵌入组件 | 请管理员在 API Key 详情中开启嵌入组件 |
| 50080 | EMBED_MEMBER_NOT_FOUND | 指定的用户不是本团队的有效成员 | 有效成员指状态为正常、管理员或待激活且未退出团队的成员,离职成员不可用;签发接口不会自动创建账号 |
API Key 无效或已撤销时,按错误处理中的认证错误处理。其他相关错误码:
| code | 错误 | 出现场景 |
|---|---|---|
| 50081 | EMBED_CODE_INVALID | 嵌入页兑换 code 时,code 无效、已过期或已被使用;嵌入页会发送 musedam-embed-auth-failed |
| 50082 | EMBED_ORIGIN_INVALID | 管理员保存「允许嵌入的域名」时格式不正确,不会在签发或嵌入时出现 |
加载嵌入页
将签发得到的 code 拼接到嵌入页地址,用 iframe 打开:
https://musedam.cc/outer/embed?code={code}&theme=light
| 参数 | 必填 | 说明 |
|---|---|---|
| code | 是 | 签发接口返回的一次性 code,请使用 encodeURIComponent 编码 |
| theme | 否 | light 或 dark,只作用于本次嵌入,不写入用户偏好 |
嵌入页域名与团队成员登录 MuseDAM 使用的域名一致,即 https://musedam.cc。
嵌入页本身是透明的空白页,只承载组件弹窗。建议 iframe 全屏覆盖,在组件返回结果后由你的页面隐藏或移除 iframe。code 兑换后会从嵌入页地址栏中移除。
测试页
联调时可以使用嵌入组件测试页:粘贴签发得到的 code 即可加载嵌入页,选择 action、编辑参数后发送,并查看生命周期事件和返回结果。
- 测试页与 MuseDAM 同域,加载后当前浏览器的 MuseDAM 登录身份会切换为
code对应的用户,建议在无痕窗口中使用。 - 如果 API Key 配置了允许嵌入的域名,需要把
https://musedam.cc也加进去,否则会鉴权失败。 - 本页面用于测试「在你的系统中嵌入 MuseDAM 组件」;嵌入应用与组件中的组件测试页用于测试应用嵌入 MuseDAM 内部运行的场景。
生命周期事件
嵌入页通过 postMessage 向父页面发送以下事件,消息结构为 { source: 'musedam', type, timestamp, payload }:
| type | 时机 | payload |
|---|---|---|
| musedam-embed-ready | 鉴权与初始化完成,可以开始发送 action | {} |
| musedam-embed-auth-failed | code 缺失、无效、过期、已使用,或父页面域名不在允许列表中 | { message } |
| musedam-embed-auth-expired | 使用过程中登录态失效 | { code? } |
收到 musedam-embed-auth-failed 或 musedam-embed-auth-expired 时,请让后端重新签发 code 并刷新 iframe 地址。嵌入页不会跳转到登录页。
调用组件
收到 musedam-embed-ready 后,通过 iframe.contentWindow.postMessage 发送 action。
消息格式
你的页面 → 嵌入页(请求)
{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: { /* 各组件参数 */ }}
嵌入页 → 你的页面(结果)
// 成功{ 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 | 请求参数错误或格式不正确 |
| forbidden | action 不在可调用列表中,或用户没有该操作权限 |
| 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:
{initialSelectedAssetIds?: number[];// 可选择的素材类型,不传则不限制// 取值:image、video、audio、js、document、PDocument、excel、font、zip、dll、code、model、triD、urlallowedTypes?: 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:
{selectedItems?: {members?: Array<{ id: string; name: string }>;departments?: Array<{ id: string; name: string }>;groups?: Array<{ id: string; name: string }>;};}
- result.data:
{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
// 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
// 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 标签。
| action | args | 说明 |
|---|---|---|
| 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 只保存在服务端环境变量中:
// POST /api/musedam/embed-code —— 你自己的后端接口,需校验当前登录用户app.post('/api/musedam/embed-code', requireLogin, async (req, res) => {const response = await fetch('https://open.musedam.cc/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 })})
前端打开组件
const MUSEDAM_ORIGIN = 'https://musedam.cc'async function openMuseDAM(action, args) {// 1. 每次打开都向自己的后端获取新的 codeconst { code } = await fetch('/api/musedam/embed-code', { method: 'POST' }).then((r) => r.json())// 2. 创建全屏 iframeconst 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) returnconst data = event.data || {}switch (data.type) {case 'musedam-embed-ready':// 4. 初始化完成后发送 actioniframe.contentWindow.postMessage({source: 'musedam-app',target: 'musedam',type: 'action',timestamp: new Date().toISOString(),dispatchId,action,args,},MUSEDAM_ORIGIN,)breakcase 'musedam-embed-auth-failed':case 'musedam-embed-auth-expired':cleanup()reject(new Error(data.payload?.message || data.type))breakcase 'action_result':// 5. 按 dispatchId 匹配结果if (data.dispatchId !== dispatchId) returncleanup()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,员工标识应取自你的登录会话,不要信任前端传入的邮箱或手机号。 code5 分钟内有效且只能使用一次。不要记录到日志、埋点或分享链接中。- 在生产环境中配置「允许嵌入的域名」,只填写实际承载 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 的团队内。