fix
This commit is contained in:
1 parent
75f293f877
commit
d8f8b629d2
8 files changed
+350
-956
No files matched your search
@@ -1,13 +1,14 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onActivated, onBeforeUnmount, onMounted, ref } from 'vue'
|
||||
import { computed, onActivated, onBeforeUnmount, onMounted, ref, watch } from 'vue'
|
||||
import { AgGridVue } from 'ag-grid-vue3'
|
||||
import type { CellValueChangedEvent, ColDef } from 'ag-grid-community'
|
||||
import type { CellValueChangedEvent, ColDef, GridApi, GridReadyEvent } from 'ag-grid-community'
|
||||
import { AG_GRID_LOCALE_CN } from '@ag-grid-community/locale'
|
||||
import { myTheme, gridOptions } from '@/lib/diyAgGridOptions'
|
||||
import localforage from 'localforage'
|
||||
import { decimalAggSum, roundTo, sumByNumber } from '@/lib/decimal'
|
||||
import { formatThousands } from '@/lib/numberFormat'
|
||||
import { industryTypeList } from '@/sql'
|
||||
import { SwitchRoot, SwitchThumb } from 'reka-ui'
|
||||
|
||||
interface DetailRow {
|
||||
id: string
|
||||
@@ -20,12 +21,19 @@ interface DetailRow {
|
||||
amount: number | null
|
||||
landArea: number | null
|
||||
path: string[]
|
||||
hide?: boolean
|
||||
}
|
||||
|
||||
interface XmBaseInfoState {
|
||||
projectIndustry?: string
|
||||
}
|
||||
|
||||
interface GridPersistState {
|
||||
detailRows?: DetailRow[]
|
||||
roughCalcEnabled?: boolean
|
||||
totalAmount?: number | null
|
||||
}
|
||||
|
||||
const props = defineProps<{
|
||||
title: string
|
||||
rowData: DetailRow[]
|
||||
@@ -34,6 +42,7 @@ const props = defineProps<{
|
||||
|
||||
const BASE_INFO_KEY = 'xm-base-info-v1'
|
||||
let persistTimer: ReturnType<typeof setTimeout> | null = null
|
||||
const gridApi = ref<GridApi<DetailRow> | null>(null)
|
||||
const activeIndustryId = ref('')
|
||||
const industryNameMap = new Map(
|
||||
industryTypeList.flatMap(item => [
|
||||
@@ -45,6 +54,18 @@ const totalLabel = computed(() => {
|
||||
const industryName = industryNameMap.get(activeIndustryId.value.trim()) || ''
|
||||
return industryName ? `${industryName}总投资` : '总投资'
|
||||
})
|
||||
const roughCalcEnabled = ref(false)
|
||||
const visibleRowData = computed(() => props.rowData.filter(row => !row.hide))
|
||||
|
||||
const refreshPinnedTotalLabelCell = () => {
|
||||
if (!gridApi.value) return
|
||||
const pinnedTopNode = gridApi.value.getPinnedTopRow(0)
|
||||
if (!pinnedTopNode) return
|
||||
gridApi.value.refreshCells({
|
||||
rowNodes: [pinnedTopNode],
|
||||
force: true
|
||||
})
|
||||
}
|
||||
|
||||
const columnDefs: ColDef<DetailRow>[] = [
|
||||
{
|
||||
@@ -53,14 +74,21 @@ const columnDefs: ColDef<DetailRow>[] = [
|
||||
headerClass: 'ag-right-aligned-header',
|
||||
minWidth: 100,
|
||||
flex: 1,
|
||||
editable: params => !params.node?.group && !params.node?.rowPinned && Boolean(params.data?.hasCost),
|
||||
editable: params => {
|
||||
if (roughCalcEnabled.value) return Boolean(params.node?.rowPinned)
|
||||
return !params.node?.group && !params.node?.rowPinned && Boolean(params.data?.hasCost)
|
||||
},
|
||||
cellClass: params =>
|
||||
!params.node?.group && !params.node?.rowPinned && params.data?.hasCost
|
||||
roughCalcEnabled.value && params.node?.rowPinned
|
||||
? 'ag-right-aligned-cell editable-cell-line'
|
||||
: !roughCalcEnabled.value && !params.node?.group && !params.node?.rowPinned && params.data?.hasCost
|
||||
? 'ag-right-aligned-cell editable-cell-line'
|
||||
: 'ag-right-aligned-cell',
|
||||
cellClassRules: {
|
||||
'editable-cell-empty': params =>
|
||||
!params.node?.group && !params.node?.rowPinned && Boolean(params.data?.hasCost) && (params.value == null || params.value === '')
|
||||
roughCalcEnabled.value && params.node?.rowPinned
|
||||
? params.value == null || params.value === ''
|
||||
: !params.node?.group && !params.node?.rowPinned && Boolean(params.data?.hasCost) && (params.value == null || params.value === '')
|
||||
},
|
||||
aggFunc: decimalAggSum,
|
||||
valueParser: params => {
|
||||
@@ -69,6 +97,11 @@ const columnDefs: ColDef<DetailRow>[] = [
|
||||
return Number.isFinite(v) ? roundTo(v, 2) : null
|
||||
},
|
||||
valueFormatter: params => {
|
||||
if (roughCalcEnabled.value) {
|
||||
if (!params.node?.rowPinned) return ''
|
||||
if (params.value == null || params.value === '') return '点击输入'
|
||||
return formatThousands(params.value)
|
||||
}
|
||||
if (!params.node?.group && !params.node?.rowPinned && !params.data?.hasCost) {
|
||||
return ''
|
||||
}
|
||||
@@ -85,7 +118,7 @@ const columnDefs: ColDef<DetailRow>[] = [
|
||||
headerClass: 'ag-right-aligned-header',
|
||||
minWidth: 100,
|
||||
flex: 1,
|
||||
editable: params => !params.node?.group && !params.node?.rowPinned && Boolean(params.data?.hasArea),
|
||||
editable: params => !roughCalcEnabled.value && !params.node?.group && !params.node?.rowPinned && Boolean(params.data?.hasArea),
|
||||
cellClass: params =>
|
||||
!params.node?.group && !params.node?.rowPinned && params.data?.hasArea
|
||||
? 'ag-right-aligned-cell editable-cell-line'
|
||||
@@ -100,6 +133,9 @@ const columnDefs: ColDef<DetailRow>[] = [
|
||||
return Number.isFinite(v) ? roundTo(v, 3) : null
|
||||
},
|
||||
valueFormatter: params => {
|
||||
if (roughCalcEnabled.value) {
|
||||
return ''
|
||||
}
|
||||
if (!params.node?.group && !params.node?.rowPinned && !params.data?.hasArea) {
|
||||
return ''
|
||||
}
|
||||
@@ -129,18 +165,16 @@ const autoGroupColumnDef: ColDef = {
|
||||
}
|
||||
}
|
||||
|
||||
const totalAmount = computed(() => sumByNumber(props.rowData, row => row.amount))
|
||||
|
||||
const pinnedTopRowData = computed<DetailRow[]>(() => [
|
||||
const pinnedTopRowData = ref<DetailRow[]>([
|
||||
{
|
||||
id: 'pinned-total-row',
|
||||
groupCode: '',
|
||||
groupName: '',
|
||||
majorCode: '',
|
||||
majorName: totalLabel.value,
|
||||
majorName: '',
|
||||
hasCost: false,
|
||||
hasArea: false,
|
||||
amount: totalAmount.value,
|
||||
amount: null,
|
||||
landArea: null,
|
||||
path: ['TOTAL']
|
||||
}
|
||||
@@ -148,19 +182,74 @@ const pinnedTopRowData = computed<DetailRow[]>(() => [
|
||||
|
||||
const saveToIndexedDB = async () => {
|
||||
try {
|
||||
await localforage.setItem(props.dbKey, {
|
||||
detailRows: JSON.parse(JSON.stringify(props.rowData))
|
||||
})
|
||||
const payload: GridPersistState = {
|
||||
detailRows: props.rowData.map(row => ({
|
||||
...JSON.parse(JSON.stringify(row)),
|
||||
hide: Boolean(row.hide)
|
||||
}))
|
||||
}
|
||||
payload.roughCalcEnabled = roughCalcEnabled.value
|
||||
payload.totalAmount = pinnedTopRowData.value[0].amount
|
||||
await localforage.setItem(props.dbKey, payload)
|
||||
} catch (error) {
|
||||
console.error('saveToIndexedDB failed:', error)
|
||||
}
|
||||
}
|
||||
|
||||
const onCellValueChanged = (_event: CellValueChangedEvent) => {
|
||||
const schedulePersist = () => {
|
||||
if (persistTimer) clearTimeout(persistTimer)
|
||||
persistTimer = setTimeout(() => {
|
||||
void saveToIndexedDB()
|
||||
}, 1000)
|
||||
}, 600)
|
||||
}
|
||||
|
||||
const setDetailRowsHidden = (hidden: boolean) => {
|
||||
for (const row of props.rowData) {
|
||||
row.hide = hidden
|
||||
}
|
||||
}
|
||||
|
||||
const onRoughCalcSwitch = (checked: boolean) => {
|
||||
gridApi.value?.stopEditing(true)
|
||||
roughCalcEnabled.value = checked
|
||||
setDetailRowsHidden(checked)
|
||||
if (!checked) {
|
||||
syncPinnedTotalForNormalMode()
|
||||
}else{
|
||||
pinnedTopRowData.value[0].amount = null
|
||||
const pinnedTopNode = gridApi.value?.getPinnedTopRow(0)
|
||||
if (pinnedTopNode) {
|
||||
pinnedTopNode.setDataValue('amount', null)
|
||||
}
|
||||
}
|
||||
schedulePersist()
|
||||
}
|
||||
|
||||
|
||||
|
||||
const onCellValueChanged = (event: CellValueChangedEvent) => {
|
||||
if (roughCalcEnabled.value && event.node?.rowPinned && event.colDef.field === 'amount') {
|
||||
if (typeof event.newValue === 'number') {
|
||||
pinnedTopRowData.value[0].amount = roundTo(event.newValue, 2)
|
||||
} else {
|
||||
const parsed = Number(event.newValue)
|
||||
pinnedTopRowData.value[0].amount = Number.isFinite(parsed) ? roundTo(parsed, 2) : null
|
||||
}
|
||||
|
||||
} else if (!roughCalcEnabled.value) {
|
||||
syncPinnedTotalForNormalMode()
|
||||
}
|
||||
schedulePersist()
|
||||
}
|
||||
|
||||
const onGridReady = (event: GridReadyEvent<DetailRow>) => {
|
||||
gridApi.value = event.api
|
||||
void loadIndustryFromBaseInfo()
|
||||
|
||||
|
||||
void loadGridPersistState()
|
||||
void refreshPinnedTotalLabelCell()
|
||||
|
||||
}
|
||||
|
||||
const processCellForClipboard = (params: any) => {
|
||||
@@ -191,16 +280,69 @@ const loadIndustryFromBaseInfo = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
void loadIndustryFromBaseInfo()
|
||||
const loadGridPersistState = async () => {
|
||||
try {
|
||||
const data = await localforage.getItem<GridPersistState>(props.dbKey)
|
||||
roughCalcEnabled.value = Boolean(data?.roughCalcEnabled)
|
||||
const detailRows = Array.isArray(data?.detailRows) ? data.detailRows : []
|
||||
const detailRowById = new Map(detailRows.map(row => [row.id, row]))
|
||||
for (const row of props.rowData) {
|
||||
const persisted = detailRowById.get(row.id)
|
||||
if (!persisted) {
|
||||
row.hide = roughCalcEnabled.value
|
||||
continue
|
||||
}
|
||||
row.amount = typeof persisted.amount === 'number' ? roundTo(persisted.amount, 2) : null
|
||||
row.landArea = typeof persisted.landArea === 'number' ? roundTo(persisted.landArea, 3) : null
|
||||
row.hide = typeof persisted.hide === 'boolean' ? persisted.hide : roughCalcEnabled.value
|
||||
}
|
||||
pinnedTopRowData.value[0].amount = typeof data?.totalAmount === 'number' ? data.totalAmount : null
|
||||
|
||||
const pinnedTopNode = (gridApi as any).value.getPinnedTopRow(0)
|
||||
if (pinnedTopNode) {
|
||||
pinnedTopNode.setDataValue('amount', pinnedTopRowData.value[0].amount)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('loadGridPersistState failed:', error)
|
||||
roughCalcEnabled.value = false
|
||||
pinnedTopRowData.value[0].amount= null
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
watch(totalLabel, () => {
|
||||
refreshPinnedTotalLabelCell()
|
||||
})
|
||||
|
||||
onActivated(() => {
|
||||
void loadIndustryFromBaseInfo()
|
||||
})
|
||||
const syncPinnedTotalForNormalMode = () => {
|
||||
if (roughCalcEnabled.value) return
|
||||
|
||||
if (!gridApi.value) {
|
||||
pinnedTopRowData.value[0].amount = sumByNumber(props.rowData, row => row.amount)
|
||||
return
|
||||
}
|
||||
let total = 0
|
||||
let hasValue = false
|
||||
|
||||
props.rowData.forEach(node => {
|
||||
|
||||
const amount = node.amount
|
||||
if (typeof amount === 'number' && Number.isFinite(amount)) {
|
||||
total += amount
|
||||
hasValue = true
|
||||
}
|
||||
})
|
||||
pinnedTopRowData.value[0].amount = hasValue ? roundTo(total, 2) : null
|
||||
const pinnedTopNode = gridApi.value.getPinnedTopRow(0)
|
||||
if (pinnedTopNode) {
|
||||
pinnedTopNode.setDataValue('amount', hasValue ? roundTo(total, 2) : null)
|
||||
}
|
||||
}
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
if (persistTimer) clearTimeout(persistTimer)
|
||||
gridApi.value = null
|
||||
void saveToIndexedDB()
|
||||
})
|
||||
</script>
|
||||
@@ -212,18 +354,30 @@ onBeforeUnmount(() => {
|
||||
<h3 class="text-sm font-semibold text-foreground cursor-pointer select-none transition-colors hover:text-primary">
|
||||
{{ props.title }}
|
||||
</h3>
|
||||
<div class="text-xs text-muted-foreground"></div>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class=" text-xs text-muted-foreground">粗略计算</span>
|
||||
<SwitchRoot
|
||||
class="cursor-pointer peer h-5 w-9 shrink-0 rounded-full border border-transparent bg-muted shadow-sm transition-colors data-[state=checked]:bg-primary"
|
||||
:modelValue="roughCalcEnabled"
|
||||
@update:modelValue="onRoughCalcSwitch"
|
||||
>
|
||||
<SwitchThumb
|
||||
class="block h-4 w-4 translate-x-0.5 rounded-full bg-background shadow transition-transform data-[state=checked]:translate-x-4"
|
||||
/>
|
||||
</SwitchRoot>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ag-theme-quartz w-full flex-1 min-h-0 h-full">
|
||||
<AgGridVue
|
||||
:style="{ height: '100%' }"
|
||||
:rowData="props.rowData"
|
||||
:rowData="visibleRowData"
|
||||
:pinnedTopRowData="pinnedTopRowData"
|
||||
:columnDefs="columnDefs"
|
||||
:autoGroupColumnDef="autoGroupColumnDef"
|
||||
:gridOptions="gridOptions"
|
||||
:theme="myTheme"
|
||||
@grid-ready="onGridReady"
|
||||
@cell-value-changed="onCellValueChanged"
|
||||
:suppressColumnVirtualisation="true"
|
||||
:suppressRowVirtualisation="true"
|
||||
|
||||
@@ -1,9 +1,32 @@
|
||||
<script setup lang="ts">
|
||||
import { parseDate } from '@internationalized/date'
|
||||
import { onMounted, ref, watch } from 'vue'
|
||||
import localforage from 'localforage'
|
||||
import { industryTypeList } from '@/sql'
|
||||
import { CircleHelp } from 'lucide-vue-next'
|
||||
import { Calendar as CalendarIcon, CircleHelp } from 'lucide-vue-next'
|
||||
import { TooltipContent, TooltipProvider, TooltipRoot, TooltipTrigger } from '@/components/ui/tooltip'
|
||||
import {
|
||||
DatePickerAnchor,
|
||||
DatePickerArrow,
|
||||
DatePickerCalendar,
|
||||
DatePickerCell,
|
||||
DatePickerCellTrigger,
|
||||
DatePickerClose,
|
||||
DatePickerContent,
|
||||
DatePickerField,
|
||||
DatePickerGrid,
|
||||
DatePickerGridBody,
|
||||
DatePickerGridHead,
|
||||
DatePickerGridRow,
|
||||
DatePickerHeadCell,
|
||||
DatePickerHeader,
|
||||
DatePickerHeading,
|
||||
DatePickerInput,
|
||||
DatePickerNext,
|
||||
DatePickerPrev,
|
||||
DatePickerRoot,
|
||||
DatePickerTrigger
|
||||
} from 'reka-ui'
|
||||
|
||||
interface XmInfoState {
|
||||
projectIndustry?: string
|
||||
@@ -31,6 +54,37 @@ const preparedBy = ref('')
|
||||
const reviewedBy = ref('')
|
||||
const preparedCompany = ref('')
|
||||
const preparedDate = ref('')
|
||||
const preparedDatePickerValue = ref<any>(undefined)
|
||||
|
||||
const normalizeDateString = (value: unknown): string => {
|
||||
if (typeof value !== 'string') return ''
|
||||
const trimmed = value.trim()
|
||||
if (!trimmed) return ''
|
||||
try {
|
||||
const parsed = parseDate(trimmed)
|
||||
return parsed.toString() === trimmed ? trimmed : ''
|
||||
} catch {
|
||||
return ''
|
||||
}
|
||||
}
|
||||
|
||||
const syncPreparedDatePickerFromString = () => {
|
||||
if (!preparedDate.value) {
|
||||
preparedDatePickerValue.value = undefined
|
||||
return
|
||||
}
|
||||
try {
|
||||
preparedDatePickerValue.value = parseDate(preparedDate.value)
|
||||
} catch {
|
||||
preparedDatePickerValue.value = undefined
|
||||
preparedDate.value = ''
|
||||
}
|
||||
}
|
||||
|
||||
const handlePreparedDateSelect = (date: any) => {
|
||||
preparedDatePickerValue.value = date
|
||||
preparedDate.value = date?.toString() ?? ''
|
||||
}
|
||||
|
||||
const majorParentNodes: MajorParentNode[] = industryTypeList.map(item => ({
|
||||
id: item.id,
|
||||
@@ -69,7 +123,8 @@ const loadFromIndexedDB = async () => {
|
||||
preparedBy.value = typeof data.preparedBy === 'string' ? data.preparedBy : ''
|
||||
reviewedBy.value = typeof data.reviewedBy === 'string' ? data.reviewedBy : ''
|
||||
preparedCompany.value = typeof data.preparedCompany === 'string' ? data.preparedCompany : ''
|
||||
preparedDate.value = typeof data.preparedDate === 'string' ? data.preparedDate : ''
|
||||
preparedDate.value = normalizeDateString(data.preparedDate)
|
||||
syncPreparedDatePickerFromString()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -80,6 +135,7 @@ const loadFromIndexedDB = async () => {
|
||||
reviewedBy.value = ''
|
||||
preparedCompany.value = ''
|
||||
preparedDate.value = ''
|
||||
syncPreparedDatePickerFromString()
|
||||
} catch (error) {
|
||||
console.error('loadFromIndexedDB failed:', error)
|
||||
projectIndustry.value = DEFAULT_PROJECT_INDUSTRY
|
||||
@@ -88,6 +144,7 @@ const loadFromIndexedDB = async () => {
|
||||
reviewedBy.value = ''
|
||||
preparedCompany.value = ''
|
||||
preparedDate.value = ''
|
||||
syncPreparedDatePickerFromString()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,6 +183,7 @@ const createProject = async () => {
|
||||
reviewedBy.value = ''
|
||||
preparedCompany.value = ''
|
||||
preparedDate.value = ''
|
||||
syncPreparedDatePickerFromString()
|
||||
isProjectInitialized.value = true
|
||||
showCreateDialog.value = false
|
||||
await saveToIndexedDB()
|
||||
@@ -238,12 +296,112 @@ onMounted(async () => {
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-foreground">编制日期</label>
|
||||
<input
|
||||
v-model="preparedDate"
|
||||
type="text"
|
||||
placeholder="请输入编制日期"
|
||||
class="mt-2 h-10 w-full rounded-lg border bg-background px-4 text-sm outline-none ring-offset-background shadow-sm transition placeholder:text-muted-foreground/70 focus-visible:border-primary/60 focus-visible:ring-2 focus-visible:ring-ring"
|
||||
/>
|
||||
<DatePickerRoot
|
||||
locale="en-CA"
|
||||
:model-value="preparedDatePickerValue"
|
||||
@update:model-value="handlePreparedDateSelect"
|
||||
>
|
||||
<DatePickerAnchor class="mt-2 block w-full">
|
||||
<DatePickerField
|
||||
v-slot="{ segments }"
|
||||
class="flex h-10 w-full items-center justify-between rounded-lg border bg-background px-4 text-sm outline-none ring-offset-background shadow-sm transition focus-within:border-primary/60 focus-within:ring-2 focus-within:ring-ring"
|
||||
>
|
||||
<div class="flex items-center">
|
||||
<template
|
||||
v-for="(segment, index) in segments"
|
||||
:key="`${segment.part}-${index}`"
|
||||
>
|
||||
<DatePickerInput
|
||||
:part="segment.part"
|
||||
:class="segment.part === 'literal' ? 'text-muted-foreground/70' : 'text-foreground'"
|
||||
>
|
||||
{{ segment.part === 'literal' ? '-' : segment.value }}
|
||||
</DatePickerInput>
|
||||
</template>
|
||||
</div>
|
||||
<DatePickerTrigger as-child>
|
||||
<button type="button" class="inline-flex h-6 w-6 items-center justify-center text-muted-foreground">
|
||||
<CalendarIcon class="h-4 w-4" />
|
||||
</button>
|
||||
</DatePickerTrigger>
|
||||
</DatePickerField>
|
||||
</DatePickerAnchor>
|
||||
<DatePickerContent class="z-50 w-[22rem] rounded-lg border bg-card p-4 shadow-lg">
|
||||
<DatePickerArrow class="fill-border" />
|
||||
<DatePickerCalendar v-slot="{ weekDays, grid }" locale="zh-CN">
|
||||
<DatePickerHeader class="mb-2 flex items-center justify-between">
|
||||
<DatePickerPrev as-child>
|
||||
<button
|
||||
type="button"
|
||||
class="inline-flex h-9 w-9 items-center justify-center rounded-md border text-sm transition hover:bg-muted"
|
||||
>
|
||||
‹
|
||||
</button>
|
||||
</DatePickerPrev>
|
||||
<DatePickerHeading class="text-base font-medium text-foreground" />
|
||||
<DatePickerNext as-child>
|
||||
<button
|
||||
type="button"
|
||||
class="inline-flex h-9 w-9 items-center justify-center rounded-md border text-sm transition hover:bg-muted"
|
||||
>
|
||||
›
|
||||
</button>
|
||||
</DatePickerNext>
|
||||
</DatePickerHeader>
|
||||
<div class="space-y-2">
|
||||
<DatePickerGrid
|
||||
v-for="month in grid"
|
||||
:key="month.value.toString()"
|
||||
class="w-full border-collapse select-none"
|
||||
>
|
||||
<DatePickerGridHead>
|
||||
<DatePickerGridRow class="grid grid-cols-7">
|
||||
<DatePickerHeadCell
|
||||
v-for="day in weekDays"
|
||||
:key="day"
|
||||
class="flex h-9 items-center justify-center text-sm text-muted-foreground"
|
||||
>
|
||||
{{ day }}
|
||||
</DatePickerHeadCell>
|
||||
</DatePickerGridRow>
|
||||
</DatePickerGridHead>
|
||||
<DatePickerGridBody>
|
||||
<DatePickerGridRow
|
||||
v-for="(weekDates, index) in month.rows"
|
||||
:key="`${month.value.toString()}-${index}`"
|
||||
class="grid grid-cols-7"
|
||||
>
|
||||
<DatePickerCell
|
||||
v-for="dateValue in weekDates"
|
||||
:key="dateValue.toString()"
|
||||
:date="dateValue"
|
||||
class="h-10 w-full p-0.5"
|
||||
>
|
||||
<DatePickerCellTrigger
|
||||
:day="dateValue"
|
||||
:month="month.value"
|
||||
class="h-full w-full rounded-md border border-transparent bg-transparent text-base outline-none transition hover:bg-muted data-[outside-view]:text-muted-foreground/40 data-[selected]:border-primary data-[selected]:bg-transparent data-[selected]:text-foreground data-[disabled]:opacity-40 data-[unavailable]:text-muted-foreground/40"
|
||||
>
|
||||
{{ dateValue.day }}
|
||||
</DatePickerCellTrigger>
|
||||
</DatePickerCell>
|
||||
</DatePickerGridRow>
|
||||
</DatePickerGridBody>
|
||||
</DatePickerGrid>
|
||||
</div>
|
||||
</DatePickerCalendar>
|
||||
<div class="mt-2 flex justify-end">
|
||||
<DatePickerClose as-child>
|
||||
<button
|
||||
type="button"
|
||||
class="h-8 rounded-md border px-3 text-xs text-foreground transition hover:bg-muted"
|
||||
>
|
||||
关闭
|
||||
</button>
|
||||
</DatePickerClose>
|
||||
</div>
|
||||
</DatePickerContent>
|
||||
</DatePickerRoot>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+2
-2
@@ -10,7 +10,7 @@ import {
|
||||
RowAutoHeightModule,
|
||||
TextEditorModule,
|
||||
TooltipModule,
|
||||
UndoRedoEditModule,
|
||||
UndoRedoEditModule,RenderApiModule
|
||||
|
||||
} from 'ag-grid-community'
|
||||
import {
|
||||
@@ -40,7 +40,7 @@ const AG_GRID_MODULES = [
|
||||
LargeTextEditorModule,
|
||||
UndoRedoEditModule,
|
||||
CellStyleModule,
|
||||
PinnedRowModule,
|
||||
PinnedRowModule,RenderApiModule ,
|
||||
TooltipModule,
|
||||
TreeDataModule,
|
||||
AggregationModule,
|
||||
|
||||
Reference in new issue
Block a user