update: 优化Search组件
This commit is contained in:
parent
5f6aa6ce65
commit
2a2a8121c3
|
@ -15,6 +15,7 @@
|
|||
"dependencies": {
|
||||
"@vitejs/plugin-vue-jsx": "^3.0.0",
|
||||
"@vuemap/vue-amap": "^1.1.20",
|
||||
"@vueuse/core": "^9.10.0",
|
||||
"ant-design-vue": "^3.2.15",
|
||||
"axios": "^1.2.1",
|
||||
"echarts": "^5.4.1",
|
||||
|
|
|
@ -5,4 +5,4 @@ export const getDeviceCount_api = () => server.get(`/device/instance/_count`);
|
|||
// 产品数量
|
||||
export const getProductCount_api = (data) => server.post(`/device-product/_count`, data);
|
||||
// 查询产品列表
|
||||
export const getProductList_api = (data) => server.get(`/device/product/_query/no-paging?paging=false`, data);
|
||||
export const getProductList_api = (data) => server.get(`/device/product/_query/no-paging?paging=false`, data);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div class='JSearch-item'>
|
||||
<div class='JSearch-item--type'>
|
||||
<div class='JSearch-item--type' v-if='expand'>
|
||||
<a-select
|
||||
v-if='index !== 1 && index !== 4'
|
||||
:options='typeOptions'
|
||||
|
@ -27,45 +27,55 @@
|
|||
<a-input
|
||||
v-if='component === componentType.input'
|
||||
v-model:value='termsModel.value'
|
||||
@change='(v) => valueChange(v)'
|
||||
/>
|
||||
<a-select
|
||||
v-else-if='component === componentType.select'
|
||||
v-model:value='termsModel.value'
|
||||
:options='options'
|
||||
@change='(v) => valueChange(v)'
|
||||
/>
|
||||
<a-inputnumber
|
||||
v-else-if='component === componentType.inputNumber'
|
||||
v-model:value='termsModel.value'
|
||||
@change='(v) => valueChange(v)'
|
||||
/>
|
||||
<a-input-password
|
||||
v-else-if='component === componentType.password'
|
||||
v-model:value='termsModel.value'
|
||||
@change='(v) => valueChange(v)'
|
||||
/>
|
||||
<a-switch
|
||||
v-else-if='component === componentType.switch'
|
||||
v-model:checked='termsModel.value'
|
||||
@change='(v) => valueChange(v)'
|
||||
/>
|
||||
<a-radio-group
|
||||
v-else-if='component === componentType.radio'
|
||||
v-model:value='termsModel.value'
|
||||
@change='(v) => valueChange(v)'
|
||||
/>
|
||||
<a-checkbox-group
|
||||
v-else-if='component === componentType.checkbox'
|
||||
v-model:value='termsModel.value'
|
||||
:options='options'
|
||||
@change='(v) => valueChange(v)'
|
||||
/>
|
||||
<a-time-picker
|
||||
v-else-if='component === componentType.time'
|
||||
v-model:value='termsModel.value'
|
||||
@change='(v) => valueChange(v)'
|
||||
/>
|
||||
<a-date-picker
|
||||
v-else-if='component === componentType.date'
|
||||
v-model:value='termsModel.value'
|
||||
@change='(v) => valueChange(v)'
|
||||
/>
|
||||
<a-tree-select
|
||||
v-else-if='component === componentType.tree'
|
||||
v-model:value='termsModel.value'
|
||||
:tree-data='options'
|
||||
@change='(v) => valueChange(v)'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -73,27 +83,99 @@
|
|||
|
||||
<script setup lang='ts' name='SearchItem'>
|
||||
import { componentType } from 'components/Form'
|
||||
import { typeOptions } from './util'
|
||||
import { typeOptions, termType } from './util'
|
||||
import { PropType } from 'vue'
|
||||
import type { SearchItemProps, SearchItemData } from './types'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
|
||||
type ItemDataProps = Omit<SearchItemData, 'title'>
|
||||
|
||||
interface Emit {
|
||||
(e: 'change', data: ItemDataProps): void
|
||||
}
|
||||
|
||||
const props = defineProps({
|
||||
component: {
|
||||
type: String,
|
||||
default: componentType.input
|
||||
columns: {
|
||||
type: Array as PropType<SearchItemProps[]>,
|
||||
default: () => [],
|
||||
required: true
|
||||
},
|
||||
index: {
|
||||
type: Number,
|
||||
default: 1
|
||||
},
|
||||
expand: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
|
||||
const termsModel = reactive({})
|
||||
const emit = defineEmits<Emit>()
|
||||
|
||||
const termsModel = reactive<ItemDataProps>({
|
||||
type: 'or',
|
||||
value: '',
|
||||
termType: 'eq',
|
||||
column: ''
|
||||
})
|
||||
|
||||
const component = ref(componentType.input)
|
||||
|
||||
const options = ref([])
|
||||
|
||||
const columnOptions = reactive([])
|
||||
const columnOptions = ref<({ label: string, value: string})[]>([])
|
||||
const columnOptionMap = new Map()
|
||||
|
||||
const termTypeOptions = reactive([])
|
||||
const termTypeOptions = reactive(termType)
|
||||
|
||||
const getTermType = (type: string) => {
|
||||
switch (type) {
|
||||
case 'select':
|
||||
case 'treeSelect':
|
||||
return 'eq'
|
||||
case 'date':
|
||||
case 'time':
|
||||
return 'gt'
|
||||
default:
|
||||
return 'like'
|
||||
}
|
||||
}
|
||||
|
||||
const handleItem = () => {
|
||||
columnOptionMap.clear()
|
||||
columnOptions.value = []
|
||||
if (!props.columns.length) return
|
||||
|
||||
// 获取第一个值
|
||||
|
||||
const sortColumn = cloneDeep(props.columns)
|
||||
sortColumn?.sort((a, b) => a.sortIndex! - b.sortIndex!)
|
||||
|
||||
const _index = props.index > sortColumn.length ? sortColumn.length - 1 : props.index
|
||||
const _itemColumn = sortColumn[_index - 1]
|
||||
|
||||
termsModel.column = _itemColumn.column
|
||||
termsModel.termType = _itemColumn.defaultTermType || getTermType(_itemColumn.type as string)
|
||||
|
||||
columnOptions.value = props.columns.map(item => {
|
||||
columnOptionMap.set(item.column, item)
|
||||
return {
|
||||
label: item.title,
|
||||
value: item.column
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const valueChange = (value: any) => {
|
||||
emit('change', {
|
||||
type: termsModel.type,
|
||||
value: termsModel.value,
|
||||
termType: termsModel.termType,
|
||||
column: termsModel.column,
|
||||
})
|
||||
}
|
||||
|
||||
handleItem()
|
||||
|
||||
</script>
|
||||
|
||||
|
@ -103,7 +185,7 @@ const termTypeOptions = reactive([])
|
|||
gap: 16px;
|
||||
|
||||
.JSearch-item--type {
|
||||
min-width: 120px;
|
||||
min-width: 80px;
|
||||
> span {
|
||||
line-height: 34px;
|
||||
font-weight: bold;
|
||||
|
@ -111,11 +193,11 @@ const termTypeOptions = reactive([])
|
|||
}
|
||||
|
||||
.JSearch-item--column {
|
||||
min-width: 120px;
|
||||
min-width: 100px;
|
||||
}
|
||||
|
||||
.JSearch-item--termType {
|
||||
min-width: 120px;
|
||||
min-width: 100px;
|
||||
}
|
||||
|
||||
.JSearch-item--value {
|
||||
|
|
|
@ -1,19 +1,74 @@
|
|||
<template>
|
||||
<div class='JSearch-content'>
|
||||
<div class='left'>
|
||||
<SearchItem :index='1' />
|
||||
<SearchItem :index='2' />
|
||||
<SearchItem :index='3' />
|
||||
<div class='JSearch-warp' ref='searchRef'>
|
||||
<!-- 高级模式 -->
|
||||
<div v-if='props.type === "advanced"' :class='["JSearch-content senior", expand ? "senior-expand" : "", screenSize ? "big" : "small"]'>
|
||||
<div :class='["JSearch-items", expand ? "items-expand" : "", layout]'>
|
||||
<div class='left'>
|
||||
<SearchItem :expand='expand' :index='1' :columns='searchItems' />
|
||||
<SearchItem v-if='expand' :expand='expand' :index='2' :columns='searchItems' />
|
||||
<SearchItem v-if='expand' :expand='expand' :index='3' :columns='searchItems' />
|
||||
</div>
|
||||
<div class='center' v-if='expand'>
|
||||
<a-select
|
||||
v-model:value='termType'
|
||||
class='center-select'
|
||||
:options='typeOptions'
|
||||
/>
|
||||
</div>
|
||||
<div class='right' v-if='expand'>
|
||||
<SearchItem :expand='expand' :index='4' :columns='searchItems' />
|
||||
<SearchItem :expand='expand' :index='5' :columns='searchItems' />
|
||||
<SearchItem :expand='expand' :index='6' :columns='searchItems' />
|
||||
</div>
|
||||
</div>
|
||||
<div :class='["JSearch-footer", expand ? "expand" : ""]'>
|
||||
<div class='JSearch-footer--btns'>
|
||||
<a-dropdown-button type="primary">
|
||||
搜索
|
||||
<template #overlay>
|
||||
<a-menu v-if='!!historyList.length'>
|
||||
<a-menu-item>
|
||||
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
<a-empty v-else />
|
||||
</template>
|
||||
<template #icon><SearchOutlined /></template>
|
||||
</a-dropdown-button>
|
||||
<a-button>
|
||||
<template #icon><PoweroffOutlined /></template>
|
||||
保存
|
||||
</a-button>
|
||||
<a-button>
|
||||
<template #icon><PoweroffOutlined /></template>
|
||||
重置
|
||||
</a-button>
|
||||
</div>
|
||||
<a-button type='link' class='more-btn' @click='expandChange'>
|
||||
更多筛选
|
||||
<DownOutlined :class='["more-icon",expand ? "more-up" : "more-down"]' />
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class='center'>
|
||||
<a-select
|
||||
:options='typeOptions'
|
||||
/>
|
||||
</div>
|
||||
<div class='right'>
|
||||
<SearchItem :index='4' />
|
||||
<SearchItem :index='5' />
|
||||
<SearchItem :index='6' />
|
||||
<!-- 简单模式 -->
|
||||
<div v-else class='JSearch-content simple big'>
|
||||
<div class='JSearch-items'>
|
||||
<div class='left'>
|
||||
<SearchItem :expand='false' :index='1' />
|
||||
</div>
|
||||
</div>
|
||||
<div class='JSearch-footer'>
|
||||
<div class='JSearch-footer--btns'>
|
||||
<a-button type="primary">
|
||||
<template #icon><SearchOutlined /></template>
|
||||
搜索
|
||||
</a-button>
|
||||
<a-button>
|
||||
<template #icon><PoweroffOutlined /></template>
|
||||
重置
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -21,6 +76,12 @@
|
|||
<script setup lang='ts' name='Search'>
|
||||
import SearchItem from './Item.vue'
|
||||
import { typeOptions } from './util'
|
||||
import { useElementSize } from '@vueuse/core'
|
||||
import { omit } from 'lodash-es'
|
||||
import { SearchOutlined, DownOutlined } from '@ant-design/icons-vue';
|
||||
import type { SearchItemProps } from './types'
|
||||
import { PropType } from 'vue'
|
||||
import { JColumnsProps } from 'components/Table/types'
|
||||
|
||||
const props = defineProps({
|
||||
defaultParams: {
|
||||
|
@ -28,14 +89,14 @@ const props = defineProps({
|
|||
default: () => ({})
|
||||
},
|
||||
columns: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
type: Array as PropType<JColumnsProps[]>,
|
||||
default: () => [],
|
||||
required: true
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: 'advanced'
|
||||
},
|
||||
|
||||
key: {
|
||||
type: String,
|
||||
default: '',
|
||||
|
@ -43,30 +104,153 @@ const props = defineProps({
|
|||
}
|
||||
})
|
||||
|
||||
const searchRef = ref(null)
|
||||
const { width } = useElementSize(searchRef)
|
||||
|
||||
// 是否展开更多筛选
|
||||
const expand = ref(false)
|
||||
|
||||
// 第一组,第二组的关系
|
||||
const termType = ref('or')
|
||||
// 搜索历史记录
|
||||
const historyList = ref([])
|
||||
|
||||
// 组件方向
|
||||
const layout = ref('horizontal')
|
||||
// 当前组件宽度 true 大于1000
|
||||
const screenSize = ref(true)
|
||||
|
||||
const searchItems = ref<SearchItemProps[]>([])
|
||||
|
||||
const expandChange = () => {
|
||||
expand.value = !expand.value
|
||||
}
|
||||
|
||||
const searchParams = reactive({
|
||||
data: {}
|
||||
})
|
||||
|
||||
const handleItems = () => {
|
||||
searchItems.value = []
|
||||
props.columns!.forEach((item, index) => {
|
||||
if (item.search && Object.keys(item.search).length) {
|
||||
searchItems.value.push({
|
||||
...omit(item.search, ['rename']),
|
||||
sortIndex: item.search.first ? 0 : index + 1,
|
||||
title: item.title,
|
||||
column: item.dataIndex,
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
watch(width, (value) => {
|
||||
if (value < 1000) {
|
||||
layout.value = 'vertical'
|
||||
screenSize.value = false
|
||||
} else {
|
||||
layout.value = 'horizontal'
|
||||
screenSize.value = true
|
||||
}
|
||||
})
|
||||
|
||||
handleItems()
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped lang='less'>
|
||||
.JSearch-content {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
.left, & .right {
|
||||
.JSearch-warp {
|
||||
padding: 24px;
|
||||
background-color: #fff;
|
||||
|
||||
.JSearch-content {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
flex-direction: column;
|
||||
width: 0;
|
||||
flex-grow: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
gap: 12px;
|
||||
|
||||
.JSearch-items,& .JSearch-footer {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.JSearch-items {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
|
||||
.left, & .right {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
flex-direction: column;
|
||||
width: 0;
|
||||
flex-grow: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.center {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
min-width: 80px;
|
||||
}
|
||||
|
||||
&.vertical {
|
||||
flex-direction: column;
|
||||
.left,& .right,& .center {
|
||||
width: 100%;
|
||||
}
|
||||
.center {
|
||||
flex-direction: row;
|
||||
}
|
||||
.center-select {
|
||||
width: 120px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.JSearch-footer {
|
||||
display: flex;
|
||||
gap: 64px;
|
||||
position: relative;
|
||||
|
||||
&.expand {
|
||||
margin-top: 12px;
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
|
||||
.center {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
flex-basis: 120px;
|
||||
.more-btn {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.JSearch-footer--btns {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
&.senior-expand {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.more-up {
|
||||
transform: rotate(-180deg);
|
||||
}
|
||||
|
||||
&.big {
|
||||
gap: 64px;
|
||||
}
|
||||
|
||||
&.small {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
&.simple {
|
||||
.JSearch-items {
|
||||
flex-grow: 4;
|
||||
}
|
||||
.JSearch-footer {
|
||||
flex-grow: 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,21 @@
|
|||
export interface SearchProps {
|
||||
rename?: string
|
||||
type?: 'select' | 'number' | 'string' | 'treeSelect' | 'date' | 'time'
|
||||
format?: string
|
||||
options?: any[] | Function
|
||||
first?: boolean
|
||||
defaultTermType?: string // 默认 eq
|
||||
title?: ColumnType.title
|
||||
sortIndex?: number
|
||||
}
|
||||
|
||||
export interface SearchItemData {
|
||||
column: ColumnType.dataIndex
|
||||
rename?: string
|
||||
value: any
|
||||
termType: string
|
||||
type?: string
|
||||
title: string
|
||||
}
|
||||
|
||||
export interface SearchItemProps extends SearchProps, SearchItemData {}
|
|
@ -1,4 +1,17 @@
|
|||
export const typeOptions = [
|
||||
{ label: '或者', value: 'or' },
|
||||
{ label: '并且', value: 'and' },
|
||||
]
|
||||
]
|
||||
|
||||
export const termType = [
|
||||
{ label: '=', value: 'eq' },
|
||||
{ label: '!=', value: 'not' },
|
||||
{ label: '包含', value: 'like' },
|
||||
{ label: '不包含', value: 'nlike' },
|
||||
{ label: '>', value: 'gt' },
|
||||
{ label: '>=', value: 'gte' },
|
||||
{ label: '<', value: 'lt' },
|
||||
{ label: '<=', value: 'lte' },
|
||||
{ label: '属于', value: 'in' },
|
||||
{ label: '不属于', value: 'nin' },
|
||||
];
|
|
@ -0,0 +1,7 @@
|
|||
import { SearchItemProps } from 'components/Search/types'
|
||||
import { ColumnType } from 'ant-design-vue/es/table'
|
||||
|
||||
export interface JColumnsProps extends ColumnType{
|
||||
scopedSlots?: boolean;
|
||||
search: SearchItemProps
|
||||
}
|
|
@ -15,7 +15,7 @@ const filterPath = [
|
|||
|
||||
router.beforeEach((to, from, next) => {
|
||||
const token = LocalStore.get(TOKEN_KEY)
|
||||
|
||||
// TODO 切换路由取消请求
|
||||
if (token || filterPath.includes(to.path)) {
|
||||
next()
|
||||
} else {
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import { TOKEN_KEY } from '@/utils/variable'
|
||||
|
||||
/**
|
||||
* 静态图片资源处理
|
||||
* @param path {String} 路径
|
||||
|
@ -29,4 +31,8 @@ export const LocalStore = {
|
|||
removeAll() {
|
||||
localStorage.clear()
|
||||
}
|
||||
}
|
||||
|
||||
export const getToken = () => {
|
||||
return LocalStore.get(TOKEN_KEY)
|
||||
}
|
|
@ -1,11 +1,56 @@
|
|||
<template>
|
||||
<div class='search'>
|
||||
<Search />
|
||||
<Search
|
||||
:columns='columns'
|
||||
/>
|
||||
<Search type='simple' />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup name='demoSearch'>
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: '名称',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
search: {
|
||||
rename: 'deviceId',
|
||||
type: 'select',
|
||||
handValue: (v) => {
|
||||
return '123'
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'ID',
|
||||
dataIndex: 'id',
|
||||
key: 'id',
|
||||
scopedSlots: true,
|
||||
search: {
|
||||
type: 'string',
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '分类',
|
||||
dataIndex: 'classifiedName',
|
||||
key: 'classifiedName',
|
||||
search: {
|
||||
first: true,
|
||||
type: 'treeSelect',
|
||||
// options: async () => {
|
||||
// return await
|
||||
// }
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
fixed: 'right',
|
||||
width: 250,
|
||||
scopedSlots: true,
|
||||
}
|
||||
]
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
|
Loading…
Reference in New Issue