1
This commit is contained in:
parent
b1728bbc47
commit
bbcd07a595
@ -18,7 +18,7 @@ import {
|
||||
} from '@/lib/workspace'
|
||||
import { collectActiveProjectSessionLocks, initProjectSessionLock } from '@/lib/projectSessionLock'
|
||||
import { listenProjectDeleted, listenResetAll } from '@/lib/projectEvents'
|
||||
import { createProject, listProjects, type ProjectMeta } from '@/lib/projectRegistry'
|
||||
import { listProjects, type ProjectMeta } from '@/lib/projectRegistry'
|
||||
|
||||
const tabStore = useTabStore()
|
||||
const { t } = useI18n()
|
||||
@ -130,9 +130,8 @@ const openProjectInNewTab = (projectId: string, options?: { newProject?: boolean
|
||||
}
|
||||
|
||||
const createProjectAndOpen = () => {
|
||||
const project = createProject(t('xmInfo.defaultProjectName'))
|
||||
refreshConflictProjectList()
|
||||
openProjectInNewTab(project.id, { newProject: true })
|
||||
openProjectInNewTab(DEFAULT_PROJECT_ID, { newProject: true })
|
||||
}
|
||||
|
||||
const syncRouteRequestFlags = () => {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { computed, onBeforeUnmount, onMounted, ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { Card, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { Button } from '@/components/ui/button'
|
||||
@ -47,6 +47,7 @@ import {
|
||||
} from '@/lib/workspace'
|
||||
import { createProject, listProjects, upsertProject } from '@/lib/projectRegistry'
|
||||
import { createProjectKvAdapter } from '@/lib/projectKvStore'
|
||||
import { collectActiveProjectSessionLocks } from '@/lib/projectSessionLock'
|
||||
|
||||
interface QuickProjectInfoState {
|
||||
projectIndustry?: string
|
||||
@ -75,7 +76,6 @@ 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 PROJECT_INIT_CHANGED_EVENT = 'xm-project-init-changed'
|
||||
const getActiveProjectId = () => readCurrentProjectId()
|
||||
|
||||
const tabStore = useTabStore()
|
||||
@ -96,6 +96,8 @@ const existingProjectDialogOpen = ref(false)
|
||||
const existingProjects = ref<Array<{ id: string; name: string; updatedAt: string }>>([])
|
||||
const existingProjectLoading = ref(false)
|
||||
const hasExistingProjects = ref(false)
|
||||
const openedProjectIds = ref<string[]>([])
|
||||
let existingProjectPollTimer: ReturnType<typeof setInterval> | null = null
|
||||
const projectIndustryLabel = computed(() => {
|
||||
const target = String(projectIndustry.value || '').trim()
|
||||
if (!target) return ''
|
||||
@ -175,8 +177,19 @@ const openProjectCalc = async () => {
|
||||
projectDialogOpen.value = true
|
||||
}
|
||||
|
||||
const refreshExistingProjects = async () => {
|
||||
existingProjectLoading.value = true
|
||||
const syncExistingProjectOpenedState = (projectIds: string[]) => {
|
||||
openedProjectIds.value = Array.from(collectActiveProjectSessionLocks(projectIds))
|
||||
}
|
||||
|
||||
const isExistingProjectOpened = (projectIdRaw: string) => {
|
||||
const projectId = String(projectIdRaw || '').trim()
|
||||
return projectId ? openedProjectIds.value.includes(projectId) : false
|
||||
}
|
||||
|
||||
const refreshExistingProjects = async (options?: { showLoading?: boolean }) => {
|
||||
if (options?.showLoading !== false) {
|
||||
existingProjectLoading.value = true
|
||||
}
|
||||
try {
|
||||
const projects = listProjects()
|
||||
.filter(item => item.id !== QUICK_PROJECT_ID)
|
||||
@ -191,23 +204,42 @@ const refreshExistingProjects = async () => {
|
||||
updatedAt: project.updatedAt
|
||||
}))
|
||||
hasExistingProjects.value = projects.length > 0
|
||||
syncExistingProjectOpenedState(projects.map(project => project.id))
|
||||
} finally {
|
||||
existingProjectLoading.value = false
|
||||
if (options?.showLoading !== false) {
|
||||
existingProjectLoading.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const stopExistingProjectPolling = () => {
|
||||
if (existingProjectPollTimer == null) return
|
||||
clearInterval(existingProjectPollTimer)
|
||||
existingProjectPollTimer = null
|
||||
}
|
||||
|
||||
const startExistingProjectPolling = () => {
|
||||
stopExistingProjectPolling()
|
||||
existingProjectPollTimer = setInterval(() => {
|
||||
if (!existingProjectDialogOpen.value) return
|
||||
void refreshExistingProjects({ showLoading: false })
|
||||
}, 3000)
|
||||
}
|
||||
|
||||
const openExistingProjectDialog = async () => {
|
||||
existingProjectDialogOpen.value = true
|
||||
await refreshExistingProjects()
|
||||
startExistingProjectPolling()
|
||||
}
|
||||
|
||||
const closeExistingProjectDialog = () => {
|
||||
existingProjectDialogOpen.value = false
|
||||
stopExistingProjectPolling()
|
||||
}
|
||||
|
||||
const enterExistingProject = (projectIdRaw: string) => {
|
||||
const projectId = String(projectIdRaw || '').trim()
|
||||
if (!projectId) return
|
||||
if (!projectId || isExistingProjectOpened(projectId)) return
|
||||
upsertProject(projectId, resolveProjectRegistryName(projectId))
|
||||
if (!navigateToWorkspace(projectId, 'project')) return
|
||||
tabStore.enterWorkspace({
|
||||
@ -229,29 +261,9 @@ const confirmProjectCalc = async () => {
|
||||
|
||||
projectSubmitting.value = true
|
||||
try {
|
||||
const activeProjectId = getActiveProjectId()
|
||||
if (activeProjectId === DEFAULT_PROJECT_ID) {
|
||||
const project = createProject(t('xmInfo.defaultProjectName'))
|
||||
const kvAdapter = createProjectKvAdapter(project.id)
|
||||
await kvAdapter.setItem<ProjectInfoState>(PROJECT_INFO_KEY, {
|
||||
projectIndustry: industry,
|
||||
projectName: t('xmInfo.defaultProjectName'),
|
||||
preparedBy: '',
|
||||
reviewedBy: '',
|
||||
preparedCompany: '',
|
||||
preparedDate: getTodayDateString()
|
||||
})
|
||||
await initializeProjectFactorStates(
|
||||
kvAdapter,
|
||||
industry,
|
||||
PROJECT_CONSULT_CATEGORY_FACTOR_KEY,
|
||||
PROJECT_MAJOR_FACTOR_KEY
|
||||
)
|
||||
writeWorkspaceMode('project')
|
||||
window.location.href = buildProjectUrl(project.id, { forceHome: false, newProject: false })
|
||||
return
|
||||
}
|
||||
await kvStore.setItem<ProjectInfoState>(PROJECT_INFO_KEY, {
|
||||
const project = createProject(t('xmInfo.defaultProjectName'))
|
||||
const kvAdapter = createProjectKvAdapter(project.id)
|
||||
await kvAdapter.setItem<ProjectInfoState>(PROJECT_INFO_KEY, {
|
||||
projectIndustry: industry,
|
||||
projectName: t('xmInfo.defaultProjectName'),
|
||||
preparedBy: '',
|
||||
@ -260,13 +272,13 @@ const confirmProjectCalc = async () => {
|
||||
preparedDate: getTodayDateString()
|
||||
})
|
||||
await initializeProjectFactorStates(
|
||||
kvStore,
|
||||
kvAdapter,
|
||||
industry,
|
||||
PROJECT_CONSULT_CATEGORY_FACTOR_KEY,
|
||||
PROJECT_MAJOR_FACTOR_KEY
|
||||
)
|
||||
window.dispatchEvent(new CustomEvent<boolean>(PROJECT_INIT_CHANGED_EVENT, { detail: true }))
|
||||
enterProjectCalc()
|
||||
writeWorkspaceMode('project')
|
||||
window.location.href = buildProjectUrl(project.id, { forceHome: false, newProject: false })
|
||||
} finally {
|
||||
projectSubmitting.value = false
|
||||
projectDialogOpen.value = false
|
||||
@ -369,10 +381,22 @@ const confirmHomeImport = async () => {
|
||||
cancelHomeImportConfirm()
|
||||
}
|
||||
|
||||
const handleHomeWindowFocus = () => {
|
||||
if (!existingProjectDialogOpen.value) return
|
||||
void refreshExistingProjects({ showLoading: false })
|
||||
}
|
||||
|
||||
const handleHomeVisibilityChange = () => {
|
||||
if (document.visibilityState !== 'visible') return
|
||||
handleHomeWindowFocus()
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
void refreshExistingProjects()
|
||||
void loadProjectDefaults()
|
||||
void loadQuickDefaults()
|
||||
window.addEventListener('focus', handleHomeWindowFocus)
|
||||
document.addEventListener('visibilitychange', handleHomeVisibilityChange)
|
||||
try {
|
||||
const url = new URL(window.location.href)
|
||||
const isNewProject = url.searchParams.get(NEW_PROJECT_QUERY_KEY) === '1'
|
||||
@ -392,6 +416,12 @@ onMounted(() => {
|
||||
// ignore url parsing errors
|
||||
}
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
stopExistingProjectPolling()
|
||||
window.removeEventListener('focus', handleHomeWindowFocus)
|
||||
document.removeEventListener('visibilitychange', handleHomeVisibilityChange)
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -580,11 +610,20 @@ onMounted(() => {
|
||||
v-for="project in existingProjects"
|
||||
:key="project.id"
|
||||
type="button"
|
||||
class="flex w-full cursor-pointer items-center justify-between rounded-lg border border-slate-200 px-3 py-2 text-left transition hover:border-slate-300 hover:bg-slate-50"
|
||||
:disabled="isExistingProjectOpened(project.id)"
|
||||
class="flex w-full items-center justify-between rounded-lg border border-slate-200 px-3 py-2 text-left transition"
|
||||
:class="isExistingProjectOpened(project.id)
|
||||
? 'cursor-not-allowed border-slate-200 bg-slate-100/80 opacity-70'
|
||||
: 'cursor-pointer hover:border-slate-300 hover:bg-slate-50'"
|
||||
@click="enterExistingProject(project.id)"
|
||||
>
|
||||
<div class="min-w-0">
|
||||
<div class="truncate text-sm font-medium text-slate-800">{{ project.name }}</div>
|
||||
<div class="truncate text-sm font-medium text-slate-800">
|
||||
{{ project.name }}
|
||||
<span v-if="isExistingProjectOpened(project.id)" class="ml-1 text-xs text-slate-500">
|
||||
{{ t('tab.toolbar.opened') }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="mt-0.5 text-xs text-slate-500">{{ project.id }}</div>
|
||||
</div>
|
||||
<div class="shrink-0 pl-2 text-xs text-slate-500">
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onActivated, onMounted, ref, watch } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { Check, ChevronDown, Circle, CircleDot } from 'lucide-vue-next'
|
||||
import { Circle, CircleDot } from 'lucide-vue-next'
|
||||
import {
|
||||
getQuickCalcGroups,
|
||||
getMajorDictItemById,
|
||||
@ -17,18 +17,6 @@ import { getIndustryMajorEntry } from '@/lib/pricingScaleCalc'
|
||||
import { getBenchmarkBudgetSplitByScale, getScaleBudgetFeeSplit } from '@/lib/pricingScaleFee'
|
||||
import { QUICK_PROJECT_INFO_KEY } from '@/lib/workspace'
|
||||
import { initializeProjectFactorStates } from '@/lib/projectWorkspace'
|
||||
import {
|
||||
SelectContent,
|
||||
SelectIcon,
|
||||
SelectItem,
|
||||
SelectItemIndicator,
|
||||
SelectItemText,
|
||||
SelectPortal,
|
||||
SelectRoot,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
SelectViewport
|
||||
} from 'reka-ui'
|
||||
|
||||
const props = defineProps<{
|
||||
contractId: string
|
||||
@ -140,6 +128,15 @@ const industryLabel = computed(() => {
|
||||
return getIndustryDisplayName(target, locale.value) || t('quickCalc.notSelected')
|
||||
})
|
||||
|
||||
const isIndustrySelected = (industryId: string | number) => {
|
||||
return projectIndustry.value.trim() === String(industryId).trim()
|
||||
}
|
||||
|
||||
const handleIndustrySelect = (industryId: string | number) => {
|
||||
const nextIndustry = String(industryId).trim()
|
||||
projectIndustry.value = isIndustrySelected(nextIndustry) ? '' : nextIndustry
|
||||
}
|
||||
|
||||
const selectedConsultOption = computed(() =>
|
||||
quickCalcGroups
|
||||
.find(item => item.key === 'consult')
|
||||
@ -446,41 +443,44 @@ watch(canUseLandScale, enabled => {
|
||||
|
||||
|
||||
<div class="quick-calc-toolbar">
|
||||
<label class="quick-calc-toolbar__field">
|
||||
<label class="quick-calc-toolbar__field quick-calc-toolbar__field--cards">
|
||||
<span class="quick-calc-field__label">{{ t('quickCalc.fields.industry') }}</span>
|
||||
<SelectRoot v-model="projectIndustry">
|
||||
<SelectTrigger class="quick-calc-toolbar__trigger">
|
||||
<SelectValue :placeholder="t('quickCalc.selectIndustry')" />
|
||||
<SelectIcon as-child>
|
||||
<ChevronDown class="h-4 w-4 text-[var(--qc-muted)]" />
|
||||
</SelectIcon>
|
||||
</SelectTrigger>
|
||||
<SelectPortal>
|
||||
<SelectContent
|
||||
:side-offset="6"
|
||||
position="popper"
|
||||
class="z-[120] w-[var(--reka-select-trigger-width)] overflow-hidden rounded-xl border border-border bg-popover text-popover-foreground shadow-xl"
|
||||
<div class="quick-calc-industry-grid" role="radiogroup" :aria-label="t('quickCalc.fields.industry')">
|
||||
<label
|
||||
v-for="item in industryTypeList"
|
||||
:key="`quick-workbench-${item.id}`"
|
||||
class="quick-calc-industry-card"
|
||||
:class="{ 'is-selected': isIndustrySelected(item.id) }"
|
||||
:aria-checked="isIndustrySelected(item.id)"
|
||||
role="radio"
|
||||
tabindex="0"
|
||||
@click.prevent="handleIndustrySelect(item.id)"
|
||||
@keydown.enter.prevent="handleIndustrySelect(item.id)"
|
||||
@keydown.space.prevent="handleIndustrySelect(item.id)"
|
||||
>
|
||||
<input
|
||||
:checked="isIndustrySelected(item.id)"
|
||||
type="radio"
|
||||
name="quick-calc-industry-choice"
|
||||
class="quick-calc-option__input"
|
||||
tabindex="-1"
|
||||
>
|
||||
<SelectViewport class="p-1">
|
||||
<SelectItem
|
||||
v-for="item in industryTypeList"
|
||||
:key="`quick-workbench-${item.id}`"
|
||||
:value="String(item.id)"
|
||||
class="relative flex h-9 w-full cursor-default select-none items-center rounded-md pl-3 pr-8 text-sm outline-none data-[highlighted]:bg-muted data-[highlighted]:text-foreground data-[state=checked]:bg-slate-100"
|
||||
>
|
||||
<SelectItemText>{{ getIndustryDisplayName(item.id, locale) }}</SelectItemText>
|
||||
<SelectItemIndicator class="absolute right-2 inline-flex items-center text-slate-700">
|
||||
<Check class="h-4 w-4" />
|
||||
</SelectItemIndicator>
|
||||
</SelectItem>
|
||||
</SelectViewport>
|
||||
</SelectContent>
|
||||
</SelectPortal>
|
||||
</SelectRoot>
|
||||
<span
|
||||
class="quick-calc-industry-card__icon"
|
||||
:class="{ 'is-selected': isIndustrySelected(item.id) }"
|
||||
>
|
||||
<CircleDot v-if="isIndustrySelected(item.id)" class="h-3.5 w-3.5" />
|
||||
<Circle v-else class="h-3.5 w-3.5" />
|
||||
</span>
|
||||
<span class="quick-calc-industry-card__text">
|
||||
{{ getIndustryDisplayName(item.id, locale) }}
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</label>
|
||||
|
||||
<span class="quick-calc-toolbar__meta">
|
||||
{{ industrySaving ? t('quickCalc.saving') : hasSelectedIndustry ? t('quickCalc.synced') : t('quickCalc.notSelectedIndustry') }}
|
||||
{{ industrySaving ? t('quickCalc.saving') : industryLabel }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@ -739,30 +739,76 @@ watch(canUseLandScale, enabled => {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.quick-calc-toolbar__trigger {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
min-height: 42px;
|
||||
padding: 0 14px;
|
||||
border: 1px solid var(--qc-border);
|
||||
border-radius: 12px;
|
||||
background: color-mix(in srgb, white 55%, var(--qc-surface));
|
||||
color: var(--qc-text);
|
||||
box-shadow: inset 0 1px 0 color-mix(in srgb, white 80%, transparent);
|
||||
outline: none;
|
||||
.quick-calc-toolbar__field--cards {
|
||||
align-content: start;
|
||||
}
|
||||
|
||||
.quick-calc-toolbar__trigger:focus-visible {
|
||||
.quick-calc-industry-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(136px, 1fr));
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.quick-calc-industry-card {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
min-width: 0;
|
||||
min-height: 42px;
|
||||
padding: 0 12px;
|
||||
border: 1px solid var(--qc-border);
|
||||
border-radius: 12px;
|
||||
background: color-mix(in srgb, var(--background) 92%, var(--card));
|
||||
color: var(--qc-text);
|
||||
cursor: pointer;
|
||||
transition: transform 160ms ease, border-color 160ms ease, background 160ms ease, box-shadow 160ms ease;
|
||||
}
|
||||
|
||||
.quick-calc-industry-card:hover {
|
||||
transform: translateY(-1px);
|
||||
border-color: var(--qc-border-strong);
|
||||
box-shadow: 0 0 0 3px color-mix(in srgb, hsl(var(--destructive)) 14%, transparent);
|
||||
}
|
||||
|
||||
.quick-calc-industry-card:focus-visible {
|
||||
outline: none;
|
||||
border-color: color-mix(in srgb, hsl(var(--destructive)) 42%, var(--qc-border));
|
||||
box-shadow: 0 0 0 3px color-mix(in srgb, hsl(var(--destructive)) 10%, transparent);
|
||||
}
|
||||
|
||||
.quick-calc-industry-card.is-selected {
|
||||
border-color: color-mix(in srgb, hsl(var(--destructive)) 42%, var(--qc-border));
|
||||
background: color-mix(in srgb, hsl(var(--destructive)) 7%, white);
|
||||
box-shadow: 0 0 0 3px color-mix(in srgb, hsl(var(--destructive)) 10%, transparent);
|
||||
}
|
||||
|
||||
.quick-calc-industry-card__icon {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
flex: 0 0 14px;
|
||||
color: color-mix(in srgb, var(--foreground) 44%, transparent);
|
||||
transition: color 140ms ease, transform 140ms ease;
|
||||
}
|
||||
|
||||
.quick-calc-industry-card__icon.is-selected {
|
||||
color: hsl(var(--destructive));
|
||||
transform: scale(1.02);
|
||||
}
|
||||
|
||||
.quick-calc-industry-card__text {
|
||||
min-width: 0;
|
||||
font-size: 14px;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.quick-calc-toolbar__meta {
|
||||
flex-shrink: 0;
|
||||
font-size: 12px;
|
||||
color: var(--qc-muted);
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.quick-calc-empty-state {
|
||||
@ -1100,22 +1146,23 @@ watch(canUseLandScale, enabled => {
|
||||
}
|
||||
|
||||
.quick-calc-panel--form .quick-calc-panel__title {
|
||||
font-size: clamp(0.88rem, 0.94vw, 1rem);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.quick-calc-panel--form .quick-calc-panel__eyebrow {
|
||||
font-size: 9px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.quick-calc-panel--form .quick-calc-status__item {
|
||||
min-height: 20px;
|
||||
padding: 0 6px;
|
||||
font-size: 9px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.quick-calc-panel--form .quick-calc-form {
|
||||
overflow: auto;
|
||||
padding: 8px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.quick-calc-panel--form .quick-calc-form-stack {
|
||||
@ -1166,11 +1213,13 @@ watch(canUseLandScale, enabled => {
|
||||
}
|
||||
|
||||
.quick-calc-panel--form .quick-calc-form-section__eyebrow {
|
||||
font-size: 9px;
|
||||
font-size: 14px;
|
||||
letter-spacing: 0;
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
.quick-calc-panel--form .quick-calc-form-section__title {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.quick-calc-form-grid {
|
||||
@ -1222,14 +1271,17 @@ watch(canUseLandScale, enabled => {
|
||||
}
|
||||
|
||||
.quick-calc-panel--form .quick-calc-field__label {
|
||||
font-size: 9px;
|
||||
font-size: 14px;
|
||||
letter-spacing: 0;
|
||||
text-transform: none;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.quick-calc-panel--form .quick-calc-field__input,
|
||||
.quick-calc-panel--form .quick-calc-field__readonly {
|
||||
min-height: 32px;
|
||||
padding: 0 8px;
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
border-radius: 9px;
|
||||
}
|
||||
|
||||
@ -1288,8 +1340,8 @@ watch(canUseLandScale, enabled => {
|
||||
}
|
||||
|
||||
.quick-calc-panel--form .quick-calc-form-hint {
|
||||
font-size: 9px;
|
||||
line-height: 1.15;
|
||||
font-size: 14px;
|
||||
line-height: 1.35;
|
||||
}
|
||||
|
||||
.quick-calc-segment {
|
||||
@ -1325,6 +1377,15 @@ watch(canUseLandScale, enabled => {
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.quick-calc-toolbar {
|
||||
align-items: stretch;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.quick-calc-toolbar__meta {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.quick-calc-layout {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
@ -67,8 +67,8 @@ export const zhCN = {
|
||||
projectList: '项目列表',
|
||||
projectCount: '项目数量:{count}',
|
||||
createProject: '新建项目',
|
||||
backHome: '返回首页',
|
||||
resetAll: '重置全部项目',
|
||||
backHome: '返回入口',
|
||||
resetAll: '清除全部项目',
|
||||
opened: '(已打开)',
|
||||
lastEdited: '最后编辑:{time}'
|
||||
},
|
||||
|
||||
@ -1169,11 +1169,7 @@ const buildExportReportPayload = async (): Promise<ExportReportPayload> => {
|
||||
|
||||
const projectScale = projectScaleSource.roughCalcEnabled ? [] : toExportScaleRows(projectScaleSource.detailRows)
|
||||
const projectScaleCost = toFiniteNumber(projectScaleSource.totalAmount) ?? sumNumbers(projectScale.map(item => item.cost))
|
||||
projectScale.push({
|
||||
majorid: -1,
|
||||
major: -1, cost: projectScaleCost,
|
||||
area: null
|
||||
})
|
||||
|
||||
|
||||
const projectServiceCoes = buildProjectServiceCoes(consultCategoryFactorState.resolved?.detailRows)
|
||||
const projectMajorCoes = buildProjectMajorCoes(majorFactorState.resolved?.detailRows)
|
||||
@ -1374,11 +1370,7 @@ const buildExportReportPayload = async (): Promise<ExportReportPayload> => {
|
||||
const reserveFee = toMoney(reserve ? reserve.fee : 0)
|
||||
const contractFee = toMoney(addNumbers(serviceFee, addtionalFee, reserveFee))
|
||||
const contractScale = htInfoRaw?.roughCalcEnabled ? [] : toExportScaleRows(htInfoRaw?.detailRows)
|
||||
contractScale.push({
|
||||
majorid: -1,
|
||||
major: -1, cost: contractFee,
|
||||
area: null
|
||||
})
|
||||
|
||||
const contractServiceCoesRaw = buildProjectServiceCoes(htConsultCategoryFactorState.resolved?.detailRows)
|
||||
const contractMajorCoesRaw = buildProjectMajorCoes(htMajorFactorState.resolved?.detailRows)
|
||||
console.log('[export][contract factor rows]', {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user