medicine-form.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505
  1. <template>
  2. <Dialog v-model="dialogVisible" title="新增" width="80%" @close="handleClosed" scroll>
  3. <div class="daily-fee-form">
  4. <el-form
  5. ref="formRef"
  6. :model="dataForm"
  7. :rules="dataRule"
  8. label-width="100px"
  9. >
  10. <el-row>
  11. <el-col :span="12" :xs="24">
  12. <el-form-item label="长者姓名" prop="elderId">
  13. <selectElder v-model="dataForm.elderId" @elder="handleUpdateElder" :tId="dataForm.tenantId" inStatus=""/>
  14. </el-form-item>
  15. </el-col>
  16. <el-col :span="12" :xs="24">
  17. <el-form-item label="证件号码" prop="idCard">
  18. <TgInput v-model="dataForm.idCard" disabled />
  19. </el-form-item>
  20. </el-col>
  21. <el-col :span="12" :xs="24">
  22. <el-form-item label="社会保障卡号" prop="socialSecurityCardNumber">
  23. <TgInput v-model="dataForm.socialSecurityCardNumber" disabled />
  24. </el-form-item>
  25. </el-col>
  26. <el-col :span="12" :xs="24">
  27. <el-form-item label="床位号" prop="bedName">
  28. <TgInput v-model="dataForm.bedName" disabled />
  29. </el-form-item>
  30. </el-col>
  31. <el-col :span="12" :xs="24">
  32. <el-form-item label="护理等级" prop="nursingLevel">
  33. <el-select
  34. v-model="dataForm.nursingLevel"
  35. placeholder="请选择护理等级"
  36. disabled
  37. >
  38. <el-option
  39. v-for="(n, i) in nurseLevelList"
  40. :key="i"
  41. :label="n.nurseLevelName"
  42. :value="n.id"
  43. />
  44. </el-select>
  45. </el-form-item>
  46. </el-col>
  47. <el-col :span="12" :xs="24">
  48. <el-form-item label="归属账单月" prop="billingMonth">
  49. <el-date-picker
  50. v-model="dataForm.billingMonth"
  51. type="month"
  52. value-format="YYYY-MM"
  53. placeholder="请选择日期"
  54. :style="{ width: '100%' }"
  55. />
  56. </el-form-item>
  57. </el-col>
  58. </el-row>
  59. <el-button type="primary" @click="handleAdd" link class="mb5" :disabled="!dataForm.billingMonth">
  60. <Icon icon="ep:zoom-in" class="mr-5px" />添加明细
  61. </el-button>
  62. <el-table :data="dataForm.medicineRecordList">
  63. <el-table-column width="55" >
  64. <template #default="scope">
  65. <Icon
  66. class="mt1"
  67. :size="26"
  68. color="#f09d31"
  69. icon="ep:remove"
  70. @click="handleDelete(scope.$index)"
  71. />
  72. </template>
  73. </el-table-column>
  74. <el-table-column prop="date" label="序号" width="55">
  75. <template #default="scope">
  76. {{ scope.$index + 1 }}
  77. </template>
  78. </el-table-column>
  79. <el-table-column label="药品名称" :width="180">
  80. <template #default="scope">
  81. <el-select
  82. v-model="scope.row.id"
  83. @change="handleChangeDrug(scope.row)"
  84. placeholder="请选择药品"
  85. filterable
  86. >
  87. <el-option
  88. v-for="(n, i) in treeList"
  89. :key="i"
  90. :label="n.drugName"
  91. :value="n.id"
  92. />
  93. </el-select>
  94. </template>
  95. </el-table-column>
  96. <el-table-column label="药品批次" :width="200">
  97. <template #default="scope">
  98. <el-select
  99. v-model="scope.row.itemId"
  100. @change="handleChangeItemName(scope.row)"
  101. filterable
  102. >
  103. <el-option
  104. v-for="(n, i) in bathList"
  105. :key="i"
  106. :label="n.name"
  107. :value="n.id"
  108. />
  109. </el-select>
  110. </template>
  111. </el-table-column>
  112. <el-table-column prop="specification" label="规格" width="100">
  113. <template #default="scope">
  114. {{ scope.row.specification }}
  115. </template>
  116. </el-table-column>
  117. <el-table-column prop="productionDate" label="生产日期" width="100">
  118. <template #default="scope">
  119. {{ scope.row.productionDate }}
  120. </template>
  121. </el-table-column>
  122. <el-table-column prop="expiryDate" label="有效日期" width="100">
  123. <template #default="scope">
  124. {{ scope.row.expiryDate }}
  125. </template>
  126. </el-table-column>
  127. <el-table-column prop="price" label="单价(元)" width="100">
  128. <template #default="scope">
  129. <TgInputNumber v-model="scope.row.price" @change="handleChangeNum(scope.row)" disabled />
  130. </template>
  131. </el-table-column>
  132. <el-table-column prop="count" label="数量" width="100">
  133. <template #default="scope">
  134. <TgInputNumber
  135. v-model="scope.row.count"
  136. :controls="false"
  137. @change="handleChangeNum(scope.row)"
  138. />
  139. </template>
  140. </el-table-column>
  141. <el-table-column prop="amount" label="费用(元)" :width="120">
  142. <template #default="scope">
  143. {{ formatNum(scope.row.amount) }}
  144. </template>
  145. </el-table-column>
  146. <el-table-column prop="startDate" label="开始日期" width="160">
  147. <template #default="scope">
  148. <TgDatePicker v-model="scope.row.startDate" type="date" :clearable="false" :editable="false"/>
  149. </template>
  150. </el-table-column>
  151. <el-table-column prop="endDate" label="结束日期" width="160">
  152. <template #default="scope">
  153. <TgDatePicker v-model="scope.row.endDate" type="date" :clearable="false" :editable="false"/>
  154. </template>
  155. </el-table-column>
  156. <el-table-column prop="accountDate" label="记账时间" width="180">
  157. <template #default="scope">
  158. {{ formatDate(scope.row.accountDate) }}
  159. </template>
  160. </el-table-column>
  161. <el-table-column prop="remarks" label="备注" :width="180">
  162. <template #default="scope">
  163. <TgTextarea v-model="scope.row.remarks" />
  164. </template>
  165. </el-table-column>
  166. </el-table>
  167. </el-form>
  168. </div>
  169. <template #footer>
  170. <el-button @click="handleClosed">取消</el-button>
  171. <el-button v-loading="formLoading" type="primary" @click="submitForm" >确定</el-button>
  172. </template>
  173. </Dialog>
  174. </template>
  175. <script setup lang="ts">
  176. import {
  177. getMedicineListByTenant,
  178. useMedicine
  179. } from '@/api/elderly/fee/daily-fee'
  180. import { formatDate } from '@/utils/formatTime'
  181. import { getNursingLevelPage } from '@/api/elderly/nursing'
  182. import { useSettingStore } from '@/store/modules/setting'
  183. import { formatNum } from '@/utils/formatter'
  184. const message = useMessage() // 消息弹窗
  185. const { t } = useI18n() // 国际化
  186. const dialogVisible = ref(false)
  187. const settingStore = useSettingStore()
  188. const formLoading = ref(false)
  189. const formRef = ref()
  190. const bathList = ref([] as any)
  191. const treeList = ref([] as any)
  192. const originalMedicineData = ref([]) // 存储原始数据
  193. // 护理等级列表
  194. const nurseLevelList = ref<{ nurseLevelName: string; id: string }[]>([])
  195. interface DrugFormType {
  196. elderId: number | undefined
  197. billingMonth: string
  198. tenantId: number
  199. idCard: string
  200. socialSecurityCardNumber: string
  201. bedName: string
  202. nursingLevel: string
  203. medicineRecordList: DrugTableFormType[]
  204. }
  205. interface DrugTableFormType {
  206. id: number | undefined
  207. itemId: number | undefined
  208. yetId:number | undefined
  209. count: number
  210. price: number
  211. amount: number
  212. startDate: string
  213. endDate: string
  214. accountDate: number
  215. billingMonth: string
  216. remarks: string
  217. specification: string
  218. productionDate: string
  219. expiryDate: string
  220. }
  221. const state = reactive({
  222. dataForm: {
  223. elderId: undefined,
  224. idCard: '',
  225. socialSecurityCardNumber: '',
  226. bedName: '',
  227. nursingLevel: '',
  228. billingMonth: '',
  229. medicineRecordList: [],
  230. tenantId: 0
  231. } as DrugFormType,
  232. dataRule: {
  233. elderId: [
  234. {
  235. required: true,
  236. message: '姓名不能为空',
  237. trigger: 'blur'
  238. }
  239. ],
  240. billingMonth: [
  241. {
  242. required: true,
  243. message: '归属账单月不能为空',
  244. trigger: 'blur'
  245. }
  246. ],
  247. }
  248. })
  249. defineOptions({ name: 'MedicineForm' })
  250. const { dataForm, dataRule } = toRefs(state)
  251. // 获取药品/耗材数据
  252. const getMedicineList = async () => {
  253. try {
  254. const data = await getMedicineListByTenant(dataForm.value.tenantId)
  255. originalMedicineData.value = data
  256. // 转换数据格式
  257. const result = [] as any[]
  258. // 处理耗材类型数据(数组)
  259. if (Array.isArray(data[0])) {
  260. data[0].forEach(item => {
  261. result.push({
  262. type: 2,
  263. drugName: item.drugName,
  264. expiryDate: item.expiryDate,
  265. id: item.id,
  266. originalData: item
  267. })
  268. })
  269. }
  270. // 处理药品类型数据(对象)
  271. if (typeof data[1] === 'object' && data[1] !== null) {
  272. Object.keys(data[1]).forEach(key => {
  273. if (Array.isArray(data[1][key]) && data[1][key].length > 0) {
  274. result.push({
  275. type: 1,
  276. drugName: key,
  277. id: data[1][key][0].id, // 使用第一个批次的id作为药品id
  278. batchData: data[1][key] // 存储所有批次数据
  279. })
  280. }
  281. })
  282. }
  283. treeList.value = result
  284. } catch (error) {
  285. console.error('获取药品列表失败:', error)
  286. }
  287. }
  288. // 获取护理等级
  289. const getNurseLevelList = async () => {
  290. try {
  291. const data = await getNursingLevelPage({
  292. pageNum: 1,
  293. pageSize: 100
  294. })
  295. nurseLevelList.value = data.list
  296. } finally {
  297. }
  298. }
  299. /** 打开弹窗 */
  300. const open = async (tenantId) => {
  301. dataForm.value.tenantId = tenantId
  302. dataForm.value.billingMonth = formatDate(new Date(), 'YYYY-MM')
  303. getNurseLevelList()
  304. getMedicineList()
  305. dialogVisible.value = true
  306. }
  307. // 关闭
  308. const handleClosed = () => {
  309. dataForm.value = {
  310. elderId: undefined,
  311. idCard: '',
  312. socialSecurityCardNumber: '',
  313. bedName: '',
  314. nursingLevel: '',
  315. billingMonth: '',
  316. medicineRecordList: [],
  317. tenantId: 0
  318. }
  319. dialogVisible.value = false
  320. }
  321. // 添加明细
  322. const handleAdd = () => {
  323. // 获取当前日期
  324. const now = new Date()
  325. // 获取本月第一天
  326. const startDate = formatDate(new Date(now.getFullYear(), now.getMonth(), 1), 'YYYY-MM-DD')
  327. // 获取本月最后一天
  328. const endDate = formatDate(new Date(now.getFullYear(), now.getMonth() + 1, 0), 'YYYY-MM-DD')
  329. dataForm.value.medicineRecordList.push({
  330. id: undefined,
  331. itemId: undefined,
  332. yetId: undefined,
  333. specification: '',
  334. productionDate: '',
  335. count: 1,
  336. amount: 0,
  337. accountDate: Date.now(),
  338. remarks: '',
  339. startDate: startDate,
  340. endDate: endDate,
  341. expiryDate: '',
  342. price: 0,
  343. billingMonth: dataForm.value.billingMonth,
  344. })
  345. }
  346. // 删除明细
  347. const handleDelete = (index) => {
  348. dataForm.value.medicineRecordList.splice(index, 1)
  349. }
  350. // 药品选择
  351. const handleChangeDrug = (row) => {
  352. // 先清空所有数据
  353. row.itemId = undefined
  354. row.yetId = undefined
  355. row.specification = ''
  356. row.productionDate = ''
  357. row.count = 1
  358. row.amount = 0
  359. row.accountDate = Date.now()
  360. row.expiryDate = ''
  361. row.price = 0
  362. // 获取选中的药品
  363. const selectedMedicine = treeList.value.find(item => item.id === row.id)
  364. if (!selectedMedicine) return
  365. // 更新药品名称
  366. if (selectedMedicine.type == 2) {
  367. // 耗材类型 - 直接填充数据
  368. const item = selectedMedicine.originalData
  369. row.price = item.unitPrice
  370. row.specification = item.specification
  371. row.productionDate = item.productionDate
  372. row.expiryDate = item.expiryDate
  373. row.yetId = item.id // 直接设置itemId
  374. // 计算金额
  375. handleChangeNum(row)
  376. // 清空批次列表
  377. bathList.value = []
  378. } else if (selectedMedicine.type === 1) {
  379. // 药品类型 - 设置批次数据
  380. bathList.value = selectedMedicine.batchData.map(batch => ({
  381. ...batch,
  382. name: `${batch.drugName}(${batch.batchNumber})`,
  383. id: batch.id
  384. }))
  385. // 清空itemId,等待选择批次
  386. row.itemId = ''
  387. }
  388. }
  389. // 计算金额
  390. const handleChangeNum = (row) => {
  391. row.amount = Number(row.count || 0) * Number(row.price || 0)
  392. }
  393. // 药品批次选择
  394. const handleChangeItemName = (row) => {
  395. let item = bathList.value.find((item) => item.id == row.itemId)
  396. if (!item) return
  397. row.yetId = item.id
  398. row.price = item.unitPrice || item.price
  399. row.specification = item.specification
  400. row.productionDate = item.productionDate
  401. row.expiryDate = item.expiryDate
  402. handleChangeNum(row)
  403. }
  404. // 老人选择
  405. const handleUpdateElder = async (item) => {
  406. dataForm.value.elderId = item.id.toString()
  407. dataForm.value.idCard = item.idCard
  408. dataForm.value.socialSecurityCardNumber = item.socialSecurityCardNumber
  409. dataForm.value.bedName = item.bedName
  410. dataForm.value.nursingLevel = item.nurseLevelId
  411. }
  412. /** 提交表单 */
  413. const submitForm = async () => {
  414. if(formLoading.value){
  415. return
  416. }
  417. formLoading.value = true
  418. // 提交请求
  419. try {
  420. if(!dataForm.value.medicineRecordList.length){
  421. message.error('至少添加一条数据')
  422. return
  423. }
  424. // 验证所有行数据
  425. for (let i = 0; i < dataForm.value.medicineRecordList.length; i++) {
  426. const row = dataForm.value.medicineRecordList[i]
  427. if (!row.yetId) {
  428. message.error(`第 ${i + 1} 行:请选择有效的药品`)
  429. return
  430. }
  431. }
  432. // 校验表单
  433. if (!formRef.value) return
  434. const valid = await formRef.value.validate()
  435. if (!valid) return
  436. let params = { ...JSON.parse(JSON.stringify(dataForm.value)) }
  437. params = {
  438. elderId: Number(params.elderId),
  439. medicineRecordList:params.medicineRecordList
  440. }
  441. params.medicineRecordList = params.medicineRecordList.map(item => {
  442. return {
  443. id: item.yetId,
  444. count: Number(item.count),
  445. price: Number(item.price),
  446. amount: Number(item.amount),
  447. startDate: item.startDate,
  448. endDate: item.endDate,
  449. accountDate: formatDate(item.accountDate),
  450. remarks: item.remarks,
  451. billingMonth: dataForm.value.billingMonth,
  452. }
  453. })
  454. const res = await useMedicine(params)
  455. if (res) {
  456. message.success(`操作成功!`)
  457. handleClosed()
  458. }
  459. }catch (e){
  460. setTimeout(()=>{
  461. formLoading.value = false
  462. },500)
  463. } finally {
  464. setTimeout(()=>{
  465. formLoading.value = false
  466. },500)
  467. }
  468. }
  469. defineExpose({ open }) // 提供 open 方法,用于打开弹窗
  470. </script>
  471. <style lang="scss" scoped>
  472. .w80 {
  473. width: 250px !important;
  474. }
  475. .underline{
  476. margin-left: 0.5rem;
  477. padding: 5px;
  478. background-color: #f2f2f2;
  479. color: #d5d5d5;
  480. text-decoration: line-through;
  481. }
  482. </style>