| src/api/basicData/productModel.js | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/api/productionManagement/processRoute.js | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/api/productionManagement/processRouteItem.js | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/api/productionManagement/productionProcess.js | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/views/basicData/product/ProductSelectDialog.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/views/productionManagement/processRoute/ItemsForm.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/views/productionManagement/processRoute/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
src/api/basicData/productModel.js
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,9 @@ import request from "@/utils/request.js"; export function productModelList(query) { return request({ url: '/basic/product/pageModel', method: 'get', params: query }) } src/api/productionManagement/processRoute.js
@@ -1,4 +1,4 @@ // å·¥åºé¡µé¢æ¥å£ // å·¥èºè·¯çº¿é¡µé¢æ¥å£ import request from "@/utils/request"; // å页æ¥è¯¢ src/api/productionManagement/processRouteItem.js
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,19 @@ // å·¥èºè·¯çº¿é¡¹ç®é¡µé¢æ¥å£ import request from "@/utils/request"; // å表æ¥è¯¢ export function findProcessRouteItemList(query) { return request({ url: "/processRouteItem/list", method: "get", params: query, }); } export function addOrUpdateProcessRouteItem(data) { return request({ url: "/processRouteItem", method: "post", data: data, }); } src/api/productionManagement/productionProcess.js
@@ -10,6 +10,14 @@ }); } export function processList(query) { return request({ url: "/productProcess/list", method: "get", params: query, }); } export function add(data) { return request({ url: "/productProcess", src/views/basicData/product/ProductSelectDialog.vue
@@ -37,8 +37,10 @@ :data="tableData" height="420" highlight-current-row @current-change="onCurrentChange" row-key="id" @selection-change="handleSelectionChange" > <el-table-column type="selection" width="55" /> <el-table-column type="index" label="#" width="60"/> <el-table-column prop="productName" label="产å大类" min-width="160"/> <el-table-column prop="model" label="åå·åç§°" min-width="200"/> @@ -60,7 +62,7 @@ <template #footer> <el-button @click="close()">åæ¶</el-button> <el-button type="primary" :disabled="!selectedRow" @click="onConfirm"> <el-button type="primary" :disabled="multipleSelection.length === 0" @click="onConfirm"> ç¡®å® </el-button> </template> @@ -70,7 +72,7 @@ <script setup lang="ts"> import {computed, onMounted, reactive, ref, watch} from "vue"; import {ElMessage} from "element-plus"; import {list} from '@/api/basicData/productModel' import {productModelList} from '@/api/basicData/productModel' export type ProductRow = { id: number; @@ -83,10 +85,7 @@ modelValue: boolean; }>(); const emit = defineEmits<{ (e: "update:modelValue", v: boolean): void; (e: "confirm", row: ProductRow): void; // ææ´è¡æ°æ®è¿ç»ç¶ç»ä»¶ }>(); const emit = defineEmits(['update:modelValue', 'confirm']); const visible = computed({ get: () => props.modelValue, @@ -106,14 +105,14 @@ const loading = ref(false); const tableData = ref<ProductRow[]>([]); const total = ref(0); const selectedRow = ref<ProductRow | null>(null); const multipleSelection = ref<ProductRow[]>([]) function close() { visible.value = false; } function onCurrentChange(row: ProductRow | null) { selectedRow.value = row; const handleSelectionChange = (val: ProductRow[]) => { multipleSelection.value = val } function onSearch() { @@ -133,25 +132,25 @@ } function onConfirm() { if (!selectedRow.value) { if (multipleSelection.value.length === 0) { ElMessage.warning("è¯·éæ©ä¸æ¡äº§å"); return; } emit("confirm", selectedRow.value); emit("confirm", multipleSelection.value); close(); } async function loadData() { loading.value = true; try { selectedRow.value = null; // 翻页/æç´¢åæ¸ ç©ºéæ©æ´ç¬¦å颿 const res = await list({ multipleSelection.value = []; // 翻页/æç´¢åæ¸ ç©ºéæ©æ´ç¬¦å颿 const res = await productModelList({ productName: query.productName.trim(), model: query.model.trim(), pageNum: page.pageNum, pageSize: page.pageSize, }); tableData.value = res.list; tableData.value = res.records; total.value = res.total; } finally { loading.value = false; src/views/productionManagement/processRoute/ItemsForm.vue
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,285 @@ <template> <div> <el-dialog v-model="isShow" title="å·¥èºè·¯çº¿é¡¹ç®" width="800px" @close="closeModal" > <el-button type="primary" @click="isShowProductSelectDialog = true" class="mb5" style="margin-bottom: 10px;" > éæ©äº§å </el-button> <el-table ref="multipleTable" v-loading="tableLoading" border :data="routeItems" :header-cell-style="{ background: '#F0F1F5', color: '#333333' }" row-key="id" tooltip-effect="dark" class="lims-table" style="cursor: move;" > <el-table-column align="center" label="åºå·" type="index" width="60" /> <el-table-column v-for="(item, index) in tableColumn" :key="index" :label="item.label" :width="item.width" show-overflow-tooltip > <template #default="scope" v-if="item.dataType === 'action'"> <el-button v-for="(op, opIndex) in item.operation" :key="opIndex" :type="op.type" :link="op.link" size="small" @click.stop="op.clickFun(scope.row)" > {{ op.name }} </el-button> </template> <template #default="scope" v-else> <template v-if="item.prop === 'processId'"> <el-select v-model="scope.row[item.prop]" style="width: 100%;" @mousedown.stop > <el-option v-for="process in processOptions" :key="process.id" :label="process.name" :value="process.id" /> </el-select> </template> <template v-else> {{ scope.row[item.prop] || '-' }} </template> </template> </el-table-column> </el-table> <template #footer> <div class="dialog-footer"> <el-button type="primary" @click="handleSubmit">确认</el-button> <el-button @click="closeModal">åæ¶</el-button> </div> </template> </el-dialog> <ProductSelectDialog v-model="isShowProductSelectDialog" @confirm="handelSelectProducts" /> </div> </template> <script setup> import { ref, computed, getCurrentInstance, onMounted, onUnmounted, nextTick } from "vue"; import ProductSelectDialog from "@/views/basicData/product/ProductSelectDialog.vue"; import { findProcessRouteItemList, addOrUpdateProcessRouteItem } from "@/api/productionManagement/processRouteItem.js"; import { processList } from "@/api/productionManagement/productionProcess.js"; import Sortable from 'sortablejs'; const props = defineProps({ visible: { type: Boolean, required: true, default: false }, record: { type: Object, required: true, default: () => ({}) } }); const emit = defineEmits(['update:visible', 'completed']); const processOptions = ref([]); const tableLoading = ref(false); const isShowProductSelectDialog = ref(false); const routeItems = ref([]); let sortable = null; const multipleTable = ref(null); const isShow = computed({ get() { return props.visible; }, set(val) { emit('update:visible', val); } }); const tableColumn = ref([ { label: "产ååç§°", prop: "productName", width: 180 }, { label: "è§æ ¼åç§°", prop: "model", width: 150 }, { label: "åä½", prop: "unit", width: 80 }, { label: "å·¥åºåç§°", prop: "processId", width: 180 }, { dataType: "action", label: "æä½", align: "center", fixed: "right", width: 100, operation: [ { name: "å é¤", type: "danger", link: true, clickFun: (row) => { const idx = routeItems.value.findIndex(item => item.id === row.id); if (idx > -1) { routeItems.value.splice(idx, 1); } } } ] } ]); const closeModal = () => { isShow.value = false; }; const handelSelectProducts = (products) => { const newData = products.map(({ id, ...product }) => ({ ...product, productModelId: id, routeId: props.record.id, id: `${Date.now()}-${Math.random().toString(36).slice(2)}`, // çææ ç¹æ®å符çID processId: undefined })); routeItems.value.push(...newData); nextTick(() => initSortable()); }; const findProcessRouteItems = () => { tableLoading.value = true; findProcessRouteItemList({ routeId: props.record.id }) .then(res => { tableLoading.value = false; routeItems.value = res.data.map(item => ({ ...item, processId: item.processId === 0 ? undefined : item.processId })); nextTick(() => initSortable()); }) .catch(err => { tableLoading.value = false; console.error("è·åå表失败ï¼", err); }); }; const findProcessList = () => { processList({}) .then(res => { processOptions.value = res.data; }) .catch(err => { console.error("è·åå·¥åºå¤±è´¥ï¼", err); }); }; const { proxy } = getCurrentInstance() || {}; const handleSubmit = () => { if (routeItems.value.length === 0) { proxy?.$modal?.msgError("请添å 路线项ç®"); return; } const hasEmptyProcess = routeItems.value.some(item => !item.processId); if (hasEmptyProcess) { proxy?.$modal?.msgError("请为ææé¡¹ç®éæ©å·¥åº"); return; } addOrUpdateProcessRouteItem({ routeId: props.record.id, processRouteItem: routeItems.value.map(({ id, ...item }) => item) }) .then(res => { isShow.value = false; emit('completed'); proxy?.$modal?.msgSuccess("æäº¤æå"); }) .catch(err => { proxy?.$modal?.msgError(`æäº¤å¤±è´¥ï¼${err.msg || "ç½ç»å¼å¸¸"}`); }); }; const initSortable = () => { if (sortable) { sortable.destroy(); sortable = null; } if (!multipleTable.value) return; const tbody = multipleTable.value.$el.querySelector('.el-table__body tbody') || multipleTable.value.$el.querySelector('.el-table__body-wrapper > table > tbody'); if (!tbody) return; sortable = new Sortable(tbody, { animation: 150, ghostClass: 'sortable-ghost', handle: '.el-table__row', filter: '.el-button, .el-select', onEnd: (evt) => { const moveItem = routeItems.value.splice(evt.oldIndex, 1)[0]; routeItems.value.splice(evt.newIndex, 0, moveItem); } }); }; onMounted(() => { findProcessRouteItems(); findProcessList(); }); onUnmounted(() => { if (sortable) { sortable.destroy(); } }); // ä¿®å¤ï¼æ´é²æ¹æ³æ¶é¿å è¯æ³é误 defineExpose({ closeModal, handleSubmit, isShow }); </script> <style scoped> :deep(.sortable-ghost) { opacity: 0.6; background-color: #f5f7fa !important; } :deep(.el-table__row) { transition: background-color 0.2s; } :deep(.el-table__row:hover) { background-color: #f9fafc !important; } .mb5 { margin-bottom: 5px; } </style> src/views/productionManagement/processRoute/index.vue
@@ -2,8 +2,8 @@ <div class="app-container"> <div class="search_form"> <el-form :model="searchForm" :inline="true"> <el-form-item label="é¶ä»¶åç§°:"> <el-input v-model="searchForm.speculativeTradingName" placeholder="请è¾å ¥" clearable prefix-icon="Search" <el-form-item label="è§æ ¼åç§°:"> <el-input v-model="searchForm.model" placeholder="请è¾å ¥" clearable prefix-icon="Search" style="width: 200px;" @change="handleQuery" /> </el-form-item> @@ -27,7 +27,7 @@ :tableLoading="tableLoading" @pagination="pagination" :total="page.total" ></PIMTable> /> </div> <new-process v-if="isShowNewModal" @@ -41,6 +41,14 @@ :record="record" @completed="getList" /> <route-item-form v-if="isShowItemModal" v-model:visible="isShowItemModal" :record="record" @completed="getList" /> RouteItemForm </div> </template> @@ -48,6 +56,7 @@ import {onMounted, ref} from "vue"; import NewProcess from "@/views/productionManagement/processRoute/New.vue"; import EditProcess from "@/views/productionManagement/processRoute/Edit.vue"; import RouteItemForm from "@/views/productionManagement/processRoute/ItemsForm.vue"; import {listPage, del} from "@/api/productionManagement/processRoute.js"; const data = reactive({ @@ -73,17 +82,17 @@ width: 280, operation: [ { name: "详æ ", name: "ç¼è¾", type: "text", clickFun: (row) => { showEditModal(row); } }, { name: "ç¼è¾", name: "路线项ç®", type: "text", clickFun: (row) => { showEditModal(row); showItemModal(row); } } ] @@ -94,6 +103,7 @@ const tableLoading = ref(false); const isShowNewModal = ref(false); const isShowEditModal = ref(false); const isShowItemModal = ref(false); const record = ref({}); const page = reactive({ current: 1, @@ -143,6 +153,11 @@ record.value = row }; const showItemModal = (row) => { isShowItemModal.value = true record.value = row }; // å é¤ function handleDelete() { const ids = selectedRows.value.map((item) => item.id);