production-work-order.ts 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. import request from '@/utils/http'
  2. import type { ProductionWorkOrderTabKey } from '@/utils/production-work-order-tabs'
  3. import type {
  4. QcWorkOrderReceptionDetail,
  5. OutboundQcDetailApplication,
  6. OutboundQcDetailPatient,
  7. OutboundQcDetailInfusionPlan,
  8. OutboundQcDetailInfusionSchedule,
  9. OutboundQcDetailProduct,
  10. QcProtocolPaymentSummary,
  11. QcBasicExperimentFeeSummary
  12. } from '@/api/qc-work-order'
  13. export interface ProductionWorkOrderItem {
  14. id: string
  15. workOrderNo: string
  16. workOrderType?: string
  17. protocolNo?: string
  18. sampleCode?: string
  19. receptionSource?: string
  20. receptionSourceLabel?: string
  21. sampleTypeName?: string
  22. isSpecial?: boolean
  23. specialRemark?: string
  24. handlerName?: string
  25. receptionTime?: string
  26. workOrderStatus: string
  27. /** 门禁质检结论:tg=合格 / btg=不合格(细胞存储/科研捐赠入库) */
  28. qcConclusion?: string
  29. qcConclusionLabel?: string
  30. createTime?: string
  31. /** 卡片展示:工单下全部工艺步骤计划开始时间区间(前端聚合) */
  32. plannedStartTimeRange?: string
  33. remark?: string
  34. labStorageCode?: string
  35. protocolStatus?: string
  36. bizType?: string
  37. bizTypeLabel?: string
  38. /** 出库工单:关联申请单号 */
  39. applicationNo?: string
  40. applicationName?: string
  41. channelName?: string
  42. /** 出库工单:患者/用户姓名 */
  43. patientName?: string
  44. /** 出库工单:当前为第几次输注计划 */
  45. sortNo?: number | null
  46. /** 出库工单:输注计划总次数 */
  47. totalCourseCount?: number | null
  48. infusionTime?: string
  49. /** 基础实验出库:课题名称(列表 enrich) */
  50. projectTitle?: string
  51. /** 基础实验出库:客户名称(列表 enrich) */
  52. customerName?: string
  53. /** 出库工单:输注计划细胞类型(列表 enrich,无产品明细时兜底) */
  54. cellType?: string
  55. /** 出库工单:输注计划规格(列表 enrich,无产品明细时兜底) */
  56. specText?: string
  57. /** 出库工单:输注计划关联细胞产品明细(列表 enrich) */
  58. products?: OutboundQcDetailProduct[]
  59. paymentSummary?: QcProtocolPaymentSummary | null
  60. feeSummary?: QcBasicExperimentFeeSummary | null
  61. /** 细胞存储 / 科研捐赠:母亲姓名 */
  62. motherName?: string
  63. /** 细胞存储 / 科研捐赠:新生儿姓名 */
  64. newbornName?: string
  65. }
  66. export type ProductionWorkOrderDetail = ProductionWorkOrderItem & {
  67. reception?: QcWorkOrderReceptionDetail
  68. productionReception?:
  69. | import('@/utils/sample-production-reception-fields').ProductionReceptionRecord
  70. | null
  71. bizType?: string
  72. bizTypeLabel?: string
  73. /** 临床应用出库:简要申请单 */
  74. pilotApplication?: {
  75. applicationNo?: string
  76. applicationName?: string
  77. applicationStatus?: string
  78. applicationStatusLabel?: string
  79. }
  80. /** 临床应用出库:简要输注计划 */
  81. infusionSchedule?: {
  82. cellType?: string
  83. specText?: string
  84. infusionTime?: string
  85. infusionLocation?: string
  86. infusionMethod?: string
  87. scheduleStatus?: string
  88. }
  89. /** 中试服务出库:与出库质检工单查看弹窗一致 */
  90. application?: OutboundQcDetailApplication
  91. patient?: OutboundQcDetailPatient
  92. infusionPlan?: OutboundQcDetailInfusionPlan
  93. products?: OutboundQcDetailProduct[]
  94. }
  95. export function fetchProductionWorkOrderList(params?: {
  96. page?: number
  97. pageSize?: number
  98. keyword?: string
  99. workOrderStatus?: string
  100. workOrderType?: string
  101. receptionSource?: string
  102. bizType?: string
  103. createTimeStart?: string
  104. createTimeEnd?: string
  105. infusionTimeStart?: string
  106. infusionTimeEnd?: string
  107. plannedStartTimeStart?: string
  108. plannedStartTimeEnd?: string
  109. excludeVoidProtocol?: boolean
  110. }) {
  111. return request.get<{
  112. list: ProductionWorkOrderItem[]
  113. total: number
  114. page: number
  115. pageSize: number
  116. totalPages: number
  117. }>({ url: '/web/admin/production-work-orders', params })
  118. }
  119. export function fetchProductionWorkOrderDetail(id: string) {
  120. return request.get<ProductionWorkOrderDetail>({ url: `/web/admin/production-work-orders/${id}` })
  121. }
  122. export function fetchCompleteProductionWorkOrder(id: string, data: Record<string, any>) {
  123. return request.put({ url: `/web/admin/production-work-orders/${id}/complete`, params: data })
  124. }
  125. export function fetchCreateProductionWorkOrder(data: {
  126. workOrderType?: string
  127. protocolNo?: string
  128. sampleCode?: string
  129. receptionSource?: string
  130. remark?: string
  131. }) {
  132. return request.post<ProductionWorkOrderItem>({
  133. url: '/web/admin/production-work-orders',
  134. data
  135. })
  136. }
  137. export function fetchConfigureProductionWorkOrder(
  138. id: string,
  139. data: {
  140. taskName?: string
  141. taskType?: string
  142. routeId?: string
  143. description?: string
  144. remark?: string
  145. }
  146. ) {
  147. return request.post<ProductionWorkOrderItem>({
  148. url: `/web/admin/production-work-orders/${id}/configure`,
  149. data
  150. })
  151. }
  152. export function fetchUpdateProductionWorkOrder(id: string, data: { remark?: string }) {
  153. return request.put<ProductionWorkOrderItem>({
  154. url: `/web/admin/production-work-orders/${id}`,
  155. data
  156. })
  157. }
  158. export function fetchDeleteProductionWorkOrder(id: string) {
  159. return request.del({ url: `/web/admin/production-work-orders/${id}` })
  160. }
  161. export interface ProductionQcDispatchOption {
  162. id: string
  163. stepOutputId?: string
  164. taskId?: string
  165. taskNo?: string
  166. taskName?: string
  167. stepId?: string
  168. processName?: string
  169. outputTypeId?: string
  170. outputTypeName?: string
  171. outputName?: string
  172. outputBarcode?: string
  173. cellProductId?: string
  174. }
  175. /** @deprecated 使用 ProductionQcDispatchOption */
  176. export type ProductionCellProductOption = ProductionQcDispatchOption
  177. export function fetchProductionWorkOrderQcDispatchOptions(workOrderId: string) {
  178. return request.get<ProductionQcDispatchOption[]>({
  179. url: `/web/admin/production-work-orders/${workOrderId}/cell-product-options`
  180. })
  181. }
  182. /** @deprecated 使用 fetchProductionWorkOrderQcDispatchOptions */
  183. export function fetchProductionWorkOrderCellProductOptions(workOrderId: string) {
  184. return fetchProductionWorkOrderQcDispatchOptions(workOrderId)
  185. }
  186. export function fetchCreateQcFromProduction(
  187. workOrderId: string,
  188. data: { productionStepOutputId: string; remark?: string }
  189. ) {
  190. return request.post({
  191. url: `/web/admin/production-work-orders/${workOrderId}/qc-work-orders`,
  192. data
  193. })
  194. }
  195. export type ProductionWorkOrderQcInfoDetail = {
  196. showGateSection?: boolean
  197. gateQcInfo?: import('@/components/business/sample-qc-info-cards/index.vue').SampleQcInfo | null
  198. processQcInfoList?: import('@/components/business/sample-qc-info-cards/index.vue').SampleQcInfo[]
  199. sampleReception?:
  200. | import('@/components/business/sample-qc-info-cards/index.vue').SampleReceptionBrief
  201. | null
  202. }
  203. export function fetchProductionWorkOrderQcInfo(workOrderId: string) {
  204. return request.get<ProductionWorkOrderQcInfoDetail>({
  205. url: `/web/admin/production-work-orders/${workOrderId}/qc-info`
  206. })
  207. }
  208. export type ProductionWorkOrderTabStepCount = {
  209. overdueIncomplete: number
  210. futureIncomplete: number
  211. }
  212. export type ProductionWorkOrderTabStepCounts = Record<
  213. ProductionWorkOrderTabKey,
  214. ProductionWorkOrderTabStepCount
  215. >
  216. export function formatProductionWorkOrderTabStepBadge(
  217. count: ProductionWorkOrderTabStepCount
  218. ): string {
  219. const fmt = (n: number) => (n > 99 ? '99+' : String(n))
  220. return `${fmt(count.overdueIncomplete)}+${fmt(count.futureIncomplete)}`
  221. }
  222. export function isProductionWorkOrderTabStepBadgeZero(
  223. count: ProductionWorkOrderTabStepCount
  224. ): boolean {
  225. return (count.overdueIncomplete || 0) === 0 && (count.futureIncomplete || 0) === 0
  226. }
  227. /** 汇总可见 Tab 的步骤角标(左侧菜单展示各 Tab 数值之和) */
  228. export function sumProductionWorkOrderTabStepCounts(
  229. counts: Partial<ProductionWorkOrderTabStepCounts> | null | undefined,
  230. tabKeys: ProductionWorkOrderTabKey[]
  231. ): ProductionWorkOrderTabStepCount {
  232. return tabKeys.reduce(
  233. (acc, key) => {
  234. const c = counts?.[key]
  235. acc.overdueIncomplete += c?.overdueIncomplete || 0
  236. acc.futureIncomplete += c?.futureIncomplete || 0
  237. return acc
  238. },
  239. { overdueIncomplete: 0, futureIncomplete: 0 }
  240. )
  241. }
  242. export function fetchProductionWorkOrderTabStepCounts() {
  243. return request.get<ProductionWorkOrderTabStepCounts>({
  244. url: '/web/admin/production-work-orders/tab-step-counts'
  245. })
  246. }
  247. let manageTabStepCountsInflight: Promise<ProductionWorkOrderTabStepCounts> | null = null
  248. /** 并发去重:Tab 栏与侧栏角标共享同一次 tab-step-counts 请求 */
  249. export function fetchProductionWorkOrderTabStepCountsDeduped() {
  250. if (!manageTabStepCountsInflight) {
  251. manageTabStepCountsInflight = fetchProductionWorkOrderTabStepCounts().finally(() => {
  252. manageTabStepCountsInflight = null
  253. })
  254. }
  255. return manageTabStepCountsInflight
  256. }
  257. export function fetchMineProductionWorkOrderTabStepCounts() {
  258. return request.get<ProductionWorkOrderTabStepCounts>({
  259. url: '/web/admin/production-work-orders/mine-tab-step-counts'
  260. })
  261. }
  262. let mineTabStepCountsInflight: Promise<ProductionWorkOrderTabStepCounts> | null = null
  263. export function fetchMineProductionWorkOrderTabStepCountsDeduped() {
  264. if (!mineTabStepCountsInflight) {
  265. mineTabStepCountsInflight = fetchMineProductionWorkOrderTabStepCounts().finally(() => {
  266. mineTabStepCountsInflight = null
  267. })
  268. }
  269. return mineTabStepCountsInflight
  270. }