------------------------ DataManager.js ------------------------
//{ 实体类的关联和属性列表
// “entities”: {
// “Dingdan”: {
// “properties”: {
// “id”: “Integer”,
// “number”: “String”,
// “xiadan”: “Date”,
// “jiaohuo”: “Date”,
// “dingdan_chanpin”: “List<Dingdan_chanpin> (OneToMany)”,
// “dingdan_chanpins_zujian”: “List<Dingdan_chanpin_zujian> (OneToMany)”
// },
// “relations”: [
// “关联订单产品(Dingdan_chanpin)”,
// “关联订单组件(Dingdan_chanpin_zujian)”
// ]
// },
// “Dingdan_chanpin”: {
// “properties”: {
// “id”: “Integer”,
// “shuliang”: “Integer”
// },
// “relations”: [
// “多对一关联订单(Dingdan)”,
// “多对一关联产品(Chanpin)”
// ]
// },
// “Dingdan_chanpin_zujian”: {
// “properties”: {
// “id”: “Integer”,
// “shuliang”: “Integer”
// },
// “relations”: [
// “多对一关联订单(Dingdan)”,
// “多对一关联组件(Chanpin_zujian)”,
// “多对一关联板材(Bancai)”
// ]
// },
// “Jinhuo”: {
// “properties”: {
// “id”: “Integer”,
// “shuliang”: “Integer”,
// “date”: “Date”
// },
// “relations”: [
// “多对一关联订单(Dingdan)”,
// “多对一关联产品(Chanpin)”,
// “多对一关联组件(Zujian)”,
// “多对一关联板材(Bancai)”,
// “多对一关联用户(User)”
// ]
// },
// “Kucun”: {
// “properties”: {
// “id”: “Integer”,
// “shuliang”: “Long”
// },
// “relations”: [
// “一对一关联板材(Bancai)”
// ]
// },
// “Mupi”: {
// “properties”: {
// “id”: “Integer”,
// “name”: “String”,
// “you”: “Boolean”
// },
// “relations”: [
// “被板材关联(Bancai - mupi1/mupi2)”
// ]
// },
// “User”: {
// “properties”: {
// “id”: “Integer”,
// “name”: “String”,
// “andy”: “String”,
// “pass”: “String”,
// “role”: “int”
// }
// },
// “Zujian”: {
// “properties”: {
// “id”: “Integer”,
// “name”: “String”
// },
// “relations”: [
// “一对多关联产品组件(Chanpin_zujian)”
// ]
// },
// “Bancai”: {
// “properties”: {
// “id”: “Integer”,
// “houdu”: “Double”
// },
// “relations”: [
// “多对一关联材质(Caizhi)”,
// “多对一关联木皮(Mupi - mupi1/mupi2)”,
// “一对一关联库存(Kucun)”
// ]
// },
// “Caizhi”: {
// “properties”: {
// “id”: “Integer”,
// “name”: “String”
// },
// “relations”: [
// “一对多关联板材(Bancai)”
// ]
// },
// “Chanpin”: {
// “properties”: {
// “id”: “Integer”,
// “bianhao”: “String”
// },
// “relations”: [
// “一对多关联订单产品(Dingdan_chanpin)”,
// “一对多关联产品组件(Chanpin_zujian)”
// ]
// },
// “Chanpin_zujian”: {
// “properties”: {
// “id”: “Integer”,
// “one_howmany”: “Double”
// },
// “relations”: [
// “多对一关联产品(Chanpin)”,
// “多对一关联组件(Zujian)”,
// “多对一关联板材(Bancai)”
// ]
// }
// },
// “relationsSummary”: [
// “订单(Dingdan) 1:N 订单产品(Dingdan_chanpin)”,
// “订单(Dingdan) 1:N 订单组件(Dingdan_chanpin_zujian)”,
// “产品(Chanpin) 1:N 产品组件(Chanpin_zujian)”,
// “组件(Zujian) 1:N 产品组件(Chanpin_zujian)”,
// “板材(Bancai) 1:1 库存(Kucun)”,
// “材质(Caizhi) 1:N 板材(Bancai)”
// ]
//}
/**
优化后的关联解析函数(解决空值问题)
@param {Object} data - 后端原始数据
@returns {Object} 处理后的完整关联数据
*/
function __resolveDataReferences(data) {
// 创建ID映射表(带空值保护)
const idMaps = {};
Object.keys(data).forEach(key => {
if (Array.isArray(data[key])) {
idMaps[key] = new Map();
data[key].forEach(item => item.id && idMaps[key].set(item.id, item));
}
});
// 通用关联解析方法(带安全检测)
const resolveRef = (sourceArray, sourceKey, targetKey, refProperty) => {
if (!Array.isArray(sourceArray)) return;
sourceArray.forEach(item => { const refObj = item[refProperty]; if (refObj && refObj.id && idMaps[targetKey]) { const target = idMaps[targetKey].get(refObj.id); if (target) { // 建立正向引用 item[refProperty] = target; // 建立反向引用(自动创建关联数组) const reverseProp = sourceKey.endsWith('s') ? sourceKey.slice(0, -1) + '_list' : sourceKey + '_list'; if (!target[reverseProp]) target[reverseProp] = []; if (!target[reverseProp].includes(item)) { target[reverseProp].push(item); } } } });
};
// 处理特定关联(使用新安全方法)
// 订单 ↔ 订单产品
if (data.dingdans && data.dingdan_chanpins) {
resolveRef(data.dingdan_chanpins, ‘dingdans’, ‘dingdans’, ‘dingdan’);
}
// 订单 ↔ 订单组件
if (data.dingdans && data.dingdan_chanpin_zujians) {
resolveRef(data.dingdan_chanpin_zujians, ‘dingdans’, ‘dingdans’, ‘dingdan’);
}
// 产品 ↔ 产品组件
if (data.chanpins && data.chanpin_zujians) {
resolveRef(data.chanpin_zujians, ‘chanpins’, ‘chanpins’, ‘chanpin’);
}
// 组件 ↔ 产品组件
if (data.zujians && data.chanpin_zujians) {
resolveRef(data.chanpin_zujians, ‘zujians’, ‘zujians’, ‘zujian’);
}
// 材质 ↔ 板材
if (data.caizhis && data.bancais) {
resolveRef(data.bancais, ‘caizhis’, ‘caizhis’, ‘caizhi’);
}
// 板材 ↔ 库存(一对一)
if (data.bancais && data.kucuns) {
resolveRef(data.bancais, ‘kucuns’, ‘kucuns’, ‘kucun’);
resolveRef(data.kucuns, ‘bancais’, ‘bancais’, ‘bancai’); // 反向引用
}
// 板材 ↔ 木皮(mupi1/mupi2)
if (data.bancais && data.mupis) {
resolveRef(data.bancais, ‘mupis’, ‘mupis’, ‘mupi1’);
resolveRef(data.bancais, ‘mupis’, ‘mupis’, ‘mupi2’);
}
// 订单产品 ↔ 产品
if (data.dingdan_chanpins && data.chanpins) {
resolveRef(data.dingdan_chanpins, ‘chanpins’, ‘chanpins’, ‘chanpin’);
}
// 订单组件 ↔ 产品组件
if (data.dingdan_chanpin_zujians && data.chanpin_zujians) {
resolveRef(data.dingdan_chanpin_zujians, ‘chanpin_zujians’, ‘chanpin_zujians’, ‘chanpin_zujian’);
}
// 订单组件 ↔ 板材
if (data.dingdan_chanpin_zujians && data.bancais) {
resolveRef(data.dingdan_chanpin_zujians, ‘bancais’, ‘bancais’, ‘bancai’);
}
// 进货 ↔ 相关实体
if (data.jinhuos) {
[‘dingdan’, ‘chanpin’, ‘zujian’, ‘bancai’, ‘user’].forEach(entity => {
const plural = entity + ‘s’;
if (data[plural]) {
resolveRef(data.jinhuos, plural, plural, entity);
}
});
}
return data;
}
function resolveDataReferences(data) {
// 获取 data 对象的所有顶层键
const keys = Object.keys(data);
// 遍历每个顶层键(如 users, posts 等) for (const key of keys) { const entities = data[key]; // 遍历该顶层键下的每个实体(如每个 user 或 post) for (const entity of entities) { // 遍历实体的每个属性 for (const attribute in entity) { if (entity.hasOwnProperty(attribute)) { var trpe=attribute.replace(/\d/g, ''); // 确保属性属于当前实体 if (Array.isArray(entity[attribute])) { if(data[trpe]==null){ trpe+="s" } // 如果属性是一个数组,则将数组中的每个 ID 替换为对应的实际对象 entity[attribute] = entity[attribute].map(item => data[trpe ]?.find(updateItem => updateItem.id === item.id) || item ); } else if (typeof entity[attribute] === "object" && entity[attribute] !== null) { // 如果属性是一个对象,则将其替换为对应的实际对象 entity[attribute] = data[trpe + "s"]?.find(updateItem => updateItem.id === entity[attribute].id); } } } } } console.log(data) return data;
}
/**
数据管理器类,负责与后端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: [],
// …其他实体
};
this.syncQueue = Promise.resolve();
}
/**
获取所有数据
@returns {Promise} 是否成功
*/
async fetchAll() {
console.log(this)
try {
const response = await fetch(${this.baseUrl}/app/all);
if (!response.ok) throw new Error(‘Network response was not ok’);
const result = await response.json();
if (result.status !== 200) throw new Error(result.text || ‘API error’);
const resolvedData = resolveDataReferences(result.data);
// 更新本地数据
Object.keys(this.data).forEach(key => {
if (resolvedData[key]) {
this.data[key] = resolvedData[key];
}
});
this.lastSync = new Date();
// 关键改进:数据更新后触发刷新回调
this.triggerCallbacks(‘refresh’, ‘all’, this.data);
return true;
} catch (error) {
console.error(‘Fetch error:’, error);
// 触发错误回调
this.triggerCallbacks(‘fetch_error’, ‘all’, { error });
return false;
}
}
/**
注册回调函数
@param {string} entity - 实体类型(如’bancai’)或’all’表示全局回调
@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 - 操作类型(‘add’, ‘update’, ‘delete’)
@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: ‘POST’,
headers: {‘Content-Type’: ‘application/json’},
body: JSON.stringify(data)
});
if (!response.ok) throw new Error(‘Network response was not ok’);
const result = await response.json();
if (result.status !== 200) throw new Error(result.text || ‘API error’);
// 自动同步数据
this.syncData();
// 触发操作成功的回调
this.triggerCallbacks(operation, entity, data);
return result;
} catch (error) {
console.error(‘CRUD error:’, error);
// 触发操作失败的回调
this.triggerCallbacks(${operation}_error, entity, {
data,
error: error.message
});
throw error;
}
}
/**
执行CRUD操作
@param {string} operation - ‘add’, ‘delete’, ‘update’
@param {string} entity - 实体名称单数性质(小写)
@param {Object} data - 要发送的数据 后端要求数据格式为{属性: “值”, 关联对象: {id:0}, 关联对象集: [{id:0}]}
@returns {Promise} 响应结果
*/
async crudOperation(operation, entity, data) {
try {
const response = await fetch(${this.baseUrl}/app/${operation}/${entity}, {
method: ‘POST’,
headers: {‘Content-Type’: ‘application/json’},
body: JSON.stringify(data)
});
if (!response.ok) throw new Error('Network response was not ok'); const result = await response.json(); if (result.status !== 200) throw new Error(result.text || 'API error'); // 自动同步数据 this.syncQueue = this.syncQueue.then(async () => { await this.syncData(); // 同步完成后触发操作回调 this.triggerCallbacks(operation, entity, data); }); return result;
} catch (error) {
console.error(‘CRUD error:’, error);
// 触发操作失败的回调
this.triggerCallbacks(${operation}_error, entity, {
data,
error: error.message
});
throw error;
}
}
/**
自动同步数据(防止频繁请求)
*/
async syncData() {
if (this.isSyncing) {
this.pendingSync = true;
return;
}
this.isSyncing = true;
try {
await this.fetchAll();
} catch (error) {
console.error(‘Sync failed:’, error);
} finally {
this.isSyncing = false;
// 处理等待中的同步请求 if (this.pendingSync) { this.pendingSync = false; setTimeout(() => this.syncData(), 1000); }
}
}
/**
添加实体
@param {string} entity - 实体名称单数性质
@param {Object} data - 实体数据
*/
async addEntity(entity, data) {
return this.crudOperation(‘add’, entity, data);
}
/**
更新实体
@param {string} entity - 实体名称单数性质
@param {Object} data - 实体数据(必须包含id)
*/
async updateEntity(entity, data) {
return this.crudOperation(‘update’, entity, data);
}
/**
删除实体
@param {string} entity - 实体名称单数性质
@param {number} id - 实体ID
*/
async deleteEntity(entity, id) {
return this.crudOperation(‘delete’, entity, {id});
}
/**
新增方法:手动触发数据刷新
*/
async refreshData() {
return this.syncQueue = this.syncQueue.then(() => this.syncData());
}
}
export { DataManager };
// 创建单例实例
//const dataManager = new DataManager(‘http://127.0.0.1:8080/KuCun2’);
//// 初始化时获取所有数据
//dataManager.fetchAll().then(() => {
// console.log(‘Initial data loaded’);
//});
// 导出数据对象,外部可以直接访问 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);
------------------------ dingdan.html ------------------------
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>订单-产品-组件-板材查询系统</title> <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> <style> body { font-family: Arial, sans-serif; max-width: 1000px; margin: 0 auto; padding: 20px; background-color: #f5f5f5; } .container { background: white; border-radius: 8px; padding: 20px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); } h1 { color: #2c3e50; text-align: center; margin-bottom: 30px; } .query-section { background: #f8f9fa; border-radius: 6px; padding: 15px; margin-bottom: 25px; border-left: 4px solid #3498db; } .query-section h2 { color: #2c3e50; margin-top: 0; } label { display: block; margin-bottom: 8px; font-weight: bold; color: #34495e; } input, select, button { padding: 10px; border: 1px solid #ddd; border-radius: 4px; font-size: 16px; margin-bottom: 15px; width: 100%; box-sizing: border-box; } button { background: #3498db; color: white; border: none; cursor: pointer; transition: background 0.3s; font-weight: bold; } button:hover { background: #2980b9; } .query-row { display: flex; gap: 15px; align-items: flex-end; } .query-row .form-group { flex: 1; } .results { background: white; border: 1px solid #eee; border-radius: 6px; padding: 15px; margin-top: 20px; max-height: 300px; overflow-y: auto; } .results h3 { margin-top: 0; color: #2c3e50; border-bottom: 2px solid #3498db; padding-bottom: 8px; } table { width: 100%; border-collapse: collapse; margin-top: 10px; } th, td { padding: 10px; text-align: left; border-bottom: 1px solid #eee; } th { background: #f8f9fa; font-weight: bold; color: #2c3e50; } tr:hover td { background-color: #f1f8ff; } .loading { display: none; text-align: center; padding: 10px; color: #3498db; } .loading::after { content: "加载中..."; } </style> </head> <body> <div class="container"> <h1>订单-产品-组件-板材查询系统</h1> <!-- 查询面板 --> <div class="query-section"> <h2>查询条件</h2> <div class="query-row"> <div class="form-group"> <label for="orderId">订单ID:</label> <input type="text" id="orderId" placeholder="输入订单ID"> </div> <div class="form-group"> <button id="searchButton">查询订单</button> </div> </div> <div class="loading" id="orderLoading"></div> <div class="results" id="orderResults"></div> </div> <div class="query-section"> <h2>产品查询</h2> <div class="query-row"> <div class="form-group"> <label for="productId">产品ID (从上方选择):</label> <select id="productId"></select> </div> <div class="form-group"> <button id="searchProductButton">查询产品</button> </div> </div> <div class="loading" id="productLoading"></div> <div class="results" id="productResults"></div> </div> <div class="query-section"> <h2>组件查询</h2> <div class="query-row"> <div class="form-group"> <label for="componentId">组件ID (从上方选择):</label> <select id="componentId"></select> </div> <div class="form-group"> <button id="searchComponentButton">查询组件</button> </div> </div> <div class="loading" id="componentLoading"></div> <div class="results" id="componentResults"></div> </div> <div class="query-section"> <h2>板材查询</h2> <div class="loading" id="boardLoading"></div> <div class="results" id="boardResults"></div> </div> </div> <script> // 全局数据存储 let globalData = { orders: [], products: [], components: [], boards: [] }; // 模拟后端数据存储 const mockData = { orders: [ {id: 1, number: "ORD-2023-001"}, {id: 2, number: "ORD-2023-002"} ], products: [ {id: 101, bianhao: "P-001", orderId: 1}, {id: 102, bianhao: "P-002", orderId: 1}, {id: 103, bianhao: "P-003", orderId: 2} ], components: [ {id: 201, name: "门板", productId: 101}, {id: 202, name: "抽屉面板", productId: 101}, {id: 203, name: "框架", productId: 102} ], boards: [ { id: 301, caizhi: {id: 401, name: "橡木"}, mupi1: {id: 501, name: "直纹橡木"}, houdu: 18.0, kucun: {id: 601, shuliang: 150} }, { id: 302, caizhi: {id: 402, name: "胡桃木"}, mupi1: {id: 502, name: "直纹胡桃木"}, houdu: 25.0, kucun: {id: 602, shuliang: 80} } ] }; // 通信函数实现 async function https(url, data, callback) { const defaultConfig = { contentType: 'application/json', dataType: 'json', timeout: 10000 }; // 显示对应区域的加载动画 const domain = url.split("/")[1]; const loadingElement = $("#" + domain + "Loading"); loadingElement.show(); try { // 在实际项目中是AJAX请求 // 这里用模拟数据代替 let response; // 模拟不同端点的响应 switch(url) { case "/order/search": // 模拟搜索延迟 await new Promise(resolve => setTimeout(resolve, 800)); response = new Information(200, "success", mockData.orders.filter( o => o.id == data.id )); break; case "/product/searchByOrder": await new Promise(resolve => setTimeout(resolve, 600)); response = new Information(200, "success", mockData.products.filter( p => p.orderId == data.orderId )); break; case "/component/searchByProduct": await new Promise(resolve => setTimeout(resolve, 500)); response = new Information(200, "success", mockData.components.filter( c => c.productId == data.productId )); break; case "/board/searchByComponent": await new Promise(resolve => setTimeout(resolve, 400)); // 在实际应用中,组件到板材可能有复杂的映射关系 response = new Information(200, "success", mockData.boards); break; default: response = new Information(404, "Endpoint not found", null); } console.log(response) console.log(mockData) // 在实际项目中: // const response = await $.ajax({ // ...defaultConfig, // url: BASE_URL + url, // method: 'POST', // data: JSON.stringify(data) // }); if (response.Status === 200) { callback?.(response.data); } else { handleBusinessError(response); } return response.data; } catch (error) { handleNetworkError(error, url); return null; } finally { loadingElement.hide(); } } function handleBusinessError(response) { console.error('业务错误:', response.text); alert(`业务错误: ${response.text}`); } function handleNetworkError(error, url) { console.error(`网络请求错误: ${url}`, error); alert(`网络请求错误: ${url}, 请检查控制台`); } // 通信信息类 class Information { constructor(status, text, data) { this.Status = status; this.text = text; this.data = data; } static NewSuccess(data) { return new Information(200, "success", data); } static Newfail(status, text, data) { return new Information(status, text, data); } } // 订单查询 $("#searchButton").click(() => { const orderId = $("#orderId").val(); if (!orderId) { alert("请输入订单ID"); return; } https("/order/search", { id: parseInt(orderId) }, function(orders) { globalData.orders = orders; renderOrderResults(); $("#productId").empty(); // 清除产品选择框 }); }); // 产品查询 $("#searchProductButton").click(() => { const productId = $("#productId").val(); if (!productId) { alert("请先查询订单并选择产品"); return; } // 查找当前订单ID const selectedOrderId = globalData.orders[0].id; https("/product/searchByOrder", { orderId: selectedOrderId }, function(products) { globalData.products = products; renderProductResults(); $("#componentId").empty(); // 清除组件选择框 }); }); // 组件查询 $("#searchComponentButton").click(() => { const componentId = $("#componentId").val(); if (!componentId) { alert("请先查询产品并选择组件"); return; } // 查找当前产品ID const selectedProductId = $("#productId").val(); https("/component/searchByProduct", { productId: parseInt(selectedProductId) }, function(components) { globalData.components = components; renderComponentResults(); // 自动发起板材查询 $("#boardLoading").show(); https("/board/searchByComponent", { componentId: parseInt(componentId) }, function(boards) { globalData.boards = boards; renderBoardResults(); }); }); }); // 渲染函数 function renderOrderResults() { const container = $("#orderResults"); container.empty(); if (!globalData.orders || globalData.orders.length === 0) { container.html("<p>未找到相关订单</p>"); return; } let html = `<h3>订单查询结果 (${globalData.orders.length})</h3>`; html += `<table> <tr> <th>ID</th> <th>订单号</th> </tr>`; globalData.orders.forEach(order => { html += `<tr> <td>${order.id}</td> <td>${order.number}</td> </tr>`; }); html += `</table>`; container.html(html); // 填充产品选择框 $("#productId").empty(); $("#productId").append('<option value="">-- 选择产品 --</option>'); globalData.products.forEach(product => { $("#productId").append(`<option value="${product.id}">${product.bianhao}</option>`); console.log(product) }); } function renderProductResults() { const container = $("#productResults"); container.empty(); if (!globalData.products || globalData.products.length === 0) { container.html("<p>该订单下无产品</p>"); return; } let html = `<h3>产品查询结果 (${globalData.products.length})</h3>`; html += `<table> <tr> <th>ID</th> <th>产品编号</th> <th>所属订单</th> </tr>`; globalData.products.forEach(product => { html += `<tr> <td>${product.id}</td> <td>${product.bianhao}</td> <td>${product.orderId}</td> </tr>`; }); html += `</table>`; container.html(html); // 填充组件选择框 $("#componentId").empty(); $("#componentId").append('<option value="">-- 选择组件 --</option>'); globalData.components.forEach(component => { $("#componentId").append(`<option value="${component.id}">${component.name}</option>`); }); } function renderComponentResults() { const container = $("#componentResults"); container.empty(); if (!globalData.components || globalData.components.length === 0) { container.html("<p>该产品下无组件</p>"); return; } let html = `<h3>组件查询结果 (${globalData.components.length})</h3>`; html += `<table> <tr> <th>ID</th> <th>组件名称</th> <th>所属产品</th> </tr>`; globalData.components.forEach(component => { html += `<tr> <td>${component.id}</td> <td>${component.name}</td> <td>${component.productId}</td> </tr>`; }); container.html(html); } function renderBoardResults() { const container = $("#boardResults"); container.empty(); if (!globalData.boards || globalData.boards.length === 0) { container.html("<p>该组件无相关板材信息</p>"); return; } let html = `<h3>板材查询结果 (${globalData.boards.length})</h3>`; html += `<table> <tr> <th>板材ID</th> <th>材质</th> <th>木皮1</th> <th>厚度(mm)</th> <th>库存量</th> </tr>`; globalData.boards.forEach(board => { html += `<tr> <td>${board.id}</td> <td>${board.caizhi.name}</td> <td>${board.mupi1.name}</td> <td>${board.houdu}</td> <td>${board.kucun ? board.kucun.shuliang : 0}</td> </tr>`; }); container.html(html); } // 为选择框添加变更事件 $("#productId").change(function() { $("#searchProductButton").click(); }); $("#componentId").change(function() { $("#searchComponentButton").click(); }); </script> </body> </html> ------------------------ 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 './data/DataManager.js?'; document.addEventListener('DOMContentLoaded', async () => { try { // 创建实例并挂载到window window.dataManager = new DataManager('/KuCun2'); // 初始化数据 await window.dataManager.fetchAll(); console.log('Data Manager initialized successfully'); // 设置iframe通信 const iframe = document.getElementById('iframeid'); iframe.onload = () => { // 通知iframe数据已准备好 iframe.contentWindow.postMessage('DataManagerReady', '*'); }; // 如果iframe已经加载,立即发送消息 if (iframe.contentDocument.readyState === 'complete') { iframe.contentWindow.postMessage('DataManagerReady', '*'); } } catch (error) { console.error('Failed to initialize DataManager:', 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='frame-header-li' style="color:#fff">峤丞木材仓库管理</a> <a id="username" class='frame-header-li' style="color:#520">峤丞木材仓库管理</a> <!-- 菜单 --> <ul class="menu"> <li id="profile">个人资料</li> <li id="change-password">修改密码</li> <li id="logout-btn">注销</li> </ul> </div> <div class="frame-body"> <div class="frame-side"> <ul id="main_u" class='frame-ul' style="text-align:center;"> <li ><a href="main/test.html" target="main">首页</a></li> <li><a href="main/bancai.html" target="main">板材查询</a></li> <li><a href="main/tianjia.html" target="main">订单板材录入</a></li> <li><a href="main/Guanli.html" target="main">人员管理</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> ------------------------ dingdan.js ------------------------ // 监听来自父窗口的消息 window.addEventListener('message', function(event) { // 确保消息来自父窗口且是 DataManagerReady 事件 if (event.source === window.parent && event.data === 'DataManagerReady') { initializeDataManager(); } });
// 初始化数据管理器
function initializeDataManager() {
// 从父窗口获取 DataManager 实例
const dataManager = window.parent.dataManager;
if (dataManager) { // 注册数据刷新回调 dataManager.registerCallback('all', function(operation, entity, data) { console.log("dhdhdh") // 当数据更新时刷新页面 updatePageData(dataManager); }); // 初始页面数据更新 updatePageData(dataManager); // 设置搜索功能 setupSearch(dataManager); } else { console.error('DataManager not available'); }
}
// 更新页面数据
function updatePageData(dataManager) {
// 更新统计卡片
updateStats(dataManager);
// 更新结果表格 updateTable(dataManager); // 更新最后更新时间 document.getElementById('lastUpdate').textContent = new Date().toLocaleTimeString();
}
// 更新统计卡片数据
function updateStats(dataManager) {
const data = dataManager.data;
// 订单总数 document.getElementById('orderCount').textContent = data.dingdans?.length || 0; // 产品种类 document.getElementById('productCount').textContent = data.chanpins?.length || 0; // 板材库存种类 document.getElementById('materialCount').textContent = data.bancais?.length || 0; // 库存总量(所有库存数量之和) const totalStock = data.kucuns?.reduce((sum, kucun) => sum + kucun.shuliang, 0) || 0; document.getElementById('totalStock').textContent = totalStock;
}
// 更新结果表格
function updateTable(dataManager) {
const tableBody = document.getElementById(‘resultBody’);
tableBody.innerHTML = ‘’; // 清空现有内容
const data = dataManager.data; const results = []; let resultCount = 0; // 处理订单产品数据 if (data.dingdan_chanpins) { data.dingdan_chanpins.forEach(dc => { if (!dc.dingdan || !dc.chanpin) return; // 处理产品组件 if (dc.chanpin.chanpin_zujians) { dc.chanpin.chanpin_zujians.forEach(cz => { if (!cz.zujian || !cz.bancai) return; results.push({ orderNumber: dc.dingdan.number, productInfo: dc.chanpin.bianhao, productQuantity: dc.shuliang, component: cz.zujian.name, material: `${cz.bancai.id} (${cz.bancai.caizhi?.name || '未知材质'})`, materialPerComponent: cz.one_howmany, materialOrderQuantity: dc.shuliang * cz.one_howmany, operation: '<button class="btn btn-sm btn-outline-primary">详情</button>' }); resultCount++; }); } }); } // 处理直接订单组件数据 if (data.dingdan_chanpin_zujians) { data.dingdan_chanpin_zujians.forEach(dcz => { if (!dcz.dingdan || !dcz.chanpin_zujian || !dcz.chanpin_zujian.zujian || !dcz.bancai) return; results.push({ orderNumber: dcz.dingdan.number, productInfo: dcz.chanpin_zujian.chanpin?.bianhao || '独立组件', productQuantity: dcz.shuliang, component: dcz.chanpin_zujian.zujian.name, material: `${dcz.bancai.id} (${dcz.bancai.caizhi?.name || '未知材质'})`, materialPerComponent: dcz.chanpin_zujian.one_howmany, materialOrderQuantity: dcz.shuliang * dcz.chanpin_zujian.one_howmany, operation: '<button class="btn btn-sm btn-outline-primary">详情</button>' }); resultCount++; }); } // 填充表格 if (resultCount > 0) { document.getElementById('noResults').style.display = 'none'; results.forEach(row => { const tr = document.createElement('tr'); tr.innerHTML = ` <td>${row.orderNumber}</td> <td>${row.productInfo}</td> <td>${row.productQuantity}</td> <td>${row.component}</td> <td>${row.material}</td> <td>${row.materialPerComponent}</td> <td>${row.materialOrderQuantity}</td> <td>${row.operation}</td> `; tableBody.appendChild(tr); }); } else { document.getElementById('noResults').style.display = 'flex'; } // 更新结果计数 document.getElementById('resultCount').textContent = resultCount;
}
// 设置搜索功能
function setupSearch(dataManager) {
// 订单搜索
ParseError: KaTeX parse error: Expected 'EOF', got '#' at position 3: ('#̲orderSearch').o…(this).val().toLowerCase(), 0);
});
// 产品搜索 $('#productSearch').on('input', function() { filterTable($(this).val().toLowerCase(), 1); }); // 板材搜索 $('#materialSearch').on('input', function() { filterTable($(this).val().toLowerCase(), 4); }); // 木皮搜索 $('#woodSearch').on('input', function() { filterTable($(this).val().toLowerCase(), 4); // 假设材质信息在第4列 }); // 厚度搜索 $('#thicknessBtn').click(function() { const thickness = parseFloat($('#minThickness').val()); if (!isNaN(thickness)) { filterByThickness(thickness); } });
}
// 表格过滤函数
function filterTable(searchTerm, columnIndex) {
const rows = $(‘#resultBody tr’);
let visibleCount = 0;
rows.each(function() { const cellText = $(this).find(`td:eq(${columnIndex})`).text().toLowerCase(); const isMatch = cellText.includes(searchTerm); $(this).toggle(isMatch); // 高亮匹配文本 if (isMatch && searchTerm) { $(this).addClass('highlight'); } else { $(this).removeClass('highlight'); } if (isMatch) visibleCount++; }); // 更新结果计数 document.getElementById('resultCount').textContent = visibleCount; document.getElementById('noResults').style.display = visibleCount > 0 ? 'none' : 'flex';
}
// 按厚度过滤
function filterByThickness(thickness) {
const rows = $(‘#resultBody tr’);
let visibleCount = 0;
rows.each(function() { const materialText = $(this).find('td:eq(4)').text(); // 从文本中提取厚度值(假设格式为"ID (材质) 厚度mm") const match = materialText.match(/(\d+(\.\d+)?)\s*mm/); if (match) { const materialThickness = parseFloat(match[1]); const isMatch = !isNaN(materialThickness) && materialThickness >= thickness; $(this).toggle(isMatch); if (isMatch) visibleCount++; } else { $(this).hide(); } }); // 更新结果计数 document.getElementById('resultCount').textContent = visibleCount; document.getElementById('noResults').style.display = visibleCount > 0 ? 'none' : 'flex';
}
// 如果直接加载test.html,尝试请求DataManager
if (window.parent) {
window.parent.postMessage(‘RequestDataManager’, ‘*’);
}
修改dingdan.js,使之更契合数据原,板材显示要全面材质和木皮都要显示