文件

759 行
24 KiB
HTML

此文件含有模棱两可的 Unicode 字符

此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。

<!--[1.8.50] 菜单设置 - 区域选择版本-->
<!-- Vue.js -->
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.min.js"></script>
<!-- Element UI -->
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
<script src="https://unpkg.com/element-ui/lib/index.js"></script>
<!-- 腾讯地图API -->
<script charset="utf-8" type="text/javascript" src="https://map.qq.com/api/gljs?v=1.exp&key=BHHBZ-CVK3Q-PKW5B-BJBDV-PZILV-IIBJA&libraries=service,geometry"></script>
<script>
// 全局地图加载状态
window.mapLoadStatus = {
loaded: false,
error: false
};
// 监听地图API加载
window.addEventListener('load', function() {
setTimeout(function() {
if (typeof TMap !== 'undefined') {
window.mapLoadStatus.loaded = true;
console.log('腾讯地图API加载成功');
} else {
window.mapLoadStatus.error = true;
console.error('腾讯地图API加载失败');
}
}, 1000);
});
</script>
<style type="text/css">
.tips {
font-size: 13px;
color: #999;
margin-left: 20px;
}
.el-switch__label span[aria-hidden='true'] {
color: #999;
font-weight: normal;
}
.small-input {
width: 190px;
}
.mini-input {
width: 150px;
}
.inline-block {
display: inline-block;
margin-right: 50px;
}
.time-block {
margin-top: 8px;
}
.time-block:nth-child(1) {
margin-top: 0;
}
#menuManager .el-date-editor {
width: 130px;
}
#menuManager .el-form-item--small.el-form-item {
margin-bottom: 30px;
}
#menuManager .el-input-group__append {
padding: 0 7px;
}
/* 区域选择样式 */
.area-selection-container {
border: 1px solid #e4e7ed;
border-radius: 8px;
overflow: hidden;
background: #fff;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}
.area-selection-header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 15px 20px;
display: flex;
justify-content: space-between;
align-items: center;
}
.area-selection-title {
font-size: 16px;
font-weight: 600;
margin: 0;
}
.area-selection-status {
font-size: 12px;
opacity: 0.9;
}
.area-selection-wrapper {
position: relative;
background: #f8f9fa;
padding: 20px;
}
.area-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 15px;
margin-bottom: 20px;
}
.area-card {
background: white;
border: 2px solid #e4e7ed;
border-radius: 12px;
padding: 20px 15px;
cursor: pointer;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
text-align: center;
position: relative;
overflow: hidden;
}
.area-card::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(64, 158, 255, 0.1), transparent);
transition: left 0.5s;
}
.area-card:hover {
border-color: #409eff;
box-shadow: 0 8px 25px rgba(64, 158, 255, 0.15);
transform: translateY(-4px) scale(1.02);
}
.area-card:hover::before {
left: 100%;
}
.area-card.selected {
border-color: #409eff;
background: linear-gradient(135deg, #f0f9ff 0%, #e6f7ff 100%);
box-shadow: 0 8px 25px rgba(64, 158, 255, 0.25);
transform: translateY(-2px);
}
.area-card.selected::after {
content: '✓';
position: absolute;
top: 8px;
right: 8px;
width: 20px;
height: 20px;
background: #409eff;
color: white;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 12px;
font-weight: bold;
}
.area-card-icon {
font-size: 32px;
color: #409eff;
margin-bottom: 10px;
}
.area-card.selected .area-card-icon {
color: #409eff;
}
.area-card-name {
font-size: 14px;
font-weight: 600;
color: #303133;
margin-bottom: 5px;
}
.area-card-desc {
font-size: 12px;
color: #909399;
line-height: 1.4;
}
.selected-areas {
background: white;
border: 1px solid #e4e7ed;
border-radius: 8px;
padding: 15px;
margin-top: 20px;
}
.selected-areas-title {
font-size: 14px;
font-weight: 600;
color: #303133;
margin-bottom: 10px;
display: flex;
align-items: center;
}
.selected-areas-list {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.selected-area-tag {
background: #f0f9ff;
border: 1px solid #409eff;
color: #409eff;
padding: 4px 12px;
border-radius: 16px;
font-size: 12px;
display: flex;
align-items: center;
gap: 5px;
}
.selected-area-tag .remove-btn {
cursor: pointer;
font-weight: bold;
opacity: 0.7;
}
.selected-area-tag .remove-btn:hover {
opacity: 1;
}
/* 菜单分类样式 */
.menu-category-container {
border: 1px solid #e4e7ed;
border-radius: 8px;
background: #fff;
margin-bottom: 20px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.menu-category-header {
background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
padding: 15px 20px;
border-bottom: 1px solid #e4e7ed;
display: flex;
justify-content: space-between;
align-items: center;
border-radius: 8px 8px 0 0;
}
.menu-category-title {
font-size: 16px;
font-weight: 600;
color: #303133;
margin: 0;
display: flex;
align-items: center;
gap: 8px;
}
.menu-category-content {
padding: 20px;
}
.menu-item-list {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 15px;
}
.menu-item-card {
background: #f8f9fa;
border: 1px solid #e4e7ed;
border-radius: 8px;
padding: 15px;
transition: all 0.3s ease;
}
.menu-item-card:hover {
border-color: #409eff;
box-shadow: 0 2px 8px rgba(64, 158, 255, 0.15);
}
.menu-item-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
}
.menu-item-name {
font-size: 14px;
font-weight: 600;
color: #303133;
}
.menu-item-price {
font-size: 14px;
color: #f56c6c;
font-weight: 600;
}
.menu-item-desc {
font-size: 12px;
color: #606266;
line-height: 1.4;
margin-bottom: 10px;
}
.menu-item-actions {
display: flex;
gap: 8px;
}
/* 响应式设计 */
@media (max-width: 768px) {
.area-grid {
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
}
.menu-item-list {
grid-template-columns: 1fr;
}
}
</style>
<div id="menuManager" style="padding-bottom: 60px">
<el-collapse accordion>
<el-collapse-item>
<template slot="title">
<el-button type="text" size="mini">菜单设置须知>>></el-button>
</template>
<div>1. 菜单设置可以按区域进行个性化配置</div>
<div>2. 不同区域可以设置不同的菜单内容和价格</div>
<div>3. 区域选择功能可以精确定位目标客户群体</div>
</el-collapse-item>
</el-collapse>
<el-form label-width="150px" size="mini" style="width: 762px" :model="ruleForm" :rules="rules" ref="ruleForm">
<el-form-item label="菜单状态" prop="status">
<el-switch v-model="ruleForm.status"
:active-text="ruleForm.status==0?'关闭':'开启'"
inactive-value="0"
active-value="1">
</el-switch>
<span class="tips">关闭则不在前台展示菜单</span>
</el-form-item>
<el-form-item label="菜单名称" prop="menu_name">
<el-input style="width: 300px" v-model="ruleForm.menu_name" placeholder="请输入菜单名称"></el-input>
<span class="tips">菜单的显示名称</span>
</el-form-item>
<el-form-item label="菜单描述" prop="menu_desc">
<el-input type="textarea" :rows="3" style="width: 500px" v-model="ruleForm.menu_desc" placeholder="请输入菜单描述"></el-input>
<span class="tips">菜单的详细描述信息</span>
</el-form-item>
<el-form-item label="显示顺序" prop="sort_order">
<el-input-number class="small-input"
:min="0"
:max="999"
v-model="ruleForm.sort_order"></el-input-number>
<span class="tips">数字越小越靠前显示</span>
</el-form-item>
<el-form-item label="营业时间" prop="business_time">
<template v-for="(item,index) in ruleForm.business_time">
<div class="time-block">
<el-time-select
placeholder="开始时间"
v-model="item[0]"
value-format="HH:mm"
:picker-options="getBusinessTime(index,1)">
</el-time-select>
<el-time-select
placeholder="结束时间"
v-model="item[1]"
value-format="HH:mm"
:picker-options="getBusinessTime(index,2)">
</el-time-select>
<template v-if="index==0">
<el-button :disabled="ruleForm.business_time.length>=3?true:false"
size="mini"
@click="addBusinessTime()"
circle>
<i class="el-icon-plus"></i>
</el-button>
<span class="tips">菜单供应时间,最多可以设置3个时间段</span>
</template>
<el-button v-else
@click="spliceBusinessTime(index)"
size="mini"
circle>
<i class="el-icon-minus"></i>
</el-button>
</div>
</template>
</el-form-item>
<!-- 区域选择设置 -->
<el-form-item label="适用区域" prop="delivery_areas">
<div class="area-selection-container">
<!-- 区域选择头部 -->
<div class="area-selection-header">
<div>
<h3 class="area-selection-title">
<i class="el-icon-location"></i>
菜单适用区域设置
</h3>
<div class="area-selection-status">
<span v-if="selectedAreas.length === 0">请选择菜单适用区域</span>
<span v-else>已选择 {{selectedAreas.length}} 个区域</span>
</div>
</div>
</div>
<!-- 区域选择容器 -->
<div class="area-selection-wrapper">
<div class="area-grid">
<div class="area-card"
v-for="area in availableAreas"
:key="area.id"
:class="{selected: isAreaSelected(area.id)}"
@click="toggleArea(area.id)">
<div class="area-card-icon">
<i :class="area.icon"></i>
</div>
<div class="area-card-name">{{area.name}}</div>
<div class="area-card-desc">{{area.description}}</div>
</div>
</div>
<!-- 已选择区域显示 -->
<div class="selected-areas" v-if="selectedAreas.length > 0">
<div class="selected-areas-title">
<i class="el-icon-check"></i>
已选择区域
</div>
<div class="selected-areas-list">
<div class="selected-area-tag" v-for="areaId in selectedAreas" :key="areaId">
{{getAreaName(areaId)}}
<span class="remove-btn" @click="removeArea(areaId)">×</span>
</div>
</div>
</div>
</div>
</div>
</el-form-item>
<!-- 菜单分类管理 -->
<el-form-item label="菜单分类" prop="menu_categories">
<div class="menu-category-container">
<div class="menu-category-header">
<h3 class="menu-category-title">
<i class="el-icon-menu"></i>
菜单分类管理
</h3>
<el-button type="primary" size="mini" @click="addCategory">
<i class="el-icon-plus"></i>
添加分类
</el-button>
</div>
<div class="menu-category-content">
<template v-for="(category, index) in ruleForm.menu_categories" :key="index">
<el-card class="category-card" style="margin-bottom: 15px;">
<div slot="header" class="clearfix">
<el-input v-model="category.name" placeholder="分类名称" style="width: 200px; margin-right: 10px;"></el-input>
<el-button type="danger" size="mini" @click="removeCategory(index)">
<i class="el-icon-delete"></i>
删除分类
</el-button>
</div>
<div class="menu-item-list">
<div class="menu-item-card" v-for="(item, itemIndex) in category.items" :key="itemIndex">
<div class="menu-item-header">
<el-input v-model="item.name" placeholder="菜品名称" class="menu-item-name"></el-input>
<el-input-number v-model="item.price" :precision="2" :step="0.01" :min="0" size="mini"></el-input-number>
</div>
<el-input type="textarea" :rows="2" v-model="item.description" placeholder="菜品描述" class="menu-item-desc"></el-input>
<div class="menu-item-actions">
<el-button type="danger" size="mini" @click="removeMenuItem(index, itemIndex)">
<i class="el-icon-delete"></i>
删除
</el-button>
</div>
</div>
<el-button type="primary" size="mini" @click="addMenuItem(index)" style="width: 100%; margin-top: 10px;">
<i class="el-icon-plus"></i>
添加菜品
</el-button>
</div>
</el-card>
</template>
</div>
</div>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitForm('ruleForm')" :loading="submitLoading">
<i class="el-icon-check"></i>
保存设置
</el-button>
<el-button @click="resetForm('ruleForm')">
<i class="el-icon-refresh"></i>
重置
</el-button>
</el-form-item>
</el-form>
</div>
<script>
var menuApp = new Vue({
el: '#menuManager',
data() {
return {
submitLoading: false,
ruleForm: {
status: '1',
menu_name: '',
menu_desc: '',
sort_order: 0,
business_time: [['09:00', '21:00']],
delivery_areas: [],
menu_categories: []
},
rules: {
menu_name: [
{ required: true, message: '请输入菜单名称', trigger: 'blur' }
],
menu_desc: [
{ required: true, message: '请输入菜单描述', trigger: 'blur' }
]
},
selectedAreas: [],
availableAreas: [
{
id: 1,
name: '市中心',
description: '市中心核心商业区',
icon: 'el-icon-office-building'
},
{
id: 2,
name: '高新区',
description: '高新技术开发区',
icon: 'el-icon-cpu'
},
{
id: 3,
name: '大学城',
description: '大学城及周边区域',
icon: 'el-icon-school'
},
{
id: 4,
name: '工业园区',
description: '工业园区及厂区',
icon: 'el-icon-goods'
},
{
id: 5,
name: '住宅区',
description: '居民住宅小区',
icon: 'el-icon-house'
},
{
id: 6,
name: '商务区',
description: '商务办公区域',
icon: 'el-icon-suitcase'
}
]
}
},
methods: {
// 时间选择器配置
getBusinessTime(index, type) {
return {
start: '00:00',
step: '00:30',
end: '23:59'
};
},
// 添加营业时间段
addBusinessTime() {
if (this.ruleForm.business_time.length < 3) {
this.ruleForm.business_time.push(['', '']);
}
},
// 删除营业时间段
spliceBusinessTime(index) {
this.ruleForm.business_time.splice(index, 1);
},
// 区域选择相关方法
isAreaSelected(areaId) {
return this.selectedAreas.includes(areaId);
},
toggleArea(areaId) {
const index = this.selectedAreas.indexOf(areaId);
if (index > -1) {
this.selectedAreas.splice(index, 1);
} else {
this.selectedAreas.push(areaId);
}
this.ruleForm.delivery_areas = this.selectedAreas;
},
removeArea(areaId) {
const index = this.selectedAreas.indexOf(areaId);
if (index > -1) {
this.selectedAreas.splice(index, 1);
this.ruleForm.delivery_areas = this.selectedAreas;
}
},
getAreaName(areaId) {
const area = this.availableAreas.find(a => a.id === areaId);
return area ? area.name : '';
},
// 菜单分类管理
addCategory() {
this.ruleForm.menu_categories.push({
name: '',
items: []
});
},
removeCategory(index) {
this.$confirm('确定要删除这个分类吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.ruleForm.menu_categories.splice(index, 1);
this.$message({
type: 'success',
message: '删除成功!'
});
}).catch(() => {
this.$message({
type: 'info',
message: '已取消删除'
});
});
},
addMenuItem(categoryIndex) {
this.ruleForm.menu_categories[categoryIndex].items.push({
name: '',
price: 0,
description: ''
});
},
removeMenuItem(categoryIndex, itemIndex) {
this.ruleForm.menu_categories[categoryIndex].items.splice(itemIndex, 1);
},
// 表单提交
submitForm(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
this.submitLoading = true;
// 模拟提交数据
const submitData = {
...this.ruleForm,
delivery_areas: this.selectedAreas
};
console.log('提交数据:', submitData);
// 调用实际的API接口
this.sendRequest('/index.php/Retail/Xcx/storeSet', {
data: submitData,
success: (res) => {
this.submitLoading = false;
if (res.status == 1) {
this.$message({
type: 'success',
message: res.message || '保存成功!'
});
} else {
this.$message({
type: 'error',
message: res.message || '保存失败,请检查权限设置'
});
}
},
error: (xhr, status, error) => {
this.submitLoading = false;
console.error('请求失败:', xhr.responseText);
this.$message({
type: 'error',
message: '网络请求失败,请稍后重试'
});
}
});
} else {
this.$message({
type: 'error',
message: '请检查表单填写是否正确!'
});
return false;
}
});
},
// 重置表单
resetForm(formName) {
this.$refs[formName].resetFields();
this.selectedAreas = [];
this.ruleForm.delivery_areas = [];
this.ruleForm.menu_categories = [];
}
},
// 通用请求方法
sendRequest(url, options) {
const defaultOptions = {
type: 'POST',
dataType: 'json',
data: options.data || {},
success: options.success || function() {},
error: options.error || function() {}
};
$.ajax({
url: url,
type: defaultOptions.type,
dataType: defaultOptions.dataType,
data: defaultOptions.data,
success: defaultOptions.success,
error: defaultOptions.error
});
},
mounted() {
// 初始化数据
console.log('菜单管理页面已加载');
}
});
</script>