my-resources.vue 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864
  1. <template>
  2. <view class="my-resources">
  3. <!-- 顶部导航栏 -->
  4. <view class="header">
  5. <text class="header-title">我的资源</text>
  6. <view class="header-right">
  7. <text class="dropdown-arrow">▼</text>
  8. </view>
  9. </view>
  10. <!-- 搜索栏 -->
  11. <view class="search-bar">
  12. <view class="search-input-wrapper">
  13. <view class="search-icon-small"></view>
  14. <input type="text" class="search-input" placeholder="请输入搜索关键词" v-model="searchKeyword" @input="handleSearch" />
  15. </view>
  16. </view>
  17. <scroll-view scroll-y class="content">
  18. <!-- 资源列表 -->
  19. <view class="resource-item" v-for="(item, index) in resources" :key="index">
  20. <!-- 右上角选中图标 -->
  21. <view class="select-icon">✓-</view>
  22. <view class="resource-header">
  23. <image
  24. :src="item.avatar"
  25. mode="aspectFill"
  26. class="resource-avatar"
  27. @error="handleImageError(index)"
  28. @load="handleImageLoad(index)"
  29. :lazy-load="true"
  30. ></image>
  31. <view class="resource-basic-info">
  32. <view class="name-gender">
  33. <text class="resource-name">{{ item.name }}</text>
  34. <text class="resource-gender">{{ item.gender }}</text>
  35. </view>
  36. <view class="status-tag-wrapper">
  37. <text class="status-tag" :class="{ 'approved': item.status === '已审核', 'pending': item.status === '待审核' }">{{ item.status }}</text>
  38. </view>
  39. </view>
  40. </view>
  41. <view class="resource-details">
  42. <view class="labels">
  43. <text class="label" v-for="(label, idx) in item.labels" :key="idx">{{ label }}</text>
  44. </view>
  45. <view class="requirement-box">
  46. <text class="requirement-label">择偶要求:</text>
  47. <text class="requirement-content">{{ item.requirement }}</text>
  48. </view>
  49. <view class="contact-box">
  50. <text class="contact-label">联系方式:</text>
  51. <text class="contact-number">{{ item.contact }}</text>
  52. <text class="copy-btn" @click.stop="handleCopy(item.contact)">复制</text>
  53. </view>
  54. <view class="action-buttons">
  55. <view class="delete-btn" @click.stop="handleDelete(item.id)">删除</view>
  56. <view class="match-btn" @click.stop="handleMatch(item.id)">精准匹配</view>
  57. </view>
  58. </view>
  59. <!-- 优质资源标签 -->
  60. <view class="quality-tag" v-if="item.isQuality">
  61. <text class="quality-star">★</text>
  62. <text class="quality-text">优质资源</text>
  63. </view>
  64. </view>
  65. </scroll-view>
  66. <!-- 底部添加按钮 -->
  67. <view class="add-button" @click="handleAdd">
  68. <text class="add-button-icon">+</text>
  69. </view>
  70. <!-- 底部导航 -->
  71. <view class="tabbar">
  72. <view class="tabbar-item" @click="navigateToWorkbench">
  73. <view class="tabbar-icon home"></view>
  74. <text class="tabbar-text">工作台</text>
  75. </view>
  76. <view class="tabbar-item active" @click="navigateToMyResources">
  77. <view class="tabbar-icon resources"></view>
  78. <text class="tabbar-text">我的资源</text>
  79. </view>
  80. <view class="tabbar-item" @click="navigateToRanking">
  81. <view class="tabbar-icon trophy"></view>
  82. <text class="tabbar-text">排行榜</text>
  83. </view>
  84. <view class="tabbar-item" @click="navigateToMessage">
  85. <view class="tabbar-icon message">
  86. <view class="badge">3</view>
  87. </view>
  88. <text class="tabbar-text">消息</text>
  89. </view>
  90. <view class="tabbar-item" @click="navigateToMine">
  91. <view class="tabbar-icon mine"></view>
  92. <text class="tabbar-text">我的</text>
  93. </view>
  94. </view>
  95. </view>
  96. </template>
  97. <script>
  98. import api from '@/utils/api.js'
  99. export default {
  100. data() {
  101. return {
  102. searchKeyword: '',
  103. resources: [
  104. {
  105. id: 1,
  106. avatar: 'https://example.com/avatar1.jpg',
  107. name: '小高',
  108. gender: '男',
  109. status: '已审核',
  110. isPlus: true,
  111. labels: ['气质男', '小清新'],
  112. requirement: '165+ 本科',
  113. contact: '123****8912',
  114. isQuality: false
  115. },
  116. {
  117. id: 2,
  118. avatar: 'https://example.com/avatar2.jpg',
  119. name: '小美',
  120. gender: '女',
  121. status: '已审核',
  122. isPlus: true,
  123. labels: ['温柔', '知性', '爱读书'],
  124. requirement: '175+ 硕士 有房',
  125. contact: '138****6543',
  126. isQuality: false
  127. },
  128. {
  129. id: 3,
  130. avatar: 'https://example.com/avatar3.jpg',
  131. name: '阿强',
  132. gender: '男',
  133. status: '待审核',
  134. isPlus: false,
  135. labels: ['阳光', '运动型'],
  136. requirement: '160+ 大专以上',
  137. contact: '159****2234',
  138. isQuality: true
  139. }
  140. ]
  141. }
  142. },
  143. onLoad() {
  144. // 加载我的资源数据
  145. this.loadMyResources()
  146. },
  147. methods: {
  148. // 加载我的资源数据
  149. async loadMyResources() {
  150. try {
  151. // 获取当前登录用户ID
  152. const userInfo = uni.getStorageSync('userInfo') || {}
  153. const userId = uni.getStorageSync('userId')
  154. const currentUserId = userInfo.userId || userId || null
  155. if (!currentUserId) {
  156. console.error('无法获取当前登录用户ID')
  157. uni.showToast({
  158. title: '请先登录',
  159. icon: 'none'
  160. })
  161. return
  162. }
  163. // 调用后端接口,传递当前用户ID作为matchmakerId
  164. const baseUrl = process.env.NODE_ENV === 'development'
  165. ? 'http://localhost:8083/api' // 开发环境 - 通过网关
  166. : 'https://your-domain.com/api' // 生产环境
  167. // 构建查询参数
  168. let url = `${baseUrl}/my-resource/list?currentUserId=${currentUserId}&pageNum=1&pageSize=100`
  169. if (this.searchKeyword && this.searchKeyword.trim()) {
  170. url += `&keyword=${encodeURIComponent(this.searchKeyword.trim())}`
  171. }
  172. const [error, res] = await uni.request({
  173. url: url,
  174. method: 'GET'
  175. })
  176. if (error) {
  177. console.error('加载资源数据失败:', error)
  178. return
  179. }
  180. if (res.statusCode === 200 && res.data && res.data.code === 200) {
  181. // 处理返回的数据
  182. const pageData = res.data.data
  183. console.log('=== 后端返回的完整数据 ===')
  184. console.log('pageData:', JSON.stringify(pageData, null, 2))
  185. if (pageData && pageData.records) {
  186. console.log('records数量:', pageData.records.length)
  187. if (pageData.records.length > 0) {
  188. console.log('第一条记录的完整数据:', JSON.stringify(pageData.records[0], null, 2))
  189. }
  190. // 将后端数据转换为前端需要的格式
  191. this.resources = pageData.records.map(item => {
  192. // 构建择偶要求字符串
  193. let requirement = ''
  194. if (item.height) {
  195. requirement += item.height + '+'
  196. }
  197. if (item.diploma) {
  198. requirement += item.diploma
  199. }
  200. if (item.house === 1) {
  201. requirement += ' 有房'
  202. }
  203. // 处理标签(可以根据实际需求扩展)
  204. const labels = []
  205. if (item.constellation) {
  206. labels.push(item.constellation)
  207. }
  208. if (item.occupation) {
  209. labels.push(item.occupation)
  210. }
  211. // 处理头像URL:确保使用完整的URL
  212. // 尝试多种可能的字段名
  213. let avatarUrl = item.avatarUrl || item.avatar_url || item.avatar || ''
  214. console.log('=== 头像URL处理 ===')
  215. console.log('资源ID:', item.resourceId, '姓名:', item.name)
  216. console.log('原始数据字段:', {
  217. avatarUrl: item.avatarUrl,
  218. avatar_url: item.avatar_url,
  219. avatar: item.avatar,
  220. 'item完整对象': item
  221. })
  222. console.log('提取的avatarUrl值:', avatarUrl, '类型:', typeof avatarUrl, '是否为空:', !avatarUrl)
  223. // 如果头像URL为空或null,设置为空字符串(让CSS背景图显示)
  224. if (!avatarUrl || avatarUrl.trim() === '' || avatarUrl === 'null' || avatarUrl === null || avatarUrl === undefined) {
  225. avatarUrl = '' // 设置为空,让CSS默认背景显示
  226. console.log('⚠️ 头像URL为空,设置为空字符串,将显示CSS默认背景')
  227. } else {
  228. // 确保URL是完整的(如果已经是完整URL则直接使用)
  229. avatarUrl = avatarUrl.trim()
  230. // 如果URL不是以http开头,可能需要拼接基础URL(根据实际情况调整)
  231. if (!avatarUrl.startsWith('http://') && !avatarUrl.startsWith('https://')) {
  232. console.warn('⚠️ 头像URL不是完整URL,可能需要拼接:', avatarUrl)
  233. // 如果是相对路径,可以尝试拼接MinIO基础URL
  234. // avatarUrl = 'http://115.190.125.125:9000/' + avatarUrl
  235. }
  236. // 确保MinIO URL可以正常访问(可能需要处理跨域)
  237. // 如果MinIO配置了公共访问,直接使用URL即可
  238. console.log('✅ 使用用户头像URL:', avatarUrl)
  239. // 验证URL格式
  240. if (avatarUrl.includes('115.190.125.125:9000')) {
  241. console.log('✅ 检测到MinIO URL,URL格式:', avatarUrl)
  242. }
  243. }
  244. console.log('最终设置的avatarUrl:', avatarUrl)
  245. console.log('=== 头像URL处理结束 ===')
  246. return {
  247. id: item.resourceId,
  248. avatar: avatarUrl, // 使用处理后的头像URL
  249. name: item.name || '',
  250. gender: item.gender === 1 ? '男' : item.gender === 2 ? '女' : '未知',
  251. status: '已审核', // 可以根据实际字段判断
  252. isPlus: false,
  253. labels: labels,
  254. requirement: requirement || '暂无要求',
  255. contact: item.phone ? item.phone.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2') : '',
  256. isQuality: false
  257. }
  258. })
  259. console.log('处理后的资源列表:', this.resources)
  260. }
  261. }
  262. } catch (e) {
  263. console.error('加载资源数据失败:', e)
  264. }
  265. },
  266. // 搜索
  267. async handleSearch() {
  268. // 重新加载资源数据,包含搜索关键词
  269. await this.loadMyResources()
  270. },
  271. // 复制联系方式
  272. handleCopy(contact) {
  273. uni.setClipboardData({
  274. data: contact.replace(/\*+/g, ''),
  275. success: () => {
  276. uni.showToast({
  277. title: '复制成功',
  278. icon: 'success'
  279. })
  280. }
  281. })
  282. },
  283. // 删除资源
  284. handleDelete(id) {
  285. uni.showModal({
  286. title: '删除确认',
  287. content: '确定要删除该资源吗?',
  288. success: (res) => {
  289. if (res.confirm) {
  290. // 实现删除功能
  291. console.log('删除资源:', id)
  292. uni.showToast({
  293. title: '删除成功',
  294. icon: 'success'
  295. })
  296. }
  297. }
  298. })
  299. },
  300. // 精准匹配
  301. handleMatch(id) {
  302. // 实现精准匹配功能
  303. console.log('精准匹配:', id)
  304. uni.navigateTo({
  305. url: `/pages/matchmaker-workbench/precise-match?id=${id}`
  306. })
  307. },
  308. // 客户点击事件,跳转到客户详情页面
  309. handleClientClick(id) {
  310. uni.navigateTo({
  311. url: `/pages/matchmaker-workbench/client-detail?id=${id}`
  312. })
  313. },
  314. // 添加资源
  315. handleAdd() {
  316. // 跳转到信息录入页面
  317. uni.navigateTo({
  318. url: '/pages/matchmaker-workbench/resource-input',
  319. success: () => {
  320. console.log('跳转到信息录入页面成功')
  321. },
  322. fail: (err) => {
  323. console.error('跳转失败:', err)
  324. uni.showToast({
  325. title: '页面跳转失败',
  326. icon: 'none'
  327. })
  328. }
  329. })
  330. },
  331. // 导航到工作台
  332. navigateToWorkbench() {
  333. uni.navigateTo({
  334. url: '/pages/matchmaker-workbench/index'
  335. })
  336. },
  337. // 导航到我的资源
  338. navigateToMyResources() {
  339. // 已在我的资源页面,无需跳转
  340. },
  341. // 导航到排行榜
  342. navigateToRanking() {
  343. uni.navigateTo({
  344. url: '/pages/matchmaker-workbench/ranking'
  345. })
  346. },
  347. // 导航到消息
  348. navigateToMessage() {
  349. uni.navigateTo({
  350. url: '/pages/matchmaker-workbench/message'
  351. })
  352. },
  353. // 导航到我的
  354. navigateToMine() {
  355. uni.navigateTo({
  356. url: '/pages/matchmaker-workbench/mine'
  357. })
  358. },
  359. // 图片加载错误处理
  360. handleImageError(index) {
  361. try {
  362. const resource = this.resources && this.resources[index]
  363. if (!resource) {
  364. console.warn('图片加载失败:资源不存在,index:', index)
  365. return
  366. }
  367. const originalUrl = resource.avatar
  368. console.error('❌ 图片加载失败:', {
  369. index: index,
  370. resourceName: resource.name,
  371. resourceId: resource.id,
  372. originalAvatarUrl: originalUrl,
  373. 'URL类型': typeof originalUrl,
  374. 'URL长度': originalUrl ? originalUrl.length : 0,
  375. '是否包含placeholder': originalUrl && originalUrl.includes('placeholder'),
  376. '完整resource对象': resource
  377. })
  378. // 如果图片加载失败,且不是已经设置的默认占位图,则设置为空(显示CSS背景)
  379. // 避免重复设置导致循环
  380. if (originalUrl &&
  381. originalUrl.trim() !== '' &&
  382. !originalUrl.includes('placeholder') &&
  383. !originalUrl.includes('default') &&
  384. !originalUrl.includes('via.placeholder')) {
  385. // 设置为空字符串,让CSS默认背景显示
  386. console.log('图片加载失败,将URL设置为空,显示CSS默认背景')
  387. this.$set(this.resources[index], 'avatar', '')
  388. } else {
  389. console.log('图片加载失败,但URL已经是占位图或空,无需处理')
  390. console.log('当前URL:', originalUrl)
  391. }
  392. } catch (e) {
  393. console.error('处理图片错误时发生异常:', e)
  394. }
  395. },
  396. // 图片加载成功处理
  397. handleImageLoad(index) {
  398. try {
  399. if (this.resources && this.resources[index]) {
  400. console.log('图片加载成功:', {
  401. index: index,
  402. resource: this.resources[index]?.name,
  403. avatarUrl: this.resources[index]?.avatar
  404. })
  405. }
  406. } catch (e) {
  407. console.error('处理图片加载成功时发生异常:', e)
  408. }
  409. }
  410. }
  411. }
  412. </script>
  413. <style lang="scss" scoped>
  414. .my-resources {
  415. min-height: 100vh;
  416. background: #F5F5F5;
  417. display: flex;
  418. flex-direction: column;
  419. }
  420. /* 顶部导航栏 */
  421. .header {
  422. display: flex;
  423. align-items: center;
  424. justify-content: space-between;
  425. padding: 20rpx 30rpx;
  426. padding-top: calc(20rpx + env(safe-area-inset-top));
  427. background: #FFFFFF;
  428. border-bottom: 1rpx solid #F0F0F0;
  429. .header-title {
  430. font-size: 36rpx;
  431. font-weight: bold;
  432. color: #9C27B0;
  433. }
  434. .header-right {
  435. .dropdown-arrow {
  436. font-size: 24rpx;
  437. color: #9C27B0;
  438. font-weight: normal;
  439. }
  440. }
  441. }
  442. /* 搜索栏 */
  443. .search-bar {
  444. padding: 20rpx;
  445. background: #F5F5F5;
  446. .search-input-wrapper {
  447. display: flex;
  448. align-items: center;
  449. background: #FFFFFF;
  450. border-radius: 30rpx;
  451. padding: 12rpx 20rpx;
  452. box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.03);
  453. .search-icon-small {
  454. width: 32rpx;
  455. height: 32rpx;
  456. background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="%23999"><path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/></svg>');
  457. background-size: contain;
  458. background-repeat: no-repeat;
  459. background-position: center;
  460. margin-right: 15rpx;
  461. }
  462. .search-input {
  463. flex: 1;
  464. font-size: 28rpx;
  465. color: #333;
  466. border: none;
  467. outline: none;
  468. background: transparent;
  469. &::placeholder {
  470. color: #999;
  471. }
  472. }
  473. }
  474. }
  475. .content {
  476. flex: 1;
  477. padding: 0 20rpx 140rpx;
  478. }
  479. /* 资源列表 */
  480. .resource-item {
  481. background: #FFFFFF;
  482. border-radius: 20rpx;
  483. margin-bottom: 20rpx;
  484. padding: 25rpx;
  485. position: relative;
  486. box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
  487. /* 右上角选中图标 */
  488. .select-icon {
  489. position: absolute;
  490. top: 20rpx;
  491. right: 20rpx;
  492. width: 40rpx;
  493. height: 40rpx;
  494. background: #FF4444;
  495. color: #FFFFFF;
  496. border-radius: 50%;
  497. display: flex;
  498. align-items: center;
  499. justify-content: center;
  500. font-size: 24rpx;
  501. font-weight: bold;
  502. z-index: 10;
  503. }
  504. .resource-header {
  505. display: flex;
  506. align-items: flex-start;
  507. margin-bottom: 20rpx;
  508. padding-right: 50rpx;
  509. .resource-avatar {
  510. width: 100rpx;
  511. height: 100rpx;
  512. border-radius: 8rpx;
  513. margin-right: 20rpx;
  514. flex-shrink: 0;
  515. background-color: #F5F5F5;
  516. background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="%23CCCCCC"><path d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"/></svg>');
  517. background-size: 60% 60%;
  518. background-position: center;
  519. background-repeat: no-repeat;
  520. }
  521. .resource-basic-info {
  522. flex: 1;
  523. min-width: 0;
  524. .name-gender {
  525. display: flex;
  526. align-items: center;
  527. gap: 10rpx;
  528. margin-bottom: 12rpx;
  529. .resource-name {
  530. font-size: 30rpx;
  531. font-weight: bold;
  532. color: #333;
  533. }
  534. .resource-gender {
  535. font-size: 26rpx;
  536. color: #666;
  537. }
  538. }
  539. .status-tag-wrapper {
  540. display: flex;
  541. align-items: center;
  542. .status-tag {
  543. display: inline-block;
  544. padding: 4rpx 12rpx;
  545. border-radius: 12rpx;
  546. font-size: 22rpx;
  547. font-weight: 500;
  548. &.approved {
  549. background: #E3F2FD;
  550. color: #2196F3;
  551. }
  552. &.pending {
  553. background: #FFF3E0;
  554. color: #FF9800;
  555. }
  556. }
  557. }
  558. }
  559. }
  560. .resource-details {
  561. .labels {
  562. display: flex;
  563. flex-wrap: wrap;
  564. gap: 10rpx;
  565. margin-bottom: 15rpx;
  566. .label {
  567. display: inline-block;
  568. padding: 6rpx 14rpx;
  569. background: #F3E5F5;
  570. color: #9C27B0;
  571. border-radius: 15rpx;
  572. font-size: 22rpx;
  573. font-weight: 500;
  574. }
  575. }
  576. .requirement-box {
  577. background: #F3E5F5;
  578. border-radius: 12rpx;
  579. padding: 16rpx 20rpx;
  580. margin-bottom: 15rpx;
  581. display: flex;
  582. align-items: center;
  583. flex-wrap: wrap;
  584. .requirement-label {
  585. font-size: 26rpx;
  586. color: #7B1FA2;
  587. font-weight: 500;
  588. margin-right: 10rpx;
  589. white-space: nowrap;
  590. }
  591. .requirement-content {
  592. font-size: 26rpx;
  593. color: #7B1FA2;
  594. font-weight: 500;
  595. }
  596. }
  597. .contact-box {
  598. background: #F3E5F5;
  599. border-radius: 12rpx;
  600. padding: 16rpx 20rpx;
  601. margin-bottom: 20rpx;
  602. display: flex;
  603. align-items: center;
  604. flex-wrap: wrap;
  605. .contact-label {
  606. font-size: 26rpx;
  607. color: #7B1FA2;
  608. font-weight: 500;
  609. margin-right: 10rpx;
  610. white-space: nowrap;
  611. }
  612. .contact-number {
  613. flex: 1;
  614. font-size: 26rpx;
  615. color: #7B1FA2;
  616. font-weight: 500;
  617. min-width: 0;
  618. }
  619. .copy-btn {
  620. font-size: 24rpx;
  621. color: #9C27B0;
  622. font-weight: 600;
  623. margin-left: 15rpx;
  624. white-space: nowrap;
  625. }
  626. }
  627. .action-buttons {
  628. display: flex;
  629. justify-content: space-between;
  630. align-items: center;
  631. gap: 15rpx;
  632. .delete-btn {
  633. flex: 1;
  634. padding: 14rpx 0;
  635. background: #FFEBEE;
  636. color: #E91E63;
  637. border-radius: 25rpx;
  638. font-size: 26rpx;
  639. font-weight: 500;
  640. text-align: center;
  641. }
  642. .match-btn {
  643. flex: 1.5;
  644. padding: 14rpx 0;
  645. background: linear-gradient(135deg, #9C27B0 0%, #BA68C8 100%);
  646. color: #FFFFFF;
  647. border-radius: 25rpx;
  648. font-size: 26rpx;
  649. font-weight: 500;
  650. text-align: center;
  651. }
  652. }
  653. }
  654. /* 优质资源标签 */
  655. .quality-tag {
  656. position: absolute;
  657. top: 80rpx;
  658. right: 20rpx;
  659. background: linear-gradient(135deg, #9C27B0 0%, #BA68C8 100%);
  660. color: #FFFFFF;
  661. font-size: 22rpx;
  662. font-weight: bold;
  663. padding: 8rpx 16rpx;
  664. border-radius: 20rpx;
  665. display: flex;
  666. align-items: center;
  667. gap: 4rpx;
  668. box-shadow: 0 2rpx 8rpx rgba(156, 39, 176, 0.3);
  669. z-index: 5;
  670. .quality-star {
  671. font-size: 20rpx;
  672. }
  673. .quality-text {
  674. font-size: 22rpx;
  675. }
  676. }
  677. }
  678. /* 添加按钮 */
  679. .add-button {
  680. position: fixed;
  681. bottom: 130rpx;
  682. right: 40rpx;
  683. width: 100rpx;
  684. height: 100rpx;
  685. border-radius: 50%;
  686. background: linear-gradient(135deg, #9C27B0 0%, #BA68C8 100%);
  687. display: flex;
  688. align-items: center;
  689. justify-content: center;
  690. box-shadow: 0 4rpx 20rpx rgba(156, 39, 176, 0.4);
  691. z-index: 999;
  692. .add-button-icon {
  693. font-size: 60rpx;
  694. color: #FFFFFF;
  695. line-height: 1;
  696. font-weight: bold;
  697. }
  698. }
  699. /* 底部导航 */
  700. .tabbar {
  701. position: fixed;
  702. bottom: 0;
  703. left: 0;
  704. right: 0;
  705. height: 100rpx;
  706. background: #FFFFFF;
  707. border-top: 1rpx solid #F0F0F0;
  708. display: flex;
  709. justify-content: space-around;
  710. align-items: center;
  711. padding-bottom: env(safe-area-inset-bottom);
  712. .tabbar-item {
  713. display: flex;
  714. flex-direction: column;
  715. align-items: center;
  716. gap: 8rpx;
  717. padding: 10rpx 0;
  718. .tabbar-icon {
  719. width: 44rpx;
  720. height: 44rpx;
  721. background-size: contain;
  722. background-repeat: no-repeat;
  723. background-position: center;
  724. position: relative;
  725. .badge {
  726. position: absolute;
  727. top: -8rpx;
  728. right: -8rpx;
  729. background: #FF4444;
  730. color: #FFFFFF;
  731. font-size: 20rpx;
  732. font-weight: bold;
  733. width: 32rpx;
  734. height: 32rpx;
  735. display: flex;
  736. align-items: center;
  737. justify-content: center;
  738. border-radius: 16rpx;
  739. }
  740. }
  741. .tabbar-text {
  742. font-size: 20rpx;
  743. color: #999;
  744. }
  745. &.active {
  746. .tabbar-text {
  747. color: #9C27B0;
  748. font-weight: bold;
  749. }
  750. }
  751. &.home .tabbar-icon {
  752. background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="%23999"><path d="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z"/></svg>');
  753. }
  754. &.active.home .tabbar-icon {
  755. background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="%239C27B0"><path d="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z"/></svg>');
  756. }
  757. &.resources .tabbar-icon {
  758. background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="%239C27B0"><path d="M16 11c1.66 0 2.99-1.34 2.99-3S17.66 5 16 5c-1.66 0-3 1.34-3 3s1.34 3 3 3zm-8 0c1.66 0 2.99-1.34 2.99-3S9.66 5 8 5C6.34 5 5 6.34 5 8s1.34 3 3 3zm0 2c-2.33 0-7 1.17-7 3.5V19h14v-2.5c0-2.33-4.67-3.5-7-3.5zm8 0c-.29 0-.62.02-.97.05 1.16.84 1.97 1.97 1.97 3.45V19h6v-2.5c0-2.33-4.67-3.5-7-3.5z"/></svg>');
  759. }
  760. &.trophy .tabbar-icon {
  761. background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="%23999"><path d="M18 6l-1.42 1.42-1.59-1.59L13 8.17l-1.42-1.42L9 8.17l-1.59-1.59L6 6l3 3V18c0 1.1.9 2 2 2h4c1.1 0 2-.9 2-2V9l3-3zm-4 12H8v-7.5l4-4 4 4V18z"/></svg>');
  762. }
  763. &.active.trophy .tabbar-icon {
  764. background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="%239C27B0"><path d="M18 6l-1.42 1.42-1.59-1.59L13 8.17l-1.42-1.42L9 8.17l-1.59-1.59L6 6l3 3V18c0 1.1.9 2 2 2h4c1.1 0 2-.9 2-2V9l3-3zm-4 12H8v-7.5l4-4 4 4V18z"/></svg>');
  765. }
  766. &.message .tabbar-icon {
  767. background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="%23999"><path d="M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 14H6l-2 2V4h16v12z"/></svg>');
  768. }
  769. &.active.message .tabbar-icon {
  770. background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="%239C27B0"><path d="M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 14H6l-2 2V4h16v12z"/></svg>');
  771. }
  772. &.mine .tabbar-icon {
  773. background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="%23999"><path d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"/></svg>');
  774. }
  775. &.active.mine .tabbar-icon {
  776. background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="%239C27B0"><path d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"/></svg>');
  777. }
  778. }
  779. }
  780. </style>