form.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522
  1. <script setup>
  2. import { ref, computed, watch, nextTick, reactive } from 'vue';
  3. import { onLoad } from '@dcloudio/uni-app';
  4. import { getRuiYingReportDetail } from '@/api/ruiying';
  5. import { useDataDictStore } from '@/store/modules/dataDict';
  6. const props = defineProps({
  7. type: {
  8. type: String,
  9. default: 'edit',
  10. },
  11. });
  12. const FORM_KEYS = [
  13. 'id',
  14. 'deptId',
  15. 'projectId',
  16. 'taskId',
  17. 'deptName',
  18. 'contractName',
  19. 'taskName',
  20. 'repairStatus',
  21. 'technique',
  22. 'wellCategory',
  23. 'designWellDepth',
  24. 'casingPipeSize',
  25. 'wellControlLevel',
  26. 'currentOperation',
  27. 'nextPlan',
  28. 'transitTime',
  29. 'ratedProductionTime',
  30. 'productionTime',
  31. 'nonProductionTime',
  32. 'ryNptReason',
  33. 'productionStatus',
  34. 'totalStaffNum',
  35. 'onDutyStaffNum',
  36. 'leaveStaffNum',
  37. 'remark',
  38. 'createTime',
  39. 'opinion',
  40. 'status',
  41. 'auditStatus',
  42. ];
  43. const formType = ref('edit');
  44. const initFormData = () => ({});
  45. const form = ref(initFormData());
  46. async function loadDetail(id) {
  47. try {
  48. const { data } = await getRuiYingReportDetail({ id });
  49. FORM_KEYS.forEach(key => {
  50. form.value[key] = data[key] ?? form.value[key];
  51. });
  52. form.value.id = id;
  53. if (props.type.includes('approval') && data.auditStatus !== 10) {
  54. formType.value = 'readonly';
  55. }
  56. if (props.type.includes('edit') && data.status !== 0) {
  57. formType.value = 'readonly';
  58. }
  59. if (props.type.includes('detail')) {
  60. formType.value = 'readonly';
  61. }
  62. } finally {
  63. }
  64. }
  65. const dictStore = useDataDictStore();
  66. const nptReasonOptions = ref([]);
  67. const rigStatusOptions = ref([]);
  68. const techniqueOptions = ref([]);
  69. const loadOptions = () => {
  70. nptReasonOptions.value = dictStore.getStrDictOptions('ryNptReason').map(v => ({
  71. text: v.label,
  72. value: v.value,
  73. }));
  74. rigStatusOptions.value = dictStore.getStrDictOptions('repairStatus').map(v => ({
  75. text: v.label,
  76. value: v.value,
  77. }));
  78. techniqueOptions.value = dictStore.getStrDictOptions('rq_iot_project_technology_ry').map(v => ({
  79. text: v.label,
  80. value: v.value,
  81. }));
  82. };
  83. onLoad(options => {
  84. if (dictStore.dataDict.length <= 0) {
  85. dictStore.loadDataDictList().then(() => {
  86. loadOptions();
  87. });
  88. } else loadOptions();
  89. loadDetail(options.id);
  90. });
  91. const defaultProps = computed(() => ({
  92. inputBorder: false,
  93. clearable: false,
  94. placeholder: '请输入',
  95. style: {
  96. 'text-align': 'right',
  97. },
  98. styles: {
  99. disableColor: '#fff',
  100. },
  101. }));
  102. const disabled = computed(() => field => {
  103. if (field === 'edit')
  104. return formType.value === 'readonly' || props.type.includes('approval') || props.type.includes('detail');
  105. else return formType.value === 'readonly';
  106. });
  107. const transitTime = computed(() => {
  108. const cap = form.value.productionTime ?? 0;
  109. const gas = form.value.ratedProductionTime ?? 0;
  110. if (!gas) return { original: 0, value: '0%' };
  111. const original = cap / gas;
  112. return { original, value: (original * 100).toFixed(2) + '%' };
  113. });
  114. const formRef = ref(null);
  115. const onDutyStaffNum = computed(() => {
  116. return (form.value.totalStaffNum ?? 0) - (form.value.leaveStaffNum ?? 0);
  117. });
  118. // 辅助函数:计算总时间
  119. const sumTimes = () => {
  120. const { productionTime = 0, nonProductionTime = 0 } = form.value;
  121. return Number(productionTime) + Number(nonProductionTime);
  122. };
  123. // 校验函数:总时间必须为 24
  124. const validateTotalTime = (rule, value, data, callback) => {
  125. const total = sumTimes();
  126. if (total !== Number(form.value.ratedProductionTime)) {
  127. callback(`生产时间和非生产时间之和必须等于额定生产时间`);
  128. } else {
  129. callback();
  130. }
  131. };
  132. // // 校验函数:非生产时间原因
  133. // const validateNptReason = (rule, value, data, callback) => {
  134. // const npt = Number(form.value.nonProductionTime) || 0;
  135. // console.log('npt :>> ', npt);
  136. // console.log('value :>> ', value);
  137. // if (npt > 0 && !value) {
  138. // callback('非生产时间大于 0 时,必须选择原因');
  139. // }
  140. // return true;
  141. // };
  142. // 复用的时间规则
  143. // const timeRuleItem = {
  144. // rules: [
  145. // { required: true, errorMessage: '请输入时间' },
  146. // { validateFunction: validateTotalTime }, // 关联自定义校验
  147. // ],
  148. // };
  149. // uni-forms 规则定义
  150. const rules = reactive({
  151. repairStatus: {
  152. rules: [{ required: true, errorMessage: '请输入施工状态' }],
  153. },
  154. productionStatus: {
  155. rules: [{ required: true, errorMessage: '请输入生产动态' }],
  156. },
  157. // 时间字段应用复用规则
  158. // productionTime: timeRuleItem,
  159. // nonProductionTime: timeRuleItem,
  160. // ratedProductionTime: timeRuleItem,
  161. // nptReason: {
  162. // rules: [{ validateFunction: validateNptReason }],
  163. // },
  164. });
  165. watch(
  166. [() => form.value.dailyInjectGasTime, () => form.value.dailyInjectWaterTime, () => form.value.nonProductionTime],
  167. () => {
  168. nextTick(() => {
  169. formRef.value?.validateField(['ryNptReason']).catch(() => {});
  170. if (sumTimes() === 24) {
  171. formRef.value?.clearValidate(['dailyInjectGasTime', 'dailyInjectWaterTime', 'nonProductionTime']);
  172. }
  173. });
  174. }
  175. );
  176. defineExpose({ formRef, form, loadDetail });
  177. </script>
  178. <template>
  179. <view class="content">
  180. <view class="tip">
  181. <view class="item">
  182. <view class="left">
  183. <span>运行时效:</span>
  184. 生产时间/额定生产时间
  185. </view>
  186. <span class="right red"> >100% 红色预警 </span>
  187. </view>
  188. <view class="item">
  189. <view class="left">
  190. <span>时间平衡:</span>
  191. 生产 + 非生产 = 额定生产
  192. </view>
  193. <span class="right orange"> ≠额定生产 橙色预警 </span>
  194. </view>
  195. </view>
  196. <view v-if="!type.includes('approval') && form.opinion" class="opinion">
  197. <span class="left">审批意见:</span>
  198. <span class="right"> {{ form.opinion }} </span>
  199. </view>
  200. <uni-forms
  201. ref="formRef"
  202. labelWidth="auto"
  203. :model="form"
  204. :rules="rules"
  205. validateTrigger="blur"
  206. err-show-type="toast">
  207. <uni-forms-item label="施工队伍" name="deptName">
  208. <span class="readOnly">{{ form.deptName }}</span>
  209. </uni-forms-item>
  210. <uni-forms-item label="项目" name="contractName">
  211. <span class="readOnly">{{ form.contractName }}</span>
  212. </uni-forms-item>
  213. <uni-forms-item label="任务" name="taskName">
  214. <span class="readOnly">{{ form.taskName }}</span>
  215. </uni-forms-item>
  216. <uni-forms-item label="施工状态" name="repairStatus" required>
  217. <uni-data-select
  218. :clear="true"
  219. align="right"
  220. placeholder="请选择"
  221. :localdata="rigStatusOptions"
  222. placement="top"
  223. :disabled="disabled('edit')"
  224. v-model="form.repairStatus" />
  225. </uni-forms-item>
  226. <uni-forms-item label="施工工艺" name="technique">
  227. <uni-data-select
  228. :clear="true"
  229. align="right"
  230. placeholder="请选择"
  231. :localdata="techniqueOptions"
  232. placement="top"
  233. :disabled="disabled('edit')"
  234. v-model="form.technique" />
  235. </uni-forms-item>
  236. <uni-forms-item label="井别" name="wellCategory">
  237. <uni-easyinput v-bind="defaultProps" v-model="form.wellCategory" :disabled="disabled('edit')" />
  238. </uni-forms-item>
  239. <uni-forms-item label="设计井深(m)" name="designWellDepth">
  240. <uni-easyinput v-bind="defaultProps" v-model="form.designWellDepth" :disabled="disabled('edit')" />
  241. </uni-forms-item>
  242. <uni-forms-item label="井控级别" name="wellControlLevel">
  243. <uni-easyinput v-bind="defaultProps" v-model="form.wellControlLevel" :disabled="disabled('edit')" />
  244. </uni-forms-item>
  245. <uni-forms-item label="套生段产管尺寸(mm)" name="casingPipeSize">
  246. <uni-easyinput v-bind="defaultProps" v-model="form.casingPipeSize" :disabled="disabled('edit')" />
  247. </uni-forms-item>
  248. <uni-forms-item label="目前工序" name="currentOperation">
  249. <uni-easyinput
  250. type="textarea"
  251. autoHeight
  252. v-bind="defaultProps"
  253. v-model="form.currentOperation"
  254. :disabled="disabled('edit')"
  255. :maxlength="1000" />
  256. </uni-forms-item>
  257. <uni-forms-item label="下步工序" name="nextPlan">
  258. <uni-easyinput
  259. type="textarea"
  260. autoHeight
  261. v-bind="defaultProps"
  262. v-model="form.nextPlan"
  263. :disabled="disabled('edit')"
  264. :maxlength="1000" />
  265. </uni-forms-item>
  266. <uni-forms-item label="运行时效" name="transitTime">
  267. <span class="readOnly" :class="{ 'red-text': transitTime.original > 1.0 }">{{ transitTime.value }}</span>
  268. </uni-forms-item>
  269. <uni-forms-item label="额定生产时间(H)" name="ratedProductionTime" required>
  270. <uni-easyinput
  271. type="number"
  272. v-bind="defaultProps"
  273. :class="{ 'orange-text': sumTimes() !== Number(form.ratedProductionTime) }"
  274. :disabled="disabled('edit')"
  275. v-model="form.ratedProductionTime" />
  276. </uni-forms-item>
  277. <uni-forms-item label="生产时间(H)" name="productionTime" required>
  278. <uni-easyinput
  279. type="number"
  280. v-bind="defaultProps"
  281. :class="{ 'orange-text': sumTimes() !== Number(form.ratedProductionTime) }"
  282. :disabled="disabled('edit')"
  283. v-model="form.productionTime" />
  284. </uni-forms-item>
  285. <uni-forms-item label="非生产时间(H)" name="nonProductionTime" required>
  286. <uni-easyinput
  287. type="number"
  288. v-bind="defaultProps"
  289. :class="{ 'orange-text': sumTimes() !== Number(form.ratedProductionTime) }"
  290. :disabled="disabled('edit')"
  291. v-model="form.nonProductionTime" />
  292. </uni-forms-item>
  293. <uni-forms-item label="非生产时间原因" name="ryNptReason">
  294. <uni-data-select
  295. :clear="true"
  296. align="right"
  297. placeholder="请选择"
  298. :localdata="nptReasonOptions"
  299. placement="top"
  300. :disabled="disabled('edit')"
  301. v-model="form.ryNptReason" />
  302. </uni-forms-item>
  303. <uni-forms-item label="全员数量" name="totalStaffNum">
  304. <uni-easyinput type="number" v-bind="defaultProps" :disabled="disabled('edit')" v-model="form.totalStaffNum" />
  305. </uni-forms-item>
  306. <uni-forms-item label="在岗人数" name="onDutyStaffNum">
  307. <uni-easyinput type="number" v-bind="defaultProps" disabled v-model="onDutyStaffNum" />
  308. </uni-forms-item>
  309. <uni-forms-item label="休假人员数量" name="leaveStaffNum">
  310. <uni-easyinput type="number" v-bind="defaultProps" :disabled="disabled('edit')" v-model="form.leaveStaffNum" />
  311. </uni-forms-item>
  312. <uni-forms-item label="生产动态" name="productionStatus" required>
  313. <uni-easyinput
  314. type="textarea"
  315. autoHeight
  316. v-bind="defaultProps"
  317. v-model="form.productionStatus"
  318. :disabled="disabled('edit')"
  319. :maxlength="1000" />
  320. </uni-forms-item>
  321. <uni-forms-item label="备注" name="remark">
  322. <uni-easyinput
  323. type="textarea"
  324. autoHeight
  325. v-bind="defaultProps"
  326. :disabled="disabled('edit')"
  327. v-model="form.remark"
  328. :maxlength="1000" />
  329. </uni-forms-item>
  330. <uni-forms-item v-if="type.includes('approval')" label="审批意见" name="opinion">
  331. <uni-easyinput
  332. type="textarea"
  333. autoHeight
  334. v-bind="defaultProps"
  335. :disabled="disabled('approval')"
  336. v-model="form.opinion"
  337. :maxlength="1000" />
  338. </uni-forms-item>
  339. </uni-forms>
  340. </view>
  341. </template>
  342. <style lang="scss" scoped>
  343. .content {
  344. background-color: white;
  345. padding: 16px 16px;
  346. border-radius: 8px;
  347. box-sizing: border-box;
  348. }
  349. .uni-forms {
  350. margin-top: 10px;
  351. height: 100%;
  352. .uni-form {
  353. height: 100%;
  354. }
  355. .uni-forms-item {
  356. display: flex;
  357. align-items: center;
  358. flex: 1;
  359. margin-bottom: 6px;
  360. border-bottom: 1px dashed #cacccf;
  361. }
  362. :deep(.uni-forms-item__content) {
  363. text-align: right;
  364. .readOnly {
  365. padding-right: 10px;
  366. }
  367. }
  368. :deep(.uni-forms-item__label) {
  369. height: 44px;
  370. font-weight: 500;
  371. font-size: 14px;
  372. color: #333333 !important;
  373. width: max-content !important;
  374. }
  375. :deep(.uni-select) {
  376. border: none;
  377. text-align: right;
  378. padding-right: 0;
  379. .uniui-bottom:before {
  380. content: '\e6b5' !important;
  381. font-size: 16px !important;
  382. }
  383. }
  384. :deep(.uni-easyinput__content-textarea) {
  385. min-height: inherit;
  386. margin: 10px;
  387. }
  388. :deep(.is-disabled) {
  389. color: #333333 !important;
  390. }
  391. :deep(.red-text > .is-disabled) {
  392. color: rgb(220 38 38 / 0.8) !important;
  393. }
  394. :deep(.orange-text > .is-disabled) {
  395. color: rgb(234 88 12 / 0.8) !important;
  396. }
  397. :deep(.uni-select--disabled) {
  398. background-color: #fff;
  399. }
  400. }
  401. .red-text {
  402. color: rgb(220 38 38 / 0.8) !important;
  403. }
  404. .orange-text {
  405. color: rgb(234 88 12 / 0.8) !important;
  406. }
  407. .red {
  408. border: 1px solid rgb(254 226 226);
  409. color: rgb(220 38 38 / 0.8);
  410. background-color: rgb(254 226 226);
  411. }
  412. .orange {
  413. border: 1px solid rgb(254 215 170);
  414. color: rgb(234 88 12 / 0.8);
  415. background-color: rgb(254 215 170);
  416. }
  417. .tip {
  418. border-radius: 8px;
  419. border: 1px solid #e5e5e5;
  420. background-color: rgba(239, 246, 255, 0.8);
  421. box-sizing: border-box;
  422. padding: 10px;
  423. display: flex;
  424. flex-direction: column;
  425. gap: 6px;
  426. .item {
  427. display: flex;
  428. align-items: center;
  429. justify-content: space-between;
  430. font-size: 12px;
  431. .left {
  432. color: rgb(75 85 99);
  433. span {
  434. color: rgb(31 41 55);
  435. font-weight: 600;
  436. }
  437. }
  438. .right {
  439. display: inline-flex;
  440. align-items: center;
  441. border-radius: 4px;
  442. padding: 2px 4px;
  443. font-weight: 500;
  444. }
  445. }
  446. }
  447. .opinion {
  448. border-radius: 8px;
  449. border: 1px solid rgb(254 240 138);
  450. background-color: rgb(254 252 232);
  451. box-sizing: border-box;
  452. padding: 10px;
  453. display: flex;
  454. align-items: center;
  455. justify-content: space-between;
  456. font-size: 12px;
  457. margin-top: 10px;
  458. .left {
  459. font-weight: 600;
  460. color: rgb(133 77 14);
  461. }
  462. .right {
  463. font-weight: 500;
  464. color: rgb(75 85 99);
  465. }
  466. }
  467. </style>