apply.vue 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. <template>
  2. <view class="page-apply">
  3. <view class="header">
  4. <text class="header-title">申请人工方案</text>
  5. </view>
  6. <view class="form-container">
  7. <view class="form-group">
  8. <text class="form-label">需求类型</text>
  9. <radio-group class="radio-group" @change="onDemandTypeChange">
  10. <label class="radio-item" v-for="type in demandTypes" :key="type">
  11. <radio :value="type"></radio>
  12. <text>{{ type }}</text>
  13. </label>
  14. </radio-group>
  15. </view>
  16. <view class="form-group">
  17. <text class="form-label">具体需求描述</text>
  18. <textarea
  19. class="textarea"
  20. placeholder="请详细描述您的需求,我们将根据此信息匹配合适的能量师"
  21. maxlength="300"
  22. v-model="form.description"
  23. @input="onInput"
  24. ></textarea>
  25. <view class="counter">
  26. {{ form.description.length }}/300
  27. </view>
  28. </view>
  29. <view class="form-group">
  30. <text class="form-label">期望价格范围</text>
  31. <picker
  32. class="picker"
  33. mode="selector"
  34. :range="priceRanges"
  35. v-model="form.priceIndex"
  36. >
  37. <view class="picker-value">
  38. {{ priceRanges[form.priceIndex] }}
  39. </view>
  40. </picker>
  41. </view>
  42. </view>
  43. <button
  44. class="submit-btn"
  45. @click="onSubmit"
  46. :disabled="loading"
  47. >
  48. {{ loading ? '提交中...' : '提交申请' }}
  49. </button>
  50. </view>
  51. </template>
  52. <script setup>
  53. import { ref, computed } from 'vue'
  54. import { planApi } from '@/utils/api'
  55. import { useUserStore } from '@/stores/user'
  56. const form = ref({
  57. demandType: '',
  58. description: '',
  59. priceIndex: 0
  60. })
  61. const demandTypes = ['学业方向', '职业规划', '亲子关系', '其他']
  62. const priceRanges = ['¥50-99', '¥100-299', '¥300-499', '¥500-999', '面议']
  63. const loading = ref(false)
  64. const chartRecordId = ref('')
  65. // Get chartRecordId from query parameters on page load
  66. const onLoad = (query) => {
  67. if (query && query.chartRecordId) {
  68. chartRecordId.value = query.chartRecordId
  69. }
  70. }
  71. const onInput = (e) => {
  72. // Ensure we don't exceed 300 characters (though maxlength should handle it)
  73. if (form.value.description.length > 300) {
  74. form.value.description = form.value.description.slice(0, 300)
  75. }
  76. }
  77. const onDemandTypeChange = (e) => {
  78. form.value.demandType = e.detail.value
  79. }
  80. const onSubmit = async () => {
  81. // Validate form
  82. if (!form.value.demandType) {
  83. uni.showToast({ title: '请选择需求类型', icon: 'none' })
  84. return
  85. }
  86. if (!form.value.description.trim()) {
  87. uni.showToast({ title: '请填写具体需求描述', icon: 'none' })
  88. return
  89. }
  90. loading.value = true
  91. try {
  92. const res = await planApi.apply({
  93. chartRecordId: chartRecordId.value,
  94. demandType: form.value.demandType,
  95. description: form.value.description.trim(),
  96. priceRange: priceRanges[form.value.priceIndex]
  97. })
  98. // Backend returns { planRequest, assigned, message }
  99. // If no practitioner assigned, show the guidance message
  100. if (res && !res.assigned) {
  101. uni.showModal({
  102. title: '提交成功',
  103. content: res.message || '暂未开放系统分配,请通过推荐链接找到专属能量师',
  104. showCancel: false,
  105. confirmText: '知道了'
  106. })
  107. } else {
  108. uni.showToast({ title: '提交成功', icon: 'success' })
  109. }
  110. // Navigate back after a short delay to let the toast/modal show
  111. setTimeout(() => {
  112. uni.navigateBack()
  113. }, res && !res.assigned ? 3000 : 1500)
  114. } catch (e) {
  115. // Error handling: show toast with error message from API
  116. uni.showToast({
  117. title: e.message || '提交失败,请重试',
  118. icon: 'none'
  119. })
  120. } finally {
  121. loading.value = false
  122. }
  123. }
  124. </script>
  125. <style scoped lang="scss">
  126. .page-apply {
  127. padding: 20px;
  128. min-height: 100vh;
  129. background: linear-gradient(180deg, #0c0a1a 0%, #1a1232 100%);
  130. }
  131. .header {
  132. text-align: center;
  133. margin-bottom: 24px;
  134. }
  135. .header-title {
  136. font-size: 22px;
  137. font-weight: 700;
  138. color: rgba(255,255,255,0.9);
  139. display: block;
  140. }
  141. .form-container {
  142. background: rgba(255,255,255,0.02);
  143. border-radius: 16px;
  144. padding: 24px;
  145. margin-bottom: 24px;
  146. }
  147. .form-group {
  148. margin-bottom: 20px;
  149. }
  150. .form-label {
  151. display: block;
  152. font-size: 14px;
  153. color: rgba(255,255,255,0.6);
  154. margin-bottom: 8px;
  155. font-weight: 500;
  156. }
  157. .radio-group {
  158. display: flex;
  159. flex-wrap: wrap;
  160. gap: 12px;
  161. }
  162. .radio-item {
  163. display: flex;
  164. align-items: center;
  165. cursor: pointer;
  166. font-size: 14px;
  167. color: rgba(255,255,255,0.8);
  168. }
  169. .radio-item text {
  170. margin-left: 6px;
  171. }
  172. .textarea {
  173. width: 100%;
  174. min-height: 80px;
  175. padding: 12px;
  176. border: 1px solid rgba(255,255,255,0.1);
  177. border-radius: 8px;
  178. background: rgba(255,255,255,0.03);
  179. color: #fff;
  180. font-size: 14px;
  181. resize: none;
  182. }
  183. .textarea:focus {
  184. outline: none;
  185. border-color: rgba(255,255,255,0.2);
  186. }
  187. .counter {
  188. margin-top: 8px;
  189. text-align: right;
  190. font-size: 12px;
  191. color: rgba(255,255,255,0.4);
  192. }
  193. .picker {
  194. width: 100%;
  195. padding: 12px;
  196. border: 1px solid rgba(255,255,255,0.1);
  197. border-radius: 8px;
  198. background: rgba(255,255,255,0.03);
  199. color: #fff;
  200. font-size: 14px;
  201. }
  202. .picker-value {
  203. display: block;
  204. text-align: center;
  205. }
  206. .submit-btn {
  207. width: 100%;
  208. padding: 16px;
  209. background: linear-gradient(135deg, #f59e0b, #d97706);
  210. color: #fff;
  211. border: none;
  212. border-radius: 14px;
  213. font-size: 17px;
  214. font-weight: 600;
  215. letter-spacing: 2px;
  216. }
  217. .submit-btn[disabled] {
  218. opacity: 0.4;
  219. }
  220. </style>