-
收貨確認(rèn)頁(yè)面
頁(yè)面

差異反饋?lái)?yè)面

選擇差異類(lèi)型
差異反饋原因: 可以新增、可以刪除、可以選擇品相(品相不可以重復(fù),選擇一項(xiàng)減少一項(xiàng),刪除一項(xiàng)釋放一項(xiàng))、少收(數(shù)量最大為下單量、多收無(wú)限制)

差異反饋原因
-
主要是這4個(gè)文件
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="收貨人手機(jī)號(hào)" placeholder="請(qǐng)輸入收貨人手機(jī)號(hào)" input-align="right" required="{{true}}" />
<van-field bind:change="onChange" data-field='driverPhone' type="number" value="{{ form.driverPhone || '' }}" label="送貨司機(jī)電話" placeholder="請(qǐng)輸入送貨司機(jī)電話" input-align="right" required="{{true}}" />
<van-field bind:click-input='chooseOptions' value="{{ form.differenceTypeName || '' }}" label="差異類(lèi)型" placeholder="請(qǐng)選擇差異類(lèi)型" 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="(如訂單品項(xiàng)、數(shù)量、質(zhì)量有差異,請(qǐng)當(dāng)場(chǎng)與相關(guān)人員共識(shí),明確差異類(lèi)型,并留下您的聯(lián)系方式。最多可填寫(xiě)500個(gè)字) " show-word-limit="{{true}}" input-align="right" /> -->
<van-collapse value="{{ activeNames }}" bind:change="onChangeCollapse">
<van-collapse-item name="1">
<!-- 使用插槽自定義標(biāo)題 -->
<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">選擇訂單內(nèi)的品相</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>
<!-- 添加新項(xiàng)按鈕 -->
<view class="add-btn" bindtap="addFeedbackItem">
<text>+ 添加差異項(xiàng)</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">關(guān)閉</view>
<view wx:if="{{code !== 'view'}}" class="bottom-btn-best" bindtap="sumbit">確認(rèn)</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":"銷(xiāo)售計(jì)劃提報(bào)"
}
- 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({
/**
* 頁(yè)面的初始數(shù)據(jù)
*/
data: {
navH: app.globalData.navHeight,
fontColor: "#ffffff",
activeNames: ["1"],
list: [],
buttons: [
{
name: "新建報(bào)損單",
code: "add",
},
],
username3: "",
dictTypeCodeList: ["difference_delivery_type"],
filesList: [],
pdProdcut: {},
pfProduct: {},
form: {},
isShow: false,
options: [
{ value: "1", label: "漏袋" },
{ value: "2", label: "封口不嚴(yán)" },
{ value: "3", label: "瓶體破損" },
{ value: "4", label: "針眼狀" },
{ value: "161", label: "角漏" },
{ value: "162", label: "摔漏" },
{ value: "163", label: "劃漏" },
{ value: "157", label: "杯蓋/杯體破損" },
],
autosize: {
maxHeight: 200,
minHeight: 100,
},
allDifferenceDeliveryList: [], // 完整的品類(lèi)列表
differenceDeliveryList: [], // 實(shí)際顯示的品類(lèi)列表(會(huì)動(dòng)態(tài)變化)
selectedTypeIds: [], // 已選擇的品類(lèi)ID集合
feedbackItems: [
{
typeId: "",
type: "",
quantity: 1
},
],
typeOptions: [
{ value: "多收", label: "多收" },
{ value: "少收", label: "少收" },
],
// 手機(jī)號(hào)校驗(yàn)規(guī)則
isValidPhone: function(phone) {
return /^1[3-9]\d{9}$/.test(phone);
}
},
// picker 選擇事件
handleTypeIdChange(e) {
const { index } = e.currentTarget.dataset; // 獲取當(dāng)前項(xiàng)的索引
const selectedIndex = e.detail.value; // 用戶(hù)選擇的數(shù)組下標(biāo)
const selectedItem =
this.data.differenceDeliveryList[selectedIndex]; // 獲取選擇的 typeId
// 更新已選集合
const newSelectedTypeIds = new Set(this.data.selectedTypeIds);
// 先移除這個(gè)位置之前選擇的品類(lèi)(如果有)
if(this.data.feedbackItems[index].typeId) {
newSelectedTypeIds.delete(this.data.feedbackItems[index].typeId);
}
// 添加新選擇的品類(lèi)
newSelectedTypeIds.add(selectedItem.typeId);
// 更新 feedbackItems 中對(duì)應(yīng)項(xiàng)的 typeId
this.setData({
[`feedbackItems[${index}].typeId`]: selectedItem.typeId,
[`feedbackItems[${index}].quantity`]: 1,
selectedTypeIds: newSelectedTypeIds
});
// 少收的時(shí)候最大填寫(xiě)數(shù)量為交貨單數(shù)量;多收的時(shí)候沒(méi)有數(shù)量限制
if (this.data.feedbackItems[index].type == '少收' && this.data.feedbackItems[index].quantity > selectedItem.quantity) {
this.setData({
[`feedbackItems[${index}].quantity`]: selectedItem.quantity,
});
}
// 更新可選列表
this.updateAvailableTypes();
},
// 更新可選品類(lèi)列表
updateAvailableTypes: function() {
// 過(guò)濾掉已選的品類(lèi)
const availableTypes = this.data.allDifferenceDeliveryList.filter(
item => !this.data.selectedTypeIds.has(item.typeId)
);
this.setData({
differenceDeliveryList: availableTypes
});
},
handleTypeChange(e) {
const { index } = e.currentTarget.dataset; // 獲取當(dāng)前項(xiàng)的索引
const selectedIndex = e.detail.value; // 用戶(hù)選擇的數(shù)組下標(biāo)
const selectedType = this.data.typeOptions[selectedIndex].value;
// 獲取當(dāng)前品類(lèi)的交貨單數(shù)量
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;
}
// 設(shè)置數(shù)量和類(lèi)型
let newQuantity = 1;
// 如果是少收且當(dāng)前數(shù)量超過(guò)交貨單數(shù)量,則限制為交貨單數(shù)量
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: `最大可填寫(xiě)數(shù)量為${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;
// 如果是少收,限制最大數(shù)量
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: `已達(dá)到最大數(shù)量${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;
// 如果是少收,檢查是否超過(guò)最大數(shù)量
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: `已達(dá)到最大數(shù)量${maxQuantity}`,
icon: 'none',
duration: 1000
});
return;
}
}
value++;
this.setData({
[`feedbackItems[${index}].quantity`]: value
});
},
// 刪除項(xiàng)目時(shí)需要釋放已選品類(lèi)
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);
}
// 獲取當(dāng)前 feedbackItems 數(shù)組
let feedbackItems = this.data.feedbackItems;
// 刪除指定索引的項(xiàng)
feedbackItems.splice(index, 1);
// 更新數(shù)據(jù)
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: '已清空所有差異項(xiàng)數(shù)據(jù)!',
icon: 'none',
duration: 2000
});
}
}
},
// 添加新反饋?lái)?xiàng)
addFeedbackItem() {
// 檢查是否已達(dá)到最大允許的反饋?lái)?xiàng)數(shù)量
const maxItems = this.data.allDifferenceDeliveryList.length;
if (this.data.feedbackItems.length >= maxItems) {
wx.showToast({
title: `最多只能添加${maxItems}個(gè)差異項(xiàng)`,
icon: 'none',
duration: 2000
});
return;
}
this.setData({
feedbackItems: [
...this.data.feedbackItems,
{ typeId: "", type: "", quantity: 1 }
]
});
},
onChangeCollapse(event) {
this.setData({
activeNames: event.detail,
});
},
//獲取數(shù)據(jù)字典
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. 校驗(yàn)基礎(chǔ)表單字段
if (!form.differenceContact) {
return Notify({
type: "danger",
top: this.data.navH,
message: "請(qǐng)輸入收貨人手機(jī)號(hào)",
});
}
if (!this.data.isValidPhone(form.differenceContact)) {
return Notify({
type: "danger",
top: this.data.navH,
message: "收貨人手機(jī)號(hào)格式不正確",
});
}
if (!form.driverPhone) {
return Notify({
type: "danger",
top: this.data.navH,
message: "請(qǐng)輸入送貨司機(jī)電話",
});
}
if (!this.data.isValidPhone(form.driverPhone)) {
return Notify({
type: "danger",
top: this.data.navH,
message: "送貨司機(jī)電話格式不正確",
});
}
if (!form.differenceType) {
return Notify({
type: "danger",
top: this.data.navH,
message: "請(qǐng)選擇差異類(lèi)型",
});
}
// 2. 校驗(yàn)差異項(xiàng)
if (feedbackItems.length === 0) {
return Notify({
type: "danger",
top: this.data.navH,
message: "請(qǐng)至少添加一個(gè)差異項(xiàng)",
});
}
// 檢查每個(gè)差異項(xiàng)是否完整填寫(xiě)
for (let i = 0; i < feedbackItems.length; i++) {
const item = feedbackItems[i];
// 檢查品類(lèi)是否選擇
if (!item.typeId) {
return Notify({
type: "danger",
top: this.data.navH,
message: `請(qǐng)選擇第${i+1}項(xiàng)的品相`,
});
}
// 檢查(多收/少收)是否選擇
if (!item.type) {
return Notify({
type: "danger",
top: this.data.navH,
message: `請(qǐng)選擇第${i+1}項(xiàng)的多收/少收類(lèi)型`,
});
}
// 檢查數(shù)量是否有效
if (!item.quantity || item.quantity <= 0) {
return Notify({
type: "danger",
top: this.data.navH,
message: `請(qǐng)輸入第${i+1}項(xiàng)的有效數(shù)量`,
});
}
// 如果是少收,檢查數(shù)量是否超過(guò)最大限制
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}項(xiàng)的數(shù)量不能超過(guò)${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();
},
/**
* 生命周期函數(shù)--監(jiān)聽(tīng)頁(yè)面加載
*/
onLoad: function (options) {
this.setData({
outId: options.id,
});
this.getDicts();
this.getDifferenceDeliveryList();
},
/**
* 生命周期函數(shù)--監(jiān)聽(tīng)頁(yè)面初次渲染完成
*/
onReady: function () {},
/**
* 生命周期函數(shù)--監(jiān)聽(tīng)頁(yè)面顯示
*/
onShow: function () {},
/**
* 生命周期函數(shù)--監(jiān)聽(tīng)頁(yè)面隱藏
*/
onHide: function () {},
/**
* 生命周期函數(shù)--監(jiān)聽(tīng)頁(yè)面卸載
*/
onUnload: function () {},
/**
* 頁(yè)面相關(guān)事件處理函數(shù)--監(jiān)聽(tīng)用戶(hù)下拉動(dòng)作
*/
onPullDownRefresh: function () {},
/**
* 頁(yè)面上拉觸底事件的處理函數(shù)
*/
onReachBottom: function () {},
/**
* 用戶(hù)點(diǎn)擊右上角分享
*/
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; /* 右側(cè)留出箭頭空間 */
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; /* 防止箭頭遮擋點(diǎn)擊 */
}
.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;
}
/* 數(shù)字輸入框容器 */
.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;
}

