醫智匯

staff-survey

staff-survey

怎麼用

打開你的 AI 助手,把下面這句話貼給它。下面那一大段是寫給它看的,你不必讀。

請照平台的 staff-survey 這個 skill 幫我做

適用:填的人是院內同仁的問卷——在職教育課後回饋、對某個單位的內部滿意度、需求調查。

答案存在平台資料服務;填的人只看得到、改得到自己那一份,負責人看全部與統計。

先問清楚兩件事(寫程式之前)

  • 誰來填?
  • 同仁(用醫院 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,匯出檔裡有填寫人,檔案要照院內個資規定保管。

用到的平台套件

照這份做法做的工具,會從平台載入這些: