- 修复路由守卫:未登录时直接跳转,不显示提示信息 - 修复API拦截器:401错误直接跳转,无需确认 - 移除不必要的ElMessageBox确认框 - 优化Token过期处理逻辑 - 修复文件管理API引入路径和URL前缀 - 修复调拨/回收管理API端点不匹配问题 - 修复通知管理API方法不匹配问题 - 统一系统配置API路径为单数形式 影响文件: - src/router/index.ts - src/api/request.ts - src/api/file.ts - src/api/index.ts 测试状态: - 前端构建通过 - 所有API路径已验证 - 登录流程测试通过 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
400 lines
6.8 KiB
Markdown
400 lines
6.8 KiB
Markdown
# 动态表单组件 - 快速开始
|
||
|
||
## 1. 基础使用
|
||
|
||
### 最简单的例子
|
||
|
||
```vue
|
||
<template>
|
||
<DynamicFieldRenderer
|
||
v-model="formData"
|
||
:fields="fields"
|
||
/>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { ref } from 'vue'
|
||
import DynamicFieldRenderer from '@/components/form/DynamicFieldRenderer.vue'
|
||
|
||
const formData = ref({})
|
||
|
||
const fields = [
|
||
{
|
||
id: '1',
|
||
name: 'username',
|
||
label: '用户名',
|
||
fieldType: 'text',
|
||
required: true
|
||
}
|
||
]
|
||
</script>
|
||
```
|
||
|
||
## 2. 添加验证
|
||
|
||
```vue
|
||
<script setup lang="ts">
|
||
const fields = [
|
||
{
|
||
id: '1',
|
||
name: 'email',
|
||
label: '邮箱',
|
||
fieldType: 'email',
|
||
required: true,
|
||
validationRules: {
|
||
pattern: '^[^@]+@[^@]+\\\\.[^@]+$',
|
||
customMessage: '请输入有效的邮箱地址'
|
||
}
|
||
},
|
||
{
|
||
id: '2',
|
||
name: 'age',
|
||
label: '年龄',
|
||
fieldType: 'number',
|
||
validationRules: {
|
||
min: 18,
|
||
max: 65
|
||
}
|
||
}
|
||
]
|
||
</script>
|
||
```
|
||
|
||
## 3. 字段联动
|
||
|
||
```vue
|
||
<template>
|
||
<DynamicFieldRenderer
|
||
v-model="formData"
|
||
:fields="fields"
|
||
:dependencies="dependencies"
|
||
/>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
const fields = [
|
||
{
|
||
id: '1',
|
||
name: 'hasDiscount',
|
||
label: '是否有优惠',
|
||
fieldType: 'boolean'
|
||
},
|
||
{
|
||
id: '2',
|
||
name: 'discountCode',
|
||
label: '优惠码',
|
||
fieldType: 'text',
|
||
// 只有选择有优惠时才显示
|
||
visible: (data) => data.hasDiscount === true
|
||
}
|
||
]
|
||
</script>
|
||
```
|
||
|
||
## 4. 处理表单提交
|
||
|
||
```vue
|
||
<template>
|
||
<DynamicFieldRenderer
|
||
ref="formRef"
|
||
v-model="formData"
|
||
:fields="fields"
|
||
/>
|
||
<el-button @click="handleSubmit">提交</el-button>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { ref } from 'vue'
|
||
import { ElMessage } from 'element-plus'
|
||
|
||
const formRef = ref()
|
||
const formData = ref({})
|
||
|
||
const handleSubmit = async () => {
|
||
const valid = await formRef.value?.validateForm()
|
||
if (valid) {
|
||
console.log('表单数据:', formData.value)
|
||
ElMessage.success('提交成功')
|
||
}
|
||
}
|
||
</script>
|
||
```
|
||
|
||
## 5. 使用Composable
|
||
|
||
```vue
|
||
<script setup lang="ts">
|
||
import { useDynamicForm } from '@/composables/useDynamicForm'
|
||
|
||
const fields = [
|
||
// ... 字段配置
|
||
]
|
||
|
||
const {
|
||
formData, // 表单数据
|
||
isValid, // 是否验证通过
|
||
setFieldValue, // 设置字段值
|
||
validateAll, // 验证所有字段
|
||
submitForm // 提交表单
|
||
} = useDynamicForm(fields)
|
||
|
||
const handleSubmit = async () => {
|
||
await submitForm(async (data) => {
|
||
console.log('提交数据:', data)
|
||
})
|
||
}
|
||
</script>
|
||
```
|
||
|
||
## 6. 加载设备类型字段
|
||
|
||
```vue
|
||
<script setup lang="ts">
|
||
import { onMounted } from 'vue'
|
||
import { useFieldConfig } from '@/composables/useFieldConfig'
|
||
|
||
const { loadFieldConfig } = useFieldConfig()
|
||
const fields = ref([])
|
||
|
||
onMounted(async () => {
|
||
// 从API加载设备类型的字段配置
|
||
fields.value = await loadFieldConfig(1) // 1是设备类型ID
|
||
})
|
||
</script>
|
||
```
|
||
|
||
## 7. 常用字段类型示例
|
||
|
||
```typescript
|
||
const fields = [
|
||
// 单行文本
|
||
{
|
||
id: '1',
|
||
name: 'title',
|
||
label: '标题',
|
||
fieldType: 'text',
|
||
required: true
|
||
},
|
||
|
||
// 多行文本
|
||
{
|
||
id: '2',
|
||
name: 'description',
|
||
label: '描述',
|
||
fieldType: 'textarea',
|
||
rows: 4
|
||
},
|
||
|
||
// 数字
|
||
{
|
||
id: '3',
|
||
name: 'price',
|
||
label: '价格',
|
||
fieldType: 'number',
|
||
validationRules: {
|
||
min: 0,
|
||
max: 999999
|
||
}
|
||
},
|
||
|
||
// 日期
|
||
{
|
||
id: '4',
|
||
name: 'birthday',
|
||
label: '生日',
|
||
fieldType: 'date'
|
||
},
|
||
|
||
// 下拉选择
|
||
{
|
||
id: '5',
|
||
name: 'gender',
|
||
label: '性别',
|
||
fieldType: 'select',
|
||
options: [
|
||
{ label: '男', value: 'male' },
|
||
{ label: '女', value: 'female' }
|
||
]
|
||
},
|
||
|
||
// 多选
|
||
{
|
||
id: '6',
|
||
name: 'hobbies',
|
||
label: '爱好',
|
||
fieldType: 'multiselect',
|
||
options: [
|
||
{ label: '读书', value: 'reading' },
|
||
{ label: '运动', value: 'sports' },
|
||
{ label: '音乐', value: 'music' }
|
||
]
|
||
},
|
||
|
||
// 开关
|
||
{
|
||
id: '7',
|
||
name: 'isActive',
|
||
label: '是否激活',
|
||
fieldType: 'boolean',
|
||
defaultValue: false
|
||
}
|
||
]
|
||
```
|
||
|
||
## 8. 布局控制
|
||
|
||
```typescript
|
||
const fields = [
|
||
// 半行
|
||
{
|
||
id: '1',
|
||
name: 'firstName',
|
||
label: '名',
|
||
fieldType: 'text',
|
||
span: 12 // 占12列(半行)
|
||
},
|
||
|
||
// 半行
|
||
{
|
||
id: '2',
|
||
name: 'lastName',
|
||
label: '姓',
|
||
fieldType: 'text',
|
||
span: 12 // 占12列(半行)
|
||
},
|
||
|
||
// 整行
|
||
{
|
||
id: '3',
|
||
name: 'address',
|
||
label: '地址',
|
||
fieldType: 'text',
|
||
span: 24 // 占24列(整行)
|
||
}
|
||
]
|
||
```
|
||
|
||
## 9. 自定义验证
|
||
|
||
```typescript
|
||
const fields = [
|
||
{
|
||
id: '1',
|
||
name: 'password',
|
||
label: '密码',
|
||
fieldType: 'text',
|
||
required: true,
|
||
validationRules: {
|
||
custom: (value) => {
|
||
if (value.length < 8) {
|
||
return '密码长度不能少于8位'
|
||
}
|
||
if (!/[A-Z]/.test(value)) {
|
||
return '密码必须包含大写字母'
|
||
}
|
||
if (!/[0-9]/.test(value)) {
|
||
return '密码必须包含数字'
|
||
}
|
||
return true
|
||
}
|
||
}
|
||
}
|
||
]
|
||
```
|
||
|
||
## 10. 完整示例
|
||
|
||
```vue
|
||
<template>
|
||
<el-card>
|
||
<DynamicFieldRenderer
|
||
ref="formRef"
|
||
v-model="formData"
|
||
:fields="fields"
|
||
label-width="100px"
|
||
@field-change="handleChange"
|
||
/>
|
||
<el-button type="primary" @click="handleSubmit">提交</el-button>
|
||
<el-button @click="handleReset">重置</el-button>
|
||
</el-card>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { ref } from 'vue'
|
||
import { ElMessage } from 'element-plus'
|
||
import DynamicFieldRenderer from '@/components/form/DynamicFieldRenderer.vue'
|
||
|
||
const formRef = ref()
|
||
const formData = ref({
|
||
username: '',
|
||
email: '',
|
||
role: 'user'
|
||
})
|
||
|
||
const fields = [
|
||
{
|
||
id: '1',
|
||
name: 'username',
|
||
label: '用户名',
|
||
fieldType: 'text',
|
||
required: true,
|
||
span: 12,
|
||
validationRules: {
|
||
min: 3,
|
||
max: 20
|
||
}
|
||
},
|
||
{
|
||
id: '2',
|
||
name: 'email',
|
||
label: '邮箱',
|
||
fieldType: 'email',
|
||
required: true,
|
||
span: 12
|
||
},
|
||
{
|
||
id: '3',
|
||
name: 'role',
|
||
label: '角色',
|
||
fieldType: 'select',
|
||
required: true,
|
||
span: 12,
|
||
options: [
|
||
{ label: '管理员', value: 'admin' },
|
||
{ label: '普通用户', value: 'user' }
|
||
]
|
||
},
|
||
{
|
||
id: '4',
|
||
name: 'isActive',
|
||
label: '激活状态',
|
||
fieldType: 'boolean',
|
||
span: 12,
|
||
defaultValue: true
|
||
}
|
||
]
|
||
|
||
const handleChange = (event) => {
|
||
console.log('字段变化:', event)
|
||
}
|
||
|
||
const handleSubmit = async () => {
|
||
const valid = await formRef.value?.validateForm()
|
||
if (valid) {
|
||
console.log('提交数据:', formData.value)
|
||
ElMessage.success('提交成功')
|
||
}
|
||
}
|
||
|
||
const handleReset = () => {
|
||
formRef.value?.resetForm()
|
||
}
|
||
</script>
|
||
```
|
||
|
||
## 更多资源
|
||
|
||
- [完整文档](./DYNAMIC_FORM_COMPONENTS_README.md)
|
||
- [开发总结](./DYNAMIC_FORM_DEVELOPMENT_SUMMARY.md)
|
||
- [示例代码](./src/views/examples/DynamicFormExample.vue)
|