sky-ui-kit 0.1.12 → 0.1.13

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.
Files changed (201) hide show
  1. package/README.md +18 -13
  2. package/components.json +158 -1
  3. package/dist/chunks/{UiScrollbar-67b064c7.js → UiScrollbar-450d1521.js} +118 -118
  4. package/dist/components/MapCategoryMenu.vue.d.ts +36 -0
  5. package/dist/components/MapPageLayout.vue.d.ts +9 -9
  6. package/dist/components/MapSideMenu.vue.d.ts +4 -0
  7. package/dist/components/PlatformLayout.vue.d.ts +55 -0
  8. package/dist/components/index.d.ts +152 -0
  9. package/dist/components.js +2537 -2239
  10. package/dist/entries/AppSelect.js +1 -1
  11. package/dist/entries/AtlasMapTitle.js +1 -1
  12. package/dist/entries/AtlasProcessChart.js +1 -1
  13. package/dist/entries/AtlasProcessPanel.js +1 -1
  14. package/dist/entries/AtlasRankingPanel.js +1 -1
  15. package/dist/entries/AtlasRightToggleButton.js +1 -1
  16. package/dist/entries/DefenseMapLayout.js +1 -1
  17. package/dist/entries/GlobalMapLayerDock.js +1 -1
  18. package/dist/entries/ManagementFormDialog.js +1 -1
  19. package/dist/entries/ManagementResourceLayout.js +1 -1
  20. package/dist/entries/MapBottomProcessPanel.js +1 -1
  21. package/dist/entries/MapCategoryMenu.d.ts +1 -0
  22. package/dist/entries/MapCategoryMenu.js +10 -0
  23. package/dist/entries/MapEditorDialog.js +1 -1
  24. package/dist/entries/MapFeatureListPanel.js +1 -1
  25. package/dist/entries/MapLayerControl.js +1 -1
  26. package/dist/entries/MapOperationPanel.js +1 -1
  27. package/dist/entries/MapPageLayout.js +1 -1
  28. package/dist/entries/MapPointProcessDialog.js +1 -1
  29. package/dist/entries/MapSchemeSelect.js +1 -1
  30. package/dist/entries/MapSideMenu.js +1 -1
  31. package/dist/entries/MapTimeline.js +1 -1
  32. package/dist/entries/MapTools.js +1 -1
  33. package/dist/entries/MapValueLegend.js +1 -1
  34. package/dist/entries/MapWorkspacePanel.js +1 -1
  35. package/dist/entries/MetricCard.js +1 -1
  36. package/dist/entries/ModelWorkspace.js +1 -1
  37. package/dist/entries/ModelWorkspaceHeader.js +1 -1
  38. package/dist/entries/ModelWorkspacePanel.js +1 -1
  39. package/dist/entries/NationalHydroSidebar.js +1 -1
  40. package/dist/entries/PlatformLayout.d.ts +1 -0
  41. package/dist/entries/PlatformLayout.js +10 -0
  42. package/dist/entries/ProcessChart.js +1 -1
  43. package/dist/entries/ProcessDataTable.js +1 -1
  44. package/dist/entries/ProjectMenu.js +1 -1
  45. package/dist/entries/RainfallProcessChart.js +1 -1
  46. package/dist/entries/RainfallRankingTable.js +1 -1
  47. package/dist/entries/SpaceAnalysisPanel.js +1 -1
  48. package/dist/entries/SpatialScopeInput.js +1 -1
  49. package/dist/entries/StationComparisonPanel.js +1 -1
  50. package/dist/entries/SvgIcon.js +1 -1
  51. package/dist/entries/TemplateLayout.js +1 -1
  52. package/dist/entries/TimeRangeQuickSelect.js +1 -1
  53. package/dist/entries/TreeSelectPanel.js +1 -1
  54. package/dist/entries/UiAffix.js +3 -3
  55. package/dist/entries/UiAlert.js +3 -3
  56. package/dist/entries/UiAnchor.js +1 -1
  57. package/dist/entries/UiAnchorLink.js +1 -1
  58. package/dist/entries/UiAside.js +1 -1
  59. package/dist/entries/UiAutoResizer.js +1 -1
  60. package/dist/entries/UiAutocomplete.js +3 -3
  61. package/dist/entries/UiAvatar.js +3 -3
  62. package/dist/entries/UiAvatarGroup.js +2 -2
  63. package/dist/entries/UiBacktop.js +1 -1
  64. package/dist/entries/UiBadge.js +1 -1
  65. package/dist/entries/UiBreadcrumb.js +1 -1
  66. package/dist/entries/UiBreadcrumbItem.js +1 -1
  67. package/dist/entries/UiButton.js +1 -1
  68. package/dist/entries/UiButtonGroup.js +1 -1
  69. package/dist/entries/UiCalendar.js +1 -1
  70. package/dist/entries/UiCard.js +1 -1
  71. package/dist/entries/UiCarousel.js +1 -1
  72. package/dist/entries/UiCarouselItem.js +1 -1
  73. package/dist/entries/UiCascader.js +2 -2
  74. package/dist/entries/UiCascaderPanel.js +1 -1
  75. package/dist/entries/UiCheckTag.js +1 -1
  76. package/dist/entries/UiCheckbox.js +2 -2
  77. package/dist/entries/UiCheckboxButton.js +1 -1
  78. package/dist/entries/UiCheckboxGroup.js +1 -1
  79. package/dist/entries/UiCol.js +1 -1
  80. package/dist/entries/UiCollapse.js +1 -1
  81. package/dist/entries/UiCollapseItem.js +1 -1
  82. package/dist/entries/UiCollapseTransition.js +1 -1
  83. package/dist/entries/UiColorPicker.js +1 -1
  84. package/dist/entries/UiColorPickerPanel.js +1 -1
  85. package/dist/entries/UiConfigProvider.js +1 -1
  86. package/dist/entries/UiContainer.js +1 -1
  87. package/dist/entries/UiCountdown.js +1 -1
  88. package/dist/entries/UiDatePicker.js +1 -1
  89. package/dist/entries/UiDatePickerPanel.js +1 -1
  90. package/dist/entries/UiDescriptions.js +1 -1
  91. package/dist/entries/UiDescriptionsItem.js +1 -1
  92. package/dist/entries/UiDialog.js +3 -3
  93. package/dist/entries/UiDivider.js +1 -1
  94. package/dist/entries/UiDrawer.js +1 -1
  95. package/dist/entries/UiDropdown.js +1 -1
  96. package/dist/entries/UiDropdownItem.js +1 -1
  97. package/dist/entries/UiDropdownMenu.js +1 -1
  98. package/dist/entries/UiEmpty.js +1 -1
  99. package/dist/entries/UiFooter.js +1 -1
  100. package/dist/entries/UiForm.js +3 -3
  101. package/dist/entries/UiFormItem.js +2 -2
  102. package/dist/entries/UiHeader.js +1 -1
  103. package/dist/entries/UiIcon.js +1 -1
  104. package/dist/entries/UiImage.js +2 -2
  105. package/dist/entries/UiImageViewer.js +1 -1
  106. package/dist/entries/UiInput.js +2 -2
  107. package/dist/entries/UiInputNumber.js +3 -3
  108. package/dist/entries/UiInputOtp.js +3 -3
  109. package/dist/entries/UiInputTag.js +1 -1
  110. package/dist/entries/UiLink.js +1 -1
  111. package/dist/entries/UiLoading.js +1 -1
  112. package/dist/entries/UiMain.js +1 -1
  113. package/dist/entries/UiMention.js +1 -1
  114. package/dist/entries/UiMenu.js +3 -3
  115. package/dist/entries/UiMenuItem.js +3 -3
  116. package/dist/entries/UiMenuItemGroup.js +1 -1
  117. package/dist/entries/UiMessage.js +1 -1
  118. package/dist/entries/UiMessageBox.js +1 -1
  119. package/dist/entries/UiNotification.js +1 -1
  120. package/dist/entries/UiOption.js +3 -3
  121. package/dist/entries/UiOptionGroup.js +1 -1
  122. package/dist/entries/UiPageHeader.js +3 -3
  123. package/dist/entries/UiPagination.js +2 -2
  124. package/dist/entries/UiPopconfirm.js +1 -1
  125. package/dist/entries/UiPopover.js +1 -1
  126. package/dist/entries/UiPopper.js +1 -1
  127. package/dist/entries/UiProgress.js +3 -3
  128. package/dist/entries/UiRadio.js +1 -1
  129. package/dist/entries/UiRadioButton.js +1 -1
  130. package/dist/entries/UiRadioGroup.js +1 -1
  131. package/dist/entries/UiRate.js +3 -3
  132. package/dist/entries/UiResult.js +1 -1
  133. package/dist/entries/UiRow.js +1 -1
  134. package/dist/entries/UiScrollbar.js +3 -3
  135. package/dist/entries/UiSegmented.js +1 -1
  136. package/dist/entries/UiSelect.js +3 -3
  137. package/dist/entries/UiSelectV2.js +3 -3
  138. package/dist/entries/UiSkeleton.js +2 -2
  139. package/dist/entries/UiSkeletonItem.js +1 -1
  140. package/dist/entries/UiSlider.js +1 -1
  141. package/dist/entries/UiSpace.js +1 -1
  142. package/dist/entries/UiSplitter.js +1 -1
  143. package/dist/entries/UiSplitterPanel.js +1 -1
  144. package/dist/entries/UiStatistic.js +1 -1
  145. package/dist/entries/UiStep.js +1 -1
  146. package/dist/entries/UiSteps.js +1 -1
  147. package/dist/entries/UiSubMenu.js +1 -1
  148. package/dist/entries/UiSwitch.js +3 -3
  149. package/dist/entries/UiTabPane.js +1 -1
  150. package/dist/entries/UiTable.js +1 -1
  151. package/dist/entries/UiTableColumn.js +1 -1
  152. package/dist/entries/UiTableV2.js +1 -1
  153. package/dist/entries/UiTabs.js +1 -1
  154. package/dist/entries/UiTag.js +1 -1
  155. package/dist/entries/UiText.js +1 -1
  156. package/dist/entries/UiTimePicker.js +1 -1
  157. package/dist/entries/UiTimeSelect.js +1 -1
  158. package/dist/entries/UiTimeline.js +1 -1
  159. package/dist/entries/UiTimelineItem.js +1 -1
  160. package/dist/entries/UiTooltip.js +2 -2
  161. package/dist/entries/UiTour.js +1 -1
  162. package/dist/entries/UiTourStep.js +1 -1
  163. package/dist/entries/UiTransfer.js +1 -1
  164. package/dist/entries/UiTree.js +1 -1
  165. package/dist/entries/UiTreeSelect.js +1 -1
  166. package/dist/entries/UiTreeV2.js +1 -1
  167. package/dist/entries/UiUpload.js +3 -3
  168. package/dist/entries/UiWatermark.js +1 -1
  169. package/dist/entries/WorkbenchPanel.js +1 -1
  170. package/dist/entries/WorkbenchShell.js +1 -1
  171. package/dist/entries/vUiInfiniteScroll.js +1 -1
  172. package/dist/entries/vUiLoading.js +1 -1
  173. package/dist/entries/vUiPopover.js +1 -1
  174. package/dist/style.css +1 -1
  175. package/docs/AI-CHANGELOG.md +159 -55
  176. package/docs/AI-GUIDE.md +17 -9
  177. package/docs/COMPONENT-API.md +46 -0
  178. package/docs/CSS-THEME.md +104 -34
  179. package/docs/ELEMENT-PLUS-COVERAGE.md +1 -1
  180. package/docs/INTEGRATION.md +112 -14
  181. package/docs/START-HERE.md +13 -0
  182. package/docs/VUE-TEMPLATES.md +33 -0
  183. package/examples/vue-starter/.env.example +2 -0
  184. package/examples/vue-starter/AGENTS.md +3 -0
  185. package/examples/vue-starter/README.md +12 -0
  186. package/examples/vue-starter/index.html +1 -0
  187. package/examples/vue-starter/package.json +26 -0
  188. package/examples/vue-starter/src/App.vue +26 -0
  189. package/examples/vue-starter/src/ForecastPage.vue +65 -0
  190. package/examples/vue-starter/src/ManagementPage.vue +30 -0
  191. package/examples/vue-starter/src/MapPage.vue +34 -0
  192. package/examples/vue-starter/src/StarterBasemap.vue +52 -0
  193. package/examples/vue-starter/src/env.d.ts +1 -0
  194. package/examples/vue-starter/src/layout.css +12 -0
  195. package/examples/vue-starter/src/main.ts +8 -0
  196. package/examples/vue-starter/src/router.ts +14 -0
  197. package/examples/vue-starter/tsconfig.json +22 -0
  198. package/examples/vue-starter/vite.config.ts +3 -0
  199. package/llms-full.txt +374 -99
  200. package/llms.txt +3 -1
  201. package/package.json +107 -105
@@ -1,6 +1,8 @@
1
1
  # 主题接入规则更新
2
-
3
- 接入前必须先按 [引导式接入流程](START-HERE.md)询问用户 HTML/Vue 和目标范围,等待答案后再采用本文对应路线;不得从项目结构自行猜测。
2
+
3
+ 本轮新增 PlatformLayout 平台外壳(未发布):统一头部、地图贯穿背景、悬浮导航及管理分组侧栏。起步模板依赖该新出口,npm 0.1.12 尚不可直接使用;详见 [Vue 模板](VUE-TEMPLATES.md)。
4
+
5
+ 接入前必须先按 [引导式接入流程](START-HERE.md)询问用户 HTML/Vue 和目标范围,等待答案后再采用本文对应路线;不得从项目结构自行猜测。
4
6
 
5
7
  ## 新布局接入(源码尚未发布)
6
8
 
@@ -208,15 +210,111 @@ ManagementResourceLayout默认按VFit保持结构,不在700px阈值自动切
208
210
  <MapPageLayout :right-panel-width="320" :edge="12" :gap="8" />
209
211
  ```
210
212
  预览入口:MapPageLayout布局演示,点击“查看紧凑尺寸”比较右栏宽度、边距与面板内边距。其他组件参数与默认值可在生成API查询。当前未构建、打包、发布或浏览器验证,用户预览确认后自行发版。
211
-
212
- ## 2026-11-04:HTML 按需样式接入(未发布)
213
-
214
- 新增 theme/panel/button/input 四个 CSS 出口,完整 model-workspace.css 与拆分入口共享源码;网站新增隔离 HTML 示例。外观模式保留原 DOM/事件,不能当作 Vue 组件接入。结构、安装、状态、主题、浮层及 AGENTS.md 规则见 [HTML 接入](HTML-INTEGRATION.md)。仅生成文档和静态检查,未构建、发布或浏览器验证;历史“CSS 未拆分”对 Vue style.css 仍成立。
215
-
216
- ### 原生控件自动换肤调整(未发布)
217
-
218
- 新增 sky-html 容器入口,按需样式直接匹配原生 button/input/select/textarea;支持 data-sky-native="off" 本身及后代退出、Element Plus/地图内部控件排除,保留旧工作区 opt-in。主题默认低优先级,特殊按钮使用 data-variant,支持只读、禁用、错误及焦点状态;面板继续使用明确结构类,不覆盖所有 div。示例网站展示完整原生 HTML 状态及退出区域,详见 HTML-INTEGRATION.md。
219
-
220
- ## 2026-11-04:0.1.10 原生 HTML 迁移文档修订(未发布)
221
-
222
- 修正 AI 指南相对链接及 README 发布状态;新增透明地图覆盖层/主题面板/body 浮层示例,补齐旧页面 CSS 皮肤移除、布局保留和逐状态验收。迁移前后共用布局及业务脚本,不要求 Vue、不增加业务项目专用全局覆盖。详见 [HTML 迁移](HTML-INTEGRATION.md)和[迁移后示例](../examples/html/migration/after.html)。本轮仅文档、示例及生成器修订,未构建、发布或浏览器验证。
213
+
214
+ ## 2026-11-04:HTML 按需样式接入(未发布)
215
+
216
+ 新增 theme/panel/button/input 四个 CSS 出口,完整 model-workspace.css 与拆分入口共享源码;网站新增隔离 HTML 示例。外观模式保留原 DOM/事件,不能当作 Vue 组件接入。结构、安装、状态、主题、浮层及 AGENTS.md 规则见 [HTML 接入](HTML-INTEGRATION.md)。仅生成文档和静态检查,未构建、发布或浏览器验证;历史“CSS 未拆分”对 Vue style.css 仍成立。
217
+
218
+ ### 原生控件自动换肤调整(未发布)
219
+
220
+ 新增 sky-html 容器入口,按需样式直接匹配原生 button/input/select/textarea;支持 data-sky-native="off" 本身及后代退出、Element Plus/地图内部控件排除,保留旧工作区 opt-in。主题默认低优先级,特殊按钮使用 data-variant,支持只读、禁用、错误及焦点状态;面板继续使用明确结构类,不覆盖所有 div。示例网站展示完整原生 HTML 状态及退出区域,详见 HTML-INTEGRATION.md。
221
+
222
+ ## 2026-11-04:0.1.10 原生 HTML 迁移文档修订(未发布)
223
+
224
+ 修正 AI 指南相对链接及 README 发布状态;新增透明地图覆盖层/主题面板/body 浮层示例,补齐旧页面 CSS 皮肤移除、布局保留和逐状态验收。迁移前后共用布局及业务脚本,不要求 Vue、不增加业务项目专用全局覆盖。详见 [HTML 迁移](HTML-INTEGRATION.md)和[迁移后示例](../examples/html/migration/after.html)。本轮仅文档、示例及生成器修订,未构建、发布或浏览器验证。
225
+
226
+ ## Vue 地图与系统管理起步模板(未发布)
227
+
228
+ 新增模板选择问答及 examples/vue-starter 完整组合项目。使用真实库布局,包含地图待配置说明、查询/详情/过程区和管理查询/分页/新增编辑。实际地图引擎和底图须用户确认,不自行生成其他业务工作台。见 [模板指南](VUE-TEMPLATES.md)。本轮不构建、发布或浏览器验证。
229
+
230
+ ## Vue 平台路由与页面隔离修复(未发布)
231
+
232
+ 起步项目使用 Vue Router hash 路由区分地图页与管理页,RouterView + KeepAlive 缓存页面。管理页增加唯一根容器,修复多根节点配合 v-show 导致两页同时显示;头部改为按内容宽度排列、修正地图模式高度及管理滚动边界,配置中文组件语言。不声称已通过浏览器视觉验收。
233
+
234
+ ## 一张图参考模板组合完善(未发布)
235
+
236
+ 起步地图页复用 AtlasMapTitle、RainfallRankingTable、MapValueLegend、AtlasRightToggleButton 及 MapTools,替换通用表格/卡片。新增 MapCategoryMenu 组合分类胶囊菜单:groups/activeGroup/active/compact/dark,update:activeGroup/update:active/update:compact,无插槽;分类皮肤复用 MapSideMenu。PlatformLayout 顶部收紧为参考平台的独立品牌/导航/工具胶囊。数据均显式为示例,图例开关只更新示例图例,不声称操控真实图层;尚无真实地图/测站接口。未修改参考项目,不构建或发布。
237
+
238
+
239
+ ## 地图分类栈内容高度修正(未发布)
240
+
241
+ MapSideMenu 新增 embedded(默认 false)和 showTools(默认 true)。embedded 按内容撑高、工具栏参与文档流;showTools=false 不渲染工具栏也不预留工具空间。独立菜单默认行为保持不变。MapCategoryMenu 为每组启用 embedded,只有活动组显示工具,分类栈不设置内部滚动,由 MapPageLayout 左侧区域统一滚动,避免重复工具留白和双滚动条。消费项目无需补写菜单皮肤 CSS。已同步本地 Vue 起步预览源码;地图底图、业务站点数据仍需消费方接入,不代表完整地图效果已验证。
242
+
243
+
244
+ ## 起步项目真实底图接入(未发布)
245
+
246
+ Vue 起步项目的地图路线现通过 StarterBasemap.vue 初始化 MapLibre GL JS(^5.24.0),不再显示空占位层。默认使用 https://demotiles.maplibre.org/style.json 公共演示底图,中心为中国区域;它不等同于参考平台的中文底图、行政边界或业务测站。复制 .env.example 为 .env.local 后,可通过 VITE_MAP_STYLE_URL 指向自己的 MapLibre style JSON,修改后重启开发服务器。公共演示服务依赖外网,仅供起步验证,不承诺生产可用性;生产需配置自有或授权服务,保留数据署名。底图不添加原生导航控件,数据署名保留在左下角,避免右侧排行覆盖;容器变化自动 resize,离开地图页销毁实例,失败通过库 UiAlert 展示。CSS 顺序:MapLibre CSS、Element Plus CSS、库 style.css、应用布局 CSS,各导入一次。
247
+
248
+ sky-ui-kit 的地图 UI 组件不包含 initMap/createMap 引擎导出;参考平台的 initMap 属于其地图核心。地图引擎由起步应用依赖,不新增库的强制引擎依赖。本文更新覆盖此前“默认不加载地图”的起步说明。业务测站、行政区选择及图例仍为独立示例,尚未绑定底图操作。未进行浏览器视觉验证,也未构建发布。
249
+
250
+
251
+ ### 地图左侧滚动条统一(未发布)
252
+
253
+ MapPageLayout 左侧插槽改用库公开 UiScrollbar(封装 Element Plus ElScrollbar),高度跟随左右边距限定的可视区域。移除外层 overflow:auto,避免原生粗滚动条;MapCategoryMenu 仍按内容撑高,不创建第二个滚动容器。滚轮、触摸及键盘聚焦滚动沿用 ElScrollbar,滚动条皮肤使用现有主题,不在消费项目仿写。已同步本地起步预览;未进行浏览器视觉验证、构建或发布。
254
+
255
+
256
+ ## MapCategoryMenu 完整分类导航(未发布)
257
+
258
+ 替代此前逐分类嵌套 MapSideMenu 的实现。组件使用与 MapSideMenu 共用的 scoped map-side-menu.css,内部固定唯一搜索/图标切换工具区,下面由 UiScrollbar 滚动;放入 MapPageLayout 左侧时高度为100%,不依赖消费方复制皮肤。搜索覆盖全部分类、子项及其 children:分类命中显示整组,否则保留匹配分支,搜索时展开匹配组;清空或关闭搜索后恢复之前展开状态。点击分类展开/收起,选中分类和选中子项分别管理;切换分类不会自动选择子项。compact 只隐藏文字,按钮保留 title/可访问名称及焦点样式。无子项分类作为一级入口,不展开空内容,无匹配显示“未找到匹配菜单”。
259
+
260
+ API:groups 必填,为 {key,label,items:MapMenuItem[]}[];activeGroup/active 默认空字符串,compact/dark 默认 false。支持 v-model:active-group、v-model:active、v-model:compact;新增 select({groupKey,item}) 事件,仅点击具体子项触发。无插槽。分类 key 应唯一,组内子项 key 应唯一;本版展示子项及其一层 children,不提供任意深度树编辑或地图业务加载。
261
+
262
+ ```vue
263
+ <script setup>
264
+ import { ref } from 'vue'
265
+ import { MapPageLayout, MapCategoryMenu } from 'sky-ui-kit'
266
+ const group = ref('observed'), active = ref('rain'), compact = ref(false)
267
+ const groups = [{ key:'observed', label:'实况测站', items:[{key:'rain',label:'降水'},{key:'water',label:'水位'}] }, {key:'satellite',label:'卫星产品',items:[]}]
268
+ function select({groupKey,item}) { console.log('消费方加载图层',groupKey,item.key) }
269
+ </script>
270
+ <template>
271
+ <MapPageLayout :left-panel-width="230">
272
+ <template #left><MapCategoryMenu v-model:active-group="group" v-model:active="active" v-model:compact="compact" :groups="groups" @select="select" /></template>
273
+ </MapPageLayout>
274
+ </template>
275
+ ```
276
+
277
+ 组件父容器须有明确高度;上例 MapPageLayout 须放在有明确高度且 position:relative 的工作区。起步项目移除“布局预览”假子项,未接入分类保留一级入口,标题提示尚未接入业务图层。源码与本地预览同步,生成AI文档;浏览器视觉和交互仍待验证,未构建打包发布。
278
+
279
+
280
+ MapCategoryMenu 叶子分类修正(未发布):items=[] 为一级可选入口,仅触发 update:activeGroup,不显示子列表、连线、aria-expanded 或空菜单提示;有子项的分类才展开/收起。全局搜索无匹配仍显示“未找到匹配菜单”。业务是否接入由消费页说明,不由导航组件伪装为子菜单。
281
+
282
+
283
+ ### 起步项目顶部菜单收敛(未发布)
284
+
285
+ 顶部仅保留“一张图”“预报”“系统设置”三个入口,分别对应 /map/atlas、/map/forecast、/management/users。系统设置继续使用管理布局与已有用户管理示例。预报目前复用地图布局,尚未接入真实预报数据或独立预报工作流;不再把中尺度、水文、中长期、调度及模型等条目直接铺在顶部。本次只修改示例菜单配置,不把具体业务栏目写死在 PlatformLayout 组件中。未构建发布或浏览器验证。
286
+
287
+
288
+ 起步地图交互调整(未发布):不注册 MapLibre NavigationControl,不显示原生放大、缩小、指南针按钮;滚轮缩放、拖动及触摸手势保留。数据署名 AttributionControl 保留,未通过隐藏控件容器的全局 CSS 移除署名。同步起步示例及本地预览,未构建发布或浏览器验证。
289
+
290
+
291
+ ### 平台顶部导航切换稳定性(未发布)
292
+
293
+ PlatformLayout 地图/管理两种模式统一头部高度(header-height 默认52px加6px顶部留白)、品牌/导航/工具胶囊的边框、圆角、内边距和阴影;主导航相对整个平台头部水平居中,不受行政区选择器显示隐藏影响。导航默认、悬停、聚焦与选中保持相同边框宽度和字重,不使用位移反馈。管理内容区同步避让统一头部高度。模式仍保留地图透明背景和管理实底背景,仅改变内容布局而不重设顶部导航尺寸。源码预览和AI文档同步,类型检查验证;未浏览器验证、构建发布,实际切换效果待确认。
294
+
295
+
296
+ ### 起步地图菜单精简(未发布)
297
+
298
+ 起步项目左侧仅保留“实况(测站) → 降水”,移除其余气象、水文分类和子菜单。排行、图例及过程区继续使用明确标记的模拟数据,无实况接口;通用 MapCategoryMenu 的多分类能力保持可配置,不将示例精简写死进组件。已同步本地预览,未构建发布或浏览器验证。
299
+
300
+
301
+ ### 起步地图模拟菜单(未发布)
302
+
303
+ 在“实况(测站) → 降水”之外,增加三个明确标记的模拟入口:“预报产品(模拟)”(逐小时/逐日两个模拟子项)、“卫星影像(模拟)”、“视频监控(模拟)”。用于检查分组展开、叶子入口选中及搜索;标题明确提示未接入业务图层,不模拟真实接口,右侧仍为降水示例数据。共四个一级分类,避免恢复此前过长菜单。同步本地预览及AI文档,未构建发布或浏览器验证。
304
+
305
+
306
+ ### 地图工具栏统一与演示测站(未发布)
307
+
308
+ 起步项目使用 MapTools.before 插槽容纳 AtlasRightToggleButton,折叠按钮与其他工具共用一个背景面板,不再拼接不同宽度的两组控件。两组件使用 --userssite-map-tool-size(默认34个cp单位)、border-box、相同图标字号;hover/active不向上位移。颜色和边框继续使用库主题变量,不添加消费方仿写皮肤。工具仍由消费方处理业务动作。
309
+
310
+ StarterBasemap 增加六个固定演示测站标记,点击显示站名、模拟雨量及非实况/演示坐标说明;使用 MapLibre Marker/Popup,卸载时清理。站点不代表真实观测,不与右侧排行或图例联动。同步本地预览与AI文档,未构建发布或浏览器验证。
311
+
312
+
313
+ ### 预报独立录入/结果布局(未发布)
314
+
315
+ 起步项目 /map/forecast 使用独立 ForecastPage.vue,不再复用一张图菜单、排行或底部过程表。左侧 MapWorkspacePanel(320)承载 UiForm 信息录入:方案名称、区域、演示模型、时长、输出间隔及底部生成/重置;右侧面板(420)承载结果空状态、参数快照、累计模拟降水与分时表格,中间保留真实底图。使用现有库组件,不新增业务皮肤。点击生成使用固定规则生成模拟结果,无真实预报API;名称必填,间隔不可大于时长。参数变化标记结果过期,重置同时清空结果。模拟区域尚未与地图范围联动,地图演示测站仍为独立数据。顶部预报高亮由独立路由控制。类型检查,未浏览器验证、构建发布。本文覆盖此前预报复用地图页的说明。
316
+
317
+
318
+ ### 预报工具栏补齐(未发布)
319
+
320
+ ForecastPage 在 MapPageLayout.toolbar 中复用 MapTools 和 before 插槽内的 AtlasRightToggleButton,统一显示在右侧结果面板左边。支持结果面板收起/展开、生成模拟结果、重置参数及结果;面板收起后工具栏随布局贴近右边缘,生成成功自动展开结果。与一张图共用组件皮肤,不添加页面级工具栏外观。同步起步源码、本地预览与AI文档,未浏览器验证或构建发布。
@@ -35,6 +35,19 @@ Vue 用户:
35
35
 
36
36
  答案已明确时不重复问;缺失答案不能通过等待超时自动补成默认选择。用户暂未回答时只做不依赖答案的读取工作。
37
37
 
38
+ ## Vue 新项目:必须继续询问布局模板
39
+
40
+ 当用户选择 Vue 真实组件并要求“搭建项目”时,不能直接自行设计项目工作台。继续问:
41
+
42
+ > 你希望使用哪种起步布局?
43
+ > 1. 地图一张图:查询面板、工具、图例、详情侧栏和底部过程区
44
+ > 2. 系统管理:顶部栏、侧栏、查询、表格、分页和新增编辑弹窗
45
+ > 3. 两种布局都要,提供切换入口
46
+ > 4. 只要基础空项目
47
+
48
+ 等待用户选择;已明确同时需要两种布局时直接采用,不重复询问。地图路线再询问使用已有地图引擎/底图,还是先保留明确待配置的地图容器;不能擅自选地图服务、申请 token 或用背景图冒充真实地图。
49
+ 实现必须先读 [Vue 起步模板](VUE-TEMPLATES.md),优先复制包内 examples/vue-starter 的实际文件,按用户选择裁剪;不自行发明 TeamAgent 等品牌或业务模块。
50
+
38
51
  ## 第三步:实施所选路线
39
52
 
40
53
  | 用户选择 | 阅读资料 | 实施要求 |
@@ -0,0 +1,33 @@
1
+ # Vue 起步项目(0.1.13)
2
+
3
+ 先按 [START-HERE](START-HERE.md) 确认 HTML/Vue 与模板目标。Vue 组合起步项目随 0.1.13 发布,完整源码位于 examples/vue-starter;复制整个目录到新项目,再运行 npm install、npm run dev。全部组件从 sky-ui-kit@0.1.13 导入,无本机源码路径、alias或组件快照。
4
+
5
+ ## 三个入口
6
+
7
+ | 菜单 | 路由 | 文件与内容 |
8
+ |---|---|---|
9
+ | 一张图 | /map/atlas | MapPage.vue:分类导航、降水示例排行、图例、工具栏和底部过程表 |
10
+ | 预报 | /map/forecast | ForecastPage.vue:左侧录入、中间地图、右侧结果、工具栏 |
11
+ | 系统设置 | /management/users | ManagementPage.vue:查询、新增编辑、分页及管理侧栏 |
12
+
13
+ [App.vue](../examples/vue-starter/src/App.vue) 使用 PlatformLayout、单一 RouterView 与 KeepAlive,切换时保留页面状态。[配置](../examples/vue-starter/package.json)、[路由](../examples/vue-starter/src/router.ts)、[入口](../examples/vue-starter/src/main.ts)、[说明](../examples/vue-starter/README.md) 均随包提供。若只需要一种布局,移除其余路由及菜单,不移除共享主题。
14
+
15
+ ## 地图
16
+
17
+ StarterBasemap.vue 由应用依赖 MapLibre GL JS ^5.24.0 初始化,库不导出 initMap/createMap。默认请求 https://demotiles.maplibre.org/style.json 公共演示底图;可复制 .env.example 为 .env.local 并设置 VITE_MAP_STYLE_URL(MapLibre style JSON URL),重启服务生效。公共服务依赖外网,无生产可用性承诺,生产使用自有或授权底图并保留署名。底图不等同参考平台中文底图。无原生放大缩小/指南针按钮,保留拖动缩放手势。
18
+
19
+ 六个固定模拟测站可点击查看模拟雨量,不代表真实坐标或实况。排行、图例、过程表是示例数据,点排行仅打开过程示例,不与地图定位联动。左侧仅降水及三个明确模拟菜单。MapCategoryMenu 固定顶部全菜单搜索/图标切换、内部 UiScrollbar;无子项分类为一级入口,不显示空子菜单。工具由 MapTools 与 before 插槽内的 AtlasRightToggleButton 组合,不复制皮肤。
20
+
21
+ ## 预报
22
+
23
+ MapWorkspacePanel 左侧录入方案名称、区域、模型、时长和间隔,生成后右侧展示固定规则模拟结果。名称必填、间隔不可大于时长,参数修改提示重新生成,重置清空结果。工具栏支持结果面板显隐、生成和重置。无真实预报计算或接口,区域选择不联动地图。
24
+
25
+ ## 系统设置
26
+
27
+ ManagementResourceLayout、ManagementFormDialog、UiPagination 实现用户名称/状态筛选、每页5条、新增编辑与名称必填。数据仅内存,刷新重置,无认证、数据库或后端服务。其他侧栏页面明确为未实现入口。
28
+
29
+ ## 主题与验证边界
30
+
31
+ main.ts 依次且仅一次引入 MapLibre CSS、Element Plus CSS、sky-ui-kit/style.css 和应用 layout.css。字体不随包提供,遵循 [CSS-THEME](CSS-THEME.md);浮层由库主题处理,应用只写布局。PlatformLayout 两种模式的顶部导航保持一致尺寸与位置。
32
+
33
+ 发布检查包括库类型检查、包构建、包内容检查及独立消费项目安装/类型检查/生产构建;结果见发布交付说明。没有浏览器视觉验证,不能把安装或构建成功当成视觉验收。新目录测试须检查三个入口、地图、菜单与搜索、工具栏、预报生成重置、查询分页和新增编辑及主题浮层。
@@ -0,0 +1,2 @@
1
+ # Optional MapLibre style JSON URL. Default: public demonstration basemap.
2
+ VITE_MAP_STYLE_URL=https://demotiles.maplibre.org/style.json
@@ -0,0 +1,3 @@
1
+ # 起步项目规则
2
+
3
+ 用户已确认 Vue 真实组件路线;地图/管理模板按用户选择保留。先读 node_modules/sky-ui-kit/llms.txt、docs/START-HERE.md、docs/VUE-TEMPLATES.md。旧版本未带模板文档时以随模板提供的 README 为准并明确报告版本差异。组件、布局和皮肤使用包内真实导出,不另写皮肤。确认地图引擎、底图和数据来源后才加载。示例数据和未配置地图必须明确标注;验收查询、重置、分页、新增编辑、面板开关及主题。
@@ -0,0 +1,12 @@
1
+ # sky-ui-kit Vue 组合起步项目
2
+
3
+ 依赖 sky-ui-kit@0.1.13。复制整个目录到独立项目后运行:
4
+
5
+ ```sh
6
+ npm install
7
+ npm run dev
8
+ ```
9
+
10
+ 顶部提供一张图、预报、系统设置。地图默认加载 MapLibre 公共演示底图;复制 .env.example 为 .env.local 可设置 VITE_MAP_STYLE_URL。支持模拟站点、分类菜单与降水排行;预报左侧录入、右侧展示模拟结果;系统设置提供内存用户查询、分页、新增编辑。所有数据均为演示,非实况,无真实认证或业务API。地图底图依赖外网,生产需自有或授权服务。
11
+
12
+ 全部UI组件从安装包导入,不需要源码目录或本地快照。入口CSS按MapLibre、Element Plus、库主题、应用布局顺序加载。详情见安装包 docs/VUE-TEMPLATES.md;AI先阅读 docs/START-HERE.md,再将确认的接入约定写入本项目 AGENTS.md。切换使用Hash路由与KeepAlive保留页面状态,刷新清空内存业务数据。
@@ -0,0 +1 @@
1
+ <!doctype html><html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>地图与系统管理起步模板</title></head><body><div id="app"></div><script type="module" src="/src/main.ts"></script></body></html>
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "sky-ui-kit-layout-starter",
3
+ "version": "0.0.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite --host 127.0.0.1",
8
+ "typecheck": "vue-tsc --noEmit",
9
+ "build": "vue-tsc --noEmit && vite build"
10
+ },
11
+ "dependencies": {
12
+ "sky-ui-kit": "0.1.13",
13
+ "vue": "^3.4.21",
14
+ "element-plus": "^2.14.7",
15
+ "@element-plus/icons-vue": "^2.3.2",
16
+ "motion-v": "^2.3.0",
17
+ "vue-router": "^4.5.0",
18
+ "maplibre-gl": "^5.24.0"
19
+ },
20
+ "devDependencies": {
21
+ "vite": "^4.5.14",
22
+ "@vitejs/plugin-vue": "^4.5.2",
23
+ "typescript": "^5.4.2",
24
+ "vue-tsc": "^3.3.12"
25
+ }
26
+ }
@@ -0,0 +1,26 @@
1
+ <script setup lang="ts">
2
+ import { computed, ref } from 'vue'
3
+ import StarterBasemap from './StarterBasemap.vue'
4
+ import { useRoute, useRouter, RouterView } from 'vue-router'
5
+ import { PlatformLayout, UiButton, UiSpace, UiTag, UiSelect, UiOption, UiConfigProvider } from 'sky-ui-kit'
6
+ import zhCn from 'element-plus/es/locale/lang/zh-cn'
7
+ const route=useRoute(),router=useRouter()
8
+ const sidebarOpen=ref(true),dark=ref(false),region=ref('全国')
9
+ const management=computed(()=>route.name==='management')
10
+ const active=computed(()=>management.value?'management':route.name==='forecast'?'forecast':String(route.params.section||'atlas'))
11
+ const menu=computed(()=>String(route.params.page||'users'))
12
+ function navigate(key:string){void router.push(key==='management'?'/management/users':'/map/'+key)}
13
+ function theme(){dark.value=!dark.value;document.documentElement.classList.toggle('theme-dark',dark.value)}
14
+ const topMenus=[{key:'atlas',label:'一张图'},{key:'forecast',label:'预报'},{key:'management',label:'系统设置'}]
15
+ const groups=[{label:'权限与组织',items:[{key:'users',label:'用户管理'},{key:'roles',label:'角色管理'},{key:'departments',label:'组织机构'}]},{label:'系统配置',items:[{key:'menus',label:'菜单目录'},{key:'logs',label:'操作日志'}]}]
16
+ </script>
17
+ <template>
18
+ <UiConfigProvider :locale="zhCn">
19
+ <PlatformLayout title="气象水文一体化" :mode="management?'management':'map'" :active="active" :items="topMenus" :groups="groups" :menu-active="menu" v-model:sidebar-open="sidebarOpen" @update:active="navigate" @update:menu-active="router.push('/management/'+$event)">
20
+ <template #region><UiSelect v-if="!management" v-model="region" style="width:150px"><UiOption label="请选择行政区划" value="全国" /><UiOption label="长江流域" value="长江流域" /></UiSelect></template>
21
+ <template #utilities><UiSpace><UiButton text @click="theme">{{ dark?'明亮':'暗色' }}</UiButton><UiTag>演示用户</UiTag></UiSpace></template>
22
+ <template #map><StarterBasemap v-if="!management" /></template>
23
+ <RouterView v-slot="{Component}"><KeepAlive><component :is="Component" :region="region" /></KeepAlive></RouterView>
24
+ </PlatformLayout>
25
+ </UiConfigProvider>
26
+ </template>
@@ -0,0 +1,65 @@
1
+ <script setup lang="ts">
2
+ import { computed, reactive, ref } from 'vue'
3
+ import { TrendCharts, RefreshRight } from '@element-plus/icons-vue'
4
+ import { MapPageLayout, MapTools, AtlasRightToggleButton, MapWorkspacePanel, UiForm, UiFormItem, UiInput, UiSelect, UiOption, UiInputNumber, UiButton, UiSpace, UiAlert, UiEmpty, UiDescriptions, UiDescriptionsItem, UiTable, UiTableColumn, UiTag } from 'sky-ui-kit'
5
+ const defaults = () => ({ name:'降水预报演示', area:'长江流域', model:'demo', hours:24, step:6 })
6
+ const form = reactive(defaults())
7
+ const error = ref('')
8
+ const rightOpen = ref(true)
9
+ const result = ref<{ name:string; area:string; hours:number; signature:string; rows:{period:string;rain:number}[] }>()
10
+ const signature = computed(() => JSON.stringify(form))
11
+ const changed = computed(() => result.value && result.value.signature !== signature.value)
12
+ function generate() {
13
+ if (!form.name.trim()) { error.value = '请填写方案名称'; return }
14
+ if (!form.hours || !form.step || form.step > form.hours) { error.value = '输出间隔应小于或等于预报时长'; return }
15
+ error.value = ''
16
+ rightOpen.value = true
17
+ const rows = Array.from({ length:Math.ceil(form.hours / form.step) }, (_, index) => ({
18
+ period:`第${index * form.step}—${Math.min((index + 1) * form.step, form.hours)}小时`,
19
+ rain:Number((4 + ((index * 7 + form.hours) % 19) * 0.8).toFixed(1))
20
+ }))
21
+ result.value = { name:form.name.trim(), area:form.area, hours:form.hours, signature:signature.value, rows }
22
+ }
23
+ function reset() { Object.assign(form, defaults()); result.value = undefined; error.value = '' }
24
+ const total = computed(() => result.value?.rows.reduce((sum,row) => sum + row.rain, 0).toFixed(1))
25
+ </script>
26
+ <template>
27
+ <MapPageLayout v-model:right-open="rightOpen" :toolbar-top="12" :edge="8" :top-offset="4" :bottom-offset="8" :left-panel-width="320" :right-panel-width="420">
28
+ <template #left>
29
+ <MapWorkspacePanel title="预报信息录入" subtitle="录入参数,生成右侧模拟结果">
30
+ <UiAlert title="演示流程:无真实预报接口或计算模型" type="info" :closable="false" />
31
+ <UiForm :model="form" label-position="top" @submit.prevent="generate">
32
+ <UiFormItem label="方案名称" required><UiInput v-model="form.name" maxlength="40" placeholder="请输入方案名称" /></UiFormItem>
33
+ <UiFormItem label="预报区域"><UiSelect v-model="form.area" style="width:100%"><UiOption label="长江流域" value="长江流域" /><UiOption label="珠江流域" value="珠江流域" /><UiOption label="黄河流域" value="黄河流域" /></UiSelect></UiFormItem>
34
+ <UiFormItem label="模型"><UiSelect v-model="form.model" style="width:100%"><UiOption label="演示模型(固定规则)" value="demo" /></UiSelect></UiFormItem>
35
+ <UiFormItem label="预报时长(小时)"><UiInputNumber v-model="form.hours" :min="6" :max="168" :step="6" :precision="0" /></UiFormItem>
36
+ <UiFormItem label="输出间隔(小时)"><UiSelect v-model="form.step" style="width:100%"><UiOption v-for="step in [3,6,12,24]" :key="step" :label="String(step)" :value="step" /></UiSelect></UiFormItem>
37
+ </UiForm>
38
+ <UiAlert v-if="error" :title="error" type="error" :closable="false" />
39
+ <template #footer><UiSpace><UiButton type="primary" @click="generate">生成模拟结果</UiButton><UiButton @click="reset">重置</UiButton></UiSpace></template>
40
+ </MapWorkspacePanel>
41
+ </template>
42
+ <template #toolbar>
43
+ <MapTools active="" :items="[{key:'generate',label:'生成模拟预报结果',icon:TrendCharts},{key:'reset',label:'重置预报参数和结果',icon:RefreshRight}]" @tool-click="key => key === 'generate' ? generate() : reset()">
44
+ <template #before><AtlasRightToggleButton :open="rightOpen" @toggle="rightOpen = !rightOpen" /></template>
45
+ </MapTools>
46
+ </template>
47
+ <template #right>
48
+ <MapWorkspacePanel title="预报结果" subtitle="展示最近一次生成的结果快照">
49
+ <UiEmpty v-if="!result" description="请在左侧填写参数并生成模拟结果" />
50
+ <template v-else>
51
+ <UiAlert title="以下为固定规则生成的模拟数据,不可用于业务决策" type="warning" :closable="false" />
52
+ <UiAlert v-if="changed" title="左侧参数已修改,请重新生成结果" type="info" :closable="false" />
53
+ <UiDescriptions :column="1" border>
54
+ <UiDescriptionsItem label="方案">{{ result.name }}</UiDescriptionsItem>
55
+ <UiDescriptionsItem label="区域">{{ result.area }}</UiDescriptionsItem>
56
+ <UiDescriptionsItem label="预报时长">{{ result.hours }} 小时</UiDescriptionsItem>
57
+ <UiDescriptionsItem label="累计模拟雨量">{{ total }} mm</UiDescriptionsItem>
58
+ </UiDescriptions>
59
+ <UiTable :data="result.rows" stripe><UiTableColumn prop="period" label="预报时段" min-width="150" /><UiTableColumn prop="rain" label="模拟降水(mm)" min-width="130" /></UiTable>
60
+ </template>
61
+ <template #footer><UiTag type="warning">模拟结果 · 无真实接口</UiTag></template>
62
+ </MapWorkspacePanel>
63
+ </template>
64
+ </MapPageLayout>
65
+ </template>
@@ -0,0 +1,30 @@
1
+ <script setup lang="ts">
2
+ import { computed, ref } from 'vue'
3
+ import { UiAlert, ManagementResourceLayout, ManagementFormDialog, UiButton, UiForm, UiFormItem, UiInput, UiSelect, UiOption, UiTable, UiTableColumn, UiPagination, UiSpace, UiTag } from 'sky-ui-kit'
4
+ type Row={id:number;name:string;status:string}
5
+ const props=defineProps<{page?:string}>()
6
+ const titles:Record<string,string>={users:'用户管理',roles:'角色管理',departments:'组织机构',menus:'菜单目录',logs:'操作日志'}
7
+ const keyword=ref(''),status=ref(''),applied=ref({keyword:'',status:''}),page=ref(1)
8
+ const rows=ref<Row[]>(Array.from({length:16},(_,i)=>({id:i+1,name:'示例用户 '+(i+1),status:i%2?'禁用':'启用'})))
9
+ const filtered=computed(()=>rows.value.filter(row=>row.name.includes(applied.value.keyword)&&(!applied.value.status||row.status===applied.value.status)))
10
+ const visible=computed(()=>filtered.value.slice((page.value-1)*5,page.value*5))
11
+ const open=ref(false),error=ref(''),draft=ref<Row>({id:0,name:'',status:'启用'})
12
+ function query(){applied.value={keyword:keyword.value,status:status.value};page.value=1}
13
+ function edit(row?:Row){draft.value=row?{...row}:{id:0,name:'',status:'启用'};error.value='';open.value=true}
14
+ function save(){if(!draft.value.name.trim()){error.value='请输入用户名称';return}if(draft.value.id){const index=rows.value.findIndex(row=>row.id===draft.value.id);rows.value[index]={...draft.value}}else rows.value.unshift({...draft.value,id:Math.max(0,...rows.value.map(row=>row.id))+1});open.value=false;query()}
15
+ </script>
16
+ <template>
17
+ <section class="starter-management-page">
18
+ <div class="starter-scroll"><UiAlert v-if="props.page && props.page!=='users'" title="该导航展示布局位置;下方仍为用户管理演示,不代表对应模块已实现" :closable="false" />
19
+ <ManagementResourceLayout :title="titles[props.page||'users']" description="系统管理布局 · 当前用户示例支持查询、分页和新增编辑">
20
+ <template #filters><UiForm label-position="top" @submit.prevent="query"><UiFormItem label="名称"><UiInput v-model="keyword" clearable /></UiFormItem><UiFormItem label="状态"><UiSelect v-model="status" clearable><UiOption label="启用" value="启用" /><UiOption label="禁用" value="禁用" /></UiSelect></UiFormItem><UiSpace><UiButton native-type="submit" type="primary">查询</UiButton><UiButton @click="keyword='';status='';query()">重置</UiButton></UiSpace></UiForm></template>
21
+ <template #heading>用户列表</template><template #actions><UiButton type="primary" @click="edit()">新增用户</UiButton></template>
22
+ <UiTable :data="visible" row-key="id"><UiTableColumn prop="id" label="编号" /><UiTableColumn prop="name" label="名称" /><UiTableColumn prop="status" label="状态" /><UiTableColumn label="操作"><template #default="scope"><UiButton text type="primary" @click="edit(rows.find(row=>row.id===scope.row.id))">编辑</UiButton></template></UiTableColumn></UiTable>
23
+ <UiPagination v-model:current-page="page" :page-size="5" :total="filtered.length" layout="total, prev, pager, next" />
24
+ </ManagementResourceLayout></div>
25
+ <ManagementFormDialog v-model="open" :split="false" :title="draft.id?'编辑用户':'新增用户'">
26
+ <UiForm label-position="top" @submit.prevent="save"><UiFormItem label="名称" :error="error"><UiInput v-model="draft.name" /></UiFormItem><UiFormItem label="状态"><UiSelect v-model="draft.status"><UiOption label="启用" value="启用" /><UiOption label="禁用" value="禁用" /></UiSelect></UiFormItem></UiForm>
27
+ <template #footer><UiButton @click="open=false">取消</UiButton><UiButton type="primary" @click="save">保存</UiButton></template>
28
+ </ManagementFormDialog>
29
+ </section>
30
+ </template>
@@ -0,0 +1,34 @@
1
+ <script setup lang="ts">
2
+ import { computed, ref } from 'vue'
3
+ import { Operation, RefreshRight, Aim, Setting, List, TrendCharts } from '@element-plus/icons-vue'
4
+ import { MapPageLayout, MapCategoryMenu, MapTools, AtlasMapTitle, AtlasRightToggleButton, RainfallRankingTable, MapValueLegend, WorkbenchPanel, UiTable, UiTableColumn } from 'sky-ui-kit'
5
+ const props=defineProps<{section?:string;region?:string}>()
6
+ const activeGroup=ref('stations'),topic=ref('rain'),compact=ref(false),rightOpen=ref(true),processOpen=ref(false),selected=ref('')
7
+ const groups=[
8
+ {key:'stations',label:'实况(测站)',items:[{key:'rain',label:'降水'}]},
9
+ {key:'demo-forecast',label:'预报产品(模拟)',items:[{key:'demo-hourly',label:'逐小时预报(模拟)'},{key:'demo-daily',label:'逐日预报(模拟)'}]},
10
+ {key:'demo-satellite',label:'卫星影像(模拟)',items:[]},
11
+ {key:'demo-video',label:'视频监控(模拟)',items:[]}
12
+ ]
13
+ const rows=[{id:1,stationName:'阳江',rainfall:103.8,region:'广东'},{id:2,stationName:'德兴',rainfall:47.5,region:'江西'},{id:3,stationName:'阳春',rainfall:47,region:'广东'},{id:4,stationName:'乐平',rainfall:42.6,region:'江西'},{id:5,stationName:'常山',rainfall:41.4,region:'浙江'},{id:6,stationName:'万年',rainfall:41.1,region:'江西'},{id:7,stationName:'青岛',rainfall:32,region:'山东'},{id:8,stationName:'新兴',rainfall:31.8,region:'广东'},{id:9,stationName:'恭城',rainfall:30.2,region:'广西'},{id:10,stationName:'弋阳',rainfall:29.6,region:'江西'}]
14
+ const legend=ref([{key:'heavy',label:'100~250',color:'#f405ed',count:1,visible:true},{key:'storm',label:'50~100',color:'#0000ff',count:0,visible:true},{key:'moderate',label:'25~50',color:'#61b8ff',count:9,visible:true},{key:'light',label:'10~25',color:'#35b43a',count:0,visible:true},{key:'none',label:'0~10',color:'#ffffff',count:0,visible:true}])
15
+ const layerTitle=computed(()=>{
16
+ const group=groups.find(group=>group.key===activeGroup.value)
17
+ if(group?.key==='stations')return '近24小时降水'
18
+ return (group?.items.find(item=>item.key===topic.value)?.label||group?.label||'模拟菜单')+' · 未接入业务图层'
19
+ })
20
+ function tool(key:string){if(key==='ranking')rightOpen.value=!rightOpen.value;if(key==='process')processOpen.value=!processOpen.value}
21
+ function toggle(key:string,visible:boolean){const item=legend.value.find(item=>item.key===key);if(item)item.visible=visible}
22
+ </script>
23
+ <template>
24
+ <div class="starter-map">
25
+ <AtlasMapTitle :title="groups.find(group=>group.key===activeGroup)?.label||'实况(测站)'" :subtitle="'当前图层:'+layerTitle" :right-open="rightOpen" />
26
+ <MapPageLayout v-model:right-open="rightOpen" :edge="8" :top-offset="4" :bottom-offset="8" :toolbar-top="12" :right-panel-width="328" :left-panel-width="230" :timeline-left="250">
27
+ <template #left><MapCategoryMenu v-model:active-group="activeGroup" v-model:active="topic" v-model:compact="compact" :groups="groups" /></template>
28
+ <template #right><RainfallRankingTable :rows="rows" :data-source-options="[{label:'示例数据(非实况)',value:'demo'}]" :data-source="['demo']" @close="rightOpen=false" @locate="selected=$event.stationName;processOpen=true" /></template>
29
+ <template #toolbar><MapTools :items="[{key:'ranking',label:'降水排行',icon:List},{key:'process',label:'过程区',icon:TrendCharts}]" @tool-click="tool"><template #before><AtlasRightToggleButton :open="rightOpen" @toggle="rightOpen=!rightOpen" /></template></MapTools></template>
30
+ <template #legend><MapValueLegend title="降水" unit="mm" :items="legend" @toggle="toggle" @toggle-all="value=>legend.forEach(item=>item.visible=value)" /></template>
31
+ <template v-if="processOpen" #timeline><WorkbenchPanel :padding="16"><h3>{{ selected||'测站' }} · 过程数据示例</h3><UiTable :data="rows.slice(0,3)"><UiTableColumn prop="stationName" label="测站" /><UiTableColumn prop="rainfall" label="示例雨量(mm)" /></UiTable></WorkbenchPanel></template>
32
+ </MapPageLayout>
33
+ </div>
34
+ </template>
@@ -0,0 +1,52 @@
1
+ <script setup lang="ts">
2
+ import { onMounted, onBeforeUnmount, ref } from 'vue'
3
+ import { Map, AttributionControl, Marker, Popup } from 'maplibre-gl'
4
+ import { UiAlert } from 'sky-ui-kit'
5
+ const host = ref<HTMLElement>()
6
+ const failure = ref('')
7
+ let map: Map | undefined
8
+ let observer: ResizeObserver | undefined
9
+ const markers: Marker[] = []
10
+ // 固定演示坐标与雨量,不代表实时测站观测。
11
+ const stations = [
12
+ { name:'阳江', lng:111.98, lat:21.86, rain:103.8 },
13
+ { name:'德兴', lng:117.58, lat:28.95, rain:47.5 },
14
+ { name:'常山', lng:118.51, lat:28.90, rain:41.4 },
15
+ { name:'青岛', lng:120.38, lat:36.07, rain:32 },
16
+ { name:'成都演示站', lng:104.07, lat:30.67, rain:18.2 },
17
+ { name:'武汉演示站', lng:114.31, lat:30.59, rain:26.8 }
18
+ ]
19
+ onMounted(() => {
20
+ if (!host.value) return
21
+ try {
22
+ map = new Map({
23
+ container: host.value,
24
+ style: import.meta.env.VITE_MAP_STYLE_URL || 'https://demotiles.maplibre.org/style.json',
25
+ center: [104, 34], zoom: 3.5, attributionControl: false
26
+ })
27
+ for (const station of stations) {
28
+ const popup = new Popup({ offset:25 }).setText(`${station.name} · 模拟雨量 ${station.rain} mm(非实况,坐标为演示位置)`)
29
+ markers.push(new Marker({ color: station.rain >= 100 ? '#f405ed' : '#409eff', scale:0.7 })
30
+ .setLngLat([station.lng, station.lat]).setPopup(popup).addTo(map))
31
+ }
32
+ map.addControl(new AttributionControl({ compact: true }), 'bottom-left')
33
+ map.on('error', () => { failure.value = '地图资源加载失败,请检查网络及 VITE_MAP_STYLE_URL 配置。' })
34
+ map.on('idle', () => { if (map?.areTilesLoaded()) failure.value = '' })
35
+ observer = new ResizeObserver(() => map?.resize())
36
+ observer.observe(host.value)
37
+ } catch (error) {
38
+ failure.value = '地图初始化失败:' + (error instanceof Error ? error.message : String(error))
39
+ }
40
+ })
41
+ onBeforeUnmount(() => { observer?.disconnect(); markers.forEach(marker => marker.remove()); map?.remove() })
42
+ </script>
43
+ <template>
44
+ <div class="starter-basemap">
45
+ <div ref="host" class="starter-basemap__canvas" aria-label="可拖动缩放的地理底图" />
46
+ <div v-if="failure" class="starter-basemap__status"><UiAlert :title="failure" type="error" :closable="false" /></div>
47
+ </div>
48
+ </template>
49
+ <style scoped>
50
+ .starter-basemap, .starter-basemap__canvas { position:absolute; inset:0; }
51
+ .starter-basemap__status { position:absolute; bottom:40px; left:60px; right:360px; z-index:1; pointer-events:auto; }
52
+ </style>
@@ -0,0 +1 @@
1
+ /// <reference types="vite/client" />
@@ -0,0 +1,12 @@
1
+ /* 消费端仅布局尺寸;组件皮肤由 sky-ui-kit 提供。 */
2
+ html,body,#app { height:100%; } body { margin:0; overflow:hidden; }
3
+ .starter-map { position:absolute; inset:0; }
4
+ .starter-map-menu { position:absolute; left:16px; top:20px; z-index:8; pointer-events:auto; }
5
+ .starter-scroll { height:100%; overflow:auto; padding:20px; box-sizing:border-box; }
6
+ .starter-header { display:flex; justify-content:space-between; align-items:center; gap:16px; }
7
+
8
+ .starter-management-page { position:relative; height:100%; min-height:0; overflow:hidden; }
9
+
10
+ .starter-table-spacing { margin-top:16px; }
11
+
12
+ .starter-tools { display:flex; flex-direction:column; gap:6px; align-items:center; }
@@ -0,0 +1,8 @@
1
+ import { createApp } from 'vue'
2
+ import 'maplibre-gl/dist/maplibre-gl.css'
3
+ import 'element-plus/dist/index.css'
4
+ import 'sky-ui-kit/style.css'
5
+ import './layout.css'
6
+ import App from './App.vue'
7
+ import { router } from './router'
8
+ createApp(App).use(router).mount('#app')
@@ -0,0 +1,14 @@
1
+ import { createRouter, createWebHashHistory } from 'vue-router'
2
+ import MapPage from './MapPage.vue'
3
+ import ForecastPage from './ForecastPage.vue'
4
+ import ManagementPage from './ManagementPage.vue'
5
+ export const router=createRouter({
6
+ history:createWebHashHistory(),
7
+ routes:[
8
+ {path:'/',redirect:'/map/atlas'},
9
+ {path:'/map/forecast',name:'forecast',component:ForecastPage},
10
+ {path:'/map/:section',name:'map',component:MapPage,props:route=>({section:String(route.params.section)})},
11
+ {path:'/management/:page',name:'management',component:ManagementPage,props:route=>({page:String(route.params.page)})},
12
+ {path:'/:pathMatch(.*)*',redirect:'/map/atlas'}
13
+ ]
14
+ })
@@ -0,0 +1,22 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ESNext",
5
+ "moduleResolution": "Bundler",
6
+ "strict": true,
7
+ "skipLibCheck": true,
8
+ "lib": [
9
+ "ES2022",
10
+ "DOM"
11
+ ],
12
+ "types": [
13
+ "vite/client"
14
+ ],
15
+ "noEmit": true
16
+ },
17
+ "include": [
18
+ "src/**/*.ts",
19
+ "src/**/*.vue",
20
+ "vite.config.ts"
21
+ ]
22
+ }
@@ -0,0 +1,3 @@
1
+ import { defineConfig } from 'vite'
2
+ import vue from '@vitejs/plugin-vue'
3
+ export default defineConfig({plugins:[vue()]})