`Pinia` + `Formily` + `useTable` 实现搜索条件缓存方案

Pinia + Formily + useTable 实现搜索条件缓存方案

背景

在当前的应用体验中,每当用户刷新页面或退出系统时,之前的搜索条件就会消失不见。为了进一步提升工作效率并增强用户体验,希望能够实现这样一个功能:即使用户进行了页面刷新或是暂时离开了平台,再次返回时也能自动恢复其上一次的搜索设置。这样不仅能够大大节省用户重复输入的时间与精力,还能让整个使用过程变得更加流畅、便捷。

方案

在我们对表单系统进行深度重构的过程中,我们选择了引入Formily这一先进的表单解决方案。通过这一方案,我们实现了表单功能的显著优化与升级。现在,当用户二次启用表单时,他们可以直接利用Formily提供的API进行统一赋值操作,无需再手动逐一填写各项数据。更为便捷的是,这种赋值方式还能够自动触发表单中的联动关系,确保所有相关字段都能根据赋值内容自动更新,从而极大地减少了人工操作的需求,提高了工作效率。

难点

1、需要细致梳理表单项之间的交互逻辑,并巧妙构思如何将其转化为Formily所提供的强大联动方案。在此过程中,还需考虑是否需要将这些逻辑单独抽离出来,封装成可复用的组件,以便在后续的开发中能够轻松调用。同时,对于自定义组件,还需要加入桥接层,使其能够完美融入Formily的规范体系,确保整个表单系统的一致性和高效性。
2、在使用远程搜索功能的下拉表单项时,我遇到了一个挑战:当用户再次启用该表单项时,数据源会丢失,导致无法正常回显之前选择的内容。为了解决这个问题,需要深入分析远程搜索的工作原理,并寻找合适的方法来保存和恢复数据源,以确保用户能够顺畅地使用这一功能。
3、对于选择性表单项,需要处理立即触发请求的情况。然而,Formily表单并没有提供统一的API来处理这种需求。因此,需要自行设计和实现相应的处理机制,以确保在用户做出选择后,能够立即触发请求并更新表单状态。这将需要深入了解Formily的底层原理,并灵活运用其提供的API和工具来实现我们的目标。

实现步骤

step1:重构表单

使用Formily重构现有搜索区域表单

1、基础内容

<template>
  <div class="filter-content">
    <common-schema-form :form="form" class="filter-form">
      <component :is="FormilySchema.SchemaField" :schema="formSchema" />
    </common-schema-form>
    <el-button class="search-class" @click="onSearch">{
  
  { t('I18N_COMMON_SEARCH') }}</el-button>
  </div>
</template>
<script lang='ts' setip>
import CommonSchemaForm from '@pkg/common-lib/components/common/common-schema-form/index.vue'
import { useSchema } from '@pkg/common-lib/components/common/common-schema-form/useSchema'
import { createForm, ISchema, onFieldValueChange } from '@pkg/common-lib/components/common/common-schema-form/config'

// 实例化表单 Form
const form = createForm({})

// 创建表单 Schema
const formSchema = reactive<ISchema>({
  // ... 符合 JsonSchema 规范
}) 

</script>

2、根据现有表单项组件构建Formily组件库
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

3、根据现有表单项之间的联动关系添加Formily联动

const form = createForm({
   
   
  effects() {
   
   
    // 监听国家、省份城市 动态获取场站数据源
    onFieldValueChange('*(countryId, [province,city])', (filed, form) => {
   
   
      if (!first.value) {
   
   
        form.setFieldState('psIds', (state) => {
   
   
          state.value = undefined
        })
      }
      // 获取数据
      const params: any = {
   
   
        pageNo: 1,
        pageSize: 10000,
      }
      if (sungrowUtils.isChinaSite()) {
   
   
        const [province, city] = form.getFieldState('[province,city]').value
        params.provinceId = province
        params.cityId = city
      } else {
   
   
        params.countryId = form.getFieldState('countryId').value
      }
      // 获取场站数据源
      getStationList(params)
    })
    // 监听设备型号 动态获取固件版本数据源
   
<think>好的,用户现在想要寻找使用Vue3、Pinia和TypeScript实现轮播图功能的示例代码或教程。我需要结合之前的对话和提供的引用内容来生成合适的回答。 首先,回顾之前的对话,用户之前询问过Vue3横向轮播图的实现,我给出了使用vue3-carousel库的示例。现在用户希望加入Pinia和TypeScript,可能需要将轮播图的状态管理部分用Pinia处理,同时代码要符合TypeScript的类型检查。 接下来,查看用户提供的引用内容。引用[1]、[3]、[4]、[5]提到了Pinia在Vue3中的使用,包括安装步骤、与TypeScript的结合,以及如何在项目中集成。特别是引用[5]提到了创建Vue3+TS+Vite+Pinia项目的步骤,这可能对搭建环境有帮助。 用户的需求是示例代码,所以需要提供一个包含Vue3组件、Pinia状态管理和TypeScript类型的完整示例。需要确保代码结构清晰,各部分分工明确,比如用Pinia管理轮播图的数据和当前激活的索引,组件负责渲染和交互。 考虑到轮播图通常需要自动播放、切换动画等功能,可能需要用到Vue的过渡组件或第三方库如vue3-carousel,但用户这次希望结合Pinia,所以可能需要手动实现一些功能,或者结合现有库进行状态管理。 需要检查之前的回答是否已经使用了vue3-carousel,现在可能需要调整代码,将数据源从本地转移到Pinia store中,同时添加TypeScript接口定义。此外,还需要处理Pinia的store创建、在组件中的调用,以及响应式更新。 另外,用户可能希望代码示例包含响应式布局、自动播放、切换动画等常见功能,同时展示如何在TypeScript环境下正确使用Pinia的状态和动作。还需要注意代码的模块化,比如将轮播图的数据存储在Pinia中,方便其他组件复用或修改。 最后,确保回答符合用户的结构要求,包括安装步骤、代码示例、核心实现说明和相关问题。可能还需要引用之前的引用内容,比如安装Pinia的步骤来自引用[3],项目搭建参考引用[5]等。</think>### Vue3 + Pinia + TypeScript 轮播图实现示例 #### 一、环境搭建(基于引用[5]) ```bash # 创建项目 yarn create vite vue3-pinia-carousel --template vue-ts # 安装依赖 yarn add pinia @vueuse/core sass ``` #### 二、核心实现代码 1. **Pinia Store 定义**(结合引用[1][3]) ```typescript // stores/carousel.ts import { defineStore } from 'pinia' interface CarouselItem { id: number image: string title: string description: string } export const useCarouselStore = defineStore('carousel', { state: () => ({ currentIndex: 0, items: [] as CarouselItem[], intervalId: null as number | null }), actions: { initItems(items: CarouselItem[]) { this.items = items }, next() { this.currentIndex = (this.currentIndex + 1) % this.items.length }, prev() { this.currentIndex = (this.currentIndex - 1 + this.items.length) % this.items.length }, startAutoPlay(interval: number = 3000) { this.intervalId = window.setInterval(() => this.next(), interval) }, stopAutoPlay() { if (this.intervalId) { clearInterval(this.intervalId) this.intervalId = null } } }, getters: { activeItem: (state) => state.items[state.currentIndex] || null } }) ``` 2. **轮播组件实现**(包含TS类型) ```vue <!-- components/Carousel.vue --> <template> <div class="carousel-container"> <transition :name="transitionName"> <div v-for="(item, index) in items" :key="item.id" v-show="index === currentIndex" class="carousel-item" > <img :src="item.image" :alt="item.title" /> <div class="carousel-caption"> <h3>{{ item.title }}</h3> <p>{{ item.description }}</p> </div> </div> </transition> <button @click="prev" class="carousel-control prev">‹</button> <button @click="next" class="carousel-control next">›</button> </div> </template> <script setup lang="ts"> import { computed, onMounted, onUnmounted } from 'vue' import { useCarouselStore } from '@/stores/carousel' const store = useCarouselStore() const transitionName = ref('slide-next') // 初始化数据(可通过API获取) const initialItems: CarouselItem[] = [ { id: 1, image: '/images/1.jpg', title: '标题1', description: '描述内容1' }, { id: 2, image: '/images/2.jpg', title: '标题2', description: '描述内容2' } ] onMounted(() => { store.initItems(initialItems) store.startAutoPlay() }) onUnmounted(() => { store.stopAutoPlay() }) const prev = () => { transitionName.value = 'slide-prev' store.prev() } const next = () => { transitionName.value = 'slide-next' store.next() } // 类型定义 interface CarouselItem { id: number image: string title: string description: string } </script> <style lang="scss"> .carousel-container { position: relative; width: 100%; height: 400px; overflow: hidden; } .carousel-item { position: absolute; width: 100%; height: 100%; transition: transform 0.5s ease; img { width: 100%; height: 100%; object-fit: cover; } } .slide-next-enter-active, .slide-next-leave-active { transition: transform 0.5s ease; } .slide-next-enter-from { transform: translateX(100%); } .slide-next-leave-to { transform: translateX(-100%); } .slide-prev-enter-active, .slide-prev-leave-active { transition: transform 0.5s ease; } .slide-prev-enter-from { transform: translateX(-100%); } .slide-prev-leave-to { transform: translateX(100%); } </style> ``` #### 三、功能亮点说明 1. **状态管理**:使用Pinia集中管理轮播图状态(当前索引、数据项、定时器等),支持跨组件复用[^1] 2. **类型安全**:通过TypeScript接口定义数据结构和组件props 3. **自动播放**:基于Pinia actions实现自动播放控制(startAutoPlay/stopAutoPlay) 4. **平滑过渡**:使用Vue transition组件实现滑动动画效果[^3] 5. **响应式设计**:通过@vueuse/core实现自适应布局(示例中省略,可扩展) #### 四、优化建议 1. **图片懒加载**:使用`v-lazy`指令优化图片加载性能 2. **手势支持**:集成`@vueuse/gesture`实现触摸滑动[^1] 3. **响应式断点**:结合Pinia状态实现响应式布局控制 ```typescript // 扩展store const useCarouselStore = defineStore('carousel', { // ... getters: { visibleItems: (state) => { const breakpoints = { mobile: 1, tablet: 2, desktop: 3 } return state.items.slice(0, breakpoints.desktop) } } }) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值