php网站开发河南火焰山网站开发禹

张小明 2026/1/1 19:11:43
php网站开发,河南火焰山网站开发禹,重庆网站推广系统,软文内容文章目录一、Vue3 TypeScript 版本代码#xff08;完整优化版#xff09;二、后端接口对接逻辑#xff08;Axios 实战版#xff09;1. 安装依赖2. 创建接口请求工具#xff08;api/product.ts#xff09;3. 组件中对接接口#xff08;替换模拟数据#xff09;4. 后端接…文章目录一、Vue3 TypeScript 版本代码完整优化版二、后端接口对接逻辑Axios 实战版1. 安装依赖2. 创建接口请求工具api/product.ts3. 组件中对接接口替换模拟数据4. 后端接口设计参考RESTful 风格一、Vue3 TypeScript 版本代码完整优化版template div classproduct-list-container h2 classpage-title眼镜商品列表/h2 !-- 筛选区域 -- div classfilter-container !-- 搜索框 -- el-input v-modelfilters.keyword placeholder搜索商品名称... prefix-iconSearch classsearch-input inputhandleFilterChange(keyword, $event) / !-- 分类筛选 -- el-select v-modelfilters.category placeholder选择分类 classcategory-select changehandleFilterChange(category, $event) el-option label全部分类 valueall / el-option v-forcategory in categoryOptions :keycategory.value :labelcategory.label :valuecategory.value / /el-select !-- 品牌筛选带全选 -- div classbrand-filter div classfilter-label选择品牌/div el-checkbox v-modelisBrandAllChecked :indeterminateisBrandIndeterminate changehandleBrandAllChange classall-checkbox 全选 /el-checkbox div classbrand-checkbox-group el-checkbox v-forbrand in brandOptions :keybrand.value :labelbrand.value v-modelfilters.brand changehandleFilterChange(brand, filters.brand) {{ brand.label }} /el-checkbox /div /div !-- 价格筛选滑块 -- div classprice-filter div classfilter-label 价格范围¥{{ filters.priceRange[0] }} - ¥{{ filters.priceRange[1] }} /div el-slider v-modelfilters.priceRange :min0 :maxmaxPrice :step10 range changehandleFilterChange(priceRange, $event) / /div !-- 重置按钮 -- el-button typeprimary iconFilter clickresetFilters classreset-btn 重置筛选 /el-button /div !-- 排序区域 -- div classsort-container el-select v-modelsortType placeholder选择排序方式 classsort-select changehandleSortChange el-option label默认排序 valuedefault / el-option label价格从低到高 valuepriceAsc / el-option label价格从高到低 valuepriceDesc / el-option label销量从高到低 valuesalesDesc / /el-select /div !-- 商品列表区域 -- el-skeleton v-ifloading :countpageSize classskeleton-container item-classskeleton-item / el-empty v-else-ifsortedProducts.length 0 description暂无符合条件的商品 classempty-container / div classproduct-grid v-else el-card v-forproduct in paginatedProducts :keyproduct.id hover classproduct-card clickhandleProductClick(product) img :srcproduct.image :altproduct.name classproduct-image / div classproduct-info h3 classproduct-name{{ product.name }}/h3 div classproduct-price¥{{ product.price }}/div div classproduct-meta span classproduct-brand品牌{{ product.brand }}/span span classproduct-sales销量{{ product.sales }}/span /div /div /el-card /div !-- 分页区域 -- el-pagination v-ifsortedProducts.length 0 v-model:current-pagecurrentPage v-model:page-sizepageSize :totalsortedProducts.length :page-sizes[6, 12, 24] layouttotal, sizes, prev, pager, next, jumper size-changehandlePageSizeChange current-changehandleCurrentPageChange classpagination-container / /div /template script setup langts import { ref, computed, watch, onMounted } from vue; import { ElInput, ElSelect, ElOption, ElCheckbox, ElSlider, ElButton, ElCard, ElSkeleton, ElEmpty, ElPagination } from element-plus; // 类型定义 interface Product { id: number; name: string; price: number; category: string; brand: string; sales: number; image: string; } interface FilterParams { keyword: string; category: string; brand: string[]; priceRange: [number, number]; } interface ProductListProps { pageSize?: number; maxPrice?: number; placeholder?: string; onProductClick?: (product: Product) void; } // Props定义支持外部配置 const props withDefaults(definePropsProductListProps(), { pageSize: 12, maxPrice: 10000, placeholder: 搜索商品名称..., }); // 状态管理 const loading refboolean(true); const products refProduct[]([]); const filters refFilterParams({ keyword: , category: all, brand: [], priceRange: [0, props.maxPrice], }); const sortType refdefault | priceAsc | priceDesc | salesDesc(default); const currentPage refnumber(1); const pageSize refnumber(props.pageSize); // 衍生状态 const categoryOptions computed(() { return Array.from(new Set(products.value.map(p p.category))).map(cat ({ label: cat, value: cat, })); }); const brandOptions computed(() { return Array.from(new Set(products.value.map(p p.brand))).map(brand ({ label: brand, value: brand, })); }); // 品牌全选相关计算属性 const isBrandAllChecked computed({ get: () filters.value.brand.length brandOptions.value.length brandOptions.value.length 0, set: (val) handleBrandAllChange(val), }); const isBrandIndeterminate computed(() { return filters.value.brand.length 0 filters.value.brand.length brandOptions.value.length; }); // 筛选后的数据 const filteredProducts computed(() { return products.value.filter(product { const matchKeyword product.name.toLowerCase().includes(filters.value.keyword.toLowerCase()); const matchCategory filters.value.category all ? true : product.category filters.value.category; const matchBrand filters.value.brand.length 0 ? true : filters.value.brand.includes(product.brand); const matchPrice product.price filters.value.priceRange[0] product.price filters.value.priceRange[1]; return matchKeyword matchCategory matchBrand matchPrice; }); }); // 排序后的数据 const sortedProducts computed(() { const copy [...filteredProducts.value]; switch (sortType.value) { case priceAsc: return copy.sort((a, b) a.price - b.price); case priceDesc: return copy.sort((a, b) b.price - a.price); case salesDesc: return copy.sort((a, b) b.sales - a.sales); default: return copy.sort((a, b) a.id - b.id); } }); // 分页后的数据 const paginatedProducts computed(() { const start (currentPage.value - 1) * pageSize.value; const end start pageSize.value; return sortedProducts.value.slice(start, end); }); // 模拟接口请求商品数据 const fetchProducts async () { loading.value true; try { // 模拟延迟 await new Promise(resolve setTimeout(resolve, 800)); // 模拟商品数据 const mockData: Product[] Array.from({ length: 60 }, (_, i) ({ id: i 1, name: 智能防蓝光眼镜 ${i 1}代 - ${[商务款, 休闲款, 运动款][i % 3]}, price: 199 Math.floor(Math.random() * 800), category: [防蓝光, 近视镜, 太阳镜, 老花镜][i % 4], brand: [暴龙, 陌森, 雷朋, 帕森, 海伦凯勒][i % 5], sales: Math.floor(Math.random() * 1000), image: https://picsum.photos/200/200?random${i}, })); products.value mockData; } catch (error) { console.error(获取商品数据失败, error); } finally { loading.value false; } }; // 生命周期钩子 onMounted(() { fetchProducts(); }); // 筛选条件变更处理 const handleFilterChange (key: keyof FilterParams, value: any) { filters.value { ...filters.value, [key]: value }; currentPage.value 1; // 筛选后重置到第一页 }; // 品牌全选/取消全选 const handleBrandAllChange (val: boolean) { filters.value.brand val ? brandOptions.value.map(b b.value) : []; }; // 重置筛选条件 const resetFilters () { filters.value { keyword: , category: all, brand: [], priceRange: [0, props.maxPrice], }; sortType.value default; currentPage.value 1; }; // 排序变更处理 const handleSortChange (val: string) { sortType.value val as any; currentPage.value 1; // 排序后重置到第一页 }; // 分页相关处理 const handlePageSizeChange (val: number) { pageSize.value val; currentPage.value 1; }; const handleCurrentPageChange (val: number) { currentPage.value val; }; // 商品点击事件触发外部回调 const handleProductClick (product: Product) { props.onProductClick?.(product); }; /script style scoped .product-list-container { padding: 20px; max-width: 1400px; margin: 0 auto; } .page-title { margin-bottom: 20px; font-weight: 600; color: #1989fa; } .filter-container { background: #f8f9fa; padding: 20px; border-radius: 8px; margin-bottom: 16px; display: flex; flex-wrap: wrap; gap: 16px; align-items: center; } .search-input { flex: 1; min-width: 200px; } .category-select { width: 180px; } .brand-filter { flex: 1; min-width: 250px; } .filter-label { font-size: 14px; color: #666; margin-bottom: 8px; display: flex; justify-content: space-between; align-items: center; } .brand-checkbox-group { display: flex; flex-wrap: wrap; gap: 8px; } .all-checkbox { font-size: 14px; } .price-filter { width: 250px; } .reset-btn { white-space: nowrap; } .sort-container { display: flex; justify-content: flex-end; margin-bottom: 16px; } .sort-select { width: 200px; } .skeleton-container { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 20px; margin-bottom: 20px; } .skeleton-item { border-radius: 8px; } .product-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 20px; margin-bottom: 20px; } .product-card { border-radius: 8px; transition: transform 0.3s ease; cursor: pointer; } .product-card:hover { transform: translateY(-5px); } .product-image { width: 100%; height: 200px; object-fit: cover; border-radius: 8px 8px 0 0; } .product-info { padding: 12px; } .product-name { font-size: 14px; font-weight: 500; margin-bottom: 8px; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; } .product-price { color: #ff4d4f; font-size: 18px; font-weight: 600; margin-bottom: 4px; } .product-meta { font-size: 12px; color: #999; display: flex; justify-content: space-between; } .empty-container { padding: 50px 0; } .pagination-container { display: flex; justify-content: center; margin-top: 30px; } /style二、后端接口对接逻辑Axios 实战版1. 安装依赖npminstallaxios types/axios# Vue3 项目# 或yarnaddaxios types/axios2. 创建接口请求工具api/product.tsimportaxiosfromaxios;importtype{Product,FilterParams}from/components/ProductList.vue;// 导入组件类型// 创建Axios实例constserviceaxios.create({baseURL:import.meta.env.VITE_API_BASE_URL,// 从环境变量读取接口地址timeout:5000,headers:{Content-Type:application/json,},});/** * 获取商品列表支持筛选、排序、分页 * param params 筛选分页参数 * returns 商品列表总数 */exportconstfetchProductListasync(params:{filters:FilterParams;sortType:string;pageNum:number;pageSize:number;}){constresponseawaitservice.get(/api/product/list,{params:{keyword:params.filters.keyword,category:params.filters.category,brand:params.filters.brand.join(,),// 品牌数组转字符串传递minPrice:params.filters.priceRange[0],maxPrice:params.filters.priceRange[1],sortType:params.sortType,pageNum:params.pageNum,pageSize:params.pageSize,},});returnresponse.data;// 假设后端返回格式{ code: 200, data: { list: Product[], total: number } }};/** * 获取商品分类列表 */exportconstfetchCategoryListasync(){constresponseawaitservice.get(/api/product/categories);returnresponse.data;// 后端返回分类数组{ code: 200, data: { label: string, value: string }[] }};/** * 获取商品品牌列表 */exportconstfetchBrandListasync(){constresponseawaitservice.get(/api/product/brands);returnresponse.data;// 后端返回品牌数组{ code: 200, data: { label: string, value: string }[] }};3. 组件中对接接口替换模拟数据script setup langts // 导入接口函数 import { fetchProductList, fetchCategoryList, fetchBrandList } from /api/product; // 替换原fetchProducts函数 const fetchProducts async () { loading.value true; try { // 并行请求分类、品牌、商品数据 const [categoryRes, brandRes, productRes] await Promise.all([ fetchCategoryList(), fetchBrandList(), fetchProductList({ filters: filters.value, sortType: sortType.value, pageNum: currentPage.value, pageSize: pageSize.value, }), ]); // 赋值分类和品牌替换原计算属性 categoryOptions.value categoryRes.data; brandOptions.value brandRes.data; products.value productRes.data.list; total.value productRes.data.total; // 新增total状态存储总条数 } catch (error) { console.error(获取商品数据失败, error); ElMessage.error(加载商品失败请重试); } finally { loading.value false; } }; // 监听筛选条件变化重新请求接口替换原computed筛选逻辑 watch([() filters.value, () sortType.value], async () { currentPage.value 1; await fetchProducts(); }, { deep: true }); // 监听分页变化重新请求接口 watch([() currentPage.value, () pageSize.value], async () { await fetchProducts(); }); /script4. 后端接口设计参考RESTful 风格接口地址请求方式参数说明返回格式/api/product/listGETkeyword、category、brand、minPrice、maxPrice、sortType、pageNum、pageSize{ code: 200, data: { list: Product[], total: number }, message: “success” }/api/product/categoriesGET无{ code: 200, data: { label: string, value: string }[], message: “success” }/api/product/brandsGET无{ code: 200, data: { label: string, value: string }[], message: “success” }
版权声明:本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!

中国网站建设哪家公司好国际网站哪里做

Vue-Spinner:解锁Vue应用加载动画的无限可能 【免费下载链接】vue-spinner vue spinners 项目地址: https://gitcode.com/gh_mirrors/vu/vue-spinner Vue-Spinner是一个专为Vue.js设计的加载指示器组件库,提供了15种预设的spinner动画效果&#x…

张小明 2025/12/30 22:37:02 网站建设

邮轮哪个网站是可以做特价做的网站上传到服务器吗

QMCDecode终极指南:Mac平台音频解密神器免费解锁加密音乐 【免费下载链接】QMCDecode QQ音乐QMC格式转换为普通格式(qmcflac转flac,qmc0,qmc3转mp3, mflac,mflac0等转flac),仅支持macOS,可自动识别到QQ音乐下载目录,默…

张小明 2025/12/30 22:36:24 网站建设

赚钱的网站平台WordPress查看用户密码

如果你正在寻找一种方法来简化MikroTik设备的日常管理,RouterOS脚本项目正是你需要的解决方案。这个项目提供了一系列实用的RouterOS自动化脚本,能够帮助你自动处理证书管理、系统更新、健康监控等常见任务。让我们一起来探索如何利用这些脚本提升你的网…

张小明 2025/12/30 22:35:50 网站建设

公司官方网站推广策划阳江招聘临时工

机器学习模型优化:平衡策略与集成方法实战指南 【免费下载链接】ludwig Low-code framework for building custom LLMs, neural networks, and other AI models 项目地址: https://gitcode.com/gh_mirrors/lu/ludwig 你是否在为模型训练中的过拟合和性能不稳…

张小明 2025/12/30 22:35:16 网站建设

wdcp设置网站安全自己有网站做点什么

第一章:医疗护理任务提醒的现状与挑战在现代医疗环境中,护理任务的及时执行直接关系到患者的安全与治疗效果。然而,当前的护理任务提醒系统仍面临诸多挑战,影响了其实际应用效能。信息传递滞后 许多医疗机构仍依赖纸质记录或基础电…

张小明 2025/12/30 22:34:40 网站建设

自助建站网站平台做网站怎么赚钱广告

模型部署终极指南:5步完成AI模型生产环境部署 【免费下载链接】mmdeploy OpenMMLab Model Deployment Framework 项目地址: https://gitcode.com/gh_mirrors/mm/mmdeploy 模型部署是深度学习项目从实验走向生产的关键环节,MMDeploy作为OpenMMLab生…

张小明 2026/1/1 13:35:56 网站建设