Flex加载多个Module时出现“TypeError: Error #1034: 强制转换类型失败”错误

本文探讨了Flex中使用ModuleLoader加载多个模块时遇到的TypeError#1034错误,并详细解释了此错误产生的原因及如何通过修改ModuleLoader设置来解决这一问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Flex加载多个Module时出现“TypeError: Error #1034: 强制转换类型失败”错误
2010年06月13日
  使用Accodian加载不同Module时,使用combobox时切换选项时,出现错误TypeError: Error #1034: 强制转换类型失败
  出现这种错误的原因在于 ModuleLoader shared code problem 当Module中使用managers时(如PopUpManager,DragManager, HistoryManager等)则可能出现这个问题,
  当application里在loader之前没有引入这些manager的引用时,manager的方法是静态方法,整个应用程序中创建了一个该manager接口的singleton实例, 但module仅在自己的 Application domain中使用该单例, 当多个module使用同一个单例manager且main application没有使用时,就会出现这个空对象引用问题。第一个引入某manager的module不能将该manager接口的 singleton跟其他module共享,其他module调用该Manager的方法时,应用程序不会再创建该manager接口的实例,这个 module就无法引用到该manager接口的实例,就出现了空对象引用问题.
  解决方法:在ModuleLoader 的creationComplete方法中加入如下代码: moduleLoader.applicationDomain = ApplicationDomain.currentDomain; 就可以在Application里切换多个module而不需要在Application里明文引用单例manager声明。
  另外,推荐在moduleloader做切换的时候,加上:moduleLoader.unloadModule再做moduleLoader.loadModule()。
runtime-core.esm-bundler.js:268 Uncaught TypeError: Cesium.UrlTemplateTerrainProvider is not a constructor at loadMapService (CesiumViewer.vue:163:30) at switchMapType (CesiumViewer.vue:135:5) at callWithErrorHandling (runtime-core.esm-bundler.js:199:19) at callWithAsyncErrorHandling (runtime-core.esm-bundler.js:206:17) at HTMLSelectElement.invoker (runtime-dom.esm-bundler.js:729:5) 如上所示报错,还是没有加载三维地形图,修改下面代码:<template> <div class="monitor-container"> <div class="top-bar"> <div class="left-section"> <div class="weather-module"> <i class="fas fa-sun weather-icon"></i> <span>晴 31℃/西北风</span> </div> </div> <div class="center-section"> <div class="platform-title"> <div class="trapezoid-bg"> <h1>株洲市"天空地水"动态监测平台</h1> </div> </div> </div> <div class="right-section"> <div class="time-module"> {{ currentTime }} {{ currentWeek }} </div> <div class="controls-group"> <div class="projection-switcher"> <select v-model="projectionType" @change="switchProjectionType"> <option value="c">经纬度投影</option> <option value="w">球面墨卡托投影</option> </select> </div> <div class="map-switcher"> <select v-model="selectedMapType" @change="switchMapType"> <option v-for="option in mapOptions" :value="option.value" :key="option.value"> {{ option.label }} </option> </select> </div> <div class="network-switcher"> <select v-model="networkType" @change="switchNetworkType"> <option value="external">外网地图</option> <option value="internal">内网地图</option> </select> </div> </div> </div> </div> <div id="cesiumContainer"></div> <div v-if="terrainEnabled" class="terrain-badge"> <i class="fas fa-mountain"></i> <span>三维地形模式</span> </div> </div> </template> <script setup> import { ref, onMounted, onBeforeUnmount } from &#39;vue&#39;; // 天地图服务密钥 const TIANDITU_TOKEN = &#39;72d487f15710ca558987b9baaba13736&#39;; // 当前间和星期 const currentTime = ref(""); const currentWeek = ref(""); const weekMap = [&#39;星期日&#39;, &#39;星期一&#39;, &#39;星期二&#39;, &#39;星期三&#39;, &#39;星期四&#39;, &#39;星期五&#39;, &#39;星期六&#39;]; const updateTime = () => { const now = new Date(); currentTime.value = now.toLocaleString(&#39;zh-CN&#39;, { year: &#39;numeric&#39;, month: &#39;2-digit&#39;, day: &#39;2-digit&#39;, hour: &#39;2-digit&#39;, minute: &#39;2-digit&#39;, second: &#39;2-digit&#39;, hour12: false }).replace(/\//g, &#39;-&#39;).replace(/(\d{4})-(\d{2})-(\d{2})/, &#39;$ 1年$ 2月$ 3日&#39;); currentWeek.value = weekMap[now.getDay()]; }; // 网络类型选择 const networkType = ref(&#39;external&#39;); // 地图类型选择 const selectedMapType = ref(&#39;img_c&#39;); // 投影类型选择 const projectionType = ref(&#39;c&#39;); // 三维地形启用状态 const terrainEnabled = ref(false); // 地图选项配置 const mapOptions = ref([ { value: &#39;img_c&#39;, label: &#39;影像+注记&#39; }, { value: &#39;vec_c&#39;, label: &#39;矢量+注记&#39; }, { value: &#39;terrain&#39;, label: &#39;三维地形图&#39; } ]); // Cesium相关变量 let viewer = null; let tiandituLayers = { baseLayer: null, annotationLayer: null }; let timeInterval = null; // 石河子范围矩形 const shiheziRectangle = ref(null); // 网络类型切换 const switchNetworkType = () => { if (networkType.value === &#39;internal&#39;) { mapOptions.value = [ { value: &#39;img&#39;, label: &#39;内网影像地图&#39; }, { value: &#39;vec&#39;, label: &#39;内网矢量地图&#39; } ]; selectedMapType.value = &#39;img&#39;; terrainEnabled.value = false; } else { mapOptions.value = [ { value: &#39;img_c&#39;, label: &#39;影像+注记&#39; }, { value: &#39;vec_c&#39;, label: &#39;矢量+注记&#39; }, { value: &#39;terrain&#39;, label: &#39;三维地形图&#39; } ]; selectedMapType.value = &#39;img_c&#39;; } loadMapService(selectedMapType.value); loadShiheziVector(); setTimeout(flyToShihezi, 500); }; // 地图类型切换 const switchMapType = () => { if (viewer) { terrainEnabled.value = selectedMapType.value === &#39;terrain&#39;; loadMapService(selectedMapType.value); } }; // 投影类型切换 const switchProjectionType = () => { if (viewer) { loadMapService(selectedMapType.value); setTimeout(flyToShihezi, 500); } }; // 加载地图服务 const loadMapService = (type) => { if (!viewer || !viewer.imageryLayers) return; // 清除现有图层 if (tiandituLayers.baseLayer) viewer.imageryLayers.remove(tiandituLayers.baseLayer); if (tiandituLayers.annotationLayer) viewer.imageryLayers.remove(tiandituLayers.annotationLayer); // 处理三维地形模式 if (type === &#39;terrain&#39;) { // 设置三维地形 if (!(viewer.scene.terrainProvider instanceof Cesium.CesiumTerrainProvider)) { viewer.terrainProvider = new Cesium.EllipsoidTerrainProvider(); } // 修复:使用正确的 UrlTemplateTerrainProvider viewer.terrainProvider = new Cesium.UrlTemplateTerrainProvider({ url: `https://t{s}.tianditu.gov.cn/mapservice/swdx?tk=${TIANDITU_TOKEN}`, subdomains: [&#39;0&#39;,&#39;1&#39;,&#39;2&#39;,&#39;3&#39;,&#39;4&#39;,&#39;5&#39;,&#39;6&#39;,&#39;7&#39;] }); // 添加地形注记层 const annoUrl = projectionType.value === &#39;c&#39; ? `https://t{s}.tianditu.gov.cn/cta/wmts?tk=${TIANDITU_TOKEN}` : `https://t{s}.tianditu.gov.cn/cta_w/wmts?tk=${TIANDITU_TOKEN}`; tiandituLayers.baseLayer = viewer.imageryLayers.addImageryProvider( new Cesium.WebMapTileServiceImageryProvider({ url: annoUrl, layer: projectionType.value === &#39;c&#39; ? &#39;cta&#39; : &#39;cta_w&#39;, style: "default", tileMatrixSetID: projectionType.value, format: "tiles", subdomains: [&#39;0&#39;,&#39;1&#39;,&#39;2&#39;,&#39;3&#39;,&#39;4&#39;,&#39;5&#39;,&#39;6&#39;,&#39;7&#39;], maximumLevel: 18, credit: new Cesium.Credit("天地图地形注记") }) ); } else { // 确保不使用三维地形 viewer.terrainProvider = new Cesium.EllipsoidTerrainProvider(); if (networkType.value === &#39;internal&#39;) { // 内网地图服务 const baseUrl = "http://59.255.48.160:81"; const projectionSuffix = projectionType.value === &#39;c&#39; ? &#39;_c&#39; : &#39;_w&#39;; if (type === &#39;img&#39;) { // 内网影像地图 tiandituLayers.baseLayer = viewer.imageryLayers.addImageryProvider( new Cesium.WebMapTileServiceImageryProvider({ url: `${baseUrl}/img${projectionSuffix}/wmts`, layer: "img", style: "default", tileMatrixSetID: projectionType.value, format: "tiles", credit: new Cesium.Credit("内网影像地图"), maximumLevel: 18 }) ); } else if (type === &#39;vec&#39;) { // 内网矢量地图 tiandituLayers.baseLayer = viewer.imageryLayers.addImageryProvider( new Cesium.WebMapTileServiceImageryProvider({ url: `${baseUrl}/vec${projectionSuffix}/wmts`, layer: "vec", style: "default", tileMatrixSetID: projectionType.value, format: "tiles", credit: new Cesium.Credit("内网矢量地图"), maximumLevel: 18 }) ); } } else { // 外网天地图服务 const baseUrlTemplate = `https://t{s}.tianditu.gov.cn/{layer}_${projectionType.value}/wmts?tk=${TIANDITU_TOKEN}`; const layerConfigs = { img_c: { baseLayer: { layer: "img", credit: "天地图影像" }, annotationLayer: { layer: projectionType.value === &#39;c&#39; ? "cia" : "cia_w", credit: "天地图注记" } }, vec_c: { baseLayer: { layer: "vec", credit: "天地图矢量" }, annotationLayer: { layer: projectionType.value === &#39;c&#39; ? "cva" : "cva_w", credit: "天地图注记" } } }; const config = layerConfigs[type]; if (!config) return; // 添加基础图层 tiandituLayers.baseLayer = viewer.imageryLayers.addImageryProvider( new Cesium.WebMapTileServiceImageryProvider({ url: baseUrlTemplate.replace(&#39;{layer}&#39;, config.baseLayer.layer), layer: config.baseLayer.layer, style: "default", tileMatrixSetID: projectionType.value, format: "tiles", credit: new Cesium.Credit(config.baseLayer.credit), subdomains: [&#39;0&#39;,&#39;1&#39;,&#39;2&#39;,&#39;3&#39;,&#39;4&#39;,&#39;5&#39;,&#39;6&#39;,&#39;7&#39;], maximumLevel: 18 }) ); // 添加注记图层 tiandituLayers.annotationLayer = viewer.imageryLayers.addImageryProvider( new Cesium.WebMapTileServiceImageryProvider({ url: baseUrlTemplate.replace(&#39;{layer}&#39;, config.annotationLayer.layer), layer: config.annotationLayer.layer, style: "default", tileMatrixSetID: projectionType.value, format: "tiles", credit: new Cesium.Credit(config.annotationLayer.credit), subdomains: [&#39;0&#39;,&#39;1&#39;,&#39;2&#39;,&#39;3&#39;,&#39;4&#39;,&#39;5&#39;,&#39;6&#39;,&#39;7&#39;], maximumLevel: 18 }) ); } } }; // 加载石河子矢量数据 const loadShiheziVector = async () => { if (!viewer) return; try { // 清除现有石河子数据 const dataSources = viewer.dataSources; for (let i = dataSources.length - 1; i >= 0; i--) { if (dataSources.get(i).name === &#39;shihezi&#39;) { dataSources.remove(dataSources.get(i)); } } // 加载石河子市整体边界 const cityDataSource = await Cesium.GeoJsonDataSource.load( "https://geo.datav.aliyun.com/areas_v3/bound/geojson?code=659001", { clampToGround: true, stroke: Cesium.Color.GREEN.withAlpha(0.8), strokeWidth: 2, fill: Cesium.Color.WHITE.withAlpha(0.3), name: &#39;shihezi&#39; } ); viewer.dataSources.add(cityDataSource); // 添加石河子市标签 cityDataSource.entities.values.forEach(entity => { if (entity.polygon && entity.properties && entity.properties.name) { const hierarchy = entity.polygon.hierarchy.getValue(Cesium.JulianDate.now()); const positions = hierarchy.positions; const center = Cesium.BoundingSphere.fromPoints(positions).center; entity.label = { text: "石河子市", font: &#39;18px 黑体&#39;, fillColor: Cesium.Color.WHITE, outlineColor: Cesium.Color.BLACK, outlineWidth: 3, style: Cesium.LabelStyle.FILL_AND_OUTLINE, verticalOrigin: Cesium.VerticalOrigin.CENTER, horizontalOrigin: Cesium.HorizontalOrigin.CENTER, pixelOffset: new Cesium.Cartesian2(0, 0), disableDepthTestDistance: Number.POSITIVE_INFINITY, scaleByDistance: new Cesium.NearFarScalar(1e3, 1.0, 1e6, 0.5) }; entity.position = center; } }); // 保存石河子范围 const positions = cityDataSource.entities.values[0].polygon.hierarchy.getValue().positions; const cartographicPositions = positions.map(pos => Cesium.Cartographic.fromCartesian(pos)); const minLon = Math.min(...cartographicPositions.map(p => p.longitude)); const maxLon = Math.max(...cartographicPositions.map(p => p.longitude)); const minLat = Math.min(...cartographicPositions.map(p => p.latitude)); const maxLat = Math.max(...cartographicPositions.map(p => p.latitude)); shiheziRectangle.value = new Cesium.Rectangle(minLon, minLat, maxLon, maxLat); } catch (error) { console.error("加载石河子数据失败:", error); } }; // 定位到石河子 const flyToShihezi = () => { if (viewer && shiheziRectangle.value) { // 根据当前投影类型转换坐标 let destination; if (projectionType.value === &#39;c&#39;) { destination = shiheziRectangle.value; } else { const sw = Cesium.WebMercatorProjection.geodeticToWebMercator( new Cesium.Cartographic(shiheziRectangle.value.west, shiheziRectangle.value.south) ); const ne = Cesium.WebMercatorProjection.geodeticToWebMercator( new Cesium.Cartographic(shiheziRectangle.value.east, shiheziRectangle.value.north) ); destination = new Cesium.Rectangle(sw.x, sw.y, ne.x, ne.y); } viewer.camera.flyTo({ destination: destination, orientation: { heading: Cesium.Math.toRadians(0), pitch: Cesium.Math.toRadians(-70), roll: 0 }, duration: 1 }); } }; onMounted(async () => { try { // 设置Cesium Ion访问令牌 Cesium.Ion.defaultAccessToken = &#39;eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI0ZTdiZDBhZS0xNzBhLTRjZGUtOTY4NC1kYzA5ZDEyNGEyNjUiLCJpZCI6MzE2OTI5LCJpYXQiOjE3NTEzMzg2Mzh9.9QHGIwaWkUOX0NOSre5369rrf1k6bGhZu7xUQia4JmE&#39;; // 初始化间更新 timeInterval = setInterval(updateTime, 1000); updateTime(); // 初始化Cesium Viewer viewer = new Cesium.Viewer(&#39;cesiumContainer&#39;, { baseLayerPicker: false, timeline: false, animation: false, geocoder: false, sceneModePicker: false, navigationHelpButton: false, homeButton: false, selectionIndicator: false, infoBox: false, navigationInstructionsInitiallyVisible: false, scene3DOnly: true, terrainProvider: new Cesium.EllipsoidTerrainProvider(), imageryProvider: new Cesium.WebMapTileServiceImageryProvider({ url: `https://t{s}.tianditu.gov.cn/img_${projectionType.value}/wmts?tk=${TIANDITU_TOKEN}`, layer: "img", style: "default", tileMatrixSetID: projectionType.value, subdomains: [&#39;0&#39;,&#39;1&#39;,&#39;2&#39;,&#39;3&#39;,&#39;4&#39;,&#39;5&#39;,&#39;6&#39;,&#39;7&#39;], maximumLevel: 18 }) }); // 强制设置中国视角 viewer.camera.setView({ destination: Cesium.Rectangle.fromDegrees(73.0, 3.0, 136.0, 59.0) }); // 加载石河子数据 await loadShiheziVector(); flyToShihezi(); // 加载地图服务 loadMapService(selectedMapType.value); } catch (error) { console.error("初始化失败:", error); } }); onBeforeUnmount(() => { // 清除定器 if (timeInterval) clearInterval(timeInterval); // 销毁Cesium Viewer if (viewer) { viewer.destroy(); viewer = null; } }); </script> <style scoped> * { margin: 0; padding: 0; box-sizing: border-box; font-family: "Microsoft YaHei", sans-serif; } .monitor-container { width: 100vw; height: 100vh; background: radial-gradient(circle at center, #0c2a50 0%, #0a1a35 100%); overflow: hidden; position: fixed; top: 0; left: 0; display: flex; flex-direction: column; } .top-bar { height: 70px; background: linear-gradient(90deg, #0a1a35 0%, #1a3a6e 50%, #0a1a35 100%); display: flex; align-items: center; justify-content: space-between; padding: 0 15px; position: relative; z-index: 100; border-bottom: 1px solid rgba(0, 150, 255, 0.3); } .left-section { display: flex; align-items: center; flex: 0 0 auto; } .center-section { flex: 1 1 auto; display: flex; justify-content: center; } .right-section { display: flex; align-items: center; flex: 0 0 auto; gap: 10px; } .controls-group { display: flex; gap: 10px; } .weather-module { display: flex; align-items: center; color: white; font-size: 14px; min-width: 120px; justify-content: center; } .weather-icon { color: #ffcc00; margin-right: 8px; font-size: 16px; } .platform-title { position: relative; z-index: 10; max-width: 500px; } .trapezoid-bg { position: relative; padding: 0 40px; height: 70px; display: flex; align-items: center; } .trapezoid-bg::before { content: &#39;&#39;; position: absolute; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0, 60, 113, 0.6); clip-path: polygon(0% 0%, 100% 0%, 90% 100%, 10% 100%); z-index: -1; } .platform-title h1 { font-size: 24px; font-weight: bold; background: linear-gradient(to bottom, #a2e7eb, #00f2fe); -webkit-background-clip: text; background-clip: text; color: transparent; text-shadow: 0 0 8px rgba(0, 242, 254, 0.3); white-space: nowrap; text-overflow: ellipsis; overflow: hidden; max-width: 100%; } .time-module { color: white; font-size: 14px; min-width: 220px; text-align: center; white-space: nowrap; } .network-switcher select, .map-switcher select, .projection-switcher select { background: rgba(10, 26, 53, 0.8); color: #66ffff; border: 1px solid rgba(0, 150, 255, 0.5); border-radius: 4px; padding: 6px 12px; font-size: 14px; outline: none; cursor: pointer; box-shadow: 0 0 8px rgba(0, 150, 255, 0.3); transition: all 0.3s ease; min-width: 120px; } .network-switcher select:hover, .map-switcher select:hover, .projection-switcher select:hover { background: rgba(26, 58, 110, 0.8); border-color: #00f2fe; box-shadow: 0 0 12px rgba(0, 242, 254, 0.5); } .network-switcher select:focus, .map-switcher select:focus, .projection-switcher select:focus { border-color: #00f2fe; } #cesiumContainer { width: 100%; height: calc(100vh - 70px); background-color: #000; position: relative; } .terrain-badge { position: absolute; top: 80px; right: 20px; padding: 6px 12px; background: rgba(0, 150, 255, 0.3); color: #66ffff; border-radius: 4px; font-size: 12px; display: flex; align-items: center; gap: 6px; z-index: 100; backdrop-filter: blur(5px); } @media (max-width: 1600px) { .platform-title h1 { font-size: 20px; } .weather-module, .time-module { font-size: 13px; } .network-switcher select, .map-switcher select, .projection-switcher select { padding: 5px 10px; font-size: 13px; min-width: 110px; } } @media (max-width: 1400px) { .platform-title h1 { font-size: 18px; } .trapezoid-bg { padding: 0 30px; } .time-module { min-width: 180px; } .controls-group { gap: 8px; } } @media (max-width: 1200px) { .platform-title h1 { font-size: 16px; } .trapezoid-bg { padding: 0 20px; } .time-module { min-width: 160px; font-size: 12px; } .network-switcher select, .map-switcher select, .projection-switcher select { padding: 4px 8px; font-size: 12px; min-width: 100px; } } @media (max-width: 992px) { .time-module { display: none; } .platform-title h1 { font-size: 14px; } .trapezoid-bg { padding: 0 15px; } .weather-module { min-width: auto; } .network-switcher select, .map-switcher select, .projection-switcher select { padding: 4px 6px; font-size: 11px; min-width: 90px; } } @media (max-width: 768px) { .weather-module { display: none; } .platform-title h1 { font-size: 12px; } .trapezoid-bg { padding: 0 10px; } .controls-group { gap: 5px; } .network-switcher select, .map-switcher select, .projection-switcher select { min-width: 80px; } } </style>
07-11
------------------------ index.html ------------------------ <!doctype html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"> <title>峤丞板材库存管理</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" type="text/css" href="fonts/font-awesome-4.7.0/css/font-awesome.min.css"> <link rel="stylesheet" type="text/css" href="main/bootstrap-3.3.7-dist/css/bootstrap.css.map"> <link rel="stylesheet" type="text/css" href="css/util.css"> <link rel="stylesheet" type="text/css" href="css/main.css"> <link rel="stylesheet" type="text/css" href="css/index2.css"> <script type="text/javascript" src="js/jquery-3.2.1.min.js"></script> <script type="text/javascript" src="js/jsyilai.js"></script> <script type="module"> // 共享的DataManager类 import { DataManager } from &#39;./data/DataManager.js&#39;; document.addEventListener(&#39;DOMContentLoaded&#39;, async () => { try { // 创建实例并挂载到window window.dataManager = new DataManager(&#39;http://127.0.0.1:8080/KuCun2&#39;); // 初始化数据 await window.dataManager.fetchAll(); console.log(&#39;Data Manager initialized successfully&#39;); // 设置iframe通信 const iframe = document.getElementById(&#39;iframeid&#39;); iframe.onload = () => { // 通知iframe数据已准备好 iframe.contentWindow.postMessage(&#39;DataManagerReady&#39;, &#39;*&#39;); }; // 如果iframe已经加载,立即发送消息 if (iframe.contentDocument.readyState === &#39;complete&#39;) { iframe.contentWindow.postMessage(&#39;DataManagerReady&#39;, &#39;*&#39;); } } catch (error) { console.error(&#39;Failed to initialize DataManager:&#39;, error); } }); </script> <style type="text/css"> *{ margin:0; padding:0; } .frame-header { height: 60px; background-color: #23262E; justify-content: space-between; } .frame-header-li{ font-family: Arial, Helvetica, sans-serif; font-size:40px; } .frame-ul{ } .frame-ul li{ border-style: solid; border-width:1px 0px 1px 0px; margin-top: 1px; height: 35px; text-align: center } #username{ position: absolute; right: 0; /* 靠右 */ } .frame-body { position: fixed; top: 60px; right: 0; bottom: 0; left: 0; display: flex; flex-direction: row; } .frame-side { scrollbar-width: none; /* firefox隐藏滚动条 */ -ms-overflow-style: none; /* IE 10+隐藏滚动条 */ overflow-x: hidden; overflow-y: auto; width: 200px; background-color:#9e5; } .frame-side::-webkit-scrollbar { display: none; /* Chrome Safari 隐藏滚动条*/ } .frame-main { flex-grow: 1; background-color:#fff; } .jiaoluo{ margin: auto; margin-right: 0px; } .menu { display: none; position: absolute; background-color: #f9f9f9; border: 1px solid #ccc; padding: 10px; list-style-type: none; margin: 0; z-index: 10; } </style> </head> <body> <div class="frame-header"> <a class=&#39;frame-header-li&#39; style="color:#fff">峤丞木材仓库管理</a> <a id="username" class=&#39;frame-header-li&#39; style="color:#520">峤丞木材仓库管理</a> <!-- 菜单 --> <ul class="menu"> <li>选项 1</li> <li>选项 2</li> <li>选项 3</li> </ul> </div> <div class="frame-body"> <div class="frame-side"> <ul id="main_u" class=&#39;frame-ul&#39; style="text-align:center;"> <li ><a href="main/test.html" target="main">首页</a></li> <li><a href="main/dingdan.html" target="main">订单查询</a></li> <li><a href="main/bancai.html" target="main">板材查询</a></li> <li><a href="main/Guanli.html" target="main">人员管理</a></li> <li><a href="main/test.html" target="main">test</a></li> <li><a href="main/tianjia.html" target="main">test</a></li> </ul> </div> <div class="frame-main"> <!-- 内容主体区域 --> <iframe id="iframeid" name="main" src="main/bancai.html" width="100%" height="100%" frameborder="0"> </iframe> </div> </div> </body> </html> ------------------------ bancai.js ------------------------ // bancai.js $(document).ready(function () { const modal = new bootstrap.Modal(&#39;#bancaiModal&#39;); let currentMode = &#39;view&#39;; let caizhiList = []; let mupiList = []; let currentSearchText = &#39;&#39;; // 从父窗口获取 DataManager let dataManager = null; // 等待父窗口的 DataManager 准备就绪 async function waitForDataManager() { return new Promise((resolve, reject) => { if (window.parent && window.parent.dataManager) { resolve(window.parent.dataManager); } else { reject(new Error(&#39;无法从父窗口获取 DataManager&#39;)); } }); } // 初始化函数 async function initialize() { try { dataManager = await waitForDataManager(); if (!dataManager || typeof dataManager.fetchAll !== &#39;function&#39;) { throw new Error(&#39;无效的 DataManager 实例&#39;); } // 解构需要的方法和属性 const { addEntity, updateEntity, deleteEntity, fetchAll } = dataManager; // 确保数据已加载 await fetchAll(); // 更新材质和木皮选项 updateOptions(); // 渲染板材表格 refreshTable(); } catch (error) { console.error(&#39;初始化失败:&#39;, error); alert(&#39;系统初始化失败,请刷新页面或联系管理员&#39;); } } // 更新材质和木皮选项 function updateOptions() { caizhiList = dataManager.data.caizhis; updateSelectOptions(&#39;#caizhiSelect&#39;, caizhiList); mupiList = dataManager.data.mupis.map(m => ({ ...m, name: m.you ? `${m.name}(油漆)` : m.name, })); updateSelectOptions(&#39;#mupi1Select&#39;, mupiList); updateSelectOptions(&#39;#mupi2Select&#39;, mupiList); } // 更新下拉框选项 function updateSelectOptions(selector, data) { $(selector).empty(); data.forEach(item => { $(selector).append(`<option value="${item.id}">${item.name}</option>`); }); } // 刷新表格 function refreshTable() { const filteredData = filterBancais(currentSearchText); renderBancaiTable(filteredData); } // 搜索过滤 function filterBancais(searchText) { if (!searchText) return dataManager.data.bancais; return dataManager.data.bancais.filter(bancai => { const caizhiName = bancai.caizhi?.name || &#39;&#39;; const mupi1Name = bancai.mupi1?.name || &#39;&#39;; const mupi2Name = bancai.mupi2?.name || &#39;&#39;; const houdu = bancai.houdu.toString(); return [ caizhiName.toLowerCase(), mupi1Name.toLowerCase(), mupi2Name.toLowerCase(), houdu.toLowerCase(), ].some(field => field.includes(searchText.toLowerCase())); }); } // 渲染表格 function renderBancaiTable(bancais) { const $tbody = $(&#39;#bancaiTable tbody&#39;); $tbody.empty(); bancais.forEach(bancai => { const caizhiName = bancai.caizhi?.name || &#39;未知&#39;; const mupi1Name = bancai.mupi1?.name || &#39;未知&#39;; const mupi2Name = bancai.mupi2?.name || &#39;未知&#39;; const row = ` <tr data-id="${bancai.id}"> <td>${bancai.id}</td> <td>${caizhiName}</td> <td>${mupi1Name} ${bancai.mupi1?.you ? &#39;(油漆)&#39; : &#39;&#39;}</td> <td>${mupi2Name} ${bancai.mupi2?.you ? &#39;(油漆)&#39; : &#39;&#39;}</td> <td>${bancai.houdu}</td> <td> <button class="btn btn-sm btn-info view-btn">查看</button> <button class="btn btn-sm btn-warning edit-btn">编辑</button> <button class="btn btn-sm btn-danger delete-btn">删除</button> </td> </tr> `; $tbody.append(row); }); bindTableEvents(); } // 绑定表格事件 function bindTableEvents() { $(&#39;.view-btn&#39;).click(function () { const id = $(this).closest(&#39;tr&#39;).data(&#39;id&#39;); openModalForBancai(id, &#39;view&#39;); }); $(&#39;.edit-btn&#39;).click(function () { const id = $(this).closest(&#39;tr&#39;).data(&#39;id&#39;); openModalForBancai(id, &#39;edit&#39;); }); $(&#39;.delete-btn&#39;).click(function () { const id = $(this).closest(&#39;tr&#39;).data(&#39;id&#39;); deleteBancai(id); }); } // 添加按钮事件 $(&#39;#addBancaiBtn&#39;).click(function () { $(&#39;#bancaiForm&#39;)[0].reset(); $(&#39;#modalTitle&#39;).text(&#39;添加新板材&#39;); currentMode = &#39;add&#39;; enableForm(true); updateOptions(); modal.show(); }); // 搜索按钮事件 $(&#39;#searchBtn&#39;).click(function () { currentSearchText = $(&#39;#searchInput&#39;).val(); refreshTable(); }); // 输入框实搜索 $(&#39;#searchInput&#39;).on(&#39;input&#39;, function () { currentSearchText = $(this).val(); refreshTable(); }); // 打开弹窗显示板材数据 function openModalForBancai(id, mode) { const bancai = dataManager.data.bancais.find(b => b.id === id); if (!bancai) return; currentMode = mode; $(&#39;#bancaiId&#39;).val(bancai.id); $(&#39;#caizhiSelect&#39;).val(bancai.caizhi.id); $(&#39;#mupi1Select&#39;).val(bancai.mupi1.id); $(&#39;#mupi2Select&#39;).val(bancai.mupi2.id); $(&#39;#houdu&#39;).val(bancai.houdu); $(&#39;#modalTitle&#39;).text(mode === &#39;view&#39; ? &#39;板材详情&#39; : &#39;编辑板材&#39;); enableForm(mode === &#39;edit&#39;); modal.show(); } // 启用/禁用表单 function enableForm(enable) { $(&#39;#caizhiSelect&#39;).prop(&#39;disabled&#39;, !enable); $(&#39;#mupi1Select&#39;).prop(&#39;disabled&#39;, !enable); $(&#39;#mupi2Select&#39;).prop(&#39;disabled&#39;, !enable); $(&#39;#houdu&#39;).prop(&#39;disabled&#39;, !enable); $(&#39;#saveBtn&#39;).toggle(enable); } // 保存按钮点击事件 $(&#39;#saveBtn&#39;).click(async function () { const formData = { id: $(&#39;#bancaiId&#39;).val(), caizhiId: parseInt($(&#39;#caizhiSelect&#39;).val()), mupi1Id: parseInt($(&#39;#mupi1Select&#39;).val()), mupi2Id: parseInt($(&#39;#mupi2Select&#39;).val()), houdu: parseFloat($(&#39;#houdu&#39;).val()), }; try { if (currentMode === &#39;add&#39;) { await addEntity(&#39;bancais&#39;, formData); } else { await updateEntity(&#39;bancais&#39;, formData); } refreshTable(); modal.hide(); } catch (error) { console.error(&#39;操作失败:&#39;, error); alert(&#39;操作失败,请重试&#39;); } }); // 删除板材 async function deleteBancai(id) { if (!confirm(&#39;确定要删除此板材吗?&#39;)) return; try { await deleteEntity(&#39;bancais&#39;, id); refreshTable(); } catch (error) { console.error(&#39;删除失败:&#39;, error); alert(&#39;删除失败,请重试&#39;); } } // 初始化应用 initialize(); }); ------------------------ bancai.html ------------------------ <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>板材数据管理</title> <!-- 引入 Bootstrap CSS --> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet"> <!-- 引入 jQuery --> <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> </head> <body> <div class="container mt-4"> <h1 class="mb-4">板材数据管理</h1> <!-- 搜索框 --> <div class="row mb-3"> <div class="col-md-6"> <div class="input-group"> <input type="text" class="form-control" id="searchInput" placeholder="搜索材质、木皮或厚度..."> <button class="btn btn-outline-secondary" type="button" id="searchBtn"> <i class="bi bi-search"></i> 搜索 </button> </div> </div> <div class="col-md-6 text-end"> <button class="btn btn-primary" id="addBancaiBtn">添加新板材</button> </div> </div> <table class="table table-striped mt-3" id="bancaiTable"> <thead> <tr> <th>ID</th> <th>材质</th> <th>木皮1</th> <th>木皮2</th> <th>厚度</th> <th>操作</th> </tr> </thead> <tbody> <!-- 数据将通过 DataManager 加载 --> </tbody> </table> </div> <!-- 模态框保持不变 --> <div class="modal fade" id="bancaiModal" tabindex="-1" aria-hidden="true"> <!-- ... 原有模态框内容 ... --> </div> <!-- 查看/编辑弹窗 --> <div class="modal fade" id="bancaiModal" tabindex="-1" aria-hidden="true"> <div class="modal-dialog modal-lg"> <div class="modal-content"> <div class="modal-header"> <h5 class="modal-title" id="modalTitle">板材详情</h5> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> </div> <div class="modal-body"> <form id="bancaiForm"> <input type="hidden" id="bancaiId"> <div class="mb-3"> <label class="form-label">材质</label> <select class="form-select" id="caizhiSelect" name="caizhi"></select> </div> <div class="mb-3"> <label class="form-label">木皮1</label> <select class="form-select" id="mupi1Select" name="mupi1"></select> </div> <div class="mb-3"> <label class="form-label">木皮2</label> <select class="form-select" id="mupi2Select" name="mupi2"></select> </div> <div class="mb-3"> <label class="form-label">厚度</label> <input type="number" step="0.01" class="form-control" id="houdu" name="houdu"> </div> </form> </div> <div class="modal-footer"> <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">关闭</button> <button type="button" class="btn btn-primary" id="saveBtn">保存</button> </div> </div> </div> </div> <!-- 引入 Bootstrap JS --> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script> <script type="module" src="../js/bancai.js"></script> <!-- 改为模块方式导入 --> </body> </html> ------------------------ DataManager.js ------------------------ /** * 数据管理器类,负责与后端API通信并管理数据 */ class DataManager { constructor(baseUrl) { this.baseUrl = baseUrl; this.data = { bancais: [], dingdans: [], mupis: [], chanpins: [], kucuns: [], dingdan_chanpin_zujians: [], chanpin_zujians: [], zujians: [], caizhis: [], dingdan_chanpins: [], users: [] }; this.isSyncing = false; this.lastSync = null; // 回调注册表 this.callbacks = { // 全局回调 all: [], // 按实体类型分类的回调 bancais: [], dingdan: [], mupi: [], chanpin: [], kucun: [], dingdan_chanpin_zujian: [], chanpin_zujian: [], zujian: [], caizhi: [], dingdan_chanpin: [], user: [] // ...其他实体 }; } /** * 获取所有数据 * @returns {Promise<boolean>} 是否成功 */ async fetchAll() { try { const response = await fetch(`${this.baseUrl}/app/all`); if (!response.ok) throw new Error(&#39;Network response was not ok&#39;); const result = await response.json(); if (result.status !== 200) throw new Error(result.text || &#39;API error&#39;); // 更新本地数据 Object.keys(this.data).forEach(key => { if (result.data[key]) { this.data[key] = result.data[key]; } }); this.lastSync = new Date(); return true; } catch (error) { console.error(&#39;Fetch error:&#39;, error); return false; } } /** * 注册回调函数 * @param {string} entity - 实体类型(如&#39;bancai&#39;)或&#39;all&#39;表示全局回调 * @param {Function} callback - 回调函数,参数为(operation, data) */ registerCallback(entity, callback) { if (!this.callbacks[entity]) { this.callbacks[entity] = []; } this.callbacks[entity].push(callback); } /** * 移除回调函数 * @param {string} entity - 实体类型 * @param {Function} callback - 要移除的回调函数 */ unregisterCallback(entity, callback) { if (!this.callbacks[entity]) return; const index = this.callbacks[entity].indexOf(callback); if (index !== -1) { this.callbacks[entity].splice(index, 1); } } /** * 触发回调 * @param {string} operation - 操作类型(&#39;add&#39;, &#39;update&#39;, &#39;delete&#39;) * @param {string} entity - 实体类型 * @param {Object} data - 相关数据 */ triggerCallbacks(operation, entity, data) { // 触发全局回调 this.callbacks.all.forEach(cb => cb(operation, entity, data)); // 触发特定实体回调 if (this.callbacks[entity]) { this.callbacks[entity].forEach(cb => cb(operation, data)); } } /** * 执行CRUD操作并触发回调 */ async crudOperation(operation, entity, data) { try { const response = await fetch(`${this.baseUrl}/app/${operation}/${entity}`, { method: &#39;POST&#39;, headers: {&#39;Content-Type&#39;: &#39;application/json&#39;}, body: JSON.stringify(data) }); if (!response.ok) throw new Error(&#39;Network response was not ok&#39;); const result = await response.json(); if (result.status !== 200) throw new Error(result.text || &#39;API error&#39;); // 触发操作成功的回调 this.triggerCallbacks(operation, entity, data); // 自动同步数据 this.syncData(); return result; } catch (error) { console.error(&#39;CRUD error:&#39;, error); // 触发操作失败的回调 this.triggerCallbacks(`${operation}_error`, entity, { data, error: error.message }); throw error; } } /** * 执行CRUD操作 * @param {string} operation - &#39;add&#39;, &#39;delete&#39;, &#39;update&#39; * @param {string} entity - 实体名称(小写) * @param {Object} data - 要发送的数据 * @returns {Promise<Object>} 响应结果 */ async crudOperation(operation, entity, data) { try { const response = await fetch(`${this.baseUrl}/app/${operation}/${entity}`, { method: &#39;POST&#39;, headers: {&#39;Content-Type&#39;: &#39;application/json&#39;}, body: JSON.stringify(data) }); if (!response.ok) throw new Error(&#39;Network response was not ok&#39;); const result = await response.json(); if (result.status !== 200) throw new Error(result.text || &#39;API error&#39;); // 触发操作成功的回调 this.triggerCallbacks(operation, entity, data); // 自动同步数据 this.syncData(); return result; } catch (error) { console.error(&#39;CRUD error:&#39;, error); // 触发操作失败的回调 this.triggerCallbacks(`${operation}_error`, entity, { data, error: error.message }); throw error; } } /** * 自动同步数据(防止频繁请求) */ async syncData() { if (this.isSyncing) return; // 距离上次同步超过5秒才执行新同步 if (this.lastSync && new Date() - this.lastSync < 5000) { setTimeout(() => this.syncData(), 5000 - (new Date() - this.lastSync)); return; } this.isSyncing = true; try { await this.fetchAll(); } finally { this.isSyncing = false; } } /** * 添加实体 * @param {string} entity - 实体名称 * @param {Object} data - 实体数据 */ async addEntity(entity, data) { return this.crudOperation(&#39;add&#39;, entity, data); } /** * 更新实体 * @param {string} entity - 实体名称 * @param {Object} data - 实体数据(必须包含id) */ async updateEntity(entity, data) { return this.crudOperation(&#39;update&#39;, entity, data); } /** * 删除实体 * @param {string} entity - 实体名称 * @param {number} id - 实体ID */ async deleteEntity(entity, id) { return this.crudOperation(&#39;delete&#39;, entity, {id}); } } export { DataManager }; // 创建单例实例 const dataManager = new DataManager(&#39;http://127.0.0.1:8080/KuCun2&#39;); // 初始化获取所有数据 dataManager.fetchAll().then(() => { console.log(&#39;Initial data loaded&#39;); }); // 导出数据对象,外部可以直接访问 data.bancais, data.dingdans 等 export const data = dataManager.data; // 导出操作方法 export const addEntity = dataManager.addEntity.bind(dataManager); export const updateEntity = dataManager.updateEntity.bind(dataManager); export const deleteEntity = dataManager.deleteEntity.bind(dataManager); export const fetchAll = dataManager.fetchAll.bind(dataManager); DataManager.js:68 Fetch error: TypeError: Cannot read properties of undefined (reading &#39;baseUrl&#39;) at fetchAll (DataManager.js:52:44) at initialize (bancai.js:36:19)
06-16
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值