1. 新增生产配置-工厂模型-工厂区域完整CRUD页面 2. 新增通用表格、弹窗表单、i18n工具组件 3. 升级sass-loader并修复Sass废弃警告 4. 添加文档记录Sass迁移修复细节
954 lines
30 KiB
Markdown
954 lines
30 KiB
Markdown
# è¡¨æ ¼ç»„ä»¶ä½¿ç”¨è¯´æ˜Ž
|
||
|
||
> 基于 `page-table` + `page-dialog-form` 的新一ä»?CRUD è¡¨æ ¼æ–¹æ¡ˆã€?
|
||
> æº<EFBFBD>ç <EFBFBD>ä½<EFBFBD>置:`src/components/page-table/`ã€<C3A3>`src/components/page-dialog-form/`ã€<C3A3>`src/composables/`
|
||
> 完整å<EFBFBD>¯è¿<EFBFBD>行示例:`src/views/production-master-data/factory-model/factory-area/index.vue`
|
||
|
||
---
|
||
|
||
## 目录
|
||
|
||
1. [快速开始(三æ¥èµ°ï¼‰](#1-快速开始三æ¥èµ°)
|
||
2. [完整示例代ç <EFBFBD>](#2-完整示例代ç <C3A7>)
|
||
3. [组件 API å<>‚考](#3-组件-api-å<>‚è€?
|
||
4. [Composable API å<>‚考](#4-composable-api-å<>‚è€?
|
||
5. [i18n 国际化方案](#5-i18n-国际化方�
|
||
6. [常用场景速查](#6-常用场景速查)
|
||
7. [路由é…<EFBFBD>ç½®](#7-路由é…<C3A9>ç½®)
|
||
8. [API 文件写法](#8-api-文件写法)
|
||
9. [旧代ç <C3A7>è¿<C3A8>移对照](#9-旧代ç <C3A7>è¿<C3A8>移对ç…?
|
||
10. [常è§<EFBFBD>问题排查](#10-常è§<C3A8>问题排查)
|
||
|
||
---
|
||
|
||
## 1. 快速开始(三æ¥èµ°ï¼‰
|
||
|
||
### 第一æ¥ï¼šå®šä¹‰åˆ—和按钮(`created()` ä¸ï¼‰
|
||
|
||
```js
|
||
import { useTableColumns } from '@/composables/useTableColumns'
|
||
import { useTableButtons } from '@/composables/useTableButtons'
|
||
import { i18nMixin } from '@/composables/useI18n'
|
||
|
||
export default {
|
||
mixins: [i18nMixin('page.模å<C2A1>—å<E28094>?二级模å<C2A1>—.三级模å<C2A1>—')],
|
||
|
||
created () {
|
||
// --- 列定�---
|
||
// å<>ªéœ€å£°æ˜Ž prop å’?label,idx 自动补é½<C3A9>
|
||
// prop: '_actions' 约定为æ“<C3A6>作列,自动渲æŸ?rowButtons
|
||
this.columns = useTableColumns([
|
||
{ prop: 'sort', label: this.key('sort'), width: 80 },
|
||
{ prop: 'code', label: this.key('code'), minWidth: 120 },
|
||
{ prop: 'name', label: this.key('name') },
|
||
{ prop: '_actions', label: this.key('operation'), width: 160, fixed: 'right' }
|
||
])
|
||
|
||
// --- 按钮定义 ---
|
||
// ä¸<C3A4>å†<C3A5>分开å†?buttonList / tableButtonList
|
||
const btns = useTableButtons({
|
||
toolbar: [
|
||
{ key: 'add', label: this.key('add'), icon: 'el-icon-plus',
|
||
type: 'primary', auth: '/xxx/create', onClick: this.openAdd }
|
||
],
|
||
row: [
|
||
{ key: 'edit', label: this.key('edit'), icon: 'el-icon-edit',
|
||
auth: '/xxx/edit', onClick: this.openEdit },
|
||
{ key: 'delete', label: this.key('delete'), icon: 'el-icon-delete',
|
||
color: 'danger', auth: '/xxx/delete', onClick: this.handleDelete }
|
||
]
|
||
}, this.$permission) // â†?第二个å<C2AA>‚æ•°ä¼ å…¥æ<C2A5>ƒé™<C3A9>æ ¡éªŒå‡½æ•?
|
||
this.toolbarButtons = btns.toolbarButtons
|
||
this.rowButtons = btns.rowButtons
|
||
}
|
||
}
|
||
```
|
||
|
||
### 第二æ¥ï¼šå†™æ¨¡æ<C2A1>¿ï¼ˆ`<template>` ä¸ï¼‰
|
||
|
||
```vue
|
||
<template>
|
||
<d2-container>
|
||
<!-- æ<EFBFBD>œç´¢åŒ?-->
|
||
<template #header>
|
||
<el-form :inline="true" size="mini">
|
||
<el-form-item :label="$t(key('code'))">
|
||
<el-input v-model="search.code" :placeholder="$t(key('enter_code'))"
|
||
clearable style="width:200px" @keyup.enter.native="onSearch" />
|
||
</el-form-item>
|
||
<el-form-item>
|
||
<el-button type="primary" icon="el-icon-search" @click="onSearch">
|
||
{{ $t(key('search')) }}
|
||
</el-button>
|
||
<el-button icon="el-icon-refresh" @click="onReset">
|
||
{{ $t(key('reset')) }}
|
||
</el-button>
|
||
</el-form-item>
|
||
</el-form>
|
||
</template>
|
||
|
||
<!-- è¡¨æ ¼ + 按钮æ ?+ 分页 -->
|
||
<page-table
|
||
:columns="columns"
|
||
:data="tableData"
|
||
:loading="loading"
|
||
:toolbar-buttons="toolbarButtons"
|
||
:row-buttons="rowButtons"
|
||
:pagination="pagination"
|
||
help-url="/help/your-page"
|
||
auto-height
|
||
@page-change="onPageChange"
|
||
@selection-change="onSelect"
|
||
/>
|
||
|
||
<!-- 新增/编辑弹框 -->
|
||
<page-dialog-form
|
||
ref="dialogForm"
|
||
:visible.sync="dialogVisible"
|
||
:title="dialogTitle"
|
||
width="35%"
|
||
:form-cols="formCols"
|
||
:form-data="formData"
|
||
:rules="rules"
|
||
:submitting="submitting"
|
||
:confirm-text="$t(key('confirm'))"
|
||
:cancel-text="$t(key('cancel'))"
|
||
@submit="onDialogSubmit"
|
||
@close="onDialogClose"
|
||
/>
|
||
</d2-container>
|
||
</template>
|
||
```
|
||
|
||
### 第三æ¥ï¼šå†™ä¸šåŠ¡æ–¹æ³•ï¼ˆå¢žåˆ æ”¹æŸ¥ï¼?
|
||
```js
|
||
methods: {
|
||
// 获å<C2B7>–列表数æ<C2B0>®
|
||
async fetchData () {
|
||
this.loading = true
|
||
try {
|
||
const res = await getList({
|
||
...this.search,
|
||
page_no: this.pagination.current,
|
||
page_size: this.pagination.size
|
||
})
|
||
this.tableData = res.data || []
|
||
this.pagination.total = res.count || 0
|
||
} finally { this.loading = false }
|
||
},
|
||
|
||
// æ<>œç´¢ / é‡<C3A9>ç½®
|
||
onSearch () { this.pagination.current = 1; this.fetchData() },
|
||
onReset () { this.search = { code: '', name: '' }; this.pagination.current = 1; this.fetchData() },
|
||
|
||
// 分页å<C2B5>˜åŒ–
|
||
onPageChange (page) {
|
||
this.pagination.current = page.current
|
||
this.pagination.size = page.size
|
||
this.fetchData()
|
||
},
|
||
|
||
// 新增:打开弹窗
|
||
openAdd () {
|
||
this.handleType = 'create'
|
||
this.dialogTitle = this.key('add_title')
|
||
this.$nextTick(() => {
|
||
this.$refs.dialogForm.reset()
|
||
this.resetForm()
|
||
this.dialogVisible = true
|
||
})
|
||
},
|
||
|
||
// 编辑:回填表å<C2A8>? openEdit (row) {
|
||
this.handleType = 'edit'
|
||
this.dialogTitle = this.key('edit_title')
|
||
this.editId = row.id
|
||
this.formData = { code: row.code, name: row.name }
|
||
this.dialogVisible = true
|
||
},
|
||
|
||
// æ<><C3A6>交表å<C2A8>•
|
||
async onDialogSubmit () {
|
||
this.submitting = true
|
||
try {
|
||
if (this.handleType === 'create') {
|
||
await createApi(this.formData)
|
||
} else {
|
||
await editApi({ ...this.formData, id: this.editId })
|
||
}
|
||
this.$message.success(this.$t(this.key('operation_success')))
|
||
this.dialogVisible = false
|
||
this.fetchData()
|
||
} finally { this.submitting = false }
|
||
},
|
||
|
||
// å…³é—弹窗
|
||
onDialogClose () { this.resetForm() },
|
||
|
||
// åˆ é™¤
|
||
async handleDelete (row) {
|
||
try {
|
||
await this.$confirm(
|
||
this.$t(this.key('confirm_delete')),
|
||
this.$t(this.key('tip')),
|
||
{ confirmButtonText: this.$t(this.key('confirm')),
|
||
cancelButtonText: this.$t(this.key('cancel')),
|
||
type: 'warning', closeOnClickModal: false }
|
||
)
|
||
await deleteApi({ id: [row.id] })
|
||
this.$message.success(this.$t(this.key('operation_success')))
|
||
this.fetchData()
|
||
} catch (e) { /* å<>–消或失è´?*/ }
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 2. 完整示例代ç <C3A7>
|
||
|
||
> ðŸ“<EFBFBD> `src/views/production-master-data/factory-model/factory-area/index.vue`
|
||
> 这是一ä¸?*å<>¯ç›´æŽ¥è¿<C3A8>行的完整 CRUD 页é<C2B5>¢**,包å<E280A6>«æ<C2AB>œç´¢æ <C3A6>ã€<C3A3>è¡¨æ ¼ã€<C3A3>分页ã€<C3A3>æ–°å¢?编辑弹框ã€<C3A3>åˆ é™¤ç¡®è®¤ã€?
|
||
### 模æ<C2A1>¿éƒ¨åˆ†
|
||
|
||
```vue
|
||
<template>
|
||
<d2-container>
|
||
<template #header>
|
||
<div class="search-bar">
|
||
<el-form :inline="true" size="mini">
|
||
<el-form-item :label="$t(key('code'))">
|
||
<el-input v-model="search.code" :placeholder="$t(key('enter_code'))"
|
||
clearable style="width:200px" @keyup.enter.native="onSearch" />
|
||
</el-form-item>
|
||
<el-form-item :label="$t(key('name'))">
|
||
<el-input v-model="search.name" :placeholder="$t(key('enter_name'))"
|
||
clearable style="width:200px" @keyup.enter.native="onSearch" />
|
||
</el-form-item>
|
||
<el-form-item>
|
||
<el-button type="primary" icon="el-icon-search" @click="onSearch">
|
||
{{ $t(key('search')) }}
|
||
</el-button>
|
||
<el-button icon="el-icon-refresh" @click="onReset">
|
||
{{ $t(key('reset')) }}
|
||
</el-button>
|
||
</el-form-item>
|
||
</el-form>
|
||
</div>
|
||
</template>
|
||
|
||
<page-table
|
||
ref="pageTable"
|
||
:columns="columns"
|
||
:data="tableData"
|
||
:loading="loading"
|
||
:toolbar-buttons="toolbarButtons"
|
||
:row-buttons="rowButtons"
|
||
:pagination="pagination"
|
||
help-url="/help/factory-area"
|
||
auto-height
|
||
@page-change="onPageChange"
|
||
@selection-change="onSelect"
|
||
/>
|
||
|
||
<page-dialog-form
|
||
ref="dialogForm"
|
||
:visible.sync="dialogVisible"
|
||
:title="dialogTitle"
|
||
width="35%"
|
||
:form-cols="formCols"
|
||
:form-data="formData"
|
||
:rules="rules"
|
||
:submitting="submitting"
|
||
:confirm-text="$t(key('confirm'))"
|
||
:cancel-text="$t(key('cancel'))"
|
||
@submit="onDialogSubmit"
|
||
@close="onDialogClose"
|
||
/>
|
||
</d2-container>
|
||
</template>
|
||
```
|
||
|
||
### Script 部分
|
||
|
||
```js
|
||
import { useTableColumns } from '@/composables/useTableColumns'
|
||
import { useTableButtons } from '@/composables/useTableButtons'
|
||
import { i18nMixin } from '@/composables/useI18n'
|
||
import { getFactoryAreaList, createFactoryArea, editFactoryArea, deleteFactoryArea }
|
||
from '@/api/production-master-data/factory-area'
|
||
import PageTable from '@/components/page-table'
|
||
import PageDialogForm from '@/components/page-dialog-form'
|
||
|
||
export default {
|
||
name: 'factory-area',
|
||
components: { PageTable, PageDialogForm },
|
||
mixins: [i18nMixin('page.production_master_data.factory_model.factory_area')],
|
||
|
||
data () {
|
||
const t = this.$t.bind(this)
|
||
const k = (s) => t(this.key(s))
|
||
return {
|
||
loading: false,
|
||
submitting: false,
|
||
tableData: [],
|
||
selectedRows: [],
|
||
dialogVisible: false,
|
||
dialogTitle: '',
|
||
editId: '',
|
||
handleType: 'create',
|
||
search: { code: '', name: '' },
|
||
pagination: { current: 1, size: 10, total: 0 },
|
||
formData: { code: '', name: '', remark: '' },
|
||
rules: {
|
||
code: [
|
||
{ required: true, message: k('enter_code'), trigger: 'blur' },
|
||
{ min: 1, max: 100, message: k('remark_length'), trigger: 'blur' }
|
||
],
|
||
name: [
|
||
{ required: true, message: k('enter_name'), trigger: 'blur' },
|
||
{ min: 1, max: 100, message: k('remark_length'), trigger: 'blur' }
|
||
]
|
||
},
|
||
columns: [],
|
||
toolbarButtons: [],
|
||
rowButtons: [],
|
||
formCols: [
|
||
[
|
||
{ type: 'input', prop: 'code',
|
||
label: k('code'), placeholder: k('enter_code'),
|
||
clearable: true, style: { width: '90%' } }
|
||
],
|
||
[
|
||
{ type: 'input', prop: 'name',
|
||
label: k('name'), placeholder: k('enter_name'),
|
||
clearable: true, style: { width: '90%' } }
|
||
],
|
||
[
|
||
{ type: 'input', prop: 'remark', inputType: 'textarea',
|
||
autosize: { minRows: 2, maxRows: 6 },
|
||
label: k('remark'), placeholder: k('remark_required'),
|
||
clearable: true, style: { width: '90%' } }
|
||
]
|
||
]
|
||
}
|
||
},
|
||
|
||
created () {
|
||
this.columns = useTableColumns([
|
||
{ prop: 'sort', label: this.key('sort'), width: 80 },
|
||
{ prop: 'code', label: this.key('code'), minWidth: 120 },
|
||
{ prop: 'name', label: this.key('name'), minWidth: 120 },
|
||
{ prop: 'remark', label: this.key('remark') },
|
||
{ prop: '_actions', label: this.key('operation'), width: 160, fixed: 'right' }
|
||
])
|
||
const btns = useTableButtons({
|
||
toolbar: [
|
||
{ key: 'add', label: this.key('add'), icon: 'el-icon-plus', type: 'primary',
|
||
auth: '/production_master_data/factory_model/factory_area/create',
|
||
onClick: this.openAdd }
|
||
],
|
||
row: [
|
||
{ key: 'edit', label: this.key('edit'), icon: 'el-icon-edit',
|
||
auth: '/production_master_data/factory_model/factory_area/edit',
|
||
onClick: this.openEdit },
|
||
{ key: 'delete', label: this.key('delete'), icon: 'el-icon-delete',
|
||
color: 'danger',
|
||
auth: '/production_master_data/factory_model/factory_area/delete',
|
||
onClick: this.handleDelete }
|
||
]
|
||
}, this.$permission)
|
||
this.toolbarButtons = btns.toolbarButtons
|
||
this.rowButtons = btns.rowButtons
|
||
this.fetchData()
|
||
},
|
||
|
||
methods: {
|
||
async fetchData () {
|
||
this.loading = true
|
||
try {
|
||
const res = await getFactoryAreaList({
|
||
...this.search, page_no: this.pagination.current, page_size: this.pagination.size
|
||
})
|
||
this.tableData = res.data || []
|
||
this.pagination.total = res.count || 0
|
||
} finally { this.loading = false }
|
||
},
|
||
|
||
onSearch () { this.pagination.current = 1; this.fetchData() },
|
||
onReset () { this.search = { code: '', name: '' }; this.pagination.current = 1; this.fetchData() },
|
||
onPageChange (page) {
|
||
this.pagination.current = page.current
|
||
this.pagination.size = page.size
|
||
this.fetchData()
|
||
},
|
||
onSelect (rows) { this.selectedRows = rows },
|
||
|
||
resetForm () { this.formData = { code: '', name: '', remark: '' }; this.editId = '' },
|
||
|
||
openAdd () {
|
||
this.handleType = 'create'
|
||
this.dialogTitle = this.key('add_title')
|
||
this.$nextTick(() => {
|
||
this.$refs.dialogForm && this.$refs.dialogForm.reset()
|
||
this.resetForm()
|
||
this.dialogVisible = true
|
||
})
|
||
},
|
||
|
||
openEdit (row) {
|
||
this.handleType = 'edit'
|
||
this.dialogTitle = this.key('edit_title')
|
||
this.editId = row.id
|
||
this.formData = { code: row.code, name: row.name, remark: row.remark || '' }
|
||
this.dialogVisible = true
|
||
},
|
||
|
||
async onDialogSubmit () {
|
||
this.submitting = true
|
||
try {
|
||
if (this.handleType === 'create') {
|
||
await createFactoryArea(this.formData)
|
||
} else {
|
||
await editFactoryArea({ ...this.formData, id: this.editId })
|
||
}
|
||
this.$message.success(this.$t(this.key('operation_success')))
|
||
this.dialogVisible = false
|
||
this.fetchData()
|
||
} finally { this.submitting = false }
|
||
},
|
||
|
||
onDialogClose () { this.resetForm() },
|
||
|
||
async handleDelete (row) {
|
||
try {
|
||
await this.$confirm(
|
||
this.$t(this.key('confirm_delete')),
|
||
this.$t(this.key('tip')),
|
||
{ confirmButtonText: this.$t(this.key('confirm')),
|
||
cancelButtonText: this.$t(this.key('cancel')),
|
||
type: 'warning', closeOnClickModal: false }
|
||
)
|
||
await deleteFactoryArea({ id: [row.id] })
|
||
this.$message.success(this.$t(this.key('operation_success')))
|
||
this.pagination.current = Math.min(
|
||
this.pagination.current,
|
||
Math.ceil((this.pagination.total - 1) / this.pagination.size) || 1
|
||
)
|
||
this.fetchData()
|
||
} catch (e) { /* å<>–æ¶ˆåˆ é™¤ / 请求失败ä¸<C3A4>处ç<E2809E>?*/ }
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 3. 组件 API å<>‚è€?
|
||
### 3.1 `page-table` â€?è¡¨æ ¼ + 按钮æ ?+ 分页
|
||
|
||
#### Props
|
||
|
||
| Prop | 类型 | 默认�| 说明 |
|
||
|------|------|--------|------|
|
||
| `columns` | `Array` | `[]` | 列定义,ç”?`useTableColumns()` 生æˆ<C3A6> |
|
||
| `data` | `Array` | `[]` | è¡¨æ ¼è¡Œæ•°æ<C2B0>?|
|
||
| `loading` | `Boolean` | `false` | 是å<C2AF>¦æ˜¾ç¤º loading é<>®ç½© |
|
||
| `height` | `String/Number` | â€?| è¡¨æ ¼é«˜åº¦ã€‚ä¸<C3A4>ä¼ åˆ™éš<C3A9>å†…å®¹æ’‘å¼€ï¼›ä¼ å…·ä½“æ•°å€¼å›ºå®šé«˜åº¦ï¼›ä¼?`'auto'` å<>¯ç”¨è‡ªé€‚应 |
|
||
| `auto-height` | `Boolean` | `false` | å<>¯ç”¨é«˜åº¦è‡ªé€‚åº”ï¼Œè¡¨æ ¼è‡ªåŠ¨å¡«æ»¡å<C2A1>¯ç”¨ç©ºé—?|
|
||
| `border` | `Boolean` | `true` | 是å<C2AF>¦å¸¦è¾¹æ¡?|
|
||
| `row-key` | `String` | `'id'` | 行唯一 key |
|
||
| `toolbar-buttons` | `Array` | `[]` | 顶部工具æ <C3A6>按钮,ç”?`useTableButtons()` 生æˆ<C3A6> |
|
||
| `row-buttons` | `Array` | `[]` | 行内æ“<C3A6>作按钮,由 `useTableButtons()` 生æˆ<C3A6> |
|
||
| `pagination` | `Object` | `null` | 分页å<C2B5>‚æ•° `{ current, size, total }`ï¼Œä¼ äº†æ‰<EFBFBD>显示分页 |
|
||
| `table-attrs` | `Object` | `{}` | é¢<C3A9>外é€<C3A9>ä¼ ç»?`el-table` 的属æ€?|
|
||
| `table-listeners` | `Object` | `{}` | é¢<C3A9>外é€<C3A9>ä¼ ç»?`el-table` 的事ä»?|
|
||
| `help-url` | `String` | `''` | 帮助文档跳转 URLã€‚ä¼ äº†æ‰<C3A6>显示工具æ <C3A6>å<EFBFBD>³ä¾§çš„é—®å<C2AE>·æŒ‰é’®ï¼Œç‚¹å‡»æ–°çª—å<E28094>£æ‰“å¼€ |
|
||
| `help-text` | `String` | `'帮助'` | 帮助按钮文å—,支æŒ?i18n key(组件自åŠ?`$t()` 翻译ï¼?|
|
||
|
||
#### 事件
|
||
|
||
| 事件å<C2B6>?| å<>‚æ•° | 说明 |
|
||
|--------|------|------|
|
||
| `@page-change` | `{ current, size, total }` | 分页å<C2B5>˜åŒ–(切æ<E280A1>¢é¡µç ?æ<>¡æ•°ï¼?|
|
||
| `@selection-change` | `rows: Array` | 选ä¸è¡Œå<C592>˜åŒ?|
|
||
| `@sort-change` | é€<C3A9>ä¼ el-table 原生事件 | 排åº<C3A5>å<EFBFBD>˜åŒ– |
|
||
|
||
#### æ<>’æ§½
|
||
|
||
| æ<>’æ§½å<C2BD>?| 作用åŸ?| 说明 |
|
||
|--------|--------|------|
|
||
| `#col-{prop}` | `{ row, index }` | è‡ªå®šä¹‰åˆ—çš„æ¸²æŸ“ï¼ˆåˆ—å®šä¹‰ä¸ prop 需è®?`slot: true`ï¼?|
|
||
| `#toolbar-extra` | â€?| 工具æ <C3A6>åŒºåŸŸè¿½åŠ è‡ªå®šä¹‰å†…å®¹ |
|
||
| `#empty` | â€?| è¡¨æ ¼ç©ºæ•°æ<C2B0>®æ—¶çš„å<E2809E> ä½?|
|
||
| `#append` | â€?| è¡¨æ ¼æœ€å<E282AC>Žä¸€è¡Œå<C592>Žè¿½åŠ |
|
||
| `#extra` | â€?| 页é<C2B5>¢åº•éƒ¨è¿½åŠ åŒºåŸŸ |
|
||
|
||
#### 列定义规�
|
||
```js
|
||
// 普通列
|
||
{ prop: 'code', label: 'ç¼–ç <C3A7>', minWidth: 120 }
|
||
|
||
// æ“<C3A6>作列(约定:prop === '_actions'ï¼?{ prop: '_actions', label: 'æ“<C3A6>作', width: 160, fixed: 'right' }
|
||
|
||
// 自定义æ<E280B0>’槽列(slot: trueï¼?{ prop: 'status', label: '状æ€?, slot: true, width: 100 }
|
||
|
||
// å¤<C3A5>选框åˆ?+ åº<C3A5>å<EFBFBD>·åˆ—(通过 useTableColumns 第二个å<C2AA>‚数)
|
||
useTableColumns([...], { selectionWidth: 55, indexWidth: 60 })
|
||
```
|
||
|
||
---
|
||
|
||
### 3.2 `page-dialog-form` â€?å¢žåˆ æ”¹æŸ¥å¼¹æ¡†
|
||
|
||
#### Props
|
||
|
||
| Prop | 类型 | 默认�| 说明 |
|
||
|------|------|--------|------|
|
||
| `visible` | `Boolean` | `false` | 弹框显éš<C3A9>,使ç”?`.sync` 修饰ç¬?|
|
||
| `title` | `String` | `''` | å¼¹æ¡†æ ‡é¢˜ï¼Œæ”¯æŒ?i18n key |
|
||
| `width` | `String` | `'35%'` | 弹框宽度 |
|
||
| `form-cols` | `Array` | `[]` | 表å<C2A8>•å—æ®µç»“构(二维数组,è§<C3A8>下方) |
|
||
| `form-data` | `Object` | `{}` | 表å<C2A8>•æ•°æ<C2B0>®å¯¹è±¡ |
|
||
| `rules` | `Object` | `{}` | æ ¡éªŒè§„åˆ™ï¼Œä¸Ž `el-form` rules 一è‡?|
|
||
| `label-width` | `String` | `'100px'` | label 宽度 |
|
||
| `submitting` | `Boolean` | `false` | æ<><C3A6>交 loading 状æ€?|
|
||
| `confirm-text` | `String` | `'确定'` | ç¡®å®šæŒ‰é’®æ–‡å— |
|
||
| `cancel-text` | `String` | `'å<>–消'` | å<>–æ¶ˆæŒ‰é’®æ–‡å— |
|
||
|
||
#### 事件
|
||
|
||
| 事件å<C2B6>?| 说明 |
|
||
|--------|------|
|
||
| `@submit` | 表å<C2A8>•验è¯<C3A8>通过å<E280A1>Žè§¦å<C2A6>‘,父组件执行æ<C592><C3A6>交逻辑 |
|
||
| `@close` | 弹框关é—å<C2AD>Žè§¦å<C2A6>?|
|
||
|
||
#### 方法(通过 ref 调用�
|
||
| 方法 | 说明 |
|
||
|------|------|
|
||
| `reset()` | é‡<C3A9>置表å<C2A8>• |
|
||
| `validate()` | 手动验è¯<C3A8>,返å›?`Promise<boolean>` |
|
||
|
||
#### formCols æ•°æ<C2B0>®ç»“æž„
|
||
|
||
**注æ„<C3A6>:需åœ?`data()` ä¸ç”¨ `k(prop)` æ<><C3A6>å‰<C3A5>完æˆ<C3A6> i18n 翻译**,ä¸<C3A4>è¦<C3A8>ä¼ raw keyã€?
|
||
```js
|
||
// 例:两个普通输入框 + 一个多行文本框
|
||
formCols: [
|
||
[
|
||
{ type: 'input', prop: 'code',
|
||
label: k('code'), placeholder: k('enter_code'),
|
||
clearable: true, style: { width: '90%' } }
|
||
],
|
||
[
|
||
{ type: 'input', prop: 'name',
|
||
label: k('name'), placeholder: k('enter_name'),
|
||
clearable: true, style: { width: '90%' } }
|
||
],
|
||
[
|
||
{ type: 'input', prop: 'remark', inputType: 'textarea',
|
||
autosize: { minRows: 2, maxRows: 6 },
|
||
label: k('remark'), placeholder: k('remark_required'),
|
||
clearable: true, style: { width: '90%' } }
|
||
]
|
||
]
|
||
```
|
||
|
||
**å—æ®µæ”¯æŒ<C3A6>的属性:**
|
||
|
||
| 属�| 适用类型 | 说明 |
|
||
|------|---------|------|
|
||
| `type` | 全部 | `'input'`(文本输入)/ `'select'`(下拉) |
|
||
| `prop` | 全部 | 绑定 `formData` ä¸çš„ key |
|
||
| `label` | 全部 | 表å<C2A8>•é¡¹æ ‡ç?|
|
||
| `placeholder` | 全部 | å<> ä½<C3A4>æ<EFBFBD><C3A6>示 |
|
||
| `inputType` | `input` | `'textarea'` 多行 / ä¸<C3A4>ä¼ ä¸ºæ™®é€?text |
|
||
| `autosize` | `input` | textarea çš?`{ minRows, maxRows }` |
|
||
| `clearable` | 全部 | 是å<C2AF>¦å<C2A6>¯æ¸…空,默认 `true` |
|
||
| `style` | 全部 | æ ·å¼<C3A5>对象 |
|
||
| `options` | `select` | 选项数组 `[{ label, value }]` |
|
||
| `filterable` | `select` | 是å<C2AF>¦å<C2A6>¯æ<C2AF>œç´¢ï¼Œé»˜è®¤ `true` |
|
||
|
||
---
|
||
|
||
## 4. Composable API å<>‚è€?
|
||
### 4.1 `useTableColumns(rawColumns, options?)`
|
||
|
||
**作用**:消除手动分é…?`idx` åº<C3A5>å<EFBFBD>·ï¼Œè‡ªåŠ¨è¯†åˆ«æ“<C3A6>作列和æ<C592>’槽列ã€?
|
||
| å<>‚æ•° | 类型 | 默认å€?| 说明 |
|
||
|------|------|--------|------|
|
||
| `rawColumns` | `Array` | �| 列定�|
|
||
| `options.selectionWidth` | `number` | `55` | å¤<C3A5>é€‰æ¡†åˆ—å®½ï¼Œä¼ `0` éš<C3A9>è—<C3A8> |
|
||
| `options.indexWidth` | `number` | `0` | åº<C3A5>å<EFBFBD>·åˆ—å®½ï¼Œä¼ `0` éš<C3A9>è—<C3A8> |
|
||
|
||
```js
|
||
const columns = useTableColumns([
|
||
{ prop: 'code', label: 'ç¼–ç <C3A7>', width: 120 },
|
||
{ prop: 'name', label: 'å<><C3A5>ç§°' },
|
||
{ prop: '_actions', label: 'æ“<C3A6>作', width: 160, fixed: 'right' } // â†?æ“<C3A6>作åˆ?])
|
||
```
|
||
|
||
**内部约定**:`prop === '_actions'` è‡ªåŠ¨æ ‡è®°ä¸?`slot: '_actions'`,由 `page-table` 渲染为æ“<C3A6>作列ã€?
|
||
---
|
||
|
||
### 4.2 `useTableButtons(options, permissionCheck?)`
|
||
|
||
**作用**:统一生æˆ<C3A6>工具æ <C3A6>按钮和行内æ“<C3A6>作按钮,自动注入æ<C2A5>ƒé™<C3A9>æ ¡éªŒç»“æžœã€?
|
||
| å<>‚æ•° | 类型 | 说明 |
|
||
|------|------|------|
|
||
| `options.toolbar` | `Array` | 顶部按钮列表 |
|
||
| `options.row` | `Array` | 行内按钮列表 |
|
||
| `permissionCheck` | `Function` | æ<>ƒé™<C3A9>函数,通常ä¼?`this.$permission` |
|
||
|
||
**返回�*:`{ toolbarButtons, rowButtons }`
|
||
|
||
**æŒ‰é’®å—æ®µï¼?*
|
||
|
||
| å—æ®µ | toolbar | row | 说明 |
|
||
|------|:---:|:---:|------|
|
||
| `key` | âœ?| âœ?| å”¯ä¸€æ ‡è¯† |
|
||
| `label` | �| �| 显示文本 |
|
||
| `icon` | âœ?| âœ?| Element UI å›¾æ ‡ |
|
||
| `type` | �| �| `primary`/`success`/`warning`/`danger` |
|
||
| `color` | â€?| âœ?| `'danger'` 使文å—å<E28094>˜çº?|
|
||
| `auth` | âœ?| âœ?| æ<>ƒé™<C3A9> key |
|
||
| `cssStyle` | âœ?| âœ?| è‡ªå®šä¹‰æ ·å¼?|
|
||
| `onClick` | �| �| 点击回调 |
|
||
| `hasPermission` | âœ?| âœ?| **自动注入**,由æ<C2B1>ƒé™<C3A9>函数计算 |
|
||
| `needSelection` | âœ?| â€?| 需选ä¸è¡Œæ‰<C3A6>能点å‡?|
|
||
|
||
```js
|
||
const btns = useTableButtons({
|
||
toolbar: [
|
||
{ key: 'add', label: '新增', icon: 'el-icon-plus', type: 'primary',
|
||
auth: '/xxx/create', onClick: this.openAdd }
|
||
],
|
||
row: [
|
||
{ key: 'edit', label: '编辑', icon: 'el-icon-edit',
|
||
auth: '/xxx/edit', onClick: this.openEdit },
|
||
{ key: 'delete', label: 'åˆ é™¤', icon: 'el-icon-delete',
|
||
color: 'danger', auth: '/xxx/delete', onClick: this.handleDelete }
|
||
]
|
||
}, this.$permission)
|
||
```
|
||
|
||
---
|
||
|
||
### 4.3 `i18nMixin(prefix)`
|
||
|
||
**作用**:注å…?`key(suffix)` å’?`ckey(suffix)` 两个方法,消除æ¯<C3A6>个页é<C2B5>¢æ‰‹å†?`T` 常é‡<C3A9>å’?`tkey` 方法ã€?
|
||
| 方法 | 返回�| 说明 |
|
||
|------|--------|------|
|
||
| `key(suffix)` | `prefix.suffix` | 当å‰<C3A5>页é<C2B5>¢çš?i18n key |
|
||
| `ckey(suffix)` | `page.common.suffix` | 公共 i18n key(如"帮助"ã€?确定"ã€?å<>–消"ï¼?|
|
||
|
||
| å<>‚æ•° | 类型 | 说明 |
|
||
|------|------|------|
|
||
| `prefix` | `String` | 该页é<C2B5>¢çš„ i18n key å‰<C3A5>缀,如 `'page.production_master_data.factory_model.factory_area'` |
|
||
|
||
```js
|
||
import { i18nMixin } from '@/composables/useI18n'
|
||
|
||
export default {
|
||
mixins: [i18nMixin('page.production_master_data.factory_model.factory_area')],
|
||
// æ¤å<C2A4>Žæ¨¡æ<C2A1>¿ä¸ç”¨ $t(key('code')) 访问当å‰<C3A5>页é<C2B5>¢çš„ç¿»è¯? // ç”?$t(ckey('help')) 访问公共翻译 'page.common.help'
|
||
// JS ä¸ç”¨ this.$t(this.key('code')) / this.$t(this.ckey('help'))
|
||
}
|
||
```
|
||
|
||
**`data()` ä¸ç¿»è¯‘文本的技å·?*ï¼?
|
||
```js
|
||
data () {
|
||
const t = this.$t.bind(this) // 绑定 i18n 翻译函数
|
||
const k = (s) => t(this.key(s)) // 当å‰<C3A5>页é<C2B5>¢ç¿»è¯‘:key + translate
|
||
const ck = (s) => t(this.ckey(s)) // 公共翻译:ckey + translate
|
||
|
||
return {
|
||
formCols: [
|
||
[{ label: k('code'), placeholder: k('enter_code') }] // 页é<C2B5>¢çº?key
|
||
],
|
||
helpText: ck('help') // 公共 key
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 5. i18n 国际化方�
|
||
### 5.1 è¯è¨€åŒ…æ–‡ä»?
|
||
| è¯è¨€ | 文件路径 |
|
||
|------|---------|
|
||
| ç®€ä½“ä¸æ–?| `src/locales/zh-chs.json` |
|
||
| ç¹<C3A7>体䏿–‡ | `src/locales/zh-cht.json` |
|
||
| 英文 | `src/locales/en.json` |
|
||
| 日文 | `src/locales/ja.json` |
|
||
|
||
### 5.2 è¯è¨€åŒ…结æž?
|
||
æŒ?`一级模å<C2A1>?â†?二级模å<C2A1>— â†?三级模å<C2A1>—` 三层嵌套ï¼?
|
||
```json
|
||
{
|
||
"page": {
|
||
"production_master_data": {
|
||
"factory_model": {
|
||
"factory_area": {
|
||
"search": "查询",
|
||
"reset": "é‡<C3A9>ç½®",
|
||
"code": "所区编ç ?,
|
||
"name": "所区å<EFBFBD><EFBFBD>ç§?,
|
||
"add": "��,
|
||
"edit": "ç¼?è¾?,
|
||
"delete": "��,
|
||
"enter_code": "请输入所区编ç ?,
|
||
"enter_name": "请输入所区å<C2BA><C3A5>ç§?,
|
||
"add_title": "新增所�,
|
||
"edit_title": "编辑所�,
|
||
"confirm": "确定",
|
||
"cancel": "å<EFBFBD>–消",
|
||
"operation_success": "æ“<EFBFBD>作æˆ<EFBFBD>功"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
### 5.3 组件自动翻译
|
||
|
||
`page-table` �`page-dialog-form` 内置 `$t()`�
|
||
- **�label** �自动翻译
|
||
- **按钮 label** �自动翻译
|
||
- **表å<C2A8>• label / placeholder** â†?自动翻译
|
||
- **弹框 title / confirm / cancel** �自动翻译
|
||
|
||
å› æ¤é¡µé<EFBFBD>¢å<EFBFBD>ªéœ€ä¼ å…¥ i18n key å—符串,组件渲染时自动替æ<C2BF>¢ä¸ºå½“å‰<C3A5>è¯è¨€çš„æ–‡æœ¬ã€?
|
||
### 5.4 页é<C2B5>¢ä¸æ‰‹åŠ¨ç¿»è¯?
|
||
æ<EFBFBD>œç´¢åŒºã€<EFBFBD>æ ¡éªŒæ¶ˆæ<EFBFBD>¯ã€<EFBFBD>`$confirm` ç?UI æ–‡å—需手动ç”?`$t()`ï¼?
|
||
```vue
|
||
<!-- 模æ<EFBFBD>¿ä¸?â€?key() 返回 i18n key -->
|
||
<el-form-item :label="$t(key('code'))">
|
||
<el-input :placeholder="$t(key('enter_code'))" />
|
||
</el-form-item>
|
||
<el-button>{{ $t(key('search')) }}</el-button>
|
||
```
|
||
|
||
```js
|
||
// JS �this.$message.success(this.$t(this.key('operation_success')))
|
||
this.$confirm(this.$t(this.key('confirm_delete')), this.$t(this.key('tip')), { ... })
|
||
```
|
||
|
||
---
|
||
|
||
## 6. 常用场景速查
|
||
|
||
### 场景 1:自定义列渲染(状�tag�
|
||
åˆ—å®šä¹‰åŠ å…?`slot: true`ï¼?
|
||
```js
|
||
useTableColumns([
|
||
{ prop: 'status', label: '状�, slot: true, width: 100 }
|
||
])
|
||
```
|
||
|
||
模æ<EFBFBD>¿ä¸ç”¨ `#col-status` æ<>’æ§½ï¼?
|
||
```vue
|
||
<page-table :columns="columns" :data="tableData">
|
||
<template #col-status="{ row }">
|
||
<el-tag :type="row.status === 1 ? 'success' : 'danger'">
|
||
{{ row.status === 1 ? 'å<>¯ç”¨' : 'ç¦<C3A7>用' }}
|
||
</el-tag>
|
||
</template>
|
||
</page-table>
|
||
```
|
||
|
||
### 场景 2:工具æ <C3A6>è¿½åŠ è‡ªå®šä¹‰æŒ‰é’?
|
||
```vue
|
||
<page-table :columns="columns" :toolbar-buttons="toolbarButtons">
|
||
<template #toolbar-extra>
|
||
<el-button size="mini" icon="el-icon-printer" @click="print">打å<EFBFBD>°</el-button>
|
||
</template>
|
||
</page-table>
|
||
```
|
||
|
||
### 场景 3:å¤<C3A5>选框åˆ?+ åº<C3A5>å<EFBFBD>·åˆ?
|
||
```js
|
||
useTableColumns(
|
||
[
|
||
{ prop: 'code', label: 'ç¼–ç <C3A7>' },
|
||
{ prop: 'name', label: 'å<><C3A5>ç§°' }
|
||
],
|
||
{ selectionWidth: 55, indexWidth: 60 }
|
||
)
|
||
```
|
||
|
||
### 场景 4:带下拉选择的表å<C2A8>?
|
||
```js
|
||
formCols: [
|
||
[
|
||
{ type: 'select', prop: 'area_id',
|
||
label: k('area'), placeholder: k('select_area'),
|
||
options: [{ label: 'A厂区', value: 1 }, { label: 'B厂区', value: 2 }],
|
||
clearable: true, style: { width: '90%' } }
|
||
]
|
||
]
|
||
```
|
||
|
||
### 场景 5:批é‡<C3A9>åˆ é™¤ï¼ˆå·¥å…·æ ?+ 需è¦<C3A8>选ä¸è¡Œï¼‰
|
||
|
||
```js
|
||
useTableButtons({
|
||
toolbar: [
|
||
{ key: 'batchDelete', label: '批é‡<C3A9>åˆ é™¤', icon: 'el-icon-delete',
|
||
type: 'danger', auth: '/xxx/batch-delete',
|
||
needSelection: true, // â†?需è¦<C3A8>选ä¸è¡Œæ‰<C3A6>能点å‡? onClick: this.batchDelete }
|
||
]
|
||
})
|
||
```
|
||
|
||
### 场景 6ï¼šè¡¨æ ¼è‡ªé€‚åº”é«˜åº¦
|
||
|
||
å<EFBFBD>ªéœ€åŠ?`auto-height` 属性:
|
||
|
||
```vue
|
||
<page-table ... auto-height />
|
||
```
|
||
|
||
è¡¨æ ¼é«˜åº¦ä¼šè‡ªåŠ¨å¡«æ»?`d2-container` çš?body 区域剩余空间,窗å<E28094>?resize / ä¾§æ <C3A6>折å<CB9C> 时自动é‡<C3A9>ç®—ã€?
|
||
### 场景 7:帮助按�
|
||
ç»?`help-url` ä¼ å€¼å<C2BC>³å<C2B3>¯åœ¨å·¥å…·æ <C3A6>最å<E282AC>³ä¾§æ˜¾ç¤ºé—®å<C2AE>·å¸®åŠ©æŒ‰é’®ï¼Œç‚¹å‡»åœ¨æ–°çª—å<E28094>£æ‰“开帮助文档ï¼?
|
||
```vue
|
||
<page-table ... help-url="/help/factory-area" :help-text="$t(ckey('help'))" />
|
||
```
|
||
|
||
按钮文å—通过 `help-text` prop 支æŒ<C3A6> i18n。推è<C2A8><C3A8>使用公å…?key `page.common.help`(模æ<EFBFBD>¿ä¸å†?`$t(ckey('help'))`),所有页é<EFBFBD>¢å…±äº«ï¼Œæ— 需æ¯<EFBFBD>个模å<EFBFBD>—é‡<EFBFBD>å¤<EFBFBD>定义ã€?
|
||
ä¸<EFBFBD>ä¼ `help-url` æˆ–ä¼ ç©ºå—符串则ä¸<C3A4>显示帮助按钮ã€?
|
||
---
|
||
|
||
## 7. 路由é…<C3A9>ç½®
|
||
|
||
```js
|
||
// src/router/modules/production-master-data.js
|
||
import layoutHeaderAside from '@/layout/header-aside'
|
||
|
||
const meta = { auth: true }
|
||
const _import = require('@/libs/util.import.' + process.env.NODE_ENV)
|
||
|
||
export default {
|
||
path: '/production_master_data',
|
||
component: layoutHeaderAside,
|
||
children: (pre => [
|
||
{
|
||
path: 'factory_model/factory_area',
|
||
name: `${pre}factory_model-factory_area`,
|
||
meta: { ...meta, cache: true, title: '工厂区域' },
|
||
component: _import('production-master-data/factory-model/factory-area')
|
||
}
|
||
])('production_master_data-')
|
||
}
|
||
```
|
||
|
||
之å<EFBFBD>Žåœ?`src/router/routes.js` ä¸å¼•入该模å<C2A1>—å¹¶åŠ å…?`frameIn` 数组ï¼?
|
||
```js
|
||
import productionConfiguration from './modules/production-master-data'
|
||
|
||
const frameIn = [
|
||
// ... 其他路由 ...
|
||
productionConfiguration
|
||
]
|
||
```
|
||
|
||
---
|
||
|
||
## 8. API 文件写法
|
||
|
||
```js
|
||
// src/api/production-master-data/factory-area.js
|
||
import { request } from '@/api/_service'
|
||
|
||
const BASE = 'production_master_data/factory_model/factory_area/'
|
||
|
||
function apiParams (method, data = {}) {
|
||
return {
|
||
method: `production_master_data_factory_model_factory_area_${method}`,
|
||
platform: 'background',
|
||
...data
|
||
}
|
||
}
|
||
|
||
export function getFactoryAreaList (data) {
|
||
return request({
|
||
url: BASE + 'list',
|
||
method: 'get',
|
||
params: apiParams('list', data)
|
||
})
|
||
}
|
||
|
||
export function createFactoryArea (data) {
|
||
return request({
|
||
url: BASE + 'create',
|
||
method: 'post',
|
||
data: apiParams('create', data)
|
||
})
|
||
}
|
||
|
||
export function editFactoryArea (data) {
|
||
return request({
|
||
url: BASE + 'edit',
|
||
method: 'put',
|
||
data: apiParams('edit', data)
|
||
})
|
||
}
|
||
|
||
export function deleteFactoryArea (data) {
|
||
return request({
|
||
url: BASE + 'delete',
|
||
method: 'delete',
|
||
data: apiParams('delete', data)
|
||
})
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 9. 旧代ç <C3A7>è¿<C3A8>移对ç…?
|
||
| 旧写�| 新写�|
|
||
|--------|--------|
|
||
| 手动构建 `columns: [{ idx: 0, attrs: { prop, label } }]` | `useTableColumns([{ prop, label }])` |
|
||
| `buttonList: [...]` + `tableButtonList: [...]` 分两�| `useTableButtons({ toolbar: [...], row: [...] })` |
|
||
| `<sct-base-table>` + `<sct-base-dialog>` + `<SctBaseForm>` 三层 | `<page-table>` + `<page-dialog-form>` 两层 |
|
||
| `<sct-form-search>` 组件 | 原生 `<el-form :inline>` |
|
||
| `<sct-back-to-top>` 组件 | 需è¦<C3A8>æ—¶è‡ªè¡Œæ·»åŠ |
|
||
| 分页需è¦<C3A8>é¢<C3A9>å¤?`<page-footer>` | `page-table` 内置分页 |
|
||
| æ¯<C3A6>个页é<C2B5>¢å®šä¹‰ `T` 常é‡<C3A9> + `tkey()` 方法 | `mixins: [i18nMixin(prefix)]`,使ç”?`this.key('xxx')` |
|
||
| 表å<C2A8>•å—æ®µå?i18n raw key,å<C3A5>组件翻译 | `data()` ä¸ç”¨ `k(prop)` æ<><C3A6>å‰<C3A5>翻译 |
|
||
|
||
---
|
||
|
||
## 10. 常è§<C3A8>问题排查
|
||
|
||
### Q1:弹框打开å<E282AC>Žä¸<C3A4>显示内容ï¼?
|
||
确认 `formCols` ä¸çš„ `label` å’?`placeholder` 是å<C2AF>¦åœ?`data()` 阶段已翻译。å<C3A5>组件 `page-dialog-form` 会调ç”?`$t()` 处ç<E2809E>† label,但建议åœ?`data()` 阶段就用 `k()` æ<><C3A6>å‰<C3A5>翻译好,é<C592>¿å…<C3A5> webpack HMR 缓å˜é—®é¢˜ã€?
|
||
### Q2ï¼šè¡¨æ ¼é«˜åº¦è‡ªé€‚åº”ä¸<C3A4>生效?
|
||
|
||
确认 `d2-container` çš?body 区域高度被æ£ç¡®çº¦æ<C2A6>Ÿï¼ˆflex 填充)。如æž?`d2-container` 本身æœ?`overflow: auto` 或其他高度约æ<C2A6>Ÿé—®é¢˜ï¼Œ`page-table` çš?`height: 100%` 会失效。给 `d2-container` çš?body 部分è®?`overflow: hidden` 通常å<C2B8>¯è§£å†³ã€?
|
||
### Q3:æ<C5A1>ƒé™<C3A9>æ ¡éªŒæœ‰æŒ‰é’®ä¸<C3A4>显示?
|
||
|
||
`useTableButtons` 的第二个å<C2AA>‚æ•°å¿…é¡»ä¼?`this.$permission`。如果项目没有注册这个全局方法,å<EFBFBD>¯ä»¥åœ¨ `useTableButtons` ä¸ä¼ 入一个返å›?`true` çš„å<E2809E> ä½<C3A4>函数:`() => true`ã€?
|
||
### Q4:新增一æ<E282AC>¡å<C2A1>Žé¡µç <C3A7>ä¸<C3A4>跳回第一页?
|
||
|
||
åœ?`onDialogSubmit` æˆ<C3A6>功å<C5B8>Žè°ƒç”?`this.fetchData()` å‰<C3A5>ï¼Œå¦‚æžœåˆ é™¤äº†å½“å‰<C3A5>页最å<E282AC>Žä¸€è¡Œå¯¼è‡?`total - 1` 超出范围,需手动修æ£é¡µç <C3A7>ï¼?
|
||
```js
|
||
this.pagination.current = Math.min(
|
||
this.pagination.current,
|
||
Math.ceil((this.pagination.total - 1) / this.pagination.size) || 1
|
||
)
|
||
```
|
||
|
||
### Q5:表å<C2A8>•验è¯<C3A8>ä¸<C3A4>æ<EFBFBD><C3A6>示错误文å—ï¼?
|
||
确认 `page-dialog-form` çš?`<style>` å<>—å˜åœ¨ï¼ˆé»˜è®¤ 22px `margin-bottom` + `position: absolute` 错误文å—)。如果表å<C2A8>•é¡¹ä¹‹é—´è¢«å…¶ä»–æ ·å¼<C3A5>覆盖,检查是å<C2AF>¦æœ‰å…¨å±€ CSS é‡<C3A9>ç½®äº?`.el-form-item` çš?marginã€?
|
||
### Q6:如何新增一ä¸?CRUD 页é<C2B5>¢æœ€å¿«ï¼Ÿ
|
||
|
||
1. å¤<C3A5>制 `src/views/production-master-data/factory-model/factory-area/index.vue`
|
||
2. 替æ<C2BF>¢ API 引用(`import { getList, create, edit, ... } from '@/api/xxx'`ï¼?3. 修改 `i18nMixin` å<>‚æ•°ã€<C3A3>`columns`ã€<EFBFBD>`formCols`ã€<EFBFBD>`rules`
|
||
4. åœ?`zh-chs.json` å’?`en.json` 䏿·»åŠ è¯è¨€åŒ?5. æ·»åŠ è·¯ç”±é…<C3A9>ç½®
|
||
|
||
å¹³å<EFBFBD>‡ 10 分钟å<C5B8>³å<C2B3>¯å®Œæˆ<C3A6>ä¸€ä¸ªæ ‡å‡?CRUD 页é<C2B5>¢ã€?
|