accessories.vue 10 KB

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