优化更新

This commit is contained in:
2026-09-13 18:12:07 +08:00
parent 7f758cc6e9
commit d7a081e8b7
92 changed files with 2429 additions and 1117 deletions
@@ -3,7 +3,7 @@
<!-- Copyright (c) 2021-2026 小蚂蚁云团队 版权所有 -->
:::tip 说明
项目提供丰富的公共组件,位于 `ui/src/components/` 目录,基于 ElementPlus 二次封装,覆盖表格、表单、弹窗、上传、富文本编辑等常见场景。组件通过 `src/plugins/` 中的 `setupCustomComponents` 全局注册,任何页面直接使用,无需单独 import。
项目提供丰富的公共组件,位于 `ui/src/components/` 目录,基于 NaiveUI 二次封装,覆盖表格、表单、弹窗、上传、富文本编辑等常见场景。组件通过 `src/plugins/` 中的 `setupCustomComponents` 全局注册,任何页面直接使用,无需单独 import。
:::
## 组件总览
@@ -19,7 +19,7 @@
| CropperImage | `components/Cropper/` | 图片裁剪(圆形/矩形、实时预览) |
| QrCode | `components/Qrcode/` | 二维码生成(canvas/img、内嵌 logo) |
| ChinaArea | `components/ChinaArea/` | 省市区级联(懒加载、二/三级联动) |
| Icon | `components/icon/` | 图标(支持 ElementPlus 图标 + SVG 图标) |
| Icon | `components/icon/` | 图标(支持 @vicons/antd 图标 + SVG 图标) |
| BasicSelect | `components/Select/` | 增强下拉(远程请求、本地缓存) |
| CountTo | `components/CountTo/` | 数字动画(前缀/后缀、千分位、缓动曲线) |
| Password | `components/Password/` | 密码强度(强度指示器、重复确认、复杂度校验) |
@@ -40,7 +40,7 @@
## BasicTable 表格
表格组件是使用频率最高的组件,基于 `el-table` + `el-pagination` 封装,详见 [BasicTable 表格](/zh/volume4/frontend/table) 章节。
表格组件是使用频率最高的组件,基于 NaiveUI 的 `n-data-table` 封装,详见 [BasicTable 表格](/zh/volume4/frontend/table) 章节。
### Props
@@ -79,8 +79,8 @@
| 属性 | 类型 | 说明 |
|------|------|------|
| `label` | `string` | 列标题 |
| `prop` | `string` | 字段名 |
| `title` | `string` | 列标题 |
| `key` | `string` | 字段名 |
| `width` | `number` | 列宽 |
| `align` | `string` | 对齐方式 |
| `type` | `string` | 类型:`index` / `selection` |
@@ -96,7 +96,7 @@
|------|------|------|
| `fetch-success` | `(data, result)` | 数据请求成功 |
| `fetch-error` | `(error)` | 数据请求失败 |
| `selection-change` | `(rows)` | 多选变化 |
| `update:checked-row-keys` | `(keys)` | 多选变化 |
| `columns-change` | `(columns)` | 列设置变化 |
| `checked-row-change` | `(rowKeys)` | 选中行变化 |
@@ -129,39 +129,59 @@
```vue
<template>
<BasicTable :columns="columns" :request="loadDataTable" ref="tableRef">
<BasicTable :columns="columns" :request="loadDataTable" ref="basicTableRef" :actionColumn="actionColumn">
<template #tableTitle>
<el-button v-perm="['sys:level:add']" type="primary" @click="handleAdd">
<el-icon><Plus /></el-icon>新增
</el-button>
<n-space>
<n-button v-perm="['sys:level:add']" type="primary" @click="handleAdd">
<template #icon><n-icon><PlusOutlined /></n-icon></template>
新增
</n-button>
</n-space>
</template>
</BasicTable>
</template>
<script setup lang="ts">
import { h, reactive, ref } from 'vue';
import { NTag } from 'naive-ui';
import { PlusOutlined, FormOutlined, DeleteOutlined } from '@vicons/antd';
import { TableAction } from '@/components/Table';
import { renderIcon } from '@/utils';
const basicTableRef = ref();
const columns = [
{ label: '名称', prop: 'name', width: 200 },
{ label: '状态', prop: 'status', width: 100 },
{ label: '创建时间', prop: 'createTime', width: 180 },
{ title: '名称', key: 'name', width: 200 },
{
title: '状态', key: 'status', width: 100,
render(record) {
return h(NTag, { bordered: false, type: record.status == 1 ? 'success' : 'error' },
{ default: () => record.status == 1 ? '正常' : '停用' });
},
},
{ title: '创建时间', key: 'createTime', width: 180 },
];
const actionColumn = {
const actionColumn = reactive({
width: 200,
actions: [
{ label: '编辑', auth: ['sys:level:update'], onClick: handleEdit },
{
label: '删除',
auth: ['sys:level:delete'],
isConfirm: true,
popConfirm: { title: '确认删除?' },
onClick: handleDelete,
},
],
};
title: '操作',
align: 'center',
key: 'action',
fixed: 'right',
render(record) {
return h(TableAction as any, {
style: 'button',
actions: [
{ label: '编辑', icon: renderIcon(FormOutlined), type: 'info', onClick: handleEdit.bind(null, record), auth: ['sys:level:update'] },
{ label: '删除', icon: renderIcon(DeleteOutlined), type: 'error', onClick: handleDelete.bind(null, record), auth: ['sys:level:delete'] },
],
});
},
});
async function loadDataTable({ page, pageSize }) {
const res = await getListApi({ pageNo: page, pageSize });
return { items: res.data, total: res.count };
async function loadDataTable(params) {
const result = await getListApi(params);
return result;
}
</script>
```
@@ -230,13 +250,13 @@ async function loadDataTable({ page, pageSize }) {
|------|------|------|
| `field` | `string` | 字段名(必填) |
| `label` | `string` | 标签文字 |
| `component` | `ComponentType` | 渲染组件:`Input` / `Select` / `DatePicker` 等 |
| `component` | `ComponentType` | 渲染组件:`NInput` / `NSelect` / `NDatePicker` 等 |
| `componentProps` | `object` | 组件属性 |
| `defaultValue` | `any` | 默认值 |
| `rules` | `object \| object[]` | 字段校验规则 |
| `slot` | `string` | 自定义插槽名 |
| `hidden` | `boolean \| Function` | 是否隐藏 |
| `colProps` | `ColProps` | 栅格配置 |
| `gridProps` | `GridProps` | 栅格配置(如 `{ cols: '1 s:1 m:2 l:3' }`) |
| `labelMessage` | `string` | 标签提示信息 |
| `suffix` | `string` | 后缀文字 |
@@ -268,17 +288,19 @@ async function loadDataTable({ page, pageSize }) {
<script setup lang="ts">
const [registerForm] = useForm({
gridProps: { cols: '1 s:1 m:2 l:3 xl:4 2xl:4' },
labelWidth: 80,
schemas: [
{
field: 'name',
label: '名称',
component: 'Input',
component: 'NInput',
componentProps: { placeholder: '请输入名称' },
},
{
field: 'status',
label: '状态',
component: 'Select',
component: 'NSelect',
componentProps: {
options: [
{ label: '启用', value: 1 },
@@ -286,12 +308,6 @@ const [registerForm] = useForm({
],
},
},
{
field: 'dateRange',
label: '日期范围',
component: 'DatePicker',
componentProps: { type: 'daterange' },
},
],
});
</script>
@@ -301,7 +317,7 @@ const [registerForm] = useForm({
## BasicModal 弹窗
弹窗组件基于 `el-dialog` 封装,支持拖拽、自定义按钮,详见 [Modal 弹窗](/zh/volume4/frontend/modal) 章节。
弹窗组件基于 NaiveUI 封装,支持拖拽、自定义按钮,详见 [Modal 弹窗](/zh/volume4/frontend/modal) 章节。
### Props
@@ -335,21 +351,29 @@ const [registerForm] = useForm({
```vue
<template>
<BasicModal title="编辑用户" :width="600" @register="registerModal" @ok="handleSubmit">
<BasicForm @register="registerForm" />
</BasicModal>
<basicModal @register="modalRegister" ref="modalRef" class="basicModal basicFormModal" @on-ok="handleSubmit">
<template #default>
<n-form ref="formRef" :model="formData" label-placement="left" label-width="85px">
<n-form-item label="用户名" path="name" :rule="{ required: true, message: '请输入用户名', trigger: 'blur' }">
<n-input v-model:value="formData.name" placeholder="请输入用户名" />
</n-form-item>
</n-form>
</template>
</basicModal>
</template>
<script setup lang="ts">
const [registerModal, { openModal, closeModal, setModalProps }] = useModal();
import { useModal } from '@/components/Modal';
const [modalRegister, { openModal, closeModal, setSubLoading }] = useModal({
title: '编辑用户',
subBtuText: '确定',
width: 600,
});
function handleEdit(record) {
setModalProps({ title: '编辑用户' });
openModal();
// 回填表单数据
nextTick(() => {
setFieldsValue(record);
});
}
</script>
```
@@ -358,7 +382,7 @@ function handleEdit(record) {
## BasicUpload 上传
文件/图片上传组件,基于 `el-upload` 封装,支持卡片/列表模式、数量限制、图片预览。
文件/图片上传组件,基于 NaiveUI 封装,支持卡片/列表模式、数量限制、图片预览。
### Props
@@ -700,10 +724,10 @@ async function getDeptOptions() {
<BasicTable :columns="columns" :request="loadData" />
<template #leftFooter>
<el-button @click="handleCancel">取消</el-button>
<n-button @click="handleCancel">取消</n-button>
</template>
<template #rightFooter>
<el-button type="primary" @click="handleSubmit">提交</el-button>
<n-button type="primary" @click="handleSubmit">提交</n-button>
</template>
</PageWrapper>
</template>
@@ -726,7 +750,7 @@ async function getDeptOptions() {
```vue
<template>
<Authority :value="['sys:user:add']">
<el-button type="primary">新增用户</el-button>
<n-button type="primary">新增用户</n-button>
</Authority>
</template>
```
@@ -734,7 +758,7 @@ async function getDeptOptions() {
:::tip v-perm 指令
除了 `Authority` 组件,项目还提供 `v-perm` 指令,功能相同但更简洁:
```vue
<el-button v-perm="['sys:user:add']">新增用户</el-button>
<n-button v-perm="['sys:user:add']">新增用户</n-button>
```
:::
@@ -742,7 +766,7 @@ async function getDeptOptions() {
## Icon 图标
统一图标组件,支持 ElementPlus 图标和 SVG 图标。
统一图标组件,支持 @vicons/antd 图标和 SVG 图标。
### Props
@@ -756,8 +780,8 @@ async function getDeptOptions() {
```vue
<template>
<!-- ElementPlus 图标 -->
<Icon name="el-icon-Plus" size="18px" />
<!-- @vicons/antd 图标 -->
<n-icon :component="PlusOutlined" size="18" />
<!-- SVG 图标 -->
<Icon name="svg-icon-user" />
</template>
@@ -838,4 +862,4 @@ setupCustomComponents(app); // 注册所有自定义组件
## 总结
公共组件库基于 ElementPlus 二次封装,核心三件套 BasicTable(表格)、BasicForm(表单)、BasicModal(弹窗)覆盖列表页标准场景。辅以 Upload、Editor、Cropper 等业务组件,以及 Authority、v-perm 权限体系。通过全局注册,任何页面直接使用,无需单独 import。优先使用公共组件,避免重复开发。
公共组件库基于 NaiveUI 二次封装,核心三件套 BasicTable(表格)、BasicForm(表单)、BasicModal(弹窗)覆盖列表页标准场景。辅以 Upload、Editor、Cropper 等业务组件,以及 Authority、v-perm 权限体系。通过全局注册,任何页面直接使用,无需单独 import。优先使用公共组件,避免重复开发。