material-detail-table.vue 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  1. <template>
  2. <!-- 物料明细表格容器 -->
  3. <div class="material-detail-container">
  4. <!-- 表格头部操作区域 -->
  5. <div class="material-detail-header">
  6. <div class="header-left">
  7. <span class="section-title">物料明细</span>
  8. <el-tag
  9. v-if="materialDetails.length > 0"
  10. type="info"
  11. size="small"
  12. class="count-tag"
  13. >
  14. 共 {{ materialDetails.length }} 条
  15. </el-tag>
  16. </div>
  17. <div class="header-right">
  18. <!-- 物料选择区域 -->
  19. <div class="material-select-container">
  20. <el-select
  21. v-model="selectedMaterialId"
  22. placeholder="请选择物料"
  23. filterable
  24. remote
  25. reserve-keyword
  26. :remote-method="remoteSearchMaterial"
  27. :loading="materialLoading"
  28. size="small"
  29. style="width: 300px; margin-right: 8px;"
  30. clearable
  31. >
  32. <el-option
  33. v-for="item in materialOptions"
  34. :key="item.id"
  35. :label="`${item.itemName} (${item.itemCode})`"
  36. :value="item.id"
  37. >
  38. <span style="float: left">{{ item.itemName }}</span>
  39. <span style="float: right; color: #8492a6; font-size: 13px">{{ item.itemCode }}</span>
  40. </el-option>
  41. </el-select>
  42. <el-button
  43. type="primary"
  44. icon="el-icon-plus"
  45. size="small"
  46. :disabled="!selectedMaterialId"
  47. @click="handleImportSelectedMaterial"
  48. >
  49. 增加
  50. </el-button>
  51. <el-button
  52. v-if="editMode && selectedRows.length > 0"
  53. type="danger"
  54. icon="el-icon-delete"
  55. size="small"
  56. @click="handleBatchDelete"
  57. >
  58. 批量删除 ({{ selectedRows.length }})
  59. </el-button>
  60. </div>
  61. </div>
  62. </div>
  63. <!-- 物料明细表格 -->
  64. <div class="material-detail-content">
  65. <avue-crud
  66. ref="materialDetailCrud"
  67. :data="currentPageData"
  68. :option="tableOption"
  69. :page.sync="page"
  70. @refresh-change="handleRefresh"
  71. @current-change="handleCurrentChange"
  72. @size-change="handleSizeChange"
  73. @selection-change="handleSelectionChange"
  74. @row-del="handleRowDelete"
  75. @row-update="handleRowUpdate"
  76. >
  77. <!-- 可用数量自定义模板 -->
  78. <template slot="availableQuantity" slot-scope="scope">
  79. <span>{{ formatFloatNumber(scope.row.availableQuantity) }}</span>
  80. </template>
  81. <!-- 确认数量自定义模板 -->
  82. <template slot="confirmQuantity" slot-scope="scope">
  83. <span>{{ formatFloatNumber(scope.row.confirmQuantity) }}</span>
  84. </template>
  85. <!-- 单价自定义模板 -->
  86. <template slot="unitPrice" slot-scope="scope">
  87. <el-input
  88. v-if="editMode && isRowEditable(scope.row)"
  89. v-model="scope.row.unitPrice"
  90. size="mini"
  91. style="width: 100%"
  92. placeholder="请输入单价"
  93. :disabled="true"
  94. @input="validateFloatInput($event, scope.row, 'unitPrice')"
  95. @blur="handleUnitPriceBlur(scope.row, scope.$index)"
  96. />
  97. <span v-else>{{ formatUnitPrice(scope.row.unitPrice) }}</span>
  98. </template>
  99. <!-- 订单数量自定义模板 -->
  100. <template slot="orderQuantity" slot-scope="scope">
  101. <el-input
  102. v-if="editMode && isRowEditable(scope.row)"
  103. v-model="scope.row.orderQuantity"
  104. size="mini"
  105. style="width: 100%"
  106. placeholder="请输入订单数量"
  107. @input="validateIntegerInput($event, scope.row, 'orderQuantity')"
  108. @blur="handleQuantityBlur(scope.row, scope.$index)"
  109. />
  110. <span v-else>{{ formatIntegerNumber(scope.row.orderQuantity) }}</span>
  111. </template>
  112. <!-- 税率自定义模板 -->
  113. <template slot="taxRate" slot-scope="scope">
  114. <el-input
  115. v-if="editMode && isRowEditable(scope.row)"
  116. v-model="scope.row.taxRate"
  117. size="mini"
  118. style="width: 100%"
  119. placeholder="请输入税率"
  120. :disabled="true"
  121. @input="validateFloatInput($event, scope.row, 'taxRate', 0, 100)"
  122. @blur="validateAndFormatFloatOnBlur(scope.row, 'taxRate', 0, 100); handleTaxRateChange(scope.row, scope.$index)"
  123. />
  124. <span v-else>{{ formatTaxRate(scope.row.taxRate, false) }}%</span>
  125. </template>
  126. <!-- 税额自定义模板 -->
  127. <template slot="taxAmount" slot-scope="scope">
  128. <el-input
  129. v-if="editMode && isRowEditable(scope.row)"
  130. v-model="scope.row.taxAmount"
  131. size="mini"
  132. style="width: 100%"
  133. placeholder="请输入税额"
  134. :disabled="true"
  135. @input="validateFloatInput($event, scope.row, 'taxAmount')"
  136. @blur="validateAndFormatFloatOnBlur(scope.row, 'taxAmount'); handleTaxAmountChange(scope.row, scope.$index)"
  137. />
  138. <span v-else>{{ formatAmount(scope.row.taxAmount, false) }}</span>
  139. </template>
  140. <!-- 总金额自定义模板 -->
  141. <template slot="totalAmount" slot-scope="scope">
  142. <el-input
  143. v-if="editMode && isRowEditable(scope.row)"
  144. v-model="scope.row.totalAmount"
  145. size="mini"
  146. style="width: 100%"
  147. placeholder="请输入总金额"
  148. :disabled="true"
  149. @input="validateFloatInput($event, scope.row, 'totalAmount')"
  150. @blur="validateAndFormatFloatOnBlur(scope.row, 'totalAmount'); handleTotalAmountChange(scope.row, scope.$index)"
  151. />
  152. <span v-else>{{ formatAmount(scope.row.totalAmount, false) }}</span>
  153. </template>
  154. <!-- 明细状态列自定义渲染 -->
  155. <template slot="status" slot-scope="{ row }">
  156. <el-tag
  157. :type="getStatusTagType(row.itemStatus)"
  158. size="mini"
  159. >
  160. {{ getStatusText(row.itemStatus) }}
  161. </el-tag>
  162. </template>
  163. <!-- 操作列自定义渲染 -->
  164. <template slot="menu" slot-scope="{ row, index }">
  165. <el-button
  166. v-if="row.isDeletable"
  167. type="text"
  168. size="mini"
  169. icon="el-icon-delete"
  170. class="delete-btn"
  171. @click="handleDeleteMaterial(row, index)"
  172. >
  173. 删除
  174. </el-button>
  175. </template>
  176. </avue-crud>
  177. </div>
  178. </div>
  179. </template>
  180. <script>
  181. import materialDetailMixin from './material-detail-mixin';
  182. export default {
  183. name: 'MaterialDetailTable',
  184. mixins: [materialDetailMixin],
  185. }
  186. </script>
  187. <style scoped>
  188. .material-detail-container {
  189. padding: 20px;
  190. }
  191. .material-detail-header {
  192. display: flex;
  193. justify-content: space-between;
  194. align-items: center;
  195. margin-bottom: 16px;
  196. padding-bottom: 12px;
  197. border-bottom: 1px solid #ebeef5;
  198. }
  199. .header-left {
  200. display: flex;
  201. align-items: center;
  202. gap: 12px;
  203. }
  204. .section-title {
  205. font-size: 16px;
  206. font-weight: 600;
  207. color: #303133;
  208. }
  209. .count-tag {
  210. font-size: 12px;
  211. }
  212. .header-right {
  213. display: flex;
  214. gap: 8px;
  215. margin-right: 32px;
  216. }
  217. .material-select-container {
  218. display: flex;
  219. align-items: center;
  220. gap: 8px;
  221. }
  222. .material-select-container .el-select {
  223. min-width: 300px;
  224. }
  225. .material-select-container .el-button {
  226. white-space: nowrap;
  227. }
  228. .material-detail-content {
  229. background-color: #ffffff;
  230. border-radius: 4px;
  231. }
  232. .delete-btn {
  233. color: #f56c6c;
  234. }
  235. .delete-btn:hover {
  236. color: #f78989;
  237. }
  238. .amount-text {
  239. font-weight: 600;
  240. color: #409eff;
  241. }
  242. .dialog-footer {
  243. text-align: right;
  244. }
  245. /* 响应式设计 */
  246. @media (max-width: 768px) {
  247. .material-detail-container {
  248. padding: 12px;
  249. }
  250. .material-detail-header {
  251. flex-direction: column;
  252. gap: 12px;
  253. align-items: flex-start;
  254. }
  255. .header-right {
  256. width: 100%;
  257. justify-content: flex-end;
  258. }
  259. }
  260. </style>