-
收货确认页面
页面

差异反馈页面

选择差异类型
差异反馈原因: 可以新增、可以删除、可以选择品相(品相不可以重复,选择一项减少一项,删除一项释放一项)、少收(数量最大为下单量、多收无限制)

差异反馈原因
-
主要是这4个文件
image.png index.wxml
<view class="container">
<view class="navBar" style="height:{{navH}}px;width:100%;">
<navBar page-name="差异反馈" showNav="{{true}}" showHome="{{true}}" fontColor="{{fontColor}}" />
</view>
<scroll-view scroll-y="{{true}}" class="main-content" style="height:calc(100% - {{navH}}px - 100px);">
<card title='差异反馈'>
<van-cell-group>
<van-field bind:change="onChange" data-field='differenceContact' type="number" value="{{ form.differenceContact || '' }}" label="收货人手机号" placeholder="请输入收货人手机号" input-align="right" required="{{true}}" />
<van-field bind:change="onChange" data-field='driverPhone' type="number" value="{{ form.driverPhone || '' }}" label="送货司机电话" placeholder="请输入送货司机电话" input-align="right" required="{{true}}" />
<van-field bind:click-input='chooseOptions' value="{{ form.differenceTypeName || '' }}" label="差异类型" placeholder="请选择差异类型" input-align="right" required="{{true}}" is-link="{{true}}" readonly="{{true}}" />
<!-- <van-field class="textarea" maxlength="{{500}}" autosize="{{autosize}}" type='textarea' bind:change="onChange" data-field='content' value="{{ form.content || '' }}" label="备注" placeholder="(如订单品项、数量、质量有差异,请当场与相关人员共识,明确差异类型,并留下您的联系方式。最多可填写500个字) " show-word-limit="{{true}}" input-align="right" /> -->
<van-collapse value="{{ activeNames }}" bind:change="onChangeCollapse">
<van-collapse-item name="1">
<!-- 使用插槽自定义标题 -->
<view slot="title" class="required-title">
<text class="required-star">*</text>
<text>差异反馈原因</text>
</view>
<view wx:for="{{feedbackItems}}" class="reason-item" wx:key="index">
<picker mode="selector" range="{{differenceDeliveryList}}" range-key="typeId" bindchange="handleTypeIdChange" data-index="{{index}}">
<view class="reason-item-typeId picker">
<text wx:if="{{item.typeId}}">{{item.typeId}}</text>
<text wx:else class="select-placeholder">选择订单内的品相</text>
</view>
</picker>
<view class="reason-item-row">
<picker mode="selector" range="{{typeOptions}}" range-key="label" bindchange="handleTypeChange" data-index="{{index}}">
<view class="reason-item-type picker">
<text wx:if="{{item.type}}">{{item.type}}</text>
<text wx:else class="select-placeholder">(多收/少收)</text>
</view>
</picker>
<view class="number-input-container">
<view class="number-input-wrapper">
<view class="number-input-minus" bindtap="handleDecrease" data-index="{{index}}">-</view>
<input
type="number"
placeholder="0"
value="{{item.quantity || 0}}"
bindinput="handleQuantityInput"
data-index="{{index}}"
class="quantity-input"
/>
<view class="number-input-plus" bindtap="handleIncrease" data-index="{{index}}">+</view>
</view>
<view class="number-input-unit">件</view>
</view>
<view class="delete-btn" bindtap="handleDelete" data-index="{{index}}">
<van-icon name="delete" color="#fff" size="32rpx" />
</view>
</view>
<view class="dashed-divider"></view>
</view>
<!-- 添加新项按钮 -->
<view class="add-btn" bindtap="addFeedbackItem">
<text>+ 添加差异项</text>
</view>
</van-collapse-item>
</van-collapse>
</van-cell-group>
<view style="height: 40rpx;"></view>
</card>
</scroll-view>
<view class="btm-btn">
<view class="bottom-btn-best" bindtap="close">关闭</view>
<view wx:if="{{code !== 'view'}}" class="bottom-btn-best" bindtap="sumbit">确认</view>
</view>
</view>
<van-notify class="notify" id="van-notify" />
<van-popup show="{{ isShow }}" position="bottom">
<van-picker show-toolbar value-key='label' columns="{{ options }}" bind:cancel='onCancel' bind:confirm="onConfirm" />
</van-popup>
<wxs src="/utils/enums.wxs" module="transEnums" />
- index.json
{
"usingComponents": {
"navBar":"/components/navbar/index",
"van-popup": "@vant/weapp/popup/index",
"van-datetime-picker": "@vant/weapp/datetime-picker/index",
"card":"/components/card/card",
"van-cell": "@vant/weapp/cell/index",
"van-field": "@vant/weapp/field/index",
"van-picker": "@vant/weapp/picker/index",
"van-collapse": "@vant/weapp/collapse/index",
"van-collapse-item": "@vant/weapp/collapse-item/index"
},
"navigationStyle":"custom",
"navigationBarTitleText":"销售计划提报"
}
- index.js
// finance-center/freezerProperty/index.js
import Notify from "@vant/weapp/notify/notify";
import { commonModel } from "../../../model/Common";
import { orderModel } from "../../../model/Order";
const app = getApp();
Page({
/**
* 页面的初始数据
*/
data: {
navH: app.globalData.navHeight,
fontColor: "#ffffff",
activeNames: ["1"],
list: [],
buttons: [
{
name: "新建报损单",
code: "add",
},
],
username3: "",
dictTypeCodeList: ["difference_delivery_type"],
filesList: [],
pdProdcut: {},
pfProduct: {},
form: {},
isShow: false,
options: [
{ value: "1", label: "漏袋" },
{ value: "2", label: "封口不严" },
{ value: "3", label: "瓶体破损" },
{ value: "4", label: "针眼状" },
{ value: "161", label: "角漏" },
{ value: "162", label: "摔漏" },
{ value: "163", label: "划漏" },
{ value: "157", label: "杯盖/杯体破损" },
],
autosize: {
maxHeight: 200,
minHeight: 100,
},
allDifferenceDeliveryList: [], // 完整的品类列表
differenceDeliveryList: [], // 实际显示的品类列表(会动态变化)
selectedTypeIds: [], // 已选择的品类ID集合
feedbackItems: [
{
typeId: "",
type: "",
quantity: 1
},
],
typeOptions: [
{ value: "多收", label: "多收" },
{ value: "少收", label: "少收" },
],
// 手机号校验规则
isValidPhone: function(phone) {
return /^1[3-9]\d{9}$/.test(phone);
}
},
// picker 选择事件
handleTypeIdChange(e) {
const { index } = e.currentTarget.dataset; // 获取当前项的索引
const selectedIndex = e.detail.value; // 用户选择的数组下标
const selectedItem =
this.data.differenceDeliveryList[selectedIndex]; // 获取选择的 typeId
// 更新已选集合
const newSelectedTypeIds = new Set(this.data.selectedTypeIds);
// 先移除这个位置之前选择的品类(如果有)
if(this.data.feedbackItems[index].typeId) {
newSelectedTypeIds.delete(this.data.feedbackItems[index].typeId);
}
// 添加新选择的品类
newSelectedTypeIds.add(selectedItem.typeId);
// 更新 feedbackItems 中对应项的 typeId
this.setData({
[`feedbackItems[${index}].typeId`]: selectedItem.typeId,
[`feedbackItems[${index}].quantity`]: 1,
selectedTypeIds: newSelectedTypeIds
});
// 少收的时候最大填写数量为交货单数量;多收的时候没有数量限制
if (this.data.feedbackItems[index].type == '少收' && this.data.feedbackItems[index].quantity > selectedItem.quantity) {
this.setData({
[`feedbackItems[${index}].quantity`]: selectedItem.quantity,
});
}
// 更新可选列表
this.updateAvailableTypes();
},
// 更新可选品类列表
updateAvailableTypes: function() {
// 过滤掉已选的品类
const availableTypes = this.data.allDifferenceDeliveryList.filter(
item => !this.data.selectedTypeIds.has(item.typeId)
);
this.setData({
differenceDeliveryList: availableTypes
});
},
handleTypeChange(e) {
const { index } = e.currentTarget.dataset; // 获取当前项的索引
const selectedIndex = e.detail.value; // 用户选择的数组下标
const selectedType = this.data.typeOptions[selectedIndex].value;
// 获取当前品类的交货单数量
const currentTypeId = this.data.feedbackItems[index].typeId;
let deliveryQuantity = 0;
if (currentTypeId) {
const selectedItem = this.data.allDifferenceDeliveryList.find(
item => item.typeId === currentTypeId
);
deliveryQuantity = selectedItem ? selectedItem.quantity : 0;
}
// 设置数量和类型
let newQuantity = 1;
// 如果是少收且当前数量超过交货单数量,则限制为交货单数量
if (selectedType === '少收' && currentTypeId) {
const currentQuantity = this.data.feedbackItems[index].quantity || 1;
newQuantity = Math.min(currentQuantity, deliveryQuantity);
}
this.setData({
[`feedbackItems[${index}].type`]: selectedType,
[`feedbackItems[${index}].quantity`]: newQuantity
});
// 如果是少收,显示提示信息
if (selectedType === '少收') {
wx.showToast({
title: `最大可填写数量为${deliveryQuantity}`,
icon: 'none',
duration: 2000
});
}
},
handleQuantityInput(e) {
const index = e.currentTarget.dataset.index;
const item = this.data.feedbackItems[index];
let value = parseInt(e.detail.value) || 0;
// 如果是少收,限制最大数量
if (item.type === '少收' && item.typeId) {
const selectedItem = this.data.allDifferenceDeliveryList.find(
i => i.typeId === item.typeId
);
const maxQuantity = selectedItem ? selectedItem.quantity : 0;
value = Math.min(value, maxQuantity);
if (value >= maxQuantity) {
wx.showToast({
title: `已达到最大数量${maxQuantity}`,
icon: 'none',
duration: 1000
});
}
}
// 最小值限制
value = Math.max(value, 0);
this.setData({
[`feedbackItems[${index}].quantity`]: value
});
},
handleDecrease(e) {
const index = e.currentTarget.dataset.index;
let value = parseInt(this.data.feedbackItems[index].quantity) || 1;
if(value > 1) value--;
this.setData({
[`feedbackItems[${index}].quantity`]: value
});
},
handleIncrease(e) {
const index = e.currentTarget.dataset.index;
const item = this.data.feedbackItems[index];
let value = parseInt(item.quantity) || 1;
// 如果是少收,检查是否超过最大数量
if (item.type === '少收' && item.typeId) {
const selectedItem = this.data.allDifferenceDeliveryList.find(
i => i.typeId === item.typeId
);
const maxQuantity = selectedItem ? selectedItem.quantity : 0;
if (value >= maxQuantity) {
wx.showToast({
title: `已达到最大数量${maxQuantity}`,
icon: 'none',
duration: 1000
});
return;
}
}
value++;
this.setData({
[`feedbackItems[${index}].quantity`]: value
});
},
// 删除项目时需要释放已选品类
handleDelete(e) {
const index = e.currentTarget.dataset.index;
const deletedItem = this.data.feedbackItems[index];
if(index !== 0 || this.data.feedbackItems.length>1) {
// 从已选集合中移除
const newSelectedTypeIds = new Set(this.data.selectedTypeIds);
if(deletedItem.typeId) {
newSelectedTypeIds.delete(deletedItem.typeId);
}
// 获取当前 feedbackItems 数组
let feedbackItems = this.data.feedbackItems;
// 删除指定索引的项
feedbackItems.splice(index, 1);
// 更新数据
this.setData({
feedbackItems,
selectedTypeIds: newSelectedTypeIds
}, () => {
// 删除后更新可选列表
this.updateAvailableTypes();
});
} else {
if(deletedItem.typeId || deletedItem.typeId || deletedItem.quantity!=1) {
// 从已选集合中移除
const newSelectedTypeIds = new Set(this.data.selectedTypeIds);
if(deletedItem.typeId) {
newSelectedTypeIds.delete(deletedItem.typeId);
}
this.setData({
[`feedbackItems[0]`]: {
typeId: "",
type: "",
quantity: 1,
},
selectedTypeIds: newSelectedTypeIds
}, () => {
// 删除后更新可选列表
this.updateAvailableTypes();
});
} else {
wx.showToast({
title: '已清空所有差异项数据!',
icon: 'none',
duration: 2000
});
}
}
},
// 添加新反馈项
addFeedbackItem() {
// 检查是否已达到最大允许的反馈项数量
const maxItems = this.data.allDifferenceDeliveryList.length;
if (this.data.feedbackItems.length >= maxItems) {
wx.showToast({
title: `最多只能添加${maxItems}个差异项`,
icon: 'none',
duration: 2000
});
return;
}
this.setData({
feedbackItems: [
...this.data.feedbackItems,
{ typeId: "", type: "", quantity: 1 }
]
});
},
onChangeCollapse(event) {
this.setData({
activeNames: event.detail,
});
},
//获取数据字典
async getDicts() {
const { dictTypeCodeList } = this.data;
let params = {
dictTypeCodeList,
};
const res = await commonModel.getDicts(params);
if (res.success) {
const { result } = res;
for (let key in result) {
this.setData({
dicts: result[key].map((a) => ({
...a,
value: a.dictCode,
label: a.dictValue,
})),
});
}
}
},
//获取差异反馈原因
async getDifferenceDeliveryList() {
const res = await orderModel.getDifferenceDeliveryList({
outId: this.data.outId,
});
if (res.success) {
const { result } = res;
this.setData({
allDifferenceDeliveryList: result || [],
differenceDeliveryList: result || []
});
}
},
onCancel() {
this.setData({
isShow: false,
});
},
onConfirm(e) {
const {
detail: { value },
} = e;
this.setData(
{
["form.differenceType"]: value.value,
["form.differenceTypeName"]: value.label,
},
() => {
this.onCancel();
}
);
},
onChange(e) {
const {
currentTarget: {
dataset: { field },
},
detail,
} = e;
const str = `form.${field}`;
this.setData({
[str]: detail,
});
},
chooseOptions(e) {
this.setData({
isShow: true,
options: this.data.dicts,
});
},
async sumbit() {
const { form, feedbackItems } = this.data;
// 1. 校验基础表单字段
if (!form.differenceContact) {
return Notify({
type: "danger",
top: this.data.navH,
message: "请输入收货人手机号",
});
}
if (!this.data.isValidPhone(form.differenceContact)) {
return Notify({
type: "danger",
top: this.data.navH,
message: "收货人手机号格式不正确",
});
}
if (!form.driverPhone) {
return Notify({
type: "danger",
top: this.data.navH,
message: "请输入送货司机电话",
});
}
if (!this.data.isValidPhone(form.driverPhone)) {
return Notify({
type: "danger",
top: this.data.navH,
message: "送货司机电话格式不正确",
});
}
if (!form.differenceType) {
return Notify({
type: "danger",
top: this.data.navH,
message: "请选择差异类型",
});
}
// 2. 校验差异项
if (feedbackItems.length === 0) {
return Notify({
type: "danger",
top: this.data.navH,
message: "请至少添加一个差异项",
});
}
// 检查每个差异项是否完整填写
for (let i = 0; i < feedbackItems.length; i++) {
const item = feedbackItems[i];
// 检查品类是否选择
if (!item.typeId) {
return Notify({
type: "danger",
top: this.data.navH,
message: `请选择第${i+1}项的品相`,
});
}
// 检查(多收/少收)是否选择
if (!item.type) {
return Notify({
type: "danger",
top: this.data.navH,
message: `请选择第${i+1}项的多收/少收类型`,
});
}
// 检查数量是否有效
if (!item.quantity || item.quantity <= 0) {
return Notify({
type: "danger",
top: this.data.navH,
message: `请输入第${i+1}项的有效数量`,
});
}
// 如果是少收,检查数量是否超过最大限制
if (item.type === '少收') {
const selectedItem = this.data.allDifferenceDeliveryList.find(
d => d.typeId === item.typeId
);
if (selectedItem && item.quantity > selectedItem.quantity) {
return Notify({
type: "danger",
top: this.data.navH,
message: `第${i+1}项的数量不能超过${selectedItem.quantity}`,
});
}
}
}
// 拼接差异反馈原因
const differenceReason = feedbackItems
.map(item => `${item.typeId}${item.type}${item.quantity}件`)
.join(',');
let params = {
outId: this.data.outId,
differenceContact: form.differenceContact,
driverPhone: form.driverPhone,
differenceType: form.differenceType,
differenceReason
};
const res = await orderModel.differenceDeliveryConfirmCustomer(params);
if (res.success) {
wx.navigateBack({
delta: 1,
});
}
},
close() {
wx.navigateBack();
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
this.setData({
outId: options.id,
});
this.getDicts();
this.getDifferenceDeliveryList();
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: function () {},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function () {},
/**
* 用户点击右上角分享
*/
onShareAppMessage: function () {},
});
- index.wxss
.container {
width: 100vw;
display: flex;
flex-direction: column;
align-items: center;
background: #f7f7f7;
position: relative;
}
.container .navBar {
position: sticky;
top: 0;
left: 0;
z-index: 99;
}
.main-content {
box-sizing: border-box;
position: relative;
}
.main-content {
position: absolute;
top: 160rpx;
display: flex;
flex-direction: column;
align-items: center;
/* background-color: red; */
width: 710rpx;
margin-bottom: 164rpx;
}
.container image {
width: 100vw;
height: 150rpx;
}
.main-content .item {
display: flex;
justify-content: space-between;
padding: 20rpx 10rpx;
font-size: 28rpx;
color: #646566;
}
.fileName .check {
width: 32rpx;
height: 32rpx;
vertical-align: bottom;
margin: 0rpx 18rpx;
}
.fileName {
display: flex;
justify-content: space-between;
font-size: 26rpx;
margin: 10rpx 0;
color: #9a9a9a;
}
.btm-btn {
height: 164rpx;
width: 100%;
background-color: #ffffff;
z-index: 99;
position: fixed;
bottom: 0;
display: flex;
align-items: center;
justify-content: space-around;
}
.textarea {
height: 200rpx;
}
.index--van-cell.van-cell.cell-index--van-cell.van-cell--clickable.cell-index--van-cell--clickable {
padding-left: 14rpx;
}
.required-star {
color: red;
margin-right: 4rpx;
font-size: 28rpx;
}
.reason-item {
margin-bottom: 20rpx;
}
.picker {
position: relative;
border: 1px solid #ccc;
padding: 15rpx 40rpx 15rpx 20rpx; /* 右侧留出箭头空间 */
border-radius: 6rpx;
}
/* 三角形箭头 */
.picker::after {
content: "";
position: absolute;
top: 50%;
right: 20rpx;
transform: translateY(-50%);
width: 0;
height: 0;
border-left: 10rpx solid transparent;
border-right: 10rpx solid transparent;
border-top: 12rpx solid #333; /* 箭头颜色 */
pointer-events: none; /* 防止箭头遮挡点击 */
}
.reason-item-typeId {
color: #666666;
}
.select-placeholder {
color: #cccccc;
}
.reason-item-row {
margin-top:10rpx;
display: flex;
align-items: center;
}
.reason-item-type {
width: 162rpx;
}
/* 数字输入框容器 */
.number-input-container {
margin-left: 10px;
display: flex;
flex: 1;
align-items: center;
height: 64rpx;
}
.number-input-wrapper {
display: flex;
align-items: center;
border: 1px solid #ddd;
border-radius: 4px;
overflow: hidden;
flex: 1;
height: 100%;
}
/* 加减按钮样式 */
.number-input-minus,
.number-input-plus {
width: 30px;
height: 100%; /* 使用容器高度 */
display: flex;
justify-content: center;
align-items: center;
background-color: #f5f5f5;
font-size: 16px;
}
/* 输入框样式 */
.quantity-input {
flex:1;
height: 100%; /* 使用容器高度 */
text-align: center;
border-left: 1px solid #ddd;
border-right: 1px solid #ddd;
border-top: none;
border-bottom: none;
padding: 0;
}
/* 单位文字样式 - 移到外面 */
.number-input-unit {
margin-left: 8px;
font-size: 14px;
color: #666;
}
.delete-btn {
margin-left: 8px;
width: 54rpx;
height: 54rpx;
display: flex;
align-items: center;
justify-content: center;
background: #f78989;
border-color: #f78989;
border-radius: 6rpx;
padding: 2rpx;
}
/* 分割线 */
.dashed-divider {
height: 1px;
margin: 20rpx 0;
border-top: 1px dashed #ccc;
}
/* 添加按钮样式 */
.add-btn {
color: #1890ff;
padding: 20rpx;
text-align: center;
border: 1rpx dashed #1890ff;
border-radius: 8rpx;
margin-top: 20rpx;
}

