From 995d29302ebf2672375d9c4379cc58319ac1e80e Mon Sep 17 00:00:00 2001 From: "zangch@mesnac.com" Date: Wed, 24 Dec 2025 13:28:30 +0800 Subject: [PATCH 1/6] =?UTF-8?q?feat(projectReceiving):=20=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E5=85=A8=E9=83=A8=E5=88=B0=E8=B4=A7=E6=A0=87=E8=AF=86?= =?UTF-8?q?=E5=AD=97=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在编辑页面表单中添加是否全部到货的单选框组件 - 新增 isAllReceiving 字段到表单数据模型中 - 在列表页面表格中添加全部到货标识列并实现字典标签显示 - 更新类型定义文件,为 ProjectReceivingVO、ProjectReceivingForm 和 ProjectReceivingQuery 接口添加 isAllReceiving 属性 - 调整列表页面列配置和初始化表单数据结构以支持新字段 --- src/api/oa/erp/projectReceiving/types.ts | 15 +++++++++++++++ src/views/oa/erp/projectReceiving/edit.vue | 14 ++++++++++++++ src/views/oa/erp/projectReceiving/index.vue | 21 +++++++++++++++------ 3 files changed, 44 insertions(+), 6 deletions(-) diff --git a/src/api/oa/erp/projectReceiving/types.ts b/src/api/oa/erp/projectReceiving/types.ts index c8a6de8..9e3193c 100644 --- a/src/api/oa/erp/projectReceiving/types.ts +++ b/src/api/oa/erp/projectReceiving/types.ts @@ -78,6 +78,11 @@ export interface ProjectReceivingVO { * 项目状态(1暂存 2审批中 3可用) */ receivingStatus?: string; + + /** + * 全部到货标识(0是1否) + */ + isAllReceiving?: string; } export interface ProjectReceivingForm extends BaseEntity { @@ -160,6 +165,11 @@ export interface ProjectReceivingForm extends BaseEntity { */ receivingStatus?: string; + /** + * 全部到货标识(0是1否) + */ + isAllReceiving?: string; + /** * 流程编码 */ @@ -214,6 +224,11 @@ export interface ProjectReceivingQuery extends PageQuery { */ deputyId?: string | number; + /** + * 全部到货标识(0是1否) + */ + isAllReceiving?: string; + /** * 日期范围参数 */ diff --git a/src/views/oa/erp/projectReceiving/edit.vue b/src/views/oa/erp/projectReceiving/edit.vue index 2e4c3f3..44b8abb 100644 --- a/src/views/oa/erp/projectReceiving/edit.vue +++ b/src/views/oa/erp/projectReceiving/edit.vue @@ -48,6 +48,15 @@ + + + + + {{ dict.label }} + + + + @@ -102,6 +111,10 @@ const { proxy } = getCurrentInstance() as ComponentInternalInstance; const route = useRoute(); const router = useRouter(); +const { wf_business_status, receiving_status, is_all_receiving } = toRefs( + proxy?.useDict('wf_business_status', 'receiving_status', 'is_all_receiving') +); + // 路由参数(与项目信息保持一致) const routeParams = ref>({}); @@ -138,6 +151,7 @@ const initFormData: ProjectReceivingForm = { deputyId: undefined, deputyName: undefined as any, remark: undefined, + isAllReceiving: undefined, receivingStatus: '1', flowStatus: 'draft' as any, flowCode: FlowCodeEnum.PROJECT_RECEIVING_CODE, diff --git a/src/views/oa/erp/projectReceiving/index.vue b/src/views/oa/erp/projectReceiving/index.vue index 6b96ec6..7db084c 100644 --- a/src/views/oa/erp/projectReceiving/index.vue +++ b/src/views/oa/erp/projectReceiving/index.vue @@ -77,12 +77,17 @@ - + + + + - + @@ -140,7 +145,9 @@ import ApprovalRecord from '@/components/Process/approvalRecord.vue'; const { proxy } = getCurrentInstance() as ComponentInternalInstance; const router = useRouter(); const route = useRoute(); -const { wf_business_status, receiving_status } = toRefs(proxy?.useDict('wf_business_status', 'receiving_status')); +const { wf_business_status, receiving_status, is_all_receiving } = toRefs( + proxy?.useDict('wf_business_status', 'receiving_status', 'is_all_receiving') +); const projectReceivingList = ref([]); const buttonLoading = ref(false); @@ -172,8 +179,9 @@ const columns = ref([ { key: 7, label: `部门负责人`, visible: true }, { key: 8, label: `分管副总`, visible: true }, { key: 9, label: `备注`, visible: true }, - { key: 10, label: `业务状态`, visible: true }, - { key: 11, label: `流程状态`, visible: false } + { key: 10, label: `全部到货标识`, visible: true }, + { key: 11, label: `业务状态`, visible: true }, + { key: 12, label: `流程状态`, visible: false } ]); const initFormData: ProjectReceivingForm = { @@ -185,7 +193,8 @@ const initFormData: ProjectReceivingForm = { ossId: undefined, chargeId: undefined, deputyId: undefined, - remark: undefined + remark: undefined, + isAllReceiving: undefined }; const data = reactive>({ form: { ...initFormData }, From 3ce272693fcfda713335f96efe0c17394001e828 Mon Sep 17 00:00:00 2001 From: Yangk Date: Wed, 24 Dec 2025 15:01:25 +0800 Subject: [PATCH 2/6] =?UTF-8?q?feat(crm/TripBusine):=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E5=87=BA=E5=B7=AE=E7=94=B3=E8=AF=B7=E9=A1=B5=E9=9D=A2=E5=AE=A1?= =?UTF-8?q?=E6=89=B9=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/oa/crm/businessTripApply/index.ts | 24 +- src/api/oa/crm/businessTripApply/types.ts | 25 + src/enums/OAEnum.ts | 10 + src/router/index.ts | 6 + src/views/oa/crm/businessTripApply/edit.vue | 528 +++++++++++++++++++ src/views/oa/crm/businessTripApply/index.vue | 228 ++------ 6 files changed, 628 insertions(+), 193 deletions(-) create mode 100644 src/views/oa/crm/businessTripApply/edit.vue diff --git a/src/api/oa/crm/businessTripApply/index.ts b/src/api/oa/crm/businessTripApply/index.ts index b3d4ea1..4a0b33c 100644 --- a/src/api/oa/crm/businessTripApply/index.ts +++ b/src/api/oa/crm/businessTripApply/index.ts @@ -67,10 +67,22 @@ export const delBusinessTripApply = (tripId: string | number | Array { + return request({ + url: '/oa/crm/businessTripApply/submitAndFlowStart', + method: 'post', + data: data + }); }; diff --git a/src/api/oa/crm/businessTripApply/types.ts b/src/api/oa/crm/businessTripApply/types.ts index 49a2aa5..04e382f 100644 --- a/src/api/oa/crm/businessTripApply/types.ts +++ b/src/api/oa/crm/businessTripApply/types.ts @@ -238,6 +238,31 @@ export interface BusinessTripApplyForm extends BaseEntity { * 附件ID(多个用逗号分隔) */ ossId?: string | number; + + /** + * 项目名称 + */ + projectName?: string; + + /** + * 项目编码 + */ + projectCode?: string; + + /** + * 流程变量 + */ + variables?: Record; + + /** + * 业务扩展参数 + */ + bizExt?: Record; + + /** + * 流程编码 + */ + flowCode?: string; } export interface BusinessTripApplyQuery extends PageQuery { diff --git a/src/enums/OAEnum.ts b/src/enums/OAEnum.ts index 5aef2ff..52c67fd 100644 --- a/src/enums/OAEnum.ts +++ b/src/enums/OAEnum.ts @@ -38,6 +38,11 @@ export enum CodeRuleEnum { * 邮寄申请编号规则 */ MAILING_APPLY = '1016', + + /** + * 出差申请编号规则 + */ + BUSINESS_TRIP = '1017' } /** @@ -138,4 +143,9 @@ export enum FlowCodeEnum { * 合同订单标识KEY */ CONTRACT_ORDER_KEY = 'htdd', + + /** + * 出差申请标识KEY + */ + BUSINESS_TRIP_CODE = 'OABT' } diff --git a/src/router/index.ts b/src/router/index.ts index f087679..837c43c 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -159,6 +159,12 @@ export const constantRoutes: RouteRecordRaw[] = [ component: () => import('@/views/oa/crm/crmQuoteInfo/edit.vue'), name: 'crmQuoteInfoView', meta: { title: '报价单查看', activeMenu: '/oa/crm/crmQuoteInfo' } + }, + { + path: 'businessTripApply/edit', + component: () => import('@/views/oa/crm/businessTripApply/edit.vue'), + name: 'BusinessTripApplyEdit', + meta: { title: '出差申请编辑', activeMenu: '/oa/crm/businessTripApply' } } ] }, diff --git a/src/views/oa/crm/businessTripApply/edit.vue b/src/views/oa/crm/businessTripApply/edit.vue new file mode 100644 index 0000000..8d1dcb0 --- /dev/null +++ b/src/views/oa/crm/businessTripApply/edit.vue @@ -0,0 +1,528 @@ + + + diff --git a/src/views/oa/crm/businessTripApply/index.vue b/src/views/oa/crm/businessTripApply/index.vue index e882c6a..a5c9dd9 100644 --- a/src/views/oa/crm/businessTripApply/index.vue +++ b/src/views/oa/crm/businessTripApply/index.vue @@ -49,7 +49,7 @@ - + 新增 @@ -478,7 +497,8 @@ const form = reactive({ supplierContactName: undefined, supplierContactPhone: undefined, supplierContactEmail: undefined, - remark: undefined + remark: undefined, + ossId: undefined }); const rules = { @@ -759,22 +779,16 @@ const generateQuoteCode = async () => { } }; -// 附件上传对话框(UI占位,复用通用上传组件) -const type = ref(0); -const dialog = reactive({ visible: false, title: '' }); -const ossFileModel = ref(undefined); -const handleFile = () => { - type.value = 0; - dialog.visible = true; - dialog.title = '上传报价附件'; -}; -const submitOss = () => { - dialog.visible = false; - proxy?.$modal.msgSuccess('附件已更新'); -}; -const cancel = () => { - dialog.visible = false; -}; +// 附件ID字符串转换(用于FileUpload组件) +const ossIdString = computed({ + get() { + const v = (form as any).ossId; + return v === undefined || v === null ? '' : String(v); + }, + set(val: string) { + (form as any).ossId = val || undefined; + } +}); const submitForm = (status: string, mode: boolean) => { quoteFormRef.value?.validate(async (valid: boolean) => { if (!valid) return; @@ -873,6 +887,7 @@ onMounted(async () => { form.quoteId = id as any; const res = await getCrmQuoteInfo(id as any); Object.assign(form, res.data); + console.log('crmQuoteInfo/edit.vue数据加载成功,ossId:', form.ossId); // 添加调试日志 // 编辑模式:已存在编号则禁用生成 isCodeGenerated.value = !!form.quoteCode; // 根据已有联系人ID回填姓名、电话、邮箱(若后端已返回则保持) diff --git a/src/views/oa/erp/erpProjectPlan/edit.vue b/src/views/oa/erp/erpProjectPlan/edit.vue index 596c7f0..3f32e63 100644 --- a/src/views/oa/erp/erpProjectPlan/edit.vue +++ b/src/views/oa/erp/erpProjectPlan/edit.vue @@ -80,12 +80,14 @@ - - + @@ -280,22 +282,6 @@ - - - - - - - - - - - @@ -371,14 +357,6 @@ const projectSelectRef = ref>(); const projectChangeList = ref([]); // 变更记录列表 const activeChangeTab = ref('0'); // 当前活跃的变更标签 -// 附件上传对话框状态 -const type = ref(0); -const dialog = reactive<{ visible: boolean; title: string }>({ - visible: false, - title: '' -}); -const ossFileModel = ref(undefined); - const createEmptyForm = (): ErpProjectPlanForm => ({ projectPlanId: undefined, projectId: undefined, @@ -549,38 +527,16 @@ const recalcReceivableDate = (row: any) => { row.receivableDate = proxy?.parseTime ? proxy.parseTime(target, '{y}-{m}-{d}') : target.toISOString().slice(0, 10); }; -// 打开附件上传对话框(编辑模式) -const handleFile = () => { - type.value = 0; - dialog.visible = true; - dialog.title = '上传项目计划附件'; - // 回显已有附件 - ossFileModel.value = form.value.ossId as any; -}; - -// 查看模式下预览附件 -const handlePreview = () => { - if (!form.value.ossId) { - proxy?.$modal.msgWarning('暂无附件可预览'); - return; +// 附件ID字符串转换(用于FileUpload组件) +const ossIdString = computed({ + get() { + const v = form.value.ossId as any; + return v === undefined || v === null ? '' : String(v); + }, + set(val: string) { + form.value.ossId = val || (undefined as any); } - type.value = 0; - dialog.visible = true; - dialog.title = '预览项目计划附件'; - ossFileModel.value = form.value.ossId as any; -}; - -// 提交附件 -const submitOss = () => { - form.value.ossId = ossFileModel.value as any; - dialog.visible = false; - proxy?.$modal.msgSuccess('附件已更新'); -}; - -// 关闭附件对话框 -const cancel = () => { - dialog.visible = false; -}; +}); /** 提交按钮(与项目信息保持一致的处理方式) */ const submitForm = (status: string, mode: boolean) => { @@ -615,7 +571,8 @@ const submitForm = (status: string, mode: boolean) => { form.value.variables = { projectId: form.value.projectId, projectName: project?.projectName, - managerId: form.value.managerId + managerId: form.value.managerId, + chargeId: form.value.chargeId }; // 流程实例业务扩展字段 form.value.bizExt = { @@ -755,6 +712,7 @@ const loadFormData = async () => { if (!form.value.planStageList) { form.value.planStageList = []; } + console.log('ossId:', form.value.ossId); // 添加ossId调试日志 if (form.value.projectId) { // 编辑模式下,同步项目名称和编号 const project = projectInfoList.value.find((item) => String(item.projectId) === String(form.value.projectId)); diff --git a/src/views/oa/erp/projectAcceptance/edit.vue b/src/views/oa/erp/projectAcceptance/edit.vue index ec0cf1d..b24801d 100644 --- a/src/views/oa/erp/projectAcceptance/edit.vue +++ b/src/views/oa/erp/projectAcceptance/edit.vue @@ -14,7 +14,7 @@ - + @@ -50,13 +50,21 @@ type="datetime" value-format="YYYY-MM-DD HH:mm:ss" placeholder="请选择验收日期" + :disabled="formDisabled" clearable /> - + @@ -71,7 +79,13 @@ - + @@ -198,9 +212,12 @@ const onProjectChange = async (val: any) => { const applyLeaderFromManager = () => {}; const loadDetail = async () => { + console.log('loadDetail start'); if (!routeParams.value.id) return; const res = await getProjectAcceptance(routeParams.value.id); + console.log('loadDetail res:', res); Object.assign(form.value, res.data); + console.log('loadDetail end'); }; const submitForm = (status: string, mode: boolean) => { diff --git a/src/views/oa/erp/projectReceiving/edit.vue b/src/views/oa/erp/projectReceiving/edit.vue index 44b8abb..3140e14 100644 --- a/src/views/oa/erp/projectReceiving/edit.vue +++ b/src/views/oa/erp/projectReceiving/edit.vue @@ -14,7 +14,7 @@ - + @@ -45,12 +45,19 @@ - + - + {{ dict.label }} @@ -59,7 +66,14 @@ - + @@ -74,7 +88,13 @@ - + @@ -214,7 +234,11 @@ const applyLeaderFromManager = () => {}; const loadDetail = async () => { if (!routeParams.value.id) return; const res = await getProjectReceiving(routeParams.value.id); + console.log('[projectReceiving] 后端返回数据:', res.data); + console.log('[projectReceiving] 后端返回ossId:', res.data?.ossId); Object.assign(form.value, res.data); + console.log('[projectReceiving] 赋值后form.ossId:', form.value.ossId); + console.log('[projectReceiving] ossIdString计算属性值:', ossIdString.value); }; const submitForm = (status: string, mode: boolean) => { From faab5a36f90a9eddcca6b3101504fab171c8ebf2 Mon Sep 17 00:00:00 2001 From: Yangk Date: Fri, 26 Dec 2025 10:59:43 +0800 Subject: [PATCH 5/6] =?UTF-8?q?feat(erp/TimesheetInfo):=20=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E5=B7=A5=E6=97=B6=E8=A1=A8=E5=8D=95=E9=83=A8=E9=97=A8?= =?UTF-8?q?=E4=BF=A1=E6=81=AF=E6=98=BE=E7=A4=BA=E5=92=8C=E5=91=A8=E4=B8=80?= =?UTF-8?q?=E6=97=A5=E6=9C=9F=E9=99=90=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在工时编辑页面的时间选择器中添加非周一日期禁用功能 - 在工时列表页面添加人员部门列显示 --- src/views/oa/erp/timesheetInfo/edit.vue | 7 +++++++ src/views/oa/erp/timesheetInfo/index.vue | 4 +++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/views/oa/erp/timesheetInfo/edit.vue b/src/views/oa/erp/timesheetInfo/edit.vue index 8730758..e235397 100644 --- a/src/views/oa/erp/timesheetInfo/edit.vue +++ b/src/views/oa/erp/timesheetInfo/edit.vue @@ -49,6 +49,7 @@ value-format="YYYY-MM-DD" placeholder="本周一" style="width: 100%" + :disabled-date="disableNonMonday" @change="handleStartTimeChange" /> @@ -436,6 +437,12 @@ const getDeptName = (deptId: any) => { return dept ? dept.deptName : ''; }; +/** 禁用非周一的日期 */ +const disableNonMonday = (date: Date) => { + // getDay() 返回 0=周日, 1=周一, ..., 6=周六 + return date.getDay() !== 1; +}; + /** 生成编号 */ /** 自动计算工时 */ const calculateHours = () => { diff --git a/src/views/oa/erp/timesheetInfo/index.vue b/src/views/oa/erp/timesheetInfo/index.vue index 5853ad8..9c389ed 100644 --- a/src/views/oa/erp/timesheetInfo/index.vue +++ b/src/views/oa/erp/timesheetInfo/index.vue @@ -54,6 +54,7 @@ + @@ -145,7 +146,8 @@ const columns = ref([ { key: 16, label: `创建时间`, visible: true }, { key: 17, label: `更新者`, visible: true }, { key: 18, label: `更新时间`, visible: true }, - { key: 99, label: `人员`, visible: true } + { key: 99, label: `人员`, visible: true }, + { key: 100, label: `人员部门`, visible: true } ]); const initFormData: TimesheetInfoForm = { From ac8452686771e331227f8984aaec46a03c664bbd Mon Sep 17 00:00:00 2001 From: xs Date: Fri, 26 Dec 2025 15:37:11 +0800 Subject: [PATCH 6/6] =?UTF-8?q?1.1.00=E5=89=8D=E7=AB=AF=EF=BC=9A=20=20=20?= =?UTF-8?q?=20feat(budget):=E6=A0=B9=E6=8D=AE=E7=A0=94=E5=8F=91=E9=A2=84?= =?UTF-8?q?=E7=AE=97=E6=96=B0=E6=A8=A1=E6=9D=BF=E4=BF=AE=E6=94=B9=E4=BF=9D?= =?UTF-8?q?=E5=AD=98=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/oa/erp/budgetInfo/index.ts | 12 + src/views/oa/erp/budgetInfo/edit.vue | 549 ++++---------- .../oa/erp/budgetInfo/rd/BudgetDefinition.vue | 39 + .../oa/erp/budgetInfo/rd/BudgetTable.vue | 210 +++--- .../oa/erp/budgetInfo/rd/EquipmentCost.vue | 261 ------- .../oa/erp/budgetInfo/rd/LaborService.vue | 711 ++---------------- .../oa/erp/budgetInfo/rd/LiteratureCost.vue | 385 ---------- .../oa/erp/budgetInfo/rd/MaterialCost.vue | 191 ++--- src/views/oa/erp/budgetInfo/rd/OtherCost.vue | 50 +- src/views/oa/erp/budgetInfo/rd/TechCost.vue | 653 ++++++++++++++++ .../oa/erp/budgetInfo/rd/TestingCost.vue | 31 +- .../budgetInfo/rd/TravelMeetingExchange.vue | 407 ++-------- 12 files changed, 1210 insertions(+), 2289 deletions(-) create mode 100644 src/views/oa/erp/budgetInfo/rd/BudgetDefinition.vue delete mode 100644 src/views/oa/erp/budgetInfo/rd/EquipmentCost.vue delete mode 100644 src/views/oa/erp/budgetInfo/rd/LiteratureCost.vue create mode 100644 src/views/oa/erp/budgetInfo/rd/TechCost.vue diff --git a/src/api/oa/erp/budgetInfo/index.ts b/src/api/oa/erp/budgetInfo/index.ts index f603184..afb9e44 100644 --- a/src/api/oa/erp/budgetInfo/index.ts +++ b/src/api/oa/erp/budgetInfo/index.ts @@ -121,3 +121,15 @@ export const listMaterialInfo = (query?: MaterialInfoQuery): AxiosPromise -
+
- - - - - - - - - - - - - + + + + + + + + + + + @@ -55,7 +53,10 @@ - + + + + - - - - + - - - - + - - + + - + - + + + + @@ -190,7 +188,14 @@ + + diff --git a/src/views/oa/erp/budgetInfo/rd/BudgetTable.vue b/src/views/oa/erp/budgetInfo/rd/BudgetTable.vue index ca10cbe..914a85f 100644 --- a/src/views/oa/erp/budgetInfo/rd/BudgetTable.vue +++ b/src/views/oa/erp/budgetInfo/rd/BudgetTable.vue @@ -1,7 +1,5 @@ diff --git a/src/views/oa/erp/budgetInfo/rd/EquipmentCost.vue b/src/views/oa/erp/budgetInfo/rd/EquipmentCost.vue deleted file mode 100644 index ccade65..0000000 --- a/src/views/oa/erp/budgetInfo/rd/EquipmentCost.vue +++ /dev/null @@ -1,261 +0,0 @@ - - - - - diff --git a/src/views/oa/erp/budgetInfo/rd/LaborService.vue b/src/views/oa/erp/budgetInfo/rd/LaborService.vue index f8c9acd..ed6f57e 100644 --- a/src/views/oa/erp/budgetInfo/rd/LaborService.vue +++ b/src/views/oa/erp/budgetInfo/rd/LaborService.vue @@ -1,205 +1,5 @@ - + - - -
- 人工费合计: {{ formatNumber(laborTotal) }} 万元 -
-
- - - -
-

劳务费预算明细表

-
- 行数: - - - - - - 添加 - - - - - - 删除 - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- 劳务费合计: {{ formatNumber(serviceTotal) }} 万元 +
+

注: 人工费的预算,人工标准,各项目组按照成员的大概平均工资水平进行估算。表格所给数据为建议参考数据。

+

        人员类别,按照各项目实际情况填写。如,产品不在本公司内加工和组装的,生产加工费已经核算在外协件里,不再有生产加工费。

@@ -374,16 +84,7 @@ - - diff --git a/src/views/oa/erp/budgetInfo/rd/MaterialCost.vue b/src/views/oa/erp/budgetInfo/rd/MaterialCost.vue index 4bead70..34dd3ed 100644 --- a/src/views/oa/erp/budgetInfo/rd/MaterialCost.vue +++ b/src/views/oa/erp/budgetInfo/rd/MaterialCost.vue @@ -22,12 +22,17 @@
- - + + + + + @@ -41,25 +46,12 @@ default-first-option style="width: 100%" clearable - v-if="scope.row.materialType === MATERIAL_TYPE.MAIN" > - - - + - - + + + + + + + + @@ -131,11 +120,8 @@
--> -
-
- 主要材料费小计: {{ format2TenThousandNumber(mainMaterialTotalAmount) }} 万元 - 合计: {{ formatNumber(totalAmount) }} 万元 -
+
+

填写说明:指研究开发活动过程中投入的各种半成品、原材料、辅助材料等费用。(半成品:包括自制半成品、外协加工件。原材料:外购件、带料外协的外购件。辅助材料:现场使用的耗材)

@@ -153,34 +139,11 @@ const props = defineProps<{ projectId: string; }>(); -const MATERIAL_TYPE = { - MAIN: '1', //主要材料费 - OTHER: '2' //其他材料费 -}; // 材料费数据 const materialData = ref([]); const toDeletedMaterialCostIdList = ref([]); -// 其他材料费 -const otherMaterialData = ref(); - -const defaultOtherMaterial = ref({ - sortOrder: 1, - materialName: '其他材料费', - materialType: MATERIAL_TYPE.OTHER, - unitId: undefined, - unitPrice: undefined, - amount: undefined, - price: 0 -}); -// 响应式数据 -const otherMaterial = ref(defaultOtherMaterial); - -// 合并所有材料数据(主要材料 + 其他材料) -const allMaterialData = computed(() => { - return [...materialData.value, otherMaterial.value]; -}); const unitOptions = ref([]); @@ -188,13 +151,12 @@ const unitOptions = ref([]); // const unitOptions = ref(['kg', 'g', 't', 'm', 'cm', 'mm', 'm²', 'm³', '个', '件', '套', '批', '箱', '袋', '瓶', '小时', '天', '月', '年', '次']); // 总金额和总数量 -const totalAmount = ref(0); const totalQuantity = ref(0); // 主要材料费合计(排除其他材料费) -const mainMaterialTotalAmount = computed(() => { - return materialData.value.reduce((sum, item) => sum + (Number(item.price) || 0), 0); -}); +// const totalAmount = computed(() => { +// return materialData.value.reduce((sum, item) => sum + (Number(item.price) || 0), 0); +// }); // 选中的行 const selectedRows = ref([]); @@ -214,31 +176,6 @@ const format2TenThousandNumber = (value: number) => { return (parseFloat(value) / 10000).toFixed(2); }; -const checkSelectable = (row: rdBudgetMaterialCostVO, index: number) => { - return row.materialType === MATERIAL_TYPE.MAIN; -}; - -// 监听 otherMaterialData 的变化 -watch( - otherMaterialData, - (newVal) => { - if (newVal) { - otherMaterial.value = newVal; - } else { - otherMaterial.value = defaultOtherMaterial.value; - } - }, - { immediate: true } -); // immediate: true 立即执行一次 - -// 监听材料数据变化,更新总合计 -watch( - () => [materialData, otherMaterial], - () => { - calculateTotal(); - }, - { deep: true } -); // 添加指定行数 const addSpecifiedRows = async () => { @@ -253,14 +190,13 @@ const addSpecifiedRows = async () => { materialData.value.push({ sortOrder: currentSortOrder + i + 1, materialName: '', - materialType: MATERIAL_TYPE.MAIN, + materialType: '', unitId: undefined, unitPrice: undefined, amount: undefined, price: 0 }); } - otherMaterial.value.sortOrder = currentSortOrder + 1 + addRowCount.value; // 等待DOM更新后滚动到底部 await nextTick(); scrollToBottom(); @@ -290,8 +226,6 @@ const handleDelete = (index: number, row: rdBudgetMaterialCostVO) => { if (row.materialCostId) { toDeletedMaterialCostIdList.value.push(row.materialCostId); } - otherMaterial.value.sortOrder = otherMaterial.value.sortOrder - 1; - calculateTotal(); }; // 批量删除 @@ -311,8 +245,6 @@ const handleBatchDelete = () => { materialData.value.forEach((item, index) => { item.sortOrder = index + 1; }); - otherMaterial.value.sortOrder = otherMaterial.value.sortOrder - selectedRows.value.length; - calculateTotal(); selectedRows.value = []; }; @@ -322,24 +254,44 @@ const calculateAmount = () => { materialData.value.forEach((item) => { item.price = (item.amount || 0) * (item.unitPrice || 0); }); - - // 计算其他材料金额 - // otherMaterial.value.price = (otherMaterial.value.amount || 0) * (otherMaterial.value.unitPrice || 0); - - // 计算总金额 - calculateTotal(); }; -// 计算总金额和总数量 -const calculateTotal = () => { - const mainAmount = parseFloat(((Number(mainMaterialTotalAmount.value) || 0) / 10000).toFixed(2)); - const otherAmount = parseFloat((Number(otherMaterial.value.price) || 0).toFixed(2)); - totalAmount.value = (mainAmount + otherAmount).toFixed(2); -}; +// 合计方法 +const getSummaries = (param: any) => { + const { columns, data } = param; + const sums: any[] = []; + columns.forEach((column: any, index: number) => { + if (index === 2) { + sums[index] = '合计(万元)'; + return; + } + if (column.property === 'price') { + const values = data.map((item: any) => Number(item[column.property])); + if (!values.every((value: number) => Number.isNaN(value))) { + sums[index] = values.reduce((prev: number, curr: number) => { + const value = Number(curr) + if (!Number.isNaN(value)) { + return prev + curr + } else { + return prev + } + }, 0); + sums[index] = formatNumber(sums[index]); + } else { + sums[index] = ''; + } + } else { + sums[index] = ''; + } + }); + return sums; +} + // 获取总金额(供父组件调用) const getTotalAmount = () => { - return (Number(totalAmount.value) || 0) * 10000; + const totalAmount = materialData.value.reduce((sum, item) => sum + Number(item.price), 0) + return totalAmount * 10000; }; // 获取单位列表 @@ -361,32 +313,13 @@ const refreshData = () => { // 重置数据 const resetData = () => { materialData.value.splice(0, materialData.value.length); - totalAmount.value = 0; totalQuantity.value = 0; - - // 重置其他材料费 - // otherMaterial.unit = ''; - // otherMaterial.quantity = 0; - // otherMaterial.unitPrice = 0; - // otherMaterial.amount = 0; }; -// 获取表单数据 -const getFormData = () => { - return { - materialData: [...materialData.value], - otherMaterial: { ...otherMaterial }, - totalAmount: totalAmount.value, - totalQuantity: totalQuantity.value - }; -}; // 暴露方法给父组件 defineExpose({ materialData, - otherMaterial, - otherMaterialData, - allMaterialData, toDeletedMaterialCostIdList, getTotalAmount }); diff --git a/src/views/oa/erp/budgetInfo/rd/OtherCost.vue b/src/views/oa/erp/budgetInfo/rd/OtherCost.vue index 9280276..46c9d76 100644 --- a/src/views/oa/erp/budgetInfo/rd/OtherCost.vue +++ b/src/views/oa/erp/budgetInfo/rd/OtherCost.vue @@ -21,12 +21,26 @@ - - + + - + @@ -34,20 +48,21 @@ + + + - - -
合计: {{ formatNumber(totalAmount) }} 万元
- @@ -63,11 +78,26 @@ const props = defineProps<{ // 响应式数据 const loading = ref(false); -const otherCostList = ref([]); +// const otherCostList = ref([]); const selectedRows = ref([]); const addRowCount = ref(1); const toDeletedOtherCostIdList = ref([]); +// 预算数据 +const otherCostList = ref([ + { sortOrder: 1, itemDesc: '安装费' }, + { sortOrder: 2, itemDesc: '资料/文献费' }, + { sortOrder: 3, itemDesc: '会议费' }, + { sortOrder: 4, itemDesc: '劳务费' }, + { sortOrder: 5, itemDesc: '运输装卸费' }, + { sortOrder: 6, itemDesc: '中介代理费' }, + { sortOrder: 7, itemDesc: '其他费用' } +]); + +const checkSelectable = (row: rdBudgetOtherCostVO, index: number) => { + return row.sortOrder > 7; +}; + // 格式化数字,元转换为万元 const formatNumber = (value: number) => { if (!value) return '0.00'; diff --git a/src/views/oa/erp/budgetInfo/rd/TechCost.vue b/src/views/oa/erp/budgetInfo/rd/TechCost.vue new file mode 100644 index 0000000..e18bcad --- /dev/null +++ b/src/views/oa/erp/budgetInfo/rd/TechCost.vue @@ -0,0 +1,653 @@ + + + + + diff --git a/src/views/oa/erp/budgetInfo/rd/TestingCost.vue b/src/views/oa/erp/budgetInfo/rd/TestingCost.vue index 8eb394b..06b3abc 100644 --- a/src/views/oa/erp/budgetInfo/rd/TestingCost.vue +++ b/src/views/oa/erp/budgetInfo/rd/TestingCost.vue @@ -23,17 +23,31 @@ - + - + - + @@ -64,10 +78,8 @@ -
-
- 测试化验加工费总计(万元):{{ formatNumber(totalAmount) }} -
+
+

注:是指支付给外单位(包括公司内部经济核算单位)的检验、测试、化验及加工等费用。

@@ -215,7 +227,6 @@ const refreshData = () => { const resetData = () => { testData.value.splice(0, testData.value.length); totalAmount.value = 0; - idCounter = 1; }; // 获取表单数据 diff --git a/src/views/oa/erp/budgetInfo/rd/TravelMeetingExchange.vue b/src/views/oa/erp/budgetInfo/rd/TravelMeetingExchange.vue index eab7c8d..4964f3e 100644 --- a/src/views/oa/erp/budgetInfo/rd/TravelMeetingExchange.vue +++ b/src/views/oa/erp/budgetInfo/rd/TravelMeetingExchange.vue @@ -39,10 +39,10 @@ '', '', '', - '合计(万元)', - format2TenThousandNumber(travelTransportTotal), - format2TenThousandNumber(travelAccommodationTotal), - format2TenThousandNumber(travelSubsidyTotal), + '合计', + formatNumber(travelTransportTotal), + formatNumber(travelAccommodationTotal), + formatNumber(travelSubsidyTotal), formatNumber(travelSubtotalTotal), '' ]; @@ -111,118 +111,21 @@
- - -
-

会议费预算明细表

-
- 行数: - - - - - - 添加 - - - - - - 删除 - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - +
-

国际交流费预算明细表

+

交通费明细表

行数: - - + + 添加 - + @@ -233,97 +136,61 @@ - + - + - - - - + + - - - - - - - - - + + +
+

注:开展实验(试验)、考察、业务调研、学术交流等发生的外埠差旅费、市内交通费用等。开支标准按照公司有关规定执行。

+
@@ -331,14 +198,17 @@ import { ref, computed, watch } from 'vue'; import { ElMessage } from 'element-plus'; import { rdBudgetTravelCostVO } from '@/api/oa/erp/budgetInfo/rd/rdBudgetTravelCost/types'; -import { rdBudgetMeetingCostVO } from '@/api/oa/erp/budgetInfo/rd/rdBudgetMeetingCost/types'; -import { rdBudgetExchangeCostVO } from '@/api/oa/erp/budgetInfo/rd/rdBudgetExchangeCost/types'; // Props const props = defineProps<{ projectId?: string; }>(); +const TRIP_TYPE = { + TRAVEL: '1', //差旅费 + TRANSPORTATION: '2' //交通费 +}; + // 响应式数据 const loading = ref(false); @@ -348,17 +218,11 @@ const selectedTravelRows = ref([]); const travelAddRowCount = ref(1); const toDeletedTravelCostIdList = ref([]); -// 会议费相关 -const meetingList = ref([]); -const selectedMeetingRows = ref([]); -const meetingAddRowCount = ref(1); -const toDeletedMeetingCostIdList = ref([]); - -// 国际交流费相关 -const exchangeList = ref([]); -const selectedInternationalRows = ref([]); -const internationalAddRowCount = ref(1); -const toDeletedExchangeCostIdList = ref([]); +// 交通费相关 +const transportationList = ref([]); +const selectedTransportationRows = ref([]); +const transportationAddRowCount = ref(1); +const toDeletedTransportationCostIdList = ref([]); // 计算差旅费合计 //往返路费 @@ -385,19 +249,10 @@ const travelSubtotalTotal = computed(() => { }, 0); }); -// 计算会议费合计(需要转成万元,2位小数再相加) -const meetingFeeTotal = computed(() => { - return meetingList.value.reduce((sum, item) => { - const amountInTenThousand = (Number(item.meetingPrice) || 0) / 10000; - const formattedAmount = parseFloat(amountInTenThousand.toFixed(2)); - return sum + formattedAmount; - }, 0); -}); - -// 计算国际交流费合计(需要转成万元,2位小数再相加) -const internationalSubtotalTotal = computed(() => { - return exchangeList.value.reduce((sum, item) => { - const amountInTenThousand = (Number(item.price) || 0) / 10000; +// 计算交通费合计(需要转成万元,2位小数再相加) +const transportationSubtotalTotal = computed(() => { + return transportationList.value.reduce((sum, item) => { + const amountInTenThousand = (Number(item.subtotalCosts) || 0) / 10000; const formattedAmount = parseFloat(amountInTenThousand.toFixed(2)); return sum + formattedAmount; }, 0); @@ -419,47 +274,19 @@ const format2TenThousandNumber = (value: number) => { const calculateTravelSubtotal = () => { travelList.value.forEach((item) => { // 住宿费 = 人数 * 天数 * 住宿标准 - item.stayCosts = (item.peopleNumber || 0) * (item.days || 0) * (item.stayStandard || 0); + item.stayCosts = (item.peopleNumber || 0) * (item.frequency || 0) * (item.days || 0) * (item.stayStandard || 0); // 补贴 = 90 * 人数 * 天数 - item.subsidyCosts = 90 * (item.peopleNumber || 0) * (item.days || 0); + item.subsidyCosts = 90 * (item.peopleNumber || 0) * (item.frequency || 0) * (item.days || 0); // 小计 = (往返路费 + 住宿费 + 补贴) / 10000 item.subtotalCosts = (item.travelExpenses || 0) + item.stayCosts + item.subsidyCosts; }); }; -// 计算会议费 -const calculateMeetingFee = () => { - meetingList.value.forEach((item) => { - // 公司组织会议:((场地日租金 + 日均杂费) * 天数 + 专家交通住宿费) / 10000 - // 外出参加会议:(人数 * 人均交费) / 10000 - //会议费=公司组织会议费用+外出参加会议费用 - item.meetingPrice = - ((item.rentalFee || 0) + (item.dailyExpense || 0)) * (item.days || 0) + - (item.expertExpense || 0) + - (item.peopleNumber || 0) * (item.perPersonExpense || 0); - }); -}; -// 计算国际交流费小计 -const calculateInternationalSubtotal = () => { - exchangeList.value.forEach((item) => { - // 如果合作交流类型是出国考察,则补贴=40*人数*时间 - if (item.communicationType === '出国考察') { - item.subsidy = 40 * (item.peopleNumber || 0) * (item.days || 0); - } else if (!item.subsidy) { - // 其他类型如果没有设置补贴,默认设为0 - item.subsidy = 0; - } - // 小计 = (往返路费及住宿费 + 补贴) / 10000 - item.price = (item.travelAccommodationExpense || 0) + (item.subsidy || 0); - }); -}; - -const getTravelMeetingExchangeAmount = () => { +const getTravelAmount = () => { return { travelSubtotalTotal: (Number(travelSubtotalTotal.value) || 0) * 10000, - meetingFeeTotal: (Number(meetingFeeTotal.value) || 0) * 10000, - internationalSubtotalTotal: (Number(internationalSubtotalTotal.value) || 0) * 10000 + transportationSubtotalTotal: (Number(transportationSubtotalTotal.value) || 0) * 10000 }; }; @@ -469,12 +296,13 @@ const confirmTravelAdd = () => { for (let i = 0; i < travelAddRowCount.value; i++) { const newItem: rdBudgetTravelCostVO = { sortOrder: currentSortOrder + i + 1, + tripType: TRIP_TYPE.TRAVEL, tripLocation: '', reason: '', frequency: undefined, peopleNumber: undefined, days: undefined, - stayStandard: undefined, + stayStandard: 180, travelExpenses: undefined, stayCosts: 0, subsidyCosts: 0, @@ -540,23 +368,19 @@ const handleTravelBatchDelete = () => { selectedTravelRows.value = []; }; -// 会议费相关方法 -const confirmMeetingAdd = () => { - const currentSortOrder = meetingList.value && meetingList.value.length > 0 ? Math.max(...meetingList.value.map((item) => item.sortOrder)) : 0; - for (let i = 0; i < meetingAddRowCount.value; i++) { - const newItem: rdBudgetMeetingCostVO = { +// 交通费相关方法 +const confirmTransportationAdd = () => { + const currentSortOrder = transportationList.value && transportationList.value.length > 0 ? Math.max(...transportationList.value.map((item) => item.sortOrder)) : 0; + + for (let i = 0; i < transportationAddRowCount.value; i++) { + const newItem: rdBudgetTravelCostVO = { sortOrder: currentSortOrder + i + 1, - meetingContent: '', - rentalFee: undefined, - dailyExpense: undefined, - days: undefined, - expertExpense: undefined, - peopleNumber: undefined, - perPersonExpense: undefined, - meetingPrice: 0 + tripType: TRIP_TYPE.TRANSPORTATION, + tripLocation: '', + reason: '', }; - meetingList.value.push(newItem); + transportationList.value.push(newItem); } // 滚动到最下方并聚焦到新添加的行 @@ -568,83 +392,7 @@ const confirmMeetingAdd = () => { // 尝试聚焦到新添加的第一行的第一个输入框 const rows = document.querySelectorAll('.el-table__body tr'); - const newRow = rows[rows.length - meetingAddRowCount.value]; - if (newRow) { - const firstInput = newRow.querySelector('input, select'); - if (firstInput) { - firstInput.focus(); - } - } - }, 0); -}; - -const handleMeetingSelectionChange = (selection: rdBudgetMeetingCostVO[]) => { - selectedMeetingRows.value = selection; -}; - -// 删除单行 -const handleMeetingDelete = (index: number, row: rdBudgetMeetingCostVO) => { - meetingList.value.splice(index, 1); - // 重新编号 - meetingList.value.forEach((item, index) => { - item.sortOrder = index + 1; - }); - if (row.meetingCostId) { - toDeletedMeetingCostIdList.value.push(row.meetingCostId); - } - calculateMeetingFee(); -}; - -// 批量删除 -const handleMeetingBatchDelete = () => { - if (selectedMeetingRows.value.length === 0) { - ElMessage.warning('请选择要删除的行'); - return; - } - selectedMeetingRows.value.forEach((selectedRow) => { - if (selectedRow.meetingCostId) { - toDeletedMeetingCostIdList.value.push(selectedRow.meetingCostId); - } - }); - - meetingList.value = meetingList.value.filter((item) => !selectedMeetingRows.value.includes(item)); - // 重新编号 - meetingList.value.forEach((item, index) => { - item.sortOrder = index + 1; - }); - calculateMeetingFee(); - selectedMeetingRows.value = []; -}; - -// 国际交流费相关方法 -const confirmInternationalAdd = () => { - const currentSortOrder = exchangeList.value && exchangeList.value.length > 0 ? Math.max(...exchangeList.value.map((item) => item.sortOrder)) : 0; - - for (let i = 0; i < internationalAddRowCount.value; i++) { - const newItem: rdBudgetExchangeCostVO = { - sortOrder: currentSortOrder + i + 1, - communicationType: '', - countryRegion: '', - institution: '', - peopleNumber: undefined, - days: undefined, - travelAccommodationExpense: undefined, - subsidy: undefined, - price: 0 - }; - exchangeList.value.push(newItem); - } - - // 滚动到最下方并聚焦到新添加的行 - setTimeout(() => { - const table = document.querySelector('.el-table__body-wrapper'); - if (table) { - table.scrollTop = table.scrollHeight; - } - - // 尝试聚焦到新添加的第一行的第一个输入框 - const rows = document.querySelectorAll('.el-table__body tr'); - const newRow = rows[rows.length - internationalAddRowCount.value]; + const newRow = rows[rows.length - transportationAddRowCount.value]; if (newRow) { const firstInput = newRow.querySelector('input, select'); if (firstInput) { @@ -655,42 +403,40 @@ const confirmInternationalAdd = () => { }; -const handleInternationalSelectionChange = (selection: rdBudgetExchangeCostVO[]) => { - selectedInternationalRows.value = selection; +const handleTransportationSelectionChange = (selection: rdBudgetTravelCostVO[]) => { + selectedTransportationRows.value = selection; }; // 删除单行 -const handleExchangeDelete = (index: number, row: rdBudgetExchangeCostVO) => { - exchangeList.value.splice(index, 1); +const handleTransportationDelete = (index: number, row: rdBudgetTravelCostVO) => { + transportationList.value.splice(index, 1); // 重新编号 - exchangeList.value.forEach((item, index) => { + transportationList.value.forEach((item, index) => { item.sortOrder = index + 1; }); - if (row.exchangeCostId) { - toDeletedExchangeCostIdList.value.push(row.exchangeCostId); + if (row.travelCostId) { + toDeletedTravelCostIdList.value.push(row.travelCostId); } - calculateInternationalSubtotal(); }; // 批量删除 -const handleExchangeBatchDelete = () => { - if (selectedInternationalRows.value.length === 0) { +const handleTransportationBatchDelete = () => { + if (selectedTransportationRows.value.length === 0) { ElMessage.warning('请选择要删除的行'); return; } - selectedInternationalRows.value.forEach((selectedRow) => { - if (selectedRow.exchangeCostId) { - toDeletedExchangeCostIdList.value.push(selectedRow.exchangeCostId); + selectedTransportationRows.value.forEach((selectedRow) => { + if (selectedRow.travelCostId) { + toDeletedTravelCostIdList.value.push(selectedRow.travelCostId); } }); - exchangeList.value = exchangeList.value.filter((item) => !selectedInternationalRows.value.includes(item)); + transportationList.value = transportationList.value.filter((item) => !selectedTransportationRows.value.includes(item)); // 重新编号 - exchangeList.value.forEach((item, index) => { + transportationList.value.forEach((item, index) => { item.sortOrder = index + 1; }); - calculateInternationalSubtotal(); - selectedInternationalRows.value = []; + selectedTransportationRows.value = []; }; // 监听projectId变化,重新加载数据 @@ -707,11 +453,8 @@ watch( defineExpose({ travelList, toDeletedTravelCostIdList, - meetingList, - toDeletedMeetingCostIdList, - exchangeList, - toDeletedExchangeCostIdList, - getTravelMeetingExchangeAmount + transportationList, + getTravelAmount });