yanghao пре 1 дан
родитељ
комит
53b7efee81

+ 12 - 9
src/components/mt-preview/index.vue

@@ -3,7 +3,7 @@
     <el-scrollbar
       ref="elScrollbarRef"
       class="w-1/1 h-1/1"
-      max-height="100vh"
+      max-height="100%"
       @scroll="onScroll">
       <div
         class="canvasStage">
@@ -120,12 +120,15 @@ const fitCanvasToScreen = () => {
     return
   }
 
-  const rootRect = previewRootRef.value?.getBoundingClientRect()
-  if (!rootRect?.width || !rootRect?.height || !canvas_cfg.value.width || !canvas_cfg.value.height) {
+  const root = previewRootRef.value
+  if (!root?.clientWidth || !root?.clientHeight || !canvas_cfg.value.width || !canvas_cfg.value.height) {
     return
   }
 
-  const scale = Math.min(rootRect.width / canvas_cfg.value.width, rootRect.height / canvas_cfg.value.height)
+  const scale = Math.min(
+    root.clientWidth / canvas_cfg.value.width,
+    root.clientHeight / canvas_cfg.value.height
+  )
   canvas_cfg.value.scale = Number(Math.max(scale, 0.01).toFixed(4))
 }
 const fitCanvasToScreenAfterRender = async () => {
@@ -204,16 +207,16 @@ defineExpose({
 </script>
 <style scoped>
 .mt-preview-root {
-  width: 100vw;
-  height: 100vh;
+  width: 100%;
+  height: 100%;
   overflow: hidden;
   background-color: v-bind('canvas_cfg.color || "#fff"');
 }
 
 .mt-preview-root :deep(.el-scrollbar__view) {
-  min-width: 100%;
-  min-height: 100vh;
   display: flex;
+  min-width: 100%;
+  min-height: 100%;
   align-items: center;
   justify-content: center;
 }
@@ -227,8 +230,8 @@ defineExpose({
 
 .canvasArea {
   position: absolute;
-  left: 0;
   top: 0;
+  left: 0;
   width: v-bind('canvas_cfg.width + "px"');
   height: v-bind('canvas_cfg.height + "px"');
   background-color: v-bind('canvas_cfg.color');

+ 107 - 1
src/views/maotu/preview.vue

@@ -1,5 +1,26 @@
 <template>
-  <mt-preview ref="MtPreviewRef" @on-event-call-back="onEventCallBack" />
+  <div :class="['preview-page', { 'preview-page--switched': orientationSwitched }]">
+    <mt-preview ref="MtPreviewRef" @on-event-call-back="onEventCallBack" />
+    <button
+      class="orientation-toggle"
+      type="button"
+      aria-label="切换显示方向"
+      @click="orientationSwitched = !orientationSwitched">
+      <svg
+        class="orientation-toggle__icon"
+        aria-hidden="true"
+        viewBox="0 0 24 24"
+        fill="none">
+        <path
+          d="M20 11a8 8 0 1 0-2.34 5.66M20 4v7h-7"
+          stroke="currentColor"
+          stroke-width="2"
+          stroke-linecap="round"
+          stroke-linejoin="round" />
+      </svg>
+      <span class="orientation-toggle__text">切换方向</span>
+    </button>
+  </div>
 </template>
 <script setup lang="ts">
 import { WebtopoProjectApi, parseWebtopoDataModel } from '@/api/pms/maotu'
@@ -16,6 +37,7 @@ const MtPreviewRef = ref<InstanceType<typeof MtPreview>>()
 const previewData = ref<IExportJson>()
 const pollingTimer = ref<number>()
 const polling = ref(false)
+const orientationSwitched = ref(false)
 
 const getProjectId = () => {
   const id = Number(route.params.id)
@@ -169,3 +191,87 @@ onUnmounted(() => {
   stopDeviceBindPolling()
 })
 </script>
+
+<style scoped>
+.preview-page {
+  position: relative;
+  width: 100vw;
+  width: 100dvw;
+  height: 100vh;
+  height: 100dvh;
+  overflow: hidden;
+}
+
+.preview-page--switched {
+  position: fixed;
+  top: 0;
+  left: 0;
+  width: 100vh;
+  width: 100dvh;
+  height: 100vw;
+  height: 100dvw;
+  transform: rotate(90deg) translateY(-100%);
+  transform-origin: left top;
+}
+
+.orientation-toggle {
+  position: fixed;
+  top: max(12px, env(safe-area-inset-top));
+  right: max(12px, env(safe-area-inset-right));
+  z-index: 10;
+  display: inline-flex;
+  height: 36px;
+  padding: 0 14px;
+  font-size: 14px;
+  line-height: 1;
+  color: #fff;
+  cursor: pointer;
+  background: rgb(0 0 0 / 65%);
+  border: 1px solid rgb(255 255 255 / 35%);
+  border-radius: 18px;
+  box-shadow: 0 2px 10px rgb(0 0 0 / 25%);
+  align-items: center;
+  gap: 6px;
+  backdrop-filter: blur(6px);
+  -webkit-tap-highlight-color: transparent;
+}
+
+.orientation-toggle:active {
+  background: rgb(0 0 0 / 80%);
+}
+
+.orientation-toggle__icon {
+  width: 16px;
+  height: 16px;
+  flex: 0 0 auto;
+}
+
+.orientation-toggle__text {
+  line-height: 16px;
+}
+
+/* 手机竖屏时模拟横屏,横屏状态和桌面端保持原方向 */
+/* stylelint-disable-next-line order/order -- 基础视口样式需要位于移动端覆盖样式之前 */
+@media screen and (width <= 767px) and (orientation: portrait) {
+  .preview-page {
+    position: fixed;
+    top: 0;
+    left: 0;
+    width: 100vh;
+    width: 100dvh;
+    height: 100vw;
+    height: 100dvw;
+    transform: rotate(90deg) translateY(-100%);
+    transform-origin: left top;
+  }
+
+  .preview-page.preview-page--switched {
+    position: relative;
+    width: 100vw;
+    width: 100dvw;
+    height: 100vh;
+    height: 100dvh;
+    transform: none;
+  }
+}
+</style>

+ 14 - 1
src/views/pms/iotrddailyreport/FillDailyReportForm.vue

@@ -188,6 +188,7 @@ const handleRowValidate = (pid: number, key: string) => {
   const propsToValidate = NON_PROD_FIELDS.map((field) => `${pid}.${field.key}`)
 
   if (key === 'otherNptTime') propsToValidate.push(`${pid}.otherNptReason`)
+  if (key === 'accidentTime') propsToValidate.push('malfunction')
 
   formRef.value.validateField(propsToValidate)
 }
@@ -213,6 +214,10 @@ const opinion = ref('')
 
 const form = ref<Form>(original())
 
+const isMalfunctionRequired = computed(() =>
+  form.value.platformIds.some((pid) => Number(form.value[pid]?.accidentTime) > 0)
+)
+
 function initPlatformData(reportId: number, sourceData: any) {
   form.value[reportId] = {
     rdStatus: sourceData.rdStatus,
@@ -1220,7 +1225,15 @@ const inContent = async (attachment) => {
             :maxlength="1000"
             placeholder="请输入外租设备" />
         </el-form-item>
-        <el-form-item class="col-span-2" label="故障情况" prop="malfunction">
+        <el-form-item
+          class="col-span-2"
+          label="故障情况"
+          prop="malfunction"
+          :rules="{
+            required: isMalfunctionRequired,
+            message: '工程质量时间大于0时,请输入故障情况',
+            trigger: ['blur', 'change']
+          }">
           <el-input
             v-model="form.malfunction"
             type="textarea"

+ 2 - 2
src/views/pms/iotrhdailyreport/rh-form.vue

@@ -387,7 +387,7 @@ const handleAudit = async (auditStatus: 20 | 30) => {
               </div>
               <span
                 class="inline-flex items-center rounded border border-red-200 bg-red-100 px-2 py-0.5 text-xs font-medium text-red-600 dark:bg-red-900/20 dark:border-red-800 dark:text-red-400">
-                >120% 红色预警
+                >100% 红色预警
               </span>
             </div>
             <!-- 可以显示一个总计提示,辅助用户 -->
@@ -432,7 +432,7 @@ const handleAudit = async (auditStatus: 20 | 30) => {
           <el-input
             :model-value="transitTime.value"
             disabled
-            :class="{ 'warning-input': transitTime.original > 1.2 }" />
+            :class="{ 'warning-input': transitTime.original > 1 }" />
         </el-form-item>
         <el-form-item label="施工状态" prop="constructionStatus">
           <el-select

+ 3 - 0
src/views/pms/plan-work/fill.vue

@@ -197,6 +197,9 @@ const handleOpenForm = async (mode: FormMode, id?: number) => {
         planWorkload:
           workloadValue === null || workloadValue === undefined ? undefined : Number(workloadValue)
       }
+    } else if (mode === 'create' && query.value.deptId) {
+      createFormData.value.deptId = query.value.deptId
+      await handleDeptChange(query.value.deptId)
     }
 
     await nextTick()