|
|
@@ -4,9 +4,6 @@
|
|
|
<div v-show="showSearch" class="mb-[10px]">
|
|
|
<el-card shadow="hover">
|
|
|
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
|
|
|
- <el-form-item label="门店编号" prop="storeCode">
|
|
|
- <el-input v-model="queryParams.storeCode" placeholder="请输入门店编号" clearable @keyup.enter="handleQuery" />
|
|
|
- </el-form-item>
|
|
|
<el-form-item label="门店名称" prop="name">
|
|
|
<el-input v-model="queryParams.name" placeholder="请输入门店名称" clearable @keyup.enter="handleQuery" />
|
|
|
</el-form-item>
|
|
|
@@ -38,48 +35,30 @@
|
|
|
>删除</el-button
|
|
|
>
|
|
|
</el-col>
|
|
|
- <el-col :span="1.5">
|
|
|
- <el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['physical:store:export']">导出</el-button>
|
|
|
- </el-col>
|
|
|
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
|
|
</el-row>
|
|
|
</template>
|
|
|
|
|
|
<el-table v-loading="loading" border :data="storeList" @selection-change="handleSelectionChange">
|
|
|
<el-table-column type="selection" width="55" align="center" />
|
|
|
- <el-table-column label="编号" align="center" prop="id" v-if="true" />
|
|
|
- <el-table-column label="门店编号" align="center" prop="storeCode" />
|
|
|
- <el-table-column label="门店名称" align="center" prop="name" />
|
|
|
- <el-table-column label="门店类型" align="center" prop="storeTypeName" />
|
|
|
- <el-table-column label="所属赛区" align="center" prop="zoneId" />
|
|
|
- <el-table-column label="详细地址" align="center" prop="address" />
|
|
|
- <el-table-column label="门店图标" align="center" prop="iconUrl" width="120">
|
|
|
+ <el-table-column label="门店 ID" align="center" prop="id" v-if="true" />
|
|
|
+ <el-table-column label="主页图标" align="center" prop="iconUrl" width="120">
|
|
|
<template #default="scope">
|
|
|
<img v-if="scope.row.iconUrl" :src="scope.row.iconUrl" alt="" style="width: 40px; height: 40px; object-fit: cover; border-radius: 4px" />
|
|
|
<span v-else>无</span>
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
-
|
|
|
- <el-table-column label="门店背景图" align="center" prop="backgroundUrl" width="120">
|
|
|
- <template #default="scope">
|
|
|
- <img
|
|
|
- v-if="scope.row.backgroundUrl"
|
|
|
- :src="scope.row.backgroundUrl"
|
|
|
- alt=""
|
|
|
- style="width: 40px; height: 40px; object-fit: cover; border-radius: 4px"
|
|
|
- />
|
|
|
- <span v-else>无</span>
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- <el-table-column label="营业开始时间" align="center" prop="businessStartTime" width="180">
|
|
|
+ <el-table-column label="门店名称" align="center" prop="name" />
|
|
|
+ <el-table-column label="门店类型" align="center" prop="storeTypeName" />
|
|
|
+ <el-table-column label="详细地址" align="center" prop="address" />
|
|
|
+ <el-table-column label="兑换开始时间" align="center" prop="businessStartTime" width="180">
|
|
|
<template #default="scope">
|
|
|
- <span>{{ parseTime(scope.row.businessStartTime, '{h}:{i}:{s}') }}</span>
|
|
|
+ <span>{{ parseTime(scope.row.businessStartTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
-
|
|
|
- <el-table-column label="营业结束时间" align="center" prop="businessEndTime" width="180">
|
|
|
+ <el-table-column label="兑换结束时间" align="center" prop="businessEndTime" width="180">
|
|
|
<template #default="scope">
|
|
|
- <span>{{ parseTime(scope.row.businessEndTime, '{h}:{i}:{s}') }}</span>
|
|
|
+ <span>{{ parseTime(scope.row.businessEndTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
<el-table-column label="状态" align="center" prop="status">
|
|
|
@@ -104,29 +83,26 @@
|
|
|
|
|
|
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
|
|
|
</el-card>
|
|
|
+
|
|
|
<!-- 添加或修改门店信息对话框 -->
|
|
|
<el-dialog :title="dialog.title" v-model="dialog.visible" width="600px" append-to-body>
|
|
|
<el-form ref="storeFormRef" :model="form" :rules="rules" label-width="120px">
|
|
|
- <el-form-item label="门店编号" prop="storeCode">
|
|
|
- <el-input v-model="form.storeCode" placeholder="请输入门店编号" />
|
|
|
- </el-form-item>
|
|
|
<el-form-item label="门店名称" prop="name">
|
|
|
- <el-input v-model="form.name" placeholder="请输入门店名称,最多20字" />
|
|
|
- </el-form-item>
|
|
|
- <el-form-item label="门店类型" prop="storeTypeId">
|
|
|
- <el-select v-model="form.storeTypeId" placeholder="请选择门店类型" style="width: 100%">
|
|
|
- <el-option v-for="item in tagOptions" :key="item.id" :label="item.serviceName" :value="item.id" />
|
|
|
- </el-select>
|
|
|
- </el-form-item>
|
|
|
- <el-form-item label="所属赛区" prop="zoneId">
|
|
|
- <el-input v-model="form.zoneId" placeholder="请输入所属赛区" />
|
|
|
+ <el-input v-model="form.name" placeholder="请输入门店名称,最多 20 字" />
|
|
|
</el-form-item>
|
|
|
<el-form-item label="详细地址" prop="address">
|
|
|
<el-input v-model="form.address" type="textarea" placeholder="请输入内容" />
|
|
|
- <!-- 添加地图选择按钮 -->
|
|
|
- <el-button type="primary" @click="openMapSelector">选择地图位置</el-button>
|
|
|
+ <el-button type="primary" @click="openMapSelector" style="margin-top: 8px">选择地图位置</el-button>
|
|
|
</el-form-item>
|
|
|
- <el-form-item label="门店图标" prop="iconUrl">
|
|
|
+ <el-form-item label="联系方式" prop="phone">
|
|
|
+ <el-input v-model="form.phone" type="textarea" placeholder="请输入内容" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="服务类型" prop="storeTypeId">
|
|
|
+ <el-select v-model="form.storeTypeId" placeholder="请选择服务类型" style="width: 100%">
|
|
|
+ <el-option v-for="item in tagOptions" :key="item.id" :label="item.serviceName" :value="item.id" />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="主页图标" prop="iconUrl">
|
|
|
<div class="upload-container">
|
|
|
<el-upload
|
|
|
class="upload-icon"
|
|
|
@@ -142,7 +118,6 @@
|
|
|
<el-button type="primary">点击选择图标</el-button>
|
|
|
</template>
|
|
|
|
|
|
- <!-- 预览图区域 -->
|
|
|
<template #default>
|
|
|
<div class="preview-area" @click="handlePreviewClick">
|
|
|
<img
|
|
|
@@ -163,93 +138,37 @@
|
|
|
</el-upload>
|
|
|
</div>
|
|
|
</el-form-item>
|
|
|
-
|
|
|
- <el-form-item label="门店背景" prop="backgroundUrl">
|
|
|
- <div class="upload-container">
|
|
|
- <el-upload
|
|
|
- class="upload-icon"
|
|
|
- action="#"
|
|
|
- :on-change="handleIconChange2"
|
|
|
- :on-remove="handleIconRemove2"
|
|
|
- :file-list="fileList2"
|
|
|
- :auto-upload="false"
|
|
|
- :limit="1"
|
|
|
- accept="image/*"
|
|
|
- >
|
|
|
- <template #trigger>
|
|
|
- <el-button type="primary">点击选择背景</el-button>
|
|
|
- </template>
|
|
|
-
|
|
|
- <!-- 预览图区域 -->
|
|
|
- <template #default>
|
|
|
- <div class="preview-area" @click="handlePreviewClick2">
|
|
|
- <img
|
|
|
- v-if="iconPreviewUrl2 || form.backgroundUrl"
|
|
|
- :src="iconPreviewUrl2 || form.backgroundUrl"
|
|
|
- alt="预览图"
|
|
|
- style="max-width: 100px; max-height: 100px; margin-top: 10px; cursor: pointer"
|
|
|
- />
|
|
|
- <div v-else style="margin-top: 10px; color: #999">无</div>
|
|
|
- </div>
|
|
|
- </template>
|
|
|
-
|
|
|
- <template #tip>
|
|
|
- <div class="el-upload__tip">
|
|
|
- <span v-if="fileList2.length > 0">当前已选文件:{{ fileList2[0].name }}</span>
|
|
|
- </div>
|
|
|
- </template>
|
|
|
- </el-upload>
|
|
|
- </div>
|
|
|
+ <el-form-item label="主标题" prop="mainTitle">
|
|
|
+ <el-input v-model="form.mainTitle" type="textarea" placeholder="请输入内容" />
|
|
|
</el-form-item>
|
|
|
-
|
|
|
- <el-form-item label="门店标签" prop="customTags">
|
|
|
- <div class="tag-container" style="display: flex; align-items: center; gap: 8px">
|
|
|
- <!-- 已选标签 -->
|
|
|
- <el-tag
|
|
|
- v-for="(tag, index) in customTags"
|
|
|
- :key="index"
|
|
|
- closable
|
|
|
- @close="handleTagClose(index)"
|
|
|
- style="margin-right: 8px; margin-bottom: 8px"
|
|
|
- >
|
|
|
- {{ tag }}
|
|
|
- </el-tag>
|
|
|
-
|
|
|
- <!-- 添加新标签按钮 -->
|
|
|
- <el-button type="primary" size="small" plain @click="openAddTagDialog" style="margin-top: 4px"> + 添加标签 </el-button>
|
|
|
- </div>
|
|
|
+ <el-form-item label="门店简介" prop="storeDescription">
|
|
|
+ <el-input v-model="form.storeDescription" type="textarea" placeholder="请输入内容" />
|
|
|
</el-form-item>
|
|
|
-
|
|
|
- <el-form-item label="营业时间" prop="businessStartTime">
|
|
|
- <el-time-picker
|
|
|
+ <el-form-item label="门店appId">
|
|
|
+ <el-input v-model="form.storeAppIds" type="textarea" placeholder="请输入内容" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="门店宣传图" prop="storePromotionImage">
|
|
|
+ <imageUpload v-model="form.storePromotionImageOsId" :limit="5" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="开始兑换时间" prop="businessStartTime">
|
|
|
+ <el-date-picker
|
|
|
+ clearable
|
|
|
v-model="form.businessStartTime"
|
|
|
- format="HH:mm:ss"
|
|
|
- value-format="HH:mm:ss"
|
|
|
- placeholder="请选择营业开始时间"
|
|
|
- :picker-options="{
|
|
|
- start: '00:00',
|
|
|
- end: '23:59',
|
|
|
- step: '00:30'
|
|
|
- }"
|
|
|
- />
|
|
|
+ type="datetime"
|
|
|
+ value-format="YYYY-MM-DD HH:mm:ss"
|
|
|
+ placeholder="请选择开始兑换时间"
|
|
|
+ >
|
|
|
+ </el-date-picker>
|
|
|
</el-form-item>
|
|
|
-
|
|
|
- <el-form-item label="闭店时间" prop="businessEndTime">
|
|
|
- <el-time-picker
|
|
|
+ <el-form-item label="结束兑换时间" prop="businessEndTime">
|
|
|
+ <el-date-picker
|
|
|
+ clearable
|
|
|
v-model="form.businessEndTime"
|
|
|
- format="HH:mm:ss"
|
|
|
- value-format="HH:mm:ss"
|
|
|
- placeholder="请选择营业结束时间"
|
|
|
- :picker-options="{
|
|
|
- start: '00:00',
|
|
|
- end: '23:59',
|
|
|
- step: '00:30'
|
|
|
- }"
|
|
|
- />
|
|
|
- </el-form-item>
|
|
|
-
|
|
|
- <el-form-item label="联系方式" prop="backgroundUrl">
|
|
|
- <el-input v-model="form.phone" type="textarea" placeholder="请输入内容" />
|
|
|
+ type="datetime"
|
|
|
+ value-format="YYYY-MM-DD HH:mm:ss"
|
|
|
+ placeholder="请选择结束兑换时间"
|
|
|
+ >
|
|
|
+ </el-date-picker>
|
|
|
</el-form-item>
|
|
|
<el-form-item label="状态" prop="status">
|
|
|
<el-radio-group v-model="form.status">
|
|
|
@@ -269,27 +188,26 @@
|
|
|
<!-- 地图选择对话框 -->
|
|
|
<el-dialog title="选择地理位置" v-model="mapDialogVisible" width="800px" append-to-body>
|
|
|
<div style="height: 500px">
|
|
|
- <!-- 搜索栏 -->
|
|
|
- <div style="margin-bottom: 10px; display: flex">
|
|
|
- <el-input v-model="searchKeyword" placeholder="请输入地址关键字搜索" style="flex: 1" @keyup.enter="searchAddress">
|
|
|
+ <div style="margin-bottom: 10px; display: flex; justify-content: space-between; align-items: center">
|
|
|
+ <div style="display: flex; gap: 8px">
|
|
|
+ <el-button type="primary" icon="Location" @click="getCurrentLocation">定位当前位置</el-button>
|
|
|
+ </div>
|
|
|
+ <!-- <el-input v-model="searchKeyword" placeholder="请输入地址关键字搜索" style="flex: 1; max-width: 400px" @keyup.enter="searchAddress">
|
|
|
<template #append>
|
|
|
<el-button @click="searchAddress">搜索</el-button>
|
|
|
</template>
|
|
|
</el-input>
|
|
|
- <el-button @click="clearSearch" style="margin-left: 10px">清空</el-button>
|
|
|
+ <el-button @click="clearSearch" style="margin-left: 10px">清空</el-button>-->
|
|
|
</div>
|
|
|
|
|
|
- <!-- 地图容器 -->
|
|
|
- <div id="amap-container" style="width: 100%; height: 400px"></div>
|
|
|
+ <div id="leaflet-map-container" style="width: 100%; height: 400px; background: #f0f0f0"></div>
|
|
|
|
|
|
- <!-- 选中地址显示 -->
|
|
|
<div style="margin-top: 10px">
|
|
|
<el-input v-model="selectedAddress" placeholder="点击地图选择位置或搜索地址" readonly>
|
|
|
<template #prepend>选中地址:</template>
|
|
|
</el-input>
|
|
|
</div>
|
|
|
</div>
|
|
|
-
|
|
|
<template #footer>
|
|
|
<div class="dialog-footer">
|
|
|
<el-button @click="cancelMapSelection">取 消</el-button>
|
|
|
@@ -297,23 +215,11 @@
|
|
|
</div>
|
|
|
</template>
|
|
|
</el-dialog>
|
|
|
+
|
|
|
<!-- 图片预览弹窗 -->
|
|
|
<el-dialog v-model="dialogVisible" title="图片预览" width="50%">
|
|
|
<img :src="previewSrc || iconPreviewUrl || form.iconUrl" alt="预览图片" style="max-width: 100%; max-height: 80vh" />
|
|
|
</el-dialog>
|
|
|
-
|
|
|
- <el-dialog v-model="dialogVisible2" title="图片预览" width="50%">
|
|
|
- <img :src="previewSrc2 || iconPreviewUrl2 || form.backgroundUrl" alt="预览图片" style="max-width: 100%; max-height: 80vh" />
|
|
|
- </el-dialog>
|
|
|
-
|
|
|
- <!-- 添加标签弹窗 -->
|
|
|
- <el-dialog title="添加门店标签" v-model="addTagDialogVisible" width="400px">
|
|
|
- <el-input v-model="newTagName" placeholder="请输入标签名称" clearable @keyup.enter="confirmAddTag" style="width: 100%; margin-bottom: 16px" />
|
|
|
- <template #footer>
|
|
|
- <el-button @click="addTagDialogVisible = false">取消</el-button>
|
|
|
- <el-button type="primary" @click="confirmAddTag">确定</el-button>
|
|
|
- </template>
|
|
|
- </el-dialog>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
@@ -322,10 +228,12 @@ import { listStore, getStore, delStore, addStore, updateStore } from '@/api/syst
|
|
|
import { StoreVO, StoreQuery, StoreForm } from '@/api/system/physical/store/types';
|
|
|
import { uploadTournament } from '@/api/system/business/tournaments';
|
|
|
import { selectAllPhysicalTagsSelList } from '@/api/system/physical/tag';
|
|
|
-
|
|
|
-// 引入高德地图加载器
|
|
|
-import AMapLoader from '@amap/amap-jsapi-loader';
|
|
|
import { TagVO } from '@/api/system/physical/tag/types';
|
|
|
+import { ElMessage } from 'element-plus';
|
|
|
+import L from 'leaflet';
|
|
|
+import 'leaflet/dist/leaflet.css';
|
|
|
+import { listByIds, delOss } from '@/api/system/oss';
|
|
|
+
|
|
|
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
|
|
|
|
|
const storeList = ref<StoreVO[]>([]);
|
|
|
@@ -358,11 +266,15 @@ const initFormData: StoreForm = {
|
|
|
businessEndTime: undefined,
|
|
|
status: 1,
|
|
|
phone: '',
|
|
|
- customTags: [], // 初始化为空数组
|
|
|
+ customTags: [],
|
|
|
createdAt: undefined,
|
|
|
updatedAt: undefined,
|
|
|
- deletedAt: undefined
|
|
|
+ deletedAt: undefined,
|
|
|
+ latitude: undefined,
|
|
|
+ longitude: undefined,
|
|
|
+ storePromotionImageOsId: []
|
|
|
};
|
|
|
+
|
|
|
const data = reactive<PageData<StoreForm, StoreQuery>>({
|
|
|
form: { ...initFormData },
|
|
|
queryParams: {
|
|
|
@@ -384,21 +296,48 @@ const data = reactive<PageData<StoreForm, StoreQuery>>({
|
|
|
params: {}
|
|
|
},
|
|
|
rules: {
|
|
|
- id: [{ required: true, message: '主键ID不能为空', trigger: 'blur' }],
|
|
|
- storeCode: [{ required: true, message: '门店编号,如M0001不能为空', trigger: 'blur' }],
|
|
|
- name: [{ required: true, message: '门店名称,最多20字不能为空', trigger: 'blur' }],
|
|
|
- storeTypeId: [{ required: true, message: '门店类型ID,关联service_type表不能为空', trigger: 'blur' }],
|
|
|
- zoneId: [{ required: true, message: '所属赛区分组ID,关联zone表不能为空', trigger: 'blur' }],
|
|
|
+ id: [{ required: true, message: '主键 ID 不能为空', trigger: 'blur' }],
|
|
|
+ name: [{ required: true, message: '门店名称,最多 20 字不能为空', trigger: 'blur' }],
|
|
|
+ storeTypeId: [{ required: true, message: '门店类型 ID 不能为空', trigger: 'blur' }],
|
|
|
address: [{ required: true, message: '详细地址不能为空', trigger: 'blur' }],
|
|
|
- businessStartTime: [{ required: true, message: '营业开始时间不能为空', trigger: 'blur' }],
|
|
|
- businessEndTime: [{ required: true, message: '营业结束时间不能为空', trigger: 'blur' }],
|
|
|
- status: [{ required: true, message: '状态:1=开业中,2=闭店中不能为空', trigger: 'change' }]
|
|
|
+ status: [{ required: true, message: '状态不能为空', trigger: 'change' }],
|
|
|
+ mainTitle: [{ required: true, message: '主标题不能为空', trigger: 'change' }],
|
|
|
+ storeDescription: [{ required: true, message: '门店简介不能为空', trigger: 'change' }],
|
|
|
+ phone: [
|
|
|
+ { required: true, message: '联系方式不能为空', trigger: 'blur' },
|
|
|
+ { pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号码', trigger: 'blur' }
|
|
|
+ ],
|
|
|
+ businessStartTime: [
|
|
|
+ { required: true, message: '营业开始时间不能为空', trigger: 'blur' },
|
|
|
+ {
|
|
|
+ validator: (rule, value, callback) => {
|
|
|
+ if (value && form.value.businessEndTime && value > form.value.businessEndTime) {
|
|
|
+ callback(new Error('开始时间不能大于结束时间'));
|
|
|
+ } else {
|
|
|
+ callback();
|
|
|
+ }
|
|
|
+ },
|
|
|
+ trigger: 'change'
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ businessEndTime: [
|
|
|
+ { required: true, message: '营业结束时间不能为空', trigger: 'blur' },
|
|
|
+ {
|
|
|
+ validator: (rule, value, callback) => {
|
|
|
+ if (value && form.value.businessStartTime && value < form.value.businessStartTime) {
|
|
|
+ callback(new Error('结束时间不能小于开始时间'));
|
|
|
+ } else {
|
|
|
+ callback();
|
|
|
+ }
|
|
|
+ },
|
|
|
+ trigger: 'change'
|
|
|
+ }
|
|
|
+ ],
|
|
|
}
|
|
|
});
|
|
|
|
|
|
const { queryParams, form, rules } = toRefs(data);
|
|
|
|
|
|
-/** 查询门店信息列表 */
|
|
|
const getList = async () => {
|
|
|
loading.value = true;
|
|
|
const res = await listStore(queryParams.value);
|
|
|
@@ -407,91 +346,125 @@ const getList = async () => {
|
|
|
loading.value = false;
|
|
|
};
|
|
|
|
|
|
-/** 取消按钮 */
|
|
|
const cancel = () => {
|
|
|
reset();
|
|
|
dialog.visible = false;
|
|
|
};
|
|
|
|
|
|
-/** 表单重置 */
|
|
|
const reset = () => {
|
|
|
form.value = { ...initFormData };
|
|
|
storeFormRef.value?.resetFields();
|
|
|
-
|
|
|
- // 显式重置 customTags
|
|
|
customTags.value = [];
|
|
|
-
|
|
|
- // 清空上传相关状态
|
|
|
fileList.value = [];
|
|
|
fileList2.value = [];
|
|
|
iconPreviewUrl.value = '';
|
|
|
iconPreviewUrl2.value = '';
|
|
|
- form.value.iconUrl = ''; // 清除表单中的URL
|
|
|
- form.value.backgroundUrl = ''; // 清除表单中的URL
|
|
|
+ form.value.iconUrl = '';
|
|
|
+ form.value.backgroundUrl = '';
|
|
|
};
|
|
|
-/** 搜索按钮操作 */
|
|
|
+
|
|
|
const handleQuery = () => {
|
|
|
queryParams.value.pageNum = 1;
|
|
|
getList();
|
|
|
};
|
|
|
|
|
|
-/** 重置按钮操作 */
|
|
|
const resetQuery = () => {
|
|
|
queryFormRef.value?.resetFields();
|
|
|
handleQuery();
|
|
|
};
|
|
|
|
|
|
-/** 多选框选中数据 */
|
|
|
const handleSelectionChange = (selection: StoreVO[]) => {
|
|
|
ids.value = selection.map((item) => item.id);
|
|
|
single.value = selection.length != 1;
|
|
|
multiple.value = !selection.length;
|
|
|
};
|
|
|
|
|
|
-/** 新增按钮操作 */
|
|
|
const handleAdd = () => {
|
|
|
reset();
|
|
|
dialog.visible = true;
|
|
|
dialog.title = '添加门店信息';
|
|
|
- // 清空上传相关状态
|
|
|
fileList.value = [];
|
|
|
fileList2.value = [];
|
|
|
iconPreviewUrl.value = '';
|
|
|
iconPreviewUrl2.value = '';
|
|
|
- form.value.iconUrl = ''; // 清除表单中的URL
|
|
|
- form.value.backgroundUrl = ''; // 清除表单中的URL
|
|
|
- // 确保 customTags 被清空
|
|
|
+ form.value.iconUrl = '';
|
|
|
+ form.value.backgroundUrl = '';
|
|
|
customTags.value = [];
|
|
|
};
|
|
|
|
|
|
-/** 修改按钮操作 */
|
|
|
const handleUpdate = async (row?: StoreVO) => {
|
|
|
reset();
|
|
|
const _id = row?.id || ids.value[0];
|
|
|
const res = await getStore(_id);
|
|
|
Object.assign(form.value, res.data);
|
|
|
|
|
|
- // 保留已有的图片URL,但清除上传组件的状态
|
|
|
if (form.value.iconUrl) {
|
|
|
- iconPreviewUrl.value = form.value.iconUrl; // 设置预览图
|
|
|
+ iconPreviewUrl.value = form.value.iconUrl;
|
|
|
}
|
|
|
if (form.value.backgroundUrl) {
|
|
|
- iconPreviewUrl2.value = form.value.backgroundUrl; // 设置预览图
|
|
|
+ iconPreviewUrl2.value = form.value.backgroundUrl;
|
|
|
}
|
|
|
- // 从后端获取的标签数据
|
|
|
if (res.data.customTags && Array.isArray(res.data.customTags)) {
|
|
|
customTags.value = [...res.data.customTags];
|
|
|
} else {
|
|
|
customTags.value = [];
|
|
|
}
|
|
|
+ const processImages = async (images: any[]) => {
|
|
|
+ const imageUrls = await Promise.all(
|
|
|
+ images.map(async (image) => {
|
|
|
+ try {
|
|
|
+ const res = await getIds(image.osId);
|
|
|
+ const firstItem = Array.isArray(res) ? res[0] : res.data?.[0] || res[0];
|
|
|
+ return {
|
|
|
+ url: firstItem?.url || firstItem?.picUrl,
|
|
|
+ ossId: image.osId,
|
|
|
+ name: image.osId
|
|
|
+ };
|
|
|
+ } catch (error) {
|
|
|
+ console.error('获取图片URL失败:', error);
|
|
|
+ return { url: '', ossId: '' };
|
|
|
+ }
|
|
|
+ })
|
|
|
+ );
|
|
|
+ return imageUrls
|
|
|
+ .filter((item) => item.url !== '')
|
|
|
+ .map((item) => item.ossId)
|
|
|
+ .join(',');
|
|
|
+ };
|
|
|
+ if (res.data.storePromotionImage && Array.isArray(res.data.storePromotionImage)) {
|
|
|
+ form.value.storePromotionImageOsId = await processImages(res.data.storePromotionImage);
|
|
|
+ } else {
|
|
|
+ form.value.storePromotionImageOsId = [];
|
|
|
+ }
|
|
|
dialog.visible = true;
|
|
|
dialog.title = '修改门店信息';
|
|
|
};
|
|
|
-/** 提交按钮 */
|
|
|
+const getIds = async (ids: string | number) => {
|
|
|
+ return await listByIds(ids);
|
|
|
+};
|
|
|
const submitForm = () => {
|
|
|
storeFormRef.value?.validate(async (valid: boolean) => {
|
|
|
if (valid) {
|
|
|
buttonLoading.value = true;
|
|
|
+
|
|
|
+ const processOsIds = async (osIdString: any, imageType: string) => {
|
|
|
+ if (!osIdString) return [];
|
|
|
+ const osIds = osIdString
|
|
|
+ .toString()
|
|
|
+ .split(',')
|
|
|
+ .map((id: string) => id.trim());
|
|
|
+ const imageList = [];
|
|
|
+ for (const osId of osIds) {
|
|
|
+ const res = await listByIds(osId);
|
|
|
+ const firstItem = Array.isArray(res) ? res[0] : res.data?.[0] || res[0];
|
|
|
+ const picUrl = firstItem?.url || firstItem?.picUrl;
|
|
|
+ imageList.push({ osId, imageUrl: picUrl, imageType });
|
|
|
+ }
|
|
|
+ return imageList;
|
|
|
+ };
|
|
|
+
|
|
|
+ form.value.storePromotionImage = await processOsIds(form.value.storePromotionImageOsId, 'STORE');
|
|
|
+
|
|
|
if (form.value.id) {
|
|
|
await updateStore(form.value).finally(() => (buttonLoading.value = false));
|
|
|
} else {
|
|
|
@@ -503,32 +476,22 @@ const submitForm = () => {
|
|
|
}
|
|
|
});
|
|
|
};
|
|
|
+// ... existing code ...
|
|
|
+
|
|
|
|
|
|
-/** 删除按钮操作 */
|
|
|
const handleDelete = async (row?: StoreVO) => {
|
|
|
const _ids = row?.id || ids.value;
|
|
|
- await proxy?.$modal.confirm('是否确认删除门店信息编号为"' + _ids + '"的数据项?').finally(() => (loading.value = false));
|
|
|
+ await proxy?.$modal.confirm('是否确认删除门店信息编号为"' + _ids + '"的数据项?');
|
|
|
await delStore(_ids);
|
|
|
proxy?.$modal.msgSuccess('删除成功');
|
|
|
await getList();
|
|
|
};
|
|
|
|
|
|
-/** 导出按钮操作 */
|
|
|
-const handleExport = () => {
|
|
|
- proxy?.download(
|
|
|
- 'system/store/export',
|
|
|
- {
|
|
|
- ...queryParams.value
|
|
|
- },
|
|
|
- `store_${new Date().getTime()}.xlsx`
|
|
|
- );
|
|
|
-};
|
|
|
-
|
|
|
onMounted(() => {
|
|
|
getList();
|
|
|
- loadTagOptions(); // 加载门店类型选项
|
|
|
+ loadTagOptions();
|
|
|
});
|
|
|
-// 响应式变量
|
|
|
+
|
|
|
const iconPreviewUrl = ref('');
|
|
|
const iconPreviewUrl2 = ref('');
|
|
|
const fileList = ref([]);
|
|
|
@@ -538,7 +501,6 @@ const dialogVisible2 = ref(false);
|
|
|
const previewSrc = ref('');
|
|
|
const previewSrc2 = ref('');
|
|
|
|
|
|
-/** 处理图标上传 */
|
|
|
const handleIconChange = async (file) => {
|
|
|
const index = fileList.value.findIndex((f) => f.uid === file.uid);
|
|
|
fileList.value = [];
|
|
|
@@ -548,7 +510,6 @@ const handleIconChange = async (file) => {
|
|
|
}
|
|
|
|
|
|
if (index === -1) {
|
|
|
- // 如果文件不在列表中,则添加进去
|
|
|
fileList.value.push(file);
|
|
|
}
|
|
|
|
|
|
@@ -556,7 +517,6 @@ const handleIconChange = async (file) => {
|
|
|
const rawFile = file.raw;
|
|
|
const res = await uploadTournament(rawFile);
|
|
|
if (res.code === 200) {
|
|
|
- // 更新文件状态为成功
|
|
|
fileList.value[index] = {
|
|
|
...file,
|
|
|
status: 'success',
|
|
|
@@ -569,7 +529,6 @@ const handleIconChange = async (file) => {
|
|
|
throw new Error(res.msg);
|
|
|
}
|
|
|
} catch (error) {
|
|
|
- // 更新文件状态为失败
|
|
|
fileList.value[index] = {
|
|
|
...file,
|
|
|
status: 'fail',
|
|
|
@@ -579,15 +538,12 @@ const handleIconChange = async (file) => {
|
|
|
}
|
|
|
};
|
|
|
|
|
|
-/** 处理图标删除 */
|
|
|
const handleIconRemove = (file, updatedFileList) => {
|
|
|
fileList.value = updatedFileList;
|
|
|
- // 清除预览图和临时链接
|
|
|
iconPreviewUrl.value = '';
|
|
|
- form.value.iconUrl = ''; // 清除表单中的URL
|
|
|
+ form.value.iconUrl = '';
|
|
|
};
|
|
|
|
|
|
-/** 点击预览图触发放大 */
|
|
|
const handlePreviewClick = () => {
|
|
|
const currentSrc = iconPreviewUrl.value || form.value.iconUrl;
|
|
|
if (currentSrc) {
|
|
|
@@ -595,7 +551,6 @@ const handlePreviewClick = () => {
|
|
|
}
|
|
|
};
|
|
|
|
|
|
-/** 处理背景图上传 */
|
|
|
const handleIconChange2 = async (file) => {
|
|
|
const index = fileList2.value.findIndex((f) => f.uid === file.uid);
|
|
|
fileList2.value = [];
|
|
|
@@ -605,7 +560,6 @@ const handleIconChange2 = async (file) => {
|
|
|
}
|
|
|
|
|
|
if (index === -1) {
|
|
|
- // 如果文件不在列表中,则添加进去
|
|
|
fileList2.value.push(file);
|
|
|
}
|
|
|
|
|
|
@@ -613,7 +567,6 @@ const handleIconChange2 = async (file) => {
|
|
|
const rawFile = file.raw;
|
|
|
const res = await uploadTournament(rawFile);
|
|
|
if (res.code === 200) {
|
|
|
- // 更新文件状态为成功
|
|
|
fileList2.value[index] = {
|
|
|
...file,
|
|
|
status: 'success',
|
|
|
@@ -626,7 +579,6 @@ const handleIconChange2 = async (file) => {
|
|
|
throw new Error(res.msg);
|
|
|
}
|
|
|
} catch (error) {
|
|
|
- // 更新文件状态为失败
|
|
|
fileList2.value[index] = {
|
|
|
...file,
|
|
|
status: 'fail',
|
|
|
@@ -636,24 +588,20 @@ const handleIconChange2 = async (file) => {
|
|
|
}
|
|
|
};
|
|
|
|
|
|
-/** 处理背景图删除 */
|
|
|
const handleIconRemove2 = (file, updatedFileList) => {
|
|
|
fileList2.value = updatedFileList;
|
|
|
- // 清除预览图和临时链接
|
|
|
iconPreviewUrl2.value = '';
|
|
|
- form.value.backgroundUrl = ''; // 清除表单中的URL
|
|
|
+ form.value.backgroundUrl = '';
|
|
|
};
|
|
|
|
|
|
-/** 点击预览图触发放大 */
|
|
|
const handlePreviewClick2 = () => {
|
|
|
const currentSrc = iconPreviewUrl2.value || form.value.backgroundUrl;
|
|
|
if (currentSrc) {
|
|
|
dialogVisible2.value = true;
|
|
|
}
|
|
|
};
|
|
|
-// 响应式变量
|
|
|
+
|
|
|
const tagOptions = ref<TagVO[]>([]);
|
|
|
-// 加载门店类型选项
|
|
|
const loadTagOptions = async () => {
|
|
|
try {
|
|
|
const res = await selectAllPhysicalTagsSelList();
|
|
|
@@ -667,22 +615,20 @@ const loadTagOptions = async () => {
|
|
|
ElMessage.error('请求失败,请检查网络');
|
|
|
}
|
|
|
};
|
|
|
-// 自定义标签相关状态
|
|
|
+
|
|
|
const customTags = ref<string[]>([]);
|
|
|
const addTagDialogVisible = ref(false);
|
|
|
const newTagName = ref('');
|
|
|
-// 添加标签
|
|
|
+
|
|
|
const confirmAddTag = () => {
|
|
|
if (!newTagName.value.trim()) {
|
|
|
ElMessage.warning('标签名称不能为空');
|
|
|
return;
|
|
|
}
|
|
|
- // 检查是否已达到最大数量限制
|
|
|
if (customTags.value.length >= 3) {
|
|
|
- ElMessage.warning('最多只能添加3个标签');
|
|
|
+ ElMessage.warning('最多只能添加 3 个标签');
|
|
|
return;
|
|
|
}
|
|
|
- // 检查是否已存在
|
|
|
if (customTags.value.includes(newTagName.value)) {
|
|
|
ElMessage.warning('该标签已存在');
|
|
|
return;
|
|
|
@@ -693,16 +639,13 @@ const confirmAddTag = () => {
|
|
|
addTagDialogVisible.value = false;
|
|
|
};
|
|
|
|
|
|
-// 删除标签
|
|
|
const handleTagClose = (index: number) => {
|
|
|
customTags.value.splice(index, 1);
|
|
|
};
|
|
|
|
|
|
-// 表单提交时,将标签数组赋值给 form.customTags
|
|
|
watch(
|
|
|
customTags,
|
|
|
() => {
|
|
|
- // 确保赋值的是一个数组
|
|
|
if (Array.isArray(customTags.value)) {
|
|
|
form.value.customTags = customTags.value;
|
|
|
}
|
|
|
@@ -713,153 +656,234 @@ const openAddTagDialog = () => {
|
|
|
newTagName.value = '';
|
|
|
addTagDialogVisible.value = true;
|
|
|
};
|
|
|
-// 添加响应式变量
|
|
|
+
|
|
|
const mapDialogVisible = ref(false);
|
|
|
const selectedAddress = ref('');
|
|
|
const selectedLocation = ref({ lng: 0, lat: 0 });
|
|
|
const searchKeyword = ref('');
|
|
|
-let amapInstance: any = null;
|
|
|
-let marker: any = null;
|
|
|
+let mapInstance: L.Map | null = null;
|
|
|
+let markerInstance: L.Marker | null = null;
|
|
|
|
|
|
-/** 打开地图选择器 */
|
|
|
const openMapSelector = () => {
|
|
|
mapDialogVisible.value = true;
|
|
|
nextTick(() => {
|
|
|
- initAMap(); // 等待 el-dialog 渲染完成再初始化
|
|
|
+ setTimeout(() => {
|
|
|
+ initLeafletMap();
|
|
|
+ }, 100);
|
|
|
});
|
|
|
};
|
|
|
|
|
|
-/** 初始化高德地图 */
|
|
|
-const initAMap = () => {
|
|
|
- AMapLoader.load({
|
|
|
- key: 'ed2e29e4cbcc0f96905acb2106e82b42', // 请替换为您的高德地图key
|
|
|
- version: '2.0',
|
|
|
- plugins: ['AMap.Geocoder', 'AMap.PlaceSearch', 'AMap.AutoComplete', 'AMap.Marker', 'AMap.ToolBar', 'AMap.Scale', 'AMap.Geolocation']
|
|
|
- })
|
|
|
- .then((AMap) => {
|
|
|
- amapInstance = AMap;
|
|
|
-
|
|
|
- // 确保容器存在后再创建地图
|
|
|
- const container = document.getElementById('amap-container');
|
|
|
- if (!container) return;
|
|
|
-
|
|
|
- const map = new AMap.Map(container, {
|
|
|
- resizeEnable: true,
|
|
|
- zoom: 12,
|
|
|
- center: [116.397428, 39.90923] // 默认中心点
|
|
|
- });
|
|
|
-
|
|
|
- // 添加定位功能
|
|
|
- map.addControl(
|
|
|
- new AMap.Geolocation({
|
|
|
- zoomToCurrent: true,
|
|
|
- position: 'rtb'
|
|
|
- })
|
|
|
- );
|
|
|
-
|
|
|
- // 添加点击事件
|
|
|
- map.on('click', function (e) {
|
|
|
- const lnglat = e.lnglat;
|
|
|
- setSelectedLocation(lnglat, AMap);
|
|
|
- });
|
|
|
-
|
|
|
- // 添加地图控件
|
|
|
- map.addControl(new AMap.ToolBar());
|
|
|
- map.addControl(new AMap.Scale());
|
|
|
-
|
|
|
- // 初始化标记
|
|
|
- marker = new AMap.Marker({
|
|
|
- map: map,
|
|
|
- draggable: true,
|
|
|
- cursor: 'move'
|
|
|
- });
|
|
|
-
|
|
|
- // 标记拖拽结束事件
|
|
|
- marker.on('dragend', function (e) {
|
|
|
- setSelectedLocation(e.lnglat, AMap);
|
|
|
- });
|
|
|
-
|
|
|
- // 将map实例保存到DOM元素上
|
|
|
- (container as any).__map__ = map;
|
|
|
- })
|
|
|
- .catch((e) => {
|
|
|
- console.error(e);
|
|
|
- proxy?.$modal.msgError('地图加载失败: ' + e.message);
|
|
|
- });
|
|
|
+const initLeafletMap = () => {
|
|
|
+ const container = document.getElementById('leaflet-map-container');
|
|
|
+ if (!container) return;
|
|
|
+
|
|
|
+ if (mapInstance) {
|
|
|
+ mapInstance.remove();
|
|
|
+ mapInstance = null;
|
|
|
+ markerInstance = null;
|
|
|
+ }
|
|
|
+
|
|
|
+ mapInstance = L.map('leaflet-map-container', {
|
|
|
+ center: [35.8617, 104.1954],
|
|
|
+ zoom: 5,
|
|
|
+ zoomControl: true,
|
|
|
+ preferCanvas: true,
|
|
|
+ fadeAnimation: false,
|
|
|
+ zoomAnimation: false
|
|
|
+ });
|
|
|
+
|
|
|
+ L.tileLayer('https://{s}.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}{r}.png', {
|
|
|
+ attribution:
|
|
|
+ '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors © <a href="https://carto.com/attributions">CARTO</a>',
|
|
|
+ subdomains: ['a', 'b', 'c', 'd'],
|
|
|
+ maxZoom: 20
|
|
|
+ }).addTo(mapInstance);
|
|
|
+
|
|
|
+ mapInstance.on('click', function (e: L.LeafletMouseEvent) {
|
|
|
+ setSelectedLocation(e.latlng);
|
|
|
+ });
|
|
|
+
|
|
|
+ markerInstance = L.marker([35.8617, 104.1954], {
|
|
|
+ draggable: true,
|
|
|
+ riseOnHover: true
|
|
|
+ }).addTo(mapInstance);
|
|
|
+
|
|
|
+ markerInstance.on('dragend', function (e) {
|
|
|
+ const marker = e.target;
|
|
|
+ const position = marker.getLatLng();
|
|
|
+ setSelectedLocation(position);
|
|
|
+ });
|
|
|
+
|
|
|
+ setTimeout(() => {
|
|
|
+ if (mapInstance) {
|
|
|
+ mapInstance.invalidateSize();
|
|
|
+ }
|
|
|
+ }, 300);
|
|
|
};
|
|
|
|
|
|
-/** 设置选中位置 */
|
|
|
-const setSelectedLocation = (lnglat: any, AMap: any) => {
|
|
|
+const setSelectedLocation = async (latlng: L.LatLng) => {
|
|
|
selectedLocation.value = {
|
|
|
- lng: lnglat.getLng(),
|
|
|
- lat: lnglat.getLat()
|
|
|
+ lng: latlng.lng,
|
|
|
+ lat: latlng.lat
|
|
|
};
|
|
|
+ if (markerInstance) {
|
|
|
+ markerInstance.setLatLng(latlng);
|
|
|
+ }
|
|
|
|
|
|
- marker.setPosition(lnglat);
|
|
|
+ const coordsText = `纬度:${latlng.lat.toFixed(6)}, 经度:${latlng.lng.toFixed(6)}`;
|
|
|
|
|
|
- // 逆地理编码获取地址
|
|
|
- const geocoder = new AMap.Geocoder();
|
|
|
- geocoder.getAddress(lnglat, function (status: string, result: any) {
|
|
|
- debugger;
|
|
|
- if (status === 'complete' && result.regeocode) {
|
|
|
- selectedAddress.value = result.regeocode.formattedAddress;
|
|
|
+ try {
|
|
|
+ const response = await fetch(
|
|
|
+ `https://api.opencagedata.com/geocode/v1/json?q=${latlng.lat}+${latlng.lng}&key=82827937f9614a6eb17c772efa63d75d&language=zh&no_annotations=1`
|
|
|
+ );
|
|
|
+
|
|
|
+ if (response.ok) {
|
|
|
+ const data = await response.json();
|
|
|
+ if (data && data.results && data.results.length > 0) {
|
|
|
+ const address = data.results[0].formatted;
|
|
|
+ const cleanedAddress = address.replace(/^\d{5,}\s*/, '').replace(/,/g, '');
|
|
|
+ selectedAddress.value = cleanedAddress;
|
|
|
+ } else {
|
|
|
+ selectedAddress.value = coordsText;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ selectedAddress.value = coordsText;
|
|
|
}
|
|
|
- });
|
|
|
+ } catch (error) {
|
|
|
+ console.error('地址解析失败:', error);
|
|
|
+ selectedAddress.value = coordsText;
|
|
|
+ }
|
|
|
};
|
|
|
+const searchAddress = async () => {
|
|
|
+ if (!searchKeyword.value.trim()) {
|
|
|
+ proxy?.$modal.msgWarning('请输入搜索关键字');
|
|
|
+ return;
|
|
|
+ }
|
|
|
|
|
|
-/** 搜索地址 */
|
|
|
-const searchAddress = () => {
|
|
|
- if (!searchKeyword.value || !amapInstance) return;
|
|
|
-
|
|
|
- amapInstance.plugin('AMap.PlaceSearch', () => {
|
|
|
- const placeSearch = new amapInstance.PlaceSearch({
|
|
|
- pageSize: 5,
|
|
|
- pageIndex: 1,
|
|
|
- city: '全国', // 搜索范围
|
|
|
- map: document.getElementById('amap-container')?.__map__ // 使用正确的map引用
|
|
|
- });
|
|
|
-
|
|
|
- placeSearch.search(searchKeyword.value, (status: string, result: any) => {
|
|
|
- if (status === 'complete' && result.info === 'OK') {
|
|
|
- if (result.poiList && result.poiList.pois.length > 0) {
|
|
|
- const firstPoi = result.poiList.pois[0];
|
|
|
- const lnglat = [firstPoi.location.lng, firstPoi.location.lat];
|
|
|
-
|
|
|
- // 更新地图中心点
|
|
|
- const mapElement = document.getElementById('amap-container');
|
|
|
- if (mapElement) {
|
|
|
- const map = mapElement.__map__;
|
|
|
- if (map) {
|
|
|
- map.setCenter(lnglat);
|
|
|
- setSelectedLocation(new amapInstance.LngLat(firstPoi.location.lng, firstPoi.location.lat), amapInstance);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- } else {
|
|
|
- proxy?.$modal.msgWarning('未找到相关地址');
|
|
|
+ try {
|
|
|
+ const response = await fetch(
|
|
|
+ `https://nominatim.openstreetmap.org/search?format=json&q=${encodeURIComponent(searchKeyword.value)}&countrycodes=cn&limit=5`
|
|
|
+ );
|
|
|
+
|
|
|
+ if (!response.ok) {
|
|
|
+ throw new Error('搜索失败');
|
|
|
+ }
|
|
|
+
|
|
|
+ const results = await response.json();
|
|
|
+
|
|
|
+ if (results && results.length > 0) {
|
|
|
+ const firstResult = results[0];
|
|
|
+ const latlng = L.latLng(parseFloat(firstResult.lat), parseFloat(firstResult.lon));
|
|
|
+
|
|
|
+ if (mapInstance) {
|
|
|
+ mapInstance.setView(latlng, 15);
|
|
|
+ setSelectedLocation(latlng);
|
|
|
+ selectedAddress.value = firstResult.display_name || searchKeyword.value;
|
|
|
+ proxy?.$modal.msgSuccess('找到地址');
|
|
|
}
|
|
|
- });
|
|
|
- });
|
|
|
+ } else {
|
|
|
+ proxy?.$modal.msgWarning('未找到相关地址');
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('搜索失败:', error);
|
|
|
+ proxy?.$modal.msgError('搜索失败,请重试');
|
|
|
+ }
|
|
|
};
|
|
|
-
|
|
|
-/** 确认选择地图位置 */
|
|
|
const confirmMapSelection = () => {
|
|
|
if (selectedAddress.value) {
|
|
|
form.value.address = selectedAddress.value;
|
|
|
- // 如果需要保存经纬度,可以在form中添加相应字段
|
|
|
- // form.value.longitude = selectedLocation.value.lng;
|
|
|
- // form.value.latitude = selectedLocation.value.lat;
|
|
|
+ form.value.latitude = selectedLocation.value.lat;
|
|
|
+ form.value.longitude = selectedLocation.value.lng;
|
|
|
}
|
|
|
mapDialogVisible.value = false;
|
|
|
};
|
|
|
|
|
|
-/** 取消地图选择 */
|
|
|
const cancelMapSelection = () => {
|
|
|
mapDialogVisible.value = false;
|
|
|
};
|
|
|
|
|
|
-/** 清除搜索关键词 */
|
|
|
const clearSearch = () => {
|
|
|
searchKeyword.value = '';
|
|
|
};
|
|
|
+// ... existing code ...
|
|
|
+// ... existing code ...
|
|
|
+const getCurrentLocation = () => {
|
|
|
+ if (!navigator.geolocation) {
|
|
|
+ proxy?.$modal.msgError('您的浏览器不支持地理位置功能');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ proxy?.$modal.loading('正在获取您的位置...');
|
|
|
+
|
|
|
+ navigator.geolocation.getCurrentPosition(
|
|
|
+ async (position) => {
|
|
|
+ const accuracy = position.coords.accuracy;
|
|
|
+ console.log('定位精度:', accuracy, '米');
|
|
|
+
|
|
|
+ if (accuracy > 100) {
|
|
|
+ proxy?.$modal.msgWarning('定位精度较低 (' + Math.round(accuracy) + '米),建议在窗边或室外开阔地重试');
|
|
|
+ }
|
|
|
+
|
|
|
+ const latlng = L.latLng(position.coords.latitude, position.coords.longitude);
|
|
|
+ console.log('定位坐标:', latlng);
|
|
|
+
|
|
|
+ if (mapInstance) {
|
|
|
+ const zoom = accuracy < 50 ? 17 : accuracy < 100 ? 16 : 15;
|
|
|
+ mapInstance.setView(latlng, zoom);
|
|
|
+ await setSelectedLocation(latlng);
|
|
|
+ }
|
|
|
+
|
|
|
+ proxy?.$modal.closeLoading();
|
|
|
+ if (accuracy <= 50) {
|
|
|
+ proxy?.$modal.msgSuccess('位置获取成功 (精度:' + Math.round(accuracy) + '米)');
|
|
|
+ }
|
|
|
+ },
|
|
|
+ (error) => {
|
|
|
+ proxy?.$modal.closeLoading();
|
|
|
+ switch (error.code) {
|
|
|
+ case error.PERMISSION_DENIED:
|
|
|
+ proxy?.$modal.msgError('用户拒绝了地理位置请求');
|
|
|
+ break;
|
|
|
+ case error.POSITION_UNAVAILABLE:
|
|
|
+ proxy?.$modal.msgError('位置信息不可用');
|
|
|
+ break;
|
|
|
+ case error.TIMEOUT:
|
|
|
+ proxy?.$modal.msgError('获取位置超时');
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ proxy?.$modal.msgError('获取位置失败');
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ enableHighAccuracy: true,
|
|
|
+ timeout: 20000,
|
|
|
+ maximumAge: 0
|
|
|
+ }
|
|
|
+ );
|
|
|
+};
|
|
|
+// ... existing code ...
|
|
|
+
|
|
|
+// ... existing code ...
|
|
|
</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+.upload-container {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ align-items: flex-start;
|
|
|
+}
|
|
|
+
|
|
|
+.upload-icon {
|
|
|
+ margin-bottom: 10px;
|
|
|
+}
|
|
|
+
|
|
|
+.preview-area {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ align-items: center;
|
|
|
+}
|
|
|
+
|
|
|
+.tag-container {
|
|
|
+ flex-wrap: wrap;
|
|
|
+}
|
|
|
+</style>
|