feat: 优化关联首付分期政策列表显示字段\n\n- 精简列表只显示政策名称、已售面积、已售套数、已售底价总价、生效日期、失效日期\n- 移除序号、首付比例、付款方式、房源套数、楼栋范围、备注等字段\n- 隐藏表头标题,数据改为纯文本显示\n- 调整新增按钮位置和样式

This commit is contained in:
Admin
2025-09-15 15:56:11 +08:00
parent adb75b01dd
commit 5c8ba42850
21 changed files with 2516 additions and 793 deletions

12
.run/ruoyi-debug.run.xml Normal file
View File

@ -0,0 +1,12 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="ruoyi-debug" type="Remote">
<option name="USE_SOCKET_TRANSPORT" value="true" />
<option name="SERVER_MODE" value="false" />
<option name="SHMEM_ADDRESS" />
<option name="HOST" value="localhost" />
<option name="PORT" value="5005" />
<option name="AUTO_RESTART" value="false" />
<module name="ruoyi-admin" />
<method v="2" />
</configuration>
</component>

View File

@ -0,0 +1,12 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="ruoyi-monitor-admin" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
<deployment type="dockerfile">
<settings>
<option name="imageTag" value="ruoyi/ruoyi-monitor-admin:5.4.1" />
<option name="buildOnly" value="true" />
<option name="sourceFilePath" value="ruoyi-extend/ruoyi-monitor-admin/Dockerfile" />
</settings>
</deployment>
<method v="2" />
</configuration>
</component>

12
.run/ruoyi-server.run.xml Normal file
View File

@ -0,0 +1,12 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="ruoyi-server" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
<deployment type="dockerfile">
<settings>
<option name="imageTag" value="ruoyi/ruoyi-server:5.4.1" />
<option name="buildOnly" value="true" />
<option name="sourceFilePath" value="ruoyi-admin/Dockerfile" />
</settings>
</deployment>
<method v="2" />
</configuration>
</component>

View File

@ -0,0 +1,12 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="ruoyi-snailjob-server" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
<deployment type="dockerfile">
<settings>
<option name="imageTag" value="ruoyi/ruoyi-snailjob-server:5.4.1" />
<option name="buildOnly" value="true" />
<option name="sourceFilePath" value="ruoyi-extend/ruoyi-snailjob-server/Dockerfile" />
</settings>
</deployment>
<method v="2" />
</configuration>
</component>

View File

@ -0,0 +1,84 @@
package org.dromara.system.controller;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.system.domain.bo.CompositePeriodicPolicyBo;
import org.dromara.system.service.IPeriodicPolicyCompositeService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
/**
* 组合分期方案控制器 - 一次性保存分期方案、分期政策、付款明细
*
* @author System
* @date 2025-01-15
*/
@Slf4j
@RestController
@RequestMapping("/system/composite/periodicpolicy")
@RequiredArgsConstructor
public class CompositePeriodicPolicyController {
private final IPeriodicPolicyCompositeService compositeService;
/**
* 组合保存分期方案及其关联数据
*
* @param compositeBo 组合分期方案业务对象
* @return 保存结果
*/
@Log(title = "组合分期方案", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping("/save")
public R<String> saveComposite(@Validated(AddGroup.class) @RequestBody CompositePeriodicPolicyBo compositeBo) {
try {
log.info("开始组合保存分期方案数据");
String schemeId = compositeService.saveCompositePeriodicPolicy(
compositeBo.getPeriodicpolicy(),
compositeBo.getPolicies(),
compositeBo.getPaymentDetails()
);
log.info("组合保存分期方案数据成功方案ID: {}", schemeId);
return R.ok(schemeId);
} catch (Exception e) {
log.error("组合保存分期方案数据失败", e);
return R.fail("保存失败: " + e.getMessage());
}
}
/**
* 组合更新分期方案及其关联数据
*
* @param compositeBo 组合分期方案业务对象
* @return 更新结果
*/
@Log(title = "组合分期方案", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping("/update")
public R<Void> updateComposite(@Validated(EditGroup.class) @RequestBody CompositePeriodicPolicyBo compositeBo) {
try {
log.info("开始组合更新分期方案数据");
boolean success = compositeService.updateCompositePeriodicPolicy(
compositeBo.getPeriodicpolicy(),
compositeBo.getPolicies(),
compositeBo.getPaymentDetails()
);
if (success) {
log.info("组合更新分期方案数据成功");
return R.ok();
} else {
log.error("组合更新分期方案数据失败");
return R.fail("更新失败");
}
} catch (Exception e) {
log.error("组合更新分期方案数据失败", e);
return R.fail("更新失败: " + e.getMessage());
}
}
}

View File

@ -99,8 +99,8 @@ public class TPolicy extends TenantEntity {
/** /**
* 首付比例 * 首付比例
*/ */
@TableField("downPaymentRatio") @TableField("down_payment_ratio")
private BigDecimal downPaymentRatio; private String downPaymentRatio;
/** /**
* 删除标志0存在 1删除 * 删除标志0存在 1删除

View File

@ -0,0 +1,44 @@
package org.dromara.system.domain.bo;
import lombok.Data;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
import java.util.List;
/**
* 组合分期方案业务对象 - 用于一次性保存分期方案、分期政策、付款明细
*
* @author System
* @date 2025-01-15
*/
@Data
public class CompositePeriodicPolicyBo {
/**
* 分期方案信息
*/
@Valid
@NotNull(message = "分期方案信息不能为空", groups = {AddGroup.class, EditGroup.class})
private TPeriodicpolicyBo periodicpolicy;
/**
* 分期政策列表
*/
@Valid
private List<TPolicyBo> policies;
/**
* 付款明细列表(按政策分组)
* 每个元素对应一个政策的付款明细列表
*/
@Valid
private List<List<TPaymentDetailsBo>> paymentDetails;
/**
* 是否为编辑模式
*/
private boolean editMode = false;
}

View File

@ -147,6 +147,6 @@ public class TPolicyBo extends BaseEntity {
/** /**
* 首付比例 * 首付比例
*/ */
private BigDecimal downPaymentRatio; private String downPaymentRatio;
} }

View File

@ -120,7 +120,7 @@ public class TPolicyVo implements Serializable {
* 首付比例 * 首付比例
*/ */
@ExcelProperty(value = "首付比例") @ExcelProperty(value = "首付比例")
private BigDecimal downPaymentRatio; private String downPaymentRatio;
} }

View File

@ -0,0 +1,40 @@
package org.dromara.system.service;
import org.dromara.system.domain.bo.TPeriodicpolicyBo;
import org.dromara.system.domain.bo.TPolicyBo;
import org.dromara.system.domain.bo.TPaymentDetailsBo;
import java.util.List;
/**
* 分期方案组合服务接口
*
* @author System
* @date 2025-01-15
*/
public interface IPeriodicPolicyCompositeService {
/**
* 组合保存分期方案及其关联的分期政策和付款明细
*
* @param periodicpolicyBo 分期方案业务对象
* @param policyBos 分期政策业务对象列表
* @param paymentDetailsBos 付款明细业务对象列表(按政策分组)
* @return 保存成功的分期方案ID
*/
String saveCompositePeriodicPolicy(TPeriodicpolicyBo periodicpolicyBo,
List<TPolicyBo> policyBos,
List<List<TPaymentDetailsBo>> paymentDetailsBos);
/**
* 组合更新分期方案及其关联的分期政策和付款明细
*
* @param periodicpolicyBo 分期方案业务对象
* @param policyBos 分期政策业务对象列表
* @param paymentDetailsBos 付款明细业务对象列表(按政策分组)
* @return 是否更新成功
*/
boolean updateCompositePeriodicPolicy(TPeriodicpolicyBo periodicpolicyBo,
List<TPolicyBo> policyBos,
List<List<TPaymentDetailsBo>> paymentDetailsBos);
}

View File

@ -0,0 +1,186 @@
package org.dromara.system.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.system.domain.bo.TPeriodicpolicyBo;
import org.dromara.system.domain.bo.TPolicyBo;
import org.dromara.system.domain.bo.TPaymentDetailsBo;
import org.dromara.system.domain.TPeriodicpolicy;
import org.dromara.system.domain.TPolicy;
import org.dromara.system.domain.TPaymentDetails;
import org.dromara.system.mapper.TPeriodicpolicyMapper;
import org.dromara.system.mapper.TPolicyMapper;
import org.dromara.system.mapper.TPaymentDetailsMapper;
import org.dromara.system.service.IPeriodicPolicyCompositeService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
/**
* 分期方案组合服务 - 将分期方案、分期政策、付款明细放在同一个事务下保存
*
* @author System
* @date 2025-01-15
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class PeriodicPolicyCompositeServiceImpl implements IPeriodicPolicyCompositeService {
private final TPeriodicpolicyMapper periodicpolicyMapper;
private final TPolicyMapper policyMapper;
private final TPaymentDetailsMapper paymentDetailsMapper;
/**
* 组合保存分期方案及其关联的分期政策和付款明细
*
* @param periodicpolicyBo 分期方案业务对象
* @param policyBos 分期政策业务对象列表
* @param paymentDetailsBos 付款明细业务对象列表(按政策分组)
* @return 保存成功的分期方案ID
*/
@Override
@Transactional(rollbackFor = Exception.class)
public String saveCompositePeriodicPolicy(TPeriodicpolicyBo periodicpolicyBo,
List<TPolicyBo> policyBos,
List<List<TPaymentDetailsBo>> paymentDetailsBos) {
try {
log.info("开始组合保存分期方案数据");
// 1. 保存分期方案
TPeriodicpolicy periodicpolicy = MapstructUtils.convert(periodicpolicyBo, TPeriodicpolicy.class);
// 设置当前登录用户为创建人
periodicpolicy.setCreator(LoginHelper.getUsername());
periodicpolicyMapper.insert(periodicpolicy);
String schemeId = periodicpolicy.getId();
log.info("分期方案保存成功ID: {}", schemeId);
// 2. 保存分期政策及其付款明细
if (policyBos != null && !policyBos.isEmpty()) {
for (int i = 0; i < policyBos.size(); i++) {
TPolicyBo policyBo = policyBos.get(i);
policyBo.setPeriodicPolicyId(schemeId); // 设置关联的分期方案ID
TPolicy policy = MapstructUtils.convert(policyBo, TPolicy.class);
// 设置当前登录用户为创建人
policy.setCreator(LoginHelper.getUsername());
policyMapper.insert(policy);
String policyId = policy.getId();
log.info("分期政策保存成功ID: {}", policyId);
// 3. 保存该政策的付款明细
if (paymentDetailsBos != null && i < paymentDetailsBos.size()) {
List<TPaymentDetailsBo> detailsForPolicy = paymentDetailsBos.get(i);
if (detailsForPolicy != null && !detailsForPolicy.isEmpty()) {
for (TPaymentDetailsBo paymentDetailsBo : detailsForPolicy) {
paymentDetailsBo.setPolicyid(policyId); // 设置关联的政策ID
TPaymentDetails paymentDetails = MapstructUtils.convert(paymentDetailsBo, TPaymentDetails.class);
// 设置当前登录用户为创建人
paymentDetails.setCreator(LoginHelper.getUsername());
// 设置创建人ID
paymentDetails.setCreatorid(LoginHelper.getUserIdStr());
paymentDetailsMapper.insert(paymentDetails);
}
log.info("政策 {} 的付款明细保存成功,共 {} 条", policyId, detailsForPolicy.size());
}
}
}
}
log.info("组合保存分期方案数据完成方案ID: {}", schemeId);
return schemeId;
} catch (Exception e) {
log.error("组合保存分期方案数据失败", e);
throw new RuntimeException("保存分期方案数据失败: " + e.getMessage(), e);
}
}
/**
* 组合更新分期方案及其关联的分期政策和付款明细
*
* @param periodicpolicyBo 分期方案业务对象
* @param policyBos 分期政策业务对象列表
* @param paymentDetailsBos 付款明细业务对象列表(按政策分组)
* @return 是否更新成功
*/
@Override
@Transactional(rollbackFor = Exception.class)
public boolean updateCompositePeriodicPolicy(TPeriodicpolicyBo periodicpolicyBo,
List<TPolicyBo> policyBos,
List<List<TPaymentDetailsBo>> paymentDetailsBos) {
try {
log.info("开始组合更新分期方案数据方案ID: {}", periodicpolicyBo.getId());
String schemeId = periodicpolicyBo.getId();
// 1. 更新分期方案
TPeriodicpolicy periodicpolicy = MapstructUtils.convert(periodicpolicyBo, TPeriodicpolicy.class);
// 设置当前登录用户为创建人
periodicpolicy.setCreator(LoginHelper.getUsername());
periodicpolicyMapper.updateById(periodicpolicy);
log.info("分期方案更新成功ID: {}", schemeId);
// 2. 删除原有的分期政策和付款明细
// 先获取关联的政策ID列表
List<Object> policyIds = policyMapper.selectObjs(Wrappers.<TPolicy>lambdaQuery()
.eq(TPolicy::getPeriodicPolicyId, schemeId)
.select(TPolicy::getId));
// 如果有政策ID才删除对应的付款明细
if (policyIds != null && !policyIds.isEmpty()) {
paymentDetailsMapper.delete(Wrappers.<TPaymentDetails>lambdaQuery()
.in(TPaymentDetails::getPolicyid, policyIds));
}
// 再删除分期政策
policyMapper.delete(Wrappers.<TPolicy>lambdaQuery()
.eq(TPolicy::getPeriodicPolicyId, schemeId));
log.info("原有的分期政策和付款明细已删除");
// 3. 重新保存分期政策及其付款明细
if (policyBos != null && !policyBos.isEmpty()) {
for (int i = 0; i < policyBos.size(); i++) {
TPolicyBo policyBo = policyBos.get(i);
policyBo.setPeriodicPolicyId(schemeId);
TPolicy policy = MapstructUtils.convert(policyBo, TPolicy.class);
// 设置当前登录用户为创建人
policy.setCreator(LoginHelper.getUsername());
policyMapper.insert(policy);
String policyId = policy.getId();
// 保存该政策的付款明细
if (paymentDetailsBos != null && i < paymentDetailsBos.size()) {
List<TPaymentDetailsBo> detailsForPolicy = paymentDetailsBos.get(i);
if (detailsForPolicy != null && !detailsForPolicy.isEmpty()) {
for (TPaymentDetailsBo paymentDetailsBo : detailsForPolicy) {
paymentDetailsBo.setPolicyid(policyId);
TPaymentDetails paymentDetails = MapstructUtils.convert(paymentDetailsBo, TPaymentDetails.class);
// 设置当前登录用户为创建人
paymentDetails.setCreator(LoginHelper.getUsername());
// 设置创建人ID
paymentDetails.setCreatorid(LoginHelper.getUserIdStr());
paymentDetailsMapper.insert(paymentDetails);
}
log.info("政策 {} 的付款明细更新成功,共 {} 条", policyId, detailsForPolicy.size());
}
}
}
}
log.info("组合更新分期方案数据完成方案ID: {}", schemeId);
return true;
} catch (Exception e) {
log.error("组合更新分期方案数据失败", e);
throw new RuntimeException("更新分期方案数据失败: " + e.getMessage(), e);
}
}
}

View File

@ -0,0 +1,69 @@
import type { InstallmentpolicyVO, InstallmentpolicyForm } from './types'
import request from '@/utils/request'
// 分期政策相关API
export function listInstallmentpolicy(params?: any): Promise<{ rows: InstallmentpolicyVO[]; total: number }> {
return request({
url: '/system/installmentpolicy/list',
method: 'get',
params: params
})
}
export function getInstallmentpolicy(id: string | number): Promise<InstallmentpolicyVO> {
return request({
url: '/system/installmentpolicy/' + id,
method: 'get'
})
}
export function addInstallmentpolicy(data: InstallmentpolicyForm): Promise<any> {
return request({
url: '/system/installmentpolicy',
method: 'post',
data: data
})
}
export function updateInstallmentpolicy(data: InstallmentpolicyForm): Promise<any> {
return request({
url: '/system/installmentpolicy',
method: 'put',
data: data
})
}
export function delInstallmentpolicy(ids: string | number | (string | number)[]): Promise<any> {
return request({
url: '/system/installmentpolicy/' + ids,
method: 'delete'
})
}
// 组合保存分期政策方案、政策和付款明细
export function saveCompositeInstallmentPolicy(data: any): Promise<any> {
return request({
url: '/system/composite/installmentpolicy/save',
method: 'post',
data: data
})
}
// 组合更新分期政策方案、政策和付款明细
export function updateCompositeInstallmentPolicy(data: any): Promise<any> {
return request({
url: '/system/composite/installmentpolicy/update',
method: 'put',
data: data
})
}
// 导出分期政策数据
export function exportInstallmentpolicy(params?: any): Promise<any> {
return request({
url: '/system/installmentpolicy/export',
method: 'post',
params: params,
responseType: 'blob'
})
}

View File

@ -0,0 +1,27 @@
// 分期政策VO
export interface InstallmentpolicyVO {
id?: string | number
schemeName?: string
installments?: string[]
fillingInstruction?: string
effectiveDate?: string
invalidDate?: string
creator?: string
createDate?: string
status?: string
policies?: any[]
}
// 分期政策表单
export interface InstallmentpolicyForm {
id?: string | number
schemeName?: string
installments?: string[]
fillingInstruction?: string
effectiveDate?: string
invalidDate?: string
creator?: string
createDate?: string
status?: string
policies?: any[]
}

View File

@ -39,3 +39,21 @@ export function delPeriodicpolicy(ids: string | number | (string | number)[]): P
method: 'delete' method: 'delete'
}) })
} }
// 组合保存分期方案、政策和付款明细
export function saveCompositePeriodicPolicy(data: any): Promise<any> {
return request({
url: '/system/composite/periodicpolicy/save',
method: 'post',
data: data
})
}
// 组合更新分期方案、政策和付款明细
export function updateCompositePeriodicPolicy(data: any): Promise<any> {
return request({
url: '/system/composite/periodicpolicy/update',
method: 'put',
data: data
})
}

View File

@ -123,12 +123,31 @@ export const constantRoutes: RouteRecordRaw[] = [
children: [ children: [
{ {
path: 'add', path: 'add',
component: () => import('@/views/system/policy/add/index.vue'), component: () => import('@/views/system/periodicpolicy/add/index.vue'),
name: 'AddPolicy', name: 'AddPolicy',
meta: { title: '新增首付分期政策', activeMenu: '/system/policy' } meta: { title: '新增首付分期政策', activeMenu: '/system/policy' }
} }
] ]
}, },
{
path: '/system/periodicpolicy/installment',
component: Layout,
hidden: true,
children: [
{
path: '',
component: () => import('@/views/system/periodicpolicy/installment/index.vue'),
name: 'InstallmentPolicy',
meta: { title: '分期政策管理', activeMenu: '/system/periodicpolicy' }
},
{
path: 'add',
component: () => import('@/views/system/periodicpolicy/installment/add/index.vue'),
name: 'AddInstallmentPolicy',
meta: { title: '新增分期政策方案', activeMenu: '/system/periodicpolicy' }
}
]
},
{ {
path: '/system/project', path: '/system/project',
component: Layout, component: Layout,

File diff suppressed because it is too large Load Diff

View File

@ -191,9 +191,18 @@
</el-table-column> </el-table-column>
<el-table-column label="操作" width="120" align="center" fixed="right"> <el-table-column label="操作" width="120" align="center" fixed="right">
<template #default="scope"> <template #default="scope">
<el-button type="text" size="small" @click="handleEdit(scope.row)" v-if="scope.row.schemeType === 'downPayment'"> 首付分期 </el-button> <el-button type="primary" link size="small" @click="handleEdit(scope.row)" v-if="scope.row.schemeType === 'downPayment'">
<el-button type="text" size="small" @click="handleEditPolicy(scope.row)" v-else> 编辑 </el-button> <el-icon><Edit /></el-icon>首付分期
<el-button type="text" size="small">删除</el-button> </el-button>
<el-button type="primary" link size="small" @click="handleEditPolicy(scope.row)" v-else>
<el-icon><Edit /></el-icon>编辑
</el-button>
<el-button type="danger" link size="small" @click="handleDelete(scope.row)">
<el-icon><Delete /></el-icon>删除
</el-button>
<el-button type="primary" link size="small" @click="handleInstallmentPolicy(scope.row)">
<el-icon><Document /></el-icon>分期政策
</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@ -219,6 +228,7 @@
import { ref, computed, onMounted, onUnmounted } from 'vue'; import { ref, computed, onMounted, onUnmounted } from 'vue';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import { ElMessage } from 'element-plus'; import { ElMessage } from 'element-plus';
import { Edit, Delete, Document } from '@element-plus/icons-vue';
import { listProject } from '@/api/system/project'; import { listProject } from '@/api/system/project';
import { listPeriodicpolicy } from '@/api/system/periodicpolicy'; import { listPeriodicpolicy } from '@/api/system/periodicpolicy';
@ -605,8 +615,81 @@ const handleEdit = (row) => {
// 处理分期政策编辑跳转 // 处理分期政策编辑跳转
const handleEditPolicy = (row) => { const handleEditPolicy = (row) => {
// 这里可以添加分期政策的编辑逻辑
console.log('编辑分期政策:', row); console.log('编辑分期政策:', row);
if (!row.id) {
console.error('缺少必要的ID参数:', row);
ElMessage.error('无法编辑缺少分期方案ID');
return;
}
// 跳转到分期政策编辑页面
const queryString = new URLSearchParams({
id: String(row.id),
schemeType: row.schemeType || 'policy',
installments: row.projGuid || ''
}).toString();
window.location.href = `/periodicpolicy/add?${queryString}`;
};
// 处理分期政策按钮跳转
const handleInstallmentPolicy = (row) => {
console.log('跳转到分期政策管理:', row);
if (!row.id) {
console.error('缺少必要的ID参数:', row);
ElMessage.error('无法跳转缺少分期方案ID');
return;
}
// 跳转到分期政策管理页面
const queryString = new URLSearchParams({
schemeId: String(row.id),
schemeName: row.schemeName || '',
projectId: row.projGuid || ''
}).toString();
window.location.href = `/system/periodicpolicy/installment?${queryString}`;
};
// 处理删除分期方案
const handleDelete = async (row) => {
if (!row.id) {
console.error('缺少必要的ID参数:', row);
ElMessage.error('无法删除缺少分期方案ID');
return;
}
try {
// 确认删除
await ElMessageBox.confirm(
`确定要删除分期方案"${row.schemeName || row.schemeName || '未命名方案'}"吗?`,
'删除确认',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}
);
// 执行删除操作
loading.value = true;
const { delPeriodicpolicy } = await import('@/api/system/periodicpolicy');
await delPeriodicpolicy(row.id);
ElMessage.success('删除成功');
// 重新加载数据
await fetchTableData();
} catch (error) {
if (error !== 'cancel') {
console.error('删除分期方案失败:', error);
ElMessage.error(error?.response?.data?.message || '删除失败,请稍后重试');
}
} finally {
loading.value = false;
}
}; };
</script> </script>

View File

@ -0,0 +1,982 @@
<template>
<div class="form-container">
<div class="page-title">{{ isEditMode ? '编辑分期方案' : '新增分期方案' }}</div>
<el-form :model="form" :rules="formRules" ref="formRef" label-width="100px" class="policy-form">
<el-form-item label="方案名称" prop="schemeName">
<el-input v-model="form.schemeName" placeholder="请输入分期方案名称" maxLength="50" />
</el-form-item>
<el-form-item label="分期" prop="installments">
<el-select v-model="form.installments" multiple placeholder="请选择关联分期" style="width: 100%" @change="handleInstallmentChange">
<el-option v-for="item in installmentOptions" :key="item.projguid" :label="item.projname" :value="item.projguid" />
</el-select>
</el-form-item>
<el-form-item label="填报说明" prop="fillingInstruction">
<el-input v-model="form.fillingInstruction" type="textarea" placeholder="请输入分期方案填报说明" rows="4" maxLength="200" />
</el-form-item>
<div class="form-row">
<el-form-item label="生效日期" class="half-width">
<div v-if="isEditMode" class="text-display">{{ displayEffectiveDate || '-' }}</div>
<div v-else class="text-display">-</div>
</el-form-item>
<el-form-item label="失效日期" class="half-width">
<div v-if="isEditMode" class="text-display">{{ displayInvalidDate || '-' }}</div>
<div v-else class="text-display">-</div>
</el-form-item>
</div>
<div class="form-row">
<el-form-item label="创建人" class="half-width">
<el-input v-model="form.creator" readonly />
</el-form-item>
<el-form-item label="创建时间" class="half-width">
<el-input v-model="form.createDate" readonly />
</el-form-item>
</div>
</el-form>
<div class="table-section">
<div class="table-header">
<span>关联分期政策</span>
<el-button type="primary" size="small" @click="handleAddPolicy">
<el-icon><Plus /></el-icon>
新增政策
</el-button>
</div>
<el-table
:data="policyTableData"
border
style="width: 100%"
:header-cell-style="{ color: '#606266', fontWeight: 'normal' }"
>
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column prop="policyName" label="政策名称" min-width="150" show-overflow-tooltip />
<el-table-column prop="installmentRatio" label="分期比例" width="90" align="center">
<template #default="{ row }">
<el-tag type="warning" size="small">{{ row.installmentRatio }}%</el-tag>
</template>
</el-table-column>
<el-table-column prop="paymentType" label="付款方式" width="100" align="center">
<template #default="{ row }">
<el-tag type="info" size="small">{{ row.paymentType }}</el-tag>
</template>
</el-table-column>
<el-table-column prop="usableHousesCount" label="房源套数" width="90" align="center">
<template #default="{ row }">
<span>{{ row.usableHousesCount }}</span>
</template>
</el-table-column>
<el-table-column prop="effectiveDate" label="生效日期" width="100" align="center" />
<el-table-column prop="expirationDate" label="失效日期" width="100" align="center" />
<el-table-column prop="buildingNames" label="楼栋范围" min-width="120" show-overflow-tooltip />
<el-table-column prop="remarks" label="备注" min-width="120" show-overflow-tooltip />
<el-table-column label="操作" width="120" fixed="right" align="center">
<template #default="{ row }">
<el-button type="primary" link @click="handleEditPolicy(row)" size="small">
<el-icon><Edit /></el-icon>
编辑
</el-button>
<el-button type="danger" link @click="handleDeletePolicy(row)" size="small">
<el-icon><Delete /></el-icon>
删除
</el-button>
</template>
</el-table-column>
</el-table>
<div class="table-footer">
<el-pagination
background
layout="sizes, prev, pager, next, jumper"
:page-size="pageSize"
:page-sizes="[5, 10, 20, 50]"
:total="totalCount"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</div>
<!-- 政策对话框 -->
<el-dialog :title="policyDialog.title" v-model="policyDialog.visible" width="800px" @close="closePolicyDialog">
<el-form :model="policyForm" :rules="policyRules" ref="policyFormRef" label-width="100px" label-suffix="">
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="政策名称" prop="policyName">
<el-input v-model="policyForm.policyName" placeholder="请输入政策名称" maxLength="50" show-word-limit />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="分期比例" prop="installmentRatio">
<el-select v-model="policyForm.installmentRatio" placeholder="请选择分期比例" style="width: 100%">
<el-option label="10-20" value="10-20" />
<el-option label="20-30" value="20-30" />
<el-option label="30-40" value="30-40" />
<el-option label="40-50" value="40-50" />
<el-option label="50-60" value="50-60" />
<el-option label="60-70" value="60-70" />
<el-option label="70-80" value="70-80" />
<el-option label="80-90" value="80-90" />
<el-option label="90-100" value="90-100" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="楼栋名称" prop="buildingNames">
<el-input v-model="policyForm.buildingNames" placeholder="请输入楼栋名称" style="width: 100%" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="生效日期" prop="effectiveDate">
<el-date-picker
v-model="policyForm.effectiveDate"
type="date"
placeholder="选择生效日期"
style="width: 100%"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="失效日期" prop="expirationDate">
<el-date-picker
v-model="policyForm.expirationDate"
type="date"
placeholder="选择失效日期"
style="width: 100%"
format="YYYY-MM-DD"
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-input v-model="policyForm.creator" readonly />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="创建时间">
<el-input v-model="policyForm.createDate" readonly />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="类别">
<span>分期付款商业贷款公积金贷款</span>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="付款方式类型">
<span>{{ policyForm.paymentType || '分期付款' }}</span>
</el-form-item>
</el-col>
</el-row>
</el-form>
<!-- 付款明细表 -->
<div class="payment-details-table">
<div class="table-header" style="display: flex; justify-content: flex-end; margin-bottom: 10px">
<el-button type="primary" size="small" @click="addPaymentDetail"> 新增 </el-button>
</div>
<el-table :data="paymentDetailsData" border style="width: 100%" size="small">
<el-table-column prop="id" label="序号" width="50" align="center" />
<el-table-column prop="paymentTime" label="付款时间" width="150" align="center">
<template #default="{ row }">
<span>{{ row.paymentTime }}</span>
</template>
</el-table-column>
<el-table-column prop="daysWithin" label="几天内" width="80" align="center" />
<el-table-column prop="monthsWithin" label="几个月内" width="80" align="center" />
<el-table-column prop="paymentType" label="款项类型" width="100" align="center" />
<el-table-column prop="paymentName" label="款项名称" width="100" align="center" />
<el-table-column prop="ratio" label="比例" width="80" align="center">
<template #default="{ row }">
<span>{{ row.ratio }}%</span>
</template>
</el-table-column>
<el-table-column prop="amount" label="金额" width="100" align="center">
<template #default="{ row }">
<span>¥{{ row.amount }}</span>
</template>
</el-table-column>
<el-table-column prop="remarks" label="备注" min-width="120" show-overflow-tooltip />
<el-table-column label="操作" width="120" fixed="right" align="center">
<template #default="{ $index }">
<el-button type="primary" link @click="editPaymentDetail($index)" size="small">
<el-icon><Edit /></el-icon>
编辑
</el-button>
<el-button type="danger" link @click="deletePaymentDetail($index)" size="small">
<el-icon><Delete /></el-icon>
删除
</el-button>
</template>
</el-table-column>
</el-table>
</div>
<template #footer>
<div style="display: flex; justify-content: center;">
<el-button type="primary" @click="submitPolicyForm">保存</el-button>
<el-button @click="closePolicyDialog" style="margin-left: 12px;">取消</el-button>
</div>
</template>
</el-dialog>
<!-- 新增分期详情对话框 -->
<el-dialog title="新增分期详情" v-model="paymentDetailDialog.visible" width="700px" @close="closePaymentDetailDialog">
<el-form :model="paymentDetailForm" :rules="paymentDetailRules" ref="paymentDetailFormRef" label-width="120px" label-suffix="">
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="付款时间" prop="paymentTime">
<el-select v-model="paymentDetailForm.paymentTime" placeholder="请选择付款时间" style="width: 100%">
<el-option label="小订/认购签署日期" value="小订/认购签署日期" />
<el-option label="合同签署日期" value="合同签署日期" />
<el-option label="结顶/竣备日期(实际日期优先)" value="结顶/竣备日期(实际日期优先)" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12" v-if="false">
<el-form-item label="时间单位" prop="timeUnit">
<el-select v-model="paymentDetailForm.timeUnit" placeholder="请选择" style="width: 100%">
<el-option label="天" value="天" />
<el-option label="月" value="月" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="几天内" prop="daysWithin">
<el-input-number v-model="paymentDetailForm.daysWithin" :min="-100000000000" :max="100000000000" :precision="0" placeholder="请输入天数" style="width: 100%" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="几个月内" prop="monthsWithin">
<el-input-number v-model="paymentDetailForm.monthsWithin" :min="-100000000000" :max="100000000000" :precision="0" placeholder="请输入月数" style="width: 100%" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="款项类型" prop="paymentType">
<span>非贷款类房款</span>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="款项名称" prop="paymentName">
<el-select v-model="paymentDetailForm.paymentName" placeholder="请选择" style="width: 100%">
<el-option label="分期款" value="分期款" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="比例" prop="ratio">
<el-input-number v-model="paymentDetailForm.ratio" :min="0" :max="100" :precision="2" placeholder="请输入比例" style="width: 100%" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="金额" prop="amount">
<el-input-number v-model="paymentDetailForm.amount" :min="0" :precision="2" placeholder="请输入金额" style="width: 100%" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="24">
<el-form-item label="备注" prop="remarks">
<el-input v-model="paymentDetailForm.remarks" type="textarea" placeholder="请输入备注信息" rows="3" maxLength="200" show-word-limit />
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<div style="display: flex; justify-content: center;">
<el-button type="primary" @click="submitPaymentDetailForm" :loading="paymentDetailDialog.loading">保存</el-button>
<el-button type="primary" @click="submitPaymentDetailFormAndAddNew" :loading="paymentDetailDialog.loading" style="margin-left: 12px;">保存并新增</el-button>
<el-button @click="closePaymentDetailDialog" style="margin-left: 12px;">取消</el-button>
</div>
</template>
</el-dialog>
<div class="form-actions">
<el-button type="primary" @click="savePlan">{{ isEditMode ? '更新方案' : '保存方案' }}</el-button>
<el-button @click="handleCancel">取消</el-button>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, onUnmounted, computed } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { ElMessage, ElMessageBox, ElLoading } from 'element-plus';
import { Plus, Edit, Delete } from '@element-plus/icons-vue';
import { listPaymentDetails, addPaymentDetails, delPaymentDetails } from '@/api/system/paymentDetails';
import type { PaymentDetailsVO, PaymentDetailsForm } from '@/api/system/paymentDetails/types';
import { addInstallmentpolicy, updateInstallmentpolicy, getInstallmentpolicy, saveCompositeInstallmentPolicy, updateCompositeInstallmentPolicy } from '@/api/system/installmentpolicy';
import { addPolicy, updatePolicy, listPolicy } from '@/api/system/policy';
import { savePaymentDetailsForPolicy } from '@/api/system/paymentDetails';
import { listProject } from '@/api/system/project';
import { useUserStore } from '@/store/modules/user';
const route = useRoute();
const router = useRouter();
const userStore = useUserStore();
// 表单引用
const formRef = ref();
const policyFormRef = ref();
const paymentDetailFormRef = ref();
// 表单数据
const form = ref({
schemeName: '',
installments: [],
fillingInstruction: '',
creator: '',
createDate: '',
effectiveDate: '',
invalidDate: ''
});
const policyForm = ref({
id: '',
policyName: '',
installmentRatio: '',
buildingNames: '',
effectiveDate: '',
expirationDate: '',
creator: '',
createDate: '',
paymentType: '分期付款'
});
const paymentDetailForm = ref({
paymentTime: '',
timeUnit: '天',
daysWithin: 0,
monthsWithin: 0,
paymentType: '非贷款类房款',
paymentName: '分期款',
ratio: 0,
amount: 0,
remarks: ''
});
// 表格数据
const policyTableData = ref<any[]>([]);
const paymentDetailsData = ref<any[]>([]);
// 分页数据
const pageSize = ref(10);
const currentPage = ref(1);
const totalCount = ref(0);
// 对话框状态
const policyDialog = ref({
visible: false,
title: '',
type: 'add'
});
const paymentDetailDialog = ref({
visible: false,
loading: false
});
// 选项数据
const installmentOptions = ref<any[]>([]);
// 表单验证规则
const formRules = {
schemeName: [{ required: true, message: '请输入方案名称', trigger: 'blur' }],
installments: [{ required: true, message: '请选择关联分期', trigger: 'change' }]
};
const policyRules = {
policyName: [{ required: true, message: '请输入政策名称', trigger: 'blur' }],
installmentRatio: [{ required: true, message: '请选择分期比例', trigger: 'change' }],
effectiveDate: [{ required: true, message: '请选择生效日期', trigger: 'change' }],
expirationDate: [{ required: true, message: '请选择失效日期', trigger: 'change' }]
};
const paymentDetailRules = {
paymentTime: [{ required: true, message: '请选择付款时间', trigger: 'change' }],
daysWithin: [{ required: true, message: '请输入几天内', trigger: 'blur' }],
monthsWithin: [{ required: true, message: '请输入几个月内', trigger: 'blur' }],
paymentType: [{ required: true, message: '请选择款项类型', trigger: 'change' }],
paymentName: [{ required: true, message: '请输入款项名称', trigger: 'blur' }],
ratio: [{ required: true, message: '请输入比例', trigger: 'blur' }]
};
// 计算属性
const isEditMode = computed(() => !!route.query.id);
const displayEffectiveDate = computed(() => form.value.effectiveDate || '-');
const displayInvalidDate = computed(() => form.value.invalidDate || '-');
// 缓存相关方法
const paymentDetailsCache = new Map<string, any[]>();
const cachePaymentDetails = (policyId: string, details: any[]) => {
paymentDetailsCache.set(policyId, [...details]);
};
const getCachedPaymentDetails = (policyId: string) => {
return paymentDetailsCache.get(policyId) || null;
};
const clearPaymentDetailsCache = (policyId: string) => {
paymentDetailsCache.delete(policyId);
};
// 方法定义
const handleAddPolicy = () => {
try {
resetPolicyForm();
policyDialog.value.type = 'add';
policyDialog.value.title = '新增分期政策';
policyDialog.value.visible = true;
policyForm.value.creator = userStore.nickname;
policyForm.value.createDate = new Date().toISOString().split('T')[0];
} catch (error) {
console.error('新增政策按钮点击错误:', error);
ElMessage.error('新增政策失败');
}
};
const handleEditPolicy = (row: any) => {
try {
policyDialog.value.type = 'edit';
policyDialog.value.title = '编辑分期政策';
policyDialog.value.visible = true;
// 填充表单数据
Object.keys(policyForm.value).forEach(key => {
if (row[key] !== undefined) {
(policyForm.value as any)[key] = row[key];
}
});
// 加载对应的付款明细
loadPaymentDetailsForPolicy(row.id);
} catch (error) {
console.error('编辑政策错误:', error);
ElMessage.error('编辑政策失败');
}
};
const handleDeletePolicy = async (row: any) => {
try {
await ElMessageBox.confirm('确定要删除该政策吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
});
await delPaymentDetails(row.id);
// 从表格数据中移除
const index = policyTableData.value.findIndex(item => item.id === row.id);
if (index > -1) {
policyTableData.value.splice(index, 1);
}
ElMessage.success('删除成功');
} catch (error) {
if (error !== 'cancel') {
console.error('删除政策错误:', error);
ElMessage.error('删除政策失败');
}
}
};
const loadPaymentDetailsForPolicy = async (policyId: string) => {
try {
const cachedDetails = getCachedPaymentDetails(policyId);
if (cachedDetails) {
paymentDetailsData.value = cachedDetails;
return;
}
const { data } = await listPaymentDetails({ policyId });
paymentDetailsData.value = data || [];
cachePaymentDetails(policyId, paymentDetailsData.value);
} catch (error) {
console.error('加载付款明细错误:', error);
paymentDetailsData.value = [];
}
};
const resetPolicyForm = () => {
policyForm.value = {
id: '',
policyName: '',
installmentRatio: '',
buildingNames: '',
effectiveDate: '',
expirationDate: '',
creator: '',
createDate: '',
paymentType: '分期付款'
};
paymentDetailsData.value = [];
};
const closePolicyDialog = () => {
policyDialog.value.visible = false;
resetPolicyForm();
};
const submitPolicyForm = async () => {
try {
await policyFormRef.value?.validate();
if (paymentDetailsData.value.length === 0) {
ElMessage.warning('请至少添加一条付款明细');
return;
}
const loading = ElLoading.service({
lock: true,
text: '保存中...',
background: 'rgba(0, 0, 0, 0.7)'
});
try {
const policyData = {
...policyForm.value,
paymentDetails: paymentDetailsData.value
};
let result;
if (policyDialog.value.type === 'add') {
result = await addInstallmentpolicy(policyData);
} else {
result = await updateInstallmentpolicy(policyData);
}
if (result.code === 200) {
ElMessage.success(policyDialog.value.type === 'add' ? '新增成功' : '更新成功');
closePolicyDialog();
// 刷新表格
const newPolicy = result.data;
if (policyDialog.value.type === 'add') {
policyTableData.value.unshift(newPolicy);
} else {
const index = policyTableData.value.findIndex(item => item.id === newPolicy.id);
if (index > -1) {
policyTableData.value.splice(index, 1, newPolicy);
}
}
}
} finally {
loading.close();
}
} catch (error) {
console.error('保存政策错误:', error);
ElMessage.error('保存政策失败');
}
};
// 付款明细相关方法
const addPaymentDetail = () => {
paymentDetailForm.value = {
paymentTime: '',
timeUnit: '天',
daysWithin: 0,
monthsWithin: 0,
paymentType: '非贷款类房款',
paymentName: '分期款',
ratio: 0,
amount: 0,
remarks: ''
};
paymentDetailDialog.value.visible = true;
paymentDetailDialog.value.loading = false;
};
const editPaymentDetail = (index: number) => {
const detail = paymentDetailsData.value[index];
paymentDetailForm.value = { ...detail };
paymentDetailDialog.value.visible = true;
paymentDetailDialog.value.loading = false;
};
const deletePaymentDetail = (index: number) => {
paymentDetailsData.value.splice(index, 1);
};
const submitPaymentDetailForm = async () => {
try {
await paymentDetailFormRef.value?.validate();
const detail = { ...paymentDetailForm.value };
// 检查是否已存在相同ID的记录
const existingIndex = paymentDetailsData.value.findIndex(item => item.id === detail.id);
if (existingIndex > -1) {
paymentDetailsData.value.splice(existingIndex, 1, detail);
} else {
detail.id = Date.now().toString();
paymentDetailsData.value.push(detail);
}
closePaymentDetailDialog();
ElMessage.success('保存成功');
} catch (error) {
console.error('保存付款明细错误:', error);
ElMessage.error('保存失败');
}
};
const submitPaymentDetailFormAndAddNew = async () => {
await submitPaymentDetailForm();
// 重置表单并继续添加
paymentDetailForm.value = {
paymentTime: '',
timeUnit: '天',
daysWithin: 0,
monthsWithin: 0,
paymentType: '非贷款类房款',
paymentName: '分期款',
ratio: 0,
amount: 0,
remarks: ''
};
};
const closePaymentDetailDialog = () => {
paymentDetailDialog.value.visible = false;
paymentDetailFormRef.value?.resetFields();
};
// 页面主要方法
const loadInstallmentOptions = async () => {
try {
const { data } = await listProject({});
installmentOptions.value = data || [];
} catch (error) {
console.error('加载分期选项错误:', error);
installmentOptions.value = [];
}
};
const loadData = async () => {
if (!isEditMode.value) return;
try {
const loading = ElLoading.service({
lock: true,
text: '加载中...',
background: 'rgba(0, 0, 0, 0.7)'
});
try {
const { data } = await getInstallmentpolicy(route.query.id as string);
if (data) {
Object.keys(form.value).forEach(key => {
if (data[key] !== undefined) {
(form.value as any)[key] = data[key];
}
});
// 加载政策数据
policyTableData.value = data.policies || [];
}
} finally {
loading.close();
}
} catch (error) {
console.error('加载数据错误:', error);
ElMessage.error('加载数据失败');
}
};
const savePlan = async () => {
try {
await formRef.value?.validate();
if (policyTableData.value.length === 0) {
ElMessage.warning('请至少添加一条政策');
return;
}
const loading = ElLoading.service({
lock: true,
text: '保存中...',
background: 'rgba(0, 0, 0, 0.7)'
});
try {
const planData = {
...form.value,
policies: policyTableData.value
};
let result;
if (isEditMode.value) {
result = await updateCompositeInstallmentPolicy(planData);
} else {
result = await saveCompositeInstallmentPolicy(planData);
}
if (result.code === 200) {
ElMessage.success(isEditMode.value ? '更新成功' : '保存成功');
router.push('/system/periodicpolicy/installment');
}
} finally {
loading.close();
}
} catch (error) {
console.error('保存方案错误:', error);
ElMessage.error('保存方案失败');
}
};
const handleCancel = () => {
router.push('/system/periodicpolicy/installment');
};
const handleInstallmentChange = (value: string[]) => {
console.log('选择的分期:', value);
};
const handleSizeChange = (val: number) => {
pageSize.value = val;
};
const handleCurrentChange = (val: number) => {
currentPage.value = val;
};
// 生命周期
onMounted(() => {
loadInstallmentOptions();
loadData();
// 设置创建人
form.value.creator = userStore.nickname;
form.value.createDate = new Date().toISOString().split('T')[0];
});
onUnmounted(() => {
// 清理缓存
paymentDetailsCache.clear();
});
</script>
<style scoped>
.form-container {
padding: 20px;
min-height: calc(100vh - 120px);
font-family: 'PingFang SC', 'Microsoft Yahei', Verdana, simsun, Arial, Helvetica, sans-serif;
color: #666;
font-size: 14px;
}
.page-title {
font-size: 18px;
font-weight: normal;
margin-bottom: 20px;
color: #303133;
font-family: 'PingFang SC', 'Microsoft Yahei', Verdana, simsun, Arial, Helvetica, sans-serif;
}
.policy-form {
padding: 20px;
border-radius: 8px;
margin-bottom: 20px;
}
.form-row {
display: flex;
gap: 20px;
}
.form-row .half-width {
flex: 1;
}
.text-display {
padding: 8px 12px;
border-radius: 4px;
color: #666;
font-size: 14px;
font-family: 'PingFang SC', 'Microsoft Yahei', Verdana, simsun, Arial, Helvetica, sans-serif;
}
.table-section {
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}
.table-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
}
.table-header span {
font-size: 16px;
font-weight: normal;
color: #303133;
}
.table-footer {
margin-top: 15px;
display: flex;
justify-content: flex-end;
}
.form-actions {
margin-top: 30px;
display: flex;
justify-content: center;
gap: 20px;
}
:deep(.el-form-item__label) {
font-weight: normal;
color: #606266;
font-family: 'PingFang SC', 'Microsoft Yahei', Verdana, simsun, Arial, Helvetica, sans-serif;
font-size: 14px;
}
:deep(.el-table th) {
color: #606266;
font-weight: normal;
font-family: 'PingFang SC', 'Microsoft Yahei', Verdana, simsun, Arial, Helvetica, sans-serif;
font-size: 14px;
}
:deep(.el-input__wrapper) {
box-shadow: none;
border: 1px solid #dcdfe6;
font-family: 'PingFang SC', 'Microsoft Yahei', Verdana, simsun, Arial, Helvetica, sans-serif;
color: #666;
font-size: 14px;
}
:deep(.el-textarea__inner) {
box-shadow: none;
border: 1px solid #dcdfe6;
font-family: 'PingFang SC', 'Microsoft Yahei', Verdana, simsun, Arial, Helvetica, sans-serif;
color: #666;
font-size: 14px;
}
:deep(.el-input__inner) {
font-family: 'PingFang SC', 'Microsoft Yahei', Verdana, simsun, Arial, Helvetica, sans-serif;
color: #666;
font-size: 14px;
}
:deep(.el-select__wrapper) {
font-family: 'PingFang SC', 'Microsoft Yahei', Verdana, simsun, Arial, Helvetica, sans-serif;
color: #666;
font-size: 14px;
}
:deep(.el-select__placeholder) {
font-family: 'PingFang SC', 'Microsoft Yahei', Verdana, simsun, Arial, Helvetica, sans-serif;
color: #666;
font-size: 14px;
}
:deep(.el-input-number) {
font-family: 'PingFang SC', 'Microsoft Yahei', Verdana, simsun, Arial, Helvetica, sans-serif;
color: #666;
font-size: 14px;
}
.payment-details-section {
margin: 20px 0;
}
.section-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
padding: 10px 0;
border-bottom: 1px solid #e4e7ed;
}
.section-header span {
font-size: 16px;
font-weight: normal;
color: #303133;
}
:deep(.el-table .cell) {
padding: 8px 4px;
font-family: 'PingFang SC', 'Microsoft Yahei', Verdana, simsun, Arial, Helvetica, sans-serif;
color: #666;
font-size: 14px;
}
:deep(.el-input-number) {
width: 100% !important;
}
:deep(.el-input-number .el-input__wrapper) {
padding: 0 8px;
font-family: 'PingFang SC', 'Microsoft Yahei', Verdana, simsun, Arial, Helvetica, sans-serif;
color: #666;
font-size: 14px;
}
@media screen and (max-width: 768px) {
.form-row {
flex-direction: column;
gap: 0;
}
.form-container {
padding: 10px;
}
.policy-form {
padding: 15px;
}
}
:deep(.el-form-item__content) {
font-family: 'PingFang SC', 'Microsoft Yahei', Verdana, simsun, Arial, Helvetica, sans-serif;
color: #666;
font-size: 14px;
}
:deep(.el-table__body .el-table__row) {
font-family: 'PingFang SC', 'Microsoft Yahei', Verdana, simsun, Arial, Helvetica, sans-serif;
color: #666;
font-size: 14px;
}
:deep(.el-table__body .cell) {
font-family: 'PingFang SC', 'Microsoft Yahei', Verdana, simsun, Arial, Helvetica, sans-serif;
color: #666;
font-size: 14px;
}
</style>

View File

@ -0,0 +1,297 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="方案名称" prop="schemeName">
<el-input
v-model="queryParams.schemeName"
placeholder="请输入方案名称"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="状态" prop="status">
<el-select v-model="queryParams.status" placeholder="请选择状态" clearable>
<el-option
v-for="dict in dict.type.sys_normal_disable"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
type="primary"
plain
icon="el-icon-plus"
size="mini"
@click="handleAdd"
v-hasPermi="['system:installmentpolicy:add']"
>新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="success"
plain
icon="el-icon-edit"
size="mini"
:disabled="single"
@click="handleUpdate"
v-hasPermi="['system:installmentpolicy:edit']"
>修改</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="danger"
plain
icon="el-icon-delete"
size="mini"
:disabled="multiple"
@click="handleDelete"
v-hasPermi="['system:installmentpolicy:remove']"
>删除</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="warning"
plain
icon="el-icon-download"
size="mini"
@click="handleExport"
v-hasPermi="['system:installmentpolicy:export']"
>导出</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="installmentpolicyList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="序号" type="index" width="50" align="center" />
<el-table-column label="方案名称" align="center" prop="schemeName" />
<el-table-column label="关联分期" align="center" prop="installments" :show-overflow-tooltip="true">
<template slot-scope="scope">
<span>{{ formatInstallments(scope.row.installments) }}</span>
</template>
</el-table-column>
<el-table-column label="填报说明" align="center" prop="fillingInstruction" :show-overflow-tooltip="true" />
<el-table-column label="生效日期" align="center" prop="effectiveDate" width="100" />
<el-table-column label="失效日期" align="center" prop="invalidDate" width="100" />
<el-table-column label="状态" align="center" prop="status">
<template slot-scope="scope">
<dict-tag :options="dict.type.sys_normal_disable" :value="scope.row.status" />
</template>
</el-table-column>
<el-form-item label="创建人" align="center" prop="creator" />
<el-table-column label="创建时间" align="center" prop="createDate" width="100" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button
size="mini"
type="primary"
link
@click="handleUpdate(scope.row)"
v-hasPermi="['system:installmentpolicy:edit']"
>
<i class="el-icon-edit"></i>编辑
</el-button>
<el-button
size="mini"
type="danger"
link
@click="handleDelete(scope.row)"
v-hasPermi="['system:installmentpolicy:remove']"
>
<i class="el-icon-delete"></i>删除
</el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total>0"
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
</div>
</template>
<script>
import { listInstallmentpolicy, getInstallmentpolicy, delInstallmentpolicy, addInstallmentpolicy, updateInstallmentpolicy, exportInstallmentpolicy } from "@/api/system/installmentpolicy";
export default {
name: "Installmentpolicy",
dicts: ['sys_normal_disable'],
data() {
return {
// 遮罩层
loading: true,
// 选中数组
ids: [],
// 非单个禁用
single: true,
// 非多个禁用
multiple: true,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 分期方案表格数据
installmentpolicyList: [],
// 弹出层标题
title: "",
// 是否显示弹出层
open: false,
// 查询参数
queryParams: {
pageNum: 1,
pageSize: 10,
schemeName: null,
status: null,
},
// 表单参数
form: {},
// 表单校验
rules: {
schemeName: [
{ required: true, message: "方案名称不能为空", trigger: "blur" }
],
}
};
},
created() {
this.getList();
},
methods: {
/** 查询分期方案列表 */
getList() {
this.loading = true;
listInstallmentpolicy(this.queryParams).then(response => {
this.installmentpolicyList = response.rows;
this.total = response.total;
this.loading = false;
});
},
// 取消按钮
cancel() {
this.open = false;
this.reset();
},
// 表单重置
reset() {
this.form = {
id: null,
schemeName: null,
installments: null,
fillingInstruction: null,
effectiveDate: null,
invalidDate: null,
status: "0",
creator: null,
createDate: null,
updateBy: null,
updateTime: null,
remark: null
};
this.resetForm("form");
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
// 多选框选中数据
handleSelectionChange(selection) {
this.ids = selection.map(item => item.id)
this.single = selection.length!==1
this.multiple = !selection.length
},
/** 新增按钮操作 */
handleAdd() {
this.$router.push({
path: '/system/periodicpolicy/installment/add',
query: { type: 'add' }
});
},
/** 修改按钮操作 */
handleUpdate(row) {
const id = row.id || this.ids
this.$router.push({
path: '/system/periodicpolicy/installment/add',
query: { id: id, type: 'edit' }
});
},
/** 删除按钮操作 */
handleDelete(row) {
const ids = row.id || this.ids;
this.$modal.confirm('是否确认删除分期方案编号为"' + ids + '"的数据项?').then(function() {
return delInstallmentpolicy(ids);
}).then(() => {
this.getList();
this.$modal.msgSuccess("删除成功");
}).catch(() => {});
},
/** 导出按钮操作 */
handleExport() {
const queryParams = this.queryParams;
this.$modal.confirm('是否确认导出所有分期方案数据项?').then(() => {
this.exportLoading = true;
return exportInstallmentpolicy(queryParams);
}).then(response => {
this.$download.download(response, '分期方案.xlsx');
}).catch(() => {}).finally(() => {
this.exportLoading = false;
});
},
formatInstallments(installments) {
if (!installments || installments.length === 0) return '-';
return installments.join(', ');
}
}
};
</script>
<style scoped>
.app-container {
font-family: 'PingFang SC', 'Microsoft Yahei', Verdana, simsun, Arial, Helvetica, sans-serif;
color: #666;
font-size: 14px;
}
.mb8 {
margin-bottom: 8px;
}
:deep(.el-form-item__label) {
font-weight: normal;
color: #606266;
font-family: 'PingFang SC', 'Microsoft Yahei', Verdana, simsun, Arial, Helvetica, sans-serif;
font-size: 14px;
}
:deep(.el-table th) {
color: #606266;
font-weight: normal;
font-family: 'PingFang SC', 'Microsoft Yahei', Verdana, simsun, Arial, Helvetica, sans-serif;
font-size: 14px;
}
:deep(.el-table__body .el-table__row) {
font-family: 'PingFang SC', 'Microsoft Yahei', Verdana, simsun, Arial, Helvetica, sans-serif;
color: #666;
font-size: 14px;
}
</style>

View File

@ -332,6 +332,8 @@ const handleExport = () => {
); );
}; };
onMounted(() => { onMounted(() => {
getList(); getList();
}); });

View File

@ -81,7 +81,7 @@ CREATE TABLE t_policy (
`description` TEXT COMMENT '说明', `description` TEXT COMMENT '说明',
`category` VARCHAR ( 128 ) NOT NULL COMMENT '类别', `category` VARCHAR ( 128 ) NOT NULL COMMENT '类别',
`payment_type` VARCHAR ( 128 ) NOT NULL COMMENT '付款方式类型', `payment_type` VARCHAR ( 128 ) NOT NULL COMMENT '付款方式类型',
`down_payment_ratio` DECIMAL (5,2) DEFAULT NULL COMMENT '首付比例(%)', `down_payment_ratio` VARCHAR(20) DEFAULT NULL COMMENT '首付比例(%)',
`payment_method` VARCHAR ( 50 ) DEFAULT NULL COMMENT '付款方式', `payment_method` VARCHAR ( 50 ) DEFAULT NULL COMMENT '付款方式',
`policy_type` VARCHAR ( 50 ) DEFAULT NULL COMMENT '政策类型', `policy_type` VARCHAR ( 50 ) DEFAULT NULL COMMENT '政策类型',
`tenant_id` VARCHAR ( 20 ) DEFAULT '000000' COMMENT '租户编号', `tenant_id` VARCHAR ( 20 ) DEFAULT '000000' COMMENT '租户编号',