tj
2025-05-30 d9a780fa538cb7a83aefa04e75cb53185d690d7d
微封装
已修改11个文件
已添加1个文件
476 ■■■■ 文件已修改
components/comment/comment-sub-item.vue 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
composables/useGlobal.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
main.js 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
plugins/storage.js 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
sub-pages/community/index.vue 130 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
sub-pages/community/search-page.vue 137 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
sub-pages/film-list/film-list.vue 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
sub-pages/mine/index.vue 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
sub-pages/protocol/protocol.vue 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
sub-pages/utils/api.ts 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
sub-pages/utils/storageKeys.ts 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
types/index.ts 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
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
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,
  }
}
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({
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
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>
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 {
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;
  }
}
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>
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
}
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;
    }
  }
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))
  }
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;