apply.vue 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  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. @change="onPriceChange"
  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 onPriceChange = (e) => {
  81. form.value.priceIndex = parseInt(e.detail.value, 10)
  82. }
  83. const onSubmit = async () => {
  84. // Validate form
  85. if (!form.value.demandType) {
  86. uni.showToast({ title: '请选择需求类型', icon: 'none' })
  87. return
  88. }
  89. if (!form.value.description.trim()) {
  90. uni.showToast({ title: '请填写具体需求描述', icon: 'none' })
  91. return
  92. }
  93. loading.value = true
  94. try {
  95. const res = await planApi.apply({
  96. chartRecordId: chartRecordId.value,
  97. demandType: form.value.demandType,
  98. description: form.value.description.trim(),
  99. priceRange: priceRanges[form.value.priceIndex]
  100. })
  101. // Backend returns { planRequest, assigned, message }
  102. // If no practitioner assigned, show the guidance message
  103. if (res && !res.assigned) {
  104. uni.showModal({
  105. title: '提交成功',
  106. content: res.message || '暂未开放系统分配,请通过推荐链接找到专属能量师',
  107. showCancel: false,
  108. confirmText: '知道了'
  109. })
  110. } else {
  111. uni.showToast({ title: '提交成功', icon: 'success' })
  112. }
  113. // Navigate back after a short delay to let the toast/modal show
  114. setTimeout(() => {
  115. uni.navigateBack()
  116. }, res && !res.assigned ? 3000 : 1500)
  117. } catch (e) {
  118. // Error handling: show toast with error message from API
  119. uni.showToast({
  120. title: e.message || '提交失败,请重试',
  121. icon: 'none'
  122. })
  123. } finally {
  124. loading.value = false
  125. }
  126. }
  127. </script>
  128. <style scoped lang="scss">
  129. .page-apply {
  130. padding: 20px;
  131. min-height: 100vh;
  132. background: linear-gradient(180deg, #0c0a1a 0%, #1a1232 100%);
  133. }
  134. .header {
  135. text-align: center;
  136. margin-bottom: 24px;
  137. }
  138. .header-title {
  139. font-size: 22px;
  140. font-weight: 700;
  141. color: rgba(255,255,255,0.9);
  142. display: block;
  143. }
  144. .form-container {
  145. background: rgba(255,255,255,0.02);
  146. border-radius: 16px;
  147. padding: 24px;
  148. margin-bottom: 24px;
  149. }
  150. .form-group {
  151. margin-bottom: 20px;
  152. }
  153. .form-label {
  154. display: block;
  155. font-size: 14px;
  156. color: rgba(255,255,255,0.6);
  157. margin-bottom: 8px;
  158. font-weight: 500;
  159. }
  160. .radio-group {
  161. display: flex;
  162. flex-wrap: wrap;
  163. gap: 12px;
  164. }
  165. .radio-item {
  166. display: flex;
  167. align-items: center;
  168. cursor: pointer;
  169. font-size: 14px;
  170. color: rgba(255,255,255,0.8);
  171. }
  172. .radio-item text {
  173. margin-left: 6px;
  174. }
  175. .textarea {
  176. width: 100%;
  177. min-height: 80px;
  178. padding: 12px;
  179. border: 1px solid rgba(255,255,255,0.1);
  180. border-radius: 8px;
  181. background: rgba(255,255,255,0.03);
  182. color: #fff;
  183. font-size: 14px;
  184. resize: none;
  185. }
  186. .textarea:focus {
  187. outline: none;
  188. border-color: rgba(255,255,255,0.2);
  189. }
  190. .counter {
  191. margin-top: 8px;
  192. text-align: right;
  193. font-size: 12px;
  194. color: rgba(255,255,255,0.4);
  195. }
  196. .picker {
  197. width: 100%;
  198. padding: 12px;
  199. border: 1px solid rgba(255,255,255,0.1);
  200. border-radius: 8px;
  201. background: rgba(255,255,255,0.03);
  202. color: #fff;
  203. font-size: 14px;
  204. }
  205. .picker-value {
  206. display: block;
  207. text-align: center;
  208. }
  209. .submit-btn {
  210. width: 100%;
  211. padding: 16px;
  212. background: linear-gradient(135deg, #f59e0b, #d97706);
  213. color: #fff;
  214. border: none;
  215. border-radius: 14px;
  216. font-size: 17px;
  217. font-weight: 600;
  218. letter-spacing: 2px;
  219. }
  220. .submit-btn[disabled] {
  221. opacity: 0.4;
  222. }
  223. </style>