1
This commit is contained in:
parent
1d016f8c51
commit
de3585bde3
@ -44,7 +44,7 @@ import {
|
||||
setPendingHomeImportFile,
|
||||
writeWorkspaceMode
|
||||
} from '@/lib/workspace'
|
||||
import { upsertProject } from '@/lib/projectRegistry'
|
||||
import { createProject, upsertProject } from '@/lib/projectRegistry'
|
||||
|
||||
interface QuickProjectInfoState {
|
||||
projectIndustry?: string
|
||||
@ -188,7 +188,7 @@ const enterQuickCalc = (contractName: string) => {
|
||||
writeWorkspaceMode('quick')
|
||||
tabStore.enterWorkspace({
|
||||
id: `contract-${QUICK_CONTRACT_ID}`,
|
||||
title: contractName,
|
||||
title: t('home.quickCalcTab'),
|
||||
componentName: 'QuickCalcWorkbenchView',
|
||||
props: {
|
||||
contractId: QUICK_CONTRACT_ID,
|
||||
@ -261,9 +261,8 @@ const confirmHomeImport = () => {
|
||||
window.dispatchEvent(new CustomEvent('home-import-selected', {
|
||||
detail: { file }
|
||||
}))
|
||||
const projectId = getActiveProjectId()
|
||||
upsertProject(projectId, projectId === DEFAULT_PROJECT_ID ? t('tab.messages.defaultProjectLabel') : undefined)
|
||||
writeProjectIdToUrl(projectId)
|
||||
const project = createProject()
|
||||
writeProjectIdToUrl(project.id)
|
||||
writeWorkspaceMode('project')
|
||||
tabStore.enterWorkspace({
|
||||
id: PROJECT_TAB_ID,
|
||||
|
||||
@ -339,6 +339,11 @@ const toggleItem = (groupKey: string, optionKey: string) => {
|
||||
selectedMajor.value = { groupKey, optionKey }
|
||||
}
|
||||
|
||||
const getGroupTitle = (group: { key: string; label: string }) => {
|
||||
if (group.key !== 'consult') return group.label
|
||||
return group.label.replace(/([((](?:常用|Common)[))])$/i, '\n$1')
|
||||
}
|
||||
|
||||
const loadFactorDefaults = async () => {
|
||||
const [consultMap, majorMap] = await Promise.all([
|
||||
loadConsultCategoryFactorMap(props.projectConsultCategoryFactorKey),
|
||||
@ -440,8 +445,7 @@ watch(canUseLandScale, enabled => {
|
||||
<section class="quick-calc-panel quick-calc-panel--catalog">
|
||||
<header class="quick-calc-panel__header">
|
||||
<div class="quick-calc-panel__title-wrap">
|
||||
<div class="quick-calc-panel__eyebrow">{{ t('quickCalc.catalogEyebrow') }}</div>
|
||||
<h2 class="quick-calc-panel__title">{{ t('quickCalc.catalogTitle') }}</h2>
|
||||
<h2 class="quick-calc-panel__title">{{ t('quickCalc.catalogEyebrow') }}</h2>
|
||||
</div>
|
||||
|
||||
<div class="quick-calc-status">
|
||||
@ -516,7 +520,7 @@ watch(canUseLandScale, enabled => {
|
||||
>
|
||||
<div class="quick-calc-group__side">
|
||||
<div class="quick-calc-group__eyebrow">{{ group.key === 'consult' ? t('quickCalc.consultCategory') : t('quickCalc.majorCategory') }}</div>
|
||||
<h3 class="quick-calc-group__title">{{ group.label }}</h3>
|
||||
<h3 class="quick-calc-group__title quick-calc-group__title--multiline">{{ getGroupTitle(group) }}</h3>
|
||||
|
||||
</div>
|
||||
|
||||
@ -559,8 +563,7 @@ watch(canUseLandScale, enabled => {
|
||||
<aside class="quick-calc-panel quick-calc-panel--form">
|
||||
<header class="quick-calc-panel__header">
|
||||
<div class="quick-calc-panel__title-wrap">
|
||||
<div class="quick-calc-panel__eyebrow">{{ t('quickCalc.formEyebrow') }}</div>
|
||||
<h2 class="quick-calc-panel__title">{{ t('quickCalc.formTitle') }}</h2>
|
||||
<h2 class="quick-calc-panel__title">{{ t('quickCalc.formEyebrow') }}</h2>
|
||||
</div>
|
||||
|
||||
|
||||
@ -569,10 +572,7 @@ watch(canUseLandScale, enabled => {
|
||||
<div class="quick-calc-form">
|
||||
<div class="quick-calc-form-stack">
|
||||
<section class="quick-calc-form-section quick-calc-form-section--summary">
|
||||
<header class="quick-calc-form-section__header">
|
||||
<div class="quick-calc-form-section__eyebrow">{{ t('quickCalc.sections.currentSelection') }}</div>
|
||||
<h3 class="quick-calc-form-section__title">{{ t('quickCalc.sections.basicInfo') }}</h3>
|
||||
</header>
|
||||
|
||||
|
||||
<div class="quick-calc-form-grid quick-calc-form-grid--summary">
|
||||
<label class="quick-calc-field">
|
||||
@ -588,10 +588,7 @@ watch(canUseLandScale, enabled => {
|
||||
</section>
|
||||
|
||||
<section class="quick-calc-form-section">
|
||||
<header class="quick-calc-form-section__header">
|
||||
<div class="quick-calc-form-section__eyebrow">{{ t('quickCalc.sections.scaleBase') }}</div>
|
||||
<h3 class="quick-calc-form-section__title">{{ t('quickCalc.sections.scaleParams') }}</h3>
|
||||
</header>
|
||||
|
||||
|
||||
<div class="quick-calc-form-grid">
|
||||
<label class="quick-calc-field">
|
||||
@ -628,8 +625,7 @@ watch(canUseLandScale, enabled => {
|
||||
|
||||
<section class="quick-calc-form-section">
|
||||
<header class="quick-calc-form-section__header">
|
||||
<div class="quick-calc-form-section__eyebrow">{{ t('quickCalc.sections.benchmarkBudget') }}</div>
|
||||
<h3 class="quick-calc-form-section__title">{{ t('quickCalc.sections.budgetBase') }}</h3>
|
||||
<h3 class="quick-calc-form-section__title">{{ t('quickCalc.sections.benchmarkBudget') }}</h3>
|
||||
</header>
|
||||
|
||||
<div class="quick-calc-form-grid">
|
||||
@ -647,8 +643,7 @@ watch(canUseLandScale, enabled => {
|
||||
|
||||
<section class="quick-calc-form-section">
|
||||
<header class="quick-calc-form-section__header">
|
||||
<div class="quick-calc-form-section__eyebrow">{{ t('quickCalc.sections.serviceBudget') }}</div>
|
||||
<h3 class="quick-calc-form-section__title">{{ t('quickCalc.sections.factorsAndResult') }}</h3>
|
||||
<h3 class="quick-calc-form-section__title">{{ t('quickCalc.sections.serviceBudget') }}</h3>
|
||||
</header>
|
||||
|
||||
<div class="quick-calc-form-grid">
|
||||
@ -667,11 +662,11 @@ watch(canUseLandScale, enabled => {
|
||||
</label>
|
||||
|
||||
<div class="quick-calc-field">
|
||||
<span class="quick-calc-field__label">{{ t('quickCalc.fields.workEnvFactor') }}</span>
|
||||
<span class="quick-calc-field__label">{{ t('quickCalc.fields.workEnvCoefficient') }}</span>
|
||||
<input
|
||||
v-model="workEnvFactor"
|
||||
class="quick-calc-field__input"
|
||||
:placeholder="t('quickCalc.fields.workEnvFactorPlaceholder')"
|
||||
:placeholder="t('quickCalc.fields.workEnvCoefficientPlaceholder')"
|
||||
@blur="applyWorkEnvFactorInput"
|
||||
@keydown.enter.prevent="applyWorkEnvFactorInput"
|
||||
>
|
||||
@ -821,6 +816,11 @@ watch(canUseLandScale, enabled => {
|
||||
color: var(--qc-text);
|
||||
}
|
||||
|
||||
.quick-calc-group__title--multiline {
|
||||
white-space: pre-line;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.quick-calc-status {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
@ -21,6 +21,7 @@ export const enUS = {
|
||||
title: 'Calculation Entry',
|
||||
subtitle: 'Project Budget · Quick Calc · Import Data',
|
||||
projectCalcTab: 'Project Calculation',
|
||||
quickCalcTab: 'Quick Calculation',
|
||||
cards: {
|
||||
heroTitle: 'One-Click Smart Budget',
|
||||
heroSubTitle: 'Accelerate standards adoption',
|
||||
@ -463,9 +464,7 @@ export const enUS = {
|
||||
quickCalc: {
|
||||
projectName: 'Quick Calculation',
|
||||
catalogEyebrow: 'Category List',
|
||||
catalogTitle: 'Quick Calc Options',
|
||||
formEyebrow: 'Parameter Form',
|
||||
formTitle: 'Calculation Parameters',
|
||||
industryLabel: 'Industry {name}',
|
||||
selectIndustry: 'Select industry',
|
||||
saving: 'Saving...',
|
||||
@ -474,6 +473,28 @@ export const enUS = {
|
||||
notSelected: 'Not selected',
|
||||
consultCategory: 'Consult Category',
|
||||
majorCategory: 'Major',
|
||||
types: {
|
||||
consult: {
|
||||
label: 'Consult Category (Common)',
|
||||
hint: 'Select consult category first, then complete scale and budget parameters.'
|
||||
},
|
||||
general: {
|
||||
label: 'General Major',
|
||||
hint: 'Cross-industry common compensation and other expense majors.'
|
||||
},
|
||||
road: {
|
||||
label: 'Highway Major',
|
||||
hint: 'Shown by default when industry is Highway Engineering.'
|
||||
},
|
||||
railway: {
|
||||
label: 'Railway Major',
|
||||
hint: 'Shown by default when industry is Railway Engineering.'
|
||||
},
|
||||
waterway: {
|
||||
label: 'Waterway Major',
|
||||
hint: 'Shown by default when industry is Waterway Engineering.'
|
||||
}
|
||||
},
|
||||
fields: {
|
||||
industry: 'Industry',
|
||||
code: 'Code',
|
||||
@ -483,19 +504,16 @@ export const enUS = {
|
||||
amount: 'Amount (CNY)',
|
||||
consultFactor: 'Consult Category Factor',
|
||||
majorFactor: 'Major Factor',
|
||||
workEnvFactor: 'Work Environment Factor',
|
||||
workEnvFactorPlaceholder: 'Default 1',
|
||||
workEnvCoefficient: 'Work environment coefficient',
|
||||
workEnvCoefficientPlaceholder: 'Default 1',
|
||||
budgetAmount: 'Budget Amount (CNY)'
|
||||
},
|
||||
sections: {
|
||||
currentSelection: 'Current Selection',
|
||||
basicInfo: 'Basic Info',
|
||||
scaleBase: 'Scale Base',
|
||||
scaleParams: 'Scale Parameters',
|
||||
benchmarkBudget: 'Benchmark Budget',
|
||||
budgetBase: 'Budget Base',
|
||||
serviceBudget: 'Service Budget',
|
||||
factorsAndResult: 'Factors and Result'
|
||||
},
|
||||
empty: {
|
||||
selectIndustry: 'Select an industry first. Then choose consult category and matched majors will appear.',
|
||||
|
||||
@ -21,6 +21,7 @@ export const zhCN = {
|
||||
title: '计算入口',
|
||||
subtitle: '项目计算 · 单项速算 · 导入数据',
|
||||
projectCalcTab: '项目计算',
|
||||
quickCalcTab: '快速计算',
|
||||
cards: {
|
||||
heroTitle: '智能预算一键生成',
|
||||
heroSubTitle: '助力《规范》高效落地',
|
||||
@ -463,9 +464,7 @@ export const zhCN = {
|
||||
quickCalc: {
|
||||
projectName: '快速计算',
|
||||
catalogEyebrow: '分类清单',
|
||||
catalogTitle: '快速计算选项',
|
||||
formEyebrow: '参数表单',
|
||||
formTitle: '计算参数',
|
||||
industryLabel: '行业 {name}',
|
||||
selectIndustry: '请选择工程行业',
|
||||
saving: '保存中...',
|
||||
@ -474,6 +473,28 @@ export const zhCN = {
|
||||
notSelected: '未选择',
|
||||
consultCategory: '咨询类别',
|
||||
majorCategory: '工程专业',
|
||||
types: {
|
||||
consult: {
|
||||
label: '咨询类别(常用)',
|
||||
hint: '先选择咨询类别,再补规模和预算参数。'
|
||||
},
|
||||
general: {
|
||||
label: '通用专业',
|
||||
hint: '跨行业共用的补偿与其他费用专业。'
|
||||
},
|
||||
road: {
|
||||
label: '公路工程专业',
|
||||
hint: '首页行业为公路工程时默认展示。'
|
||||
},
|
||||
railway: {
|
||||
label: '铁路工程专业',
|
||||
hint: '首页行业为铁路工程时默认展示。'
|
||||
},
|
||||
waterway: {
|
||||
label: '水运工程专业',
|
||||
hint: '首页行业为水运工程时默认展示。'
|
||||
}
|
||||
},
|
||||
fields: {
|
||||
industry: '工程行业',
|
||||
code: '编码',
|
||||
@ -483,19 +504,15 @@ export const zhCN = {
|
||||
amount: '金额(元)',
|
||||
consultFactor: '咨询分类系数',
|
||||
majorFactor: '工程专业系数',
|
||||
workEnvFactor: '工作环境系数',
|
||||
workEnvFactorPlaceholder: '默认 1',
|
||||
workEnvCoefficient: '工作环节系数',
|
||||
workEnvCoefficientPlaceholder: '默认 1',
|
||||
budgetAmount: '预算金额(元)'
|
||||
},
|
||||
sections: {
|
||||
currentSelection: '当前选项',
|
||||
basicInfo: '基础信息',
|
||||
scaleBase: '计算基数',
|
||||
scaleParams: '规模参数',
|
||||
benchmarkBudget: '基准预算',
|
||||
budgetBase: '预算基础值',
|
||||
serviceBudget: '服务预算',
|
||||
factorsAndResult: '系数与结果'
|
||||
},
|
||||
empty: {
|
||||
selectIndustry: '请选择工程行业。选中后可先选择咨询类别,再显示对应专业分类。',
|
||||
|
||||
@ -1433,14 +1433,6 @@ const prepareImportPayloadFromFile = async (
|
||||
if (!isDataPackageLike(payload)) {
|
||||
throw new Error('INVALID_DATA_PACKAGE')
|
||||
}
|
||||
const currentProjectId = readCurrentProjectId()
|
||||
const payloadProjectId = String(payload.projectId || '').trim()
|
||||
if (!payloadProjectId) {
|
||||
throw new Error('PROJECT_ID_MISSING')
|
||||
}
|
||||
if (payloadProjectId && payloadProjectId !== currentProjectId) {
|
||||
throw new Error(`PROJECT_ID_MISMATCH:${payloadProjectId}:${currentProjectId}`)
|
||||
}
|
||||
pendingImportPayload.value = payload
|
||||
pendingImportFileName.value = file.name
|
||||
if (options?.skipConfirm) {
|
||||
@ -1459,14 +1451,6 @@ const importData = async (event: Event) => {
|
||||
await prepareImportPayloadFromFile(file)
|
||||
} catch (error) {
|
||||
console.error('import failed:', error)
|
||||
if (error instanceof Error && error.message === 'PROJECT_ID_MISSING') {
|
||||
showMessageDialog(t('tab.messages.importFailedTitle'), t('tab.messages.importProjectIdMissing'))
|
||||
return
|
||||
}
|
||||
if (error instanceof Error && error.message.startsWith('PROJECT_ID_MISMATCH:')) {
|
||||
showMessageDialog(t('tab.messages.importFailedTitle'), t('tab.messages.importProjectMismatch'))
|
||||
return
|
||||
}
|
||||
showMessageDialog(t('tab.messages.importFailedTitle'), t('tab.messages.importInvalidFile'))
|
||||
} finally {
|
||||
input.value = ''
|
||||
|
||||
37
src/sql.ts
37
src/sql.ts
@ -124,7 +124,8 @@ const localizeDictItem = (item: Record<string, any>) => {
|
||||
return {
|
||||
...item,
|
||||
name: item.nameEn || item.name,
|
||||
quickLabel: item.quickLabelEn || item.quickLabel || item.nameEn || item.name,
|
||||
// English locale should prefer English name when quickLabelEn is absent.
|
||||
quickLabel: item.quickLabelEn || item.nameEn || item.quickLabel || item.name,
|
||||
basicParam: item.basicParamEn || item.basicParam,
|
||||
unit: item.unitEn || item.unit,
|
||||
desc: item.descEn || item.desc
|
||||
@ -790,6 +791,16 @@ export type QuickCalcGroup = {
|
||||
industryId?: string
|
||||
}
|
||||
|
||||
const getQuickCalcI18nText = (path: string, fallback: string) => {
|
||||
try {
|
||||
const text = i18n?.global?.t?.(path as any)
|
||||
if (typeof text === 'string' && text !== path) return text
|
||||
} catch {
|
||||
// ignore i18n runtime errors and fallback to default text
|
||||
}
|
||||
return fallback
|
||||
}
|
||||
|
||||
const getQuickDictLabel = (item: DictItem | undefined, fallback = '') =>
|
||||
String(item?.quickLabel || item?.name || fallback)
|
||||
|
||||
@ -820,8 +831,8 @@ const createQuickOptionByMajorKey = (
|
||||
export const getQuickCalcGroups = (): QuickCalcGroup[] => [
|
||||
{
|
||||
key: 'consult',
|
||||
label: '咨询类别(常用)',
|
||||
hint: '先选择咨询类别,再补规模和预算参数。',
|
||||
label: getQuickCalcI18nText('quickCalc.types.consult.label', '咨询类别(常用)'),
|
||||
hint: getQuickCalcI18nText('quickCalc.types.consult.hint', '先选择咨询类别,再补规模和预算参数。'),
|
||||
items: [
|
||||
createQuickOptionByServiceKey('0'),
|
||||
createQuickOptionByServiceKey('2'),
|
||||
@ -863,8 +874,8 @@ export const getQuickCalcGroups = (): QuickCalcGroup[] => [
|
||||
},
|
||||
{
|
||||
key: 'general',
|
||||
label: '通用专业',
|
||||
hint: '跨行业共用的补偿与其他费用专业。',
|
||||
label: getQuickCalcI18nText('quickCalc.types.general.label', '通用专业'),
|
||||
hint: getQuickCalcI18nText('quickCalc.types.general.hint', '跨行业共用的补偿与其他费用专业。'),
|
||||
items: [
|
||||
createQuickOptionByMajorKey('1'),
|
||||
createQuickOptionByMajorKey('2'),
|
||||
@ -875,8 +886,8 @@ export const getQuickCalcGroups = (): QuickCalcGroup[] => [
|
||||
},
|
||||
{
|
||||
key: 'road',
|
||||
label: '公路工程专业',
|
||||
hint: '首页行业为公路工程时默认展示。',
|
||||
label: getQuickCalcI18nText('quickCalc.types.road.label', '公路工程专业'),
|
||||
hint: getQuickCalcI18nText('quickCalc.types.road.hint', '首页行业为公路工程时默认展示。'),
|
||||
industryId: '0',
|
||||
items: [
|
||||
createQuickOptionByMajorKey('7'),
|
||||
@ -899,8 +910,8 @@ export const getQuickCalcGroups = (): QuickCalcGroup[] => [
|
||||
},
|
||||
{
|
||||
key: 'railway',
|
||||
label: '铁路工程专业',
|
||||
hint: '首页行业为铁路工程时默认展示。',
|
||||
label: getQuickCalcI18nText('quickCalc.types.railway.label', '铁路工程专业'),
|
||||
hint: getQuickCalcI18nText('quickCalc.types.railway.hint', '首页行业为铁路工程时默认展示。'),
|
||||
industryId: '1',
|
||||
items: [
|
||||
createQuickOptionByMajorKey('18'),
|
||||
@ -922,8 +933,8 @@ export const getQuickCalcGroups = (): QuickCalcGroup[] => [
|
||||
},
|
||||
{
|
||||
key: 'waterway',
|
||||
label: '水运工程专业',
|
||||
hint: '首页行业为水运工程时默认展示。',
|
||||
label: getQuickCalcI18nText('quickCalc.types.waterway.label', '水运工程专业'),
|
||||
hint: getQuickCalcI18nText('quickCalc.types.waterway.hint', '首页行业为水运工程时默认展示。'),
|
||||
industryId: '2',
|
||||
items: [
|
||||
createQuickOptionByMajorKey('28'),
|
||||
@ -931,7 +942,7 @@ export const getQuickCalcGroups = (): QuickCalcGroup[] => [
|
||||
createQuickOptionByMajorKey('30'),
|
||||
createQuickOptionByMajorKey('31'),
|
||||
createQuickOptionByMajorKey('32'),
|
||||
createQuickOptionByMajorKey('33', { label: '房建工程(房屋建筑及附属工程)' })
|
||||
createQuickOptionByMajorKey('33')
|
||||
],
|
||||
rows: [
|
||||
['28'],
|
||||
@ -1140,7 +1151,7 @@ async function generateTemplate(data) {
|
||||
const suffixTexts = ['协助委托人完成咨询服务相应造价文件的报审、报备、报批、检查与审计涉及的解释与回复、修改与调整等工作', '完成本合同造价档案的收集、整理和归档'];
|
||||
try {
|
||||
// 获取模板
|
||||
let templateExcel = 'template20260226001test010';
|
||||
let templateExcel = 'template202603';
|
||||
let templateUrl = `./${templateExcel}.xlsx`;
|
||||
let buf = await (await fetch(templateUrl)).arrayBuffer();
|
||||
let workbook = new ExcelJS.Workbook();
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user