废话不多说,上干货,注释简单易懂。
注意:重点是前后端联动,实现多字段过滤的搜索查询,以下从后端连接数据库开始到前端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>
完结!以上展示的仅是部分代码,并不完整,供学习参考用。