index.vue 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622
  1. <template>
  2. <div class="user">
  3. <div class="content">
  4. <byTable :source="sourceList.data" :pagination="sourceList.pagination" :config="config" :loading="loading" highlight-current-row
  5. :selectConfig="selectConfig" :action-list="[
  6. ]" @get-list="getList">
  7. <template #prodTag="{ item }">
  8. <div style="width: 100%">
  9. <!-- <el-icon :size="16" style="cursor:pointer;margin-right: 5px;position:relative;top:5px" color="#409EFF" @click="handleEditTag(item)">
  10. <Edit />
  11. </el-icon> -->
  12. <!-- closable @close="prodTagClose(index, item)" -->
  13. <el-tag style="margin-right: 8px" type="success" v-for="(tag, index) in item.prodTags" :key="index">
  14. {{ dictKeyValue(tag, contractTag) }}
  15. </el-tag>
  16. </div>
  17. </template>
  18. <template #list="{ item }">
  19. <div style="width:100%">
  20. <span v-for="(product ,index) in item.produceOrderDetailList" style="margin-right:15px">
  21. <el-popover placement="top-start" :width="300" trigger="hover">
  22. <div>
  23. <div>产品编码:{{product.productCode}}</div>
  24. <div>产品名称:{{product.productName}}</div>
  25. <div>产品尺寸:{{product.productLength}}cm*{{product.productWidth}}cm*{{product.productHeight}}cm</div>
  26. </div>
  27. <template #reference>
  28. <el-progress type="circle" :percentage="(Number(product.finishQuantity) / Number(product.quantity))*100" width="60"
  29. :status="(Number(product.finishQuantity) / Number(product.quantity))*100 == 100 ? 'success' : ''" />
  30. </template>
  31. </el-popover>
  32. </span>
  33. </div>
  34. </template>
  35. </byTable>
  36. </div>
  37. <div class="schedule-right">
  38. <div class="schedule-top">
  39. <el-row>
  40. <!-- <el-col :span="10" style="text-align: left">
  41. <el-select v-model="status" style="width: 110px">
  42. <el-option v-for="item in tableStatus" :key="item.value" :label="item.label" :value="item.value" />
  43. </el-select>
  44. <el-button type="info" style="margin-left: 8px" @click="clickToday()" plain>今日</el-button>
  45. </el-col> -->
  46. <el-col :span="24" style="text-align: center; height: 32px; line-height: 32px">
  47. <div style="display: flex; justify-content: space-between">
  48. <el-button @click="clickToday()" plain>今日</el-button>
  49. <el-button :icon="ArrowLeftBold" @click="prevMonth()" />
  50. <span style="font-weight: 700">{{ month }}</span>
  51. <el-button :icon="ArrowRightBold" @click="nextMonth()" />
  52. </div>
  53. </el-col>
  54. <!-- <el-col :span="10" style="text-align: right">
  55. <el-button type="primary" @click="newSchedule()">新建日程</el-button>
  56. </el-col> -->
  57. </el-row>
  58. </div>
  59. <div class="schedule-bottom">
  60. <el-calendar v-model="today" ref="calendar">
  61. <template #date-cell="{ data }">
  62. <div style="text-align:center">
  63. <div>
  64. {{ data.day.substr(8, 10) }}
  65. </div>
  66. <el-popover placement="left" :width="400" trigger="hover" @show="onShow(data.day)">
  67. <template #reference>
  68. <div v-if="isShow(data.day)">
  69. <div style="height:5px;margin-bottom:5px;border-radius:2px" v-for="(item,index) in judgeDay(data.day)" :key="index"
  70. :style="{ background: colorData[item]}">
  71. </div>
  72. </div>
  73. </template>
  74. <div>
  75. <div v-for="item in showData" :key="item" style="margin-bottom:20px">
  76. <div style="display:flex">
  77. <div>颜色:</div>
  78. <div :style="{ background: colorData[item]}" style="width:20px;height:20px;border-radius:10px"></div>
  79. </div>
  80. <div>
  81. 订单号:<span v-if="rightDataObj[item]">{{rightDataObj[item]['code']}}</span>
  82. </div>
  83. <div>
  84. 产品:<span v-if="rightDataObj[item]">{{rightDataObj[item]['productName']}}</span>
  85. </div>
  86. </div>
  87. </div>
  88. </el-popover>
  89. </div>
  90. </template>
  91. </el-calendar>
  92. </div>
  93. </div>
  94. <el-dialog :title="modalType == 'add' ? '添加店铺' : '编辑店铺'" v-model="dialogVisible" width="500px" destroy-on-close>
  95. <byForm :formConfig="formConfig" :formOption="formOption" v-model="formData.data" :rules="rules" ref="formDom" v-loading="submitLoading">
  96. </byForm>
  97. <template #footer>
  98. <el-button @click="dialogVisible = false" size="defualt">取 消</el-button>
  99. <el-button type="primary" @click="submitForm()" size="defualt" :loading="submitLoading">
  100. 确 定
  101. </el-button>
  102. </template>
  103. </el-dialog>
  104. <el-dialog :title="'投产'" v-model="productionDialog" width="500px" destroy-on-close>
  105. <byForm :formConfig="productionFormConfig" :formOption="formOption" v-model="formData.data" :rules="productionRules" ref="productionFormDom"
  106. v-loading="formLoading">
  107. </byForm>
  108. <template #footer>
  109. <el-button @click="productionDialog =false" size="default" v-debounce>取 消</el-button>
  110. <el-button @click="submitProduction" type="primary" size="default" v-debounce>提 交</el-button>
  111. </template>
  112. </el-dialog>
  113. </div>
  114. </template>
  115. <script setup>
  116. import byTable from "@/components/byTable/index";
  117. import byForm from "@/components/byForm/index";
  118. import moment from "moment";
  119. import * as date from "@/utils/date.js";
  120. import { ArrowLeftBold, ArrowRightBold } from "@element-plus/icons-vue";
  121. const { proxy } = getCurrentInstance();
  122. const contractTag = computed(
  123. () => proxy.useUserStore().allDict["contract_prod_tag"]
  124. );
  125. const loading = ref(false);
  126. const submitLoading = ref(false);
  127. const sourceList = ref({
  128. data: [],
  129. pagination: {
  130. total: 3,
  131. pageNum: 1,
  132. pageSize: 10,
  133. keyword: "",
  134. produceStatus: "",
  135. staDeliveryPeriod: "",
  136. endDeliveryPeriod: "",
  137. beginTime: "",
  138. endTime: "",
  139. },
  140. });
  141. const treeData = ref([]);
  142. const dialogVisible = ref(false);
  143. const modalType = ref("add");
  144. const status = ref("0");
  145. const today = ref(moment().format("yyyy-MM-DD"));
  146. const dateList = ref({});
  147. const month = ref(moment().format("yyyy年MM月"));
  148. const monthOne = ref(moment().format("yyyy-MM"));
  149. const calendar = ref(null);
  150. const statusData = ref([
  151. {
  152. label: "未开始",
  153. value: "0",
  154. },
  155. {
  156. label: "进行中",
  157. value: "1",
  158. },
  159. {
  160. label: "已完成",
  161. value: "2",
  162. },
  163. ]);
  164. const selectConfig = computed(() => [
  165. {
  166. label: "生产状态",
  167. prop: "produceStatus",
  168. data: statusData.value,
  169. },
  170. {
  171. type: "time",
  172. label: "交期",
  173. placeholder: "开始日期",
  174. prop: "staDeliveryPeriod",
  175. placeholderOne: "结束日期",
  176. propOne: "endDeliveryPeriod",
  177. },
  178. {
  179. type: "time",
  180. label: "下单日期",
  181. placeholder: "开始日期",
  182. prop: "beginTime",
  183. placeholderOne: "结束日期",
  184. propOne: "endTime",
  185. },
  186. ]);
  187. const config = computed(() => {
  188. return [
  189. {
  190. attrs: {
  191. label: "订单号",
  192. prop: "code",
  193. width: 150,
  194. },
  195. },
  196. {
  197. attrs: {
  198. label: "下单时间",
  199. prop: "createTime",
  200. width: 160,
  201. },
  202. },
  203. {
  204. attrs: {
  205. label: "交期",
  206. prop: "deliveryPeriod",
  207. width: 160,
  208. },
  209. },
  210. {
  211. attrs: {
  212. label: "投产时间",
  213. prop: "produceTime",
  214. width: 160,
  215. },
  216. },
  217. {
  218. attrs: {
  219. label: "生产状态",
  220. prop: "produceStatus",
  221. width: 120,
  222. },
  223. render(val) {
  224. return proxy.dictValueLabel(val, statusData.value);
  225. },
  226. },
  227. {
  228. attrs: {
  229. label: "生产指示",
  230. slot: "prodTag",
  231. "min-width": 220,
  232. },
  233. },
  234. {
  235. attrs: {
  236. slot: "list",
  237. label: "产品生产进度",
  238. // prop: "name",
  239. "min-width": 300,
  240. },
  241. },
  242. {
  243. attrs: {
  244. label: "操作",
  245. width: "100",
  246. align: "center",
  247. fixed: "right",
  248. },
  249. renderHTML(row) {
  250. return [
  251. row.produceTime
  252. ? {}
  253. : {
  254. attrs: {
  255. label: "投产",
  256. type: "primary",
  257. text: true,
  258. },
  259. el: "button",
  260. click() {
  261. // proxy
  262. // .msgConfirm()
  263. // .then((res) => {
  264. // proxy
  265. // .post("/produceOrder/putProduction", {
  266. // id: row.id,
  267. // })
  268. // .then((res) => {
  269. // proxy.msgTip("操作成功", 1);
  270. // getList();
  271. // });
  272. // })
  273. // .catch((err) => {});
  274. clickDistributeProduction(row);
  275. },
  276. },
  277. ];
  278. },
  279. },
  280. ];
  281. });
  282. const formData = reactive({
  283. data: {},
  284. });
  285. const formOption = reactive({
  286. inline: true,
  287. labelWidth: 100,
  288. itemWidth: 100,
  289. });
  290. const formDom = ref(null);
  291. const formConfig = computed(() => {
  292. return [
  293. {
  294. type: "input",
  295. prop: "code",
  296. label: "店铺编号",
  297. itemWidth: 100,
  298. disabled: false,
  299. },
  300. {
  301. type: "input",
  302. prop: "name",
  303. label: "店铺名称",
  304. itemWidth: 100,
  305. disabled: false,
  306. },
  307. {
  308. type: "treeSelect",
  309. prop: "deptId",
  310. label: "负责部门",
  311. data: treeData.value,
  312. propsTreeLabel: "deptName",
  313. propsTreeValue: "deptId",
  314. itemWidth: 100,
  315. disabled: false,
  316. },
  317. ];
  318. });
  319. const rules = ref({
  320. deptId: [{ required: true, message: "请选择负责部门", trigger: "change" }],
  321. name: [{ required: true, message: "请输入店铺名称", trigger: "blur" }],
  322. code: [{ required: true, message: "请输入店铺编号", trigger: "blur" }],
  323. });
  324. const getList = async (req) => {
  325. sourceList.value.pagination = { ...sourceList.value.pagination, ...req };
  326. loading.value = true;
  327. proxy.post("/produceOrder/page", sourceList.value.pagination).then((res) => {
  328. res.rows.forEach((x) => {
  329. if (x.prodTag) {
  330. x.prodTags = x.prodTag.split(",");
  331. } else {
  332. x.prodTags = [];
  333. }
  334. });
  335. sourceList.value.data = res.rows;
  336. sourceList.value.pagination.total = res.total;
  337. setTimeout(() => {
  338. loading.value = false;
  339. }, 200);
  340. });
  341. };
  342. const openModal = () => {
  343. dialogVisible.value = true;
  344. modalType.value = "add";
  345. formData.data = {
  346. definition: "2",
  347. fileList: [],
  348. };
  349. if (currencyData.value && currencyData.value.length > 0) {
  350. formData.data.currency = currencyData.value[0].dictKey;
  351. formData.data.costCurrency = currencyData.value[0].dictKey;
  352. }
  353. };
  354. const submitForm = () => {
  355. formDom.value.handleSubmit((valid) => {
  356. submitLoading.value = true;
  357. proxy.post("/shopInfo/" + modalType.value, formData.data).then(
  358. (res) => {
  359. proxy.msgTip("操作成功", 1);
  360. dialogVisible.value = false;
  361. submitLoading.value = false;
  362. getList();
  363. },
  364. (err) => {
  365. submitLoading.value = false;
  366. }
  367. );
  368. });
  369. };
  370. const getDtl = (row) => {
  371. modalType.value = "edit";
  372. proxy.post("/shopInfo/detail", { id: row.id }).then((res) => {
  373. formData.data = res;
  374. dialogVisible.value = true;
  375. });
  376. };
  377. getList();
  378. const productionFormDom = ref(null);
  379. const productionDialog = ref(false);
  380. const formLoading = ref(false);
  381. const productionFormConfig = computed(() => [
  382. {
  383. type: "date",
  384. itemType: "datetime",
  385. label: "投产时间",
  386. prop: "produceTime",
  387. // placeholder: "合同开始时间",
  388. itemWidth: 100,
  389. clearable: true,
  390. },
  391. ]);
  392. const productionRules = ref({
  393. produceTime: [
  394. { required: true, message: "请选择投产时间", trigger: "change" },
  395. ],
  396. });
  397. const clickDistributeProduction = (row) => {
  398. formData.data = {
  399. id: row.id,
  400. produceTime: "",
  401. };
  402. productionDialog.value = true;
  403. };
  404. const submitProduction = () => {
  405. productionFormDom.value.handleSubmit(() => {
  406. formLoading.value = true;
  407. proxy.post("/produceOrder/putProduction", formData.data).then((res) => {
  408. proxy.msgTip("操作成功");
  409. formLoading.value = false;
  410. productionDialog.value = false;
  411. getList();
  412. });
  413. });
  414. };
  415. const rightData = ref([]);
  416. const rightDataObj = ref({});
  417. const colorData = ref({});
  418. let colorList = [
  419. "#BBFF00",
  420. "#FFFF00",
  421. "#FFBB00",
  422. "#FF3333",
  423. "#D28EFF",
  424. "#CCEEFF",
  425. "#FFC8B4",
  426. "#CCDDFF",
  427. "#007799",
  428. "#550088",
  429. "#AAFFEE",
  430. "#FFB3FF",
  431. "#FFFF33",
  432. ];
  433. const getRightData = () => {
  434. proxy
  435. .post("/produceOrder/schedulingList", { beginDate: monthOne.value })
  436. .then(
  437. (res) => {
  438. rightDataObj.value = {};
  439. rightData.value = res.map((x, index) => ({
  440. ...x,
  441. produceTimeOne: x.produceTime,
  442. produceTime: x.produceTime.substr(0, 10),
  443. deliveryPeriod: x.deliveryPeriod.substr(0, 10),
  444. }));
  445. for (let i = 0; i < res.length; i++) {
  446. const ele = res[i];
  447. rightDataObj.value[ele.id] = ele;
  448. if (i <= 19) {
  449. colorData.value[ele.id] = colorList[i];
  450. } else {
  451. colorData.value[ele.id] = colorList[19 - i] || colorList[0];
  452. }
  453. }
  454. },
  455. (err) => {
  456. loading.value = false;
  457. }
  458. );
  459. };
  460. getRightData();
  461. const getBackGround = (id) => {
  462. return { background: colorData.value[id] };
  463. };
  464. const prodTagClose = (index, row) => {
  465. row.prodTags.splice(index, 1);
  466. proxy
  467. .post("/contract/updateProductionTag", {
  468. id: row.contractId,
  469. prodTag: row.prodTags.join(","),
  470. })
  471. .then((res) => {
  472. getList();
  473. });
  474. };
  475. const clickToday = () => {
  476. today.value = moment().format("yyyy-MM-DD");
  477. month.value = moment().format("yyyy年MM月");
  478. monthOne.value = moment().format("yyyy-MM");
  479. getRightData();
  480. };
  481. const isShow = (day) => {
  482. let nowDay = new Date(day + " 23:59:59").getTime();
  483. let flag = false;
  484. for (let i = 0; i < rightData.value.length; i++) {
  485. const e = rightData.value[i];
  486. let startDay = new Date(e.produceTime).getTime();
  487. let endDay = new Date(e.deliveryPeriod + " 23:59:59").getTime();
  488. if (nowDay >= startDay && nowDay <= endDay) {
  489. flag = true;
  490. break;
  491. }
  492. }
  493. return flag;
  494. };
  495. const judgeDay = (day) => {
  496. // return dateList.value[day] && dateList.value[day].length > 0;
  497. let nowDay = new Date(day + " 23:59:59").getTime();
  498. let rows = [];
  499. for (let i = 0; i < rightData.value.length; i++) {
  500. const e = rightData.value[i];
  501. let startDay = new Date(e.produceTime).getTime();
  502. let endDay = new Date(e.deliveryPeriod + " 23:59:59").getTime();
  503. if (nowDay >= startDay && nowDay <= endDay) {
  504. rows.push(e.id);
  505. }
  506. }
  507. return rows;
  508. };
  509. const showData = ref([]);
  510. const onShow = (day) => {
  511. let rows = judgeDay(day);
  512. showData.value = rows;
  513. };
  514. const getData = (id, att) => {
  515. const current = rightData.value.find((x) => x.id == id);
  516. if (current && current[att]) {
  517. return current[att];
  518. }
  519. };
  520. const selectDate = (val) => {
  521. calendar.value.selectDate(val);
  522. };
  523. const prevMonth = () => {
  524. month.value = moment(
  525. moment(month.value, "yyyy年MM月").add(-1, "month").calendar()
  526. ).format("yyyy年MM月");
  527. monthOne.value = moment(moment(month.value, "yyyy年MM月")).format("yyyy-MM");
  528. selectDate("prev-month");
  529. getRightData();
  530. };
  531. const nextMonth = () => {
  532. month.value = moment(
  533. moment(month.value, "yyyy年MM月").add(+1, "month").calendar()
  534. ).format("yyyy年MM月");
  535. monthOne.value = moment(moment(month.value, "yyyy年MM月")).format("yyyy-MM");
  536. selectDate("next-month");
  537. getRightData();
  538. };
  539. </script>
  540. <style lang="scss" scoped>
  541. ::v-deep(.el-progress__text) {
  542. font-size: 14px !important;
  543. }
  544. .user {
  545. padding: 10px;
  546. display: flex;
  547. justify-content: space-between;
  548. .schedule-right {
  549. width: 400px;
  550. .schedule-top {
  551. width: 100%;
  552. background: #fff;
  553. padding: 20px;
  554. }
  555. .schedule-bottom {
  556. width: 100%;
  557. background: #fff;
  558. height: calc(100vh - 100px - 20px - 83px);
  559. padding: 20px;
  560. margin-top: 10px;
  561. overflow-y: auto;
  562. &::-webkit-scrollbar {
  563. width: 0px;
  564. }
  565. .line-class {
  566. height: 18px;
  567. line-height: 18px;
  568. overflow: hidden;
  569. white-space: nowrap;
  570. text-overflow: ellipsis;
  571. -o-text-overflow: ellipsis;
  572. margin: 1px 0;
  573. }
  574. }
  575. }
  576. .content {
  577. width: calc(100% - 410px);
  578. }
  579. }
  580. ::v-deep(.el-calendar__header) {
  581. display: none;
  582. }
  583. ::v-deep(.el-calendar__body) {
  584. padding: 0;
  585. }
  586. ::v-deep(.el-calendar-table .el-calendar-day) {
  587. // padding: 0;
  588. min-height: 50px;
  589. height: calc((100vh - 100px - 20px - 83px - 95px) / 5);
  590. overflow-y: hidden;
  591. }
  592. </style>