☰ Java 复习 · C8 实战总图更新 10-13 15:41

⑤ Site.js 全讲

课件 6 全部:文件 8 段结构、默认值、监听、按钮、写数据的全部方法

全部来自课件《6、js》36 页(含截图里的代码)+ 知识库验证过的例子。Site.js 是 JavaScript,不是 Java,但语法很像。

0 · 一个 Site.js 文件的 8 段(课件第 14 页)

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 你都能找到「我要改的东西在第几段」。

① 头部引入(课件第 15 页)

代码(一行一行)这一行在干什么
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 一个意思。平时不用动,用到新工具才往两边同时加。

② 全局变量(课件第 16 页)

代码(一行一行)这一行在干什么
var currentuser = session.getCurrentUser().$URL;当前登录用户的 URL(唯一地址)
var userName = session.getCurrentUser().$Name;当前用户名字
var istyt = "C24229"; //临时款类型把某个固定对象的编号存起来,后面反复用
var istyt1 = "C738"; //正常款类型

写在 define 里面、所有函数外面 → 整个文件都能用。像 Java 类里的成员变量。

③ 跨对象编辑权限(课件第 23 页)

代码(一行一行)这一行在干什么
api.setRelationshipAccess("MCProduct", "C8_MCP_Sample", true);允许在 MCProduct(款式 Offer)页面上,直接编辑它关联的 C8_MCP_Sample(样品)的字段。默认是不能跨对象改的

④ 隐藏带版本对象的按钮(课件第 24 页)

代码(一行一行)这一行在干什么
//隐藏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");隐藏「放弃」
课件注意:对管理员不起效果。你用管理员账号测,按钮还在,不代表写错了。

⑤ 自定义方法(课件第 19 页)

弹一个提示框

代码(一行一行)这一行在干什么
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;返回
}

⑥ 页面订阅:新建表单的默认值 / 只读 / 隐藏(课件第 20、25 页)

外壳

代码(一行一行)这一行在干什么
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; };打开的是「新建款式」表单时做……
}
});

课件第 25 页:新建款式时开发类型默认值,逐行

代码(一行一行)这一行在干什么
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")去掉表单按钮
课件注意:「按钮事件开发带表单,保存&继续不会触发事件方法」——所以上面要把这两个按钮去掉,防止用户绕过逻辑。

课件第 20 页:先查再默认(新建主题时,子类型默认「趋势」)

代码(一行一行)这一行在干什么
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) 才拿到对象。

⑦ 属性值修改的监听 watchAttribute(课件第 21、26、27 页)

参数课件原话大白话
ac可通过该对象进行增删改操作「操作员」:ac.m_writer 写数据,ac.newValue 刚填的新值
cn可通过该对象修改 Sample 的字段当前这条的「修改器」
node可通过该字段 Sample 的所有字段当前这条数据,node.$URL、node.$Name、node.字段
id—被改的字段 Id
课件注意:图片、文件等类型的属性值修改监听不到。

课件第 27 页:样品上勾「下发 BOM」→ 自动建一张 BOM,逐行

代码(一行一行)这一行在干什么
//制作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 结束
});

课件第 28 页:表达式修改、查询后批量修改

代码(一行一行)这一行在干什么
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;刷新页面数值

reflist 的三种改法(课件第 26 页)

代码(一行一行)这一行在干什么
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 改当前这条,三种写法同上

⑧ 按钮事件:return 块(课件第 22、29–31 页)

Site.xml 里 OnAction="Site.X" 的 X,都写在文件最后的 return { … } 里。函数之间用逗号隔开(知识库踩过坑)。

课件第 30 页:在款式 Offer 上点按钮 → 按表单建「供应商请求」和「样品」,逐行

代码(一行一行)这一行在干什么
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 结束
},注意逗号:后面还有别的按钮函数

表单取值的几种写法(课件第 31 页)

代码(一行一行)这一行在干什么
ac.m_formData.getSelectedValue();取选中的值
ac.m_formData.m_fields;所有字段
ac.m_formData.getFieldValue('季节');按字段名取值
ac.m_formData.m_fields[2].m_control.displayedValue;取 ref 类型字段的 URL

写数据的方法总表(课件 6 全部出现过的)

写法干什么
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()刷新
什么时候要写 execute:课件说 PostAction 和按钮里要 ac.m_writer.execute({sync:true});知识库验证过的审核按钮只写了 ac.bRefreshNode = true 也生效了。两者区别待问

C8 表达式(课件第 10 页「纠正」)

代码(一行一行)这一行在干什么
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() 当前对象。

知识库里虚拟机跑通的例子 已验证 08-05

⑦ 监听:选了审核人 → 状态自动变「审核中」

代码(一行一行)这一行在干什么
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;
}
);

⑧ 按钮:点「审核通过」→ 状态变 Approved

代码(一行一行)这一行在干什么
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,只是来源不同。

← ④ Site.xml 全讲⑥ en.js 全讲 →

出处:带教同事《箱包属性信息整理 V1.0》、H厂.zip、会议纪要 10-08、课件 5《前端开发 xml》、课件 6《js》、知识库 C8配置规则_7.8.1。