Browse Source

直接补料扫描数量

hella_online_20240829
lijuncheng 5 months ago
parent
commit
3130e4fc75
  1. 2
      .env.development
  2. 8
      .env.production
  3. 31
      src/common/basic.js
  4. 2
      src/main.js
  5. 4
      src/manifest.json
  6. 4
      src/mycomponents/scan/winComScanBalance.vue
  7. 29
      src/pages/fg/receiptByPlan.vue
  8. 29
      src/pages/fg/receiptNoPlan.vue
  9. 32
      src/pages/package/record/overPackageRecord.vue
  10. 347
      src/pages/package/record/splitPackageRecord.vue
  11. 6
      src/pages/repleinsh/record/directRepleinshRecord.vue
  12. 8
      src/uni_modules/wz-select-popup/changelog.md
  13. 22
      src/uni_modules/wz-select-popup/components/wz-select-popup/util/defaultConfig.js
  14. 74
      src/uni_modules/wz-select-popup/components/wz-select-popup/util/index.js
  15. 130
      src/uni_modules/wz-select-popup/components/wz-select-popup/wz-list.vue
  16. 90
      src/uni_modules/wz-select-popup/components/wz-select-popup/wz-search.vue
  17. 357
      src/uni_modules/wz-select-popup/components/wz-select-popup/wz-select-popup.vue
  18. 82
      src/uni_modules/wz-select-popup/package.json
  19. 132
      src/uni_modules/wz-select-popup/readme.md

2
.env.development

@ -1,7 +1,7 @@
#VITE_BASE_URL=http://dev.ccwin-in.com:25300/api/admin-api
#VITE_BASE_URL_IMAGE=http://dev.ccwin-in.com:25300/api/admin-api
VITE_BASE_URL=http://172.21.32.13:81/api/admin-api VITE_BASE_URL_IMAGE=http://172.21.32.13:81/admin-api
VITE_BASE_URL=http://172.22.32.9:81/api/admin-api VITE_BASE_URL_IMAGE=http://172.21.32.13:81/admin-api
# 租户配置 VITE_TENANT='[{"text":"长春1379","value":1},{"text":"成都1397","value":2},{"text":"长春2379","value":3}]'
# 是否是测试环境

8
.env.production

@ -1,11 +1,11 @@
VITE_BASE_URL=http://172.22.32.9:81/api/admin-api
VITE_BASE_URL_IMAGE=http://172.22.32.9:81/admin-api
VITE_BASE_URL=http://172.21.32.14:81/api/admin-api
VITE_BASE_URL_IMAGE=http://172.21.32.14:81/admin-api
# 租户配置
VITE_TENANT='[{"text":"成都1397","value":2},{"text":"长春2379","value":3}]'
VITE_TENANT='[{"text":"长春1379","value":1}]'
# 是否是测试环境
VITE_isDevelopment=false
# 积木报表请求路径
VITE_JMREPORT_BASE_URL='http://172.22.32.9:90'
VITE_JMREPORT_BASE_URL='http://172.21.32.14:90'

31
src/common/basic.js

@ -732,22 +732,21 @@ export function getPackingNumberAndBatch(managementList, itemCode, packingNumber
}
// //提示是否消息
// export function showConfirmMsg(content, callback) {
// uni.showModal({
// title: '提示',
// cancelText: '否',
// confirmText: '是',
// content: content,
// success: function(res) {
// if (res.confirm) {
// callback(true);
// } else {
// callback(false);
// }
// },
// })
// scanErrorAudio();
// }
export function showConfirmMsg(content, callback) {
uni.showModal({
title: '提示',
cancelText: '否',
confirmText: '是',
content: content,
success: function(res) {
if (res.confirm) {
callback(true);
} else {
callback(false);
}
},
})
}
export function compare(key) {
return function(a, b) {

2
src/main.js

@ -4,7 +4,6 @@ import './router/my_router.js'; //引入拦截
import VueClipboard from 'vue-clipboard2' //复制粘贴
import uView from './uni_modules/vk-uview-ui';// 引入 uView UI
import comMessage from './mycomponents/common/comMessage.vue'
import comLoading from './mycomponents/common/comLoading.vue'
// #ifndef VUE3
import Vue from 'vue'
@ -34,7 +33,6 @@ export function createApp() {
app.use(VueClipboard)
app.use(uView)
app.component('comMessage', comMessage)
app.component('comLoading', comLoading)
console.log("网络22请求")
// startApp(app);

4
src/manifest.json

@ -2,8 +2,8 @@
"name" : "wms",
"appid" : "__UNI__C9CF4BF",
"description" : "",
"versionName" : "1.0.13",
"versionCode" : 13,
"versionName" : "1.0.23",
"versionCode" : 23,
"transformPx" : false,
/* 5+App */
"app-plus" : {

4
src/mycomponents/scan/winComScanBalance.vue

@ -149,7 +149,11 @@
"未查找到库存余额"
this.showErrorMessage(hint)
} else if (res.data.length == 1) {
result.balance = res.data[0]
if(result.label.packingNumber!=result.balance.packingNumber){
result.balance.lableQty=result.label.qty
}
this.$emit("getBalance", result)
// this.closeScanPopup()
} else {

29
src/pages/fg/receiptByPlan.vue

@ -1,7 +1,36 @@
<template>
<view class="">
<view class="">
<com-blank-view @goScan='openFg' v-if="detailSource.length==0"></com-blank-view>
</view>
</view>
</template>
<script>
import comBlankView from '@/mycomponents/common/comBlankView.vue'
export default {
components: {
comBlankView
},
data() {
return {
detailSource:[]
};
},
onShow() {
},
mounted() {
},
methods: {
openFg(){
}
}
}
</script>
<style>

29
src/pages/fg/receiptNoPlan.vue

@ -1,7 +1,36 @@
<template>
<view class="">
<view class="">
<com-blank-view @goScan='openFg' v-if="detailSource.length==0"></com-blank-view>
</view>
</view>
</template>
<script>
import comBlankView from '@/mycomponents/common/comBlankView.vue'
export default {
components: {
comBlankView
},
data() {
return {
detailSource:[]
};
},
onShow() {
},
mounted() {
},
methods: {
openFg(){
}
}
}
</script>
<style>

32
src/pages/package/record/overPackageRecord.vue

@ -157,7 +157,7 @@
resultData: {}
};
},
onShow() {
onLoad() {
this.getBusinessType();
},
//
@ -259,6 +259,9 @@
}
var itemp = createItemInfo(balance, pack);
let newDetail = createDetailInfo(balance, pack); //
if(balance.lableQty){
newDetail.handleQty =balance.lableQty
}
itemp.subList.push(newDetail);
this.detailSource.push(itemp)
this.itemCode = balance.itemCode;
@ -275,6 +278,9 @@
})
if (detail == undefined) {
let newDetail = createDetailInfo(balance, pack);
if(balance.lableQty){
newDetail.handleQty =balance.lableQty
}
item.subList.push(newDetail);
this.scanPopupGetFocus()
} else {
@ -319,18 +325,6 @@
this.getBusinessType()
}
},
showFromLocationPopup() {
this.$nextTick(() => {
let overPackageRecord = uni.getStorageSync('overPackageRecord')
if (overPackageRecord) {
this.$refs.scanLocationCode.openScanPopupSimulate(overPackageRecord)
} else {
this.$refs.scanLocationCode.openScanPopup();
}
})
},
closeScanPopup() {
if (this.$refs.scanPopup) {
@ -468,23 +462,13 @@
closeScanMessage() {
this.scanPopupGetFocus();
},
getLocation(location, code) {
if (code) {
uni.setStorageSync('overPackageRecord', code)
}
this.getFromLocationCode(location, code)
},
getFromLocationCode(location, code) {
this.fromLocationCode = code;
this.openScanPopup();
},
showCommitSuccessMessage(hint, pointData) {
this.$refs.comMessage.showSuccessMessage(hint, res => {
this.detailSource = []
this.fromLocationCode = '';
this.dataContent = {}
this.itemCode=""
if (pointData.length > 0) {
uni.navigateTo({
url: `/pages/point/index?points=${JSON.stringify(pointData)}`

347
src/pages/package/record/splitPackageRecord.vue

@ -1,7 +1,7 @@
<template>
<view class="page-wraper">
<view class="">
<com-blank-view @goScan='showFromLocationPopup' v-if="detailSource.length==0"></com-blank-view>
<com-blank-view @goScan='getBusinessType' v-if="detailSource.length==0"></com-blank-view>
</view>
<view class="page-wraper" v-if="detailSource.length>0">
@ -9,11 +9,18 @@
<scroll-view scroll-y="true" class="page-main-scroll">
<view class="detail-list" v-for="(item, index) in detailSource" :key="item.id">
<view class="">
<com-package-record :dataContent="item" @removeItem="removeItem(index,item)"
@updateData="updateData" @removePack='updateData'>
</com-package-record>
<record-com-detail-card :dataContent="item" @removeItem="removeItem(index,item)"
@updateData="updateData" @removePack='updateData' :isShowToLocation="false">
</record-com-detail-card>
</view>
</view>
<view class="uni-flex uni-row" style="margin-left: 30rpx;margin-top: 10rpx; ">
<text style="font-size: 35rpx; margin-right: 20rpx;">拆出数量 :</text>
<uni-number-box @change="calcQty($event,splitCount)" :value="splitCount">
</uni-number-box>
<uom :uom="detailSource[0].uom"></uom>
</view>
</scroll-view>
</view>
@ -28,14 +35,11 @@
</view>
</view>
<win-scan-button @goScan='showScanPopupPack'></win-scan-button>
<win-scan-button @goScan='openScanPopup'></win-scan-button>
</view>
<win-scan-pack-and-location ref="scanPopup" @getResult='getScanResult' :allowModifyLocation="false">
</win-scan-pack-and-location>
<win-scan-location ref="scanLocationCode" title="来源库位" @getLocation='getLocation'
:locationAreaTypeList="fromLocationAreaTypeList"></win-scan-location>
<win-scan-pack title="拆分标签" ref="scanPopupPack" @getResult='getScanPackResult'></win-scan-pack>
<winComScanBalance ref="scanPopup" @getBalance='getScanResult' :bussinessCode="typeCode">
</winComScanBalance>
<comMessage ref="comMessage"></comMessage>
</view>
</template>
@ -43,6 +47,7 @@
<script>
import {
goHome,
showConfirmMsg
} from '@/common/basic.js';
import {
splitPackageRecordSubmit
@ -73,6 +78,8 @@
import winScanPackAndLocation from "@/mycomponents/scan/winScanPackAndLocation.vue"
import recommendBalance from '@/mycomponents/balance/recommendBalance.vue'
import comPackageRecord from '@/pages/package/coms/comPackageRecord.vue'
import winComScanBalance from '@/mycomponents/scan/winComScanBalance.vue'
import uom from '@/mycomponents/qty/uom.vue'
export default {
components: {
@ -84,7 +91,9 @@
winScanPackAndLocation,
recommendBalance,
recordComDetailCard,
comPackageRecord
comPackageRecord,
winComScanBalance,
uom
},
data() {
return {
@ -98,10 +107,14 @@
businessType: {},
dataContent: {},
currentItemCode: "",
toPackingNumber: ""
toPackingNumber: "",
itemCode:"",
splitCount:1,
typeCode:"SplitPackage"
};
},
onLoad(option) {
<<<<<<< Updated upstream
uni.setNavigationBarTitle({
title: option.title
})
@ -115,6 +128,9 @@
this.showErrorMessage(res.message)
}
});
=======
this.getBusinessType();
>>>>>>> Stashed changes
},
//
onNavigationBarButtonTap(e) {
@ -127,86 +143,98 @@
onPullDownRefresh() {},
mounted() {
// this.showFromLocationPopup();
},
mounted() {},
methods: {
showScanPopupPack() {
this.$refs.scanPopupPack.openScanPopup();
getBusinessType() {
getBusinessType(this.typeCode, res => {
if (res.success) {
this.businessType = res.businessType;
this.fromInventoryStatuses = getDirectoryItemArray(res.fromInventoryStatuses);
this.fromLocationAreaTypeList = res.fromLocationAreaTypeList
this.openScanPopup();
} else {
this.showErrorMessage(res.message)
}
});
},
openScanPopup() {
if (this.businessType) {
if(this.detailSource.length>0){
showConfirmMsg("拆包信息还没提交,是否要重新扫描?",res=>{
if(res){
this.clearData();
this.$refs.scanPopup.openScanPopup(this.businessType);
}
})
}else {
this.$refs.scanPopup.openScanPopup(this.businessType);
}
} else {
this.getBusinessType()
}
},
getScanResult(result) {
this.setData(result);
},
setData(result) {
let balance = result.balance;
let label = result.label;
let pack = result.package;
this.fromPackUnit = pack.packUnit;
this.fromPack = pack;
if (this.fromLocationCode && this.fromLocationCode != balance.locationCode) {
this.showErrorMessage("箱码[" + balance.packingNumber + "批次[" + balance.batch + "]在库位[" + this
.fromLocationCode + "]没有库存余额")
return;
}
var item = this.detailSource.find(res => {
if (res.itemCode == balance.itemCode) {
return res
}
})
if (item == undefined) {
if (this.itemCode != "" && this.itemCode != balance.itemCode) {
this.showErrorMessage("请扫描物料为【" + this.itemCode + "】的箱码")
return;
}
var itemp = createItemInfo(balance, pack);
itemp.handleQty=0
let newDetail = createDetailInfo(balance, pack); //
newDetail.scaned = false;
newDetail.Records = [];
newDetail.handleQty = 0
newDetail.handleQty=0
newDetail.scaned=false
itemp.subList.push(newDetail);
this.detailSource.push(itemp)
this.itemCode = balance.itemCode;
this.fromLocationCode = balance.locationCode
this.closeScanPopup()
} else {
this.showErrorMessage("箱码[" + balance.packingNumber + "批次[" + balance.batch + "]已经在列表中")
}
this.$refs.scanPopup.closeScanPopup();
this.showScanPopupPack();
},
getScanPackResult(result) {
var packingNumber = result.label.packingNumber;
var batch = result.label.batch;
var qty = result.label.qty;
var itemCode = result.label.itemCode;
var uom = result.package.uom;
var item = this.detailSource.find(r => r.itemCode == itemCode);
if (item == undefined) {
this.showErrorMessage("物料号【" + itemCode + "】不在列表中")
} else {
var temp = {
scaned: true,
packingNumber: packingNumber,
batch: batch,
qty: Number(qty),
LabelQty: Number(qty),
stdPackUnit: result.package.stdPackUnit,
stdPackQty: result.package.stdPackQty,
uom: uom
var detail = item.subList.find(r => {
if (r.packingNumber == balance.packingNumber &&
r.batch == balance.batch &&
r.locationCode == balance.locationCode &&
r.inventoryStatus == balance.inventoryStatus) {
return r;
}
})
if (detail == undefined) {
let newDetail = createDetailInfo(balance, pack);
newDetail.handleQty=0
item.subList.push(newDetail);
this.closeScanPopup()
} else {
if (detail.scaned == true) {
this.showErrorMessage("箱码[" + balance.packingNumber + "批次[" + balance.batch + "]已经在列表中")
}
}
var sumQty = 0;
item.subList[0].Records.forEach(function(item1, index) {
sumQty = calc.add(sumQty,item1.qty)
});
// if (calc.add(qty,sumQty)> item.subList[0].qty) {
// this.showErrorMessage("" + (Number(qty) + sumQty) + "(" + qty + "+" +
// sumQty +
// ")" + Number(item.subList[0].qty) + "!");
// return;
// }
item.subList[0].Records.push(temp);
this.$forceUpdate();
}
this.calcHandleQty();
},
calcHandleQty() {
for (let detail of this.detailSource[0].subList) {
let hQty = 0;
if (detail.Records.length > 0) {
detail.Records.forEach(r => {
hQty = calc.add(hQty,r.qty)
})
}
detail.handleQty = hQty;
}
calcHandleQty(this.detailSource)
this.$forceUpdate();
},
@ -219,29 +247,19 @@
});
},
updateData() {
this.calcHandleQty();
for (var i = 0; i < this.detailSource.length; i++) {
let item = this.detailSource[i];
if (item.qty == 0) {
this.detailSource.splice(i, 1)
}
}
},
removeItem(index, item) {
this.detailSource.splice(index, 1)
},
openScanPopup() {
if (this.fromLocationCode == "") {
this.showFromLocationPopup();
return
}
this.$refs.scanPopup.openScanPopupForType(this.fromLocationCode, this.businessType);
},
showFromLocationPopup() {
this.$nextTick(() => {
this.$refs.scanLocationCode.openScanPopup();
})
},
closeScanPopup() {
if (this.$refs.scanPopup != undefined) {
this.$refs.scanPopup.closeScanPopup();
@ -256,6 +274,15 @@
commit() {
if (this.detailSource.length > 0 && this.detailSource[0].subList.length > 0) {
if(this.splitCount==0){
this.showErrorMessage("拆出数量不能等于0")
return;
}
if(this.splitCount>this.detailSource[0].qty){
this.showErrorMessage("拆出数量不能大于库存数量")
return;
}
uni.showLoading({
title: "提交中....",
mask: true
@ -263,17 +290,38 @@
var params = this.setParams();
console.log("提交" + JSON.stringify(params))
splitPackageRecordSubmit(params).then(res => {
uni.hideLoading()
if (res.data) {
this.showCommitSuccessMessage("提交成功<br>生成拆包记录<br>" + res.data)
} else {
this.showErrorMessage("提交失败[" + res.msg + "]")
}
}).catch(error => {
uni.hideLoading()
this.showErrorMessage(error)
})
// splitPackageRecordSubmit(params).then(res => {
// uni.hideLoading()
// if (res.data) {
// let list = []
// res.data.forEach(item => {
// list.push({
// itemCode: item.itemCode, //
// itemName: item.itemName, //
// packName: item.packName, //
// packageCode: item.toPackingNumber, //
// batch: item.toBatch, //
// parentNumber: item.parentNumber, //
// itemType: item.itemType, //
// asnNumber: item.asnNumber, //ASN
// supplierCode: item.supplierCode, //
// qty: item.qty, //
// printTimes: getCurrDateTime(), //
// productionLineCode: item.productionLineCode, //线
// barcodeString: item.barcodeString, //
// barcodeBase64: '',
// })
// })
// this.showCommitSuccessMessage("<br><br>", list)
// } else {
// this.showErrorMessage("[" + res.msg + "]")
// }
// }).catch(error => {
// uni.hideLoading()
// this.showErrorMessage(error)
// })
} else {
this.showErrorMessage("没有要提交的数据")
}
@ -281,38 +329,33 @@
},
setParams() {
var subList = []
var creator = this.$store.state.user.id
this.detailSource.forEach(item => {
item.subList.forEach(detail => {
detail.Records.forEach(record => {
if (record.scaned) {
var subItem = {};
Object.assign(subItem, detail)
subItem.itemCode = subItem.itemCode;
subItem.itemName = detail.package.itemName;
subItem.itemDesc1 = detail.package.itemDesc1;
subItem.itemDesc2 = detail.package.itemDesc2;
subItem.fromInventoryStatus = subItem.inventoryStatus;
subItem.toInventoryStatus = subItem.inventoryStatus;
subItem.fromQty = subItem.qty
subItem.toQty = record.qty
subItem.fromPackingNumber = subItem.packingNumber;
subItem.toPackingNumber = record.packingNumber;
subItem.fromBatch = subItem.batch;
subItem.toBatch = record.batch;
subItem.fromLocationCode = subItem.fromLocationCode;
subItem.package = ""
subItem.Records = ""
subList.push(subItem)
}
})
var subItem = {};
Object.assign(subItem, detail)
subItem.itemCode = subItem.itemCode;
subItem.itemName = detail.package.itemName;
subItem.itemDesc1 = detail.package.itemDesc1;
subItem.itemDesc2 = detail.package.itemDesc2;
subItem.fromInventoryStatus = subItem.inventoryStatus;
subItem.toInventoryStatus = subItem.inventoryStatus;
subItem.fromQty = subItem.qty
subItem.toQty = subItem.qty
subItem.fromPackingNumber = subItem.packingNumber;
subItem.toPackingNumber = subItem.packingNumber;
subItem.fromBatch = subItem.batch;
subItem.toBatch = subItem.batch;
subItem.fromLocationCode = subItem.fromLocationCode;
subItem.package = ""
subItem.Records = ""
subList.push(subItem)
})
})
@ -326,13 +369,6 @@
if (res) {}
});
},
showErrorMessage(message) {
this.$refs.comMessage.showErrorMessage(message, res => {
if (res) {
}
});
},
showScanMessage(message) {
this.$refs.comMessage.showScanMessage(message);
@ -345,41 +381,38 @@
closeScanMessage() {
this.scanPopupGetFocus();
},
getLocation(location, code) {
this.getFromLocationCode(location, code)
},
getFromLocationCode(location, code) {
this.fromLocationCode = code;
this.openScanPopup();
},
getToLocationCode(location, code) {
if (this.fromLocationCode == code) {
uni.showToast({
title: "来源库位[" + this.fromLocationCode + "]不能与目标库位[" + code + "]一致",
duration: 2000
})
return
}
this.toLocationCode = code;
},
showCommitSuccessMessage(hint) {
showCommitSuccessMessage(hint, pointData) {
this.$refs.comMessage.showSuccessMessage(hint, res => {
this.detailSource = []
this.fromLocationCode = '';
this.currentItemCode = ""
this.dataContent = {}
this.clearData();
if (pointData.length > 0) {
uni.navigateTo({
url: `/pages/point/index?points=${JSON.stringify(pointData)}`
});
}
})
},
clearData(){
this.detailSource = []
this.fromLocationCode = '';
this.currentItemCode = ""
this.dataContent = {}
this.itemCode=""
this.splitCount=1
},
calcQty(val) {
this.splitCount =val;
// if(val<=0){
// this.showErrorMessage("0")
// }
},
updateData() {
this.calcHandleQty();
}
}
}
</script>
<style scoped lang="scss">
</style>
</style>

6
src/pages/repleinsh/record/directRepleinshRecord.vue

@ -238,6 +238,9 @@
newDetail.packingNumber =pack.number
newDetail.packUnit =pack.packUnit;
newDetail.packQty=pack.packQty;
if(balance.lableQty){
newDetail.handleQty =balance.lableQty
}
itemp.subList.push(newDetail);
this.detailSource.push(itemp)
this.itemCode = balance.itemCode;
@ -258,6 +261,9 @@
newDetail.packingNumber =pack.number
newDetail.packUnit =pack.packUnit;
newDetail.packQty=pack.packQty;
if(balance.lableQty){
newDetail.handleQty =balance.lableQty
}
item.subList.push(newDetail);
this.scanPopupGetFocus()
} else {

8
src/uni_modules/wz-select-popup/changelog.md

@ -0,0 +1,8 @@
## 1.0.11(2023-11-15)
reqFun只允许传入Promise
## 1.0.10(2023-11-13)
去除默认值
## 1.0.1(2023-11-13)
更新插件依赖
## 1.0.0(2023-11-13)
第一次发布

22
src/uni_modules/wz-select-popup/components/wz-select-popup/util/defaultConfig.js

@ -0,0 +1,22 @@
export default {
mode: 'radio', //radio checkbox 单选、多选
showType: 'text', //暂时不用 text image 纯文字、图文
selected: '', //已选中的
dataList: null, //如果dataList传入了数组则直接使用传入的数组渲染,无需再配置proxyConfig
proxyConfig: { //组件内部代理请求数据配置
reqFun: Promise.resolve(), //请求方法
localPaging: false //前端本地分页,如果使用的是dataList默认是本地分页
},
page: { //如果是组件内部代理请求会把page的值加入到请求参数里
pageIndex: 1, //当前页
pageSize: 20 //页大小
},
search: {
type: 'local', //搜索的类型,local本地搜索, remote向服务器请求。只有使用proxyConfig.reqFun请求才能配置为remote
},
fields: { //显示的key,value
label: 'name',
value: 'code',
image: 'image', //暂时不用
}
}

74
src/uni_modules/wz-select-popup/components/wz-select-popup/util/index.js

@ -0,0 +1,74 @@
/**深度合并对象*/
export const deepMerge = function(target = {}, source = {}) {
target = deepClone(target); //深度克隆
if (typeof target !== 'object' || typeof source !== 'object') return false; //判断类型
for (let prop in source) {
if (!source.hasOwnProperty(prop)) continue; //检测属性是否为对象的自有属性
if (prop in target) { //prop 中是否有 target 属性
if (typeof target[prop] !== 'object') {
target[prop] = source[prop];
} else {
if (typeof source[prop] !== 'object') {
target[prop] = source[prop];
} else {
if (target[prop].concat && source[prop].concat) {
target[prop] = target[prop].concat(source[prop]); //合并
} else {
target[prop] = deepMerge(target[prop], source[prop]); //递归
}
}
}
} else {
target[prop] = source[prop];
}
}
return target;
}
const isArray = function(arr) {
return Object.prototype.toString.call(arr) === '[object Array]';
}
/**深度合并对象*/
export const deepClone = function(obj) {
// 对常见的“非”值,直接返回原来值
if ([null, undefined, NaN, false].includes(obj)) return obj;
if (typeof obj !== "object" && typeof obj !== 'function') {
//原始类型直接返回
return obj;
}
let o = isArray(obj) ? [] : {};
for (let i in obj) {
if (obj.hasOwnProperty(i)) {
o[i] = typeof obj[i] === "object" ? deepClone(obj[i]) : obj[i];
}
}
return o;
};
let timeout = null
/**
* 防抖原理一定时间内只有最后一次操作再过wait毫秒后才执行函数
*
* @param {Function} func 要执行的回调函数
* @param {Number} wait 延时的时间
* @param {Boolean} immediate 是否立即执行
* @return null
*/
export const debounce = function(func, wait = 500, immediate = false) {
// 清除定时器
if (timeout !== null) clearTimeout(timeout)
// 立即执行,此类情况一般用不到
if (immediate) {
const callNow = !timeout
timeout = setTimeout(() => {
timeout = null
}, wait)
if (callNow) typeof func === 'function' && func()
} else {
// 设置定时器,当最后一次操作后,timeout不会再被清除,所以在延时wait毫秒后执行func回调方法
timeout = setTimeout(() => {
typeof func === 'function' && func()
}, wait)
}
}

130
src/uni_modules/wz-select-popup/components/wz-select-popup/wz-list.vue

@ -0,0 +1,130 @@
<template>
<view class="wz-list">
<view :class="item.check?'check-item':'list-item'" v-for="(item,index) in list" :key="index"
@click="$emit('checkList',item)">
<view class="" v-if="isShowIcon">
<uni-icons v-if="item.check" type="checkbox-filled" color="#007aff" size="24"></uni-icons>
<uni-icons v-else type="circle" color="#999" size="24"></uni-icons>
</view>
({{index+1}})
<view class="uni-flex" style="flex-direction: column; font-size: 32rpx;">
<view class="list-item-text">ERP料号 : {{item.itemCode}}</view>
<view class="list-item-text">名称 : {{item.itemName}}</view>
<view class="list-item-text">描述 : {{item.itemDesc1}}</view>
</view>
</view>
<view class="loadmore" @click="$emit('loadmore')">
<view v-if="page.loading == 'loading'" class="icon">
<uni-icons type="spinner-cycle" size="24"></uni-icons>
</view>
<view class="">{{loadmore[page.loading]}}</view>
</view>
</view>
</template>
<script>
export default {
name: 'wz-list',
props: {
list: {
type: Array,
default: () => []
},
isShowIcon: {
type: Boolean,
default: true
},
page: {
type: Object,
default: () => {
return {
loading: 'loading', //loading|loadmore|nomore
isEmpty: false
}
}
},
fields: {
type: Object,
default: () => {
return {
label: 'name',
value: 'code',
image: 'image'
}
}
}
},
data() {
return {
loadmore: {
loading: '正在加载',
loadmore: '点击或上拉加载更多',
nomore: '没有更多了'
}
}
},
onLoad() {
},
methods: {
}
}
</script>
<style scoped lang="scss">
.wz-list {
@mixin wz-line ($line) {
overflow: hidden;
word-break: break-all;
text-overflow: ellipsis;
display: -webkit-box; //
-webkit-box-orient: vertical; //
-webkit-line-clamp: $line;
}
.list-item {
display: flex;
align-items: center;
color: $uni-text-color-grey;
padding: $uni-spacing-col-base;
margin-bottom: $uni-spacing-col-sm;
background-color: #fff;
.list-item-text {
@include wz-line(2);
margin-left: 4px;
margin-bottom: 1px
}
}
.check-item {
@extend .list-item;
color: $uni-text-color;
}
.loadmore {
display: flex;
justify-content: center;
align-items: center;
font-size: $uni-font-size-sm;
padding: 8px;
@keyframes rotateInfinite {
form {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
.icon {
animation: rotateInfinite infinite 1s linear;
}
}
}
</style>

90
src/uni_modules/wz-select-popup/components/wz-select-popup/wz-search.vue

@ -0,0 +1,90 @@
<template>
<view class="wz-search">
<view class="search-input">
<uni-icons color="#808080" size="22" type="search"></uni-icons>
<view class="right">
<input :value="value" :placeholder="placeholder" @input="change" />
</view>
<uni-icons v-if="value || value === 0 || value === '0'" color="#808080" size="22" type="clear"
@click="clear">
</uni-icons>
</view>
<view class="search-button">
<button class="button" @click="searchData">搜索</button>
</view>
</view>
</template>
<script>
export default {
name: 'wz-search',
props: {
value: {},
placeholder: {
type: String,
default: '请输入搜索关键字'
}
},
data() {
return {
searchVal: ''
}
},
methods: {
change({
detail
}) {
this.$emit('input', detail.value)
},
clear() {
this.$emit('clear')
this.$emit('input', '')
},
searchData() {
this.$emit('search', this.value)
}
}
}
</script>
<style scoped lang="scss">
.wz-search {
display: flex;
align-items: center;
font-size: $uni-font-size-base;
.search-input {
flex: 1;
display: flex;
align-items: center;
height: 32px;
padding: 0 8px;
border: 1px solid $uni-border-color;
border-radius: 100px;
.right {
flex: 1;
width: 100%;
margin: 0 4px;
}
}
.search-button {
margin-left: 8px;
.button {
padding: 0;
font-size: inherit;
background-color: transparent;
color: inherit;
width: 100%;
height: 32px;
line-height: 32px;
&::after {
border: 0;
}
}
}
}
</style>

357
src/uni_modules/wz-select-popup/components/wz-select-popup/wz-select-popup.vue

@ -0,0 +1,357 @@
<template>
<view>
<u-popup ref="popup" v-model="showPopup" border-radius="15" style="width: 100%;" mode="center">
<view class="select-popup-content">
<view class="uni-flex" style="display: flex; width: 100%;justify-content: center; ">
<view class="" style=" font-size: 35rpx; margin: 20rpx; font-weight: bold;">
{{title}}
</view>
</view>
<u-line></u-line>
<view class="popup-search">
<wz-search v-model="searchValue" @search="searchData('search')" @clear="searchData('clear')" @input="change"/>
</view>
<scroll-view class="popup-scroll-y" :scroll-top="scroll.newTop" scroll-y
:style="{'--popup-scroll-height': scrollH}" @scrolltolower="scrolltolower" @scroll="viewScroll">
<wz-list :list="dataList" :fields="popConfig.fields" :page="pageStatus" @checkList="checkList"
@loadmore="scrolltolower" :isShowIcon="false" />
</scroll-view>
<view v-if="popConfig.mode =='checkbox'" class="footer-button">
<view class="button-item">
<button class="button" @click="close">取消</button>
</view>
<view class="button-item">
<button class="button" type="primary" @click="checkboxConfirm">确定</button>
</view>
</view>
</view>
</u-popup>
</view>
</template>
<script>
import wzSearch from "./wz-search.vue"
import wzList from "./wz-list.vue"
import {
deepMerge,
deepClone,
debounce
} from "./util/index.js"
import defaultConfig from "./util/defaultConfig.js"
let allDataList = null
/**
* 下拉选择弹窗
* @function open 打开弹窗
* @property {String} scrollH 滚动内容的高度不包含搜索框和多选时的底部按钮栏需要带单位
* @property {String} popType = popup弹出方向默认bottom 请查看uni-popup文档
*/
export default {
name: "wz-select-popup",
components: {
wzSearch,
wzList
},
props: {
scrollH: {
type: String,
default: '50vh'
},
popType: {
type: String,
default: 'bottom'
},
title: {
type: String,
default: '物料查询'
},
},
data() {
return {
dataList: [],
searchValue: '',
pageStatus: {
loading: '', //loading|loadmore|nomore
isEmpty: false
},
scroll: {
oldTop: 0,
newTop: 0
},
popConfig: {},
showPopup:false
};
},
mounted() {
this.popConfig = deepClone(defaultConfig)
},
destroyed() {
allDataList = null
},
methods: {
viewScrollTop() {
// view
this.scroll.newTop = this.scroll.oldTop
this.$nextTick(function() {
this.scroll.newTop = 0
});
},
viewScroll(e) {
debounce(() => {
this.scroll.oldTop = e.detail.scrollTop
}, 100)
},
change(data){
console.log("输入",data)
this.searchData("search")
},
searchData(type) {
const {
search,
fields,
proxyConfig
} = this.popConfig
this.pageStatus.loading == 'loading'
this.pageStatus.isEmpty = false
this.popConfig.page.pageIndex = 1
this.dataList = []
this.viewScrollTop()
if (search.type === 'local' && proxyConfig.localPaging) {
if (type === 'clear') {
this.listInit(allDataList)
return
}
this.dataList = allDataList?.filter(v => v[fields.label].includes(this.searchValue))
} else {
this.proxyRequest()
}
},
/**
* 打开弹窗选择器
* @function
* @param {Object} configs 弹出层配置
* @prmam {String} configs.mode 选择模式radio单选 | checkbox多选 默认是radio
* @param {String | Array} configs.selected 已选中的value,单选传字符串多选传数组
*
* @param {Array} dataList 如果dataList传入了数组则直接使用传入的数组渲染无需再配置proxyConfig
*
* @param {Object} configs.proxyConfig 组件内部代理请求数据配置
* @param {Function} proxyConfig.reqFun 请求数据的方法
* @param {Boolean} proxyConfig.localPaging 是否本地分页
*
* @param {Object} configs.search //
* @param {String} search.type = local组件内部排序搜索 | remote请求接口会把输入的搜索文字传入proxyConfig.reqFun
*
* @param {Object} configs.page 分页的页码参数
* @param {Number} page.pageIndex 当前页
* @param {Number} page.pageSize 页大小
*
* @param {Object} configs.fields 传入选项数据的key value
* @param {String} fields.label 用于显示文字的key
* @param {String} fields.value 用于取值的key
*/
open(configs = {}) {
const {
dataList,
...config
} = configs || {}
if (config) {
this.popConfig = deepMerge(this.popConfig, config)
}
this.dataList=[]
this.searchValue=""
this.listInit(dataList)
this.showPopup =true;
},
/**列表数据初始化*/
listInit(dataList) {
this.pageStatus.loading = 'loading'
//dataListproxyConfig.reqFun
if (dataList && dataList instanceof Array) {
console.warn('直接使用传入的dataList')
allDataList = deepClone(dataList)
this.popConfig.proxyConfig.localPaging = true
this.getLocalPagingData()
return
}
if (this.popConfig.proxyConfig.reqFun instanceof Function) {
// if (Object.prototype.toString.call(this.popConfig.proxyConfig.reqFun) != '[object Promise]') {
// throw 'proxyConfig.reqFunreturn Promise.resolve()'
// }
this.proxyRequest()
}
},
/**组件代理请求*/
proxyRequest() {
console.log("请求proxyRequest")
const {
proxyConfig,
page
} = this.popConfig
this.pageStatus.loading = 'loading'
proxyConfig.reqFun({
...page,
searchValue: this.searchValue
}).then(list => {
list.items.forEach(res=>{
res.code =res.itemCode
res.name =res.itemName;
})
if (proxyConfig.localPaging) { //
allDataList = list.items
this.getLocalPagingData()
return
}
this.pageStatus.loading = 'loadmore'
// if (!list.items || list.items.length != page.pageSize) {
// this.pageStatus.loading = 'nomore'
// this.pageStatus.isEmpty = true
// }
if (!list.items || list.items.length ==0) {
this.pageStatus.loading = 'nomore'
this.pageStatus.isEmpty = true
}
this.handleSelected(list.items)
this.dataList = this.dataList.concat(list.items)
})
},
close() {
this.showPopup =false;
this.$emit('close')
setTimeout(() => {
this.allDataList = null
this.pageStatus.isEmpty = false
this.pageStatus.loading = 'loading'
this.popConfig = defaultConfig
this.dataList = []
}, 500)
},
/**上拉触底*/
scrolltolower() {
if (!this.pageStatus.isEmpty && this.pageStatus.loading != 'loading') {
this.popConfig.page.pageIndex++
if (this.popConfig.proxyConfig.localPaging) {
this.getLocalPagingData()
} else {
this.proxyRequest()
}
}
},
/**前端本地分页*/
getLocalPagingData() {
const {
pageIndex,
pageSize
} = this.popConfig.page
const list = allDataList.slice(pageSize * (pageIndex - 1), pageSize + (pageSize * (pageIndex - 1)))
this.pageStatus.loading = 'loadmore'
this.handleSelected(list)
this.dataList = this.dataList.concat(list)
if (!list || list.length != pageSize) {
this.pageStatus.loading = 'nomore'
this.pageStatus.isEmpty = true
}
},
/**多选点击确定*/
checkboxConfirm() {
const list = this.dataList.filter(v => v.check)
const data = {
names: [],
values: []
}
const {
fields,
mode
} = this.popConfig
list.forEach(v => {
data.names.push(v[fields.label])
data.values.push(v[fields.value])
})
this.$emit('select', mode, {
...data,
origin: list,
})
this.close()
},
/**选择*/
checkList(data, index) {
const {
mode,
fields
} = this.popConfig
if (mode == 'radio') {
this.dataList.forEach(v => {
v.check = v[fields.value] == data[fields.value] ? true : false
})
this.$emit('select', mode, data)
this.close()
} else {
data.check = !data.check
}
},
/**处理已选中的选项*/
handleSelected(list) {
const {
selected,
fields
} = this.popConfig
list?.forEach(item => {
const valueCode = item.itemCode
const isChecked = selected instanceof Array ? selected.includes(valueCode) : selected ==
valueCode
item.check = isChecked ? true : false
})
},
}
}
</script>
<style lang="scss" scoped>
.select-popup-content {
background-color: #fff;
.popup-search {
font-size: $uni-font-size-base;
padding: 4px $uni-spacing-row-base;
border-bottom: 1px solid $uni-border-color;
}
.footer-button {
display: flex;
justify-content: space-between;
align-items: center;
text-align: center;
.button-item {
flex: 1;
padding: 4px 16px;
.button {
width: 100%;
height: 36px;
line-height: 36px;
font-size: $uni-font-size-base;
}
}
}
.popup-scroll-y {
height: var(--popup-scroll-height);
padding: $uni-spacing-col-sm $uni-spacing-col-base;
box-sizing: border-box;
background-color: $uni-bg-color-grey;
}
}
</style>

82
src/uni_modules/wz-select-popup/package.json

@ -0,0 +1,82 @@
{
"id": "wz-select-popup",
"displayName": "popup弹窗选择器",
"version": "1.0.11",
"description": "popup弹窗选择器支持单选,多选,组件内部代理请求,自定义label,value显示,分页加载,",
"keywords": [
"wz-select-popup"
],
"repository": "",
"engines": {
"HBuilderX": "^3.96"
},
"dcloudext": {
"type": "component-vue",
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "插件不采集任何数据",
"permissions": "无"
},
"npmurl": ""
},
"uni_modules": {
"dependencies": ["uni-icons", "uni-popup"],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"Vue": {
"vue2": "y",
"vue3": "u"
},
"App": {
"app-vue": "y",
"app-nvue": "u",
"app-uvue": "n"
},
"H5-mobile": {
"Safari": "u",
"Android Browser": "u",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "u"
},
"H5-pc": {
"Chrome": "y",
"IE": "u",
"Edge": "y",
"Firefox": "u",
"Safari": "u"
},
"小程序": {
"微信": "y",
"阿里": "u",
"百度": "u",
"字节跳动": "u",
"QQ": "u",
"钉钉": "u",
"快手": "u",
"飞书": "u",
"京东": "u"
},
"快应用": {
"华为": "u",
"联盟": "u"
}
}
}
}
}

132
src/uni_modules/wz-select-popup/readme.md

@ -0,0 +1,132 @@
# wz-select-popup
## 示例
```vue
<template>
<view>
<view class="" style="margin-bottom: 16px;">
<view style="margin-bottom: 8px;">names:{{userForm.names}}</view>
<view style="margin-bottom: 8px;">values:{{userForm.values}}</view>
<view style="margin-bottom: 8px;">origin:{{userForm.origin}}</view>
</view>
<button @click="open">打开</button>
<wz-select-popup ref="wzSelectPopup" @select="selectCheckbox" />
</view>
</template>
<script>
import {
simulationReqGetList,
getDataList
} from "@/common/mock.js"
export default {
data() {
return {
userForm: {
names: [],
values: [],
origin:[]
}
}
},
methods: {
open() {
this.$refs.wzSelectPopup.open({
mode: 'checkbox', //radio checkbox 单选、多选
// dataList:[], //如果dataList传入了数组则直接使用传入的数组渲染,无需再配置proxyConfig
selected: this.userForm.values, //已选中的数据
proxyConfig: { //组件内部代理请求数据配置
reqFun: this.reqGetList, //请求方法,在方法中请返回Promise.resolve([])
localPaging: false //前端本地分页
},
search: {
type: 'local', //local本地数据搜索 | remote请求接口
},
fields: {
label: 'name',
value: 'code'
}
})
},
selectCheckbox(mode, data) {
console.log(mode, data)
this.userForm.names = data.names
this.userForm.values = data.values
this.userForm.origin = data.origin
},
reqGetList(data) {
return simulationReqGetList({
pageIndex: data.pageIndex,
pageSize: data.pageSize,
name: data.searchValue
})
},
}
}
</script>
<style>
</style>
```
## 属性props
| 属性 | 类型 | 默认值 | 说明 |
| ------- | ------ | ------ | ------------------------------------------------------------ |
| scrollH | String | 50vh | 滚动内容的高度,不包含搜索框和多选时的底部按钮栏。需要带单位 |
## 方法methods
| 方法名 | 说明 | 参数 |
| ------ | -------- | ----------------------------- |
| open | 打开弹窗 | [configs](#open方法的configs) |
| close | 关闭弹窗 | |
### open方法的configs
| 参数名 | 说明 | 类型 | 默认值 |
| ----------- | ------------------------------------------------------------ | ------------- | -------------------------- |
| mode | 选择模式radio单选 ,checkbox多选 | String | radio |
| selected | 已选中的value, 单选传字符串,多选传数组 | String\|Array | [] |
| dataList | 传入了数组,则直接使用传入的数组渲染,无需再配置proxyConfig | Array | null |
| proxyConfig | 组件内部代理请求数据配置 | Object | [proxyConfig](#proxyConfig的属性) |
| search | 搜索配置 | Object | [search](#search的属性) |
| page | 分页配置,如果是组件内部代理请求会把page的值加入到proxyConfig.reqFun请求参数里 | Object | [page](#page的属性) |
| fields | 显示和取值的label,value | Object | [fields](#fields的属性) |
#### proxyConfig的属性
| 参数名 | 说明 | 类型 | 默认值 | 返回值 |
| ----------- | --------------------------------------------------------- | ------- | ------------------- | --------------------------------------- |
| reqFun | 向服务器请求数据的Promise方法,会返回页码以及搜索框的value | Promise | Promise.resolve([]) | ({pageIndex, pageSize,searchValue})=>{} |
| localPaging | 是否本地分页 | Boolean | false | |
#### search的属性
| 参数名 | 说明 | 类型 | 默认值 |
| ------ | ------------------------------------------------------------ | ------ | ------ |
| type | 搜索的类型,local本地搜索, remote向服务器请求。只有使用proxyConfig.reqFun请求才能配置为remote | String | local |
#### page的属性
| 参数名 | 说明 | 类型 | 默认值 |
| --------- | ------ | ------ | ------ |
| pageIndex | 当前页 | Number | 1 |
| pageSize | 页大小 | Number | 20 |
#### fields的属性
| 参数名 | 说明 | 类型 | 默认值 |
| ------ | ------------ | ------ | ------ |
| label | 显示的字段名 | String | name |
| value | 取值的字段名 | String | code |
## 事件enevt
| 名称 | 触发时机 | 返回值 |
| ------ | ------------------------------------------ | ------------------------------------------------------------ |
| select | 单选点击列表时触发,多选点击确定按钮时触发 | mode等于radio时:(mode, {check,name,code})=>{}<br/>mode等于checkbox时:(mode, {names,values,origin})=>{} |
| close | popup关闭 | void |
Loading…
Cancel
Save