Browse Source

库存查询

lilei 1 year ago
parent
commit
25ccb0f603

+ 79 - 0
App.vue

@@ -27,4 +27,83 @@
 		display: flex;
 		flex-direction: column;
 	}
+	/* felx布局 */
+	.flex{
+		display: -webkit-flex; /* Chrome 21+, Safari 6.1+, iOS Safari 7+, Opera 15/16 */  
+		display: -moz-flex; /* Firefox 18+ */  
+		display: -ms-flexbox; /* IE 10 */  
+		display: flex; /* Chrome 29+, Firefox 22+, IE 11+, Opera 12.1/17/18, Android 4.4+ */
+	}
+	/*伸缩流方向*/
+	.flex_column{
+	    -webkit-box-orient: vertical;
+	    -ms-flex-direction: column;
+	    -webkit-flex-direction: column;
+	    flex-direction: column;
+	}
+	/*主轴对齐*/
+	.justify_center{
+	    -webkit-box-pack: center;
+	    -ms-flex-pack: center;
+	    -webkit-justify-content: center;
+	    justify-content: center;
+	}
+	.justify_end{
+	    -webkit-box-pack: end;
+	    -ms-flex-pack: end;
+	    -webkit-justify-content: flex-end;
+	    justify-content: flex-end;
+	}
+	.justify_between{
+	    -webkit-box-pack: justify;
+	    -ms-flex-pack: justify;
+	    -webkit-justify-content: space-between;
+	    justify-content: space-between;
+	}
+	/*侧轴对齐*/
+	.align_start{
+	    -webkit-box-align: start;
+	    -ms-flex-align: start;
+	    -webkit-align-items: flex-start;
+	    align-items: flex-start;
+	}
+	.align_end{
+	    -webkit-box-align: end;
+	    -ms-flex-align: end;
+	    -webkit-align-items: flex-end;
+	    align-items: flex-end;
+	}
+	.align_center{
+	    -webkit-box-align: center;
+	    -ms-flex-align: center;
+	    -webkit-align-items: center;
+	    align-items: center;
+	}
+	.align_baseline{
+	    -webkit-box-align: baseline;
+	    -ms-flex-align: baseline;
+	    -webkit-align-items: baseline;
+	    align-items: baseline;
+	}
+	/*伸缩性*/
+	.flex_auto{
+	    -webkit-box-flex: 1;
+	    -ms-flex: auto;
+	    -webkit-flex: auto;
+	    flex: auto;
+	}
+	.flex_1{
+	    -webkit-box-flex: 1;
+	    -ms-flex: 1;
+	    -webkit-flex: 1;
+	    flex: 1;    
+	}
+	// ios15原生input的type=search时会默认自带搜索图标。
+	// 在全局的公共样式隐藏ios自带搜索图标即可
+	[type="search"]::-webkit-search-decoration {
+		display: none;
+	}
+	uni-toast {
+		z-index: 1000000;
+	}
 </style>

+ 18 - 0
config/api.js

@@ -102,3 +102,21 @@ export const storeCallOutDetailCount = (params, config = {}) => http.post('/stor
 // 产品维护
 export const dealerProductList = (params, config = {}) => http.post(`/dealerProduct/queryPage/${params.pageNo}/${params.pageSize}`, params, config)
 export const dealerProductSave = (params, config = {}) => http.post('/dealerProduct/save', params, config)
+
+//  产品品牌列表  无分页
+export const dealerProductBrandQuery = (params, config = {}) => http.post('/dealerProductBrand/queryList', params, config)
+//  产品分类 列表  无分页
+export const dealerProductTypeList = (params, config = {}) => http.post('/dealerProductType/query', params, config)
+
+//  库存 列表 合计
+export const stockCount = (params, config = {}) => http.post('/stock/queryCount', params, config)
+//  库存列表  分页
+export const stockList = (params, config = {}) => http.post(`/stock/queryPage/${params.pageNo}/${params.pageSize}`, params, config)
+//  库存 详情列表  分页
+export const stockDetailList = (params, config = {}) => http.post(`/stock/detail/queryPage/${params.pageNo}/${params.pageSize}`, params, config)
+//  库存流水(出入库明细) 列表  有分页
+export const stockFlowList = (params, config = {}) => http.post(`/stockFlow/queryPage/${params.pageNo}/${params.pageSize}`, params, config)
+//  库存 详情列表  分页
+export const querySumByProductLocation = (params, config = {}) => http.post(`/stock/detail/querySumByProductLocation/${params.pageNo}/${params.pageSize}`, params, config)
+//  出入库明细  列表  合计
+export const stockByProductSn = (params, config = {}) => http.post(`/stock/findByProductSn/${params.productSn}`, params, config)

+ 49 - 0
js_sdk/dc-clipboard/clipboard.js

@@ -0,0 +1,49 @@
+
+/**
+ * 设置粘贴板数据
+ * @param {String} text 要设置的字符串
+ * 如果未设置参数,则清空数据
+ */
+function setClipboardText(text){
+	try{
+	var os = plus.os.name;
+	text = text||'';
+	if('iOS' == os){
+		// var UIPasteboard  = plus.ios.importClass('UIPasteboard');  
+		// var pasteboard = UIPasteboard.generalPasteboard();  
+		// pasteboard.setValueforPasteboardType(text, 'public.utf8-plain-text');
+		var pasteboard = plus.ios.invoke('UIPasteboard', 'generalPasteboard');
+		plus.ios.invoke(pasteboard, 'setValue:forPasteboardType:', text, 'public.utf8-plain-text');
+	}else{
+		var main = plus.android.runtimeMainActivity();
+		// var Context = plus.android.importClass('android.content.Context');
+		// var clip = main.getSystemService(Context.CLIPBOARD_SERVICE);
+		var clip = main.getSystemService('clipboard');
+		plus.android.invoke(clip, 'setText', text);
+	}
+	}catch(e){
+		console.error('error @setClipboardText!!');
+	}
+}
+
+function getClipboardText(){
+	try{
+	var os = plus.os.name;
+	if('iOS' == os){
+		var pasteboard = plus.ios.invoke('UIPasteboard', 'generalPasteboard');
+		return plus.ios.invoke(pasteboard, 'valueForPasteboardType:', 'public.utf8-plain-text')
+	}else{
+		var main = plus.android.runtimeMainActivity();
+		var clip = main.getSystemService('clipboard');
+		return plus.android.invoke(clip, 'getText');
+	}
+	}catch(e){
+		console.error('error @getClipboardText!!');
+	}
+}
+
+
+module.exports = {
+	setText: setClipboardText,
+	getText: getClipboardText
+}

+ 23 - 0
libs/tools.js

@@ -127,4 +127,27 @@ export const checkVersionToLoadUpdate = function(newVersion,type,callback){
 			callback()
 		}
 	}
+}
+
+// 处理数字千位分隔符  num 数值,decimal要保留的小数位数
+export const toThousands = (num, decimal) => {
+  if (decimal) {
+    num = formatDecimal(num, decimal)
+  }
+  return num.toString().replace(/\d+/, function (n) { // 先提取整数部分
+    return n.replace(/(\d)(?=(\d{3})+$)/g, function ($1) {
+      return $1 + ','
+    })
+  })
+}
+// 保留decimal位小数(不四舍五入)  num 数值,decimal要保留的小数位数
+export const formatDecimal = function (num, decimal) {
+  num = num.toString()
+  const index = num.indexOf('.')
+  if (index !== -1) {
+    num = num.substring(0, decimal + index + 1)
+  } else {
+    num = num.substring(0)
+  }
+  return parseFloat(num).toFixed(decimal)
 }

+ 18 - 0
main.js

@@ -14,6 +14,24 @@ Vue.use(uView)
 
 let vuexStore = require("@/store/$u.mixin.js");
 Vue.mixin(vuexStore);
+ 
+/**
+ * 权限检查
+ */
+Vue.prototype.$hasPermissions = function (value) {
+ let isExist = false;
+ let permissionsStr = store.state.vuex_userData.permCodes;
+ if (permissionsStr == undefined || permissionsStr == null) {
+  return false;
+ }
+ let has = permissionsStr.find(item => {
+   return item == value
+ })
+ if (has) {
+  isExist = true;
+ }
+ return isExist;
+}
 
 const app = new Vue({
 	store,

+ 36 - 11
pages.json

@@ -133,15 +133,6 @@
                 "enablePullDownRefresh": false
             }
             
-        }
-        ,{
-            "path" : "pages/record/index",
-            "style" :                                                                                    
-            {
-                "navigationBarTitleText": "操作记录",
-                "enablePullDownRefresh": false
-            }
-            
         }
         ,{
             "path" : "pages/product/add",
@@ -256,8 +247,42 @@
                 "navigationBarTitleText": "条码维护",
                 "enablePullDownRefresh": false
             }
-            
-        }
+        },
+		{
+			"path": "pages/stock/index", // 库存
+			"style": {
+				"navigationBarTitleText": "库存",
+				"enablePullDownRefresh": false
+			}
+		},
+		{
+			"path": "pages/stock/stockSearch", // 库存查询
+			"style": {
+				"navigationBarTitleText": "库存查询",
+				"enablePullDownRefresh": false
+			}
+		},
+		{
+			"path": "pages/stock/detaiList", // 库存详情列表
+			"style": {
+				"navigationBarTitleText": "库存详情",
+				"enablePullDownRefresh": false
+			}
+		},
+		{
+			"path": "pages/stock/stockDetail", // 库存详情
+			"style": {
+				"navigationBarTitleText": "库存详情",
+				"enablePullDownRefresh": false
+			}
+		},
+		{
+			"path": "pages/stock/warehouseDetail", // 出入库明细详情
+			"style": {
+				"navigationBarTitleText": "出入库明细",
+				"enablePullDownRefresh": false
+			}
+		}
     ],
 	"globalStyle": {
 		"navigationBarTextStyle": "black",

+ 42 - 0
pages/common/copyText.vue

@@ -0,0 +1,42 @@
+<template>
+	<text class="copyText" @click.stop="copyText">复制</text>
+</template>
+
+<script>
+	import clipboard from "@/js_sdk/dc-clipboard/clipboard.js"
+	export default{
+		props:{
+			text: {
+				type: String,
+				default: ''
+			},
+			label: {
+				type: String,
+				default: ''
+			},
+		},
+		methods:{
+			// 复制
+			copyText(){
+				clipboard.setText(this.text);
+				uni.showToast({
+					icon: 'none',
+					title: this.label + '已复制成功'
+				})
+			},
+		}
+	}
+</script>
+
+<style>
+	.copyText{
+		font-size: 24upx;
+		color:#333;
+		margin-left: 20upx;
+		background: #c1c1bf;
+		font-weight: normal;
+		padding: 2upx 8upx;
+		border-radius: 8upx;
+		color: #fff;
+	}
+</style>

+ 175 - 0
pages/common/productBrand.vue

@@ -0,0 +1,175 @@
+<template>
+	<u-popup :show="isShow" class="brand-picker" @close="isShow=false" mode="bottom">
+		<view class="brand-header flex justify_between align_center">
+			<text class="brand-picker-btn" @click="onClean">清空</text>
+			<text>产品品牌</text>
+			<text class="brand-picker-btn color-blue" @click="isShow=false">
+				<u-icon name="close"></u-icon>
+			</text>
+		</view>
+		<view class="search-box">
+			<u-search
+				placeholder="请输入品牌名称搜索" 
+				v-model="queryWord" 
+				:show-action="isGobleSearch||queryWord!=''"
+				@focus="isGobleSearch=true"
+				@blur="isGobleSearch=false"
+				@custom="searchBrand"
+				@search="searchBrand"
+				@clear="clearSearch"
+				:action-style="{'color': '#fff', 'font-size': '24upx', 'background-color': '#00aaff', 'border-radius': '6upx', 'padding': '12upx 0'}">
+			</u-search>
+		</view>
+		<scroll-view class="picker-content" scroll-y>
+			<view class="picker-main">
+				<view class="picker-main-item" v-for="(item, index) in listData" :key="item.brandSn" @click="chooseItem(index)">
+					<view class="item-name">{{item.brandName}}</view>
+					<u-icon v-show="item.brandSn==brandSn" class="item-icon" name="checkbox-mark" color="#00aaff" size="28"></u-icon>
+				</view>
+				<view v-if="listData && listData.length == 0 && status!='loading'">
+					<u-empty text="没有找到品牌" mode="list" :img-width="150" :margin-top="-60"></u-empty>
+				</view>
+				<view style="padding-bottom: 20upx;">
+					<u-loadmore v-if="totalNum>listData.length || status=='loading'" :status="status" />
+				</view>
+			</view>
+		</scroll-view>
+	</u-popup>
+</template>
+
+<script>
+	import { dealerProductBrandQuery } from '@/config/api.js'
+	export default {
+		props: {
+			openModal: { //  弹框显示状态
+				type: Boolean,
+				default: false
+			},
+			itemSn: {
+				type: String,
+				default: ''
+			}
+		},
+		data() {
+			return {
+				isShow: this.openModal, //  是否打开弹框
+				listData: [],
+				queryWord: '',
+				isGobleSearch: false,
+				brandSn: this.itemSn || null,
+				status: 'loadmore',
+				totalNum: 0
+			}
+		},
+		mounted() {
+			this.clearSearch()
+		},
+		methods: {
+			// 搜索品牌
+			searchBrand(){
+				this.brandSn = null
+				this.getBrandList()
+			},
+			clearSearch(){
+				this.queryWord = ''
+				this.listData = []
+				this.getBrandList()
+			},
+			getBrandList(){
+				this.status = "loading"
+				dealerProductBrandQuery({ brandName: this.queryWord }).then(res => {
+					if(res.status == 200 && res.data){
+						this.listData = res.data
+						this.totalNum = res.data.count || 0
+					}else{
+						this.listData = []
+						this.totalNum = 0
+					}
+					this.status = "loadmore"
+				})
+			},
+			//  选择
+			chooseItem(ind){
+				this.brandSn = this.listData[ind].brandSn
+				this.$emit('choose', this.listData[ind])
+				this.isShow = false
+			},
+			// 清空
+			onClean(){
+				this.queryWord = ''
+				this.brandSn = null
+				this.$emit('clean')
+				this.isShow = false
+				this.getBrandList()
+			}
+		},
+		watch: {
+			//  父页面传过来的弹框状态
+			openModal (newValue, oldValue) {
+				console.log(newValue)
+				this.isShow = newValue
+			},
+			//  重定义的弹框状态
+			isShow (newValue, oldValue) {
+			  if (!newValue) {
+			    this.$emit('close')
+			  }
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.brand-picker{
+		.brand-header{
+			padding: 10upx 30upx;
+			position: relative;
+			.brand-picker-btn{
+				color: rgb(96, 98, 102);
+				margin: 10upx;
+			}
+			.color-blue{
+				display: inline-block;
+				padding: 10upx 20upx;
+				margin: 10upx 0;
+			}
+		}
+		.brand-header:after{
+			content: "";
+			position: absolute;
+			border-bottom: 1px solid #eaeef1;
+			-webkit-transform: scaleY(.5);
+			transform: scaleY(.5);
+			bottom: 0;
+			right: 0;
+			left: 0;
+		}
+		.search-box{
+			padding: 10upx 20upx;
+		}
+		.picker-content{
+			height: 50vh;
+			padding: 6upx 0 20upx;
+			position: relative;
+			.picker-main{
+				width: 100%;
+				.picker-main-item{
+					position: relative;
+					padding: 0 30upx;
+					.item-name{
+						padding: 14upx 0;
+						text-align: center;
+					}
+					.item-icon{
+						position: absolute;
+						right: 100upx;
+						top: 19upx;
+					}
+					&:active{
+						background: #f8f8f8;
+					}
+				}
+			}
+		}
+	}
+</style>

+ 219 - 0
pages/common/productType.vue

@@ -0,0 +1,219 @@
+<template>
+	<u-popup :show="isShow" class="type-picker" @close="isShow=false" mode="bottom">
+		<view class="type-header flex justify_between align_center">
+			<text class="type-picker-btn" @click="onClean">清空</text>
+			<text>产品分类</text>
+			<text class="type-picker-btn color-blue" @click="isShow=false">
+				<u-icon name="close"></u-icon>
+			</text>
+		</view>
+		<view class="choose-info">
+			<view :span="10" :style="{color: '#00aaff',fontSize: '12px'}">
+				已选:
+				<text v-for="(item, index) in infoDataS" :key="index">
+					{{item.productTypeName}} {{index+1 != infoData.length ? '>' : ''}}
+				</text>
+			</view>
+		</view>
+		<scroll-view class="picker-content" scroll-y>
+			<view class="picker-main">
+				<view class="picker-main-item" v-for="(item, index) in typeList" :key="item.productTypeSn" @click="chooseItem(index)">
+					<view class="item-name">{{item.productTypeName}}</view>
+					<u-icon v-show="item.productTypeSn==(infoDataS[nowInd]&&infoDataS[nowInd].productTypeSn)" class="item-icon" name="checkbox-mark" color="#00aaff" size="28"></u-icon>
+				</view>
+				<view v-if="typeList && typeList.length == 0">
+					<u-empty text="数据为空" mode="list" :img-width="200" :margin-top="-60"></u-empty>
+				</view>
+			</view>
+		</scroll-view>
+		<view>
+			<u-row class="choose-info">
+				<u-col :span="4" style="text-align: center;">
+					<u-button v-if="infoDataS.length>0" @click="handlerReset" plain hover-class="none" shape="circle">重选</u-button>
+				</u-col>
+				<u-col :span="4"></u-col>
+				<u-col :span="4" style="text-align: center;">
+					<u-button v-if="infoDataS.length>0" @click="handlerConfirm" type="primary" hover-class="none" shape="circle">确定</u-button>
+				</u-col>
+			</u-row>
+		</view>
+	</u-popup>
+</template>
+
+<script>
+	import { dealerProductTypeList } from '@/config/api.js'
+	export default {
+		props: {
+			openModal: { //  弹框显示状态
+				type: Boolean,
+				default: false
+			},
+			nowData: {
+				type: Object,
+				default: ()=>{
+					return {
+						data: [],
+						indArr: [],
+						nowInd: 0
+					}
+				}
+			}
+		},
+		data() {
+			return {
+				isShow: this.openModal, //  是否打开弹框
+				listData: [],
+				typeList: [],
+				nowInd: 0,
+				indArr: [],
+				infoData: [],
+				infoDataS: []
+			}
+		},
+		mounted() {
+			this.getTypeList()
+		},
+		methods: {
+			getTypeList(){
+				dealerProductTypeList({}).then(res => {
+					if(res.status == 200 && res.data){
+						this.listData = res.data
+						this.typeList = res.data
+					}else{
+						this.listData = []
+						this.typeList = []
+					}
+				})
+			},
+			//  选择
+			chooseItem(ind){
+				if(this.nowInd == 0){
+					this.infoData[this.nowInd] = { productTypeName: this.typeList[ind].productTypeName, productTypeSn: this.typeList[ind].productTypeSn }
+					this.infoData[1] ? delete this.infoData[1] : null
+					this.infoData[2] ? delete this.infoData[2] : null
+					this.indArr[this.nowInd] = ind
+					this.indArr[1] ? delete this.indArr[1] : null
+					this.indArr[2] ? delete this.indArr[2] : null
+					this.nowInd++
+				}else if(this.nowInd == 1){
+					this.infoData[this.nowInd] = { productTypeName: this.typeList[ind].productTypeName, productTypeSn: this.typeList[ind].productTypeSn }
+					this.infoData[2] ? delete this.infoData[2] : null
+					this.indArr[this.nowInd] = ind
+					this.indArr[2] ? delete this.indArr[2] : null
+					this.nowInd++
+				}else if(this.nowInd == 2){
+					this.infoData[this.nowInd] = { productTypeName: this.typeList[ind].productTypeName, productTypeSn: this.typeList[ind].productTypeSn }
+					this.indArr[this.nowInd] = ind
+				}
+				this.infoDataS = JSON.parse(JSON.stringify(this.infoData))
+			},
+			handlerConfirm(){
+				const params = {
+					data: this.infoDataS,
+					indArr: this.indArr,
+					nowInd: this.nowInd
+				}
+				this.$emit('choose', params)
+				this.isShow = false
+			},
+			handlerReset(){
+				this.infoData = []
+				this.infoDataS = []
+				this.indArr = []
+				this.nowInd = 0
+			},
+			// 清空
+			onClean(){
+				this.handlerReset()
+				this.$emit('clean')
+				this.isShow = false
+			}
+		},
+		watch: {
+			//  父页面传过来的弹框状态
+			openModal (newValue, oldValue) {
+				this.isShow = newValue
+			},
+			//  重定义的弹框状态
+			isShow (newValue, oldValue) {
+				if (!newValue) {
+					this.$emit('close')
+				}else{
+					this.infoData = JSON.parse(JSON.stringify(this.nowData.data))
+					this.infoDataS = JSON.parse(JSON.stringify(this.nowData.data))
+					this.indArr = this.nowData.indArr
+					this.nowInd = this.nowData.nowInd
+				}
+			},
+			nowInd (newValue, oldValue) {
+				if (newValue || newValue == 0) {
+					if (newValue == 0) {
+						this.typeList = this.listData || []
+					}else if (newValue == 1) {
+						this.typeList = (this.indArr[0] || this.indArr[0]==0) && this.listData[this.indArr[0]] && this.listData[this.indArr[0]].children ? this.listData[this.indArr[0]].children : []
+					}else if (newValue == 2) {
+						let oList = (this.indArr[0] || this.indArr[0]==0) && this.listData[this.indArr[0]] && this.listData[this.indArr[0]].children ? this.listData[this.indArr[0]].children : []
+						let tList = (this.indArr[1] || this.indArr[1]==0) && oList[this.indArr[1]] && oList[this.indArr[1]].children ? oList[this.indArr[1]].children : []
+						this.typeList = tList
+					}
+				}
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.type-picker{
+		.type-header{
+			padding: 10upx 30upx;
+			position: relative;
+			.brand-picker-btn{
+				color: rgb(96, 98, 102);
+				margin: 10upx;
+			}
+			.color-blue{
+				display: inline-block;
+				padding: 10upx 20upx;
+				margin: 10upx 0;
+			}
+		}
+		.type-header:after{
+			content: "";
+			position: absolute;
+			border-bottom: 1px solid #eaeef1;
+			-webkit-transform: scaleY(.5);
+			transform: scaleY(.5);
+			bottom: 0;
+			right: 0;
+			left: 0;
+		}
+		.choose-info{
+			padding: 14upx 30upx 14upx;
+			color: rgb(96, 98, 102);
+		}
+		.picker-content{
+			height: 50vh;
+			padding: 6upx 0 20upx;
+			position: relative;
+			.picker-main{
+				width: 100%;
+				.picker-main-item{
+					position: relative;
+					padding: 0 30upx;
+					.item-name{
+						padding: 14upx 0;
+						text-align: center;
+					}
+					.item-icon{
+						position: absolute;
+						right: 100upx;
+						top: 19upx;
+					}
+					&:active{
+						background: #f8f8f8;
+					}
+				}
+			}
+		}
+	}
+</style>

+ 6 - 1
pages/index/index.vue

@@ -27,7 +27,7 @@
 				 </view>
 			</view>
 			<view class="grid-box">
-				 <view @click="toEditCode">
+				 <view @click="toStockQuery">
 					 <text class="iconfont icon-kucunchaxun"></text>
 					 <text class="label">库存查询</text>
 				 </view>
@@ -167,6 +167,11 @@
 				uni.navigateTo({
 					url:"/pages/codeBar/codeBar"
 				})
+			},
+			toStockQuery(){
+				uni.navigateTo({
+					url:"/pages/stock/index"
+				})
 			}
 		}
 	}

+ 6 - 0
pages/login/index.vue

@@ -88,6 +88,11 @@
 					uni.hideLoading()
 				})
 			},
+			// 过滤权限code
+			fiterAuthCode(data) {
+				data.permCodes = data.permCodes.filter(item => item.indexOf('_mobile') > 0);
+				return data;
+			},
 			// 登录
 			async formSubmit(e) {
 				const formdata = {
@@ -97,6 +102,7 @@
 				if(formdata.username && formdata.password){
 					const res = await login(formdata)
 					if(res.status == 200){
+						this.$store.state.vuex_userData = this.fiterAuthCode(res.data.auth_user)
 						this.$store.state.vuex_token = res.data.access_token
 						this.$store.state.vuex_user = res.data.auth_user
 						// 记住用户密码

+ 264 - 0
pages/stock/detaiList.vue

@@ -0,0 +1,264 @@
+<template>
+	<view class="st-detailList-wrap">
+		<view class="panel-box">
+			<view class="type-box">
+				<u-subsection @change="sectionChange" mode="subsection" height="60" :list="list" :current="curNow" active-color="#00aaff"></u-subsection>
+			</view>
+			<view class="st-detailList-info">
+				<view class="st-detailList-tit">
+					{{stockInfo&&stockInfo.productName || '--'}}
+				</view>
+				<view class="st-detailList-info-con flex align_center">
+					<view class="pimgs">
+						<u-image :src="stockInfo&&stockInfo.productMsg||`../../static/def_img@2x.png`" width="128" height="128" border-radius="10"></u-image>
+					</view>
+					<view class="flex_1">
+						<view>产品编码:{{stockInfo&&stockInfo.productCode || '--'}}</view>
+						<view class="padding_3">原厂编码:{{stockInfo&&stockInfo.productOrigCode || '--'}}</view>
+						<view class="flex align_center justify_between">
+							<view class="font_13">
+								可用总数量:{{toThousands(stockInfo&&stockInfo.currentStockQty||0)}}{{stockInfo&&stockInfo.productUnit||''}}
+							</view>
+							<view class="font_13" v-if="$hasPermissions('M_showCostPrice_mobile')">
+								可用总成本:
+								¥{{toThousands(stockInfo&&stockInfo.currentStockCost||0, 2)}}
+							</view>
+						</view>
+						<view class="flex align_center justify_between">
+							<view class="font_13">
+								冻结总数量:{{toThousands(stockInfo&&stockInfo.freezeQty||0)}}{{stockInfo&&stockInfo.productUnit||''}}
+							</view>
+							<view class="font_13" v-if="$hasPermissions('M_showCostPrice_mobile')">
+								冻结总成本:
+								¥{{toThousands(stockInfo&&stockInfo.freezeCost||0, 2)}}
+							</view>
+						</view>
+					</view>
+				</view>
+			</view>
+		</view>
+		<!-- 查询结果 -->
+		<scroll-view class="st-detailList-con" scroll-y @scrolltolower="onreachBottom">
+			<view class="st-detailList-main">
+				<!-- 库存详情 -->
+				<view v-if="curNow==0" class="st-detailList-main-item flex align_center" v-for="(item, index) in listData" :key="item.id" @click="getDetail(item)">
+					<view>
+						<view class="flex align_center justify_between margin-b20">
+							<view :style="{color: '#00aaff'}">{{item.putTime || '--'}}</view>
+							<u-tag v-if="item.putBizTypeDictValue" :text="item.putBizTypeDictValue" border-color="#55aaff" shape="circle" size="mini" />
+						</view>
+						<view class="flex align_center justify_between">
+							<view v-if="item.warehouseName || item.warehouseLocationName">{{item.warehouseName}} > {{item.warehouseLocationName}}</view>
+							<view v-else>--</view>
+							<view>¥{{toThousands(item.putCost||0, 2)}} * {{toThousands(item.currentQty||0)}}{{item.unit}}</view>
+						</view>
+					</view>
+					<view>
+						<u-icon name="arrow-right" color="#969da3" size="28"></u-icon>
+					</view>
+				</view>
+				<!-- 出入库明细 -->
+				<view v-if="curNow==1" class="st-detailList-main-item flex align_center" v-for="(item, index) in listData" :key="item.id" @click="getDetail(item)">
+					<view>
+						<view class="flex align_center justify_between margin-b20">
+							<view :style="{color: '#00aaff'}">{{item.auditTime || '--'}}</view>
+							<u-tag v-if="item.bizTypeDictValue" :text="item.bizTypeDictValue" border-color="#55aaff" shape="circle" size="mini" />
+						</view>
+						<view class="flex align_center justify_between">
+							<view>
+							<text v-if="item.unitType&&item.unitType=='CUSTOMER'">{{ item.unitNameCurrent||'--' }}</text>
+							<text v-else>{{ item.unitName||'--' }}</text>
+							</view>
+							<view>
+								<view :style="{color: item.qty && item.qty!=0 ? (item.flowType=='PUT' ? 'green':'red'):'', display:'inline-block'}"><text v-if="item.qty && item.qty!=0">{{ item.flowType=='PUT' ? '+':'-' }}</text>{{ toThousands(item.qty||0) }}</view>
+								{{item.product && item.product.unit}}
+							</view>
+						</view>
+					</view>
+					<view>
+						<u-icon name="arrow-right" color="#969da3" size="24"></u-icon>
+					</view>
+				</view>
+				<view v-if="listData && listData.length == 0">
+					<u-empty :text="noDataText" mode="list" :margin-top="50"></u-empty>
+				</view>
+			</view>
+		</scroll-view>
+	</view>
+</template>
+
+<script>
+	import { toThousands } from '@/libs/tools'
+	import { stockDetailList, stockFlowList } from '@/config/api.js'
+	export default{
+		data(){
+			return {
+				list: [
+					{ name: '库存详情' },
+					{ name: '出入库明细' }
+				],
+				totalData: null,
+				totalNum: 0,
+				listData: [],
+				pageNo: 1,
+				pageSize: 10,
+				stockInfo: null,
+				noDataText: '暂无数据',
+				curNow: 0,
+				theme: '',
+				toThousands
+			}
+		},
+		onLoad(option) {
+			this.theme = getApp().globalData.theme
+			this.stockInfo = option.data ? JSON.parse(option.data) : null
+			this.getList()
+		},
+		methods: {
+			// 列表
+			getList(pageNo){
+				const _this = this
+				if (pageNo) {
+					this.pageNo = pageNo
+				}
+				let params = {}
+				let url = stockDetailList
+				if(this.curNow == 0){  // 库存详情
+					url = stockDetailList
+					params = {
+						pageNo: this.pageNo,
+						pageSize: this.pageSize,
+						stockSn: this.stockInfo && this.stockInfo.stockSn || '',
+						isSellOut: '0'
+					}
+				}else if(this.curNow == 1){  // 出入库明细
+					url = stockFlowList
+					params = {
+						pageNo: this.pageNo,
+						pageSize: this.pageSize,
+						productSn: this.stockInfo && this.stockInfo.productSn || ''
+					}
+				}
+				url(params).then(res => {
+					if (res.status == 200) {
+						if(res.data && res.data.list){
+							if(this.curNow == 1){  // 出入库明细
+								res.data.list.map(item => {
+									if(item.flowType == 'PUT'){
+										item.qty = item.putQty || 0
+									}else if(item.flowType == 'OUT'){
+										item.qty = item.outQty || 0
+									}
+								})
+							}
+							if(this.pageNo>1){
+								this.listData = this.listData.concat(res.data && res.data.list || [])
+							}else{
+								this.listData = res.data.list || []
+							}
+						}else{
+							this.listData = []
+						}
+						this.totalNum = res.data && res.data.count || 0
+					} else {
+						this.listData = []
+						this.totalNum = 0
+						this.noDataText = res.message
+					}
+				})
+			},
+			// scroll-view到底部加载更多
+			onreachBottom() {
+				if(this.listData.length < this.totalNum){
+					this.pageNo += 1
+					this.getList()
+				}
+			},
+			// 详情
+			getDetail(data){
+				if(this.curNow == 0){  // 库存详情
+					uni.navigateTo({ url: "/pages/stock/stockDetail?productData="+JSON.stringify(this.stockInfo)+"&data="+JSON.stringify(data) })
+				}else if(this.curNow == 1){  // 出入库明细
+					uni.navigateTo({ url: "/pages/stock/warehouseDetail?productData="+JSON.stringify(this.stockInfo)+"&data="+JSON.stringify(data) })
+				}
+			},
+			sectionChange(index) {
+				this.curNow = index
+				this.getList(1)
+			}
+		}
+	}
+</script>
+
+<style lang="less">
+	.st-detailList-wrap{
+		width: 100%;
+		height: 100vh;
+		.panel-box {
+			background-color: #ffffff;
+			box-shadow: 3upx 2upx 6upx #eee;
+			margin-bottom: 20upx;
+			.type-box{
+				margin: 0 auto 20upx;
+				width: 65%;
+			}
+			.st-detailList-info{
+				background: #ffffff;
+				padding: 20upx;
+				margin-bottom: 25upx;
+				border-radius: 25upx;
+				box-shadow: 1px 1px 3px #EEEEEE;
+				.st-detailList-tit{
+					padding-bottom: 20upx;
+					border-bottom: 1px solid #e4e7ed;
+					font-weight: bold;
+					.u-line{
+						display: inline-block;
+						width: 6upx;
+						height: 40upx;
+						background-color: red;
+						vertical-align: bottom;
+						margin: 0 20upx 0 10upx;
+					}
+				}
+				.st-detailList-info-con{
+					padding: 20upx 20upx 6upx;
+					font-size: 26upx;
+					.pimgs{
+						margin-right: 16upx;
+					}
+					.font_13{
+						font-size: 26upx;
+					}
+					.padding_3{
+						padding: 6upx 0;
+					}
+				}
+			}
+		}
+		.st-detailList-con{
+			height: calc(100vh - 370upx);
+			.st-detailList-main{
+				.st-detailList-main-item{
+					background-color: #fff;
+					margin: 0 20upx 10upx;
+					border-radius: 15upx;
+					box-shadow: 1px 1px 3px #EEEEEE;
+					padding: 20upx 20upx;
+					.margin-b20{
+						margin-bottom: 20upx;
+					}
+					> view{
+						&:first-child{
+							flex-grow: 1;
+						}
+						&:last-child{
+							padding: 0 0 0 20upx;
+						}
+					}
+				}
+			}
+			
+		}
+	}
+</style>

+ 247 - 0
pages/stock/index.vue

@@ -0,0 +1,247 @@
+<template>
+	<view class="stock-wrap">
+		<!-- 统计 -->
+		<view class="panel-box" v-if="totalData&&$hasPermissions('M_stockTj_mobile')">
+			<view class="item-box">
+				<view>库存总数量(个)</view>
+				<u-count-to :end-val="totalData&&totalData.totalCurrentStockQty ? totalData.totalCurrentStockQty : 0" separator=","></u-count-to>
+			</view>
+			<view class="item-box" v-if="$hasPermissions('M_showCostPrice_mobile')">
+				<view>库存总成本(¥)</view>
+				<u-count-to :end-val="totalData&&totalData.totalCurrentStockCost ? totalData.totalCurrentStockCost : 0" separator="," :decimals="2"></u-count-to>
+			</view>
+		</view>
+		<!-- 库存查询 -->
+		<view class="stock-con" v-if="$hasPermissions('M_stockQuery_mobile')">
+			<view class="stock-tit flex align_center">
+				<view class="u-line" :style="{background: '#335fb6'}"></view>
+				库存查询
+			</view>
+			<u-form :model="form" ref="uForm" label-width="100">
+				<u-form-item label="产品编码"><u-input v-model.trim="form.productCode" border="bottom" placeholder="请输入产品编码" input-align="right" /></u-form-item>
+				<u-form-item label="原厂编码"><u-input v-model.trim="form.productOrigCode" border="bottom" placeholder="请输入原厂编码" input-align="right" /></u-form-item>
+				<u-form-item label="产品名称"><u-input v-model.trim="form.productName" border="bottom" placeholder="请输入产品名称" input-align="right" /></u-form-item>
+				<u-form-item label="产品品牌" @click="brandModal=true"><u-input v-model="brandName" border="bottom" readonly placeholder="请选择产品品牌" input-align="right" /></u-form-item>
+				<u-form-item label="产品分类" @click="showTypeModal=true"><u-input v-model="productTypeNameArr" border="bottom" readonly placeholder="请选择产品分类" input-align="right" /></u-form-item>
+				<u-form-item label="滞销天数">
+					<u-input type="number" :min="0" :max="999999" border="bottom" v-model.trim="form.minUnsalableDays" placeholder="起始天数" input-align="left" />
+					<text style="display: block;text-align: center;line-height: 32px;">至</text>
+					<u-input type="number" :min="0" :max="999999" border="bottom" v-model.trim="form.maxUnsalableDays" placeholder="截止天数" input-align="right" />
+				</u-form-item>
+				<u-form-item label="只查看有库存"><u-switch slot="right" v-model="form.zeroQtyFlag"></u-switch></u-form-item>
+				<u-form-item label="包括禁用产品"><u-switch slot="right" v-model="form.enableFlag"></u-switch></u-form-item>
+			</u-form>
+			<view class="form-footer-btn">
+				<u-button size="medium" shape="circle" hover-class="none" @click="handleClean">清空</u-button>
+				<u-button size="medium" hover-class="none" shape="circle" :custom-style="customBtnStyle" @click="handleSearch">查询</u-button>
+			</view>
+		</view>
+		<!-- 产品品牌 -->
+		<productBrand ref="productBrand" :openModal="brandModal" :itemSn="form.productBrandSn" @choose="brandChoose" @clean="brandClean" @close="brandModal=false" />
+		<!-- 产品分类 -->
+		<productType ref="productType" :openModal="showTypeModal" :nowData="nowData" @choose="typeChoose" @clean="typeClean" @close="showTypeModal=false" />
+	</view>
+</template>
+
+<script>
+	import productBrand from '@/pages/common/productBrand.vue'
+	import productType from '@/pages/common/productType.vue'
+	import { dealerProductTypeList , stockCount } from '@/config/api'
+	export default{
+		components: { productBrand, productType },
+		data(){
+			return {
+				form: {
+					productCode: '', //  产品编码
+					productName: '', //  产品名称
+					productOrigCode: '', //  原厂编码
+					productBrandSn: undefined, //  产品品牌
+					productTypeSn1: undefined, //  产品分类1
+					productTypeSn2: undefined, //  产品分类2
+					productTypeSn3: undefined, //  产品分类3
+					zeroQtyFlag: false ,//  库存情况
+					enableFlag: true, // 显示禁用产品
+					minUnsalableDays: undefined, // 滞销天数最小值
+					maxUnsalableDays: undefined // 滞销天数最大值
+				},
+				totalData: null,
+				brandModal: false,
+				brandName: '',
+				showTypeModal: false,
+				nowData: {
+					data: [],
+					indArr: [],
+					nowInd: 0
+				},
+				customBtnStyle: {  //  自定义按钮样式
+					backgroundColor: '#335fb6',
+					color: '#fff'
+				},
+			}
+		},
+		computed: {
+			productTypeNameArr: function() {
+				let str = ''
+				this.nowData.data.map(item => {
+					str += item.productTypeName+' / '
+				})
+				return str ? str.substr(0, str.length-3) : ''
+			}
+		},
+		watch: {
+		  'form.enableFlag' (newValue, oldValue) {
+		    uni.setStorageSync('productEnabledFlag-' + this.$store.state.vuex_userData.orgId, newValue)
+		  },
+		},
+		onShow() {
+			// 是否默认包括禁用产品
+			const a = uni.getStorageSync('productEnabledFlag-' + this.$store.state.vuex_userData.orgId)
+			console.log(typeof a == 'boolean')
+			this.form.enableFlag = typeof a == 'boolean' ? a : true
+			this.getTotal()
+		},
+		methods: {
+			$message(msg){
+				uni.showToast({
+					icon: 'none',
+					title: msg
+				})
+			},
+			// 校验滞销天数数值范围
+			checkValueRange () {
+			  const a =	!this.form.minUnsalableDays
+			  const b = !this.form.maxUnsalableDays
+			  
+			  if(a && !b){
+				  this.$message('请输入滞销天数的起始天数!')
+				  return false
+			  } 
+			  if(b && !a){
+				  this.$message('请输入滞销天数的截止天数!')
+				  return false
+			  }
+			  console.log(this.form.minUnsalableDays , this.form.maxUnsalableDays,this.form.minUnsalableDays > this.form.maxUnsalableDays)
+			  if (Number(this.form.minUnsalableDays) > Number(this.form.maxUnsalableDays)) {
+			    this.$message('截止天数应大于起始天数!')
+			    return false
+			  }
+			  this.form.minUnsalableDays = this.form.minUnsalableDays > 999999 ? 999999 : this.form.minUnsalableDays
+			  this.form.maxUnsalableDays = this.form.maxUnsalableDays > 999999 ? 999999 : this.form.maxUnsalableDays
+			  return true
+			},
+			// 合计
+			getTotal () {
+				stockCount({}).then(res => {
+					if (res.status == 200 && res.data) {
+						this.totalData = res.data
+					} else {
+						this.totalData = null
+					}
+				})
+			},
+			// 表单清空
+			handleClean(){
+				this.form.productCode = ''
+				this.form.productName = ''
+				this.form.productOrigCode = ''
+				this.brandName = ''
+				this.form.productBrandSn = undefined
+				this.nowData = {
+					data: [],
+					indArr: [],
+					nowInd: 0
+				}
+				this.form.productTypeSn1 = undefined
+				this.form.productTypeSn2 = undefined
+				this.form.productTypeSn3 = undefined
+				this.form.zeroQtyFlag = false
+				this.form.minUnsalableDays = undefined // 滞销天数最小值
+				this.form.maxUnsalableDays = undefined // 滞销天数最大值
+				this.$refs.productBrand.onClean()
+			},
+			// 查询
+			handleSearch(){
+				if (this.checkValueRange()) {
+					let params = JSON.parse(JSON.stringify(this.form))
+					params.zeroQtyFlag = params.zeroQtyFlag ? '0' : ''
+					params.enableFlag = params.enableFlag ? '' : '1'
+					uni.navigateTo({ url: "/pages/stock/stockSearch?data="+JSON.stringify(params) })
+				}
+			},
+			// 选择品牌
+			brandChoose(data){
+				this.brandName = data.brandName
+				this.form.productBrandSn = data.brandSn
+			},
+			// 清空品牌
+			brandClean(){
+				this.brandName = ''
+				this.form.productBrandSn = undefined
+			},
+			// 选择分类
+			typeChoose(obj){
+				this.nowData = {
+					data: obj.data,
+					indArr: obj.indArr,
+					nowInd: obj.nowInd
+				}
+				this.form.productTypeSn1 = this.nowData.data[0]&&this.nowData.data[0].productTypeSn ? this.nowData.data[0].productTypeSn : undefined
+				this.form.productTypeSn2 = this.nowData.data[1]&&this.nowData.data[1].productTypeSn ? this.nowData.data[1].productTypeSn : undefined
+				this.form.productTypeSn3 = this.nowData.data[2]&&this.nowData.data[2].productTypeSn ? this.nowData.data[2].productTypeSn : undefined
+			},
+			// 清空分类
+			typeClean(){
+				this.nowData = {
+					data: [],
+					indArr: [],
+					nowInd: 0
+				}
+				this.form.productTypeSn1 = undefined
+				this.form.productTypeSn2 = undefined
+				this.form.productTypeSn3 = undefined
+			}
+		}
+	}
+</script>
+
+<style lang="scss">
+	.stock-wrap{
+		padding: 0 30upx;
+		.panel-box {
+			background-color: #ffffff;
+			padding: 20upx 20upx;
+			.item-box{
+				text-align: center;
+				display: flex;
+				align-items: center;
+				justify-content: space-between;
+				font-size: 14px;
+			}
+		}
+		.stock-con{
+			background-color: #ffffff;
+			padding: 0 20upx;
+			.stock-tit{
+				font-weight: bold;
+				.u-line{
+					display: inline-block;
+					width: 6upx;
+					height: 30upx;
+					vertical-align: bottom;
+					margin: 0 10upx;
+					border-radius: 5upx;
+				}
+			}
+			.form-footer-btn{
+				margin: 60upx 30upx 30upx;
+				display: flex;
+				justify-content: space-between;
+			}
+			.form-footer-btn uni-button{
+				width: 40%;
+				margin: 0 auto;
+				font-size: 30upx;
+			}
+		}
+	}
+</style>

+ 99 - 0
pages/stock/stockDetail.vue

@@ -0,0 +1,99 @@
+<template>
+	<view class="stockDetail-wrap">
+		<view class="panel-box flex align_center justify_between">
+			<view class="pimgs">
+				<u-image :src="productData && productData.productMsg||`../../static/def_img@2x.png`" width="120" height="120" border-radius="10"></u-image>
+			</view>
+			<view class="info-con flex_1">
+				<view class="padding_7">{{productData && productData.productName || '--'}}</view>
+				<view>{{productData && productData.productCode || '--'}}</view>
+			</view>
+		</view>
+		<view class="stockDetail-info">
+			<view class="info-item flex align_center justify_between">
+				<view class="info-name">仓库</view>
+				<view>{{paramsData && paramsData.warehouseName || '--'}}</view>
+			</view>
+			<view class="info-item flex align_center justify_between">
+				<view class="info-name">仓位</view>
+				<view>{{paramsData && paramsData.warehouseLocationName || '--'}}</view>
+			</view>
+			<view class="info-item flex align_center justify_between">
+				<view class="info-name">入库时间</view>
+				<view>{{paramsData && paramsData.putTime || '--'}}</view>
+			</view>
+			<view class="info-item flex align_center justify_between">
+				<view class="info-name">入库类型</view>
+				<view>{{paramsData && paramsData.putBizTypeDictValue || '--'}}</view>
+			</view>
+			<view class="info-item flex align_center justify_between">
+				<view class="info-name">可用库存数量</view>
+				<view>{{toThousands(paramsData && paramsData.currentQty || 0)}}</view>
+			</view>
+			<view class="info-item flex align_center justify_between">
+				<view class="info-name">冻结库存数量</view>
+				<view>{{toThousands(paramsData && paramsData.freezeQty || 0)}}</view>
+			</view>
+			<view class="info-item flex align_center justify_between" v-if="$hasPermissions('M_showCostPrice_mobile')">
+				<view class="info-name">成本单价</view>
+				<view>¥{{toThousands(paramsData && paramsData.putCost || 0, 2)}}</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import { toThousands } from '@/libs/tools'
+	export default{
+		data(){
+			return {
+				productData: null,
+				paramsData: null,
+				theme: '',
+				toThousands
+			}
+		},
+		onLoad(option) {
+			this.theme = getApp().globalData.theme
+			this.productData = option.productData ? JSON.parse(option.productData) : null
+			this.paramsData = option.data ? JSON.parse(option.data) : null
+		},
+		methods: {}
+	}
+</script>
+
+<style lang="scss">
+	.stockDetail-wrap{
+		padding: 30upx;
+		.panel-box {
+			background-color: #ffffff;
+			padding: 20upx 20upx;
+			border-radius: 20upx;
+			box-shadow: 3upx 2upx 6upx #eee;
+			margin-bottom: 30upx;
+			display: flex;
+			justify-content: space-between;
+			align-items: center;
+			.info-con{
+				margin-left: 20upx;
+				.padding_7{
+					padding: 14upx 0 0;
+					font-size: 28upx;
+				}
+			}
+		}
+		.stockDetail-info{
+			background-color: #ffffff;
+			padding: 10upx 20upx 20upx;
+			border-radius: 20upx;
+			box-shadow: 3upx 2upx 6upx #eee;
+			.info-item{
+				border-bottom: 1px dashed #e4e7ed;
+				padding: 20upx 0;
+				.info-name{
+					color: #464646;
+				}
+			}
+		}
+	}
+</style>

+ 264 - 0
pages/stock/stockSearch.vue

@@ -0,0 +1,264 @@
+<template>
+	<view class="stockSearch-wrap">
+		<!-- 统计 -->
+		<view class="panel-box" v-if="totalNum">
+			<view class="item-box">
+				<view>查询结果</view>
+				<view>{{toThousands(totalNum||0)}}条</view>
+			</view>
+			<view class="item-box">
+				<view>可用库存数量</view>
+				<view>{{toThousands(totalData&&totalData.totalCurrentStockQty||0)}}个</view>
+			</view>
+			<view class="item-box" v-if="$hasPermissions('M_showCostPrice_mobile')">
+				<view>可用库存成本</view>
+				<view>¥{{toThousands(totalData&&totalData.totalCurrentStockCost||0, 2)}}</view>
+			</view>
+		</view>
+		<!-- 查询结果 -->
+		<scroll-view class="stockSearch-con" scroll-y @scrolltolower="onreachBottom">
+			<view class="stockSearch-main">
+				<view class="stockSearch-main-item" v-for="(item, index) in listData" :key="item.id" @click.stop="getDetail(item)">
+					<view class="stockSearch-tit flex">
+						<view class="u-line" :style="{backgroundColor: '#00aaff'}"></view>
+						<view class="u-name">
+							{{item.productName}}
+						</view>
+						<view class="u-last">
+							<copyText :text="item.productName" label="产品名称"></copyText>
+						</view>
+					</view>
+					<view class="stockSearch-item-con flex align_center">
+						<view class="pimgs">
+							<u-image :src="item.productMsg?item.productMsg:`../../static/def_img@2x.png`" width="128" height="128" border-radius="10"></u-image>
+						</view>
+						<view class="flex_1">
+							<view>产品编码:{{item.productCode || '--'}} <copyText :text="item.productCode" label="产品编码"></copyText></view>
+							<view class="padding_3">原厂编码:{{item.productOrigCode && item.productOrigCode !== ' '? item.productOrigCode : '--'}}</view>
+						</view>
+					</view>
+					<view class="stockSearch-item-footer flex align_center">
+						<view class="font_13">
+							可用库存数量
+							<view>{{toThousands(item.currentStockQty||0)}}{{item.productUnit}}</view>
+						</view>
+						<view class="font_13" v-if="$hasPermissions('M_showCostPrice_mobile')">
+							可用库存成本
+							<view>¥{{toThousands(item.currentStockCost||0, 2)}}</view>
+						</view>
+						<view class="font_13">
+							滞销天数
+							<view>
+								<span v-if="item.unsalableDays>=180&&item.unsalableDays<=360" style="color:#FF9900">{{ num(item.unsalableDays) }}</span>
+								<span v-else-if="item.unsalableDays>360" style="color:#FF0000">{{ num(item.unsalableDays) }}</span>
+								<span v-else >{{ num(item.unsalableDays) }}</span>
+							</view>
+						</view>
+					</view>
+				</view>
+				<view v-if="listData&&listData.length==0">
+					<u-empty v-if="status!='loading'" :text="noDataText" mode="list" :margin-top="50"></u-empty>
+				</view>
+				<view style="padding: 20upx;" v-if="totalNum>pageSize || status=='loading'">
+					<u-loadmore :status="status" />
+				</view>
+			</view>
+		</scroll-view>
+	</view>
+</template>
+
+<script>
+	import { toThousands } from '@/libs/tools'
+	import { stockList, stockCount } from '@/config/api.js'
+	import copyText from '@/pages/common/copyText.vue'
+	export default{
+		components:{copyText},
+		data(){
+			return {
+				totalData: null,
+				totalNum: 0,
+				status: 'loading',
+				listData: [],
+				pageNo: 1,
+				pageSize: 50,
+				params: {},
+				noDataText: '暂无数据',
+				theme: '',
+				toThousands
+			}
+		},
+		onLoad(option) {
+			this.theme = getApp().globalData.theme
+			this.params = option.data ? JSON.parse(option.data) : {}
+			this.getList()
+		},
+		methods: {
+			// 格式化滞销天数为几个月零几天
+			num (val) {
+			  let days
+			  if (val != null && val != undefined) {
+			    if (val >= 30) {
+			      const a = Math.floor(val / 30)
+			      const b = val - a * 30
+			      if (b > 0) {
+			        days = a + '个月' + b + '天'
+			      } else {
+			        days = a + '个月'
+			      }
+			    } else {
+			      days = val + '天'
+			    }
+			  } else {
+			    days = '--'
+			  }
+			  return days
+			},
+			// 列表
+			getList(pageNo){
+				const _this = this
+				if (pageNo) {
+					this.pageNo = pageNo
+				}
+				this.status = 'loading'
+				let params = Object.assign(this.params, { pageNo: this.pageNo, pageSize: this.pageSize })
+				stockList(params).then(res => {
+					this.getTotal(params)
+					if(res.status == 200){
+						let list = res.data.list
+						if (list && list.length){
+							// 分页 拼接数据
+							if (_this.pageNo != 1) {
+								_this.listData = _this.listData ? _this.listData.concat(list) : list
+							} else {
+								_this.listData = list
+							}
+							this.totalNum = res.data.count
+							if (_this.listData.length == res.data.count) {
+								_this.status = 'nomore'
+							} else {
+								_this.status = 'loadmore'
+							}
+						} else {
+							_this.listData = list || []
+							this.totalNum = 0
+							_this.status = 'nomore'
+						}
+						_this.noDataText = '暂无数据'
+					}else{
+						_this.status = 'loadmore'
+						_this.listData = []
+						_this.totalNum = 0
+						_this.noDataText = res.message ? res.message : '网络似乎出错了,请稍后再试'
+					}
+				})
+			},
+			// 合计
+			getTotal (params) {
+				stockCount(params).then(res => {
+					if (res.status == 200 && res.data) {
+						this.totalData = res.data
+					} else {
+						this.totalData = null
+					}
+				})
+			},
+			// scroll-view到底部加载更多
+			onreachBottom() {
+				if(this.listData.length < this.totalNum){
+					this.pageNo += 1
+					this.getList()
+				}else{
+					this.status = "nomore"
+				}
+			},
+			// 详情
+			getDetail(data){
+				uni.navigateTo({ url: "/pages/stock/detaiList?data="+JSON.stringify(data) })
+			}
+		}
+	}
+</script>
+
+<style lang="less">
+	.stockSearch-wrap{
+		width: 100%;
+		height: 100vh;
+		.panel-box {
+			background-color: #ffffff;
+			padding: 20upx 0;
+			.item-box{
+				padding: 10upx 30upx;
+				display: flex;
+				align-items: center;
+				justify-content: space-between;
+			}
+			.item-box:last-child{
+				border-bottom: none;
+			}
+		}
+		.stockSearch-con{
+			height: calc(100vh - 138upx);
+			.stockSearch-main{
+				.stockSearch-main-item{
+					background: #ffffff;
+					padding: 20upx;
+					margin: 25upx;
+					border-radius: 20upx;
+					border: 1px solid #e4e7ed;
+					&:first-child{
+						margin-top: 0;
+					}
+					.stockSearch-tit{
+						font-size: 14px;
+						padding-bottom: 20upx;
+						font-weight: 900;
+						// white-space: nowrap;
+						// overflow: hidden;
+						// text-overflow: ellipsis;
+						.u-line{
+							display: inline-block;
+							width: 6upx;
+							height: 30upx;
+							margin: 6upx 10upx 0 10upx;
+							border-radius: 5upx;
+						}
+						.u-name{
+							flex-grow: 1;
+							width: 80%;
+						}
+						.u-last{
+							width: 100upx;
+							text-align: center;
+						}
+					}
+					.stockSearch-item-con{
+						padding: 20upx;
+						font-size: 28upx;
+						.pimgs{
+							margin-right: 16upx;
+						}
+						.padding_3{
+							padding: 6upx 0;
+							word-break: break-all;
+						}
+					}
+					.stockSearch-item-footer{
+						border-top: 2upx solid #eee;
+						.font_13{
+							width: 33%;
+							font-size: 26upx;
+							text-align: center;
+							border-left: 2upx solid #eee;
+							padding-top: 20upx;
+							&:first-child{
+								border: 0;
+							}
+						}
+					}
+				}
+			}
+			
+			
+		}
+	}
+</style>

+ 133 - 0
pages/stock/warehouseDetail.vue

@@ -0,0 +1,133 @@
+<template>
+	<view class="warehouseDetail-wrap">
+		<view class="panel-box flex align_center justify_between">
+			<view class="pimgs">
+				<u-image :src="productData && productData.productMsg||`../../static/def_img@2x.png`" width="128" height="128" border-radius="10"></u-image>
+			</view>
+			<view class="info-con flex_1">
+				<view>{{productData && productData.productCode || '--'}}</view>
+				<view class="padding_7">{{productData && productData.productName || '--'}}</view>
+			</view>
+		</view>
+		<view class="warehouseDetail-info">
+			<view class="info-item flex align_center justify_between">
+				<view class="info-name">变动类型</view>
+				<view v-if="paramsData && paramsData.flowType">{{paramsData.flowType=='OUT'?'出库':paramsData.flowType=='PUT'?'入库':'--'}}</view>
+				<view v-else>--</view>
+			</view>
+			<view class="info-item flex align_center justify_between">
+				<view class="info-name">变动数量</view>
+				<view v-if="paramsData && paramsData.flowType && paramsData.flowType=='PUT'">
+					<view style="color: green;display: inline-block">
+						<text v-if="paramsData && paramsData.putQty && paramsData.putQty!=0">+</text>{{ toThousands(paramsData && paramsData.putQty||0) }}
+					</view>
+					{{paramsData && paramsData.product && paramsData.product.unit}}
+				</view>
+				<view v-if="paramsData && paramsData.flowType && paramsData.flowType=='OUT'">
+					<view style="color: red;display: inline-block">
+						<text v-if="paramsData && paramsData.outQty && paramsData.outQty!=0">-</text>{{ toThousands(paramsData && paramsData.outQty||0) }}
+					</view>
+					{{paramsData && paramsData.product && paramsData.product.unit}}
+				</view>
+			</view>
+			<view class="info-item flex align_center justify_between">
+				<view class="info-name">单据类型</view>
+				<view>{{paramsData && paramsData.bizTypeDictValue || '--'}}</view>
+			</view>
+			<view class="info-item flex align_center justify_between">
+				<view class="info-name">单据审核时间</view>
+				<view>{{paramsData && paramsData.auditTime || '--'}}</view>
+			</view>
+			<view class="info-item flex align_center justify_between">
+				<view class="info-name">单位名称</view>
+				<view v-if="paramsData">
+					<text v-if="paramsData.unitType&&paramsData.unitType=='CUSTOMER'">{{ paramsData.unitNameCurrent||'--' }}</text>
+					<text v-else>{{ paramsData.unitName||'--' }}</text>
+				</view>
+			</view>
+			<view class="info-item flex align_center justify_between">
+				<view class="info-name">批次号</view>
+				<view>{{paramsData && paramsData.stockBatchNo || '--'}}</view>
+			</view>
+			<view class="info-item flex align_center justify_between">
+				<view class="info-name">关联单据号</view>
+				<view>{{paramsData && paramsData.bizNo || '--'}}</view>
+			</view>
+			<view class="info-item flex align_center justify_between">
+				<view class="info-name">仓库</view>
+				<view>{{paramsData && paramsData.warehouseName || '--'}}</view>
+			</view>
+			<view class="info-item flex align_center justify_between">
+				<view class="info-name">仓位</view>
+				<view>{{paramsData && paramsData.warehouseLocationName || '--'}}</view>
+			</view>
+			<view class="info-item flex align_center justify_between" v-if="$hasPermissions('M_showCostPrice_mobile')">
+				<view class="info-name">总成本</view>
+				<view>¥{{toThousands(paramsData && paramsData.totalCost || 0, 2)}}</view>
+			</view>
+			<view class="info-item flex align_center justify_between">
+				<view class="info-name">总售价</view>
+				<view>¥{{toThousands(paramsData && paramsData.totalPrice || 0, 2)}}</view>
+			</view>
+			<view class="info-item flex align_center justify_between">
+				<view class="info-name">出入库状态</view>
+				<view>{{paramsData && paramsData.stateDictValue || '--'}}</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import { toThousands } from '@/libs/tools'
+	export default{
+		data(){
+			return {
+				productData: null,
+				paramsData: null,
+				theme: '',
+				toThousands
+			}
+		},
+		onLoad(option) {
+			this.theme = getApp().globalData.theme
+			this.productData = option.productData ? JSON.parse(option.productData) : null
+			this.paramsData = option.data ? JSON.parse(option.data) : null
+		},
+		methods: {}
+	}
+</script>
+
+<style lang="scss">
+	.warehouseDetail-wrap{
+		padding: 30upx;
+		.panel-box {
+			background-color: #ffffff;
+			padding: 20upx 20upx;
+			border-radius: 20upx;
+			box-shadow: 3upx 2upx 6upx #eee;
+			margin-bottom: 30upx;
+			display: flex;
+			justify-content: space-between;
+			align-items: center;
+			.info-con{
+				margin-left: 20upx;
+				.padding_7{
+					padding: 14upx 0 0;
+				}
+			}
+		}
+		.warehouseDetail-info{
+			background-color: #ffffff;
+			padding: 10upx 20upx 20upx;
+			border-radius: 20upx;
+			box-shadow: 3upx 2upx 6upx #eee;
+			.info-item{
+				border-bottom: 1px dashed #e4e7ed;
+				padding: 20upx 0;
+				.info-name{
+					color: #464646;
+				}
+			}
+		}
+	}
+</style>

BIN
static/def_img@2x.png


BIN
static/def_no_data@3x.png


+ 2 - 1
store/index.js

@@ -12,7 +12,7 @@ try{
 }
 
 // 需要永久存储,且下次APP启动需要取出的,在state中的变量名
-let saveStateKeys = ['vuex_user', 'vuex_token', 'vuex_deviceId', 'vuex_userAccount'];
+let saveStateKeys = ['vuex_user', 'vuex_token', 'vuex_deviceId', 'vuex_userAccount','vuex_userData'];
 
 // 保存变量到本地存储中
 const saveLifeData = function(key, value){
@@ -34,6 +34,7 @@ const store = new Vuex.Store({
 		// 加上vuex_前缀,是防止变量名冲突,也让人一目了然
 		vuex_user: lifeData.vuex_user ? lifeData.vuex_user : {name: '默认'},
 		vuex_token: lifeData.vuex_token ? lifeData.vuex_token : '',
+		vuex_userData: lifeData.vuex_userData ? lifeData.vuex_userData : '',
 		vuex_versionInfo: lifeData.vuex_versionInfo ? lifeData.vuex_versionInfo : {}, // 版本信息
 		// 如果vuex_version无需保存到本地永久存储,无需lifeData.vuex_version方式
 		vuex_supplierList:[], // 供应商列表

File diff suppressed because it is too large
+ 0 - 1
unpackage/dist/dev/app-plus/app-config-service.js


File diff suppressed because it is too large
+ 0 - 0
unpackage/dist/dev/app-plus/app-service.js


File diff suppressed because it is too large
+ 31 - 17
unpackage/dist/dev/app-plus/app-view.js


BIN
unpackage/dist/dev/app-plus/static/def_img@2x.png


BIN
unpackage/dist/dev/app-plus/static/def_no_data@3x.png


Some files were not shown because too many files changed in this diff