【前端】实习随笔2 - 数据统计 - 商品维度订单统计

 你怪我没站在你这边,其实,我一直站在你这边。

需求说明

(1)商品ID:精准匹配,不超过50字,超出直接不可输入;每次仅能输入一个,查询结果为该商品ID对应数据,若有N个渠道都是此商品ID,展示一条数据,指此商品ID对应的渠道试听课订单数总和

(2)商品后台名称:根据商品名称模糊检索  不超过50字,超出直接不可输入;每次仅能输入一个 例如:输入:巴,   会把巴拉巴拉、新巴拉巴拉都搜索出来 

(3)商品类型:点击‘’商品类型‘’展示下拉列表(‘’试听课‘’  ‘’主修课‘’  ‘’辅修课‘’  “全部”四个选项,筛选所需数据,四个选项只能选择一个,鼠标悬停时为阴影,选中时为蓝色标注文本颜色由黑色变为蓝色)

(4)商品现价:点击‘’商品现价‘’展示下拉列表(‘’非0元‘’  ‘’0元‘’ “全部” 三个选项,筛选所需数据,三个选项只能选择一个,鼠标悬停时为阴影,选中时为蓝色标注文本颜色由黑色变为蓝色)

(5)开课日期:选择开课日期,点击,展示符合授课时间条件的数据内容。举例:选择2020年8月30日,则展示开课时间在8.30日的数据;

(6)支付成功时间:

  • 仅要求结束日期≥开始日期,手动切换,最长可查看一年范围宽度的数据。若筛选条件没有选择支付成功时间,同样展示一年范围宽度的数据。
  • 结束日期=开始日期时,表示查询当天的数据;检索时选择时间范围(按天时间),点击查询,展示符合支付时间所选择范围的数据内容。
  • 结束日期>开始日期,查询开始日期至结束日期时间段的订单数据。

(7)点击【查询】:展示查询结果,若没选择任意筛选项进行查询,toast提示:请选择商品ID/商品后台名称/商品类型/商品现价/开课日期/支付成功时间后再查询;

(8)点击【清空】:指清除商品ID,商品类型,商品现价,开课日期,支付成功时间,页面保持清空前状态不变;

(9)点击【导出】:

  • 导出当前筛选结果数据,字段同列表字段;
  • 导出格式:Excel表;
  • 命名规则:商品维度统计表_商品类型_开课时间,若三者有未筛选的,则不展示,举例:三者都筛选,展示商品维度统计表_商品类型_2020.08.03,若未筛选商品类型,则展示商品维度统计表_2020.08.03,若未筛选开课时间,则展示商品维度统计表_商品类型

(10)数据过多,分页展示;每页100条;

(11)统计:展示搜索的累计订单数,订单数为当前筛选条件下所有页面数据的总和(例如有10页数据显示的是10页的试听课订单数总和),并非当前1页的数据总和。

一、页面实现

1. 核心表格的HTML

<div class="table" style="width:750px;margin-top:20px">
        <el-table
          :header-cell-style="{background:'#F2F6FC','text-align':'center'}"
          :data="orderList"
          border
          fit
          highlight-current-row
          element-loading-text="Loading"
          style="margin-top:20px"
        >
          <el-table-column label="商品ID" align="center" width="100">
            <template slot-scope="scope">{{scope.row.goodsId}}</template>
          </el-table-column>
          <el-table-column label="商品类型" align="center" width="100">
            <template slot-scope="scope">{{scope.row.typeName}}</template>
          </el-table-column>
          <el-table-column label="商品后台名称" align="center" width="200">
            <template slot-scope="scope">{{scope.row.name}}</template>
          </el-table-column>
          <el-table-column label="商品现价" align="center" width="120">
            <template slot-scope="scope">{{scope.row.sellPrice}}</template>
          </el-table-column>
          <el-table-column label="开课日期" align="center" width="120">
            <template slot-scope="scope">{{scope.row.startTime}}</template>
          </el-table-column>
          <el-table-column label="订单数" align="center">
            <template slot-scope="scope">{{scope.row.orderCount}}</template>
          </el-table-column>
        </el-table>
        <el-pagination
          style="display:flex; justify-content:center; margin:10px"
          layout="total, prev, pager, next, jumper"
          :current-page="currentPage"
          @current-change="currentPageChange"
          :page-size="pageNum"
          :total="total"
          background
        ></el-pagination>
      </div>
      <div class="table" style="width:750px;margin-top:20px">
        <el-table :data="acount" border :show-header="false">
          <el-table-column width="100" align="center" class-name="gray bor">
            <template slot-scope="scope">{{ scope.row.name }}</template>
          </el-table-column>
          <!--<el-table-column width="100" align="center" class-name="gray bor">
            <template slot-scope="scope">{{ scope.row.type }}</template>
          </el-table-column>
          <el-table-column width="200" align="center" class-name="gray bor">
            <template slot-scope="scope">{{ scope.row.goodsName }}</template>
          </el-table-column>
          <el-table-column width="120" align="center" class-name="gray bor">
            <template slot-scope="scope">{{ scope.row.priceType }}</template>
          </el-table-column>
          <el-table-column width="120" align="center" class-name="gray bor">
            <template slot-scope="scope">{{ scope.row.startTime }}</template>
          </el-table-column>-->
          <el-table-column align="center" class-name="bor">
            <template slot-scope="scope">{{ scope.row.orderSum }}</template>
          </el-table-column>
        </el-table>
      </div>

2. 数据结构概览

data() {
    return {
      loading: true,
      isflag: true,   // 
      isInit: false,    // 初始化
      noresult: false,
      pageNum: 100,   // 最大显示个数
      currentPage: 1,    // 当前页码
      total: 0,
      goodsId: '',  // 商品ID
      goodsName: '',   // 商品名称
      type: '',    // 商品类型
      priceType: '',   // 商品现价
      sellPrice: '',  
      orderList: [],   
      startTime: null,   // 开课时间
      orderNumByDay: [],
      count: 0,     // 统计
      paymentStartTime: null,     // 支付成功开始时间
      paymentEndTime: null,    // 支付成功结束时间
      goodsTypeOptions: [    // 商品类型选项
        {
          type: '0',
          label: '试听课',
        },
        {
          type: '1',
          label: '主修课',
        },
        {
          type: '2',
          label: '辅修课',
        },
        {
          type: '3',
          label: '全部',
        },
      ],
      goodsPriceOptions: [    // 商品价格选项
        {
          priceType: '0',
          label: '0元',
        },
        {
          priceType: '1',
          label: '非0元',
        },
        {
          priceType: '2',
          label: '全部',
        },
      ],
      acount: [    // 下方统计表格数据结构
        {
          name: '统计',
          type: '',
          goodsName: '',
          priceType: '',
          startTime: '',
          orderSum: 0,
        },
      ], 
      search: {      // 存放查询结果,便于导出
        pageIndex: 1,
        pageNum: 100,
        goodsId: '',
        goodsName: '',
        isInit: true,
        type: '',
        priceType: '',
        startTime: '',
        paymentStartTime: '',
        paymentEndTime: '',
      },
      time: [],
    }
  },

 二、功能实现

1. 页面初始化数据

思路:在created中初始化页面数据,调用函数getOrderList() 。首次页面加载会显示当前日期的下周一的情况(这里后端已经做了处理,前端只需要计算时间戳,并传参isInit即可,此时currentpage=1默认显示第一页)使用 post请求数据即可。

接口:/admin/statistics/orderStatistics

created() {
    this.getOrderList()    // 页面初始化
    this.isflag = true
    this.$ajax.timeout = 600000
},
// methods
async getOrderList() {
      // 获取当前日期的下周一时间戳
      var myDate = new Date()
      myDate.setDate(
        myDate.getDay() == 0
          ? myDate.getDate() - 6
          : myDate.getDate() - (myDate.getDay() - 1)
      ) //先获取当前日期的周一
      myDate.setHours(0,0,0,0)
      var nextmon = myDate.setDate(myDate.getDate() + 7) //+7代表下一个周一
      var temp = new Date(nextmon)
      temp = temp.toISOString();
      this.startTime = temp
      this.isInit = true  
      this.getListAjax()
    },
async getListAjax() {
      this.commonListAjax(1)
    },

2. 清空搜索数据

// 清空按钮
    async clearBtn() {
      this.time = []
      this.goodsId = null
      this.goodsName = null
      this.type = null
      this.priceType = null
      this.startTime = null
    },

 3. 页码更改

结合el-pagination进行绑定currentPageChange() 是页面通用函数

<el-pagination
    style="display:flex; justify-content:center; margin:10px"
    layout="total, prev, pager, next, jumper"
    :current-page="currentPage"
    @current-change="currentPageChange"
    :page-size="pageNum"
    :total="total"
    background
></el-pagination>
// method实现
async currentPageChange(currentPage) {
      console.log(currentPage)
      this.currentPage = currentPage
      this.commonListAjax(currentPage)
},

4. 查询按钮的实现

思路:先做判空的处理,然后调用getListAjax() 函数,间接调用commonListAjax(1) (1表示显示查询结果的第一页,同第一次加载页面初始化时的commonListAjax数据请求操作)。接口:/admin/statistics/orderStatistics。

注意:这里在最后有一个修改search查询数组的操作,主要用来暂存查询结果条件,便于导出。

// 搜索按钮
    async searchBtn() {
      // console.log('search')
      if (this.time == null) this.time = []
      if (
        this.goodsId || this.goodsName || this.type ||
        this.priceType || this.time[0] != undefined || this.startTime != null
      ) {
        this.getListAjax()
        // this.isSearched = true;
      } else {
        this.$message.error('请选择商品ID /商品类型 /商品后台名称 /商品现价 /开课日期后再查询')
      }
      this.isflag = false
    },

 在baseData() 中对查询的开始时间和结束时间,以及商品ID进行了处理

baseData() {
      let start, end
      if (this.time == null) this.time = []
      if (this.time[0] == undefined) {
        start = null
        end = null
      } else {
        start = this.time[0]
        end = this.time[1]
      }
      let goodsId = null
      if (this.goodsId !== null) {
        goodsId = Number(this.goodsId)
        if (Number.isNaN(goodsId)) {
          this.$message.error('请输入正确的商品Id')
          return -1
        }
      }
      return {
        goodsId,
        paymentStartTime: start,
        paymentEndTime: end,
      }
    },

调用接口返回数据之前的判断:(1)如果20001 查询时常有问题 (2) 如果orderResultList为null,说明无查询结果,需要将orderResultList置空(注意,这里不能直接把orderResultList赋值为空对象,这样后面无法拿到对象中的数据,需要对对象中的数据进行赋空)这里使用的方法比较复杂,其实直接使用parse() 和 Stringify()也可以实现同样的效果。

注意:这里在最后有一个修改search查询数组的操作,主要用来暂存查询结果条件,便于导出。

async commonListAjax(currentPage) {
      const tmpData = this.baseData()
      if (tmpData === -1) {
        this.loading = false
        return
      }
      const { paymentStartTime, paymentEndTime, goodsId } = tmpData
      let res = await this.$ajax.post('/admin/statistics/orderStatistics', {
        pageIndex: this.currentPage,
        pageNum: this.pageNum,
        goodsId: this.goodsId,
        goodsName: this.goodsName,
        isInit: this.isInit,
        type: this.type,
        priceType: this.priceType,
        startTime: this.startTime,
        paymentStartTime: paymentStartTime,
        paymentEndTime: paymentEndTime,
      })
      this.isInit = false
      this.loading = false
      let data = res.data.data
      if (res.data.retcode == 20001) {
        this.$message({
          message: '查询时间长度不能超过一年',
          type: 'warning',
        })
        console.log('一年')
      } else if (data.orderResultList == null) {
        this.orderList = []
        this.total = data.count
        this.acount[0].orderSum = data.orderSum
        this.search.pageIndex = this.currentPage
        this.search.pageNum = this.pageNum
        ;(this.search.goodsId = ''),
          (this.search.goodsName = ''),
          (this.search.isInit = false), //
          (this.search.type = ''),
          (this.search.priceType = ''),
          (this.search.startTime = ''),
          (this.search.paymentStartTime = ''),
          (this.search.paymentEndTime = ''),
          (this.noresult = true)
        this.$message({
          message: '无查询结果',
          type: 'warning',
        })
      } else if (data.orderResultList != null) {
        this.orderList = data.orderResultList.map(item => {
          const time = item['startTime']
          item['startTime'] = this.format(new Date(time), 'yyyy-MM-dd')
          return item
        })
        this.total = data.count
        this.acount[0].orderSum = data.orderSum
        // 修改search查询数组
        console.log(this.search)
        this.search.pageIndex = this.currentPage
        this.search.pageNum = this.pageNum
        this.search.goodsId = this.goodsId
        this.search.goodsName = this.goodsName
        this.search.isInit = this.isflag
        this.search.type = this.type
        this.search.priceType = this.priceType
        this.search.startTime = this.startTime
        this.search.paymentStartTime = this.paymentStartTime
        this.search.paymentEndTime = this.paymentEndTime
        this.noresult = false
      }
    },

5. 导出表格

导出数据接口: '/admin/statistics/exportOrderStatistics'

// 导出为excel
    async expExcel() {
      if (this.noresult) {
        this.$message({
          message: '无导出结果',
          type: 'warning',
        })
      } else {
        var params = {
          goodsId: this.search.goodsId,
          goodsName: this.search.goodsName,
          isInit: this.isflag,
          pageIndex: this.search.pageIndex,
          pageNum: this.search.pageNum,
          paymentStartTime: this.search.paymentStartTime,
          paymentEndTime: this.search.paymentEndTime,
          priceType: this.search.priceType,
          startTime: this.search.startTime,
          type: this.search.type,
        }
        let myurl = '/admin/statistics/exportOrderStatistics'
        {
          this.$ajax({
            url: myurl,
            method: 'post',
            data: params,
            responseType: 'arraybuffer',
          }).then(res => {
            console.log(res)
            let fileName = '商品维度统计表'
            let date = this.format(new Date(this.startTime), 'yyyy-MM-dd')
            var name
            if (this.search.type == '0') {name = '试听课'
            } else if (this.search.type == '1') {name = '主修课'
            } else if (this.search.type == '2') {name = '辅修课'
            } else {name = ''
            }
            if ((this.search.type == '' || this.search.type == '3') &&
              this.search.startTime == null
            ) {
              fileName = '商品维度统计表'
            } else if (
              (this.search.type == '' || this.search.type == '3') &&
              this.search.startTime == null
            ) {
              fileName = `商品维度统计表_${name}`
            } else if (
              this.search.startTime != null &&
              (this.search.type == '' || this.search.type == '3')
            ) {
              fileName = `商品维度统计表_${date}`
            } else {
              fileName = `商品维度统计表_${name}_${date}`
            }
            this.exportExcel(res.data, fileName)
          })
        }
      }
    },

在导出之前,需要按照要求对导出文件名做一定拼接(情况挺多的,我写的肯定是最笨的方法),之后进行下载,调用exportExcel()这个函数可以通用了的

// 自动下载从后端返回的excel
    exportExcel(blobData, filename) {
      const link = document.createElement('a')
      let blob = new Blob([blobData], {
        type:
          'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
      })
      link.style.display = 'none'
      link.href = URL.createObjectURL(blob)
      console.log(link.href)
      link.download = filename //下载的文件名 ret.data['Content-Disposition'].split(';')[1].split('filename=')[1]
      document.body.appendChild(link)
      link.click()
      document.body.removeChild(link)
    },

其中使用到的日期formate()方法也是可以通用的。

format(time, fmt) {
      //author: meizz
      var o = {
        'M+': time.getMonth() + 1, //月份
        'd+': time.getDate(), //日
        'h+': time.getHours(), //小时
        'm+': time.getMinutes(), //分
        's+': time.getSeconds(), //秒
        'q+': Math.floor((time.getMonth() + 3) / 3), //季度
        S: time.getMilliseconds(), //毫秒
      }
      if (/(y+)/.test(fmt))
        fmt = fmt.replace(
          RegExp.$1,
          (time.getFullYear() + '').substr(4 - RegExp.$1.length)
        )
      for (var k in o)
        if (new RegExp('(' + k + ')').test(fmt))
          fmt = fmt.replace(
            RegExp.$1,
            RegExp.$1.length == 1
              ? o[k]
              : ('00' + o[k]).substr(('' + o[k]).length)
          )
      return fmt
    },

(后记:这是第一次参与实际项目(我太菜了 勿吐槽),并且排期只给一天联调一天,做的还是战战兢兢如履薄冰的,其实记得遇到了很多问题,各种各样的小问题,(我太菜了 勿吐槽)很多还是面试常考点,当时只会背面试题,现在才算用到,(我太菜了 勿吐槽)但还好完成了,收获很大,果然只有做项目才学的更快)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
ThinkPHP 是一个优秀的PHP框架,可以帮助我们快速开发高质量的 Web 应用程序。因此,使用 ThinkPHP 来开发 OA 系统是非常方便和实用的。下面我们来讨论一下如何使用 ThinkPHP 来开发一个人力资源管理系统。 1. 创建数据库 首先,我们需要创建一个数据库来存储我们的数据。在该数据库中,我们需要创建一些表来存储员工信息、部门信息、职位信息、薪资信息等等。我们可以使用 MySQL 数据库来创建这些表。 2. 创建 ThinkPHP 项目 在创建完数据库之后,我们需要创建一个 ThinkPHP 项目来实现我们的 OA 系统。我们可以使用 ThinkPHP 官方提供的命令行工具来创建项目,具体命令如下: ``` composer create-project topthink/think oa ``` 执行完上述命令后,会在当前目录下创建一个名为 oa 的 ThinkPHP 项目。 3. 配置数据库 在项目创建好之后,我们需要配置数据库连接信息。在项目的 `.env` 文件中,我们可以配置数据库的相关信息,如下所示: ``` # 数据库类型 DB_CONNECTION=mysql # 数据库地址 DB_HOST=127.0.0.1 # 数据库端口 DB_PORT=3306 # 数据库名 DB_DATABASE=oa # 数据库用户名 DB_USERNAME=root # 数据库密码 DB_PASSWORD= ``` 我们需要根据自己的实际情况来修改这些配置项。 4. 创建控制器和模型 在 ThinkPHP 中,我们可以通过创建控制器和模型来实现 OA 系统的功能。我们可以使用命令行工具来快速创建控制器和模型,具体命令如下: ``` # 创建控制器 php think make:controller Index # 创建模型 php think make:model User ``` 执行完上述命令后,会在项目中生成一个名为 Index 的控制器和一个名为 User 的模型。 5. 实现功能 在创建好控制器和模型之后,我们就可以开始实现 OA 系统的功能了。比如,我们可以实现员工信息的添加、修改、删除、查询等操作,部门信息的添加、修改、删除、查询等操作,职位信息的添加、修改、删除、查询等操作,薪资信息的添加、修改、删除、查询等操作等等。我们可以在控制器中编写处理逻辑,在模型中编写数据库操作。最后,我们可以使用视图来展示数据。 6. 测试系统 在实现完功能之后,我们需要对系统进行测试。我们可以使用浏览器或者 Postman 等工具来测试系统的功能是否正常。如果有问题,我们可以根据错误提示来进行调试和修复。 总之,使用 ThinkPHP 来开发 OA 系统是非常方便和实用的。我们可以根据自己的实际情况来定制系统的功能和界面,从而更好地管理公司的人力资源。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小白Rachel

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值