外部系統嵌入組件
在自有業務系統中通過 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 的團隊內。