681 lines
14 KiB
Vue
681 lines
14 KiB
Vue
<template>
|
||
<view class="winelist">
|
||
<!-- 页面标题 -->
|
||
<view class="page-title">3步快速生成酒单</view>
|
||
|
||
<!-- 顶部酒款信息 -->
|
||
<view class="beer-info">
|
||
<view class="info-card" v-if="beerInfo.name">
|
||
<image :src="beerInfo.cover" mode="aspectFill" class="cover"></image>
|
||
<view class="info">
|
||
<view class="title">{{ beerInfo.name }}</view>
|
||
<view class="brewery">{{ beerInfo.brewery }}</view>
|
||
<view class="style">{{ beerInfo.style }}</view>
|
||
</view>
|
||
</view>
|
||
<view class="info-card empty-state" v-if="!beerInfo.name">
|
||
<view class="info">
|
||
<view class="title">请先选择酒款</view>
|
||
<view class="search-btn" @click="toSearch">
|
||
<text class="cuIcon-search"></text>
|
||
<text>搜索酒款</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 步骤引导 -->
|
||
<view class="steps">
|
||
<view class="step-item" :class="{'active': currentStep >= 1}">
|
||
<view class="step-number">1</view>
|
||
<view class="step-content">
|
||
<view class="step-title">输入酒枪编号</view>
|
||
<view class="step-desc">下方输入酒款对应的酒枪编号</view>
|
||
</view>
|
||
</view>
|
||
<view class="step-line" :class="{'active': currentStep >= 2}"></view>
|
||
<view class="step-item" :class="{'active': currentStep >= 2}">
|
||
<view class="step-number">2</view>
|
||
<view class="step-content">
|
||
<view class="step-title">编辑售卖规格</view>
|
||
<view class="step-desc">设置酒款的售卖规格和价格</view>
|
||
</view>
|
||
</view>
|
||
<view class="step-line" :class="{'active': currentStep >= 3}"></view>
|
||
<view class="step-item" :class="{'active': currentStep >= 3}">
|
||
<view class="step-number">3</view>
|
||
<view class="step-content">
|
||
<view class="step-title">选择导出方式</view>
|
||
<view class="step-desc">选择图片或文件格式导出</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 步骤内容区 -->
|
||
<view class="step-content-area">
|
||
<!-- 步骤1:输入酒枪编号 -->
|
||
<view class="step-panel" v-if="currentStep === 1">
|
||
<view class="input-group">
|
||
<text class="label">酒枪编号</text>
|
||
<input type="number" v-model="tapNumber" placeholder="请输入整数" class="input" />
|
||
</view>
|
||
<view class="btn-group">
|
||
<button class="btn next" @click="nextStep">下一步</button>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 步骤2:编辑售卖规格 -->
|
||
<view class="step-panel" v-if="currentStep === 2">
|
||
<view class="specs-list">
|
||
<view class="spec-item" v-for="(spec, index) in specs" :key="index">
|
||
<view class="spec-header">
|
||
<text class="spec-name">{{ spec.name }}</text>
|
||
<text class="delete" @click="deleteSpec(index)">删除</text>
|
||
</view>
|
||
<view class="spec-content">
|
||
<view class="input-group">
|
||
<text class="label">规格</text>
|
||
<view class="input-wrapper">
|
||
<input type="number" v-model="spec.unit" placeholder="请输入数字" class="input" @focus="handleUnitFocus(index)" @blur="handleUnitBlur(index)" />
|
||
<text class="unit" v-if="!spec.unitFocused">ML</text>
|
||
</view>
|
||
</view>
|
||
<view class="input-group">
|
||
<text class="label">价格</text>
|
||
<view class="input-wrapper">
|
||
<input type="number" v-model="spec.price" placeholder="请输入数字" class="input" @focus="handlePriceFocus(index)" @blur="handlePriceBlur(index)" />
|
||
<text class="unit" v-if="!spec.priceFocused">元</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<view class="add-spec" @click="addSpec" v-if="specs.length < 2">
|
||
<text class="cuIcon-add"></text>
|
||
<text>添加规格</text>
|
||
</view>
|
||
<view class="btn-group">
|
||
<button class="btn prev" @click="prevStep">
|
||
<text class="cuIcon-back"></text>
|
||
<text>上一步</text>
|
||
</button>
|
||
<button class="btn next" @click="nextStep">下一步</button>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 步骤3:选择导出方式 -->
|
||
<view class="step-panel" v-if="currentStep === 3">
|
||
<view class="export-options">
|
||
<view class="option-item" @click="toPerview(1)">
|
||
<text class="cuIcon-pic"></text>
|
||
<text>导出图片</text>
|
||
<text class="desc">以图片形式分享</text>
|
||
</view>
|
||
<view class="option-item" @click="toPerview(2)">
|
||
<text class="cuIcon-file"></text>
|
||
<text>导出文件</text>
|
||
<text class="desc">PDF文件,适合打印(460mm*230mm)</text>
|
||
</view>
|
||
</view>
|
||
<view class="btn-group">
|
||
<button class="btn prev" @click="prevStep">
|
||
<text class="cuIcon-back"></text>
|
||
<text>上一步</text>
|
||
</button>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
import {
|
||
getBeerInfo
|
||
} from "@/api/bar.js"
|
||
|
||
// 常量定义
|
||
const MAX_SPECS = 2
|
||
const DEFAULT_SPEC = {
|
||
name: '标准杯',
|
||
price: '',
|
||
unit: '',
|
||
unitFocused: false,
|
||
priceFocused: false
|
||
}
|
||
|
||
export default {
|
||
data() {
|
||
return {
|
||
currentStep: 1,
|
||
beerId: '',
|
||
beerInfo: {
|
||
cover: '',
|
||
name: '',
|
||
brewery: '',
|
||
style: ''
|
||
},
|
||
tapNumber: '',
|
||
specs: [DEFAULT_SPEC]
|
||
}
|
||
},
|
||
computed: {
|
||
canProceed() {
|
||
if (!this.beerInfo.name) return false
|
||
|
||
switch(this.currentStep) {
|
||
case 1: return this.tapNumber.length > 0
|
||
case 2: return this.specs.every(spec => spec.price && spec.unit)
|
||
default: return true
|
||
}
|
||
},
|
||
stepErrorMessage() {
|
||
if (!this.beerInfo.name) return '请先选择酒款'
|
||
|
||
switch(this.currentStep) {
|
||
case 1: return '请输入酒枪编号'
|
||
case 2: return '请完善规格信息'
|
||
default: return '请完成当前步骤'
|
||
}
|
||
}
|
||
},
|
||
onLoad(options) {
|
||
if(options.beerId) {
|
||
this.beerId = options.beerId
|
||
this.getBeerInfo()
|
||
}
|
||
},
|
||
methods: {
|
||
// 数据获取
|
||
async getBeerInfo() {
|
||
try {
|
||
const res = await getBeerInfo(this.beerId)
|
||
this.beerInfo = {
|
||
cover: res.data.cover,
|
||
name: res.data.beerName,
|
||
brewery: res.data.brandName,
|
||
style: res.data.beerStyles
|
||
}
|
||
} catch (error) {
|
||
uni.showToast({
|
||
title: '获取酒款信息失败',
|
||
icon: 'none'
|
||
})
|
||
console.error('获取酒款信息失败:', error)
|
||
}
|
||
},
|
||
|
||
// 步骤控制
|
||
nextStep() {
|
||
if (!this.beerInfo.name) {
|
||
this.showToast('请先选择酒款')
|
||
return
|
||
}
|
||
|
||
if (!this.canProceed) {
|
||
this.showToast(this.stepErrorMessage)
|
||
return
|
||
}
|
||
|
||
if (this.currentStep < 3) {
|
||
this.currentStep++
|
||
}
|
||
},
|
||
|
||
prevStep() {
|
||
if (this.currentStep > 1) {
|
||
this.currentStep--
|
||
}
|
||
},
|
||
|
||
// 规格管理
|
||
addSpec() {
|
||
if (this.specs.length >= MAX_SPECS) {
|
||
this.showToast('最多添加2个规格')
|
||
return
|
||
}
|
||
|
||
this.specs.push({
|
||
...DEFAULT_SPEC,
|
||
name: `规格${this.specs.length + 1}`
|
||
})
|
||
},
|
||
|
||
deleteSpec(index) {
|
||
if (this.specs.length <= 1) {
|
||
this.showToast('至少保留一个规格')
|
||
return
|
||
}
|
||
this.specs.splice(index, 1)
|
||
},
|
||
|
||
// 输入处理
|
||
handleUnitFocus(index) {
|
||
this.specs[index].unitFocused = true
|
||
},
|
||
|
||
handleUnitBlur(index) {
|
||
this.specs[index].unitFocused = false
|
||
if (this.specs[index].unit) {
|
||
this.specs[index].unit = `${this.specs[index].unit}ML`
|
||
}
|
||
},
|
||
|
||
handlePriceFocus(index) {
|
||
this.specs[index].priceFocused = true
|
||
},
|
||
|
||
handlePriceBlur(index) {
|
||
this.specs[index].priceFocused = false
|
||
if (this.specs[index].price) {
|
||
this.specs[index].price = `${this.specs[index].price}元`
|
||
}
|
||
},
|
||
|
||
// 导出处理
|
||
toPerview(type) {
|
||
if (!this.beerInfo.name) {
|
||
this.showToast('请先选择酒款')
|
||
return
|
||
}
|
||
|
||
if (!this.canProceed) {
|
||
this.showToast(this.stepErrorMessage)
|
||
return
|
||
}
|
||
|
||
const specs = this.specs.map(spec => ({
|
||
amount: spec.price,
|
||
specs: spec.unit
|
||
}))
|
||
|
||
const params = {
|
||
type,
|
||
beerId: this.beerId,
|
||
no: this.tapNumber,
|
||
amount: specs[0].amount,
|
||
amount1: specs[1]?.amount || '',
|
||
specs: specs[0].specs,
|
||
specs1: specs[1]?.specs || ''
|
||
}
|
||
|
||
uni.navigateTo({
|
||
url: `/pagesActivity/bgSelect?${this.buildQueryString(params)}`
|
||
})
|
||
},
|
||
|
||
// 工具方法
|
||
showToast(title) {
|
||
uni.showToast({
|
||
title,
|
||
icon: 'none'
|
||
})
|
||
},
|
||
|
||
buildQueryString(params) {
|
||
return Object.entries(params)
|
||
.map(([key, value]) => `${key}=${encodeURIComponent(value)}`)
|
||
.join('&')
|
||
},
|
||
|
||
// 页面跳转
|
||
toSearch() {
|
||
uni.navigateTo({
|
||
url: '/pagesActivity/homeSearch'
|
||
})
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
// 通用样式
|
||
.input {
|
||
width: 100%;
|
||
height: 88rpx;
|
||
background: #F7F7F7;
|
||
border-radius: 12rpx;
|
||
padding: 0 24rpx;
|
||
font-size: 28rpx;
|
||
color: #0B0E26;
|
||
}
|
||
|
||
.btn {
|
||
width: 45%;
|
||
height: 88rpx;
|
||
border-radius: 44rpx;
|
||
font-size: 32rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
transition: all 0.3s ease;
|
||
|
||
&.prev {
|
||
background: #F7F7F7;
|
||
color: #606060;
|
||
border: 2rpx solid #E5E5E5;
|
||
|
||
.cuIcon-back {
|
||
font-size: 32rpx;
|
||
margin-right: 8rpx;
|
||
}
|
||
|
||
&:active {
|
||
background: #EEEEEE;
|
||
border-color: #D8D8D8;
|
||
transform: scale(0.98);
|
||
}
|
||
}
|
||
|
||
&.next {
|
||
background: #19367A;
|
||
color: #FFFFFF;
|
||
box-shadow: 0rpx 4rpx 12rpx rgba(25, 54, 122, 0.2);
|
||
|
||
&:active {
|
||
background: #152B5C;
|
||
transform: scale(0.98);
|
||
}
|
||
}
|
||
}
|
||
|
||
.winelist {
|
||
min-height: 100vh;
|
||
background: #F7F7F7;
|
||
padding: 32rpx;
|
||
|
||
.page-title {
|
||
font-size: 36rpx;
|
||
color: #0B0E26;
|
||
font-weight: 600;
|
||
margin-bottom: 32rpx;
|
||
text-align: center;
|
||
}
|
||
|
||
.beer-info {
|
||
margin-bottom: 48rpx;
|
||
|
||
.info-card {
|
||
background: #FFFFFF;
|
||
border-radius: 24rpx;
|
||
padding: 32rpx;
|
||
display: flex;
|
||
box-shadow: 0rpx 4rpx 12rpx rgba(0, 0, 0, 0.08);
|
||
height: 277rpx;
|
||
|
||
.cover {
|
||
width: 120rpx;
|
||
height: 213rpx;
|
||
border-radius: 16rpx;
|
||
margin-right: 24rpx;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.info {
|
||
flex: 1;
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: space-between;
|
||
|
||
.title {
|
||
font-size: 32rpx;
|
||
color: #0B0E26;
|
||
font-weight: 600;
|
||
margin-bottom: 12rpx;
|
||
}
|
||
|
||
.brewery {
|
||
font-size: 28rpx;
|
||
color: #606060;
|
||
margin-bottom: 8rpx;
|
||
}
|
||
|
||
.style {
|
||
font-size: 24rpx;
|
||
color: #979797;
|
||
}
|
||
|
||
.search-btn {
|
||
height: 72rpx;
|
||
background: #D42E78;
|
||
border-radius: 36rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
color: #FFFFFF;
|
||
font-size: 26rpx;
|
||
transition: all 0.3s ease;
|
||
width: 200rpx;
|
||
|
||
.cuIcon-search {
|
||
font-size: 28rpx;
|
||
margin-right: 8rpx;
|
||
}
|
||
|
||
&:active {
|
||
transform: scale(0.98);
|
||
background: #B81F5F;
|
||
}
|
||
}
|
||
}
|
||
|
||
&.empty-state {
|
||
.info {
|
||
justify-content: center;
|
||
align-items: center;
|
||
|
||
.title {
|
||
color: #606060;
|
||
text-align: center;
|
||
margin-bottom: 24rpx;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.steps {
|
||
background: #FFFFFF;
|
||
border-radius: 24rpx;
|
||
padding: 32rpx;
|
||
margin-bottom: 48rpx;
|
||
box-shadow: 0rpx 4rpx 12rpx rgba(0, 0, 0, 0.08);
|
||
|
||
.step-item {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
margin-bottom: 32rpx;
|
||
|
||
&:last-child {
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
&.active {
|
||
.step-number {
|
||
background: #19367A;
|
||
color: #FFFFFF;
|
||
}
|
||
|
||
.step-title {
|
||
color: #19367A;
|
||
}
|
||
}
|
||
|
||
.step-number {
|
||
width: 48rpx;
|
||
height: 48rpx;
|
||
border-radius: 50%;
|
||
background: #F5F5F5;
|
||
color: #979797;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 24rpx;
|
||
margin-right: 24rpx;
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.step-content {
|
||
flex: 1;
|
||
|
||
.step-title {
|
||
font-size: 28rpx;
|
||
color: #606060;
|
||
margin-bottom: 8rpx;
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.step-desc {
|
||
font-size: 24rpx;
|
||
color: #979797;
|
||
}
|
||
}
|
||
}
|
||
|
||
.step-line {
|
||
height: 32rpx;
|
||
width: 2rpx;
|
||
background: #F5F5F5;
|
||
margin-left: 24rpx;
|
||
margin-bottom: 32rpx;
|
||
|
||
&.active {
|
||
background: #19367A;
|
||
}
|
||
}
|
||
}
|
||
|
||
.step-content-area {
|
||
background: #FFFFFF;
|
||
border-radius: 24rpx;
|
||
padding: 32rpx;
|
||
box-shadow: 0rpx 4rpx 12rpx rgba(0, 0, 0, 0.08);
|
||
|
||
.input-group {
|
||
margin-bottom: 24rpx;
|
||
|
||
.label {
|
||
font-size: 28rpx;
|
||
color: #606060;
|
||
margin-bottom: 12rpx;
|
||
display: block;
|
||
}
|
||
}
|
||
|
||
.input-wrapper {
|
||
position: relative;
|
||
width: 100%;
|
||
|
||
.unit {
|
||
position: absolute;
|
||
right: 24rpx;
|
||
top: 50%;
|
||
transform: translateY(-50%);
|
||
font-size: 28rpx;
|
||
color: #979797;
|
||
}
|
||
}
|
||
|
||
.specs-list {
|
||
margin-bottom: 32rpx;
|
||
|
||
.spec-item {
|
||
background: #F7F7F7;
|
||
border-radius: 16rpx;
|
||
padding: 24rpx;
|
||
margin-bottom: 24rpx;
|
||
|
||
&:last-child {
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
.spec-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 24rpx;
|
||
|
||
.spec-name {
|
||
font-size: 28rpx;
|
||
color: #0B0E26;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.delete {
|
||
font-size: 24rpx;
|
||
color: #FF4D4F;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.add-spec {
|
||
height: 88rpx;
|
||
background: #F7F7F7;
|
||
border-radius: 12rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
margin-bottom: 32rpx;
|
||
|
||
.cuIcon-add {
|
||
font-size: 32rpx;
|
||
color: #19367A;
|
||
margin-right: 8rpx;
|
||
}
|
||
|
||
text {
|
||
font-size: 28rpx;
|
||
color: #19367A;
|
||
}
|
||
}
|
||
|
||
.export-options {
|
||
display: flex;
|
||
justify-content: space-around;
|
||
margin-bottom: 48rpx;
|
||
|
||
.option-item {
|
||
width: 240rpx;
|
||
height: 240rpx;
|
||
background: #F7F7F7;
|
||
border-radius: 24rpx;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
transition: all 0.3s ease;
|
||
|
||
&:active {
|
||
transform: scale(0.98);
|
||
}
|
||
|
||
.cuIcon-pic,
|
||
.cuIcon-file {
|
||
font-size: 64rpx;
|
||
color: #19367A;
|
||
margin-bottom: 16rpx;
|
||
}
|
||
|
||
text {
|
||
font-size: 28rpx;
|
||
color: #606060;
|
||
|
||
&.desc {
|
||
font-size: 24rpx;
|
||
color: #979797;
|
||
margin-top: 8rpx;
|
||
text-align: center;
|
||
padding: 0 16rpx;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.btn-group {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
margin-top: 48rpx;
|
||
}
|
||
}
|
||
}
|
||
</style> |