部门管理: - ✅ dept.js API文件 - ✅ dept.vue 树形表格页面 - ✅ 路由配置 项目管理: - ✅ project.js API文件(修复updateProjectStatus接口) - ✅ list.vue 页面优化(await fetchData、错误处理) 合同管理: - ✅ contract.js API文件(独立出来) - ✅ contract.vue 页面优化(修复导入路径、await fetchData、错误处理) 优化内容: 1. 统一使用 await fetchData() 确保刷新完成 2. 改进表单验证逻辑(Promise方式) 3. 添加明确的错误提示 4. 删除操作优化(删除最后一条自动返回上一页) 5. 统一代码风格和最佳实践
344 lines
10 KiB
Vue
344 lines
10 KiB
Vue
<template>
|
|
<div class="page-container">
|
|
<el-card>
|
|
<template #header>
|
|
<div class="card-header">
|
|
<span>合同管理</span>
|
|
<el-button type="primary" @click="handleAdd">新增合同</el-button>
|
|
</div>
|
|
</template>
|
|
|
|
<el-form :inline="true" class="search-form">
|
|
<el-form-item label="合同名称">
|
|
<el-input v-model="searchForm.contractName" placeholder="请输入合同名称" />
|
|
</el-form-item>
|
|
<el-form-item label="合同状态">
|
|
<el-select v-model="searchForm.contractStatus" placeholder="请选择" clearable>
|
|
<el-option label="草稿" value="DRAFT" />
|
|
<el-option label="已签署" value="SIGNED" />
|
|
<el-option label="执行中" value="EXECUTING" />
|
|
<el-option label="已完成" value="COMPLETED" />
|
|
<el-option label="已终止" value="TERMINATED" />
|
|
</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>
|
|
|
|
<el-table :data="tableData" border v-loading="loading">
|
|
<el-table-column prop="contractCode" label="合同编码" width="120" />
|
|
<el-table-column prop="contractName" label="合同名称" />
|
|
<el-table-column prop="contractStatus" label="合同状态" width="100">
|
|
<template #default="scope">
|
|
<el-tag :type="getStatusType(scope.row.contractStatus)">
|
|
{{ getStatusText(scope.row.contractStatus) }}
|
|
</el-tag>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column prop="contractAmount" label="合同金额" width="120">
|
|
<template #default="scope">
|
|
{{ formatAmount(scope.row.contractAmount) }}
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column prop="signDate" label="签署日期" width="120" />
|
|
<el-table-column prop="effectiveDate" label="生效日期" width="120" />
|
|
<el-table-column label="操作" width="200" fixed="right">
|
|
<template #default="scope">
|
|
<el-button type="primary" link @click="handleEdit(scope.row)">编辑</el-button>
|
|
<el-button type="danger" link @click="handleDelete(scope.row)">删除</el-button>
|
|
</template>
|
|
</el-table-column>
|
|
</el-table>
|
|
|
|
<el-pagination
|
|
class="pagination"
|
|
v-model:current-page="page.current"
|
|
v-model:page-size="page.size"
|
|
:total="page.total"
|
|
layout="total, sizes, prev, pager, next"
|
|
@size-change="handleSizeChange"
|
|
@current-change="handleCurrentChange"
|
|
/>
|
|
</el-card>
|
|
|
|
<!-- 新增/编辑对话框 -->
|
|
<el-dialog v-model="dialogVisible" :title="dialogTitle" width="700px">
|
|
<el-form ref="formRef" :model="form" :rules="rules" label-width="100px">
|
|
<el-row :gutter="20">
|
|
<el-col :span="12">
|
|
<el-form-item label="合同编码" prop="contractCode">
|
|
<el-input v-model="form.contractCode" placeholder="请输入合同编码" />
|
|
</el-form-item>
|
|
</el-col>
|
|
<el-col :span="12">
|
|
<el-form-item label="合同名称" prop="contractName">
|
|
<el-input v-model="form.contractName" placeholder="请输入合同名称" />
|
|
</el-form-item>
|
|
</el-col>
|
|
</el-row>
|
|
<el-row :gutter="20">
|
|
<el-col :span="12">
|
|
<el-form-item label="合同金额" prop="contractAmount">
|
|
<el-input-number v-model="form.contractAmount" :precision="2" :min="0" style="width: 100%" />
|
|
</el-form-item>
|
|
</el-col>
|
|
<el-col :span="12">
|
|
<el-form-item label="合同类型">
|
|
<el-select v-model="form.contractType" placeholder="请选择" style="width: 100%">
|
|
<el-option label="销售合同" value="SALES" />
|
|
<el-option label="采购合同" value="PURCHASE" />
|
|
<el-option label="服务合同" value="SERVICE" />
|
|
<el-option label="其他" value="OTHER" />
|
|
</el-select>
|
|
</el-form-item>
|
|
</el-col>
|
|
</el-row>
|
|
<el-row :gutter="20">
|
|
<el-col :span="12">
|
|
<el-form-item label="签署日期">
|
|
<el-date-picker v-model="form.signDate" type="date" placeholder="选择日期" style="width: 100%" value-format="YYYY-MM-DD" />
|
|
</el-form-item>
|
|
</el-col>
|
|
<el-col :span="12">
|
|
<el-form-item label="生效日期">
|
|
<el-date-picker v-model="form.effectiveDate" type="date" placeholder="选择日期" style="width: 100%" value-format="YYYY-MM-DD" />
|
|
</el-form-item>
|
|
</el-col>
|
|
</el-row>
|
|
<el-row :gutter="20">
|
|
<el-col :span="12">
|
|
<el-form-item label="到期日期">
|
|
<el-date-picker v-model="form.expiryDate" type="date" placeholder="选择日期" style="width: 100%" value-format="YYYY-MM-DD" />
|
|
</el-form-item>
|
|
</el-col>
|
|
<el-col :span="12">
|
|
<el-form-item label="签署地点">
|
|
<el-input v-model="form.signLocation" placeholder="请输入签署地点" />
|
|
</el-form-item>
|
|
</el-col>
|
|
</el-row>
|
|
<el-form-item label="付款条款">
|
|
<el-input v-model="form.paymentTerms" type="textarea" rows="2" placeholder="请输入付款条款" />
|
|
</el-form-item>
|
|
<el-form-item label="备注">
|
|
<el-input v-model="form.remark" type="textarea" rows="2" placeholder="请输入备注" />
|
|
</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>
|
|
|
|
<script setup>
|
|
import { ref, reactive, onMounted } from 'vue'
|
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
|
import { getContractList, createContract, updateContract, deleteContract } from '../../api/contract'
|
|
|
|
const loading = ref(false)
|
|
const tableData = ref([])
|
|
|
|
const searchForm = reactive({
|
|
contractName: '',
|
|
contractStatus: ''
|
|
})
|
|
|
|
const page = reactive({
|
|
current: 1,
|
|
size: 10,
|
|
total: 0
|
|
})
|
|
|
|
const dialogVisible = ref(false)
|
|
const dialogTitle = ref('')
|
|
const formRef = ref(null)
|
|
const form = reactive({
|
|
contractId: null,
|
|
contractCode: '',
|
|
contractName: '',
|
|
contractType: 'SALES',
|
|
contractAmount: 0,
|
|
signDate: '',
|
|
effectiveDate: '',
|
|
expiryDate: '',
|
|
signLocation: '',
|
|
paymentTerms: '',
|
|
remark: ''
|
|
})
|
|
|
|
const rules = {
|
|
contractCode: [{ required: true, message: '请输入合同编码', trigger: 'blur' }],
|
|
contractName: [{ required: true, message: '请输入合同名称', trigger: 'blur' }],
|
|
contractAmount: [{ required: true, message: '请输入合同金额', trigger: 'blur' }]
|
|
}
|
|
|
|
const fetchData = async () => {
|
|
loading.value = true
|
|
try {
|
|
const res = await getContractList({
|
|
current: page.current,
|
|
size: page.size,
|
|
contractName: searchForm.contractName,
|
|
contractStatus: searchForm.contractStatus
|
|
})
|
|
tableData.value = res.records
|
|
page.total = res.total
|
|
} catch (error) {
|
|
console.error('获取合同列表失败:', error)
|
|
} finally {
|
|
loading.value = false
|
|
}
|
|
}
|
|
|
|
const handleSearch = () => {
|
|
page.current = 1
|
|
fetchData()
|
|
}
|
|
|
|
const handleReset = () => {
|
|
searchForm.contractName = ''
|
|
searchForm.contractStatus = ''
|
|
handleSearch()
|
|
}
|
|
|
|
const handleAdd = () => {
|
|
dialogTitle.value = '新增合同'
|
|
resetForm()
|
|
dialogVisible.value = true
|
|
}
|
|
|
|
const handleEdit = (row) => {
|
|
dialogTitle.value = '编辑合同'
|
|
resetForm()
|
|
Object.assign(form, row)
|
|
dialogVisible.value = true
|
|
}
|
|
|
|
const resetForm = () => {
|
|
form.contractId = null
|
|
form.contractCode = ''
|
|
form.contractName = ''
|
|
form.contractType = 'SALES'
|
|
form.contractAmount = 0
|
|
form.signDate = ''
|
|
form.effectiveDate = ''
|
|
form.expiryDate = ''
|
|
form.signLocation = ''
|
|
form.paymentTerms = ''
|
|
form.remark = ''
|
|
}
|
|
|
|
const handleSubmit = async () => {
|
|
if (!formRef.value) return
|
|
|
|
try {
|
|
await formRef.value.validate()
|
|
} catch (error) {
|
|
ElMessage.warning('请检查表单填写是否完整')
|
|
return
|
|
}
|
|
|
|
try {
|
|
if (form.contractId) {
|
|
await updateContract(form.contractId, form)
|
|
ElMessage.success('更新成功')
|
|
} else {
|
|
await createContract(form)
|
|
ElMessage.success('创建成功')
|
|
}
|
|
dialogVisible.value = false
|
|
await fetchData()
|
|
} catch (error) {
|
|
console.error('保存失败:', error)
|
|
ElMessage.error(error.message || '操作失败')
|
|
}
|
|
}
|
|
|
|
const handleDelete = (row) => {
|
|
ElMessageBox.confirm('确定要删除该合同吗?', '提示', {
|
|
confirmButtonText: '确定',
|
|
cancelButtonText: '取消',
|
|
type: 'warning'
|
|
}).then(async () => {
|
|
try {
|
|
await deleteContract(row.contractId)
|
|
ElMessage.success('删除成功')
|
|
// 如果删除的是当前页最后一条且不是第一页,返回上一页
|
|
if (tableData.value.length === 1 && page.current > 1) {
|
|
page.current--
|
|
}
|
|
await fetchData()
|
|
} catch (error) {
|
|
console.error('删除失败:', error)
|
|
ElMessage.error(error.message || '删除失败')
|
|
}
|
|
})
|
|
}
|
|
|
|
const handleSizeChange = (val) => {
|
|
page.size = val
|
|
fetchData()
|
|
}
|
|
|
|
const handleCurrentChange = (val) => {
|
|
page.current = val
|
|
fetchData()
|
|
}
|
|
|
|
const getStatusType = (status) => {
|
|
const map = {
|
|
'DRAFT': 'info',
|
|
'SIGNED': 'warning',
|
|
'EXECUTING': 'primary',
|
|
'COMPLETED': 'success',
|
|
'TERMINATED': 'danger'
|
|
}
|
|
return map[status] || 'info'
|
|
}
|
|
|
|
const getStatusText = (status) => {
|
|
const map = {
|
|
'DRAFT': '草稿',
|
|
'SIGNED': '已签署',
|
|
'EXECUTING': '执行中',
|
|
'COMPLETED': '已完成',
|
|
'TERMINATED': '已终止'
|
|
}
|
|
return map[status] || status
|
|
}
|
|
|
|
const formatAmount = (amount) => {
|
|
if (!amount) return '¥0.00'
|
|
return '¥' + parseFloat(amount).toFixed(2).replace(/\d(?=(\d{3})+\.)/g, '$&,')
|
|
}
|
|
|
|
onMounted(() => {
|
|
fetchData()
|
|
})
|
|
</script>
|
|
|
|
<style scoped>
|
|
.page-container {
|
|
padding: 20px;
|
|
}
|
|
|
|
.card-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
}
|
|
|
|
.search-form {
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.pagination {
|
|
margin-top: 20px;
|
|
justify-content: flex-end;
|
|
}
|
|
</style>
|