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