This commit is contained in:
zjf committed 2025-10-31 13:17:42 +08:00
commit 20b5569412
31 files changed
+1382 -258

No files matched your search

+4 -4
View File
@@ -20,15 +20,15 @@ VITE_OAUTH2_APP_CLIENT='mini:mini'
# VITE_APP_DEV_SERVICE = https://stgl.pj.gov.cn/api # 域名线上
#VITE_APP_DEV_SERVICE = http://59.202.170.152:81/api # 信创线上
# VITE_APP_DEV_SERVICE = http://192.168.101.174:9999 # 邓-本地
VITE_APP_DEV_SERVICE = http://127.0.0.1:9999 # 邓-本地
; VITE_APP_DEV_SERVICE = http://123.157.250.162:8089/api # 测试线上
# VITE_APP_DEV_SERVICE = http://127.0.0.1:9999 # 邓-本地
VITE_APP_DEV_SERVICE = http://123.157.250.162:8089/api # 测试线上
# --------------------------- 开发环境 图片访问基础地址 ---------------------------
# VITE_IMAGE_DEV_PATH = https://stgl.pj.gov.cn/api # 域名线上
#VITE_IMAGE_DEV_PATH = http://59.202.170.152:81/api # 信创线上
# VITE_IMAGE_DEV_PATH = http://192.168.101.174:9999 # 邓-本地
VITE_IMAGE_DEV_PATH = http://127.0.0.1:9999
; VITE_IMAGE_DEV_PATH = http://123.157.250.162:8089/api # 测试线上
# VITE_IMAGE_DEV_PATH = http://127.0.0.1:9999
VITE_IMAGE_DEV_PATH = http://123.157.250.162:8089/api # 测试线上
# --------------------------- 生产环境 api地址 ----------------------------------
VITE_APP_PROD_SERVICE = https://stgl.pj.gov.cn/api # 域名线上
@@ -0,0 +1,277 @@
<template>
<view class="plate-number-keyboard-box" v-if="showKeyboard" @tap.stop>
<view class="pnk-main" :animation="animationData">
<view class="pnk-main-title">
<text>请点击选择车牌号</text>
<view class="xiala" @tap="_closeKeyboard"></view>
</view>
<view class="pnk-content">
<!-- 省份面板 -->
<view class="province-plane" v-if="showProvince">
<block v-for="(item, index) in provinceList" :key="index">
<view class="line">
<view v-if="index === 'line4'" class='number-item handle-btn' @tap='_clickChangePlane'>ABC
</view>
<view class="number-item"
:class="(cursor === 0 && index === 'line4' && ind >= 2) || (cursor >= 6 && (index !== 'line4' || (index === 'line4' && ind < 2))) ? 'disabled' : ''"
v-if="!((cursor === 0 && index === 'line4' && ind > 7) || (cursor >= 6 && index === 'line4' && ind < 2))"
v-for="(it, ind) in item" :key="ind" :data-text='it'
:data-disabled="(cursor === 0 && index === 'line4' && ind >= 2) || (cursor >= 6 && (index !== 'line4' || (index === 'line4' && ind < 2)))"
@tap='_handleClick'>{{ it }}</view>
<view v-if="index === 'line4'" class='number-item handle-btn delete-btn'
@tap='_handleDelete'></view>
</view>
</block>
</view>
<!-- 字母数字面板 -->
<view class='letter-number-plane' v-if="!showProvince">
<block v-for="(item, index) in letterNumberList" :key="index">
<view class='line'>
<view v-if="index === 'line4' && cursor === 0" class='number-item handle-btn'
@tap='_clickChangePlane'>省</view>
<view v-if="index === 'line4' && cursor > 0" class="number-item handle-btn"
:class="cursor < 6 ? 'disabled' : ''" :data-disabled="cursor < 6"
@tap='_clickChangePlane'>字</view>
<view class="number-item"
:class="index === 'line2' && (it === 'I' || it === 'O') ? 'disabled' : ''"
:data-disabled="index === 'line2' && (it === 'I' || it === 'O')"
v-for="(it, ind) in item" :key="ind" :data-text='it' @tap='_handleClick'>{{ it }}</view>
<view v-if="index === 'line4'" class='number-item handle-btn delete-btn'
@tap='_handleDelete'></view>
</view>
</block>
</view>
</view>
</view>
</view>
</template>
<script>
export default {
props: {
cursor: {
type: Number,
default: 0,
}
},
data() {
return {
showKeyboard: true, // 是否显示键盘
provinceList: {
line1: ['京', '津', '渝', '沪', '冀', '晋', '辽', '吉', '黑', '苏'],
line2: ['浙', '皖', '闽', '赣', '鲁', '豫', '鄂', '湘', '粤', '琼'],
line3: ['川', '贵', '云', '陕', '甘', '青', '蒙', '桂', '宁', '新'],
line4: ['藏', '使', '领', '警', '学', '挂', '港', '澳', '试', '超', '军']
},
letterNumberList: {
line1: [1, 2, 3, 4, 5, 6, 7, 8, 9, '0'],
line2: ['Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P'],
line3: ['A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L'],
line4: ['Z', 'X', 'C', 'V', 'B', 'N', 'M'],
// line5: ['领', '警', '学', '挂', '港', '澳', '试', '超', '军']
},
showProvince: true, // 是否显示省份面板,控制省份面板和字符面板显示
animationData: {} // 键盘动画
};
},
watch: {
cursor(val) {
this.showProvince = (val === 0);
},
},
created() {
this.showProvince = (this.cursor === 0);
},
methods: {
show() {
this.showKeyboard = true;
const animation = uni.createAnimation({
duration: 200,
});
// 立即执行无效,延迟一段时间执行弹出动画
setTimeout(() => {
animation.translateY(0).step();
this.animationData = animation.export();
this.$emit('onShow');
}, 100);
},
hide() {
const animation = uni.createAnimation({
duration: 200
})
setTimeout(() => {
animation.translateY(300).step();
this.animationData = animation.export();
setTimeout(() => {
this.showKeyboard = false;
}, 300);
this.$emit('onHide');
}, 100);
},
/**
* 键盘面板切换操作,控制省份内容和字符内容显示
*/
_clickChangePlane: function (e) {
if (e.currentTarget.dataset.disabled) {
return;
}
this.showProvince = !this.showProvince
},
/**
* 关闭键盘,将键盘隐藏
*/
_closeKeyboard: function (e) {
// 创建动画,执行键盘面板退出动画,动画结束后隐藏整个键盘组件
this.hide();
},
/**
* 键盘主要键点击事件,将点击内容更新到plateNumber
*/
_handleClick: function (e) {
// 如果当前显示的省份面板,当即任意省份后,自动切换到字符面板,同时将结果值的第一个字符修改
if (e.currentTarget.dataset.disabled) {
return;
}
if (this.showProvince) {
this.showProvince = false;
}
if (this.cursor === 7) {
this.hide();
}
this.$emit('onInput', e.currentTarget.dataset.text);
},
_handleCursor: function (e) {
console.log(e.currentTarget.dataset.index)
this.cursor = e.currentTarget.dataset.index;
},
/**
* 删除回退点击事件
*/
_handleDelete: function () {
this.$emit('onDel');
},
},
}
</script>
<style scoped>
button {
padding: 0rpx;
}
.plate-number-keyboard-box {
position: fixed;
bottom: 0rpx;
left: 0rpx;
width: 100%;
height: 432rpx;
overflow: hidden;
background: rgba(255, 255, 255, 0);
z-index: 9999;
}
.pnk-main {
position: absolute;
bottom: 0;
left: 0rpx;
height: 432rpx;
width: 100%;
background: #d0d5d9;
transform: translateY(432rpx);
color: #333333;
}
.pnk-main .pnk-main-title {
font-size: 24rpx;
height: 50rpx;
line-height: 50rpx;
color: #333;
border-bottom: 1rpx solid #ddd;
padding: 0rpx 10rpx;
overflow: hidden;
}
.pnk-main-title text {
float: left;
}
.pnk-main-title .xiala {
position: relative;
float: right;
width: 70rpx;
height: 50rpx;
font-size: 40rpx;
text-align: center;
color: #333;
}
.pnk-content {
padding: 10rpx;
}
.line {
display: flex;
justify-content: space-between;
width: 100%;
margin-bottom: 14rpx;
}
.line:last-of-type {
margin-bottom: 0rpx;
}
.line .number-item {
width: 60rpx;
height: 80rpx;
border-radius: 12rpx;
text-align: center;
line-height: 80rpx;
background: #fff;
font-size: 30rpx;
}
.line .number-item.disabled {
background: #ccc !important;
color: #999 !important;
}
.line .number-item:active {
background: #eee;
}
.line .handle-btn {
width: 80rpx;
background: #acb3bb;
font-size: 28rpx;
}
.xiala::after {
content: '';
position: absolute;
left: calc(50% - 14rpx);
top: calc(50% - 8rpx);
width: 28rpx;
height: 28rpx;
background-size: 100%;
background-repeat: no-repeat;
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAAAsCAMAAADFL88lAAAAclBMVEUAAAADGyYDGyYAADUDGyYAKjMAKysAIiIDGyYDGyYDGyYDGiYDGyYDGyYDGiYDGiUCGiYDGyYDGyYDGyUEGyYDGyYDGiUCGyYDGyUEGyUCGiUAGCYDGyYDGyYCGiUAGCQDHCYDGiUCHCcDHCcDGyYDHSnHUSRaAAAAJHRSTlMA/fsD+AUGB7yzubatqaSdf6+XkYvcwczGhHoK8udqDmVLb1ulZTqDAAACG0lEQVRIx6XV6WLCIAwA4AAF6rGtHvWaO9zg/V9xIVaMomuh+VWRfhBIAUA17bHdW9AwIjSYfVu1DSK69s45v/0CW+5Z+NoRUyto/FE6WfntO4ml3tZXyBx9A62TDqNYJO8dvaAI9wH0QOLOgi30dsEjUUCLLD1WfmPA5HuGvDMicYaHiFdhSXX+/troYZoHgBp/losazIYBU2xSscFRg87z9OY2RWxSNRNnACZn/dSdp0Kj5uIkQ7Q0Ge4ZaiYxVs8CxaEeTNmLUwWWDSTjQC+D1pGGncXUJHnxLy5K/zpApC4T7t0kpmP1kPgGoAZ48+srSXmoVMzwsDjiC0ycZojovTBvknSneuTi8onIPcc8/bCoZqnY7z0vNBvKQEZxdRVT75V1XNCrz8WuDoRfo9jnOe6lYfgchd9zkXtvfH7/Fq1ixS87MfWWzJtTl8Fi86i8VmdP4H50Xo84ScTEY599/yd1Lyp1463RyztGbBRFEA8kMk9grvEQMQADRXm5xkjknnQuz6NuXPxBsdyj0LfiJ4p3nkAv2Y/eveZiIPdedPUio5chLpj4DUZBc/FkPN6KRHEW4XDJV1Y5Ht+ZORN/P4Mn4vGrITtsEOVFxOg8gV4YbrQY93cZvaKshegg5mkoDBXneNauV0OxyC43+rSv3niR6m9V5KUXnOQX18gwdCVVQlTk0X6MFtfeO+dTr3wdT5vjR30alO8fxlZei+kmmyYAAAAASUVORK5CYII=);
}
.delete-btn {
position: relative;
}
.delete-btn::after {
content: '';
position: absolute;
left: calc(50% - 18rpx);
top: calc(50% - 15rpx);
width: 36rpx;
height: 36rpx;
background-size: 100%;
background-repeat: no-repeat;
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAAvCAMAAACIYFl1AAAAk1BMVEUAAAADGyYAHCsAHh8DGyUDHCYDGyYAHykDGycDGyYDGyYAHSYEGiUAHCcDGyYCGyYEGyYAFCoCGyYDGyYCGiYDGyYDGyYEGSYFGyUDGyYDGyUDGiYCGycCGyYCGiYDGiYDGiUAHCgDGyYDGiYEGyQFGiUDGyYDGyYEGyUDGyUAHSQDGyYAGiUDGyUDGyYDGyYDHCi5/q4pAAAAL3RSTlMA+gQI/En3GeFj8RFFItl8QgzN49S+Wjs06LKSd3JrV00q3rg5L8aah1IX6x2splBDDs0AAAJiSURBVEjHrZaNdvIgDIYDxTptrbbz/3/qpvPbRu7/6j4Sh7Ht2VnpGdtRAzzkBQIEHosG2GTKNin94wgqrD4i2mYFcVOBzxgbo/jfFyW/lPJVylhlMAUpEaTsFpsUa2M8lkSP0TjWWPfnfvgvSx/++7uRq/qPji/oGnE123d+KfvRdEF+Hth5jMZgAU2KPqASWANMyLYnSKJbyaOfSgKDZ4HJ8caJVniByA+na+703VP3iWHPvvxDpTDToO8dK7TURB729Z/OUpgKO1x7yhPDIbfW4SlaE+MZtK/oIfbIEtbXCOzncMBY4SLxjuHdosUdaGF3VPMOugyTvcLYid7d2ZGlwWwqs0wNKoM4Al2FUxb9CXe4W/ASmJnXOe9zRXatwBqigvz0ByB7kYxdq8F4DtrvhWMPSVk2WUMWvS0tLYcBjxhpbyw+mC3Br5ZET3R5cV9vQp+6QDJo9PilslUclyRascBH+hQzneX6jVlFC1CFt9x0qUZUDh3L835bcgdZeoFhEJPoIhfWd6HIId/EWpxKB4GXJBo79wNRCSpjFQ+wBV2HeyxtJaJLR2H9fZWYNVk1OEOlaDvzOku+CurktoII+EPPMud9qznDB6/2OAla7fb7XI8wcwqKMIlt0za25VRtQk+VnGcaOPQ8+1uHhC9b3CRyh/Va3GHOvmZ8EV3Db09uQhL+5eGAe5uqvnhO+6AXQ7ZmQXN6zoPfKra3TriiByf4lSTkSDaOgt5niUfODLKwzEAWhHOS8ykkJxHhRXg2JK5nNjgPE5qi9JbgBWSAksZN2uSekvWOG2a9yxnAf+zPn543ZzPrAAAAAElFTkSuQmCC);
}
</style>
@@ -0,0 +1,312 @@
<template>
<view class="plate-number">
<view class="plate-number-box" :class="plate[7] && 'green'" @tap.stop>
<template v-for="(item, index) in 8" :key="`plate-${index}`">
<text v-if="index == 2" class="fz-000000 fz-weight fz-32">·</text>
<label :id="'input' + index"
:class="[(index == cursor && focus ? 'focus' : '') + (index === 7 && !plate[index] ? ' xinnengyuan' : ''), 'bglabel']"
:data-index="index" @tap='_handleCursor'>
{{ plate[index] || '' }}
</label>
</template>
</view>
<plate-keyboard ref='plateKeyboard' :cursor="cursor" @onInput="_keyboardInput" @onDel="_keyboardDel"
@onShow="plateKeyboardOnShow" @onHide="plateKeyboardOnHide" />
</view>
</template>
<script>
import PlateKeyboard from './plate-keyboard.vue';
export default {
components: {
PlateKeyboard
},
data() {
return {
plate: new Array(8),
cursor: 0,
focus: false,
}
},
created() {
// 根据IP获取到当前省份
this.getLocationByIP();
},
methods: {
setPlate: function (plateNumber) {
const plate = plateNumber.split('');
while (plate.length < 8) {
plate.push('');
}
this.plate = plate;
this.cursor = plateNumber.length;
this.focus = false;
},
getPlate: function () {
return this.plate.join('');
},
onInput: function (text) {
// 车牌号最多8位,大多数7位,新能源8位,控制不能超过8位数
if (this.cursor < 8) {
this.plate.splice(this.cursor++, 1, text);
}
},
onDel: function () {
if (this.plate[this.cursor]) {
this.plate.splice(this.cursor, 1, '');
} else if (this.cursor > 0) {
this.plate.splice(--this.cursor, 1, '');
}
},
_handleCursor: function (e) {
let index = parseInt(e.currentTarget.dataset.index);
for (; index > 0 && !this.plate[index] && !this.plate[index - 1]; index--);
this.cursor = index;
this.focus = true;
this.$refs.plateKeyboard.show();
},
hidePlateKeyboard() {
this.$refs.plateKeyboard.hide();
},
_keyboardInput(val) {
console.log('===input:' + val);
this.onInput(val);
},
_keyboardDel() {
console.log('===del')
this.onDel();
},
plateKeyboardOnShow() {
this.$emit('plateKeyboardOnShow');
},
plateKeyboardOnHide() {
this.focus = false;
this.$emit('plateKeyboardOnHide');
},
async getLocationByIP() {
if (getApp().globalData.provinceShort) {
this.setPlate(getApp().globalData.provinceShort);
} else {
if (!this.$Request) {
return;
}
const res = await this.$Request(this.$Api.general.park.getLocationByIP, {});
if (res.success) {
var prov = res.data.province;
if (prov) {
let provShort = this.provinceForShort(prov);
if (provShort) {
getApp().globalData.provinceShort = provShort;
if (!this.getPlate()) {
this.setPlate(provShort);
}
}
}
}
}
},
provinceForShort(province) {
if (province == "北京市")
return "京";
else if (province == "天津市")
return "津";
else if (province == "重庆市")
return "渝";
else if (province == "上海市")
return "沪";
else if (province == "河北省")
return "冀";
else if (province == "山西省")
return "晋";
else if (province == "辽宁省")
return "辽";
else if (province == "吉林省")
return "吉";
else if (province == "黑龙江省")
return "黑";
else if (province == "江苏省")
return "苏";
else if (province == "浙江省")
return "浙";
else if (province == "安徽省")
return "皖";
else if (province == "福建省")
return "闽";
else if (province == "江西省")
return "赣";
else if (province == "山东省")
return "鲁";
else if (province == "河南省")
return "豫";
else if (province == "湖北省")
return "鄂";
else if (province == "湖南省")
return "湘";
else if (province == "广东省")
return "粤";
else if (province == "海南省")
return "琼";
else if (province == "四川省")
return "川";
else if (province == "贵州省")
return "贵";
else if (province == "云南省")
return "云";
else if (province == "陕西省")
return "陕";
else if (province == "甘肃省")
return "甘";
else if (province == "青海省")
return "青";
else if (province == "台湾省")
return "台";
else if (province == "内蒙古自治区")
return "蒙";
else if (province == "广西壮族自治区")
return "桂";
else if (province == "宁夏回族自治区")
return "宁";
else if (province == "新疆维吾尔自治区 ")
return "新";
else if (province == "西藏自治区")
return "藏";
else if (province == "香港特别行政区")
return "港";
else if (province == "澳门特别行政区")
return "澳";
else return '';
}
},
}
</script>
<style scoped>
.plate-number {
/* display: flex; */
/* justify-content: spa; */
}
.plate-number-box {
position: relative;
/* height: 50rpx; */
padding: 24rpx 0;
box-sizing: content-box;
margin: auto;
border-radius: 10rpx;
transition: all 1s;
display: flex;
justify-content: space-between;
align-items: center;
}
/* .plate-number-box::before {
content: '';
border: 2rpx solid #fff;
position: absolute;
top: 5rpx;
left: 5rpx;
border-radius: 10rpx;
height: 86rpx;
} */
.bglabel {
background-color: #F2F5FA;
}
/* .plate-number-box::after {
content: '·';
position: absolute;
left: 122rpx;
top: 30rpx;
color: #fff;
font-size: 50rpx;
line-height: 40rpx;
} */
.plate-number-box.green {
/* background: linear-gradient(#f8f8f8 20%, #16b235); */
box-shadow: 0 0 4rpx 0 #ebebeb;
}
/* .plate-number-box.green::before {
border: 2rpx solid #000;
} */
/* .plate-number-box.green::after {
content: '';
position: absolute;
left: 120rpx;
top: 36rpx;
width: 26rpx;
height: 26rpx;
background-size: 100%;
background-repeat: no-repeat;
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAF6UExURUdwTH3EyJfLtffOk9mwguW7jHvftPvQofnPoVlKOYzOo4jMs5HPp5fIvqbYnOhFQfrCkJG6pIbLw4vUzYjKv5vjs9q8JvLPn4bJpJHqtYrNqnStopDh3oLJwViHcIjLv3q2qozMvmRYSYfLpovMqm6ggum9j+PMnqDSuqTQuOjOpKPOoGaZfnGqi+DCKXooHt20hqWvqW9fEMt9bKG7p8LDXnuSiKS4o////zqp4Deo4jCuMS2tLfTz8+vr7OUUW9rZ22JbUvv8/Uuy2zGxMUO2SiaZJ9DhLjdEOkpPTckubzSj2lu62VO+YDJERD9NR/f290JFQbe1tV3BbuLg4d/c3bXXLzIxJ1dVUj1OVnHF0W3IhMXCwSlMXhlAGiZOJ8zhL0ZiUVdfW7mGmU1aVqakpZiWlyNGV2mnp4KBgkpybhg9UTKh2bbWKmlnZLDcfbrcMWeofoOMGaa3IzpLVFx3GcngQ9wzaa/chHoJLK0NRGEiRWppaD1PV9NqRZ0AAAA4dFJOUwATR/X49w/6+v3hs3ByFPT09uNHzUf0+Swpy+YuL+Xw1f398P3c9/f98Pu6yMb0+vjc+vi5+NyxP0I0ygAAAWxJREFUKM910ldXwjAYBuC0KgqlVeZhKEPAhXuGKYIIKKscNsjGvff87yZNOdYLn8v3bZKTLwWAoGjGardbGQ0FpChWXU1GkOSZjZVUU9pqxOvxeL0e72X8Qjs5yDXqJMqI4/p52qwRv1dHBjEq8jCXNgtrKG1SyI+wq9MChPW0TYYKtnuIfaa/K5XKUyIIITzpsWjB2usu8v4VDGBZlMNsYpsDdBfnH3wgLICCfocGmxvjyEpJTpTxVjCU2QHzs0PYiEgewkWgpwJzC2PYqGg6RA75pwiXVWirCWRmdZggWwXRCveWXq9fXi/tETfi4W5Av8VardgjH4QS/bYOcIqXGPJQllwQdlwcAMrb62KxePdcimPCSHIZJZqVzJTy+3y+/QNBAw0xn1nEQwQGRVRoMF8zDwsJi5E8CK1I+XGF+JuhPm/RDZ7QYKpF/USDby8Zfx9dplTUUlEkde9Syv78JxzNOJxOB6PjxOAHI6di/dhTK/cAAAAASUVORK5CYII=);
} */
.plate-number-box.green label {
color: #222;
text-shadow: 0 0 2rpx #fff;
}
.plate-number-box label {
width: 60rpx;
position: relative;
vertical-align: middle;
display: inline-block;
/* width: 8%; */
color: #fff;
text-align: center;
font-size: 50rpx;
height: 86rpx;
line-height: 86rpx;
text-shadow: 0rpx 0rpx 2rpx #000;
/* margin: 0 10rpx; */
}
.plate-number-box label:nth-child(2) {
/* margin-right: 20rpx; */
}
.plate-number-box label:last-child {
content: "\e63f";
}
/* .plate-number-box label.focus::after {
content: '';
position: absolute;
bottom: -6rpx;
left: 5%;
width: 90%;
height: 4rpx;
background: #fff;
animation: cursor 1s ease-in infinite;
} */
/* .plate-number-box label.xinnengyuan::before {
content: '';
position: absolute;
left: 12%;
top: 12%;
width: 76%;
height: 76%;
background-size: 100%;
background-repeat: no-repeat;
} */
@keyframes cursor {
from {
opacity: 1;
}
to {
opacity: 0.2;
}
}
</style>
+7
View File
@@ -198,6 +198,13 @@
"navigationBarTitleText": "商品分类管理",
"disableScroll": true
}
},
{
"path": "settlement",
"style": {
"navigationBarTitleText": "结算",
"disableScroll": true
}
}
]
},
+2 -2
View File
@@ -55,7 +55,7 @@ const getDetail = async () => {
// 获取购物车信息
const getFoodCart = async () => {
const { data } = await foodApi.getShopCar({ merchantId: state.detailId, hardType: "CANTEEN" })
const { data } = await orderApi.getShopCar({ merchantId: state.detailId, hardType: "CANTEEN" })
state.cartList = data ?? []
if (isEmpty(state.cartList)) {
state.foodList.forEach((v: any) => {
@@ -154,7 +154,7 @@ const updateFoodCart = async () => {
})
if (isEmpty(list)) return
const _pa = { merchantId: state.detailId, list: list, hardType: "CANTEEN" }
const { data, code }: any = await foodApi.updateShopCar(_pa)
const { data, code }: any = await orderApi.updateShopCar(_pa)
if (code == 0) await getFoodCart()
}
+11 -1
View File
@@ -1,6 +1,7 @@
<script setup lang="ts">
import { ref, reactive } from "vue"
import { onLoad, onShow } from "@dcloudio/uni-app"
import PlateNum from '@/components/tongtong-plate/plate-number.vue';
import { hooks, router } from "@@/index"
import { userCarApi } from "@@/api"
@@ -9,6 +10,7 @@ import { serviceEnum } from "@/sheep/constant"
const { carTypeEnum } = serviceEnum
const applyFormRef = ref<any>(null)
const plateNumRef = ref<any>(null)
const visitorReasonList = ref<Array<{ title: string; id: string }>>(Object.keys(carTypeEnum).map((key) => ({
title: carTypeEnum[key],
@@ -49,6 +51,14 @@ const carNumberPickerConfirm = (e) => {
}
const doSubmit = async () => {
// 车牌号
const carNumber = plateNumRef.value.getPlate()
if (!carNumber) {
uni.showToast({ title: "请输入车牌号", icon: "none" })
return
}
applyForm.carNumber = carNumber
// 校验
applyFormRef.value.submit()
const { isPass } = applyFormRef.value.validate()
if (!isPass) return
@@ -81,7 +91,7 @@ const doSubmit = async () => {
</tm-picker>
</tm-form-item>
<tm-form-item name="carNumber" label="车牌号" :showBottom="false">
<tm-input v-model="applyForm.carNumber" placeholder="请输入" show-char-count></tm-input>
<PlateNum ref="plateNumRef" />
</tm-form-item>
</tm-form>
</view>
@@ -4,15 +4,9 @@
<tm-sheet :margin="[0]">
<tm-alert :border="['2']" :borderColor="['#89c0ff']" :darkBorderColor="['#3b5066']">访客邀约请填写以下信息</tm-alert>
<tm-form @submit="onsubmit" v-model="form" :rules="rules" direction="vertical">
<!-- <tm-form-item name="name" label="访客姓名">
<tm-input color="transparent" v-model.trim="form.name" placeholder="请输入真实姓名" />
</tm-form-item> -->
<tm-form-item name="mobile" label="访客手机号">
<tm-input color="transparent" v-model.trim="form.mobile" placeholder="请输入访客手机号" />
</tm-form-item>
<!-- <tm-form-item name="companyName" label="来访公司名称">
<tm-input color="transparent" v-model.trim="form.companyName" placeholder="来访公司名称" />
</tm-form-item> -->
<tm-form-item name="vStartDate" label="拜访开始时间 >" @click="startDatePicker('vStartDate')">
<template #label>
<span>拜访开始时间 </span>
@@ -39,30 +33,6 @@
color="transparent"
/>
</tm-form-item>
<!-- <tm-form-item name="deptId" label="拜访部门">
<tm-drawer :show-close="true" :show-footer="true" size="auto" title="请选择拜访部门">
<template #trigger>
<tm-input :modelValue="form.deptId" placeholder="请选择拜访部门" style="pointer-events: none" disabled color="transparent">
<template v-slot:rightIcon>
<tm-icon name="arrow-right-s-line"></tm-icon>
</template>
</tm-input>
</template>
<tm-text color="#999999"></tm-text>
</tm-drawer>
</tm-form-item> -->
<!-- <tm-form-item name="reasonValue" label="拜访原因">
<tm-picker @confirm="reasonPickerConfirm" :list="visitorReasonList">
<tm-input :modelValue="form.reasonText" placeholder="请选择拜访原因" style="pointer-events: none" disabled color="transparent">
<template #rightIcon>
<tm-icon name="arrow-right-s-line"></tm-icon>
</template>
</tm-input>
</tm-picker>
</tm-form-item> -->
<tm-form-item name="isOtherPeople" label="是否可随行人员" direction="horizontal" labelWidth="500">
<tm-switch v-model="form.isOtherPeople" />
</tm-form-item>
@@ -133,49 +103,20 @@ interface FormData {
}
const form = reactive<FormData>({
// name: "",
mobile: "",
// companyName: "",
vStartDate: "",
vEndDate: "",
// reasonValue: "",
// reasonText: "",
isOtherPeople: true,
isDrive: true,
})
const showDatePicker = ref(false)
const curDateType = ref("")
const confirmModalShow = ref(false)
// const visitorReasonList = ref<{ title: string; id: string }[]>([])
// onLoad(() => {
// getVisitorReasonList()
// })
// 获取拜访原因列表
// const getVisitorReasonList = async () => {
// const { code, data } = await invitationApi.getDictListByType("1")
// if (code == 0 && data) {
// visitorReasonList.value = data.map((item: any) => ({ title: item.label, id: item.value }))
// return
// }
// visitorReasonList.value = []
// }
// 来访原因选择器确认
// const reasonPickerConfirm = (ids: string) => {
// const reason = visitorReasonList.value.find((item) => item.id === ids[0])
// form.reasonText = reason?.title || ""
// form.reasonValue = reason?.id || ""
// }
const rules: Record<string, TM.FORM_RULE[]> = {
// name: [{ required: true, message: "姓名2到5个字符", rule: /^.{2,5}$/ }],
mobile: [{ required: true, message: "请输入正确的手机号", rule: /^(?:(?:\+|00)86)?1\d{10}$/ }],
// companyName: [{ required: true, message: "请输入来访公司名称" }],
vStartDate: [{ required: true, message: "请选择拜访开始时间" }],
vEndDate: [{ required: true, message: "请选择拜访离开时间" }],
// reasonValue: [{ required: true, message: "请选择拜访原因" }],
}
// 开始日期选择
@@ -237,13 +178,9 @@ const confirmSubmit = async () => {
// 重置表单
const resetForm = () => {
Object.assign(form, {
// name: "",
mobile: "",
// companyName: "",
vstartDate: "",
vendDate: "",
// reasonValue: "",
// reasonText: "",
isOtherPeople: true,
isDrive: true,
})
+3 -3
View File
@@ -138,7 +138,7 @@ import { reactive, nextTick } from "vue"
import { onLoad, onShow } from "@dcloudio/uni-app"
import { isEmpty } from "radash"
import { hooks, router } from "@@/index"
import { foodApi, merchantApi, payApi, shopApi } from "@@/api"
import { foodApi, merchantApi, orderApi, payApi, shopApi } from "@@/api"
import { filePath } from "@@/utils/file"
// import ProductRow from "./components/product-row.vue"
// import ProductCart from "./components/product-cart.vue"
@@ -196,7 +196,7 @@ const getDetail = async () => {
}
const getShopCart = async () => {
const { data } = await foodApi.getShopCar({ merchantId: state.merchantId, hardType: "SUPERMARKET" })
const { data } = await orderApi.getShopCar({ merchantId: state.merchantId, hardType: "SUPERMARKET" })
state.cartList = data ?? []
state.totPrice = state.cartList.map((v) => Number(v.totalPrice)).reduce((pre, next) => pre + next, 0)
state.totCount = state.cartList.map((v) => v.productCount).reduce((pre, next) => pre + next, 0)
@@ -293,7 +293,7 @@ const updateShopCart = async () => {
productId: v.productId,
}
})
const { code }: any = await foodApi.updateShopCar({ merchantId: state.merchantId, list, hardType: "SUPERMARKET" })
const { code }: any = await orderApi.updateShopCar({ merchantId: state.merchantId, list, hardType: "SUPERMARKET" })
if (code == 0) await getShopCart()
}
+4 -4
View File
@@ -44,15 +44,17 @@
<script setup lang="ts">
import ProductTotal from "./components/product-total.vue"
import { isEmpty } from "radash"
import { useCartStore } from "@/sheep/stores/cart"
import { useOnlineCartStore } from "@/sheep/stores/online-cart"
import { payApi } from "@/sheep/api"
import { hooks, router } from "@/sheep"
import { filePath } from "@/sheep/utils/file"
const cartStore = useCartStore()
const cartStore = useOnlineCartStore()
// 点击提交订单
const doBuy = async () => {
if (isEmpty(cartStore.cart)) return uni.showToast({ title: "请至少购买一件商品", icon: "none" })
uni.showModal({
title: "提示",
content: "是否确定购买",
@@ -67,8 +69,6 @@ const doBuy = async () => {
// 提交订单
const submitPay = async () => {
try {
if (isEmpty(cartStore.cart)) return uni.showToast({ title: "请至少购买一件商品", icon: "none" })
uni.showLoading({ title: "支付中...", mask: false })
const { code, msg }: any = await payApi.createSupermarketOrder({ carParentId: cartStore.cart[0].parentCarId, hardType: "SUPERMARKET" })
if (code != 0) throw new Error(msg)
+2 -2
View File
@@ -4,11 +4,11 @@ import { onLoad } from "@dcloudio/uni-app"
import { onlineShopApi } from "@@/api"
import { filePath } from "@/sheep/utils/file"
import ProductSku from "./components/product-sku.vue"
import { useCartStore } from "@/sheep/stores/cart"
import { useOnlineCartStore } from "@/sheep/stores/online-cart"
import { hooks, router } from "@/sheep"
import { displayPrice } from "@/sheep/utils/formatMoney"
const cartStore = useCartStore()
const cartStore = useOnlineCartStore()
const state = reactive<any>({
detailId: null,
@@ -0,0 +1,57 @@
<template>
<tm-drawer z-index="800" v-model:show="modelValue" showClose title="购物车" size="80%" bgColor="#ECF4F9" @close="doClose">
<view v-if="totPrice > 0" class="flex-row card px-15rpx py-25rpx mb-20rpx" v-for="(v, i) in cartList" :key="i">
<image class="size-160rpx rounded-12rpx mr-20rpx bg-#eeeeee" :src="filePath(v.productPic)" mode="aspectFill" />
<view class="flex-1 overflow-hidden">
<view class="sub-text-30 truncate">{{ v.productName }}</view>
<view class="second-text-26 show-line">单位 {{ v.unit }}</view>
<view class="flex-row justify-between items-center">
<view class="second-text-30"> ¥{{ hooks.useMoney(v.productPrice) }} </view>
<view class="second-text-30">x {{ v.productCount }}</view>
</view>
<view class="flex-row justify-between items-center">
<view class="second-text-32 c-red!">¥{{ hooks.useMoney(v.totalPrice) }}</view>
<view class="flex-row justify-between items-center">
<view
class="flex-row-center size-40rpx rounded-full border-1px border-solid border-color-#0094ff"
@tap.stop="removeProductCount(v.productId, merchantId)"
>
<view class="iconfont icon-a--" />
</view>
<view class="sub-text-28 m-x-20rpx">{{ v.productCount }}</view>
<view class="flex-row-center size-40rpx rounded-full bg-#0094ff" @tap.stop="addProductCount(v.productId, merchantId)">
<view class="font-size-40rpx color-white iconfont icon-a-" />
</view>
</view>
</view>
</view>
</view>
<view v-else class="flex-col justify-center items-center pt-300rpx">
<image src="@/static/page/cart_empty.png" class="size-350rpx" />
<text class="sub-text-32 font-bold">购物车空空如也</text>
</view>
<view class="h-100rpx"></view>
</tm-drawer>
</template>
<script setup lang="ts">
import { filePath } from "@/sheep/utils/file"
import useCartBuy from "../use-cart-buy"
import { hooks } from "@/sheep"
const modelValue = defineModel("modelValue", { default: false })
const props = defineProps({
merchantId: {
type: String,
default: null,
},
})
const { totPrice, cartList, addProductCount, removeProductCount } = useCartBuy()
// 关闭
const doClose = () => {
modelValue.value = false
}
</script>
@@ -0,0 +1,157 @@
<template>
<tm-drawer z-index="2000" v-model:show="modelValue" showFooter showClose title="加入购物车" size="70%" bgColor="#ECF4F9" @close="doClose">
<view class="flex-col card py-20rpx px-30rpx mb-20rpx">
<view class="prefix_title mb-15rpx">商品信息</view>
<view class="flex items-center">
<image
class="size-160rpx bg-#eeeeee rounded-1 mr-2"
:src="filePath(goodsInfo.coverImage)"
mode="aspectFill"
@tap.stop="goodsInfo.coverImage && usePreviewImage(filePath(goodsInfo.coverImage))"
/>
<view class="flex-1 overflow-hidden">
<view class="sub-text-32 truncate">{{ goodsInfo.title }}</view>
<view class="second-text-32 c-red!">
<text>¥{{ displayPrice(state.selectedSku.price, "") || 0 }}</text>
<!-- <text class="m-l-20rpx second-text-28 line-through">¥{{ displayPrice(state.selectedSku.retailPrice, "") || 0 }}</text> -->
</view>
<view class="second-text-26">库存 {{ state.selectedSku.stock || 0 }}</view>
<view class="second-text-26">
已选择
{{
`${state.selectedSku.skuName || "未选择"} / ${
state.selectedSku.weightG ? state.selectedSku.weightG + "克" : "未选择"
},数量 ${state.goodsCount}`
}}
</view>
</view>
</view>
</view>
<!-- 规格 -->
<view class="flex-col card p-y-20rpx p-x-30rpx mb-20rpx">
<view class="prefix_title mb-15rpx">规格</view>
<view class="flex flex-wrap">
<view
class="text-center rounded-2 mr-4 mb-2 py-2 px-3 text-28 bg-#F2F2F2 c-black border border-solid border-#E5E5E5"
:class="{ 'bg-#0094ff! c-white!': state.selectedSku.id === v.id }"
v-for="v in goodsInfo.skuList"
:key="v.id"
@click="onSelectSku(v)"
>
{{ (v.skuName || "") + " / " + (v.skuWeight || "") + "克" }}
</view>
</view>
</view>
<!-- 购买数量 -->
<view class="flex-col card p-y-20rpx p-x-30rpx mb-20rpx">
<view class="prefix_title mb-15rpx">购买数量</view>
<view class="flex items-center justify-between">
<view class="second-text-28">库存 {{ state.selectedSku.stock }}</view>
<view class="flex">
<view class="flex-row-center size-40rpx rounded-full border-1px border-solid border-color-#0094ff" @tap.stop="handleNumRemove">
<view class="iconfont icon-a--" />
</view>
<view class="sub-text-28 m-x-20rpx">{{ String(state.goodsCount).padStart(2, "0") }}</view>
<view class="flex-row-center size-40rpx rounded-full bg-#0094ff" @tap.stop="handleNumAdd">
<view class="font-size-40rpx color-white iconfont icon-a-" />
</view>
</view>
</view>
</view>
<template #footer>
<button
class="mx-12 rounded-3 bg-#0094ff sub-text-30 py-2 c-white! active:bg-#0094ff99"
:class="{ 'bg-#0094ff77!': Object.keys(state.selectedSku).length === 0 }"
@tap="handleSkuAddToCart"
>
加入购物车
</button>
</template>
</tm-drawer>
</template>
<script setup lang="ts">
import { reactive, watch } from "vue"
import { filePath } from "@/sheep/utils/file"
import { usePreviewImage } from "@/sheep/hooks/use-img"
import { isEmpty } from "radash"
import { displayPrice } from "@/sheep/utils/formatMoney"
const modelValue = defineModel("modelValue", { default: false })
const emit = defineEmits<{ (e: "onSkuAddToCart", val: object): void }>()
const props = defineProps({
goodsInfo: {
type: Object,
default: () => ({}),
},
})
const state = reactive<any>({
selectedSku: {},
goodsCount: 1,
})
watch(modelValue, (newValue) => {
if (newValue && !isEmpty(props.goodsInfo)) {
state.selectedSku = props.goodsInfo.skuList[0]
}
})
// 选择规格/取消选择规格
const onSelectSku = (skuItem: any) => {
if (state.selectedSku.id == skuItem.id) {
state.selectedSku = {}
return
}
state.selectedSku = skuItem
}
// 加数量
const handleNumAdd = () => {
if (state.goodsCount >= state.selectedSku.stock) return uni.showToast({ title: "库存不足", icon: "none" })
state.goodsCount += 1
}
// 减数量
const handleNumRemove = () => {
if (state.goodsCount <= 1) return
state.goodsCount -= 1
}
// 点击sku加入到购物车
const handleSkuAddToCart = async () => {
if (!state.selectedSku.id) return uni.showToast({ title: "请选择包装单位", icon: "none" })
// 检查库存
// try {
// uni.showLoading({ title: "加载中", mask: true })
// const { code }: any = await shopApi.checkSpuStock({ spuId: props.goodsInfo.id, incrCount: state.goodsCount })
// if (code != 0) return uni.showToast({ title: "库存不足", icon: "none" })
// } catch (e) {
// console.error(e)
// } finally {
// uni.hideLoading()
// }
// 检查库存
if (props.goodsInfo.stock === 0 || Number(state.goodsCount) > Number(state.selectedSku.stock))
return uni.showToast({ title: "库存不足", icon: "none" })
emit("onSkuAddToCart", {
spuId: props.goodsInfo.id,
skuId: state.selectedSku.id,
productCount: state.goodsCount,
...state.selectedSku,
})
}
// 关闭
const doClose = () => {
modelValue.value = false
state.selectedSku = {}
state.goodsCount = 1
}
</script>
@@ -0,0 +1,38 @@
<template>
<view class="flex-row items-center justify-between" @tap="doContainer">
<view class="flex-row-center">
<tm-badge bg-color="red" :label="totalCount" :offset="[8, 8]">
<image class="size-100rpx" src="/static/icon/cart.png" />
</tm-badge>
<view class="flex-col m-l-30rpx">
<view class="second-text-36 color-#f63a36!">¥ {{ hooks.useMoney(props.totPrice) }}</view>
<view class="sub-text-28">共{{ props.totCount }}件</view>
</view>
</view>
<view class="bg-#0094ff p-x-60rpx p-y-20rpx text-center rounded-40rpx sub-text-30 color-white!" @tap.stop="goSettlement">去结算</view>
</view>
</template>
<script setup lang="ts">
import { hooks } from "@@/index"
import useCartBuy from "../use-cart-buy"
const emit = defineEmits(["submit", "doContainer"])
const props = withDefaults(
defineProps<{
totPrice: number
totCount: number
}>(),
{
totPrice: 0,
totCount: 0,
},
)
const { totalCount } = useCartBuy()
const goSettlement = () => emit("submit")
const doContainer = () => emit("doContainer")
</script>
+62 -23
View File
@@ -1,41 +1,45 @@
<template>
<crud-layout mainIsScroll bg="/static/page/blue-bg.png">
<image :src="filePath(state.detail.coverImage)" class="w-full h-500rpx bg-#eeeeee" mode="aspectFill" />
<view v-if="state.detail.id" class="card p-20rpx mt-10rpx mx-20rpx bg-gradient-to-b from-#0094ff33 to-#fff">
<view v-if="state.detail.priceType == '2'" class="w-full flex justify-between items-center">
<image :src="filePath(goodsDetail.coverImage)" class="w-full h-500rpx bg-#eeeeee" mode="aspectFill" />
<view v-if="goodsDetail.id" class="card p-20rpx mt-10rpx mx-20rpx bg-gradient-to-b from-#0094ff33 to-#fff">
<view v-if="goodsDetail.priceType == '2'" class="w-full flex justify-between items-center">
<view class="flex items-center">
<text class="second-text-34 line-through">¥</text>
<text class="font-bold second-text-40 line-through mr-2">{{ displayPrice(state.detail.originalPrice, "") }}</text>
<text class="font-bold second-text-40 line-through mr-2">{{ displayPrice(goodsDetail.originalPrice, "") }}</text>
<text class="text-34rpx color-red!">¥</text>
<text class="text-46rpx font-bold color-red!">{{ displayPrice(state.detail.spuPrice, "") }}</text>
<text class="text-46rpx font-bold color-red!">{{ displayPrice(goodsDetail.spuPrice, "") }}</text>
<text class="view-clip px-2 text-22rpx c-white bg-red ml-2 rounded-r-1 rounded-lt-1">到手价</text>
</view>
<view class="main-text-32 c-red! font-italic">
<text>约省</text>
<text class="text-44rpx mx-1">{{ displayPrice(Number(state.detail.originalPrice) - Number(state.detail.spuPrice), "") }}</text>
<text class="text-44rpx mx-1">{{ displayPrice(Number(goodsDetail.originalPrice) - Number(goodsDetail.spuPrice), "") }}</text>
<text>元</text>
</view>
</view>
<view v-else class="w-full flex justify-start items-center">
<text class="text-34rpx color-red!">¥</text>
<text class="text-46rpx font-bold color-red!">{{ displayPrice(state.detail.skuList[0].price, "") }}</text>
<text class="text-46rpx font-bold color-red!">{{ displayPrice(goodsDetail.skuList[0].price, "") }}</text>
<text class="view-clip px-2 text-22rpx c-white bg-red ml-2 rounded-r-1 rounded-lt-1">到手价</text>
</view>
<view class="w-full sub-text-36 flex items-center mt-1">
<text class="c-#0094ff! sub-text-24 px-2 bg-#0094ff22 rounded-1 mr-2 whitespace-nowrap">
{{ state.detail.status == "0" ? "在售" : "未售" }}
{{ goodsDetail.status == "0" ? "在售" : "未售" }}
</text>
<text class="truncate">{{ state.detail.title }}</text>
<text class="truncate">{{ goodsDetail.title }}</text>
</view>
<view class="second-text-26 mt-1">售卖时间 {{ state.detail.saleStartTime }} ~ {{ state.detail.saleEndTime }}</view>
<view class="second-text-26 truncate mt-1">
<text class="mr-20rpx">售卖单位 {{ state.detail.unit }}</text>
<text class="mr-20rpx">剩余库存 {{ state.detail.totalStock }}</text>
<view class="second-text-26 mt-1">售卖时间 {{ goodsDetail.saleStartTime }} ~ {{ goodsDetail.saleEndTime }}</view>
<view class="second-text-26 truncate mt-1 flex justify-between items-center">
<view>
<text class="mr-20rpx">售卖单位 {{ goodsDetail.unit }}</text>
<text class="mr-20rpx">剩余库存 {{ goodsDetail.totalStock }}</text>
</view>
<view class="second-text-24 px-2 py-8rpx bg-#0094ff c-white! border-rd-8" @tap.stop="state.showSkuDialog = true">加入购物车</view>
</view>
</view>
<view v-if="state.detail.priceType == '1'" class="mt-20rpx card p-20rpx mx-20rpx">
<view class="prefix_title">包装单位</view>
<view class="bg-#1879ff09 rounded-1 my-2 p-3" v-for="(item, index) in state.detail.skuList" :key="index">
<view v-if="goodsDetail.priceType == '1'" class="mt-20rpx card p-20rpx mx-20rpx">
<view class="prefix_title">规格</view>
<view class="bg-#1879ff09 rounded-1 my-2 p-3" v-for="(item, index) in goodsDetail.skuList" :key="index">
<view class="sub-text-30">{{ item.skuName }}</view>
<view class="second-text-28 mt-1 flex justify-between items-center">
<text>售价 {{ displayPrice(item.price) }}</text>
@@ -44,37 +48,72 @@
</view>
</view>
</view>
<view class="mt-20rpx card p-20rpx mx-20rpx">
<view class="prefix_title">商品描述</view>
<view class="mt-20rpx overflow-auto text-26rpx!" v-html="state.detail.description"></view>
<view class="mt-20rpx overflow-auto text-26rpx!" v-html="goodsDetail.description"></view>
</view>
<view class="h-30rpx" />
<template #main-bottom>
<view class="z-200 p-x-30rpx p-y-30rpx bg-white">
<product-total :totPrice="totPrice" :totCount="totalCount" @doContainer="doCart" @submit="goSettlement" />
</view>
</template>
</crud-layout>
<product-sku v-model="state.showSkuDialog" :goodsInfo="state.goodsDetail" @onSkuAddToCart="onSkuAddToCart" />
<product-cart v-model="state.showCartDialog" :merchantId="state.mchId" />
</template>
<script setup lang="ts">
import { onLoad } from "@dcloudio/uni-app"
import { reactive } from "vue"
import { computed, reactive } from "vue"
import { prosperityShopApi } from "@/sheep/api"
import { filePath } from "@/sheep/utils/file"
import { displayPrice } from "@/sheep/utils/formatMoney"
import ProductTotal from "./components/product-total.vue"
import ProductSku from "./components/product-sku.vue"
import ProductCart from "./components/product-cart.vue"
import useCartBuy from "./use-cart-buy"
const { totPrice, totalCount, skuAddToCart, getShopCart, goSettlement } = useCartBuy()
const state = reactive<any>({
id: "",
detail: {},
mchId: "",
goodsDetail: {},
showSkuDialog: false,
showCartDialog: false,
})
const goodsDetail = computed(() => state.goodsDetail)
onLoad(async (option: any) => {
if (!option.id) return uni.showToast({ title: "参数错误", icon: "error" })
uni.showLoading({ title: "加载中..." })
const { id, mchId } = option
if (!id || !mchId) return uni.showToast({ title: "参数错误", icon: "error" })
state.id = option.id
await getGoodsDetail(option.id)
state.mchId = option.mchId
uni.showLoading({ title: "加载中..." })
await getGoodsDetail(id)
await getShopCart(mchId)
uni.hideLoading()
})
// 获取商品详情
const getGoodsDetail = async (id: string) => {
const { code, data, msg } = await prosperityShopApi.getGoodsDetail(id)
if (code != 0) return uni.showToast({ title: msg || "商品详情获取失败", icon: "none" })
state.detail = data
state.goodsDetail = data
}
// 选择sku加入到购物车
const onSkuAddToCart = (sku: any) => {
skuAddToCart({ ...state.goodsDetail, ...sku, productId: sku.skuId }, state.mchId)
state.showSkuDialog = false
}
// 查看购物车
const doCart = () => {
state.showCartDialog = true
}
</script>
+1 -1
View File
@@ -110,7 +110,7 @@ const search = () => {
const doGoodsDetail = (id: string) => {
if (!id) return uni.showToast({ title: "商品id不存在", icon: "none" })
uni.navigateTo({
url: `/pages/prosperity-shop/goods-detail?id=${id}`,
url: `/pages/prosperity-shop/goods-detail?id=${id}&merchantId=${state.params.merchantId}`,
})
}
</script>
+66
View File
@@ -0,0 +1,66 @@
<template>
<crud-layout title="结算" mainIsScroll bg="/static/page/blue-bg.png">
<view class="h-20rpx" />
<view class="bg-#FFA61Ecc color-#fff px-2 py-1 text-28rpx rounded-2">温馨提示:当天的订单预计明天开始配送</view>
<tm-sticky-header>
<view class="h-20rpx" />
<view class="card p-20rpx">
<view class="text-36rpx">请选择自提点</view>
<!-- <tm-picker rang-key="title" rang-text="title" v-model="state.curPickUp" v-model:model-str="state.curPickUp" :list="state.pickUpList"> -->
<view class="flex-row items-center justify-between">
<view class="sub-text-28">请选择自提点</view>
<view class="flex-row items-center second-text-24">
<text>{{ 0 || "请选择自提点" }} </text>
<text class="ml-10rpx color-#b1b1b1 iconfont icon-xiangxiajiantouxiao" />
</view>
</view>
<!-- </tm-picker> -->
<view class="c-#0089ff text-24rpx">配送订单较多时,配送时间可能波动</view>
</view>
<view class="h-20rpx" />
</tm-sticky-header>
<view class="card p-20rpx mb-20rpx mx-1">
<view v-if="totalCount > 0" class="flex-row px-15rpx mb-20rpx" v-for="(v, i) in cartList" :key="i">
<image class="size-125rpx rounded-12rpx mr-20rpx bg-#eeeeee" :src="filePath(v.productPic)" mode="aspectFill" />
<view class="flex-1 overflow-hidden">
<view class="sub-text-30 truncate">{{ v.productName }}</view>
<view class="flex justify-between items-center">
<view class="flex-col">
<view class="second-text-30">¥{{ hooks.useMoney(v.productPrice) }}</view>
<view class="second-text-30">x {{ v.productCount }}</view>
</view>
<view class="second-text-32 c-red!">¥{{ hooks.useMoney(v.totalPrice) }}</view>
</view>
</view>
</view>
<view v-else class="text-center second-text-28">你尚未选择任何商品</view>
</view>
<view class="h-20rpx" />
<template #main-bottom>
<view class="flex justify-end items-center p-4">
<view class="flex-col mr-4">
<view class="second-text-36 color-#f63a36!">¥ {{ hooks.useMoney(totPrice) }}</view>
<view class="sub-text-28">共{{ totalCount }}件</view>
</view>
<view class="bg-#0094ff px-60rpx py-20rpx text-center rounded-full sub-text-32 color-white!" @tap.stop="doBuy">提交订单</view>
</view>
</template>
</crud-layout>
</template>
<script setup lang="ts">
import { reactive } from "vue"
import { hooks } from "@/sheep"
import useCartBuy from "./use-cart-buy"
import { filePath } from "@/sheep/utils/file"
const { cartList, totPrice, totalCount, doBuy } = useCartBuy()
const state = reactive({
pickUpList: [],
curPickUp: {},
})
</script>
+97 -51
View File
@@ -1,7 +1,7 @@
<template>
<crud-layout mainIsScroll bg="/static/page/blue-bg.png" @scroll="onScroll">
<view class="w-full h-400rpx relative">
<image :src="filePath(state.detail.images)" class="w-full h-full bg-#eeeeee" mode="aspectFill" />
<image :src="filePath(mchDetail.images)" class="w-full h-full bg-#eeeeee" mode="aspectFill" />
<view class="absolute w-full h-full top-0 left-0" :style="filterStyle"></view>
</view>
<view
@@ -12,25 +12,20 @@
<image class="w-36rpx h-36rpx" src="@/static/page/shop-goods-search.png" />
<text v-if="scrollDetail.scrollTop > 30" class="bg-#eeeeee44 whitespace-nowrap overflow-hidden second-text-26 ml-2">搜索商品</text>
</view>
<view v-if="state.detail.id" class="card p-20rpx mt-10rpx mx-20rpx">
<view class="w-full sub-text-32 truncate">{{ state.detail.name }}</view>
<view class="sub-text-24 mt-1">
<text class="c-#0094ff! mr-20rpx px-2 bg-#0094ff22 rounded-2">{{ state.detail.status == "0" ? "开业中" : "未开业" }}</text>
<text class="rounded-2 c-#0094ff px-2 bg-#0094ff22 rounded-2">{{ state.detail.businessHours }}</text>
</view>
<view class="second-text-24 truncate mt-1">
<text class="mr-20rpx">营业人 {{ state.detail.contactName }}·{{ state.detail.contactPhone }}</text>
<text class="mr-20rpx">许可 {{ state.detail.licenseNo }}</text>
</view>
<view v-if="mchDetail.id" class="card p-20rpx mt-10rpx mx-20rpx">
<view class="w-full sub-text-32 truncate">{{ mchDetail.name }}</view>
<text class="sub-text-24 c-#0094ff! px-2 bg-#0094ff22 rounded-2 mr-4">营业</text>
<text class="second-text-24 truncate mt-1">营业人 {{ mchDetail.contactName }}·{{ mchDetail.contactPhone }}</text>
<view class="second-text-26 mt-1">
<image class="w-24rpx h-24rpx mr-1" src="@/static/page/address.svg" />
<text>{{ state.detail.address }}</text>
<text>{{ mchDetail.address }}</text>
</view>
<marquee class="second-text-24 mt-1">{{ state.detail.remark }}</marquee>
<marquee class="second-text-24 mt-1">{{ mchDetail.remark }}</marquee>
</view>
<view class="flex mt-2">
<scroll-view scroll-y class="menu w-200rpx mb-20rpx bg-#f6f6f6 pl-20rpx" style="height: calc(100vh - 420rpx)">
<scroll-view scroll-y class="menu w-200rpx mb-20rpx bg-#f6f6f6 pl-20rpx" style="height: calc(100vh - 300rpx)">
<view
class="menu_item flex-row-center"
v-for="(v, i) in state.category"
@@ -48,7 +43,7 @@
style="height: calc(100vh - 420rpx)"
>
<view v-for="v in state.goodsList" :key="v.id" class="card flex mx-15rpx mb-2 p-2" @click="doGoodsDetail(v.id)">
<image :src="filePath(v.coverImage)" class="size-160rpx rounded-12rpx bg-#eeeeee" mode="aspectFill" />
<image :src="filePath(v.coverImage)" class="size-170rpx rounded-12rpx bg-#eeeeee" mode="aspectFill" />
<view class="flex-1 ml-2 overflow-hidden">
<view class="truncate sub-text-30">{{ v.title }}</view>
<view>
@@ -58,80 +53,106 @@
<text class="second-text-26 mr-2">单位 {{ v.unit }}</text>
<text class="second-text-26">库存 {{ v.totalStock }}</text>
</view>
<view>
<template v-if="v.priceType == '2'">
<text class="second-text-28 line-through mr-1">
<text class="text-26rpx">¥</text>
<text class="font-bold">{{ displayPrice(v.originalPrice, "") }}</text>
</text>
<text class="color-red">
<text class="text-26rpx">¥</text>
<text class="text-32rpx font-bold">{{ displayPrice(v.spuPrice, "").split(".")[0] }}.</text>
<text class="text-30rpx font-bold">{{ displayPrice(v.spuPrice, "").split(".")[1] }}</text>
</text>
</template>
<template v-else>
<text class="color-red">
<text class="text-26rpx">¥</text>
<text class="text-32rpx font-bold">{{ displayPrice(v.minPrice, "").split(".")[0] }}.</text>
<text class="text-30rpx font-bold">{{ displayPrice(v.minPrice, "").split(".")[1] }}</text>
</text>
</template>
<text class="view-clip px-1 text-18rpx c-white bg-red ml-1 rounded-r-1 rounded-lt-1">到手价</text>
<view class="flex items-center justify-between">
<view class="flex items-center">
<template v-if="v.priceType == '2'">
<text class="second-text-28 line-through mr-1">
<text class="text-26rpx">¥</text>
<text>{{ displayPrice(v.originalPrice, "") }}</text>
</text>
<text class="color-red">
<text class="text-26rpx">¥</text>
<text class="text-32rpx">{{ displayPrice(v.spuPrice, "").split(".")[0] }}.</text>
<text class="text-30rpx">{{ displayPrice(v.spuPrice, "").split(".")[1] }}</text>
</text>
</template>
<template v-else>
<text class="color-red">
<text class="text-26rpx">¥</text>
<text class="text-32rpx">{{ displayPrice(v.minPrice, "").split(".")[0] }}.</text>
<text class="text-30rpx">{{ displayPrice(v.minPrice, "").split(".")[1] }}</text>
</text>
</template>
<text class="view-clip px-1 text-20rpx c-white bg-red ml-1 rounded-r-1 rounded-lt-1">到手价</text>
</view>
<view class="second-text-24 px-2 py-2rpx bg-#0094ff c-white! border-rd-8" @tap.stop="doSelectSku(v.id)">选规格</view>
</view>
</view>
</view>
</scroll-view>
<view v-else class="mx-auto mt-10 second-text-28">暂无商品</view>
</view>
<template #main-bottom>
<view class="z-200 p-x-30rpx p-y-30rpx bg-white">
<product-total :totPrice="totPrice" :totCount="totalCount" @doContainer="doCart" @submit="goSettlement" />
</view>
</template>
</crud-layout>
<product-sku v-model="state.showSkuDialog" :goodsInfo="state.goodsDetail" @onSkuAddToCart="onSkuAddToCart" />
<product-cart v-model="state.showCartDialog" :merchantId="state.mchId" />
</template>
<script setup lang="ts">
import { reactive } from "vue"
import { prosperityShopApi } from "@/sheep/api"
import { computed, reactive } from "vue"
import { onLoad } from "@dcloudio/uni-app"
import { prosperityShopApi } from "@/sheep/api"
import { filePath } from "@/sheep/utils/file"
import treeToList from "@/sheep/utils/tree-to-list"
import { displayPrice } from "@/sheep/utils/formatMoney"
import { useShopDetailScroll } from "./use-shop-detail-scroll"
import useShopDetailScroll from "./use-shop-detail-scroll"
import useCartBuy from "./use-cart-buy"
import ProductTotal from "./components/product-total.vue"
import ProductSku from "./components/product-sku.vue"
import ProductCart from "./components/product-cart.vue"
const state = reactive<any>({
id: "",
detail: {},
mchId: "",
mchDetail: {},
category: [],
curCategoryIndex: 0,
goodsList: [],
goodsDetail: {},
showSkuDialog: false,
showCartDialog: false,
})
const mchDetail = computed(() => state.mchDetail)
const { scrollDetail, onScroll, filterStyle, searchStyle } = useShopDetailScroll()
const { totPrice, totalCount, skuAddToCart, getShopCart, goSettlement } = useCartBuy()
onLoad(async (option: any) => {
if (!option.id) return uni.showToast({ title: "参数错误", icon: "error" })
const { id } = option
if (!id) return uni.showToast({ title: "参数错误", icon: "error" })
state.mchId = id
uni.showLoading({ title: "加载中..." })
state.id = option.id
await getShopDetail(option.id)
await getGoodsCategoryTree(option.id)
await getMchDetail(id)
await getGoodsCategoryTree(id)
await getGoodsList()
await getShopCart(id)
uni.hideLoading()
})
const getShopDetail = async (id: string) => {
// 获取商户详情
const getMchDetail = async (id: string) => {
const { code, data, msg } = await prosperityShopApi.getShopDetail(id)
if (code != 0) return uni.showToast({ title: msg || "超市详情获取失败", icon: "none" })
state.detail = data
state.mchDetail = data
}
// 获取商品分类
const getGoodsCategoryTree = async (id: string) => {
const { code, data, msg } = await prosperityShopApi.getGoodsCategoryTree({ merchantId: id })
if (code != 0) return uni.showToast({ title: msg || "商品分类获取失败", icon: "none" })
state.category = treeToList(data) ?? []
}
// 获取商品列表
const getGoodsList = async () => {
const params = {
current: 1,
size: 99999,
merchantId: state.id,
merchantId: state.mchId,
categoryId: state.category[state.curCategoryIndex].id,
}
const { code, data, msg } = await prosperityShopApi.getGoodsList(params)
@@ -139,6 +160,7 @@ const getGoodsList = async () => {
state.goodsList = data?.records ?? []
}
// 切换分类
const doCategory = async (index: number) => {
if (state.curCategoryIndex == index) return
state.curCategoryIndex = index
@@ -147,16 +169,40 @@ const doCategory = async (index: number) => {
uni.hideLoading()
}
// 去查看商品详情
const doGoodsDetail = (id: string) => {
if (!id) return uni.showToast({ title: "商品id不存在", icon: "none" })
if (!id || !state.mchId) return uni.showToast({ title: "id错误", icon: "none" })
uni.navigateTo({
url: `/pages/prosperity-shop/goods-detail?id=${id}`,
url: `/pages/prosperity-shop/goods-detail?id=${id}&mchId=${state.mchId}`,
})
}
// 去搜索商品
const goSearch = () => {
if (!state.detail.id) return uni.showToast({ title: "超市id不存在", icon: "none" })
uni.navigateTo({ url: `/pages/prosperity-shop/goods-search?merchantId=${state.detail.id}` })
if (!state.mchDetail.id) return uni.showToast({ title: "超市id不存在", icon: "none" })
uni.navigateTo({ url: `/pages/prosperity-shop/goods-search?merchantId=${state.mchDetail.id}` })
}
// 选规格
const doSelectSku = async (id: string) => {
if (!id) return uni.showToast({ title: "商品id不存在", icon: "none" })
uni.showLoading({ title: "加载中..." })
const { code, data, msg } = await prosperityShopApi.getGoodsDetail(id)
if (code != 0) return uni.showToast({ title: msg || "商品详情获取失败", icon: "none" })
uni.hideLoading()
state.goodsDetail = data
state.showSkuDialog = true
}
// 选择sku加入到购物车
const onSkuAddToCart = (sku: any) => {
skuAddToCart({ ...state.goodsDetail, ...sku, productId: sku.skuId }, state.mchId)
state.showSkuDialog = false
}
// 查看购物车
const doCart = () => {
state.showCartDialog = true
}
</script>
+95
View File
@@ -0,0 +1,95 @@
import { router } from "@/sheep"
import { orderApi } from "@/sheep/api"
import { useProsperityCartStore } from "@/sheep/stores/prosperity-cart"
import { isEmpty } from "radash"
import { computed } from "vue"
export default function useCartBuy() {
const cartStore = useProsperityCartStore()
// 购物车商品总金额
const totPrice = computed(() => {
return cartStore.totPrice
})
// 购物车商品总数量
const totalCount = computed(() => {
return cartStore.totalCount
})
// 购物车商品列表
const cartList = computed(() => {
return cartStore.cartList
})
// 选择sku确认加入到购物车
const skuAddToCart = (sku: any, merchantId: string) => {
cartStore.addGoodsItemToCart(sku)
cartStore.updateShopCart(merchantId)
}
// 获取购物车列表
const getShopCart = async (merchantId: string) => {
await cartStore.getShopCart(merchantId)
}
// 添加一个商品数量
const addProductCount = async (productId: string, merchantId: string) => {
if (!merchantId) return
await cartStore.addProductCount(productId, merchantId)
}
// 减少一个商品数量
const removeProductCount = async (productId: string, merchantId: string) => {
if (!merchantId) return
await cartStore.removeProductCount(productId, merchantId)
}
const goSettlement = () => {
uni.navigateTo({ url: "/pages/prosperity-shop/settlement" })
}
// 点击提交订单
const doBuy = async () => {
if (isEmpty(cartList.value)) return uni.showToast({ title: "请至少购买一件商品", icon: "none" })
uni.showModal({
title: "提示",
content: "是否确定购买",
confirmColor: "#0094ff",
success: async (res) => {
if (!res.confirm) return
submitPay()
},
})
}
// 确认提交订单
const submitPay = async () => {
try {
uni.showLoading({ title: "支付中...", mask: false })
const { code, msg }: any = await orderApi.appCreateFarmerReserveOrder({
carParentId: cartStore.cartList[0].parentCarId,
hardType: "FARMER",
})
if (code != 0) throw new Error(msg)
uni.showToast({ title: "支付成功", icon: "none" })
router.jump("/pages/tabbar/order")
} catch (error: any) {
uni.showToast({ title: error.message, icon: "none" })
} finally {
uni.hideLoading()
}
}
return {
totPrice,
totalCount,
cartList,
getShopCart,
skuAddToCart,
addProductCount,
removeProductCount,
goSettlement,
doBuy,
}
}
@@ -9,7 +9,7 @@ interface scrollDetail {
scrollWidth: number
}
export function useShopDetailScroll() {
export default function useShopDetailScroll() {
const scrollDetail = ref<scrollDetail>({} as scrollDetail)
const onScroll = (e: any) => {
@@ -31,7 +31,7 @@ export function useShopDetailScroll() {
const scrollY = scrollDetail.value.scrollTop
const scrollHeight = scrollDetail.value.scrollHeight
const scrollWidth = scrollDetail.value.scrollWidth
const calcWidth = Math.min((scrollY / scrollHeight) * scrollWidth, scrollWidth)
const calcWidth = Math.min(((scrollY / scrollHeight) * scrollWidth) / 2, scrollWidth)
if (isNaN(calcWidth)) return { width: `${minWidth}%` }
if (calcWidth < 2) return { width: `${calcWidth + minWidth}%` }
return {
+16 -25
View File
@@ -1,21 +1,13 @@
<template>
<crud-layout title="喜好分析" bg="/static/page/blue-bg.png">
<z-paging
ref="pageRef"
height="100%"
:fixed="false"
safe-area-inset-bottom
:loading-more-enabled="true"
:show-scrollbar="false"
@query="query"
:auto-clean-list-when-reload="false"
:auto-scroll-to-top-when-reload="false"
hide-empty-view
>
<z-paging ref="pageRef" height="100%" :fixed="false" safe-area-inset-bottom :loading-more-enabled="true"
:show-scrollbar="false" @query="query" :auto-clean-list-when-reload="false"
:auto-scroll-to-top-when-reload="false" hide-empty-view>
<tm-sheet :margin="[0, 20]">
<view class="prefix_title mb-15rpx">菜品分类购买数量占比图</view>
<view class="w-full h-600rpx">
<qiun-data-charts type="pie" :opts="pieChartOpts" :chartData="pieChartData" tooltipFormat="canteenPieTooltip" />
<qiun-data-charts type="pie" :opts="pieChartOpts" :chartData="pieChartData"
tooltipFormat="canteenPieTooltip" />
</view>
</tm-sheet>
@@ -30,40 +22,37 @@
<tm-sheet :margin="[0, 20]">
<view class="prefix_title mb-15rpx">个人菜品喜好排行</view>
<view v-if="productLikeTopList.length > 0" class="rounded-16rpx bg-white overflow-hidden card">
<view class="bg-gradient-to-r from-blue-100 to-blue-100 px-20rpx py-16rpx flex items-center border-b border-gray-100">
<view
class="bg-gradient-to-r from-blue-100 to-blue-100 px-20rpx py-16rpx flex items-center border-b border-gray-100">
<view class="w-80rpx text-center text-24rpx font-bold text-gray-600">排名</view>
<view class="flex-1 px-16rpx text-24rpx font-bold text-gray-600">菜品名称</view>
<view class="w-120rpx text-center text-24rpx font-bold text-gray-600">分类</view>
<view class="w-160rpx text-center text-24rpx font-bold text-gray-600">购买数量</view>
</view>
<view
v-for="(item, index) in productLikeTopList"
:key="item.productId"
<view v-for="(item, index) in productLikeTopList" :key="item.productId"
class="flex items-center px-20rpx py-16rpx border-b border-gray-50 hover:bg-gray-50 transition-colors"
:class="{
'bg-gradient-to-r from-yellow-50 to-orange-50': index === 0,
'bg-gradient-to-r from-gray-50 to-gray-100': index === 1,
'bg-gradient-to-r from-orange-50 to-red-50': index === 2,
}"
>
}">
<view class="w-80rpx flex justify-center items-center">
<view
v-if="index < 3"
<view v-if="index < 3"
class="w-40rpx h-40rpx rounded-1 flex items-center justify-center text-20rpx font-bold text-white"
:class="{
'bg-gradient-to-r from-yellow-400 to-yellow-500': index === 0,
'bg-gradient-to-r from-gray-400 to-gray-500': index === 1,
'bg-gradient-to-r from-orange-400 to-orange-500': index === 2,
}"
>
}">
{{ index + 1 }}
</view>
<view v-else class="text-24rpx font-medium text-gray-500">{{ index + 1 }}</view>
</view>
<view class="flex-1 px-16rpx">
<view class="text-26rpx font-medium text-gray-800 truncate">{{ item.productName || "未知菜品" }}</view>
<view class="text-26rpx font-medium text-gray-800 truncate">{{ item.productName || "未知菜品" }}
</view>
</view>
<view class="w-120rpx text-center">
@@ -71,7 +60,9 @@
</view>
<view class="w-160rpx text-center">
<view class="px-12rpx py-6rpx bg-blue-100 text-blue-600 text-22rpx rounded-1 whitespace-nowrap">{{ item.payCount }}次</view>
<view
class="px-12rpx py-6rpx bg-blue-100 text-blue-600 text-22rpx rounded-1 whitespace-nowrap">
{{ item.payCount }}次</view>
</view>
</view>
</view>
+17 -29
View File
@@ -1,35 +1,17 @@
<template>
<crud-layout bg="/static/page/blue-bg.png">
<z-paging
ref="pageRef"
:fixed="false"
height="100%"
v-model="pageHook.list"
:default-page-size="10"
safe-area-inset-bottom
:loading-more-enabled="false"
:show-scrollbar="false"
@query="query"
:auto-clean-list-when-reload="false"
:auto-scroll-to-top-when-reload="false"
@scroll="pageHook.scroll"
>
<z-paging ref="pageRef" :fixed="false" height="100%" v-model="pageHook.list" :default-page-size="10"
safe-area-inset-bottom :loading-more-enabled="false" :show-scrollbar="false" @query="query"
:auto-clean-list-when-reload="false" :auto-scroll-to-top-when-reload="false" @scroll="pageHook.scroll">
<template #top>
<view class="flex items-center justify-between my-2">
<view class="flex-1 bg-white rounded-12rpx p-20rpx flex justify-between items-center">
<text class="text-30 font-600">流水统计</text>
<tm-text color="#999999" font-size="26">{{ formatVal }}</tm-text>
<view class="flex items-center cursor-pointer">
<tm-between-time
start="2020-1-1"
end="2050-1-1"
v-model="state.date"
v-model:model-str="state.formatVal"
@confirm="datePickerConfirm"
format="YYYY-MM-DD"
:quickDate="[]"
drawerSize="900"
>
<tm-between-time start="2020-1-1" end="2050-1-1" v-model="state.date"
v-model:model-str="state.formatVal" @confirm="datePickerConfirm" format="YYYY-MM-DD"
:quickDate="[]" drawerSize="900">
<image class="w-[30rpx] h-[30rpx]" src="@/static/page/filter.svg" />
<text class="ml-1 text-30">筛选</text>
</tm-between-time>
@@ -50,7 +32,8 @@
<view class="flex justify-between mb-2">
<view class="flex-1 flex items-center p-2 bg-white rounded-8rpx">
<text class="flex-1 text-sm text-center text-gray-600">流水金额</text>
<text class="flex-1 text-sm text-center text-gray-800">¥ {{ hooks.useMoney(v.totalPayPrice) }}</text>
<text class="flex-1 text-sm text-center text-gray-800">¥ {{ hooks.useMoney(v.totalPayPrice)
}}</text>
</view>
<view class="flex-1 flex items-center p-2 bg-white rounded-8rpx ml-2">
<text class="flex-1 text-sm text-center text-gray-600">流水次数</text>
@@ -60,7 +43,9 @@
<view class="flex justify-between mb-2">
<view class="flex-1 flex items-center p-2 bg-white rounded-8rpx">
<text class="flex-1 text-sm text-center text-gray-600">盈利金额</text>
<text class="flex-1 text-sm text-center text-green-500">¥ {{ hooks.useMoney(v.totalProfitPrice) }}</text>
<text class="flex-1 text-sm text-center text-green-500">¥ {{
hooks.useMoney(v.totalProfitPrice)
}}</text>
</view>
<view class="flex-1 flex items-center p-2 bg-white rounded-8rpx ml-2">
<text class="flex-1 text-sm text-center text-gray-600">盈利次数</text>
@@ -70,7 +55,8 @@
<view class="flex justify-between mb-0">
<view class="flex-1 flex items-center p-2 bg-white rounded-8rpx">
<text class="flex-1 text-sm text-center text-gray-600">退款金额</text>
<text class="flex-1 text-sm text-center text-red-500">¥ {{ hooks.useMoney(v.totalRefundPrice) }}</text>
<text class="flex-1 text-sm text-center text-red-500">¥ {{
hooks.useMoney(v.totalRefundPrice) }}</text>
</view>
<view class="flex-1 flex items-center p-2 bg-white rounded-8rpx ml-2">
<text class="flex-1 text-sm text-center text-gray-600">退款次数</text>
@@ -80,7 +66,7 @@
</view>
</view>
<view class="fixed bottom-120rpx right-40rpx">
<view class="fixed bottom-200rpx right-40rpx">
<tm-button size="m" class="px-1 ml-1" @click="doWaterTotal">每月流水合计</tm-button>
</view>
</z-paging>
@@ -126,7 +112,9 @@ const query = (page: number, pagesize: number) => {
// 时间范围选择器确认
const datePickerConfirm = (date: any) => {
const list = formatVal.value.split("~")
state.pa.createTimeList = [list[0] + " 00:00:00", list[1] + " 23:59:59"]
if (list && list.length == 2) {
state.pa.createTimeList = [list[0] + " 00:00:00", list[1] + " 23:59:59"]
}
pageRef.value.reload(false)
}
+13 -12
View File
@@ -2,13 +2,10 @@
<crud-layout mainIsScroll bg="/static/page/blue-bg.png">
<template #main-top>
<view class="flex items-center py-3 px-5">
<image
v-if="state.userInfo?.sysUser?.avatar"
class="size-100rpx rounded-full"
:src="filePath(state.userInfo.sysUser.avatar)"
mode="aspectFill"
/>
<image v-else class="w-100rpx h-100rpx rounded-full" src="/static/icon/default-avatar.png" mode="aspectFill" />
<image v-if="state.userInfo?.sysUser?.avatar" class="size-100rpx rounded-full"
:src="filePath(state.userInfo.sysUser.avatar)" mode="aspectFill" />
<image v-else class="w-100rpx h-100rpx rounded-full" src="/static/icon/default-avatar.png"
mode="aspectFill" />
<view class="ml-30rpx" v-if="state.userInfo?.sysUser?.userId">
<view class="sub-text-30">{{ state.userInfo?.sysUser?.name }}</view>
<view class="sub-text-26">ID {{ state.userInfo?.sysUser?.userId }}</view>
@@ -21,7 +18,8 @@
</template>
<view class="mt-10rpx rounded-20rpx shadow-main p-30rpx pb-40rpx bg-white">
<view class="service_item flex items-center justify-between" v-for="(v, i) in state.menuList" :key="i" @tap="onTabMenu(v)">
<view class="service_item flex items-center justify-between" v-for="(v, i) in state.menuList" :key="i"
@tap="onTabMenu(v)">
<view class="flex-row-center">
<image class="size-40rpx" :src="v.icon" mode="aspectFill" />
<text class="sub-text-28 m-l-20rpx">{{ v.text }}</text>
@@ -31,13 +29,15 @@
</view>
<view v-if="isDevEnv()" class="flex-col-center mt-40rpx mx-30rpx">
<view class="w-full text-center sub-text-28 p-y-20rpx rounded-40rpx border-solid border border-#0094ff" @tap="doLogout">
<view class="w-full text-center sub-text-28 p-y-20rpx rounded-40rpx border-solid border border-#0094ff"
@tap="doLogout">
退出登录
</view>
</view>
<view class="h-30rpx" />
<view v-if="state.appInfo == APPENV.SH" class="flex-col-center mt-40rpx mx-30rpx">
<view class="w-full text-center sub-text-28 p-y-20rpx rounded-40rpx border-solid border border-#0094ff" @tap="doChangeRole">
<view class="w-full text-center sub-text-28 p-y-20rpx rounded-40rpx border-solid border border-#0094ff"
@tap="doChangeRole">
切换角色
</view>
</view>
@@ -50,7 +50,8 @@
</view>
</crud-layout>
<tm-modal :show="state.actModalShow" :showCancel="false" title="核销活动兑换券" zIndex="999" @confirm="" @cancel="" @close="">
<tm-modal :show="state.actModalShow" :showCancel="false" title="核销活动兑换券" zIndex="999" @confirm="" @cancel=""
@close="">
<view class="text-center text-28">是否核销该活动兑换券?</view>
<view class="text-center text-28">确认核销将会对用户的 消费钱包 扣除 1元 钱</view>
<template #footer>
@@ -80,7 +81,7 @@ const state = reactive<any>({
{ text: "扫一扫", icon: "/static/icon/scan.png", path: "" },
{ text: "我的钱包", icon: "/static/icon/service_3.png", path: "/pages/wallet/list" },
{ text: "我的人脸", icon: "/static/icon/service_5.png", path: "/pages/human-face/list" },
// { text: "车辆管理", icon: "/static/icon/service_6.png", path: "/pages/car/userCar/list" },
{ text: "车辆管理", icon: "/static/icon/service_6.png", path: "/pages/car/userCar/list" },
{ text: "清除缓存", icon: "/static/icon/clear-storage.png", path: "" },
],
appInfo: "",
+2 -2
View File
@@ -143,10 +143,10 @@ import { isEmpty } from "radash"
import { filePath } from "@@/utils/file"
import { merchantApi, onlineShopApi } from "@@/api"
import { hooks, router } from "@@/index"
import { useCartStore } from "@/sheep/stores/cart"
import { useOnlineCartStore } from "@/sheep/stores/online-cart"
import treeToList from "@/sheep/utils/tree-to-list"
const cartStore = useCartStore()
const cartStore = useOnlineCartStore()
const state = reactive<any>({
params: {
+11 -16
View File
@@ -57,20 +57,15 @@
<view class="card p-20rpx mb-20rpx" v-for="(v, i) in pageHook.list" :key="i" @tap="toDetail(v)">
<view class="flex">
<image class="w-150rpx h-150rpx bg-#eeeeee rounded-12rpx" :src="filePath(v.images)" mode="aspectFill" />
<image class="w-160rpx h-160rpx bg-#eeeeee rounded-12rpx" :src="filePath(v.images)" mode="aspectFill" />
<view class="ml-20rpx flex-1 overflow-hidden">
<view class="sub-text-30 truncate">{{ v.name }}</view>
<view class="second-text-26 truncate">
<text class="mr-20rpx">{{ v.contactName }}</text>
<text class="mr-20rpx">
<view class="sub-text-32 truncate">{{ v.name }}222222222222222222222222 </view>
<view class="second-text-26 flex justify-between my-6rpx">
<view>
<image class="w-24rpx h-24rpx mr-1" src="@/static/page/phone.svg" />
<text @tap.stop="doPhone(v.contactPhone)">{{ v.contactPhone }}</text>
</text>
<text>{{ v.email }}</text>
</view>
<view>
<text class="c-#0094ff! sub-text-26 px-2 bg-#0094ff22 rounded-2 mr-2">营业</text>
<text class="c-#0094ff! sub-text-26 px-2 bg-#0094ff22 rounded-2">{{ v.businessHours }}</text>
</view>
<text class="c-#0094ff! sub-text-26 px-2 bg-#0094ff22 rounded-2">营业</text>
</view>
<view class="second-text-26">
<image class="w-24rpx h-24rpx mr-1" src="@/static/page/address.svg" />
@@ -83,21 +78,21 @@
<view class="w-160rpx flex-col items-center mr-20rpx" v-for="(pV, pI) in v.featuredProducts" :key="pI">
<image class="w-160rpx h-160rpx rounded-12rpx bg-#eeeeee" :src="filePath(pV.coverImage)" mode="aspectFill" />
<view class="w-full mt-4rpx sub-text-24 truncate">{{ pV.title }}</view>
<view class="w-full flex justify-between pr-1">
<view class="w-full flex justify-between">
<text class="main-text-26 color-red!">
<text class="text-22rpx">¥</text>
<text>{{ pV.priceType == "1" ? displayPrice(pV.minPrice, "") : displayPrice(pV.spuPrice, "") }}</text>
</text>
<text class="sub-text-24">{{ pV.unit || "" }}</text>
</view>
<view class="w-full flex justify-between pr-1">
<text class="second-text-24 line-through">
<template v-if="pV.priceType == '2'">
<text class="text-22rpx">¥</text>
<text>{{ displayPrice(pV.originalPrice, "") }}</text>
</template>
</text>
<text class="second-text-24">库存{{ pV.totalStock }}</text>
</view>
<view class="w-full flex justify-between second-text-24">
<text>{{ pV.unit || "" }}</text>
<text>库存{{ pV.totalStock }}</text>
</view>
</view>
</view>
-12
View File
@@ -23,18 +23,6 @@ export default {
method: "GET",
params,
}),
getShopCar: (params: any) =>
request({
url: `/hard/hard-car/app/mch-detail`,
method: "GET",
params,
}),
updateShopCar: (data: any) =>
request({
url: `/hard/hard-car/app/save-or-upload`,
method: "POST",
data,
}),
checkSpuStock: (params) => {
return request({
url: "/canteen/canteenOnlineSpu/app/checkSpuStock",
+24
View File
@@ -25,6 +25,18 @@ export default {
method: "GET",
params,
}),
getShopCar: (params: any) =>
request({
url: `/hard/hard-car/app/mch-detail`,
method: "GET",
params,
}),
updateShopCar: (data: any) =>
request({
url: `/hard/hard-car/app/save-or-upload`,
method: "POST",
data,
}),
/**
* 预约时间列表
*/
@@ -44,4 +56,16 @@ export default {
method: "POST",
data,
}),
/**
* 创建共富商城预约订单
* @body body.carParentId 购物车id
* @body body.walletTypeId 钱包类型id
* @body body.pickUpId 自提点id
*/
appCreateFarmerReserveOrder: (data: any) =>
request({
url: "/hard/hard-order/app/create-farmer-order",
method: "POST",
data,
}),
}
+1 -1
View File
@@ -20,7 +20,7 @@ export default {
data,
}),
/**
* 个人菜品喜好排行
*
*/
productLikeTop: (data: any): any =>
request({
+22
View File
@@ -69,4 +69,26 @@ export default {
method: "GET",
data: {},
}),
/**
* 自提点分页
* @param params.pageNum 页码
* @param params.pageSize 每页数量
* @param params.mchIds 商户ID列表
*/
pickUpPage: (params: any): Promise<any> =>
request({
url: "wallet/mch-pick-up/app/page",
method: "GET",
params: params,
}),
/**
* 自提点详情
* @param params.id 自提点id
*/
pickUpDetail: (params: any): Promise<any> =>
request({
url: "wallet/mch-pick-up/app/detail",
method: "GET",
params: params,
}),
}
@@ -1,14 +1,14 @@
import { defineStore } from "pinia"
import { foodApi } from "../api"
import { orderApi } from "../api"
export const useCartStore = defineStore("cartStore", {
export const useOnlineCartStore = defineStore("onlineCartStore", {
state: () => ({
cart: JSON.parse(uni.getStorageSync("onlineShopCart") || "[]"),
shopMerchantId: uni.getStorageSync("onlineShopMerchantId") || "",
}),
getters: {
totalCount(state) {
return state.cart.reduce((prev: string, cur: any) => {
return state.cart.reduce((prev: number, cur: { productCount: number }) => {
return prev + cur.productCount
}, 0)
},
@@ -58,7 +58,7 @@ export const useCartStore = defineStore("cartStore", {
await this.getShopCart()
},
async getShopCart() {
const { data } = await foodApi.getShopCar({ merchantId: this.shopMerchantId, hardType: "SUPERMARKET" })
const { data } = await orderApi.getShopCar({ merchantId: this.shopMerchantId, hardType: "SUPERMARKET" })
this.saveShopCart(data ?? [])
},
async updateShopCart() {
@@ -70,7 +70,7 @@ export const useCartStore = defineStore("cartStore", {
productId: v.productId,
}
})
const { code }: any = await foodApi.updateShopCar({ merchantId: this.shopMerchantId, list, hardType: "SUPERMARKET" })
const { code }: any = await orderApi.updateShopCar({ merchantId: this.shopMerchantId, list, hardType: "SUPERMARKET" })
if (code == 0) return await this.getShopCart()
uni.showToast({ title: "更新购物车失败", icon: "none" })
},
+74
View File
@@ -0,0 +1,74 @@
import { defineStore } from "pinia"
import { orderApi } from "../api"
// 共富商城购物车
export const useProsperityCartStore = defineStore("prosperityCartStore", {
state: (): any => ({
cartList: [],
}),
getters: {
totalCount(state) {
return state.cartList.reduce((prev: number, cur: { productCount: number }) => {
return prev + cur.productCount
}, 0)
},
totPrice(state) {
return state.cartList
.map((item: any) => Number(item.totalPrice))
.reduce((prev: number, cur: number) => prev + cur, 0)
.toFixed(2)
},
},
actions: {
// 购物车添加商品项
addGoodsItemToCart(goodsItem: any) {
const findResult = this.cartList.find((item: any) => {
return item.productId === goodsItem.productId
})
if (!findResult) {
this.cartList.unshift(goodsItem)
} else {
findResult.productCount++
}
},
// 购物车商品项数量减一
async removeProductCount(productId: string, merchantId: string) {
this.cartList.forEach((item: any) => {
if (item.productId === productId) {
item.productCount--
}
})
await this.updateShopCart(merchantId)
await this.getShopCart(merchantId)
},
// 购物车商品项数量加一
async addProductCount(productId: string, merchantId: string) {
this.cartList.forEach((item: any) => {
if (item.productId === productId) {
item.productCount++
}
})
await this.updateShopCart(merchantId)
await this.getShopCart(merchantId)
},
// 获取购物车商品列表
async getShopCart(merchantId: string) {
const { data } = await orderApi.getShopCar({ merchantId, hardType: "FARMER" })
this.cartList = data ?? []
},
// 更新购物车商品列表
async updateShopCart(merchantId: string) {
const list = this.cartList
.filter((v) => v.productCount && v.productCount > 0)
.map((v) => {
return {
productCount: v.productCount,
productId: v.productId,
}
})
const { code }: any = await orderApi.updateShopCar({ merchantId, list, hardType: "FARMER" })
if (code == 0) return await this.getShopCart(merchantId)
uni.showToast({ title: "更新购物车失败", icon: "none" })
},
},
})
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB