documentaryRecord.vue 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615
  1. <template>
  2. <PageMain class="vh100">
  3. <el-form v-model="form" label-width="100">
  4. <el-row :gutter="20">
  5. <el-col :span="8">
  6. <el-form-item prop="recordId" label="记录编号">
  7. <el-input v-model="form.recordId"></el-input>
  8. </el-form-item>
  9. </el-col>
  10. <el-col :span="8">
  11. <el-form-item prop="invoiceNo" label="发票号">
  12. <el-input v-model="form.invoiceNo"></el-input>
  13. </el-form-item>
  14. </el-col>
  15. <el-col :span="8">
  16. <el-form-item prop="companyId" label="保险公司">
  17. <el-select v-model="form.companyId" placeholder="请选择" clearable filterable>
  18. <el-option v-for="item in companyList" :key="item.id" :value="item.id" :label="item.name"></el-option>
  19. </el-select>
  20. </el-form-item>
  21. </el-col>
  22. <el-col :span="8">
  23. <el-form-item prop="invoiceParty" label="开票方">
  24. <el-select v-model="form.invoiceParty" clearable>
  25. <el-option v-for="item in optionObj['invoice_party']" :key="item.value" :value="item.value"
  26. :label="item.label"></el-option>
  27. </el-select>
  28. </el-form-item>
  29. </el-col>
  30. <el-col :span="8">
  31. <el-form-item prop="accountsReceivable" label="应收项">
  32. <el-select v-model="form.accountsReceivable" clearable>
  33. <el-option v-for="item in optionObj['invoice_risk_type']" :key="item.value" :value="item.value"
  34. :label="item.label"></el-option>
  35. </el-select>
  36. </el-form-item>
  37. </el-col>
  38. <el-col :span="8">
  39. <el-form-item prop="status" label="结算状态">
  40. <el-select v-model="form.settlementStatus" clearable>
  41. <el-option v-for="item in optionObj['settlement_status']" :key="item.value" :value="item.value"
  42. :label="item.label"></el-option>
  43. </el-select>
  44. </el-form-item>
  45. </el-col>
  46. <el-col :span="8">
  47. <el-form-item prop="invoicePerson" label="开票人">
  48. <el-input v-model="form.invoicePerson"></el-input>
  49. </el-form-item>
  50. </el-col>
  51. <el-col :span="8">
  52. <el-form-item prop="invoiceDate" label="开票时间">
  53. <el-date-picker type="daterange" v-model="form.invoiceDate" value-format="YYYY-MM-DD HH:mm:ss"
  54. format="YYYY-MM-DD HH:mm:ss" />
  55. </el-form-item>
  56. </el-col>
  57. <el-col :span="8">
  58. <el-form-item label="保险分支公司">
  59. <el-cascader filterable clearable placeholder="请选择" v-model="form.partnerCompanyId" :options="companyList1"
  60. :props="companyIdprops" style="width: 100%;" @change="handleCompanyChange" />
  61. </el-form-item>
  62. </el-col>
  63. <el-col :span="8" style="display: flex; align-items: start; justify-content: flex-end">
  64. <el-button type="primary" @click="search">查询</el-button>
  65. <el-button plain type="primary" @click="reset">重置</el-button>
  66. </el-col>
  67. </el-row>
  68. </el-form>
  69. <el-table :data="tableData" style="height: calc(100% - 192px)" show-summary :summary-method="getSummaries">
  70. <el-table-column prop="id" label="开票记录编号" width="200"></el-table-column>
  71. <el-table-column prop="companyAllName" label="保险公司" width="100"></el-table-column>
  72. <el-table-column prop="partnerCompanyName" label="支公司" width="350" align="center"></el-table-column>
  73. <el-table-column prop="companyNameSimple" label="保险机构" width="150"></el-table-column>
  74. <el-table-column prop="invoiceNo" label="发票号" width="150"></el-table-column>
  75. <el-table-column prop="year" label="年份" width="150"></el-table-column>
  76. <el-table-column prop="month" label="月份" width="150"></el-table-column>
  77. <el-table-column prop="invoiceParty" label="开票方" width="150"></el-table-column>
  78. <el-table-column prop="remainSettlementAmount" label="未结算金额" width="100"></el-table-column>
  79. <el-table-column prop="accountsReceivable" label="应收项" width="100"></el-table-column>
  80. <el-table-column prop="receivableAmount" label="应收金额" width="120"></el-table-column>
  81. <el-table-column prop="settlementPaymentReason" label="差额原因" width="120"></el-table-column>
  82. <el-table-column prop="taxPoint" label="税点" width="120"></el-table-column>
  83. <el-table-column prop="settlementPaymentDifference" label="回款差额" width="120"></el-table-column>
  84. <el-table-column prop="receivableSupervisePremium" label="开票金额" width="120"></el-table-column>
  85. <el-table-column prop="settlementStatus" label="结算状态" width="120">
  86. </el-table-column>
  87. <el-table-column prop="settlementAmount" label="已结算金额" width="120"></el-table-column>
  88. <el-table-column prop="createBy" label="开票人" width="120"></el-table-column>
  89. <el-table-column prop="invoiceTime" label="开票时间" width="150"></el-table-column>
  90. <el-table-column label="操作" width="300" fixed="right" align="center">
  91. <template #default="scope">
  92. <div class="flex a-c ">
  93. <el-button link type="primary" :disabled="scope.row.settlementStatus == '已结算'"
  94. @click="handleCount(scope.row)">结算</el-button>
  95. <el-button link type="primary" :disabled="scope.row.settlementStatus == '已结算'"
  96. @click="handleDelete(scope.row)">删除</el-button>
  97. <el-button link type="primary" :disabled="scope.row.settlementStatus == '已结算'"
  98. @click="handleSettlement(scope.row)">标识结清</el-button>
  99. <el-dropdown placement="bottom-start" trigger="click">
  100. <span class="el-dropdown-link">
  101. 明细查询
  102. </span>
  103. <template #dropdown>
  104. <el-dropdown-menu>
  105. <el-dropdown-item @click="handleCountRecord(scope.row)">结算记录</el-dropdown-item>
  106. </el-dropdown-menu>
  107. </template>
  108. </el-dropdown>
  109. </div>
  110. </template>
  111. </el-table-column>
  112. </el-table>
  113. <div class="pagination">
  114. <el-pagination v-model:current-page="pages" v-model:page-size="size" :page-sizes="[10, 20, 30, 40]"
  115. layout="total, sizes, prev, pager, next, jumper" :total="total" @size-change="handleSizeChange"
  116. @current-change="handleCurrentChange"></el-pagination>
  117. </div>
  118. <el-dialog :close-on-click-modal="false" v-model="countShow" width="600" title="结算">
  119. <el-form ref="CountForm" :model="countForm" :rules="countRules" label-width="120">
  120. <el-form-item prop="invoiceParty" label="开票方">
  121. <el-input v-model="countForm.invoiceParty" disabled placeholder="请输入"></el-input>
  122. </el-form-item>
  123. <el-form-item prop="receivableAmount" label="应收金额">
  124. <el-input v-model="countForm.receivableAmount" disabled placeholder="请输入"></el-input>
  125. </el-form-item>
  126. <!-- <el-form-item prop="overinflatedAmount" label="虚增金额">
  127. <el-input v-model="countForm.overinflatedAmount" disabled placeholder="请输入"></el-input>
  128. </el-form-item> -->
  129. <el-form-item prop="remainSettlementAmount" label="未结算金额">
  130. <el-input v-model="countForm.remainSettlementAmount" disabled placeholder="请输入"></el-input>
  131. </el-form-item>
  132. <el-form-item prop="taxPoint" label="税点">
  133. <el-input v-model="countForm.taxPoint" disabled placeholder="请输入"></el-input>
  134. </el-form-item>
  135. <el-form-item prop="receivableSupervisePremium" label="开票金额">
  136. <el-input v-model="countForm.receivableSupervisePremium" disabled placeholder="请输入"></el-input>
  137. </el-form-item>
  138. <el-form-item prop="actualReceivedAmount" label="实收金额">
  139. <el-input-number v-model="countForm.actualReceivedAmount" style="width: 100%;" :min="0" :max="100000000"
  140. :precision="2" controls-position="right" @change="handleChange()" />
  141. </el-form-item>
  142. <el-form-item label="回款差额" v-if="countForm.actualReceivedAmount >= countForm.remainSettlementAmount">
  143. <el-input v-model="countForm.settlementPaymentDifference" disabled placeholder="自动计算" />
  144. </el-form-item>
  145. <el-form-item prop="settlementPaymentReason" label="差额原因"
  146. v-if="countForm.actualReceivedAmount > countForm.remainSettlementAmount">
  147. <el-input v-model="countForm.settlementPaymentReason" placeholder="请输入"></el-input>
  148. </el-form-item>
  149. <el-form-item prop="receivePaymentDate" label="收款日期">
  150. <el-date-picker v-model="countForm.receivePaymentDate" type="date" value-format="YYYY-MM-DD" />
  151. </el-form-item>
  152. <el-form-item prop="attachmentIds" label="收款凭证" class="upload-btn">
  153. <el-upload style="margin: 0 10px 10px 0" :auto-upload="false" multiple v-if="!countForm.h"
  154. :show-file-list="false" accept=".png, .jpg, .jpeg" @change="uploadImage" drag>
  155. <el-button size="large" icon="Plus" />
  156. </el-upload>
  157. <div class="imageDiv" v-for="(item, index) in imageUrl" :key="item">
  158. <img :src="item" style="width: 80px; height: 80px;" />
  159. <div class="model">
  160. <span class="icon-view" @click="handelViewImage(item, index)">
  161. <el-icon>
  162. <View />
  163. </el-icon>
  164. </span>
  165. <span class="icon-del" @click="handelDelImage(item, index)">
  166. <el-icon>
  167. <Delete />
  168. </el-icon>
  169. </span>
  170. </div>
  171. </div>
  172. </el-form-item>
  173. </el-form>
  174. <template #footer>
  175. <el-button class="border-[#0083FF] color-[#0083FF]" plain @click="handelCancel">取消</el-button>
  176. <el-button type="primary" @click="handelComfirm(CountForm)">确定</el-button>
  177. </template>
  178. </el-dialog>
  179. </PageMain>
  180. <el-image-viewer v-if="imageShow" :url-list="[bigImageUrl]" @close="imageShow = false" />
  181. <el-dialog v-model="handleSettlementShow" title="标识结清" width="500">
  182. <div>
  183. <el-form label-width="auto" style="max-width: 600px">
  184. <el-form-item label="差额原因">
  185. <el-input v-model="settlementPaymentReason" style="width: 100%;" type="textarea" />
  186. </el-form-item>
  187. </el-form>
  188. </div>
  189. <template #footer>
  190. <div class="dialog-footer">
  191. <el-button type="primary" @click="settlementPaymentComfirm()">
  192. 确定
  193. </el-button>
  194. </div>
  195. </template>
  196. </el-dialog>
  197. </template>
  198. <script setup lang="ts">
  199. import api from "@/api";
  200. import { useRoute, useRouter } from 'vue-router'
  201. import { ElMessage, ElMessageBox } from 'element-plus'
  202. const route = useRoute()
  203. const router = useRouter()
  204. let form = ref({})
  205. let tableData = ref([])
  206. let pages = ref(1)
  207. let size = ref(10)
  208. let total = ref(0)
  209. const handleSizeChange = (size) => {
  210. size.value = size
  211. initList()
  212. }
  213. const handleCurrentChange = (page) => {
  214. pages.value = page
  215. initList()
  216. }
  217. const companyIdprops = {
  218. expandTrigger: 'hover' as const,
  219. value: 'id',
  220. label: 'name',
  221. checkStrictly: true,
  222. }
  223. const search = () => {
  224. pages.value = 1
  225. size.value = 10
  226. initList()
  227. }
  228. const reset = () => {
  229. form.value = {
  230. recordId: '',
  231. companyId: '',
  232. invoiceParty: '',
  233. accountsReceivable: '',
  234. settlementStatus: '',
  235. invoicePerson: '',
  236. invoiceDate: []
  237. }
  238. pages.value = 1
  239. size.value = 10
  240. initList()
  241. }
  242. const invoiceId = route.query.invoiceId
  243. if (invoiceId) {
  244. form.value.recordId = invoiceId
  245. }
  246. let countShow = ref(false)
  247. let countForm = ref({})
  248. let countRules = ref({
  249. actualReceivedAmount: [{ required: true, trigger: "blur", message: "请输入" }],
  250. receivePaymentDate: [{ required: true, trigger: "change", message: "请选择" }],
  251. attachmentIds: [{ required: true, message: "请上传" }]
  252. })
  253. let imageUrl = ref([])
  254. let attachmentIds = ref([])
  255. let invoicingId = ref('')
  256. const handleCount = (row) => {
  257. countShow.value = true
  258. imageUrl.value = []
  259. attachmentIds.value = []
  260. invoicingId.value = row.id
  261. countForm.value = {
  262. invoiceParty: row.invoiceParty,
  263. remainSettlementAmount: row.remainSettlementAmount,
  264. receivableAmount: row.receivableAmount,
  265. receivableSupervisePremium: row.receivableSupervisePremium,
  266. overinflatedAmount: row.overinflatedAmount,
  267. taxPoint: row.taxPoint,
  268. }
  269. }
  270. const uploadImage = async (file) => {
  271. let files = file.raw
  272. const params = new FormData(); // 声明formData数据类型
  273. params.append("file", files);
  274. params.append("type", "image");
  275. const { code, data, msg } = await api.commonApi.fileUpload(params)
  276. if (code == 200) {
  277. imageUrl.value.push(data.downloadUrl)
  278. attachmentIds.value.push(data.id)
  279. }
  280. }
  281. /** 计算实收金额与应收监管保费的差值,并将结果赋值给付款差额字段 */
  282. const handleChange = async () => {
  283. if (countForm.value.actualReceivedAmount > countForm.value.remainSettlementAmount) {
  284. const num = await calSub(countForm.value.actualReceivedAmount, countForm.value.receivableSupervisePremium)
  285. countForm.value.settlementPaymentDifference = num
  286. } else {
  287. countForm.value.settlementPaymentDifference = 0
  288. }
  289. }
  290. /** 计算应收监管保费差额并更新虚增金额 */
  291. /**
  292. * 计算两个数值的差值(被减数 - 减数)
  293. * @param minuend 被减数
  294. * @param subtrahend 减数
  295. * @returns 差值,若接口无数据则返回 0
  296. */
  297. const calSub = async (minuend: number = 0, subtrahend: number) => {
  298. const res = await api.financeApi.calSub({ minuend, subtrahend })
  299. return res.data || 0
  300. }
  301. // 标识结清
  302. let settlementPaymentReason = ref('')
  303. const handleSettlementShow = ref(false)
  304. const id = ref('')
  305. const handleSettlement = (row: any) => {
  306. id.value = row.id
  307. handleSettlementShow.value = true
  308. }
  309. // 删除
  310. const handleDelete = (row) => {
  311. ElMessageBox.confirm(
  312. '确定要删除吗',
  313. '删除发票',
  314. {
  315. confirmButtonText: '确定',
  316. type: 'error',
  317. center: true,
  318. confirmButtonClass: 'el-button--danger',
  319. }
  320. )
  321. .then(() => {
  322. api.financeApi.deleteFollowInvoiceById(row.id).then((res: any) => {
  323. if (res.code == 200) {
  324. reset()
  325. ElMessage({
  326. message: res.msg,
  327. type: 'success'
  328. })
  329. } else {
  330. }
  331. })
  332. })
  333. .catch(() => {
  334. })
  335. }
  336. // 结算记录查询
  337. const handleCountRecord = (row) => {
  338. router.push({
  339. path: '/receivable/documentary/documentaryReport',
  340. query: {
  341. activeName: '2',
  342. id: row.id
  343. }
  344. })
  345. }
  346. // 开票明细查询
  347. const handleInvoiceDetail = (row) => {
  348. router.push({
  349. path: '/receivable/documentary/documentaryReport',
  350. query: {
  351. activeName: '3',
  352. id: row.id
  353. }
  354. })
  355. }
  356. // 列表
  357. const initList = () => {
  358. api.invoiceRecordApi.invoiceRecord({
  359. pages: pages.value,
  360. size: size.value,
  361. invoiceTypes: ['2', '4'],
  362. ...form.value
  363. }).then((res: any) => {
  364. if (res.code == 200) {
  365. tableData.value = res.data.records
  366. total.value = res.data.total
  367. }
  368. })
  369. }
  370. // 保险公司
  371. let companyList = ref([])
  372. let companyList1 = ref([])
  373. const initCompany = () => {
  374. api.insuranceApi.leftList('').then((res: any) => {
  375. if (res.code == 200) {
  376. companyList.value = res.data
  377. }
  378. })
  379. }
  380. const initCompany1 = () => {
  381. api.agreementApi.findTree({ name: "", systemCode: localStorage.getItem('login_system') }).then((res: any) => {
  382. if (res.code == 200) {
  383. companyList1.value = res.data
  384. }
  385. })
  386. }
  387. const handleCompanyChange = (e) => {
  388. console.log(e)
  389. form.value.partnerCompanyId = e[e.length - 1] || ''
  390. }
  391. let imageShow = ref(false)
  392. let bigImageUrl = ref('')
  393. const handelViewImage = (item, index) => {
  394. bigImageUrl.value = item
  395. imageShow.value = true
  396. }
  397. const handelDelImage = (item, index) => {
  398. imageUrl.value.splice(index, 1)
  399. attachmentIds.value.splice(index, 1)
  400. }
  401. let CountForm = ref()
  402. const handelComfirm = (CountForm) => {
  403. countForm.value.attachmentIds = attachmentIds.value
  404. if (!CountForm) return
  405. CountForm.validate((valid) => {
  406. if (valid) {
  407. api.financeApi.followSettlement({
  408. invoicingId: invoicingId.value,
  409. ...countForm.value,
  410. }).then((res: any) => {
  411. countShow.value = false
  412. if (res.code == 200) {
  413. reset()
  414. ElMessage({
  415. message: res.msg,
  416. type: 'success'
  417. })
  418. }
  419. })
  420. }
  421. })
  422. }
  423. // 标识结清确认
  424. const settlementPaymentComfirm = () => {
  425. if (!settlementPaymentReason.value) {
  426. ElMessage({
  427. message: '请输入差额原因',
  428. type: 'warning'
  429. })
  430. return
  431. }
  432. api.financeApi.updateSettlementStatus({ invoiceId: id.value, settlementPaymentReason: settlementPaymentReason.value }).then((res: any) => {
  433. if (res.code == 200) {
  434. ElMessage({
  435. message: res.msg,
  436. type: 'success'
  437. })
  438. handleSettlementShow.value = false
  439. initList()
  440. } else {
  441. }
  442. })
  443. }
  444. const handelCancel = () => {
  445. countShow.value = false
  446. countForm.value = {
  447. invoiceParty: '',
  448. receivableSupervisePremium: '',
  449. overinflatedAmount: '',
  450. taxPoint: '',
  451. actualReceivedAmount: '',
  452. receivePaymentDate: [],
  453. attachmentIds: []
  454. }
  455. imageUrl.value = []
  456. }
  457. const optionObj = ref({})
  458. const initOption = () => {
  459. let list = ['invoice_risk_type', 'invoice_party', 'settlement_status']
  460. list.forEach(item => {
  461. api.additionalApi.dictDetails(item).then((res: any) => {
  462. if (res.code == 200) {
  463. if (res.data) {
  464. optionObj.value[item] = res.data
  465. }
  466. }
  467. })
  468. })
  469. }
  470. const getSummaries = (param) => {
  471. const { columns, data } = param
  472. const sums: (string | VNode)[] = []
  473. // 指定需要计算的字段列表(可根据需求添加字段名)
  474. const specifiedFields = ['taxPoint', 'receivableAmount'] // 替换为您需要的字段名
  475. columns.forEach((column, index) => {
  476. if (index === 0) {
  477. sums[index] = h('div', { style: { textDecoration: 'underline' } }, [
  478. '',
  479. ])
  480. return
  481. }
  482. // 计算数字类型字段和指定字段
  483. const values = data.map((item) => {
  484. const val = item[column.property]
  485. // 排除空值
  486. if (val === '' || val === null || val === undefined) {
  487. return NaN
  488. }
  489. // 如果是数字类型,直接参与计算
  490. if (typeof val === 'number') {
  491. return val
  492. }
  493. // 如果字段在指定列表中,尝试转换为数字
  494. if (specifiedFields.includes(column.property)) {
  495. const num = Number(val)
  496. return Number.isNaN(num) ? NaN : num
  497. }
  498. // 其他情况不参与计算
  499. return NaN
  500. })
  501. if (!values.every((value) => Number.isNaN(value))) {
  502. const total = values.reduce((prev, curr) => {
  503. if (!Number.isNaN(curr)) {
  504. return prev + curr
  505. } else {
  506. return prev
  507. }
  508. }, 0)
  509. // 保留五位小数
  510. sums[index] = ` ${total.toFixed(5)}`
  511. } else {
  512. sums[index] = '-'
  513. }
  514. })
  515. return sums
  516. }
  517. onMounted(() => {
  518. if (route.query.invoiceType) {
  519. form.value.partnerCompanyId = route.query.partnerCompanyId || ''
  520. if (route.query.invoiceType == '2') {
  521. form.value.accountsReceivable = '10'
  522. } else {
  523. form.value.accountsReceivable = '20'
  524. }
  525. }
  526. initList();
  527. initOption();
  528. initCompany();
  529. initCompany1();
  530. });
  531. </script>
  532. <style scoped lang="scss">
  533. .pagination {
  534. margin-top: 10px;
  535. display: flex;
  536. justify-content: flex-end;
  537. }
  538. .upload-btn {
  539. :deep(.el-button) {
  540. width: 80px;
  541. height: 80px;
  542. font-size: 30px;
  543. color: #ccc;
  544. }
  545. }
  546. :deep(.el-dropdown-link) {
  547. display: flex;
  548. align-items: center;
  549. margin-left: 15px;
  550. color: var(--el-color-primary);
  551. cursor: pointer;
  552. }
  553. .imageDiv {
  554. position: relative;
  555. width: 80px;
  556. height: 80px;
  557. margin: 0 10px 10px 0;
  558. &:hover {
  559. .model {
  560. display: flex;
  561. align-items: center;
  562. justify-content: center;
  563. background-color: rgba(0, 0, 0, .3);
  564. }
  565. }
  566. img {
  567. display: block;
  568. width: 100%;
  569. height: 100%;
  570. border-radius: 4px;
  571. }
  572. .model {
  573. position: absolute;
  574. width: 80px;
  575. height: 80px;
  576. border-radius: 4px;
  577. display: none;
  578. top: 0;
  579. left: 0;
  580. .icon-view,
  581. .icon-del {
  582. color: #FFFFFF;
  583. margin: 0 10px;
  584. cursor: pointer;
  585. }
  586. }
  587. }
  588. :deep(.el-upload-dragger) {
  589. padding: unset;
  590. border: unset;
  591. border-radius: unset;
  592. }
  593. </style>