Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions docs/demos/ma-form/conditional-rendering/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -765,8 +765,6 @@ const handleClear = () => {
<style scoped>
.conditional-rendering-demo {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}

.demo-description {
Expand Down
197 changes: 140 additions & 57 deletions docs/demos/ma-pro-table-examples/advanced-search/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -21,21 +21,79 @@ const formData = reactive({
// 部门,多选
department: [],
// 薪资范围
salaryMin: 0,
salaryMax: 10,
salaryMin: undefined,
salaryMax: undefined,
// 工作经验
experience: [0, 15], // 如果 slider range 默认值
// 入职时间范围
joinDateRange: [],
// 职级,多选
level: [],
// 绩效评分
performanceMin: 1,
performanceMax: 2,
performanceMin: undefined,
performanceMax: undefined,
// 在职状态
status: '',
})

// 通用过滤方法
function filterData(data: any[], params: any) {
return data.filter(item => {
return Object.entries(params).every(([key, value]) => {
if (value == null || value === '' || (Array.isArray(value) && value.length === 0)) {
return true // 跳过空参数
}

switch (key) {
case 'name': // 模糊匹配
return item.name.includes(value)

case 'department': // 多选匹配
return Array.isArray(value) && value.some((dep: string) => item.department.includes(dep))

case 'salaryMin':
return item.salary >= value
case 'salaryMax':
return item.salary <= value

case 'experience': // 工作经验范围
if (Array.isArray(value) && value.length === 2) {
const [min, max] = value
return item.experience >= min && item.experience <= max
}
return true

case 'joinDateRange': // 入职时间范围
if (Array.isArray(value) && value.length === 2) {
const [startDate, endDate] = value
const itemDate = new Date(item.joinDate)
const start = new Date(startDate)
const end = new Date(endDate)
return itemDate >= start && itemDate <= end
}
return true

case 'level': // 职级多选
if (Array.isArray(value) && value.length > 0) {
return value.includes(item.level)
}
return true

case 'performanceMin':
return item.performance >= value
case 'performanceMax':
return item.performance <= value

case 'status': // 精确匹配
return item.status === value

default:
return true
}
})
})
}
Comment on lines +39 to +95
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

filterData 边界更健壮:经验区间乱序、日期比较的时区问题

  • 经验区间若用户拖拽导致 [max, min] 乱序会误判。
  • 日期用 new Date('YYYY-MM-DD') 存在跨时区歧义;给定 value-format 为 YYYY-MM-DD,可直接按字符串比较。
       case 'experience': // 工作经验范围
-          if (Array.isArray(value) && value.length === 2) {
-            const [min, max] = value
-            return item.experience >= min && item.experience <= max
-          }
+          if (Array.isArray(value) && value.length === 2) {
+            const [a, b] = value
+            const min = Math.min(a, b)
+            const max = Math.max(a, b)
+            return item.experience >= min && item.experience <= max
+          }
           return true
@@
-      case 'joinDateRange': // 入职时间范围
-          if (Array.isArray(value) && value.length === 2) {
-            const [startDate, endDate] = value
-            const itemDate = new Date(item.joinDate)
-            const start = new Date(startDate)
-            const end = new Date(endDate)
-            return itemDate >= start && itemDate <= end
-          }
+      case 'joinDateRange': // 入职时间范围(YYYY-MM-DD 可用字符串比较避免时区问题)
+          if (Array.isArray(value) && value.length === 2) {
+            const [startDate, endDate] = value
+            const date10 = (s: string) => s.slice(0, 10)
+            const d = date10(item.joinDate)
+            return d >= date10(startDate) && d <= date10(endDate)
+          }
           return true
🤖 Prompt for AI Agents
In docs/demos/ma-pro-table-examples/advanced-search/index.vue around lines 39 to
95, make the filterData logic more robust by normalizing the experience and date
ranges: for the 'experience' case, accept arrays in any order by deriving min =
Math.min(...value) and max = Math.max(...value) (also guard for non-array or
missing values) and then compare item.experience >= min && item.experience <=
max; for the 'joinDateRange' case, avoid timezone ambiguity with new
Date('YYYY-MM-DD') by either comparing the provided YYYY-MM-DD strings directly
(since value-format is YYYY-MM-DD) using string comparison
item.joinDate.slice(0,10) >= startDate && item.joinDate.slice(0,10) <= endDate,
or normalize both sides to UTC datetimes (e.g., append 'T00:00:00Z') before
creating Date objects; ensure these checks remain inclusive and still return
true when value is invalid or missing.


// 模拟 API 接口
const getAdvancedList = async (params: any) => {
console.log('高级搜索参数:', params)
Expand Down Expand Up @@ -114,13 +172,15 @@ const getAdvancedList = async (params: any) => {
}
]

const filtered = filterData(data, params)

return new Promise((resolve) => {
setTimeout(() => {
resolve({
code: 200,
data: {
list: data,
total: data.length
list: filtered,
total: filtered.length
}
})
}, 800)
Expand All @@ -141,11 +201,15 @@ const options = reactive<MaProTableOptions>({
pageSize: 10
}
},
searchFormOptions: { labelWidth: '90px' },
searchOptions: {
fold: true,
cols: { xs: 2, sm: 2, md: 2, lg: 2, xl: 2 },
text: {
searchBtn: () => '高级搜索',
resetBtn: () => '清空条件'
searchBtn: () => '立即搜索',
resetBtn: () => '重置条件',
isFoldBtn: () => '展开更多条件',
notFoldBtn: () => '收起条件'
}
},
header: {
Expand All @@ -159,12 +223,32 @@ const options = reactive<MaProTableOptions>({
},
onSearchReset: (form: Record<string, any>) => {
ElMessage.info('搜索条件已重置')

// 重置表单数据到初始状态
Object.assign(formData, {
name: '',
department: [],
salaryMin: undefined,
salaryMax: undefined,
experience: [0, 15],
joinDateRange: [],
level: [],
performanceMin: undefined,
performanceMax: undefined,
status: '',
})

// 重新请求数据
if (tableRef.value) {
tableRef.value.refresh()
}

return form
}
})

// 表格架构
const schema = reactive<MaProTableSchema>({
const schema = ref<MaProTableSchema>({
searchItems: [
{
label: '姓名',
Expand Down Expand Up @@ -196,34 +280,34 @@ const schema = reactive<MaProTableSchema>({
{
label: '薪资范围',
prop: 'salaryRange',
render: ({ formData }: any) => (
<div style="display: flex; gap: 8px; align-items: center;">
<el-input-number
v-model={formData.salaryMin}
placeholder="最低薪资"
min={0}
max={100000}
controls-position="right"
style="width: 140px;"
/>
<span>-</span>
<el-input-number
v-model={formData.salaryMax}
placeholder="最高薪资"
min={0}
max={100000}
controls-position="right"
style="width: 140px;"
/>
</div>
),
span: 2
render: () => <div class="!p-0 flex gap-2 w-full isPadding" />,
children: [
{
prop: 'salaryMin',
render: 'InputNumber',
renderProps: {
controlsPosition: 'right',
placeholder: '最低薪资',
},
cols: { md: 12, xs: 24 },
},
{
prop: 'salaryMax',
render: 'InputNumber',
renderProps: {
controlsPosition: 'right',
placeholder: '最高薪资',
},
cols: { md: 12, xs: 24 },
},
],
},
{
label: '工作经验',
prop: 'experience',
render: 'slider',
renderProps: {
size: 'small',
min: 0,
max: 15,
range: true,
Expand Down Expand Up @@ -266,28 +350,27 @@ const schema = reactive<MaProTableSchema>({
{
label: '绩效评分',
prop: 'performanceRange',
render: ({ formData }: any) => (
<div style="display: flex; gap: 8px; align-items: center;">
<el-input-number
v-model={formData.performanceMin}
placeholder="最低分"
min={0}
max={100}
controls-position="right"
style="width: 120px;"
/>
<span>-</span>
<el-input-number
v-model={formData.performanceMax}
placeholder="最高分"
min={0}
max={100}
controls-position="right"
style="width: 120px;"
/>
</div>
),
span: 2
render: () => <div class="!p-0 flex gap-2 w-full isPadding" />,
children: [
{
prop: 'performanceMin',
render: 'InputNumber',
renderProps: {
controlsPosition: 'right',
placeholder: '最低分',
},
cols: { md: 12, xs: 24 },
},
{
prop: 'performanceMax',
render: 'InputNumber',
renderProps: {
controlsPosition: 'right',
placeholder: '最高分',
},
cols: { md: 12, xs: 24 },
},
],
},
{
label: '在职状态',
Expand Down Expand Up @@ -434,10 +517,6 @@ const schema = reactive<MaProTableSchema>({
</script>

<style scoped>
.demo-advanced-search {
padding: 20px;
}

.demo-advanced-search h3 {
margin-bottom: 8px;
color: #333;
Expand All @@ -448,4 +527,8 @@ const schema = reactive<MaProTableSchema>({
color: #666;
font-size: 14px;
}

:deep(.isPadding > div) {
padding-left: 0 !important;
}
</style>
61 changes: 40 additions & 21 deletions docs/demos/ma-pro-table-examples/basic/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<div class="demo-basic">
<h3>基础用法</h3>
<p>最简单的表格使用方式,包含搜索、分页和基本操作功能。</p>

<MaProTable ref="tableRef" :options="options" :schema="schema" />
</div>
</template>
Expand All @@ -15,28 +15,33 @@ import { ElMessage, ElTag } from 'element-plus'
const tableRef = ref<MaProTableExpose>()

// 模拟 API 接口
const getBasicList = async (params: any) => {
console.log('请求参数:', params)

// 模拟数据
const getBasicList = async (params: Record<string, any>) => {
const data = [
{ id: 1, name: '张三', department: '技术部', position: '前端工程师', salary: 15000, status: 1, createTime: '2024-01-15' },
{ id: 2, name: '李四', department: '产品部', position: '产品经理', salary: 18000, status: 1, createTime: '2024-01-16' },
{ id: 3, name: '王五', department: '设计部', position: 'UI设计师', salary: 12000, status: 0, createTime: '2024-01-17' },
{ id: 4, name: '赵六', department: '技术部', position: '后端工程师', salary: 16000, status: 1, createTime: '2024-01-18' },
{ id: 5, name: '孙七', department: '运营部', position: '运营专员', salary: 10000, status: 1, createTime: '2024-01-19' }
]

return new Promise((resolve) => {
setTimeout(() => {
resolve({
code: 200,
data: {
list: data,
total: data.length
}
})
}, 500)

// 获取搜索字段列表
const searchFields = schema.searchItems.map(item => item.prop)

// 只过滤搜索字段
const filtered = data.filter(item => {
return searchFields.every(key => {
const value = params[key]
if (!value) return true
return item[key]?.toString().includes(value)
})
})

return Promise.resolve({
code: 200,
data: {
list: filtered,
total: filtered.length
}
})
}

Expand Down Expand Up @@ -84,6 +89,20 @@ const schema = reactive<MaProTableSchema>({
{ label: '运营部', value: '运营部' }
]
}
},
{
label: '职位',
prop: 'position',
render: 'select',
renderProps: {
options: [
{ label: '前端工程师', value: '前端工程师' },
{ label: '产品经理', value: '产品经理' },
{ label: 'UI设计师', value: 'UI设计师' },
{ label: '后端工程师', value: '后端工程师' },
{ label: '运营专员', value: '运营专员' }
]
}
}
],
tableColumns: [
Expand All @@ -92,13 +111,13 @@ const schema = reactive<MaProTableSchema>({
{ label: '部门', prop: 'department', width: 120 },
{ label: '职位', prop: 'position', width: 150 },
{ label: '薪资', prop: 'salary', width: 120, formatter: (row: any) => `¥${row.salary.toLocaleString()}` },
{
label: '状态',
{
label: '状态',
prop: 'status',
cellRender: ({ row }) => (
<ElTag type={row.status === 1 ? 'success' : 'danger'}>
{row.status === 1 ? '在职' : '离职'}
</ElTag>
<ElTag type={row.status === 1 ? 'success' : 'danger'}>
{row.status === 1 ? '在职' : '离职'}
</ElTag>
),
},
{ label: '入职时间', prop: 'createTime', width: 150 },
Expand Down
Loading
Loading