PurchaseSelector.vue 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. <template>
  2. <view>
  3. <u-form-item label="采购编号" prop="purchaseNo" :required="true">
  4. <u--input :value="currentValue" placeholder="请选择采购编号" readonly :disabled="disabled"
  5. @focus="openPicker"></u--input>
  6. </u-form-item>
  7. <view class="selector-mask" :class="{ show: visible }" @tap="closePicker"></view>
  8. <view class="selector-panel" :class="{ show: visible }">
  9. <view class="selector-header">
  10. <view class="selector-action" @tap="closePicker">取消</view>
  11. <view class="selector-title">选择采购单</view>
  12. <view class="selector-action selector-action-placeholder">取消</view>
  13. </view>
  14. <view class="selector-search">
  15. <u-search :show-action="false" v-model="keyword" placeholder="搜索采购编号或采购名称"></u-search>
  16. </view>
  17. <scroll-view scroll-y class="selector-list">
  18. <view v-if="loading" class="selector-state">加载中...</view>
  19. <view v-else-if="filteredList.length === 0" class="selector-state">暂无可选采购单</view>
  20. <view v-else class="selector-list-inner">
  21. <view class="selector-card" v-for="item in filteredList" :key="item.id || item.purchaseNo"
  22. @tap="selectItem(item)">
  23. <view class="selector-name">{{ item.purchaseNo }}</view>
  24. <view class="selector-desc">{{ item.purchaseSketch || '未填写采购名称' }}</view>
  25. <view class="selector-meta">
  26. <text v-if="item.handledByName">经办人:{{ item.handledByName }}</text>
  27. <text v-if="item.createDate">申请时间:{{ item.createDate }}</text>
  28. </view>
  29. </view>
  30. </view>
  31. </scroll-view>
  32. </view>
  33. </view>
  34. </template>
  35. <script>
  36. import WareHouseService from '@/api/psi/WareHouseService'
  37. export default {
  38. name: 'PurchaseSelector',
  39. props: {
  40. inputForm: Object,
  41. disabled: Boolean
  42. },
  43. data() {
  44. return {
  45. visible: false,
  46. loading: false,
  47. keyword: '',
  48. purchaseList: [],
  49. wareHouseService: null
  50. }
  51. },
  52. computed: {
  53. currentValue() {
  54. return (this.inputForm && this.inputForm.purchaseNo) || ''
  55. },
  56. filteredList() {
  57. const keyword = (this.keyword || '').trim().toLowerCase()
  58. if (!keyword) {
  59. return this.purchaseList
  60. }
  61. return this.purchaseList.filter(item => {
  62. const purchaseNo = (item.purchaseNo || '').toLowerCase()
  63. const purchaseSketch = (item.purchaseSketch || '').toLowerCase()
  64. return purchaseNo.includes(keyword) || purchaseSketch.includes(keyword)
  65. })
  66. }
  67. },
  68. created() {
  69. this.wareHouseService = new WareHouseService()
  70. },
  71. methods: {
  72. async openPicker() {
  73. if (this.disabled) {
  74. return
  75. }
  76. this.visible = true
  77. this.keyword = ''
  78. if (!this.purchaseList.length) {
  79. await this.loadPurchaseList()
  80. }
  81. },
  82. closePicker() {
  83. this.visible = false
  84. },
  85. async loadPurchaseList() {
  86. this.loading = true
  87. try {
  88. const data = await this.wareHouseService.reimbursementList({
  89. current: 1,
  90. size: 50,
  91. status: '5',
  92. orders: []
  93. })
  94. this.purchaseList = (data && data.records) || []
  95. } catch (e) {
  96. this.purchaseList = []
  97. uni.showToast({
  98. title: '采购单加载失败',
  99. icon: 'none'
  100. })
  101. } finally {
  102. this.loading = false
  103. }
  104. },
  105. selectItem(item) {
  106. this.$emit('selected', item)
  107. this.closePicker()
  108. }
  109. }
  110. }
  111. </script>
  112. <style scoped>
  113. .selector-mask {
  114. position: fixed;
  115. inset: 0;
  116. background: rgba(0, 0, 0, 0.35);
  117. opacity: 0;
  118. visibility: hidden;
  119. transition: all 0.25s ease;
  120. z-index: 1000;
  121. }
  122. .selector-mask.show {
  123. opacity: 1;
  124. visibility: visible;
  125. }
  126. .selector-panel {
  127. position: fixed;
  128. left: 0;
  129. right: 0;
  130. bottom: 0;
  131. height: 72vh;
  132. background: #f7f9fc;
  133. border-radius: 28rpx 28rpx 0 0;
  134. transform: translateY(100%);
  135. transition: transform 0.25s ease;
  136. z-index: 1001;
  137. display: flex;
  138. flex-direction: column;
  139. }
  140. .selector-panel.show {
  141. transform: translateY(0);
  142. }
  143. .selector-header {
  144. display: flex;
  145. align-items: center;
  146. justify-content: space-between;
  147. padding: 28rpx 32rpx 16rpx;
  148. background: #fff;
  149. border-bottom: 1rpx solid #eef2f7;
  150. }
  151. .selector-title {
  152. font-size: 32rpx;
  153. font-weight: 600;
  154. color: #1f2937;
  155. }
  156. .selector-action {
  157. min-width: 80rpx;
  158. font-size: 28rpx;
  159. color: #2979ff;
  160. }
  161. .selector-action-placeholder {
  162. opacity: 0;
  163. }
  164. .selector-search {
  165. padding: 20rpx 24rpx 8rpx;
  166. background: #fff;
  167. }
  168. .selector-list {
  169. flex: 1;
  170. padding: 16rpx 24rpx 32rpx;
  171. }
  172. .selector-list-inner {
  173. display: flex;
  174. flex-direction: column;
  175. gap: 16rpx;
  176. }
  177. .selector-state {
  178. padding-top: 120rpx;
  179. text-align: center;
  180. font-size: 28rpx;
  181. color: #94a3b8;
  182. }
  183. .selector-card {
  184. background: #fff;
  185. border-radius: 20rpx;
  186. padding: 24rpx;
  187. box-shadow: 0 8rpx 24rpx rgba(15, 23, 42, 0.06);
  188. }
  189. .selector-name {
  190. font-size: 30rpx;
  191. font-weight: 600;
  192. color: #0f172a;
  193. }
  194. .selector-desc {
  195. margin-top: 8rpx;
  196. font-size: 26rpx;
  197. color: #334155;
  198. }
  199. .selector-meta {
  200. display: flex;
  201. flex-direction: column;
  202. gap: 8rpx;
  203. margin-top: 12rpx;
  204. font-size: 24rpx;
  205. color: #64748b;
  206. }
  207. </style>