877 lines
24 KiB
Vue
877 lines
24 KiB
Vue
<template>
|
||
<div class="app-container">
|
||
<div class="content-wrapper">
|
||
<!-- 操作区域 -->
|
||
<div class="operation-header">
|
||
<h2>我的酒单</h2>
|
||
<div class="operation-buttons">
|
||
<el-button type="primary" :icon="Plus" @click="showAddBeerDialog">
|
||
新酒上枪
|
||
</el-button>
|
||
<el-button type="success" :icon="Download" @click="showExportMenuDialog">
|
||
整版酒单
|
||
</el-button>
|
||
<el-button type="info" :icon="Refresh" @click="refreshData">
|
||
刷新
|
||
</el-button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 在售酒款列表 -->
|
||
<div class="tap-list" v-loading="loading">
|
||
<div v-if="tapList.length === 0" class="empty-state">
|
||
<el-empty description="暂无在售酒款" />
|
||
</div>
|
||
|
||
<div v-else class="tap-items">
|
||
<div v-for="item in tapList" :key="item.id" class="tap-item">
|
||
<!-- 酒头编号 -->
|
||
<div class="tap-number">
|
||
<span class="tap-badge">{{ item.tapNo }}号</span>
|
||
</div>
|
||
|
||
<!-- 酒款信息 -->
|
||
<div class="beer-info">
|
||
<div class="beer-image">
|
||
<img
|
||
:src="item.beerCover || '/images/default-beer.png'"
|
||
:alt="item.beerName"
|
||
/>
|
||
</div>
|
||
<div class="beer-details">
|
||
<h3 class="beer-name">{{ item.beerName }}</h3>
|
||
<p class="beer-brand">{{ item.brandName }}</p>
|
||
<p class="beer-style">
|
||
{{ item.beerStyles || item.customStyle }}
|
||
</p>
|
||
<p class="beer-abv">ABV {{ item.beerAbv }}%</p>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 规格价格 -->
|
||
<div class="specs-info">
|
||
<div
|
||
v-for="spec in item.specList"
|
||
:key="spec.id"
|
||
class="spec-item"
|
||
>
|
||
<span class="spec-name">{{ spec.specName }}</span>
|
||
<span class="spec-price">¥{{ spec.specPrice }}</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 操作按钮 -->
|
||
<div class="actions">
|
||
<el-button size="small" type="primary" @click="editSpecs(item)" class="action-btn">
|
||
修改规格
|
||
</el-button>
|
||
<el-button size="small" type="success" @click="exportSingleBeer(item)" class="action-btn">
|
||
导出图片
|
||
</el-button>
|
||
<el-button size="small" type="warning" @click="offTap(item)" class="action-btn">
|
||
下架
|
||
</el-button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 新酒上枪对话框 -->
|
||
<el-dialog
|
||
v-model="addBeerDialog.visible"
|
||
title="新酒上枪"
|
||
width="1000px"
|
||
:close-on-click-modal="false"
|
||
:close-on-press-escape="false"
|
||
>
|
||
<BeerSelectionWizard
|
||
ref="beerSelectionWizardRef"
|
||
@submit="handleAddBeer"
|
||
@cancel="handleAddBeerCancel"
|
||
/>
|
||
</el-dialog>
|
||
|
||
<!-- 导出酒单对话框 -->
|
||
<el-dialog v-model="exportMenuDialog.visible" title="整版导出" width="900px">
|
||
<MenuExportWizard
|
||
ref="menuExportWizardRef"
|
||
:tap-list="tapList"
|
||
@submit="handleExportMenu"
|
||
@cancel="handleExportMenuCancel"
|
||
/>
|
||
</el-dialog>
|
||
|
||
<!-- 编辑规格价格对话框 -->
|
||
<el-dialog v-model="editSpecDialog.visible" title="编辑规格价格" width="900px">
|
||
<EditSpecForm
|
||
:tap-beer="editSpecDialog.data"
|
||
@submit="handleUpdateSpecs"
|
||
@cancel="editSpecDialog.visible = false"
|
||
/>
|
||
</el-dialog>
|
||
|
||
<!-- 单酒款导出对话框 -->
|
||
<el-dialog v-model="singleExportDialog.visible" title="导出单酒款图片" width="900px">
|
||
<SingleBeerExportWizard
|
||
ref="singleExportWizardRef"
|
||
:beer-data="singleExportDialog.beerData"
|
||
@submit="handleSingleBeerExport"
|
||
@cancel="singleExportDialog.visible = false"
|
||
/>
|
||
</el-dialog>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref, reactive, onMounted } from 'vue'
|
||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||
import { Plus, Download, Refresh } from '@element-plus/icons-vue'
|
||
import {
|
||
getTapList,
|
||
addBeerToTap,
|
||
offTapBeer,
|
||
updateTapBeerSpecs
|
||
} from '@/api/barmgr/tap'
|
||
import { formatTime } from '@/utils/date-util'
|
||
import { generateWineMenu, generateSingleBeerPoster } from '@/api/template'
|
||
import BeerSelectionWizard from './components/BeerSelectionWizard.vue'
|
||
import EditSpecForm from './components/EditSpecForm.vue'
|
||
import MenuExportWizard from './components/MenuExportWizard.vue'
|
||
import SingleBeerExportWizard from './components/SingleBeerExportWizard.vue'
|
||
|
||
// 响应式数据
|
||
const loading = ref(false)
|
||
const tapList = ref([])
|
||
const beerSelectionWizardRef = ref()
|
||
const menuExportWizardRef = ref()
|
||
const singleExportWizardRef = ref()
|
||
|
||
const addBeerDialog = reactive({
|
||
visible: false
|
||
})
|
||
|
||
const exportMenuDialog = reactive({
|
||
visible: false
|
||
})
|
||
|
||
const editSpecDialog = reactive({
|
||
visible: false,
|
||
data: null
|
||
})
|
||
|
||
const singleExportDialog = reactive({
|
||
visible: false,
|
||
beerData: null
|
||
})
|
||
|
||
// 获取在售酒款列表
|
||
const fetchTapList = async () => {
|
||
try {
|
||
loading.value = true
|
||
const response = await getTapList({ pageNum: 1, pageSize: 1000 })
|
||
// axios 返回的数据在 response.data 中
|
||
const responseData = response.data
|
||
|
||
if (responseData && responseData.code === 200) {
|
||
// TableDataInfo 结构:{code: 200, msg: "查询成功", rows: [...], total: 1}
|
||
tapList.value = responseData.rows || []
|
||
} else {
|
||
ElMessage.error('获取酒款列表失败: ' + (responseData?.msg || '未知错误'))
|
||
}
|
||
} catch (error) {
|
||
ElMessage.error('获取酒款列表失败: ' + error.message)
|
||
} finally {
|
||
loading.value = false
|
||
}
|
||
}
|
||
|
||
// 刷新数据
|
||
const refreshData = async () => {
|
||
await fetchTapList()
|
||
ElMessage.success('数据刷新成功')
|
||
}
|
||
|
||
// 显示新酒上枪对话框
|
||
const showAddBeerDialog = () => {
|
||
addBeerDialog.visible = true
|
||
}
|
||
|
||
// 处理新酒上枪取消
|
||
const handleAddBeerCancel = () => {
|
||
addBeerDialog.visible = false
|
||
// 重置表单(下次打开时状态会重置)
|
||
if (beerSelectionWizardRef.value) {
|
||
beerSelectionWizardRef.value.resetForm()
|
||
}
|
||
}
|
||
|
||
// 显示导出酒单对话框
|
||
const showExportMenuDialog = () => {
|
||
exportMenuDialog.visible = true
|
||
}
|
||
|
||
// 处理导出酒单取消
|
||
const handleExportMenuCancel = () => {
|
||
exportMenuDialog.visible = false
|
||
// 重置向导状态(下次打开时状态会重置)
|
||
if (menuExportWizardRef.value) {
|
||
menuExportWizardRef.value.resetWizard()
|
||
}
|
||
}
|
||
|
||
// 处理新酒上枪
|
||
const handleAddBeer = async (data) => {
|
||
try {
|
||
const response = await addBeerToTap(data)
|
||
|
||
// 检查不同的响应数据结构
|
||
if (response.data && response.data.code === 200) {
|
||
ElMessage.success('上枪成功')
|
||
addBeerDialog.visible = false
|
||
// 重置表单
|
||
if (beerSelectionWizardRef.value) {
|
||
beerSelectionWizardRef.value.resetForm()
|
||
}
|
||
await refreshData()
|
||
} else if (response.code === 200) {
|
||
ElMessage.success('上枪成功')
|
||
addBeerDialog.visible = false
|
||
// 重置表单
|
||
if (beerSelectionWizardRef.value) {
|
||
beerSelectionWizardRef.value.resetForm()
|
||
}
|
||
await refreshData()
|
||
} else {
|
||
const errorMsg = (response.data && response.data.msg) || response.msg || '上枪失败'
|
||
throw new Error(errorMsg)
|
||
}
|
||
} catch (error) {
|
||
ElMessage.error('上枪失败: ' + error.message)
|
||
}
|
||
}
|
||
|
||
// 处理导出酒单
|
||
const handleExportMenu = async (exportData) => {
|
||
try {
|
||
// 构建请求参数,按照整版酒单编辑器的数据结构要求
|
||
const params = {
|
||
uuid: exportData.template.uuid || exportData.template.posterId || String(exportData.template.id), // 优先使用uuid字段
|
||
format: exportData.format || 'PDF'
|
||
}
|
||
|
||
// 按照 beer{index}_{field} 格式传递酒款数据
|
||
exportData.generateParams.beerList.forEach((beer, index) => {
|
||
// 基础酒款信息
|
||
params[`beer${index}_name`] = beer.beerName || ''
|
||
params[`beer${index}_name_en`] = beer.beerNameEn || ''
|
||
// 风格优先级:custom_style > beer_style
|
||
params[`beer${index}_style`] = beer.customStyle || beer.beerStyles || ''
|
||
params[`beer${index}_custom_style`] = beer.customStyle || ''
|
||
params[`beer${index}_beer_styles_en`] = beer.beerStylesEn || ''
|
||
params[`beer${index}_abv`] = beer.beerAbv ? `ABV: ${beer.beerAbv}%` : ''
|
||
params[`beer${index}_ibu`] = beer.beerIbus ? `IBU: ${beer.beerIbus}` : ''
|
||
params[`beer${index}_og`] = beer.beerOg ? `OG: ${beer.beerOg}` : ''
|
||
params[`beer${index}_intro`] = beer.beerDesc || ''
|
||
params[`beer${index}_juice_content`] = beer.juiceContent || ''
|
||
params[`beer${index}_score`] = beer.beerScore || ''
|
||
params[`beer${index}_img`] = beer.cover || beer.beerCover || ''
|
||
// 鱼眼标:处理逗号分隔的图片URL,取第一张
|
||
let fisheyeLogo = beer.fisheyeLogo || ''
|
||
if (fisheyeLogo) {
|
||
// 按逗号分隔,取第一张图片
|
||
const logoArray = fisheyeLogo.split(',').map(url => url.trim()).filter(url => url)
|
||
fisheyeLogo = logoArray.length > 0 ? logoArray[0] : fisheyeLogo
|
||
}
|
||
|
||
// 如果鱼眼标为空,使用酒款图片并标记为圆形处理
|
||
if (!fisheyeLogo && (beer.cover || beer.beerCover)) {
|
||
fisheyeLogo = beer.cover || beer.beerCover
|
||
// 添加圆形处理标识
|
||
params[`beer${index}_fisheye_logo_crop`] = 'circle'
|
||
}
|
||
|
||
params[`beer${index}_fisheye_logo`] = fisheyeLogo
|
||
|
||
// 厂牌信息
|
||
params[`beer${index}_brewery_name`] = beer.breweryName || beer.brandName || ''
|
||
params[`beer${index}_brewery_name_en`] = beer.breweryNameEn || ''
|
||
params[`beer${index}_brewery_logo`] = beer.brandLogo || beer.breweryLogo || ''
|
||
params[`beer${index}_country`] = beer.country || ''
|
||
params[`beer${index}_city`] = beer.city || ''
|
||
|
||
// 酒头信息
|
||
params[`beer${index}_tapno`] = beer.tapNo ? String(beer.tapNo) : ''
|
||
|
||
// 规格价格信息
|
||
params[`beer${index}_price`] = beer.specs && beer.specs[0] ? String(beer.specs[0].specPrice) : ''
|
||
params[`beer${index}_spec`] = beer.specs && beer.specs[0] ? beer.specs[0].specName : ''
|
||
|
||
// 如果有多个规格,添加第二个规格
|
||
if (beer.specs && beer.specs[1]) {
|
||
params[`beer${index}_spec1`] = beer.specs[1].specName
|
||
params[`beer${index}_price1`] = String(beer.specs[1].specPrice)
|
||
}
|
||
})
|
||
|
||
ElMessage({
|
||
message: '正在生成酒单,请稍候...',
|
||
type: 'info',
|
||
duration: 0
|
||
})
|
||
|
||
// 调用现有的整版酒单生成接口
|
||
const response = await generateWineMenu(params)
|
||
|
||
if (response.data && response.data.code === 200 && response.data.data && response.data.data.image) {
|
||
const result = response.data
|
||
// 现有接口返回base64图片,需要转换为可下载的文件
|
||
const base64Image = result.data.image
|
||
|
||
// 将base64转换为blob
|
||
const byteCharacters = atob(base64Image)
|
||
const byteNumbers = new Array(byteCharacters.length)
|
||
for (let i = 0; i < byteCharacters.length; i++) {
|
||
byteNumbers[i] = byteCharacters.charCodeAt(i)
|
||
}
|
||
const byteArray = new Uint8Array(byteNumbers)
|
||
|
||
// 根据格式设置正确的MIME类型和文件扩展名
|
||
let mimeType = 'image/png'
|
||
let fileExtension = 'png'
|
||
|
||
if (result.data.format === 'base64_pdf') {
|
||
mimeType = 'application/pdf'
|
||
fileExtension = 'pdf'
|
||
}
|
||
|
||
const blob = new Blob([byteArray], { type: mimeType })
|
||
|
||
// 创建下载链接
|
||
const url = window.URL.createObjectURL(blob)
|
||
const a = document.createElement('a')
|
||
a.href = url
|
||
a.download = `酒单_${new Date().getTime()}.${fileExtension}`
|
||
document.body.appendChild(a)
|
||
a.click()
|
||
document.body.removeChild(a)
|
||
window.URL.revokeObjectURL(url)
|
||
|
||
ElMessage.closeAll() // 关闭loading消息
|
||
if (result.data.format === 'base64_pdf') {
|
||
ElMessage.success('PDF酒单导出成功')
|
||
} else {
|
||
ElMessage.success('PNG酒单导出成功')
|
||
}
|
||
exportMenuDialog.visible = false
|
||
// 重置向导状态
|
||
if (menuExportWizardRef.value) {
|
||
menuExportWizardRef.value.resetWizard()
|
||
}
|
||
} else {
|
||
// axios响应错误处理
|
||
const errorMsg = response.data?.msg || response.msg || '生成失败'
|
||
throw new Error(errorMsg)
|
||
}
|
||
} catch (error) {
|
||
ElMessage.closeAll() // 关闭loading消息
|
||
ElMessage.error('导出酒单失败: ' + error.message)
|
||
}
|
||
}
|
||
|
||
// 下架酒款
|
||
const offTap = async (row) => {
|
||
try {
|
||
await ElMessageBox.confirm(
|
||
`确定要下架酒头 ${row.tapNo} 上的"${row.beerName}"吗?`,
|
||
'下架确认',
|
||
{
|
||
confirmButtonText: '确定',
|
||
cancelButtonText: '取消',
|
||
type: 'warning'
|
||
}
|
||
)
|
||
|
||
const response = await offTapBeer(row.tapId)
|
||
// axios 返回的数据在 response.data 中
|
||
const responseData = response.data || response
|
||
|
||
if (responseData && responseData.code === 200) {
|
||
ElMessage.success('下架成功')
|
||
await refreshData()
|
||
} else {
|
||
throw new Error(responseData?.msg || '下架失败')
|
||
}
|
||
} catch (error) {
|
||
if (error !== 'cancel') {
|
||
ElMessage.error('下架失败: ' + (error.message || error))
|
||
}
|
||
}
|
||
}
|
||
|
||
// 编辑规格价格
|
||
const editSpecs = (row) => {
|
||
// 安全地复制数据,避免Vue内部属性
|
||
editSpecDialog.data = {
|
||
id: row.id,
|
||
tapBeerId: row.id, // 确保有tapBeerId
|
||
tapId: row.tapId,
|
||
tapNo: row.tapNo,
|
||
beerId: row.beerId,
|
||
beerName: row.beerName,
|
||
beerCover: row.beerCover,
|
||
brandName: row.brandName,
|
||
beerStyles: row.beerStyles,
|
||
customStyle: row.customStyle,
|
||
specList: row.specList || []
|
||
}
|
||
editSpecDialog.visible = true
|
||
}
|
||
|
||
// 导出单酒款图片
|
||
const exportSingleBeer = (row) => {
|
||
// 安全地复制数据,避免Vue内部属性
|
||
singleExportDialog.beerData = {
|
||
id: row.id,
|
||
tapId: row.tapId,
|
||
tapNo: row.tapNo,
|
||
beerId: row.beerId,
|
||
beerName: row.beerName,
|
||
beerNameEn: row.beerNameEn,
|
||
beerCover: row.beerCover,
|
||
cover: row.cover,
|
||
brandName: row.brandName,
|
||
brandLogo: row.brandLogo,
|
||
beerStyles: row.beerStyles,
|
||
customStyle: row.customStyle,
|
||
beerStylesEn: row.beerStylesEn,
|
||
beerAbv: row.beerAbv,
|
||
beerIbus: row.beerIbus,
|
||
beerOg: row.beerOg,
|
||
beerDesc: row.beerDesc,
|
||
juiceContent: row.juiceContent,
|
||
fisheyeLogo: row.fisheyeLogo,
|
||
breweryName: row.breweryName,
|
||
breweryNameEn: row.breweryNameEn,
|
||
country: row.country,
|
||
city: row.city,
|
||
specList: row.specList || []
|
||
}
|
||
singleExportDialog.visible = true
|
||
}
|
||
|
||
// 处理单酒款导出
|
||
const handleSingleBeerExport = async (exportData) => {
|
||
try {
|
||
// //console.log('导出单酒款数据:', exportData)
|
||
|
||
// 构建请求参数,用于单酒款海报
|
||
const params = {
|
||
uuid: exportData.template.uuid || String(exportData.template.id),
|
||
format: exportData.format || 'PNG'
|
||
}
|
||
|
||
// 传递完整的酒款数据,确保兼容所有模板组件
|
||
const beerData = exportData.beerData
|
||
|
||
// 酒款信息组件
|
||
params[`beer_name`] = beerData.beerName || ''
|
||
params[`beer_name_en`] = beerData.beerNameEn || beerData.beerEnglishName || ''
|
||
params[`beer_image`] = beerData.beerCover || beerData.cover || ''
|
||
// 风格优先级:custom_style > beer_style
|
||
params[`beer_style`] = beerData.customStyle || beerData.beerStyles || ''
|
||
params[`custom_style`] = beerData.customStyle || ''
|
||
params[`beer_styles_en`] = beerData.beerStylesEn || ''
|
||
params[`abv`] = beerData.beerAbv ? `ABV: ${beerData.beerAbv}%` : ''
|
||
params[`ibu`] = beerData.beerIbus ? `IBU: ${beerData.beerIbus}` : ''
|
||
params[`og`] = beerData.beerOg ? `OG: ${beerData.beerOg}` : ''
|
||
params[`intro`] = beerData.beerDesc || beerData.beerIntro || beerData.beerDescription || ''
|
||
params[`juice_content`] = beerData.juiceContent || ''
|
||
params[`score`] = beerData.beerScore || ''
|
||
// 鱼眼标:处理逗号分隔的图片URL,取第一张
|
||
let fisheyeLogo = beerData.fisheyeLogo || ''
|
||
if (fisheyeLogo) {
|
||
// 按逗号分隔,取第一张图片
|
||
const logoArray = fisheyeLogo.split(',').map(url => url.trim()).filter(url => url)
|
||
fisheyeLogo = logoArray.length > 0 ? logoArray[0] : fisheyeLogo
|
||
}
|
||
|
||
// 如果鱼眼标为空,使用酒款图片并标记为圆形处理
|
||
if (!fisheyeLogo && (beerData.beerCover || beerData.cover)) {
|
||
fisheyeLogo = beerData.beerCover || beerData.cover
|
||
// 添加圆形处理标识
|
||
params[`fisheye_logo_crop`] = 'circle'
|
||
}
|
||
|
||
params[`fisheye_logo`] = fisheyeLogo
|
||
|
||
// 厂牌信息组件
|
||
params[`brewery_name`] = beerData.breweryName || beerData.brandName || ''
|
||
params[`brewery_name_en`] = beerData.breweryNameEn || ''
|
||
params[`country`] = beerData.country || ''
|
||
params[`city`] = beerData.city || ''
|
||
params[`brewery_logo`] = beerData.breweryLogo || beerData.brandLogo || ''
|
||
|
||
// 市售规格组件
|
||
params[`tapno`] = beerData.tapNo || ''
|
||
if (beerData.specList && beerData.specList.length > 0) {
|
||
params[`spec1_name`] = beerData.specList[0].specName || ''
|
||
params[`spec1_price`] = beerData.specList[0].specPrice ? `¥${beerData.specList[0].specPrice}` : ''
|
||
|
||
if (beerData.specList.length > 1) {
|
||
params[`spec2_name`] = beerData.specList[1].specName || ''
|
||
params[`spec2_price`] = beerData.specList[1].specPrice ? `¥${beerData.specList[1].specPrice}` : ''
|
||
}
|
||
}
|
||
|
||
// 其他常用组件(预留)
|
||
params[`qtcy_text`] = ''
|
||
params[`qtcy_image`] = ''
|
||
params[`qtcy_avatar`] = ''
|
||
params[`qtcy_qrcode`] = ''
|
||
|
||
//console.log('单酒款图片生成参数:', params)
|
||
|
||
// 调用生成图片接口
|
||
const response = await generateSingleBeerPoster(params)
|
||
|
||
if (response.data?.code === 200) {
|
||
const { url } = response.data.data
|
||
|
||
if (url) {
|
||
// 生成下载文件名
|
||
const fileExtension = params.format.toLowerCase()
|
||
const fileName = `${beerData.beerName || '酒款'}_${beerData.tapNo}号酒头.${fileExtension}`
|
||
|
||
// 下载文件
|
||
try {
|
||
const downloadResponse = await fetch(url)
|
||
const blob = await downloadResponse.blob()
|
||
|
||
// 触发下载
|
||
const link = document.createElement('a')
|
||
link.href = URL.createObjectURL(blob)
|
||
link.download = fileName
|
||
document.body.appendChild(link)
|
||
link.click()
|
||
document.body.removeChild(link)
|
||
URL.revokeObjectURL(link.href)
|
||
|
||
ElMessage.success('单酒款图片导出成功')
|
||
singleExportDialog.visible = false
|
||
// 重置弹窗状态
|
||
if (singleExportWizardRef.value) {
|
||
singleExportWizardRef.value.resetState()
|
||
}
|
||
} catch (downloadError) {
|
||
// 如果下载失败,直接打开链接
|
||
window.open(url, '_blank')
|
||
ElMessage.success('图片已在新窗口打开,请右键保存')
|
||
singleExportDialog.visible = false
|
||
// 重置弹窗状态
|
||
if (singleExportWizardRef.value) {
|
||
singleExportWizardRef.value.resetState()
|
||
}
|
||
}
|
||
} else {
|
||
throw new Error('生成的图片URL为空')
|
||
}
|
||
} else {
|
||
throw new Error(response.data?.msg || response.msg || '生成图片失败')
|
||
}
|
||
} catch (error) {
|
||
console.error('导出单酒款图片失败:', error)
|
||
ElMessage.error('导出失败: ' + error.message)
|
||
}
|
||
}
|
||
|
||
// 处理更新规格价格
|
||
const handleUpdateSpecs = async (data) => {
|
||
try {
|
||
//console.log('Update specs data:', data)
|
||
const response = await updateTapBeerSpecs(data)
|
||
//console.log('Update specs response:', response)
|
||
|
||
// axios 返回的数据在 response.data 中
|
||
const responseData = response.data || response
|
||
|
||
if (responseData && responseData.code === 200) {
|
||
ElMessage.success('价格更新成功')
|
||
editSpecDialog.visible = false
|
||
await refreshData()
|
||
} else {
|
||
throw new Error(responseData?.msg || '价格更新失败')
|
||
}
|
||
} catch (error) {
|
||
console.error('Update specs error:', error)
|
||
ElMessage.error('价格更新失败: ' + error.message)
|
||
}
|
||
}
|
||
|
||
// 组件挂载时获取数据
|
||
onMounted(() => {
|
||
refreshData()
|
||
})
|
||
</script>
|
||
|
||
<style scoped>
|
||
.app-container {
|
||
min-height: 100vh;
|
||
height: auto; /* 允许高度自动扩展 */
|
||
background: #f5f7fa;
|
||
padding: 20px 0 80px 0; /* 增加底部padding,为版权信息留出空间 */
|
||
position: relative; /* 确保背景能够包含所有内容 */
|
||
}
|
||
|
||
.content-wrapper {
|
||
max-width: 1200px;
|
||
margin: 0 auto 20px auto; /* 增加底部margin,与背景分离 */
|
||
background: white;
|
||
border-radius: 12px;
|
||
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
|
||
overflow: hidden;
|
||
position: relative; /* 确保相对定位 */
|
||
z-index: 1; /* 确保内容在背景之上 */
|
||
}
|
||
|
||
.operation-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding: 24px 32px;
|
||
border-bottom: 1px solid #f0f0f0;
|
||
background: #fafafa;
|
||
}
|
||
|
||
.operation-header h2 {
|
||
margin: 0;
|
||
color: #303133;
|
||
font-size: 20px;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.operation-buttons {
|
||
display: flex;
|
||
gap: 12px;
|
||
}
|
||
|
||
.tap-list {
|
||
padding: 0;
|
||
min-height: 200px; /* 确保列表区域有最小高度 */
|
||
position: relative;
|
||
}
|
||
|
||
.empty-state {
|
||
padding: 60px 32px;
|
||
text-align: center;
|
||
}
|
||
|
||
.tap-items {
|
||
padding: 0;
|
||
}
|
||
|
||
.tap-item {
|
||
display: flex;
|
||
align-items: center;
|
||
padding: 24px 32px;
|
||
border-bottom: 1px solid #f0f0f0;
|
||
transition: background-color 0.2s;
|
||
}
|
||
|
||
.tap-item:hover {
|
||
background-color: #fafafa;
|
||
}
|
||
|
||
.tap-item:last-child {
|
||
border-bottom: none;
|
||
}
|
||
|
||
.tap-number {
|
||
margin-right: 24px;
|
||
}
|
||
|
||
.tap-badge {
|
||
display: inline-block;
|
||
width: 48px;
|
||
height: 48px;
|
||
line-height: 48px;
|
||
text-align: center;
|
||
background: #409eff;
|
||
color: white;
|
||
border-radius: 50%;
|
||
font-weight: bold;
|
||
font-size: 14px;
|
||
}
|
||
|
||
.beer-info {
|
||
display: flex;
|
||
align-items: center;
|
||
flex: 1;
|
||
margin-right: 24px;
|
||
}
|
||
|
||
.beer-image {
|
||
margin-right: 16px;
|
||
}
|
||
|
||
.beer-image img {
|
||
width: 60px;
|
||
height: 90px;
|
||
border-radius: 8px;
|
||
object-fit: cover;
|
||
border: 2px solid #f0f0f0;
|
||
}
|
||
|
||
.beer-details {
|
||
flex: 1;
|
||
}
|
||
|
||
.beer-name {
|
||
margin: 0 0 4px 0;
|
||
font-size: 16px;
|
||
font-weight: 600;
|
||
color: #303133;
|
||
line-height: 1.4;
|
||
}
|
||
|
||
.beer-brand {
|
||
margin: 0 0 2px 0;
|
||
font-size: 14px;
|
||
color: #606266;
|
||
line-height: 1.4;
|
||
}
|
||
|
||
.beer-style {
|
||
margin: 0 0 2px 0;
|
||
font-size: 12px;
|
||
color: #67c23a;
|
||
line-height: 1.4;
|
||
}
|
||
|
||
.beer-abv {
|
||
margin: 0;
|
||
font-size: 12px;
|
||
color: #909399;
|
||
line-height: 1.4;
|
||
}
|
||
|
||
.specs-info {
|
||
margin-right: 24px;
|
||
min-width: 120px;
|
||
}
|
||
|
||
.spec-item {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 8px;
|
||
padding: 8px 12px;
|
||
background: #f8f9fa;
|
||
border-radius: 6px;
|
||
font-size: 14px;
|
||
width: 200px;
|
||
margin-right: 100px;
|
||
}
|
||
|
||
.spec-item:last-child {
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
.spec-name {
|
||
font-size: 14px;
|
||
color: #606266;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.spec-price {
|
||
font-size: 16px;
|
||
font-weight: 600;
|
||
color: #e6a23c;
|
||
}
|
||
|
||
.actions {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 8px;
|
||
min-width: 80px;
|
||
}
|
||
|
||
.actions .el-button {
|
||
width: 100%;
|
||
height: 32px;
|
||
font-size: 12px;
|
||
}
|
||
|
||
.action-btn {
|
||
width: 100% !important;
|
||
height: 32px !important;
|
||
font-size: 12px !important;
|
||
margin: 0 !important;
|
||
}
|
||
|
||
/* 响应式设计 */
|
||
@media (max-width: 768px) {
|
||
.content-wrapper {
|
||
margin: 0 16px;
|
||
border-radius: 8px;
|
||
}
|
||
|
||
.operation-header {
|
||
padding: 16px 20px;
|
||
flex-direction: column;
|
||
gap: 16px;
|
||
align-items: stretch;
|
||
}
|
||
|
||
.operation-buttons {
|
||
justify-content: center;
|
||
}
|
||
|
||
.tap-item {
|
||
padding: 16px 20px;
|
||
flex-direction: column;
|
||
align-items: stretch;
|
||
gap: 16px;
|
||
}
|
||
|
||
.tap-number {
|
||
margin-right: 0;
|
||
text-align: center;
|
||
}
|
||
|
||
.beer-info {
|
||
margin-right: 0;
|
||
justify-content: center;
|
||
}
|
||
|
||
.specs-info {
|
||
margin-right: 0;
|
||
min-width: auto;
|
||
}
|
||
|
||
.actions {
|
||
flex-direction: row;
|
||
justify-content: center;
|
||
}
|
||
}
|
||
|
||
/* 确保页面整体布局正确 */
|
||
:deep(.ele-pro-body) {
|
||
min-height: auto !important;
|
||
height: auto !important;
|
||
}
|
||
|
||
/* 确保主内容区域能够正确扩展 */
|
||
:deep(.ele-pro-layout-body) {
|
||
min-height: auto !important;
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
|
||
/* 确保路由视图容器能够正确扩展 */
|
||
:deep(.router-view-wrapper) {
|
||
flex: 1;
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
</style> |