apply.vue 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  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. @import "@/styles/theme.scss";
  130. .page-apply {
  131. padding: 20px;
  132. min-height: 100vh;
  133. background: linear-gradient(180deg, #0c0a1a 0%, #1a1232 100%);
  134. }
  135. .header {
  136. text-align: center;
  137. margin-bottom: 24px;
  138. }
  139. .header-title {
  140. font-size: 22px;
  141. font-weight: 700;
  142. color: rgba(255,255,255,0.9);
  143. display: block;
  144. }
  145. .form-container {
  146. background: rgba(255,255,255,0.02);
  147. border-radius: 16px;
  148. padding: 24px;
  149. margin-bottom: 24px;
  150. }
  151. .form-group {
  152. margin-bottom: 20px;
  153. }
  154. .form-label {
  155. display: block;
  156. font-size: 14px;
  157. color: rgba(255,255,255,0.6);
  158. margin-bottom: 8px;
  159. font-weight: 500;
  160. }
  161. .radio-group {
  162. display: flex;
  163. flex-wrap: wrap;
  164. gap: 12px;
  165. }
  166. .radio-item {
  167. display: flex;
  168. align-items: center;
  169. cursor: pointer;
  170. font-size: 14px;
  171. color: rgba(255,255,255,0.8);
  172. }
  173. .radio-item text {
  174. margin-left: 6px;
  175. }
  176. .textarea {
  177. width: 100%;
  178. min-height: 80px;
  179. padding: 12px;
  180. border: 1px solid rgba(255,255,255,0.1);
  181. border-radius: 8px;
  182. background: rgba(255,255,255,0.03);
  183. color: #fff;
  184. font-size: 14px;
  185. resize: none;
  186. }
  187. .textarea:focus {
  188. outline: none;
  189. border-color: rgba(255,255,255,0.2);
  190. }
  191. .counter {
  192. margin-top: 8px;
  193. text-align: right;
  194. font-size: 12px;
  195. color: rgba(255,255,255,0.4);
  196. }
  197. .picker {
  198. width: 100%;
  199. padding: 12px;
  200. border: 1px solid rgba(255,255,255,0.1);
  201. border-radius: 8px;
  202. background: rgba(255,255,255,0.03);
  203. color: #fff;
  204. font-size: 14px;
  205. }
  206. .picker-value {
  207. display: block;
  208. text-align: center;
  209. }
  210. .submit-btn {
  211. width: 100%;
  212. padding: 16px;
  213. background: linear-gradient(135deg, #f59e0b, #d97706);
  214. color: #fff;
  215. border: none;
  216. border-radius: 14px;
  217. font-size: 17px;
  218. font-weight: 600;
  219. letter-spacing: 2px;
  220. }
  221. .submit-btn[disabled] {
  222. opacity: 0.4;
  223. }
  224. </style>