Browse Source

feat: 添加参考线实现

jiaxing.liao 2 weeks ago
parent
commit
75f54c01e9

+ 6 - 4
src/renderer/src/types/page.d.ts

@@ -4,11 +4,13 @@ export type ReferenceLine = {
   // ID
   id: string
   // 垂直 水平
-  layout: 'horizontal' | 'vertical'
+  type: 'horizontal' | 'vertical' | null
   // 位置
-  position: number
-  // 显示
-  visible: boolean
+  value: number
+  // x
+  x: number
+  // y
+  y: number
 }
 
 export type Page = {

+ 6 - 0
src/renderer/src/views/designer/sidebar/Method.vue

@@ -12,6 +12,12 @@
         :data="item"
         @delete="handleDelete(item.id)"
       />
+      <div
+        v-if="!projectStore.project?.methods?.length"
+        class="text-center text-text-secondary mt-4"
+      >
+        暂无数据~
+      </div>
     </el-scrollbar>
   </div>
 </template>

+ 1 - 1
src/renderer/src/views/designer/sidebar/Resource.vue

@@ -151,7 +151,7 @@ const handleAddFont = async () => {
     ]
   })
 
-  paths.forEach(async (path) => {
+  paths?.forEach(async (path) => {
     const fileName = path.split('\\').pop()
     // 复制文件
     await window.electron.ipcRenderer.invoke(

+ 4 - 0
src/renderer/src/views/designer/sidebar/Schema.vue

@@ -25,6 +25,10 @@ watch(
   () => projectStore.project,
   () => {
     editorRef.value?.setValue(JSON.stringify(projectStore.project, null, 2))
+  },
+  {
+    deep: true,
+    immediate: true
   }
 )
 </script>

+ 10 - 5
src/renderer/src/views/designer/sidebar/components/MethodItem.vue

@@ -14,13 +14,15 @@
           size="small"
           v-if="edit"
           v-model.trim="title"
+          ref="inputRef"
           @keydown.enter="handleChangeName"
           @blur="handleChangeName"
+          @click.stop
         />
         <span v-else>{{ data.name }}</span>
       </span>
       <span class="flex gap-4px items-center">
-        <LuPencilLine size="12px" @click.capture.stop="handleEdit" />
+        <LuPencilLine size="12px" @click.stop="handleEdit" />
         <el-popconfirm
           :append-to="contentRef"
           class="box-item"
@@ -28,14 +30,14 @@
           @confirm="$emit('delete')"
         >
           <template #reference>
-            <span class="cursor-pointer" @click.capture.stop><LuTrash2 size="14px" /></span>
+            <span class="cursor-pointer" @click.stop><LuTrash2 size="14px" /></span>
           </template>
         </el-popconfirm>
       </span>
     </div>
     <div class="my-12px text-12px px-12px text-text-secondary" v-if="expand">代码内容:</div>
     <div class="border" v-if="expand">
-      <MonacoEditor v-model="data.action" :allow-fullscreen="false" language="C" bordered />
+      <MonacoEditor v-model="data.action" :allow-fullscreen="false" language="c" bordered />
     </div>
   </div>
 </template>
@@ -43,7 +45,7 @@
 <script setup lang="ts">
 import type { Method } from '@/types/method'
 
-import { ref, defineProps, defineEmits } from 'vue'
+import { ref, defineProps, defineEmits, nextTick } from 'vue'
 import MonacoEditor from '@/components/MonacoEditor/index.vue'
 import { LuBox, LuPencilLine, LuTrash2 } from 'vue-icons-plus/lu'
 import { AiOutlineRight, AiOutlineDown } from 'vue-icons-plus/ai'
@@ -57,10 +59,13 @@ const expand = ref(false)
 const edit = ref(false)
 const title = ref('')
 const contentRef = ref<HTMLDivElement>()
+const inputRef = ref<HTMLInputElement>()
 
-const handleEdit = () => {
+const handleEdit = async () => {
   title.value = props.data.name
   edit.value = true
+  await nextTick()
+  inputRef.value?.focus()
 }
 const handleChangeName = () => {
   if (title.value) {

+ 1 - 1
src/renderer/src/views/designer/workspace/index.vue

@@ -10,7 +10,7 @@
         v-if="projectStore.project?.meta.screenType === 'dual'"
       />
       <SplitterPanel v-if="projectStore.project?.meta.screenType === 'dual'">
-        <Stage key="2" :data="projectStore.project?.screens[0]" />
+        <Stage key="2" :data="projectStore.project?.screens[1]" />
       </SplitterPanel>
     </SplitterGroup>
   </div>

+ 66 - 21
src/renderer/src/views/designer/workspace/stage/Scaleplate.vue

@@ -1,8 +1,9 @@
 <template>
   <div class="scaleplate">
     <!-- 显示/隐藏参考线 -->
-    <div class="refer-line-img">
-      <LuEye :size="16" />
+    <div class="refer-line-img" @click="state.showReferenceLine = !state.showReferenceLine">
+      <LuEye :size="16" v-if="state.showReferenceLine" />
+      <LuEyeOff :size="16" v-else />
     </div>
     <!-- 标尺 -->
     <div
@@ -33,19 +34,20 @@
     </div>
 
     <!-- 参考线 -->
-    <!-- <div
+    <div
       class="refer-line"
-      v-for="item in []"
-      :key="item.key"
+      v-show="state.showReferenceLine"
+      v-for="item in getReferLines || []"
+      :key="item.id"
       :style="{ left: item.x + 'px', top: item.y + 'px' }"
       :class="item.type === 'horizontal' ? 'refer-line-h' : 'refer-line-v'"
-      @dblclick=""
+      @dblclick="handleRemoveReferLine(item.id)"
     >
-      <UseDraggable @move="(position, event) => handleDragReferLine(position, event, item.key)">
+      <UseDraggable @move="(position, event) => handleDragReferLine(position, event, item.id)">
         <span class="refer-line__txt">{{ item.value }}px</span>
         <span class="refer-line__line"></span>
       </UseDraggable>
-    </div> -->
+    </div>
     <!-- 临时参考线 -->
     <div
       class="refer-line virtual-refer-line"
@@ -65,16 +67,18 @@
 <script setup lang="ts">
 import type { Ref } from 'vue'
 import type { StageState } from './type'
-import type { ReferenceLine } from '@/types/page'
+import type { Page, ReferenceLine } from '@/types/page'
 
-import { onMounted, ref, onBeforeUnmount, nextTick, watch, defineProps } from 'vue'
+import { onMounted, ref, onBeforeUnmount, nextTick, watch, defineProps, computed } from 'vue'
 import { LuEye, LuEyeOff } from 'vue-icons-plus/lu'
 import { UseDraggable } from '@vueuse/components'
 import { drawScaleplate } from './utils'
 import { useElementSize, useResizeObserver } from '@vueuse/core'
+import { v4 } from 'uuid'
 
 const props = defineProps<{
   state: StageState
+  page?: Page
 }>()
 
 const horizontalRef = ref<HTMLElement | null>(null)
@@ -82,8 +86,39 @@ const verticalRef = ref<HTMLElement | null>(null)
 const scaleplateHorizontalRef = ref<HTMLCanvasElement | null>(null)
 const scaleplateVerticalRef = ref<HTMLCanvasElement | null>(null)
 
-const { width: horizontalWidth, height: horizontalHeight } = useElementSize(horizontalRef)
-const { width: verticalWidth, height: verticalHeight } = useElementSize(verticalRef)
+const { width: horizontalWidth, height: _horizontalHeight } = useElementSize(horizontalRef)
+const { width: _verticalWidth, height: verticalHeight } = useElementSize(verticalRef)
+
+// 根据滚动和缩放,重新计算辅助线位置
+const getReferLines = computed(() => {
+  const state = props.state
+  const { scale, scrollX, scrollY, originX, originY } = state
+
+  return props.page?.referenceLine
+    ?.map((line) => {
+      let x = line.x || 0
+      let y = line.y || 0
+      if (line.type === 'horizontal') {
+        x = originX + line.value * scale - scrollX + 20
+      } else {
+        y = originY + line.value * scale - scrollY + 20
+      }
+
+      return {
+        ...line,
+        x,
+        y
+      }
+    })
+    .filter((line) => {
+      // 过滤掉不在视口内的辅助线
+      if (line.type === 'horizontal') {
+        return line.x > 20 && line.x < state.viewportWidth
+      } else {
+        return line.y > 20 && line.y < state.viewportHeight
+      }
+    })
+})
 
 /* 绘制标尺刻度 */
 const handleDrawHScaleplate = async () => {
@@ -120,8 +155,8 @@ const handleDrawVScaleplate = async () => {
 }
 
 /* =============================== 参考线 ================================= */
-const virtualReferLine: Ref<ReferLine> = ref({
-  key: '0',
+const virtualReferLine: Ref<ReferenceLine> = ref({
+  id: '0',
   x: 0,
   y: 0,
   value: 0,
@@ -147,11 +182,23 @@ const handleMouseMoveHScaleplate = (e: MouseEvent, type: 'horizontal' | 'vertica
 const handleAddReferLine = () => {
   if (!virtualReferLine.value.type) return
 
-  // todo
+  props.page?.referenceLine.push({
+    ...virtualReferLine.value,
+    id: v4()
+  })
 }
+
+/* 删除参考线 */
+const handleRemoveReferLine = (id: string) => {
+  props.page?.referenceLine.splice(
+    props.page?.referenceLine.findIndex((item) => item.id === id),
+    1
+  )
+}
+
 /* 拖拽参考线 */
-const handleDragReferLine = ({ x, y }: { x: number; y: number }, _: PointerEvent, key: string) => {
-  const referLine: ReferenceLine | undefined = undefined // todo
+const handleDragReferLine = ({ x, y }: { x: number; y: number }, _: PointerEvent, id: string) => {
+  const referLine = props.page?.referenceLine.find((item) => item.id === id)
   if (!referLine) return
 
   const { scale, originX, originY, scrollX, scrollY } = props.state
@@ -170,8 +217,6 @@ const handleDragReferLine = ({ x, y }: { x: number; y: number }, _: PointerEvent
     referLine.value = Math.round((lineY + offsetY - 20) / scale)
     referLine.y = lineY
   }
-
-  // todo 更新参考线
 }
 /* ===============================参考线结束================================= */
 
@@ -235,7 +280,7 @@ useResizeObserver(verticalRef, handleDrawVScaleplate)
   color: blur;
   &-h {
     width: 5px;
-    height: 100%;
+    height: calc(100% - 16px);
     .refer-line__line {
       border-left: solid 1px red;
       cursor: e-resize;
@@ -245,7 +290,7 @@ useResizeObserver(verticalRef, handleDrawVScaleplate)
     }
   }
   &-v {
-    width: 100%;
+    width: calc(100% - 16px);
     height: 5px;
     .refer-line__line {
       border-top: solid 1px red;

+ 20 - 4
src/renderer/src/views/designer/workspace/stage/index.vue

@@ -12,9 +12,14 @@
       </div>
       <div class="workspace-top">
         <!-- 画布 -->
-        <DesignerCanvas :state="state" ref="canvasRef" @changeState="handleSetState" />
+        <DesignerCanvas
+          :state="state"
+          :page="currentScreenActivePage"
+          ref="canvasRef"
+          @changeState="handleSetState"
+        />
         <!-- 标尺 -->
-        <Scaleplate :state="state" v-show="state.showRuler" />
+        <Scaleplate :state="state" :page="currentScreenActivePage" v-show="state.showRuler" />
       </div>
       <!-- 底部工具栏 -->
       <div
@@ -81,7 +86,7 @@
 import type { StageState } from './type'
 import type { Screen } from '@/types/screen'
 
-import { ref, reactive, defineProps, watch, nextTick } from 'vue'
+import { ref, reactive, defineProps, watch, nextTick, computed } from 'vue'
 import Scaleplate from './Scaleplate.vue'
 import DesignerCanvas from './DesignerCanvas.vue'
 import { throttle } from 'lodash'
@@ -99,6 +104,7 @@ import { useAppStore } from '@/store/modules/app'
 const props = defineProps<{
   data?: Screen
 }>()
+
 const projectStore = useProjectStore()
 const appStore = useAppStore()
 const canvasRef = ref<InstanceType<typeof DesignerCanvas>>()
@@ -118,7 +124,14 @@ const state = reactive<StageState>({
   wrapperHeight: 0,
   showBorder: false,
   showRuler: true,
-  showBgGrid: false
+  showBgGrid: false,
+  showReferenceLine: false
+})
+// 当前活动页面ID
+const currentPageId = ref<string>()
+// 当前屏幕活动页面
+const currentScreenActivePage = computed(() => {
+  return props.data?.pages.find((item) => item.id === currentPageId.value)
 })
 
 watch(
@@ -130,6 +143,9 @@ watch(
       await nextTick()
       handleCenter()
     }
+    if (!currentPageId.value && val?.pages.length) {
+      currentPageId.value = val.pages[0].id
+    }
   }
 )
 

+ 2 - 0
src/renderer/src/views/designer/workspace/stage/type.d.ts

@@ -31,4 +31,6 @@ export type StageState = {
   showRuler: boolean
   // 背景网格
   showBgGrid: boolean
+  // 显示参考线
+  showReferenceLine: boolean
 }