resource-input.vue 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755
  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. <view class="tags-container">
  133. <view
  134. class="tag-item"
  135. :class="{ 'selected': selectedTagIds.includes(tag.id) }"
  136. v-for="tag in tagList"
  137. :key="tag.id"
  138. @click="toggleTag(tag.id)"
  139. >
  140. <text class="tag-text">{{ tag.name }}</text>
  141. </view>
  142. <view v-if="tagList.length === 0" class="tag-loading">
  143. <text>加载中...</text>
  144. </view>
  145. </view>
  146. </view>
  147. </view>
  148. <!-- 联系方式 -->
  149. <view class="form-section">
  150. <view class="section-title">联系方式</view>
  151. <view class="form-item">
  152. <text class="form-label">手机号<text class="required">*</text></text>
  153. <input type="text" class="form-input" placeholder="请输入手机号" v-model="formData.phone" />
  154. </view>
  155. <view class="form-item">
  156. <text class="form-label">备用手机号</text>
  157. <input type="text" class="form-input" placeholder="请输入备用手机号" v-model="formData.backupPhone" />
  158. </view>
  159. </view>
  160. </view>
  161. </scroll-view>
  162. <!-- 底部提交按钮 -->
  163. <view class="submit-bar">
  164. <button class="submit-btn" @click="handleSubmit">提交</button>
  165. </view>
  166. </view>
  167. </template>
  168. <script>
  169. import api from '@/utils/api.js'
  170. export default {
  171. data() {
  172. return {
  173. activeSection: 'basic',
  174. currentUserId: null, // 当前登录用户的用户ID
  175. formData: {
  176. name: '',
  177. age: null,
  178. gender: null,
  179. constellation: '',
  180. height: null,
  181. weight: null,
  182. marrStatus: null,
  183. diploma: '',
  184. income: '',
  185. address: '',
  186. domicile: '',
  187. occupation: '',
  188. house: null,
  189. car: null,
  190. mateSelectionCriteria: '',
  191. phone: '',
  192. backupPhone: ''
  193. },
  194. genderOptions: ['男', '女'],
  195. genderIndex: 0,
  196. constellationOptions: ['白羊座', '金牛座', '双子座', '巨蟹座', '狮子座', '处女座', '天秤座', '天蝎座', '射手座', '摩羯座', '水瓶座', '双鱼座'],
  197. constellationIndex: 0,
  198. maritalStatusOptions: ['未婚', '离异', '丧偶'],
  199. maritalStatusIndex: -1, // -1表示未选择,0-未婚,1-离异,2-丧偶
  200. diplomaOptions: ['高中', '专科', '本科', '硕士', '博士', '无'],
  201. diplomaIndex: 0,
  202. houseOptions: ['无', '有'],
  203. houseIndex: 0,
  204. carOptions: ['无', '有'],
  205. carIndex: 0,
  206. tagList: [], // 标签列表
  207. selectedTagIds: [] // 选中的标签ID列表
  208. }
  209. },
  210. onLoad() {
  211. // 获取当前登录用户信息
  212. const userInfo = uni.getStorageSync('userInfo') || {}
  213. const userId = uni.getStorageSync('userId')
  214. // 从userInfo中获取用户ID,将作为红娘ID
  215. let rawUserId = userInfo.userId || userId || null
  216. // 验证并转换currentUserId为有效的整数
  217. if (rawUserId !== null && rawUserId !== undefined) {
  218. rawUserId = parseInt(rawUserId)
  219. if (isNaN(rawUserId) || rawUserId <= 0) {
  220. rawUserId = null
  221. }
  222. }
  223. this.currentUserId = rawUserId
  224. console.log('当前登录用户ID:', this.currentUserId)
  225. // 加载标签列表
  226. this.loadTags()
  227. },
  228. methods: {
  229. goBack() {
  230. uni.navigateBack()
  231. },
  232. // 加载标签列表
  233. async loadTags() {
  234. try {
  235. const baseUrl = process.env.NODE_ENV === 'development'
  236. ? 'http://localhost:8083/api' // 开发环境 - 通过网关
  237. : 'https://your-domain.com/api' // 生产环境
  238. const [error, res] = await uni.request({
  239. url: `${baseUrl}/tag/list`,
  240. method: 'GET',
  241. timeout: 10000
  242. })
  243. if (error) {
  244. console.error('加载标签失败:', error)
  245. return
  246. }
  247. if (res.statusCode === 200 && res.data && res.data.code === 200) {
  248. this.tagList = res.data.data || []
  249. console.log('标签列表加载成功:', this.tagList)
  250. } else {
  251. console.error('加载标签失败:', res.data.message)
  252. }
  253. } catch (e) {
  254. console.error('加载标签异常:', e)
  255. }
  256. },
  257. // 切换标签选择状态
  258. toggleTag(tagId) {
  259. const index = this.selectedTagIds.indexOf(tagId)
  260. if (index > -1) {
  261. // 已选中,取消选择
  262. this.selectedTagIds.splice(index, 1)
  263. } else {
  264. // 未选中,添加到选中列表
  265. this.selectedTagIds.push(tagId)
  266. }
  267. console.log('选中的标签ID:', this.selectedTagIds)
  268. },
  269. onGenderChange(e) {
  270. this.genderIndex = e.detail.value
  271. this.formData.gender = this.genderIndex + 1 // 1-男,2-女
  272. },
  273. onConstellationChange(e) {
  274. this.constellationIndex = e.detail.value
  275. this.formData.constellation = this.constellationOptions[this.constellationIndex]
  276. },
  277. onMaritalStatusChange(e) {
  278. this.maritalStatusIndex = parseInt(e.detail.value)
  279. // 0-未婚,1-离异,2-丧偶
  280. // 直接使用索引值作为marrStatus的值
  281. this.formData.marrStatus = this.maritalStatusIndex
  282. console.log('婚姻状态选择:', this.maritalStatusIndex, '对应值:', this.formData.marrStatus, '选项:', this.maritalStatusOptions[this.maritalStatusIndex])
  283. },
  284. onDiplomaChange(e) {
  285. this.diplomaIndex = e.detail.value
  286. this.formData.diploma = this.diplomaOptions[this.diplomaIndex]
  287. },
  288. onHouseChange(e) {
  289. this.houseIndex = e.detail.value
  290. this.formData.house = this.houseIndex // 0-无,1-有
  291. },
  292. onCarChange(e) {
  293. this.carIndex = e.detail.value
  294. this.formData.car = this.carIndex // 0-无,1-有
  295. },
  296. async handleSubmit() {
  297. // 验证必填项
  298. if (!this.formData.name || !this.formData.name.trim()) {
  299. uni.showToast({
  300. title: '请输入姓名',
  301. icon: 'none'
  302. })
  303. return
  304. }
  305. // 验证年龄
  306. if (this.formData.age === null || this.formData.age === undefined || this.formData.age === '') {
  307. uni.showToast({
  308. title: '请输入年龄',
  309. icon: 'none'
  310. })
  311. return
  312. }
  313. // 验证性别
  314. if (this.formData.gender === null || this.formData.gender === undefined) {
  315. uni.showToast({
  316. title: '请选择性别',
  317. icon: 'none'
  318. })
  319. return
  320. }
  321. // 验证身高
  322. if (this.formData.height === null || this.formData.height === undefined || this.formData.height === '') {
  323. uni.showToast({
  324. title: '请输入身高',
  325. icon: 'none'
  326. })
  327. return
  328. }
  329. // 验证体重
  330. if (this.formData.weight === null || this.formData.weight === undefined || this.formData.weight === '') {
  331. uni.showToast({
  332. title: '请输入体重',
  333. icon: 'none'
  334. })
  335. return
  336. }
  337. // 验证婚况
  338. if ((this.maritalStatusIndex < 0 || this.maritalStatusIndex === null || this.maritalStatusIndex === undefined)
  339. && (this.formData.marrStatus === null || this.formData.marrStatus === undefined)) {
  340. uni.showToast({
  341. title: '请选择婚况',
  342. icon: 'none'
  343. })
  344. return
  345. }
  346. // 验证住址
  347. if (!this.formData.address || !this.formData.address.trim()) {
  348. uni.showToast({
  349. title: '请输入住址',
  350. icon: 'none'
  351. })
  352. return
  353. }
  354. if (!this.formData.phone || !this.formData.phone.trim()) {
  355. uni.showToast({
  356. title: '请输入手机号',
  357. icon: 'none'
  358. })
  359. return
  360. }
  361. // 验证手机号格式
  362. const phoneReg = /^1[3-9]\d{9}$/
  363. if (!phoneReg.test(this.formData.phone)) {
  364. uni.showToast({
  365. title: '请输入正确的手机号',
  366. icon: 'none'
  367. })
  368. return
  369. }
  370. uni.showLoading({
  371. title: '提交中...'
  372. })
  373. try {
  374. // 确保婚姻状态已选择
  375. if ((this.maritalStatusIndex < 0 || this.maritalStatusIndex === null || this.maritalStatusIndex === undefined)
  376. && (this.formData.marrStatus === null || this.formData.marrStatus === undefined)) {
  377. uni.showToast({
  378. title: '请选择婚况',
  379. icon: 'none'
  380. })
  381. return
  382. }
  383. // 调试:打印原始值
  384. console.log('=== 提交前的原始数据 ===')
  385. console.log('formData.marrStatus:', this.formData.marrStatus, '类型:', typeof this.formData.marrStatus)
  386. console.log('maritalStatusIndex:', this.maritalStatusIndex, '类型:', typeof this.maritalStatusIndex)
  387. console.log('formData.backupPhone:', this.formData.backupPhone, '类型:', typeof this.formData.backupPhone)
  388. console.log('formData.mateSelectionCriteria:', this.formData.mateSelectionCriteria, '类型:', typeof this.formData.mateSelectionCriteria)
  389. // 确定婚姻状态的值:优先使用formData.marrStatus,如果没有则使用maritalStatusIndex
  390. let marrStatusValue = null
  391. if (this.formData.marrStatus !== null && this.formData.marrStatus !== undefined && this.formData.marrStatus !== 'undefined' && this.formData.marrStatus !== 'null' && this.formData.marrStatus !== '') {
  392. const parsed = parseInt(this.formData.marrStatus)
  393. marrStatusValue = isNaN(parsed) ? null : parsed
  394. console.log('婚姻状态处理: formData.marrStatus ->', marrStatusValue)
  395. } else if (this.maritalStatusIndex !== null && this.maritalStatusIndex !== undefined && this.maritalStatusIndex >= 0) {
  396. const parsed = parseInt(this.maritalStatusIndex)
  397. marrStatusValue = isNaN(parsed) ? null : parsed
  398. console.log('婚姻状态处理: maritalStatusIndex ->', marrStatusValue)
  399. } else {
  400. console.log('婚姻状态处理: 未找到有效值')
  401. }
  402. // 处理备用手机号:保留有效值,空字符串转为null
  403. let backupPhoneValue = null
  404. const backupPhoneRaw = this.formData.backupPhone
  405. if (backupPhoneRaw !== null && backupPhoneRaw !== undefined && backupPhoneRaw !== 'undefined' && backupPhoneRaw !== 'null') {
  406. const trimmed = String(backupPhoneRaw).trim()
  407. if (trimmed !== '' && trimmed !== 'undefined' && trimmed !== 'null') {
  408. backupPhoneValue = trimmed
  409. console.log('备用手机号处理: 保留值 ->', backupPhoneValue)
  410. } else {
  411. console.log('备用手机号处理: 空值,转为null')
  412. }
  413. } else {
  414. console.log('备用手机号处理: 原始值为null/undefined')
  415. }
  416. // 处理择偶标准:保留有效值,空字符串转为null
  417. let mateSelectionCriteriaValue = null
  418. const mateSelectionCriteriaRaw = this.formData.mateSelectionCriteria
  419. if (mateSelectionCriteriaRaw !== null && mateSelectionCriteriaRaw !== undefined && mateSelectionCriteriaRaw !== 'undefined' && mateSelectionCriteriaRaw !== 'null') {
  420. const trimmed = String(mateSelectionCriteriaRaw).trim()
  421. if (trimmed !== '' && trimmed !== 'undefined' && trimmed !== 'null') {
  422. mateSelectionCriteriaValue = trimmed
  423. console.log('择偶标准处理: 保留值 ->', mateSelectionCriteriaValue)
  424. } else {
  425. console.log('择偶标准处理: 空值,转为null')
  426. }
  427. } else {
  428. console.log('择偶标准处理: 原始值为null/undefined')
  429. }
  430. // 辅助函数:安全地将值转换为整数,无效值返回null
  431. const safeParseInt = (value) => {
  432. if (value === null || value === undefined || value === '' || value === 'undefined' || value === 'null') {
  433. return null
  434. }
  435. const parsed = parseInt(value)
  436. return isNaN(parsed) ? null : parsed
  437. }
  438. // 辅助函数:安全地获取字符串值,无效值返回null
  439. const safeString = (value) => {
  440. if (value === null || value === undefined || value === '' || value === 'undefined' || value === 'null') {
  441. return null
  442. }
  443. const str = String(value).trim()
  444. return str === '' ? null : str
  445. }
  446. // 转换数据类型,明确构建提交数据对象
  447. const submitData = {
  448. name: safeString(this.formData.name),
  449. age: safeParseInt(this.formData.age),
  450. gender: safeParseInt(this.formData.gender),
  451. constellation: safeString(this.formData.constellation),
  452. height: safeParseInt(this.formData.height),
  453. weight: safeParseInt(this.formData.weight),
  454. // 确保婚姻状态被正确设置(0-未婚,1-离异,2-丧偶)
  455. marrStatus: marrStatusValue, // 直接使用处理后的值,不需要再次转换
  456. diploma: safeString(this.formData.diploma),
  457. income: safeString(this.formData.income),
  458. address: safeString(this.formData.address),
  459. domicile: safeString(this.formData.domicile),
  460. occupation: safeString(this.formData.occupation),
  461. house: safeParseInt(this.formData.house),
  462. car: safeParseInt(this.formData.car),
  463. phone: safeString(this.formData.phone),
  464. // 确保备用手机号被正确传递(保留有效值,null表示空)
  465. backupPhone: backupPhoneValue,
  466. // 确保择偶标准被正确传递(保留有效值,null表示空)
  467. mateSelectionCriteria: mateSelectionCriteriaValue,
  468. // 选中的标签ID列表
  469. tagIds: this.selectedTagIds.length > 0 ? this.selectedTagIds : null
  470. }
  471. console.log('提交数据 - 婚姻状态:', submitData.marrStatus, 'maritalStatusIndex:', this.maritalStatusIndex, 'formData.marrStatus:', this.formData.marrStatus, '选项:', submitData.marrStatus !== null ? this.maritalStatusOptions[submitData.marrStatus] : '未选择')
  472. console.log('提交数据 - 备用手机号:', submitData.backupPhone, '类型:', typeof submitData.backupPhone)
  473. console.log('提交数据 - 择偶标准:', submitData.mateSelectionCriteria, '类型:', typeof submitData.mateSelectionCriteria)
  474. console.log('提交数据 - 标签ID列表:', submitData.tagIds)
  475. console.log('完整提交数据:', JSON.stringify(submitData, null, 2))
  476. // 调用后端接口(通过网关)
  477. const baseUrl = process.env.NODE_ENV === 'development'
  478. ? 'http://localhost:8083/api' // 开发环境 - 通过网关
  479. : 'https://your-domain.com/api' // 生产环境
  480. // 构建请求URL,包含当前登录用户的ID作为参数(将作为红娘ID)
  481. let url = `${baseUrl}/my-resource/add`
  482. // 确保currentUserId是有效的整数才添加到URL中
  483. if (this.currentUserId !== null && this.currentUserId !== undefined) {
  484. const userId = parseInt(this.currentUserId)
  485. if (!isNaN(userId) && userId > 0) {
  486. url += `?currentUserId=${userId}`
  487. }
  488. }
  489. const res = await uni.request({
  490. url: url,
  491. method: 'POST',
  492. data: submitData,
  493. header: {
  494. 'Content-Type': 'application/json'
  495. }
  496. })
  497. uni.hideLoading()
  498. if (res[1].statusCode === 200 && res[1].data.code === 200) {
  499. uni.showToast({
  500. title: '提交成功',
  501. icon: 'success'
  502. })
  503. // 发送刷新事件,通知我的资源页面刷新列表
  504. uni.$emit('refreshResourceList')
  505. // 延迟返回上一页
  506. setTimeout(() => {
  507. uni.navigateBack()
  508. }, 1500)
  509. } else {
  510. uni.showToast({
  511. title: res[1].data.message || '提交失败',
  512. icon: 'none',
  513. duration: 2000
  514. })
  515. }
  516. } catch (error) {
  517. uni.hideLoading()
  518. console.error('提交失败:', error)
  519. uni.showToast({
  520. title: '提交失败,请稍后重试',
  521. icon: 'none'
  522. })
  523. }
  524. }
  525. }
  526. }
  527. </script>
  528. <style lang="scss" scoped>
  529. .resource-input-page {
  530. min-height: 100vh;
  531. background: #FFF9F9;
  532. display: flex;
  533. flex-direction: column;
  534. }
  535. .header {
  536. display: flex;
  537. align-items: center;
  538. justify-content: space-between;
  539. padding: 25rpx 30rpx;
  540. padding-top: calc(25rpx + env(safe-area-inset-top));
  541. background: #FFF9F9;
  542. border-bottom: 1rpx solid #F0F0F0;
  543. .header-left {
  544. width: 60rpx;
  545. }
  546. .back-icon {
  547. font-size: 40rpx;
  548. color: #333;
  549. }
  550. .header-title {
  551. flex: 1;
  552. text-align: center;
  553. font-size: 38rpx;
  554. font-weight: bold;
  555. color: #9C27B0;
  556. }
  557. .header-right {
  558. width: 60rpx;
  559. text-align: right;
  560. }
  561. .more-icon {
  562. font-size: 40rpx;
  563. color: #333;
  564. }
  565. }
  566. .content {
  567. flex: 1;
  568. padding-bottom: 120rpx;
  569. }
  570. .form-container {
  571. padding: 20rpx;
  572. }
  573. .form-section {
  574. background: #FFFFFF;
  575. border-radius: 20rpx;
  576. padding: 30rpx;
  577. margin-bottom: 20rpx;
  578. border: 2rpx solid transparent;
  579. &.active {
  580. border-color: #FF4081;
  581. }
  582. .section-title {
  583. font-size: 32rpx;
  584. font-weight: bold;
  585. color: #333;
  586. margin-bottom: 30rpx;
  587. padding-bottom: 15rpx;
  588. border-bottom: 2rpx solid #F0F0F0;
  589. }
  590. }
  591. .form-item {
  592. margin-bottom: 30rpx;
  593. &:last-child {
  594. margin-bottom: 0;
  595. }
  596. .form-label {
  597. display: block;
  598. font-size: 28rpx;
  599. color: #666;
  600. margin-bottom: 15rpx;
  601. .required {
  602. color: #FF4444;
  603. margin-left: 5rpx;
  604. }
  605. }
  606. .tags-container {
  607. display: flex;
  608. flex-wrap: wrap;
  609. gap: 15rpx;
  610. margin-top: 10rpx;
  611. .tag-item {
  612. padding: 12rpx 24rpx;
  613. background: #F8F8F8;
  614. border: 2rpx solid #E0E0E0;
  615. border-radius: 24rpx;
  616. font-size: 26rpx;
  617. color: #666;
  618. transition: all 0.3s;
  619. &.selected {
  620. background: linear-gradient(135deg, #F3E5F5 0%, #E1BEE7 100%);
  621. border-color: #9C27B0;
  622. color: #7B1FA2;
  623. font-weight: 600;
  624. box-shadow: 0 2rpx 8rpx rgba(156, 39, 176, 0.2);
  625. }
  626. .tag-text {
  627. font-size: 26rpx;
  628. }
  629. }
  630. .tag-loading {
  631. padding: 20rpx;
  632. color: #999;
  633. font-size: 26rpx;
  634. }
  635. }
  636. .form-input {
  637. width: 100%;
  638. height: 80rpx;
  639. background: #F8F8F8;
  640. border: 2rpx solid #E0E0E0;
  641. border-radius: 10rpx;
  642. padding: 0 20rpx;
  643. font-size: 28rpx;
  644. color: #333;
  645. box-sizing: border-box;
  646. &:focus {
  647. border-color: #9C27B0;
  648. background: #FFFFFF;
  649. }
  650. }
  651. .picker-input {
  652. display: flex;
  653. align-items: center;
  654. justify-content: space-between;
  655. .placeholder {
  656. color: #999;
  657. }
  658. .picker-arrow {
  659. font-size: 24rpx;
  660. color: #999;
  661. }
  662. }
  663. }
  664. .submit-bar {
  665. position: fixed;
  666. bottom: 0;
  667. left: 0;
  668. right: 0;
  669. padding: 20rpx 30rpx;
  670. padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
  671. background: #FFFFFF;
  672. border-top: 1rpx solid #F0F0F0;
  673. box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.05);
  674. .submit-btn {
  675. width: 100%;
  676. height: 88rpx;
  677. background: linear-gradient(135deg, #9C27B0 0%, #BA68C8 100%);
  678. color: #FFFFFF;
  679. font-size: 32rpx;
  680. font-weight: bold;
  681. border-radius: 44rpx;
  682. border: none;
  683. display: flex;
  684. align-items: center;
  685. justify-content: center;
  686. }
  687. }
  688. </style>