index.vue 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. <template>
  2. <view class="after-sale">
  3. <!-- 商品信息 -->
  4. <view class="product-info">
  5. <image :src="product.image" mode="aspectFill" class="product-image"></image>
  6. <view class="product-detail">
  7. <view class="product-name">{{product.name}}</view>
  8. <view class="product-spec">{{product.spec}}</view>
  9. <view class="product-price">¥{{product.price}}</view>
  10. </view>
  11. </view>
  12. <!-- 售后表单 -->
  13. <view class="form-section">
  14. <u-form :model="form" ref="form">
  15. <u-form-item label="售后类型" prop="type" required>
  16. <u-radio-group v-model="form.type">
  17. <u-radio
  18. v-for="(item, index) in typeOptions"
  19. :key="index"
  20. :name="item.value"
  21. :label="item.label">
  22. </u-radio>
  23. </u-radio-group>
  24. </u-form-item>
  25. <u-form-item label="申请原因" prop="reason" required>
  26. <u-select
  27. v-model="form.reason"
  28. :list="reasonOptions"
  29. placeholder="请选择申请原因">
  30. </u-select>
  31. </u-form-item>
  32. <u-form-item label="问题描述" prop="description" required>
  33. <u-textarea
  34. v-model="form.description"
  35. placeholder="请详细描述您遇到的问题"
  36. count
  37. maxlength="500">
  38. </u-textarea>
  39. </u-form-item>
  40. <u-form-item label="上传凭证">
  41. <u-upload
  42. :fileList="form.images"
  43. @afterRead="afterRead"
  44. @delete="deletePic"
  45. name="1"
  46. multiple
  47. maxCount="9">
  48. </u-upload>
  49. </u-form-item>
  50. <u-form-item label="联系人" prop="contact" required>
  51. <u-input v-model="form.contact" placeholder="请输入联系人姓名" />
  52. </u-form-item>
  53. <u-form-item label="手机号码" prop="phone" required>
  54. <u-input v-model="form.phone" placeholder="请输入手机号码" type="number" maxlength="11" />
  55. </u-form-item>
  56. </u-form>
  57. </view>
  58. <!-- 提交按钮 -->
  59. <view class="submit-btn">
  60. <u-button type="primary" @click="submit" color="#D93025">提交申请</u-button>
  61. </view>
  62. </view>
  63. </template>
  64. <script>
  65. export default {
  66. data() {
  67. return {
  68. orderId: '',
  69. productId: '',
  70. product: {
  71. image: '',
  72. name: '',
  73. spec: '',
  74. price: ''
  75. },
  76. form: {
  77. type: 'refund',
  78. reason: '',
  79. description: '',
  80. images: [],
  81. contact: '',
  82. phone: ''
  83. },
  84. typeOptions: [
  85. { label: '仅退款', value: 'refund' },
  86. { label: '退货退款', value: 'return' }
  87. ],
  88. reasonOptions: [
  89. { label: '商品质量问题', value: 'quality' },
  90. { label: '商品损坏', value: 'damaged' },
  91. { label: '商品与描述不符', value: 'mismatch' },
  92. { label: '收到错误商品', value: 'wrong' },
  93. { label: '其他原因', value: 'other' }
  94. ],
  95. rules: {
  96. type: [{
  97. required: true,
  98. message: '请选择售后类型',
  99. trigger: ['change']
  100. }],
  101. reason: [{
  102. required: true,
  103. message: '请选择申请原因',
  104. trigger: ['change']
  105. }],
  106. description: [{
  107. required: true,
  108. message: '请输入问题描述',
  109. trigger: ['blur']
  110. }],
  111. contact: [{
  112. required: true,
  113. message: '请输入联系人姓名',
  114. trigger: ['blur']
  115. }],
  116. phone: [{
  117. required: true,
  118. message: '请输入手机号码',
  119. trigger: ['blur']
  120. }, {
  121. pattern: /^1[3-9]\d{9}$/,
  122. message: '请输入正确的手机号码',
  123. trigger: ['blur']
  124. }]
  125. }
  126. }
  127. },
  128. onLoad(options) {
  129. this.orderId = options.orderId
  130. this.productId = options.productId
  131. this.getProductInfo()
  132. },
  133. methods: {
  134. async getProductInfo() {
  135. try {
  136. const res = await this.$api.product.detail(this.productId)
  137. this.product = res.data
  138. } catch (e) {
  139. this.$u.toast('获取商品信息失败')
  140. }
  141. },
  142. async afterRead(event) {
  143. const { file } = event
  144. const uploadPromises = (Array.isArray(file) ? file : [file]).map(item => {
  145. return this.uploadFilePromise(item)
  146. })
  147. try {
  148. const urls = await Promise.all(uploadPromises)
  149. this.form.images = [...this.form.images, ...urls]
  150. } catch (e) {
  151. this.$u.toast('上传图片失败')
  152. }
  153. },
  154. uploadFilePromise(file) {
  155. return new Promise((resolve, reject) => {
  156. uni.uploadFile({
  157. url: this.$api.common.uploadUrl,
  158. filePath: file.url,
  159. name: 'file',
  160. success: (res) => {
  161. const data = JSON.parse(res.data)
  162. resolve(data.url)
  163. },
  164. fail: reject
  165. })
  166. })
  167. },
  168. deletePic(event) {
  169. const index = event.index
  170. this.form.images.splice(index, 1)
  171. },
  172. async submit() {
  173. try {
  174. await this.$refs.form.validate()
  175. await this.$api.order.afterSale({
  176. orderId: this.orderId,
  177. productId: this.productId,
  178. ...this.form
  179. })
  180. this.$u.toast('提交成功')
  181. setTimeout(() => {
  182. uni.navigateBack()
  183. }, 1500)
  184. } catch (e) {
  185. if (e.errors) return
  186. this.$u.toast('提交失败')
  187. }
  188. }
  189. }
  190. }
  191. </script>
  192. <style lang="scss" scoped>
  193. .after-sale {
  194. min-height: 100vh;
  195. background: #f5f5f5;
  196. padding-bottom: 120rpx;
  197. .product-info {
  198. background: #fff;
  199. padding: 30rpx;
  200. display: flex;
  201. margin-bottom: 20rpx;
  202. .product-image {
  203. width: 160rpx;
  204. height: 160rpx;
  205. border-radius: 8rpx;
  206. margin-right: 20rpx;
  207. }
  208. .product-detail {
  209. flex: 1;
  210. .product-name {
  211. font-size: 28rpx;
  212. color: #333;
  213. margin-bottom: 10rpx;
  214. }
  215. .product-spec {
  216. font-size: 24rpx;
  217. color: #999;
  218. margin-bottom: 20rpx;
  219. }
  220. .product-price {
  221. font-size: 32rpx;
  222. color: #D93025;
  223. font-weight: bold;
  224. }
  225. }
  226. }
  227. .form-section {
  228. background: #fff;
  229. padding: 30rpx;
  230. :deep(.u-form-item) {
  231. padding: 20rpx 0;
  232. }
  233. }
  234. .submit-btn {
  235. position: fixed;
  236. left: 0;
  237. right: 0;
  238. bottom: 0;
  239. padding: 20rpx 40rpx;
  240. background: #fff;
  241. }
  242. }
  243. </style>