form.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556
  1. <script setup>
  2. import { ref, computed, watch, nextTick } from "vue";
  3. import { onLoad } from "@dcloudio/uni-app";
  4. import { getRuiHenReportDetail } from "@/api/ruihen";
  5. import { useDataDictStore } from "@/store/modules/dataDict";
  6. const props = defineProps({
  7. type: {
  8. type: String,
  9. default: "edit",
  10. },
  11. });
  12. const NON_PROD_FIELDS = [
  13. { key: "repairTime", label: "设备故障" },
  14. { key: "selfStopTime", label: "设备保养" },
  15. { key: "accidentTime", label: "工程质量" },
  16. { key: "complexityTime", label: "技术受限" },
  17. { key: "rectificationTime", label: "生产组织" },
  18. { key: "waitingStopTime", label: "不可抗力" },
  19. { key: "partyaDesign", label: "甲方设计" },
  20. { key: "partyaPrepare", label: "甲方准备" },
  21. { key: "partyaResource", label: "甲方资源" },
  22. { key: "relocationTime", label: "生产配合" },
  23. { key: "winterBreakTime", label: "待命" },
  24. { key: "otherNptTime", label: "其他非生产时间" },
  25. ];
  26. const FORM_KEYS = [
  27. "id",
  28. "deptId",
  29. "projectId",
  30. "taskId",
  31. "deptName",
  32. "contractName",
  33. "taskName",
  34. "dailyGasInjection",
  35. "dailyWaterInjection",
  36. "dailyInjectGasTime",
  37. "dailyInjectWaterTime",
  38. "productionStatus",
  39. "remark",
  40. "relocationDays",
  41. "capacity",
  42. "createTime",
  43. "opinion",
  44. "repairTime",
  45. "selfStopTime",
  46. "accidentTime",
  47. "complexityTime",
  48. "rectificationTime",
  49. "waitingStopTime",
  50. "partyaDesign",
  51. "partyaPrepare",
  52. "partyaResource",
  53. "relocationTime",
  54. "winterBreakTime",
  55. "otherNptTime",
  56. "otherNptReason",
  57. "status",
  58. "auditStatus",
  59. "location",
  60. ];
  61. const formType = ref("edit");
  62. const initFormData = () => {
  63. const base = {
  64. dailyGasInjection: 0,
  65. dailyWaterInjection: 0,
  66. dailyInjectGasTime: 0,
  67. dailyInjectWaterTime: 0,
  68. relocationDays: 0,
  69. capacity: 0,
  70. };
  71. // 初始化所有非生产时间字段为 0
  72. NON_PROD_FIELDS.forEach((field) => {
  73. base[field.key] = 0;
  74. });
  75. return base;
  76. };
  77. const form = ref(initFormData());
  78. async function loadDetail(id) {
  79. try {
  80. const { data } = await getRuiHenReportDetail({ id });
  81. form.value = initFormData();
  82. FORM_KEYS.forEach((key) => {
  83. if (
  84. Object.prototype.hasOwnProperty.call(data, key) &&
  85. data[key] !== null &&
  86. data[key] !== undefined
  87. ) {
  88. form.value[key] = data[key];
  89. }
  90. });
  91. form.value.id = id;
  92. if (props.type.includes("approval") && data.auditStatus !== 10) {
  93. formType.value = "readonly";
  94. }
  95. if (props.type.includes("edit") && data.status !== 0) {
  96. formType.value = "readonly";
  97. }
  98. if (props.type.includes("detail")) {
  99. formType.value = "readonly";
  100. }
  101. if (!form.value.capacity) {
  102. uni.showToast({ title: "请维护增压机产能", icon: "none" });
  103. }
  104. } finally {
  105. }
  106. }
  107. const dictStore = useDataDictStore();
  108. const nptReasonOptions = ref([]);
  109. const constructionStatusOptions = ref([]);
  110. const loadOptions = () => {
  111. nptReasonOptions.value = dictStore
  112. .getStrDictOptions("nptReason")
  113. .map((v) => ({
  114. text: v.label,
  115. value: v.value,
  116. }));
  117. constructionStatusOptions.value = dictStore
  118. .getStrDictOptions("constructionStatus")
  119. .map((v) => ({
  120. text: v.label,
  121. value: v.value,
  122. }));
  123. };
  124. onLoad((options) => {
  125. if (dictStore.dataDict.length <= 0) {
  126. dictStore.loadDataDictList().then(() => {
  127. loadOptions();
  128. });
  129. } else loadOptions();
  130. loadDetail(options.id);
  131. });
  132. const defaultProps = computed(() => ({
  133. inputBorder: false,
  134. clearable: false,
  135. placeholder: "请输入",
  136. style: {
  137. "text-align": "right",
  138. },
  139. styles: {
  140. disableColor: "#fff",
  141. },
  142. }));
  143. const disabled = computed(() => (field) => {
  144. if (field === "edit")
  145. return (
  146. formType.value === "readonly" ||
  147. props.type.includes("approval") ||
  148. props.type.includes("detail")
  149. );
  150. else return formType.value === "readonly";
  151. });
  152. const transitTime = computed(() => {
  153. const cap = form.value.capacity;
  154. const gas = form.value.dailyGasInjection ?? 0;
  155. if (!cap) return { original: 0, value: "0%" };
  156. const original = gas / cap;
  157. return { original, value: (original * 100).toFixed(2) + "%" };
  158. });
  159. const formRef = ref(null);
  160. // 辅助函数:计算总时间
  161. const sumNonProdTimes = () => {
  162. let sum = 0;
  163. NON_PROD_FIELDS.forEach((field) => {
  164. sum += Number(form.value[field.key] || 0);
  165. });
  166. return sum;
  167. };
  168. const validateTotalTime = (rule, value, data, callback) => {
  169. const gasTime = Number(form.value.dailyInjectGasTime || 0);
  170. const waterTime = Number(form.value.dailyInjectWaterTime || 0);
  171. const nonProdSum = sumNonProdTimes();
  172. let total = 0;
  173. let msg = "";
  174. if (gasTime === 0 && waterTime > 0) {
  175. total = parseFloat((waterTime + nonProdSum).toFixed(2));
  176. msg = `注水(${waterTime})+非生产(${nonProdSum})=${total}H,必须为24H`;
  177. } else {
  178. total = parseFloat((gasTime + nonProdSum).toFixed(2));
  179. msg = `注气(${gasTime})+非生产(${nonProdSum})=${total}H,必须为24H`;
  180. }
  181. if (Math.abs(total - 24) > 0.01) {
  182. callback(msg);
  183. }
  184. return true;
  185. };
  186. // uni-forms 规则定义
  187. const rules = ref({
  188. dailyGasInjection: {
  189. rules: [{ required: true, errorMessage: "请输入当日注气量" }],
  190. },
  191. dailyWaterInjection: {
  192. rules: [{ required: true, errorMessage: "请输入当日注水量" }],
  193. },
  194. productionStatus: {
  195. rules: [{ required: true, errorMessage: "请输入生产动态" }],
  196. },
  197. constructionStatus: {
  198. rules: [{ required: true, errorMessage: "请选择施工状态" }],
  199. },
  200. dailyInjectGasTime: {
  201. rules: [
  202. { required: true, errorMessage: "请输入当日注气量时间" },
  203. { validateFunction: validateTotalTime },
  204. ],
  205. },
  206. });
  207. const allTimeKeys = [
  208. "dailyInjectGasTime",
  209. "dailyInjectWaterTime",
  210. ...NON_PROD_FIELDS.map((f) => f.key),
  211. ];
  212. watch(
  213. () => allTimeKeys.map((key) => form.value[key]),
  214. () => {
  215. nextTick(() => {
  216. formRef.value?.validateField("dailyInjectGasTime");
  217. });
  218. },
  219. {
  220. deep: true,
  221. }
  222. );
  223. defineExpose({ formRef, form, loadDetail });
  224. </script>
  225. <template>
  226. <view class="content">
  227. <view class="tip">
  228. <view class="item">
  229. <view class="left">
  230. <span>运行时效:</span>
  231. 当日注气量 / 产能
  232. </view>
  233. <span class="right red"> >120% 红色预警 </span>
  234. </view>
  235. <!-- <view class="item">
  236. <view class="left">
  237. <span>时间平衡:</span>
  238. 注气 + 注水 + 非生产 = 24H
  239. </view>
  240. <span class="right orange"> ≠24H 橙色预警 </span>
  241. </view> -->
  242. </view>
  243. <view v-if="!type.includes('approval') && form.opinion" class="opinion">
  244. <span class="left">审批意见:</span>
  245. <span class="right"> {{ form.opinion }} </span>
  246. </view>
  247. <uni-forms
  248. ref="formRef"
  249. labelWidth="auto"
  250. :model="form"
  251. :rules="rules"
  252. validateTrigger="submit"
  253. err-show-type="toast">
  254. <uni-forms-item label="施工队伍" name="deptName">
  255. <span class="readOnly">{{ form.deptName }}</span>
  256. </uni-forms-item>
  257. <uni-forms-item label="项目" name="contractName">
  258. <span class="readOnly">{{ form.contractName }}</span>
  259. </uni-forms-item>
  260. <uni-forms-item label="任务" name="taskName">
  261. <span class="readOnly">{{ form.taskName }}</span>
  262. </uni-forms-item>
  263. <uni-forms-item label="施工区域" name="location">
  264. <span class="readOnly">{{ form.location }}</span>
  265. </uni-forms-item>
  266. <uni-forms-item label="搬迁安装天数(D)" name="relocationDays">
  267. <span class="readOnly">{{ form.relocationDays }}</span>
  268. </uni-forms-item>
  269. <uni-forms-item label="运行时效" name="transitTime">
  270. <span
  271. class="readOnly"
  272. :class="{ 'red-text': transitTime.original > 1.2 }"
  273. >{{ transitTime.value }}</span
  274. >
  275. </uni-forms-item>
  276. <uni-forms-item label="施工状态" name="constructionStatus" required>
  277. <uni-data-select
  278. :clear="true"
  279. align="right"
  280. placeholder="请选择"
  281. :localdata="constructionStatusOptions"
  282. placement="top"
  283. :disabled="disabled('edit')"
  284. v-model="form.constructionStatus" />
  285. </uni-forms-item>
  286. <uni-forms-item label="当日注气量(方)" name="dailyGasInjection" required>
  287. <uni-easyinput
  288. type="number"
  289. v-bind="defaultProps"
  290. :disabled="disabled('edit')"
  291. v-model="form.dailyGasInjection" />
  292. </uni-forms-item>
  293. <uni-forms-item
  294. label="当日注水量(方)"
  295. name="dailyWaterInjection"
  296. required>
  297. <uni-easyinput
  298. type="number"
  299. v-bind="defaultProps"
  300. :disabled="disabled('edit')"
  301. v-model="form.dailyWaterInjection" />
  302. </uni-forms-item>
  303. <uni-forms-item label="生产动态" name="productionStatus" required>
  304. <uni-easyinput
  305. type="textarea"
  306. autoHeight
  307. v-bind="defaultProps"
  308. v-model="form.productionStatus"
  309. :disabled="disabled('edit')"
  310. :maxlength="1000" />
  311. </uni-forms-item>
  312. <uni-forms-item label="备注" name="remark">
  313. <uni-easyinput
  314. type="textarea"
  315. autoHeight
  316. v-bind="defaultProps"
  317. :disabled="disabled('edit')"
  318. v-model="form.remark"
  319. :maxlength="1000" />
  320. </uni-forms-item>
  321. <uv-divider text="生产时间" textPosition="left"></uv-divider>
  322. <uni-forms-item label="当日注气时间(H)" name="dailyInjectGasTime">
  323. <uni-easyinput
  324. type="number"
  325. v-bind="defaultProps"
  326. :disabled="disabled('edit')"
  327. v-model="form.dailyInjectGasTime" />
  328. </uni-forms-item>
  329. <uni-forms-item label="当日注水时间(H)" name="dailyInjectWaterTime">
  330. <uni-easyinput
  331. type="number"
  332. v-bind="defaultProps"
  333. :disabled="disabled('edit')"
  334. v-model="form.dailyInjectWaterTime" />
  335. </uni-forms-item>
  336. <uv-divider text="非生产时间" textPosition="left"></uv-divider>
  337. <uni-forms-item
  338. v-for="field in NON_PROD_FIELDS"
  339. :key="field.key"
  340. :label="field.label + '(H)'"
  341. :name="field.key">
  342. <uni-easyinput
  343. type="number"
  344. v-bind="defaultProps"
  345. :disabled="disabled('edit')"
  346. v-model="form[field.key]" />
  347. </uni-forms-item>
  348. <uni-forms-item label="其他非生产原因" name="otherNptReason">
  349. <uni-easyinput
  350. type="textarea"
  351. autoHeight
  352. v-bind="defaultProps"
  353. v-model="form.otherNptReason"
  354. :disabled="disabled('edit')"
  355. :maxlength="1000" />
  356. </uni-forms-item>
  357. <uni-forms-item
  358. v-if="type.includes('approval')"
  359. label="审批意见"
  360. name="opinion">
  361. <uni-easyinput
  362. type="textarea"
  363. autoHeight
  364. v-bind="defaultProps"
  365. :disabled="disabled('approval')"
  366. v-model="form.opinion"
  367. :maxlength="1000" />
  368. </uni-forms-item>
  369. </uni-forms>
  370. </view>
  371. </template>
  372. <style lang="scss" scoped>
  373. .content {
  374. background-color: white;
  375. padding: 16px 16px;
  376. border-radius: 8px;
  377. box-sizing: border-box;
  378. }
  379. .uni-forms {
  380. margin-top: 10px;
  381. height: 100%;
  382. .uni-form {
  383. height: 100%;
  384. }
  385. .uni-forms-item {
  386. display: flex;
  387. align-items: center;
  388. flex: 1;
  389. margin-bottom: 6px;
  390. border-bottom: 1px dashed #cacccf;
  391. }
  392. :deep(.uni-forms-item__content) {
  393. text-align: right;
  394. .readOnly {
  395. padding-right: 10px;
  396. }
  397. }
  398. :deep(.uni-forms-item__label) {
  399. height: 44px;
  400. font-weight: 500;
  401. font-size: 14px;
  402. color: #333333 !important;
  403. width: max-content !important;
  404. }
  405. :deep(.uni-select) {
  406. border: none;
  407. text-align: right;
  408. padding-right: 0;
  409. .uniui-bottom:before {
  410. content: "\e6b5" !important;
  411. font-size: 16px !important;
  412. }
  413. }
  414. :deep(.uni-easyinput__content-textarea) {
  415. min-height: inherit;
  416. margin: 10px;
  417. }
  418. :deep(.is-disabled) {
  419. color: #333333 !important;
  420. }
  421. :deep(.red-text > .is-disabled) {
  422. color: rgb(220 38 38 / 0.8) !important;
  423. }
  424. :deep(.orange-text > .is-disabled) {
  425. color: rgb(234 88 12 / 0.8) !important;
  426. }
  427. :deep(.uni-select--disabled) {
  428. background-color: #fff;
  429. }
  430. }
  431. .red-text {
  432. color: rgb(220 38 38 / 0.8) !important;
  433. }
  434. .orange-text {
  435. color: rgb(234 88 12 / 0.8) !important;
  436. }
  437. .red {
  438. border: 1px solid rgb(254 226 226);
  439. color: rgb(220 38 38 / 0.8);
  440. background-color: rgb(254 226 226);
  441. }
  442. .orange {
  443. border: 1px solid rgb(254 215 170);
  444. color: rgb(234 88 12 / 0.8);
  445. background-color: rgb(254 215 170);
  446. }
  447. .tip {
  448. border-radius: 8px;
  449. border: 1px solid #e5e5e5;
  450. background-color: rgba(239, 246, 255, 0.8);
  451. box-sizing: border-box;
  452. padding: 10px;
  453. display: flex;
  454. flex-direction: column;
  455. gap: 6px;
  456. .item {
  457. display: flex;
  458. align-items: center;
  459. justify-content: space-between;
  460. font-size: 12px;
  461. .left {
  462. color: rgb(75 85 99);
  463. span {
  464. color: rgb(31 41 55);
  465. font-weight: 600;
  466. }
  467. }
  468. .right {
  469. display: inline-flex;
  470. align-items: center;
  471. border-radius: 4px;
  472. padding: 2px 4px;
  473. font-weight: 500;
  474. }
  475. }
  476. }
  477. .opinion {
  478. border-radius: 8px;
  479. border: 1px solid rgb(254 240 138);
  480. background-color: rgb(254 252 232);
  481. box-sizing: border-box;
  482. padding: 10px;
  483. display: flex;
  484. align-items: center;
  485. justify-content: space-between;
  486. font-size: 12px;
  487. margin-top: 10px;
  488. .left {
  489. font-weight: 600;
  490. color: rgb(133 77 14);
  491. }
  492. .right {
  493. font-weight: 500;
  494. color: rgb(75 85 99);
  495. }
  496. }
  497. </style>