@epxing/utils 0.0.0-stage → 5.3.0-beta.1

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 (230) hide show
  1. package/API.md +602 -0
  2. package/README.md +22 -2
  3. package/dist/_vendor/.pnpm/jsencrypt@3.5.4/_vendor/jsencrypt/bin/jsencrypt.min.js +1691 -0
  4. package/dist/_virtual/_commonjsHelpers.js +6 -0
  5. package/dist/_virtual/jsencrypt.min.js +31 -0
  6. package/dist/_virtual/jsencrypt.min2.js +4 -0
  7. package/dist/_virtual/preload-helper.js +88 -0
  8. package/dist/assets/ai-BBFn3QRy.png +0 -0
  9. package/dist/assets/ai_import-BxQ7Ohn6.png +0 -0
  10. package/dist/assets/app-DBNIokDP.png +0 -0
  11. package/dist/assets/approval-DGcBv-re.png +0 -0
  12. package/dist/assets/create_market_system-BH__UVbt.png +0 -0
  13. package/dist/assets/create_system-irg--ePh.png +0 -0
  14. package/dist/assets/custom-C-ujlHz1.png +0 -0
  15. package/dist/assets/default-icon-2193FMjv.png +0 -0
  16. package/dist/assets/ex_link-4fYPd9nh.png +0 -0
  17. package/dist/assets/excel-BYqRm2Y8.png +0 -0
  18. package/dist/assets/folder-CFVsNbjj.png +0 -0
  19. package/dist/assets/form-DD9HV-jN.png +0 -0
  20. package/dist/assets/import-tfKWbWVS.png +0 -0
  21. package/dist/assets/meta-Qorpi70B.png +0 -0
  22. package/dist/assets/module-DQrs7VAS.png +0 -0
  23. package/dist/assets/null-B2An29h4.png +0 -0
  24. package/dist/assets/option-CG02tIgh.svg +1 -0
  25. package/dist/assets/photo-uscRzRXF.png +0 -0
  26. package/dist/assets/process-Dcndu9m2.png +0 -0
  27. package/dist/assets/rule-DcYhYse0.png +0 -0
  28. package/dist/assets/service-CLYLpPl3.png +0 -0
  29. package/dist/assets/single_file-B3TJtJli.png +0 -0
  30. package/dist/assets/snapshot-history-BsVY9cVj.png +0 -0
  31. package/dist/assets/snapshot-manual-DBRfFHYI.png +0 -0
  32. package/dist/assets/snapshot-realtime-DjHnJI6C.png +0 -0
  33. package/dist/assets/staff-Bn_-_00l.png +0 -0
  34. package/dist/assets/sync_append-Bm8mtvZs.png +0 -0
  35. package/dist/assets/sync_append_update-DzjxqJ_U.png +0 -0
  36. package/dist/assets/sync_loading-D7LoNPFP.png +0 -0
  37. package/dist/assets/sync_replace-Cbbzz327.png +0 -0
  38. package/dist/assets/sync_update-Birao6Cr.png +0 -0
  39. package/dist/assets/userRole-D1LAdM6N.png +0 -0
  40. package/dist/assets/user_bg-BFHtaZjz.png +0 -0
  41. package/dist/src/Config.js +25 -0
  42. package/dist/src/asset-images.js +125 -0
  43. package/dist/src/assets/images/add.png.js +4 -0
  44. package/dist/src/assets/images/ai-button.png.js +4 -0
  45. package/dist/src/assets/images/ai.png.js +4 -0
  46. package/dist/src/assets/images/ai_import.png.js +4 -0
  47. package/dist/src/assets/images/app.png.js +4 -0
  48. package/dist/src/assets/images/approval.png.js +4 -0
  49. package/dist/src/assets/images/book.png.js +4 -0
  50. package/dist/src/assets/images/canvas-editor/alignment.svg.js +4 -0
  51. package/dist/src/assets/images/canvas-editor/arrow-left.svg.js +4 -0
  52. package/dist/src/assets/images/canvas-editor/arrow-right.svg.js +4 -0
  53. package/dist/src/assets/images/canvas-editor/block.svg.js +4 -0
  54. package/dist/src/assets/images/canvas-editor/bold.svg.js +4 -0
  55. package/dist/src/assets/images/canvas-editor/catalog.svg.js +4 -0
  56. package/dist/src/assets/images/canvas-editor/center.svg.js +4 -0
  57. package/dist/src/assets/images/canvas-editor/checkbox.svg.js +4 -0
  58. package/dist/src/assets/images/canvas-editor/close.svg.js +4 -0
  59. package/dist/src/assets/images/canvas-editor/codeblock.svg.js +4 -0
  60. package/dist/src/assets/images/canvas-editor/color.svg.js +4 -0
  61. package/dist/src/assets/images/canvas-editor/control.svg.js +4 -0
  62. package/dist/src/assets/images/canvas-editor/date.svg.js +4 -0
  63. package/dist/src/assets/images/canvas-editor/exit-fullscreen.svg.js +4 -0
  64. package/dist/src/assets/images/canvas-editor/format.svg.js +4 -0
  65. package/dist/src/assets/images/canvas-editor/highlight.svg.js +4 -0
  66. package/dist/src/assets/images/canvas-editor/hyperlink.svg.js +4 -0
  67. package/dist/src/assets/images/canvas-editor/image.svg.js +4 -0
  68. package/dist/src/assets/images/canvas-editor/italic.svg.js +4 -0
  69. package/dist/src/assets/images/canvas-editor/justify.svg.js +4 -0
  70. package/dist/src/assets/images/canvas-editor/latex.svg.js +4 -0
  71. package/dist/src/assets/images/canvas-editor/left.svg.js +4 -0
  72. package/dist/src/assets/images/canvas-editor/line-dash-dot-dot.svg.js +4 -0
  73. package/dist/src/assets/images/canvas-editor/line-dash-dot.svg.js +4 -0
  74. package/dist/src/assets/images/canvas-editor/line-dash-large-gap.svg.js +4 -0
  75. package/dist/src/assets/images/canvas-editor/line-dash-small-gap.svg.js +4 -0
  76. package/dist/src/assets/images/canvas-editor/line-dot.svg.js +4 -0
  77. package/dist/src/assets/images/canvas-editor/line-double.svg.js +4 -0
  78. package/dist/src/assets/images/canvas-editor/line-single.svg.js +4 -0
  79. package/dist/src/assets/images/canvas-editor/line-wavy.svg.js +4 -0
  80. package/dist/src/assets/images/canvas-editor/list.svg.js +4 -0
  81. package/dist/src/assets/images/canvas-editor/option.svg.js +4 -0
  82. package/dist/src/assets/images/canvas-editor/page-break.svg.js +4 -0
  83. package/dist/src/assets/images/canvas-editor/page-mode.svg.js +4 -0
  84. package/dist/src/assets/images/canvas-editor/page-scale-add.svg.js +4 -0
  85. package/dist/src/assets/images/canvas-editor/page-scale-minus.svg.js +4 -0
  86. package/dist/src/assets/images/canvas-editor/painter.svg.js +4 -0
  87. package/dist/src/assets/images/canvas-editor/paper-direction.svg.js +4 -0
  88. package/dist/src/assets/images/canvas-editor/paper-margin.svg.js +4 -0
  89. package/dist/src/assets/images/canvas-editor/paper-size.svg.js +4 -0
  90. package/dist/src/assets/images/canvas-editor/print.svg.js +4 -0
  91. package/dist/src/assets/images/canvas-editor/radio.svg.js +4 -0
  92. package/dist/src/assets/images/canvas-editor/redo.svg.js +4 -0
  93. package/dist/src/assets/images/canvas-editor/request-fullscreen.svg.js +4 -0
  94. package/dist/src/assets/images/canvas-editor/right.svg.js +4 -0
  95. package/dist/src/assets/images/canvas-editor/row-margin.svg.js +4 -0
  96. package/dist/src/assets/images/canvas-editor/search.svg.js +4 -0
  97. package/dist/src/assets/images/canvas-editor/separator.svg.js +4 -0
  98. package/dist/src/assets/images/canvas-editor/signature-undo.svg.js +4 -0
  99. package/dist/src/assets/images/canvas-editor/signature.svg.js +4 -0
  100. package/dist/src/assets/images/canvas-editor/size-add.svg.js +4 -0
  101. package/dist/src/assets/images/canvas-editor/size-minus.svg.js +4 -0
  102. package/dist/src/assets/images/canvas-editor/strikeout.svg.js +4 -0
  103. package/dist/src/assets/images/canvas-editor/subscript.svg.js +4 -0
  104. package/dist/src/assets/images/canvas-editor/superscript.svg.js +4 -0
  105. package/dist/src/assets/images/canvas-editor/table.svg.js +4 -0
  106. package/dist/src/assets/images/canvas-editor/title.svg.js +4 -0
  107. package/dist/src/assets/images/canvas-editor/trash.svg.js +4 -0
  108. package/dist/src/assets/images/canvas-editor/underline.svg.js +4 -0
  109. package/dist/src/assets/images/canvas-editor/undo.svg.js +4 -0
  110. package/dist/src/assets/images/canvas-editor/watermark.svg.js +4 -0
  111. package/dist/src/assets/images/canvas-editor/word-tool.svg.js +4 -0
  112. package/dist/src/assets/images/clear.png.js +4 -0
  113. package/dist/src/assets/images/create_market_system.png.js +4 -0
  114. package/dist/src/assets/images/create_system.png.js +4 -0
  115. package/dist/src/assets/images/custom.png.js +4 -0
  116. package/dist/src/assets/images/default-icon.png.js +4 -0
  117. package/dist/src/assets/images/ex_link.png.js +4 -0
  118. package/dist/src/assets/images/excel.png.js +4 -0
  119. package/dist/src/assets/images/file.png.js +4 -0
  120. package/dist/src/assets/images/folder.png.js +4 -0
  121. package/dist/src/assets/images/hall/form.png.js +4 -0
  122. package/dist/src/assets/images/hall/meta.png.js +4 -0
  123. package/dist/src/assets/images/hall/process.png.js +4 -0
  124. package/dist/src/assets/images/hall/rule.png.js +4 -0
  125. package/dist/src/assets/images/hall/service.png.js +4 -0
  126. package/dist/src/assets/images/hall/userRole.png.js +4 -0
  127. package/dist/src/assets/images/hr.png.js +4 -0
  128. package/dist/src/assets/images/import.png.js +4 -0
  129. package/dist/src/assets/images/jian.png.js +4 -0
  130. package/dist/src/assets/images/module.png.js +4 -0
  131. package/dist/src/assets/images/null.png.js +4 -0
  132. package/dist/src/assets/images/photo.png.js +4 -0
  133. package/dist/src/assets/images/qq.png.js +4 -0
  134. package/dist/src/assets/images/robot.png.js +4 -0
  135. package/dist/src/assets/images/single_file.png.js +4 -0
  136. package/dist/src/assets/images/snapshot-history.png.js +4 -0
  137. package/dist/src/assets/images/snapshot-manual.png.js +4 -0
  138. package/dist/src/assets/images/snapshot-realtime.png.js +4 -0
  139. package/dist/src/assets/images/staff.png.js +4 -0
  140. package/dist/src/assets/images/switch_app.png.js +4 -0
  141. package/dist/src/assets/images/sync_append.png.js +4 -0
  142. package/dist/src/assets/images/sync_append_update.png.js +4 -0
  143. package/dist/src/assets/images/sync_loading.png.js +4 -0
  144. package/dist/src/assets/images/sync_replace.png.js +4 -0
  145. package/dist/src/assets/images/sync_update.png.js +4 -0
  146. package/dist/src/assets/images/user_bg.png.js +4 -0
  147. package/dist/src/assets/images/weixin.png.js +4 -0
  148. package/dist/src/chart-config-update.js +17 -0
  149. package/dist/src/chart-data-adapters.js +112 -0
  150. package/dist/src/chart-option-override.js +128 -0
  151. package/dist/src/chart-option-paths.js +64 -0
  152. package/dist/src/chart-registry.js +46 -0
  153. package/dist/src/chart-render-host.js +57 -0
  154. package/dist/src/chart-request-broker.js +176 -0
  155. package/dist/src/chartTheme.js +683 -0
  156. package/dist/src/collection.js +25 -0
  157. package/dist/src/date-helper.js +60 -0
  158. package/dist/src/dom-helper.js +22 -0
  159. package/dist/src/eval-with.js +14 -0
  160. package/dist/src/fieldFactory.js +5 -0
  161. package/dist/src/formula.js +365 -0
  162. package/dist/src/hljs-lite.js +29 -0
  163. package/dist/src/index.js +24 -0
  164. package/dist/src/lazy-jsencrypt.js +13 -0
  165. package/dist/src/legacy.js +25 -0
  166. package/dist/src/locale.js +88 -0
  167. package/dist/src/menu-open.js +43 -0
  168. package/dist/src/mermaid.js +79 -0
  169. package/dist/src/meta.js +4 -0
  170. package/dist/src/mobile-chart-appearance.js +148 -0
  171. package/dist/src/moment-compat.js +558 -0
  172. package/dist/src/nav-history.js +184 -0
  173. package/dist/src/page.js +116 -0
  174. package/dist/src/pdf.viewer.js +4 -0
  175. package/dist/src/placeholder.js +64 -0
  176. package/dist/src/public.js +49 -0
  177. package/dist/src/security.js +26 -0
  178. package/dist/src/storage.js +107 -0
  179. package/dist/src/token-storage.js +108 -0
  180. package/dist/src/util.js +4 -0
  181. package/package.json +109 -3
  182. package/src/Config.js +25 -0
  183. package/src/asset-images.js +16 -0
  184. package/src/chart-config-update.js +16 -0
  185. package/src/chart-data-adapters.js +106 -0
  186. package/src/chart-option-override.js +126 -0
  187. package/src/chart-option-paths.js +63 -0
  188. package/src/chart-registry.js +41 -0
  189. package/src/chart-render-host.js +56 -0
  190. package/src/chart-request-broker.js +171 -0
  191. package/src/chartTheme.js +711 -0
  192. package/src/collection.js +28 -0
  193. package/src/date-helper.js +72 -0
  194. package/src/dom-helper.js +26 -0
  195. package/src/eval-with.js +21 -0
  196. package/src/fieldFactory.js +3 -0
  197. package/src/formula.js +362 -0
  198. package/src/hljs-lite.js +41 -0
  199. package/src/index.js +2 -0
  200. package/src/lazy-jsencrypt.js +36 -0
  201. package/src/legacy.js +29 -0
  202. package/src/locale.js +104 -0
  203. package/src/menu-open.js +72 -0
  204. package/src/mermaid.js +115 -0
  205. package/src/meta.js +2 -0
  206. package/src/mobile-chart-appearance.js +147 -0
  207. package/src/moment-compat.d.ts +91 -0
  208. package/src/moment-compat.js +744 -0
  209. package/src/nav-history.js +274 -0
  210. package/src/page.js +129 -0
  211. package/src/pdf.viewer.js +2 -0
  212. package/src/placeholder.js +107 -0
  213. package/src/public.js +11 -0
  214. package/src/security.js +29 -0
  215. package/src/storage.js +135 -0
  216. package/src/token-storage.js +120 -0
  217. package/src/util.js +2 -0
  218. package/types/Config.d.ts +4 -0
  219. package/types/collection.d.ts +40 -0
  220. package/types/date-helper.d.ts +11 -0
  221. package/types/date-time.d.ts +2 -0
  222. package/types/date.d.ts +1 -0
  223. package/types/dom-helper.d.ts +3 -0
  224. package/types/dom.d.ts +1 -0
  225. package/types/index.d.ts +1 -0
  226. package/types/moment-compat.d.ts +91 -0
  227. package/types/page.d.ts +56 -0
  228. package/types/placeholder.d.ts +42 -0
  229. package/types/public.d.ts +10 -0
  230. package/types/storage.d.ts +31 -0
package/package.json CHANGED
@@ -1,6 +1,112 @@
1
1
  {
2
2
  "name": "@epxing/utils",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "5.3.0-beta.1",
4
+ "description": "工具函数(util/fieldFactory/meta/formula/pdf 等;深路径 @epxing/utils/src/* 为公共 API)",
5
+ "type": "module",
6
+ "main": "./dist/src/public.js",
7
+ "module": "./dist/src/public.js",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./types/index.d.ts",
11
+ "import": "./dist/src/public.js",
12
+ "default": "./dist/src/public.js"
13
+ },
14
+ "./src": "./dist/src/legacy.js",
15
+ "./config": {
16
+ "types": "./types/Config.d.ts",
17
+ "import": "./dist/src/Config.js",
18
+ "default": "./dist/src/Config.js"
19
+ },
20
+ "./page": {
21
+ "types": "./types/page.d.ts",
22
+ "import": "./dist/src/page.js",
23
+ "default": "./dist/src/page.js"
24
+ },
25
+ "./placeholder": {
26
+ "types": "./types/placeholder.d.ts",
27
+ "import": "./dist/src/placeholder.js",
28
+ "default": "./dist/src/placeholder.js"
29
+ },
30
+ "./storage": {
31
+ "types": "./types/storage.d.ts",
32
+ "import": "./dist/src/storage.js",
33
+ "default": "./dist/src/storage.js"
34
+ },
35
+ "./date": {
36
+ "types": "./types/date-helper.d.ts",
37
+ "import": "./dist/src/date-helper.js",
38
+ "default": "./dist/src/date-helper.js"
39
+ },
40
+ "./dom": {
41
+ "types": "./types/dom-helper.d.ts",
42
+ "import": "./dist/src/dom-helper.js",
43
+ "default": "./dist/src/dom-helper.js"
44
+ },
45
+ "./date-time": {
46
+ "types": "./types/moment-compat.d.ts",
47
+ "import": "./dist/src/moment-compat.js",
48
+ "default": "./dist/src/moment-compat.js"
49
+ },
50
+ "./collection": {
51
+ "types": "./types/collection.d.ts",
52
+ "import": "./dist/src/collection.js",
53
+ "default": "./dist/src/collection.js"
54
+ },
55
+ "./src/*.css": "./src/*.css",
56
+ "./src/*.scss": "./src/*.scss",
57
+ "./src/*.png": "./src/*.png",
58
+ "./src/*.svg": "./src/*.svg",
59
+ "./src/*.jpg": "./src/*.jpg",
60
+ "./src/*.gif": "./src/*.gif",
61
+ "./src/*.woff": "./src/*.woff",
62
+ "./src/*.woff2": "./src/*.woff2",
63
+ "./src/*.ttf": "./src/*.ttf",
64
+ "./src/*.js": "./dist/src/*.js",
65
+ "./src/*.jsx": "./dist/src/*.js",
66
+ "./src/*.vue": "./dist/src/*.js",
67
+ "./src/*": "./dist/src/*.js",
68
+ "./package.json": "./package.json"
69
+ },
70
+ "files": [
71
+ "dist",
72
+ "src",
73
+ "types",
74
+ "API.md"
75
+ ],
76
+ "dependencies": {
77
+ "dayjs": "^1.11.21",
78
+ "highlight.js": "^11.11.1",
79
+ "lodash-es": "^4.17.23"
80
+ },
81
+ "peerDependencies": {
82
+ "@epxing/core": "^5.3.0-beta",
83
+ "mermaid": "^11.17.2"
84
+ },
85
+ "peerDependenciesMeta": {
86
+ "@epxing/core": {
87
+ "optional": true
88
+ },
89
+ "mermaid": {
90
+ "optional": true
91
+ }
92
+ },
93
+ "types": "./types/index.d.ts",
94
+ "sideEffects": [
95
+ "**/*.css",
96
+ "**/*.scss",
97
+ "**/*.vue",
98
+ "./dist/src/app.js",
99
+ "./dist/src/plugins/**",
100
+ "./dist/src/store/**",
101
+ "./dist/src/mixins/**",
102
+ "./dist/src/runtime-utils/util.js",
103
+ "./dist/src/runtime-services.js"
104
+ ],
105
+ "publishConfig": {
106
+ "access": "public",
107
+ "registry": "https://registry.npmjs.org/"
108
+ },
109
+ "scripts": {
110
+ "build": "cd ../.. && cross-env PKG=utils vite build -c build/vite.pkg.config.mjs"
111
+ }
6
112
  }
package/src/Config.js ADDED
@@ -0,0 +1,25 @@
1
+ import { isObject } from 'lodash-es'
2
+
3
+ class Config {
4
+ // 设置配置
5
+ set (config) {
6
+ for (const key in config) {
7
+ try {
8
+ if (isObject(config[key])) {
9
+ if (Array.isArray(config[key])) {
10
+ this[key] = [...config[key]]
11
+ } else {
12
+ if (!this[key]) this[key] = {}
13
+ this[key] = { ...this[key], ...config[key] }
14
+ }
15
+ } else {
16
+ this[key] = config[key]
17
+ }
18
+ } catch (error) {
19
+ this[key] = config[key]
20
+ }
21
+ }
22
+ }
23
+ }
24
+
25
+ export default Config
@@ -0,0 +1,16 @@
1
+ // 动态路径图片资产表(Vite 版)—— 与 asset-images.js(webpack 版)保持同步!
2
+ // vite.config alias 把 '@/utils/asset-images.js' 切到本文件(webpack 4 不认 import.meta,无法单文件双兼容)。
3
+ const hall = import.meta.glob('@/assets/images/hall/*.png', { eager: true, query: '?url', import: 'default' })
4
+ const root = import.meta.glob('@/assets/images/*.png', { eager: true, query: '?url', import: 'default' })
5
+ const ce = import.meta.glob('@/assets/images/canvas-editor/*.{png,svg}', { eager: true, query: '?url', import: 'default' })
6
+
7
+ const map = {}
8
+ const strip = key => key.split('/assets/images/')[1]
9
+ Object.keys(hall).forEach(key => { map[strip(key)] = hall[key] })
10
+ Object.keys(root).forEach(key => { map[strip(key)] = root[key] })
11
+ Object.keys(ce).forEach(key => { map[strip(key)] = ce[key] })
12
+
13
+ // 按相对路径取图片 URL,如 imageAsset(`hall/${key}.png`)
14
+ export function imageAsset (relPath) {
15
+ return map[relPath]
16
+ }
@@ -0,0 +1,16 @@
1
+ // Unknown outer settings conservatively retain the designer's existing reload behavior.
2
+ const visualKeys = new Set(['chartConfig', 'theme', 'color', 'componentName', 'title', 'title_i18n', 'height', 'minHeight', 'backgroundColor', 'backgroundStyle', 'backgroundImage', 'items', 'showFullscreenBtn'])
3
+ const dataConfig = config => {
4
+ const chart = config.chartConfig || {}
5
+ const map = chart['series-map'] || {}
6
+ return {
7
+ ...Object.fromEntries(Object.entries(config).filter(([key]) => !visualKeys.has(key))),
8
+ refresh: chart.realtimeRefresh,
9
+ mapDimension: map.dimension,
10
+ mapAdcode: map.adcode
11
+ }
12
+ }
13
+
14
+ export function chartConfigRequiresReload (before, after, isEqual) {
15
+ return !isEqual(dataConfig(before || {}), dataConfig(after || {}))
16
+ }
@@ -0,0 +1,106 @@
1
+ // New types reuse verified report protocols. No data snapshots are written to chartConfig.
2
+ export function excludeBoxplotMissingValues (params) {
3
+ const fields = [...new Set((params.chartData?.statisticalList || []).map(item => item.fieldCode).filter(Boolean))]
4
+ if (!fields.length) return
5
+ const scopes = JSON.parse(params.scopeParams || '[]').map(item => ({ ...item }))
6
+ if (scopes.length) {
7
+ scopes[0].lb = '(' + (scopes[0].lb || '')
8
+ const last = scopes[scopes.length - 1]
9
+ last.rb = (last.rb || '') + ')'
10
+ last.link = 'and'
11
+ }
12
+ fields.forEach(field => scopes.push({ field, mode: 'not_null', link: 'and' }))
13
+ scopes[scopes.length - 1].link = ''
14
+ params.scopeParams = JSON.stringify(scopes)
15
+ }
16
+
17
+ const numeric = value => {
18
+ if (value === undefined || value === null || String(value).trim() === '') return null
19
+ const number = typeof value === 'number' ? value : Number(String(value ?? '').replace(/%$/, ''))
20
+ return Number.isFinite(number) && value !== null && value !== '' ? number : null
21
+ }
22
+ const failure = message => ({ hasData: false, message })
23
+ const quantile = (sorted, p) => {
24
+ const index = (sorted.length - 1) * p
25
+ const lower = Math.floor(index)
26
+ return sorted[lower] + (sorted[Math.ceil(index)] - sorted[lower]) * (index - lower)
27
+ }
28
+
29
+ export function summarizeBoxplot (values) {
30
+ const sorted = values.map(numeric).filter(value => value !== null).sort((a, b) => a - b)
31
+ if (!sorted.length) return null
32
+ const q1 = quantile(sorted, 0.25)
33
+ const median = quantile(sorted, 0.5)
34
+ const q3 = quantile(sorted, 0.75)
35
+ const gap = (q3 - q1) * 1.5
36
+ const retained = sorted.filter(value => value >= q1 - gap && value <= q3 + gap)
37
+ return { value: [retained[0], q1, median, q3, retained[retained.length - 1]], outliers: sorted.filter(value => value < q1 - gap || value > q3 + gap), count: sorted.length }
38
+ }
39
+
40
+ export function buildAdvancedChartOption (type, data, config = {}, chartData = {}, theme = '') {
41
+ const option = Object.fromEntries(Object.entries(config).filter(([key]) => key !== 'title' && !key.startsWith('series-') && key !== 'echartsOptionOverride'))
42
+ option.tooltip = { trigger: 'item', confine: true, ...option.tooltip }
43
+ if (option.legend?._orient) {
44
+ const { direction = 'bottom', horizontal = 'center', vertical = 'center' } = option.legend._orient
45
+ const across = direction === 'top' || direction === 'bottom'
46
+ option.legend = { ...option.legend, orient: across ? 'horizontal' : 'vertical', top: direction === 'top' ? '20px' : !across && vertical === 'top' ? '20px' : !across && vertical === 'center' ? 'center' : 'auto', bottom: direction === 'bottom' || (!across && vertical === 'bottom') ? '20px' : 'auto', left: direction === 'left' || (across && horizontal === 'left') ? '20px' : across && horizontal === 'center' ? 'center' : 'auto', right: direction === 'right' || (across && horizontal === 'right') ? '20px' : 'auto' }
47
+ delete option.legend._orient
48
+ }
49
+ if (option.dataZoom?.show) {
50
+ option.dataZoom = [{ type: 'slider', startValue: 0, ...option.dataZoom, endValue: Number(option.dataZoom.endValue || 5) - 1 }]
51
+ } else if (option.dataZoom && !Array.isArray(option.dataZoom)) delete option.dataZoom
52
+ if (type === 'boxplot') {
53
+ if (chartData.categoryList?.length || chartData.statisticalList?.length !== 2) return failure('箱线图需要两个数字统计字段,并且不设置分类维度')
54
+ if (chartData.statisticalList.some(item => item.type !== 'indexField' || !item.fieldCode || item.fieldCode === 'record')) return failure('箱线图请使用数字字段,不支持记录数或自定义统计项')
55
+ const rows = data?.data
56
+ if (!Array.isArray(rows) || !rows.length) return failure()
57
+ if (rows.some(row => !Array.isArray(row) || row.length !== 2)) return failure('箱线图需要原始数字对数据')
58
+ const summaries = [0, 1].map(index => summarizeBoxplot(rows.map(row => row[index])))
59
+ if (summaries.some(item => !item)) return failure()
60
+ const names = chartData.statisticalList.map((item, index) => item.name || `指标 ${index + 1}`)
61
+ option.grid = { containLabel: true, left: 30, right: 20, top: 30, bottom: 30, ...option.grid }
62
+ option.xAxis = { ...option.xAxis, type: 'category', data: names }
63
+ option.yAxis = { type: 'value', ...option.yAxis }
64
+ option.series = [{ type: 'boxplot', name: '箱线', ...config['series-boxplot'], data: summaries.map((item, index) => ({ name: names[index], value: item.value })) }]
65
+ const outliers = summaries.flatMap((item, index) => item.outliers.map(value => [index, value]))
66
+ option.series.push({ type: 'scatter', name: '离群值', data: outliers })
67
+ return { hasData: true, option }
68
+ }
69
+ const series = data?.series
70
+ if (!Array.isArray(series) || !series.length || series.every(item => !item.data?.length)) return failure()
71
+ if (series.some(row => !Array.isArray(row.data) || row.data.some(item => numeric(item.value) === null))) return failure('当前统计结果包含非数值,不能生成此图表')
72
+ if (type === 'gauge') {
73
+ const cells = series.flatMap(row => row.data)
74
+ if (cells.length !== 1) return failure('仪表盘需要单个统计值;请设置一个指标且不拆分分类')
75
+ const cell = cells[0]
76
+ const value = numeric(cell.value)
77
+ option.series = [{ type: 'gauge', min: Math.min(0, value), max: Math.max(100, value), progress: { show: true }, detail: { formatter: '{value}' }, ...config['series-gauge'], data: [{ ...cell, value }] }]
78
+ if (['dark', 'technology'].includes(theme)) {
79
+ const gauge = option.series[0]
80
+ gauge.axisLabel = { color: '#d9e1ec', ...gauge.axisLabel }
81
+ gauge.detail = { color: '#d9e1ec', ...gauge.detail }
82
+ gauge.title = { color: '#d9e1ec', ...gauge.title }
83
+ }
84
+ if (!(Number(option.series[0].max) > Number(option.series[0].min))) return failure('仪表盘最大刻度必须大于最小刻度')
85
+ delete option.xAxis
86
+ delete option.yAxis
87
+ delete option.grid
88
+ } else if (type === 'treemap') {
89
+ if (series.some(row => row.data.some(item => numeric(item.value) < 0))) return failure('矩形树图的统计值不能为负数')
90
+ option.series = [{ type: 'treemap', roam: false, breadcrumb: { show: true }, ...config['series-treemap'], data: series.map((row, index) => ({ name: row.name || `指标 ${index + 1}`, children: row.data.map(cell => ({ ...cell, value: numeric(cell.value) })) })) }]
91
+ delete option.xAxis
92
+ delete option.yAxis
93
+ delete option.grid
94
+ } else if (type === 'heatmap') {
95
+ const categories = series[0].data.map(item => item.name || '')
96
+ if (series.some(row => row.data.length !== categories.length || row.data.some((cell, index) => (cell.name || '') !== categories[index]))) return failure('热力图要求各指标使用同一组分类维度')
97
+ const cells = series.flatMap((row, rowIndex) => row.data.map((cell, colIndex) => ({ ...cell, value: [colIndex, rowIndex, numeric(cell.value)] })))
98
+ const values = cells.map(cell => cell.value[2])
99
+ option.grid = { containLabel: true, left: 30, right: 30, top: 30, bottom: 60, ...option.grid }
100
+ option.xAxis = { type: 'category', splitArea: { show: true }, ...option.xAxis, data: categories }
101
+ option.yAxis = { type: 'category', splitArea: { show: true }, ...option.yAxis, data: series.map((row, index) => row.name || `指标 ${index + 1}`) }
102
+ option.visualMap = { min: values.reduce((min, value) => Math.min(min, value), Infinity), max: values.reduce((max, value) => Math.max(max, value), 1), calculable: true, orient: 'horizontal', left: 'center', bottom: 0, ...option.visualMap }
103
+ option.series = [{ type: 'heatmap', label: { show: false }, ...config['series-heatmap'], data: cells }]
104
+ } else return failure('不支持的图表类型')
105
+ return { hasData: true, option }
106
+ }
@@ -0,0 +1,126 @@
1
+ import { getChartType } from './chart-registry'
2
+
3
+ const own = (value, key) => Object.prototype.hasOwnProperty.call(value, key)
4
+ const object = value => value !== null && typeof value === 'object' && !Array.isArray(value)
5
+ const unsafeKeys = new Set(['__proto__', 'prototype', 'constructor'])
6
+ const MAX_OVERRIDE_BYTES = 64 * 1024
7
+
8
+ // The override is JSON option syntax. Platform statistics still own all data arrays.
9
+ export function validateChartOptionOverride (override, baseOption = {}, chartType = '') {
10
+ const errors = []
11
+ if (!object(override)) return [{ path: '$', message: '覆盖层必须是 JSON 对象' }]
12
+ let encoded
13
+ try { encoded = JSON.stringify(override) } catch { return [{ path: '$', message: '覆盖层必须是可序列化的 JSON 对象' }] }
14
+ const size = new TextEncoder().encode(encoded).length
15
+ if (size > MAX_OVERRIDE_BYTES) errors.push({ path: '$', message: '覆盖层不能超过 64 KB' })
16
+ const visit = (value, path, depth = 0) => {
17
+ if (depth > 64) {
18
+ errors.push({ path, message: '配置层级不能超过 64 层' })
19
+ return
20
+ }
21
+ if (Array.isArray(value)) {
22
+ value.forEach((item, index) => visit(item, `${path}[${index}]`, depth + 1))
23
+ return
24
+ }
25
+ if (!object(value)) return
26
+ for (const [key, child] of Object.entries(value)) {
27
+ const childPath = path === '$' ? key : `${path}.${key}`
28
+ if (unsafeKeys.has(key)) errors.push({ path: childPath, message: '不支持的属性名' })
29
+ if (key === 'source' && (path === 'dataset' || /^dataset\[\d+\]$/.test(path))) errors.push({ path: childPath, message: '数据源由平台维护' })
30
+ if (key === 'data' && /^series\[\d+\]$/.test(path)) errors.push({ path: childPath, message: '系列数据由平台维护' })
31
+ visit(child, childPath, depth + 1)
32
+ }
33
+ }
34
+ visit(override, '$')
35
+
36
+ if (own(override, 'series')) {
37
+ const baseSeries = Array.isArray(baseOption.series) ? baseOption.series : [baseOption.series].filter(Boolean)
38
+ if (!Array.isArray(override.series) || override.series.length !== baseSeries.length || !override.series.every(object)) {
39
+ errors.push({ path: 'series', message: `系列覆盖必须与当前 ${baseSeries.length} 个系列逐项对应` })
40
+ } else {
41
+ override.series.forEach((series, index) => {
42
+ if (own(series, 'type') && series.type !== baseSeries[index]?.type) errors.push({ path: `series[${index}].type`, message: '不能改变平台系列类型' })
43
+ if (own(series, 'coordinateSystem')) {
44
+ const coordinate = baseSeries[index]?.coordinateSystem || getChartType(chartType)?.coordinate
45
+ if (series.coordinateSystem !== coordinate) errors.push({ path: `series[${index}].coordinateSystem`, message: '不能改变平台坐标系' })
46
+ }
47
+ })
48
+ }
49
+ }
50
+ if (Array.isArray(override.dataset)) errors.push({ path: 'dataset', message: '暂不支持替换数据集数组' })
51
+ if (own(override, 'dataset') && (!object(override.dataset) || Array.isArray(baseOption.dataset))) errors.push({ path: 'dataset', message: '不能替换平台数据集结构' })
52
+ for (const axis of ['xAxis', 'yAxis']) {
53
+ if (own(override, axis) && !(object(override[axis]) || (Array.isArray(override[axis]) && override[axis].length && override[axis].every(object)))) {
54
+ errors.push({ path: axis, message: '坐标轴必须是对象或非空对象数组' })
55
+ }
56
+ }
57
+ if (getChartType(chartType) && getChartType(chartType).coordinate !== 'cartesian2d') {
58
+ for (const axis of ['xAxis', 'yAxis', 'grid', 'dataZoom']) {
59
+ if (own(override, axis)) errors.push({ path: axis, message: '当前图表类型不使用直角坐标系' })
60
+ }
61
+ }
62
+ return errors
63
+ }
64
+
65
+ function mergeObject (base, patch) {
66
+ const result = object(base) ? { ...base } : {}
67
+ for (const [key, value] of Object.entries(patch)) {
68
+ if (object(value)) result[key] = mergeObject(result[key], value)
69
+ else if (Array.isArray(value)) result[key] = value.map(item => object(item) ? mergeObject({}, item) : item)
70
+ else result[key] = value
71
+ }
72
+ return result
73
+ }
74
+
75
+ export function applyChartOptionOverride (baseOption, override, chartType = '') {
76
+ // Identity is intentional: charts saved before code mode keep the old option.
77
+ if (override === undefined) return baseOption
78
+ const errors = validateChartOptionOverride(override, baseOption, chartType)
79
+ if (errors.length) return baseOption
80
+ const { series, ...other } = override
81
+ const result = mergeObject(baseOption, other)
82
+ // Native component patches preserve platform axes/data; ordinary arrays still replace.
83
+ for (const key of ['xAxis', 'yAxis', 'grid', 'legend', 'visualMap', 'dataZoom']) {
84
+ if (Array.isArray(other[key])) {
85
+ const base = Array.isArray(baseOption[key]) ? baseOption[key] : [baseOption[key]].filter(Boolean)
86
+ result[key] = Array.from({ length: Math.max(base.length, other[key].length) }, (_, index) => mergeObject(base[index], other[key][index] || {}))
87
+ } else if (object(other[key]) && Array.isArray(baseOption[key])) {
88
+ result[key] = baseOption[key].map((item, index) => index === 0 ? mergeObject(item, other[key]) : item)
89
+ }
90
+ }
91
+ if (series) {
92
+ result.series = baseOption.series.map((item, index) => mergeObject(item, series[index]))
93
+ }
94
+ return result
95
+ }
96
+
97
+ export function setChartOption (host, baseOption, override) {
98
+ host.chartOption = applyChartOptionOverride(baseOption, override, host.chartType)
99
+ if (host.designMode && host.$refs?.container) {
100
+ host.$refs.container.dispatchEvent(new CustomEvent('epx-chart-option-built', {
101
+ bubbles: true,
102
+ detail: { base: baseOption, effective: host.chartOption }
103
+ }))
104
+ }
105
+ }
106
+
107
+ // Render an explicit editor preview in a temporary host; the visible chart stays untouched on failure.
108
+ export function validateChartOptionRendering (option, echarts) {
109
+ if (!echarts) return [{ path: '$', message: '等待图表加载后再预览' }]
110
+ const element = document.createElement('div')
111
+ element.style.cssText = 'position:fixed;left:-10000px;top:0;width:800px;height:400px;visibility:hidden;pointer-events:none'
112
+ document.body.appendChild(element)
113
+ let chart
114
+ try {
115
+ chart = echarts.init(element)
116
+ chart.setOption(option, { notMerge: true })
117
+ const expected = Array.isArray(option.series) ? option.series.length : option.series ? 1 : 0
118
+ if (chart.getOption && (chart.getOption().series?.length || 0) !== expected) throw new Error('当前 ECharts 未能加载全部系列类型')
119
+ return []
120
+ } catch (error) {
121
+ return [{ path: '$', message: `图表预览失败:${error.message}` }]
122
+ } finally {
123
+ if (chart) chart.dispose()
124
+ element.remove()
125
+ }
126
+ }
@@ -0,0 +1,63 @@
1
+ // Maps legacy visual fields to the native option paths they actually control.
2
+ export function chartOverridePaths (key, base = {}, patch = {}) {
3
+ if (key === 'title' || key.startsWith('title.')) return []
4
+ if (key.startsWith('series-')) {
5
+ const [group, ...parts] = key.split('.')
6
+ const type = group.slice(7) === 'wordcloud' ? 'wordCloud' : group.slice(7)
7
+ if (['total', 'totalText'].includes(parts[0])) return []
8
+ const native = parts[0] === 'showArea' ? ['areaStyle'] : parts[0] === 'barType' ? ['stack'] : parts.join('.') === 'label.customFormatter' ? ['label', 'formatter'] : parts
9
+ return (base.series || []).flatMap((series, index) => series.type === type ? [['series', index, ...native]] : [])
10
+ }
11
+ if (/^yAxis\.(leftAxis|rightAxis)\./.test(key)) {
12
+ const [, side, ...parts] = key.split('.')
13
+ return [['yAxis', side === 'leftAxis' ? 0 : 1, ...parts]]
14
+ }
15
+ if (['yAxis.leftName', 'yAxis.rightName'].includes(key)) return [['yAxis', key === 'yAxis.leftName' ? 0 : 1, 'name']]
16
+ if (key.startsWith('legend._orient.')) {
17
+ const fields = key.endsWith('.horizontal') ? ['left', 'right'] : key.endsWith('.vertical') ? ['top', 'bottom'] : ['orient', 'left', 'right', 'top', 'bottom']
18
+ return fields.map(field => ['legend', field])
19
+ }
20
+ const [root, ...parts] = key.split('.')
21
+ if (Array.isArray(patch[root])) return patch[root].map((_, index) => [root, index, ...parts])
22
+ return [[root, ...parts]]
23
+ }
24
+
25
+ const readPath = (value, path) => {
26
+ if (!path.length) return value
27
+ if (Array.isArray(value) && typeof path[0] !== 'number') return value.map(item => readPath(item, path)).find(item => item !== undefined)
28
+ if (!Array.isArray(value) && typeof path[0] === 'number') return path[0] === 0 ? readPath(value, path.slice(1)) : undefined
29
+ return readPath(value?.[path[0]], path.slice(1))
30
+ }
31
+
32
+ export function isChartPathOverridden (patch, key, base) {
33
+ return chartOverridePaths(key, base, patch).some(path => readPath(patch, path) !== undefined)
34
+ }
35
+
36
+ export function clearChartOverridePath (patch, key, base) {
37
+ const remove = (value, path) => {
38
+ if (!value || !path.length) return
39
+ if (Array.isArray(value) && typeof path[0] !== 'number') {
40
+ value.forEach(item => remove(item, path))
41
+ return
42
+ }
43
+ if (!Array.isArray(value) && typeof path[0] === 'number') {
44
+ if (path[0] === 0) remove(value, path.slice(1))
45
+ return
46
+ }
47
+ const [part, ...rest] = path
48
+ if (!rest.length) {
49
+ if (Array.isArray(value)) value[part] = {}
50
+ else delete value[part]
51
+ } else {
52
+ remove(value[part], rest)
53
+ if (value[part] && !Array.isArray(value[part]) && typeof value[part] === 'object' && !Object.keys(value[part]).length && !Array.isArray(value)) delete value[part]
54
+ }
55
+ }
56
+ const paths = chartOverridePaths(key, base, patch)
57
+ for (const path of paths) remove(patch, path)
58
+ for (const root of new Set(paths.map(path => path[0]))) {
59
+ const value = patch[root]
60
+ if (Array.isArray(value) && value.every(item => item && typeof item === 'object' && !Object.keys(item).length)) delete patch[root]
61
+ }
62
+ return patch
63
+ }
@@ -0,0 +1,41 @@
1
+ // Shared metadata; persisted type names and backend request types retain their existing meaning.
2
+ export const chartTypes = [
3
+ { type: 'table', text: '统计表', icon: 'epx-icon-tongjibiao', component: 'chart-table', requestType: 'table', coordinate: 'none' },
4
+ { type: 'pie', text: '饼状图', icon: 'epx-icon-bingzhuangtu', component: 'chart-common', requestType: 'pie', coordinate: 'none' },
5
+ { type: 'circularPie', text: '环形饼图', icon: 'epx-icon-bingzhuangtu', component: 'chart-common', requestType: 'pie', coordinate: 'none' },
6
+ { type: 'line', text: '折线图', icon: 'epx-icon-zhexiantu', component: 'chart-common', requestType: 'line', coordinate: 'cartesian2d' },
7
+ { type: 'column', text: '柱状图', icon: 'epx-icon-zhuzhuangtu', component: 'chart-common', requestType: 'bar', coordinate: 'cartesian2d' },
8
+ { type: 'horiColumn', text: '横向柱图', icon: 'epx-icon-zhuzhuangtu', component: 'chart-common', requestType: 'bar', cellScopeType: 'column', coordinate: 'cartesian2d' },
9
+ { type: 'funnel', text: '漏斗图', icon: 'epx-icon-loudoutu', component: 'chart-common', requestType: 'funnel', coordinate: 'none' },
10
+ { type: 'wordcloud', text: '词云', icon: 'epx-icon-ciyun', component: 'chart-wordcloud', requestType: 'wordCloud', coordinate: 'none' },
11
+ { type: 'radar', text: '雷达图', icon: 'epx-icon-leidatu', component: 'chart-radar', requestType: 'radar', coordinate: 'radar' },
12
+ { type: 'columnAndLine', text: '折柱混合', icon: 'epx-icon-zhezhutu', component: 'chart-common', requestType: 'bar', coordinate: 'cartesian2d' },
13
+ { type: 'scatter', text: '散点图', icon: 'epx-icon-sandiantu', component: 'chart-scatter', requestType: 'scatter', coordinate: 'cartesian2d', binding: 'numericPairs', drill: false },
14
+ { type: 'map', text: '地图', icon: 'epx-icon-ditu', component: 'chart-map', requestType: 'map', coordinate: 'geo' },
15
+ { type: 'gauge', text: '仪表盘', icon: 'epx-icon-leidatu', component: 'chart-advanced', requestType: 'bar', cellScopeType: 'column', coordinate: 'none', dataHint: '单个统计值;设置一个指标且不拆分分类。' },
16
+ { type: 'treemap', text: '矩形树图', icon: 'epx-icon-tongjibiao', component: 'chart-advanced', requestType: 'bar', cellScopeType: 'column', coordinate: 'none', dataHint: '按指标、分类组成两层矩形树,使用非负统计值;叶子支持钻取。' },
17
+ { type: 'heatmap', text: '热力图', icon: 'epx-icon-zhuzhuangtu', component: 'chart-advanced', requestType: 'bar', cellScopeType: 'column', coordinate: 'cartesian2d', dataHint: '横轴为分类,纵轴为指标;各指标使用同一组分类。' },
18
+ { type: 'boxplot', text: '箱线图', selectable: false, icon: 'epx-icon-zhexiantu', component: 'chart-advanced', requestType: 'scatter', coordinate: 'cartesian2d', binding: 'numericPairs', drill: false, dataHint: '选择两个数字统计字段,不设置分类;使用原始值计算四分位数与 1.5 倍四分位距的离群值,不支持钻取。' }
19
+ ]
20
+
21
+ export const selectableChartTypes = chartTypes.filter(item => item.selectable !== false)
22
+
23
+ export function getChartType (type) {
24
+ return chartTypes.find(item => item.type === (type === 'bar' ? 'column' : type))
25
+ }
26
+
27
+ export const chartConfigCategories = [
28
+ { key: 'all', label: '全部' },
29
+ { key: 'appearance', label: '外观' }, { key: 'axis', label: '坐标与刻度' },
30
+ { key: 'shape', label: '图形' }, { key: 'label', label: '标签与提示' },
31
+ { key: 'interaction', label: '交互' }, { key: 'advanced', label: '高级' }
32
+ ]
33
+
34
+ export function getChartConfigCategory (key) {
35
+ if (['title', 'grid', 'color', 'backgroundColor'].includes(key)) return 'appearance'
36
+ if (['xAxis', 'yAxis', 'dualAxes', 'dataZoom'].includes(key)) return 'axis'
37
+ if (key.startsWith('series-') || ['series.map', 'radar', 'scatter', 'table', 'visualMap'].includes(key)) return 'shape'
38
+ if (['legend', 'tooltip'].includes(key)) return 'label'
39
+ if (key === 'toolbox') return 'interaction'
40
+ return 'advanced'
41
+ }
@@ -0,0 +1,56 @@
1
+ // Shared lifecycle for inline charts and saved report charts.
2
+ import { getChartType } from './chart-registry'
3
+
4
+ const registeredThemes = new WeakSet()
5
+ export function registerChartThemes (echarts, themes) {
6
+ if (registeredThemes.has(echarts)) return
7
+ for (const key in themes) echarts.registerTheme(key, themes[key])
8
+ registeredThemes.add(echarts)
9
+ }
10
+ export function disposeChartHost (host) {
11
+ if (host.observedChartDom) {
12
+ host.resizeObserver?.unobserve(host.observedChartDom)
13
+ host.observedChartDom = null
14
+ }
15
+ if (!host.myChart) return
16
+ host.myChart.off('click', host.openPivotPage)
17
+ host.myChart.dispose()
18
+ host.myChart = null
19
+ host.renderedChartTheme = null
20
+ }
21
+
22
+ export function renderChartHost (host, option, settings) {
23
+ const element = host.$refs?.chart
24
+ if (!element || !host.echarts || !option) return false
25
+ const { width, height } = element.getBoundingClientRect()
26
+ if (width <= 0 || height <= 0) return false
27
+
28
+ const staleDom = host.myChart && host.myChart.getDom && host.myChart.getDom() !== element
29
+ const theme = settings && Object.prototype.hasOwnProperty.call(settings, 'theme') ? settings.theme : host.chartTheme
30
+ const changedTheme = host.myChart && host.renderedChartTheme !== theme
31
+ if (staleDom || changedTheme) disposeChartHost(host)
32
+ if (!host.myChart) {
33
+ host.myChart = host.echarts.init(element, theme)
34
+ host.renderedChartTheme = theme
35
+ if (host.openClickEvent) host.myChart.on('click', host.openPivotPage)
36
+ }
37
+ // Header/search controls can change the plot height while the outer card stays fixed.
38
+ if (host.resizeObserver && host.observedChartDom !== element) {
39
+ if (host.observedChartDom) host.resizeObserver.unobserve(host.observedChartDom)
40
+ host.resizeObserver.observe(element)
41
+ host.observedChartDom = element
42
+ }
43
+ // Old code disposed on every update, so notMerge preserves its full-replacement semantics.
44
+ try {
45
+ host.myChart.setOption(option, { notMerge: true })
46
+ const expected = Array.isArray(option.series) ? option.series.length : option.series ? 1 : 0
47
+ const enforceSeries = getChartType(host.chartType)?.component === 'chart-advanced' || host.chartConfigs?.chartConfig?.echartsOptionOverride !== undefined
48
+ if (enforceSeries && host.myChart.getOption && (host.myChart.getOption().series?.length || 0) !== expected) throw new Error('未能加载全部系列类型')
49
+ } catch (error) {
50
+ disposeChartHost(host)
51
+ host.error = true
52
+ host.chartErrorText = `图表配置无法渲染:${error.message}`
53
+ return false
54
+ }
55
+ return true
56
+ }