Skouter mortgage estimates. Web application with view written in PHP and Vue, but controller and models in Go.
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. <template>
  2. <div id="new" class="page">
  3. <h2>New Loan</h2>
  4. <section class="loans-list">
  5. <h3 v-for="(l, indx) in loans"
  6. :class="sel == indx ? 'sel' : ''"
  7. @click="() => sel = indx"
  8. >
  9. {{l.title}}
  10. </h3>
  11. <div class="add">
  12. <svg @click="create"
  13. xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor"
  14. class="bi bi-plus" viewBox="0 0 16 16"> <path d="M8 4a.5.5 0 0 1 .5.5v3h3a.5.5 0
  15. 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3A.5.5 0 0 1 8 4z"/> </svg>
  16. </div>
  17. </section>
  18. <loan-details v-if="hash == '#new'"
  19. :estimate="estimate"
  20. :loans="estimate.loans"
  21. :sel="sel"
  22. @update:name="(name) => loans[sel].title = name"
  23. @del="del"
  24. @update:borrowers="(b) => estimate.borrowers = b"
  25. @update:creditScore="(c) => estimate.creditScore = c"
  26. @update:mIncome="(m) => estimate.mIncome = m"
  27. @update:transaction="(t) => estimate.transaction = t"
  28. @update:price="(p) => estimate.price = p"
  29. @update:property="(p) => estimate.property = p"
  30. @update:loanType="(lt) => loans[sel].type = lt"
  31. @update:term="(lt) => loans[sel].term = lt"
  32. @update:program="(p) => loans[sel].program = p"
  33. @update:ltv="setLtv"
  34. @update:amount="setAmount"
  35. @update:housingDti="setHousingDti"
  36. @update:dti="setDti"
  37. @update:hoa="(hoa) => loans[sel].hoa = hoa"
  38. @update:interest="(i) => loans[sel].interest = i"
  39. @update:interestDays="(d) => loans[sel].interestDays = d"
  40. @update:hazardEscrow="(h) => loans[sel].hazardEscrow = h"
  41. @update:hazard="(h) => loans[sel].hazard = h"
  42. @update:taxEscrow="(t) => loans[sel].taxEscrow = t"
  43. @update:tax="(t) => loans[sel].tax = t"
  44. @continue="generate"
  45. />
  46. <loan-summary v-if="hash == '#new/summary'"
  47. :estimate="estimate"
  48. :loans="estimate.loans"
  49. :sel="sel" />
  50. </div>
  51. </template>
  52. <script>
  53. import { stripLetters, strip, stripInt, stripPerc } from "../../helpers.js"
  54. import LoanDetails from "./details.vue"
  55. import LoanSummary from "./summary.vue"
  56. // The default values of a new estimate
  57. const example = {
  58. title: "Example",
  59. type: "",
  60. term: 0,
  61. ltv: 0, // Loan to home value ratio
  62. dti: 0,
  63. housingDti: 0,
  64. amount: 0,
  65. interest: 0,
  66. interestDays: 0,
  67. hazard: 0, // Hazard insurance monthly payment
  68. hazardEscrow: 0, // Hazard insurance escrow in months (0 is none)
  69. tax: 0, // Real Estate taxes monthly payment
  70. taxEscrow: 0, // Months to escrow (0 is none)
  71. hoa: 100, // Home owner's association monthly fee
  72. program: "",
  73. pud: true, // Property under development
  74. zip: '',
  75. fees: [],
  76. mi: {}
  77. }
  78. // The default loans on a new estimate
  79. const loans = [
  80. Object.assign({}, example,),
  81. Object.assign(
  82. Object.assign({}, example),
  83. {title: "Another One",}
  84. ),
  85. ]
  86. // Default estimate fields
  87. const estimate = {
  88. property: "",
  89. transaction: 0,
  90. price: 0,
  91. borrowers: 0,
  92. creditScore: 0,
  93. mIncome: 0,
  94. loans: loans,
  95. }
  96. // Clone loan from initial example as a new loan
  97. function create() {
  98. this.estimate.loans.push(
  99. Object.assign({}, example, {fees: this.createFees()})
  100. )
  101. }
  102. function createFees() {
  103. return this.fees.map(f => Object.assign({}, f))
  104. }
  105. function del() {
  106. if (this.loans.length > 1) {
  107. let x = this.sel
  108. this.sel = 0
  109. this.loans.splice(x, 1)
  110. }
  111. }
  112. // Updates the property price for all loans and their fee amounts.
  113. function setPrice(value) {
  114. this.estimate.price = value
  115. this.estimate.loans[this.sel].fees.forEach(fee => {
  116. if (fee.perc) fee.amount = (fee.perc / 100 * value).toFixed(2)
  117. })
  118. this.estimate.loans.forEach(l => {l.ltv = 0; l.amount = 0})
  119. }
  120. // Changes loan.ltv's <input> and data() values, then syncs with data.amount
  121. function setLtv(e) {
  122. let ltv = strip(e)
  123. let loan = this.loans[this.sel]
  124. if (!this.estimate.price) return
  125. if (ltv > 100) ltv = 100
  126. if (ltv < 0) ltv = 0
  127. loan.ltv = ltv
  128. loan.amount = (ltv / 100 * this.estimate.price).toFixed(2)
  129. }
  130. // Changes loan.amount\'s <input> and data() values, then syncs with data.ltv
  131. function setAmount(e) {
  132. let amount = strip(e)
  133. let loan = this.loans[this.sel]
  134. if (!this.estimate.price) return
  135. if (amount > loan.price) amount = loan.price
  136. if (amount < 0) amount = 0
  137. loan.amount = amount
  138. loan.ltv = (amount / this.estimate.price * 100).toFixed(2)
  139. }
  140. function setDti(e) {
  141. let dti = strip(e)
  142. let loan = this.loans[this.sel]
  143. if (!loan.price) return
  144. if (dti > 100) dti = 100
  145. if (dti < 0) dti = 0
  146. e.target.value = dti
  147. loan.dti = dti
  148. }
  149. function setHousingDti(e) {
  150. let housingDti = strip(e)
  151. let loan = this.loans[this.sel]
  152. if (!loan.price) return
  153. if (housingDti > 100) housingDti = 100
  154. if (housingDti < 0) housingDti = 0
  155. e.target.value = housingDti
  156. loan.housingDti = housingDti
  157. }
  158. function generate() {
  159. window.location.hash = 'new/summary'
  160. }
  161. // Percentage values of fees always takek precedent over amounts. The conversion
  162. // happens in setPrice()
  163. export default {
  164. components: { LoanSummary, LoanDetails },
  165. methods: {
  166. generate, createFees, del, create, setPrice, setLtv, setAmount,
  167. setDti, setHousingDti
  168. },
  169. props: ['user', 'fees'],
  170. data() {
  171. return {
  172. estimate: estimate,
  173. loans: estimate.loans,
  174. sel: 0,
  175. errors: [],
  176. hash: window.location.hash
  177. }
  178. },
  179. created() {
  180. this.estimate.loans.forEach(l => l.fees = this.createFees())
  181. window.addEventListener("hashchange", () => this.hash = window.location.hash)
  182. }
  183. }
  184. </script>