跳到內容
Syno Package Land
語言: 繁體中文

UI 元件

@synopackageland/ui 是遵循 DSM 7 樣式的 Vue 3 元件庫。預設的 native-go 專案就是使用它的 Vue App:側邊欄外殼、狀態頁,以及你啟用的各項能力的頁面。外殼與能力頁面來自 SDK 的 @synopackageland/sdk/vue。你也可以用純 JavaScript 撰寫視窗。

Terminal window
synopkgland init -t native-go --ui plain my-plain-app

預設的 --ui vue 專案不需要額外旗標。它的 frontend/ 資料夾固定 @synopackageland/ui 0.9.10 與 @synopackageland/sdk 0.10.12 的精確版本。先執行一次 npm install,並提交它寫出的 package-lock.json;之後 npm ci 會安裝與專案建置時完全相同的內容。NAS 不會下載 npm 套件:建置時已把所有內容打包。

Vue(預設)Plain(--ui plain)
原始碼frontend/src/(Vue 3、Vite、TypeScript)ui/app.js、ui/app.css
建置在 frontend/ 執行 npm run build,由 synopkgland build 啟動無
檔案、jobs、delegate 畫面包含不包含
獨立登入(--with login)支援被 check 與 init 拒絕
元件@synopackageland/ui、@synopackageland/sdk/vue自行撰寫

第一次建置 Vue 專案之前,請在 frontend/ 執行 npm install。build 不會安裝套件。

要在其他 Vue 專案安裝元件庫:

Terminal window
npm install --save-exact @synopackageland/ui@0.9.10 --registry=https://registry.npmjs.org

若 UI 是用其他工具建置,請在 dsm-package.yaml 的 ui 指向建置輸出,並以清單形式提供建置命令。建置必須產生 app.js 與 app.css。

ui:
dir: web/dist
build:
cwd: web
command: [npm, run, build]

app.js 以 window.DsmPackageApps[<package>].mount(root, ctx) 註冊視窗。產生的 plain UI 示範了如何從 document.currentScript 讀取套件名稱。

每個 DSM 視窗都與桌面共用同一個頁面。請匯入內嵌佈景主題而不是 theme.css,以 AppRoot 包住 App,並為每個掛載的 App 設定唯一的 idPrefix。

import { createApp } from 'vue'
import App from './App.vue'
import '@synopackageland/ui/themes/dsm7-embedded.css'
export function mount(root: HTMLElement, ctx: unknown) {
const app = createApp(App, { ctx })
app.config.idPrefix = `my-package-${crypto.getRandomValues(new Uint32Array(4)).join('-')}`
app.mount(root)
return { unmount: () => app.unmount() }
}

產生的 frontend/src/main.ts 也會為 DSM 視窗載入器註冊 mount。編輯時請保留該部分。

<script setup lang="ts">
import { AppRoot, ConfigProvider } from '@synopackageland/ui'
</script>
<template>
<AppRoot modal-scope="app" style="height: 100%">
<ConfigProvider locale="en">
<!-- your pages -->
</ConfigProvider>
</AppRoot>
</template>

AppRoot 會把佈景主題限定在你的 App 範圍內,並把對話框、選單與通知導向它自己內部,使它們留在視窗內。使用 modal-scope="app"(預設)時,對話框只會擋住你的 App。請為根元素設定有界的大小,例如 height: 100%。整頁的 Web 或 Portal App 可以使用 modal-scope="document",但不要在 DSM 視窗內使用。也不要在視窗內匯入 theme.css。

Application Portal 全頁啟動時,請把 ctx 傳給 AppShell。DSM 可見的 banner 顯示 App 名稱時,shell 會隱藏重複的品牌區;DSM 帳號選單可見時,會隱藏預設帳號區。自訂 sidebar-footer 會保留。banner 隱藏、缺少或無法辨識時,仍顯示 shell 自己的控制項。偵測只讀取所屬 DSM standalone 視窗的 DOM,不需要管理員權限。

<script setup lang="ts">
import { AppLayout, AppSidebar, PageContent, PageHeader } from '@synopackageland/ui'
</script>
<template>
<AppLayout min-height="0" wide-layout="window">
<template #sidebar>
<AppSidebar v-model="page" :groups="groups" />
</template>
<PageContent>
<PageHeader title="Status" description="MyApp" />
</PageContent>
</AppLayout>
</template>

AppLayout 有 sidebar、toolbar、預設與 footer 插槽。在 DSM 視窗中請保持 wide-layout="window",讓最大化的視窗維持其版面。只有整頁的 Portal App 才使用 wide-layout="web"。版面寬度低於 600 px 時,導覽會變成滑出式。產生的外殼會讓每個頁面保持掛載,並以 v-show 切換,所以使用者查看其他頁面時,某一頁的輪詢仍會繼續。

在 AppShell 顯示的頁面中,從 @synopackageland/sdk/vue 匯入 PageInfo。它會在頁面標題旁、自訂 #meta 內容之前放置一個資訊入口。notes 的非空項目以清單顯示;空項目會略過,沒有自訂 slot 且整份清單為空時不顯示按鈕。按鈕名稱使用 shell 的語言與附註數量。點擊或鍵盤 focus 可開啟,Escape 關閉。

<script setup lang="ts">
import { PageInfo } from '@synopackageland/sdk/vue'
</script>
<template>
<PageInfo :notes="['Sizes include snapshots.', 'Refresh to read current usage.']" />
<PageInfo inline label="Search details" :notes="['Search covers indexed files.']" />
<PageInfo label="About this page" :notes="['Details']">
<p>Custom content replaces the notes list.</p>
</PageInfo>
</template>

inline 讓按鈕留在宣告位置;沒有 AppShell 時也會留在原位。label 覆寫無障礙名稱。預設 slot 取代清單,也能在沒有 notes 時顯示按鈕。side 與 align 傳給 InfoTip,預設為 bottom 與 start。獨立頁面 fixture 可從同一個 SDK 入口匯入 PageInfoTarget injection key,呼叫 provide(PageInfoTarget, shallowRef<HTMLElement | null>(null)),再把 ref 設為標題元素以提供目的地,用法如同 PageActionsTarget。

PageHeader 的標題與 meta 保持同一行。actions 放不下時,整組移到標題下方並靠右。標題仍放不下時以省略號截斷,title 屬性保留完整文字。

@synopackageland/ui 的 InfoTip 接受 align="start|center|end"(預設 center)與 icon="help|info"(預設 help)。氣泡以目前 App 視窗作為碰撞邊界,保留 8 px 間距,限制在有界的 AppRoot 內。

只從 @synopackageland/ui 匯入。不要以路徑匯入元件檔案。

元件用途
AppRoot、ConfigProviderApp 根元素、語言
AppLayout、AppSidebar、PageContent、PageHeader視窗版面
Button、Input、Textarea、Select、Checkbox、Switch控制項
Form、FormItem、FormActions表單。表單寬度小於 520 px 時,標籤會改為堆疊
SettingsSection、Callout、EmptyState、LoadingArea、Progress設定頁、訊息、狀態
Dialog、ToastProvider、useToast對話框與通知。useToast 必須在 ToastProvider 的子元件中呼叫
TreeView階層結構,有 nodes、v-model、v-model:expanded,以及在第一次展開時取得子節點的 loadChildren
DataGrid表格,有 columns、rows、row-key、排序、可選取的列(selectable)與以 page-size 分頁
FilePicker從使用者的電腦選擇檔案

請使用元件庫的控制項,不要用原生的 <select>、<input>、<label> 或 <a> 元素。圖示來自 @lucide/vue,專案已將它列為自己的相依套件。

要查詢屬性、插槽與事件,請在 frontend/ 執行:

Terminal window
npx --no syno-pkg-land-ui catalog
npx --no syno-pkg-land-ui describe Button

NasPicker 與 FileBrowser 是 @synopackageland/sdk/vue 的匯出,不屬於 @synopackageland/ui。FileBrowser 以已登入使用者的 File Station 檢視,結合 TreeView 與 DataGrid。NasPicker 則把它放進對話框。

<NasPicker v-model:open="open" :provider="provider" :locale="ctx.locale" mode="file" @selected="path = $event" />
NasPicker 屬性或事件意義
open(v-model:open)對話框是否顯示
provider來自 @synopackageland/sdk 的 createNasProvider(ctx)
localectx.locale
mode'file' 或 'folder'
title、description可選的對話框文字
selected 事件送出選到的路徑。選取路徑不會授予任何權限:後端在使用時會再次檢查工作階段與權限

後端部分請見檔案。

FilePicker 回傳瀏覽器的 File 物件。它不認識 NAS。產生的檔案瀏覽器把它當作上傳輸入,並限制為單一檔案、最大 16 GiB:

<FilePicker v-model="uploads" variant="compact" :max-files="1" :max-size="16 * 1024 ** 3" @rejected="onRejected" />
FilePicker 屬性意義
v-model選到的 File[]
accept、multiple、directory可以選擇什麼
maxFiles、maxSize數量與大小限制(位元組)。檔案被拒絕時會觸發 rejected,原因為 type、size、count 或 duplicate
variant'dropzone' 或 'compact'
locale、text語言與文字覆寫

請在自己的 App 根元素上,於元件庫樣式表之後覆寫 token,並使用不會比對到其他 App 的選擇器。請在 AppRoot 上設定 data-package。

[data-ui-app][data-package='my-package'] {
--ui-primary: #0086e5;
}

在元素上加上 data-ui-density="compact",可讓其內的控制項與間距更緊湊。請讓你自己的 CSS 不要放進 cascade layer。若需要為裸元素設定基礎規則,請依序載入 dsm7-embedded-reset.css、你的 CSS,再載入 dsm7-embedded-theme.css。這兩個檔案合起來等同 dsm7-embedded.css。

元件庫會以 App 的九種語言翻譯自己的文字(檔案選擇器、對話框按鈕、連結註解):en、zh-TW、zh-CN、ja、ko、de、fr、es 與 it。請依 ctx.locale 設定 ConfigProvider locale;@synopackageland/sdk 的 uiLocale 會把 cht 等 DSM 語言代碼對應到這些名稱。你自己的文字放在 frontend/src/i18n/<locale>.json,請見原生 App。

項目限制
透過檔案瀏覽器的瀏覽器上傳1 個檔案,16 GiB
最小的 DSM 視窗寬 700 px,由產生的視窗設定
DSM 視窗內的浮層僅限 App 區域。不要混用 App 區域與整份文件的浮層
文字大小單位rem,會跟隨 DSM 桌面的字體大小
同一頁面上掛載的 App每個都需要唯一的 idPrefix
症狀原因解法
對話框或選單出現在視窗外App 不在 AppRoot 內以 AppRoot 包住 App
邊界與字型看起來不對載入的是 theme.css 而不是內嵌佈景主題匯入 themes/dsm7-embedded.css
對話框標題指向另一個 App兩個 App 共用 ID 前綴在 mount 之前設定唯一的 app.config.idPrefix
建置說缺少 node_modulesbuild 不會安裝套件在 frontend/ 執行 npm install
init 拒絕 --ui plain --with login登入需要 Vue UI使用 --ui vue
小檔案上傳卻顯示上傳大小訊息選擇器因其他原因拒絕了檔案,例如選了多個檔案只選擇單一檔案