report-survey.vue 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928
  1. <template>
  2. <view class="survey-container">
  3. <!-- 导航 -->
  4. <!-- 进度条 -->
  5. <view class="progress-bar">
  6. <view class="progress-fill" :style="{ width: progressPercent + '%' }"></view>
  7. </view>
  8. <!-- 提示说明 -->
  9. <view class="tip-banner">
  10. <text class="tip-icon">💡</text>
  11. <text class="tip-text">请根据成员和家庭的实际情况填写,帮助AI更准确地解读报告</text>
  12. </view>
  13. <!-- 表单 -->
  14. <view class="form-section">
  15. <!-- ===================== 第一部分:生活习惯 ===================== -->
  16. <view class="section-header">
  17. <text class="section-icon">🍚</text>
  18. <text class="section-title">生活习惯</text>
  19. </view>
  20. <!-- Q1: 饮食结构 -->
  21. <view class="question-card">
  22. <text class="q-number">1/15</text>
  23. <text class="q-title">成员平时的饮食结构是怎样的?</text>
  24. <view class="q-options">
  25. <view class="q-option" :class="{ 'q-selected': survey.diet === 'balanced' }" @click="survey.diet = 'balanced'">
  26. <text class="q-icon">🥗</text>
  27. <text class="q-text">均衡饮食,荤素搭配</text>
  28. </view>
  29. <view class="q-option" :class="{ 'q-selected': survey.diet === 'meat' }" @click="survey.diet = 'meat'">
  30. <text class="q-icon">🥩</text>
  31. <text class="q-text">偏荤食,爱吃肉</text>
  32. </view>
  33. <view class="q-option" :class="{ 'q-selected': survey.diet === 'veggie' }" @click="survey.diet = 'veggie'">
  34. <text class="q-icon">🥬</text>
  35. <text class="q-text">偏素食,不爱吃肉</text>
  36. </view>
  37. <view class="q-option" :class="{ 'q-selected': survey.diet === 'junk' }" @click="survey.diet = 'junk'">
  38. <text class="q-icon">🍟</text>
  39. <text class="q-text">偏好零食/快餐</text>
  40. </view>
  41. </view>
  42. </view>
  43. <!-- Q1b: 绝对忌口(必填) -->
  44. <view class="question-card">
  45. <text class="q-number">1b/15</text>
  46. <text class="q-title">成员有哪些绝对不能吃/喝的食物?(必填,含过敏原)</text>
  47. <view class="q-options multi">
  48. <view class="q-option" :class="{ 'q-selected': survey.absoluteAvoid.indexOf('牛奶') !== -1 }" @click="toggleAbsAvoid('牛奶')">
  49. <text class="q-text">牛奶/乳制品</text>
  50. </view>
  51. <view class="q-option" :class="{ 'q-selected': survey.absoluteAvoid.indexOf('鸡蛋') !== -1 }" @click="toggleAbsAvoid('鸡蛋')">
  52. <text class="q-text">鸡蛋</text>
  53. </view>
  54. <view class="q-option" :class="{ 'q-selected': survey.absoluteAvoid.indexOf('海鲜') !== -1 }" @click="toggleAbsAvoid('海鲜')">
  55. <text class="q-text">海鲜/虾蟹</text>
  56. </view>
  57. <view class="q-option" :class="{ 'q-selected': survey.absoluteAvoid.indexOf('花生') !== -1 }" @click="toggleAbsAvoid('花生')">
  58. <text class="q-text">花生/坚果</text>
  59. </view>
  60. <view class="q-option" :class="{ 'q-selected': survey.absoluteAvoid.indexOf('麸质') !== -1 }" @click="toggleAbsAvoid('麸质')">
  61. <text class="q-text">麸质/小麦</text>
  62. </view>
  63. <view class="q-option" :class="{ 'q-selected': survey.absoluteAvoid.length === 0 }" @click="survey.absoluteAvoid = []">
  64. <text class="q-text">无</text>
  65. </view>
  66. </view>
  67. <view class="extra-input-row">
  68. <input class="extra-input" placeholder="或其他忌口,回车添加..." v-model="absAvoidInput" @confirm="addAbsAvoidInput" />
  69. </view>
  70. </view>
  71. <!-- Q1c: 必须要吃 -->
  72. <view class="question-card">
  73. <text class="q-number">1c/15</text>
  74. <text class="q-title">有什么一定要保证摄入的食物?(可选)</text>
  75. <view class="q-options multi">
  76. <view class="q-option" :class="{ 'q-selected': survey.mustEat.indexOf('蔬菜') !== -1 }" @click="toggleMustEat('蔬菜')">
  77. <text class="q-text">🥬 蔬菜</text>
  78. </view>
  79. <view class="q-option" :class="{ 'q-selected': survey.mustEat.indexOf('水果') !== -1 }" @click="toggleMustEat('水果')">
  80. <text class="q-text">🍎 水果</text>
  81. </view>
  82. <view class="q-option" :class="{ 'q-selected': survey.mustEat.indexOf('蛋白质') !== -1 }" @click="toggleMustEat('蛋白质')">
  83. <text class="q-text">🥚 蛋白质</text>
  84. </view>
  85. <view class="q-option" :class="{ 'q-selected': survey.mustEat.indexOf('益生菌') !== -1 }" @click="toggleMustEat('益生菌')">
  86. <text class="q-text">🥛 益生菌/酸奶</text>
  87. </view>
  88. </view>
  89. <view class="extra-input-row">
  90. <input class="extra-input" placeholder="或其他,回车添加..." v-model="mustEatInput" @confirm="addMustEatInput" />
  91. </view>
  92. </view>
  93. <!-- Q1d: 宗教饮食 -->
  94. <view class="question-card">
  95. <text class="q-number">1d/15</text>
  96. <text class="q-title">是否有宗教或特殊饮食要求?(可选)</text>
  97. <view class="q-options">
  98. <view class="q-option" :class="{ 'q-selected': survey.religiousDiet === 'none' }" @click="survey.religiousDiet = 'none'">
  99. <text class="q-icon">✅</text>
  100. <text class="q-text">无限制</text>
  101. </view>
  102. <view class="q-option" :class="{ 'q-selected': survey.religiousDiet === 'halal' }" @click="survey.religiousDiet = 'halal'">
  103. <text class="q-icon">☪️</text>
  104. <text class="q-text">清真(Halal)</text>
  105. </view>
  106. <view class="q-option" :class="{ 'q-selected': survey.religiousDiet === 'vegetarian' }" @click="survey.religiousDiet = 'vegetarian'">
  107. <text class="q-icon">🥬</text>
  108. <text class="q-text">素食</text>
  109. </view>
  110. <view class="q-option" :class="{ 'q-selected': survey.religiousDiet === 'vegan' }" @click="survey.religiousDiet = 'vegan'">
  111. <text class="q-icon">🌱</text>
  112. <text class="q-text">纯素(不含任何动物制品)</text>
  113. </view>
  114. </view>
  115. </view>
  116. <!-- Q2: 消化状况 -->
  117. <view class="question-card">
  118. <text class="q-number">2/15</text>
  119. <text class="q-title">成员近期消化状况如何?</text>
  120. <view class="q-options">
  121. <view class="q-option" :class="{ 'q-selected': survey.digestion === 'good' }" @click="survey.digestion = 'good'">
  122. <text>😊</text>
  123. <text class="q-text">正常,消化良好</text>
  124. </view>
  125. <view class="q-option" :class="{ 'q-selected': survey.digestion === 'constipation' }" @click="survey.digestion = 'constipation'">
  126. <text>😣</text>
  127. <text class="q-text">容易便秘</text>
  128. </view>
  129. <view class="q-option" :class="{ 'q-selected': survey.digestion === 'diarrhea' }" @click="survey.digestion = 'diarrhea'">
  130. <text>😟</text>
  131. <text class="q-text">容易腹泻</text>
  132. </view>
  133. <view class="q-option" :class="{ 'q-selected': survey.digestion === 'irregular' }" @click="survey.digestion = 'irregular'">
  134. <text>🤔</text>
  135. <text class="q-text">不太规律</text>
  136. </view>
  137. </view>
  138. </view>
  139. <!-- Q3: 睡眠质量 -->
  140. <view class="question-card">
  141. <text class="q-number">3/15</text>
  142. <text class="q-title">成员近期的睡眠质量如何?</text>
  143. <view class="q-options">
  144. <view class="q-option" :class="{ 'q-selected': survey.sleep === 'good' }" @click="survey.sleep = 'good'">
  145. <text>😴</text>
  146. <text class="q-text">睡得好,作息规律</text>
  147. </view>
  148. <view class="q-option" :class="{ 'q-selected': survey.sleep === 'late' }" @click="survey.sleep = 'late'">
  149. <text>🦉</text>
  150. <text class="q-text">睡得太晚</text>
  151. </view>
  152. <view class="q-option" :class="{ 'q-selected': survey.sleep === 'restless' }" @click="survey.sleep = 'restless'">
  153. <text>😰</text>
  154. <text class="q-text">翻来覆去睡不安稳</text>
  155. </view>
  156. <view class="q-option" :class="{ 'q-selected': survey.sleep === 'short' }" @click="survey.sleep = 'short'">
  157. <text>⏰</text>
  158. <text class="q-text">睡眠时间不足</text>
  159. </view>
  160. </view>
  161. </view>
  162. <!-- Q4: 运动习惯 -->
  163. <view class="question-card">
  164. <text class="q-number">4/15</text>
  165. <text class="q-title">成员每周运动情况?</text>
  166. <view class="q-options">
  167. <view class="q-option" :class="{ 'q-selected': survey.exercise === 'frequent' }" @click="survey.exercise = 'frequent'">
  168. <text>🏃</text>
  169. <text class="q-text">经常运动(3次以上/周)</text>
  170. </view>
  171. <view class="q-option" :class="{ 'q-selected': survey.exercise === 'moderate' }" @click="survey.exercise = 'moderate'">
  172. <text>🚶</text>
  173. <text class="q-text">偶尔运动(1-2次/周)</text>
  174. </view>
  175. <view class="q-option" :class="{ 'q-selected': survey.exercise === 'rare' }" @click="survey.exercise = 'rare'">
  176. <text>🪑</text>
  177. <text class="q-text">很少运动</text>
  178. </view>
  179. </view>
  180. </view>
  181. <!-- Q5: 饮水习惯 -->
  182. <view class="question-card">
  183. <text class="q-number">5/15</text>
  184. <text class="q-title">成员每日饮水量如何?</text>
  185. <view class="q-options">
  186. <view class="q-option" :class="{ 'q-selected': survey.water === 'enough' }" @click="survey.water = 'enough'">
  187. <text>💧</text>
  188. <text class="q-text">充足(>1500ml)</text>
  189. </view>
  190. <view class="q-option" :class="{ 'q-selected': survey.water === 'moderate' }" @click="survey.water = 'moderate'">
  191. <text>🥤</text>
  192. <text class="q-text">一般(500-1500ml)</text>
  193. </view>
  194. <view class="q-option" :class="{ 'q-selected': survey.water === 'little' }" @click="survey.water = 'little'">
  195. <text>😥</text>
  196. <text class="q-text">偏少(<500ml)</text>
  197. </view>
  198. </view>
  199. </view>
  200. <!-- Q6: 用药情况 -->
  201. <view class="question-card">
  202. <text class="q-number">6/15</text>
  203. <text class="q-title">成员近期是否有以下用药情况?(可多选)</text>
  204. <view class="q-options multi">
  205. <view class="q-option" :class="{ 'q-selected': hasMedication('antibiotic') }" @click="toggleMedication('antibiotic')">
  206. <text class="q-text">抗生素(近3个月内使用过)</text>
  207. </view>
  208. <view class="q-option" :class="{ 'q-selected': hasMedication('probiotic') }" @click="toggleMedication('probiotic')">
  209. <text class="q-text">益生菌/益生元</text>
  210. </view>
  211. <view class="q-option" :class="{ 'q-selected': hasMedication('cold') }" @click="toggleMedication('cold')">
  212. <text class="q-text">感冒药/退烧药</text>
  213. </view>
  214. <view class="q-option" :class="{ 'q-selected': hasMedication('stomach') }" @click="toggleMedication('stomach')">
  215. <text class="q-text">肠胃药</text>
  216. </view>
  217. <view class="q-option" :class="{ 'q-selected': hasMedication('none') }" @click="toggleMedication('none')">
  218. <text class="q-text">无特殊用药</text>
  219. </view>
  220. </view>
  221. </view>
  222. <!-- ===================== 第二部分:疾病史 ===================== -->
  223. <view class="section-header">
  224. <text class="section-icon">🏥</text>
  225. <text class="section-title">疾病史</text>
  226. </view>
  227. <!-- Q7: 慢性病/系统性疾病 -->
  228. <view class="question-card">
  229. <text class="q-number">7/15</text>
  230. <text class="q-title">成员是否有以下慢性或系统性疾病?(可多选)</text>
  231. <view class="q-options multi">
  232. <view class="q-option" :class="{ 'q-selected': hasChronic('none') }" @click="toggleChronic('none')">
  233. <text class="q-text">无已知疾病</text>
  234. </view>
  235. <view class="q-option" :class="{ 'q-selected': hasChronic('asthma') }" @click="toggleChronic('asthma')">
  236. <text class="q-text">哮喘/过敏性鼻炎</text>
  237. </view>
  238. <view class="q-option" :class="{ 'q-selected': hasChronic('eczema') }" @click="toggleChronic('eczema')">
  239. <text class="q-text">湿疹/特应性皮炎</text>
  240. </view>
  241. <view class="q-option" :class="{ 'q-selected': hasChronic('anemia') }" @click="toggleChronic('anemia')">
  242. <text class="q-text">贫血</text>
  243. </view>
  244. <view class="q-option" :class="{ 'q-selected': hasChronic('obesity') }" @click="toggleChronic('obesity')">
  245. <text class="q-text">超重/肥胖</text>
  246. </view>
  247. <view class="q-option" :class="{ 'q-selected': hasChronic('underweight') }" @click="toggleChronic('underweight')">
  248. <text class="q-text">偏瘦/营养不良</text>
  249. </view>
  250. <view class="q-option" :class="{ 'q-selected': hasChronic('thyroid') }" @click="toggleChronic('thyroid')">
  251. <text class="q-text">甲状腺问题</text>
  252. </view>
  253. <view class="q-option" :class="{ 'q-selected': hasChronic('digestive') }" @click="toggleChronic('digestive')">
  254. <text class="q-text">消化系统疾病(胃炎/肠炎等)</text>
  255. </view>
  256. <view class="q-option" :class="{ 'q-selected': hasChronic('other') }" @click="toggleChronic('other')">
  257. <text class="q-text">其他(请在备注说明)</text>
  258. </view>
  259. </view>
  260. <!-- 其他疾病描述 -->
  261. <textarea v-if="hasChronic('other')" class="extra-input" v-model="survey.diseaseHistory.otherDesc" placeholder="请描述其他疾病情况..." maxlength="200" />
  262. </view>
  263. <!-- Q8: 过敏史 -->
  264. <view class="question-card">
  265. <text class="q-number">8/15</text>
  266. <text class="q-title">成员是否有以下过敏情况?(可多选)</text>
  267. <view class="q-options multi">
  268. <view class="q-option" :class="{ 'q-selected': survey.allergies.indexOf('none') !== -1 }" @click="toggleAllergy('none')">
  269. <text class="q-text">无明显过敏</text>
  270. </view>
  271. <view class="q-option" :class="{ 'q-selected': survey.allergies.indexOf('food') !== -1 }" @click="toggleAllergy('food')">
  272. <text class="q-text">食物过敏(牛奶/鸡蛋/海鲜等)</text>
  273. </view>
  274. <view class="q-option" :class="{ 'q-selected': survey.allergies.indexOf('drug') !== -1 }" @click="toggleAllergy('drug')">
  275. <text class="q-text">药物过敏</text>
  276. </view>
  277. <view class="q-option" :class="{ 'q-selected': survey.allergies.indexOf('pollen') !== -1 }" @click="toggleAllergy('pollen')">
  278. <text class="q-text">花粉/尘螨过敏</text>
  279. </view>
  280. <view class="q-option" :class="{ 'q-selected': survey.allergies.indexOf('skin') !== -1 }" @click="toggleAllergy('skin')">
  281. <text class="q-text">皮肤过敏/荨麻疹</text>
  282. </view>
  283. </view>
  284. <!-- 过敏详情 -->
  285. <textarea v-if="survey.allergies.length > 0 && survey.allergies.indexOf('none') === -1" class="extra-input" v-model="survey.allergyDetail" placeholder="请补充过敏详情(过敏原、症状等)..." maxlength="200" />
  286. </view>
  287. <!-- Q9: 手术/住院史 -->
  288. <view class="question-card">
  289. <text class="q-number">9/15</text>
  290. <text class="q-title">成员是否有手术或住院史?</text>
  291. <view class="q-options">
  292. <view class="q-option" :class="{ 'q-selected': survey.surgery === 'none' }" @click="survey.surgery = 'none'">
  293. <text class="q-text">无</text>
  294. </view>
  295. <view class="q-option" :class="{ 'q-selected': survey.surgery === 'yes' }" @click="survey.surgery = 'yes'">
  296. <text class="q-text">有</text>
  297. </view>
  298. </view>
  299. <textarea v-if="survey.surgery === 'yes'" class="extra-input" v-model="survey.surgeryDetail" placeholder="请描述手术/住院原因和时间..." maxlength="200" />
  300. </view>
  301. <!-- ===================== 第三部分:家族史 ===================== -->
  302. <view class="section-header">
  303. <text class="section-icon">🧬</text>
  304. <text class="section-title">家族病史</text>
  305. </view>
  306. <!-- Q10: 家族病史 -->
  307. <view class="question-card">
  308. <text class="q-number">10/15</text>
  309. <text class="q-title">直系亲属是否有以下疾病史?(可多选)</text>
  310. <view class="family-grid">
  311. <view class="family-col">
  312. <text class="family-role">父亲</text>
  313. <view class="family-options">
  314. <view v-for="opt in familyDiseaseOptions" :key="opt.value"
  315. class="family-tag" :class="{ 'tag-selected': hasFamilyDisease('father', opt.value) }"
  316. @click="toggleFamilyDisease('father', opt.value)">
  317. <text>{{ opt.label }}</text>
  318. </view>
  319. <view class="family-tag" :class="{ 'tag-selected': hasFamilyDisease('father', 'other') }"
  320. @click="toggleFamilyDisease('father', 'other')">
  321. <text>其他</text>
  322. </view>
  323. </view>
  324. </view>
  325. <view class="family-col">
  326. <text class="family-role">母亲</text>
  327. <view class="family-options">
  328. <view v-for="opt in familyDiseaseOptions" :key="opt.value"
  329. class="family-tag" :class="{ 'tag-selected': hasFamilyDisease('mother', opt.value) }"
  330. @click="toggleFamilyDisease('mother', opt.value)">
  331. <text>{{ opt.label }}</text>
  332. </view>
  333. <view class="family-tag" :class="{ 'tag-selected': hasFamilyDisease('mother', 'other') }"
  334. @click="toggleFamilyDisease('mother', 'other')">
  335. <text>其他</text>
  336. </view>
  337. </view>
  338. </view>
  339. <view class="family-col">
  340. <text class="family-role">兄弟姐妹</text>
  341. <view class="family-options">
  342. <view v-for="opt in familyDiseaseOptions" :key="opt.value"
  343. class="family-tag" :class="{ 'tag-selected': hasFamilyDisease('sibling', opt.value) }"
  344. @click="toggleFamilyDisease('sibling', opt.value)">
  345. <text>{{ opt.label }}</text>
  346. </view>
  347. <view class="family-tag" :class="{ 'tag-selected': hasFamilyDisease('sibling', 'other') }"
  348. @click="toggleFamilyDisease('sibling', 'other')">
  349. <text>其他</text>
  350. </view>
  351. </view>
  352. </view>
  353. </view>
  354. </view>
  355. <!-- Q11: 祖父母病史 -->
  356. <view class="question-card">
  357. <text class="q-number">11/15</text>
  358. <text class="q-title">祖父母/外祖父母是否有以下疾病史?(可多选)</text>
  359. <view class="q-options multi">
  360. <view class="q-option" :class="{ 'q-selected': survey.grandparentHistory.indexOf('unknown') !== -1 }" @click="toggleGrandparent('unknown')">
  361. <text class="q-text">不清楚</text>
  362. </view>
  363. <view class="q-option" :class="{ 'q-selected': survey.grandparentHistory.indexOf('hypertension') !== -1 }" @click="toggleGrandparent('hypertension')">
  364. <text class="q-text">高血压</text>
  365. </view>
  366. <view class="q-option" :class="{ 'q-selected': survey.grandparentHistory.indexOf('diabetes') !== -1 }" @click="toggleGrandparent('diabetes')">
  367. <text class="q-text">糖尿病</text>
  368. </view>
  369. <view class="q-option" :class="{ 'q-selected': survey.grandparentHistory.indexOf('heart') !== -1 }" @click="toggleGrandparent('heart')">
  370. <text class="q-text">心脏病</text>
  371. </view>
  372. <view class="q-option" :class="{ 'q-selected': survey.grandparentHistory.indexOf('stroke') !== -1 }" @click="toggleGrandparent('stroke')">
  373. <text class="q-text">脑卒中</text>
  374. </view>
  375. <view class="q-option" :class="{ 'q-selected': survey.grandparentHistory.indexOf('cancer') !== -1 }" @click="toggleGrandparent('cancer')">
  376. <text class="q-text">癌症</text>
  377. </view>
  378. <view class="q-option" :class="{ 'q-selected': survey.grandparentHistory.indexOf('autoimmune') !== -1 }" @click="toggleGrandparent('autoimmune')">
  379. <text class="q-text">自身免疫病</text>
  380. </view>
  381. <view class="q-option" :class="{ 'q-selected': survey.grandparentHistory.indexOf('none') !== -1 }" @click="toggleGrandparent('none')">
  382. <text class="q-text">无明显疾病</text>
  383. </view>
  384. </view>
  385. </view>
  386. <!-- ===================== 第四部分:健康目标 ===================== -->
  387. <view class="section-header">
  388. <text class="section-icon">🎯</text>
  389. <text class="section-title">健康目标</text>
  390. </view>
  391. <!-- Q12: 健康目标 -->
  392. <view class="question-card">
  393. <text class="q-number">12/15</text>
  394. <text class="q-title">您希望通过本次健康管理改善什么?(可多选)</text>
  395. <view class="q-options multi">
  396. <view class="q-option" :class="{ 'q-selected': survey.goals.indexOf('immunity') !== -1 }" @click="toggleGoal('immunity')">
  397. <text>🛡️</text>
  398. <text class="q-text">增强免疫力</text>
  399. </view>
  400. <view class="q-option" :class="{ 'q-selected': survey.goals.indexOf('digestion') !== -1 }" @click="toggleGoal('digestion')">
  401. <text>🌿</text>
  402. <text class="q-text">改善消化</text>
  403. </view>
  404. <view class="q-option" :class="{ 'q-selected': survey.goals.indexOf('growth') !== -1 }" @click="toggleGoal('growth')">
  405. <text>📈</text>
  406. <text class="q-text">促进生长发育</text>
  407. </view>
  408. <view class="q-option" :class="{ 'q-selected': survey.goals.indexOf('focus') !== -1 }" @click="toggleGoal('focus')">
  409. <text>🎯</text>
  410. <text class="q-text">提升专注力</text>
  411. </view>
  412. <view class="q-option" :class="{ 'q-selected': survey.goals.indexOf('sleep') !== -1 }" @click="toggleGoal('sleep')">
  413. <text>😴</text>
  414. <text class="q-text">改善睡眠</text>
  415. </view>
  416. <view class="q-option" :class="{ 'q-selected': survey.goals.indexOf('weight') !== -1 }" @click="toggleGoal('weight')">
  417. <text>⚖️</text>
  418. <text class="q-text">体重管理</text>
  419. </view>
  420. <view class="q-option" :class="{ 'q-selected': survey.goals.indexOf('allergy') !== -1 }" @click="toggleGoal('allergy')">
  421. <text>🌸</text>
  422. <text class="q-text">改善过敏</text>
  423. </view>
  424. <view class="q-option" :class="{ 'q-selected': survey.goals.indexOf('mood') !== -1 }" @click="toggleGoal('mood')">
  425. <text>😊</text>
  426. <text class="q-text">改善情绪</text>
  427. </view>
  428. </view>
  429. </view>
  430. <!-- 提交按钮 -->
  431. <view class="submit-btn" :class="{ 'btn-disabled': !canSubmit }" @click="doSubmit">
  432. <text v-if="submitting">提交中...</text>
  433. <text v-else>提交并进入 解读</text>
  434. </view>
  435. </view>
  436. <view class="bottom-spacer"></view>
  437. </view>
  438. </template>
  439. <script>
  440. import { createReportSurvey, submitReportSurvey, getReportSurveyStatus } from '../../utils/api.js'
  441. export default {
  442. data() {
  443. return {
  444. reportId: null,
  445. memberId: null,
  446. surveyId: null,
  447. survey: {
  448. // 生活习惯
  449. diet: '',
  450. absoluteAvoid: [], // 绝对忌口(必填)
  451. mustEat: [], // 必须要吃(可选)
  452. religiousDiet: 'none', // none/清真/halal/素食/vegan
  453. digestion: '',
  454. sleep: '',
  455. exercise: '',
  456. water: '',
  457. medications: [],
  458. // 疾病史
  459. diseaseHistory: {
  460. chronic: [],
  461. otherDesc: ''
  462. },
  463. allergies: [],
  464. allergyDetail: '',
  465. absAvoidInput: '',
  466. mustEatInput: '',
  467. surgery: '',
  468. surgeryDetail: '',
  469. // 家族史
  470. familyHistory: {
  471. father: [],
  472. mother: [],
  473. sibling: []
  474. },
  475. grandparentHistory: [],
  476. // 健康目标
  477. goals: []
  478. },
  479. familyDiseaseOptions: [
  480. { value: 'none', label: '无' },
  481. { value: 'hypertension', label: '高血压' },
  482. { value: 'diabetes', label: '糖尿病' },
  483. { value: 'heart', label: '心脏病' },
  484. { value: 'stroke', label: '脑卒中' },
  485. { value: 'cancer', label: '癌症' },
  486. { value: 'allergy', label: '过敏性疾病' },
  487. { value: 'autoimmune', label: '自身免疫病' },
  488. { value: 'mental', label: '精神/心理疾病' },
  489. { value: 'obesity', label: '肥胖' },
  490. ],
  491. submitting: false
  492. }
  493. },
  494. computed: {
  495. progressPercent: function() {
  496. var answered = 0
  497. var total = 15
  498. if (this.survey.diet) answered++
  499. if (this.survey.absoluteAvoid.length > 0) answered++
  500. if (this.survey.mustEat.length > 0 || true) answered++
  501. if (this.survey.religiousDiet && this.survey.religiousDiet !== 'none') answered++
  502. if (this.survey.digestion) answered++
  503. if (this.survey.sleep) answered++
  504. if (this.survey.exercise) answered++
  505. if (this.survey.water) answered++
  506. if (this.survey.medications.length > 0) answered++
  507. if (this.survey.diseaseHistory.chronic.length > 0) answered++
  508. if (this.survey.allergies.length > 0) answered++
  509. if (this.survey.surgery) answered++
  510. if (this.survey.familyHistory.father.length > 0
  511. || this.survey.familyHistory.mother.length > 0
  512. || this.survey.familyHistory.sibling.length > 0) answered++
  513. if (this.survey.grandparentHistory.length > 0) answered++
  514. if (this.survey.goals.length > 0) answered++
  515. return Math.round((answered / total) * 100)
  516. },
  517. canSubmit: function() {
  518. return this.survey.diet && this.survey.absoluteAvoid.length > 0
  519. && this.survey.sleep && this.survey.exercise
  520. && this.survey.diseaseHistory.chronic.length > 0
  521. && this.survey.allergies.length > 0
  522. && this.survey.goals.length > 0
  523. }
  524. },
  525. onLoad: function(options) {
  526. this.reportId = options.reportId ? parseInt(options.reportId) : null
  527. this.memberId = options.memberId ? parseInt(options.memberId) : null
  528. this.initSurvey()
  529. },
  530. methods: {
  531. initSurvey: function() {
  532. var self = this
  533. if (!self.reportId) return
  534. getSurveyStatus(self.reportId).then(function(res) {
  535. if (res.code === 200 && res.data) {
  536. if (res.data.completed) {
  537. uni.showToast({ title: '该报告问卷已提交', icon: 'none' })
  538. return
  539. }
  540. if (res.data.pendingSurveyId) {
  541. self.surveyId = res.data.pendingSurveyId
  542. return
  543. }
  544. }
  545. return createReportSurvey(self.reportId, self.memberId || 0)
  546. }).then(function(res) {
  547. if (res && res.code === 200 && res.data && res.data.id) {
  548. self.surveyId = res.data.id
  549. }
  550. }).catch(function(e) {
  551. console.log('初始化问卷失败', e)
  552. })
  553. },
  554. // === 用药多选 ===
  555. hasMedication: function(val) {
  556. return this.survey.medications.indexOf(val) !== -1
  557. },
  558. toggleMedication: function(val) {
  559. var idx = this.survey.medications.indexOf(val)
  560. if (val === 'none') {
  561. this.survey.medications = ['none']
  562. } else {
  563. var noneIdx = this.survey.medications.indexOf('none')
  564. if (noneIdx !== -1) this.survey.medications.splice(noneIdx, 1)
  565. if (idx === -1) {
  566. this.survey.medications.push(val)
  567. } else {
  568. this.survey.medications.splice(idx, 1)
  569. }
  570. if (this.survey.medications.length === 0) {
  571. this.survey.medications.push('none')
  572. }
  573. }
  574. },
  575. // === 慢性病多选 ===
  576. hasChronic: function(val) {
  577. return this.survey.diseaseHistory.chronic.indexOf(val) !== -1
  578. },
  579. toggleChronic: function(val) {
  580. var arr = this.survey.diseaseHistory.chronic
  581. var idx = arr.indexOf(val)
  582. if (val === 'none') {
  583. this.survey.diseaseHistory.chronic = ['none']
  584. } else {
  585. var noneIdx = arr.indexOf('none')
  586. if (noneIdx !== -1) arr.splice(noneIdx, 1)
  587. if (idx === -1) {
  588. arr.push(val)
  589. } else {
  590. arr.splice(idx, 1)
  591. }
  592. if (arr.length === 0) {
  593. arr.push('none')
  594. }
  595. }
  596. },
  597. // === 过敏多选 ===
  598. toggleAllergy: function(val) {
  599. var idx = this.survey.allergies.indexOf(val)
  600. if (val === 'none') {
  601. this.survey.allergies = ['none']
  602. } else {
  603. var noneIdx = this.survey.allergies.indexOf('none')
  604. if (noneIdx !== -1) this.survey.allergies.splice(noneIdx, 1)
  605. if (idx === -1) {
  606. this.survey.allergies.push(val)
  607. } else {
  608. this.survey.allergies.splice(idx, 1)
  609. }
  610. if (this.survey.allergies.length === 0) {
  611. this.survey.allergies.push('none')
  612. }
  613. }
  614. },
  615. // === 家族病史 ===
  616. hasFamilyDisease: function(role, val) {
  617. return this.survey.familyHistory[role].indexOf(val) !== -1
  618. },
  619. toggleFamilyDisease: function(role, val) {
  620. var arr = this.survey.familyHistory[role]
  621. var idx = arr.indexOf(val)
  622. if (val === 'none') {
  623. this.survey.familyHistory[role] = ['none']
  624. } else {
  625. var noneIdx = arr.indexOf('none')
  626. if (noneIdx !== -1) arr.splice(noneIdx, 1)
  627. if (idx === -1) {
  628. arr.push(val)
  629. } else {
  630. arr.splice(idx, 1)
  631. }
  632. if (arr.length === 0) {
  633. arr.push('none')
  634. }
  635. }
  636. },
  637. // === 祖父母病史 ===
  638. toggleGrandparent: function(val) {
  639. var idx = this.survey.grandparentHistory.indexOf(val)
  640. if (val === 'unknown') {
  641. this.survey.grandparentHistory = ['unknown']
  642. } else {
  643. var unkIdx = this.survey.grandparentHistory.indexOf('unknown')
  644. if (unkIdx !== -1) this.survey.grandparentHistory.splice(unkIdx, 1)
  645. if (idx === -1) {
  646. this.survey.grandparentHistory.push(val)
  647. } else {
  648. this.survey.grandparentHistory.splice(idx, 1)
  649. }
  650. if (this.survey.grandparentHistory.length === 0) {
  651. this.survey.grandparentHistory.push('unknown')
  652. }
  653. }
  654. },
  655. // === 健康目标 ===
  656. toggleGoal: function(val) {
  657. var idx = this.survey.goals.indexOf(val)
  658. if (idx === -1) {
  659. this.survey.goals.push(val)
  660. } else {
  661. this.survey.goals.splice(idx, 1)
  662. }
  663. },
  664. toggleAbsAvoid: function(item) {
  665. var idx = this.survey.absoluteAvoid.indexOf(item)
  666. if (idx >= 0) this.survey.absoluteAvoid.splice(idx, 1)
  667. else this.survey.absoluteAvoid.push(item)
  668. },
  669. addAbsAvoidInput: function() {
  670. var v = this.absAvoidInput.trim()
  671. if (v && this.survey.absoluteAvoid.indexOf(v) === -1) this.survey.absoluteAvoid.push(v)
  672. this.absAvoidInput = ''
  673. },
  674. toggleMustEat: function(item) {
  675. var idx = this.survey.mustEat.indexOf(item)
  676. if (idx >= 0) this.survey.mustEat.splice(idx, 1)
  677. else this.survey.mustEat.push(item)
  678. },
  679. addMustEatInput: function() {
  680. var v = this.mustEatInput.trim()
  681. if (v && this.survey.mustEat.indexOf(v) === -1) this.survey.mustEat.push(v)
  682. this.mustEatInput = ''
  683. },
  684. goBack: function() {
  685. uni.navigateBack()
  686. },
  687. doSubmit: function() {
  688. if (!this.canSubmit || this.submitting) return
  689. var self = this
  690. self.submitting = true
  691. uni.showLoading({ title: '提交中...' })
  692. var doSubmit = function() {
  693. submitReportSurvey(self.surveyId, JSON.stringify(self.survey)).then(function(res) {
  694. uni.hideLoading()
  695. if (res.code === 200) {
  696. uni.showToast({ title: '提交成功', icon: 'success' })
  697. // 问答功能已屏蔽
  698. // setTimeout(function() {
  699. // var params = 'reportId=' + self.reportId + '&surveyId=' + self.surveyId + '&firstMessage=' + encodeURIComponent('请根据报告和问卷信息,提供个性化的健康建议')
  700. // uni.redirectTo({ url: '/pages/ai/chat?' + params })
  701. // }, 500)
  702. } else {
  703. uni.showToast({ title: res.message || '提交失败', icon: 'none' })
  704. self.submitting = false
  705. }
  706. }).catch(function(e) {
  707. uni.hideLoading()
  708. uni.showToast({ title: '提交失败,请重试', icon: 'none' })
  709. console.log('提交问卷失败', e)
  710. self.submitting = false
  711. })
  712. }
  713. if (this.surveyId) {
  714. doSubmit()
  715. } else {
  716. createReportSurvey(this.reportId, this.memberId || 0).then(function(res) {
  717. if (res.code === 200 && res.data && res.data.id) {
  718. self.surveyId = res.data.id
  719. doSubmit()
  720. } else {
  721. uni.hideLoading()
  722. uni.showToast({ title: '创建问卷失败', icon: 'none' })
  723. self.submitting = false
  724. }
  725. }).catch(function(e) {
  726. uni.hideLoading()
  727. uni.showToast({ title: '创建问卷失败', icon: 'none' })
  728. self.submitting = false
  729. console.log('创建问卷失败', e)
  730. })
  731. }
  732. }
  733. }
  734. }
  735. </script>
  736. <style scoped>
  737. .survey-container {
  738. min-height: 100vh;
  739. background: #F3F7FA;
  740. }
  741. .nav-placeholder { width: 80rpx; }
  742. .progress-bar {
  743. height: 6rpx;
  744. background: #E2E8F0;
  745. margin: 0 30rpx;
  746. border-radius: 3rpx;
  747. overflow: hidden;
  748. }
  749. .progress-fill {
  750. height: 100%;
  751. background: linear-gradient(90deg, #66BB6A, #43A047);
  752. border-radius: 3rpx;
  753. transition: width 0.3s;
  754. }
  755. .tip-banner {
  756. display: flex;
  757. flex-direction: row;
  758. align-items: center;
  759. background: #FFF8E1;
  760. margin: 16rpx 30rpx;
  761. padding: 16rpx 20rpx;
  762. border-radius: 12rpx;
  763. }
  764. .tip-icon { font-size: 28rpx; margin-right: 12rpx; flex-shrink: 0; }
  765. .tip-text { font-size: 22rpx; color: #F57F17; line-height: 1.5; }
  766. .form-section { margin: 10rpx 30rpx 30rpx; }
  767. /* 分段标题 */
  768. .section-header {
  769. display: flex;
  770. flex-direction: row;
  771. align-items: center;
  772. margin: 30rpx 0 16rpx;
  773. padding-left: 8rpx;
  774. }
  775. .section-icon { font-size: 36rpx; margin-right: 12rpx; }
  776. .section-title {
  777. font-size: 32rpx;
  778. font-weight: bold;
  779. color: #333;
  780. }
  781. .question-card {
  782. background: #fff;
  783. border-radius: 20rpx;
  784. padding: 28rpx 24rpx;
  785. margin-bottom: 16rpx;
  786. box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.04);
  787. }
  788. .q-number {
  789. font-size: 20rpx;
  790. color: #66BB6A;
  791. font-weight: bold;
  792. display: block;
  793. margin-bottom: 8rpx;
  794. }
  795. .q-title {
  796. font-size: 28rpx;
  797. font-weight: bold;
  798. color: #333;
  799. display: block;
  800. margin-bottom: 16rpx;
  801. }
  802. .q-options {
  803. display: flex;
  804. flex-direction: column;
  805. gap: 12rpx;
  806. }
  807. .q-options.multi {
  808. gap: 10rpx;
  809. }
  810. .q-option {
  811. display: flex;
  812. flex-direction: row;
  813. align-items: center;
  814. gap: 12rpx;
  815. padding: 16rpx 18rpx;
  816. border-radius: 14rpx;
  817. background: #F5F7FA;
  818. border: 2rpx solid transparent;
  819. }
  820. .q-selected {
  821. background: #E8F5E9;
  822. border-color: #66BB6A;
  823. }
  824. .q-option:active { opacity: 0.7; }
  825. .q-icon { font-size: 32rpx; flex-shrink: 0; }
  826. .q-text { font-size: 26rpx; color: #333; }
  827. /* 家族史网格 */
  828. .family-grid {
  829. display: flex;
  830. flex-direction: column;
  831. gap: 20rpx;
  832. }
  833. .family-col {
  834. background: #F5F7FA;
  835. border-radius: 14rpx;
  836. padding: 16rpx;
  837. }
  838. .family-role {
  839. font-size: 26rpx;
  840. font-weight: bold;
  841. color: #5B9BD5;
  842. display: block;
  843. margin-bottom: 12rpx;
  844. }
  845. .family-options {
  846. display: flex;
  847. flex-direction: row;
  848. flex-wrap: wrap;
  849. gap: 8rpx;
  850. }
  851. .family-tag {
  852. padding: 8rpx 16rpx;
  853. border-radius: 20rpx;
  854. font-size: 22rpx;
  855. color: #666;
  856. background: #fff;
  857. border: 2rpx solid #E2E8F0;
  858. }
  859. .family-tag:active { opacity: 0.7; }
  860. .tag-selected {
  861. background: #E3F2FD;
  862. border-color: #5B9BD5;
  863. color: #1565C0;
  864. }
  865. /* 额外文本输入 */
  866. .extra-input {
  867. width: 100%;
  868. min-height: 100rpx;
  869. background: #F5F7FA;
  870. border-radius: 12rpx;
  871. padding: 16rpx 20rpx;
  872. font-size: 24rpx;
  873. color: #333;
  874. margin-top: 16rpx;
  875. box-sizing: border-box;
  876. }
  877. .submit-btn {
  878. background: linear-gradient(135deg, #43A047, #2E7D32);
  879. color: #fff;
  880. font-size: 30rpx;
  881. font-weight: bold;
  882. text-align: center;
  883. padding: 28rpx;
  884. border-radius: 16rpx;
  885. margin-top: 20rpx;
  886. }
  887. .submit-btn:active { opacity: 0.85; }
  888. .btn-disabled { opacity: 0.5; }
  889. .bottom-spacer { height: 60rpx; }
  890. </style>