139 lines
3.9 KiB
Vue
139 lines
3.9 KiB
Vue
|
|
<template>
|
||
|
|
<div class="nutrient-editor">
|
||
|
|
<el-form label-width="100px">
|
||
|
|
<el-form-item v-for="(nutrient, index) in localNutrients" :key="index" :label="nutrient.nutrient_name">
|
||
|
|
<el-input-number v-model="nutrient.value" :min="0" controls-position="right" style="width: 150px;"></el-input-number>
|
||
|
|
<el-button type="danger" @click="removeNutrient(index)" style="margin-left: 10px;">
|
||
|
|
<el-icon><Delete /></el-icon>
|
||
|
|
</el-button>
|
||
|
|
</el-form-item>
|
||
|
|
</el-form>
|
||
|
|
|
||
|
|
<div class="add-nutrient-section">
|
||
|
|
<el-select v-model="newNutrientId" placeholder="选择要添加的营养素" filterable>
|
||
|
|
<el-option v-for="item in availableNutrients" :key="item.id" :label="item.name" :value="item.id"></el-option>
|
||
|
|
</el-select>
|
||
|
|
<el-button type="primary" @click="addNutrient" style="margin-left: 10px;">添加</el-button>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="dialog-footer">
|
||
|
|
<el-button @click="handleCancel">取消</el-button>
|
||
|
|
<el-button type="primary" @click="handleSave">保存</el-button>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</template>
|
||
|
|
|
||
|
|
<script>
|
||
|
|
import { ref, watch, onMounted } from 'vue';
|
||
|
|
import { FeedApi } from '../../api/feed';
|
||
|
|
import { ElMessage } from 'element-plus';
|
||
|
|
import { Delete } from '@element-plus/icons-vue';
|
||
|
|
|
||
|
|
export default {
|
||
|
|
name: 'NutrientEditor',
|
||
|
|
components: {
|
||
|
|
Delete,
|
||
|
|
},
|
||
|
|
props: {
|
||
|
|
initialData: {
|
||
|
|
type: Object,
|
||
|
|
required: true,
|
||
|
|
},
|
||
|
|
},
|
||
|
|
emits: ['save', 'cancel'],
|
||
|
|
setup(props, { emit }) {
|
||
|
|
const localNutrients = ref([]);
|
||
|
|
const availableNutrients = ref([]);
|
||
|
|
const newNutrientId = ref(null);
|
||
|
|
|
||
|
|
// 监听 initialData 的变化,深拷贝以避免直接修改 props
|
||
|
|
watch(() => props.initialData, (newData) => {
|
||
|
|
if (newData && newData.raw_material_nutrients) {
|
||
|
|
localNutrients.value = JSON.parse(JSON.stringify(newData.raw_material_nutrients));
|
||
|
|
} else {
|
||
|
|
localNutrients.value = [];
|
||
|
|
}
|
||
|
|
}, { immediate: true, deep: true });
|
||
|
|
|
||
|
|
// 获取所有可用的营养素列表
|
||
|
|
const fetchAllNutrients = async () => {
|
||
|
|
try {
|
||
|
|
// 通常我们会获取一个完整的、未分页的列表用于选择
|
||
|
|
const response = await FeedApi.getNutrients({ page_size: 999 });
|
||
|
|
if (response.data) {
|
||
|
|
availableNutrients.value = response.data.list;
|
||
|
|
}
|
||
|
|
} catch (error) {
|
||
|
|
ElMessage.error('获取营养素列表失败');
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
const removeNutrient = (index) => {
|
||
|
|
localNutrients.value.splice(index, 1);
|
||
|
|
};
|
||
|
|
|
||
|
|
const addNutrient = () => {
|
||
|
|
if (!newNutrientId.value) {
|
||
|
|
ElMessage.warning('请先选择一个营养素');
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
const nutrientExists = localNutrients.value.some(n => n.nutrient_id === newNutrientId.value);
|
||
|
|
if (nutrientExists) {
|
||
|
|
ElMessage.warning('该营养素已存在');
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
const nutrientToAdd = availableNutrients.value.find(n => n.id === newNutrientId.value);
|
||
|
|
if (nutrientToAdd) {
|
||
|
|
localNutrients.value.push({
|
||
|
|
nutrient_id: nutrientToAdd.id,
|
||
|
|
nutrient_name: nutrientToAdd.name,
|
||
|
|
value: 0, // 默认值为0
|
||
|
|
});
|
||
|
|
newNutrientId.value = null; // 重置选择
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
const handleSave = () => {
|
||
|
|
// 构造符合 API 要求的数据格式
|
||
|
|
const nutrientsToSave = localNutrients.value.map(n => ({
|
||
|
|
nutrient_id: n.nutrient_id,
|
||
|
|
value: n.value,
|
||
|
|
}));
|
||
|
|
emit('save', { nutrients: nutrientsToSave });
|
||
|
|
};
|
||
|
|
|
||
|
|
const handleCancel = () => {
|
||
|
|
emit('cancel');
|
||
|
|
};
|
||
|
|
|
||
|
|
onMounted(() => {
|
||
|
|
fetchAllNutrients();
|
||
|
|
});
|
||
|
|
|
||
|
|
return {
|
||
|
|
localNutrients,
|
||
|
|
availableNutrients,
|
||
|
|
newNutrientId,
|
||
|
|
removeNutrient,
|
||
|
|
addNutrient,
|
||
|
|
handleSave,
|
||
|
|
handleCancel,
|
||
|
|
};
|
||
|
|
},
|
||
|
|
};
|
||
|
|
</script>
|
||
|
|
|
||
|
|
<style scoped>
|
||
|
|
.nutrient-editor {
|
||
|
|
padding: 20px;
|
||
|
|
}
|
||
|
|
.add-nutrient-section {
|
||
|
|
margin-top: 20px;
|
||
|
|
display: flex;
|
||
|
|
align-items: center;
|
||
|
|
}
|
||
|
|
.dialog-footer {
|
||
|
|
margin-top: 30px;
|
||
|
|
text-align: right;
|
||
|
|
}
|
||
|
|
</style>
|