代客下单页面修改,增加店铺配置页面

This commit is contained in:
YeMingfei666 2025-02-25 13:37:24 +08:00
parent ffcaaf9e41
commit 4eb7744111
22 changed files with 2181 additions and 123 deletions

View File

@ -37,6 +37,7 @@
"@wangeditor-next/editor": "^5.6.31",
"@wangeditor-next/editor-for-vue": "^5.1.14",
"axios": "^1.7.9",
"bwip-js": "^4.5.1",
"codemirror": "^5.65.18",
"codemirror-editor-vue3": "^2.8.0",
"dayjs": "^1.11.13",
@ -51,6 +52,7 @@
"path-browserify": "^1.0.1",
"path-to-regexp": "^8.2.0",
"pinia": "^2.3.1",
"qrcode": "^1.5.4",
"qs": "^6.14.0",
"sockjs-client": "^1.6.1",
"sortablejs": "^1.15.6",

View File

@ -9,7 +9,13 @@ const OrderApi = {
params: params,
});
},
add() { },
add(data: addRequest) {
return request<any, OrderInfo>({
url: `${baseURL}/createOrder`,
method: "post",
data
});
},
edit() { },
delete() { },
};
@ -214,3 +220,244 @@ export interface OrderDetailSmallVO {
skuName?: string;
[property: string]: any;
}
export interface addRequest {
/**
* dine-in take-out take-away
*/
dineMode: string;
/**
* 使
*/
orderId?: number;
/**
* +
*/
originAmount?: number;
/**
*
*/
packFee?: number;
/**
*
*/
placeNum?: number;
/**
*
*/
remark?: string;
/**
*
*/
seatNum?: number;
/**
* Id
*/
shopId?: number;
/**
*
*/
tableCode: string;
userId?: number;
/**
* 使
*/
vipPrice?: boolean;
/**
* 0 1
*/
waitCall?: boolean;
[property: string]: any;
}
/**
* OrderInfo
*/
export interface OrderInfo {
/**
* 使
*/
couponInfoList?: string;
createTime?: string;
/**
* id
*/
creditBuyerId?: number;
/**
* dine-in take-out take-away
*/
dineMode?: string;
/**
*
*/
discountAmount?: number;
/**
* json
*/
discountInfo?: string;
/**
*
*/
discountRatio?: number;
/**
*
*/
fullCouponDiscountAmount?: number;
id?: number;
/**
* 0-1
*/
isDel?: number;
/**
* 使
*/
isFreeDine?: number;
/**
* 0 1
*/
isWaitCall?: number;
/**
* ()
*/
orderAmount?: number;
/**
*
* pc PC+ID
* wechat WX+ID
* alipay ALI+ID
* admin-pc PC管理端 WEB+ID
* admin-app APP管理端 APP+ID
*/
orderNo?: string;
/**
* -
* cash收银( )
* miniapp小程序
*/
orderType?: string;
/**
*
*/
originAmount?: number;
/**
*
*/
packFee?: number;
/**
*
*/
paidTime?: string;
/**
*
*/
payAmount?: number;
/**
* :
* after-pay
* before-pay
* no-table
*/
payMode?: string;
/**
*
* tb_order_payment.id
* tb_shop_user_flow.id
*/
payOrderId?: number;
payOrderNo?: string;
/**
*
* main-scan
* back-scan
* wechat-mini
* alipay-mini
* vip-pay
* cash-pay
*/
payType?: string;
/**
*
*/
placeNum?: number;
/**
* pc wechat alipay admin-pc PC管理端 admin-app APP管理端
*/
platformType?: string;
/**
*
*/
pointsDiscountAmount?: number;
/**
* 使
*/
pointsNum?: number;
/**
*
*/
productCouponDiscountAmount?: number;
/**
* 退1退 0退
*/
refundAble?: number;
/**
* 退
*/
refundAmount?: number;
/**
* 退
*/
refundRemark?: string;
/**
*
*/
remark?: string;
/**
*
*/
roundAmount?: number;
/**
*
*/
seatAmount?: number;
/**
*
*/
seatNum?: number;
/**
* Id
*/
shopId?: number;
/**
* id
*/
staffId?: number;
/**
* OrderStatusEnums
* 状态: unpaid-;in-production ;wait-out
* ;;done-;refunding-退;refund-退;part-refund 退;cancelled-
*/
status?: string;
/**
* Id
*/
tableCode?: string;
/**
*
*/
tableName?: string;
/**
*
*/
takeCode?: string;
/**
*
*/
tradeDay?: string;
updateTime?: string;
/**
* Id user_info表的id
*/
userId?: number;
[property: string]: any;
}

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1740451152130" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4602" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><path d="M928 544 96 544c-17.664 0-32-14.336-32-32s14.336-32 32-32l832 0c17.696 0 32 14.336 32 32S945.696 544 928 544zM832 928l-192 0c-17.696 0-32-14.304-32-32s14.304-32 32-32l192 0c17.664 0 32-14.336 32-32l0-160c0-17.696 14.304-32 32-32s32 14.304 32 32l0 160C928 884.928 884.928 928 832 928zM352 928 192 928c-52.928 0-96-43.072-96-96l0-160c0-17.696 14.336-32 32-32s32 14.304 32 32l0 160c0 17.664 14.368 32 32 32l160 0c17.664 0 32 14.304 32 32S369.664 928 352 928zM128 384c-17.664 0-32-14.336-32-32L96 192c0-52.928 43.072-96 96-96l160 0c17.664 0 32 14.336 32 32s-14.336 32-32 32L192 160C174.368 160 160 174.368 160 192l0 160C160 369.664 145.664 384 128 384zM896 384c-17.696 0-32-14.336-32-32L864 192c0-17.632-14.336-32-32-32l-192 0c-17.696 0-32-14.336-32-32s14.304-32 32-32l192 0c52.928 0 96 43.072 96 96l0 160C928 369.664 913.696 384 896 384z" fill="#bfbfbf" p-id="4603"></path></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1,217 @@
<template>
<el-dialog
title="选择地址"
v-model="state.showLocation"
:modal="true"
:modal-append-to-body="false"
>
<div class="map_box">
<div class="map">
<el-amap ref="map" :center="state.amapOptions.center" @init="mapInit">
<el-amap-marker :position="state.amapOptions.center"></el-amap-marker>
</el-amap>
</div>
<div class="search_box">
<el-input
v-model="state.searchOption.keyword"
placeholder="请输入关键字"
@focus="state.searchOption.focus = true"
@blur="autoCompleteSearchBlur"
@input="autoCompleteSearch(state.searchOption.keyword)"
>
<template #append>
<el-button type="primary" @click="placeSearchSearch(state.searchOption.keyword)">
搜索
</el-button>
</template>
</el-input>
<div class="list" v-if="state.searchOption.focus && state.searchOption.show">
<div
class="item"
@click="autoCompleteListClick(item)"
v-for="item in state.autoCompleteList"
:key="item.id"
>
{{ item.name }}
</div>
</div>
</div>
<div class="search_wrap">
<div class="item" v-for="item in state.locationSearchList" :key="item.id">
<div class="left">
<div class="name">{{ item.name }}-{{ item.address }}</div>
<div class="location">经纬度{{ item.location.lng }},{{ item.location.lat }}</div>
</div>
<div class="btn">
<el-button type="primary" @click="selectLocationHandle(item)">选择</el-button>
</div>
</div>
</div>
</div>
</el-dialog>
</template>
<script setup>
import { ElAmap } from "@vuemap/vue-amap";
import { initMapLoad } from "@/utils/mapLoadUtil";
onBeforeMount(async () => {
const res = await initMapLoad();
});
const state = reactive({
showLocation: false,
amapOptions: {
center: [108.946465, 34.347984],
position: [],
},
searchOption: {
city: "西安",
citylimit: false,
keyword: "",
show: false,
focus: false,
},
autoCompleteList: [],
locationSearchList: [],
});
let ElMap = undefined;
let placeSearch = undefined;
let autoComplete = undefined;
let PlaceSearchIndex = 1;
function open() {
state.showLocation = true;
}
function close() {
state.showLocation = false;
}
function autoCompleteSearchBlur() {
setTimeout(() => {
state.searchOption.show = false;
}, 200);
}
function autoCompleteSearch(keyword) {
if (keyword === "") {
state.autoCompleteList = [];
return;
}
autoComplete.search(keyword, function (status, result) {
if (status === "complete" && result.info === "OK") {
// result
console.log(result);
state.searchOption.show = true;
state.autoCompleteList = result.tips;
}
});
}
function autoCompleteListClick(item) {
console.log(item);
state.searchOption.keyword = item.name;
state.autoCompleteList = [];
placeSearchSearch(item.name);
}
function placeSearchSearch(keyword) {
PlaceSearchIndex = 1;
if (keyword === "") {
state.locationSearchList = [];
return;
}
//
placeSearch.search(keyword, (status, result) => {
console.log(status);
console.log(result);
state.locationSearchList = result.poiList.pois;
});
}
function mapInit(map) {
map.plugin(["AMap.PlaceSearch", "AMap.AutoComplete", "AMap.ToolBar"], () => {
const toolBar = new AMap.ToolBar();
map.addControl(toolBar);
// 2.0AMap.AutoComplete1.4使AMap.Autocomplete
// AutoComplete
var autoOptions = {
city: "西安",
};
autoComplete = new AMap.AutoComplete(autoOptions);
//
placeSearch = new AMap.PlaceSearch({
pageSize: 10, //
pageIndex: PlaceSearchIndex, //
city: "西安", //
citylimit: true, //
map: map, //
// panel: "search_wrap", //
autoFitView: true, // 使 Marker
});
});
ElMap = map;
}
const emits = defineEmits(["choose"]);
function selectLocationHandle(e) {
emits("choose", e);
}
defineExpose({
open,
close,
});
</script>
<style scoped lang="scss">
.map_box {
width: 100%;
position: relative;
.map {
height: 300px;
}
.search_box {
position: absolute;
top: 10px;
left: 10px;
z-index: 3000;
.list {
background-color: #fff;
.item {
height: 40px;
line-height: 40px;
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
padding: 0 10px;
cursor: pointer;
&:hover {
background-color: #eee;
}
}
}
}
.search_wrap {
padding: 6px 0;
.item {
display: flex;
padding: 12px 0;
.left {
flex: 1;
display: flex;
flex-direction: column;
padding-right: 20px;
.location {
color: #999;
padding-top: 4px;
}
}
}
}
}
.amap-sug-result {
z-index: 2000;
}
</style>

View File

@ -106,7 +106,7 @@ export const constantRoutes: RouteRecordRaw[] = [
children: [
{
path: "index",
component: () => import("@/views/shop/index.vue"),
component: () => import("@/views/shop/config/index.vue"),
name: "shopConfig",
meta: {
title: "店铺配置",

View File

@ -77,7 +77,7 @@ export const useCartsStore = defineStore("carts", () => {
return;
}
const newNumber = item.number * 1 + step * 1;
update({ ...item, number: step * 1, pack_number: newNumber < item.pack_number ? (item.pack_number * 1 + step * 1) : item.pack_number });
update({ ...item, number: item.number * 1 + step * 1, pack_number: newNumber < item.pack_number ? (item.pack_number * 1 + step * 1) : item.pack_number });
}
function changeSelCart(cart: CartsState) {
@ -95,8 +95,29 @@ export const useCartsStore = defineStore("carts", () => {
selListIndex.value = list.value.findIndex((item: CartsState) => item.id === cart.id);
}
}
function add(data: any) {
sendMessage('add', data);
const msg = {
number: 1,
is_pack: 0,
is_gift: 0,
is_temporary: 0,
discount_sale_amount: 0,
discount_sale_note: "",
is_print: 0,
is_wait_call: 0,
product_name: "",
remark: "",
...data
}
const hasCart = list.value.find((cartItem) => {
return cartItem.product_id == msg.product_id && cartItem.sku_id == msg.sku_id;
});
if (hasCart) {
update({ ...hasCart, ...msg, number: hasCart.number * 1 + msg.number * 1 })
} else {
sendMessage('add', msg);
}
}
function del(data: any) {
@ -108,14 +129,39 @@ export const useCartsStore = defineStore("carts", () => {
sendMessage('edit', data);
}
function updateTag(key: string, val: any, cart: CartsState) {
sendMessage('edit', { ...cart || selCart.value, number: 0, [key]: val });
sendMessage('edit', { ...cart || selCart.value, [key]: val });
}
function clear() {
sendMessage('cleanup', {});
}
// 寻找套餐商品sku
interface GroupSnap {
goods: { [key: string]: any }[];
}
function findInGroupSnapSku(groupSnap: GroupSnap[], sku_id: string | number) {
for (let i in groupSnap) {
const sku = groupSnap[i].goods.find(v => v.sku_id == sku_id)
if (sku) {
return sku
}
}
}
function getProductDetails(v: { product_id: string, sku_id: string }) {
const goods = goodsMap[v.product_id]
const skuData = goods?.skuList.find((sku: { id: string, salePrice: number }) => sku.id == v.sku_id);
if (!goods) {
return undefined
}
let skuData = undefined;
if (goods.type == 'package') {
//套餐商品
const SnapSku = findInGroupSnapSku(goods.groupSnap, v.sku_id)
skuData = { ...SnapSku, salePrice: SnapSku ? SnapSku.price : 0 }
} else {
skuData = goods?.skuList.find((sku: { id: string, salePrice: number }) => sku.id == v.sku_id);
}
if (skuData) {
return {
salePrice: skuData ? skuData.salePrice : 0,
@ -136,8 +182,10 @@ export const useCartsStore = defineStore("carts", () => {
if (msg.hasOwnProperty('status') && msg.status != 1) {
return ElMessage.error(msg.message || '操作失败')
}
if (msg && msg.data && msg.data[0]) {
table_code.value = table_code.value ? table_code.value : msg.data[0].table_code
if (msg && msg.data) {
const tableCode = Array.isArray(msg.data) ? msg.data[0].table_code : msg.data.table_code
table_code.value = table_code.value ? table_code.value : tableCode
console.log(table_code.value)
}
// 初始化
if (msg.operate_type === "manage_init") {
@ -158,17 +206,21 @@ export const useCartsStore = defineStore("carts", () => {
});
console.log(giftList.value)
}
//广播
if (msg.type === "bc") {
msg.operate_type = 'manage_' + msg.operate_type
}
if (msg.operate_type === "manage_add") {
const skuData = getProductDetails({ product_id: msg.data[0].product_id, sku_id: msg.data[0].sku_id })
list.value.push({ ...skuData, ...msg.data[0] })
const skuData = getProductDetails({ product_id: msg.data.product_id, sku_id: msg.data.sku_id })
list.value.push({ ...skuData, ...msg.data })
return ElMessage.success(msg.message || '添加成功')
}
if (msg.operate_type === "manage_edit") {
const newCart = msg.data[0]
const newCart = msg.data
const index = list.value.findIndex((item) => item.id === newCart.id)
const giftIndex = giftList.value.findIndex((item) => item.id === newCart.id)
const cartItem = list.value[index];
const cartItem = list.value[index] || { is_gift: false };
const giftItem = giftList.value[giftIndex];
if (isSelGift.value) {
//操作赠菜
@ -227,10 +279,11 @@ export const useCartsStore = defineStore("carts", () => {
});
}
function sendMessage(operate_type: msgType, message: any) {
console.log({ ...message, operate_type: operate_type, table_code: table_code.value })
WebSocketManager.sendMessage({ ...message, operate_type: operate_type, table_code: table_code.value });
const msg = { ...message, operate_type: operate_type, table_code: table_code.value }
WebSocketManager.sendMessage(msg);
}
return {
table_code,
updateTag,
list,
add,

View File

@ -53,7 +53,6 @@ export const useUserStore = defineStore("user", () => {
return;
}
localStorage.setItem("shopId", "" + userInfo.value.shopId);
console.log("获取用户信息", data);
Object.assign(userInfo.value, { ...data, roles: [], promissionList: [], shopId: userInfo.value.shopId });
resolve(userInfo.value);
})

View File

@ -25,8 +25,8 @@ body {
width: 100%;
height: 100%;
margin: 0;
font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB",
"Microsoft YaHei", "微软雅黑", Arial, sans-serif;
font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei",
"微软雅黑", Arial, sans-serif;
line-height: inherit;
-moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased;
@ -74,3 +74,7 @@ a:active,
div:focus {
outline: none;
}
p {
margin: 0;
line-height: 1;
}

View File

@ -53,7 +53,6 @@ class WebSocketManager {
return this.client;
}
const url = qs.stringify(this.initParams)
console.log(this.initParams)
this.client = new WebSocket(endpoint + '?' + url);
this.client.onopen = () => {
this.connected = true;
@ -108,8 +107,6 @@ class WebSocketManager {
// 订阅主题
public subscribeToTopic(initParams: ApifoxModel, onMessage: (message: any) => void) {
console.log(`正在订阅主题: `);
console.log(initParams);
this.initParams = { ...this.initParams, ...initParams }
if (this.client && this.connected) {
this.disconnect();

View File

@ -0,0 +1,129 @@
<template>
<div>
<el-form ref="form" :model="form" label-width="120px" label-position="left">
<el-form-item label="操作密码">
<el-input
v-model="form.password"
type="number"
@input="jiantingshuru"
:disabled="disabled"
:placeholder="disabled ? '******' : '请输入操作密码'"
style="width: 200px"
></el-input>
<el-button type="primary" @click="resetting">重置</el-button>
</el-form-item>
<el-form-item label="安全手机号">
{{ form.phone | phoneFilter }}
</el-form-item>
<el-form-item label="验证码">
<el-input
v-model="form.prepareAmount"
placeholder="点击发送"
style="width: 200px"
></el-input>
<el-button type="primary" @click="onSubmit">发送</el-button>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitHandle">保存</el-button>
</el-form-item>
</el-form>
<hr />
<el-form ref="form" :model="form" label-width="120px" label-position="left">
<el-form-item label="校验安全密码">
<el-checkbox v-model="form.isReturn">退款</el-checkbox>
<el-checkbox v-model="form.isMemberIn">会员充值</el-checkbox>
<el-checkbox v-model="form.isMemberReturn">会员退款</el-checkbox>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitHandles">保存</el-button>
</el-form-item>
</el-form>
</div>
</template>
<script>
import ShopApi from "@/api/account/shop";
export default {
data() {
return {
disabled: true,
form: {},
};
},
mounted() {
this.getinfo();
},
methods: {
jiantingshuru(e) {
this.form.password = e.substr(0, 6);
},
//
async getinfo() {
const res = await ShopApi.get();
this.form = res;
this.form.isReturn = this.form.isReturn == 1 ? true : false;
this.form.isMemberIn = this.form.isMemberIn == 1 ? true : false;
this.form.isMemberReturn = this.form.isMemberReturn == 1 ? true : false;
},
async submitHandle() {
if (!this.form.prepareAmount) {
this.$message({
message: "请输入验证码",
});
return;
}
if (!this.form.password) {
this.$message({
message: "请输入密码",
});
return;
}
const res = await modfiyUserInfo({
code: this.form.prepareAmount,
pwd: md5(this.form.password),
});
console.log(222);
this.form.prepareAmount = "";
this.form.password = "******";
this.disabled = true;
this.$message({
message: "修改成功",
type: "success",
});
},
async submitHandles() {
const res = await tbShopInfoPUT({
// code: this.form.prepareAmount,
// id: this.form.id,
// status:this.form.status,
...this.form,
isReturn: this.form.isReturn == true ? 1 : 0,
isMemberIn: this.form.isMemberIn == true ? 1 : 0,
isMemberReturn: this.form.isMemberReturn == true ? 1 : 0,
});
this.$message({
message: "修改成功",
type: "success",
});
},
resetting() {
this.form.password = "";
this.disabled = false;
},
async onSubmit() {
const res = await sendMsg();
this.$message({
message: "发送成功",
type: "success",
});
},
},
filters: {
phoneFilter(d) {
let str = d + "";
return str.substr(0, 3) + "***" + str.substr(-4);
},
},
};
</script>

View File

@ -0,0 +1,562 @@
<template>
<div>
<div>
<el-form ref="form" :model="form" :rules="rules" label-width="160px" label-position="left">
<el-form-item label="门店名称" prop="shopName">
<el-input
v-model.trim="form.shopName"
placeholder="请输入门店名称"
style="width: 500px"
></el-input>
</el-form-item>
<el-form-item label="连锁店扩展店名">
<el-input
v-model.trim="form.chainName"
placeholder="请输入连锁店扩展店名"
style="width: 500px"
></el-input>
</el-form-item>
<el-form-item label="门店logo">
<div class="img_box">
<single-image-upload
style="width: 80px; height: 80px"
v-model="form.logo"
></single-image-upload>
</div>
</el-form-item>
<!-- <el-form-item label="门店照片">
<div class="img_box">
<el-image :src="form.coverImg || require('@/assets/images/upload.png')" fit="contain"
style="width: 80px;height: 80px;" @click="
showUpload = true;
uploadIndex = 2;
"></el-image>
<el-button type="primary" plain v-if="form.coverImg"
@click="downloadImgHandle(form.coverImg)">下载</el-button>
</div>
</el-form-item> -->
<el-form-item label="门店收款码">
<div class="img_box">
<canvas ref="canvas" id="QRCode_header" style="width: 80px; height: 80px"></canvas>
<el-button
size="mini"
plain
v-if="form.paymentQrcode"
@click="downloadCanvas(form.paymentQrcode)"
>
下载
</el-button>
</div>
</el-form-item>
<el-form-item label="微信二维码">
<div class="img_box">
<single-image-upload
style="width: 80px; height: 80px"
v-model="form.shopQrcode"
></single-image-upload>
</div>
</el-form-item>
<el-form-item label="店铺小程序码">
<div class="img_box">
<el-image
:src="form.smallQrcode || img_download_error"
fit="contain"
style="width: 80px; height: 80px"
></el-image>
<el-button
size="mini"
plain
v-if="form.shopQrcode"
@click="downloadImgHandle(form.smallQrcode)"
>
下载
</el-button>
</div>
</el-form-item>
<el-form-item label="经营模式「单选」">
<el-radio-group v-model="form.registerType">
<el-radio value="munchies">快餐版先支付后下单</el-radio>
<el-radio value="restaurant">餐饮版先下单后支付</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="就餐模式「多选」">
<el-checkbox-group v-model="form.eatModel">
<el-checkbox value="dine-in">堂食自取</el-checkbox>
<el-checkbox value="take-out">允许打包</el-checkbox>
</el-checkbox-group>
</el-form-item>
<!-- <el-form-item label="积分群体">
<el-radio-group v-model="form.consumeColony">
<el-radio label="all">所有</el-radio>
<el-radio label="vip">仅针对会员</el-radio>
</el-radio-group>
</el-form-item> -->
<el-form-item label="联系电话" prop="phone">
<el-input
v-model.trim="form.phone"
placeholder="请输入联系电话"
style="width: 500px"
></el-input>
</el-form-item>
<!-- <el-form-item label="外卖起送金额">
<el-input-number v-model="form.takeaway_money" placeholder="0.00" controls-position="right"
:min="0"></el-input-number>
</el-form-item> -->
<el-form-item label="店铺经度" prop="provinces">
<el-row gutter="6">
<el-col :span="9" v-if="form.provinces">
<el-input :value="`${form.provinces}-${form.cities}-${form.districts}`" disabled />
</el-col>
<el-col :span="4" v-if="form.lng">
<el-input v-model="form.lng" placeholder="经度" disabled></el-input>
</el-col>
<el-col :span="4" v-if="form.lng">
<el-input v-model="form.lat" placeholder="纬度" disabled></el-input>
</el-col>
<el-col :span="4">
<el-button type="primary" plain icon="place" @click="chooseAddressShow">
选择坐标
</el-button>
</el-col>
</el-row>
<div style="color: #999">准确的定位便于用户导航到店铺</div>
</el-form-item>
<el-form-item label="门店详细地址">
<el-input
type="textarea"
v-model.trim="form.address"
placeholder="请输入门店详细地址"
style="width: 500px"
></el-input>
</el-form-item>
<el-form-item label="营业时间">
<div class="u-flex gap-2" style="width: 50%">
<el-select v-model="form.businessStartDay" placeholder="周几开始">
<el-option
:value="item.label"
:label="item.label"
v-for="item in weeks"
:key="item.value"
></el-option>
</el-select>
<el-select v-model="form.businessEndDay" placeholder="周几结束">
<el-option
:value="item.label"
:label="item.label"
v-for="item in weeks"
:key="item.value"
></el-option>
</el-select>
<el-time-picker
placeholder="起始时间"
v-model="startTime"
:picker-options="{
selectableRange: '00:00:00 - 23:59:59',
format: 'HH:mm',
}"
format="HH:mm"
value-format="HH:mm"
></el-time-picker>
<el-time-picker
placeholder="结束时间"
v-model="endTime"
:picker-options="{
selectableRange: '00:00:00 - 23:59:59',
}"
format="HH:mm"
value-format="HH:mm"
></el-time-picker>
</div>
</el-form-item>
<el-form-item label="桌位费/位/元">
<el-input-number :disabled="!!form.isTableFee" v-model="form.tableFee" :min="0" />
<!-- <el-checkbox v-model="form.isTableFee" :label="1">免餐位费</el-checkbox> -->
<el-switch
class="u-m-l-10"
v-model.trim="form.isTableFee"
:active-value="1"
:inactive-value="0"
active-text="免餐位费"
></el-switch>
</el-form-item>
<!-- <el-form-item label="是否开启8折活动">
<el-switch v-model.trim="form.isOpenYhq" active-value="true" inactive-value="false"></el-switch>
<div style="color: #999;">是否允许用户在小程序端支付订单</div>
</el-form-item> -->
<el-form-item label="是否启用商品会员价">
<el-switch
v-model.trim="form.isMemberPrice"
:active-value="1"
::inactive-value="0"
></el-switch>
<!-- <div style="color: #999;">是否允许用户在小程序端支付订单</div> -->
</el-form-item>
<el-form-item label="是否开启会员余额支付">
<el-switch
v-model.trim="form.isAccountPay"
:active-value="1"
:inactive-value="0"
></el-switch>
<!-- <div style="color: #999;">是否允许用户在小程序端支付订单</div> -->
</el-form-item>
<!-- <el-form-item label="结算类型">
<el-radio-group v-model="form.settleType">
<el-radio :label="0">今日</el-radio>
<el-radio :label="1">次日</el-radio>
</el-radio-group>
</el-form-item> -->
<!-- <el-form-item label="结算时间" prop="settleTime">
<el-time-picker
placeholder="请选择结算时间"
v-model="form.settleTime"
:picker-options="{
selectableRange: '00:00:00 - 23:59:59',
format: 'HH:mm'
}"
format="HH:mm"
value-format="HH:mm"
>
</el-time-picker>
</el-form-item> -->
<el-form-item label="店铺简介">
<el-input
type="textarea"
v-model.trim="form.detail"
placeholder="请输入店铺简介"
style="width: 500px"
></el-input>
</el-form-item>
<el-form-item label="台桌预订短信">
<el-input
type="textarea"
v-model.trim="form.bookingSms"
placeholder="请输入台桌预订短信"
style="width: 500px"
></el-input>
</el-form-item>
<el-form-item label="状态">
<el-radio-group v-model="form.status">
<el-radio :value="1">营业中</el-radio>
<el-radio :value="2">休息中</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitHandle" :loading="formLoading">
<span v-if="!formLoading">保存</span>
<span v-else>保存中...</span>
</el-button>
</el-form-item>
</el-form>
</div>
<ChooseAddress ref="refChooseAddress" @choose="chooseAddressConfirm"></ChooseAddress>
<el-dialog
v-model="showUpload"
:close-on-click-modal="false"
append-to-body
width="500px"
@close="showUpload = false"
>
<el-upload
:before-remove="handleBeforeRemove"
:on-success="handleSuccess"
:on-error="handleError"
:file-list="fileList"
:headers="headers"
:action="qiNiuUploadApi"
:limit="1"
list-type="picture"
class="upload-demo"
>
<el-button size="small" type="primary">点击上传</el-button>
<template #tip>
<div style="display: block" class="el-upload__tip">请勿上传违法文件且文件不超过15M</div>
</template>
</el-upload>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="doSubmit">确认</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script>
import { ElMessage } from "element-plus";
import QRCode from "qrcode";
import img_download_error from "@/assets/images/img_download_error.png";
import ShopApi from "@/api/account/shop";
// import { tbShopInfo, tbShopInfoPut } from "@/api/user";
// import { geocode } from "@/api/shop";
export default {
data() {
return {
img_download_error,
showUpload: false,
uploadIndex: 1,
startTime: "",
endTime: "",
formLoading: false,
form: {
eatModel: [],
paymentQrcode: "",
consumeColony: "all",
},
rules: {
shopName: [
{
required: true,
message: " ",
trigger: "blur",
},
],
phone: [
{
required: true,
message: " ",
trigger: "blur",
},
],
settleTime: [
{
required: true,
message: " ",
trigger: "blur",
},
],
},
fileList: [],
files: [],
searchOption: {
city: "西安",
citylimit: false,
},
locationSearchList: [],
amapOptions: {
center: [108.946465, 34.347984],
position: [],
},
weeks: [
{
value: "1",
label: "周一",
},
{
value: "2",
label: "周二",
},
{
value: "3",
label: "周三",
},
{
value: "4",
label: "周四",
},
{
value: "5",
label: "周五",
},
{
value: "6",
label: "周六",
},
{
value: "7",
label: "周天",
},
],
};
},
mounted() {
this.tbShopInfo();
},
methods: {
chooseAddressConfirm(e) {
console.log(e);
this.$refs.refChooseAddress.close();
this.selectLocationHandle(e);
},
chooseAddressShow() {
this.$refs.refChooseAddress.open();
},
// url
downloadImgHandle(url) {
if (url) window.open(url, "_blank");
},
//
downloadCanvas(url) {
if (url) {
this.saveCanvasAsImage(this.$refs.canvas, "pay_code");
}
},
saveCanvasAsImage(canvas, filename) {
// canvasURL
const dataURL = canvas.toDataURL("image/png");
// a
const downloadLink = document.createElement("a");
downloadLink.href = dataURL;
downloadLink.download = filename;
document.body.appendChild(downloadLink);
//
downloadLink.click();
//
document.body.removeChild(downloadLink);
},
onSearchResult(res) {
this.locationSearchList = res;
this.amapOptions.center = [res[0].lng, res[0].lat];
},
//
async selectLocationHandle(item) {
this.form.lng = item.location.lng;
this.form.lat = item.location.lat;
this.form.address = item.address;
const position = `${item.location.lng},${item.location.lat}`;
this.form.provinces = item.pname;
this.form.cities = item.cityname;
this.form.districts = item.adname;
},
async tbShopInfo() {
try {
const shopId = localStorage.getItem("shopId");
const res = await ShopApi.get();
this.form = { ...res, eatModel: res.eatModel.split(",") };
if (res.businessTime) {
const businessTime = res.businessTime.split("-");
this.startTime = businessTime[0];
this.endTime = businessTime[1];
}
QRCode.toCanvas(
this.$refs.canvas,
this.form.paymentQrcode,
{ margin: 0 },
function (error) {
console.log(error);
}
);
} catch (error) {
console.log(error);
}
},
//
submitHandle() {
this.$refs.form.validate(async (valid) => {
if (valid) {
this.formLoading = true;
try {
if (this.startTime && this.endTime) {
this.form.businessTime = `${this.startTime}-${this.endTime}`;
}
console.log(this.startTime, this.endTime);
const res = await ShopApi.edit({
...this.form,
eatModel: this.form.eatModel.join(","),
});
this.formLoading = false;
ElMessage.success({
title: "成功",
message: "提交成功",
});
setTimeout(() => {
// location.reload();
}, 1000);
} catch (error) {}
}
});
},
handleSuccess(response, file, fileList) {
// const uid = file.uid
// const id = response.id
// this.files.push({ uid, id })
console.log("上传成功", response);
this.files = response.data;
},
handleBeforeRemove(file, fileList) {
for (let i = 0; i < this.files.length; i++) {
if (this.files[i].uid === file.uid) {
crudQiNiu.del([this.files[i].id]).then((res) => {});
return true;
}
}
},
handlePictureCardPreview(file) {
this.dialogImageUrl = file.url;
this.dialogVisible = true;
},
//
handleError(e, file, fileList) {
const msg = JSON.parse(e.message);
this.crud.notify(msg.message, CRUD.NOTIFICATION_TYPE.ERROR);
},
//
doSubmit() {
this.fileList = [];
this.showUpload = false;
switch (this.uploadIndex) {
case 1:
this.form.logo = this.files[0];
break;
case 2:
this.form.coverImg = this.files[0];
break;
case 3:
this.form.shopQrcode = this.files[0];
break;
default:
break;
}
},
},
};
</script>
<style scoped lang="scss">
.img_box {
display: flex;
align-items: flex-end;
gap: 20px;
padding-bottom: 20px;
}
.map_box {
width: 100%;
position: relative;
.map {
height: 300px;
}
.search_box {
position: absolute;
top: 10px;
left: 10px;
}
.search_wrap {
padding: 6px 0;
.item {
display: flex;
padding: 12px 0;
.left {
flex: 1;
display: flex;
flex-direction: column;
padding-right: 20px;
.location {
color: #999;
padding-top: 4px;
}
}
}
}
}
</style>

View File

@ -0,0 +1,27 @@
<template>
<div class="app-container">
<div class="u-m-10 bg-fff u-p-20">
<el-tabs v-model="activeName" type="card">
<el-tab-pane label="店铺信息" name="1"></el-tab-pane>
<el-tab-pane label="安全设置" name="4"></el-tab-pane>
<el-tab-pane label="跳转小程序" name="6" v-if="shopId == 1"></el-tab-pane>
<el-tab-pane label="通知中心" name="7"></el-tab-pane>
</el-tabs>
<shopInfo v-if="activeName == 1" />
<safe v-if="activeName == 4" />
<!-- <goxcx v-if="activeName == 6" />
<notifications v-if="activeName == 7" /> -->
</div>
</div>
</template>
<script setup>
import shopInfo from "./components/shopInfo.vue";
import safe from "./components/safe.vue";
const state = reactive({
activeName: "1",
shopId: localStorage.getItem("shopId"),
});
const { activeName, shopId } = toRefs(state);
</script>

View File

@ -82,18 +82,18 @@
<template v-if="item.is_gift || item.status == 'return'">
<div>0</div>
<div class="free-price">
<span v-if="isSeatFee">{{ item.totalAmount }}</span>
<span v-else>{{ isShowVipPrice ? vipAllPrice : allPrice }}</span>
<span v-if="isSeatFee">{{ to2(item.totalAmount) }}</span>
<span v-else>{{ to2(isShowVipPrice ? vipAllPrice : allPrice) }}</span>
</div>
</template>
<template v-else-if="item.discountSaleAmount">
<div>{{ discountNewPrice }}</div>
<div class="free-price">
<span>{{ isShowVipPrice ? vipAllPrice : allPrice }}</span>
<span>{{ to2(isShowVipPrice ? vipAllPrice : allPrice) }}</span>
</div>
</template>
<template v-else>
<div v-if="isSeatFee">{{ item.totalAmount }}</div>
<div v-if="isSeatFee">{{ to2(item.totalAmount) }}</div>
<div v-else>
<div v-if="isShowVipPrice && vipAllPrice != allPrice">{{ vipAllPrice }}</div>
<div
@ -101,7 +101,7 @@
'free-price': isShowVipPrice && vipAllPrice != allPrice,
}"
>
<span>{{ allPrice }}</span>
<span>{{ to2(allPrice) }}</span>
</div>
</div>
</template>
@ -161,7 +161,9 @@ const props = defineProps({
default: 0,
},
});
function to2(n) {
return n.toFixed(2);
}
let number = ref(0);
const currentPrice = computed(() => {

View File

@ -35,9 +35,9 @@
</div>
</div>
<div class="btn-group">
<el-button type="primary">微信/支付宝</el-button>
<el-button type="primary">现金</el-button>
<el-button type="primary">更多支付</el-button>
<el-button type="primary" @click="createOrder">微信/支付宝</el-button>
<el-button type="primary" @click="createOrder">现金</el-button>
<el-button type="primary" @click="createOrder">更多支付</el-button>
</div>
</div>
</div>
@ -58,11 +58,14 @@ const props = defineProps({
},
});
const emits = defineEmits(["editNote"]);
const emits = defineEmits(["editNote", "createOrder"]);
function editNote() {
emits("editNote");
}
function createOrder() {
emits("createOrder");
}
const selCartId = ref(null);
const carts = useCartsStore();
@ -74,7 +77,12 @@ watch(
for (let goods of props.goodsList) {
goodsMap[goods.id] = goods;
}
carts.init({}, goodsMap);
carts.init(
{
table_code: "",
},
goodsMap
);
}
}
);
@ -86,7 +94,6 @@ function itemClick(item) {
function changeNumber(step, item) {
carts.changeNumber(step * 1, item);
}
defineExpose({
carts,
});

View File

@ -0,0 +1,242 @@
<template>
<el-dialog title="选择用户" width="850px" v-model="show" top="20px">
<div class="app-container">
<div class="head-container">
<el-form :model="query" inline>
<el-form-item label="">
<el-input v-model="query.name" placeholder="请输入昵称或手机号"></el-input>
</el-form-item>
<el-form-item>
<div class="flex gap-20">
<el-button type="primary" @click="getTableData" size="medium">搜索</el-button>
<el-button @click="noChooseUser" size="medium">不选择用户</el-button>
</div>
</el-form-item>
</el-form>
</div>
<div class="head-container">
<el-table :data="tableData.data" v-loading="tableData.loading" @cell-click="cellClick">
<el-table-column label="ID" prop="id"></el-table-column>
<el-table-column label="用户" prop="headImg" width="200px">
<template v-slot="scope">
<div class="user_info">
<el-image
:src="scope.row.headImg"
style="width: 40px; height: 40px; flex-shrink: 0"
>
<template #error>
<div class="image-slot">
<i class="el-icon-user"></i>
</div>
</template>
</el-image>
<span class="name">{{ scope.row.nickName }}</span>
</div>
</template>
</el-table-column>
<el-table-column label="手机号" prop="telephone" width="160"></el-table-column>
<el-table-column label="会员" prop="isVip">
<template v-slot="scope">
<el-tag type="warning" v-if="scope.row.isVip">会员等级{{ scope.row.isVip }}</el-tag>
<span v-else></span>
</template>
</el-table-column>
<el-table-column label="余额" prop="amount"></el-table-column>
<el-table-column label="积分" prop="accountPoints"></el-table-column>
<el-table-column label="操作" width="90" fixed="right">
<template v-slot="scope">
<el-button type="primary" size="mini" @click="choose(scope.row)">选择</el-button>
</template>
</el-table-column>
</el-table>
</div>
<div class="head-container">
<el-pagination
:total="tableData.total"
:current-page="tableData.page + 1"
:page-size="tableData.size"
@size-change="sizeChange"
@current-change="paginationChange"
layout="total, sizes, prev, pager, next, jumper"
></el-pagination>
</div>
</div>
</el-dialog>
</template>
<script setup>
import shopUserApi from "@/api/account/shopUser";
import dayjs from "dayjs";
let cacheData = {};
const state = reactive({
show: false,
query: {
name: "",
isVip: 1,
},
tableData: {
data: [],
page: 0,
size: 10,
loading: false,
total: 0,
},
});
const { tableData, query, show } = toRefs(state);
function timeFilter(s) {
return dayjs(s).format("YYYY-MM-DD HH:mm:ss");
}
const emits = defineEmits(["chooseUser"]);
function cellClick(user) {
console.log(user);
emits("chooseUser", user);
close();
}
function noChooseUser() {
emits("chooseUser", null);
close();
}
function choose(user) {
emits("chooseUser", user);
close();
}
function close() {
show.value = false;
}
function open() {
getTableData();
show.value = true;
}
const router = useRouter();
function toPage(type) {
const pages = {
charge: "charge_list",
cost: "cost_list",
};
router.push({
name: pages[type],
});
}
function sizeChange() {
tableData.value.page = 0;
getTableData();
}
//
async function statusChange(e, row) {
try {
tableData.value.loading = true;
const data = { ...row };
data.status = e;
await modityActivate(data);
getTableData();
} catch (error) {
console.log(error);
tableData.value.loading = false;
}
}
//
function resetHandle() {
query.valuename = "";
getTableData();
}
//
function paginationChange(e) {
tableData.value.page = e - 1;
getTableData();
}
async function getTableData() {
tableData.value.loading = true;
try {
const res = await shopUserApi.getList({
...query.value,
size: tableData.value.size,
page: tableData.value.page + 1,
});
tableData.value.loading = false;
tableData.value.data = res.records;
tableData.value.total = res.totalRow * 1;
} catch (error) {
console.log(error);
}
}
defineExpose({
open,
close,
});
</script>
<style scoped lang="scss">
.user_info {
display: flex;
align-items: center;
.name {
margin-left: 10px;
}
}
::v-deep .el-input--small .el-input__inner {
height: 36px;
line-height: 36px;
}
::v-deep .image-slot {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
background-color: #efefef;
font-size: 20px;
color: #999;
}
.card {
background-color: #f5f5f5;
padding: 0 14px;
.title {
font-size: 22px;
padding-top: 14px;
}
.row {
display: flex;
padding: 20px 0;
.item {
flex: 1;
.t {
text-align: center;
color: #555;
}
.n {
color: #000;
font-size: 20px;
font-weight: bold;
padding-top: 6px;
text-align: center;
}
}
}
}
.flex {
display: flex;
align-items: center;
}
.gap-20 {
gap: 20px;
}
</style>

View File

@ -1,12 +1,12 @@
<template>
<div class="controls">
<div class="input-number">
<div class="reduce">
<el-icon @click="carts.changeNumber(-1, carts.selCart)"><Minus /></el-icon>
<div class="reduce" @click="carts.changeNumber(-1, carts.selCart)">
<el-icon><Minus /></el-icon>
</div>
<span class="text">{{ carts.selCart.number || 1 }}</span>
<div class="add">
<el-icon @click="carts.changeNumber(1, carts.selCart)"><Plus /></el-icon>
<div class="add" @click="carts.changeNumber(1, carts.selCart)">
<el-icon><Plus /></el-icon>
</div>
</div>
<el-button

View File

@ -0,0 +1,178 @@
<template>
<el-dialog width="400px" :title="title" v-model="show">
<div class="u-p-15">
<div class="u-m-t-20">
<el-form label-width="90px" label-position="left">
<el-form-item label="应付金额">
<div class="color-red u-font-18 font-600">{{ form.money }}</div>
<!-- <el-input :value="form.money" disabled> </el-input> -->
</el-form-item>
<el-form-item label="减免金额">
<el-input
v-model="form.reduceMoney"
clearable
autofocus
type="number"
@keyup.enter="init('reduceMoney')"
@blur="init('reduceMoney')"
>
<template #append></template>
</el-input>
</el-form-item>
<el-form-item label="优惠折扣">
<el-input
v-model="form.discount"
type="number"
@keyup.enter="init('discount')"
@blur="init('discount')"
>
<template #append>%</template>
</el-input>
</el-form-item>
<el-form-item label="实收金额">
<el-input
v-model="form.curretnMoney"
type="number"
clearable
@keyup.enter="init('curretnMoney')"
@blur="init('curretnMoney')"
>
<template #append></template>
</el-input>
</el-form-item>
<div class="u-flex u-row-center u-m-t-50">
<el-button size="medium" @click="close">取消</el-button>
<el-button size="medium" type="primary" @click="confirm">确定</el-button>
</div>
</el-form>
</div>
</div>
</el-dialog>
</template>
<script>
function toFixedNoRounding(num) {
//
var numStr = num.toString();
//
var parts = numStr.split(".");
// 2
if (parts[1] && parts[1].length > 2) {
parts[1] = parts[1].slice(0, 2);
}
//
return parts.join(".");
}
export default {
props: {
title: {
type: String,
default: "优惠金额",
},
value: {
type: [String, Number],
default: 0,
},
},
data() {
return {
form: {
money: 0,
discount: 100,
reduceMoney: 0,
curretnMoney: 0,
},
number: "0",
show: false,
};
},
methods: {
init(key) {
const { money, reduceMoney, discount, curretnMoney } = this.form;
if (key == "reduceMoney") {
if (reduceMoney < 0) {
this.$message.error("减免金额不能小于0");
this.form.reduceMoney = 0;
}
if (reduceMoney > money) {
this.$message.error("减免金额不能大于总金额");
this.form.reduceMoney = money;
}
this.form.curretnMoney = (money - this.form.reduceMoney).toFixed(2);
this.form.discount = toFixedNoRounding(((this.form.curretnMoney / money) * 100).toFixed(3));
return;
}
if (key == "discount") {
if (discount < 0) {
this.$message.error("折扣不能小于0");
this.form.discount = 0;
}
if (discount > 100) {
this.$message.error("折扣不能大于100");
this.form.discount = 100;
}
this.form.curretnMoney = ((money * this.form.discount) / 100).toFixed(2);
this.form.reduceMoney = ((money * (100 - this.form.discount)) / 100).toFixed(2);
return;
}
if (key == "curretnMoney") {
if (curretnMoney < 0) {
this.$message.error("实收金额不能小于0");
this.form.curretnMoney = 0;
}
if (curretnMoney > money) {
this.$message.error("实收金额不能大于总金额");
this.form.curretnMoney = this.form.money;
}
this.form.reduceMoney = (money - this.form.curretnMoney).toFixed(2);
this.form.discount = toFixedNoRounding(((this.form.curretnMoney / money) * 100).toFixed(3));
return;
}
this.form.curretnMoney = ((money * discount) / 100).toFixed(2);
this.form.reduceMoney = (money - this.form.curretnMoney).toFixed(2);
},
changeKey(key, val) {
this[key] = val;
},
confirm() {
console.log(this.form.discount / 100);
this.$emit("confirm", {
discount: this.form.discount / 100,
currentPrice: ((this.form.discount * this.form.money) / 100).toFixed(2),
});
this.close();
},
open(data) {
console.log(data);
this.form.money = data.amount * 1;
this.form.discount = data.discount ? toFixedNoRounding(data.discount.toFixed(3)) : 100;
this.show = true;
this.init();
},
close() {
this.show = false;
},
},
mounted() {
this.number = `${this.value}`;
},
};
</script>
<style lang="scss" scoped>
.codeImg {
width: 160px;
border: 1px solid rgb(220, 223, 230);
height: 160px;
}
:deep(.el-input .el-input__inner::-webkit-inner-spin-button) {
-webkit-appearance: none;
margin: 0;
}
:deep(.el-input .el-input__inner::-webkit-outer-spin-button) {
-webkit-appearance: none;
margin: 0;
}
</style>

View File

@ -93,8 +93,9 @@ const props = defineProps({
},
});
let number = ref(0);
const number = defineModel({
default: 0,
});
watch(
() => props.value,
(newval) => {
@ -102,12 +103,6 @@ watch(
}
);
const emits = defineEmits(["input", "confirm"]);
watch(
() => number.value,
(newval) => {
emits("input", newval);
}
);
function clearFunction() {
if (props.isFloat) {
@ -127,6 +122,7 @@ function keyboradAdd(n) {
if (newval * 1 > props.max * 1) {
return ElMessage.error(this.maxTips);
}
console.log(number.value);
number.value = newval;
}
function keyboradReduce() {

View File

@ -0,0 +1,190 @@
<template>
<div class="order-box">
<div class="left">
<div class="user bg-fff u-p-20">
<div class="userinfo">
<el-avatar class="avatar" :size="50" />
<div class="u-m-l-12">
<p>
<span class="name u-font-16">hh</span>
<span class="vip">VIP1</span>
</p>
<p class="u-font-14 color-666 u-m-t-10">
<span class="money">余额4.00</span>
<span class="score u-m-l-10">积分0</span>
</p>
</div>
</div>
<div class="score">
<div class="u-flex u-col-center u-m-t-10">
<span class="u-font-14 font-bold u-m-r-20">积分抵扣</span>
<el-radio-group v-model="score.sel" size="small" class="">
<el-radio :value="0">
<span class="u-font-14">全部抵扣</span>
</el-radio>
<el-radio :value="1">
<span class="u-font-14">部分抵扣</span>
</el-radio>
</el-radio-group>
</div>
<p class="u-font-14 color-666 u-m-t-10">
<span class="color-red">*</span>
<span>积分不足或小于最低使用门槛1</span>
</p>
</div>
<div class="u-flex u-col-center u-m-t-20">
<span class="u-font-14 font-bold u-m-r-20">团购代金券</span>
<div class="u-flex my-select">
<span class="u-m-r-10">代金券名称</span>
<el-icon><ArrowDown /></el-icon>
</div>
<svg-icon iconClass="scan" size="30" class="u-m-l-10 cur-pointer"></svg-icon>
</div>
<div class="u-flex u-col-center u-m-t-20">
<span class="u-font-14 font-bold u-m-r-20">优惠券</span>
<div class="u-flex my-select">
<span class="u-m-r-10">选择优惠券</span>
<el-icon><ArrowDown /></el-icon>
</div>
</div>
</div>
<div class="u-m-t-30">
<el-button size="large" @click="discountShow">整单打折/减免</el-button>
</div>
<div class="u-m-t-30">
<p class="u-font-16 font-bold u-m-r-20 font-bold">选择支付方式</p>
<div class="u-m-t-20">
<el-button
v-for="(item, index) in payType.list"
:key="index"
size="large"
:type="index == payType.sel ? 'primary' : ''"
@click="changePayType(index)"
>
{{ item.label }}
</el-button>
</div>
<div class="u-m-t-20">
<el-button type="primary" size="large">立即支付</el-button>
</div>
</div>
</div>
<div class="right">
<h3>账单明细</h3>
<div class="order-info">
<div class="u-flex u-m-b-10 u-row-between">
<span class="title">订单号</span>
<span class="u-m-l-10 value">202111111111111111</span>
</div>
<div class="u-flex u-m-b-10 u-row-between">
<span class="title">门店优惠</span>
<span class="u-m-l-10 value"></span>
</div>
<div class="u-flex u-m-b-10 u-row-between">
<span class="title">优惠券</span>
<span class="u-m-l-10 value"></span>
</div>
<div class="u-flex u-m-b-10 u-row-between">
<span class="title">积分抵扣</span>
<span class="u-m-l-10 value"></span>
</div>
<div class="u-flex u-m-b-10 u-row-between">
<span class="title">整单改价</span>
<span class="u-m-l-10 value"></span>
</div>
<div class="u-flex u-m-b-10 u-row-between">
<span class="title">餐位费/附加费</span>
<span class="u-m-l-10 value"></span>
</div>
<div class="u-flex u-m-b-10 u-row-between">
<span class="title">总价</span>
<span class="u-m-l-10 value"></span>
</div>
<div class="u-flex u-m-b-10 u-row-between">
<span class="title">抹零</span>
<span class="u-m-l-10 value"></span>
</div>
<div class="u-flex u-m-b-10 u-row-between">
<span class="title">应付金额</span>
<span class="u-m-l-10 value"></span>
</div>
</div>
</div>
</div>
</template>
<script setup>
const score = ref({
list: [],
sel: 0,
});
const emits = defineEmits(["discountShow"]);
function discountShow() {
emits("discountShow");
}
const payType = reactive({
list: [
{ label: "现金", value: "" },
{ label: "扫码支付", value: "" },
{ label: "会员支付", value: "" },
],
sel: 0,
});
function changePayType(i) {
payType.sel = i;
}
</script>
<style lang="scss" scoped>
.userinfo {
display: flex;
align-items: center;
line-height: 1;
}
.vip {
padding: 2px 5px;
background: #f7793d;
color: #fff;
border-radius: 4px;
margin-left: 10px;
font-size: 10px;
}
.order-box {
display: flex;
padding: 20px 20px;
background-color: #f7f7fa;
min-height: 100%;
.left,
.right {
flex: 1;
}
.left {
padding-right: 20px;
}
.right {
border-left: 1px solid #ebebeb;
padding-left: 20px;
.order-info {
font-size: 14px;
.title {
}
.value {
}
.price {
color: #fa5555;
font-size: 20px;
}
}
}
}
.my-select {
border: 1px solid #d9d9d9;
border-radius: 4px;
margin-left: 12px;
cursor: pointer;
font-size: 14px;
color: #666;
padding: 8px 10px 8px 20px;
}
</style>

View File

@ -0,0 +1,124 @@
<template>
<div class="select_desk">
<el-dialog title="可选套餐" v-model="show">
<div v-for="(item, index) in listdata.groupSnap" :key="index">
<div class="box">
<h2 class="boxspan">{{ item.title }}</h2>
<h4 class="boxspan">本组菜品{{ item.count }}{{ item.number || 1 }}</h4>
<el-alert
v-if="item.alertshow"
title="错误:请按照规定选择套餐"
type="warning"
:closable="false"
></el-alert>
</div>
<el-table
ref="refdialogpackagetable"
:data="item.goods"
tooltip-effect="dark"
style="width: 100%"
@selection-change="handleSelectionChange($event, index)"
>
<el-table-column type="selection" width="55"></el-table-column>
<el-table-column label="名称" prop="proName"></el-table-column>
<el-table-column prop="name" label="规格"></el-table-column>
<el-table-column prop="price" label="价格"></el-table-column>
<el-table-column prop="number" label="数量"></el-table-column>
</el-table>
</div>
<div class="buttonbox">
<el-button type="primary" :disabled="disabledshow" @click="confirm">确定</el-button>
<el-button @click="toggleSelection">取消</el-button>
</div>
</el-dialog>
</div>
</template>
<script setup>
const state = reactive({
show: false,
disabledshow: true,
listdata: {
groupSnap: [],
},
tableData: [],
multipleSelection: [],
});
const refdialogpackagetable = ref();
const { show, disabledshow, listdata, tableData, multipleSelection } = toRefs(state);
function toggleSelection() {
try {
refdialogpackagetable.value.forEach((a) => {
a.clearSelection();
});
show.value = false;
} catch (error) {}
}
function handleSelectionChange(val, index) {
try {
listdata.value.groupSnap.forEach((a, i) => {
multipleSelection.value[index] = i === index ? val : multipleSelection.value[index] || [];
});
this.disabledshow = !listdata.value.groupSnap.every(
(element, num) => element.number == multipleSelection.value[num].length
);
} catch (error) {}
listdata.value.groupSnap[index] = {
...listdata.value.groupSnap[index],
alertshow:
listdata.value.groupSnap[index].number != multipleSelection.value[index].length
? true
: false,
};
}
const emits = defineEmits(["dialogpackageconfirm"]);
function confirm() {
emits("dialogpackageconfirm", listdata.value, multipleSelection.value);
show.value = false;
}
function open(item) {
console.log(item);
listdata.value = item;
try {
refdialogpackagetable.value.forEach((a) => {
a.clearSelection();
});
} catch (error) {}
multipleSelection.value = [];
//
if (item.groupType == 0) {
emits("dialogpackageconfirm", listdata.value, [item.groupSnap[0].goods]);
return false;
}
disabledshow.value = true;
show.value = true;
}
defineExpose({
open,
close,
});
</script>
<style lang="scss" scoped>
::v-deep.el-button {
padding: 12px 20px;
}
.select_desk {
.box {
margin: 20px 10px;
.boxspan {
}
}
.buttonbox {
margin: 0 auto;
padding: 20px 0;
text-align: right;
}
}
</style>

View File

@ -106,13 +106,12 @@ function confirm() {
emits("confirm", item.value, (number.value * 1).toFixed(2));
close();
}
function open(item) {
console.log(item);
item.value = item;
function open(data) {
item.value = data;
show.value = true;
}
function close() {
show.valuie = false;
show.value = false;
number.value = "";
item.value = "";
}
@ -131,10 +130,6 @@ defineExpose({
border: none;
}
::v-deep .select_desk_dialog .el-input__inner {
// border: none;
}
::v-deep .el-input__inner::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;

View File

@ -5,8 +5,29 @@
<div class="left u-flex u-col-center">
<span class="title">代客下单</span>
<div class="u-m-l-20 flex">
<el-button type="primary">选择用户</el-button>
<el-popover placement="right" width="333" trigger="click" v-model="tableShow">
<div class="choose-user flex">
<el-button type="primary" v-if="!user.id" @click="showChooseUser">选择用户</el-button>
<div v-else class="flex cur-pointer" @click="refChooseUserOpen">
<img
v-if="user.headImg && user.headImg != 'null'"
class="headimg"
:src="user.headImg"
alt=""
/>
<div v-else class="headimg flex flex-x-y-center">
<i class="el-icon-user"></i>
</div>
<div>
<div class="u-flex">
<div class="ft-13 color-000">{{ user.nickName }}</div>
<div class="vip" v-if="user.isVip">VIP{{ user.isVip }}</div>
</div>
<div style="margin-top: 2px" class="color-666 ft-12">余额{{ user.amount }}</div>
</div>
</div>
</div>
<el-popover placement="right" width="333" trigger="click" ref="refTable">
<el-input
placeholder="请输入内容"
prefix-icon="search"
@ -30,7 +51,7 @@
</div>
</div>
<template #reference>
<el-button>选择桌号</el-button>
<el-button>{{ table.name ? "桌台号:" + table.name : "选择桌号" }}</el-button>
</template>
</el-popover>
<el-button type="warning">扫码验券</el-button>
@ -52,10 +73,12 @@
<div class="diancan">
<div class="left">
<div class="diners">
<!-- 就餐类型 -->
<el-button-group v-model="diners.sel" style="width: 100%; display: flex">
<el-button
:class="{ active: index == diners.sel }"
v-for="(item, index) in diners.list"
@click="changeDinersSel(index)"
:key="index"
>
{{ item.label }}
@ -75,6 +98,7 @@
</div>
<cartsList
@editNote="showNote(true)"
@createOrder="createOrder"
:goodsMapisFinish="goodsMapisFinish"
:goodsList="goods.list"
ref="refCart"
@ -88,48 +112,53 @@
/>
</div>
<div class="right">
<div class="flex categoty u-col-center">
<div
class="show_more_btn"
:class="{ showAll: category.showAll }"
@click="toggleShowAll"
>
<div class="flex">
<div class="flex showmore">
<el-icon color="#fff"><ArrowDown /></el-icon>
</div>
<span>{{ category.showAll ? "收起" : "展开" }}</span>
</div>
</div>
<div class="flex categorys" :class="{ 'flex-wrap': category.showAll }">
<template v-if="!showOrder">
<div class="flex categoty u-col-center">
<div
v-for="(item, index) in category.list"
:key="index"
@click="changeCategoryId(item)"
class="show_more_btn"
:class="{ showAll: category.showAll }"
@click="toggleShowAll"
>
<el-tag
size="large"
:type="goods.query.categoryId === item.id ? 'primary' : 'info'"
effect="dark"
<div class="flex">
<div class="flex showmore">
<el-icon color="#fff"><ArrowDown /></el-icon>
</div>
<span>{{ category.showAll ? "收起" : "展开" }}</span>
</div>
</div>
<div class="flex categorys" :class="{ 'flex-wrap': category.showAll }">
<div
v-for="(item, index) in category.list"
:key="index"
@click="changeCategoryId(item)"
>
{{ item.name }}
</el-tag>
<el-tag
size="large"
:type="goods.query.categoryId === item.id ? 'primary' : 'info'"
effect="dark"
>
{{ item.name }}
</el-tag>
</div>
</div>
</div>
</div>
<div v-if="goods.list.length <= 0" class="no-goods">未找到相关商品</div>
<div class="goods-list">
<div class="lingshicai" @click="showaddLingShiCai">
<el-icon size="26"><Plus /></el-icon>
<div class="u-m-t-10">临时菜</div>
<div v-if="goods.list.length <= 0" class="no-goods">未找到相关商品</div>
<div class="goods-list">
<div class="lingshicai" @click="showaddLingShiCai">
<el-icon size="26"><Plus /></el-icon>
<div class="u-m-t-10">临时菜</div>
</div>
<GoodsItem
:item="item"
@click="goodsClick(item)"
v-for="item in goods.list"
:key="item.id"
></GoodsItem>
</div>
<GoodsItem
:item="item"
@click="goodsClick(item)"
v-for="item in goods.list"
:key="item.id"
></GoodsItem>
</div>
</template>
<!-- 订单信息展示 -->
<Order @discountShow="discountShow" v-else></Order>
</div>
</div>
</div>
@ -146,14 +175,24 @@
<changePrice ref="refChangePrice" @confirm="changePriceConfirm"></changePrice>
<!-- 称重商品 -->
<changeWeight ref="refChangeWeight" @confirm="changeWeightConfirm"></changeWeight>
<!-- 可选套餐 -->
<changeTaocan ref="refAddTaocan" @confirm="taocanConfirm"></changeTaocan>
<!-- 选择用户 -->
<chooseUser ref="refChooseUser" @chooseUser="chooseUserConfirm"></chooseUser>
<!-- 打折 -->
<discount ref="refDiscount" @confirm="discountConfirm"></discount>
</div>
</template>
<script setup>
import Controls from "./components/control.vue";
import discount from "./components/discount.vue";
import note from "./components/note.vue";
import Order from "./components/order.vue";
import pack from "./components/pack.vue";
import changePrice from "./components/popup-cart-changePrice.vue";
import chooseUser from "./components/choose-user.vue";
import changeWeight from "./components/popup-weight-goods.vue";
import changeTaocan from "./components/popup-taocan-goods.vue";
import addLingShiCai from "./components/popup-linshiCai.vue";
import GoodsItem from "./components/goods-item.vue";
import dialogGoodsSel from "./components/dialog-goods-sel.vue";
@ -162,15 +201,62 @@ import categoryApi from "@/api/product/productclassification";
import productApi from "@/api/product/index";
import tableApi from "@/api/account/table";
import $status from "@/views/tool/table/status.js";
import orderApi from "@/api/order/order";
//
const refDiscount = ref();
function discountConfirm(e) {}
function discountShow(e) {
refDiscount.value.open({
amount: 10,
});
}
//
let user = ref({});
const refChooseUser = ref();
function chooseUserConfirm(e) {
console.log(e);
user.value = e;
}
function showChooseUser() {
refChooseUser.value.open();
}
//
const showOrder = ref(false);
function createOrder() {
console.log(refCart.value.carts.table_code);
orderApi.add({
tableCode: refCart.value.carts.table_code,
dineMode: "dine-in",
});
}
//
const refAddTaocan = ref();
function taocanConfirm() {}
function taocanShow(item) {
refAddTaocan.value.open(item);
}
//
const refChangeWeight = ref();
function changeWeightConfirm(e) {}
function changeWeightConfirm(goods, number) {
console.log(goods, number);
addCarts({
product_id: goods.id,
sku_id: goods.skuList[0].id,
number,
});
}
function showWeight(item) {
console.log(item);
refChangeWeight.value.open(item);
}
//
const table = ref({}); //
const refTable = ref();
const tableSearchText = ref("");
const tableList = ref([]);
function getTableList() {
@ -190,6 +276,8 @@ function returnTableLabel(key) {
}
function tableClick(item) {
console.log(item);
table.value = item;
refTable.value.hide();
}
//
@ -258,6 +346,11 @@ const diners = reactive({
],
sel: 0,
});
function changeDinersSel(index) {
diners.sel = index;
}
//
const category = reactive({
list: [],
@ -311,16 +404,7 @@ function goodsClick(item) {
addCarts({
product_id: item.id,
sku_id: item.skuList[0].id,
number: 1,
is_pack: 0,
is_gift: 0,
is_temporary: 0,
discount_sale_amount: 0,
discount_sale_note: "",
is_print: 0,
is_wait_call: 0,
product_name: "",
remark: "",
number: item.skuList[0].suitNum || 1,
});
return;
}
@ -334,6 +418,24 @@ function goodsClick(item) {
showWeight(item);
return;
}
//
if (item.type === "package") {
//
if (item.groupType == 0) {
addCarts({
sku_id: item.groupSnap[0].goods[0].skuId,
product_id: item.id,
number: item.groupSnap[0].goods[0].number,
});
return;
}
//
if (item.groupType == 1) {
taocanShow(item);
return;
}
return;
}
}
//
@ -345,18 +447,7 @@ function skuSelConfirm(item) {
if (listItem) {
refCart.value.carts.update({ ...listItem, ...item });
} else {
refCart.value.carts.add({
is_pack: 0,
is_gift: 0,
is_temporary: 0,
discount_sale_amount: 0,
discount_sale_note: "",
is_print: 0,
is_wait_call: 0,
product_name: "",
remark: "",
...item,
});
addCarts(item);
}
}
@ -372,15 +463,7 @@ function clearCarts() {
});
}
function addCarts(item) {
const hasCart = refCart.value.carts.list.find((cartItem) => {
return cartItem.product_id == item.product_id && cartItem.sku_id == item.sku_id;
});
console.log(hasCart);
if (hasCart) {
refCart.value.carts.update({ ...item, id: hasCart.id });
} else {
refCart.value.carts.add(item);
}
refCart.value.carts.add(item);
}
watch(
@ -585,4 +668,7 @@ $pl: 30px;
.no-goods {
color: #999;
}
.choose-user {
margin-right: 10px;
}
</style>