taskForm.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573
  1. <template>
  2. <view class="">
  3. <view class="sticky-subsection" :style="{ top: `${stickyTop}px` }">
  4. <view class="subsection-wrap">
  5. <u-subsection
  6. fontSize="25"
  7. mode="subsection"
  8. :list="tabList"
  9. :current="curNow"
  10. @change="sectionChange"
  11. activeColor="#157A2C"
  12. ></u-subsection>
  13. </view>
  14. </view>
  15. <view
  16. class="sticky-subsection-placeholder"
  17. :style="{ height: `${stickyPlaceholderHeight}px` }"
  18. ></view>
  19. <!-- Tab 0: 基本信息 -->
  20. <view v-show="curNow === 0" class="basic_info">
  21. <view class="info_header">
  22. <view class="info_title">基本信息</view>
  23. <u-gap height="4" bgColor="#1890FF"></u-gap>
  24. </view>
  25. <view class="info_content">
  26. <u-row>
  27. <u-col span="4">
  28. <view class="label">类型:</view>
  29. </u-col>
  30. <u-col span="8">
  31. <view class="value">{{ typeLabel || "—" }}</view>
  32. </u-col>
  33. </u-row>
  34. <u-row>
  35. <u-col span="4">
  36. <view class="label">紧急程度:</view>
  37. </u-col>
  38. <u-col span="8">
  39. <view class="value">{{ priorityLabel || "—" }}</view>
  40. </u-col>
  41. </u-row>
  42. <u-row>
  43. <u-col span="4">
  44. <view class="label">名称:</view>
  45. </u-col>
  46. <u-col span="8">
  47. <view class="value">{{ form.name || "—" }}</view>
  48. </u-col>
  49. </u-row>
  50. <u-row>
  51. <u-col span="4">
  52. <view class="label">请托部门:</view>
  53. </u-col>
  54. <u-col span="8">
  55. <view class="value">{{ form.pleaseEntrustDeptName || "—" }}</view>
  56. </u-col>
  57. </u-row>
  58. <u-row>
  59. <u-col span="4">
  60. <view class="label">请托人:</view>
  61. </u-col>
  62. <u-col span="8">
  63. <view class="value">{{ form.pleaseEntrustUserName || "—" }}</view>
  64. </u-col>
  65. </u-row>
  66. <u-row>
  67. <u-col span="4">
  68. <view class="label">所属工厂:</view>
  69. </u-col>
  70. <u-col span="8">
  71. <view class="value">{{ form.factoriesName || "—" }}</view>
  72. </u-col>
  73. </u-row>
  74. <u-row>
  75. <u-col span="4">
  76. <view class="label">受托工厂:</view>
  77. </u-col>
  78. <u-col span="8">
  79. <view class="value">{{ form.beEntrustedFactoriesName || "—" }}</view>
  80. </u-col>
  81. </u-row>
  82. <u-row>
  83. <u-col span="4">
  84. <view class="label">受托部门:</view>
  85. </u-col>
  86. <u-col span="8">
  87. <view class="value">{{ form.beEntrustedDeptName || "—" }}</view>
  88. </u-col>
  89. </u-row>
  90. </view>
  91. </view>
  92. <!-- Tab 1: 产品信息 -->
  93. <view v-show="curNow === 1" class="basic_info">
  94. <view class="info_header">
  95. <view class="info_title">产品信息</view>
  96. <u-gap height="4" bgColor="#1890FF"></u-gap>
  97. </view>
  98. <view v-if="!orderList.length" class="empty_text">暂无产品信息</view>
  99. <view v-for="(row, idx) in orderList" :key="idx" class="card_box">
  100. <view class="card_title">产品 {{ idx + 1 }}</view>
  101. <u-row>
  102. <u-col span="4">
  103. <view class="label">产品名称:</view>
  104. </u-col>
  105. <u-col span="8">
  106. <view class="value">{{ row.categoryName || "—" }}</view>
  107. </u-col>
  108. </u-row>
  109. <u-row>
  110. <u-col span="4">
  111. <view class="label">计划编号:</view>
  112. </u-col>
  113. <u-col span="8">
  114. <view class="value">{{ row.productionPlanCode || "—" }}</view>
  115. </u-col>
  116. </u-row>
  117. <u-row>
  118. <u-col span="4">
  119. <view class="label">牌号:</view>
  120. </u-col>
  121. <u-col span="8">
  122. <view class="value">{{ row.brandNum || "—" }}</view>
  123. </u-col>
  124. </u-row>
  125. <u-row>
  126. <u-col span="4">
  127. <view class="label">批次号:</view>
  128. </u-col>
  129. <u-col span="8">
  130. <view class="value">{{ row.batchNo || "—" }}</view>
  131. </u-col>
  132. </u-row>
  133. <u-row>
  134. <u-col span="4">
  135. <view class="label">需求描述:</view>
  136. </u-col>
  137. <u-col span="8">
  138. <view class="value">{{ row.describes || "—" }}</view>
  139. </u-col>
  140. </u-row>
  141. <u-row>
  142. <u-col span="4">
  143. <view class="label">请托数量:</view>
  144. </u-col>
  145. <u-col span="8">
  146. <view class="value">{{ row.totalCount || "—" }}{{ row.measuringUnit || "" }}</view>
  147. </u-col>
  148. </u-row>
  149. <u-row>
  150. <u-col span="4">
  151. <view class="label">计划类型:</view>
  152. </u-col>
  153. <u-col span="8">
  154. <view class="value">{{ getPlanTypeLabel(row.planType) || "—" }}</view>
  155. </u-col>
  156. </u-row>
  157. <u-row>
  158. <u-col span="4">
  159. <view class="label">生产工单号:</view>
  160. </u-col>
  161. <u-col span="8">
  162. <view class="value">{{ row.code || "—" }}</view>
  163. </u-col>
  164. </u-row>
  165. <u-row>
  166. <u-col span="4">
  167. <view class="label">规格:</view>
  168. </u-col>
  169. <u-col span="8">
  170. <view class="value">{{ row.specification || "—" }}</view>
  171. </u-col>
  172. </u-row>
  173. <u-row>
  174. <u-col span="4">
  175. <view class="label">名称:</view>
  176. </u-col>
  177. <u-col span="8">
  178. <view class="value">{{ row.name || "—" }}</view>
  179. </u-col>
  180. </u-row>
  181. <u-row>
  182. <u-col span="4">
  183. <view class="label">型号:</view>
  184. </u-col>
  185. <u-col span="8">
  186. <view class="value">{{ row.modelType || "—" }}</view>
  187. </u-col>
  188. </u-row>
  189. <u-row>
  190. <u-col span="4">
  191. <view class="label">工艺路线:</view>
  192. </u-col>
  193. <u-col span="8">
  194. <view class="value">{{ row.produceRoutingName || "—" }}</view>
  195. </u-col>
  196. </u-row>
  197. <u-row>
  198. <u-col span="4">
  199. <view class="label">BOM分类:</view>
  200. </u-col>
  201. <u-col span="8">
  202. <view class="value">{{ getBomTypeLabel(row.bomType) || "—" }}</view>
  203. </u-col>
  204. </u-row>
  205. <u-row>
  206. <u-col span="4">
  207. <view class="label">BOM版本:</view>
  208. </u-col>
  209. <u-col span="8">
  210. <view class="value">{{ getBomVersionLabel(row) || "—" }}</view>
  211. </u-col>
  212. </u-row>
  213. <u-row>
  214. <u-col span="4">
  215. <view class="label">工序:</view>
  216. </u-col>
  217. <u-col span="8">
  218. <view class="value">{{ row.taskName || "—" }}</view>
  219. </u-col>
  220. </u-row>
  221. <u-row>
  222. <u-col span="4">
  223. <view class="label">编码:</view>
  224. </u-col>
  225. <u-col span="8">
  226. <view class="value">{{ row.categoryCode || "—" }}</view>
  227. </u-col>
  228. </u-row>
  229. <u-row>
  230. <u-col span="4">
  231. <view class="label">完成日期:</view>
  232. </u-col>
  233. <u-col span="8">
  234. <view class="value">{{ row.planDeliveryTime || "—" }}</view>
  235. </u-col>
  236. </u-row>
  237. <u-row>
  238. <u-col span="4">
  239. <view class="label">计划开始时间:</view>
  240. </u-col>
  241. <u-col span="8">
  242. <view class="value">{{ row.planStartTime || "—" }}</view>
  243. </u-col>
  244. </u-row>
  245. <u-row>
  246. <u-col span="4">
  247. <view class="label">计划结束时间:</view>
  248. </u-col>
  249. <u-col span="8">
  250. <view class="value">{{ row.planCompleteTime || "—" }}</view>
  251. </u-col>
  252. </u-row>
  253. <u-row>
  254. <u-col span="4">
  255. <view class="label">工单开始时间:</view>
  256. </u-col>
  257. <u-col span="8">
  258. <view class="value">{{ row.startTime || "—" }}</view>
  259. </u-col>
  260. </u-row>
  261. <u-row>
  262. <u-col span="4">
  263. <view class="label">工单结束时间:</view>
  264. </u-col>
  265. <u-col span="8">
  266. <view class="value">{{ row.completeTime || "—" }}</view>
  267. </u-col>
  268. </u-row>
  269. </view>
  270. </view>
  271. </view>
  272. </template>
  273. <script>
  274. import { pleaseEntrustDetail, getProduceTaskList } from "@/api/entrust/index.js";
  275. const PLAN_TYPE_MAP = {
  276. 1: "内销计划",
  277. 2: "外销计划",
  278. 3: "预制计划",
  279. };
  280. const BOM_TYPE_MAP = {
  281. 1: "产品(PBOM)",
  282. 2: "加工(MBOM)",
  283. 3: "装配(ABOM)",
  284. };
  285. export default {
  286. props: {
  287. businessId: {
  288. default: "",
  289. },
  290. taskDefinitionKey: {
  291. default: "",
  292. },
  293. },
  294. data() {
  295. return {
  296. form: {
  297. name: "",
  298. type: "",
  299. priority: "",
  300. pleaseEntrustDeptName: "",
  301. pleaseEntrustUserName: "",
  302. factoriesName: "",
  303. beEntrustedFactoriesName: "",
  304. beEntrustedDeptName: "",
  305. },
  306. orderList: [],
  307. tabList: ["基本信息", "产品信息"],
  308. curNow: 0,
  309. stickyPlaceholderHeight: 0,
  310. stickyTop: 44,
  311. options: [
  312. { name: "一般", value: 1 },
  313. { name: "紧急", value: 2 },
  314. ],
  315. };
  316. },
  317. computed: {
  318. typeLabel() {
  319. return this.form.type ? this.form.type + "" : "";
  320. },
  321. priorityLabel() {
  322. const item = this.options.find((i) => i.value == this.form.priority);
  323. return item ? item.name : "";
  324. },
  325. },
  326. async mounted() {
  327. this.initStickyTop();
  328. await this.init();
  329. this.updateStickyPlaceholderHeight();
  330. },
  331. methods: {
  332. initStickyTop() {
  333. try {
  334. const systemInfo = uni.getSystemInfoSync();
  335. const statusBarHeight = Number(systemInfo.statusBarHeight || 0);
  336. this.stickyTop = statusBarHeight + 44;
  337. } catch (error) {
  338. this.stickyTop = 50;
  339. }
  340. },
  341. updateStickyPlaceholderHeight() {
  342. this.$nextTick(() => {
  343. const query = uni.createSelectorQuery().in(this);
  344. query
  345. .select(".sticky-subsection")
  346. .boundingClientRect((rect) => {
  347. if (rect && rect.height) {
  348. this.stickyPlaceholderHeight = rect.height;
  349. }
  350. })
  351. .exec();
  352. });
  353. },
  354. sectionChange(index) {
  355. this.curNow = index;
  356. },
  357. async init() {
  358. if (!this.businessId) return;
  359. try {
  360. const data = await pleaseEntrustDetail(this.businessId);
  361. const {
  362. applyDeptName,
  363. applyDeptId,
  364. createUserName,
  365. createUserId,
  366. applyFactoriesId,
  367. applyFactoriesName,
  368. beEntrustedDeptId,
  369. beEntrustedDeptName,
  370. beEntrustedFactoriesName,
  371. beEntrustedFactoriesId,
  372. name,
  373. type,
  374. priority,
  375. id: dataId,
  376. } = data;
  377. Object.assign(this.form, {
  378. pleaseEntrustDeptName: applyDeptName,
  379. pleaseEntrustDeptId: applyDeptId,
  380. pleaseEntrustUserName: createUserName,
  381. pleaseEntrustUserId: createUserId,
  382. factoriesId: applyFactoriesId,
  383. factoriesName: applyFactoriesName,
  384. beEntrustedDeptId,
  385. beEntrustedDeptName,
  386. beEntrustedFactoriesName,
  387. beEntrustedFactoriesId,
  388. name,
  389. type,
  390. priority,
  391. id: dataId,
  392. });
  393. const newData = {
  394. ...this.deepCopy(data),
  395. taskList: [],
  396. };
  397. this.orderList.push(newData);
  398. this.getOrderTaskList();
  399. this.form.type = this.form.type + "";
  400. } catch (e) {
  401. console.warn("获取请托详情失败", e);
  402. }
  403. },
  404. deepCopy(obj, hash = new WeakMap()) {
  405. if (obj === null) return null;
  406. if (obj instanceof Date) return new Date(obj);
  407. if (obj instanceof RegExp) return new RegExp(obj);
  408. if (typeof obj !== "object" && typeof obj !== "function") return obj;
  409. if (hash.has(obj)) return hash.get(obj);
  410. const result = Array.isArray(obj) ? [] : {};
  411. hash.set(obj, result);
  412. return Object.keys(obj).reduce((acc, key) => {
  413. acc[key] = this.deepCopy(obj[key], hash);
  414. return acc;
  415. }, result);
  416. },
  417. async getOrderTaskList() {
  418. for (const item of this.orderList) {
  419. try {
  420. const res = await getProduceTaskList({
  421. isDetail: true,
  422. pageNum: 1,
  423. routingId: item.produceRoutingId,
  424. size: -1,
  425. });
  426. item.taskList = res.list || [];
  427. if (item.taskId && item.taskList.length) {
  428. const matched = item.taskList.find((t) => t.id === item.taskId);
  429. if (matched) {
  430. this.$set(item, "taskName", matched.name);
  431. }
  432. }
  433. } catch (e) {
  434. console.warn("获取工序列表失败", e);
  435. }
  436. }
  437. },
  438. getPlanTypeLabel(val) {
  439. return PLAN_TYPE_MAP[val] || "";
  440. },
  441. getBomTypeLabel(val) {
  442. return BOM_TYPE_MAP[val] || "";
  443. },
  444. getBomVersionLabel(row) {
  445. if (row.bomCategoryName) {
  446. return `${row.bomCategoryName} (V${row.bomCategoryVersions}.0)`;
  447. }
  448. return "";
  449. },
  450. getTableValue() {
  451. return {
  452. form: this.form,
  453. orderList: this.orderList,
  454. };
  455. },
  456. },
  457. };
  458. </script>
  459. <style lang="scss">
  460. .sticky-subsection {
  461. position: fixed;
  462. left: 0;
  463. right: 0;
  464. z-index: 99;
  465. width: 100%;
  466. box-sizing: border-box;
  467. background: #f5f7fa;
  468. }
  469. .sticky-subsection-placeholder {
  470. width: 100%;
  471. }
  472. .subsection-wrap {
  473. background: #f5f7fa;
  474. padding: 0 16rpx 12rpx;
  475. width: 100%;
  476. box-sizing: border-box;
  477. }
  478. .basic_info {
  479. box-sizing: border-box;
  480. padding: 0 20rpx;
  481. }
  482. .info_header {
  483. margin-top: 10rpx;
  484. }
  485. .info_title {
  486. position: relative;
  487. padding: 16rpx 50rpx;
  488. font-size: 36rpx;
  489. color: #606266;
  490. }
  491. .info_title::after {
  492. content: "";
  493. position: absolute;
  494. top: 16rpx;
  495. left: 0px;
  496. width: 16rpx;
  497. height: 50rpx;
  498. background: #1890ff;
  499. }
  500. .info_content {
  501. .label {
  502. padding: 20rpx 0;
  503. text-align: right;
  504. font-size: 28rpx;
  505. font-weight: 700;
  506. color: #6e6e6e;
  507. }
  508. .value {
  509. padding: 20rpx 0;
  510. font-size: 28rpx;
  511. color: #606266;
  512. }
  513. }
  514. .card_box {
  515. box-sizing: border-box;
  516. padding: 20rpx;
  517. margin: 20rpx;
  518. width: calc(100% - 40rpx);
  519. border-radius: 20rpx;
  520. box-shadow: 0 0 12rpx -6rpx #000;
  521. .card_title {
  522. font-size: 32rpx;
  523. font-weight: 700;
  524. color: #666;
  525. margin-bottom: 10rpx;
  526. }
  527. .label {
  528. padding: 18rpx 0;
  529. text-align: right;
  530. font-size: 28rpx;
  531. font-weight: 700;
  532. color: #6e6e6e;
  533. }
  534. .value {
  535. padding: 18rpx 0;
  536. font-size: 28rpx;
  537. color: #606266;
  538. }
  539. }
  540. .empty_text {
  541. text-align: center;
  542. color: #909399;
  543. font-size: 28rpx;
  544. padding: 80rpx 0;
  545. }
  546. </style>