feat: 材料新增/编辑/详情改用抽屉展示

- 新增/编辑弹窗换成 el-drawer(size 60%,禁止点遮罩关闭)
- 列表"详情"不再跳路由,改为打开只读抽屉,内联展示字段和宣传页
- 移除未使用的 useRouter 引用

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
caoqianming 2026-04-23 22:07:29 +08:00
parent 1245fb3da8
commit b4a4314058
1 changed files with 93 additions and 6 deletions

View File

@ -73,7 +73,14 @@
/>
</div>
<el-dialog v-model="dialogVisible" :title="dialogTitle" width="820px" class="dialog-scroll">
<el-drawer
v-model="dialogVisible"
:title="dialogTitle"
size="60%"
:close-on-click-modal="false"
:destroy-on-close="false"
class="material-drawer"
>
<el-form :model="form" label-width="110px">
<el-form-item label="材料名称" required>
<el-input v-model="form.name" />
@ -224,7 +231,52 @@
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="onSave">保存</el-button>
</template>
</el-dialog>
</el-drawer>
<el-drawer
v-model="detailVisible"
title="材料详情"
size="60%"
class="material-drawer"
>
<el-descriptions v-if="detailData" :column="1" border>
<el-descriptions-item label="材料名称">{{ displayText(detailData.name) }}</el-descriptions-item>
<el-descriptions-item label="材料大类">{{ displayText(detailData.major_category_display) }}</el-descriptions-item>
<el-descriptions-item label="细分种类">{{ displayText(detailData.material_category) }}</el-descriptions-item>
<el-descriptions-item label="材料子类">{{ displayText(detailData.material_subcategory) }}</el-descriptions-item>
<el-descriptions-item label="阶段">{{ displayText(detailData.stage_display) }}</el-descriptions-item>
<el-descriptions-item label="重要等级">{{ displayText(detailData.importance_level_display) }}</el-descriptions-item>
<el-descriptions-item label="落地项目">{{ displayText(detailData.landing_project) }}</el-descriptions-item>
<el-descriptions-item label="对接人">{{ displayText(detailData.contact_person) }}</el-descriptions-item>
<el-descriptions-item label="对接人联系方式">{{ displayText(detailData.contact_phone) }}</el-descriptions-item>
<el-descriptions-item label="经办人">{{ displayText(detailData.handler) }}</el-descriptions-item>
<el-descriptions-item label="备注">{{ displayText(detailData.remark) }}</el-descriptions-item>
<el-descriptions-item label="规格型号">{{ displayText(detailData.spec) }}</el-descriptions-item>
<el-descriptions-item label="符合标准">{{ displayText(detailData.standard) }}</el-descriptions-item>
<el-descriptions-item label="应用场景">{{ displayList(detailData.application_scene_display) }}</el-descriptions-item>
<el-descriptions-item label="应用说明">{{ displayText(detailData.application_desc) }}</el-descriptions-item>
<el-descriptions-item label="替代材料类型">{{ displayText(detailData.replace_type_display) }}</el-descriptions-item>
<el-descriptions-item label="竞争优势">{{ displayList(detailData.advantage_display) }}</el-descriptions-item>
<el-descriptions-item label="优势说明">{{ displayText(detailData.advantage_desc) }}</el-descriptions-item>
<el-descriptions-item label="成本对比">{{ formatPercent(detailData.cost_compare) }}</el-descriptions-item>
<el-descriptions-item label="成本说明">{{ displayText(detailData.cost_desc) }}</el-descriptions-item>
<el-descriptions-item label="案例">{{ displayText(detailData.cases) }}</el-descriptions-item>
<el-descriptions-item label="供应商">{{ displayText(detailData.factory_name) }}</el-descriptions-item>
<el-descriptions-item label="品牌">{{ displayText(detailData.brand_name) }}</el-descriptions-item>
<el-descriptions-item label="质量等级">{{ formatStarLevel(detailData.quality_level) }}</el-descriptions-item>
<el-descriptions-item label="耐久等级">{{ formatStarLevel(detailData.durability_level) }}</el-descriptions-item>
<el-descriptions-item label="环保等级">{{ formatStarLevel(detailData.eco_level) }}</el-descriptions-item>
<el-descriptions-item label="低碳等级">{{ formatStarLevel(detailData.carbon_level) }}</el-descriptions-item>
<el-descriptions-item label="总评分">{{ formatStarLevel(detailData.score_level) }}</el-descriptions-item>
<el-descriptions-item label="连接方式">{{ displayText(detailData.connection_method) }}</el-descriptions-item>
<el-descriptions-item label="施工工艺">{{ displayText(detailData.construction_method) }}</el-descriptions-item>
<el-descriptions-item label="限制条件">{{ displayText(detailData.limit_condition) }}</el-descriptions-item>
</el-descriptions>
<div v-if="detailData?.brochure_url" class="brochure">
<div class="brochure-title">宣传页</div>
<img :src="detailData.brochure_url" alt="宣传页" />
</div>
</el-drawer>
<el-dialog v-model="importDialogVisible" title="导入材料" width="420px">
<div class="import-dialog">
@ -249,7 +301,6 @@
<script setup>
import { ref, reactive, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { ElMessage, ElMessageBox } from 'element-plus'
import { useAuth } from '@/store/auth'
import { fetchMaterials, fetchMaterialDetail, createMaterial, updateMaterial, deleteMaterial, submitMaterial, approveMaterial, rejectMaterial, fetchMaterialChoices, uploadImage, importMaterialsExcel, exportMaterialsExcel } from '@/api/material'
@ -257,7 +308,6 @@ import { fetchCategories, fetchSubcategories } from '@/api/category'
import { fetchFactorySimple } from '@/api/factory'
import { fetchBrands } from '@/api/brand'
const router = useRouter()
const { isAdmin } = useAuth()
const materials = ref([])
const tableLoading = ref(false)
@ -268,6 +318,8 @@ const pagination = reactive({
})
const factories = ref([])
const dialogVisible = ref(false)
const detailVisible = ref(false)
const detailData = ref(null)
const importDialogVisible = ref(false)
const dialogTitle = ref('')
const isEdit = ref(false)
@ -635,10 +687,20 @@ const canDelete = (row) => isAdmin.value || row.status === 'draft'
const canSubmit = (row) => !isAdmin.value && row.status === 'draft'
const canApprove = (row) => isAdmin.value && row.status === 'pending'
const goDetail = (row) => {
router.push(`/materials/${row.id}`)
const goDetail = async (row) => {
try {
detailData.value = await fetchMaterialDetail(row.id)
detailVisible.value = true
} catch (error) {
ElMessage.error(error.response?.data?.detail || '加载详情失败')
}
}
const displayText = (value) => value || '-'
const displayList = (value) => (value?.length ? value.join('、') : '-')
const formatPercent = (value) => (value === null || value === undefined || value === '' ? '-' : `${value}%`)
const formatStarLevel = (value) => (value ? `${value}` : '-')
const onPageChange = (page) => {
pagination.page = page
loadMaterials()
@ -688,6 +750,31 @@ onMounted(() => {
overflow: auto;
padding-right: 8px;
}
.material-drawer :deep(.el-drawer__body) {
padding: 20px 24px;
overflow-y: auto;
}
.material-drawer :deep(.el-drawer__footer) {
padding: 12px 24px;
}
.brochure {
margin-top: 20px;
}
.brochure-title {
margin-bottom: 12px;
font-size: 16px;
font-weight: 600;
}
.brochure img {
max-width: 100%;
border-radius: 8px;
border: 1px solid #eee;
}
</style>
<style scoped>