|
|
|
@ -59,6 +59,34 @@
|
|
|
|
<button type="button" class="btn-nav" :disabled="checking" @click="resetTraining"><el-icon><RefreshLeft /></el-icon>清空填写</button>
|
|
|
|
<button type="button" class="btn-nav" :disabled="checking" @click="resetTraining"><el-icon><RefreshLeft /></el-icon>清空填写</button>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</template>
|
|
|
|
</template>
|
|
|
|
|
|
|
|
<template v-else-if="currentStep === 2">
|
|
|
|
|
|
|
|
<div class="step-title-row">
|
|
|
|
|
|
|
|
<div><p class="section-eyebrow">Step 02</p><h3>{{ trainingSteps[1] }}</h3></div>
|
|
|
|
|
|
|
|
<span class="step-chip">项目排期练习</span>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<p class="process-intro">将开发一款互联网产品的模糊目标,拆解为可执行的具体工作任务。根据案例信息,填写参考工期和项目依赖关系。</p>
|
|
|
|
|
|
|
|
<p v-if="!hasCompletedStepOne" class="step-warning">请先完成第 1 步并判题通过;关键任务结果会自动带入本表。</p>
|
|
|
|
|
|
|
|
<div class="training-table-wrap">
|
|
|
|
|
|
|
|
<table class="process-table schedule-table">
|
|
|
|
|
|
|
|
<thead><tr><th>阶段</th><th>任务编号</th><th>具体工作任务</th><th>是否关键任务</th><th>开始时间</th><th>完成时间</th><th>项目依赖关系</th></tr></thead>
|
|
|
|
|
|
|
|
<tbody>
|
|
|
|
|
|
|
|
<tr v-for="row in processRows" :key="row.taskNo">
|
|
|
|
|
|
|
|
<td class="stage-cell">{{ row.stage }}</td>
|
|
|
|
|
|
|
|
<td class="task-no-cell">{{ row.taskNo }}</td>
|
|
|
|
|
|
|
|
<td class="task-cell">{{ row.task }}</td>
|
|
|
|
|
|
|
|
<td class="classification-cell">{{ row.classification || '—' }}</td>
|
|
|
|
|
|
|
|
<td><input v-model="row.startDate" type="date" :disabled="!hasCompletedStepOne || checking" /></td>
|
|
|
|
|
|
|
|
<td><input v-model="row.endDate" type="date" :disabled="!hasCompletedStepOne || checking" /></td>
|
|
|
|
|
|
|
|
<td><input v-model.trim="row.dependency" class="dependency-input" :disabled="!hasCompletedStepOne || checking" placeholder="如:1.1;无" maxlength="100" /></td>
|
|
|
|
|
|
|
|
</tr>
|
|
|
|
|
|
|
|
</tbody>
|
|
|
|
|
|
|
|
</table>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div class="task-actions">
|
|
|
|
|
|
|
|
<button type="button" class="btn-nav btn-submit" :disabled="checking || !hasCompletedStepOne" @click="saveAndGoToStepThree">{{ checking ? '校验中…' : '保存并进入第 3 步' }}</button>
|
|
|
|
|
|
|
|
<button type="button" class="btn-nav" :disabled="checking" @click="resetStepTwo"><el-icon><RefreshLeft /></el-icon>清空本步</button>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
</template>
|
|
|
|
<div v-else class="step-placeholder"><p>第 {{ currentStep }} 步内容待配置</p><span>第 1 步答案已保存,可从上方步骤栏返回查看。</span></div>
|
|
|
|
<div v-else class="step-placeholder"><p>第 {{ currentStep }} 步内容待配置</p><span>第 1 步答案已保存,可从上方步骤栏返回查看。</span></div>
|
|
|
|
</section>
|
|
|
|
</section>
|
|
|
|
</section>
|
|
|
|
</section>
|
|
|
|
@ -75,7 +103,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
<script setup>
|
|
|
|
<script setup>
|
|
|
|
import { Download, Operation, RefreshLeft } from "@element-plus/icons-vue";
|
|
|
|
import { Download, Operation, RefreshLeft } from "@element-plus/icons-vue";
|
|
|
|
import { checkProductDevelopmentProcessStepOne, getStudentTrainingAnswer, saveStudentTrainingAnswer } from "@/api/studentTrainingAnswer";
|
|
|
|
import { checkProductDevelopmentProcessStepOne, checkProductDevelopmentProcessStepTwo, getStudentTrainingAnswer, saveStudentTrainingAnswer } from "@/api/studentTrainingAnswer";
|
|
|
|
import { getTrainingTaskByKey } from "@/api/trainingTask";
|
|
|
|
import { getTrainingTaskByKey } from "@/api/trainingTask";
|
|
|
|
import useUserStore from "@/store/modules/user";
|
|
|
|
import useUserStore from "@/store/modules/user";
|
|
|
|
import TrainingAiSidebar from "@/views/components/TrainingAiSidebar.vue";
|
|
|
|
import TrainingAiSidebar from "@/views/components/TrainingAiSidebar.vue";
|
|
|
|
@ -107,12 +135,14 @@ const currentStep = ref(1);
|
|
|
|
const checking = ref(false);
|
|
|
|
const checking = ref(false);
|
|
|
|
const draftReady = ref(false);
|
|
|
|
const draftReady = ref(false);
|
|
|
|
const checkResult = ref(null);
|
|
|
|
const checkResult = ref(null);
|
|
|
|
|
|
|
|
const stepOnePassed = ref(false);
|
|
|
|
const taskTitle = computed(() => taskConfig.value?.taskName || defaultTask.title);
|
|
|
|
const taskTitle = computed(() => taskConfig.value?.taskName || defaultTask.title);
|
|
|
|
const taskBackground = computed(() => taskConfig.value?.background || defaultTask.background);
|
|
|
|
const taskBackground = computed(() => taskConfig.value?.background || defaultTask.background);
|
|
|
|
const taskGoals = computed(() => parseArray(taskConfig.value?.objectives, defaultTask.goals));
|
|
|
|
const taskGoals = computed(() => parseArray(taskConfig.value?.objectives, defaultTask.goals));
|
|
|
|
const taskRequirement = computed(() => taskConfig.value?.requirements || defaultTask.requirement);
|
|
|
|
const taskRequirement = computed(() => taskConfig.value?.requirements || defaultTask.requirement);
|
|
|
|
const trainingSteps = computed(() => parseArray(taskConfig.value?.steps, DEFAULT_STEPS));
|
|
|
|
const trainingSteps = computed(() => parseArray(taskConfig.value?.steps, DEFAULT_STEPS));
|
|
|
|
const feedbackByTaskNo = computed(() => Object.fromEntries((checkResult.value?.items || []).map((item) => [item.taskNo, item])));
|
|
|
|
const feedbackByTaskNo = computed(() => Object.fromEntries((checkResult.value?.items || []).map((item) => [item.taskNo, item])));
|
|
|
|
|
|
|
|
const hasCompletedStepOne = computed(() => stepOnePassed.value);
|
|
|
|
const progressItems = computed(() => [
|
|
|
|
const progressItems = computed(() => [
|
|
|
|
{ text: `进行中:${trainingSteps.value[Math.max(0, currentStep.value - 1)] || taskTitle.value}`, status: "doing" },
|
|
|
|
{ text: `进行中:${trainingSteps.value[Math.max(0, currentStep.value - 1)] || taskTitle.value}`, status: "doing" },
|
|
|
|
{ text: "完成本步后进入第 2 步", status: "" },
|
|
|
|
{ text: "完成本步后进入第 2 步", status: "" },
|
|
|
|
@ -129,7 +159,7 @@ onMounted(async () => {
|
|
|
|
watch(() => JSON.stringify(buildDraft()), persistDraft);
|
|
|
|
watch(() => JSON.stringify(buildDraft()), persistDraft);
|
|
|
|
|
|
|
|
|
|
|
|
function createProcessRows() {
|
|
|
|
function createProcessRows() {
|
|
|
|
return PROCESS_TASKS.map(([stage, taskNo, task]) => ({ stage, taskNo, task, classification: "" }));
|
|
|
|
return PROCESS_TASKS.map(([stage, taskNo, task]) => ({ stage, taskNo, task, classification: "", startDate: "", endDate: "", dependency: "" }));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function parseArray(value, fallback) {
|
|
|
|
function parseArray(value, fallback) {
|
|
|
|
@ -153,8 +183,10 @@ async function loadSavedAnswer() {
|
|
|
|
const answer = (await getStudentTrainingAnswer(TASK_KEY))?.data;
|
|
|
|
const answer = (await getStudentTrainingAnswer(TASK_KEY))?.data;
|
|
|
|
if (answer?.step1Answer) {
|
|
|
|
if (answer?.step1Answer) {
|
|
|
|
restoreOutcome(answer.step1Answer);
|
|
|
|
restoreOutcome(answer.step1Answer);
|
|
|
|
|
|
|
|
stepOnePassed.value = processRows.value.every((row) => CLASSIFICATION_OPTIONS.includes(row.classification));
|
|
|
|
clearDraft();
|
|
|
|
clearDraft();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (answer?.step2Answer) restoreStepTwoOutcome(answer.step2Answer);
|
|
|
|
const savedStep = Number(answer?.currentStep);
|
|
|
|
const savedStep = Number(answer?.currentStep);
|
|
|
|
if (Number.isInteger(savedStep) && savedStep > 0) currentStep.value = savedStep;
|
|
|
|
if (Number.isInteger(savedStep) && savedStep > 0) currentStep.value = savedStep;
|
|
|
|
} catch (error) {
|
|
|
|
} catch (error) {
|
|
|
|
@ -173,6 +205,21 @@ function restoreOutcome(value) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function restoreStepTwoOutcome(value) {
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
const parsed = typeof value === "string" ? JSON.parse(value) : value;
|
|
|
|
|
|
|
|
const byTaskNo = Object.fromEntries((Array.isArray(parsed?.items) ? parsed.items : []).map((item) => [item.taskNo, item]));
|
|
|
|
|
|
|
|
processRows.value = processRows.value.map((row) => ({
|
|
|
|
|
|
|
|
...row,
|
|
|
|
|
|
|
|
startDate: byTaskNo[row.taskNo]?.startDate || "",
|
|
|
|
|
|
|
|
endDate: byTaskNo[row.taskNo]?.endDate || "",
|
|
|
|
|
|
|
|
dependency: byTaskNo[row.taskNo]?.dependency || "",
|
|
|
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
|
|
// Ignore a malformed historical second-step answer.
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function getLocalDraftKey() {
|
|
|
|
function getLocalDraftKey() {
|
|
|
|
const info = userStore.userInfo || {};
|
|
|
|
const info = userStore.userInfo || {};
|
|
|
|
return `${TASK_KEY}:draft:${info.userId || info.user_id || info.username || info.userName || "anonymous"}`;
|
|
|
|
return `${TASK_KEY}:draft:${info.userId || info.user_id || info.username || info.userName || "anonymous"}`;
|
|
|
|
@ -182,7 +229,14 @@ function buildOutcome() {
|
|
|
|
return { title: taskTitle.value, items: processRows.value.map(({ taskNo, classification }) => ({ taskNo, classification })) };
|
|
|
|
return { title: taskTitle.value, items: processRows.value.map(({ taskNo, classification }) => ({ taskNo, classification })) };
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function buildDraft() { return { step1: buildOutcome(), currentStep: currentStep.value }; }
|
|
|
|
function buildStepTwoOutcome() {
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
|
|
|
title: taskTitle.value,
|
|
|
|
|
|
|
|
items: processRows.value.map(({ taskNo, classification, startDate, endDate, dependency }) => ({ taskNo, classification, startDate, endDate, dependency })),
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function buildDraft() { return { step1: buildOutcome(), step2: buildStepTwoOutcome(), currentStep: currentStep.value }; }
|
|
|
|
|
|
|
|
|
|
|
|
function persistDraft() {
|
|
|
|
function persistDraft() {
|
|
|
|
if (!draftReady.value) return;
|
|
|
|
if (!draftReady.value) return;
|
|
|
|
@ -195,6 +249,7 @@ function loadDraft() {
|
|
|
|
if (raw) {
|
|
|
|
if (raw) {
|
|
|
|
const draft = JSON.parse(raw);
|
|
|
|
const draft = JSON.parse(raw);
|
|
|
|
restoreOutcome(draft?.step1 || draft);
|
|
|
|
restoreOutcome(draft?.step1 || draft);
|
|
|
|
|
|
|
|
if (draft?.step2) restoreStepTwoOutcome(draft.step2);
|
|
|
|
if (Number(draft?.currentStep) > 0) currentStep.value = Number(draft.currentStep);
|
|
|
|
if (Number(draft?.currentStep) > 0) currentStep.value = Number(draft.currentStep);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} catch (error) { clearDraft(); }
|
|
|
|
} catch (error) { clearDraft(); }
|
|
|
|
@ -226,6 +281,7 @@ async function saveAndGoToStepTwo() {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
await saveStudentTrainingAnswer(TASK_KEY, { step1Answer: JSON.stringify(buildOutcome()), currentStep: 2, submitted: false, saveAction: "SAVE" });
|
|
|
|
await saveStudentTrainingAnswer(TASK_KEY, { step1Answer: JSON.stringify(buildOutcome()), currentStep: 2, submitted: false, saveAction: "SAVE" });
|
|
|
|
clearDraft();
|
|
|
|
clearDraft();
|
|
|
|
|
|
|
|
stepOnePassed.value = true;
|
|
|
|
currentStep.value = 2;
|
|
|
|
currentStep.value = 2;
|
|
|
|
proxy?.$modal?.msgSuccess("已保存,已进入第 2 步");
|
|
|
|
proxy?.$modal?.msgSuccess("已保存,已进入第 2 步");
|
|
|
|
} catch (error) {
|
|
|
|
} catch (error) {
|
|
|
|
@ -239,6 +295,7 @@ async function resetTraining() {
|
|
|
|
processRows.value = createProcessRows();
|
|
|
|
processRows.value = createProcessRows();
|
|
|
|
currentStep.value = 1;
|
|
|
|
currentStep.value = 1;
|
|
|
|
checkResult.value = null;
|
|
|
|
checkResult.value = null;
|
|
|
|
|
|
|
|
stepOnePassed.value = false;
|
|
|
|
clearDraft();
|
|
|
|
clearDraft();
|
|
|
|
try {
|
|
|
|
try {
|
|
|
|
checking.value = true;
|
|
|
|
checking.value = true;
|
|
|
|
@ -251,6 +308,27 @@ async function resetTraining() {
|
|
|
|
proxy?.$modal?.msgSuccess("已清空填写内容");
|
|
|
|
proxy?.$modal?.msgSuccess("已清空填写内容");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async function saveAndGoToStepThree() {
|
|
|
|
|
|
|
|
if (checking.value || !hasCompletedStepOne.value) return;
|
|
|
|
|
|
|
|
checking.value = true;
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
await checkProductDevelopmentProcessStepTwo(buildStepTwoOutcome().items);
|
|
|
|
|
|
|
|
await saveStudentTrainingAnswer(TASK_KEY, { step2Answer: JSON.stringify(buildStepTwoOutcome()), currentStep: 3, submitted: false, saveAction: "SAVE" });
|
|
|
|
|
|
|
|
clearDraft();
|
|
|
|
|
|
|
|
currentStep.value = 3;
|
|
|
|
|
|
|
|
proxy?.$modal?.msgSuccess("已保存,已进入第 3 步");
|
|
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
|
|
proxy?.$modal?.msgError?.(error?.message || "请完整填写任务工期和项目依赖关系");
|
|
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
|
|
checking.value = false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function resetStepTwo() {
|
|
|
|
|
|
|
|
processRows.value = processRows.value.map((row) => ({ ...row, startDate: "", endDate: "", dependency: "" }));
|
|
|
|
|
|
|
|
proxy?.$modal?.msgSuccess("已清空第 2 步填写内容");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function exportOutcome() {
|
|
|
|
function exportOutcome() {
|
|
|
|
const blob = new Blob([JSON.stringify(buildDraft(), null, 2)], { type: "application/json;charset=utf-8" });
|
|
|
|
const blob = new Blob([JSON.stringify(buildDraft(), null, 2)], { type: "application/json;charset=utf-8" });
|
|
|
|
const url = URL.createObjectURL(blob);
|
|
|
|
const url = URL.createObjectURL(blob);
|
|
|
|
@ -274,6 +352,7 @@ function exportOutcome() {
|
|
|
|
.workbench-actions, .task-actions { display: flex; align-items: center; gap: 12px; }.workbench-actions { justify-content: flex-end; }.outline-action, .btn-nav { border: 1px solid #2cc9ff; border-radius: 999px; padding: 10px 18px; color: #dff8ff; background: rgba(8, 87, 121, .55); cursor: pointer; }.outline-action { display: inline-flex; align-items: center; gap: 6px; }
|
|
|
|
.workbench-actions, .task-actions { display: flex; align-items: center; gap: 12px; }.workbench-actions { justify-content: flex-end; }.outline-action, .btn-nav { border: 1px solid #2cc9ff; border-radius: 999px; padding: 10px 18px; color: #dff8ff; background: rgba(8, 87, 121, .55); cursor: pointer; }.outline-action { display: inline-flex; align-items: center; gap: 6px; }
|
|
|
|
.step-indicator { display: flex; align-items: center; gap: 12px; margin: 28px 0; padding: 12px; border: 1px solid rgba(30, 190, 255, .28); border-radius: 22px; }.step-tab { display: flex; align-items: center; flex: 1; gap: 10px; min-width: 0; padding: 10px; border: 0; border-radius: 16px; color: #96c6d8; background: transparent; text-align: left; cursor: pointer; }.step-tab.active { color: #fff; background: linear-gradient(110deg, #148fc6, #0879aa); }.step-marker { display: grid; place-items: center; flex: 0 0 42px; width: 42px; height: 42px; border: 1px solid #4ed7ff; border-radius: 50%; font-weight: 800; }.step-copy { display: grid; min-width: 0; gap: 4px; }.step-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }.step-action { font-size: 12px; color: #5ec9ed; }.step-connector { flex: 0 0 25px; height: 2px; background: rgba(83, 204, 255, .4); }
|
|
|
|
.step-indicator { display: flex; align-items: center; gap: 12px; margin: 28px 0; padding: 12px; border: 1px solid rgba(30, 190, 255, .28); border-radius: 22px; }.step-tab { display: flex; align-items: center; flex: 1; gap: 10px; min-width: 0; padding: 10px; border: 0; border-radius: 16px; color: #96c6d8; background: transparent; text-align: left; cursor: pointer; }.step-tab.active { color: #fff; background: linear-gradient(110deg, #148fc6, #0879aa); }.step-marker { display: grid; place-items: center; flex: 0 0 42px; width: 42px; height: 42px; border: 1px solid #4ed7ff; border-radius: 50%; font-weight: 800; }.step-copy { display: grid; min-width: 0; gap: 4px; }.step-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }.step-action { font-size: 12px; color: #5ec9ed; }.step-connector { flex: 0 0 25px; height: 2px; background: rgba(83, 204, 255, .4); }
|
|
|
|
.process-step-card { padding: 24px; border: 1px solid rgba(31, 185, 249, .32); border-radius: 18px; background: rgba(1, 21, 34, .65); }.step-title-row { display: flex; justify-content: space-between; align-items: flex-start; }.step-title-row h3 { margin: 5px 0 0; color: #fff; font-size: 24px; }.step-chip { border: 1px solid #38caff; border-radius: 999px; padding: 7px 12px; color: #9aeaff; }.process-intro { margin: 24px 0; line-height: 1.8; color: #c5e4ef; }.training-table-wrap { overflow-x: auto; }.process-table { width: 100%; min-width: 740px; border-collapse: collapse; color: #dff5ff; }.process-table th { padding: 14px; color: #fff; background: #079ed9; font-size: 16px; }.process-table td, .process-table th { border: 1px solid rgba(139, 213, 241, .45); text-align: center; }.process-table td { padding: 9px 12px; }.stage-cell { width: 16%; font-weight: 700; }.task-no-cell { width: 15%; }.task-cell { width: 37%; }.select-cell { width: 32%; }.select-cell select { width: 170px; max-width: 100%; padding: 6px 10px; border: 1px solid #6ea9bd; color: #132c36; background: #fff; font-size: 15px; }.classification-feedback { margin: 6px 0 0; color: #ff9b8f; font-size: 12px; }.is-correct { background: rgba(55, 196, 147, .08); }.is-incorrect { background: rgba(255, 91, 82, .1); }.classification-summary { margin: 16px 0 0; color: #ffbc86; }.classification-summary.is-success { color: #6ee8b1; }.task-actions { justify-content: center; margin-top: 26px; }.btn-submit { border-color: #36d9ff; background: linear-gradient(110deg, #0eaae4, #0572ac); font-weight: 700; }.btn-nav:disabled { opacity: .6; cursor: not-allowed; }.step-placeholder { padding: 90px 20px; text-align: center; color: #a8cfdd; }.step-placeholder p { color: #fff; font-size: 20px; }
|
|
|
|
.process-step-card { padding: 24px; border: 1px solid rgba(31, 185, 249, .32); border-radius: 18px; background: rgba(1, 21, 34, .65); }.step-title-row { display: flex; justify-content: space-between; align-items: flex-start; }.step-title-row h3 { margin: 5px 0 0; color: #fff; font-size: 24px; }.step-chip { border: 1px solid #38caff; border-radius: 999px; padding: 7px 12px; color: #9aeaff; }.process-intro { margin: 24px 0; line-height: 1.8; color: #c5e4ef; }.training-table-wrap { overflow-x: auto; }.process-table { width: 100%; min-width: 740px; border-collapse: collapse; color: #dff5ff; }.process-table th { padding: 14px; color: #fff; background: #079ed9; font-size: 16px; }.process-table td, .process-table th { border: 1px solid rgba(139, 213, 241, .45); text-align: center; }.process-table td { padding: 9px 12px; }.stage-cell { width: 16%; font-weight: 700; }.task-no-cell { width: 15%; }.task-cell { width: 37%; }.select-cell { width: 32%; }.select-cell select { width: 170px; max-width: 100%; padding: 6px 10px; border: 1px solid #6ea9bd; color: #132c36; background: #fff; font-size: 15px; }.classification-feedback { margin: 6px 0 0; color: #ff9b8f; font-size: 12px; }.is-correct { background: rgba(55, 196, 147, .08); }.is-incorrect { background: rgba(255, 91, 82, .1); }.classification-summary { margin: 16px 0 0; color: #ffbc86; }.classification-summary.is-success { color: #6ee8b1; }.task-actions { justify-content: center; margin-top: 26px; }.btn-submit { border-color: #36d9ff; background: linear-gradient(110deg, #0eaae4, #0572ac); font-weight: 700; }.btn-nav:disabled { opacity: .6; cursor: not-allowed; }.step-placeholder { padding: 90px 20px; text-align: center; color: #a8cfdd; }.step-placeholder p { color: #fff; font-size: 20px; }
|
|
|
|
|
|
|
|
.step-warning { margin: 0 0 16px; padding: 10px 14px; border: 1px solid rgba(255, 173, 93, .62); border-radius: 8px; color: #ffd19f; background: rgba(187, 102, 27, .16); }.schedule-table { min-width: 1180px; }.schedule-table .stage-cell { width: 13%; }.schedule-table .task-no-cell { width: 9%; }.schedule-table .task-cell { width: 20%; }.classification-cell { width: 13%; font-weight: 700; }.schedule-table input { box-sizing: border-box; width: 150px; max-width: 100%; padding: 7px 8px; border: 1px solid #6ea9bd; color: #132c36; background: #fff; }.schedule-table .dependency-input { width: 190px; }.schedule-table input:disabled { cursor: not-allowed; opacity: .6; }
|
|
|
|
@media (max-width: 1280px) { .product-process { grid-template-columns: 1fr; }.step-indicator { overflow-x: auto; }.step-tab { min-width: 190px; } }
|
|
|
|
@media (max-width: 1280px) { .product-process { grid-template-columns: 1fr; }.step-indicator { overflow-x: auto; }.step-tab { min-width: 190px; } }
|
|
|
|
@media (max-width: 720px) { .product-process { padding: 12px; }.process-workbench { margin: 12px; padding: 14px; }.task-header { padding: 18px; }.workbench-actions { justify-content: flex-start; flex-wrap: wrap; }.step-title-row { gap: 12px; }.step-chip { display: none; } }
|
|
|
|
@media (max-width: 720px) { .product-process { padding: 12px; }.process-workbench { margin: 12px; padding: 14px; }.task-header { padding: 18px; }.workbench-actions { justify-content: flex-start; flex-wrap: wrap; }.step-title-row { gap: 12px; }.step-chip { display: none; } }
|
|
|
|
</style>
|
|
|
|
</style>
|
|
|
|
|