| | |
| | | url: "/personalShift/export", |
| | | method: "get", |
| | | params: query, |
| | | responseType: "blob", |
| | | }); |
| | | } |
| | | |
| | |
| | | // å表æ¥è¯¢ |
| | | export function findProductProcessRouteItemList(query) { |
| | | return request({ |
| | | url: `/processRouteItemInstance/list/${query.orderId}`, |
| | | method: "get", |
| | | params: query, |
| | | }); |
| | | } |
| | | // å表æ¥è¯¢-ç产订å |
| | | export function findProcessRouteItemInstanceList(query) { |
| | | return request({ |
| | | url: `/processRouteItemInstance/list/${query.orderId}`, |
| | | url: `/productionOrderRouteItem/list/${query.orderId}`, |
| | | method: "get", |
| | | }); |
| | | } |
| | | |
| | | |
| | | |
| | | export function addOrUpdateProductProcessRouteItem(data) { |
| | | return request({ |
| | | url: "/processRouteItemInstance/update", |
| | | url: "/productionOrderRouteItem/update", |
| | | method: "put", |
| | | data: data, |
| | | }); |
| | |
| | | // ç产订åä¸ï¼æ°å¢å·¥èºè·¯çº¿é¡¹ç® |
| | | export function addRouteItem(data) { |
| | | return request({ |
| | | url: "/processRouteItemInstance/add", |
| | | url: "/productionOrderRouteItem/add", |
| | | method: "post", |
| | | data, |
| | | }); |
| | |
| | | // å é¤å·¥èºè·¯çº¿é¡¹ç®ï¼è·¯ç±åæ¼æ¥ idï¼ |
| | | export function deleteRouteItem(id) { |
| | | return request({ |
| | | url: `/processRouteItemInstance/delete/${id}`, |
| | | url: `/productionOrderRouteItem/delete/${id}`, |
| | | method: "delete", |
| | | }); |
| | | } |
| | |
| | | // ç产订åä¸ï¼æåºå·¥èºè·¯çº¿é¡¹ç® |
| | | export function sortRouteItem(data) { |
| | | return request({ |
| | | url: "/productProcessRoute/sortRouteItem", |
| | | url: "/productionOrderRouteItem/sort", |
| | | method: "post", |
| | | data, |
| | | }); |
| | |
| | | // è·åå·¥åºåæ°å表-ç产订å |
| | | export function findProcessParamListOrder(query) { |
| | | return request({ |
| | | url: `/processRouteItemParamInstance/list`, |
| | | url: `/productionOrderRouteItemParam/list`, |
| | | method: "get", |
| | | params: query, |
| | | }); |
| | |
| | | // å·¥èºè·¯çº¿åæ°æ°å¢-ç产订å |
| | | export function addProcessRouteItemParamOrder(data) { |
| | | return request({ |
| | | url: "/processRouteItemParamInstance/add", |
| | | url: "/productionOrderRouteItemParam/add", |
| | | method: "post", |
| | | data: data, |
| | | }); |
| | |
| | | // å·¥èºè·¯çº¿åæ°ä¿®æ¹-ç产订å |
| | | export function editProcessRouteItemParamOrder(data) { |
| | | return request({ |
| | | url: "/processRouteItemParamInstance/update", |
| | | url: "/productionOrderRouteItemParam/update", |
| | | method: "put", |
| | | data: data, |
| | | }); |
| | |
| | | // å·¥èºè·¯çº¿åæ°å é¤-ç产订å |
| | | export function delProcessRouteItemParamOrder(id) { |
| | | return request({ |
| | | url: `/processRouteItemParamInstance/delete/${id}`, |
| | | url: `/productionOrderRouteItemParam/delete/${id}`, |
| | | method: "delete", |
| | | }); |
| | | } |
| | |
| | | |
| | | export function add(data) { |
| | | return request({ |
| | | url: "/productStructure", |
| | | url: "/productStructure/"+data.bomId, |
| | | method: "post", |
| | | data: data, |
| | | data: data.children, |
| | | }); |
| | | } |
| | | // å页æ¥è¯¢-产å订å |
| | | export function queryList2(id) { |
| | | return request({ |
| | | url: "/productionOrderStructure/getBomStructs/" + id, |
| | | method: "get", |
| | | }); |
| | | } |
| | | |
| | | export function add2(data) { |
| | | return request({ |
| | | url: "/productionOrderStructure/addOrUpdateBomStructs/"+data.orderId, |
| | | method: "put", |
| | | data: data.children, |
| | | }); |
| | | } |
| | |
| | | data: data, |
| | | }); |
| | | } |
| | | |
| | | //ç产订åå é¤ |
| | | export function delProductOrder(ids) { |
| | | return request({ |
| | | url: `/productOrder/${ids}`, |
| | | method: "delete", |
| | | }); |
| | | } |
| | | |
| | | //ç产订åéå |
| | | export function revokeProductOrder(data) { |
| | | return request({ |
| | | url: "/productOrder/revoke", |
| | | method: "post", |
| | | data: data, |
| | | }); |
| | | } |
| | | // ç产订å-æ¥è¯¢äº§åç»æå表 |
| | | export function listProcessBom(query) { |
| | | return request({ |
| | |
| | | method: "post", |
| | | data: data, |
| | | }); |
| | | } |
| | | |
| | | export function getProductOrderSource(id) { |
| | | return request({ |
| | | url: `/productOrder/productOrderSource/${id}`, |
| | | method: "get", |
| | | }); |
| | | } |
| | |
| | | if (props.pageType === "order") { |
| | | addProcessRouteItemParamOrder({ |
| | | orderId: Number(props.orderId), |
| | | processId: props.process.id, |
| | | routeItemId: Number(props.routeId), |
| | | // processId: props.process.id, |
| | | routeItemId: props.process.id, |
| | | // routeItemId: Number(props.routeId), |
| | | paramId: selectedParam.value.id, |
| | | standardValue: isNumericMode |
| | | ? selectedParam.value.standardValue || "" |
| | |
| | | <template>
|
| | | <div class="navbar">
|
| | | <div>
|
| | | <hamburger id="hamburger-container" :is-active="appStore.sidebar.opened" class="hamburger-container"
|
| | | @toggleClick="toggleSideBar" />
|
| | | <breadcrumb v-if="!settingsStore.topNav" id="breadcrumb-container" class="breadcrumb-container" />
|
| | | <hamburger id="hamburger-container"
|
| | | :is-active="appStore.sidebar.opened"
|
| | | class="hamburger-container"
|
| | | @toggleClick="toggleSideBar" />
|
| | | <breadcrumb v-if="!settingsStore.topNav"
|
| | | id="breadcrumb-container"
|
| | | class="breadcrumb-container" />
|
| | | </div>
|
| | | <!-- <top-nav v-if="settingsStore.topNav" id="topmenu-container" class="topmenu-container" />-->
|
| | | <div class="right-menu">
|
| | | <!-- æ¶æ¯éç¥ -->
|
| | | <el-popover
|
| | | v-model:visible="notificationVisible"
|
| | | :width="500"
|
| | | placement="bottom-end"
|
| | | trigger="click"
|
| | | :popper-options="{ modifiers: [{ name: 'offset', options: { offset: [0, 10] } }] }"
|
| | | popper-class="notification-popover"
|
| | | >
|
| | | <el-popover v-model:visible="notificationVisible"
|
| | | :width="500"
|
| | | placement="bottom-end"
|
| | | trigger="click"
|
| | | :popper-options="{ modifiers: [{ name: 'offset', options: { offset: [0, 10] } }] }"
|
| | | popper-class="notification-popover">
|
| | | <template #reference>
|
| | | <div class="notification-container right-menu-item hover-effect">
|
| | | <el-badge :value="unreadCount" :hidden="unreadCount === 0" class="notification-badge">
|
| | | <el-icon :size="20" style="cursor: pointer;">
|
| | | <el-badge :value="unreadCount"
|
| | | :hidden="unreadCount === 0"
|
| | | class="notification-badge">
|
| | | <el-icon :size="20"
|
| | | style="cursor: pointer;">
|
| | | <Bell />
|
| | | </el-icon>
|
| | | </el-badge>
|
| | | </div>
|
| | | </template>
|
| | | <NotificationCenter
|
| | | @unreadCountChange="handleUnreadCountChange"
|
| | | ref="notificationCenterRef"
|
| | | />
|
| | | <NotificationCenter @unreadCountChange="handleUnreadCountChange"
|
| | | ref="notificationCenterRef" />
|
| | | </el-popover>
|
| | | <div class="avatar-container">
|
| | | <el-dropdown @command="handleCommand" class="right-menu-item hover-effect" trigger="click">
|
| | | <el-dropdown @command="handleCommand"
|
| | | class="right-menu-item hover-effect"
|
| | | trigger="click">
|
| | | <div class="avatar-wrapper">
|
| | | <img :src="userStore.avatar" class="user-avatar" />
|
| | | <img :src="userStore.avatar"
|
| | | class="user-avatar" />
|
| | | <el-icon><caret-bottom /></el-icon>
|
| | | </div>
|
| | | <template #dropdown>
|
| | |
| | | <router-link to="/user/profile">
|
| | | <el-dropdown-item>个人ä¸å¿</el-dropdown-item>
|
| | | </router-link>
|
| | | <el-dropdown-item command="setLayout" v-if="settingsStore.showSettings">
|
| | | <el-dropdown-item command="setLayout"
|
| | | v-if="settingsStore.showSettings">
|
| | | <span>å¸å±è®¾ç½®</span>
|
| | | </el-dropdown-item>
|
| | | <el-dropdown-item divided command="logout">
|
| | | <el-dropdown-item divided
|
| | | command="logout">
|
| | | <span>éåºç»å½</span>
|
| | | </el-dropdown-item>
|
| | | </el-dropdown-menu>
|
| | |
| | | </template>
|
| | |
|
| | | <script setup>
|
| | | import { ElMessageBox } from 'element-plus'
|
| | | import { Bell } from '@element-plus/icons-vue'
|
| | | import Breadcrumb from '@/components/Breadcrumb'
|
| | | import TopNav from '@/components/TopNav'
|
| | | import Hamburger from '@/components/Hamburger'
|
| | | import Screenfull from '@/components/Screenfull'
|
| | | import SizeSelect from '@/components/SizeSelect'
|
| | | import HeaderSearch from '@/components/HeaderSearch'
|
| | | import RuoYiGit from '@/components/RuoYi/Git'
|
| | | import RuoYiDoc from '@/components/RuoYi/Doc'
|
| | | import NotificationCenter from './NotificationCenter/index.vue'
|
| | | import useAppStore from '@/store/modules/app'
|
| | | import useUserStore from '@/store/modules/user'
|
| | | import useSettingsStore from '@/store/modules/settings'
|
| | | import { ElMessageBox } from "element-plus";
|
| | | import { Bell } from "@element-plus/icons-vue";
|
| | | import Breadcrumb from "@/components/Breadcrumb";
|
| | | import TopNav from "@/components/TopNav";
|
| | | import Hamburger from "@/components/Hamburger";
|
| | | import Screenfull from "@/components/Screenfull";
|
| | | import SizeSelect from "@/components/SizeSelect";
|
| | | import HeaderSearch from "@/components/HeaderSearch";
|
| | | import RuoYiGit from "@/components/RuoYi/Git";
|
| | | import RuoYiDoc from "@/components/RuoYi/Doc";
|
| | | import NotificationCenter from "./NotificationCenter/index.vue";
|
| | | import useAppStore from "@/store/modules/app";
|
| | | import useUserStore from "@/store/modules/user";
|
| | | import useSettingsStore from "@/store/modules/settings";
|
| | |
|
| | | const appStore = useAppStore()
|
| | | const userStore = useUserStore()
|
| | | const settingsStore = useSettingsStore()
|
| | | const notificationVisible = ref(false)
|
| | | const notificationCenterRef = ref(null)
|
| | | const unreadCount = ref(0)
|
| | | function toggleSideBar() {
|
| | | appStore.toggleSideBar()
|
| | | }
|
| | | // const redirect = ref(undefined)
|
| | | // watch(route, (newRoute) => {
|
| | | // redirect.value = newRoute.query && newRoute.query.redirect
|
| | | // }, { immediate: true })
|
| | |
|
| | | function handleCommand(command) {
|
| | | switch (command) {
|
| | | case "setLayout":
|
| | | setLayout()
|
| | | break
|
| | | case "logout":
|
| | | logout()
|
| | | break
|
| | | default:
|
| | | break
|
| | | const appStore = useAppStore();
|
| | | const userStore = useUserStore();
|
| | | const settingsStore = useSettingsStore();
|
| | | const notificationVisible = ref(false);
|
| | | const notificationCenterRef = ref(null);
|
| | | const unreadCount = ref(0);
|
| | | function toggleSideBar() {
|
| | | appStore.toggleSideBar();
|
| | | }
|
| | | }
|
| | | // const redirect = ref(undefined)
|
| | | // watch(route, (newRoute) => {
|
| | | // redirect.value = newRoute.query && newRoute.query.redirect
|
| | | // }, { immediate: true })
|
| | |
|
| | | function logout() {
|
| | | ElMessageBox.confirm('ç¡®å®æ³¨éå¹¶éåºç³»ç»åï¼', 'æç¤º', {
|
| | | confirmButtonText: 'ç¡®å®',
|
| | | cancelButtonText: 'åæ¶',
|
| | | type: 'warning'
|
| | | }).then(() => {
|
| | | userStore.logOut().then(() => {
|
| | | location.href = '/index'
|
| | | function handleCommand(command) {
|
| | | switch (command) {
|
| | | case "setLayout":
|
| | | setLayout();
|
| | | break;
|
| | | case "logout":
|
| | | logout();
|
| | | break;
|
| | | default:
|
| | | break;
|
| | | }
|
| | | }
|
| | |
|
| | | function logout() {
|
| | | ElMessageBox.confirm("ç¡®å®æ³¨éå¹¶éåºç³»ç»åï¼", "æç¤º", {
|
| | | confirmButtonText: "ç¡®å®",
|
| | | cancelButtonText: "åæ¶",
|
| | | type: "warning",
|
| | | })
|
| | | }).catch(() => { })
|
| | | }
|
| | | .then(() => {
|
| | | userStore.logOut().then(() => {
|
| | | location.href = "/index";
|
| | | });
|
| | | })
|
| | | .catch(() => {});
|
| | | }
|
| | |
|
| | | const emits = defineEmits(['setLayout'])
|
| | | function setLayout() {
|
| | | emits('setLayout')
|
| | | }
|
| | | const emits = defineEmits(["setLayout"]);
|
| | | function setLayout() {
|
| | | emits("setLayout");
|
| | | }
|
| | |
|
| | | function toggleTheme() {
|
| | | settingsStore.toggleTheme()
|
| | | }
|
| | | function toggleTheme() {
|
| | | settingsStore.toggleTheme();
|
| | | }
|
| | |
|
| | | // æ¶æ¯éç¥ç¸å
³
|
| | | function handleUnreadCountChange(count) {
|
| | | unreadCount.value = count
|
| | | }
|
| | | // æ¶æ¯éç¥ç¸å
³
|
| | | function handleUnreadCountChange(count) {
|
| | | unreadCount.value = count;
|
| | | }
|
| | |
|
| | | // ç»ä»¶æè½½æ¶å è½½æªè¯»æ°éå宿¶å·æ°
|
| | | let unreadCountTimer = null
|
| | | onMounted(() => {
|
| | | // å»¶è¿å è½½ï¼ç¡®ä¿ç»ä»¶å·²æ¸²æ
|
| | | nextTick(() => {
|
| | | if (notificationCenterRef.value) {
|
| | | notificationCenterRef.value.loadUnreadCount()
|
| | | }
|
| | | })
|
| | | // 宿¶å·æ°æªè¯»æ°éï¼æ¯30ç§ï¼
|
| | | unreadCountTimer = setInterval(() => {
|
| | | if (notificationCenterRef.value) {
|
| | | notificationCenterRef.value.loadUnreadCount()
|
| | | }
|
| | | }, 30000)
|
| | | })
|
| | |
|
| | | // çå¬ popover æ¾ç¤ºç¶æï¼æå¼æ¶å è½½æ¶æ¯å表
|
| | | watch(notificationVisible, (val) => {
|
| | | if (val && notificationCenterRef.value) {
|
| | | // ç»ä»¶æè½½æ¶å è½½æªè¯»æ°éå宿¶å·æ°
|
| | | let unreadCountTimer = null;
|
| | | onMounted(() => {
|
| | | // å»¶è¿å è½½ï¼ç¡®ä¿ç»ä»¶å·²æ¸²æ
|
| | | nextTick(() => {
|
| | | notificationCenterRef.value.loadMessages()
|
| | | })
|
| | | }
|
| | | })
|
| | | if (notificationCenterRef.value) {
|
| | | notificationCenterRef.value.loadUnreadCount();
|
| | | }
|
| | | });
|
| | | // 宿¶å·æ°æªè¯»æ°éï¼æ¯30ç§ï¼
|
| | | // unreadCountTimer = setInterval(() => {
|
| | | // if (notificationCenterRef.value) {
|
| | | // notificationCenterRef.value.loadUnreadCount()
|
| | | // }
|
| | | // }, 30000)
|
| | | });
|
| | |
|
| | | onUnmounted(() => {
|
| | | if (unreadCountTimer) {
|
| | | clearInterval(unreadCountTimer)
|
| | | }
|
| | | })
|
| | | // çå¬ popover æ¾ç¤ºç¶æï¼æå¼æ¶å è½½æ¶æ¯å表
|
| | | watch(notificationVisible, val => {
|
| | | if (val && notificationCenterRef.value) {
|
| | | nextTick(() => {
|
| | | notificationCenterRef.value.loadMessages();
|
| | | });
|
| | | }
|
| | | });
|
| | |
|
| | | onUnmounted(() => {
|
| | | if (unreadCountTimer) {
|
| | | clearInterval(unreadCountTimer);
|
| | | }
|
| | | });
|
| | | </script>
|
| | |
|
| | | <style lang='scss' scoped>
|
| | | .navbar {
|
| | | height: 50px;
|
| | | overflow: hidden;
|
| | | position: relative;
|
| | | background: var(--navbar-bg);
|
| | | box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
|
| | | .navbar {
|
| | | height: 50px;
|
| | | overflow: hidden;
|
| | | position: relative;
|
| | | background: var(--navbar-bg);
|
| | | box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
|
| | |
|
| | | .hamburger-container {
|
| | | line-height: 46px;
|
| | | height: 100%;
|
| | | float: left;
|
| | | cursor: pointer;
|
| | | transition: background 0.3s;
|
| | | -webkit-tap-highlight-color: transparent;
|
| | |
|
| | | &:hover {
|
| | | background: rgba(0, 0, 0, 0.025);
|
| | | }
|
| | | }
|
| | |
|
| | | .breadcrumb-container {
|
| | | float: left;
|
| | | }
|
| | |
|
| | | .topmenu-container {
|
| | | position: absolute;
|
| | | left: 50px;
|
| | | }
|
| | |
|
| | | .errLog-container {
|
| | | display: inline-block;
|
| | | vertical-align: top;
|
| | | }
|
| | |
|
| | | .right-menu {
|
| | | float: right;
|
| | | height: 100%;
|
| | | line-height: 50px;
|
| | | display: flex;
|
| | |
|
| | | &:focus {
|
| | | outline: none;
|
| | | }
|
| | |
|
| | | .right-menu-item {
|
| | | display: inline-block;
|
| | | padding: 0 8px;
|
| | | .hamburger-container {
|
| | | line-height: 46px;
|
| | | height: 100%;
|
| | | font-size: 18px;
|
| | | color: var(--navbar-text);
|
| | | vertical-align: text-bottom;
|
| | | float: left;
|
| | | cursor: pointer;
|
| | | transition: background 0.3s;
|
| | | -webkit-tap-highlight-color: transparent;
|
| | |
|
| | | &.hover-effect {
|
| | | cursor: pointer;
|
| | | transition: background 0.3s;
|
| | | &:hover {
|
| | | background: rgba(0, 0, 0, 0.025);
|
| | | }
|
| | | }
|
| | |
|
| | | &:hover {
|
| | | background: rgba(0, 0, 0, 0.025);
|
| | | .breadcrumb-container {
|
| | | float: left;
|
| | | }
|
| | |
|
| | | .topmenu-container {
|
| | | position: absolute;
|
| | | left: 50px;
|
| | | }
|
| | |
|
| | | .errLog-container {
|
| | | display: inline-block;
|
| | | vertical-align: top;
|
| | | }
|
| | |
|
| | | .right-menu {
|
| | | float: right;
|
| | | height: 100%;
|
| | | line-height: 50px;
|
| | | display: flex;
|
| | |
|
| | | &:focus {
|
| | | outline: none;
|
| | | }
|
| | |
|
| | | .right-menu-item {
|
| | | display: inline-block;
|
| | | padding: 0 8px;
|
| | | height: 100%;
|
| | | font-size: 18px;
|
| | | color: var(--navbar-text);
|
| | | vertical-align: text-bottom;
|
| | |
|
| | | &.hover-effect {
|
| | | cursor: pointer;
|
| | | transition: background 0.3s;
|
| | |
|
| | | &:hover {
|
| | | background: rgba(0, 0, 0, 0.025);
|
| | | }
|
| | | }
|
| | |
|
| | | &.theme-switch-wrapper {
|
| | | display: flex;
|
| | | align-items: center;
|
| | |
|
| | | svg {
|
| | | transition: transform 0.3s;
|
| | |
|
| | | &:hover {
|
| | | transform: scale(1.15);
|
| | | }
|
| | | }
|
| | | }
|
| | | }
|
| | |
|
| | | &.theme-switch-wrapper {
|
| | | .notification-container {
|
| | | margin-right: 20px;
|
| | | display: flex;
|
| | | align-items: center;
|
| | | cursor: pointer;
|
| | |
|
| | | svg {
|
| | | transition: transform 0.3s;
|
| | | .notification-badge {
|
| | | :deep(.el-badge__content) {
|
| | | border: none;
|
| | | }
|
| | | }
|
| | | }
|
| | |
|
| | | &:hover {
|
| | | transform: scale(1.15);
|
| | | .avatar-container {
|
| | | margin-right: 40px;
|
| | |
|
| | | .avatar-wrapper {
|
| | | margin-top: 5px;
|
| | | position: relative;
|
| | |
|
| | | .user-avatar {
|
| | | cursor: pointer;
|
| | | width: 40px;
|
| | | height: 40px;
|
| | | border-radius: 50px;
|
| | | }
|
| | |
|
| | | i {
|
| | | cursor: pointer;
|
| | | position: absolute;
|
| | | right: -20px;
|
| | | top: 14px;
|
| | | font-size: 12px;
|
| | | }
|
| | | }
|
| | | }
|
| | | }
|
| | |
|
| | | .notification-container {
|
| | | margin-right: 20px;
|
| | | display: flex;
|
| | | align-items: center;
|
| | | cursor: pointer;
|
| | |
|
| | | .notification-badge {
|
| | | :deep(.el-badge__content) {
|
| | | border: none;
|
| | | }
|
| | | }
|
| | | }
|
| | |
|
| | | .avatar-container {
|
| | | margin-right: 40px;
|
| | |
|
| | | .avatar-wrapper {
|
| | | margin-top: 5px;
|
| | | position: relative;
|
| | |
|
| | | .user-avatar {
|
| | | cursor: pointer;
|
| | | width: 40px;
|
| | | height: 40px;
|
| | | border-radius: 50px;
|
| | | }
|
| | |
|
| | | i {
|
| | | cursor: pointer;
|
| | | position: absolute;
|
| | | right: -20px;
|
| | | top: 14px;
|
| | | font-size: 12px;
|
| | | }
|
| | | }
|
| | | }
|
| | | }
|
| | | }
|
| | |
|
| | | </style>
|
| | |
|
| | | <style lang="scss">
|
| | | .notification-popover {
|
| | | padding: 0 !important;
|
| | | |
| | | .el-popover__title {
|
| | | display: none;
|
| | | }
|
| | | |
| | | .el-popover__body {
|
| | | .notification-popover {
|
| | | padding: 0 !important;
|
| | |
|
| | | .el-popover__title {
|
| | | display: none;
|
| | | }
|
| | |
|
| | | .el-popover__body {
|
| | | padding: 0 !important;
|
| | | }
|
| | | }
|
| | | }
|
| | | .el-badge__content.is-fixed{
|
| | | top: 12px;
|
| | | }
|
| | | .el-badge__content.is-fixed {
|
| | | top: 12px;
|
| | | }
|
| | | </style>
|
| | |
| | | .user-stats { |
| | | /* display: flex; */ |
| | | /* flex-wrap: wrap; |
| | | gap: 10px; */ |
| | | gap: 10px; */ |
| | | margin-bottom: 4px; |
| | | } |
| | | |
| | |
| | | { |
| | | label: "å·¥èºè·¯çº¿ç¼å·", |
| | | prop: "processRouteCode", |
| | | width: "200px", |
| | | className: "status-cell", |
| | | }, |
| | | { |
| | | label: "ç¶æ", |
| | |
| | | { |
| | | label: "产åç±»å", |
| | | prop: "dictLabel", |
| | | dataType: "tag", |
| | | }, |
| | | { |
| | | label: "BOMç¼å·", |
| | |
| | | model: row.model || "", |
| | | bomNo: row.bomNo || "", |
| | | dictLabel: row.dictLabel || "", |
| | | orderId: row.id || "", |
| | | bomId: row.bomId || null, |
| | | description: row.description || "", |
| | | type: "route", |
| | |
| | | </script> |
| | | |
| | | <style scoped></style> |
| | | <style lang="scss"> |
| | | .status-cell { |
| | | font-weight: 600; |
| | | color: #409eff; |
| | | font-family: "Courier New", monospace; |
| | | text-shadow: 0 1px 2px rgba(64, 158, 255, 0.2); |
| | | } |
| | | </style> |
| | |
| | | <div class="section-BOM"> |
| | | <div class="section-header"> |
| | | <div class="section-title">BOM</div> |
| | | <div class="section-actions" |
| | | v-if="pageType === 'order'"> |
| | | <el-button type="primary" |
| | | @click="toggleBomEdit"> |
| | | {{ bomDataValue.isEdit ? 'åæ¶' : 'ç¼è¾' }} |
| | | </el-button> |
| | | <el-button v-if=" bomDataValue.isEdit" |
| | | type="success" |
| | | @click="saveBomChanges">ä¿å</el-button> |
| | | </div> |
| | | </div> |
| | | <div> |
| | | <!-- BOMè¡¨æ ¼ --> |
| | |
| | | style="width: 100%"> |
| | | <el-table-column type="expand"> |
| | | <template #default="props"> |
| | | <el-table :data="props.row.bomList" |
| | | row-key="id" |
| | | default-expand-all |
| | | :tree-props="{children: 'children', hasChildren: 'hasChildren'}" |
| | | style="width: 100%"> |
| | | <el-table-column prop="productName" |
| | | label="产å" /> |
| | | <el-table-column prop="model" |
| | | label="è§æ ¼" /> |
| | | <el-table-column prop="processName" |
| | | label="æ¶èå·¥åº" /> |
| | | <el-table-column prop="unitQuantity" |
| | | label="åä½äº§åºæéæ°é" /> |
| | | <el-table-column prop="unit" |
| | | label="åä½" /> |
| | | </el-table> |
| | | <el-form ref="bomFormRef" |
| | | :model="bomDataValue"> |
| | | <el-table :data="props.row.bomList" |
| | | row-key="tempId" |
| | | default-expand-all |
| | | :tree-props="{children: 'children', hasChildren: 'hasChildren'}" |
| | | style="width: 100%"> |
| | | <el-table-column prop="productName" |
| | | label="产å" /> |
| | | <el-table-column prop="model" |
| | | label="è§æ ¼"> |
| | | <template #default="{ row }"> |
| | | <el-form-item v-if="bomDataValue.isEdit" |
| | | style="margin: 0"> |
| | | <el-select v-model="row.model" |
| | | placeholder="è¯·éæ©è§æ ¼" |
| | | :disabled="!bomDataValue.isEdit" |
| | | style="width: 100%" |
| | | @visible-change="(v) => { if (v) openBomProductDialog(row.tempId) }"> |
| | | <el-option v-if="row.model" |
| | | :label="row.model" |
| | | :value="row.model" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <span v-else>{{ row.model }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="processName" |
| | | label="æ¶èå·¥åº"> |
| | | <template #default="{ row }"> |
| | | <el-form-item v-if="bomDataValue.isEdit" |
| | | style="margin: 0"> |
| | | <el-select v-model="row.processId" |
| | | placeholder="è¯·éæ©æ¶èå·¥åº" |
| | | :disabled="!bomDataValue.isEdit" |
| | | style="width: 100%"> |
| | | <el-option v-for="process in processOptions" |
| | | :key="process.id" |
| | | :label="process.name" |
| | | :value="process.id" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <span v-else>{{ row.processName }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="unitQuantity" |
| | | label="åä½äº§åºæéæ°é"> |
| | | <template #default="{ row }"> |
| | | <el-form-item v-if="bomDataValue.isEdit" |
| | | style="margin: 0"> |
| | | <el-input-number v-model="row.unitQuantity" |
| | | :min="0" |
| | | :step="1" |
| | | controls-position="right" |
| | | style="width: 100%" |
| | | :disabled="!bomDataValue.isEdit" /> |
| | | </el-form-item> |
| | | <span v-else>{{ row.unitQuantity }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="unit" |
| | | label="åä½"> |
| | | <template #default="{ row }"> |
| | | <el-form-item v-if="bomDataValue.isEdit" |
| | | style="margin: 0"> |
| | | <el-input v-model="row.unit" |
| | | placeholder="请è¾å
¥åä½" |
| | | clearable |
| | | :disabled="!bomDataValue.isEdit" /> |
| | | </el-form-item> |
| | | <span v-else>{{ row.unit }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="æä½" |
| | | fixed="right" |
| | | v-if="pageType === 'order'" |
| | | width="180"> |
| | | <template #default="{ row }"> |
| | | <el-button v-if="bomDataValue.isEdit" |
| | | type="danger" |
| | | text |
| | | size="small" |
| | | @click="removeBomItem(row.tempId)">å é¤</el-button> |
| | | <el-button v-if="bomDataValue.isEdit" |
| | | type="primary" |
| | | text |
| | | size="small" |
| | | @click="addBomItem2(row.tempId)">æ·»å å项</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </el-form> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="BOMç¼å·" |
| | | prop="bomNo" /> |
| | | <el-table-column label="产åç±»å" |
| | | prop="dictLabel" /> |
| | | <!-- <el-table-column label="æä½" |
| | | width="150"> |
| | | <template #default="{ row }"> |
| | | |
| | | </template> |
| | | </el-table-column> --> |
| | | <!-- <el-table-column label="产åç¼ç " |
| | | prop="productCode" /> |
| | | <el-table-column label="产ååç§°" |
| | |
| | | <el-table-column label="è§æ ¼åå·" |
| | | prop="model" /> --> |
| | | </el-table> |
| | | <div v-if="bomDataValue.isEdit" |
| | | style="text-align: center;border: 1px solid #e4e7ed;padding: 10px;transition: all 0.3s ease;cursor: pointer;" |
| | | :class="{'hover-effect': bomDataValue.isEdit}"> |
| | | <el-button type="primary" |
| | | text |
| | | @click="addBomItem"> |
| | | <el-icon style="vertical-align: middle;margin-right: 5px;"> |
| | | <Plus /> |
| | | </el-icon> |
| | | æ·»å |
| | | </el-button> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <!-- æ°å¢/ç¼è¾å¼¹çª --> |
| | |
| | | <ProductSelectDialog v-model="showProductSelectDialog" |
| | | @confirm="handleProductSelect" |
| | | single /> |
| | | <!-- BOM产åéæ©å¯¹è¯æ¡ --> |
| | | <ProductSelectDialog v-model="bomDataValue.showProductDialog" |
| | | @confirm="handleBomProductSelect" |
| | | single /> |
| | | <!-- åæ°åè¡¨å¯¹è¯æ¡ --> |
| | | <ProcessParamListDialog v-model="showParamListDialog" |
| | | :title="`${currentProcess ? getProcessName(currentProcess.processId) : ''} - åæ°å表`" |
| | |
| | | sortRouteItem, |
| | | } from "@/api/productionManagement/productProcessRoute.js"; |
| | | import { processList } from "@/api/productionManagement/productionProcess.js"; |
| | | import { queryList } from "@/api/productionManagement/productStructure.js"; |
| | | import { |
| | | queryList2, |
| | | queryList, |
| | | add2, |
| | | } from "@/api/productionManagement/productStructure.js"; |
| | | import { useRoute } from "vue-router"; |
| | | import { ElMessageBox, ElMessage } from "element-plus"; |
| | | import Sortable from "sortablejs"; |
| | |
| | | const currentProcess = ref(null); |
| | | const paramList = ref([]); |
| | | const bomTableData = ref([]); |
| | | const bomFormRef = ref(null); |
| | | const bomDataValue = ref({ |
| | | dataList: [], |
| | | showProductDialog: false, |
| | | currentRowName: null, |
| | | loading: false, |
| | | isEdit: false, |
| | | }); |
| | | let tableSortable = null; |
| | | let cardSortable = null; |
| | | |
| | |
| | | bomId: route.query.bomId || null, |
| | | description: route.query.description || "", |
| | | }; |
| | | |
| | | // 妿æbomIdï¼è·åBOMæ°æ® |
| | | if (routeInfo.value.bomId) { |
| | | queryList(routeInfo.value.bomId) |
| | | if (pageType.value === "order") { |
| | | queryList2(route.query.orderId) |
| | | .then(res => { |
| | | if (res.data) { |
| | | // 为BOMæ°æ®è®¾ç½®tempId |
| | | const setTempIdRecursively = items => { |
| | | items.forEach(item => { |
| | | item.tempId = item.id || new Date().getTime(); |
| | | if (item.children && item.children.length > 0) { |
| | | setTempIdRecursively(item.children); |
| | | } |
| | | }); |
| | | }; |
| | | setTempIdRecursively(res.data); |
| | | |
| | | bomTableData.value = [ |
| | | { |
| | | bomNo: routeInfo.value.bomNo, |
| | |
| | | bomList: res.data, |
| | | }, |
| | | ]; |
| | | |
| | | // ä¿ååå§BOMæ°æ® |
| | | bomDataValue.value.dataList = res.data; |
| | | } |
| | | }) |
| | | .catch(err => { |
| | | console.error("è·åBOMæ°æ®å¤±è´¥ï¼", err); |
| | | }); |
| | | } else { |
| | | queryList(Number(route.query.bomId)) |
| | | .then(res => { |
| | | if (res.data) { |
| | | // 为BOMæ°æ®è®¾ç½®tempId |
| | | const setTempIdRecursively = items => { |
| | | items.forEach(item => { |
| | | item.tempId = item.id || new Date().getTime(); |
| | | if (item.children && item.children.length > 0) { |
| | | setTempIdRecursively(item.children); |
| | | } |
| | | }); |
| | | }; |
| | | setTempIdRecursively(res.data); |
| | | |
| | | bomTableData.value = [ |
| | | { |
| | | bomNo: routeInfo.value.bomNo, |
| | | dictLabel: routeInfo.value.dictLabel, |
| | | productCode: "", |
| | | productName: routeInfo.value.productName, |
| | | model: routeInfo.value.model, |
| | | bomList: res.data, |
| | | }, |
| | | ]; |
| | | |
| | | // ä¿ååå§BOMæ°æ® |
| | | bomDataValue.value.dataList = res.data; |
| | | } |
| | | }) |
| | | .catch(err => { |
| | | console.error("è·åBOMæ°æ®å¤±è´¥ï¼", err); |
| | | }); |
| | | } |
| | | // è·åBOMæ°æ®ï¼ä½¿ç¨æ°çæ¥å£ |
| | | }; |
| | | |
| | | // æ°å¢ |
| | |
| | | const closeDialog = () => { |
| | | dialogVisible.value = false; |
| | | resetForm(); |
| | | }; |
| | | |
| | | // BOMç¸å
³æ¹æ³ |
| | | // 忢BOMç¼è¾æ¨¡å¼ |
| | | const toggleBomEdit = () => { |
| | | bomDataValue.value.isEdit = !bomDataValue.value.isEdit; |
| | | if (!bomDataValue.value.isEdit) { |
| | | // åæ¶ç¼è¾æ¶éæ°å è½½æ°æ® |
| | | getRouteInfo(); |
| | | } |
| | | }; |
| | | |
| | | // æ·»å BOM项 |
| | | const addBomItem = () => { |
| | | if (bomTableData.value.length > 0) { |
| | | const newItem = { |
| | | parentId: "", |
| | | parentTempId: "", |
| | | productName: "", |
| | | productId: "", |
| | | model: undefined, |
| | | productModelId: undefined, |
| | | processId: "", |
| | | processName: "", |
| | | unitQuantity: 0, |
| | | unit: "", |
| | | children: [], |
| | | tempId: new Date().getTime(), |
| | | }; |
| | | bomTableData.value[0].bomList.push(newItem); |
| | | // ç±äºbomDataValue.value.dataListåbomTableData.value[0].bomListæååä¸ä¸ªæ°ç»ï¼ä¸éè¦é夿·»å |
| | | } |
| | | }; |
| | | |
| | | // æ·»å BOMå项 |
| | | const addBomItem2 = tempId => { |
| | | const addChildItem = (items, tempId) => { |
| | | for (let i = 0; i < items.length; i++) { |
| | | const item = items[i]; |
| | | if (item.tempId === tempId) { |
| | | if (!item.children) { |
| | | item.children = []; |
| | | } |
| | | item.children.push({ |
| | | parentId: item.id || "", |
| | | parentTempId: item.tempId || "", |
| | | productName: "", |
| | | productId: "", |
| | | model: undefined, |
| | | productModelId: undefined, |
| | | processId: "", |
| | | processName: "", |
| | | unitQuantity: 0, |
| | | unit: "", |
| | | children: [], |
| | | tempId: new Date().getTime(), |
| | | }); |
| | | return true; |
| | | } |
| | | if (item.children && item.children.length > 0) { |
| | | if (addChildItem(item.children, tempId)) { |
| | | return true; |
| | | } |
| | | } |
| | | } |
| | | return false; |
| | | }; |
| | | |
| | | if (bomTableData.value.length > 0) { |
| | | addChildItem(bomTableData.value[0].bomList, tempId); |
| | | // ç±äºbomDataValue.value.dataListåbomTableData.value[0].bomListæååä¸ä¸ªæ°ç»ï¼ä¸éè¦é夿·»å |
| | | } |
| | | }; |
| | | |
| | | // å é¤BOM项 |
| | | const removeBomItem = tempId => { |
| | | // ä»BOMè¡¨æ ¼æ°æ®ä¸å é¤ |
| | | if (bomTableData.value.length > 0) { |
| | | const removeFromList = (items, tempId) => { |
| | | for (let i = 0; i < items.length; i++) { |
| | | const item = items[i]; |
| | | if (item.tempId === tempId) { |
| | | items.splice(i, 1); |
| | | return true; |
| | | } |
| | | if (item.children && item.children.length > 0) { |
| | | if (removeFromList(item.children, tempId)) { |
| | | return true; |
| | | } |
| | | } |
| | | } |
| | | return false; |
| | | }; |
| | | removeFromList(bomTableData.value[0].bomList, tempId); |
| | | // ç±äºbomDataValue.value.dataListåbomTableData.value[0].bomListæååä¸ä¸ªæ°ç»ï¼ä¸éè¦éå¤å é¤ |
| | | } |
| | | }; |
| | | |
| | | // æå¼BOM产åéæ©å¯¹è¯æ¡ |
| | | const openBomProductDialog = tempId => { |
| | | bomDataValue.value.currentRowName = tempId; |
| | | bomDataValue.value.showProductDialog = true; |
| | | }; |
| | | |
| | | // å¤çBOM产åéæ© |
| | | const handleBomProductSelect = products => { |
| | | if (products && products.length > 0) { |
| | | const product = products[0]; |
| | | const updateProductInfo = (items, tempId, productData) => { |
| | | for (let i = 0; i < items.length; i++) { |
| | | const item = items[i]; |
| | | if (item.tempId === tempId) { |
| | | item.productName = productData.productName; |
| | | item.model = productData.model; |
| | | item.productModelId = productData.id; |
| | | item.unit = productData.unit || ""; |
| | | return true; |
| | | } |
| | | if (item.children && item.children.length > 0) { |
| | | if (updateProductInfo(item.children, tempId, productData)) { |
| | | return true; |
| | | } |
| | | } |
| | | } |
| | | return false; |
| | | }; |
| | | |
| | | if (bomTableData.value.length > 0) { |
| | | updateProductInfo( |
| | | bomTableData.value[0].bomList, |
| | | bomDataValue.value.currentRowName, |
| | | product |
| | | ); |
| | | // ç±äºbomDataValue.value.dataListåbomTableData.value[0].bomListæååä¸ä¸ªæ°ç»ï¼ä¸éè¦é夿´æ° |
| | | } |
| | | bomDataValue.value.showProductDialog = false; |
| | | } |
| | | }; |
| | | |
| | | // ä¿åBOMæ´æ¹ |
| | | const saveBomChanges = () => { |
| | | // æ ¡éªBOMæ°æ® |
| | | const validateBomData = items => { |
| | | for (let i = 0; i < items.length; i++) { |
| | | const item = items[i]; |
| | | // æ ¡éªäº§åæ¯å¦å¿
å¡« |
| | | if (!item.productModelId) { |
| | | ElMessage.error("è¯·éæ©äº§å"); |
| | | return false; |
| | | } |
| | | // æ ¡éªåä½äº§åºæéæ°éæ¯å¦å¿
å¡« |
| | | if ( |
| | | item.unitQuantity === undefined || |
| | | item.unitQuantity === null || |
| | | item.unitQuantity === 0 |
| | | ) { |
| | | ElMessage.error("请填ååä½äº§åºæéæ°é"); |
| | | return false; |
| | | } |
| | | // é彿 ¡éªå项 |
| | | if (item.children && item.children.length > 0) { |
| | | if (!validateBomData(item.children)) { |
| | | return false; |
| | | } |
| | | } |
| | | } |
| | | return true; |
| | | }; |
| | | |
| | | // æ§è¡æ ¡éª |
| | | if (bomTableData.value.length > 0) { |
| | | if (!validateBomData(bomTableData.value[0].bomList)) { |
| | | return; |
| | | } |
| | | } |
| | | |
| | | // è°ç¨æ°çä¿åæ¥å£ |
| | | // åå¤ä¿åæ°æ®ï¼ç¡®ä¿æ ¼å¼æ£ç¡® |
| | | // éå½å¤çBOM项åå
¶å项 |
| | | const processBomItem = (item, parentId = null, parentTempId = null) => { |
| | | const cleanItem = { |
| | | id: item.id || null, |
| | | orderId: Number(orderId.value) || null, |
| | | parentId: parentId, |
| | | parentTempId: parentTempId || null, |
| | | productModelId: item.productModelId || null, |
| | | processId: item.processId || null, |
| | | unitQuantity: item.unitQuantity || 0, |
| | | demandedQuantity: item.demandedQuantity || null, |
| | | unit: item.unit || "", |
| | | tempId: item.tempId || new Date().getTime(), |
| | | tenantId: item.tenantId || null, |
| | | bomId: Number(route.query.bomId) || null, |
| | | children: [], |
| | | }; |
| | | |
| | | // éå½å¤çå项 |
| | | if (item.children && item.children.length > 0) { |
| | | cleanItem.children = item.children.map(child => |
| | | processBomItem(child, item.id, item.tempId || null) |
| | | ); |
| | | } |
| | | |
| | | return cleanItem; |
| | | }; |
| | | |
| | | const saveData = bomTableData.value[0].bomList.map(item => |
| | | processBomItem(item, item.parentId, item.parentTempId || null) |
| | | ); |
| | | const formData = { |
| | | orderId: Number(orderId.value) || null, |
| | | children: saveData, |
| | | }; |
| | | |
| | | add2(formData) |
| | | .then(res => { |
| | | if (res.code === 200) { |
| | | ElMessage.success("BOMä¿åæå"); |
| | | bomDataValue.value.isEdit = false; |
| | | // éæ°å è½½æ°æ®ä»¥è·åææ°ç¶æ |
| | | getRouteInfo(); |
| | | } else { |
| | | ElMessage.error("BOMä¿å失败ï¼" + (res.msg || "æªç¥é误")); |
| | | } |
| | | }) |
| | | .catch(err => { |
| | | console.error("ä¿åBOMæ°æ®å¤±è´¥ï¼", err); |
| | | ElMessage.error("BOMä¿å失败ï¼ç½ç»é误"); |
| | | }); |
| | | }; |
| | | |
| | | // åæ¶BOMç¼è¾ |
| | | const cancelBomEdit = () => { |
| | | bomDataValue.value.isEdit = false; |
| | | getRouteInfo(); |
| | | }; |
| | | |
| | | // åå§åææ½æåº |
| | |
| | | word-break: break-all; |
| | | } |
| | | </style> |
| | | <style scoped> |
| | | .hover-effect:hover { |
| | | border-color: #409eff; |
| | | background-color: #ecf5ff; |
| | | transform: translateY(-2px); |
| | | box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); |
| | | } |
| | | </style> |
| | |
| | | style="margin: 0"> |
| | | <el-select v-model="row.model" |
| | | placeholder="è¯·éæ©è§æ ¼" |
| | | clearable |
| | | :disabled="!dataValue.isEdit" |
| | | style="width: 100%" |
| | | @visible-change="(v) => { if (v) openDialog(row.tempId) }"> |
| | |
| | | style="margin: 0"> |
| | | <el-input-number v-model="row.unitQuantity" |
| | | :min="0" |
| | | :precision="2" |
| | | :step="1" |
| | | controls-position="right" |
| | | style="width: 100%" |
| | |
| | | <div class="search_form"> |
| | | <el-form :model="searchForm" |
| | | :inline="true"> |
| | | <el-form-item label="订åå·:"> |
| | | <el-input v-model="searchForm.npsNo" |
| | | placeholder="请è¾å
¥" |
| | | clearable |
| | | prefix-icon="Search" |
| | | style="width: 160px;" |
| | | @change="handleQuery" /> |
| | | </el-form-item> |
| | | <el-form-item label="产ååç§°:"> |
| | | <el-input v-model="searchForm.productName" |
| | | placeholder="请è¾å
¥" |
| | |
| | | prefix-icon="Search" |
| | | style="width: 160px;" |
| | | @change="handleQuery" /> |
| | | </el-form-item> |
| | | <el-form-item label="产åç±»å:"> |
| | | <el-select v-model="searchForm.strength" |
| | | style="width: 200px;" |
| | | placeholder="è¯·éæ©äº§åç±»å" |
| | | clearable |
| | | @change="handleQuery"> |
| | | <el-option v-for="option in productTypeOptions2" |
| | | :key="option.dictLabel" |
| | | :label="option.dictLabel" |
| | | :value="option.dictLabel" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="å建æ¶é´:"> |
| | | <el-date-picker v-model="createTime" |
| | | type="daterange" |
| | | range-separator="è³" |
| | | start-placeholder="å¼å§æ¥æ" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | end-placeholder="ç»ææ¥æ" |
| | | style="width: 300px;" |
| | | @change="handleQuery" /> |
| | | </el-form-item> |
| | | <el-form-item label="è§æ ¼:"> |
| | | <el-input v-model="searchForm.model" |
| | |
| | | @click="handleQuery">æç´¢</el-button> |
| | | <el-button type="primary" |
| | | @click="handleReset">éç½®</el-button> |
| | | <el-button type="danger" |
| | | @click="handleDelete">éå</el-button> |
| | | <el-button @click="handleOut">导åº</el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | <div> |
| | | <el-button type="danger" |
| | | @click="handleDelete">éå</el-button> |
| | | <el-button @click="handleOut">导åº</el-button> |
| | | </div> |
| | | <!-- <div style="width:350px;text-align:right;"> |
| | | </div> --> |
| | | </div> |
| | | <div class="table_list"> |
| | | <PIMTable rowKey="id" |
| | |
| | | <el-progress :percentage="toProgressPercentage(row?.completionStatus)" |
| | | :color="progressColor(toProgressPercentage(row?.completionStatus))" |
| | | :status="toProgressPercentage(row?.completionStatus) >= 100 ? 'success' : ''" /> |
| | | </template> |
| | | <template #quantity="{ row }"> |
| | | {{ row.quantity || '-' }}<span style="color:rgb(63, 95, 211)"> å</span> |
| | | </template> |
| | | <template #completeQuantity="{ row }"> |
| | | {{ row.completeQuantity || '-' }}<span style="color:rgb(42, 169, 146)"> æ¹</span> |
| | | </template> |
| | | </PIMTable> |
| | | </div> |
| | |
| | | <new-product-order v-if="isShowNewModal" |
| | | v-model:visible="isShowNewModal" |
| | | @completed="handleQuery" /> |
| | | <!-- æ¥æºæ°æ®å¼¹çª --> |
| | | <el-dialog v-model="sourceDataDialogVisible" |
| | | title="æ¥æºæ°æ®" |
| | | width="1000px"> |
| | | <div class="applyno-summary1"> |
| | | <div class="summary-item"> |
| | | <span class="summary-label">产ååç§°ï¼</span> |
| | | <span class="summary-value"> |
| | | <el-tag type="primary">{{ sourceRowData.productName || '-' }}</el-tag> |
| | | </span> |
| | | </div> |
| | | <div class="summary-item"> |
| | | <span class="summary-label">产åè§æ ¼ï¼</span> |
| | | <span class="summary-value">{{ sourceRowData.model || '-' }}</span> |
| | | </div> |
| | | <div class="summary-item"> |
| | | <span class="summary-label">ç©æç¼ç ï¼</span> |
| | | <span class="summary-value">{{ sourceRowData.materialCode || '-' }}</span> |
| | | </div> |
| | | <div class="summary-item"> |
| | | <span class="summary-label">强度ï¼</span> |
| | | <span class="summary-value">{{ sourceRowData.strength || '-' }}</span> |
| | | </div> |
| | | </div> |
| | | <div class="source-data-container"> |
| | | <!-- 左侧applyNoå表 --> |
| | | <div class="applyno-list"> |
| | | <div class="list-header">ç³è¯·åå表</div> |
| | | <div class="list-body"> |
| | | <div v-for="(item, index) in sourceTableData" |
| | | :key="item.applyNo || index" |
| | | class="applyno-item" |
| | | :class="{ active: selectedApplyNo === item.applyNo }" |
| | | @click="selectApplyNo(item)"> |
| | | <div class="applyno-text">{{ item.applyNo }}</div> |
| | | <div class="applyno-info">{{ item.customerName }}</div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <!-- å³ä¾§è¯¦ç»ä¿¡æ¯ --> |
| | | <div class="detail-info"> |
| | | <div v-if="selectedSourceData && selectedSourceData.items && selectedSourceData.items.length > 0"> |
| | | <div v-for="item in selectedSourceData.items" |
| | | :key="item.id" |
| | | class="source-data-card"> |
| | | <!-- <div class="card-header"> |
| | | <div class="data-source-tag"> |
| | | </div> |
| | | <div class="card-title">产åæç»</div> |
| | | </div> --> |
| | | <div class="card-body"> |
| | | <div class="info-grid"> |
| | | <div class="info-item"> |
| | | <div class="info-label">æ°æ®æ¥æº</div> |
| | | <div class="info-value"> |
| | | <el-tag :type="item.dataSourceType == 1 ? 'primary' : 'warning'"> |
| | | {{ item.dataSourceType == 1 ? 'éé忥' : 'æå¨æ°å¢' }} |
| | | </el-tag> |
| | | </div> |
| | | </div> |
| | | <div class="info-item"> |
| | | <div class="info-label">åæ°</div> |
| | | <div class="info-value">{{ item.quantity || '-' }}<span style="color:rgb(63, 95, 211)"> å</span></div> |
| | | </div> |
| | | <div class="info-item"> |
| | | <div class="info-label">æ¹æ°</div> |
| | | <div class="info-value">{{ item.volume || '-' }}<span style="color:rgba(27, 104, 90, 0.76)"> æ¹</span></div> |
| | | </div> |
| | | <div class="info-item"> |
| | | <div class="info-label">ä¸åç¶æ</div> |
| | | <div class="info-value"> |
| | | <el-tag :type="{ |
| | | 0: 'warning', |
| | | 1: 'primary', |
| | | 2: 'info' |
| | | }[item.status] || 'info'"> |
| | | {{ item.status == 0 ? 'å¾
ä¸å' : item.status == 1 ? 'é¨åä¸å' : 'å·²ä¸å' }} |
| | | </el-tag> |
| | | </div> |
| | | </div> |
| | | <div class="info-item"> |
| | | <div class="info-label">å·²ä¸åæ¹æ°</div> |
| | | <div class="info-value">{{ item.assignedQuantity ? `${item.assignedQuantity}` : 0 }}<span style="color:rgba(214, 134, 22, 0.76)"> æ¹</span></div> |
| | | </div> |
| | | <div class="info-item"> |
| | | <div class="info-label">尺寸</div> |
| | | <div class="info-value">{{ item.length || '-' }}mm à {{ item.width || '-' }}mm à {{ item.height || '-' }}mm</div> |
| | | </div> |
| | | <div class="info-item"> |
| | | <div class="info-label">计åå¼å§æ¥æ</div> |
| | | <div class="info-value">{{ item.startDate ? dayjs(item.startDate).format('YYYY-MM-DD') : '' }}</div> |
| | | </div> |
| | | <div class="info-item"> |
| | | <div class="info-label">计åç»ææ¥æ</div> |
| | | <div class="info-value">{{ item.endDate ? dayjs(item.endDate).format('YYYY-MM-DD') : '' }}</div> |
| | | </div> |
| | | <!-- <div class="info-item"> |
| | | <div class="info-label">强度</div> |
| | | <div class="info-value">{{ item.strength || '' }}</div> |
| | | </div> --> |
| | | </div> |
| | | <div class="remarks-section"> |
| | | <div class="info-item full-width"> |
| | | <div class="info-label">夿³¨ 1</div> |
| | | <div class="info-value">{{ item.remarkOne || '-' }}</div> |
| | | </div> |
| | | <div class="info-item full-width"> |
| | | <div class="info-label">夿³¨ 2</div> |
| | | <div class="info-value">{{ item.remarkTwo || '-' }}</div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <div v-else |
| | | class="empty-state"> |
| | | <el-empty :description="selectedSourceData ? '该ç³è¯·åææ æ°æ®' : 'è¯·éæ©ä¸ä¸ªç³è¯·å'" /> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | |
| | | bindingRoute, |
| | | listProcessBom, |
| | | delProductOrder, |
| | | revokeProductOrder, |
| | | getProductOrderSource, |
| | | } from "@/api/productionManagement/productionOrder.js"; |
| | | import { listPage } from "@/api/productionManagement/processRoute.js"; |
| | | import { listMain as getOrderProcessRouteMain } from "@/api/productionManagement/productProcessRoute.js"; |
| | |
| | | |
| | | const router = useRouter(); |
| | | const isShowNewModal = ref(false); |
| | | const sourceDataDialogVisible = ref(false); |
| | | const sourceTableData = ref([]); |
| | | const selectedApplyNo = ref(""); |
| | | const selectedSourceData = ref(null); |
| | | const sourceRowData = ref(null); |
| | | |
| | | const tableColumn = ref([ |
| | | { |
| | |
| | | label: "产ååç§°", |
| | | prop: "productName", |
| | | width: "120px", |
| | | dataType: "tag", |
| | | }, |
| | | { |
| | | label: "è§æ ¼", |
| | | prop: "model", |
| | | width: "120px", |
| | | }, |
| | | { |
| | | label: "强度", |
| | | prop: "strength", |
| | | width: "120px", |
| | | dataType: "tag", |
| | | }, |
| | | { |
| | | label: "ç©æç¼ç ", |
| | |
| | | label: "å·¥èºè·¯çº¿ç¼å·", |
| | | prop: "processRouteCode", |
| | | width: "200px", |
| | | className: "status-cell", |
| | | }, |
| | | { |
| | | label: "éæ±æ°é", |
| | | prop: "quantity", |
| | | dataType: "slot", |
| | | align: "right", |
| | | slot: "quantity", |
| | | width: 120, |
| | | }, |
| | | { |
| | | label: "宿æ°é", |
| | | prop: "completeQuantity", |
| | | dataType: "slot", |
| | | align: "right", |
| | | slot: "completeQuantity", |
| | | width: 120, |
| | | }, |
| | | { |
| | | dataType: "slot", |
| | |
| | | label: "äº¤ä»æ¥æ", |
| | | prop: "planCompleteTime", |
| | | formatData: val => (val ? dayjs(val).format("YYYY-MM-DD") : ""), |
| | | width: 120, |
| | | }, |
| | | { |
| | | label: "å建æ¶é´", |
| | | prop: "createTime", |
| | | formatData: val => (val ? dayjs(val).format("YYYY-MM-DD HH:mm:ss") : ""), |
| | | width: 120, |
| | | }, |
| | | |
| | |
| | | }); |
| | | const selectedRows = ref([]); |
| | | |
| | | // æ¥æºæ°æ®å¼¹çªç¸å
³ |
| | | const sourceTableColumn = ref([ |
| | | { |
| | | label: "æ°æ®æ¥æº", |
| | | width: "100px", |
| | | prop: "dataSourceType", |
| | | dataType: "tag", |
| | | formatType: params => { |
| | | const typeMap = { |
| | | 2: "warning", |
| | | 1: "primary", |
| | | }; |
| | | return typeMap[params] || "info"; |
| | | }, |
| | | formatData: cell => (cell == 1 ? "éé忥" : "æå¨æ°å¢"), |
| | | }, |
| | | { |
| | | label: "ç³è¯·åç¼å·", |
| | | prop: "applyNo", |
| | | width: "150px", |
| | | }, |
| | | { |
| | | label: "客æ·åç§°", |
| | | prop: "customerName", |
| | | width: "150px", |
| | | }, |
| | | { |
| | | label: "产ååç§°", |
| | | prop: "productName", |
| | | width: "200px", |
| | | dataType: "tag", |
| | | formatType: params => { |
| | | return "primary"; |
| | | }, |
| | | }, |
| | | { |
| | | label: "产åè§æ ¼", |
| | | prop: "model", |
| | | width: "150px", |
| | | className: "spec-cell", |
| | | }, |
| | | { |
| | | label: "ç©æç¼ç ", |
| | | prop: "materialCode", |
| | | width: "150px", |
| | | }, |
| | | { |
| | | label: "åæ°", |
| | | prop: "quantity", |
| | | align: "right", |
| | | dataType: "slot", |
| | | slot: "quantity", |
| | | }, |
| | | { |
| | | label: "æ¹æ°", |
| | | prop: "volume", |
| | | width: "150px", |
| | | align: "right", |
| | | dataType: "slot", |
| | | slot: "volume", |
| | | className: "volume-cell", |
| | | }, |
| | | { |
| | | label: "ä¸åç¶æ", |
| | | prop: "status", |
| | | width: "150px", |
| | | className: "status-cell", |
| | | dataType: "tag", |
| | | formatType: params => { |
| | | const typeMap = { |
| | | 0: "warning", |
| | | 1: "primary", |
| | | 2: "info", |
| | | }; |
| | | return typeMap[params] || "info"; |
| | | }, |
| | | formatData: cell => { |
| | | const statusMap = { |
| | | 0: "å¾
ä¸å", |
| | | 1: "é¨åä¸å", |
| | | 2: "å·²ä¸å", |
| | | }; |
| | | return statusMap[cell] || ""; |
| | | }, |
| | | }, |
| | | { |
| | | label: "å·²ä¸åæ¹æ°", |
| | | prop: "assignedQuantity", |
| | | width: "150px", |
| | | className: "spec-cell", |
| | | formatData: cell => (cell ? `${cell}æ¹` : 0), |
| | | }, |
| | | { |
| | | label: "é¿", |
| | | prop: "length", |
| | | className: "dimension-cell", |
| | | formatData: cell => (cell ? `${cell}mm` : ""), |
| | | }, |
| | | { |
| | | label: "宽", |
| | | prop: "width", |
| | | className: "dimension-cell", |
| | | formatData: cell => (cell ? `${cell}mm` : ""), |
| | | }, |
| | | { |
| | | label: "é«", |
| | | prop: "height", |
| | | className: "dimension-cell", |
| | | formatData: cell => (cell ? `${cell}mm` : ""), |
| | | }, |
| | | { |
| | | label: "计åå¼å§æ¥æ", |
| | | prop: "startDate", |
| | | width: "150px", |
| | | className: "date-cell", |
| | | formatData: cell => (cell ? dayjs(cell).format("YYYY-MM-DD") : ""), |
| | | }, |
| | | { |
| | | label: "计åç»ææ¥æ", |
| | | prop: "endDate", |
| | | width: "150px", |
| | | className: "date-cell", |
| | | formatData: cell => (cell ? dayjs(cell).format("YYYY-MM-DD") : ""), |
| | | }, |
| | | { |
| | | label: "强度", |
| | | prop: "strength", |
| | | formatData: cell => { |
| | | if (!cell) return ""; |
| | | return cell; |
| | | }, |
| | | }, |
| | | { |
| | | label: "夿³¨ 1", |
| | | width: "150px", |
| | | prop: "remarkOne", |
| | | }, |
| | | { |
| | | label: "夿³¨ 2", |
| | | width: "150px", |
| | | prop: "remarkTwo", |
| | | }, |
| | | ]); |
| | | const sourceTableLoading = ref(false); |
| | | const sourcePage = reactive({ |
| | | current: 1, |
| | | size: 100, |
| | | total: 0, |
| | | }); |
| | | |
| | | const data = reactive({ |
| | | searchForm: { |
| | | customerName: "", |
| | |
| | | projectName: "", |
| | | productName: "", |
| | | model: "", |
| | | dictCode: null, |
| | | startTime: null, |
| | | endTime: null, |
| | | strength: null, |
| | | status: "", |
| | | }, |
| | | }); |
| | | const { searchForm } = toRefs(data); |
| | |
| | | productName: "", |
| | | model: "", |
| | | status: "", |
| | | strength: null, |
| | | startTime: null, |
| | | endTime: null, |
| | | }; |
| | | createTime.value = []; |
| | | handleQuery(); |
| | | }; |
| | | // æ¥è¯¢å表 |
| | |
| | | } |
| | | handleQuery(); |
| | | }; |
| | | const createTime = ref([]); |
| | | const getList = () => { |
| | | tableLoading.value = true; |
| | | // æé ä¸ä¸ªæ°ç对象ï¼ä¸å
å«entryDateåæ®µ |
| | | const params = { ...searchForm.value, ...page }; |
| | | params.entryDate = undefined; |
| | | params.startTime = |
| | | createTime.value.length > 0 |
| | | ? dayjs(createTime.value[0]).format("YYYY-MM-DD HH:mm:ss") |
| | | : undefined; |
| | | params.endTime = |
| | | createTime.value.length > 0 |
| | | ? dayjs(createTime.value[1]) |
| | | .hour(23) |
| | | .minute(59) |
| | | .second(59) |
| | | .format("YYYY-MM-DD HH:mm:ss") |
| | | : undefined; |
| | | |
| | | productOrderListPage(params) |
| | | .then(res => { |
| | | tableLoading.value = false; |
| | |
| | | }); |
| | | }; |
| | | |
| | | // éæ©ç³è¯·å |
| | | const selectApplyNo = item => { |
| | | selectedApplyNo.value = item.applyNo; |
| | | selectedSourceData.value = item; |
| | | }; |
| | | |
| | | // æ¥çæ¥æºçäº§è®¡åæ°æ® |
| | | const showSourceData = row => { |
| | | // è¿ééè¦æ ¹æ®å®é
çAPIåè·¯ç±è¿è¡è°æ´ |
| | | // å设ç产订å䏿ç产计åIDåæ®µï¼æ¯å¦productionPlanId |
| | | if (row.productionPlanId) { |
| | | // 跳转å°ç产计å详æ
é¡µé¢ |
| | | router.push({ |
| | | path: "/productionManagement/productionPlan", |
| | | query: { |
| | | id: row.productionPlanId, |
| | | }, |
| | | // åå¨ç¹å»æ¥æºæé®æ¶ä¼ éçrowåæ° |
| | | sourceRowData.value = row; |
| | | // è°ç¨APIè·åæ¥æºæ°æ® |
| | | getProductOrderSource(row.id) |
| | | .then(res => { |
| | | if (res.code === 200) { |
| | | // å¤çæ¥å£è¿åçæ°æ®ï¼è°æ´ä¸ºæä»¬éè¦çæ ¼å¼ |
| | | sourceTableData.value = res.data.map(item => { |
| | | return { |
| | | applyNo: item.applyNo, |
| | | customerName: item.productPlans[0]?.customerName || "", |
| | | items: item.productPlans.map(plan => { |
| | | return { |
| | | id: plan.id, |
| | | dataSourceType: plan.dataSourceType, |
| | | productName: plan.productName, |
| | | model: plan.model, |
| | | materialCode: plan.materialCode, |
| | | quantity: plan.quantity, |
| | | volume: plan.volume, |
| | | status: plan.status, |
| | | assignedQuantity: plan.assignedQuantity, |
| | | length: plan.length, |
| | | width: plan.width, |
| | | height: plan.height, |
| | | startDate: plan.startDate, |
| | | endDate: plan.endDate, |
| | | strength: plan.strength, |
| | | remarkOne: plan.remarkOne, |
| | | remarkTwo: plan.remarkTwo, |
| | | }; |
| | | }), |
| | | }; |
| | | }); |
| | | sourcePage.total = sourceTableData.value.length; |
| | | // é»è®¤éæ©ç¬¬ä¸ä¸ªç³è¯·å |
| | | if (sourceTableData.value.length > 0) { |
| | | selectApplyNo(sourceTableData.value[0]); |
| | | } else { |
| | | selectedApplyNo.value = ""; |
| | | selectedSourceData.value = null; |
| | | } |
| | | // æå¼å¼¹çª |
| | | sourceDataDialogVisible.value = true; |
| | | } else { |
| | | proxy.$modal.msgError(res.msg || "è·åæ¥æºæ°æ®å¤±è´¥"); |
| | | } |
| | | }) |
| | | .catch(err => { |
| | | proxy.$modal.msgError("è·åæ¥æºæ°æ®å¤±è´¥"); |
| | | console.error(err); |
| | | }); |
| | | } else { |
| | | proxy.$modal.msgWarning("å½åè®¢åæ²¡æå
³èçç产计å"); |
| | | } |
| | | }; |
| | | |
| | | // è¡¨æ ¼éæ©æ°æ® |
| | |
| | | selectedRows.value = selection; |
| | | }; |
| | | const handleDeleteSolo = row => { |
| | | ElMessageBox.confirm("éä¸çå
容å°è¢«éåï¼æ¯å¦ç¡®è®¤éåï¼", "导åº", { |
| | | ElMessageBox.confirm("éä¸çå
容å°è¢«å é¤ï¼æ¯å¦ç¡®è®¤å é¤ï¼", "导åº", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | delProductOrder([row.id]).then(res => { |
| | | delProductOrder(row.id).then(res => { |
| | | proxy.$modal.msgSuccess("å 餿å"); |
| | | getList(); |
| | | }); |
| | |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | delProductOrder(ids).then(res => { |
| | | proxy.$modal.msgSuccess("å 餿å"); |
| | | revokeProductOrder(ids).then(res => { |
| | | proxy.$modal.msgSuccess("éåæå"); |
| | | getList(); |
| | | }); |
| | | }) |
| | |
| | | }; |
| | | |
| | | const handleConfirmRoute = () => {}; |
| | | const productTypeOptions2 = ref([]); |
| | | // è·å产åç±»ååå
¸ |
| | | const getProductTypeOptions = () => { |
| | | getDicts("block_strength") |
| | | .then(res => { |
| | | if (res.code === 200) { |
| | | productTypeOptions2.value = res.data; |
| | | } |
| | | }) |
| | | .catch(err => { |
| | | console.error("è·å产åç±»ååå
¸å¤±è´¥ï¼", err); |
| | | }); |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | getProductTypeOptions(); |
| | | getList(); |
| | | }); |
| | | </script> |
| | |
| | | background-color: #f4defa; |
| | | } |
| | | </style> |
| | | <style lang="scss"> |
| | | .status-cell { |
| | | font-weight: 600; |
| | | color: #409eff; |
| | | font-family: "Courier New", monospace; |
| | | text-shadow: 0 1px 2px rgba(64, 158, 255, 0.2); |
| | | } |
| | | |
| | | .source-data-container { |
| | | display: flex; |
| | | gap: 20px; |
| | | height: 500px; |
| | | |
| | | .applyno-list { |
| | | width: 250px; |
| | | background: #fff; |
| | | border-radius: 8px; |
| | | box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); |
| | | overflow: hidden; |
| | | |
| | | .list-header { |
| | | padding: 12px 16px; |
| | | background: #f5f7fa; |
| | | border-bottom: 1px solid #e4e7ed; |
| | | font-weight: 600; |
| | | color: #303133; |
| | | } |
| | | |
| | | .list-body { |
| | | height: calc(100% - 48px); |
| | | overflow-y: auto; |
| | | |
| | | .applyno-item { |
| | | padding: 12px 16px; |
| | | border-bottom: 1px solid #f0f2f5; |
| | | cursor: pointer; |
| | | transition: all 0.3s; |
| | | |
| | | &:hover { |
| | | background: #f5f7fa; |
| | | } |
| | | |
| | | &.active { |
| | | background: #ecf5ff; |
| | | border-left: 4px solid #409eff; |
| | | } |
| | | |
| | | .applyno-text { |
| | | font-weight: 600; |
| | | color: #303133; |
| | | font-family: "Courier New", monospace; |
| | | margin-bottom: 4px; |
| | | } |
| | | |
| | | .applyno-info { |
| | | font-size: 12px; |
| | | color: #909399; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | .detail-info { |
| | | flex: 1; |
| | | background: #fff; |
| | | border-radius: 8px; |
| | | // box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); |
| | | overflow: auto; |
| | | display: flex; |
| | | flex-direction: column; |
| | | |
| | | .applyno-summary { |
| | | padding: 16px 20px; |
| | | background: #f5f7fa; |
| | | border-bottom: 1px solid #e4e7ed; |
| | | display: flex; |
| | | flex-wrap: wrap; |
| | | gap: 16px; |
| | | |
| | | .summary-item { |
| | | display: flex; |
| | | align-items: center; |
| | | |
| | | .summary-label { |
| | | font-size: 13px; |
| | | color: #909399; |
| | | margin-right: 8px; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | .summary-value { |
| | | font-size: 14px; |
| | | color: #303133; |
| | | font-weight: 500; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .empty-state { |
| | | flex: 1; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | } |
| | | |
| | | .source-data-card { |
| | | flex: 1; |
| | | display: flex; |
| | | flex-direction: column; |
| | | overflow: hidden; |
| | | margin-top: 20px; |
| | | margin-right: 20px; |
| | | box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); |
| | | |
| | | .card-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | padding: 16px 20px; |
| | | background: #f5f7fa; |
| | | border-bottom: 1px solid #e4e7ed; |
| | | |
| | | .data-source-tag { |
| | | flex-shrink: 0; |
| | | } |
| | | |
| | | .card-title { |
| | | font-weight: 600; |
| | | color: #303133; |
| | | font-size: 14px; |
| | | } |
| | | } |
| | | |
| | | .card-body { |
| | | flex: 1; |
| | | padding: 20px; |
| | | overflow-y: auto; |
| | | background-color: #f5f7fa; |
| | | |
| | | .info-grid { |
| | | display: grid; |
| | | grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); |
| | | gap: 16px; |
| | | margin-bottom: 20px; |
| | | |
| | | .info-item { |
| | | display: flex; |
| | | flex-direction: column; |
| | | |
| | | .info-label { |
| | | font-size: 12px; |
| | | color: #909399; |
| | | margin-bottom: 4px; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | .info-value { |
| | | font-size: 14px; |
| | | color: #303133; |
| | | font-weight: 500; |
| | | } |
| | | } |
| | | |
| | | .info-item.full-width { |
| | | grid-column: 1 / -1; |
| | | } |
| | | } |
| | | |
| | | .remarks-section { |
| | | display: flex; |
| | | // flex-direction: column; |
| | | gap: 12px; |
| | | border-top: 1px solid #e4e7ed; |
| | | padding-top: 16px; |
| | | |
| | | .info-item { |
| | | display: flex; |
| | | width: 50%; |
| | | flex-direction: column; |
| | | |
| | | .info-label { |
| | | font-size: 12px; |
| | | color: #909399; |
| | | margin-bottom: 4px; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | .info-value { |
| | | font-size: 14px; |
| | | color: #303133; |
| | | line-height: 1.5; |
| | | padding: 8px; |
| | | background: #f9fafc; |
| | | border-radius: 4px; |
| | | border: 1px solid #ecf5ff; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | .applyno-summary1 { |
| | | padding: 16px 20px; |
| | | background: #f5f7fa; |
| | | border-bottom: 1px solid #e4e7ed; |
| | | display: flex; |
| | | flex-wrap: wrap; |
| | | gap: 16px; |
| | | |
| | | .summary-item { |
| | | display: flex; |
| | | align-items: center; |
| | | margin-right: 20px; |
| | | |
| | | .summary-label { |
| | | font-size: 13px; |
| | | color: #909399; |
| | | margin-right: 8px; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | .summary-value { |
| | | font-size: 14px; |
| | | color: #303133; |
| | | font-weight: 500; |
| | | } |
| | | } |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <el-dialog v-model="localVisible" |
| | | :title="dialogTitle" |
| | | width="800px" |
| | | @close="handleClose"> |
| | | <!-- æ¥éª¤æ¡ --> |
| | | <el-steps :active="activeStep" |
| | | finish-status="success" |
| | | v-if="!props.data.id"> |
| | | <el-step title="éæ©ç产订å" /> |
| | | <el-step title="å¡«ååºç¡ä¿¡æ¯" /> |
| | | <el-step title="æ¥çå·¥åºåæ°" /> |
| | | <el-step title="å¡«å产éä¿¡æ¯" /> |
| | | </el-steps> |
| | | <!-- ç¬¬ä¸æ¥ï¼éæ©ç产订å --> |
| | | <div v-if="activeStep === 0"> |
| | | <el-form :model="form" |
| | | ref="formRef" |
| | | label-width="120px"> |
| | | <el-form-item label="ç产订å" |
| | | prop="orderId" |
| | | required> |
| | | <el-select v-model="orderId" |
| | | placeholder="è¯·éæ©ç产订å" |
| | | clearable |
| | | filterable |
| | | style="width: 100%" |
| | | :loading="orderLoading" |
| | | @change="handleOrderChange"> |
| | | <el-option v-for="order in orderList" |
| | | :key="order.id" |
| | | :label="`${order.npsNo} - ${order.productName} ${order.model}`" |
| | | :value="order.id" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-form> |
| | | </div> |
| | | <!-- ç¬¬äºæ¥ï¼å¡«ååºç¡ä¿¡æ¯ --> |
| | | <div v-else-if="activeStep === 1"> |
| | | <el-form :model="form" |
| | | :rules="rules" |
| | | ref="formRef" |
| | | label-width="120px"> |
| | | <el-form-item label="ç产订åå·" |
| | | prop="npsNo"> |
| | | <el-input disabled |
| | | v-model="form.npsNo" /> |
| | | </el-form-item> |
| | | <el-form-item label="çç»" |
| | | prop="teamName" |
| | | required> |
| | | <el-select v-model="form.teamName" |
| | | placeholder="è¯·éæ©çç»" |
| | | style="width: 100%"> |
| | | <el-option label="ç½ç" |
| | | value="ç½ç" /> |
| | | <el-option label="å¤ç" |
| | | value="å¤ç" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="产åç¼ç " |
| | | prop="materialCode"> |
| | | <el-input disabled |
| | | v-model="form.materialCode" /> |
| | | </el-form-item> |
| | | <el-form-item label="产ååç§°" |
| | | prop="productName"> |
| | | <el-input disabled |
| | | v-model="form.productName" /> |
| | | </el-form-item> |
| | | <el-form-item label="è§æ ¼" |
| | | prop="specification"> |
| | | <el-input disabled |
| | | v-model="form.specification" /> |
| | | </el-form-item> |
| | | <el-form-item label="å建人" |
| | | prop="createBy" |
| | | required> |
| | | <el-input v-model="form.createBy" |
| | | placeholder="请è¾å
¥å建人" /> |
| | | </el-form-item> |
| | | <el-form-item label="å建æ¶é´" |
| | | prop="createTime"> |
| | | <el-date-picker disabled |
| | | v-model="form.createTime" |
| | | type="datetime" |
| | | placeholder="è¯·éæ©å建æ¶é´" |
| | | style="width: 100%" /> |
| | | </el-form-item> |
| | | </el-form> |
| | | </div> |
| | | <!-- ç¬¬ä¸æ¥ï¼æ¥çå·¥åºåæ° --> |
| | | <div v-else-if="activeStep === 2"> |
| | | <!-- å·¥åºTab页 --> |
| | | <el-tabs v-model="activeProcessId" |
| | | @tab-click="handleTabClick"> |
| | | <el-tab-pane v-for="process in processList" |
| | | :key="process.id" |
| | | :label="process.processName" |
| | | :name="process.id + ''"> |
| | | <el-form :model="form" |
| | | ref="formRef" |
| | | label-width="120px"> |
| | | <!-- 卿忰 --> |
| | | <el-form-item v-for="param in params" |
| | | :key="param.id" |
| | | :label="param.paramName"> |
| | | <template v-if="param.paramType == '1'"> |
| | | <!-- æ°åç±»å --> |
| | | <el-input v-model="form.params[param.id]" /> |
| | | </template> |
| | | <template v-else-if="param.paramType == '2'"> |
| | | <!-- ææ¬ç±»å --> |
| | | <el-input v-model="form.params[param.id]" /> |
| | | </template> |
| | | <template v-else-if="param.paramType == '3'"> |
| | | <!-- åå
¸ç±»å --> |
| | | <el-select v-model="form.params[param.id]" |
| | | placeholder="è¯·éæ©" |
| | | style="width: 100%"> |
| | | <el-option v-for="option in dictOptions[param.paramFormat] || []" |
| | | :key="option.dictValue" |
| | | :label="option.dictLabel" |
| | | :value="option.dictValue" /> |
| | | </el-select> |
| | | </template> |
| | | <template v-else-if="param.paramType == '4'"> |
| | | <!-- æ¥æç±»å --> |
| | | <el-date-picker v-model="form.params[param.id]" /> |
| | | </template> |
| | | <template v-else> |
| | | <!-- å
¶ä»ç±»å --> |
| | | <el-input v-model="form.params[param.id]" /> |
| | | </template> |
| | | </el-form-item> |
| | | </el-form> |
| | | </el-tab-pane> |
| | | </el-tabs> |
| | | </div> |
| | | <!-- ç¬¬åæ¥ï¼å¡«å产éä¿¡æ¯ --> |
| | | <div v-else-if="activeStep === 3"> |
| | | <el-form :model="form" |
| | | :rules="rules" |
| | | ref="formRef" |
| | | label-width="120px"> |
| | | <el-form-item label="äº§åºæ¹é" |
| | | prop="outputVolume" |
| | | required> |
| | | <el-input-number v-model="form.outputVolume" |
| | | :min="0" |
| | | :precision="2" |
| | | style="width: 100%" /> |
| | | </el-form-item> |
| | | <el-form-item label="ä¸åæ ¼æ¹é" |
| | | prop="unqualifiedVolume" |
| | | required> |
| | | <el-input-number v-model="form.unqualifiedVolume" |
| | | :min="0" |
| | | :precision="2" |
| | | style="width: 100%" /> |
| | | </el-form-item> |
| | | <el-form-item label="宿æ¹é" |
| | | prop="completedVolume" |
| | | required> |
| | | <el-input-number v-model="form.completedVolume" |
| | | :min="0" |
| | | :precision="2" |
| | | style="width: 100%" /> |
| | | </el-form-item> |
| | | </el-form> |
| | | </div> |
| | | <!-- ç¼è¾æ¨¡å¼ï¼ç´æ¥æ¾ç¤ºææä¿¡æ¯ --> |
| | | <div v-if="props.data.id"> |
| | | <el-form :model="form" |
| | | :rules="rules" |
| | | ref="formRef" |
| | | label-width="120px"> |
| | | <el-form-item label="ç产订åå·" |
| | | prop="npsNo" |
| | | disabled> |
| | | <el-input v-model="form.npsNo" /> |
| | | </el-form-item> |
| | | <el-form-item label="çç»" |
| | | prop="teamName"> |
| | | <el-select v-model="form.teamName" |
| | | placeholder="è¯·éæ©çç»" |
| | | style="width: 100%"> |
| | | <el-option label="ç½ç" |
| | | value="ç½ç" /> |
| | | <el-option label="å¤ç" |
| | | value="å¤ç" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="产åç¼ç " |
| | | prop="materialCode" |
| | | disabled> |
| | | <el-input v-model="form.materialCode" /> |
| | | </el-form-item> |
| | | <el-form-item label="产ååç§°" |
| | | prop="productName" |
| | | disabled> |
| | | <el-input v-model="form.productName" /> |
| | | </el-form-item> |
| | | <el-form-item label="è§æ ¼" |
| | | prop="specification" |
| | | disabled> |
| | | <el-input v-model="form.specification" /> |
| | | </el-form-item> |
| | | <el-form-item label="å建人" |
| | | prop="createBy"> |
| | | <el-input v-model="form.createBy" |
| | | placeholder="请è¾å
¥å建人" /> |
| | | </el-form-item> |
| | | <el-form-item label="å建æ¶é´" |
| | | prop="createTime" |
| | | disabled> |
| | | <el-date-picker v-model="form.createTime" |
| | | type="datetime" |
| | | placeholder="è¯·éæ©å建æ¶é´" |
| | | style="width: 100%" /> |
| | | </el-form-item> |
| | | <el-form-item label="äº§åºæ¹é" |
| | | prop="outputVolume"> |
| | | <el-input-number v-model="form.outputVolume" |
| | | :min="0" |
| | | :precision="2" |
| | | style="width: 100%" /> |
| | | </el-form-item> |
| | | <el-form-item label="ä¸åæ ¼æ¹é" |
| | | prop="unqualifiedVolume"> |
| | | <el-input-number v-model="form.unqualifiedVolume" |
| | | :min="0" |
| | | :precision="2" |
| | | style="width: 100%" /> |
| | | </el-form-item> |
| | | <el-form-item label="宿æ¹é" |
| | | prop="completedVolume"> |
| | | <el-input-number v-model="form.completedVolume" |
| | | :min="0" |
| | | :precision="2" |
| | | style="width: 100%" /> |
| | | </el-form-item> |
| | | </el-form> |
| | | <!-- å·¥åºTab页 --> |
| | | <el-tabs v-model="activeProcessId" |
| | | @tab-click="handleTabClick"> |
| | | <el-tab-pane v-for="process in processList" |
| | | :key="process.id" |
| | | :label="process.processName" |
| | | :name="process.id + ''"> |
| | | <el-form :model="form" |
| | | :rules="rules" |
| | | ref="formRef" |
| | | label-width="120px"> |
| | | <!-- 卿忰 --> |
| | | <el-form-item v-for="param in params" |
| | | :key="param.id" |
| | | :label="param.paramName" |
| | | :prop="`params.${param.id}`" |
| | | :rules="param.isRequired ? [{ required: true, message: `请è¾å
¥${param.paramName}`, trigger: 'blur' }] : []"> |
| | | <template v-if="param.paramType == '1'"> |
| | | <!-- æ°åç±»å --> |
| | | <el-input-number v-model="form.params[param.id]" |
| | | :min="0" |
| | | :precision="getPrecision(param.paramFormat)" |
| | | style="width: 100%" /> |
| | | </template> |
| | | <template v-else-if="param.paramType == '2'"> |
| | | <!-- ææ¬ç±»å --> |
| | | <el-input v-model="form.params[param.id]" |
| | | :placeholder="`请è¾å
¥${param.paramName}`" /> |
| | | </template> |
| | | <template v-else-if="param.paramType == '3'"> |
| | | <!-- åå
¸ç±»å --> |
| | | <el-select v-model="form.params[param.id]" |
| | | placeholder="è¯·éæ©" |
| | | style="width: 100%"> |
| | | <el-option v-for="option in dictOptions[param.paramFormat] || []" |
| | | :key="option.dictValue" |
| | | :label="option.dictLabel" |
| | | :value="option.dictValue" /> |
| | | </el-select> |
| | | </template> |
| | | <template v-else-if="param.paramType == '4'"> |
| | | <!-- æ¥æç±»å --> |
| | | <el-date-picker v-model="form.params[param.id]" |
| | | type="datetime" |
| | | placeholder="è¯·éæ©æ¥ææ¶é´" |
| | | style="width: 100%" /> |
| | | </template> |
| | | <template v-else> |
| | | <!-- å
¶ä»ç±»å --> |
| | | <el-input v-model="form.params[param.id]" |
| | | :placeholder="`请è¾å
¥${param.paramName}`" /> |
| | | </template> |
| | | </el-form-item> |
| | | </el-form> |
| | | </el-tab-pane> |
| | | </el-tabs> |
| | | </div> |
| | | <template #footer> |
| | | <span class="dialog-footer"> |
| | | <el-button @click="handleClose">å æ¶</el-button> |
| | | <el-button type="primary" |
| | | v-if="activeStep > 0" |
| | | @click="activeStep--">ä¸ä¸æ¥</el-button> |
| | | <el-button type="primary" |
| | | v-if="activeStep < 3 && !props.data.id" |
| | | @click="handleNextStep">ä¸ä¸æ¥</el-button> |
| | | <el-button type="primary" |
| | | v-if="activeStep === 3 || props.data.id" |
| | | :loading="submitLoading" |
| | | @click="handleSubmit">确 认</el-button> |
| | | </span> |
| | | </template> |
| | | </el-dialog> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, computed, watch } from "vue"; |
| | | import { ElMessage } from "element-plus"; |
| | | import { getDicts } from "@/api/system/dict/data"; |
| | | import { productOrderListPage } from "@/api/productionManagement/productionOrder.js"; |
| | | import { |
| | | findProductProcessRouteItemList, |
| | | findProcessParamListOrder, |
| | | } from "@/api/productionManagement/productProcessRoute.js"; |
| | | |
| | | const props = defineProps({ |
| | | visible: { |
| | | type: Boolean, |
| | | default: false, |
| | | }, |
| | | data: { |
| | | type: Object, |
| | | default: () => ({}), |
| | | }, |
| | | }); |
| | | |
| | | const emit = defineEmits(["update:visible", "completed"]); |
| | | |
| | | const dialogTitle = computed(() => (props.data.id ? "ç¼è¾æ¥å·¥" : "æ°å¢æ¥å·¥")); |
| | | |
| | | const formRef = ref(null); |
| | | const submitLoading = ref(false); |
| | | const orderLoading = ref(false); |
| | | const processLoading = ref(false); |
| | | const localVisible = ref(props.visible); |
| | | const activeStep = ref(0); |
| | | |
| | | const orderId = ref(props.data.orderId || ""); |
| | | const processId = ref(props.data.processId || ""); |
| | | const activeProcessId = ref(""); |
| | | const orderList = ref([]); |
| | | const processList = ref([]); |
| | | const params = ref([]); |
| | | const dictOptions = ref({}); |
| | | |
| | | const form = reactive({ |
| | | id: props.data.id || undefined, |
| | | orderId: props.data.orderId || "", |
| | | npsNo: props.data.npsNo || "", |
| | | teamName: props.data.teamName || "", |
| | | materialCode: props.data.materialCode || "", |
| | | productName: props.data.productName || "", |
| | | specification: props.data.specification || "", |
| | | outputVolume: props.data.outputVolume || 0, |
| | | unqualifiedVolume: props.data.unqualifiedVolume || 0, |
| | | completedVolume: props.data.completedVolume || 0, |
| | | createBy: props.data.createBy || "å½åç»å½äºº", |
| | | createTime: props.data.createTime || new Date(), |
| | | params: props.data.params || {}, |
| | | }); |
| | | |
| | | const rules = { |
| | | teamName: [{ required: true, message: "è¯·éæ©çç»", trigger: "blur" }], |
| | | outputVolume: [ |
| | | { required: true, message: "请è¾å
¥äº§åºæ¹é", trigger: "blur" }, |
| | | ], |
| | | unqualifiedVolume: [ |
| | | { required: true, message: "请è¾å
¥ä¸åæ ¼æ¹é", trigger: "blur" }, |
| | | ], |
| | | completedVolume: [ |
| | | { required: true, message: "请è¾å
¥å®ææ¹é", trigger: "blur" }, |
| | | ], |
| | | createBy: [{ required: true, message: "请è¾å
¥å建人", trigger: "blur" }], |
| | | }; |
| | | |
| | | // å è½½ç产订åå表 |
| | | const loadOrders = () => { |
| | | orderLoading.value = true; |
| | | productOrderListPage({ pageNum: 1, pageSize: 100 }) |
| | | .then(res => { |
| | | orderList.value = res.data.records || []; |
| | | }) |
| | | .finally(() => { |
| | | orderLoading.value = false; |
| | | }); |
| | | }; |
| | | |
| | | // å¤çç产订åéæ© |
| | | const handleOrderChange = val => { |
| | | if (val) { |
| | | const order = orderList.value.find(item => item.id === val); |
| | | if (order) { |
| | | form.orderId = val; |
| | | form.npsNo = order.npsNo; |
| | | form.materialCode = order.materialCode; |
| | | form.productName = order.productName; |
| | | form.specification = order.model; |
| | | } |
| | | // å 载工åºå表 |
| | | loadProcesses(val); |
| | | } else { |
| | | form.orderId = ""; |
| | | form.npsNo = ""; |
| | | form.materialCode = ""; |
| | | form.productName = ""; |
| | | form.specification = ""; |
| | | processId.value = ""; |
| | | activeProcessId.value = ""; |
| | | processList.value = []; |
| | | params.value = []; |
| | | form.params = {}; |
| | | } |
| | | }; |
| | | |
| | | // å 载工åºå表 |
| | | const loadProcesses = orderId => { |
| | | processLoading.value = true; |
| | | findProductProcessRouteItemList({ orderId }) |
| | | .then(res => { |
| | | processList.value = res.data || []; |
| | | // 妿æå·¥åºï¼é»è®¤éæ©ç¬¬ä¸ä¸ª |
| | | if (processList.value.length > 0) { |
| | | const firstProcess = processList.value[0]; |
| | | activeProcessId.value = firstProcess.id + ""; |
| | | processId.value = firstProcess.id; |
| | | form.processId = firstProcess.id; |
| | | // å 载第ä¸ä¸ªå·¥åºçåæ° |
| | | loadParams(firstProcess.id, orderId); |
| | | } |
| | | }) |
| | | .finally(() => { |
| | | processLoading.value = false; |
| | | }); |
| | | }; |
| | | |
| | | // å¤çtab页ç¹å» |
| | | const handleTabClick = tab => { |
| | | const selectedProcessId = parseInt(tab.paneName); |
| | | processId.value = selectedProcessId; |
| | | form.processId = selectedProcessId; |
| | | // å è½½åæ°å表 |
| | | loadParams(selectedProcessId, form.orderId); |
| | | }; |
| | | |
| | | // è·ååå
¸æ°æ® |
| | | const getDictOptions = async dictType => { |
| | | if (!dictType) return []; |
| | | if (dictOptions.value[dictType]) return dictOptions.value[dictType]; |
| | | |
| | | try { |
| | | const res = await getDicts(dictType); |
| | | if (res.code === 200) { |
| | | dictOptions.value[dictType] = res.data; |
| | | return res.data; |
| | | } |
| | | return []; |
| | | } catch (error) { |
| | | console.error("è·ååå
¸æ°æ®å¤±è´¥:", error); |
| | | return []; |
| | | } |
| | | }; |
| | | |
| | | // å è½½åæ°å表 |
| | | const loadParams = (processId, orderId) => { |
| | | findProcessParamListOrder({ orderId, routeItemId: processId }).then( |
| | | async res => { |
| | | params.value = res.data || []; |
| | | // åå§ååæ°å¼å¹¶è·ååå
¸æ°æ® |
| | | for (const param of params.value) { |
| | | if (!form.params[param.id]) { |
| | | form.params[param.id] = param.standardValue || ""; |
| | | } |
| | | // 妿æ¯åå
¸ç±»ååæ°ï¼è·ååå
¸æ°æ® |
| | | if (param.paramType == "3" && param.paramFormat) { |
| | | await getDictOptions(param.paramFormat); |
| | | } |
| | | } |
| | | } |
| | | ); |
| | | }; |
| | | |
| | | // è·åå°æ°ç²¾åº¦ |
| | | const getPrecision = format => { |
| | | if (!format) return 2; |
| | | const match = format.match(/\.(\d+)/); |
| | | return match ? parseInt(match[1].length) : 2; |
| | | }; |
| | | |
| | | // å¤çä¸ä¸æ¥ |
| | | const handleNextStep = () => { |
| | | if (activeStep.value === 0) { |
| | | // ç¬¬ä¸æ¥ï¼éªè¯ç产订åéæ© |
| | | if (!orderId.value) { |
| | | ElMessage.error("è¯·éæ©ç产订å"); |
| | | return; |
| | | } |
| | | activeStep.value = 1; |
| | | } else if (activeStep.value === 1) { |
| | | // ç¬¬äºæ¥ï¼éªè¯åºç¡ä¿¡æ¯ |
| | | formRef.value.validate(valid => { |
| | | if (valid) { |
| | | activeStep.value = 2; |
| | | } |
| | | }); |
| | | } else if (activeStep.value === 2) { |
| | | // ç¬¬ä¸æ¥ï¼ç´æ¥è¿å
¥ç¬¬åæ¥ |
| | | activeStep.value = 3; |
| | | } |
| | | }; |
| | | |
| | | // å¤çæäº¤ |
| | | const handleSubmit = () => { |
| | | formRef.value.validate(valid => { |
| | | if (valid) { |
| | | submitLoading.value = true; |
| | | // è¿éå¯ä»¥è°ç¨APIè¿è¡æäº¤ |
| | | setTimeout(() => { |
| | | ElMessage.success(props.data.id ? "ä¿®æ¹æå" : "æ°å¢æå"); |
| | | localVisible.value = false; |
| | | emit("completed"); |
| | | submitLoading.value = false; |
| | | }, 1000); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | // å¤çå
³é |
| | | const handleClose = () => { |
| | | // éç½®å°åå§ç¶æ |
| | | activeStep.value = 0; |
| | | orderId.value = ""; |
| | | processId.value = ""; |
| | | activeProcessId.value = ""; |
| | | processList.value = []; |
| | | params.value = []; |
| | | Object.assign(form, { |
| | | id: undefined, |
| | | orderId: "", |
| | | npsNo: "", |
| | | teamName: "", |
| | | materialCode: "", |
| | | productName: "", |
| | | specification: "", |
| | | outputVolume: 0, |
| | | unqualifiedVolume: 0, |
| | | completedVolume: 0, |
| | | createBy: "å½åç»å½äºº", |
| | | createTime: new Date(), |
| | | params: {}, |
| | | }); |
| | | localVisible.value = false; |
| | | }; |
| | | |
| | | // åå§å |
| | | const init = () => { |
| | | if (!props.data.id) { |
| | | // æ°å¢æ¶å 载订åå表 |
| | | loadOrders(); |
| | | // 设置é»è®¤å建人åå建æ¶é´ |
| | | form.createBy = "å½åç»å½äºº"; |
| | | form.createTime = new Date(); |
| | | } else { |
| | | // ç¼è¾æ¶å è½½ç¸å
³æ°æ® |
| | | if (props.data.orderId) { |
| | | orderId.value = props.data.orderId; |
| | | if (props.data.processId) { |
| | | processId.value = props.data.processId; |
| | | activeProcessId.value = props.data.processId + ""; |
| | | loadParams(props.data.processId, props.data.orderId); |
| | | } else { |
| | | loadProcesses(props.data.orderId); |
| | | } |
| | | } else { |
| | | loadOrders(); |
| | | } |
| | | } |
| | | }; |
| | | |
| | | // çå¬props.visibleåå |
| | | watch( |
| | | () => props.visible, |
| | | newVal => { |
| | | localVisible.value = newVal; |
| | | if (newVal) { |
| | | init(); |
| | | } |
| | | } |
| | | ); |
| | | |
| | | // çå¬localVisibleåå |
| | | watch(localVisible, newVal => { |
| | | emit("update:visible", newVal); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .dialog-footer { |
| | | text-align: right; |
| | | } |
| | | </style> |
| | |
| | | <div class="search_form"> |
| | | <el-form :model="searchForm" |
| | | :inline="true"> |
| | | <el-form-item label="æ¥å·¥äººååç§°:"> |
| | | <el-input v-model="searchForm.nickName" |
| | | <el-form-item label="ç产订åå·:"> |
| | | <el-input v-model="searchForm.orderNo" |
| | | placeholder="请è¾å
¥" |
| | | clearable |
| | | prefix-icon="Search" |
| | | style="width: 200px;" |
| | | @change="handleQuery" /> |
| | | style="width: 160px;" |
| | | @keyup.enter="handleQuery" /> |
| | | </el-form-item> |
| | | <el-form-item label="å·¥åå·:"> |
| | | <el-input v-model="searchForm.workOrderNo" |
| | | <el-form-item label="çç»:"> |
| | | <el-input v-model="searchForm.teamName" |
| | | placeholder="请è¾å
¥" |
| | | clearable |
| | | prefix-icon="Search" |
| | | style="width: 200px;" |
| | | @change="handleQuery" /> |
| | | style="width: 160px;" |
| | | @keyup.enter="handleQuery" /> |
| | | </el-form-item> |
| | | <el-form-item label="产ååç§°:"> |
| | | <el-input v-model="searchForm.productName" |
| | | placeholder="请è¾å
¥" |
| | | clearable |
| | | style="width: 160px;" |
| | | @keyup.enter="handleQuery" /> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" |
| | | @click="handleQuery">æç´¢</el-button> |
| | | <el-button type="primary" |
| | | @click="handleReset">éç½®</el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | <div> |
| | | <el-button type="primary" |
| | | @click="handleAdd">æ°å¢</el-button> |
| | | <el-button @click="handleExport">导åº</el-button> |
| | | </div> |
| | | </div> |
| | | <div class="table_list"> |
| | | <div style="text-align: right" |
| | | class="mb10"> |
| | | <!-- <el-button type="primary" |
| | | @click="openForm('add')">ç产æ¥å·¥</el-button> --> |
| | | <el-button @click="handleOut">导åº</el-button> |
| | | </div> |
| | | <PIMTable rowKey="id" |
| | | :column="tableColumn" |
| | | :tableData="tableData" |
| | | :page="page" |
| | | :isSelection="true" |
| | | :expandRowKeys="expandedRowKeys" |
| | | @expand-change="expandChange" |
| | | @selection-change="handleSelectionChange" |
| | | :tableLoading="tableLoading" |
| | | @pagination="pagination" |
| | | :total="page.total"> |
| | | <template #expand="{ row }"> |
| | | <el-table :data="expandData" |
| | | border |
| | | show-summary |
| | | :summary-method="summarizeMainTable" |
| | | v-loading="childrenLoading"> |
| | | <el-table-column align="center" |
| | | label="åºå·" |
| | | type="index" |
| | | width="60" /> |
| | | <el-table-column label="æ¬æ¬¡ç产æ°é" |
| | | prop="finishedNum" |
| | | align="center" |
| | | width="400"> |
| | | <template #default="scope"> |
| | | <el-input-number :step="0.01" |
| | | :min="0" |
| | | style="width: 100%" |
| | | v-model="scope.row.finishedNum" |
| | | :disabled="!scope.row.editType" |
| | | :precision="2" |
| | | placeholder="请è¾å
¥" |
| | | clearable |
| | | @change="changeNum(scope.row)" /> |
| | | </template> |
| | | </el-table-column> |
| | | <!-- <el-table-column label="å¾
ç产æ°é" prop="pendingNum" width="240" align="center"></el-table-column>--> |
| | | <el-table-column label="ç产人" |
| | | prop="schedulingUserId" |
| | | width="400"> |
| | | <template #default="scope"> |
| | | <el-select v-model="scope.row.schedulingUserId" |
| | | placeholder="éæ©äººå" |
| | | :disabled="!scope.row.editType" |
| | | style="width: 100%;"> |
| | | <el-option v-for="user in userList" |
| | | :key="user.userId" |
| | | :label="user.nickName" |
| | | :value="user.userId" /> |
| | | </el-select> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="çäº§æ¥æ" |
| | | prop="schedulingDate" |
| | | width="400"> |
| | | <template #default="scope"> |
| | | <el-date-picker v-model="scope.row.schedulingDate" |
| | | type="date" |
| | | :disabled="!scope.row.editType" |
| | | placeholder="è¯·éæ©æ¥æ" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | clearable |
| | | style="width: 100%" /> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="æä½" |
| | | > |
| | | <template #default="scope"> |
| | | <el-button link |
| | | type="primary" |
| | | size="small" |
| | | @click="changeEditType(scope.row)" |
| | | v-if="!scope.row.editType" |
| | | :disabled="scope.row.parentStatus === 3">ç¼è¾</el-button> |
| | | <el-button link |
| | | type="primary" |
| | | size="small" |
| | | @click="saveReceiptPayment(scope.row)" |
| | | v-if="scope.row.editType">ä¿å</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | :isSelection="false" |
| | | @selection-change="handleSelectionChange" |
| | | @pagination="pagination"> |
| | | <template #outputVolume="{ row }"> |
| | | <span style="font-weight: bold;color: #409eff;">{{ row.outputVolume }}</span><span style="margin-left: 5px;color: #909399;">æ¹</span> |
| | | </template> |
| | | <template #unqualifiedVolume="{ row }"> |
| | | <span style="font-weight: bold;color: #b43434;">{{ row.unqualifiedVolume }}</span><span style="margin-left: 5px;color: #909399;">æ¹</span> |
| | | </template> |
| | | <template #completedVolume="{ row }"> |
| | | <span style="font-weight: bold;color: #28e431;">{{ row.completedVolume }}</span><span style="margin-left: 5px;color: #909399;">æ¹</span> |
| | | </template> |
| | | </PIMTable> |
| | | </div> |
| | | <form-dia ref="formDia" |
| | | @close="handleQuery"></form-dia> |
| | | <input-modal v-if="isShowInput" |
| | | v-model:visible="isShowInput" |
| | | :production-product-main-id="isShowingId" /> |
| | | <ReportingDialog v-model:visible="dialogVisible" |
| | | :data="form" |
| | | @completed="handleQuery" /> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { onMounted, ref } from "vue"; |
| | | import FormDia from "@/views/productionManagement/productionReporting/components/formDia.vue"; |
| | | import { ElMessageBox } from "element-plus"; |
| | | import { onMounted, ref, reactive, getCurrentInstance } from "vue"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | import dayjs from "dayjs"; |
| | | import { |
| | | workListPage, |
| | | productionReport, |
| | | productionReportUpdate, |
| | | workListPageById, |
| | | productionReportDelete, |
| | | } from "@/api/productionManagement/productionReporting.js"; |
| | | import { productionProductMainListPage } from "@/api/productionManagement/productionProductMain.js"; |
| | | import { userListNoPageByTenantId } from "@/api/system/user.js"; |
| | | import InputModal from "@/views/productionManagement/productionReporting/Input.vue"; |
| | | import PIMTable from "@/components/PIMTable/PIMTable.vue"; |
| | | import ReportingDialog from "./components/ReportingDialog.vue"; |
| | | |
| | | const data = reactive({ |
| | | searchForm: { |
| | | nickName: "", |
| | | workOrderNo: "", |
| | | workOrderStatus: "", |
| | | }, |
| | | }); |
| | | const { searchForm } = toRefs(data); |
| | | const expandedRowKeys = ref([]); |
| | | const expandData = ref([]); |
| | | const userList = ref([]); |
| | | const { proxy } = getCurrentInstance(); |
| | | |
| | | const tableColumn = ref([ |
| | | { |
| | | label: "æ¥å·¥åå·", |
| | | prop: "productNo", |
| | | width: 120, |
| | | label: "ç产订åå·", |
| | | prop: "orderNo", |
| | | width: "150px", |
| | | }, |
| | | { |
| | | label: "æ¥å·¥äººå", |
| | | prop: "nickName", |
| | | width: 120, |
| | | label: "çç»", |
| | | prop: "teamName", |
| | | width: "120px", |
| | | dataType: "tag", |
| | | }, |
| | | { |
| | | label: "å·¥åº", |
| | | prop: "process", |
| | | width: 120, |
| | | }, |
| | | { |
| | | label: "å·¥åç¼å·", |
| | | prop: "workOrderNo", |
| | | width: 120, |
| | | }, |
| | | { |
| | | label: "éå®ååå·", |
| | | prop: "salesContractNo", |
| | | width: 120, |
| | | label: "产åç¼ç ", |
| | | prop: "materialCode", |
| | | width: "150px", |
| | | }, |
| | | { |
| | | label: "产ååç§°", |
| | | prop: "productName", |
| | | width: 120, |
| | | width: "150px", |
| | | }, |
| | | { |
| | | label: "产åè§æ ¼åå·", |
| | | prop: "productModelName", |
| | | width: 120, |
| | | label: "è§æ ¼", |
| | | prop: "specification", |
| | | width: "120px", |
| | | className: "specification-cell", |
| | | }, |
| | | { |
| | | label: "äº§åºæ°é", |
| | | prop: "quantity", |
| | | width: 120, |
| | | label: "äº§åºæ¹é", |
| | | prop: "outputVolume", |
| | | width: "120px", |
| | | align: "right", |
| | | dataType: "slot", |
| | | slot: "outputVolume", |
| | | }, |
| | | { |
| | | label: "æ¥åºæ°é", |
| | | prop: "scrapQty", |
| | | width: 120, |
| | | label: "ä¸åæ ¼æ¹é", |
| | | prop: "unqualifiedVolume", |
| | | width: "120px", |
| | | align: "right", |
| | | dataType: "slot", |
| | | slot: "unqualifiedVolume", |
| | | }, |
| | | { |
| | | label: "åä½", |
| | | prop: "unit", |
| | | width: 120, |
| | | label: "宿æ¹é", |
| | | prop: "completedVolume", |
| | | width: "120px", |
| | | align: "right", |
| | | dataType: "slot", |
| | | slot: "completedVolume", |
| | | }, |
| | | |
| | | { |
| | | label: "å建人", |
| | | prop: "createBy", |
| | | width: "120px", |
| | | dataType: "tag", |
| | | }, |
| | | { |
| | | label: "å建æ¶é´", |
| | | prop: "createTime", |
| | | width: 120, |
| | | width: "160px", |
| | | formatData: val => (val ? dayjs(val).format("YYYY-MM-DD HH:mm:ss") : ""), |
| | | }, |
| | | { |
| | | dataType: "action", |
| | | label: "æä½", |
| | | align: "center", |
| | | dataType: "action", |
| | | width: "200px", |
| | | fixed: "right", |
| | | operation: [ |
| | | { |
| | | name: "æ¥çæå
¥", |
| | | name: "详æ
", |
| | | type: "text", |
| | | clickFun: row => { |
| | | showInput(row); |
| | | handleDetail(row); |
| | | }, |
| | | }, |
| | | { |
| | | name: "ç¼è¾", |
| | | type: "text", |
| | | clickFun: row => { |
| | | handleEdit(row); |
| | | }, |
| | | }, |
| | | { |
| | | name: "å é¤", |
| | | type: "danger", |
| | | type: "text", |
| | | clickFun: row => { |
| | | deleteReport(row); |
| | | handleDelete(row); |
| | | }, |
| | | }, |
| | | ], |
| | | }, |
| | | ]); |
| | | |
| | | const tableData = ref([]); |
| | | const selectedRows = ref([]); |
| | | const tableLoading = ref(false); |
| | | const childrenLoading = ref(false); |
| | | const page = reactive({ |
| | | current: 1, |
| | | size: 100, |
| | | size: 10, |
| | | total: 0, |
| | | }); |
| | | const formDia = ref(); |
| | | const { proxy } = getCurrentInstance(); |
| | | |
| | | // æ¥è¯¢å表 |
| | | /** æç´¢æé®æä½ */ |
| | | const searchForm = reactive({ |
| | | orderNo: "", |
| | | teamName: "", |
| | | productName: "", |
| | | }); |
| | | |
| | | const mockData = [ |
| | | { |
| | | id: 1, |
| | | orderNo: "PO202401001", |
| | | teamName: "ç产ä¸ç»", |
| | | materialCode: "PC001", |
| | | productName: "æ åç å", |
| | | specification: "600Ã240Ã200", |
| | | outputVolume: 120.5, |
| | | unqualifiedVolume: 2.3, |
| | | completedVolume: 118.2, |
| | | createBy: "å¼ ä¸", |
| | | createTime: "2024-01-15 08:30:00", |
| | | }, |
| | | { |
| | | id: 2, |
| | | orderNo: "PO202401002", |
| | | teamName: "ç产äºç»", |
| | | materialCode: "PC002", |
| | | productName: "æ åç å", |
| | | specification: "600Ã240Ã200", |
| | | outputVolume: 150.8, |
| | | unqualifiedVolume: 1.5, |
| | | completedVolume: 149.3, |
| | | createBy: "æå", |
| | | createTime: "2024-01-15 09:15:00", |
| | | }, |
| | | { |
| | | id: 3, |
| | | orderNo: "PO202401003", |
| | | teamName: "ç产ä¸ç»", |
| | | materialCode: "PC003", |
| | | productName: "å æ°ç å", |
| | | specification: "600Ã240Ã250", |
| | | outputVolume: 95.2, |
| | | unqualifiedVolume: 0.8, |
| | | completedVolume: 94.4, |
| | | createBy: "çäº", |
| | | createTime: "2024-01-15 10:00:00", |
| | | }, |
| | | { |
| | | id: 4, |
| | | orderNo: "PO202401004", |
| | | teamName: "ç产ä¸ç»", |
| | | materialCode: "PC004", |
| | | productName: "æ åç å", |
| | | specification: "600Ã240Ã200", |
| | | outputVolume: 180.6, |
| | | unqualifiedVolume: 3.2, |
| | | completedVolume: 177.4, |
| | | createBy: "èµµå
", |
| | | createTime: "2024-01-15 14:20:00", |
| | | }, |
| | | { |
| | | id: 5, |
| | | orderNo: "PO202401005", |
| | | teamName: "ç产äºç»", |
| | | materialCode: "PC005", |
| | | productName: "å æ°ç å", |
| | | specification: "600Ã240Ã250", |
| | | outputVolume: 110.3, |
| | | unqualifiedVolume: 1.1, |
| | | completedVolume: 109.2, |
| | | createBy: "åä¸", |
| | | createTime: "2024-01-15 15:45:00", |
| | | }, |
| | | { |
| | | id: 6, |
| | | orderNo: "PO202401006", |
| | | teamName: "ç产ä¸ç»", |
| | | materialCode: "PC006", |
| | | productName: "æ åç å", |
| | | specification: "600Ã240Ã200", |
| | | outputVolume: 135.7, |
| | | unqualifiedVolume: 2.5, |
| | | completedVolume: 133.2, |
| | | createBy: "å¨å
«", |
| | | createTime: "2024-01-16 08:00:00", |
| | | }, |
| | | { |
| | | id: 7, |
| | | orderNo: "PO202401007", |
| | | teamName: "ç产ä¸ç»", |
| | | materialCode: "PC007", |
| | | productName: "å æ°ç å", |
| | | specification: "600Ã240Ã250", |
| | | outputVolume: 88.4, |
| | | unqualifiedVolume: 0.6, |
| | | completedVolume: 87.8, |
| | | createBy: "å´ä¹", |
| | | createTime: "2024-01-16 09:30:00", |
| | | }, |
| | | { |
| | | id: 8, |
| | | orderNo: "PO202401008", |
| | | teamName: "ç产äºç»", |
| | | materialCode: "PC008", |
| | | productName: "æ åç å", |
| | | specification: "600Ã240Ã200", |
| | | outputVolume: 165.2, |
| | | unqualifiedVolume: 2.8, |
| | | completedVolume: 162.4, |
| | | createBy: "éå", |
| | | createTime: "2024-01-16 11:00:00", |
| | | }, |
| | | { |
| | | id: 9, |
| | | orderNo: "PO202401009", |
| | | teamName: "ç产ä¸ç»", |
| | | materialCode: "PC009", |
| | | productName: "å æ°ç å", |
| | | specification: "600Ã240Ã250", |
| | | outputVolume: 102.5, |
| | | unqualifiedVolume: 1.3, |
| | | completedVolume: 101.2, |
| | | createBy: "é±åä¸", |
| | | createTime: "2024-01-16 13:15:00", |
| | | }, |
| | | { |
| | | id: 10, |
| | | orderNo: "PO202401010", |
| | | teamName: "ç产ä¸ç»", |
| | | materialCode: "PC010", |
| | | productName: "æ åç å", |
| | | specification: "600Ã240Ã200", |
| | | outputVolume: 142.8, |
| | | unqualifiedVolume: 2.1, |
| | | completedVolume: 140.7, |
| | | createBy: "ååäº", |
| | | createTime: "2024-01-16 15:00:00", |
| | | }, |
| | | ]; |
| | | |
| | | const dialogVisible = ref(false); |
| | | const form = reactive({ |
| | | id: undefined, |
| | | orderId: "", |
| | | orderNo: "", |
| | | teamName: "", |
| | | materialCode: "", |
| | | productName: "", |
| | | specification: "", |
| | | outputVolume: 0, |
| | | unqualifiedVolume: 0, |
| | | completedVolume: 0, |
| | | processId: "", |
| | | params: {}, |
| | | }); |
| | | |
| | | const selectedRows = ref([]); |
| | | |
| | | const getList = () => { |
| | | tableLoading.value = true; |
| | | setTimeout(() => { |
| | | tableLoading.value = false; |
| | | const start = (page.current - 1) * page.size; |
| | | const end = start + page.size; |
| | | tableData.value = mockData.slice(start, end); |
| | | page.total = mockData.length; |
| | | }, 500); |
| | | }; |
| | | |
| | | const handleQuery = () => { |
| | | page.current = 1; |
| | | getList(); |
| | | }; |
| | | const changeDaterange = value => { |
| | | if (value) { |
| | | searchForm.value.entryDateStart = value[0]; |
| | | searchForm.value.entryDateEnd = value[1]; |
| | | } else { |
| | | searchForm.value.entryDateStart = undefined; |
| | | searchForm.value.entryDateEnd = undefined; |
| | | } |
| | | handleQuery(); |
| | | |
| | | const handleReset = () => { |
| | | searchForm.orderNo = ""; |
| | | searchForm.teamName = ""; |
| | | searchForm.productName = ""; |
| | | page.current = 1; |
| | | getList(); |
| | | }; |
| | | const deleteReport = row => { |
| | | ElMessageBox.confirm("ç¡®å®å é¤è¯¥æ¥å·¥åï¼", "æç¤º", { |
| | | confirmButtonText: "ç¡®å®", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }).then(() => { |
| | | productionReportDelete({ id: row.id }).then(res => { |
| | | if (res.code === 200) { |
| | | proxy.$modal.msgSuccess("å 餿å"); |
| | | getList(); |
| | | } else { |
| | | ElMessageBox.alert(res.msg || "å é¤å¤±è´¥", "æç¤º", { |
| | | confirmButtonText: "ç¡®å®", |
| | | }); |
| | | } |
| | | }); |
| | | }); |
| | | }; |
| | | |
| | | const pagination = obj => { |
| | | page.current = obj.page; |
| | | page.size = obj.limit; |
| | | getList(); |
| | | }; |
| | | const getList = () => { |
| | | tableLoading.value = true; |
| | | const params = { ...searchForm.value, ...page }; |
| | | params.entryDate = undefined; |
| | | expandedRowKeys.value = []; |
| | | productionProductMainListPage(params) |
| | | .then(res => { |
| | | tableLoading.value = false; |
| | | tableData.value = res.data.records.map(item => ({ |
| | | ...item, |
| | | pendingFinishNum: |
| | | (Number(item.schedulingNum) || 0) - (Number(item.finishedNum) || 0), |
| | | })); |
| | | page.total = res.data.total; |
| | | }) |
| | | .catch(err => { |
| | | tableLoading.value = false; |
| | | }); |
| | | }; |
| | | // å±å¼è¡ |
| | | const expandChange = (row, expandedRows) => { |
| | | userListNoPageByTenantId().then(res => { |
| | | userList.value = res.data; |
| | | }); |
| | | if (expandedRows.length > 0) { |
| | | nextTick(() => { |
| | | expandedRowKeys.value = []; |
| | | try { |
| | | childrenLoading.value = true; |
| | | workListPageById({ id: row.id }).then(res => { |
| | | childrenLoading.value = false; |
| | | const index = tableData.value.findIndex(item => item.id === row.id); |
| | | if (index > -1) { |
| | | expandData.value = res.data.map(item => ({ |
| | | ...item, |
| | | pendingNum: |
| | | (Number(item.schedulingNum) || 0) - |
| | | (Number(item.finishedNum) || 0), |
| | | parentStatus: row.status, // æ°å¢ç¶è¡¨ç¶æ |
| | | })); |
| | | } |
| | | expandedRowKeys.value.push(row.id); |
| | | }); |
| | | } catch (error) { |
| | | childrenLoading.value = false; |
| | | console.log(error); |
| | | } |
| | | }); |
| | | } else { |
| | | expandedRowKeys.value = []; |
| | | } |
| | | }; |
| | | const changeNum = row => { |
| | | // æ¾å°ç¶è¡¨æ ¼æ°æ® |
| | | const parentRow = tableData.value.find( |
| | | item => item.id === expandedRowKeys.value[0] |
| | | ); |
| | | // è®¡ç®ææåè¡¨æ ¼ finishedNum çæ»å |
| | | const totalFinishedNum = expandData.value.reduce( |
| | | (sum, item) => sum + (Number(item.finishedNum) || 0), |
| | | 0 |
| | | ); |
| | | // ç¶è¡¨æ ¼çæäº§æ°é |
| | | const schedulingNum = parentRow ? Number(parentRow.schedulingNum) : 0; |
| | | |
| | | if (totalFinishedNum > schedulingNum) { |
| | | // åéæ¬æ¬¡è¾å
¥ |
| | | row.finishedNum = |
| | | schedulingNum - (totalFinishedNum - Number(row.finishedNum)); |
| | | proxy.$modal.msgWarning("æææ¬æ¬¡ç产æ°éä¹åä¸å¯å¤§äºæäº§æ°é"); |
| | | } |
| | | row.pendingNum = row.schedulingNum - row.finishedNum; |
| | | }; |
| | | // ç¼è¾ä¿®æ¹ç¶æ |
| | | const changeEditType = row => { |
| | | row.editType = !row.editType; |
| | | }; |
| | | // ä¿åè®°å½ |
| | | const saveReceiptPayment = row => { |
| | | productionReportUpdate(row).then(res => { |
| | | row.editType = !row.editType; |
| | | getList(); |
| | | proxy.$modal.msgSuccess("æäº¤æå"); |
| | | }); |
| | | }; |
| | | // è¡¨æ ¼éæ©æ°æ® |
| | | const handleSelectionChange = selection => { |
| | | selectedRows.value = selection; |
| | | }; |
| | | const summarizeMainTable = param => { |
| | | return proxy.summarizeTable(param, ["finishedNum"]); |
| | | }; |
| | | // æå¼å¼¹æ¡ |
| | | const openForm = (type, row) => { |
| | | if (selectedRows.value.length !== 1) { |
| | | proxy.$message.error("è¯·éæ©ä¸æ¡æ°æ®"); |
| | | return; |
| | | } |
| | | if (selectedRows.value[0].pendingFinishNum == 0) { |
| | | proxy.$message.warning("æ é忥工"); |
| | | return; |
| | | } |
| | | nextTick(() => { |
| | | const rowInfo = type === "add" ? selectedRows.value[0] : row; |
| | | formDia.value?.openDialog(type, rowInfo); |
| | | |
| | | const handleAdd = () => { |
| | | Object.assign(form, { |
| | | id: undefined, |
| | | orderId: "", |
| | | orderNo: "", |
| | | teamName: "", |
| | | materialCode: "", |
| | | productName: "", |
| | | specification: "", |
| | | outputVolume: 0, |
| | | unqualifiedVolume: 0, |
| | | completedVolume: 0, |
| | | processId: "", |
| | | params: {}, |
| | | }); |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | // æå¼æå
¥æ¨¡ææ¡ |
| | | const isShowInput = ref(false); |
| | | const isShowingId = ref(0); |
| | | const showInput = row => { |
| | | isShowInput.value = true; |
| | | isShowingId.value = row.id; |
| | | const handleEdit = row => { |
| | | Object.assign(form, { |
| | | id: row.id, |
| | | orderId: row.orderId || "", |
| | | orderNo: row.orderNo, |
| | | teamName: row.teamName, |
| | | materialCode: row.materialCode, |
| | | productName: row.productName, |
| | | specification: row.specification, |
| | | outputVolume: row.outputVolume, |
| | | unqualifiedVolume: row.unqualifiedVolume, |
| | | completedVolume: row.completedVolume, |
| | | createBy: row.createBy || "", |
| | | createTime: row.createTime || new Date(), |
| | | processId: row.processId || "", |
| | | params: row.params || {}, |
| | | }); |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | // å¯¼åº |
| | | const handleOut = () => { |
| | | ElMessageBox.confirm("éä¸çå
容å°è¢«å¯¼åºï¼æ¯å¦ç¡®è®¤å¯¼åºï¼", "导åº", { |
| | | confirmButtonText: "确认", |
| | | const handleDetail = row => { |
| | | ElMessage.info("详æ
åè½å¾
å®ç°"); |
| | | }; |
| | | |
| | | const handleDelete = row => { |
| | | ElMessageBox.confirm("确认å é¤è¯¥æ¡æ°æ®å?", "æç¤º", { |
| | | confirmButtonText: "ç¡®å®", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | proxy.download("/productionProductMain/export", {}, "ç产æ¥å·¥.xlsx"); |
| | | productionReportDelete({ id: row.id }) |
| | | .then(() => { |
| | | ElMessage.success("å 餿å"); |
| | | getList(); |
| | | }) |
| | | .catch(() => { |
| | | ElMessage.error("å é¤å¤±è´¥"); |
| | | }); |
| | | }) |
| | | .catch(() => { |
| | | proxy.$modal.msg("已忶"); |
| | | }); |
| | | .catch(() => {}); |
| | | }; |
| | | |
| | | const handleExport = () => { |
| | | ElMessage.info("导åºåè½å¾
å®ç°"); |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | getList(); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped></style> |
| | | <style scoped lang="scss"> |
| | | .app-container { |
| | | padding: 24px; |
| | | background-color: #f0f2f5; |
| | | min-height: calc(100vh - 48px); |
| | | } |
| | | |
| | | .search_form { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: flex-start; |
| | | flex-wrap: wrap; |
| | | gap: 16px; |
| | | margin-bottom: 24px; |
| | | padding: 20px; |
| | | background-color: #ffffff; |
| | | border-radius: 6px; |
| | | box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05); |
| | | transition: all 0.3s ease; |
| | | |
| | | &:hover { |
| | | box-shadow: 0 4px 16px 0 rgba(0, 0, 0, 0.08); |
| | | } |
| | | |
| | | :deep(.el-form) { |
| | | display: flex; |
| | | flex-wrap: wrap; |
| | | gap: 0; |
| | | |
| | | .el-form-item { |
| | | margin-right: 16px; |
| | | margin-bottom: 0; |
| | | |
| | | &:last-child { |
| | | margin-right: 0; |
| | | } |
| | | } |
| | | } |
| | | |
| | | > div { |
| | | display: flex; |
| | | flex-wrap: wrap; |
| | | gap: 8px; |
| | | flex-shrink: 0; |
| | | } |
| | | } |
| | | |
| | | .table_list { |
| | | background-color: #ffffff; |
| | | border-radius: 6px; |
| | | box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05); |
| | | overflow: hidden; |
| | | height: calc(100vh - 220px); |
| | | } |
| | | |
| | | :deep(.el-table) { |
| | | border: none; |
| | | border-radius: 6px; |
| | | overflow: hidden; |
| | | |
| | | .el-table__header-wrapper { |
| | | background-color: #fafafa; |
| | | |
| | | th { |
| | | background-color: #fafafa; |
| | | font-weight: 600; |
| | | color: #303133; |
| | | border-bottom: 1px solid #ebeef5; |
| | | padding: 14px 0; |
| | | } |
| | | } |
| | | |
| | | .el-table__body-wrapper { |
| | | tr { |
| | | transition: all 0.3s ease; |
| | | |
| | | &:hover { |
| | | background-color: #f5f7fa; |
| | | } |
| | | |
| | | td { |
| | | border-bottom: 1px solid #ebeef5; |
| | | padding: 12px 0; |
| | | } |
| | | } |
| | | |
| | | tr.current-row { |
| | | background-color: #ecf5ff; |
| | | } |
| | | } |
| | | |
| | | .el-table__empty-block { |
| | | padding: 40px 0; |
| | | } |
| | | } |
| | | </style> |
| | | <style lang="scss"> |
| | | .specification-cell { |
| | | color: #7a7d81; |
| | | font-style: italic; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <div class="search_form"> |
| | | <el-form :model="searchForm" |
| | | :inline="true"> |
| | | <el-form-item label="æ¥å·¥äººååç§°:"> |
| | | <el-input v-model="searchForm.nickName" |
| | | placeholder="请è¾å
¥" |
| | | clearable |
| | | prefix-icon="Search" |
| | | style="width: 200px;" |
| | | @change="handleQuery" /> |
| | | </el-form-item> |
| | | <el-form-item label="å·¥åå·:"> |
| | | <el-input v-model="searchForm.workOrderNo" |
| | | placeholder="请è¾å
¥" |
| | | clearable |
| | | prefix-icon="Search" |
| | | style="width: 200px;" |
| | | @change="handleQuery" /> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" |
| | | @click="handleQuery">æç´¢</el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | </div> |
| | | <div class="table_list"> |
| | | <div style="text-align: right" |
| | | class="mb10"> |
| | | <!-- <el-button type="primary" |
| | | @click="openForm('add')">ç产æ¥å·¥</el-button> --> |
| | | <el-button @click="handleOut">导åº</el-button> |
| | | </div> |
| | | <PIMTable rowKey="id" |
| | | :column="tableColumn" |
| | | :tableData="tableData" |
| | | :page="page" |
| | | :isSelection="true" |
| | | :expandRowKeys="expandedRowKeys" |
| | | @expand-change="expandChange" |
| | | @selection-change="handleSelectionChange" |
| | | :tableLoading="tableLoading" |
| | | @pagination="pagination" |
| | | :total="page.total"> |
| | | <template #expand="{ row }"> |
| | | <el-table :data="expandData" |
| | | border |
| | | show-summary |
| | | :summary-method="summarizeMainTable" |
| | | v-loading="childrenLoading"> |
| | | <el-table-column align="center" |
| | | label="åºå·" |
| | | type="index" |
| | | width="60" /> |
| | | <el-table-column label="æ¬æ¬¡ç产æ°é" |
| | | prop="finishedNum" |
| | | align="center" |
| | | width="400"> |
| | | <template #default="scope"> |
| | | <el-input-number :step="0.01" |
| | | :min="0" |
| | | style="width: 100%" |
| | | v-model="scope.row.finishedNum" |
| | | :disabled="!scope.row.editType" |
| | | :precision="2" |
| | | placeholder="请è¾å
¥" |
| | | clearable |
| | | @change="changeNum(scope.row)" /> |
| | | </template> |
| | | </el-table-column> |
| | | <!-- <el-table-column label="å¾
ç产æ°é" prop="pendingNum" width="240" align="center"></el-table-column>--> |
| | | <el-table-column label="ç产人" |
| | | prop="schedulingUserId" |
| | | width="400"> |
| | | <template #default="scope"> |
| | | <el-select v-model="scope.row.schedulingUserId" |
| | | placeholder="éæ©äººå" |
| | | :disabled="!scope.row.editType" |
| | | style="width: 100%;"> |
| | | <el-option v-for="user in userList" |
| | | :key="user.userId" |
| | | :label="user.nickName" |
| | | :value="user.userId" /> |
| | | </el-select> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="çäº§æ¥æ" |
| | | prop="schedulingDate" |
| | | width="400"> |
| | | <template #default="scope"> |
| | | <el-date-picker v-model="scope.row.schedulingDate" |
| | | type="date" |
| | | :disabled="!scope.row.editType" |
| | | placeholder="è¯·éæ©æ¥æ" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | clearable |
| | | style="width: 100%" /> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="æä½" |
| | | > |
| | | <template #default="scope"> |
| | | <el-button link |
| | | type="primary" |
| | | size="small" |
| | | @click="changeEditType(scope.row)" |
| | | v-if="!scope.row.editType" |
| | | :disabled="scope.row.parentStatus === 3">ç¼è¾</el-button> |
| | | <el-button link |
| | | type="primary" |
| | | size="small" |
| | | @click="saveReceiptPayment(scope.row)" |
| | | v-if="scope.row.editType">ä¿å</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </template> |
| | | </PIMTable> |
| | | </div> |
| | | <form-dia ref="formDia" |
| | | @close="handleQuery"></form-dia> |
| | | <input-modal v-if="isShowInput" |
| | | v-model:visible="isShowInput" |
| | | :production-product-main-id="isShowingId" /> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { onMounted, ref } from "vue"; |
| | | import FormDia from "@/views/productionManagement/productionReporting/components/formDia.vue"; |
| | | import { ElMessageBox } from "element-plus"; |
| | | import { |
| | | productionReportUpdate, |
| | | workListPageById, |
| | | productionReportDelete, |
| | | } from "@/api/productionManagement/productionReporting.js"; |
| | | import { productionProductMainListPage } from "@/api/productionManagement/productionProductMain.js"; |
| | | import { userListNoPageByTenantId } from "@/api/system/user.js"; |
| | | import InputModal from "@/views/productionManagement/productionReporting/Input.vue"; |
| | | |
| | | const data = reactive({ |
| | | searchForm: { |
| | | nickName: "", |
| | | workOrderNo: "", |
| | | workOrderStatus: "", |
| | | }, |
| | | }); |
| | | const { searchForm } = toRefs(data); |
| | | const expandedRowKeys = ref([]); |
| | | const expandData = ref([]); |
| | | const userList = ref([]); |
| | | const tableColumn = ref([ |
| | | { |
| | | label: "æ¥å·¥åå·", |
| | | prop: "productNo", |
| | | width: 120, |
| | | }, |
| | | { |
| | | label: "æ¥å·¥äººå", |
| | | prop: "nickName", |
| | | width: 120, |
| | | }, |
| | | { |
| | | label: "å·¥åº", |
| | | prop: "process", |
| | | width: 120, |
| | | }, |
| | | { |
| | | label: "å·¥åç¼å·", |
| | | prop: "workOrderNo", |
| | | width: 120, |
| | | }, |
| | | { |
| | | label: "éå®ååå·", |
| | | prop: "salesContractNo", |
| | | width: 120, |
| | | }, |
| | | { |
| | | label: "产ååç§°", |
| | | prop: "productName", |
| | | width: 120, |
| | | }, |
| | | { |
| | | label: "产åè§æ ¼åå·", |
| | | prop: "productModelName", |
| | | width: 120, |
| | | }, |
| | | { |
| | | label: "äº§åºæ°é", |
| | | prop: "quantity", |
| | | width: 120, |
| | | }, |
| | | { |
| | | label: "æ¥åºæ°é", |
| | | prop: "scrapQty", |
| | | width: 120, |
| | | }, |
| | | { |
| | | label: "åä½", |
| | | prop: "unit", |
| | | width: 120, |
| | | }, |
| | | |
| | | { |
| | | label: "å建æ¶é´", |
| | | prop: "createTime", |
| | | width: 120, |
| | | }, |
| | | { |
| | | dataType: "action", |
| | | label: "æä½", |
| | | align: "center", |
| | | fixed: "right", |
| | | operation: [ |
| | | { |
| | | name: "æ¥çæå
¥", |
| | | type: "text", |
| | | clickFun: row => { |
| | | showInput(row); |
| | | }, |
| | | }, |
| | | { |
| | | name: "å é¤", |
| | | type: "danger", |
| | | clickFun: row => { |
| | | deleteReport(row); |
| | | }, |
| | | }, |
| | | ], |
| | | }, |
| | | ]); |
| | | const tableData = ref([]); |
| | | const selectedRows = ref([]); |
| | | const tableLoading = ref(false); |
| | | const childrenLoading = ref(false); |
| | | const page = reactive({ |
| | | current: 1, |
| | | size: 100, |
| | | total: 0, |
| | | }); |
| | | const formDia = ref(); |
| | | const { proxy } = getCurrentInstance(); |
| | | |
| | | // æ¥è¯¢å表 |
| | | /** æç´¢æé®æä½ */ |
| | | const handleQuery = () => { |
| | | page.current = 1; |
| | | getList(); |
| | | }; |
| | | const changeDaterange = value => { |
| | | if (value) { |
| | | searchForm.value.entryDateStart = value[0]; |
| | | searchForm.value.entryDateEnd = value[1]; |
| | | } else { |
| | | searchForm.value.entryDateStart = undefined; |
| | | searchForm.value.entryDateEnd = undefined; |
| | | } |
| | | handleQuery(); |
| | | }; |
| | | const deleteReport = row => { |
| | | ElMessageBox.confirm("ç¡®å®å é¤è¯¥æ¥å·¥åï¼", "æç¤º", { |
| | | confirmButtonText: "ç¡®å®", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }).then(() => { |
| | | productionReportDelete({ id: row.id }).then(res => { |
| | | if (res.code === 200) { |
| | | proxy.$modal.msgSuccess("å 餿å"); |
| | | getList(); |
| | | } else { |
| | | ElMessageBox.alert(res.msg || "å é¤å¤±è´¥", "æç¤º", { |
| | | confirmButtonText: "ç¡®å®", |
| | | }); |
| | | } |
| | | }); |
| | | }); |
| | | }; |
| | | const pagination = obj => { |
| | | page.current = obj.page; |
| | | page.size = obj.limit; |
| | | getList(); |
| | | }; |
| | | const getList = () => { |
| | | tableLoading.value = true; |
| | | const params = { ...searchForm.value, ...page }; |
| | | params.entryDate = undefined; |
| | | expandedRowKeys.value = []; |
| | | productionProductMainListPage(params) |
| | | .then(res => { |
| | | tableLoading.value = false; |
| | | tableData.value = res.data.records.map(item => ({ |
| | | ...item, |
| | | pendingFinishNum: |
| | | (Number(item.schedulingNum) || 0) - (Number(item.finishedNum) || 0), |
| | | })); |
| | | page.total = res.data.total; |
| | | }) |
| | | .catch(err => { |
| | | tableLoading.value = false; |
| | | }); |
| | | }; |
| | | // å±å¼è¡ |
| | | const expandChange = (row, expandedRows) => { |
| | | userListNoPageByTenantId().then(res => { |
| | | userList.value = res.data; |
| | | }); |
| | | if (expandedRows.length > 0) { |
| | | nextTick(() => { |
| | | expandedRowKeys.value = []; |
| | | try { |
| | | childrenLoading.value = true; |
| | | workListPageById({ id: row.id }).then(res => { |
| | | childrenLoading.value = false; |
| | | const index = tableData.value.findIndex(item => item.id === row.id); |
| | | if (index > -1) { |
| | | expandData.value = res.data.map(item => ({ |
| | | ...item, |
| | | pendingNum: |
| | | (Number(item.schedulingNum) || 0) - |
| | | (Number(item.finishedNum) || 0), |
| | | parentStatus: row.status, // æ°å¢ç¶è¡¨ç¶æ |
| | | })); |
| | | } |
| | | expandedRowKeys.value.push(row.id); |
| | | }); |
| | | } catch (error) { |
| | | childrenLoading.value = false; |
| | | console.log(error); |
| | | } |
| | | }); |
| | | } else { |
| | | expandedRowKeys.value = []; |
| | | } |
| | | }; |
| | | const changeNum = row => { |
| | | // æ¾å°ç¶è¡¨æ ¼æ°æ® |
| | | const parentRow = tableData.value.find( |
| | | item => item.id === expandedRowKeys.value[0] |
| | | ); |
| | | // è®¡ç®ææåè¡¨æ ¼ finishedNum çæ»å |
| | | const totalFinishedNum = expandData.value.reduce( |
| | | (sum, item) => sum + (Number(item.finishedNum) || 0), |
| | | 0 |
| | | ); |
| | | // ç¶è¡¨æ ¼çæäº§æ°é |
| | | const schedulingNum = parentRow ? Number(parentRow.schedulingNum) : 0; |
| | | |
| | | if (totalFinishedNum > schedulingNum) { |
| | | // åéæ¬æ¬¡è¾å
¥ |
| | | row.finishedNum = |
| | | schedulingNum - (totalFinishedNum - Number(row.finishedNum)); |
| | | proxy.$modal.msgWarning("æææ¬æ¬¡ç产æ°éä¹åä¸å¯å¤§äºæäº§æ°é"); |
| | | } |
| | | row.pendingNum = row.schedulingNum - row.finishedNum; |
| | | }; |
| | | // ç¼è¾ä¿®æ¹ç¶æ |
| | | const changeEditType = row => { |
| | | row.editType = !row.editType; |
| | | }; |
| | | // ä¿åè®°å½ |
| | | const saveReceiptPayment = row => { |
| | | productionReportUpdate(row).then(res => { |
| | | row.editType = !row.editType; |
| | | getList(); |
| | | proxy.$modal.msgSuccess("æäº¤æå"); |
| | | }); |
| | | }; |
| | | // è¡¨æ ¼éæ©æ°æ® |
| | | const handleSelectionChange = selection => { |
| | | selectedRows.value = selection; |
| | | }; |
| | | const summarizeMainTable = param => { |
| | | return proxy.summarizeTable(param, ["finishedNum"]); |
| | | }; |
| | | // æå¼å¼¹æ¡ |
| | | const openForm = (type, row) => { |
| | | if (selectedRows.value.length !== 1) { |
| | | proxy.$message.error("è¯·éæ©ä¸æ¡æ°æ®"); |
| | | return; |
| | | } |
| | | if (selectedRows.value[0].pendingFinishNum == 0) { |
| | | proxy.$message.warning("æ é忥工"); |
| | | return; |
| | | } |
| | | nextTick(() => { |
| | | const rowInfo = type === "add" ? selectedRows.value[0] : row; |
| | | formDia.value?.openDialog(type, rowInfo); |
| | | }); |
| | | }; |
| | | |
| | | // æå¼æå
¥æ¨¡ææ¡ |
| | | const isShowInput = ref(false); |
| | | const isShowingId = ref(0); |
| | | const showInput = row => { |
| | | isShowInput.value = true; |
| | | isShowingId.value = row.id; |
| | | }; |
| | | |
| | | // å¯¼åº |
| | | const handleOut = () => { |
| | | ElMessageBox.confirm("éä¸çå
容å°è¢«å¯¼åºï¼æ¯å¦ç¡®è®¤å¯¼åºï¼", "导åº", { |
| | | confirmButtonText: "确认", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | proxy.download("/productionProductMain/export", {}, "ç产æ¥å·¥.xlsx"); |
| | | }) |
| | | .catch(() => { |
| | | proxy.$modal.msg("已忶"); |
| | | }); |
| | | }; |
| | | onMounted(() => { |
| | | getList(); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped></style> |
| | |
| | | :selectable="isSelectable" |
| | | @selection-change="handleSelectionChange" |
| | | @pagination="pagination"> |
| | | <template #quantity="{ row }"> |
| | | {{ row.quantity || '-' }}<span style="color:rgb(63, 95, 211)"> å</span> |
| | | </template> |
| | | <template #volume="{ row }"> |
| | | {{ row.volume || '-' }}<span style="color:rgba(12, 46, 40, 0.76)"> æ¹</span> |
| | | </template> |
| | | </PIMTable> |
| | | </div> |
| | | <!-- åå¹¶ä¸åå¼¹çª --> |
| | |
| | | { |
| | | label: "åæ°", |
| | | prop: "quantity", |
| | | formatData: cell => (cell ? `${cell}å` : ""), |
| | | align: "right", |
| | | dataType: "slot", |
| | | slot: "quantity", |
| | | }, |
| | | { |
| | | label: "æ¹æ°", |
| | | prop: "volume", |
| | | width: "150px", |
| | | align: "right", |
| | | dataType: "slot", |
| | | slot: "volume", |
| | | className: "volume-cell", |
| | | formatData: cell => (cell ? `${cell}æ¹` : ""), |
| | | }, |
| | | { |
| | | label: "ä¸åç¶æ", |
| | |
| | | sum + |
| | | (row.volume == null |
| | | ? 0 |
| | | : (Number(row.volume) - Number(row.assignedQuantity)).toFixed(4)) |
| | | : Number(Number(row.volume) - Number(row.assignedQuantity).toFixed(4))) |
| | | ); |
| | | }, 0); |
| | | sumAssignedQuantity.value = totalAssignedQuantity; |