供应链开发第五天
# 发布运单-页面
相较之前的页面,需要掌握的新的知识是element-plus里的 抽屉, 用于实现历史地址!!
# 大致布局
代码都写过了,此处关键在于分析页面的布局.
Ps: 货物类型处的下拉框内容,本应该在进入发布运单页面时onMounted里向后端发送请求获得,这里简化了,直接在前端写死了,Hhhh.
快捷日期的选择,关键代码如下: (钱包功能那的日期是日期段,运单这里是某个日期)
<template>
<el-form label-width="80px" style="max-width: 500px;">
<el-form-item label="发货时间" :error="state.formError.from_date">
<el-date-picker
v-model="state.form.from_date"
type="date"
placeholder="计划发货时间"
:disabled-date="disabledDate"
:shortcuts="shortcuts"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
style="width: 100%"
/>
</el-form>
</template>
<script setup>
const shortcuts = [
{
text: '今天',
value: new Date(),
},
{
text: '明天',
value: () => {
const date = new Date()
date.setTime(date.getTime() + 3600 * 1000 * 24)
return date
},
}
]
const state = reactive({
form: {
from_date: "",
},
formError: {
from_date: "",
},
})
// 这样设置后,今日以前的日期都选不了!! (当然,没有该需求的话,可以不设置:disabled-date="disabledDate"
function disabledDate(time) {
return time.getTime() < Date.now()
}
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# 抽屉展示
点击地址库, 出现抽屉, 用于展示历史地址.
抽屉区域,默认隐藏; 点击地址库,展示抽屉!!
# 抽屉数据加载
点击地址库按钮,会出现抽屉,里面是应是地址列表!
即弹出抽屉时, 会向后端发送请求获取当前登陆用户的地址库!!
先来看看效果!
准备工作:后端有关地址库的模型迁移生成数据表后,为了方便测试,我们手动在数据表中添加了几行数据!!
你可以发现,我点击了“地址库”按钮后,发送了一个请求;当我关闭抽屉,再次点击“地址库”,抽屉中也会有数据,不会再次发送请求啦!
你不经要问,why?
因为地址库中的数据,不会经常更新,就没必要点一次“地址库”按钮就从后端获取一次,
即取一次有了,后需的点击该按钮对应的方法中就不会再发送请求啦!
在前端代码中的体现在于showAddressTable方法中的return!注:state.addressLoaded一开始我们设置的值为false.
2
3
4
5
6
7
关键代码如下:
PS: 针对该功能,为了优化用户体验,我们还会在数据表中加一个代表使用频率的字段,返回给前端时根据该字段来进行排序. 暂略.
# 选择实现
当我们选中某个地址库中的一行数据, 会自动在表单对应的位置赋值上数据!!
先来看看效果!!
为了区分选中后,是给发货地址、还是收获地址自动赋值, 我们需要给点击事件showAddressTable添加参数"from"、"to" !!
关键代码如下: (只需要写点前端代码就可以实现啦!!)
# 后端视图扩展 res
钩子有返回值就直接返回返回值给前端!!
# 运单发布
ψ(`∇´)ψ
# 数据表的设计
为了完成该需求, 新增了4张表.
看上面这张截图,针对截图中新增的这四张表, 结合业务场景作一点说明:
[Order运单表]
- status运单状态:
已发布-后台管理人员审核通过后变为待接单-等待司机接单,司机接单后变为待提货
-提货后变为运输中-运输到目的地后变为待结算-给了司机钱后,变为已结算. 另外,发布后可取消,哪怕司机已接单,也可协商下取消.
只不过,在这里为了方便测试,其默认值为1,即待接单,默认发布后后台管理人员已经审核通过了.
- goods_type货物类型: 理论上前端应发送一个请求来获取它,但我犯懒了,在前端直接写死了,就是为了少发一个请求来获取货物类型Hhhh
- title货物名称
- cost货物价值:即一车货值多少钱
- oid订单号:
这里准确点来说,是运单号!!跟交易记录里的充值、提现时,我们自己生成的订单号是两码事哦!!
当然运单发布成功后,也会产生一条交易记录,其类型是“下单”,So,还得改一下交易记录表的ORM模型,哈哈哈
tran_type_choices = ((-2, "下单"), (-1, "提现"), (1, "充值"), (2, "取消"))
tran_type = models.SmallIntegerField(verbose_name="转账类型", choices=tran_type_choices)
order_id = models.CharField(verbose_name="运单号", max_length=64, null=True, blank=True, db_index=True)
- weight货物重量:运单发布时需要预估货物重量
- settle_weight货物结算重量:司机装货后会称重,是准确的,所以在发布运单时该值可以为空
- unit_price运单单价
- 发货地址相关字段 from_addr from_name from_mobile from_date
- 收获地址相关字段 to_addr to_name to_mobile to_date
- ctime创建时间:会自动生成
- company供应商:需指明哪个供应商提供的
- driver接单司机:哪个司机接单的 (一开始是空的,接单后才会关联上!)
[Driver司机表]
参考着供应商表来实现 与供应商表相比,就多了个字段plate_number
注意:我们简化了一下,每个司机只绑定了一个车牌,事实上可绑定多辆车,绑定过程需要认证,上传行驶证之类的!
[DriverAuth司机认证表]
和供应商认证表一样,还有个供应商认证表,两者倒差不差
[OrderRecord订单运输记录表]
还要考虑一些变化,比如司机接了单,临时有事去不了了等.所以会加上一个订单的运输记录表!
比如A司机在什么时候接单了,A司机又在什么时候取消订单了,B司机又在什么时候接单了.具体的事情都会写在remark描述里.
- order哪个订单
- ctime时间
- remark描述
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# CreateModelMixin
运单发布, 就是CURD中的增, 但会在此的基础上根据业务需求加上一些逻辑!!
先在交易记录表中修改和新增点东西.
运单发布后会添加一条下单记录,在下单记录的表格里可看到运单号,点击运单号可查看运单详情信息; 当然发布的运单也可以取消!
tran_type_choices = ((-2, "下单"), (-1, "提现"), (1, "充值"), (2, "取消"))
tran_type = models.SmallIntegerField(verbose_name="交易类型", choices=tran_type_choices)
order_id = models.CharField(verbose_name="运单号", max_length=64, null=True, blank=True, db_index=True)
2
3
关键代码如下: (请结合着 运单发布-数据表的设计 这一小节进行分析)
你观察视图函数,在新增逻辑的基础上根据业务需求加上一些逻辑!!
需要注意的是,在生成一条运单数据时,使用地址表中的id是不合适的!!
你试想,后续一旦地址库中的地址发送变动,那么与其关联的运单就会同步进行变动.
比如原本是成都到重庆,改成了成都到北京,距离变长了,佣金没变,该种情况是不允许出现的.
----
注意同类型的比较!!
unit_price*weight 单价*重量 与 当前登陆的供应商自己的可用余额balance 进行比较.
这三个字段都是DecimalField类型的数据!!注:前端传递过来的对应字段值是字符串,但提交过来后,其值在内部会转化成ORM表中字段的类型!!
----
在save时,ORM表中的绝大部分都用,零星几个不用,编写代码时用exclude!!
----
?突然犯病卡壳了,driver在ORM表中,是可为空的.So,运单发布是新增,这里不写driver字段也没事!!
紧接着,我想了一些问题 (这些都是老生常谈的问题了..无语子,说了很多遍了
1> 如果我写了,用APIFOX模拟前端发送请求,该请求携带的json数据中,包含了driver字段,会怎么样?
2> driver字段是外键字段,回想下ORM语句,可以是driver,也可以是driver_id,又是怎么一个场景?
我进行了一系列的实践,比如
- driver是外键,不为空
- exclude = ["company", "oid"] 前端传了driver,后端会自动检验外键字段在关联表中是否存在
- driver_id = serializers.IntegerField() exclude = ["company", "driver", "oid"]
前端传了driver_id, 若关联表中不存在该值,在save时会报错FOREIGN KEY constraint failed
- ◇ driver是外键,可为空
- exclude = ["company", "driver", "oid"] ◇ 前端传driver,传driver_id 都不起任何作用,当没传
- exclude = ["company", "oid"] 前端传了driver,后端会自动检验外键字段在关联表中是否存在
还有其他的搭配方案.话说回来,这些实践看似很复杂,实则记住下面这个逻辑就行:
[0] 要清楚的意识到前端传递的json,就是为序列化器类服务的,哪些要哪些不要,一切还得序列化器说的算Hhh
[1] 拿捏序列化器类中所有字段对象,进行分类 可验证的字段对象/可写、可序列化的字段对象/可读
[2] 可验证的字段对象 --> 进行验证 --> 验证通过validated_data --> 进行save存储,save时候还可额外传递一些数据
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# 运单管理
运单管理页面, 很多功能的具体实现都跟"我的钱包"那的"交易记录"是类似的, 所以在这里我们将会省略具体的步骤!!
页面长这样哦!!
来品一品新知识,哈哈哈
可以发现,页面可以分为几个区域 面板标题、按钮组(运单状态相关)、搜索条件、功能区、表格+分页
1> 按钮组和搜索条件功能的实现:参考"我的钱包"里"交易记录"的搜索
若运单运单状态以下拉框的形式放在表单里,一切都一样啦,此处是放到按钮组里的,问题不大!!依旧是url参数的形式传递过去.
2> 表格和分页和前面的差不多,多的新知识是 在表格里加了个多选, 选中后触发的方法可以拿到选中的那些行!!
@selection-change="handleSelectionChange"
<el-table-column type="selection" width="55"/>
3> 功能区有个下拉菜单,这是个新知识!点击某个选项,同样会触发某个方法,拿到特定的值
你可以进行if判断,当是某个值的时候执行某个操作.
4> 在这个业务场景中,获取列表数据和新增数据,序列化器类应该这么设计
- oid我们将其设置为了只读,而不是放到exclude列表中.
是因为只读,获取时返回,提交的时候不用.
代入业务场景:‘
oid发布运单时候,前端不用传,也不用返回它,就放到exclude中了,只是说sava新增时要自己生成一个传进去
oid展示列表时候,需要在表格中展示它
- DecimalField类型的字段,coerce_to_string值设置为False,是让序列化返回时以浮点型返回,而不是字符串类型
你看下源码,还可以全局设置哦!!
class OrderModelSerializer(serializers.ModelSerializer):
# oid = serializers.CharField(read_only=True)
status_text = serializers.CharField(source='get_status_display')
class Meta:
model = models.Order
# fields = "__all__"
exclude = ["company", "driver"]
extra_kwargs = {
"unit_price": {"coerce_to_string": False},
"weight": {"coerce_to_string": False},
"oid": {"read_only": True},
}
5> 关于按钮组里的内容,标准的流程应该是在页面在onMounted中发个请求获取,只不过有时候偷懒,就在前端写死了哈哈哈.
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 电子签
额,用到了再说吧,了解了下,不是很明白..Hhh 略.
▲ 在线签合同 途径
- 腾讯电子签
- 企业用户
- 个人用户,仅支持小程序
- 君子签
▲ 在线签合同 方式
- 人工
==> 注册企业账号-创建合同模板-发起合同-签署
- 程序
腾讯给提供了两种环境:正式环境、沙箱环境
以供应链系统为例,平台需作为中间方与供应商签署合同、也需与司机签署合同
So,平台需要自动实现一些功能
1> 自动生成PDF格式合同, 只有合同部分信息(不包含签署信息)
2> 自动上传给腾讯电子签
3> 自定义签署位置
- 坐标来找位置
- 关键字找位置(推荐)
4> 触发签合同
- 自动签署,公章上传平台 + 开通自动签章的服务.(企业静默签)
- 手动签署,小程序扫码
5> 对方签完了怎么通知我们? 电子签平台通知.(网址)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22