Files
smart-project-pricing/前端应用/src/views/settings/materials/index.vue
2026-01-31 21:33:06 +08:00

316 lines
9.0 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup lang="ts">
/**
* 耗材管理页面
*/
import { ref, reactive, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { materialApi, Material, MaterialCreate, MaterialUpdate, materialTypeOptions } from '@/api'
// 查询参数
const queryParams = reactive({
page: 1,
page_size: 20,
keyword: '',
material_type: undefined as string | undefined,
is_active: undefined as boolean | undefined,
})
// 数据
const loading = ref(false)
const tableData = ref<Material[]>([])
const total = ref(0)
const dialogVisible = ref(false)
const dialogTitle = ref('新增耗材')
const formRef = ref()
const form = ref<MaterialCreate>({
material_code: '',
material_name: '',
unit: '',
unit_price: 0,
supplier: null,
material_type: 'consumable',
is_active: true,
})
const editingId = ref<number | null>(null)
// 表单规则
const rules = {
material_code: [
{ required: true, message: '请输入耗材编码', trigger: 'blur' },
],
material_name: [
{ required: true, message: '请输入耗材名称', trigger: 'blur' },
],
unit: [
{ required: true, message: '请输入单位', trigger: 'blur' },
],
unit_price: [
{ required: true, message: '请输入单价', trigger: 'blur' },
],
material_type: [
{ required: true, message: '请选择类型', trigger: 'change' },
],
}
// 获取数据
const fetchData = async () => {
loading.value = true
try {
const res = await materialApi.getList(queryParams)
tableData.value = res.data?.items || []
total.value = res.data?.total || 0
} catch (error) {
console.error('获取耗材失败:', error)
} finally {
loading.value = false
}
}
// 搜索
const handleSearch = () => {
queryParams.page = 1
fetchData()
}
// 重置
const handleReset = () => {
queryParams.keyword = ''
queryParams.material_type = undefined
queryParams.is_active = undefined
queryParams.page = 1
fetchData()
}
// 新增
const handleAdd = () => {
editingId.value = null
dialogTitle.value = '新增耗材'
form.value = {
material_code: '',
material_name: '',
unit: '',
unit_price: 0,
supplier: null,
material_type: 'consumable',
is_active: true,
}
dialogVisible.value = true
}
// 编辑
const handleEdit = (row: Material) => {
editingId.value = row.id
dialogTitle.value = '编辑耗材'
form.value = { ...row }
dialogVisible.value = true
}
// 删除
const handleDelete = async (row: Material) => {
try {
await ElMessageBox.confirm(`确定要删除耗材「${row.material_name}」吗?`, '提示', {
type: 'warning',
})
await materialApi.delete(row.id)
ElMessage.success('删除成功')
fetchData()
} catch (error: any) {
if (error !== 'cancel') {
console.error('删除失败:', error)
}
}
}
// 提交表单
const handleSubmit = async () => {
try {
await formRef.value?.validate()
if (editingId.value) {
await materialApi.update(editingId.value, form.value as MaterialUpdate)
ElMessage.success('更新成功')
} else {
await materialApi.create(form.value)
ElMessage.success('创建成功')
}
dialogVisible.value = false
fetchData()
} catch (error) {
console.error('提交失败:', error)
}
}
// 分页
const handleCurrentChange = (page: number) => {
queryParams.page = page
fetchData()
}
const handleSizeChange = (size: number) => {
queryParams.page_size = size
queryParams.page = 1
fetchData()
}
// 获取类型标签
const getTypeLabel = (type: string) => {
return materialTypeOptions.find(item => item.value === type)?.label || type
}
// 初始化
onMounted(() => {
fetchData()
})
</script>
<template>
<div class="page-container">
<el-card shadow="never">
<!-- 搜索栏 -->
<el-form :inline="true" class="search-form">
<el-form-item label="关键词">
<el-input
v-model="queryParams.keyword"
placeholder="编码/名称"
clearable
@keyup.enter="handleSearch"
/>
</el-form-item>
<el-form-item label="类型">
<el-select v-model="queryParams.material_type" placeholder="全部" clearable>
<el-option
v-for="item in materialTypeOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="状态">
<el-select v-model="queryParams.is_active" placeholder="全部" clearable>
<el-option label="启用" :value="true" />
<el-option label="禁用" :value="false" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleSearch">搜索</el-button>
<el-button @click="handleReset">重置</el-button>
</el-form-item>
</el-form>
<!-- 操作栏 -->
<div class="toolbar">
<el-button type="primary" @click="handleAdd">
<el-icon><Plus /></el-icon>
新增耗材
</el-button>
</div>
<!-- 表格 -->
<el-table v-loading="loading" :data="tableData" border>
<el-table-column prop="material_code" label="编码" width="120" />
<el-table-column prop="material_name" label="名称" min-width="150" />
<el-table-column prop="unit" label="单位" width="80" align="center" />
<el-table-column prop="unit_price" label="单价" width="100" align="right">
<template #default="{ row }">
¥{{ row.unit_price.toFixed(2) }}
</template>
</el-table-column>
<el-table-column prop="material_type" label="类型" width="100" align="center">
<template #default="{ row }">
<el-tag size="small">{{ getTypeLabel(row.material_type) }}</el-tag>
</template>
</el-table-column>
<el-table-column prop="supplier" label="供应商" min-width="120" />
<el-table-column prop="is_active" label="状态" width="80" align="center">
<template #default="{ row }">
<el-tag :type="row.is_active ? 'success' : 'info'" size="small">
{{ row.is_active ? '启用' : '禁用' }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="140" align="center" fixed="right">
<template #default="{ row }">
<el-button type="primary" link size="small" @click="handleEdit(row)">
编辑
</el-button>
<el-button type="danger" link size="small" @click="handleDelete(row)">
删除
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<div class="pagination">
<el-pagination
v-model:current-page="queryParams.page"
v-model:page-size="queryParams.page_size"
:page-sizes="[10, 20, 50, 100]"
:total="total"
layout="total, sizes, prev, pager, next, jumper"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</el-card>
<!-- 表单对话框 -->
<el-dialog v-model="dialogVisible" :title="dialogTitle" width="500px">
<el-form ref="formRef" :model="form" :rules="rules" label-width="80px">
<el-form-item label="编码" prop="material_code">
<el-input v-model="form.material_code" placeholder="请输入编码" />
</el-form-item>
<el-form-item label="名称" prop="material_name">
<el-input v-model="form.material_name" placeholder="请输入名称" />
</el-form-item>
<el-form-item label="单位" prop="unit">
<el-input v-model="form.unit" placeholder="如支、ml、个" />
</el-form-item>
<el-form-item label="单价" prop="unit_price">
<el-input-number v-model="form.unit_price" :min="0" :precision="2" />
</el-form-item>
<el-form-item label="类型" prop="material_type">
<el-select v-model="form.material_type">
<el-option
v-for="item in materialTypeOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="供应商" prop="supplier">
<el-input v-model="form.supplier" placeholder="请输入供应商" />
</el-form-item>
<el-form-item label="状态" prop="is_active">
<el-switch v-model="form.is_active" active-text="启用" inactive-text="禁用" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="handleSubmit">确定</el-button>
</template>
</el-dialog>
</div>
</template>
<style scoped>
.search-form {
margin-bottom: 16px;
}
.toolbar {
margin-bottom: 16px;
display: flex;
gap: 12px;
}
.pagination {
margin-top: 16px;
display: flex;
justify-content: flex-end;
}
</style>