一、云贝餐饮连锁V3系统概述:全开源扫码点餐新标杆  
1.1 什么是云贝餐饮连锁V3扫码点餐系统?  
**云贝餐饮连锁V3扫码点餐小程序全开源系统**是一款专为连锁餐饮品牌设计的数字化转型工具。该系统以扫码点餐为核心功能,覆盖堂食、外卖、自提等多种场景,支持多门店数据统一管理,同时提供完整的开源代码,满足企业个性化开发需求。

1.2 全开源系统的核心优势  
- **自主可控**:100%开放源代码,企业可自由修改功能模块  
- **多端适配**:兼容微信小程序、支付宝及H5页面  
- **连锁管理**:总部后台统一管控菜品、订单、会员数据  
- **成本节约**:无需支付年费,一次部署终身使用  

二、云贝V3扫码点餐系统核心功能解析  
 2.1 智能化扫码点餐流程  
通过**云贝餐饮连锁V3扫码点餐小程序**,顾客只需扫描桌台二维码,即可完成:  
1. 实时菜单浏览(含图片/视频展示)  
2. 多人同步点餐与合并支付  
3. 特殊需求备注(如忌口、辣度调节)  
4. 后厨自动分单打印  

 2.2 连锁餐饮管理闭环  
多门店管理后台系统为连锁品牌提供:  
- **中央厨房管控**:配方标准化与原料损耗分析  
- **动态价格策略**:不同区域门店差异化定价  
- **会员通存通兑**:跨店消费积分累计  
- **大数据看板**:热销菜品排行、客流量预测  

2.3 全开源技术架构亮点  
| 模块 | 技术方案 | 扩展性 |  
|-------|---------|--------|  
| 前端 | UniApp跨端框架 | 支持编译至6大平台 |  
| 后端 | SpringBoot微服务 | 可对接ERP/CRM系统 |  
| 数据库 | MySQL集群+Redis缓存 | 支撑10万级并发 |  
| 部署 | Docker容器化 | 快速扩容降本50% |  

超管后台setup.js

const name = process['env']['VUE_' + 'APP_' + 'GITHUB_' + 'USER_' + 'NAME']
const noTest = name !== 'test'
const noEmpty = name !== 'undefined'
const dev = process['env']['NODE_' + 'ENV'] === 'dev' + 'elop' + 'ment'

// 加载雪碧图
// import '@/icon'
// 加载全局样式样式
import './styles/public.css'
import '../src/styles/public.css'

export function setupVab(app) {
  if ((noTest && noEmpty) || dev) {
    // 加载背景
    // const Themes = require.context('./styles/background', false, /\.scss$/)
    // Themes.keys().map(Themes)

    // 加载插件
    const Plugins = require.context('./plugins', true, /\.js$/)
    Plugins.keys().forEach((key) => Plugins(key).setup(app))
  }
}

叫号取餐manifest.jason

{
    "name" : "叫号大屏",
    "appid" : "__UNI__3E8A075",
    "description" : "",
    "versionName" : "1.0.0",
    "versionCode" : "100",
    "transformPx" : false,
    /* 5+App特有相关 */
    "app-plus" : {
        "usingComponents" : true,
        "nvueStyleCompiler" : "uni-app",
        "compilerVersion" : 3,
        "splashscreen" : {
            "alwaysShowBeforeRender" : true,
            "waiting" : true,
            "autoclose" : true,
            "delay" : 0
        },
        /* 模块配置 */
        "modules" : {},
        /* 应用发布信息 */
        "distribute" : {
            /* android打包配置 */
            "android" : {
                "permissions" : [
                    "<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
                    "<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
                    "<uses-permission android:name=\"android.permission.VIBRATE\"/>",
                    "<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
                    "<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
                    "<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
                    "<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
                    "<uses-permission android:name=\"android.permission.CAMERA\"/>",
                    "<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
                    "<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
                    "<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
                    "<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
                    "<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
                    "<uses-feature android:name=\"android.hardware.camera\"/>",
                    "<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
                ]
            },
            /* ios打包配置 */
            "ios" : {
                "dSYMs" : false,
                "idfa" : false
            },
            /* SDK配置 */
            "sdkConfigs" : {
                "ad" : {}
            },
            "icons" : {
                "android" : {
                    "hdpi" : "unpackage/res/icons/72x72.png",
                    "xhdpi" : "unpackage/res/icons/96x96.png",
                    "xxhdpi" : "unpackage/res/icons/144x144.png",
                    "xxxhdpi" : "unpackage/res/icons/192x192.png"
                },
                "ios" : {
                    "appstore" : "unpackage/res/icons/1024x1024.png",
                    "ipad" : {
                        "app" : "unpackage/res/icons/76x76.png",
                        "app@2x" : "unpackage/res/icons/152x152.png",
                        "notification" : "unpackage/res/icons/20x20.png",
                        "notification@2x" : "unpackage/res/icons/40x40.png",
                        "proapp@2x" : "unpackage/res/icons/167x167.png",
                        "settings" : "unpackage/res/icons/29x29.png",
                        "settings@2x" : "unpackage/res/icons/58x58.png",
                        "spotlight" : "unpackage/res/icons/40x40.png",
                        "spotlight@2x" : "unpackage/res/icons/80x80.png"
                    },
                    "iphone" : {
                        "app@2x" : "unpackage/res/icons/120x120.png",
                        "app@3x" : "unpackage/res/icons/180x180.png",
                        "notification@2x" : "unpackage/res/icons/40x40.png",
                        "notification@3x" : "unpackage/res/icons/60x60.png",
                        "settings@2x" : "unpackage/res/icons/58x58.png",
                        "settings@3x" : "unpackage/res/icons/87x87.png",
                        "spotlight@2x" : "unpackage/res/icons/80x80.png",
                        "spotlight@3x" : "unpackage/res/icons/120x120.png"
                    }
                }
            }
        }
    },
    /* 快应用特有相关 */
    "quickapp" : {},
    /* 小程序特有相关 */
    "mp-weixin" : {
        "appid" : "wx17ed5fca3bdf53e0",
        "setting" : {
            "urlCheck" : false,
            "minified" : true
        },
        "usingComponents" : true,
        "permission" : {
            "scope.userLocation" : {
                "desc" : "你的位置信息将用于小程序位置接口的效果展示"
            }
        },
        "requiredPrivateInfos" : [ "getLocation", "chooseLocation" ],
        "requiredBackgroundModes" : [ "audio" ],
        "lazyCodeLoading" : "requiredComponents"
    },
    "mp-alipay" : {
        "usingComponents" : true
    },
    "mp-baidu" : {
        "usingComponents" : true
    },
    "mp-toutiao" : {
        "usingComponents" : true
    },
    "uniStatistics" : {
        "enable" : false
    },
    "vueVersion" : "2"
}

商家端index.css

page {
	height: 100%;
	font-size: 28rpx;
	color: #333;
	background: #F5F5F5;
}

view,
scroll-view,
swiper,
movable-view,
icon,
text,
progress,
button,
checkbox,
form,
input,
label,
picker,
picker-view,
radio,
slider,
switch,
textarea,
navigator,
audio,
image,
video,
map,
canvas,
contact-button {
	box-sizing: border-box;
}

button {
	position: relative;
	display: block;
	margin-left: auto;
	margin-right: auto;
	padding-left: 14px;
	padding-right: 14px;
	font-size: 18px;
}

::-webkit-scrollbar {
	display: none;
}

button::after {
	border: none;
}

.uni-page-head {
	z-index: 9999 !important;
}

.container {
	width: 100%;
	height: auto;
}

.p-a {
	position: absolute;
}

.p-r {
	position: relative;
}

.p-f {
	position: fixed;
}


/*底部导航样式*/

.navbar {
	position: fixed;
	bottom: 0;
	left: 0;
	width: 100%;
	height: 115rpx;
	background: #fff;
	color: #555;
	z-index: 2001;
	border-top: 1rpx solid rgba(0, 0, 0, 0.1);
}

.navbargator {
	height: 100%;
	width: 1%;
}

.navbar-view {
	width: 100%;
	height: 100%;
}

.navbar .navbar-icon {
	width: 36rpx;
	height: 36rpx;
}

.navbar .navbar-text {
	font-size: 22rpx;
	text-align: center;
	text-overflow: ellipsis;
	white-space: nowrap;
	overflow: hidden;
	margin-top: 8rpx;
}


/*+-*/

.reducecon {
	height: 60rpx;
	overflow: hidden;
	transition: all 0.3s linear;
	opacity: 0;
	/* width:0; */
	transform: rotate(180deg);
	transform: translateX(50rpx);
}

.reducecon.active {
	opacity: 1;
	min-width: 105rpx;
	transform: rotate(0);
}

.cartc {
	width: 60rpx;
	height: 60rpx;
}

.cartggc {
	height: 60rpx;
}

.cartadd {
	background: #C2C2C2;
	position: relative;
	width: 50rpx;
	height: 50rpx;
	border-radius: 50%;
	padding: 0;
	margin: 0;
}

.cartadd::after {
	position: absolute;
	left: 50%;
	top: 50%;
	transform: translate(-50%, -50%);
	content: "";
	height: 5rpx;
	width: 20rpx;
	background: #fff;
	display: block;
	border-radius: 10rpx;
}

.cartadd::before {
	position: absolute;
	left: 50%;
	top: 50%;
	transform: translate(-50%, -50%);
	content: "";
	height: 20rpx;
	width: 5rpx;
	background: #fff;
	display: block;
	border-radius: 10rpx;
}

.cartdec {
	background: #fff;
	position: relative;
	width: 50rpx;
	height: 50rpx;
	border-radius: 50%;
	padding: 0;
	border: 2rpx solid #C2C2C2;
	margin: 0;
}

.cartdecab {
	position: absolute;
	left: 50%;
	top: 50%;
	transform: translate(-50%, -50%);
	height: 5rpx;
	width: 18rpx;
	background: #fff;
	display: block;
	border-radius: 10rpx;
}

.tac {
	text-align: center;
}

.tal {
	text-align: left;
}

.tar {
	text-align: right;
}

.bb1 {
	border-bottom: 1px solid #f6f6f6;
}

.bd1 {
	border-bottom: 3rpx dotted #e5e5e5;
}

.bt1 {
		border-top: 3rpx dotted #e5e5e5;
	}

li {
	list-style: none;
}

/*flex布局*/
.dfa {
	display: flex;
	align-items: center;
}

.dfac {
	display: flex;
	justify-content: space-around;
	align-items: center;
}

.dfbc {
	display: flex;
	justify-content: space-between;
	align-items: center;
}

.flex {
	display: -webkit-box;
	display: -webkit-flex;
	display: flex;
}

.f-row {
	display: -webkit-box;
	display: -webkit-flex;
	display: flex;
	-webkit-box-orient: horizontal;
	-webkit-flex-direction: row;
	flex-direction: row;
}

.f-col {
	display: -webkit-box;
	display: -webkit-flex;
	display: flex;
	-webkit-box-orient: vertical;
	-webkit-flex-direction: column;
	flex-direction: column;
}

.f-c {
	display: flex;
	align-items: center;
}

/*占比*/

.f-g-0 {
	min-width: 0;
	flex-grow: 0;
	flex-shrink: 0;
}

.f-g-1 {
	min-width: 0;
	flex-grow: 1;
	flex-shrink: 1;
}

.f-1 {
	flex: 1;
	min-width: 0;
}

.f-s-1 {
	flex-shrink: 1;
}

.f-s-0 {
	flex-shrink: 0;
}

.f-b-25 {
	flex-basis: 25%;
}

/*居中*/

.f-c {
	display: flex;
	flex-direction: row;
	justify-content: center;
	align-items: center;
}

.f-c-c {
	display: flex;
	flex-direction: column;
	justify-content: center;
	align-items: center;
}

.f-x-c {
	display: -webkit-box;
	display: -webkit-flex;
	display: flex;
	-webkit-box-pack: center;
	-webkit-justify-content: center;
	-ms-flex-pack: center;
	justify-content: center;
}

.f-y-c {
	display: -webkit-box;
	display: -webkit-flex;
	display: flex;
	-webkit-box-align: center;
	-webkit-align-items: center;
	-ms-flex-align: center;
	-ms-grid-row-align: center;
	align-items: center;
}

.f-c-xc {
	display: flex;
	flex-direction: column;
	justify-content: center;
}

.f-c-ac {
	display: flex;
	flex-direction: column;
	align-items: center;
}

/*wrap*/

.f-w {
	flex-wrap: wrap;
}

.f-raw {
	display: flex;
	flex-direction: row;
	align-items: center;
	flex-wrap: wrap;
}

.f-nw {
	flex-wrap: nowrap;
}

.b0 {
	bottom: 0;
}

/*end*/
.f-e {
	display: flex;
	align-items: center;
	justify-content: flex-end;
}

.f-c-e {
	display: flex;
	flex-direction: column;
	align-items: flex-end;
}

.f-e-c {
	display: flex;
	justify-content: flex-end;
	flex-direction: column;
}

.f-s {
	display: flex;
	align-items: flex-start;
}

.f-s-ac {
	display: flex;
	align-items: flex-start;
	align-items: center;
}

.f-c-s {
	display: flex;
	flex-direction: column;
	align-items: flex-start;
}

.f-y-e {
	display: flex;
	align-items: flex-end;
}

.f-x-e {
	display: flex;
	justify-content: flex-end;
}

.f-x-c {
	display: flex;
	justify-content: flex-end;
	align-items: center;
}

.f-sh {
	display: flex;
	align-items: stretch;
}

/*space-betwee*/

.f-bt {
	display: flex;
	justify-content: space-between;
}

.f-x-bt {
	display: flex;
	justify-content: space-between;
	align-items: center;
}

.f-e-bt {
	display: flex;
	justify-content: space-between;
	align-items: flex-end;
}

.f-s-bt {
	display: flex;
	justify-content: space-between;
	align-items: flex-start;
}

.f-y-bt {
	display: flex;
	flex-direction: column;
	justify-content: space-between;
}

.f-x-ad {
	display: flex;
	justify-content: space-around;
}

.f-x-c-sa {
	display: flex;
	align-items: center;
	justify-content: space-around;
}

.f-y-ad {
	display: flex;
	flex-direction: column;
	justify-content: space-around;
}

/**/
.f-a-b {
	align-items: baseline;
}

.f-c-b {
	display: flex;
	justify-content: center;
	align-items: baseline;
}

.f-y-t {
  display: flex;
  align-items: flex-start;
}

/*  宽度样式  */

.w100 {
	width: 100%;
}

.w50 {
	width: 50%;
}

.w45 {
	width: 45%;
}

.w20 {
	width: 20%;
}

.w30 {
	width: 30%;
}

.w33 {
	width: 33.33%;
}

.w70 {
	width: 70%;
}

.w75 {
	width: 75%;
}

.w80 {
	width: 80%;
}

.h100 {
	height: 100%;
}

.mh100 {
	min-height: 100%;
}

.mvh100 {
	min-height: 100vh;
}

.h0 {
	height: 0;
}

.wh {
	width: 100%;
	height: 100%;
}

.w100v {
	width: 100vw;
}

.h100v {
	height: 100vh;
}


/* 字体大小 */
.f10 {
	font-size: 20rpx;
}

.f11 {
	font-size: 22rpx;
}

.f12 {
	font-size: 24rpx;
}

.f13 {
	font-size: 26rpx;
}

.f14 {
	font-size: 28rpx;
}

.f15 {
	font-size: 30rpx;
}

.f16 {
	font-size: 32rpx;
}

.f17 {
	font-size: 34rpx;
}

.f18 {
	font-size: 36rpx;
}

.f20 {
	font-size: 40rpx;
}

.f22 {
	font-size: 44rpx;
}

.f24 {
	font-size: 48rpx;
}

.f26 {
	font-size: 52rpx;
}

.f28 {
	font-size: 56rpx;
}

.f30 {
	font-size: 60rpx;
}

.f32 {
	font-size: 64rpx;
}

.f34 {
	font-size: 68rpx;
}

.f40 {
	font-size: 80rpx;
}

.f70 {
	font-size: 140rpx;
}

/* 字体是否加粗 */

.wei {
	font-weight: bold;
}

.nowei {
	font-weight: normal;
}

.wei4 {
	font-weight: 400;
}

.wei5 {
	font-weight: 500;
}

.wei6 {
	font-weight: 600;
}

/*border-radius*/

.bs4{
	border-radius: 8rpx;
}
.bs20 {
	border-radius: 40rpx;
	overflow: hidden;
}

.bs5 {
	border-radius: 10rpx;
	overflow: hidden;
}

.bs6 {
	border-radius: 12rpx;
	overflow: hidden;
}

.bs10 {
	border-radius: 20rpx;
	overflow: hidden;
}

.bs15 {
	border-radius: 30rpx;
	overflow: hidden;
}

.bs30 {
	border-radius: 60rpx;
	overflow: hidden;
}

.bsf {
	border-radius: 50%;
	overflow: hidden;
}

.o-h {
	overflow: hidden;
}

.o-x-s {
	overflow-x: scroll;
}

.o-x-s::-webkit-scrollbar {
	display: none;
}

.o-y-s {
	overflow-y: scroll;
	-webkit-overflow-scrolling: touch;
}

.o-a {
	overflow: auto;
}

.o-v {
	overflow: visible;
}

/* 文字颜色 */
.cl {
	color: #3d3d3d;
}

.fc {
	color: #3d3d3d;
	font-weight: 600;
}

.c9 {
	color: #999;
}

.c7 {
	color: #777;
}

.c6 {
	color: #666;
}

.c5 {
	color: #555;
}

.c3 {
	color: #333;
}

.c0 {
	color: #000;
}

.cf {
	color: #fff;
}

.cd {
	color: #ddd;
}

.ce {
	color: #eee;
}

.cef {
	color: #efefef;
}

.cb {
	color: #bbb;
}

.crb {
	color: #FA463D;
}

.cf7 {
	color: #fead77;
}

.cfa {
	color: #ff4d3a;
}

.cf5f {
	color: #FF5F2F;
}

.cf5 {
	color: #ef585e;
}

.ce5 {
	color: #e5e5e5;
}

.cf70 {
	color: #FF7F00;
}

.c8 {
	color: #888;
}

.cb2 {
	color: #b2b2b2;
}

.cf2 {
	color: #f2f2f2;
}

.cd3 {
	color: #d3d3d3;
}

/* 背景为白色 */

.bf {
	background: #fff;
}

.bfa {
	background: #fafafa;
}

.bf5 {
	background: #f5f5f5;
}

.bf6 {
	background: #f6f6f6;
}

.bf7 {
	background: #f7f7f7;
}

.bf8 {
	background-color: #f8f8f8;
}

.bec {
	background-color: #ECECEC;
}

.bfc {
	background: #FAFBFC;
}

.be {
	background: #eee;
}

.bf6 {
	background: #F6F6F6;
}

.bf9 {
	background: #f9f9f9;
}

.bd6 {
	background: #D6D6D6;
}

.bdd {
	background: #DDDDDD;
}

.b3 {
	background: #333;
}

.b9 {
	background: #999;
}

.bg0 {
	background: #000;
}

.b05 {
	background-color: rgba(0, 0, 0, 0.5);
}

.bt {
	background: transparent;
}

.br {
	background: #f00;
}

.bfbf {
	background: #fbf1e5;
}

.bf5f {
	background: #FF5F2F;
}

.bf25 {
	background: #ff0025;
}

.bb {
	background: #2d95ff;
}

.bg {
	background: #15c42d;
}

.by {
	background: #ffb71c;
}

.be6 {
	background: #EFF3F6;
}

.bf2f {
	background: #f2f2f2;
}

.b00 {
	background: #07C160;
}

.bef {
	background: #EF371F;
}

.bf0a {
	background: #FF5B0A;
}

.b-l-f4ee {
	background: linear-gradient(90deg, #f83144 0%, #ed4e6e 300%);
}

.b-l-f0f0 {
	background: linear-gradient(to right, #ff8200, #fd5b00);
}

.b-l-fdf3 {
	background: linear-gradient(to right, #f8c10d, #ff9503);
}

.b89d{
	background: #F8F9FD;
}

.t-o-e {
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

.t-d-l {
	text-decoration: line-through;
}

.l-h1 {
	line-height: 1;
}

.mlr5{
	margin: 0 10rpx;
}

.m02 {
	margin: 0 20rpx;
}
.mt5 {
	margin-top: 10rpx;
}

.mt10 {
	margin-top: 20rpx;
}

.mt15 {
	margin-top: 30rpx;
}

.mt20 {
	margin-top: 40rpx;
}

.mt30 {
	margin-top: 60rpx;
}

.mt35 {
	margin-top: 70rpx;
}

.mt50 {
	margin-top: 100rpx;
}

.mt80 {
	margin-top: 160rpx;
}

.mb5 {
	margin-bottom: 10rpx;
}

.mb10 {
	margin-bottom: 20rpx;
}

.mb12 {
	margin-bottom: 24rpx;
}

.mb15 {
	margin-bottom: 30rpx;
}

.mb20 {
	margin-bottom: 40rpx;
}

.mb30 {
	margin-bottom: 60rpx;
}

.mr4 {
	margin-right: 8rpx;
}

.mr5 {
	margin-right: 10rpx;
}

.mr10 {
	margin-right: 20rpx;
}

.mr15 {
	margin-right: 30rpx;
}

.ml5 {
	margin-left: 10rpx;
}

.ml7 {
	margin-left: 14rpx;
}

.ml10 {
	margin-left: 20rpx;
}

.ml20 {
	margin-left: 40rpx;
}

.ml30 {
	margin-left: 60rpx;
}

.ml45 {
	margin-left: 90rpx;
}

.p2 {
	padding: 20rpx;
}

.p5 {
	padding: 10rpx;
}

.p10 {
	padding: 20rpx;
}

.p20 {
	padding: 40rpx;
}

.p15 {
	padding: 30rpx;
}

.p20 {
	padding: 40rpx;
}

.pb10 {
	padding-bottom: 20rpx;
}

.pb15 {
	padding-bottom: 30rpx;
}

.pb70 {
	padding-bottom: 70rpx;
}

.pb40 {
	padding-bottom: 80rpx;
}

.pb46 {
	padding-bottom: 92rpx;
}

.pb60 {
	padding-bottom: 120rpx;
}

.pb80 {
	padding-bottom: 160rpx;
}

.pt5 {
	padding-top: 10rpx;
}

.pt10 {
	padding-top: 20rpx
}

.pt15 {
	padding-top: 30rpx;
}

.pt50 {
	padding-top: 100rpx;
}

.pl5 {
	padding-left: 10rpx;
}

.pl10 {
	padding-left: 20rpx;
}

.pl20 {
	padding-left: 40rpx;
}

.pl30 {
	padding-left: 60rpx;
}

.pl40 {
	padding-left: 80rpx;
}

.pl50 {
	padding-left: 100rpx;
}

.pl80 {
	padding-left: 160rpx;
}

.pr5 {
	padding-right: 10rpx;
}

.pr10 {
	padding-right: 20rpx;
}

.pr15 {
	padding-right: 30rpx;
}

.pr20 {
	padding-right: 40rpx;
}

.p-0-5 {
	padding: 0 10rpx;
}

.p-0-10 {
	padding: 0 20rpx;
}

.p-0-20 {
	padding: 0 40rpx;
}

.p-5-0 {
	padding: 10rpx 0;
}

.p-5-8 {
	padding: 10rpx 16rpx;
}

.p-5-10 {
	padding: 10rpx 20rpx;
}

.p-5-15 {
	padding: 10rpx 30rpx;
}

.p-5-20 {
	padding: 10rpx 40rpx;
}

.p-5-25 {
	padding: 10rpx 50rpx;
}

.p-10-0 {
	padding: 20rpx 0;
}

.p-10-13 {
	padding: 20rpx 26rpx;
}

.p-10-15 {
	padding: 20rpx 30rpx;
}

.p-10-20 {
	padding: 20rpx 40rpx;
}

.p-15-13 {
	padding: 30rpx 26rpx;
}

.p-15-20 {
	padding: 26rpx 40rpx;
}

.p-15-0 {
	padding: 30rpx 0;
}

.p-15-10 {
	padding: 30rpx 20rpx;
}

.p-20-0 {
	padding: 40rpx 0;
}

.p-20-15 {
	padding: 40rpx 30rpx;
}

.p-25-0 {
	padding: 50rpx 0;
}

.p-30-0 {
	padding: 60rpx 0;
}

.p-30-20 {
	padding: 60rpx 40rpx;
}

.p-35-0 {
	padding: 70rpx 0;
}

.p-40-0 {
	padding: 80rpx 0;
}

.p-45-0 {
	padding: 90rpx 0;
}

.p-50-0 {
	padding: 100rpx 0;
}

 收银台main.js

import App from './App'
import store from './store'
import request from '@/common/request';
import api from '@/api';
// import dLoading from '@/uni_modules/d-loading/components/d-loading/d-loading.vue'

// #ifndef VUE3
import Vue from 'vue'
Vue.config.productionTip = false
App.mpType = 'app'
const app = new Vue({
	store,
	...App
})
import uView from '@/uni_modules/uview-ui'
Vue.use(uView)
Vue.prototype.beg = request
Vue.prototype.api = api
// Vue.component('dLoading',dLoading)

app.$mount()
// #endif

// #ifdef VUE3
import {
	createSSRApp
} from 'vue'
export function createApp() {
	const app = createSSRApp(App)
	return {
		app
	}
}
// #endif

小程序index.js

import Vue from 'vue'
import Vuex from 'vuex'
import util from '../common/core/util';
import api from '../common/api';
import utils from '@/common/utils.js'
import { aliAuthCode } from "@/common/wechat-util.js"
Vue.use(Vuex)

import dndc from './dndc'

const store = new Vuex.Store({
	state: {
		system: {
			custom: {
				live: '',
				takeOut: '',
				integral: '',
				balance: '',
				informationTitle: '',
				hbfh: '',
				hbmc: '',
				inStore: '',
			},
			powerList: {},
		},
		user: {},
		mobile: uni.getStorageSync('userTel'),
		token: '',
		isLogin: false,
		noLogin: false,
		isIpx: false,
		scarList: {
			out: {
				data: []
			},
			fast: {
				data: []
			},
			ins: {
				data: []
			},
		},
		sjxx: {},
		sjgood: {},
		ingood: {},
		layout: {
			index: {},
			user: {},
			nav: {},
			copywriting: {},
			defaultImg: {},
			firing: {},
			loadingImg: {},
			style: {},
			copyright: {},
			notice: [],
			orderCollect: {},
			goodStyle: {},
		},
		config: {
			hasKp: false,
			storeBasicSetting: {},
			orderSetting: {},
			integralSettingcustomTxt: '',
			growthSettingcustomTxt: '',
			zfbSettingapp_id: '',
			basicSetting: {},
			storageVal: {},
			vipset: {},
			storageset: {},
			integralSetting: {},
			personPayOrderConfig: {},
			inStoreOrderConfig:{},
			birthdayGift: {},
			shopInfo: {},
		},
		adList:{
			index: {},
			orderInfo: {},
			user: {},
		},
	},
	mutations: {
		setUser(state, data) {
			uni.setStorageSync('userId', data.id)
			data.integral = data.integral || 0
			data.balance = data.balance || 0
			state.user = data
			if (data.mobile) state.isLogin = true
			// console.log('setUsersetUsersetUser', data, state.isLogin)
		},
		setNoLogin(state, data) {
			state.noLogin = true
		},
		setMobile(state, data) {
			uni.setStorageSync('userTel', data)
			state.mobile = data
		},
		setToken(state, data) {
			// console.log('token', data)
			uni.setStorageSync('token', data)
			// // #ifdef H5
			// window.sessionStorage.setItem('token', data)
			// // #endif
			state.token = data
		},
		setSystem(state, data) {
			state.system = data
		},
		setScarList(state, data) {
			// console.log('setScarList', data, state.scarList)
			if (!data.key) {
				state.scarList['out'] = data.data
			} else {
				state.scarList[data.key] = data.data
			}
		},
		setInCar(state, data) {
			state.scarList['ins'] = data.data
		},
		setSjxx(state, data) {
			state.sjxx = data
			// uni.setStorageSync('storeId', data.id)
		},
		setGoods(state, {
			params,
			data
		}) {
			if(params.dType == "ins"){
				state.ingood = data
			}else{
				state.sjgood = data
			}
		},
		setLayout(state, {
			params,
			data
		}) {
			state.layout[params] = data
			// console.log(params,data)
		},
		setConfig(state, {
			params,
			data
		}) {
			state.config[params] = data
		},
		setadList(state, {
			params,
			data
		}) {
			state.adList[params] = data
		},
	},
	getters: {

	},
	actions: {
		async getSystem({
			commit,
			state
		}, params = {}) {
			// console.log('state', state, params)
			if (!state.system.color || params.get) {
				let res = {};
				state.isIpx = util.getSb().model.search('iPhone X') != -1 || util.getSb().model.search(
						'iPhone 1') != -1 || util
					.getSb().model.search('iPhone1') != -1
			}
		},
		async checkBindTel({
			commit,
			rootState
		}, params) {
			return new Promise((reslove, reject) => {
				if (!rootState.user.userTel) {
					uni.showModal({
						title: '提示',
						content: '请先绑定手机号',
						confirmText: '前往绑定',
						cancelText: '暂不绑定',
						success: res => {
							if (res.confirm) {
								uni.navigateTo({
									url: '/pages/my/login/index',
								});
							}
						}
					});
				} else {
					reslove()
				}
			})
		},
		async getLoginInfo({
			commit,
			state
		}, params = {}) {
			if (state.user.id) {
				return
			} else {
				return await new Promise(async (resolve, reject) => {
					let siteInfo = getApp().globalData.siteInfo
					// #ifndef H5
					// util.showLoading()
					uni.login({
						success: async (lres) => {
							let res = await util.request({
								'url': api.login,
								method: 'post',
								// mask: 1,
								data: {
									code: lres.code
								}
							})
							if (res.code == 200) {
								if (res?.data?.token) {
									if (res.data.session_key) {
										getApp().globalData.session_key = res.data
											.session_key
									}
									commit('setToken', res.data.token)
									if (res.data && res.data.userInfo) {
										commit('setUser', res.data.userInfo)
										resolve()
									} else {
										let resu = await util.request({
											'url': api.profix,
											data: {}
										})
										commit('setUser', resu.data)
										resolve()
									}
								}
								if (res?.data?.openid) {
									getApp().globalData.session_key = res.data
										.session_key
									getApp().globalData.openid = res.data
										.openid
									getApp().globalData.unionid = res.data
										.unionid
									console.log('游客', res.data)
									if (res && res.data) {
										let openid = res.data.openid,
											unionid = res.data.unionid ? res.data
											.unionid : ''
										let rest = await util.request({
											'url': api.wrg,
											method: 'post',
											data: {
												avatar: '',
												nickname: `用户_${util.sj()}`,
												mobile: '',
												openid,
												unionid,
											}
										}).then(async (rest) => {
											console.log('rest', rest)
											resolve()
											rest.data && commit(
												'setToken', rest
												.data.token)
											rest.data && commit(
												'setUser', rest.data
												.userInfo)
										}).catch((e) => {
											console.log(e)
										})
									} else {
										uni.showModal({
											title: '温馨提示',
											content: '请重新进入小程序',
											showCancel: false
										});
										return;
									}
								} else {
									// console.log('已登录', res)
								}
							} else {
								reject()
								util.modal('小程序秘钥配置')
							}
						},
						fail: (err) => {
							console.log('失败', err)
							if (err.errMsg.indexOf('permission') > -1) {
								resolve()
							}
							reject(err)
						}
					})
					// #endif
					// #ifdef H5
					if (api.platform == 'wechat') {
						console.log('%c api.platform ',
							'color: white; background-color: #ff55ff', api.platform, uni.getStorageSync('token'))
						if (!getApp().globalData.siteInfo.isDev) {
							let link = window.location.href,params = utils.getUrlParams(link)
							console.log('%c params ', 'color: white; background-color: #ff0000',params)
								if(!uni.getStorageSync('token')){
									window.location.href = `${getApp().globalData.siteInfo.siteroot}/wechat/${params.uniacid}?refererUrl=${btoa(window.location.href)}`
								}
								if (!state.user.id && uni.getStorageSync('token')) {
									let {
										data
									} = await util.request({
										url: api.profix
									})
									data && commit('setUser', data)
									resolve()
								}
								if (uni.getStorageSync('token')) return;
								return
							if (params.code && uni.getStorageSync('bdCode')) {
								window.sessionStorage.setItem('wcode', params.code)
							}
							if (!state.user.id && uni.getStorageSync('token')) {
								let {
									data
								} = await util.request({
									'url': api.profix
								})
								data && commit('setUser', data)
								resolve()
							}
							if (process.env.NODE_ENV === 'development' && params.token) {
								commit('setToken', params.token)
								let {
									data
								} = await util.request({
									'url': api.profix
								})
								data && commit('setUser', data)
								resolve()
							}
							if (uni.getStorageSync('token')) return;
							if (state.user && state.user.unionid && !uni.getStorageSync(
								'token')) return;
							if (params.code && window.sessionStorage.getItem('wcode')) {
								console.log(params, link)
								let rest = await util.request({
									'url': api.wcl,
									method: 'post',
									data: {
										code: window.sessionStorage.getItem('wcode') ||
											params.code
									}
								}).then(async (rest) => {
									if (rest.data.token) {
										commit('setToken', rest.data.token)
										// util.message('登录成功', 1, 1000)
										commit('setUser', rest.data.userInfo)
										resolve()
										uni.removeStorageSync('bdCode')
									} else if (!rest.data.id && rest.data.openid) {
										commit('setUser', rest.data)
										uni.removeStorageSync('bdCode')
										// uni.navigateTo({
										// 	url: '/pages/other/register',
										// });
										resolve()
									} else {
										util.showLoading()
										let {
											data
										} = await util.request({
											'url': api.wau,
											mask: 1,
											data: {
												refererUrl: link
											}
										})
										// window.location.href = `${data.split('&')[0]}`
										window.location.href = data;
									}
								}).catch((error) => {
									console.log('error', error)
									this.loading = false
								})
							}
						}
					}else if (api.platform == 'alih5'){
						console.log('%c api.platform ','color: white; background-color: #ff55ff', api.platform, uni.getStorageSync('token'))
						let ress = await util.request({
							'url': api.cMap,
							method: 'POST',
							ct: 1,
							data: {
								idents: ['zfbSetting.app_id']
							},
						})
						if (ress) {
							commit('setConfig', {
								params:'zfbSettingapp_id',
								data: ress.data['zfbSetting.app_id']
							})
							let code = await aliAuthCode()
							console.log('code',code)
							let res = await util.request({
								'url': api.login,
								method: 'post',
								// mask: 1,
								data: {
									code: code.authCode
								}
							})
							if (res.code == 200) {
								if (res?.data?.token) {
									commit('setToken', res.data.token)
									if (res.data && res.data.userInfo) {
										commit('setUser', res.data.userInfo)
										resolve()
									} else {
										let resu = await util.request({
											'url': api.profix,
											data: {}
										})
										commit('setUser', resu.data)
										resolve()
									}
								}
							} else {
								reject()
								util.modal('支付宝秘钥配置')
							}
						}
					} else {
						console.log('%c api.platform ','color: white; background-color: #ff55ff', api.platform)
						// commit('setUser', {
						// 	id: "1",
						// 	userId: '1',
						// 	openId: "123",
						// 	userName: "测试",
						// 	userTel: "13823515936",
						// 	portrait: ""
						// })
						
						// commit('setToken', 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL3YzLmJreWNtcy5jb20vY2hhbm5lbEFwaS93ZWNoYXQvbG9naW4iLCJpYXQiOjE3MDEwNTEzNTcsImV4cCI6MTcwMjM0NzM1NywibmJmIjoxNzAxMDUxMzU3LCJqdGkiOiJSQVEyMEpyYWhiSVdqZHhDIiwic3ViIjo0NTY4LCJwcnYiOiI4NjY1YWU5Nzc1Y2YyNmY2YjhlNDk2Zjg2ZmE1MzZkNjhkZDcxODE4In0.ymfMqsS9sTE3BwqPy8ctEEXeWbBKzOxZFmjP4MDKNkA')
						// commit('setUser', {
						// 	id: 4568,
						// 	nickname: "从前慢",
						// 	mobile: "13823515936",
						// })
						resolve()
						if (api.platform == 'h5') {
							return uni.showModal({
								title: '提示',
								content: '请在微信内打开',
								showCancel: false
							})
						}
					}
					// #endif
				})
			}

		},
		async refreshUser({
			commit,
			state
		}, params) {
			// console.log(params)
			// return
			let {
				data
			} = await util.request({
				'url': params.get ? api.profix : api.xgyh,
				method: params.get ? 'GET' : 'POST',
				mask: params.nomask ? 0 : 1,
				data: params
			})
			if (params.now) {
				data && commit('setUser', data)
			} else {
				setTimeout(() => {
					data && commit('setUser', data)
				}, 200);
			}
			return data
		},
		async loginBind({
			commit,
			state
		}, params) {
			let res = await util.request({
				'url': api.bind,
				method: 'POST',
				mask: 1,
				data: params
			})
			res && commit('setUser', res.data)
			return res
		},
		async clearMycar({
			dispatch,
			commit,
			state
		}, params) {
			let res = await util.request({
				'url': api.qkgwc,
				method: 'DELETE',
				mask: 1,
				data: params
			})
			// if (res) {
			// 	dispatch('getMycar', {
			// 		// key: params.key,
			// 		storeId: params.storeId,
			// 	})
			// }
			res && commit('setScarList', {
				key: params.key,
				data: {
					goodsList:[],
					goodsCount: 0
				}
			})
		},
		async getMycar({
			commit,
			state
		}, params) {
			let res = await util.request({
				'url': api.wdgwc,
				mask: params.mask,
				data: params
			})
			res && commit('setScarList', {
				key: params.key,
				data: res.data
			})
			// console.log('index.js,getMycar', params, state.scarList)
		},
		async supdCar({
			dispatch,
			commit,
			state
		}, params) {
			let res = await util.request({
				'url': api.xggwc,
				ct: 1,
				method: 'POST',
				// mask: 1,
				data: params
			})
			// console.log('index.js,SaveShopCar', params, res)
			if (res) {
				commit('setScarList', {
					key: params.key,
					data: res.data.cart
				})
				return +res.data.count
				// dispatch('getMycar', {
				// 	// key: params.key,
				// 	// userId: params.userId,
				// 	// item: params.item,
				// 	storeId: params.storeId,
				// 	lat: params.lat,
				// 	lng: params.lng,
				// })
				// console.log(this)
			}
			// console.log('index.js,supdCar', params)
		},
		async getInCar({
			commit,
			state
		}, params) {
			let res = await util.request({
				'url': api.ingwc,
				mask: params.mask,
				data: params
			})
			res && commit('setInCar', {
				key: params.key,
				data: res.data
			})
		},
		async supInCar({
			dispatch,
			commit,
			state
		}, params) {
			let res = await util.request({
				'url': api.ingwc,
				ct: 1,
				method: 'POST',
				data: params
			})
			if (res) {
				commit('setInCar', {
					data: res.data.cart
				})
				return +res.data.count
			}
		},
		async clearIncar({
			dispatch,
			commit,
			state
		}, params) {
			let res = await util.request({
				'url': api.inqkgwc,
				method: 'DELETE',
				mask: 1,
				data: params
			})
			res && commit('setInCar', {
				data: {
					goodsList:[],
					goodsCount: 0
				}
			})
		},
		async getSjxx({
			commit,
			state
		}, params) {
			let {
				data
			} = await util.request({
				'url': `${api.dplb}/${params.storeId}`,
				data:{
					lat: params.lat,
					lng: params.lng,
				}
			})
			commit('setSjxx', data)
			return data
		},

		async getGoods({
			commit,
			state
		}, params) {
			let {
				data
			} = await util.request({
				'url': params.dType == 'ins' ? api.ingoods : api.goods,
				data: params
			})
			commit('setGoods', {data,params,})
			return data
		},
		async getLayout({
			commit,
			state
		}, params = {
			page: 'index',
			id: '1',
		}) {
			state.isIpx = util.getSb().model.search('iPhone X') != -1 || util.getSb().model.search(
					'iPhone 1') != -1 || util.getSb().model.search('iPhone1') != -1
			for (let k in state.layout) {
				if (state.layout[k].isget && params.rf!=1) {
					return
				}
			}
			let res = await util.request({
				url: api.layout,
				data: params
			})
			// console.timeEnd('getLayout')
			if (res) {
				for (let k in res.data) {
					commit('setLayout', {
						params:k,
						data: {
							isget: true,
							...res.data[k],
						}
					})
				}
			}
		},
		async getAd({
			commit,
			state
		}, params) {
			for (let k in state.adList) {
				if (state.adList[k].isget && !params) {
					return
				}
			}
			let res = await util.request({
				'url': api.gglb,
				data: params
			})
			if (res) {
				for (let k in res.data) {
					commit('setadList', {
						params:k,
						data: {
							isget: true,
							...res.data[k],
						}
					})
				}
			}
		},
		async getConfig({
			commit,
			state
		}, params) {
			if (params.ident) {
				for (let k of params.ident) {
					let nkey = k.replace(/\./g, "")
					if (state.config[nkey].isget) {
						return
					}
				}
				let res = await util.request({
					'url': api.cMap,
					method: 'POST',
					ct: 1,
					data: {
						idents: params.ident,
						lat: params.lat,
						lng: params.lng,
					},
				})
				if (res) {
					for (let k of params.ident) {
						if(k.indexOf('.')>-1){
							let nkey = k.replace(/\./g, "")
							commit('setConfig', {
								params:nkey,
								data: res.data[k]
							})
						}else{
							commit('setConfig', {
								params: k,
								data: {
									isget: true,
									...res.data[k]
								}
							})
						}
					}
					if(res.data.shopInfo){
						commit('setConfig', {
							params:'shopInfo',
							data: res.data.shopInfo
						})
					}
				}
			}else {
				commit('setConfig', {
					params:params.key,
					data: params.data,
				})
			}
		},
	},
	modules: {
		dndc,
	}
})

export default store

业务后台jsconfig.json

{
  "compilerOptions": {
    "target": "es5",
    "module": "esnext",
    "baseUrl": "./",
    "moduleResolution": "node",
    "paths": {
      "@/*": [
        "src/*"
      ]
    },
    "lib": [
      "esnext",
      "dom",
      "dom.iterable",
      "scripthost"
    ]
  }
}

装修后台vite.config.js

import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import { resolve } from "path";
import { ElementPlusResolver } from "unplugin-vue-components/resolvers";
import AutoImport from "unplugin-auto-import/vite";
import Components from "unplugin-vue-components/vite";
import Icons from "unplugin-icons/vite";
import IconsResolver from "unplugin-icons/resolver";
import inject from '@rollup/plugin-inject';

export default defineConfig({
  base: "./",
  plugins: [
    vue(),
    inject({ 
      $: "jquery",
      jQuery: "jquery",
      "windows.jQuery": "jquery"
    }),
    AutoImport({
      include: [
        /\.[tj]sx?$/,
        /\.vue$/,
        /\.vue\?vue/, 
        /\.md$/, 
      ],
      dts: true,
      imports: ["vue", "vue-router"],
      resolvers: [
        ElementPlusResolver(),
        IconsResolver({
          prefix: "Icon",
        }),
      ],
    }),
    Components({
      dts: true,
      resolvers: [
        ElementPlusResolver(),
        IconsResolver({
          enabledCollections: ["ep"],
        }),
      ],
    }),
    Icons({
      autoInstall: true,
    }),
  ],
  resolve: {
    alias: {
      "@": resolve("src"),
    },
  },
  build: {
    assetsDir: 'static',
    rollupOptions: {
      input: {
        index: resolve(__dirname, 'index.html'),
      },
      output: {
        chunkFileNames: 'static/js/[name]-[hash].js',
        entryFileNames: 'static/js/[name]-[hash].js',
        assetFileNames: 'static/[ext]/name-[hash].[ext]'
      }
    }
  },
  server: {
    host: "0.0.0.0",
    port: 10086,
    open: true,
  },
  proxy: {

  },
});

三、为什么选择全开源系统?  
3.1 对比SaaS平台的显著优势  
传统SaaS点餐系统存在**年费高昂**、**数据隔离**、**功能僵化**三大痛点,而**云贝餐饮连锁V3全开源系统**提供:  
- **零年费终身授权**:节省年均3-8万元支出  
- **数据私有化部署**:保障商业信息安全  
- **深度定制开发**:根据品牌调性修改UI/UX  

3.2 典型应用场景案例  
 场景1:快餐连锁店  
某中式快餐品牌接入系统后:  
- 点餐耗时从5分钟缩短至1.2分钟  
- 人力成本降低40%(减少3名前台人员)  
- 会员复购率提升27%  

 场景2:火锅连锁集团  
通过自定义开发实现:  
- 智能锅底推荐算法  
- AR菜品立体展示  
- 桌边服务呼叫系统  

四、如何快速部署云贝V3系统?  
4.1 基础环境要求  
| 项目 | 最低配置 | 推荐配置 |  
|-------|---------|----------|  
| 服务器 | 2核4G | 4核8G集群 |  
| 带宽 | 5Mbps | 10Mbps+CDN |  
| 存储 | 100GB | 500GB SSD |  

 4.2 六步落地指南  
1. 源码下载与环境检测  
2. 数据库初始化配置  
3. 支付接口对接(微信/支付宝/银联)  
4. 门店信息批量导入  
5. 员工权限分级设置  
6. 压力测试与灰度发布  

---

 五、行业影响与未来演进  
 5.1 推动餐饮业数字化转型  
据统计,采用**云贝扫码点餐小程序全开源系统**的商户:  
- 平均翻台率提升15%-22%  
- 纸质菜单成本年节省超8000元  
- 顾客满意度达92.3%  

**结语**  
云贝餐饮连锁V3扫码点餐小程序全开源系统正在重新定义餐饮服务标准,其开源性、灵活性、高性价比特性,使其成为连锁餐饮企业数字化转型的首选方案。立即获取源码,开启您的智慧餐饮新时代!

Logo

一站式 AI 云服务平台

更多推荐