492 lines
14 KiB
Vue
492 lines
14 KiB
Vue
<script setup>
|
||
import { ref, reactive, computed, onMounted } from 'vue'
|
||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||
import { Search } from '@element-plus/icons-vue'
|
||
import {
|
||
getContractList,
|
||
createContract,
|
||
updateContract,
|
||
deleteContractAPI
|
||
} from '../api/contract'
|
||
|
||
const search = ref('')
|
||
const select = ref('1')
|
||
const dateRange = ref([])
|
||
const loading = ref(false)
|
||
const searchResults = ref([])
|
||
const showSearchResults = ref(false)
|
||
const showAddForm = ref(false)
|
||
const isEditMode = ref(false)
|
||
const currentContractId = ref(null)
|
||
const showDetailDialog = ref(false)
|
||
const currentDetail = ref(null)
|
||
|
||
const form = reactive({
|
||
contract_no: '',
|
||
contract_name: '',
|
||
customer_id: '',
|
||
contract_content: '',
|
||
employee_id: '',
|
||
effective_date: '',
|
||
expiry_date: '',
|
||
amount: 0,
|
||
status: '待审批',
|
||
remark: ''
|
||
})
|
||
|
||
onMounted(() => {
|
||
fetchData()
|
||
})
|
||
|
||
// 获取合同列表
|
||
const fetchData = async () => {
|
||
loading.value = true
|
||
const res = await getContractList()
|
||
searchResults.value = res.data
|
||
showSearchResults.value = true
|
||
loading.value = false
|
||
}
|
||
|
||
// 处理搜索
|
||
const handleSearch = async () => {
|
||
// 如果在表单页面,先关闭表单
|
||
if (showAddForm.value) {
|
||
cancelAdd()
|
||
}
|
||
|
||
loading.value = true
|
||
|
||
let keyword = search.value
|
||
if (select.value === '1' && keyword && !keyword.startsWith('HT')) {
|
||
keyword = 'HT' + keyword
|
||
}
|
||
|
||
const params = {
|
||
keyword,
|
||
searchField: select.value,
|
||
startDate: dateRange.value?.[0],
|
||
endDate: dateRange.value?.[1]
|
||
}
|
||
|
||
const res = await getContractList(params)
|
||
searchResults.value = res.data
|
||
showSearchResults.value = true
|
||
loading.value = false
|
||
}
|
||
|
||
// 日期变更处理
|
||
const handleDateChange = () => {
|
||
handleSearch()
|
||
}
|
||
|
||
// 重置搜索
|
||
const resetSearch = () => {
|
||
search.value = ''
|
||
select.value = '1'
|
||
dateRange.value = []
|
||
fetchData()
|
||
}
|
||
|
||
// 新增合同
|
||
const addContract = () => {
|
||
resetForm()
|
||
showAddForm.value = true
|
||
showSearchResults.value = false
|
||
}
|
||
|
||
// 编辑合同
|
||
const editContract = (row) => {
|
||
isEditMode.value = true
|
||
currentContractId.value = row.id
|
||
Object.assign(form, {
|
||
contract_no: row.contract_no,
|
||
contract_name: row.contract_name,
|
||
customer_id: row.customer_id,
|
||
contract_content: row.contract_content,
|
||
employee_id: row.employee_id,
|
||
effective_date: row.effective_date,
|
||
expiry_date: row.expiry_date,
|
||
amount: row.amount,
|
||
status: row.status,
|
||
remark: row.remark
|
||
})
|
||
showAddForm.value = true
|
||
showSearchResults.value = false
|
||
}
|
||
|
||
// 保存合同
|
||
const saveContract = async () => {
|
||
if (isEditMode.value) {
|
||
await updateContract(currentContractId.value, { ...form })
|
||
ElMessage.success('更新成功')
|
||
} else {
|
||
await createContract({ ...form })
|
||
ElMessage.success('新增成功')
|
||
}
|
||
cancelAdd()
|
||
fetchData()
|
||
}
|
||
|
||
// 删除合同
|
||
const deleteContract = (id) => {
|
||
ElMessageBox.confirm('确定删除该合同吗?', '提示', {
|
||
type: 'warning'
|
||
}).then(async () => {
|
||
await deleteContractAPI(id)
|
||
ElMessage.success('删除成功')
|
||
fetchData()
|
||
})
|
||
}
|
||
|
||
// 取消
|
||
const cancelAdd = () => {
|
||
resetForm()
|
||
showAddForm.value = false
|
||
showSearchResults.value = true
|
||
}
|
||
|
||
// 重置表单
|
||
const resetForm = () => {
|
||
Object.assign(form, {
|
||
contract_no: '',
|
||
contract_name: '',
|
||
customer_id: '',
|
||
contract_content: '',
|
||
employee_id: '',
|
||
effective_date: '',
|
||
expiry_date: '',
|
||
amount: 0,
|
||
status: '待审批',
|
||
remark: ''
|
||
})
|
||
isEditMode.value = false
|
||
currentContractId.value = null
|
||
}
|
||
|
||
// 双击查看详情
|
||
const showDetail = (row) => {
|
||
currentDetail.value = row
|
||
showDetailDialog.value = true
|
||
}
|
||
|
||
// 关闭详情弹窗
|
||
const closeDetail = () => {
|
||
showDetailDialog.value = false
|
||
currentDetail.value = null
|
||
}
|
||
|
||
// 状态样式
|
||
const getStatusType = (status) => {
|
||
const map = {
|
||
'生效中': 'success',
|
||
'已到期': 'danger',
|
||
'待审批': 'warning',
|
||
'已终止': 'info'
|
||
}
|
||
return map[status] || 'info'
|
||
}
|
||
|
||
// 日期快捷选项
|
||
const dateShortcuts = [
|
||
{
|
||
text: '最近一周',
|
||
value: () => {
|
||
const end = new Date()
|
||
const start = new Date()
|
||
start.setTime(start.getTime() - 7 * 24 * 3600 * 1000)
|
||
return [start, end]
|
||
}
|
||
},
|
||
{
|
||
text: '最近一个月',
|
||
value: () => {
|
||
const end = new Date()
|
||
const start = new Date()
|
||
start.setTime(start.getTime() - 30 * 24 * 3600 * 1000)
|
||
return [start, end]
|
||
}
|
||
},
|
||
{
|
||
text: '最近三个月',
|
||
value: () => {
|
||
const end = new Date()
|
||
const start = new Date()
|
||
start.setTime(start.getTime() - 90 * 24 * 3600 * 1000)
|
||
return [start, end]
|
||
}
|
||
}
|
||
]
|
||
|
||
// 格式化日期
|
||
const formatDate = (date) => {
|
||
if (!date) return ''
|
||
return date
|
||
}
|
||
</script>
|
||
|
||
<template>
|
||
<div class="contract-container">
|
||
<!-- 搜索栏区域 -->
|
||
<div class="contract-search">
|
||
<!-- 第一行:搜索框 + 按钮 -->
|
||
<div class="search-row">
|
||
<el-input v-model="search" style="max-width: 500px" placeholder="请输入搜索关键词"
|
||
class="contract-search-with-select" @keyup.enter="handleSearch">
|
||
<template #prepend>
|
||
<el-select v-model="select" placeholder="请选择" style="width: 115px">
|
||
<el-option label="编号" value="1" />
|
||
<el-option label="名称" value="2" />
|
||
</el-select>
|
||
</template>
|
||
<template #append>
|
||
<el-button :icon="Search" @click="handleSearch" />
|
||
</template>
|
||
</el-input>
|
||
|
||
<div class="search-buttons">
|
||
<el-button type="success" @click="addContract">新增合同</el-button>
|
||
<el-button type="default" @click="resetSearch">重置</el-button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 第二行:筛选条件 -->
|
||
<div class="filter-row">
|
||
<span class="filter-label">筛选条件:</span>
|
||
|
||
<el-date-picker
|
||
v-model="dateRange"
|
||
type="daterange"
|
||
range-separator="至"
|
||
start-placeholder="生效日期"
|
||
end-placeholder="到期日期"
|
||
style="margin-left: 10px;"
|
||
@change="handleDateChange"
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 合同列表 -->
|
||
<div v-if="showSearchResults" class="contract-list">
|
||
<div v-if="loading" class="loading-container">
|
||
<el-skeleton :rows="5" animated />
|
||
</div>
|
||
|
||
<div v-else-if="searchResults.length === 0" class="empty-container">
|
||
<el-empty description="暂无合同数据" />
|
||
</div>
|
||
|
||
<div v-else>
|
||
<div class="list-header">
|
||
<span class="total-count">共找到 {{ searchResults.length }} 条合同</span>
|
||
</div>
|
||
|
||
<el-table :data="searchResults" border style="width: 100%" @row-dblclick="showDetail" row-class-name="clickable-row">
|
||
<el-table-column prop="id" label="ID" width="60" />
|
||
<el-table-column prop="contract_no" label="合同编号" width="130" />
|
||
<el-table-column prop="contract_name" label="合同名称" min-width="180" />
|
||
<el-table-column prop="customer_id" label="客户ID" width="80" />
|
||
<el-table-column prop="employee_id" label="业务员ID" width="90" />
|
||
<el-table-column prop="amount" label="金额" width="110">
|
||
<template #default="{ row }">
|
||
¥{{ row.amount?.toLocaleString() }}
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="status" label="状态" width="80">
|
||
<template #default="{ row }">
|
||
<el-tag :type="getStatusType(row.status)">{{ row.status }}</el-tag>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="effective_date" label="生效日期" width="110" />
|
||
<el-table-column prop="expiry_date" label="到期日期" width="110" />
|
||
<el-table-column label="操作" width="140" fixed="right">
|
||
<template #default="{ row }">
|
||
<el-button link type="primary" @click="editContract(row)">编辑</el-button>
|
||
<el-button link type="danger" @click="deleteContract(row.id)">删除</el-button>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 新增/编辑表单 -->
|
||
<div v-if="showAddForm" class="contract-form" style="margin-top: 20px;">
|
||
<el-divider content-position="left">{{ isEditMode ? '编辑合同' : '新增合同' }}</el-divider>
|
||
<el-form :model="form" label-width="auto" style="max-width: 700px;" label-position="top">
|
||
<el-row :gutter="20">
|
||
<el-col :span="12">
|
||
<el-form-item label="合同编号">
|
||
<el-input v-model="form.contract_no" placeholder="如: HT-001" />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-form-item label="合同名称">
|
||
<el-input v-model="form.contract_name" placeholder="请输入合同名称" />
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
|
||
<el-row :gutter="20">
|
||
<el-col :span="12">
|
||
<el-form-item label="客户ID">
|
||
<el-input v-model="form.customer_id" style="width: 100%;" />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-form-item label="业务员ID">
|
||
<el-input v-model="form.employee_id" style="width: 100%;" />
|
||
</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.effective_date" type="date" style="width: 100%;" />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-form-item label="到期日期">
|
||
<el-date-picker v-model="form.expiry_date" type="date" style="width: 100%;" />
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
|
||
<el-row :gutter="20">
|
||
<el-col :span="12">
|
||
<el-form-item label="合同金额">
|
||
<el-input-number v-model="form.amount" :min="0" :step="1000" style="width: 100%;" />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-form-item label="状态">
|
||
<el-select v-model="form.status" style="width: 100%;">
|
||
<el-option label="待审批" value="待审批" />
|
||
<el-option label="生效中" value="生效中" />
|
||
<el-option label="已到期" value="已到期" />
|
||
<el-option label="已终止" value="已终止" />
|
||
</el-select>
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
|
||
<el-form-item label="合同内容/条款">
|
||
<el-input v-model="form.contract_content" type="textarea" :rows="4" placeholder="请输入合同内容" />
|
||
</el-form-item>
|
||
|
||
<el-form-item label="备注">
|
||
<el-input v-model="form.remark" type="textarea" :rows="2" />
|
||
</el-form-item>
|
||
|
||
<el-form-item>
|
||
<el-button type="primary" @click="saveContract">保存</el-button>
|
||
<el-button @click="cancelAdd">取消</el-button>
|
||
</el-form-item>
|
||
</el-form>
|
||
</div>
|
||
|
||
<!-- 合同详情弹窗 -->
|
||
<el-dialog v-model="showDetailDialog" title="合同详情" width="650px" @close="closeDetail">
|
||
<div v-if="currentDetail" class="detail-content">
|
||
<el-descriptions :column="2" border>
|
||
<el-descriptions-item label="合同编号">{{ currentDetail.contract_no }}</el-descriptions-item>
|
||
<el-descriptions-item label="合同名称" :span="2">{{ currentDetail.contract_name }}</el-descriptions-item>
|
||
<el-descriptions-item label="客户ID">{{ currentDetail.customer_id }}</el-descriptions-item>
|
||
<el-descriptions-item label="业务员ID">{{ currentDetail.employee_id }}</el-descriptions-item>
|
||
<el-descriptions-item label="合同金额" :span="2">
|
||
<span class="amount-text">¥{{ currentDetail.amount?.toLocaleString() }}</span>
|
||
</el-descriptions-item>
|
||
<el-descriptions-item label="状态">
|
||
<el-tag :type="getStatusType(currentDetail.status)">{{ currentDetail.status }}</el-tag>
|
||
</el-descriptions-item>
|
||
<el-descriptions-item label="生效日期">{{ currentDetail.effective_date }}</el-descriptions-item>
|
||
<el-descriptions-item label="到期日期" :span="2">{{ currentDetail.expiry_date }}</el-descriptions-item>
|
||
<el-descriptions-item label="合同内容" :span="2">
|
||
<div class="content-text">{{ currentDetail.contract_content || '无' }}</div>
|
||
</el-descriptions-item>
|
||
<el-descriptions-item label="备注" :span="2">
|
||
{{ currentDetail.remark || '无' }}
|
||
</el-descriptions-item>
|
||
</el-descriptions>
|
||
</div>
|
||
<template #footer>
|
||
<el-button @click="closeDetail">关闭</el-button>
|
||
<el-button type="primary" @click="closeDetail(); editContract(currentDetail)">编辑</el-button>
|
||
</template>
|
||
</el-dialog>
|
||
</div>
|
||
</template>
|
||
|
||
<style scoped>
|
||
.contract-container {
|
||
padding: 20px;
|
||
}
|
||
|
||
.contract-search {
|
||
margin-bottom: 20px;
|
||
padding: 15px;
|
||
background: #fff;
|
||
border-radius: 8px;
|
||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
||
}
|
||
|
||
.search-row {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
flex-wrap: wrap;
|
||
gap: 10px;
|
||
}
|
||
|
||
.search-buttons {
|
||
display: flex;
|
||
gap: 10px;
|
||
}
|
||
|
||
.filter-row {
|
||
display: flex;
|
||
align-items: center;
|
||
margin-top: 15px;
|
||
padding-top: 15px;
|
||
border-top: 1px solid #eee;
|
||
}
|
||
|
||
.filter-label {
|
||
color: #606266;
|
||
font-size: 14px;
|
||
margin-right: 10px;
|
||
}
|
||
|
||
.list-header {
|
||
margin-bottom: 15px;
|
||
}
|
||
|
||
.total-count {
|
||
color: #909399;
|
||
font-size: 14px;
|
||
}
|
||
|
||
.loading-container, .empty-container {
|
||
padding: 40px 0;
|
||
}
|
||
|
||
.clickable-row {
|
||
cursor: pointer;
|
||
}
|
||
|
||
.detail-content {
|
||
padding: 10px 0;
|
||
}
|
||
|
||
.amount-text {
|
||
font-weight: 600;
|
||
color: #e6a23c;
|
||
font-size: 16px;
|
||
}
|
||
|
||
.content-text {
|
||
white-space: pre-wrap;
|
||
line-height: 1.6;
|
||
max-height: 120px;
|
||
overflow-y: auto;
|
||
}
|
||
</style> |