添加银行端后端接口

This commit is contained in:
2025-09-24 17:49:32 +08:00
parent b58ed724b0
commit 111ebaec84
95 changed files with 22115 additions and 4246 deletions

View File

@@ -795,6 +795,345 @@ export const api = {
async batchDelete(data) {
return api.delete('/supervision-tasks/batch', { data })
}
},
// 待安装任务API
installationTasks: {
/**
* 获取待安装任务列表
* @param {Object} params - 查询参数
* @returns {Promise} 待安装任务列表
*/
async getList(params = {}) {
return api.get('/installation-tasks', { params })
},
/**
* 获取待安装任务详情
* @param {number} id - 待安装任务ID
* @returns {Promise} 待安装任务详情
*/
async getById(id) {
return api.get(`/installation-tasks/${id}`)
},
/**
* 创建待安装任务
* @param {Object} data - 待安装任务数据
* @returns {Promise} 创建结果
*/
async create(data) {
return api.post('/installation-tasks', data)
},
/**
* 更新待安装任务
* @param {number} id - 待安装任务ID
* @param {Object} data - 待安装任务数据
* @returns {Promise} 更新结果
*/
async update(id, data) {
return api.put(`/installation-tasks/${id}`, data)
},
/**
* 删除待安装任务
* @param {number} id - 待安装任务ID
* @returns {Promise} 删除结果
*/
async delete(id) {
return api.delete(`/installation-tasks/${id}`)
},
/**
* 获取待安装任务统计
* @returns {Promise} 统计数据
*/
async getStats() {
return api.get('/installation-tasks/stats')
},
/**
* 批量更新待安装任务状态
* @param {Object} data - 批量更新数据
* @returns {Promise} 更新结果
*/
async batchUpdateStatus(data) {
return api.put('/installation-tasks/batch/status', data)
},
/**
* 批量删除待安装任务
* @param {Object} data - 批量删除数据
* @returns {Promise} 删除结果
*/
async batchDelete(data) {
return api.delete('/installation-tasks/batch/delete', { data })
}
},
// 监管任务已结项API
completedSupervisions: {
/**
* 获取监管任务已结项列表
* @param {Object} params - 查询参数
* @returns {Promise} 监管任务已结项列表
*/
async getList(params = {}) {
return api.get('/completed-supervisions', { params })
},
/**
* 获取监管任务已结项详情
* @param {number} id - 监管任务已结项ID
* @returns {Promise} 监管任务已结项详情
*/
async getById(id) {
return api.get(`/completed-supervisions/${id}`)
},
/**
* 创建监管任务已结项
* @param {Object} data - 监管任务已结项数据
* @returns {Promise} 创建结果
*/
async create(data) {
return api.post('/completed-supervisions', data)
},
/**
* 更新监管任务已结项
* @param {number} id - 监管任务已结项ID
* @param {Object} data - 监管任务已结项数据
* @returns {Promise} 更新结果
*/
async update(id, data) {
return api.put(`/completed-supervisions/${id}`, data)
},
/**
* 删除监管任务已结项
* @param {number} id - 监管任务已结项ID
* @returns {Promise} 删除结果
*/
async delete(id) {
return api.delete(`/completed-supervisions/${id}`)
},
/**
* 获取监管任务已结项统计
* @returns {Promise} 统计数据
*/
async getStats() {
return api.get('/completed-supervisions/stats')
},
/**
* 批量更新结清状态
* @param {Object} data - 批量更新数据
* @returns {Promise} 更新结果
*/
async batchUpdateStatus(data) {
return api.put('/completed-supervisions/batch/status', data)
},
/**
* 批量删除监管任务已结项
* @param {Object} data - 批量删除数据
* @returns {Promise} 删除结果
*/
async batchDelete(data) {
return api.delete('/completed-supervisions/batch/delete', { data })
}
},
// 贷款商品API
loanProducts: {
/**
* 获取贷款商品列表
* @param {Object} params - 查询参数
* @returns {Promise} 贷款商品列表
*/
async getList(params = {}) {
return api.get('/loan-products', { params })
},
/**
* 获取贷款商品详情
* @param {number} id - 贷款商品ID
* @returns {Promise} 贷款商品详情
*/
async getById(id) {
return api.get(`/loan-products/${id}`)
},
/**
* 创建贷款商品
* @param {Object} data - 贷款商品数据
* @returns {Promise} 创建结果
*/
async create(data) {
return api.post('/loan-products', data)
},
/**
* 更新贷款商品
* @param {number} id - 贷款商品ID
* @param {Object} data - 贷款商品数据
* @returns {Promise} 更新结果
*/
async update(id, data) {
return api.put(`/loan-products/${id}`, data)
},
/**
* 删除贷款商品
* @param {number} id - 贷款商品ID
* @returns {Promise} 删除结果
*/
async delete(id) {
return api.delete(`/loan-products/${id}`)
},
/**
* 获取贷款商品统计
* @returns {Promise} 统计数据
*/
async getStats() {
return api.get('/loan-products/stats')
},
/**
* 批量更新在售状态
* @param {Object} data - 批量更新数据
* @returns {Promise} 更新结果
*/
async batchUpdateStatus(data) {
return api.put('/loan-products/batch/status', data)
},
/**
* 批量删除贷款商品
* @param {Object} data - 批量删除数据
* @returns {Promise} 删除结果
*/
async batchDelete(data) {
return api.delete('/loan-products/batch/delete', { data })
}
},
// 贷款申请API
loanApplications: {
/**
* 获取贷款申请列表
* @param {Object} params - 查询参数
* @returns {Promise} 申请列表
*/
async getList(params = {}) {
return api.get('/loan-applications', { params })
},
/**
* 获取贷款申请详情
* @param {number} id - 申请ID
* @returns {Promise} 申请详情
*/
async getById(id) {
return api.get(`/loan-applications/${id}`)
},
/**
* 审核贷款申请
* @param {number} id - 申请ID
* @param {Object} data - 审核数据
* @returns {Promise} 审核结果
*/
async audit(id, data) {
return api.post(`/loan-applications/${id}/audit`, data)
},
/**
* 获取申请统计信息
* @returns {Promise} 统计信息
*/
async getStats() {
return api.get('/loan-applications/stats')
},
/**
* 批量更新申请状态
* @param {Object} data - 批量操作数据
* @returns {Promise} 更新结果
*/
async batchUpdateStatus(data) {
return api.put('/loan-applications/batch/status', data)
}
},
// 贷款合同API
loanContracts: {
/**
* 获取贷款合同列表
* @param {Object} params - 查询参数
* @returns {Promise} 合同列表
*/
async getList(params = {}) {
return api.get('/loan-contracts', { params })
},
/**
* 获取贷款合同详情
* @param {number} id - 合同ID
* @returns {Promise} 合同详情
*/
async getById(id) {
return api.get(`/loan-contracts/${id}`)
},
/**
* 创建贷款合同
* @param {Object} data - 合同数据
* @returns {Promise} 创建结果
*/
async create(data) {
return api.post('/loan-contracts', data)
},
/**
* 更新贷款合同
* @param {number} id - 合同ID
* @param {Object} data - 合同数据
* @returns {Promise} 更新结果
*/
async update(id, data) {
return api.put(`/loan-contracts/${id}`, data)
},
/**
* 删除贷款合同
* @param {number} id - 合同ID
* @returns {Promise} 删除结果
*/
async delete(id) {
return api.delete(`/loan-contracts/${id}`)
},
/**
* 获取合同统计信息
* @returns {Promise} 统计信息
*/
async getStats() {
return api.get('/loan-contracts/stats')
},
/**
* 批量更新合同状态
* @param {Object} data - 批量操作数据
* @returns {Promise} 更新结果
*/
async batchUpdateStatus(data) {
return api.put('/loan-contracts/batch/status', data)
}
}
}

View File

@@ -72,6 +72,164 @@
</template>
</a-table>
</div>
<!-- 编辑监管任务已结项对话框 -->
<a-modal
v-model:open="editModalVisible"
title="编辑监管任务已结项"
width="800px"
@ok="handleEditTask"
@cancel="handleCancelEdit"
:confirmLoading="editLoading"
>
<a-form
ref="editTaskFormRef"
:model="editTaskForm"
layout="vertical"
>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="申请单号" name="applicationNumber">
<a-input v-model:value="editTaskForm.applicationNumber" placeholder="请输入申请单号" />
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="放款合同编号" name="contractNumber">
<a-input v-model:value="editTaskForm.contractNumber" placeholder="请输入放款合同编号" />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="产品名称" name="productName">
<a-input v-model:value="editTaskForm.productName" placeholder="请输入产品名称" />
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="客户姓名" name="customerName">
<a-input v-model:value="editTaskForm.customerName" placeholder="请输入客户姓名" />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="证件类型" name="idType">
<a-select v-model:value="editTaskForm.idType" placeholder="请选择证件类型">
<a-select-option value="ID_CARD">身份证</a-select-option>
<a-select-option value="PASSPORT">护照</a-select-option>
<a-select-option value="OTHER">其他</a-select-option>
</a-select>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="证件号码" name="idNumber">
<a-input v-model:value="editTaskForm.idNumber" placeholder="请输入证件号码" />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="养殖生资种类" name="assetType">
<a-input v-model:value="editTaskForm.assetType" placeholder="请输入养殖生资种类" />
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="监管生资数量" name="assetQuantity">
<a-input-number
v-model:value="editTaskForm.assetQuantity"
:min="0"
placeholder="请输入监管生资数量"
style="width: 100%"
/>
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="总还款期数" name="totalRepaymentPeriods">
<a-input-number
v-model:value="editTaskForm.totalRepaymentPeriods"
:min="0"
placeholder="请输入总还款期数"
style="width: 100%"
/>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="结清状态" name="settlementStatus">
<a-select v-model:value="editTaskForm.settlementStatus" placeholder="请选择结清状态">
<a-select-option value="settled">已结清</a-select-option>
<a-select-option value="unsettled">未结清</a-select-option>
<a-select-option value="partial">部分结清</a-select-option>
</a-select>
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="结清日期" name="settlementDate">
<a-date-picker
v-model:value="editTaskForm.settlementDate"
placeholder="请选择结清日期"
style="width: 100%"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
/>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="结清任务导入时间" name="importTime">
<a-date-picker
v-model:value="editTaskForm.importTime"
placeholder="请选择导入时间"
style="width: 100%"
format="YYYY-MM-DD HH:mm:ss"
value-format="YYYY-MM-DD HH:mm:ss"
show-time
/>
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="结清金额" name="settlementAmount">
<a-input-number
v-model:value="editTaskForm.settlementAmount"
:min="0"
:precision="2"
placeholder="请输入结清金额"
style="width: 100%"
/>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="剩余金额" name="remainingAmount">
<a-input-number
v-model:value="editTaskForm.remainingAmount"
:min="0"
:precision="2"
placeholder="请输入剩余金额"
style="width: 100%"
/>
</a-form-item>
</a-col>
</a-row>
<a-form-item label="结清备注" name="settlementNotes">
<a-textarea
v-model:value="editTaskForm.settlementNotes"
placeholder="请输入结清备注"
:rows="3"
/>
</a-form-item>
</a-form>
</a-modal>
</div>
</template>
@@ -79,11 +237,19 @@
import { ref, reactive, computed, onMounted } from 'vue'
import { message } from 'ant-design-vue'
import { UploadOutlined, SearchOutlined } from '@ant-design/icons-vue'
import { api } from '@/utils/api'
import dayjs from 'dayjs'
const loading = ref(false)
const tasks = ref([])
// 编辑相关
const editModalVisible = ref(false)
const editTaskFormRef = ref()
const editTaskForm = ref({})
const editLoading = ref(false)
const currentEditTask = ref(null)
const searchForm = reactive({
contractNumber: undefined,
keyword: '',
@@ -170,44 +336,39 @@ const mockTasks = [
const fetchTasks = async () => {
loading.value = true
try {
// 实际项目中这里会调用API获取数据
// const response = await api.completedSupervision.getList({
// page: pagination.current,
// pageSize: pagination.pageSize,
// ...searchForm,
// })
console.log('开始获取监管任务已结项列表...', {
page: pagination.current,
pageSize: pagination.pageSize,
search: searchForm.keyword,
contractNumber: searchForm.contractNumber
})
// 使用模拟数据
tasks.value = mockTasks.map(task => ({
...task,
settlementDate: task.settlementDate ? dayjs(task.settlementDate) : null,
importTime: dayjs(task.importTime),
}))
pagination.total = mockTasks.length
const response = await api.completedSupervisions.getList({
page: pagination.current,
limit: pagination.pageSize,
search: searchForm.keyword,
contractNumber: searchForm.contractNumber
})
console.log('监管任务已结项列表响应:', response)
if (response.success) {
tasks.value = response.data.tasks || []
pagination.total = response.data.pagination.total
} else {
message.error(response.message || '获取监管任务已结项列表失败')
}
} catch (error) {
console.error('获取结项任务失败:', error)
message.error('获取结项任务失败')
console.error('获取监管任务已结项失败:', error)
message.error('获取监管任务已结项失败')
} finally {
loading.value = false
}
}
const filteredTasks = computed(() => {
let result = tasks.value
if (searchForm.contractNumber) {
result = result.filter(task => task.contractNumber === searchForm.contractNumber)
}
if (searchForm.keyword) {
result = result.filter(task =>
task.applicationNumber.toLowerCase().includes(searchForm.keyword.toLowerCase()) ||
task.customerName.toLowerCase().includes(searchForm.keyword.toLowerCase()) ||
task.productName.toLowerCase().includes(searchForm.keyword.toLowerCase())
)
}
return result
// 后端已经处理了过滤,直接返回任务列表
return tasks.value
})
const handleSearch = () => {
@@ -250,16 +411,92 @@ const getSettlementStatusName = (status) => {
return names[status] || status
}
const viewTask = (record) => {
message.info(`查看任务: ${record.applicationNumber}`)
const viewTask = async (record) => {
try {
const response = await api.completedSupervisions.getById(record.id)
if (response.success) {
message.info(`查看任务: ${record.applicationNumber}`)
// 这里可以打开详情对话框显示任务信息
console.log('任务详情:', response.data)
} else {
message.error('获取任务详情失败')
}
} catch (error) {
console.error('获取任务详情失败:', error)
message.error('获取任务详情失败')
}
}
const editTask = (record) => {
message.info(`编辑任务: ${record.applicationNumber}`)
const editTask = async (record) => {
try {
// 保存当前编辑的任务
currentEditTask.value = record
// 填充编辑表单数据
editTaskForm.value = {
applicationNumber: record.applicationNumber || '',
contractNumber: record.contractNumber || '',
productName: record.productName || '',
customerName: record.customerName || '',
idType: record.idType || 'ID_CARD',
idNumber: record.idNumber || '',
assetType: record.assetType || '',
assetQuantity: record.assetQuantity || 0,
totalRepaymentPeriods: record.totalRepaymentPeriods || 0,
settlementStatus: record.settlementStatus || 'unsettled',
settlementDate: record.settlementDate ? dayjs(record.settlementDate) : null,
importTime: record.importTime ? dayjs(record.importTime) : null,
settlementAmount: record.settlementAmount || null,
remainingAmount: record.remainingAmount || null,
settlementNotes: record.settlementNotes || ''
}
// 打开编辑对话框
editModalVisible.value = true
} catch (error) {
console.error('打开编辑对话框失败:', error)
message.error('打开编辑对话框失败')
}
}
const exportTask = (record) => {
message.success(`导出任务: ${record.applicationNumber}`)
const exportTask = async (record) => {
try {
message.success(`导出任务: ${record.applicationNumber}`)
// 这里可以实现导出功能
} catch (error) {
console.error('导出任务失败:', error)
message.error('导出任务失败')
}
}
// 编辑任务处理函数
const handleEditTask = async () => {
try {
editLoading.value = true
const response = await api.completedSupervisions.update(currentEditTask.value.id, editTaskForm.value)
if (response.success) {
message.success('编辑监管任务已结项成功')
editModalVisible.value = false
editTaskFormRef.value.resetFields()
currentEditTask.value = null
fetchTasks() // 刷新列表
} else {
message.error(response.message || '编辑监管任务已结项失败')
}
} catch (error) {
console.error('编辑监管任务已结项失败:', error)
message.error('编辑监管任务已结项失败')
} finally {
editLoading.value = false
}
}
const handleCancelEdit = () => {
editModalVisible.value = false
editTaskFormRef.value.resetFields()
currentEditTask.value = null
}
onMounted(() => {

View File

@@ -80,6 +80,143 @@
</template>
</a-table>
</div>
<!-- 编辑待安装任务对话框 -->
<a-modal
v-model:open="editModalVisible"
title="编辑待安装任务"
width="800px"
@ok="handleEditTask"
@cancel="handleCancelEdit"
:confirmLoading="editLoading"
>
<a-form
ref="editTaskFormRef"
:model="editTaskForm"
layout="vertical"
>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="申请单号" name="applicationNumber">
<a-input v-model:value="editTaskForm.applicationNumber" placeholder="请输入申请单号" />
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="放款合同编号" name="contractNumber">
<a-input v-model:value="editTaskForm.contractNumber" placeholder="请输入放款合同编号" />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="产品名称" name="productName">
<a-input v-model:value="editTaskForm.productName" placeholder="请输入产品名称" />
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="客户姓名" name="customerName">
<a-input v-model:value="editTaskForm.customerName" placeholder="请输入客户姓名" />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="证件类型" name="idType">
<a-select v-model:value="editTaskForm.idType" placeholder="请选择证件类型">
<a-select-option value="ID_CARD">身份证</a-select-option>
<a-select-option value="PASSPORT">护照</a-select-option>
<a-select-option value="OTHER">其他</a-select-option>
</a-select>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="证件号码" name="idNumber">
<a-input v-model:value="editTaskForm.idNumber" placeholder="请输入证件号码" />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="养殖生资种类" name="assetType">
<a-input v-model:value="editTaskForm.assetType" placeholder="请输入养殖生资种类" />
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="待安装设备" name="equipmentToInstall">
<a-input v-model:value="editTaskForm.equipmentToInstall" placeholder="请输入待安装设备" />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="安装状态" name="installationStatus">
<a-select v-model:value="editTaskForm.installationStatus" placeholder="请选择安装状态">
<a-select-option value="pending">待安装</a-select-option>
<a-select-option value="in-progress">安装中</a-select-option>
<a-select-option value="completed">已完成</a-select-option>
<a-select-option value="failed">安装失败</a-select-option>
</a-select>
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="生成安装任务时间" name="taskGenerationTime">
<a-date-picker
v-model:value="editTaskForm.taskGenerationTime"
placeholder="请选择生成时间"
style="width: 100%"
format="YYYY-MM-DD HH:mm:ss"
value-format="YYYY-MM-DD HH:mm:ss"
show-time
/>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="安装完成生效时间" name="completionTime">
<a-date-picker
v-model:value="editTaskForm.completionTime"
placeholder="请选择完成时间"
style="width: 100%"
format="YYYY-MM-DD HH:mm:ss"
value-format="YYYY-MM-DD HH:mm:ss"
show-time
/>
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="安装员姓名" name="installerName">
<a-input v-model:value="editTaskForm.installerName" placeholder="请输入安装员姓名" />
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="安装员电话" name="installerPhone">
<a-input v-model:value="editTaskForm.installerPhone" placeholder="请输入安装员电话" />
</a-form-item>
</a-col>
</a-row>
<a-form-item label="安装地址" name="installationAddress">
<a-input v-model:value="editTaskForm.installationAddress" placeholder="请输入安装地址" />
</a-form-item>
<a-form-item label="安装备注" name="installationNotes">
<a-textarea
v-model:value="editTaskForm.installationNotes"
placeholder="请输入安装备注"
:rows="3"
/>
</a-form-item>
</a-form>
</a-modal>
</div>
</template>
@@ -87,11 +224,19 @@
import { ref, reactive, computed, onMounted } from 'vue'
import { message } from 'ant-design-vue'
import { DownloadOutlined, SearchOutlined } from '@ant-design/icons-vue'
import { api } from '@/utils/api'
import dayjs from 'dayjs'
const loading = ref(false)
const tasks = ref([])
// 编辑相关
const editModalVisible = ref(false)
const editTaskFormRef = ref()
const editTaskForm = ref({})
const editLoading = ref(false)
const currentEditTask = ref(null)
const searchForm = reactive({
contractNumber: '',
dateRange: [],
@@ -176,50 +321,47 @@ const mockTasks = [
const fetchTasks = async () => {
loading.value = true
try {
// 实际项目中这里会调用API获取数据
// const response = await api.installationTasks.getList({
// page: pagination.current,
// pageSize: pagination.pageSize,
// ...searchForm,
// })
// 构建日期范围参数
let dateRangeParam = ''
if (searchForm.dateRange && Array.isArray(searchForm.dateRange) && searchForm.dateRange.length === 2) {
dateRangeParam = `${searchForm.dateRange[0].format('YYYY-MM-DD')},${searchForm.dateRange[1].format('YYYY-MM-DD')}`
}
// 使用模拟数据
tasks.value = mockTasks.map(task => ({
...task,
taskGenerationTime: dayjs(task.taskGenerationTime),
completionTime: task.completionTime ? dayjs(task.completionTime) : null,
}))
pagination.total = mockTasks.length
console.log('开始获取待安装任务列表...', {
page: pagination.current,
pageSize: pagination.pageSize,
search: searchForm.contractNumber,
installationStatus: searchForm.installationStatus,
dateRange: dateRangeParam
})
const response = await api.installationTasks.getList({
page: pagination.current,
limit: pagination.pageSize,
search: searchForm.contractNumber,
installationStatus: searchForm.installationStatus,
dateRange: dateRangeParam
})
console.log('待安装任务列表响应:', response)
if (response.success) {
tasks.value = response.data.tasks || []
pagination.total = response.data.pagination.total
} else {
message.error(response.message || '获取待安装任务列表失败')
}
} catch (error) {
console.error('获取安装任务失败:', error)
message.error('获取安装任务失败')
console.error('获取安装任务失败:', error)
message.error('获取安装任务失败')
} finally {
loading.value = false
}
}
const filteredTasks = computed(() => {
let result = tasks.value
if (searchForm.contractNumber) {
result = result.filter(task =>
task.contractNumber.toLowerCase().includes(searchForm.contractNumber.toLowerCase())
)
}
if (searchForm.installationStatus) {
result = result.filter(task => task.installationStatus === searchForm.installationStatus)
}
if (searchForm.dateRange && searchForm.dateRange.length === 2) {
const [startDate, endDate] = searchForm.dateRange
result = result.filter(task => {
const taskTime = dayjs(task.taskGenerationTime)
return taskTime.isAfter(startDate.startOf('day')) && taskTime.isBefore(endDate.endOf('day'))
})
}
return result
// 后端已经处理了过滤,直接返回任务列表
return tasks.value
})
const handleSearch = () => {
@@ -265,16 +407,100 @@ const getStatusName = (status) => {
return names[status] || status
}
const viewTask = (record) => {
message.info(`查看任务: ${record.applicationNumber}`)
const viewTask = async (record) => {
try {
const response = await api.installationTasks.getById(record.id)
if (response.success) {
message.info(`查看任务: ${record.applicationNumber}`)
// 这里可以打开详情对话框显示任务信息
console.log('任务详情:', response.data)
} else {
message.error('获取任务详情失败')
}
} catch (error) {
console.error('获取任务详情失败:', error)
message.error('获取任务详情失败')
}
}
const editTask = (record) => {
message.info(`编辑任务: ${record.applicationNumber}`)
const editTask = async (record) => {
try {
// 保存当前编辑的任务
currentEditTask.value = record
// 填充编辑表单数据
editTaskForm.value = {
applicationNumber: record.applicationNumber || '',
contractNumber: record.contractNumber || '',
productName: record.productName || '',
customerName: record.customerName || '',
idType: record.idType || 'ID_CARD',
idNumber: record.idNumber || '',
assetType: record.assetType || '',
equipmentToInstall: record.equipmentToInstall || '',
installationStatus: record.installationStatus || 'pending',
taskGenerationTime: record.taskGenerationTime ? dayjs(record.taskGenerationTime) : null,
completionTime: record.completionTime ? dayjs(record.completionTime) : null,
installerName: record.installerName || '',
installerPhone: record.installerPhone || '',
installationAddress: record.installationAddress || '',
installationNotes: record.installationNotes || ''
}
// 打开编辑对话框
editModalVisible.value = true
} catch (error) {
console.error('打开编辑对话框失败:', error)
message.error('打开编辑对话框失败')
}
}
const startInstallation = (record) => {
message.success(`开始安装任务: ${record.applicationNumber}`)
const startInstallation = async (record) => {
try {
const response = await api.installationTasks.update(record.id, {
installationStatus: 'in-progress'
})
if (response.success) {
message.success(`开始安装任务: ${record.applicationNumber}`)
fetchTasks() // 刷新列表
} else {
message.error('开始安装任务失败')
}
} catch (error) {
console.error('开始安装任务失败:', error)
message.error('开始安装任务失败')
}
}
// 编辑任务处理函数
const handleEditTask = async () => {
try {
editLoading.value = true
const response = await api.installationTasks.update(currentEditTask.value.id, editTaskForm.value)
if (response.success) {
message.success('编辑待安装任务成功')
editModalVisible.value = false
editTaskFormRef.value.resetFields()
currentEditTask.value = null
fetchTasks() // 刷新列表
} else {
message.error(response.message || '编辑待安装任务失败')
}
} catch (error) {
console.error('编辑待安装任务失败:', error)
message.error('编辑待安装任务失败')
} finally {
editLoading.value = false
}
}
const handleCancelEdit = () => {
editModalVisible.value = false
editTaskFormRef.value.resetFields()
currentEditTask.value = null
}
onMounted(() => {

View File

@@ -7,9 +7,9 @@
<a-button type="primary" @click="showAddTaskModal">
<plus-outlined /> 新增监管任务
</a-button>
<a-button type="primary" @click="showBatchAddModal">
<!-- <a-button type="primary" @click="showBatchAddModal">
<plus-outlined /> 批量新增
</a-button>
</a-button> -->
</div>
</div>
@@ -221,6 +221,190 @@
</a-descriptions>
</div>
</a-modal>
<!-- 编辑监管任务对话框 -->
<a-modal
v-model:open="editModalVisible"
title="编辑监管任务"
width="800px"
@ok="handleEditTask"
@cancel="handleCancelEdit"
:confirmLoading="editLoading"
>
<a-form
ref="editTaskFormRef"
:model="editTaskForm"
:rules="addTaskRules"
layout="vertical"
>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="申请单号" name="applicationNumber">
<a-input v-model:value="editTaskForm.applicationNumber" placeholder="请输入申请单号" />
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="放款合同编号" name="contractNumber">
<a-input v-model:value="editTaskForm.contractNumber" placeholder="请输入放款合同编号" />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="产品名称" name="productName">
<a-input v-model:value="editTaskForm.productName" placeholder="请输入产品名称" />
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="客户姓名" name="customerName">
<a-input v-model:value="editTaskForm.customerName" placeholder="请输入客户姓名" />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="证件类型" name="idType">
<a-select v-model:value="editTaskForm.idType" placeholder="请选择证件类型">
<a-select-option value="id_card">身份证</a-select-option>
<a-select-option value="passport">护照</a-select-option>
<a-select-option value="other">其他</a-select-option>
</a-select>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="证件号码" name="idNumber">
<a-input v-model:value="editTaskForm.idNumber" placeholder="请输入证件号码" />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="养殖生资种类" name="assetType">
<a-select v-model:value="editTaskForm.assetType" placeholder="请选择养殖生资种类">
<a-select-option value="cattle"></a-select-option>
<a-select-option value="sheep"></a-select-option>
<a-select-option value="pig"></a-select-option>
<a-select-option value="poultry">家禽</a-select-option>
<a-select-option value="other">其他</a-select-option>
</a-select>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="监管生资数量" name="assetQuantity">
<a-input-number
v-model:value="editTaskForm.assetQuantity"
:min="0"
placeholder="请输入监管生资数量"
style="width: 100%"
/>
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="监管状态" name="supervisionStatus">
<a-select v-model:value="editTaskForm.supervisionStatus" placeholder="请选择监管状态">
<a-select-option value="pending">待监管</a-select-option>
<a-select-option value="supervising">监管中</a-select-option>
<a-select-option value="completed">已完成</a-select-option>
<a-select-option value="suspended">已暂停</a-select-option>
</a-select>
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="监管起始时间" name="startTime">
<a-date-picker
v-model:value="editTaskForm.startTime"
placeholder="请选择监管起始时间"
style="width: 100%"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
/>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="监管结束时间" name="endTime">
<a-date-picker
v-model:value="editTaskForm.endTime"
placeholder="请选择监管结束时间"
style="width: 100%"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
/>
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :span="8">
<a-form-item label="贷款金额" name="loanAmount">
<a-input-number
v-model:value="editTaskForm.loanAmount"
:min="0"
:precision="2"
placeholder="请输入贷款金额"
style="width: 100%"
/>
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item label="利率" name="interestRate">
<a-input-number
v-model:value="editTaskForm.interestRate"
:min="0"
:max="1"
:step="0.0001"
:precision="4"
placeholder="请输入利率"
style="width: 100%"
/>
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item label="贷款期限(月)" name="loanTerm">
<a-input-number
v-model:value="editTaskForm.loanTerm"
:min="0"
placeholder="请输入贷款期限"
style="width: 100%"
/>
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="监管员姓名" name="supervisorName">
<a-input v-model:value="editTaskForm.supervisorName" placeholder="请输入监管员姓名" />
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="监管员电话" name="supervisorPhone">
<a-input v-model:value="editTaskForm.supervisorPhone" placeholder="请输入监管员电话" />
</a-form-item>
</a-col>
</a-row>
<a-form-item label="养殖场地址" name="farmAddress">
<a-input v-model:value="editTaskForm.farmAddress" placeholder="请输入养殖场地址" />
</a-form-item>
<a-form-item label="备注" name="remarks">
<a-textarea
v-model:value="editTaskForm.remarks"
placeholder="请输入备注"
:rows="3"
/>
</a-form-item>
</a-form>
</a-modal>
</div>
</template>
@@ -241,6 +425,13 @@ const detailModalVisible = ref(false)
const selectedTask = ref(null)
const addTaskFormRef = ref()
// 编辑相关
const editModalVisible = ref(false)
const editTaskFormRef = ref()
const editTaskForm = ref({})
const editLoading = ref(false)
const currentEditTask = ref(null)
// 搜索表单
const searchForm = ref({
contractNumber: '',
@@ -463,13 +654,18 @@ const viewTask = (task) => {
const fetchTasks = async (params = {}) => {
try {
loading.value = true
// 构建日期范围参数
let dateRangeParam = ''
if (searchForm.value.dateRange && Array.isArray(searchForm.value.dateRange) && searchForm.value.dateRange.length === 2) {
dateRangeParam = `${searchForm.value.dateRange[0].format('YYYY-MM-DD')},${searchForm.value.dateRange[1].format('YYYY-MM-DD')}`
}
console.log('开始获取监管任务列表...', {
page: pagination.value.current,
limit: pagination.value.pageSize,
search: searchForm.value.contractNumber,
supervisionStatus: searchForm.value.supervisionStatus,
dateRange: searchForm.value.dateRange ?
`${searchForm.value.dateRange[0].format('YYYY-MM-DD')},${searchForm.value.dateRange[1].format('YYYY-MM-DD')}` : ''
dateRange: dateRangeParam
})
const response = await api.supervisionTasks.getList({
@@ -477,8 +673,7 @@ const fetchTasks = async (params = {}) => {
limit: pagination.value.pageSize,
search: searchForm.value.contractNumber,
supervisionStatus: searchForm.value.supervisionStatus,
dateRange: searchForm.value.dateRange ?
`${searchForm.value.dateRange[0].format('YYYY-MM-DD')},${searchForm.value.dateRange[1].format('YYYY-MM-DD')}` : '',
dateRange: dateRangeParam,
...params
})
@@ -522,11 +717,36 @@ const handleReset = () => {
const editTask = async (task) => {
try {
// 这里可以实现编辑功能
message.info(`编辑任务: ${task.applicationNumber}`)
// 保存当前编辑的任务
currentEditTask.value = task
// 填充编辑表单数据
editTaskForm.value = {
applicationNumber: task.applicationNumber || '',
contractNumber: task.contractNumber || '',
productName: task.productName || '',
customerName: task.customerName || '',
idType: task.idType || '',
idNumber: task.idNumber || '',
assetType: task.assetType || '',
assetQuantity: task.assetQuantity || 0,
supervisionStatus: task.supervisionStatus || '',
startTime: task.startTime || null,
endTime: task.endTime || null,
loanAmount: task.loanAmount || 0,
interestRate: task.interestRate || 0,
loanTerm: task.loanTerm || 0,
supervisorName: task.supervisorName || '',
supervisorPhone: task.supervisorPhone || '',
farmAddress: task.farmAddress || '',
remarks: task.remarks || ''
}
// 打开编辑对话框
editModalVisible.value = true
} catch (error) {
console.error('编辑任务失败:', error)
message.error('编辑任务失败')
console.error('打开编辑对话框失败:', error)
message.error('打开编辑对话框失败')
}
}
@@ -570,6 +790,36 @@ const handleCancelAdd = () => {
addTaskFormRef.value.resetFields()
}
// 编辑任务处理函数
const handleEditTask = async () => {
try {
await editTaskFormRef.value.validate()
editLoading.value = true
const response = await api.supervisionTasks.update(currentEditTask.value.id, editTaskForm.value)
if (response.success) {
message.success('编辑监管任务成功')
editModalVisible.value = false
editTaskFormRef.value.resetFields()
currentEditTask.value = null
fetchTasks() // 刷新列表
} else {
message.error(response.message || '编辑监管任务失败')
}
} catch (error) {
console.error('编辑监管任务失败:', error)
message.error('编辑监管任务失败')
} finally {
editLoading.value = false
}
}
const handleCancelEdit = () => {
editModalVisible.value = false
editTaskFormRef.value.resetFields()
}
const handleExport = () => {
message.info('任务导出功能开发中...')
}

View File

@@ -183,6 +183,7 @@
import { ref, computed, onMounted } from 'vue'
import { message } from 'ant-design-vue'
import { SearchOutlined } from '@ant-design/icons-vue'
import api from '@/utils/api'
// 响应式数据
const loading = ref(false)
@@ -293,94 +294,8 @@ const columns = [
}
]
// 模拟申请数据
const applications = ref([
{
id: 1,
applicationNumber: '20240325123703784',
productName: '惠农贷',
farmerName: '刘超',
borrowerName: '11',
borrowerIdNumber: '511***********3017',
assetType: '牛',
applicationQuantity: '10头',
policyInfo: '查看保单',
amount: 100000.00,
status: 'pending_review',
applicationTime: '2024-03-25 12:37:03',
phone: '13800138000',
purpose: '养殖贷款',
remark: '',
auditRecords: [
{
id: 1,
action: 'submit',
auditor: '刘超',
time: '2024-03-25 12:37:03',
comment: '提交申请'
}
]
},
{
id: 2,
applicationNumber: '20240229110801968',
productName: '中国工商银行扎旗支行"畜禽活体抵押"',
farmerName: '刘超',
borrowerName: '1',
borrowerIdNumber: '511***********3017',
assetType: '牛',
applicationQuantity: '10头',
policyInfo: '查看保单',
amount: 100000.00,
status: 'verification_pending',
applicationTime: '2024-02-29 11:08:01',
phone: '13900139000',
purpose: '养殖贷款',
remark: '',
auditRecords: [
{
id: 1,
action: 'submit',
auditor: '刘超',
time: '2024-02-29 11:08:01',
comment: '提交申请'
},
{
id: 2,
action: 'approve',
auditor: '王经理',
time: '2024-03-01 10:15:00',
comment: '资料齐全,符合条件,同意放款'
}
]
},
{
id: 3,
applicationNumber: '20240229105806431',
productName: '惠农贷',
farmerName: '刘超',
borrowerName: '1',
borrowerIdNumber: '511***********3017',
assetType: '牛',
applicationQuantity: '10头',
policyInfo: '查看保单',
amount: 100000.00,
status: 'pending_binding',
applicationTime: '2024-02-29 10:58:06',
phone: '13700137000',
purpose: '养殖贷款',
remark: '',
auditRecords: [
{
id: 1,
action: 'submit',
auditor: '刘超',
time: '2024-02-29 10:58:06',
comment: '提交申请'
}
]
}
])
// 申请数据
const applications = ref([])
// 计算属性
const filteredApplications = computed(() => {
@@ -406,9 +321,35 @@ const filteredApplications = computed(() => {
return result
})
// 获取申请列表
const fetchApplications = async () => {
try {
loading.value = true
const response = await api.loanApplications.getList({
page: pagination.value.current,
pageSize: pagination.value.pageSize,
searchField: searchQuery.value.field,
searchValue: searchQuery.value.value
})
if (response.success) {
applications.value = response.data.applications
pagination.value.total = response.data.pagination.total
} else {
message.error(response.message || '获取申请列表失败')
}
} catch (error) {
console.error('获取申请列表失败:', error)
message.error('获取申请列表失败')
} finally {
loading.value = false
}
}
// 方法
const handleSearch = () => {
// 搜索逻辑已在计算属性中处理
pagination.value.current = 1
fetchApplications()
}
const handleReset = () => {
@@ -421,6 +362,7 @@ const handleReset = () => {
const handleTableChange = (pag) => {
pagination.value.current = pag.current
pagination.value.pageSize = pag.pageSize
fetchApplications()
}
const handleView = (record) => {
@@ -447,26 +389,29 @@ const viewPolicy = (record) => {
// 实际项目中这里会打开保单详情页面
}
const handleAuditSubmit = () => {
const handleAuditSubmit = async () => {
if (!auditForm.value.comment) {
message.error('请输入审核意见')
return
}
// 更新申请状态
selectedApplication.value.status = auditForm.value.action === 'approve' ? 'approved' : 'rejected'
// 添加审核记录
selectedApplication.value.auditRecords.push({
id: Date.now(),
action: auditForm.value.action,
auditor: '当前用户',
time: new Date().toLocaleString(),
comment: auditForm.value.comment
})
try {
const response = await api.loanApplications.audit(selectedApplication.value.id, {
action: auditForm.value.action,
comment: auditForm.value.comment
})
auditModalVisible.value = false
message.success('审核完成')
if (response.success) {
message.success('审核完成')
auditModalVisible.value = false
fetchApplications() // 刷新列表
} else {
message.error(response.message || '审核失败')
}
} catch (error) {
console.error('审核失败:', error)
message.error('审核失败')
}
}
const handleAuditCancel = () => {
@@ -554,7 +499,7 @@ const formatAmount = (amount) => {
// 生命周期
onMounted(() => {
pagination.value.total = applications.value.length
fetchApplications()
})
</script>

View File

@@ -12,8 +12,10 @@
placeholder="申请单号"
style="width: 100%"
>
<a-select-option value="contractNumber">合同编号</a-select-option>
<a-select-option value="applicationNumber">申请单号</a-select-option>
<a-select-option value="customerName">客户姓名</a-select-option>
<a-select-option value="borrowerName">贷款人姓名</a-select-option>
<a-select-option value="farmerName">申请养殖户</a-select-option>
<a-select-option value="productName">贷款产品</a-select-option>
</a-select>
</a-col>
@@ -38,12 +40,13 @@
<div class="contracts-table-section">
<a-table
:columns="columns"
:data-source="filteredContracts"
:data-source="contracts"
:pagination="pagination"
:loading="loading"
row-key="id"
@change="handleTableChange"
:locale="{ emptyText: '暂无数据' }"
:expand-row-by-click="false"
:expand-icon-column-index="0"
>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'status'">
@@ -54,16 +57,16 @@
<template v-else-if="column.key === 'amount'">
{{ formatAmount(record.amount) }}
</template>
<template v-else-if="column.key === 'paidAmount'">
{{ formatAmount(record.paidAmount) }}
</template>
<template v-else-if="column.key === 'action'">
<a-space>
<a-button type="link" size="small" @click="handleView(record)">
查看
</a-button>
<a-button type="link" size="small" @click="handleEdit(record)">
编辑
</a-button>
<a-button type="link" size="small" @click="handleDownload(record)">
下载
<a-button type="link" size="small" @click="handleView(record)">
详情
</a-button>
</a-space>
</template>
@@ -75,7 +78,7 @@
<a-modal
v-model:open="detailModalVisible"
title="合同详情"
width="900px"
width="800px"
:footer="null"
>
<div v-if="selectedContract" class="contract-detail">
@@ -83,110 +86,229 @@
<a-descriptions-item label="合同编号">
{{ selectedContract.contractNumber }}
</a-descriptions-item>
<a-descriptions-item label="客户姓名">
{{ selectedContract.customerName }}
<a-descriptions-item label="申请单号">
{{ selectedContract.applicationNumber }}
</a-descriptions-item>
<a-descriptions-item label="合同类型">
<a-tag :color="getTypeColor(selectedContract.type)">
{{ getTypeText(selectedContract.type) }}
</a-tag>
<a-descriptions-item label="贷款产品">
{{ selectedContract.productName }}
</a-descriptions-item>
<a-descriptions-item label="申请养殖户">
{{ selectedContract.farmerName }}
</a-descriptions-item>
<a-descriptions-item label="贷款人姓名">
{{ selectedContract.borrowerName }}
</a-descriptions-item>
<a-descriptions-item label="贷款人身份证号">
{{ selectedContract.borrowerIdNumber }}
</a-descriptions-item>
<a-descriptions-item label="生资种类">
{{ selectedContract.assetType }}
</a-descriptions-item>
<a-descriptions-item label="申请数量">
{{ selectedContract.applicationQuantity }}
</a-descriptions-item>
<a-descriptions-item label="合同金额">
{{ formatAmount(selectedContract.amount) }}
</a-descriptions-item>
<a-descriptions-item label="已还款金额">
{{ formatAmount(selectedContract.paidAmount) }}
</a-descriptions-item>
<a-descriptions-item label="剩余金额">
{{ formatAmount(selectedContract.remainingAmount) }}
</a-descriptions-item>
<a-descriptions-item label="合同状态">
<a-tag :color="getStatusColor(selectedContract.status)">
{{ getStatusText(selectedContract.status) }}
</a-tag>
</a-descriptions-item>
<a-descriptions-item label="贷款金额">
{{ formatAmount(selectedContract.amount) }}
<a-descriptions-item label="合同类型">
{{ getTypeText(selectedContract.type) }}
</a-descriptions-item>
<a-descriptions-item label="贷款期限">
<a-descriptions-item label="合同期限">
{{ selectedContract.term }} 个月
</a-descriptions-item>
<a-descriptions-item label="利率">
<a-descriptions-item label="利率">
{{ selectedContract.interestRate }}%
</a-descriptions-item>
<a-descriptions-item label="还款方式">
{{ getRepaymentMethodText(selectedContract.repaymentMethod) }}
</a-descriptions-item>
<a-descriptions-item label="合同签署日期">
{{ selectedContract.signDate || '未签署' }}
</a-descriptions-item>
<a-descriptions-item label="合同生效日期">
{{ selectedContract.effectiveDate || '未生效' }}
</a-descriptions-item>
<a-descriptions-item label="到期日期">
{{ selectedContract.maturityDate }}
</a-descriptions-item>
<a-descriptions-item label="联系电话">
{{ selectedContract.phone }}
</a-descriptions-item>
<a-descriptions-item label="身份证号">
{{ selectedContract.idCard }}
<a-descriptions-item label="贷款用途">
{{ selectedContract.purpose }}
</a-descriptions-item>
<a-descriptions-item label="合同条款" :span="2">
<div class="contract-terms">
<p v-for="(term, index) in selectedContract.terms" :key="index">
{{ index + 1 }}. {{ term }}
</p>
</div>
<a-descriptions-item label="合同签订时间">
{{ selectedContract.contractTime }}
</a-descriptions-item>
<a-descriptions-item label="放款时间">
{{ selectedContract.disbursementTime || '未放款' }}
</a-descriptions-item>
<a-descriptions-item label="到期时间">
{{ selectedContract.maturityTime || '未设置' }}
</a-descriptions-item>
<a-descriptions-item label="完成时间">
{{ selectedContract.completedTime || '未完成' }}
</a-descriptions-item>
<a-descriptions-item label="备注" :span="2">
{{ selectedContract.remark || '无' }}
</a-descriptions-item>
</a-descriptions>
<!-- 合同历史 -->
<div class="contract-history" v-if="selectedContract.history">
<h4>合同历史</h4>
<a-timeline>
<a-timeline-item
v-for="record in selectedContract.history"
:key="record.id"
:color="getHistoryColor(record.action)"
>
<div class="history-item">
<div class="history-header">
<span class="history-action">{{ getHistoryActionText(record.action) }}</span>
<span class="history-time">{{ record.time }}</span>
</div>
<div class="history-user">操作人{{ record.operator }}</div>
<div class="history-comment" v-if="record.comment">
备注{{ record.comment }}
</div>
</div>
</a-timeline-item>
</a-timeline>
</div>
</div>
</a-modal>
<!-- 合同签署模态框 -->
<!-- 编辑合同模态框 -->
<a-modal
v-model:open="signModalVisible"
title="合同签署"
@ok="handleSignSubmit"
@cancel="handleSignCancel"
v-model:open="editModalVisible"
title="编辑合同"
width="800px"
:confirm-loading="editLoading"
@ok="handleEditSubmit"
@cancel="handleEditCancel"
>
<div class="sign-content">
<a-alert
message="请确认合同信息无误后签署"
type="info"
show-icon
style="margin-bottom: 16px"
/>
<a-form :model="signForm" layout="vertical">
<a-form-item label="签署密码" required>
<a-input-password
v-model:value="signForm.password"
placeholder="请输入签署密码"
/>
</a-form-item>
<a-form-item label="签署备注">
<a-textarea
v-model:value="signForm.comment"
placeholder="请输入签署备注(可选)"
:rows="3"
/>
</a-form-item>
</a-form>
</div>
<a-form
ref="editFormRef"
:model="editForm"
:rules="editFormRules"
layout="vertical"
v-if="editModalVisible"
>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="贷款产品" name="productName">
<a-input v-model:value="editForm.productName" placeholder="请输入贷款产品名称" />
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="申请养殖户" name="farmerName">
<a-input v-model:value="editForm.farmerName" placeholder="请输入申请养殖户姓名" />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="贷款人姓名" name="borrowerName">
<a-input v-model:value="editForm.borrowerName" placeholder="请输入贷款人姓名" />
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="贷款人身份证号" name="borrowerIdNumber">
<a-input v-model:value="editForm.borrowerIdNumber" placeholder="请输入身份证号" />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="生资种类" name="assetType">
<a-input v-model:value="editForm.assetType" placeholder="请输入生资种类" />
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="申请数量" name="applicationQuantity">
<a-input v-model:value="editForm.applicationQuantity" placeholder="请输入申请数量" />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="合同金额" name="amount">
<a-input-number
v-model:value="editForm.amount"
placeholder="请输入合同金额"
:min="0"
:precision="2"
style="width: 100%"
addon-after=""
/>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="已还款金额" name="paidAmount">
<a-input-number
v-model:value="editForm.paidAmount"
placeholder="请输入已还款金额"
:min="0"
:precision="2"
style="width: 100%"
addon-after=""
/>
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="合同状态" name="status">
<a-select v-model:value="editForm.status" placeholder="请选择合同状态">
<a-select-option value="pending">待放款</a-select-option>
<a-select-option value="active">已放款</a-select-option>
<a-select-option value="completed">已完成</a-select-option>
<a-select-option value="defaulted">违约</a-select-option>
<a-select-option value="cancelled">已取消</a-select-option>
</a-select>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="合同类型" name="type">
<a-select v-model:value="editForm.type" placeholder="请选择合同类型">
<a-select-option value="livestock_collateral">畜禽活体抵押</a-select-option>
<a-select-option value="farmer_loan">惠农贷</a-select-option>
<a-select-option value="business_loan">商业贷款</a-select-option>
<a-select-option value="personal_loan">个人贷款</a-select-option>
</a-select>
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="合同期限" name="term">
<a-input-number
v-model:value="editForm.term"
placeholder="请输入合同期限"
:min="1"
style="width: 100%"
addon-after="个月"
/>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="利率" name="interestRate">
<a-input-number
v-model:value="editForm.interestRate"
placeholder="请输入利率"
:min="0"
:max="100"
:precision="2"
style="width: 100%"
addon-after="%"
/>
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="联系电话" name="phone">
<a-input v-model:value="editForm.phone" placeholder="请输入联系电话" />
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="贷款用途" name="purpose">
<a-input v-model:value="editForm.purpose" placeholder="请输入贷款用途" />
</a-form-item>
</a-col>
</a-row>
<a-form-item label="备注" name="remark">
<a-textarea
v-model:value="editForm.remark"
placeholder="请输入备注"
:rows="3"
/>
</a-form-item>
</a-form>
</a-modal>
</div>
</template>
@@ -195,21 +317,85 @@
import { ref, computed, onMounted } from 'vue'
import { message } from 'ant-design-vue'
import { SearchOutlined } from '@ant-design/icons-vue'
import api from '@/utils/api'
// 响应式数据
const loading = ref(false)
const searchQuery = ref({
field: 'applicationNumber',
field: 'contractNumber',
value: ''
})
const detailModalVisible = ref(false)
const signModalVisible = ref(false)
const editModalVisible = ref(false)
const editLoading = ref(false)
const selectedContract = ref(null)
const signForm = ref({
password: '',
comment: ''
const contracts = ref([])
// 编辑表单
const editForm = ref({
id: null,
productName: '',
farmerName: '',
borrowerName: '',
borrowerIdNumber: '',
assetType: '',
applicationQuantity: '',
amount: null,
paidAmount: null,
status: 'pending',
type: 'livestock_collateral',
term: null,
interestRate: null,
phone: '',
purpose: '',
remark: ''
})
const editFormRef = ref()
// 表单验证规则
const editFormRules = {
productName: [
{ required: true, message: '请输入贷款产品名称', trigger: 'blur' }
],
farmerName: [
{ required: true, message: '请输入申请养殖户姓名', trigger: 'blur' }
],
borrowerName: [
{ required: true, message: '请输入贷款人姓名', trigger: 'blur' }
],
borrowerIdNumber: [
{ required: true, message: '请输入贷款人身份证号', trigger: 'blur' }
],
assetType: [
{ required: true, message: '请输入生资种类', trigger: 'blur' }
],
applicationQuantity: [
{ required: true, message: '请输入申请数量', trigger: 'blur' }
],
amount: [
{ required: true, message: '请输入合同金额', trigger: 'blur' },
{ type: 'number', min: 0.01, message: '合同金额必须大于0', trigger: 'blur' }
],
status: [
{ required: true, message: '请选择合同状态', trigger: 'change' }
],
type: [
{ required: true, message: '请选择合同类型', trigger: 'change' }
],
term: [
{ required: true, message: '请输入合同期限', trigger: 'blur' },
{ type: 'number', min: 1, message: '合同期限必须大于0', trigger: 'blur' }
],
interestRate: [
{ required: true, message: '请输入利率', trigger: 'blur' },
{ type: 'number', min: 0, max: 100, message: '利率必须在0-100之间', trigger: 'blur' }
],
phone: [
{ required: true, message: '请输入联系电话', trigger: 'blur' }
]
}
// 分页配置
const pagination = ref({
current: 1,
@@ -222,6 +408,12 @@ const pagination = ref({
// 表格列配置
const columns = [
{
title: '',
key: 'expand',
width: 50,
customRender: () => '>'
},
{
title: '申请单号',
dataIndex: 'applicationNumber',
@@ -276,51 +468,57 @@ const columns = [
title: '当前状态',
dataIndex: 'status',
key: 'status',
width: 120
width: 120,
filters: [
{ text: '待放款', value: 'pending' },
{ text: '已放款', value: 'active' },
{ text: '已完成', value: 'completed' },
{ text: '违约', value: 'defaulted' },
{ text: '已取消', value: 'cancelled' }
]
},
{
title: '操作',
key: 'action',
width: 200,
width: 150,
fixed: 'right'
}
]
// 模拟合同数据 - 设置为空数据以匹配图片
const contracts = ref([])
// 计算属性
const filteredContracts = computed(() => {
let result = contracts.value
if (searchQuery.value.value) {
const searchValue = searchQuery.value.value.toLowerCase()
const field = searchQuery.value.field
result = result.filter(contract => {
if (field === 'applicationNumber') {
return contract.applicationNumber.toLowerCase().includes(searchValue)
} else if (field === 'customerName') {
return contract.borrowerName.toLowerCase().includes(searchValue) ||
contract.farmerName.toLowerCase().includes(searchValue)
} else if (field === 'productName') {
return contract.productName.toLowerCase().includes(searchValue)
}
return true
// 获取合同列表
const fetchContracts = async () => {
try {
loading.value = true
const response = await api.loanContracts.getList({
page: pagination.value.current,
pageSize: pagination.value.pageSize,
searchField: searchQuery.value.field,
searchValue: searchQuery.value.value
})
if (response.success) {
contracts.value = response.data.contracts
pagination.value.total = response.data.pagination.total
} else {
message.error(response.message || '获取合同列表失败')
}
} catch (error) {
console.error('获取合同列表失败:', error)
message.error('获取合同列表失败')
} finally {
loading.value = false
}
return result
})
}
// 方法
const handleSearch = () => {
// 搜索逻辑已在计算属性中处理
pagination.value.current = 1
fetchContracts()
}
const handleReset = () => {
searchQuery.value = {
field: 'applicationNumber',
field: 'contractNumber',
value: ''
}
}
@@ -328,6 +526,7 @@ const handleReset = () => {
const handleTableChange = (pag) => {
pagination.value.current = pag.current
pagination.value.pageSize = pag.pageSize
fetchContracts()
}
const handleView = (record) => {
@@ -336,127 +535,109 @@ const handleView = (record) => {
}
const handleEdit = (record) => {
message.info(`编辑合同: ${record.applicationNumber}`)
}
const handleDownload = (record) => {
message.info(`下载合同: ${record.applicationNumber}`)
}
const handleSignSubmit = () => {
if (!signForm.value.password) {
message.error('请输入签署密码')
return
}
// 更新合同状态
selectedContract.value.status = 'signed'
selectedContract.value.signDate = new Date().toISOString().split('T')[0]
selectedContract.value.effectiveDate = new Date().toISOString().split('T')[0]
// 添加历史记录
selectedContract.value.history.push({
id: Date.now(),
action: 'sign',
operator: '当前用户',
time: new Date().toLocaleString(),
comment: signForm.value.comment || '合同签署'
Object.assign(editForm.value, {
id: record.id,
productName: record.productName,
farmerName: record.farmerName,
borrowerName: record.borrowerName,
borrowerIdNumber: record.borrowerIdNumber,
assetType: record.assetType,
applicationQuantity: record.applicationQuantity,
amount: record.amount,
paidAmount: record.paidAmount,
status: record.status,
type: record.type,
term: record.term,
interestRate: record.interestRate,
phone: record.phone,
purpose: record.purpose,
remark: record.remark
})
signModalVisible.value = false
message.success('合同签署成功')
editModalVisible.value = true
}
const handleSignCancel = () => {
signModalVisible.value = false
selectedContract.value = null
const handleEditSubmit = async () => {
try {
await editFormRef.value.validate()
editLoading.value = true
const response = await api.loanContracts.update(editForm.value.id, {
productName: editForm.value.productName,
farmerName: editForm.value.farmerName,
borrowerName: editForm.value.borrowerName,
borrowerIdNumber: editForm.value.borrowerIdNumber,
assetType: editForm.value.assetType,
applicationQuantity: editForm.value.applicationQuantity,
amount: editForm.value.amount,
paidAmount: editForm.value.paidAmount,
status: editForm.value.status,
type: editForm.value.type,
term: editForm.value.term,
interestRate: editForm.value.interestRate,
phone: editForm.value.phone,
purpose: editForm.value.purpose,
remark: editForm.value.remark
})
if (response.success) {
message.success('合同更新成功')
editModalVisible.value = false
fetchContracts() // 刷新列表
} else {
message.error(response.message || '更新失败')
}
} catch (error) {
console.error('更新失败:', error)
message.error('更新失败')
} finally {
editLoading.value = false
}
}
const handleEditCancel = () => {
editModalVisible.value = false
editFormRef.value?.resetFields()
}
const getStatusColor = (status) => {
const colors = {
pending_review: 'blue',
verification_pending: 'blue',
pending_binding: 'blue',
approved: 'green',
rejected: 'red',
signed: 'green',
pending: 'blue',
active: 'green',
completed: 'success',
terminated: 'red'
completed: 'cyan',
defaulted: 'red',
cancelled: 'gray'
}
return colors[status] || 'default'
}
const getStatusText = (status) => {
const texts = {
pending_review: '待初审',
verification_pending: '核验待放款',
pending_binding: '待绑定',
approved: '已通过',
rejected: '已拒绝',
signed: '已签署',
active: '生效中',
pending: '待放款',
active: '放款',
completed: '已完成',
terminated: '已终止'
defaulted: '违约',
cancelled: '已取消'
}
return texts[status] || status
}
const getTypeColor = (type) => {
const colors = {
personal: 'blue',
business: 'green',
mortgage: 'purple'
}
return colors[type] || 'default'
}
const getTypeText = (type) => {
const texts = {
personal: '个人贷款',
business: '企业贷款',
mortgage: '抵押贷款'
livestock_collateral: '畜禽活体抵押',
farmer_loan: '惠农贷',
business_loan: '商业贷款',
personal_loan: '个人贷款'
}
return texts[type] || type
}
const getRepaymentMethodText = (method) => {
const texts = {
equal_installment: '等额本息',
equal_principal: '等额本金',
balloon: '气球贷',
interest_only: '先息后本'
}
return texts[method] || method
}
const getHistoryColor = (action) => {
const colors = {
create: 'blue',
sign: 'green',
activate: 'green',
terminate: 'red'
}
return colors[action] || 'default'
}
const getHistoryActionText = (action) => {
const texts = {
create: '合同创建',
sign: '合同签署',
activate: '合同生效',
terminate: '合同终止'
}
return texts[action] || action
}
const formatAmount = (amount) => {
return `${amount.toFixed(2)}`
}
// 生命周期
onMounted(() => {
pagination.value.total = contracts.value.length
fetchContracts()
})
</script>
@@ -504,71 +685,6 @@ onMounted(() => {
padding: 16px 0;
}
.contract-terms {
background: #f5f5f5;
padding: 12px;
border-radius: 4px;
max-height: 200px;
overflow-y: auto;
}
.contract-terms p {
margin: 0 0 8px 0;
font-size: 14px;
line-height: 1.5;
}
.contract-terms p:last-child {
margin-bottom: 0;
}
.contract-history {
margin-top: 24px;
}
.contract-history h4 {
margin-bottom: 16px;
font-size: 16px;
font-weight: 600;
}
.history-item {
padding: 8px 0;
}
.history-header {
display: flex;
justify-content: space-between;
margin-bottom: 4px;
}
.history-action {
font-weight: 600;
}
.history-time {
color: #999;
font-size: 12px;
}
.history-user {
color: #666;
font-size: 12px;
margin-bottom: 4px;
}
.history-comment {
color: #333;
font-size: 12px;
background: #f5f5f5;
padding: 8px;
border-radius: 4px;
}
.sign-content {
padding: 16px 0;
}
/* 表格样式优化 */
:deep(.ant-table-thead > tr > th) {
background-color: #fafafa;
@@ -604,16 +720,6 @@ onMounted(() => {
text-align: right;
}
/* 空数据样式 */
:deep(.ant-empty) {
padding: 40px 0;
}
:deep(.ant-empty-description) {
color: #999;
font-size: 14px;
}
/* 响应式调整 */
@media (max-width: 768px) {
.page-header {
@@ -630,4 +736,4 @@ onMounted(() => {
margin-bottom: 0;
}
}
</style>
</style>

View File

@@ -31,6 +31,17 @@
</a-row>
</div>
<!-- 批量操作工具栏 -->
<div class="batch-toolbar" v-if="selectedRowKeys.length > 0">
<a-space>
<span>已选择 {{ selectedRowKeys.length }} </span>
<a-button @click="handleBatchDelete" danger>批量删除</a-button>
<a-button @click="handleBatchEnable">批量启用</a-button>
<a-button @click="handleBatchDisable">批量停用</a-button>
<a-button @click="clearSelection">取消选择</a-button>
</a-space>
</div>
<!-- 数据表格 -->
<div class="table-section">
<a-table
@@ -41,23 +52,170 @@
@change="handleTableChange"
row-key="id"
:locale="{ emptyText: '暂无数据' }"
:row-selection="rowSelection"
>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'onSaleStatus'">
<a-switch
v-model:checked="record.onSaleStatus"
@change="handleToggleStatus(record)"
/>
</template>
<template v-if="column.key === 'action'">
<a-space>
<a-button type="link" size="small" @click="handleEdit(record)">编辑</a-button>
<a-button type="link" size="small" @click="handleView(record)">详情</a-button>
</a-space>
</template>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'onSaleStatus'">
<a-switch
v-model:checked="record.onSaleStatus"
@change="handleToggleStatus(record)"
/>
</template>
<template v-if="column.key === 'action'">
<a-space>
<a-button type="link" size="small" @click="handleEdit(record)">编辑</a-button>
<a-button type="link" size="small" @click="handleView(record)">详情</a-button>
<a-popconfirm
title="确定要删除这个贷款商品吗?"
ok-text="确定"
cancel-text="取消"
@confirm="handleDelete(record)"
>
<a-button type="link" size="small" danger>删除</a-button>
</a-popconfirm>
</a-space>
</template>
</template>
</a-table>
</div>
<!-- 编辑对话框 -->
<a-modal
v-model:open="editModalVisible"
title="编辑贷款商品"
width="800px"
:confirm-loading="editLoading"
@ok="handleEditSubmit"
@cancel="handleEditCancel"
>
<a-form
ref="editFormRef"
:model="editForm"
:rules="editFormRules"
layout="vertical"
v-if="editModalVisible"
>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="贷款产品名称" name="productName">
<a-input v-model:value="editForm.productName" placeholder="请输入贷款产品名称" />
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="贷款额度" name="loanAmount">
<a-input
v-model:value="editForm.loanAmount"
placeholder="请输入贷款额度50000~5000000"
style="width: 100%"
addon-after=""
/>
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="贷款周期" name="loanTerm">
<a-input-number
v-model:value="editForm.loanTerm"
placeholder="请输入贷款周期"
:min="1"
style="width: 100%"
addon-after="个月"
/>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="贷款利率" name="interestRate">
<a-input
v-model:value="editForm.interestRate"
placeholder="请输入贷款利率3.90"
style="width: 100%"
addon-after="%"
/>
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="服务区域" name="serviceArea">
<a-input v-model:value="editForm.serviceArea" placeholder="请输入服务区域" />
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="服务电话" name="servicePhone">
<a-input v-model:value="editForm.servicePhone" placeholder="请输入服务电话" />
</a-form-item>
</a-col>
</a-row>
<a-form-item label="产品描述" name="description">
<a-textarea
v-model:value="editForm.description"
placeholder="请输入产品描述"
:rows="3"
/>
</a-form-item>
<a-form-item label="在售状态" name="onSaleStatus">
<a-switch
v-model:checked="editForm.onSaleStatus"
checked-children="在售"
un-checked-children="停售"
/>
</a-form-item>
</a-form>
</a-modal>
<!-- 详情对话框 -->
<a-modal
v-model:open="detailModalVisible"
title="贷款商品详情"
width="800px"
:footer="null"
>
<a-descriptions :column="2" bordered v-if="currentProduct">
<a-descriptions-item label="产品名称" :span="2">
{{ currentProduct.productName }}
</a-descriptions-item>
<a-descriptions-item label="贷款额度">
{{ currentProduct.loanAmount }} 万元
</a-descriptions-item>
<a-descriptions-item label="贷款周期">
{{ currentProduct.loanTerm }} 个月
</a-descriptions-item>
<a-descriptions-item label="贷款利率">
{{ currentProduct.interestRate }}%
</a-descriptions-item>
<a-descriptions-item label="服务区域">
{{ currentProduct.serviceArea }}
</a-descriptions-item>
<a-descriptions-item label="服务电话">
{{ currentProduct.servicePhone }}
</a-descriptions-item>
<a-descriptions-item label="服务客户总数">
{{ currentProduct.totalCustomers }}
</a-descriptions-item>
<a-descriptions-item label="监管中客户">
{{ currentProduct.supervisionCustomers }}
</a-descriptions-item>
<a-descriptions-item label="已结项客户">
{{ currentProduct.completedCustomers }}
</a-descriptions-item>
<a-descriptions-item label="在售状态">
<a-tag :color="currentProduct.onSaleStatus ? 'green' : 'red'">
{{ currentProduct.onSaleStatus ? '在售' : '停售' }}
</a-tag>
</a-descriptions-item>
<a-descriptions-item label="添加时间" :span="2">
{{ currentProduct.createdAt }}
</a-descriptions-item>
<a-descriptions-item label="产品描述" :span="2" v-if="currentProduct.description">
{{ currentProduct.description }}
</a-descriptions-item>
</a-descriptions>
</a-modal>
</div>
</template>
@@ -65,9 +223,111 @@
import { ref, reactive, computed, onMounted } from 'vue'
import { message } from 'ant-design-vue'
import { PlusOutlined, SearchOutlined } from '@ant-design/icons-vue'
import { api } from '@/utils/api'
const loading = ref(false)
const searchText = ref('')
const products = ref([])
// 编辑相关
const editModalVisible = ref(false)
const editLoading = ref(false)
const editFormRef = ref(null)
const editForm = reactive({
id: null,
productName: '',
loanAmount: null,
loanTerm: null,
interestRate: null,
serviceArea: '',
servicePhone: '',
description: '',
onSaleStatus: true
})
// 详情相关
const detailModalVisible = ref(false)
const currentProduct = ref(null)
// 批量操作相关
const selectedRowKeys = ref([])
const selectedRows = ref([])
// 表单验证规则
const editFormRules = {
productName: [
{ required: true, message: '请输入贷款产品名称', trigger: 'blur' },
{ min: 2, max: 50, message: '产品名称长度在2-50个字符', trigger: 'blur' }
],
loanAmount: [
{ required: true, message: '请输入贷款额度', trigger: 'blur' },
{
validator: (rule, value) => {
if (!value) return Promise.reject('请输入贷款额度')
// 支持数字或范围字符串50000~5000000
if (typeof value === 'number') {
if (value <= 0) return Promise.reject('贷款额度必须大于0')
} else if (typeof value === 'string') {
// 处理范围字符串
if (value.includes('~')) {
const [min, max] = value.split('~').map(v => parseFloat(v.trim()))
if (isNaN(min) || isNaN(max) || min <= 0 || max <= 0) {
return Promise.reject('贷款额度范围格式不正确')
}
} else {
const numValue = parseFloat(value)
if (isNaN(numValue) || numValue <= 0) {
return Promise.reject('贷款额度必须大于0')
}
}
}
return Promise.resolve()
},
trigger: 'blur'
}
],
loanTerm: [
{ required: true, message: '请输入贷款周期', trigger: 'blur' },
{ type: 'number', min: 1, message: '贷款周期必须大于0', trigger: 'blur' }
],
interestRate: [
{ required: true, message: '请输入贷款利率', trigger: 'blur' },
{
validator: (rule, value) => {
if (!value) return Promise.reject('请输入贷款利率')
const numValue = parseFloat(value)
if (isNaN(numValue)) return Promise.reject('请输入有效的数字')
if (numValue < 0 || numValue > 100) {
return Promise.reject('贷款利率必须在0-100之间')
}
return Promise.resolve()
},
trigger: 'blur'
}
],
serviceArea: [
{ required: true, message: '请输入服务区域', trigger: 'blur' }
],
servicePhone: [
{ required: true, message: '请输入服务电话', trigger: 'blur' },
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号码', trigger: 'blur' }
]
}
// 行选择配置
const rowSelection = {
selectedRowKeys: selectedRowKeys,
onChange: (keys, rows) => {
selectedRowKeys.value = keys
selectedRows.value = rows
},
onSelect: (record, selected, selectedRows) => {
console.log('选择行:', record, selected, selectedRows)
},
onSelectAll: (selected, selectedRows, changeRows) => {
console.log('全选:', selected, selectedRows, changeRows)
}
}
const pagination = reactive({
current: 1,
@@ -142,8 +402,8 @@ const columns = [
},
{
title: '添加时间',
dataIndex: 'createTime',
key: 'createTime',
dataIndex: 'createdAt',
key: 'createdAt',
sorter: true,
width: 150
},
@@ -161,78 +421,31 @@ const columns = [
},
]
// 模拟数据
const products = ref([
{
id: 1,
productName: '惠农贷',
loanAmount: '50000~5000000元',
loanTerm: '24',
interestRate: '3.90%',
serviceArea: '内蒙古自治区:通辽市',
servicePhone: '15004901368',
totalCustomers: 16,
supervisionCustomers: 11,
completedCustomers: 5,
createTime: '2023-12-18 16:23:03',
onSaleStatus: true,
},
{
id: 2,
productName: '中国工商银行扎旗支行"畜禽活体抵押"',
loanAmount: '200000~1000000元',
loanTerm: '12',
interestRate: '4.70%',
serviceArea: '内蒙古自治区:通辽市',
servicePhone: '15004901368',
totalCustomers: 10,
supervisionCustomers: 5,
completedCustomers: 5,
createTime: '2023-06-20 17:36:17',
onSaleStatus: true,
},
{
id: 3,
productName: '中国银行扎旗支行"畜禽活体抵押"',
loanAmount: '200000~1000000元',
loanTerm: '12',
interestRate: '4.60%',
serviceArea: '内蒙古自治区:通辽市',
servicePhone: '15004901368',
totalCustomers: 2,
supervisionCustomers: 2,
completedCustomers: 0,
createTime: '2023-06-20 17:34:33',
onSaleStatus: true,
},
{
id: 4,
productName: '中国农业银行扎旗支行"畜禽活体抵押"',
loanAmount: '200000~1000000元',
loanTerm: '12',
interestRate: '4.80%',
serviceArea: '内蒙古自治区:通辽市',
servicePhone: '15004901368',
totalCustomers: 26,
supervisionCustomers: 24,
completedCustomers: 2,
createTime: '2023-06-20 17:09:39',
onSaleStatus: true,
},
])
// 获取贷款商品列表
const fetchProducts = async () => {
loading.value = true
try {
// 实际项目中这里会调用API获取数据
// const response = await api.loanProducts.getList({
// page: pagination.current,
// pageSize: pagination.pageSize,
// search: searchText.value,
// })
const response = await api.loanProducts.getList({
page: pagination.current,
pageSize: pagination.pageSize,
search: searchText.value,
})
// 使用模拟数据
pagination.total = products.value.length
console.log('API响应数据:', response)
if (response.success) {
console.log('产品数据:', response.data.products)
console.log('分页数据:', response.data.pagination)
products.value = response.data.products || []
pagination.total = response.data.pagination?.total || 0
pagination.current = response.data.pagination?.current || 1
pagination.pageSize = response.data.pagination?.pageSize || 10
console.log('设置后的products.value:', products.value)
} else {
message.error(response.message || '获取贷款商品失败')
}
} catch (error) {
console.error('获取贷款商品失败:', error)
message.error('获取贷款商品失败')
@@ -242,15 +455,9 @@ const fetchProducts = async () => {
}
const filteredProducts = computed(() => {
let result = products.value
if (searchText.value) {
result = result.filter(product =>
product.productName.toLowerCase().includes(searchText.value.toLowerCase())
)
}
return result
// 后端已经处理了搜索,直接返回数据
console.log('filteredProducts computed:', products.value)
return products.value
})
const handleSearch = () => {
@@ -268,17 +475,204 @@ const handleAddProduct = () => {
message.info('新增贷款功能开发中...')
}
const handleEdit = (record) => {
message.info(`编辑产品: ${record.productName}`)
const handleEdit = async (record) => {
try {
const response = await api.loanProducts.getById(record.id)
if (response.success) {
// 填充编辑表单
Object.assign(editForm, {
id: record.id,
productName: record.productName,
loanAmount: record.loanAmount,
loanTerm: record.loanTerm,
interestRate: record.interestRate,
serviceArea: record.serviceArea,
servicePhone: record.servicePhone,
description: record.description || '',
onSaleStatus: record.onSaleStatus
})
editModalVisible.value = true
} else {
message.error(response.message || '获取产品详情失败')
}
} catch (error) {
console.error('获取产品详情失败:', error)
message.error('获取产品详情失败')
}
}
const handleView = (record) => {
message.info(`查看详情: ${record.productName}`)
const handleView = async (record) => {
try {
const response = await api.loanProducts.getById(record.id)
if (response.success) {
currentProduct.value = response.data
detailModalVisible.value = true
} else {
message.error(response.message || '获取产品详情失败')
}
} catch (error) {
console.error('获取产品详情失败:', error)
message.error('获取产品详情失败')
}
}
const handleToggleStatus = (record) => {
const status = record.onSaleStatus ? '启用' : '停用'
message.success(`${record.productName}${status}`)
// 编辑提交
const handleEditSubmit = async () => {
try {
await editFormRef.value.validate()
editLoading.value = true
const response = await api.loanProducts.update(editForm.id, {
productName: editForm.productName,
loanAmount: editForm.loanAmount,
loanTerm: editForm.loanTerm,
interestRate: editForm.interestRate,
serviceArea: editForm.serviceArea,
servicePhone: editForm.servicePhone,
description: editForm.description,
onSaleStatus: editForm.onSaleStatus
})
if (response.success) {
message.success('贷款商品更新成功')
editModalVisible.value = false
fetchProducts() // 刷新列表
} else {
message.error(response.message || '更新失败')
}
} catch (error) {
console.error('更新失败:', error)
message.error('更新失败')
} finally {
editLoading.value = false
}
}
// 编辑取消
const handleEditCancel = () => {
editModalVisible.value = false
editFormRef.value?.resetFields()
}
// 删除产品
const handleDelete = async (record) => {
try {
const response = await api.loanProducts.delete(record.id)
if (response.success) {
message.success(`${record.productName} 删除成功`)
fetchProducts() // 刷新列表
} else {
message.error(response.message || '删除失败')
}
} catch (error) {
console.error('删除失败:', error)
message.error('删除失败')
}
}
// 批量删除
const handleBatchDelete = async () => {
if (selectedRowKeys.value.length === 0) {
message.warning('请先选择要删除的项目')
return
}
try {
const response = await api.loanProducts.batchDelete({
ids: selectedRowKeys.value
})
if (response.success) {
message.success(`成功删除 ${selectedRowKeys.value.length} 个贷款商品`)
clearSelection()
fetchProducts() // 刷新列表
} else {
message.error(response.message || '批量删除失败')
}
} catch (error) {
console.error('批量删除失败:', error)
message.error('批量删除失败')
}
}
// 批量启用
const handleBatchEnable = async () => {
if (selectedRowKeys.value.length === 0) {
message.warning('请先选择要启用的项目')
return
}
try {
const response = await api.loanProducts.batchUpdateStatus({
ids: selectedRowKeys.value,
onSaleStatus: true
})
if (response.success) {
message.success(`成功启用 ${selectedRowKeys.value.length} 个贷款商品`)
clearSelection()
fetchProducts() // 刷新列表
} else {
message.error(response.message || '批量启用失败')
}
} catch (error) {
console.error('批量启用失败:', error)
message.error('批量启用失败')
}
}
// 批量停用
const handleBatchDisable = async () => {
if (selectedRowKeys.value.length === 0) {
message.warning('请先选择要停用的项目')
return
}
try {
const response = await api.loanProducts.batchUpdateStatus({
ids: selectedRowKeys.value,
onSaleStatus: false
})
if (response.success) {
message.success(`成功停用 ${selectedRowKeys.value.length} 个贷款商品`)
clearSelection()
fetchProducts() // 刷新列表
} else {
message.error(response.message || '批量停用失败')
}
} catch (error) {
console.error('批量停用失败:', error)
message.error('批量停用失败')
}
}
// 清除选择
const clearSelection = () => {
selectedRowKeys.value = []
selectedRows.value = []
}
const handleToggleStatus = async (record) => {
try {
const response = await api.loanProducts.update(record.id, {
onSaleStatus: record.onSaleStatus
})
if (response.success) {
const status = record.onSaleStatus ? '启用' : '停用'
message.success(`${record.productName}${status}`)
} else {
message.error(response.message || '更新状态失败')
// 恢复原状态
record.onSaleStatus = !record.onSaleStatus
}
} catch (error) {
console.error('更新状态失败:', error)
message.error('更新状态失败')
// 恢复原状态
record.onSaleStatus = !record.onSaleStatus
}
}
const handleTableChange = (pag, filters, sorter) => {
@@ -368,6 +762,27 @@ onMounted(() => {
color: #40a9ff;
}
/* 批量操作工具栏样式 */
.batch-toolbar {
background: #e6f7ff;
border: 1px solid #91d5ff;
border-radius: 6px;
padding: 12px 16px;
margin-bottom: 16px;
display: flex;
align-items: center;
justify-content: space-between;
}
.batch-toolbar .ant-space {
flex: 1;
}
.batch-toolbar span {
color: #1890ff;
font-weight: 500;
}
/* 响应式设计 */
@media (max-width: 768px) {
.page-header {
@@ -383,5 +798,15 @@ onMounted(() => {
.search-section .ant-col:last-child {
margin-bottom: 0;
}
.batch-toolbar {
flex-direction: column;
gap: 12px;
align-items: stretch;
}
.batch-toolbar .ant-space {
justify-content: center;
}
}
</style>