You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1081 lines
27 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<view class="container">
<HeaderCustom
:title="props.id ? '门店修改' : '门店新增'"
:showBack="true"
></HeaderCustom>
<div class="contentBox">
<div class="title">基本信息</div>
<div class="formBox">
<uv-form
labelPosition="left"
:model="formField"
labelWidth="100"
:rules="rules"
ref="form"
>
<uv-form-item required label="门店名称" prop="store_name">
<uv-input
v-model="formField.store_name"
border="none"
placeholder="请输入门店名称"
></uv-input>
</uv-form-item>
<uv-form-item label="地区" prop="location" @click="openAddressPicker">
<uv-input
v-model="formField.location"
readonly
border="none"
placeholder="请选择地区"
></uv-input>
<uv-picker
ref="addressPicker"
:columns="addressList"
keyName="name"
@change="addressChange"
@confirm="confirm"
></uv-picker>
</uv-form-item>
<uv-form-item label="地址详情" prop="address">
<uv-input
v-model="formField.address"
border="none"
placeholder="请输入地址详情"
></uv-input>
</uv-form-item>
<uv-form-item label="工作时间" prop="business_hours">
<uv-input
v-model="formField.business_hours"
border="none"
placeholder="请输入工作时间"
></uv-input>
</uv-form-item>
<uv-form-item label="联系电话" prop="contact_phone">
<uv-input
v-model="formField.contact_phone"
border="none"
placeholder="请输入联系电话"
></uv-input>
</uv-form-item>
<uv-form-item label="分类标签" prop="tags">
<uv-input
v-model="formField.tags"
border="none"
placeholder="请输入分类标签,多个以,分隔"
></uv-input>
</uv-form-item>
<uv-form-item label="经度" prop="longitude" @click="chooseAdress">
<uv-input
readonly
v-model="formField.longitude"
border="none"
placeholder="点击选择经纬度"
></uv-input>
</uv-form-item>
<uv-form-item label="纬度" prop="latitude" @click="chooseAdress">
<uv-input
readonly
v-model="formField.latitude"
border="none"
placeholder="点击选择经纬度"
></uv-input>
</uv-form-item>
<uv-form-item label="门店主图" prop="main_image">
<div class="main_imageBox">
<div class="closeBox">
<uv-icon
color="#f12323"
v-if="formField.main_image"
name="close-circle"
stop
size="28"
@click="() => (formField.main_image = '')"
></uv-icon>
</div>
<img
@click="previewImage(formField.main_image)"
v-if="formField.main_image"
class="img"
:src="formField.main_image"
alt=""
/>
<UploadFile
v-else
ref="imageUploadRef"
:accept="'.jpg,.jpeg,.png,.gif'"
:maxSize="5"
@onSuccess="handleUploadSuccess"
/>
</div>
</uv-form-item>
</uv-form>
</div>
<div class="title">上传视频</div>
<div class="videoBox">
<div class="video_content">
<div class="closeBox">
<uv-icon
color="#f12323"
v-if="formField.video_intro"
name="close-circle"
stop
size="28"
@click="() => (formField.video_intro = '')"
></uv-icon>
</div>
<video
v-if="formField.video_intro"
class="video"
:src="formField.video_intro"
controls
></video>
<UploadFile
v-else
ref="videoUploadRef"
:accept="'.mp4,.avi,.mov'"
:maxSize="50"
@onSuccess="handleVideoUploadSuccess"
/>
</div>
</div>
<div class="title">
产品图片
<text class="sort-tip" v-if="productImgList.length > 1">
长按图片可排序
</text>
</div>
<div class="detail_img_box">
<div
class="detail_img_item"
v-for="(item, index) in productImgList"
:key="item"
@longpress="showSortMenu(index)"
>
<div class="closeBox">
<uv-icon
color="#f12323"
name="close-circle"
stop
size="28"
@click.stop="
() =>
(productImgList = productImgList.filter(
(ele) => ele !== item
))
"
></uv-icon>
</div>
<img
@click="previewImageArr(productImgList, index)"
class="img"
:src="item"
alt=""
/>
<div class="sort-index">{{ index + 1 }}</div>
</div>
<div class="detail_img_item">
<UploadFile
:accept="'.jpg,.jpeg,.png,.gif'"
:maxSize="5"
@onSuccess="productImgSuccess"
/>
</div>
</div>
<div class="title">
企业资质
<text class="sort-tip" v-if="businessImgList.length > 1">
长按图片可排序
</text>
</div>
<div class="detail_img_box">
<div
class="detail_img_item"
v-for="(item, index) in businessImgList"
:key="item"
@longpress="showBusinessSortMenu(index)"
>
<div class="closeBox">
<uv-icon
color="#f12323"
name="close-circle"
stop
size="28"
@click.stop="
() =>
(businessImgList = businessImgList.filter(
(ele) => ele !== item
))
"
></uv-icon>
</div>
<img
@click="previewImageArr(businessImgList, index)"
class="img"
:src="item"
alt=""
/>
<div class="sort-index">{{ index + 1 }}</div>
</div>
<div class="detail_img_item">
<UploadFile
:accept="'.jpg,.jpeg,.png,.gif'"
:maxSize="5"
@onSuccess="businessImgSuccess"
/>
</div>
</div>
<div class="title">
产品资料PDF
<text class="sort-tip" v-if="pdfList.length > 1">长按文件可排序</text>
</div>
<div class="pdf_box">
<div
class="pdf_item"
v-for="(item, index) in pdfList"
:key="item.url"
@click="openPdf(item)"
@longpress="showPdfSortMenu(index)"
>
<div class="pdf_icon">
<uv-icon name="file-text-fill" color="#2979ff" size="48"></uv-icon>
</div>
<div class="nameBox">
<div class="name">{{ item.name }}</div>
<div class="pdfInfoBox">
<div class="size">{{ item.size }}MB</div>
<div class="date">{{ item.date }}</div>
<!-- <div class="time">{{item.time}}</div> -->
</div>
</div>
<div class="sort-index pdf-sort-index">{{ index + 1 }}</div>
<div class="pdfCloseBox">
<uv-icon
color="#f12323"
name="close-circle"
stop
size="28"
@click.stop="
() => (pdfList = pdfList.filter((ele) => ele.url !== item.url))
"
></uv-icon>
</div>
</div>
<div class="pdf_item">
<UploadFile :accept="'pdf'" :maxSize="50" @onSuccess="pdfSuccess" />
</div>
</div>
<uv-button
type="primary"
text="提交"
customStyle="margin-top: 10px"
@click="submit"
></uv-button>
</div>
</view>
</template>
<script setup>
import { computed, onMounted, ref } from "vue";
import { onShow } from "@dcloudio/uni-app";
import { useAuthStore, useUserStore } from "@/store";
import { regions } from "/static/json/regions.js";
import HeaderCustom from "/components/HeaderCustom/HeaderCustom.vue";
import RPC from "@/utils/request";
import dayjs from "dayjs";
const props = defineProps({
id: [String],
});
console.log("props", props.id);
if (props.id && props.id !== "null") {
RPC.post("/store/detail", {
id: Number(props.id),
}).then((res) => {
formField.value = {
...res.store,
};
productImgList.value = res.store.product_images
? res.store.product_images.split(",")
: [];
businessImgList.value = res.store.product_images
? res.store.qualification_images.split(",")
: [];
pdfList.value = res.store.product_pdfs
? JSON.parse(res.store.product_pdfs)
: [];
});
}
const previewImage = (url) => {
uni.previewImage({
urls: [url],
});
};
const previewImageArr = (arr, index) => {
uni.previewImage({
urls: arr,
current: index,
});
};
const openPdf = (item) => {
uni.downloadFile({
url: item.url,
success: function (res) {
var filePath = res.tempFilePath;
uni.openDocument({
filePath: filePath,
showMenu: true,
success: function (res) {
console.log("打开文档成功");
},
});
},
});
};
const productImgList = ref([]);
const businessImgList = ref([]);
const pdfList = ref([
// {url:'1',name:'同理电梯产品介绍1',size:8.82,date:'07-19',time:'12:25'},
// {url:'32',name:'同理电梯产品介绍2',size:8.82,date:'07-19',time:'12:25'},
// {url:'3',name:'同理电梯产品介绍3',size:8.82,date:'07-19',time:'12:25'},
]);
// 排序相关数据
const currentSortIndex = ref(-1);
const productImgSuccess = (result) => {
console.log("上传成功:", result);
productImgList.value.push(result.response.result.fullUrl);
console.log(productImgList.value);
// 这里可以处理服务器返回的数据
// result.response 包含服务器返回的数据
// result.fileInfo 包含文件信息
uni.showToast({
title: "上传成功",
icon: "success",
});
};
const businessImgSuccess = (result) => {
console.log("上传成功:", result);
businessImgList.value.push(result.response.result.fullUrl);
console.log(businessImgList.value);
// 这里可以处理服务器返回的数据
// result.response 包含服务器返回的数据
// result.fileInfo 包含文件信息
uni.showToast({
title: "上传成功",
icon: "success",
});
};
const pdfSuccessList = ref([]);
const pdfSuccess = (result) => {
console.log("上传成功:", result);
pdfList.value.push({
url: result.response.result.fullUrl,
name: result.fileInfo.name,
size: (result.fileInfo.size / 1024 / 1024).toFixed(2),
date: dayjs().format("YYYY-MM-DD HH:mm"),
// time:dayjs().format('HH:mm')
});
console.log(pdfList.value);
// 这里可以处理服务器返回的数据
// result.response 包含服务器返回的数据
// result.fileInfo 包含文件信息
uni.showToast({
title: "上传成功",
icon: "success",
});
};
const handleUploadSuccess = (result) => {
console.log("上传成功:", result);
formField.value.main_image = result.response.result.fullUrl;
console.log(formField.value.main_image);
// 这里可以处理服务器返回的数据
// result.response 包含服务器返回的数据
// result.fileInfo 包含文件信息
uni.showToast({
title: "上传成功",
icon: "success",
});
};
const handleVideoUploadSuccess = (result) => {
console.log("上传成功:", result);
formField.value.video_intro = result.response.result.fullUrl;
console.log(formField.value.video_intro);
// 这里可以处理服务器返回的数据
// result.response 包含服务器返回的数据
// result.fileInfo 包含文件信息
uni.showToast({
title: "上传成功",
icon: "success",
});
};
const authStore = useAuthStore();
const userStore = useUserStore();
function copyText(text) {
uni.setClipboardData({
data: text,
success: function () {
uni.showToast({
title: "复制成功",
icon: "success",
});
},
fail: function () {
uni.showToast({
title: "复制失败",
icon: "none",
});
},
});
}
const chooseAdress = () => {
uni.chooseLocation({
success: (res) => {
formField.value.longitude = res.longitude;
formField.value.latitude = res.latitude;
},
fail: (err) => {
console.error("选择位置失败:", err);
// 检查是否是因为权限问题
if (err.errMsg.includes("auth deny")) {
uni.showModal({
title: "提示",
content: "需要您授权使用位置信息",
confirmText: "去设置",
success: (res) => {
if (res.confirm) {
uni.openSetting({
success: (settingRes) => {
console.log("设置结果:", settingRes);
},
});
}
},
});
} else {
uni.showToast({
title: "选择位置失败",
icon: "none",
});
}
},
});
};
onShow(() => {
if (!authStore?.isLoggedIn) {
uni.switchTab({
url: `/pages/index/index`,
});
uni.showToast({
title: "请先登录",
icon: "none",
});
return;
}
});
const form = ref(null);
const formField = ref({
store_name: "",
latitude: "",
longitude: "",
});
const pickType = (type) => {
const index = formField.value.type.findIndex((item) => item === type);
if (index >= 0) {
formField.value.type.splice(index, 1);
} else {
formField.value.type.push(type);
}
};
const rules = {
store_name: {
type: "string",
required: true,
message: "请填写门店名称",
trigger: ["blur", "change"],
},
latitude: {
type: "number",
required: true,
message: "请选择经纬度",
trigger: ["blur", "change"],
},
longitude: {
type: "number",
required: true,
message: "请选择经纬度",
trigger: ["blur", "change"],
},
};
const submit = () => {
// 如果有错误会在catch中返回报错信息数组校验通过则在then中返回true
form.value
.validate()
.then((res) => {
// uni.showToast({
// icon: "success",
// title: "校验成功",
// });
const store = {
...formField.value,
product_images: productImgList.value.join(","),
qualification_images: businessImgList.value.join(","),
product_pdfs: JSON.stringify(pdfList.value),
};
console.log("store", store, productImgList.value);
if (props.id) {
RPC.post("/store/update", {
store,
id: Number(props.id),
}).then((res) => {
uni.showToast({
icon: "success",
title: "操作成功",
});
setTimeout(() => {
uni.navigateBack();
}, 1000);
});
} else {
RPC.post("/store/create", {
store,
}).then((res) => {
uni.showToast({
icon: "success",
title: "操作成功",
});
setTimeout(() => {
uni.navigateBack();
}, 1000);
});
}
})
.catch((errors) => {
// uni.showToast({
// icon: 'error',
// title: '校验失败'
// })
});
};
const provinces = ref([]);
const citys = ref([]);
const areas = ref([]);
const addressList = computed(() => [provinces.value, citys.value, areas.value]);
const addressPicker = ref(null);
const pickerValue = ref(["0", "0", "0"]);
const defaultValue = ref(["3442", "1", "2"]);
const addressChange = (e) => {
const { columnIndex, index, indexs } = e;
// 改变了省
if (columnIndex === 0) {
citys.value = provinces.value[index]?.children || [];
areas.value = citys.value[0]?.children || [];
addressPicker.value.setIndexs([index, 0, 0], true);
} else if (columnIndex === 1) {
areas.value = citys.value[index]?.children || [];
addressPicker.value.setIndexs(indexs, true);
}
};
const confirm = (e) => {
// console.log('确认选择的地区:', e);
// uni.showToast({
// icon: 'none',
// title: `${e.value[0].name}/${e.value[1].name}/${e.value[2].name}`
// })
formField.value.location = `${e.value[0].name}/${e.value[1].name}/${e.value[2].name}`;
};
const handlePickValueDefault = () => {
pickerValue.value[0] = provinces.value.findIndex(
(item) => Number(item.id) == defaultValue.value[0]
);
// 设置市
citys.value = provinces.value[pickerValue.value[0]]?.children || [];
pickerValue.value[1] = citys.value.findIndex(
(item) => Number(item.id) == defaultValue.value[1]
);
// 设置区
areas.value = citys.value[pickerValue.value[1]]?.children || [];
pickerValue.value[2] = areas.value.findIndex(
(item) => Number(item.id) == defaultValue.value[2]
);
// 重置下位置
addressPicker.value.setIndexs(
[pickerValue.value[0], pickerValue.value[1], pickerValue.value[2]],
true
);
};
const openAddressPicker = () => {
addressPicker.value.open();
handlePickValueDefault();
};
const getData = () => {
provinces.value = regions.sort((left, right) =>
Number(left.code) > Number(right.code) ? 1 : -1
);
handlePickValueDefault();
};
// 显示排序菜单
const showSortMenu = (index) => {
if (productImgList.value.length <= 1) return;
currentSortIndex.value = index;
// 生成菜单选项
const itemList = productImgList.value.map((_, i) => `移动到第${i + 1}`);
uni.showActionSheet({
itemList: ["向前移动", "向后移动", "移动到开头", "移动到末尾"],
success: (res) => {
const tapIndex = res.tapIndex;
switch (tapIndex) {
case 0: // 向前移动
if (index > 0) {
moveImage(index, index - 1);
}
break;
case 1: // 向后移动
if (index < productImgList.value.length - 1) {
moveImage(index, index + 1);
}
break;
case 2: // 移动到开头
if (index > 0) {
moveImage(index, 0);
}
break;
case 3: // 移动到末尾
if (index < productImgList.value.length - 1) {
moveImage(index, productImgList.value.length - 1);
}
break;
}
},
fail: () => {
currentSortIndex.value = -1;
},
});
// 添加震动反馈
uni.vibrateShort({
type: "medium",
});
};
// 移动图片位置的方法
const moveImage = (fromIndex, toIndex) => {
if (fromIndex === toIndex) return;
const newList = [...productImgList.value];
const draggedItem = newList[fromIndex];
// 移除拖拽的元素
newList.splice(fromIndex, 1);
// 在新位置插入
newList.splice(toIndex, 0, draggedItem);
productImgList.value = newList;
currentSortIndex.value = -1;
uni.showToast({
title: "排序成功",
icon: "success",
duration: 1000,
});
};
// 显示企业资质排序菜单
const showBusinessSortMenu = (index) => {
if (businessImgList.value.length <= 1) return;
currentSortIndex.value = index;
uni.showActionSheet({
itemList: ["向前移动", "向后移动", "移动到开头", "移动到末尾"],
success: (res) => {
const tapIndex = res.tapIndex;
switch (tapIndex) {
case 0: // 向前移动
if (index > 0) {
moveBusinessImage(index, index - 1);
}
break;
case 1: // 向后移动
if (index < businessImgList.value.length - 1) {
moveBusinessImage(index, index + 1);
}
break;
case 2: // 移动到开头
if (index > 0) {
moveBusinessImage(index, 0);
}
break;
case 3: // 移动到末尾
if (index < businessImgList.value.length - 1) {
moveBusinessImage(index, businessImgList.value.length - 1);
}
break;
}
},
fail: () => {
currentSortIndex.value = -1;
},
});
// 添加震动反馈
uni.vibrateShort({
type: "medium",
});
};
// 移动企业资质图片位置的方法
const moveBusinessImage = (fromIndex, toIndex) => {
if (fromIndex === toIndex) return;
const newList = [...businessImgList.value];
const draggedItem = newList[fromIndex];
// 移除拖拽的元素
newList.splice(fromIndex, 1);
// 在新位置插入
newList.splice(toIndex, 0, draggedItem);
businessImgList.value = newList;
currentSortIndex.value = -1;
uni.showToast({
title: "排序成功",
icon: "success",
duration: 1000,
});
};
// 显示PDF排序菜单
const showPdfSortMenu = (index) => {
if (pdfList.value.length <= 1) return;
currentSortIndex.value = index;
uni.showActionSheet({
itemList: ["向前移动", "向后移动", "移动到开头", "移动到末尾"],
success: (res) => {
const tapIndex = res.tapIndex;
switch (tapIndex) {
case 0: // 向前移动
if (index > 0) {
movePdf(index, index - 1);
}
break;
case 1: // 向后移动
if (index < pdfList.value.length - 1) {
movePdf(index, index + 1);
}
break;
case 2: // 移动到开头
if (index > 0) {
movePdf(index, 0);
}
break;
case 3: // 移动到末尾
if (index < pdfList.value.length - 1) {
movePdf(index, pdfList.value.length - 1);
}
break;
}
},
fail: () => {
currentSortIndex.value = -1;
},
});
// 添加震动反馈
uni.vibrateShort({
type: "medium",
});
};
// 移动PDF位置的方法
const movePdf = (fromIndex, toIndex) => {
if (fromIndex === toIndex) return;
const newList = [...pdfList.value];
const draggedItem = newList[fromIndex];
// 移除拖拽的元素
newList.splice(fromIndex, 1);
// 在新位置插入
newList.splice(toIndex, 0, draggedItem);
pdfList.value = newList;
currentSortIndex.value = -1;
uni.showToast({
title: "排序成功",
icon: "success",
duration: 1000,
});
};
onMounted(() => {
getData();
});
</script>
<style lang="scss" scoped>
.container {
font-size: 14px;
min-height: 100vh;
background-color: #f8f8f8;
}
.contentBox {
padding: 10px;
}
.title {
font-size: 16px;
font-weight: 700;
margin-bottom: 5px;
display: flex;
align-items: center;
justify-content: space-between;
.sort-tip {
font-size: 12px;
color: #999;
font-weight: normal;
}
}
.main_imageBox {
position: relative;
width: 180px;
height: 60px;
.img {
max-width: 100%;
max-height: 100%;
}
}
.closeBox {
position: absolute;
top: 0;
right: 0;
z-index: 999;
}
.formBox {
margin-bottom: 14px;
:deep(.uv-form) {
background-color: #fff;
padding: 10px;
border-radius: 10px;
.uv-form-item {
background-color: #f4f4f4;
margin-bottom: 10px;
border-radius: 10px;
padding-left: 20px;
}
}
.type_select_box {
display: flex;
flex-wrap: wrap;
gap: 15px;
padding-right: 20px;
margin-top: 10px;
.item {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
background-color: #fff;
font-size: 12px;
width: 90px;
height: 70px;
border-radius: 6px;
.img {
width: 20px;
height: 20px;
margin-bottom: 6px;
}
&.selected {
border: 1px solid #3f70ff;
background-color: rgba(63, 112, 255, 0.1);
}
}
}
}
.infoBox {
margin-top: 20px;
padding: 10px 5vw;
background-color: #fff;
border-radius: 10px;
font-size: 16px;
.line {
display: flex;
margin: 10px 0;
.key {
width: 70px;
font-weight: 700;
}
}
}
.videoBox {
background-color: #fff;
padding: 10px;
border-radius: 10px;
margin-bottom: 14px;
.video_content {
position: relative;
height: 100px;
}
.video {
width: 100%;
height: 100%;
}
}
.detail_img_box {
background-color: #fff;
padding: 10px;
border-radius: 10px;
margin-bottom: 14px;
display: flex;
flex-wrap: wrap;
.detail_img_item {
width: 90px;
height: 90px;
margin-right: 16px;
border-radius: 5px;
overflow: hidden;
position: relative;
margin-bottom: 10px;
transition: all 0.2s ease;
.img {
max-width: 100%;
max-height: 100%;
}
.sort-index {
position: absolute;
top: 2px;
left: 2px;
background-color: rgba(0, 0, 0, 0.7);
color: white;
font-size: 12px;
padding: 2px 6px;
border-radius: 10px;
min-width: 16px;
text-align: center;
}
&:active {
transform: scale(0.95);
}
}
}
.pdf_box {
background-color: #fff;
padding: 10px;
border-radius: 10px;
margin-bottom: 14px;
.pdf_item {
display: flex;
border-bottom: 1px solid #ccc;
display: flex;
justify-content: center;
align-items: center;
padding: 10px 0;
position: relative;
transition: all 0.2s ease;
&:active {
transform: scale(0.98);
background-color: #f5f5f5;
}
.pdf_icon {
display: flex;
align-items: center;
justify-content: center;
width: 16%;
margin-right: 10px;
}
.nameBox {
flex: 1;
.name {
font-weight: 700;
margin-bottom: 4px;
}
.pdfInfoBox {
font-size: 12px;
color: #aaa;
display: flex;
align-items: center;
.date {
margin: 0 14px;
}
}
}
.pdf-sort-index {
position: absolute;
top: 8px;
left: 8px;
background-color: rgba(0, 0, 0, 0.7);
color: white;
font-size: 12px;
padding: 2px 6px;
border-radius: 10px;
min-width: 16px;
text-align: center;
}
}
}
</style>