適用:填的人是院內同仁的問卷——在職教育課後回饋、對某個單位的內部滿意度、需求調查。
答案存在平台資料服務;填的人只看得到、改得到自己那一份,負責人看全部與統計。
先問清楚兩件事(寫程式之前)
- 誰來填?
- 同仁(用醫院 Google 帳號登入)→ 這份 skill。
- 病人、家屬、民眾 → 平台做不到。他們沒有院方帳號,只能開「公開」的頁面,而公開的應用不能收任何資料(llms.txt §4.55)。
照實說,請他改用院方核准的問卷管道(不知道是哪個就請他洽資訊室)。不要叫他去申請開放,那個開關不存在。
- 要不要匿名?平台一定會記下是誰填的,負責人在應用管理頁匯出 CSV 時看得到填寫人。
看得到填寫人的是這個應用的負責人、代理人,和負責人在「成員」指定看得到全部的人——不一定是主管,主管不在裡面就看不到。
他問「主管會不會看到是誰寫的」,照這句回答,不要說「通常是主管」(S35 第 208 項)。
- 畫面上不顯示名字,不等於匿名。頁面與對話裡都不可以寫「匿名」「不記名」。
- 他需要真正的匿名(例如對主管的回饋),照實說平台做不到,請他改用院方核准、能匿名的問卷工具;要做的話,頁面上一定寫清楚
「本問卷會記錄填寫人,只有問卷負責人看得到」。
- 用白話問,例如:「這個問卷平台會記下是誰填的,負責人匯出時看得到。這樣可以嗎?還是你需要大家匿名填?」
做法
archetype: A,visibility: hospital(或只給特定一群人時listed),skills_used: [staff-survey]。
data.collects 列出每一題的欄位名(例如 [course, q_overall, q_pace, q_comment])。
- 在 manifest 宣告角色,讓平台擋「填的人只能看、改自己那一份」:
```yaml
roles:
respondent:
label: 填答者
default_role: respondent # 院內同仁打開就是填答者,不用一個個加名單
access:
responses:
respondent: [create, read_own, update_own]
```
負責人與副負責人不用寫,什麼都看得到。要讓「教學組的同事也看得到統計」,就再加一個角色(例如 viewer: [read]),
請負責人到應用管理頁的「成員」把他們加成這個角色。
- 每人一份:打開時先
UberTeach.data.list({ collection: 'responses' })——填答者只讀得到自己的;
已經有一筆就顯示「你已經填過,可以修改」,送出時用 update(id, data),不要再 save() 一筆。
這是畫面上的方便,平台不會擋重複填;統計時以每人最新一筆為準(同一個 created_by.user_id 只算一次)。
- 統計頁只給看得到全部的人:用
UberTeach.me()的app_can.responses判斷——有read才顯示「看結果」。
統計要讀到最後一頁(list() 一頁最多 50 筆,見 form-app 的 listAll())。要畫圖照 chart-dashboard skill。
- 題目設計:
- 選擇題用固定選項(1–5 分、是/否),比自由填答好統計。
- 不要問工號、身分證、生日這類資料——那是員工個資,會讓問卷變成二級。要分組看就問「單位」「職類」這種選項。
- 自由填答欄旁邊寫「請勿填寫病人資料或同事的個人資訊」。
- 截止日:寫在頁面上,過了就把表單收起來、只留「感謝填寫」。這也是畫面上的事,負責人要真的停止收件可以把應用設成「只有你」。
- 同仁填的文字一律用
textContent放上畫面(統計頁列出意見時也是),不要塞進innerHTML。
骨架
<!doctype html>
<html lang="zh-Hant">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>在職教育課後回饋</title>
</head>
<body>
<h1>在職教育課後回饋</h1>
<p>本問卷會記錄填寫人,只有問卷負責人看得到。截止日:2026-10-31。</p>
<form id="f" hidden>
<fieldset>
<legend>整體滿意度</legend>
<label><input type="radio" name="q_overall" value="5" required> 非常滿意</label>
<label><input type="radio" name="q_overall" value="4"> 滿意</label>
<label><input type="radio" name="q_overall" value="3"> 普通</label>
<label><input type="radio" name="q_overall" value="2"> 不滿意</label>
<label><input type="radio" name="q_overall" value="1"> 非常不滿意</label>
</fieldset>
<label>其他建議(請勿填寫病人資料)<textarea name="q_comment" maxlength="500"></textarea></label>
<button type="submit">送出</button>
</form>
<p id="status"></p>
<button id="results" hidden>看結果</button>
<script src="https://platform.wanfangai.tw/sdk/v1.js"></script>
<script>
const BAG = { collection: 'responses' };
let mine = null;
async function start() {
const me = await UberTeach.me();
const can = me.app_can.responses ?? [];
if (can.includes('read')) document.getElementById('results').hidden = false;
const page = await UberTeach.data.list(BAG); // a respondent only gets back their own
mine = page.records.find((r) => r.created_by.user_id === me.user_id) ?? null;
const form = document.getElementById('f');
if (mine) {
for (const [k, v] of Object.entries(mine.data)) {
const field = form.elements[k];
if (field) field.value = v;
}
document.getElementById('status').textContent = '你已經填過,可以修改後再送出。';
}
form.hidden = false;
}
document.getElementById('f').addEventListener('submit', async (e) => {
e.preventDefault();
const data = Object.fromEntries(new FormData(e.target));
try {
mine = mine
? await UberTeach.data.update(mine.id, data)
: await UberTeach.data.save(data, BAG);
document.getElementById('status').textContent = '已送出,謝謝你。';
} catch (err) {
document.getElementById('status').textContent = err.message; // 平台回的中文訊息
}
});
start();
</script>
</body>
</html>存成 PDF
使用者要把統計結果頁印出來、或存成檔案交出去時,用瀏覽器的「列印 → 另存為 PDF」,不用套件(jsPDF 沒有中文字型,不要用)。
2026-10-15 用 Chrome 實測:書架的中文字型會嵌進 PDF、長表格跨頁時表頭會重複、圖表完整。
- 放一顆按鈕:
<button type="button" class="no-print" onclick="window.print()">存成 PDF</button>,
旁邊一句「在列印視窗的『目的地』選『另存為 PDF』;不要日期與網址就取消勾選『頁首及頁尾』」。
- 在
<style>最後加列印樣式——選單、按鈕不印,表格的列不切半、每頁重複表頭,圖不超出紙張:
```css
@media print {
@page { size: A4; margin: 15mm; }
nav, button, .no-print { display: none !important; }
body { padding: 0; font-size: 11pt; color: #000; background: #fff; }
h1, h2, h3 { break-after: avoid; }
tr, img, canvas, figure, .card, .chart { break-inside: avoid; }
thead { display: table-header-group; }
table { width: 100%; }
canvas, img { max-width: 100% !important; height: auto !important; }
- { print-color-adjust: exact; -webkit-print-color-adjust: exact; }
}
```
有 Chart.js 圖表時再加這兩行,圖會照紙的寬度重畫、印出來比較清楚(不加也印得出來,只是放大縮小過):
```js
addEventListener('beforeprint', () => { for (const c of Object.values(Chart.instances)) c.resize(680, 300); });
addEventListener('afterprint', () => { for (const c of Object.values(Chart.instances)) c.resize(); });
```
- 只想印某一塊(例如只要圖不要明細):其他區塊加
class="no-print"。 - 想讓每台電腦印出來的字都一樣,用書架的黑體:
<link rel="stylesheet" href="https://platform.wanfangai.tw/lib/noto-sans-tc@5.3.1/400.css">,font-family: 'Noto Sans TC', system-ui, sans-serif(粗體再載700.css)。
發佈前檢查
- 有「存成 PDF」的:在瀏覽器按一次看預覽——中文正常、按鈕與選單沒印出來、表格沒切半、圖完整。
- 頁面上寫了「會記錄填寫人」,沒有任何地方寫「匿名」。
- manifest 有
roles/default_role/access,responses袋的填答者只有create、read_own、update_own。 - 發佈試用版後,請他用另一個同仁的帳號(或請同事)打開看看:應該只看得到自己的那一份、看不到「看結果」。
- 告訴他:結果可以在應用管理頁匯出成 CSV,匯出檔裡有填寫人,檔案要照院內個資規定保管。