You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

455 lines
14 KiB
Vue

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<Dialog :title="dialogTitle" v-model="dialogVisible">
<el-form ref="formRef" :model="formData" :rules="formRules" label-width="100px" v-loading="formLoading">
<el-form-item label="名称" prop="name">
<el-input v-model="formData.name" placeholder="请输入名称" />
</el-form-item>
<el-form-item label="类型" prop="type">
<el-radio-group v-model="formData.type">
<el-radio v-for="dict in getDictOptions('mes_zj_schema_type')" :key="dict.value" :label="dict.value">
{{ dict.label }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item v-if="isSampleType" label="抽检方式" prop="sampleMethod">
<el-radio-group v-model="formData.sampleMethod">
<el-radio v-for="dict in getDictOptions('mes_zj_schema_sample_method')" :key="dict.value" :label="dict.value">
{{ dict.label }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item v-if="isSampleType" label="值" prop="val">
<template v-if="isRateMethod">
<el-input-number v-model="rateVal" :min="0" class="!w-160px" />
<span class="ml-8px">%</span>
</template>
<template v-else-if="isGapMethod">
<span>每间隔</span>
<el-input-number v-model="gapInterval" :min="1" class="!w-120px mx-8px" :controls="false" />
<span>抽取</span>
<el-input-number v-model="gapCount" :min="1" class="!w-120px ml-8px" :controls="false" />
</template>
<template v-else>
<el-input v-model="formData.val" placeholder="请输入值" />
</template>
</el-form-item>
<el-form-item label="关联项目" prop="item">
<el-row :gutter="8" class="w-full">
<el-col :span="22">
<el-input :model-value="displayItemNames" placeholder="请选择检验项目" readonly />
</el-col>
<el-col :span="2" class="text-right">
<el-button type="primary" @click="openItemDialog">选择</el-button>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="formData.remark" placeholder="请输入备注" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="submitForm" type="primary" :disabled="formLoading"> </el-button>
<el-button @click="dialogVisible = false"> </el-button>
</template>
</Dialog>
<el-dialog v-model="itemDialogVisible" title="选择检验项目" width="920px" draggable>
<div v-loading="itemLoading">
<div class="formula-config-picklist">
<div class="formula-config-panel">
<div class="formula-config-panel__header">
<div>来源</div>
<div class="formula-config-panel__meta">{{ filteredSourceItems.length }}</div>
</div>
<div class="formula-config-panel__filter">
<el-input v-model="sourceKeyword" placeholder="筛选" clearable />
</div>
<el-table
class="formula-config-panel__table"
:data="filteredSourceItems"
height="440"
row-key="key"
@selection-change="handleSourceSelectionChange"
>
<el-table-column type="selection" width="44" />
<el-table-column label="名称" prop="name" />
<el-table-column label="检验类型">
<template #default="scope">
{{ scope.row.zjTypeName }}
</template>
</el-table-column>
</el-table>
</div>
<div class="formula-config-actions">
<el-button type="primary" :disabled="!sourceCheckedKeys.length" @click="addToTarget">
&gt;&gt;
</el-button>
<el-button :disabled="!targetCheckedKeys.length" @click="removeFromTarget">
&lt;&lt;
</el-button>
</div>
<div class="formula-config-panel">
<div class="formula-config-panel__header">
<div>目标</div>
<div class="formula-config-panel__meta">{{ filteredTargetItems.length }}</div>
</div>
<div class="formula-config-panel__filter">
<el-input v-model="targetKeyword" placeholder="筛选" clearable />
</div>
<el-table
class="formula-config-panel__table"
:data="filteredTargetItems"
height="440"
row-key="key"
@selection-change="handleTargetSelectionChange"
>
<el-table-column type="selection" width="44" />
<el-table-column label="名称" prop="name" />
<el-table-column label="检验类型">
<template #default="scope">
{{ scope.row.zjTypeName }}
</template>
</el-table-column>
</el-table>
</div>
</div>
</div>
<template #footer>
<el-button @click="itemDialogVisible = false">取 消</el-button>
<el-button type="primary" @click="confirmSelectItems">确 定</el-button>
</template>
</el-dialog>
</template>
<script setup lang="ts">
import { getDictOptions } from '@/utils/dict'
import { ZjSchemaApi, ZjSchemaVO } from '@/api/mes/zjschema'
import { ZjItemApi, ZjItemVO } from '@/api/mes/zjitem'
/** */
defineOptions({ name: 'ZjSchemaForm' })
const { t } = useI18n() //
const message = useMessage() //
const dialogVisible = ref(false) //
const dialogTitle = ref('') //
const formLoading = ref(false) // 12
const formType = ref('') // create - update -
const formData = ref({
type: 'All' as string | undefined,
name: undefined as string | undefined,
remark: undefined as string | undefined,
sampleMethod: undefined as string | undefined,
val: undefined as string | undefined,
item: undefined as string | undefined,
})
const rateVal = ref<number | undefined>(undefined)
const gapInterval = ref<number | undefined>(undefined)
const gapCount = ref<number | undefined>(undefined)
const itemDialogVisible = ref(false)
const itemLoading = ref(false)
const itemList = ref<ZjItemVO[]>([])
const selectedItemIds = ref<number[]>([])
const sourceKeyword = ref('')
const targetKeyword = ref('')
const sourceCheckedKeys = ref<number[]>([])
const targetCheckedKeys = ref<number[]>([])
const formRules = reactive({
name: [{ required: true, message: '名称不能为空', trigger: 'blur' }],
type: [{ required: true, message: '类型不能为空', trigger: 'change' }],
sampleMethod: [{ required: true, message: '抽检方式不能为空', trigger: 'change' }],
val: [{ validator: (_: any, __: any, callback: any) => validateVal(callback), trigger: 'blur' }],
})
const formRef = ref() // 表单 Ref
const isSampleType = computed(() => {
const type = formData.value.type
return type === 'Sample'
})
const isRateMethod = computed(() => formData.value.sampleMethod === 'Rate')
const isGapMethod = computed(() => formData.value.sampleMethod === 'Gap')
const displayItemNames = computed(() => {
if (!selectedItemIds.value.length) return ''
if (!itemList.value.length) {
return formData.value.item ? String(formData.value.item) : ''
}
const map = new Map(itemList.value.map((item) => [item.id, item.name]))
const names = selectedItemIds.value
.map((id) => map.get(id))
.filter((name) => name)
return names.join(',')
})
const allItems = computed(() =>
itemList.value.map((item) => {
const typeName = item.zjTypeName || ''
const name = item.name || ''
const label = typeName ? `${typeName}-${name}` : name
return {
...item,
key: item.id,
label,
}
})
)
const sourceItems = computed(() =>
allItems.value.filter((item) => !selectedItemIds.value.includes(item.id))
)
const targetItems = computed(() =>
allItems.value.filter((item) => selectedItemIds.value.includes(item.id))
)
const filteredSourceItems = computed(() => {
const keyword = sourceKeyword.value.trim()
if (!keyword) return sourceItems.value
return sourceItems.value.filter((item) => item.label?.includes(keyword))
})
const filteredTargetItems = computed(() => {
const keyword = targetKeyword.value.trim()
if (!keyword) return targetItems.value
return targetItems.value.filter((item) => item.label?.includes(keyword))
})
const validateVal = (callback: any) => {
if (!isSampleType.value) {
callback()
return
}
if (isRateMethod.value) {
if (rateVal.value === undefined || rateVal.value === null) {
callback(new Error('值不能为空'))
return
}
} else if (isGapMethod.value) {
if (!gapInterval.value || !gapCount.value) {
callback(new Error('值不能为空'))
return
}
} else if (!formData.value.val) {
callback(new Error('值不能为空'))
return
}
callback()
}
watch(
() => formData.value.type,
(val) => {
if (val === 'Sample') {
if (!formData.value.sampleMethod) {
const opts = getDictOptions('mes_zj_schema_sample_method')
if (opts && opts.length > 0) {
formData.value.sampleMethod = String(opts[0].value)
}
}
return
}
formData.value.sampleMethod = undefined
formData.value.val = undefined
rateVal.value = undefined
gapInterval.value = undefined
gapCount.value = undefined
formRef.value?.clearValidate?.(['sampleMethod', 'val'])
}
)
const openItemDialog = async () => {
itemDialogVisible.value = true
if (!itemList.value.length) {
itemLoading.value = true
try {
itemList.value = await ZjItemApi.getZjItemList()
} finally {
itemLoading.value = false
}
}
initSelectedItemIds()
sourceCheckedKeys.value = []
targetCheckedKeys.value = []
}
const initSelectedItemIds = () => {
if (!formData.value.item) {
selectedItemIds.value = []
return
}
const ids = String(formData.value.item)
.split(',')
.map((v) => Number(v.trim()))
.filter((v) => !Number.isNaN(v))
selectedItemIds.value = ids
}
const confirmSelectItems = () => {
formData.value.item = selectedItemIds.value.join(',')
itemDialogVisible.value = false
}
const handleSourceSelectionChange = (rows: any[]) => {
sourceCheckedKeys.value = rows.map((row: any) => row.id)
}
const handleTargetSelectionChange = (rows: any[]) => {
targetCheckedKeys.value = rows.map((row: any) => row.id)
}
const addToTarget = () => {
if (!sourceCheckedKeys.value.length) return
const set = new Set(selectedItemIds.value)
sourceCheckedKeys.value.forEach((id) => set.add(id))
selectedItemIds.value = Array.from(set)
sourceCheckedKeys.value = []
}
const removeFromTarget = () => {
if (!targetCheckedKeys.value.length) return
const removeSet = new Set(targetCheckedKeys.value)
selectedItemIds.value = selectedItemIds.value.filter((id) => !removeSet.has(id))
targetCheckedKeys.value = []
}
/** 打开弹窗 */
const open = async (type: string, id?: number) => {
dialogVisible.value = true
dialogTitle.value = t('action.' + type)
formType.value = type
resetForm()
// 修改时,设置数据
if (id) {
formLoading.value = true
try {
const data = await ZjSchemaApi.getZjSchema(id)
formData.value = data
initSelectedItemIds()
if (formData.value.type === 'Sample') {
if (formData.value.sampleMethod === 'Rate') {
const n = Number(formData.value.val)
rateVal.value = Number.isNaN(n) ? undefined : n
} else if (formData.value.sampleMethod === 'Gap' && typeof formData.value.val === 'string') {
const parts = formData.value.val.split(',').map((v: string) => v.trim())
const interval = Number(parts[0])
const count = Number(parts[1])
gapInterval.value = Number.isNaN(interval) ? undefined : interval
gapCount.value = Number.isNaN(count) ? undefined : count
}
}
} finally {
formLoading.value = false
}
}
}
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
/** 提交表单 */
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
const submitForm = async () => {
// 校验表单
await formRef.value.validate()
if (isSampleType.value) {
if (isRateMethod.value) {
formData.value.val =
rateVal.value === undefined || rateVal.value === null ? undefined : String(rateVal.value)
} else if (isGapMethod.value) {
if (gapInterval.value && gapCount.value) {
formData.value.val = `${gapInterval.value},${gapCount.value}`
} else {
formData.value.val = undefined
}
}
} else {
formData.value.sampleMethod = ''
formData.value.val = ''
}
// 提交请求
formLoading.value = true
try {
const data = formData.value as unknown as ZjSchemaVO
if (formType.value === 'create') {
await ZjSchemaApi.createZjSchema(data)
message.success(t('common.createSuccess'))
} else {
await ZjSchemaApi.updateZjSchema(data)
message.success(t('common.updateSuccess'))
}
dialogVisible.value = false
// 发送操作成功的事件
emit('success')
} finally {
formLoading.value = false
}
}
/** 重置表单 */
const resetForm = () => {
formData.value = {
type: 'All',
name: undefined,
remark: undefined,
sampleMethod: undefined,
val: undefined,
item: undefined,
}
rateVal.value = undefined
gapInterval.value = undefined
gapCount.value = undefined
selectedItemIds.value = []
sourceKeyword.value = ''
targetKeyword.value = ''
sourceCheckedKeys.value = []
targetCheckedKeys.value = []
formRef.value?.resetFields()
}
</script>
<style scoped>
.formula-config-picklist {
display: flex;
width: 100%;
}
.formula-config-panel {
width: calc((100% - 96px) / 2);
overflow: hidden;
background: var(--el-bg-color);
border: 1px solid var(--el-border-color);
border-radius: 4px;
}
.formula-config-panel__header {
display: flex;
padding: 10px 12px;
font-weight: 600;
border-bottom: 1px solid var(--el-border-color);
justify-content: space-between;
}
.formula-config-panel__meta {
font-weight: 400;
color: var(--el-text-color-secondary);
}
.formula-config-panel__filter {
padding: 10px 12px;
border-bottom: 1px solid var(--el-border-color);
}
.formula-config-panel__table {
width: 100%;
}
.formula-config-actions {
display: flex;
width: 96px;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 12px;
}
</style>