浏览代码

feat: 新增用户反馈页面及功能

ylong 3 天之前
父节点
当前提交
fb887e5467

+ 168 - 0
pages-sell/components/image-upload/index.vue

@@ -0,0 +1,168 @@
+<template>
+	<view class="image-upload-container">
+		<view class="upload-list">
+			<view class="upload-item" v-for="(item, index) in value" :key="index">
+				<u-image :src="item" width="160rpx" height="160rpx" border-radius="8"
+					@click="onPreview(index)"></u-image>
+				<view class="delete-btn" @click.stop="onDelete(index)">
+					<u-icon name="close" color="#ffffff" size="20"></u-icon>
+				</view>
+			</view>
+			<view class="upload-btn" v-if="value.length < maxCount" @click="onChoose" :style="{ backgroundColor: bgColor }"> 
+				<u-icon name="camera" size="48" color="#909399"></u-icon>
+				<text class="btn-text">图片/视频</text>
+			</view>
+		</view>
+		<view class="upload-tip">
+			<u-icon name="info-circle" size="24" color="#909399"></u-icon>
+			<text class="tip-text">上传“有效截图”可以让问题优先被发现哦!</text>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		name: 'ImageUpload',
+		props: {
+			value: {
+				type: Array,
+				default: () => []
+			},
+			maxCount: {
+				type: Number,
+				default: 9
+			},
+			bgColor: {
+				type: String,
+				default: '#fff'
+			}
+		},
+		methods: {
+			onChoose() {
+				uni.chooseImage({
+					count: this.maxCount - this.value.length,
+					success: async (res) => {
+						uni.showLoading({
+							title: '上传中...'
+						});
+						const tempFilePaths = res.tempFilePaths;
+						const uploadPromises = tempFilePaths.map(path => {
+							return this.uploadFeedbackFileAjax(path);
+						});
+
+						try {
+							const results = await Promise.all(uploadPromises);
+							const newList = [...this.value, ...results];
+							this.$emit('input', newList);
+							uni.hideLoading();
+						} catch (e) {
+							uni.hideLoading();
+							uni.showToast({
+								title: '上传失败',
+								icon: 'none'
+							});
+							console.error(e);
+						}
+					}
+				});
+			},
+			uploadFeedbackFileAjax: (filePath) => {
+				return new Promise((resolve, reject) => {
+					uni.uploadFile({
+						url: uni.$u.http.config.baseUrl + '/token/shop/feedback/fileUpload',
+						filePath: filePath,
+						name: 'file',
+						header: {
+							'Authorization': uni.getStorageSync('token') || '',
+						},
+						success: (res) => {
+							const data = JSON.parse(res.data);
+							if (data.code == 200) {
+								resolve(data.data);
+							} else {
+								reject(data.msg);
+							}
+						},
+						fail: (err) => {
+							reject(err);
+						}
+					});
+				});
+			},
+
+			onDelete(index) {
+				const newList = [...this.value];
+				newList.splice(index, 1);
+				this.$emit('input', newList);
+			},
+			onPreview(index) {
+				uni.previewImage({
+					urls: this.value,
+					current: index
+				});
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.image-upload-container {
+		width: 100%;
+	}
+
+	.upload-list {
+		display: flex;
+		flex-wrap: wrap;
+		margin: 0 -10rpx;
+	}
+
+	.upload-item,
+	.upload-btn {
+		width: 160rpx;
+		height: 160rpx;
+		margin: 10rpx;
+		border-radius: 8rpx;
+		position: relative;
+	}
+
+	.upload-btn {
+		background-color: #f4f5f6;
+		display: flex;
+		flex-direction: column;
+		align-items: center;
+		justify-content: center;
+
+		.btn-text {
+			font-size: 24rpx;
+			color: #909399;
+			margin-top: 10rpx;
+		}
+	}
+
+	.delete-btn {
+		position: absolute;
+		top: 0;
+		right: 0;
+		width: 40rpx;
+		height: 40rpx;
+		background-color: rgba(0, 0, 0, 0.5);
+		border-bottom-left-radius: 8rpx;
+		border-top-right-radius: 8rpx;
+		display: flex;
+		align-items: center;
+		justify-content: center;
+		z-index: 1;
+	}
+
+	.upload-tip {
+		margin-top: 20rpx;
+		display: flex;
+		align-items: center;
+
+		.tip-text {
+			font-size: 24rpx;
+			color: #909399;
+			margin-left: 10rpx;
+		}
+	}
+</style>

+ 1 - 1
pages-sell/components/sell-container/index.vue

@@ -258,7 +258,7 @@
 			},
 			//获取首页装修信息
 			getIndexCateInfo() {
-				uni.$u.http.get('/token/shop/showIndex/getIndexCateInfo?position=diamond_area').then(res => {
+				uni.$u.http.get('/token/shop/showIndex/getIndexCateInfo').then(res => {
 					console.log(res)
 					if (res.code == 200) {
 						console.log(res.data)

+ 162 - 0
pages-sell/pages/feedback.vue

@@ -0,0 +1,162 @@
+<template>
+	<view class="feedback-page">
+		<u-navbar title="反馈" :is-back="true" :border-bottom="false"></u-navbar>
+
+		<view class="content">
+			<view class="card">
+				<view class="card-title">您在搜索书籍环节遇到了哪些问题?</view>
+				<view class="input-container">
+					<u-input v-model="description" type="textarea" placeholder="填写的信息越全,问题越可有效解决哦~" :border="false"
+						:height="300" :auto-height="false" :custom-style="{ padding: '0' }" show-limit-word />
+					<view class="upload-wrapper">
+						<image-upload v-model="fileList" :max-count="3"></image-upload>
+					</view>
+				</view>
+			</view>
+
+			<view class="card">
+				<view class="card-title">您愿意为本次体验打多少分?</view>
+				<view class="score-wrapper">
+					<view class="score-text">体验糟糕</view>
+					<view class="score-options">
+						<view class="score-item" v-for="i in 5" :key="i" :class="{ active: score === i + 1 }"
+							@click="score = i + 1">
+							{{ i + 1 }}
+						</view>
+					</view>
+					<view class="score-text">体验超赞</view>
+				</view>
+			</view>
+		</view>
+
+		<view class="footer">
+			<u-button type="success" shape="circle"
+				:custom-style="{ backgroundColor: '#38C148', height: '88rpx', fontSize: '32rpx' }" @click="onSubmit">
+				提交
+			</u-button>
+		</view>
+	</view>
+</template>
+
+<script>
+	import ImageUpload from '@/pages-sell/components/image-upload/index.vue';
+
+	export default {
+		components: {
+			ImageUpload
+		},
+		data() {
+			return {
+				description: '',
+				fileList: [],
+				score: 5
+			};
+		},
+		methods: {
+			onSubmit() {
+				if (!this.description.trim() && this.fileList.length === 0) {
+					this.$u.toast('请填写问题描述或上传图片');
+					return;
+				}
+
+				const params = {
+					description: this.description,
+					fileUrlList: this.fileList,
+					score: this.score
+				};
+				uni.$u.http.post('/token/shop/feedback/submit', params).then(res => {
+					this.$u.toast('提交成功');
+					setTimeout(() => {
+						uni.navigateBack();
+					}, 1500);
+				}).catch(err => {
+					// 错误处理通常由拦截器完成,这里可以根据需要补充
+				});
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.feedback-page {
+		min-height: 100vh;
+		background-color: #f8f8f8;
+	}
+
+	.content {
+		padding: 20rpx 30rpx;
+	}
+
+	.card {
+		background-color: #ffffff;
+		border-radius: 16rpx;
+		padding: 30rpx;
+		margin-bottom: 30rpx;
+
+		.card-title {
+			font-size: 30rpx;
+			font-weight: bold;
+			color: #333333;
+			margin-bottom: 30rpx;
+		}
+	}
+
+	.input-container {
+		background-color: #f8f8f8;
+		border-radius: 12rpx;
+		padding: 20rpx;
+
+		.upload-wrapper {
+			margin-top: 20rpx;
+		}
+	}
+
+	.score-wrapper {
+		display: flex;
+		justify-content: space-between;
+		align-items: center;
+		padding: 0 10rpx;
+
+		.score-text {
+			font-size: 24rpx;
+			color: #999999;
+		}
+
+		.score-options {
+			display: flex;
+			gap: 20rpx;
+		}
+
+		.score-item {
+			width: 60rpx;
+			height: 60rpx;
+			background-color: #ffffff;
+			border: 2rpx solid #e5e5e5;
+			border-radius: 8rpx;
+			display: flex;
+			align-items: center;
+			justify-content: center;
+			font-size: 30rpx;
+			color: #333333;
+			font-weight: bold;
+
+			&.active {
+				background-color: #d7f4da; // Light green background for active
+				border-color: #38C148;
+				color: #38C148;
+			}
+		}
+	}
+
+	.footer {
+		position: fixed;
+		bottom: 0;
+		left: 0;
+		width: 100%;
+		background-color: #ffffff;
+		padding: 20rpx 30rpx;
+		padding-bottom: calc(20rpx + constant(safe-area-inset-bottom));
+		padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
+		box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.05);
+	}
+</style>

+ 7 - 2
pages-sell/pages/search-result.vue

@@ -6,9 +6,9 @@
 
         <!-- Custom Header -->
         <view class="search-bar-wrapper">
-            <u-search style="width: 100%;" v-model="keyword" placeholder="书名/作者/ISBN" :show-action="true" action-text="取消"
+            <u-search style="width: 100%;" v-model="keyword" placeholder="书名/作者/ISBN" :show-action="true" action-text="反馈"
                 :action-style="{ color: '#fff' }" bg-color="#fff" shape="round" :clearabled="true"
-                search-icon="/pages-sell/static/search-icon.png" @custom="goBack" @search="onSearch"></u-search>
+                search-icon="/pages-sell/static/search-icon.png" @custom="toFeedback" @search="onSearch"></u-search>
         </view>
 
         <!-- Banner -->
@@ -118,6 +118,11 @@ export default {
         goBack() {
             uni.navigateBack();
         },
+        toFeedback() {
+            uni.navigateTo({
+                url: '/pages-sell/pages/feedback'
+            });
+        },
         onSearch(val) {
             console.log('Search:', val);
         },

+ 6 - 0
pages.json

@@ -382,6 +382,12 @@
                         "navigationStyle": "custom"
                     }
                 },
+                {
+                    "path": "pages/feedback",
+                    "style": {
+                        "navigationStyle": "custom"
+                    }
+                },
                 {
                     "path": "pages/topic",
                     "style": {