feedback.vue 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371
  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" @click.stop="getPicCaptcha()" />
  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.getPicCaptcha()
  95. this.from = {
  96. score: 0,
  97. feedbackType: undefined,
  98. phone: undefined,
  99. feedbackContent: undefined,
  100. validCode: undefined,
  101. attachmentList: undefined,
  102. validCodeReqNo:undefined
  103. }
  104. },
  105. async getPicCaptcha() {
  106. try {
  107. const data = await getPicCaptcha_Api();
  108. console.log("data == ", data)
  109. if (data) {
  110. this.CaptchaImg = data.validCodeBase64;
  111. this.from.validCodeReqNo = data.validCodeReqNo;
  112. };
  113. } catch (error) {
  114. //TODO handle the exception
  115. }
  116. },
  117. getLable(val) {
  118. const item = this.FeedbackType.find(el => el.value === val)
  119. return item ? item.label : item
  120. },
  121. isPlaceholder(val) {
  122. return val ? false : true
  123. },
  124. getDictTree() {
  125. getDictTree_Api("FEEDBACK_TYPE").then(res => {
  126. const arr = [];
  127. if (res && Object.keys(res).length) {
  128. for (var key in res) {
  129. console.log("key === ", res, key)
  130. arr.push({
  131. label: res[key],
  132. value: key
  133. })
  134. }
  135. }
  136. this.FeedbackType = arr || [];
  137. })
  138. },
  139. handlerUploadImage() {
  140. const L_ = this.from.attachmentList ? this.from.attachmentList.length : 0;
  141. const N_ = this.fileNum - L_;
  142. if (N_ && N_ > 0) {
  143. uploadImage(N_).then(res => {
  144. console.log("uploadImage = ", res)
  145. this.from.attachmentList = [...(this.from.attachmentList || []), ...(res || [])]
  146. })
  147. } else {
  148. uni.showToast({
  149. title: `只能上传${this.fileNum}张`,
  150. icon: 'none'
  151. })
  152. }
  153. },
  154. async SubmitFeedback() {
  155. try {
  156. if (this.SubmitLoading) return
  157. this.SubmitLoading = true;
  158. uni.showLoading()
  159. if (!this.from.score) {
  160. throw new Error("请先选择评分")
  161. }
  162. if (!this.from.feedbackType) {
  163. throw new Error("请先选问题分类")
  164. }
  165. if (this.from.phone) {
  166. const phone = this.from.phone + ''
  167. const regex = /^(0|[1-9]\d*)$/;
  168. if (!regex.test(phone) || phone.length !== 11) {
  169. throw new Error("请输入正确的手机号码")
  170. }
  171. } else {
  172. throw new Error("请输入手机号码")
  173. };
  174. if (!this.from.feedbackContent) {
  175. throw new Error("请输入建议内容")
  176. }
  177. if (this.CaptchaImg && !this.from.validCode) {
  178. throw new Error("请输入验证码")
  179. }
  180. await addMapuserfeedback_Api(this.from)
  181. uni.showToast({
  182. title: '提交成功'
  183. });
  184. this.init()
  185. } catch (error) {
  186. console.log("error.message = ", error.message)
  187. if (error.message) {
  188. uni.showToast({
  189. title: error.message,
  190. icon: 'none'
  191. });
  192. }
  193. //TODO handle the exception
  194. } finally {
  195. uni.hideLoading()
  196. this.SubmitLoading = false;
  197. }
  198. }
  199. }
  200. }
  201. </script>
  202. <style lang="scss" scoped>
  203. .hint {
  204. font-size: 28rpx;
  205. padding: 20rpx;
  206. background-color: #E5F3FE;
  207. color: #68A1E4;
  208. }
  209. .from {
  210. width: 100%;
  211. padding: 20rpx 30rpx;
  212. .from-item {
  213. width: 100%;
  214. display: flex;
  215. justify-content: space-between;
  216. align-items: center;
  217. min-height: 100rpx;
  218. padding: 10rpx 0;
  219. border-bottom: 0.5px solid #dadbde;
  220. .item-label {
  221. width: 180rpx;
  222. }
  223. .item-value {
  224. width: calc(100% - 180rpx);
  225. }
  226. }
  227. .from-item-textarea {
  228. .item-label {
  229. height: 100rpx;
  230. line-height: 100rpx;
  231. }
  232. align-items: flex-start;
  233. ::v-deep .u-textarea__field {
  234. min-height: 200rpx;
  235. }
  236. }
  237. .select-item {
  238. display: flex;
  239. justify-content: space-between;
  240. align-items: center;
  241. padding-left: 20rpx;
  242. }
  243. .from-item-code {
  244. display: flex;
  245. justify-content: space-between;
  246. height: 80rpx;
  247. .code-input {
  248. width: calc(100% - 180rpx);
  249. }
  250. .code-img {
  251. width: 180rpx;
  252. height: 100%;
  253. }
  254. }
  255. .submit-btn {
  256. width: 620rpx;
  257. height: 80rpx;
  258. margin: 50rpx auto;
  259. text-align: center;
  260. line-height: 80rpx;
  261. background-color: #1D72E6;
  262. color: #fff;
  263. border-radius: 10rpx;
  264. }
  265. }
  266. .file-box {
  267. flex-wrap: wrap;
  268. justify-content: flex-start !important;
  269. padding: 50rpx 0 0 0 !important;
  270. .file-item {
  271. width: 210rpx;
  272. height: 210rpx;
  273. flex-shrink: 0;
  274. margin: 0 30rpx 30rpx 0;
  275. border-radius: 10rpx;
  276. overflow: hidden;
  277. &:nth-child(3n) {
  278. margin-right: 0;
  279. }
  280. .file-value {
  281. width: 100%;
  282. height: 100%;
  283. }
  284. }
  285. .u-album {
  286. uni-image {
  287. width: 210rpx !important;
  288. height: 210rpx !important;
  289. }
  290. }
  291. .updata-img-box {
  292. background-color: #EFEFEF;
  293. display: flex;
  294. flex-direction: column;
  295. justify-content: center;
  296. align-items: center;
  297. border: 1rpx solid #BBBBBB;
  298. .camera {
  299. width: 80rpx;
  300. height: 80rpx;
  301. }
  302. .text {
  303. color: #9A9A9A;
  304. }
  305. }
  306. }
  307. .u-input,
  308. .file-box,
  309. .submit {
  310. border: none !important;
  311. }
  312. .sign {
  313. position: relative;
  314. padding-left: 20rpx;
  315. &::before {
  316. position: absolute;
  317. left: 0;
  318. top: 0;
  319. content: "*";
  320. color: red;
  321. font-size: 40rpx;
  322. }
  323. }
  324. .file-hint {
  325. color: red;
  326. }
  327. .input-placeholder {
  328. color: rgb(192, 196, 204);
  329. padding: 0 9rpx;
  330. }
  331. </style>