detail.uvue 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404
  1. <template>
  2. <uni-navbar-lite :showRight=false :title="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="section-header">
  8. <view class="section-indicator"></view>
  9. <text class="section-title">订单信息</text>
  10. </view>
  11. <view class="info-card">
  12. <view class="info-row">
  13. <text class="info-label">采购单编号</text>
  14. <text class="info-value primary">{{ purchaseCode }}</text>
  15. </view>
  16. <view class="info-row">
  17. <text class="info-label">供应商</text>
  18. <text class="info-value">{{ vendorName || '-' }}</text>
  19. </view>
  20. <view class="info-row">
  21. <text class="info-label">合同号</text>
  22. <text class="info-value">{{ contractNumber || '-' }}</text>
  23. </view>
  24. <view class="info-row">
  25. <text class="info-label">总金额</text>
  26. <text class="info-value warning">{{ totalAmount }}</text>
  27. </view>
  28. <view class="info-row">
  29. <text class="info-label">状态</text>
  30. <view class="status-badge" :class="'status-' + status">
  31. {{ statusText }}
  32. </view>
  33. </view>
  34. </view>
  35. </view>
  36. <!-- 订单行列表 -->
  37. <view class="section">
  38. <view class="section-header">
  39. <view class="section-indicator"></view>
  40. <text class="section-title">采购明细</text>
  41. <text class="section-count">共 {{ lineList.length }} 项</text>
  42. </view>
  43. <view class="line-list">
  44. <view v-for="(item, index) in lineList" :key="getLineId(item, index)" class="line-card">
  45. <view class="line-top">
  46. <text class="line-index">{{ index + 1 }}</text>
  47. <view class="line-right">
  48. <text class="item-name">{{ getItemName(item) }}</text>
  49. <text class="item-code-tag">{{ getItemCode(item) }}</text>
  50. </view>
  51. </view>
  52. <view class="line-body">
  53. <view class="line-row">
  54. <view class="line-cell">
  55. <text class="line-label">规格</text>
  56. <text class="line-value">{{ item.specification || '-' }}</text>
  57. </view>
  58. <view class="line-cell">
  59. <text class="line-label">单位</text>
  60. <text class="line-value">{{ item.measureName || '-' }}</text>
  61. </view>
  62. </view>
  63. <view class="line-row">
  64. <view class="line-cell">
  65. <text class="line-label">数量</text>
  66. <text class="line-value qty">{{ item.quantity || '0' }}</text>
  67. </view>
  68. <view class="line-cell">
  69. <text class="line-label">单价</text>
  70. <text class="line-value">{{ getPrice(item) }}</text>
  71. </view>
  72. </view>
  73. <view class="line-row">
  74. <view class="line-cell">
  75. <text class="line-label">金额</text>
  76. <text class="line-value amount">{{ getTotalPrice(item) }}</text>
  77. </view>
  78. </view>
  79. </view>
  80. </view>
  81. </view>
  82. </view>
  83. <!-- 备注 -->
  84. <view class="section" v-if="remark && remark.length > 0">
  85. <view class="section-header">
  86. <view class="section-indicator"></view>
  87. <text class="section-title">备注</text>
  88. </view>
  89. <view class="info-card">
  90. <text class="remark-text">{{ remark }}</text>
  91. </view>
  92. </view>
  93. </scroll-view>
  94. </view>
  95. </template>
  96. <script setup lang="uts">
  97. import { ref, computed, onMounted } from 'vue'
  98. import { getPurchase } from '../../api/purchase/index'
  99. const purchaseId = ref<string>('')
  100. const purchaseCode = ref<string>('')
  101. const vendorName = ref<string>('')
  102. const contractNumber = ref<string>('')
  103. const totalAmount = ref<string>('')
  104. const status = ref<string>('')
  105. const remark = ref<string>('')
  106. const lineList = ref<any[]>([])
  107. const title = computed(() => '采购订单详情')
  108. const statusText = computed(() => {
  109. switch (status.value) {
  110. case 'PREPARE': return '草稿'
  111. case 'CONFIRMED': return '已确认'
  112. case 'FINISHED': return '已完成'
  113. case 'CANCEL': return '已取消'
  114. default: return status.value
  115. }
  116. })
  117. const formatAmount = (val: any): string => {
  118. if (val == null) return '0.00'
  119. const num = Number(val)
  120. if (isNaN(num)) return '0.00'
  121. return num.toFixed(2)
  122. }
  123. const getPrice = (item: any | null): string => {
  124. if (item == null) return '0.00'
  125. const jsonItem = item as UTSJSONObject
  126. return formatAmount(jsonItem['price'])
  127. }
  128. const getTotalPrice = (item: any | null): string => {
  129. if (item == null) return '0.00'
  130. const jsonItem = item as UTSJSONObject
  131. return formatAmount(jsonItem['totalPrice'])
  132. }
  133. const getItemCode = (item: any | null): string => {
  134. if (item == null) return ''
  135. const jsonItem = item as UTSJSONObject
  136. const val = jsonItem['itemCode']
  137. return val != null ? val.toString() : ''
  138. }
  139. const getItemName = (item: any | null): string => {
  140. if (item == null) return ''
  141. const jsonItem = item as UTSJSONObject
  142. const val = jsonItem['itemName']
  143. let name = val != null ? val.toString() : ''
  144. const idx = name.lastIndexOf('(')
  145. if (idx > 0) {
  146. name = name.substring(0, idx)
  147. }
  148. return name
  149. }
  150. const getLineId = (item: any, index: number): string => {
  151. return `line-${index}`
  152. }
  153. const loadDetail = (): void => {
  154. if (purchaseId.value.length === 0) return
  155. getPurchase(purchaseId.value).then((response: any) => {
  156. const res = response as UTSJSONObject
  157. const data = res['data'] as UTSJSONObject
  158. purchaseCode.value = data['purchaseCode'] != null ? data['purchaseCode'].toString() : ''
  159. vendorName.value = data['vendorName'] != null ? data['vendorName'].toString() : ''
  160. contractNumber.value = data['contractNumber'] != null ? data['contractNumber'].toString() : ''
  161. totalAmount.value = formatAmount(data['totalAmount'])
  162. status.value = data['status'] != null ? data['status'].toString() : ''
  163. remark.value = data['remark'] != null ? data['remark'].toString() : ''
  164. const lines = data['wmItemPurchaseLineList'] as any[]
  165. if (lines && lines.length > 0) {
  166. lineList.value = lines
  167. }
  168. }).catch((e) => {
  169. const error = e as UTSError
  170. uni.showToast({ title: error?.message.toString() || '加载失败', icon: 'none' })
  171. })
  172. }
  173. onMounted(() => {
  174. const pages = getCurrentPages()
  175. const currentPage = pages[pages.length - 1]
  176. const options = currentPage.options
  177. if (options != null && options.id != null) {
  178. purchaseId.value = options.id.toString()
  179. loadDetail()
  180. }
  181. })
  182. </script>
  183. <style lang="scss">
  184. .page-container {
  185. flex: 1;
  186. background-color: #e8f0f9;
  187. }
  188. .page-content {
  189. flex: 1;
  190. padding: 20rpx;
  191. padding-bottom: 20rpx;
  192. }
  193. .section {
  194. margin-bottom: 20rpx;
  195. background: #ffffff;
  196. border-radius: 16rpx;
  197. padding: 20rpx;
  198. }
  199. .section-header {
  200. display: flex;
  201. flex-direction: row;
  202. align-items: center;
  203. margin-bottom: 20rpx;
  204. }
  205. .section-indicator {
  206. width: 6rpx;
  207. height: 32rpx;
  208. background-color: #007aff;
  209. border-radius: 3rpx;
  210. margin-right: 12rpx;
  211. }
  212. .section-title {
  213. font-size: 32rpx;
  214. font-weight: bold;
  215. color: #333333;
  216. }
  217. .section-count {
  218. margin-left: auto;
  219. font-size: 24rpx;
  220. color: #999999;
  221. }
  222. .info-card {
  223. background-color: #f8f9fa;
  224. border-radius: 12rpx;
  225. padding: 20rpx;
  226. }
  227. .info-row {
  228. display: flex;
  229. flex-direction: row;
  230. align-items: center;
  231. padding: 16rpx 0;
  232. border-bottom: 1rpx solid #e9ecef;
  233. &:last-child {
  234. border-bottom: none;
  235. }
  236. }
  237. .info-label {
  238. font-size: 28rpx;
  239. color: #666666;
  240. width: 180rpx;
  241. flex-shrink: 0;
  242. }
  243. .info-value {
  244. font-size: 28rpx;
  245. color: #333333;
  246. flex: 1;
  247. &.warning {
  248. color: #faad14;
  249. }
  250. &.primary {
  251. color: #007aff;
  252. font-weight: bold;
  253. }
  254. }
  255. .status-badge {
  256. font-size: 24rpx;
  257. padding: 6rpx 16rpx;
  258. border-radius: 6rpx;
  259. &.status-PREPARE {
  260. background-color: #f0f0f0;
  261. color: #666666;
  262. }
  263. &.status-CONFIRMED {
  264. background-color: #e6f7ff;
  265. color: #1890ff;
  266. }
  267. &.status-FINISHED {
  268. background-color: #f6ffed;
  269. color: #52c41a;
  270. }
  271. &.status-CANCEL {
  272. background-color: #fff1f0;
  273. color: #ff4d4f;
  274. }
  275. }
  276. .line-list {
  277. display: flex;
  278. flex-direction: column;
  279. }
  280. .line-card {
  281. background-color: #ffffff;
  282. border-radius: 12rpx;
  283. padding: 16rpx;
  284. margin-bottom: 12rpx;
  285. }
  286. .line-top {
  287. flex-direction: row;
  288. align-items: center;
  289. margin-bottom: 10rpx;
  290. }
  291. .line-index {
  292. font-size: 40rpx;
  293. font-weight: 700;
  294. color: #007aff;
  295. margin-right: 20rpx;
  296. width: 60rpx;
  297. text-align: center;
  298. }
  299. .line-right {
  300. flex-direction: column;
  301. flex: 1;
  302. }
  303. .item-name {
  304. font-size: 30rpx;
  305. font-weight: 600;
  306. color: #1a1a1a;
  307. }
  308. .item-code-tag {
  309. font-size: 20rpx;
  310. color: #007aff;
  311. margin-top: 4rpx;
  312. }
  313. .line-body {
  314. display: flex;
  315. flex-direction: column;
  316. gap: 8rpx;
  317. }
  318. .line-row {
  319. display: flex;
  320. flex-direction: row;
  321. gap: 12rpx;
  322. }
  323. .line-cell {
  324. display: flex;
  325. flex-direction: row;
  326. align-items: center;
  327. flex: 1;
  328. padding: 8rpx 12rpx;
  329. background-color: #f8f9fa;
  330. border-radius: 8rpx;
  331. }
  332. .line-label {
  333. font-size: 22rpx;
  334. color: #999999;
  335. margin-right: 4rpx;
  336. }
  337. .line-value {
  338. font-size: 24rpx;
  339. color: #333333;
  340. &.qty {
  341. color: #007aff;
  342. }
  343. &.amount {
  344. color: #faad14;
  345. }
  346. }
  347. .remark-text {
  348. font-size: 28rpx;
  349. color: #333333;
  350. line-height: 1.6;
  351. }
  352. </style>