优化新版台桌显示

This commit is contained in:
gyq
2026-03-28 17:37:40 +08:00
parent 32d150fd15
commit a5fdbd0c13
5 changed files with 540 additions and 76 deletions

File diff suppressed because one or more lines are too long

View File

@@ -227,11 +227,11 @@ async function printOrderLable(isBefore = false) {
printStore.pushReceiptData(commOrderPrintData({ ...data, isBefore: isBefore })); printStore.pushReceiptData(commOrderPrintData({ ...data, isBefore: isBefore }));
} else { } else {
// 本地没有可用打印机使用云打印机 // 本地没有可用打印机使用云打印机
// await orderPrint({ await orderPrint({
// type: isBefore ? 1 : 0, type: isBefore ? 1 : 0,
// id: orderId, id: orderId,
// }); });
// ElMessage.success(`云打印${isBefore ? '预' : ''}结算单成功`); ElMessage.success(`云打印${isBefore ? '预' : ''}结算单成功`);
} }
} }
} catch (error) { } catch (error) {

View File

@@ -1,7 +1,7 @@
<!-- 空闲台桌 --> <!-- 空闲台桌 -->
<template> <template>
<div class="table_wrap"> <div class="table_wrap">
<div class="header"> <!-- <div class="header">
<span class="t">{{ props.tableInfo.name }}</span> <span class="t">{{ props.tableInfo.name }}</span>
<div class="close" @click="close"> <div class="close" @click="close">
<el-icon class="icon"> <el-icon class="icon">
@@ -14,15 +14,20 @@
<Clock /> <Clock />
</el-icon> </el-icon>
<span class="t">{{tableStatusList.find(val => val.type == props.tableInfo.status).label}}</span> <span class="t">{{tableStatusList.find(val => val.type == props.tableInfo.status).label}}</span>
</div> </div> -->
<div class="cart" v-loading="payLoading" v-if="props.tableInfo.status == 'unsettled'"> <div class="cart" v-loading="payLoading" v-if="props.tableInfo.status == 'unsettled'">
<div class="cart_list"> <div class="cart_list">
<div class="item" v-for="item in cartList" :key="item.id"> <div class="item" v-for="item in cartList" :key="item.id">
<div class="top"> <div class="top">
<span class="name">{{ item.productName }}</span> <span class="name">{{ item.productName }}</span>
<span class="n">x{{ item.num }}</span> <span class="n">x{{ item.num - item.returnNum }}</span>
<span class="p">{{ item.price }}</span> <span class="p">{{ item.price }}</span>
</div> </div>
<div class="top" v-if="item.returnNum > 0" style="font-size: 12px;color: #999;">
<span class="name">[退菜]</span>
<span class="n">x{{ item.returnNum }}</span>
<span class="p" color="color:var(--el-color-danger)">-{{ item.returnAmount }}</span>
</div>
<div class="tag_wrap" v-if="item.skuName"> <div class="tag_wrap" v-if="item.skuName">
<div class="tag" v-for="item in item.skuName.split(',')"> <div class="tag" v-for="item in item.skuName.split(',')">
{{ item }} {{ item }}
@@ -182,7 +187,7 @@ async function getOrderDetail() {
let total = 0 let total = 0
res.cartList.forEach(item => { res.cartList.forEach(item => {
total += +item.payAmount total += +item.payAmount - (item.returnAmount || 0)
}) })
orderInfo.value.orderAmount = formatDecimal(total) orderInfo.value.orderAmount = formatDecimal(total)
} }
@@ -256,7 +261,8 @@ onMounted(() => {
<style scoped lang="scss"> <style scoped lang="scss">
.table_wrap { .table_wrap {
padding: 20px; // padding: 20px;
padding-top: 14px;
.header { .header {
display: flex; display: flex;
@@ -298,14 +304,14 @@ onMounted(() => {
} }
.cart { .cart {
height: calc(100vh - 160px); height: calc(100vh - 75px);
display: flex; display: flex;
flex-direction: column; flex-direction: column;
.cart_list { .cart_list {
flex: 1; flex: 1;
border-radius: 6px; border-radius: 6px;
background-color: #efefef; background-color: #fff;
padding: 0 var(--el-font-size-base); padding: 0 var(--el-font-size-base);
overflow-y: auto; overflow-y: auto;

View File

@@ -5,12 +5,24 @@
<div class="menus"> <div class="menus">
<div class="item" :class="{ active: tabActive == index }" v-for="(item, index) in tabAreas" <div class="item" :class="{ active: tabActive == index }" v-for="(item, index) in tabAreas"
:key="item.id" @click="tabChange(item, index)"> :key="item.id" @click="tabChange(item, index)">
<div class="dot" :style="{ backgroundColor: item.color }" v-if="item.color"></div>
<el-text>{{ item.label }}</el-text> <el-text>{{ item.label }}</el-text>
</div> </div>
</div> </div>
<!-- <div class="all"> <!-- <div class="all">
<el-button type="link" icon="Clock">预定管理</el-button> <el-button type="link" icon="Clock">预定管理</el-button>
</div> --> </div> -->
<div class="order_info">
<div class="title">
<el-icon>
<Tickets />
</el-icon>
<el-text>未结账</el-text>
</div>
<el-text type="primary">
{{ orderInfo.num }} | {{ orderInfo.personNum }} | {{ formatDecimal(orderInfo.amount) }}
</el-text>
</div>
</div> </div>
<div class="tab_container"> <div class="tab_container">
<div class="tab_head"> <div class="tab_head">
@@ -22,25 +34,28 @@
</div> </div>
<div class="overflow_y" v-loading="loading"> <div class="overflow_y" v-loading="loading">
<div class="tab_list"> <div class="tab_list">
<div class="item" :class="{ active: tableItemActive == index }" <div class="item"
:style="{ '--color': tableStatusList.find(val => val.type == item.status).color }" :style="{ '--color': tableStatusList.find(val => val.type == item.status).color }"
v-for="(item, index) in tableList" :key="item.id" @click="slectTableHandle(index, item)"> v-for="(item, index) in tableList" :key="item.id" @click="slectTableHandle(index, item)">
<div class="tab_title" :class="`${item.status}`"> <div class="tab_title" :class="`${item.status}`">
<span>{{ item.name }}</span> <span>{{ item.areaName }} | {{ item.name }}</span>
<span>0/{{ item.maxCapacity }}</span> <span>{{ item.personNum || 0 }}/{{ item.maxCapacity }}</span>
</div> </div>
<div class="tab_cont"> <div class="tab_cont">
<el-icon class="icon" v-if="item.status != 'using'"> <div class="using"
<CircleClose /> v-if="item.status == 'unsettled' || item.status == 'unsettled' || item.status == 'paying' || item.status == 'settled' || item.status == 'unpaid'">
</el-icon> <div class="t1">{{ formatDecimal(+item.orderAmount) }}</div>
<div class="using" v-else> <div class="t2">
<div class="t1">开台中</div> <el-icon color="#333">
<!-- <div class="t2">
<el-icon>
<Timer /> <Timer />
</el-icon> </el-icon>
<span>{{ countTime(item.updatedAt) }}</span> <span :key="refreshKey">{{ countTime(item.orderCreateTime) }}</span>
</div> --> </div>
</div>
<div class="icon_wrap" v-else>
<el-icon class="icon">
<CircleClose />
</el-icon>
</div> </div>
</div> </div>
</div> </div>
@@ -49,19 +64,22 @@
<el-empty description="空空如也~" /> <el-empty description="空空如也~" />
</div> </div>
</div> </div>
<div class="pagination"> <!-- <div class="pagination">
<el-pagination background v-model:current-page="query.page" :pager-count="5" <el-pagination background v-model:current-page="query.page" :pager-count="5"
layout=" pager, jumper, total" :total="query.total" layout=" pager, jumper, total" :total="query.total"
@current-change="shopTableAjax"></el-pagination> @current-change="shopTableAjax"></el-pagination>
</div> -->
</div> </div>
</div> </div>
</div> <el-drawer v-model="drawerVisible"
<div class="right_card card"> :title="`${tableList[tableItemActive].name} | ${tableList[tableItemActive].areaName}`" size="350px"
<!-- 台桌统计 --> v-if="tableList.length">
<tableInfo :tableInfo="tableList[tableItemActive]" @success="paySuccess" />
</el-drawer>
<!-- <div class="right_card card">
<countCard v-if="!slectTable.id" /> <countCard v-if="!slectTable.id" />
<!-- 台桌信息 -->
<tableInfo v-else :tableInfo="slectTable" @close="slectTableClose" @success="paySuccess" /> <tableInfo v-else :tableInfo="slectTable" @close="slectTableClose" @success="paySuccess" />
</div> </div> -->
</div> </div>
</template> </template>
@@ -69,9 +87,16 @@
import { shopArea, shopTable } from "@/api/account.js"; import { shopArea, shopTable } from "@/api/account.js";
import countCard from '@/views/table/components/countCard.vue' import countCard from '@/views/table/components/countCard.vue'
import tableInfo from '@/views/table/components/tableInfo.vue' import tableInfo from '@/views/table/components/tableInfo.vue'
import { ref, onMounted } from 'vue' import { ref, onMounted, onUnmounted } from 'vue'
import { dayjs } from 'element-plus' import { dayjs } from 'element-plus'
import tableStatusList from './statusList.js' import tableStatusList from './statusList.js'
import { formatDecimal } from '@/utils/index.js'
const orderInfo = ref({
num: 0,
personNum: 0,
amount: 0
})
const tabActive = ref(0) const tabActive = ref(0)
const tabAreas = ref([ const tabAreas = ref([
@@ -82,18 +107,22 @@ const tabAreas = ref([
{ {
label: '空闲', label: '空闲',
type: 'idle', type: 'idle',
color: "#187CAA",
}, },
{ {
label: '使用中', label: '使用中',
type: 'unsettled' type: 'unsettled',
color: "#DD3F41",
}, },
{ {
label: '待清理', label: '待清理',
type: 'settled', type: 'settled',
color: "#FF9500",
}, },
{ {
label: '已预订', label: '已预订',
type: 'subscribe', type: 'subscribe',
color: "#58B22C",
} }
]) ])
@@ -110,7 +139,7 @@ const tableList = ref([])
// 所选区域 // 所选区域
const area = ref('') const area = ref('')
// 选择台桌索引 // 选择台桌索引
const tableItemActive = ref(-1) const tableItemActive = ref(0)
// 选择台桌信息 // 选择台桌信息
const slectTable = ref('') const slectTable = ref('')
@@ -122,31 +151,50 @@ function tabChange(item, index) {
// 计算当前的时间差 // 计算当前的时间差
function countTime(t) { function countTime(t) {
let ctime = dayjs().valueOf() if (!t) return '0小时1分'
return dayjs(ctime - t).format('H小时m分')
const now = dayjs().valueOf()
const createTime = dayjs(t).valueOf()
let diff = now - createTime
// 负数 或 小于1分钟都强制显示 0小时1分
if (diff < 0 || diff < 60 * 1000) {
return '0小时1分'
}
const h = Math.floor(diff / 3600000)
const m = Math.floor((diff % 3600000) / 60000)
return `${h}小时${m}`
} }
// 支付成功,刷新状态 // 支付成功,刷新状态
async function paySuccess() { async function paySuccess() {
drawerVisible.value = false
await shopTableAjax() await shopTableAjax()
slectTableHandle(tableItemActive.value, tableList.value[tableItemActive.value]) // slectTableHandle(tableItemActive.value, tableList.value[tableItemActive.value])
} }
// 选择台桌 // 选择台桌
const drawerVisible = ref(false)
function slectTableHandle(index, item) { function slectTableHandle(index, item) {
if (tableItemActive.value == index) {
tableItemActive.value = -1
slectTable.value = ''
} else {
tableItemActive.value = index tableItemActive.value = index
slectTable.value = item drawerVisible.value = true
} // if (tableItemActive.value == index) {
// tableItemActive.value = -1
// slectTable.value = ''
// } else {
// tableItemActive.value = index
// slectTable.value = item
// }
} }
// 关闭台桌 // 关闭台桌
function slectTableClose() { function slectTableClose() {
tableItemActive.value = -1 tableItemActive.value = -1
slectTable.value = '' slectTable.value = ''
drawerVisible.value = false
} }
// 获取台桌区域 // 获取台桌区域
@@ -163,12 +211,21 @@ async function shopAreaAjax() {
} }
// 获取台桌列表 // 获取台桌列表
async function shopTableAjax() { async function shopTableAjax(isLoading = true) {
try { try {
orderInfo.value = {
num: 0,
personNum: 0,
amount: 0
}
if (isLoading) {
loading.value = true loading.value = true
}
const res = await shopTable({ const res = await shopTable({
page: query.value.page, // page: query.value.page,
size: query.value.size, // size: query.value.size,
page: 1,
size: 999,
areaId: area.value, areaId: area.value,
tableCode: '', tableCode: '',
name: '', name: '',
@@ -176,19 +233,41 @@ async function shopTableAjax() {
}) })
tableList.value = res.records tableList.value = res.records
query.value.total = +res.totalRow query.value.total = +res.totalRow
tableList.value.forEach(item => {
if (item.status == 'unsettled') {
orderInfo.value.num += 1
orderInfo.value.personNum += item.personNum || 0
orderInfo.value.amount += item.orderAmount || 0
}
})
if (isLoading) {
setTimeout(() => { setTimeout(() => {
loading.value = false loading.value = false
}, 500) }, 500)
}
} catch (error) { } catch (error) {
if (isLoading) {
loading.value = false loading.value = false
}
console.log(error) console.log(error)
} }
} }
const refreshKey = ref(0)
onMounted(() => { onMounted(() => {
shopAreaAjax() shopAreaAjax()
shopTableAjax() shopTableAjax()
const timeTimer = setInterval(() => {
refreshKey.value++ // 强制视图更新时间
}, 60000)
onUnmounted(() => {
clearInterval(timeTimer)
})
}) })
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@@ -219,7 +298,7 @@ onMounted(() => {
padding: 0 10px; padding: 0 10px;
.item { .item {
padding: 0 10px; padding: 0 20px;
display: flex; display: flex;
align-items: center; align-items: center;
position: relative; position: relative;
@@ -228,6 +307,13 @@ onMounted(() => {
font-size: var(--el-font-size-base); font-size: var(--el-font-size-base);
} }
.dot {
width: 8px;
height: 8px;
border-radius: 50%;
margin-right: 6px;
}
&.active { &.active {
&::after { &::after {
@@ -252,6 +338,17 @@ onMounted(() => {
display: flex; display: flex;
align-items: center; align-items: center;
} }
.order_info {
display: flex;
padding-right: 14px;
.title {
display: flex;
align-items: center;
gap: 4px;
}
}
} }
.tab_container { .tab_container {
@@ -262,25 +359,27 @@ onMounted(() => {
} }
.overflow_y { .overflow_y {
height: calc(100vh - 220px); // height: calc(100vh - 220px);
height: calc(100vh - 160px);
overflow-y: auto; overflow-y: auto;
} }
.tab_list { .tab_list {
display: grid; display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr; grid-template-columns: 1fr 1fr 1fr 1fr 1fr;
grid-template-rows: auto; grid-template-rows: auto;
gap: 10px; gap: 10px;
.item { .item {
border-radius: 6px; border-radius: 6px;
overflow: hidden; overflow: hidden;
padding: 2px; padding: 4px;
background-color: var(--color); background-color: var(--color);
&.active { &.active {
.tab_cont { .tab_cont {
position: relative; position: relative;
&::before { &::before {
content: ""; content: "";
width: 100%; width: 100%;
@@ -309,14 +408,19 @@ onMounted(() => {
} }
.tab_cont { .tab_cont {
height: 112px; height: 108px;
display: flex; display: flex;
align-items: center;
justify-content: center;
background-color: #fff; background-color: #fff;
border-radius: 4px; border-radius: 4px;
position: relative; position: relative;
.icon_wrap {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
.icon { .icon {
color: var(--color); color: var(--color);
font-size: 30px; font-size: 30px;
@@ -325,6 +429,29 @@ onMounted(() => {
z-index: 2 z-index: 2
} }
} }
.using {
display: flex;
flex-direction: column;
justify-content: center;
gap: 20px;
padding-left: 14px;
.t1 {
color: #333;
}
.t2 {
display: flex;
align-items: center;
gap: 4px;
span {
color: #555;
}
}
}
}
} }
} }
} }

View File

@@ -0,0 +1,331 @@
<template>
<div class="content">
<div class="cart_wrap card">
<div class="header">
<div class="menus">
<div class="item" :class="{ active: tabActive == index }" v-for="(item, index) in tabAreas"
:key="item.id" @click="tabChange(item, index)">
<el-text>{{ item.label }}</el-text>
</div>
</div>
<!-- <div class="all">
<el-button type="link" icon="Clock">预定管理</el-button>
</div> -->
</div>
<div class="tab_container">
<div class="tab_head">
<el-radio-group v-model="area" @change="shopTableAjax">
<el-radio-button label="全部" value=""></el-radio-button>
<el-radio-button :label="item.name" :value="item.id" v-for="item in areaList"
:key="item.id"></el-radio-button>
</el-radio-group>
</div>
<div class="overflow_y" v-loading="loading">
<div class="tab_list">
<div class="item" :class="{ active: tableItemActive == index }"
:style="{ '--color': tableStatusList.find(val => val.type == item.status).color }"
v-for="(item, index) in tableList" :key="item.id" @click="slectTableHandle(index, item)">
<div class="tab_title" :class="`${item.status}`">
<span>{{ item.name }}</span>
<span>0/{{ item.maxCapacity }}</span>
</div>
<div class="tab_cont">
<el-icon class="icon" v-if="item.status != 'using'">
<CircleClose />
</el-icon>
<div class="using" v-else>
<div class="t1">开台中</div>
<!-- <div class="t2">
<el-icon>
<Timer />
</el-icon>
<span>{{ countTime(item.updatedAt) }}</span>
</div> -->
</div>
</div>
</div>
</div>
<div class="empty" v-if="!tableList.length">
<el-empty description="空空如也~" />
</div>
</div>
<div class="pagination">
<el-pagination background v-model:current-page="query.page" :pager-count="5"
layout=" pager, jumper, total" :total="query.total"
@current-change="shopTableAjax"></el-pagination>
</div>
</div>
</div>
<div class="right_card card">
<!-- 台桌统计 -->
<countCard v-if="!slectTable.id" />
<!-- 台桌信息 -->
<tableInfo v-else :tableInfo="slectTable" @close="slectTableClose" @success="paySuccess" />
</div>
</div>
</template>
<script setup>
import { shopArea, shopTable } from "@/api/account.js";
import countCard from '@/views/table/components/countCard.vue'
import tableInfo from '@/views/table/components/tableInfo.vue'
import { ref, onMounted } from 'vue'
import { dayjs } from 'element-plus'
import tableStatusList from './statusList.js'
const tabActive = ref(0)
const tabAreas = ref([
{
label: '全部',
type: '',
},
{
label: '空闲',
type: 'idle',
},
{
label: '使用中',
type: 'unsettled'
},
{
label: '待清理',
type: 'settled',
},
{
label: '已预订',
type: 'subscribe',
}
])
const query = ref({
page: 1,
size: 12,
total: 0
})
const loading = ref(false)
// 区域列表
const areaList = ref([])
// 台桌列表
const tableList = ref([])
// 所选区域
const area = ref('')
// 选择台桌索引
const tableItemActive = ref(-1)
// 选择台桌信息
const slectTable = ref('')
// 切换类型
function tabChange(item, index) {
tabActive.value = index
shopTableAjax()
}
// 计算当前的时间差
function countTime(t) {
let ctime = dayjs().valueOf()
return dayjs(ctime - t).format('H小时m分')
}
// 支付成功,刷新状态
async function paySuccess() {
await shopTableAjax()
slectTableHandle(tableItemActive.value, tableList.value[tableItemActive.value])
}
// 选择台桌
function slectTableHandle(index, item) {
if (tableItemActive.value == index) {
tableItemActive.value = -1
slectTable.value = ''
} else {
tableItemActive.value = index
slectTable.value = item
}
}
// 关闭台桌
function slectTableClose() {
tableItemActive.value = -1
slectTable.value = ''
}
// 获取台桌区域
async function shopAreaAjax() {
try {
const res = await shopArea({
page: 1,
size: 500
})
areaList.value = res.records
} catch (error) {
console.log(error)
}
}
// 获取台桌列表
async function shopTableAjax() {
try {
loading.value = true
const res = await shopTable({
page: query.value.page,
size: query.value.size,
areaId: area.value,
tableCode: '',
name: '',
status: tabAreas.value[tabActive.value].type,
})
tableList.value = res.records
query.value.total = +res.totalRow
setTimeout(() => {
loading.value = false
}, 500)
} catch (error) {
loading.value = false
console.log(error)
}
}
onMounted(() => {
shopAreaAjax()
shopTableAjax()
})
</script>
<style scoped lang="scss">
.pagination {
display: flex;
justify-content: flex-end;
padding-top: var(--el-font-size-base);
}
.cart_wrap {
flex: 2;
}
.right_card {
flex: 1;
height: 100%;
margin-left: var(--el-font-size-base);
}
.header {
display: flex;
height: var(--el-component-size-large);
justify-content: space-between;
border-bottom: 1px solid #ececec;
.menus {
display: flex;
padding: 0 10px;
.item {
padding: 0 10px;
display: flex;
align-items: center;
position: relative;
span {
font-size: var(--el-font-size-base);
}
&.active {
&::after {
content: "";
width: 70%;
height: 4px;
border-radius: 4px;
position: absolute;
bottom: 0;
left: 15%;
background-color: var(--primary-color);
}
span {
color: var(--primary-color);
}
}
}
}
.all {
display: flex;
align-items: center;
}
}
.tab_container {
padding: var(--el-font-size-base);
.tab_head {
padding-bottom: var(--el-font-size-base);
}
.overflow_y {
height: calc(100vh - 220px);
overflow-y: auto;
}
.tab_list {
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr;
grid-template-rows: auto;
gap: 10px;
.item {
border-radius: 6px;
overflow: hidden;
padding: 2px;
background-color: var(--color);
&.active {
.tab_cont {
position: relative;
&::before {
content: "";
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
background-color: var(--color);
opacity: .2;
z-index: 1;
}
}
}
&:hover {
cursor: pointer;
}
.tab_title {
height: var(--el-component-size-large);
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 10px;
color: #fff;
}
.tab_cont {
height: 112px;
display: flex;
align-items: center;
justify-content: center;
background-color: #fff;
border-radius: 4px;
position: relative;
.icon {
color: var(--color);
font-size: 30px;
transform: rotate(45deg);
position: relative;
z-index: 2
}
}
}
}
}
</style>