Files
wdsxh/pages/member/product/edit.vue
2026-04-29 15:33:58 +08:00

492 lines
12 KiB
Vue
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!-- +----------------------------------------------------------------------
| 麦沃德科技赋能开发者助力商协会发展
+----------------------------------------------------------------------
| Copyright (c) 20172024 www.wdsxh.cn All rights reserved.
+----------------------------------------------------------------------
| 沃德商协会系统并不是自由软件不加密并不代表开源未经许可不可自由转售和商用
+----------------------------------------------------------------------
| Author: MY WORLD Team <bd@maiwd.cn> www.maiwd.cn
+----------------------------------------------------------------------
| 添加/修改产品 开发者: 麦沃德科技-半夏
+---------------------------------------------------------------------- -->
<template>
<view class="container" :style="{ '--theme-color': themeColor }">
<!-- 标题栏 -->
<title-bar :showBack="true" :title="productId ? '修改产品' : '添加产品'"></title-bar>
<!-- 内容区 -->
<view class="container-main" v-if="loadEnd">
<!-- 表单 -->
<view class="main-form">
<!-- 产品图片 -->
<view class="form-item">
<view class="item-title required">
<text class="text">产品图片</text>
<text class="tips">图片建议尺寸1:1</text>
</view>
<view class="item-upload">
<view class="upload-image" v-for="(img, num) in selectImages" :key="num" @click="previewImage(num)">
<image class="image-select" :src="img" mode="aspectFill"></image>
<image class="image-delete" src="/static/delete.png" mode="aspectFit" @click.stop="deleteImage(num)"></image>
</view>
<view class="upload-image" v-if="selectImages.length < 9" @click="chooseImage()">
<view class="image-background"></view>
<view class="image-choose">
<view class="icon">
<image src="/static/camera.png" mode="aspectFit"></image>
</view>
<view class="text">上传图片</view>
</view>
</view>
</view>
</view>
<!-- 产品名称 -->
<view class="form-item">
<view class="item-title required">产品名称</view>
<view class="item-input">
<input class="input" type="text" v-model="formData.name" placeholder="请输入产品名称" placeholder-class="placeholder" />
</view>
</view>
<!-- 产品介绍 -->
<view class="form-item">
<view class="item-title">产品介绍</view>
<view class="item-input">
<sp-editor ref="spEditor" style="width: 100%;" :toolbar-config="toolbarConfig" @init="initEditor" @upinImage="upinImage" @overMax="overMax" @fullscreen="toEditor"></sp-editor>
</view>
</view>
</view>
<!-- 提交按钮 -->
<view class="main-footer">
<view class="footer-btn" @click="handleSubmit()">{{productId ? '修改' : '添加'}}</view>
<view class="safe-padding"></view>
</view>
</view>
</view>
</template>
<script>
import { mapState } from "vuex"
export default {
data() {
return {
// 加载完成
loadEnd: false,
// 产品id
productId: null,
// 表单数据
formData: {
images: "",
name: "",
content: "",
},
// 已选择图片
selectImages: [],
// 编辑器实例
editorIns: null,
// 编辑器配置
toolbarConfig: {
excludeKeys: ['direction', 'date', 'lineHeight', 'letterSpacing', 'listCheck', 'export'],
iconSize: '18px',
showFullscreen: true,
},
};
},
computed: {
...mapState({
themeColor: state => state.app.themeColor,
})
},
onLoad(option) {
if (option.id) {
this.productId = option.id
uni.showLoading({
title: "加载中"
})
this.getProductdDetails(() => {
uni.hideLoading()
this.loadEnd = true
})
} else {
this.loadEnd = true
}
},
onShow() {
let pages = getCurrentPages();
if (pages[pages.length - 1].$vm.editorContent) {
const result = pages[pages.length - 1].$vm.editorContent
this.editorIns.setContents({
html: result || ""
})
delete pages[pages.length - 1].$vm.editorContent;
}
},
methods: {
// 获取产品详情
getProductdDetails(fn) {
this.$util.request("member.product.editDetails", {
id: this.productId
}).then(res => {
if (fn) fn()
if (res.code == 1) {
this.formData = {
id: res.data.id,
images: res.data.images,
name: res.data.name,
content: res.data.content,
}
this.selectImages = this.splitImages(res.data.images)
} else {
uni.showToast({
title: res.msg,
icon: 'none'
})
}
}).catch(error => {
if (fn) fn()
console.error('获取产品详情', error)
})
},
// 字符串转数组格式图片
splitImages(images) {
try {
if (images) return images.split(',');
else return []
} catch (error) {
return [];
}
},
// 选择图片
chooseImage() {
// #ifdef MP-WEIXIN
uni.chooseMedia({
count: 9 - this.selectImages.length,
mediaType: ['image'],
sourceType: ['album', 'camera'],
sizeType: ['compressed'],
success: (res) => {
this.selectImages = [...this.selectImages, ...res.tempFiles.map(item => item.tempFilePath)]
}
})
// #endif
// #ifndef MP-WEIXIN
uni.chooseImage({
count: 9 - this.selectImages.length,
sourceType: ['album', 'camera'],
sizeType: ['compressed'],
success: (res) => {
this.selectImages = [...this.selectImages, ...res.tempFilePaths]
}
});
// #endif
},
// 删除图片
deleteImage(index) {
this.$delete(this.selectImages, index)
},
// 预览图片
previewImage(index) {
uni.previewImage({
urls: this.selectImages,
current: index,
})
},
// 超出最大内容限制
overMax() {
uni.showToast({
title: "输入内容已超过最大字数限制"
})
},
// 初始化编辑器
initEditor(editor) {
this.editorIns = editor
this.editorIns.setContents({
html: this.formData.content || ""
})
},
// 上传图片
upinImage(tempFiles, editorCtx) {
let imageList = []
// #ifdef MP-WEIXIN
imageList = tempFiles.map(item => item.tempFilePath)
// #endif
// #ifndef MP-WEIXIN
imageList = tempFiles.map(item => item.path)
// #endif
uni.showLoading({
title: '上传中请稍后',
mask: true
})
this.$util.uploadFileMultiple(imageList, [], 2).then(result => {
result.forEach((item) => {
editorCtx.insertImage({
src: item,
width: '80%',
success: () => {
uni.hideLoading()
}
})
});
}).catch(error => {
console.error('上传图片 ', error)
})
},
// 跳转编辑器页面
async toEditor() {
uni.showLoading({
title: "加载中",
mask: true,
})
const introduction = await this.$refs.spEditor.getHtml()
this.$store.commit('app/setEditorContent', introduction)
uni.navigateTo({
url: "/pages/member/product/editor",
animationType: "fade-in",
complete: () => {
uni.hideLoading()
},
})
},
// 添加/修改产品
async handleSubmit() {
if (!this.selectImages.length) {
uni.showToast({
title: "请上传产品图片",
icon: 'none',
duration: 2000
})
return
}
if (!this.formData.name) {
uni.showToast({
title: "请输入产品名称",
icon: 'none',
duration: 2000
})
return
}
uni.showLoading({
title: "提交中",
mask: true
})
try {
this.formData.content = await this.$refs.spEditor.getHtml()
} catch (error) {
this.formData.content = ""
}
const oldImages = this.splitImages(this.formData.images)
this.$util.uploadFileMultiple(this.selectImages, oldImages).then(result => {
this.formData.images = result
this.submitEvent()
}).catch(error => {
uni.hideLoading()
console.error('上传图片 ', error)
})
},
// 提交事件
submitEvent() {
var url = ""
if (this.productId) url = "member.product.edit"
else url = "member.product.add"
this.$util.request(url, this.formData).then(res => {
uni.hideLoading()
if (res.code == 1) {
uni.redirectTo({
url: `/pages/member/product/success?type=${this.productId ? 2 : 1}`
})
} else {
uni.showToast({
title: res.msg,
icon: 'none'
})
}
}).catch(error => {
uni.hideLoading()
console.error('添加/修改产品', error)
})
},
}
}
</script>
<style lang="scss">
.container {
.container-main {
padding-bottom: 112rpx;
.main-form {
padding: 32rpx 48rpx;
.form-item {
margin-top: 32rpx;
&:first-child {
margin-top: 0;
}
.item-title {
color: #5A5B6E;
font-size: 32rpx;
font-weight: 600;
line-height: 44rpx;
.tips {
font-size: 24rpx;
font-weight: 400;
}
&.required::before {
display: inline-block;
content: "*";
color: #E60012;
font-size: 32rpx;
font-weight: 600;
line-height: 44rpx;
}
}
.item-input {
margin-top: 32rpx;
display: flex;
align-items: center;
border-radius: 16rpx;
background: #ffffff;
.input {
color: #5A5B6E;
font-size: 28rpx;
height: 112rpx;
line-height: 112rpx;
flex: 1;
padding: 0 32rpx;
}
.textarea {
color: #5A5B6E;
font-size: 28rpx;
line-height: 40rpx;
flex: 1;
padding: 36rpx 32rpx;
width: 100%;
height: 240rpx;
}
.placeholder {
color: #ACADB7;
}
.icon {
width: 32rpx;
height: 32rpx;
padding-right: 32rpx;
}
}
.item-upload {
display: flex;
flex-wrap: wrap;
margin-top: 32rpx;
column-gap: 3.5%;
row-gap: 24rpx;
.upload-image {
position: relative;
width: 31%;
height: 0;
padding-top: 31%;
.image-select {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
border-radius: 10rpx;
}
.image-video {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
border-radius: 10rpx;
background: var(--theme-color);
padding: 56rpx;
}
.image-delete {
position: absolute;
top: -16rpx;
right: -16rpx;
width: 48rpx;
height: 48rpx;
}
.image-choose {
position: absolute;
top: 20rpx;
left: 20rpx;
right: 20rpx;
bottom: 20rpx;
z-index: 6;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
background: #ffffff;
border-radius: 10rpx;
.icon {
width: 80rpx;
height: 80rpx;
padding: 18rpx;
background: var(--theme-color);
border-radius: 50%;
}
.text {
margin-top: 16rpx;
color: var(--theme-color);
font-size: 28rpx;
line-height: 40rpx;
}
}
.image-background {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 1;
background: var(--theme-color);
opacity: 0.08;
}
}
}
}
}
.main-footer {
position: fixed;
left: 0;
right: 0;
bottom: 0;
z-index: 99;
padding: 12rpx 32rpx;
background: #ffffff;
border-top: 1rpx solid #F6F7FB;
.footer-btn {
color: #ffffff;
font-size: 32rpx;
line-height: 44rpx;
padding: 22rpx 24rpx;
border-radius: 16rpx;
background: var(--theme-color);
text-align: center;
}
}
}
}
</style>