Browse Source

新增反馈,修改却换区无中心点

Fly 1 month ago
parent
commit
3c62b12dd9

+ 4 - 0
api/common.js

@@ -0,0 +1,4 @@
+import http from "@/utils/request.js";
+
+// 获取业务字典树
+// export const uploadFile_Api = () => http.get('/dev/file/uploadHuaweiYunReturnUrl')

+ 25 - 0
api/feedback.js

@@ -0,0 +1,25 @@
+import http from "@/utils/request.js";
+
+// 获取业务字典树
+export const getDictTree_Api = (dictValue) => http.get('/dev/dict/listByDictValue' , {params:{dictValue} })
+// 验证码
+export const getPicCaptcha_Api = () => http.get('/h5/auth/b/getPicCaptcha' )
+// 提交反馈
+export const addMapuserfeedback_Api= (data) => http.post('/h5/biz/mapuserfeedback/add', data )
+ 
+
+
+  
+ 
+// // 获取地图列表
+// export const getMapList_Api = (params) => http.get('/h5/biz/map/list' , {params:params})
+
+
+// // 获取地图类型
+// export const getMaptypeList_Api = (params) => http.get('/h5/biz/maptype/list' , {params:params})
+
+
+// // 获取地图点位服务
+// export const getMapMatterList_Api = (params) => http.get('/h5/biz/map/matterList' , {params:params})
+// // 获取地图点位服务
+// export const getMapMatterListH6_Api = (params) => http.get('/h6/biz/map/matterList' , {params:params})

+ 3 - 0
components/tiandituMap/tiandituMap.vue

@@ -7,6 +7,9 @@
 			<view class="position" @click.stop="$emit('openHint')">
 				<image style="width: 100%;height: 100%;" src="@/static/images/hint.png" mode="aspectFit"></image>
 			</view>
+			<view class="position" @click.stop="$emit('openFeedback')">
+				<image style="width: 100%;height: 100%;" src="@/static/images/feedback.png" mode="aspectFit"></image>
+			</view>
 		</view>
 	</view>
 </template>

+ 11 - 10
config/index.js

@@ -2,19 +2,20 @@ export default {
 	// baseURL: "https://ditu.qqzzhou.com/oa",
 	baseURL: "http://zwdt.wuhan-cloud.com:10030/oa", 
 	// baseURL: "http://192.168.0.78:8282", 
-	
-	
-	
-	
+
+
+
+
 	// 高德
 	// key:'f4226f3eb5a974d153652665b64b497c',
- 
+
 	// 天地图
 	// tianKey:'1edd9c001a8425cb93631398109d5ab2',
-	tianKey:'d131bbacd0a5428f6781fa4e11871196',
-	
+	tianKey: 'd131bbacd0a5428f6781fa4e11871196',
+
 	// h5 已废弃,禁止修改
-	type:'H6' , // H5 / H6,  web端打包需要修改 manifest.json web配置的基础运行路径
-	
-	isGetLocation:false
+	type: 'H6', // H5 / H6,  web端打包需要修改 manifest.json web配置的基础运行路径
+
+	isGetLocation: false, // 是否显示定位
+	isFeedback: false,		// 是否显示反馈
 }

+ 1 - 0
main.js

@@ -10,6 +10,7 @@ import './uni.promisify.adaptor'
 
 import uView from "uview-ui";
 Vue.use(uView);
+uni.$u.config.unit = 'rpx'
 
 Vue.prototype.$config = $config
 Vue.config.productionTip = false

+ 6 - 0
pages.json

@@ -21,6 +21,12 @@
 			"style": {
 				"navigationBarTitleText": "政务地图"
 			}
+		},
+		{
+			"path": "pages/feedback/feedback",
+			"style": {
+				"navigationBarTitleText": "反馈"
+			}
 		}
 
 

+ 77 - 0
pages/feedback/SelectClassify.vue

@@ -0,0 +1,77 @@
+<template>
+	<uni-popup ref="SelectMap" :isMaskClick='true' type="bottom" border-radius="10px 10px 0 0"
+		maskBackgroundColor='rgba(0, 0, 0, 0.1)'>
+		<view class="map-list">
+			<view class="title">请选择问题分类</view>
+			<view class="map-item" @click.stop="handlerFeedbackType(item.value)" v-for="item in list">
+				{{item.label }}
+			</view>
+			<view class="map-item cancel">
+				取消
+			</view>
+		</view>
+	</uni-popup>
+</template>
+
+<script>
+	export default {
+		props: {
+			list: {
+				type: Array,
+				default: () => []
+			}
+		},
+		data() {
+			return {
+
+			};
+		},
+		created() {
+	 
+		},
+		mounted() {
+			// setTimeout(() => {
+			// 	this.open()
+			// }, 200)
+		},
+		methods: {
+			open() {
+				this.$refs.SelectMap.open()
+			},
+			close() {
+				this.$refs.SelectMap.close()
+			},
+			handlerFeedbackType(val) {
+				this.$emit("chang" , val)
+				this.close()
+			}
+
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.map-list {
+		width: 100%;
+		// height: 30vh;
+		background-color: #fff;
+		border-radius: 20rpx 20rpx 0 0;
+		text-align: center;
+
+		.title {
+			width: 100%;
+			text-align: center;
+			padding: 30rpx 20rpx 50rpx 20rpx;
+			font-size: 28rpx;
+		}
+
+		.map-item {
+			width: 100%;
+			padding: 30rpx 20rpx;
+		}
+
+		.cancel {
+			padding: 50rpx 30rpx;
+		}
+	}
+</style>

+ 363 - 0
pages/feedback/feedback.vue

@@ -0,0 +1,363 @@
+<template>
+	<view>
+		<view class="hint">
+			尊敬的市民朋友,您好!【武汉公安政务服务地图】正在试运行中,欢迎提出宝贵的意见和建议。
+		</view>
+		<view class="from">
+			<view class="from-item">
+				<view class="item-label sign">
+					评价
+				</view>
+				<view class="item-value" style="padding-left: 20rpx;">
+					<u-rate :size="40" v-model="from.score"></u-rate>
+				</view>
+			</view>
+
+			<view class="from-item">
+				<view class="item-label sign">
+					问题分类
+				</view>
+				<view class="item-value select-item" @click.stop="$refs.SelectClassifyRefs.open()">
+					<view :class="['select-value' , isPlaceholder(from.feedbackType) ? 'input-placeholder' : '']">
+						{{ isPlaceholder(from.feedbackType) ? '请选择' :  getLable(from.feedbackType) }}
+					</view>
+					<u-icon class="select-icon" size="40" name="arrow-down" />
+				</view>
+			</view>
+
+			<view class="from-item">
+				<view class="item-label sign">
+					联系方式
+				</view>
+				<view class="item-value">
+					<u--input placeholder="请输入内容" border="surround" v-model="from.phone" />
+				</view>
+			</view>
+
+			<view class="from-item from-item-textarea ">
+				<view class="item-label sign">
+					建议内容
+				</view>
+				<view class="item-value">
+					<u--textarea v-model="from.feedbackContent" placeholder="请输入内容" autoHeight count :maxlength="400" />
+				</view>
+			</view>
+
+			<view class="from-item" v-if="CaptchaImg">
+				<view class="item-label">
+					验证码
+				</view>
+				<view class="item-value from-item-code">
+					<u--input class="code-input" placeholder="请输入内容" border="surround" v-model="from.validCode" />
+					<image class="code-img" :src="CaptchaImg" mode="scaleToFill" />
+				</view>
+			</view>
+			<view class="from-item file-box">
+				<view class="file-item" v-for="item in (from.attachmentList||[])">
+					<image class="file-value" :src="item.annexUrl" mode="scaleToFill" />
+				</view>
+				<view class="file-item updata-img-box" @click.stop="handlerUploadImage"
+					v-if="!from.attachmentList || from.attachmentList.length < fileNum">
+					<image class="camera" src="/static/images/camera.png" mode="aspectFit" />
+					<text class="text">上传照片</text>
+				</view>
+			</view>
+			<view class="sign file-hint"> 上传类型:照片jpg、png;</view>
+			<view class="from-item submit">
+				<view class="submit-btn" @click.stop="SubmitFeedback">
+					提 交
+				</view>
+			</view>
+		</view>
+		<SelectClassify ref="SelectClassifyRefs" :list="FeedbackType" @chang="r => from.feedbackType = r" />
+	</view>
+</template>
+
+<script>
+	import SelectClassify from "./SelectClassify.vue";
+	import { uploadImage } from "@/utils/tool.js"
+	import { getDictTree_Api, getPicCaptcha_Api, addMapuserfeedback_Api } from "@/api/feedback.js";
+	export default {
+		components: { SelectClassify },
+		data() {
+			return {
+				singleSize: uni.upx2px(210),
+				FeedbackType: [],
+				CaptchaImg: undefined,
+				SubmitLoading: false,
+				// isShow
+				from: {},
+				fileNum: 6
+			};
+		},
+		onLoad() {
+			this.init()
+			this.getDictTree()
+		},
+		methods: {
+			init() {
+				this.from = {
+					score: 0,
+					feedbackType: undefined,
+					phone: undefined,
+					feedbackContent: undefined,
+					validCode: undefined,
+					attachmentList: undefined
+				}
+			},
+			getLable(val){
+				const item  = this.FeedbackType.find(el => el.value === val)
+				return item ? item.label : item
+			},
+			isPlaceholder(val) {
+				return val ? false : true
+			},
+			getDictTree() {
+				getDictTree_Api("FEEDBACK_TYPE").then(res => {
+					const arr = [];
+					if (res && Object.keys(res).length) {
+						for (var key in res) {
+							console.log("key === ", res, key)
+							arr.push({
+								label: res[key],
+								value: key
+							})
+						}
+					}
+					this.FeedbackType = arr || [];
+				})
+			},
+			handlerUploadImage() {
+				const L_ = this.from.attachmentList ? this.from.attachmentList.length : 0;
+				const N_ = this.fileNum - L_;
+				if (N_ && N_ > 0) {
+					uploadImage(N_).then(res => {
+						console.log("uploadImage = ", res)
+						this.from.attachmentList = [...(this.from.attachmentList || []), ...(res || [])]
+					})
+				} else {
+					uni.showToast({
+						title: `只能上传${this.fileNum}张`,
+						icon: 'none'
+					})
+				}
+			},
+
+			async SubmitFeedback() {
+				try {
+					if (this.SubmitLoading) return
+					this.SubmitLoading = true;
+					uni.showLoading()
+					if (!this.from.score) {
+						throw new Error("请先选择评分")
+					}
+					if (!this.from.feedbackType) {
+						throw new Error("请先选问题分类")
+					}
+					if (this.from.phone) {
+						const phone = this.from.phone + ''
+						const regex = /^(0|[1-9]\d*)$/;
+						if (!regex.test(phone) || phone.length !== 11) {
+							throw new Error("请输入正确的手机号码")
+						}
+					}else{
+						throw new Error("请输入手机号码")
+					};
+					
+					if (!this.from.feedbackContent) {
+						throw new Error("请输入建议内容")
+					}
+					if (this.CaptchaImg && !this.from.validCode) {
+						throw new Error("请输入验证码")
+					}
+					const { data } = await getPicCaptcha_Api();
+					if (data) {
+						this.CaptchaImg = data;
+						uni.showToast({
+							title: '请先输入验证码',
+							icon: 'none'
+						}); 
+						return
+					};
+					await addMapuserfeedback_Api(this.from)
+					uni.showToast({
+						title: '提交成功'
+					});
+
+					this.init()
+				} catch (error) {
+					console.log("error.message = ", error.message)
+					if (error.message) {
+						uni.showToast({
+							title: error.message,
+							icon: 'none'
+						});
+					}
+					//TODO handle the exception
+				} finally {
+					uni.hideLoading()
+					this.SubmitLoading = false;
+				}
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.hint {
+		font-size: 28rpx;
+		padding: 20rpx;
+		background-color: #E5F3FE;
+		color: #68A1E4;
+	}
+
+	.from {
+		width: 100%;
+		padding: 20rpx 30rpx;
+
+		.from-item {
+			width: 100%;
+			display: flex;
+			justify-content: space-between;
+			align-items: center;
+			min-height: 100rpx;
+			padding: 10rpx 0;
+			border-bottom: 0.5px solid #dadbde;
+
+			.item-label {
+				width: 180rpx;
+			}
+
+			.item-value {
+				width: calc(100% - 180rpx);
+			}
+		}
+
+		.from-item-textarea {
+			.item-label {
+				height: 100rpx;
+				line-height: 100rpx;
+			}
+
+			align-items: flex-start;
+
+			::v-deep .u-textarea__field {
+				min-height: 200rpx;
+			}
+		}
+
+		.select-item {
+			display: flex;
+			justify-content: space-between;
+			align-items: center;
+			padding-left: 20rpx;
+		}
+
+		.from-item-code {
+			display: flex;
+			justify-content: space-between;
+			height: 80rpx;
+
+			.code-input {
+				width: calc(100% - 180rpx);
+			}
+
+			.code-img {
+				width: 180rpx;
+				height: 100%;
+			}
+		}
+
+		.submit-btn {
+			width: 620rpx;
+			height: 80rpx;
+			margin: 50rpx auto;
+			text-align: center;
+			line-height: 80rpx;
+			background-color: #1D72E6;
+			color: #fff;
+			border-radius: 10rpx;
+		}
+	}
+
+	.file-box {
+		flex-wrap: wrap;
+		justify-content: flex-start !important;
+		padding: 50rpx 0 0 0 !important;
+
+		.file-item {
+			width: 210rpx;
+			height: 210rpx;
+			flex-shrink: 0;
+			margin: 0 30rpx 30rpx 0;
+			border-radius: 10rpx;
+			overflow: hidden;
+
+			&:nth-child(3n) {
+				margin-right: 0;
+			}
+
+			.file-value {
+				width: 100%;
+				height: 100%;
+			}
+		}
+
+		.u-album {
+			uni-image {
+				width: 210rpx !important;
+				height: 210rpx !important;
+			}
+		}
+
+		.updata-img-box {
+			background-color: #EFEFEF;
+			display: flex;
+			flex-direction: column;
+			justify-content: center;
+			align-items: center;
+			border: 1rpx solid #BBBBBB;
+
+			.camera {
+				width: 80rpx;
+				height: 80rpx;
+			}
+
+			.text {
+				color: #9A9A9A;
+			}
+		}
+
+	}
+
+
+
+	.u-input,
+	.file-box,
+	.submit {
+		border: none !important;
+	}
+
+	.sign {
+		position: relative;
+		padding-left: 20rpx;
+
+		&::before {
+			position: absolute;
+			left: 0;
+			top: 0;
+			content: "*";
+			color: red;
+			font-size: 40rpx;
+		}
+	}
+
+	.file-hint {
+		color: red;
+	}
+
+	.input-placeholder {
+		color: rgb(192, 196, 204);
+		padding: 0 9rpx;
+	}
+</style>

+ 11 - 4
pages/map/index.vue

@@ -5,7 +5,7 @@
 		<tiandituMap ref="tiandituMapRefs" @handleSearch="$refs.searchRef.onSearchFocus()" @clickMap="handleClickMap"
 			@moveMap="moveMapSearch" @onLoadTianDiTu="initMaps" @onSelect="selectPoint" :apiKey="apiKey"
 			@handleMapSite="handleMapSite" @centre-text-callback="e => centreText = e" :siteListArr='siteListArr'
-			:searchDot="searchDot" @openHint="$refs.HintContentRef.open()" />
+			:searchDot="searchDot" @openHint="$refs.HintContentRef.open()" @openFeedback="goPages('feedback')"/>
 
 
 		<Touchbox ref="TouchboxRef" :maxHeight="0.8" v-if="showMap && siteListArr && siteListArr.length > 0"
@@ -167,8 +167,8 @@
 				// 如果存在收索条件,则去掉 范围 和 中心点
 				if (parms.locationName || parms.mapTypeId || parms.areaCode) {
 					delete parms.radius
-					delete parms.latitude
-					delete parms.longitude
+					// delete parms.latitude
+					// delete parms.longitude
 				}
 				delete parms.search;
 				this.searchContent = parms
@@ -421,7 +421,14 @@
 				//最大高度
 				this.scrollMaxHeight = val;
 			},
-
+			
+			// 页面跳转
+			goPages(){
+				// 'feedback'
+				uni.navigateTo({
+					url:"/pages/feedback/feedback"
+				})
+			}
 
 		}
 	}

BIN
static/images/camera.png


BIN
static/images/feedback.png


+ 104 - 1
utils/tool.js

@@ -1,3 +1,6 @@
+import $Config from "@/config/index.js"
+import { uploadFile_Api } from "@/api/feedback.js"
+
 // 计算点位距离
 export const distanceCalculate = (num) => {
 	const num_ = Number(num);
@@ -68,4 +71,104 @@ export const getLocation = () => {
 		}
 	})
 }
- 
+
+
+
+export const uploadImage = (num = 1, type = 'img') => {
+	return new Promise(async (resolve, reject) => {
+		uni.chooseImage({
+			count: num,
+			sizeType: ['original', 'compressed'], //可以指定是原图还是压缩图,默认二者都有
+			sourceType: ['album'], //从相册选择
+			// crop: {
+			// 	quality: 100, // 图片裁剪质量
+			// 	width: 100, // 裁剪的宽度
+			// 	height: 100, //裁剪的高度
+			// 	resize: false //是否将width和height作为裁剪保存图片真实的像素值。默认值为true。注:设置为false时在裁剪编辑界面显示图片的像素值,设置为true时不显示
+			// },
+			success: (res) => {
+				console.log("res = ", res)
+				// 选中的文件对象
+				const FilePaths = res.tempFilePaths;
+				const Names = []
+				res.tempFiles.forEach(el => {
+					Names.push(el.name)
+				})
+				uni.showLoading({
+					title: '上传中',
+					mask: true
+				});
+				const UploadList = FilePaths.map(el => SingleFileUpload(el))
+				Promise.all(UploadList).then(res => {
+					const Files = [];
+					res.forEach((el, index) => {
+						if (el) {
+							Files.push({
+								annexUrl: el,
+								annexName: Names[index]
+							})
+						}
+					});
+					if (Files && Files.length) {
+						uni.hideLoading();
+						uni.showToast({
+							title: '上传成功',
+							icon: 'none',
+							duration: 1000
+						})
+						resolve(Files)
+					} else {
+						uni.showToast({
+							title: '上传失败',
+							icon: 'none',
+							duration: 1000
+						})
+						resolve([])
+					}
+				});
+
+			},
+			fail: () => {
+				uni.hideLoading();
+			},
+			complete: () => {
+				// uni.hideLoading();
+			}
+		})
+	})
+}
+
+// uploadFile_Api
+
+
+export const SingleFileUpload = (file, showLoading = false) => {
+	return new Promise((resolve, reject) => {
+		if (showLoading) {
+			uni.showLoading({
+				title: '上传中',
+				mask: true
+			});
+		}
+		uni.uploadFile({
+			url: $Config.baseURL + '/dev/file/uploadHuaweiYunReturnUrl',
+			filePath: file,
+			name: 'file',
+			success: (uploadFileRes) => {
+				const { code, data, msg } = JSON.parse(uploadFileRes.data)
+				if (code === 200) {
+					resolve(data)
+				} else {
+					resolve()
+				}
+			},
+			fail(fail) {
+				resolve()
+			},
+			complete: () => {
+				if (showLoading) {
+					uni.hideLoading();
+				}
+			}
+		})
+	})
+}