文件

1200 行
54 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 -->
<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>