------------------------ 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 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>
------------------------ bancai.js ------------------------
// bancai.js
$(document).ready(function () {
const modal = new bootstrap.Modal('#bancaiModal');
let currentMode = 'view';
let caizhiList = [];
let mupiList = [];
let currentSearchText = '';
// 从父窗口获取 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('无法从父窗口获取 DataManager'));
}
});
}
window.addEventListener('message', (event) => {
if (event.data === 'DataManagerReady') {
initialize();
}
});
// 初始化函数
async function initialize() {
try {
dataManager = await waitForDataManager();
if (!dataManager || typeof dataManager.fetchAll !== 'function') {
throw new Error('无效的 DataManager 实例');
}
// 解构需要的方法和属性
// const { addEntity, dataManager.deleteEntity, deleteEntity, fetchAll } = dataManager;
// 确保数据已加载
await dataManager.fetchAll();
// 更新材质和木皮选项
updateOptions();
// 渲染板材表格
refreshTable();
} catch (error) {
console.error('初始化失败:', error);
alert('系统初始化失败,请刷新页面或联系管理员');
}
}
// 更新材质和木皮选项
function updateOptions() {
caizhiList = dataManager.data.caizhis;
updateSelectOptions('#caizhiSelect', caizhiList);
mupiList = dataManager.data.mupis.map(m => ({
...m,
name: m.you ? `${m.name}(油漆)` : m.name,
}));
updateSelectOptions('#mupi1Select', mupiList);
updateSelectOptions('#mupi2Select', 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 || '';
const mupi1Name = bancai.mupi1?.name || '';
const mupi2Name = bancai.mupi2?.name || '';
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 = $('#bancaiTable tbody');
$tbody.empty();
bancais.forEach(bancai => {
const caizhiName = bancai.caizhi?.name || '未知';
const mupi1Name = bancai.mupi1?.name || '未知';
const mupi2Name = bancai.mupi2?.name || '未知';
const row = `
<tr data-id="${bancai.id}">
<td>${bancai.id}</td>
<td>${caizhiName}</td>
<td>${mupi1Name} ${bancai.mupi1?.you ? '(油漆)' : ''}</td>
<td>${mupi2Name} ${bancai.mupi2?.you ? '(油漆)' : ''}</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() {
$('.view-btn').click(function () {
const id = $(this).closest('tr').data('id');
openModalForBancai(id, 'view');
});
$('.edit-btn').click(function () {
const id = $(this).closest('tr').data('id');
openModalForBancai(id, 'edit');
});
$('.delete-btn').click(function () {
const id = $(this).closest('tr').data('id');
deleteBancai(id);
});
}
// 添加按钮事件
$('#addBancaiBtn').click(function () {
$('#bancaiForm')[0].reset();
$('#modalTitle').text('添加新板材');
currentMode = 'add';
enableForm(true);
updateOptions();
modal.show();
});
// 搜索按钮事件
$('#searchBtn').click(function () {
currentSearchText = $('#searchInput').val();
refreshTable();
});
// 输入框实时搜索
$('#searchInput').on('input', function () {
currentSearchText = $(this).val();
refreshTable();
});
// 打开弹窗显示板材数据
function openModalForBancai(id, mode) {
const bancai = dataManager.data.bancais.find(b => b.id === id);
if (!bancai) return;
currentMode = mode;
$('#bancaiId').val(bancai.id);
$('#caizhiSelect').val(bancai.caizhi.id);
$('#mupi1Select').val(bancai.mupi1.id);
$('#mupi2Select').val(bancai.mupi2.id);
$('#houdu').val(bancai.houdu);
$('#modalTitle').text(mode === 'view' ? '板材详情' : '编辑板材');
enableForm(mode === 'edit');
modal.show();
}
// 启用/禁用表单
function enableForm(enable) {
$('#caizhiSelect').prop('disabled', !enable);
$('#mupi1Select').prop('disabled', !enable);
$('#mupi2Select').prop('disabled', !enable);
$('#houdu').prop('disabled', !enable);
$('#saveBtn').toggle(enable);
}
// 保存按钮点击事件
$('#saveBtn').click(async function () {
const formData = {
id: $('#bancaiId').val(),
caizhiId: parseInt($('#caizhiSelect').val()),
mupi1Id: parseInt($('#mupi1Select').val()),
mupi2Id: parseInt($('#mupi2Select').val()),
houdu: parseFloat($('#houdu').val()),
};
try {
if (currentMode === 'add') {
await dataManager.addEntity('bancai', formData);
} else {
await dataManager.updateEntity('bancai', formData);
}
refreshTable();
modal.hide();
} catch (error) {
console.error('操作失败:', error);
alert('操作失败,请重试');
}
});
// 删除板材
async function deleteBancai(id) {
if (!confirm('确定要删除此板材吗?')) return;
try {
await deleteEntity('bancai', id);
refreshTable();
} catch (error) {
console.error('删除失败:', error);
alert('删除失败,请重试');
}
}
// 初始化应用
//initialize();
});
------------------------ DataManager.js ------------------------
//{ 实体类的关联和属性列表
// "entities": {
// "Dingdan": {
// "properties": {
// "id": "Integer",
// "number": "String",
// "xiadan": "Date",
// "jiaohuo": "Date",
// "dingdan_chanpins": "List<Dingdan_chanpin> (OneToMany)",
// "dingdan_chanpins_zujians": "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)"
// ]
//}
/**
* 数据管理器类,负责与后端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() {
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');
// 更新本地数据
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('Fetch error:', error);
return false;
}
}
/**
* 解析数据关联关系,将ID引用转换为对象引用
* @param {Object} data - 从后端加载的原始数据
* @returns {Object} - 处理后的数据,包含完整的对象关联
*/
function resolveDataReferences(data) {
// 创建ID映射表
const idMaps = {};
Object.keys(data).forEach(key => {
idMaps[key] = new Map();
data[key].forEach(item => idMaps[key].set(item.id, item));
});
// 处理多对一和一对一关系
const resolveRef = (source, sourceKey, targetKey, propertyName) => {
source.forEach(item => {
if (item[propertyName] && item[propertyName].id) {
const refId = item[propertyName].id;
const target = idMaps[targetKey]?.get(refId);
if (target) {
item[propertyName] = target;
// 建立反向引用(一对多关系)
if (!target[sourceKey]) target[sourceKey] = [];
if (!target[sourceKey].includes(item)) {
target[sourceKey].push(item);
}
}
}
});
};
// 处理一对多关系(直接创建关联数组)
const resolveOneToMany = (sourceKey, targetKey, propertyName) => {
const sourceItems = data[sourceKey];
sourceItems.forEach(source => {
if (!source[propertyName]) source[propertyName] = [];
});
data[targetKey].forEach(target => {
if (target[sourceKey]?.id) {
const sourceId = target[sourceKey].id;
const source = idMaps[sourceKey]?.get(sourceId);
if (source && source[propertyName]) {
source[propertyName].push(target);
}
}
});
};
// 处理特定关联关系
// 订单 ↔ 订单产品 (1:N)
resolveOneToMany('dingdans', 'dingdan_chanpins', 'dingdan_chanpins');
resolveRef(data.dingdan_chanpins, 'dingdans', 'dingdans', 'dingdan');
// 订单 ↔ 订单组件 (1:N)
resolveOneToMany('dingdans', 'dingdan_chanpin_zujians', 'dingdan_chanpin_zujians');
resolveRef(data.dingdan_chanpin_zujians, 'dingdans', 'dingdans', 'dingdan');
// 产品 ↔ 产品组件 (1:N)
resolveOneToMany('chanpins', 'chanpin_zujians', 'chanpin_zujians');
resolveRef(data.chanpin_zujians, 'chanpins', 'chanpins', 'chanpin');
// 组件 ↔ 产品组件 (1:N)
resolveOneToMany('zujians', 'chanpin_zujians', 'chanpin_zujians');
resolveRef(data.chanpin_zujians, 'zujians', 'zujians', 'zujian');
// 材质 ↔ 板材 (1:N)
resolveOneToMany('caizhis', 'bancais', 'bancais');
resolveRef(data.bancais, 'caizhis', 'caizhis', 'caizhi');
// 板材 ↔ 库存 (1:1)
resolveRef(data.bancais, 'kucuns', 'kucuns', 'kucun');
resolveRef(data.kucuns, 'bancais', 'bancais', 'bancai');
// 板材 ↔ 木皮 (多对一)
resolveRef(data.bancais, 'mupis', 'mupis', 'mupi1');
resolveRef(data.bancais, 'mupis', 'mupis', 'mupi2');
// 订单产品 ↔ 产品 (多对一)
resolveRef(data.dingdan_chanpins, 'chanpins', 'chanpins', 'chanpin');
// 订单组件 ↔ 组件 (多对一)
resolveRef(data.dingdan_chanpin_zujians, 'chanpin_zujians', 'chanpin_zujians', 'chanpin_zujian');
// 订单组件 ↔ 板材 (多对一)
resolveRef(data.dingdan_chanpin_zujians, 'bancais', 'bancais', 'bancai');
// 进货 ↔ 相关实体 (多对一)
['dingdans', 'chanpins', 'zujians', 'bancais', 'users'].forEach(entity => {
resolveRef(data.jinhuos, entity, entity, entity.slice(0, -1));
});
return data;
}
/**
* 注册回调函数
* @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.triggerCallbacks(operation, entity, data);
// 自动同步数据
this.syncData();
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 - 要发送的数据
* @returns {Promise<Object>} 响应结果
*/
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.triggerCallbacks(operation, entity, data);
// 自动同步数据
this.syncData();
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) 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('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});
}
}
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);
表格数据只有未知,但点开详情有数据
最新发布