前言
在移动端的功能开发时,会沉淀很多通用的行业包标准功能。在实际的项目交付时,会经常出现同一个功能,但复制了多份源码,导致【标准版本】和【交付版本】很难实现代码同步,代码维护困难。
移动端底座的核心功能就是实现同一个功能只保留一份源码。同时,可以动态组合业务功能的【标准版本】支撑项目交付,也可以增量添加项目交付时的【定制化功能】
移动端底座架构设计

移动端底座构建流程

一、环境准备
1.1 基础环境
node环境安装
node.js
具体配置请参照文档:本地部署运行-node
npm
具体配置请参照文档:本地部署运行-npm
yarn
具体配置请参照文档:本地部署运行-yarn
1.2 开发环境
本地
hosts配置
具体配置请参照文档:系统快速上手-配置本地 hosts
npm配置
具体配置请参照文档:系统快速上手-npm 配置准备工作
sie-cli脚手架安装
全局安装 sie-cli 脚手架
npm install -g sie-cli
查看 sie-cli 版本,确认安装成功,使用大写 -V
sie-cli -V

二、业务包开发
提示
内部开发使用现有的gitLab项目,已经创建好了移动端底座,无需再手动创建,可以跳过步骤2.1和步骤2.2
标品项目,主要包含【
LED】【标品SMT】【标准行业包功能PUBLIC】【标准工治具功能TOOL】:GIT地址:
http://192.168.175.208/dme/mbm-mom-app-ui.git项目名:
mbm-mom-app-ui分支名:
dev
定制化项目,主要包含【国星
SMT】GIT地址:
http://192.168.175.208/ns-odc/mbm-mom-nationstar-app.git项目名:
mbm-mom-nationstar-app分支名:
dev-package
2.1 appCode 生成
打开管理系统中的业务包管理页面,页面路径是【DEVOPS-移动端管理-业务包管理】,我们要操作的内容位于“业务包管理”面板。

业务包管理面板中的业务包名称由一级菜单和二级菜单构成,例如:《锡膏管理》下包含了“回温”、“粘度测试”、“锡膏查询”、“搅拌”、“报废”、“冷藏” 6 个功能页面。
假设我们现在挑选了“回温”、“搅拌”,“开封”、“密封”作为我们新项目里已有的功能,在对应的位置勾选上,然后点击“生成 AppCode” 按钮,会出现一个确认的弹窗。

点击“确认”按钮后,需要填写相关信息来生成 AppCode。

应用名称:前端团队在 DCloud 官网创建的可选应用列表,选择你认为合适的一个;
描述:备注;
点击“保存”按钮,apk 会生成一个唯一的 appCode,点击“复制”按钮,留待后续生成二开底座或者 apk 时使用,至此动态组合业务包生成 appCode 完成。
2.2 创建移动端开发底座
回顾上一个步骤(动态组合业务包生成 appCode),如果你记得复制 appCode 的话,appCode 已经存在于你的剪切板中。不记得也没有关系,可以去【DEVOPS-移动端管理-业务包管理】标签页,点击“生成 apk”按钮,会打开一个弹窗,里面有一个选项是“从历史记录生成”。

打开选项,你会看见很多已经存在的 appCode,这时候你要发挥你的记忆力,找到对应的标题,可以逆推出你所需要的 appCode。

选择一个空白的文件夹打开命令行程序(以 Windows 操作系统为例,使用 cmd.exe 进行命令输入操作)
创建移动端开发底座 sie-cli init当命令开始执行后,会有部分配置提供选择,例如:
项目模板

项目名称(项目名)

appCode(在此处粘贴上你上一步复制的 appCode)

站点信息(通俗理解为服务器 ip 地址)

回车之后,脚手架会自动拉取基座内容,等待安装结束。

成功标志!

底座目录结构 demo-app ├─public ├─src │ ├─api │ │ └─modules │ ├─config │ ├─const │ ├─pages │ │ ├─public │ │ │ ├─msd-material-operate-opening │ │ │ └─msd-material-operate-sealing │ │ └─smt │ │ ├─solder-paste-mixing │ │ └─solder-paste-warming │ ├─static │ │ ├─img │ │ └─style │ ├─store │ │ └─modules │ ├─utils │ └─views │ ├─demo │ ├─index │ │ └─components │ ├─systemLog │ └─user │ └─api └─unpackage └─res └─icons
2.3 业务包创建与开发
2.3.1 创建业务包
进入基座所在文件夹
cd demo-app
执行脚手架的创建业务包命令
sie-cli createPage
以“MSD管理”下的“品质确认”作为例子
产品线:public
包名:msd-quality-confirm
分组名称:MSD管理
分组编码:msd-management
菜单名称:品质确认
版本:0.0.1
描述:msd-品质确认
提示
可以直接把
产品线当作文件夹,比如国星的客开项目,产品线一般直接填写nationstar即可分组编码和分组名称填写时请从MBM的栏目管理中查找,如果没有对应的分组,请提前创建
当命令开始执行后,会有部分配置需要填写,例如:
产品线:同一产品归类在一个文件夹下;
业务包名称:烤串语法,输入后回车会立刻创建对应的文件夹;
分组名称:可以理解为一级菜单名称,必填;
分组编码:可以理解为一级菜单的编码,必填;
菜单名称:必填;
版本:格式为【Major(主版本号).Minor(次版本号).Patch(修订版本号)】;
描述:package.json 里的 description;
关键词:package.json 里的 keywords;
作者:package.json 里的 author;

创建了功能页后的目录结构,可以看出`public`下增加了`msd-quality-confirm`文件夹
demo-app
├─public
├─src
│ ├─api
│ │ └─modules
│ ├─config
│ ├─const
│ ├─pages
│ │ ├─public
│ │ │ ├─msd-material-operate-opening
│ │ │ ├─msd-material-operate-sealing
│ │ │ └─msd-quality-confirm
│ │ │ └─api
│ │ └─smt
│ │ ├─solder-paste-mixing
│ │ └─solder-paste-warming
│ ├─static
│ │ ├─img
│ │ └─style
│ ├─store
│ │ └─modules
│ ├─utils
│ └─views
│ ├─demo
│ ├─index
│ │ └─components
│ ├─systemLog
│ └─user
│ └─api
└─unpackage
└─res
└─icons
2.3.2 业务包开发
进入品质确认的文件夹
cd src\pages\public\msd-quality-confirm
使用编辑器打开文件夹(建议使用vscode)
code .
开发“品质确认”需要以下几个文件。
/api/index.js
需要注意的地方:
(1)使用基座进行开发时,用基座提供的请求接口
(2)页面接口要加上备注,方便后续维护
import request from "@/api/index";
const basePath = "/smt/msd-material-app-operate";
/**
* 取出干燥柜--检查物料信息
* @param data
* @returns {Promise}
*/
export const checkMaterialWhenTakeOut = data => {
return request({
url: `${basePath}/app/checkMaterialWhenTakeOut`,
method: "GET",
data
});
};
/**
* 品质确认
* @param data
* @returns {Promise}
*/
export const qualityConfirm = data => {
return request({
url: `${basePath}/app/qualityConfirm`,
method: "GET",
data
});
};
/components/usedRecord.vue
<template>
<view class="used-record-item">
<view>
<view>
<view class="half">
<view class="label-value w-50">
<view class="label">
<text>物料条码</text>
</view>
<view class="value">
<text>{{ data.barCode }}</text>
</view>
</view>
<view class="label-value w-50">
<view class="label">
<text>编码</text>
</view>
<view class="value">
<text>{{ data.materialCode }}</text>
</view>
</view>
</view>
<view class="half">
<view class="label-value w-50">
<view class="label">
<text>名称</text>
</view>
<view class="value">
<text>{{ data.materialName }}</text>
</view>
</view>
<view class="label-value w-50">
<view class="label">
<text>数量</text>
</view>
<view class="value">
<text>{{ data.qty }}</text>
</view>
</view>
</view>
<view class="half">
<view class="label-value w-50">
<view class="label">
<text>暴露时长</text>
</view>
<view class="value">
<text>{{ data.remainderExposureHour }}</text>
</view>
</view>
<view class="label-value w-50">
<view class="label">
<text>剩余寿命</text>
</view>
<view class="value">
<text>{{ data.residueExposureHour }}</text>
</view>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
export default {
name: "used-record",
props: {
data: {
type: Object,
default: () => {}
}
},
computed: {},
methods: {}
};
</script>
<style lang="scss" scoped>
@import "../../../../uni.scss";
.used-record-item {
border: 1px solid $uni-border-color;
padding: 0 20rpx;
margin: 10rpx 0;
font-size: 28rpx;
.u-row {
margin: $uni-spacing-col-sm 0;
}
.label {
width: 130rpx;
}
.half {
display: flex;
.w-50 {
flex: 2;
width: 0;
}
}
}
</style>
/index.vue
<template>
<view class="station-pass-collection-index">
<view>
<u-sticky class="sticky">
<view class="scan-bar">
<app-scan-bar
placeholder="请扫描物料条码"
v-model="barCode"
:focus="inputFocus"
@blur="handleBlur"
@search="handleSearch"
@scan="handleSearch"></app-scan-bar>
</view>
</u-sticky>
</view>
<view>
<view>
<view class="header">
<view class="title">扫描明细</view>
<view class="title">记录 {{ recordSize }}</view>
<view class="title" style="margin-right: 20px">数量 {{ totalQty }}</view>
</view>
</view>
<view class="list">
<usedRecord v-for="(item, index) in usedRecordList" :key="index" :data="item" />
</view>
</view>
<view class="bottom-fixed">
<view>
<u-button type="primary" text="重置" @click="reset"></u-button>
</view>
</view>
</view>
</template>
<script>
import { qualityConfirm } from "./api/index";
import { $toast } from "@dme/uni-toast";
import usedRecord from "./components/usedRecord.vue";
import appScanBar from "@dme/app-scan-bar";
export default {
name: "user",
components: {
usedRecord,
appScanBar
},
data() {
return {
usedRecordList: [],
inputFocus: true,
isShow: true,
barCode: "",
recordSize: 0,
totalQty: 0
};
},
methods: {
reset() {
this.recordSize = 0;
this.totalQty = 0;
this.usedRecordList = [];
this.barCode = "";
this.inputFocus = true;
},
handleBlur() {
this.inputFocus = false;
},
async handleSearch(v) {
this.barCode = v;
if (this.barCode == "") {
$toast(`请先扫描产品标签`);
return;
}
try {
const res = await qualityConfirm({
barcode: this.barCode
});
console.log("res :>> ", res);
if (res.code == "0") {
this.recordSize++;
this.totalQty = this.totalQty + res.data.qty;
this.usedRecordList.unshift({
barCode: this.barCode,
materialCode: res.data.materialCode,
materialName: res.data.materialName,
qty: res.data.qty,
remainderExposureHour: res.data.remainderExposureHour,
residueExposureHour: res.data.residueExposureHour
});
} else {
$toast(res.msg);
}
} catch (err) {
console.error("品质确认失败");
}
this.barCode = "";
this.inputFocus = true;
}
},
onNavigationBarButtonTap() {
$toast("点击了右上角按钮");
}
};
</script>
<style lang="scss" scoped>
@import "../../../uni.scss";
.station-pass-collection-index {
$padding-width: $uni-spacing-col-base;
$margin-width: $uni-spacing-col-base;
.sticky {
background-color: fff !important;
.scan-bar {
width: 100%;
margin: $margin-width 0;
}
.title {
color: $uni-color-primary;
margin-top: $margin-width;
padding: 0 $padding-width;
}
}
.header {
display: flex;
flex-direction: row;
justify-content: space-between;
margin-left: 20px;
}
.bottom-fixed {
position: fixed;
right: 0;
bottom: 0;
}
}
</style>
<!-- TODO:等文档补全后,将这里替换具体的组件文档 -->
为了方便开发,前端团队创建了一些基础库给大家使用,分别是:
接口开发用:@dme/template-app-utils-request
消息弹窗: @dme/uni-toast
扫码框组件:@dme/app-scan-bar
底部选择组件:@dme/app-bottom-picker
右侧打开侧边栏列表组件:@dme/app-cell-vue
图片组件:@dme/app-image-vue
等等等等...
在 demo-app/src/package.json 可以找到这些基础库
"dependencies": {
"@dme/app-bottom-picker": "^0.0.0-4",
"@dme/app-cell-vue": "^0.0.0-6",
"@dme/app-image-vue": "^0.0.0-6",
"@dme/app-list-vue": "^1.0.0-0",
"@dme/app-nav-bar": "^0.0.0-5",
"@dme/app-picker-vue": "^0.0.1-10",
"@dme/app-popup-picker": "^0.0.0-11",
"@dme/app-scan-bar": "^0.0.0-11",
"@dme/app-slide2del": "^0.0.0-4",
"@dme/app-table-vue": "^0.0.0-8",
"@dme/app-upload-dme": "^1.0.0-0",
"@dme/datetime": "^0.0.0-6",
"@dme/style-app": "^0.0.1-0",
"@dme/template-app-style-global": "^0.0.0-6",
"@dme/template-app-utils-loading": "^0.0.0-1",
"@dme/template-app-utils-request": "^0.0.0-5",
"@dme/uni-clog": "^0.0.0-3",
"@dme/uni-modal": "^0.0.0-1",
"@dme/uni-toast": "^0.0.0-1",
"@dme/utils-form": "^0.0.0-1",
"@dme/utils": "^0.0.1-1",
"@dme/utils-app": "^0.0.3",
}
2.3.3 详情页开发
如果业务包有详情页,需要额外手动创建详情页,主要有三个步骤:
在【
业务包】根目录,新建详情页文件,命名规则不做限制,参考命名为indexNext.vue或者detail.vue

在【
业务包】根目录,修改配置文件package.json,以indexNext.vue为例,确保exportPath和exports的信息匹配

完成这前步后,其实已经完成了业务包开发,但是为了本地开发调试方便,需要在
pages.json页面给新建的详情页添加路由,需要保证path字段的值是一致的。并且需要特别注意,pages.json的path首部会少一个/

2.4 项目运行验证
依赖安装
回到项目根目录,执行依赖安装命令,如果有疑问请仔细阅读项目根目录的README.md
cd 到项目根目录中
npm run ins:all

项目运行
npm run dev

三、业务包发布
3.1 业务包打包
业务包开发完成后,进入【业务包】根目录,执行打包命令。
提示
举个例子,过站采集的业务包根目录为:src\pages\public\station-pass-collection 可以通过检查对应的目录是否有package.json文件,来判断当前目录是否有问题
cd src\pages\public\station-pass-collection
执行打包命令,对应修订版本号更新
npm run pack:z
根据实际需要也可以执行以下命令
npm run pack:x 对应主版本号更新
npm run pack:y 对应次版本号更新

如下图,业务包根目录出现*.tgz文件的时候证明打包成功了

3.2 业务包发布
上传业务包

业务包默认信息
业务包有四项主要的默认信息,分别是功能菜单编码、功能菜单名称、分组名称,分组编码
功能菜单编码:业务包的唯一ID,不可变
功能菜单名称:可变,数据ID是
功能菜单编码
分组名称:可变,数据ID是
功能菜单编码
分组编码:可变,数据ID是
功能菜单编码

业务包信息变更
在MBM系统的栏目管理菜单中,可以基于功能菜单编码,对其他三项数据进行修改
提示
针对其他三项数据的修改只做覆盖,一旦删除栏目管理对应的值后,业务包依旧会使用默认信息
修改前
MBM系统栏目管理如图

修改
功能菜单名称

修改
分组名称和分组编码

修改后
MBM系统栏目管理如图

修改后
DEV环境业务包管理如图

3.3 代码提交【切记!!】
确保业务包发布完成后,需要将本地代码提交到gitLab,同步业务包的版本信息,防止协同开发时版本冲突
提示
内部开发使用现有的gitLab项目
标品项目,主要包含【
LED】【标品SMT】【标准行业包功能PUBLIC】【标准工治具功能TOOL】:GIT地址:
http://192.168.175.208/dme/mbm-mom-app-ui.git项目名:
mbm-mom-app-ui分支名:
dev
定制化项目,主要包含【国星
SMT】GIT地址:
http://192.168.175.208/ns-odc/mbm-mom-nationstar-app.git项目名:
mbm-mom-nationstar-app分支名:
dev-package
3.4 流水线运行
在
业务包管理菜单中,点击从历史选择业务包,选中其中一个历史记录,比如叫标品SMT【全部功能】,然后点击确定按钮

上次历史记录包含的所有业务包会自动反选到当前页面,确认勾选的业务包和对应的版本无误后,点击
生成AppCode按钮,选择对应的应用名称和写入描述,比如叫标品SMT【全部功能】,最后点击保存按钮,复制生成的AppCode


找到对应的流水线,点击
运行按钮后,输入获取到的AppCode,即可完成功能发布
提示
AppCode代表的是指定版本业务包的集合,可以同时使用于dev或者联建等任意环境

3.5 手动引入标品业务包
提示
手动引入业务包仅用于本地功能测试,并不影响APK打包
手动引入标品业务包主要参照以下前三个步骤:
在
业务包管理菜单中,勾选对应功能模块,点击生成AppCode按钮,任意选择一个应用和填写描述后,点击保存,并继续下一步,点击复制按钮复制生成的appCode

任意选择一个
demo的目录结构,执行项目生成命令,根据提示填写信息,并输入上一步获取的appCode,可以参照[2.2 创建移动端开发底座](2.2 创建移动端开发底座)
sie-cli init

打开生成的项目,将四部分(文件
package.json、pages.json、menuList.json和文件夹pages)内容复制到目标项目中,即可实现将标品业务包引入到实施项目中,以国星实施项目为例:
文件
package.json(注意此处的package.json为src下的文件)
生成的demo项目:

修改后的国星实施项目:

提示
npm包版本设置为latest意味着只会安装最新版本npm包版本设置为^开头意味着主版本不变,但是次版本和补丁版本可以匹配当前版本或者更高的任意版本,都符合要求,具体选择哪个版本根据依赖间的关系自行调整,只要不低于当前版本都是符合的
文件
pages.json
生成的demo项目:

修改后的国星实施项目:

提示
由于这里还包含了一个详情页,所以一共是3个页面,一定要注意
文件
menuList.json
生成的demo项目:

修改后的国星实施项目:

提示
如果对应的菜单分组
不存在,则复制整个大红框的所有内容如果对应的菜单分组
已存在,则只需要复制小红框的功能菜单即可
文件夹
pages
生成的demo项目:

修改后的国星实施项目:

提示
如果对应的
public文件夹不存在,则复制整个文件夹如果对应的
public文件夹已存在,则只需要复制其中的子文件夹即可
至此,手动引入标品业务包所有工作完成,安装依赖后运行查看效果
进入 src 目录安装依赖
cd src
yarn install
回到项目根目录运行项目
cd ..
npm run dev
四、APK 打包发布
4.1 APK 打包
APK打包生成
在
业务包管理菜单中,勾选对应功能模块,点击生成APK按钮

勾选的所有功能会带到
业务包的表格中,按顺序选择应用名称和运行环境后,会自动带出推荐的下一个版本号,也可以手动修改。按要求填写发版内容,并按需填写备注后,即可点击保存按钮,点击后会触发APK自动生成的任务

提示
表单中从历史记录生成可以选择之前勾选的业务包记录,减少重复勾选业务包的工作
APK生成结果
在
APP管理菜单中,可以看到上一步生成APK任务的生成状态

APK生成日志查看

4.2 APK 下载验证
下载生成的
APK

在模拟器中验证
APK功能
APK安装成功

输入账号密码后登录

确认功能菜单是跟
4.1-1勾选的业务包一致

任意点击一个功能菜单,并测试功能是否正常

4.3 APK 发布
功能确认无误后,在APP管理菜单中,找到对应的APK生成记录,选择更多,并点击发布按钮


五、异常情况处理
5.1 APP 日志查看
登录页日志查看
连续点击6次登录页logo,可以打开日志,便于检查问题


菜单页日志查看


5.2 基于 HbuilderX 的模拟器调试
如果打包APK后问题较多,可以在本地生成一个项目,通过HbuilderX的模拟器调试,方便定位问题
获取指定的
appCode


生成本地项目
任意选择一个demo的目录结构,执行项目生成命令,根据提示填写信息,并输入上一步获取的appCode
sie-cli init

进入项目根目录,执行依赖安装命令


打开模拟器,获取
ADB调试端口


使用
HbuilderX的打开生成的项目,并配置模拟器调试端口


回到项目中,按步骤运行项目到模拟器中
选择
运行到Android App基座

选中模拟器,并默认使用自定义基座,点击
运行按钮

提示
如果HbuilderX无法检测到模拟器,可以尝试重启HbuilderX

正常运行项目到模拟器中,并在控制台显示调试信息

六、常见问题
6.1 代码报错
引用组件路径错误
提示
@符号一般代表目录src
错误信息摘要:
Module not found: Error: Can't resolve '@/components/pickerVue/index.vue'

错误原因分析:在移动端底座中,所有的公共组件都剥离到组件库中,对应的目录架构
src中不存在components文件夹,所以会报错,无法找到并解析对应的模块解决方案:
【推荐】使用包引入方式,替代原来的引入方式,将
import popupPicker from "@/components/popupPicker/index.vue";改成import popupPicker from "@dme/app-popup-picker";

【不建议】使用相对路径替代原来的引入方式,但需同时复制对应的
组件到指定目录中【即业务包根目录】

页面滚动条无法滚动
错误信息摘要:
无
错误原因分析:在移动端底座中,为了适配多场景使用需求,将原来默认
瀑布流的布局修改成为了flex布局瀑布流由于没有设置任何样式,所以天生自带滚动条flex布局则是将整个页面进行了分块,每个块都可以单独做滚动好处就是可以适配更多场景
坏处就是天生的滚动条是无效的,在需要滚动的
区块处,需要手动添加滚动样式
解决方案:
【推荐】在最外层增加
page-flex样式类,在需要使用的滚动的区块处,添加滚动的样式类cus-overflow-auto

【不建议】在最外层直接增加
cus-overflow-auto样式类,其他不变,这种方式可以恢复成类似原来瀑布流的布局,虽然简单,但是相对没那么灵活

6.2 代码优化
标准的移动端组件库已全局引入,符合规则的组件可以直接使用,而无需单独声明
在
pages.json中已经统一注册了u-和app-开头的两种格式的组件,意味这uview框架的组件和内部开发的移动端组件库都可以直接使用,减少代码量,改造如下图,将原有的组件<popupPicker />改成<app-popup-picker />,其他不变

以上修改完成后,引入组件和注册组件的代码可以省略,下图红框中的代码可以删除

提示
依此类推,其他u-和app-开头的组件可以使用相同的方式优化
6.3 注意事项
在移动端底座中,
onShow、onNavigationBarButtonTap等生命周期需要通过新的方式使用
可用的生命周期包含以下9个:
onShow、onReady、onHide、onUnload、onPullDownRefresh、onReachBottom、onPageScroll、onResize、onNavigationBarButtonTap使用时需要写入
methods中,并在前面增加$符号


在移动端底座中,不支持
onLoad生命周期,需要获取页面跳转的option参数可以通过this.onLoadOption变量直接获取,数据跟option一致

七、问答知识库
针对
善治提到的问题如何快速定位BUG文件目录?
提示
熟悉项目的可以直接找,如果找不到可以参照本案例
通过这种方式找的原因是,
业务包名称和业务包分组有可能做了变更,不一定能直接找到,相关文档请参照3.2 业务包发布的3. 业务包信息变更
解决方案:
【推荐】通过页面
URL定位业务包功能
第一步,确认功能名称,比如叫
工治具维修

第二步,打开
DEV环境,点击进入具体的业务包功能,查看具体的URL路径,比如/pages/tool/repair-execute/index,这个路径就是项目文件的所在路径

第三步,打开对应项目,根据第二步获取的路径找到对应的功能

通过业务包的
唯一ID定位业务包功能
第一步,确认功能名称,比如叫
工治具维修

第二步,在
业务包管理中,查询工治具维修,并找到对应的包名@dme/app-tool-repair-execute

第三步,打开对应项目,并全局搜索
@dme/app-tool-repair-execute

提示
不清楚业务功能具体对应哪个项目的,可以参照二、业务包开发
以上,即可定位具体文件目录
补充说明:
由于本地开发中的
业务包名称是写死的,而线上的是可以动态变更的,所以如果想找到本地代码中的业务包名称,可以在目录src\const\menuList.json中进行搜索搜索的信息是
第二步中的编码字段tool-repair-execute-index,搜索结果中对应的title字段维修执行就是本地开发中看到的具体业务功能名称

如何根据
旧的业务功能名称如何快速找到修改后对应的业务包名称?
提示
本案例跟上一个案例是反过来的,上个案例是“新”找“旧”,这个案例是“旧”找“新”
第一步,确认“原始”的功能名称,比如叫
离线备料第二步,打开对应项目,在目录
src\const\menuList.json中搜索离线备料, 搜索结果中对应的code字段smt-management-offline-material-preparation-index就是功能菜单的唯一编码,具体请仔细查阅3.2 业务包发布的2. 业务包默认信息

第三步,在
业务包管理中搜索smt-management-offline-material-preparation-index即可找到“翻新”的功能名称
