增加版本管理页面,修改店铺添加地图组件使用

This commit is contained in:
2025-02-14 09:17:34 +08:00
parent 0e3759b34d
commit dda2f32ccc
36 changed files with 2486 additions and 318 deletions

View File

@@ -6,7 +6,7 @@
>
<div style="height: 50vh; overflow-y: auto">
<el-form
ref="form"
ref="refForm"
:model="state.form"
:rules="state.rules"
label-width="120px"
@@ -16,14 +16,14 @@
<el-input v-model="state.form.shopName" placeholder="请输入门店名称"></el-input>
</el-form-item>
<el-form-item label="店铺类型">
<el-radio-group v-model="state.form.type">
<el-radio-group v-model="state.form.shopType">
<el-radio-button value="only">单店</el-radio-button>
<el-radio-button value="chain">连锁店</el-radio-button>
<el-radio-button value="join">加盟店</el-radio-button>
</el-radio-group>
<div class="tips">请谨慎修改</div>
</el-form-item>
<el-form-item label="主店账号" prop="mainId" v-if="state.form.type != 'only'">
<el-form-item label="主店账号" prop="mainId" v-if="state.form.shopType != 'only'">
<el-select
v-model="state.form.mainId"
placeholder="请选择主店铺"
@@ -45,26 +45,10 @@
<el-input v-model="state.form.chainName" placeholder="请输入连锁店扩展店名"></el-input>
</el-form-item>
<el-form-item label="门店logo" prop="logo">
<el-image
:src="state.form.logo || uploadImg"
fit="contain"
style="width: 80px; height: 80px"
@click="
state.showUpload = true;
state.uploadIndex = 1;
"
></el-image>
<SingleImageUpload v-model="state.form.logo" />
</el-form-item>
<el-form-item label="门店照片">
<el-image
:src="state.form.coverImg || uploadImg"
fit="contain"
style="width: 80px; height: 80px"
@click="
state.showUpload = true;
state.uploadIndex = 2;
"
></el-image>
<SingleImageUpload v-model="state.form.frontImg" />
</el-form-item>
<el-form-item label="经营模式">
<el-radio-group v-model="state.form.registerType">
@@ -73,7 +57,7 @@
</el-radio-group>
<div class="tips">请谨慎修改</div>
</el-form-item>
<el-form-item label="管理方式" v-if="state.form.type != 'only'">
<el-form-item label="管理方式" v-if="state.form.shopType != 'only'">
<el-radio-group v-model="state.form.tube_type">
<el-radio-button value="0">不可直接管理</el-radio-button>
<el-radio-button value="1">直接管理</el-radio-button>
@@ -87,17 +71,17 @@
</el-radio-group>
</el-form-item>
<el-form-item label="激活码">
<el-input v-model="state.form.registerCode" placeholder="请输入激活码"></el-input>
<el-input v-model="state.form.activateCode" placeholder="请输入激活码"></el-input>
<div class="tips">输入有效激活码表示添加的同时直接激活该店铺</div>
</el-form-item>
<el-form-item label="登录账号" prop="account">
<el-input v-model="state.form.account" placeholder="请输入登录账号"></el-input>
<el-form-item label="登录账号" prop="accountName">
<el-input v-model="state.form.accountName" placeholder="请输入登录账号"></el-input>
</el-form-item>
<el-form-item label="登录密码" prop="password" v-if="!state.form.id">
<el-input
type="password"
show-password
v-model="state.form.password"
v-model="state.form.accountPwd"
placeholder="请输入登录密码"
></el-input>
</el-form-item>
@@ -117,7 +101,7 @@
<el-input-number v-model="form.takeaway_money" placeholder="0.00" controls-position="right"
:min="0"></el-input-number>
</el-form-item> -->
<el-form-item label="店铺经度" prop="provinces">
<el-form-item label="店铺经度" prop="lat">
<el-row>
<el-col :span="9" v-if="state.form.provinces">
<el-input
@@ -168,46 +152,48 @@
>
<div class="map_box">
<div class="map">
<el-amap ref="map" :center="state.amapOptions.center">
<el-amap ref="map" :center="state.amapOptions.center" @init="mapInit">
<el-amap-marker :position="state.amapOptions.center"></el-amap-marker>
<el-amap-search-box
:visible="true"
@select="onSearchResult"
@choose="onSearchResult"
city="西安"
></el-amap-search-box>
</el-amap>
</div>
</div>
</el-dialog>
<el-dialog
v-model="state.showUpload"
:close-on-click-modal="false"
append-to-body
width="500px"
@close="state.showUpload = false"
>
<el-upload
:before-remove="handleBeforeRemove"
:on-success="handleSuccess"
:on-error="handleError"
:file-list="state.fileList"
:headers="headers"
:action="qiNiuUploadApi"
class="upload-demo"
multiple
>
<el-button size="small" type="primary">点击上传</el-button>
<template #tip>
<div style="display: block" class="el-upload__tip">请勿上传违法文件且文件不超过15M</div>
</template>
</el-upload>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="doSubmit">确认</el-button>
<el-button @click="uploadClose">取消</el-button>
<div class="search_box">
<el-input
v-model="state.searchOption.keyword"
placeholder="请输入关键字"
@focus="state.searchOption.focus = true"
@blur="autoCompleteSearchBlur"
@input="autoCompleteSearch(state.searchOption.keyword)"
>
<template #append>
<el-button type="primary" @click="placeSearchSearch(state.searchOption.keyword)">
搜索
</el-button>
</template>
</el-input>
<div class="list" v-if="state.searchOption.focus && state.searchOption.show">
<div
class="item"
@click="autoCompleteListClick(item)"
v-for="item in state.autoCompleteList"
:key="item.id"
>
{{ item.name }}
</div>
</div>
</div>
</template>
<div class="search_wrap">
<div class="item" v-for="item in state.locationSearchList" :key="item.id">
<div class="left">
<div class="name">{{ item.name }}-{{ item.address }}</div>
<div class="location">经纬度{{ item.location.lng }},{{ item.location.lat }}</div>
</div>
<div class="btn">
<el-button type="primary" @click="selectLocationHandle(item)">选择</el-button>
</div>
</div>
</div>
</div>
</el-dialog>
<template #footer>
@@ -225,6 +211,9 @@
<script setup>
import { getToken } from "@/utils/auth";
import uploadImg from "@/assets/images/upload.png";
import { ElAmap } from "@vuemap/vue-amap";
import { initMapLoad } from "@/utils/mapLoadUtil";
import { ElNotification } from "element-plus";
// { geocode, ShopApi.getList }
import ShopApi from "@/api/account/shop";
const validateLogo = (rule, value, callback) => {
@@ -248,13 +237,13 @@ const state = reactive({
id: "",
shopName: "",
mainId: "",
type: "only",
shopType: "only",
tube_type: "0",
registerType: "restaurant",
profiles: "release",
registerCode: "",
account: "",
password: "",
activateCode: "",
accountName: "",
accountPwd: "",
phone: "",
supportDeviceNumber: 1,
lat: "",
@@ -263,7 +252,7 @@ const state = reactive({
detail: "",
status: 1,
logo: "",
coverImg: "",
frontImg: "",
provinces: "",
cities: "",
districts: "",
@@ -285,7 +274,7 @@ const state = reactive({
trigger: "blur",
},
],
provinces: [
lat: [
{
required: true,
message: "请选择坐标",
@@ -299,14 +288,14 @@ const state = reactive({
trigger: "change",
},
],
account: [
accountName: [
{
required: true,
message: " ",
trigger: "change",
},
],
password: [
accountPwd: [
{
required: true,
message: " ",
@@ -322,7 +311,11 @@ const state = reactive({
searchOption: {
city: "西安",
citylimit: false,
keyword: "",
show: false,
focus: false,
},
autoCompleteList: [],
locationSearchList: [],
amapOptions: {
center: [108.946465, 34.347984],
@@ -331,6 +324,9 @@ const state = reactive({
shopListLoading: false,
shopList: [],
});
onBeforeMount(async () => {
const res = await initMapLoad();
});
onMounted(() => {
state.resetForm = { ...state.form };
});
@@ -352,39 +348,33 @@ async function getTableData(query = "") {
console.log(error);
}
}
function onSearchResult(res) {
console.log("res");
console.log(res);
state.locationSearchList = res;
state.amapOptions.center = [res[0].lng, res[0].lat];
}
// 确认地址选择
async function selectLocationHandle(item) {
console.log(item);
state.form.lng = item.lng;
state.form.lat = item.lat;
state.form.lng = item.location.lng;
state.form.lat = item.location.lat;
state.form.address = item.address;
state.showLocation = false;
const position = `${item.lng},${item.lat}`;
const res = JSON.parse(await geocode({ location: position }));
console.log(res);
const position = `${item.location.lng},${item.location.lat}`;
state.form.provinces = res.addressComponent.province;
state.form.cities = res.addressComponent.city;
state.form.districts = res.addressComponent.district;
state.form.provinces = item.pname;
state.form.cities = item.cityname;
state.form.districts = item.adname;
}
const emits = defineEmits(["close", "success"]);
const refForm = ref(null);
// 保存
function submitHandle() {
state.$refs.form.validate(async (valid) => {
refForm.value.validate(async (valid) => {
if (valid) {
state.formLoading = true;
try {
await ShopApi.getListPost(state.form, state.form.id ? "put" : "post");
state.form.id ? await ShopApi.edit(state.form) : await ShopApi.add(state.form);
emits("success");
state.formLoading = false;
$notify({
ElNotification({
title: "成功",
message: `${state.form.id ? "编辑" : "添加"}成功`,
type: "success",
@@ -404,38 +394,7 @@ function handleSuccess(response, file, fileList) {
console.log("上传成功", response);
state.files = response.data;
}
function handleBeforeRemove(file, fileList) {
for (let i = 0; i < state.files.length; i++) {
if (state.files[i].uid === file.uid) {
crudQiNiu.del([state.files[i].id]).then((res) => {});
return true;
}
}
}
function handlePictureCardPreview(file) {
state.dialogImageUrl = file.url;
state.dialogVisible = true;
}
// 监听上传失败
function handleError(e, file, fileList) {
const msg = JSON.parse(e.message);
state.crud.notify(msg.message, CRUD.NOTIFICATION_TYPE.ERROR);
}
// 刷新列表数据
function doSubmit() {
state.fileList = [];
state.showUpload = false;
switch (state.uploadIndex) {
case 1:
state.form.logo = state.files[0];
break;
case 2:
state.form.coverImg = state.files[0];
break;
default:
break;
}
}
function show(obj) {
getTableData();
state.dialogVisible = true;
@@ -447,12 +406,80 @@ function show(obj) {
function close() {
state.dialogVisible = false;
}
function uploadClose() {
state.showUpload = false;
}
function reset() {
state.form = { ...state.resetForm };
}
let ElMap = undefined;
let placeSearch = undefined;
let autoComplete = undefined;
let PlaceSearchIndex = 1;
function autoCompleteSearchBlur() {
setTimeout(() => {
state.searchOption.show = false;
}, 200);
}
function autoCompleteSearch(keyword) {
if (keyword === "") {
state.autoCompleteList = [];
return;
}
autoComplete.search(keyword, function (status, result) {
if (status === "complete" && result.info === "OK") {
// 搜索成功时result即是对应的匹配数据
console.log(result);
state.searchOption.show = true;
state.autoCompleteList = result.tips;
}
});
}
function autoCompleteListClick(item) {
console.log(item);
state.searchOption.keyword = item.name;
state.autoCompleteList = [];
placeSearchSearch(item.name);
}
function placeSearchSearch(keyword) {
PlaceSearchIndex = 1;
if (keyword === "") {
state.locationSearchList = [];
return;
}
// 关键字查询
placeSearch.search(keyword, (status, result) => {
console.log(status);
console.log(result);
state.locationSearchList = result.poiList.pois;
});
}
function mapInit(map) {
map.plugin(["AMap.PlaceSearch", "AMap.AutoComplete", "AMap.ToolBar"], () => {
const toolBar = new AMap.ToolBar();
map.addControl(toolBar);
// 注意输入提示插件2.0版本需引入AMap.AutoComplete而1.4版本应使用AMap.Autocomplete
// 实例化AutoComplete
var autoOptions = {
city: "西安",
};
autoComplete = new AMap.AutoComplete(autoOptions);
//构造地点查询类
placeSearch = new AMap.PlaceSearch({
pageSize: 10, // 单页显示结果条数
pageIndex: PlaceSearchIndex, // 页码
city: "西安", // 兴趣点城市
citylimit: true, //是否强制限制在设置的城市内搜索
map: map, // 展现结果的地图实例
// panel: "search_wrap", // 结果列表将在此容器中进行展示。
autoFitView: true, // 是否自动调整地图视野使绘制的 Marker点都处于视口的可见范围
});
});
ElMap = map;
}
defineExpose({
show,
close,
@@ -473,6 +500,20 @@ defineExpose({
position: absolute;
top: 10px;
left: 10px;
z-index: 3000;
.list {
background-color: #fff;
.item {
height: 40px;
line-height: 40px;
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
padding: 0 10px;
cursor: pointer;
&:hover {
background-color: #eee;
}
}
}
}
.search_wrap {

View File

@@ -4,7 +4,7 @@
<el-row :gutter="20">
<el-col :span="3">
<el-input
v-model="query.name"
v-model="state.query.name"
clearable
placeholder="请输入店铺名称"
style="width: 100%"
@@ -14,7 +14,7 @@
</el-col>
<el-col :span="3">
<el-input
v-model="query.account"
v-model="state.query.account"
clearable
placeholder="请输入商户号"
style="width: 100%"
@@ -23,11 +23,11 @@
/>
</el-col>
<el-col :span="3">
<el-select v-model="query.status" placeholder="请选择店铺状态" style="width: 100%">
<el-select v-model="state.query.status" placeholder="请选择店铺状态" style="width: 100%">
<el-option
:label="item.label"
:value="item.type"
v-for="item in status"
v-for="item in state.status"
:key="item.type"
/>
</el-select>
@@ -39,12 +39,10 @@
</el-row>
</div>
<div class="head-container">
<el-button type="primary" icon="el-icon-plus" @click="$refs.addShop.show()">
添加店铺
</el-button>
<el-button type="primary" icon="plus" @click="addShopShow">添加店铺</el-button>
</div>
<div class="head-container">
<el-table :data="tableData.list" v-loading="tableData.loading">
<el-table :data="state.tableData.list" v-loading="state.tableData.loading">
<el-table-column label="店铺信息" width="200">
<template v-slot="scope">
<div class="shop_info">
@@ -52,9 +50,11 @@
:src="scope.row.logo"
style="width: 50px; height: 50px; border-radius: 4px; background-color: #efefef"
>
<div class="img_error" slot="error">
<i class="icon el-icon-document-delete"></i>
</div>
<template #error>
<div class="img_error">
<i class="icon el-icon-document-delete"></i>
</div>
</template>
</el-image>
<div class="info">
<span>{{ scope.row.shopName }}</span>
@@ -102,11 +102,18 @@
</el-table-column>
<el-table-column label="操作" width="150">
<template v-slot="scope">
<el-link icon="edit" @click="$refs.addShop.show(scope.row)">编辑</el-link>
<el-link @click="addShopShow(scope.row)">
<el-icon><Edit /></el-icon>
编辑
</el-link>
<el-dropdown @command="dropdownClick">
<el-link icon="arrow-down">更多</el-link>
<el-dropdown-menu>
<template #dropdown>
<el-link>
更多
<el-icon><ArrowDown /></el-icon>
</el-link>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item :command="{ row: scope.row, command: 1 }">
三方配置
</el-dropdown-item>
@@ -114,8 +121,8 @@
<el-dropdown-item :command="3">前往店铺</el-dropdown-item>
<el-dropdown-item :command="4">重置密码</el-dropdown-item>
<el-dropdown-item divided :command="5">删除</el-dropdown-item>
</template>
</el-dropdown-menu>
</el-dropdown-menu>
</template>
</el-dropdown>
</template>
</el-table-column>
@@ -123,98 +130,98 @@
</div>
<div class="head-container">
<el-pagination
:total="tableData.total"
v-model:current-page="tableData.page"
:page-size="tableData.size"
:total="state.tableData.total"
v-model:current-page="state.tableData.page"
v-model:page-size="state.tableData.size"
:page-sizes="[10, 20, 30, 50, 100]"
@current-change="paginationChange"
layout="total, sizes, prev, pager, next, jumper"
layout="total, sizes , prev, pager ,next, jumper "
></el-pagination>
</div>
<addShop ref="addShop" @success="getTableData" />
<detailModal ref="detailModal" />
<addShop ref="refAddShop" @success="getTableData" />
<detailModal ref="refDetailModal" />
</div>
</template>
<script>
<script setup>
import dayjs from "dayjs";
import ShopApi from "@/api/account/shop";
import { ElNotification } from "element-plus";
import addShop from "./components/addShop.vue";
import detailModal from "./components/detailModal.vue";
export default {
components: { addShop, detailModal },
data() {
return {
dayjs,
query: {
name: "",
account: "",
status: "",
},
status: [
{
type: 1,
label: "开启",
},
{
type: 0,
label: "关闭",
},
],
tableData: {
list: [],
page: 1,
size: 10,
loading: false,
total: 0,
},
};
const refAddShop = ref(null);
function addShopShow(row) {
refAddShop.value.show(row);
}
const state = reactive({
query: {
name: "",
account: "",
status: "",
},
mounted() {
this.getTableData();
status: [
{
type: 1,
label: "开启",
},
{
type: 0,
label: "关闭",
},
],
tableData: {
list: [],
page: 1,
size: 10,
loading: false,
total: 0,
},
methods: {
dropdownClick(e) {
switch (e.command) {
case 1:
this.$refs.detailModal.show(e.row);
break;
default:
break;
}
},
// 重置查询
resetHandle() {
this.query.name = "";
this.query.account = "";
this.query.status = "";
this.getTableData();
},
// 分页回调
paginationChange(e) {
this.tableData.page = e;
this.getTableData();
},
// 获取商家列表
async getTableData() {
this.tableData.loading = true;
try {
const res = await ShopApi.getList({
page: this.tableData.page,
size: this.tableData.size,
shopName: this.query.name,
account: this.query.account,
status: this.query.status,
});
this.tableData.loading = false;
this.tableData.list = res.records;
this.tableData.total = res.totalRow;
} catch (error) {
console.log(error);
}
},
},
};
});
onMounted(() => {
getTableData();
});
const refDetailModal = ref(null);
function dropdownClick(e) {
switch (e.command) {
case 1:
refDetailModal.value.show(e.row);
break;
default:
break;
}
}
// 重置查询
function resetHandle() {
state.query.name = "";
state.query.account = "";
state.query.status = "";
getTableData();
}
// 分页回调
function paginationChange(e) {
state.tableData.page = e;
getTableData();
}
// 获取商家列表
async function getTableData() {
state.tableData.loading = true;
try {
const res = await ShopApi.getList({
page: state.tableData.page,
size: state.tableData.size,
shopName: state.query.name,
account: state.query.account,
status: state.query.status,
});
state.tableData.loading = false;
state.tableData.list = res.records;
state.tableData.total = res.totalRow * 1;
} catch (error) {
console.log(error);
}
}
</script>
<style scoped lang="scss">
@@ -229,4 +236,8 @@ export default {
padding-left: 4px;
}
}
.el-link {
min-height: 23px;
margin: 0 5px;
}
</style>