From 62546bc9376278127809d74a4fb16e48492170fe Mon Sep 17 00:00:00 2001 From: wintsa <770775984@qq.com> Date: Mon, 2 Mar 2026 18:12:32 +0800 Subject: [PATCH] 1 --- src/components/views/zxFw.vue | 76 ++++++++++++++++++++++++++++++++--- src/main.ts | 13 ++---- src/style.css | 12 ++++++ tsconfig.tsbuildinfo | 2 +- vite.config.ts | 26 +++++++++--- 5 files changed, 107 insertions(+), 22 deletions(-) diff --git a/src/components/views/zxFw.vue b/src/components/views/zxFw.vue index 8e2fdbf..a345bb9 100644 --- a/src/components/views/zxFw.vue +++ b/src/components/views/zxFw.vue @@ -162,6 +162,7 @@ function handleSnapHostScroll() { const pickerOpen = ref(false) const pickerTempIds = ref([]) const pickerSearch = ref('') +const pickerListScrollRef = ref(null) const clearConfirmOpen = ref(false) const pendingClearServiceId = ref(null) const dragSelecting = ref(false) @@ -171,6 +172,61 @@ const dragBaseIds = ref([]) const dragStartPoint = ref({ x: 0, y: 0 }) const dragCurrentPoint = ref({ x: 0, y: 0 }) const pickerItemElMap = new Map() +let dragTouchedIds = new Set() +let dragAutoScrollRafId: number | null = null + +const DRAG_AUTO_SCROLL_EDGE = 36 +const DRAG_AUTO_SCROLL_MAX_STEP = 16 + +const stopDragAutoScroll = () => { + if (dragAutoScrollRafId !== null) { + cancelAnimationFrame(dragAutoScrollRafId) + dragAutoScrollRafId = null + } +} + +const resolveDragAutoScrollStep = () => { + const scroller = pickerListScrollRef.value + if (!scroller) return 0 + const rect = scroller.getBoundingClientRect() + const pointerY = dragCurrentPoint.value.y + let step = 0 + + if (pointerY > rect.bottom - DRAG_AUTO_SCROLL_EDGE) { + const ratio = Math.min(2, (pointerY - (rect.bottom - DRAG_AUTO_SCROLL_EDGE)) / DRAG_AUTO_SCROLL_EDGE) + step = Math.max(2, Math.round(ratio * DRAG_AUTO_SCROLL_MAX_STEP)) + } else if (pointerY < rect.top + DRAG_AUTO_SCROLL_EDGE) { + const ratio = Math.min(2, ((rect.top + DRAG_AUTO_SCROLL_EDGE) - pointerY) / DRAG_AUTO_SCROLL_EDGE) + step = -Math.max(2, Math.round(ratio * DRAG_AUTO_SCROLL_MAX_STEP)) + } + return step +} + +const runDragAutoScroll = () => { + if (!dragSelecting.value) { + stopDragAutoScroll() + return + } + + const scroller = pickerListScrollRef.value + if (scroller) { + const step = resolveDragAutoScrollStep() + if (step !== 0) { + const prev = scroller.scrollTop + scroller.scrollTop += step + if (scroller.scrollTop !== prev) { + applyDragSelectionByRect() + } + } + } + + dragAutoScrollRafId = requestAnimationFrame(runDragAutoScroll) +} + +const startDragAutoScroll = () => { + if (dragAutoScrollRafId !== null) return + dragAutoScrollRafId = requestAnimationFrame(runDragAutoScroll) +} const selectedServiceText = computed(() => { if (selectedIds.value.length === 0) return '' @@ -642,11 +698,15 @@ const applyDragSelectionByRect = () => { const itemRect = el.getBoundingClientRect() const hit = isRectIntersect(rect, itemRect) if (hit) { - if (dragSelectChecked) { - nextSelectedSet.add(code) - } else { - nextSelectedSet.delete(code) - } + dragTouchedIds.add(code) + } + } + + for (const code of dragTouchedIds) { + if (dragSelectChecked) { + nextSelectedSet.add(code) + } else { + nextSelectedSet.delete(code) } } @@ -659,6 +719,8 @@ const stopDragSelect = () => { dragSelecting.value = false dragMoved.value = false dragBaseIds.value = [] + dragTouchedIds.clear() + stopDragAutoScroll() window.removeEventListener('mousemove', onDragSelectingMove) window.removeEventListener('mouseup', stopDragSelect) } @@ -679,10 +741,12 @@ const startDragSelect = (event: MouseEvent, code: string) => { dragSelecting.value = true dragMoved.value = false dragBaseIds.value = [...pickerTempIds.value] + dragTouchedIds = new Set([code]) dragSelectChecked = !pickerTempIds.value.includes(code) dragStartPoint.value = { x: event.clientX, y: event.clientY } dragCurrentPoint.value = { x: event.clientX, y: event.clientY } applyTempChecked(code, dragSelectChecked) + startDragAutoScroll() window.addEventListener('mousemove', onDragSelectingMove) window.addEventListener('mouseup', stopDragSelect) } @@ -821,7 +885,7 @@ onBeforeUnmount(() => { -
+
diff --git a/src/main.ts b/src/main.ts index fa58441..2bc46f2 100644 --- a/src/main.ts +++ b/src/main.ts @@ -2,7 +2,6 @@ import { CellStyleModule, ClientSideRowModelModule, ColumnAutoSizeModule, - CsvExportModule, LargeTextEditorModule, LocaleModule, ModuleRegistry, @@ -12,17 +11,15 @@ import { TextEditorModule, TooltipModule, UndoRedoEditModule, - ValidationModule + } from 'ag-grid-community' import { AggregationModule, CellSelectionModule, ClipboardModule, - ContextMenuModule, LicenseManager, - MenuModule, RowGroupingModule, - TreeDataModule + TreeDataModule,ContextMenuModule } from 'ag-grid-enterprise' import { createPinia } from 'pinia' import piniaPluginPersistedstate from 'pinia-plugin-persistedstate' @@ -37,10 +34,9 @@ LicenseManager.setLicenseKey( const AG_GRID_MODULES = [ ClientSideRowModelModule, ColumnAutoSizeModule, - CsvExportModule, TextEditorModule, NumberEditorModule, - RowAutoHeightModule, + RowAutoHeightModule,ContextMenuModule, LargeTextEditorModule, UndoRedoEditModule, CellStyleModule, @@ -49,12 +45,9 @@ const AG_GRID_MODULES = [ TreeDataModule, AggregationModule, RowGroupingModule, - MenuModule, CellSelectionModule, - ContextMenuModule, ClipboardModule, LocaleModule, - ValidationModule ] const pinia = createPinia() diff --git a/src/style.css b/src/style.css index 6d5e380..8384cbc 100644 --- a/src/style.css +++ b/src/style.css @@ -5,6 +5,10 @@ height: 100dvh; } +html { + font-size: 16px; +} + @custom-variant dark (&:is(.dark *)); @theme inline { @@ -126,6 +130,7 @@ } body { @apply bg-background text-foreground; + margin: 0; } input, textarea, @@ -234,3 +239,10 @@ height:100%; font-style: italic; } + +/* Laptop screens often need a slightly denser desktop layout. */ +@media (max-width: 1536px) { + html { + font-size: 14.4px; + } +} diff --git a/tsconfig.tsbuildinfo b/tsconfig.tsbuildinfo index 677c7d7..7bc323f 100644 --- a/tsconfig.tsbuildinfo +++ b/tsconfig.tsbuildinfo @@ -1 +1 @@ -{"root":["./src/main.ts","./src/sql.ts","./src/components/ui/button/index.ts","./src/components/ui/card/index.ts","./src/components/ui/scroll-area/index.ts","./src/components/ui/tooltip/index.ts","./src/lib/decimal.ts","./src/lib/diyaggridoptions.ts","./src/lib/numberformat.ts","./src/lib/pricingmethodtotals.ts","./src/lib/utils.ts","./src/lib/xmfactordefaults.ts","./src/lib/zwarchive.ts","./src/lib/zxfwpricingsync.ts","./src/pinia/pricingpanereload.ts","./src/pinia/tab.ts","./src/app.vue","./src/components/ui/button/button.vue","./src/components/ui/card/card.vue","./src/components/ui/card/cardaction.vue","./src/components/ui/card/cardcontent.vue","./src/components/ui/card/carddescription.vue","./src/components/ui/card/cardfooter.vue","./src/components/ui/card/cardheader.vue","./src/components/ui/card/cardtitle.vue","./src/components/ui/scroll-area/scrollarea.vue","./src/components/ui/scroll-area/scrollbar.vue","./src/components/ui/tooltip/tooltipcontent.vue","./src/components/views/contractdetailview.vue","./src/components/views/ht.vue","./src/components/views/xm.vue","./src/components/views/xmconsultcategoryfactor.vue","./src/components/views/xmfactorgrid.vue","./src/components/views/xmmajorfactor.vue","./src/components/views/zxfwview.vue","./src/components/views/htinfo.vue","./src/components/views/xminfo.vue","./src/components/views/zxfw.vue","./src/components/views/pricingview/hourlypricingpane.vue","./src/components/views/pricingview/investmentscalepricingpane.vue","./src/components/views/pricingview/landscalepricingpane.vue","./src/components/views/pricingview/workloadpricingpane.vue","./src/layout/tab.vue","./src/layout/typeline.vue"],"version":"5.9.3"} \ No newline at end of file +{"root":["./src/main.ts","./src/sql.ts","./src/components/ui/button/index.ts","./src/components/ui/card/index.ts","./src/components/ui/scroll-area/index.ts","./src/components/ui/tooltip/index.ts","./src/lib/decimal.ts","./src/lib/diyaggridoptions.ts","./src/lib/number.ts","./src/lib/numberformat.ts","./src/lib/pricingmethodtotals.ts","./src/lib/pricingscalefee.ts","./src/lib/utils.ts","./src/lib/xmfactordefaults.ts","./src/lib/zwarchive.ts","./src/lib/zxfwpricingsync.ts","./src/pinia/pricingpanereload.ts","./src/pinia/tab.ts","./src/app.vue","./src/components/ui/button/button.vue","./src/components/ui/card/card.vue","./src/components/ui/card/cardaction.vue","./src/components/ui/card/cardcontent.vue","./src/components/ui/card/carddescription.vue","./src/components/ui/card/cardfooter.vue","./src/components/ui/card/cardheader.vue","./src/components/ui/card/cardtitle.vue","./src/components/ui/scroll-area/scrollarea.vue","./src/components/ui/scroll-area/scrollbar.vue","./src/components/ui/tooltip/tooltipcontent.vue","./src/components/views/contractdetailview.vue","./src/components/views/ht.vue","./src/components/views/xm.vue","./src/components/views/xmconsultcategoryfactor.vue","./src/components/views/xmfactorgrid.vue","./src/components/views/xmmajorfactor.vue","./src/components/views/zxfwview.vue","./src/components/views/htinfo.vue","./src/components/views/xminfo.vue","./src/components/views/zxfw.vue","./src/components/views/pricingview/hourlypricingpane.vue","./src/components/views/pricingview/investmentscalepricingpane.vue","./src/components/views/pricingview/landscalepricingpane.vue","./src/components/views/pricingview/workloadpricingpane.vue","./src/layout/tab.vue","./src/layout/typeline.vue"],"version":"5.9.3"} \ No newline at end of file diff --git a/vite.config.ts b/vite.config.ts index 069a877..3b2e37b 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -29,11 +29,27 @@ export default defineConfig({ entryFileNames: 'static/js/[name]-[hash].js', // 自定义 css 文件名 assetFileNames: 'static/[ext]/[name]-[hash].[ext]', - // 拆分第三方依赖(如 vue、axios 单独打包) - manualChunks(id) { - if (id.includes('node_modules')) { - return id.toString().split('node_modules/')[1].split('/')[0].toString(); - } + codeSplitting: { + minSize: 20 * 1024, + groups: [ + { + name: 'vendor-ag-grid', + test: /node_modules[\\/](ag-grid-community|ag-grid-enterprise|ag-grid-vue3|@ag-grid-community)[\\/]/, + priority: 30, + entriesAware: true, + maxSize: 450 * 1024 + }, + { + name: 'vendor-vue', + test: /node_modules[\\/](vue|pinia|@vue|pinia-plugin-persistedstate)[\\/]/, + priority: 20 + }, + { + name: 'vendor-ui', + test: /node_modules[\\/](reka-ui|@floating-ui|lucide-vue-next|@iconify[\\/]vue)[\\/]/, + priority: 10 + } + ] } } },