ProcessForm.vue 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536
  1. <template>
  2. <el-form
  3. ref="formRef"
  4. :model="dataForm"
  5. label-width="110px"
  6. :rules="dataRule"
  7. class="bed-change-form"
  8. :toggleType="isDetail"
  9. >
  10. <div class="info-wrap" v-if="!isDetail">
  11. <el-row :gutter="20">
  12. <el-col :span="12" :xs="24">
  13. <el-form-item label="长者姓名" prop="elderlyId">
  14. <SelectElder v-model="dataForm.elderlyId" @elder="handleSelectElder" type="1" :tId="dataForm.tenantId"/>
  15. </el-form-item>
  16. </el-col>
  17. <el-col :span="12" :xs="24">
  18. <el-form-item label="证件号码" prop="idCard">
  19. <TgInput v-model="dataForm.idCard" disabled />
  20. </el-form-item>
  21. </el-col>
  22. <el-col :span="12" :xs="24">
  23. <el-form-item label="原床位" prop="originalName">
  24. <TgInput v-model="dataForm.originalName" disabled />
  25. </el-form-item>
  26. </el-col>
  27. <el-col :span="12" :xs="24">
  28. <el-form-item label="原床位类型" prop="overheadChargeId">
  29. <TgSelect
  30. v-model="dataForm.overheadChargeId"
  31. placeholder="请选择"
  32. disabled
  33. :list="bedList"
  34. dict-label="chargeName"
  35. dict-value="id"
  36. >
  37. <el-option v-for="t in bedList" :key="t.id" :label="t.chargeName" :value="t.id" />
  38. </TgSelect>
  39. </el-form-item>
  40. </el-col>
  41. <el-col :span="24" :xs="24">
  42. <el-col :span="12" :xs="24">
  43. <el-form-item label="变更生效日期" prop="changeDate">
  44. <!-- :disabledDate="(arg) => disabledDate(arg)" -->
  45. <TgDatePicker v-model="dataForm.changeDate" type="date" placeholder="变更生效日期" />
  46. </el-form-item>
  47. </el-col>
  48. </el-col>
  49. <el-col :span="12" :xs="24">
  50. <el-form-item label="新床位" prop="expectId">
  51. <SelectRoom v-model="dataForm.expectId" @bed="bedChange" :disabled="!dataForm.elderlyId" status="0" :tId="dataForm.tenantId"/>
  52. </el-form-item>
  53. </el-col>
  54. <el-col :span="12" :xs="24">
  55. <el-form-item label="是否包房" prop="isPrivateRoom">
  56. <TgRadio
  57. v-model="dataForm.isPrivateRoom"
  58. :list="getDictOptions(DICT_TYPE.PRIVATE_ROOM)"
  59. inline
  60. >
  61. <el-radio
  62. v-for="(item, index) in getIntDictOptions(DICT_TYPE.PRIVATE_ROOM)"
  63. :label="item.label"
  64. :value="item.value"
  65. :key="index"
  66. >{{ item.label }}</el-radio
  67. >
  68. </TgRadio>
  69. <el-tooltip class="item" effect="dark" placement="top-start">
  70. <template #content
  71. >包房:当前床位所在的房间的其他床位将被锁定,无法再为其他人办理入住。包房金额以页面显示金额为准,本房间其他床位的金额不计算在内。<br />
  72. 非包房:仅锁定当前床位,并不影响房间内的其他床位办理入住。</template
  73. >
  74. <Icon icon="ep:info-filled" />
  75. </el-tooltip>
  76. </el-form-item>
  77. </el-col>
  78. <el-col :span="6" :xs="24">
  79. <el-form-item label="新床位类型" prop="expectOverheadChargeId">
  80. <TgSelect
  81. v-model="dataForm.expectOverheadChargeId"
  82. placeholder="请选择"
  83. clearable
  84. @change="handleChange"
  85. :list="bedList"
  86. dict-label="chargeName"
  87. dict-value="id"
  88. >
  89. <el-option v-for="t in bedList" :key="t.id" :label="t.chargeName" :value="t.id" />
  90. </TgSelect>
  91. </el-form-item>
  92. </el-col>
  93. <el-col :span="6" :xs="24">
  94. <el-form-item label="床位费原价" prop="amount">
  95. <span class="price">¥{{ formatNum(dataForm.amount) }}元/月</span>
  96. </el-form-item>
  97. </el-col>
  98. <el-col :span="12" :xs="24">
  99. <el-form-item label="是否打折" prop="isDiscount">
  100. <TgSwitch v-model="dataForm.isDiscount" @change="handleSwitch"/>
  101. </el-form-item>
  102. </el-col>
  103. <template v-if="dataForm.isDiscount==1">
  104. <el-col :span="9" :xs="24">
  105. <el-form-item label="折扣金额(元)" prop="discountAmount">
  106. <TgInput v-model="discountAmount" append-text="¥" @blur="handleBlur(dataForm, 1)" />
  107. </el-form-item>
  108. </el-col>
  109. <el-col :span="9" :xs="24">
  110. <el-form-item label="折扣率" prop="discount">
  111. <TgInputNumber v-model="discount" @blur="handleBlur(dataForm, 2)" />
  112. </el-form-item>
  113. </el-col>
  114. </template>
  115. <el-col :span="24">
  116. <el-form-item label="变更原因" prop="reason">
  117. <TgTextarea v-model="dataForm.reason" />
  118. </el-form-item>
  119. </el-col>
  120. <el-col :span="24">
  121. <el-form-item prop="changeFiles" label="附件">
  122. <SelectUpload
  123. fun-name="附件"
  124. v-model="dataForm.changeFiles"
  125. :elder="{ elderId: dataForm.elderlyId, elderName: dataForm.elderName }"
  126. />
  127. </el-form-item>
  128. </el-col>
  129. </el-row>
  130. </div>
  131. <div v-else>
  132. <div class="info-title">长者信息</div>
  133. <div class="info-wrap">
  134. <el-row :gutter="20">
  135. <el-col :xs="24" :sm="24" :md="24" :lg="processType == 2 ? 24 : 8">
  136. <el-form-item label="长者姓名" prop="elderlyId">
  137. <SelectElder v-model="dataForm.elderlyId" type="1" :toggleType="isDetail"/>
  138. </el-form-item>
  139. </el-col>
  140. <el-col :xs="24" :sm="24" :md="24" :lg="processType == 2 ? 24 : 8">
  141. <el-form-item label="证件号码" prop="idCard">
  142. <TgInput v-model="dataForm.idCard" disabled :toggleType="isDetail"/>
  143. </el-form-item>
  144. </el-col>
  145. <el-col :xs="24" :sm="24" :md="24" :lg="processType == 2 ? 24 : 8">
  146. <el-form-item label="变更生效日期" prop="changeDate">
  147. <TgDatePicker v-model="dataForm.changeDate" type="date" placeholder="变更生效日期" :toggleType="isDetail"/>
  148. </el-form-item>
  149. </el-col>
  150. </el-row>
  151. </div>
  152. <div class="info-title">变更前</div>
  153. <div class="info-wrap">
  154. <el-row :gutter="20">
  155. <el-col :xs="24" :sm="24" :md="24" :lg="processType == 2 ? 24 : 8">
  156. <el-form-item label="床位类型" prop="overheadChargeId">
  157. <TgSelect
  158. v-model="dataForm.overheadChargeId"
  159. placeholder="请选择"
  160. clearable
  161. @change="handleChange"
  162. :list="bedList"
  163. dict-label="chargeName"
  164. dict-value="id"
  165. :toggleType="isDetail"
  166. >
  167. <el-option v-for="t in bedList" :key="t.id" :label="t.chargeName" :value="t.id" />
  168. </TgSelect>
  169. </el-form-item>
  170. </el-col>
  171. <el-col :xs="24" :sm="24" :md="24" :lg="processType == 2 ? 24 : 8">
  172. <el-form-item label="床位号" prop="originalName">
  173. <TgInput v-model="dataForm.originalName" :toggleType="isDetail"/>
  174. </el-form-item>
  175. </el-col>
  176. </el-row>
  177. <el-row :gutter="20">
  178. <el-col :xs="24" :sm="24" :md="24" :lg="processType == 2 ? 24 : 8">
  179. <el-form-item label="床位价格" prop="originalAmount">
  180. <TgInput v-model="dataForm.originalAmount" :toggleType="isDetail"/>
  181. </el-form-item>
  182. </el-col>
  183. <el-col :xs="24" :sm="24" :md="24" :lg="processType == 2 ? 24 : 8">
  184. <el-form-item label="是否打折" prop="originalIsDiscount">
  185. <TgSwitch v-model="dataForm.originalIsDiscount" :toggleType="isDetail"/>
  186. </el-form-item>
  187. </el-col>
  188. </el-row>
  189. </div>
  190. <div class="info-title">变更后</div>
  191. <div class="info-wrap">
  192. <el-row :gutter="20">
  193. <el-col :xs="24" :sm="24" :md="24" :lg="processType == 2 ? 24 : 8">
  194. <el-form-item label="床位类型" prop="expectOverheadChargeId">
  195. <TgSelect
  196. v-model="dataForm.expectOverheadChargeId"
  197. placeholder="请选择"
  198. clearable
  199. @change="handleChange"
  200. :list="bedList"
  201. dict-label="chargeName"
  202. dict-value="id"
  203. :toggleType="isDetail"
  204. >
  205. <el-option v-for="t in bedList" :key="t.id" :label="t.chargeName" :value="t.id" />
  206. </TgSelect>
  207. </el-form-item>
  208. </el-col>
  209. <el-col :xs="24" :sm="24" :md="24" :lg="processType == 2 ? 24 : 8">
  210. <el-form-item label="床位号" prop="expectId">
  211. <TgInput v-model="dataForm.bedName" :toggleType="isDetail"/>
  212. </el-form-item>
  213. </el-col>
  214. </el-row>
  215. <el-row :gutter="20">
  216. <el-col :xs="24" :sm="24" :md="24" :lg="processType == 2 ? 24 : 8">
  217. <el-form-item label="床位价格" prop="amount">
  218. {{ formatNum(dataForm.amount) }}
  219. </el-form-item>
  220. </el-col>
  221. <el-col :xs="24" :sm="24" :md="24" :lg="processType == 2 ? 24 : 8">
  222. <el-form-item :label="(diffValueStr(dataForm)==0?'差额':diffValueStr(dataForm)>0?'需补缴':'需退款')" prop="actualAmount">
  223. <el-text style="font-weight: bold;color: red">{{ Math.abs(diffValueStr(dataForm)).toFixed(2) }}</el-text>
  224. </el-form-item>
  225. </el-col>
  226. <el-col :xs="24" :sm="24" :md="24" :lg="processType == 2 ? 24 : 8">
  227. <el-form-item label="是否包房" prop="isPrivateRoom">
  228. <TgRadio
  229. v-model="dataForm.isPrivateRoom"
  230. :list="getDictOptions(DICT_TYPE.PRIVATE_ROOM)"
  231. inline
  232. :toggleType="isDetail"
  233. >
  234. <el-radio
  235. v-for="(item, index) in getIntDictOptions(DICT_TYPE.PRIVATE_ROOM)"
  236. :label="item.label"
  237. :value="item.value"
  238. :key="index"
  239. >{{ item.label }}</el-radio
  240. >
  241. </TgRadio>
  242. </el-form-item>
  243. </el-col>
  244. <el-col :xs="24" :sm="24" :md="24" :lg="processType == 2 ? 24 : 8">
  245. <el-form-item label="是否打折" prop="originalIsDiscount">
  246. <TgSwitch v-model="dataForm.originalIsDiscount" :toggleType="isDetail"/>
  247. </el-form-item>
  248. </el-col>
  249. <el-col :span="6" :xs="24" v-show="dataForm.isDiscount==1">
  250. <el-form-item label="折后价格" prop="actualAmount">
  251. <span class="price">{{ formatNum(dataForm.actualAmount) }}</span>
  252. </el-form-item>
  253. </el-col>
  254. </el-row>
  255. <el-col :span="24">
  256. <el-form-item label="变更原因" prop="reason">
  257. <TgTextarea v-model="dataForm.reason" :toggleType="isDetail"/>
  258. </el-form-item>
  259. </el-col>
  260. <el-col :span="24">
  261. <el-form-item prop="changeFiles" label="附件">
  262. <SelectUpload
  263. v-model="dataForm.changeFiles"
  264. fun-name="附件"
  265. :elder="{ elderId: dataForm.elderlyId, elderName: dataForm.elderName }"
  266. :is-detail="isDetail"
  267. />
  268. </el-form-item>
  269. </el-col>
  270. </div>
  271. </div>
  272. </el-form>
  273. </template>
  274. <script setup lang="ts">
  275. import { getOverheadListByType } from '@/api/elderly/fee/overheadCharge'
  276. import { getChargeCategoryTree } from '@/api/elderly/fee/chargeCategory'
  277. import { filteredTreeData, findTreeNode } from '@/utils/tree'
  278. import { formatTime, getParentNodesById } from '@/utils'
  279. import { getIntDictOptions, DICT_TYPE, getDictOptions, getDictLabel } from '@/utils/dict'
  280. import { formatNum } from '@/utils/formatter'
  281. import { getBusinessId } from '@/api/elderly/common'
  282. import {getBedChangeRecordById, getBedChangeRecordElderlyById} from '@/api/elderly/elder/bed-change'
  283. import { bedChangeFormType } from '../types'
  284. import { getTenantId } from '@/utils/auth'
  285. defineOptions({ name: 'BedChangeProcessForm' })
  286. const formRef = ref() // 表单 Ref
  287. const state = reactive<bedChangeFormType>({
  288. dataForm: {
  289. id: '',
  290. elderlyId: '',
  291. elderName: '',
  292. idCard: '',
  293. originalId: '', // 原床位id
  294. originalName: '', // 原床位名称
  295. changeDate: formatTime(Date.now(), 'yyyy-MM-dd'),
  296. expectId: '', // 现床位id
  297. expectName: '', // 床位类型名称
  298. isPrivateRoom: '',
  299. overheadChargeId: '', // 原床位类型id
  300. expectOverheadChargeId: '', // 现床位类型id
  301. amount: 0,
  302. discountAmount: '',
  303. discount: undefined,
  304. actualAmount: 0,
  305. isDiscount: '',
  306. categoryName: '',
  307. originalAmount: '',
  308. originalIsDiscount: '',
  309. changeFiles: [],
  310. reason: '',
  311. startTenantId: '',
  312. tenantId: undefined
  313. },
  314. dataRule: {
  315. elderlyId: [{ required: true, message: '长者不能为空', trigger: 'blur' }],
  316. changeDate: [{ required: true, message: '变更生效日期不能为空', trigger: 'blur' }],
  317. expectId: [{ required: true, message: '新床位不能为空', trigger: 'blur' }],
  318. expectOverheadChargeId: [{ required: true, message: '床位类型不能为空', trigger: 'blur' }],
  319. isPrivateRoom: [{ required: true, message: '是否包房不能为空', trigger: 'blur' }],
  320. discountAmount: [{ required: true, message: '折扣金额不能为空', trigger: 'blur' }],
  321. discount: [{ required: true, message: '折扣率不能为空', trigger: 'blur' }]
  322. }
  323. })
  324. const { dataForm, dataRule } = toRefs(state)
  325. const resetFormField = reactive({ ...dataForm.value })
  326. const loading = ref(false)
  327. const discount = ref()
  328. const discountAmount = ref()
  329. const processStatus = ref('')
  330. const isDetail = ref(false)
  331. /** 打开弹窗 */
  332. const init = async (id, detail, status,elderly=undefined) => {
  333. dataForm.value.startTenantId = getTenantId()
  334. getTreeData()
  335. isDetail.value = detail
  336. if(elderly){
  337. //等新接口,查询后赋值
  338. const item = await getBedChangeRecordElderlyById(elderly.elderId)
  339. dataForm.value.elderlyId = item.id
  340. dataForm.value.idCard = item.idCard
  341. dataForm.value.elderName = item.elderName
  342. dataForm.value.overheadChargeId = item.overheadChargeId
  343. dataForm.value.originalId = item.bedId
  344. dataForm.value.originalName = item.bedName
  345. dataForm.value.originalAmount = item.actualAmount
  346. }
  347. //http://192.168.100.21:48080/admin-api/build/getBedList?bedName=&status=0&tenantIds=0&orgType=1
  348. if (id) {
  349. const res = await getBedChangeRecordById(id, status,isDetail.value)
  350. dataForm.value = res
  351. dataForm.value.changeFiles = res.changeFiles ? JSON.parse(res.changeFiles) : []
  352. discount.value = formatNum(res.discount)
  353. discountAmount.value = formatNum(res.discountAmount)
  354. if(!res.startTenantId){
  355. dataForm.value.startTenantId = getTenantId()
  356. }
  357. }
  358. getOverheadList()
  359. }
  360. /** 提交表单 */
  361. const submitForm = async () => {
  362. // 校验表单
  363. if (!formRef.value) return
  364. const valid = await formRef.value.validate()
  365. if (!valid) return
  366. // 提交请求
  367. return {
  368. valid,
  369. dataForm: {
  370. ...dataForm.value,
  371. changeFiles: JSON.stringify(dataForm.value.changeFiles),
  372. type: 4 // 床位变更
  373. }
  374. }
  375. }
  376. const diffValueStr = (dataForm) =>{
  377. try {
  378. return parseFloat((dataForm.actualAmount - dataForm.originalAmount).toFixed(2))
  379. }catch (_) {}
  380. return 0
  381. }
  382. const bedList = ref<{ chargeName: string; id: string; price: number; superiorsId: string }[]>([])
  383. const getOverheadList = async () => {
  384. const res = await getOverheadListByType(1, dataForm.value.startTenantId)
  385. bedList.value = res
  386. }
  387. const bedChange = () => {
  388. // bedList.value.map((item)=>{
  389. // if(item.id==overheadChargeId){
  390. // dataForm.value.originalAmount = item.price
  391. // }
  392. // })
  393. }
  394. // 项目类别
  395. const treeList = ref()
  396. const getTreeData = async () => {
  397. try {
  398. loading.value = true
  399. const data = await getChargeCategoryTree()
  400. treeList.value = filteredTreeData(data, 'childrenList')
  401. } catch (err) {
  402. console.log('err', err)
  403. } finally {
  404. loading.value = false
  405. }
  406. }
  407. const handleChange = () => {
  408. const item = bedList.value.find((item) => item.id == dataForm.value.expectOverheadChargeId)
  409. dataForm.value.amount = Number(item?.price)
  410. // 在tree中找到这棵树的上一级
  411. let node = findTreeNode(treeList.value, item?.superiorsId, 'id', 'childrenList')
  412. // 找到对应的名称
  413. const res = getParentNodesById(treeList.value, node.parentId, node.id)
  414. // 只保留最后两位
  415. if(res.length - 2 > 0){
  416. for(let i = 0; i <= res.length - 2; i++){
  417. res.pop()
  418. }
  419. }
  420. dataForm.value.categoryName = res.reverse().join('-')
  421. // 如果没有打折
  422. if(!dataForm.value.discount){
  423. dataForm.value.actualAmount = dataForm.value.amount
  424. }else{
  425. handleBlur(dataForm.value, 1)
  426. }
  427. for (const item of bedList.value){
  428. if(item.id==dataForm.value.expectOverheadChargeId){
  429. dataForm.value.expectName = item.chargeName
  430. break
  431. }
  432. }
  433. }
  434. // 是否启用打折
  435. const handleSwitch = () => {
  436. dataForm.value.actualAmount = dataForm.value.amount
  437. dataForm.value.discountAmount = ''
  438. dataForm.value.discount = undefined
  439. discount.value = undefined
  440. discountAmount.value = ''
  441. }
  442. // 折扣和折扣率换算
  443. const handleBlur = (item, type = 0) => {
  444. if (type == 1) {
  445. // 折扣价格
  446. item.discountAmount = discountAmount.value
  447. item.actualAmount = item.amount - discountAmount.value
  448. item.discount = item.discountAmount ? (1000 - (item.discountAmount / item.amount) * 1000) * 10 / 1000 : ''
  449. discount.value = item.discount
  450. } else if (type == 2) {
  451. item.discount = discount.value
  452. item.actualAmount = item.amount * (((item.discount / 100) * 10 * 100) / 100)
  453. item.discountAmount = ((item.amount * (1 - item.discount / 10)) / 10) * 10
  454. discountAmount.value = formatNum(((item.amount * (1 - item.discount / 10)) / 10) * 10)
  455. }
  456. item.totalAmount = item.isDiscount
  457. ? item.discount
  458. ? item.actualAmount
  459. : item.amount
  460. : item.amount
  461. }
  462. //let overheadChargeId = 0;
  463. const handleSelectElder = (item) => {
  464. dataForm.value.idCard = item.idCard
  465. dataForm.value.elderName = item.elderName
  466. dataForm.value.overheadChargeId = item.overheadChargeId
  467. dataForm.value.originalId = item.bedId
  468. dataForm.value.originalName = item.bedName
  469. dataForm.value.originalAmount = item.actualAmount
  470. //overheadChargeId = item.overheadChargeId
  471. }
  472. const handleChangeBed = () => {
  473. for (const item of bedList.value){
  474. if(item.id==dataForm.value.expectOverheadChargeId){
  475. dataForm.value.expectName = item.chargeName
  476. break
  477. }
  478. }
  479. }
  480. /** 重置表单 */
  481. const resetForm = () => {
  482. dataForm.value = {...resetFormField}
  483. formRef.value?.resetFields()
  484. }
  485. const disabledDate = (time) =>{
  486. const now = new Date()
  487. const startOfMonth = new Date(now.getFullYear(), now.getMonth(), 1)
  488. const endOfMonth = new Date(now.getFullYear(), now.getMonth() + 1, 0)
  489. return time < startOfMonth || time > endOfMonth
  490. }
  491. const processType = ref()
  492. const getProcess = async (id, type, status) => {
  493. const res = await getBusinessId(id)
  494. init(res.businessId, true, status)
  495. processType.value = type
  496. }
  497. const setTenantId = (tId) => {
  498. dataForm.value.tenantId = tId
  499. }
  500. defineExpose({ init, submitForm, getProcess, resetForm, setTenantId }) // 提供 open 方法,用于打开弹窗
  501. </script>
  502. <style lang="scss" scoped>
  503. .bed-change-form {
  504. .info {
  505. position: relative;
  506. .right {
  507. position: absolute;
  508. right: 10px;
  509. top: 20px;
  510. }
  511. }
  512. .price {
  513. color: #ff801d;
  514. }
  515. }
  516. </style>