vue3-andsign 中实现实物电商列表的页面

news2025/6/9 19:23:25

这里自己做一个代码整理 做了一个实物电商 选品中心的页面 看里面有些效果挺好 这里记录一下

直接粘贴代码了 我自己能看懂  做了一个列表显示 骨架屏等 效果 使用了grid 布局 比媒体查询好使

<script setup lang="ts">
import { ref, onMounted, watch } from 'vue'
import { useRoute } from 'vue-router'
import { message, Skeleton } from 'ant-design-vue'
import * as echarts from 'echarts'
import Tip from './components/Tip.vue'
import { useBrandStore } from '@/store/modules/brand'
import { DownOutlined } from '@ant-design/icons-vue'
import Item from './components/item.vue'
import * as phyicalApi from '@/api/physical'

const route = useRoute()
let chart = ref<echarts.ECharts | null>(null)
const oneRef = ref(null)
const dataSource = ref([])
const loading = ref(false)
const skeletonLoading = ref(false)
const addGoodsRef = ref(null)

interface Pagination {
  page: number
  pageSize: number
  total: number
  current?: number
  onChange: Function
  showSizeChanger: boolean
  showQuickJumper: boolean
}

const handlePageChange = (page: any, pageSize: any) => {
  pagination.value.pageSize = pageSize
  searchParams.value.pageSize = pageSize
}

const pagination = ref<Pagination>({
  page: 1,
  pageSize: 12,
  total: 0,
  onChange: handlePageChange,
  showSizeChanger: true,
  showQuickJumper: true,
})

const searchParams = ref<SearchParams>({
  currentPage: pagination.value.page,
  pageSize: pagination.value.pageSize,
  pdrPutAwayTimeNum: 0,
  fortyBelowPrice: 0,
})

interface SearchParams {
  currentPage: number
  pageSize: number
  [propName: string]: any
}

const onChangePage = (page: any, pageSize: any) => {
  searchParams.value.currentPage = page
  skeletonLoading.value = true
  getList()
}

onMounted(() => {
  getList()
  getCateList()
})

const getList = async () => {
  try {
    loading.value = true
    skeletonLoading.value = true
    const { state, data, message: msg } = await phyicalApi.getGoodsList(searchParams.value)
    if (state == 200) {
      dataSource.value = data.list
      pagination.value.total = Number(data.totalCount)
    } else {
      message.error(msg)
    }
  } catch (error) {
    message.error('网络请求连接失败~')
  } finally {
    loading.value = false
    setTimeout(() => {
      skeletonLoading.value = false
    }, 500)
  }
}

const drawOne = (chart: any) => {
  let option = {
    xAxis: {
      type: 'category',
      data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
    },
    yAxis: {
      type: 'value',
    },
    series: [
      {
        data: [
          120,
          {
            value: 200,
            itemStyle: {
              color: '#a90000',
            },
          },
          150,
          80,
          70,
          110,
          130,
        ],
        type: 'bar',
      },
    ],
  }
  chart.setOption(option)
}

const onResize = () => {
  if (chart.value) {
    chart.value.dispose()
    chart.value = echarts.init(oneRef.value)
    drawOne(chart.value)
  }
}

window.addEventListener('resize', onResize)

let dxTokenRef = ref()
const add = () => {
  dxTokenRef.value.callbackFn()
}

const getToken = (e: any) => {
  console.log(e)
}

const getCateList = async () => {
  try {
    const { data, state, message: msg } = await phyicalApi.getProductCateList()
    if (state == 200) {
      filterList.value = data.map((item: any) => {
        return {
          label: item.cateName,
          id: item.cateId
        }
      })
      filterList.value.unshift({
        label: "所有类目",
        id: 0
      })
    } else {
      message.error(msg)
    }
  } catch (error) {
    console.error(error)
  }
}

const filterList = ref([])
const filterSelected = ref(0)
const filterSelectedId = ref(0)

const filterClick = (item: any, index: any) => {
  filterSelected.value = index
  filterSelectedId.value = item.id
  searchParams.value.cateId = item.id
  if (item.id == 0) {
    delete searchParams.value.cateId
  }
  pagination.value.page = 1
  searchParams.value.currentPage = 1
  getList()
}

let checked1 = ref(false)
let checked2 = ref(false)

watch(checked1, (newVal) => {
  searchParams.value.fortyBelowPrice = newVal ? 1 : 0
  pagination.value.page = 1
  searchParams.value.currentPage = 1
  getList()
})

watch(checked2, (newVal) => {
  searchParams.value.pdrPutAwayTimeNum = newVal ? 1 : 0
  pagination.value.page = 1
  searchParams.value.currentPage = 1
  getList()
})

const beforePrice = ref("")
const afterPrice = ref("")

import type { SelectProps } from 'ant-design-vue';
const value = ref("")
const options = ref<SelectProps['options']>([
  {
    value: '1',
    label: '描述不符包退',
  },
  {
    value: '2',
    label: '二十四小时发货',
  },
  {
    value: '3',
    label: '四十八小时发货',
  },
  {
    value: '4',
    label: '假货包赔',
  },
]);

const handleChange = (e) => {
  value.value = e
}

const handleSearch = () => {
  if (value.value) {
    searchParams.value.serviceAssurance = Number(value.value)
  }

  searchParams.value.beforePrice = beforePrice.value * 100
  searchParams.value.afterPrice = afterPrice.value * 100
  if (!searchParams.value.beforePrice) {
    delete searchParams.value.beforePrice
  }
  if (!searchParams.value.afterPrice) {
    delete searchParams.value.afterPrice
  }

  pagination.value.page = 1
  searchParams.value.currentPage = 1
  getList()
}

const handleReset = () => {
  delete searchParams.value.beforePrice
  delete searchParams.value.afterPrice
  delete searchParams.value.serviceAssurance
  searchParams.value.currentPage = 1
  checked1.value = false
  checked2.value = false
  beforePrice.value = ""
  afterPrice.value = ""
  value.value = ""
  pagination.value.page = 1
  filterClick(filterList.value[0], 0)
}
</script>

<template>
  <page-container :title="route.meta.title">
    <a-card >
      <div class="category-list">
        <div class="title">分销分类</div>
        <div class="left">
          <div class="box">
            <div class="item hiddenText" v-for="(item, index) in filterList"
              :class="{ active: filterSelected === index }" @click="filterClick(item, index)" :key="item.id">
              {{ item.label }}
            </div>
          </div>
        </div>
      </div>

      <div class="filter-list">
        <div class="title">商品信息</div>
        <div class="left">
          <div class="price-between">
            <div class="left-price">
              <a-input-number placeholder="开始价格" v-model:value="beforePrice" style="width: 120px"
                :disabled="loading"></a-input-number>
            </div>
            <div class="line">—</div>
            <div class="right-price">
              <a-input-number placeholder="结束价格" v-model:value="afterPrice" style="width: 120px"
                :disabled="loading"></a-input-number>
            </div>
          </div>

          <div>
            <a-select ref="select" v-model:value="value" placeholder="服务类型" allowClear style="width: 150px"
              :options="options" @change="handleChange" :disabled="loading"></a-select>
          </div>

          <div class="filter-actions">
            <a-button @click="handleSearch" type="primary" :loading="loading">查询</a-button>
            <a-button @click="handleReset" type="default" :disabled="loading">重置</a-button>
          </div>

          <div class="checkbox-group">
            <a-checkbox v-model:checked="checked1" :disabled="loading">40元以下商品</a-checkbox>
            <a-checkbox v-model:checked="checked2" :disabled="loading">新上架商品</a-checkbox>
          </div>
        </div>
      </div>

      <div class="good-list-container">
        <template v-if="skeletonLoading && !loading">
          <div class="skeleton-list">
            <Skeleton v-for="i in 12" :key="i" active />
          </div>
        </template>

        <template v-else-if="dataSource.length > 0">
          <div class="good-list">
            <div class="good-item" v-for="(item, index) in dataSource" :key="index">
              <Item :info="item"></Item>
            </div>
          </div>
        </template>

        <template v-else>
          <div class="empty-list">
            <img src="@/assets/empty.png" alt="暂无数据">
            <div class="empty-text">暂无商品数据</div>
          </div>
        </template>
      </div>

      <div class="pagination-container" v-if="dataSource.length > 0">
        <a-pagination v-model:current="pagination.page" show-quick-jumper :total="pagination.total"
          :show-total="(total) => `共 ${total} 件商品`" @change="onChangePage" :disabled="loading" />
      </div>
    </a-card>
  </page-container>
</template>

<style lang="less" scoped>
.hiddenText {
  display: inline-block;
  width: 100px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.good-list-container {
  min-height: 500px;
  position: relative;
}

.skeleton-list {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
  gap: 24px;
  padding: 16px;
}

.good-list {
  width: 100%;
  margin: 0 auto;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
  gap: 24px;
  padding: 16px;
}

.empty-list {
  height: 400px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  color: #999;

  img {
    width: 200px;
    height: auto;
    margin-bottom: 20px;
  }

  .empty-text {
    font-size: 16px;
  }
}

.good-item {
  border-radius: 8px;
  overflow: hidden;
  border: 1px solid #f0f0f0;
  background: #fff;
  transition: all 0.3s;

  &:hover {
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
    transform: translateY(-2px);
  }
}

.filter-list {
  display: flex;
  align-items: flex-start;
  margin: 24px 0;
  padding: 16px;
  background: #fafafa;
  border-radius: 8px;

  .title {
    font-size: 14px;
    color: #666;
    margin-right: 25px;
    min-width: 80px;
    line-height: 32px;
  }

  .left {
    flex: 1;
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 16px;

    .price-between {
      display: flex;
      align-items: center;

      .line {
        color: #999;
        margin: 0 10px;
      }
    }

    .filter-actions {
      display: flex;
      gap: 8px;
    }

    .checkbox-group {
      display: flex;
      gap: 16px;
      margin-left: 16px;
    }
  }
}

.category-list {
  display: flex;
  margin-bottom: 24px;
  padding: 16px;
  background: #fafafa;
  border-radius: 8px;

  .title {
    font-size: 14px;
    color: #666;
    margin-right: 25px;
    min-width: 80px;
    line-height: 32px;
  }

  .left {
    flex: 1;

    .box {
      display: flex;
      align-items: center;
      flex-wrap: wrap;
      gap: 8px;

      .item {
        padding: 6px 12px;
        border-radius: 16px;
        cursor: pointer;
        border: 1px solid #e8e8e8;
        color: #666;
        transition: all 0.3s;
        font-size: 14px;

        &:hover {
          border-color: #1890ff;
          color: #1890ff;
        }

        &.active {
          background: #1890ff;
          color: #fff;
          border-color: #1890ff;
        }
      }
    }
  }
}

.pagination-container {
  margin-top: 24px;
  display: flex;
  justify-content: center;
}

a:hover {
  color: #1890ff;
}
</style>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2405747.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux Docker的简介

参考资料 30分钟Docker入门教程 ◀ 本篇博客所有图片皆来自于该视频截图阮一峰 - Docker 入门教程 目录 一. 环境配置时可能会遇到的问题二. 什么是Docker三. 虚拟机 与 Docker 的区别3.1 虚拟机3.2 Docker 四. Docker的基本架构五. Dockerfile 一. 环境配置时可能会遇到的问题…

极昆仑智慧与数元灵科技达成战略合作

近日&#xff0c;北京极昆仑智慧科技有限公司与北京数元灵科技有限公司正式签署产品级融合战略合作协议&#xff0c;双方将围绕 "AIBI商业智能分析" " Hybrid RAG 大模型问答" 等核心大模型应用&#xff0c;实现技术架构与业务场景的深度集成&#xff0c;…

第四讲:类和对象(下)

1. 再探构造函数 • 之前我们实现构造函数时&#xff0c;初始化成员变量主要使⽤函数体内赋值&#xff0c;构造函数初始化还有⼀种⽅ 式&#xff0c;就是初始化列表&#xff0c;初始化列表的使⽤⽅式是以⼀个冒号开始&#xff0c;接着是⼀个以逗号分隔的数据成 员列表&#xff…

50天50个小项目 (Vue3 + Tailwindcss V4) ✨ | Dad Jokes(冷笑话卡片)

&#x1f4c5; 我们继续 50 个小项目挑战&#xff01;—— DadJokes 组件 仓库地址&#xff1a;https://github.com/SunACong/50-vue-projects 项目预览地址&#xff1a;https://50-vue-projects.vercel.app/ 豆包翻译确实可以&#xff0c;冷笑话应该属于各类语言比较难理解的…

Spring AOP执行原理源码解析

对【com.example.demo.TestAspect#aopTest】连接点增加了五个通知 在调用【com.example.demo.A#testAop()】&#xff08;用户自定义&#xff09;方法时&#xff0c;Cglib拦截器对其进行了拦截 可以看到执行顺序分别是环绕前置&#xff0c;前置&#xff0c;环绕后置&#xff0c;…

基于FPGA的超声波显示水位距离,通过蓝牙传输水位数据到手机,同时支持RAM存储水位数据,读取数据。

基于FPGA的超声波显示水位距离 前言一、整体框架二、代码架构1.超声波测距模块2.蓝牙数据发送模块3.数码管数据切换模块4.数码管驱动模块6.串口驱动7.顶层模块8.RAM ip核 仿真相关截图 前言 随着工业化进程的加速和环境保护意识的提升&#xff0c;对水资源管理和水位监测的需求…

在Windows下利用LoongArch-toolchain交叉编译Qt

文章目录 0.交叉编译的必要性1.下载交叉编译工具链1.1.直接在Windows下使用mingw&#xff08;不使用虚拟机&#xff09;编译&#xff08;还没成功&#xff0c;无法编译&#xff09;1.2.在虚拟机中的Ubuntu中进行交叉编译 2.下载qt源码3.编译Qt3.1.创建loongarch64的mkspec3.2.创…

AIRIOT无人机安防解决方案

随着无人机技术的飞速发展和广泛应用&#xff0c;其在安防领域的价值日益凸显&#xff0c;从关键设施巡检、大型活动安保到边境巡防、应急救援&#xff0c;无人机正成为立体化安防体系不可或缺的“空中哨兵”。然而&#xff0c;无人机安防应用蓬勃发展的同时&#xff0c;其自身…

华为OD机考 - 水仙花数 Ⅰ(2025B卷 100分)

import java.util.*; public static Integer get(int count,int c){if(count<3||count>7){return -1;}//存储每位数的最高位……最低位int[] arr new int[count];List<Integer> res new ArrayList<>();for(int i(int) Math.pow(10,count-1);i<(int) Math…

php apache构建 Web 服务器

虚拟机配置流程winsever2016配置Apache、Mysql、php_windows server 2016配置web服务器-CSDN博客 PHP 和 Apache 通过 ​​模块化协作​​ 共同构建 Web 服务器&#xff0c;以下是它们的交互机制和工作流程&#xff1a; ​​一、核心组件分工​​ 组件角色​​Apache​​Web …

打通印染车间“神经末梢”:DeviceNet转Ethernet/IP连接机器人的高效方案

在印染行业自动化升级中&#xff0c;设备联网需求迫切。老旧印染设备多采用Devicenet协议&#xff0c;而新型工业机器人普遍支持Ethernet/IP协议&#xff0c;协议不兼容导致数据交互困难&#xff0c;设备协同效率低、生产监控滞后&#xff0c;成了行业数字化转型的阻碍。本文将…

2025-06-02-IP 地址规划及案例分析

IP 地址规划及案例分析 参考资料 Plan for IP addressing - Cloud Adoption Frameworkwww.cnblogs.comimage-hosting/articles at master jonsam-ng/image-hosting 概述 在网络通信中&#xff0c;MAC 地址与 IP 地址分别位于 OSI 模型的数据链路层和网络层&#xff0c;二者协…

AUTOSAR实战教程--开放式通用DoIP刷写工具OpenOTA开发计划

目录 软件概述 安装与运行 界面说明 3.1 功能区划分 3.2 状态显示 基本操作流程 4.1 DoIP连接配置 4.2 服务配置&#xff08;刷写流程&#xff09; 4.3 执行操作 4.4 保存配置 4.5 加载配置 功能详解 5.1 核心功能模块 诊断服务配置 通信设置 文件下载 工具功…

AI赋能的浏览器自动化:Playwright MCP安装配置与实操案例

以下是对Playwright MCP的简单介绍&#xff1a; Playwright MCP 是一个基于 Playwright 的 MCP 工具&#xff0c;提供浏览器自动化功能不要求视觉模型支持&#xff0c;普通的文本大语言模型就可以通过结构化数据与网页交互支持多种浏览器操作&#xff0c;包括截图、点击、拖动…

【技术笔记】MSYS2 指定 Python 版本安装方案

#工作记录 MSYS2 指定 Python 版本安装 一、前置条件 安装指定版本需要在干净的 MSYS2 环境中执行&#xff0c;为保证工具链的兼容性&#xff0c;若已安装 Python&#xff0c;需先卸载 Python 及与该版本深度绑定的工具链。具体操作如下&#xff1a; 卸载 Python&#xff1a…

《校园生活平台从 0 到 1 的搭建》第一篇:创建项目与构建目录结构

在本系列第一篇中&#xff0c;我们将从项目初始化开始&#xff0c;搭建基本的目录结构&#xff0c;并完成四个主页面的创建与 TabBar 设置。 &#xff08;tip&#xff1a;你可能会觉得有点 ai 化&#xff0c;因为这个文案是我自己写了一遍文案之后让 ai 去优化输出的&#xff0…

1 Studying《蓝牙核心规范5.3》

目录 [Vol 0][Part B 蓝牙规范要求] 3 定义 3.1 蓝牙产品类型 4 核心配置 4.1 基本速率核心配置 4.2 增强型数据速率核心配置 4.4 低功耗核心配置 4.5 基本速率和低功耗结合的核心配置 4.6 主机控制器接口核心配置 [Vol 1][Part A 架构]1 概述 1.1 BR/EDR操作概述 …

STM32+MPU6050传感器

#创作灵感## 在嵌入式系统开发中&#xff0c;STM32F103C8T6单片机与MPU6050传感器的组合因其高性能、低功耗以及丰富的功能而备受青睐。本文将简单介绍如何在Keil 5开发环境中实现STM32F103C8T6与MPU6050的连接和基本数据采集&#xff0c;带你快速入门智能硬件开发。 一、硬件…

26考研——数据的表示和运算_整数和实数的表示(2)

408答疑 文章目录 二、整数和实数的表示1、整数的表示1.1、无符号整数的表示1.2、有符号整数的表示1.3、C 语言中的整数类型及类型转换1.3.1、C 语言中的整型数据类型1.3.2、有符号数和无符号数的转换1.3.3、不同字长整数之间的转换 2、实数的表示2.1、浮点数的相关概念2.2、浮…

关于智能体API参考接口

关于智能体在Flask的源码&#xff1a;请求体(在payload里的是请求体)、请求头&#xff08;在headers里的i局势请求头&#xff09;。 我的例子&#xff1a; 我的疑问&#xff1a;为什么没按Coze官方API文档格式&#xff0c;在Apifox里发POST请求却能收到回复&#xff1f; 1. 你…