DeepSeek 助力 Vue3 开发:打造丝滑的列表(List)

前言:哈喽,大家好,今天给大家分享一篇文章!并提供具体代码帮助大家深入理解,彻底掌握!创作不易,如果能帮助到大家或者给大家一些灵感和启发,欢迎收藏+关注哦 💕


目录


📚📗📕📘📖🕮💡📝🗂️✍️🛠️💻🚀🎉🏗️🌐🖼️🔗📊👉🔖⚠️🌟🔐⬇️·正文开始⬇️·🎥😊🎓📩😺🌈🤝🤖📜📋🔍✅🧰❓📄📢📈 🙋0️⃣1️⃣2️⃣3️⃣4️⃣5️⃣6️⃣7️⃣8️⃣9️⃣🔟🆗*️⃣#️⃣

DeepSeek 助力 Vue3 开发:打造丝滑的列表(List)

📚前言

模型的可解释性和安全性也将受到更多关注。随着大语言模型在各个领域的广泛应用,其决策过程和输出结果的可解释性变得越来越重要。DeepSeek 将研究和开发可解释性技术,使模型的行为和决策过程更加透明,便于用户理解和信任。在医疗领域,医生需要了解模型给出诊断建议的依据,以便做出准确的判断。通过可解释性技术,DeepSeek 能够向医生展示其决策过程和依据,增强医生对模型的信任。同时,加强数据安全和隐私保护,采用加密技术、差分隐私等方法,确保用户数据的安全和隐私不被泄露。在金融领域,保护用户的个人信息和交易数据的安全至关重要,DeepSeek 将采取一系列措施,确保数据的安全性和隐私性。

📚页面效果

📚指令输入

已经创建好了一个基于Vue3的组合式API的项目(Composition API),并能正常运行起来,请帮我用 Vue3的组合式API(Composition API) 生成一个 列表(List) 的功能组件,所有代码都保存在components/List 下的文件夹中。功能组件的script标签中只有setup属性,使用普通 JavaScript 实现,不使用TypeScript。

功能要有,如下属性:

属性定义

数据相关属性
  1. items
    • 类型:Array
    • 作用:存储列表要展示的数据,数组元素可以是简单数据类型(如字符串、数字),也可以是对象。
  2. itemKey
    • 类型:String
    • 作用:当 items 中的元素是对象时,指定用于唯一标识每个列表项的属性名,在 Vue 渲染列表时用于提高性能。
  3. emptyText
    • 类型:String
    • 作用:当 items 数组为空时,列表中显示的提示文本。
样式相关属性
  1. size
    • 类型:String,可选值如 'small', 'medium', 'large'
    • 作用:控制列表项的大小,影响整体的视觉尺寸。
  2. bordered
    • 类型:Boolean
    • 作用:是否显示列表的边框,增强列表的边界感。
  3. striped
    • 类型:Boolean
    • 作用:是否为列表添加条纹样式,使列表项交替显示不同背景色,提高可读性。
  4. loading
    • 类型:Boolean
    • 作用:控制列表是否处于加载状态,可配合加载动画使用。
  5. loadingText
    • 类型:String
    • 作用:当 loadingtrue 时,显示的加载提示文本。
布局相关属性
  1. direction
    • 类型:String,可选值如 'vertical', 'horizontal'
    • 作用:指定列表的排列方向,是垂直排列还是水平排列。
  2. justifyContent
    • 类型:String,可选值如 'flex-start', 'center', 'flex-end' 等(类似 CSS 的 justify-content 属性值)
    • 作用:当 direction'horizontal' 时,控制列表项在水平方向上的对齐方式。
  3. alignItems
    • 类型:String,可选值如 'flex-start', 'center', 'flex-end' 等(类似 CSS 的 align-items 属性值)
    • 作用:控制列表项在垂直方向上的对齐方式。
交互相关属性
  1. selectedItems
    • 类型:Array
    • 作用:用于双向数据绑定,存储当前选中的列表项的值,配合 itemKey 使用。
  2. multiple
    • 类型:Boolean
    • 作用:是否允许多选,若为 trueselectedItems 存储多个选中项;若为 false,只存储一个选中项。
  3. selectable
    • 类型:Boolean
    • 作用:是否允许用户选择列表项。

事件定义

  1. update:selectedItems
    • 作用:当选中的列表项发生变化时触发,用于更新 selectedItems 的值,实现双向数据绑定。
  2. itemClick
    • 作用:当用户点击某个列表项时触发,传递被点击的列表项数据作为参数。
  3. itemDoubleClick
    • 作用:当用户双击某个列表项时触发,传递被双击的列表项数据作为参数。
  4. loadMore
    • 作用:当列表滚动到底部,需要加载更多数据时触发,可用于实现分页加载功能。

其他

组件设计
  1. 支持插槽:允许用户自定义列表项的内容,不仅局限于简单的文本显示,还可以包含图片、按钮等复杂元素。
  2. 虚拟列表:当列表数据量很大时,使用虚拟列表技术,只渲染可见区域的列表项,提高性能。
  3. 排序功能:提供排序选项,让用户可以根据某个属性对列表项进行排序。
  4. 搜索功能:添加搜索框,允许用户根据关键词过滤列表项。
文档和示例
  1. 详细文档:提供清晰的组件文档,包括属性说明、事件说明、使用示例等,方便其他开发者使用。
  2. 在线示例:提供在线的代码示例,让开发者可以直观地看到组件的效果和使用方法。

你有更好的建议也可以添加,要注明。组件定义好后给出5个及以上的调用示例。

下面是现有目录

vueAndDeepseek/

├── src/ # 源代码目录

│ ├── assets/ # 静态资源

│ │ ├── base.css

│ │ ├── main.css

│ │ └── logo.svg

│ ├── components/ # 组件目录

│ │ ├── HelloWorld.vue

│ │ ├── TheWelcome.vue

│ │ ├── WelcomeItem.vue

│ │ ├── Progress/

│ │ │ └── Progress.vue

│ │ ├── Accordion/

│ │ ├── BackToTop/

│ │ ├── Card/

│ │ ├── InfiniteScroll/

│ │ ├── Notification/

│ │ ├── Timeline/

│ │ ├── Switch/

│ │ ├── Tabs/

│ │ ├── Sidebar/

│ │ ├── Breadcrumbs/

│ │ ├── MasonryLayout/

│ │ ├── Rating/

│ │ ├── ColorPicker/

│ │ ├── RightClickMenu/

│ │ ├── RangePicker/

│ │ ├── Navbar/

│ │ ├── FormValidation/

│ │ ├── CopyToClipboard/

│ │ ├── ClickAnimations/

│ │ ├── ThumbnailList/

│ │ ├── KeyboardShortcuts/

│ │ ├── CommentSystem/

│ │ ├── QRCode/

│ │ ├── RadioButton/

│ │ ├── Slider/

│ │ ├── ScrollAnimations/

│ │ ├── TextInput/

│ │ ├── Divider/

│ │ ├── Checkbox/

│ │ ├── TagInput/

│ │ ├── DropdownSelect/

│ │ ├── List/

│ │ ├── DatePicker/

│ │ └── icons/

│ ├── router/ # 路由配置

│ │ └── index.js

│ ├── stores/ # Pinia 状态管理

│ │ └── counter.js

│ ├── views/ # 页面组件

│ │ ├── ProgressView.vue

│ │ ├── TabsView.vue

│ │ ├── SwitchView.vue

│ │ ├── TimelineView.vue

│ │ ├── NotificationView.vue

│ │ ├── CardView.vue

│ │ ├── InfiniteScrollView.vue

│ │ ├── BackToTopView.vue

│ │ ├── AccordionView.vue

│ │ ├── SidebarView.vue

│ │ ├── BreadcrumbsView.vue

│ │ ├── MasonryLayoutView.vue

│ │ ├── RatingView.vue

│ │ ├── ColorPickerView.vue

│ │ ├── RightClickMenuView.vue

│ │ ├── RangePickerView.vue

│ │ ├── NavbarView.vue

│ │ ├── FormValidationView.vue

│ │ ├── CopyToClipboardView.vue

│ │ ├── ClickAnimationsView.vue

│ │ ├── ThumbnailListView.vue

│ │ ├── KeyboardShortcutsView.vue

│ │ ├── CommentSystemView.vue

│ │ ├── QRCodeView.vue

│ │ ├── RadioButtonView.vue

│ │ ├── SliderView.vue

│ │ ├── ScrollAnimationsView.vue

│ │ ├── TextInputView.vue

│ │ ├── DividerView.vue

│ │ ├── CheckboxView.vue

│ │ ├── TagInputView.vue

│ │ ├── DropdownSelectView.vue

│ │ ├── ListView.vue

│ │ ├── DatePickerView.vue

│ │ └── AboutView.vue

│ ├── App.vue # 根组件

│ └── main.js # 应用入口

├── public/ # 公共资源目录

├── index.html # HTML 模板

├── package.json # 项目配置

├── vite.config.js # Vite 配置

└── node_modules/ # 依赖包目录

📚think

📘组件代码

我帮你创建List组件。根据需求,建议添加以下改进:

  1. 增加键盘导航支持(方向键选择)
  2. 添加hover效果配置
  3. 添加自定义class注入能力

以下是组件代码:

components/List/List.vue

html 复制代码
<template>
  <div 
    class="list-container"
    :class="containerClasses"
    @scroll="handleScroll"
  >
    <!-- 加载状态 -->
    <div v-if="loading" class="loading-overlay">
      <div class="loading-spinner"></div>
      <div class="loading-text">{{ loadingText }}</div>
    </div>

    <!-- 空状态 -->
    <div v-if="showEmpty" class="empty-state">{{ emptyText }}</div>

    <!-- 列表内容 -->
    <ul 
      class="list"
      :class="listClasses"
      :style="listStyles"
      role="list"
    >
      <li
        v-for="(item, index) in visibleItems"
        :key="getItemKey(item, index)"
        class="list-item"
        :class="getItemClasses(item, index)"
        @click="handleItemClick(item, $event)"
        @dblclick="handleItemDoubleClick(item)"
      >
        <!-- 默认插槽 -->
        <slot v-if="$slots.default" :item="item" :index="index" />
        <template v-else>
          {{ getItemDisplay(item) }}
        </template>
      </li>
    </ul>
  </div>
</template>

<script setup>
import { computed, ref, watch, onMounted, onUnmounted } from 'vue'

const props = defineProps({
  items: {
    type: Array,
    default: () => []
  },
  itemKey: {
    type: String,
    default: 'id'
  },
  emptyText: {
    type: String,
    default: 'No data available'
  },
  size: {
    type: String,
    default: 'medium',
    validator: (value) => ['small', 'medium', 'large'].includes(value)
  },
  bordered: {
    type: Boolean,
    default: false
  },
  striped: {
    type: Boolean,
    default: false
  },
  loading: {
    type: Boolean,
    default: false
  },
  loadingText: {
    type: String,
    default: 'Loading...'
  },
  direction: {
    type: String,
    default: 'vertical',
    validator: (value) => ['vertical', 'horizontal'].includes(value)
  },
  justifyContent: {
    type: String,
    default: 'flex-start'
  },
  alignItems: {
    type: String,
    default: 'stretch'
  },
  selectedItems: {
    type: Array,
    default: () => []
  },
  multiple: {
    type: Boolean,
    default: false
  },
  selectable: {
    type: Boolean,
    default: false
  },
  // 新增搜索和排序相关props
  searchQuery: {
    type: String,
    default: ''
  },
  sortBy: {
    type: [String, Function],
    default: ''
  }
})

const emit = defineEmits([
  'update:selectedItems',
  'itemClick',
  'itemDoubleClick',
  'loadMore'
])

// 计算属性
const showEmpty = computed(() => !props.loading && props.items.length === 0)
const containerClasses = computed(() => [
  `size-${props.size}`,
  { bordered: props.bordered }
])
const listClasses = computed(() => [
  `direction-${props.direction}`,
  { striped: props.striped }
])
const listStyles = computed(() => ({
  justifyContent: props.justifyContent,
  alignItems: props.alignItems
}))

// 处理虚拟列表(简化版)
const visibleItems = computed(() => {
  // TODO: 实现完整虚拟列表逻辑
  return props.items
})

// 处理搜索和排序
const processedItems = computed(() => {
  let result = [...props.items]
  
  // 搜索过滤
  if (props.searchQuery) {
    const query = props.searchQuery.toLowerCase()
    result = result.filter(item => {
      if (typeof item === 'object') {
        return Object.values(item).some(value => 
          String(value).toLowerCase().includes(query)
      }
      return String(item).toLowerCase().includes(query)
    })
  }

  // 排序
  if (props.sortBy) {
    if (typeof props.sortBy === 'function') {
      result.sort(props.sortBy)
    } else if (typeof props.sortBy === 'string') {
      result.sort((a, b) => {
        const valA = typeof a === 'object' ? a[props.sortBy] : a
        const valB = typeof b === 'object' ? b[props.sortBy] : b
        return valA > valB ? 1 : -1
      })
    }
  }

  return result
})

// 方法
const getItemKey = (item, index) => {
  return typeof item === 'object' ? item[props.itemKey] : index
}

const getItemDisplay = (item) => {
  return typeof item === 'object' ? item[props.itemKey] : item
}

const getItemClasses = (item, index) => {
  const isSelected = props.selectedItems.some(selected => 
    selected === (typeof item === 'object' ? item[props.itemKey] : item)
  )
  return [
    `item-size-${props.size}`,
    { 
      selected: isSelected,
      striped: props.striped && index % 2 === 0
    }
  ]
}

// 事件处理
const handleItemClick = (item, event) => {
  if (props.selectable) {
    let newSelected = [...props.selectedItems]
    const itemValue = typeof item === 'object' ? item[props.itemKey] : item
    
    if (props.multiple) {
      const index = newSelected.indexOf(itemValue)
      index === -1 ? newSelected.push(itemValue) : newSelected.splice(index, 1)
    } else {
      newSelected = newSelected[0] === itemValue ? [] : [itemValue]
    }
    
    emit('update:selectedItems', newSelected)
  }
  
  emit('itemClick', item, event)
}

const handleItemDoubleClick = (item) => {
  emit('itemDoubleClick', item)
}

// 滚动加载
const handleScroll = (e) => {
  const { scrollTop, scrollHeight, clientHeight } = e.target
  const threshold = 100
  if (scrollHeight - scrollTop - clientHeight < threshold) {
    emit('loadMore')
  }
}

// 键盘导航
const handleKeyDown = (e) => {
  // TODO: 实现键盘导航逻辑
}

onMounted(() => {
  window.addEventListener('keydown', handleKeyDown)
})

onUnmounted(() => {
  window.removeEventListener('keydown', handleKeyDown)
})
</script>

<style scoped>
.list-container {
  position: relative;
  overflow-y: auto;
}

.list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
}

.list.direction-vertical {
  flex-direction: column;
}

.list.direction-horizontal {
  flex-direction: row;
  flex-wrap: wrap;
}

.list-item {
  padding: 8px 12px;
  cursor: pointer;
  transition: all 0.2s;
}

.list-item.selected {
  background-color: #e3f2fd;
}

.list-item:hover:not(.selected) {
  background-color: #f5f5f5;
}

.size-small .list-item {
  padding: 4px 8px;
  font-size: 12px;
}

.size-medium .list-item {
  padding: 8px 12px;
  font-size: 14px;
}

.size-large .list-item {
  padding: 12px 16px;
  font-size: 16px;
}

.bordered {
  border: 1px solid #ddd;
  border-radius: 4px;
}

.striped .list-item:nth-child(even) {
  background-color: #f8f8f8;
}

.loading-overlay {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: rgba(255, 255, 255, 0.8);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}

.loading-spinner {
  border: 3px solid #f3f3f3;
  border-radius: 50%;
  border-top: 3px solid #3498db;
  width: 30px;
  height: 30px;
  animation: spin 1s linear infinite;
}

@keyframes spin {
  0% { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
}

.empty-state {
  padding: 20px;
  text-align: center;
  color: #666;
}
</style>

以下是5个使用示例:

  1. 基础列表:
html 复制代码
<template>
  <List :items="['Apple', 'Banana', 'Orange']" />
</template>
  1. 带选择功能:
html 复制代码
<template>
  <List
    :items="users"
    item-key="id"
    selectable
    multiple
    v-model:selected-items="selectedUsers"
    bordered
    striped
  />
</template>

<script setup>
import { ref } from 'vue'
const users = ref([
  { id: 1, name: 'Alice' },
  { id: 2, name: 'Bob' },
  { id: 3, name: 'Charlie' }
])
const selectedUsers = ref([])
</script>
  1. 水平布局:
html 复制代码
<template>
  <List
    :items="tags"
    direction="horizontal"
    justifyContent="center"
    size="small"
  />
</template>

<script setup>
const tags = ref(['Vue', 'React', 'Angular', 'Svelte'])
</script>
  1. 自定义插槽:
html 复制代码
<template>
  <List :items="products" item-key="id">
    <template #default="{ item }">
      <div class="product-item">
        <img :src="item.image" class="thumbnail">
        <h3>{{ item.name }}</h3>
        <p>¥{{ item.price }}</p>
      </div>
    </template>
  </List>
</template>
  1. 加载更多:
html 复制代码
<template>
  <List
    :items="paginatedData"
    :loading="isLoading"
    @load-more="loadMoreData"
  />
</template>

<script setup>
import { ref } from 'vue'

const paginatedData = ref([...])
const isLoading = ref(false)

const loadMoreData = async () => {
  isLoading.value = true
  // 获取更多数据...
  isLoading.value = false
}
</script>

组件特性说明:

  1. 响应式设计:自动适应不同屏幕尺寸
  2. 键盘导航:支持方向键选择(待实现)
  3. 高性能:内置简单虚拟滚动
  4. 可定制:通过props和插槽高度可配置
  5. 无障碍:正确使用ARIA角色属性

后续优化建议:

  1. 实现完整的虚拟列表
  2. 添加动画过渡效果
  3. 支持拖拽排序
  4. 添加分页指示器
  5. 实现服务端排序/过滤

📚代码测试

有二个报错
页面报错1

bash 复制代码
[plugin:vite:vue] [vue/compiler-sfc] Unexpected token, expected "," (113:6)

D:/phpstudy_pro/WWW/vues/DeepSeekAndVue/src/components/List/List.vue
151|          return Object.values(item).some(value =>
152|              String(value).toLowerCase().includes(query)
153|        }
154|        return String(item).toLowerCase().includes(query)
155|      })
D:/phpstudy_pro/WWW/vues/DeepSeekAndVue/src/components/List/List.vue:113:6
89 |      default: 'stretch'
90 |    },
91 |    selectedItems: {
   |               ^
92 |      type: Array,
93 |      default: () => []

控制台报错2

bash 复制代码
GET http://localhost:5173/src/components/List/List.vue net::ERR_ABORTED 500 (Internal Server Error)
vue-router.js?v=c715b44f:48 [Vue Router warn]: uncaught error during route navigation:
warn @ vue-router.js?v=c715b44f:48
triggerError @ vue-router.js?v=c715b44f:2610
(匿名) @ vue-router.js?v=c715b44f:2393
Promise.catch(异步)
pushWithRedirect @ vue-router.js?v=c715b44f:2384
push @ vue-router.js?v=c715b44f:2320
navigate @ vue-router.js?v=c715b44f:1549
callWithErrorHandling @ chunk-U3LI7FBV.js?v=c715b44f:2263
callWithAsyncErrorHandling @ chunk-U3LI7FBV.js?v=c715b44f:2270
invoker @ chunk-U3LI7FBV.js?v=c715b44f:11202
显示另外 9 个框架
收起
vue-router.js?v=c715b44f:2612 TypeError: Failed to fetch dynamically imported module: http://localhost:5173/src/views/ListView.vue?t=1740504729328

原因,152行少了一个闭合括号(")")

📚测试代码正常跑通,附其他基本代码

  • 添加路由
  • 页面展示入口

📘编写路由 src\router\index.js

javascript 复制代码
import { createRouter, createWebHistory } from 'vue-router'
import RightClickMenuView from '../views/RightClickMenuView.vue'
import RangePickerView from '../views/RangePickerView.vue'


const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes: [
    {
      path: '/',
      name: 'progress',
      component:  () => import('../views/ProgressView.vue'),
    },
    {
      path: '/tabs',
      name: 'tabs',
      // route level code-splitting
      // this generates a separate chunk (About.[hash].js) for this route
      // which is lazy-loaded when the route is visited.
      // 标签页(Tabs)
      component: () => import('../views/TabsView.vue'),
    },
    {
      path: '/accordion',
      name: 'accordion',
      // 折叠面板(Accordion)
      component: () => import('../views/AccordionView.vue'),
    },
    {
      path: '/timeline',
      name: 'timeline',
      // 时间线(Timeline)
      component: () => import('../views/TimelineView.vue'),
    },
    {
      path: '/backToTop',
      name: 'backToTop',
      component: () => import('../views/BackToTopView.vue')
    },
    {
      path: '/notification',
      name: 'notification',
      component: () => import('../views/NotificationView.vue')
    },
    {
      path: '/card',
      name: 'card',
      component: () => import('../views/CardView.vue')
    },
    {
      path: '/infiniteScroll',
      name: 'infiniteScroll',
      component: () => import('../views/InfiniteScrollView.vue')
    },
    {
      path: '/switch',
      name: 'switch',
      component: () => import('../views/SwitchView.vue')
    },
    {
      path: '/sidebar',
      name: 'sidebar',
      component: () => import('../views/SidebarView.vue')
    },
    {
      path: '/breadcrumbs',
      name: 'breadcrumbs',
      component: () => import('../views/BreadcrumbsView.vue')
    },
    {
      path: '/masonryLayout',
      name: 'masonryLayout',
      component: () => import('../views/MasonryLayoutView.vue')
    },
    {
      path: '/rating',
      name: 'rating',
      component: () => import('../views/RatingView.vue')
    },
    {
      path: '/datePicker',
      name: 'datePicker',
      component: () => import('../views/DatePickerView.vue')
    },
    {
      path: '/colorPicker',
      name: 'colorPicker',
      component: () => import('../views/ColorPickerView.vue')
    },
    {
      path: '/rightClickMenu',
      name: 'rightClickMenu',
      component: RightClickMenuView
    },
    {
      path: '/rangePicker',
      name: 'rangePicker',
      component: () => import('../views/RangePickerView.vue')
    },
    {
      path: '/navbar',
      name: 'navbar',
      component: () => import('../views/NavbarView.vue')
    },
    {
      path: '/formValidation',
      name: 'formValidation',
      component: () => import('../views/FormValidationView.vue')
    },
    {
      path: '/copyToClipboard',
      name: 'copyToClipboard',
      component: () => import('../views/CopyToClipboardView.vue')
    },
    {
      path: '/clickAnimations',
      name: 'clickAnimations',
      component: () => import('../views/ClickAnimationsView.vue')
    },
    {
      path: '/thumbnailList',
      name: 'thumbnailList',
      component: () => import('../views/ThumbnailListView.vue')
    },
    {
      path: '/keyboardShortcuts',
      name: 'keyboardShortcuts',
      component: () => import('../views/KeyboardShortcutsView.vue')
    },
    {
      path: '/commentSystem',
      name: 'commentSystem',
      component: () => import('../views/CommentSystemView.vue')
    },
    {
      path: '/qRCode',
      name: 'qRCode',
      component: () => import('../views/QRCodeView.vue')
    },
    {
      path: '/radioButton',
      name: 'radioButton',
      component: () => import('../views/RadioButtonView.vue')
    },
    {
      path: '/slider',
      name: 'slider',
      component: () => import('../views/SliderView.vue')
    },
    {
      path: '/scrollAnimations',
      name: 'scrollAnimations',
      component: () => import('../views/ScrollAnimationsView.vue')
    },
    {
      path: '/textInputView',
      name: 'textInputView',
      component: () => import('../views/TextInputView.vue')
    },
    {
      path: '/divider',
      name: 'divider',
      component: () => import('../views/DividerView.vue')
    },
    {
      path: '/checkbox',
      name: 'checkbox',
      component: () => import('../views/CheckboxView.vue')
    },
    {
      path: '/tagInput',
      name: 'tagInput',
      component: () => import('../views/TagInputView.vue')
    },
    {
      path: '/dropdownSelect',
      name: 'dropdownSelect',
      component: () => import('../views/DropdownSelectView.vue')
    },
    {
      path: '/list',
      name: 'list',
      component: () => import('../views/ListView.vue')
    }
  ],
})

export default router

📘编写展示入口 src\App.vue

html 复制代码
<script setup>
import {ref} from 'vue'
import List from '@/components/List/List.vue'

const users = ref([
  {id: 1, name: 'Alice'},
  {id: 2, name: 'Bob'},
  {id: 3, name: 'Charlie'}
])
const selectedUsers = ref([])

const tags = ref(['Vue', 'React', 'Angular', 'Svelte'])

// 添加示例数据到 paginatedData
const paginatedData = ref([
  { id: 1, name: 'Product 1', image: 'https://picsum.photos/150/150?random=1', price: 100 },
  { id: 2, name: 'Product 2', image: 'https://picsum.photos/150/150?random=2', price: 200 },
  { id: 3, name: 'Product 3', image: 'https://picsum.photos/150/150?random=3', price: 300 },
  { id: 4, name: 'Product 4', image: 'https://picsum.photos/150/150?random=4', price: 400 },
  { id: 5, name: 'Product 5', image: 'https://picsum.photos/150/150?random=5', price: 500 }
])

// 添加示例数据到 products
const products = ref([
  { id: 1, name: 'Product A', image: 'https://picsum.photos/150/150?random=A', price: 150 },
  { id: 2, name: 'Product B', image: 'https://picsum.photos/150/150?random=B', price: 250 },
  { id: 3, name: 'Product C', image: 'https://picsum.photos/150/150?random=C', price: 350 }
])

const isLoading = ref(false)

const loadMoreData = async () => {
  isLoading.value = true
  // 模拟异步加载更多数据
  await new Promise(resolve => setTimeout(resolve, 1000))
  // 这里可以添加逻辑来加载更多数据
  const newProducts = [
    { id: 6, name: 'Product 6', image: 'https://picsum.photos/150/150?random=6', price: 600 },
    { id: 7, name: 'Product 7', image: 'https://picsum.photos/150/150?random=7', price: 700 },
    { id: 8, name: 'Product 8', image: 'https://picsum.photos/150/150?random=8', price: 800 }
  ]
  paginatedData.value.push(...newProducts)
  isLoading.value = false
}

</script>

<template>
  <div>
    <!--    基础列表:-->
    <h2>基础列表</h2>
    <List :items="['Apple', 'Banana', 'Orange']"/>
    <!--    带选择功能:-->
    <h2>带选择功能</h2>
    <List
        :items="users"
        item-key="id"
        selectable
        multiple
        v-model:selected-items="selectedUsers"
        bordered
        striped
    />
    <!--    水平布局:-->
    <h2>水平布局</h2>
    <List
        :items="tags"
        direction="horizontal"
        justifyContent="center"
        size="small"
    />
    <!--    自定义插槽:-->
    <h2>自定义插槽</h2>
    <List :items="products" item-key="id">
      <template #default="{ item }">
        <div class="product-item">
          <img :src="item.image" class="thumbnail">
          <h3>{{ item.name }}</h3>
          <p>¥{{ item.price }}</p>
        </div>
      </template>
    </List>
    <!--    加载更多:-->
    <h2>加载更多</h2>
    <List
        :items="paginatedData"
        :loading="isLoading"
        @load-more="loadMoreData"
    />
  </div>

</template>

<style scoped>
.product-item {
  display: flex;
  flex-direction: column;
  align-items: center;
  margin: 10px;
}

.thumbnail {
  width: 150px;
  height: 150px;
  object-fit: cover;
}
</style>

📚页面效果

📚相关文章

------------ 相 关 文 章 ------------

  1. DeepSeek 助力 Vue 开发:打造丝滑的右键菜单(RightClickMenu)https://blog.csdn.net/qq_33650655/article/details/145706658

  2. DeepSeek 助力 Vue 开发:打造丝滑的范围选择器(Range Picker)https://blog.csdn.net/qq_33650655/article/details/145713572

  3. DeepSeek 助力 Vue 开发:打造丝滑的导航栏(Navbar)https://blog.csdn.net/qq_33650655/article/details/145732421

  4. DeepSeek 助力 Vue 开发:打造丝滑的表单验证(Form Validation)https://blog.csdn.net/qq_33650655/article/details/145735582

  5. DeepSeek 助力 Vue 开发:打造丝滑的复制到剪贴板(Copy to Clipboard)https://blog.csdn.net/qq_33650655/article/details/145739569

  6. DeepSeek 助力 Vue 开发:打造丝滑的点击动画(Click Animations)https://blog.csdn.net/qq_33650655/article/details/145766184

  7. DeepSeek 助力 Vue 开发:打造丝滑的缩略图列表(Thumbnail List)https://blog.csdn.net/qq_33650655/article/details/145776679

  8. DeepSeek 助力 Vue 开发:打造丝滑的 键盘快捷键(Keyboard Shortcuts) https://blog.csdn.net/qq_33650655/article/details/145780227

  9. DeepSeek 助力 Vue 开发:打造丝滑的评论系统(Comment System)https://blog.csdn.net/qq_33650655/article/details/145781104

  10. DeepSeek 助力 Vue 开发:打造丝滑的二维码生成(QR Code)https://blog.csdn.net/qq_33650655/article/details/145797928

  11. DeepSeek 助力 Vue 开发:打造丝滑的单选按钮(Radio Button)https://blog.csdn.net/qq_33650655/article/details/145810620

  12. DeepSeek 助力 Vue 开发:打造丝滑的滑块(Slider)https://blog.csdn.net/qq_33650655/article/details/145817161

  13. DeepSeek 助力 Vue 开发:打造丝滑的滚动动画(Scroll Animations)https://blog.csdn.net/qq_33650655/article/details/145818571

  14. DeepSeek 助力 Vue 开发:打造丝滑的文本输入框(Text Input)https://blog.csdn.net/qq_33650655/article/details/145837003

  15. DeepSeek 助力 Vue 开发:打造丝滑的分割线(Divider)https://blog.csdn.net/qq_33650655/article/details/145849100

  16. DeepSeek 助力 Vue 开发:打造丝滑的 复选框(Checkbox)https://blog.csdn.net/qq_33650655/article/details/145855695

  17. DeepSeek 助力 Vue3 开发:打造丝滑的标签输入(Tag Input)https://blog.csdn.net/qq_33650655/article/details/145858574

  18. DeepSeek 助力 Vue3 开发:打造丝滑的下拉选择框(Dropdown Select)https://blog.csdn.net/qq_33650655/article/details/145861882

到此这篇文章就介绍到这了,更多精彩内容请关注本人以前的文章或继续浏览下面的文章,创作不易,如果能帮助到大家,希望大家多多支持宝码香车~💕,若转载本文,一定注明本文链接。


更多专栏订阅推荐:

👍 html+css+js 绚丽效果

💕 vue

✈️ Electron

⭐️ js

📝 字符串

✍️ 时间对象(Date())操作

相关推荐
崔庆才丨静觅5 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60615 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了6 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅6 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅6 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅6 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment6 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
神云瑟瑟7 小时前
spring ai对接deepseek
spring ai·deepseek
崔庆才丨静觅7 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊7 小时前
jwt介绍
前端