feat:食堂库存校验

This commit is contained in:
dzc committed 2025-01-13 20:41:25 +08:00
1 parent 44fbb4af25
commit 7b45cdfecd
7 files changed
+374 -363

No files matched your search

+1 -1
View File
@@ -87,4 +87,4 @@
"vite": "5.2.8",
"vue-tsc": "^1.0.24"
}
}
}
+74 -82
View File
@@ -1,6 +1,6 @@
<script setup lang="ts">
import { ref, reactive, withDefaults, defineProps, defineEmits } from "vue";
import { onLoad, onShow } from "@dcloudio/uni-app";
import { ref, reactive, withDefaults, defineProps, defineEmits } from "vue"
import { onLoad, onShow } from "@dcloudio/uni-app"
import { hooks, router } from "@@/index"
import { merchantApi } from "@@/api"
@@ -8,101 +8,93 @@ import { merchantApi } from "@@/api"
const emit = defineEmits(["update:modelValue", "add", "remove"])
export type Props = {
mode: "default" | "car",
productId: number | null,
modelValue: number,
pic: string,
name: string,
price: any,
marketPrice: number,
salesCount: number,
stock: number,
count: number,
mode: "default" | "car"
productId: number | null
modelValue: number
pic: string
name: string
price: any
marketPrice: number
salesCount: number
stock: number
count: number
}
const props = withDefaults(defineProps<Props>(), {
mode: "default",
productId: null,
modelValue: 0,
pic: '',
name: '',
price: 0,
marketPrice: 0,
salesCount: 0,
stock: 0,
count: 0,
});
const state = reactive<any>({
mode: "default",
productId: null,
modelValue: 0,
pic: "",
name: "",
price: 0,
marketPrice: 0,
salesCount: 0,
stock: 0,
count: 0,
})
const state = reactive<any>({})
onLoad(() => {
});
onShow(() => {
})
onLoad(() => {})
onShow(() => {})
const add = () => {
if (props.modelValue >= 99) {
emit('update:modelValue', 99);
return
}
emit('update:modelValue', props.modelValue + 1)
emit('add')
if (props.modelValue >= 99) {
emit("update:modelValue", 99)
return
}
// emit("update:modelValue", props.modelValue + 1)
emit("add")
}
const remove = () => {
if (props.modelValue < 0) {
emit('update:modelValue', 0);
return
}
emit('update:modelValue', props.modelValue - 1)
emit('remove')
if (props.modelValue < 0) {
emit("update:modelValue", 0)
return
}
// emit("update:modelValue", props.modelValue - 1)
emit("remove")
}
</script>
<template>
<view :class="[props.mode == 'car' ? 'car_border' : '']"
class="flex-row items-start card p-x-15rpx p-y-25rpx m-b-20rpx"
@tap="props.productId && router.jump('/pages/merchant/product-detail', { id: props.productId })">
<image v-if="props.mode == 'default'" class=" size-180rpx rounded-12rpx" :src="props.pic" mode="aspectFill" />
<image v-if="props.mode == 'car'" class=" size-120rpx rounded-12rpx" :src="props.pic" mode="aspectFill" />
<view class="flex-1 m-l-20rpx">
<view class="sub-text-28 show-line">{{ props.name }}</view>
<view class="m-t-10rpx flex-row items-center">
<view class="second-text-32 color-red!">
¥{{ hooks.useMoney(props.price) }}
</view>
<view v-if="props.mode == 'default'" class="m-l-20rpx second-text-24 line-through">
¥{{ hooks.useMoney(props.marketPrice) }}
</view>
</view>
<view v-if="props.mode == 'car'" class="m-t-10rpx second-text-28">
x{{ props.count }}
</view>
<view v-if="props.mode == 'default'" class="flex-row items-center MT 10rpx">
<view class="second-text-24">{{ hooks.useFormatNum("库存", "", props.stock) }}</view>
<view class="w-1px h-20rpx m-x-5rpx bg-#999" />
<view class="second-text-24">{{ hooks.useFormatNum("销量", "", props.salesCount) }}</view>
</view>
<view class="flex-row items-center justify-end m-t-20rpx">
<view class="flex-row-center size-40rpx rounded-full border-1px border-solid border-color-#0094ff"
@tap.stop="remove">
<view class="iconfont icon-a--" />
</view>
<view class="sub-text-28 m-x-20rpx">{{ props.modelValue }}</view>
<view class="flex-row-center size-40rpx rounded-full bg-#0094ff" @tap.stop="add">
<view class="font-size-40rpx color-white iconfont icon-a-" />
</view>
</view>
</view>
</view>
<view
:class="[props.mode == 'car' ? 'car_border' : '']"
class="flex-row items-start card p-x-15rpx p-y-25rpx m-b-20rpx"
@tap="props.productId && router.jump('/pages/merchant/product-detail', { id: props.productId })"
>
<image v-if="props.mode == 'default'" class="size-180rpx rounded-12rpx" :src="props.pic" mode="aspectFill" />
<image v-if="props.mode == 'car'" class="size-120rpx rounded-12rpx" :src="props.pic" mode="aspectFill" />
<view class="flex-1 m-l-20rpx">
<view class="sub-text-28 show-line">{{ props.name }}</view>
<view class="m-t-10rpx flex-row items-center">
<view class="second-text-32 color-red!"> ¥{{ hooks.useMoney(props.price) }} </view>
<view v-if="props.mode == 'default'" class="m-l-20rpx second-text-24 line-through">
¥{{ hooks.useMoney(props.marketPrice) }}
</view>
</view>
<view v-if="props.mode == 'car'" class="m-t-10rpx second-text-28"> x{{ props.count }} </view>
<view v-if="props.mode == 'default'" class="flex-row items-center MT 10rpx">
<view class="second-text-24">{{ hooks.useFormatNum("库存", "", props.stock) }}</view>
<view class="w-1px h-20rpx m-x-5rpx bg-#999" />
<view class="second-text-24">{{ hooks.useFormatNum("销量", "", props.salesCount) }}</view>
</view>
<view class="flex-row items-center justify-end m-t-20rpx">
<view
class="flex-row-center size-40rpx rounded-full border-1px border-solid border-color-#0094ff"
@tap.stop="remove"
>
<view class="iconfont icon-a--" />
</view>
<view class="sub-text-28 m-x-20rpx">{{ props.modelValue }}</view>
<view class="flex-row-center size-40rpx rounded-full bg-#0094ff" @tap.stop="add">
<view class="font-size-40rpx color-white iconfont icon-a-" />
</view>
</view>
</view>
</view>
</template>
<style lang="scss" scoped>
.car_border {
border: 1px solid #f2f2f2;
border: 1px solid #f2f2f2;
}
</style>
</style>
+34 -11
View File
@@ -50,7 +50,13 @@ const getShopCar = async () => {
const _pa = { merchantId: state.detailId, hardType: "CANTEEN" }
const { data } = await foodApi.getShopCar(_pa)
state.carList = data ?? []
asyncCarCount(state.shopList)
if (isEmpty(state.carList)) {
state.shopList.forEach((v: any) => {
v.productCount = 0
})
} else {
asyncCarCount(state.shopList)
}
state.totPrice = state.carList.map((v) => Number(v.totalPrice)).reduce((pre, next) => pre + next, 0)
state.totCount = state.carList.map((v) => v.productCount).reduce((pre, next) => pre + next, 0)
}
@@ -110,22 +116,39 @@ const asyncCarCount = (shopList) => {
}
const doAdd = async (item: any) => {
chentProductCount(item)
chentProductCount(item, "add")
}
const remove = async (item: any) => {
chentProductCount(item)
chentProductCount(item, "remove")
}
const chentProductCount = async (item: any) => {
const target = state.carList.find((v) => v.productId == item.productId)
const targetIndex = state.carList.findIndex((v) => v.productId == item.productId)
if (target) {
state.carList[targetIndex].productCount = item.productCount
} else {
state.carList.push({ ...item, productId: item.id })
const chentProductCount = async (item: any, type: "add" | "remove") => {
try {
uni.showLoading({ title: "加载中", mask: true })
let count = item.productCount
if (type === "add") {
count += 1
const { code }: any = await foodApi.checkSpuStock({ spuId: item.productId, incrCount: count })
if (code != 0) throw new Error()
} else if (type === "remove") {
count -= 1
}
const target = state.carList.find((v) => v.productId == item.productId)
const targetIndex = state.carList.findIndex((v) => v.productId == item.productId)
if (target) {
state.carList[targetIndex].productCount = count
} else {
state.carList.push({ ...item, productId: item.id, productCount: count })
}
nextTick(async () => {
await updateShopCar()
})
} catch (e) {
console.error(e)
} finally {
uni.hideLoading()
}
nextTick(() => updateShopCar())
}
const doClassify = (i: any) => {
+156 -143
View File
@@ -1,171 +1,184 @@
<script setup lang="ts">
import { ref, reactive } from "vue";
import { onLoad, onShow } from "@dcloudio/uni-app";
import { isEmpty } from "radash";
import { ref, reactive } from "vue"
import { onLoad, onShow } from "@dcloudio/uni-app"
import { isEmpty } from "radash"
import { hooks, router, constant } from "@@/index";
import { merchantApi, userApi, walletApi } from "@@/api";
import { hooks, router, constant } from "@@/index"
import { merchantApi, userApi, walletApi } from "@@/api"
const data = {
serviceList: [
{ text: "钱包管理", icon: "/static/icon/service_3.png", router: "/pages/wallet/list" },
{ text: "包厢预定", icon: "/static/icon/service_4.png", router: "/pages/boxes/apply-list" },
{ text: "外来人员就餐", icon: "/static/icon/service_1.png", router: "/pages/apply/foreign-list" },
{ text: "跨单位就餐", icon: "/static/icon/service_1.png", router: "/pages/apply/stride-list" },
{ text: "人脸管理", icon: "/static/icon/service_5.png", router: "/pages/human-face/list" },
{ text: "测试登录", icon: "/static/icon/login.png", router: "/pages/test/login" },
],
};
serviceList: [
{ text: "钱包管理", icon: "/static/icon/service_3.png", router: "/pages/wallet/list" },
{ text: "包厢预定", icon: "/static/icon/service_4.png", router: "/pages/boxes/apply-list" },
{ text: "外来人员就餐", icon: "/static/icon/service_1.png", router: "/pages/apply/foreign-list" },
{ text: "跨单位就餐", icon: "/static/icon/service_1.png", router: "/pages/apply/stride-list" },
{ text: "人脸管理", icon: "/static/icon/service_5.png", router: "/pages/human-face/list" },
{ text: "测试登录", icon: "/static/icon/login.png", router: "/pages/test/login" },
],
}
const state = reactive<any>({
wallet: [],
userInfo: {},
});
wallet: [],
userInfo: {},
})
onLoad((options) => {
(window as any).CP2.getAppInfo().then((res) => {
console.log(res);
if (res.code == 1) {
//诗画
console.log("诗画环境");
(window as any).CP2.getUserInfo().then((res) => {
let accountId = res.data.accountId;
let sessionId = res.sessionId;
doLogin(1, null, accountId, sessionId);
});
} else {
//浙政钉或者h5
const url = window.location.href;
const params = new URL(url).searchParams;
const token = params.get("uabaseToken"); // 获取 uabaseToken 参数值
console.log("Token:", token);
if (null != token) {
//浙政钉
doLogin(2, token, null, null);
}
}
});
});
;(window as any).CP2.getAppInfo().then((res) => {
console.log(res)
if (res.code == 1) {
//诗画
console.log("诗画环境")
;(window as any).CP2.getUserInfo().then((res) => {
let accountId = res.data.accountId
let sessionId = res.sessionId
doLogin(1, null, accountId, sessionId)
})
} else {
//浙政钉或者h5
const url = window.location.href
const params = new URL(url).searchParams
const token = params.get("uabaseToken") // 获取 uabaseToken 参数值
console.log("Token:", token)
if (null != token) {
//浙政钉
doLogin(2, token, null, null)
}
}
})
})
onShow(() => {
getUserInfo();
});
getUserInfo()
})
const getUserInfo = () => {
const userInfoKey = constant.storageConstant.USERINFO;
const userStorage = uni.getStorageSync(userInfoKey);
if (!isEmpty(userStorage)) {
state.userInfo = userStorage;
console.log(state.userInfo,"state.userInfo");
}
};
const userInfoKey = constant.storageConstant.USERINFO
const userStorage = uni.getStorageSync(userInfoKey)
if (!isEmpty(userStorage)) {
state.userInfo = userStorage
console.log(state.userInfo, "state.userInfo")
}
}
const doLogin = async (loginType, token, accountId, sessionId) => {
const tokenKey = constant.storageConstant.TOKEN;
const userInfoKey = constant.storageConstant.USERINFO;
const tokenStorage = uni.getStorageSync(tokenKey);
const userStorage = uni.getStorageSync(userInfoKey);
if (isEmpty(tokenStorage) || isEmpty(userStorage)) {
uni.showLoading({
title: "登录中...",
mask: true,
});
let authData;
if (loginType == 1) {
// 诗画
const params = {
accountId: accountId,
sessionId: sessionId,
};
const response = await userApi.authsh(params);
authData = response.data;
} else if (loginType == 2) {
//浙政钉
const response = await userApi.auth(token);
authData = response.data;
} else {
//后期手机号+验证码登录
router.jump("/pages/test/login");
uni.hideLoading();
return;
}
// const {data: authData} = await userApi.auth(token);
uni.setStorageSync(tokenKey, authData);
const { data: userData } = await userApi.userInfo();
uni.setStorageSync(userInfoKey, userData);
getUserInfo();
uni.hideLoading();
uni.showToast({ title: "登录成功", icon: "none" });
}
};
const tokenKey = constant.storageConstant.TOKEN
const userInfoKey = constant.storageConstant.USERINFO
const tokenStorage = uni.getStorageSync(tokenKey)
const userStorage = uni.getStorageSync(userInfoKey)
if (isEmpty(tokenStorage) || isEmpty(userStorage)) {
uni.showLoading({
title: "登录中...",
mask: true,
})
let authData
if (loginType == 1) {
// 诗画
const params = {
accountId: accountId,
sessionId: sessionId,
}
const response = await userApi.authsh(params)
authData = response.data
} else if (loginType == 2) {
//浙政钉
const response = await userApi.auth(token)
authData = response.data
} else {
//后期手机号+验证码登录
router.jump("/pages/test/login")
uni.hideLoading()
return
}
// const {data: authData} = await userApi.auth(token);
uni.setStorageSync(tokenKey, authData)
const { data: userData } = await userApi.userInfo()
uni.setStorageSync(userInfoKey, userData)
getUserInfo()
uni.hideLoading()
uni.showToast({ title: "登录成功", icon: "none" })
}
}
const toService = async (v:any)=>{
const isLogin = await hooks.useCheckLogin();
if(isLogin){
router.jump(v.router)
}
const toService = async (v: any, i: number) => {
if (i == data.serviceList.length - 1) {
router.jump(v.router)
return
}
const isLogin = await hooks.useCheckLogin()
if (isLogin) {
router.jump(v.router)
}
}
const doLogout = async () => {
const isLogin = await hooks.useCheckLogin();
if(!isLogin) return
uni.showLoading({
title: "退出登录中...",
mask: true,
});
const tokenKey = constant.storageConstant.TOKEN;
const userInfoKey = constant.storageConstant.USERINFO;
uni.removeStorageSync(tokenKey);
uni.removeStorageSync(userInfoKey);
state.userInfo = {};
uni.hideLoading();
uni.showToast({ title: "退出登录成功", icon: "none" });
};
const isLogin = await hooks.useCheckLogin()
if (!isLogin) return
uni.showLoading({
title: "退出登录中...",
mask: true,
})
const tokenKey = constant.storageConstant.TOKEN
const userInfoKey = constant.storageConstant.USERINFO
uni.removeStorageSync(tokenKey)
uni.removeStorageSync(userInfoKey)
state.userInfo = {}
uni.hideLoading()
uni.showToast({ title: "退出登录成功", icon: "none" })
}
</script>
<template>
<crud-layout title="我的" bg="/static/page/blue-bg.png">
<view class="flex-row items-center">
<image class="w-100rpx h-100rpx rounded-full" v-if="state.userInfo?.sysUser?.avatar" :src="state.userInfo.sysUser.avatar"
mode="aspectFill" />
<image class="w-100rpx h-100rpx rounded-full" v-else src="/static/icon/default-avatar.png" mode="aspectFill" />
<view class="m-l-30rpx" v-if="state.userInfo?.sysUser?.userId">
<view class="flex-row items-center">
<text class="sub-text-30">{{ state.userInfo?.sysUser?.name }}</text>
<text class="iconfont icon-youjiantou color-#666666 m-l-20rpx"></text>
</view>
<text class="sub-text-26">ID {{ state.userInfo?.sysUser?.userId }}</text>
</view>
<view v-else class="flex-row items-center m-l-30rpx" @tap="doLogin">
<text class="sub-text-32">请先登录</text>
<text class="iconfont icon-youjiantou"></text>
</view>
</view>
<crud-layout title="我的" bg="/static/page/blue-bg.png">
<view class="flex-row items-center">
<image
class="w-100rpx h-100rpx rounded-full"
v-if="state.userInfo?.sysUser?.avatar"
:src="state.userInfo.sysUser.avatar"
mode="aspectFill"
/>
<image class="w-100rpx h-100rpx rounded-full" v-else src="/static/icon/default-avatar.png" mode="aspectFill" />
<view class="m-l-30rpx" v-if="state.userInfo?.sysUser?.userId">
<view class="flex-row items-center">
<text class="sub-text-30">{{ state.userInfo?.sysUser?.name }}</text>
<text class="iconfont icon-youjiantou color-#666666 m-l-20rpx"></text>
</view>
<text class="sub-text-26">ID {{ state.userInfo?.sysUser?.userId }}</text>
</view>
<view v-else class="flex-row items-center m-l-30rpx" @tap="doLogin">
<text class="sub-text-32">请先登录</text>
<text class="iconfont icon-youjiantou"></text>
</view>
</view>
<view class="m-t-40rpx rounded-20rpx shadow-main p-30rpx p-b-40rpx bg-white!">
<view class="service_item flex-row items-center justify-between" v-for="(v, i) in data.serviceList" :key="i"
@tap="toService(v)">
<view class="flex-row-center">
<image class="w-40rpx h-40rpx" :src="v.icon" mode="aspectFill" />
<text class="sub-text-28 m-l-20rpx">{{ v.text }}</text>
</view>
<view class="iconfont icon-youjiantou"></view>
</view>
</view>
<view class="m-t-40rpx rounded-20rpx shadow-main p-30rpx p-b-40rpx bg-white!">
<view
class="service_item flex-row items-center justify-between"
v-for="(v, i) in data.serviceList"
:key="i"
@tap="toService(v, i)"
>
<view class="flex-row-center">
<image class="w-40rpx h-40rpx" :src="v.icon" mode="aspectFill" />
<text class="sub-text-28 m-l-20rpx">{{ v.text }}</text>
</view>
<view class="iconfont icon-youjiantou"></view>
</view>
</view>
<view class="flex-col-center m-t-40rpx m-x-30rpx">
<view class="w-full text-center sub-text-28 p-y-20rpx rounded-40rpx border-solid border border-#0094ff"
@tap="doLogout">
退出登录
</view>
</view>
</crud-layout>
<view class="flex-col-center m-t-40rpx m-x-30rpx">
<view
class="w-full text-center sub-text-28 p-y-20rpx rounded-40rpx border-solid border border-#0094ff"
@tap="doLogout"
>
退出登录
</view>
</view>
</crud-layout>
</template>
<style lang="scss" scoped>
.service_item:not(:last-child) {
padding-bottom: 40rpx;
border-bottom: 1px solid #fafafa;
margin-bottom: 40rpx;
padding-bottom: 40rpx;
border-bottom: 1px solid #fafafa;
margin-bottom: 40rpx;
}
</style>
+61 -51
View File
@@ -1,53 +1,63 @@
import request from "@/sheep/utils/request";
import request from "@/sheep/utils/request"
export default {
page: (data: any) =>
request({
url: `/canteen/canteenDailyMenu/app/page`,
method: "POST",
data,
params:{
pageNo: data.pageNo,
pagesize: data.pagesize
},
custom: {
showLoading: false,
},
}),
classify: (params: any) =>
request({
url: `/canteen/canteenOnlineCategory/app/getAllSimple`,
method: "GET",
params,
custom: {
showLoading: false,
},
}),
product: (params: any) =>
request({
url: `/canteen/canteenOnlineSpu/app/getCanteenSpuList`,
method: "GET",
params,
custom: {
showLoading: false,
},
}),
getShopCar: (params: any) =>
request({
url: `/hard/hard-car/app/mch-detail`,
method: "GET",
params,
custom: {
showLoading: false,
},
}),
updateShopCar: (data: any) =>
request({
url: `/hard/hard-car/app/save-or-upload`,
method: "POST",
data,
custom: {
showLoading: false,
},
}),
};
page: (data: any) =>
request({
url: `/canteen/canteenDailyMenu/app/page`,
method: "POST",
data,
params: {
pageNo: data.pageNo,
pagesize: data.pagesize,
},
custom: {
showLoading: false,
},
}),
classify: (params: any) =>
request({
url: `/canteen/canteenOnlineCategory/app/getAllSimple`,
method: "GET",
params,
custom: {
showLoading: false,
},
}),
product: (params: any) =>
request({
url: `/canteen/canteenOnlineSpu/app/getCanteenSpuList`,
method: "GET",
params,
custom: {
showLoading: false,
},
}),
getShopCar: (params: any) =>
request({
url: `/hard/hard-car/app/mch-detail`,
method: "GET",
params,
custom: {
showLoading: false,
},
}),
updateShopCar: (data: any) =>
request({
url: `/hard/hard-car/app/save-or-upload`,
method: "POST",
data,
custom: {
showLoading: false,
},
}),
checkSpuStock: (params) => {
return request({
url: "/canteen/canteenOnlineSpu/app/checkSpuStock",
method: "GET",
params,
custom: {
showLoading: false,
},
})
},
}
+9 -9
View File
@@ -1,11 +1,11 @@
import request from "@/sheep/utils/request";
import request from "@/sheep/utils/request"
export default {
get: (params) => {
return request({
url: "/canteen/canteenOnlineSpu/app/getCanteenSpuDetails",
method: "GET",
params,
});
},
};
get: (params) => {
return request({
url: "/canteen/canteenOnlineSpu/app/getCanteenSpuDetails",
method: "GET",
params,
})
},
}
+39 -66
View File
@@ -1,68 +1,41 @@
import request from "@/sheep/utils/request";
import request from "@/sheep/utils/request"
export default {
getSupermarketSpuCategory: (params: any) =>
request({
url: `/supermarket/supermarketCategory/app/simpleAll`,
method: "GET",
params,
custom: {
showLoading: false,
},
}),
getSupermarketSpuList: (data: any) =>
request({
url: `/supermarket/supermarketSpu/app/selectListAll`,
method: "POST",
data,
custom: {
showLoading: false,
},
}),
getSupermarketSpuDetail: (params: any) =>
request({
url: `/supermarket/supermarketSpu/app/SpuDetails`,
method: "GET",
params,
custom: {
showLoading: false,
},
}),
// del: (params: any) =>
// request({
// url: `/sys/user/face-delete`,
// method: "DELETE",
// params,
// custom: {
// showLoading: false,
// },
// }),
// create: (data: any) =>
// request({
// url: `/sys/user/face-create`,
// method: "POST",
// data,
// custom: {
// showLoading: false,
// },
// }),
// get: (params: any) =>
// request({
// url: `/sys/user/face-get`,
// method: "GET",
// params,
// custom: {
// showLoading: false,
// },
// }),
// put: (data: any) =>
// request({
// url: `/sys/user/face-update`,
// method: "PUT",
// data,
// custom: {
// showLoading: false,
// },
// }),
};
getSupermarketSpuCategory: (params: any) =>
request({
url: `/supermarket/supermarketCategory/app/simpleAll`,
method: "GET",
params,
custom: {
showLoading: false,
},
}),
getSupermarketSpuList: (data: any) =>
request({
url: `/supermarket/supermarketSpu/app/selectListAll`,
method: "POST",
data,
custom: {
showLoading: false,
},
}),
getSupermarketSpuDetail: (params: any) =>
request({
url: `/supermarket/supermarketSpu/app/SpuDetails`,
method: "GET",
params,
custom: {
showLoading: false,
},
}),
checkSpuStock: (params) => {
return request({
url: "/supermarket/supermarketSpu/app/checkSpuStock",
method: "GET",
params,
custom: {
showLoading: false,
},
})
},
}