금리비교 추가 #32

Merged
owrainfo merged 1 commits from feature/template into master 2026-01-21 12:04:06 +09:00
13 changed files with 2720 additions and 1000 deletions

View File

@@ -66,6 +66,8 @@ $routes->group('', ['namespace' => 'App\Controllers\Article'], static function (
$routes->get('getResultList', 'Receipt::getResultList'); $routes->get('getResultList', 'Receipt::getResultList');
$routes->get('excel', 'Receipt::excel'); $routes->get('excel', 'Receipt::excel');
$routes->post('saveTel', 'Receipt::saveTel'); // 연락가능전화 저장
}); });
/** /**
@@ -623,6 +625,12 @@ $routes->group('manage', ['namespace' => 'App\Controllers\Manage'], function ($r
$routes->get('loginlog/excel', 'LoginLog::excel'); $routes->get('loginlog/excel', 'LoginLog::excel');
}); });
/**
* 금리비교
*/
$routes->group('interest_rates', ['namespace' => 'App\Controllers\Interest'], function ($routes) {
$routes->get('interest/lists', 'Rates::lists');
});
/** /**
* 로그인 API * 로그인 API

View File

@@ -173,4 +173,28 @@ class Receipt extends BaseController
return view("pages/article/receipt/detail", $this->data); return view("pages/article/receipt/detail", $this->data);
} }
// 연락처 저장
public function saveTel()
{
try {
$tel = $this->request->getPost('agent_tel');
$this->model->saveTel($tel);
return $this->response->setJSON([
'code' => '0',
'msg' => 'success'
]);
} catch (\Exception $e) {
return $this->response->setJSON([
'code' => '9',
'msg' => $e->getMessage(),
]);
}
}
} }

View File

@@ -0,0 +1,40 @@
<?php
namespace App\Controllers\Interest;
use App\Controllers\BaseController;
use App\Models\common\CodeModel;
use App\Models\interest\RatesModel;
class Rates extends BaseController
{
private $model, $codeModel;
public function __construct()
{
$this->model = new RatesModel();
$this->codeModel = new CodeModel();
}
public function lists(): string
{
$codes = $this->codeModel->getCodeLists(['BANK', 'INSURANCE', 'LOAN_PLACE']); // 코드조회
$placeArr = [];
foreach ($codes as $c) {
if ($c['category'] == "LOAN_PLACE") {
array_push($placeArr, $c);
}
}
$data = $this->model->getLists();
$this->data['place'] = $placeArr;
$this->data['data'] = $data;
return view("pages/interest/list", $this->data);
}
}

View File

@@ -1253,4 +1253,54 @@ class ReceiptModel extends Model
return $query2->getResultArray(); return $query2->getResultArray();
} }
// 연락처 저장
public function saveTel($rcpt_sq, $tel)
{
$this->db->transStart();
$usr_id = session('usr_id');
$sql = "SELECT rcpt_stat, agent_tel FROM receipt WHERE rcpt_sq = ?";
$data = [$rcpt_sq];
$query = $this->db->query($sql, $data);
$row = $query->getRowArray();
$sql = "UPDATE receipt" .
" SET agent_tel = ?" .
" WHERE rcpt_sq = ?";
$data = [$tel, $rcpt_sq];
if ($query = $this->db->query($sql, $data) === false) {
return [
'success' => false,
'msg' => '저장실패',
];
}
$this->saveChangedHistory($rcpt_sq, $row['rcpt_stat'], 'C20', $usr_id, $row['agent_tel']);
$this->db->transComplete();
return [
'success' => true,
];
}
public function saveChangedHistory($rcpt_sq, $rcpt_stat, $changed_type, $usr_id, $remark)
{
$sql = "INSERT INTO changed_history" .
" (rcpt_sq, rcpt_stat, changed_type, changed_id, changed_tm, remark)" .
" VALUES" .
" (?, ?, ?, ?, now(), ?)";
$data = [
$rcpt_sq,
$rcpt_stat,
$changed_type,
$usr_id,
$remark
];
$res = $this->db->query($sql, $data);
}
} }

View File

@@ -0,0 +1,44 @@
<?php
namespace App\Models\interest;
use CodeIgniter\Model;
class RatesModel extends Model
{
public function getLists()
{
$builder = $this->db->table("tb_lender AS a");
$builder->select("a.seq
, IFNULL(CASE
WHEN a.bank_type = 'B' THEN (SELECT cd_nm FROM codes WHERE cd = a.bank AND category = 'BANK')
WHEN a.bank_type = 'I' THEN (SELECT cd_nm FROM codes WHERE cd = a.bank AND category = 'INSURANCE') END, a.bank_nm) AS bank
, a.bank_type
, a.product
, a.tel
, a.min_rate
, a.avg_rate
, a.max_rate
, a.chg_yn
, a.split_yn
, b.extra_fee
, b.late_rate
, b.join_method
, b.prepay_fee
, b.loan_limit
, IFNULL(DATE_FORMAT(b.prov_date, '%Y-%m-%d'), '') AS prov_date");
$builder->join("tbl_lender_dtl AS b", "b.seq = a.seq", "LEFT");
$builder->where("a.use_yn", "Y");
// $builder->orderBy('a.seq', 'DESC');
return $builder->get()->getResultArray();
}
}

View File

@@ -98,7 +98,7 @@ $usr_level = session('usr_level');
<div class="d-flex align-items-center gap-2"> <div class="d-flex align-items-center gap-2">
<input type="text" id="agent_tel" name="agent_tel" class="form-control form-control-sm" maxlength="16" <input type="text" id="agent_tel" name="agent_tel" class="form-control form-control-sm" maxlength="16"
value="<?= esc($agent_tel) ?>" style="max-width: 220px;" /> value="<?= esc($agent_tel) ?>" style="max-width: 220px;" />
<button type="button" class="btn btn-sm btn-outline-light">저장</button> <button type="button" class="btn btn-sm btn-outline-light" onclick="fn_save_tel();">저장</button>
</div> </div>
</td> </td>
</tr> </tr>
@@ -109,6 +109,8 @@ $usr_level = session('usr_level');
</div> </div>
<!-- 매물정보 --> <!-- 매물정보 -->
<form id="rcptFrm" onsubmit="return false;">
<input type="hidden" name="rcpt_product" id="rcpt_product" value="<?= $data['rcpt_product'] ?>" />
<div class="main-card mb-3 card"> <div class="main-card mb-3 card">
<div class="card-body"> <div class="card-body">
<h5 class="card-title">매물 정보</h5> <h5 class="card-title">매물 정보</h5>
@@ -151,7 +153,7 @@ $usr_level = session('usr_level');
<td><?= $data['rcpt_product_nm'] ?></td> <td><?= $data['rcpt_product_nm'] ?></td>
<th>거래구분</th> <th>거래구분</th>
<td> <td>
<select class="form-select" id="trade_type" disabled> <select class="form-select" id="trade_type" onchange="trade_type_onchange();" disabled>
<option value="">거래구분</option> <option value="">거래구분</option>
<?php foreach ($codes as $c): ?> <?php foreach ($codes as $c): ?>
<?php if ($c['category'] === "TRADE_TYPE"): ?> <?php if ($c['category'] === "TRADE_TYPE"): ?>
@@ -229,8 +231,6 @@ $usr_level = session('usr_level');
$chk_array = ['B01', 'B02', 'B03']; $chk_array = ['B01', 'B02', 'B03'];
$show_sale_premium = (in_array(($data['rcpt_product'] ?? ''), $chk_array) || ($data['trade_type'] ?? '') === 'A1'); $show_sale_premium = (in_array(($data['rcpt_product'] ?? ''), $chk_array) || ($data['trade_type'] ?? '') === 'A1');
// 월세 영역 노출(서버에서 1차 결정: 값이 있으면 보여주기 / 없으면 숨김)
$show_monthly = (!empty($v3));
?> ?>
<!-- 보증금/금액 --> <!-- 보증금/금액 -->
@@ -242,7 +242,7 @@ $usr_level = session('usr_level');
</div> </div>
<!-- 월세(월) --> <!-- 월세(월) -->
<div id="div_trade_type_price_monthly" class="<?= $show_monthly ? '' : 'd-none' ?>"> <div id="div_trade_type_price_monthly">
<div class="d-flex align-items-center gap-1 flex-wrap"> <div class="d-flex align-items-center gap-1 flex-wrap">
<input type="text" class="form-control form-control-sm" name="rcpt_product_info3" <input type="text" class="form-control form-control-sm" name="rcpt_product_info3"
id="rcpt_product_info3" value="<?= esc($v3) ?>" style="width: 110px;" disabled /> id="rcpt_product_info3" value="<?= esc($v3) ?>" style="width: 110px;" disabled />
@@ -268,9 +268,11 @@ $usr_level = session('usr_level');
<?php endif; ?> <?php endif; ?>
<!-- 버튼 --> <!-- 버튼 -->
<div class="d-flex align-items-center gap-1 mt-2"> <div class="d-flex align-items-center justify-content-end gap-1 mt-2">
<button type="button" class="btn btn-sm btn-outline-light btn-edit">수정</button> <button type="button" class="btn btn-sm btn-outline-light btn-edit"
<button type="button" class="btn btn-sm btn-outline-success btn-save">가격수정</button> onclick="editPriceInfo();">수정</button>
<button type="button" class="btn btn-sm btn-outline-success btn-save"
onclick="modifyPriceInfo();">가격수정</button>
</div> </div>
</div> </div>
</td> </td>
@@ -299,12 +301,25 @@ $usr_level = session('usr_level');
?> ?>
<td class="d-flex gap-1"> <td class="d-flex gap-1">
<input type="text" class="form-control" name="rcpt_floor" id="rcpt_floor" <input type="text" class="form-control" name="rcpt_floor" id="rcpt_floor"
value="<?= $data['rcpt_floor'] ?>" size="8" maxlength="3" disabled="disabled" style="width: 100px;" /> value="<?= $data['rcpt_floor'] ?>" size="8" maxlength="3" disabled="disabled"
style="width: 100px;" />
<input type="text" class="form-control" name="rcpt_floor2" id="rcpt_floor2" <input type="text" class="form-control" name="rcpt_floor2" id="rcpt_floor2"
value="<?= $data['rcpt_floor2'] ?>" size="8" maxlength="3" disabled="disabled" value="<?= $data['rcpt_floor2'] ?>" size="8" maxlength="3" disabled="disabled"
style="width: 100px;" /> style="width: 100px;" />
</td> </td>
</tr> </tr>
<tr class="spc">
<th></th>
<td></td>
<th>면적확인파일1</th>
<td></td>
</tr>
<tr class="spc">
<th>방</th>
<td></td>
<th>면적확인파일2</th>
<td></td>
</tr>
<tr> <tr>
<th>지도좌표</th> <th>지도좌표</th>
<td class="d-flex text-nowrap gap-1"> <td class="d-flex text-nowrap gap-1">
@@ -1061,7 +1076,8 @@ $usr_level = session('usr_level');
<div class="mb-3"> <div class="mb-3">
<div class="d-flex justify-content-between align-items-center mb-2"> <div class="d-flex justify-content-between align-items-center mb-2">
<div class="fw-semibold">분양권 (최대 5장)</div> <div class="fw-semibold">분양권 (최대 5장)</div>
<button type="button" class="btn btn-sm btn-outline-secondary" onclick="viewFilePop('I8', '0')">파일</button> <button type="button" class="btn btn-sm btn-outline-secondary"
onclick="viewFilePop('I8', '0')">파일</button>
</div> </div>
<div class="d-flex flex-wrap gap-2"> <div class="d-flex flex-wrap gap-2">
@@ -1080,7 +1096,8 @@ $usr_level = session('usr_level');
<div class="fw-semibold">매물사진 (최대 15장)</div> <div class="fw-semibold">매물사진 (최대 15장)</div>
<div class="d-flex gap-1 flex-wrap"> <div class="d-flex gap-1 flex-wrap">
<button type="button" class="btn btn-sm btn-outline-secondary" onclick="viewFilePop('I4', '')">파일</button> <button type="button" class="btn btn-sm btn-outline-secondary"
onclick="viewFilePop('I4', '')">파일</button>
<button type="button" class="btn btn-sm btn-outline-secondary" <button type="button" class="btn btn-sm btn-outline-secondary"
onclick="saveImgOrder('I4', 'M201')">저장</button> onclick="saveImgOrder('I4', 'M201')">저장</button>
<button type="button" class="btn btn-sm btn-outline-danger" <button type="button" class="btn btn-sm btn-outline-danger"
@@ -1249,6 +1266,7 @@ $usr_level = session('usr_level');
</table> </table>
</div> </div>
</div> </div>
</form>
<!-- 정보변경 이력 --> <!-- 정보변경 이력 -->
<div class="main-card mb-3 card"> <div class="main-card mb-3 card">
@@ -1302,6 +1320,8 @@ $usr_level = session('usr_level');
$(function () { $(function () {
trade_type_onchange();
map = new naver.maps.Map('mapArea', { map = new naver.maps.Map('mapArea', {
center: new naver.maps.LatLng(lat, lng), center: new naver.maps.LatLng(lat, lng),
useStyleMap: true, useStyleMap: true,
@@ -1323,8 +1343,128 @@ $usr_level = session('usr_level');
map: map map: map
}); });
}); });
function trade_type_onchange() {
var trade_type = $('#trade_type').val();
if (trade_type == 'B2' || trade_type == 'B3') {
// 월세...
$('#div_trade_type_price_monthly').show();
} else {
$('#div_trade_type_price_monthly').hide();
}
}
// 가격수정 btn
function editPriceInfo() {
var rcpt_product = $('#rcpt_product').val();
var trade_type = $('#trade_type').val();
$("#trade_type").prop("disabled", false);
$("#rcpt_product_info2").prop("disabled", false);
$("#rcpt_product_info3").prop("disabled", false);
if (trade_type == "A1") {
if (rcpt_product == 'A01' || rcpt_product == 'A02' || rcpt_product == 'A03' || rcpt_product == 'B01' || rcpt_product == 'B02' || rcpt_product == 'B03') {
$("#rcpt_product_info4").prop("disabled", false);
$("#rcpt_product_info5").prop("disabled", false);
}
}
}
// 가격수정 저장
function modifyPriceInfo() {
swal.fire({
text: "저장 하시겠습니까?",
type: "warning",
showCancelButton: true,
confirmButtonText: "예",
cancelButtonText: "아니오",
closeOnConfirm: false,
closeOnCancel: true,
confirmButtonColor: "#3085d6",
cancelButtonColor: "#d33",
}).then((result) => {
if (result.isConfirmed) {
var params = {
'rcpt_sq': '<?= $data['rcpt_sq'] ?>',
'agent_tel': $("#agent_tel").val(),
};
callAjax("/article/apt/modifyPriceInfo", params, fn_result);
}
});
}
// 연락가능전화 저장
function fn_save_tel() {
swal.fire({
text: "저장 하시겠습니까?",
type: "warning",
showCancelButton: true,
confirmButtonText: "예",
cancelButtonText: "아니오",
closeOnConfirm: false,
closeOnCancel: true,
confirmButtonColor: "#3085d6",
cancelButtonColor: "#d33",
}).then((result) => {
if (result.isConfirmed) {
var params = {
'rcpt_sq': '<?= $data['rcpt_sq'] ?>',
'agent_tel': $("#agent_tel").val(),
};
callAjax("/article/apt/saveAptMemo", params, fn_result);
}
});
}
function callAjax(target, params, callback) {
$.ajax({
url: target,
method: "POST",
dataType: "json",
data: params,
beforeSend: function () {
blockUI.blockPage({
message: tpl
})
},
complete: function () {
blockUI.unblockPage()
},
success: function (result) {
callback(result);
}
});
}
function fn_result(result) {
if (result.code == '0') {
Swal.fire({
title: "정상 처리되었습니다.",
icon: "success",
draggable: true
})
setTimeout(() => {
location.reload();
}, 1000);
} else {
Swal.fire({
title: result.msg,
icon: "error",
draggable: true
})
}
}
</script> </script>

View File

@@ -669,7 +669,7 @@ $usr_nm = session('usr_nm');
if (!rowData) return; if (!rowData) return;
const rcpt_atclno = rowData.rcpt_atclno; const rcpt_atclno = rowData.rcpt_atclno;
location.href = "<?= site_url('article/dept/detail') ?>/" + rcpt_atclno; location.href = "<?= site_url('article/receipt/detail') ?>/" + rcpt_atclno;
}); });

View File

@@ -0,0 +1,998 @@
<?= $this->extend('layouts/main') ?>
<?= $this->section('content') ?>
<?php
/**
* 문자열/JSON/배열 모두 받아서 "항목 리스트"로 정규화
* - JSON 배열 문자열: '["a","b"]'
* - 개행 문자열: "a\nb"
* - 배열: ["a","b"]
*/
function normalize_lines($v): array
{
if ($v === null)
return [];
// 이미 배열이면 그대로
if (is_array($v)) {
return array_values(array_filter(array_map('trim', $v), fn($x) => $x !== ''));
}
$s = trim((string) $v);
if ($s === '')
return [];
// JSON 배열 문자열이면 decode
if ($s[0] === '[') {
$decoded = json_decode($s, true);
if (json_last_error() === JSON_ERROR_NONE && is_array($decoded)) {
return array_values(array_filter(array_map('trim', $decoded), fn($x) => $x !== ''));
}
}
// 개행 기반 분리
$lines = preg_split("/\r\n|\r|\n/", $s);
return array_values(array_filter(array_map('trim', $lines), fn($x) => $x !== ''));
}
/**
* 상세 섹션 렌더
*/
function render_detail_section(string $title, $value): string
{
$lines = normalize_lines($value);
if (empty($lines))
return '';
$html = '<div class="detail-row">';
$html .= '<div class="detail-title">' . esc($title) . '</div>';
$html .= '<ul class="detail-list">';
foreach ($lines as $line) {
$html .= '<li>' . esc($line) . '</li>';
}
$html .= '</ul></div>';
return $html;
}
/**
* 원리금균등상환
* @param float $principal
* @param float $annualRate
* @param int $years
* @return float|int
*/
function calcEqualPaymentMonthly(float $principal, float $annualRate, int $years): float
{
$n = $years * 12;
$r = ($annualRate / 100.0) / 12.0;
if ($n <= 0)
return 0.0;
if (abs($r) < 1e-12)
return $principal / $n;
$pow = pow(1.0 + $r, $n);
return ($principal * $r * $pow) / ($pow - 1.0);
}
/**
* 원금균등상환
* @param float $principal
* @param float $annualRate
* @param int $years
* @return float|int
*/
function calcEqualPrincipalAvgMonthly(float $principal, float $annualRate, int $years): float
{
$n = $years * 12;
$r = ($annualRate / 100.0) / 12.0;
if ($n <= 0)
return 0.0;
// 이자합(원금균등): P*r*(n+1)/2 (매달 원금이 선형 감소)
$totalInterest = $principal * $r * ($n + 1) / 2.0;
$totalPayment = $principal + $totalInterest;
return $totalPayment / $n;
}
function won(float $v): string
{
return number_format((int) round($v));
}
?>
<style>
.product_list {
list-style: none;
padding: 0;
margin: 0;
display: grid;
gap: 12px;
}
.product-card {
border: 1px solid #e9ecef;
border-radius: 12px;
padding: 14px;
background: #fff;
}
.title-row {
display: flex;
flex-wrap: wrap;
gap: 8px;
align-items: center;
}
.bank_name {
font-weight: 700;
text-decoration: none;
color: #00f;
}
.product_name {
font-weight: 600;
}
.badges {
/*margin-left: auto;*/
display: flex;
gap: 6px;
flex-wrap: wrap;
}
.label_icon {
display: inline-block;
color: #555;
margin: 4px 0 0 5px;
border-radius: 15px;
-webkit-border-radius: 15px;
font-size: 11px;
line-height: 13px;
border: 1px solid #555;
padding: 2px 6px;
vertical-align: top;
font-weight: bold;
}
.rate_detail {
display: flex;
gap: 14px;
margin: 10px 0 0;
}
.rate_detail .rate {
display: flex;
gap: 6px;
align-items: baseline;
}
.rate_detail dt {
color: #6c757d;
font-size: 12px;
}
.rate_detail dd {
margin: 0;
}
.rate_text {
font-size: 16px;
color: #F58027;
}
.text_red {
font-size: 14px;
color: #F58027;
}
.product-actions {
display: flex;
gap: 8px;
margin-top: 12px;
}
.collapse-box {
margin-top: 12px;
border-top: 1px dashed #e9ecef;
padding-top: 12px;
}
.detail-grid {
display: grid;
gap: 12px;
}
.detail-row {
border: 1px solid #f1f3f5;
border-radius: 10px;
padding: 12px;
background: #fcfcfd;
}
.detail-title {
font-weight: 800;
font-size: 13px;
color: #343a40;
margin-bottom: 6px;
}
.detail-list {
margin: 0;
padding-left: 18px;
color: #495057;
}
.detail-list li {
margin: 3px 0;
}
.detail-foot {
margin-top: 10px;
font-size: 12px;
color: #868e96;
}
table {
text-align: center;
}
.rate-cell {
vertical-align: middle !important;
/* fallback */
padding: 12px 10px;
}
.rate-cell {
display: grid;
flex-direction: column;
justify-content: center;
/* 세로 가운데 */
align-items: flex-start;
/* 왼쪽 정렬 */
height: 100%;
}
.rate-cell span {
line-height: 1.2;
}
.rate-cell p {
margin: 2px 0 0;
line-height: 1.2;
}
/* ===== Owl wrapper ===== */
.owl-wrap {
position: relative;
width: 100%;
max-width: 100%;
min-width: 0;
}
/* Owl 내부 overflow/폭 안정화 */
.owl-wrap .owl-stage-outer {
overflow: hidden;
width: 100%;
max-width: 100%;
}
/* Owl 기본 nav/dots는 우리가 커스텀 컨트롤을 쓰므로 숨김 */
.product-carousel .owl-nav,
.product-carousel .owl-dots {
display: none !important;
}
/* ===== 컨트롤 바 ===== */
.owl-controls-bar {
display: flex;
align-items: center;
justify-content: center;
gap: 12px;
margin-top: 14px;
user-select: none;
}
/* 화살표 버튼 */
.owl-controls-bar .owl-btn {
width: 44px;
height: 44px;
border-radius: 999px;
border: 1px solid #dee2e6;
background: #fff;
color: #212529;
font-size: 28px;
/* 아이콘 크기 */
line-height: 1;
display: inline-flex;
align-items: center;
justify-content: center;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
cursor: pointer;
}
.owl-controls-bar .owl-btn:hover {
background: #f8f9fa;
}
.owl-controls-bar .owl-btn:active {
transform: translateY(1px);
}
/* 비활성(맨 처음/맨 끝) */
.owl-controls-bar .owl-btn.is-disabled {
opacity: 0.45;
cursor: default;
box-shadow: none;
}
/* 1/N 카운터 */
.owl-controls-bar .owl-counter {
min-width: 74px;
text-align: center;
font-weight: 700;
color: #495057;
}
/* 모바일 터치 영역 살짝 키우기 */
@media (max-width: 576px) {
.owl-controls-bar .owl-btn {
width: 50px;
height: 50px;
font-size: 30px;
}
.owl-controls-bar .owl-counter {
min-width: 84px;
}
}
</style>
<div class="d-grid">
<div class="mb-3 card">
<div class="tabs-lg-alternate card-header">
<ul class="nav nav-justified">
<li class="nav-item">
<a data-bs-toggle="tab" href="#tab-eg9-0" class="active nav-link" data-cd="all">
<div class="widget-number">전체</div>
</a>
</li>
<?php foreach ($place as $p): ?>
<li class="nav-item">
<a data-bs-toggle="tab" href="#tab-eg9-0" class="nav-link" data-cd="<?= $p['cd'] ?>">
<div class="widget-number"><?= $p['cd_nm'] ?></div>
</a>
</li>
<?php endforeach; ?>
</ul>
</div>
<div class="tab-content">
<div class="tab-pane active" id="tab-eg9-0" role="tabpanel">
<div class="card-body">
<?php
// $data: 상품 리스트 배열이라고 가정
$chunks = array_chunk($data, 5); // 5개씩 슬라이드 1장
?>
<div class="owl-wrap">
<div class="owl-carousel product-carousel">
<?php foreach ($chunks as $pageIdx => $items): ?>
<div class="item">
<ul class="product_list">
<?php foreach ($items as $d): ?>
<li class="product-card">
<div class="product-head">
<div class="title-row">
<a href="<?= esc($d['bank_url'] ?? '#') ?>" class="bank_name" target="_blank" rel="noopener">
<?= esc($d['bank'] ?? '') ?>
</a>
<span class="product_name"><?= esc($d['product'] ?? '') ?></span>
<div class="badges">
<?php if ($d['chg_yn'] == "Y"): ?>
<span class="label_icon">변동금리</span>
<?php endif; ?>
<?php if ($d['split_yn'] == "Y"): ?>
<span class="label_icon">분할상환</span>
<?php endif; ?>
</div>
</div>
<dl class="rate_detail">
<div class="rate">
<dt>최저</dt>
<dd><strong class="rate_text"><?= esc($d['min_rate'] ?? '-') ?>%</strong></dd>
</div>
<div class="rate">
<dt>최고</dt>
<dd><strong class="rate_text"><?= esc($d['max_rate'] ?? '-') ?>%</strong></dd>
</div>
<div class="rate">
<dt>평균</dt>
<dd><strong class="rate_text"><?= esc($d['avg_rate'] ?? '-') ?>%</strong></dd>
</div>
</dl>
</div>
<!-- 액션 버튼 -->
<div class="product-actions">
<button type="button" class="btn btn-sm btn-outline-secondary js-toggle"
data-target="#detail-<?= $d['seq'] ?>">
상세보기
</button>
<button type="button" class="btn btn-sm btn-outline-secondary js-toggle"
data-target="#pay-<?= $d['seq'] ?>">
월평균상환액
</button>
</div>
<!-- 상세 (접기/펼치기) -->
<div class="collapse-box" id="detail-<?= $d['seq'] ?>" hidden>
<div class="detail-grid">
<?= render_detail_section('대출부대비용', $d['extra_fee'] ?? null) ?>
<?= render_detail_section('연체이자율', $d['late_rate'] ?? null) ?>
<?= render_detail_section('가입방식', $d['join_method'] ?? null) ?>
<?= render_detail_section('중도상환수수료', $d['prepay_fee'] ?? null) ?>
<?= render_detail_section('대출한도', $d['loan_limit'] ?? null) ?>
<div class="detail-row">
<div class="detail-title">상품문의</div>
<ul class="detail-list">
<li>
<?= $d['bank'] ?> <?= $d['tel'] ?? '' ?>
</li>
<li><a href="https://m.map.naver.com/search2/search.nhn?query=<?= $d['bank'] ?>"
target="_blank">지점찾기</a>
</li>
</ul>
</div>
</div>
<?php if (!empty($d['prov_date'])): ?>
<div class="detail-foot">금융회사 최종 제공일
<?= esc($d['prov_date']) ?>.
</div>
<?php endif; ?>
</div>
<!-- 월평균상환액 -->
<div class="collapse-box" id="pay-<?= $d['seq'] ?>" hidden>
<?php
$basis = $d['pay_basis'] ?? '1억원 대출, 10년 분할 상환시';
?>
<div class="pay-box">
<div class="detail-row" style="margin-bottom: 15px;">
<ul class="detail-list">
<li>
<?= esc($basis) ?>
</li>
</ul>
</div>
<table class="table table-bordered">
<thead>
<tr>
<th rowspan="2" style="width: 30%;">금리</th>
<th colspan="2">월평균 상환액</th>
</tr>
<tr>
<th style="width: 35%;">원리금 균등상환</th>
<th style="width: 35%;">원금 균등상환</th>
</tr>
</thead>
<tbody>
<tr>
<td class="rate-cell">
<span class="text_black">당월최저</span>
<p><strong class="text_red"><?= $d['min_rate'] ?>%</strong></p>
</td>
<td>
<strong><?= won(calcEqualPaymentMonthly(100000000, $d['min_rate'], 10)) ?></strong>원
</td>
<td><strong>
<?= won(calcEqualPrincipalAvgMonthly(100000000, $d['min_rate'], 10)) ?>
</strong>원
</td>
</tr>
<tr>
<td class="rate-cell">
<span class="text_black">당월최고</span>
<p><strong class="text_red"><?= $d['max_rate'] ?>%</strong></p>
</td>
<td>
<strong><?= won(calcEqualPaymentMonthly(100000000, $d['max_rate'], 10)) ?></strong>원
</td>
<td><strong>
<?= won(calcEqualPrincipalAvgMonthly(100000000, $d['max_rate'], 10)) ?>
</strong>원
</td>
</tr>
<?php if (!empty($d['avg_rate'])): ?>
<tr>
<td class="rate-cell">
<span class="text_black">전월평균</span>
<p><strong class="text_red"><?= $d['avg_rate'] ?>%</strong></p>
</td>
<td>
<strong><?= won(calcEqualPaymentMonthly(100000000, $d['avg_rate'], 10)) ?></strong>원
</td>
<td><strong>
<?= won(calcEqualPrincipalAvgMonthly(100000000, $d['avg_rate'], 10)) ?>
</strong>원
</td>
</tr>
<?php endif; ?>
</tbody>
</table>
<div class="detail-foot">
<?php if (!empty($d['prov_date'])): ?>
<span>금융회사 최종 제공일
<?= esc($d['prov_date']) ?>
</span>
<?php endif; ?>
</div>
</div>
</div>
</li>
<?php endforeach; ?>
</ul>
</div>
<?php endforeach; ?>
</div>
<div class="owl-controls-bar">
<button type="button" class="owl-btn owl-prev" aria-label="이전"></button>
<div class="owl-counter" aria-live="polite">1 / 1</div>
<button type="button" class="owl-btn owl-next" aria-label="다음"></button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/OwlCarousel2/2.3.4/assets/owl.carousel.min.css">
<link rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/OwlCarousel2/2.3.4/assets/owl.theme.default.min.css">
<script type="text/javascript"
src="https://cdnjs.cloudflare.com/ajax/libs/OwlCarousel2/2.3.4/owl.carousel.min.js"></script>
<script type="text/javascript">
const datas = <?= json_encode($data, JSON_UNESCAPED_UNICODE); ?>;
let owlInited = false;
const esc = (s) => String(s ?? '').replace(/[&<>"']/g, m => ({
'&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#039;'
}[m]));
$(function () {
$('.nav-link').on('click', function () {
const cd = $(this).data('cd');
var arr = new Array();
rebuildByCd(cd);
// switch (cd) {
// // 전체
// case "all":
// arr = datas;
// break;
// // 은행
// case "B":
// $.each(datas, function (idx, v) {
// if (v.bank_type == "B") {
// arr.push(v);
// }
// });
// break;
// // 저축은행
// case "SB":
// $.each(datas, function (idx, v) {
// if (v.bank_type == "SB") {
// arr.push(v);
// }
// });
// break;
// // 보험
// case "I":
// $.each(datas, function (idx, v) {
// if (v.bank_type == "I") {
// arr.push(v);
// }
// });
// break;
// }
// 다시 렌더링
});
const $owl = $('.product-carousel');
initOwlOnce();
// ✅ Owl 드래그가 버튼 클릭을 먹어버리는 문제 방지
$(document).on('mousedown touchstart pointerdown', '.product-carousel .js-toggle, .product-carousel a', function (e) {
e.stopPropagation();
});
// ✅ 클릭도 가끔 막히는 케이스 방지
$(document).on('click', '.product-carousel .js-toggle', function (e) {
e.preventDefault();
e.stopPropagation();
const sel = $(this).data('target');
const $target = $(sel);
if ($target.length) {
$target.prop('hidden', !$target.prop('hidden'));
// 내용 펼치면 높이 변하니까 autoHeight 갱신
setTimeout(() => $('.product-carousel').trigger('refresh.owl.carousel'), 0);
}
});
// Bootstrap 탭 전환 완료 후 폭 재계산
$(document).on('shown.bs.tab', 'a[data-bs-toggle="tab"]', function () {
const $owl = $('.product-carousel');
if ($owl.length) {
// 숨김->보임 전환 직후엔 약간 텀 주는 게 안정적
setTimeout(() => $owl.trigger('refresh.owl.carousel'), 0);
}
});
// ===== 외부 버튼 컨트롤 =====
$('.owl-controls-bar .owl-prev').on('click', function () {
$owl.trigger('prev.owl.carousel');
});
$('.owl-controls-bar .owl-next').on('click', function () {
$owl.trigger('next.owl.carousel');
});
});
function initOwlOnce() {
if (owlInited) return;
owlInited = true;
$('.product-carousel').owlCarousel({
items: 1,
loop: false,
margin: 16,
autoHeight: true,
smartSpeed: 250,
mouseDrag: false,
touchDrag: false,
pullDrag: false,
onInitialized: updateOwlUI,
onTranslated: updateOwlUI,
onResized: updateOwlUI
});
}
// ===== 카운터 + 버튼 활성/비활성 =====
function updateOwlUI(event) {
// event가 없는 경우(수동 호출) 대비
const e = event || $owl.data('owl.carousel');
const carousel = event ? event.relatedTarget : e;
if (!carousel) return;
const total = carousel.items().length; // 총 슬라이드 수(= 5개 묶음 페이지 수)
const current = carousel.relative(carousel.current()) + 1;
$('.owl-counter').text(`${current} / ${total}`);
// 버튼 disable 처리
const $prev = $('.owl-controls-bar .owl-prev');
const $next = $('.owl-controls-bar .owl-next');
$prev.toggleClass('is-disabled', current <= 1);
$next.toggleClass('is-disabled', current >= total);
}
function chunk(arr, size) {
const out = [];
for (let i = 0; i < arr.length; i += size) out.push(arr.slice(i, i + size));
return out;
}
// ✅ 카드 HTML 생성 함수 (여기서 너의 카드 마크업을 그대로 문자열로 만들어야 함)
function renderCard(d) {
// 중요: XSS 방지 필요하면 escape 처리(최소한 텍스트는 escape)
const badges = `
${d.chg_yn === 'Y' ? `<span class="label_icon">변동금리</span>` : ``}
${d.split_yn === 'Y' ? `<span class="label_icon">분할상환</span>` : ``}
`;
// detail/pay 영역은 지금 너가 PHP 함수(render_detail_section, won...)를 쓰고 있어서
// 프론트에서 완전 동일하게 만들려면 "서버에서 HTML을 내려받는" 방식이 더 좋음.
// 일단은 카드 상단(요약)만 재렌더하고 상세는 필요 시 Ajax로 가져오는 패턴 추천.
return `
<li class="product-card">
<div class="product-head">
<div class="title-row">
<a href="${esc(d.bank_url || '#')}" class="bank_name" target="_blank" rel="noopener">${esc(d.bank)}</a>
<span class="product_name">${esc(d.product)}</span>
<div class="badges">${badges}</div>
</div>
<dl class="rate_detail">
<div class="rate"><dt>최저</dt><dd><strong class="rate_text">${esc(d.min_rate)}%</strong></dd></div>
<div class="rate"><dt>최고</dt><dd><strong class="rate_text">${esc(d.max_rate)}%</strong></dd></div>
<div class="rate"><dt>평균</dt><dd><strong class="rate_text">${esc(d.avg_rate)}%</strong></dd></div>
</dl>
</div>
<div class="product-actions">
<button type="button" class="btn btn-sm btn-outline-secondary js-toggle" data-target="#detail-${esc(d.seq)}">상세보기</button>
<button type="button" class="btn btn-sm btn-outline-secondary js-toggle" data-target="#pay-${esc(d.seq)}">월평균상환액</button>
</div>
<div class="collapse-box" id="detail-${esc(d.seq)}" hidden>
<div class="detail-grid">
${render_detail_section('대출부대비용', esc(d.extra_fee))}
${render_detail_section('연체이자율', esc(d.late_rate))}
${render_detail_section('가입방식', esc(d.join_method))}
${render_detail_section('중도상환수수료', esc(d.prepay_fee))}
${render_detail_section('대출한도', esc(d.loan_limit))}
<div class="detail-row">
<div class="detail-title">상품문의</div>
<ul class="detail-list">
<li>${esc(d.bank)}</li>
<li>
<a href="https://m.map.naver.com/search2/search.nhn?query=<?= $d['bank'] ?>" target="_blank">${esc(d.tel)}</a>
</li>
</ul>
</div>
</div>
<div class="detail-foot">
금융회사 최종 제공일 ${esc(d.prov_date)}
</div>
</div>
<div class="collapse-box" id="pay-${esc(d.seq)}" hidden>
${render_rates_section(d)}
</div>
</li>
`;
}
function renderSlides(list) {
const pages = chunk(list, 5);
if (!pages.length) {
return `<div class="item"><div class="text-center text-muted py-5">데이터가 없습니다.</div></div>`;
}
return pages.map(page => `
<div class="item">
<ul class="product_list">
${page.map(renderCard).join('')}
</ul>
</div>
`).join('');
}
function destroyOwl($owl) {
if ($owl.hasClass('owl-loaded')) {
$owl.trigger('destroy.owl.carousel');
// owl이 감싼 wrapper 제거
$owl.find('.owl-stage-outer').children().unwrap();
$owl.removeClass('owl-center owl-loaded owl-text-select-on');
$owl.find('.owl-stage').children().unwrap();
}
}
function rebuildByCd(cd) {
const filtered = (cd === 'all')
? datas
: datas.filter(x => String(x.bank_type) === String(cd));
const $owl = $('.product-carousel');
destroyOwl($owl);
$owl.html(renderSlides(filtered));
initOwl($owl);
}
function initOwl($owl) {
$owl.owlCarousel({
items: 1,
loop: false,
margin: 16,
autoHeight: true,
smartSpeed: 250,
mouseDrag: false,
touchDrag: false,
pullDrag: false,
onInitialized: updateOwlUI,
onTranslated: updateOwlUI,
onResized: updateOwlUI
});
}
function render_detail_section(title, value) {
const lines = normalize_lines(value);
if (!lines.length) return '';
str = `
<div class="detail-row">
<div class="detail-title">${title}</div>
<ul class="detail-list">`;
for (const line of lines) {
const text = Array.isArray(line) ? line.join(' ') : String(line ?? '').trim();
if (!text) continue;
str += `<li>${esc(text)}</li>`;
}
str += `
</ul>
</div>
`;
return str;
}
function render_rates_section(d) {
var basis = '1억원 대출, 10년 분할 상환시';
var str = `
<div class="pay-box">
<div class="detail-row" style="margin-bottom: 15px;">
<ul class="detail-list">
<li>
<?= esc($basis) ?>
</li>
</ul>
</div>
<table class="table table-bordered">
<thead>
<tr>
<th rowspan="2" style="width: 30%;">금리</th>
<th colspan="2">월평균 상환액</th>
</tr>
<tr>
<th style="width: 35%;">원리금 균등상환</th>
<th style="width: 35%;">원금 균등상환</th>
</tr>
</thead>
<tbody>
<tr>
<td class="rate-cell">
<span class="text_black">당월최저</span>
<p><strong class="text_red">${esc(d.min_rate)}%</strong></p>
</td>
<td>
<strong>${won(calcEqualPaymentMonthly(100000000, d.min_rate, 10))}</strong>원
</td>
<td>
<strong>${won(calcEqualPaymentMonthly(100000000, d.min_rate, 10))}</strong>원
</td>
</tr>
<tr>
<td class="rate-cell">
<span class="text_black">당월최고</span>
<p><strong class="text_red">${esc(d.max_rate)}%</strong></p>
</td>
<td>
<strong>${won(calcEqualPaymentMonthly(100000000, d.max_rate, 10))}/strong>원
</td>
<td>
<strong>${won(calcEqualPrincipalAvgMonthly(100000000, d.max_rate, 10))}</strong>원
</td>
</tr>`;
if (d.avg_rate != null) {
str += `
<tr>
<td class="rate-cell">
<span class="text_black">전월평균</span>
<p><strong class="text_red">${esc(d.avg_rate)}%</strong></p>
</td>
<td>
<strong>${won(calcEqualPaymentMonthly(100000000, d.avg_rate, 10))} ?></strong>원
</td>
<td>
<strong>${won(calcEqualPrincipalAvgMonthly(100000000, d.avg_rate, 10))}</strong>원
</td>
</tr>
`;
}
str += `
</tbody>
</table>
<div class="detail-foot">
<span>금융회사 최종 제공일 ${esc(d.prov_date)}</span>
</div>
</div>
`;
return str;
}
function normalize_lines(v) {
if (v === null || v === undefined) return [];
// 이미 배열이면
if (Array.isArray(v)) {
return v.map(x => String(x).trim()).filter(x => x !== '');
}
let s = String(v).trim();
if (!s) return [];
// JSON 배열 문자열이면
if (s.startsWith('[')) {
try {
const decoded = JSON.parse(s);
if (Array.isArray(decoded)) {
return decoded.map(x => String(x).trim()).filter(x => x !== '');
}
} catch (e) { }
}
// 개행 기준 분리
return s
.split(/\r\n|\r|\n/)
.map(x => x.trim())
.filter(x => x !== '');
}
// 원리금균등상환
function calcEqualPaymentMonthly(principal, annualRate, years) {
const n = years * 12;
const r = (annualRate / 100.0) / 12.0;
if (n <= 0) return 0.0;
if (Math.abs(r) < 1e-12) return principal / n;
const pow = Math.pow(1.0 + r, n);
return (principal * r * pow) / (pow - 1.0);
}
// 원금균등상환
function calcEqualPrincipalAvgMonthly(principal, annualRate, years) {
const n = years * 12;
const r = (annualRate / 100.0) / 12.0;
if (n <= 0) return 0.0;
// 이자합(원금균등): P * r * (n + 1) / 2
const totalInterest = principal * r * (n + 1) / 2.0;
const totalPayment = principal + totalInterest;
return totalPayment / n;
}
function won(v) {
return Math.round(v).toLocaleString('ko-KR');
}
</script>
<?= $this->endSection() ?>

View File

@@ -0,0 +1,292 @@
@charset "utf-8";
/* reset */
/* body {font-size:1rem;color:#333;line-height:1.5;letter-spacing:0.5px;padding:0;margin:0;overflow-x:hidden;-webkit-text-size-adjust:none;
font-family: -apple-system,BlinkMacSystemFont,'Malgun Gothic','맑은 고딕',helvetica,'Apple SD Gothic Neo',helvetica,'나눔바른고딕 옛한글','NanumBarunGothic YetHangul',sans-serif;}
*/
ul, li,dl,dt,dd {list-style:none;margin:0;padding:0;}
a {color:#333;text-decoration:none;}
p,h1,h2,h3,h4,h5,h6 {margin:0;}
table {width:100%;border-collapse:collapse;border-spacing:0;font-size:inherit;}
table th {font-weight:normal;}
caption {height:0;width:0;overflow:hidden;font-size:0;line-height:0;text-indent:-99999em;}
u {font-style:oblique;text-decoration:none;}
html body * {box-sizing:border-box;}
/* tab */
.tab_type01 {height:50px;border-bottom:1px solid #ddd;}
.tab_type01 ul:after {content:'';display:block;clear:both;}
.tab_type01 ul {border-left:1px solid #ddd;}
.tab_type01 ul li {float:left;}
.tab_type01 ul li a {display:block;height:49px;padding:0 5px;text-align:center;font-size:13px;color:#666;line-height:49px;border:1px solid #ddd;
border-left:none;border-bottom:none;background-color:#f1f1f1;}
.tab_type01 ul li a.active {position:relative;height:50px;line-height:48px;color:#333;background-color:#fff;}
.tab_type01 ul li a.active:before {content:'';position:absolute;top:-1px;left:-1px;right:-1px;height:2px;background-color:#222;}
.tab_type01.col_2 ul li {width:25%;}
.tab_view_box {display:none;margin-top:35px;}
.tab_view_box.block {display:block;}
.loan_rate {padding:20px;}
.inner {position:relative;}
.owl_page {text-align:center;padding:0 0 20px 0;}
.owl_page > span {display:inline-block;text-align:center;padding:0 10px;}
.owl_page .owl_prev_btn,
.owl_page .owl_next_btn {cursor:pointer;}
.owl_page .owl_counter b {color:#00f;}
/* 담보대출정보 */
.loan_rate .product_list .item {
border-top: 1px solid #ddd;
padding: 14px 18px 18px 18px;
}
.loan_rate .product_list .product_title {
font-size: 15px;
margin-bottom: 1px;
font-weight: 600;
line-height: 26px;
}
.loan_rate .product_list .product_title .bank_name {
color: #00f;
}
.loan_rate .product_list .product_title .label_icon {
display: inline-block;
color:#555;
margin: 4px 0 0 5px;
border-radius: 15px;
-webkit-border-radius: 15px;
font-size: 11px;
line-height: 13px;
border: 1px solid #555;
padding: 2px 6px;
vertical-align: top;
font-weight: bold;
}
.loan_rate .product_list .product_title .label_icon + .label_icon {
margin-left: 0;
}
.loan_rate .product_list .rate_detail {
overflow: hidden;
padding: 0 0 2px;
font-size: 13px;
line-height: 22px;
letter-spacing: -0.5px;
}
.loan_rate .product_list .rate_detail dt {
float: left;
margin: 3px 12px 0 0;
clear: both;
}
.loan_rate .product_list .rate_detail dd {
overflow: hidden;
margin-top: 3px;
}
.loan_rate .product_list .rate_detail dd.line_align {
float: left;
position: relative;
margin-right: 10px;
padding-right: 10px;
}
.loan_rate .product_list .rate_detail dd.line_align + dt {
clear: inherit;
}
.loan_rate .product_list .rate_detail dd strong.rate_text {
color: #f7901e;
}
.loan_rate .product_list .rate_detail dd.line_align:after {
content: '';
display: block;
position: absolute;
top: 5px;
right: 0;
width: 1px;
height: 13px;
background-color: #eee;
}
.loan_rate .info_detail .list_detail {
display: block;
width: 100%;
padding: 10px 0 0;
text-align: center;
overflow: hidden;
}
.loan_rate .info_detail .list_detail li {
display: block;
text-align: center;
width: 50%;
float: left;
}
.loan_rate .info_detail .list_detail li ~ li .option_btn {border-left:none;}
.loan_rate .info_detail .list_detail li._foldingWrapper.active .layer_box {display:block;}
.loan_rate .info_detail .list_detail li._foldingWrapper.active .option_btn {background-color:#fff;border-bottom:0;}
.loan_rate .info_detail .list_detail .option_btn {
display: block;
font-size:12px;
padding: 8px 0 0 0;
height: 36px;
vertical-align: middle;
color: #333;
background-color: #f2f2f2;
border: 1px solid #ddd;
border-right: 1px solid #ddd;
box-sizing: border-box;
-webkit-box-sizing: border-box;
}
.loan_rate .info_detail .list_detail .option_btn::after {
content: '';
display: inline-block;
width: 8px;
height: 5px;
background: url(../images/ico_arrow_down_gray.png) no-repeat;
margin: 5px 0 0 5px;
vertical-align: top;
background-size:8px auto;
}
.loan_rate .info_detail .list_detail ._foldingWrapper.active .option_btn::after {
transform: rotate(180deg);
}
.loan_rate .info_detail .list_detail .layer_box {
display: none;
padding: 15px 0 0 15px;
border: 1px solid #e4e5e7;
border-width: 0px 1px 1px;
width: 200%;
box-sizing: border-box;
-webkit-box-sizing: border-box;
}
.loan_rate .info_detail .list_detail li + li .layer_box {
margin-left: -100%;
padding:15px 10px 0 15px;
}
.loan_rate .info_detail .list_detail .info_box {
margin: -1px 0 0;
padding-bottom: 3px;
}
.loan_rate .info_detail .list_detail .detial_info {
overflow: hidden;
padding-right: 15px;
font-size: 13px;
line-height: 23px;
text-align: left;
}
.loan_rate .info_detail .list_detail .detial_info dt {
float: left;
color: #222;
margin: 6px 10px 0 0;
font-weight:600;
}
.loan_rate .info_detail .list_detail .detial_info dt.none_float {
float: none;
margin-top: 10px;
}
.loan_rate .info_detail .list_detail .detial_info dd {
overflow: hidden;
margin: 0 0 0 0;
}
.loan_rate .info_detail .list_detail .detial_info dt + dd {
margin-top: 6px;
}
.loan_rate .info_detail .list_detail .detial_info .none_float + dd {
margin-top: 1px;
}
.loan_rate .info_detail .list_detail .layer_box .notice_text {
text-align: left;
margin: 7px 0 0 0;
font-size: 13px;
color:#222;
line-height: 21px;
font-weight:600;
}
.loan_rate .info_detail .list_detail .layer_box .bottom_area {
margin: 10px 15px 0 0;
border-top: 1px solid #eee;
}
.loan_rate .info_detail .list_detail .layer_box .bottom_area .btn_close {
display: block;
font-size:12px;
height: 36px;
padding: 8px 0 0;
box-sizing: border-box;
-webkit-box-sizing: border-box;
text-align: center;
font-weight:600;
}
.loan_rate .info_detail .list_detail .table_box table {
width: 100%;
table-layout: fixed;
}
.loan_rate .info_detail .list_detail .table_box thead tr th {
color: #222;
font-size: 13px;
text-align: center;
background-color: #f2f2f2;
border: 1px solid #ddd;
padding: 10px 5px;
font-weight: normal;
}
.loan_rate .info_detail .list_detail .table_box tbody tr td {
font-size: 13px;
min-height: 42px;
color: #222;
vertical-align: middle;
border: 1px solid #ddd;
padding: 10px 7px;
line-height: 18px;
}
.loan_rate .info_detail .list_detail .table_box tbody tr td > span {
display: inline-block;
}
.loan_rate .info_detail .list_detail .layer_box .desp_info {
padding: 0 15px 10px 0;
font-size: 13px;
line-height: 23px;
color: #222;
text-align: left;
}
.loan_rate .info_detail .list_detail .table_box tbody tr td .text_red {
font-weight: 600;
display: block;
color: #f7901e;
}
.loan_rate .info_detail .list_detail .layer_box .sub_info dl {
overflow: hidden;
font-size: 13px;
line-height: 23px;
color: #222;
text-align: left;
}
.loan_rate .info_detail .list_detail .layer_box .notice_text + dl {
margin: 7px 0 0 0;
}
.loan_rate .info_detail .list_detail .layer_box .sub_info dl dt {
float: left;
margin: 3px 10px 0 0;
font-size:13px;
font-weight:600;
}
.loan_rate .info_detail .list_detail .layer_box .sub_info dl dd {
overflow: hidden;
margin-top: 3px;
font-weight:400;
}
.loan_rate .info_detail .list_detail .layer_box .sub_info a,
.info_detail .list_detail .detial_info dd a {
color:#00f;
}

View File

@@ -0,0 +1,6 @@
/**
* Owl Carousel v2.3.4
* Copyright 2013-2018 David Deutsch
* Licensed under: SEE LICENSE IN https://github.com/OwlCarousel2/OwlCarousel2/blob/master/LICENSE
*/
.owl-carousel,.owl-carousel .owl-item{-webkit-tap-highlight-color:transparent;position:relative}.owl-carousel{display:none;width:100%;z-index:1}.owl-carousel .owl-stage{position:relative;-ms-touch-action:pan-Y;touch-action:manipulation;-moz-backface-visibility:hidden}.owl-carousel .owl-stage:after{content:".";display:block;clear:both;visibility:hidden;line-height:0;height:0}.owl-carousel .owl-stage-outer{position:relative;overflow:hidden;-webkit-transform:translate3d(0,0,0)}.owl-carousel .owl-item,.owl-carousel .owl-wrapper{-webkit-backface-visibility:hidden;-moz-backface-visibility:hidden;-ms-backface-visibility:hidden;-webkit-transform:translate3d(0,0,0);-moz-transform:translate3d(0,0,0);-ms-transform:translate3d(0,0,0)}.owl-carousel .owl-item{min-height:1px;float:left;-webkit-backface-visibility:hidden;-webkit-touch-callout:none}.owl-carousel .owl-item img{display:block;width:100%}.owl-carousel .owl-dots.disabled,.owl-carousel .owl-nav.disabled{display:none}.no-js .owl-carousel,.owl-carousel.owl-loaded{display:block}.owl-carousel .owl-dot,.owl-carousel .owl-nav .owl-next,.owl-carousel .owl-nav .owl-prev{cursor:pointer;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.owl-carousel .owl-nav button.owl-next,.owl-carousel .owl-nav button.owl-prev,.owl-carousel button.owl-dot{background:0 0;color:inherit;border:none;padding:0!important;font:inherit}.owl-carousel.owl-loading{opacity:0;display:block}.owl-carousel.owl-hidden{opacity:0}.owl-carousel.owl-refresh .owl-item{visibility:hidden}.owl-carousel.owl-drag .owl-item{-ms-touch-action:pan-y;touch-action:pan-y;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.owl-carousel.owl-grab{cursor:move;cursor:grab}.owl-carousel.owl-rtl{direction:rtl}.owl-carousel.owl-rtl .owl-item{float:right}.owl-carousel .animated{animation-duration:1s;animation-fill-mode:both}.owl-carousel .owl-animated-in{z-index:0}.owl-carousel .owl-animated-out{z-index:1}.owl-carousel .fadeOut{animation-name:fadeOut}@keyframes fadeOut{0%{opacity:1}100%{opacity:0}}.owl-height{transition:height .5s ease-in-out}.owl-carousel .owl-item .owl-lazy{opacity:0;transition:opacity .4s ease}.owl-carousel .owl-item .owl-lazy:not([src]),.owl-carousel .owl-item .owl-lazy[src^=""]{max-height:0}.owl-carousel .owl-item img.owl-lazy{transform-style:preserve-3d}.owl-carousel .owl-video-wrapper{position:relative;height:100%;background:#000}.owl-carousel .owl-video-play-icon{position:absolute;height:80px;width:80px;left:50%;top:50%;margin-left:-40px;margin-top:-40px;background:url(owl.video.play.png) no-repeat;cursor:pointer;z-index:1;-webkit-backface-visibility:hidden;transition:transform .1s ease}.owl-carousel .owl-video-play-icon:hover{-ms-transform:scale(1.3,1.3);transform:scale(1.3,1.3)}.owl-carousel .owl-video-playing .owl-video-play-icon,.owl-carousel .owl-video-playing .owl-video-tn{display:none}.owl-carousel .owl-video-tn{opacity:0;height:100%;background-position:center center;background-repeat:no-repeat;background-size:contain;transition:opacity .4s ease}.owl-carousel .owl-video-frame{position:relative;z-index:1;height:100%;width:100%}

View File

@@ -0,0 +1,6 @@
/**
* Owl Carousel v2.3.4
* Copyright 2013-2018 David Deutsch
* Licensed under: SEE LICENSE IN https://github.com/OwlCarousel2/OwlCarousel2/blob/master/LICENSE
*/
.owl-theme .owl-dots,.owl-theme .owl-nav{text-align:center;-webkit-tap-highlight-color:transparent}.owl-theme .owl-nav{margin-top:10px}.owl-theme .owl-nav [class*=owl-]{color:#FFF;font-size:14px;margin:5px;padding:4px 7px;background:#D6D6D6;display:inline-block;cursor:pointer;border-radius:3px}.owl-theme .owl-nav [class*=owl-]:hover{background:#869791;color:#FFF;text-decoration:none}.owl-theme .owl-nav .disabled{opacity:.5;cursor:default}.owl-theme .owl-nav.disabled+.owl-dots{margin-top:10px}.owl-theme .owl-dots .owl-dot{display:inline-block;zoom:1}.owl-theme .owl-dots .owl-dot span{width:10px;height:10px;margin:5px 7px;background:#D6D6D6;display:block;-webkit-backface-visibility:visible;transition:opacity .2s ease;border-radius:30px}.owl-theme .owl-dots .owl-dot.active span,.owl-theme .owl-dots .owl-dot:hover span{background:#869791}

View File

@@ -0,0 +1,105 @@
$(document).ready(function(){
// tab
$('.tab_type01 a').click(function (e) {
var contentid = $(this).attr('title');
e.preventDefault();
$('.tab_type01 .active').removeClass('active');
$(this).addClass('active');
$('.tab_view_box').removeClass('block');
$('#' + contentid).addClass('block');
$('.owl-carousel.loan_rate_sgroup').trigger('to.owl.carousel', 0);
$('.loan_rate ._foldingWrapper').removeClass('active');
});
// content slide.
var loanTotalOwl = $('.owl-carousel.type_total');
var loanBankOwl = $('.owl-carousel.type_bank');
var loanSavingsbankOwl = $('.owl-carousel.type_savingsbank');
var loanInsuranceOwl = $('.owl-carousel.type_insurance');
loanTotalOwl.owlCarousel({
items:1,
loop: false,
nav: false,
dots: false,
mouseDrag: false,
touchDrag: false,
onInitialized : counter,
onTranslated : counter
});
loanBankOwl.owlCarousel({
items:1,
loop: false,
nav: false,
dots: false,
mouseDrag: false,
touchDrag: false,
onInitialized : counter,
onTranslated : counter
});
loanSavingsbankOwl.owlCarousel({
items:1,
loop: false,
nav: false,
dots: false,
mouseDrag: false,
touchDrag: false,
onInitialized : counter,
onTranslated : counter
});
loanInsuranceOwl.owlCarousel({
items:1,
loop: false,
nav: false,
dots: false,
mouseDrag: false,
touchDrag: false,
onInitialized : counter,
onTranslated : counter
});
$('.loan_rate .owl_prev_btn').click(function() {
$(this).closest('.tab_view_box').find('.owl-carousel.loan_rate_sgroup').trigger('prev.owl.carousel');
});
$('.loan_rate .owl_next_btn').click(function() {
$(this).closest('.tab_view_box').find('.owl-carousel.loan_rate_sgroup').trigger('next.owl.carousel');
})
function counter(event) {
var element = event.target;
var items = event.item.count;
var item = event.item.index + 1;
if(item > items) {
item = item - items
}
$(element).closest('.tab_view_box').find('.owl_counter').html("<b>" + item + "</b> / " + items);
}
// 상세보기 & 월평균상환액 tab.
$('.loan_rate ul.list_detail .option_btn').click(function () {
var isActive = $(this).parent('li._foldingWrapper').hasClass('active');
$(this).closest('ul.list_detail').find('li._foldingWrapper').removeClass('active');
if(!isActive) {
$(this).parent('li._foldingWrapper').addClass('active');
} else {
$(this).parent('li._foldingWrapper').removeClass('active');
}
});
$('.loan_rate .info_detail .bottom_area .btn_close').click(function () {
$(this).closest('ul.list_detail').find('li._foldingWrapper').removeClass('active');
});
});

7
public/plugin/js/owl.carousel.min.js vendored Normal file

File diff suppressed because one or more lines are too long