feat: 新增工厂区域管理页面,修复Sass废弃警告
1. 新增生产配置-工厂模型-工厂区域完整CRUD页面 2. 新增通用表格、弹窗表单、i18n工具组件 3. 升级sass-loader并修复Sass废弃警告 4. 添加文档记录Sass迁移修复细节
This commit is contained in:
323
src/components/page-dialog-form/index.vue
Normal file
323
src/components/page-dialog-form/index.vue
Normal file
@@ -0,0 +1,323 @@
|
||||
<template>
|
||||
<!--
|
||||
page-dialog-form — 增删改查弹框组件
|
||||
===================================
|
||||
这是一个「带表单验证的弹框」组件,配合 page-table 使用。
|
||||
负责:弹框显隐控制 + 表单渲染 + 表单验证 + 确定/取消按钮。
|
||||
|
||||
不支持复杂表单联动——如有需要,通过默认插槽自定义内容。
|
||||
|
||||
依赖:element-ui 的 <el-dialog> <el-form> <el-input> <el-select>
|
||||
i18n:title / label / placeholder / confirm/cancel 按钮 全部自动 $t() 翻译
|
||||
|
||||
@author 前端团队
|
||||
@since 2026-05
|
||||
-->
|
||||
|
||||
<!--
|
||||
el-dialog:
|
||||
visible.sync → 通过 .sync 修饰符双向绑定父组件的 visible prop
|
||||
destroy-on-close → 每次关闭销毁 DOM 重建,避免表单残留上次数据
|
||||
close-on-click-modal → 禁止点击遮罩关闭,防止误操作丢失填写数据
|
||||
-->
|
||||
<el-dialog
|
||||
:visible.sync="visibleProxy"
|
||||
:title="$t(title)"
|
||||
:width="width"
|
||||
:close-on-click-modal="false"
|
||||
:destroy-on-close="true"
|
||||
@close="onClose"
|
||||
>
|
||||
<!-- ==================== 表单区 ==================== -->
|
||||
<!--
|
||||
el-form:
|
||||
rules 由父组件传入,字段名与 formData 的 key 一一对应
|
||||
label-width 默认 100px,可自定义
|
||||
-->
|
||||
<el-form
|
||||
ref="form"
|
||||
:model="formData"
|
||||
:rules="rules"
|
||||
:label-width="labelWidth || '100px'"
|
||||
>
|
||||
<!--
|
||||
遍历 formCols 中所有行 → 每行中的每个字段 → 渲染对应的表单项
|
||||
当前支持两种字段类型:
|
||||
- type='input' → <el-input>(支持 textarea、密码等)
|
||||
- type='select' → <el-select> + <el-option>
|
||||
|
||||
label / placeholder 会自动调用 $t() 翻译,传 i18n key 即可
|
||||
-->
|
||||
<el-form-item
|
||||
v-for="col in flatFormCols"
|
||||
:key="col.prop"
|
||||
:label="$t(col.label)"
|
||||
:prop="col.prop"
|
||||
>
|
||||
<!-- ===== 输入框类型 ===== -->
|
||||
<!--
|
||||
input:
|
||||
inputType='textarea' → 多行文本框
|
||||
不传 inputType → 普通文本输入框
|
||||
clearable → 默认 true,传 false 可关闭
|
||||
-->
|
||||
<el-input
|
||||
v-if="col.type === 'input'"
|
||||
v-model="formData[col.prop]"
|
||||
:placeholder="$t(col.placeholder)"
|
||||
:type="col.inputType || 'text'"
|
||||
:autosize="col.autosize"
|
||||
:clearable="col.clearable !== false"
|
||||
:style="col.style"
|
||||
/>
|
||||
<!-- ===== 下拉选择类型 ===== -->
|
||||
<!--
|
||||
select:
|
||||
options → [{ label: '苹果', value: 1 }]
|
||||
filterable → 默认 true,支持搜索过滤
|
||||
-->
|
||||
<el-select
|
||||
v-else-if="col.type === 'select'"
|
||||
v-model="formData[col.prop]"
|
||||
:placeholder="$t(col.placeholder)"
|
||||
:clearable="col.clearable !== false"
|
||||
:style="col.style"
|
||||
:filterable="col.filterable !== false"
|
||||
>
|
||||
<el-option
|
||||
v-for="opt in col.options"
|
||||
:key="opt.value"
|
||||
:label="$t(opt.label)"
|
||||
:value="opt.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<!-- ===== 自定义表单内容插槽 ===== -->
|
||||
<!--
|
||||
如果需要超出 input/select 的复杂表单控件
|
||||
父组件可以用此插槽添加任意内容
|
||||
-->
|
||||
<slot />
|
||||
</el-form>
|
||||
|
||||
<!-- ==================== 底部按钮 ==================== -->
|
||||
<!--
|
||||
确定:type='primary' + submitting loading 状态
|
||||
取消:调用 onClose → 重置表单 + 关闭弹框
|
||||
按钮文字自动 $t() 翻译
|
||||
-->
|
||||
<template #footer>
|
||||
<el-button @click="onClose">{{ $t(cancelText) }}</el-button>
|
||||
<el-button type="primary" :loading="submitting" @click="onSubmit">{{ $t(confirmText) }}</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* PageDialogForm — 增删改查弹框组件
|
||||
*
|
||||
* 【核心职责】
|
||||
* 1. 管理弹框显隐
|
||||
* 2. 根据 formCols 声明式渲染表单
|
||||
* 3. 表单验证 + 验证失败提示
|
||||
* 4. 提交/取消事件通知父组件
|
||||
*
|
||||
* 【典型用法】
|
||||
* <page-dialog-form
|
||||
* ref="dialogForm"
|
||||
* :visible.sync="dialogVisible"
|
||||
* :title="dialogTitle"
|
||||
* width="35%"
|
||||
* :form-cols="formCols"
|
||||
* :form-data="formData"
|
||||
* :rules="rules"
|
||||
* :submitting="submitting"
|
||||
* @submit="onDialogSubmit"
|
||||
* @close="onDialogClose"
|
||||
* />
|
||||
*
|
||||
* 【父组件调用的方法(通过 ref)】
|
||||
* this.$refs.dialogForm.reset() — 重置表单
|
||||
* this.$refs.dialogForm.validate() — 手动验证,返回 Promise<boolean>
|
||||
*
|
||||
* 【formCols 数据结构说明】
|
||||
* 二维数组:外层是行,里层是该行的字段。大多数场景每行一个字段即可。
|
||||
*
|
||||
* 例:
|
||||
* formCols: [
|
||||
* [ { type: 'input', prop: 'code', label: T+'.code', placeholder: T+'.enter_code' } ],
|
||||
* [ { type: 'input', prop: 'remark', label: T+'.remark', inputType: 'textarea', autosize: { minRows: 2 } } ],
|
||||
* [ { type: 'select', prop: 'area_id', label: T+'.area', options: [{ label: 'A区', value: 1 }] } ]
|
||||
* ]
|
||||
*
|
||||
* 【表单字段支持的属性】
|
||||
* 基础:type / prop / label / placeholder
|
||||
* input:inputType('textarea' | 'text')/ autosize / clearable
|
||||
* select:options / filterable
|
||||
* 通用:style(如 { width: '90%' })
|
||||
*
|
||||
* 【表单验证(rules)】
|
||||
* rules 与 el-form 的 rules 完全一致:
|
||||
* rules: {
|
||||
* code: [{ required: true, message: '请输入编码', trigger: 'blur' }]
|
||||
* }
|
||||
* 验证失败时,组件会用 $message.warning 显示第一条错误信息
|
||||
* message 字段支持 i18n key,父组件用 this.$t() 传值即可
|
||||
*/
|
||||
export default {
|
||||
name: 'PageDialogForm',
|
||||
props: {
|
||||
/**
|
||||
* 弹框显隐状态,父组件用 .sync 绑定
|
||||
* 例::visible.sync="dialogVisible"
|
||||
*/
|
||||
visible: { type: Boolean, default: false },
|
||||
|
||||
/**
|
||||
* 弹框标题,支持 i18n key(组件自动 $t() 翻译)
|
||||
*/
|
||||
title: { type: String, default: '' },
|
||||
|
||||
/**
|
||||
* 弹框宽度,可以是百分比或像素
|
||||
* 例:'35%' / '600px'
|
||||
*/
|
||||
width: { type: String, default: '35%' },
|
||||
|
||||
/**
|
||||
* 表单字段结构,二维数组
|
||||
* 外层数组每一个元素代表表单的一行
|
||||
* 内层数组每个元素代表该行中的一个字段
|
||||
*
|
||||
* 每个字段对象支持的属性见组件顶部的注释
|
||||
*/
|
||||
formCols: { type: Array, default: () => [] },
|
||||
|
||||
/**
|
||||
* 表单数据对象,key 与 formCols 中的 prop 一一对应
|
||||
* 编辑时父组件将 row 数据赋值给 formData
|
||||
*/
|
||||
formData: { type: Object, default: () => ({}) },
|
||||
|
||||
/**
|
||||
* 表单验证规则,与 el-form 的 rules 完全一致
|
||||
* 例:{ code: [{ required: true, message: '请输入编码', trigger: 'blur' }] }
|
||||
*/
|
||||
rules: { type: Object, default: () => ({}) },
|
||||
|
||||
/**
|
||||
* 表单 label 宽度,默认 '100px'
|
||||
*/
|
||||
labelWidth: { type: String, default: '100px' },
|
||||
|
||||
/**
|
||||
* 提交 loading 状态,提交期间显示转圈防止重复点击
|
||||
*/
|
||||
submitting: { type: Boolean, default: false },
|
||||
|
||||
/**
|
||||
* 确定按钮文字,默认 '确定',支持 i18n key
|
||||
*/
|
||||
confirmText: { type: String, default: '确定' },
|
||||
|
||||
/**
|
||||
* 取消按钮文字,默认 '取消',支持 i18n key
|
||||
*/
|
||||
cancelText: { type: String, default: '取消' }
|
||||
},
|
||||
computed: {
|
||||
/**
|
||||
* visible 代理:用于 .sync 双向绑定
|
||||
* get → 返回父组件传入的 visible
|
||||
* set → emit update:visible 通知父组件
|
||||
*/
|
||||
visibleProxy: {
|
||||
get () { return this.visible },
|
||||
set (val) { this.$emit('update:visible', val) }
|
||||
},
|
||||
|
||||
/**
|
||||
* 将二维 formCols 打平为一维数组,方便 v-for 遍历
|
||||
* 二维数组 → 一维数组:[ [{a}], [{b},{c}] ] → [a, b, c]
|
||||
*/
|
||||
flatFormCols () {
|
||||
return this.formCols.flat()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* 点击确定按钮
|
||||
* 1. 调用 el-form 的 validate 方法验证所有字段
|
||||
* 2. 验证通过 → emit('submit') 通知父组件执行提交逻辑
|
||||
* 3. 验证失败 → 用 $message.warning 显示第一条错误信息
|
||||
*
|
||||
* 注意:rules 中的 message 由父组件传入时已用 $t() 翻译
|
||||
* 这里只负责显示,翻译在父组件或 i18n 语言包中完成
|
||||
*/
|
||||
onSubmit () {
|
||||
this.$refs.form.validate((valid, invalidFields) => {
|
||||
if (!valid) {
|
||||
// 验证失败:取第一条错误信息提示用户
|
||||
const firstKey = Object.keys(invalidFields)[0]
|
||||
if (firstKey && invalidFields[firstKey].length) {
|
||||
const msg = invalidFields[firstKey][0].message
|
||||
this.$message.warning(this.$t(msg) || msg)
|
||||
}
|
||||
return
|
||||
}
|
||||
this.$emit('submit')
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* 关闭弹框
|
||||
* 1. 重置表单清除验证状态和输入内容
|
||||
* 2. emit update:visible 关闭弹框(.sync 机制)
|
||||
* 3. emit close 通知父组件执行清理逻辑
|
||||
*/
|
||||
onClose () {
|
||||
this.$refs.form && this.$refs.form.resetFields()
|
||||
this.$emit('update:visible', false)
|
||||
this.$emit('close')
|
||||
},
|
||||
|
||||
/**
|
||||
* 手动验证表单,返回 Promise<boolean>
|
||||
* 主要用于父组件需要自行控制验证时机的场景
|
||||
*
|
||||
* 用法:const ok = await this.$refs.dialogForm.validate()
|
||||
*/
|
||||
validate () {
|
||||
return new Promise(resolve => {
|
||||
this.$refs.form.validate(valid => resolve(valid))
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* 重置表单(清除验证状态和输入值)
|
||||
* 通常在打开新增弹框时调用
|
||||
*/
|
||||
reset () {
|
||||
this.$refs.form && this.$refs.form.resetFields()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 表单项间距固定 22px,不受 Element UI 状态覆盖 */
|
||||
/deep/ .el-form-item {
|
||||
margin-bottom: 22px !important;
|
||||
}
|
||||
/* 错误文字下沉到 margin 间隙中,不挤占表单项自身高度 */
|
||||
/deep/ .el-form-item__error {
|
||||
position: absolute;
|
||||
top: auto;
|
||||
bottom: -18px;
|
||||
}
|
||||
/* textarea 对齐顶部 */
|
||||
/deep/ .el-textarea {
|
||||
vertical-align: top;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user