|
|
@@ -22,16 +22,19 @@
|
|
|
<div v-else class="route-line">
|
|
|
<div
|
|
|
v-for="(item, index) in taskList"
|
|
|
- :key="`route-${item._taskKey}`"
|
|
|
- class="route-item"
|
|
|
+ :key="`route-seg-${item._taskKey}`"
|
|
|
+ class="route-segment"
|
|
|
>
|
|
|
<span
|
|
|
class="route-node"
|
|
|
- :class="{ 'route-node--done': isConfigured(item.dotLineConfig) }"
|
|
|
+ :class="{ 'route-node--done': isRouteItemDone(item) }"
|
|
|
>
|
|
|
- {{ item.name || item.taskName || `工艺${index + 1}` }}
|
|
|
+ {{ item.taskName || item.name || `工艺${index + 1}` }}
|
|
|
</span>
|
|
|
- <span v-if="index < taskList.length - 1" class="route-arrow"
|
|
|
+ <span
|
|
|
+ v-if="index < taskList.length - 1"
|
|
|
+ class="route-arrow"
|
|
|
+ aria-hidden="true"
|
|
|
>→</span
|
|
|
>
|
|
|
</div>
|
|
|
@@ -48,7 +51,7 @@
|
|
|
:data="taskList"
|
|
|
border
|
|
|
size="small"
|
|
|
- :row-key="getRowKey"
|
|
|
+ row-key="_taskKey"
|
|
|
max-height="420"
|
|
|
class="config-table"
|
|
|
>
|
|
|
@@ -57,35 +60,46 @@
|
|
|
label="序号"
|
|
|
width="48"
|
|
|
align="center"
|
|
|
- header-align="center"
|
|
|
/>
|
|
|
<el-table-column
|
|
|
- label="工序"
|
|
|
- min-width="90"
|
|
|
+ label="工序名称"
|
|
|
+ min-width="100"
|
|
|
show-overflow-tooltip
|
|
|
class-name="task-name-cell"
|
|
|
align="center"
|
|
|
- header-align="center"
|
|
|
>
|
|
|
<template slot-scope="{ row, $index }">
|
|
|
<span class="task-name-text">{{
|
|
|
- row.name || row.taskName || `工艺${$index + 1}`
|
|
|
+ row.taskName || row.name || `工艺${$index + 1}`
|
|
|
}}</span>
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
- <el-table-column
|
|
|
- label="班组"
|
|
|
- min-width="130"
|
|
|
- align="center"
|
|
|
- header-align="center"
|
|
|
- >
|
|
|
+ <el-table-column label="执行模式" min-width="100" align="center">
|
|
|
<template slot-scope="{ row }">
|
|
|
<el-select
|
|
|
- v-model="row.dotLineConfig.teamId"
|
|
|
+ v-model="row.executionType"
|
|
|
+ placeholder="执行模式"
|
|
|
+ clearable
|
|
|
+ class="config-table-control"
|
|
|
+ >
|
|
|
+ <el-option
|
|
|
+ v-for="opt in executionTypeOptions"
|
|
|
+ :key="opt.value"
|
|
|
+ :label="opt.label"
|
|
|
+ :value="opt.value"
|
|
|
+ />
|
|
|
+ </el-select>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="执行班组" min-width="130" align="center">
|
|
|
+ <template slot-scope="{ row }">
|
|
|
+ <el-select
|
|
|
+ v-model="row.executionTeamId"
|
|
|
placeholder="请选择班组"
|
|
|
clearable
|
|
|
filterable
|
|
|
class="config-table-control"
|
|
|
+ @change="onExecutionTeamChange(row)"
|
|
|
>
|
|
|
<el-option
|
|
|
v-for="team in teamOptions"
|
|
|
@@ -97,61 +111,37 @@
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
<el-table-column
|
|
|
- label="开始时间"
|
|
|
+ label="执行开始时间"
|
|
|
min-width="168"
|
|
|
align="center"
|
|
|
- header-align="center"
|
|
|
>
|
|
|
<template slot-scope="{ row }">
|
|
|
<el-date-picker
|
|
|
- v-model="row.dotLineConfig.startTime"
|
|
|
+ v-model="row.executionStartTime"
|
|
|
type="datetime"
|
|
|
value-format="yyyy-MM-dd HH:mm:ss"
|
|
|
- placeholder="开始时间"
|
|
|
+ placeholder="执行开始时间"
|
|
|
class="config-table-control"
|
|
|
- @change="handleTimeChange(row, 'startTime')"
|
|
|
+ @change="handleTimeChange(row, 'executionStartTime')"
|
|
|
/>
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
<el-table-column
|
|
|
- label="结束时间"
|
|
|
+ label="执行结束时间"
|
|
|
min-width="168"
|
|
|
align="center"
|
|
|
- header-align="center"
|
|
|
>
|
|
|
<template slot-scope="{ row }">
|
|
|
<el-date-picker
|
|
|
- v-model="row.dotLineConfig.endTime"
|
|
|
+ v-model="row.executionEndTime"
|
|
|
type="datetime"
|
|
|
value-format="yyyy-MM-dd HH:mm:ss"
|
|
|
- placeholder="结束时间"
|
|
|
+ placeholder="执行结束时间"
|
|
|
class="config-table-control"
|
|
|
- @change="handleTimeChange(row, 'endTime')"
|
|
|
+ @change="handleTimeChange(row, 'executionEndTime')"
|
|
|
/>
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
- <el-table-column
|
|
|
- label="类型"
|
|
|
- min-width="100"
|
|
|
- align="center"
|
|
|
- header-align="center"
|
|
|
- >
|
|
|
- <template slot-scope="{ row }">
|
|
|
- <el-select
|
|
|
- v-model="row.dotLineConfig.type"
|
|
|
- placeholder="请选择类型"
|
|
|
- clearable
|
|
|
- class="config-table-control"
|
|
|
- >
|
|
|
- <el-option
|
|
|
- v-for="type in typeOptions"
|
|
|
- :key="type.value"
|
|
|
- :label="type.label"
|
|
|
- :value="type.value"
|
|
|
- />
|
|
|
- </el-select>
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
</el-table>
|
|
|
</div>
|
|
|
</div>
|
|
|
@@ -164,170 +154,282 @@
|
|
|
</template>
|
|
|
|
|
|
<script>
|
|
|
+ import {
|
|
|
+ getPlanDotLine,
|
|
|
+ savePlanDotLine
|
|
|
+ } from '@/api/productionPlan/planDotLine';
|
|
|
import { getTaskListById } from '@/api/materialPlan';
|
|
|
import { teamPage } from '@/api/mainData';
|
|
|
+
|
|
|
+ const EXEC_TYPE = Object.freeze({ HOMEMADE: 0, ENTRUST: 1, OUTSOURCE: 2 });
|
|
|
+
|
|
|
+ const EXEC_TYPE_OPTIONS = Object.freeze([
|
|
|
+ { label: '自制', value: EXEC_TYPE.HOMEMADE },
|
|
|
+ { label: '请托', value: EXEC_TYPE.ENTRUST },
|
|
|
+ { label: '委外', value: EXEC_TYPE.OUTSOURCE }
|
|
|
+ ]);
|
|
|
+
|
|
|
+ function toSafeNumber(val) {
|
|
|
+ if (val === '' || val == null) return undefined;
|
|
|
+ const n = Number(val);
|
|
|
+ return Number.isNaN(n) ? undefined : n;
|
|
|
+ }
|
|
|
+
|
|
|
+ function formatDateTime(val) {
|
|
|
+ if (val == null || val === '') return '';
|
|
|
+ const str = String(val).trim();
|
|
|
+ if (/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/.test(str)) return str;
|
|
|
+ const d = new Date(val);
|
|
|
+ if (Number.isNaN(d.getTime())) return '';
|
|
|
+ const p = (n) => String(n).padStart(2, '0');
|
|
|
+ return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())} ${p(
|
|
|
+ d.getHours()
|
|
|
+ )}:${p(d.getMinutes())}:${p(d.getSeconds())}`;
|
|
|
+ }
|
|
|
+
|
|
|
+ /** 比较两个时间字符串,结束 < 开始 则返回 true */
|
|
|
+ function isEndBeforeStart(startStr, endStr) {
|
|
|
+ if (!startStr || !endStr) return false;
|
|
|
+ const s = new Date(startStr).getTime();
|
|
|
+ const e = new Date(endStr).getTime();
|
|
|
+ return !Number.isNaN(s) && !Number.isNaN(e) && e < s;
|
|
|
+ }
|
|
|
+
|
|
|
export default {
|
|
|
data() {
|
|
|
return {
|
|
|
visible: false,
|
|
|
dialogVisible: false,
|
|
|
- title: '计划描点',
|
|
|
+ title: '计划布点',
|
|
|
taskList: [],
|
|
|
teamOptions: [],
|
|
|
currentPlan: null,
|
|
|
- typeOptions: [
|
|
|
- { label: '自制', value: '1' },
|
|
|
- { label: '请托', value: '2' },
|
|
|
- { label: '委外', value: '3' }
|
|
|
- ],
|
|
|
- // 与 typeOptions 一致:自制 / 请托 / 委外
|
|
|
- TYPE_HOMEMADE: '1',
|
|
|
- TYPE_ENTRUST: '2',
|
|
|
- TYPE_OUTSOURCE: '3'
|
|
|
+ planRoutingPayload: null,
|
|
|
+ executionTypeOptions: EXEC_TYPE_OPTIONS
|
|
|
};
|
|
|
},
|
|
|
methods: {
|
|
|
- getRowKey(row) {
|
|
|
- return row._taskKey;
|
|
|
- },
|
|
|
async open(data) {
|
|
|
this.currentPlan = data || null;
|
|
|
+ this.planRoutingPayload = null;
|
|
|
await Promise.all([this.loadTaskList(), this.loadTeamOptions()]);
|
|
|
- this.fillSavedTaskConfig();
|
|
|
this.visible = true;
|
|
|
this.dialogVisible = true;
|
|
|
},
|
|
|
+
|
|
|
async loadTaskList() {
|
|
|
- if (!this.currentPlan || !this.currentPlan.produceRoutingId) {
|
|
|
- this.taskList = [];
|
|
|
+ this.taskList = [];
|
|
|
+ this.planRoutingPayload = null;
|
|
|
+ if (!this.currentPlan?.id) return;
|
|
|
+
|
|
|
+ let planData;
|
|
|
+ try {
|
|
|
+ planData = await getPlanDotLine({ planId: this.currentPlan.id });
|
|
|
+ } catch (e) {
|
|
|
+ this.$message.error(e.message || '获取工序布点信息失败');
|
|
|
return;
|
|
|
}
|
|
|
- const list = await getTaskListById(this.currentPlan.produceRoutingId);
|
|
|
- this.taskList = (list || []).map((item, index) => ({
|
|
|
+
|
|
|
+ this.planRoutingPayload = planData || {};
|
|
|
+ const details = planData?.detailList;
|
|
|
+
|
|
|
+ if (details?.length) {
|
|
|
+ this.taskList = [...details]
|
|
|
+ .sort((a, b) => (a.taskSort ?? 0) - (b.taskSort ?? 0))
|
|
|
+ .map((item, i) => this.normalizeDetailRow(item, i));
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!this.currentPlan.produceRoutingId) {
|
|
|
+ this.$message.warning('暂无布点明细且缺少工艺路线,无法加载工序');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ const list = await getTaskListById(this.currentPlan.produceRoutingId);
|
|
|
+ this.taskList = (list || []).map((item, i) =>
|
|
|
+ this.normalizeRoutingTaskRow(item, i)
|
|
|
+ );
|
|
|
+ } catch (e) {
|
|
|
+ this.$message.error(e.message || '获取工序列表失败');
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ normalizeDetailRow(item, index) {
|
|
|
+ return {
|
|
|
...item,
|
|
|
- _taskKey: item.id || item.taskId || item.sourceTaskId || index,
|
|
|
- dotLineConfig: {
|
|
|
- teamId: '',
|
|
|
- startTime: '',
|
|
|
- endTime: '',
|
|
|
- type: this.TYPE_HOMEMADE
|
|
|
- }
|
|
|
- }));
|
|
|
+ _taskKey: item.id ?? `detail-${item.taskId ?? index}`,
|
|
|
+ executionStartTime: formatDateTime(item.executionStartTime),
|
|
|
+ executionEndTime: formatDateTime(item.executionEndTime),
|
|
|
+ executionType: toSafeNumber(item.executionType) ?? EXEC_TYPE.HOMEMADE,
|
|
|
+ executionTeamId: item.executionTeamId ?? '',
|
|
|
+ executionTeamLeader: item.executionTeamLeader ?? '',
|
|
|
+ executionTeamLeaderId: item.executionTeamLeaderId ?? '',
|
|
|
+ executionTeamName: item.executionTeamName ?? ''
|
|
|
+ };
|
|
|
+ },
|
|
|
+
|
|
|
+ normalizeRoutingTaskRow(item, index) {
|
|
|
+ const plan = this.currentPlan;
|
|
|
+ const taskId = item.taskId ?? item.sourceTaskId ?? item.id;
|
|
|
+ return {
|
|
|
+ ...item,
|
|
|
+ _taskKey: item.id ?? `new-${taskId ?? index}-${index}`,
|
|
|
+ taskId: taskId ?? undefined,
|
|
|
+ taskName: item.name ?? item.taskName,
|
|
|
+ taskCode: item.code ?? item.taskCode,
|
|
|
+ taskSort: item.taskSort ?? index + 1,
|
|
|
+ type: item.type ?? undefined,
|
|
|
+ sourceTaskId: item.sourceTaskId ?? taskId ?? index,
|
|
|
+ planId: plan.id,
|
|
|
+ planCode: plan.code,
|
|
|
+ routingId: plan.produceRoutingId,
|
|
|
+ routingName: plan.produceRoutingName ?? plan.routingName,
|
|
|
+ executionType: EXEC_TYPE.HOMEMADE,
|
|
|
+ executionTeamId: '',
|
|
|
+ executionTeamLeader: '',
|
|
|
+ executionTeamLeaderId: '',
|
|
|
+ executionTeamName: '',
|
|
|
+ executionStartTime: '',
|
|
|
+ executionEndTime: ''
|
|
|
+ };
|
|
|
+ },
|
|
|
+
|
|
|
+ onExecutionTeamChange(row) {
|
|
|
+ const team = this.teamOptions.find((t) => t.id === row.executionTeamId);
|
|
|
+ row.executionTeamName = team?.name ?? '';
|
|
|
+ row.executionTeamLeader = team?.leaderUserName ?? '';
|
|
|
+ row.executionTeamLeaderId = team?.leaderUserId ?? '';
|
|
|
},
|
|
|
+
|
|
|
async loadTeamOptions() {
|
|
|
try {
|
|
|
- const res = await teamPage({ size: 500 });
|
|
|
- const list = res?.list || [];
|
|
|
- this.teamOptions = list.map((team) => ({
|
|
|
- id: team.id,
|
|
|
- name: team.name
|
|
|
- }));
|
|
|
- } catch (error) {
|
|
|
+ const factoryId = this.$store.state.user.info.factoryId;
|
|
|
+ const res = await teamPage({ pageNum: 1, size: -1, factoryId });
|
|
|
+ this.teamOptions = (res?.list || []).map(
|
|
|
+ ({ id, name, leaderUserName, leaderUserId }) => ({
|
|
|
+ id,
|
|
|
+ name,
|
|
|
+ leaderUserName,
|
|
|
+ leaderUserId
|
|
|
+ })
|
|
|
+ );
|
|
|
+ } catch {
|
|
|
this.teamOptions = [];
|
|
|
}
|
|
|
},
|
|
|
- fillSavedTaskConfig() {
|
|
|
- const savedList = this.currentPlan?.dotLineTaskList || [];
|
|
|
- if (!savedList.length) {
|
|
|
- return;
|
|
|
- }
|
|
|
- const map = {};
|
|
|
- savedList.forEach((item) => {
|
|
|
- map[item._taskKey] = item.dotLineConfig || {};
|
|
|
- });
|
|
|
- this.taskList = this.taskList.map((item) => {
|
|
|
- const merged = {
|
|
|
- ...item.dotLineConfig,
|
|
|
- ...(map[item._taskKey] || {})
|
|
|
- };
|
|
|
- if (!merged.type) {
|
|
|
- merged.type = this.TYPE_HOMEMADE;
|
|
|
- }
|
|
|
- return {
|
|
|
- ...item,
|
|
|
- dotLineConfig: merged
|
|
|
- };
|
|
|
- });
|
|
|
- },
|
|
|
- isConfigured(config) {
|
|
|
- if (!config) {
|
|
|
- return false;
|
|
|
- }
|
|
|
- return !!(
|
|
|
- config.teamId ||
|
|
|
- config.startTime ||
|
|
|
- config.endTime ||
|
|
|
- config.type
|
|
|
- );
|
|
|
+
|
|
|
+ isRouteItemDone(row) {
|
|
|
+ return row.executionType != null && row.executionType !== '';
|
|
|
},
|
|
|
- handleTimeChange(item, changeKey) {
|
|
|
- const cfg = item?.dotLineConfig || {};
|
|
|
- if (!cfg.startTime || !cfg.endTime) {
|
|
|
- return;
|
|
|
- }
|
|
|
- const start = new Date(cfg.startTime).getTime();
|
|
|
- const end = new Date(cfg.endTime).getTime();
|
|
|
- if (!Number.isNaN(start) && !Number.isNaN(end) && end < start) {
|
|
|
- this.$message.warning('结束时间不能小于开始时间');
|
|
|
- this.$set(cfg, changeKey, '');
|
|
|
+
|
|
|
+ handleTimeChange(row, changeKey) {
|
|
|
+ if (isEndBeforeStart(row.executionStartTime, row.executionEndTime)) {
|
|
|
+ this.$message.warning('执行结束时间不能小于执行开始时间');
|
|
|
+ this.$set(row, changeKey, '');
|
|
|
}
|
|
|
},
|
|
|
- handleSave() {
|
|
|
- const { TYPE_HOMEMADE, TYPE_ENTRUST, TYPE_OUTSOURCE } = this;
|
|
|
- const taskLabel = (task) => task.name || task.taskName || '当前工艺';
|
|
|
|
|
|
- for (const item of this.taskList) {
|
|
|
- const cfg = item.dotLineConfig || {};
|
|
|
- if (!cfg.type) {
|
|
|
- this.$set(item.dotLineConfig, 'type', TYPE_HOMEMADE);
|
|
|
- }
|
|
|
- }
|
|
|
+ validateTaskList() {
|
|
|
+ const label = (t) => t.taskName || t.name || '当前工艺';
|
|
|
|
|
|
for (const item of this.taskList) {
|
|
|
- const cfg = item.dotLineConfig || {};
|
|
|
- const type = cfg.type || TYPE_HOMEMADE;
|
|
|
+ if (item.executionType == null || item.executionType === '') {
|
|
|
+ this.$set(item, 'executionType', EXEC_TYPE.HOMEMADE);
|
|
|
+ }
|
|
|
+ const execType = Number(item.executionType);
|
|
|
|
|
|
- if (type === TYPE_HOMEMADE) {
|
|
|
- if (!cfg.teamId || !cfg.startTime || !cfg.endTime) {
|
|
|
- this.$message.warning(
|
|
|
- `${taskLabel(item)}类型为自制时,需填写班组、开始时间和结束时间`
|
|
|
- );
|
|
|
- return;
|
|
|
- }
|
|
|
- const start = new Date(cfg.startTime).getTime();
|
|
|
- const end = new Date(cfg.endTime).getTime();
|
|
|
- if (Number.isNaN(start) || Number.isNaN(end)) {
|
|
|
- this.$message.warning(`${taskLabel(item)}开始时间或结束时间无效`);
|
|
|
- return;
|
|
|
- }
|
|
|
- if (end < start) {
|
|
|
+ if (execType === EXEC_TYPE.HOMEMADE) {
|
|
|
+ if (
|
|
|
+ !item.executionTeamId ||
|
|
|
+ !item.executionStartTime ||
|
|
|
+ !item.executionEndTime
|
|
|
+ ) {
|
|
|
this.$message.warning(
|
|
|
- `${taskLabel(item)}结束时间不能小于开始时间`
|
|
|
+ `${label(
|
|
|
+ item
|
|
|
+ )}执行模式为自制时,需填写执行班组、执行开始和结束时间`
|
|
|
);
|
|
|
- return;
|
|
|
- }
|
|
|
- } else if (type === TYPE_ENTRUST || type === TYPE_OUTSOURCE) {
|
|
|
- if (cfg.startTime && cfg.endTime) {
|
|
|
- const start = new Date(cfg.startTime).getTime();
|
|
|
- const end = new Date(cfg.endTime).getTime();
|
|
|
- if (!Number.isNaN(start) && !Number.isNaN(end) && end < start) {
|
|
|
- this.$message.warning(
|
|
|
- `${taskLabel(item)}结束时间不能小于开始时间`
|
|
|
- );
|
|
|
- return;
|
|
|
- }
|
|
|
+ return false;
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ if (
|
|
|
+ isEndBeforeStart(item.executionStartTime, item.executionEndTime)
|
|
|
+ ) {
|
|
|
+ this.$message.warning(
|
|
|
+ `${label(item)}执行结束时间不能小于执行开始时间`
|
|
|
+ );
|
|
|
+ return false;
|
|
|
+ }
|
|
|
}
|
|
|
- if (this.currentPlan) {
|
|
|
- this.$set(this.currentPlan, 'dotLineTaskList', this.taskList);
|
|
|
- }
|
|
|
- this.$emit('save', {
|
|
|
- plan: this.currentPlan,
|
|
|
- taskList: this.taskList
|
|
|
+ return true;
|
|
|
+ },
|
|
|
+
|
|
|
+ buildSavePayload() {
|
|
|
+ const plan = this.currentPlan;
|
|
|
+ const head = this.planRoutingPayload || {};
|
|
|
+
|
|
|
+ const detailList = this.taskList.map((row) => {
|
|
|
+ const detail = {
|
|
|
+ executionEndTime: row.executionEndTime || undefined,
|
|
|
+ executionStartTime: row.executionStartTime || undefined,
|
|
|
+ executionTeamId: row.executionTeamId || undefined,
|
|
|
+ executionTeamLeader: row.executionTeamLeader || '',
|
|
|
+ executionTeamLeaderId: row.executionTeamLeaderId || '',
|
|
|
+ executionTeamName: row.executionTeamName || '',
|
|
|
+ executionType: toSafeNumber(row.executionType) ?? 0,
|
|
|
+ planCode: row.planCode || plan?.code,
|
|
|
+ planId: row.planId ?? plan?.id,
|
|
|
+ routingId:
|
|
|
+ row.routingId ?? plan?.produceRoutingId ?? head.routingId,
|
|
|
+ routingName:
|
|
|
+ row.routingName || plan?.produceRoutingName || head.routingName,
|
|
|
+ sourceTaskId: row.sourceTaskId ?? row.taskId,
|
|
|
+ taskCode: row.taskCode,
|
|
|
+ taskId: row.taskId,
|
|
|
+ taskName: row.taskName,
|
|
|
+ taskSort: row.taskSort,
|
|
|
+ type: toSafeNumber(row.type)
|
|
|
+ };
|
|
|
+ if (row.id != null) detail.id = row.id;
|
|
|
+ return detail;
|
|
|
});
|
|
|
- this.$message.success('保存成功');
|
|
|
- this.onModalClose();
|
|
|
+
|
|
|
+ return {
|
|
|
+ detailList,
|
|
|
+ fileParam: head.fileParam ?? {},
|
|
|
+ id: head.id,
|
|
|
+ planCode: plan?.code ?? head.planCode,
|
|
|
+ planId: plan?.id ?? head.planId,
|
|
|
+ produceVersionId: head.produceVersionId,
|
|
|
+ produceVersionName: head.produceVersionName,
|
|
|
+ releaseTime: head.releaseTime,
|
|
|
+ routingCode: head.routingCode,
|
|
|
+ routingId: head.routingId ?? plan?.produceRoutingId,
|
|
|
+ routingName: head.routingName ?? plan?.produceRoutingName,
|
|
|
+ routingVersion: head.routingVersion
|
|
|
+ };
|
|
|
+ },
|
|
|
+
|
|
|
+ async handleSave() {
|
|
|
+ if (!this.validateTaskList()) return;
|
|
|
+
|
|
|
+ const payload = this.buildSavePayload();
|
|
|
+ const loading = this.$loading({ lock: true, text: '保存中...' });
|
|
|
+ try {
|
|
|
+ await savePlanDotLine(payload);
|
|
|
+ this.$emit('save', {
|
|
|
+ plan: this.currentPlan,
|
|
|
+ taskList: this.taskList,
|
|
|
+ payload
|
|
|
+ });
|
|
|
+ this.$message.success('保存成功');
|
|
|
+ this.onModalClose();
|
|
|
+ } catch (e) {
|
|
|
+ this.$message.error(e.message || '保存失败');
|
|
|
+ } finally {
|
|
|
+ loading.close();
|
|
|
+ }
|
|
|
},
|
|
|
|
|
|
onModalClose() {
|
|
|
@@ -335,15 +437,25 @@
|
|
|
this.dialogVisible = false;
|
|
|
this.taskList = [];
|
|
|
this.currentPlan = null;
|
|
|
+ this.planRoutingPayload = null;
|
|
|
}
|
|
|
}
|
|
|
};
|
|
|
</script>
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
+ .plan-dot-line,
|
|
|
+ .top-route,
|
|
|
+ .config-panel,
|
|
|
+ .route-line {
|
|
|
+ width: 100%;
|
|
|
+ max-width: 100%;
|
|
|
+ min-width: 0;
|
|
|
+ box-sizing: border-box;
|
|
|
+ }
|
|
|
+
|
|
|
.plan-dot-line {
|
|
|
min-height: 360px;
|
|
|
- display: block;
|
|
|
}
|
|
|
|
|
|
.top-route,
|
|
|
@@ -366,32 +478,37 @@
|
|
|
|
|
|
.route-line {
|
|
|
display: flex;
|
|
|
- flex-wrap: wrap;
|
|
|
- align-items: center;
|
|
|
- row-gap: 4px;
|
|
|
+ flex-wrap: nowrap;
|
|
|
+ align-items: stretch;
|
|
|
color: #303133;
|
|
|
}
|
|
|
|
|
|
- .route-item {
|
|
|
- display: inline-flex;
|
|
|
+ .route-segment {
|
|
|
+ flex: 1 1 0;
|
|
|
+ min-width: 0;
|
|
|
+ display: flex;
|
|
|
align-items: center;
|
|
|
- font-size: 12px;
|
|
|
+ justify-content: center;
|
|
|
}
|
|
|
|
|
|
.route-node {
|
|
|
- display: inline-flex;
|
|
|
+ flex: 0 1 auto;
|
|
|
+ display: flex;
|
|
|
align-items: center;
|
|
|
justify-content: center;
|
|
|
- min-width: 38px;
|
|
|
- height: 20px;
|
|
|
+ min-width: 0;
|
|
|
+ min-height: 22px;
|
|
|
border-radius: 4px;
|
|
|
- padding: 0 7px;
|
|
|
- color: #ffffff;
|
|
|
+ padding: 2px 10px;
|
|
|
+ color: #fff;
|
|
|
background: #909399;
|
|
|
- font-size: 12px;
|
|
|
+ font-size: clamp(10px, 2.6vw, 12px);
|
|
|
font-weight: 600;
|
|
|
- line-height: 1;
|
|
|
- box-sizing: border-box;
|
|
|
+ line-height: 1.2;
|
|
|
+ overflow: hidden;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+ white-space: nowrap;
|
|
|
+ text-align: center;
|
|
|
}
|
|
|
|
|
|
.route-node--done {
|
|
|
@@ -399,38 +516,52 @@
|
|
|
}
|
|
|
|
|
|
.route-arrow {
|
|
|
- margin: 0 5px;
|
|
|
+ flex: 1 1 0;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
color: #909399;
|
|
|
- font-size: 12px;
|
|
|
+ font-size: clamp(14px, 2.5vw, 18px);
|
|
|
line-height: 1;
|
|
|
}
|
|
|
|
|
|
- .task-config-table-wrap {
|
|
|
- width: 100%;
|
|
|
- }
|
|
|
-
|
|
|
- .config-table ::v-deep .el-table th > .cell,
|
|
|
- .config-table ::v-deep .el-table td > .cell {
|
|
|
- font-size: 14px;
|
|
|
- text-align: center;
|
|
|
- }
|
|
|
-
|
|
|
- .config-table ::v-deep .el-table__body .cell {
|
|
|
- padding-left: 6px;
|
|
|
- padding-right: 6px;
|
|
|
- }
|
|
|
-
|
|
|
- .config-table ::v-deep td.task-name-cell .task-name-text {
|
|
|
- font-size: 14px;
|
|
|
- font-weight: 500;
|
|
|
- line-height: 1.4;
|
|
|
+ @media (max-width: 576px) {
|
|
|
+ .top-route,
|
|
|
+ .config-panel {
|
|
|
+ padding: 8px;
|
|
|
+ }
|
|
|
+ .route-node {
|
|
|
+ padding: 2px 4px;
|
|
|
+ }
|
|
|
+ .route-arrow {
|
|
|
+ font-size: 12px;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- .config-table ::v-deep .config-table-control {
|
|
|
- width: 100% !important;
|
|
|
+ .task-config-table-wrap {
|
|
|
+ width: 100%;
|
|
|
}
|
|
|
|
|
|
- .config-table ::v-deep .config-table-control.el-date-editor {
|
|
|
- max-width: 100%;
|
|
|
+ .config-table ::v-deep {
|
|
|
+ .el-table th > .cell,
|
|
|
+ .el-table td > .cell {
|
|
|
+ font-size: 14px;
|
|
|
+ text-align: center;
|
|
|
+ }
|
|
|
+ .el-table__body .cell {
|
|
|
+ padding-left: 6px;
|
|
|
+ padding-right: 6px;
|
|
|
+ }
|
|
|
+ td.task-name-cell .task-name-text {
|
|
|
+ font-size: 14px;
|
|
|
+ font-weight: 500;
|
|
|
+ line-height: 1.4;
|
|
|
+ }
|
|
|
+ .config-table-control {
|
|
|
+ width: 100% !important;
|
|
|
+ }
|
|
|
+ .config-table-control.el-date-editor {
|
|
|
+ max-width: 100%;
|
|
|
+ }
|
|
|
}
|
|
|
</style>
|