From d9a780fa538cb7a83aefa04e75cb53185d690d7d Mon Sep 17 00:00:00 2001 From: tj <1378534974@qq.com> Date: 星期五, 30 五月 2025 16:39:07 +0800 Subject: [PATCH] 微封装 --- composables/useGlobal.ts | 2 types/index.ts | 51 ++++++ sub-pages/community/search-page.vue | 137 ++++++++++++++--- sub-pages/film-list/film-list.vue | 33 +-- components/comment/comment-sub-item.vue | 2 sub-pages/community/index.vue | 130 ++++++++++------ sub-pages/utils/api.ts | 47 +++++ main.js | 2 sub-pages/mine/index.vue | 15 + sub-pages/protocol/protocol.vue | 3 plugins/storage.js | 6 sub-pages/utils/storageKeys.ts | 48 ++++++ 12 files changed, 366 insertions(+), 110 deletions(-) diff --git a/components/comment/comment-sub-item.vue b/components/comment/comment-sub-item.vue index 22f6188..fa270cf 100644 --- a/components/comment/comment-sub-item.vue +++ b/components/comment/comment-sub-item.vue @@ -28,8 +28,6 @@ </template> <script setup lang="ts"> -import { defineProps, defineEmits } from 'vue' - const props = defineProps<{ avatar: string nickname: string diff --git a/composables/useGlobal.ts b/composables/useGlobal.ts index bd52b13..57126ca 100644 --- a/composables/useGlobal.ts +++ b/composables/useGlobal.ts @@ -7,11 +7,13 @@ const $http = instance?.proxy?.$http const $util = instance?.proxy?.$util const $store = instance?.proxy?.$store + const $storage = instance?.proxy?.$storage return { $message, $http, $util, $store, + $storage, } } diff --git a/main.js b/main.js index 25e2fac..56c3820 100644 --- a/main.js +++ b/main.js @@ -4,6 +4,7 @@ import { createPinia } from 'pinia' import message from './plugins/message' import http from './plugins/http.js' +import storage from './plugins/storage.js' // import store from './store/index.js' import { @@ -41,6 +42,7 @@ // 全局属性 app.config.globalProperties.$message = message app.config.globalProperties.$http = http + app.config.globalProperties.$storage = storage // app.config.globalProperties.$store = store // uni.$u.setConfig({ diff --git a/plugins/storage.js b/plugins/storage.js index 0f9adf4..c58a9ec 100644 --- a/plugins/storage.js +++ b/plugins/storage.js @@ -1,10 +1,10 @@ // import Vue from 'vue' -let APPID = 'hmy-token' + process.env.PUB_TYPE +let APPID = 'film-token' + process.env.PUB_TYPE // #ifdef PUB_CUSTOMER -APPID = 'hmy-token-customer' +APPID = 'film-token-customer' // #endif // #ifdef PUB_PARTNER -APPID = 'hmy-token-partner' +APPID = 'film-token-partner' // #endif diff --git a/sub-pages/community/index.vue b/sub-pages/community/index.vue index 51f7dbb..4e4684a 100644 --- a/sub-pages/community/index.vue +++ b/sub-pages/community/index.vue @@ -5,7 +5,7 @@ <up-icon name="list" size="40rpx" color="#333333" @click="onSettingClick" /> <view> <!-- <up-tabs :list="list1"> --> - <up-tabs :list="userTabList"> + <up-tabs v-model:current="parentTabIndex" :list="userTabList" @click="onChangeTab"> <!-- <template #left> <up-icon name="list" size="40rpx" color="#333333" /> </template> --> @@ -26,7 +26,7 @@ </up-tabs> </view> <view class="content-area"> - <up-waterfall v-model="flowList"> + <up-waterfall v-model="films"> <template #left="{ leftList }"> <FlowCard v-for="(item, index) in leftList" :key="index" :item="item" @click="handleDetailClick" /> </template> @@ -35,8 +35,9 @@ <FlowCard v-for="(item, index) in rightList" :key="index" :item="item" @click="handleDetailClick" /> </template> </up-waterfall> + <up-loadmore :status="filmStatus" :line="true" /> </view> - <!-- <view v-for="(item, index) in 100">safsafda</view> --> + <view style="height: 50px; width: 100%;"></view> <common-footer flg="0"></common-footer> <setting-popup v-model="settingShow" /> </view> @@ -49,7 +50,12 @@ import { FilmTabCategory, FilmWorksCategory } from '@/enums/dict' import { useGlobal } from '@/composables/useGlobal' const { $http, $message, $store } = useGlobal() -import { getTabList } from '@/sub-pages/utils/api' +import { getParentTabList, getTabList, getFilmWorksBase } from '@/sub-pages/utils/api' +import { FilmWorksQueryDTO } from '@/types/index' + +import { useNavigator } from '@/composables/useNavigator' +const { navigateTo } = useNavigator() + // 控制设置弹窗显示 const settingShow = ref(false); // 方法定义 @@ -63,56 +69,23 @@ // { name: '发现' }, // { name: '苏州' }, // ]); +const parentTabIndex = ref<number>(0) const userTabList = ref<FilmCategoryTree[]>([]) const tabList = ref<FilmCategoryTree[]>([]) -const flowList = ref([ - { - id: 1, - title: 'iPhone 高清全屏壁纸,划走就后悔', - imgurl: 'https://ai-public.mastergo.com/ai/img_res/6a226f9e9652c51cd535c3490535dfeb.jpg', - username: '草莓喵喵', - avatar: '/static/avatar1.jpg', - likes: 1397 - }, - { - id: 2, - title: '高清 4K 全屏手机壁纸 #高质量壁纸', - imgurl: 'https://img.yzcdn.cn/vant/cat.jpeg', - username: '4K wallpaper', - avatar: '/static/avatar2.jpg', - likes: 167 - }, - { - id: 3, - title: 'iPhone 实况动态壁纸 #动态壁纸', - imgurl: 'https://img.yzcdn.cn/vant/cat.jpeg', - username: '图墙精选', - avatar: '/static/avatar3.jpg', - likes: 980 - }, - { - id: 4, - title: '高清小清新壁纸 浪漫的人都有自己的海', - imgurl: 'https://ai-public.mastergo.com/ai/img_res/6a226f9e9652c51cd535c3490535dfeb.jpg', - username: 'wallpaper', - avatar: '/static/avatar4.jpg', - likes: 456 - } -]) - -const handleDetailClick = (item: Item) => { - const url = - item.id === 1 - ? `/sub-pages/film-list/film-official-detail?id=${item.id}` - : `/sub-pages/film-list/film-detail?id=${item.id}` - uni.navigateTo({ url }) +const handleDetailClick = (item: FilmCategoryTree) => { + const urlOfficicl = `/sub-pages/film-list/film-official-detail?id=${item.id}`; + const url = `/sub-pages/film-list/film-detail?id=${item.id}` + navigateTo(url) } const toSearchPage = () => { - uni.navigateTo({ - url: '/sub-pages/community/search-page' - }) + const url = `/sub-pages/community/search-page` + navigateTo(url) +} + +const onChangeTab = (item: FilmCategoryTree) => { + getTabList(item.id, tabList) } const theme = ref('light') @@ -123,9 +96,65 @@ onShow(() => { - getTabList(FilmTabCategory.USER_TYPE,userTabList,false) - getTabList(FilmTabCategory.FILM_TYPE, tabList) + getDefaultTabList() + // 获取电影 + getfilms() }); + +onPullDownRefresh(async () => { + console.log('用户下拉刷新了') + filmPage.value = 1 + getfilms() + uni.stopPullDownRefresh() // 停止下拉刷新动画 +}) + +onReachBottom(() => { + console.log('用户触底了') + getfilms() +}) + +const getDefaultTabList = async () => { + await getParentTabList(userTabList, false) + const curItem = userTabList.value[parentTabIndex.value] + getTabList(curItem.id, tabList) +} + +const films = ref<FilmWorks[]>([]) +const filmPage = ref(1) +const filmSize = 10 +const filmStatus = ref('loading') + +const getfilms = async () => { + if (filmStatus.value === 'nomore') return + + filmStatus.value = 'loading' + + // TODO 暂时使用光影社区的类别 + const query: FilmWorksQueryDTO = { + classify: '', + type: '', + current: filmPage.value, + size: filmSize, + }; + const records = await getFilmWorksBase(query) + + if (records && records.length > 0) { + // 使用 Set 进行去重 + const existingIds = new Set(films.value.map(item => item.id)) + const uniqueRecords = records.filter(item => !existingIds.has(item.id)) + + films.value = [...films.value, ...uniqueRecords] + + // 如果返回的记录数少于请求的 size,说明没有更多数据 + if (records.length < filmSize) { + filmStatus.value = 'noMore' + } + + filmPage.value++ + } else { + filmStatus.value = 'noMore' + } +} </script> <style lang="scss" scoped> @@ -136,5 +165,6 @@ .content-area { // padding: 20rpx; + margin-bottom: 100px; } </style> \ No newline at end of file diff --git a/sub-pages/community/search-page.vue b/sub-pages/community/search-page.vue index b87fa31..40eb1df 100644 --- a/sub-pages/community/search-page.vue +++ b/sub-pages/community/search-page.vue @@ -2,19 +2,30 @@ <view class="search-page"> <!-- 搜索框 --> <view class="search-bar"> - <up-search v-model="searchValue" placeholder="新疆本地小团" show-action @search="onSearch" /> + <up-search v-model="searchValue" :placeholder="searchPlaceHolder" show-action @search="onSearch" + @custom="onSearch" @click-icon="onSearch" /> </view> - <!-- 历史记录 --> - <view class="section"> + <view class="section" v-if="historyTags.length"> <view class="section-title"> <text>历史记录</text> - <up-icon name="trash" size="20" @click="onClearHistory" class="delete-icon" /> - + <view v-if="!showCloseBtn"> <up-icon name="trash" size="20" @click="onClearHistory" + class="delete-icon" /></view> + <view v-else class="action-group"> + <text class="action-btn" @click="onDeleteAll">全部删除</text> + <text class="action-btn" @click="onClearHistory">完成</text> + </view> </view> <view class="tag-list"> - <up-tag v-for="(item, index) in historyTags" :key="index" plain size="mini" class="tag" shape="circle" - borderColor="#E9E9E9" color="#333333" textSize="20rpx">{{ item }}</up-tag> + <!-- <up-tag v-for="(item, index) in historyTags" :key="index" plain size="mini" class="tag" shape="circle" + borderColor="#E9E9E9" color="#333333" textSize="20rpx">{{ item }} + <up-icon name="close" size="10" @click="handleDeleteHistoryOne(item, index)" /> + </up-tag> --> + <view v-for="(item, index) in historyTags" :key="index" @click.stop class="tag2"> + <view class="tag-text">{{ item }}</view> + <up-icon v-if="showCloseBtn" name="close" size="10" + @click.stop="handleDeleteHistoryOne(item, index)" /> + </view> </view> </view> @@ -57,9 +68,9 @@ index === 2 ? 'gradient-yellow' : '' ]">{{ index + 1 }}</text> <text class="hot-text">{{ item.title }}</text> - <up-tag v-if="item.tag" + <up-tag v-if="item.tag" :type="item.tag === '热' ? 'error' : item.tag === '荐' ? 'warning' : 'primary'" size="mini" - class="tag"> + class="tag" > {{ item.tag }} </up-tag> </view> @@ -67,18 +78,29 @@ </view> </view> </view> + + + <up-modal v-model:show="showModal" title="" showCancelButton confirmText="确定" cancelText="取消" @confirm="onConfirm"> + <template #default> + <text style="color: red;">删除全部搜索历史</text> + </template> + </up-modal> + + </view> </template> <script setup lang="ts"> -import { ref } from 'vue' +import { handleError, ref } from 'vue' import { onShow, onPullDownRefresh, onReachBottom } from '@dcloudio/uni-app' +import { useGlobal } from '@/composables/useGlobal' +const { $http, $message, $storage } = useGlobal() +import { getSearchHistory, addSearchHistory, clearSearchHistory, removeSearchHistoryByIndex } from '@/sub-pages/utils/storageKeys' const searchValue = ref('') - -const historyTags = ref<string[]>([ - '新疆靠谱旅行社', '重庆往返乌鲁木齐', '旅游行程安排文案', '旅游行程安排表' -]) +const searchPlaceHolder = ref('新疆本地小团') +const historyTags = ref<string[]>([]) +const showModal = ref(false) const guessList = [ @@ -99,17 +121,48 @@ { title: '你可能不认识汪 but 一定看过汪', tag: '荐', views: '603.5万' } ] -function onSearch(val: string) { - console.log('搜索内容:', val) -} - -function onClearHistory() { - console.log('清除历史记录') - // 这里可以清空 historyTags 或调用实际删除逻辑 - historyTags.value.length = 0 +const onSearch = () => { + // 这里判断,如果输入的内容是空的话,那么将searchPlaceHolder的值赋值给searchValue + if (!searchValue.value) { + searchValue.value = searchPlaceHolder.value + } + addSearchHistory(searchValue.value) + historyTags.value = getSearchHistory() + searchValue.value = '' + console.log('搜索内容:', searchValue.value) + // TODO 跳转到搜索页面 } +const showCloseBtn = ref(false) // 控制按钮显示 +const onClearHistory = () => { + showCloseBtn.value = !showCloseBtn.value +} +const onDeleteAll = () => { + showModal.value = true +} + +const onConfirm = ()=>{ + clearSearchHistory() + historyTags.value = getSearchHistory() + showModal.value = false +} + +/** + * 删除一个 historyTags 中的项目 + * @param item 列表项目 + * @param index + */ +const handleDeleteHistoryOne = (item: String, index: Number) => { + removeSearchHistoryByIndex(index) + historyTags.value = getSearchHistory() + onClearHistory() +} + + +onShow(() => { + historyTags.value = getSearchHistory() +}); </script> <style lang="scss" scoped> @@ -138,7 +191,21 @@ .icon-list { display: flex; gap: 10rpx; + color: #999; } + + .action-group { + display: flex; + gap: 24rpx; + /* 控制两个按钮之间的间距 */ + + .action-btn { + color: #999; + font-size: 24rpx; + } + } + + } .hot-title { @@ -151,7 +218,28 @@ gap: 10rpx; .tag { - padding: 6rpx 20rpx; + padding: 6rpx; + display: flex; + } + + .tag2 { + display: inline-flex; + align-items: center; + border: 1px solid #E9E9E9; + border-radius: 50rpx; + /* 模拟 shape="circle" */ + padding: 4rpx 10rpx; + font-size: 20rpx; + /* textSize */ + color: #333333; + /* color */ + margin: 6rpx; + background-color: transparent; + /* plain 样式 */ + } + + .tag-text { + margin-right: 6rpx; } } @@ -189,7 +277,7 @@ .hot-text { font-weight: bold; - font-size: 28rpx; + font-size: 26rpx; margin-left: 10rpx; max-width: 400rpx; display: inline-block; @@ -219,6 +307,7 @@ .tag { margin-left: 10rpx; + font-size: 22rpx; } .hot-views { diff --git a/sub-pages/film-list/film-list.vue b/sub-pages/film-list/film-list.vue index f069e46..acdf69c 100644 --- a/sub-pages/film-list/film-list.vue +++ b/sub-pages/film-list/film-list.vue @@ -30,14 +30,14 @@ <script setup lang="ts"> import { ref, onMounted } from 'vue' import { onLoad, onShow, onPullDownRefresh, onReachBottom } from '@dcloudio/uni-app' -import { FilmCategoryTree,FilmWorks } from '@/types/index' +import { FilmCategoryTree,FilmWorks,FilmWorksQueryDTO } from '@/types/index' import { useGlobal } from '@/composables/useGlobal' const { $http, $message, $store } = useGlobal() import { FilmTabCategory,FilmWorksCategory } from '@/enums/dict' import { useNavigator } from '@/composables/useNavigator' const { navigateTo } = useNavigator() -import { getTabList } from '@/sub-pages/utils/api' +import { getTabList,getFilmWorksBase } from '@/sub-pages/utils/api' const theme = ref('light') const search = ref('') @@ -66,7 +66,7 @@ onMounted(() => { - flowList.value = items + }) onShow(() => { @@ -100,7 +100,13 @@ filmStatus.value = 'loading' // TODO 暂时使用光影社区的类别 - const records = await getFilmWorksBase('', filmSize, filmPage.value) + const query: FilmWorksQueryDTO = { + classify: '', + type: '', + current: filmPage.value, + size: filmSize, + }; + const records = await getFilmWorksBase(query) if (records && records.length > 0) { // 使用 Set 进行去重 @@ -120,23 +126,8 @@ } } -const getFilmWorksBase = async (type: String, pageSize: Number, currentPage: Number) => { - const { - code, data - } = await $http.request('get', '/api/filmWorks/list', { - params: { - classify: type, - size: pageSize, - current: currentPage - } - }) - if (code == 0) { - return data.records - } else { - $message.showToast('系统异常,无法获取数据') - return null; - } -} + + diff --git a/sub-pages/mine/index.vue b/sub-pages/mine/index.vue index 6b913e7..977b58d 100644 --- a/sub-pages/mine/index.vue +++ b/sub-pages/mine/index.vue @@ -27,7 +27,7 @@ </view> </view> - <view class="edit-profile">点击这里,填写简介</view> + <view class="edit-profile" @click="goToProfile">点击这里,填写简介</view> <view class="stats"> <view class="stat-row"> @@ -113,10 +113,8 @@ ) onShow(() => { - console.log('我的') getLocation() }); - // 当前 tab 索引 @@ -151,17 +149,22 @@ }); // 方法定义 -function onSettingClick() { +const onSettingClick= ()=> { settingShow.value = true; } -function onTabChange(item: { index: number }) { +const onTabChange=(item: { index: number })=> { current.value = item.index; } -function onSwiperChange(e: any) { +const onSwiperChange=(e: any) => { current.value = e.detail.current; } + + +const goToProfile= ()=> { + navigateTo('/profile'); +} </script> diff --git a/sub-pages/protocol/protocol.vue b/sub-pages/protocol/protocol.vue index fd85cb3..ee3e0b3 100644 --- a/sub-pages/protocol/protocol.vue +++ b/sub-pages/protocol/protocol.vue @@ -17,12 +17,10 @@ onMounted(() => { const localTheme = uni.getStorageSync('theme') || 'light' theme.value = localTheme - console.log('theme', theme.value) }) onLoad((options: { title?: string }) => { const title = options.title ? decodeURIComponent(options.title) : ''; - console.log('接收到的title:', title); uni.setNavigationBarTitle({ title: title }); @@ -34,7 +32,6 @@ $message.showLoading() const { data } = await $http.request('get', '/api/config/content/list/view?id=' +title, {}) $message.hideLoading() - console.log('data:', data) protocol.value=data } diff --git a/sub-pages/utils/api.ts b/sub-pages/utils/api.ts index 1eed362..4dcc8a9 100644 --- a/sub-pages/utils/api.ts +++ b/sub-pages/utils/api.ts @@ -4,7 +4,39 @@ import { FilmCategoryTree, FilmWorks } from '@/types/index' import http from '@/plugins/http.js' import message from '@/plugins/message' +import {FilmWorksQueryDTO} from '@/types/index' +export const getParentTabList = async ( + targetList: Ref<FilmCategoryTree[]>, + isShowDefault: boolean = true +) => { + const { code, data } = await http.request('get', '/api/film/category/parentList', { + params: { } + }) + + if (code === 0) { + const defaultOption: FilmCategoryTree = { + id: '', + name: '全部', + imageUrl: '', + color: '', + sortBy: 0, + shown: true, + levelLimit: '', + childrenCount: 0, + children: [] + } + if (isShowDefault) { + targetList.value = [defaultOption, ...data] + } else { + targetList.value = data + } + + } else { + message.showToast('系统异常,无法获取数据') // 或者用 uni.showToast() + return null + } +} export const getTabList = async ( parentId: string, targetList: Ref<FilmCategoryTree[]>, @@ -33,9 +65,22 @@ targetList.value = data } - console.log('tabList', targetList.value) } else { message.showToast('系统异常,无法获取数据') // 或者用 uni.showToast() return null } } + + +export const getFilmWorksBase = async (query: FilmWorksQueryDTO) => { + const { code, data } = await http.request('get', '/api/filmWorks/list', { + params: query + }); + + if (code === 0) { + return data.records; + } else { + message.showToast('系统异常,无法获取数据'); + return null; + } + } \ No newline at end of file diff --git a/sub-pages/utils/storageKeys.ts b/sub-pages/utils/storageKeys.ts new file mode 100644 index 0000000..0dc6315 --- /dev/null +++ b/sub-pages/utils/storageKeys.ts @@ -0,0 +1,48 @@ +import storage from "../../plugins/storage"; + +export const STORAGE_KEYS = { + SEARCH_HISTORY: 'film:search:history', + USER_TOKEN: 'film:user:token', + // 你可以继续拓展其他常量 + } + + export function getSearchHistory(): string[] { + const history = storage.getItem(STORAGE_KEYS.SEARCH_HISTORY) + return history ? JSON.parse(history) : [] + } + + export function addSearchHistory(keyword: string) { + // 去除前后空格 + const trimmed = keyword.trim() + if (!trimmed) return // 空字符串不保存 + + let history = getSearchHistory() + + // 如果已经存在就不保存 + if (history.includes(trimmed)) return + + history = [trimmed, ...history] + + // 限制最多保留10条 + if (history.length > 10) history = history.slice(0, 10) + + storage.setItem(STORAGE_KEYS.SEARCH_HISTORY, JSON.stringify(history)) + } + + export function clearSearchHistory() { + storage.removeItem(STORAGE_KEYS.SEARCH_HISTORY) + } + + /** + * 根据索引删除某一项搜索历史 + * @param index 要删除的项的索引 + */ +export function removeSearchHistoryByIndex(index: number) { + const history = getSearchHistory() + + if (index < 0 || index >= history.length) return // 防止越界 + + history.splice(index, 1) // 删除指定索引的项 + + storage.setItem(STORAGE_KEYS.SEARCH_HISTORY, JSON.stringify(history)) + } \ No newline at end of file diff --git a/types/index.ts b/types/index.ts index 4984d83..27e31d1 100644 --- a/types/index.ts +++ b/types/index.ts @@ -22,6 +22,57 @@ content: string; } +export interface PaginationQuery { + /** 当前页码 */ + current?: number; + + /** 每页数量 */ + size?: number; + + /** 排序字段和方向列表 */ + orders?: OrderItem[]; + + /** 是否优化 count 查询 */ + optimizeCountSql?: boolean; + + /** 是否查询总数 */ + isSearchCount?: boolean; +} + +/** 排序项定义 */ +export interface OrderItem { + column: string; + asc: boolean; +} + + +export interface FilmWorksQueryDTO extends PaginationQuery { + /** 中文名称 */ + nameCn?: string; + + /** 片场类型(FILMSET_TYPE) */ + type?: string; + + /** 发布状态(COMMON_PUBLISH_STATUS) */ + status?: string; + + /** 分类:1-为你精选,2-光影社区 */ + classify?: number; + + /** 创建日期(yyyy-mm-dd)开始 */ + createDateBeginStr?: string; + + /** 创建日期(yyyy-mm-dd)结束 */ + createDateEndStr?: string; + + /** 创建日期开始(LocalDateTime 对应 ISO 字符串或 Date 类型) */ + createDateBegin?: string | Date; + + /** 创建日期结束(LocalDateTime 对应 ISO 字符串或 Date 类型) */ + createDateEnd?: string | Date; +} + + export interface FilmWorks { id?: number; -- Gitblit v1.9.3