1579 行
53 KiB
HTML
可执行文件
1579 行
53 KiB
HTML
可执行文件
<!--[1.8.50] 门店管理 - 可视化配送区域版本-->
|
||
<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;
|
||
}
|
||
|
||
#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;
|
||
}
|
||
|
||
/* 可视化配送区域样式 */
|
||
.delivery-area-container {
|
||
border: 1px solid #e4e7ed;
|
||
border-radius: 8px;
|
||
overflow: hidden;
|
||
background: #fff;
|
||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
||
}
|
||
|
||
.delivery-map-header {
|
||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||
color: white;
|
||
padding: 15px 20px;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
}
|
||
|
||
.delivery-map-title {
|
||
font-size: 16px;
|
||
font-weight: 600;
|
||
margin: 0;
|
||
}
|
||
|
||
.delivery-map-status {
|
||
font-size: 12px;
|
||
opacity: 0.9;
|
||
}
|
||
|
||
.delivery-map-wrapper {
|
||
position: relative;
|
||
background: #f8f9fa;
|
||
}
|
||
|
||
.delivery-map {
|
||
width: 100%;
|
||
height: 500px;
|
||
border: none;
|
||
display: block;
|
||
}
|
||
|
||
.map-toolbar {
|
||
position: absolute;
|
||
top: 15px;
|
||
left: 15px;
|
||
z-index: 1000;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 8px;
|
||
}
|
||
|
||
.map-tool-group {
|
||
background: rgba(255, 255, 255, 0.95);
|
||
border-radius: 8px;
|
||
padding: 8px;
|
||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
||
backdrop-filter: blur(10px);
|
||
}
|
||
|
||
.map-tool-btn {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
width: 40px;
|
||
height: 40px;
|
||
border: none;
|
||
border-radius: 6px;
|
||
background: transparent;
|
||
color: #606266;
|
||
cursor: pointer;
|
||
transition: all 0.3s ease;
|
||
font-size: 16px;
|
||
margin-bottom: 4px;
|
||
}
|
||
|
||
.map-tool-btn:last-child {
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
.map-tool-btn:hover {
|
||
background: #f0f2f5;
|
||
color: #409eff;
|
||
transform: scale(1.05);
|
||
}
|
||
|
||
.map-tool-btn.active {
|
||
background: #409eff;
|
||
color: white;
|
||
box-shadow: 0 2px 4px rgba(64, 158, 255, 0.3);
|
||
}
|
||
|
||
.map-tool-btn.danger {
|
||
color: #f56c6c;
|
||
}
|
||
|
||
.map-tool-btn.danger:hover {
|
||
background: #fef0f0;
|
||
color: #f56c6c;
|
||
}
|
||
|
||
.drawing-tips {
|
||
position: absolute;
|
||
top: 15px;
|
||
right: 15px;
|
||
background: rgba(255, 255, 255, 0.95);
|
||
border-radius: 8px;
|
||
padding: 12px 16px;
|
||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
||
backdrop-filter: blur(10px);
|
||
max-width: 280px;
|
||
z-index: 1000;
|
||
}
|
||
|
||
.drawing-tips.hidden {
|
||
display: none;
|
||
}
|
||
|
||
.tips-title {
|
||
font-size: 14px;
|
||
font-weight: 600;
|
||
color: #303133;
|
||
margin: 0 0 8px 0;
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
|
||
.tips-content {
|
||
font-size: 12px;
|
||
color: #606266;
|
||
line-height: 1.5;
|
||
margin: 0;
|
||
}
|
||
|
||
.area-management {
|
||
padding: 20px;
|
||
background: #fafbfc;
|
||
border-top: 1px solid #e4e7ed;
|
||
}
|
||
|
||
.area-list-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 15px;
|
||
}
|
||
|
||
.area-list-title {
|
||
font-size: 16px;
|
||
font-weight: 600;
|
||
color: #303133;
|
||
margin: 0;
|
||
}
|
||
|
||
.area-stats {
|
||
font-size: 12px;
|
||
color: #909399;
|
||
}
|
||
|
||
.area-list {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
||
gap: 15px;
|
||
max-height: 300px;
|
||
overflow-y: auto;
|
||
}
|
||
|
||
.area-item {
|
||
background: white;
|
||
border: 1px solid #e4e7ed;
|
||
border-radius: 8px;
|
||
padding: 15px;
|
||
transition: all 0.3s ease;
|
||
position: relative;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.area-item:hover {
|
||
border-color: #409eff;
|
||
box-shadow: 0 2px 8px rgba(64, 158, 255, 0.15);
|
||
transform: translateY(-2px);
|
||
}
|
||
|
||
.area-item.active {
|
||
border-color: #409eff;
|
||
background: #f0f9ff;
|
||
}
|
||
|
||
.area-item-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 10px;
|
||
}
|
||
|
||
.area-name {
|
||
font-size: 14px;
|
||
font-weight: 600;
|
||
color: #303133;
|
||
margin: 0;
|
||
}
|
||
|
||
.area-actions {
|
||
display: flex;
|
||
gap: 5px;
|
||
}
|
||
|
||
.area-action-btn {
|
||
width: 24px;
|
||
height: 24px;
|
||
border: none;
|
||
border-radius: 4px;
|
||
background: transparent;
|
||
color: #909399;
|
||
cursor: pointer;
|
||
transition: all 0.3s ease;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 12px;
|
||
}
|
||
|
||
.area-action-btn:hover {
|
||
background: #f0f2f5;
|
||
color: #409eff;
|
||
}
|
||
|
||
.area-action-btn.danger:hover {
|
||
background: #fef0f0;
|
||
color: #f56c6c;
|
||
}
|
||
|
||
.area-info {
|
||
display: grid;
|
||
grid-template-columns: 1fr 1fr;
|
||
gap: 10px;
|
||
font-size: 12px;
|
||
color: #606266;
|
||
}
|
||
|
||
.area-info-item {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 5px;
|
||
}
|
||
|
||
.area-info-icon {
|
||
color: #909399;
|
||
}
|
||
|
||
.empty-area-list {
|
||
text-align: center;
|
||
padding: 40px 20px;
|
||
color: #909399;
|
||
}
|
||
|
||
.empty-area-icon {
|
||
font-size: 48px;
|
||
margin-bottom: 15px;
|
||
opacity: 0.5;
|
||
}
|
||
|
||
.search-controls {
|
||
position: absolute;
|
||
bottom: 15px;
|
||
left: 15px;
|
||
right: 15px;
|
||
z-index: 1000;
|
||
}
|
||
|
||
.search-input-wrapper {
|
||
display: flex;
|
||
gap: 10px;
|
||
background: rgba(255, 255, 255, 0.95);
|
||
border-radius: 8px;
|
||
padding: 10px;
|
||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
||
backdrop-filter: blur(10px);
|
||
}
|
||
|
||
.search-input {
|
||
flex: 1;
|
||
border: 1px solid #e4e7ed;
|
||
border-radius: 6px;
|
||
padding: 8px 12px;
|
||
font-size: 14px;
|
||
outline: none;
|
||
transition: border-color 0.3s ease;
|
||
}
|
||
|
||
.search-input:focus {
|
||
border-color: #409eff;
|
||
}
|
||
|
||
.search-btn {
|
||
padding: 8px 16px;
|
||
background: #409eff;
|
||
color: white;
|
||
border: none;
|
||
border-radius: 6px;
|
||
cursor: pointer;
|
||
font-size: 14px;
|
||
transition: background 0.3s ease;
|
||
}
|
||
|
||
.search-btn:hover {
|
||
background: #66b1ff;
|
||
}
|
||
|
||
/* 响应式设计 */
|
||
@media (max-width: 768px) {
|
||
.delivery-map {
|
||
height: 400px;
|
||
}
|
||
|
||
.map-toolbar {
|
||
top: 10px;
|
||
left: 10px;
|
||
}
|
||
|
||
.drawing-tips {
|
||
top: 10px;
|
||
right: 10px;
|
||
max-width: 200px;
|
||
}
|
||
|
||
.area-list {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
|
||
.search-controls {
|
||
bottom: 10px;
|
||
left: 10px;
|
||
right: 10px;
|
||
}
|
||
}
|
||
</style>
|
||
|
||
<div id="storeManager">
|
||
<el-form :model="ruleForm" :rules="rules" ref="ruleForm" label-width="120px" size="small">
|
||
|
||
<!-- 基本设置 -->
|
||
<el-form-item label="门店状态">
|
||
<el-switch v-model="ruleForm.status" active-value="1" inactive-value="0">
|
||
</el-switch>
|
||
<span class="tips">关闭后,小程序商城将不显示该门店</span>
|
||
</el-form-item>
|
||
|
||
<el-form-item label="送货上门" prop="skip">
|
||
<el-switch v-model="ruleForm.skip" active-value="0" inactive-value="1">
|
||
</el-switch>
|
||
<span class="tips">开启后,用户可选择送货上门服务</span>
|
||
</el-form-item>
|
||
|
||
<el-form-item label="到店自提" prop="self_pick">
|
||
<el-switch v-model="ruleForm.self_pick" active-value="1" inactive-value="0">
|
||
</el-switch>
|
||
<span class="tips">开启后,用户可选择到店自提</span>
|
||
</el-form-item>
|
||
|
||
<!-- 可视化配送区域设置 -->
|
||
<el-form-item label="配送区域设置" prop="delivery_fences">
|
||
<div class="delivery-area-container">
|
||
<!-- 地图头部 -->
|
||
<div class="delivery-map-header">
|
||
<div>
|
||
<h3 class="delivery-map-title">
|
||
<i class="el-icon-location"></i>
|
||
可视化配送区域设置
|
||
</h3>
|
||
<div class="delivery-map-status">
|
||
<span v-if="!mapReady">地图加载中...</span>
|
||
<span v-else-if="isDrawing">绘制模式 - 点击地图添加区域边界点</span>
|
||
<span v-else>已设置 {{deliveryFences.length}} 个配送区域</span>
|
||
</div>
|
||
</div>
|
||
<div>
|
||
<el-button size="mini" type="primary" plain @click="toggleFullscreen">
|
||
<i class="el-icon-full-screen"></i>
|
||
</el-button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 地图容器 -->
|
||
<div class="delivery-map-wrapper">
|
||
<div id="tencentMap" class="delivery-map"></div>
|
||
|
||
<!-- 地图工具栏 -->
|
||
<div class="map-toolbar">
|
||
<!-- 绘制工具组 -->
|
||
<div class="map-tool-group">
|
||
<div
|
||
class="map-tool-btn"
|
||
:class="{active: isDrawing}"
|
||
@click.stop.prevent="handleDrawingToggle"
|
||
title="绘制配送区域">
|
||
<i class="el-icon-edit"></i>
|
||
</div>
|
||
<div
|
||
class="map-tool-btn"
|
||
@click.stop.prevent="handleFinishDrawing"
|
||
v-if="isDrawing && currentPath.length >= 3"
|
||
title="完成当前区域绘制">
|
||
<i class="el-icon-check"></i>
|
||
</div>
|
||
<div
|
||
class="map-tool-btn danger"
|
||
@click.stop.prevent="handleCancelDrawing"
|
||
v-if="isDrawing"
|
||
title="取消绘制">
|
||
<i class="el-icon-close"></i>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 视图工具组 -->
|
||
<div class="map-tool-group">
|
||
<div
|
||
class="map-tool-btn"
|
||
@click.stop.prevent="centerToStore"
|
||
title="定位到门店">
|
||
<i class="el-icon-position"></i>
|
||
</div>
|
||
<div
|
||
class="map-tool-btn"
|
||
@click.stop.prevent="fitAllAreas"
|
||
v-if="deliveryFences.length > 0"
|
||
title="查看所有区域">
|
||
<i class="el-icon-zoom-in"></i>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 管理工具组 -->
|
||
<div class="map-tool-group" v-if="deliveryFences.length > 0">
|
||
<div
|
||
class="map-tool-btn"
|
||
@click.stop.prevent="exportFences"
|
||
title="导出配送区域">
|
||
<i class="el-icon-download"></i>
|
||
</div>
|
||
<div
|
||
class="map-tool-btn danger"
|
||
@click.stop.prevent="clearAllFences"
|
||
title="清空所有区域">
|
||
<i class="el-icon-delete"></i>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 导入工具 -->
|
||
<div class="map-tool-group">
|
||
<div
|
||
class="map-tool-btn"
|
||
@click.stop.prevent="importFences"
|
||
title="导入配送区域">
|
||
<i class="el-icon-upload2"></i>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 绘制提示 -->
|
||
<div class="drawing-tips" :class="{hidden: !isDrawing}">
|
||
<h4 class="tips-title">
|
||
<i class="el-icon-info"></i>
|
||
绘制提示
|
||
</h4>
|
||
<p class="tips-content">
|
||
• 点击地图添加区域边界点<br>
|
||
• 至少需要3个点形成区域<br>
|
||
• 双击地图完成绘制<br>
|
||
• 当前已添加 {{currentPath.length}} 个点
|
||
</p>
|
||
</div>
|
||
|
||
<!-- 地址搜索 -->
|
||
<div class="search-controls">
|
||
<div class="search-input-wrapper">
|
||
<input
|
||
type="text"
|
||
class="search-input"
|
||
v-model="searchAddress"
|
||
placeholder="输入地址快速定位..."
|
||
@keyup.enter="searchLocation">
|
||
<div class="search-btn" @click.stop.prevent="searchLocation">
|
||
<i class="el-icon-search"></i> 搜索
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 区域管理面板 -->
|
||
<div class="area-management">
|
||
<div class="area-list-header">
|
||
<h4 class="area-list-title">配送区域管理</h4>
|
||
<div class="area-stats">
|
||
总计 {{deliveryFences.length}} 个区域
|
||
<span v-if="totalArea > 0">,覆盖面积约 {{totalArea.toFixed(2)}} 平方公里</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 区域列表 -->
|
||
<div class="area-list" v-if="deliveryFences.length > 0">
|
||
<div
|
||
v-for="(fence, index) in deliveryFences"
|
||
:key="fence.id"
|
||
class="area-item"
|
||
:class="{active: selectedAreaIndex === index}"
|
||
@click="selectArea(index)">
|
||
|
||
<div class="area-item-header">
|
||
<h5 class="area-name">{{fence.name || `配送区域 ${index + 1}`}}</h5>
|
||
<div class="area-actions">
|
||
<button
|
||
type="button"
|
||
class="area-action-btn"
|
||
@click.stop.prevent="locateFence(index)"
|
||
title="定位到此区域">
|
||
<i class="el-icon-location-outline"></i>
|
||
</button>
|
||
<button
|
||
type="button"
|
||
class="area-action-btn"
|
||
@click.stop.prevent="editAreaName(index)"
|
||
title="编辑区域名称">
|
||
<i class="el-icon-edit-outline"></i>
|
||
</button>
|
||
<button
|
||
type="button"
|
||
class="area-action-btn danger"
|
||
@click.stop.prevent="deleteFence(index)"
|
||
title="删除此区域">
|
||
<i class="el-icon-delete"></i>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="area-info">
|
||
<div class="area-info-item">
|
||
<i class="el-icon-s-grid area-info-icon"></i>
|
||
<span>面积: {{fence.area || '计算中...'}} km²</span>
|
||
</div>
|
||
<div class="area-info-item">
|
||
<i class="el-icon-connection area-info-icon"></i>
|
||
<span>边界点: {{fence.points ? fence.points.length : 0}} 个</span>
|
||
</div>
|
||
<div class="area-info-item">
|
||
<i class="el-icon-time area-info-icon"></i>
|
||
<span>创建: {{formatDate(fence.createTime)}}</span>
|
||
</div>
|
||
<div class="area-info-item">
|
||
<i class="el-icon-circle-check area-info-icon"></i>
|
||
<span>状态:
|
||
<span style="color: #67c23a;">已启用</span>
|
||
</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 空状态 -->
|
||
<div class="empty-area-list" v-else>
|
||
<div class="empty-area-icon">
|
||
<i class="el-icon-location-outline"></i>
|
||
</div>
|
||
<h4>暂无配送区域</h4>
|
||
<p>点击地图工具栏中的绘制按钮开始创建配送区域</p>
|
||
<el-button type="primary" size="small" @click="handleStartDrawing">
|
||
<i class="el-icon-plus"></i> 创建第一个配送区域
|
||
</el-button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</el-form-item>
|
||
|
||
<!-- 其他设置 -->
|
||
<el-form-item label="门店地址" prop="address">
|
||
<el-input v-model="ruleForm.address" placeholder="请输入门店详细地址">
|
||
<el-button slot="append" @click="searchAddress">定位</el-button>
|
||
</el-input>
|
||
<span class="tips">用于地图定位和配送区域设置</span>
|
||
</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="smmoney">
|
||
<el-input class="mini-input" v-model="ruleForm.smmoney">
|
||
<template slot="append">元</template>
|
||
</el-input>
|
||
</el-form-item>
|
||
|
||
<!-- 提交按钮 -->
|
||
<el-form-item>
|
||
<el-button type="primary" @click="submitForm('ruleForm')" :loading="pageLoading">保存设置</el-button>
|
||
<el-button @click="resetForm('ruleForm')">重置</el-button>
|
||
</el-form-item>
|
||
</el-form>
|
||
</div>
|
||
|
||
<!-- 导入对话框 -->
|
||
<el-dialog title="导入配送区域JSON" :visible.sync="importDialogVisible" width="600px">
|
||
<el-input type="textarea" :rows="10" v-model="importText" placeholder="粘贴之前导出的配送区域JSON"></el-input>
|
||
<span slot="footer" class="dialog-footer">
|
||
<el-button @click="importDialogVisible = false">取 消</el-button>
|
||
<el-button type="primary" @click="confirmImport">确 定</el-button>
|
||
</span>
|
||
</el-dialog>
|
||
|
||
<script>
|
||
var storeManager = new Vue({
|
||
el: '#storeManager',
|
||
data() {
|
||
return {
|
||
pageLoading: false,
|
||
|
||
// 地图相关
|
||
map: null,
|
||
marker: null,
|
||
mapReady: false,
|
||
isDrawing: false,
|
||
currentPolygon: null,
|
||
currentPath: [],
|
||
drawingLine: null,
|
||
deliveryFences: [],
|
||
polygons: [],
|
||
importDialogVisible: false,
|
||
importText: '',
|
||
|
||
// 可视化功能属性
|
||
selectedAreaIndex: null,
|
||
searchAddress: '',
|
||
isFullscreen: false,
|
||
|
||
ruleForm: {
|
||
id: 0,
|
||
status: "0",
|
||
skip: "1",
|
||
self_pick: "1",
|
||
address: "",
|
||
longitude: 116.404,
|
||
latitude: 39.915,
|
||
tip: "",
|
||
phone: "",
|
||
smmoney: "",
|
||
delivery_fences: "",
|
||
},
|
||
|
||
rules: {
|
||
address: [
|
||
{ required: true, message: '请输入门店地址', trigger: 'blur' }
|
||
],
|
||
phone: [
|
||
{ required: true, message: '请输入门店电话', trigger: 'blur' },
|
||
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号码', trigger: 'blur' }
|
||
]
|
||
}
|
||
}
|
||
},
|
||
|
||
computed: {
|
||
// 计算总覆盖面积
|
||
totalArea() {
|
||
return this.deliveryFences.reduce((total, fence) => {
|
||
const area = parseFloat(fence.area) || 0;
|
||
return total + area;
|
||
}, 0);
|
||
}
|
||
},
|
||
|
||
mounted() {
|
||
this.waitForTMapLoad();
|
||
this.loadStoreData();
|
||
},
|
||
|
||
methods: {
|
||
// 地图初始化
|
||
waitForTMapLoad() {
|
||
let attempts = 0;
|
||
const maxAttempts = 40;
|
||
|
||
const checkTMap = () => {
|
||
attempts++;
|
||
console.log(`检查地图加载状态 (${attempts}/${maxAttempts})`);
|
||
|
||
if (window.mapLoadStatus.error) {
|
||
console.error('地图API加载失败');
|
||
this.showMapError();
|
||
return;
|
||
}
|
||
|
||
if (typeof TMap !== 'undefined') {
|
||
console.log('TMap已定义,开始初始化地图');
|
||
this.initMap();
|
||
return;
|
||
}
|
||
|
||
if (attempts < maxAttempts) {
|
||
setTimeout(checkTMap, 500);
|
||
} else {
|
||
console.error('地图API加载超时');
|
||
this.showMapError();
|
||
}
|
||
};
|
||
|
||
checkTMap();
|
||
},
|
||
|
||
initMap() {
|
||
try {
|
||
const mapContainer = document.getElementById('tencentMap');
|
||
if (!mapContainer) {
|
||
this.$message.error('地图容器未找到');
|
||
return;
|
||
}
|
||
|
||
mapContainer.innerHTML = '';
|
||
|
||
const center = new TMap.LatLng(this.ruleForm.latitude, this.ruleForm.longitude);
|
||
|
||
this.map = new TMap.Map(mapContainer, {
|
||
center: center,
|
||
zoom: 13,
|
||
viewMode: '2D'
|
||
});
|
||
|
||
this.map.on('tilesloaded', () => {
|
||
this.onMapReady();
|
||
});
|
||
|
||
this.map.on('click', (evt) => {
|
||
try {
|
||
if (this.isDrawing && evt && evt.latLng) {
|
||
this.addDrawingPoint(evt.latLng);
|
||
}
|
||
} catch (error) {
|
||
console.error('地图点击处理失败:', error);
|
||
}
|
||
});
|
||
|
||
this.map.on('dblclick', (evt) => {
|
||
try {
|
||
if (this.isDrawing && this.currentPath.length >= 3) {
|
||
evt.preventDefault && evt.preventDefault();
|
||
this.finishDrawing();
|
||
}
|
||
} catch (error) {
|
||
console.error('地图双击处理失败:', error);
|
||
}
|
||
});
|
||
|
||
setTimeout(() => {
|
||
if (this.map && !this.mapReady) {
|
||
this.onMapReady();
|
||
}
|
||
}, 5000);
|
||
|
||
} catch (error) {
|
||
console.error('地图初始化失败:', error);
|
||
this.showMapError();
|
||
}
|
||
},
|
||
|
||
onMapReady() {
|
||
if (this.mapReady) return;
|
||
this.mapReady = true;
|
||
|
||
this.createStoreMarker();
|
||
this.loadExistingFences();
|
||
this.$message.success('地图加载成功');
|
||
},
|
||
|
||
showMapError() {
|
||
const mapContainer = document.getElementById('tencentMap');
|
||
if (mapContainer) {
|
||
mapContainer.innerHTML = `
|
||
<div style="display: flex; align-items: center; justify-content: center; height: 100%; background: #f5f5f5; color: #666; flex-direction: column; border: 1px solid #ddd; border-radius: 4px;">
|
||
<i class="el-icon-warning" style="font-size: 48px; margin-bottom: 15px; color: #f56c6c;"></i>
|
||
<h4 style="margin: 0 0 10px 0;">地图加载失败</h4>
|
||
<p style="margin: 5px 0; font-size: 14px;">请检查网络连接或稍后重试</p>
|
||
<div style="margin-top: 15px;">
|
||
<button onclick="location.reload()" style="margin-right: 10px; padding: 8px 16px; background: #409eff; color: white; border: none; border-radius: 4px; cursor: pointer;">刷新页面</button>
|
||
<button onclick="storeManager.retryMapLoad()" style="padding: 8px 16px; background: #67c23a; color: white; border: none; border-radius: 4px; cursor: pointer;">重试加载</button>
|
||
</div>
|
||
</div>
|
||
`;
|
||
}
|
||
this.$message.error('地图加载失败,请检查网络连接');
|
||
},
|
||
|
||
retryMapLoad() {
|
||
this.mapReady = false;
|
||
this.waitForTMapLoad();
|
||
},
|
||
|
||
// 门店标记
|
||
createStoreMarker() {
|
||
try {
|
||
if (this.marker) {
|
||
this.marker.setMap(null);
|
||
this.marker = null;
|
||
}
|
||
|
||
const position = new TMap.LatLng(this.ruleForm.latitude, this.ruleForm.longitude);
|
||
|
||
if (TMap.Marker) {
|
||
this.marker = new TMap.Marker({
|
||
map: this.map,
|
||
position: position,
|
||
title: '门店位置'
|
||
});
|
||
} else if (TMap.MultiMarker) {
|
||
this.marker = new TMap.MultiMarker({
|
||
map: this.map,
|
||
styles: {
|
||
'marker': new TMap.MarkerStyle({
|
||
'width': 25,
|
||
'height': 35,
|
||
'anchor': { x: 16, y: 32 }
|
||
})
|
||
},
|
||
geometries: [{
|
||
id: 'store-marker',
|
||
styleId: 'marker',
|
||
position: position,
|
||
properties: { title: '门店位置' }
|
||
}]
|
||
});
|
||
}
|
||
} catch (error) {
|
||
console.error('创建门店标记失败:', error);
|
||
}
|
||
},
|
||
|
||
// 可视化配送区域方法
|
||
handleDrawingToggle() {
|
||
console.log('点击绘制按钮,当前状态:', this.isDrawing);
|
||
try {
|
||
if (this.isDrawing) {
|
||
this.handleCancelDrawing();
|
||
} else {
|
||
this.handleStartDrawing();
|
||
}
|
||
} catch (error) {
|
||
console.error('绘制切换失败:', error);
|
||
}
|
||
return false; // 确保不会导致页面刷新
|
||
},
|
||
|
||
toggleDrawing(event) {
|
||
if (event) {
|
||
event.preventDefault();
|
||
event.stopPropagation();
|
||
}
|
||
|
||
if (this.isDrawing) {
|
||
this.cancelDrawing();
|
||
} else {
|
||
this.startDrawing();
|
||
}
|
||
},
|
||
|
||
handleStartDrawing() {
|
||
console.log('开始绘制');
|
||
if (!this.mapReady || !this.map) {
|
||
this.$message.error('地图未准备就绪,请稍后再试');
|
||
return;
|
||
}
|
||
|
||
this.resetDrawingState();
|
||
this.isDrawing = true;
|
||
this.selectedAreaIndex = null;
|
||
this.$message.info('请在地图上点击多个点来绘制配送区域,至少需要3个点');
|
||
},
|
||
|
||
handleFinishDrawing() {
|
||
console.log('完成绘制');
|
||
if (this.currentPath.length < 3) {
|
||
this.$message.warning('至少需要3个点才能形成配送区域');
|
||
return;
|
||
}
|
||
|
||
try {
|
||
const closedPath = this.ensureClosedPath([...this.currentPath]);
|
||
const area = this.calculatePolygonArea(closedPath);
|
||
|
||
const newFence = {
|
||
id: Date.now(),
|
||
name: `配送区域${this.deliveryFences.length + 1}`,
|
||
points: closedPath,
|
||
area: area.toFixed(2),
|
||
createTime: Date.now(),
|
||
status: 'active'
|
||
};
|
||
|
||
this.deliveryFences.push(newFence);
|
||
this.createFencePolygon(newFence, this.deliveryFences.length - 1);
|
||
this.resetDrawingState();
|
||
|
||
this.selectedAreaIndex = this.deliveryFences.length - 1;
|
||
this.$message.success(`配送区域 "${newFence.name}" 创建完成`);
|
||
this.updateDeliveryFencesData();
|
||
} catch (error) {
|
||
console.error('完成绘制失败:', error);
|
||
this.$message.error('创建配送区域失败,请重试');
|
||
}
|
||
},
|
||
|
||
handleCancelDrawing() {
|
||
console.log('取消绘制');
|
||
this.isDrawing = false;
|
||
this.resetDrawingState();
|
||
this.$message.info('已取消绘制');
|
||
},
|
||
|
||
startDrawing(event) {
|
||
if (event) {
|
||
event.preventDefault();
|
||
event.stopPropagation();
|
||
}
|
||
|
||
if (!this.mapReady || !this.map) {
|
||
this.$message.error('地图未准备就绪,请稍后再试');
|
||
return false;
|
||
}
|
||
|
||
this.resetDrawingState();
|
||
this.isDrawing = true;
|
||
this.selectedAreaIndex = null;
|
||
this.$message.info('请在地图上点击多个点来绘制配送区域,至少需要3个点');
|
||
return false;
|
||
},
|
||
|
||
resetDrawingState() {
|
||
this.currentPath = [];
|
||
this.clearCurrentDrawing();
|
||
},
|
||
|
||
clearCurrentDrawing() {
|
||
if (this.currentPolygon) {
|
||
this.currentPolygon.setMap(null);
|
||
this.currentPolygon = null;
|
||
}
|
||
if (this.drawingLine) {
|
||
this.drawingLine.setMap(null);
|
||
this.drawingLine = null;
|
||
}
|
||
},
|
||
|
||
addDrawingPoint(latLng) {
|
||
this.currentPath.push({
|
||
lat: latLng.lat,
|
||
lng: latLng.lng
|
||
});
|
||
|
||
this.updateDrawingDisplay();
|
||
},
|
||
|
||
updateDrawingDisplay() {
|
||
if (this.currentPath.length < 2) return;
|
||
|
||
this.clearCurrentDrawing();
|
||
|
||
const latLngArray = this.currentPath.map(point => new TMap.LatLng(point.lat, point.lng));
|
||
|
||
try {
|
||
if (TMap.Polyline) {
|
||
this.drawingLine = new TMap.Polyline({
|
||
map: this.map,
|
||
path: latLngArray,
|
||
strokeColor: '#409eff',
|
||
strokeWeight: 3,
|
||
strokeDashArray: [10, 10]
|
||
});
|
||
}
|
||
} catch (error) {
|
||
console.error('更新绘制显示失败:', error);
|
||
}
|
||
},
|
||
|
||
finishDrawing(event) {
|
||
if (event) {
|
||
event.preventDefault();
|
||
event.stopPropagation();
|
||
}
|
||
|
||
if (this.currentPath.length < 3) {
|
||
this.$message.warning('至少需要3个点才能形成配送区域');
|
||
return false;
|
||
}
|
||
|
||
try {
|
||
const closedPath = this.ensureClosedPath([...this.currentPath]);
|
||
const area = this.calculatePolygonArea(closedPath);
|
||
|
||
const newFence = {
|
||
id: Date.now(),
|
||
name: `配送区域${this.deliveryFences.length + 1}`,
|
||
points: closedPath,
|
||
area: area.toFixed(2),
|
||
createTime: Date.now(),
|
||
status: 'active'
|
||
};
|
||
|
||
this.deliveryFences.push(newFence);
|
||
this.createFencePolygon(newFence, this.deliveryFences.length - 1);
|
||
this.resetDrawingState();
|
||
|
||
// 自动选中新创建的区域
|
||
this.selectedAreaIndex = this.deliveryFences.length - 1;
|
||
|
||
this.$message.success(`配送区域 "${newFence.name}" 创建完成`);
|
||
this.updateDeliveryFencesData();
|
||
} catch (error) {
|
||
console.error('完成绘制失败:', error);
|
||
this.$message.error('创建配送区域失败,请重试');
|
||
}
|
||
|
||
return false;
|
||
},
|
||
|
||
cancelDrawing(event) {
|
||
if (event) {
|
||
event.preventDefault();
|
||
event.stopPropagation();
|
||
}
|
||
|
||
this.isDrawing = false;
|
||
this.resetDrawingState();
|
||
this.$message.info('已取消绘制');
|
||
return false;
|
||
},
|
||
|
||
ensureClosedPath(points) {
|
||
const path = [...points];
|
||
const firstPoint = path[0];
|
||
const lastPoint = path[path.length - 1];
|
||
|
||
if (firstPoint.lat !== lastPoint.lat || firstPoint.lng !== lastPoint.lng) {
|
||
path.push({...firstPoint});
|
||
}
|
||
|
||
return path;
|
||
},
|
||
|
||
calculatePolygonArea(points) {
|
||
if (points.length < 3) return 0;
|
||
|
||
let area = 0;
|
||
const earthRadius = 6371000; // 地球半径(米)
|
||
|
||
for (let i = 0; i < points.length - 1; i++) {
|
||
const p1 = points[i];
|
||
const p2 = points[i + 1];
|
||
|
||
const lat1 = p1.lat * Math.PI / 180;
|
||
const lat2 = p2.lat * Math.PI / 180;
|
||
const lng1 = p1.lng * Math.PI / 180;
|
||
const lng2 = p2.lng * Math.PI / 180;
|
||
|
||
area += (lng2 - lng1) * (2 + Math.sin(lat1) + Math.sin(lat2));
|
||
}
|
||
|
||
area = Math.abs(area) * earthRadius * earthRadius / 2;
|
||
return area / 1000000; // 转换为平方公里
|
||
},
|
||
|
||
createFencePolygon(fence, index) {
|
||
try {
|
||
if (!fence || !fence.points || !Array.isArray(fence.points) || fence.points.length < 3) {
|
||
console.error(`围栏${index + 1}数据无效`);
|
||
return;
|
||
}
|
||
|
||
const validPoints = this.validateFencePoints(fence.points);
|
||
if (validPoints.length < 3) {
|
||
console.error(`围栏${index + 1}有效点数不足`);
|
||
return;
|
||
}
|
||
|
||
const closedPath = this.ensureClosedPath(validPoints);
|
||
const latLngArray = closedPath.map(point => new TMap.LatLng(point.lat, point.lng));
|
||
|
||
let polygon = null;
|
||
|
||
if (TMap.Polygon) {
|
||
polygon = new TMap.Polygon({
|
||
map: this.map,
|
||
paths: [latLngArray],
|
||
fillColor: 'rgba(16, 185, 129, 0.3)',
|
||
strokeColor: '#10b981',
|
||
strokeWeight: 2,
|
||
fillOpacity: 0.3
|
||
});
|
||
} else if (TMap.Polyline) {
|
||
polygon = new TMap.Polyline({
|
||
map: this.map,
|
||
path: latLngArray,
|
||
strokeColor: '#10b981',
|
||
strokeWeight: 3
|
||
});
|
||
}
|
||
|
||
if (polygon) {
|
||
this.polygons.push(polygon);
|
||
}
|
||
} catch (error) {
|
||
console.error(`创建围栏${index + 1}失败:`, error);
|
||
}
|
||
},
|
||
|
||
validateFencePoints(points) {
|
||
return points.filter(point =>
|
||
point &&
|
||
typeof point.lat === 'number' &&
|
||
typeof point.lng === 'number' &&
|
||
!isNaN(point.lat) && !isNaN(point.lng) &&
|
||
point.lat >= -90 && point.lat <= 90 &&
|
||
point.lng >= -180 && point.lng <= 180
|
||
);
|
||
},
|
||
|
||
// 可视化功能方法
|
||
toggleFullscreen() {
|
||
const container = document.querySelector('.delivery-area-container');
|
||
if (!document.fullscreenElement) {
|
||
container.requestFullscreen().catch(err => {
|
||
console.log('无法进入全屏模式:', err);
|
||
});
|
||
} else {
|
||
document.exitFullscreen();
|
||
}
|
||
},
|
||
|
||
centerToStore() {
|
||
if (this.map && this.ruleForm.latitude && this.ruleForm.longitude) {
|
||
const center = new TMap.LatLng(this.ruleForm.latitude, this.ruleForm.longitude);
|
||
this.map.setCenter(center);
|
||
this.map.setZoom(15);
|
||
this.$message.success('已定位到门店位置');
|
||
}
|
||
},
|
||
|
||
fitAllAreas() {
|
||
if (!this.map || this.deliveryFences.length === 0) return;
|
||
|
||
try {
|
||
let bounds = new TMap.LatLngBounds();
|
||
|
||
this.deliveryFences.forEach(fence => {
|
||
if (fence.points && fence.points.length > 0) {
|
||
fence.points.forEach(point => {
|
||
bounds.extend(new TMap.LatLng(point.lat, point.lng));
|
||
});
|
||
}
|
||
});
|
||
|
||
this.map.fitBounds(bounds, {
|
||
padding: 50
|
||
});
|
||
|
||
this.$message.success('已显示所有配送区域');
|
||
} catch (error) {
|
||
console.error('适配区域视图失败:', error);
|
||
}
|
||
},
|
||
|
||
selectArea(index) {
|
||
this.selectedAreaIndex = this.selectedAreaIndex === index ? null : index;
|
||
if (this.selectedAreaIndex !== null) {
|
||
this.locateFence(index);
|
||
}
|
||
},
|
||
|
||
locateFence(index) {
|
||
const fence = this.deliveryFences[index];
|
||
if (!fence || !fence.points || fence.points.length === 0) return;
|
||
|
||
try {
|
||
let bounds = new TMap.LatLngBounds();
|
||
fence.points.forEach(point => {
|
||
bounds.extend(new TMap.LatLng(point.lat, point.lng));
|
||
});
|
||
|
||
this.map.fitBounds(bounds, {
|
||
padding: 100
|
||
});
|
||
|
||
this.selectedAreaIndex = index;
|
||
this.$message.success(`已定位到配送区域 ${index + 1}`);
|
||
} catch (error) {
|
||
console.error('定位区域失败:', error);
|
||
}
|
||
},
|
||
|
||
editAreaName(index) {
|
||
const fence = this.deliveryFences[index];
|
||
const currentName = fence.name || `配送区域 ${index + 1}`;
|
||
|
||
this.$prompt('请输入新的区域名称', '编辑区域名称', {
|
||
confirmButtonText: '确定',
|
||
cancelButtonText: '取消',
|
||
inputValue: currentName,
|
||
inputValidator: (value) => {
|
||
if (!value || value.trim().length === 0) {
|
||
return '区域名称不能为空';
|
||
}
|
||
if (value.length > 20) {
|
||
return '区域名称不能超过20个字符';
|
||
}
|
||
return true;
|
||
}
|
||
}).then(({ value }) => {
|
||
this.deliveryFences[index].name = value.trim();
|
||
this.updateDeliveryFencesData();
|
||
this.$message.success('区域名称已更新');
|
||
}).catch(() => {});
|
||
},
|
||
|
||
deleteFence(index) {
|
||
this.$confirm('确定要删除这个配送区域吗?', '提示', {
|
||
confirmButtonText: '确定',
|
||
cancelButtonText: '取消',
|
||
type: 'warning'
|
||
}).then(() => {
|
||
// 删除地图上的多边形
|
||
if (this.polygons[index]) {
|
||
this.polygons[index].setMap(null);
|
||
this.polygons.splice(index, 1);
|
||
}
|
||
|
||
this.deliveryFences.splice(index, 1);
|
||
this.updateDeliveryFencesData();
|
||
this.selectedAreaIndex = null;
|
||
this.$message.success('配送区域已删除');
|
||
}).catch(() => {});
|
||
},
|
||
|
||
clearAllFences() {
|
||
this.$confirm('确定要清空所有配送区域吗?', '提示', {
|
||
confirmButtonText: '确定',
|
||
cancelButtonText: '取消',
|
||
type: 'warning'
|
||
}).then(() => {
|
||
this.clearExistingPolygons();
|
||
this.deliveryFences = [];
|
||
this.selectedAreaIndex = null;
|
||
this.updateDeliveryFencesData();
|
||
this.$message.success('已清空所有配送区域');
|
||
}).catch(() => {});
|
||
},
|
||
|
||
clearExistingPolygons() {
|
||
this.polygons.forEach(polygon => {
|
||
if (polygon && polygon.setMap) {
|
||
polygon.setMap(null);
|
||
}
|
||
});
|
||
this.polygons = [];
|
||
},
|
||
|
||
searchLocation() {
|
||
if (!this.searchAddress || !this.searchAddress.trim()) {
|
||
this.$message.warning('请输入搜索地址');
|
||
return;
|
||
}
|
||
|
||
if (!this.map || typeof TMap === 'undefined' || !TMap.service || !TMap.service.Geocoder) {
|
||
this.$message.error('地图服务未加载');
|
||
return;
|
||
}
|
||
|
||
const geocoder = new TMap.service.Geocoder();
|
||
geocoder.getLocation({
|
||
address: this.searchAddress.trim()
|
||
}).then((result) => {
|
||
if (result.result && result.result.location) {
|
||
const location = result.result.location;
|
||
const newCenter = new TMap.LatLng(location.lat, location.lng);
|
||
this.map.setCenter(newCenter);
|
||
this.map.setZoom(16);
|
||
this.$message.success('地址定位成功');
|
||
} else {
|
||
this.$message.error('未找到该地址,请检查地址是否正确');
|
||
}
|
||
}).catch((error) => {
|
||
this.$message.error('地址搜索失败');
|
||
console.error('地址搜索错误:', error);
|
||
});
|
||
},
|
||
|
||
searchAddress() {
|
||
if (!this.ruleForm.address) {
|
||
this.$message.warning('请输入地址');
|
||
return;
|
||
}
|
||
|
||
if (!this.map || typeof TMap === 'undefined' || !TMap.service || !TMap.service.Geocoder) {
|
||
this.$message.error('地图服务未加载');
|
||
return;
|
||
}
|
||
|
||
const geocoder = new TMap.service.Geocoder();
|
||
geocoder.getLocation({
|
||
address: this.ruleForm.address
|
||
}).then((result) => {
|
||
if (result.result && result.result.location) {
|
||
const location = result.result.location;
|
||
this.ruleForm.longitude = location.lng;
|
||
this.ruleForm.latitude = location.lat;
|
||
|
||
const newCenter = new TMap.LatLng(location.lat, location.lng);
|
||
this.map.setCenter(newCenter);
|
||
this.createStoreMarker();
|
||
|
||
this.$message.success('地址定位成功');
|
||
} else {
|
||
this.$message.error('地址解析失败,请检查地址是否正确');
|
||
}
|
||
}).catch((error) => {
|
||
this.$message.error('地址搜索失败');
|
||
console.error('地址搜索错误:', error);
|
||
});
|
||
},
|
||
|
||
formatDate(timestamp) {
|
||
if (!timestamp) return '未知';
|
||
const date = new Date(timestamp);
|
||
return date.toLocaleDateString('zh-CN', {
|
||
month: '2-digit',
|
||
day: '2-digit',
|
||
hour: '2-digit',
|
||
minute: '2-digit'
|
||
});
|
||
},
|
||
|
||
// 数据管理
|
||
updateDeliveryFencesData() {
|
||
this.ruleForm.delivery_fences = JSON.stringify(this.deliveryFences);
|
||
},
|
||
|
||
loadExistingFences() {
|
||
if (!this.ruleForm.delivery_fences) {
|
||
return;
|
||
}
|
||
|
||
try {
|
||
this.clearExistingPolygons();
|
||
|
||
const fencesData = JSON.parse(this.ruleForm.delivery_fences);
|
||
|
||
if (Array.isArray(fencesData) && fencesData.length > 0) {
|
||
this.deliveryFences = fencesData;
|
||
|
||
setTimeout(() => {
|
||
this.deliveryFences.forEach((fence, index) => {
|
||
this.createFencePolygon(fence, index);
|
||
});
|
||
|
||
if (this.deliveryFences.length > 0) {
|
||
this.$message.success(`已加载${this.deliveryFences.length}个配送区域`);
|
||
}
|
||
}, 500);
|
||
} else {
|
||
this.deliveryFences = [];
|
||
}
|
||
} catch (error) {
|
||
console.error('解析围栏数据失败:', error);
|
||
this.deliveryFences = [];
|
||
this.ruleForm.delivery_fences = '';
|
||
this.$message.warning('围栏数据格式错误,已清空');
|
||
}
|
||
},
|
||
|
||
// 导入导出
|
||
exportFences() {
|
||
if (this.deliveryFences.length === 0) {
|
||
this.$message.warning('暂无配送区域可导出');
|
||
return;
|
||
}
|
||
|
||
try {
|
||
const exportData = {
|
||
version: '1.0',
|
||
exportTime: new Date().toISOString(),
|
||
fences: this.deliveryFences
|
||
};
|
||
|
||
const text = JSON.stringify(exportData, null, 2);
|
||
|
||
if (navigator.clipboard) {
|
||
navigator.clipboard.writeText(text).then(() => {
|
||
this.$message.success('配送区域JSON已复制到剪贴板');
|
||
}).catch(() => {
|
||
this.downloadAsFile(text, 'delivery_fences.json');
|
||
});
|
||
} else {
|
||
this.downloadAsFile(text, 'delivery_fences.json');
|
||
}
|
||
} catch (error) {
|
||
this.$message.error('导出失败');
|
||
console.error('导出配送区域失败:', error);
|
||
}
|
||
},
|
||
|
||
downloadAsFile(content, filename) {
|
||
const blob = new Blob([content], { type: 'application/json' });
|
||
const url = URL.createObjectURL(blob);
|
||
const a = document.createElement('a');
|
||
a.href = url;
|
||
a.download = filename;
|
||
document.body.appendChild(a);
|
||
a.click();
|
||
document.body.removeChild(a);
|
||
URL.revokeObjectURL(url);
|
||
this.$message.success('配送区域JSON文件已下载');
|
||
},
|
||
|
||
importFences() {
|
||
this.importDialogVisible = true;
|
||
this.importText = '';
|
||
},
|
||
|
||
confirmImport() {
|
||
if (!this.importText.trim()) {
|
||
this.$message.warning('请输入要导入的JSON数据');
|
||
return;
|
||
}
|
||
|
||
try {
|
||
const data = JSON.parse(this.importText);
|
||
let fences = [];
|
||
|
||
if (data.fences && Array.isArray(data.fences)) {
|
||
fences = data.fences;
|
||
} else if (Array.isArray(data)) {
|
||
fences = data;
|
||
} else {
|
||
throw new Error('数据格式不正确');
|
||
}
|
||
|
||
if (fences.length === 0) {
|
||
this.$message.warning('导入的数据中没有有效的配送区域');
|
||
return;
|
||
}
|
||
|
||
this.clearExistingPolygons();
|
||
this.deliveryFences = fences;
|
||
|
||
setTimeout(() => {
|
||
this.deliveryFences.forEach((fence, index) => {
|
||
this.createFencePolygon(fence, index);
|
||
});
|
||
}, 100);
|
||
|
||
this.updateDeliveryFencesData();
|
||
this.importDialogVisible = false;
|
||
this.$message.success(`成功导入${fences.length}个配送区域`);
|
||
} catch (error) {
|
||
this.$message.error('导入失败:JSON解析错误');
|
||
console.error('导入配送区域失败:', error);
|
||
}
|
||
},
|
||
|
||
// 表单操作
|
||
loadStoreData() {
|
||
// 这里应该从服务器加载门店数据
|
||
// 示例数据
|
||
this.ruleForm = {
|
||
id: 1,
|
||
status: "1",
|
||
skip: "0",
|
||
self_pick: "1",
|
||
address: "北京市朝阳区三里屯",
|
||
longitude: 116.404,
|
||
latitude: 39.915,
|
||
tip: "欢迎光临我们的门店",
|
||
phone: "13800138000",
|
||
smmoney: "30",
|
||
delivery_fences: "",
|
||
};
|
||
},
|
||
|
||
submitForm(formName) {
|
||
this.$refs[formName].validate((valid) => {
|
||
if (valid) {
|
||
this.pageLoading = true;
|
||
|
||
// 这里应该提交到服务器
|
||
setTimeout(() => {
|
||
this.pageLoading = false;
|
||
this.$message.success('保存成功');
|
||
}, 1000);
|
||
} else {
|
||
this.$message.error('请检查表单填写');
|
||
return false;
|
||
}
|
||
});
|
||
},
|
||
|
||
resetForm(formName) {
|
||
this.$refs[formName].resetFields();
|
||
this.clearAllFences();
|
||
}
|
||
}
|
||
});
|
||
</script> |