Skip to content

在途管理与打包发布(一) ​

本页对应 在途管理与打包发布,共 8 段视频,约 1 小时 7 分。

学习目标 ​

  • 能根据路由与目录结构定位“在途交付”页面,并确认地址栏中存在任务 ID 后再发起交付操作。
  • 能完成交付单据与现场照片的上传,使用计算属性将上传结果二次加工为仅包含 URL 的接口参数。
  • 能封装交付接口并绑定表单提交事件,提交前通过计算属性控制按钮可用状态,提交后检查请求参数与响应结果并完成页面跳转。
  • 能根据任务状态值切换列表中的“交付/回车登记”入口及跳转参数,并在详情页按状态条件展示提货、交货对接人及图片信息。
  • 能实现回车登记页出车时间与回车时间的接收、回显与提交,确保两个入口均正确传递 transportId 和出车时间参数。
  • 能抽取并复用“是/否选项”子组件,完成组件引入、模板替换、样式迁移,并验证选项交互与显示隐藏效果。

知识点 ​

本节目标:先理解安卓证书,再准备 JRE 环境 ​

安卓证书是一种数字签名技术。安卓系统要求安装到系统上的 App 必须使用证书进行签名。签名相当于 App 的“身份证书”,用来证明 App 的归属者和合法性。安卓证书的生成规则由 Google 规定,由开发者自助生成且免费。

为什么需要:没有签名,App 无法按 Android 的规则被系统认可;签名还能帮助识别 App 来源,并防止安装包被随意篡改。
怎么想:把证书理解成“身份证 + 防篡改封条”。本节先不急着生成证书,而是先准备生成证书所依赖的 Java 运行环境。
怎么做:按笔记顺序安装 JRE、配置环境变量、验证 JRE 是否可用。
易错点:不要把证书当成账号密码;不要混用调试证书和发布证书;环境变量没配好时,后续命令会直接找不到。

安装 JRE 环境 ​

为什么需要:后续生成、管理安卓证书会用到 Java 工具链,因此需要先有可用的 JRE 环境。笔记明确要求先安装 JRE。
怎么想:安装时重点不是一路“下一步”,而是记住安装目录。笔记特别提示:默认安装到 C:\Program Files\Java\jre-1.8\bin 目录,因此后面配置环境变量时要用到这个路径。
怎么做:运行 JRE 安装程序,按向导完成安装,并记录实际安装路径。

JRE 安装向导

选择 JRE 安装路径

JRE 安装完成

易错点:安装路径不要随手改到自己记不住的位置;如果系统位数与安装包不匹配,应先换正确安装包;安装完成后不要马上关掉记录路径的窗口。

配置环境变量 ​

为什么需要:只在图形界面安装完 JRE 还不够,命令行工具需要从环境变量里找到 Java 相关命令。
怎么想:环境变量配置的核心是“让系统知道 Java 装在哪里”。笔记中的默认路径包含 bin,因此配置时要确保路径与实际安装目录一致。
怎么做:打开系统环境变量设置,新建或确认 JAVA_HOME,再编辑 Path,把 JRE 的 bin 目录加入进去。若按笔记默认路径,就是 C:\Program Files\Java\jre-1.8\bin 这一层对应的路径。

打开环境变量设置

新建环境变量

编辑 Path 变量

确认环境变量配置

易错点:Path 里不要删除原有内容;变量名不要写错,例如把 JAVA_HOME 写成 JAVA-HOME;路径不要多写或漏写分号;配置完成后要重新打开命令行窗口,旧窗口不会自动刷新环境变量。

验证 JRE 是否安装成功 ​

为什么需要:配置完不代表一定成功,必须用命令验证,避免后面生成证书时才发现环境有问题。
怎么想:验证要看“命令是否能被系统找到”和“版本是否为我们安装的版本”。
怎么做:新开一个命令行窗口,执行:

bash
# 验证 JRE 是否安装成功
java -version

如果能看到类似 1.8 的版本信息,说明 JRE 基本可用。
易错点:如果提示“不是内部或外部命令”,优先检查 Path 是否指向 bin 目录;如果版本不对,检查是否装了多个 Java;如果后续生成证书时发现缺少 keytool,说明当前环境可能只有运行环境,应改用同版本 JDK 或确认安装包是否完整。

关于转写稿的说明与调试思路迁移 ​

本节笔记主题是“安卓证书”,但提供的转写稿讲的是“神领物流—在途/回车登记”,涉及交付、云存储、transport ID、回收登记等内容,与安卓证书没有直接关系。为避免编造,不能把云存储、任务 ID 等硬套到证书生成里。
可以迁移的只有调试习惯:转写稿反复强调“先检查前置条件,再看结果”。对应到本节就是:先确认 JRE 安装路径,再确认环境变量,最后用 java -version 验证;任何一步失败,都按“安装路径 → 环境变量 → 新开终端验证”的顺序排查。

本节目标与前提 ​

本节讨论的是证书生成之后的“云打包”环节。笔记给出的核心结论是:生成好的证书既可以用来打正式的 APK 包,也可以用于自定义调试基座。 也就是说,证书不是只能服务于正式发布;在开发阶段制作自定义调试基座时,同样要使用证书完成打包。学习本节时,重点不是记某个按钮的位置,而是分清“自定义基座”和“正式包”的用途,并在打包前核对包名。

为什么需要:安卓应用打包离不开证书;如果证书与打包类型的关系不清楚,就容易把调试基座当成正式包,或者误以为正式包要另做一套证书。
怎么想:先确认证书已经生成,再判断当前目标是“开发调试”还是“上线发布”,最后选择对应的打包类型。
怎么做:在云打包配置中按用途选择自定义基座或正式打包,二者都使用已生成的证书。
易错点:不要把“证书用途”和“打包类型”混为一谈;证书确定后应保持稳定,避免后续签名不一致。

自定义基座 ​

为什么需要:开发阶段需要在真机或测试设备上运行、调试应用,自定义调试基座就是为开发环境准备的安装包。它方便在开发过程中验证功能,而不是直接拿去做应用商店上架。
怎么想:看到“自定义基座”时,应立刻把它归到开发环境,而不是生产环境。笔记明确说明:自定义调试基座用于开发环境。因此它的目标是“能调试、能验证”,不是“能提交商店”。
怎么做:在云打包环节选择自定义基座,使用已经生成好的证书进行打包;打包完成后安装到测试设备上用于开发调试。笔记中的截图就是自定义基座打包的界面与结果示意。
易错点:最常见的错误是把自定义调试基座当成正式包去提交应用商店;另一个风险是开发阶段随意改包名,导致后面正式包与调试基座对不上。制作基座前要先确认包名,不要等打包完再回头改。

正式打包 ​

为什么需要:应用开发完成后要上线到安卓应用商店,例如 360、小米、华为应用商店等,这时必须生成正式 APK 包。正式包面向生产环境,不能再用开发阶段的调试基座替代。
怎么想:正式打包的关键词是“上线”。它与自定义调试基座的区别不在证书有没有,而在使用场景:自定义调试基座用于开发环境,正式包用于上线到安卓应用商店。因此打包前要确认当前版本是否已经具备发布条件。
怎么做:在云打包中选择正式打包,使用生成好的证书生成正式 APK;随后按目标应用商店的要求提交。笔记截图展示了正式打包的界面位置。
易错点:不要用自定义调试基座冒充正式包提交商店;正式包的包名要保持稳定,否则后续升级、覆盖安装和商店审核都可能出现问题;证书也要与后续版本保持一致,避免签名冲突。

包名必须一致 ​

为什么需要:包名是安卓应用的身份标识。正式包与自定义调试基座如果包名不一致,系统会把它们当成两个不同的应用,开发调试与正式发布之间就无法顺利对应。笔记特别强调:正式包与自定义调试基座的包名要一致!
怎么想:打包前不要只看“能不能打出来”,还要看“打出来的包是不是同一个应用”。自定义基座和正式包虽然用途不同,但应指向同一个应用身份。
怎么做:在制作自定义基座和正式打包之前,分别核对两处配置中的包名,确保拼写、大小写和层级完全一致;确认后再执行云打包。
易错点:包名拼写错误、大小写不一致、调试时临时改包名后忘记同步正式包,都会造成“看起来都能装,但实际上不是同一个应用”的问题。正式发布前应把包名一致性作为一项固定检查。

本节以界面操作为主,没有新增代码;学习时把“证书可复用、基座用于开发、正式包用于上线、包名必须一致”四件事串起来即可。

本节要解决的问题 ​

地图服务在 uni-app 里不能简单理解成“随便引一个地图组件就行”。因为 uni-app 要同时跑在 App、H5、小程序等端,而不同地图服务商对各端的支持并不一致。笔记先给出兼容性表格,目的就是让我们在写代码前先做选型,避免后面出现“H5 能显示、App 不能显示”或“小程序根本不支持”的问题。

为什么需要:地图能力涉及服务商 SDK、Key、包名、签名等平台限制,选错服务商会直接导致功能不可用。
怎么想:先看项目要发布到哪些端,再对照兼容性表选择服务商;不要先写业务代码再回头换地图。
怎么做:保留并理解下面的兼容性表:

地图服务商AppH5小程序
高德√√
Google仅nvue页面√
腾讯√√

易错点:不要把 H5 的支持情况直接当成 App 或小程序也支持;Google 在 App 端还限制为仅 nvue 页面,这不是普通 vue 页面随便就能用的。

平台兼容性对照与选型思路 ​

为什么需要:表格不是背概念,而是决定“哪个端用哪家服务商”。
怎么想:本项目 H5 和小程序选择腾讯地图服务平台,App 选择高德地图服务平台。这样选是因为表格中腾讯覆盖 H5、小程序,高德覆盖 App、H5。
怎么做:按端拆分配置:H5/小程序走腾讯地图,App 走高德地图;注册高德地图开发者账号并创建应用。
易错点:不要用一个平台的 Key 去套所有端;不同服务商、不同端的 Key 配置通常要分开管理。

创建应用 Key:PackageName 与 SHA1 ​

为什么需要:App 端使用高德地图时,平台需要校验应用身份,否则 Key 可能不可用。笔记特别说明:PackageName 是在自定义调试基座和正式包时定义的包名;SHA1 是签名相关信息。
怎么想:先确认当前运行的是自定义调试基座还是正式包,因为它们对应的包名、签名可能不同;创建 Key 时要按实际包信息填写。
怎么做:进入高德开放平台创建应用,按下图填写应用信息:

创建高德地图应用 Key 页面

笔记中 SHA1 获取方式给了一个代码块,但内容为空,因此不能凭空补命令。保留原代码块如下:

bash

实操时应以高德开放平台文档和项目签名文件为准,不要照抄网上示例值。
易错点:PackageName 不要写错;调试基座和正式包的包名/SHA1 可能不一致;不要把示例 Key、示例包名直接用于项目;代码块为空时不要编造命令。

关于转写稿的说明与调试原则 ​

本次转写稿主要讲的是物流业务中的交付、回车登记、状态判断、图片上传、接口参数等内容,与“1.3 地图服务”不是同一主题。因此不能把“交付”“回车登记”等流程硬写进地图服务小节,否则就是编造。地图服务这一节能确定的调试原则是:先检查平台是否支持,再检查服务商选型,最后检查 Key、PackageName、SHA1 是否与当前运行包一致。
易错点:不要把其他章节的业务代码当成地图服务代码;遇到地图不显示时,不要只改页面代码,要回看控制台报错、Key 配置和包信息。

代码示例 ​

代码一:交付接口封装 api/task.js ​

js
// api/task.js —— 在途-交付接口
export const delivery = (data) => {
  // 参数自检:没有 id 直接抛一个自定义错误码,方便排查
  if (!data.id) {
    return Promise.reject({ code: -2, msg: '参数有误' })
  }

  return uni.request({
    url: 'https://api.shenling.com/driver/task/delivery',
    method: 'POST', // 接口要求以 POST 方式提交
    data: {
      id: data.id,           // 运输任务 ID
      voucher: data.voucher, // 交货凭证 [{ url }]
      picture: data.picture  // 现场照片 [{ url }]
    }
  })
}

export default { delivery }

关键行与易错点

  • if (!data.id) return Promise.reject({ code: -2, msg: '参数有误' }):故意造一个“很奇怪的码”-2,只要看到 -2 就说明是自己调 API 时参数没传全,方便排错。
  • method: 'POST':该接口要求 POST,写成 GET 后端收不到 data。
  • 参数名(id / voucher / picture)必须和后端约定一致,写代码时先把三个参数名“占位”写全,再逐个填空,避免漏传。
  • 调试时看两个地方:POST 请求看 payload(你给后端的数据),再看响应结果(后端给你的)。有错先检查自己传的参数。

代码二:在途-交付页面 pages/task/delivery.vue ​

vue
<!-- pages/task/delivery.vue -->
<template>
  <view class="delivery">
    <view class="block">
      <view class="title">交货凭证</view>
      <!-- 上传成功后 v-model 自动把文件信息写入 voucherList -->
      <uni-file-picker v-model="voucherList" :limit="9" file-mediatype="image" />
    </view>

    <view class="block">
      <view class="title">现场照片</view>
      <uni-file-picker v-model="pictureList" :limit="9" file-mediatype="image" />
    </view>

    <button class="submit" :disabled="!enableSubmit" @click="onFormSubmit">
      提交
    </button>
  </view>
</template>

<script setup>
import { ref, computed } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import taskAPI from '@/api/task.js'

// 1. 两个响应式数组,接收上传后的图片数据(元素是对象)
const voucherList = ref([])
const pictureList = ref([])

// 2. 接口只要 url,用计算属性做二次加工
const voucher = computed(() =>
  voucherList.value.map((item) => ({ url: item.url }))
)
const picture = computed(() =>
  pictureList.value.map((item) => ({ url: item.url }))
)

// 3. 是否允许提交(两张都上传了才可点)
const enableSubmit = computed(
  () => voucherList.value.length > 0 && pictureList.value.length > 0
)

// 4. 从地址上取任务 ID
const id = ref('')
onLoad((query) => {
  id.value = query.id
})

// 5. 提交
const onFormSubmit = async () => {
  const res = await taskAPI.delivery({
    id: id.value,
    voucher: voucher.value,
    picture: picture.value
  })
  if (res.code !== 200) {
    return uni.showToast({ title: '交付失败', icon: 'none' })
  }
  uni.showToast({ title: '交付成功', icon: 'none' })
  // 交付成功后必须离开页面,否则再点一次就交付不了
  uni.reLaunch({ url: '/pages/index/index' })
}
</script>

<style lang="scss" scoped>
.delivery {
  padding: 24rpx;

  .block {
    background: #fff;
    border-radius: 12rpx;
    padding: 24rpx;
    margin-bottom: 24rpx;

    .title {
      font-size: 28rpx;
      color: #333;
      margin-bottom: 16rpx;
    }
  }

  .submit {
    margin-top: 40rpx;
    background: #2b7afb;
    color: #fff;

    &[disabled] {
      background: #c8c9cc;
      color: #fff;
    }
  }
}
</style>

关键行与易错点

  • map((item) => ({ url: item.url })):箭头函数直接返回对象时外层必须包 (),写成 item => { url: item.url } 会变成函数体、返回 undefined。最终数据只剩 url 属性,正好是接口要的格式。
  • computed 里必须 return(或直接用箭头函数表达式),否则计算属性没有结果。
  • enableSubmit 表示“允许提交”,按钮上是 :disabled="!enableSubmit",要取反,别写反了。
  • 模板里不写 .value,JS 里必须写 .value:voucherList.length 在模板中直接写,在 <script setup> 里必须写 voucherList.value.length,漏写 .value 会导致按钮一直不亮。
  • 不建议在模板里堆 a.length > 0 && b.length > 0 这种逻辑,按 Vue 设计原则应放到计算属性里,模板只做渲染和极简判断。
  • 上传前必须先关联云空间并重启项目(先关联、再重启),顺序反了会上传失败;上传很快时进度条一闪而过,看不到进度条基本就是没上传成功。
  • 交付页地址上必须带 id,没有 id 无法确定交付哪条任务,onLoad 里取到后立刻赋给 id.value。

代码三:回车登记页面 pages/task/record.vue ​

vue
<!-- pages/task/record.vue -->
<template>
  <view class="record">
    <!-- 出车时间:来自地址参数,只读展示 -->
    <view class="item">
      <text class="label">出车时间</text>
      <text class="value">{{ startTime }}</text>
    </view>

    <!-- 回车时间:组件 v-model 双向绑定 -->
    <view class="item">
      <text class="label">回车时间</text>
      <uni-datetime-picker v-model="endTime" type="datetime" />
    </view>
  </view>
</template>

<script setup>
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'

// 出车时间(接口字段名 startTime,数据名与接口保持一致,减少出错)
const startTime = ref('')
// 回车时间(接口字段名 endTime)
const endTime = ref('')

onLoad((query) => {
  // query 里的名字必须和跳转时拼接的参数名一模一样
  startTime.value = query.startTime
})
</script>

<style lang="scss" scoped>
.record {
  padding: 24rpx;

  .item {
    display: flex;
    align-items: center;
    justify-content: space-between;
    background: #fff;
    padding: 28rpx 24rpx;
    border-radius: 12rpx;
    margin-bottom: 20rpx;

    .label { font-size: 28rpx; color: #333; }
    .value { font-size: 28rpx; color: #666; }
  }
}
</style>

关键行与易错点

  • 出车时间取的是“实际提货时间”,跳转时用 & 拼参数,别把 & 丢掉,丢了就不是变量而是普通字符串:
    js
    // 列表页跳转
    `/pages/task/record?transportId=${item.transportId}&startTime=${item.departTime}`
    // 详情页跳转
    `/pages/task/record?transportId=${detail.transportId}&startTime=${detail.departTime}`
  • 回车登记有两个入口(列表、详情),两处都要拼 startTime,漏一个后面拿不到时间。
  • 注意回车登记传的是 transportId,不是普通 id,传错后面全错。
  • onLoad((query) => {...}) 里的参数名必须和地址上拼的名字一致;名字太长容易写错,建议从接口/列表数据里直接复制。
  • uni-datetime-picker 用 v-model 即可拿到用户选择的时间,不需要再监听 change 事件;v-model 绑定的数据名要和接口字段 endTime 对齐,改组件绑定时两处(v-model 和数据定义)都要改,只改一处会失效。

常见错误 ​

  1. 现象:安装 JRE 后命令行仍提示找不到 java。→ 原因:环境变量未指向 jre-1.8\bin 或配置后未重开终端。→ 修法:检查 Path 是否包含该 bin 目录,重开命令行再验证。
  2. 现象:正式包安装/地图 Key 校验失败。→ 原因:正式包与自定义基座包名不一致,或地图平台 PackageName、SHA1 填错。→ 修法:统一包名,重新核对 Key 配置中的包名与 SHA1。
  3. 现象:图片上传成功但接口报参数错误。→ 原因:把上传组件返回的完整对象直接传给接口,未只保留 url。→ 修法:提交前用计算属性/映射处理成接口要求的仅含 url 的结构。
  4. 现象:交付按钮在错误状态下仍可操作,或提交后还能重复交付。→ 原因:未按任务状态控制入口,提交成功后未及时离开页面。→ 修法:仅允许在途状态进入交付,成功后立即跳转并刷新状态。
  5. 现象:回收登记页面拿不到 transportId 或参数丢失。→ 原因:两个入口传参不一致,或拼接地址时漏掉 &、参数名写错。→ 修法:列表页和详情页都核对参数名与 transportId,跳转后检查地址栏参数。
  6. 现象:切换“是/否”后下方内容不显示/不隐藏。→ 原因:控制变量未正确取反,或 v-if 绑定错变量。→ 修法:在事件中明确切换布尔值,模板绑定对应控制变量并回归验证。
  7. 现象:抽出的选项组件样式错乱。→ 原因:模板已迁移,但对应样式未随组件迁移。→ 修法:把原作用域内相关样式剪切到新组件,确认类名一致。
  8. 现象:模板中大量条件判断导致难维护、易写错。→ 原因:把业务判断直接堆在模板里,且漏写 .value。→ 修法:把判断收敛到 computed,模板只绑定结果。

练习 ​

  1. 检查本机 JRE 环境:重新打开终端,验证 java 可用,并记录环境变量实际指向目录。
  2. 对照自定义基座与正式包配置,列出包名、证书、地图 Key 中必须保持一致的三项,并逐项核对。
  3. 在交付页面构造一次请求,分别检查请求参数和响应结果;确认提交数据中图片字段只保留 url。
  4. 从列表页和详情页分别进入回收登记,检查地址栏是否都携带正确的 transportId 与出车时间参数。
  5. 将“是/否”控制区域抽成组件后,完成一次显示/隐藏、样式、提交数据三项回归测试。

原视频对照 ​

#视频时长
1神领物流-在途-112:47
2神领物流-在途-215:37
3神领物流-在途-311:06
4神领物流-回车登记介绍03:40
5神领物流-回车登记-105:39
6神领物流-回车登记-206:15
7神领物流-回车登记-305:25
8神领物流-回车登记-407:10

小结 ​

  1. 环境配置类问题优先查路径、重开终端、再做命令验证。
  2. 包名、证书、地图 Key 是打包与地图服务联调的高频核对点。
  3. 上传组件返回值不能直接等同于接口参数,提交前必须做数据整形。
  4. 交付与回收登记都强依赖任务状态和正确 ID,入口与参数要双入口核对。
  5. 地址传参要重点检查参数名、拼接符号和跳转后的实际地址。
  6. 模板只做展示和少量判断,复杂条件应收敛到计算属性。
  7. 组件拆分不是只搬模板,样式、数据依赖和交互控制要一起迁移验证。
  8. 提交类操作成功后应及时跳转,避免重复提交引发状态异常。