DeepSeek 助力 Vue3 开发:打造丝滑的列表(List)
前言:哈喽,大家好,今天给大家分享一篇文章!并提供具体代码帮助大家深入理解,彻底掌握!创作不易,如果能帮助到大家或者给大家一些灵感和启发,欢迎收藏+关注哦 💕
目录
- DeepSeek 助力 Vue3 开发:打造丝滑的列表(List)
- 📚前言
- 📚页面效果
- 📚指令输入
- 属性定义
- 数据相关属性
- 样式相关属性
- 布局相关属性
- 交互相关属性
- 事件定义
- 其他
- 组件设计
- 文档和示例
- 📚think
- 📘组件代码
- 📚代码测试
- 📚测试代码正常跑通,附其他基本代码
- 📘编写路由 src\router\index.js
- 📘编写展示入口 src\App.vue
- 📚页面效果
- 📚相关文章
📚📗📕📘📖🕮💡📝🗂️✍️🛠️💻🚀🎉🏗️🌐🖼️🔗📊👉🔖⚠️🌟🔐⬇️·正文开始
⬇️·🎥😊🎓📩😺🌈🤝🤖📜📋🔍✅🧰❓📄📢📈 🙋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。
功能要有,如下属性:
属性定义
数据相关属性
items
- 类型:
Array
- 作用:存储列表要展示的数据,数组元素可以是简单数据类型(如字符串、数字),也可以是对象。
- 类型:
itemKey
- 类型:
String
- 作用:当
items
中的元素是对象时,指定用于唯一标识每个列表项的属性名,在 Vue 渲染列表时用于提高性能。
- 类型:
emptyText
- 类型:
String
- 作用:当
items
数组为空时,列表中显示的提示文本。
- 类型:
样式相关属性
size
- 类型:
String
,可选值如'small'
,'medium'
,'large'
- 作用:控制列表项的大小,影响整体的视觉尺寸。
- 类型:
bordered
- 类型:
Boolean
- 作用:是否显示列表的边框,增强列表的边界感。
- 类型:
striped
- 类型:
Boolean
- 作用:是否为列表添加条纹样式,使列表项交替显示不同背景色,提高可读性。
- 类型:
loading
- 类型:
Boolean
- 作用:控制列表是否处于加载状态,可配合加载动画使用。
- 类型:
loadingText
- 类型:
String
- 作用:当
loading
为true
时,显示的加载提示文本。
- 类型:
布局相关属性
direction
- 类型:
String
,可选值如'vertical'
,'horizontal'
- 作用:指定列表的排列方向,是垂直排列还是水平排列。
- 类型:
justifyContent
- 类型:
String
,可选值如'flex-start'
,'center'
,'flex-end'
等(类似 CSS 的justify-content
属性值) - 作用:当
direction
为'horizontal'
时,控制列表项在水平方向上的对齐方式。
- 类型:
alignItems
- 类型:
String
,可选值如'flex-start'
,'center'
,'flex-end'
等(类似 CSS 的align-items
属性值) - 作用:控制列表项在垂直方向上的对齐方式。
- 类型:
交互相关属性
selectedItems
- 类型:
Array
- 作用:用于双向数据绑定,存储当前选中的列表项的值,配合
itemKey
使用。
- 类型:
multiple
- 类型:
Boolean
- 作用:是否允许多选,若为
true
,selectedItems
存储多个选中项;若为false
,只存储一个选中项。
- 类型:
selectable
- 类型:
Boolean
- 作用:是否允许用户选择列表项。
- 类型:
事件定义
update:selectedItems
- 作用:当选中的列表项发生变化时触发,用于更新
selectedItems
的值,实现双向数据绑定。
- 作用:当选中的列表项发生变化时触发,用于更新
itemClick
- 作用:当用户点击某个列表项时触发,传递被点击的列表项数据作为参数。
itemDoubleClick
- 作用:当用户双击某个列表项时触发,传递被双击的列表项数据作为参数。
loadMore
- 作用:当列表滚动到底部,需要加载更多数据时触发,可用于实现分页加载功能。
其他
组件设计
- 支持插槽:允许用户自定义列表项的内容,不仅局限于简单的文本显示,还可以包含图片、按钮等复杂元素。
- 虚拟列表:当列表数据量很大时,使用虚拟列表技术,只渲染可见区域的列表项,提高性能。
- 排序功能:提供排序选项,让用户可以根据某个属性对列表项进行排序。
- 搜索功能:添加搜索框,允许用户根据关键词过滤列表项。
文档和示例
- 详细文档:提供清晰的组件文档,包括属性说明、事件说明、使用示例等,方便其他开发者使用。
- 在线示例:提供在线的代码示例,让开发者可以直观地看到组件的效果和使用方法。
你有更好的建议也可以添加,要注明。组件定义好后给出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组件。根据需求,建议添加以下改进:
- 增加键盘导航支持(方向键选择)
- 添加hover效果配置
- 添加自定义class注入能力
以下是组件代码:
components/List/List.vue
<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个使用示例:
- 基础列表:
<template>
<List :items="['Apple', 'Banana', 'Orange']" />
</template>
- 带选择功能:
<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>
- 水平布局:
<template>
<List
:items="tags"
direction="horizontal"
justifyContent="center"
size="small"
/>
</template>
<script setup>
const tags = ref(['Vue', 'React', 'Angular', 'Svelte'])
</script>
- 自定义插槽:
<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>
- 加载更多:
<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>
组件特性说明:
- 响应式设计:自动适应不同屏幕尺寸
- 键盘导航:支持方向键选择(待实现)
- 高性能:内置简单虚拟滚动
- 可定制:通过props和插槽高度可配置
- 无障碍:正确使用ARIA角色属性
后续优化建议:
- 实现完整的虚拟列表
- 添加动画过渡效果
- 支持拖拽排序
- 添加分页指示器
- 实现服务端排序/过滤
📚代码测试
有二个报错
页面报错1
[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
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
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
<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>
📚页面效果
📚相关文章
-
DeepSeek 助力 Vue 开发:打造丝滑的右键菜单(RightClickMenu)https://blog.csdn.net/qq_33650655/article/details/145706658
-
DeepSeek 助力 Vue 开发:打造丝滑的范围选择器(Range Picker)https://blog.csdn.net/qq_33650655/article/details/145713572
-
DeepSeek 助力 Vue 开发:打造丝滑的导航栏(Navbar)https://blog.csdn.net/qq_33650655/article/details/145732421
-
DeepSeek 助力 Vue 开发:打造丝滑的表单验证(Form Validation)https://blog.csdn.net/qq_33650655/article/details/145735582
-
DeepSeek 助力 Vue 开发:打造丝滑的复制到剪贴板(Copy to Clipboard)https://blog.csdn.net/qq_33650655/article/details/145739569
-
DeepSeek 助力 Vue 开发:打造丝滑的点击动画(Click Animations)https://blog.csdn.net/qq_33650655/article/details/145766184
-
DeepSeek 助力 Vue 开发:打造丝滑的缩略图列表(Thumbnail List)https://blog.csdn.net/qq_33650655/article/details/145776679
-
DeepSeek 助力 Vue 开发:打造丝滑的 键盘快捷键(Keyboard Shortcuts) https://blog.csdn.net/qq_33650655/article/details/145780227
-
DeepSeek 助力 Vue 开发:打造丝滑的评论系统(Comment System)https://blog.csdn.net/qq_33650655/article/details/145781104
-
DeepSeek 助力 Vue 开发:打造丝滑的二维码生成(QR Code)https://blog.csdn.net/qq_33650655/article/details/145797928
-
DeepSeek 助力 Vue 开发:打造丝滑的单选按钮(Radio Button)https://blog.csdn.net/qq_33650655/article/details/145810620
-
DeepSeek 助力 Vue 开发:打造丝滑的滑块(Slider)https://blog.csdn.net/qq_33650655/article/details/145817161
-
DeepSeek 助力 Vue 开发:打造丝滑的滚动动画(Scroll Animations)https://blog.csdn.net/qq_33650655/article/details/145818571
-
DeepSeek 助力 Vue 开发:打造丝滑的文本输入框(Text Input)https://blog.csdn.net/qq_33650655/article/details/145837003
-
DeepSeek 助力 Vue 开发:打造丝滑的分割线(Divider)https://blog.csdn.net/qq_33650655/article/details/145849100
-
DeepSeek 助力 Vue 开发:打造丝滑的 复选框(Checkbox)https://blog.csdn.net/qq_33650655/article/details/145855695
-
DeepSeek 助力 Vue3 开发:打造丝滑的标签输入(Tag Input)https://blog.csdn.net/qq_33650655/article/details/145858574
-
DeepSeek 助力 Vue3 开发:打造丝滑的下拉选择框(Dropdown Select)https://blog.csdn.net/qq_33650655/article/details/145861882
到此这篇文章就介绍到这了,更多精彩内容请关注本人以前的文章或继续浏览下面的文章,创作不易,如果能帮助到大家,希望大家多多支持宝码香车~💕,若转载本文,一定注明本文链接。
更多专栏订阅推荐:
👍 html+css+js 绚丽效果
💕 vue
✈️ Electron
⭐️ js
📝 字符串
✍️ 时间对象(Date())操作