addEditor.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402
  1. <template>
  2. <div class="w-full h-full flex flex-col">
  3. <div class="flex-1 overflow-y-auto">
  4. <van-field v-model="value" label="合同所属部门" placeholder="请选择" input-align="right" readonly
  5. @click="showDepartmentSelection">
  6. <template #input v-if="departmentRow.departmentName != ''">
  7. <TranslationComponent type="departmentName" :openId="departmentRow.departmentName" />
  8. </template>
  9. </van-field>
  10. <CustomerForm ref="formFormRef" :formJson="formJson" :formValue="formVal"></CustomerForm>
  11. <template v-for="(item, index) in paymentPlanList">
  12. <FoldingPanel :title="`回款计划(${index + 1})`" class="mb-4">
  13. <template #foldingRight>
  14. <div class="flex items-center">
  15. <div class="pr-3 themeTextColor" @click="addPaymentCollection(index)">添加</div>
  16. <div class="text-[red]" @click="deletePaymentCollection(index)">删除</div>
  17. </div>
  18. </template>
  19. <template #foldContainer>
  20. <div>
  21. <van-cell title="是否已回款">
  22. <template #default>
  23. <div class="flex justify-end">
  24. <van-checkbox v-model="item.isPayed">已回款</van-checkbox>
  25. </div>
  26. </template>
  27. </van-cell>
  28. <van-cell title="回款日期" @click="showDatePicker(index, 'payDate')">
  29. <template #default>
  30. <van-field v-model="item.payDate" input-align="right" placeholder="请选择日期" class="resetStyles"
  31. readonly />
  32. </template>
  33. </van-cell>
  34. <van-cell title="金额(含税)">
  35. <template #default>
  36. <van-field v-model="item.amount" type="digit" input-align="right" placeholder="请输入金额"
  37. class="resetStyles" />
  38. </template>
  39. </van-cell>
  40. <van-cell title="是否已开票">
  41. <template #default>
  42. <div class="flex justify-end">
  43. <van-checkbox v-model="item.isBilled">已开票</van-checkbox>
  44. </div>
  45. </template>
  46. </van-cell>
  47. <van-cell title="开票日期" @click="showDatePicker(index, 'billDate')">
  48. <template #default>
  49. <van-field v-model="item.billDate" input-align="right" placeholder="请选择日期" class="resetStyles"
  50. readonly />
  51. </template>
  52. </van-cell>
  53. <van-cell title="开票种类增值税" @click="showSelectTaxAmount(index)">
  54. <template #default >
  55. <van-field v-model="item.invoiceTypeName" type="text" input-align="right" placeholder="请选择"
  56. class="resetStyles" readonly="" />
  57. </template>
  58. </van-cell>
  59. <van-cell title="税率%">
  60. <template #default>
  61. <van-field v-model="item.taxRate" type="digit" input-align="right" placeholder="请输入"
  62. class="resetStyles" @change="inputNumberChange(index)" />
  63. </template>
  64. </van-cell>
  65. <van-cell title="税额">
  66. <template #default>
  67. <van-field v-model="item.taxAmount" type="digit" input-align="right" placeholder="请输入"
  68. class="resetStyles" />
  69. </template>
  70. </van-cell>
  71. </div>
  72. </template>
  73. </FoldingPanel>
  74. </template>
  75. </div>
  76. <div class="mar-20px ">
  77. <van-button type="primary" @click="onSubmit" class="w-full">
  78. {{ Object.keys(formVal).length > 0 ? '确定修改' : '确定添加' }}
  79. </van-button>
  80. </div>
  81. <!-- 日期选择器 -->
  82. <van-popup v-model:show="showPicker" destroy-on-close position="bottom" :style="{ height: '50%' }">
  83. <van-date-picker v-model="pickerValue" @confirm="showPickerConfirm" @cancel="showPicker = false" />
  84. </van-popup>
  85. <!-- 部门选择 -->
  86. <!-- <treeSelect
  87. ref="treeSelectRef"
  88. v-model:show="departmentShow"
  89. :modelValue="departmentShow.departmentId"
  90. :listData="options"
  91. :multiple='false'
  92. placeholder="请选择"
  93. @changeModelValue="changeModelValue"
  94. :configurationItem="{ label: 'label', value: 'id', children: 'children' }"
  95. ></treeSelect> -->
  96. <van-popup v-model:show="departmentShow" destroy-on-close position="bottom" :style="{ height: '85%' }">
  97. <div class="selectDepartment">
  98. <div class="w-full absolute top-12 z-10">
  99. <van-tabs v-model:active="departmentRow.tabIndex" shrink scrollspy @click-tab="tabClick">
  100. <van-tab v-for="item in tableSelectSelectionShowList">
  101. <template #title>
  102. <TranslationComponent type="departmentName" :openId="item.label" />
  103. </template>
  104. </van-tab>
  105. </van-tabs>
  106. </div>
  107. <div class="soDept">
  108. <van-cascader v-model="departmentRow.departmentId" ref="cascaderDeptRef" title="合同所属部门" :options="deptOptions"
  109. @change="changeModelValue" @close="departmentShow = false" :field-names="fieldNames"
  110. @finish="finishModelValue">
  111. <template #option="{ option }">
  112. <div>
  113. <TranslationComponent type="departmentName" :openId="option.label" />
  114. </div>
  115. </template>
  116. </van-cascader>
  117. </div>
  118. </div>
  119. </van-popup>
  120. </div>
  121. <van-popup v-model:show="showSelectTaxAmountShow" destroy-on-close position="bottom" :style="{ height: '50%' }">
  122. <van-picker :columns="columns" @confirm="onConfirm" />
  123. </van-popup>
  124. </template>
  125. <script setup>
  126. import { ref, onActivated, watch } from 'vue';
  127. import { useLifecycle } from '@hooks/useCommon.js';
  128. import CustomerForm from '@components/common/formForm/formView.vue'
  129. import requests from "@common/requests";
  130. import useToast from "@hooks/useToast"
  131. import { CONTRACT_ADDITION_EDITING, CONTRACT_EDITING, OBTAIN_THE_CONTRACT_REMITTANCE_LIST } from "@hooks/useApi"
  132. import useRouterStore from "@store/useRouterStore.js";
  133. import FoldingPanel from '@components/common/foldingPanel.vue';
  134. import dayjs from 'dayjs';
  135. import commonUtil from "@utility/commonUtil"
  136. import { number } from 'echarts';
  137. import { routingInfos, getRouterImg } from "@utility/generalVariables.js";
  138. import TreeSelect from "@components/common/treeSelect/selectTree.vue"
  139. const router = useRouterStore()
  140. const { toastText, toastSuccess, toastFail, toastLoading } = useToast()
  141. const props = defineProps({
  142. formJson: { required: true },
  143. formValue: { required: true },
  144. });
  145. const formFormRef = ref(null)
  146. const formVal = ref({})
  147. const paymentPlanList = ref([{}])
  148. const showPicker = ref(false)
  149. const pickerValue = ref([])
  150. const rowIndex = ref(0)
  151. const departmentRow = ref({
  152. departmentName: '',
  153. departmentId: '',
  154. sureDepartmentId: '',
  155. tableSelect: ''
  156. })
  157. const departmentShow = ref(false)
  158. const deptOptions = ref([])
  159. const tableSelectSelectionShowList = ref([])
  160. const fieldNames = {
  161. text: 'label',
  162. value: 'id',
  163. children: 'children',
  164. };
  165. const cascaderDeptRef = ref(null)
  166. const dataType = ref('payDate')
  167. const showSelectTaxAmountShow = ref(false)
  168. const selectIndex = ref(0)
  169. const columns = [
  170. { text: '增值税专用发票', value: 0 },
  171. { text: '增值税普通发票', value: 1 },
  172. ]
  173. watch(() => formVal.value, (newValue) => {
  174. console.log(newValue, '<==== 看看')
  175. if (!newValue.id) {
  176. paymentPlanList.value = [{}]
  177. return
  178. }
  179. departmentRow.value = {
  180. departmentName: newValue.departmentName,
  181. departmentId: newValue.departmentId,
  182. sureDepartmentId: newValue.departmentId,
  183. tableSelect: ''
  184. }
  185. getPaymentCollectionList(newValue.id)
  186. })
  187. function inputNumberChange(index) {
  188. const { amount = 0, taxRate = 0, taxAmount = 0 } = paymentPlanList.value[index]
  189. if(!amount && !amount) {
  190. return
  191. }
  192. const shui = taxRate / 100
  193. const zhi = amount / (1 + shui) * shui
  194. paymentPlanList.value[index].taxAmount = +(zhi.toFixed(2))
  195. }
  196. function onConfirm(val) {
  197. const selectedOptions = val.selectedOptions[0]
  198. paymentPlanList.value[selectIndex.value].invoiceType = selectedOptions.value
  199. paymentPlanList.value[selectIndex.value].invoiceTypeName = selectedOptions.text
  200. showSelectTaxAmountShow.value = false
  201. }
  202. function showSelectTaxAmount(index) {
  203. selectIndex.value = index
  204. showSelectTaxAmountShow.value = true
  205. }
  206. function finishModelValue({ value, selectedOptions }) {
  207. const row = selectedOptions.find(item => item.id == value)
  208. departmentRow.value.sureDepartmentId = row.id
  209. departmentRow.value.departmentName = row.label
  210. departmentShow.value = false
  211. }
  212. function tabClick(name) {
  213. const elements = cascaderDeptRef.value.$el.querySelectorAll('[role="tab"]');
  214. elements[name.name].click()
  215. tableSelectSelectionShowList.value.splice(name.name + 1, tableSelectSelectionShowList.value.length)
  216. }
  217. function changeModelValue({ value, selectedOptions, tabIndex }) {
  218. const row = selectedOptions.find(item => item.id == value)
  219. tableSelectSelectionShowList.value.push({
  220. label: row.label,
  221. index: tabIndex,
  222. id: row.id
  223. })
  224. setTimeout(() => {
  225. departmentRow.value.tabIndex = tableSelectSelectionShowList.value.length - 1
  226. }, 200)
  227. }
  228. function showDepartmentSelection() {
  229. tableSelectSelectionShowList.value = []
  230. departmentRow.value.departmentId = ''
  231. departmentShow.value = true
  232. }
  233. function obtainDepartmentalData() {
  234. requests.post('/department/list', {}).then((res) => {
  235. deptOptions.value = res.data
  236. })
  237. }
  238. function onSubmit() {
  239. formFormRef.value.getJsonData().then((res) => {
  240. if (!res.data) {
  241. return
  242. }
  243. const determineArray = paymentPlanList.value.filter(item => item.isPayed || (item.payDate || item.amount))
  244. let totalNum = 0
  245. for (let i in determineArray) {
  246. const row = determineArray[i]
  247. if (!row.payDate) {
  248. toastText('回款日期不能为空')
  249. return
  250. }
  251. if (!row.amount || row.amount == 0) {
  252. toastText('回款金额不能为空和0')
  253. return
  254. }
  255. totalNum += Number(determineArray[i].amount)
  256. if (formVal.value.id) {
  257. determineArray[i].contractId = formVal.value.id
  258. }
  259. }
  260. if (totalNum > 0 && !res.data.amounts) {
  261. toastText('请输入合同金额')
  262. return
  263. }
  264. if (totalNum > res.data.amounts) {
  265. toastText('回款金额不能大于合同金额')
  266. return
  267. }
  268. toastLoading('保存中', 0)
  269. requests.post(props.formValue?.id ? CONTRACT_EDITING : CONTRACT_ADDITION_EDITING, { ...commonUtil.getFromValue({
  270. ...props.formValue,
  271. ...res.data,
  272. paymentListStr: JSON.stringify((determineArray || []).map(item => {
  273. delete item.invoiceTypeName
  274. return {
  275. ...item,
  276. isBilled: item.isBilled ? 1 : 0
  277. }
  278. }))}),
  279. departmentId: departmentRow.value.sureDepartmentId
  280. }).then(() => {
  281. toastSuccess('保存成功')
  282. const currentPages = router.$state.currentPages || []
  283. const parentRoute = (currentPages[currentPages.length - 2] || {}).pathName
  284. const jumpTo = routingInfos['contract']
  285. setTimeout(() => {
  286. if(parentRoute == 'home') {
  287. router.redirectTo({
  288. pathName: 'moduleList',
  289. success: () => {
  290. router.eventEmit('moduleListRefreshData', {})
  291. router.emit('moduleListDetailParameter', {
  292. row: JSON.stringify(jumpTo)
  293. })
  294. }
  295. })
  296. } else {
  297. router.navigateBack({
  298. success: () => {
  299. router.eventEmit('moduleListRefreshData', {})
  300. }
  301. })
  302. }
  303. }, 2000)
  304. // setTimeout(() => {
  305. // router.navigateBack({
  306. // success: () => {
  307. // router.eventEmit('moduleListRefreshData', {})
  308. // }
  309. // })
  310. // }, 2000)
  311. }).catch((err) => {
  312. toastFail('保存失败:' + err.msg)
  313. })
  314. })
  315. }
  316. function addPaymentCollection(index) {
  317. paymentPlanList.value.splice(index + 1, 0, { isPayed: false, payDate: dayjs(new Date()).format('YYYY-MM-DD'), amount: null, isBilled: true })
  318. }
  319. function deletePaymentCollection(index) {
  320. paymentPlanList.value.splice(index, 1)
  321. }
  322. function showDatePicker(index, type) {
  323. const { payDate, billDate } = paymentPlanList.value[index];
  324. dataType.value = type
  325. rowIndex.value = index;
  326. const dates = type == 'payDate' ? payDate : billDate
  327. const currentDate = dayjs(dates ? new Date(dates) : new Date()).format('YYYY-MM-DD').split('-');
  328. pickerValue.value = currentDate;
  329. showPicker.value = true;
  330. }
  331. function showPickerConfirm({ selectedValues }) {
  332. if (dataType.value == 'payDate') {
  333. paymentPlanList.value[rowIndex.value].payDate = selectedValues.join("-");
  334. }
  335. if (dataType.value == 'billDate') {
  336. paymentPlanList.value[rowIndex.value].billDate = selectedValues.join("-");
  337. }
  338. showPicker.value = false;
  339. }
  340. function getPaymentCollectionList(id) {
  341. requests.post(OBTAIN_THE_CONTRACT_REMITTANCE_LIST, { contractId: id }).then((res) => {
  342. paymentPlanList.value = res.data.length > 0 ? res.data : [{}]
  343. })
  344. }
  345. useLifecycle({
  346. load: () => {
  347. formVal.value = props.formValue
  348. paymentPlanList.value = [{}]
  349. obtainDepartmentalData()
  350. },
  351. init: () => {
  352. formVal.value = props.formValue
  353. paymentPlanList.value = [{}]
  354. obtainDepartmentalData()
  355. }
  356. });
  357. onActivated(() => {
  358. })
  359. </script>
  360. <style lang='scss' scoped>
  361. /* 样式代码 */
  362. .resetStyles {
  363. padding: 0;
  364. }
  365. .soDept {
  366. position: relative;
  367. :deep(.van-tabs__nav--complete) {
  368. display: none;
  369. }
  370. }
  371. </style>