MAX Family 概述 (或者草稿?)

MAX Family是一种MDA开发方法,体现利用实例知识和适应公司环境变化的思想。用户可在MAX Progeny中调整部分外观实现界面个性化。还探讨了界面生成器产生Web应用系统的可能性。关键部分是报表设计与输出工具MAX Report,应屏蔽数据库,方便用户设计输出报表。

?? MAX Family全名是基于模型的可扩展应用系列Model-based Application eXtensible Family,是一种MDA的开发方法。MDA(模型驱动应用Modeling Driven Application)是一种革命性的软件设计方法,其本质特征是通过使用动态的对象模型来建立一个新的信息系统。在MAX中,主要体现了两个思想:一是在生成某公司的特定模型时,充分利用最好的实例知识和实践经验,表现在企业参考模型的使用;二是在动态公司中,公司的信息系统能够适应公司环境等的快速变化,表现在MDA工具——MAX Builder的使用。


1?MDA的三个主要优点:
??速度快
  体现在简短、紧凑的企业建模工具的使用,通过使用预定义业务模板来减小该过程的难度。
??柔性高
MDA的软件结构可以随着企业业务过程的变化而相应改变,而无需更改代码。
??质量好
MDA提高了企业建模的质量,因为它使用了一种简单的方式使其中很复杂的功能可视化。同时避免了直接书写代码而产生Bugs。


2?MAX的四个主要组成部分:
??建模工具MAX Builder
它可以用图形描述业务模型的组织结构、功能特点以及业务过程的先后顺序。
??企业应用模板MAX Progeny
这些模板为各个行业领域内的企业,或者不同的企业应用系统(PDM, CRM, HIS…),提供了建立本企业模型的参考。这些系统都是由MAX Builder构建的衍生物。
??企业级报表 MAX Report
基于企业应用的柔性报表系统。提供强大、灵活的报表定义工具,以及高效和丰富的报表生成系统。
??企业模型管理工具MAX Configuration
系统管理以及工作流定义与实现。


2.1?建模工具MAX Builder
这一部分是MAX Family与一般企业应用系统的区别所在。一般的企业应用软件都是通过用户定义字段、用户界面个性化等方法来达到客户化的效果。但是,这种自定义的方法不能从根本上来定义一个柔性的系统,只是在现有软件的基础上进行小修小改。当用户的需求与已有的软件差别比较大的时候,还是要通过编写代码来实现业务逻辑。然而,在MAX Builder中,我们采用一系列的可视化工具来让用户“为所欲为”,不仅能自定义字段,还有方法,以及对象和对象间的关系。然后,根据用户定义的类模型来生成软件的界面和数据库结构。生成的用户界面UI还可以被用户按个人喜好进行更改,比如布局,语言等等。
类模型,数据模型和用户界面的定义分别保存为xml文件。类模型和数据模型格式可以参考MS ObjectSpaces中的映射文件格式;UI定义个是可以参考MS XAML的格式(XAML是用在下一个Windows版本中的界面定义语言)。


2.1.1?类模型定义工具
这个工具类似于Rational Rose或者Visio的静态类图定义工具。用户在这个可视化工具里定义各个业务对象的属性、方法、事件,以及各个对象之间的关系。最终的界面应该类似下图:


?
在这个可视化环境中,开发者可以定义类的属性/方法以及它们的类型(Public, Private),定义类之间的关系(Inherited, Association, Aggregation, Composition)。建模的方法可以借鉴Rational Rose Logic View或者Visio的类图定义方法。应该至少支持Package视图,如果可能,还应该支持类层次图(能清晰地反映类的继承关系)。根据目前的发展方向,确定为仅支持单继承。
对于类的方法和事件,提供脚本编辑器。脚本拟采用VB.NET和C#.NET。这样脚本直接由.NET CLR支持,比较容易实现;而且是编译后执行(不同于解释执行的脚本),执行速度上将有优势。


2.1.2?数据库结构生成
根据类图定义,MAX Builder可以自动产生数据库结构,以及数据表之间的关系。比如,每个类作为一个表,类的属性作为表的一个字段,Object属性作为表的外键连接。
同时,也提供方法让用户来制定数据映射。比如,对于只读的Class,可以映射到View。或者映射到其他的应用系统数据库的一个或多个表中。
这个部分的难点在于如何做到高效而且与数据库平台无关的数据库结构。


2.1.3?用户界面定义
根据用户的类模型定义,系统会生成默认的用户界面。默认的用户界面更具以下原则生成:
??所有的Public属性都对应着可见的控件(比如,String 属性对应TextBox, Boolean 属性对应Checkbox, Object 属性对应Listview,等等)。对于<
><>方法,作为属性处理(仅有<>就是只读,仅有<>就是只写)。
生成的界面应该支持多种控件属性。比如,对String类型的属性,应该能让用户指明是否能为空,默认值,是否能重复,最大字符数,是否可编辑等等。
控件按照某种默认的顺序排列(用户定义时的顺序,或者字母顺序?)
??一般的Public方法,对应于该对象窗口上的工具栏按钮和菜单
用户能指定这些方法的顺序和图标,以及快捷键等信息。如果可能,是否可以让用户指定可用/不可用的条件。当工具栏按钮或者菜单被单击时,执行方法脚本。
??事件没有界面,由系统触发调用事件脚本
事件应该包括对象事件和属性事件。
默认的用户界面应该是可以被灵活的修改的。比如,窗口的大小,数据类型(单记录窗口还是多记录窗口),控件的位置,字体,颜色,控件类型(列表框还是弹出按钮?)等等。
下面是一个实例,一个由Customer类自动产生的窗口界面,其中可以看到使用属性窗对CustomerID这个属性进行定义的界面:
?

?

单记录视图


多记录视图

另外,用户在MAX Progeny(由Builder产生的应用系统)中,部分外观也可以进行调整,并且保持在客户端,这样可以实现界面个性化。
可以从上面的图看出,用户通过对类的属性所对应的界面元素进行设置,取得灵活的外观效果。这个界面生成器(也可以叫做UI Factory),可以根据用户的定义,产生相应的UI元素,那么,是否可以产生Web元素从而产生整个Web应用系统呢?我认为可以实现,但是否值得?对于下一个Windows版本(Longhorn,2006年发布),其实B/S模式和C/S模式已经完全混合了,C/S的应用也是无需客户端安装,使用时才从服务器上下载。


2.1.4?代码的生成与编译
定义好了类模型,数据模型和UI后,根据这些定义产生.NET平台的代码,然后编译成.NET IL(中间语言) DLL。代码不是可持久的,只在编译时存在于内存中,生成的DLL存放于服务器上。


2.2?企业应用模板 MAX Progeny
其实,每个不同的行业,或者不同的应用都有其自身的特点。因此,一个完全“通用”的系统是不存在的。即使利用像MAX Builder这样灵活的建模工具,也有很难实现的地方。比如,PDM系统中的文档管理功能,用户会经常上传/下载文件,以及Check in/Check out文件。这些文件操作要求是高性能的,而且和系统的其他模块结合很紧密。如果完全通过建模和脚本来实现这些对象和功能,无疑是困难的。这时候,我们就可以使用“企业应用模板”来解决这个问题了。
我们可以建立一个“PDM应用模板”,在这个模板里,我们事先建立一个内部类“MAXFileControl”,那么,只要和这个FileControl类建立了关联的其他对象,如图档类,文档类,都具有了上传/下载文件,以及Check in/Check out文件的功能。这样,对于这些共同的而且核心的功能,我们都可以通过采用“内部类”来解决问题。
应用模板的另一个功能是,对于用户(开发者)来说,它可能并不清楚一个企业应用具体是什么样的。PDM应该有哪些模块和功能?HIS又该如何架构和构建?如果我们提供的标准应用模板,用户就可以在我们模板的基础上进行开发,而不是从一个空白的模型开始。这里,体现了我们对企业应用的理解,也是系统增值的一部分内容。
这里有一个架构的问题,这些“内部类”如何和我们定义的模型关联,“内部类”的功能如何和整个系统关联,需要进一步的研究,现在暂定为使用.NET自定义属性(Attribute)的方法实现。


2.3?企业模型管理工具MAX Configuration
这一部分是企业应用系统的外围部分,但也是十分重要的。包括的内容有:


2.3.1?系统工具
包括日志管理,用户管理,权限管理,存储管理等各个系统都通用的部分。


2.3.2?工作流定义工具
简单地说,工作流就是一系列相互衔接、自动进行的业务活动或任务。通过这个可视化的工具,用户定义工作流程,并且映射到模型的类上去。这样,对于类的实例,就可以走一个预定义的流程。工作流的模型可以参考WfMC的标准。
这个工具的开发是一个难点。可能类似下面的界面:
?

2.4?报表设计与输出工具 MAX Report
这也是一个关键的部分。报表的设计应该简单,但同时也能支持复杂的报表设计。报表的输出可以采用第三方的工具(比如Crystal Report, VS Report等),但需要和我们的系统紧密地联结起来,让用户既灵活,又方便的设计和输出报表。这个工具应该屏蔽数据库,用户只会看到模型中的类。
这部分的工作主要是架构问题,由于以前有类似的架构经验,我认为这部分可能会是MAX Family的强项。

<!--#include file="../include/db.inc"--> <% ' ========== 设置编码 ========== Response.CodePage = 65001 ' ========== 处理 AJAX 提交请求 ========== If Request.Form("action") = "submit_rating" Then Dim course_id, examinee, item_name, comment, final_score_input, final_score course_id = Trim(Request.Form("course_id")) examinee = Trim(Request.Form("examinee")) item_name = Trim(Request.Form("item_name")) comment = Trim(Request.Form("comment")) final_score_input = Trim(Request.Form("final_score")) ' 验证输入 If Not IsNumeric(course_id) Or course_id = "" Then Response.Write "{""success"":false,""msg"":""无效的课程ID。""}" ElseIf item_name = "" Then Response.Write "{""success"":false,""msg"":""请填写评分项目名称。""}" ElseIf Not IsNumeric(final_score_input) Or CDbl(final_score_input) < 10 Or CDbl(final_score_input) > 100 Then Response.Write "{""success"":false,""msg"":""评分总分异常,请重新提交(应在10~100之间)。""}" Else On Error Resume Next final_score = CDbl(final_score_input) ' 更新主表 Dim sqlUpdate sqlUpdate = "UPDATE certify_od_rating_list " & _ "SET STATUS = 'Y', " & _ " score = " & CDbl(final_score) & ", " & _ " updated_at = SYSDATE " & _ "WHERE course_id = " & CLng(course_id) conn.Execute sqlUpdate If Err.Number = 0 Then Response.Write "{""success"":true,""msg"":"" 评分提交成功!总分为 " & final_score & " 分。""}" Else Response.Write "{""success"":false,""msg"":""数据保存失败:" & Replace(Err.Description, """", "'") & """}" End If Err.Clear On Error GoTo 0 End If Response.End End If ' ========== 查询待评分任务列表 ========== Dim username, sql, rs username = Session("username") If username = "" Then Response.Redirect "../login/Ratinglogin.asp" Response.End End If sql = "SELECT DISTINCT e.course_id, f.EMPLOYEE_ID, b.ITEM " & _ "FROM certify_od_rating_account a, " & _ "certify_tb_item_mapping b, " & _ "certify_tb_pe_team c, " & _ "mfg_tb_newtraining_course e, " & _ "certify_od_rating_list f " & _ "WHERE a.username = '" & Replace(username, "'", "''") & "' " & _ "AND a.username = c.pe " & _ "AND b.pesuper = c.pesuper " & _ "AND e.COURSE_NAME = b.item " & _ "AND e.course_id = f.course_id " & _ "AND f.STATUS = 'N' " & _ "ORDER BY e.course_id" Set rs = conn.Execute(sql) %> <!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>在线评分系统</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: 'Microsoft YaHei', sans-serif; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); min-height: 100vh; padding: 20px; } .container { max-width: 75%; margin: 0 auto; } .header { background: white; border-radius: 10px; padding: 20px 30px; margin-bottom: 20px; box-shadow: 0 5px 15px rgba(0,0,0,0.1); display: flex; justify-content: space-between; align-items: center; } .header h1 { color: #333; font-size: 24px; } .user-info { display: flex; gap: 15px; } .user-name { color: #667eea; font-weight: 500; } .logout-btn { background: #e74c3c; color: white; padding: 8px 16px; border: none; border-radius: 5px; cursor: pointer; text-decoration: none; font-size: 14px; } .content { display: grid; grid-template-columns: 300px 1fr; gap: 20px; } .task-list { background: white; border-radius: 10px; padding: 15px; box-shadow: 0 5px 15px rgba(0,0,0,0.1); max-height: 700px; overflow-y: auto; } .task-item { padding: 12px; border-bottom: 1px solid #eee; cursor: pointer; transition: background 0.2s; font-size: 14px; } .task-title { font-weight: 500; color: #333; } .task-meta { font-size: 12px; color: #999; } .card { background: white; border-radius: 10px; padding: 25px; box-shadow: 0 5px 15px rgba(0,0,0,0.1); display: none; } .card.active { display: block; } .form-group { margin-bottom: 15px; } .form-group label { display: block; margin-bottom: 5px; color: #333; font-weight: 500; } .form-group input, .form-group textarea { width: 100%; padding: 10px; border: 2px solid #ddd; border-radius: 5px; font-size: 14px; } table { border-collapse: collapse; width: 100%; } th, td { border: 1px solid #000; padding: 8px; text-align: center; } th { background-color: #f2f2f2; } .score-options { display: flex; gap: 8px; white-space: nowrap; overflow-x: auto; padding: 5px 0; scrollbar-width: thin; max-width: 100%; -webkit-overflow-scrolling: touch; } .score-options::-webkit-scrollbar { height: 6px; } .score-options::-webkit-scrollbar-track { background: #f1f1f1; border-radius: 3px; } .score-options::-webkit-scrollbar-thumb { background: #ccc; border-radius: 3px; } .score-options label { white-space: nowrap; display: inline-flex; align-items: center; font-size: 13px; min-width: 36px; justify-content: center; } .submit-btn { width: 100%; padding: 12px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; border: none; border-radius: 5px; font-size: 16px; cursor: pointer; } </style> </head> <body> <div class="container"> <div class="header"> <h1>在线评分系统</h1> <div class="user-info"> <span class="user-name">欢迎,<%= Server.HTMLEncode(username) %></span> <a href="../setup/RatingLogins.asp" class="logout-btn">退出登录</a> </div> </div> <div class="content"> <!-- 左侧任务列表 --> <div class="task-list"> <h2 style="margin-bottom:15px;color:#333;">待评分任务</h2> <% If Not rs.EOF Then %> <% Do While Not rs.EOF %> <div class="task-item" onclick="loadTask( '<%= Server.HTMLEncode(rs("course_id")) %>', '<%= Server.HTMLEncode(rs("EMPLOYEE_ID")) %>', '<%= Replace(Server.HTMLEncode(rs("ITEM")), "'", "\'") %>' )"> <div class="task-title"><%= Server.HTMLEncode(rs("ITEM")) %></div> <div class="task-meta"> 课程ID: <%= rs("course_id") %><br> 被评人: <%= rs("EMPLOYEE_ID") %> </div> </div> <% rs.MoveNext %> <% Loop %> <% Else %> <p style="color:#999;text-align:center;padding:20px;">暂无待评分任务</p> <% End If %> <% If IsObject(rs) And rs.State = 1 Then rs.Close %> </div> <!-- 右侧评分表单 --> <div id="ratingForm" class="card"> <h2>提交评分</h2> <div id="msgBox"></div> <!-- 注意:action 可以为空,因为我们用 fetch --> <form method="post" action="" id="ratingFormElement"> <input type="hidden" name="action" value="submit_rating"> <input type="hidden" id="course_id" name="course_id"> <input type="hidden" id="examinee" name="examinee"> <div class="form-group"> <label for="item_name">评分项目</label> <input type="text" id="item_name" name="item_name" required readonly> </div> <div class="form-group"> <label for="comment">评分备注(可选)</label> <textarea id="comment" name="comment" rows="3" placeholder="请输入您的评分备注"></textarea> </div> <div class="form-group"> <table id="scoreTable"></table> </div> <!-- 改为 button + JS 控制 --> <button type="submit" class="submit-btn">提交评分</button> </form> </div> </div> </div> <script> function loadTask(cid, eid, item) { document.getElementById('course_id').value = cid; document.getElementById('examinee').value = eid; document.getElementById('item_name').value = item; // 显示评分表单 document.querySelectorAll('.card').forEach(el => el.classList.remove('active')); document.getElementById('ratingForm').classList.add('active'); document.getElementById('msgBox').innerHTML = ''; const table = document.getElementById('scoreTable'); table.innerHTML = ` <tr> <th width="65%">评分项目</th> <th width="35%"> 得分(1-10分) <div id="totalScoreBadge" style=" font-size:12px;color:#d35400;font-weight:bold; margin-top:6px;border-top:1px dashed #ccc;padding-top:4px;"> 当前总分:0 </div> </th> </tr> `; const items = [ null, "工程部相关联络电话是否知道?安全注意事项,逃生路线及冲身洗眼器…etc…是否了解", "是否通读ECP Item 的相关 OI/TECN/PN?", "机台操作是否熟练?", "异常状况处理程序是否了解?", "控挡片使用程序是否了解?", "量测流程、Spec,及操作是否熟练?", "相关之量测机台是否会操作?", "PM后复机程序是否熟料?", "热机程序及条件状况之了解?", "Daily Check 项目是否明确?" ]; for (let i = 1; i <= 10; i++) { let opts = '<div class="score-options">'; for (let j = 1; j <= 10; j++) { const checked = j === 5 ? 'checked' : ''; opts += `<label><input type="radio" name="detail_${i}" value="${j}" ${checked}> ${j}</label>`; } opts += '</div>'; table.innerHTML += ` <tr> <td><strong>[${i}]</strong> ${items[i]}</td> <td>${opts}</td> </tr>`; } updateTotalDisplay(); } function calculateTotalScore() { let total = 0; for (let i = 1; i <= 10; i++) { const radios = document.getElementsByName(`detail_${i}`); const checked = Array.from(radios).find(r => r.checked); if (checked) total += parseInt(checked.value); } return total; } function updateTotalDisplay() { const total = calculateTotalScore(); const display = document.getElementById('totalScoreBadge'); if (display) { display.textContent = `当前总分:${total}`; display.style.color = total >= 100 ? 'green' : '#d35400'; } } // 页面加载完成后绑定事件 document.addEventListener("DOMContentLoaded", function () { const scoreTable = document.getElementById('scoreTable'); if (scoreTable) { scoreTable.addEventListener('change', function(e) { if (e.target.name && e.target.name.startsWith('detail_')) { updateTotalDisplay(); } }); } // 绑定表单提交事件 const form = document.getElementById('ratingFormElement'); if (form) { form.onsubmit = async function(e) { e.preventDefault(); // 阻止默认提交 const total = calculateTotalScore(); let msg = `本次评分总计 ${total} 分。\n`; msg += total < 100 ? "⚠️ 警告:未满100分属于未通过!\n确认提交?" : "✅ 总分达标,确认提交?"; if (!confirm(msg)) return; const formData = new FormData(this); formData.append("final_score", total); // 添加计算出的总分 try { const res = await fetch(location.href, { method: "POST", body: new URLSearchParams(formData) }); const data = await res.json(); if (data.success) { alert(data.msg); // 可在此处清空表单或刷新任务列表 location.reload(); // 刷新页面以更新左侧任务列表 } else { alert("提交失败:" + data.msg); } } catch (err) { alert("网络错误,请重试!"); console.error(err); } }; } }); </script> </body> </html> <% ' 清理资源 If IsObject(rs) Then If rs.State = 1 Then rs.Close Set rs = Nothing End If %> 你先看看这个代码
12-25
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值