全局自适应样式调整,换行自适应行高
This commit is contained in:
parent
75a6209010
commit
241a6c4003
@ -93,15 +93,15 @@ let data1 = {
|
|||||||
services: [
|
services: [
|
||||||
{
|
{
|
||||||
id: 0,
|
id: 0,
|
||||||
fee: 100000,
|
fee: 250000,//小计
|
||||||
finalFee: 100000,
|
finalFee: 250000,//确认金额
|
||||||
process: 0,// 工作环节,0为编制,1为审核
|
process: 0,// 工作环节,0为编制,1为审核
|
||||||
method1: { // 投资规模法
|
method1: { // 投资规模法
|
||||||
cost: 100000,
|
cost: 100000,
|
||||||
basicFee: 200,
|
basicFee: 200,
|
||||||
basicFee_basic: 200,
|
basicFee_basic: 200,
|
||||||
basicFee_optional: 0,
|
basicFee_optional: 0,
|
||||||
fee: 250000,
|
fee: 250000, //小计
|
||||||
proAmount: 3,
|
proAmount: 3,
|
||||||
det: [
|
det: [
|
||||||
{
|
{
|
||||||
@ -195,7 +195,9 @@ let data1 = {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
tasks: [{ serviceid: 0, process: 0, text: ['abc', 'efg'] }, { serviceid: 1, process: 0, text: ['abc', 'efg'] }],// 工作内容
|
tasks: [{ serviceid: 0, text: ['abc', 'efg'] },
|
||||||
|
{ serviceid: 2,text: ['abc', 'efg'] } //tasks不分组的时候传单对象[{text: ['abc', 'efg']}],分组的时候传分组的serviceid
|
||||||
|
],// 工作内容
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
addtional: {// 附加工作费
|
addtional: {// 附加工作费
|
||||||
@ -252,6 +254,7 @@ let data1 = {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
tasks:[]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 1,
|
id: 1,
|
||||||
@ -306,6 +309,7 @@ let data1 = {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
tasks:[]
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -313,6 +317,7 @@ let data1 = {
|
|||||||
ref: { richText: [{ font: { charset: 134, color: { theme: 1 }, italic: true, name: '宋体', size: 10 }, text: 'Y' }, { font: { charset: 134, color: { theme: 1 }, italic: true, name: 'Calibri', size: 10, vertAlign: 'subscript' }, text: 'B' }] },
|
ref: { richText: [{ font: { charset: 134, color: { theme: 1 }, italic: true, name: '宋体', size: 10 }, text: 'Y' }, { font: { charset: 134, color: { theme: 1 }, italic: true, name: 'Calibri', size: 10, vertAlign: 'subscript' }, text: 'B' }] },
|
||||||
name: '预备费',
|
name: '预备费',
|
||||||
fee: 10000,
|
fee: 10000,
|
||||||
|
tasks:[],
|
||||||
m0: {
|
m0: {
|
||||||
coe: 0.03,
|
coe: 0.03,
|
||||||
fee: 10000,
|
fee: 10000,
|
||||||
|
|||||||
@ -1469,7 +1469,7 @@ watch(budgetRefreshSignature, (next, prev) => {
|
|||||||
<CardHeader
|
<CardHeader
|
||||||
:class="[
|
:class="[
|
||||||
'flex flex-row items-center justify-between gap-0 space-y-0',
|
'flex flex-row items-center justify-between gap-0 space-y-0',
|
||||||
isListLayout ? 'px-3 py-2' : 'pb-6'
|
isListLayout ? 'px-3 py-2' : 'pb-4'
|
||||||
]"
|
]"
|
||||||
>
|
>
|
||||||
<CardTitle
|
<CardTitle
|
||||||
@ -1542,7 +1542,7 @@ watch(budgetRefreshSignature, (next, prev) => {
|
|||||||
v-if="!isListLayout"
|
v-if="!isListLayout"
|
||||||
:class="[
|
:class="[
|
||||||
'px-6 text-xs text-muted-foreground',
|
'px-6 text-xs text-muted-foreground',
|
||||||
'space-y-1 '
|
'space-y-1 pb-1'
|
||||||
]"
|
]"
|
||||||
>
|
>
|
||||||
<div class="break-all">ID:{{ element.id }}</div>
|
<div class="break-all">ID:{{ element.id }}</div>
|
||||||
|
|||||||
@ -211,7 +211,7 @@ onBeforeUnmount(() => {
|
|||||||
<label class="space-y-1.5">
|
<label class="space-y-1.5">
|
||||||
<div class="text-xs text-muted-foreground">费率(%)</div>
|
<div class="text-xs text-muted-foreground">费率(%)</div>
|
||||||
<input v-model="rateInput" type="text" inputmode="decimal" placeholder="请输入费率,建议1 ~ 5"
|
<input v-model="rateInput" type="text" inputmode="decimal" placeholder="请输入费率,建议1 ~ 5"
|
||||||
class="h-9 w-full rounded-md border bg-background px-3 text-sm text-foreground outline-none focus:ring-2 focus:ring-primary/30"
|
class="rate-input h-9 w-full rounded-md border bg-background px-3 text-sm text-foreground outline-none focus:ring-2 focus:ring-primary/30"
|
||||||
@blur="applyRateInput" />
|
@blur="applyRateInput" />
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
@ -231,3 +231,8 @@ onBeforeUnmount(() => {
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
:deep(.rate-input) {
|
||||||
|
text-align: left !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, onActivated, onBeforeUnmount, onDeactivated, onMounted, ref, watch } from 'vue'
|
import { computed, nextTick, onActivated, onBeforeUnmount, onDeactivated, onMounted, ref, watch } from 'vue'
|
||||||
import { AgGridVue } from 'ag-grid-vue3'
|
import { AgGridVue } from 'ag-grid-vue3'
|
||||||
import type { ColDef, ColGroupDef, GridApi, GridReadyEvent } from 'ag-grid-community'
|
import type { ColDef, ColGroupDef, GridApi, GridReadyEvent } from 'ag-grid-community'
|
||||||
import { expertList } from '@/sql'
|
import { expertList } from '@/sql'
|
||||||
@ -293,7 +293,7 @@ const editableNumberCol = <K extends keyof DetailRow>(
|
|||||||
): ColDef<DetailRow> => ({
|
): ColDef<DetailRow> => ({
|
||||||
headerName,
|
headerName,
|
||||||
field,
|
field,
|
||||||
minWidth: 150,
|
minWidth: 120,
|
||||||
flex: 1,
|
flex: 1,
|
||||||
editable: params => !params.node?.group && !params.node?.rowPinned,
|
editable: params => !params.node?.group && !params.node?.rowPinned,
|
||||||
cellClass: params => (!params.node?.group && !params.node?.rowPinned ? 'editable-cell-line' : ''),
|
cellClass: params => (!params.node?.group && !params.node?.rowPinned ? 'editable-cell-line' : ''),
|
||||||
@ -314,7 +314,7 @@ const editableMoneyCol = <K extends keyof DetailRow>(
|
|||||||
headerName,
|
headerName,
|
||||||
field,
|
field,
|
||||||
headerClass: 'ag-right-aligned-header',
|
headerClass: 'ag-right-aligned-header',
|
||||||
minWidth: 150,
|
minWidth: 120,
|
||||||
flex: 1,
|
flex: 1,
|
||||||
editable: params => !params.node?.group && !params.node?.rowPinned,
|
editable: params => !params.node?.group && !params.node?.rowPinned,
|
||||||
cellClass: params =>
|
cellClass: params =>
|
||||||
@ -343,7 +343,7 @@ const readonlyTextCol = <K extends keyof DetailRow>(
|
|||||||
): ColDef<DetailRow> => ({
|
): ColDef<DetailRow> => ({
|
||||||
headerName,
|
headerName,
|
||||||
field,
|
field,
|
||||||
minWidth: 170,
|
minWidth: 120,
|
||||||
flex: 1,
|
flex: 1,
|
||||||
editable: false,
|
editable: false,
|
||||||
valueFormatter: params => params.value || '',
|
valueFormatter: params => params.value || '',
|
||||||
@ -354,8 +354,8 @@ const columnDefs: (ColDef<DetailRow> | ColGroupDef<DetailRow>)[] = [
|
|||||||
{
|
{
|
||||||
headerName: '编码',
|
headerName: '编码',
|
||||||
field: 'expertCode',
|
field: 'expertCode',
|
||||||
minWidth: 120,
|
minWidth: 90,
|
||||||
width: 140,
|
width: 100,
|
||||||
pinned: 'left',
|
pinned: 'left',
|
||||||
colSpan: params => (params.node?.rowPinned ? 2 : 1),
|
colSpan: params => (params.node?.rowPinned ? 2 : 1),
|
||||||
valueFormatter: params => (params.node?.rowPinned ? '总合计' : params.value || '')
|
valueFormatter: params => (params.node?.rowPinned ? '总合计' : params.value || '')
|
||||||
@ -363,10 +363,13 @@ const columnDefs: (ColDef<DetailRow> | ColGroupDef<DetailRow>)[] = [
|
|||||||
{
|
{
|
||||||
headerName: '人员名称',
|
headerName: '人员名称',
|
||||||
field: 'expertName',
|
field: 'expertName',
|
||||||
minWidth: 200,
|
minWidth: 210,
|
||||||
width: 220,
|
width: 230,
|
||||||
pinned: 'left',
|
pinned: 'left',
|
||||||
tooltipField: 'expertName',
|
tooltipField: 'expertName',
|
||||||
|
wrapText: true,
|
||||||
|
autoHeight: true,
|
||||||
|
cellStyle: { whiteSpace: 'normal', lineHeight: '1.2' },
|
||||||
valueFormatter: params => (params.node?.rowPinned ? '' : params.value || '')
|
valueFormatter: params => (params.node?.rowPinned ? '' : params.value || '')
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -391,7 +394,7 @@ const columnDefs: (ColDef<DetailRow> | ColGroupDef<DetailRow>)[] = [
|
|||||||
headerName: '服务预算(元)',
|
headerName: '服务预算(元)',
|
||||||
field: 'serviceBudget',
|
field: 'serviceBudget',
|
||||||
headerClass: 'ag-right-aligned-header',
|
headerClass: 'ag-right-aligned-header',
|
||||||
minWidth: 150,
|
minWidth: 120,
|
||||||
flex: 1,
|
flex: 1,
|
||||||
cellClass: 'ag-right-aligned-cell',
|
cellClass: 'ag-right-aligned-cell',
|
||||||
editable: false,
|
editable: false,
|
||||||
@ -405,8 +408,8 @@ const columnDefs: (ColDef<DetailRow> | ColGroupDef<DetailRow>)[] = [
|
|||||||
{
|
{
|
||||||
headerName: '说明',
|
headerName: '说明',
|
||||||
field: 'remark',
|
field: 'remark',
|
||||||
minWidth: 180,
|
minWidth: 120,
|
||||||
flex: 1.2,
|
flex: 1,
|
||||||
cellEditor: 'agLargeTextCellEditor',
|
cellEditor: 'agLargeTextCellEditor',
|
||||||
wrapText: true,
|
wrapText: true,
|
||||||
autoHeight: true,
|
autoHeight: true,
|
||||||
@ -522,11 +525,40 @@ const loadFromIndexedDB = async () => {
|
|||||||
const handleCellValueChanged = () => {
|
const handleCellValueChanged = () => {
|
||||||
syncServiceBudgetToRows()
|
syncServiceBudgetToRows()
|
||||||
gridApi.value?.refreshCells({ force: true })
|
gridApi.value?.refreshCells({ force: true })
|
||||||
|
scheduleAutoRowHeights()
|
||||||
void saveToIndexedDB()
|
void saveToIndexedDB()
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleGridReady = (event: GridReadyEvent<DetailRow>) => {
|
const handleGridReady = (event: GridReadyEvent<DetailRow>) => {
|
||||||
gridApi.value = event.api
|
gridApi.value = event.api
|
||||||
|
scheduleAutoRowHeights()
|
||||||
|
}
|
||||||
|
|
||||||
|
let autoHeightSyncTimer: ReturnType<typeof setTimeout> | null = null
|
||||||
|
const syncAutoRowHeights = async () => {
|
||||||
|
await nextTick()
|
||||||
|
const api = gridApi.value
|
||||||
|
if (!api) return
|
||||||
|
api.resetRowHeights()
|
||||||
|
api.onRowHeightChanged()
|
||||||
|
api.refreshCells({ force: true })
|
||||||
|
api.redrawRows()
|
||||||
|
}
|
||||||
|
|
||||||
|
const scheduleAutoRowHeights = () => {
|
||||||
|
if (autoHeightSyncTimer) clearTimeout(autoHeightSyncTimer)
|
||||||
|
autoHeightSyncTimer = setTimeout(() => {
|
||||||
|
autoHeightSyncTimer = null
|
||||||
|
void syncAutoRowHeights()
|
||||||
|
}, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
const onGridSizeChanged = () => {
|
||||||
|
scheduleAutoRowHeights()
|
||||||
|
}
|
||||||
|
|
||||||
|
const onColumnResized = () => {
|
||||||
|
scheduleAutoRowHeights()
|
||||||
}
|
}
|
||||||
|
|
||||||
const processCellForClipboard = (params: any) => {
|
const processCellForClipboard = (params: any) => {
|
||||||
@ -546,16 +578,26 @@ const processCellFromClipboard = (params: any) => {
|
|||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
await loadFromIndexedDB()
|
await loadFromIndexedDB()
|
||||||
|
scheduleAutoRowHeights()
|
||||||
})
|
})
|
||||||
|
|
||||||
onActivated(async () => {
|
onActivated(async () => {
|
||||||
await loadFromIndexedDB()
|
await loadFromIndexedDB()
|
||||||
|
scheduleAutoRowHeights()
|
||||||
})
|
})
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => props.storageKey,
|
() => props.storageKey,
|
||||||
() => {
|
() => {
|
||||||
void loadFromIndexedDB()
|
void loadFromIndexedDB()
|
||||||
|
scheduleAutoRowHeights()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => detailRows.value.length,
|
||||||
|
() => {
|
||||||
|
scheduleAutoRowHeights()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -567,6 +609,10 @@ onDeactivated(() => {
|
|||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
gridApi.value?.stopEditing()
|
gridApi.value?.stopEditing()
|
||||||
gridApi.value = null
|
gridApi.value = null
|
||||||
|
if (autoHeightSyncTimer) {
|
||||||
|
clearTimeout(autoHeightSyncTimer)
|
||||||
|
autoHeightSyncTimer = null
|
||||||
|
}
|
||||||
void saveToIndexedDB()
|
void saveToIndexedDB()
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
@ -602,6 +648,8 @@ onBeforeUnmount(() => {
|
|||||||
:undoRedoCellEditing="true"
|
:undoRedoCellEditing="true"
|
||||||
:undoRedoCellEditingLimit="20"
|
:undoRedoCellEditingLimit="20"
|
||||||
@grid-ready="handleGridReady"
|
@grid-ready="handleGridReady"
|
||||||
|
@grid-size-changed="onGridSizeChanged"
|
||||||
|
@column-resized="onColumnResized"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -219,7 +219,7 @@ const hydrateRowsFromMethodStores = async (rows: FeeMethodRow[]): Promise<FeeMet
|
|||||||
const rateValue = toFiniteUnknown(rateData?.rate)
|
const rateValue = toFiniteUnknown(rateData?.rate)
|
||||||
const rateFee =
|
const rateFee =
|
||||||
contractBase != null && rateValue != null
|
contractBase != null && rateValue != null
|
||||||
? round3(contractBase * rateValue)
|
? round3(contractBase * rateValue / 100)
|
||||||
: storedRateFee != null
|
: storedRateFee != null
|
||||||
? round3(storedRateFee)
|
? round3(storedRateFee)
|
||||||
: null
|
: null
|
||||||
|
|||||||
@ -250,7 +250,15 @@ onMounted(async () => {
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="md:col-span-2 xl:col-span-4">
|
||||||
|
<label class="block text-sm font-medium text-foreground">项目概况</label>
|
||||||
|
<textarea
|
||||||
|
v-model="overview"
|
||||||
|
rows="3"
|
||||||
|
placeholder="请输入项目概况"
|
||||||
|
class="mt-2 w-full rounded-lg border bg-background px-4 py-2 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 resize-none"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label class="block text-sm font-medium text-foreground">编制人</label>
|
<label class="block text-sm font-medium text-foreground">编制人</label>
|
||||||
<input
|
<input
|
||||||
@ -391,18 +399,10 @@ onMounted(async () => {
|
|||||||
</DatePickerContent>
|
</DatePickerContent>
|
||||||
</DatePickerRoot>
|
</DatePickerRoot>
|
||||||
</div>
|
</div>
|
||||||
<div class="md:col-span-2 xl:col-span-4">
|
|
||||||
<label class="block text-sm font-medium text-foreground">项目概况</label>
|
|
||||||
<textarea
|
|
||||||
v-model="overview"
|
|
||||||
rows="3"
|
|
||||||
placeholder="请输入项目概况"
|
|
||||||
class="mt-2 w-full rounded-lg border bg-background px-4 py-2 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 resize-none"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="md:col-span-2 xl:col-span-4">
|
<div class="md:col-span-2 xl:col-span-4">
|
||||||
<label class="block text-sm font-medium text-foreground">备注</label>
|
<label class="block text-sm font-medium text-foreground">其他说明</label>
|
||||||
<textarea
|
<textarea
|
||||||
v-model="desc"
|
v-model="desc"
|
||||||
rows="4"
|
rows="4"
|
||||||
|
|||||||
@ -8,7 +8,7 @@ import { useKvStore } from '@/pinia/kv'
|
|||||||
import { Button } from '@/components/ui/button'
|
import { Button } from '@/components/ui/button'
|
||||||
import { ScrollArea } from '@/components/ui/scroll-area'
|
import { ScrollArea } from '@/components/ui/scroll-area'
|
||||||
import { TooltipContent, TooltipProvider, TooltipRoot, TooltipTrigger } from '@/components/ui/tooltip'
|
import { TooltipContent, TooltipProvider, TooltipRoot, TooltipTrigger } from '@/components/ui/tooltip'
|
||||||
import { ChevronDown, CircleHelp, RotateCcw, X } from 'lucide-vue-next'
|
import { ChevronDown, CircleHelp, Loader2, RotateCcw, X } from 'lucide-vue-next'
|
||||||
import localforage from 'localforage'
|
import localforage from 'localforage'
|
||||||
import {
|
import {
|
||||||
AlertDialogAction,
|
AlertDialogAction,
|
||||||
@ -361,6 +361,7 @@ const MAJOR_FACTOR_DB_KEY = 'xm-major-factor-v1'
|
|||||||
const PINIA_PERSIST_DB_NAME = 'DB'
|
const PINIA_PERSIST_DB_NAME = 'DB'
|
||||||
const PINIA_PERSIST_BASE_STORE_NAME = 'pinia'
|
const PINIA_PERSIST_BASE_STORE_NAME = 'pinia'
|
||||||
const PINIA_PERSIST_STORE_IDS = ['tabs', 'zxFwPricing', 'kv'] as const
|
const PINIA_PERSIST_STORE_IDS = ['tabs', 'zxFwPricing', 'kv'] as const
|
||||||
|
const RESET_MIN_LOADING_MS = 1000
|
||||||
const userGuideSteps: UserGuideStep[] = [
|
const userGuideSteps: UserGuideStep[] = [
|
||||||
{
|
{
|
||||||
title: '欢迎使用',
|
title: '欢迎使用',
|
||||||
@ -457,6 +458,8 @@ const tabContextRef = ref<HTMLElement | null>(null)
|
|||||||
|
|
||||||
const dataMenuOpen = ref(false)
|
const dataMenuOpen = ref(false)
|
||||||
const dataMenuRef = ref<HTMLElement | null>(null)
|
const dataMenuRef = ref<HTMLElement | null>(null)
|
||||||
|
const resetConfirmOpen = ref(false)
|
||||||
|
const isResetting = ref(false)
|
||||||
const importFileRef = ref<HTMLInputElement | null>(null)
|
const importFileRef = ref<HTMLInputElement | null>(null)
|
||||||
const importConfirmOpen = ref(false)
|
const importConfirmOpen = ref(false)
|
||||||
const pendingImportPayload = shallowRef<DataPackage | null>(null)
|
const pendingImportPayload = shallowRef<DataPackage | null>(null)
|
||||||
@ -1731,6 +1734,9 @@ const confirmImportOverride = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const handleReset = async () => {
|
const handleReset = async () => {
|
||||||
|
if (isResetting.value) return
|
||||||
|
const wait = (ms: number) => new Promise<void>(resolve => setTimeout(resolve, ms))
|
||||||
|
const resetStartedAt = Date.now()
|
||||||
const deleteIndexedDBByName = (dbName: string) =>
|
const deleteIndexedDBByName = (dbName: string) =>
|
||||||
new Promise<void>((resolve) => {
|
new Promise<void>((resolve) => {
|
||||||
try {
|
try {
|
||||||
@ -1758,53 +1764,54 @@ const handleReset = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
isResetting.value = true
|
||||||
dataMenuOpen.value = false
|
dataMenuOpen.value = false
|
||||||
|
|
||||||
// 1) 先清运行时内存态,避免旧状态在清库过程被再次持久化
|
// 1) 仅清持久化层,不先改当前页面内存态,避免“先切首页再刷新”的二次闪烁。
|
||||||
tabStore.resetTabs()
|
|
||||||
zxFwPricingStore.$patch({
|
|
||||||
contracts: {},
|
|
||||||
contractLoaded: {},
|
|
||||||
servicePricingStates: {},
|
|
||||||
htFeeMainStates: {},
|
|
||||||
htFeeMethodStates: {},
|
|
||||||
keyedStates: {}
|
|
||||||
} as any)
|
|
||||||
await kvStore.clear()
|
|
||||||
|
|
||||||
// 2) 清浏览器存储与默认 localforage 数据
|
|
||||||
localStorage.clear()
|
localStorage.clear()
|
||||||
sessionStorage.clear()
|
sessionStorage.clear()
|
||||||
await localforage.clear()
|
await localforage.clear()
|
||||||
|
|
||||||
// 3) 清 pinia 分库持久化
|
// 2) 清 pinia 分库持久化
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
getPiniaPersistStores().map(async ({ store }) => {
|
getPiniaPersistStores().map(async ({ store }) => {
|
||||||
await store.clear()
|
await store.clear()
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
// 4) 清插件按 store 维护的持久化 key(双保险)
|
// 3) 清插件按 store 维护的持久化 key(双保险)
|
||||||
const clearTasks: Promise<void>[] = []
|
const clearTasks: Promise<void>[] = []
|
||||||
if (tabStore.$clearPersisted) clearTasks.push(tabStore.$clearPersisted())
|
if (tabStore.$clearPersisted) clearTasks.push(tabStore.$clearPersisted())
|
||||||
if (zxFwPricingStore.$clearPersisted) clearTasks.push(zxFwPricingStore.$clearPersisted())
|
if (zxFwPricingStore.$clearPersisted) clearTasks.push(zxFwPricingStore.$clearPersisted())
|
||||||
if (kvStore.$clearPersisted) clearTasks.push(kvStore.$clearPersisted())
|
if (kvStore.$clearPersisted) clearTasks.push(kvStore.$clearPersisted())
|
||||||
await Promise.all(clearTasks)
|
await Promise.all(clearTasks)
|
||||||
|
|
||||||
// 5) 强制删除已知 IndexedDB,避免页面卸载阶段旧组件回写脏数据
|
// 4) 强制删除已知 IndexedDB,避免页面卸载阶段旧组件回写脏数据
|
||||||
await purgeKnownIndexedDB()
|
await purgeKnownIndexedDB()
|
||||||
|
|
||||||
// 6) 需要保留的最小标记恢复
|
// 5) 需要保留的最小标记恢复
|
||||||
writeWorkspaceMode('project')
|
writeWorkspaceMode('project')
|
||||||
localStorage.setItem(USER_GUIDE_COMPLETED_KEY, '1')
|
localStorage.setItem(USER_GUIDE_COMPLETED_KEY, '1')
|
||||||
|
|
||||||
|
// 6) 保证重置按钮进度动画至少可见 2s,避免“刚点就消失”。
|
||||||
|
const elapsed = Date.now() - resetStartedAt
|
||||||
|
if (elapsed < RESET_MIN_LOADING_MS) {
|
||||||
|
await wait(RESET_MIN_LOADING_MS - elapsed)
|
||||||
|
}
|
||||||
|
|
||||||
// 7) 直接刷新,交由默认初始化重建空状态
|
// 7) 直接刷新,交由默认初始化重建空状态
|
||||||
window.location.reload()
|
window.location.reload()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('reset failed:', error)
|
console.error('reset failed:', error)
|
||||||
|
isResetting.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleResetConfirmOpenChange = (open: boolean) => {
|
||||||
|
if (isResetting.value) return
|
||||||
|
resetConfirmOpen.value = open
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
window.addEventListener('mousedown', handleGlobalMouseDown)
|
window.addEventListener('mousedown', handleGlobalMouseDown)
|
||||||
window.addEventListener('keydown', handleGlobalKeyDown)
|
window.addEventListener('keydown', handleGlobalKeyDown)
|
||||||
@ -1931,22 +1938,26 @@ watch(
|
|||||||
<div class="flex shrink-0 self-center items-center gap-1">
|
<div class="flex shrink-0 self-center items-center gap-1">
|
||||||
<div ref="dataMenuRef" class="relative shrink-0">
|
<div ref="dataMenuRef" class="relative shrink-0">
|
||||||
<Button variant="outline" size="sm"
|
<Button variant="outline" size="sm"
|
||||||
class="h-9 min-h-9 shrink-0 px-3 py-0 text-sm leading-none cursor-pointer"
|
class="app-toolbar-btn shrink-0 cursor-pointer"
|
||||||
|
:disabled="isResetting"
|
||||||
@click="dataMenuOpen = !dataMenuOpen">
|
@click="dataMenuOpen = !dataMenuOpen">
|
||||||
<ChevronDown class="h-4 w-4 mr-1" />
|
<ChevronDown class="h-4 w-4 mr-1" />
|
||||||
导入/导出
|
导入/导出
|
||||||
</Button>
|
</Button>
|
||||||
<div v-if="dataMenuOpen"
|
<div v-if="dataMenuOpen"
|
||||||
class="absolute right-0 top-full mt-1 z-50 rounded-md border bg-background p-1 shadow-md">
|
class="absolute right-0 top-full mt-1 z-50 rounded-md border bg-background p-1 shadow-md">
|
||||||
<button class="w-full cursor-pointer rounded px-3 py-1.5 text-left text-sm hover:bg-muted"
|
<button class="w-full cursor-pointer rounded px-3 py-1.5 text-left text-sm hover:bg-muted disabled:cursor-not-allowed disabled:opacity-50"
|
||||||
|
:disabled="isResetting"
|
||||||
@click="triggerImport">
|
@click="triggerImport">
|
||||||
导入
|
导入
|
||||||
</button>
|
</button>
|
||||||
<button class="w-full cursor-pointer rounded px-3 py-1.5 text-left text-sm hover:bg-muted"
|
<button class="w-full cursor-pointer rounded px-3 py-1.5 text-left text-sm hover:bg-muted disabled:cursor-not-allowed disabled:opacity-50"
|
||||||
|
:disabled="isResetting"
|
||||||
@click="exportData">
|
@click="exportData">
|
||||||
导出
|
导出
|
||||||
</button>
|
</button>
|
||||||
<button class="w-full cursor-pointer rounded px-3 py-1.5 text-left text-sm hover:bg-muted"
|
<button class="w-full cursor-pointer rounded px-3 py-1.5 text-left text-sm hover:bg-muted disabled:cursor-not-allowed disabled:opacity-50"
|
||||||
|
:disabled="isResetting"
|
||||||
v-if="readWorkspaceMode() !== 'quick'"
|
v-if="readWorkspaceMode() !== 'quick'"
|
||||||
@click="exportReport">
|
@click="exportReport">
|
||||||
导出报表
|
导出报表
|
||||||
@ -1955,16 +1966,18 @@ watch(
|
|||||||
<input ref="importFileRef" type="file" accept=".zw" class="hidden" @change="importData" />
|
<input ref="importFileRef" type="file" accept=".zw" class="hidden" @change="importData" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Button variant="outline" size="sm" class="h-9 min-h-9 shrink-0 px-3 py-0 text-sm leading-none cursor-pointer"
|
<Button variant="outline" size="sm" class="app-toolbar-btn shrink-0 cursor-pointer"
|
||||||
|
:disabled="isResetting"
|
||||||
@click="openUserGuide(0)">
|
@click="openUserGuide(0)">
|
||||||
<CircleHelp class="h-4 w-4 mr-1" />
|
<CircleHelp class="h-4 w-4 mr-1" />
|
||||||
使用引导
|
使用引导
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<AlertDialogRoot>
|
<AlertDialogRoot :open="resetConfirmOpen" @update:open="handleResetConfirmOpenChange">
|
||||||
<AlertDialogTrigger as-child>
|
<AlertDialogTrigger as-child>
|
||||||
<Button variant="destructive" size="sm"
|
<Button variant="destructive" size="sm"
|
||||||
class="h-9 min-h-9 shrink-0 px-3 py-0 text-sm leading-none cursor-pointer">
|
class="app-toolbar-btn shrink-0 cursor-pointer"
|
||||||
|
:disabled="isResetting">
|
||||||
<RotateCcw class="h-4 w-4 mr-1" />
|
<RotateCcw class="h-4 w-4 mr-1" />
|
||||||
重置
|
重置
|
||||||
</Button>
|
</Button>
|
||||||
@ -1978,12 +1991,11 @@ watch(
|
|||||||
将清空所有项目数据,并恢复默认页面,确认继续吗?
|
将清空所有项目数据,并恢复默认页面,确认继续吗?
|
||||||
</AlertDialogDescription>
|
</AlertDialogDescription>
|
||||||
<div class="mt-4 flex items-center justify-end gap-2">
|
<div class="mt-4 flex items-center justify-end gap-2">
|
||||||
<AlertDialogCancel as-child>
|
<Button variant="outline" :disabled="isResetting" @click="resetConfirmOpen = false">取消</Button>
|
||||||
<Button variant="outline">取消</Button>
|
<Button variant="destructive" :disabled="isResetting" @click="handleReset">
|
||||||
</AlertDialogCancel>
|
<Loader2 v-if="isResetting" class="mr-1 h-4 w-4 animate-spin" />
|
||||||
<AlertDialogAction as-child>
|
{{ isResetting ? '重置中...' : '确认重置' }}
|
||||||
<Button variant="destructive" @click="handleReset">确认重置</Button>
|
</Button>
|
||||||
</AlertDialogAction>
|
|
||||||
</div>
|
</div>
|
||||||
</AlertDialogContent>
|
</AlertDialogContent>
|
||||||
</AlertDialogPortal>
|
</AlertDialogPortal>
|
||||||
@ -2012,6 +2024,7 @@ watch(
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
<div v-if="isResetting" class="fixed inset-0 z-40 cursor-wait bg-transparent" />
|
||||||
|
|
||||||
<div class="flex-1 overflow-auto relative">
|
<div class="flex-1 overflow-auto relative">
|
||||||
<div v-for="tab in tabStore.tabs" :key="tab.id" :ref="el => setTabPanelRef(tab.id, el)"
|
<div v-for="tab in tabStore.tabs" :key="tab.id" :ref="el => setTabPanelRef(tab.id, el)"
|
||||||
|
|||||||
@ -73,6 +73,13 @@ const activeComponent = computed(() => {
|
|||||||
return selected?.component || props.categories[0]?.component || null
|
return selected?.component || props.categories[0]?.component || null
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const sideWidthStyle = computed(() => ({ width: 'var(--app-typeline-side-w)' }))
|
||||||
|
const itemGapStyle = computed(() => ({ gap: 'var(--app-typeline-gap)' }))
|
||||||
|
const dotStyle = computed(() => ({ width: 'var(--app-typeline-dot)', height: 'var(--app-typeline-dot)' }))
|
||||||
|
const dotInnerStyle = computed(() => ({ width: 'var(--app-typeline-dot-inner)', height: 'var(--app-typeline-dot-inner)' }))
|
||||||
|
const labelStyle = computed(() => ({ fontSize: 'var(--app-typeline-label-font)', lineHeight: 'var(--app-typeline-label-line)' }))
|
||||||
|
const lineStyle = computed(() => ({ left: 'var(--app-typeline-line-left)' }))
|
||||||
|
|
||||||
const copyBtnText = ref('复制')
|
const copyBtnText = ref('复制')
|
||||||
const sheetOpen = ref(false)
|
const sheetOpen = ref(false)
|
||||||
|
|
||||||
@ -199,7 +206,7 @@ useMotionValueEvent(
|
|||||||
<template>
|
<template>
|
||||||
<TooltipProvider>
|
<TooltipProvider>
|
||||||
<div class="flex h-full w-full bg-background p-2">
|
<div class="flex h-full w-full bg-background p-2">
|
||||||
<div class="w-[200px] shrink-0 border-r px-4 py-3 flex flex-col gap-6 relative ">
|
<div :style="sideWidthStyle" class="shrink-0 border-r px-4 py-3 flex flex-col gap-6 relative">
|
||||||
<div v-if="props.title || props.subtitle || props.metaText" class="space-y-1">
|
<div v-if="props.title || props.subtitle || props.metaText" class="space-y-1">
|
||||||
<TooltipRoot>
|
<TooltipRoot>
|
||||||
<TooltipTrigger as-child>
|
<TooltipTrigger as-child>
|
||||||
@ -228,25 +235,25 @@ useMotionValueEvent(
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div :class="['flex flex-col gap-6 relative', (props.title || props.subtitle || props.metaText) ? 'mt-4' : '']">
|
<div :class="['flex flex-col gap-6 relative ', (props.title || props.subtitle || props.metaText) ? 'mt-3' : 'mt-6']">
|
||||||
<div class="absolute left-[9px] top-3 bottom-3 w-[1.5px] bg-border/60"></div>
|
<div :style="lineStyle" class="absolute top-3 bottom-3 w-[1.5px] bg-border/60"></div>
|
||||||
|
|
||||||
<div v-for="item in props.categories" :key="item.key"
|
<div v-for="item in props.categories" :key="item.key"
|
||||||
class="relative flex items-center gap-3 cursor-pointer group" @click="switchCategory(item.key)">
|
:style="itemGapStyle" class="relative flex items-center cursor-pointer group" @click="switchCategory(item.key)">
|
||||||
<div :class="[
|
<div :class="[
|
||||||
'z-10 w-5 h-5 rounded-full border-2 flex items-center justify-center transition-all duration-200',
|
'z-10 rounded-full border-2 flex items-center justify-center transition-all duration-200',
|
||||||
activeCategory === item.key
|
activeCategory === item.key
|
||||||
? 'bg-primary border-primary shadow-[0_0_0_3px_rgba(var(--primary),0.15)]'
|
? 'bg-primary border-primary shadow-[0_0_0_3px_rgba(var(--primary),0.15)]'
|
||||||
: 'bg-background border-muted-foreground/40 group-hover:border-muted-foreground/70'
|
: 'bg-background border-muted-foreground/40 group-hover:border-muted-foreground/70'
|
||||||
]">
|
]" :style="dotStyle">
|
||||||
<div v-if="activeCategory === item.key" class="w-1.5 h-1.5 bg-background rounded-full"></div>
|
<div v-if="activeCategory === item.key" class="bg-background rounded-full" :style="dotInnerStyle"></div>
|
||||||
</div>
|
</div>
|
||||||
<span :class="[
|
<span :class="[
|
||||||
'text-[12px] leading-4 transition-colors duration-200',
|
'transition-colors duration-200',
|
||||||
activeCategory === item.key
|
activeCategory === item.key
|
||||||
? 'font-semibold text-primary'
|
? 'font-semibold text-primary'
|
||||||
: 'text-muted-foreground group-hover:text-foreground'
|
: 'text-muted-foreground group-hover:text-foreground'
|
||||||
]">
|
]" :style="labelStyle">
|
||||||
{{ item.label }}
|
{{ item.label }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -62,7 +62,11 @@ export const gridOptions: GridOptions = {
|
|||||||
sortable: false,
|
sortable: false,
|
||||||
filter: false,
|
filter: false,
|
||||||
wrapHeaderText: true,
|
wrapHeaderText: true,
|
||||||
autoHeaderHeight: true
|
autoHeaderHeight: true,
|
||||||
|
// 默认把数值型单元格右对齐,减少每个列重复配置。
|
||||||
|
cellClassRules: {
|
||||||
|
'ag-right-aligned-cell': params => typeof params.value === 'number' && Number.isFinite(params.value)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
defaultColGroupDef: {
|
defaultColGroupDef: {
|
||||||
wrapHeaderText: true,
|
wrapHeaderText: true,
|
||||||
|
|||||||
@ -14,7 +14,7 @@ import {
|
|||||||
|
|
||||||
} from 'ag-grid-community'
|
} from 'ag-grid-community'
|
||||||
import {
|
import {
|
||||||
AggregationModule,
|
AggregationModule,ServerSideRowModelApiModule ,
|
||||||
CellSelectionModule,
|
CellSelectionModule,
|
||||||
ClipboardModule,
|
ClipboardModule,
|
||||||
LicenseManager,
|
LicenseManager,
|
||||||
@ -47,7 +47,7 @@ const AG_GRID_MODULES = [
|
|||||||
RowGroupingModule,
|
RowGroupingModule,
|
||||||
CellSelectionModule,
|
CellSelectionModule,
|
||||||
ClipboardModule,
|
ClipboardModule,
|
||||||
LocaleModule,ValidationModule ,CellSpanModule ,RowStyleModule ,RowSelectionModule
|
LocaleModule,ValidationModule ,CellSpanModule ,RowStyleModule ,RowSelectionModule ,ServerSideRowModelApiModule
|
||||||
]
|
]
|
||||||
|
|
||||||
const pinia = createPinia()
|
const pinia = createPinia()
|
||||||
|
|||||||
176
src/style.css
176
src/style.css
@ -50,6 +50,19 @@ html {
|
|||||||
}
|
}
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
|
--app-toolbar-btn-h: 2.25rem;
|
||||||
|
--app-toolbar-btn-px: 0.75rem;
|
||||||
|
--app-toolbar-btn-font: 0.875rem;
|
||||||
|
--app-grid-header-h: 3rem;
|
||||||
|
--app-grid-row-h: 2.25rem;
|
||||||
|
--app-grid-font-size: 0.875rem;
|
||||||
|
--app-typeline-side-w: 12.5rem;
|
||||||
|
--app-typeline-gap: 0.75rem;
|
||||||
|
--app-typeline-label-font: 0.8125rem;
|
||||||
|
--app-typeline-label-line: 1rem;
|
||||||
|
--app-typeline-dot: 1.25rem;
|
||||||
|
--app-typeline-dot-inner: 0.375rem;
|
||||||
|
--app-typeline-line-left: 0.5625rem;
|
||||||
--radius: 0.625rem;
|
--radius: 0.625rem;
|
||||||
--background: oklch(1 0 0);
|
--background: oklch(1 0 0);
|
||||||
--foreground: oklch(0.145 0 0);
|
--foreground: oklch(0.145 0 0);
|
||||||
@ -143,6 +156,43 @@ html {
|
|||||||
overflow-y: scroll !important;
|
overflow-y: scroll !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Numeric-like inputs: align right in forms. */
|
||||||
|
input[type='number'],
|
||||||
|
input[inputmode='decimal'],
|
||||||
|
input[inputmode='numeric'] {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Keep AG Grid numeric cells right aligned even with custom flex cell layout. */
|
||||||
|
.ag-theme-quartz .ag-cell.ag-right-aligned-cell {
|
||||||
|
justify-content: flex-end;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ag-theme-quartz .ag-cell.ag-right-aligned-cell .ag-cell-wrapper {
|
||||||
|
width: 100%;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ag-theme-quartz .ag-cell.ag-right-aligned-cell .ag-cell-value {
|
||||||
|
width: 100%;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-toolbar-btn {
|
||||||
|
height: var(--app-toolbar-btn-h) !important;
|
||||||
|
min-height: var(--app-toolbar-btn-h) !important;
|
||||||
|
padding-left: var(--app-toolbar-btn-px) !important;
|
||||||
|
padding-right: var(--app-toolbar-btn-px) !important;
|
||||||
|
font-size: var(--app-toolbar-btn-font) !important;
|
||||||
|
line-height: 1 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ag-theme-quartz {
|
||||||
|
--ag-font-size: var(--app-grid-font-size);
|
||||||
|
--ag-row-height: var(--app-grid-row-h);
|
||||||
|
}
|
||||||
|
|
||||||
/* When one column uses auto-height rows, keep other columns vertically centered. */
|
/* When one column uses auto-height rows, keep other columns vertically centered. */
|
||||||
/* .xmMx .ag-row .ag-cell-wrapper {
|
/* .xmMx .ag-row .ag-cell-wrapper {
|
||||||
|
|
||||||
@ -308,9 +358,129 @@ html {
|
|||||||
font-style: italic;
|
font-style: italic;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Laptop screens often need a slightly denser desktop layout. */
|
/* Web adaptive typography baseline: tablet / laptop / 1080p / 2K / 4K */
|
||||||
@media (max-width: 1536px) {
|
@media (max-width: 1024px) {
|
||||||
html {
|
html {
|
||||||
font-size: 14.4px;
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
:root {
|
||||||
|
--app-toolbar-btn-h: 2.125rem;
|
||||||
|
--app-toolbar-btn-px: 0.625rem;
|
||||||
|
--app-toolbar-btn-font: 0.8125rem;
|
||||||
|
--app-grid-header-h: 2.5rem;
|
||||||
|
--app-grid-row-h: 2.125rem;
|
||||||
|
--app-grid-font-size: 0.8125rem;
|
||||||
|
--app-typeline-side-w: 11.5rem;
|
||||||
|
--app-typeline-gap: 0.625rem;
|
||||||
|
--app-typeline-label-font: 0.75rem;
|
||||||
|
--app-typeline-label-line: 0.95rem;
|
||||||
|
--app-typeline-dot: 1.125rem;
|
||||||
|
--app-typeline-dot-inner: 0.3125rem;
|
||||||
|
--app-typeline-line-left: 0.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 1025px) and (max-width: 1440px) {
|
||||||
|
html {
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
:root {
|
||||||
|
--app-toolbar-btn-h: 2.25rem;
|
||||||
|
--app-toolbar-btn-px: 0.75rem;
|
||||||
|
--app-toolbar-btn-font: 0.875rem;
|
||||||
|
--app-grid-header-h: 2.75rem;
|
||||||
|
--app-grid-row-h: 2.25rem;
|
||||||
|
--app-grid-font-size: 0.875rem;
|
||||||
|
--app-typeline-side-w: 12rem;
|
||||||
|
--app-typeline-gap: 0.6875rem;
|
||||||
|
--app-typeline-label-font: 0.8125rem;
|
||||||
|
--app-typeline-label-line: 1rem;
|
||||||
|
--app-typeline-dot: 1.1875rem;
|
||||||
|
--app-typeline-dot-inner: 0.375rem;
|
||||||
|
--app-typeline-line-left: 0.5625rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 1441px) and (max-width: 1919px) {
|
||||||
|
html {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
:root {
|
||||||
|
--app-toolbar-btn-h: 2.25rem;
|
||||||
|
--app-toolbar-btn-px: 0.75rem;
|
||||||
|
--app-toolbar-btn-font: 0.875rem;
|
||||||
|
--app-grid-header-h: 3rem;
|
||||||
|
--app-grid-row-h: 2.25rem;
|
||||||
|
--app-grid-font-size: 0.875rem;
|
||||||
|
--app-typeline-side-w: 12.5rem;
|
||||||
|
--app-typeline-gap: 0.75rem;
|
||||||
|
--app-typeline-label-font: 0.875rem;
|
||||||
|
--app-typeline-label-line: 1.1rem;
|
||||||
|
--app-typeline-dot: 1.25rem;
|
||||||
|
--app-typeline-dot-inner: 0.4375rem;
|
||||||
|
--app-typeline-line-left: 0.625rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 1920px) and (max-width: 2559px) {
|
||||||
|
html {
|
||||||
|
font-size: 17px;
|
||||||
|
}
|
||||||
|
:root {
|
||||||
|
--app-toolbar-btn-h: 2.5rem;
|
||||||
|
--app-toolbar-btn-px: 0.875rem;
|
||||||
|
--app-toolbar-btn-font: 0.9375rem;
|
||||||
|
--app-grid-header-h: 3.125rem;
|
||||||
|
--app-grid-row-h: 2.5rem;
|
||||||
|
--app-grid-font-size: 0.9375rem;
|
||||||
|
--app-typeline-side-w: 13rem;
|
||||||
|
--app-typeline-gap: 0.8125rem;
|
||||||
|
--app-typeline-label-font: 0.9375rem;
|
||||||
|
--app-typeline-label-line: 1.2rem;
|
||||||
|
--app-typeline-dot: 1.375rem;
|
||||||
|
--app-typeline-dot-inner: 0.5rem;
|
||||||
|
--app-typeline-line-left: 0.6875rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 2560px) and (max-width: 3839px) {
|
||||||
|
html {
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
:root {
|
||||||
|
--app-toolbar-btn-h: 2.625rem;
|
||||||
|
--app-toolbar-btn-px: 1rem;
|
||||||
|
--app-toolbar-btn-font: 1rem;
|
||||||
|
--app-grid-header-h: 3.25rem;
|
||||||
|
--app-grid-row-h: 2.625rem;
|
||||||
|
--app-grid-font-size: 1rem;
|
||||||
|
--app-typeline-side-w: 13.5rem;
|
||||||
|
--app-typeline-gap: 0.875rem;
|
||||||
|
--app-typeline-label-font: 1rem;
|
||||||
|
--app-typeline-label-line: 1.25rem;
|
||||||
|
--app-typeline-dot: 1.5rem;
|
||||||
|
--app-typeline-dot-inner: 0.5625rem;
|
||||||
|
--app-typeline-line-left: 0.75rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 3840px) {
|
||||||
|
html {
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
:root {
|
||||||
|
--app-toolbar-btn-h: 2.875rem;
|
||||||
|
--app-toolbar-btn-px: 1.125rem;
|
||||||
|
--app-toolbar-btn-font: 1.0625rem;
|
||||||
|
--app-grid-header-h: 3.5rem;
|
||||||
|
--app-grid-row-h: 2.875rem;
|
||||||
|
--app-grid-font-size: 1.0625rem;
|
||||||
|
--app-typeline-side-w: 14.5rem;
|
||||||
|
--app-typeline-gap: 1rem;
|
||||||
|
--app-typeline-label-font: 1.0625rem;
|
||||||
|
--app-typeline-label-line: 1.35rem;
|
||||||
|
--app-typeline-dot: 1.625rem;
|
||||||
|
--app-typeline-dot-inner: 0.625rem;
|
||||||
|
--app-typeline-line-left: 0.8125rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user