form.vue 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773
  1. <template>
  2. <div class="app-container maint-plan-form-page">
  3. <!-- 页面头部 -->
  4. <div class="page-header">
  5. <div class="header-left">
  6. <span class="page-title">{{ pageTitle }}</span>
  7. </div>
  8. <div class="header-right">
  9. <el-button icon="el-icon-arrow-left" @click="goBack">返回</el-button>
  10. <el-button v-if="formData.planId && isPlannedStatus" @click="handleSaveOnly" :loading="savingOnly">仅保存</el-button>
  11. <el-button type="primary" @click="handleSubmit" :loading="submitting">
  12. {{ isPlannedStatus ? '提交申请' : (formData.planId && formData.approvalStatus === '1' ? '再次提交申请' : '确 定') }}
  13. </el-button>
  14. </div>
  15. </div>
  16. <!-- 表单内容 -->
  17. <el-card class="form-card">
  18. <el-form ref="form" :model="formData" :rules="rules" label-width="120px">
  19. <!-- 基础信息区域 -->
  20. <div class="form-section">
  21. <el-row :gutter="20">
  22. <!-- 左侧:基础信息 -->
  23. <el-col :span="14">
  24. <div class="section-header">
  25. <i class="el-icon-info"></i>
  26. <span class="section-title">计划基础信息</span>
  27. </div>
  28. <el-row :gutter="20">
  29. <el-col :span="12">
  30. <el-form-item label="选择设备" prop="devId">
  31. <el-select
  32. v-model="formData.devId"
  33. filterable
  34. remote
  35. :remote-method="searchDevice"
  36. :loading="deviceLoading"
  37. placeholder="请输入设备位号或名称搜索"
  38. style="width: 100%"
  39. @change="handleDeviceChange"
  40. @focus="handleDeviceFocus"
  41. :disabled="!!formData.planId"
  42. clearable>
  43. <el-option
  44. v-for="device in deviceOptions"
  45. :key="device.devId"
  46. :label="`${device.devTag} - ${device.devName}`"
  47. :value="device.devId">
  48. <span style="float: left">{{ device.devTag }}</span>
  49. <span style="float: right; color: #8492a6; font-size: 13px">{{ device.devName }}</span>
  50. </el-option>
  51. </el-select>
  52. </el-form-item>
  53. </el-col>
  54. <el-col :span="12">
  55. <el-form-item label="计划开始时间" prop="planTime">
  56. <el-date-picker
  57. v-model="formData.planTime"
  58. type="date"
  59. value-format="yyyy-MM-dd"
  60. placeholder="选择开始时间"
  61. style="width: 100%">
  62. </el-date-picker>
  63. </el-form-item>
  64. </el-col>
  65. </el-row>
  66. <el-row :gutter="20">
  67. <el-col :span="12">
  68. <el-form-item label="计划结束时间" prop="planEndTime">
  69. <el-date-picker
  70. v-model="formData.planEndTime"
  71. type="date"
  72. value-format="yyyy-MM-dd"
  73. placeholder="选择结束时间"
  74. style="width: 100%">
  75. </el-date-picker>
  76. </el-form-item>
  77. </el-col>
  78. <el-col :span="12">
  79. <el-form-item label="责任人" prop="responsible">
  80. <el-select
  81. v-model="formData.responsible"
  82. placeholder="请选择责任人"
  83. style="width: 100%"
  84. clearable
  85. filterable>
  86. <el-option
  87. v-for="staff in staffOptions"
  88. :key="staff.staffid"
  89. :label="`${staff.name} (${staff.staffid})`"
  90. :value="staff.staffid">
  91. </el-option>
  92. </el-select>
  93. </el-form-item>
  94. </el-col>
  95. </el-row>
  96. <el-row :gutter="20">
  97. <el-col :span="12">
  98. <el-form-item label="审批人" prop="approver">
  99. <el-select
  100. v-model="formData.approver"
  101. placeholder="请选择审批人"
  102. style="width: 100%"
  103. clearable
  104. filterable
  105. :disabled="isApproverDisabled">
  106. <el-option
  107. v-for="staff in staffOptions"
  108. :key="staff.staffid"
  109. :label="`${staff.name} (${staff.staffid})`"
  110. :value="staff.staffid">
  111. </el-option>
  112. </el-select>
  113. </el-form-item>
  114. </el-col>
  115. <el-col :span="12">
  116. <el-form-item label="备注" prop="remarks">
  117. <el-input v-model="formData.remarks" type="textarea" :rows="1" placeholder="请输入备注" />
  118. </el-form-item>
  119. </el-col>
  120. </el-row>
  121. </el-col>
  122. <!-- 设备信息展示:右侧 -->
  123. <el-col :span="10" v-if="selectedDevice">
  124. <!-- 设备信息展示 -->
  125. <div class="section-header">
  126. <i class="el-icon-s-platform"></i>
  127. <span class="section-title">设备信息</span>
  128. </div>
  129. <el-descriptions :column="2" border size="small">
  130. <!-- 设备名称单独占一行 -->
  131. <el-descriptions-item label="设备名称" :span="2">{{ selectedDevice.devName }}</el-descriptions-item>
  132. <!-- 其余字段两列排列 -->
  133. <el-descriptions-item label="设备位号">{{ selectedDevice.devTag }}</el-descriptions-item>
  134. <el-descriptions-item label="位置">{{ selectedDevice.devLoc }}</el-descriptions-item>
  135. <el-descriptions-item label="设备类型">{{ selectedDevice.devType }}</el-descriptions-item>
  136. <el-descriptions-item label="区域负责人">{{ getStaffNameById(selectedDevice.areaResponsible) }}</el-descriptions-item>
  137. </el-descriptions>
  138. </el-col>
  139. </el-row>
  140. </div>
  141. <!-- 部件列表及维修记录选择 -->
  142. <div class="form-section" v-if="compoList.length > 0">
  143. <div class="section-header">
  144. <i class="el-icon-s-grid"></i>
  145. <span class="section-title">部件维修选择</span>
  146. <span style="margin-left: 10px; font-size: 12px; color: #909399;">(共 {{ compoList.length }} 个部件)</span>
  147. </div>
  148. <el-table
  149. :data="pagedCompoList"
  150. border
  151. size="small"
  152. :height="tableHeight"
  153. style="margin-top: 10px">
  154. <el-table-column label="序号" width="60" align="center">
  155. <template slot-scope="scope">
  156. {{ (compoCurrentPage - 1) * compoPageSize + scope.$index + 1 }}
  157. </template>
  158. </el-table-column>
  159. <el-table-column label="部件名称" prop="compoName" :show-overflow-tooltip="true">
  160. <template slot-scope="scope">
  161. <span>{{ scope.row.compoName }}</span>
  162. <el-tag v-if="scope.row.hasMemo" type="warning" size="mini" style="margin-left: 6px;">备</el-tag>
  163. </template>
  164. </el-table-column>
  165. <el-table-column label="检查频率/日期" width="120" align="center">
  166. <template slot-scope="scope">
  167. <div style="line-height: 1.2;">
  168. <div style="font-weight: bold; margin-bottom: 2px;">{{ translateFrequency(scope.row.checkFreq) || '-' }}</div>
  169. <div style="font-size: 12px; color: #666;">{{ scope.row.lastCheckDate ? parseTime(scope.row.lastCheckDate, '{y}-{m}-{d}') : '-' }}</div>
  170. </div>
  171. </template>
  172. </el-table-column>
  173. <el-table-column label="维修频率/日期" width="120" align="center">
  174. <template slot-scope="scope">
  175. <div style="line-height: 1.2;">
  176. <div style="font-weight: bold; margin-bottom: 2px;">{{ translateFrequency(scope.row.maintFreq) || '-' }}</div>
  177. <div style="font-size: 12px; color: #666;">{{ scope.row.lastMaintDate ? parseTime(scope.row.lastMaintDate, '{y}-{m}-{d}') : '-' }}</div>
  178. </div>
  179. </template>
  180. </el-table-column>
  181. <el-table-column label="更换频率/日期" width="120" align="center">
  182. <template slot-scope="scope">
  183. <div style="line-height: 1.2;">
  184. <div style="font-weight: bold; margin-bottom: 2px;">{{ translateFrequency(scope.row.replaceFreq) || '-' }}</div>
  185. <div style="font-size: 12px; color: #666;">{{ scope.row.lastReplaceDate ? parseTime(scope.row.lastReplaceDate, '{y}-{m}-{d}') : '-' }}</div>
  186. </div>
  187. </template>
  188. </el-table-column>
  189. <el-table-column label="本次维修" width="100" align="center">
  190. <template slot-scope="scope">
  191. <el-checkbox
  192. v-model="scope.row.needMaint"
  193. :disabled="scope.row.recordStatus === 2 || scope.row.recordStatus === '2'"
  194. @change="handleMaintCheckChange(scope.row)"
  195. >需要维修</el-checkbox>
  196. </template>
  197. </el-table-column>
  198. <el-table-column label="维修形式" width="200" align="center">
  199. <template slot-scope="scope">
  200. <el-radio-group
  201. v-model="scope.row.maintType"
  202. :disabled="!scope.row.needMaint || scope.row.recordStatus === 2 || scope.row.recordStatus === '2'"
  203. @change="handleMaintTypeChange(scope.row)"
  204. style="display: flex; flex-direction: row; gap: 0px;">
  205. <el-radio label="1" style="margin-right: 0; margin-left: 0;">检查</el-radio>
  206. <el-radio label="2" style="margin-right: 0; margin-left: 0;">维修</el-radio>
  207. <el-radio label="3" style="margin-right: 0; margin-left: 0;">更换</el-radio>
  208. </el-radio-group>
  209. </template>
  210. </el-table-column>
  211. <el-table-column label="负责人" width="150" align="center">
  212. <template slot-scope="scope">
  213. <el-select
  214. v-model="scope.row.maintResponsible"
  215. :placeholder="getResponsiblePlaceholder(scope.row.maintType)"
  216. style="width: 100%"
  217. :disabled="!scope.row.needMaint || scope.row.recordStatus === 2 || scope.row.recordStatus === '2'"
  218. clearable
  219. filterable>
  220. <el-option
  221. v-for="staff in staffOptions"
  222. :key="staff.staffid"
  223. :label="`${staff.name} (${staff.staffid})`"
  224. :value="staff.staffid">
  225. </el-option>
  226. </el-select>
  227. </template>
  228. </el-table-column>
  229. <el-table-column label="维修状态" width="100" align="center">
  230. <template slot-scope="scope">
  231. <el-tag
  232. v-if="scope.row.recordStatus !== undefined && scope.row.recordStatus !== null"
  233. :type="getRecordStatusTagType(scope.row.recordStatus)"
  234. size="small"
  235. >
  236. {{ getRecordStatusText(scope.row.recordStatus) }}
  237. </el-tag>
  238. <span v-else>-</span>
  239. </template>
  240. </el-table-column>
  241. </el-table>
  242. <!-- 分页器 -->
  243. <el-pagination
  244. v-if="compoList.length > compoPageSize"
  245. style="margin-top: 10px; text-align: right;"
  246. @size-change="handleCompoSizeChange"
  247. @current-change="handleCompoPageChange"
  248. :current-page="compoCurrentPage"
  249. :page-sizes="[50, 100, 200]"
  250. :page-size="compoPageSize"
  251. layout="total, sizes, prev, pager, next"
  252. :total="compoList.length">
  253. </el-pagination>
  254. </div>
  255. </el-form>
  256. </el-card>
  257. </div>
  258. </template>
  259. <script>
  260. import { listRel_device, getRel_device } from "@/api/reliability/rel_device";
  261. import { listRel_compo } from "@/api/reliability/rel_compo";
  262. import { listStaffmgrAll } from "@/api/plant/staffmgr";
  263. import { getRel_maint_plan, submitApprove, resubmitApprove, saveOnlyRel_maint_plan } from "@/api/reliability/rel_maint_plan";
  264. import { parseTime as formatTime } from "@/utils/ruoyi";
  265. export default {
  266. name: "MaintPlanFormPage",
  267. data() {
  268. return {
  269. submitting: false,
  270. savingOnly: false,
  271. formData: {},
  272. rules: {
  273. devId: [
  274. { required: true, message: "请选择设备", trigger: "change" }
  275. ],
  276. planTime: [
  277. { required: true, message: "请选择计划开始时间", trigger: "change" }
  278. ],
  279. approver: [
  280. { required: true, message: "请选择审批人", trigger: "change" }
  281. ]
  282. },
  283. // 设备相关
  284. deviceOptions: [],
  285. deviceLoading: false,
  286. selectedDevice: null,
  287. // 部件相关
  288. compoList: [],
  289. // 部件分页
  290. compoCurrentPage: 1,
  291. compoPageSize: 50,
  292. // 表格高度
  293. tableHeight: 400,
  294. // 所有人员选项
  295. staffOptions: []
  296. };
  297. },
  298. computed: {
  299. /** 页面标题 */
  300. pageTitle() {
  301. if (this.$route.query.planId) {
  302. return this.isPlannedStatus ? '编辑维修计划' : '修改维修计划';
  303. }
  304. return '新增维修计划';
  305. },
  306. /** 是否为计划中状态 */
  307. isPlannedStatus() {
  308. return this.formData.approvalStatus === '9' || this.formData.completionStatus === '9';
  309. },
  310. /** 审批人是否禁用 */
  311. isApproverDisabled() {
  312. if (!this.formData.planId) return false;
  313. if (this.isPlannedStatus) return false;
  314. return true;
  315. },
  316. /** 分页后的部件列表 */
  317. pagedCompoList() {
  318. const start = (this.compoCurrentPage - 1) * this.compoPageSize;
  319. const end = start + this.compoPageSize;
  320. return this.compoList.slice(start, end);
  321. }
  322. },
  323. created() {
  324. this.getStaffList();
  325. this.initPage();
  326. // 计算表格高度
  327. this.$nextTick(() => {
  328. this.tableHeight = Math.max(380, window.innerHeight - 440);
  329. });
  330. },
  331. methods: {
  332. /** 根据员工号获取姓名 */
  333. getStaffNameById(staffid) {
  334. if (!staffid) return '';
  335. const staff = this.staffOptions.find(s => s.staffid === staffid);
  336. return staff ? staff.name : staffid;
  337. },
  338. /** 初始化页面 */
  339. initPage() {
  340. const planId = this.$route.query.planId;
  341. if (planId) {
  342. // 编辑模式:加载计划详情
  343. this.loadPlanDetail(planId);
  344. } else {
  345. // 新增模式
  346. this.resetForm();
  347. // 检查是否有从设备页面传递过来的参数
  348. const { devId, plant, devName, devTag } = this.$route.query;
  349. // 如果带了 devId,直接复用 handleDeviceChange 逻辑,加载完整设备信息和部件
  350. if (devId) {
  351. const idNum = Number(devId);
  352. this.formData.devId = idNum;
  353. this.handleDeviceChange(idNum);
  354. } else if (devTag || plant || devName) {
  355. // 兼容只传 devTag/plant/devName 的场景,尽量预填基础字段和部件列表
  356. this.formData.plant = plant || null;
  357. this.formData.devName = devName || null;
  358. this.formData.devTag = devTag || null;
  359. if (devTag) {
  360. this.loadCompoList(devTag);
  361. }
  362. }
  363. }
  364. },
  365. /** 加载计划详情 */
  366. loadPlanDetail(planId) {
  367. getRel_maint_plan(planId).then(response => {
  368. this.formData = response.data || {};
  369. if (this.formData.relDevice) {
  370. this.selectedDevice = this.formData.relDevice;
  371. this.deviceOptions = [this.formData.relDevice];
  372. // 确保 devId 被正确设置,用于下拉框回显
  373. if (!this.formData.devId && this.formData.relDevice.devId) {
  374. this.formData.devId = this.formData.relDevice.devId;
  375. }
  376. if (this.formData.compoList && this.formData.compoList.length > 0) {
  377. this.initCompoListFromData(this.formData.compoList);
  378. } else if (this.selectedDevice.devTag) {
  379. this.loadCompoList(this.selectedDevice.devTag);
  380. }
  381. }
  382. });
  383. },
  384. handleSaveOnly() {
  385. if (this.savingOnly) {
  386. return;
  387. }
  388. if (!this.formData.planId) {
  389. this.$message.error("计划ID不能为空");
  390. return;
  391. }
  392. if (!this.isPlannedStatus) {
  393. this.$message.error("仅计划中状态允许仅保存");
  394. return;
  395. }
  396. this.savingOnly = true;
  397. if (this.compoList.length > 0) {
  398. const maintComponents = this.compoList
  399. .filter(compo => compo.needMaint)
  400. .map(compo => ({
  401. compoId: compo.compoId,
  402. compoName: compo.compoName,
  403. maintType: compo.maintType,
  404. responsible: compo.maintResponsible
  405. }));
  406. this.formData.maintComponents = maintComponents;
  407. } else {
  408. this.formData.maintComponents = [];
  409. }
  410. saveOnlyRel_maint_plan({
  411. planId: this.formData.planId,
  412. maintComponents: this.formData.maintComponents
  413. }).then(() => {
  414. this.$message.success("保存成功");
  415. }).finally(() => {
  416. this.savingOnly = false;
  417. });
  418. },
  419. /** 查询人员列表 */
  420. getStaffList() {
  421. return listStaffmgrAll().then(response => {
  422. this.staffOptions = response.rows || response.data || [];
  423. // 如果此时已经选中了设备且尚未设置责任人,根据设备的区域负责人预填责任人
  424. if (this.selectedDevice && this.selectedDevice.areaResponsible && !this.formData.responsible) {
  425. const staff = this.staffOptions.find(s => s.staffid === this.selectedDevice.areaResponsible);
  426. if (staff) {
  427. this.formData.responsible = staff.staffid;
  428. }
  429. }
  430. });
  431. },
  432. /** 表单重置 */
  433. resetForm() {
  434. this.formData = {
  435. planId: null,
  436. devId: null,
  437. plant: null,
  438. devName: null,
  439. devTag: null,
  440. planTime: null,
  441. approvalStatus: "0",
  442. responsible: null,
  443. completionStatus: "0",
  444. processId: null,
  445. approver: null,
  446. remarks: null,
  447. maintComponents: []
  448. };
  449. this.selectedDevice = null;
  450. this.compoList = [];
  451. this.deviceOptions = [];
  452. this.compoCurrentPage = 1;
  453. },
  454. /** 部件分页 - 每页条数改变 */
  455. handleCompoSizeChange(val) {
  456. this.compoPageSize = val;
  457. this.compoCurrentPage = 1;
  458. },
  459. /** 部件分页 - 页码改变 */
  460. handleCompoPageChange(val) {
  461. this.compoCurrentPage = val;
  462. },
  463. /** 设备搜索 */
  464. searchDevice(query) {
  465. if (query) {
  466. this.deviceLoading = true;
  467. const params = { devTag: query, devName: query };
  468. listRel_device(params).then(response => {
  469. this.deviceOptions = response.rows || [];
  470. this.deviceLoading = false;
  471. });
  472. }
  473. },
  474. /** 设备下拉框获得焦点 */
  475. handleDeviceFocus() {},
  476. /** 设备选择改变 */
  477. handleDeviceChange(devId) {
  478. if (!devId) {
  479. this.selectedDevice = null;
  480. this.compoList = [];
  481. return;
  482. }
  483. getRel_device(devId).then(response => {
  484. this.selectedDevice = response.data;
  485. this.formData.plant = response.data.plant;
  486. this.formData.devName = response.data.devName;
  487. this.formData.devTag = response.data.devTag;
  488. // 确保设备下拉框选项中包含当前设备,便于“选择设备”下拉回显
  489. if (response.data && response.data.devId) {
  490. const exists = this.deviceOptions.some(d => d.devId === response.data.devId);
  491. if (!exists) {
  492. this.deviceOptions = [response.data, ...this.deviceOptions];
  493. }
  494. }
  495. // 根据设备的区域负责人预填责任人(前提是人员列表已加载)
  496. if (response.data.areaResponsible && this.staffOptions.length > 0) {
  497. const staff = this.staffOptions.find(s => s.staffid === response.data.areaResponsible);
  498. if (staff) {
  499. this.formData.responsible = staff.staffid;
  500. }
  501. }
  502. if (response.data.devTag) {
  503. this.loadCompoList(response.data.devTag);
  504. }
  505. });
  506. },
  507. /** 加载设备的部件列表 */
  508. loadCompoList(devTag) {
  509. listRel_compo({ devTag: devTag, pageSize: 9999 }).then(response => {
  510. const compos = response.rows || [];
  511. this.compoList = compos.map(compo => ({
  512. ...compo,
  513. checkFreq: compo.inspFreq,
  514. lastCheckDate: compo.lastInspDate,
  515. maintFreq: compo.fixFreq,
  516. lastMaintDate: compo.lastFixDate,
  517. needMaint: false,
  518. maintType: '',
  519. maintResponsible: '',
  520. recordId: null,
  521. recordStatus: null
  522. }));
  523. this.compoCurrentPage = 1;
  524. });
  525. },
  526. /** 从已有数据初始化部件列表 */
  527. initCompoListFromData(compoList) {
  528. const maintRecordsList = this.formData.maintRecordsList || [];
  529. const recordMap = {};
  530. maintRecordsList.forEach(record => {
  531. if (record.compoId) {
  532. recordMap[record.compoId] = record;
  533. }
  534. });
  535. this.compoList = compoList.map(compo => {
  536. const record = recordMap[compo.compoId];
  537. return {
  538. ...compo,
  539. checkFreq: compo.inspFreq,
  540. lastCheckDate: compo.lastInspDate,
  541. maintFreq: compo.fixFreq,
  542. lastMaintDate: compo.lastFixDate,
  543. needMaint: !!record,
  544. maintType: record ? record.maintType : '',
  545. maintResponsible: record ? record.responsible : '',
  546. recordId: record ? record.recordId : null,
  547. recordStatus: record ? record.recordStatus : null
  548. };
  549. });
  550. this.compoCurrentPage = 1;
  551. },
  552. /** 维修选择改变 */
  553. handleMaintCheckChange(compo) {
  554. if (!compo.needMaint) {
  555. compo.maintType = '';
  556. compo.maintResponsible = '';
  557. }
  558. },
  559. /** 维修形式改变 */
  560. handleMaintTypeChange(compo) {
  561. if (compo.maintType === '1') {
  562. compo.maintResponsible = compo.inspector || '';
  563. } else if (compo.maintType === '2' || compo.maintType === '3') {
  564. compo.maintResponsible = compo.fixer || '';
  565. } else {
  566. compo.maintResponsible = '';
  567. }
  568. },
  569. getResponsiblePlaceholder(maintType) {
  570. switch(maintType) {
  571. case '1': return '选择检查人';
  572. case '2': return '选择维修人';
  573. case '3': return '选择更换人';
  574. default: return '选择负责人';
  575. }
  576. },
  577. /** 翻译频率字段 */
  578. translateFrequency(frequency) {
  579. if (!frequency) return '';
  580. const match = frequency.match(/^(\d+)([ym])$/);
  581. if (match) {
  582. const number = match[1];
  583. const unit = match[2];
  584. switch (unit) {
  585. case 'y': return `${number}年一次`;
  586. case 'm': return `${number}月一次`;
  587. default: return frequency;
  588. }
  589. }
  590. return frequency;
  591. },
  592. /** 时间格式化 */
  593. parseTime(time, pattern) {
  594. return formatTime(time, pattern);
  595. },
  596. /** 获取维修状态文本 */
  597. getRecordStatusText(status) {
  598. if (status === 9 || status === '9') return '计划中';
  599. if (status === -1 || status === '-1') return '删除申请中';
  600. if (status === 0 || status === '0') return '新增申请中';
  601. if (status === 1 || status === '1') return '待完成';
  602. if (status === 2 || status === '2') return '已完成';
  603. if (status === 3 || status === '3') return '已延期';
  604. return status != null ? status : '-';
  605. },
  606. /** 获取维修状态标签类型 */
  607. getRecordStatusTagType(status) {
  608. if (status === -1 || status === '-1') return 'danger';
  609. if (status === 0 || status === '0') return 'warning';
  610. if (status === 1 || status === '1') return 'info';
  611. if (status === 2 || status === '2') return 'success';
  612. if (status === 3 || status === '3') return 'warning';
  613. return '';
  614. },
  615. /** 提交表单 */
  616. handleSubmit() {
  617. // 前端防止重复提交:如果已经在提交中,直接返回
  618. if (this.submitting) {
  619. return;
  620. }
  621. // 先标记为提交中,避免校验期间被多次点击
  622. this.submitting = true;
  623. this.$refs.form.validate(valid => {
  624. if (!valid) {
  625. // 校验失败,恢复提交状态
  626. this.submitting = false;
  627. return;
  628. }
  629. if (this.compoList.length > 0) {
  630. const maintComponents = this.compoList
  631. .filter(compo => compo.needMaint)
  632. .map(compo => ({
  633. compoId: compo.compoId,
  634. compoName: compo.compoName,
  635. maintType: compo.maintType,
  636. responsible: compo.maintResponsible
  637. }));
  638. this.formData.maintComponents = maintComponents;
  639. }
  640. // 选择合适的API
  641. let apiCall;
  642. if (!this.formData.planId || this.isPlannedStatus) {
  643. apiCall = submitApprove(this.formData);
  644. } else if (this.formData.approvalStatus === '1') {
  645. apiCall = resubmitApprove(this.formData);
  646. } else {
  647. apiCall = submitApprove(this.formData);
  648. }
  649. apiCall.then(response => {
  650. // 使用 Element UI 消息提示,避免 $modal 未注册导致报错
  651. this.$message.success("操作成功");
  652. this.goBack();
  653. }).finally(() => {
  654. this.submitting = false;
  655. });
  656. });
  657. },
  658. /** 返回列表页 */
  659. goBack() {
  660. // 先记录当前视图
  661. const view = this.$route;
  662. // 跳转到维修计划列表
  663. this.$router.push({ path: "/reliability/rel_maint_plan" });
  664. // 直接关闭当前标签(当前项目中 $router.push 不返回 Promise,不能再调用 .then)
  665. this.$store.dispatch("tagsView/delView", view);
  666. }
  667. }
  668. };
  669. </script>
  670. <style scoped lang="scss">
  671. .maint-plan-form-page {
  672. // 缩小与顶部的间距
  673. padding-top: 8px;
  674. margin-top: 0;
  675. .page-header {
  676. display: flex;
  677. justify-content: space-between;
  678. align-items: center;
  679. margin-bottom: 8px;
  680. padding: 4px 0 6px;
  681. border-bottom: 1px solid #ebeef5;
  682. .header-left {
  683. display: flex;
  684. align-items: center;
  685. .page-title {
  686. font-size: 18px;
  687. font-weight: 600;
  688. color: #303133;
  689. }
  690. }
  691. .header-right {
  692. display: flex;
  693. align-items: center;
  694. .el-button + .el-button {
  695. margin-left: 8px;
  696. }
  697. }
  698. }
  699. .form-card {
  700. ::v-deep .el-card__body {
  701. padding: 15px;
  702. }
  703. .form-section {
  704. margin-bottom: 2px;
  705. .section-header {
  706. display: flex;
  707. align-items: center;
  708. margin-bottom: 10px;
  709. padding-bottom: 8px;
  710. border-bottom: 1px solid #e4e7ed;
  711. .el-icon-info,
  712. .el-icon-s-platform,
  713. .el-icon-s-grid {
  714. font-size: 18px;
  715. color: #409EFF;
  716. margin-right: 8px;
  717. }
  718. .section-title {
  719. font-size: 15px;
  720. font-weight: 600;
  721. color: #303133;
  722. }
  723. }
  724. }
  725. ::v-deep .el-form-item {
  726. margin-bottom: 12px;
  727. }
  728. ::v-deep .el-descriptions {
  729. .el-descriptions-item__label,
  730. .el-descriptions-item__content {
  731. padding: 8px 12px;
  732. }
  733. }
  734. }
  735. }
  736. ::v-deep .el-table .el-radio__label {
  737. padding-left: 2px !important;
  738. }
  739. </style>