parentList.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396
  1. <template>
  2. <el-dialog
  3. title="选择供应商"
  4. :visible.sync="visible"
  5. :before-close="handleClose"
  6. :close-on-click-modal="false"
  7. top="5vh"
  8. :close-on-press-escape="false"
  9. append-to-body
  10. width="70%"
  11. >
  12. <el-card shadow="never">
  13. <contact-search :fields="searchFields" @search="reload"></contact-search>
  14. <ele-split-layout
  15. :width="treeWidth"
  16. allow-collapse
  17. :right-style="{ overflow: 'hidden' }"
  18. >
  19. <div
  20. v-if="showCategoryTree"
  21. class="ele-border-lighter split-layout-right-content"
  22. >
  23. <AssetTree
  24. @handleNodeClick="handleNodeClick"
  25. id="19"
  26. :isFirstRefreshTable="false"
  27. ref="treeList"
  28. />
  29. </div>
  30. <!-- 表格 -->
  31. <template v-slot:content>
  32. <ele-pro-table
  33. ref="table"
  34. :columns="columns"
  35. :datasource="datasource"
  36. row-key="id"
  37. height="calc(100vh - 450px)"
  38. class="dict-table"
  39. @cell-click="cellClick"
  40. @header-dragend="handleHeaderDragend">
  41. <!-- 表头工具栏 -->
  42. <template v-slot:action="{ row }">
  43. <el-radio class="radio" v-model="radio" :label="row.id"
  44. ><i></i
  45. ></el-radio>
  46. </template>
  47. </ele-pro-table>
  48. </template>
  49. </ele-split-layout>
  50. </el-card>
  51. <div slot="footer">
  52. <el-button type="primary" size="small" @click="selected">选择</el-button>
  53. <el-button size="small" @click="handleClose">关闭</el-button>
  54. </div>
  55. </el-dialog>
  56. </template>
  57. <script>
  58. import { contactPageUsages } from '@/api/saleManage/contact';
  59. import ContactSearch from './contactSearch.vue';
  60. import AssetTree from '@/components/AssetTree';
  61. import tabMixins from '@/mixins/tableColumnsMixin';
  62. export default {
  63. mixins: [tabMixins],
  64. components: {
  65. ContactSearch,
  66. AssetTree
  67. },
  68. props: {
  69. classType: {
  70. type: Number,
  71. default: 1
  72. },
  73. /**
  74. * 自定义列表数据源。默认(不传)走客户/供应商档案 contactPageUsages;
  75. * 一旦传入就完全接管请求,组件只负责拼装
  76. * { pageNum, size, ...queryParams, ...paramMapper(where) }。
  77. *
  78. * 例:对账池发起对账时要选「有可对账数据的往来单位」,
  79. * 传 getAccountPoolCustomerList(/eom/accountstatementpool/customers)。
  80. */
  81. dataSourceApi: {
  82. type: Function,
  83. default: null
  84. },
  85. /** 固定查询参数,如对账池接口必填的 type(1 销售订单 / 2 采购订单) */
  86. queryParams: {
  87. type: Object,
  88. default: () => ({})
  89. },
  90. /**
  91. * 搜索条件字段名映射:接收搜索表单的 where,返回自定义接口认识的字段名。
  92. * 不传时原样透传 where(适用于参数名与搜索项一致的场景)。
  93. */
  94. paramMapper: {
  95. type: Function,
  96. default: null
  97. },
  98. /** 左侧客户类别树。接口不支持 categoryId 过滤时传 false 隐藏 */
  99. showCategoryTree: {
  100. type: Boolean,
  101. default: true
  102. },
  103. /**
  104. * 自定义搜索项,透传给 contact-search;不传用默认的客户档案搜索项。
  105. * 搜索项的 value 就是提交给列表接口的参数名。
  106. */
  107. searchFields: {
  108. type: Array,
  109. default: null
  110. },
  111. /** 自定义表格列(列对象浅拷贝,不污染父组件传入的数组);不传用默认供应商档案列 */
  112. tableColumns: {
  113. type: Array,
  114. default: null
  115. }
  116. },
  117. computed: {
  118. columns() {
  119. this.columnsVersion; // mixin 改列宽/列显隐时触发重算
  120. // 拷贝一层:applyColumnWidth 会往列对象上写 width,
  121. // 直接改父组件传进来的数组会污染调用方的数据
  122. const cols = (this.tableColumns || this.defaultColumns).map((c) => ({
  123. ...c
  124. }));
  125. return this.applyColumnWidth(cols);
  126. },
  127. cacheKeyUrl() {
  128. // 两套列(供应商档案列 / 调用方自定义列)必须用不同的缓存 key:
  129. // mixin 的 columnsContrast 按 label 比对,共用一个 key 会把另一套列
  130. // 当成「已删除列」洗掉(localStorage + 服务端 table-config 都按它存)
  131. return this.tableColumns
  132. ? 'views_purchasingManage_supplierManage_components_parentList_custom'
  133. : 'views_purchasingManage_supplierManage_components_parentList';
  134. },
  135. treeWidth() {
  136. return this.showCategoryTree ? '244px' : '0px';
  137. }
  138. },
  139. data() {
  140. return {
  141. visible: false,
  142. current: null,
  143. pageList: new Map(),
  144. defaultColumns: [
  145. {
  146. action: 'action',
  147. slot: 'action',
  148. align: 'center',
  149. label: '选择'
  150. },
  151. {
  152. columnKey: 'index',
  153. label: '序号',
  154. type: 'index',
  155. width: 55,
  156. align: 'center',
  157. showOverflowTooltip: true,
  158. fixed: 'left'
  159. },
  160. {
  161. prop: 'code',
  162. label: '供应商编码',
  163. align: 'center',
  164. showOverflowTooltip: true,
  165. minWidth: 140
  166. },
  167. {
  168. prop: 'name',
  169. label: '供应商名称',
  170. align: 'center',
  171. slot: 'name',
  172. showOverflowTooltip: true,
  173. minWidth: 200
  174. },
  175. {
  176. prop: 'version',
  177. label: '版本号',
  178. align: 'center',
  179. width: 80,
  180. formatter: (_row, _column, cellValue) => `V${cellValue || 1}.0`
  181. },
  182. {
  183. prop: 'serialNo',
  184. label: '供应商代号',
  185. align: 'center',
  186. showOverflowTooltip: true,
  187. minWidth: 140
  188. },
  189. {
  190. prop: 'phone',
  191. label: '供应商电话',
  192. align: 'center',
  193. showOverflowTooltip: true,
  194. minWidth: 120
  195. },
  196. {
  197. prop: 'addressName',
  198. label: '注册地址',
  199. align: 'center',
  200. showOverflowTooltip: true,
  201. minWidth: 120,
  202. formatter: (_row, _column, cellValue) => {
  203. return (
  204. (_row.addressName ? _row.addressName.replaceAll(',', '') : '') +
  205. ( _row.address || '')
  206. );
  207. }
  208. },
  209. {
  210. prop: 'otherAddressName',
  211. label: '联系地址',
  212. align: 'center',
  213. showOverflowTooltip: true,
  214. minWidth: 120,
  215. formatter: (_row, _column, cellValue) => {
  216. return (
  217. (_row.otherAddressName
  218. ? _row.otherAddressName.replaceAll(',', '')
  219. : '') + (_row.otherAddress || '')
  220. );
  221. }
  222. },
  223. {
  224. prop: 'linkName',
  225. label: '联系人',
  226. align: 'center',
  227. showOverflowTooltip: true,
  228. minWidth: 120
  229. },
  230. {
  231. prop: 'linkPhone',
  232. label: '联系人电话',
  233. align: 'center',
  234. showOverflowTooltip: true,
  235. minWidth: 120
  236. },
  237. {
  238. prop: 'status',
  239. label: '状态',
  240. align: 'center',
  241. showOverflowTooltip: true,
  242. minWidth: 100,
  243. formatter: (_row, _column, cellValue) => {
  244. return _row.status === 1 ? '启用' : '禁用';
  245. }
  246. }
  247. ],
  248. radio: null
  249. };
  250. },
  251. watch: {},
  252. methods: {
  253. open(item) {
  254. if (item) {
  255. this.radio = item.id;
  256. const list = [...this.pageList.values()].flat();
  257. this.current = list?.find(row => row.id === item.id) || null;
  258. }
  259. this.visible = true;
  260. },
  261. /* 表格数据源 */
  262. datasource({ page, limit, where, order }) {
  263. console.log("dataSourceApi",this.dataSourceApi);
  264. if (this.dataSourceApi) {
  265. // 自定义数据源:由调用方决定请求字段(对账池客户列表等)
  266. const mapped = this.paramMapper
  267. ? this.paramMapper(where || {})
  268. : where;
  269. return this.dataSourceApi({
  270. pageNum: page,
  271. size: limit,
  272. ...this.queryParams,
  273. ...(mapped || {})
  274. }).then((res) => this.normalizeList(res, page));
  275. }
  276. return contactPageUsages({
  277. pageNum: page,
  278. size: limit,
  279. type: this.classType,
  280. ...where,
  281. status: '1'
  282. }).then(res => {
  283. console.log(res);
  284. if(!this.pageList.has(page)) {
  285. this.pageList.set(page, res?.list || []);
  286. }
  287. return res;
  288. });
  289. },
  290. /**
  291. * 字段归一化 + 分页字段兜底。
  292. * 各接口的往来单位字段名不一样(对账池返回 contactId/contactCode/contactName,
  293. * 客户档案返回 id/code/name),这里补齐成 id/code/name,
  294. * 单选回显、open(item) 预选、父组件拿到的对象都不用改。
  295. * count/total 两个分页字段名后端都用过,统一成 ele-pro-table 认的 count。
  296. */
  297. normalizeRow(row) {
  298. return {
  299. ...row,
  300. id: row.id != null ? row.id : row.contactId,
  301. code: row.code != null ? row.code : row.contactCode,
  302. name: row.name != null ? row.name : row.contactName
  303. };
  304. },
  305. normalizeList(res, page) {
  306. // 对账池两个列表接口的响应字段后端文档都没定义全,
  307. // 这里把原始响应打出来,方便核对表格列到底该绑哪些字段名
  308. console.log('[对账池] 列表原始响应:', res);
  309. const list = ((res && res.list) || []).map((row) =>
  310. this.normalizeRow(row)
  311. );
  312. if (!this.pageList.has(page)) {
  313. this.pageList.set(page, list);
  314. }
  315. const count =
  316. res && res.count != null
  317. ? res.count
  318. : res && res.total != null
  319. ? res.total
  320. : list.length;
  321. return { ...res, list, count };
  322. },
  323. /* 刷新表格 */
  324. reload(where) {
  325. this.$refs.table.reload({ page: 1, where });
  326. },
  327. handleNodeClick(data, node) {
  328. this.curNodeData = data;
  329. this.reload({ categoryId: data.id });
  330. },
  331. // 单击获取id
  332. cellClick(row) {
  333. this.current = row;
  334. this.radio = row.id;
  335. },
  336. handleClose() {
  337. this.visible = false;
  338. this.current = null;
  339. this.radio = '';
  340. },
  341. selected() {
  342. if (!this.current) {
  343. return this.$message.warning('请选择上级客户');
  344. }
  345. this.$emit('changeParent', this.current);
  346. this.handleClose();
  347. }
  348. }
  349. };
  350. </script>
  351. <style lang="scss" scoped>
  352. .tree_col {
  353. border: 1px solid #eee;
  354. padding: 10px 0;
  355. box-sizing: border-box;
  356. height: 500px;
  357. overflow: auto;
  358. }
  359. .table_col {
  360. padding-left: 10px;
  361. ::v-deep .el-table th.el-table__cell {
  362. background: #f2f2f2;
  363. }
  364. }
  365. .pagination {
  366. text-align: right;
  367. padding: 10px 0;
  368. }
  369. .btns {
  370. text-align: center;
  371. padding: 10px 0;
  372. }
  373. .topsearch {
  374. margin-bottom: 15px;
  375. }
  376. </style>