-
Notifications
You must be signed in to change notification settings - Fork 23
fix: 修复MaSearch、MaProTable部分错误! #38
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
@westng is attempting to deploy a commit to the deathsatan's projects Team on Vercel. A member of the Team first needs to authorize it. |
❌ Deploy Preview for super-cobbler-53ff65 failed. Why did it fail? →
|
Walkthrough本次变更主要在文档示例中将字符串/slots 渲染切换为 TSX/JSX 与 renderProps 驱动的配置,精简 VitePress 主题样式导入,仅保留 var.css 与 element.css;多处 demo 调整表单/搜索项选项来源与布局样式,并更新依赖 @mineadmin/form 至 ^1.0.53。 Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor U as 用户
participant MS as MaSearch
participant Form as Form(Model)
participant Table as 表格
U->>MS: 修改搜索条件/点击搜索
MS->>Form: 更新 formData
MS->>Table: 触发查询事件(携带 formData)
Table-->>U: 渲染查询结果
note over MS,Form: 渲染由 renderProps/TSX 驱动
sequenceDiagram
autonumber
actor U as 用户
participant MF as MaForm Demo(loading-states)
participant API as Mock API
participant Q as operationQueue(安全包装)
U->>MF: 选择国家/省份
MF->>Q: 记录加载操作(安全读取)
MF->>API: 请求城市列表
API-->>MF: 返回城市数据或空
MF->>MF: 更新 cityOptions(安全读取渲染)
MF-->>U: 城市下拉/占位符联动更新
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
✨ Finishing Touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (6)
docs/demos/ma-form/conditional-rendering/index.vue (1)
304-309: 修正 disabled-date 比较逻辑,避免“今天”被误禁用当前使用
date < new Date()会因时间部分导致当天被禁用。应以日期粒度比较。- 'disabled-date': (date: Date) => date < new Date() + 'disabled-date': (date: Date) => { + const today = new Date() + today.setHours(0, 0, 0, 0) + const d = new Date(date) + d.setHours(0, 0, 0, 0) + return d < today + }docs/demos/ma-form/layout-systems/index.vue (1)
4-4: 错误的类型导入来源:应从 @mineadmin/form 获取 MaFormItem/Options当前包依赖中没有 @mineadmin/components,类型导入将报错。
-import type { MaFormItem, MaFormOptions } from '@mineadmin/components' +import type { MaFormItem, MaFormOptions } from '@mineadmin/form'docs/demos/ma-form/dynamic-validation/index.vue (1)
624-651: TSX 下请使用 PascalCase 组件标签,否则可能渲染为原生/自定义元素本文件已切换到 lang="tsx",但仍使用
<el-checkbox />。在 JSX/TSX 中应使用<ElCheckbox />且需要确保可解析(显式导入或已全局注册)。否则校验勾选项将无法工作或样式异常。建议修复示例(并补充导入):
- import { ElMessage, ElNotification } from 'element-plus' + import { ElMessage, ElNotification, ElCheckbox } from 'element-plus' - render: () => <el-checkbox />, + render: () => <ElCheckbox />, - render: () => <el-checkbox />, + render: () => <ElCheckbox />,Also applies to: 2-4
docs/demos/ma-pro-table-examples/advanced-search/index.vue (1)
199-218: TSX 中的组件标签与属性需使用 PascalCase/驼峰;当前多处为连字符写法,存在运行时/类型风险
<el-input-number>、<el-date-picker>、<el-checkbox-group>、<el-checkbox>、<el-radio-group>、<el-radio>、<el-progress>、<el-tag>以及属性controls-position、start-placeholder、value-format、stroke-width、text-inside等应改为 PascalCase 组件与驼峰属性;并确保已导入或可被自动导入。示例修复片段(其余同理批量替换):
- import { ElMessage, ElTag } from 'element-plus' + import { + ElMessage, ElTag, + ElInputNumber, ElDatePicker, ElCheckboxGroup, ElCheckbox, + ElRadioGroup, ElRadio, ElProgress + } from 'element-plus' - <el-input-number v-model={formData.salaryMin} controls-position="right" /> + <ElInputNumber v-model={formData.salaryMin} controlsPosition="right" /> - <el-date-picker v-model={formData.joinDateRange} value-format="YYYY-MM-DD" /> + <ElDatePicker v-model={formData.joinDateRange} valueFormat="YYYY-MM-DD" /> - <el-checkbox-group v-model={formData.level}> - <el-checkbox label="P4" value="P4" /> + <ElCheckboxGroup v-model={formData.level}> + <ElCheckbox label="P4" value="P4" /> ... - </el-checkbox-group> + </ElCheckboxGroup> - <el-radio-group v-model={formData.status}> - <el-radio label="" value="">全部</el-radio> + <ElRadioGroup v-model={formData.status}> + <ElRadio label="" value="">全部</ElRadio> ... - </el-radio-group> + </ElRadioGroup> - <el-progress :percentage="row.performance" text-inside stroke-width={8} /> + <ElProgress percentage={row.performance} textInside strokeWidth={8} /> - {row.skills?.map((s, i) => <el-tag key={i} size="small">{s}</el-tag>)} + {row.skills?.map((s, i) => <ElTag key={i} size="small">{s}</ElTag>)}Also applies to: 242-250, 256-264, 270-288, 296-302, 358-363, 333-339, 13-14
docs/demos/ma-search/responsive-layout/index.vue (1)
83-90: 类型仅用于注解时使用import type并修复 SSR 下对 window 的直接访问
MaFormOptions仅作类型使用,应改为import type,避免打包时引入无用运行时代码。const windowWidth = ref(window.innerWidth)在 VitePress 构建(SSR/SSG)阶段会抛window 未定义,需做守卫。-import { MaFormOptions } from "@mineadmin/form"; +import type { MaFormOptions } from "@mineadmin/form"; -const windowWidth = ref(window.innerWidth) +const windowWidth = ref(typeof window !== 'undefined' ? window.innerWidth : 0)同时建议在挂载/卸载时也做环境判断(可按需调整,不强制):
onMounted(() => { - window.addEventListener('resize', updateWindowWidth) + if (typeof window !== 'undefined') window.addEventListener('resize', updateWindowWidth) }) onUnmounted(() => { - window.removeEventListener('resize', updateWindowWidth) + if (typeof window !== 'undefined') window.removeEventListener('resize', updateWindowWidth) })docs/demos/ma-search/table-integration/index.vue (1)
345-351: 修正排序比较器(相等时应返回 0),并对日期列使用时间戳排序现有比较器在相等时返回 -1,会造成不稳定排序;且 created_at 为本地化字符串,直接比较会错乱。建议:
- 相等返回 0;
- 当排序字段为 created_at 时,改用 createdAtTs(见下方 mockData 补丁)。
- filteredData.sort((a, b) => { - const aVal = a[sortParams.value.prop!] - const bVal = b[sortParams.value.prop!] - const result = aVal > bVal ? 1 : -1 - return sortParams.value.order === 'ascending' ? result : -result - }) + filteredData.sort((a, b) => { + const prop = sortParams.value.prop! + let aVal = prop === 'created_at' ? a.createdAtTs : a[prop] + let bVal = prop === 'created_at' ? b.createdAtTs : b[prop] + const result = aVal === bVal ? 0 : (aVal > bVal ? 1 : -1) + return sortParams.value.order === 'ascending' ? result : -result + })
🧹 Nitpick comments (41)
docs/demos/ma-search/form-validation/index.vue (2)
361-369: 复用已有校验器并对输入做 trim,避免重复与空格误判此处内联了邮箱/手机正则,和上方已有的
validateEmail/validatePhone重复;同时未对字符串做trim,末尾空格会被误判为格式错误。建议复用既有校验器,并统一对输入做裁剪。- validator: (rule: any, value: any, callback: any) => { - if (!value) { - callback() - return - } - - const formData = conditionalValidationRef.value?.getSearchForm() || {} - const queryType = formData.query_type - - if (queryType === 'email') { - const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/ - if (!emailRegex.test(value)) return callback(new Error('请输入正确的邮箱格式')) - } else if (queryType === 'phone') { - if (!/^1[3-9]\d{9}$/.test(value)) return callback(new Error('请输入正确的手机号格式')) - } else if (queryType === 'username') { - if (value.length < 3 || value.length > 20) return callback(new Error('用户名长度在3-20个字符')) - } - callback() - }, + validator: (rule: any, value: any, callback: any) => { + const v = String(value ?? '').trim() + if (!v) return callback() + + const formData = conditionalValidationRef.value?.getSearchForm() || {} + const queryType = formData.query_type + + if (queryType === 'email') { + return validateEmail(rule, v, callback) + } else if (queryType === 'phone') { + return validatePhone(rule, v, callback) + } else if (queryType === 'username') { + if (v.length < 3 || v.length > 20) return callback(new Error('用户名长度在3-20个字符')) + } + return callback() + },
349-373: 当查询类型切换时应联动重新校验 query_value当前仅在 blur 触发,切换「查询类型」(radio change) 不会自动重新执行此校验,可能保留过期的错误/成功态。建议把触发条件同时包含 change,或在 query_type 的 change 后手动
validateField('query_value')。- trigger: 'blur' + trigger: ['blur', 'change']docs/demos/ma-form/conditional-rendering/index.vue (2)
322-325: 将复选框文案放入插槽,避免仅设置 props 不渲染文本Element Plus 单选/复选通常通过默认插槽展示文案;仅传 label 可能不显示文本或语义不清。
- render: () => <el-checkbox />, - renderProps: { - label: '需要填写详细地址' - }, + render: () => <el-checkbox>需要填写详细地址</el-checkbox>,
391-396: 移除多余的 value 属性,Element Plus Radio 使用 label 作为取值避免无效 prop 触发运行时告警。
- <el-radio-group> - <el-radio label="bank" value="bank">银行卡</el-radio> - <el-radio label="alipay" value="alipay">支付宝</el-radio> - </el-radio-group> + <el-radio-group> + <el-radio label="bank">银行卡</el-radio> + <el-radio label="alipay">支付宝</el-radio> + </el-radio-group>docs/demos/ma-form/layout-systems/index.vue (2)
70-78: 选项改为 renderProps.options 的写法可读性更好,建议抽取常量去重三处地区选项重复,可提取到顶层常量,便于复用与后续 i18n。
可在 <script setup> 顶部增加:
const regionOptions = { country: [ { label: '中国', value: 'china' }, { label: '美国', value: 'usa' }, { label: '日本', value: 'japan' }, { label: '韩国', value: 'korea' }, ], province: [ { label: '北京', value: 'beijing' }, { label: '上海', value: 'shanghai' }, { label: '广东', value: 'guangdong' }, { label: '江苏', value: 'jiangsu' }, ], city: [ { label: '北京', value: 'beijing' }, { label: '上海', value: 'shanghai' }, { label: '深圳', value: 'shenzhen' }, { label: '杭州', value: 'hangzhou' }, ], }并分别使用
renderProps: { options: regionOptions.country }等。Also applies to: 84-92, 98-106
152-169: TSX 复选渲染 OK,建议将选项数组提升为常量,避免每次 render 新建数组微优化,可提升可读性并减少无谓创建。
- render: () => { - return ( - <el-checkbox-group> - {[ - { label: '阅读', value: 'reading' }, - { label: '编程', value: 'coding' }, - { label: '运动', value: 'sports' }, - { label: '音乐', value: 'music' }, - { label: '旅行', value: 'travel' }, - { label: '摄影', value: 'photography' } - ].map((item) => ( - <el-checkbox key={item.value} label={item.value}> - {item.label} - </el-checkbox> - ))} - </el-checkbox-group> - ) - }, + render: () => ( + <el-checkbox-group> + {hobbyOptions.map((item) => ( + <el-checkbox key={item.value} label={item.value}>{item.label}</el-checkbox> + ))} + </el-checkbox-group> + ),在文件顶部新增(支持上述 diff):
const hobbyOptions = [ { label: '阅读', value: 'reading' }, { label: '编程', value: 'coding' }, { label: '运动', value: 'sports' }, { label: '音乐', value: 'music' }, { label: '旅行', value: 'travel' }, { label: '摄影', value: 'photography' }, ]docs/demos/ma-form/basic-usage/index.vue (2)
135-151: TSX 复选渲染 OK,建议提升选项为常量复用与其他 demo 统一,避免在 render 中创建字面量数组。
- render: () => { - return ( - <el-checkbox-group> - {[ - { label: '阅读', value: 'reading' }, - { label: '编程', value: 'coding' }, - { label: '运动', value: 'sports' }, - { label: '音乐', value: 'music' }, - { label: '旅行', value: 'travel' }, - { label: '摄影', value: 'photography' } - ].map((item) => ( - <el-checkbox key={item.value} label={item.value}> - {item.label} - </el-checkbox> - ))} - </el-checkbox-group> - ) - }, + render: () => ( + <el-checkbox-group> + {hobbyOptions.map((item) => ( + <el-checkbox key={item.value} label={item.value}>{item.label}</el-checkbox> + ))} + </el-checkbox-group> + ),在脚本顶部补充:
const hobbyOptions = [ { label: '阅读', value: 'reading' }, { label: '编程', value: 'coding' }, { label: '运动', value: 'sports' }, { label: '音乐', value: 'music' }, { label: '旅行', value: 'travel' }, { label: '摄影', value: 'photography' }, ]
168-171: 与 Element Plus 习惯一致:将文案放到插槽中避免仅通过 props.label 传文案可能不显示的问题。
- render: () => <el-checkbox />, - renderProps: { - label: '我已阅读并同意用户协议' - }, + render: () => <el-checkbox>我已阅读并同意用户协议</el-checkbox>,docs/demos/ma-search/methods-demo/index.vue (1)
520-524: renderProps 直接给数组:请确认 API 一致性此处将下拉选项直接赋给 renderProps(数组),而文件上方“状态/部门”仍使用 renderProps.options(对象)。如果 @mineadmin/search 新版同时支持两种写法则无碍;若仅保留其一,建议统一,避免示例混淆。
可选统一方案(若仅支持对象写法):
- renderProps: [ - { label: '全部', value: '' }, - { label: '活跃', value: 'active' }, - { label: '非活跃', value: 'inactive' } - ] + renderProps: { + options: [ + { label: '全部', value: '' }, + { label: '活跃', value: 'active' }, + { label: '非活跃', value: 'inactive' } + ] + }docs/demos/ma-pro-table-examples/basic/index.vue (1)
97-102: 用 cellRender + ElTag 直出状态:实现清晰,可考虑提取映射当前写法直观。为便于在多处示例复用,可选提取映射函数或常量:
- 状态值到文本:{1: '在职', 0: '离职'}
- 状态值到 tag 类型:{1: 'success', 0: 'danger'}
docs/demos/ma-form/loading-states/index.vue (3)
56-60: 多处“空值防御”冗余,可精简operationQueue/cityOptions 初始即为 ref([]),再做多处 if (!xxx.value) 置空数组意义不大,还引入维护成本。可删除这些重复防御,保留 safe* 计算属性用于模板解包即可。
应用示例 diff:
-// 确保 operationQueue 始终是一个数组 -if (!operationQueue.value) { - operationQueue.value = [] -} ... - if (!operationQueue.value) { - operationQueue.value = [] - } ... -// 确保 cityOptions 始终是一个数组 -if (!cityOptions.value) { - cityOptions.value = [] -} ... -onMounted(() => { - nextTick(() => { - // 确保所有 ref 都被正确初始化 - if (!operationQueue.value) { - operationQueue.value = [] - } - if (!cityOptions.value) { - cityOptions.value = [] - } - }) -})Also applies to: 98-100, 124-128, 615-625
309-313: renderProps.options 传入 computed:请确认组件是否解包响应式引用若 @mineadmin/form 的 select 对 options 支持直接传入 Ref/Computed,则可保持现状;若只接受数组/函数,建议改为函数以确保更新时机清晰。
- options: computed(() => safeCityOptions.value.map(city => ({ - label: city, - value: city - }))) + options: () => safeCityOptions.value.map(city => ({ + label: city, + value: city + }))
578-580: 避免直接变异 computed 的值对 safeOperationQueue(computed)做 splice 易读性差。建议直接变异源 ref。
- if (safeOperationQueue.value) { - safeOperationQueue.value.splice(0) - } + operationQueue.value.splice(0)docs/demos/ma-form/dynamic-validation/index.vue (4)
973-979: scoped 样式未命中子组件内部元素,需使用 :deep
.status-message .el-icon在<style scoped>下通常不会命中 Element Plus 组件内部结构。请使用:deep()以确保样式生效。-.status-message .el-icon { +.status-message :deep(.el-icon) { font-size: 14px; } -.status-message .el-icon.is-loading { +.status-message :deep(.el-icon.is-loading) { animation: rotating 2s linear infinite; }
758-769: 确认图标组件的注册方式(编译期自动导入或全局注册)模板中使用了
<Loading /> / <SuccessFilled /> / <CircleCloseFilled /> / <WarningFilled />。若无自动导入或全局注册,需从@element-plus/icons-vue显式导入并注册。如需,我可给出最小导入与注册示例。
122-127: renderProps 中的show-password命名与类型一致性
- 对非密码输入(如“用户名”)无需传
show-password。- TS/JSX 生态下更推荐使用驼峰
showPassword,避免中划线命名在运行时不被识别的风险(取决于封装实现)。- 'show-password': false + // 非密码输入,无需该属性 - 'show-password': true + showPassword: trueAlso applies to: 210-216, 251-256
305-335: 动态校验规则里套computed(...).value可读性与开销在
formItems的 computed 中再次创建computed(...).value每次都会生成新计算属性实例。可直接基于formData计算数组或抽成纯函数,逻辑更直观且避免多余依赖层。如需,我可以提供三个规则段的等价精简写法。
Also applies to: 395-418, 604-614
docs/demos/ma-pro-table-examples/advanced-search/index.vue (2)
311-312: 列 formatter 建议容错
row.department.join(' / ')在后端返回空/非数组时会抛错。建议加个兜底。- formatter: (row: any) => row.department.join(' / ') + formatter: (row: any) => Array.isArray(row.department) ? row.department.join(' / ') : '-'
293-302: “在职状态”表单项的值类型请保持一致当前 options/单选混用
'' | 0 | 1,可能导致筛选逻辑/序列化不一致。建议统一为字符串或数字一种。若统一为字符串:
- <ElRadio label={1} value={1}>在职</ElRadio> - <ElRadio label={0} value={0}>离职</ElRadio> + <ElRadio label="1" value="1">在职</ElRadio> + <ElRadio label="0" value="0">离职</ElRadio>或将“全部”改为
null,由提交时剔除空值处理。Also applies to: 18-37
docs/demos/ma-search/basic-usage/index.vue (1)
50-57: 下拉值类型保持一致,减少后续比较/序列化歧义
'' | 1 | 0混用会让筛选、拼接查询参数时出现类型不一。建议统一为字符串或数字(与后端约定保持一致)。- { label: '启用', value: 1 }, - { label: '禁用', value: 0 } + { label: '启用', value: '1' }, + { label: '禁用', value: '0' }如需保留数字,请将“全部”改为
null并在提交时剔除空值。docs/demos/ma-search/default/index.vue (2)
92-92: 关于整包引入 icons 的体积权衡
import * as ElementIcons会引入全部图标,不利于 Tree-shaking。当前是文档示例,影响可接受;若需优化体积,考虑基于demoList预生成所需图标名并按需静态导入(需放弃运行时字符串映射)。
11-22: 抽离图标名称转换并新增别名与回退逻辑
- 用
getElIcon替换模板中的链式字符串处理,避免在找不到组件时渲染空白:- <el-icon> - <component - :is="ElementIcons[ - demo.icon - .replace('el-icon-', '') - .split('-') - .map(s => s.charAt(0).toUpperCase() + s.slice(1)) - .join('') - ]" - /> - </el-icon> + <el-icon v-if="getElIcon(demo.icon)"> + <component :is="getElIcon(demo.icon)" /> + </el-icon>- 在
<script setup>中新增转换函数与别名映射:const el2EpIconAlias: Record<string, string> = { 'star-on': 'StarFilled', 'warning': 'WarningFilled', 'document': 'Document', 'setting': 'Setting', 'menu': 'Menu', 'magic-stick': 'MagicStick', 'refresh': 'Refresh', 'mobile': 'Mobile', 'grid': 'Grid', 'cpu': 'Cpu', } const toEpIconName = (legacy: string) => { const raw = legacy.replace(/^el-icon-/, '') return el2EpIconAlias[raw] ?? raw .split('-') .map(s => s.charAt(0).toUpperCase() + s.slice(1)) .join('') } const getElIcon = (cls: string) => { const name = toEpIconName(cls) return (ElementIcons as any)[name] || (ElementIcons as any).Document }- 根据官方文档,组件名中无
StarOn,应使用Star(空心)或StarFilled(实心),WarningFilled存在,可按需求选用。docs/demos/ma-search/collapsible-search/index.vue (2)
155-157: 渲染器名称大小写不一致,建议与其他示例统一此处使用
render: 'datePicker',其它文件同时存在DatePicker/date-picker的写法,可能导致找不到渲染器。建议统一为一个约定(如DatePicker)。- render: 'datePicker', + render: 'DatePicker',
58-61: 移除未使用的 reactive 导入
reactive未被使用,建议删除以通过 lint 并减少噪音。-import { reactive, ref } from 'vue' +import { ref } from 'vue'docs/demos/ma-search/custom-actions/index.vue (2)
112-117: ref 类型不应指向 HTMLElement这些 ref 绑定的是组件实例而非 DOM 节点,
InstanceType<typeof HTMLElement>不准确,易误导类型提示。若无公开实例类型,可用any/unknown暂存,或声明为Ref<ReturnType<typeof getCurrentInstance>['proxy'] | null>。-const customActionsOne = ref<InstanceType<typeof HTMLElement> | null>(null) -const customActionsTwo = ref<InstanceType<typeof HTMLElement> | null>(null) -const customActionsThree = ref<InstanceType<typeof HTMLElement> | null>(null) -const customActionsFour = ref<InstanceType<typeof HTMLElement> | null>(null) +const customActionsOne = ref<any>(null) +const customActionsTwo = ref<any>(null) +const customActionsThree = ref<any>(null) +const customActionsFour = ref<any>(null)
171-177: 渲染器命名与其他示例存在差异,建议全仓统一此处使用
render: 'DatePicker',而其他文件有datePicker/date-picker。为减少踩坑,请统一为单一写法(建议与组件导出名一致)。docs/demos/ma-search/advanced-search/index.vue (4)
65-86: InputNumber 命名在不同示例间不一致,建议统一为同一写法本文件使用
'InputNumber',而responsive-layout示例使用'inputNumber'。为避免渲染器注册名不匹配导致的运行时找不到组件,建议统一为一套命名(推荐与多数示例一致)。示例修正如下:- render: 'InputNumber', + render: 'inputNumber', ... - render: 'InputNumber', + render: 'inputNumber',另外,小建议:
age_range更像“最小值”,可考虑与age_max对称命名为age_min,减少歧义(若对外有兼容约束可忽略)。
65-65: 避免依赖未启用的原子化样式类
class="!p-0 flex gap-2 w-full"依赖 UnoCSS/Tailwind 等原子化体系。结合 PR 描述“精简主题样式”,请确认站点仍启用了相应原子化工具;否则这些类将无效。若不确定,可改为行内样式保证示例可用:- render: () => <div class="!p-0 flex gap-2 w-full" />, + render: () => <div style={{ padding: 0, display: 'flex', gap: '8px', width: '100%' }} />,
90-97: ElRadio 无需同时传label与valueElement Plus 的
<el-radio>使用label作为取值,额外传value可能产生无效 prop 警告。建议去掉value:- <el-radio label="" value="">全部</el-radio> - <el-radio label={1} value={1}>正常</el-radio> - <el-radio label={0} value={0}>禁用</el-radio> - <el-radio label={-1} value={-1}>锁定</el-radio> + <el-radio label="">全部</el-radio> + <el-radio label={1}>正常</el-radio> + <el-radio label={0}>禁用</el-radio> + <el-radio label={-1}>锁定</el-radio>
141-153: searchOptions 与文案配置请确认类型契约
fold: false时foldRows: 2将被忽略(若库内部直接忽略则无碍,否则可删除以免误导)。text.searchBtn/resetBtn采用函数返回字符串,请确认最新类型是否允许函数;若仅支持字符串,建议改为静态文案。docs/demos/ma-search/responsive-layout/index.vue (6)
42-45: 预览块的show选项与labelWidth单位请确认
- 请确认
searchOptions.show为公开且生效的配置项(若为内部字段,建议移除)。- 本处
labelWidth: '80px',而其他示例多为数字或'100px';建议在文档内统一单位与风格以减少读者困惑。
54-73:w-1/3等原子化类需确认样式体系是否仍启用若已精简掉 Uno/Tailwind,对应宽度类将失效。可替换为自定义类并在样式中定义,保证示例在任何站点样式下都可用:
- <div class="control-group w-1/3"> + <div class="control-group control-third"> ... - <div class="control-group w-1/3"> + <div class="control-group control-third"> ... - <div class="control-group w-1/3"> + <div class="control-group control-third"> ... - <div class="control-group w-1/3"> + <div class="control-group control-third"> ... - <div class="control-group w-1/3"> + <div class="control-group control-third">在本文件
<style scoped>追加(支持建议外的改动):.control-third { flex: 1 1 calc(33.333% - 20px); min-width: 240px; }
155-159: inputNumber 使用一致,LGTM(建议跨文件统一命名)此处采用
'inputNumber',与advanced-search的'InputNumber'存在大小写差异,建议全仓统一。Also applies to: 163-171
243-247: 动态配置的初始 cols 建议来源于 dynamicCols,避免初始闪烁当前初始值全部为 2,与面板默认值不一致。可直接取
dynamicCols.value:-const dynamicOptions = ref<MaSearchOptions>({ - fold: true, - foldRows: 2, - cols: { xs: 2, sm: 2, md: 2, lg: 2, xl: 2 } -}) +const dynamicOptions = ref<MaSearchOptions>({ + fold: true, + foldRows: 2, + cols: { ...dynamicCols.value } +})
294-304: 避免使用 JSON 深拷贝导致函数丢失,改用 structuredClone(或回退)若后续某些项包含
render: () => JSX等函数,JSON 法会丢失。建议:+// 简单深拷贝工具:优先 structuredClone,回退 JSON +const clone = <T>(v: T): T => + (typeof structuredClone === 'function' ? structuredClone(v) : JSON.parse(JSON.stringify(v))) + const createSearchItems = (type: 'default' | 'custom' | 'extreme'): MaSearchItem[] => { - let items: MaSearchItem[] = JSON.parse(JSON.stringify(searchItems.value)) // 基础项 + let items: MaSearchItem[] = clone(searchItems.value) // 基础项 if (type === 'custom' || type === 'extreme') { - items = [...items, ...JSON.parse(JSON.stringify(moreSearchItems.value))] // 加更多项 + items = [...items, ...clone(moreSearchItems.value)] // 加更多项 } if (type === 'extreme') { - items = [...items, ...JSON.parse(JSON.stringify(extendedSearchItems.value))] // 加扩展项 + items = [...items, ...clone(extendedSearchItems.value)] // 加扩展项 } return items }
306-307: 表单选项工厂简洁,LGTM若全站统一
labelWidth单位(数字或 px 字符串),可在此处一次性规范。docs/demos/ma-search/table-integration/index.vue (5)
6-6: 确认 p-5 原子类是否可用,否则本次内边距调整可能无效如果文档站点未启用 UnoCSS/Tailwind 等原子化样式,类名 p-5 不会生效。可改为使用局部样式,去除对原子类的依赖。
备选修复(二选一):
- 保留 p-5(确保原子化 CSS 已加载);
- 去掉 p-5,改用 scoped 样式:
- <div class="search-table-container p-5"> + <div class="search-table-container">.search-table-container { border: 1px solid #e4e7ed; border-radius: 6px; overflow: hidden; background-color: #fff; + padding: 20px; }
275-283: 用稳定格式保存日期并增加 createdAtTs,避免解析本地化字符串带来的不确定性toLocaleString 生成的本地化字符串难以可靠解析。建议同时保存时间戳,显示层再本地化格式化。
-const mockData = Array.from({ length: 100 }, (_, index) => ({ - id: index + 1, - username: `user_${index + 1}`, - email: `user${index + 1}@example.com`, - phone: `138${String(index + 1).padStart(8, '0')}`, - department: ['tech', 'product', 'operation', 'marketing'][index % 4], - status: index % 3 !== 0 ? 1 : 0, - created_at: new Date(Date.now() - Math.random() * 365 * 24 * 60 * 60 * 1000).toLocaleString() -})) +const mockData = Array.from({ length: 100 }, (_, index) => { + const ts = Date.now() - Math.random() * 365 * 24 * 60 * 60 * 1000 + return { + id: index + 1, + username: `user_${index + 1}`, + email: `user${index + 1}@example.com`, + phone: `138${String(index + 1).padStart(8, '0')}`, + department: ['tech', 'product', 'operation', 'marketing'][index % 4], + status: index % 3 !== 0 ? 1 : 0, + createdAtTs: ts, + created_at: new Date(ts).toLocaleString() + } +})如需更一致的显示/解析,还可把 created_at 存为 ISO 字符串(toISOString),并在列模板中本地化渲染。
421-429: 为 ElMessageBox.confirm 增加 catch,避免“未处理的 Promise 拒绝”警告用户点击“取消”会触发 Promise reject,当前代码未捕获。
}).then(() => { ElMessage.success('删除成功') loadData() - }) + }).catch(() => {})}).then(() => { ElMessage.success(`已删除 ${selectedRows.value.length} 个用户`) selectedRows.value = [] loadData() - }) + }).catch(() => {})Also applies to: 431-441
249-249: 小屏两列可能过于拥挤,建议 xs=1 提升可用性在手机端通常单列更友好。
- cols: { xs: 2, sm: 2, md: 2, lg: 2, xl: 2 }, + cols: { xs: 1, sm: 2, md: 2, lg: 2, xl: 2 },
16-20: 确认 Element Plus 图标用法:icon 传字符串依赖运行时解析Element Plus 通常需要以组件形式传入图标(如 :icon="Plus" 并从 @element-plus/icons-vue 导入,或使用 )。若项目通过自动注册/解析器支持字符串写法则可保留;否则请调整以免图标不显示。
Also applies to: 117-121
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (21)
.vitepress/theme/index.ts(1 hunks)docs/demos/ma-form/basic-usage/index.vue(4 hunks)docs/demos/ma-form/component-rendering/index.vue(0 hunks)docs/demos/ma-form/conditional-rendering/index.vue(1 hunks)docs/demos/ma-form/dynamic-validation/index.vue(35 hunks)docs/demos/ma-form/layout-systems/index.vue(3 hunks)docs/demos/ma-form/loading-states/index.vue(36 hunks)docs/demos/ma-form/slots-examples/index.vue(0 hunks)docs/demos/ma-pro-table-examples/advanced-search/index.vue(6 hunks)docs/demos/ma-pro-table-examples/basic/index.vue(2 hunks)docs/demos/ma-search/advanced-search/index.vue(6 hunks)docs/demos/ma-search/basic-usage/index.vue(1 hunks)docs/demos/ma-search/collapsible-search/index.vue(4 hunks)docs/demos/ma-search/custom-actions/index.vue(8 hunks)docs/demos/ma-search/default/index.vue(2 hunks)docs/demos/ma-search/dynamic-items/index.vue(2 hunks)docs/demos/ma-search/form-validation/index.vue(1 hunks)docs/demos/ma-search/methods-demo/index.vue(2 hunks)docs/demos/ma-search/responsive-layout/index.vue(10 hunks)docs/demos/ma-search/table-integration/index.vue(3 hunks)package.json(1 hunks)
💤 Files with no reviewable changes (2)
- docs/demos/ma-form/component-rendering/index.vue
- docs/demos/ma-form/slots-examples/index.vue
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: People-Sea
PR: mineadmin/doc-v3#0
File: :0-0
Timestamp: 2025-08-11T15:28:39.641Z
Learning: 用户 People-Sea 指示在 mineadmin/doc-v3 仓库的 PR 审查中暂时不需要检查 markdown (.md) 文件的内容。
🔇 Additional comments (23)
.vitepress/theme/index.ts (1)
32-35: 仅保留 var.css 与 element.css:请回归基础样式与可读性精简样式可能影响排版(标题、段落、代码块、表格、暗色模式变量)。建议在移动与桌面下快速核对若干页面的排版与组件间距是否退化。
重点检查:
- 首页与任意 demo 页的标题/段落/列表/代码块的行高与颜色对比
- 暗色模式切换后的 tag、card、button 对比度
- 表格/表单的 label 与控件对齐
docs/demos/ma-form/layout-systems/index.vue (1)
485-486: 容器样式清空:请确认页面在窄屏/宽屏下的可读性与留白移除 max-width/padding 后内容可能“贴边”。若需要,可在父容器或页面统一层补上约束。
docs/demos/ma-form/basic-usage/index.vue (2)
1-1: 切换为 TSX 符合本次迁移方向与 @vitejs/plugin-vue-jsx 配置匹配,无阻碍。
335-337: 移除容器约束后,请确认页面左右留白与阅读舒适度如需要可在页面层恢复适度 max-width 或 padding。
docs/demos/ma-search/methods-demo/index.vue (2)
514-514: 占位符迁移到 renderProps:变更正确从 props/slots 迁移到 renderProps 承载占位符符合本次 PR 的统一方向。无需调整。
543-543: appendItem 的占位符迁移到 renderProps:变更合理与 setItems 的写法保持一致,可读性更好。
docs/demos/ma-pro-table-examples/basic/index.vue (1)
13-13: 补充引入 ElTag:必要且正确为 TSX 内联渲染状态标签提供了依赖,没问题。
docs/demos/ma-form/loading-states/index.vue (2)
724-742: 模板中使用了图标组件但未见显式引入:请确认全局自动导入Loading、SuccessFilled、Check、CircleCloseFilled、Clock 直接出现在模板中。若文档站未配置 @element-plus/icons-vue 的自动注册/自动导入,会导致运行时报错。建议显式按需引入,或在站点级别确认已全量注册。
可选补充(脚本顶部引入):
import { Loading, SuccessFilled, Check, CircleCloseFilled, Clock } from '@element-plus/icons-vue'
250-256: 国家下拉改为 options + 占位符:写法规范,易读与下方省/市联动逻辑一致,OK。
docs/demos/ma-form/dynamic-validation/index.vue (1)
688-699: 校验返回值/异常语义需核实(Element Plus 版本差异)
getElFormRef()?.validate()在不同版本中可能返回boolean | void(Promise)或通过 reject 表示失败。当前写法依赖返回isValid。请核对 @mineadmin/form 封装与 ElForm 版本行为,必要时改为捕获异常判断。我可以根据项目锁文件版本给出对应的安全用法片段。
docs/demos/ma-search/basic-usage/index.vue (1)
25-59: 此处迁移到 renderProps.options 的方式是对的变更与本 PR 其他示例保持一致,结构更清晰,可读性更好。
docs/demos/ma-search/collapsible-search/index.vue (1)
169-179: 折叠与栅格配置改造合理,类型标注清晰
foldOptions{1,2}显式使用MaSearchOptions、引入cols断点配置与函数式按钮文案,方向正确。Also applies to: 182-192
docs/demos/ma-search/dynamic-items/index.vue (2)
106-113: 将 props 切换为 renderProps 符合新版 API占位符放到
renderProps下,兼容性与可读性更佳。
266-270: 文本配置改为函数返回值与其他示例保持一致按钮文案函数化有助于后续国际化与动态切换。
docs/demos/ma-search/advanced-search/index.vue (2)
6-7: ma-search 属性对齐新 API,LGTM
:search-items、:form-options、:options与事件用法清晰,符合预期。
53-61: 字段渲染命名规范已统一为 datePicker,LGTM由
date-picker切换为datePicker符合新版渲染约定。docs/demos/ma-search/responsive-layout/index.vue (7)
17-31: 以配置对象驱动三个 Demo,结构更清晰,LGTM通过
demoConfigs聚合searchItems/formOptions/searchOptions,模板更简洁。
97-128: searchItems 切换为 renderProps 驱动配置,LGTM占位符与下拉选项迁移到
renderProps,配置清晰。
141-149: 日期渲染改为 datePicker,LGTM
196-207: 默认响应式配置清晰,LGTM
209-233: 自定义/极端响应式配置,LGTM
282-291: 动态更新逻辑清晰,LGTM
setOptions同步组件内部状态可保证实时生效。
316-336: demoConfigs 拆分三套数据源,LGTM便于读者对比不同响应式设定。
| <el-progress | ||
| :percentage="Math.min(100, ((Date.now() - (operation.startTime || 0)) / operation.duration) * 100)" | ||
| :stroke-width="4" | ||
| status="active" | ||
| /> | ||
| :stroke-width="4" status="active" /> | ||
| </div> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
el-progress 不支持 status="active";请改用 indeterminate 或条纹流动
Element Plus 的 Progress 组件 status 枚举不包含 active。要显示活动动画,可用:
- indeterminate + duration
- 或 striped + striped-flow(2.3.4+)
-<el-progress
- :percentage="Math.min(100, ((Date.now() - (operation.startTime || 0)) / operation.duration) * 100)"
- :stroke-width="4" status="active" />
+<el-progress
+ :percentage="Math.min(100, ((Date.now() - (operation.startTime || 0)) / operation.duration) * 100)"
+ :stroke-width="4"
+ striped
+ striped-flow
+ :duration="1.5" />参考:Element Plus Progress 文档(支持 indeterminate、striped/striped-flow)。(element-plus.org)
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <el-progress | |
| :percentage="Math.min(100, ((Date.now() - (operation.startTime || 0)) / operation.duration) * 100)" | |
| :stroke-width="4" | |
| status="active" | |
| /> | |
| :stroke-width="4" status="active" /> | |
| </div> | |
| <el-progress | |
| :percentage="Math.min(100, ((Date.now() - (operation.startTime || 0)) / operation.duration) * 100)" | |
| :stroke-width="4" | |
| striped | |
| striped-flow | |
| :duration="1.5" /> | |
| </div> |
🤖 Prompt for AI Agents
In docs/demos/ma-form/loading-states/index.vue around lines 799 to 802, the
el-progress uses status="active" which Element Plus does not support; replace
the unsupported status prop with a supported animation mode — either add the
indeterminate boolean and pass the duration (e.g. :indeterminate="true"
:duration="operation.duration") for an indeterminate animation, or use striped
and striped-flow (striped and striped-flow props, requires Element Plus 2.3.4+)
to get a flowing stripe effect; keep the existing :percentage expression as-is
and remove status="active".
| <el-button type="primary" @click="handleCustomSearch" icon="Search"> | ||
| 立即查询 | ||
| </el-button> | ||
| <el-button | ||
| type="warning" | ||
| @click="handleCustomReset" | ||
| icon="Refresh" | ||
| > | ||
| <el-button type="warning" @click="handleCustomReset" icon="Refresh"> | ||
| 清空条件 | ||
| </el-button> | ||
| <el-button | ||
| type="success" | ||
| @click="handleExport" | ||
| icon="Download" | ||
| > | ||
| <el-button type="success" @click="handleExport" icon="Download"> | ||
| 导出数据 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
el-button 的 icon 属性传入了字符串,可能不生效;请改为传入组件或使用 #icon 插槽
Element Plus 推荐 :icon="IconComponent" 或 <template #icon><el-icon><Icon /></el-icon></template>。当前 icon="Search" 等字符串在多数配置下不会自动解析为组件,导致图标不显示或样式异常。
示例修复(同时避免“更多操作”按钮的双图标问题):
- <el-button type="primary" @click="handleCustomSearch" icon="Search">
+ <el-button type="primary" @click="handleCustomSearch" :icon="Search">
立即查询
</el-button>
- <el-button type="warning" @click="handleCustomReset" icon="Refresh">
+ <el-button type="warning" @click="handleCustomReset" :icon="Refresh">
清空条件
</el-button>
- <el-button type="success" @click="handleExport" icon="Download">
+ <el-button type="success" @click="handleExport" :icon="Download">
导出数据
</el-button>- <el-button type="info" @click="handleSaveTemplate" icon="Collection">
+ <el-button type="info" @click="handleSaveTemplate" :icon="Collection">
保存模板
</el-button>
- <el-button type="primary" plain @click="handleLoadTemplate" icon="FolderOpened">
+ <el-button type="primary" plain @click="handleLoadTemplate" :icon="FolderOpened">
加载模板
</el-button>- <el-dropdown @command="handleMoreActions">
- <el-button type="primary" plain icon="More">
+ <el-dropdown @command="handleMoreActions">
+ <el-button type="primary" plain>
更多操作
<el-icon class="el-icon--right"><arrow-down /></el-icon>
</el-button>- <el-button type="text" @click="handleHelp" icon="QuestionFilled">
+ <el-button type="text" @click="handleHelp" :icon="QuestionFilled">
帮助
</el-button>并在脚本中补充按需引入:
import { Search, Refresh, Download, Collection, FolderOpened, QuestionFilled } from '@element-plus/icons-vue'Also applies to: 31-35, 43-51, 85-85
🤖 Prompt for AI Agents
In docs/demos/ma-search/custom-actions/index.vue around lines 12-19 (also apply
same change to 31-35, 43-51, and 85), the el-button icon props are set as plain
strings which won't render; import the needed Element Plus icon components in
the script (e.g., Search, Refresh, Download, etc.) and replace string icons with
component refs via :icon="IconComponent" or supply the icon via the #icon slot
(wrapping the Icon in <el-icon>), and ensure any "more actions" button does not
render duplicate icons by removing one of the icon usages.
| label: '创建时间', | ||
| prop: 'created_at', | ||
| render: 'date-picker', | ||
| render: 'datePicker', | ||
| renderProps: { | ||
| type: 'datetimerange', | ||
| placeholder: ['开始时间', '结束时间'], | ||
| type: 'datetimerange', | ||
| startPlaceholder: '开始时间', | ||
| endPlaceholder: '结束时间', | ||
| format: 'YYYY-MM-DD HH:mm:ss' | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
“创建时间”搜索项已改为 datePicker,但未参与过滤,实际搜索无效
当前 loadData 未对 created_at 进行任何过滤,导致该搜索项形同虚设。建议补齐时间范围过滤逻辑(默认未设置 valueFormat 时,datePicker 返回的是 Date 对象)。
在现有筛选后追加时间范围过滤(建议与下方 mockData 补丁配合,使用 createdAtTs 进行数值比较更稳妥):
@@
if (searchCondition.value.status !== undefined && searchCondition.value.status !== '') {
filteredData = filteredData.filter(item =>
item.status === searchCondition.value.status
)
}
+ // 创建时间范围过滤(依赖 mockData 中的 createdAtTs,见下方补丁)
+ if (
+ Array.isArray(searchCondition.value.created_at) &&
+ searchCondition.value.created_at.length === 2
+ ) {
+ const [start, end] = searchCondition.value.created_at as [Date, Date]
+ const startTs = start.getTime()
+ const endTs = end.getTime()
+ filteredData = filteredData.filter(item =>
+ item.createdAtTs >= startTs && item.createdAtTs <= endTs
+ )
+ }🤖 Prompt for AI Agents
In docs/demos/ma-search/table-integration/index.vue around lines 230 to 239, the
"创建时间" datePicker is not applied in loadData so the time-range filter is
ineffective; update loadData to extract the created_at value from filters (which
will be two Date objects when valueFormat is unset), convert them to numeric
timestamps (startTs = startDate.getTime(), endTs = endDate.getTime()), and then
apply an additional filter step against each row's createdAtTs (use >= startTs
and <= endTs, treating open-ended ranges appropriately when one bound is
missing). Ensure you handle null/undefined filter values and preserve existing
filters order so the date range is applied after the current filter pipeline.
| "@imengyu/vue3-context-menu": "^1.4.2", | ||
| "@mineadmin/echarts": "^1.0.7", | ||
| "@mineadmin/form": "^1.0.51", | ||
| "@mineadmin/form": "^1.0.53", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
依赖升级到 @mineadmin/form ^1.0.53,请做一次冒烟验证
与 TSX/renderProps 的变更一致,但 caret 版本可能引入次要更新,建议本地跑一轮 demo 页面以排查潜在行为差异。
建议在本仓库根目录执行:
🏁 Script executed:
#!/bin/bash
# 确认声明版本
node -e "console.log(require('./package.json').dependencies['@mineadmin/form'])"
# 粗查旧 API 是否残留(例如 renderSlots)
rg -n --glob '!**/node_modules/**' 'renderSlots|slot[s]?\s*:' docs/demos || true
# 预览站点(确保已安装依赖)
# pnpm i && pnpm dev 或 npm i && npm run devLength of output: 2604
请本地运行 demo 并更新示例代码以兼容 @mineadmin/form ^1.0.53
- package.json 中已声明依赖版本为
^1.0.53 - docs/demos 目录下仍存在大量
renderSlots/slots:旧 API 调用,需替换为新版渲染接口(如 renderProps) - 执行
pnpm i && pnpm dev或npm i && npm run dev,确认所有示例页面无报错且功能正常后合并
🤖 Prompt for AI Agents
In package.json around line 18, the dependency was bumped to "@mineadmin/form":
"^1.0.53" but demo examples still use the old renderSlots/slots API; update
docs/demos files to use the new renderProps (or the new rendering API provided
by @mineadmin/form v1.0.53), replacing all occurrences of renderSlots and slots:
with the new interface, adjust prop names/signatures to match the new API, run
pnpm i && pnpm dev (or npm i && npm run dev) locally, verify all demo pages load
without errors and behavior matches expected, then commit the updated demo
files.
Summary by CodeRabbit