weifuwu 0.87.0 → 0.89.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +111 -135
- package/dist/client/components/ActionSheet/ActionSheet.d.ts +1 -1
- package/dist/client/components/AiChat/AiChat.d.ts +3 -4
- package/dist/client/components/ChatInput/ChatInput.d.ts +5 -0
- package/dist/client/components/CitationCard/CitationCard.d.ts +1 -1
- package/dist/client/components/Command/Command.d.ts +1 -1
- package/dist/client/components/ContextMenu/ContextMenu.d.ts +1 -1
- package/dist/client/components/DatePicker/DatePicker.d.ts +1 -1
- package/dist/client/components/Dropdown/Dropdown.d.ts +1 -1
- package/dist/client/components/Editor/model/html.d.ts +10 -0
- package/dist/client/components/ImageCropper/ImageCropper.d.ts +6 -2
- package/dist/client/components/Menu/Menu.d.ts +1 -1
- package/dist/client/components/NavMenu/NavMenu.d.ts +1 -1
- package/dist/client/components/Popconfirm/Popconfirm.d.ts +1 -1
- package/dist/client/components/SlideCanvas/SlideCanvas.d.ts +1 -1
- package/dist/client/components/Tour/Tour.d.ts +1 -1
- package/dist/client/components/TreeSelect/TreeSelect.d.ts +1 -1
- package/dist/client/components/index.js +41 -41
- package/dist/client/components/style.css +298 -644
- package/dist/client/layout/weifuwu-layout.css +286 -640
- package/dist/client/vdom/browser/Browser.d.ts +2 -0
- package/dist/client/vdom/commands.d.ts +4 -1
- package/dist/client/vdom/context/UIContext.d.ts +8 -0
- package/dist/client/vdom/core/diff/attrs.d.ts +10 -2
- package/dist/client/vdom/core/field/attributes.d.ts +12 -0
- package/dist/client/vdom/core/field/events.d.ts +8 -0
- package/dist/client/vdom/core/field/input-sync.d.ts +14 -0
- package/dist/client/vdom/core/field/key.d.ts +3 -0
- package/dist/client/vdom/core/field/props.d.ts +13 -1
- package/dist/client/vdom/core/field/ref.d.ts +6 -0
- package/dist/client/vdom/core/field/style.d.ts +7 -1
- package/dist/client/vdom/core/node/component.d.ts +3 -4
- package/dist/client/vdom/core/node/native.d.ts +6 -1
- package/dist/client/vdom/core/patch/index.d.ts +24 -1
- package/dist/client/vdom/core/patch/processors.d.ts +8 -1
- package/dist/client/vdom/core/protocol.d.ts +45 -0
- package/dist/client/vdom/core/router.d.ts +7 -0
- package/dist/client/vdom/core/ssr/absorb.d.ts +31 -4
- package/dist/client/vdom/core/transform/index.d.ts +6 -6
- package/dist/client/vdom/core/v2/cycle.d.ts +54 -0
- package/dist/client/vdom/core/v2/diff.d.ts +80 -0
- package/dist/client/vdom/core/v2/integrate.d.ts +24 -0
- package/dist/client/vdom/core/v2/render.d.ts +28 -0
- package/dist/client/vdom/core/v2/schedule.d.ts +33 -0
- package/dist/client/vdom/core/v2/serve.d.ts +4 -0
- package/dist/client/vdom/core/v2/spy.d.ts +21 -0
- package/dist/client/vdom/core/v2/ssr.d.ts +14 -0
- package/dist/client/vdom/core/vnode.d.ts +9 -6
- package/dist/client/vdom/dev/error-counter.d.ts +27 -0
- package/dist/client/vdom/dev/render-health.d.ts +60 -0
- package/dist/client/vdom/hooks/chat.d.ts +32 -5
- package/dist/client/vdom/hooks/drag-media.d.ts +3 -1
- package/dist/client/vdom/hooks/env.d.ts +32 -0
- package/dist/client/vdom/hooks/popup-manager.d.ts +21 -0
- package/dist/client/vdom/hooks/popup.d.ts +4 -1
- package/dist/client/vdom/hooks/stable.d.ts +4 -0
- package/dist/client/vdom/hooks/use-observable.d.ts +13 -0
- package/dist/client/vdom/index.d.ts +14 -6
- package/dist/client/vdom/index.js +4 -7
- package/dist/client/vdom/jsx-runtime.js +1 -1
- package/dist/client/vdom/middlewares/api.d.ts +8 -0
- package/dist/client/vdom/middlewares/auth-i18n.d.ts +7 -0
- package/dist/client/vdom/middlewares/ws.d.ts +41 -1
- package/dist/client/vdom/observable/index.d.ts +13 -0
- package/dist/client/vdom/observable/observable.d.ts +26 -0
- package/dist/client/vdom/observable/operators.d.ts +76 -0
- package/dist/client/vdom/observable/sources.d.ts +54 -0
- package/dist/client/vdom/observable/types.d.ts +45 -0
- package/dist/client/vdom/store.d.ts +17 -2
- package/dist/client/vdom/testing.js +2 -2
- package/dist/server/ai/agent.d.ts +18 -0
- package/dist/server/ai/client.d.ts +14 -2
- package/dist/server/ai/contracts.d.ts +2 -2
- package/dist/server/ai/sse.d.ts +6 -0
- package/dist/server/core/collect.d.ts +31 -0
- package/dist/server/core/error-counter.d.ts +11 -0
- package/dist/server/core/hub.d.ts +2 -0
- package/dist/server/core/router.d.ts +12 -9
- package/dist/server/db/contracts.d.ts +24 -0
- package/dist/server/db/memory-redis.d.ts +3 -4
- package/dist/server/db/memory-sql.d.ts +20 -4
- package/dist/server/db/postgres-server.d.ts +2 -0
- package/dist/server/db/query.d.ts +21 -0
- package/dist/server/db/redis-server.d.ts +4 -2
- package/dist/server/db/sql-parser.d.ts +2 -1
- package/dist/server/email/index.d.ts +2 -0
- package/dist/server/index.d.ts +4 -1
- package/dist/server/index.js +3927 -1982
- package/dist/server/messager/index.d.ts +21 -2
- package/dist/server/middleware/compress.d.ts +25 -0
- package/dist/server/ui/index.d.ts +8 -1
- package/dist/server/user/index.d.ts +4 -0
- package/dist/shared/router/chain.d.ts +9 -0
- package/dist/shared/router/context.d.ts +35 -0
- package/dist/shared/router/ctx-fields.d.ts +20 -0
- package/dist/shared/router/pipeline.d.ts +70 -0
- package/dist/shared/router/trie.d.ts +11 -3
- package/dist/shared/router/types.d.ts +23 -0
- package/docs/style-guide.md +44 -41
- package/package.json +13 -10
- package/content/apps/admin.md +0 -55
- package/content/apps/agent-platform.md +0 -35
- package/content/apps/auth.md +0 -49
- package/content/apps/multi.md +0 -44
- package/content/apps/todo.md +0 -51
- package/content/backend/ai.md +0 -28
- package/content/backend/auth.md +0 -20
- package/content/backend/email.md +0 -23
- package/content/backend/graphql.md +0 -23
- package/content/backend/http.md +0 -23
- package/content/backend/limit.md +0 -23
- package/content/backend/middleware.md +0 -17
- package/content/backend/queue.md +0 -23
- package/content/backend/redis.md +0 -26
- package/content/backend/router.md +0 -23
- package/content/backend/schedule.md +0 -23
- package/content/backend/sql.md +0 -27
- package/content/backend/sse.md +0 -26
- package/content/backend/ws.md +0 -26
- package/content/capabilities/app-node.md +0 -21
- package/content/capabilities/data.md +0 -21
- package/content/capabilities/events.md +0 -21
- package/content/capabilities/hooks.md +0 -22
- package/content/capabilities/i18n.md +0 -21
- package/content/capabilities/popup.md +0 -21
- package/content/capabilities/render-only.md +0 -23
- package/content/capabilities/router.md +0 -22
- package/content/capabilities/self-id.md +0 -21
- package/content/capabilities/store.md +0 -21
- package/content/capabilities/theme.md +0 -21
- package/content/capabilities/two-phase.md +0 -21
- package/content/components/accordion.md +0 -50
- package/content/components/actionsheet.md +0 -46
- package/content/components/affix.md +0 -51
- package/content/components/aichat.md +0 -57
- package/content/components/alert.md +0 -52
- package/content/components/alertgroup.md +0 -45
- package/content/components/anchor.md +0 -52
- package/content/components/app.md +0 -37
- package/content/components/approvalcard.md +0 -56
- package/content/components/appshell.md +0 -50
- package/content/components/aspectratio.md +0 -47
- package/content/components/authpage.md +0 -61
- package/content/components/autocomplete-v2.md +0 -40
- package/content/components/autocomplete.md +0 -56
- package/content/components/avatar.md +0 -51
- package/content/components/avatargroup.md +0 -47
- package/content/components/backtop.md +0 -53
- package/content/components/badge.md +0 -55
- package/content/components/breadcrumb.md +0 -49
- package/content/components/button.md +0 -63
- package/content/components/calendar-v2.md +0 -40
- package/content/components/calendar.md +0 -50
- package/content/components/card.md +0 -58
- package/content/components/carousel.md +0 -52
- package/content/components/cascader-v2.md +0 -39
- package/content/components/cascader.md +0 -54
- package/content/components/chart.md +0 -53
- package/content/components/chatinput.md +0 -63
- package/content/components/checkbox.md +0 -51
- package/content/components/checkboxgroup.md +0 -52
- package/content/components/citationcard.md +0 -48
- package/content/components/codeblock.md +0 -48
- package/content/components/codeeditor.md +0 -47
- package/content/components/collapse.md +0 -49
- package/content/components/colorpicker.md +0 -50
- package/content/components/command.md +0 -50
- package/content/components/confirm.md +0 -57
- package/content/components/contextmenu.md +0 -48
- package/content/components/copybutton.md +0 -51
- package/content/components/datepicker.md +0 -52
- package/content/components/descriptions-v2.md +0 -39
- package/content/components/descriptions.md +0 -52
- package/content/components/diffview-v2.md +0 -39
- package/content/components/diffview.md +0 -50
- package/content/components/divider.md +0 -48
- package/content/components/drawer.md +0 -55
- package/content/components/dropdown.md +0 -53
- package/content/components/editor.md +0 -49
- package/content/components/emptystate.md +0 -51
- package/content/components/exportcsv.md +0 -38
- package/content/components/field.md +0 -54
- package/content/components/filepreview-office.md +0 -37
- package/content/components/filepreview.md +0 -49
- package/content/components/filetree.md +0 -56
- package/content/components/fileupload-v2.md +0 -39
- package/content/components/fileupload.md +0 -57
- package/content/components/floatbutton.md +0 -52
- package/content/components/form-v2.md +0 -41
- package/content/components/form.md +0 -60
- package/content/components/grid.md +0 -51
- package/content/components/highlight-v2.md +0 -39
- package/content/components/highlight.md +0 -46
- package/content/components/hovercard.md +0 -51
- package/content/components/icon.md +0 -50
- package/content/components/imagecropper.md +0 -45
- package/content/components/img.md +0 -56
- package/content/components/infinitescroll-v2.md +0 -40
- package/content/components/infinitescroll.md +0 -54
- package/content/components/input.md +0 -65
- package/content/components/inputnumber.md +0 -58
- package/content/components/inview.md +0 -56
- package/content/components/jsonschemaform.md +0 -53
- package/content/components/jsonviewer-v2.md +0 -39
- package/content/components/jsonviewer.md +0 -49
- package/content/components/kanban.md +0 -46
- package/content/components/label.md +0 -48
- package/content/components/layout.md +0 -54
- package/content/components/layoutcontent.md +0 -43
- package/content/components/layoutheader.md +0 -43
- package/content/components/layoutsider.md +0 -43
- package/content/components/link.md +0 -52
- package/content/components/list.md +0 -44
- package/content/components/loading.md +0 -45
- package/content/components/logviewer-v2.md +0 -39
- package/content/components/logviewer.md +0 -53
- package/content/components/markdown.md +0 -51
- package/content/components/markdowneditor.md +0 -47
- package/content/components/math.md +0 -42
- package/content/components/mentions-v2.md +0 -39
- package/content/components/mentions.md +0 -52
- package/content/components/menu.md +0 -56
- package/content/components/menubar.md +0 -44
- package/content/components/messagebubble.md +0 -50
- package/content/components/modal.md +0 -59
- package/content/components/navmenu.md +0 -47
- package/content/components/notification.md +0 -51
- package/content/components/pageheader.md +0 -53
- package/content/components/pagination.md +0 -49
- package/content/components/paragraph.md +0 -45
- package/content/components/passwordinput.md +0 -57
- package/content/components/pininput-v2.md +0 -39
- package/content/components/pininput.md +0 -50
- package/content/components/pipeline.md +0 -49
- package/content/components/popconfirm.md +0 -58
- package/content/components/popover.md +0 -61
- package/content/components/progressbar.md +0 -50
- package/content/components/prompttemplate.md +0 -49
- package/content/components/qrcode.md +0 -50
- package/content/components/radiogroup.md +0 -53
- package/content/components/rate.md +0 -53
- package/content/components/reasoningblock.md +0 -47
- package/content/components/relationgraph.md +0 -52
- package/content/components/resizable.md +0 -53
- package/content/components/result.md +0 -49
- package/content/components/scrollbar.md +0 -49
- package/content/components/searchinput.md +0 -51
- package/content/components/segmentedcontrol.md +0 -59
- package/content/components/select-searchable.md +0 -47
- package/content/components/select.md +0 -66
- package/content/components/sessionlist.md +0 -52
- package/content/components/sheetgrid.md +0 -44
- package/content/components/skeleton.md +0 -55
- package/content/components/slidecanvas.md +0 -44
- package/content/components/slider.md +0 -62
- package/content/components/sortablelist.md +0 -39
- package/content/components/space.md +0 -52
- package/content/components/sparkline.md +0 -52
- package/content/components/statcard-countdown.md +0 -39
- package/content/components/statcard.md +0 -56
- package/content/components/steps.md +0 -48
- package/content/components/switch.md +0 -50
- package/content/components/tabbar.md +0 -45
- package/content/components/table-v2.md +0 -40
- package/content/components/table.md +0 -66
- package/content/components/tabs.md +0 -56
- package/content/components/tag.md +0 -51
- package/content/components/tagsinput-v2.md +0 -39
- package/content/components/tagsinput.md +0 -54
- package/content/components/text.md +0 -45
- package/content/components/textarea.md +0 -57
- package/content/components/themeswitch.md +0 -69
- package/content/components/timeline.md +0 -50
- package/content/components/title.md +0 -45
- package/content/components/toast.md +0 -52
- package/content/components/toggle-togglegroup.md +0 -52
- package/content/components/togglegroup.md +0 -47
- package/content/components/toolcallcard.md +0 -51
- package/content/components/tooltip.md +0 -50
- package/content/components/tour.md +0 -50
- package/content/components/transfer.md +0 -52
- package/content/components/tree-v2.md +0 -39
- package/content/components/tree.md +0 -59
- package/content/components/treeselect.md +0 -54
- package/content/components/typography.md +0 -51
- package/content/components/videoplayer.md +0 -52
- package/content/components/virtuallist.md +0 -52
- package/content/components/virtualtable-v2.md +0 -39
- package/content/components/virtualtable.md +0 -55
- package/content/components/watermark.md +0 -53
- package/content/components/wave.md +0 -43
- package/content/guides/ai-chat-family.md +0 -24
- package/content/guides/choose.md +0 -67
- package/content/guides/component-model.md +0 -32
- package/content/guides/component-standards.md +0 -123
- package/content/guides/components-guide.md +0 -813
- package/content/guides/custom-component.md +0 -321
- package/content/guides/data-guide.md +0 -281
- package/content/guides/file-preview-family.md +0 -18
- package/content/guides/frontend.md +0 -968
- package/content/guides/layout-choice.md +0 -64
- package/content/guides/layout-guide.md +0 -241
- package/content/guides/middleware.md +0 -459
- package/content/guides/mobile-guide.md +0 -105
- package/content/guides/page-building.md +0 -98
- package/content/guides/production.md +0 -37
- package/content/guides/quality.md +0 -60
- package/content/guides/realtime-guide.md +0 -296
- package/content/guides/render-only.md +0 -44
- package/content/guides/saas-guide.md +0 -253
- package/content/guides/server-guide.md +0 -359
- package/content/guides/start.md +0 -41
- package/content/guides/styling.md +0 -154
- package/content/guides/ui-dom-guide.md +0 -157
- package/content/index.json +0 -4344
- package/content/index.md +0 -181
- package/content/layout/align.md +0 -20
- package/content/layout/anchor.md +0 -19
- package/content/layout/app-shell.md +0 -23
- package/content/layout/border.md +0 -20
- package/content/layout/center.md +0 -19
- package/content/layout/cluster.md +0 -19
- package/content/layout/container.md +0 -19
- package/content/layout/fill.md +0 -19
- package/content/layout/grid.md +0 -19
- package/content/layout/hidden.md +0 -22
- package/content/layout/layer.md +0 -19
- package/content/layout/position.md +0 -22
- package/content/layout/row.md +0 -23
- package/content/layout/safe-area.md +0 -20
- package/content/layout/scroll.md +0 -21
- package/content/layout/spacing.md +0 -22
- package/content/layout/split.md +0 -19
- package/content/layout/stack.md +0 -19
- package/content/layout/surface.md +0 -23
- package/content/layout/text.md +0 -22
- package/content/patterns/app-shell.md +0 -34
- package/content/patterns/dashboard.md +0 -36
- package/content/patterns/data-screen.md +0 -30
- package/content/patterns/detail-page.md +0 -31
- package/content/patterns/docs.md +0 -34
- package/content/patterns/focus-task.md +0 -34
- package/content/patterns/landing.md +0 -35
- package/content/patterns/list-page.md +0 -32
- package/content/patterns/mobile.md +0 -31
- package/content/patterns/settings-page.md +0 -33
- package/content/patterns/workspace.md +0 -32
- package/dist/cli/content-sync.test.d.ts +0 -1
- package/dist/cli/docs-cli.test.d.ts +0 -1
- package/dist/cli/docs.d.ts +0 -2
- package/dist/cli/docs.mjs +0 -10961
- package/dist/client/vdom/core/build.d.ts +0 -36
- package/dist/client/vdom/core/diff/children.d.ts +0 -29
- package/dist/client/vdom/core/diff/index.d.ts +0 -22
- package/dist/client/vdom/core/diff/output.d.ts +0 -30
- package/dist/client/vdom/core/diff/same.d.ts +0 -22
- package/dist/client/vdom/core/serve.d.ts +0 -75
- package/dist/client/vdom/core/ssr/index.d.ts +0 -25
- package/examples/apps/admin/README.md +0 -23
- package/examples/apps/admin/api.ts +0 -27
- package/examples/apps/admin/app.tsx +0 -144
- package/examples/apps/admin/main.tsx +0 -8
- package/examples/apps/admin/server.ts +0 -37
- package/examples/apps/auth/README.md +0 -23
- package/examples/apps/auth/api.ts +0 -49
- package/examples/apps/auth/app.tsx +0 -137
- package/examples/apps/auth/main.tsx +0 -8
- package/examples/apps/auth/server.ts +0 -37
- package/examples/apps/multi/README.md +0 -23
- package/examples/apps/multi/app.tsx +0 -83
- package/examples/apps/multi/main.tsx +0 -8
- package/examples/apps/multi/server.ts +0 -33
- package/examples/apps/todo/README.md +0 -23
- package/examples/apps/todo/api.ts +0 -26
- package/examples/apps/todo/app.tsx +0 -128
- package/examples/apps/todo/main.tsx +0 -9
- package/examples/apps/todo/server.ts +0 -39
- package/examples/hello-world/README.md +0 -9
- package/examples/hello-world/client.ts +0 -14
- package/examples/hello-world/routes.tsx +0 -26
- package/examples/hello-world/server.ts +0 -46
- package/examples/patterns/AppShell.tsx +0 -224
- package/examples/patterns/Dashboard.tsx +0 -153
- package/examples/patterns/DataScreen.tsx +0 -104
- package/examples/patterns/DetailPage.tsx +0 -79
- package/examples/patterns/Docs.tsx +0 -105
- package/examples/patterns/FocusTask.tsx +0 -67
- package/examples/patterns/Landing.tsx +0 -90
- package/examples/patterns/ListPage.tsx +0 -77
- package/examples/patterns/Mobile.tsx +0 -154
- package/examples/patterns/SettingsPage.tsx +0 -98
- package/examples/patterns/SplitWorkspace.tsx +0 -113
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* hello-world——最小可运行起步项目(新手第一步,复制本目录即跑)
|
|
3
|
-
*
|
|
4
|
-
* 与 README「快速开始」模式 A 对应——三文件落地版:
|
|
5
|
-
* server.ts 后端:SPA 外壳 + /api/hello 数据端点
|
|
6
|
-
* routes.tsx 路由声明:两阶段异步组件(await ctx.data → 返回视图)
|
|
7
|
-
* client.ts 客户端入口:createRouter 接管渲染
|
|
8
|
-
*
|
|
9
|
-
* 跑起来:node server.ts → http://localhost:3400
|
|
10
|
-
*/
|
|
11
|
-
import { serve, Router, ui, cors } from 'weifuwu'
|
|
12
|
-
import { resolve, dirname } from 'node:path'
|
|
13
|
-
import { fileURLToPath } from 'node:url'
|
|
14
|
-
|
|
15
|
-
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
16
|
-
|
|
17
|
-
const router = new Router()
|
|
18
|
-
router.use(cors())
|
|
19
|
-
router.use(ui()) // 注入 ctx.ui.html / ctx.ui.js / ctx.ui.css
|
|
20
|
-
|
|
21
|
-
// SPA 外壳(空 root + 前端 bundle)
|
|
22
|
-
router.get('/', (req, ctx) => ctx.ui.html`
|
|
23
|
-
<!doctype html>
|
|
24
|
-
<html>
|
|
25
|
-
<head>
|
|
26
|
-
<meta charset="UTF-8">
|
|
27
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
28
|
-
<link rel="stylesheet" href="/components.css">
|
|
29
|
-
<title>weifuwu hello-world</title>
|
|
30
|
-
</head>
|
|
31
|
-
<body>
|
|
32
|
-
<div id="root"></div>
|
|
33
|
-
<script src="/static/app.js"></script>
|
|
34
|
-
</body>
|
|
35
|
-
</html>
|
|
36
|
-
`)
|
|
37
|
-
router.get('/static/app.js', (req, ctx) => ctx.ui.js(resolve(__dirname, 'client.ts')))
|
|
38
|
-
|
|
39
|
-
// 样式自托管(node_modules/weifuwu/components/style.css——包内 CSS,零外部依赖/CDN)
|
|
40
|
-
router.get('/components.css', (req, ctx) => ctx.ui.css('weifuwu/components/style.css'))
|
|
41
|
-
|
|
42
|
-
// 数据端点(ctx.data 消费——SPA 下真 fetch,SSR 下服务端直取)
|
|
43
|
-
router.get('/api/hello', () => Response.json({ msg: '你好,weifuwu!' }))
|
|
44
|
-
|
|
45
|
-
serve(router, { port: 3400 })
|
|
46
|
-
console.log('hello-world → http://localhost:3400')
|
|
@@ -1,224 +0,0 @@
|
|
|
1
|
-
import type { Component } from 'weifuwu/vdom'
|
|
2
|
-
import { Button, PageHeader, Table, Badge, Icon, Divider, Pagination, StatCard, Space, Card, Text } from 'weifuwu/components'
|
|
3
|
-
|
|
4
|
-
// ─────────────────────────────────────────────────────────────
|
|
5
|
-
// 模式 1:后台应用壳(App Shell)
|
|
6
|
-
//
|
|
7
|
-
// 最经典的后台布局:左侧可折叠导航 + 右侧内容区。
|
|
8
|
-
// 100% 使用 weifuwu/layout 原语 + weifuwu/components 组件:
|
|
9
|
-
// - wf-app-shell / wf-sidebar / wf-nav / wf-nav--collapsed / wf-main
|
|
10
|
-
// - 折叠 = --wf-sidebar-width 收窄 + wf-nav--collapsed(原语组合)
|
|
11
|
-
// 复制此文件即可得到一个标准后台。
|
|
12
|
-
// ─────────────────────────────────────────────────────────────
|
|
13
|
-
|
|
14
|
-
const NAV = [
|
|
15
|
-
{ key: 'dashboard', icon: 'dashboard', label: '仪表盘' },
|
|
16
|
-
{ key: 'orders', icon: 'box', label: '订单管理' },
|
|
17
|
-
{ key: 'users', icon: 'users', label: '用户管理' },
|
|
18
|
-
{ key: 'goods', icon: 'tag', label: '商品管理' },
|
|
19
|
-
{ key: 'settings', icon: 'settings', label: '系统设置' },
|
|
20
|
-
]
|
|
21
|
-
|
|
22
|
-
// 5 个视图内容(导航联动——每视图独立 PageHeader + 内容)
|
|
23
|
-
const USERS = [
|
|
24
|
-
{ id: 'U-001', name: '张伟', role: '管理员', email: 'zhang@acme.cn', v: 'primary' },
|
|
25
|
-
{ id: 'U-002', name: '李娜', role: '运营', email: 'li@acme.cn', v: 'default' },
|
|
26
|
-
{ id: 'U-003', name: '王强', role: '开发', email: 'wang@acme.cn', v: 'default' },
|
|
27
|
-
{ id: 'U-004', name: '赵敏', role: '客服', email: 'zhao@acme.cn', v: 'warning' },
|
|
28
|
-
]
|
|
29
|
-
|
|
30
|
-
const GOODS = [
|
|
31
|
-
{ id: 'G-101', name: '无线键盘', stock: 320, price: '¥199', status: '在售', v: 'success' },
|
|
32
|
-
{ id: 'G-102', name: '机械鼠标', stock: 58, price: '¥89', status: '补货中', v: 'warning' },
|
|
33
|
-
{ id: 'G-103', name: '显示器', stock: 12, price: '¥1299', status: '售罄', v: 'danger' },
|
|
34
|
-
{ id: 'G-104', name: '扩展坞', stock: 200, price: '¥159', status: '在售', v: 'success' },
|
|
35
|
-
]
|
|
36
|
-
|
|
37
|
-
export const AppShell: Component = async (_init, ctx) => {
|
|
38
|
-
// render-only:内部状态 let + 显式 render(design 归档)
|
|
39
|
-
let collapsed = false
|
|
40
|
-
let nav = 'dashboard'
|
|
41
|
-
let page = 1
|
|
42
|
-
const rerender = () => ctx.render()
|
|
43
|
-
|
|
44
|
-
// 订单数据(分页演示——12 条 → 每页 5 条)
|
|
45
|
-
const ORDERS_ALL = Array.from({ length: 12 }, (_, i) => ({
|
|
46
|
-
id: `A-${2000 + i}`,
|
|
47
|
-
customer: ['张伟', '李娜', '王强', '赵敏', '陈晨', '刘洋', '孙丽', '周杰', '吴磊', '郑爽', '钱进', '冯刚'][i],
|
|
48
|
-
amount: `¥${(Math.round(Math.random() * 300) + 100) * 10}`,
|
|
49
|
-
status: ['已支付', '待发货', '已完成', '已取消'][i % 4],
|
|
50
|
-
v: (['success', 'warning', 'default', 'danger'] as const)[i % 4],
|
|
51
|
-
}))
|
|
52
|
-
return async () => (
|
|
53
|
-
<div
|
|
54
|
-
class="wf-app-shell wf-rounded-lg"
|
|
55
|
-
style={{ height: 'calc(100vh - 48px)', overflow: 'hidden', '--wf-sidebar-width': collapsed ? '64px' : '240px' }}
|
|
56
|
-
>
|
|
57
|
-
{/* ── 左侧导航栏 ── */}
|
|
58
|
-
<aside class="wf-sidebar">
|
|
59
|
-
<div class="wf-sidebar-header wf-between">
|
|
60
|
-
{collapsed ? (
|
|
61
|
-
<Icon name="zap" size={18} className="wf-text-primary" />
|
|
62
|
-
) : (
|
|
63
|
-
<Space>
|
|
64
|
-
<Icon name="zap" size={18} className="wf-text-primary" />
|
|
65
|
-
<b class="wf-text-bold">Acme 管理台</b>
|
|
66
|
-
</Space>
|
|
67
|
-
)}
|
|
68
|
-
<Button
|
|
69
|
-
size="sm"
|
|
70
|
-
variant="ghost"
|
|
71
|
-
title={collapsed ? '展开侧栏' : '折叠侧栏'}
|
|
72
|
-
onClick={() => { collapsed = !collapsed; rerender() }}
|
|
73
|
-
>
|
|
74
|
-
<Icon name={collapsed ? 'chevron-right' : 'chevron-left'} size={14} />
|
|
75
|
-
</Button>
|
|
76
|
-
</div>
|
|
77
|
-
<div class="wf-sidebar-body">
|
|
78
|
-
<nav class={`wf-nav${collapsed ? ' wf-nav--collapsed' : ''}`}>
|
|
79
|
-
{NAV.map((n) => (
|
|
80
|
-
<a
|
|
81
|
-
key={n.key}
|
|
82
|
-
href="#/app-shell"
|
|
83
|
-
class={`wf-nav-item wf-pointer${nav === n.key ? ' wf-nav-item--active' : ''}`}
|
|
84
|
-
title={collapsed ? n.label : undefined}
|
|
85
|
-
onClick={() => { nav = n.key; rerender() }}
|
|
86
|
-
>
|
|
87
|
-
<span class="wf-nav-icon"><Icon name={n.icon as any} size={16} /></span>
|
|
88
|
-
<span class="wf-nav-label">{n.label}</span>
|
|
89
|
-
</a>
|
|
90
|
-
))}
|
|
91
|
-
</nav>
|
|
92
|
-
</div>
|
|
93
|
-
<div class="wf-sidebar-footer">
|
|
94
|
-
{collapsed
|
|
95
|
-
? <Icon name="shield" size={14} className="wf-text-tertiary" />
|
|
96
|
-
: <span class="wf-text-tertiary wf-text-xs">v2.4.0 · 内部系统</span>}
|
|
97
|
-
</div>
|
|
98
|
-
</aside>
|
|
99
|
-
|
|
100
|
-
{/* ── 右侧主区 ── */}
|
|
101
|
-
<main class="wf-main">
|
|
102
|
-
{/* 顶栏 */}
|
|
103
|
-
<div class="wf-between wf-p-md wf-border-b">
|
|
104
|
-
<Space>
|
|
105
|
-
<Button size="sm" variant="ghost"><Icon name="star" size={14} /> 收藏</Button>
|
|
106
|
-
<Button size="sm" variant="ghost"><Icon name="bell" size={14} /> 通知</Button>
|
|
107
|
-
</Space>
|
|
108
|
-
<Space>
|
|
109
|
-
<span class="wf-text-secondary wf-text-sm">管理员</span>
|
|
110
|
-
<Badge variant="primary">角色</Badge>
|
|
111
|
-
</Space>
|
|
112
|
-
</div>
|
|
113
|
-
|
|
114
|
-
{/* 内容区(按导航切换) */}
|
|
115
|
-
<div class="wf-main wf-p-lg wf-scroll">
|
|
116
|
-
{nav === 'dashboard' && (
|
|
117
|
-
<>
|
|
118
|
-
<PageHeader title="仪表盘" sub="经营概览" display>
|
|
119
|
-
<Button variant="primary" onClick={() => { nav = 'orders'; rerender() }}><Icon name="plus" size={14} /> 去下单</Button>
|
|
120
|
-
</PageHeader>
|
|
121
|
-
<div class="wf-grid" style={{ '--wf-cols': 'repeat(auto-fill, minmax(220px, 1fr))' }}>
|
|
122
|
-
<StatCard label="今日订单" value="128" trend="up" trendLabel="+12% 昨日" icon={<Icon name="box" size={24} className="wf-text-primary" />} />
|
|
123
|
-
<StatCard label="待发货" value="23" trend="down" trendLabel="-5% 昨日" icon={<Icon name="inbox" size={24} className="wf-text-primary" />} />
|
|
124
|
-
<StatCard label="销售额" value="¥42,860" trend="up" trendLabel="+8.6% 昨日" icon={<Icon name="bar-chart" size={24} className="wf-text-primary" />} />
|
|
125
|
-
<StatCard label="客户数" value="1,024" trend="up" trendLabel="+3.2% 昨日" icon={<Icon name="users" size={24} className="wf-text-primary" />} />
|
|
126
|
-
</div>
|
|
127
|
-
</>
|
|
128
|
-
)}
|
|
129
|
-
{nav === 'orders' && (
|
|
130
|
-
<>
|
|
131
|
-
<PageHeader title="订单管理" sub="查看和处理所有订单" display>
|
|
132
|
-
<Button variant="primary"><Icon name="plus" size={14} /> 新建订单</Button>
|
|
133
|
-
</PageHeader>
|
|
134
|
-
|
|
135
|
-
<div class="wf-grid wf-mt-md" style={{ '--wf-cols': 'repeat(auto-fill, minmax(220px, 1fr))' }}>
|
|
136
|
-
<StatCard label="今日订单" value="128" trend="up" trendLabel="+12% 昨日" icon={<Icon name="box" size={24} className="wf-text-primary" />} />
|
|
137
|
-
<StatCard label="待发货" value="23" trend="down" trendLabel="-5% 昨日" icon={<Icon name="inbox" size={24} className="wf-text-primary" />} />
|
|
138
|
-
<StatCard label="销售额" value="¥42,860" trend="up" trendLabel="+8.6% 昨日" icon={<Icon name="bar-chart" size={24} className="wf-text-primary" />} />
|
|
139
|
-
</div>
|
|
140
|
-
|
|
141
|
-
<Divider />
|
|
142
|
-
|
|
143
|
-
<Card outlined>
|
|
144
|
-
<Table
|
|
145
|
-
data={ORDERS_ALL.slice((page - 1) * 5, page * 5)}
|
|
146
|
-
columns={[
|
|
147
|
-
{ key: 'id', label: '订单号' },
|
|
148
|
-
{ key: 'customer', label: '客户' },
|
|
149
|
-
{ key: 'amount', label: '金额' },
|
|
150
|
-
{ key: 'status', label: '状态', render: (v, row) => <Badge variant={row.v}>{v}</Badge> },
|
|
151
|
-
]}
|
|
152
|
-
/>
|
|
153
|
-
<div class="wf-p-md wf-between">
|
|
154
|
-
<Text className="wf-text-sm">共 {ORDERS_ALL.length} 条</Text>
|
|
155
|
-
<Pagination total={ORDERS_ALL.length} page={page} pageSize={5} onChange={(p) => { page = p; rerender() }} />
|
|
156
|
-
</div>
|
|
157
|
-
</Card>
|
|
158
|
-
</>
|
|
159
|
-
)}
|
|
160
|
-
{nav === 'users' && (
|
|
161
|
-
<>
|
|
162
|
-
<PageHeader title="用户管理" sub="平台用户与角色" display>
|
|
163
|
-
<Button variant="primary"><Icon name="plus" size={14} /> 邀请用户</Button>
|
|
164
|
-
</PageHeader>
|
|
165
|
-
<Card outlined>
|
|
166
|
-
<Table
|
|
167
|
-
data={USERS}
|
|
168
|
-
columns={[
|
|
169
|
-
{ key: 'id', label: '用户 ID' },
|
|
170
|
-
{ key: 'name', label: '姓名' },
|
|
171
|
-
{ key: 'role', label: '角色', render: (v, row) => <Badge variant={row.v}>{v}</Badge> },
|
|
172
|
-
{ key: 'email', label: '邮箱' },
|
|
173
|
-
]}
|
|
174
|
-
/>
|
|
175
|
-
</Card>
|
|
176
|
-
</>
|
|
177
|
-
)}
|
|
178
|
-
{nav === 'goods' && (
|
|
179
|
-
<>
|
|
180
|
-
<PageHeader title="商品管理" sub="库存与销售状态" display>
|
|
181
|
-
<Button variant="primary"><Icon name="plus" size={14} /> 上架商品</Button>
|
|
182
|
-
</PageHeader>
|
|
183
|
-
<Card outlined>
|
|
184
|
-
<Table
|
|
185
|
-
data={GOODS}
|
|
186
|
-
columns={[
|
|
187
|
-
{ key: 'id', label: '商品 ID' },
|
|
188
|
-
{ key: 'name', label: '名称' },
|
|
189
|
-
{ key: 'stock', label: '库存' },
|
|
190
|
-
{ key: 'price', label: '价格' },
|
|
191
|
-
{ key: 'status', label: '状态', render: (v, row) => <Badge variant={row.v}>{v}</Badge> },
|
|
192
|
-
]}
|
|
193
|
-
/>
|
|
194
|
-
</Card>
|
|
195
|
-
</>
|
|
196
|
-
)}
|
|
197
|
-
{nav === 'settings' && (
|
|
198
|
-
<>
|
|
199
|
-
<PageHeader title="系统设置" sub="基础配置" display>
|
|
200
|
-
<Button variant="primary"><Icon name="check" size={14} /> 保存</Button>
|
|
201
|
-
</PageHeader>
|
|
202
|
-
<Card outlined>
|
|
203
|
-
<div class="wf-stack wf-gap-md">
|
|
204
|
-
{[
|
|
205
|
-
['站点名称', 'Acme 管理台'],
|
|
206
|
-
['默认语言', '中文(简体)'],
|
|
207
|
-
['数据保留', '90 天'],
|
|
208
|
-
['时区', 'Asia/Shanghai'],
|
|
209
|
-
].map(([k, v]) => (
|
|
210
|
-
<div key={k} class="wf-between wf-p-md wf-border-b">
|
|
211
|
-
<span class="wf-text-secondary">{k}</span>
|
|
212
|
-
<b>{v}</b>
|
|
213
|
-
</div>
|
|
214
|
-
))}
|
|
215
|
-
</div>
|
|
216
|
-
</Card>
|
|
217
|
-
</>
|
|
218
|
-
)}
|
|
219
|
-
</div>
|
|
220
|
-
</main>
|
|
221
|
-
</div>
|
|
222
|
-
)
|
|
223
|
-
}
|
|
224
|
-
|
|
@@ -1,153 +0,0 @@
|
|
|
1
|
-
import type { Component } from 'weifuwu/vdom'
|
|
2
|
-
import { Title, Text, StatCard, PageHeader, ProgressBar, SegmentedControl, Switch, Table, Badge, Card, Divider, Icon, Space } from 'weifuwu/components'
|
|
3
|
-
|
|
4
|
-
// ─────────────────────────────────────────────────────────────
|
|
5
|
-
// 模式 3:仪表盘(Dashboard)
|
|
6
|
-
//
|
|
7
|
-
// 响应式卡片网格:KPI 行(StatCard)+ 数据表 + 侧栏概览。
|
|
8
|
-
// SegmentedControl 切换时间范围 → KPI/表格数据联动(let + render())。
|
|
9
|
-
// 100% 原语 + 组件:wf-grid(--wf-cols 控制列数)窄屏自动单列。
|
|
10
|
-
// ─────────────────────────────────────────────────────────────
|
|
11
|
-
|
|
12
|
-
interface PeriodData {
|
|
13
|
-
kpis: { label: string; value: string; trend: 'up' | 'down'; trendLabel: string; icon: string }[]
|
|
14
|
-
rows: { name: string; sales: string; growth: string; status: string; v: 'success' | 'default' | 'warning' | 'danger' }[]
|
|
15
|
-
total: string
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const DATA: Record<string, PeriodData> = {
|
|
19
|
-
'7d': {
|
|
20
|
-
kpis: [
|
|
21
|
-
{ label: '总营收', value: '¥32.4万', trend: 'up' as const, trendLabel: '8.2% 环比', icon: 'bar-chart' },
|
|
22
|
-
{ label: '活跃用户', value: '2,156', trend: 'up' as const, trendLabel: '5.1% 环比', icon: 'users' },
|
|
23
|
-
{ label: '转化率', value: '7.4%', trend: 'up' as const, trendLabel: '1.2% 环比', icon: 'trending-up' },
|
|
24
|
-
{ label: '客单价', value: '¥312', trend: 'up' as const, trendLabel: '3.8% 环比', icon: 'tag' },
|
|
25
|
-
],
|
|
26
|
-
rows: [
|
|
27
|
-
{ name: '新品 B', sales: '¥12.8万', growth: '22.6%', status: '热销', v: 'success' },
|
|
28
|
-
{ name: '商品 A', sales: '¥10.5万', growth: '15.2%', status: '热销', v: 'success' },
|
|
29
|
-
{ name: '商品 C', sales: '¥6.3万', growth: '2.4%', status: '稳定', v: 'default' },
|
|
30
|
-
],
|
|
31
|
-
total: '¥32,480',
|
|
32
|
-
},
|
|
33
|
-
'30d': {
|
|
34
|
-
kpis: [
|
|
35
|
-
{ label: '总营收', value: '¥128.6万', trend: 'up' as const, trendLabel: '12.4% 环比', icon: 'bar-chart' },
|
|
36
|
-
{ label: '活跃用户', value: '8,432', trend: 'up' as const, trendLabel: '3.2% 环比', icon: 'users' },
|
|
37
|
-
{ label: '转化率', value: '6.8%', trend: 'up' as const, trendLabel: '0.5% 环比', icon: 'trending-up' },
|
|
38
|
-
{ label: '客单价', value: '¥286', trend: 'down' as const, trendLabel: '2.1% 环比', icon: 'tag' },
|
|
39
|
-
],
|
|
40
|
-
rows: [
|
|
41
|
-
{ name: '商品 A', sales: '¥42.8万', growth: '18.2%', status: '热销', v: 'success' },
|
|
42
|
-
{ name: '商品 B', sales: '¥36.5万', growth: '9.7%', status: '稳定', v: 'default' },
|
|
43
|
-
{ name: '商品 C', sales: '¥21.3万', growth: '-4.1%', status: '下滑', v: 'warning' },
|
|
44
|
-
{ name: '商品 D', sales: '¥15.9万', growth: '22.6%', status: '热销', v: 'success' },
|
|
45
|
-
],
|
|
46
|
-
total: '¥1,286,400',
|
|
47
|
-
},
|
|
48
|
-
'90d': {
|
|
49
|
-
kpis: [
|
|
50
|
-
{ label: '总营收', value: '¥356.2万', trend: 'up' as const, trendLabel: '18.6% 环比', icon: 'bar-chart' },
|
|
51
|
-
{ label: '活跃用户', value: '24,680', trend: 'up' as const, trendLabel: '9.8% 环比', icon: 'users' },
|
|
52
|
-
{ label: '转化率', value: '6.1%', trend: 'down' as const, trendLabel: '0.3% 环比', icon: 'trending-up' },
|
|
53
|
-
{ label: '客单价', value: '¥268', trend: 'down' as const, trendLabel: '4.2% 环比', icon: 'tag' },
|
|
54
|
-
],
|
|
55
|
-
rows: [
|
|
56
|
-
{ name: '商品 B', sales: '¥98.5万', growth: '31.4%', status: '热销', v: 'success' },
|
|
57
|
-
{ name: '商品 A', sales: '¥86.2万', growth: '12.8%', status: '稳定', v: 'default' },
|
|
58
|
-
{ name: '新品 D', sales: '¥52.1万', growth: '45.2%', status: '热销', v: 'success' },
|
|
59
|
-
{ name: '商品 C', sales: '¥38.9万', growth: '-6.3%', status: '下滑', v: 'warning' },
|
|
60
|
-
],
|
|
61
|
-
total: '¥3,562,000',
|
|
62
|
-
},
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
export const Dashboard: Component = async (_init, ctx) => {
|
|
66
|
-
let period = '30d'
|
|
67
|
-
let showGoal = true
|
|
68
|
-
const rerender = () => ctx.render()
|
|
69
|
-
|
|
70
|
-
return async () => {
|
|
71
|
-
const data = DATA[period] ?? DATA['30d']
|
|
72
|
-
return (
|
|
73
|
-
<div class="wf-stack wf-gap-lg wf-p-lg wf-scroll" style={{ minHeight: 'calc(100vh - 48px)' }}>
|
|
74
|
-
{/* 页头 + 时间范围切换(wf-row 提供 wrap——窄屏切换器折行,不挤压标题) */}
|
|
75
|
-
<div class="wf-row wf-between">
|
|
76
|
-
<PageHeader title="经营仪表盘" sub="实时经营数据 · 自动刷新" />
|
|
77
|
-
<SegmentedControl
|
|
78
|
-
value={period}
|
|
79
|
-
onChange={(v) => { period = v; rerender() }}
|
|
80
|
-
options={[
|
|
81
|
-
{ value: '7d', label: '近 7 天' },
|
|
82
|
-
{ value: '30d', label: '近 30 天' },
|
|
83
|
-
{ value: '90d', label: '近 90 天' },
|
|
84
|
-
]}
|
|
85
|
-
/>
|
|
86
|
-
</div>
|
|
87
|
-
|
|
88
|
-
{/* KPI 行:wf-grid 自适应(窄屏自动单列) */}
|
|
89
|
-
<div class="wf-grid" style={{ '--wf-cols': 'repeat(auto-fill, minmax(240px, 1fr))' }}>
|
|
90
|
-
{data.kpis.map((k) => (
|
|
91
|
-
<StatCard key={k.label} label={k.label} value={k.value} trend={k.trend} trendLabel={k.trendLabel} icon={<Icon name={k.icon as any} size={24} className="wf-text-primary" />} />
|
|
92
|
-
))}
|
|
93
|
-
</div>
|
|
94
|
-
|
|
95
|
-
{/* 内容区:销售排行 + 侧栏 */}
|
|
96
|
-
<div class="wf-grid" style={{ '--wf-cols': '2fr 1fr', alignItems: 'start' }}>
|
|
97
|
-
<Card outlined>
|
|
98
|
-
<Space direction="vertical">
|
|
99
|
-
<Title level={4}>商品销售排行 · {period}</Title>
|
|
100
|
-
<Table
|
|
101
|
-
data={data.rows}
|
|
102
|
-
columns={[
|
|
103
|
-
{ key: 'name', label: '商品' },
|
|
104
|
-
{ key: 'sales', label: '销售额' },
|
|
105
|
-
{ key: 'growth', label: '增长率' },
|
|
106
|
-
{ key: 'status', label: '状态', render: (v, row) => <Badge variant={row.v}>{v}</Badge> },
|
|
107
|
-
]}
|
|
108
|
-
/>
|
|
109
|
-
</Space>
|
|
110
|
-
</Card>
|
|
111
|
-
|
|
112
|
-
{/* 侧栏概览 */}
|
|
113
|
-
<div class="wf-stack wf-gap-md">
|
|
114
|
-
<Card outlined>
|
|
115
|
-
<Space direction="vertical" size="sm">
|
|
116
|
-
<Text type="secondary" className="wf-text-sm">本期总额</Text>
|
|
117
|
-
<Text strong className="wf-text-lg">{data.total}</Text>
|
|
118
|
-
<Text type="success" className="wf-text-sm">↑ 18.6% vs 上一期</Text>
|
|
119
|
-
</Space>
|
|
120
|
-
</Card>
|
|
121
|
-
<Card outlined>
|
|
122
|
-
<Space direction="vertical" size="sm">
|
|
123
|
-
<div class="wf-between">
|
|
124
|
-
<Text type="secondary" className="wf-text-sm">季度目标</Text>
|
|
125
|
-
<Switch
|
|
126
|
-
label="展示"
|
|
127
|
-
checked={showGoal}
|
|
128
|
-
onChange={(v) => { showGoal = v; rerender() }}
|
|
129
|
-
/>
|
|
130
|
-
</div>
|
|
131
|
-
{showGoal && (
|
|
132
|
-
<Space direction="vertical" size="sm">
|
|
133
|
-
<ProgressBar value={78} max={100} label="达成率" showValue />
|
|
134
|
-
<Text type="secondary" className="wf-text-xs">距离 ¥150 万目标还差 ¥21.4 万</Text>
|
|
135
|
-
</Space>
|
|
136
|
-
)}
|
|
137
|
-
</Space>
|
|
138
|
-
</Card>
|
|
139
|
-
<Card outlined>
|
|
140
|
-
<Space direction="vertical" size="sm">
|
|
141
|
-
<Text type="secondary" className="wf-text-sm">数据刷新</Text>
|
|
142
|
-
<Text className="wf-text-sm">切换时间范围观察数据联动(let + render() 状态)</Text>
|
|
143
|
-
</Space>
|
|
144
|
-
</Card>
|
|
145
|
-
<Divider />
|
|
146
|
-
<Text type="secondary" className="wf-text-sm">本页展示了 wf-grid + StatCard + $ 状态组合——无需任何媒体查询即可响应式。</Text>
|
|
147
|
-
</div>
|
|
148
|
-
</div>
|
|
149
|
-
</div>
|
|
150
|
-
)
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
import type { Component } from 'weifuwu/vdom'
|
|
2
|
-
import {Text, Badge, Card, Icon, Sparkline, StatCard, Space } from 'weifuwu/components'
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
// ─────────────────────────────────────────────────────────────
|
|
6
|
-
// 模式 7:数据大屏(Data Screen)
|
|
7
|
-
//
|
|
8
|
-
// 全屏网格 + 固定角标(标题/时间/状态灯)。
|
|
9
|
-
// 100% 原语 + 组件:wf-fill(撑满)、wf-layer + wf-pop(容器内角标——
|
|
10
|
-
// 嵌进任意父容器不错位;全屏使用等价 wf-pin)、wf-grid(大屏格)
|
|
11
|
-
// Sparkline(曲线)、StatCard、Badge dot(状态灯)
|
|
12
|
-
// ─────────────────────────────────────────────────────────────
|
|
13
|
-
|
|
14
|
-
const SERIES = {
|
|
15
|
-
cpu: [42, 45, 38, 52, 48, 61, 55, 72, 68, 75, 70, 66, 78, 82],
|
|
16
|
-
mem: [30, 32, 35, 33, 38, 42, 40, 45, 43, 48, 52, 50, 55, 58],
|
|
17
|
-
net: [20, 28, 25, 40, 35, 52, 48, 44, 60, 55, 68, 62, 74, 80],
|
|
18
|
-
qps: [120, 145, 138, 162, 158, 190, 175, 210, 198, 235, 220, 245, 260, 280],
|
|
19
|
-
latency: [38, 42, 35, 48, 52, 45, 58, 62, 55, 68, 72, 65, 74, 70],
|
|
20
|
-
disk: [45, 46, 48, 47, 50, 52, 51, 55, 58, 57, 62, 64, 66, 68],
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
const CARDS = [
|
|
24
|
-
{ title: 'CPU 使用率', key: 'cpu', unit: '%' },
|
|
25
|
-
{ title: '内存占用', key: 'mem', unit: '%' },
|
|
26
|
-
{ title: '网络吞吐', key: 'net', unit: 'MB/s' },
|
|
27
|
-
{ title: '请求 QPS', key: 'qps', unit: '/s' },
|
|
28
|
-
{ title: '接口延迟', key: 'latency', unit: 'ms' },
|
|
29
|
-
{ title: '磁盘使用', key: 'disk', unit: '%' },
|
|
30
|
-
]
|
|
31
|
-
|
|
32
|
-
const tick = (series: number[]) => {
|
|
33
|
-
const base = series[series.length - 1]
|
|
34
|
-
const next = Math.max(5, Math.min(95, base + (Math.random() * 8 - 4)))
|
|
35
|
-
return [...series.slice(1), Math.round(next)]
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export const DataScreen: Component = async (_init, ctx) => {
|
|
39
|
-
// render-only:内部状态 let + 显式 render(实时曲线数据 + 时间)
|
|
40
|
-
const series: Record<string, number[]> = {}
|
|
41
|
-
for (const c of CARDS) series[c.key] = [...SERIES[c.key as keyof typeof SERIES]]
|
|
42
|
-
let time = new Date().toTimeString().slice(0, 8)
|
|
43
|
-
const today = new Date().toISOString().slice(0, 10)
|
|
44
|
-
// 实时刷新:2 秒滚动更新全部曲线 + 时间(布局蓝本演示实时监控)
|
|
45
|
-
const timer = setInterval(() => {
|
|
46
|
-
for (const c of CARDS) series[c.key] = tick(series[c.key])
|
|
47
|
-
time = new Date().toTimeString().slice(0, 8)
|
|
48
|
-
ctx.render()
|
|
49
|
-
}, 2000)
|
|
50
|
-
|
|
51
|
-
// ref 纪律:稳定引用定义在 mount 作用域——ref(null) 只在真正卸载时调用
|
|
52
|
-
// (内联 ref 每次渲染新引用 → 反复 clearInterval → 数据永不更新)
|
|
53
|
-
const rootRef = (el: any) => { if (!el) clearInterval(timer) }
|
|
54
|
-
|
|
55
|
-
return async () => (
|
|
56
|
-
<div ref={rootRef} class="wf-fill wf-layer wf-stack wf-gap-md wf-p-md wf-bg-tertiary" style={{ height: 'calc(100vh - 48px)' }}>
|
|
57
|
-
{/* 顶部标题(容器内角标——wf-layer + wf-pop) */}
|
|
58
|
-
<div class="wf-pop wf-center" style={{ top: 16, left: 0, right: 0 }}>
|
|
59
|
-
<Space size="sm" align="center">
|
|
60
|
-
<Icon name="zap" size={18} className="wf-text-primary" />
|
|
61
|
-
<b class="wf-text-lg wf-text-bold wf-tracking-wider">实时运维监控中心</b>
|
|
62
|
-
</Space>
|
|
63
|
-
</div>
|
|
64
|
-
{/* 右上时间(实时) */}
|
|
65
|
-
<div class="wf-pop" style={{ top: 16, right: 24 }}>
|
|
66
|
-
<Text type="secondary" className="wf-text-sm wf-nums">{today} {time}</Text>
|
|
67
|
-
</div>
|
|
68
|
-
{/* 左上状态灯 */}
|
|
69
|
-
<div class="wf-pop wf-row wf-gap-sm" style={{ top: 16, left: 24, alignItems: 'center' }}>
|
|
70
|
-
<Badge variant="success" dot />
|
|
71
|
-
<Text type="secondary" className="wf-text-sm">运行正常</Text>
|
|
72
|
-
</div>
|
|
73
|
-
|
|
74
|
-
{/* 主体:指标网格 */}
|
|
75
|
-
<div class="wf-grid wf-fill wf-p-lg" style={{ '--wf-cols': 'repeat(auto-fill, minmax(280px, 1fr))', alignContent: 'center', paddingTop: 60 }}>
|
|
76
|
-
{CARDS.map((m) => (
|
|
77
|
-
<Card outlined key={m.title} padding="lg">
|
|
78
|
-
<Space direction="vertical" size="md">
|
|
79
|
-
<div class="wf-row wf-between">
|
|
80
|
-
<Text strong>{m.title}</Text>
|
|
81
|
-
<Space size="sm" align="center">
|
|
82
|
-
<Badge variant="success" dot />
|
|
83
|
-
<Text type="success" className="wf-text-sm">实时</Text>
|
|
84
|
-
</Space>
|
|
85
|
-
</div>
|
|
86
|
-
<Sparkline data={series[m.key]} width={280} height={64} />
|
|
87
|
-
<StatCard label="当前值" value={`${series[m.key][series[m.key].length - 1]}${m.unit}`} trend="up" trendLabel="最近采样" />
|
|
88
|
-
</Space>
|
|
89
|
-
</Card>
|
|
90
|
-
))}
|
|
91
|
-
</div>
|
|
92
|
-
|
|
93
|
-
{/* 底部滚动信息 */}
|
|
94
|
-
<div class="wf-pop wf-p-sm wf-border-t" style={{ bottom: 12, left: 24, right: 24 }}>
|
|
95
|
-
<Space size="lg">
|
|
96
|
-
<Text type="secondary" className="wf-text-xs">[INFO] 集群 12 节点健康 · 2s 实时刷新</Text>
|
|
97
|
-
<Text type="secondary" className="wf-text-xs">[INFO] 自动扩容已触发(us-east-1)</Text>
|
|
98
|
-
<Text type="warning" className="wf-text-xs">[WARN] 磁盘余量 18%</Text>
|
|
99
|
-
</Space>
|
|
100
|
-
</div>
|
|
101
|
-
</div>
|
|
102
|
-
)
|
|
103
|
-
}
|
|
104
|
-
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
import type { Component } from 'weifuwu/vdom'
|
|
2
|
-
import { PageHeader, Descriptions, Timeline, Tag, Button, Space, Card, Divider } from 'weifuwu/components'
|
|
3
|
-
|
|
4
|
-
// ─────────────────────────────────────────────────────────────
|
|
5
|
-
// 模式 10:详情页(Detail Page)
|
|
6
|
-
//
|
|
7
|
-
// 业务对象详情:标题栏 + 描述列表 + 状态时间线 + 操作区。
|
|
8
|
-
// 复制此文件即可得到一个标准的"详情页":
|
|
9
|
-
// - PageHeader(对象名 + 状态 Tag + 操作按钮)
|
|
10
|
-
// - Descriptions(label/value 栅格——详情字段)
|
|
11
|
-
// - Timeline(执行历史/审批流)
|
|
12
|
-
// - Card 分区(补充信息块)
|
|
13
|
-
// 改造:换 data 字段 → 自己的业务对象。
|
|
14
|
-
// ─────────────────────────────────────────────────────────────
|
|
15
|
-
|
|
16
|
-
const ORDER = {
|
|
17
|
-
id: 'SO-1003',
|
|
18
|
-
customer: '王强',
|
|
19
|
-
amount: 3200,
|
|
20
|
-
status: '已发货',
|
|
21
|
-
date: '2026-08-12',
|
|
22
|
-
address: '杭州市西湖区文三路 138 号',
|
|
23
|
-
note: '加急——客户要求周五前送达',
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const HISTORY = [
|
|
27
|
-
{ title: '订单创建', time: '2026-08-12 09:12', tone: 'success' as const },
|
|
28
|
-
{ title: '已支付', time: '2026-08-12 09:15' },
|
|
29
|
-
{ title: '仓库发货', time: '2026-08-12 16:40' },
|
|
30
|
-
{ title: '运输中', time: '2026-08-13 08:05' },
|
|
31
|
-
]
|
|
32
|
-
|
|
33
|
-
export const DetailPage: Component = async (_init: any, ctx: any) => {
|
|
34
|
-
let cancelled = false
|
|
35
|
-
return async (_p: any) => (
|
|
36
|
-
<div class="wf-container wf-stack" style="--wf-max:960px;--wf-gap:16px;padding:24px 16px">
|
|
37
|
-
<PageHeader title={`订单 ${ORDER.id}`} sub={`客户:${ORDER.customer} · 下单时间:${ORDER.date}`}>
|
|
38
|
-
<Space size="md">
|
|
39
|
-
<Tag variant="primary">{ORDER.status}</Tag>
|
|
40
|
-
<Button variant="ghost" onClick={() => ctx.render()}>
|
|
41
|
-
<Icon name="copy" size={14} /> 导出
|
|
42
|
-
</Button>
|
|
43
|
-
<Button variant="danger" disabled={cancelled} onClick={() => { cancelled = true; ctx.render() }}>
|
|
44
|
-
{cancelled ? '已取消' : '取消订单'}
|
|
45
|
-
</Button>
|
|
46
|
-
</Space>
|
|
47
|
-
</PageHeader>
|
|
48
|
-
|
|
49
|
-
<Descriptions
|
|
50
|
-
items={[
|
|
51
|
-
{ label: '订单号', value: ORDER.id },
|
|
52
|
-
{ label: '客户', value: ORDER.customer },
|
|
53
|
-
{ label: '金额', value: `¥${ORDER.amount.toLocaleString()}` },
|
|
54
|
-
{ label: '状态', value: ORDER.status },
|
|
55
|
-
{ label: '收货地址', value: ORDER.address, span: 2 },
|
|
56
|
-
{ label: '备注', value: cancelled ? '(已取消)' : ORDER.note, span: 2 },
|
|
57
|
-
]}
|
|
58
|
-
bordered
|
|
59
|
-
/>
|
|
60
|
-
|
|
61
|
-
<div class="wf-grid" style="--wf-cols:2fr 1fr;--wf-gap:16px">
|
|
62
|
-
<Card>
|
|
63
|
-
<div class="wf-text-bold wf-mb-sm">补充信息</div>
|
|
64
|
-
<div class="wf-text-secondary wf-text-sm wf-stack wf-gap-xs">
|
|
65
|
-
<span>支付方式:对公转账</span>
|
|
66
|
-
<span>发票:已开具(¥3,200.00)</span>
|
|
67
|
-
<span>业务员:陈晨</span>
|
|
68
|
-
</div>
|
|
69
|
-
</Card>
|
|
70
|
-
<Card>
|
|
71
|
-
<div class="wf-text-bold wf-mb-sm">执行历史</div>
|
|
72
|
-
<Timeline items={HISTORY.map((h, i) => ({ ...h, key: String(i) }))} />
|
|
73
|
-
</Card>
|
|
74
|
-
</div>
|
|
75
|
-
</div>
|
|
76
|
-
)
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
import { Icon } from 'weifuwu/components'
|