全案設計報價系統
本 skill 將工程量清單數據渲染為專業 PDF 報價單,支援封面、總價表、明細頁多頁輸出。
1. 何時使用本 Skill
觸發條件
以下場景應使用本 skill:
- 用戶輸入
/報價或/quote— 生成 PDF 報價單 - 用戶提到要將多維表或 Excel 中的報價數據轉為 PDF 報價單
以下場景不應使用本 skill:
- 用戶只是在編輯多維表數據(應使用 lark-base skill)
- 用戶只是在查看報價歷史(不需要重新生成)
前置依賴
lark-baseskill — 讀取飛書多維表數據lark-imskill — 發送 PDF 到飛書對話lark-driveskill — 上傳 PDF 到飛書文件xlsxskill — 解析 Excel 檔案(當用戶提供 Excel 時)- Node.js 執行環境 + Playwright(已安裝於專案目錄)
Step 0: 初次使用檢查
當用戶第一次使用 Skill,或執行命令時遇到依賴相關錯誤,Agent 應執行以下檢查流程。
1. 檢查 Node.js 環境
嘗試執行 node --version,要求 >= 18。如果失敗或版本過低:
- 提示用戶安裝 Node.js (>= 18): https://nodejs.org
- 建議使用 nvm 管理版本
2. 檢查 Playwright 瀏覽器
檢查專案目錄下 npx playwright install chromium 是否已完成。如果 render.js 執行時報 Executable doesn't exist 錯誤:
- 在專案目錄下執行:
npx playwright install chromium - 重試渲染
3. 檢查 lark-cli
嘗試執行 lark-cli --version。如果失敗(command not found):
- 提示用戶安裝 lark-cli(飛書命令列工具)
- 安裝後執行:
lark-cli auth login
4. 檢查飛書登入狀態
嘗試執行 lark-cli base +table-list --base-token LfjJbLrTHacijesHmIjcDtSpnJf 測試登入狀態。如果返回認證錯誤:
- 提示用戶執行:
lark-cli auth login - 完成後重試
5. 提供飛書模板
用戶需要把專案模板複製到自己的飛書空間:
- 模板連結: https://li1fn1sw90.feishu.cn/base/LfjJbLrTHacijesHmIjcDtSpnJf?from=from_copylink
- 引導用戶點擊連結 → 點擊「複製此多維表」→ 在自己的空間中填入專案數據和報價明細
- 完成後將新多維表連結發給 Agent
Excel 數據源處理
用戶提供 Excel 檔案時,不直接解析渲染。引導流程:
- 提示:推薦先匯入飛書多維表,欄位和順序更好對齊
- 引導用戶將 Excel 匯入飛書:
- 開啟飛書 → 新建多維表 → 匯入 → 選擇 Excel 檔案
- 或者直接複製模板多維表,將 Excel 數據貼上進去
- 用戶提供多維表連結後,繼續
/報價流程
2. 工作流程
用戶觸發 → 環境檢查 → 確認數據源 → 讀取數據 → 數據驗證 → 確認 Logo → 確認稅率 → 確認模板 → 渲染 PDF → 發送飛書
Step 1: 確認數據源
用戶需提供以下之一:
- 飛書多維表連結 — 如
https://xxx.feishu.cn/base/XXX - Excel 檔案 — 本地檔案路徑或透過對話上傳
如果用戶未提供,詢問用戶選擇數據源。
Step 2: 讀取數據
飛書多維表數據源
- 從用戶提供的連結中提取
base-token(URL 中/base/後的部分) - 列出表:
lark-cli base +table-list --base-token <token> - 找到 專案資訊 和 報價明細 兩張表的 table_id
- 讀取專案資訊:
lark-cli base +record-list --base-token <token> --table-id <專案資訊table_id> --format json - 先查檢視:
lark-cli base +view-list --base-token <token> --table-id <報價明細table_id>,取預設 grid 檢視(如「全部項目」)的 view_id - 讀取報價明細(必須帶
--view-id,否則返回順序與用戶在飛書介面拖拽後的順序不一致):lark-cli base +record-list --base-token <token> --table-id <報價明細table_id> --view-id <檢視ID> --format json --limit 200 - 從專案資訊中提取:專案名稱、工程編號、編製日期、編製人員、聯絡信箱、公司Logo、稅率、管理費
- 從報價明細中提取:區域、工程分類、項目名稱、項目特徵、單位、數量、綜合單價、合價、備註(序號無需讀取,渲染時自動生成)
Excel 數據源
使用 xlsx skill 解析 Excel,提取相同結構的數據。Excel 可能有多 sheet,需找到包含報價明細的 sheet(通常有「序號」、「工程分類」、「綜合單價」等列頭)。
Step 3: 數據驗證
在渲染 PDF 之前,Agent 必須對讀取到的數據進行驗證。不要直接跳過。
驗證清單(逐項執行):
3.1 專案資訊完整性
檢查 專案名稱、工程編號、編製日期 三個欄位:
- 任一為空 → 告知用戶具體缺少哪個欄位,請用戶在飛書多維表的「專案資訊」表中補充
- 全部通過 → 繼續
3.2 報價明細條目數
檢查 items 陣列:
- 為 0 或不存在 → 「報價明細中沒有數據,請在飛書多維表的「報價明細」表中添加條目後重試」
- 有數據 → 繼續
3.3 逐條欄位檢查
對每條記錄檢查必填欄位(工程分類、項目名稱、單位、數量、綜合單價),彙總報告:
數據驗證結果:
- 總條目: 104
- 區域缺失: 3 (第 6、9、20 條)
- 工程分類缺失: 2 (第 5、18 條)
- 項目名稱缺失: 0
- 單位缺失: 1 (第 12 條)
- 數量缺失: 0
- 綜合單價缺失: 3 (第 5、7、22 條)
- 工程分類不在標準列表中: 0
3.4 缺失欄位的處理
- 缺失條目 ≤ 總條目的 20%:告知用戶具體哪些條目有問題,詢問「是否跳過這些條目繼續生成?」
- 缺失條目 > 20%:要求用戶修正數據後重試,不繼續渲染
- 用戶確認跳過 → 過濾掉問題條目,用有效數據繼續
3.7 區域缺失檢查(僅按區域分類時)
當用戶選擇按區域分類(Step 5.6 選擇「按區域」)時,額外檢查每條記錄的 區域 欄位:
區域缺失: X (第 X 條)
- 缺失條目 ≤ 總條目的 20%:告知用戶具體哪些條目缺區域,詢問「是否將這些條目歸入『其他』繼續生成?」
- 缺失條目 > 20%:要求用戶補充區域後重試
- 用戶確認 → 缺失區域的條目歸入「其他」分組(與 Step 3.4 跳過邏輯互斥,用戶二選一:跳過 or 歸入其他)
3.5 合價自動修正
對每條記錄檢查 合價 是否等於 數量 × 綜合單價:
- 偏差 < 1 元 → 不做處理
- 偏差 ≥ 1 元或為空 → 自動計算
合價 = 數量 × 綜合單價,值寫入渲染數據 - 修正條目超過 10% → 提示用戶「已自動修正 X 條合價數據」,建議用戶檢查多維表公式
3.6 工程分類標準列表
以下為 13 類標準分類:
措施項目、拆除工程、砌築工程、混凝土及鋼筋混凝土工程、
金屬結構工程、防水工程、保溫隔熱工程、樓地面裝飾工程、
牆柱面裝飾與隔斷工程、天棚工程、油漆塗料工程、
其他裝飾工程、安裝工程
不在列表中的分類仍可渲染,但告知用戶「分類 'XXX' 不在標準列表中」。
Step 4: Logo 處理
必須執行,不可跳過。 無論數據源是多維表還是 Excel,都必須檢查並處理 Logo。
- 從多維表讀取:檢查專案資訊表中的
公司Logo附件欄位- 如有附件,使用 media API 下載(
drive +download不支援多維表附件):lark-cli api GET /open-apis/drive/v1/medias/{file_token}/download --output ./logo.png - 將圖片轉為 base64 data URI,傳入渲染數據的
logo_url欄位
- 如有附件,使用 media API 下載(
- 從 Excel 讀取:檢查 Excel 中是否有 logo 圖片(通常嵌入在 sheet 中或作為附件)
- 如有,提取並轉為 data URI
- Logo 缺失時(必須詢問用戶):
- 如果多維表或 Excel 中沒有找到 Logo 圖片,必須向用戶詢問:
- 「未在數據源中找到公司 Logo,請問如何處理?」
- 選項 A:上傳 Logo 圖片
- 選項 B:提供 Logo 圖片 URL
- 選項 C:不使用 Logo(PDF 中顯示預設「R M」文字標識)
- 不要預設跳過這一步,即使讀者說「直接生成」也要確認 Logo 處理方式
- 如果多維表或 Excel 中沒有找到 Logo 圖片,必須向用戶詢問:
Step 5: 確認稅率與管理費
優先使用多維表專案資訊中的 稅率、管理費 欄位值。如果多維表中沒有該欄位,則詢問用戶。
費率規則(稅率與管理費通用):
- 值 > 1 → 視為百分比整數,自動 ÷100(如
3→ 3%、10→ 10%) - 值 ≤ 1 → 視為小數直接使用(如
0.03→ 3%、0.1→ 10%) - 空值:稅率預設 3%,管理費預設 0(不收取)
計算順序:
合計 = Σ 各條目合價
管理費 = 合計 × 管理費率
增值稅 = (合計 + 管理費) × 稅率
總計 = 合計 + 管理費 + 增值稅
管理費 > 0 時總價表顯示「管理費」行;管理費 = 0 時不顯示(相容舊數據)。
Step 5.5: 確認模板風格
必須詢問,不可跳過。 即使之前使用過某個模板,也要每次都確認。
詢問用戶選擇模板:
請選擇報價單模板風格:
1. Swiss IKB(預設)— 藍底滿版封面 + 雙語分類標題
2. Swiss IKB Zebra — 同上 + 內容明細行斑馬紋(白/淺藍交替)
3. B&W — 白底封面 + 淺灰強調,適合黑白列印
4. B&W Zebra — 同上 + 內容明細行斑馬紋(白/淺灰交替)
如果用戶回覆中包含明確的模板名稱或編號,直接使用對應模板:
swiss-ikb— Swiss IKB(預設)swiss-ikb-zebra— Swiss IKB Zebrabw— B&W 黑白列印版bw-zebra— B&W Zebra 黑白列印斑馬紋版
開發中(暫不對外提供): Editorial / Editorial B&W / Card / Card B&W 四個模板程式碼已存在,待用戶後續修改完善後啟用。如用戶主動要求使用這些模板,可執行
node scripts/render.js --template editorial等命令,但優先推薦上述 4 個已上線模板。
Step 5.6: 確認分類方式
必須詢問,不可跳過。 每次渲染前都要確認,不要預設使用上次的選項。
詢問用戶選擇分類方式:
請選擇報價單分類方式:
1. 按區域(玄關、客廳、主臥……)— 總價表按區域彙總,明細表在序號後增加工程分類列
2. 按工程分類(措施項目、拆除工程……)— 傳統方式,明細表不含工程分類列
用戶回覆明確編號或名稱後使用對應模式:
area— 按區域分類,渲染命令加--group-by areacategory— 按工程分類(預設),不傳或加--group-by category
注意:選擇「按區域」時,需確保報價明細表的
區域欄位已填寫(Step 3.7 會檢查)。若數據尚未填寫區域,建議優先使用「按工程分類」。
Step 5.7: 區域英文翻譯(僅按區域分類時)
區域名每個項目不同,每次渲染前必須動態翻譯,禁止套用寫死的映射。
- 從報價明細中收集本次出現的全部區域名(去重)
- Agent 將區域名逐條翻譯為英文(設計行業慣用翻譯,如 玄關→Foyer、客廳→Living Room、陽光房→Sunroom)
- 將翻譯結果作為
region_names物件寫入渲染數據 JSON:
{
"region_names": {
"玄關": "Foyer",
"陽光房": "Sunroom"
}
}
- 渲染時 render.js 優先使用
region_names中的翻譯(未提供的區域名會回退到內建常見區域映射,再兜底顯示 AREA) - 渲染完成後在彙報中列出本次區域翻譯對照表,用戶可糾正,下次報價重新翻譯
Step 6: 渲染 PDF
在專案目錄下執行:
node scripts/render.js --input <data.json> --template <模板名> --vat-rate <稅率> --group-by <area|category> --output ./output/<專案名稱>_<工程編號>.pdf
--group-by area— 按區域分類(大分類=區域,明細含工程分類列)--group-by category或不傳 — 按工程分類(原有行為)
渲染數據 JSON 格式:
{
"專案名稱": "xxx",
"工程編號": "xxx",
"編製日期": "xxx",
"編製人員": "xxx",
"聯絡信箱": "xxx",
"logo_url": "data:image/png;base64,...",
"稅率": 0.08,
"管理費": 0.1,
"items": [
{
"區域": "玄關",
"工程分類": "措施項目",
"項目名稱": "腳手架",
"項目特徵": "室內腳手架",
"單位": "項",
"數量": 1,
"綜合單價": 3200,
"合價": 3200
}
]
}
序號自動生成規則:
序號欄位無需在數據中提供(飛書多維表可不建該列)。渲染時按分組序號.組內序號自動生成:按區域分類時 01 區域 →1.1、1.2、…,02 區域 →2.1、2.2、…;按工程分類時 措施項目 →1.x、拆除工程 →2.x…安裝工程 →13.x。組內順序 = 飛書記記錄順序(多維表中可拖拽記錄排序)。
稅率與管理費(詳見 Step 5):
稅率/管理費值 > 1 視為百分比整數(3 → 3%),≤ 1 視為小數(0.08 → 8%)- 管理費 = 合計 × 管理費率;增值稅 = (合計 + 管理費) × 稅率;總計 = 合計 + 管理費 + 增值稅
- 管理費未提供或為 0 → 不收取,總價表不顯示管理費行
- 若未提供
--vat-rate參數,render.js 優先讀取數據中的稅率欄位
Step 7: 儲存與發送
PDF 渲染完成後,必須詢問用戶選擇儲存方式:
PDF 已生成:<專案名稱>_<工程編號>_<模板名>.pdf(XX 條,¥XXX 含稅)
請問如何儲存?
1. 發送到目前聊天視窗
2. 儲存到飛書雲文件,推送文件連結
3. 兩者都要
根據用戶選擇執行:
選項 1 — 發送到聊天視窗
lark-cli im +messages-send --type media --file <pdf路徑>
選項 2 — 儲存到飛書雲文件
lark-cli drive +upload --file <pdf路徑> --title "<專案名稱>_<工程編號>"
獲取上傳後的文件連結,回覆用戶:
已儲存到飛書雲文件:<專案名稱>_<工程編號>_<模板名>.pdf
文件連結: https://xxx.feishu.cn/drive/xxx
選項 3 — 兩者都要
先執行選項 2(上傳),再執行選項 1(發送檔案 + 連結)。
清理暫存檔案
發送/儲存完成後,刪除渲染過程中產生的暫存 JSON 檔案和 Logo 下載檔案(如有)。
3. 專案結構
quote-generator-skill/
├── package.json # 專案配置
├── setup.sh # 一鍵安裝腳本
├── SKILL.md # Skill 定義
├── scripts/
│ ├── render.js # HTML → PDF 渲染引擎
│ ├── demo.js # 開箱即用演示
│ └── generate-large-test.js # 大型測試數據生成器
├── references/
│ ├── templates/
│ │ ├── content.html # Handlebars PDF 模板(內容頁)
│ │ ├── cover-screen.html # Swiss IKB 封面獨立模板
│ │ ├── cover-bw.html # B&W 黑白列印封面模板
│ │ ├── swiss-ikb.json # Swiss IKB 配置
│ │ ├── swiss-ikb-zebra.json # Swiss IKB Zebra 配置
│ │ ├── bw.json # B&W 黑白列印配置
│ │ └── bw-zebra.json # B&W Zebra 配置
│ ├── helpers.js # Handlebars 自訂 helper
│ └── bitable-config.json # 多維表欄位配置
├── docs/plans/ # 設計文件
├── output/ # 生成的 PDF 輸出目錄
└── README.md
4. 模板說明
PDF 模板包含三種頁面:
- 封面 — 工程名稱、編號、日期、編製人員、logo(右上角)
- 總價表 — 按工程分類彙總金額 + 合計/增值稅/總計
- 明細頁 — 每頁約 8 行數據,含分類標題、明細行、小計行、頁碼
5. 注意事項
- 多維表中的
合價是公式欄位(=數量×綜合單價),讀取時已計算好 序號無需在飛書中維護:渲染時自動生成(分組序號.組內序號,組內按記錄順序)。若要調整明細順序,在飛書多維表中拖拽記錄排序即可工程分類是單選欄位,13 個選項對應 13 類工程區域是自由文字欄位(建議在飛書中改為單選:玄關、客廳、餐廳、廚房、主臥、次臥、衛生間、陽台、書房、衣帽間、走廊、全屋等),僅按區域分類時使用,可為空- 區域模式的分組標題顯示中英對照(如
Living Room 客廳)。區域名每個項目不同,Agent 每次渲染前按 Step 5.7 動態翻譯並寫入region_names;內建常見區域映射僅作兜底,未命中時顯示AREA 單位是單選欄位,支援自訂擴展- Logo 支援圖片(推薦)和文字兩種形式
- 增值稅稅率每次由用戶指定,不固定
- 輸出 PDF 為 A4 尺寸,適合列印
6. 常見問題處理
| 錯誤現象 | 原因 | Agent 處理方式 |
|---|---|---|
command not found: lark-cli | lark-cli 未安裝 | 提示安裝 lark-cli,參考 Step 0.3 |
lark-cli 返回認證錯誤 | 未登入或 token 過期 | 提示執行 lark-cli auth login |
Executable doesn't exist | Playwright Chromium 未安裝 | 執行 npx playwright install chromium |
| 渲染 PDF 為空或格式混亂 | 數據 JSON 格式不正確 | 檢查 專案名稱、工程編號 非空,items 至少 1 條 |
| 明細序號與手動填的不一樣 | 序號已改為自動生成 | 正常現象:序號按分組自動重編(如 01 全屋 → 1.1、1.2…)。調整順序請在飛書拖拽記錄 |
| 字型異常 | 極少發生(字型已內建為 WOFF2,不依賴外部 CDN) | 檢查專案 references/fonts/ 目錄下字型檔案是否完整 |
| Logo 下載失敗 | 多維表附件 API 權限問題 | 確認用戶已授權,或選擇「不使用 Logo」 |
base-token 無法從 URL 提取 | 用戶提供的不是多維表連結 | 提示用戶提供飛書多維表連結(URL 中應包含 /base/) |