| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="reporting-page"> |
| | | <!-- 页é¢å¤´é¨ --> |
| | | <PageHeader content="çäº§å·¥åæ¥å·¥"> |
| | | </PageHeader> |
| | | <!-- æ¥éª¤æç¤ºå¨ --> |
| | | <div class="step-indicator"> |
| | | <div v-for="(step, index) in steps" |
| | | :key="index" |
| | | class="step-item" |
| | | :class="{ |
| | | 'active': index === activeStep, |
| | | 'completed': index < activeStep |
| | | }"> |
| | | <div class="step-circle"> |
| | | <span v-if="index < activeStep" |
| | | class="step-check"> |
| | | <el-icon> |
| | | <Check /> |
| | | </el-icon> |
| | | </span> |
| | | <span v-else |
| | | class="step-number">{{ index + 1 }}</span> |
| | | </div> |
| | | <div class="step-content"> |
| | | <div class="step-title">{{ step.title }}</div> |
| | | <div class="step-description">{{ step.description }}</div> |
| | | </div> |
| | | <div class="step-line" |
| | | v-if="index < steps.length - 1"></div> |
| | | </div> |
| | | </div> |
| | | <!-- 页é¢å
容 --> |
| | | <div class="page-content"> |
| | | <!-- ç¬¬ä¸æ¥ï¼éæ©ç产订å --> |
| | | <div v-if="activeStep === 0" |
| | | class="step-panel"> |
| | | <div class="panel-header"> |
| | | <div> |
| | | <h3 class="panel-title">éæ©ç产订å</h3> |
| | | <p class="panel-subtitle">请ä»ä»¥ä¸å表ä¸éæ©éè¦æ¥å·¥çç产订å</p> |
| | | </div> |
| | | <div class="header-actions"> |
| | | <el-button @click="activeStep--" |
| | | v-if="activeStep > 0" |
| | | :disabled="isSubmitting"> |
| | | <el-icon> |
| | | <ArrowLeft /> |
| | | </el-icon> ä¸ä¸æ¥ |
| | | </el-button> |
| | | <el-button type="primary" |
| | | @click="handleNextStep" |
| | | v-if="activeStep < 3" |
| | | :disabled="isSubmitting"> |
| | | ä¸ä¸æ¥ <el-icon> |
| | | <ArrowRight /> |
| | | </el-icon> |
| | | </el-button> |
| | | </div> |
| | | </div> |
| | | <el-form :model="form" |
| | | ref="formRef" |
| | | class="form-container"> |
| | | <el-form-item label="ç产订å" |
| | | prop="orderId" |
| | | required> |
| | | <el-select v-model="orderId" |
| | | placeholder="è¯·éæ©ç产订å" |
| | | clearable |
| | | filterable |
| | | class="form-select" |
| | | :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" |
| | | class="step-panel"> |
| | | <div class="panel-header"> |
| | | <div> |
| | | <h3 class="panel-title">å¡«ååºç¡ä¿¡æ¯</h3> |
| | | <p class="panel-subtitle">è¯·å¡«åæ¥å·¥çåºæ¬ä¿¡æ¯</p> |
| | | </div> |
| | | <div class="header-actions"> |
| | | <el-button @click="activeStep--" |
| | | v-if="activeStep > 0" |
| | | :disabled="isSubmitting"> |
| | | <el-icon> |
| | | <ArrowLeft /> |
| | | </el-icon> ä¸ä¸æ¥ |
| | | </el-button> |
| | | <el-button type="primary" |
| | | @click="handleNextStep" |
| | | v-if="activeStep < 3" |
| | | :disabled="isSubmitting"> |
| | | ä¸ä¸æ¥ <el-icon> |
| | | <ArrowRight /> |
| | | </el-icon> |
| | | </el-button> |
| | | </div> |
| | | </div> |
| | | <el-form :model="form" |
| | | :rules="rules" |
| | | ref="formRef" |
| | | class="form-container"> |
| | | <div class="form-grid"> |
| | | <el-form-item label="ç产订åå·" |
| | | prop="npsNo" |
| | | class="form-item"> |
| | | <el-input disabled |
| | | v-model="form.npsNo" |
| | | class="form-input" /> |
| | | </el-form-item> |
| | | <el-form-item label="çç»" |
| | | prop="teamName" |
| | | required |
| | | class="form-item"> |
| | | <el-select v-model="form.teamName" |
| | | placeholder="è¯·éæ©çç»" |
| | | class="form-select"> |
| | | <el-option label="ç½ç" |
| | | value="ç½ç" /> |
| | | <el-option label="å¤ç" |
| | | value="å¤ç" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="产åç¼ç " |
| | | prop="materialCode" |
| | | class="form-item"> |
| | | <el-input disabled |
| | | v-model="form.materialCode" |
| | | class="form-input" /> |
| | | </el-form-item> |
| | | <el-form-item label="产ååç§°" |
| | | prop="productName" |
| | | class="form-item"> |
| | | <el-input disabled |
| | | v-model="form.productName" |
| | | class="form-input" /> |
| | | </el-form-item> |
| | | <el-form-item label="è§æ ¼" |
| | | prop="specification" |
| | | class="form-item"> |
| | | <el-input disabled |
| | | v-model="form.specification" |
| | | class="form-input" /> |
| | | </el-form-item> |
| | | <el-form-item label="å建人" |
| | | prop="createBy" |
| | | required |
| | | class="form-item"> |
| | | <el-select v-model="form.createBy" |
| | | placeholder="è¯·éæ©å建人" |
| | | class="form-select" |
| | | :loading="userLoading"> |
| | | <el-option v-for="user in userList" |
| | | :key="user.id" |
| | | :label="user.nickName || user.userName" |
| | | :value="user.nickName || user.userName" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="å建æ¶é´" |
| | | prop="createTime" |
| | | class="form-item"> |
| | | <el-date-picker disabled |
| | | v-model="form.createTime" |
| | | type="datetime" |
| | | placeholder="è¯·éæ©å建æ¶é´" |
| | | class="form-input" /> |
| | | </el-form-item> |
| | | </div> |
| | | </el-form> |
| | | </div> |
| | | <!-- ç¬¬ä¸æ¥ï¼æ¥çå·¥åºåæ° --> |
| | | <div v-else-if="activeStep === 2" |
| | | class="step-panel process-panel"> |
| | | <div class="panel-header"> |
| | | <div> |
| | | <h3 class="panel-title">å·¥åºåæ°ç®¡ç</h3> |
| | | <p class="panel-subtitle">请æ¥çå¹¶å¡«ååå·¥åºçåæ°ä¿¡æ¯</p> |
| | | </div> |
| | | <div class="header-actions"> |
| | | <el-button @click="activeStep--" |
| | | v-if="activeStep > 0" |
| | | :disabled="isSubmitting"> |
| | | <el-icon> |
| | | <ArrowLeft /> |
| | | </el-icon> ä¸ä¸æ¥ |
| | | </el-button> |
| | | <el-button type="primary" |
| | | @click="handleNextStep" |
| | | v-if="activeStep < 3" |
| | | :disabled="isSubmitting"> |
| | | ä¸ä¸æ¥ <el-icon> |
| | | <ArrowRight /> |
| | | </el-icon> |
| | | </el-button> |
| | | </div> |
| | | </div> |
| | | <div class="process-container"> |
| | | <!-- 左侧工åºå¯¼èª --> |
| | | <div class="process-nav"> |
| | | <div v-for="process in processList" |
| | | :key="process.processId" |
| | | class="process-nav-item" |
| | | :class="{ 'active': activeProcessId === process.processId + '' }" |
| | | @click="handleProcessClick(process.processId)"> |
| | | <span class="process-name">{{ process.processName }}</span> |
| | | <span class="process-badge" |
| | | v-if="getProcessInfo(parseInt(process.processId)).postPersonnel"> |
| | | {{ getProcessInfo(parseInt(process.processId)).postPersonnel }} |
| | | </span> |
| | | </div> |
| | | </div> |
| | | <!-- å³ä¾§å·¥åºå
容 --> |
| | | <div class="process-content"> |
| | | <div v-if="activeProcessId" |
| | | class="process-details"> |
| | | <!-- åºå®åæ° --> |
| | | <div class="param-section"> |
| | | <div class="section-header"> |
| | | <h4 class="section-title">å·¥åºåºæ¬ä¿¡æ¯</h4> |
| | | </div> |
| | | <div class="param-form"> |
| | | <el-form :label-position="'top'"> |
| | | <div class="form-grid"> |
| | | <el-form-item label="å²ä½äººå" |
| | | class="form-item"> |
| | | <el-select v-model="getProcessInfo(parseInt(activeProcessId)).postPersonnel" |
| | | placeholder="è¯·éæ©å²ä½äººå" |
| | | class="form-select" |
| | | :loading="userLoading"> |
| | | <el-option v-for="user in userList" |
| | | :key="user.id" |
| | | :label="user.nickName || user.userName" |
| | | :value="user.nickName || user.userName" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="设å¤å¼å¸¸æ
åµ" |
| | | class="form-item"> |
| | | <el-input v-model="getProcessInfo(parseInt(activeProcessId)).equipmentAbnormality" |
| | | placeholder="请è¾å
¥è®¾å¤å¼å¸¸æ
åµ" |
| | | type="textarea" |
| | | :rows="2" |
| | | class="form-textarea" /> |
| | | </el-form-item> |
| | | <el-form-item label="å½ç设å¤å¤ç½®" |
| | | class="form-item"> |
| | | <el-input v-model="getProcessInfo(parseInt(activeProcessId)).equipmentHandling" |
| | | placeholder="请è¾å
¥å½ç设å¤å¤ç½®" |
| | | type="textarea" |
| | | :rows="2" |
| | | class="form-textarea" /> |
| | | </el-form-item> |
| | | <el-form-item label="å·¥èºäººå交å¾
" |
| | | class="form-item"> |
| | | <el-input v-model="getProcessInfo(parseInt(activeProcessId)).processInstructions" |
| | | placeholder="请è¾å
¥å·¥èºäººå交å¾
" |
| | | type="textarea" |
| | | :rows="2" |
| | | class="form-textarea" /> |
| | | </el-form-item> |
| | | <el-form-item label="ä¸ä¼ æä»¶" |
| | | class="form-item" |
| | | :span="24"> |
| | | <el-upload class="upload-demo upload-block" |
| | | action="#" |
| | | :on-preview="handlePreview" |
| | | :on-remove="handleRemove" |
| | | :file-list="getProcessInfo(parseInt(activeProcessId)).files || []" |
| | | :auto-upload="false" |
| | | :accept="'.jpg,.png'" |
| | | :max-size="500000" |
| | | :on-change="handleFileChange"> |
| | | <el-button type="primary" |
| | | :icon="Upload">ç¹å»ä¸ä¼ </el-button> |
| | | <template #tip> |
| | | <div class="el-upload__tip"> |
| | | åªè½ä¸ä¼ jpg/pngæä»¶ï¼ä¸ä¸è¶
è¿500kb |
| | | </div> |
| | | </template> |
| | | </el-upload> |
| | | </el-form-item> |
| | | </div> |
| | | </el-form> |
| | | </div> |
| | | </div> |
| | | <!-- BOMä¿¡æ¯ --> |
| | | <div class="param-section" |
| | | v-if="getProcessStructures(parseInt(activeProcessId)).length > 0"> |
| | | <div class="section-header"> |
| | | <h4 class="section-title">BOMä¿¡æ¯</h4> |
| | | </div> |
| | | <div class="param-form"> |
| | | <el-form :label-position="'top'"> |
| | | <div class="form-grid"> |
| | | <el-form-item v-for="item in getProcessStructures(parseInt(activeProcessId))" |
| | | :key="item.id" |
| | | :label="`${item.productName} ${item.model}`" |
| | | class="form-item"> |
| | | <div class="consumable-input-group"> |
| | | <el-input-number v-model="getProcessInfo(parseInt(activeProcessId)).consumables[item.id]" |
| | | :min="0" |
| | | :model-value="getConsumableValue(parseInt(activeProcessId), item.id)" |
| | | @change="val => getProcessInfo(parseInt(activeProcessId)).consumables[item.id] = val" |
| | | class="consumable-input" /> |
| | | <span class="consumable-unit">{{ item.unit }}</span> |
| | | </div> |
| | | </el-form-item> |
| | | </div> |
| | | </el-form> |
| | | </div> |
| | | </div> |
| | | <!-- åæ°ç»å表 --> |
| | | <div class="param-section"> |
| | | <div class="section-header"> |
| | | <h4 class="section-title">åæ°ç»ç®¡ç</h4> |
| | | <div class="section-actions"> |
| | | <el-switch v-model="useTableView" |
| | | active-text="è¡¨æ ¼è§å¾" |
| | | inactive-text="å¡çè§å¾" |
| | | inline-prompt /> |
| | | <el-button type="primary" |
| | | @click="addParamGroup(parseInt(activeProcessId))" |
| | | :icon="Plus"> |
| | | æ°å¢åæ°ç» |
| | | </el-button> |
| | | </div> |
| | | </div> |
| | | <!-- å¡çè§å¾ --> |
| | | <div v-if="!useTableView" |
| | | class="param-cards"> |
| | | <div v-for="(group, index) in form.paramGroups[activeProcessId] || []" |
| | | :key="index" |
| | | class="param-card"> |
| | | <div class="card-header"> |
| | | <span class="card-title">åæ°ç» {{ index + 1 }}</span> |
| | | <el-button type="danger" |
| | | size="small" |
| | | @click="removeParamGroup(parseInt(activeProcessId), index)" |
| | | v-if="(form.paramGroups[activeProcessId] || []).length > 1" |
| | | circle> |
| | | <el-icon> |
| | | <Delete /> |
| | | </el-icon> |
| | | </el-button> |
| | | </div> |
| | | <div class="card-body"> |
| | | <div class="param-grid"> |
| | | <el-form-item v-for="param in params" |
| | | :key="param.id" |
| | | :label="param.paramName" |
| | | :label-width="120" |
| | | class="param-item"> |
| | | <template v-if="param.paramType == '1'"> |
| | | <!-- æ°åç±»å --> |
| | | <div class="param-input-group"> |
| | | <el-input-number v-model="form.paramGroups[activeProcessId][index][param.id]" |
| | | controls-position="right" |
| | | :precision="getPrecision(param.paramFormat)" |
| | | class="param-input" /> |
| | | <span v-if="param.unit && param.unit != '/'" |
| | | class="param-unit"> |
| | | {{ param.unit }} |
| | | </span> |
| | | </div> |
| | | </template> |
| | | <template v-else-if="param.paramType == '2'"> |
| | | <!-- ææ¬ç±»å --> |
| | | <div class="param-input-group"> |
| | | <el-input v-model="form.paramGroups[activeProcessId][index][param.id]" |
| | | class="param-input" /> |
| | | <span v-if="param.unit && param.unit != '/'" |
| | | class="param-unit"> |
| | | {{ param.unit }} |
| | | </span> |
| | | </div> |
| | | </template> |
| | | <template v-else-if="param.paramType == '3'"> |
| | | <!-- åå
¸ç±»å --> |
| | | <div class="param-input-group"> |
| | | <el-select v-model="form.paramGroups[activeProcessId][index][param.id]" |
| | | placeholder="è¯·éæ©" |
| | | class="param-select" |
| | | style="width: 100%"> |
| | | <el-option v-for="option in dictOptions[param.paramFormat] || []" |
| | | :key="option.dictValue" |
| | | :label="option.dictLabel" |
| | | :value="option.dictValue" /> |
| | | </el-select> |
| | | <span v-if="param.unit && param.unit != '/'" |
| | | class="param-unit"> |
| | | {{ param.unit }} |
| | | </span> |
| | | </div> |
| | | </template> |
| | | <template v-else-if="param.paramType == '4'"> |
| | | <!-- æ¥æç±»å --> |
| | | <div class="param-input-group"> |
| | | <el-date-picker :value-format="param.paramFormat" |
| | | :format="param.paramFormat" |
| | | :type="param.paramFormat=='YYYY-MM-DD'?'date':'datetime'" |
| | | v-model="form.paramGroups[activeProcessId][index][param.id]" |
| | | class="param-input" /> |
| | | <span v-if="param.unit && param.unit != '/'" |
| | | class="param-unit"> |
| | | {{ param.unit }} |
| | | </span> |
| | | </div> |
| | | </template> |
| | | <template v-else> |
| | | <!-- å
¶ä»ç±»å --> |
| | | <div class="param-input-group"> |
| | | <el-input v-model="form.paramGroups[activeProcessId][index][param.id]" |
| | | class="param-input" /> |
| | | <span v-if="param.unit && param.unit != '/'" |
| | | class="param-unit"> |
| | | {{ param.unit }} |
| | | </span> |
| | | </div> |
| | | </template> |
| | | </el-form-item> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <!-- è¡¨æ ¼è§å¾ --> |
| | | <div v-else |
| | | class="param-table"> |
| | | <el-table :data="form.paramGroups[activeProcessId] || []" |
| | | style="width: 100%" |
| | | class="table-view"> |
| | | <!-- æä½å --> |
| | | <el-table-column label="æä½" |
| | | width="100" |
| | | fixed> |
| | | <template #default="{ $index }"> |
| | | <el-button type="danger" |
| | | size="small" |
| | | @click="removeParamGroup(parseInt(activeProcessId), $index)" |
| | | circle> |
| | | <el-icon> |
| | | <Delete /> |
| | | </el-icon> |
| | | </el-button> |
| | | </template> |
| | | </el-table-column> |
| | | <!-- åæ°å --> |
| | | <el-table-column v-for="param in params" |
| | | :key="param.id" |
| | | :label="param.paramName" |
| | | min-width="200"> |
| | | <template #default="{ row }"> |
| | | <template v-if="param.paramType == '1'"> |
| | | <!-- æ°åç±»å --> |
| | | <el-input-number v-model="row[param.id]" |
| | | controls-position="right" |
| | | :precision="getPrecision(param.paramFormat)" |
| | | class="table-input" /> |
| | | </template> |
| | | <template v-else-if="param.paramType == '2'"> |
| | | <!-- ææ¬ç±»å --> |
| | | <el-input v-model="row[param.id]" |
| | | class="table-input" /> |
| | | </template> |
| | | <template v-else-if="param.paramType == '3'"> |
| | | <!-- åå
¸ç±»å --> |
| | | <el-select v-model="row[param.id]" |
| | | placeholder="è¯·éæ©" |
| | | class="table-select"> |
| | | <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 :value-format="param.paramFormat" |
| | | :format="param.paramFormat" |
| | | width="100%" |
| | | :type="param.paramFormat=='YYYY-MM-DD'?'date':'datetime'" |
| | | v-model="row[param.id]" |
| | | class="table-input table-select" /> |
| | | </template> |
| | | <template v-else> |
| | | <!-- å
¶ä»ç±»å --> |
| | | <el-input v-model="row[param.id]" |
| | | class="table-input" /> |
| | | </template> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </div> |
| | | <!-- æ°å¢åæ°ç»æé® --> |
| | | <!-- <div class="param-actions"> |
| | | <el-button type="primary" |
| | | @click="addParamGroup(parseInt(activeProcessId))" |
| | | :icon="Plus"> |
| | | æ°å¢åæ°ç» |
| | | </el-button> |
| | | </div> --> |
| | | </div> |
| | | </div> |
| | | <div v-else |
| | | class="empty-process"> |
| | | <el-empty description="è¯·éæ©ä¸ä¸ªå·¥åº" |
| | | :image-size="120" /> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <!-- ç¬¬åæ¥ï¼å¡«å产éä¿¡æ¯ --> |
| | | <div v-else-if="activeStep === 3" |
| | | class="step-panel"> |
| | | <div class="panel-header"> |
| | | <div> |
| | | <h3 class="panel-title">å¡«å产éä¿¡æ¯</h3> |
| | | <p class="panel-subtitle">è¯·å¡«åæ¬æ¬¡æ¥å·¥çäº§éæ°æ®</p> |
| | | </div> |
| | | <div class="header-actions"> |
| | | <el-button @click="activeStep--" |
| | | v-if="activeStep > 0" |
| | | :disabled="isSubmitting"> |
| | | <el-icon> |
| | | <ArrowLeft /> |
| | | </el-icon> ä¸ä¸æ¥ |
| | | </el-button> |
| | | <el-button type="primary" |
| | | @click="handleSubmit" |
| | | v-if="activeStep === 3" |
| | | :loading="isSubmitting"> |
| | | <el-icon v-if="!isSubmitting"> |
| | | <Check /> |
| | | </el-icon> |
| | | <el-icon v-else> |
| | | <Loading /> |
| | | </el-icon> |
| | | {{ isSubmitting ? 'æäº¤ä¸...' : '确认æäº¤' }} |
| | | </el-button> |
| | | </div> |
| | | </div> |
| | | <el-form :model="form" |
| | | :rules="rules" |
| | | ref="formRef" |
| | | :label-position="'top'" |
| | | class="form-container"> |
| | | <div class="form-grid1"> |
| | | <el-form-item label="äº§åºæ¹é" |
| | | prop="outputVolume" |
| | | required |
| | | class="form-item"> |
| | | <div class="volume-input-group"> |
| | | <el-input-number v-model="form.outputVolume" |
| | | :min="0" |
| | | :precision="2" |
| | | class="volume-input" /> |
| | | <span class="volume-unit">æ¹</span> |
| | | </div> |
| | | </el-form-item> |
| | | <el-form-item label="ä¸åæ ¼æ¹é" |
| | | prop="unqualifiedVolume" |
| | | required |
| | | class="form-item"> |
| | | <div class="volume-input-group"> |
| | | <el-input-number v-model="form.unqualifiedVolume" |
| | | :min="0" |
| | | :precision="2" |
| | | class="volume-input" /> |
| | | <span class="volume-unit">æ¹</span> |
| | | </div> |
| | | </el-form-item> |
| | | <el-form-item label="宿æ¹é" |
| | | prop="completedVolume" |
| | | required |
| | | class="form-item"> |
| | | <div class="volume-input-group"> |
| | | <el-input-number v-model="form.completedVolume" |
| | | :min="0" |
| | | :precision="2" |
| | | class="volume-input" /> |
| | | <span class="volume-unit">æ¹</span> |
| | | </div> |
| | | </el-form-item> |
| | | </div> |
| | | </el-form> |
| | | </div> |
| | | </div> |
| | | <!-- åºé¨æé® --> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, computed, watch, onMounted } from "vue"; |
| | | import { ElMessage, ElEmpty } from "element-plus"; |
| | | import { useRouter, useRoute } from "vue-router"; |
| | | import { getDicts } from "@/api/system/dict/data"; |
| | | import { productOrderListPage } from "@/api/productionManagement/productionOrder.js"; |
| | | import { productionRecordAdd } from "@/api/productionManagement/productProcessRoute.js"; |
| | | import { userListNoPage } from "@/api/system/user.js"; |
| | | import { getInfo } from "@/api/login.js"; |
| | | import { |
| | | Check, |
| | | Close, |
| | | Delete, |
| | | Plus, |
| | | ArrowLeft, |
| | | ArrowRight, |
| | | Loading, |
| | | Upload, |
| | | } from "@element-plus/icons-vue"; |
| | | |
| | | const router = useRouter(); |
| | | |
| | | // ä»è·¯ç±åæ°è·åæ°æ® |
| | | const route = useRoute(); |
| | | const data = route.query.data ? JSON.parse(route.query.data) : {}; |
| | | |
| | | const dialogTitle = computed(() => (data.id ? "ç¼è¾æ¥å·¥" : "æ°å¢æ¥å·¥")); |
| | | |
| | | const formRef = ref(null); |
| | | const isSubmitting = ref(false); |
| | | const orderLoading = ref(false); |
| | | const processLoading = ref(false); |
| | | const activeStep = ref(0); |
| | | |
| | | const steps = [ |
| | | { title: "éæ©ç产订å", description: "éæ©éè¦æ¥å·¥çç产订å" }, |
| | | { title: "å¡«ååºç¡ä¿¡æ¯", description: "填忥工çåºæ¬ä¿¡æ¯" }, |
| | | { title: "æ¥çå·¥åºåæ°", description: "å¡«ååå·¥åºçåæ°ä¿¡æ¯" }, |
| | | { title: "å¡«å产éä¿¡æ¯", description: "å¡«åæ¬æ¬¡æ¥å·¥çäº§éæ°æ®" }, |
| | | ]; |
| | | |
| | | // 计ç®å½åå·¥åºçåæ°ç»æ°é |
| | | const paramGroupCount = computed(() => { |
| | | if (!activeProcessId.value) return 0; |
| | | return (form.paramGroups[activeProcessId.value] || []).length; |
| | | }); |
| | | |
| | | const orderId = ref(data.orderId || ""); |
| | | const processId = ref(data.processId || ""); |
| | | const activeProcessId = ref(""); |
| | | const orderList = ref([]); |
| | | const processList = ref([]); |
| | | const params = ref([]); |
| | | const dictOptions = ref({}); |
| | | const userList = ref([]); |
| | | const userLoading = ref(false); |
| | | const useTableView = ref(false); // æ§å¶æ¯å¦ä½¿ç¨è¡¨æ ¼è§å¾ |
| | | |
| | | const form = reactive({ |
| | | id: data.id || undefined, |
| | | orderId: data.orderId || "", |
| | | npsNo: data.npsNo || "", |
| | | teamName: data.teamName || "", |
| | | materialCode: data.materialCode || "", |
| | | productName: data.productName || "", |
| | | specification: data.specification || "", |
| | | outputVolume: data.outputVolume || 0, |
| | | unqualifiedVolume: data.unqualifiedVolume || 0, |
| | | completedVolume: data.completedVolume || 0, |
| | | createBy: data.createBy || "å½åç»å½äºº", |
| | | createTime: data.createTime || new Date(), |
| | | paramGroups: data.paramGroups || {}, // å卿¯ä¸ªå·¥åºçåæ°ç» |
| | | processInfo: data.processInfo || {}, // å卿¯ä¸ªå·¥åºçåºæ¬ä¿¡æ¯ |
| | | }); |
| | | |
| | | 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; |
| | | // è°ç¨æ°çæ¥å£ |
| | | productionRecordAdd(orderId) |
| | | .then(res => { |
| | | if (res.code === 200) { |
| | | const data = res.data; |
| | | // æåå·¥åºå表 |
| | | processList.value = data.productionOrderRouteItemVos || []; |
| | | // åå¨å·¥åºç»ææ°æ® |
| | | form.processStructures = {}; |
| | | processList.value.forEach(process => { |
| | | form.processStructures[process.processId] = |
| | | process.orderStructureVos || []; |
| | | }); |
| | | // 妿æå·¥åºï¼é»è®¤éæ©ç¬¬ä¸ä¸ª |
| | | if (processList.value.length > 0) { |
| | | const firstProcess = processList.value[0]; |
| | | activeProcessId.value = firstProcess.processId + ""; |
| | | processId.value = firstProcess.processId; |
| | | form.processId = firstProcess.processId; |
| | | // å 载第ä¸ä¸ªå·¥åºçåæ° |
| | | loadParams(firstProcess.processId, orderId); |
| | | } |
| | | } |
| | | }) |
| | | .finally(() => { |
| | | processLoading.value = false; |
| | | }); |
| | | }; |
| | | |
| | | // å¤çå·¥åºå¯¼èªç¹å» |
| | | const handleProcessClick = selectedProcessId => { |
| | | activeProcessId.value = selectedProcessId + ""; |
| | | processId.value = selectedProcessId; |
| | | form.processId = selectedProcessId; |
| | | // å è½½åæ°å表 |
| | | loadParams(selectedProcessId, form.orderId); |
| | | }; |
| | | |
| | | // è·åå·¥åºåºæ¬ä¿¡æ¯ï¼ä¸åå¨ååå§å |
| | | const getProcessInfo = processId => { |
| | | if (!form.processInfo) { |
| | | form.processInfo = {}; |
| | | } |
| | | if (!form.processInfo[processId]) { |
| | | form.processInfo[processId] = { |
| | | postPersonnel: "", |
| | | equipmentAbnormality: "", |
| | | equipmentHandling: "", |
| | | processInstructions: "", |
| | | files: [], |
| | | consumables: {}, |
| | | }; |
| | | } |
| | | return form.processInfo[processId]; |
| | | }; |
| | | |
| | | // è·åå·¥åºç»ææ°æ®ï¼BOMåè¡¨ï¼ |
| | | const getProcessStructures = processId => { |
| | | return form.processStructures && form.processStructures[processId] |
| | | ? form.processStructures[processId] |
| | | : []; |
| | | }; |
| | | |
| | | // è·åæ¶èåæ°éï¼é»è®¤ä¸º0 |
| | | const getConsumableValue = (processId, itemId) => { |
| | | const processInfo = getProcessInfo(processId); |
| | | if (!processInfo.consumables[itemId]) { |
| | | processInfo.consumables[itemId] = 0; |
| | | } |
| | | return processInfo.consumables[itemId]; |
| | | }; |
| | | |
| | | // å¤çæä»¶é¢è§ |
| | | const handlePreview = file => { |
| | | // æ£æ¥æ¯å¦æ¯å¾çæä»¶ |
| | | if (file.raw && file.raw.type.startsWith("image/")) { |
| | | // å建å¾çé¢è§ |
| | | const imageUrl = URL.createObjectURL(file.raw); |
| | | const image = new Image(); |
| | | image.src = imageUrl; |
| | | |
| | | // å建é¢è§å®¹å¨ |
| | | const previewContainer = document.createElement("div"); |
| | | previewContainer.style.position = "fixed"; |
| | | previewContainer.style.top = "0"; |
| | | previewContainer.style.left = "0"; |
| | | previewContainer.style.width = "100%"; |
| | | previewContainer.style.height = "100%"; |
| | | previewContainer.style.backgroundColor = "rgba(0, 0, 0, 0.8)"; |
| | | previewContainer.style.display = "flex"; |
| | | previewContainer.style.alignItems = "center"; |
| | | previewContainer.style.justifyContent = "center"; |
| | | previewContainer.style.zIndex = "9999"; |
| | | previewContainer.style.cursor = "pointer"; |
| | | |
| | | // æ·»å å¾ç |
| | | previewContainer.appendChild(image); |
| | | image.style.maxWidth = "90%"; |
| | | image.style.maxHeight = "90%"; |
| | | |
| | | // æ·»å å
³éåè½ |
| | | previewContainer.addEventListener("click", () => { |
| | | URL.revokeObjectURL(imageUrl); |
| | | document.body.removeChild(previewContainer); |
| | | }); |
| | | |
| | | // æ·»å å°ææ¡£ |
| | | document.body.appendChild(previewContainer); |
| | | } |
| | | }; |
| | | |
| | | // å¤çæä»¶å é¤ |
| | | const handleRemove = (file, fileList) => { |
| | | const processId = parseInt(activeProcessId.value); |
| | | if (processId) { |
| | | const processInfo = getProcessInfo(processId); |
| | | processInfo.files = fileList; |
| | | } |
| | | }; |
| | | |
| | | // å¤çæä»¶åæ´ |
| | | const handleFileChange = (file, fileList) => { |
| | | const processId = parseInt(activeProcessId.value); |
| | | if (processId) { |
| | | const processInfo = getProcessInfo(processId); |
| | | processInfo.files = fileList; |
| | | } |
| | | }; |
| | | |
| | | // è·ååå
¸æ°æ® |
| | | 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) => { |
| | | // ä»å·²å è½½çå·¥åºæ°æ®ä¸è·ååæ°å表 |
| | | const process = processList.value.find( |
| | | p => p.processId === parseInt(processId) |
| | | ); |
| | | if (process) { |
| | | params.value = process.orderRouteItemParaVos || []; |
| | | |
| | | // åå§ååæ°ç» |
| | | if (!form.paramGroups[processId]) { |
| | | form.paramGroups[processId] = []; |
| | | } |
| | | // å¦ææ²¡æåæ°ç»ï¼æ·»å ä¸ä¸ªé»è®¤åæ°ç» |
| | | if (form.paramGroups[processId].length === 0) { |
| | | const defaultGroup = {}; |
| | | for (const param of params.value) { |
| | | defaultGroup[param.id] = param.standardValue || ""; |
| | | // 妿æ¯åå
¸ç±»ååæ°ï¼è·ååå
¸æ°æ® |
| | | if (param.paramType == "3" && param.paramFormat) { |
| | | getDictOptions(param.paramFormat); |
| | | } |
| | | } |
| | | form.paramGroups[processId].push(defaultGroup); |
| | | } |
| | | } |
| | | }; |
| | | |
| | | // è·åå°æ°ç²¾åº¦ |
| | | 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) { |
| | | isSubmitting.value = true; |
| | | // è¿éå¯ä»¥è°ç¨APIè¿è¡æäº¤ |
| | | setTimeout(() => { |
| | | ElMessage.success(data.id ? "ä¿®æ¹æå" : "æ°å¢æå"); |
| | | router.back(); |
| | | isSubmitting.value = false; |
| | | }, 1000); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | // å¤çåæ¶ |
| | | const handleCancel = () => { |
| | | router.back(); |
| | | }; |
| | | |
| | | // æ°å¢åæ°ç» |
| | | const addParamGroup = processId => { |
| | | if (!form.paramGroups[processId]) { |
| | | form.paramGroups[processId] = []; |
| | | } |
| | | // å建ä¸ä¸ªæ°çåæ°ç»ï¼ä½¿ç¨é»è®¤å¼ |
| | | const newGroup = {}; |
| | | params.value.forEach(param => { |
| | | newGroup[param.id] = param.standardValue || ""; |
| | | }); |
| | | form.paramGroups[processId].push(newGroup); |
| | | }; |
| | | |
| | | // å é¤åæ°ç» |
| | | const removeParamGroup = (processId, index) => { |
| | | if (form.paramGroups[processId] && form.paramGroups[processId].length > 1) { |
| | | form.paramGroups[processId].splice(index, 1); |
| | | } |
| | | }; |
| | | |
| | | // å è½½ç¨æ·å表 |
| | | const loadUsers = () => { |
| | | userLoading.value = true; |
| | | userListNoPage() |
| | | .then(res => { |
| | | userList.value = res.data || []; |
| | | }) |
| | | .finally(() => { |
| | | userLoading.value = false; |
| | | }); |
| | | }; |
| | | |
| | | // è·åå½åç»å½äººä¿¡æ¯ |
| | | const getCurrentUser = async () => { |
| | | try { |
| | | const res = await getInfo(); |
| | | if (res && res.user) { |
| | | form.createBy = res.user.nickName || res.user.userName; |
| | | } |
| | | } catch (error) { |
| | | console.error("è·åå½åç»å½äººä¿¡æ¯å¤±è´¥:", error); |
| | | } |
| | | }; |
| | | |
| | | // åå§å |
| | | const init = () => { |
| | | // æ 论æ°å¢è¿æ¯ç¼è¾ï¼é½å 载订åå表åç¨æ·å表 |
| | | loadOrders(); |
| | | loadUsers(); |
| | | getCurrentUser(); |
| | | |
| | | if (data.id) { |
| | | // ç¼è¾æ¶è®¾ç½®è¡¨åæ°æ® |
| | | Object.assign(form, data); |
| | | // 设置orderId |
| | | orderId.value = data.orderId || ""; |
| | | // 妿æè®¢åIDï¼å 载工åºååæ° |
| | | if (data.orderId) { |
| | | // 模æéæ©è®¢åçæä½ï¼è§¦åæ°æ®å è½½ |
| | | setTimeout(() => { |
| | | handleOrderChange(data.orderId); |
| | | }, 100); |
| | | } |
| | | } else { |
| | | // æ°å¢æ¶è®¾ç½®é»è®¤å¼ |
| | | form.createTime = new Date(); |
| | | } |
| | | // å§ç»ä»ç¬¬ä¸æ¥å¼å§ |
| | | activeStep.value = 0; |
| | | }; |
| | | |
| | | // 页é¢å è½½æ¶åå§å |
| | | onMounted(() => { |
| | | init(); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped> |
| | | /* 页é¢å®¹å¨ */ |
| | | .reporting-page { |
| | | min-height: 100vh; |
| | | padding: 24px; |
| | | background-color: #f0f2f5; |
| | | } |
| | | |
| | | /* 页é¢å¤´é¨ */ |
| | | .page-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 32px; |
| | | padding-bottom: 16px; |
| | | border-bottom: 1px solid #e8e8e8; |
| | | } |
| | | |
| | | .page-title { |
| | | margin: 0; |
| | | font-size: 24px; |
| | | font-weight: 600; |
| | | color: #1f2329; |
| | | } |
| | | |
| | | .header-actions { |
| | | display: flex; |
| | | gap: 12px; |
| | | } |
| | | |
| | | /* æ¥éª¤æç¤ºå¨ */ |
| | | .step-indicator { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | margin-bottom: 32px; |
| | | padding: 0 16px; |
| | | padding-top: 16px; |
| | | } |
| | | |
| | | .step-item { |
| | | display: flex; |
| | | align-items: center; |
| | | flex: 1; |
| | | position: relative; |
| | | } |
| | | |
| | | .step-circle { |
| | | width: 40px; |
| | | height: 40px; |
| | | border-radius: 50%; |
| | | background-color: #f0f0f0; |
| | | color: #999; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | font-size: 16px; |
| | | font-weight: 600; |
| | | z-index: 2; |
| | | transition: all 0.3s ease; |
| | | box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); |
| | | } |
| | | |
| | | .step-item.active .step-circle { |
| | | background-color: #1890ff; |
| | | color: white; |
| | | box-shadow: 0 0 0 8px rgba(24, 144, 255, 0.1); |
| | | } |
| | | |
| | | .step-item.completed .step-circle { |
| | | background-color: #52c41a; |
| | | color: white; |
| | | box-shadow: 0 0 0 8px rgba(82, 196, 26, 0.1); |
| | | } |
| | | |
| | | .step-check { |
| | | font-size: 20px; |
| | | } |
| | | |
| | | .step-content { |
| | | margin-left: 16px; |
| | | flex: 1; |
| | | } |
| | | |
| | | .step-title { |
| | | font-size: 14px; |
| | | font-weight: 600; |
| | | color: #666; |
| | | margin-bottom: 4px; |
| | | transition: color 0.3s ease; |
| | | } |
| | | |
| | | .step-item.active .step-title { |
| | | color: #1890ff; |
| | | } |
| | | |
| | | .step-item.completed .step-title { |
| | | color: #52c41a; |
| | | } |
| | | |
| | | .step-description { |
| | | font-size: 12px; |
| | | color: #999; |
| | | } |
| | | |
| | | .step-line { |
| | | position: absolute; |
| | | top: 20px; |
| | | left: 50%; |
| | | right: -50%; |
| | | height: 2px; |
| | | background-color: #e8e8e8; |
| | | z-index: 1; |
| | | transition: all 0.3s ease; |
| | | } |
| | | |
| | | .step-item.completed .step-line { |
| | | background-color: #52c41a; |
| | | } |
| | | |
| | | /* 页é¢å
容 */ |
| | | .page-content { |
| | | background-color: white; |
| | | border-radius: 12px; |
| | | padding: 0; |
| | | box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08); |
| | | margin-bottom: 32px; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | /* æ¥éª¤é¢æ¿ */ |
| | | .step-panel { |
| | | padding: 32px; |
| | | } |
| | | |
| | | .panel-header { |
| | | margin-bottom: 24px; |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: flex-start; |
| | | } |
| | | |
| | | .panel-title { |
| | | font-size: 18px; |
| | | font-weight: 600; |
| | | color: #1f2329; |
| | | margin: 0 0 8px 0; |
| | | } |
| | | |
| | | .panel-subtitle { |
| | | font-size: 14px; |
| | | color: #666; |
| | | margin: 0; |
| | | } |
| | | |
| | | .header-actions { |
| | | display: flex; |
| | | gap: 12px; |
| | | align-items: center; |
| | | } |
| | | |
| | | /* 表åå®¹å¨ */ |
| | | .form-container { |
| | | width: 100%; |
| | | } |
| | | |
| | | /* ä¸ä¼ ç»ä»¶æ ·å¼ */ |
| | | .upload-block { |
| | | display: block; |
| | | } |
| | | |
| | | .upload-block .el-upload__tip { |
| | | margin-top: 8px; |
| | | } |
| | | |
| | | /* BOMè¾å
¥ç»æ ·å¼ */ |
| | | .consumable-input-group { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 8px; |
| | | } |
| | | |
| | | .consumable-input { |
| | | flex: 1; |
| | | } |
| | | |
| | | .consumable-unit { |
| | | font-size: 14px; |
| | | color: #666; |
| | | white-space: nowrap; |
| | | } |
| | | |
| | | .form-grid { |
| | | display: grid; |
| | | grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); |
| | | gap: 20px; |
| | | } |
| | | .form-grid1 { |
| | | display: grid; |
| | | grid-template-columns: repeat(auto-fill, minmax(400px, 1fr)); |
| | | gap: 20px; |
| | | } |
| | | |
| | | .form-item { |
| | | margin-bottom: 0; |
| | | } |
| | | |
| | | .form-select, |
| | | .form-input { |
| | | width: 100%; |
| | | } |
| | | |
| | | .form-textarea { |
| | | width: 100%; |
| | | resize: vertical; |
| | | } |
| | | |
| | | /* 产éè¾å
¥ç» */ |
| | | .volume-input-group { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 8px; |
| | | } |
| | | |
| | | .volume-input { |
| | | flex: 1; |
| | | width: 100%; |
| | | } |
| | | |
| | | .volume-unit { |
| | | font-size: 14px; |
| | | color: #666; |
| | | white-space: nowrap; |
| | | } |
| | | |
| | | /* å·¥åºå®¹å¨ */ |
| | | .process-container { |
| | | display: flex; |
| | | gap: 24px; |
| | | min-height: 500px; |
| | | } |
| | | |
| | | /* å·¥åºå¯¼èª */ |
| | | .process-nav { |
| | | width: 200px; |
| | | background-color: #fafafa; |
| | | border-radius: 8px; |
| | | padding: 16px; |
| | | box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05); |
| | | overflow-y: auto; |
| | | } |
| | | |
| | | .process-nav-item { |
| | | padding: 12px 16px; |
| | | margin-bottom: 8px; |
| | | border-radius: 6px; |
| | | cursor: pointer; |
| | | transition: all 0.3s ease; |
| | | font-size: 14px; |
| | | font-weight: 500; |
| | | color: #666; |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | } |
| | | |
| | | .process-nav-item:hover { |
| | | background-color: #e6f7ff; |
| | | color: #1890ff; |
| | | } |
| | | |
| | | .process-nav-item.active { |
| | | background-color: #1890ff; |
| | | color: white; |
| | | box-shadow: 0 2px 8px rgba(24, 144, 255, 0.3); |
| | | } |
| | | |
| | | .process-badge { |
| | | font-size: 12px; |
| | | background-color: rgba(255, 255, 255, 0.2); |
| | | padding: 2px 8px; |
| | | border-radius: 10px; |
| | | } |
| | | |
| | | /* å·¥åºå
容 */ |
| | | .process-content { |
| | | flex: 1; |
| | | overflow-y: auto; |
| | | } |
| | | |
| | | .empty-process { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | height: 400px; |
| | | } |
| | | |
| | | /* åæ°é¨å */ |
| | | .param-section { |
| | | margin-bottom: 32px; |
| | | padding: 24px; |
| | | background-color: #fafafa; |
| | | border-radius: 8px; |
| | | } |
| | | |
| | | .section-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 16px; |
| | | } |
| | | |
| | | .section-title { |
| | | font-size: 16px; |
| | | font-weight: 600; |
| | | color: #1f2329; |
| | | margin: 0; |
| | | } |
| | | |
| | | .section-actions { |
| | | display: flex; |
| | | gap: 8px; |
| | | } |
| | | |
| | | /* åæ°è¡¨å */ |
| | | .param-form { |
| | | background-color: white; |
| | | padding: 20px; |
| | | border-radius: 8px; |
| | | box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05); |
| | | } |
| | | |
| | | /* åæ°å¡ç */ |
| | | .param-cards { |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 16px; |
| | | } |
| | | |
| | | .param-card { |
| | | background-color: white; |
| | | border-radius: 8px; |
| | | box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08); |
| | | overflow: hidden; |
| | | transition: all 0.3s ease; |
| | | } |
| | | |
| | | .param-card:hover { |
| | | box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12); |
| | | transform: translateY(-2px); |
| | | } |
| | | |
| | | .card-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | padding: 16px 20px; |
| | | background-color: #fafafa; |
| | | border-bottom: 1px solid #e8e8e8; |
| | | } |
| | | |
| | | .card-title { |
| | | font-size: 14px; |
| | | font-weight: 600; |
| | | color: #1f2329; |
| | | } |
| | | |
| | | .card-body { |
| | | padding: 20px; |
| | | } |
| | | |
| | | /* åæ°ç½æ ¼ */ |
| | | .param-grid { |
| | | display: grid; |
| | | grid-template-columns: repeat(auto-fill, minmax(350px, 1fr)); |
| | | gap: 16px; |
| | | } |
| | | |
| | | .param-item { |
| | | margin-bottom: 0; |
| | | } |
| | | |
| | | .param-input-group { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 8px; |
| | | width: 100%; |
| | | } |
| | | |
| | | .param-input, |
| | | .param-select { |
| | | flex: 1; |
| | | } |
| | | |
| | | .param-unit { |
| | | font-size: 14px; |
| | | color: #666; |
| | | white-space: nowrap; |
| | | } |
| | | |
| | | /* è¡¨æ ¼è§å¾ */ |
| | | .param-table { |
| | | margin: 16px 0; |
| | | } |
| | | |
| | | .table-view { |
| | | border-radius: 8px; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .table-view th { |
| | | background-color: #fafafa; |
| | | font-weight: 600; |
| | | } |
| | | |
| | | .table-input, |
| | | .table-select { |
| | | width: 100%; |
| | | } |
| | | |
| | | /* åæ°æä½ */ |
| | | .param-actions { |
| | | margin-top: 16px; |
| | | display: flex; |
| | | justify-content: flex-end; |
| | | } |
| | | |
| | | /* 页é¢åºé¨ */ |
| | | .page-footer { |
| | | display: flex; |
| | | justify-content: center; |
| | | padding: 24px; |
| | | background-color: white; |
| | | border-top: 1px solid #e8e8e8; |
| | | border-radius: 0 0 12px 12px; |
| | | } |
| | | |
| | | .footer-actions { |
| | | display: flex; |
| | | gap: 12px; |
| | | } |
| | | |
| | | /* ååºå¼è®¾è®¡ */ |
| | | @media (max-width: 1024px) { |
| | | .form-grid { |
| | | grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); |
| | | } |
| | | |
| | | .param-grid { |
| | | grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); |
| | | } |
| | | } |
| | | |
| | | @media (max-width: 768px) { |
| | | .reporting-page { |
| | | padding: 16px; |
| | | } |
| | | |
| | | .step-indicator { |
| | | flex-direction: column; |
| | | align-items: flex-start; |
| | | gap: 16px; |
| | | padding: 0; |
| | | } |
| | | |
| | | .step-item { |
| | | flex-direction: row; |
| | | width: 100%; |
| | | } |
| | | |
| | | .step-line { |
| | | display: none; |
| | | } |
| | | |
| | | .step-panel { |
| | | padding: 20px; |
| | | } |
| | | |
| | | .form-grid { |
| | | grid-template-columns: 1fr; |
| | | } |
| | | |
| | | .process-container { |
| | | flex-direction: column; |
| | | } |
| | | |
| | | .process-nav { |
| | | width: 100%; |
| | | margin-bottom: 16px; |
| | | } |
| | | |
| | | .param-grid { |
| | | grid-template-columns: 1fr; |
| | | } |
| | | |
| | | .footer-actions { |
| | | flex-direction: column; |
| | | width: 100%; |
| | | } |
| | | |
| | | .footer-actions button { |
| | | width: 100%; |
| | | } |
| | | } |
| | | |
| | | /* æ»å¨æ¡æ ·å¼ */ |
| | | .process-nav::-webkit-scrollbar, |
| | | .process-content::-webkit-scrollbar { |
| | | width: 6px; |
| | | height: 6px; |
| | | } |
| | | |
| | | .process-nav::-webkit-scrollbar-track, |
| | | .process-content::-webkit-scrollbar-track { |
| | | background: #f1f1f1; |
| | | border-radius: 3px; |
| | | } |
| | | |
| | | .process-nav::-webkit-scrollbar-thumb, |
| | | .process-content::-webkit-scrollbar-thumb { |
| | | background: #c1c1c1; |
| | | border-radius: 3px; |
| | | } |
| | | |
| | | .process-nav::-webkit-scrollbar-thumb:hover, |
| | | .process-content::-webkit-scrollbar-thumb:hover { |
| | | background: #a8a8a8; |
| | | } |
| | | |
| | | /* å¨ç»ææ */ |
| | | @keyframes fadeIn { |
| | | from { |
| | | opacity: 0; |
| | | transform: translateY(10px); |
| | | } |
| | | to { |
| | | opacity: 1; |
| | | transform: translateY(0); |
| | | } |
| | | } |
| | | |
| | | .step-panel { |
| | | animation: fadeIn 0.3s ease; |
| | | } |
| | | |
| | | /* å è½½ç¶æ */ |
| | | .loading-overlay { |
| | | position: fixed; |
| | | top: 0; |
| | | left: 0; |
| | | right: 0; |
| | | bottom: 0; |
| | | background-color: rgba(255, 255, 255, 0.8); |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | z-index: 9999; |
| | | } |
| | | </style> |