accessories.vue 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  1. <template>
  2. <block v-if="!isBom">
  3. <view class="search-box">
  4. <up-search placeholder="搜索关键词" v-model="keyword" height="35" @blur="onSearch" :clearabled="false"
  5. :showAction="false" />
  6. <view v-if="content.where.condition" class="clear" @click.stop="onSearch('')">
  7. <up-icon name="close-circle-fill" size="20" />
  8. </view>
  9. </view>
  10. <view style="height: 20rpx;" />
  11. </block>
  12. <My_listbox v-if="!isBom" ref="listBox" :empty="!list.length" :pullDown="!isBom" @getlist="getList">
  13. <showList :result="resultIds" :list='list' @onClick="onSelect" />
  14. <view style="height: 200rpx;" />
  15. </My_listbox>
  16. <view v-else-if="bomList.length" class="bom">
  17. <view class="left">
  18. <view class="class1" :class="active.class1 == index ? 'class1active' : ''" @click="changeClass1(index)"
  19. v-for="(item, index) in bomList" :key="item.plm_bomid" hover-class="navigator-hover">
  20. {{ item.bomname }}
  21. </view>
  22. </view>
  23. <view class="right">
  24. <My_listbox ref="listBox" :pullDown="false">
  25. <up-collapse :value="collapse1" @change="changeCollapse1($event)">
  26. <up-collapse-item :ref="el => {
  27. if (el) {
  28. collapseRefs['collapse' + item.plm_bomid] = el
  29. }
  30. }" :cellCustomStyle="{
  31. backgroundColor: '#fff',
  32. }" v-for="item in bomList[active.class1].subdep" :key="item.plm_bomid" :title="item.bomname"
  33. :name="item.plm_bomid">
  34. <up-collapse @change="changeCollapse($event, item.plm_bomid)" v-if="item.subdep.length"
  35. :name="item.plm_bomid">
  36. <up-collapse-item :cellCustomStyle="{
  37. backgroundColor: '#fff',
  38. }" :ref="el => {
  39. if (el) {
  40. collapseRefs['collapse' + item.plm_bomid] = el
  41. }
  42. }" v-for="item1 in item.subdep" :key="item1.plm_bomid" :title="item1.bomname"
  43. :name="item1.plm_bomid">
  44. <showList v-if="item1.items.length" size="small" :result="resultIds" :list='item1.items'
  45. @onClick="onSelect" />
  46. </up-collapse-item>
  47. </up-collapse>
  48. <showList v-if="item.items.length" size="small" :result="resultIds" :list='item.items'
  49. @onClick="onSelect" />
  50. </up-collapse-item>
  51. </up-collapse>
  52. <view style="height: 200rpx;" />
  53. </My_listbox>
  54. </view>
  55. </view>
  56. <My_listbox v-else ref="listBox" :empty="true" @getlist="getBomList" />
  57. <view class="footer">
  58. <My-button :text="`确定添加(${resultIds.length})`" @onClick="onAdd" />
  59. </view>
  60. </template>
  61. <script setup>
  62. import { ref, reactive, getCurrentInstance, nextTick } from 'vue';
  63. import { onLoad } from '@dcloudio/uni-app';
  64. import showList from "./accessoriesList.vue"
  65. const { $Http } = getCurrentInstance().proxy;
  66. const listBox = ref(null);
  67. const isBom = ref(false);
  68. let result = reactive([]);
  69. const resultIds = ref([]);
  70. const content = reactive({
  71. loading: false,
  72. "pageNumber": 1,
  73. "pageSize": 20,
  74. "where": {
  75. "condition": ""
  76. }
  77. })
  78. function onAdd() {
  79. $Http.selectAcc(result)
  80. }
  81. function onSelect(e) {
  82. if (result.some(item => item.itemid == e.itemid)) {
  83. result = result.filter(item => item.itemid != e.itemid);
  84. } else {
  85. result.push(e);
  86. }
  87. resultIds.value = result.map(item => item.itemid);
  88. }
  89. const list = ref([]);
  90. onLoad((options) => {
  91. console.log("options", options)
  92. console.log("$Http", $Http)
  93. result = result.concat(JSON.parse(options.list || '[]'));
  94. resultIds.value = result.map(item => item.itemid);
  95. console.log("初始选中", result, resultIds.value)
  96. let content1 = $Http.content1;
  97. content.sys_enterpriseid = content1.sys_enterpriseid;
  98. content.sa_workorderid = content1.sa_workorderid;
  99. if (content1.sku) $Http.basic({
  100. "id": 2025080610424703,
  101. "content": content1
  102. }).then(res => {
  103. console.log("查询产品是否存在BOM", res)
  104. if (res.data == 0) {
  105. // 不存在BOM
  106. getList(true);
  107. } else {
  108. // 存在BOM
  109. isBom.value = true;
  110. getBomList();
  111. }
  112. })
  113. })
  114. const bomList = ref([]);
  115. let active = reactive({
  116. class1: 0
  117. });
  118. function changeClass1(index) {
  119. active.class1 = index;
  120. }
  121. // 折叠面板
  122. const collapseRefs = ref({})
  123. const collapse1 = ref([])
  124. function changeCollapse1(e) {
  125. collapse1.value = e.filter(v => v.status == 'open').map(v => v.name)
  126. }
  127. function changeCollapse(e, id) {
  128. nextTick(() => {
  129. collapseRefs.value['collapse' + id].init()
  130. });
  131. setTimeout(() => {
  132. nextTick(() => {
  133. collapseRefs.value['collapse' + id].init()
  134. });
  135. }, 330);
  136. }
  137. // 有bom
  138. function getBomList() {
  139. $Http.basic({
  140. "id": "2025080610425503",
  141. content: {
  142. "sa_aftersalesbomid": content.sa_workorderid,
  143. "sys_enterpriseid": content.sys_enterpriseid,
  144. }
  145. }).then(res => {
  146. console.log("获取bom配件列表", res)
  147. listBox.value.refreshToComplete();
  148. listBox.value.setHeight();
  149. if (res.code == 1) {
  150. bomList.value = processBomData(res.data)
  151. console.log("bomList", bomList.value);
  152. } else {
  153. if (res.msg) uni.showToast({
  154. title: res.msg,
  155. icon: 'none'
  156. });
  157. }
  158. })
  159. }
  160. function processBomData(originalData) {
  161. // 1. 提取所有一级分类节点
  162. const topLevelNodes = [];
  163. // 遍历原始数据
  164. originalData.forEach(dataSet => {
  165. dataSet.bom?.forEach(rootNode => {
  166. // 提取根节点下的一级分类
  167. if (rootNode.subdep && rootNode.subdep.length > 0) {
  168. topLevelNodes.push(...rootNode.subdep);
  169. }
  170. });
  171. });
  172. // 2. 递归处理节点
  173. const processNode = (node) => {
  174. node.items = node.items || [];
  175. node.subdep = node.subdep || [];
  176. // 如果当前节点没有items和subdep,直接返回null
  177. if (node.items.length === 0 && node.subdep.length === 0) return null;
  178. try {
  179. if (node.items.length) node.items = node.items.map(item => {
  180. item.imageUrl = item.attinfos.length ? $Http.getSpecifiedImage(item.attinfos[0]) : ''
  181. return item;
  182. });
  183. } catch (error) {
  184. }
  185. // 创建新节点副本
  186. const newNode = { ...node };
  187. // 处理子节点
  188. if (node.subdep.length) newNode.subdep = node.subdep.map(subNode => processNode(subNode)).filter(subNode => subNode !== null);
  189. return newNode;
  190. };
  191. // 3. 处理所有一级分类节点
  192. return topLevelNodes.map(node => processNode(node)).filter(node => node && (node.subdep.length || node.items.length));
  193. }
  194. // 无BOM
  195. const keyword = ref('');
  196. function onSearch(e) {
  197. if (content.where.condition == e) return;
  198. content.where.condition = e;
  199. keyword.value = e;
  200. getList(true);
  201. }
  202. function getList(init = false) {
  203. if (isBom.value) return;
  204. if (content.loading) return;
  205. if (init) content.pageNumber = 1;
  206. content.loading = true;
  207. $Http.basic({
  208. "id": "2025080610425103",
  209. content
  210. }).then(res => {
  211. console.log("获取配件列表", res)
  212. content.loading = false;
  213. listBox.value.refreshToComplete();
  214. listBox.value.setHeight();
  215. res.data = res.data.map(item => {
  216. item.imageUrl = item.attinfos.length ? $Http.getSpecifiedImage(item.attinfos[0]) : ''
  217. return item;
  218. });
  219. if (res.code == 1) {
  220. list.value = reactive(res.firstPage ? res.data : list.value.concat(res.data));
  221. content.pageTotal = res.pageTotal;
  222. content.pageNumber = res.pageNumber;
  223. } else {
  224. if (res.msg) uni.showToast({
  225. title: res.msg,
  226. icon: 'none'
  227. });
  228. }
  229. })
  230. }
  231. </script>
  232. <style lang="scss" scoped>
  233. .bom {
  234. width: 100vw;
  235. display: flex;
  236. min-height: 100vh;
  237. .left {
  238. width: 250rpx;
  239. background: #fff;
  240. flex-shrink: 0;
  241. .class1 {
  242. padding: 30rpx 30rpx;
  243. width: 250rpx;
  244. box-sizing: border-box;
  245. background: #FFFFFF;
  246. border-radius: 0rpx 8rpx 0rpx 0rpx;
  247. font-family: PingFang SC, PingFang SC;
  248. font-size: 28rpx;
  249. color: #999999;
  250. }
  251. .class1active {
  252. position: relative;
  253. background: #F7F7FF;
  254. color: #3774F6;
  255. }
  256. .class1active::after {
  257. content: '';
  258. position: absolute;
  259. left: 0;
  260. top: 0;
  261. width: 8rpx;
  262. height: 100%;
  263. background: #3774F6;
  264. }
  265. }
  266. .right {
  267. flex: 1;
  268. }
  269. }
  270. .search-box {
  271. position: relative;
  272. padding: 20rpx;
  273. background: #fff;
  274. .clear {
  275. position: absolute;
  276. display: flex;
  277. align-items: center;
  278. right: 0;
  279. top: 50%;
  280. transform: translateY(-50%);
  281. width: 80rpx;
  282. padding-left: 10rpx;
  283. height: 70rpx;
  284. z-index: 2;
  285. }
  286. }
  287. .footer {
  288. position: fixed;
  289. bottom: 0;
  290. left: 0;
  291. width: 100%;
  292. height: 120rpx;
  293. padding: 10rpx 20rpx;
  294. background: #fff;
  295. box-sizing: border-box;
  296. }
  297. </style>