前端报错提示优化
This commit is contained in:
parent
d463fbf71a
commit
8a60e2cb00
|
|
@ -1,5 +1,5 @@
|
||||||
import { flushPromises, mount } from '@vue/test-utils'
|
import { flushPromises, mount } from '@vue/test-utils'
|
||||||
import ElementPlus, { ElMessage } from 'element-plus'
|
import ElementPlus, { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
import { beforeEach, describe, expect, test, vi } from 'vitest'
|
import { beforeEach, describe, expect, test, vi } from 'vitest'
|
||||||
import Materials from './Materials.vue'
|
import Materials from './Materials.vue'
|
||||||
import { codeRulesApi, materialCategoriesApi, unitsApi } from '../api/masterData'
|
import { codeRulesApi, materialCategoriesApi, unitsApi } from '../api/masterData'
|
||||||
|
|
@ -84,6 +84,7 @@ async function pickCategories(wrapper, { major = 1, minor = 12 } = {}) {
|
||||||
|
|
||||||
describe('新增物料时的编码联动', () => {
|
describe('新增物料时的编码联动', () => {
|
||||||
let warnSpy
|
let warnSpy
|
||||||
|
let alertSpy
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
materialCategoriesApi.list.mockResolvedValue({ data: categories })
|
materialCategoriesApi.list.mockResolvedValue({ data: categories })
|
||||||
|
|
@ -92,6 +93,7 @@ describe('新增物料时的编码联动', () => {
|
||||||
materialsStore.createMaterial.mockReset()
|
materialsStore.createMaterial.mockReset()
|
||||||
materialsStore.updateMaterial.mockReset()
|
materialsStore.updateMaterial.mockReset()
|
||||||
warnSpy = vi.spyOn(ElMessage, 'warning').mockImplementation(() => {})
|
warnSpy = vi.spyOn(ElMessage, 'warning').mockImplementation(() => {})
|
||||||
|
alertSpy = vi.spyOn(ElMessageBox, 'alert').mockResolvedValue('confirm')
|
||||||
})
|
})
|
||||||
|
|
||||||
test('还没选小类时,编码框提示先选分类', async () => {
|
test('还没选小类时,编码框提示先选分类', async () => {
|
||||||
|
|
@ -119,11 +121,11 @@ describe('新增物料时的编码联动', () => {
|
||||||
|
|
||||||
expect(codeRulesApi.next).toHaveBeenCalledWith(12)
|
expect(codeRulesApi.next).toHaveBeenCalledWith(12)
|
||||||
expect(codeInput(wrapper).element.value).toBe('A010201')
|
expect(codeInput(wrapper).element.value).toBe('A010201')
|
||||||
expect(wrapper.find('.material-code-hint').exists()).toBe(false)
|
expect(alertSpy).not.toHaveBeenCalled()
|
||||||
wrapper.unmount()
|
wrapper.unmount()
|
||||||
})
|
})
|
||||||
|
|
||||||
test('小类没有规则时清空编码并提示手工填写', async () => {
|
test('小类没有规则时清空编码,并弹出需要点掉的提示框', async () => {
|
||||||
codeRulesApi.next.mockResolvedValue({ data: { available: false, code: '' } })
|
codeRulesApi.next.mockResolvedValue({ data: { available: false, code: '' } })
|
||||||
|
|
||||||
const wrapper = mountMaterials()
|
const wrapper = mountMaterials()
|
||||||
|
|
@ -132,7 +134,11 @@ describe('新增物料时的编码联动', () => {
|
||||||
await pickCategories(wrapper)
|
await pickCategories(wrapper)
|
||||||
|
|
||||||
expect(codeInput(wrapper).element.value).toBe('')
|
expect(codeInput(wrapper).element.value).toBe('')
|
||||||
expect(wrapper.find('.material-code-hint').text()).toContain('请手工填写')
|
expect(alertSpy).toHaveBeenCalledWith(
|
||||||
|
'该小类未配置编码规则,请手工填写物料编码',
|
||||||
|
'提示',
|
||||||
|
expect.objectContaining({ confirmButtonText: '知道了' })
|
||||||
|
)
|
||||||
wrapper.unmount()
|
wrapper.unmount()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -193,6 +199,22 @@ describe('新增物料时的编码联动', () => {
|
||||||
wrapper.unmount()
|
wrapper.unmount()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('名称为空时保存被拦下并提示补名称', async () => {
|
||||||
|
codeRulesApi.next.mockResolvedValue({ data: { available: true, code: 'A010201' } })
|
||||||
|
|
||||||
|
const wrapper = mountMaterials()
|
||||||
|
await flushPromises()
|
||||||
|
await openAddDialog(wrapper)
|
||||||
|
await pickCategories(wrapper)
|
||||||
|
|
||||||
|
await findButton(wrapper, '保存').trigger('click')
|
||||||
|
await flushPromises()
|
||||||
|
|
||||||
|
expect(materialsStore.createMaterial).not.toHaveBeenCalled()
|
||||||
|
expect(warnSpy).toHaveBeenCalledWith('请填写物料名称')
|
||||||
|
wrapper.unmount()
|
||||||
|
})
|
||||||
|
|
||||||
test('小类没有规则且没填编码时,保存被拦下并提示补编码', async () => {
|
test('小类没有规则且没填编码时,保存被拦下并提示补编码', async () => {
|
||||||
codeRulesApi.next.mockResolvedValue({ data: { available: false, code: '' } })
|
codeRulesApi.next.mockResolvedValue({ data: { available: false, code: '' } })
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -79,9 +79,8 @@
|
||||||
/>
|
/>
|
||||||
<el-button v-if="isCreating" link type="primary" @click="regenerateMaterialCode">重新生成</el-button>
|
<el-button v-if="isCreating" link type="primary" @click="regenerateMaterialCode">重新生成</el-button>
|
||||||
</div>
|
</div>
|
||||||
<p v-if="isCreating && codeHint" class="material-code-hint">{{ codeHint }}</p>
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="名称">
|
<el-form-item label="名称" required>
|
||||||
<el-input v-model="form.name" />
|
<el-input v-model="form.name" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="规格">
|
<el-form-item label="规格">
|
||||||
|
|
@ -159,7 +158,7 @@ import { useAuthStore } from '../stores/auth'
|
||||||
import BaseTable from '../components/business/BaseTable.vue'
|
import BaseTable from '../components/business/BaseTable.vue'
|
||||||
import DeleteConfirm from '../components/business/DeleteConfirm.vue'
|
import DeleteConfirm from '../components/business/DeleteConfirm.vue'
|
||||||
import AlertTip from '../components/business/AlertTip.vue'
|
import AlertTip from '../components/business/AlertTip.vue'
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
import { createMaterialForm, toMaterialPayload } from './materialForm'
|
import { createMaterialForm, toMaterialPayload } from './materialForm'
|
||||||
import { codeRulesApi, materialCategoriesApi, unitsApi } from '../api/masterData'
|
import { codeRulesApi, materialCategoriesApi, unitsApi } from '../api/masterData'
|
||||||
import { getMajorCategories, getMinorCategories, resolveCategoryPath } from '../utils/materialCategories'
|
import { getMajorCategories, getMinorCategories, resolveCategoryPath } from '../utils/materialCategories'
|
||||||
|
|
@ -187,7 +186,6 @@ const categories = ref([])
|
||||||
const units = ref([])
|
const units = ref([])
|
||||||
// 用户是否手改过编码:没改过时切分类要按新规则重算,改过就以用户的值为准。
|
// 用户是否手改过编码:没改过时切分类要按新规则重算,改过就以用户的值为准。
|
||||||
const codeEdited = ref(false)
|
const codeEdited = ref(false)
|
||||||
const codeHint = ref('')
|
|
||||||
const isCreating = computed(() => dialogTitle.value === '新增物料')
|
const isCreating = computed(() => dialogTitle.value === '新增物料')
|
||||||
// 小类还没选时编码框必然是空的,提示用户先把分类选完:选完才会自动填入编码,
|
// 小类还没选时编码框必然是空的,提示用户先把分类选完:选完才会自动填入编码,
|
||||||
// 或者由"请手工填写"的提示接手。
|
// 或者由"请手工填写"的提示接手。
|
||||||
|
|
@ -227,16 +225,14 @@ function syncMajorCategory() {
|
||||||
// 大类换了,原来的小类规则不再适用;同样不动用户手改过的编码
|
// 大类换了,原来的小类规则不再适用;同样不动用户手改过的编码
|
||||||
if (!codeEdited.value) {
|
if (!codeEdited.value) {
|
||||||
form.material_code = ''
|
form.material_code = ''
|
||||||
codeHint.value = ''
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 按当前小类的编码规则填编码:有规则自动填,没规则就清空并提示手工填写。
|
// 按当前小类的编码规则填编码:有规则自动填,没规则就清空并弹窗让用户手工填写。
|
||||||
async function generateMaterialCode() {
|
async function generateMaterialCode() {
|
||||||
if (!isCreating.value) return
|
if (!isCreating.value) return
|
||||||
if (!form.category_id) {
|
if (!form.category_id) {
|
||||||
form.material_code = ''
|
form.material_code = ''
|
||||||
codeHint.value = ''
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
|
@ -244,15 +240,18 @@ async function generateMaterialCode() {
|
||||||
const { available, code } = response.data || {}
|
const { available, code } = response.data || {}
|
||||||
if (available) {
|
if (available) {
|
||||||
form.material_code = code
|
form.material_code = code
|
||||||
codeHint.value = ''
|
|
||||||
} else {
|
} else {
|
||||||
form.material_code = ''
|
form.material_code = ''
|
||||||
codeHint.value = '该小类未配置编码规则,请手工填写物料编码'
|
// 用模态框而不是会自动消失的 toast:这条提示要等用户点掉才走
|
||||||
|
ElMessageBox.alert('该小类未配置编码规则,请手工填写物料编码', '提示', {
|
||||||
|
confirmButtonText: '知道了',
|
||||||
|
type: 'warning'
|
||||||
|
// ESC 或点遮罩关掉时 alert 会 reject,这里不做处理
|
||||||
|
}).catch(() => {})
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// 预览失败不该挡住填表:留空让用户手工填写,接口层已经弹过错误提示
|
// 预览失败不该挡住填表:留空让用户手工填写,失败原因由拦截器提示
|
||||||
form.material_code = ''
|
form.material_code = ''
|
||||||
codeHint.value = '未能获取编码规则,请手工填写物料编码'
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -305,7 +304,6 @@ function showAddModal() {
|
||||||
dialogTitle.value = '新增物料'
|
dialogTitle.value = '新增物料'
|
||||||
Object.assign(form, createMaterialForm())
|
Object.assign(form, createMaterialForm())
|
||||||
codeEdited.value = false
|
codeEdited.value = false
|
||||||
codeHint.value = ''
|
|
||||||
imagePreview.value = ''
|
imagePreview.value = ''
|
||||||
if (imageInput.value) imageInput.value.value = ''
|
if (imageInput.value) imageInput.value.value = ''
|
||||||
dialogVisible.value = true
|
dialogVisible.value = true
|
||||||
|
|
@ -322,7 +320,6 @@ function showEditModal(row) {
|
||||||
Object.assign(form, createMaterialForm(row, categories.value))
|
Object.assign(form, createMaterialForm(row, categories.value))
|
||||||
// 编辑时编码只读,标记成"已改过",避免切小类时被规则覆盖
|
// 编辑时编码只读,标记成"已改过",避免切小类时被规则覆盖
|
||||||
codeEdited.value = true
|
codeEdited.value = true
|
||||||
codeHint.value = ''
|
|
||||||
imagePreview.value = ''
|
imagePreview.value = ''
|
||||||
if (imageInput.value) imageInput.value.value = ''
|
if (imageInput.value) imageInput.value.value = ''
|
||||||
dialogVisible.value = true
|
dialogVisible.value = true
|
||||||
|
|
@ -338,6 +335,11 @@ async function handleSave() {
|
||||||
ElMessage.warning('请填写物料编码')
|
ElMessage.warning('请填写物料编码')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
// 后端对名称也标了 binding:"required",前端先拦下来,避免抛回原始校验错误
|
||||||
|
if (!form.name.trim()) {
|
||||||
|
ElMessage.warning('请填写物料名称')
|
||||||
|
return
|
||||||
|
}
|
||||||
saveLoading.value = true
|
saveLoading.value = true
|
||||||
try {
|
try {
|
||||||
const formData = toMaterialPayload(form)
|
const formData = toMaterialPayload(form)
|
||||||
|
|
@ -444,21 +446,14 @@ function formatDate(dateStr) {
|
||||||
:deep(.el-table__cell.operation-column) {
|
:deep(.el-table__cell.operation-column) {
|
||||||
border-left: 1px solid var(--el-table-border-color) !important;
|
border-left: 1px solid var(--el-table-border-color) !important;
|
||||||
}
|
}
|
||||||
|
/* 一行里横排「输入框 + 重新生成」;作为表单项内容区的 flex item,flex:1 让它
|
||||||
|
占满整行 —— 不设的话会收缩到 el-input 的固有宽度(约 177px),比表单里其它
|
||||||
|
字段窄一截。 */
|
||||||
.material-code-field {
|
.material-code-field {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
}
|
|
||||||
/* 不设 flex 时这一行会收缩到 el-input 的固有宽度(约 177px),比表单里其它
|
|
||||||
字段窄一截;换成 flex:1 后输入框占满整行剩余宽度,与其它字段对齐。 */
|
|
||||||
.material-code-field {
|
|
||||||
flex: 1;
|
flex: 1;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
}
|
}
|
||||||
.material-code-hint {
|
|
||||||
margin: 4px 0 0;
|
|
||||||
color: #e6a23c;
|
|
||||||
font-size: 12px;
|
|
||||||
line-height: 1.5;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user