新增排队叫号功能

This commit is contained in:
gyq
2024-12-12 09:33:01 +08:00
parent 0711d4b07d
commit 4b54fdaff1
12 changed files with 17112 additions and 93 deletions

View File

@@ -0,0 +1,139 @@
<template>
<el-dialog v-model="showAddTable" :title="addTabForm.id ? '编辑桌型' : '新增桌型'" top="10vh" @closed="addTabFormReset">
<el-form ref="AddTabFormRef" :model="addTabForm" :rules="addTabFormRules" label-position="left"
label-width="100">
<el-form-item label="名称" prop="name">
<el-input v-model="addTabForm.name" placeholder="请输入名称" />
</el-form-item>
<el-form-item label="描述" prop="note">
<el-input v-model="addTabForm.note" placeholder="请输入描述例如1-2人" />
</el-form-item>
<el-form-item label="等待时间" prop="waitTime">
<el-input v-model="addTabForm.waitTime" placeholder="0">
<template #append>分钟/1</template>
</el-input>
</el-form-item>
<el-form-item label="号码前缀" prop="prefix">
<el-input v-model="addTabForm.prefix" placeholder="请输入英文字母,不支持中文" />
</el-form-item>
<el-form-item label="开始号码" prop="start">
<el-input v-model="addTabForm.start" placeholder="请输入开始号码" />
</el-form-item>
<el-form-item label="过号保留">
<el-input v-model="addTabForm.nearNum" :disabled="!addTabForm.isPostpone" placeholder="临近几桌提醒">
<template #prepend>
<el-checkbox v-model="addTabForm.isPostpone" :true-value="1" :false-value="0" label="开启顺延" />
</template>
<template #append></template>
</el-input>
</el-form-item>
</el-form>
<div class="footer" style="display: flex;">
<el-button style="width: 100%" @click="showAddTable = false">
取消
</el-button>
<el-button type="primary" style="width: 100%" :loading="addTabFormLoading" @click="addTabConfirmHandle">
确认
</el-button>
</div>
</el-dialog>
</template>
<script setup>
import { ElMessage } from 'element-plus'
import { ref, onMounted } from 'vue'
import { addCallTable } from '@/api/queue.js'
import { useUser } from "@/store/user.js"
const store = useUser()
const emits = defineEmits(['success'])
const showAddTable = ref(false)
const addTabFormLoading = ref(false)
const AddTabFormRef = ref(null)
const resetAddTabForm = ref({})
const addTabForm = ref({
name: '',
note: '',
waitTime: '',
prefix: '',
start: '',
isPostpone: 0,
nearNum: ''
})
const addTabFormRules = ref({
name: [
{
required: true,
message: ' ',
trigger: 'blur',
}
],
waitTime: [
{
required: true,
message: ' ',
trigger: 'blur',
}
],
prefix: [
{
required: true,
message: ' ',
trigger: 'blur',
}
],
start: [
{
required: true,
message: ' ',
trigger: 'blur',
}
],
})
// 初始化
function addTabFormReset() {
addTabForm.value = { ...resetAddTabForm.value }
AddTabFormRef.value.resetFields()
}
// 提交
function addTabConfirmHandle() {
AddTabFormRef.value.validate(async valid => {
try {
if (valid) {
addTabFormLoading.value = true
addTabForm.value.shopId = store.userInfo.shopId
if (addTabForm.value.id) {
addTabForm.value.callTableId = addTabForm.value.id
}
const res = await addCallTable(addTabForm.value)
addTabFormLoading.value = false
showAddTable.value = false
ElMessage.success(addTabForm.value.id ? '编辑成功' : '添加成功')
emits('success')
}
} catch (error) {
addTabFormLoading.value = false
console.log(error);
}
})
}
function show(obj) {
if (obj && obj.id) {
addTabForm.value = { ...obj }
}
showAddTable.value = true
}
defineExpose({
show
})
onMounted(() => {
resetAddTabForm.value = { ...addTabForm.value }
})
</script>

View File

@@ -0,0 +1,108 @@
<!-- 取号 -->
<template>
<el-dialog title="取号" v-model="visible" @closed="onClose">
<el-form ref="formRef" :model="form" :rules="rules" label-position="top">
<el-form-item label="选择桌型">
<el-radio-group v-model="form.callTableId">
<el-radio :value="item.id" border v-for="item in list" :key="item.id">{{ item.name
}}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="手机号码" prop="phone">
<el-input v-model="form.phone" placeholder="请填写手机号" style="width: 60%;" />
</el-form-item>
</el-form>
<div class="footer" style="display: flex;padding-top: 30px;">
<el-button style="width: 100%" @click="visible = false">
取消
</el-button>
<el-button type="primary" style="width: 100%" :loading="loading" @click="confirmHandle">
确认
</el-button>
</div>
</el-dialog>
</template>
<script setup>
import { ElMessage } from 'element-plus'
import { onMounted, ref } from 'vue';
import { takeNumber } from '@/api/queue.js'
import { useUser } from "@/store/user.js"
const store = useUser()
const emits = defineEmits(['success'])
const visible = ref(false)
const list = ref([])
const loading = ref(false)
const formRef = ref(null)
const resetForm = ref({})
const form = ref({
callTableId: '',
shopId: '',
phone: '',
note: '',
name: ''
})
const rules = ref({
phone: [
{
required: true,
validator: (rule, value, callback) => {
let reg = /^(?:(?:\+|00)86)?1\d{10}$/
if (!reg.test(form.value.phone)) {
callback(new Error('手机号码不正确'))
} else {
callback()
}
},
trigger: 'blur',
}
]
})
// 提交
function confirmHandle() {
formRef.value.validate(async vaild => {
try {
if (vaild) {
loading.value = true
form.value.shopId = store.userInfo.shopId
form.value.note = list.value.find(item => item.id == form.value.callTableId).note
form.value.name = list.value.find(item => item.id == form.value.callTableId).name
await takeNumber(form.value)
loading.value = false
ElMessage.success('取号成功')
emits('success')
visible.value = false
}
} catch (error) {
loading.value = false
console.log(error);
}
})
}
// 初始化
function onClose() {
form.value = { ...resetForm.value }
formRef.value.resetFields()
}
function show(arr) {
visible.value = true
list.value = [...arr]
form.value.callTableId = list.value[0].id
}
defineExpose({
show
})
onMounted(() => {
resetForm.value = { ...form }
})
</script>

View File

@@ -0,0 +1,99 @@
<!-- 叫号记录 -->
<template>
<el-dialog v-model="dialogVisible" title="叫号记录" @closed="reset" width="80vw">
<el-table :data="tableData.list" border style="height: 84%;" height="50vh">
<el-table-column label="桌号" prop="name"></el-table-column>
<el-table-column label="桌型" prop="note"></el-table-column>
<el-table-column label="手机号" prop="phone"></el-table-column>
<el-table-column label="状态" prop="state">
<template v-slot="scope">
{{ statusList[scope.row.state].text }}
</template>
</el-table-column>
<el-table-column label="时间" prop="callTime" width="200">
<template v-slot="scope">{{ dayjs(scope.row.callTime).format('YYYY-MM-DD HH:mm:ss') }}</template>
</el-table-column>
</el-table>
<div class="pagination" style="padding-top:15px;display: flex;justify-content: flex-end;">
<el-pagination v-model:current-page="tableData.page" v-model:page-size="tableData.size"
layout="total, prev, pager, next" :total="tableData.total" background @current-change="paginationChange"
@size-change="paginationChange">
</el-pagination>
</div>
</el-dialog>
</template>
<script setup>
import { dayjs } from 'element-plus'
import { onMounted, reactive, ref } from 'vue';
import { callRecord } from '@/api/queue.js'
import { useUser } from "@/store/user.js"
const store = useUser()
const dialogVisible = ref(false)
const tableData = reactive({
loading: false,
list: [],
page: 1,
size: 20,
total: 0
})
const statusList = {
'-1': {
type: 'warning',
text: '已取消'
},
0: {
type: 'danger',
text: '排队中'
},
1: {
type: 'success',
text: '叫号中'
},
2: {
type: 'success',
text: '已入座'
},
3: {
type: 'success',
text: '已过号'
}
}
// 分页变化
function paginationChange(e) {
callRecordAjax()
}
// 记录数据
async function callRecordAjax() {
try {
tableData.loading = true
const res = await callRecord({
callTableId: '',
shopId: store.userInfo.shopId,
page: tableData.page,
size: tableData.size
})
tableData.loading = false
tableData.list = res.records
tableData.total = res.total
} catch (error) {
console.log(error);
}
}
function reset() {
tableData.page = 1
}
function show() {
dialogVisible.value = true
callRecordAjax()
}
defineExpose({
show
})
</script>

View File

@@ -0,0 +1,75 @@
<!-- 播报结果 -->
<template>
<el-dialog title="提示" v-model="visible">
<div class="content">
<div style="font-size: 18px;">正在叫号请稍等</div>
<el-alert :title="statusList[item.status].text" :type="statusList[item.status].type" :closable="false" />
</div>
<div class="footer" style="display: flex;">
<el-button style="width: 100%" @click="confirmHandle(2)">完成</el-button>
<el-button type="primary" style="width: 100%" :loading="loading" @click="confirmHandle(3)">过号</el-button>
</div>
</el-dialog>
</template>
<script setup>
import { onMounted, reactive, ref } from 'vue';
import { updateState } from '@/api/queue.js'
import { useUser } from "@/store/user.js"
const store = useUser()
const emits = defineEmits(['success'])
const visible = ref(false)
const item = ref({})
const loading = ref(false)
const statusList = {
'-1': {
type: 'warning',
text: '用户未订阅'
},
0: {
type: 'danger',
text: '失败'
},
1: {
type: 'success',
text: '成功'
}
}
// 过号修改状态
async function confirmHandle(state) {
try {
await updateState({
shopId: store.userInfo.shopId,
callQueueId: item.value.id,
state: state
})
visible.value = false
emits('success')
} catch (error) {
console.log(error);
}
}
function show(obj) {
visible.value = true
item.value = { ...obj }
}
defineExpose({
show
})
</script>
<style scoped lang="scss">
.content {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 15px;
padding-bottom: 20px;
}
</style>

View File

@@ -0,0 +1,233 @@
<template>
<el-dialog v-model="dialogVisible" title="基本设置" width="90vw" top="5vh" @closed="emits('success')">
<div class="scroll_y">
<el-form ref="formRef" :model="form" label-position="left" label-width="140">
<el-form-item label="排队页面地址">{{ config.pageAddress }}</el-form-item>
<el-form-item label="线上取号">
<el-switch v-model="form.isOnline" :active-value="1" :inactive-value="0"></el-switch>
</el-form-item>
<el-form-item label="背景图片">
<UploadImg ref="UploadImgRef" @success="e => form.bgCover = e" />
</el-form-item>
<el-form-item label="桌型">
<el-table :data="tableData.list" border v-loading="tableData.loading">
<el-table-column label="名称" prop="name"></el-table-column>
<el-table-column label="描述" prop="note"></el-table-column>
<el-table-column label="等待时间[桌]" prop="waitTime"></el-table-column>
<el-table-column label="号码前缀" prop="prefix"></el-table-column>
<el-table-column label="开始号码" prop="start"></el-table-column>
<el-table-column label="操作">
<template #header>
<el-button type="primary" @click="AddTabRef.show()">添加</el-button>
</template>
<template v-slot="scope">
<div style="display: flex;gap: 10px;">
<el-text type="primary" @click="AddTabRef.show(scope.row)">编辑</el-text>
<el-text type="danger" @click="delCallTableHandle(scope.row)">删除</el-text>
</div>
</template>
</el-table-column>
</el-table>
</el-form-item>
<div class="title" style="padding-bottom: 20px;">通知模板</div>
<el-form-item label="排队成功提醒">
<el-input disabled style="width: 50%;" v-model="config.successMsg"></el-input>
</el-form-item>
<el-form-item label="排队即将排到通知">
<div style="display: flex;flex-direction: column;">
<el-input disabled style="width: 50%;" v-model="config.nearMsg"></el-input>
<el-input v-model="config.nearNum" disabled style="margin-top: 10px;">
<template #prepend>前面等待</template>
<template #append>桌时提醒</template>
</el-input>
</div>
</el-form-item>
<el-form-item label="排队到号提醒">
<el-input disabled style="width: 50%;" v-model="config.callingMsg"></el-input>
</el-form-item>
</el-form>
</div>
<div class="footer">
<el-button style="width: 100%" @click="dialogVisible = false">
取消
</el-button>
<el-button type="primary" style="width: 100%" :loading="loading" @click="confirmHandle">
确认
</el-button>
</div>
</el-dialog>
<AddTab ref="AddTabRef" @success="getTableAjax" />
</template>
<script setup>
import UploadImg from '@/components/uploadImg.vue'
import { ElMessageBox, ElMessage } from 'element-plus'
import AddTab from './addTab.vue'
import { onMounted, reactive, ref } from 'vue';
import { callTable, delCallTable, callTableConfig, callTableConfigPut } from '@/api/queue.js'
import { useUser } from "@/store/user.js"
const store = useUser()
const dialogVisible = ref(false)
const AddTabRef = ref(null)
const formRef = ref(null)
const loading = ref(false)
const form = ref({
isOnline: 1,
bgCover: '',
nearNum: '',
})
function beforeAvatarUpload(file) {
console.log(2, file);
}
// 桌型
const tableData = reactive({
loading: false,
list: []
})
// 显示基本配置
function show() {
dialogVisible.value = true
getTableAjax()
callTableConfigAjax()
}
// 删除桌型
function delCallTableHandle(row) {
ElMessageBox.confirm('确认要删除吗?', '注意').then(async () => {
try {
tableData.loading = true
const res = await delCallTable({
shopId: store.userInfo.shopId,
callTableId: row.id
})
getTableAjax()
} catch (error) {
console.log(error);
}
}).catch(() => { })
}
// 获取桌型列表
async function getTableAjax() {
try {
tableData.loading = true
const res = await callTable({
page: 1,
size: 100,
shopId: store.userInfo.shopId,
callTableId: '',
state: ''
})
tableData.loading = false
tableData.list = res.records
} catch (error) {
tableData.loading = false
console.log(error);
}
}
// 获取配置信息
const config = ref({})
const UploadImgRef = ref(null)
async function callTableConfigAjax() {
try {
const res = await callTableConfig({ shopId: store.userInfo.shopId })
config.value = res
form.value.nearNum = res.nearNum
if (res.bgCover) {
UploadImgRef.value.init([{ url: res.bgCover }])
}
} catch (error) {
console.log(error);
}
}
// 提交
const emits = defineEmits(['success'])
async function confirmHandle() {
try {
loading.value = true
form.value.shopId = store.userInfo.shopId
const res = await callTableConfigPut(form.value)
loading.value = false
ElMessage.success('保存成功')
dialogVisible.value = false
emits('success')
} catch (error) {
loading.value = false
console.log(error);
}
}
defineExpose({
show
})
</script>
<style>
.avatar-uploader .el-upload {
border: 1px dashed var(--el-border-color);
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
transition: var(--el-transition-duration-fast);
}
.avatar-uploader .el-upload:hover {
border-color: var(--el-color-primary);
}
.el-icon.avatar-uploader-icon {
font-size: 28px;
color: #8c939d;
width: 120px;
height: 120px;
text-align: center;
}
</style>
<style scoped lang="scss">
.title {
font-size: 18px;
font-weight: bold;
color: #333;
}
.avatar-uploader .avatar {
width: 120px;
height: 120px;
display: block;
}
$btmH: 50px;
.scroll_y {
height: 65vh;
overflow-y: auto;
padding-bottom: $btmH;
}
.footer {
display: flex;
padding: 0 100px 0;
position: relative;
&::before {
content: "";
height: $btmH;
background: linear-gradient(to bottom, rgba(255, 255, 255, 0), rgba(255, 255, 255, 1));
width: 100%;
position: absolute;
top: $btmH*-1;
left: 0;
z-index: 10;
}
}
</style>