detail.uvue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411
  1. <template>
  2. <uni-navbar-lite @leftClick="handleBack" :show-right=false title="出库单详情"></uni-navbar-lite>
  3. <view class="page-container">
  4. <scroll-view class="page-content" :scroll-y="true">
  5. <!-- 出库单信息 -->
  6. <view class="section">
  7. <view class="info-card">
  8. <view class="info-row">
  9. <text class="info-label">出库单号</text>
  10. <text class="info-value">{{ salseCode }}</text>
  11. </view>
  12. <view class="info-row">
  13. <text class="info-label">状态</text>
  14. <text class="info-value status-text" :class="'status-' + status">{{ statusText }}</text>
  15. </view>
  16. <view class="info-row">
  17. <text class="info-label">创建人</text>
  18. <text class="info-value">{{ createBy }}</text>
  19. </view>
  20. <view class="info-row">
  21. <text class="info-label">领用人</text>
  22. <text class="info-value">{{ receiverUser }}</text>
  23. </view>
  24. <view class="info-row">
  25. <text class="info-label">创建时间</text>
  26. <text class="info-value">{{ createTime }}</text>
  27. </view>
  28. </view>
  29. </view>
  30. <!-- 物料明细 -->
  31. <view class="section">
  32. <view class="section-header">
  33. <view class="section-indicator"></view>
  34. <text class="section-title">物料明细</text>
  35. </view>
  36. <view class="material-list">
  37. <view v-if="lineList.length === 0" class="empty-tip">
  38. <text class="empty-tip-text">暂无物料</text>
  39. </view>
  40. <view
  41. v-else
  42. v-for="(item, index) in lineList"
  43. :key="index"
  44. class="material-item"
  45. >
  46. <view class="material-header">
  47. <text class="material-name">{{ getItemName(item) }}</text>
  48. <text class="material-status" :class="getLineStatus(item) === 'Y' ? 'status-received' : 'status-pending'">
  49. {{ getLineStatusText(item) }}
  50. </text>
  51. </view>
  52. <view class="material-spec" v-if="getSpecification(item)">
  53. <text class="spec-label">规格:</text>
  54. <text class="spec-value">{{ getSpecification(item) }}</text>
  55. </view>
  56. <view class="material-footer">
  57. <view class="quantity-info">
  58. <text class="quantity-label">数量:</text>
  59. <text class="quantity-value">{{ getQuantity(item) }} {{ getMeasureName(item) }}</text>
  60. </view>
  61. <view v-if="status == 'FINISHED' && getLineStatus(item) != 'Y'" class="receive-btn-wrap">
  62. <button class="receive-btn" @click="handleSignReceive(item)">签收</button>
  63. </view>
  64. </view>
  65. </view>
  66. </view>
  67. </view>
  68. </scroll-view>
  69. </view>
  70. </template>
  71. <script setup lang="uts">
  72. import { ref, computed } from 'vue'
  73. import { getProductSalseById, signReceiveLine } from '../../api/out/index'
  74. import { getUserInfo } from '../../utils/storage'
  75. const salseId = ref<string>("")
  76. const salseCode = ref<string>("")
  77. const status = ref<string>("")
  78. const createBy = ref<string>("")
  79. const receiverUser = ref<string>("")
  80. const createTime = ref<string>("")
  81. const lineList = ref<UTSJSONObject[]>([])
  82. let currentUserId: string = ''
  83. const statusText = computed((): string => {
  84. const s = status.value
  85. switch (s) {
  86. case 'PREPARE': return '待确认'
  87. case 'CONFIRMED': return '已确认'
  88. case 'EXECUTING': return '执行中'
  89. case 'FINISHED': return '已完成'
  90. case 'CANCEL': return '已取消'
  91. default: return s
  92. }
  93. })
  94. const getItemName = (item: UTSJSONObject): string => {
  95. if (item == null) return ''
  96. const val = item['itemName']
  97. return val != null ? val.toString() : ''
  98. }
  99. const getSpecification = (item: UTSJSONObject): string => {
  100. if (item == null) return ''
  101. const val = item['specification']
  102. return val != null ? val.toString() : ''
  103. }
  104. const getQuantity = (item: UTSJSONObject): string => {
  105. if (item == null) return '0'
  106. const val = item['quantitySalse']
  107. return val != null ? val.toString() : '0'
  108. }
  109. const getMeasureName = (item: UTSJSONObject): string => {
  110. if (item == null) return ''
  111. const val = item['measureName']
  112. return val != null ? val.toString() : ''
  113. }
  114. const getLineStatus = (item: UTSJSONObject): string => {
  115. if (item == null) return ''
  116. const val = item['receiverStatus']
  117. return val != null ? val.toString() : ''
  118. }
  119. const getLineStatusText = (item: UTSJSONObject): string => {
  120. const s = getLineStatus(item)
  121. if (s == 'Y') return '已签收'
  122. if (s == 'N') return '待签收'
  123. return s
  124. }
  125. const loadDetail = (): void => {
  126. if (salseId.value.length === 0) return
  127. getProductSalseById(salseId.value).then((res: any) => {
  128. const resData = res as UTSJSONObject
  129. const data = resData["data"] as UTSJSONObject
  130. salseCode.value = data['salseCode'] != null ? data['salseCode'].toString() : ''
  131. status.value = data['status'] != null ? data['status'].toString() : ''
  132. createBy.value = data['createNickName'] != null ? data['createNickName'].toString() : ''
  133. receiverUser.value = data['receiverUser'] != null ? data['receiverUser'].toString() : ''
  134. createTime.value = data['createTime'] != null ? data['createTime'].toString() : ''
  135. const lines = data['wmProductSalseLineList']
  136. if (lines != null) {
  137. const allLines = lines as UTSJSONObject[]
  138. // 只显示当前用户的明细
  139. lineList.value = allLines.filter((line: UTSJSONObject) => {
  140. const receiverUserId = line['receiverUserId']
  141. return receiverUserId != null && receiverUserId.toString() === currentUserId
  142. })
  143. } else {
  144. lineList.value = []
  145. }
  146. }).catch((e) => {
  147. const error = e as UTSError
  148. const errMsg = error?.message
  149. uni.showToast({ title: errMsg.toString(), icon: 'none' })
  150. })
  151. }
  152. const handleSignReceive = (item: UTSJSONObject): void => {
  153. const lineId = item['lineId']
  154. if (lineId == null) {
  155. uni.showToast({ title: '明细ID不存在', icon: 'none' })
  156. return
  157. }
  158. uni.showModal({
  159. title: '提示',
  160. content: '确认签收该物料?',
  161. success: (res) => {
  162. if (res.confirm) {
  163. signReceiveLine(lineId.toString()).then((res: any) => {
  164. uni.showToast({ title: '签收成功', icon: 'success' })
  165. loadDetail()
  166. }).catch((e) => {
  167. const error = e as UTSError
  168. const errMsg = error?.message
  169. uni.showToast({ title: errMsg.toString(), icon: 'none' })
  170. })
  171. }
  172. }
  173. })
  174. }
  175. const handleBack = (): void => {
  176. uni.navigateBack()
  177. }
  178. onLoad((options: any) => {
  179. const params = options as UTSJSONObject
  180. if (params != null && params['id'] != null) {
  181. salseId.value = params['id'].toString()
  182. const userInfo = getUserInfo()
  183. if (userInfo != null) {
  184. const userId = userInfo['userId']
  185. currentUserId = userId != null ? userId.toString() : ''
  186. }
  187. loadDetail()
  188. }
  189. })
  190. </script>
  191. <style lang="scss">
  192. .page-container {
  193. flex: 1;
  194. background-color: #e8f0f9;
  195. }
  196. .page-content {
  197. flex: 1;
  198. padding: 20rpx;
  199. }
  200. .section {
  201. margin-bottom: 20rpx;
  202. background: #ffffff;
  203. border-radius: 16rpx;
  204. padding: 20rpx;
  205. }
  206. .section-header {
  207. flex-direction: row;
  208. align-items: center;
  209. margin-bottom: 20rpx;
  210. }
  211. .section-indicator {
  212. width: 6rpx;
  213. height: 32rpx;
  214. background-color: #007aff;
  215. border-radius: 3rpx;
  216. margin-right: 12rpx;
  217. }
  218. .section-title {
  219. font-size: 32rpx;
  220. color: #333333;
  221. font-weight: bold;
  222. }
  223. .info-card {
  224. background-color: #f8f9fa;
  225. border-radius: 8rpx;
  226. padding: 20rpx;
  227. }
  228. .info-row {
  229. flex-direction: row;
  230. justify-content: space-between;
  231. margin-bottom: 16rpx;
  232. &:last-child {
  233. margin-bottom: 0;
  234. }
  235. }
  236. .info-label {
  237. font-size: 28rpx;
  238. color: #666666;
  239. }
  240. .info-value {
  241. font-size: 28rpx;
  242. color: #333333;
  243. }
  244. .status-text {
  245. &.status-PREPARE {
  246. color: #faad14;
  247. }
  248. &.status-CONFIRMED {
  249. color: #1890ff;
  250. }
  251. &.status-EXECUTING {
  252. color: #722ed1;
  253. }
  254. &.status-FINISHED {
  255. color: #52c41a;
  256. }
  257. &.status-CANCEL {
  258. color: #ff4d4f;
  259. }
  260. &.status-received {
  261. color: #52c41a;
  262. }
  263. &.status-pending {
  264. color: #faad14;
  265. }
  266. }
  267. .material-list {
  268. background-color: #f8f9fa;
  269. border-radius: 8rpx;
  270. padding: 20rpx;
  271. }
  272. .empty-tip {
  273. align-items: center;
  274. padding: 40rpx;
  275. }
  276. .empty-tip-text {
  277. color: #999999;
  278. font-size: 28rpx;
  279. }
  280. .material-item {
  281. background-color: #ffffff;
  282. border-radius: 8rpx;
  283. padding: 24rpx;
  284. margin-bottom: 16rpx;
  285. &:last-child {
  286. margin-bottom: 0;
  287. }
  288. }
  289. .material-header {
  290. flex-direction: row;
  291. justify-content: space-between;
  292. align-items: center;
  293. margin-bottom: 12rpx;
  294. }
  295. .material-name {
  296. font-size: 30rpx;
  297. color: #333333;
  298. font-weight: bold;
  299. flex: 1;
  300. }
  301. .material-status {
  302. font-size: 24rpx;
  303. padding: 6rpx 16rpx;
  304. border-radius: 6rpx;
  305. &.status-received {
  306. background-color: #f6ffed;
  307. color: #52c41a;
  308. }
  309. &.status-pending {
  310. background-color: #fff7e6;
  311. color: #fa8c16;
  312. }
  313. }
  314. .material-spec {
  315. flex-direction: row;
  316. margin-bottom: 12rpx;
  317. }
  318. .spec-label {
  319. font-size: 24rpx;
  320. color: #999999;
  321. }
  322. .spec-value {
  323. font-size: 24rpx;
  324. color: #666666;
  325. }
  326. .material-footer {
  327. flex-direction: row;
  328. justify-content: space-between;
  329. align-items: center;
  330. }
  331. .quantity-info {
  332. flex-direction: row;
  333. }
  334. .quantity-label {
  335. font-size: 26rpx;
  336. color: #999999;
  337. }
  338. .quantity-value {
  339. font-size: 26rpx;
  340. color: #007aff;
  341. font-weight: bold;
  342. }
  343. .receive-btn-wrap {
  344. margin-left: 20rpx;
  345. }
  346. .receive-btn {
  347. padding: 12rpx 32rpx;
  348. background-color: #007aff;
  349. color: #ffffff;
  350. font-size: 26rpx;
  351. border-radius: 8rpx;
  352. border: none;
  353. }
  354. .material-detail {
  355. flex-direction: row;
  356. justify-content: space-between;
  357. }
  358. .detail-row {
  359. flex-direction: row;
  360. }
  361. </style>