index.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498
  1. <template>
  2. <view class="search-page">
  3. <!-- 搜索输入框 -->
  4. <u-input placeholder="请输入搜索的服务" prefixIcon="search" v-model="value" class="rounded-input" @confirm="handleSearch"
  5. clearable @clear="handleClear"></u-input>
  6. <!-- 历史记录 -->
  7. <view class="history-search" v-if="historyList.length > 0">
  8. <view class="history-search-title">历史搜索</view>
  9. <view @click="clearHistory">
  10. <image src="@/static/img/delete-bin-6-fill@1x.png" mode="widthFix" class="delete-icon"></image>
  11. 清空
  12. </view>
  13. </view>
  14. <!-- 历史搜索列表 -->
  15. <view class="history-list" v-if="historyList.length > 0">
  16. <view class="history-item" v-for="(item, index) in historyList" :key="index" @click="searchByHistory(item)">
  17. {{ item.businessTierName || item }}
  18. </view>
  19. </view>
  20. <!-- 无历史记录提示 -->
  21. <view class="no-history" v-if="historyList.length === 0">
  22. <text>暂无搜索历史记录</text>
  23. </view>
  24. <!-- tab -->
  25. <view>
  26. <up-tabs
  27. :list="list2"
  28. @change="handleTabChange">
  29. </up-tabs>
  30. </view>
  31. <!-- 瀑布流展示区域 -->
  32. <view class="home-ranking">
  33. <ServIces :leftList="leftList" :rightList="rightList" :ValueZoneSwiper="ValueZoneSwiper" v-if="userType == 1">
  34. </ServIces>
  35. </view>
  36. <!-- 加载更多组件 -->
  37. <up-loadmore style="margin-top: 40rpx" :status="loadmoreInfo.status" :loadmoreText="loadmoreInfo.loadingText"
  38. :loadingText="loadmoreInfo.loadmoreText" :nomoreText="loadmoreInfo.nomoreText" @loadmore="handleLoadmore"
  39. v-if="userType == 1" />
  40. </view>
  41. </template>
  42. <script setup>
  43. import { ref, reactive, onMounted, watch } from 'vue'
  44. import { onShow, onReachBottom } from '@dcloudio/uni-app'
  45. import { volTierName, volBusinessTypeList, searchHistoryBusinessTireNameHistory } from '@/api/volunteerDetailsApi/details.js'
  46. import RankingList from '@/pages/common/rankingList/index.vue'
  47. import ServIces from '@/components/Services/services.vue'
  48. import store from '@/store'
  49. // 搜索关键词
  50. const value = ref('')
  51. // 瀑布流左右列表数据
  52. const rightList = ref([])
  53. const leftList = ref([])
  54. const ValueZoneSwiper = ref([''])
  55. const list2 = reactive([]);
  56. // 历史搜索列表
  57. const historyList = ref([]);
  58. // 用户类型
  59. const userType = uni.getStorageSync('userType') || 1; //读取本地存储
  60. // 添加搜索定时器引用
  61. const searchTimer = ref(null);
  62. // 分页参数配置
  63. const pages = ref({
  64. current: 1,
  65. pageSize: 10,
  66. total: 0,
  67. serviceCategory: '',
  68. businessTierName: '', // 添加搜索关键词参数
  69. businessManagementId: 0
  70. })
  71. // 加载更多状态配置
  72. const loadmoreInfo = ref({
  73. status: 'loadmore',
  74. loadingText: '努力加载中...',
  75. loadmoreText: '点击加载更多~',
  76. nomoreText: '已经到底啦~',
  77. })
  78. /**
  79. * 获取历史搜索记录
  80. */
  81. const getHistoryList = async () => {
  82. try {
  83. console.log('开始获取历史搜索记录')
  84. const res = await searchHistoryBusinessTireNameHistory({})
  85. console.log('历史搜索API响应:', res)
  86. if (res && res.data) {
  87. // 检查返回的数据类型并适当处理
  88. if (Array.isArray(res.data)) {
  89. // 如果是简单字符串数组,转换为对象数组
  90. if (typeof res.data[0] === 'string') {
  91. historyList.value = res.data.map(item => ({ businessTierName: item }))
  92. } else {
  93. historyList.value = res.data
  94. }
  95. } else {
  96. historyList.value = []
  97. }
  98. console.log('设置历史记录列表:', historyList.value)
  99. } else {
  100. console.log('API返回数据为空或没有data字段')
  101. historyList.value = []
  102. }
  103. } catch (error) {
  104. console.error('获取历史搜索记录失败:', error)
  105. historyList.value = []
  106. }
  107. }
  108. /**
  109. * 清空历史搜索记录
  110. */
  111. const clearHistory = async () => {
  112. try {
  113. // 这里假设接口支持清空历史记录,如果没有专门的清空接口,可能需要另外实现
  114. await searchHistoryBusinessTireNameHistory({ clear: true })
  115. historyList.value = []
  116. uni.showToast({
  117. title: '历史记录已清空',
  118. icon: 'none'
  119. })
  120. } catch (error) {
  121. console.error('清空历史记录失败:', error)
  122. uni.showToast({
  123. title: '清空历史记录失败',
  124. icon: 'none'
  125. })
  126. }
  127. }
  128. /**
  129. * 点击历史记录进行搜索
  130. */
  131. const searchByHistory = (item) => {
  132. // 处理不同的数据格式
  133. value.value = typeof item === 'string' ? item : item.businessTierName
  134. handleSearch()
  135. }
  136. /**
  137. * 处理搜索确认事件
  138. */
  139. const handleSearch = () => {
  140. console.log('触发搜索:', value.value); // 添加日志
  141. // 重置页码
  142. pages.value.current = 1
  143. // 执行搜索
  144. getList()
  145. }
  146. /**
  147. * 加载更多数据
  148. */
  149. async function handleLoadmore(e) {
  150. if (
  151. pages.value.current < Math.ceil(pages.value.total / pages.value.pageSize)
  152. ) {
  153. pages.value.current += 1
  154. loadmoreInfo.value.status = 'loading'
  155. await getList()
  156. } else {
  157. loadmoreInfo.value.status = 'nomore'
  158. }
  159. }
  160. /**
  161. * 处理Tab切换事件
  162. * @param {Object} e - 事件对象,包含选中tab的索引
  163. */
  164. const handleTabChange = (e) => {
  165. // 安全地获取索引
  166. const index = typeof e === 'object' && e !== null ? (e.index !== undefined ? e.index : 0) : (typeof e === 'number' ? e : 0);
  167. console.log('切换到Tab:', e, '索引:', index)
  168. // 重置页码
  169. pages.value.current = 1
  170. // 设置businessManagementId
  171. if (index === 0) {
  172. // 如果是"全部"选项,设置为0
  173. pages.value.businessManagementId = 0
  174. } else if (index > 0 && index < list2.length) {
  175. const selectedTab = list2[index]
  176. console.log('选中的Tab数据:', selectedTab)
  177. // 从businessManagementId字段获取,其次id字段获取
  178. if (selectedTab && selectedTab.businessManagementId !== undefined) {
  179. pages.value.businessManagementId = selectedTab.businessManagementId
  180. } else if (selectedTab && selectedTab.id !== undefined) {
  181. pages.value.businessManagementId = selectedTab.id
  182. } else {
  183. pages.value.businessManagementId = 0
  184. }
  185. // 如果有children中的id,设置到请求参数中
  186. if (selectedTab && selectedTab.children && Array.isArray(selectedTab.children) && selectedTab.children.length > 0) {
  187. // 查找第一个有id的child
  188. const firstChildWithId = selectedTab.children.find(child => child && child.id !== undefined);
  189. if (firstChildWithId && firstChildWithId.id !== undefined) {
  190. console.log('使用children中的ID:', firstChildWithId.id);
  191. pages.value.businessManagementId = firstChildWithId.id;
  192. }
  193. }
  194. } else {
  195. // 默认为0
  196. pages.value.businessManagementId = 0
  197. }
  198. console.log('设置businessManagementId:', pages.value.businessManagementId)
  199. // 执行搜索
  200. getList()
  201. }
  202. /**
  203. * 获取列表数据
  204. * 支持分页和关键词搜索
  205. */
  206. const getList = async () => {
  207. try {
  208. loadmoreInfo.value.status = 'loading'
  209. // 获取地址信息
  210. const address = store.state.user.addressInfo
  211. const { cityCode, latitude, longitude } = address
  212. // 构建请求参数 - 根据API接口定义调整参数
  213. const params = {
  214. pageNum: pages.value.current,
  215. pageSize: pages.value.pageSize,
  216. serviceCategory: pages.value.serviceCategory || '',
  217. businessManagementId: pages.value.businessManagementId,
  218. // API接口使用的模糊搜索参数
  219. businessTierName: value.value, // 名称搜索参数
  220. // 地址相关参数
  221. provinceName: cityCode.data[0], // 省
  222. provinceCode: cityCode.code[0],
  223. cityName: cityCode.data[1], // 市
  224. cityCode: cityCode.code[1],
  225. districtName: cityCode.data[2],
  226. districtCode: cityCode.code[2],
  227. latitude,
  228. longitude,
  229. }
  230. const paramstyle = {
  231. businessTierName: value.value, // 名称搜索参数
  232. businessManagementId: pages.value.businessManagementId,
  233. }
  234. console.log('businessManagementId 类型:', typeof params.businessManagementId);
  235. console.log('businessManagementId 值:', params.businessManagementId);
  236. console.log('paramstyle 对象:', paramstyle);
  237. console.log('paramstyle中的businessManagementId:', paramstyle.businessManagementId);
  238. // 调用API前的最终参数检查
  239. console.log('发送前的最终参数(JSON):', JSON.stringify(params));
  240. console.log('发送前的style参数(JSON):', JSON.stringify(paramstyle));
  241. // 调用API获取数据
  242. const res = await volTierName(params)
  243. // 如果用户输入了搜索关键词,始终更新标签列表以显示相关标签
  244. // 如果没有关键词,且标签列表为空,则获取所有标签
  245. if (value.value || list2.length === 0) {
  246. const res1 = await volBusinessTypeList(paramstyle)
  247. // 搜索成功后更新历史记录
  248. if (value.value) {
  249. getHistoryList() // 刷新历史记录
  250. }
  251. if (res1 && res1.data) {
  252. // 打印原始数据以检查id字段
  253. console.log('原始API返回的Tabs数据:', JSON.stringify(res1.data))
  254. // 记住当前选中的标签名称,以便在重新生成列表后恢复选择
  255. let selectedTabName = '';
  256. if (list2.length > 0 && pages.value && pages.value.businessManagementId !== undefined) {
  257. const currentIndex = Math.min(Math.max(0, pages.value.businessManagementId === 0 ? 0 : 1), list2.length - 1);
  258. selectedTabName = list2[currentIndex]?.name || '';
  259. }
  260. // 清空原有数据
  261. list2.splice(0, list2.length)
  262. // 添加全部选项
  263. list2.push({
  264. name: '全部',
  265. value: '0',
  266. businessManagementId: 0,
  267. id: 0
  268. })
  269. // 转换API返回的数据为up-tabs需要的格式
  270. res1.data.forEach(item => {
  271. // 检查是否有children数组
  272. let childrenIds = [];
  273. if (item.children && Array.isArray(item.children)) {
  274. // 收集所有children的ID
  275. childrenIds = item.children.map(child => child.id).filter(id => id !== undefined);
  276. console.log('项目children IDs:', childrenIds);
  277. }
  278. list2.push({
  279. name: item.businessTierName,
  280. // 尝试获取正确的ID字段 - 优先使用businessManagementId,其次使用id
  281. businessManagementId: item.businessManagementId || item.id || 0,
  282. id: item.id || item.businessManagementId || 0,
  283. // 保存children的id数组
  284. childrenIds: childrenIds,
  285. // 保存完整的children数据
  286. children: item.children || []
  287. })
  288. })
  289. // 尝试恢复选中的标签
  290. if (selectedTabName) {
  291. // 查找具有相同名称的标签
  292. const matchingIndex = list2.findIndex(item => item.name === selectedTabName);
  293. if (matchingIndex !== -1 && pages.value) {
  294. // 设置businessManagementId为找到的匹配项的ID
  295. pages.value.businessManagementId = list2[matchingIndex].businessManagementId || list2[matchingIndex].id || 0;
  296. }
  297. }
  298. console.log('转换后的Tabs数据:', JSON.stringify(list2))
  299. }
  300. }
  301. if (!res || !res.rows) {
  302. return
  303. }
  304. // 如果是第一页,先清空数据
  305. if (pages.value.current === 1) {
  306. leftList.value = []
  307. rightList.value = []
  308. }
  309. // 每次都追加新数据 - 左右瀑布流交替分配
  310. res.rows.forEach((item, index) => {
  311. index % 2 !== 0 ? rightList.value.push(item) : leftList.value.push(item)
  312. })
  313. // 更新分页和加载状态
  314. pages.value.total = res.total
  315. if (pages.value.current >= Math.ceil(res.total / pages.value.pageSize)) {
  316. loadmoreInfo.value.status = 'nomore'
  317. } else {
  318. loadmoreInfo.value.status = 'loadmore'
  319. }
  320. } catch (error) {
  321. // 错误处理
  322. leftList.value = []
  323. rightList.value = []
  324. loadmoreInfo.value.status = 'loadmore'
  325. pages.value.total = 0
  326. console.error('Error fetching data:', error)
  327. }
  328. }
  329. /**
  330. * 页面滚动到底部触发加载更多
  331. */
  332. onReachBottom(() => {
  333. if (
  334. pages.value.current < Math.ceil(pages.value.total / pages.value.pageSize)
  335. ) {
  336. pages.value.current = pages.value.current + 1
  337. loadmoreInfo.value.status = 'nomore'
  338. getList()
  339. }
  340. })
  341. /**
  342. * 组件挂载时获取初始数据
  343. */
  344. onMounted(() => {
  345. // 获取路由参数
  346. const query = uni.getLaunchOptionsSync().query || {}
  347. if (query && query.businessTierName) {
  348. value.value = query.businessTierName
  349. }
  350. // 加载数据
  351. getList()
  352. // 获取历史搜索记录
  353. getHistoryList()
  354. })
  355. /**
  356. * 监听搜索输入变化,自动触发搜索
  357. */
  358. watch(value, (newVal) => {
  359. console.log('输入变化:', newVal); // 添加日志
  360. // 重置页码
  361. pages.value.current = 1
  362. // 延迟搜索,避免频繁请求
  363. clearTimeout(searchTimer.value);
  364. searchTimer.value = setTimeout(() => {
  365. getList()
  366. }, 500)
  367. })
  368. /**
  369. * 处理清除搜索内容
  370. */
  371. const handleClear = () => {
  372. value.value = ''
  373. // 重置页码
  374. pages.value.current = 1
  375. // 清空后显示全部数据
  376. getList()
  377. }
  378. </script>
  379. <style scoped lang="scss">
  380. .search-page {
  381. /* 搜索输入框样式 */
  382. .rounded-input {
  383. width: 682rpx;
  384. height: 64rpx;
  385. overflow: hidden;
  386. border-radius: 36rpx;
  387. background: #F4F4F4;
  388. margin-top: 18rpx;
  389. margin-left: 36rpx;
  390. margin-right: 32rpx;
  391. }
  392. .history-search {
  393. display: flex;
  394. justify-content: space-between;
  395. align-items: center;
  396. margin-top: 24rpx;
  397. margin-left: 44rpx;
  398. margin-right: 44rpx;
  399. padding-bottom: 6rpx;
  400. border-bottom: 2rpx solid #f0f0f0;
  401. .history-search-title {
  402. width: 140rpx;
  403. height: 40rpx;
  404. font-family: PingFang SC;
  405. font-size: 32rpx;
  406. font-weight: 600;
  407. line-height: 40rpx;
  408. letter-spacing: normal;
  409. color: #313131;
  410. }
  411. .delete-icon {
  412. width: 28rpx;
  413. height: 28rpx;
  414. margin-right: 6rpx;
  415. }
  416. }
  417. /* 历史搜索列表样式 */
  418. .history-list {
  419. display: flex;
  420. flex-wrap: wrap;
  421. margin: 20rpx 44rpx;
  422. padding: 10rpx 0;
  423. background-color: #f9f9f9;
  424. border-radius: 16rpx;
  425. .history-item {
  426. padding: 14rpx 28rpx;
  427. margin: 12rpx;
  428. background-color: #EAEAEA;
  429. border-radius: 30rpx;
  430. font-size: 28rpx;
  431. color: #000;
  432. font-weight: 500;
  433. box-shadow: 0 2rpx 6rpx rgba(0, 0, 0, 0.1);
  434. }
  435. }
  436. /* 瀑布流容器样式 */
  437. .home-ranking {
  438. margin-top: 30rpx;
  439. margin-left: 11rpx;
  440. margin-right: 17rpx;
  441. margin-bottom: 120rpx;
  442. }
  443. }
  444. </style>