|
@@ -1,7 +1,271 @@
|
|
|
<template>
|
|
<template>
|
|
|
- <view>// 产品相册内容面板</view>
|
|
|
|
|
|
|
+ <view class="album-panel">
|
|
|
|
|
+ <view class="search-section">
|
|
|
|
|
+ <view class="search-card">
|
|
|
|
|
+ <input
|
|
|
|
|
+ v-model="keyword"
|
|
|
|
|
+ class="search-input"
|
|
|
|
|
+ type="text"
|
|
|
|
|
+ confirm-type="search"
|
|
|
|
|
+ placeholder="请输入相册名称"
|
|
|
|
|
+ placeholder-class="search-placeholder"
|
|
|
|
|
+ @input="handleKeywordInput"
|
|
|
|
|
+ @confirm="handleConfirmSearch"
|
|
|
|
|
+ />
|
|
|
|
|
+
|
|
|
|
|
+ <view v-if="keyword" class="clear-action" @tap.stop="clearKeyword">
|
|
|
|
|
+ <wd-icon name="close" class="clear-icon" />
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+
|
|
|
|
|
+ <view class="album-list">
|
|
|
|
|
+ <ProductAlbumItem
|
|
|
|
|
+ v-for="item in albumList"
|
|
|
|
|
+ :key="item.id"
|
|
|
|
|
+ :item="item"
|
|
|
|
|
+ :share-icon-url="SHARE_ICON_URL"
|
|
|
|
|
+ :active-tab="ACTIVE_TAB"
|
|
|
|
|
+ @select="handleAlbumClick"
|
|
|
|
|
+ />
|
|
|
|
|
+
|
|
|
|
|
+ <view v-if="!loading && albumList.length === 0" class="empty-state">
|
|
|
|
|
+ <wd-empty icon="no-content" tip="暂无内容" />
|
|
|
|
|
+ </view>
|
|
|
|
|
+
|
|
|
|
|
+ <view v-if="albumList.length > 0" class="load-more">
|
|
|
|
|
+ <text v-if="loading">加载中...</text>
|
|
|
|
|
+ <text v-else-if="finished">没有更多了</text>
|
|
|
|
|
+ <text v-else>上拉加载更多</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
</template>
|
|
</template>
|
|
|
|
|
|
|
|
-<script setup lang="ts"></script>
|
|
|
|
|
|
|
+<script setup lang="ts">
|
|
|
|
|
+import { onBeforeUnmount, onMounted, ref } from 'vue'
|
|
|
|
|
+
|
|
|
|
|
+import { debounce } from '@/plugins/debounce'
|
|
|
|
|
+
|
|
|
|
|
+import { getAlbumPageApi } from '@/services/modules/information'
|
|
|
|
|
+import type { AlbumPageNewItem } from '@/services/modules/information/type'
|
|
|
|
|
+
|
|
|
|
|
+import { useUserStore } from '@/stores/modules/user'
|
|
|
|
|
+
|
|
|
|
|
+import ProductAlbumItem from './ProductAlbumItem.vue'
|
|
|
|
|
+
|
|
|
|
|
+const PAGE_SIZE = 10
|
|
|
|
|
+const ACTIVE_TAB = 'album'
|
|
|
|
|
+const SHARE_ICON_URL = 'https://yy-cloud-oss.oss-cn-beijing.aliyuncs.com/img/fenxiang.png'
|
|
|
|
|
+
|
|
|
|
|
+const userStore = useUserStore()
|
|
|
|
|
+
|
|
|
|
|
+const albumList = ref<AlbumPageNewItem[]>([])
|
|
|
|
|
+const loading = ref(false)
|
|
|
|
|
+const finished = ref(false)
|
|
|
|
|
+const currentPage = ref(1)
|
|
|
|
|
+const keyword = ref('')
|
|
|
|
|
+const searchName = ref('')
|
|
|
|
|
+
|
|
|
|
|
+let latestRequestId = 0
|
|
|
|
|
+
|
|
|
|
|
+const buildRequestParams = (current: number) => {
|
|
|
|
|
+ const xcmc = searchName.value.trim()
|
|
|
|
|
+
|
|
|
|
|
+ return {
|
|
|
|
|
+ current,
|
|
|
|
|
+ size: PAGE_SIZE,
|
|
|
|
|
+ shareUserId: userStore.currentUserInfo?.userId || '',
|
|
|
|
|
+ ...(xcmc ? { xcmc } : {}),
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const getAlbumList = async (current = 1, append = false) => {
|
|
|
|
|
+ if (append && (loading.value || finished.value)) return
|
|
|
|
|
+
|
|
|
|
|
+ const requestId = ++latestRequestId
|
|
|
|
|
+
|
|
|
|
|
+ if (!append) {
|
|
|
|
|
+ finished.value = false
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ loading.value = true
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ const { data } = await getAlbumPageApi(buildRequestParams(current))
|
|
|
|
|
+
|
|
|
|
|
+ if (requestId !== latestRequestId) return
|
|
|
|
|
+
|
|
|
|
|
+ const records = Array.isArray(data?.records) ? data.records : []
|
|
|
|
|
+ const page = Number(data?.current) || current
|
|
|
|
|
+ const pages = Number(data?.pages) || 0
|
|
|
|
|
+ const total = Number(data?.total) || 0
|
|
|
|
|
+
|
|
|
|
|
+ albumList.value = append ? [...albumList.value, ...records] : records
|
|
|
|
|
+ currentPage.value = page
|
|
|
|
|
+
|
|
|
|
|
+ const isLastPage = pages > 0 ? page >= pages : records.length < PAGE_SIZE
|
|
|
|
|
+ const isTotalLoaded = total > 0 && albumList.value.length >= total
|
|
|
|
|
+
|
|
|
|
|
+ finished.value = records.length < PAGE_SIZE || isLastPage || isTotalLoaded
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ console.error('获取产品相册分页失败:', error)
|
|
|
|
|
+
|
|
|
|
|
+ uni.showToast({
|
|
|
|
|
+ title: '相册加载失败',
|
|
|
|
|
+ icon: 'none',
|
|
|
|
|
+ })
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ if (requestId === latestRequestId) {
|
|
|
|
|
+ loading.value = false
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const refresh = async () => {
|
|
|
|
|
+ currentPage.value = 1
|
|
|
|
|
+ await getAlbumList(1)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const loadMore = async () => {
|
|
|
|
|
+ await getAlbumList(currentPage.value + 1, true)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const applySearch = async () => {
|
|
|
|
|
+ const nextSearchName = keyword.value.trim()
|
|
|
|
|
+
|
|
|
|
|
+ if (nextSearchName === searchName.value) return
|
|
|
|
|
+
|
|
|
|
|
+ searchName.value = nextSearchName
|
|
|
|
|
+ await refresh()
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const debouncedSearch = debounce(applySearch, {
|
|
|
|
|
+ wait: 500,
|
|
|
|
|
+ leading: false,
|
|
|
|
|
+ trailing: true,
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+const handleKeywordInput = () => {
|
|
|
|
|
+ debouncedSearch()
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const handleConfirmSearch = async () => {
|
|
|
|
|
+ debouncedSearch.cancel()
|
|
|
|
|
+ await applySearch()
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const clearKeyword = async () => {
|
|
|
|
|
+ if (!keyword.value && !searchName.value) return
|
|
|
|
|
+
|
|
|
|
|
+ debouncedSearch.cancel()
|
|
|
|
|
+ keyword.value = ''
|
|
|
|
|
+
|
|
|
|
|
+ if (!searchName.value) return
|
|
|
|
|
+
|
|
|
|
|
+ searchName.value = ''
|
|
|
|
|
+ await refresh()
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const handleAlbumClick = (item: AlbumPageNewItem) => {
|
|
|
|
|
+ if (!item.id) {
|
|
|
|
|
+ uni.showToast({
|
|
|
|
|
+ title: '相册信息异常',
|
|
|
|
|
+ icon: 'none',
|
|
|
|
|
+ })
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ uni.navigateTo({
|
|
|
|
|
+ url: `/pages/information/detail/index?type=album&id=${encodeURIComponent(String(item.id))}`,
|
|
|
|
|
+ })
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+onMounted(refresh)
|
|
|
|
|
+onBeforeUnmount(debouncedSearch.cancel)
|
|
|
|
|
+
|
|
|
|
|
+defineExpose({
|
|
|
|
|
+ refresh,
|
|
|
|
|
+ loadMore,
|
|
|
|
|
+})
|
|
|
|
|
+</script>
|
|
|
|
|
+
|
|
|
|
|
+<style lang="scss" scoped>
|
|
|
|
|
+.album-panel {
|
|
|
|
|
+ min-height: 100%;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.search-section {
|
|
|
|
|
+ position: relative;
|
|
|
|
|
+ z-index: 1;
|
|
|
|
|
+ padding: 24rpx 28rpx 8rpx;
|
|
|
|
|
+ background: #fff;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.search-card {
|
|
|
|
|
+ position: relative;
|
|
|
|
|
+ box-sizing: border-box;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+ height: 72rpx;
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+ background: #fff;
|
|
|
|
|
+ border: 2rpx solid #edf0f5;
|
|
|
|
|
+ border-radius: 999rpx;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.search-input {
|
|
|
|
|
+ box-sizing: border-box;
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+ height: 72rpx;
|
|
|
|
|
+ min-height: 72rpx;
|
|
|
|
|
+ padding: 0 88rpx 0 32rpx;
|
|
|
|
|
+ font-size: 28rpx;
|
|
|
|
|
+ line-height: 72rpx;
|
|
|
|
|
+ color: #333;
|
|
|
|
|
+ background: transparent;
|
|
|
|
|
+ border: 0;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.search-placeholder {
|
|
|
|
|
+ color: #9aa3af;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.clear-action {
|
|
|
|
|
+ position: absolute;
|
|
|
|
|
+ top: 0;
|
|
|
|
|
+ right: 8rpx;
|
|
|
|
|
+ z-index: 2;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ justify-content: center;
|
|
|
|
|
+ width: 72rpx;
|
|
|
|
|
+ height: 72rpx;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.clear-icon {
|
|
|
|
|
+ display: block;
|
|
|
|
|
+ font-size: 32rpx;
|
|
|
|
|
+ line-height: 32rpx;
|
|
|
|
|
+ color: #8f98a8;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.album-list {
|
|
|
|
|
+ padding: 20rpx 28rpx 36rpx;
|
|
|
|
|
+ background: #fff;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.empty-state {
|
|
|
|
|
+ padding: 160rpx 0;
|
|
|
|
|
+ text-align: center;
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
-<style lang="scss" scoped></style>
|
|
|
|
|
|
|
+.load-more {
|
|
|
|
|
+ padding: 30rpx 0 36rpx;
|
|
|
|
|
+ font-size: 24rpx;
|
|
|
|
|
+ line-height: 34rpx;
|
|
|
|
|
+ color: #a3acba;
|
|
|
|
|
+ text-align: center;
|
|
|
|
|
+}
|
|
|
|
|
+</style>
|