1200 行
54 KiB
HTML
可执行文件
1200 行
54 KiB
HTML
可执行文件
<!--[1.8.50]-->
|
||
<!-- 引入Vue.js -->
|
||
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.min.js"></script>
|
||
<!-- 引入Element UI -->
|
||
<script src="https://unpkg.com/element-ui/lib/index.js"></script>
|
||
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
|
||
<!-- 引入jQuery -->
|
||
<script src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.min.js"></script>
|
||
<!-- 引入腾讯地图组件 -->
|
||
<script charset="utf-8" type="text/javascript" src="/Public/js/get-position-map-tencent.js?v=1.0.2"></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;
|
||
}
|
||
|
||
#storeManager .el-date-editor {
|
||
width: 130px;
|
||
}
|
||
|
||
#storeManager .el-form-item--small.el-form-item {
|
||
margin-bottom: 30px;
|
||
}
|
||
|
||
#storeManager .el-input-group__append {
|
||
padding: 0 7px;
|
||
}
|
||
|
||
/* 电子围栏相关样式 */
|
||
.fence-container {
|
||
border: 1px solid #e4e7ed;
|
||
border-radius: 4px;
|
||
padding: 15px;
|
||
margin-top: 10px;
|
||
background-color: #fafafa;
|
||
}
|
||
|
||
.fence-list {
|
||
max-height: 300px;
|
||
overflow-y: auto;
|
||
margin-bottom: 15px;
|
||
}
|
||
|
||
.fence-item {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: 8px 12px;
|
||
margin-bottom: 8px;
|
||
background-color: #fff;
|
||
border: 1px solid #e4e7ed;
|
||
border-radius: 4px;
|
||
}
|
||
|
||
.fence-item:last-child {
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
.fence-info {
|
||
flex: 1;
|
||
}
|
||
|
||
.fence-name {
|
||
font-weight: 500;
|
||
color: #303133;
|
||
margin-bottom: 4px;
|
||
}
|
||
|
||
.fence-status {
|
||
font-size: 12px;
|
||
color: #909399;
|
||
}
|
||
|
||
.fence-actions {
|
||
display: flex;
|
||
gap: 8px;
|
||
}
|
||
|
||
.fence-map-container {
|
||
height: 400px;
|
||
border: 1px solid #dcdfe6;
|
||
border-radius: 4px;
|
||
margin-top: 10px;
|
||
position: relative;
|
||
}
|
||
|
||
.fence-map-tools {
|
||
position: absolute;
|
||
top: 10px;
|
||
left: 10px;
|
||
z-index: 1000;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 5px;
|
||
}
|
||
|
||
.drawing-mode {
|
||
background-color: #409eff !important;
|
||
border-color: #409eff !important;
|
||
}
|
||
</style>
|
||
<div id="storeManager" 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>
|
||
</el-collapse-item>
|
||
</el-collapse>
|
||
<el-form label-width="150px" :disabled="storeAuth.store_xcx_info==1?false:true" 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="skip">
|
||
<el-switch v-model="ruleForm.skip" :active-text="ruleForm.skip==1?'关闭':'开启'" inactive-value="1"
|
||
active-value="0">
|
||
</el-switch>
|
||
<span class="tips">关闭用户在商城下单则不可以选择同城配送</span>
|
||
</el-form-item>
|
||
<el-form-item label="默认配送时间" prop="send_delayed_time">
|
||
<el-input class="mini-input" oninput="value=this.value.replace(/^[0]|\D/,'')"
|
||
v-model="ruleForm.send_delayed_time" maxLength="8">
|
||
<template slot="append">分钟后</template>
|
||
</el-input>
|
||
<el-tooltip class="item" effect="dark" placement="top-start">
|
||
<div slot="content">
|
||
<p>根据下单时间往后推</p>
|
||
<p>拼团订单不会根据设置的时间设置</p>
|
||
<p>预售订单在预售商品发货期间内下单,将使用该设置</p>
|
||
</div>
|
||
<el-button icon="el-icon-question" type="text"></el-button>
|
||
</el-tooltip>
|
||
</el-form-item>
|
||
<el-form-item label="到店自提功能" prop="self_pick">
|
||
<el-switch v-model="ruleForm.self_pick" :active-text="ruleForm.self_pick==0?'关闭':'开启'" inactive-value="1"
|
||
active-value="0">
|
||
</el-switch>
|
||
<span class="tips">关闭用户在商城下单则不可以选择到店自提</span>
|
||
</el-form-item>
|
||
<el-form-item label="默认自提时间" prop="pickup_delayed_time">
|
||
<el-input class="mini-input" oninput="value=this.value.replace(/^[0]|\D/,'')"
|
||
v-model="ruleForm.pickup_delayed_time" maxLength="8">
|
||
<template slot="append">分钟后</template>
|
||
</el-input>
|
||
<el-tooltip class="item" effect="dark" placement="top-start">
|
||
<div slot="content">
|
||
<p>根据下单时间往后推</p>
|
||
<p>拼团订单不会根据设置的时间设置</p>
|
||
<p>预售订单在预售商品发货期间内下单,将使用该设置</p>
|
||
</div>
|
||
<el-button icon="el-icon-question" type="text"></el-button>
|
||
</el-tooltip>
|
||
</el-form-item>
|
||
<el-form-item label="自动生成时间" prop="auto_set_time">
|
||
<el-switch v-model="ruleForm.auto_set_time" :active-text="ruleForm.auto_set_time==1?'开启':'关闭'"
|
||
inactive-value="0" active-value="1">
|
||
</el-switch>
|
||
<span class="tips">开启后用户在商城下单将会按照最近时间为客户生成自提/配送时间</span>
|
||
</el-form-item>
|
||
<el-form-item label="快递发货功能" prop="express_status">
|
||
<el-switch v-model="ruleForm.express_status" :active-text="ruleForm.express_status==1?'关闭':'开启'"
|
||
inactive-value="1" active-value="2">
|
||
</el-switch>
|
||
<span class="tips">关闭后用户在商城下单则不可以选择快递发货</span>
|
||
</el-form-item>
|
||
<el-form-item label="非营业时间下单" prop="business_non_order">
|
||
<el-switch v-model="ruleForm.business_non_order" :active-text="ruleForm.business_non_order==2?'关闭':'开启'"
|
||
inactive-value="2" active-value="1">
|
||
</el-switch>
|
||
<span class="tips">关闭后不在营业时间内则商城不可以下单</span>
|
||
</el-form-item>
|
||
<el-form-item label="满额免配送费" prop="delivery_status">
|
||
<el-switch v-model="ruleForm.delivery_status" :active-text="ruleForm.delivery_status==0?'关闭':'开启'"
|
||
inactive-value="0" active-value="1">
|
||
</el-switch>
|
||
<span class="tips">开启后根据下面的金额设置启动免运费功能</span>
|
||
</el-form-item>
|
||
<el-form-item label="免配送费金额" prop="delivery_cost">
|
||
<el-input-number class="small-input" :precision="2" :step="0.01" :max="1000000" :min="0.00"
|
||
v-model="ruleForm.delivery_cost"></el-input-number>
|
||
<span class="tips">达到该金额以后免配送费(同城、快递)</span>
|
||
</el-form-item>
|
||
<el-form-item label="送货上门时间" prop="business">
|
||
<template v-for="(item,index) in ruleForm.business">
|
||
<div class="time-block">
|
||
<el-time-select placeholder="开始时间" v-model="item[0]" value-format="HH:mm"
|
||
:picker-options="getDeliveryTime(index,1)">
|
||
</el-time-select>
|
||
<el-time-select placeholder="结束时间" v-model="item[1]" value-format="HH:mm"
|
||
:picker-options="getDeliveryTime(index,2)">
|
||
</el-time-select>
|
||
<template v-if="index==0">
|
||
<el-button :disabled="ruleForm.business.length>=3?true:false" size="mini" @click="addBusiness()"
|
||
circle>
|
||
<i class="el-icon-plus"></i>
|
||
</el-button>
|
||
<span class="tips">送货上门服务的时间,最多可以设置3个时间段</span>
|
||
</template>
|
||
<el-button v-else @click="spliceBusiness(index)" size="mini" circle>
|
||
<i class="el-icon-minus"></i>
|
||
</el-button>
|
||
</div>
|
||
</template>
|
||
</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="原地址">
|
||
<el-input style="width: 500px" readonly v-model="before_address"></el-input>
|
||
</el-form-item>
|
||
<el-form-item label="经纬度" prop="longitude">
|
||
<div class="inline-block">
|
||
<span>经度</span>
|
||
<el-input class="small-input" readonly v-model="ruleForm.longitude"></el-input>
|
||
</div>
|
||
<div class="inline-block">
|
||
<span>维度</span>
|
||
<el-input class="small-input" readonly v-model="ruleForm.latitude"></el-input>
|
||
</div>
|
||
</el-form-item>
|
||
<el-form-item label="门店地址" prop="address">
|
||
<get-position-map-tencent v-if="pageLoading" :longitude.sync="ruleForm.longitude"
|
||
:latitude.sync="ruleForm.latitude" :address.sync="ruleForm.address">
|
||
</get-position-map-tencent>
|
||
</el-form-item>
|
||
<el-form-item label="推广语" prop="tip">
|
||
<el-input v-model="ruleForm.tip"></el-input>
|
||
<span class="tips">小程序商城门店切换页面展示</span>
|
||
</el-form-item>
|
||
<el-form-item label="门店电话" prop="phone">
|
||
<el-input class="small-input" oninput="value=this.value.replace(/^\D|[-]{0,2}/,'')" v-model="ruleForm.phone"
|
||
maxLength="11"></el-input>
|
||
<span class="tips">小程序商城门店联系电话</span>
|
||
</el-form-item>
|
||
<el-form-item label="服务半径" prop="math">
|
||
<el-input class="mini-input" v-model="ruleForm.math">
|
||
<template slot="append">m</template>
|
||
</el-input>
|
||
</el-form-item>
|
||
<el-form-item label="起送价" prop="smmoney">
|
||
<el-input class="mini-input" v-model="ruleForm.smmoney">
|
||
<template slot="append">元</template>
|
||
</el-input>
|
||
</el-form-item>
|
||
|
||
<!-- 电子围栏设置 -->
|
||
<el-form-item label="电子围栏">
|
||
<div class="fence-container">
|
||
<div style="margin-bottom: 15px;">
|
||
<el-button type="primary" size="mini" @click="addNewFence" icon="el-icon-plus">
|
||
添加围栏
|
||
</el-button>
|
||
<span class="tips">设置配送区域的电子围栏,支持多边形围栏</span>
|
||
</div>
|
||
|
||
<!-- 围栏列表 -->
|
||
<div class="fence-list" v-if="deliveryFences.length > 0">
|
||
<div class="fence-item" v-for="(fence, index) in deliveryFences" :key="fence.tempId">
|
||
<div class="fence-info">
|
||
<div class="fence-name">{{ fence.fence_name || '未命名围栏' }}</div>
|
||
<div class="fence-status">
|
||
{{ fence.is_active == 1 ? '启用' : '禁用' }} |
|
||
{{ fence.fence_type == 1 ? '多边形' : '圆形' }} |
|
||
坐标点数: {{ fence.fence_data ? fence.fence_data.length : 0 }}
|
||
</div>
|
||
</div>
|
||
<div class="fence-actions">
|
||
<el-button size="mini" type="text" @click="editFence(index)" icon="el-icon-edit">
|
||
编辑
|
||
</el-button>
|
||
<el-button size="mini" type="text" @click="toggleFenceStatus(index)"
|
||
:icon="fence.is_active == 1 ? 'el-icon-view' : 'el-icon-hide'">
|
||
{{ fence.is_active == 1 ? '禁用' : '启用' }}
|
||
</el-button>
|
||
<el-button size="mini" type="text" @click="deleteFence(index)" icon="el-icon-delete"
|
||
style="color: #f56c6c;">
|
||
删除
|
||
</el-button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div v-else style="text-align: center; color: #909399; padding: 20px;">
|
||
暂无围栏,点击"添加围栏"开始设置
|
||
</div>
|
||
|
||
<!-- 围栏编辑区域 -->
|
||
<div v-if="currentEditingFence !== null">
|
||
<el-divider content-position="left">
|
||
<span style="color: #409eff; font-weight: 500;">
|
||
{{ currentEditingFence === -1 ? '新建围栏' : '编辑围栏' }}
|
||
</span>
|
||
</el-divider>
|
||
|
||
<div style="margin-bottom: 15px;">
|
||
<el-row :gutter="20">
|
||
<el-col :span="8">
|
||
<el-input v-model="editingFenceData.fence_name" placeholder="请输入围栏名称" size="mini">
|
||
<template slot="prepend">围栏名称</template>
|
||
</el-input>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<el-switch v-model="editingFenceData.is_active" :active-value="1" :inactive-value="0"
|
||
active-text="启用" inactive-text="禁用">
|
||
</el-switch>
|
||
</el-col>
|
||
<el-col :span="10">
|
||
<el-button size="mini" @click="saveCurrentFence" type="primary">
|
||
保存围栏
|
||
</el-button>
|
||
<el-button size="mini" @click="cancelEditFence">
|
||
取消
|
||
</el-button>
|
||
</el-col>
|
||
</el-row>
|
||
</div>
|
||
|
||
<!-- 地图绘制工具栏 -->
|
||
<div style="margin-bottom: 10px;">
|
||
<el-button size="mini" @click="clearCurrentDrawing" type="danger" icon="el-icon-delete">
|
||
清除
|
||
</el-button>
|
||
<span style="margin-left: 15px; font-size: 12px; color: #909399;">
|
||
在地图上点击多个点来绘制多边形围栏,双击完成绘制
|
||
</span>
|
||
</div>
|
||
|
||
<!-- 地图绘制区域 -->
|
||
<div class="fence-map-container">
|
||
<div id="fenceMap" style="width: 100%; height: 100%;"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</el-form-item>
|
||
</el-form>
|
||
<div v-if="storeAuth.store_xcx_info==1" class="footer-button-box">
|
||
<el-button type="primary" @click="submitForm('ruleForm')" size="medium">立即提交
|
||
</el-button>
|
||
</div>
|
||
</div>
|
||
<script type="text/javascript" src="/Public/js/calculate.js"></script>
|
||
<script type="text/javascript">
|
||
var storeManager = new Vue({
|
||
el: "#storeManager",
|
||
data() {
|
||
return {
|
||
operate: {
|
||
delivery_fee: false,
|
||
},
|
||
before_address: "",
|
||
pageLoading: false,
|
||
|
||
// 电子围栏相关数据
|
||
deliveryFences: [], // 电子围栏列表
|
||
currentEditingFence: null, // 当前编辑的围栏索引,-1表示新建,null表示不在编辑状态
|
||
editingFenceData: { // 当前编辑的围栏数据
|
||
fence_name: '',
|
||
fence_data: '',
|
||
fence_type: 1, // 1=多边形,2=圆形
|
||
is_active: 1,
|
||
tempId: null
|
||
},
|
||
isDrawingMode: false, // 是否处于绘制模式
|
||
fenceMap: null, // 腾讯地图实例
|
||
currentPolygon: null, // 当前绘制的多边形
|
||
drawingManager: null, // 绘制管理器
|
||
tempFenceId: 1, // 临时ID计数器
|
||
mapKey: '', // 腾讯地图API Key
|
||
|
||
ruleForm: {
|
||
id: 0,
|
||
status: "0",//小程序商城 1开启 0关闭
|
||
skip: "1",//送货上门服务 0开启 1关闭
|
||
send_delayed_time: "0",//配送延时时间
|
||
pickup_delayed_time: "0",//自提延时时间
|
||
auto_set_time: "1",//自动生成时间
|
||
self_pick: "1",//到店自提功能 0开启 1关闭
|
||
express_status: "1",//快递发货功能 2开启 1关闭
|
||
business_non_order: "2",//非营业时间下单 1开启 2关闭
|
||
delivery_status: "0",//满额免配送费 1开启 0关闭
|
||
delivery_cost: '',//免配送费金额
|
||
business: [],//配送时间
|
||
business_time: [],//营业时间
|
||
address: "",//地址
|
||
longitude: 116.404,
|
||
latitude: 39.915,
|
||
tip: "",//推广语
|
||
phone: "",//联系手机
|
||
smmoney: "",//起送价
|
||
math: ""
|
||
},
|
||
rules: {
|
||
delivery_cost: [{ message: '请设置金额', required: false, trigger: 'change' }, {
|
||
validator: (rule, value, callback) => {
|
||
var reg = /^[+]{0,1}(\d+)$|^[+]{0,1}(\d+\.\d{0,2})$/;
|
||
if (storeManager.ruleForm.delivery_status == 1 && value === '' || value === undefined) {
|
||
callback(new Error("请设置金额"));
|
||
} else if (storeManager.ruleForm.delivery_status == 1 && (reg.test(value) == false || value < 0.01)) {
|
||
callback(new Error("请设置正确的金额"));
|
||
} else {
|
||
callback();
|
||
}
|
||
}, trigger: 'change'
|
||
}],
|
||
business: [{ message: '请选择配送时间段', required: true, trigger: 'change' }, {
|
||
validator: (rule, value, callback) => {
|
||
if (!value || !value.length) {
|
||
callback(new Error("请选择配送时间段"));
|
||
} else {
|
||
for (let i = 0; i < value.length; i++) {
|
||
let key = i + 1;
|
||
if (!value[i] || !value[i][0] || !value[i][1]) {
|
||
callback(new Error("请完善第" + key + "组时间段"));
|
||
return;
|
||
}
|
||
if (value[i][0] == value[i][1]) {
|
||
callback(new Error("第" + key + "组时间段开始时间不得等于结束时间"));
|
||
return;
|
||
}
|
||
|
||
// 检查与其他时间段是否冲突
|
||
for (let j = 0; j < value.length; j++) {
|
||
if (i !== j && value[j] && value[j][0] && value[j][1]) {
|
||
if (storeManager.isTimeConflict(value[i][0], value[i][1], value[j][0], value[j][1])) {
|
||
callback(new Error("第" + key + "组时间段与第" + (j + 1) + "组时间段存在冲突"));
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
callback();
|
||
}
|
||
}, trigger: 'change'
|
||
}],
|
||
business_time: [{ message: '请选择营业时间段', required: true, trigger: 'change' }, {
|
||
validator: (rule, value, callback) => {
|
||
if (!value || !value.length) {
|
||
callback(new Error("请选择营业时间段"));
|
||
} else {
|
||
for (let i = 0; i < value.length; i++) {
|
||
let key = i + 1;
|
||
if (!value[i] || !value[i][0] || !value[i][1]) {
|
||
callback(new Error("请完善第" + key + "组时间段"));
|
||
return;
|
||
}
|
||
if (value[i][0] == value[i][1]) {
|
||
callback(new Error("第" + key + "组时间段开始时间不得等于结束时间"));
|
||
return;
|
||
}
|
||
|
||
// 检查与其他时间段是否冲突
|
||
for (let j = 0; j < value.length; j++) {
|
||
if (i !== j && value[j] && value[j][0] && value[j][1]) {
|
||
if (storeManager.isTimeConflict(value[i][0], value[i][1], value[j][0], value[j][1])) {
|
||
callback(new Error("第" + key + "组时间段与第" + (j + 1) + "组时间段存在冲突"));
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
callback();
|
||
}
|
||
}, trigger: 'change'
|
||
}],
|
||
smmoney: [{ message: '请设置正确的起送价', required: false, trigger: 'change' }, {
|
||
validator: (rule, value, callback) => {
|
||
var reg = /^[+]{0,1}(\d+)$|^[+]{0,1}(\d+\.\d{0,2})$/;
|
||
if (value && (reg.test(value) == false || value < 0)) {
|
||
callback(new Error("请设置正确的起送价"));
|
||
} else {
|
||
callback();
|
||
}
|
||
}, trigger: 'change'
|
||
}],
|
||
}
|
||
}
|
||
},
|
||
methods: {
|
||
//根据[时:分]获取时间戳
|
||
getTimestamp(time) {
|
||
let timestamp = Date.parse(new Date('1970-01-01 ' + time + ':00'));
|
||
// console.log(time, timestamp);
|
||
return timestamp;
|
||
},
|
||
//检查时间范围是否跨天
|
||
isCrossDay(startTime, endTime) {
|
||
return this.getTimestamp(startTime) > this.getTimestamp(endTime);
|
||
},
|
||
//比较两个时间段是否有冲突(支持跨天)
|
||
isTimeConflict(time1Start, time1End, time2Start, time2End) {
|
||
const t1s = this.getTimestamp(time1Start);
|
||
const t1e = this.getTimestamp(time1End);
|
||
const t2s = this.getTimestamp(time2Start);
|
||
const t2e = this.getTimestamp(time2End);
|
||
|
||
// 如果时间段1跨天
|
||
if (this.isCrossDay(time1Start, time1End)) {
|
||
// 如果时间段2也跨天
|
||
if (this.isCrossDay(time2Start, time2End)) {
|
||
return true; // 两个跨天时间段必然冲突
|
||
}
|
||
// 时间段2不跨天,检查是否与时间段1的前半部分或后半部分冲突
|
||
return (t2s >= t1s || t2e <= t1e || t2s <= t1e);
|
||
}
|
||
|
||
// 如果时间段1不跨天,时间段2跨天
|
||
if (this.isCrossDay(time2Start, time2End)) {
|
||
return (t1s >= t2s || t1e <= t2e || t1s <= t2e);
|
||
}
|
||
|
||
// 两个时间段都不跨天,使用常规比较
|
||
return !(t1e <= t2s || t1s >= t2e);
|
||
},
|
||
//获取配送时间限制
|
||
getDeliveryTime(index, type) {
|
||
// 移除时间范围限制,允许跨天时间选择
|
||
return {
|
||
start: '00:00',
|
||
step: '00:05',
|
||
end: '23:59'
|
||
}
|
||
},
|
||
//获取营业时间限制
|
||
getBusinessTime(index, type) {
|
||
// 移除时间范围限制,允许跨天时间选择
|
||
return {
|
||
start: '00:00',
|
||
step: '00:05',
|
||
end: '23:59'
|
||
}
|
||
},
|
||
//添加配送时间段
|
||
addBusiness() {
|
||
this.ruleForm.business.push(['', '']);
|
||
},
|
||
//删除配送时间段
|
||
spliceBusiness(index) {
|
||
this.ruleForm.business.splice(index, 1);
|
||
},
|
||
//添加营业时间段
|
||
addBusinessTime() {
|
||
this.ruleForm.business_time.push(['', '']);
|
||
},
|
||
//删除营业时间段
|
||
spliceBusinessTime(index) {
|
||
this.ruleForm.business_time.splice(index, 1);
|
||
},
|
||
submitForm(formName) {
|
||
this.$refs[formName].validate((valid) => {
|
||
if (valid) {
|
||
let data = { ...this.ruleForm };
|
||
|
||
// 添加电子围栏数据到提交数据中
|
||
data.delivery_fences = this.deliveryFences.map(fence => ({
|
||
fence_name: fence.fence_name,
|
||
fence_data: fence.fence_data,
|
||
fence_type: fence.fence_type,
|
||
is_active: fence.is_active
|
||
}));
|
||
|
||
$.ajax({
|
||
url: '{:U("storeSet")}',
|
||
type: 'post',
|
||
data: data,
|
||
success: (res) => {
|
||
if (res.status == 1) {
|
||
this.$message.success(res.message);
|
||
this.detail_address = this.ruleForm.address;
|
||
} else {
|
||
this.$message.error(res.message);
|
||
}
|
||
},
|
||
})
|
||
} else {
|
||
this.$message.warning('请检查必填项');
|
||
}
|
||
})
|
||
},
|
||
|
||
// 处理地图位置选择
|
||
handlePositionSelected(position) {
|
||
this.selectedPosition = position;
|
||
},
|
||
|
||
// 确认地图选择
|
||
confirmMapSelection() {
|
||
if (this.selectedPosition) {
|
||
this.ruleForm.address = this.selectedPosition.address;
|
||
this.ruleForm.longitude = this.selectedPosition.lng;
|
||
this.ruleForm.latitude = this.selectedPosition.lat;
|
||
this.showMapDialog = false;
|
||
this.selectedPosition = null;
|
||
this.$message.success('地址选择成功');
|
||
} else {
|
||
this.$message.warning('请先在地图上选择位置');
|
||
}
|
||
},
|
||
|
||
// 处理地图对话框关闭
|
||
handleMapDialogClose() {
|
||
this.showMapDialog = false;
|
||
this.selectedPosition = null;
|
||
},
|
||
|
||
// ========== 电子围栏相关方法 ==========
|
||
|
||
// 添加新围栏
|
||
addNewFence() {
|
||
this.currentEditingFence = -1; // -1表示新建
|
||
this.editingFenceData = {
|
||
fence_name: '',
|
||
fence_data: '', // 将改为数组存储多个多边形
|
||
fence_polygons: [], // 新增:存储多个多边形的数组
|
||
fence_type: 1,
|
||
is_active: 1,
|
||
tempId: this.tempFenceId++
|
||
};
|
||
this.$nextTick(() => {
|
||
this.initFenceMap();
|
||
});
|
||
},
|
||
|
||
// 编辑围栏
|
||
editFence(index) {
|
||
this.currentEditingFence = index;
|
||
const fence = this.deliveryFences[index];
|
||
this.editingFenceData = {
|
||
fence_name: fence.fence_name,
|
||
fence_data: fence.fence_data,
|
||
fence_polygons: fence.fence_polygons || [], // 兼容旧数据
|
||
fence_type: fence.fence_type,
|
||
is_active: fence.is_active,
|
||
tempId: fence.tempId
|
||
};
|
||
|
||
this.$nextTick(() => {
|
||
this.initFenceMap();
|
||
});
|
||
},
|
||
|
||
// 切换围栏状态
|
||
toggleFenceStatus(index) {
|
||
this.deliveryFences[index].is_active = this.deliveryFences[index].is_active == 1 ? 0 : 1;
|
||
this.$message.success('围栏状态已更新');
|
||
},
|
||
|
||
// 删除围栏
|
||
deleteFence(index) {
|
||
this.$confirm('确定要删除这个围栏吗?', '提示', {
|
||
confirmButtonText: '确定',
|
||
cancelButtonText: '取消',
|
||
type: 'warning'
|
||
}).then(() => {
|
||
const fence = this.deliveryFences[index];
|
||
|
||
// 如果围栏有ID,说明是已保存的围栏,需要异步删除
|
||
if (fence.id) {
|
||
// 发送删除请求到后端
|
||
$.ajax({
|
||
url: '{:U("deleteFence")}',
|
||
type: 'POST',
|
||
data: { id: fence.id },
|
||
dataType: 'json',
|
||
success: (res) => {
|
||
if (res.status == 1) {
|
||
// 后端删除成功,从前端数组中移除
|
||
this.deliveryFences.splice(index, 1);
|
||
this.$message.success('围栏删除成功');
|
||
} else {
|
||
this.$message.error(res.msg || '删除失败');
|
||
}
|
||
},
|
||
error: () => {
|
||
this.$message.error('删除请求失败');
|
||
}
|
||
});
|
||
} else {
|
||
// 没有ID的围栏是新创建未保存的,直接从数组中删除
|
||
this.deliveryFences.splice(index, 1);
|
||
this.$message.success('围栏已删除');
|
||
}
|
||
}).catch(() => { });
|
||
},
|
||
|
||
// 处理绘制完成事件
|
||
handleDrawComplete(geometry) {
|
||
console.log('处理绘制完成的几何图形:', geometry);
|
||
|
||
// 获取多边形的坐标数据
|
||
const polygonId = geometry.id;
|
||
const polygonGeometry = this.polygonLayer.geometries.find(g => g.id === polygonId);
|
||
|
||
if (polygonGeometry && polygonGeometry.paths) {
|
||
// 将TMap.LatLng对象转换为普通的坐标对象
|
||
const paths = polygonGeometry.paths.map(coord => ({
|
||
lat: coord.lat,
|
||
lng: coord.lng,
|
||
height: 0 // 添加height字段以匹配后端格式
|
||
}));
|
||
|
||
// 将新绘制的多边形添加到数组中
|
||
const polygonData = {
|
||
id: polygonId,
|
||
paths: paths,
|
||
timestamp: Date.now()
|
||
};
|
||
|
||
this.editingFenceData.fence_polygons.push(polygonData);
|
||
|
||
// 更新fence_data为多边形数组(不再是JSON字符串)
|
||
this.editingFenceData.fence_data = [...this.editingFenceData.fence_polygons];
|
||
|
||
console.log('已添加多边形到围栏数据:', polygonData);
|
||
console.log('当前围栏包含多边形数量:', this.editingFenceData.fence_polygons.length);
|
||
|
||
this.$message.success(`多边形绘制完成!当前围栏共有 ${this.editingFenceData.fence_polygons.length} 个多边形`);
|
||
}
|
||
},
|
||
|
||
// 保存当前围栏
|
||
saveCurrentFence() {
|
||
if (!this.editingFenceData.fence_name.trim()) {
|
||
this.$message.warning('请输入围栏名称');
|
||
return;
|
||
}
|
||
|
||
// 检查是否有绘制的多边形数据
|
||
if (!this.editingFenceData.fence_polygons || this.editingFenceData.fence_polygons.length === 0) {
|
||
this.$message.warning('请在地图上绘制至少一个围栏多边形');
|
||
return;
|
||
}
|
||
|
||
const fenceData = {
|
||
fence_name: this.editingFenceData.fence_name,
|
||
fence_data: this.editingFenceData.fence_data, // 现在是数组格式
|
||
fence_type: this.editingFenceData.fence_type,
|
||
is_active: this.editingFenceData.is_active,
|
||
tempId: this.editingFenceData.tempId
|
||
};
|
||
|
||
if (this.currentEditingFence === -1) {
|
||
// 新建围栏
|
||
this.deliveryFences.push(fenceData);
|
||
this.$message.success(`围栏添加成功!包含 ${this.editingFenceData.fence_polygons.length} 个多边形`);
|
||
} else {
|
||
// 编辑围栏
|
||
this.$set(this.deliveryFences, this.currentEditingFence, fenceData);
|
||
this.$message.success(`围栏更新成功!包含 ${this.editingFenceData.fence_polygons.length} 个多边形`);
|
||
}
|
||
|
||
this.cancelEditFence();
|
||
},
|
||
|
||
// 取消编辑围栏
|
||
cancelEditFence() {
|
||
this.currentEditingFence = null;
|
||
this.editingFenceData = {
|
||
fence_name: '',
|
||
fence_data: '',
|
||
fence_polygons: [], // 重置多边形数组
|
||
fence_type: 1,
|
||
is_active: 1,
|
||
tempId: null
|
||
};
|
||
this.isDrawingMode = false;
|
||
if (this.fenceMap) {
|
||
this.fenceMap.destroy();
|
||
this.fenceMap = null;
|
||
}
|
||
},
|
||
|
||
// 初始化围栏地图
|
||
initFenceMap() {
|
||
if (this.fenceMap) {
|
||
this.fenceMap.destroy();
|
||
}
|
||
|
||
// 获取腾讯地图Key并初始化地图
|
||
this.getTencentMapKey().then(success => {
|
||
if (success && this.mapKey) {
|
||
this.loadTencentMapScript();
|
||
}
|
||
});
|
||
},
|
||
|
||
// 获取腾讯地图Key
|
||
async getTencentMapKey() {
|
||
try {
|
||
const response = await fetch('/index.php/Retail/Store/getTencentMapKey', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
}
|
||
});
|
||
const result = await response.json();
|
||
|
||
if (result.status === 1 && result.key) {
|
||
this.mapKey = result.key;
|
||
return true;
|
||
} else {
|
||
console.error('获取腾讯地图API Key失败:', result.msg || '未知错误');
|
||
this.$message.error('获取地图配置失败,请联系管理员');
|
||
return false;
|
||
}
|
||
} catch (error) {
|
||
console.error('获取腾讯地图API Key异常:', error);
|
||
this.$message.error('获取地图配置异常,请检查网络连接');
|
||
return false;
|
||
}
|
||
},
|
||
|
||
// 动态加载腾讯地图脚本
|
||
loadTencentMapScript() {
|
||
// 检查是否已经加载了腾讯地图API
|
||
if (window.TMap && window.TMap.tools && window.TMap.tools.GeometryEditor) {
|
||
this.initTencentFenceMap();
|
||
return;
|
||
}
|
||
|
||
// 先加载主要的腾讯地图API
|
||
const mainScript = document.createElement('script');
|
||
mainScript.src = `https://map.qq.com/api/gljs?v=1.exp&key=${this.mapKey}`;
|
||
mainScript.onload = () => {
|
||
// 主API加载完成后,再加载绘制工具库
|
||
const drawingScript = document.createElement('script');
|
||
drawingScript.src = `https://map.qq.com/api/gljs?v=1.exp&key=${this.mapKey}&libraries=tools`;
|
||
drawingScript.onload = () => {
|
||
// 确保绘制工具完全加载后再初始化地图
|
||
setTimeout(() => {
|
||
if (window.TMap && window.TMap.tools && window.TMap.tools.GeometryEditor) {
|
||
this.initTencentFenceMap();
|
||
} else {
|
||
this.$message.error('地图绘制工具加载失败');
|
||
}
|
||
}, 500);
|
||
};
|
||
drawingScript.onerror = () => {
|
||
this.$message.error('地图绘制工具加载失败');
|
||
};
|
||
document.head.appendChild(drawingScript);
|
||
};
|
||
mainScript.onerror = () => {
|
||
this.$message.error('地图加载失败,请检查网络连接');
|
||
};
|
||
document.head.appendChild(mainScript);
|
||
},
|
||
|
||
// 初始化腾讯地图实例
|
||
initTencentFenceMap() {
|
||
try {
|
||
// 使用门店的经纬度作为地图中心
|
||
const center = new TMap.LatLng(this.ruleForm.latitude, this.ruleForm.longitude);
|
||
|
||
this.fenceMap = new TMap.Map(document.getElementById('fenceMap'), {
|
||
center: center,
|
||
zoom: 15,
|
||
mapTypeId: 'roadmap'
|
||
});
|
||
|
||
// 添加门店标记
|
||
new TMap.MultiMarker({
|
||
map: this.fenceMap,
|
||
styles: {
|
||
'store': new TMap.MarkerStyle({
|
||
'width': 25,
|
||
'height': 35,
|
||
'anchor': { x: 16, y: 32 },
|
||
'src': 'https://mapapi.qq.com/web/lbs/javascriptGL/demo/img/markerDefault.png'
|
||
})
|
||
},
|
||
geometries: [{
|
||
id: 'store',
|
||
styleId: 'store',
|
||
position: center,
|
||
properties: {
|
||
title: '门店位置'
|
||
}
|
||
}]
|
||
});
|
||
|
||
// 初始化多边形图层
|
||
this.polygonLayer = new TMap.MultiPolygon({
|
||
map: this.fenceMap
|
||
});
|
||
|
||
// 初始化几何编辑器 - 完全按照官方示例,默认启用绘制模式
|
||
this.geometryEditor = new TMap.tools.GeometryEditor({
|
||
map: this.fenceMap, // 编辑器绑定的地图对象
|
||
overlayList: [ // 可编辑图层
|
||
{
|
||
id: 'polygon', // 图层ID
|
||
overlay: this.polygonLayer // MultiPolygon图层
|
||
}
|
||
],
|
||
actionMode: TMap.tools.constants.EDITOR_ACTION.DRAW, // 编辑器的工作模式
|
||
snappable: true // 开启邻近吸附
|
||
});
|
||
|
||
// 默认激活多边形绘制模式
|
||
this.geometryEditor.setActiveOverlay('polygon');
|
||
|
||
// 监听绘制完成事件,获取绘制的多边形数据
|
||
this.geometryEditor.on('draw_complete', (geometry) => {
|
||
console.log('绘制完成,获取到多边形数据:', geometry);
|
||
this.handleDrawComplete(geometry);
|
||
});
|
||
|
||
// 地图加载完成后的提示
|
||
this.fenceMap.on('tilesloaded', () => {
|
||
console.log('地图加载完成,可以开始绘制');
|
||
|
||
// 如果是编辑模式且有围栏数据,在地图加载完成后显示围栏
|
||
if (this.currentEditingFence !== null && this.editingFenceData.fence_data) {
|
||
setTimeout(() => {
|
||
this.displayFenceOnMap(this.editingFenceData.fence_data);
|
||
}, 100); // 稍微延迟确保地图完全加载
|
||
}
|
||
});
|
||
|
||
this.$message.success('地图初始化完成');
|
||
|
||
} catch (error) {
|
||
console.error('地图初始化失败:', error);
|
||
this.$message.error('地图初始化失败,请刷新页面重试');
|
||
}
|
||
},
|
||
|
||
// 切换绘制模式
|
||
toggleDrawingMode() {
|
||
if (!this.fenceMap) {
|
||
this.$message.warning('地图未初始化');
|
||
return;
|
||
}
|
||
|
||
this.isDrawingMode = !this.isDrawingMode;
|
||
|
||
if (this.isDrawingMode) {
|
||
this.startDrawing();
|
||
} else {
|
||
this.stopDrawing();
|
||
}
|
||
},
|
||
|
||
// 开始绘制 - 按照官方示例实现
|
||
startDrawing() {
|
||
if (!this.fenceMap || !this.geometryEditor) {
|
||
this.$message.warning('地图或编辑器未初始化');
|
||
return;
|
||
}
|
||
|
||
try {
|
||
// 清除之前的绘制
|
||
this.clearCurrentDrawing();
|
||
|
||
// 激活多边形绘制 - 按照官方示例的方式
|
||
this.geometryEditor.setActiveOverlay('polygon');
|
||
|
||
this.$message.success('开始绘制模式,请在地图上点击绘制多边形,双击结束绘制');
|
||
|
||
} catch (error) {
|
||
console.error('启动绘制模式失败:', error);
|
||
this.$message.error('启动绘制模式失败,请重新尝试');
|
||
this.isDrawingMode = false;
|
||
}
|
||
},
|
||
|
||
// 停止绘制 - 按照官方示例实现
|
||
stopDrawing() {
|
||
if (this.geometryEditor) {
|
||
try {
|
||
// 停止编辑器
|
||
this.geometryEditor.stop();
|
||
this.$message.info('已停止绘制模式');
|
||
} catch (error) {
|
||
console.error('停止绘制失败:', error);
|
||
}
|
||
}
|
||
this.isDrawingMode = false;
|
||
},
|
||
|
||
// 清除当前绘制 - 修复清除方法
|
||
clearCurrentDrawing() {
|
||
try {
|
||
// 停止编辑器
|
||
if (this.geometryEditor) {
|
||
this.geometryEditor.stop();
|
||
}
|
||
|
||
// 清除多边形图层中的所有图形
|
||
if (this.polygonLayer) {
|
||
this.polygonLayer.setGeometries([]);
|
||
}
|
||
|
||
// 清空编辑数据
|
||
this.editingFenceData.fence_data = '';
|
||
this.editingFenceData.fence_polygons = []; // 清空多边形数组
|
||
|
||
// 重置绘制状态
|
||
this.isDrawingMode = false;
|
||
|
||
this.$message.success('已清除绘制内容');
|
||
} catch (error) {
|
||
console.error('清除绘制失败:', error);
|
||
this.$message.error('清除失败,请重试');
|
||
}
|
||
},
|
||
|
||
// 在地图上显示围栏
|
||
displayFenceOnMap(fenceData) {
|
||
try {
|
||
let polygonsToDisplay = [];
|
||
|
||
// 兼容新的后端数据格式和旧数据格式
|
||
if (typeof fenceData === 'string') {
|
||
try {
|
||
const parsedData = JSON.parse(fenceData);
|
||
if (Array.isArray(parsedData)) {
|
||
// 检查是否是新格式(包含id和paths的对象数组)
|
||
if (parsedData.length > 0 && parsedData[0].id && parsedData[0].paths) {
|
||
// 新格式:多个多边形对象数组
|
||
polygonsToDisplay = parsedData;
|
||
} else {
|
||
// 旧格式:多个多边形数组
|
||
polygonsToDisplay = parsedData.map((paths, index) => ({
|
||
id: `polygon_${index}`,
|
||
paths: paths
|
||
}));
|
||
}
|
||
} else if (parsedData.lat && parsedData.lng) {
|
||
// 旧格式:单个多边形坐标数组
|
||
polygonsToDisplay = [{ id: 'polygon_0', paths: parsedData }];
|
||
}
|
||
} catch (e) {
|
||
console.error('解析围栏数据失败:', e);
|
||
return;
|
||
}
|
||
} else if (Array.isArray(fenceData)) {
|
||
// 检查是否是新格式的数组
|
||
if (fenceData.length > 0 && fenceData[0].id && fenceData[0].paths) {
|
||
// 新格式:直接使用
|
||
polygonsToDisplay = fenceData;
|
||
} else {
|
||
// 旧格式:直接是坐标数组
|
||
polygonsToDisplay = [{ id: 'polygon_0', paths: fenceData }];
|
||
}
|
||
}
|
||
|
||
if (polygonsToDisplay.length === 0) {
|
||
console.log('没有可显示的围栏数据');
|
||
return;
|
||
}
|
||
|
||
// 准备地图上的几何图形数据
|
||
const geometries = [];
|
||
const bounds = new TMap.LatLngBounds();
|
||
|
||
polygonsToDisplay.forEach((polygon, index) => {
|
||
if (polygon.paths && Array.isArray(polygon.paths)) {
|
||
const path = polygon.paths.map(coord => new TMap.LatLng(coord.lat, coord.lng));
|
||
|
||
geometries.push({
|
||
id: polygon.id || `fence_${index}`,
|
||
styleId: 'fence',
|
||
paths: path
|
||
});
|
||
|
||
// 扩展边界
|
||
polygon.paths.forEach(coord => {
|
||
bounds.extend(new TMap.LatLng(coord.lat, coord.lng));
|
||
});
|
||
}
|
||
});
|
||
|
||
// 创建多边形图层显示所有围栏
|
||
this.currentPolygon = new TMap.MultiPolygon({
|
||
map: this.fenceMap,
|
||
styles: {
|
||
'fence': new TMap.PolygonStyle({
|
||
'color': 'rgba(55,119,255,0.16)',
|
||
'showBorder': true,
|
||
'fillColor': '#409eff',
|
||
'fillOpacity': 0.3,
|
||
'borderColor': '#409eff',
|
||
'borderOpacity': 0.8,
|
||
'borderWidth': 2
|
||
})
|
||
},
|
||
geometries: geometries
|
||
});
|
||
|
||
// 调整地图视野以包含所有多边形
|
||
this.fenceMap.fitBounds(bounds);
|
||
|
||
console.log(`已显示 ${geometries.length} 个围栏多边形`);
|
||
|
||
} catch (error) {
|
||
console.error('显示围栏数据错误:', error);
|
||
}
|
||
},
|
||
|
||
},
|
||
mounted() {
|
||
//获取基本设置
|
||
$.ajax({
|
||
url: '/index.php/Retail/Xcx/manager',
|
||
type: 'post',
|
||
success: (res) => {
|
||
if (res.status == 1) {
|
||
// 先赋值表单,确保有经纬度可用
|
||
this.ruleForm = res.data;
|
||
this.pageLoading = true;
|
||
this.before_address = res.data.address;
|
||
|
||
// 加载围栏数据
|
||
if (res.data.delivery_fences && res.data.delivery_fences.length > 0) {
|
||
this.deliveryFences = res.data.delivery_fences;
|
||
console.log('已加载围栏数据:', this.deliveryFences);
|
||
}
|
||
|
||
} else {
|
||
this.$message.error(res.msg)
|
||
}
|
||
}
|
||
});
|
||
},
|
||
created() {
|
||
//获取操作权限
|
||
var auth_list = {
|
||
delivery_fee: '/index.php/Retail/Xcx/distribution'
|
||
};
|
||
$.ajax({
|
||
url: '/index.php/Retail/Component/checkOperateAuth',
|
||
type: "post",
|
||
data: {
|
||
auth_list: JSON.stringify(auth_list),
|
||
},
|
||
success: (res) => {
|
||
if (res.status == 1) {
|
||
this.operate = res.data;
|
||
}
|
||
}
|
||
})
|
||
},
|
||
watch: {
|
||
}
|
||
})
|
||
</script> |