课件 6 全部:文件 8 段结构、默认值、监听、按钮、写数据的全部方法
全部来自课件《6、js》36 页(含截图里的代码)+ 知识库验证过的例子。Site.js 是 JavaScript,不是 Java,但语法很像。
define([ …要用的工具… ], function ( …给工具起的名字… ) { ← ① 头部引入
var currentuser = …; ← ② 全局变量
api.setRelationshipAccess(…); ← ③ 跨对象编辑权限
api.hideRevisionAction(…); ← ④ 隐藏版本按钮
function alertMessage(…) { … } ← ⑤ 自定义方法
topic.subscribe("/csi/view/start", function (view) { … });← ⑥ 页面订阅(默认值/只读/隐藏)
api.watchAttribute("Sample", "字段", function (…) { … }); ← ⑦ 属性值修改的监听
return { ← ⑧ 按钮事件(OnAction 调的函数都在这)
C8_onActionXXX: function (ac) { … },
C8_onActionYYY: function (ac) { … }
};
});
记住这个骨架,任何 Site.js 你都能找到「我要改的东西在第几段」。
| 代码(一行一行) | 这一行在干什么 |
|---|---|
| define([ | 声明:这个文件要用下面这些工具 |
| "dojo/topic", | 页面事件(第 ⑥ 段 topic.subscribe 要用) |
| "csi/api", | C8 的 api(watchAttribute、hideRevisionAction) |
| "csi/types", | 类型工具(如 types.getDateTime() 取当前时间) |
| "csi/session", | 当前登录信息(当前用户、当前页面) |
| "csi/widget/MessageDialog", | 弹框 |
| "csi/resolver", | 取对象数据 |
| "csi/xml", | 常量:xml.ATTR_REF、xml.OP_EQUALS… |
| "csi/xml/Search", | 查询 |
| "csi/comm", | comm.createUrl() 生成新对象的 URL |
| … 共 24 个 … | |
| ], function (topic, lang, on, monitor, Controller, api, win, types, session, locale, MessageDialog, resolver, xml, Search, comm, …) { | 上面引了几个,这里就按同样顺序起几个名字。第 1 个 "dojo/topic" 叫 topic,第 6 个 "csi/api" 叫 api……顺序错了名字就对错了工具 |
跟 Java 的 import 一个意思。平时不用动,用到新工具才往两边同时加。
| 代码(一行一行) | 这一行在干什么 |
|---|---|
| var currentuser = session.getCurrentUser().$URL; | 当前登录用户的 URL(唯一地址) |
| var userName = session.getCurrentUser().$Name; | 当前用户名字 |
| var istyt = "C24229"; //临时款类型 | 把某个固定对象的编号存起来,后面反复用 |
| var istyt1 = "C738"; //正常款类型 |
写在 define 里面、所有函数外面 → 整个文件都能用。像 Java 类里的成员变量。
| 代码(一行一行) | 这一行在干什么 |
|---|---|
| api.setRelationshipAccess("MCProduct", "C8_MCP_Sample", true); | 允许在 MCProduct(款式 Offer)页面上,直接编辑它关联的 C8_MCP_Sample(样品)的字段。默认是不能跨对象改的 |
| 代码(一行一行) | 这一行在干什么 |
|---|---|
| //隐藏BOM版本冻结等按钮 | 注释 |
| api.hideRevisionAction("ApparelBOM", "MakePending"); | BOM 页上隐藏「设为待定」 |
| api.hideRevisionAction("ApparelBOM", "Approve"); | 隐藏「审批」 |
| api.hideRevisionAction("ApparelBOM", "EndPhase"); | 隐藏「结束阶段」 |
| api.hideRevisionAction("ApparelBOM", "ReviseAndAbandon"); | 隐藏「修订并放弃」 |
| api.hideRevisionAction("ApparelBOM", "PurgeRevisions"); | 隐藏「清除版本」 |
| api.hideRevisionAction("ApparelBOM", "Abandon"); | 隐藏「放弃」 |
| 代码(一行一行) | 这一行在干什么 |
|---|---|
| function alertMessage(messageInfo) { | 定义一个方法,参数是要显示的文字。跟 Java 的 static void alertMessage(String messageInfo) 一样 |
| new MessageDialog({ | new 一个弹框对象(Java 第 06 点的 new) |
| buttons: ["OK"], | 只有一个 OK 按钮 |
| iconClass: "", | 不要图标 |
| message: messageInfo, | 内容 = 传进来的文字 |
| title: "提示" | 标题 |
| }).show(); | 显示出来 |
| } |
| 代码(一行一行) | 这一行在干什么 |
|---|---|
| function getEnumDescription(fullName) { | 传进来 "C8_StyleStatus:紧急" 这种完整枚举值 |
| var des = ""; | 准备返回值,默认空 |
| var mtquery = new Search(); | new 一个查询 |
| var mq = mtquery.getQuery(); | 拿到查询条件 |
| mq.Node(xml.PROP_TYPE, xml.OP_EQUALS, "EnumValue") | 查的对象类型 = EnumValue(枚举值) |
| .Attr("Node Name", xml.OP_EQUALS, fullName.split(":")[0], "Child:__Parent__") | 它的父对象名 = 冒号前面那段(枚举名)。split(":")[0] 跟 Java 的 split 一样,取第 0 个 |
| .Attr("Value", xml.OP_EQUALS, fullName); | 它的值 = 完整写法 |
| var resmt = mtquery.execute({sync: true}); | 执行查询,sync:true = 等它查完再往下 |
| var mtResults = resmt.getResultNodes(); | 拿到结果列表 |
| if (mtResults.length > 0) { | 有结果(= Java 的 size() > 0) |
| des = mtResults[0].Description; | 取第一个的描述(= get(0)) |
| } | |
| return des; | 返回 |
| } |
| 代码(一行一行) | 这一行在干什么 |
|---|---|
| topic.subscribe("/csi/view/start", function (view) { | 「每次打开一个视图(表单)时」执行这个函数,view = 打开的那个视图 |
| var key = view.defView.getKey(); | 这个视图的身份证,格式「视图Id|对象类型」 |
| var currentNode = session.getLocationNode(); | 当前所在的对象(从哪个页面点进来的) |
| switch (key) { | 按身份证分情况(Java 的 switch 一样) |
| case "C8_NewThemeForm|Theme": { … break; }; | 打开的是「新建主题」表单时做…… |
| case "__Form__|Style": { … break; }; | 打开的是「新建款式」表单时做…… |
| } | |
| }); |
| 代码(一行一行) | 这一行在干什么 |
|---|---|
| case "__Form__|Style": { | 打开的是款式的新建表单 |
| var tab = session._location.Tab; | 从哪个标签页点的「新建」(View ID) |
| if (tab == "C8_BuyerStyle") { | 如果是从「买手款」标签点的 |
| view.addFixedValue("DevelopmentType","DevtType:02"); | 开发类型固定为 02(外购样)——默认,而且不能改 |
| var MCollection = session.getLocationNode(); | 取当前所在的项目对象 |
| view.addFixedValue("C8_Style_MCollection", MCollection.$URL); | 项目字段固定为当前项目(ref 类型给 URL) |
| } else if (tab == "C8_StyleDesign") { | 如果是从「设计款」标签点的 |
| view.addFixedValue("DevelopmentType","DevtType:01"); | 开发类型固定为 01 |
| view.addDefaultValue(attributename, value); | 默认但可以改的写法(课件这里是模板占位) |
| view.addHideExpression("C8_Style_MCollection", true); | 满足条件就隐藏项目字段 |
| } | |
| view.defView.dataPointResolver.defView.m_defActions.removeAction("Save_Copy"); | 去掉表单上「保存&复制」按钮 |
| view.defView.dataPointResolver.defView.m_defActions.removeAction("Save_Go"); | 去掉「保存&继续」按钮 |
| break; | 必须带 break,不然会接着执行下一个 case |
| }; |
| 方法 | 效果 |
|---|---|
view.addFixedValue(字段, 值) | 默认 + 不可编辑 |
view.addDefaultValue(字段, 值) | 默认 + 可编辑 |
view.addHideExpression(字段, true) | 隐藏 |
…m_defActions.removeAction("Save_Copy") | 去掉表单按钮 |
| 代码(一行一行) | 这一行在干什么 |
|---|---|
| case "C8_NewThemeForm|Theme": { | |
| var query = new Search(); | new 查询 |
| var q = query.getQuery(); | |
| q.Node(xml.PROP_TYPE, xml.OP_EQUALS, "ThemeMasterSubtype") | 查「主题子类型」 |
| .Attr("Node Name", xml.OP_EQUALS, "趋势"); | 名字叫「趋势」的那个 |
| var res = query.execute({ sync: true }); | 执行 |
| var result = res.getResultNodes(); | |
| var resultStr = ""; | |
| if (result.length > 0) { resultStr = result[0].$URL; } | 查到了就取它的 URL |
| view.addFixedValue("ThemeMaster/Subtype", resultStr); | 把子类型固定为「趋势」。ref 字段的值永远是 URL,不是名字 |
| var RequestNode = session.getLocationNode(); | 当前所在的「供应商请求」 |
| var MCollection = RequestNode.C8_SR_MCollection; | 从它身上取「客户项目」字段(得到的是 URL) |
| var MCollectionNode = resolver.m_cache.getNode(MCollection, true); | 用 URL 把项目对象本身取出来 |
| var DevSeason = MCollectionNode.C8_MC_DevSeason; | 再取项目的开发季 |
| view.addFixedValue("ThemeSeason", DevSeason); | 季节固定为项目的开发季 |
| view.addHideExpression("ThemeBOMType", true); | 隐藏主题 BOM 类型 |
| break; | |
| }; |
这一段把「URL → 对象 → 属性」的取法完整走了一遍:node.字段 拿到的是 URL,要 resolver.m_cache.getNode(url, true) 才拿到对象。
| 参数 | 课件原话 | 大白话 |
|---|---|---|
ac | 可通过该对象进行增删改操作 | 「操作员」:ac.m_writer 写数据,ac.newValue 刚填的新值 |
cn | 可通过该对象修改 Sample 的字段 | 当前这条的「修改器」 |
node | 可通过该字段 Sample 的所有字段 | 当前这条数据,node.$URL、node.$Name、node.字段 |
id | — | 被改的字段 Id |
| 代码(一行一行) | 这一行在干什么 |
|---|---|
| //制作BOM By Andy 20220509 | 注释:谁、哪天写的(公司习惯) |
| api.watchAttribute("Sample", "C8_Sample_IssueBOM", function (ac, cn, node, id) { | 盯住样品的「下发 BOM」字段,一变就执行 |
| if (ac.newValue) { | 新值是 true(勾上了)才做 |
| var bomname = node.$Name + "BOM"; | BOM 名 = 样品名 + "BOM"(字符串拼接,Java 第 10 点) |
| var styurl = node.C8_Sample_Style; | 样品所属款式的 URL |
| var newurl = comm.createUrl("ApparelBOM"); | 先给新 BOM 生成一个自增 URL(还没建) |
| ac.m_writer.callMethod("ProductSpec", "StyleNewBOM") | 调用 C8 内置方法「给款式新建 BOM」 |
| .addParameter("URL", xml.ATTR_REF, newurl) | 参数:新 BOM 用这个 URL |
| .addParameter("ParentURL", xml.ATTR_REF, styurl) | 参数:挂在这个款式下 |
| .addParameter("ParentAttr", "string", "DataSheets") | 挂在款式的 DataSheets 下面 |
| .addParameter("Type", "string", "ApparelBOM") | 类型是 ApparelBOM |
| .addParameter("BeforeURL", xml.ATTR_REF, "centric:"); | "centric:" = 空引用 |
| ac.m_writer.validateNode(newurl, true); | 校验新对象 |
| ac.m_writer.changeNode(node.$URL).changeAttribute("C8_Sample_BOM", "ref", newurl); | 样品的「BOM」字段指向新 BOM |
| ac.m_writer.changeNode(node.$URL).changeAttributeFlag("C8_Sample_IssueBOM", xml.ATTR_FLAG_NOEDIT, true, true); | 把「下发 BOM」设为不可编辑(防止重复点) |
| ac.m_writer.changeNode(newurl).changeAttribute("Node Name", "string", bomname); | 新 BOM 起名 |
| ac.m_writer.changeNode(newurl).changeAttribute("BOMSample", "ref", node.$URL); | 新 BOM 反过来记住是哪个样品 |
| } | |
| return true; | 必须 return true 结束 |
| }); |
| 代码(一行一行) | 这一行在干什么 |
|---|---|
| ac.m_writer.changeNode(url).changeAttributeExp("C8_Season_Supplier", exp, isOneTimeExp); | 不直接给值,给一个表达式让系统算。isOneTimeExp = true:只算一次 |
| ac.m_writer.changeNode(ThemeNode.$URL) | |
| .changeAttributeExp("C8_Theme_ColorS", "block(add(C8_Theme_ColorS, ref(\""+Url+"\")))", true); | 往主题的颜色列表里追加一个颜色 |
| ac.m_writer.changeQuery(ac.m_writer.createQuery() | 批量改:先建一个查询 |
| .Node(xml.PROP_TYPE, xml.OP_EQUALS, "ApparelBOM") | 条件 1:类型是 BOM |
| .RefAttr("C8_DataSheetRev_Supplier", xml.OP_EQUALS, "centric:") | 条件 2:供应商为空 |
| .RefAttr("Category1", xml.OP_EQUALS, "C2308", "Child:__Parent__") | 条件 3:父对象的大类是 C2308 |
| ).changeAttribute("C8_DataSheetR", "ref", url); | 查出来的每一条都改这个字段 |
| ac.bRefreshNode = true; | 刷新页面数值 |
| 代码(一行一行) | 这一行在干什么 |
|---|---|
| ac.m_writer.changeNode(url).changeAttribute("C8_Season_SupplierList","reflist",[tURL]); | 替换:列表变成只有 tURL |
| ac.m_writer.changeNode(url).changeAttribute("C8_Season_SupplierList","reflist",[tURL],true,false); | 追加 tURL |
| ac.m_writer.changeNode(url).changeAttribute("C8_Season_SupplierList","reflist",[tURL],false,true); | 移除 tURL |
| cn.changeAttribute("C8_Season_SupplierList","reflist",[tURL]); | 用 cn 改当前这条,三种写法同上 |
Site.xml 里 OnAction="Site.X" 的 X,都写在文件最后的 return { … } 里。函数之间用逗号隔开(知识库踩过坑)。
| 代码(一行一行) | 这一行在干什么 |
|---|---|
| C8_NewProductSample: function(ac) { | 按钮函数 |
| var f = ac.getFormData(); | 拿到弹出表单里用户填的东西 |
| var supplier = f.m_fields[0].getValue(); | 表单第 0 个字段:供应商 |
| var C8_Sample_DemandType_Index = f.m_fields[1].m_control.displayedValue; | 第 1 个字段:需求类型显示的值 |
| var C8_Sample_Type_Index = f.m_fields[2].m_control.displayedValue; | 第 2 个:样品类型 |
| var C8_Sample_DemandType = "C8_SampleDemandType:" + C8_Sample_DemandType_Index; | 拼成枚举完整写法「枚举名:Key」 |
| var C8_Sample_Type = "C8_SampleType:" + C8_Sample_Type_Index; | 同上 |
| var RequestType = f.m_fields[3].getValue(); | 第 3 个:请求类型 |
| var RequestName = f.m_fields[4].getValue(); | 第 4 个:请求名称 |
| var Url = ac.m_urlNode; | 当前点按钮的这一条(款式 Offer)的 URL |
| var styUrl = ac.m_cache.getNode(Url, true).ProductStyle; | 取它对应的款式 |
| var mcnode = session.getLocationNode(); | 当前项目 |
| var newRurl = comm.createUrl("SupplierRequest"); | 给新「供应商请求」生成 URL |
| ac.m_writer.createNode(newRurl, "SupplierRequest"); | 新建这个对象 |
| ac.m_writer.changeNode(newRurl) | 给它填字段 ↓(链式,一路点下去) |
| .changeAttribute("C8_SR_RequestType", "enum", RequestType) | |
| .changeAttribute("Node Name", "string", RequestName) | |
| .changeAttribute("C8_SR_ProductOffer", "ref", Url) | 记住属于哪个款式 Offer |
| .changeAttribute("C8_SR_MCollection", "ref", mcnode.$URL); | 记住属于哪个项目 |
| ac.m_writer.validateNode(newRurl, true); | 校验 |
| var newurl = comm.createUrl("Sample"); | 再建一个「样品」,同样的套路 ↓ |
| ac.m_writer.createNode(newurl, "Sample"); | |
| ac.m_writer.changeNode(newurl).changeAttribute("C8_Sample_Supplier","ref",supplier[0]) … | 填供应商、需求类型、样品类型、所属请求、所属款式…… |
| ac.m_writer.validateNode(newurl, true); | |
| ac.m_writer.changeNode(mcnode.$URL).changeAttribute("C8_MC_Requests","refvector",[newRurl],true); | 项目的「请求列表」追加这个新请求(true = 追加) |
| ac.m_writer.changeNode(Url).changeAttribute("C8_MCP_Requests","refvector",[newRurl],true); | 款式 Offer 的请求列表也追加 |
| ac.m_writer.changeNode(newRurl).changeAttribute("C8_SR_ProductSample","refvector",[newurl],true); | 请求下面挂上样品 |
| ac.m_writer.changeNode(Url).changeAttribute("C8_MCP_Sample","refvector",[newurl],true); | 款式 Offer 下也挂样品 |
| ac.m_writer.execute({sync:true}); // 提交数据 | 前面所有 changeNode 都只是「记在本子上」,这一句才真正提交 |
| ac.widget.refreshData(); //刷新页面 | |
| return true; //必须是以return true 结束 | |
| }, | 注意逗号:后面还有别的按钮函数 |
| 代码(一行一行) | 这一行在干什么 |
|---|---|
| ac.m_formData.getSelectedValue(); | 取选中的值 |
| ac.m_formData.m_fields; | 所有字段 |
| ac.m_formData.getFieldValue('季节'); | 按字段名取值 |
| ac.m_formData.m_fields[2].m_control.displayedValue; | 取 ref 类型字段的 URL |
| 写法 | 干什么 |
|---|---|
comm.createUrl("类型") | 给新对象生成 URL |
ac.m_writer.createNode(url, "类型") | 新建对象 |
ac.m_writer.changeNode(url) | 选中要改的对象,后面接 .changeAttribute |
.changeAttribute(字段, 类型, 值) | 改一个字段 |
.changeAttribute(字段, "reflist"/"refvector", [url], true, false) | 列表追加(false,true = 移除) |
.changeAttributeFlag(字段, xml.ATTR_FLAG_NOEDIT, true, true) | 设成不可编辑 |
.changeAttributeExp(字段, 表达式, true) | 用表达式算值 |
ac.m_writer.changeQuery(查询).changeAttribute(…) | 查出来的批量改 |
ac.m_writer.callMethod(模块, 方法).addParameter(…) | 调 C8 内置方法 |
ac.m_writer.validateNode(url, true) | 校验对象 |
ac.m_writer.execute({sync:true}) | 提交(按钮/PostAction 里要写) |
ac.bRefreshNode = true / ac.widget.refreshData() | 刷新 |
ac.m_writer.execute({sync:true});知识库验证过的审核按钮只写了 ac.bRefreshNode = true 也生效了。两者区别待问| 代码(一行一行) | 这一行在干什么 |
|---|---|
| block( | 把多条表达式包成一块,依次算 |
| tempn = 0, | 赋值 |
| tempn1 = tempn+99/3*10-29-0.0-1, | 先乘除后加减(跟数学、Java 一样) |
| tempn1>tempn, tempn1>=tempn, tempn1<tempn, tempn1<=tempn, | 比较,结果是 true/false |
| abs(tempn-tempn1), | 绝对值 |
| integer(tempn1-0.5), | 强制舍去小数取整 |
| integer(tempn1-0.5).double(), | 转成小数类型 |
| round(tempn1-0.15, 0.1) | 四舍五入保留一位小数 |
| ) |
表达式用在 Site.xml 的 Rule Exp="…" 和 Site.js 的 changeAttributeExp 里。Site.xml 里见过的还有:attr('字段') 取值、currentRoleName() 当前角色、nowTime() 现在、date(0) 空日期、rightstr(s,5) 右边 5 个字、thisNode() 当前对象。
| 代码(一行一行) | 这一行在干什么 |
|---|---|
| api.watchAttribute( | 监听开始 |
| "Style", | 盯的是款式 |
| "C8_Style_Reviewer", | 盯的字段:审核人 |
| function (ac, cn, node, id) { | 审核人一变就执行 |
| if (ac.newValue && ac.newValue !== "centric:") { | 新值不为空,并且不是「空引用」centric:(清空审核人时不触发) |
| ac.m_writer | 写数据 |
| .changeNode(node.$URL) | 改的是当前这个款 |
| .changeAttribute( | 改一个字段 |
| "C8_Style_ReviewStatus", | 审核状态 |
| "enum", | 下拉类型 |
| "C8_Style_ReviewStatus:Reviewing" | 值:审核中。必须带枚举名,只写 "Reviewing" 不行(知识库已纠正过) |
| ); | |
| ac.bRefreshNode = true; | 刷新 |
| } | |
| return true; | |
| } | |
| ); |
| 代码(一行一行) | 这一行在干什么 |
|---|---|
| return { | |
| C8_onActionProductApproval: function (ac) { … }, | 原来就有的按钮函数,不能删;后面的逗号必须有 |
| C8_onActionReviewApproval: function (ac) { | 新加的,名字对上 Site.xml 的 OnAction="Site.C8_onActionReviewApproval" |
| ac.m_writer | 写数据 |
| .changeNode(ac.m_urlNode) | 改的是「点了按钮的那一行」 |
| .changeAttribute("C8_Style_ReviewStatus", "enum", "C8_Style_ReviewStatus:Approved"); | 状态 = 已通过 |
| ac.bRefreshNode = true; | |
| return true; | |
| } | 最后一个函数后面不要逗号 |
| }; |
注意 node.$URL(监听里用)和 ac.m_urlNode(按钮里用):都是「当前这条」的 URL,只是来源不同。
出处:带教同事《箱包属性信息整理 V1.0》、H厂.zip、会议纪要 10-08、课件 5《前端开发 xml》、课件 6《js》、知识库 C8配置规则_7.8.1。