You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

3796 lines
130 KiB

4 weeks ago
4 weeks ago
1 month ago
4 weeks ago
1 month ago
1 month ago
4 months ago
2 months ago
4 months ago
4 months ago
4 weeks ago
2 months ago
4 months ago
1 month ago
1 month ago
4 weeks ago
4 months ago
4 months ago
4 months ago
2 months ago
3 weeks ago
2 months ago
3 weeks ago
2 months ago
3 weeks ago
2 months ago
3 weeks ago
2 months ago
1 month ago
1 month ago
4 months ago
4 months ago
1 month ago
4 months ago
1 month ago
4 months ago
4 months ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
4 months ago
1 month ago
4 months ago
3 weeks ago
4 months ago
3 months ago
4 months ago
3 months ago
4 months ago
3 months ago
1 month ago
4 weeks ago
4 weeks ago
4 weeks ago
1 month ago
2 months ago
1 month ago
2 weeks ago
1 month ago
2 weeks ago
4 weeks ago
2 weeks ago
4 weeks ago
2 weeks ago
4 weeks ago
2 weeks ago
4 weeks ago
1 month ago
2 weeks ago
4 weeks ago
2 weeks ago
4 weeks ago
1 month ago
2 weeks ago
1 month ago
3 weeks ago
1 month ago
3 weeks ago
3 weeks ago
1 month ago
4 weeks ago
3 weeks ago
1 month ago
2 months ago
4 weeks ago
2 months ago
4 weeks ago
2 months ago
3 weeks ago
3 weeks ago
2 months ago
1 month ago
2 months ago
1 month ago
1 month ago
4 weeks ago
4 months ago
3 weeks ago
1 month ago
1 month ago
2 months ago
4 weeks ago
3 weeks ago
4 weeks ago
2 weeks ago
4 weeks ago
2 weeks ago
2 weeks ago
4 weeks ago
1 month ago
4 weeks ago
4 months ago
4 months ago
4 months ago
1 month ago
1 month ago
4 weeks ago
4 months ago
4 months ago
4 weeks ago
1 month ago
4 weeks ago
1 month ago
1 month ago
1 month ago
2 weeks ago
3 weeks ago
3 weeks ago
1 month ago
4 months ago
2 months ago
4 months ago
1 month ago
3 weeks ago
4 months ago
1 month ago
1 month ago
1 month ago
2 months ago
4 weeks ago
2 months ago
1 month ago
1 month ago
1 month ago
4 months ago
4 months ago
3 months ago
4 months ago
4 months ago
4 months ago
1 month ago
4 months ago
4 weeks ago
4 months ago
4 months ago
4 months ago
4 weeks ago
1 month ago
4 months ago
4 months ago
4 months ago
1 month ago
4 months ago
1 month ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
2 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
3 weeks ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 weeks ago
1 month ago
4 weeks ago
2 weeks ago
4 weeks ago
2 weeks ago
4 weeks ago
2 weeks ago
4 weeks ago
2 weeks ago
4 weeks ago
4 weeks ago
1 month ago
1 month ago
4 weeks ago
3 weeks ago
3 weeks ago
3 weeks ago
2 months ago
1 month ago
4 weeks ago
4 weeks ago
4 weeks ago
1 month ago
4 weeks ago
1 month ago
1 month ago
3 weeks ago
1 month ago
3 weeks ago
1 month ago
4 weeks ago
2 weeks ago
4 weeks ago
2 weeks ago
4 weeks ago
2 weeks ago
4 weeks ago
2 weeks ago
4 weeks ago
2 weeks ago
4 weeks ago
2 weeks ago
4 weeks ago
2 weeks ago
4 weeks ago
3 weeks ago
2 weeks ago
2 weeks ago
4 weeks ago
2 weeks ago
4 weeks ago
2 weeks ago
4 weeks ago
2 weeks ago
4 weeks ago
3 weeks ago
2 weeks ago
3 weeks ago
2 weeks ago
3 weeks ago
2 weeks ago
3 weeks ago
2 weeks ago
3 weeks ago
2 weeks ago
3 weeks ago
2 weeks ago
3 weeks ago
2 weeks ago
3 weeks ago
4 weeks ago
2 months ago
3 weeks ago
3 weeks ago
2 months ago
4 months ago
4 months ago
4 months ago
3 weeks ago
3 months ago
3 weeks ago
2 months ago
4 months ago
2 months ago
3 weeks ago
4 weeks ago
3 weeks ago
3 weeks ago
4 weeks ago
3 weeks ago
4 weeks ago
3 weeks ago
4 weeks ago
3 weeks ago
4 weeks ago
3 weeks ago
4 weeks ago
2 months ago
4 weeks ago
2 months ago
1 month ago
3 weeks ago
2 weeks ago
3 weeks ago
1 month ago
2 weeks ago
1 month ago
2 weeks ago
1 month ago
2 weeks ago
1 month ago
2 months ago
4 weeks ago
2 months ago
4 months ago
4 weeks ago
4 months ago
2 months ago
4 months ago
3 months ago
4 months ago
4 weeks ago
4 months ago
4 weeks ago
1 month ago
4 months ago
1 month ago
2 weeks ago
1 month ago
4 months ago
3 months ago
4 months ago
3 months ago
4 months ago
3 months ago
4 months ago
3 months ago
4 months ago
4 weeks ago
4 months ago
1 month ago
3 months ago
3 months ago
3 months ago
4 months ago
3 months ago
4 months ago
3 months ago
4 months ago
3 months ago
1 month ago
1 month ago
3 weeks ago
1 month ago
3 weeks ago
1 month ago
4 months ago
4 weeks ago
3 months ago
1 month ago
3 months ago
4 months ago
3 months ago
4 months ago
3 months ago
4 months ago
3 months ago
4 months ago
3 months ago
4 months ago
3 months ago
3 months ago
3 months ago
3 months ago
4 months ago
4 months ago
1 month ago
1 month ago
4 weeks ago
1 month ago
4 weeks ago
1 month ago
1 month ago
1 month ago
2 weeks ago
1 month ago
4 weeks ago
1 month ago
4 months ago
1 month ago
1 month ago
1 month ago
3 months ago
4 months ago
3 months ago
4 months ago
4 weeks ago
4 weeks ago
4 weeks ago
1 month ago
4 weeks ago
4 weeks ago
4 weeks ago
1 month ago
4 weeks ago
4 weeks ago
1 month ago
1 month ago
3 weeks ago
1 month ago
3 weeks ago
1 month ago
3 weeks ago
4 weeks ago
1 month ago
4 weeks ago
1 month ago
3 weeks ago
1 month ago
4 weeks ago
1 month ago
3 weeks ago
2 weeks ago
3 weeks ago
2 weeks ago
3 weeks ago
2 weeks ago
3 weeks ago
1 month ago
3 weeks ago
1 month ago
4 weeks ago
3 weeks ago
4 weeks ago
3 weeks ago
3 weeks ago
4 weeks ago
2 weeks ago
1 month ago
4 weeks ago
1 month ago
1 month ago
3 weeks ago
1 month ago
4 weeks ago
2 weeks ago
2 weeks ago
4 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
4 weeks ago
2 weeks ago
4 weeks ago
2 weeks ago
4 weeks ago
2 weeks ago
4 weeks ago
3 weeks ago
2 weeks ago
1 month ago
3 weeks ago
1 month ago
4 weeks ago
1 month ago
2 weeks ago
1 month ago
3 weeks ago
1 month ago
2 weeks ago
2 weeks ago
1 month ago
4 weeks ago
1 month ago
2 weeks ago
1 month ago
2 weeks ago
1 month ago
2 weeks ago
1 month ago
2 weeks ago
1 month ago
2 weeks ago
1 month ago
2 weeks ago
1 month ago
2 weeks ago
1 month ago
2 weeks ago
1 month ago
2 weeks ago
1 month ago
2 weeks ago
1 month ago
2 weeks ago
1 month ago
2 weeks ago
4 weeks ago
2 weeks ago
4 weeks ago
2 weeks ago
4 weeks ago
2 weeks ago
4 weeks ago
2 weeks ago
4 weeks ago
1 month ago
3 weeks ago
1 month ago
3 weeks ago
1 month ago
3 weeks ago
1 month ago
1 month ago
3 weeks ago
3 months ago
1 month ago
2 weeks ago
4 weeks ago
2 weeks ago
1 month ago
2 weeks ago
  1. <template>
  2. <div ref="pageRoot" class="mod-config">
  3. <com-po-order-customer-picker
  4. :site="searchData.site"
  5. v-model="searchData.customer"
  6. @change="onSessionCustomerChange" />
  7. <el-form :inline="true" label-position="top" :model="searchData">
  8. <el-form-item label="PO No">
  9. <el-input v-model="searchData.poNo" clearable style="width: 140px" @keyup.enter.native="handleSearch"></el-input>
  10. </el-form-item>
  11. <el-form-item label="订单类型">
  12. <el-select v-model="searchData.orderType" placeholder="请选择" style="width: 120px" clearable>
  13. <el-option
  14. v-for="item in poOrderTypeSearchOptions"
  15. :key="item.value"
  16. :label="item.label"
  17. :value="item.value" />
  18. </el-select>
  19. </el-form-item>
  20. <el-form-item>
  21. <span style="cursor: pointer" slot="label" @click="openSearchBaseList(2017)"><a href="javascript:void(0)">Part No</a></span>
  22. <el-input v-model="searchData.partNo" clearable style="width: 120px" @keyup.enter.native="handleSearch"></el-input>
  23. </el-form-item>
  24. <el-form-item>
  25. <span style="cursor: pointer" slot="label" @click="openSearchBaseList(520)"><a href="javascript:void(0)">供应商编码</a></span>
  26. <el-input v-model="searchData.supplierNo" clearable style="width: 120px" @keyup.enter.native="handleSearch"></el-input>
  27. </el-form-item>
  28. <el-form-item label="SKU">
  29. <el-input v-model="searchData.sku" clearable style="width: 120px" @keyup.enter.native="handleSearch"></el-input>
  30. </el-form-item>
  31. <el-form-item label="Status">
  32. <el-select v-model="searchData.status" placeholder="请选择" style="width:100px" clearable>
  33. <el-option label="全部" value=""></el-option>
  34. <el-option
  35. v-for="item in poOrderStatusOptions"
  36. :key="item.value"
  37. :label="item.label"
  38. :value="item.value" />
  39. </el-select>
  40. </el-form-item>
  41. </el-form>
  42. <el-form :inline="true" label-position="top" style="margin-top:10px ">
  43. <el-form-item>
  44. <el-button type="primary" @click="handleSearch()" v-if="!editMode" :disabled="!searchData.customer">查询</el-button>
  45. <el-button type="primary" style="margin-left: 2px" @click="exportExcel" :disabled="editMode || !searchData.customer">导出</el-button>
  46. <el-button type="warning" plain @click="advancedQueryVisible = true" v-if="!editMode" style="margin-left: 2px">高级查询</el-button>
  47. <el-button type="primary" style="margin-left: 2px" @click="toggleEditMode">
  48. {{ editMode ? '确定' : '编辑' }}
  49. </el-button>
  50. <el-button v-if="editMode" style="margin-left: 2px" @click="cancelEditMode">取消</el-button>
  51. <el-button type="primary" style="margin-left: 2px" @click="openAddDialog" v-if="!editMode">新增</el-button>
  52. <!-- <el-button type="primary" style="margin-left: 2px" @click="exportModal">导入</el-button>-->
  53. </el-form-item>
  54. </el-form>
  55. <el-table
  56. ref="poTable"
  57. :height="height"
  58. :data="masterRows"
  59. border
  60. highlight-current-row
  61. style="width: 100%;"
  62. @current-change="onTableCurrentChange">
  63. <el-table-column
  64. v-if="!editMode"
  65. fixed="left"
  66. label="操作"
  67. header-align="center"
  68. align="center"
  69. width="90">
  70. <template slot-scope="scope">
  71. <el-link
  72. v-if="canIssuePoOrder(scope.row.status)"
  73. type="primary"
  74. @click.stop="handleIssuePoOrder(scope.row)">下达</el-link>
  75. <el-link
  76. v-else-if="canShipPoOrder(scope.row.status)"
  77. type="primary"
  78. @click.stop="handleShipPoOrder(scope.row)">shipped</el-link>
  79. <el-link
  80. v-else-if="canClosePoOrder(scope.row.status)"
  81. type="primary"
  82. @click.stop="handleClosePoOrder(scope.row)">关闭</el-link>
  83. <el-link
  84. v-else-if="canOpenPoOrder(scope.row.status)"
  85. type="primary"
  86. @click.stop="handleOpenPoOrder(scope.row)">打开</el-link>
  87. </template>
  88. </el-table-column>
  89. <el-table-column
  90. v-for="(item, index) in visibleColumns"
  91. :key="item.serialNumber || index"
  92. :sortable="item.columnSortable"
  93. :prop="item.columnProp"
  94. :header-align="item.headerAlign"
  95. :show-overflow-tooltip="item.showOverflowTooltip && (!editMode || (!item.editAble && !isChooserEditColumn(item.columnProp)))"
  96. :align="item.align"
  97. :fixed="item.fixed === '' ? false : item.fixed"
  98. :min-width="item.columnWidth"
  99. :label="item.columnLabel">
  100. <template slot-scope="scope">
  101. <template v-if="editMode && item.columnProp==='supplierName' && canEditCell(scope.row, item)">
  102. <el-input
  103. v-model="scope.row.supplierName"
  104. size="mini"
  105. style="width: 100%; cursor: pointer"
  106. readonly
  107. placeholder="请选择供应商"
  108. @click.native="openSupplierSelect(scope.row)">
  109. <i slot="suffix" class="el-icon-search" style="cursor: pointer" @click.stop="openSupplierSelect(scope.row)"></i>
  110. </el-input>
  111. </template>
  112. <template v-else-if="editMode && item.columnProp==='partNo' && canEditCell(scope.row, item)">
  113. <el-input
  114. v-model="scope.row.partNo"
  115. size="mini"
  116. style="width: 100%; cursor: pointer"
  117. readonly
  118. placeholder="请选择物料"
  119. @click.native="openPartNoSelect(scope.row)">
  120. <i slot="suffix" class="el-icon-search" style="cursor: pointer" @click.stop="openPartNoSelect(scope.row)"></i>
  121. </el-input>
  122. </template>
  123. <template v-else-if="editMode && item.columnProp==='departure' && canEditCell(scope.row, item)">
  124. <el-input
  125. v-model="scope.row.departure"
  126. size="mini"
  127. style="width: 100%; cursor: pointer"
  128. readonly
  129. placeholder="请选择始发港"
  130. @click.native="openDepartureSelect(scope.row)">
  131. <i slot="suffix" class="el-icon-search" style="cursor: pointer" @click.stop="openDepartureSelect(scope.row)"></i>
  132. </el-input>
  133. </template>
  134. <template v-else-if="editMode && item.columnProp==='destination' && canEditCell(scope.row, item)">
  135. <el-input
  136. v-model="scope.row.destination"
  137. size="mini"
  138. style="width: 100%; cursor: pointer"
  139. readonly
  140. placeholder="请选择目的港"
  141. @click.native="openDestinationSelect(scope.row)">
  142. <i slot="suffix" class="el-icon-search" style="cursor: pointer" @click.stop="openDestinationSelect(scope.row)"></i>
  143. </el-input>
  144. </template>
  145. <template v-else-if="editMode && item.columnProp==='shipVia' && canEditCell(scope.row, item)">
  146. <el-input
  147. v-model="scope.row.shipVia"
  148. size="mini"
  149. style="width: 100%; cursor: pointer"
  150. readonly
  151. placeholder="请选择运输方式"
  152. @click.native="openShipViaSelect(scope.row)">
  153. <i slot="suffix" class="el-icon-search" style="cursor: pointer" @click.stop="openShipViaSelect(scope.row)"></i>
  154. </el-input>
  155. </template>
  156. <template v-else-if="editMode && canEditCell(scope.row, item)">
  157. <!-- 对于 confirmed wantReceiveDate 字段仅在新增行时可编辑 -->
  158. <template v-if="(item.columnProp === 'confirmed' || item.columnProp === 'wantReceiveDate') && !isNewRow(scope.row)">
  159. <span>{{ scope.row[item.columnProp] }}</span>
  160. </template>
  161. <!-- 对于 inspection realCrd 字段仅在修改时非新增行可编辑 -->
  162. <template v-else-if="(item.columnProp === 'inspection' || item.columnProp === 'realCrd') && isNewRow(scope.row)">
  163. <span>{{ scope.row[item.columnProp] }}</span>
  164. </template>
  165. <template v-else>
  166. <!-- 下拉框 -->
  167. <el-select
  168. v-if="item.editType === 'select' || item.editType === 'dict-select'"
  169. v-model="scope.row[item.columnProp]"
  170. size="mini"
  171. style="width: 100%"
  172. clearable
  173. @change="onCellChange(scope.row)">
  174. <el-option
  175. v-for="opt in getSelectOptionsForColumn(item)"
  176. :key="String(opt.value)"
  177. :label="opt.label"
  178. :value="opt.value">
  179. </el-option>
  180. </el-select>
  181. <!-- 数字输入框 -->
  182. <el-input
  183. v-else-if="item.editType === 'num'"
  184. v-model="scope.row[item.columnProp]"
  185. type="number"
  186. size="mini"
  187. @change="onCellChange(scope.row)">
  188. </el-input>
  189. <!-- 时间控件 -->
  190. <el-date-picker
  191. v-else-if="item.editType === 'date'"
  192. v-model="scope.row[item.columnProp]"
  193. type="date"
  194. size="mini"
  195. style="width: 100%"
  196. value-format="yyyy-MM-dd"
  197. placeholder="选择日期"
  198. @change="onCellChange(scope.row)">
  199. </el-date-picker>
  200. <el-date-picker
  201. v-else-if="item.editType === 'datetime'"
  202. v-model="scope.row[item.columnProp]"
  203. type="datetime"
  204. size="mini"
  205. style="width: 100%"
  206. value-format="yyyy-MM-dd HH:mm:ss"
  207. placeholder="选择日期时间"
  208. @change="onCellChange(scope.row)">
  209. </el-date-picker>
  210. <!-- 普通文本输入框 -->
  211. <el-input
  212. v-else
  213. v-model="scope.row[item.columnProp]"
  214. size="mini"
  215. @change="onCellChange(scope.row)">
  216. </el-input>
  217. </template>
  218. </template>
  219. <span v-else>{{ formatColumnCell(scope.row, item) }}</span>
  220. </template>
  221. </el-table-column>
  222. </el-table>
  223. <el-pagination
  224. ref="mainPager"
  225. @size-change="sizeChangeHandle"
  226. @current-change="currentChangeHandle"
  227. :current-page="pageIndex"
  228. :page-sizes="[20, 50, 100, 200, 500]"
  229. :page-size="pageSize"
  230. :total="totalPage"
  231. layout="total, sizes, prev, pager, next, jumper">
  232. </el-pagination>
  233. <!-- 导入弹窗 -->
  234. <el-dialog title="PO订单数据导入" :visible.sync="uploadDialogVisible" :height="height" width="800px">
  235. <!-- 选择文件 -->
  236. <div style="margin-bottom:15px;display:flex;align-items:center">
  237. <span style="margin-right:10px">请选择文件</span>
  238. <el-input v-model="uploadFileName" style="width:400px;margin-right:10px" readonly></el-input>
  239. <el-upload ref="upload" action="/api/pODetail/upload"
  240. :show-file-list="false" :on-change="handleFileChange"
  241. :auto-upload="false">
  242. <el-button type="primary">选择文件</el-button>
  243. </el-upload>
  244. <el-button type="success" style="margin-left:10px" @click="previewUpload">上传</el-button>
  245. <el-button type="primary" @click="downloadTemplate" style="margin-left:10px">
  246. <i class="el-icon-download"></i> 下载模板
  247. </el-button>
  248. </div>
  249. <!-- 跳过列提示 -->
  250. <el-alert
  251. v-if="uploadSkippedColumns.length"
  252. type="warning"
  253. :closable="false"
  254. show-icon
  255. style="margin-bottom:10px"
  256. :title="'已跳过列:' + uploadSkippedColumns.join('、')"
  257. />
  258. <div v-if="uploadColumnMappingEntries.length" style="margin-bottom:10px;font-size:12px;color:#606266">
  259. 列映射
  260. <span v-for="item in uploadColumnMappingEntries" :key="item.header" style="margin-right:12px">
  261. {{ item.header }} {{ item.fieldCode }}
  262. </span>
  263. </div>
  264. <!-- 预览表格 -->
  265. <el-table
  266. :data="uploadPreviewList"
  267. border
  268. height="250"
  269. style="width:100%;margin-top:10px"
  270. >
  271. <el-table-column
  272. v-for="col in uploadPreviewColumns"
  273. :key="col.prop"
  274. :prop="col.prop"
  275. :label="col.label"
  276. align="center"
  277. min-width="100"
  278. />
  279. </el-table>
  280. <!-- 按钮 -->
  281. <div style="margin-top:20px;text-align:center">
  282. <el-button type="primary" @click="confirmUpload">保存</el-button>
  283. <el-button @click="uploadDialogVisible=false" style="margin-right:30px">关闭</el-button>
  284. </div>
  285. </el-dialog>
  286. <ChooselistEam ref="eamList" @getBaseData="getEamData" />
  287. <po-order-advanced-query
  288. :visible.sync="advancedQueryVisible"
  289. :advanced-query-data="advancedQueryData"
  290. @query="handleSearch" />
  291. <!-- 新增 PO 订单主记录 + 明细暂存 -->
  292. <el-dialog title="新增 PO 订单" :visible.sync="addDialogVisible" width="1180px" top="4vh"
  293. :close-on-click-modal="false" append-to-body>
  294. <div class="po-add-form-wrap">
  295. <el-form ref="addMasterFormRef" :model="addForm" label-position="top" size="small">
  296. <el-row :gutter="15">
  297. <el-col v-for="field in addMasterBaseFields" :key="`master-${field.fieldCode}`" :span="getAddFieldSpan(field)">
  298. <el-form-item :label="field.fieldLabel" :required="field.requiredFlag === 'Y'">
  299. <template v-if="isAddChooserField(field)">
  300. <el-input
  301. v-model="addForm[getAddFieldProp(field)]"
  302. readonly
  303. :placeholder="getAddChooserPlaceholder(field)"
  304. @click.native="openAddChooserByField(field, 'master')">
  305. <i slot="suffix" class="el-icon-search" style="cursor:pointer" @click.stop="openAddChooserByField(field, 'master')"></i>
  306. </el-input>
  307. </template>
  308. <template v-else-if="isAddSelectField(field)">
  309. <el-select
  310. v-model="addForm[getAddFieldProp(field)]"
  311. :filterable="isAddFilterableSelectField(field)"
  312. clearable
  313. style="width:100%"
  314. :disabled="isAddFieldDisabled(field)">
  315. <el-option
  316. v-for="opt in getAddFieldSelectOptions(field)"
  317. :key="opt.value"
  318. :label="opt.label"
  319. :value="opt.value" />
  320. </el-select>
  321. </template>
  322. <template v-else-if="isAddDateField(field)">
  323. <el-date-picker
  324. v-model="addForm[getAddFieldProp(field)]"
  325. type="date"
  326. value-format="yyyy-MM-dd"
  327. style="width:100%"
  328. :disabled="isAddFieldDisabled(field)" />
  329. </template>
  330. <template v-else-if="isAddTextareaField(field)">
  331. <el-input
  332. v-model="addForm[getAddFieldProp(field)]"
  333. type="textarea"
  334. :rows="2"
  335. :disabled="isAddFieldDisabled(field)" />
  336. </template>
  337. <template v-else>
  338. <el-input
  339. v-model="addForm[getAddFieldProp(field)]"
  340. :type="isAddNumberField(field) ? 'number' : 'text'"
  341. :disabled="isAddFieldDisabled(field)"
  342. @input="onAddFieldInput(field, 'master')" />
  343. </template>
  344. </el-form-item>
  345. </el-col>
  346. </el-row>
  347. </el-form>
  348. <div class="po-add-detail-toolbar">
  349. <span class="po-add-detail-title">明细暂存列表</span>
  350. <div>
  351. <el-button type="primary" size="mini" @click="openAddDetailDialog">新增</el-button>
  352. <el-button size="mini" :disabled="!hasSelectedAddDetailRow" @click="openEditAddDetailDialog">修改</el-button>
  353. <el-button size="mini" :disabled="!hasSelectedAddDetailRow" @click="removeAddDetailRow">删除</el-button>
  354. <el-button size="mini" :disabled="!hasSelectedAddDetailRow" @click="openCopyAddDetailDialog">复制</el-button>
  355. </div>
  356. </div>
  357. <el-table
  358. ref="addDetailTable"
  359. :data="addDetailRows"
  360. :height="320"
  361. border
  362. stripe
  363. highlight-current-row
  364. empty-text="请点击新增维护明细"
  365. @current-change="onAddDetailCurrentChange"
  366. @row-click="onAddDetailRowClick">
  367. <el-table-column type="index" label="#" width="50" header-align="center" align="center" />
  368. <el-table-column prop="partNo" :label="getAddTableColumnLabel('part_no', '产品编码')" min-width="150" header-align="center" align="center" />
  369. <el-table-column prop="item" :label="getAddTableColumnLabel('item', 'Item')" min-width="120" header-align="center" align="center" />
  370. <el-table-column prop="sku" :label="getAddTableColumnLabel('sku', 'SKU')" min-width="120" header-align="center" align="center" />
  371. <el-table-column prop="qty" :label="getAddTableColumnLabel('qty', 'PO Qty')" min-width="110" header-align="center" align="right" />
  372. <el-table-column prop="price" :label="getAddTableColumnLabel('price', 'Unit Price')" min-width="120" header-align="center" align="right" />
  373. <el-table-column prop="value" :label="getAddTableColumnLabel('value', 'PO Value')" min-width="120" header-align="center" align="right" />
  374. <el-table-column prop="wantReceiveDate" :label="getAddTableColumnLabel('want_receive_date', 'Promise Date')" min-width="130" header-align="center" align="center" />
  375. <el-table-column prop="remark" :label="getAddTableColumnLabel('remark', 'Comments')" min-width="220" header-align="center" align="left" show-overflow-tooltip />
  376. </el-table>
  377. </div>
  378. <div slot="footer" class="dialog-footer">
  379. <el-button type="primary" :loading="addFormSaving" @click="saveAddPoOrder">保存</el-button>
  380. <el-button @click="addDialogVisible = false">取消</el-button>
  381. </div>
  382. </el-dialog>
  383. <!-- 新增明细弹窗 -->
  384. <el-dialog
  385. :title="addDetailDialogTitle"
  386. :visible.sync="addDetailDialogVisible"
  387. width="1000px"
  388. top="6vh"
  389. append-to-body
  390. :close-on-click-modal="false">
  391. <div class="po-add-form-wrap">
  392. <el-form ref="addDetailFormRef" :model="addDetailForm" label-position="top" size="small">
  393. <el-row :gutter="15">
  394. <el-col v-for="field in addDetailBaseFields" :key="`detail-${field.fieldCode}`" :span="getAddFieldSpan(field)">
  395. <el-form-item :label="field.fieldLabel" :required="field.requiredFlag === 'Y'">
  396. <template v-if="isAddChooserField(field)">
  397. <el-input
  398. v-model="addDetailForm[getAddFieldProp(field)]"
  399. readonly
  400. :placeholder="getAddChooserPlaceholder(field)"
  401. @click.native="openAddChooserByField(field, 'detail')">
  402. <i slot="suffix" class="el-icon-search" style="cursor:pointer" @click.stop="openAddChooserByField(field, 'detail')"></i>
  403. </el-input>
  404. </template>
  405. <template v-else-if="isAddSelectField(field)">
  406. <el-select
  407. v-model="addDetailForm[getAddFieldProp(field)]"
  408. :filterable="isAddFilterableSelectField(field)"
  409. clearable
  410. style="width:100%"
  411. :disabled="isAddFieldDisabled(field)">
  412. <el-option
  413. v-for="opt in getAddFieldSelectOptions(field)"
  414. :key="opt.value"
  415. :label="opt.label"
  416. :value="opt.value" />
  417. </el-select>
  418. </template>
  419. <template v-else-if="isAddDateField(field)">
  420. <el-date-picker
  421. v-model="addDetailForm[getAddFieldProp(field)]"
  422. type="date"
  423. value-format="yyyy-MM-dd"
  424. style="width:100%"
  425. :disabled="isAddFieldDisabled(field)" />
  426. </template>
  427. <template v-else-if="isAddTextareaField(field)">
  428. <el-input
  429. v-model="addDetailForm[getAddFieldProp(field)]"
  430. type="textarea"
  431. :rows="2"
  432. :disabled="isAddFieldDisabled(field)" />
  433. </template>
  434. <template v-else>
  435. <el-input
  436. v-model="addDetailForm[getAddFieldProp(field)]"
  437. :type="isAddNumberField(field) ? 'number' : 'text'"
  438. :disabled="isAddFieldDisabled(field)"
  439. @input="onAddFieldInput(field, 'detail')" />
  440. </template>
  441. </el-form-item>
  442. </el-col>
  443. </el-row>
  444. <el-row v-if="addDetailExtFields.length" :gutter="24">
  445. <el-col v-for="field in addDetailExtFields" :key="`detail-ext-${field.fieldCode}`" :span="6">
  446. <el-form-item :label="field.fieldLabel" :required="field.requiredFlag === 'Y'">
  447. <com-po-order-dynamic-field-renderer
  448. :field="field"
  449. :site="searchData.site"
  450. :value="getAddDetailExtValue(field.fieldCode)"
  451. @input="val => setAddDetailExtValue(field.fieldCode, val)"
  452. />
  453. </el-form-item>
  454. </el-col>
  455. </el-row>
  456. </el-form>
  457. </div>
  458. <div slot="footer" class="dialog-footer">
  459. <el-button type="primary" @click="saveAddDetailDraft">暂存</el-button>
  460. <el-button @click="addDetailDialogVisible = false">取消</el-button>
  461. </div>
  462. </el-dialog>
  463. <el-dialog
  464. title="选择供应商可供物料"
  465. :visible.sync="supplierPartDialogVisible"
  466. width="820px"
  467. top="6vh"
  468. append-to-body
  469. :close-on-click-modal="false"
  470. @closed="resetSupplierPartDialog">
  471. <div style="margin-bottom: 10px; color: #606266;">
  472. <span>供应商{{ supplierPartDialogSupplierNo }}</span>
  473. </div>
  474. <el-table
  475. :data="supplierPartDialogRows"
  476. :height="430"
  477. border
  478. stripe
  479. v-loading="supplierPartDialogLoading"
  480. empty-text="该供应商未维护供应商可供物料"
  481. @row-dblclick="handleSupplierPartRowDblClick">
  482. <el-table-column prop="partNo" label="物料编码" min-width="160" header-align="center" align="center" />
  483. <el-table-column prop="partDesc" label="物料描述" min-width="220" header-align="center" align="left" show-overflow-tooltip />
  484. <el-table-column prop="item" label="Item" min-width="120" header-align="center" align="center" show-overflow-tooltip />
  485. <el-table-column prop="sku" label="SKU" min-width="120" header-align="center" align="center" show-overflow-tooltip />
  486. </el-table>
  487. <div slot="footer" class="dialog-footer">
  488. <el-button @click="supplierPartDialogVisible = false">关闭</el-button>
  489. </div>
  490. </el-dialog>
  491. </div>
  492. </template>
  493. <script>
  494. import {
  495. searchPoDetailPage,
  496. searchPoDetailAdvancedPage,
  497. batchUpdatePoDetail,
  498. closePoDetail,
  499. openPoDetail,
  500. issuePoDetail,
  501. shipPoDetail,
  502. createPoOrder,
  503. getPoCustomerOptions,
  504. getPoBuyerOptions,
  505. getPoFieldSchema,
  506. listPoSupplierPartOptions,
  507. listPoCurrentTierPrice
  508. } from '@/api/order/poOrder.js'
  509. import {
  510. createAdvancedQueryData,
  511. createAdvancedQueryDataFromSchema,
  512. buildPoOrderQueryParams
  513. } from './poOrderAdvancedQueryHelper.js'
  514. import {
  515. applyFieldSchemaToColumns,
  516. getAdvancedQueryFieldsFromSchema,
  517. getEditableExtFieldsFromSchema,
  518. buildImportPreviewColumns,
  519. normalizeSchemaRowValues,
  520. buildExtValuesFromRow
  521. } from './poOrderFieldSchemaHelper.js'
  522. import {
  523. PO_ORDER_STATUS_OPTIONS,
  524. canIssuePoOrder,
  525. canShipPoOrder,
  526. canClosePoOrder,
  527. canOpenPoOrder,
  528. canEditPoColumnByStatus,
  529. resolvePoOrderStatusByInspectQty,
  530. buildPoDetailActionParams
  531. } from './poOrderStatusOptions.js'
  532. import { PO_ORDER_TYPE_OPTIONS, PO_ORDER_TYPE_FILTER_OPTIONS, formatPoOrderTypeLabel } from './poOrderTypeOptions.js'
  533. import { getPartList, searchPartInfo } from '@/api/part/partInfo.js'
  534. import excel from '@/utils/excel-util.js'
  535. import { checkSrmSupplierList } from '@/api/srm/srmSupplier.js'
  536. import { selectDictDataList } from '@/api/dict'
  537. import {
  538. SUPPLIER_CHOOSELIST_TAG,
  539. parseSupplierChooselistRow,
  540. fetchSupplierDetail,
  541. resolveSupplierShortFieldsFromDetail
  542. } from '@/utils/supplierChooselistHelper.js'
  543. import { searchTransportOptions } from '@/api/port/portTransit.js'
  544. import ChooselistEam from '@/views/modules/common/Chooselist_eam.vue'
  545. import PoOrderAdvancedQuery from './com_poOrderAdvancedQuery.vue'
  546. import ComPoOrderCustomerPicker from './com_poOrder_CustomerPicker.vue'
  547. import ComPoOrderDynamicFieldRenderer from './com_poOrder_DynamicFieldRenderer.vue'
  548. const ADD_CHOOSER_FIELD_CODES = new Set(['supplier_name', 'part_no', 'departure', 'destination', 'ship_via'])
  549. const ADD_DISPLAY_ONLY_FIELD_CODES = new Set(['short_office', 'short_npc', 'item', 'category_name', 'sku', 'value', 'status', 'crd_ori'])
  550. const ADD_TEXTAREA_FIELD_CODES = new Set(['remark'])
  551. const ADD_FULL_ROW_FIELD_CODES = new Set(['remark'])
  552. const ADD_NUMBER_FIELD_CODES = new Set(['price', 'qty', 'ship_qty', 'sum_price'])
  553. const ADD_FILTERABLE_SELECT_FIELD_CODES = new Set(['customer', 'buyer'])
  554. const ADD_FORCE_DETAIL_FIELD_CODES = new Set(['part_no', 'price', 'qty'])
  555. const ADD_READONLY_DEFAULT_TODAY_FIELD_CODES = new Set(['create_date'])
  556. const ADD_EDITABLE_DEFAULT_TODAY_FIELD_CODES = new Set(['po_place_date'])
  557. const ADD_SELECT_OPTIONS_FIELD_CODE_MAP = {
  558. order_type: 'poOrderTypeOptions',
  559. customer: 'poCustomerOptions',
  560. buyer: 'poBuyerOptions',
  561. status: 'poOrderStatusOptions',
  562. doc_sent: 'yesNoOptions',
  563. telex_release: 'yesNoOptions',
  564. advance_paid: 'yesNoOptions'
  565. }
  566. export default {
  567. name: 'PoOrder',
  568. components: {
  569. ChooselistEam,
  570. PoOrderAdvancedQuery,
  571. ComPoOrderCustomerPicker,
  572. ComPoOrderDynamicFieldRenderer
  573. },
  574. data () {
  575. return {
  576. loading: false,
  577. masterRows: [],
  578. /**
  579. * 需要单独写编辑 UI columnProp 填在这里才会走模板里自定义分支不在此数组的仍用默认 select/num/text - rqrq
  580. * 示例['mPlanner', 'comments']
  581. */
  582. rowDeparture: {}, // 存储每行选中的始发港
  583. rowDestination: {}, // 存储每行选中的目的港
  584. rowShipVia: {}, // 存储每行选中的运输方式
  585. transportOptions: [], // 运输方式选项
  586. rowPartNo: {}, //存储每行选中的物料编号
  587. currentSelectRow: null, // 当前正在选择供应商的行
  588. currentPartNoRow: null, // 当前正在选择物料的行
  589. currentDepartureRow: null, // 当前正在选择始发港的行
  590. currentDestinationRow: null, // 当前正在选择目的港的行
  591. currentShipViaRow: null, // 当前正在选择运输方式的行
  592. rowSupplierNo: {}, //存储每行选中的供应商编号
  593. poPartNoOptions: [],
  594. supplierOptions: [],
  595. poDepartureOptions: [],
  596. poDestinationOptions: [],
  597. poCustomerOptions: [],
  598. poBuyerOptions: [],
  599. /** Chooselist 弹窗选择列(editAble 可为 false,如供应商/物料) */
  600. chooserEditColumnProps: ['supplierName', 'partNo', 'departure', 'destination', 'shipVia'],
  601. /** 联表展示、不写入 PO 的列 */
  602. displayOnlyColumnProps: ['shortOffice', 'shortNpc', 'item', 'categoryName', 'sku'],
  603. customEditColumnProps: [],
  604. // 上传相关
  605. uploadDialogVisible: false,
  606. uploadFileName: '',
  607. uploadFile: null,
  608. uploadPreviewList: [],
  609. uploadSkippedColumns: [],
  610. uploadColumnMapping: {},
  611. uploadPreviewColumns: [],
  612. addDialogVisible: false,
  613. addFormSaving: false,
  614. addFormSchemaFields: [],
  615. pickContext: '',
  616. eamTagNo: '',
  617. eamPickField: '',
  618. addForm: {},
  619. addDetailDialogVisible: false,
  620. addDetailDialogMode: 'create',
  621. addDetailForm: {},
  622. addDetailRows: [],
  623. addDetailCurrentDraftId: '',
  624. addDetailEditingDraftId: '',
  625. addDetailDraftSeq: 0,
  626. supplierPartDialogVisible: false,
  627. supplierPartDialogLoading: false,
  628. supplierPartDialogRows: [],
  629. supplierPartDialogContext: '',
  630. supplierPartDialogTargetRow: null,
  631. supplierPartDialogSupplierNo: '',
  632. supplierPartDialogSite: '',
  633. addPriceLookupTimer: null,
  634. addPriceLookupSeq: 0,
  635. selectedCustomerScope: null,
  636. currentRow: null,
  637. // 各列下拉:select 列通过 selectOptionsKey 指向此处;也可在列定义里写 selectOptions 数组 - rqrq
  638. columnSelectOptions: {
  639. poStatusOptions: PO_ORDER_STATUS_OPTIONS,
  640. poPartNoOptions: [],
  641. poDepartureOptions: [],
  642. poCustomerOptions: [],
  643. poBuyerOptions: [],
  644. transportOptions: [
  645. { label: '海运 SEA', value: 'SEA' },
  646. { label: '空运 AIR', value: 'AIR' },
  647. { label: '陆运 ROAD', value: 'ROAD' }
  648. ],
  649. yesNoOptions: [
  650. { label: 'Y', value: 'Y' },
  651. { label: 'N', value: 'N' }
  652. ]
  653. },
  654. dictSelectOptionsMap: {},
  655. searchData: {
  656. site: this.$store.state.user.site,
  657. customer: '',
  658. buyer: '',
  659. poNo: '',
  660. orderType: '',
  661. partNo: '',
  662. supplierNo: '',
  663. sku: '',
  664. status: '',
  665. supplierName: ''
  666. },
  667. poOrderTypeSearchOptions: [{ label: '全部', value: '' }, ...PO_ORDER_TYPE_OPTIONS],
  668. advancedQueryVisible: false,
  669. /** 字段 schema(list 场景) */
  670. fieldSchemaFields: [],
  671. baseColumnList: null,
  672. /** 高级查询条件对象(查询均读取;仅弹窗内「重置」可清空) */
  673. advancedQueryData: createAdvancedQueryData(),
  674. poOrderStatusOptions: PO_ORDER_STATUS_OPTIONS,
  675. poOrderTypeOptions: PO_ORDER_TYPE_FILTER_OPTIONS,
  676. height: 400,
  677. pageIndex: 1,
  678. pageSize: 20,
  679. totalPage: 0,
  680. dataList: [],
  681. editMode: false,
  682. columnList: [
  683. {
  684. userId: this.$store.state.user.name,
  685. functionId: 812003,
  686. serialNumber: '812003Table1Customer',
  687. tableId: '812003Table1',
  688. tableName: 'PO Order',
  689. columnProp: 'customer',
  690. headerAlign: 'center',
  691. align: 'center',
  692. columnLabel: 'Customer',
  693. columnHidden: false,
  694. columnImage: false,
  695. columnSortable: false,
  696. sortLv: 0,
  697. status: true,
  698. fixed: '',
  699. columnWidth: 180,
  700. showOverflowTooltip: true,
  701. editAble: true,
  702. editType: 'select',
  703. selectOptionsKey: 'poCustomerOptions',
  704. selectOptions: null
  705. },
  706. {
  707. userId: this.$store.state.user.name,
  708. functionId: 812003,
  709. serialNumber: '812003Table1Buyer',
  710. tableId: '812003Table1',
  711. tableName: 'PO Order',
  712. columnProp: 'buyer',
  713. headerAlign: 'center',
  714. align: 'center',
  715. columnLabel: 'Buyer',
  716. columnHidden: false,
  717. columnImage: false,
  718. columnSortable: false,
  719. sortLv: 0,
  720. status: true,
  721. fixed: '',
  722. columnWidth: 90,
  723. showOverflowTooltip: true,
  724. editAble: true,
  725. editType: 'select',
  726. selectOptionsKey: 'poBuyerOptions',
  727. selectOptions: null
  728. },
  729. {
  730. userId: this.$store.state.user.name,
  731. functionId: 812003,
  732. serialNumber: '812003Table1VendorName',
  733. tableId: '812003Table1',
  734. tableName: 'PO Order',
  735. columnProp: 'supplierName',
  736. headerAlign: 'center',
  737. align: 'center',
  738. columnLabel: 'Vendor Name',
  739. columnHidden: false,
  740. columnImage: false,
  741. columnSortable: false,
  742. sortLv: 0,
  743. status: true,
  744. fixed: '',
  745. columnWidth: 180,
  746. showOverflowTooltip: true,
  747. editAble: false,
  748. editType: 'select',
  749. selectOptionsKey: 'supplierOptions',
  750. selectOptions: null
  751. },
  752. {
  753. userId: this.$store.state.user.name,
  754. functionId: 812003,
  755. serialNumber: '812003Table1VShortOffice',
  756. tableId: '812003Table1',
  757. tableName: 'PO Order',
  758. columnProp: 'shortOffice',
  759. headerAlign: 'center',
  760. align: 'center',
  761. columnLabel: 'V_Short_Office',
  762. columnHidden: false,
  763. columnImage: false,
  764. columnSortable: false,
  765. sortLv: 0,
  766. status: true,
  767. fixed: '',
  768. columnWidth: 110,
  769. showOverflowTooltip: true,
  770. editAble: false,
  771. editType: 'text',
  772. selectOptionsKey: '',
  773. selectOptions: null
  774. },
  775. {
  776. userId: this.$store.state.user.name,
  777. functionId: 812003,
  778. serialNumber: '812003Table1VShortNpc',
  779. tableId: '812003Table1',
  780. tableName: 'PO Order',
  781. columnProp: 'shortNpc',
  782. headerAlign: 'center',
  783. align: 'center',
  784. columnLabel: 'V_Short_NPC',
  785. columnHidden: false,
  786. columnImage: false,
  787. columnSortable: false,
  788. sortLv: 0,
  789. status: true,
  790. fixed: '',
  791. columnWidth: 100,
  792. showOverflowTooltip: true,
  793. editAble: false,
  794. editType: 'text',
  795. selectOptionsKey: '',
  796. selectOptions: null
  797. },
  798. {
  799. userId: this.$store.state.user.name,
  800. functionId: 812003,
  801. serialNumber: '812003Table1PoNo',
  802. tableId: '812003Table1',
  803. tableName: 'PO Order',
  804. columnProp: 'poNo',
  805. headerAlign: 'center',
  806. align: 'center',
  807. columnLabel: 'PO_No',
  808. columnHidden: false,
  809. columnImage: false,
  810. columnSortable: false,
  811. sortLv: 0,
  812. status: true,
  813. fixed: '',
  814. columnWidth: 120,
  815. showOverflowTooltip: true,
  816. editAble: true,
  817. editType: 'text',
  818. selectOptionsKey: '',
  819. selectOptions: null
  820. },
  821. {
  822. userId: this.$store.state.user.name,
  823. functionId: 812003,
  824. serialNumber: '812003Table1OrderType',
  825. tableId: '812003Table1',
  826. tableName: 'PO Order',
  827. columnProp: 'orderType',
  828. headerAlign: 'center',
  829. align: 'center',
  830. columnLabel: '订单类型',
  831. columnHidden: false,
  832. columnImage: false,
  833. columnSortable: false,
  834. sortLv: 0,
  835. status: true,
  836. fixed: '',
  837. columnWidth: 100,
  838. showOverflowTooltip: true,
  839. editAble: false,
  840. editType: 'text',
  841. selectOptionsKey: '',
  842. selectOptions: null
  843. },
  844. {
  845. userId: this.$store.state.user.name,
  846. functionId: 812003,
  847. serialNumber: '812003Table1PartNo',
  848. tableId: '812003Table1',
  849. tableName: 'PO Order',
  850. columnProp: 'partNo',
  851. headerAlign: 'center',
  852. align: 'center',
  853. columnLabel: 'Part No',
  854. columnHidden: false,
  855. columnImage: false,
  856. columnSortable: false,
  857. sortLv: 0,
  858. status: true,
  859. fixed: '',
  860. columnWidth: 120,
  861. showOverflowTooltip: true,
  862. editAble: false,
  863. editType: 'select',
  864. selectOptionsKey: 'poPartNoOptions',
  865. selectOptions: null
  866. },
  867. {
  868. userId: this.$store.state.user.name,
  869. functionId: 812003,
  870. serialNumber: '812003Table1Item',
  871. tableId: '812003Table1',
  872. tableName: 'PO Order',
  873. columnProp: 'item',
  874. headerAlign: 'center',
  875. align: 'center',
  876. columnLabel: 'Item',
  877. columnHidden: false,
  878. columnImage: false,
  879. columnSortable: false,
  880. sortLv: 0,
  881. status: true,
  882. fixed: '',
  883. columnWidth: 120,
  884. showOverflowTooltip: true,
  885. editAble: false,
  886. editType: 'text',
  887. selectOptionsKey: '',
  888. selectOptions: null
  889. },
  890. {
  891. userId: this.$store.state.user.name,
  892. functionId: 812003,
  893. serialNumber: '812003Table1Category',
  894. tableId: '812003Table1',
  895. tableName: 'PO Order',
  896. columnProp: 'categoryName',
  897. headerAlign: 'center',
  898. align: 'center',
  899. columnLabel: 'Category',
  900. columnHidden: false,
  901. columnImage: false,
  902. columnSortable: false,
  903. sortLv: 0,
  904. status: true,
  905. fixed: '',
  906. columnWidth: 90,
  907. showOverflowTooltip: true,
  908. editAble: false,
  909. editType: 'text',
  910. selectOptionsKey: '',
  911. selectOptions: null
  912. },
  913. {
  914. userId: this.$store.state.user.name,
  915. functionId: 812003,
  916. serialNumber: '812003Table1Sku',
  917. tableId: '812003Table1',
  918. tableName: 'PO Order',
  919. columnProp: 'sku',
  920. headerAlign: 'center',
  921. align: 'center',
  922. columnLabel: 'SKU',
  923. columnHidden: false,
  924. columnImage: false,
  925. columnSortable: false,
  926. sortLv: 0,
  927. status: true,
  928. fixed: '',
  929. columnWidth: 100,
  930. showOverflowTooltip: true,
  931. editAble: false,
  932. editType: 'text',
  933. selectOptionsKey: '',
  934. selectOptions: null
  935. },
  936. {
  937. userId: this.$store.state.user.name,
  938. functionId: 812003,
  939. serialNumber: '812003Table1UnitPrice',
  940. tableId: '812003Table1',
  941. tableName: 'PO Order',
  942. columnProp: 'price',
  943. headerAlign: 'center',
  944. align: 'center',
  945. columnLabel: 'Unit Price',
  946. columnHidden: false,
  947. columnImage: false,
  948. columnSortable: false,
  949. sortLv: 0,
  950. status: true,
  951. fixed: '',
  952. columnWidth: 90,
  953. showOverflowTooltip: true,
  954. editAble: true,
  955. editType: 'num',
  956. selectOptionsKey: '',
  957. selectOptions: null
  958. },
  959. {
  960. userId: this.$store.state.user.name,
  961. functionId: 812003,
  962. serialNumber: '812003Table1PQty',
  963. tableId: '812003Table1',
  964. tableName: 'PO Order',
  965. columnProp: 'qty',
  966. headerAlign: 'center',
  967. align: 'center',
  968. columnLabel: 'PO Qty',
  969. columnHidden: false,
  970. columnImage: false,
  971. columnSortable: false,
  972. sortLv: 0,
  973. status: true,
  974. fixed: '',
  975. columnWidth: 80,
  976. showOverflowTooltip: true,
  977. editAble: true,
  978. editType: 'num',
  979. selectOptionsKey: '',
  980. selectOptions: null
  981. },
  982. {
  983. userId: this.$store.state.user.name,
  984. functionId: 812003,
  985. serialNumber: '812003Table1PValue',
  986. tableId: '812003Table1',
  987. tableName: 'PO Order',
  988. columnProp: 'value',
  989. headerAlign: 'center',
  990. align: 'center',
  991. columnLabel: 'PO Value',
  992. columnHidden: false,
  993. columnImage: false,
  994. columnSortable: false,
  995. sortLv: 0,
  996. status: true,
  997. fixed: '',
  998. columnWidth: 90,
  999. showOverflowTooltip: true,
  1000. editAble: false,
  1001. editType: 'num',
  1002. selectOptionsKey: '',
  1003. selectOptions: null
  1004. },
  1005. {
  1006. userId: this.$store.state.user.name,
  1007. functionId: 812003,
  1008. serialNumber: '812003Table1AQty',
  1009. tableId: '812003Table1',
  1010. tableName: 'PO Order',
  1011. columnProp: 'shipQty',
  1012. headerAlign: 'center',
  1013. align: 'center',
  1014. columnLabel: 'A-Qty',
  1015. columnHidden: false,
  1016. columnImage: false,
  1017. columnSortable: false,
  1018. sortLv: 0,
  1019. status: true,
  1020. fixed: '',
  1021. columnWidth: 80,
  1022. showOverflowTooltip: true,
  1023. editAble: true,
  1024. editType: 'text',
  1025. selectOptionsKey: '',
  1026. selectOptions: null
  1027. },
  1028. {
  1029. userId: this.$store.state.user.name,
  1030. functionId: 812003,
  1031. serialNumber: '812003Table1AValue',
  1032. tableId: '812003Table1',
  1033. tableName: 'PO Order',
  1034. columnProp: 'sumPrice',
  1035. headerAlign: 'center',
  1036. align: 'center',
  1037. columnLabel: 'A-Value',
  1038. columnHidden: false,
  1039. columnImage: false,
  1040. columnSortable: false,
  1041. sortLv: 0,
  1042. status: true,
  1043. fixed: '',
  1044. columnWidth: 90,
  1045. showOverflowTooltip: true,
  1046. editAble: true,
  1047. editType: 'text',
  1048. selectOptionsKey: '',
  1049. selectOptions: null
  1050. },
  1051. {
  1052. userId: this.$store.state.user.name,
  1053. functionId: 812003,
  1054. serialNumber: '812003Table1PDeparture',
  1055. tableId: '812003Table1',
  1056. tableName: 'PO Order',
  1057. columnProp: 'departure',
  1058. headerAlign: 'center',
  1059. align: 'center',
  1060. columnLabel: 'Departure Port',
  1061. columnHidden: false,
  1062. columnImage: false,
  1063. columnSortable: false,
  1064. sortLv: 0,
  1065. status: true,
  1066. fixed: '',
  1067. columnWidth: 100,
  1068. showOverflowTooltip: true,
  1069. editAble: true,
  1070. editType: 'select',
  1071. selectOptionsKey: 'departureOptions',
  1072. selectOptions: null
  1073. },
  1074. {
  1075. userId: this.$store.state.user.name,
  1076. functionId: 812003,
  1077. serialNumber: '812003Table1PDestination',
  1078. tableId: '812003Table1',
  1079. tableName: 'PO Order',
  1080. columnProp: 'destination',
  1081. headerAlign: 'center',
  1082. align: 'center',
  1083. columnLabel: 'Destination Port',
  1084. columnHidden: false,
  1085. columnImage: false,
  1086. columnSortable: false,
  1087. sortLv: 0,
  1088. status: true,
  1089. fixed: '',
  1090. columnWidth: 110,
  1091. showOverflowTooltip: true,
  1092. editAble: true,
  1093. editType: 'select',
  1094. selectOptionsKey: 'destinationOptions',
  1095. selectOptions: null
  1096. },
  1097. {
  1098. userId: this.$store.state.user.name,
  1099. functionId: 812003,
  1100. serialNumber: '812003Table1SMethod',
  1101. tableId: '812003Table1',
  1102. tableName: 'PO Order',
  1103. columnProp: 'shipVia',
  1104. headerAlign: 'center',
  1105. align: 'center',
  1106. columnLabel: 'Method',
  1107. columnHidden: false,
  1108. columnImage: false,
  1109. columnSortable: false,
  1110. sortLv: 0,
  1111. status: true,
  1112. fixed: '',
  1113. columnWidth: 90,
  1114. showOverflowTooltip: true,
  1115. editAble: true,
  1116. editType: 'select',
  1117. selectOptionsKey: 'transportOptions',
  1118. selectOptions: null
  1119. },
  1120. {
  1121. userId: this.$store.state.user.name,
  1122. functionId: 812003,
  1123. serialNumber: '812003Table1DIssue',
  1124. tableId: '812003Table1',
  1125. tableName: 'PO Order',
  1126. columnProp: 'orderDate',
  1127. headerAlign: 'center',
  1128. align: 'center',
  1129. columnLabel: 'Buyer Release Date',
  1130. columnHidden: false,
  1131. columnImage: false,
  1132. columnSortable: false,
  1133. sortLv: 0,
  1134. status: true,
  1135. fixed: '',
  1136. columnWidth: 130,
  1137. showOverflowTooltip: true,
  1138. editAble: false,
  1139. editType: 'date',
  1140. selectOptionsKey: '',
  1141. selectOptions: null
  1142. },
  1143. {
  1144. userId: this.$store.state.user.name,
  1145. functionId: 812003,
  1146. serialNumber: '812003Table1DEnquiry',
  1147. tableId: '812003Table1',
  1148. tableName: 'PO Order',
  1149. columnProp: 'enquiry',
  1150. headerAlign: 'center',
  1151. align: 'center',
  1152. columnLabel: 'Planner Release Date',
  1153. columnHidden: false,
  1154. columnImage: false,
  1155. columnSortable: false,
  1156. sortLv: 0,
  1157. status: true,
  1158. fixed: '',
  1159. columnWidth: 140,
  1160. showOverflowTooltip: true,
  1161. editAble: false,
  1162. editType: 'date',
  1163. selectOptionsKey: '',
  1164. selectOptions: null
  1165. },
  1166. {
  1167. userId: this.$store.state.user.name,
  1168. functionId: 812003,
  1169. serialNumber: '812003Table1Status',
  1170. tableId: '812003Table1',
  1171. tableName: 'PO Order',
  1172. columnProp: 'status',
  1173. headerAlign: 'center',
  1174. align: 'center',
  1175. columnLabel: 'Status',
  1176. columnHidden: false,
  1177. columnImage: false,
  1178. columnSortable: false,
  1179. sortLv: 0,
  1180. status: true,
  1181. fixed: '',
  1182. columnWidth: 80,
  1183. showOverflowTooltip: true,
  1184. editAble: false,
  1185. editType: 'select',
  1186. selectOptionsKey: 'poStatusOptions',
  1187. selectOptions: null
  1188. },
  1189. {
  1190. userId: this.$store.state.user.name,
  1191. functionId: 812003,
  1192. serialNumber: '812003Table1DConfirmed',
  1193. tableId: '812003Table1',
  1194. tableName: 'PO Order',
  1195. columnProp: 'confirmed',
  1196. headerAlign: 'center',
  1197. align: 'center',
  1198. columnLabel: 'PI Confirmed Date',
  1199. columnHidden: false,
  1200. columnImage: false,
  1201. columnSortable: false,
  1202. sortLv: 0,
  1203. status: true,
  1204. fixed: '',
  1205. columnWidth: 120,
  1206. showOverflowTooltip: true,
  1207. editAble: true,
  1208. editType: 'date',
  1209. selectOptionsKey: '',
  1210. selectOptions: null
  1211. },
  1212. {
  1213. userId: this.$store.state.user.name,
  1214. functionId: 812003,
  1215. serialNumber: '812003Table1DNeed',
  1216. tableId: '812003Table1',
  1217. tableName: 'PO Order',
  1218. columnProp: 'wantReceiveDate',
  1219. headerAlign: 'center',
  1220. align: 'center',
  1221. columnLabel: 'Original Arrival Date',
  1222. columnHidden: false,
  1223. columnImage: false,
  1224. columnSortable: false,
  1225. sortLv: 0,
  1226. status: true,
  1227. fixed: '',
  1228. columnWidth: 130,
  1229. showOverflowTooltip: true,
  1230. editAble: true,
  1231. editType: 'date',
  1232. selectOptionsKey: '',
  1233. selectOptions: null
  1234. },
  1235. {
  1236. userId: this.$store.state.user.name,
  1237. functionId: 812003,
  1238. serialNumber: '812003Table1DNarrival',
  1239. tableId: '812003Table1',
  1240. tableName: 'PO Order',
  1241. columnProp: 'narrival',
  1242. headerAlign: 'center',
  1243. align: 'center',
  1244. columnLabel: 'Final Confirmed Arrival Date',
  1245. columnHidden: false,
  1246. columnImage: false,
  1247. columnSortable: false,
  1248. sortLv: 0,
  1249. status: true,
  1250. fixed: '',
  1251. columnWidth: 110,
  1252. showOverflowTooltip: true,
  1253. editAble: true,
  1254. editType: 'date',
  1255. selectOptionsKey: '',
  1256. selectOptions: null
  1257. },
  1258. {
  1259. userId: this.$store.state.user.name,
  1260. functionId: 812003,
  1261. serialNumber: '812003Table1DCrd',
  1262. tableId: '812003Table1',
  1263. tableName: 'PO Order',
  1264. columnProp: 'crd',
  1265. headerAlign: 'center',
  1266. align: 'center',
  1267. columnLabel: 'PI Confirmed CRD',
  1268. columnHidden: false,
  1269. columnImage: false,
  1270. columnSortable: false,
  1271. sortLv: 0,
  1272. status: true,
  1273. fixed: '',
  1274. columnWidth: 120,
  1275. showOverflowTooltip: true,
  1276. editAble: true,
  1277. editType: 'date',
  1278. selectOptionsKey: '',
  1279. selectOptions: null
  1280. },
  1281. {
  1282. userId: this.$store.state.user.name,
  1283. functionId: 812003,
  1284. serialNumber: '812003Table1DCrdOri',
  1285. tableId: '812003Table1',
  1286. tableName: 'PO Order',
  1287. columnProp: 'crdOri',
  1288. headerAlign: 'center',
  1289. align: 'center',
  1290. columnLabel: 'Original CRD',
  1291. columnHidden: false,
  1292. columnImage: false,
  1293. columnSortable: false,
  1294. sortLv: 0,
  1295. status: true,
  1296. fixed: '',
  1297. columnWidth: 120,
  1298. showOverflowTooltip: true,
  1299. editAble: false,
  1300. editType: 'date',
  1301. selectOptionsKey: '',
  1302. selectOptions: null
  1303. },
  1304. {
  1305. userId: this.$store.state.user.name,
  1306. functionId: 812003,
  1307. serialNumber: '812003Table1DInspection',
  1308. tableId: '812003Table1',
  1309. tableName: 'PO Order',
  1310. columnProp: 'inspection',
  1311. headerAlign: 'center',
  1312. align: 'center',
  1313. columnLabel: 'Inspection Date',
  1314. columnHidden: false,
  1315. columnImage: false,
  1316. columnSortable: false,
  1317. sortLv: 0,
  1318. status: true,
  1319. fixed: '',
  1320. columnWidth: 100,
  1321. showOverflowTooltip: true,
  1322. editAble: true,
  1323. editType: 'date',
  1324. selectOptionsKey: '',
  1325. selectOptions: null
  1326. },
  1327. {
  1328. userId: this.$store.state.user.name,
  1329. functionId: 812003,
  1330. serialNumber: '812003Table1ACrd',
  1331. tableId: '812003Table1',
  1332. tableName: 'PO Order',
  1333. columnProp: 'realCrd',
  1334. headerAlign: 'center',
  1335. align: 'center',
  1336. columnLabel: 'Actual Completion Date',
  1337. columnHidden: false,
  1338. columnImage: false,
  1339. columnSortable: false,
  1340. sortLv: 0,
  1341. status: true,
  1342. fixed: '',
  1343. columnWidth: 150,
  1344. showOverflowTooltip: true,
  1345. editAble: true,
  1346. editType: 'date',
  1347. selectOptionsKey: '',
  1348. selectOptions: null
  1349. },
  1350. {
  1351. userId: this.$store.state.user.name,
  1352. functionId: 812003,
  1353. serialNumber: '812003Table1Eta',
  1354. tableId: '812003Table1',
  1355. tableName: 'PO Order',
  1356. columnProp: 'eta',
  1357. headerAlign: 'center',
  1358. align: 'center',
  1359. columnLabel: 'ETA',
  1360. columnHidden: false,
  1361. columnImage: false,
  1362. columnSortable: false,
  1363. sortLv: 0,
  1364. status: true,
  1365. fixed: '',
  1366. columnWidth: 100,
  1367. showOverflowTooltip: true,
  1368. editAble: false,
  1369. editType: 'date',
  1370. selectOptionsKey: '',
  1371. selectOptions: null
  1372. },
  1373. {
  1374. userId: this.$store.state.user.name,
  1375. functionId: 812003,
  1376. serialNumber: '812003Table1Flexid',
  1377. tableId: '812003Table1',
  1378. tableName: 'PO Order',
  1379. columnProp: 'flexid',
  1380. headerAlign: 'center',
  1381. align: 'center',
  1382. columnLabel: 'Flexport Id',
  1383. columnHidden: false,
  1384. columnImage: false,
  1385. columnSortable: false,
  1386. sortLv: 0,
  1387. status: true,
  1388. fixed: '',
  1389. columnWidth: 90,
  1390. showOverflowTooltip: true,
  1391. editAble: true,
  1392. editType: 'text',
  1393. selectOptionsKey: '',
  1394. selectOptions: null
  1395. },
  1396. {
  1397. userId: this.$store.state.user.name,
  1398. functionId: 812003,
  1399. serialNumber: '812003Table1AObd',
  1400. tableId: '812003Table1',
  1401. tableName: 'PO Order',
  1402. columnProp: 'obd',
  1403. headerAlign: 'center',
  1404. align: 'center',
  1405. columnLabel: 'On Board Date',
  1406. columnHidden: false,
  1407. columnImage: false,
  1408. columnSortable: false,
  1409. sortLv: 0,
  1410. status: true,
  1411. fixed: '',
  1412. columnWidth: 100,
  1413. showOverflowTooltip: true,
  1414. editAble: true,
  1415. editType: 'date',
  1416. selectOptionsKey: '',
  1417. selectOptions: null
  1418. },
  1419. {
  1420. userId: this.$store.state.user.name,
  1421. functionId: 812003,
  1422. serialNumber: '812003Table1DocSent',
  1423. tableId: '812003Table1',
  1424. tableName: 'PO Order',
  1425. columnProp: 'docSent',
  1426. headerAlign: 'center',
  1427. align: 'center',
  1428. columnLabel: 'Doc Sent',
  1429. columnHidden: false,
  1430. columnImage: false,
  1431. columnSortable: false,
  1432. sortLv: 0,
  1433. status: true,
  1434. fixed: '',
  1435. columnWidth: 90,
  1436. showOverflowTooltip: true,
  1437. editAble: true,
  1438. editType: 'select',
  1439. selectOptionsKey: 'yesNoOptions',
  1440. selectOptions: null
  1441. },
  1442. {
  1443. userId: this.$store.state.user.name,
  1444. functionId: 812003,
  1445. serialNumber: '812003Table1TelexRelease',
  1446. tableId: '812003Table1',
  1447. tableName: 'PO Order',
  1448. columnProp: 'telexRelease',
  1449. headerAlign: 'center',
  1450. align: 'center',
  1451. columnLabel: 'Telex Release',
  1452. columnHidden: false,
  1453. columnImage: false,
  1454. columnSortable: false,
  1455. sortLv: 0,
  1456. status: true,
  1457. fixed: '',
  1458. columnWidth: 100,
  1459. showOverflowTooltip: true,
  1460. editAble: true,
  1461. editType: 'select',
  1462. selectOptionsKey: 'yesNoOptions',
  1463. selectOptions: null
  1464. },
  1465. {
  1466. userId: this.$store.state.user.name,
  1467. functionId: 812003,
  1468. serialNumber: '812003Table1AdvancePayment',
  1469. tableId: '812003Table1',
  1470. tableName: 'PO Order',
  1471. columnProp: 'advancePayment',
  1472. headerAlign: 'center',
  1473. align: 'center',
  1474. columnLabel: 'Advance Payment',
  1475. columnHidden: false,
  1476. columnImage: false,
  1477. columnSortable: false,
  1478. sortLv: 0,
  1479. status: true,
  1480. fixed: '',
  1481. columnWidth: 120,
  1482. showOverflowTooltip: true,
  1483. editAble: true,
  1484. editType: 'text',
  1485. selectOptionsKey: '',
  1486. selectOptions: null
  1487. },
  1488. {
  1489. userId: this.$store.state.user.name,
  1490. functionId: 812003,
  1491. serialNumber: '812003Table1AdvancePaid',
  1492. tableId: '812003Table1',
  1493. tableName: 'PO Order',
  1494. columnProp: 'advancePaid',
  1495. headerAlign: 'center',
  1496. align: 'center',
  1497. columnLabel: 'Advanc Paid',
  1498. columnHidden: false,
  1499. columnImage: false,
  1500. columnSortable: false,
  1501. sortLv: 0,
  1502. status: true,
  1503. fixed: '',
  1504. columnWidth: 100,
  1505. showOverflowTooltip: true,
  1506. editAble: true,
  1507. editType: 'select',
  1508. selectOptionsKey: 'yesNoOptions',
  1509. selectOptions: null
  1510. },
  1511. {
  1512. userId: this.$store.state.user.name,
  1513. functionId: 812003,
  1514. serialNumber: '812003Table1DCreate',
  1515. tableId: '812003Table1',
  1516. tableName: 'PO Order',
  1517. columnProp: 'createTime',
  1518. headerAlign: 'center',
  1519. align: 'center',
  1520. columnLabel: 'Create Date',
  1521. columnHidden: false,
  1522. columnImage: false,
  1523. columnSortable: false,
  1524. sortLv: 0,
  1525. status: true,
  1526. fixed: '',
  1527. columnWidth: 100,
  1528. showOverflowTooltip: true,
  1529. editAble: false,
  1530. editType: 'date',
  1531. selectOptionsKey: '',
  1532. selectOptions: null
  1533. },
  1534. {
  1535. userId: this.$store.state.user.name,
  1536. functionId: 812003,
  1537. serialNumber: '812003Table1MPlanner',
  1538. tableId: '812003Table1',
  1539. tableName: 'PO Order',
  1540. columnProp: 'planner',
  1541. headerAlign: 'center',
  1542. align: 'center',
  1543. columnLabel: 'M-Planner',
  1544. columnHidden: false,
  1545. columnImage: false,
  1546. columnSortable: false,
  1547. sortLv: 0,
  1548. status: true,
  1549. fixed: '',
  1550. columnWidth: 100,
  1551. showOverflowTooltip: true,
  1552. editAble: true,
  1553. editType: 'text',
  1554. selectOptionsKey: '',
  1555. selectOptions: null
  1556. },
  1557. {
  1558. userId: this.$store.state.user.name,
  1559. functionId: 812003,
  1560. serialNumber: '812003Table1DEdit',
  1561. tableId: '812003Table1',
  1562. tableName: 'PO Order',
  1563. columnProp: 'edit',
  1564. headerAlign: 'center',
  1565. align: 'center',
  1566. columnLabel: 'Edit Date',
  1567. columnHidden: false,
  1568. columnImage: false,
  1569. columnSortable: false,
  1570. sortLv: 0,
  1571. status: true,
  1572. fixed: '',
  1573. columnWidth: 100,
  1574. showOverflowTooltip: true,
  1575. editAble: false,
  1576. editType: 'date',
  1577. selectOptionsKey: '',
  1578. selectOptions: null
  1579. },
  1580. {
  1581. userId: this.$store.state.user.name,
  1582. functionId: 812003,
  1583. serialNumber: '812003Table1EPlanner',
  1584. tableId: '812003Table1',
  1585. tableName: 'PO Order',
  1586. columnProp: 'endPlanner',
  1587. headerAlign: 'center',
  1588. align: 'center',
  1589. columnLabel: 'Last Editor',
  1590. columnHidden: false,
  1591. columnImage: false,
  1592. columnSortable: false,
  1593. sortLv: 0,
  1594. status: true,
  1595. fixed: '',
  1596. columnWidth: 100,
  1597. showOverflowTooltip: true,
  1598. editAble: false,
  1599. editType: 'text',
  1600. selectOptionsKey: '',
  1601. selectOptions: null
  1602. },
  1603. {
  1604. userId: this.$store.state.user.name,
  1605. functionId: 812003,
  1606. serialNumber: '812003Table1RemarkDelay',
  1607. tableId: '812003Table1',
  1608. tableName: 'PO Order',
  1609. columnProp: 'remarkDelay',
  1610. headerAlign: 'center',
  1611. align: 'center',
  1612. columnLabel: 'Remark Delay',
  1613. columnHidden: false,
  1614. columnImage: false,
  1615. columnSortable: false,
  1616. sortLv: 0,
  1617. status: true,
  1618. fixed: '',
  1619. columnWidth: 110,
  1620. showOverflowTooltip: true,
  1621. editAble: true,
  1622. editType: 'text',
  1623. selectOptionsKey: '',
  1624. selectOptions: null
  1625. },
  1626. {
  1627. userId: this.$store.state.user.name,
  1628. functionId: 812003,
  1629. serialNumber: '812003Table1Comments',
  1630. tableId: '812003Table1',
  1631. tableName: 'PO Order',
  1632. columnProp: 'remark',
  1633. headerAlign: 'center',
  1634. align: 'center',
  1635. columnLabel: 'Comments',
  1636. columnHidden: false,
  1637. columnImage: false,
  1638. columnSortable: false,
  1639. sortLv: 0,
  1640. status: true,
  1641. fixed: '',
  1642. columnWidth: 180,
  1643. showOverflowTooltip: true,
  1644. editAble: true,
  1645. editType: 'text',
  1646. selectOptionsKey: '',
  1647. selectOptions: null
  1648. },
  1649. {
  1650. userId: this.$store.state.user.name,
  1651. functionId: 812003,
  1652. serialNumber: '812003Table1InspectionCompletedQty',
  1653. tableId: '812003Table1',
  1654. tableName: 'PO Order',
  1655. columnProp: 'inspectionCompletedQty',
  1656. headerAlign: 'center',
  1657. align: 'center',
  1658. columnLabel: 'Inspection Completed Qty',
  1659. columnHidden: false,
  1660. columnImage: false,
  1661. columnSortable: false,
  1662. sortLv: 0,
  1663. status: true,
  1664. fixed: '',
  1665. columnWidth: 140,
  1666. showOverflowTooltip: true,
  1667. editAble: false,
  1668. editType: 'text',
  1669. selectOptionsKey: '',
  1670. selectOptions: null
  1671. }
  1672. ]
  1673. }
  1674. },
  1675. computed: {
  1676. visibleColumns () {
  1677. const source = this.baseColumnList || this.columnList
  1678. const applied = applyFieldSchemaToColumns(source, this.fieldSchemaFields)
  1679. const cols = applied.filter(c => !c.columnHidden)
  1680. const leadProps = ['poNo', 'orderType']
  1681. const leading = leadProps.map(p => cols.find(c => c.columnProp === p)).filter(Boolean)
  1682. const rest = cols.filter(c => !leadProps.includes(c.columnProp))
  1683. return [...leading, ...rest]
  1684. },
  1685. addExtFields () {
  1686. return getEditableExtFieldsFromSchema(this.addFormSchemaFields)
  1687. },
  1688. addDetailExtFields () {
  1689. return this.addExtFields
  1690. },
  1691. addBaseFields () {
  1692. if (!Array.isArray(this.addFormSchemaFields)) {
  1693. return []
  1694. }
  1695. const sortedFields = this.addFormSchemaFields
  1696. .filter(f => f && f.visibleFlag === 'Y' && !this.isExtSchemaField(f))
  1697. .slice()
  1698. .sort((a, b) => {
  1699. const sa = Number(a && a.sortNo)
  1700. const sb = Number(b && b.sortNo)
  1701. const left = Number.isFinite(sa) ? sa : Number.MAX_SAFE_INTEGER
  1702. const right = Number.isFinite(sb) ? sb : Number.MAX_SAFE_INTEGER
  1703. return left - right
  1704. })
  1705. const tailFields = []
  1706. const normalFields = []
  1707. sortedFields.forEach(field => {
  1708. if (ADD_FULL_ROW_FIELD_CODES.has(this.getAddFieldCode(field))) {
  1709. tailFields.push(field)
  1710. return
  1711. }
  1712. normalFields.push(field)
  1713. })
  1714. return [...normalFields, ...tailFields]
  1715. },
  1716. addMasterBaseFields () {
  1717. return this.addBaseFields.filter(field => this.isAddMasterField(field))
  1718. },
  1719. addDetailBaseFields () {
  1720. return this.addBaseFields.filter(field => !this.isAddMasterField(field))
  1721. },
  1722. hasSelectedAddDetailRow () {
  1723. if (!this.addDetailCurrentDraftId) {
  1724. return false
  1725. }
  1726. return this.addDetailRows.some(row => row && row._draftId === this.addDetailCurrentDraftId)
  1727. },
  1728. addDetailDialogTitle () {
  1729. if (this.addDetailDialogMode === 'edit') {
  1730. return '修改明细'
  1731. }
  1732. if (this.addDetailDialogMode === 'copy') {
  1733. return '复制明细'
  1734. }
  1735. return '新增明细'
  1736. },
  1737. uploadColumnMappingEntries () {
  1738. if (!this.uploadColumnMapping || typeof this.uploadColumnMapping !== 'object') {
  1739. return []
  1740. }
  1741. return Object.keys(this.uploadColumnMapping).map(header => ({
  1742. header,
  1743. fieldCode: this.uploadColumnMapping[header]
  1744. }))
  1745. }
  1746. },
  1747. created () {
  1748. this.baseColumnList = JSON.parse(JSON.stringify(this.columnList))
  1749. this.getPartNoOptions()
  1750. this.getSupplierOptions()
  1751. this.getCustomerOptions()
  1752. this.getBuyerOptions()
  1753. },
  1754. mounted () {
  1755. this.updateTableHeight()
  1756. window.addEventListener('resize', this.onWindowResize)
  1757. },
  1758. activated () {
  1759. this.updateTableHeight()
  1760. this.refreshTableLayout()
  1761. },
  1762. deactivated () {
  1763. this.refreshTableLayout()
  1764. },
  1765. beforeDestroy () {
  1766. window.removeEventListener('resize', this.onWindowResize)
  1767. if (this.addPriceLookupTimer) {
  1768. clearTimeout(this.addPriceLookupTimer)
  1769. this.addPriceLookupTimer = null
  1770. }
  1771. },
  1772. methods: {
  1773. canIssuePoOrder,
  1774. canShipPoOrder,
  1775. canClosePoOrder,
  1776. canOpenPoOrder,
  1777. canEditPoColumnByStatus,
  1778. onWindowResize () {
  1779. this.updateTableHeight()
  1780. },
  1781. updateTableHeight () {
  1782. // 主表按可视区动态高度计算,扣除分页与底部预留,避免超出或留白 - rqrq
  1783. this.$nextTick(() => {
  1784. const pageRootEl = this.$refs.pageRoot || this.$el
  1785. const tableVm = this.$refs.poTable
  1786. const tableEl = tableVm && tableVm.$el
  1787. if (!pageRootEl || !tableEl) {
  1788. return
  1789. }
  1790. const pagerVm = this.$refs.mainPager
  1791. const pagerEl = (pagerVm && pagerVm.$el) || pageRootEl.querySelector('.el-pagination')
  1792. let pagerOuterHeight = 0
  1793. if (pagerEl) {
  1794. const pagerRect = pagerEl.getBoundingClientRect()
  1795. const pagerStyle = window.getComputedStyle(pagerEl)
  1796. pagerOuterHeight = pagerRect.height +
  1797. (parseFloat(pagerStyle.marginTop) || 0) +
  1798. (parseFloat(pagerStyle.marginBottom) || 0)
  1799. }
  1800. const rootStyle = window.getComputedStyle(pageRootEl)
  1801. const rootPaddingBottom = parseFloat(rootStyle.paddingBottom) || 0
  1802. const tableTop = tableEl.getBoundingClientRect().top
  1803. const viewportBottomGap = 16
  1804. const availableHeight = window.innerHeight - tableTop - pagerOuterHeight - rootPaddingBottom - viewportBottomGap
  1805. const nextHeight = Number.isFinite(availableHeight) ? Math.floor(availableHeight) : 240
  1806. this.height = nextHeight > 240 ? nextHeight : 240
  1807. this.refreshTableLayout()
  1808. })
  1809. },
  1810. refreshTableLayout () {
  1811. this.$nextTick(() => {
  1812. if (this.$refs.poTable && this.$refs.poTable.doLayout) {
  1813. this.$refs.poTable.doLayout()
  1814. }
  1815. })
  1816. },
  1817. formatColumnCell (row, item) {
  1818. if (item.columnProp === 'orderType') {
  1819. return formatPoOrderTypeLabel(row.orderType)
  1820. }
  1821. const prop = item && item.columnProp
  1822. if (!prop || !row) {
  1823. return ''
  1824. }
  1825. if (row[prop] !== undefined && row[prop] !== null) {
  1826. return row[prop]
  1827. }
  1828. if (row.extValues && typeof row.extValues === 'object' &&
  1829. Object.prototype.hasOwnProperty.call(row.extValues, prop)) {
  1830. return row.extValues[prop]
  1831. }
  1832. return ''
  1833. },
  1834. /** Chooselist 选择列 */
  1835. isChooserEditColumn (columnProp) {
  1836. return this.chooserEditColumnProps.includes(columnProp)
  1837. },
  1838. /**
  1839. * 当前行该列是否可编辑列配置 editAble + 状态限制Part NoVendor NameCustomerBuyerPO_No 草稿/已下达
  1840. */
  1841. canEditCell (row, item) {
  1842. if (!this.editMode || !item) return false
  1843. const prop = item.columnProp
  1844. if (prop === 'customer') return false
  1845. if (this.displayOnlyColumnProps.includes(prop)) return false
  1846. if (this.isChooserEditColumn(prop)) {
  1847. return canEditPoColumnByStatus(row.status, prop)
  1848. }
  1849. if (!item.editAble) return false
  1850. return canEditPoColumnByStatus(row.status, prop)
  1851. },
  1852. getAddFieldCode (field) {
  1853. if (!field) return ''
  1854. if (field.fieldCode) {
  1855. return String(field.fieldCode).trim().toLowerCase()
  1856. }
  1857. return this.camelToSnake(this.getAddFieldProp(field))
  1858. },
  1859. getAddFieldProp (field) {
  1860. if (!field) return ''
  1861. if (field.fieldProp) {
  1862. return String(field.fieldProp).trim()
  1863. }
  1864. if (field.fieldCode) {
  1865. return this.snakeToCamel(field.fieldCode)
  1866. }
  1867. return ''
  1868. },
  1869. isExtSchemaField (field) {
  1870. const code = this.getAddFieldCode(field)
  1871. return code.indexOf('ext_') === 0
  1872. },
  1873. normalizeAddFieldLabel (field) {
  1874. return String((field && field.fieldLabel) || '').trim().toLowerCase().replace(/\s+/g, ' ')
  1875. },
  1876. isAddCreateDateField (field) {
  1877. const code = this.getAddFieldCode(field)
  1878. if (ADD_READONLY_DEFAULT_TODAY_FIELD_CODES.has(code)) {
  1879. return true
  1880. }
  1881. return this.normalizeAddFieldLabel(field) === 'create date'
  1882. },
  1883. isAddPoPlaceDateField (field) {
  1884. const code = this.getAddFieldCode(field)
  1885. if (ADD_EDITABLE_DEFAULT_TODAY_FIELD_CODES.has(code)) {
  1886. return true
  1887. }
  1888. return this.normalizeAddFieldLabel(field) === 'po place date'
  1889. },
  1890. isAddMasterField (field) {
  1891. const code = this.getAddFieldCode(field)
  1892. if (ADD_FORCE_DETAIL_FIELD_CODES.has(code)) {
  1893. return false
  1894. }
  1895. const mandatoryFlag = String((field && field.mandatoryFlag) || '').trim().toUpperCase()
  1896. return mandatoryFlag === 'Y'
  1897. },
  1898. getSchemaFieldByCode (fieldCode) {
  1899. const normalizedCode = String(fieldCode || '').trim().toLowerCase()
  1900. if (!normalizedCode || !Array.isArray(this.addFormSchemaFields)) {
  1901. return null
  1902. }
  1903. return this.addFormSchemaFields.find(field => this.getAddFieldCode(field) === normalizedCode) || null
  1904. },
  1905. getAddTableColumnLabel (fieldCode, fallback) {
  1906. const field = this.getSchemaFieldByCode(fieldCode)
  1907. return (field && field.fieldLabel) || fallback || fieldCode
  1908. },
  1909. snakeToCamel (text) {
  1910. return String(text || '').replace(/_([a-z0-9])/g, (_, c) => c.toUpperCase())
  1911. },
  1912. camelToSnake (text) {
  1913. return String(text || '').replace(/([A-Z])/g, '_$1').toLowerCase().replace(/^_/, '')
  1914. },
  1915. isAddChooserField (field) {
  1916. return ADD_CHOOSER_FIELD_CODES.has(this.getAddFieldCode(field))
  1917. },
  1918. openAddChooserByField (field, target = 'master') {
  1919. const code = this.getAddFieldCode(field)
  1920. if (code === 'supplier_name') {
  1921. this.openAddSupplierSelect(target)
  1922. } else if (code === 'part_no') {
  1923. this.openAddPartNoSelect(target)
  1924. } else if (code === 'departure') {
  1925. this.openAddDepartureSelect(target)
  1926. } else if (code === 'destination') {
  1927. this.openAddDestinationSelect(target)
  1928. } else if (code === 'ship_via') {
  1929. this.openAddShipViaSelect(target)
  1930. }
  1931. },
  1932. getAddChooserPlaceholder (field) {
  1933. const code = this.getAddFieldCode(field)
  1934. if (code === 'supplier_name') return '请选择供应商'
  1935. if (code === 'part_no') return '请选择物料'
  1936. if (code === 'departure') return '请选择始发港'
  1937. if (code === 'destination') return '请选择目的港'
  1938. if (code === 'ship_via') return '请选择运输方式'
  1939. return ''
  1940. },
  1941. getAddFieldSelectOptions (field) {
  1942. const code = this.getAddFieldCode(field)
  1943. const optionsKey = ADD_SELECT_OPTIONS_FIELD_CODE_MAP[code]
  1944. if (!optionsKey) {
  1945. return []
  1946. }
  1947. if (Array.isArray(this[optionsKey])) {
  1948. return this[optionsKey]
  1949. }
  1950. if (this.columnSelectOptions && Array.isArray(this.columnSelectOptions[optionsKey])) {
  1951. return this.columnSelectOptions[optionsKey]
  1952. }
  1953. return []
  1954. },
  1955. isAddSelectField (field) {
  1956. return this.getAddFieldSelectOptions(field).length > 0
  1957. },
  1958. isAddFilterableSelectField (field) {
  1959. return ADD_FILTERABLE_SELECT_FIELD_CODES.has(this.getAddFieldCode(field))
  1960. },
  1961. isAddDateField (field) {
  1962. const editorType = String((field && field.editorType) || '').trim().toLowerCase()
  1963. const dataType = String((field && field.dataType) || '').trim().toLowerCase()
  1964. return editorType === 'date' || editorType === 'datetime' || dataType === 'date' || dataType === 'datetime'
  1965. },
  1966. isAddTextareaField (field) {
  1967. return ADD_TEXTAREA_FIELD_CODES.has(this.getAddFieldCode(field))
  1968. },
  1969. isAddNumberField (field) {
  1970. const code = this.getAddFieldCode(field)
  1971. if (ADD_NUMBER_FIELD_CODES.has(code)) {
  1972. return true
  1973. }
  1974. const dataType = String((field && field.dataType) || '').trim().toLowerCase()
  1975. return dataType === 'number' || dataType === 'num' || dataType === 'decimal' || dataType === 'int'
  1976. },
  1977. isAddFieldDisabled (field) {
  1978. const code = this.getAddFieldCode(field)
  1979. if (code === 'customer') {
  1980. return true
  1981. }
  1982. if (this.isAddCreateDateField(field)) {
  1983. return true
  1984. }
  1985. if (ADD_DISPLAY_ONLY_FIELD_CODES.has(code)) {
  1986. return true
  1987. }
  1988. const editorType = String((field && field.editorType) || '').trim().toLowerCase()
  1989. return editorType === 'readonly'
  1990. },
  1991. getAddFieldSpan (field) {
  1992. if (ADD_FULL_ROW_FIELD_CODES.has(this.getAddFieldCode(field))) {
  1993. return 24
  1994. }
  1995. return this.isAddTextareaField(field) ? 12 : 6
  1996. },
  1997. onAddFieldInput (field, target = 'master') {
  1998. const code = this.getAddFieldCode(field)
  1999. if (code === 'price' || code === 'qty') {
  2000. this.recalcAddFormValue(target)
  2001. }
  2002. if (code === 'qty') {
  2003. this.scheduleAutoFillAddPriceByTierPrice(target)
  2004. }
  2005. },
  2006. getAddTargetForm (target = 'master') {
  2007. return target === 'detail' ? this.addDetailForm : this.addForm
  2008. },
  2009. getSupplierCodeForTargetForm (form, target = 'master') {
  2010. const formSupplier = String((form && form.supplierNo) || '').trim()
  2011. if (formSupplier) {
  2012. return formSupplier
  2013. }
  2014. if (target === 'detail') {
  2015. return String((this.addForm && this.addForm.supplierNo) || '').trim()
  2016. }
  2017. return ''
  2018. },
  2019. scheduleAutoFillAddPriceByTierPrice (target = 'master') {
  2020. if (this.addPriceLookupTimer) {
  2021. clearTimeout(this.addPriceLookupTimer)
  2022. this.addPriceLookupTimer = null
  2023. }
  2024. this.addPriceLookupTimer = setTimeout(() => {
  2025. this.addPriceLookupTimer = null
  2026. this.autoFillAddPriceByTierPrice(target)
  2027. }, 250)
  2028. },
  2029. autoFillAddPriceByTierPrice (target = 'master') {
  2030. if (!this.addDialogVisible) {
  2031. return Promise.resolve()
  2032. }
  2033. const targetForm = this.getAddTargetForm(target)
  2034. if (!targetForm) {
  2035. return Promise.resolve()
  2036. }
  2037. const site = (this.searchData.site || this.$store.state.user.site || '').trim()
  2038. const supplierCode = this.getSupplierCodeForTargetForm(targetForm, target)
  2039. const partNo = String(targetForm.partNo || '').trim()
  2040. const qtyVal = targetForm.qty
  2041. if (!site || !supplierCode || !partNo || qtyVal === '' || qtyVal === null || qtyVal === undefined) {
  2042. return Promise.resolve()
  2043. }
  2044. const qty = Number(qtyVal)
  2045. if (Number.isNaN(qty) || qty < 0) {
  2046. return Promise.resolve()
  2047. }
  2048. const seq = ++this.addPriceLookupSeq
  2049. return listPoCurrentTierPrice({
  2050. site,
  2051. supplierCode,
  2052. partNo
  2053. }).then(({ data }) => {
  2054. if (seq !== this.addPriceLookupSeq) {
  2055. return
  2056. }
  2057. if (!(data && data.code === 0)) {
  2058. return
  2059. }
  2060. const matchedRow = this.findCurrentTierPriceByQty(data.rows || [], qty)
  2061. if (!matchedRow) {
  2062. return
  2063. }
  2064. const normalizedPrice = this.normalizeAddNumericValue(matchedRow.unitPrice)
  2065. if (normalizedPrice === '') {
  2066. return
  2067. }
  2068. this.$set(targetForm, 'price', normalizedPrice)
  2069. this.recalcAddFormValue(target)
  2070. }).catch(() => {})
  2071. },
  2072. findCurrentTierPriceByQty (rows, qty) {
  2073. if (!Array.isArray(rows) || rows.length === 0) {
  2074. return null
  2075. }
  2076. const qtyNum = Number(qty)
  2077. if (Number.isNaN(qtyNum)) {
  2078. return null
  2079. }
  2080. for (const row of rows) {
  2081. const start = Number(row.qtyStart)
  2082. const endRaw = row.qtyEnd
  2083. const end = (endRaw === '' || endRaw === null || endRaw === undefined)
  2084. ? Number.POSITIVE_INFINITY
  2085. : Number(endRaw)
  2086. if (Number.isNaN(start) || Number.isNaN(end)) {
  2087. continue
  2088. }
  2089. if (qtyNum >= start && qtyNum <= end) {
  2090. return row
  2091. }
  2092. }
  2093. return null
  2094. },
  2095. normalizeAddNumericValue (value) {
  2096. if (value === '' || value === null || value === undefined) {
  2097. return ''
  2098. }
  2099. const num = Number(value)
  2100. if (Number.isNaN(num)) {
  2101. return ''
  2102. }
  2103. if (Number.isInteger(num)) {
  2104. return String(num)
  2105. }
  2106. return String(Number(num.toFixed(4)))
  2107. },
  2108. handleClosePoOrder (row) {
  2109. const params = buildPoDetailActionParams(row, this.$store.state.user.site)
  2110. if (!params.id && !(params.orderNo && params.site && params.itemNo != null)) {
  2111. this.$message.error('无法识别订单行,请刷新后重试')
  2112. return
  2113. }
  2114. const poLabel = row.poNo ? `PO ${row.poNo}` : '该订单'
  2115. const itemLabel = row.itemNo != null ? `${row.itemNo}` : ''
  2116. this.$confirm(`确定关闭${poLabel}${itemLabel}`, '提示', { type: 'warning' }).then(() => {
  2117. closePoDetail(params).then(({ data }) => {
  2118. if (data && data.code === 0) {
  2119. this.$message.success('关闭成功')
  2120. this.getDataList()
  2121. } else {
  2122. this.$message.error((data && data.msg) || '关闭失败')
  2123. }
  2124. }).catch(err => {
  2125. this.$message.error((err && err.message) || '关闭失败')
  2126. })
  2127. }).catch(() => {})
  2128. },
  2129. handleIssuePoOrder (row) {
  2130. const params = buildPoDetailActionParams(row, this.$store.state.user.site)
  2131. if (!params.id && !(params.orderNo && params.site && params.itemNo != null)) {
  2132. this.$message.error('无法识别订单行,请刷新后重试')
  2133. return
  2134. }
  2135. const poLabel = row.poNo ? `PO ${row.poNo}` : '该订单'
  2136. const itemLabel = row.itemNo != null ? `${row.itemNo}` : ''
  2137. this.$confirm(`确定下达${poLabel}${itemLabel}`, '提示', { type: 'warning' }).then(() => {
  2138. issuePoDetail(params).then(({ data }) => {
  2139. if (data && data.code === 0) {
  2140. this.$message.success('下达成功')
  2141. this.getDataList()
  2142. } else {
  2143. this.$message.error((data && data.msg) || '下达失败')
  2144. }
  2145. }).catch(err => {
  2146. this.$message.error((err && err.message) || '下达失败')
  2147. })
  2148. }).catch(() => {})
  2149. },
  2150. handleShipPoOrder (row) {
  2151. const params = buildPoDetailActionParams(row, this.$store.state.user.site)
  2152. if (!params.id && !(params.orderNo && params.site && params.itemNo != null)) {
  2153. this.$message.error('无法识别订单行,请刷新后重试')
  2154. return
  2155. }
  2156. const poLabel = row.poNo ? `PO ${row.poNo}` : '该订单'
  2157. const itemLabel = row.itemNo != null ? `${row.itemNo}` : ''
  2158. this.$confirm(`确定将${poLabel}${itemLabel}标记为已发货?`, '提示', { type: 'warning' }).then(() => {
  2159. shipPoDetail(params).then(({ data }) => {
  2160. if (data && data.code === 0) {
  2161. this.$message.success('发货成功')
  2162. this.getDataList()
  2163. } else {
  2164. this.$message.error((data && data.msg) || '发货失败')
  2165. }
  2166. }).catch(err => {
  2167. this.$message.error((err && err.message) || '发货失败')
  2168. })
  2169. }).catch(() => {})
  2170. },
  2171. handleOpenPoOrder (row) {
  2172. const params = buildPoDetailActionParams(row, this.$store.state.user.site)
  2173. if (!params.id && !(params.orderNo && params.site && params.itemNo != null)) {
  2174. this.$message.error('无法识别订单行,请刷新后重试')
  2175. return
  2176. }
  2177. const poLabel = row.poNo ? `PO ${row.poNo}` : '该订单'
  2178. const itemLabel = row.itemNo != null ? `${row.itemNo}` : ''
  2179. const targetStatus = resolvePoOrderStatusByInspectQty(row)
  2180. this.$confirm(`确定打开${poLabel}${itemLabel}?恢复状态为:${targetStatus}`, '提示', { type: 'warning' }).then(() => {
  2181. openPoDetail(params).then(({ data }) => {
  2182. if (data && data.code === 0) {
  2183. this.$message.success('打开成功')
  2184. this.getDataList()
  2185. } else {
  2186. this.$message.error((data && data.msg) || '打开失败')
  2187. }
  2188. }).catch(err => {
  2189. this.$message.error((err && err.message) || '打开失败')
  2190. })
  2191. }).catch(() => {})
  2192. },
  2193. /**
  2194. * 表格当前行变化将选中行深拷贝到 currentRow取消选中时为 null - rqrq
  2195. */
  2196. onTableCurrentChange (row) {
  2197. if (!row) {
  2198. this.currentRow = null
  2199. return
  2200. }
  2201. this.currentRow = row
  2202. },
  2203. /**
  2204. * 解析列对应的下拉选项优先列上 selectOptions其次 columnSelectOptions[selectOptionsKey]
  2205. */
  2206. getSelectOptionsForColumn (item) {
  2207. if (item && item.editType === 'dict-select') {
  2208. const dictType = this.getSchemaDictType(item)
  2209. if (!dictType) {
  2210. return []
  2211. }
  2212. return this.dictSelectOptionsMap[dictType] || []
  2213. }
  2214. if (item.selectOptions && item.selectOptions.length) {
  2215. return item.selectOptions
  2216. }
  2217. const key = item.selectOptionsKey
  2218. if (key && this.columnSelectOptions[key]) {
  2219. return this.columnSelectOptions[key]
  2220. }
  2221. return []
  2222. },
  2223. getSchemaDictType (item) {
  2224. if (!item) {
  2225. return ''
  2226. }
  2227. if (item.dictType) {
  2228. return String(item.dictType).trim()
  2229. }
  2230. const prop = item.columnProp
  2231. if (!prop) {
  2232. return ''
  2233. }
  2234. const field = (this.fieldSchemaFields || []).find(f => {
  2235. const code = String((f && f.fieldCode) || '').trim()
  2236. const fieldProp = String((f && f.fieldProp) || '').trim()
  2237. return fieldProp === prop || this.snakeToCamel(code) === prop
  2238. })
  2239. return field && field.dictType ? String(field.dictType).trim() : ''
  2240. },
  2241. preloadDictSelectOptions (schemaFields) {
  2242. const site = this.searchData.site || this.$store.state.user.site
  2243. if (!site || !Array.isArray(schemaFields) || schemaFields.length === 0) {
  2244. this.dictSelectOptionsMap = {}
  2245. return Promise.resolve()
  2246. }
  2247. const dictTypeSet = new Set()
  2248. schemaFields.forEach(field => {
  2249. const editorType = String((field && field.editorType) || '').trim().toLowerCase()
  2250. if (editorType !== 'dict-select' && editorType !== 'dict_select') {
  2251. return
  2252. }
  2253. const dictType = String((field && field.dictType) || '').trim()
  2254. if (dictType) {
  2255. dictTypeSet.add(dictType)
  2256. }
  2257. })
  2258. const dictTypes = Array.from(dictTypeSet)
  2259. if (dictTypes.length === 0) {
  2260. this.dictSelectOptionsMap = {}
  2261. return Promise.resolve()
  2262. }
  2263. const nextMap = {}
  2264. const loaders = dictTypes.map(dictType => {
  2265. return selectDictDataList({
  2266. site,
  2267. dictType,
  2268. dictTypeList: [],
  2269. status: 'Y'
  2270. }).then(({ data }) => {
  2271. if (data && data.code === 0) {
  2272. nextMap[dictType] = (data.rows || []).map(item => ({
  2273. label: item.dictLabel || item.dictValue,
  2274. value: item.dictValue
  2275. }))
  2276. } else {
  2277. nextMap[dictType] = []
  2278. this.$message.error((data && data.msg) || `字典加载失败: ${dictType}`)
  2279. }
  2280. }).catch(() => {
  2281. nextMap[dictType] = []
  2282. this.$message.error(`字典加载失败: ${dictType}`)
  2283. })
  2284. })
  2285. return Promise.all(loaders).then(() => {
  2286. this.dictSelectOptionsMap = nextMap
  2287. })
  2288. },
  2289. buildQueryRequest() {
  2290. return buildPoOrderQueryParams({
  2291. searchData: this.searchData,
  2292. advancedQueryData: this.advancedQueryData,
  2293. page: this.pageIndex,
  2294. limit: this.pageSize
  2295. })
  2296. },
  2297. onSessionCustomerChange (customer, customerOption) {
  2298. this.searchData.customer = customer
  2299. this.selectedCustomerScope = customerOption || null
  2300. this.dictSelectOptionsMap = {}
  2301. this.getBuyerOptions(customer)
  2302. this.pageIndex = 1
  2303. this.masterRows = []
  2304. this.dataList = []
  2305. this.totalPage = 0
  2306. if (!customer) {
  2307. this.selectedCustomerScope = null
  2308. this.fieldSchemaFields = []
  2309. this.poBuyerOptions = []
  2310. this.columnSelectOptions.poBuyerOptions = []
  2311. this.refreshTableLayout()
  2312. return
  2313. }
  2314. this.loadFieldSchema(customer).then(() => {
  2315. this.getDataList()
  2316. })
  2317. },
  2318. loadFieldSchema (customer) {
  2319. return getPoFieldSchema({
  2320. site: this.searchData.site,
  2321. customer,
  2322. scene: 'list'
  2323. }).then(({ data }) => {
  2324. if (data && data.code === 0 && data.data) {
  2325. this.fieldSchemaFields = data.data.fields || []
  2326. const queryFields = getAdvancedQueryFieldsFromSchema(this.fieldSchemaFields)
  2327. const nextAdvanced = createAdvancedQueryDataFromSchema(queryFields)
  2328. this.advancedQueryData.rows = nextAdvanced.rows
  2329. return this.preloadDictSelectOptions(this.fieldSchemaFields)
  2330. } else {
  2331. this.fieldSchemaFields = []
  2332. this.$message.error((data && data.msg) || '加载字段权限失败')
  2333. }
  2334. }).then(() => {
  2335. this.refreshTableLayout()
  2336. }).catch(() => {
  2337. this.fieldSchemaFields = []
  2338. this.$message.error('加载字段权限失败')
  2339. this.refreshTableLayout()
  2340. })
  2341. },
  2342. handleSearch() {
  2343. if (!this.searchData.customer) {
  2344. this.$message.warning('请先选择 Customer')
  2345. return
  2346. }
  2347. this.pageIndex = 1
  2348. this.getDataList()
  2349. },
  2350. openEamList (tagNo, strVal = '', conSql = '', context = 'row', pickField = '') {
  2351. this.eamTagNo = tagNo
  2352. this.pickContext = context
  2353. this.eamPickField = pickField
  2354. this.$nextTick(() => {
  2355. this.$refs.eamList.init(tagNo, strVal, conSql)
  2356. })
  2357. },
  2358. resetEamPick () {
  2359. this.pickContext = ''
  2360. this.eamTagNo = ''
  2361. this.eamPickField = ''
  2362. this.currentSelectRow = null
  2363. this.currentPartNoRow = null
  2364. this.currentDepartureRow = null
  2365. this.currentDestinationRow = null
  2366. this.currentShipViaRow = null
  2367. },
  2368. openSearchBaseList (val) {
  2369. let strVal = ''
  2370. let conSql = ''
  2371. if (val === 520) {
  2372. strVal = this.searchData.supplierNo || ''
  2373. conSql = " and supplier_no <> '' "
  2374. } else if (val === 2017) {
  2375. strVal = this.searchData.partNo || ''
  2376. conSql = " and part_no <> '' "
  2377. }
  2378. this.openEamList(val, strVal, conSql, 'search')
  2379. },
  2380. getEamData (data) {
  2381. if (!data) {
  2382. return
  2383. }
  2384. const tag = this.eamTagNo
  2385. const ctx = this.pickContext
  2386. if (ctx === 'search') {
  2387. if (tag === 520) {
  2388. this.searchData.supplierNo = data.supplier_no || data.code || ''
  2389. this.searchData.supplierName = data.supplier_name || data.name || ''
  2390. } else if (tag === 2017) {
  2391. this.searchData.partNo = data.part_no || data.code || ''
  2392. }
  2393. this.resetEamPick()
  2394. return
  2395. }
  2396. if (tag === SUPPLIER_CHOOSELIST_TAG || tag === 520) {
  2397. const { supplierNo, supplierName } = parseSupplierChooselistRow(data)
  2398. if (ctx === 'add') {
  2399. this.fillSupplierShortFields(this.addForm, supplierNo, supplierName, false).then(() => {
  2400. this.scheduleAutoFillAddPriceByTierPrice('master')
  2401. })
  2402. } else if (ctx === 'addDetail') {
  2403. this.fillSupplierShortFields(this.addDetailForm, supplierNo, supplierName, false).then(() => {
  2404. this.scheduleAutoFillAddPriceByTierPrice('detail')
  2405. })
  2406. } else if (this.currentSelectRow) {
  2407. const row = this.currentSelectRow
  2408. this.fillSupplierShortFields(row, supplierNo, supplierName, true)
  2409. }
  2410. this.resetEamPick()
  2411. return
  2412. }
  2413. if (tag === 507 || tag === 2017) {
  2414. const partNo = data.part_no || data.partNo || data.code || ''
  2415. if (ctx === 'add') {
  2416. this.$set(this.addForm, 'partNo', partNo)
  2417. this.fillPartCategorySku(partNo, this.addForm)
  2418. this.scheduleAutoFillAddPriceByTierPrice('master')
  2419. } else if (ctx === 'addDetail') {
  2420. this.$set(this.addDetailForm, 'partNo', partNo)
  2421. this.fillPartCategorySku(partNo, this.addDetailForm)
  2422. this.scheduleAutoFillAddPriceByTierPrice('detail')
  2423. } else if (this.currentPartNoRow) {
  2424. this.$set(this.currentPartNoRow, 'partNo', partNo)
  2425. this.fillPartCategorySku(partNo, this.currentPartNoRow)
  2426. this.$set(this.currentPartNoRow, 'changeFlag', 1)
  2427. }
  2428. this.resetEamPick()
  2429. return
  2430. }
  2431. if (tag === 2018) {
  2432. const code = this.eamPickField === 'destination'
  2433. ? (data.port_code || data.portCode || data.destination || '')
  2434. : (data.port_code || data.portCode || data.departure || '')
  2435. const portName = data.port_name || data.portName || ''
  2436. if (ctx === 'add') {
  2437. if (this.eamPickField === 'departure') {
  2438. this.addForm.departure = code
  2439. } else if (this.eamPickField === 'destination') {
  2440. this.addForm.destination = code
  2441. }
  2442. } else if (ctx === 'addDetail') {
  2443. if (this.eamPickField === 'departure') {
  2444. this.addDetailForm.departure = code
  2445. } else if (this.eamPickField === 'destination') {
  2446. this.addDetailForm.destination = code
  2447. }
  2448. } else if (this.eamPickField === 'departure' && this.currentDepartureRow) {
  2449. this.$set(this.currentDepartureRow, 'departure', code)
  2450. this.$set(this.currentDepartureRow, 'departureName', portName)
  2451. this.$set(this.currentDepartureRow, 'changeFlag', 1)
  2452. } else if (this.eamPickField === 'destination' && this.currentDestinationRow) {
  2453. this.$set(this.currentDestinationRow, 'destination', code)
  2454. this.$set(this.currentDestinationRow, 'destinationName', portName)
  2455. this.$set(this.currentDestinationRow, 'changeFlag', 1)
  2456. }
  2457. this.resetEamPick()
  2458. return
  2459. }
  2460. if (tag === 2019) {
  2461. const via = data.method_desc || data.methodDesc || data.shipVia || ''
  2462. if (ctx === 'add') {
  2463. this.addForm.shipVia = via
  2464. } else if (ctx === 'addDetail') {
  2465. this.addDetailForm.shipVia = via
  2466. } else if (this.currentShipViaRow) {
  2467. this.$set(this.currentShipViaRow, 'shipVia', via)
  2468. this.$set(this.currentShipViaRow, 'shipViaCode', data.method_no || data.methodNo || '')
  2469. this.$set(this.currentShipViaRow, 'changeFlag', 1)
  2470. }
  2471. this.resetEamPick()
  2472. }
  2473. },
  2474. getDataList() {
  2475. if (!this.searchData.customer) {
  2476. this.loading = false
  2477. this.dataList = []
  2478. this.masterRows = []
  2479. this.totalPage = 0
  2480. this.refreshTableLayout()
  2481. return
  2482. }
  2483. this.loading = true
  2484. const { params, useAdvanced } = this.buildQueryRequest()
  2485. const request = useAdvanced ? searchPoDetailAdvancedPage(params) : searchPoDetailPage(params)
  2486. request.then(({ data }) => {
  2487. if (data.code === 0) {
  2488. const list = (data.page && data.page.list ? data.page.list : []).map(row => normalizeSchemaRowValues(row))
  2489. this.dataList = list
  2490. this.masterRows = list.map(row => ({
  2491. ...row,
  2492. changeFlag: 0
  2493. }))
  2494. this.pageIndex = data.page.currPage
  2495. this.pageSize = data.page.pageSize
  2496. this.totalPage = data.page.totalCount
  2497. } else {
  2498. this.$message.error((data && data.msg) || '获取列表失败')
  2499. }
  2500. this.loading = false
  2501. this.refreshTableLayout()
  2502. }).catch(() => {
  2503. this.loading = false
  2504. this.$message.error('请求失败')
  2505. this.refreshTableLayout()
  2506. })
  2507. },
  2508. sizeChangeHandle (val) {
  2509. this.pageSize = val
  2510. this.pageIndex = 1
  2511. this.getDataList()
  2512. },
  2513. currentChangeHandle (val) {
  2514. this.pageIndex = val
  2515. this.getDataList()
  2516. },
  2517. onCellChange (row) {
  2518. if (row.price !== undefined || row.qty !== undefined) {
  2519. this.$set(row, 'value', this.calcPoValue(row.price, row.qty))
  2520. }
  2521. this.$set(row, 'changeFlag', 1)
  2522. },
  2523. calcPoValue (price, qty) {
  2524. if (price === '' || price == null || qty === '' || qty == null) {
  2525. return ''
  2526. }
  2527. const p = Number(price)
  2528. const q = Number(qty)
  2529. if (isNaN(p) || isNaN(q)) {
  2530. return ''
  2531. }
  2532. const total = p * q
  2533. return Number.isInteger(total) ? String(total) : String(Number(total.toFixed(4)))
  2534. },
  2535. recalcAddFormValue (target = 'master') {
  2536. const form = this.getAddTargetForm(target)
  2537. if (!form) {
  2538. return
  2539. }
  2540. this.$set(form, 'value', this.calcPoValue(form.price, form.qty))
  2541. },
  2542. validatePriceAndQty (price, qty, label) {
  2543. if (price === '' || price == null) {
  2544. this.$message.warning((label ? label + ':' : '') + '请填写 Unit Price')
  2545. return false
  2546. }
  2547. if (qty === '' || qty == null) {
  2548. this.$message.warning((label ? label + ':' : '') + '请填写 PO Qty')
  2549. return false
  2550. }
  2551. return true
  2552. },
  2553. handleSupplierChange(row, supplierNo) {
  2554. console.log('供应商变更:', supplierNo, '行ID:', row.id)
  2555. const selectedOption = this.supplierOptions.find(opt => opt.value === supplierNo)
  2556. if (selectedOption) {
  2557. this.$set(row, 'supplierName', selectedOption.label)
  2558. this.$set(row, 'supplierNo', selectedOption.value)
  2559. this.$set(row, 'shortOffice', selectedOption.shortOffice || '')
  2560. this.$set(row, 'shortNpc', selectedOption.shortNpc || '')
  2561. this.$set(row, 'changeFlag', 1)
  2562. }
  2563. },
  2564. handlePartNoChange(row, partNo) {
  2565. console.log('物料变更:', partNo, '行ID:', row.id)
  2566. // 根据 partNo 找到对应的物料信息
  2567. const selectedOption = this.poPartNoOptions.find(opt => opt.value === partNo)
  2568. if (selectedOption) {
  2569. // 更新物料相关信息
  2570. this.$set(row, 'partNo', selectedOption.value)
  2571. this.$set(row, 'item', selectedOption.item || '')
  2572. //同时更新 category 和 sku
  2573. this.$set(row, 'category', selectedOption.category || '')
  2574. this.$set(row, 'categoryName', selectedOption.categoryName || '')
  2575. this.$set(row, 'sku', selectedOption.sku || '')
  2576. this.$set(row, 'changeFlag', 1)
  2577. }
  2578. },
  2579. handlePortChange(row, field, value) {
  2580. console.log(`${field}变更:`, value, '行ID:', row.id)
  2581. this.$set(row, field, value) // 直接设置字段值
  2582. this.$set(row, 'changeFlag', 1)
  2583. // 注意:这里还需要同步更新对应的 rowDeparture/rowDestination/rowShipVia
  2584. if (field === 'departure') {
  2585. this.$set(this.rowDeparture, row.id, value)
  2586. } else if (field === 'destination') {
  2587. this.$set(this.rowDestination, row.id, value)
  2588. } else if (field === 'shipVia') {
  2589. this.$set(this.rowShipVia, row.id, value)
  2590. }
  2591. // 如果三个字段都填写了,获取运输方式选项
  2592. if (row.departure && row.destination && row.shipVia) {
  2593. this.getTransportOptions(row.departure, row.destination, row.shipVia)
  2594. }
  2595. },
  2596. getTransportOptions(departure, destination, shipVia) {
  2597. console.log('获取运输方式选项:', departure, destination, shipVia)
  2598. //调用接口获取运输方式选项
  2599. searchTransportOptions({ departure, destination, shipVia }).then(({ data }) => {
  2600. if (data && data.code === 0) {
  2601. this.transportOptions = (data.rows || []).map(item => ({
  2602. label: item.shipVia,
  2603. value: item.shipVia,
  2604. eta: item.eta
  2605. }))
  2606. } else {
  2607. this.$message.error(data.msg || '获取运输方式选项失败')
  2608. }
  2609. }).catch(error => {
  2610. this.$message.error('获取运输方式选项失败: ' + error.message)
  2611. })
  2612. },
  2613. toggleEditMode () {
  2614. if (!this.editMode) {
  2615. this.editMode = true
  2616. return
  2617. }
  2618. const changed = this.masterRows.filter(r => r.changeFlag === 1)
  2619. this.batchUpdatePoOrder(changed)
  2620. },
  2621. cancelEditMode () {
  2622. this.editMode = false
  2623. this.resetEamPick()
  2624. this.getDataList()
  2625. },
  2626. formatCodeOptionLabel (code, name) {
  2627. if (!name) {
  2628. return code || ''
  2629. }
  2630. if (!code) {
  2631. return name
  2632. }
  2633. return `${code} - ${name}`
  2634. },
  2635. getCustomerOptions () {
  2636. return getPoCustomerOptions({
  2637. site: this.$store.state.user.site
  2638. }).then(({ data }) => {
  2639. if (data && data.code === 0) {
  2640. this.poCustomerOptions = (data.rows || []).map(item => ({
  2641. label: this.formatCodeOptionLabel(item.code, item.name),
  2642. value: item.code
  2643. }))
  2644. this.columnSelectOptions.poCustomerOptions = this.poCustomerOptions
  2645. } else {
  2646. this.$message.error(data.msg || '操作失败')
  2647. }
  2648. }).catch(error => {
  2649. this.$message.error('操作失败: ' + error.message)
  2650. })
  2651. },
  2652. getBuyerOptions (customer = this.searchData.customer) {
  2653. const customerCode = (customer || '').trim()
  2654. if (!customerCode) {
  2655. this.poBuyerOptions = []
  2656. this.columnSelectOptions.poBuyerOptions = []
  2657. return Promise.resolve([])
  2658. }
  2659. this.poBuyerOptions = []
  2660. this.columnSelectOptions.poBuyerOptions = []
  2661. return getPoBuyerOptions({
  2662. site: this.$store.state.user.site,
  2663. customer: customerCode
  2664. }).then(({ data }) => {
  2665. if (data && data.code === 0) {
  2666. this.poBuyerOptions = (data.rows || []).map(item => ({
  2667. label: this.formatCodeOptionLabel(item.code, item.name),
  2668. value: item.code
  2669. }))
  2670. this.columnSelectOptions.poBuyerOptions = this.poBuyerOptions
  2671. } else {
  2672. this.$message.error(data.msg || '操作失败')
  2673. }
  2674. }).catch(error => {
  2675. this.$message.error('操作失败: ' + error.message)
  2676. })
  2677. },
  2678. getPartNoOptions() {
  2679. console.log('获取分类选项')
  2680. getPartList(
  2681. {
  2682. site: this.$store.state.user.site
  2683. }
  2684. ).then(({data}) => {
  2685. if (data && data.code === 0) {
  2686. console.log('接口返回的rows:', data.rows) // 查看返回数据
  2687. this.poPartNoOptions = (data.rows || []).map(item => ({
  2688. label: item.partNo,
  2689. value: item.partNo,
  2690. item: item.item,
  2691. category: item.category,
  2692. categoryName: item.categoryName,
  2693. sku: item.sku
  2694. }))
  2695. } else {
  2696. this.$message.error(data.msg || '操作失败')
  2697. }
  2698. }).catch(error => {
  2699. this.$message.error('操作失败: ' + error.message)
  2700. })
  2701. },
  2702. getSupplierOptions(){
  2703. console.log('获取供应商选项')
  2704. checkSrmSupplierList(
  2705. {
  2706. site: this.$store.state.user.site
  2707. }
  2708. ).then(({data}) => {
  2709. if (data && data.code === 0) {
  2710. console.log('接口返回的rows:', data.rows) // 查看返回数据
  2711. this.supplierOptions = (data.rows || []).map(item => ({
  2712. label: item.supplierName,
  2713. value: item.supplierNo,
  2714. shortOffice: item.cshortOffice,
  2715. shortNpc: item.cshortNpc
  2716. }))
  2717. } else {
  2718. this.$message.error(data.msg || '操作失败')
  2719. }
  2720. }).catch(error => {
  2721. this.$message.error('操作失败: ' + error.message)
  2722. })
  2723. },
  2724. getPortOptions(){
  2725. console.log('获取港口选项')
  2726. searchTransportOptions(
  2727. {
  2728. site: this.$store.state.user.site
  2729. }
  2730. ).then(({data}) => {
  2731. if (data && data.code === 0) {
  2732. console.log('接口返回的rows:', data.rows) // 查看返回数据
  2733. this.transportOptions = (data.rows || []).map(item => ({
  2734. label: item.supplierName,
  2735. value: item.supplierNo,
  2736. destination: item.destination,
  2737. departure: item.departure,
  2738. shipVia: item.shipVia,
  2739. etc: item.eta
  2740. }))
  2741. } else {
  2742. this.$message.error(data.msg || '操作失败')
  2743. }
  2744. }).catch(error => {
  2745. this.$message.error('操作失败: ' + error.message)
  2746. })
  2747. },
  2748. // 保存订单信息
  2749. buildBatchUpdateRow (row) {
  2750. const value = this.calcPoValue(row.price, row.qty)
  2751. const extValues = buildExtValuesFromRow(row, this.fieldSchemaFields)
  2752. return {
  2753. orderNo: row.orderNo,
  2754. site: row.site,
  2755. itemNo: row.itemNo,
  2756. supplierNo: row.supplierNo,
  2757. supplierName: row.supplierName,
  2758. buyer: row.buyer,
  2759. customer: row.customer,
  2760. poNo: row.poNo,
  2761. departure: row.departure,
  2762. destination: row.destination,
  2763. shipVia: row.shipVia,
  2764. orderDate: row.orderDate,
  2765. enquiry: row.enquiry,
  2766. confirmed: row.confirmed,
  2767. partNo: row.partNo,
  2768. item: row.item,
  2769. status: row.status,
  2770. qty: row.qty === '' ? null : Number(row.qty),
  2771. price: row.price === '' ? null : Number(row.price),
  2772. value: value === '' ? null : Number(value),
  2773. shipQty: row.shipQty,
  2774. sumPrice: row.sumPrice,
  2775. wantReceiveDate: row.wantReceiveDate,
  2776. narrival: row.narrival,
  2777. crd: row.crd,
  2778. inspection: row.inspection,
  2779. realCrd: row.realCrd,
  2780. poPlaceDate: row.poPlaceDate,
  2781. poPromiseReadyDate: row.poPromiseReadyDate,
  2782. poActualReadyDate: row.poActualReadyDate,
  2783. chinaOfficeInspectionDate: row.chinaOfficeInspectionDate,
  2784. shipToNpcDate: row.shipToNpcDate,
  2785. inspectionResult: row.inspectionResult,
  2786. flexid: row.flexid,
  2787. obd: row.obd,
  2788. docSent: row.docSent,
  2789. telexRelease: row.telexRelease,
  2790. advancePayment: row.advancePayment,
  2791. advancePaid: row.advancePaid,
  2792. planner: row.planner,
  2793. remarkDelay: row.remarkDelay,
  2794. remark: row.remark,
  2795. extValues
  2796. }
  2797. },
  2798. batchUpdatePoOrder(changed) {
  2799. for (const row of changed) {
  2800. const label = row.orderNo ? `订单 ${row.orderNo}` : '订单'
  2801. if (!this.validatePriceAndQty(row.price, row.qty, label)) {
  2802. return
  2803. }
  2804. this.$set(row, 'value', this.calcPoValue(row.price, row.qty))
  2805. }
  2806. const updateData = changed.map(row => this.buildBatchUpdateRow(row))
  2807. // 调用批量修改接口
  2808. batchUpdatePoDetail(updateData)
  2809. .then(({ data }) => {
  2810. if (data && data.code === 0) {
  2811. this.$message({
  2812. message: `已提交 ${updateData.length} 行变更`,
  2813. type: 'success',
  2814. duration: 2000
  2815. })
  2816. changed.forEach(r => {
  2817. this.$set(r, 'changeFlag', 0)
  2818. })
  2819. this.editMode = false
  2820. this.getDataList()
  2821. } else {
  2822. this.$message.error(data.msg || '操作失败')
  2823. }
  2824. })
  2825. .catch(error => {
  2826. this.$message.error('操作失败: ' + error.message)
  2827. })
  2828. },
  2829. // ========== 上传功能 ==========
  2830. // 打开上传弹窗
  2831. exportModal() {
  2832. if (!this.searchData.customer) {
  2833. this.$message.warning('请先选择 Customer')
  2834. return
  2835. }
  2836. this.uploadDialogVisible = true
  2837. this.uploadFileName = ''
  2838. this.uploadFile = null
  2839. this.uploadPreviewList = []
  2840. this.uploadSkippedColumns = []
  2841. this.uploadColumnMapping = {}
  2842. this.uploadPreviewColumns = []
  2843. },
  2844. // 文件选择变化
  2845. handleFileChange(file) {
  2846. this.uploadFile = file.raw
  2847. this.uploadFileName = file.name
  2848. },
  2849. // 预览上传数据
  2850. previewUpload() {
  2851. if (!this.uploadFile) {
  2852. this.$message.warning('请先选择文件')
  2853. return
  2854. }
  2855. if (!this.searchData.customer) {
  2856. this.$message.warning('请先选择 Customer')
  2857. return
  2858. }
  2859. const formData = new FormData()
  2860. formData.append('file', this.uploadFile)
  2861. formData.append('site', this.searchData.site || this.$store.state.user.site)
  2862. formData.append('customer', this.searchData.customer)
  2863. this.$http({
  2864. url: this.$http.adornUrl('/pODetail/previewUpload'),
  2865. method: 'post',
  2866. data: formData
  2867. }).then(({ data }) => {
  2868. if (data && data.code === 0) {
  2869. const preview = data.data || {}
  2870. this.uploadPreviewList = preview.rows || []
  2871. this.uploadSkippedColumns = preview.skippedColumns || []
  2872. this.uploadColumnMapping = preview.columnMapping || {}
  2873. this.refreshUploadPreviewColumns()
  2874. this.$message.success('文件解析成功,请确认数据后点击保存')
  2875. } else {
  2876. this.$message.error(data.msg || '文件解析失败')
  2877. }
  2878. }).catch((error) => {
  2879. console.error('文件解析失败:', error)
  2880. const errorMsg = (error.response && error.response.data && error.response.data.msg) || error.message || '未知错误'
  2881. this.$message.error('文件解析失败: ' + errorMsg)
  2882. })
  2883. },
  2884. refreshUploadPreviewColumns () {
  2885. this.uploadPreviewColumns = buildImportPreviewColumns(
  2886. this.uploadPreviewList,
  2887. this.fieldSchemaFields,
  2888. this.uploadColumnMapping
  2889. )
  2890. },
  2891. exportExcel () {
  2892. if (this.editMode) {
  2893. this.$message.warning('快速编辑模式下不允许导出,请先点击取消或确定')
  2894. return
  2895. }
  2896. if (!this.searchData.customer) {
  2897. this.$message.warning('请先选择 Customer')
  2898. return
  2899. }
  2900. const now = new Date()
  2901. const year = now.getFullYear()
  2902. const month = String(now.getMonth() + 1).padStart(2, '0')
  2903. const day = String(now.getDate()).padStart(2, '0')
  2904. const hour = String(now.getHours()).padStart(2, '0')
  2905. const minute = String(now.getMinutes()).padStart(2, '0')
  2906. const second = String(now.getSeconds()).padStart(2, '0')
  2907. const exportName = `PO订单列表${year}${month}${day}${hour}${minute}${second}`
  2908. const { params, useAdvanced } = buildPoOrderQueryParams({
  2909. searchData: this.searchData,
  2910. advancedQueryData: this.advancedQueryData,
  2911. page: 1,
  2912. limit: 99999
  2913. })
  2914. try {
  2915. excel.exportTable({
  2916. url: useAdvanced ? 'pODetail/advancedPage' : 'pODetail/page',
  2917. columnMapping: this.visibleColumns,
  2918. mergeSetting: [],
  2919. params,
  2920. fileName: exportName + '.xlsx',
  2921. rowFetcher: res => {
  2922. const page = res.data && res.data.page
  2923. const rows = ((page && page.list) || []).map(row => normalizeSchemaRowValues(row))
  2924. return { rows }
  2925. },
  2926. columnFormatter: [],
  2927. dropColumns: []
  2928. })
  2929. } catch (error) {
  2930. this.$message.error('导出失败: ' + error.message)
  2931. }
  2932. },
  2933. // 确认上传
  2934. confirmUpload() {
  2935. if (this.uploadPreviewList.length === 0) {
  2936. this.$message.warning('没有可保存的数据')
  2937. return
  2938. }
  2939. this.$confirm(`确认保存 ${this.uploadPreviewList.length} 条数据吗?`, '提示', {
  2940. confirmButtonText: '确定',
  2941. cancelButtonText: '取消',
  2942. type: 'warning'
  2943. }).then(() => {
  2944. const formData = new FormData()
  2945. formData.append('file', this.uploadFile)
  2946. formData.append('site', this.searchData.site || this.$store.state.user.site)
  2947. formData.append('customer', this.searchData.customer)
  2948. this.$http({
  2949. url: this.$http.adornUrl('/pODetail/batchSave'),
  2950. method: 'post',
  2951. data: formData
  2952. }).then(({ data }) => {
  2953. if (data && data.code === 0) {
  2954. const result = data.data || {}
  2955. const successCount = result.successCount || 0
  2956. const failCount = result.failCount || 0
  2957. let msg = `导入完成:成功 ${successCount} 条,失败 ${failCount}`
  2958. if (result.rowErrors && result.rowErrors.length) {
  2959. msg += ';' + result.rowErrors.slice(0, 3).map(e => `${e.rowNo}${e.message}`).join(';')
  2960. }
  2961. if (failCount > 0) {
  2962. this.$message.warning(msg)
  2963. } else {
  2964. this.$message.success(msg)
  2965. }
  2966. this.uploadDialogVisible = false
  2967. this.getDataList()
  2968. } else {
  2969. this.$message.error(data.msg || '数据保存失败')
  2970. }
  2971. }).catch((error) => {
  2972. console.error('数据保存失败:', error)
  2973. const errorMsg = (error.response && error.response.data && error.response.data.msg) || error.message || '未知错误'
  2974. this.$message.error('数据保存失败: ' + errorMsg)
  2975. })
  2976. }).catch(() => {})
  2977. },
  2978. // 下载模板
  2979. downloadTemplate() {
  2980. const loading = this.$loading({
  2981. lock: true,
  2982. text: '正在下载模板...',
  2983. spinner: 'el-icon-loading',
  2984. background: 'rgba(0, 0, 0, 0.7)'
  2985. })
  2986. this.$http({
  2987. url: this.$http.adornUrl('/pODetail/downloadTemplate'),
  2988. method: 'get',
  2989. responseType: 'blob'
  2990. }).then(response => {
  2991. loading.close()
  2992. // 创建下载文件
  2993. const blob = new Blob([response.data], {
  2994. type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
  2995. })
  2996. const url = window.URL.createObjectURL(blob)
  2997. const link = document.createElement('a')
  2998. link.href = url
  2999. link.download = 'PO订单导入模板.xlsx'
  3000. document.body.appendChild(link)
  3001. link.click()
  3002. document.body.removeChild(link)
  3003. window.URL.revokeObjectURL(url)
  3004. this.$message.success('模板下载成功')
  3005. }).catch(error => {
  3006. loading.close()
  3007. console.error('模板下载失败:', error)
  3008. this.$message.error('模板下载失败,请联系管理员')
  3009. })
  3010. },
  3011. // 打开供应商选择弹窗
  3012. openSupplierSelect(row) {
  3013. this.currentSelectRow = row
  3014. const conSql = " and site = '" + this.$store.state.user.site + "'"
  3015. this.openEamList(SUPPLIER_CHOOSELIST_TAG, '', conSql, 'row', 'supplier')
  3016. },
  3017. applySupplierSelection (target, supplierNo, supplierName, shortOffice, shortNpc, markChanged) {
  3018. if (!target) return
  3019. this.$set(target, 'supplierName', supplierName)
  3020. this.$set(target, 'supplierNo', supplierNo)
  3021. this.$set(target, 'shortOffice', shortOffice)
  3022. this.$set(target, 'shortNpc', shortNpc)
  3023. if (markChanged) {
  3024. this.$set(target, 'changeFlag', 1)
  3025. }
  3026. },
  3027. fillSupplierShortFields (target, supplierNo, supplierName, markChanged) {
  3028. if (!target || !supplierNo) {
  3029. return Promise.resolve()
  3030. }
  3031. this.applySupplierSelection(target, supplierNo, supplierName, target.shortOffice || '', target.shortNpc || '', markChanged)
  3032. return fetchSupplierDetail(supplierNo, this.$store.state.user.site).then(detail => {
  3033. const { shortOffice, shortNpc } = resolveSupplierShortFieldsFromDetail(detail)
  3034. this.applySupplierSelection(target, supplierNo, supplierName, shortOffice, shortNpc, markChanged)
  3035. }).catch(() => {})
  3036. },
  3037. fillPartCategorySku (partNo, target) {
  3038. if (!partNo || !target) return
  3039. const targetSite = String(target.site || this.$store.state.user.site || '').trim()
  3040. const apply = (item, category, categoryName, sku) => {
  3041. this.$set(target, 'item', item)
  3042. this.$set(target, 'category', category)
  3043. this.$set(target, 'categoryName', categoryName)
  3044. this.$set(target, 'sku', sku)
  3045. }
  3046. const fromList = this.poPartNoOptions.find(opt => opt.value === partNo)
  3047. if (fromList) {
  3048. apply(fromList.item || '', fromList.category || '', fromList.categoryName || '', fromList.sku || '')
  3049. return
  3050. }
  3051. searchPartInfo({ site: targetSite, partNo }).then(({ data }) => {
  3052. if (data && data.code === 0 && data.data) {
  3053. apply(data.data.item || '', data.data.category || '', data.data.categoryName || '', data.data.sku || '')
  3054. }
  3055. }).catch(() => {})
  3056. },
  3057. applySelectedPartToTarget (target, partRow, markChanged) {
  3058. if (!target || !partRow) {
  3059. return
  3060. }
  3061. const partNo = partRow.partNo || partRow.part_no || ''
  3062. if (!partNo) {
  3063. return
  3064. }
  3065. this.$set(target, 'partNo', partNo)
  3066. this.$set(target, 'partDesc', partRow.partDesc || '')
  3067. this.$set(target, 'item', partRow.item || '')
  3068. this.$set(target, 'sku', partRow.sku || '')
  3069. this.fillPartCategorySku(partNo, target)
  3070. if (markChanged) {
  3071. this.$set(target, 'changeFlag', 1)
  3072. }
  3073. },
  3074. openSupplierPartDialog (context, row = null) {
  3075. const site = String((context === 'row'
  3076. ? (row && row.site)
  3077. : (context === 'addDetail' ? this.addDetailForm.site : this.searchData.site)) || this.$store.state.user.site || '').trim()
  3078. const supplierNo = String((context === 'row'
  3079. ? (row && (row.supplierNo || row.supplier_no || row.supplierCode || row.supplier_code))
  3080. : (context === 'addDetail'
  3081. ? (this.addDetailForm.supplierNo || this.addForm.supplierNo || '')
  3082. : this.addForm.supplierNo)) || '').trim()
  3083. if (!site) {
  3084. this.$message.warning('请先选择site')
  3085. return
  3086. }
  3087. if (!supplierNo) {
  3088. this.$message.warning('请先选择供应商')
  3089. return
  3090. }
  3091. this.supplierPartDialogVisible = true
  3092. this.supplierPartDialogLoading = true
  3093. this.supplierPartDialogRows = []
  3094. this.supplierPartDialogContext = context
  3095. this.supplierPartDialogTargetRow = context === 'row' ? row : null
  3096. this.supplierPartDialogSupplierNo = supplierNo
  3097. this.supplierPartDialogSite = site
  3098. listPoSupplierPartOptions({
  3099. site,
  3100. supplierCode: supplierNo,
  3101. active: 'Y'
  3102. }).then(({ data }) => {
  3103. if (data && data.code === 0) {
  3104. this.supplierPartDialogRows = (data.rows || []).map(item => ({
  3105. partNo: item.partNo || '',
  3106. partDesc: item.partDesc || '',
  3107. item: item.item || '',
  3108. sku: item.sku || ''
  3109. }))
  3110. if (this.supplierPartDialogRows.length === 0) {
  3111. this.$message.warning('该供应商未维护供应商可供物料')
  3112. }
  3113. return
  3114. }
  3115. this.supplierPartDialogRows = []
  3116. this.$message.error((data && data.msg) || '获取供应商可供物料失败')
  3117. }).catch(() => {
  3118. this.supplierPartDialogRows = []
  3119. this.$message.error('获取供应商可供物料失败')
  3120. }).finally(() => {
  3121. this.supplierPartDialogLoading = false
  3122. })
  3123. },
  3124. handleSupplierPartRowDblClick (row) {
  3125. if (!row) {
  3126. return
  3127. }
  3128. if (this.supplierPartDialogContext === 'add') {
  3129. this.applySelectedPartToTarget(this.addForm, row, false)
  3130. this.scheduleAutoFillAddPriceByTierPrice('master')
  3131. } else if (this.supplierPartDialogContext === 'addDetail') {
  3132. this.applySelectedPartToTarget(this.addDetailForm, row, false)
  3133. this.scheduleAutoFillAddPriceByTierPrice('detail')
  3134. } else if (this.supplierPartDialogContext === 'row' && this.supplierPartDialogTargetRow) {
  3135. this.applySelectedPartToTarget(this.supplierPartDialogTargetRow, row, true)
  3136. }
  3137. this.supplierPartDialogVisible = false
  3138. },
  3139. resetSupplierPartDialog () {
  3140. this.supplierPartDialogLoading = false
  3141. this.supplierPartDialogRows = []
  3142. this.supplierPartDialogContext = ''
  3143. this.supplierPartDialogTargetRow = null
  3144. this.supplierPartDialogSupplierNo = ''
  3145. this.supplierPartDialogSite = ''
  3146. },
  3147. resolveAddOrderTypeByCustomer () {
  3148. const customerOrderType = this.selectedCustomerScope && this.selectedCustomerScope.orderType
  3149. ? this.selectedCustomerScope.orderType
  3150. : 'PO'
  3151. return formatPoOrderTypeLabel(customerOrderType) || '采购订单'
  3152. },
  3153. openAddDialog () {
  3154. if (!this.searchData.customer) {
  3155. this.$message.warning('请选择customer')
  3156. return
  3157. }
  3158. if (this.addPriceLookupTimer) {
  3159. clearTimeout(this.addPriceLookupTimer)
  3160. this.addPriceLookupTimer = null
  3161. }
  3162. this.addPriceLookupSeq += 1
  3163. this.addForm = this.createEmptyAddForm(this.resolveAddOrderTypeByCustomer())
  3164. this.addForm.customer = this.searchData.customer
  3165. this.addDetailRows = []
  3166. this.addDetailCurrentDraftId = ''
  3167. this.addDetailDialogMode = 'create'
  3168. this.addDetailDialogVisible = false
  3169. this.addDetailEditingDraftId = ''
  3170. this.addDetailForm = this.createEmptyAddDetailForm()
  3171. this.addDialogVisible = true
  3172. this.loadAddFieldSchema(this.searchData.customer)
  3173. if (!this.poPartNoOptions.length) {
  3174. this.getPartNoOptions()
  3175. }
  3176. if (!this.poCustomerOptions.length) {
  3177. this.getCustomerOptions()
  3178. }
  3179. this.getBuyerOptions(this.searchData.customer)
  3180. },
  3181. loadAddFieldSchema (customer) {
  3182. return getPoFieldSchema({
  3183. site: this.searchData.site,
  3184. customer,
  3185. scene: 'add'
  3186. }).then(({ data }) => {
  3187. if (data && data.code === 0 && data.data) {
  3188. this.addFormSchemaFields = data.data.fields || []
  3189. this.applyAddDefaultDateBySchema()
  3190. this.addDetailForm = this.createEmptyAddDetailForm()
  3191. } else {
  3192. this.addFormSchemaFields = []
  3193. }
  3194. }).catch(() => {
  3195. this.addFormSchemaFields = []
  3196. })
  3197. },
  3198. getTodayDateYmd () {
  3199. if (this.dayjs) {
  3200. return this.dayjs().format('YYYY-MM-DD')
  3201. }
  3202. const now = new Date()
  3203. const pad = (num) => String(num).padStart(2, '0')
  3204. return `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}`
  3205. },
  3206. applyAddDefaultDateBySchema () {
  3207. this.applyDefaultDateBySchemaToForm(this.addForm)
  3208. },
  3209. applyDefaultDateBySchemaToForm (targetForm) {
  3210. if (!targetForm || !Array.isArray(this.addFormSchemaFields) || !this.addFormSchemaFields.length) {
  3211. return
  3212. }
  3213. const today = this.getTodayDateYmd()
  3214. this.addFormSchemaFields.forEach(field => {
  3215. const prop = this.getAddFieldProp(field)
  3216. if (!prop) {
  3217. return
  3218. }
  3219. if (this.isAddCreateDateField(field) || this.isAddPoPlaceDateField(field)) {
  3220. this.$set(targetForm, prop, today)
  3221. }
  3222. })
  3223. },
  3224. getFormExtValue (targetForm, fieldCode) {
  3225. if (!targetForm || !targetForm.extValues) {
  3226. return ''
  3227. }
  3228. return targetForm.extValues[fieldCode] == null ? '' : targetForm.extValues[fieldCode]
  3229. },
  3230. setFormExtValue (targetForm, fieldCode, val) {
  3231. if (!targetForm) {
  3232. return
  3233. }
  3234. if (!targetForm.extValues) {
  3235. this.$set(targetForm, 'extValues', {})
  3236. }
  3237. this.$set(targetForm.extValues, fieldCode, val)
  3238. },
  3239. getAddExtValue (fieldCode) {
  3240. return this.getFormExtValue(this.addForm, fieldCode)
  3241. },
  3242. setAddExtValue (fieldCode, val) {
  3243. this.setFormExtValue(this.addForm, fieldCode, val)
  3244. },
  3245. getAddDetailExtValue (fieldCode) {
  3246. return this.getFormExtValue(this.addDetailForm, fieldCode)
  3247. },
  3248. setAddDetailExtValue (fieldCode, val) {
  3249. this.setFormExtValue(this.addDetailForm, fieldCode, val)
  3250. },
  3251. createEmptyAddForm (orderType = '采购订单') {
  3252. const today = this.getTodayDateYmd()
  3253. return {
  3254. customer: '',
  3255. buyer: '',
  3256. supplierName: '',
  3257. supplierNo: '',
  3258. shortOffice: '',
  3259. shortNpc: '',
  3260. poNo: '',
  3261. orderType: orderType || '采购订单',
  3262. partNo: '',
  3263. item: '',
  3264. category: '',
  3265. categoryName: '',
  3266. sku: '',
  3267. price: '',
  3268. qty: '',
  3269. value: '',
  3270. shipQty: '',
  3271. sumPrice: '',
  3272. departure: '',
  3273. destination: '',
  3274. shipVia: '',
  3275. orderDate: '',
  3276. enquiry: '',
  3277. status: '草稿',
  3278. confirmed: '',
  3279. wantReceiveDate: '',
  3280. narrival: '',
  3281. crd: '',
  3282. inspection: '',
  3283. realCrd: '',
  3284. poPlaceDate: '',
  3285. poPromiseReadyDate: '',
  3286. poActualReadyDate: '',
  3287. chinaOfficeInspectionDate: '',
  3288. shipToNpcDate: '',
  3289. inspectionResult: '',
  3290. flexid: '',
  3291. obd: '',
  3292. docSent: '',
  3293. telexRelease: '',
  3294. advancePayment: 'N',
  3295. advancePaid: '',
  3296. planner: '',
  3297. remarkDelay: '',
  3298. remark: '',
  3299. extValues: {}
  3300. }
  3301. },
  3302. createEmptyAddDetailForm () {
  3303. const form = this.createEmptyAddForm(this.addForm.orderType || this.resolveAddOrderTypeByCustomer())
  3304. form.customer = this.searchData.customer || form.customer
  3305. form.site = this.searchData.site || this.$store.state.user.site
  3306. this.applyDefaultDateBySchemaToForm(form)
  3307. return form
  3308. },
  3309. cloneAddDraftRow (row) {
  3310. const cloned = JSON.parse(JSON.stringify(row || {}))
  3311. if (!cloned.extValues || typeof cloned.extValues !== 'object') {
  3312. cloned.extValues = {}
  3313. }
  3314. return cloned
  3315. },
  3316. buildAddDetailRowFromForm () {
  3317. const row = this.cloneAddDraftRow(this.addDetailForm)
  3318. if (!row._draftId) {
  3319. this.addDetailDraftSeq += 1
  3320. row._draftId = `DRAFT_${Date.now()}_${this.addDetailDraftSeq}`
  3321. }
  3322. row.value = this.calcPoValue(row.price, row.qty)
  3323. return row
  3324. },
  3325. getSelectedAddDetailRow () {
  3326. if (!this.addDetailCurrentDraftId) {
  3327. return null
  3328. }
  3329. return this.addDetailRows.find(row => row && row._draftId === this.addDetailCurrentDraftId) || null
  3330. },
  3331. onAddDetailCurrentChange (row) {
  3332. this.addDetailCurrentDraftId = row && row._draftId ? row._draftId : ''
  3333. },
  3334. onAddDetailRowClick (row) {
  3335. this.addDetailCurrentDraftId = row && row._draftId ? row._draftId : ''
  3336. if (this.$refs.addDetailTable && this.$refs.addDetailTable.setCurrentRow) {
  3337. this.$refs.addDetailTable.setCurrentRow(row)
  3338. }
  3339. },
  3340. openAddDetailDialog () {
  3341. this.addDetailDialogMode = 'create'
  3342. this.addDetailEditingDraftId = ''
  3343. this.addDetailForm = this.createEmptyAddDetailForm()
  3344. this.addDetailDialogVisible = true
  3345. },
  3346. openEditAddDetailDialog () {
  3347. const selected = this.getSelectedAddDetailRow()
  3348. if (!selected) {
  3349. this.$message.warning('请先选择一条明细')
  3350. return
  3351. }
  3352. this.addDetailDialogMode = 'edit'
  3353. this.addDetailEditingDraftId = selected._draftId
  3354. this.addDetailForm = this.cloneAddDraftRow(selected)
  3355. this.addDetailDialogVisible = true
  3356. },
  3357. openCopyAddDetailDialog () {
  3358. const selected = this.getSelectedAddDetailRow()
  3359. if (!selected) {
  3360. this.$message.warning('请先选择一条明细')
  3361. return
  3362. }
  3363. const copied = this.cloneAddDraftRow(selected)
  3364. copied._draftId = ''
  3365. copied.partNo = ''
  3366. this.addDetailDialogMode = 'copy'
  3367. this.addDetailEditingDraftId = ''
  3368. this.addDetailForm = copied
  3369. this.addDetailDialogVisible = true
  3370. },
  3371. removeAddDetailRow () {
  3372. const selected = this.getSelectedAddDetailRow()
  3373. if (!selected) {
  3374. this.$message.warning('请先选择一条明细')
  3375. return
  3376. }
  3377. this.addDetailRows = this.addDetailRows.filter(row => row && row._draftId !== selected._draftId)
  3378. this.addDetailCurrentDraftId = ''
  3379. this.$nextTick(() => {
  3380. if (this.$refs.addDetailTable && this.$refs.addDetailTable.setCurrentRow) {
  3381. this.$refs.addDetailTable.setCurrentRow(null)
  3382. }
  3383. })
  3384. },
  3385. isAddFormFieldEmpty (value) {
  3386. if (value === null || value === undefined) {
  3387. return true
  3388. }
  3389. if (typeof value === 'string') {
  3390. return value.trim() === ''
  3391. }
  3392. return false
  3393. },
  3394. validateRequiredFieldGroup (fields, form, prefixLabel = '') {
  3395. if (!Array.isArray(fields)) {
  3396. return true
  3397. }
  3398. for (const field of fields) {
  3399. if (!field || field.requiredFlag !== 'Y') {
  3400. continue
  3401. }
  3402. const prop = this.getAddFieldProp(field)
  3403. if (!prop) {
  3404. continue
  3405. }
  3406. const value = form ? form[prop] : null
  3407. if (this.isAddFormFieldEmpty(value)) {
  3408. this.$message.warning(`${prefixLabel}${field.fieldLabel}不能为空`)
  3409. return false
  3410. }
  3411. }
  3412. return true
  3413. },
  3414. validateRequiredExtFieldGroup (fields, form, prefixLabel = '') {
  3415. if (!Array.isArray(fields)) {
  3416. return true
  3417. }
  3418. for (const field of fields) {
  3419. if (!field || field.requiredFlag !== 'Y') {
  3420. continue
  3421. }
  3422. const fieldCode = String((field && field.fieldCode) || '').trim()
  3423. if (!fieldCode) {
  3424. continue
  3425. }
  3426. const value = this.getFormExtValue(form, fieldCode)
  3427. if (this.isAddFormFieldEmpty(value)) {
  3428. this.$message.warning(`${prefixLabel}${field.fieldLabel}不能为空`)
  3429. return false
  3430. }
  3431. }
  3432. return true
  3433. },
  3434. saveAddDetailDraft () {
  3435. if (!this.validateRequiredFieldGroup(this.addDetailBaseFields, this.addDetailForm, '明细:')) {
  3436. return
  3437. }
  3438. if (!this.validateRequiredExtFieldGroup(this.addDetailExtFields, this.addDetailForm, '明细:')) {
  3439. return
  3440. }
  3441. const nextRow = this.buildAddDetailRowFromForm()
  3442. if (this.addDetailDialogMode === 'edit' && this.addDetailEditingDraftId) {
  3443. const targetIndex = this.addDetailRows.findIndex(row => row && row._draftId === this.addDetailEditingDraftId)
  3444. if (targetIndex >= 0) {
  3445. nextRow._draftId = this.addDetailEditingDraftId
  3446. this.$set(this.addDetailRows, targetIndex, nextRow)
  3447. }
  3448. } else {
  3449. this.addDetailRows.push(nextRow)
  3450. }
  3451. this.addDetailCurrentDraftId = nextRow._draftId
  3452. this.addDetailDialogVisible = false
  3453. this.$nextTick(() => {
  3454. if (this.$refs.addDetailTable && this.$refs.addDetailTable.setCurrentRow) {
  3455. const selected = this.addDetailRows.find(row => row && row._draftId === nextRow._draftId) || null
  3456. this.$refs.addDetailTable.setCurrentRow(selected)
  3457. }
  3458. })
  3459. },
  3460. openAddSupplierSelect (target = 'master') {
  3461. const conSql = " and site = '" + this.$store.state.user.site + "'"
  3462. const context = target === 'detail' ? 'addDetail' : 'add'
  3463. this.openEamList(SUPPLIER_CHOOSELIST_TAG, '', conSql, context, 'supplier')
  3464. },
  3465. openAddPartNoSelect (target = 'master') {
  3466. this.openSupplierPartDialog(target === 'detail' ? 'addDetail' : 'add')
  3467. },
  3468. openAddDepartureSelect (target = 'master') {
  3469. const context = target === 'detail' ? 'addDetail' : 'add'
  3470. this.openEamList(2018, '', this.buildDeparturePortConSql(), context, 'departure')
  3471. },
  3472. openAddDestinationSelect (target = 'master') {
  3473. const context = target === 'detail' ? 'addDetail' : 'add'
  3474. this.openEamList(2018, '', this.buildDestinationPortConSql(), context, 'destination')
  3475. },
  3476. openAddShipViaSelect (target = 'master') {
  3477. const context = target === 'detail' ? 'addDetail' : 'add'
  3478. this.openEamList(2019, '', '', context, 'shipVia')
  3479. },
  3480. buildMergedAddDetailForm (detailRow) {
  3481. const merged = {}
  3482. this.addMasterBaseFields.forEach(field => {
  3483. const prop = this.getAddFieldProp(field)
  3484. if (!prop) return
  3485. merged[prop] = this.addForm[prop]
  3486. })
  3487. this.addDetailBaseFields.forEach(field => {
  3488. const prop = this.getAddFieldProp(field)
  3489. if (!prop) return
  3490. merged[prop] = detailRow ? detailRow[prop] : null
  3491. })
  3492. merged.customer = this.searchData.customer || this.addForm.customer || ''
  3493. merged.extValues = { ...(detailRow && detailRow.extValues ? detailRow.extValues : {}) }
  3494. return merged
  3495. },
  3496. saveAddPoOrder () {
  3497. if (!this.validateRequiredFieldGroup(this.addMasterBaseFields, this.addForm, '主记录:')) {
  3498. return
  3499. }
  3500. if (!this.addForm.poNo || String(this.addForm.poNo).trim() === '') {
  3501. this.$message.warning('主记录:请填写 PO No')
  3502. return
  3503. }
  3504. if ((!this.addForm.supplierName || String(this.addForm.supplierName).trim() === '') &&
  3505. (!this.addForm.supplierNo || String(this.addForm.supplierNo).trim() === '')) {
  3506. this.$message.warning('主记录:请填写 Vendor Name')
  3507. return
  3508. }
  3509. if (this.addDetailRows.length === 0) {
  3510. this.$message.warning('请至少新增一条明细')
  3511. return
  3512. }
  3513. const payloadList = []
  3514. for (let i = 0; i < this.addDetailRows.length; i++) {
  3515. const row = this.addDetailRows[i]
  3516. if (!this.validateRequiredFieldGroup(this.addDetailBaseFields, row, `${i + 1}行明细:`)) {
  3517. return
  3518. }
  3519. if (!this.validateRequiredExtFieldGroup(this.addDetailExtFields, row, `${i + 1}行明细:`)) {
  3520. return
  3521. }
  3522. const mergedForm = this.buildMergedAddDetailForm(row)
  3523. if (!mergedForm.partNo || String(mergedForm.partNo).trim() === '') {
  3524. this.$message.warning(`${i + 1}行明细:请填写 Part No`)
  3525. return
  3526. }
  3527. if (!this.validatePriceAndQty(mergedForm.price, mergedForm.qty, `${i + 1}`)) {
  3528. return
  3529. }
  3530. const calculatedValue = this.calcPoValue(mergedForm.price, mergedForm.qty)
  3531. payloadList.push(this.buildAddPayload(calculatedValue, mergedForm, this.addBaseFields, this.addExtFields))
  3532. }
  3533. if (payloadList.length === 0) {
  3534. this.$message.warning('请至少新增一条明细')
  3535. return
  3536. }
  3537. this.addFormSaving = true
  3538. const runCreate = (idx) => {
  3539. if (idx >= payloadList.length) {
  3540. return Promise.resolve()
  3541. }
  3542. return createPoOrder(payloadList[idx]).then(({ data }) => {
  3543. if (data && data.code === 0) {
  3544. return runCreate(idx + 1)
  3545. }
  3546. const errMsg = (data && data.msg) || '新增失败'
  3547. throw new Error(`${idx + 1}行:${errMsg}`)
  3548. })
  3549. }
  3550. runCreate(0).then(() => {
  3551. this.$message.success(`新增成功,共 ${payloadList.length}`)
  3552. this.addDialogVisible = false
  3553. this.addDetailDialogVisible = false
  3554. this.getDataList()
  3555. }).catch(err => {
  3556. this.$message.error((err && err.message) || '新增失败')
  3557. }).finally(() => {
  3558. this.addFormSaving = false
  3559. })
  3560. },
  3561. buildAddPayload (calculatedValue, sourceForm = this.addForm, baseFields = this.addBaseFields, extFields = this.addExtFields) {
  3562. const payload = {
  3563. site: this.$store.state.user.site,
  3564. customer: this.searchData.customer,
  3565. extValues: {}
  3566. }
  3567. baseFields.forEach(field => {
  3568. const prop = this.getAddFieldProp(field)
  3569. const code = this.getAddFieldCode(field)
  3570. if (!prop || !code) {
  3571. return
  3572. }
  3573. let value = sourceForm[prop]
  3574. if (code === 'customer') {
  3575. value = this.searchData.customer || value
  3576. } else if (code === 'value') {
  3577. value = calculatedValue === '' ? null : Number(calculatedValue)
  3578. } else if (code === 'price' || code === 'qty' || code === 'ship_qty' || code === 'sum_price') {
  3579. value = (value === '' || value === null || value === undefined) ? null : Number(value)
  3580. }
  3581. payload[prop] = value
  3582. })
  3583. extFields.forEach(field => {
  3584. const fieldCode = (field && field.fieldCode) ? String(field.fieldCode).trim() : ''
  3585. if (!fieldCode) {
  3586. return
  3587. }
  3588. const value = this.getFormExtValue(sourceForm, fieldCode)
  3589. if (value === '' || value === null || value === undefined) {
  3590. return
  3591. }
  3592. payload.extValues[fieldCode] = value
  3593. })
  3594. return payload
  3595. },
  3596. openPartNoSelect (row) {
  3597. this.openSupplierPartDialog('row', row)
  3598. },
  3599. openDepartureSelect (row) {
  3600. this.currentDepartureRow = row
  3601. this.openEamList(2018, '', this.buildDeparturePortConSql(), 'row', 'departure')
  3602. },
  3603. openDestinationSelect (row) {
  3604. this.currentDestinationRow = row
  3605. this.openEamList(2018, '', this.buildDestinationPortConSql(), 'row', 'destination')
  3606. },
  3607. openShipViaSelect (row) {
  3608. this.currentShipViaRow = row
  3609. this.openEamList(2019, '', '', 'row', 'shipVia')
  3610. },
  3611. /**
  3612. * 判断是否为新增行没有 id id 为空表示新增
  3613. */
  3614. isNewRow(row) {
  3615. return !row.id || row.id === '' || row.id === null || row.id === undefined
  3616. },
  3617. buildDeparturePortConSql () {
  3618. return " and is_departure_port = 'Y'"
  3619. },
  3620. buildDestinationPortConSql () {
  3621. return " and is_destination_port = 'Y'"
  3622. }
  3623. }
  3624. }
  3625. </script>
  3626. <style scoped lang="scss">
  3627. .po-add-form-wrap {
  3628. max-height: calc(100vh - 210px);
  3629. overflow-y: scroll;
  3630. overflow-x: hidden;
  3631. padding: 0 12px 8px;
  3632. }
  3633. /* 预留滚动槽位,避免滚动条出现/消失导致表单宽度抖动 */
  3634. @supports (scrollbar-gutter: stable) {
  3635. .po-add-form-wrap {
  3636. overflow-y: auto;
  3637. scrollbar-gutter: stable both-edges;
  3638. }
  3639. }
  3640. /* 防止滚动条切换时标签换行,进一步降低布局抖动 */
  3641. .po-add-form-wrap /deep/ .el-form--label-top .el-form-item__label {
  3642. white-space: nowrap;
  3643. }
  3644. .po-add-detail-toolbar {
  3645. margin: 10px 0 8px;
  3646. display: flex;
  3647. justify-content: space-between;
  3648. align-items: center;
  3649. }
  3650. .po-add-detail-title {
  3651. font-size: 13px;
  3652. color: #606266;
  3653. }
  3654. .el-table /deep/ .cell {
  3655. height: auto;
  3656. line-height: 1.5;
  3657. }
  3658. </style>