file-type

Flex4 Button组件属性与事件详解

PPT文件

下载需积分: 9 | 3.63MB | 更新于2024-08-16 | 139 浏览量 | 1 下载量 举报 收藏
download 立即下载
本文档介绍了Flex4中的Button组件及其常用属性和事件,同时概述了Flex4组件的基本概念、分类和设置方法,包括属性、样式、事件和行为。 在Flex4中,Button组件是构建用户界面时非常关键的一个元素。它支持多种属性来定制其外观和行为: 1. **styleName**:此属性允许开发者指定组件应使用的CSS类,从而改变组件的样式和主题。 2. **visible**:控制组件是否可见,默认为true,设置为false时组件将不可见。 3. **enabled**:若设置为false,按钮将被禁用,显示为灰色,无法交互,默认为true。 4. **emphasized**:当设置为true时,会将emphasized样式添加到styleName,使按钮具有强调效果,默认为false。 Button组件还响应多种事件,这些事件对于处理用户交互至关重要: 1. **click**:当用户单击按钮时触发。 2. **mouseDown**:鼠标按钮在按钮上按下时触发。 3. **doubleClick**:双击按钮时触发。 4. **mouseMove**:鼠标在按钮上移动时触发。 5. **mouseOut**:鼠标移出按钮区域时触发。 6. **mouseOver**:鼠标移到按钮上时触发。 7. **mouseUp**:当用户在按钮上释放鼠标按钮时触发。 Flex4组件分为系统预定义组件和用户自定义组件,开发者可以利用这些组件快速构建UI。组件的设置包括属性、样式、事件和行为: - **属性**:定义组件的特性,如文本、位置、大小等。 - **样式**:组件的视觉特性,可通过CSS定义,如字体、颜色、大小和对齐方式。 - **事件**:响应组件的不同状态变化,如创建、用户交互等。 - **行为**:组件对用户操作的动态反应,如改变大小、响应鼠标动作。 添加组件有两种方式:通过MXML设计模式拖放组件,或者直接编写MXML代码。例如,添加一个TextInput组件,可以在MXML编辑器中拖放,或使用以下代码定义: ```xml <s:TextInput id="test" width="110" height="24" x="149" y="110" text="Flex程序"/> ``` 组件的大小可以设定为默认值,也可以明确指定宽度和高度,如: ```xml <s:Panel title="默认大小组件"> <s:TextInput id="txt" text="输入框组件"/> </s:Panel> ``` Flex4允许开发者灵活地控制组件的大小,可以通过设置具体数值或使用相对单位来适应不同的布局需求。 Flex4的Button组件结合其属性和事件,提供了强大的功能,使得在ActionScript3.0环境中创建交互式的用户界面变得简单高效。通过深入理解和熟练运用这些组件,开发者可以创建出丰富多样的Flex应用程序。

相关推荐

filetype

<template> <view class="template-login"> <view class="login"> <view class="login__bg login__bg--top"> <image class="bg" src="/https/wenku.csdn.net/static/login_top2.jpg" mode="widthFix"></image> </view> <view class="login__wrapper"> <view class="tn-margin-left tn-margin-right tn-text-bold tn-text-center tn-color-indigo" style="font-size: 100rpx;"> 安博源 </view><view class="login__info tn-flex tn-flex-direction-column tn-flex-col-center tn-flex-row-center"> <block v-if="currentModeIndex === 0"> <view class="login__info__item__input tn-flex tn-flex-direction-row tn-flex-nowrap tn-flex-col-center tn-flex-row-left"> <view class="login__info__item__input__left-icon"> <view class="tn-icon-phone"></view> </view> <view class="login__info__item__input__content"> <input v-model="phoneNumber" maxlength="20" placeholder-class="input-placeholder" placeholder="请输入手机号" /> </view> </view> <view class="login__info__item__input tn-flex tn-flex-direction-row tn-flex-nowrap tn-flex-col-center tn-flex-row-left"> <view class="login__info__item__input__left-icon"> <view class="tn-icon-safe"></view> </view> <view class="login__info__item__input__content login__info__item__input__content--verify-code"> <input v-model="verifyCode" placeholder-class="input-placeholder" placeholder="请输入验证码" /> </view> <view class="login__info__item__input__right-verify-code" @tap.stop="getCode"> <tn-button backgroundColor="#01BEFF" fontColor="#FFFFFF" size="sm" padding="5rpx 10rpx" width="100%" shape="round">{{ tips }}</tn-button> </view> </view> </block> <block v-if="currentModeIndex === 1"> <view class="login__info__item__input tn-flex tn-flex-direction-row tn-flex-nowrap tn-flex-col-center tn-flex-row-left"> <view class="login__info__item__input__left-icon"> <view class="tn-icon-phone"></view> </view> <view class="login__info__item__input__content"> <input v-model="phoneNumber" maxlength="20" placeholder-class="input-placeholder" placeholder="请输入手机号" /> </view> </view> <view class="login__info__item__input tn-flex tn-flex-direction-row tn-flex-nowrap tn-flex-col-center tn-flex-row-left"> <view class="login__info__item__input__left-icon"> <view class="tn-icon-safe"></view> </view> <view class="login__info__item__input__content login__info__item__input__content--verify-code"> <input v-model="verifyCode" placeholder-class="input-placeholder" placeholder="请输入验证码" /> </view> <view class="login__info__item__input__right-verify-code" @tap.stop="getCode"> <tn-button size="sm" backgroundColor="#01BEFF" fontColor="#FFFFFF" padding="5rpx 10rpx" width="100%" shape="round">{{ tips }}</tn-button> </view> </view> <view class="login__info__item__input tn-flex tn-flex-direction-row tn-flex-nowrap tn-flex-col-center tn-flex-row-left"> <view class="login__info__item__input__left-icon"> <view class="tn-icon-lock"></view> </view> <view class="login__info__item__input__content"> <input v-model="password" :password="!showPassword" placeholder-class="input-placeholder" placeholder="请输入登录密码" /> </view> <view class="login__info__item__input__right-icon" @click="showPassword = !showPassword"> <view :class="[showPassword ? 'tn-icon-eye' : 'tn-icon-eye-hide']"></view> </view> </view> </block>

filetype

以下app.vue依照原功能樣式增加一個醫案匯入器按鈕,頁面位置在./components/mrcrud.vue, app.vue: <template>
<button class="external-link"> 醫仙 </button> <button @click="setCurrentView('mrviewer')" :class="{ active: currentView === 'mrviewer' }"> 醫案閱讀器 </button> <button @click="setCurrentView('mreditor')" :class="{ active: currentView === 'mreditor' }"> 醫案編輯器 </button> <button @click="setCurrentView('dncrud')" :class="{ active: currentView === 'dncrud' }"> 病態匯入器 </button> <button @click="setCurrentView('mncrud')" :class="{ active: currentView === 'mncrud' }"> 草本匯入器 </button>
<keep-alive :include="cachedComponents"> <component :is="currentViewComponent" :key="currentView" /> </keep-alive>
</template> <script> import mrviewer from "./components/mrviewer.vue"; import mreditor from "./components/mreditor.vue"; import dncrud from "./components/dncrud.vue"; // 导入病态编辑器组件 import mncrud from "./components/mncrud.vue"; // 导入新增的草本编辑器组件 export default { components: { mrviewer, mreditor, dncrud, mncrud }, // 注册组件(新增mncrud) data() { return { currentView: "mrviewer", // 需要缓存的组件列表(新增mncrud) cachedComponents: ["mrviewer", "mreditor", "dncrud", "mncrud"] }; }, computed: { currentViewComponent() { return this.currentView; } }, methods: { // 设置当前视图并保存状态 setCurrentView(viewName) { // 保存当前组件状态(如果需要) if (this.currentViewComponent && this.currentViewComponent.beforeLeave) { this.currentViewComponent.beforeLeave(); } // 切换到新视图 this.currentView = viewName; // 保存当前视图到本地存储 localStorage.setItem("lastActiveView", viewName); }, // 医仙按钮功能已移除 // openExternalLink() {}, // 恢复上次活动视图 restoreLastView() { const lastView = localStorage.getItem("lastActiveView"); if (lastView && this.cachedComponents.includes(lastView)) { this.currentView = lastView; } } }, mounted() { // 组件挂载时恢复上次视图 this.restoreLastView(); } }; </script> <style scoped> .app-container { display: flex; flex-direction: column; min-height: 100vh; } .fixed-menu { position: fixed; top: 0; left: 0; width: 100%; background: #2c3e50; padding: 10px; display: flex; gap: 10px; box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); z-index: 100; } .fixed-menu button { padding: 8px 16px; border: none; border-radius: 4px; background: #3498db; color: white; cursor: pointer; transition: background 0.3s; } .fixed-menu button:hover { background: #2980b9; } .fixed-menu button.active { background: #e74c3c; font-weight: bold; } .fixed-menu button.external-link { background: #2ecc71; /* 绿色背景区分 */ cursor: default; /* 禁用鼠标指针变化 */ } .fixed-menu button.external-link:hover { background: #2ecc71; /* 保持原始颜色,不变化 */ } .content-wrapper { flex: 1; margin-top: 60px; /* 增加顶部间距避免内容被顶部菜单遮挡 */ padding: 20px; background: #f5f5f5; height: calc(100vh - 80px); /* 确保内容区域高度适配 */ overflow-y: auto; /* 添加滚动条 */ } </style>

filetype

------------------------ test.html ------------------------ <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>板材库存管理系统</title> <link rel="stylesheet" href="../js/bootstrap-5.3.0-alpha1-dist/css/bootstrap.min.css"> <link rel="stylesheet" href="../js/bootstrap-icons-1.8.1/bootstrap-icons.css"> <link rel="stylesheet" href="../css/test.css"> </head> <body>

板材库存管理系统

查询订单、产品、板材及库存信息

高级搜索
<input type="text" class="form-control with-icon" id="orderSearch" placeholder="搜索订单号..." aria-label="订单号搜索">
<input type="text" class="form-control with-icon" id="productSearch" placeholder="搜索产品编号..." aria-label="产品编号搜索">
<input type="text" class="form-control with-icon" id="materialSearch" placeholder="搜索板材ID或材质..." aria-label="板材搜索">
<input type="text" class="form-control with-icon" id="woodSearch" placeholder="搜索木皮名称..." aria-label="木皮搜索">
<input type="number" class="form-control with-icon" id="thicknessSearch" placeholder="厚度(mm)" min="0" step="0.1">
<input type="number" class="form-control" id="minStock" placeholder="最小库存" min="0"> <input type="number" class="form-control" id="maxStock" placeholder="最大库存" min="0"> <button class="btn btn-primary" type="button" id="stockStatusBtn"> </button>
<button class="btn btn-sm btn-outline-secondary" id="resetFilters"> 重置筛选 </button>
查询结果
0 条记录 数据更新时间: --:--:--
订单号 产品信息 产品数量 组件 板材 单件用量 订单用量 库存数量 操作
加载中...
正在加载数据,请稍候...

没有找到匹配的记录

请尝试调整您的搜索条件

板材详情
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">关闭</button>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> <script src="../js/bootstrap-5.3.0-alpha1-dist/js/bootstrap.bundle.min.js"></script> <script src="../js/dingdan.js"></script> </body> </html> ------------------------ DataManager.js ------------------------ //这个函数不许改,也禁止废话,属性名和其他命名都哼规范不会出现意外, function resolveDataReferences(data) { console.log(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); } } } } } return data; } /** * 数据管理器类,负责与后端API通信并管理数据 */ class DataManager { constructor(baseUrl) { this.baseUrl = baseUrl; this.data = { bancais: [], dingdans: [], mupis: [], chanpins: [], kucuns: [], dingdan_bancais:[], chanpin_zujians: [], zujians: [], caizhis: [], dingdan_chanpins: [], users: [], jinhuos: [] }; this.isSyncing = false; this.lastSync = null; this.callbacks = { all: [], bancais: [], dingdan: [], mupi: [], chanpin: [], kucun: [], chanpin_zujian: [], dingdan_bancai:[], zujian: [], caizhi: [], dingdan_chanpin: [], user: [], jinhuo: [] }; this.syncQueue = Promise.resolve(); this.registerCallback('dingdan_bancai', async (operation, data) => { if (operation === 'add' || operation === 'update') { try { // 构造进货记录数据 const jinhuoData = { dingdan_bancai:data, shuliang: data.shuliang, date: new Date().toISOString(), user: { id: localStorage.getItem("userId") } }; // 创建进货记录 await this.addEntity('jinhuo', jinhuoData); } catch (error) { console.error('进货记录创建失败:', error); } } }); } /** * 获取所有数据 * @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'); console.log(result.data) 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<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'); console.log(data)//这了就没有id了 // 自动同步数据 this.syncQueue = this.syncQueue.then(async () => { await this.syncData(); // 同步完成后触发操作回调 this.triggerCallbacks(operation, entity, result.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()); } /** * 获取订单的可用板材信息 * @param {number} dingdanId - 订单ID * @returns {Object} 可用板材信息 */ getAvailableBancaisForOrder(dingdanId) { const dingdan = this.data.dingdans.find(d => d.id == dingdanId); if (!dingdan) return {}; return dingdan.availableBancais || {}; } /** * 获取产品的组件列表 * @param {number} chanpinId - 产品ID * @returns {Array} 组件列表 */ getZujiansForChanpin(chanpinId) { const chanpin = this.data.chanpins.find(c => c.id == chanpinId); if (!chanpin) return []; return (chanpin.chanpin_zujian_list || []) .map(cz => cz.zujian) .filter(z => z); } /** * 获取组件的板材信息 * @param {number} zujianId - 组件ID * @returns {Array} 板材列表 */ getBancaisForZujian(zujianId) { return (this.data.chanpin_zujians || []) .filter(cz => cz.zujian && cz.zujian.id == zujianId) .map(cz => cz.bancai) .filter(b => b); } /** * 创建进货记录 * @param {Object} jinhuoData - 进货数据 * @returns {Promise} 操作结果 */ async createJinhuo(jinhuoData) { return this.addEntity('jinhuo', jinhuoData); } /** * 获取订单列表 * @returns {Array} 订单列表 */ getDingdans() { return this.data.dingdans || []; } /** * 获取订单的产品列表 * @param {number} dingdanId - 订单ID * @returns {Array} 产品列表 */ getChanpinsForDingdan(dingdanId) { const dingdan = this.data.dingdans.find(d => d.id == dingdanId); if (!dingdan) return []; return (dingdan.dingdan_chanpin_list || []) .map(dc => dc.chanpin) .filter(c => c); } } 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); ------------------------ test.js ------------------------ // 创建DataManager实例 // 获取全局的DataManager实例 const dataManager = window.parent.dataManager; // 扁平化数据结构 function flattenData(data) { const result = []; // 遍历所有订单 data.dingdans.forEach(dingdan => { // 遍历订单中的产品 dingdan.dingdan_chanpin?.forEach(dc => { const chanpin = dc.chanpin; // 遍历产品中的组件 chanpin.chanpin_zujian.forEach(cz => { const zujian = cz.zujian; const bancai = cz.bancai; const kucun = bancai.kucun; // 计算订单用量 = 产品数量 × 组件数量 × 单件用量 const orderUsage = dc.shuliang * cz.zujianshu * cz.one_howmany; // 创建扁平化数据对象 result.push({ dingdan: dingdan, dingdan_chanpin: dc, chanpin: chanpin, chanpin_zujian: cz, zujian: zujian, bancai: bancai, kucun: kucun, orderUsage: orderUsage }); }); }); }); return result; } // 更新统计卡片 function updateStatistics(data) { // 订单总数 $('#orderCount').text(data.dingdans.length); // 产品种类 $('#productCount').text(data.chanpins.length); // 板材种类 $('#materialCount').text(data.bancais.length); // 库存总量 const totalStock = data.kucuns.reduce((sum, kucun) => sum + kucun.shuliang, 0); $('#totalStock').text(totalStock); } // 渲染表格 function renderTable(dataArray) { const $tbody = $('#resultBody'); $tbody.empty(); if (dataArray.length === 0) { $('#noResults').show(); $('#resultCount').text('0'); return; } $('#noResults').hide(); $('#resultCount').text(dataArray.length); dataArray.forEach(item => { const bancai = item.bancai; const dingdan = item.dingdan; const chanpin = item.chanpin; const zujian = item.zujian; const kucun = item.kucun; const row = ` ${dingdan.number} ${chanpin.bianhao} ${item.dingdan_chanpin.shuliang} ${zujian.name}
ID: ${bancai.id}
材质: ${bancai.caizhi.name}
厚度: ${bancai.houdu}mm
木皮1: ${bancai.mupi1.name}${bancai.mupi1.you ? '(油漆)' : ''}
木皮2: ${bancai.mupi2.name}${bancai.mupi2.you ? '(油漆)' : ''}
${item.chanpin_zujian.one_howmany} ${item.orderUsage.toFixed(2)} ${kucun.shuliang} <button class="btn btn-sm btn-outline-primary view-detail" data-bancai-id="${bancai.id}"> 详情 </button> `; $tbody.append(row); }); // 绑定详情按钮事件 $('.view-detail').off('click').on('click', function() { const bancaiId = $(this).data('bancai-id'); viewBancaiDetail(bancaiId); }); } // 查看板材详情 function viewBancaiDetail(bancaiId) { const bancai = dataManager.data.bancais.find(b => b.id === bancaiId); if (!bancai) return; const kucun = bancai.kucun; const reservedOrder = kucun.reservedOrder ? kucun.reservedOrder.number : '无'; // 构建详情内容 const detailContent = `
板材详情 #${bancai.id}
材质: ${bancai.caizhi.name}
厚度: ${bancai.houdu}mm
木皮1: ${bancai.mupi1.name}${bancai.mupi1.you ? ' (油漆)' : ''}
木皮2: ${bancai.mupi2.name}${bancai.mupi2.you ? ' (油漆)' : ''}
当前库存: ${kucun.shuliang}
预定订单: ${reservedOrder}
库存状态: ${kucun.shuliang > 50 ? '充足' : kucun.shuliang > 10 ? '正常' : '不足'}
使用此板材的产品组件
    ${dataManager.data.chanpin_zujians .filter(cz => cz.bancai.id === bancaiId) .map(cz => `
  • ${cz.zujian.name} (产品: ${cz.chanpin.bianhao})
  • `) .join('')}
`; // 使用模态框显示详情 const modal = new bootstrap.Modal(document.getElementById('detailModal')); $('#detailModal .modal-body').html(detailContent); modal.show(); } // 搜索和过滤数据 function filterData(flatData) { const orderSearch = $('#orderSearch').val().toLowerCase(); const productSearch = $('#productSearch').val().toLowerCase(); const materialSearch = $('#materialSearch').val().toLowerCase(); const woodSearch = $('#woodSearch').val().toLowerCase(); const thickness = parseFloat($('#thicknessSearch').val()); const minStock = parseInt($('#minStock').val()) || 0; const maxStock = parseInt($('#maxStock').val()) || Infinity; return flatData.filter(item => { const bancai = item.bancai; const dingdan = item.dingdan; const chanpin = item.chanpin; const kucun = item.kucun; // 订单号搜索 if (orderSearch && !dingdan.number.toLowerCase().includes(orderSearch)) { return false; } // 产品编号搜索 if (productSearch && !chanpin.bianhao.toLowerCase().includes(productSearch)) { return false; } // 板材ID或材质搜索 if (materialSearch && !(bancai.id.toString().includes(materialSearch) || bancai.caizhi.name.toLowerCase().includes(materialSearch))) { return false; } // 木皮搜索 if (woodSearch && !(bancai.mupi1.name.toLowerCase().includes(woodSearch) || bancai.mupi2.name.toLowerCase().includes(woodSearch))) { return false; } // 厚度过滤 if (!isNaN(thickness) && Math.abs(bancai.houdu - thickness) > 0.1) { return false; } // 库存范围过滤 if (kucun.shuliang < minStock || kucun.shuliang > maxStock) { return false; } return true; }); } // 初始化排序功能 function initSorting() { let currentSort = { column: null, direction: 'asc' }; $('th[data-sortable]').on('click', function() { const column = $(this).index(); const $sortIndicator = $(this).find('.sort-indicator'); // 重置其他列的排序指示器 $('.sort-indicator').html(''); // 更新当前排序状态 if (currentSort.column === column) { currentSort.direction = currentSort.direction === 'asc' ? 'desc' : 'asc'; } else { currentSort.column = column; currentSort.direction = 'asc'; } // 更新排序指示器 $sortIndicator.html(currentSort.direction === 'asc' ? '↑' : '↓'); // 执行排序 sortTable(currentSort); }); } // 排序表格数据 function sortTable(sortConfig) { const flatData = window.currentFlatData || []; if (flatData.length === 0) return; flatData.sort((a, b) => { let valueA, valueB; switch (sortConfig.column) { case 0: // 订单号 valueA = a.dingdan.number; valueB = b.dingdan.number; break; case 1: // 产品信息 valueA = a.chanpin.bianhao; valueB = b.chanpin.bianhao; break; case 2: // 产品数量 valueA = a.dingdan_chanpin.shuliang; valueB = b.dingdan_chanpin.shuliang; break; case 3: // 组件 valueA = a.zujian.name; valueB = b.zujian.name; break; case 4: // 板材 valueA = a.bancai.id; valueB = b.bancai.id; break; case 5: // 单件用量 valueA = a.chanpin_zujian.one_howmany; valueB = b.chanpin_zujian.one_howmany; break; case 6: // 订单用量 valueA = a.orderUsage; valueB = b.orderUsage; break; case 7: // 库存数量 valueA = a.kucun.shuliang; valueB = b.kucun.shuliang; break; default: return 0; } // 数字排序 if (typeof valueA === 'number') { return sortConfig.direction === 'asc' ? valueA - valueB : valueB - valueA; } // 字符串排序 if (typeof valueA === 'string') { return sortConfig.direction === 'asc' ? valueA.localeCompare(valueB) : valueB.localeCompare(valueA); } return 0; }); // 重新渲染排序后的表格 renderTable(flatData); } // 初始化页面 $(document).ready(async function() { // 创建详情模态框(如果不存在) if (!$('#detailModal').length) { $('body').append(`
板材详情
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">关闭</button>
`); } // 显示加载状态 $('#loadingRow').show(); $('#noResults').hide(); try { // 加载数据 await dataManager.fetchAll(); // 更新最后更新时间 $('#lastUpdate').text(new Date().toLocaleTimeString()); // 扁平化数据 const flatData = flattenData(dataManager.data); window.currentFlatData = flatData; // 保存当前数据用于排序 // 更新统计卡片 updateStatistics(dataManager.data); // 渲染表格 renderTable(flatData); // 初始化排序 initSorting(); } catch (error) { console.error('初始化失败:', error); $('#resultBody').html(`

数据加载失败

${error.message || '请检查网络连接后重试'}

<button class="btn btn-primary mt-2" id="retryBtn">重试</button> `); $('#retryBtn').on('click', function() { location.reload(); }); } finally { $('#loadingRow').hide(); } // 绑定搜索事件 $('#orderSearch, #productSearch, #materialSearch, #woodSearch, #thicknessSearch, #minStock, #maxStock').on('input', function() { performSearch(); }); $('#stockStatusBtn').on('click', performSearch); }); // 执行搜索 function performSearch() { if (!window.currentFlatData) return; const filteredData = filterData(window.currentFlatData); renderTable(filteredData); } 修改test.js实现页面功能 基本逻辑 一个订单有很多产品不同数量,每个产品可以再很多订单中,一个产品有很多组件,一个组件可以在很多产品中,因为每个组件因为在不同的产品中有不同的生产工艺,所以使用不同的板材和板材能生产组件数量,每个板材有不同的材质和两面木皮,木皮表面可能有油漆, 订购时可能直接购入板材,也可能按订单和产品订购板材,也用可能按订单产品组件订购板材,每次采购不标准,一个订单可能订购几次,用户有姓名 账号 密码 权限, 一个记录进货和消耗,查看的时候会查看订单下有多少板材可用
filetype

/* 文件路径: webapp/components/custom-form-modal/index.js */ // components/custom-form-modal/index.js Component({ properties: { show: { type: Boolean, value: false }, // 是否显示模态框 title: { type: String, value: '表单标题' }, // 模态框标题 config: { type: Array, value: [] }, // 表单配置数组 formData: { type: Object, value: {} } // 表单数据 }, data: { // 用于存储临时数据(如下拉选择器的选中值) tempData: {} }, methods: { // 关闭模态框 closeModal() { this.triggerEvent('close'); }, // 表单提交 submitForm() { this.triggerEvent('submit', this.data.formData); }, // 输入框变化 handleInput(e) { const { field } = e.currentTarget.dataset; const value = e.detail; this.setData({ [`formData.${field}`]: value }); this.triggerEvent('change', { field, value }); }, // 时间选择器变化 handleTimeChange(e) { const { field } = e.currentTarget.dataset; const value = e.detail; this.setData({ [`formData.${field}`]: value, [`tempData.${field}_show`]: false // 关闭时间选择器 }); this.triggerEvent('change', { field, value }); }, // 下拉选择器变化 handleSelectChange(e) { const { field } = e.currentTarget.dataset; const value = e.detail; this.setData({ [`formData.${field}`]: value, [`tempData.${field}_show`]: false // 关闭选择器 }); this.triggerEvent('change', { field, value }); }, // 打开时间选择器 openTimePicker(e) { const { field } = e.currentTarget.dataset; this.setData({ [`tempData.${field}_show`]: true }); }, // 打开下拉选择器 openSelectPicker(e) { const { field } = e.currentTarget.dataset; this.setData({ [`tempData.${field}_show`]: true }); }, // 按钮点击事件 handleButtonClick(e) { const { field } = e.currentTarget.dataset; this.triggerEvent('buttonClick', { field }); }, // 自定义组件事件 handleCustomEvent(e) { const { field, event } = e.detail; this.triggerEvent('customEvent', { field, ...event }); } } }); ================================================================================ /* 文件路径: webapp/components/custom-form-modal/index.json */ { "component": true, "usingComponents": { "van-field": "@vant/weapp/field/index", "van-picker": "@vant/weapp/picker/index", "van-datetime-picker": "@vant/weapp/datetime-picker/index", "van-button": "@vant/weapp/button/index", "van-popup": "@vant/weapp/popup/index" } } ================================================================================ /* 文件路径: webapp/components/custom-form-modal/index.wxml */ <van-popup show="{{ show }}" position="bottom" custom-class="custom-form-modal" close-on-click-overlay="{{ false }}" bind:close="closeModal" > <view class="modal-header"> <view class="title">{{ title }}</view> <van-icon name="close" size="20px" bind:tap="closeModal" /> </view> <scroll-view scroll-y class="form-content"> <block wx:for="{{ config }}" wx:key="index"> <view class="form-row"> <view class="row-title">{{ item.title }}</view> <view class="row-content"> <block wx:for="{{ item.items }}" wx:key="field"> <block wx:if="{{ subItem.type === 'input' }}"> <van-field value="{{ formData[subItem.field] }}" placeholder="{{ subItem.placeholder || '请输入' }}" data-field="{{ subItem.field }}" bind:change="handleInput" /> </block> <block wx:elif="{{ subItem.type === 'time' }}"> <view class="time-picker" bindtap="openTimePicker" data-field="{{ subItem.field }}"> {{ formData[subItem.field] || subItem.placeholder || '选择时间' }} </view> <van-popup show="{{ tempData[subItem.field + '_show'] }}" position="bottom" bind:close="closeTimePicker" > <van-datetime-picker type="time" value="{{ formData[subItem.field] }}" data-field="{{ subItem.field }}" bind:confirm="handleTimeChange" bind:cancel="closeTimePicker" /> </van-popup> </block> <block wx:elif="{{ subItem.type === 'select' }}"> <view class="select-picker" bindtap="openSelectPicker" data-field="{{ subItem.field }}"> {{ getSelectLabel(subItem.options, formData[subItem.field]) || subItem.placeholder || '请选择' }} </view> <van-popup show="{{ tempData[subItem.field + '_show'] }}" position="bottom" bind:close="closeSelectPicker" > <van-picker show-toolbar columns="{{ subItem.options }}" value-key="label" data-field="{{ subItem.field }}" bind:confirm="handleSelectChange" bind:cancel="closeSelectPicker" /> </van-popup> </block> <block wx:elif="{{ subItem.type === 'button' }}"> <van-button type="primary" size="small" data-field="{{ subItem.field }}" bind:tap="handleButtonClick" > {{ subItem.text }} </van-button> </block> <block wx:elif="{{ subItem.type === 'custom' }}"> <custom-component value="{{ formData[subItem.field] }}" config="{{ subItem.config }}" data-field="{{ subItem.field }}" bind:event="handleCustomEvent" /> </block> </block> </view> </view> </block> </scroll-view> <view class="modal-footer"> <van-button type="default" size="large" bind:tap="closeModal">取消</van-button> <van-button type="primary" size="large" bind:tap="submitForm">提交</van-button> </view> </van-popup> ================================================================================ /* 文件路径: webapp/components/custom-form-modal/index.wxss */ /* components/custom-form-modal/index.wxss */ .custom-form-modal { height: 80vh; border-top-left-radius: 16rpx; border-top-right-radius: 16rpx; display: flex; flex-direction: column; } .modal-header { display: flex; justify-content: space-between; align-items: center; padding: 24rpx 32rpx; border-bottom: 1rpx solid #eee; } .modal-header .title { font-size: 36rpx; font-weight: bold; } .form-content { flex: 1; padding: 0 32rpx; } .form-row { padding: 24rpx 0; border-bottom: 1rpx solid #f5f5f5; } .row-title { font-size: 28rpx; color: #333; margin-bottom: 16rpx; } .row-content { display: flex; flex-wrap: wrap; gap: 20rpx; } .row-content > view { flex: 1; min-width: 45%; } .time-picker, .select-picker { padding: 20rpx; border: 1rpx solid #ebedf0; border-radius: 8rpx; font-size: 28rpx; } .modal-footer { display: flex; padding: 24rpx 32rpx; gap: 20rpx; } .modal-footer van-button { flex: 1; } 利用components\custom-form-modal中的拟态框通用组件来创建一个单独文件夹,里面存放一系列json存贮这拟态框注册信息和内部逻辑,所有的拟态框都是提前注册过的,页面只需要传输一个数据然后接受返回数据的模式调用,包括数据调取数据保存,页面只传递几个特定数据,关闭时返回处理过的数据,该文件吧所有的关于拟态框的操作都封装好了,并和页面彻底解耦,拟态框中就是一个完整的功能,跟一个页面相似,页面也不需要保存逻辑,保存逻辑直接再注册拟态框时写入到配置中,页面只需要一个保存好的数据结果,页面跟调用函数一样调用,传递参数获取反回值,判断返回值做相应操作。

filetype

接下来我将提供两个Vue组件的代码,第一个是父组件postDetail.vue 第二个是子组件FeishuShare.vue。我尝试将父组件的post[]属性传递给子组件中并保存为sharedPost,请问为什么传递的是空值(父组件的post不是空值) 第一段代码 <template>

{{ post.title }}

<FeishuShare /> {{ post.createDate }} 作者: {{ post.author ? post.author.username : '匿名' }}
{{ post.summary }}
<el-tag v-for="tag in post.tags.split(/[; ;]/).map((tag) => tag.trim())" :key="tag" style="margin-right: 10px; margin-bottom: 5px; white-space: normal; height: auto;" >{{ tag }}</el-tag>
<Editor v-model="post.content" style="height: 500px; overflow-y: hidden;" :default-config="editorConfig" :mode="mode" @onCreated="onCreated" />
暂无评论
<el-avatar :size="40" icon="el-icon-user-solid" alt="评论者头像" :src="comment.isAnonymous? null : comment.author.avatar" class="comment-avatar" /> {{ comment.isAnonymous? '匿名' : comment.author.username }}
发表于:{{ comment.updateTime }} <el-button type="text" size="mini" class="reply-btn" @click="handleReply(comment)" >回复</el-button>
正在回复此评论
回复 @{{ comment.parentComment.isAnonymous ? '匿名用户' : comment.parentComment.author.username }}

回复 @{{ getReplyToUsername(comment) }}:

<pagination v-show="total > 0" :total="total" :page.sync="listQuery.page" :limit.sync="listQuery.size" @pagination="fetchPostComments" />
<el-form ref="commentForm" :model="newComment" >

发表评论

正在回复 @{{ replyingToUsername }} <el-button type="text" size="mini" @click="cancelReply">取消回复</el-button>

<el-form-item class="comments-content">
<Toolbar style="border-bottom: 1px solid #ccc" :editor="editor" :default-config="toolbarConfig2" :mode="mode" /> <Editor v-model="newComment.content" style="height: 500px; overflow-y: hidden;" :default-config="editorConfig2" :mode="mode" @onCreated="onCreated" />
</el-form-item> <el-form-item label="匿名"> <el-switch v-model="newComment.isAnonymous" active-text="是" inactive-text="否" /> </el-form-item> <el-form-item> <el-button type="primary" @click="submitComment">提交评论</el-button> </el-form-item> </el-form>
</template> <script> import { Editor, Toolbar } from '@wangeditor/editor-for-vue' import waves from '@/directive/waves' // waves directive import Pagination from '@/components/Pagination' import store from '@/store' import { addComment, getPostComments, getPostDetail } from '@/api/post' import { getToken } from '@/utils/auth' import FeishuShare from './FeishuShare.vue' export default { components: { Pagination, Editor, Toolbar ,FeishuShare}, // 注册组件 directives: { waves }, data() { var config = { placeholder: '请输入内容....', MENU_CONF: {} } config = this.setConfig(config) return { editor: null, toolbarConfig2: {}, editorConfig: { placeholder: '', readOnly: true }, editorConfig2: config, mode: 'default', // or 'simple' // 以上为富文本编辑器参数 postId: 0, post: { id: 0, title: '', content: '', author: '', createDate: '', updateDate: '', userId: 0, avatar: '', tags: '' }, comments: [], replyTo: null, // 当前回复的评论ID replyingToUsername: '', // 正在回复的用户名 newComment: { id: 0, isAnonymous: false, content: '', parentCommentId: null }, total: 0, listLoading: true, listQuery: { page: 1, size: 10 }, userInfoMap: {} // editorConfig: { // height: 150, // showUpload: false, // menubar: '', // toolbar: [ // 'undo redo forecolor backcolor bold italic underline strikethrough alignleft aligncenter alignright' // ] // } } }, created() { this.postId = Number(this.$route.params.id) if (!this.postId || isNaN(this.postId) || this.postId <= 0) { this.$message.error('帖子ID无效') this.$router.push('/') return } this.newComment.id = this.postId this.fetchPostDetails() this.fetchPostComments() }, methods: { setConfig(editorConfig) { var temp if (store.getters.token) { temp = getToken() } editorConfig.MENU_CONF['uploadImage'] = { server: '/api/post/upload/image', maxFileSize: 10 * 1024 * 1024, headers: { 'X-Token': temp } } editorConfig.MENU_CONF['uploadVideo'] = { server: '/api/post/upload/video', maxFileSize: 100 * 1024 * 1024, headers: { 'X-Token': temp } } editorConfig.MENU_CONF['codeSelectLang'] = { codeLangs: [ { text: 'CSS', value: 'css' }, { text: 'HTML', value: 'html' }, { text: 'XML', value: 'xml' } ] } return editorConfig }, onCreated(editor) { this.editor = Object.seal(editor) // 一定要用 Object.seal() ,否则会报错 }, beforeDestroy() { const editor = this.editor if (editor == null) return editor.destroy() // 组件销毁时,及时销毁编辑器 }, fetchPostDetails() { getPostDetail(this.postId) .then((response) => { this.post = response.data if (!this.post.summary) { this.post.summary = '无概要信息' } this.fetchPostComments() }) .catch((error) => { console.error('获取帖子详情失败:', error) this.$message.error('获取帖子详情失败') }) }, getReplyToUsername(comment) { const parentComment = this.comments.find(c => c.id === comment.parentCommentId) return parentComment ? (parentComment.isAnonymous ? '匿名用户' : parentComment.author.username) : '' }, fetchPostComments() { const data = { id: this.postId, page: this.listQuery.page, size: this.listQuery.size } getPostComments(data) .then((response) => { this.comments = response.data.content this.total = response.data.totalElements this.listLoading = false setTimeout(() => { this.listLoading = false }, 1.5 * 1000) }) }, handleReply(comment) { this.replyTo = comment.id this.replyingToUsername = comment.isAnonymous ? '匿名用户' : comment.author.username this.newComment.parentCommentId = comment.id this.$nextTick(() => { this.$refs.commentForm.$el.querySelector('textarea').focus() }) }, cancelReply() { this.replyTo = null this.replyingToUsername = '' this.newComment.parentCommentId = null }, submitComment() { this.newComment.id = this.postId if (!this.newComment.content.trim()) { this.$message.error('评论内容不能为空') return } addComment(this.newComment) .then((response) => { this.$message.success(this.replyTo ? '回复成功' : '评论成功') this.newComment.content = '' this.newComment.parentCommentId = null this.replyTo = null this.replyingToUsername = '' this.fetchPostComments() }) .catch((error) => { this.$message.error('发布评论失败,请稍后重试') console.error(error) }) } } } </script> <style lang="scss" scoped> .detail-container { padding:20px; background-color: #f7f8fa; min-height: inherit; .detail { background-color: white; padding: 20px; border-radius: 4px; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); .tags-container { margin: 10px 0; padding: 10px 0; border-top: 1px solid #eee; border-bottom: 1px solid #eee; display: flex; flex-wrap: wrap; align-items: center; } .title-container { display: flex; flex-direction: column; align-items: center; justify-content: center; margin-bottom: 20px; .title { color: #3e9ece; margin-bottom: 10px; text-align: center; word-break: break-word; } .date { font-size: 12px; text-align: center; line-height: 20px; color: #999; } .author { font-size: 14px; color: black; } } .summary-container{ border: #ebebeb 1px solid; background: #f8f8f8; font-size: 12px; margin: 20px 0 10px; padding: 10px; line-height: 20px; text-indent: 2em; border-radius: 4px; .summary { color: #003048; } } .content-container { margin: 20px 0; padding-bottom: 20px; .content { max-height: none; overflow-y: auto; word-break: break-word; line-height: 1.6; } } } .comment { .reply-to { color: #67c23a; font-weight: bold; margin-right: 5px; } .replying-indicator { color: #67c23a; font-size: 12px; margin: 5px 0; padding: 3px 8px; background-color: #f0f9eb; border-radius: 4px; display: inline-block; } background: white; border: 1px solid #ebeef5; border-radius: 4px; padding: 20px; margin-top: 20px; .comment-list-container { overflow-y: auto; .no-comments { text-align: center; color: #999; padding: 20px; } .comment-item { margin-bottom: 15px; border-bottom: 1px solid #ebeef5; padding-bottom: 15px; &:last-child { border-bottom: none; padding-bottom: 0; } .comment-header { display: flex; align-items: flex-start; justify-content: space-between; } .comment-header-left { display: flex; flex-direction: column; align-items: center; justify-content: flex-start; flex-grow: 1; gap: 10px; min-width: 80px; } .comment-header-right { display: flex; flex-direction: column; align-items: flex-start; flex-grow: 20; margin-left: 20px; } .comment-avatar { width: 40px; height: 40px; border-radius: 50%; } .comment-author, .comment-date { font-size: 12px; color: #999; } .reply-to { font-size: 12px; color: #666; margin: 5px 0; .reply-to-user { color: #409EFF; font-weight: bold; } } .comment-content { margin-top: 5px; word-break: break-word; line-height: 1.6; } } } } .comment-form { margin-top: 20px; padding: 20px; background: white; border-radius: 4px; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); .replying-to { color: #409EFF; font-weight: bold; margin: 0 5px; } } } </style> <style src="@wangeditor/editor/dist/css/style.css"></style> 第二段代码 <template>
<el-button class="share-btn" type="primary" size="small" icon="el-icon-share" @click="dialogVisible=true;fetchAllUsers()" > shareToYesv </el-button> <el-dialog title="分享" :visible.sync="dialogVisible" width="30%" :before-close="handleClose">
<el-select v-model="value" filterable placeholder="请输入姓名/群组名"> <el-option v-for="item in options" :key="item.uid" :label="item.username" :value="item.uid"> </el-option> </el-select>
<el-button @click="dialogVisible = false">取 消</el-button> <el-button type="primary" @click="dialogVisible = false,handleShare()">确 定</el-button> </el-dialog>
</template> <script> import Axios from 'axios'; import { findAllUsers } from '@/api/user'; import { options } from 'dropzone'; import { sharePost } from '@/api/post'; import PostDetail from './postDetail.vue'; export default { props:['postInfo'], data() { return { dialogVisible: false, options:[], value: '', sharedUserId:'', sharedPost:this.$parent.post, }; }, methods: { handleClose(done) { this.$confirm('确认关闭?') .then(_ => { done(); }) .catch(_ => {}); }, async handleShare(){ try{ if(this.value=='') { console.log('please select'); } console.log(this.sharedPost); this.sharedUserId=this.value; const response=await sharePost(sharedPost,sharedUserId) }catch(error) { console.log('分享失败'); } }, async fetchAllUsers(){ try{ const response=await findAllUsers(); //List<User> fetchResult; //fetchResult=response.data; this.options=response.data; //删除admin this.options=this.options.filter(item=>item.username!='admin'); }catch(error){ console.error('获取分享列表失败'); } }, mounted:{ } } }; </script> <style> .feishu-share-btn { min-width: 100%; height: 40px; position: relative; display:flex; justify-content:flex-end; } .share-btn { position: relative; right: 10px; top: 10px; } </style>

filetype

<script setup lang="ts"> import { userService } from '@/api/service/user' import avatar from '@/assets/images/avatar.webp' import { useAppStore, useMenuStore } from '@/store' import { User } from '@element-plus/icons-vue' import { useRouter } from 'vue-router' import apps from '~icons/custom/apps' import exit from '~icons/custom/exit' import unlock from '~icons/custom/unlock' import ChangePasswordDialog from './change-password/index.vue' import UserInfo from './user-info/index.vue' import VOYAHlogo from '/Vhome.svg' defineOptions({ name: 'LayoutHeader', }) // 修改密码的窗口是否可见 const changePasswordDialogVisible = ref(false) // 个人信息的窗口是否可见 const checkUserInfoVisible = ref(false) const appStore = useAppStore() const { isEnglish: _isEnglish, } = storeToRefs(appStore) const router = useRouter() const userInfoRef = ref() function redirectGlobal() { router.push({ name: 'Dashboard' }) } async function handleCommand(command: string | number | object) { switch (command) { case 'logout': { try { await ElMessageBox.confirm( '确认要注销吗?', '提示', { confirmButtonText: '确认', cancelButtonText: '取消', }, ) await userService.logout() localStorage.setItem('token', '') window.$message.success('注销成功') router.push({ name: 'Login' }) } catch (error) { console.error(error) window.$message?.error('注销失败,请重试') } break } case 'modifyPwd': { changePasswordDialogVisible.value = true break } case 'userInfo': { checkUserInfoVisible.value = true userInfoRef.value.open() break } default: break } } const userInfo = ref<any>({ avatar: '', username: '', roleName: '', mobile: '', roleNameList: [], source: '', email: '', }) const menuStore = useMenuStore() const { filterMenu } = menuStore onActivated(async () => { getUserInfoFn() }) onMounted(() => { getUserInfoFn() }) async function getUserInfoFn() { const { data } = await userService.getUserInfo() userInfo.value = data filterMenu(userInfo.value.roleIdList) } </script> <template>
<el-image class="wh-8" :src="VOYAHlogo" fit="scale-down" @click="redirectGlobal" /> {{ $t('system.title') }} <el-divider direction="vertical" style="height: 22px; margin:0;" /> <el-button text circle> <el-icon size="20"> <apps /> </el-icon> </el-button>
<el-popover popper-class="user-center w-50" popper-style="width: 200px; padding: 0;" placement="bottom" :show-arrow="false" > <template #reference> <el-avatar :src="userInfo.avatar" :size="40" class="cursor-pointer" /> </template> <template #default>
<el-avatar :src="userInfo.avatar" :size="40" class="cursor-pointer" /> {{ userInfo.username }} {{ userInfo.roleName }}
<el-button class="operation-btn" link @click="handleCommand('userInfo')"> <el-icon :size="20" class="mr-2"> <User /> </el-icon>个人信息 </el-button> <el-button class="operation-btn" link @click="handleCommand('modifyPwd')"> <el-icon :size="20" class="mr-2"> <unlock /> </el-icon>修改密码 </el-button> <el-divider class="operation-divider" /> <el-button class="operation-btn" type="danger" link @click="handleCommand('logout')"> <el-icon :size="20" class="mr-2"> <exit /> </el-icon>退出系统 </el-button>
</template> </el-popover>
<ChangePasswordDialog v-model="changePasswordDialogVisible" /> <UserInfo ref="userInfoRef" v-model="checkUserInfoVisible" v-model:user-info="userInfo" @confirm-ok="getUserInfoFn" />
</template> <style lang="scss" scoped> .header-title { font-family: Alimama ShuHeiTi; } .header-right-content-wrapper { .el-button + .el-button { margin-left: 0; } } </style> <style> .el-popper.el-popover.user-center { --el-popover-border-radius: 8px; background: url('@/assets/images/header/user-center-bg.webp'); background-position: center; background-size: auto 140%; background-repeat: no-repeat; border: none; box-shadow: 0px 8px 20px 0px rgba(31, 39, 51, 0.09); .operation { .operation-btn { height: 32px; margin-left: 0; } .operation-divider { margin: 12px 0; } } } </style> 为什么我修改完之后userInfo.avatar没有响应式更新啊?

filetype

------------------------ Bancai.java ------------------------ package com.kucun.data.entity; import java.lang.annotation.Annotation; import javax.persistence.CascadeType; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.OneToOne; import javax.persistence.Table; import com.kucun.data.entity.DTO.*; import com.fasterxml.jackson.annotation.JsonBackReference; import com.fasterxml.jackson.annotation.JsonManagedReference; import com.fasterxml.jackson.databind.annotation.JsonSerialize; /** 板材 @author Administrator */ @Entity @Table(name=“bancai”) @JsonSerialize(using = FullEntitySerializer.class) @UniqueEntity( repositoryName = “bancai”, fields = {“houdu”, “caizhi”, “mupi1”, “mupi2”}, message = “板材组合已存在” ) public class Bancai extends EntityBasis { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; @ManyToOne( fetch = FetchType.LAZY) @JoinColumn(name = “caizhi_id”) // private Caizhi caizhi; @ManyToOne( fetch = FetchType.LAZY) @JoinColumn(name = “mupi1_id”) private Mupi mupi1; @ManyToOne( fetch = FetchType.LAZY) @JoinColumn(name = “mupi2_id”) private Mupi mupi2; private Double houdu; @OneToOne( cascade = CascadeType.ALL, orphanRemoval = true, // 添加此配置 fetch = FetchType.LAZY ) @JoinColumn(name = “kucun_id”, referencedColumnName = “id”) private Kucun kucun; public Kucun getKucun() { return kucun; } public void setKucun(Kucun kucun) { this.kucun = kucun; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public Caizhi getCaizhi() { return caizhi; } public void setCaizhi(Caizhi caizhi) { this.caizhi = caizhi; } public Mupi getMupi1() { return mupi1; } public void setMupi1(Mupi mupi1) { this.mupi1 = mupi1; } public Mupi getMupi2() { return mupi2; } public void setMupi2(Mupi mupi2) { this.mupi2 = mupi2; } public Double getHoudu() { return houdu; } public void setHoudu(Double houdu) { this.houdu = houdu; } public Bancai(int id, Caizhi caizhi, Mupi mupi1, Mupi mupi2, Double houdu) { super(); this.id = id; this.caizhi = caizhi; this.mupi1 = mupi1; this.mupi2 = mupi2; this.houdu = houdu; } public Bancai() { super(); } } ------------------------ Caizhi.java ------------------------ package com.kucun.data.entity; import java.util.List; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.OneToMany; import javax.persistence.Table; import javax.persistence.UniqueConstraint; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.kucun.data.entity.DTO.FullEntitySerializer; import com.kucun.data.entity.DTO.UniqueEntity; /** 板材材质 @author Administrator */ @Entity @Table(name=“caizhi”, uniqueConstraints = { @UniqueConstraint(columnNames = “name”) }) @UniqueEntity( repositoryName = “caizhi”, fields = {“name”}, message = “材质已存在” ) @JsonSerialize(using = FullEntitySerializer.class) public class Caizhi extends EntityBasis{ @OneToMany(mappedBy="caizhi") private List<Bancai> bancai; private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public List<Bancai> getBancai() { return bancai; } public void setBancai(List<Bancai> bancai) { this.bancai = bancai; } } ------------------------ Chanpin.java ------------------------ package com.kucun.data.entity; import java.util.List; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.JoinTable; import javax.persistence.OneToMany; import javax.persistence.Table; import javax.persistence.UniqueConstraint; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.kucun.data.entity.DTO.*; /** 产品类 @author Administrator */ @Entity @Table(name=“chanpin”, uniqueConstraints = { @UniqueConstraint(columnNames = “bianhao”) }) @JsonSerialize(using = FullEntitySerializer.class) @UniqueEntity( repositoryName = “chanpin”, fields = {“bianhao”}, message = “该产品已存在” ) public class Chanpin extends EntityBasis { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; // 关联订单产品 @OneToMany( mappedBy = “chanpin”, cascade = CascadeType.ALL, fetch = FetchType.LAZY ) private List<Dingdan_chanpin> dingdan_chanpin; private String bianhao; @OneToMany( mappedBy = "chanpin", cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true ) private List<Chanpin_zujian> chanpin_zujian; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getBianhao() { return bianhao; } public void setBianhao(String bianhao) { this.bianhao = bianhao; } public List<Dingdan_chanpin> getDingdan_chanpin() { return dingdan_chanpin; } public void setDingdan_chanpin(List<Dingdan_chanpin> dingdan_chanpin) { this.dingdan_chanpin = dingdan_chanpin; } public List<Chanpin_zujian> getChanpin_zujian() { return chanpin_zujian; } public void setChanpin_zujian(List<Chanpin_zujian> chanpin_zujian) { this.chanpin_zujian = chanpin_zujian; } } ------------------------ Chanpin_zujian.java ------------------------ package com.kucun.data.entity; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.Table; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.kucun.data.entity.DTO.FullEntitySerializer; import com.kucun.data.entity.DTO.UniqueEntity; /** 产品组件关联类 @author Administrator */ @Entity @Table(name=“chanpin_zujian”) @JsonSerialize(using = FullEntitySerializer.class) @UniqueEntity( repositoryName = “chanpin_zhujian”, fields = { “chanpin”, “zujian”}, message = “产品下也有该组件” ) public class Chanpin_zujian extends EntityBasis { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; // 关联到产品 @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = “chanpin_id”) private Chanpin chanpin; // 关联到组件 @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "zujian_id") private Zujian zujian; // 关联到板材 @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "bancai_id") private Bancai bancai; //一张板材生产多少组件 private Double one_howmany; //生产多少组件 private Double zujianshu; public Double getZujianshu() { return zujianshu; } public void setZujianshu(Double zujianshu) { this.zujianshu = zujianshu; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public Chanpin getChanpin() { return chanpin; } public void setChanpin(Chanpin chanpin) { this.chanpin = chanpin; } public Zujian getZujian() { return zujian; } public void setZujian(Zujian zujian) { this.zujian = zujian; } public Bancai getBancai() { return bancai; } public void setBancai(Bancai bancai) { this.bancai = bancai; } public Double getOne_howmany() { return one_howmany; } public void setOne_howmany(Double one_howmany) { this.one_howmany = one_howmany; } public Chanpin_zujian() { super(); // TODO Auto-generated constructor stub } } ------------------------ Dingdan.java ------------------------ package com.kucun.data.entity; import java.util.Date; import java.util.List; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.ManyToMany; import javax.persistence.OneToMany; import javax.persistence.Table; import javax.persistence.UniqueConstraint; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.kucun.data.entity.DTO.FullEntitySerializer; import com.kucun.data.entity.DTO.UniqueEntity; /** 订单 @author Administrator */ @Entity @Table(name=“dingdan”, uniqueConstraints = { @UniqueConstraint(columnNames = “number”) }) @UniqueEntity( repositoryName = “dingdan”, fields = {“numder”}, message = “该订单已存在” ) @JsonSerialize(using = FullEntitySerializer.class) public class Dingdan extends EntityBasis{ @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; //订单号 private String number; private Date xiadan; private Date jiaohuo; @OneToMany( mappedBy = “dingdan”, cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true ) private List<Dingdan_chanpin> dingdan_chanpin; public Date getXiadan() { return xiadan; } public void setXiadan(Date xiadan) { this.xiadan = xiadan; } public Date getJiaohuo() { return jiaohuo; } public void setJiaohuo(Date jiaohuo) { this.jiaohuo = jiaohuo; } public List<Dingdan_chanpin> getDingdan_chanpin() { return dingdan_chanpin; } public void setDingdan_chanpin(List<Dingdan_chanpin> dingdan_chanpins) { this.dingdan_chanpin = dingdan_chanpins; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getNumber() { return number; } public void setNumber(String number) { this.number = number; } public Dingdan(Integer id, String number) { super(); this.id = id; this.number = number; } public Dingdan() { super(); // TODO Auto-generated constructor stub } } ------------------------ Dingdan_chanpin.java ------------------------ package com.kucun.data.entity; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.Table; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.kucun.data.entity.DTO.FullEntitySerializer; import com.kucun.data.entity.DTO.UniqueEntity; /** 订单和产品关联 @author Administrator */ @Entity @Table(name=“dingdan_chanpin”) @UniqueEntity( repositoryName = “dingdan_chanpin”, fields = {“dingdan”,“chanpin”}, message = “订单下已有该产品” ) @JsonSerialize(using = FullEntitySerializer.class) public class Dingdan_chanpin extends EntityBasis { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; //产品信息 @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = “dingdan_id”) // 指 private Dingdan dingdan; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "chanpin_id") // 指 private Chanpin chanpin; private Integer shuliang;//产品数量; public Chanpin getChanpin() { return chanpin; } public void setChanpin(Chanpin chanpin) { this.chanpin = chanpin; } public Integer getShuliang() { return shuliang; } public void setShuliang(Integer shuliang) { this.shuliang = shuliang; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public Dingdan getDingdan() { return dingdan; } public void setDingdan(Dingdan dingdan) { this.dingdan = dingdan; } public Chanpin getChanping() { return chanpin; } public void setChanping(Chanpin chanping) { this.chanpin = chanping; } } ------------------------ EntityBasis.java ------------------------ package com.kucun.data.entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.MappedSuperclass; @MappedSuperclass public abstract class EntityBasis implements EntityBasisId { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } } ------------------------ EntityBasisId.java ------------------------ package com.kucun.data.entity; public interface EntityBasisId { Integer getId(); void setId(Integer id); } ------------------------ Information.java ------------------------ package com.kucun.data.entity; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; /** 通信类 @author Administrator */ public class Information { private static final ObjectMapper mapper = new ObjectMapper(); private Integer Status ; private String text; private Object data; public Integer getStatus() { return Status; } public void setStatus(Integer status) { Status = status; } public String getText() { return text; } public void setText(String text) { this.text = text; } public Object getData() { return data; } public void setData(Object data) { this.data = data; } public Information(Integer status, String text, Object data) { super(); Status = status; this.text = text; this.data = data; } @SuppressWarnings({“unchecked”,“rawtypes”}) public Information(Integer status, String text, String data, Class T) throws Exception { super(); Status = status; this.text = text; this.data = fromJson(data,T); } public Information() { super(); // TODO Auto-generated constructor stub } public String DataJson() throws JsonProcessingException { // Java对象转JSON return mapper.writeValueAsString(this); } @SuppressWarnings(“unchecked”) public T fromJson(String json, Class clazz) throws Exception { data= mapper.readValue(json, clazz); return (T) data; } public static Information NewSuccess(Object data) { return new Information(200, "success", data); } public static Information NewSuccess(String data) { return new Information(200, "success", data); } public static Information Newfail(Integer status,String text,Object data) { return new Information(status, "success", data); } public static Information NewFail(int i, String string) { // TODO Auto-generated method stub return new Information(i,string,null); } public static Information NewFail( String string) { // TODO Auto-generated method stub return new Information(400,string,null); } } ------------------------ Jinhuo.java ------------------------ package com.kucun.data.entity; import java.util.Date; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.ManyToOne; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.kucun.data.entity.DTO.FullEntitySerializer; @Entity @JsonSerialize(using = FullEntitySerializer.class) public class Jinhuo extends EntityBasis{ @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; @ManyToOne(optional=true) private Dingdan dingdan; @ManyToOne(optional=true) private Chanpin chanpin; @ManyToOne(optional=true) private Zujian zujian; @ManyToOne(optional=true) private Bancai bancai; private Integer shuliang; private Date date; @ManyToOne private User user; public Jinhuo(Integer id, Dingdan dingdan, Chanpin chanpin, Zujian zujian, Bancai bancai, Integer shuliang, Date date, User user) { super(); this.id = id; this.dingdan = dingdan; this.chanpin = chanpin; this.zujian = zujian; this.bancai = bancai; this.shuliang = shuliang; this.date = date; this.user = user; } public Jinhuo() { super(); // TODO Auto-generated constructor stub } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public Dingdan getDingdan() { return dingdan; } public void setDingdan(Dingdan dingdan) { this.dingdan = dingdan; } public Chanpin getChanpin() { return chanpin; } public void setChanpin(Chanpin chanpin) { this.chanpin = chanpin; } public Zujian getZujian() { return zujian; } public void setZujian(Zujian zujian) { this.zujian = zujian; } public Bancai getBancai() { return bancai; } public void setBancai(Bancai bancai) { this.bancai = bancai; } public Integer getShuliang() { return shuliang; } public void setShuliang(Integer shuliang) { this.shuliang = shuliang; } public Date getDate() { return date; } public void setDate(Date date) { this.date = date; } public User getUser() { return user; } public void setUser(User user) { this.user = user; } } ------------------------ Kucun.java ------------------------ package com.kucun.data.entity; import java.util.List; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.OneToOne; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.kucun.data.entity.DTO.FullEntitySerializer; /** 库存 @author Administrator */ @Entity @JsonSerialize(using = FullEntitySerializer.class) public class Kucun extends EntityBasis{ @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; private Integer shuliang; @OneToOne(fetch = FetchType.LAZY) // 正确映射 Bancai 实体 @JoinColumn(name = "bancai_id", referencedColumnName = "id") private Bancai bancai; // 新增:库存归属订单(可为空) @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "dingdan_id") private Dingdan reservedOrder; public Integer getId() { return id; } public Dingdan getReservedOrder() { return reservedOrder; } public void setReservedOrder(Dingdan reservedOrder) { this.reservedOrder = reservedOrder; } public void setId(Integer id) { this.id = id; } public Bancai getBancai() { return bancai; } public void setBancai(Bancai bancai) { this.bancai = bancai; } public Integer getShuliang() { return shuliang; } public void setShuliang(Integer shuliang) { this.shuliang = shuliang; } public Kucun(Integer id, Bancai bancai, Integer shuliang) { super(); this.id = id; this.bancai = bancai; this.shuliang = shuliang; } public Kucun() { super(); // TODO Auto-generated constructor stub } } ------------------------ Mupi.java ------------------------ package com.kucun.data.entity; import java.util.List; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.ManyToMany; import javax.persistence.OneToMany; import javax.persistence.Table; import javax.persistence.UniqueConstraint; import org.hibernate.annotations.Type; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.kucun.data.entity.DTO.FullEntitySerializer; /** 木皮 @author Administrator */ @Entity @Table(name=“mupi”, uniqueConstraints = { @UniqueConstraint(columnNames = “name”) }) @JsonSerialize(using = FullEntitySerializer.class) public class Mupi extends EntityBasis{ /** * 是否有油漆 */ @Column(name="you") @Type(type = "org.hibernate.type.BooleanType") private Boolean you; private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } // 添加 OneToMany 映射 @OneToMany(mappedBy = "mupi1") // 指向 Bancai 中的 mupi1 字段 private List<Bancai> bancaisForMupi1; @OneToMany(mappedBy = "mupi2") // 指向 Bancai 中的 mupi2 字段 private List<Bancai> bancaisForMupi2; public List<Bancai> getBancaisForMupi1() { return bancaisForMupi1; } public void setBancaisForMupi1(List<Bancai> bancaisForMupi1) { this.bancaisForMupi1 = bancaisForMupi1; } public List<Bancai> getBancaisForMupi2() { return bancaisForMupi2; } public void setBancaisForMupi2(List<Bancai> bancaisForMupi2) { this.bancaisForMupi2 = bancaisForMupi2; } public Mupi() { super(); } public Boolean getYou() { return you; } public void setYou(Boolean you) { this.you = you; } } ------------------------ SimpleEntity.java ------------------------ package com.kucun.data.entity; import javax.persistence.Column; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.MappedSuperclass; @MappedSuperclass public abstract class SimpleEntity implements EntityBasisId{ @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; @Column(nullable = false, unique = true) private String name; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } // Getters and Setters... } ------------------------ User.java ------------------------ package com.kucun.data.entity; import java.util.Objects; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Table; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.kucun.data.entity.DTO.FullEntitySerializer; /** 用户 @author Administrator / @Entity @Table(name=“user”) @JsonSerialize(using = FullEntitySerializer.class) public class User extends EntityBasis{ @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; /* * 名字 / @Column(nullable=false) private String name; /* * 账号 / @Column(nullable=false) private String andy; /* * 密码 / @Column(nullable=false) private String pass; /* * 权限 */ @Column(nullable=false) private int role; public User() { super(); } public User(int id, String name, String andy, String pass) { super(); this.id = id; this.name = name; this.andy = andy; this.pass = pass; } public Integer getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getAndy() { return andy; } public void setAndy(String andy) { this.andy = andy; } public String getPass() { return pass; } public void setPass(String pass) { this.pass = pass; } public int getRole() { System.out.println(role); return role; } public void setRole(int role) { this.role = role; } @Override public String toString() { return "{id:" + id + ", name:" + name + ", andy:" + andy + ", pass:" + pass + ", role:" + role + "}"; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; User user = (User) o; return Objects.equals(id, user.id) && Objects.equals(name, user.name) && Objects.equals(andy, user.andy)&& Objects.equals(role, user.role); //添加所有属性比较 } @Override public int hashCode() { return Objects.hash(id, name, andy,pass,role); } @Override public void setId(Integer id) { // TODO Auto-generated method stub } } ------------------------ Zujian.java ------------------------ package com.kucun.data.entity; import java.util.List; import javax.persistence.CascadeType; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.Id; import javax.persistence.OneToMany; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.kucun.data.entity.DTO.FullEntitySerializer; @Entity @JsonSerialize(using = FullEntitySerializer.class) public class Zujian extends EntityBasis{ private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } // 反向关联到产品组件 @OneToMany( mappedBy = "zujian", cascade = CascadeType.ALL, fetch = FetchType.LAZY ) private List<Chanpin_zujian> chanping_zujian; public List<Chanpin_zujian> getChanping_zujian() { return chanping_zujian; } public void setChanping_zujian(List<Chanpin_zujian> chanping_zujian) { this.chanping_zujian = chanping_zujian; } } ------------------------ 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)” // ] //} /** 优化后的关联解析函数(解决空值问题并支持多层关联) @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.chanpin_zujians && data.bancais) { resolveRef(data.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); } }); } // 新增:计算订单可用板材 if (data.dingdans) { data.dingdans.forEach(dingdan => { // 初始化订单可用板材 dingdan.availableBancais = {}; // 遍历订单中的所有产品 if (dingdan.dingdan_chanpin_list) { dingdan.dingdan_chanpin_list.forEach(dc => { if (dc.chanpin && dc.chanpin.chanpin_zujian_list) { // 遍历产品的所有组件 dc.chanpin.chanpin_zujian_list.forEach(cz => { if (cz.bancai) { const bancaiId = cz.bancai.id; // 计算组件所需板材数量 = 产品数量 × 组件数量比例 const required = dc.shuliang * (cz.zujianshu || 1); if (!dingdan.availableBancais[bancaiId]) { dingdan.availableBancais[bancaiId] = { bancai: cz.bancai, required: 0, available: 0 }; } dingdan.availableBancais[bancaiId].required += required; } }); } }); } // 遍历订单中的所有直接组件 if (dingdan.dingdan_chanpin_zujian_list) { dingdan.dingdan_chanpin_zujian_list.forEach(dz => { if (dz.bancai) { const bancaiId = dz.bancai.id; const required = dz.shuliang; if (!dingdan.availableBancais[bancaiId]) { dingdan.availableBancais[bancaiId] = { bancai: dz.bancai, required: 0, available: 0 }; } dingdan.availableBancais[bancaiId].required += required; } }); } // 计算可用数量(从库存中减去其他订单预留量) Object.values(dingdan.availableBancais).forEach(info => { if (info.bancai.kucun) { // 获取板材总库存 let totalAvailable = info.bancai.kucun.shuliang; // 减去其他订单预留量 data.dingdans.forEach(otherDingdan => { if (otherDingdan.id !== dingdan.id && otherDingdan.availableBancais?.[info.bancai.id]) { totalAvailable -= otherDingdan.availableBancais[info.bancai.id].required; } }); // 确保可用量不小于0 info.available = Math.max(0, totalAvailable); } }); }); } 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: [], chanpin_zujians: [], zujians: [], caizhis: [], dingdan_chanpins: [], users: [], jinhuos: [] }; this.isSyncing = false; this.lastSync = null; this.callbacks = { all: [], bancais: [], dingdan: [], mupi: [], chanpin: [], kucun: [], chanpin_zujian: [], zujian: [], caizhi: [], dingdan_chanpin: [], user: [], jinhuo: [] }; 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.js ------------------------ // 监听来自父窗口的消息 window.addEventListener(‘message’, function(event) { if (event.source === window.parent && event.data === ‘DataManagerReady’) { initializeDataManager(); } }); // 初始化数据管理器 function initializeDataManager() { const dataManager = window.parent.dataManager; if (dataManager) { // 注册数据刷新回调 dataManager.registerCallback('all', function(operation, entity, data) { updatePageData(dataManager); }); // 初始页面数据更新 updatePageData(dataManager); // 设置搜索功能 setupSearch(dataManager); // 设置排序功能 setupSorting(); } 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; } // 全局变量存储当前排序状态 let currentSort = { column: null, direction: ‘asc’ // ‘asc’ 或 ‘desc’ }; // 更新结果表格 function updateTable(dataManager) { const tableBody = document.getElementById(‘resultBody’); tableBody.innerHTML = ‘’; const data = dataManager.data; let results = []; // 改为let以便排序 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; const bancai = cz.bancai; const materialInfo = getMaterialInfo(bancai); const stockQuantity = dataManager.data.kucuns?.find(ku => ku.bancai?.id==bancai.id)?.shuliang || 0; results.push({ orderNumber: dc.dingdan.number, productInfo: dc.chanpin.bianhao, productQuantity: dc.shuliang, component: cz.zujian.name, material: materialInfo, materialPerComponent: cz.one_howmany, materialOrderQuantity: dc.shuliang * cz.one_howmany, stockQuantity: stockQuantity, // 添加原始数据用于排序 raw: { orderNumber: dc.dingdan.number, productQuantity: dc.shuliang, materialPerComponent: cz.one_howmany, materialOrderQuantity: dc.shuliang * cz.one_howmany, stockQuantity: stockQuantity, thickness: bancai.houdu || 0 }, operation: `<button class="btn btn-sm btn-outline-primary" onclick="showMaterialDetail(${bancai.id})">详情</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; const bancai = dcz.bancai; const materialInfo = getMaterialInfo(bancai); const stockQuantity = dataManager.data.kucuns?.find(ku => ku.bancai?.id==bancai.id)?.shuliang || 0; results.push({ orderNumber: dcz.dingdan.number, productInfo: dcz.chanpin_zujian.chanpin?.bianhao || '独立组件', productQuantity: dcz.shuliang, component: dcz.chanpin_zujian.zujian.name, material: materialInfo, materialPerComponent: dcz.chanpin_zujian.one_howmany, materialOrderQuantity: dcz.shuliang * dcz.chanpin_zujian.one_howmany, stockQuantity: stockQuantity, // 添加原始数据用于排序 raw: { orderNumber: dcz.dingdan.number, productQuantity: dcz.shuliang, materialPerComponent: dcz.chanpin_zujian.one_howmany, materialOrderQuantity: dcz.shuliang * dcz.chanpin_zujian.one_howmany, stockQuantity: stockQuantity, thickness: bancai.houdu || 0 }, operation: `<button class="btn btn-sm btn-outline-primary" onclick="showMaterialDetail(${bancai.id})">详情</button>` }); resultCount++; }); } // 应用排序 if (currentSort.column !== null) { results = sortResults(results, currentSort.column, currentSort.direction); } // 填充表格 if (resultCount > 0) { document.getElementById('noResults').style.display = 'none'; results.forEach(row => { const tr = document.createElement('tr'); tr.innerHTML = ` ${row.orderNumber} ${row.productInfo} ${row.productQuantity} ${row.component} ${row.material} ${row.materialPerComponent} ${row.materialOrderQuantity} ${row.stockQuantity} ${row.operation} `; tableBody.appendChild(tr); }); } else { document.getElementById('noResults').style.display = 'flex'; } document.getElementById('resultCount').textContent = resultCount; } // 排序函数 function sortResults(results, columnIndex, direction) { return results.sort((a, b) => { // 根据列索引确定排序字段 let field; switch(columnIndex) { case 0: // 订单号 field = ‘orderNumber’; break; case 1: // 产品信息 field = ‘productInfo’; break; case 2: // 产品数量 field = ‘productQuantity’; break; case 3: // 组件 field = ‘component’; break; case 4: // 板材 // 特殊处理:按厚度排序 return sortByThickness(a, b, direction); case 5: // 单件用量 field = ‘materialPerComponent’; break; case 6: // 订单用量 field = ‘materialOrderQuantity’; break; case 7: // 库存数量 field = ‘stockQuantity’; break; default: return 0; } // 数值排序 if (['productQuantity', 'materialPerComponent', 'materialOrderQuantity', 'stockQuantity'].includes(field)) { return sortNumeric(a.raw[field], b.raw[field], direction); } // 字符串排序 return sortString(a[field], b[field], direction); }); } // 按厚度排序 function sortByThickness(a, b, direction) { return sortNumeric(a.raw.thickness, b.raw.thickness, direction); } // 数值排序 function sortNumeric(aVal, bVal, direction) { const aNum = parseFloat(aVal) || 0; const bNum = parseFloat(bVal) || 0; if (direction === 'asc') { return aNum - bNum; } else { return bNum - aNum; } } // 字符串排序 function sortString(aVal, bVal, direction) { const aStr = String(aVal).toLowerCase(); const bStr = String(bVal).toLowerCase(); if (aStr < bStr) { return direction === 'asc' ? -1 : 1; } else if (aStr > bStr) { return direction === 'asc' ? 1 : -1; } return 0; } // 获取板材详细信息函数(添加油状态) function getMaterialInfo(bancai) { if (!bancai) return ‘未知板材’; // 获取材质名称 const caizhiName = bancai.caizhi?.name || '未知材质'; // 获取木皮信息(添加油状态) const formatMupi = (mupi) => { if (!mupi) return null; return `${mupi.name}${mupi.you ? '(油)' : ''}`; }; const mupi1Str = formatMupi(bancai.mupi1) || '无'; const mupi2Str = formatMupi(bancai.mupi2) || '无'; const mupiInfo = mupi1Str + (mupi2Str !== '无' ? `/${mupi2Str}` : ''); // 厚度转换为字符串 const thickness = bancai.houdu ? `${bancai.houdu.toFixed(1)}mm` : '未知厚度'; return `${caizhiName} - ${mupiInfo} (${thickness})`; } // 设置搜索功能(添加油状态搜索) 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() { const searchTerm = $(this).val().toLowerCase(); // 支持中文和数字搜索 const searchKeywords = { '有油': '有油', '无油': '无油', 'you': '有油', 'wu': '无油', '1': '有油', '0': '无油', '(油)': '(油)' }; const actualTerm = searchKeywords[searchTerm] || searchTerm; filterTable(actualTerm, 4); }); // 厚度搜索(修复正则表达式) $('#thicknessSearch').on('input', function() { filterTable($(this).val().toLowerCase(), 4); }); // 库存状态搜索 $('#stockStatusBtn').click(function() { const minStock = parseInt($('#minStock').val()) || 0; const maxStock = parseInt($('#maxStock').val()) || Number.MAX_SAFE_INTEGER; filterByStock(minStock, maxStock); }); } // 设置排序功能 function setupSorting() { // 获取所有表头 const headers = document.querySelectorAll(‘#resultsTable th[data-sortable]’); headers.forEach((header, index) => { header.addEventListener('click', () => { // 更新排序状态 if (currentSort.column === index) { // 同一列:切换方向 currentSort.direction = currentSort.direction === 'asc' ? 'desc' : 'asc'; } else { // 新列:默认升序 currentSort.column = index; currentSort.direction = 'asc'; } // 更新UI updateSortIndicators(); // 重新渲染表格 const dataManager = window.parent.dataManager; if (dataManager) { updateTable(dataManager); } }); }); } // 更新排序指示器 function updateSortIndicators() { // 清除所有指示器 const headers = document.querySelectorAll(‘#resultsTable th[data-sortable]’); headers.forEach(header => { header.querySelector(‘.sort-indicator’).textContent = ‘’; }); // 为当前排序列添加指示器 if (currentSort.column !== null) { const currentHeader = headers[currentSort.column]; const indicator = currentHeader.querySelector('.sort-indicator'); indicator.textContent = currentSort.direction === 'asc' ? '↑' : '↓'; } } // 表格过滤函数(修复厚度搜索) function filterTable(searchTerm, columnIndex) { const rows = $(‘#resultBody tr’); let visibleCount = 0; rows.each(function() { const cellText = $(this).find(`td:eq(${columnIndex})`).text().toLowerCase(); let isMatch = false; // 特殊处理油状态搜索 if (searchTerm === '有油' || searchTerm === '无油' || searchTerm === '(油)') { isMatch = cellText.includes(searchTerm); } // 处理厚度字符串匹配(修复正则表达式) else if (columnIndex === 4 && !isNaN(parseFloat(searchTerm))) { // 提取厚度数字部分进行匹配 const thicknessMatch = cellText.match(/(\d+\.?\d*)/); if (thicknessMatch) { const thicknessValue = parseFloat(thicknessMatch[1]); const searchValue = parseFloat(searchTerm); // 允许小数点误差 isMatch = Math.abs(thicknessValue - searchValue) < 0.5; } } // 常规文本匹配 else { 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 filterByStock(minStock, maxStock) { const rows = $(‘#resultBody tr’); let visibleCount = 0; rows.each(function() { const stockCell = $(this).find('td:eq(7)').text(); const stockValue = parseInt(stockCell) || 0; const isMatch = stockValue >= minStock && stockValue <= maxStock; $(this).toggle(isMatch); if (isMatch) visibleCount++; }); document.getElementById('resultCount').textContent = visibleCount; document.getElementById('noResults').style.display = visibleCount > 0 ? 'none' : 'flex'; } // 显示板材详情(添加油状态显示) function showMaterialDetail(bancaiId) { const dataManager = window.parent.dataManager; if (!dataManager) return; const bancais = dataManager.data.bancais || []; const bancai = bancais.find(b => b.id === bancaiId); const kucun = dataManager.data.kucuns?.fill(ku => ku.bancai.id==bancai.id) ; if (!bancai) { alert('未找到板材信息'); return; } // 格式化木皮信息(添加油状态) const formatMupiDetail = (mupi) => { if (!mupi) return '无'; return `${mupi.name} ${mupi.you ? '(油)' : ''}`; }; // 构建详情信息 const detailHtml = `

板材详情 (ID: ${bancai.id})

材质: ${bancai.caizhi?.name || '未知'}

厚度: ${bancai.houdu ? bancai.houdu.toFixed(1) + 'mm' : '未知'}

木皮1: ${formatMupiDetail(bancai.mupi1)}

木皮2: ${formatMupiDetail(bancai.mupi2)}

库存数量: ${kucun?.shuliang || 0}

相关订单:
    ${getRelatedOrders(bancai).map(order => `
  • 订单 ${order.number} (ID: ${order.id})
  • ` ).join('') || '
  • 无相关订单
  • '}
`; // 显示详情(实际项目中可用模态框) const detailWindow = window.open('', '_blank', 'width=600,height=400'); detailWindow.document.write(` <!DOCTYPE html> <html> <head> <title>板材详情</title> <style> body { font-family: Arial, sans-serif; padding: 20px; } .material-detail { max-width: 500px; } h4 { color: #2c3e50; border-bottom: 2px solid #3498db; padding-bottom: 10px; } p { margin: 10px 0; } .related-orders { margin-top: 20px; } ul { list-style-type: none; padding: 0; } li { padding: 5px 0; border-bottom: 1px solid #eee; } </style> </head> <body> ${detailHtml} </body> </html> `); detailWindow.document.close(); } // 获取板材相关订单 function getRelatedOrders(bancai) { const dataManager = window.parent.dataManager; if (!dataManager) return []; const orders = []; const dingdan_chanpin_zujians = dataManager.data.dingdan_chanpin_zujians || []; // 查找直接订单组件关联 dingdan_chanpin_zujians.forEach(dcz => { if (dcz.bancai?.id === bancai.id && dcz.dingdan && !orders.some(o => o.id === dcz.dingdan.id)) { orders.push(dcz.dingdan); } }); // 查找产品组件关联 const chanpin_zujians = dataManager.data.chanpin_zujians || []; chanpin_zujians.forEach(cz => { if (cz.bancai?.id === bancai.id) { const dingdan_chanpins = dataManager.data.dingdan_chanpins || []; dingdan_chanpins.forEach(dc => { if (dc.chanpin?.id === cz.chanpin?.id && dc.dingdan && !orders.some(o => o.id === dc.dingdan.id)) { orders.push(dc.dingdan); } }); } }); return orders; } // 页面加载时尝试初始化 if (window.parent) { window.parent.postMessage(‘RequestDataManager’, ‘*’); } // 添加全局函数供按钮调用 window.showMaterialDetail = showMaterialDetail; ------------------------ test.html ------------------------ <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>板材库存查询系统</title> <link rel="stylesheet" href="../css/dingdan.css"> <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> <link href="../js/bootstrap-5.3.0-alpha1-dist/css/bootstrap.min.css" rel="stylesheet"> <script src="../js/bootstrap-5.3.0-alpha1-dist/umd/popper.min.js"></script> <script src="../js/bootstrap-5.3.0-alpha1-dist/js/bootstrap.min.js"></script> <link rel="stylesheet" href="../js/bootstrap-icons-1.8.1/bootstrap-icons.css"> <script src="../js/main.js"></script> </head> <body>

板材库存管理系统

查询订单、产品、板材及库存信息

订单总数

0

产品种类

0

板材库存

0

库存总量

0

高级搜索
<input type="text" class="form-control with-icon" id="orderSearch" placeholder="搜索订单号..." aria-label="订单号搜索">
<input type="text" class="form-control with-icon" id="productSearch" placeholder="搜索产品编号..." aria-label="产品编号搜索">
<input type="text" class="form-control with-icon" id="materialSearch" placeholder="搜索板材ID或材质..." aria-label="板材搜索">
<input type="text" class="form-control with-icon" id="woodSearch" placeholder="搜索木皮名称..." aria-label="木皮搜索">
<input type="number" class="form-control with-icon" id="thicknessSearch" placeholder="厚度(mm)" min="0" step="0.1">
<input type="number" class="form-control" id="minStock" placeholder="最小库存" min="0"> <input type="number" class="form-control" id="maxStock" placeholder="最大库存" min="0"> <button class="btn btn-primary" type="button" id="stockStatusBtn"> </button>
查询结果
0 条记录 实时数据更新时间: --:--:--
订单号 产品信息 产品数量 组件 板材 单件用量 订单用量 库存数量 操作
加载中...
正在加载数据,请稍候...

没有找到匹配的记录

请尝试调整您的搜索条件

<script src="../js/dingdan.js"></script> </body> </html> 改造页面逻辑,已经删除了dingdan_chanpin_zujian,java和DataManager.js.js是不能改 要求 一个订单有很多产品不同数量,每个产品可以再很多订单中,一个产品有很多组件,一个组件可以在很多产品中,因为每个组件因为在不同的产品中有不同的生产工艺,所以使用不同的板材和板材能生产组件数量,每个板材有不同的材质和两面木皮,木皮表面可能有油漆, 订购时可能直接购入板材,也可能按订单和产品订购板材,也用可能按订单产品组件订购板材,每次采购不标准,一个订单可能订购几次,用户有姓名 账号 密码 权限, 一个记录进货和消耗,查看的时候会查看订单下有多少板材可用

辰可爱啊
  • 粉丝: 30
上传资源 快速赚钱