| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784 |
- <script setup>
- import { ref, reactive, getCurrentInstance, computed } from "vue";
- import tpfTimeRange from "@/components/tpf-time-range/tpf-time-range.vue";
- import { getDeptId, getUserInfo } from "@/utils/auth.js";
- import { selectedDeptsEmployee, specifiedSimpleDepts } from "@/api/index.js";
- import { onLoad } from "@dcloudio/uni-app";
- import { useDataDictStore } from "@/store/modules/dataDict";
- import { createIotRdDailyReport, getRuiDuReportDetail } from "@/api/ruiDu.js";
- import DaTree from "@/components/da-tree/index.vue";
- const { appContext } = getCurrentInstance();
- const t = appContext.config.globalProperties.$t;
- const NON_PROD_FIELDS = [
- { key: "repairTime", label: "设备故障" },
- { key: "selfStopTime", label: "设备保养" },
- { key: "accidentTime", label: "工程质量" },
- { key: "complexityTime", label: "技术受限" },
- { key: "rectificationTime", label: "生产组织" },
- { key: "waitingStopTime", label: "不可抗力" },
- { key: "partyaDesign", label: "甲方设计" },
- { key: "partyaPrepare", label: "甲方准备" },
- { key: "partyaResource", label: "甲方资源" },
- { key: "relocationTime", label: "生产配合" },
- { key: "winterBreakTime", label: "待命" },
- { key: "otherNptTime", label: "其他非生产时间" },
- ];
- const defaultProps = {
- inputBorder: false,
- clearable: false,
- placeholder: "请输入",
- style: {
- "text-align": "right",
- },
- styles: {
- disableColor: "#fff",
- },
- };
- const original = {
- deptId: getDeptId(),
- startTime: "08:00",
- endTime: "08:00",
- ...NON_PROD_FIELDS.reduce((acc, field) => ({ ...acc, [field.key]: 0 }), {}),
- winterBreakTime: 24,
- };
- const formRef = ref();
- const formDataBaseRules = reactive({
- deptId: {
- rules: [
- {
- required: true,
- errorMessage: "请选择施工部门",
- },
- ],
- },
- constructionBrief: {
- rules: [
- {
- required: true,
- errorMessage: "请输入施工简报",
- },
- ],
- },
- createTime: {
- rules: [
- {
- required: true,
- errorMessage: "请选择施工时间",
- },
- ],
- },
- location: {
- rules: [
- {
- required: true,
- errorMessage: "请输入施工地点",
- },
- ],
- },
- responsiblePerson: {
- rules: [
- {
- required: true,
- errorMessage: "请选择带班干部",
- },
- ],
- },
- rdStatus: {
- rules: [
- {
- required: true,
- errorMessage: "请选择施工状态",
- },
- ],
- },
- dailyFuel: {
- rules: [
- {
- required: true,
- errorMessage: "请输入当日油耗",
- },
- ],
- },
- startTime: {
- rules: [
- {
- required: true,
- errorMessage: `${t("operation.PleaseSelect")}${t("ruiDu.timeNode")}`,
- },
- ],
- },
- endTime: {
- rules: [
- {
- required: true,
- errorMessage: `${t("operation.PleaseSelect")}${t("ruiDu.timeNode")}`,
- },
- ],
- },
- productionStatus: {
- rules: [
- {
- required: true,
- errorMessage: `${t("operation.PleaseFillIn")}${t(
- "ruiDu.dailyProductionDynamic"
- )}`,
- },
- ],
- },
- nextPlan: {
- rules: [
- {
- required: true,
- errorMessage: `请输入下步工作计划`,
- },
- ],
- },
- });
- function debounce(func, delay) {
- let timer;
- return function (...args) {
- if (timer) clearTimeout(timer);
- timer = setTimeout(() => func.apply(this, args), delay);
- };
- }
- const handleInputRaw = (val, field) => {
- let num = Number(val);
- if (val === "" || isNaN(num) || num < 0) {
- num = 0;
- } else if (num > 24) {
- num = 24;
- }
- form.value[field] = num;
- };
- const onInputChange = debounce(handleInputRaw, 500);
- const form = ref({ ...original });
- const userOptions = ref([]);
- const submitterNames = ref("");
- async function loadUserOptions() {
- const res = await selectedDeptsEmployee({ deptIds: [getDeptId()] });
- userOptions.value = res.data.map((item) => ({
- text: item.nickname,
- value: item.id,
- raw: item,
- }));
- }
- const deptOptions = ref([]);
- const treeData = ref([]);
- const deptLoading = ref(false);
- const handleTree = (data, id, parentId, children) => {
- if (!Array.isArray(data)) {
- console.warn("data must be an array");
- return [];
- }
- const config = {
- id: id || "id",
- parentId: parentId || "parentId",
- childrenList: children || "children",
- };
- const childrenListMap = {};
- const nodeIds = {};
- const tree = [];
- for (const d of data) {
- const parentId = d[config.parentId];
- if (childrenListMap[parentId] == null) {
- childrenListMap[parentId] = [];
- }
- nodeIds[d[config.id]] = d;
- childrenListMap[parentId].push(d);
- }
- for (const d of data) {
- const parentId = d[config.parentId];
- if (nodeIds[parentId] == null) {
- tree.push(d);
- }
- }
- for (const t of tree) {
- adaptToChildrenList(t);
- }
- function adaptToChildrenList(o) {
- if (childrenListMap[o[config.id]] !== null) {
- o[config.childrenList] = childrenListMap[o[config.id]];
- }
- if (o[config.childrenList]) {
- for (const c of o[config.childrenList]) {
- adaptToChildrenList(c);
- }
- }
- }
- return tree;
- };
- async function loadDept() {
- deptLoading.value = true;
- try {
- function sortTreeBySort(treeNodes) {
- if (!treeNodes || !Array.isArray(treeNodes)) return treeNodes;
- const sortedNodes = [...treeNodes].sort((a, b) => {
- const sortA = a.sort != null ? a.sort : 999999;
- const sortB = b.sort != null ? b.sort : 999999;
- return sortA - sortB;
- });
- sortedNodes.forEach((node) => {
- node.disabled = node.type !== "3";
- if (node.children && Array.isArray(node.children)) {
- node.children = sortTreeBySort(node.children);
- }
- });
- return sortedNodes;
- }
- const depts = await specifiedSimpleDepts(getDeptId());
- deptOptions.value = depts.data.map((item) => ({
- text: item.name,
- value: item.id,
- raw: item,
- }));
- treeData.value = sortTreeBySort(handleTree(depts.data));
- } catch (error) {
- } finally {
- deptLoading.value = false;
- }
- }
- const popup = ref();
- const openPopup = () => {
- popup.value.open();
- };
- const selectDeptId = ref("");
- function handleTreeChange(values) {
- selectDeptId.value = values;
- }
- function handleConfirm() {
- form.value.deptId = selectDeptId.value;
- popup.value.close();
- }
- const rdStatusOptions = ref([]);
- const dictStore = useDataDictStore();
- function loadRdStatusOptions() {
- rdStatusOptions.value = dictStore.getStrDictOptions("rdStatus").map((v) => ({
- text: v.label,
- value: v.value,
- }));
- }
- const formatT = (arr) =>
- `${arr[0].toString().padStart(2, "0")}:${arr[1].toString().padStart(2, "0")}`;
- const id = ref();
- const isview = ref("create");
- const detail = ref();
- onLoad(async (query) => {
- if (dictStore.dataDict.length <= 0) {
- dictStore.loadDataDictList().then(() => {
- loadRdStatusOptions();
- });
- } else loadRdStatusOptions();
- await loadUserOptions();
- id.value = query.id;
- isview.value = query.isview ?? "create";
- await loadDept();
- const info = JSON.parse(JSON.parse(getUserInfo()));
- submitterNames.value = info.user.nickname;
- if (query.id && query.isview) {
- const res = await getRuiDuReportDetail({ id: query.id });
- detail.value = res.data;
- submitterNames.value = res.data.submitterNames;
- form.value = {
- deptId: res.data.deptId,
- location: res.data.location,
- responsiblePerson: res.data.responsiblePerson,
- startTime: formatT(res.data.startTime),
- endTime: formatT(res.data.endTime),
- rdStatus: res.data.rdStatus,
- dailyFuel: res.data.dailyFuel,
- productionStatus: res.data.productionStatus,
- nextPlan: res.data.nextPlan,
- externalRental: res.data.externalRental,
- malfunction: res.data.malfunction,
- otherNptReason: res.data.otherNptReason,
- createTime: res.data.createTime,
- constructionBrief: res.data.constructionBrief,
- ...NON_PROD_FIELDS.reduce(
- (acc, field) => ({ ...acc, [field.key]: res.data[field.key] }),
- {}
- ),
- };
- }
- });
- const reportDetailsTimeRangeRef = ref(null);
- const startTime = ref("00:00");
- const startDefaultTime = ref("08:00");
- const endTime = ref("24:00");
- const endDefaultTime = ref("08:00");
- const handleClickTimeRangeItem = () => {
- reportDetailsTimeRangeRef.value.open();
- };
- const reportDetailsTimeRange = (data) => {
- form.value.startTime = data[0];
- form.value.endTime = data[1];
- };
- const formLoading = ref(false);
- async function submitForm() {
- try {
- await formRef.value.validate();
- const totalNonProdTime = NON_PROD_FIELDS.reduce((sum, field) => {
- return sum + (Number(form.value[field.key]) || 0);
- }, 0);
- if (totalNonProdTime > 24) {
- uni.showToast({
- title: "非生产时间总和不能大于24小时",
- icon: "none",
- });
- return;
- }
- const otherTime = Number(form.value.otherNptTime) || 0;
- if (otherTime >= 1) {
- if (
- !form.value.otherNptReason ||
- form.value.otherNptReason.trim() === ""
- ) {
- uni.showToast({
- title: "其他非生产时间大于0小时,请填写其他非生产原因",
- icon: "none",
- duration: 2000,
- });
- return;
- }
- }
- await createIotRdDailyReport(form.value);
- uni.showToast({ title: t("operation.success"), icon: "none" });
- uni.navigateBack();
- } catch (error) {
- console.error("提交表单失败", error);
- } finally {
- formLoading.value = false;
- }
- }
- const disabled = computed(() => {
- if (isview.value !== "create") {
- if (isview.value === "time") {
- return !(
- detail.value?.contractName === null && detail.value?.taskName === null
- );
- }
- return true;
- }
- return false;
- });
- </script>
- <template>
- <view class="page">
- <scroll-view scroll-y="true" class="segmented-content">
- <view class="content">
- <uni-forms
- ref="formRef"
- labelWidth="auto"
- :rules="formDataBaseRules"
- :model="form"
- validateTrigger="submit"
- err-show-type="toast">
- <uni-forms-item label="施工队伍" name="deptId" required>
- <view class="deptName">
- <uni-data-select
- :clear="true"
- align="right"
- placeholder="请选择"
- :localdata="deptOptions"
- hideRight
- placement="bottom"
- :disabled="true"
- v-model="form.deptId" />
- <button
- class="popup-button"
- type="primary"
- size="mini"
- :disabled="deptLoading || disabled"
- @click="openPopup">
- 选择
- </button>
- </view>
- </uni-forms-item>
- <uni-forms-item label="施工日期" name="createTime" required>
- <uni-datetime-picker
- type="date"
- return-type="timestamp"
- :clear-icon="true"
- v-model="form.createTime"
- :border="false"
- :style="{
- 'text-align': 'right',
- 'float': 'right',
- }"
- :styles="{ disableColor: '#fff' }"
- :disabled="disabled" />
- </uni-forms-item>
- <uni-forms-item label="施工地点" name="location" required>
- <uni-easyinput
- type="textarea"
- autoHeight
- v-bind="defaultProps"
- v-model="form.location"
- :disabled="disabled"
- :maxlength="1000" />
- </uni-forms-item>
- <uni-forms-item label="带班干部" name="responsiblePerson" required>
- <uni-data-select
- :clear="true"
- multiple
- align="right"
- placeholder="请选择"
- :localdata="userOptions"
- placement="bottom"
- :disabled="disabled"
- v-model="form.responsiblePerson" />
- </uni-forms-item>
- <uni-forms-item label="填报人" name="submitterNames">
- <span class="readOnly">{{ submitterNames }}</span>
- </uni-forms-item>
- <uni-forms-item :label="`${$t('ruiDu.timeNode')}:`" required>
- <view
- class="item-content"
- @click="!disabled && handleClickTimeRangeItem()">
- <view
- class="time-range-item"
- v-if="form.startTime && form.endTime">
- {{ form.startTime }} 至 {{ form.endTime }}
- </view>
- <view class="time-range-item" v-else> '请选择' </view>
- </view>
- </uni-forms-item>
- <uni-forms-item label="施工状态" name="rdStatus" required>
- <uni-data-select
- :clear="true"
- align="right"
- placeholder="请选择"
- :localdata="rdStatusOptions"
- placement="bottom"
- :disabled="disabled"
- v-model="form.rdStatus" />
- </uni-forms-item>
- <uni-forms-item label="当日油耗(L)" name="dailyFuel" required>
- <uni-easyinput
- type="number"
- v-bind="defaultProps"
- :disabled="disabled"
- v-model.number="form.dailyFuel" />
- </uni-forms-item>
- <uni-forms-item label="当日生产动态" name="productionStatus" required>
- <uni-easyinput
- type="textarea"
- autoHeight
- v-bind="defaultProps"
- :disabled="disabled"
- v-model="form.productionStatus"
- :maxlength="1000" />
- </uni-forms-item>
- <uni-forms-item label="下步工作计划" name="nextPlan" required>
- <uni-easyinput
- type="textarea"
- autoHeight
- v-bind="defaultProps"
- :disabled="disabled"
- v-model="form.nextPlan"
- :maxlength="1000" />
- </uni-forms-item>
- <uni-forms-item label="外租设备" name="externalRental">
- <uni-easyinput
- type="textarea"
- autoHeight
- v-bind="defaultProps"
- :disabled="disabled"
- v-model="form.externalRental"
- :maxlength="1000" />
- </uni-forms-item>
- <uni-forms-item label="故障情况" name="malfunction">
- <uni-easyinput
- type="textarea"
- autoHeight
- v-bind="defaultProps"
- :disabled="disabled"
- v-model="form.malfunction"
- :maxlength="1000" />
- </uni-forms-item>
- <uni-forms-item label="施工简报" name="constructionBrief" required>
- <uni-easyinput
- type="textarea"
- autoHeight
- v-bind="defaultProps"
- v-model="form.constructionBrief"
- :disabled="disabled"
- :maxlength="1000" />
- </uni-forms-item>
- <uv-divider text="非生产时间" textPosition="left"></uv-divider>
- <uni-forms-item
- v-for="field in NON_PROD_FIELDS"
- :key="field.key"
- :label="field.label + '(H)'"
- :name="field.key">
- <uni-easyinput
- type="number"
- v-bind="defaultProps"
- v-model.number="form[field.key]"
- :disabled="isview === 'detail'"
- @input="(val) => onInputChange(val, field.key)" />
- </uni-forms-item>
- <uni-forms-item label="其他非生产原因" name="otherNptReason">
- <uni-easyinput
- type="textarea"
- autoHeight
- v-bind="defaultProps"
- v-model="form.otherNptReason"
- :disabled="isview === 'detail'"
- :maxlength="1000" />
- </uni-forms-item>
- </uni-forms>
- </view>
- </scroll-view>
- <view class="segmented-footer">
- <view class="footer-btn">
- <button
- :loading="formLoading"
- class="confirm-btn"
- type="primary"
- :disabled="isview === 'detail'"
- @click="submitForm()">
- 确定
- </button>
- </view>
- </view>
- </view>
- <tpf-time-range
- ref="reportDetailsTimeRangeRef"
- :startTime="startTime"
- :startDefaultTime="startDefaultTime"
- :endTime="endTime"
- :endDefaultTime="endDefaultTime"
- @timeRange="reportDetailsTimeRange"></tpf-time-range>
- <uni-popup ref="popup" type="bottom">
- <view class="popup-content">
- <view class="tree">
- <DaTree
- :data="treeData"
- labelField="name"
- valueField="id"
- disabledField="disabled"
- defaultExpandAll
- checkedDisabled
- :defaultCheckedKeys="form.deptId"
- @change="handleTreeChange"></DaTree>
- </view>
- <button class="mini-btn" type="primary" @click="handleConfirm">
- 确定
- </button>
- </view>
- </uni-popup>
- </template>
- <style lang="scss" scoped>
- @import "@/style/work-order-segmented.scss";
- .page {
- padding-bottom: 0;
- }
- .content {
- background-color: white;
- padding: 16px 16px;
- border-radius: 8px;
- box-sizing: border-box;
- }
- .uni-forms {
- margin-top: 10px;
- height: 100%;
- .uni-form {
- height: 100%;
- }
- .uni-forms-item {
- display: flex;
- align-items: center;
- flex: 1;
- margin-bottom: 6px;
- border-bottom: 1px dashed #cacccf;
- }
- :deep(.uni-forms-item__content) {
- text-align: right;
- .readOnly {
- padding-right: 10px;
- }
- }
- :deep(.uni-forms-item__label) {
- height: 44px;
- font-weight: 500;
- font-size: 14px;
- color: #333333 !important;
- width: max-content !important;
- }
- :deep(.uni-select) {
- border: none;
- text-align: right;
- padding-right: 0;
- .uniui-bottom:before {
- content: "\e6b5" !important;
- font-size: 16px !important;
- }
- }
- :deep(.uni-easyinput__content-textarea) {
- min-height: inherit;
- margin: 10px;
- }
- :deep(.is-disabled) {
- color: #333333 !important;
- }
- :deep(.uni-date-editor--x__disabled) {
- opacity: 1;
- .uni-date__x-input {
- color: #333333 !important;
- }
- }
- :deep(.icon-calendar) {
- display: none;
- }
- :deep(.uni-select--disabled) {
- background-color: #fff;
- }
- }
- .item-content {
- display: flex;
- align-items: center;
- justify-content: end;
- }
- .time-range-item {
- margin: 10px;
- }
- .footer-btn {
- display: flex;
- justify-content: flex-end;
- align-items: center;
- padding: 0 16px;
- height: 100%;
- gap: 0 32px;
- & > uni-button {
- margin: 0;
- }
- }
- :deep(.confirm-btn) {
- height: 38px !important;
- font-size: 16px !important;
- }
- :deep(.popup-button) {
- width: 62px;
- margin: 0;
- }
- :deep(.deptName) {
- display: flex;
- justify-content: end;
- align-items: center;
- gap: 0 10px;
- }
- @mixin flex {
- /* #ifndef APP-NVUE */
- display: flex;
- /* #endif */
- flex-direction: column;
- }
- @mixin height {
- /* #ifndef APP-NVUE */
- height: 100%;
- /* #endif */
- /* #ifdef APP-NVUE */
- flex: 1;
- /* #endif */
- }
- .popup-content {
- @include flex;
- justify-content: space-between;
- padding: 15px;
- height: 500px;
- background-color: #fff;
- .tree {
- flex: 1;
- max-height: 420px;
- }
- button {
- width: 100%;
- height: 44px;
- }
- }
- </style>
|