detail.vue 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. <script setup>
  2. import { ref, computed } from 'vue'
  3. import { onLoad, onShow } from '@dcloudio/uni-app'
  4. import { getPlan, updateProductCerts, setHazardCertReady } from '@/api/pl'
  5. import { chooseAndUploadImages } from '@/utils/upload'
  6. import { dash, planCardSub, hasPurchaseCert, certReadyLabel } from '@/utils/plLabels'
  7. import { PAGE_PATHS } from '@/config/api'
  8. const id = ref('')
  9. const detail = ref(null)
  10. const acting = ref(false)
  11. const products = computed(() =>
  12. Array.isArray(detail.value?.products) ? detail.value.products : []
  13. )
  14. const goBack = () => {
  15. uni.navigateBack({
  16. fail: () => uni.navigateTo({ url: PAGE_PATHS.plCertList })
  17. })
  18. }
  19. const load = async () => {
  20. if (!id.value) return
  21. try {
  22. detail.value = await getPlan(id.value)
  23. } catch (e) {
  24. uni.showToast({ title: e.message || '加载失败', icon: 'none' })
  25. }
  26. }
  27. const certUrls = (p) => {
  28. const raw = p.purchaseCertUrl || p.purchase_cert_url || ''
  29. return String(raw)
  30. .split(',')
  31. .map((s) => s.trim())
  32. .filter(Boolean)
  33. }
  34. const preview = (urls, index) => {
  35. uni.previewImage({ urls, current: urls[index] || urls[0] })
  36. }
  37. const uploadForProduct = async (product) => {
  38. const current = certUrls(product)
  39. acting.value = true
  40. try {
  41. const added = await chooseAndUploadImages({
  42. folder: 'pl-purchase-cert',
  43. maxCount: 6,
  44. currentUrls: current
  45. })
  46. if (!added.length) return
  47. const nextUrls = current.concat(added)
  48. await updateProductCerts(id.value, [
  49. {
  50. id: product.id,
  51. purchaseCertUrl: nextUrls.join(',')
  52. }
  53. ])
  54. uni.showToast({ title: '已上传', icon: 'success' })
  55. await load()
  56. } catch (e) {
  57. uni.showToast({ title: e.message || '上传失败', icon: 'none' })
  58. } finally {
  59. acting.value = false
  60. }
  61. }
  62. const markReady = (ready) => {
  63. uni.showModal({
  64. title: ready ? '标记证书齐备' : '取消齐备',
  65. content: ready ? '确认该计划危化品证书已齐备?' : '确认取消证书齐备标记?',
  66. success: async (r) => {
  67. if (!r.confirm) return
  68. acting.value = true
  69. try {
  70. await setHazardCertReady(id.value, ready)
  71. uni.showToast({ title: '已更新', icon: 'success' })
  72. load()
  73. } catch (e) {
  74. uni.showToast({ title: e.message || '操作失败', icon: 'none' })
  75. } finally {
  76. acting.value = false
  77. }
  78. }
  79. })
  80. }
  81. onLoad((q) => {
  82. id.value = q?.id || ''
  83. })
  84. onShow(() => load())
  85. </script>
  86. <template>
  87. <view class="mp-page mp-page--sub mp-page--with-bottom">
  88. <view class="mp-header mp-header--pl">
  89. <text class="mp-subpage-back" @click="goBack">← 返回</text>
  90. <text class="mp-header__title" style="margin-top: 16rpx">购买证上传</text>
  91. </view>
  92. <view v-if="detail" class="mp-detail-hero">
  93. <view class="mp-detail-hero__top">
  94. <view>
  95. <text class="mp-detail-hero__title">{{ dash(detail.planNo) }}</text>
  96. <text class="mp-detail-hero__sub">{{ planCardSub(detail) }}</text>
  97. </view>
  98. <text class="mp-tag" :class="'mp-tag--' + certReadyLabel(detail.hazardCertReady).tag">
  99. {{ certReadyLabel(detail.hazardCertReady).text }}
  100. </text>
  101. </view>
  102. </view>
  103. <view class="mp-block">
  104. <text class="mp-block__title">产品购买证</text>
  105. <mp-empty v-if="!products.length" compact title="暂无产品明细" />
  106. <view v-for="p in products" :key="p.id" class="mp-block__panel" style="margin-bottom: 24rpx">
  107. <view class="mp-kv">
  108. <text class="mp-kv__label">{{ dash(p.productName) }}</text>
  109. <text class="mp-kv__value">{{ hasPurchaseCert(p) ? '已上传' : '未上传' }}</text>
  110. </view>
  111. <view class="mp-photo-row" style="margin-top: 16rpx">
  112. <view
  113. v-for="(url, idx) in certUrls(p)"
  114. :key="url + idx"
  115. class="mp-photo-item"
  116. @click="preview(certUrls(p), idx)"
  117. >
  118. <image :src="url" mode="aspectFill" />
  119. </view>
  120. <view
  121. class="mp-photo-add"
  122. :class="{ 'mp-photo-add--disabled': acting }"
  123. @click="uploadForProduct(p)"
  124. >
  125. <text>+</text>
  126. <text style="font-size: 22rpx">拍照</text>
  127. </view>
  128. </view>
  129. </view>
  130. </view>
  131. <view class="mp-bottom-bar">
  132. <button
  133. v-if="!detail?.hazardCertReady"
  134. class="mp-btn mp-btn--primary mp-btn--block"
  135. :disabled="acting"
  136. @click="markReady(true)"
  137. >
  138. 标记证书齐备
  139. </button>
  140. <button
  141. v-else
  142. class="mp-btn mp-btn--ghost mp-btn--block"
  143. :disabled="acting"
  144. @click="markReady(false)"
  145. >
  146. 取消齐备
  147. </button>
  148. </view>
  149. </view>
  150. </template>