feedback.vue 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363
  1. <template>
  2. <view>
  3. <view class="hint">
  4. 尊敬的市民朋友,您好!【武汉公安政务服务地图】正在试运行中,欢迎提出宝贵的意见和建议。
  5. </view>
  6. <view class="from">
  7. <view class="from-item">
  8. <view class="item-label sign">
  9. 评价
  10. </view>
  11. <view class="item-value" style="padding-left: 20rpx;">
  12. <u-rate :size="40" v-model="from.score"></u-rate>
  13. </view>
  14. </view>
  15. <view class="from-item">
  16. <view class="item-label sign">
  17. 问题分类
  18. </view>
  19. <view class="item-value select-item" @click.stop="$refs.SelectClassifyRefs.open()">
  20. <view :class="['select-value' , isPlaceholder(from.feedbackType) ? 'input-placeholder' : '']">
  21. {{ isPlaceholder(from.feedbackType) ? '请选择' : getLable(from.feedbackType) }}
  22. </view>
  23. <u-icon class="select-icon" size="40" name="arrow-down" />
  24. </view>
  25. </view>
  26. <view class="from-item">
  27. <view class="item-label sign">
  28. 联系方式
  29. </view>
  30. <view class="item-value">
  31. <u--input placeholder="请输入内容" border="surround" v-model="from.phone" />
  32. </view>
  33. </view>
  34. <view class="from-item from-item-textarea ">
  35. <view class="item-label sign">
  36. 建议内容
  37. </view>
  38. <view class="item-value">
  39. <u--textarea v-model="from.feedbackContent" placeholder="请输入内容" autoHeight count :maxlength="400" />
  40. </view>
  41. </view>
  42. <view class="from-item" v-if="CaptchaImg">
  43. <view class="item-label">
  44. 验证码
  45. </view>
  46. <view class="item-value from-item-code">
  47. <u--input class="code-input" placeholder="请输入内容" border="surround" v-model="from.validCode" />
  48. <image class="code-img" :src="CaptchaImg" mode="scaleToFill" />
  49. </view>
  50. </view>
  51. <view class="from-item file-box">
  52. <view class="file-item" v-for="item in (from.attachmentList||[])">
  53. <image class="file-value" :src="item.annexUrl" mode="scaleToFill" />
  54. </view>
  55. <view class="file-item updata-img-box" @click.stop="handlerUploadImage"
  56. v-if="!from.attachmentList || from.attachmentList.length < fileNum">
  57. <image class="camera" src="/static/images/camera.png" mode="aspectFit" />
  58. <text class="text">上传照片</text>
  59. </view>
  60. </view>
  61. <view class="sign file-hint"> 上传类型:照片jpg、png;</view>
  62. <view class="from-item submit">
  63. <view class="submit-btn" @click.stop="SubmitFeedback">
  64. 提 交
  65. </view>
  66. </view>
  67. </view>
  68. <SelectClassify ref="SelectClassifyRefs" :list="FeedbackType" @chang="r => from.feedbackType = r" />
  69. </view>
  70. </template>
  71. <script>
  72. import SelectClassify from "./SelectClassify.vue";
  73. import { uploadImage } from "@/utils/tool.js"
  74. import { getDictTree_Api, getPicCaptcha_Api, addMapuserfeedback_Api } from "@/api/feedback.js";
  75. export default {
  76. components: { SelectClassify },
  77. data() {
  78. return {
  79. singleSize: uni.upx2px(210),
  80. FeedbackType: [],
  81. CaptchaImg: undefined,
  82. SubmitLoading: false,
  83. // isShow
  84. from: {},
  85. fileNum: 6
  86. };
  87. },
  88. onLoad() {
  89. this.init()
  90. this.getDictTree()
  91. },
  92. methods: {
  93. init() {
  94. this.from = {
  95. score: 0,
  96. feedbackType: undefined,
  97. phone: undefined,
  98. feedbackContent: undefined,
  99. validCode: undefined,
  100. attachmentList: undefined
  101. }
  102. },
  103. getLable(val){
  104. const item = this.FeedbackType.find(el => el.value === val)
  105. return item ? item.label : item
  106. },
  107. isPlaceholder(val) {
  108. return val ? false : true
  109. },
  110. getDictTree() {
  111. getDictTree_Api("FEEDBACK_TYPE").then(res => {
  112. const arr = [];
  113. if (res && Object.keys(res).length) {
  114. for (var key in res) {
  115. console.log("key === ", res, key)
  116. arr.push({
  117. label: res[key],
  118. value: key
  119. })
  120. }
  121. }
  122. this.FeedbackType = arr || [];
  123. })
  124. },
  125. handlerUploadImage() {
  126. const L_ = this.from.attachmentList ? this.from.attachmentList.length : 0;
  127. const N_ = this.fileNum - L_;
  128. if (N_ && N_ > 0) {
  129. uploadImage(N_).then(res => {
  130. console.log("uploadImage = ", res)
  131. this.from.attachmentList = [...(this.from.attachmentList || []), ...(res || [])]
  132. })
  133. } else {
  134. uni.showToast({
  135. title: `只能上传${this.fileNum}张`,
  136. icon: 'none'
  137. })
  138. }
  139. },
  140. async SubmitFeedback() {
  141. try {
  142. if (this.SubmitLoading) return
  143. this.SubmitLoading = true;
  144. uni.showLoading()
  145. if (!this.from.score) {
  146. throw new Error("请先选择评分")
  147. }
  148. if (!this.from.feedbackType) {
  149. throw new Error("请先选问题分类")
  150. }
  151. if (this.from.phone) {
  152. const phone = this.from.phone + ''
  153. const regex = /^(0|[1-9]\d*)$/;
  154. if (!regex.test(phone) || phone.length !== 11) {
  155. throw new Error("请输入正确的手机号码")
  156. }
  157. }else{
  158. throw new Error("请输入手机号码")
  159. };
  160. if (!this.from.feedbackContent) {
  161. throw new Error("请输入建议内容")
  162. }
  163. if (this.CaptchaImg && !this.from.validCode) {
  164. throw new Error("请输入验证码")
  165. }
  166. const { data } = await getPicCaptcha_Api();
  167. if (data) {
  168. this.CaptchaImg = data;
  169. uni.showToast({
  170. title: '请先输入验证码',
  171. icon: 'none'
  172. });
  173. return
  174. };
  175. await addMapuserfeedback_Api(this.from)
  176. uni.showToast({
  177. title: '提交成功'
  178. });
  179. this.init()
  180. } catch (error) {
  181. console.log("error.message = ", error.message)
  182. if (error.message) {
  183. uni.showToast({
  184. title: error.message,
  185. icon: 'none'
  186. });
  187. }
  188. //TODO handle the exception
  189. } finally {
  190. uni.hideLoading()
  191. this.SubmitLoading = false;
  192. }
  193. }
  194. }
  195. }
  196. </script>
  197. <style lang="scss" scoped>
  198. .hint {
  199. font-size: 28rpx;
  200. padding: 20rpx;
  201. background-color: #E5F3FE;
  202. color: #68A1E4;
  203. }
  204. .from {
  205. width: 100%;
  206. padding: 20rpx 30rpx;
  207. .from-item {
  208. width: 100%;
  209. display: flex;
  210. justify-content: space-between;
  211. align-items: center;
  212. min-height: 100rpx;
  213. padding: 10rpx 0;
  214. border-bottom: 0.5px solid #dadbde;
  215. .item-label {
  216. width: 180rpx;
  217. }
  218. .item-value {
  219. width: calc(100% - 180rpx);
  220. }
  221. }
  222. .from-item-textarea {
  223. .item-label {
  224. height: 100rpx;
  225. line-height: 100rpx;
  226. }
  227. align-items: flex-start;
  228. ::v-deep .u-textarea__field {
  229. min-height: 200rpx;
  230. }
  231. }
  232. .select-item {
  233. display: flex;
  234. justify-content: space-between;
  235. align-items: center;
  236. padding-left: 20rpx;
  237. }
  238. .from-item-code {
  239. display: flex;
  240. justify-content: space-between;
  241. height: 80rpx;
  242. .code-input {
  243. width: calc(100% - 180rpx);
  244. }
  245. .code-img {
  246. width: 180rpx;
  247. height: 100%;
  248. }
  249. }
  250. .submit-btn {
  251. width: 620rpx;
  252. height: 80rpx;
  253. margin: 50rpx auto;
  254. text-align: center;
  255. line-height: 80rpx;
  256. background-color: #1D72E6;
  257. color: #fff;
  258. border-radius: 10rpx;
  259. }
  260. }
  261. .file-box {
  262. flex-wrap: wrap;
  263. justify-content: flex-start !important;
  264. padding: 50rpx 0 0 0 !important;
  265. .file-item {
  266. width: 210rpx;
  267. height: 210rpx;
  268. flex-shrink: 0;
  269. margin: 0 30rpx 30rpx 0;
  270. border-radius: 10rpx;
  271. overflow: hidden;
  272. &:nth-child(3n) {
  273. margin-right: 0;
  274. }
  275. .file-value {
  276. width: 100%;
  277. height: 100%;
  278. }
  279. }
  280. .u-album {
  281. uni-image {
  282. width: 210rpx !important;
  283. height: 210rpx !important;
  284. }
  285. }
  286. .updata-img-box {
  287. background-color: #EFEFEF;
  288. display: flex;
  289. flex-direction: column;
  290. justify-content: center;
  291. align-items: center;
  292. border: 1rpx solid #BBBBBB;
  293. .camera {
  294. width: 80rpx;
  295. height: 80rpx;
  296. }
  297. .text {
  298. color: #9A9A9A;
  299. }
  300. }
  301. }
  302. .u-input,
  303. .file-box,
  304. .submit {
  305. border: none !important;
  306. }
  307. .sign {
  308. position: relative;
  309. padding-left: 20rpx;
  310. &::before {
  311. position: absolute;
  312. left: 0;
  313. top: 0;
  314. content: "*";
  315. color: red;
  316. font-size: 40rpx;
  317. }
  318. }
  319. .file-hint {
  320. color: red;
  321. }
  322. .input-placeholder {
  323. color: rgb(192, 196, 204);
  324. padding: 0 9rpx;
  325. }
  326. </style>