resource-input.vue 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977
  1. <template>
  2. <view class="resource-input-page">
  3. <!-- 顶部导航栏 -->
  4. <view class="header">
  5. <view class="header-left" @click="goBack">
  6. <text class="back-icon">←</text>
  7. </view>
  8. <text class="header-title">信息录入</text>
  9. <view class="header-right">
  10. <text class="more-icon">⋯</text>
  11. </view>
  12. </view>
  13. <scroll-view scroll-y class="content">
  14. <view class="form-container">
  15. <!-- 基本信息 -->
  16. <view class="form-section" :class="{ 'active': activeSection === 'basic' }">
  17. <view class="section-title">基本信息</view>
  18. <view class="form-item">
  19. <text class="form-label">姓名<text class="required">*</text></text>
  20. <input type="text" class="form-input" placeholder="请输入姓名" v-model="formData.name" />
  21. </view>
  22. <view class="form-item">
  23. <text class="form-label">年龄<text class="required">*</text></text>
  24. <input type="number" class="form-input" placeholder="请输入年龄" v-model="formData.age" />
  25. </view>
  26. <view class="form-item">
  27. <text class="form-label">性别<text class="required">*</text></text>
  28. <picker mode="selector" :range="genderOptions" :value="genderIndex" @change="onGenderChange">
  29. <view class="form-input picker-input">
  30. <text v-if="formData.gender">{{ genderOptions[genderIndex] }}</text>
  31. <text v-else class="placeholder">请选择性别</text>
  32. <text class="picker-arrow">▼</text>
  33. </view>
  34. </picker>
  35. </view>
  36. <view class="form-item">
  37. <text class="form-label">星座</text>
  38. <picker mode="selector" :range="constellationOptions" :value="constellationIndex" @change="onConstellationChange">
  39. <view class="form-input picker-input">
  40. <text v-if="formData.constellation">{{ constellationOptions[constellationIndex] }}</text>
  41. <text v-else class="placeholder">请选择星座</text>
  42. <text class="picker-arrow">▼</text>
  43. </view>
  44. </picker>
  45. </view>
  46. </view>
  47. <!-- 身体状况 -->
  48. <view class="form-section" :class="{ 'active': activeSection === 'physical' }">
  49. <view class="section-title">身体状况</view>
  50. <view class="form-item">
  51. <text class="form-label">身高 (cm)<text class="required">*</text></text>
  52. <input type="number" class="form-input" placeholder="请输入身高" v-model="formData.height" />
  53. </view>
  54. <view class="form-item">
  55. <text class="form-label">体重 (kg)<text class="required">*</text></text>
  56. <input type="number" class="form-input" placeholder="请输入体重" v-model="formData.weight" />
  57. </view>
  58. </view>
  59. <!-- 社会属性 -->
  60. <view class="form-section" :class="{ 'active': activeSection === 'social' }">
  61. <view class="section-title">社会属性</view>
  62. <view class="form-item">
  63. <text class="form-label">婚况<text class="required">*</text></text>
  64. <picker mode="selector" :range="maritalStatusOptions" :value="maritalStatusIndex >= 0 ? maritalStatusIndex : 0" @change="onMaritalStatusChange">
  65. <view class="form-input picker-input">
  66. <text v-if="maritalStatusIndex >= 0 && maritalStatusIndex < maritalStatusOptions.length">{{ maritalStatusOptions[maritalStatusIndex] }}</text>
  67. <text v-else class="placeholder">请选择婚况</text>
  68. <text class="picker-arrow">▼</text>
  69. </view>
  70. </picker>
  71. </view>
  72. <view class="form-item">
  73. <text class="form-label">学历</text>
  74. <picker mode="selector" :range="diplomaOptions" :value="diplomaIndex" @change="onDiplomaChange">
  75. <view class="form-input picker-input">
  76. <text v-if="formData.diploma">{{ diplomaOptions[diplomaIndex] }}</text>
  77. <text v-else class="placeholder">请选择学历</text>
  78. <text class="picker-arrow">▼</text>
  79. </view>
  80. </picker>
  81. </view>
  82. <view class="form-item">
  83. <text class="form-label">年收入 (万元)</text>
  84. <input type="text" class="form-input" placeholder="请输入年收入" v-model="formData.income" />
  85. </view>
  86. <view class="form-item">
  87. <text class="form-label">住址<text class="required">*</text></text>
  88. <input type="text" class="form-input" placeholder="请输入住址" v-model="formData.address" />
  89. </view>
  90. <view class="form-item">
  91. <text class="form-label">户籍地</text>
  92. <input type="text" class="form-input" placeholder="请输入户籍地" v-model="formData.domicile" />
  93. </view>
  94. <view class="form-item">
  95. <text class="form-label">职业</text>
  96. <input type="text" class="form-input" placeholder="请输入职业" v-model="formData.occupation" />
  97. </view>
  98. <view class="form-item">
  99. <text class="form-label">购房</text>
  100. <picker mode="selector" :range="houseOptions" :value="houseIndex" @change="onHouseChange">
  101. <view class="form-input picker-input">
  102. <text v-if="formData.house !== null && formData.house !== undefined">{{ houseOptions[houseIndex] }}</text>
  103. <text v-else class="placeholder">请选择</text>
  104. <text class="picker-arrow">▼</text>
  105. </view>
  106. </picker>
  107. </view>
  108. <view class="form-item">
  109. <text class="form-label">购车</text>
  110. <picker mode="selector" :range="carOptions" :value="carIndex" @change="onCarChange">
  111. <view class="form-input picker-input">
  112. <text v-if="formData.car !== null && formData.car !== undefined">{{ carOptions[carIndex] }}</text>
  113. <text v-else class="placeholder">请选择</text>
  114. <text class="picker-arrow">▼</text>
  115. </view>
  116. </picker>
  117. </view>
  118. </view>
  119. <!-- 择偶标准 -->
  120. <view class="form-section">
  121. <view class="section-title">择偶标准</view>
  122. <view class="form-item">
  123. <text class="form-label">择偶要求</text>
  124. <input type="text" class="form-input" placeholder="请输入择偶要求,如:176+本科" v-model="formData.mateSelectionCriteria" />
  125. </view>
  126. </view>
  127. <!-- 客户标签 -->
  128. <view class="form-section">
  129. <view class="section-title">客户标签</view>
  130. <view class="form-item">
  131. <text class="form-label">选择标签</text>
  132. <!-- 分类按钮 -->
  133. <view class="category-buttons">
  134. <view
  135. class="category-btn"
  136. :class="{ 'active': selectedCategoryId === category.id }"
  137. v-for="category in categoryList"
  138. :key="category.id"
  139. @click="selectCategory(category.id)"
  140. >
  141. <text class="category-btn-text">{{ category.name }}</text>
  142. </view>
  143. </view>
  144. <!-- 标签列表 -->
  145. <view class="tags-container">
  146. <view
  147. class="tag-item"
  148. :class="{ 'selected': selectedTagIds.includes(tag.id) }"
  149. v-for="tag in filteredTagList"
  150. :key="tag.id || tag.tag_id"
  151. @click="toggleTag(tag.id || tag.tag_id)"
  152. >
  153. <text class="tag-text">{{ tag.name || tag.tag_name }}</text>
  154. </view>
  155. <view v-if="filteredTagList.length === 0 && !isLoadingTags" class="tag-empty">
  156. <text>暂无标签</text>
  157. </view>
  158. <view v-if="isLoadingTags" class="tag-loading">
  159. <text>加载中...</text>
  160. </view>
  161. </view>
  162. </view>
  163. </view>
  164. <!-- 联系方式 -->
  165. <view class="form-section">
  166. <view class="section-title">联系方式</view>
  167. <view class="form-item">
  168. <text class="form-label">手机号<text class="required">*</text></text>
  169. <input type="text" class="form-input" placeholder="请输入手机号" v-model="formData.phone" />
  170. </view>
  171. <view class="form-item">
  172. <text class="form-label">备用手机号</text>
  173. <input type="text" class="form-input" placeholder="请输入备用手机号" v-model="formData.backupPhone" />
  174. </view>
  175. </view>
  176. </view>
  177. </scroll-view>
  178. <!-- 底部提交按钮 -->
  179. <view class="submit-bar">
  180. <button class="submit-btn" @click="handleSubmit">提交</button>
  181. </view>
  182. </view>
  183. </template>
  184. <script>
  185. import api from '@/utils/api.js'
  186. export default {
  187. data() {
  188. return {
  189. activeSection: 'basic',
  190. currentUserId: null, // 当前登录用户的用户ID
  191. formData: {
  192. name: '',
  193. age: null,
  194. gender: null,
  195. constellation: '',
  196. height: null,
  197. weight: null,
  198. marrStatus: null,
  199. diploma: '',
  200. income: '',
  201. address: '',
  202. domicile: '',
  203. occupation: '',
  204. house: null,
  205. car: null,
  206. mateSelectionCriteria: '',
  207. phone: '',
  208. backupPhone: ''
  209. },
  210. genderOptions: ['男', '女'],
  211. genderIndex: 0,
  212. constellationOptions: ['白羊座', '金牛座', '双子座', '巨蟹座', '狮子座', '处女座', '天秤座', '天蝎座', '射手座', '摩羯座', '水瓶座', '双鱼座'],
  213. constellationIndex: 0,
  214. maritalStatusOptions: ['未婚', '离异', '丧偶'],
  215. maritalStatusIndex: -1, // -1表示未选择,0-未婚,1-离异,2-丧偶
  216. diplomaOptions: ['高中', '专科', '本科', '硕士', '博士', '无'],
  217. diplomaIndex: 0,
  218. houseOptions: ['无', '有'],
  219. houseIndex: 0,
  220. carOptions: ['无', '有'],
  221. carIndex: 0,
  222. tagList: [], // 所有标签列表
  223. categoryList: [], // 标签分类列表
  224. selectedCategoryId: 1, // 当前选中的分类ID,默认显示职业分类(category_id = 1)
  225. selectedTagIds: [], // 选中的标签ID列表
  226. isLoadingTags: false // 是否正在加载标签
  227. }
  228. },
  229. computed: {
  230. // 根据选中的分类筛选标签(只显示当前分类的标签,不显示全部)
  231. filteredTagList() {
  232. // 如果没有选中分类,返回空数组(不显示任何标签)
  233. if (this.selectedCategoryId === null || this.selectedCategoryId === undefined) {
  234. return []
  235. }
  236. // 确保selectedCategoryId是数字类型
  237. const selectedId = parseInt(this.selectedCategoryId)
  238. if (isNaN(selectedId)) {
  239. console.warn('selectedCategoryId不是有效数字:', this.selectedCategoryId)
  240. return []
  241. }
  242. // 筛选标签,只显示当前分类的标签
  243. const filtered = this.tagList.filter(tag => {
  244. // 获取标签的分类ID,优先使用 category_id(后端返回的字段名)
  245. const tagCategoryId = tag.category_id !== null && tag.category_id !== undefined
  246. ? parseInt(tag.category_id)
  247. : (tag.categoryId !== null && tag.categoryId !== undefined
  248. ? parseInt(tag.categoryId)
  249. : null)
  250. // 如果tagCategoryId是NaN或null,说明数据有问题或未分类,不显示
  251. if (isNaN(tagCategoryId) || tagCategoryId === null) {
  252. return false
  253. }
  254. // 只返回匹配当前分类的标签
  255. return tagCategoryId === selectedId
  256. })
  257. return filtered
  258. }
  259. },
  260. onLoad() {
  261. // 获取当前登录用户信息
  262. const userInfo = uni.getStorageSync('userInfo') || {}
  263. const userId = uni.getStorageSync('userId')
  264. // 从userInfo中获取用户ID,将作为红娘ID
  265. let rawUserId = userInfo.userId || userId || null
  266. // 验证并转换currentUserId为有效的整数
  267. if (rawUserId !== null && rawUserId !== undefined) {
  268. rawUserId = parseInt(rawUserId)
  269. if (isNaN(rawUserId) || rawUserId <= 0) {
  270. rawUserId = null
  271. }
  272. }
  273. this.currentUserId = rawUserId
  274. console.log('当前登录用户ID:', this.currentUserId)
  275. // 加载标签分类和标签列表
  276. this.loadCategories()
  277. this.loadTags()
  278. },
  279. methods: {
  280. goBack() {
  281. uni.navigateBack()
  282. },
  283. // 加载标签分类列表
  284. async loadCategories() {
  285. try {
  286. const baseUrl = process.env.NODE_ENV === 'development'
  287. ? 'http://localhost:8083/api' // 开发环境 - 通过网关
  288. : 'https://your-domain.com/api' // 生产环境
  289. const [error, res] = await uni.request({
  290. url: `${baseUrl}/tag/categories`,
  291. method: 'GET',
  292. timeout: 10000
  293. })
  294. if (error) {
  295. console.error('加载标签分类失败:', error)
  296. return
  297. }
  298. if (res.statusCode === 200 && res.data && res.data.code === 200) {
  299. // 将后端返回的下划线字段转换为驼峰命名
  300. this.categoryList = (res.data.data || []).map(category => ({
  301. id: category.id || category.category_id,
  302. name: category.name || category.category_name,
  303. code: category.code || category.category_code,
  304. sortOrder: category.sortOrder || category.sort_order,
  305. status: category.status
  306. }))
  307. console.log('标签分类列表加载成功:', this.categoryList)
  308. // 如果还没有选中分类,默认选中职业分类(category_id = 1)
  309. if (this.selectedCategoryId === null || this.selectedCategoryId === undefined) {
  310. // 查找职业分类(code为'occupation'或id为1)
  311. const occupationCategory = this.categoryList.find(cat =>
  312. (cat.code === 'occupation' || cat.id === 1)
  313. )
  314. if (occupationCategory) {
  315. this.selectedCategoryId = occupationCategory.id
  316. console.log('默认选中职业分类,ID:', this.selectedCategoryId)
  317. } else if (this.categoryList.length > 0) {
  318. // 如果找不到职业分类,选中第一个分类
  319. this.selectedCategoryId = this.categoryList[0].id
  320. console.log('默认选中第一个分类,ID:', this.selectedCategoryId)
  321. }
  322. }
  323. } else {
  324. console.error('加载标签分类失败:', res.data.message)
  325. }
  326. } catch (e) {
  327. console.error('加载标签分类异常:', e)
  328. }
  329. },
  330. // 加载标签列表
  331. async loadTags() {
  332. try {
  333. this.isLoadingTags = true
  334. const baseUrl = process.env.NODE_ENV === 'development'
  335. ? 'http://localhost:8083/api' // 开发环境 - 通过网关
  336. : 'https://your-domain.com/api' // 生产环境
  337. const [error, res] = await uni.request({
  338. url: `${baseUrl}/tag/list`,
  339. method: 'GET',
  340. timeout: 10000
  341. })
  342. if (error) {
  343. console.error('加载标签失败:', error)
  344. return
  345. }
  346. if (res.statusCode === 200 && res.data && res.data.code === 200) {
  347. // 直接使用后端返回的数据,不进行映射(因为Vue的响应式系统会保留原始属性)
  348. // 只保存有分类的标签(category_id不为null)
  349. this.tagList = (res.data.data || []).filter(tag => {
  350. const categoryId = tag.category_id !== null && tag.category_id !== undefined
  351. ? tag.category_id
  352. : (tag.categoryId !== null && tag.categoryId !== undefined ? tag.categoryId : null)
  353. return categoryId !== null && categoryId !== undefined
  354. })
  355. console.log('标签列表加载成功,数量:', this.tagList.length)
  356. if (this.tagList.length > 0) {
  357. console.log('第一条标签数据:', this.tagList[0])
  358. console.log('第一条标签的category_id:', this.tagList[0].category_id, '类型:', typeof this.tagList[0].category_id)
  359. }
  360. } else {
  361. console.error('加载标签失败:', res.data.message)
  362. }
  363. } catch (e) {
  364. console.error('加载标签异常:', e)
  365. } finally {
  366. this.isLoadingTags = false
  367. }
  368. },
  369. // 选择分类
  370. selectCategory(categoryId) {
  371. this.selectedCategoryId = categoryId
  372. console.log('=== 选择分类 ===')
  373. console.log('分类ID:', categoryId, '类型:', typeof categoryId)
  374. console.log('标签总数:', this.tagList.length)
  375. // 使用$nextTick确保DOM更新后再输出
  376. this.$nextTick(() => {
  377. console.log('筛选后的标签数量:', this.filteredTagList.length)
  378. if (this.filteredTagList.length > 0) {
  379. console.log('筛选后的标签名称:', this.filteredTagList.map(t => t.name || t.tag_name || t.tagName))
  380. } else {
  381. console.warn('没有找到匹配的标签!')
  382. // 调试:查看前几条标签的category_id
  383. if (this.tagList.length > 0) {
  384. console.log('前3条标签的category_id:',
  385. this.tagList.slice(0, 3).map(t => ({
  386. name: t.name || t.tag_name,
  387. category_id: t.category_id,
  388. categoryId: t.categoryId
  389. }))
  390. )
  391. }
  392. }
  393. })
  394. },
  395. // 切换标签选择状态
  396. toggleTag(tagId) {
  397. const index = this.selectedTagIds.indexOf(tagId)
  398. if (index > -1) {
  399. // 已选中,取消选择
  400. this.selectedTagIds.splice(index, 1)
  401. } else {
  402. // 未选中,添加到选中列表
  403. this.selectedTagIds.push(tagId)
  404. }
  405. console.log('选中的标签ID:', this.selectedTagIds)
  406. },
  407. onGenderChange(e) {
  408. this.genderIndex = parseInt(e.detail.value) || 0
  409. this.formData.gender = this.genderIndex + 1 // 1-男,2-女
  410. console.log('性别选择 - genderIndex:', this.genderIndex, 'formData.gender:', this.formData.gender)
  411. },
  412. onConstellationChange(e) {
  413. this.constellationIndex = e.detail.value
  414. this.formData.constellation = this.constellationOptions[this.constellationIndex]
  415. },
  416. onMaritalStatusChange(e) {
  417. this.maritalStatusIndex = parseInt(e.detail.value)
  418. // 0-未婚,1-离异,2-丧偶
  419. // 直接使用索引值作为marrStatus的值
  420. this.formData.marrStatus = this.maritalStatusIndex
  421. console.log('婚姻状态选择:', this.maritalStatusIndex, '对应值:', this.formData.marrStatus, '选项:', this.maritalStatusOptions[this.maritalStatusIndex])
  422. },
  423. onDiplomaChange(e) {
  424. this.diplomaIndex = e.detail.value
  425. this.formData.diploma = this.diplomaOptions[this.diplomaIndex]
  426. },
  427. onHouseChange(e) {
  428. this.houseIndex = e.detail.value
  429. this.formData.house = this.houseIndex // 0-无,1-有
  430. },
  431. onCarChange(e) {
  432. this.carIndex = e.detail.value
  433. this.formData.car = this.carIndex // 0-无,1-有
  434. },
  435. async handleSubmit() {
  436. // 验证必填项
  437. if (!this.formData.name || !this.formData.name.trim()) {
  438. uni.showToast({
  439. title: '请输入姓名',
  440. icon: 'none'
  441. })
  442. return
  443. }
  444. // 验证年龄
  445. if (this.formData.age === null || this.formData.age === undefined || this.formData.age === '') {
  446. uni.showToast({
  447. title: '请输入年龄',
  448. icon: 'none'
  449. })
  450. return
  451. }
  452. // 验证性别
  453. if (this.formData.gender === null || this.formData.gender === undefined) {
  454. uni.showToast({
  455. title: '请选择性别',
  456. icon: 'none'
  457. })
  458. return
  459. }
  460. // 验证身高
  461. if (this.formData.height === null || this.formData.height === undefined || this.formData.height === '') {
  462. uni.showToast({
  463. title: '请输入身高',
  464. icon: 'none'
  465. })
  466. return
  467. }
  468. // 验证体重
  469. if (this.formData.weight === null || this.formData.weight === undefined || this.formData.weight === '') {
  470. uni.showToast({
  471. title: '请输入体重',
  472. icon: 'none'
  473. })
  474. return
  475. }
  476. // 验证婚况
  477. if ((this.maritalStatusIndex < 0 || this.maritalStatusIndex === null || this.maritalStatusIndex === undefined)
  478. && (this.formData.marrStatus === null || this.formData.marrStatus === undefined)) {
  479. uni.showToast({
  480. title: '请选择婚况',
  481. icon: 'none'
  482. })
  483. return
  484. }
  485. // 验证住址
  486. if (!this.formData.address || !this.formData.address.trim()) {
  487. uni.showToast({
  488. title: '请输入住址',
  489. icon: 'none'
  490. })
  491. return
  492. }
  493. if (!this.formData.phone || !this.formData.phone.trim()) {
  494. uni.showToast({
  495. title: '请输入手机号',
  496. icon: 'none'
  497. })
  498. return
  499. }
  500. // 验证手机号格式
  501. const phoneReg = /^1[3-9]\d{9}$/
  502. if (!phoneReg.test(this.formData.phone)) {
  503. uni.showToast({
  504. title: '请输入正确的手机号',
  505. icon: 'none'
  506. })
  507. return
  508. }
  509. uni.showLoading({
  510. title: '提交中...'
  511. })
  512. try {
  513. // 确保婚姻状态已选择
  514. if ((this.maritalStatusIndex < 0 || this.maritalStatusIndex === null || this.maritalStatusIndex === undefined)
  515. && (this.formData.marrStatus === null || this.formData.marrStatus === undefined)) {
  516. uni.showToast({
  517. title: '请选择婚况',
  518. icon: 'none'
  519. })
  520. return
  521. }
  522. // 调试:打印原始值
  523. console.log('=== 提交前的原始数据 ===')
  524. console.log('formData.marrStatus:', this.formData.marrStatus, '类型:', typeof this.formData.marrStatus)
  525. console.log('maritalStatusIndex:', this.maritalStatusIndex, '类型:', typeof this.maritalStatusIndex)
  526. console.log('formData.backupPhone:', this.formData.backupPhone, '类型:', typeof this.formData.backupPhone)
  527. console.log('formData.mateSelectionCriteria:', this.formData.mateSelectionCriteria, '类型:', typeof this.formData.mateSelectionCriteria)
  528. // 确定婚姻状态的值:优先使用formData.marrStatus,如果没有则使用maritalStatusIndex
  529. let marrStatusValue = null
  530. if (this.formData.marrStatus !== null && this.formData.marrStatus !== undefined && this.formData.marrStatus !== 'undefined' && this.formData.marrStatus !== 'null' && this.formData.marrStatus !== '') {
  531. const parsed = parseInt(this.formData.marrStatus)
  532. marrStatusValue = isNaN(parsed) ? null : parsed
  533. console.log('婚姻状态处理: formData.marrStatus ->', marrStatusValue)
  534. } else if (this.maritalStatusIndex !== null && this.maritalStatusIndex !== undefined && this.maritalStatusIndex >= 0) {
  535. const parsed = parseInt(this.maritalStatusIndex)
  536. marrStatusValue = isNaN(parsed) ? null : parsed
  537. console.log('婚姻状态处理: maritalStatusIndex ->', marrStatusValue)
  538. } else {
  539. console.log('婚姻状态处理: 未找到有效值')
  540. }
  541. // 处理备用手机号:保留有效值,空字符串转为null
  542. let backupPhoneValue = null
  543. const backupPhoneRaw = this.formData.backupPhone
  544. if (backupPhoneRaw !== null && backupPhoneRaw !== undefined && backupPhoneRaw !== 'undefined' && backupPhoneRaw !== 'null') {
  545. const trimmed = String(backupPhoneRaw).trim()
  546. if (trimmed !== '' && trimmed !== 'undefined' && trimmed !== 'null') {
  547. backupPhoneValue = trimmed
  548. console.log('备用手机号处理: 保留值 ->', backupPhoneValue)
  549. } else {
  550. console.log('备用手机号处理: 空值,转为null')
  551. }
  552. } else {
  553. console.log('备用手机号处理: 原始值为null/undefined')
  554. }
  555. // 处理择偶标准:保留有效值,空字符串转为null
  556. let mateSelectionCriteriaValue = null
  557. const mateSelectionCriteriaRaw = this.formData.mateSelectionCriteria
  558. if (mateSelectionCriteriaRaw !== null && mateSelectionCriteriaRaw !== undefined && mateSelectionCriteriaRaw !== 'undefined' && mateSelectionCriteriaRaw !== 'null') {
  559. const trimmed = String(mateSelectionCriteriaRaw).trim()
  560. if (trimmed !== '' && trimmed !== 'undefined' && trimmed !== 'null') {
  561. mateSelectionCriteriaValue = trimmed
  562. console.log('择偶标准处理: 保留值 ->', mateSelectionCriteriaValue)
  563. } else {
  564. console.log('择偶标准处理: 空值,转为null')
  565. }
  566. } else {
  567. console.log('择偶标准处理: 原始值为null/undefined')
  568. }
  569. // 辅助函数:安全地将值转换为整数,无效值返回null
  570. const safeParseInt = (value) => {
  571. if (value === null || value === undefined || value === '' || value === 'undefined' || value === 'null') {
  572. return null
  573. }
  574. const parsed = parseInt(value)
  575. return isNaN(parsed) ? null : parsed
  576. }
  577. // 辅助函数:安全地获取字符串值,无效值返回null
  578. const safeString = (value) => {
  579. if (value === null || value === undefined || value === '' || value === 'undefined' || value === 'null') {
  580. return null
  581. }
  582. const str = String(value).trim()
  583. return str === '' ? null : str
  584. }
  585. // 转换数据类型,明确构建提交数据对象
  586. const submitData = {
  587. name: safeString(this.formData.name),
  588. age: safeParseInt(this.formData.age),
  589. gender: safeParseInt(this.formData.gender),
  590. constellation: safeString(this.formData.constellation),
  591. height: safeParseInt(this.formData.height),
  592. weight: safeParseInt(this.formData.weight),
  593. // 确保婚姻状态被正确设置(0-未婚,1-离异,2-丧偶)
  594. marrStatus: marrStatusValue, // 直接使用处理后的值,不需要再次转换
  595. diploma: safeString(this.formData.diploma),
  596. income: safeString(this.formData.income),
  597. address: safeString(this.formData.address),
  598. domicile: safeString(this.formData.domicile),
  599. occupation: safeString(this.formData.occupation),
  600. house: safeParseInt(this.formData.house),
  601. car: safeParseInt(this.formData.car),
  602. phone: safeString(this.formData.phone),
  603. // 确保备用手机号被正确传递(保留有效值,null表示空)
  604. backupPhone: backupPhoneValue,
  605. // 确保择偶标准被正确传递(保留有效值,null表示空)
  606. mateSelectionCriteria: mateSelectionCriteriaValue,
  607. // 选中的标签ID列表
  608. tagIds: this.selectedTagIds.length > 0 ? this.selectedTagIds : null
  609. }
  610. console.log('提交数据 - 婚姻状态:', submitData.marrStatus, 'maritalStatusIndex:', this.maritalStatusIndex, 'formData.marrStatus:', this.formData.marrStatus, '选项:', submitData.marrStatus !== null ? this.maritalStatusOptions[submitData.marrStatus] : '未选择')
  611. console.log('提交数据 - 备用手机号:', submitData.backupPhone, '类型:', typeof submitData.backupPhone)
  612. console.log('提交数据 - 择偶标准:', submitData.mateSelectionCriteria, '类型:', typeof submitData.mateSelectionCriteria)
  613. console.log('提交数据 - 标签ID列表:', submitData.tagIds)
  614. console.log('完整提交数据:', JSON.stringify(submitData, null, 2))
  615. // 调用后端接口(通过网关)
  616. const baseUrl = process.env.NODE_ENV === 'development'
  617. ? 'http://localhost:8083/api' // 开发环境 - 通过网关
  618. : 'https://your-domain.com/api' // 生产环境
  619. // 构建请求URL,包含当前登录用户的ID作为参数(将作为红娘ID)
  620. let url = `${baseUrl}/my-resource/add`
  621. // 确保currentUserId是有效的整数才添加到URL中
  622. if (this.currentUserId !== null && this.currentUserId !== undefined) {
  623. const userId = parseInt(this.currentUserId)
  624. if (!isNaN(userId) && userId > 0) {
  625. url += `?currentUserId=${userId}`
  626. }
  627. }
  628. const res = await uni.request({
  629. url: url,
  630. method: 'POST',
  631. data: submitData,
  632. header: {
  633. 'Content-Type': 'application/json'
  634. }
  635. })
  636. uni.hideLoading()
  637. if (res[1].statusCode === 200 && res[1].data.code === 200) {
  638. uni.showToast({
  639. title: '提交成功',
  640. icon: 'success'
  641. })
  642. // 延迟跳转到我的资源列表页面并刷新
  643. setTimeout(() => {
  644. // 获取页面栈,检查是否已经有我的资源列表页面
  645. const pages = getCurrentPages()
  646. const myResourcesPageIndex = pages.findIndex(page => {
  647. return page.route && page.route.includes('matchmaker-workbench/my-resources')
  648. })
  649. if (myResourcesPageIndex >= 0 && myResourcesPageIndex < pages.length - 1) {
  650. // 如果我的资源列表页面已经在页面栈中(且不是当前页),返回到该页面
  651. const delta = pages.length - myResourcesPageIndex - 1
  652. uni.navigateBack({
  653. delta: delta,
  654. success: () => {
  655. // 触发刷新事件
  656. uni.$emit('refreshResourceList')
  657. }
  658. })
  659. } else {
  660. // 如果我的资源列表页面不在页面栈中,跳转到该页面
  661. uni.redirectTo({
  662. url: '/pages/matchmaker-workbench/my-resources',
  663. success: () => {
  664. // 触发刷新事件
  665. uni.$emit('refreshResourceList')
  666. }
  667. })
  668. }
  669. }, 1500)
  670. } else {
  671. uni.showToast({
  672. title: res[1].data.message || '提交失败',
  673. icon: 'none',
  674. duration: 2000
  675. })
  676. }
  677. } catch (error) {
  678. uni.hideLoading()
  679. console.error('提交失败:', error)
  680. uni.showToast({
  681. title: '提交失败,请稍后重试',
  682. icon: 'none'
  683. })
  684. }
  685. }
  686. }
  687. }
  688. </script>
  689. <style lang="scss" scoped>
  690. .resource-input-page {
  691. min-height: 100vh;
  692. background: #FFF9F9;
  693. display: flex;
  694. flex-direction: column;
  695. }
  696. .header {
  697. display: flex;
  698. align-items: center;
  699. justify-content: space-between;
  700. padding: 25rpx 30rpx;
  701. padding-top: calc(25rpx + env(safe-area-inset-top));
  702. background: #FFF9F9;
  703. border-bottom: 1rpx solid #F0F0F0;
  704. .header-left {
  705. width: 60rpx;
  706. }
  707. .back-icon {
  708. font-size: 40rpx;
  709. color: #333;
  710. }
  711. .header-title {
  712. flex: 1;
  713. text-align: center;
  714. font-size: 38rpx;
  715. font-weight: bold;
  716. color: #9C27B0;
  717. }
  718. .header-right {
  719. width: 60rpx;
  720. text-align: right;
  721. }
  722. .more-icon {
  723. font-size: 40rpx;
  724. color: #333;
  725. }
  726. }
  727. .content {
  728. flex: 1;
  729. padding-bottom: 120rpx;
  730. }
  731. .form-container {
  732. padding: 20rpx;
  733. }
  734. .form-section {
  735. background: #FFFFFF;
  736. border-radius: 20rpx;
  737. padding: 30rpx;
  738. margin-bottom: 20rpx;
  739. border: 2rpx solid transparent;
  740. &.active {
  741. border-color: #FF4081;
  742. }
  743. .section-title {
  744. font-size: 32rpx;
  745. font-weight: bold;
  746. color: #333;
  747. margin-bottom: 30rpx;
  748. padding-bottom: 15rpx;
  749. border-bottom: 2rpx solid #F0F0F0;
  750. }
  751. }
  752. .form-item {
  753. margin-bottom: 30rpx;
  754. &:last-child {
  755. margin-bottom: 0;
  756. }
  757. .form-label {
  758. display: block;
  759. font-size: 28rpx;
  760. color: #666;
  761. margin-bottom: 15rpx;
  762. .required {
  763. color: #FF4444;
  764. margin-left: 5rpx;
  765. }
  766. }
  767. .category-buttons {
  768. display: flex;
  769. flex-wrap: wrap;
  770. gap: 12rpx;
  771. margin-bottom: 20rpx;
  772. .category-btn {
  773. padding: 10rpx 20rpx;
  774. background: #F8F8F8;
  775. border: 2rpx solid #E0E0E0;
  776. border-radius: 20rpx;
  777. transition: all 0.3s;
  778. &.active {
  779. background: linear-gradient(135deg, #9C27B0 0%, #BA68C8 100%);
  780. border-color: #9C27B0;
  781. .category-btn-text {
  782. color: #FFFFFF;
  783. font-weight: 600;
  784. }
  785. }
  786. .category-btn-text {
  787. font-size: 24rpx;
  788. color: #666;
  789. }
  790. }
  791. }
  792. .tags-container {
  793. display: flex;
  794. flex-wrap: wrap;
  795. gap: 15rpx;
  796. margin-top: 10rpx;
  797. min-height: 400rpx; /* 设置最小高度,确保空间不变 */
  798. align-content: flex-start; /* 标签从顶部开始排列 */
  799. .tag-item {
  800. padding: 12rpx 24rpx;
  801. background: #F8F8F8;
  802. border: 2rpx solid #E0E0E0;
  803. border-radius: 24rpx;
  804. font-size: 26rpx;
  805. color: #666;
  806. transition: all 0.3s;
  807. flex-shrink: 0; /* 防止标签被压缩 */
  808. &.selected {
  809. background: linear-gradient(135deg, #F3E5F5 0%, #E1BEE7 100%);
  810. border-color: #9C27B0;
  811. color: #7B1FA2;
  812. font-weight: 600;
  813. box-shadow: 0 2rpx 8rpx rgba(156, 39, 176, 0.2);
  814. }
  815. .tag-text {
  816. font-size: 26rpx;
  817. }
  818. }
  819. .tag-loading {
  820. padding: 20rpx;
  821. color: #999;
  822. font-size: 26rpx;
  823. text-align: center;
  824. width: 100%;
  825. min-height: 400rpx; /* 加载时也保持固定高度 */
  826. display: flex;
  827. align-items: center;
  828. justify-content: center;
  829. }
  830. .tag-empty {
  831. padding: 20rpx;
  832. color: #999;
  833. font-size: 26rpx;
  834. text-align: center;
  835. width: 100%;
  836. min-height: 400rpx; /* 空状态时也保持固定高度 */
  837. display: flex;
  838. align-items: center;
  839. justify-content: center;
  840. }
  841. }
  842. .form-input {
  843. width: 100%;
  844. height: 80rpx;
  845. background: #F8F8F8;
  846. border: 2rpx solid #E0E0E0;
  847. border-radius: 10rpx;
  848. padding: 0 20rpx;
  849. font-size: 28rpx;
  850. color: #333;
  851. box-sizing: border-box;
  852. &:focus {
  853. border-color: #9C27B0;
  854. background: #FFFFFF;
  855. }
  856. }
  857. .picker-input {
  858. display: flex;
  859. align-items: center;
  860. justify-content: space-between;
  861. .placeholder {
  862. color: #999;
  863. }
  864. .picker-arrow {
  865. font-size: 24rpx;
  866. color: #999;
  867. }
  868. }
  869. }
  870. .submit-bar {
  871. position: fixed;
  872. bottom: 0;
  873. left: 0;
  874. right: 0;
  875. padding: 20rpx 30rpx;
  876. padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
  877. background: #FFFFFF;
  878. border-top: 1rpx solid #F0F0F0;
  879. box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.05);
  880. .submit-btn {
  881. width: 100%;
  882. height: 88rpx;
  883. background: linear-gradient(135deg, #9C27B0 0%, #BA68C8 100%);
  884. color: #FFFFFF;
  885. font-size: 32rpx;
  886. font-weight: bold;
  887. border-radius: 44rpx;
  888. border: none;
  889. display: flex;
  890. align-items: center;
  891. justify-content: center;
  892. }
  893. }
  894. </style>