高级检索
PolicyList.vue
<template>
<div class="policy-container">
<el-container>
<el-aside width="250px">
<el-tree
ref="treeRef"
:data="typeTree"
:props="defaultProps"
@check="handleCheck"
show-checkbox
node-key="id"
default-expand-all
/>
</el-aside>
<el-main>
<div class="search-bar">
<el-input
v-model="searchKeyword"
placeholder="请输入关键词搜索"
class="search-input"
@keyup.enter="handleSearch"
>
<template #append>
<el-button @click="handleSearch">搜索</el-button>
</template>
</el-input>
<el-button type="primary" @click="handleTypeSearch" style="margin-left: 10px">按分类筛选</el-button>
<el-button type="primary" @click="showAdvancedSearch" style="margin-left: 10px">高级检索</el-button>
</div>
<!-- 添加高级检索对话框 -->
<el-dialog
v-model="advancedSearchVisible"
title="高级检索"
width="70%"
>
<advanced-search
:type-tree="typeTree"
@search-complete="handleAdvancedSearchComplete"
/>
</el-dialog>
<el-table :data="policies" border style="width: 100%">
<el-table-column prop="id" label="ID" width="80" />
<el-table-column prop="name" label="政策名称" />
<el-table-column prop="type" label="类型" width="120" />
<el-table-column prop="organ" label="发布机构" width="200" />
<el-table-column prop="pubdata" label="发布日期" width="120" />
<el-table-column fixed="right" label="操作" width="120">
<template #default="scope">
<el-button link type="primary" @click="viewDetails(scope.row)">查看详情</el-button>
</template>
</el-table-column>
</el-table>
<div class="pagination">
<el-pagination
v-model:current-page="currentPage"
v-model:page-size="pageSize"
:page-sizes="[10, 20, 50, 100]"
layout="total, sizes, prev, pager, next"
:total="total"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</el-main>
</el-container>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import axios from 'axios'
import AdvancedSearch from '../components/AdvancedSearch.vue'
import { ElMessage } from 'element-plus'
const router = useRouter()
const policies = ref([])
const total = ref(0)
const currentPage = ref(1)
const pageSize = ref(10)
const searchKeyword = ref('')
const fetchPolicies = async () => {
try {
const response = await axios.get('http://localhost:8090/policy/search', {
params: {
keyword: searchKeyword.value,
current: currentPage.value,
size: pageSize.value
}
})
policies.value = response.data.records
total.value = response.data.total
} catch (error) {
console.error('获取政策列表失败:', error)
}
}
const handleSearch = () => {
currentPage.value = 1
fetchPolicies()
}
const handleSizeChange = (val) => {
pageSize.value = val
fetchPolicies()
}
const handleCurrentChange = (val) => {
currentPage.value = val
fetchPolicies()
}
const viewDetails = (row) => {
router.push(`/policy/${row.id}`)
}
const typeTree = ref([])
const defaultProps = {
children: 'children',
label: 'label'
}
const fetchTypeTree = async () => {
try {
const response = await axios.get('http://localhost:8090/policy/types')
typeTree.value = response.data
} catch (error) {
console.error('获取分类树失败:', error)
}
}
const handleNodeClick = async (data) => {
try {
currentPage.value = 1;
console.log('点击的节点数据:', data);
console.log('传递的分类名称:', data.label); // 使用label获取type值
const response = await axios.get('http://localhost:8090/policy/search/byType', {
params: {
type: data.label, // 改用type字段进行查询
current: currentPage.value,
size: pageSize.value
}
})
if (response.data) {
console.log('接收到的响应数据:', response.data);
policies.value = response.data.records || []
total.value = response.data.total || 0
}
} catch (error) {
console.error('按分类获取政策列表失败:', error)
policies.value = []
total.value = 0
}
}
onMounted(() => {
fetchTypeTree()
fetchPolicies()
})
const treeRef = ref(null)
const selectedTypes = ref([])
// 替换原来的handleNodeClick为handleCheck
const handleCheck = (data, checked) => {
console.log('选中的节点:', checked.checkedNodes)
selectedTypes.value = checked.checkedNodes.map(node => node.label)
}
// 添加新的分类搜索方法
const handleTypeSearch = async () => {
if (selectedTypes.value.length === 0) {
ElMessage.warning('请至少选择一个分类')
return
}
try {
currentPage.value = 1
console.log('选中的分类:', selectedTypes.value)
const response = await axios.get('http://localhost:8090/policy/search/byTypes', {
params: {
types: selectedTypes.value.join(','),
current: currentPage.value,
size: pageSize.value
}
})
if (response.data) {
console.log('接收到的响应数据:', response.data)
policies.value = response.data.records || []
total.value = response.data.total || 0
}
} catch (error) {
console.error('按分类获取政策列表失败:', error)
policies.value = []
total.value = 0
}
}
// 添加高级检索相关变量和方法
const advancedSearchVisible = ref(false)
const showAdvancedSearch = () => {
advancedSearchVisible.value = true
}
const handleAdvancedSearchComplete = (result) => {
policies.value = result.records
total.value = result.total
advancedSearchVisible.value = false
}
</script>
<style scoped>
.policy-container {
height: 100vh;
background-color: #f0f2f5;
}
.el-aside {
background: linear-gradient(135deg, #1e2f97 0%, #1b4db2 100%);
padding: 20px;
box-shadow: 2px 0 8px rgba(0, 0, 0, 0.1);
}
/* 树形控件样式 */
:deep(.el-tree) {
background: transparent;
}
:deep(.el-tree-node__content) {
color: #fff;
}
:deep(.el-tree-node:focus > .el-tree-node__content) {
background-color: rgba(255, 255, 255, 0.1);
}
.el-main {
padding: 25px;
background-color: #fff;
border-radius: 8px;
margin: 20px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
}
.search-bar {
margin-bottom: 25px;
display: flex;
align-items: center;
gap: 10px;
}
.search-input {
width: 300px;
}
:deep(.el-input__wrapper) {
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.05);
}
:deep(.el-button--primary) {
background: linear-gradient(135deg, #1890ff 0%, #1677ff 100%);
border: none;
transition: all 0.3s;
}
:deep(.el-button--primary:hover) {
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(24, 144, 255, 0.3);
}
/* 表格样式 */
:deep(.el-table) {
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
}
:deep(.el-table th) {
background-color: #f5f7fa;
color: #1e2f97;
font-weight: 600;
}
:deep(.el-table tr:hover) {
background-color: #f0f7ff !important;
}
:deep(.el-table td) {
padding: 12px 0;
}
/* 分页控件样式 */
.pagination {
margin-top: 30px;
display: flex;
justify-content: center;
padding: 20px 0;
}
:deep(.el-pagination.is-background .el-pager li:not(.is-disabled).is-active) {
background: linear-gradient(135deg, #1890ff 0%, #1677ff 100%);
}
/* 高级检索对话框样式 */
:deep(.el-dialog) {
border-radius: 12px;
overflow: hidden;
}
:deep(.el-dialog__header) {
background: linear-gradient(135deg, #1e2f97 0%, #1b4db2 100%);
padding: 20px;
margin: 0;
}
:deep(.el-dialog__title) {
color: #fff;
font-size: 18px;
font-weight: 500;
}
:deep(.el-dialog__body) {
padding: 30px;
}
/* 响应式布局 */
@media screen and (max-width: 768px) {
.search-bar {
flex-direction: column;
align-items: stretch;
}
.search-input {
width: 100%;
}
}
</style>
AdvancedSearch.vue
<template>
<div class="advanced-search">
<el-form :model="searchForm" label-width="100px">
<el-form-item label="政策标题">
<el-input v-model="searchForm.title" placeholder="请输入政策标题"></el-input>
</el-form-item>
<el-form-item label="政策内容">
<el-input v-model="searchForm.content" placeholder="请输入政策内容"></el-input>
</el-form-item>
<el-form-item label="发文机构">
<el-input v-model="searchForm.organ" placeholder="请输入发文机构"></el-input>
</el-form-item>
<el-form-item label="政策分类">
<el-cascader
v-model="searchForm.type"
:options="typeOptions"
:props="cascaderProps"
clearable
placeholder="请选择政策分类"
/>
</el-form-item>
<el-form-item label="政策文号">
<el-input v-model="searchForm.document" placeholder="请输入政策文号"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleSearch">检索</el-button>
<el-button @click="resetForm">重置</el-button>
</el-form-item>
</el-form>
<!-- 搜索结果列表 -->
<el-table v-loading="loading" :data="policyList" style="width: 100%">
<el-table-column prop="name" label="政策标题"></el-table-column>
<el-table-column prop="organ" label="发文机构"></el-table-column>
<el-table-column prop="type" label="政策分类"></el-table-column>
<el-table-column prop="document" label="政策文号"></el-table-column>
<el-table-column prop="pubdata" label="发布日期">
<template #default="scope">
{{ formatDate(scope.row.pubdata) }}
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<div class="pagination">
<el-pagination
v-model:current-page="currentPage"
v-model:page-size="pageSize"
:total="total"
:page-sizes="[10, 20, 30, 50]"
layout="total, sizes, prev, pager, next"
@size-change="handleSizeChange"
@current-change="handleCurrentChange">
</el-pagination>
</div>
</div>
</template>
<script>
import { ref, reactive, onMounted } from 'vue'
import axios from 'axios'
export default {
name: 'AdvancedSearch',
emits: ['search-complete'],
props: {
typeTree: {
type: Array,
default: () => []
}
},
setup(props, { emit }) {
const searchForm = reactive({
title: '',
content: '',
organ: '',
type: '',
document: ''
})
const loading = ref(false)
const policyList = ref([])
const currentPage = ref(1)
const pageSize = ref(10)
const total = ref(0)
const typeOptions = ref([])
const cascaderProps = {
value: 'value', // 修改为与后端数据结构匹配的字段
label: 'label',
children: 'children',
checkStrictly: true, // 允许选择任意层级
emitPath: true // 返回完整路径
}
//const typeOptions = computed(() => props.typeTree)
// 获取政策分类选项
const fetchTypeOptions = async () => {
try {
const response = await axios.get('http://localhost:8090/policy/types')
typeOptions.value = response.data.map(type => ({
value: type.label || type.name, // 使用分类名称作为值
label: type.label || type.name,
children: type.children ? type.children.map(child => ({
value: child.label || child.name, // 使用分类名称作为值
label: child.label || child.name,
children: child.children || []
})) : []
}))
} catch (error) {
console.error('获取政策分类失败:', error)
}
}
// 修改 handleSearch 方法
const handleSearch = async () => {
loading.value = true
try {
const params = {
...searchForm,
// 直接使用选中的分类名称
type: Array.isArray(searchForm.type) ? searchForm.type[searchForm.type.length - 1] : searchForm.type,
current: currentPage.value,
size: pageSize.value
}
const response = await axios.get('http://localhost:8090/policy/advanced-search', { params })
policyList.value = response.data.records
total.value = response.data.total
emit('search-complete', response.data)
} catch (error) {
console.error('搜索失败:', error)
} finally {
loading.value = false
}
}
// 重置表单
const resetForm = () => {
Object.keys(searchForm).forEach(key => {
searchForm[key] = ''
})
currentPage.value = 1
handleSearch()
}
// 处理页码变化
const handleCurrentChange = (val) => {
currentPage.value = val
handleSearch()
}
// 处理每页条数变化
const handleSizeChange = (val) => {
pageSize.value = val
currentPage.value = 1
handleSearch()
}
// 格式化日期
const formatDate = (date) => {
if (!date) return ''
return new Date(date).toLocaleDateString()
}
onMounted(() => {
fetchTypeOptions()
handleSearch()
})
return {
searchForm,
loading,
policyList,
currentPage,
pageSize,
total,
typeOptions,
handleSearch,
resetForm,
handleCurrentChange,
handleSizeChange,
formatDate
}
}
}
</script>
<style scoped>
.advanced-search {
max-width: 1200px;
margin: 20px auto;
padding: 30px;
background-color: #fff;
border-radius: 8px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}
.el-form {
margin-bottom: 30px;
}
.el-form-item {
margin-bottom: 25px;
}
.el-input {
width: 100%;
max-width: 500px;
}
.el-cascader {
width: 100%;
max-width: 500px;
}
.el-button {
padding: 12px 25px;
font-size: 14px;
}
.el-button + .el-button {
margin-left: 15px;
}
.el-table {
margin-top: 20px;
border-radius: 4px;
overflow: hidden;
}
.el-table th {
background-color: #f5f7fa;
color: #606266;
font-weight: 500;
padding: 12px 0;
}
.el-table td {
padding: 12px 0;
}
.pagination {
margin-top: 30px;
padding: 20px 0;
text-align: right;
background-color: #fff;
border-radius: 4px;
}
/* 响应式布局 */
@media screen and (max-width: 768px) {
.advanced-search {
padding: 15px;
margin: 10px;
}
.el-input,
.el-cascader {
max-width: 100%;
}
.el-button {
width: 100%;
margin: 5px 0;
}
.el-button + .el-button {
margin-left: 0;
}
}
</style>
PolicyController
package com.example.demo.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.demo.entity.Policy;
import com.example.demo.service.PolicyService;
import com.example.demo.service.PolicyTypeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.Arrays;
import java.util.List;
@RestController
@RequestMapping("/policy")
public class PolicyController {
@Autowired
private PolicyService policyService;
@Autowired
private PolicyTypeService policyTypeService;
@GetMapping("/search")
public Page<Policy> search(
@RequestParam(defaultValue = "") String keyword,
@RequestParam(defaultValue = "1") Integer current,
@RequestParam(defaultValue = "10") Integer size) {
Page<Policy> page = new Page<>(current, size);
LambdaQueryWrapper<Policy> wrapper = new LambdaQueryWrapper<>();
// 模糊匹配名称、关键词或政策文本
wrapper.like(Policy::getName, keyword)
.or()
.like(Policy::getKeyword, keyword)
.or()
.like(Policy::getText, keyword);
return policyService.page(page, wrapper);
}
@GetMapping("/{id}")
public ResponseEntity<?> getById(@PathVariable Long id) {
Policy policy = policyService.getById(id);
if (policy == null) {
return ResponseEntity.notFound().build();
}
return ResponseEntity.ok(policy);
}
@GetMapping("/types")
public ResponseEntity<?> getTypeTree() {
return ResponseEntity.ok(policyTypeService.getTypeTree());
}
@GetMapping("/search/byType")
public Page<Policy> searchByType(
@RequestParam String type,
@RequestParam(defaultValue = "1") Integer current,
@RequestParam(defaultValue = "10") Integer size) {
Page<Policy> page = new Page<>(current, size);
LambdaQueryWrapper<Policy> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(Policy::getType, type);
return policyService.page(page, wrapper);
}
@GetMapping("/search/byTypes")
public Page<Policy> searchByTypes(
@RequestParam String types,
@RequestParam(defaultValue = "1") Integer current,
@RequestParam(defaultValue = "10") Integer size) {
List<String> typeList = Arrays.asList(types.split(","));
Page<Policy> page = new Page<>(current, size);
LambdaQueryWrapper<Policy> wrapper = new LambdaQueryWrapper<>();
wrapper.in(Policy::getType, typeList);
return policyService.page(page, wrapper);
}
@GetMapping("/advanced-search")
public Page<Policy> advancedSearch(
@RequestParam(required = false) String title,
@RequestParam(required = false) String content,
@RequestParam(required = false) String organ,
@RequestParam(required = false) String type,
@RequestParam(required = false) String document,
@RequestParam(defaultValue = "1") Integer current,
@RequestParam(defaultValue = "10") Integer size) {
Page<Policy> page = new Page<>(current, size);
LambdaQueryWrapper<Policy> wrapper = new LambdaQueryWrapper<>();
// 构建多条件查询
wrapper.like(title != null && !title.isEmpty(), Policy::getName, title)
.and(content != null && !content.isEmpty(),
w -> w.like(Policy::getText, content))
.and(organ != null && !organ.isEmpty(),
w -> w.like(Policy::getOrgan, organ))
.and(type != null && !type.isEmpty(),
w -> w.eq(Policy::getType, type))
.and(document != null && !document.isEmpty(),
w -> w.like(Policy::getDocument, document));
return policyService.page(page, wrapper);
}
}
浙公网安备 33010602011771号