This commit is contained in:
weipengfei 2024-04-09 17:16:40 +08:00
parent 2284f6d9e6
commit b01e4703ce
15 changed files with 1157 additions and 118 deletions

View File

@ -64,6 +64,13 @@ export function orderListApi(id, data) {
return request.get(`admin/${id}/order_list`, { params: data })
}
/**
* @description 核销订单列表
*/
export function verifierOrderListApi(id, code) {
return request.get(`verifier/${id}/order/${code}`)
}
/**
* @description 未支付订单列表
*/

View File

@ -1,8 +1,9 @@
<script setup>
import { ref, watch, nextTick, computed } from "vue";
import { ref, watch, nextTick, computed, onMounted, onUnmounted } from "vue";
import { orderCreateApi, orderStatusApi, orderPayApi } from "@/api/store.js";
import { ElMessage } from "element-plus";
import { audioplay } from "@/utils/audio.js";
import mitt from "@/utils/mitt.js";
const drawer = ref(false);
const active = ref(1);
@ -20,7 +21,7 @@ const open = () => {
input.value = "";
reLoad.value = true;
codeRef.value?.focus();
changeActive(active.value)
changeActive(active.value);
}, 300);
});
};
@ -29,8 +30,14 @@ const changeActive = (e) => {
active.value = e;
if (active.value == 2) {
//
document.addEventListener("keydown", keyboard);
} else document.removeEventListener("keydown", keyboard);
window.addEventListener("keydown", keyboard);
} else{
window.removeEventListener("keydown", keyboard);
nextTick(()=>{
input.value = "";
codeRef.value?.focus();
})
}
};
const form = ref({});
@ -66,29 +73,42 @@ const numList = ref([
let timecount = 0; //
const regexWechat = /^(10|11|12|13|14|15)\d{16}$/; // 10-1518
const regexPay = /^(25|26|27|28|29|30)\d{14,22}$/; // 25-3016-24
const order_id = ref("");
//
const handleEnter = () => {
loading.value = true;
codeRef.value.blur();
if (order_id.value) orderPay(order_id.value);
else
else {
let pay_type;
if (regexWechat.test(input.value)) pay_type = "micropay";
else if (regexPay.test(input.value)) pay_type = "alipayBar";
else {
loading.value = false;
input.value = "";
codeRef.value.focus();
return ElMessage.error("请输入正确的支付码");
}
orderCreateApi({
address_id: "",
key: form.value.key,
cart_id: cart_id.value,
pay_type: "micropay",
pay_type: pay_type,
auth_code: input.value,
source: 300,
})
.then((res) => {
if (res.status == 200 && res.message == "支付成功") {
if (res.status == 200 && (res.message == "支付成功" || res.message == "success")) {
drawer.value = false;
ElMessage({
message: res.message,
message: res.message=='success'?'支付成功':res.message,
type: "success",
});
audioplay(res.data.message);
audioplay(res.data.message||res.data.result.message);
beforeClose();
} else {
if (!res.data.group_order_sn) {
@ -112,27 +132,38 @@ const handleEnter = () => {
codeRef.value?.focus();
});
});
}
};
const orderPay = (id) => {
let query;
if(active.value == 1){
query = {
type: "micropay",
auth_code: input.value,
if (active.value == 1) {
let pay_type;
if (regexWechat.test(input.value)) pay_type = "micropay";
else if (regexPay.test(input.value)) pay_type = "alipayBar";
else {
loading.value = false;
input.value = "";
codeRef.value.focus();
return ElMessage.error("请输入正确的支付码");
}
} else query = {
type: "cash_payment"
}
query = {
type: pay_type,
auth_code: input.value,
};
} else
query = {
type: "cash_payment",
};
orderPayApi(id, query)
.then((res) => {
if (res.status == 200 && res.message == "支付成功") {
if (res.status == 200 && (res.message == "支付成功" || res.message == "success")) {
drawer.value = false;
ElMessage({
message: res.message,
message: res.message=='success'?'支付成功':res.message,
type: "success",
});
audioplay(res.data.message);
audioplay(res.data.message||res.data.result.message);
beforeClose();
} else {
order_id.value = res.data.group_order_id;
@ -152,7 +183,7 @@ const orderPay = (id) => {
const count = ref(0); // 3, 3,
const getOrderStatus = (id) => {
if(!id) return;
if (!id) return;
count.value++;
timecount += 5000;
orderStatusApi({
@ -197,13 +228,13 @@ const getOrderStatus = (id) => {
};
const beforeClose = () => {
window.removeEventListener("keydown", keyboard);
reLoad.value = false;
loading.value = false;
input.value = "";
collection.value = "";
collectionArray.value = [];
codeRef.value?.blur();
document.removeEventListener("keydown", keyboard);
emit("paySuccess");
drawer.value = false;
};
@ -214,6 +245,7 @@ defineExpose({
drawer,
setForm,
setRePay,
beforeClose
});
const collectionArray = ref([]);
@ -259,55 +291,62 @@ const numTap = (item) => {
};
//
const cashBnt = () => {
if(changePrice.value===''||changePrice.value===null||changePrice.value===undefined) return ;
if (
changePrice.value === "" ||
changePrice.value === null ||
changePrice.value === undefined
)
return;
if (order_id.value) orderPay(order_id.value);
else orderCreateApi({
address_id: "",
key: form.value.key,
cart_id: cart_id.value,
pay_type: "cash_payment",
source: 300,
})
.then((res) => {
if (res.status == 200 && res.message == "支付成功") {
drawer.value = false;
ElMessage({
message: res.message,
type: "success",
});
audioplay(res.data.message);
beforeClose();
} else {
if (!res.data.group_order_sn) {
order_id.value = res.data.result.order_id;
collection.value = "";
collectionArray.value = [];
loading.value = false;
return ElMessage({
message: res.message,
type: "error",
});
} else {
order_id.value = res.data.group_order_id;
count.value = 0;
timecount = 0;
getOrderStatus(res.data.group_order_sn);
}
}
else
orderCreateApi({
address_id: "",
key: form.value.key,
cart_id: cart_id.value,
pay_type: "cash_payment",
source: 300,
})
.catch((err) => {
loading.value = false;
});
.then((res) => {
if (res.status == 200 && res.message == "支付成功") {
drawer.value = false;
ElMessage({
message: res.message,
type: "success",
});
audioplay(res.data.message);
beforeClose();
} else {
if (!res.data.group_order_sn) {
order_id.value = res.data.result.order_id;
collection.value = "";
collectionArray.value = [];
loading.value = false;
return ElMessage({
message: res.message,
type: "error",
});
} else {
order_id.value = res.data.group_order_id;
count.value = 0;
timecount = 0;
getOrderStatus(res.data.group_order_sn);
}
}
})
.catch((err) => {
loading.value = false;
});
};
//
const keyboard = (event) => {
let e = event || window.event;
let key = e.keyCode;
if (true) {
event.stopPropagation(); //
event.preventDefault(); //
}
if(key == 37)return changeActive(2);
if(key == 120)return mitt.emit('F9');
if(key == 27)return beforeClose();
event.stopPropagation(); //
event.preventDefault(); //
switch (key) {
case 96:
case 48:
@ -363,6 +402,27 @@ const keyboard = (event) => {
break;
}
};
//
const aleft = () => {
if(!drawer.value) return;
if(active.value==2) return changeActive(1);
};
const aright = () => {
if(!drawer.value) return;
if(active.value==1) return changeActive(2);
};
onMounted(() => {
mitt.on("left", aleft);
mitt.on("right", aright);
});
onUnmounted(() => {
mitt.off("left", aleft);
mitt.off("right", aright);
});
</script>
<template>
@ -384,7 +444,7 @@ const keyboard = (event) => {
:class="{ active: active == 1 }"
@click="changeActive(1)"
>
微信
微信/支付宝
</div>
<div
class="right"
@ -526,12 +586,13 @@ const keyboard = (event) => {
.received {
height: 58px;
padding: 0 20px;
border: 1px solid #eeeeee;
border: 1px solid #1890ff;
box-shadow: 0 0 3px #1890ff;
border-radius: 8px;
background-color: #ffffff;
font-size: 26px;
line-height: 58px;
color: #303133;
color: #333;
}
.balance {

View File

@ -1,6 +1,34 @@
<script setup>
import myHeader from "./myHeader.vue";
import myAside from "./myAside.vue";
import { ref, nextTick, onMounted, onUnmounted } from "vue";
import mitt from "@/utils/mitt.js";
const KeyboardEvent = (e)=>{
console.log('按下', e.keyCode);
if(e.keyCode == 16) mitt.emit('shift');
if(e.keyCode == 120) mitt.emit('F9');
if(e.keyCode == 13) mitt.emit('enter');
if(e.keyCode == 37) mitt.emit('left');
if(e.keyCode == 39) mitt.emit('right');
if(e.keyCode == 38) mitt.emit('up');
if(e.keyCode == 40) mitt.emit('down');
if(e.keyCode == 46) mitt.emit('delete');
if(e.keyCode == 45) mitt.emit('insert');
}
onMounted(()=>{
window.addEventListener('keydown', KeyboardEvent);
console.log('开启键盘监听');
})
onUnmounted(()=>{
window.removeEventListener('keydown', KeyboardEvent);
console.log('关闭键盘监听');
})
</script>
<template>
@ -15,7 +43,7 @@ import myAside from "./myAside.vue";
</el-aside>
<el-main>
<router-view v-slot="{ Component }" class="my-main">
<transition name="el-fade-in-linear">
<transition name="el-zoom-in-top">
<component :is="Component"></component>
</transition>
</router-view>

View File

@ -1,5 +1,7 @@
<script setup>
import { useRoute, useRouter } from 'vue-router'
import { ref, onMounted, onUnmounted } from 'vue'
import mitt from "@/utils/mitt.js";
const router = useRouter()
const route = useRoute()
@ -7,6 +9,24 @@ const route = useRoute()
const navTo = (name) => {
router.push({ name })
}
const list = ref(['home', 'order', 'convert', 'shop']);
const aup = ()=>{
let index = list.value.indexOf(route.name);
if(index>0) navTo(list.value[index-1]);
}
const adown = ()=>{
let index = list.value.indexOf(route.name);
if(index<list.value.length-1) navTo(list.value[index+1]);
}
onMounted(() => {
mitt.on("up", aup);
mitt.on("down", adown);
});
onUnmounted(() => {
mitt.off("aup", aup);
mitt.off("adown", adown);
});
</script>
@ -21,6 +41,10 @@ const navTo = (name) => {
<el-icon size="30"><DataLine /></el-icon>
<div>订单</div>
</div>
<div class="list-item" :class="{'active': route.name=='convert'}" @click="navTo('convert')">
<el-icon size="30"><FullScreen /></el-icon>
<div>核销</div>
</div>
<div class="list-item" :class="{'active': route.name=='shop'}" @click="navTo('shop')">
<el-icon size="30"><ShoppingBag /></el-icon>
<div>商品</div>

View File

@ -10,26 +10,31 @@ const routes = [
component: layout,
redirect: '/home',
children: [
{
path: '/home',
name: 'home',
component: () => import('@/views/home/index.vue'),
},
{
path: '/order',
name: 'order',
component: () => import('@/views/order/index.vue'),
{
path: '/home',
name: 'home',
component: () => import('@/views/home/index.vue'),
},
{
path: '/order',
name: 'order',
component: () => import('@/views/order/index.vue'),
},
{
path: '/orderList',
name: 'orderList',
component: () => import('@/views/order/indexList.vue'),
},
},
{
path: '/shop',
name: 'shop',
component: () => import('@/views/shop/index.vue'),
}
},
{
path: '/convert',
name: 'convert',
component: () => import('@/views/convert/index.vue'),
}
]
},
{

View File

@ -1,5 +1,6 @@
export const audioplay = (text) => {
if(!text) return;
let ssu = new window.SpeechSynthesisUtterance(text);
window.speechSynthesis.speak(ssu);
}

View File

@ -0,0 +1,359 @@
<script setup>
import { ref } from "vue";
import {
orderListApi,
orderStatusApi,
orderLadingApi,
cartListApi,
} from "@/api/store.js";
import { useUserStore } from "@/store/user.js";
import { ElMessage } from "element-plus";
import { useRouter } from "vue-router";
import mitt from "@/utils/mitt.js";
import pay from "@/components/pay.vue";
const userStore = useUserStore();
const router = useRouter();
const formData = ref({});
mitt.on("set-order-detail", (res) => {
formData.value = res;
});
const list = ref([]);
const payRef = ref(null);
const loading = ref(false);
const total = ref(0);
const activeStore = ref(0);
const activeStoreList = ref(["商品信息", "订单详情"]);
const orderLadingSn = ref("");
const dialogVisible = ref(false);
const orderLadingComfirm = (order_sn) => {
orderLadingSn.value = order_sn;
dialogVisible.value = true;
};
const orderLading = () => {
dialogVisible.value = false;
orderLadingApi({
order_sn: orderLadingSn.value,
}).then((res) => {
ElMessage({
message: res.message,
type: "success",
});
router.push({
name: "home",
});
});
};
const rePay = (row) => {
payRef.value.setRePay({
price: row.pay_price,
order_id: row.group_order_id,
});
payRef.value.drawer = true;
};
const getOrderStatus = (id) => {
orderStatusApi({
order_sn: id,
})
.then((res) => {
if (res.data.paid == 1 || res.message == "支付成功") {
ElMessage({
message: res.message,
type: "success",
});
} else {
ElMessage({
message: res.message,
type: "error",
});
}
})
.catch((err) => {});
};
</script>
<template>
<div class="my-order">
<div class="header-nav">
<div
v-for="(item, index) in activeStoreList"
:key="index"
class="nav-item"
:class="{
'nav-item-active': activeStore == index,
'nav-item-radius1': activeStore == index + 1,
'nav-item-radius2': activeStore == index - 1,
}"
@click="activeStore = index"
>
{{ item }}
</div>
<div
class="nav-item"
:class="{
'nav-item-radius2': activeStore == activeStoreList.length - 1,
}"
style="flex: 1"
></div>
</div>
<div class="detail" v-loading="loading">
<div class="table" v-if="formData.orderProduct && activeStore == 0">
<div style="font-size: 0.9rem">
共计
<span style="color: #ff4a00">{{ formData.total_num }}</span> 件商品
</div>
<el-table
style="height: calc(100vh - 100px - 14rem)"
:data="formData.orderProduct"
>
<el-table-column prop="cart_info.product.store_name" label="商品信息">
<template #default="{ row }">
<div style="display: flex; align-items: center">
<el-image
style="height: 3rem; width: 3rem"
:src="
row.cart_info.productAttr.image ||
row.cart_info.product.image
"
></el-image>
<span style="margin-left: 0.5rem">{{
row.cart_info.product.store_name
}}</span>
</div>
</template>
</el-table-column>
<el-table-column
prop="cart_info.productAttr.price"
label="单价"
width="150"
/>
<el-table-column prop="product_num" label="数量" width="150" />
<el-table-column prop="total_price" label="总价" width="150" />
</el-table>
</div>
<div class="table" v-if="formData.orderProduct && activeStore == 1">
<div class="table-title">订单信息</div>
<div class="table-info">
<div class="info-item">
<div class="info-item-title">创建时间:</div>
<div class="info-item-info">{{ formData.create_time }}</div>
</div>
<div class="info-item">
<div class="info-item-title">商品总数:</div>
<div class="info-item-info">{{ formData.total_num }}</div>
</div>
<div class="info-item">
<div class="info-item-title">商品总价:</div>
<div class="info-item-info">¥{{ formData.total_price }}</div>
</div>
<div class="info-item">
<div class="info-item-title">支付时间:</div>
<div class="info-item-info">{{ formData.pay_time }}</div>
</div>
<div class="info-item">
<div class="info-item-title">优惠抵扣:</div>
<div class="info-item-info">¥{{ formData.deduction_price }}</div>
</div>
<div class="info-item">
<div class="info-item-title">实际支付:</div>
<div class="info-item-info">¥{{ formData.pay_price }}</div>
</div>
<div class="info-item">
<div class="info-item-title">支付方式:</div>
<div class="info-item-info">
<span v-if="formData.pay_type == 11">微信收款</span>
<span v-if="formData.pay_type == 12">现金支付</span>
<span v-if="formData.pay_type == 0">余额支付</span>
<span v-if="formData.pay_type == 1">微信支付</span>
<span v-if="formData.pay_type == 9">商户余额支付</span>
<span v-if="formData.pay_type == 10">对公转账</span>
</div>
</div>
</div>
<div class="table-title" v-if="formData.service_info">收银员信息</div>
<div
class="table-info"
v-if="formData.service_info"
style="flex-direction: column"
>
<div class="info-item">
<div class="info-item-title">头像:</div>
<div class="info-item-info">
<el-image
style="height: 4rem; width: 4rem; border-radius: 50%"
:src="formData.service_info.avatar"
></el-image>
</div>
</div>
<div class="info-item">
<div class="info-item-title">昵称:</div>
<div class="info-item-info">
{{ formData.service_info.nickname }}
</div>
</div>
<div class="info-item">
<div class="info-item-title">账号:</div>
<div class="info-item-info">
{{ formData.service_info.account }}
</div>
</div>
</div>
</div>
<div class="footer">
<div class="info">
<!-- <div class="ser">收银员: {{ formData.service_info.nickname }}</div> -->
<div class="price">
实付: <span>¥{{ formData.pay_price }}</span>
</div>
</div>
<div class="handle" v-if="formData.verify_status==0">
<el-button class="btn" type="warning" @click="rePay(formData)"
>核销</el-button
>
</div>
<div class="handle" v-else>已支付</div>
</div>
</div>
<pay ref="payRef" />
<el-dialog v-model="dialogVisible" title="提示" width="500">
<span
>提单前请清空购物车, 避免提单的商品与购物车商品混合,
请确保购物车内无数据后再进行提单</span
>
<template #footer>
<div class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button v-if="cartCount > 0" @click="goHome">
前去清空购物车
</el-button>
<el-button v-else type="primary" @click="orderLading">
确认提单
</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<style scoped lang="scss">
.my-order {
border-radius: 1.2rem;
height: 100%;
flex: 1;
background-color: #fff;
position: relative;
overflow: hidden;
.header-nav {
display: flex;
background: linear-gradient(
to bottom,
#f5f5f5 50%,
#fff 50%
); /* 创建渐变背景 */
.nav-item {
height: 4rem;
width: 8rem;
cursor: pointer;
display: flex;
justify-content: center;
align-items: center;
font-size: 1.2rem;
background-color: #f5f5f5;
border-radius: 1rem 1rem 0 0;
}
.nav-item-active {
background-color: #fff;
position: relative;
transition: 300ms;
}
.nav-item-radius1 {
border-radius: 0 0 1rem 0;
}
.nav-item-radius2 {
border-radius: 0 0 0 1rem;
}
}
.detail {
height: calc(100vh - 100px - 4rem);
box-sizing: border-box;
position: relative;
.table {
padding: 1rem;
padding-bottom: 6rem;
.table-title {
font-weight: bold;
padding-top: 1rem;
}
.table-info {
display: flex;
flex-wrap: wrap;
color: #777;
font-size: 0.9rem;
padding-bottom: 2rem;
border-bottom: 1px solid #eee;
&:last-child {
border-bottom: none;
}
.info-item {
width: 33%;
display: flex;
padding-top: 1rem;
.info-item-title {
flex-shrink: 0;
padding-right: 1rem;
}
}
}
}
.footer {
height: 6rem;
box-sizing: border-box;
padding: 0 1.5rem;
position: absolute;
bottom: 0;
left: 0;
width: 100%;
box-shadow: 0 -1px 10px #eee;
display: flex;
justify-content: space-between;
align-items: center;
background-color: #fff;
.info {
display: flex;
align-items: flex-end;
.ser {
font-weight: bold;
margin-right: 1rem;
}
.price {
margin-right: 1rem;
span {
color: #ff4a00;
font-size: 1.2rem;
font-weight: bold;
}
}
}
.handle {
.btn {
border-radius: 4rem;
padding: 1.2rem;
}
}
}
}
}
</style>

View File

@ -0,0 +1,290 @@
<script setup>
import { ref, watch } from "vue";
import {
verifierOrderListApi,
orderStatusApi,
orderLadingApi,
cartListApi,
} from "@/api/store.js";
import { useUserStore } from "@/store/user.js";
import { ElMessage } from "element-plus";
import { useRouter } from "vue-router";
import mitt from "@/utils/mitt.js";
const userStore = useUserStore();
const list = ref([]);
const tabPosition = ref(1); // 1-, 2-
const changeTabPosition = (e) => {
where.value.page = 1;
loadEnd.value = false;
loading.value = false;
orderList.value = [];
getOrderList(true);
};
const payRef = ref(null);
const where = ref({
page: 1,
limit: 20,
search_info: ''
});
const loading = ref(false);
const total = ref(0);
const activeStore = ref(0);
const loadEnd = ref(false);
const orderList = ref([]);
const getOrderList = (reload=false) => {
if(!where.value.search_info) return ElMessage.error("请输入订单编号");
if(reload) where.value.page = 1;
else if(loadEnd.value || loading.value) return;
loading.value = true;
if (tabPosition.value == 1) where.value.paid = null;
if (tabPosition.value == 2) where.value.paid = 0;
orderList.value = [];
mitt.emit("set-order-detail", {});
verifierOrderListApi(userStore.userInfo.service.mer_id, where.value.search_info).then((res) => {
orderList.value = [res.data];
mitt.emit("set-order-detail", orderList.value[0]);
// if(reload) orderList.value = res.data.list;
// else orderList.value = [...orderList.value, ...res.data.list];
// if(res.data.list.length < where.value.limit) loadEnd.value = true;
// else where.value.page++;
// activeStore.value = 0;
// mitt.emit("set-order-detail", orderList.value[0]);
// total.value = res.data.count;
loading.value = false;
}).catch(err=>{
loadEnd.value = true;
loading.value = false;
});
};
const setForm = (item, index) => {
activeStore.value = index;
mitt.emit("set-order-detail", item);
}
const setCode = (code)=>{
where.value.search_info = code;
getOrderList(true);
}
defineExpose({
setCode,
})
</script>
<template>
<div class="my-order">
<div class="header-nav">
<div class="nav-item">核销订单</div>
<div class="nav-item-clear">
<el-button type="primary" size="small">返回上一页</el-button>
</div>
</div>
<div class="header-input">
<el-input v-model="where.search_info" placeholder="请输入订单编号" @keydown.enter="getOrderList(true)" clearable>
<template #append>
<el-button
type="primary"
style="
background-color: #1890ff;
color: #fff;
border-radius: 0 5px 5px 0;
"
@click="getOrderList(true)"
>搜索</el-button
>
</template>
</el-input>
</div>
<div class="order-list" v-loading="loading" v-infinite-scroll="getOrderList"
infinite-scroll-distance="300"
infinite-scroll-delay="500"
infinite-scroll-immediate="false"
style="overflow: auto">
<div class="item" :class="{'item-active': activeStore == index}" v-for="(item, index) in orderList" :key="index" @click="setForm(item, index)">
<div class="top">
<div class="sn" :class="item.pay_type===11 || item.pay_type===12 ? 'cahier' : 'cahier2'">单号: {{ item.order_sn }}</div>
<div class="create-time">{{ item.create_time }}</div>
</div>
<div class="shop">
<div class="left" v-if="item.orderProduct">
<el-image
v-for="(shop, imgkey) in item.orderProduct.slice(0, 5)"
:key="imgkey"
:src="
shop.cart_info.productAttr.image || shop.cart_info.product.image
"
class="shop-img"
></el-image>
<div v-if="item.orderProduct.length == 1" class="shop-name">
{{ item.orderProduct[0].cart_info.product.store_name }}
</div>
</div>
<div class="right">
<div class="money">¥{{ item.pay_price }}</div>
<div class="count">{{ item.total_num }}件商品</div>
</div>
</div>
<div class="bottom">
<div class="pay">
<div v-if="item.paid">
已支付
<span v-if="item.pay_type == 12">(现金支付)</span>
<span v-if="item.pay_type == 11">(微信支付)</span>
<span v-if="item.pay_type == 0">(余额支付)</span>
<span v-if="item.pay_type == 1">(微信支付)</span>
<span v-if="item.pay_type == 9">(商户余额支付)</span>
<span v-if="item.pay_type == 10">(对公转账)</span>
</div>
<div v-else style="color: #ff4a00;">未支付</div>
</div>
<div class="cashier" v-if="item.service_info">收银员: {{ item.service_info.nickname }}</div>
<div class="cashier" v-else>平台订单</div>
</div>
</div>
<div v-if="loadEnd" class="load-end">没有更多了</div>
<div v-if="loading" class="load-end">加载中...</div>
</div>
</div>
</template>
<style scoped lang="scss">
.my-order {
border-radius: 1.2rem;
height: 100%;
background-color: #fff;
width: 30rem;
position: relative;
overflow: hidden;
.header-nav {
display: flex;
justify-content: space-between;
padding: 1rem;
height: 1.5rem;
span {
color: #ff4a00;
}
.nav-item-clear {
display: flex;
align-items: center;
font-size: 0.8rem;
cursor: pointer;
}
}
.header-input {
padding: 1rem;
padding-top: 0;
height: 2.5rem;
border-bottom: 1px solid #eee;
}
.order-list {
height: calc(100vh - 100px - 8.2rem);
overflow-y: auto;
.item {
padding: 1rem;
border-bottom: 1px solid #eee;
.top {
display: flex;
justify-content: space-between;
align-items: flex-end;
.sn {
font-weight: bold;
font-size: 0.9rem;
}
.cahier{
&::before {
content: "收银";
font-weight: 400;
font-size: 0.7rem;
padding: 0.1rem 0.1rem;
margin-right: 0.2rem;
border: 1px solid #2ec479;
color: #2ec479;
border-radius: 3px;
}
}
.cahier2{
&::before {
content: "平台";
font-weight: 400;
font-size: 0.7rem;
padding: 0.1rem 0.1rem;
margin-right: 0.2rem;
border: 1px solid #ff4a00;
color: #ff4a00;
border-radius: 3px;
}
}
.create-time {
font-size: 0.8rem;
}
}
.shop {
display: flex;
justify-content: space-between;
.right {
flex-shrink: 0;
display: flex;
flex-direction: column;
justify-content: center;
.money {
font-size: 1rem;
color: #ff4a00;
font-weight: bold;
}
.count {
font-size: 0.7rem;
color: #999;
}
}
.left {
height: 4.5rem;
display: flex;
align-items: center;
.shop-img {
width: 3.5rem;
height: 3.5rem;
border-radius: 0.3rem;
margin-right: 0.4rem;
}
.shop-name {
font-size: 0.9rem;
color: #333;
display: -webkit-box;
-webkit-box-orient: vertical;
overflow: hidden;
/* 将文本限制为三行 */
-webkit-line-clamp: 3;
}
}
}
.bottom{
display: flex;
justify-content: space-between;
font-size: 0.9rem;
color: #777;
}
}
.item-active{
background-color: #efefef;
}
}
.load-end{
text-align: center;
padding: 1rem;
color: #333;
font-size: 0.8rem;
}
}
</style>, watch

115
src/views/convert/index.vue Normal file
View File

@ -0,0 +1,115 @@
<script setup>
import order from "./component/order.vue";
import detail from "./component/detail.vue";
import padding from "@/components/padding.vue";
import { ref, nextTick, onMounted } from "vue";
import { ElMessage } from "element-plus";
const type = ref(1);
const code = ref("1372640275688400");
const inputRef = ref(null);
const orderRef = ref(null);
const handleEnterKey = () => {
if(code.value == "") return ElMessage.error("请输入订单编号");
type.value = 2;
nextTick(() => {
orderRef.value.setCode(code.value);
});
};
onMounted(() => {
nextTick(() => {
inputRef.value.focus();
});
});
</script>
<template>
<div class="my-card">
<div class="my-code" v-show="type == 1">
<h2>订单核销</h2>
<div class="box">
<input
ref="inputRef"
v-model="code"
placeholder=""
class="input"
@keyup.enter="handleEnterKey"
/>
<div class="btn" @click="handleEnterKey">查询</div>
</div>
</div>
<order v-show="type == 2" style="flex-shrink: 0" ref="orderRef" />
<padding v-show="type == 2" />
<detail v-show="type == 2" ref="detailRef" />
</div>
</template>
<style lang="scss">
.my-card {
display: flex;
.my-code {
height: 70%;
width: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
.box {
width: 38rem;
position: relative;
.input {
width: 100%;
height: 3rem;
box-sizing: border-box;
border-radius: 4rem;
border: 1px solid #1890ff;
padding: 0 1rem;
font-size: 1.1rem;
&:focus {
outline: none;
box-shadow: 0 0 5px #1890ff; /* 可选:添加阴影效果以突出显示聚焦状态 */
transition: all 0.2s ease-in-out;
}
}
.btn {
position: absolute;
right: 0;
top: 0;
width: 5rem;
height: 100%;
border-radius: 0 4rem 4rem 0;
background: #1890ff;
color: #fff;
display: flex;
justify-content: center;
align-items: center;
}
}
}
}
/* 修改滚动条的样式 */
::-webkit-scrollbar {
width: 5px; /* 设置滚动条的宽度 */
}
/* 设置滚动条的轨道样式 */
::-webkit-scrollbar-track {
background-color: #f1f1f1; /* 设置轨道的背景色 */
/* margin: 20px 0; */
}
/* 设置滚动条的滑块样式 */
::-webkit-scrollbar-thumb {
background-color: #ccc; /* 设置滑块的背景色 */
border-radius: 5px; /* 设置滑块的圆角 */
}
/* 设置滚动条鼠标悬停时的滑块样式 */
::-webkit-scrollbar-thumb:hover {
background-color: #999; /* 设置鼠标悬停时滑块的背景色 */
}
</style>

View File

@ -1,10 +1,12 @@
<script setup>
import { ref } from "vue";
import { ref, onMounted, onUnmounted } from "vue";
import { cartListApi, cartDeleteApi, cartChangeApi } from "@/api/store.js";
import price from "./price.vue";
import mitt from "@/utils/mitt.js";
const list = ref([]);
const allPrice = ref(0); //
const costPrice = ref(0); //
const discounts = ref(0); //
const clearAll = () => {
@ -32,6 +34,7 @@ const deleteShop = (arr) => {
const getList = () => {
allPrice.value = 0;
costPrice.value = 0;
discounts.value = 0;
cartListApi({
source: 300,
@ -41,6 +44,7 @@ const getList = () => {
list.value.forEach((item) => {
allPrice.value += item.productAttr.price * item.cart_num;
});
costPrice.value = allPrice.value;
} else list.value = [];
});
};
@ -60,6 +64,7 @@ const changeCartNum = (val, old) => {
list.value.forEach((item) => {
allPrice.value += item.productAttr.price * item.cart_num;
});
costPrice.value = allPrice.value;
});
};
@ -74,14 +79,14 @@ const editItem = (id, data)=>{
}
const changeAllPrice = (price)=>{
discounts.value = allPrice.value - price;
discounts.value = costPrice.value - price;
allPrice.value = +price;
}
const nowPrice = ref(0);
const priceRef = ref(null);
const showPrice = ()=>{
priceRef.value.show(true, allPrice.value);
priceRef.value.show(true, costPrice.value);
}
defineExpose({
@ -91,6 +96,21 @@ defineExpose({
discounts,
allPrice
});
//
const ainsert = () => {
if(priceRef.value.dialogVisible) priceRef.value.dialogVisible = false;
else showPrice();
};
onMounted(() => {
mitt.on("insert", ainsert);
});
onUnmounted(() => {
mitt.off("insert", ainsert);
});
</script>
<template>

View File

@ -1,13 +1,22 @@
<script setup>
import { ref } from 'vue'
import { ElMessageBox } from 'element-plus'
import { ref, nextTick, onMounted, onUnmounted } from 'vue'
import { ElMessage } from 'element-plus'
import mitt from "@/utils/mitt.js";
const dialogVisible = ref(false)
const priceRef = ref(null)
const allPrice = ref(0)
const show = (e, p = 0)=>{
priceInfo.value.nowPrice = "";
priceInfo.value.percentage = "";
dialogVisible.value = e;
allPrice.value = p;
nextTick(()=>{
setTimeout(()=>{
priceRef.value?.focus()
},150)
})
}
const priceInfo = ref({
@ -24,14 +33,33 @@ const inputPercentage = (e)=>{
const emit = defineEmits(['submit'])
const submit = ()=>{
if(priceInfo.value.nowPrice<=0){
priceInfo.value.nowPrice = "";
priceInfo.value.percentage = "";
return ElMessage.error("金额不能小于0");
}
emit('submit', priceInfo.value.nowPrice);
dialogVisible.value = false;
}
defineExpose({
show
show,
dialogVisible
})
//
const aenter = () => {
if(!dialogVisible.value) return;
else submit();
};
onMounted(() => {
mitt.on("enter", aenter);
});
onUnmounted(() => {
mitt.off("enter", aenter);
});
</script>
<template>
@ -42,10 +70,10 @@ defineExpose({
>
<div class="price">
<div class="flex">
<el-input style="flex: 3;margin: 10px;height: 2.5rem;" v-model="priceInfo.nowPrice" placeholder="请输入改价后的价格" @input="inputPrice">
<el-input ref="priceRef" style="flex: 3;margin: 10px;height: 2.5rem;" v-model="priceInfo.nowPrice" :min="0.01" type="number" placeholder="请输入改价后的价格" @input="inputPrice">
<template #suffix></template>
</el-input>
<el-input style="flex: 2;margin: 10px;height: 2.5rem;" v-model="priceInfo.percentage" placeholder="请输入比例" @input="inputPercentage">
<el-input style="flex: 2;margin: 10px;height: 2.5rem;" v-model="priceInfo.percentage" :min="0.01" type="number" placeholder="请输入比例" @input="inputPercentage">
<template #suffix>%</template>
</el-input>
</div>
@ -76,4 +104,4 @@ defineExpose({
display: flex;
}
}
</style>
</style>nextTick,

View File

@ -1,7 +1,8 @@
<script setup>
import { ref } from 'vue'
import { ref, onMounted, onUnmounted } from 'vue'
import { ElMessageBox } from 'element-plus'
import { getAttrValue } from "@/api/shop.js"
import mitt from "@/utils/mitt.js";
const dialogVisible = ref(false)
@ -55,6 +56,36 @@ defineExpose({
setForm
})
//
const aleft = () => {
if(!dialogVisible.value) return;
let index = form.value.attr.indexOf(active.value);
if(index>0) return changeActive(form.value.attr[index-1]);
};
const aright = () => {
if(!dialogVisible.value) return;
let index = form.value.attr.indexOf(active.value);
if(index<form.value.attr.length-1) return changeActive(form.value.attr[index+1]);
};
const aenter = ()=>{
if(!dialogVisible.value) return;
changeItem();
}
onMounted(() => {
mitt.on("left", aleft);
mitt.on("right", aright);
mitt.on("enter", aenter);
});
onUnmounted(() => {
mitt.off("left", aleft);
mitt.off("right", aright);
mitt.off("enter", aenter);
});
const close = ()=>{
console.log('sss');
}
</script>
<template>

View File

@ -1,5 +1,6 @@
<script setup>
import { ref, watch } from "vue";
import { ref, watch, onMounted, onUnmounted, nextTick } from "vue";
import mitt from "@/utils/mitt.js";
const props = defineProps({
storeList: {
@ -8,6 +9,8 @@ const props = defineProps({
},
});
const codeRef = ref(null);
const emit = defineEmits(["getStoreList", "changeItem", "loadMore"]);
const bar_code = ref("");
@ -29,20 +32,46 @@ const handleEnter = () => {
};
defineExpose({
bar_code
})
bar_code,
});
const isfocus = ref(false);
const focus = () => {
isfocus.value = true;
};
const blur = () => {
isfocus.value = false;
};
//
const ashift = () => {
codeRef.value?.focus();
};
onMounted(() => {
nextTick(() => {
codeRef.value?.focus();
focus();
});
mitt.on("shift", ashift);
});
onUnmounted(() => {
mitt.off("shift", ashift);
});
</script>
<template>
<div class="my-order">
<div class="header-nav">
<div class="header-nav" :class="{ 'input-focus': isfocus }">
<div class="nav-item-label">搜索</div>
<div class="nav-item-input">
<el-input
v-model="bar_code"
placeholder="搜索商品名称/ID/唯一码或点击聚焦扫码"
clearable
ref="codeRef"
@focus="focus"
@blur="blur"
@keyup.enter="handleEnter"
@clear="handleEnter"
/>
@ -72,7 +101,7 @@ defineExpose({
<div class="shop-price">
¥<span>{{ item.price }}</span>
</div>
<div class="no-stock" v-if="item.stock==0">
<div class="no-stock" v-if="item.stock == 0">
<div>
<span>暂无</span>
<span>库存</span>
@ -98,6 +127,7 @@ defineExpose({
justify-content: space-between;
overflow: hidden;
width: auto;
border: 1px solid rgba($color: #000000, $alpha: 0);
.nav-item-label {
width: 5rem;
@ -129,8 +159,12 @@ defineExpose({
}
}
.input-focus {
border: 1px solid #1890ff;
}
.shop-list {
height: calc(100vh - 100px - 3rem);
height: calc(100vh - 100px - 3.1rem);
width: auto;
overflow-y: auto;
box-sizing: border-box;
@ -190,7 +224,7 @@ defineExpose({
display: flex;
justify-content: center;
align-items: center;
div{
div {
background-color: #4e4e4e;
color: #fff;
border-radius: 50%;
@ -206,3 +240,4 @@ defineExpose({
}
}
</style>
, nextTick

View File

@ -4,10 +4,12 @@ import shop from "./component/shop.vue";
import padding from "@/components/padding.vue";
import pupop from "./component/pupop.vue";
import pay from "@/components/pay.vue";
import { ref, nextTick } from "vue";
import { ref, nextTick, onMounted, onUnmounted } from "vue";
import { cartCreateApi, orderCheckApi } from "@/api/store.js";
import { storeListApi } from "@/api/shop.js";
import { useUserStore } from "@/store/user.js";
import { useRoute } from "vue-router";
import mitt from "@/utils/mitt.js";
const pupopRef = ref(null);
const orderRef = ref(null);
@ -17,6 +19,7 @@ const payRef = ref(null);
const storeList = ref([]);
const userStore = useUserStore();
const route = useRoute();
const where = ref({
page: 0,
@ -28,30 +31,34 @@ const getStoreList = (data) => {
...where.value,
...data,
};
if (data.bar_code && isAllDigits(data.bar_code)) {
if (data.bar_code) {
storeList.value = [];
where.value.page = 1;
}
storeListApi(userStore.userInfo.service.mer_id, where.value).then((res) => {
if(res.data?.list?.length < where.value.limit) loadEnd.value = true;
if (res.data?.list?.length < where.value.limit) loadEnd.value = true;
let list = res.data.list.map((item) => {
item.attr = Object.keys(item.sku);
return item;
});
storeList.value = storeList.value.concat(list);
if (data.bar_code && storeList.value.length == 1 && isAllDigits(data.bar_code)) {
shopRef.value.bar_code = ''
cartAddInfo(storeList.value[0], storeList.value[0].attr[0]);
if (
data.bar_code &&
storeList.value.length == 1 &&
isAllDigits(data.bar_code)
) {
shopRef.value.bar_code = "";
changeItem(storeList.value[0], storeList.value[0].attr[0]);
}
});
};
const loadEnd = ref(false);
const loadMore = (data)=>{
if(loadEnd.value) return;
const loadMore = (data) => {
if (loadEnd.value) return;
where.value.page++;
getStoreList(data);
}
};
function isAllDigits(str) {
return /^\d+$/.test(str);
@ -76,22 +83,23 @@ const cartAddInfo = (item, change = "") => {
};
const changeItem = (item, change) => {
if(!item.attr || item.attr.length == 0 || item.attr.length==1) return cartAddInfo(item, item.attr[0] ? item.attr[0] : '');
else if(change) return cartAddInfo(item, change);
if (!item.attr || item.attr.length == 0 || item.attr.length == 1)
return cartAddInfo(item, item.attr[0] ? item.attr[0] : "");
else if (change) return cartAddInfo(item, change);
else {
pupopRef.value.setForm(item, 'add');
pupopRef.value.setForm(item, "add");
pupopRef.value.show(true);
}
}
};
const editItem = (id, data) => {
orderRef.value.editItem(id, data);
}
};
const editPupop = (item) => {
pupopRef.value.setForm(item, 'edit');
pupopRef.value.setForm(item, "edit");
pupopRef.value.show(true);
}
};
//
const checkOut = () => {
@ -101,8 +109,8 @@ const checkOut = () => {
use_coupon: {},
use_integral: false,
cart_id: cart_id,
}
if(orderRef.value.discounts>0){
};
if (orderRef.value.discounts > 0) {
query.deduction_price = orderRef.value.discounts.toFixed(2);
query.pay_type = "micropay";
}
@ -121,15 +129,29 @@ const goPay = () => {
payRef.value.drawer = true;
};
const paySuccess = ()=>{
const paySuccess = () => {
orderRef.value.getList();
}
};
//
const aF9 = () => {
console.log("F9");
if (route.name != "home") return;
if (!payRef.value.drawer && orderRef.value.list.length > 0) goPay();
else payRef.value.beforeClose();
};
onMounted(() => {
mitt.on("F9", aF9);
});
onUnmounted(() => {
mitt.off("F9", aF9);
});
</script>
<template>
<div class="my-card">
<order ref="orderRef" @goPay="goPay" @editPupop="editPupop"/>
<order ref="orderRef" @goPay="goPay" @editPupop="editPupop" />
<padding />
<shop
ref="shopRef"
@ -139,8 +161,8 @@ const paySuccess = ()=>{
@changeItem="changeItem"
@loadMore="loadMore"
/>
<pupop ref="pupopRef" @changeItem="changeItem" @editItem="editItem"/>
<pay ref="payRef" @paySuccess="paySuccess"/>
<pupop ref="pupopRef" @changeItem="changeItem" @editItem="editItem" />
<pay ref="payRef" @paySuccess="paySuccess" />
</div>
</template>
@ -170,3 +192,4 @@ const paySuccess = ()=>{
background-color: #999; /* 设置鼠标悬停时滑块的背景色 */
}
</style>
, onMounted, onUnmounted

View File

@ -103,7 +103,7 @@ const setForm = (item, index) => {
style="overflow: auto">
<div class="item" :class="{'item-active': activeStore == index}" v-for="(item, index) in orderList" :key="index" @click="setForm(item, index)">
<div class="top">
<div class="sn" :class="{'cahier': item.pay_type==11 || item.pay_type==12}">单号: {{ item.order_sn }}</div>
<div class="sn" :class="item.pay_type===11 || item.pay_type===12 ? 'cahier' : 'cahier2'">单号: {{ item.order_sn }}</div>
<div class="create-time">{{ item.create_time }}</div>
</div>
<div class="shop">
@ -205,6 +205,18 @@ const setForm = (item, index) => {
border-radius: 3px;
}
}
.cahier2{
&::before {
content: "平台";
font-weight: 400;
font-size: 0.7rem;
padding: 0.1rem 0.1rem;
margin-right: 0.2rem;
border: 1px solid #ff4a00;
color: #ff4a00;
border-radius: 3px;
}
}
.create-time {
font-size: 0.8rem;
}