优化目录

This commit is contained in:
zwgczx committed 2026-03-17 12:05:22 +08:00
1 parent 4e343cccf1
commit 2b18fe9991
46 files changed
+1061 -1308

No files matched your search

+151 -107
View File
@@ -4,7 +4,15 @@ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/com
import { Button } from '@/components/ui/button'
import { useTabStore } from '@/pinia/tab'
import { useKvStore } from '@/pinia/kv'
import { Calculator, Check, ChevronDown, Download, FolderKanban, X, Zap } from 'lucide-vue-next'
import {
Calculator,
Check,
ChevronDown,
Download,
FolderKanban,
X,
Zap
} from 'lucide-vue-next'
import { industryTypeList } from '@/sql'
import { initializeProjectFactorStates } from '@/lib/projectWorkspace'
import {
@@ -57,7 +65,7 @@ interface ProjectInfoState {
const PROJECT_INFO_KEY = 'xm-base-info-v1'
const PROJECT_CONSULT_CATEGORY_FACTOR_KEY = 'xm-consult-category-factor-v1'
const PROJECT_MAJOR_FACTOR_KEY = 'xm-major-factor-v1'
const DEFAULT_PROJECT_NAME = 'xxx造价咨询服务'
const DEFAULT_PROJECT_NAME = 'xxx 造价咨询服务'
const PROJECT_INIT_CHANGED_EVENT = 'xm-project-init-changed'
const tabStore = useTabStore()
@@ -69,6 +77,10 @@ const quickDialogOpen = ref(false)
const quickIndustry = ref(String(industryTypeList[0]?.id || ''))
const quickContractName = ref(QUICK_CONTRACT_FALLBACK_NAME)
const quickSubmitting = ref(false)
const homeImportInputRef = ref<HTMLInputElement | null>(null)
const projectIconAvailable = ref(false)
const quickIconAvailable = ref(false)
const importIconAvailable = ref(false)
const getTodayDateString = () => {
const now = new Date()
@@ -83,8 +95,9 @@ const enterProjectCalc = () => {
tabStore.enterWorkspace({
id: PROJECT_TAB_ID,
title: '项目计算',
componentName: PROJECT_TAB_ID
componentName: 'XmView'
})
tabStore.hasCompletedSetup = true
}
const loadProjectDefaults = async () => {
@@ -187,7 +200,6 @@ const confirmQuickCalc = async () => {
QUICK_MAJOR_FACTOR_KEY
)
writeWorkspaceMode('quick')
tabStore.enterWorkspace({
id: `contract-${QUICK_CONTRACT_ID}`,
title: contractName,
@@ -201,6 +213,7 @@ const confirmQuickCalc = async () => {
projectMajorFactorKey: QUICK_MAJOR_FACTOR_KEY
}
})
tabStore.hasCompletedSetup = true
} finally {
quickSubmitting.value = false
quickDialogOpen.value = false
@@ -219,6 +232,10 @@ const handleHomeImportChange = (event: Event) => {
input.value = ''
}
const openHomeImport = () => {
homeImportInputRef.value?.click()
}
onMounted(() => {
void loadProjectDefaults()
void loadQuickDefaults()
@@ -226,104 +243,133 @@ onMounted(() => {
</script>
<template>
<input id="home-import-input" type="file" accept=".zw" class="sr-only" @change="handleHomeImportChange" />
<div class="flex min-h-full items-center justify-center px-4 py-8">
<div class="w-full max-w-5xl">
<div class="mx-auto max-w-2xl text-center">
<p class="text-sm font-medium tracking-[0.28em] text-muted-foreground">JGJS 2026</p>
<h1 class="mt-4 text-4xl font-semibold tracking-tight text-foreground">选择计算入口</h1>
<p class="mt-3 text-sm leading-6 text-muted-foreground">
首页支持三种入口:继续项目计算、快速单合同测算,或直接导入已有数据包。
</p>
</div>
<div class="mt-10 grid items-stretch gap-5 md:grid-cols-3">
<Card
role="button"
tabindex="0"
class="flex h-full cursor-pointer flex-col border-border/70 bg-card/90 shadow-[0_18px_48px_rgba(15,23,42,0.08)] transition hover:-translate-y-0.5 hover:shadow-[0_22px_56px_rgba(15,23,42,0.12)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
@click="openProjectCalc"
@keydown.enter.prevent="openProjectCalc"
@keydown.space.prevent="openProjectCalc"
<input ref="homeImportInputRef" type="file" accept=".zw" class="sr-only" @change="handleHomeImportChange" />
<div class="flex min-h-full items-center justify-center px-4 py-8 lg:py-10">
<div class="w-full max-w-[1240px]">
<div class="grid items-stretch gap-6 lg:grid-cols-[320px_1fr]">
<div
class="relative overflow-hidden rounded-2xl bg-[linear-gradient(145deg,#041c5f_0%,#0b2e86_50%,#354cc7_100%)] p-8 text-white shadow-[0_24px_60px_rgba(11,46,134,0.45)]"
>
<CardHeader class="flex min-h-[184px] flex-col space-y-4 pb-4">
<div class="inline-flex h-12 w-12 items-center justify-center rounded-2xl bg-slate-900 text-white">
<FolderKanban class="h-6 w-6" />
</div>
<div class="min-h-[88px]">
<CardTitle class="text-2xl">项目计算</CardTitle>
<CardDescription class="mt-2 text-sm leading-6">
继续使用当前项目卡片、流程线、合同段管理和整项目导入导出能力。
</CardDescription>
</div>
</CardHeader>
<CardContent class="mt-auto flex flex-1 flex-col justify-end space-y-4">
<div class="flex min-h-[72px] items-center rounded-2xl border border-border/60 bg-muted/35 p-4 text-sm text-muted-foreground">
适合多合同段、项目级系数维护、整项目报表和批量导入导出。
</div>
<Button class="w-full justify-between" @click.stop="openProjectCalc">
进入项目计算
<Calculator class="h-4 w-4" />
</Button>
</CardContent>
</Card>
<div class="pointer-events-none absolute -right-20 -top-16 h-56 w-56 rounded-full bg-white/12 blur-2xl" />
<div
class="pointer-events-none absolute inset-0 bg-[repeating-linear-gradient(140deg,rgba(255,255,255,0.06)_0,rgba(255,255,255,0.06)_1px,transparent_1px,transparent_20px)]"
/>
<div class="relative inline-flex h-12 w-12 items-center justify-center rounded-xl bg-white/15 ring-1 ring-white/35">
<Calculator class="h-6 w-6" />
</div>
<h2 class="relative mt-10 text-3xl font-semibold leading-tight tracking-tight">智能预算一键生成-></h2>
<p class="relative mt-3 text-base text-blue-100">助力《规范》高效落地</p>
</div>
<Card
role="button"
tabindex="0"
class="flex h-full cursor-pointer flex-col border-border/70 bg-[linear-gradient(135deg,rgba(15,23,42,0.05),rgba(148,163,184,0.12))] shadow-[0_18px_48px_rgba(15,23,42,0.08)] transition hover:-translate-y-0.5 hover:shadow-[0_22px_56px_rgba(15,23,42,0.12)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
@click="openQuickCalcDialog"
@keydown.enter.prevent="openQuickCalcDialog"
@keydown.space.prevent="openQuickCalcDialog"
>
<CardHeader class="flex min-h-[184px] flex-col space-y-4 pb-4">
<div class="inline-flex h-12 w-12 items-center justify-center rounded-2xl bg-amber-500 text-slate-950">
<Zap class="h-6 w-6" />
</div>
<div class="min-h-[88px]">
<CardTitle class="text-2xl">快速计算</CardTitle>
<CardDescription class="mt-2 text-sm leading-6">
先填写工程行业和合同名称,再直接进入单合同预算费用测算页面。
</CardDescription>
</div>
</CardHeader>
<CardContent class="mt-auto flex flex-1 flex-col justify-end space-y-4">
<div class="flex min-h-[72px] items-center rounded-2xl border border-border/60 bg-background/80 p-4 text-sm text-muted-foreground">
适合快速试算、单合同预算复核和不需要项目级合同段管理的场景。
</div>
<Button variant="outline" class="w-full justify-between" @click.stop="openQuickCalcDialog">
进入快速计算
<Zap class="h-4 w-4" />
</Button>
</CardContent>
</Card>
<div class="flex flex-col justify-center">
<div class="text-center">
<h1 class="text-3xl font-semibold tracking-tight text-slate-900">计算入口</h1>
<p class="mt-2 text-sm text-slate-600">支持三种计算方式:项目计算 · 单项速算 · 导入数据</p>
</div>
<div class="mt-6 grid gap-4 md:grid-cols-2 xl:grid-cols-3">
<Card
role="button"
tabindex="0"
class="group flex cursor-pointer flex-col rounded-2xl border border-slate-200/80 bg-white/95 p-5 shadow-[0_10px_30px_rgba(15,23,42,0.08)] backdrop-blur-sm transition-all duration-200 hover:-translate-y-1 hover:shadow-[0_20px_45px_rgba(15,23,42,0.16)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-200"
@click="openProjectCalc"
@keydown.enter.prevent="openProjectCalc"
@keydown.space.prevent="openProjectCalc"
>
<CardHeader class="space-y-3 p-0">
<div>
<CardTitle class="text-2xl font-semibold text-slate-900">项目预算</CardTitle>
<CardDescription class="mt-2 text-sm leading-6 text-slate-500">
适用:多合同段、项目级整体计算
<br>
操作:支持当前项目及合同段卡片和流程线,可导出/导入完整项目数据
</CardDescription>
</div>
</CardHeader>
<CardContent class="mt-auto flex flex-col items-center p-0 pt-8">
<div
class="mt-4 inline-flex h-16 w-16 items-center justify-center rounded-full border border-blue-100 bg-[radial-gradient(circle_at_30%_30%,#ffffff,#eef4ff)] shadow-[0_10px_24px_rgba(27,76,176,0.18)]"
>
<img
v-if="projectIconAvailable"
:src="'/image_0.png'"
alt="项目预算"
class="h-8 w-8 object-contain"
@error="projectIconAvailable = false"
>
<FolderKanban v-else class="h-7 w-7 text-blue-700" />
</div>
</CardContent>
</Card>
<label for="home-import-input" class="block h-full cursor-pointer">
<Card
class="flex h-full flex-col border-border/70 bg-[linear-gradient(135deg,rgba(14,116,144,0.08),rgba(186,230,253,0.26))] shadow-[0_18px_48px_rgba(15,23,42,0.08)] transition hover:-translate-y-0.5 hover:shadow-[0_22px_56px_rgba(15,23,42,0.12)]"
>
<CardHeader class="flex min-h-[184px] flex-col space-y-4 pb-4">
<div class="inline-flex h-12 w-12 items-center justify-center rounded-2xl bg-cyan-600 text-white">
<Download class="h-6 w-6" />
</div>
<div class="min-h-[88px]">
<CardTitle class="text-2xl">导入数据</CardTitle>
<CardDescription class="mt-2 text-sm leading-6">
直接导入已有 `.zw` 数据包,恢复项目计算或快速计算的本地工作区状态。
</CardDescription>
</div>
</CardHeader>
<CardContent class="mt-auto flex flex-1 flex-col justify-end space-y-4">
<div class="flex min-h-[72px] items-center rounded-2xl border border-border/60 bg-background/80 p-4 text-sm text-muted-foreground">
适合继续上次工作、切换设备恢复数据,或直接打开别人发来的测算包。
</div>
<div class="inline-flex h-10 w-full items-center justify-between rounded-md border border-input bg-secondary px-4 text-sm font-medium text-secondary-foreground shadow-xs transition-colors">
<span>选择导入文件</span>
<Download class="h-4 w-4" />
</div>
</CardContent>
</Card>
</label>
<Card
role="button"
tabindex="0"
class="group flex cursor-pointer flex-col rounded-2xl border border-slate-200/80 bg-white/95 p-5 shadow-[0_10px_30px_rgba(15,23,42,0.08)] backdrop-blur-sm transition-all duration-200 hover:-translate-y-1 hover:shadow-[0_20px_45px_rgba(15,23,42,0.16)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-200"
@click="openQuickCalcDialog"
@keydown.enter.prevent="openQuickCalcDialog"
@keydown.space.prevent="openQuickCalcDialog"
>
<CardHeader class="space-y-3 p-0">
<div>
<CardTitle class="text-2xl font-semibold text-slate-900">单项速算</CardTitle>
<CardDescription class="mt-2 text-sm leading-6 text-slate-500">
适用:单合同段预算、单项试算
<br>
操作:选择行业与咨询类型,输入基数,秒出结果
</CardDescription>
</div>
</CardHeader>
<CardContent class="mt-auto flex flex-col items-center p-0 pt-8">
<div
class="mt-4 inline-flex h-16 w-16 items-center justify-center rounded-full border border-blue-100 bg-[radial-gradient(circle_at_30%_30%,#ffffff,#eef4ff)] shadow-[0_10px_24px_rgba(27,76,176,0.18)]"
>
<img
v-if="quickIconAvailable"
:src="'/image_1.png'"
alt="单项速算"
class="h-8 w-8 object-contain"
@error="quickIconAvailable = false"
>
<Zap v-else class="h-7 w-7 text-amber-500" />
</div>
</CardContent>
</Card>
<Card
role="button"
tabindex="0"
class="group flex cursor-pointer flex-col rounded-2xl border border-slate-200/80 bg-white/95 p-5 shadow-[0_10px_30px_rgba(15,23,42,0.08)] backdrop-blur-sm transition-all duration-200 hover:-translate-y-1 hover:shadow-[0_20px_45px_rgba(15,23,42,0.16)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-200"
@click="openHomeImport"
@keydown.enter.prevent="openHomeImport"
@keydown.space.prevent="openHomeImport"
>
<CardHeader class="space-y-3 p-0">
<div>
<CardTitle class="text-2xl font-semibold text-slate-900">导入数据</CardTitle>
<CardDescription class="mt-2 text-sm leading-6 text-slate-500">
适用:续未完成工作、跨设备恢复数据
<br>
操作:导入".zw"数据包,快速恢复项目计算状态
</CardDescription>
</div>
</CardHeader>
<CardContent class="mt-auto flex flex-col items-center p-0 pt-8">
<div
class="mt-4 inline-flex h-16 w-16 items-center justify-center rounded-full border border-blue-100 bg-[radial-gradient(circle_at_30%_30%,#ffffff,#eef4ff)] shadow-[0_10px_24px_rgba(27,76,176,0.18)]"
>
<img
v-if="importIconAvailable"
:src="'/image_2.png'"
alt="导入数据"
class="h-8 w-8 object-contain"
@error="importIconAvailable = false"
>
<Download v-else class="h-7 w-7 text-cyan-600" />
</div>
</CardContent>
</Card>
</div>
</div>
</div>
</div>
</div>
@@ -398,8 +444,8 @@ onMounted(() => {
<div class="w-full max-w-md rounded-xl border bg-background shadow-2xl">
<div class="flex items-center justify-between border-b px-5 py-4">
<div>
<h3 class="text-base font-semibold text-foreground">快速计算</h3>
<p class="mt-1 text-sm text-muted-foreground">填写工程行业和合同名称后,直接进入单合同计算页面。</p>
<h3 class="text-base font-semibold text-foreground">单项速算</h3>
<p class="mt-1 text-sm text-muted-foreground">输入合同名称后,进入单项速算页面。</p>
</div>
<Button variant="ghost" size="icon" class="h-8 w-8" @click="closeQuickCalcDialog">
<X class="h-4 w-4" />
@@ -447,18 +493,16 @@ onMounted(() => {
<input
v-model="quickContractName"
type="text"
maxlength="40"
class="inline-flex h-11 w-full items-center justify-between rounded-xl border border-border/70 bg-[linear-gradient(180deg,rgba(248,250,252,0.98),rgba(241,245,249,0.92))] px-4 text-sm text-foreground shadow-sm outline-none transition placeholder:text-slate-400 hover:border-border focus-visible:ring-2 focus-visible:ring-slate-300"
placeholder="请输入合同名称"
class="h-10 w-full rounded-md border bg-background px-3 text-sm outline-none transition focus-visible:ring-2 focus-visible:ring-ring"
@keydown.enter="confirmQuickCalc"
/>
>
</label>
</div>
<div class="flex items-center justify-end gap-2 border-t px-5 py-4">
<Button variant="outline" @click="closeQuickCalcDialog">取消</Button>
<Button :disabled="quickSubmitting || !quickIndustry || !quickContractName.trim()" @click="confirmQuickCalc">
{{ quickSubmitting ? '进入中...' : '进入快速计算' }}
<Button :disabled="quickSubmitting || !quickContractName" @click="confirmQuickCalc">
{{ quickSubmitting ? '进入中...' : '进入计算' }}
</Button>
</div>
</div>