This commit is contained in:
wintsa 2026-03-02 18:12:32 +08:00
parent a10359f7e0
commit 62546bc937
5 changed files with 107 additions and 22 deletions

View File

@ -162,6 +162,7 @@ function handleSnapHostScroll() {
const pickerOpen = ref(false) const pickerOpen = ref(false)
const pickerTempIds = ref<string[]>([]) const pickerTempIds = ref<string[]>([])
const pickerSearch = ref('') const pickerSearch = ref('')
const pickerListScrollRef = ref<HTMLElement | null>(null)
const clearConfirmOpen = ref(false) const clearConfirmOpen = ref(false)
const pendingClearServiceId = ref<string | null>(null) const pendingClearServiceId = ref<string | null>(null)
const dragSelecting = ref(false) const dragSelecting = ref(false)
@ -171,6 +172,61 @@ const dragBaseIds = ref<string[]>([])
const dragStartPoint = ref({ x: 0, y: 0 }) const dragStartPoint = ref({ x: 0, y: 0 })
const dragCurrentPoint = ref({ x: 0, y: 0 }) const dragCurrentPoint = ref({ x: 0, y: 0 })
const pickerItemElMap = new Map<string, HTMLElement>() const pickerItemElMap = new Map<string, HTMLElement>()
let dragTouchedIds = new Set<string>()
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(() => { const selectedServiceText = computed(() => {
if (selectedIds.value.length === 0) return '' if (selectedIds.value.length === 0) return ''
@ -642,11 +698,15 @@ const applyDragSelectionByRect = () => {
const itemRect = el.getBoundingClientRect() const itemRect = el.getBoundingClientRect()
const hit = isRectIntersect(rect, itemRect) const hit = isRectIntersect(rect, itemRect)
if (hit) { if (hit) {
if (dragSelectChecked) { dragTouchedIds.add(code)
nextSelectedSet.add(code) }
} else { }
nextSelectedSet.delete(code)
} for (const code of dragTouchedIds) {
if (dragSelectChecked) {
nextSelectedSet.add(code)
} else {
nextSelectedSet.delete(code)
} }
} }
@ -659,6 +719,8 @@ const stopDragSelect = () => {
dragSelecting.value = false dragSelecting.value = false
dragMoved.value = false dragMoved.value = false
dragBaseIds.value = [] dragBaseIds.value = []
dragTouchedIds.clear()
stopDragAutoScroll()
window.removeEventListener('mousemove', onDragSelectingMove) window.removeEventListener('mousemove', onDragSelectingMove)
window.removeEventListener('mouseup', stopDragSelect) window.removeEventListener('mouseup', stopDragSelect)
} }
@ -679,10 +741,12 @@ const startDragSelect = (event: MouseEvent, code: string) => {
dragSelecting.value = true dragSelecting.value = true
dragMoved.value = false dragMoved.value = false
dragBaseIds.value = [...pickerTempIds.value] dragBaseIds.value = [...pickerTempIds.value]
dragTouchedIds = new Set([code])
dragSelectChecked = !pickerTempIds.value.includes(code) dragSelectChecked = !pickerTempIds.value.includes(code)
dragStartPoint.value = { x: event.clientX, y: event.clientY } dragStartPoint.value = { x: event.clientX, y: event.clientY }
dragCurrentPoint.value = { x: event.clientX, y: event.clientY } dragCurrentPoint.value = { x: event.clientX, y: event.clientY }
applyTempChecked(code, dragSelectChecked) applyTempChecked(code, dragSelectChecked)
startDragAutoScroll()
window.addEventListener('mousemove', onDragSelectingMove) window.addEventListener('mousemove', onDragSelectingMove)
window.addEventListener('mouseup', stopDragSelect) window.addEventListener('mouseup', stopDragSelect)
} }
@ -821,7 +885,7 @@ onBeforeUnmount(() => {
</DialogClose> </DialogClose>
</div> </div>
<div class="max-h-[420px] overflow-auto px-5 py-4"> <div ref="pickerListScrollRef" class="max-h-[420px] overflow-auto px-5 py-4">
<div class="mb-3"> <div class="mb-3">
<input v-model="pickerSearch" type="text" placeholder="输入编码或名称过滤" <input v-model="pickerSearch" type="text" placeholder="输入编码或名称过滤"
class="h-9 w-full rounded-md border bg-background px-3 text-sm outline-none focus-visible:ring-2 focus-visible:ring-ring" /> class="h-9 w-full rounded-md border bg-background px-3 text-sm outline-none focus-visible:ring-2 focus-visible:ring-ring" />

View File

@ -2,7 +2,6 @@ import {
CellStyleModule, CellStyleModule,
ClientSideRowModelModule, ClientSideRowModelModule,
ColumnAutoSizeModule, ColumnAutoSizeModule,
CsvExportModule,
LargeTextEditorModule, LargeTextEditorModule,
LocaleModule, LocaleModule,
ModuleRegistry, ModuleRegistry,
@ -12,17 +11,15 @@ import {
TextEditorModule, TextEditorModule,
TooltipModule, TooltipModule,
UndoRedoEditModule, UndoRedoEditModule,
ValidationModule
} from 'ag-grid-community' } from 'ag-grid-community'
import { import {
AggregationModule, AggregationModule,
CellSelectionModule, CellSelectionModule,
ClipboardModule, ClipboardModule,
ContextMenuModule,
LicenseManager, LicenseManager,
MenuModule,
RowGroupingModule, RowGroupingModule,
TreeDataModule TreeDataModule,ContextMenuModule
} from 'ag-grid-enterprise' } from 'ag-grid-enterprise'
import { createPinia } from 'pinia' import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate' import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
@ -37,10 +34,9 @@ LicenseManager.setLicenseKey(
const AG_GRID_MODULES = [ const AG_GRID_MODULES = [
ClientSideRowModelModule, ClientSideRowModelModule,
ColumnAutoSizeModule, ColumnAutoSizeModule,
CsvExportModule,
TextEditorModule, TextEditorModule,
NumberEditorModule, NumberEditorModule,
RowAutoHeightModule, RowAutoHeightModule,ContextMenuModule,
LargeTextEditorModule, LargeTextEditorModule,
UndoRedoEditModule, UndoRedoEditModule,
CellStyleModule, CellStyleModule,
@ -49,12 +45,9 @@ const AG_GRID_MODULES = [
TreeDataModule, TreeDataModule,
AggregationModule, AggregationModule,
RowGroupingModule, RowGroupingModule,
MenuModule,
CellSelectionModule, CellSelectionModule,
ContextMenuModule,
ClipboardModule, ClipboardModule,
LocaleModule, LocaleModule,
ValidationModule
] ]
const pinia = createPinia() const pinia = createPinia()

View File

@ -5,6 +5,10 @@
height: 100dvh; height: 100dvh;
} }
html {
font-size: 16px;
}
@custom-variant dark (&:is(.dark *)); @custom-variant dark (&:is(.dark *));
@theme inline { @theme inline {
@ -126,6 +130,7 @@
} }
body { body {
@apply bg-background text-foreground; @apply bg-background text-foreground;
margin: 0;
} }
input, input,
textarea, textarea,
@ -234,3 +239,10 @@
height:100%; height:100%;
font-style: italic; font-style: italic;
} }
/* Laptop screens often need a slightly denser desktop layout. */
@media (max-width: 1536px) {
html {
font-size: 14.4px;
}
}

View File

@ -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"} {"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"}

View File

@ -29,11 +29,27 @@ export default defineConfig({
entryFileNames: 'static/js/[name]-[hash].js', entryFileNames: 'static/js/[name]-[hash].js',
// 自定义 css 文件名 // 自定义 css 文件名
assetFileNames: 'static/[ext]/[name]-[hash].[ext]', assetFileNames: 'static/[ext]/[name]-[hash].[ext]',
// 拆分第三方依赖(如 vue、axios 单独打包) codeSplitting: {
manualChunks(id) { minSize: 20 * 1024,
if (id.includes('node_modules')) { groups: [
return id.toString().split('node_modules/')[1].split('/')[0].toString(); {
} 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
}
]
} }
} }
}, },