feat:材料添加“阶段”和“重要等级”两列,并且返回按createtime倒序
This commit is contained in:
parent
0e2239b59f
commit
a6456966b6
|
|
@ -36,6 +36,8 @@ REPLACE_TYPE_MAP = {"平替": "alternative", "新研发": "new_development"}
|
|||
ADVANTAGE_MAP = {"品质": "quality", "成本": "cost"}
|
||||
# 星级:中文 -> 1/2/3
|
||||
STAR_LEVEL_MAP = {"一": 1, "二": 2, "三": 3, "1": 1, "2": 2, "3": 3}
|
||||
STAGE_VALUES = {choice[0] for choice in Material.STAGE_CHOICES}
|
||||
IMPORTANCE_LEVEL_VALUES = {choice[0] for choice in Material.IMPORTANCE_LEVEL_CHOICES}
|
||||
|
||||
|
||||
def _cell(v: Any) -> str:
|
||||
|
|
@ -88,6 +90,13 @@ def _parse_cost_compare(s: str) -> Optional[Decimal]:
|
|||
return None
|
||||
|
||||
|
||||
def _parse_choice(s: str, allowed_values: set) -> Optional[str]:
|
||||
value = _cell(s)
|
||||
if not value:
|
||||
return None
|
||||
return value if value in allowed_values else None
|
||||
|
||||
|
||||
def _first_line(s: str) -> str:
|
||||
if not s:
|
||||
return ""
|
||||
|
|
@ -248,6 +257,8 @@ class Command(BaseCommand):
|
|||
"major_category": major_category,
|
||||
"material_category": material_category,
|
||||
"material_subcategory": material_subcategory,
|
||||
"stage": _parse_choice(get(row, "阶段"), STAGE_VALUES),
|
||||
"importance_level": _parse_choice(get(row, "重要等级"), IMPORTANCE_LEVEL_VALUES),
|
||||
"spec": _single_line(get(row, "规格型号")) or None,
|
||||
"standard": _single_line(get(row, "符合标准")) or None,
|
||||
"application_scene": application_scene or None,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,23 @@
|
|||
# Generated by Django 4.2.7 on 2026-03-18 07:21
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('material', '0003_materialcategory_materialsubcategory'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='material',
|
||||
name='importance_level',
|
||||
field=models.CharField(blank=True, choices=[('核心', '核心'), ('优先', '优先'), ('一般', '一般'), ('观察/受限', '观察/受限')], max_length=20, null=True, verbose_name='重要等级'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='material',
|
||||
name='stage',
|
||||
field=models.CharField(blank=True, choices=[('初步交流阶段', '初步交流阶段'), ('完成上会审议', '完成上会审议'), ('选品上样阶段', '选品上样阶段'), ('招标阶段', '招标阶段'), ('项目落地阶段', '项目落地阶段')], max_length=20, null=True, verbose_name='阶段'),
|
||||
),
|
||||
]
|
||||
|
|
@ -41,10 +41,27 @@ class Material(models.Model):
|
|||
('approved', '已审核'),
|
||||
)
|
||||
|
||||
STAGE_CHOICES = (
|
||||
('初步交流阶段', '初步交流阶段'),
|
||||
('完成上会审议', '完成上会审议'),
|
||||
('选品上样阶段', '选品上样阶段'),
|
||||
('招标阶段', '招标阶段'),
|
||||
('项目落地阶段', '项目落地阶段'),
|
||||
)
|
||||
|
||||
IMPORTANCE_LEVEL_CHOICES = (
|
||||
('核心', '核心'),
|
||||
('优先', '优先'),
|
||||
('一般', '一般'),
|
||||
('观察/受限', '观察/受限'),
|
||||
)
|
||||
|
||||
name = models.CharField(max_length=255, verbose_name='材料名称')
|
||||
major_category = models.CharField(max_length=20, choices=MAJOR_CATEGORY_CHOICES, verbose_name='专业类别')
|
||||
material_category = models.CharField(max_length=255, verbose_name='材料分类')
|
||||
material_subcategory = models.CharField(max_length=255, verbose_name='材料子分类')
|
||||
stage = models.CharField(max_length=20, choices=STAGE_CHOICES, blank=True, null=True, verbose_name='阶段')
|
||||
importance_level = models.CharField(max_length=20, choices=IMPORTANCE_LEVEL_CHOICES, blank=True, null=True, verbose_name='重要等级')
|
||||
spec = models.CharField(max_length=255, blank=True, null=True, verbose_name='规格型号')
|
||||
standard = models.CharField(max_length=255, blank=True, null=True, verbose_name='符合标准')
|
||||
application_scene = models.JSONField(default=list, blank=True, null=True, verbose_name='应用场景')
|
||||
|
|
|
|||
|
|
@ -21,6 +21,8 @@ class MaterialSerializer(serializers.ModelSerializer):
|
|||
brand = serializers.CharField(source='factory.brand', read_only=True)
|
||||
major_category_display = serializers.CharField(source='get_major_category_display', read_only=True)
|
||||
replace_type_display = serializers.CharField(source='get_replace_type_display', read_only=True)
|
||||
stage_display = serializers.CharField(source='get_stage_display', read_only=True)
|
||||
importance_level_display = serializers.CharField(source='get_importance_level_display', read_only=True)
|
||||
application_scene = JSONListField(
|
||||
child=serializers.ChoiceField(choices=Material.APPLICATION_SCENE_CHOICES),
|
||||
required=False,
|
||||
|
|
@ -41,7 +43,8 @@ class MaterialSerializer(serializers.ModelSerializer):
|
|||
class Meta:
|
||||
model = Material
|
||||
fields = ['id', 'name', 'major_category', 'major_category_display',
|
||||
'material_category', 'material_subcategory', 'spec', 'standard',
|
||||
'material_category', 'material_subcategory', 'stage', 'stage_display',
|
||||
'importance_level', 'importance_level_display', 'spec', 'standard',
|
||||
'application_scene', 'application_scene_display', 'application_desc',
|
||||
'replace_type', 'replace_type_display', 'advantage', 'advantage_display',
|
||||
'advantage_desc', 'cost_compare', 'cost_desc', 'cases', 'brochure',
|
||||
|
|
@ -81,11 +84,14 @@ class MaterialListSerializer(serializers.ModelSerializer):
|
|||
brand = serializers.CharField(source='factory.brand', read_only=True)
|
||||
major_category_display = serializers.CharField(source='get_major_category_display', read_only=True)
|
||||
status_display = serializers.CharField(source='get_status_display', read_only=True)
|
||||
stage_display = serializers.CharField(source='get_stage_display', read_only=True)
|
||||
importance_level_display = serializers.CharField(source='get_importance_level_display', read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = Material
|
||||
fields = ['id', 'name', 'major_category', 'major_category_display',
|
||||
'material_category', 'material_subcategory', 'factory',
|
||||
'material_category', 'material_subcategory', 'stage', 'stage_display',
|
||||
'importance_level', 'importance_level_display', 'factory',
|
||||
'factory_name', 'brand', 'status', 'status_display']
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ class MaterialViewSet(ModelViewSet):
|
|||
"""
|
||||
根据用户角色过滤材料
|
||||
"""
|
||||
queryset = Material.objects.all()
|
||||
queryset = Material.objects.all().order_by('-created_at', '-id')
|
||||
|
||||
# 普通用户只能看到自己工厂的材料
|
||||
if self.request.user.role != 'admin':
|
||||
|
|
@ -190,6 +190,8 @@ class MaterialViewSet(ModelViewSet):
|
|||
"""
|
||||
return Response({
|
||||
'major_category': Material.MAJOR_CATEGORY_CHOICES,
|
||||
'stage': Material.STAGE_CHOICES,
|
||||
'importance_level': Material.IMPORTANCE_LEVEL_CHOICES,
|
||||
'replace_type': Material.REPLACE_TYPE_CHOICES,
|
||||
'advantage': Material.ADVANTAGE_CHOICES,
|
||||
'application_scene': Material.APPLICATION_SCENE_CHOICES,
|
||||
|
|
|
|||
|
|
@ -6,6 +6,21 @@ from pathlib import Path
|
|||
from decouple import config
|
||||
import os
|
||||
|
||||
|
||||
def _cast_debug(value):
|
||||
if isinstance(value, bool):
|
||||
return value
|
||||
|
||||
normalized = str(value).strip().lower()
|
||||
truthy = {'1', 'true', 't', 'yes', 'y', 'on', 'debug', 'dev', 'development'}
|
||||
falsy = {'0', 'false', 'f', 'no', 'n', 'off', 'release', 'prod', 'production'}
|
||||
|
||||
if normalized in truthy:
|
||||
return True
|
||||
if normalized in falsy:
|
||||
return False
|
||||
raise ValueError(f"Invalid DEBUG value: {value}")
|
||||
|
||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||
BASE_DIR = Path(__file__).resolve().parent.parent
|
||||
|
||||
|
|
@ -16,9 +31,13 @@ BASE_DIR = Path(__file__).resolve().parent.parent
|
|||
SECRET_KEY = config('SECRET_KEY', default='django-insecure-change-in-production')
|
||||
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = config('DEBUG', default=True, cast=bool)
|
||||
DEBUG = config('DEBUG', default=True, cast=_cast_debug)
|
||||
|
||||
ALLOWED_HOSTS = config('ALLOWED_HOSTS', default='localhost,127.0.0.1', cast=lambda v: [s.strip() for s in v.split(',')])
|
||||
ALLOWED_HOSTS = config(
|
||||
'ALLOWED_HOSTS',
|
||||
default='localhost,127.0.0.1,testserver',
|
||||
cast=lambda v: [s.strip() for s in v.split(',')],
|
||||
)
|
||||
|
||||
# Application definition
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@
|
|||
<el-descriptions-item label="专业类别">{{ displayText(material.major_category_display) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="材料分类">{{ displayText(material.material_category) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="材料子类">{{ displayText(material.material_subcategory) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="阶段">{{ displayText(material.stage_display) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="重要等级">{{ displayText(material.importance_level_display) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="规格型号">{{ displayText(material.spec) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="符合标准">{{ displayText(material.standard) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="应用场景">{{ displayList(material.application_scene_display) }}</el-descriptions-item>
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@
|
|||
<el-table-column prop="major_category_display" label="专业类别" />
|
||||
<el-table-column prop="material_category" label="材料分类" />
|
||||
<el-table-column prop="material_subcategory" label="材料子类" />
|
||||
<el-table-column prop="stage_display" label="阶段" />
|
||||
<el-table-column prop="importance_level_display" label="重要等级" />
|
||||
<el-table-column prop="brand" label="所属工厂" />
|
||||
<el-table-column prop="status_display" label="状态" width="120" />
|
||||
<el-table-column label="操作" width="320">
|
||||
|
|
@ -66,6 +68,16 @@
|
|||
<el-option v-for="item in subcategoryOptions" :key="item.value" :label="item.name" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="阶段">
|
||||
<el-select v-model="form.stage" clearable>
|
||||
<el-option v-for="item in stageOptions" :key="item[0]" :label="item[1]" :value="item[0]" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="重要等级">
|
||||
<el-select v-model="form.importance_level" clearable>
|
||||
<el-option v-for="item in importanceLevelOptions" :key="item[0]" :label="item[1]" :value="item[0]" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="规格型号">
|
||||
<el-input v-model="form.spec" />
|
||||
</el-form-item>
|
||||
|
|
@ -200,6 +212,8 @@ const form = reactive({
|
|||
major_category: '',
|
||||
material_category: '',
|
||||
material_subcategory: '',
|
||||
stage: '',
|
||||
importance_level: '',
|
||||
spec: '',
|
||||
standard: '',
|
||||
application_scene: [],
|
||||
|
|
@ -224,6 +238,8 @@ const form = reactive({
|
|||
})
|
||||
|
||||
const majorOptions = ref([])
|
||||
const stageOptions = ref([])
|
||||
const importanceLevelOptions = ref([])
|
||||
const replaceOptions = ref([])
|
||||
const advantageOptions = ref([])
|
||||
const sceneOptions = ref([])
|
||||
|
|
@ -252,6 +268,8 @@ const loadMaterials = async () => {
|
|||
const loadChoices = async () => {
|
||||
const data = await fetchMaterialChoices()
|
||||
majorOptions.value = data.major_category
|
||||
stageOptions.value = data.stage
|
||||
importanceLevelOptions.value = data.importance_level
|
||||
replaceOptions.value = data.replace_type
|
||||
advantageOptions.value = data.advantage
|
||||
sceneOptions.value = data.application_scene
|
||||
|
|
@ -291,6 +309,8 @@ const resetForm = () => {
|
|||
major_category: '',
|
||||
material_category: '',
|
||||
material_subcategory: '',
|
||||
stage: '',
|
||||
importance_level: '',
|
||||
spec: '',
|
||||
standard: '',
|
||||
application_scene: [],
|
||||
|
|
|
|||
Loading…
Reference in New Issue