Browse Source

巡检工单

lipenghui 3 months ago
parent
commit
24196eb0ca

+ 13 - 1
src/router/modules/remaining.ts

@@ -458,7 +458,19 @@ const remainingRouter: AppRouteRecordRaw[] = [
             title: '巡检工单详情',
             activeMenu: '/inspect/order/detail'
           }
-      },
+      },{
+        path: '/inspect/order/write/:id(\\d+)',
+        component: () => import('@/views/pms/inspect/order/WriteOrder.vue'),
+        name: 'InspectOrderWrite',
+        meta: {
+          noCache: false,
+          hidden: true,
+          canTo: true,
+          icon: 'ep:add',
+          title: '巡检工单填写',
+          activeMenu: '/inspect/order/write'
+        }
+      }
     ]
   },
   {

+ 172 - 0
src/views/pms/inspect/order/OrderComponent.vue

@@ -0,0 +1,172 @@
+<template>
+  <div class="step-container">
+    <!-- 横向滚动步骤条 -->
+    <div class="steps-wrapper" ref="stepsContainer">
+      <el-steps :active="currentStep" finish-status="success" class="dynamic-steps" simple>
+        <el-step
+          v-for="(step, index) in totalSteps"
+          :key="index"
+          :title="`步骤 ${index + 1}`"
+          :ref="setStepRef"
+        />
+      </el-steps>
+    </div>
+
+    <!-- 通用表单 -->
+    <div class="form-wrapper">
+      <el-form :model="formData[currentStep]" :rules="formRules" ref="formRef" label-width="120px">
+        <el-form-item label="姓名" prop="name">
+          <el-input v-model="formData[currentStep].name" />
+        </el-form-item>
+        <el-form-item label="邮箱" prop="email">
+          <el-input v-model="formData[currentStep].email" />
+        </el-form-item>
+        <el-form-item label="验证码" prop="code">
+          <el-input v-model="formData[currentStep].code" />
+        </el-form-item>
+      </el-form>
+    </div>
+
+    <!-- 导航按钮 -->
+    <div class="action-buttons">
+      <el-button @click="prevStep" :disabled="currentStep === 0"> 上一步 </el-button>
+      <span class="step-indicator"> 当前步骤:{{ currentStep + 1 }}/{{ totalSteps }} </span>
+      <el-button type="primary" @click="nextStep" :disabled="currentStep === totalSteps - 1">
+        {{ isLastStep ? '完成' : '下一步' }}
+      </el-button>
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { computed, onMounted, ref } from 'vue'
+import { ElMessage } from 'element-plus'
+import {any} from "vue-types";
+
+const totalSteps = 30 // 总步骤数
+const currentStep = ref(0) // 当前步骤索引
+const stepsContainer = ref(null) // 步骤容器引用
+const stepElements = [] // 步骤元素集合
+const formRef = ref(null) // 表单引用
+defineOptions({ name: 'OrderComponent' })
+const props = defineProps<{ data: any }>(); // 搜索参数
+
+// 初始化表单数据
+const formData = ref(
+  Array.from({ length: totalSteps }, () => ({
+    name: '',
+    email: '',
+    code: ''
+  }))
+)
+
+// 表单验证规则
+const formRules = {
+  name: [{ required: true, message: '请输入姓名', trigger: 'blur' }],
+  email: [
+    { required: true, message: '请输入邮箱', trigger: 'blur' },
+    { type: 'email', message: '邮箱格式不正确', trigger: 'blur' }
+  ],
+  code: [
+    { required: true, message: '请输入验证码', trigger: 'blur' },
+    { pattern: /^\d{6}$/, message: '请输入6位数字验证码' }
+  ]
+}
+
+// 步骤元素引用收集
+const setStepRef = (el) => {
+  if (el) stepElements.push(el)
+}
+
+// 自动滚动到当前步骤
+const scrollToCurrentStep = () => {
+  if (!stepsContainer.value) return
+
+  const container = stepsContainer.value
+  const currentEl = stepElements[currentStep.value]?.$el
+
+  if (currentEl) {
+    const containerWidth = container.offsetWidth
+    const stepLeft = currentEl.offsetLeft
+    const stepWidth = currentEl.offsetWidth
+
+    container.scrollTo({
+      left: stepLeft - containerWidth / 2 + stepWidth / 2,
+      behavior: 'smooth'
+    })
+  }
+}
+
+// 导航逻辑
+const prevStep = () => {
+  if (currentStep.value > 0) currentStep.value--
+}
+
+const nextStep = async () => {
+  try {
+    // 验证当前表单
+    await formRef.value.validate()
+
+    if (currentStep.value < totalSteps - 1) {
+      currentStep.value++
+    } else {
+      ElMessage.success('所有步骤已完成!')
+      console.log('最终提交数据:', formData.value)
+    }
+  } catch (error) {
+    ElMessage.error('请正确填写当前步骤表单')
+  }
+}
+
+// 计算属性
+const isLastStep = computed(() => currentStep.value === totalSteps - 1)
+
+// 生命周期钩子
+onMounted(() => {
+  // 初始化时滚动到第一个步骤
+  scrollToCurrentStep()
+  debugger
+})
+</script>
+
+<style scoped>
+.step-container {
+  max-width: 100%;
+  //margin: 20px auto;
+  //padding: 20px;
+}
+
+.steps-wrapper {
+  overflow-x: auto;
+  margin-bottom: 30px;
+  padding: 10px 0;
+}
+
+.dynamic-steps {
+  width: max-content;
+  min-width: 100%;
+}
+
+.el-step {
+  flex-shrink: 0;
+  min-width: 120px;
+}
+
+.form-wrapper {
+  border: 1px solid #ebeef5;
+  border-radius: 4px;
+  padding: 20px;
+  margin: 20px 0;
+}
+
+.action-buttons {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+}
+
+.step-indicator {
+  color: #666;
+  font-size: 14px;
+}
+</style>

+ 251 - 0
src/views/pms/inspect/order/WriteOrder.vue

@@ -0,0 +1,251 @@
+<template>
+    <ContentWrap>
+    <el-tabs v-model="activeTab" type="border-card" tab-position="left" v-loading="loading" style="height: 80vh">
+      <el-tab-pane style="height: 100%"
+        v-for="(tab, tabIndex) in tabs"
+        :key="tab.deviceName"
+        :name="String(tabIndex)"
+      >
+        <template #label>
+          <span class="custom-label">
+            {{ tab.deviceName }} ({{ completedSteps(tabIndex) }}/{{ JSON.parse(tab.itemJson).length }})
+          </span>
+        </template>
+
+        <div class="step-container">
+          <el-steps
+            direction="vertical"
+            :active="currentStep[tabIndex]"
+            class="steps-nav"
+          >
+            <el-step
+              v-for="(step, stepIndex) in JSON.parse(tab.itemJson)"
+              :key="stepIndex"
+              :title="`${stepIndex + 1} ${step.item}`"
+              :status="getStepStatus(tabIndex, stepIndex)"
+            />
+          </el-steps>
+
+          <div class="form-wrapper">
+            <el-form
+              :model="formData[tabIndex][currentStep[tabIndex]]"
+              :rules="formRules"
+              ref="formRefs"
+              label-width="120px"
+            >
+              <el-form-item label="是否异常" prop="ifNormal">
+                <el-select
+                  v-model="formData[tabIndex][currentStep[tabIndex]].ifNormal"
+                  placeholder="请选择是否异常"
+                  clearable
+                >
+                  <el-option
+                    v-for="dict in getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)"
+                    :key="dict.value"
+                    :label="dict.label"
+                    :value="dict.value"
+                  />
+                </el-select>
+              </el-form-item>
+
+              <el-form-item label="异常描述" prop="description">
+                <el-input
+                  v-model="formData[tabIndex][currentStep[tabIndex]].description"
+                  :min="0"
+                  type="textarea"
+                  controls-position="right"
+                  placeholder="请填写异常描述"
+                />
+              </el-form-item>
+
+              <el-form-item label="图片" prop="picUrl">
+                <UploadImg v-model="formData[tabIndex][currentStep[tabIndex]].picUrl" height="55px" width="30em" />
+              </el-form-item>
+            </el-form>
+
+            <div class="navigation-controls">
+              <el-button
+                :disabled="currentStep[tabIndex] === 0"
+                @click="handlePrev(tabIndex)"
+              >
+                上一步
+              </el-button>
+
+              <el-button
+                type="primary"
+                :disabled="!isStepValid(tabIndex)"
+                @click="handleNext(tabIndex)"
+              >
+                {{ isLastStep(tabIndex) ? '完成提交' : '下一步' }}
+              </el-button>
+            </div>
+          </div>
+        </div>
+      </el-tab-pane>
+    </el-tabs>
+    </ContentWrap>
+</template>
+
+<script setup>
+import { ref, reactive, onMounted } from 'vue'
+import { ElMessage } from 'element-plus'
+import {IotInspectOrderApi} from "@/api/pms/inspect/order";
+import {DICT_TYPE, getBoolDictOptions} from "@/utils/dict";
+
+const tabs = ref([])
+const { params, name } = useRoute() // 查询参数
+const id = params.id
+onMounted(async () => {
+  if (id) {
+    const iotInspectOrder = await IotInspectOrderApi.getIotInspectOrder(id)
+    tabs.value = JSON.parse(iotInspectOrder.deviceIds)
+    initFormData(JSON.parse(iotInspectOrder.deviceIds))
+    loading.value = false
+  }
+})
+
+// 响应式状态
+const loading = ref(true)
+const activeTab = ref('0')
+const currentStep = ref({})
+const formRefs = ref([])
+const formData = reactive([])
+
+// 表单验证规则
+const formRules = reactive({
+  ifNormal: [
+    { required: true, message: '请输入是否异常', trigger: 'blur' }
+  ],
+  description: [
+    { required: true, message: '请输入异常描述', trigger: 'blur' },
+  ],
+
+})
+
+// 初始化表单数据
+const initFormData = (tabsData) => {
+  tabsData.forEach((tab, tabIndex) => {
+    formData[tabIndex] = JSON.parse(tab.itemJson).map(() => ({
+      ifNormal: '',
+      description: null,
+      picUrl: ''
+    }))
+    currentStep.value[tabIndex] = 0
+  })
+}
+
+// 获取数据
+const fetchData = async () => {
+  try {
+    const data = await mockApi()
+    tabs.value = data
+    initFormData(data)
+  } catch (error) {
+    ElMessage.error('数据加载失败')
+  } finally {
+  }
+}
+
+// 步骤状态计算
+const getStepStatus = (tabIndex, stepIndex) => {
+  if (stepIndex < currentStep.value[tabIndex]) return 'finish'
+  return stepIndex === currentStep.value[tabIndex] ? 'process' : 'wait'
+}
+
+// 完成步骤数计算
+const completedSteps = (tabIndex) => {
+  return formData[tabIndex].filter(item =>
+    item.ifNormal && item.description !== null
+  ).length
+}
+
+// 步骤验证
+const isStepValid = (tabIndex) => {
+  const current = currentStep.value[tabIndex]
+  debugger
+  return formData[tabIndex][current].ifNormal &&
+    formData[tabIndex][current].description !== null
+}
+
+// 导航控制
+const handlePrev = (tabIndex) => {
+  if (currentStep.value[tabIndex] > 0) {
+    currentStep.value[tabIndex]--
+  }
+}
+
+const handleNext = async (tabIndex) => {
+  if (!isStepValid(tabIndex)) {
+    return ElMessage.warning('请先完成当前步骤的必填项')
+  }
+
+  const totalSteps = JSON.parse(tabs.value[tabIndex].itemJson).length
+  if (currentStep.value[tabIndex] < totalSteps - 1) {
+    currentStep.value[tabIndex]++
+  } else {
+    await submitForm(tabIndex)
+  }
+}
+
+const isLastStep = (tabIndex) => {
+  return currentStep.value[tabIndex] === JSON.parse(tabs.value[tabIndex].itemJson).length - 1
+}
+
+// 提交表单
+const submitForm = async (tabIndex) => {
+  try {
+    debugger
+    // 这里添加实际提交逻辑
+    ElMessage.success(`提交成功:${tabs.value[tabIndex].deviceName}`)
+  } catch (error) {
+    ElMessage.error('提交失败,请检查数据')
+  }
+}
+
+</script>
+
+<style scoped>
+.container {
+  padding: 10px;
+  background: #f5f7fa;
+}
+
+.step-container {
+  display: grid;
+  grid-template-columns: 220px 1fr;
+  gap: 30px;
+  height: 100%;
+  min-height: 600px;
+}
+
+.steps-nav {
+  height: 100%;
+  overflow-y: auto;
+  padding-right: 15px;
+}
+
+.form-wrapper {
+  background: #fff;
+  padding: 30px;
+  border-radius: 8px;
+  box-shadow: 0 2px 12px rgba(0,0,0,0.1);
+}
+
+.navigation-controls {
+  margin-top: 40px;
+  text-align: center;
+}
+
+.custom-label {
+  font-weight: 500;
+  padding: 0 15px;
+}
+
+.el-step__title {
+  font-size: 14px;
+}
+
+.el-form-item {
+  margin-bottom: 22px;
+}
+</style>

+ 148 - 134
src/views/pms/inspect/order/index.vue

@@ -1,145 +1,153 @@
 <template>
-  <ContentWrap>
-    <!-- 搜索工作栏 -->
-    <el-form
-      class="-mb-15px"
-      :model="queryParams"
-      ref="queryFormRef"
-      :inline="true"
-      label-width="68px"
-    >
-      <el-form-item label="工单名" prop="inspectOrderTitle">
-        <el-input
-          v-model="queryParams.inspectOrderTitle"
-          placeholder="请输入工单名"
-          clearable
-          @keyup.enter="handleQuery"
-          class="!w-240px"
-        />
-      </el-form-item>
-      <el-form-item label="工单编码" prop="inspectOrderCode">
-        <el-input
-          v-model="queryParams.inspectOrderCode"
-          placeholder="请输入工单编码"
-          clearable
-          @keyup.enter="handleQuery"
-          class="!w-240px"
-        />
-      </el-form-item>
-      <el-form-item label="工单状态" prop="status">
-        <el-select
-          v-model="queryParams.status"
-          placeholder="工单状态"
-          clearable
-          class="!w-240px"
-        >
-          <el-option
-            v-for="dict in getStrDictOptions(DICT_TYPE.PMS_INSPECT_ORDER_STATUS)"
-            :key="dict.value"
-            :label="dict.label"
-            :value="dict.value"
+  <el-row :gutter="20">
+    <el-col :span="4" :xs="24">
+      <ContentWrap class="h-1/1">
+        <DeptTree @node-click="handleDeptNodeClick" />
+      </ContentWrap>
+    </el-col>
+    <el-col :span="20" :xs="24">
+    <ContentWrap>
+      <!-- 搜索工作栏 -->
+      <el-form
+        class="-mb-15px"
+        :model="queryParams"
+        ref="queryFormRef"
+        :inline="true"
+        label-width="68px"
+      >
+        <el-form-item label="工单名" prop="inspectOrderTitle">
+          <el-input
+            v-model="queryParams.inspectOrderTitle"
+            placeholder="请输入工单名"
+            clearable
+            @keyup.enter="handleQuery"
+            class="!w-240px"
           />
-        </el-select>
-      </el-form-item>
-      <el-form-item label="创建时间" prop="createTime">
-        <el-date-picker
-          v-model="queryParams.createTime"
-          value-format="YYYY-MM-DD HH:mm:ss"
-          type="daterange"
-          start-placeholder="开始日期"
-          end-placeholder="结束日期"
-          :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
-          class="!w-220px"
-        />
-      </el-form-item>
-      <el-form-item>
-        <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
-        <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
-        <el-button
-          type="primary"
-          plain
-          @click="openForm('create')"
-          v-hasPermi="['rq:iot-inspect-order:create']"
-        >
-          <Icon icon="ep:plus" class="mr-5px" /> 新增
-        </el-button>
-        <el-button
-          type="success"
-          plain
-          @click="handleExport"
-          :loading="exportLoading"
-          v-hasPermi="['rq:iot-inspect-order:export']"
-        >
-          <Icon icon="ep:download" class="mr-5px" /> 导出
-        </el-button>
-      </el-form-item>
-    </el-form>
-  </ContentWrap>
-
-  <!-- 列表 -->
-  <ContentWrap>
-    <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
-      <el-table-column label="工单名称" align="center" prop="inspectOrderTitle" />
-      <el-table-column label="工单编码" align="center" prop="inspectOrderCode" />
-      <el-table-column label="工单类型" align="center" prop="type" />
-      <el-table-column label="工单状态" align="center" prop="status" >
-        <template #default="scope">
-          <dict-tag :type="DICT_TYPE.PMS_INSPECT_ORDER_STATUS" :value="scope.row.status" />
-        </template>
-      </el-table-column>
-      <el-table-column label="备注" align="center" prop="remark" />
-      <el-table-column
-        label="创建时间"
-        align="center"
-        prop="createTime"
-        :formatter="dateFormatter"
-        width="180px"
-      />
-      <el-table-column label="操作" align="center" min-width="120px">
-        <template #default="scope">
-<!--          <el-button-->
-<!--            link-->
-<!--            type="primary"-->
-<!--            @click="openForm('update', scope.row.id)"-->
-<!--            v-hasPermi="['rq:iot-inspect-order:update']"-->
-<!--          >-->
-<!--            编辑-->
-<!--          </el-button>-->
-<!--          <el-button-->
-<!--            link-->
-<!--            type="danger"-->
-<!--            @click="handleDelete(scope.row.id)"-->
-<!--            v-hasPermi="['rq:iot-inspect-order:delete']"-->
-<!--          >-->
-<!--            删除-->
-<!--          </el-button>-->
+        </el-form-item>
+        <el-form-item label="工单编码" prop="inspectOrderCode">
+          <el-input
+            v-model="queryParams.inspectOrderCode"
+            placeholder="请输入工单编码"
+            clearable
+            @keyup.enter="handleQuery"
+            class="!w-240px"
+          />
+        </el-form-item>
+        <el-form-item label="工单状态" prop="status">
+          <el-select
+            v-model="queryParams.status"
+            placeholder="工单状态"
+            clearable
+            class="!w-240px"
+          >
+            <el-option
+              v-for="dict in getStrDictOptions(DICT_TYPE.PMS_INSPECT_ORDER_STATUS)"
+              :key="dict.value"
+              :label="dict.label"
+              :value="dict.value"
+            />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="创建时间" prop="createTime">
+          <el-date-picker
+            v-model="queryParams.createTime"
+            value-format="YYYY-MM-DD HH:mm:ss"
+            type="daterange"
+            start-placeholder="开始日期"
+            end-placeholder="结束日期"
+            :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
+            class="!w-220px"
+          />
+        </el-form-item>
+        <el-form-item>
+          <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
+          <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
           <el-button
-            link
             type="primary"
-            @click="openForm(scope.row.id)"
+            plain
+            @click="openForm('create')"
+            v-hasPermi="['rq:iot-inspect-order:create']"
           >
-            查看
+            <Icon icon="ep:plus" class="mr-5px" /> 新增
           </el-button>
           <el-button
-            link
-            type="primary"
-            @click="openForm('update', scope.row.id)"
-            v-hasPermi="['rq:iot-inspect-order:update']"
+            type="success"
+            plain
+            @click="handleExport"
+            :loading="exportLoading"
+            v-hasPermi="['rq:iot-inspect-order:export']"
           >
-            填写
+            <Icon icon="ep:download" class="mr-5px" /> 导出
           </el-button>
-        </template>
-      </el-table-column>
-    </el-table>
-    <!-- 分页 -->
-    <Pagination
-      :total="total"
-      v-model:page="queryParams.pageNo"
-      v-model:limit="queryParams.pageSize"
-      @pagination="getList"
-    />
-  </ContentWrap>
+        </el-form-item>
+      </el-form>
+    </ContentWrap>
 
+    <!-- 列表 -->
+    <ContentWrap>
+      <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
+        <el-table-column label="工单名称" align="center" prop="inspectOrderTitle" />
+        <el-table-column label="工单编码" align="center" prop="inspectOrderCode" />
+        <el-table-column label="工单类型" align="center" prop="type" />
+        <el-table-column label="工单状态" align="center" prop="status" >
+          <template #default="scope">
+            <dict-tag :type="DICT_TYPE.PMS_INSPECT_ORDER_STATUS" :value="scope.row.status" />
+          </template>
+        </el-table-column>
+        <el-table-column label="备注" align="center" prop="remark" />
+        <el-table-column
+          label="创建时间"
+          align="center"
+          prop="createTime"
+          :formatter="dateFormatter"
+          width="180px"
+        />
+        <el-table-column label="操作" align="center" min-width="120px">
+          <template #default="scope">
+  <!--          <el-button-->
+  <!--            link-->
+  <!--            type="primary"-->
+  <!--            @click="openForm('update', scope.row.id)"-->
+  <!--            v-hasPermi="['rq:iot-inspect-order:update']"-->
+  <!--          >-->
+  <!--            编辑-->
+  <!--          </el-button>-->
+  <!--          <el-button-->
+  <!--            link-->
+  <!--            type="danger"-->
+  <!--            @click="handleDelete(scope.row.id)"-->
+  <!--            v-hasPermi="['rq:iot-inspect-order:delete']"-->
+  <!--          >-->
+  <!--            删除-->
+  <!--          </el-button>-->
+            <el-button
+              link
+              type="primary"
+              @click="openForm(scope.row.id)"
+            >
+              查看
+            </el-button>
+            <el-button
+              link
+              type="primary"
+              @click="openWrite(scope.row.id)"
+              v-hasPermi="['rq:iot-inspect-order:update']"
+            >
+              填写
+            </el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+      <!-- 分页 -->
+      <Pagination
+        :total="total"
+        v-model:page="queryParams.pageNo"
+        v-model:limit="queryParams.pageSize"
+        @pagination="getList"
+      />
+    </ContentWrap>
+    </el-col>
+  </el-row>
   <!-- 表单弹窗:添加/修改 -->
   <IotInspectOrderForm ref="formRef" @success="getList" />
 </template>
@@ -150,6 +158,7 @@ import download from '@/utils/download'
 import { IotInspectOrderApi, IotInspectOrderVO } from '@/api/pms/inspect/order'
 import IotInspectOrderForm from './IotInspectOrderForm.vue'
 import {DICT_TYPE, getStrDictOptions} from "@/utils/dict";
+import DeptTree from "@/views/system/user/DeptTree.vue";
 const { push } = useRouter()
 
 /** 巡检工单 列表 */
@@ -174,7 +183,10 @@ const queryParams = reactive({
 })
 const queryFormRef = ref() // 搜索的表单
 const exportLoading = ref(false) // 导出的加载中
-
+const handleDeptNodeClick = async (row) => {
+  queryParams.deptId = row.id
+  await getList()
+}
 /** 查询列表 */
 const getList = async () => {
   loading.value = true
@@ -204,7 +216,9 @@ const formRef = ref()
 const openForm = (id?: number) => {
   push({ name: 'InspectOrderDetail', params:{id} })
 }
-
+const openWrite = (id?: number) => {
+  push({ name: 'InspectOrderWrite', params:{id} })
+}
 /** 删除按钮操作 */
 const handleDelete = async (id: number) => {
   try {