Przeglądaj źródła

库存预警页面

zhengxin 5 lat temu
rodzic
commit
9f63bd71b6

+ 1 - 1
ghs/src/admin/warehouse/allot/allotEx.vue

@@ -23,7 +23,7 @@
 					<view
 						:class="[
 							'iconfont',
-							item.status == '03' ? 'iconcaozuojihuo' : 'iconxiajia'
+							item.status == '03' ? 'iconcaozuojihuo' : 'icondaichuku'
 						]"
 					></view>
 					<view :class="['status-name']">{{ item.statusName }}</view>

+ 1 - 1
ghs/src/admin/warehouse/allot/allotPut.vue

@@ -23,7 +23,7 @@
 					<view
 						:class="[
 							'iconfont',
-							item.status == '03' ? 'iconcaozuojihuo' : 'iconxiajia'
+							item.status == '03' ? 'iconcaozuojihuo' : 'icondairuku'
 						]"
 					></view>
 					<view :class="['status-name']">{{ item.statusName }}</view>

+ 15 - 9
ghs/src/admin/warehouse/components/Commodity.vue

@@ -13,15 +13,17 @@
 			<view class="kc">B级</view>
 			<view class="num-open_bx">
 				<view class="price">库存19/82</view>
-				<view class="open-bx" v-if="type == COMMODITY_TYPE.COMMON">
-					<i class="iconfont iconjianqu" @click="subtractEvent"></i>
-					<text class="num" @click="customNum">5/2</text>
-					<i class="iconfont icontianjia" @click="addEvent"></i>
-				</view>
-				<view class="open-bx" v-else-if="type == COMMODITY_TYPE.CHANGE">
-					<input class="change-input" type="text" placeholder="自动调价" />
-					<i class="iconfont icondelete" @click="delEvent"></i>
-				</view>
+				<template v-if="isEdit">
+					<view class="open-bx" v-if="type == COMMODITY_TYPE.COMMON">
+						<i class="iconfont iconjianqu" @click="subtractEvent"></i>
+						<text class="num" @click="customNum">5/2</text>
+						<i class="iconfont icontianjia" @click="addEvent"></i>
+					</view>
+					<view class="open-bx" v-else-if="type == COMMODITY_TYPE.CHANGE">
+						<input class="change-input" type="text" placeholder="自动调价" />
+						<i class="iconfont icondelete" @click="delEvent"></i>
+					</view>
+				</template>
 			</view>
 		</view>
 	</view>
@@ -39,6 +41,10 @@ export default {
 		type: {
 			type: String,
 			default: COMMODITY_TYPE.COMMON
+		},
+		isEdit: {
+			type: Boolean,
+			default: true
 		}
 	},
 	data() {

+ 107 - 0
ghs/src/admin/warehouse/components/CommodityStock.vue

@@ -0,0 +1,107 @@
+<template>
+	<view class="item-cmd_bx">
+		<view class="img_bx">
+			<app-img
+				src="https://img.huaml.com/uploads/12358/202005/29/de74c55596d304b85e79154958e8bf57_small.jpeg"
+				alt="商品图"
+				mode="aspectFit"
+				:height="160"
+			/>
+		</view>
+		<view class="cmd-info_bx">
+			<view class="tit">天使美人</view>
+			<view class="kc">B级</view>
+			<view class="num-open_bx">
+				<view class="price"><text class="unit">¥</text><text>42</text></view>
+
+				<view :class="['open-bx', { light: type == STOCK_TYPE.WARN }]">
+					库存2/5
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+import { STOCK_TYPE } from "@/utils/declare";
+import AppImg from "@/components/app-img";
+export default {
+	name: "Commodity",
+	components: {
+		AppImg
+	},
+	props: {
+		type: {
+			type: String,
+			default: STOCK_TYPE.WARN
+		}
+	},
+	data() {
+		return {
+			STOCK_TYPE
+		};
+	},
+	methods: {
+		customNum() {
+			this.$emit("customNum");
+		},
+		addEvent() {
+			this.$emit("add");
+		},
+		subtractEvent() {
+			this.$emit("subtract");
+		},
+		delEvent() {
+			this.$emit("del");
+		}
+	}
+};
+</script>
+
+<style lang="scss" scoped>
+.item-cmd_bx {
+	position: relative;
+	margin-bottom: 20px;
+	.img_bx {
+		height: 160px;
+		width: 160px;
+		position: absolute;
+		left: 0px;
+		top: 0px;
+	}
+	.cmd-info_bx {
+		padding-left: 177px;
+		& .tit {
+			font-size: 28px;
+			font-weight: 400;
+			color: #333333;
+			padding-top: 10px;
+		}
+		& .kc {
+			color: #999999;
+			font-size: 24px;
+			padding: 10px 0 24px;
+		}
+		& .num-open_bx {
+			display: flex;
+			align-items: center;
+			justify-content: space-between;
+			& .price {
+				font-size: 32upx;
+				color: #333;
+				.unit {
+					font-size: 22upx;
+				}
+			}
+			& .open-bx {
+				display: flex;
+				align-items: center;
+				font-size: 32upx;
+				&.light {
+					color: $redColor;
+				}
+			}
+		}
+	}
+}
+</style>

+ 19 - 3
ghs/src/admin/warehouse/components/table.vue

@@ -1,13 +1,17 @@
 <template>
 	<view class="table-view">
-		<view class="table-header">
+		<view
+			class="table-header"
+			:style="{ backgroundColor: headerBackgroundColor }"
+		>
 			<view
 				class="table-th"
 				v-for="(item, index) in columns"
 				:key="index"
 				:style="{
 					flex: item.width ? `0 0 ${item.width}rpx` : '1',
-					textAlign: item.align || 'left'
+					textAlign: item.align || 'left',
+					fontSzie: `${fontSize}rpx`
 				}"
 			>
 				<!-- <slot :name="item.dataIndex" > -->
@@ -22,7 +26,8 @@
 				:key="columnIndex"
 				:style="{
 					flex: column.width ? `0 0 ${column.width}rpx` : '1',
-					textAlign: column.align || 'left'
+					textAlign: column.align || 'left',
+					fontSzie: `${fontSize}rpx`
 				}"
 			>
 				<!-- {{ item[column.dataIndex] || "" }} -->
@@ -47,6 +52,14 @@ export default {
 		dataList: {
 			type: Array,
 			default: []
+		},
+		headerBackgroundColor: {
+			type: String,
+			default: "#ffffff"
+		},
+		fontSize: {
+			type: String,
+			default: 24
 		}
 	},
 	mounted() {
@@ -95,6 +108,9 @@ export default {
 			.success {
 				color: #09bb07;
 			}
+			.info {
+				color: #333;
+			}
 		}
 	}
 }

+ 3 - 3
ghs/src/admin/warehouse/shop/list.vue

@@ -14,7 +14,7 @@
 					</view>
 				</view>
 				<view :class="['edit']">
-					<text class="iconfont iconxinrenlingquanshezhi"> </text>
+					<text class="iconfont iconshoukuanma1"> </text>
 					<text class="iconfont iconbianji"> </text>
 					<text class="iconfont iconshanchu1"> </text>
 				</view>
@@ -133,8 +133,8 @@ export default {
 				.iconfont {
 					margin: 0 20upx;
 				}
-				.iconxinrenlingquanshezhi {
-					font-size: 46upx;
+				.iconshoukuanma1 {
+					font-size: 40upx;
 					color: $mainColor;
 				}
 				.iconbianji {

+ 239 - 0
ghs/src/admin/warehouse/stockWarn/detailed.vue

@@ -0,0 +1,239 @@
+<template>
+	<view class="ex-page">
+		<view class="top-bar">
+			<SelectList :list="selectListData" @search="searchSelectEvent" />
+		</view>
+		<scroll-view scroll-y scroll-with-animation class="main-view">
+			<view class="space-view ex-table">
+				<Tabel
+					:headerBackgroundColor="'#F0F2F6'"
+					:columns="columns"
+					:dataList="dataList"
+				>
+					<template v-slot="{ row, column }">
+						<view v-if="column.custom == 'time'">
+							<view class="icon-info">
+								<view class="info-name">
+									{{ row.date }}
+								</view>
+								<view class="info-type">
+									{{ row.time }}
+								</view>
+							</view>
+						</view>
+					</template>
+				</Tabel>
+			</view>
+		</scroll-view>
+	</view>
+</template>
+<script>
+import Tabel from "../components/table";
+import SelectList from "@/components/plugin/selectList";
+export default {
+	components: { Tabel, SelectList },
+	data() {
+		return {
+			selectListData: [
+				{
+					title: "时间",
+					key: "time",
+					default: "1",
+					data: [
+						{
+							name: "下午",
+							value: "1"
+						}
+					]
+				},
+				{
+					title: "全部",
+					key: "shopId",
+					default: "01",
+					data: [
+						{
+							name: "瑞景店",
+							value: "01"
+						},
+						{
+							name: "加州店",
+							value: "02"
+						}
+					]
+				}
+			],
+			nowTime: "",
+			showTimeDropdown: false,
+			nowShop: "",
+			showShopDropdown: false,
+			columns: [
+				{
+					name: "事项",
+					dataIndex: "name",
+					color: "info"
+				},
+				{
+					name: "变化",
+					dataIndex: "change",
+					color: "info",
+					width: 120,
+					align: "center"
+				},
+				{
+					name: "剩余库存",
+					dataIndex: "count",
+					color: "info",
+					align: "center"
+				},
+				{
+					name: "时间",
+					align: "right",
+					custom: "time",
+					color: "info"
+				}
+			],
+			dataList: [
+				{
+					name: "调拨入库",
+					count: "1",
+					change: "2",
+					date: "2020-12-01",
+					time: "12:00"
+				},
+				{
+					name: "调拨入库",
+					count: "1",
+					change: "2",
+					date: "2020-12-01",
+					time: "12:00"
+				},
+				{
+					name: "调拨入库",
+					count: "1",
+					change: "2",
+					date: "2020-12-01",
+					time: "12:00"
+				},
+				{
+					name: "调拨入库",
+					count: "1",
+					change: "2",
+					date: "2020-12-01",
+					time: "12:00"
+				},
+				{
+					name: "调拨入库",
+					count: "1",
+					change: "2",
+					date: "2020-12-01",
+					time: "12:00"
+				},
+				{
+					name: "调拨入库",
+					count: "1",
+					change: "2",
+					date: "2020-12-01",
+					time: "12:00"
+				},
+				{
+					name: "调拨入库",
+					count: "1",
+					change: "2",
+					date: "2020-12-01",
+					time: "12:00"
+				},
+				{
+					name: "调拨入库",
+					count: "1",
+					change: "2",
+					date: "2020-12-01",
+					time: "12:00"
+				},
+				{
+					name: "调拨入库",
+					count: "1",
+					change: "2",
+					date: "2020-12-01",
+					time: "12:00"
+				}
+			]
+		};
+	},
+	methods: {
+		searchSelectEvent(params) {
+			console.log(3333, pramrs);
+		}
+	}
+};
+</script>
+<style lang="scss" scoped>
+.ex-page {
+	height: 100%;
+	display: flex;
+	flex-direction: column;
+	.top-bar {
+		position: absolute;
+		left: 0;
+		width: 100%;
+		// box-shadow: 0 5upx 5upx 5upx rgba($color: #000000, $alpha: 0.2);
+		z-index: 20;
+	}
+	.main-view {
+		margin-top: 80upx;
+		flex: 1;
+		background-color: #f9fbfc;
+		.space-view {
+			background-color: #fff;
+		}
+		.ex-info {
+			padding: 30upx;
+			.info-cell {
+				display: flex;
+				align-items: center;
+				font-size: 28upx;
+				&:not(:first-child) {
+					margin-top: 20upx;
+				}
+				.cell-label {
+					width: 110upx;
+					color: #666666;
+				}
+				.cell-value {
+					margin-left: 40upx;
+					color: #333;
+					&.warning {
+						color: #ffaf1d;
+					}
+					&.success {
+						color: #27c325;
+					}
+				}
+			}
+		}
+		.ex-table {
+			/deep/.icon-info {
+				color: #333333;
+			}
+		}
+	}
+	.bar-view {
+		display: flex;
+		justify-content: space-between;
+		align-items: center;
+		padding: 0 30upx;
+		width: 100%;
+		height: 100upx;
+
+		box-shadow: 0px -1px 6px 0px rgba(4, 0, 0, 0.06);
+		.info-view {
+			font-size: 26upx;
+		}
+		.btn-view {
+			display: flex;
+			.admin-button-com {
+				margin: 0 10upx;
+			}
+		}
+	}
+}
+</style>

+ 268 - 0
ghs/src/admin/warehouse/stockWarn/manage.vue

@@ -0,0 +1,268 @@
+<template>
+	<view class="billing_box_bg">
+		<app-tabs
+			:tabs="tabs"
+			:currentTab="tabIndex"
+			@change="change"
+			itemWidth="50%"
+		/>
+		<view class="input-wrap">
+			<view class="city-select text-line">
+				瑞经典<text class="iconfont iconsanjiao_xia"></text>
+			</view>
+			<view class="search-bar">
+				<app-search-module
+					placeholder="输入商品名拼音首字母查询"
+					@search="searchFn"
+				/>
+			</view>
+		</view>
+		<view class="scroll-middle_bx">
+			<scroll-view
+				scroll-y
+				scroll-with-animation
+				class="tab-view"
+				:scroll-top="scrollTop"
+			>
+				<div
+					v-for="(item, index) in tabbar"
+					:key="index"
+					class="tab-bar-item"
+					:class="[currentTab == index ? 'active' : '']"
+					:data-current="index"
+					@tap.stop="swichNav($event, item)"
+				>
+					<text>{{ item.categoryName || "暂无" }}</text>
+				</div>
+			</scroll-view>
+			<block v-for="(item, index) in tabbar" :key="index">
+				<scroll-view scroll-y class="right-box" v-if="currentTab == index">
+					<view class="item_list_bx">
+						<view class="item_list_title">{{ item.categoryName }}</view>
+						<CommodityStock :type="pageType"></CommodityStock>
+						<CommodityStock :type="pageType"></CommodityStock>
+					</view>
+
+					<!--内容部分 start -->
+					<!-- v-if="!$util.isEmpty(list.data)" -->
+
+					<!-- <block v-else>
+                    <app-wrapper-empty title="暂无数据" :is-empty="$util.isEmpty(list.data)" />
+                </block> -->
+					<!--内容部分 end -->
+				</scroll-view>
+			</block>
+		</view>
+		<!-- :maskClosable="false" -->
+	</view>
+</template>
+
+<script>
+import AppSearchModule from "@/components/module/app-search";
+import AppTabs from "@/components/plugin/tabs";
+import AppWrapperEmpty from "@/components/app-wrapper-empty";
+import CommodityStock from "../components/CommodityStock";
+import { STOCK_TYPE } from "@/utils/declare";
+
+export default {
+	name: "Billing", // 开单
+	components: {
+		AppTabs,
+		AppSearchModule,
+		AppWrapperEmpty,
+		CommodityStock
+	},
+	data() {
+		return {
+			STOCK_TYPE,
+			tabIndex: 0,
+			tabs: [
+				{
+					name: "库存预警",
+					key: STOCK_TYPE.WARN
+				},
+				{
+					name: "库存管理",
+					key: STOCK_TYPE.MANAGE
+				}
+			],
+			tabbar: [
+				{
+					categoryName: "花束",
+					delStatus: "0",
+					description: "",
+					goodsNum: "0",
+					id: "95",
+					img: "https://img.huaml.com",
+					inTurn: "0",
+					merchantId: "12358",
+					parentId: "0",
+					sold: "0",
+					status: "1"
+				},
+				{
+					categoryName: "花篮",
+					delStatus: "0",
+					description: "",
+					goodsNum: "0",
+					id: "96",
+					img: "https://img.huaml.com",
+					inTurn: "0",
+					merchantId: "12358",
+					parentId: "0",
+					sold: "0",
+					status: "1"
+				}
+			],
+			currentTab: 0
+		};
+	},
+	computed: {
+		pageType() {
+			return this.tabs[this.tabIndex].key;
+		}
+	},
+	methods: {
+		affirm(val) {
+			if (val.index === 0) {
+				this.modalCancel();
+			} else {
+			}
+		},
+		change(e) {
+			if (this.tabIndex == e.index) {
+				return false;
+			} else {
+				this.tabIndex = e.index;
+			}
+		},
+		searchFn() {},
+		scrollTop() {},
+		swichNav(e) {
+			let cur = e.currentTarget.dataset.current;
+			if (this.currentTab == cur) {
+				return false;
+			} else {
+				this.currentTab = cur;
+				// this.form.categoryId = item.id;
+				// this.resetList();
+				// this._list();
+				// this.checkCor()
+			}
+		}
+	}
+};
+</script>
+
+<style lang="scss" scoped>
+.billing_box_bg {
+	height: 100%;
+	display: flex;
+	flex-direction: column;
+	.input-wrap {
+		display: flex;
+		padding: 22px 20px 22px 30px;
+		.search-bar {
+			flex: 1;
+		}
+		.city-select {
+			display: flex;
+			flex-shrink: 0;
+			align-items: center;
+			margin-right: 20upx;
+			background-color: #ffff;
+			.iconfont {
+				margin-left: 20upx;
+				font-size: 30upx;
+			}
+		}
+	}
+	.scroll-middle_bx {
+		flex: 1;
+		display: flex;
+	}
+	.tab-view {
+		height: 100%;
+		width: 170upx;
+		flex-shrink: 0;
+		background-color: #f0f2f6;
+		.tab-bar-item {
+			position: relative;
+			width: 170upx;
+			height: 90upx;
+			box-sizing: border-box;
+			display: flex;
+			align-items: center;
+			justify-content: center;
+			font-size: 26upx;
+			color: #444;
+			font-weight: 400;
+			.tag {
+				display: flex;
+				padding: 0 20px;
+				align-items: center;
+				position: absolute;
+				right: 0;
+				top: 0;
+				height: 32px;
+				background: #ff2842;
+				border-radius: 16px;
+				color: #fff;
+				font-size: 20px;
+			}
+		}
+
+		.active {
+			position: relative;
+			color: $mainColor;
+			font-size: 26px;
+			background: #fff;
+		}
+
+		.active::before {
+			content: "";
+			position: absolute;
+			border-left: 8upx solid $mainColor;
+			height: 100%;
+			left: 0;
+		}
+	}
+	.right-box {
+		height: 100%;
+		flex: 1;
+		box-sizing: border-box;
+	}
+	.item_list_bx {
+		padding: 40px 16px;
+	}
+	.item_list_title {
+		margin-bottom: 20px;
+		font-size: 24px;
+		font-weight: 600;
+		color: #666666;
+	}
+	.select-cmd_bx {
+		& .kc {
+			color: #999999;
+			font-size: 28px;
+			font-weight: 400;
+			margin-bottom: 60px;
+			text-align: center;
+			margin-top: 10px;
+		}
+		& .num_bx {
+			display: flex;
+			align-items: center;
+			margin-bottom: 80px;
+			& > input {
+				width: 212px;
+				height: 80px;
+				border: 1px solid #dddddd;
+				border-radius: 4px;
+				text-align: center;
+				margin-right: 24px;
+			}
+		}
+	}
+}
+</style>

+ 248 - 0
ghs/src/components/plugin/selectList.vue

@@ -0,0 +1,248 @@
+<template>
+	<view class="select-view">
+		<view class="select-mark-view" v-show="showList" @click="touchMarkEvent">
+		</view>
+		<view class="content-view" @click.stop="touchContent">
+			<slot> </slot>
+			<view class="filter-view">
+				<view class="filter-header">
+					<view
+						class="filter-title"
+						v-for="(item, listIndex) in dataList"
+						:key="listIndex"
+					>
+						<view @click="showListEvent(listIndex)">
+							<text>{{ getListCheckName(item) }}</text>
+							<text
+								v-if="listIndex == curIndex"
+								class="iconfont iconsanjiaoshang"
+							></text>
+							<text v-else class="iconfont iconsanjiao_xia"></text>
+						</view>
+					</view>
+				</view>
+
+				<view class="filter-list" v-if="showList">
+					<view
+						class="filter-item"
+						v-for="(item, index) in dataList[curIndex].data"
+						:key="index"
+						@click="selectItemEvent(item)"
+					>
+						<text class="iconfont icondagou" v-if="item.checked"> </text>
+						<text>
+							{{ item.name }}
+						</text>
+					</view>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+<script>
+import { copyObject } from "@/utils/util";
+const SELECT_TYPES = {
+	RADIO: "radio",
+	MULTIPLE: "multiple"
+};
+export default {
+	props: {
+		list: {
+			type: Array,
+			default() {
+				return [
+					//   {
+					//       title:'',  //标题
+					//        key:'',  //字段名
+					//       data:[
+					//           {
+					//               name:'',
+					//               value:''
+					//           }
+					//       ]
+					//   }
+				];
+			}
+		},
+		type: {
+			type: String,
+			default: SELECT_TYPES.RADIO //multiple
+		}
+	},
+
+	data() {
+		return {
+			curValue: "",
+			curIndex: -1,
+			showList: false,
+			dataList: []
+		};
+	},
+	onLoad() {},
+	created() {
+		this.initList();
+	},
+	computed: {
+		curItemName() {
+			let curlist = [];
+			if (this.dataList && this.dataList[this.curIndex]) {
+				curlist = this.dataList[this.curIndex].data;
+			}
+			let item = curlist.find(ele => {
+				return ele.value == this.curValue;
+			});
+			if (item) {
+				return item.name;
+			}
+			return false;
+		}
+	},
+	methods: {
+		initList() {
+			this.dataList = [];
+			for (const item of this.list) {
+				let ele = copyObject(item);
+				ele.data.forEach(element => {
+					if (element.value == ele.default) {
+						element.checked = true;
+					} else {
+						element.checked = false;
+					}
+				});
+				this.dataList.push(ele);
+			}
+		},
+		getListCheckName(info) {
+			let name = info.title;
+			for (const item of info.data) {
+				if (item.checked) {
+					name = item.name;
+					break;
+				}
+			}
+			return name;
+		},
+		touchContent() {},
+		touchMarkEvent() {
+			this.showList = false;
+			this.curIndex = -1;
+		},
+		selectItemEvent(item) {
+			if (SELECT_TYPES.RADIO == this.type) {
+				let list = this.dataList[this.curIndex].data;
+				list.forEach(element => {
+					element.checked = false;
+				});
+
+				console.log(444444, list);
+			}
+			this.curValue = item.value;
+			item.checked = true;
+
+			this.$nextTick(() => {
+				this.searchEvent();
+			});
+		},
+		showListEvent(index) {
+			this.curIndex = index;
+			this.showList = true;
+		},
+		searchEvent() {
+			this.showList = false;
+			let params = this.getParams();
+			this.$emit("search", params);
+		},
+		getParams() {
+			let params = {};
+			for (const item of this.dataList) {
+				for (const listItem of item.data) {
+					params[item.key] = "";
+					if (listItem.checked) {
+						params[item.key] = listItem.value;
+						break;
+					}
+				}
+			}
+			return params;
+		}
+	},
+	watch: {
+		list(val, old) {
+			if (val && val instanceof Array) {
+				this.initList();
+			}
+		}
+	}
+};
+</script>
+<style lang="scss" scoped>
+.select-view {
+	display: fixed;
+	width: 100%;
+
+	.select-mark-view {
+		/*  #ifndef  H5  */
+		height: 100vh;
+		/*  #endif  */
+		/*  #ifdef  H5  */
+		height: 100%;
+		/*  #endif  */
+		position: absolute;
+		width: 100%;
+		background-color: rgba(0, 0, 0, 0.5);
+	}
+	.content-view {
+		position: relative;
+		z-index: 1;
+		.filter-view {
+			.filter-header {
+				display: flex;
+				background-color: #fff;
+				.filter-title {
+					position: relative;
+					flex: 1;
+					height: 80upx;
+					line-height: 80upx;
+					text-align: center;
+					&:not(:last-child):after {
+						position: absolute;
+						content: "";
+						width: 1px;
+						height: 50%;
+						background-color: #dddddd;
+						top: 50%;
+						right: 0;
+						transform: translateY(-50%);
+					}
+					.iconfont {
+						font-size: 30upx;
+					}
+				}
+			}
+			.filter-list {
+				position: absolute;
+				top: 100%;
+				left: 0;
+				width: 100%;
+				padding: 20upx 30upx;
+				max-height: 400upx;
+				background-color: #f8f9fb;
+				border-radius: 0 0 25upx 25upx;
+				.filter-item {
+					width: 100%;
+					height: 70upx;
+					line-height: 70upx;
+					font-size: 26upx;
+					.iconfont {
+						font-size: 36upx;
+						color: $mainColor;
+					}
+					text {
+						margin: 0 10upx;
+					}
+				}
+			}
+		}
+	}
+}
+</style>

+ 14 - 0
ghs/src/components/plugin/tabs.vue

@@ -314,9 +314,23 @@ export default {
 }
 
 .tui-tabs-item {
+	position: relative;
 	display: flex;
 	align-items: center;
 	justify-content: center;
+
+	&:not(:last-child) {
+		&:after {
+			position: absolute;
+			right: 0;
+			top: 50%;
+			height: 40upx;
+			width: 1px;
+			content: "";
+			transform: translateY(-50%);
+			background-color: #dddddd;
+		}
+	}
 }
 
 .tui-tabs-disabled {

+ 19 - 0
ghs/src/pages.json

@@ -485,6 +485,25 @@
 					}
 				}
 			]
+		},
+		{
+			"root": "admin/warehouse/stockWarn/",
+			"pages": [
+				{
+					"path": "manage",
+					"style": {
+						"navigationBarTitleText": "库存",
+						"enablePullDownRefresh": true
+					}
+				},
+				{
+					"path": "detailed",
+					"style": {
+						"navigationBarTitleText": "明细",
+						"enablePullDownRefresh": true
+					}
+				}
+			]
 		}
 	],
 	"globalStyle": {

+ 9 - 3
ghs/src/utils/declare.js

@@ -1,5 +1,11 @@
 //商品选购类别
 export const COMMODITY_TYPE = {
-  COMMON: '01',
-  CHANGE: '02',
-}
+	COMMON: "01", //普通加减
+	CHANGE: "02" //改价
+};
+
+//库存类别
+export const STOCK_TYPE = {
+	WARN: "01", //预警
+	MANAGE: "02" //管理
+};

+ 161 - 117
ghs/src/utils/util.js

@@ -1,77 +1,103 @@
-import CONSTANT from '@/constant'
-import { mobileReg } from '@/utils/tools/validate'
-import { getMiniAppUrl, isWxBrowser } from '@/utils/common'
+import CONSTANT from "@/constant";
+import { mobileReg } from "@/utils/tools/validate";
+import { getMiniAppUrl, isWxBrowser } from "@/utils/common";
 // 是否为空
-const isEmpty = (val) => {
+const isEmpty = val => {
 	if (val instanceof Array) {
-		if (val.length === 0) return true
+		if (val.length === 0) return true;
 	} else if (val instanceof Object) {
-		if (!Object.keys(val).length) return true
+		if (!Object.keys(val).length) return true;
 	} else {
-		if (val === 'null' || val === null || val === 'undefined' || val === undefined || val === '') return true
-		return false
+		if (
+			val === "null" ||
+			val === null ||
+			val === "undefined" ||
+			val === undefined ||
+			val === ""
+		)
+			return true;
+		return false;
 	}
-	return false
-}
-const numberFormat =(number,type)=> {
-   if(number > 10000) {
-		let num = (number / 10000 + '').split('.')
-	   	return `${num[0]}.${(''+ num[1]).slice(0,1)} 万`
-   }else {
-	   return number
-   }
-}
+	return false;
+};
+/**
+ * 深拷贝对象
+ * @param {要拷贝对象} obj
+ */
+export const copyObject = obj => {
+	let str,
+		newobj = Array.isArray(obj) === true ? [] : {};
+	if (typeof obj !== "object") {
+		return;
+	} else if (JSON) {
+		(str = JSON.stringify(obj)), //系列化对象
+			(newobj = JSON.parse(str)); //还原
+	} else {
+		for (let i in obj) {
+			newobj[i] = typeof obj[i] === "object" ? copyObject(obj[i]) : obj[i];
+		}
+	}
+	return newobj;
+};
+const numberFormat = (number, type) => {
+	if (number > 10000) {
+		let num = (number / 10000 + "").split(".");
+		return `${num[0]}.${("" + num[1]).slice(0, 1)} 万`;
+	} else {
+		return number;
+	}
+};
 // 截取图片上传字符串
-const imgSubstr = (val) => {
-	if (isEmpty(val)) return []
+const imgSubstr = val => {
+	if (isEmpty(val)) return [];
 	if (isArray(val)) {
 		return val.map(e => {
-			let index = e.indexOf('/uploads/')
-			e = e.substring(index)
-			return e
-		})
+			let index = e.indexOf("/uploads/");
+			e = e.substring(index);
+			return e;
+		});
 	} else {
-		let index = val.indexOf('/uploads/')
-		return val.substring(index)
+		let index = val.indexOf("/uploads/");
+		return val.substring(index);
 	}
-}
+};
 
 // 是否为数组
-const isArray = (arr) => {
-	return (typeof arr == 'object') && arr.constructor == Array
-}
+const isArray = arr => {
+	return typeof arr == "object" && arr.constructor == Array;
+};
 
 // 是否为字符串
-const isString = (str) => {
-	return (typeof str == 'string') && str.constructor == String
-}
+const isString = str => {
+	return typeof str == "string" && str.constructor == String;
+};
 
 // 是否为对象
-const isObject = (obj) => {
-	return (typeof obj == 'object') && obj.constructor == Object
-}
+const isObject = obj => {
+	return typeof obj == "object" && obj.constructor == Object;
+};
 
 // 是否为数字
-const isNumber = (num) => {
-	return (typeof num == 'number') && num.constructor == Number
-}
+const isNumber = num => {
+	return typeof num == "number" && num.constructor == Number;
+};
 
 // 是否为日期类型
-const isDate = (date) => {
-	return (typeof date == 'object') && date.constructor == Date
-}
+const isDate = date => {
+	return typeof date == "object" && date.constructor == Date;
+};
 
 // 是否为函数
-const isFunction = (obj) => {
-	return (typeof obj == 'object') && obj.constructor == Function
-}
+const isFunction = obj => {
+	return typeof obj == "object" && obj.constructor == Function;
+};
 
-const checkMobile = (num) => {
-	return mobileReg.test(num)
-}
-const checkName = (str) => {
-	return /^[\u4E00-\u9FA5A-Za-z\s]+(·[\u4E00-\u9FA5A-Za-z]+)*$/.test(str)
-}
+const checkMobile = num => {
+	return mobileReg.test(num);
+};
+const checkName = str => {
+	return /^[\u4E00-\u9FA5A-Za-z\s]+(·[\u4E00-\u9FA5A-Za-z]+)*$/.test(str);
+};
 
 /** *
  * 检查登录
@@ -79,18 +105,20 @@ const checkName = (str) => {
  * @parmas isFrist 为true时开头为&
  */
 export async function getCheckLogin() {
-	let status = false
-	let token = uni.getStorageSync('token')
+	let status = false;
+	let token = uni.getStorageSync("token");
 	if (token) {
-		status = true
+		status = true;
 	} else {
 		// #ifdef H5
 		if (isWxBrowser()) {
-			let option = JSON.parse(uni.getStorageSync('currentQuery')) || {}
-			console.log('option', option)
+			let option = JSON.parse(uni.getStorageSync("currentQuery")) || {};
+			console.log("option", option);
 			// console.log('option', getMiniAppUrl(option, true))
 			// return false
-			location.href = `${CONSTANT.hostUrl}/auth/prepare?url=${encodeURIComponent(getMiniAppUrl(option, true))}`
+			location.href = `${CONSTANT.hostUrl}/auth/prepare?url=${encodeURIComponent(
+				getMiniAppUrl(option, true)
+			)}`;
 		} else {
 			// uni.showToast({
 			// 	title: '请用微信浏览器打开!',
@@ -102,13 +130,13 @@ export async function getCheckLogin() {
 		// #ifndef H5
 		// #endif
 	}
-	return status
+	return status;
 }
 
 export const floatFormat = (f, n) => {
-	let m = Math.pow(10, n)
-	return parseInt(f * m, 10) / m
-}
+	let m = Math.pow(10, n);
+	return parseInt(f * m, 10) / m;
+};
 
 /** *
  * 对象参数转为url参数
@@ -119,22 +147,25 @@ export const parse = (query, isFrist = false) => {
 	let str = Object.keys(query)
 		.filter(key => !isEmpty(query[key]))
 		.reduce((result, key) => {
-			const value = query[key]
+			const value = query[key];
 			// in查询特殊处理
 			if (Array.isArray(value) && !isEmpty(value)) {
-				return `${result}&${value.reduce((val, cVal) => `${val ? `${val}&` : val}${key}=${cVal}`, '')}`
+				return `${result}&${value.reduce(
+					(val, cVal) => `${val ? `${val}&` : val}${key}=${cVal}`,
+					""
+				)}`;
 			}
 
 			// between查询做特殊处理
-			if (typeof value === 'object' && !isEmpty(value)) {
-				const [start, end] = value
-				return `${result}&${key}[]=${start}&${key}[]=${end}`
+			if (typeof value === "object" && !isEmpty(value)) {
+				const [start, end] = value;
+				return `${result}&${key}[]=${start}&${key}[]=${end}`;
 			}
 
-			return `${result}&${key}=${value}`
-		}, '')
-	return isFrist ? str : str.replace(/^&/, '?')
-}
+			return `${result}&${key}=${value}`;
+		}, "");
+	return isFrist ? str : str.replace(/^&/, "?");
+};
 
 /** *
  * 全局时间转换
@@ -142,42 +173,55 @@ export const parse = (query, isFrist = false) => {
  * @parmas fmt 默认转换的时间格式
  * @parmas all 为true时time为13位时间戳
  */
-export const $formatDate = (num, fmt = 'YYYY-MM-DD HH:mm', all) => {
-	num = all ? num : num * 1000
-	let date = new Date()
-	date.setTime(num)
+export const $formatDate = (num, fmt = "YYYY-MM-DD HH:mm", all) => {
+	num = all ? num : num * 1000;
+	let date = new Date();
+	date.setTime(num);
 	let o = {
-		'M+': date.getMonth() + 1,
-		'D+': date.getDate(),
-		'h+': date.getHours() % 12 === 0 ? 12 : date.getHours() % 12,
-		'H+': date.getHours(),
-		'm+': date.getMinutes(),
-		's+': date.getSeconds(),
-		'q+': Math.floor((date.getMonth() + 3) / 3),
-		'S': date.getMilliseconds()
-	}
+		"M+": date.getMonth() + 1,
+		"D+": date.getDate(),
+		"h+": date.getHours() % 12 === 0 ? 12 : date.getHours() % 12,
+		"H+": date.getHours(),
+		"m+": date.getMinutes(),
+		"s+": date.getSeconds(),
+		"q+": Math.floor((date.getMonth() + 3) / 3),
+		S: date.getMilliseconds()
+	};
 	let week = {
-		'0': '\u65e5',
-		'1': '\u4e00',
-		'2': '\u4e8c',
-		'3': '\u4e09',
-		'4': '\u56db',
-		'5': '\u4e94',
-		'6': '\u516d'
-	}
+		"0": "\u65e5",
+		"1": "\u4e00",
+		"2": "\u4e8c",
+		"3": "\u4e09",
+		"4": "\u56db",
+		"5": "\u4e94",
+		"6": "\u516d"
+	};
 	if (/(Y+)/.test(fmt)) {
-		fmt = fmt.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length))
+		fmt = fmt.replace(
+			RegExp.$1,
+			(date.getFullYear() + "").substr(4 - RegExp.$1.length)
+		);
 	}
 	if (/(E+)/.test(fmt)) {
-		fmt = fmt.replace(RegExp.$1, ((RegExp.$1.length > 1) ? (RegExp.$1.length > 2 ? '\u661f\u671f' : '\u5468') : '') + week[date.getDay() + ''])
+		fmt = fmt.replace(
+			RegExp.$1,
+			(RegExp.$1.length > 1
+				? RegExp.$1.length > 2
+					? "\u661f\u671f"
+					: "\u5468"
+				: "") + week[date.getDay() + ""]
+		);
 	}
 	for (let k in o) {
-		if (new RegExp('(' + k + ')').test(fmt)) {
-			fmt = fmt.replace(RegExp.$1, (RegExp.$1.length === 1) ? (o[k]) : (('00' + o[k]).substr(('' + o[k]).length)))
+		if (new RegExp("(" + k + ")").test(fmt)) {
+			fmt = fmt.replace(
+				RegExp.$1,
+				RegExp.$1.length === 1 ? o[k] : ("00" + o[k]).substr(("" + o[k]).length)
+			);
 		}
 	}
-	return fmt
-}
+	return fmt;
+};
 
 /** *
  * 全局公共按钮跳转
@@ -186,58 +230,58 @@ export const $formatDate = (num, fmt = 'YYYY-MM-DD HH:mm', all) => {
  * @parmas item.query 为跳转携带的参数
  * @parmas item.type 为跳转调用的函数 1为navigateTo, 2为redirectTo, 3为reLaunch, 4为switchTab
  */
-export const pageTo = (item) => {
-	
+export const pageTo = item => {
 	if (isNumber(item)) {
 		uni.navigateBack({
 			delta: item
-		})
+		});
 	}
 	if (isString(item)) {
 		uni.navigateTo({
 			url: item
-		})
-		return false
+		});
+		return false;
 	}
 	if (item.funtion) {
-		item.funtion()
+		item.funtion();
 	}
 	if (item.url) {
-		let query = item.query ? parse(item.query) : ''
-		let allUrl = `${item.url}${query}`
+		let query = item.query ? parse(item.query) : "";
+		let allUrl = `${item.url}${query}`;
 		if (item.type == 2) {
 			uni.redirectTo({
 				url: allUrl
-			})
+			});
 		} else if (item.type == 3) {
 			uni.reLaunch({
 				url: allUrl
-			})
+			});
 		} else if (item.type == 4) {
-			console.log('switchTab', item.query)
-			uni.setStorageSync('switchTabQuery', item.query)
+			console.log("switchTab", item.query);
+			uni.setStorageSync("switchTabQuery", item.query);
 			uni.switchTab({
 				url: item.url
-			})
+			});
 		} else {
 			uni.navigateTo({
 				url: allUrl
-			})
+			});
 		}
 	}
-}
+};
 /** *
  * 是否登录
  */
 export const isLogin = () => {
-	let token = uni.getStorageSync('token')
+	let token = uni.getStorageSync("token");
 	if (token) {
-		return true
+		return true;
 	}
-	return false
-}
+	return false;
+};
 export default {
 	isEmpty,
+	copyObject,
 	imgSubstr,
 	isArray,
 	parse,
@@ -249,4 +293,4 @@ export default {
 	isLogin,
 	getCheckLogin,
 	numberFormat
-}
+};