vue3+node.js 封装MongoDB find,传入query对象即可实现多条件、日期时间范围查询

文章展示了如何使用Node.js连接MongoDB数据库,封装查询函数实现前后端联动的多字段过滤搜索。通过axios在Vue3应用中调用API,Pinia管理状态,动态更新表格数据。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

废话不多说,上干货,注释简单易懂。

注意:重点是前后端联动,实现多字段过滤的搜索查询,以下从后端连接数据库开始到前端dom组件绑定数据结束,过程相对完整!

node>db>mongodb.js:连接数据库

const { MongoClient } = require('mongodb')

// Connection URL:把localhost改成127.0.0.1,默认端口27017
const url = 'mongodb://127.0.0.1:27017'
const client = new MongoClient(url)

// Database Name
const dbName = 'wechat'

// Use connect method to connect to the server
client.connect()
console.log('Connected successfully to mongoDB server')
const db = client.db(dbName)

node>db>mongodb.js:封装find

注意下格式化日期时间、格式化$in的这两部分,有点绕……

// 封装:查询文档
// query:前端axiosInstance传回的params对象
const findDoc = function (query, document) {
  return new Promise(async (resolve, reject) => {
    // 排除空对象?前端有可能会传空对象导致出错
    let queryData = {}   // pDateStr被dateStr替换
    let $and = []     // 存日期时间范围查询条件对象,也可存其它查询条件对象
    let startDate = {}
    let endDate = {}
    for (const key in query) {
      if (query[key].length > 0) {
        // 去掉[]:前端传回来的有可能是个数组,索引带有[]
        let newKey = key.replaceAll('[]', '')
        // 将newKey转成小写
        let str = newKey.toLowerCase()
        if (str.includes('day') || str.includes('date') || str.includes('time')) {
          // 用new Date()格式化成标准时间,数据库即可正常比对
          startDate[newKey] = { '$gte': new Date(query[key][0]) }
          endDate[newKey] = { '$lte': new Date(query[key][1]) }
          $and.push(startDate)
          $and.push(endDate)
        } else {
          queryData[newKey] = query[key]      // 口诀:框框索引键值对
        }
      }
    }
    // 格式化查询条件:返回符合MongoDB查询条件语法的字符串
    const queryStr = JSON.stringify(queryData)
    const newStr1 = queryStr.replaceAll('[', '{"$in":[')      // 多选:构建多值查询提交
    const newStr2 = newStr1.replaceAll(']', ']}')         // 多选:构建多值查询提交
    let newQueryObj = JSON.parse(newStr2)        // 字符串格式化后再解析成对象:有可能会出错……
    // 如果数组不为空,则将其挂到查询条件对象中
    if ($and.length > 0) {
      newQueryObj['$and'] = $and
    }
    // find第一个参数:实则为JSON对象,注意日期已经被格式化成标准时间,对应的mongodb字段得是date格式
    await db.collection(document).find(newQueryObj).toArray()
      .then((res) => {
        if (res.length == 0) {
          resolve({
            status: 202,
            message: '未查找到数据!',
            data: res
          })
        }
        // 获取成功:直接返回结果
        resolve({
          status: 200,
          message: 'find document successfully!',
          data: res
        })
      })
      .catch((err) => {
        return reject({
          status: 203,
          message: '从MongoDB查找数据失败,请检查查询条件!',
          data: err
        })
      })
  })
}

node>db>mongodb.js:暴露方法函数

这个js文件定义了MongoDB增删改查常用方法函数,故用如下形式暴露:

module.exports = { findDoc, findOne, insertDoc, deleteDoc, updateDoc, distinctField }

node>router>index.js:引入并使用

很简单的调用,不需要额外再添加一层module

const { findDoc } = require('../db/MongoDB')
router.get('/mongo/task/find', async (ctx) => {
  ctx.body = await findDoc(ctx.request.query, 'task')
})

vue3>src>api:调用api并传参

axiosInstance的配置略,请参考查阅官网。

注意:用axiosInstance的params接收查询条件,其本质是一个符合JSON格式的对象;范围型字段需要传入start、end两个值,并且通过if(str.includes(?))的判断,否则封装函数执行出错

import { axiosInstance } from "./axios"

export function taskFind(query:object) {
  return axiosInstance({
    url:'/api/mongo/task/find',
    params:query,     // 用params接收get请求参数对象
    method:'get'
  })
}

vue3>src>pinia>taskStore

不用pinia也行,重要的是其实现了什么

import { taskFind } from "@/api/task"
import { defineStore } from "pinia"
import { ElMessage } from "element-plus"
import 'element-plus/es/components/message/style/css'

export interface taskState {
  // 数组嵌套对象,有多个对象,属性复杂
  taskData: [],
  taskNum:number,
  tableHeight: number,    // table高度:根据行数计算
}


export const taskStore = defineStore('task', {
  state: (): taskState => {
    return {
      taskData: [],
      taskNum:0,
      tableHeight: 1200
    }
  },
  actions: {
    async getTask(query: object) {
      const resTask: any = await taskFind(query)
      if (resTask.status == 200) {
      const len = resTask.data.length
      // 根据行数计算表格高度:
      if (len < 24) {
        // 40:表格tr高度
        this.tableHeight = (len + 2) * 40
      } else {
        this.tableHeight = 1600     // 最高1600px,超出屏幕很多
      }
        // 更新数据
        this.taskNum = resTask.data.length
        return this.taskData = resTask.data
      }
      ElMessage({
        message: resTask.message,
        type: 'warning'
      })
    }
  }
})

vue3>src>views>app>Task.vue

查询返回的数据动态响应挂载到el-table,这里仅展示与数据处理有关的代码

import { taskStore } from '@/pinia/taskStore'

const useTaskStore = taskStore()

// 初始化过滤字段的input值,v-model绑定到搜索框
let taskFieldValue: any = reactive({})
for (const key in taskField) {
  taskFieldValue[key] = ''
}

// 执行查询搜索按钮:根据条件调用pinia从MongoDB搜索内容并返回
let loading = ref(false)    // 数据加载动画:遮罩并转圈
const handleExc = async () => {
  loading.value = true
  // 从MongoDB搜索并返回任务内容,渲染到table
  // console.log('taskFieldValue:', taskFieldValue);  
  await useTaskStore.getTask(taskFieldValue)
  setTimeout(() => {
    loading.value = false     // 一般加载很快:只会显示一点点
  }, 1000)
}


// 默认加载进行中的任务!
onMounted(async () => {
  if (!useTaskStore.getTask) {
    location.reload()
  }
  await useTaskStore.getTask({ TState: "进行中" })
})

<!-- 列表:数据展示 -->
<!-- table-layout:auto根据第一行内容定宽? -->
<el-table :data="useTaskStore.taskData" style="width: 100%;" :height="useTaskStore.tableHeight" class="task-table"
  lazy @mousewheel="handleScroll" v-loading="loading" element-loading-text="Loading...">
  <el-table-column type="selection" width=".3rem" />
  <template v-for="(item, index) in taskField" :key="index">
    <el-table-column :prop="index" :label="item.label" :min-width="item.minWidth" :sortable="item.sortable"
      :fixed="item.fixed" :show-overflow-tooltip="item.showOverflowTooltip" :formatter="formatterDate"
      v-if="item.vShow" />
  </template>
</el-table>

完结!以上展示的仅是部分代码,并不完整,供学习参考用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Arrow.Light

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

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

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

打赏作者

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

抵扣说明:

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

余额充值