Merge branch 'master' into wkz

# Conflicts:
#	src/views/crm/contract/index.vue
pull/781/head
quu 2025-05-16 20:13:49 +09:00
commit 172f9a5dcd
105 changed files with 10490 additions and 608 deletions

33
package-lock.json generated
View File

@ -43,6 +43,7 @@
"markmap-view": "^0.16.0",
"min-dash": "^4.1.1",
"mitt": "^3.0.1",
"moment": "^2.30.1",
"nprogress": "^0.2.0",
"pinia": "^2.1.7",
"pinia-plugin-persistedstate": "^3.2.1",
@ -57,6 +58,7 @@
"vue-dompurify-html": "^4.1.4",
"vue-i18n": "9.10.2",
"vue-router": "4.4.5",
"vue-signature-pad": "^3.0.2",
"vue-types": "^5.1.1",
"vue3-signature": "^0.2.4",
"vuedraggable": "^4.1.0",
@ -12844,6 +12846,12 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/merge-images": {
"version": "1.2.0",
"resolved": "https://registry.npmmirror.com/merge-images/-/merge-images-1.2.0.tgz",
"integrity": "sha512-hEGvgnTdXr08uzGvEArxRsKpy7WmozM73YaSi4s5IYF4LxrhANpqfHaz9CgBZ5+0+s2NsjPnPdStz3aCc0Yulw==",
"license": "MIT"
},
"node_modules/merge-stream": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
@ -13057,6 +13065,15 @@
"saxen": "^8.1.2"
}
},
"node_modules/moment": {
"version": "2.30.1",
"resolved": "https://registry.npmmirror.com/moment/-/moment-2.30.1.tgz",
"integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==",
"license": "MIT",
"engines": {
"node": "*"
}
},
"node_modules/mpd-parser": {
"version": "0.22.1",
"resolved": "https://registry.npmjs.org/mpd-parser/-/mpd-parser-0.22.1.tgz",
@ -16967,6 +16984,22 @@
"vue": "^3.2.0"
}
},
"node_modules/vue-signature-pad": {
"version": "3.0.2",
"resolved": "https://registry.npmmirror.com/vue-signature-pad/-/vue-signature-pad-3.0.2.tgz",
"integrity": "sha512-o25o+lROfCmzASS2+fU8ZV801kV+D4/02zkZ+ez3NKeiUmbxW7kwlUf5oKQkvA+l7Ou9xGsGLsirBLch3jyX8A==",
"license": "MIT",
"dependencies": {
"merge-images": "^1.1.0",
"signature_pad": "^3.0.0-beta.3"
},
"engines": {
"node": ">=12"
},
"peerDependencies": {
"vue": "^3.2.0"
}
},
"node_modules/vue-template-compiler": {
"version": "2.7.16",
"resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.7.16.tgz",

View File

@ -59,6 +59,7 @@
"markmap-view": "^0.16.0",
"min-dash": "^4.1.1",
"mitt": "^3.0.1",
"moment": "^2.30.1",
"nprogress": "^0.2.0",
"pinia": "^2.1.7",
"pinia-plugin-persistedstate": "^3.2.1",
@ -73,6 +74,7 @@
"vue-dompurify-html": "^4.1.4",
"vue-i18n": "9.10.2",
"vue-router": "4.4.5",
"vue-signature-pad": "^3.0.2",
"vue-types": "^5.1.1",
"vue3-signature": "^0.2.4",
"vuedraggable": "^4.1.0",

View File

@ -1,49 +1,48 @@
import request from '@/config/axios'
// 资金成本 VO
export interface CapitalCostVO {
id: number // 主键
projectCode: string // 项目编码
paymentTerm: string // 结算账期
creditMethod: number // 授信方式
applicableProjects: number // 适用项目,全部产品、机票
creditCalcCycle: number // 授信额度计算周期
ticketIssuanceCycle: number // 出票周期
settlementCycle: number // 结算周期
usageDays: number // 实际资金使用天数
annualInterestRate: number // 年利率
remark: string // 备注
}
// 资金成本 API
export const CapitalCostApi = {
// 查询资金成本分页
getCapitalCostPage: async (params: any) => {
return await request.get({ url: `/crm/capital-cost/page`, params })
},
// 查询资金成本详情
getCapitalCost: async (id: number) => {
return await request.get({ url: `/crm/capital-cost/get?id=` + id })
},
// 新增资金成本
createCapitalCost: async (data: CapitalCostVO) => {
return await request.post({ url: `/crm/capital-cost/create`, data })
},
// 修改资金成本
updateCapitalCost: async (data: CapitalCostVO) => {
return await request.put({ url: `/crm/capital-cost/update`, data })
},
// 删除资金成本
deleteCapitalCost: async (id: number) => {
return await request.delete({ url: `/crm/capital-cost/delete?id=` + id })
},
// 导出资金成本 Excel
exportCapitalCost: async (params) => {
return await request.download({ url: `/crm/capital-cost/export-excel`, params })
},
}
import request from '@/config/axios'
// 资金成本 VO
export interface CapitalCostVO {
id: number // 主键
projectCode: string // 项目编码
paymentTerm: string // 结算账期
creditMethod: number // 授信方式
applicableProjects: number // 适用项目,全部产品、机票
creditCalcCycle: number // 授信额度计算周期
ticketIssuanceCycle: number // 出票周期
settlementCycle: number // 结算周期
usageDays: number // 实际资金使用天数
annualInterestRate: number // 年利率
}
// 资金成本 API
export const CapitalCostApi = {
// 查询资金成本分页
getCapitalCostPage: async (params: any) => {
return await request.get({ url: `/crm/capital-cost/page`, params })
},
// 查询资金成本详情
getCapitalCost: async (id: number) => {
return await request.get({ url: `/crm/capital-cost/get?id=` + id })
},
// 新增资金成本
createCapitalCost: async (data: CapitalCostVO) => {
return await request.post({ url: `/crm/capital-cost/create`, data })
},
// 修改资金成本
updateCapitalCost: async (data: CapitalCostVO) => {
return await request.put({ url: `/crm/capital-cost/update`, data })
},
// 删除资金成本
deleteCapitalCost: async (id: number) => {
return await request.delete({ url: `/crm/capital-cost/delete?id=` + id })
},
// 导出资金成本 Excel
exportCapitalCost: async (params) => {
return await request.download({ url: `/crm/capital-cost/export-excel`, params })
},
}

View File

@ -125,6 +125,12 @@ export const changeContract = async (data: TransferReqVO) => {
return await request.put({ url: '/crm/contract/changeContract', data })
}
// 合同变更列表
export const changeContractList = async (data: TransferReqVO) => {
return await request.get({ url: '/crm/contract-change-record/page', data })
}
// 获得待审核合同数量
export const getAuditContractCount = async () => {
return await request.get({ url: '/crm/contract/audit-count' })

View File

@ -112,6 +112,11 @@ export const getCustomer = async (id: number) => {
return await request.get({ url: `/crm/customer/get?id=` + id })
}
// 查询客户详情带报价单
export const getCustomerDetail = async (id: number) => {
return await request.get({ url: `/crm/customer/get-contract-customer-detail?id=` + id })
}
// 新增客户
export const createCustomer = async (data: CustomerVO) => {
return await request.post({ url: `/crm/customer/create`, data })

View File

@ -0,0 +1,42 @@
import request from '@/config/axios'
// 项目难度 VO
export interface ProjectDifficultyVO {
id: number // 主键ID
projectDifficulty: string // 项目难度
dockingMode: string // 说明
dockingHours: number // 对接工时(人天)
}
// 项目难度 API
export const ProjectDifficultyApi = {
// 查询项目难度分页
getProjectDifficultyPage: async (params: any) => {
return await request.get({ url: `/crm/project-difficulty/page`, params })
},
// 查询项目难度详情
getProjectDifficulty: async (id: number) => {
return await request.get({ url: `/crm/project-difficulty/get?id=` + id })
},
// 新增项目难度
createProjectDifficulty: async (data: ProjectDifficultyVO) => {
return await request.post({ url: `/crm/project-difficulty/create`, data })
},
// 修改项目难度
updateProjectDifficulty: async (data: ProjectDifficultyVO) => {
return await request.put({ url: `/crm/project-difficulty/update`, data })
},
// 删除项目难度
deleteProjectDifficulty: async (id: number) => {
return await request.delete({ url: `/crm/project-difficulty/delete?id=` + id })
},
// 导出项目难度 Excel
exportProjectDifficulty: async (params) => {
return await request.download({ url: `/crm/project-difficulty/export-excel`, params })
},
}

View File

@ -44,6 +44,11 @@ export const QuotationApi = {
getSimpleQuotationList : async () => {
return await request.get({ url: `/crm/quotation/simple-all-list` })
},
// 获得 CRM 方案营收分析
getRevenueList : async (data: any) => {
return await request.post({ url: `/crm/quotation/get-revenue-analysis`,
data })
},
// 获得 CRM 方案列表(精简)
getSelfSimpleQuotationList : async () => {
@ -75,6 +80,11 @@ export const QuotationApi = {
return await request.put({ url: `/crm/quotation/submit?id=${id}` })
},
// 客户确认
enterQuotation: async (data: QuotationVO) => {
return await request.post({ url: `/crm/quotation/customer-confirm`, data })
},
// ==================== 子表(CRM 报价产品关联) ====================
// 获得CRM 报价产品关联列表

View File

@ -0,0 +1,46 @@
import request from '@/config/axios'
// 服务费收取方式 VO
export interface ServiceFeeCollectionMethodVO {
id: number // 主键
type: number // 类型
productDetail: string // 产品明细
customerPrice: number // 客单价
unit: number // 单位
procurementCost: number // 采购成本
profit: number // 客单价 - 采购成本
describe: string // 说明
}
// 服务费收取方式 API
export const ServiceFeeCollectionMethodApi = {
// 查询服务费收取方式分页
getServiceFeeCollectionMethodPage: async (params: any) => {
return await request.get({ url: `/crm/service-fee-collection-method/page`, params })
},
// 查询服务费收取方式详情
getServiceFeeCollectionMethod: async (id: number) => {
return await request.get({ url: `/crm/service-fee-collection-method/get?id=` + id })
},
// 新增服务费收取方式
createServiceFeeCollectionMethod: async (data: ServiceFeeCollectionMethodVO) => {
return await request.post({ url: `/crm/service-fee-collection-method/create`, data })
},
// 修改服务费收取方式
updateServiceFeeCollectionMethod: async (data: ServiceFeeCollectionMethodVO) => {
return await request.put({ url: `/crm/service-fee-collection-method/update`, data })
},
// 删除服务费收取方式
deleteServiceFeeCollectionMethod: async (id: number) => {
return await request.delete({ url: `/crm/service-fee-collection-method/delete?id=` + id })
},
// 导出服务费收取方式 Excel
exportServiceFeeCollectionMethod: async (params) => {
return await request.download({ url: `/crm/service-fee-collection-method/export-excel`, params })
},
}

View File

@ -0,0 +1,51 @@
import request from '@/config/axios'
// 技术对接清单 VO
export interface TechnicalDockingChecklistVO {
id: number // 主键ID
name: string // 平台名称
dockingMode: number // 对接模式
dockingHours: number // 技术对接人力(小时)
singleSignOn: number // 单点登录
architectureSynchronization: number // 架构同步
businessTripSynchronization: number // 出差单同步
orderSynchronization: number // 订单同步
billSynchronization: number // 账单同步
approvalMessageSynchronization: number // 审批/消息同步
dockingProductSynchronization: number // 对接产品类型
progress: number // 进度
notes: string // 备注
}
// 技术对接清单 API
export const TechnicalDockingChecklistApi = {
// 查询技术对接清单分页
getTechnicalDockingChecklistPage: async (params: any) => {
return await request.get({ url: `/crm/technical-docking-checklist/page`, params })
},
// 查询技术对接清单详情
getTechnicalDockingChecklist: async (id: number) => {
return await request.get({ url: `/crm/technical-docking-checklist/get?id=` + id })
},
// 新增技术对接清单
createTechnicalDockingChecklist: async (data: TechnicalDockingChecklistVO) => {
return await request.post({ url: `/crm/technical-docking-checklist/create`, data })
},
// 修改技术对接清单
updateTechnicalDockingChecklist: async (data: TechnicalDockingChecklistVO) => {
return await request.put({ url: `/crm/technical-docking-checklist/update`, data })
},
// 删除技术对接清单
deleteTechnicalDockingChecklist: async (id: number) => {
return await request.delete({ url: `/crm/technical-docking-checklist/delete?id=` + id })
},
// 导出技术对接清单 Excel
exportTechnicalDockingChecklist: async (params) => {
return await request.download({ url: `/crm/technical-docking-checklist/export-excel`, params })
},
}

View File

@ -0,0 +1,45 @@
import request from '@/config/axios'
// 项目奖金 VO
export interface ProjectBonusVO {
id: number // 唯一主键
projectId: number // 项目ID
userId: number // 员工ID
bonusPercentage: number // 奖金百分比
bonusAmount: number // 奖金金额
bonusIssueDate: Date // 奖金发放日期
bonusState: number // 奖金发放状态
}
// 项目奖金 API
export const ProjectBonusApi = {
// 查询项目奖金分页
getProjectBonusPage: async (params: any) => {
return await request.get({ url: `/tts/project-bonus/page`, params })
},
// 查询项目奖金详情
getProjectBonus: async (id: number) => {
return await request.get({ url: `/tts/project-bonus/get?id=` + id })
},
// 新增项目奖金
createProjectBonus: async (data: ProjectBonusVO) => {
return await request.post({ url: `/tts/project-bonus/create`, data })
},
// 修改项目奖金
updateProjectBonus: async (data: ProjectBonusVO) => {
return await request.put({ url: `/tts/project-bonus/update`, data })
},
// 删除项目奖金
deleteProjectBonus: async (id: number) => {
return await request.delete({ url: `/tts/project-bonus/delete?id=` + id })
},
// 导出项目奖金 Excel
exportProjectBonus: async (params) => {
return await request.download({ url: `/tts/project-bonus/export-excel`, params })
}
}

View File

@ -0,0 +1,56 @@
import request from '@/config/axios'
// 工作简报 VO
export interface BriefWorkVO {
id: number // 唯一主键
briefType: number // 简报类型:1-日报,2-周报,3-月报
briefCount: number // 周报为周数/月报为月数
briefStartDate: Date // 简报开始日期
briefEndDate: Date // 简报结束日期
selectCustomer: boolean // 是否选择客户
workComplate: string // 本周期完成工作
workPlan: string // 下周期工作计划
workAssistance: string // 工作困难及协助
workExperience: string // 工作心得分享
attachment: string // 附件
processInstanceId: string // 工作流编号
auditStatus: number // 审批状态
}
// 工作简报 API
export const BriefWorkApi = {
// 查询工作简报分页
getBriefWorkPage: async (params: any) => {
return await request.get({ url: `/tts/brief-work/page`, params })
},
// 查询工作简报分页-根据周报查询
getBriefWorkTimePage: async (params: any) => {
return await request.get({ url: `/tts/brief-work/page`, params })
},
// 查询工作简报详情
getBriefWork: async (params: any) => {
return await request.get({ url: `/tts/brief-work/get`, params})
},
// 新增工作简报
createBriefWork: async (data: BriefWorkVO) => {
return await request.post({ url: `/tts/brief-work/create`, data })
},
// 修改工作简报
updateBriefWork: async (data: BriefWorkVO) => {
return await request.put({ url: `/tts/brief-work/update`, data })
},
// 删除工作简报
deleteBriefWork: async (id: number) => {
return await request.delete({ url: `/tts/brief-work/delete?id=` + id })
},
// 导出工作简报 Excel
exportBriefWork: async (params) => {
return await request.download({ url: `/tts/brief-work/export-excel`, params })
}
}

View File

@ -0,0 +1,40 @@
import request from '@/config/axios'
// 工作简报客户 VO
export interface BriefWorkCustomerVO {
briefId: number // 简报ID
customerId: number // 客户Id
}
// 工作简报客户 API
export const BriefWorkCustomerApi = {
// 查询工作简报客户分页
getBriefWorkCustomerPage: async (params: any) => {
return await request.get({ url: `/tts/brief-work-customer/page`, params })
},
// 查询工作简报客户详情
getBriefWorkCustomer: async (id: number) => {
return await request.get({ url: `/tts/brief-work-customer/get?id=` + id })
},
// 新增工作简报客户
createBriefWorkCustomer: async (data: BriefWorkCustomerVO) => {
return await request.post({ url: `/tts/brief-work-customer/create`, data })
},
// 修改工作简报客户
updateBriefWorkCustomer: async (data: BriefWorkCustomerVO) => {
return await request.put({ url: `/tts/brief-work-customer/update`, data })
},
// 删除工作简报客户
deleteBriefWorkCustomer: async (id: number) => {
return await request.delete({ url: `/tts/brief-work-customer/delete?id=` + id })
},
// 导出工作简报客户 Excel
exportBriefWorkCustomer: async (params) => {
return await request.download({ url: `/tts/brief-work-customer/export-excel`, params })
}
}

View File

@ -0,0 +1,51 @@
import request from '@/config/axios'
// 上线申请 VO
export interface OnlineApplicationVO {
id: number // 唯一主键
projectName: string // 产品名称
projectType: number // 项目类型
deptId: number // 申请部门
userId: number // 申请人
testCompletionDate: Date // 测试完成日期
onlineDate: Date // 申请上线日期
externalSystem: string // 上线系统是否涉及第三方系统(如:是/否)
precautions: string // 上线注意事项
emergencyManagement: string // 失败应急处理
applicantSign: string // 申请人签字
processInstanceId: string // 工作流编号
auditStatus: number // 审批状态
}
// 上线申请 API
export const OnlineApplicationApi = {
// 查询上线申请分页
getOnlineApplicationPage: async (params: any) => {
return await request.get({ url: `/tts/online-application/page`, params })
},
// 查询上线申请详情
getOnlineApplication: async (id: number) => {
return await request.get({ url: `/tts/online-application/get?id=` + id })
},
// 新增上线申请
createOnlineApplication: async (data: OnlineApplicationVO) => {
return await request.post({ url: `/tts/online-application/create`, data })
},
// 修改上线申请
updateOnlineApplication: async (data: OnlineApplicationVO) => {
return await request.put({ url: `/tts/online-application/update`, data })
},
// 删除上线申请
deleteOnlineApplication: async (id: number) => {
return await request.delete({ url: `/tts/online-application/delete?id=` + id })
},
// 导出上线申请 Excel
exportOnlineApplication: async (params) => {
return await request.download({ url: `/tts/online-application/export-excel`, params })
}
}

View File

@ -0,0 +1,43 @@
import request from '@/config/axios'
// 上线模块信息 VO
export interface OnlineModuleInfoVO {
id: number // 唯一主键
onlineApplicationId: number // 上线申请ID
involvingModule: string // 涉及模块
version: string // 版本
moduleDescription: string // 模块说明
}
// 上线模块信息 API
export const OnlineModuleInfoApi = {
// 查询上线模块信息分页
getOnlineModuleInfoPage: async (params: any) => {
return await request.get({ url: `/tts/online-module-info/page`, params })
},
// 查询上线模块信息详情
getOnlineModuleInfo: async (id: number) => {
return await request.get({ url: `/tts/online-module-info/get?id=` + id })
},
// 新增上线模块信息
createOnlineModuleInfo: async (data: OnlineModuleInfoVO) => {
return await request.post({ url: `/tts/online-module-info/create`, data })
},
// 修改上线模块信息
updateOnlineModuleInfo: async (data: OnlineModuleInfoVO) => {
return await request.put({ url: `/tts/online-module-info/update`, data })
},
// 删除上线模块信息
deleteOnlineModuleInfo: async (id: number) => {
return await request.delete({ url: `/tts/online-module-info/delete?id=` + id })
},
// 导出上线模块信息 Excel
exportOnlineModuleInfo: async (params) => {
return await request.download({ url: `/tts/online-module-info/export-excel`, params })
}
}

View File

@ -0,0 +1,56 @@
import request from '@/config/axios'
// 项目管理 VO
export interface ProjectVO {
id: number // 唯一主键
projectCode: string // 项目编号
name: string // 项目名称
projectLevel: number // 项目级别,字典值:简单、一般、中等、困难、比较困难
projectType: number // 项目类型,字典值
userId: number // 项目负责人ID
startDate: Date // 项目开始日期
endDate: Date // 项目结束日期
attachment: string // 项目附件
bonusEvaluation: boolean // 立项奖金评估,1: 是,2: 否
bonusAmount: number // 立项奖金金额
bonusIssueDate: Date // 奖金发放日期
background: string // 项目背景
introduction: string // 项目简介
expectation: string // 项目预期成果
progress: number // 项目进度
processInstanceId: string // 工作流编号
auditStatus: number // 审批状态
}
// 项目管理 API
export const ProjectApi = {
// 查询项目管理分页
getProjectPage: async (params: any) => {
return await request.get({ url: `/tts/project/page`, params })
},
// 查询项目管理详情
getProject: async (id: number) => {
return await request.get({ url: `/tts/project/get?id=` + id })
},
// 新增项目管理
createProject: async (data: ProjectVO) => {
return await request.post({ url: `/tts/project/create`, data })
},
// 修改项目管理
updateProject: async (data: ProjectVO) => {
return await request.put({ url: `/tts/project/update`, data })
},
// 删除项目管理
deleteProject: async (id: number) => {
return await request.delete({ url: `/tts/project/delete?id=` + id })
},
// 导出项目管理 Excel
exportProject: async (params) => {
return await request.download({ url: `/tts/project/export-excel`, params })
}
}

View File

@ -0,0 +1,51 @@
import request from '@/config/axios'
// 任务 VO
export interface ProjectTaskVO {
id: number // 唯一主键
taskName: string // 任务名称
userId: number // 任务负责人
taskLevel: number // 任务优先级
startDate: Date // 开始日期
endDate: Date // 截止日期
attachment: string // 任务附件
taskDescribe: string // 任务描述
taskProgress: number // 任务进度
projectId: number // 项目Id
taskState: number // 任务状态
processInstanceId: string // 工作流编号
auditStatus: number // 审批状态
}
// 任务 API
export const ProjectTaskApi = {
// 查询任务分页
getProjectTaskPage: async (params: any) => {
return await request.get({ url: `/tts/project-task/page`, params })
},
// 查询任务详情
getProjectTask: async (id: number) => {
return await request.get({ url: `/tts/project-task/get?id=` + id })
},
// 新增任务
createProjectTask: async (data: ProjectTaskVO) => {
return await request.post({ url: `/tts/project-task/create`, data })
},
// 修改任务
updateProjectTask: async (data: ProjectTaskVO) => {
return await request.put({ url: `/tts/project-task/update`, data })
},
// 删除任务
deleteProjectTask: async (id: number) => {
return await request.delete({ url: `/tts/project-task/delete?id=` + id })
},
// 导出任务 Excel
exportProjectTask: async (params) => {
return await request.download({ url: `/tts/project-task/export-excel`, params })
}
}

View File

@ -0,0 +1,20 @@
<template>
<ContentWrap :bodyStyle="{ padding: '0px' }" class="!mb-0">
<IFrame :src="src" />
</ContentWrap>
</template>
<script lang="ts" setup>
defineOptions({ name: 'previewDoc' })
const { query } = useRoute() // 查询参数
const src = ref('')
console.log('%csrccomponents/IFrame/src/preview.vue:13 query.fileExtension', 'color: #007acc;', query.src);
if(query.type === 'pdf') {
src.value = decodeURIComponent(query.src)
} else {
src.value = 'https://view.officeapps.live.com/op/embed.aspx?src='+query.src
}
</script>

View File

@ -0,0 +1,176 @@
<template>
<Dialog title="添加客户" v-model="dialogVisible" width="50%">
<!-- 搜索工作栏 -->
<el-form
class="-mb-15px"
:model="queryParams"
ref="queryFormRef"
:inline="true"
label-width="100px"
>
<el-form-item label="任务标题" prop="taskName">
<el-input
v-model="queryParams.taskName"
class="!w-240px"
clearable
placeholder="请输入任务标题"
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item>
<el-button @click="handleQuery">
<Icon class="mr-5px" icon="ep:search" />
搜索
</el-button>
<el-button @click="resetQuery">
<Icon class="mr-5px" icon="ep:refresh" />
重置
</el-button>
<!-- <el-button v-hasPermi="['crm:customer:create']" type="primary" @click="openForm('create')">
<Icon class="mr-5px" icon="ep:plus" />
新增联系人
</el-button> -->
</el-form-item>
</el-form>
<!-- 列表 -->
<ContentWrap>
<el-table ref="multipleTableRef" v-loading="loading" :data="list" @selection-change="handleSelectionChange" :row-key="getRowKey" :stripe="true" :show-overflow-tooltip="true">
<el-table-column label="序号" align="center" type="selection" :reserve-selection="true" />
<el-table-column label="任务标题" align="center" prop="taskName" />
<el-table-column label="任务编号" align="center" prop="taskNo" width="150" />
<!-- <el-table-column align="center" label="电话" prop="telephone" width="130" />
<el-table-column align="center" label="职位" prop="post" width="120" /> -->
</el-table>
<!-- 分页 -->
<Pagination
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
</ContentWrap>
<template #footer>
<el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
<el-button @click="dialogVisible = false">取 消</el-button>
</template>
</Dialog>
<!-- 表单弹窗:添加/修改 -->
<CustomerForm ref="formRef" @success="getList" />
</template>
<script setup lang="ts">
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
import * as ContactApi from '@/api/crm/contact'
import { dateFormatter } from '@/utils/formatTime'
import CustomerForm from '@/views/crm/customer/CustomerForm.vue'
import download from '@/utils/download'
import { BillTemplateApi, BillTemplateVO } from '@/api/crm/billtemplate'
import { ProjectTaskApi, ProjectTaskVO } from '@/api/task/task'
import { propTypes } from '@/utils/propTypes'
import { useUserStore } from '@/store/modules/user'
/** 票据模版 列表 */
defineOptions({ name: 'BillTemplate' })
const message = useMessage() // 消息弹窗
const { t } = useI18n() // 国际化
const loading = ref(true) // 列表的加载中
const list = ref<BillTemplateVO[]>([]) // 列表的数据
const total = ref(0) // 列表的总页数
const multipleSelection = ref([])
const dialogVisible = ref(false) // 弹窗的是否展示
const multipleTableRef = ref()
const selectedRowKeys = ref<number[]>([]);
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
userId: undefined,
taskName: undefined,
taskState: 2,
customerId: undefined
})
const queryFormRef = ref() // 搜索的表单
const exportLoading = ref(false) // 导出的加载中
/** 打开弹窗 */
const open = async (data: [], id) => {
dialogVisible.value = true
multipleSelection.value = data
queryParams.customerId = id
await getList()
await setSelections()
}
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
// 跨页唯一id
const getRowKey = (row) => {
return row.id
}
/** 添加/修改操作 */
const formRef = ref()
const openForm = (type: string, id?: number) => {
formRef.value.open(type,id, queryParams.customerId)
}
/** 查询列表 */
const getList = async () => {
loading.value = true
try {
queryParams.userId = useUserStore().getUser.id
const data = await ProjectTaskApi.getProjectTaskPage(queryParams)
list.value = data.list
total.value = data.total
} finally {
loading.value = false
}
}
const handleSelectionChange = (val: []) => {
multipleSelection.value = val
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.pageNo = 1
getList()
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value.resetFields()
multipleTableRef.value.clearSelection()
handleQuery()
}
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
const submitForm = async () => {
// 提交请求
dialogVisible.value = false
// 发送操作成功的事件
emit('success', multipleSelection.value)
}
const setSelections = async () => {
const selections = multipleSelection.value.map(item => {
return {
id: item.id
}
})
if (selections && selections.length > 0) {
list.value.forEach((row: any) => {
if (selections.some(item => item.id === row.id)) {
multipleTableRef.value.toggleRowSelection(row, true); // 设置选择状态。
}
});
} else {
multipleTableRef.value.clearSelection(); // 如果没有保存的选择,则清空当前页的选择。
}
}
</script>

View File

@ -59,7 +59,14 @@
<div v-for="(file, index) in fileList" :key="index" class="flex items-center file-list-item">
<span>{{ file.name }}</span>
<div class="ml-10px">
<el-link :href="file.url" :underline="false" download target="_blank" type="primary">
<el-link
@click="previewFile(file.url)"
:underline="false"
type="primary"
>
预览
</el-link>
<el-link class="ml-10px" :href="file.url" :underline="false" download target="_blank" type="primary">
下载
</el-link>
</div>
@ -195,6 +202,25 @@ const emitUpdateModelValue = () => {
}
emit('update:modelValue', result)
}
//预览
const router = useRouter() // 路
const previewFile = (fileUrls) => {
// // 假设 fileUrls 是一个字符串,包含文件的完整 URL
if (!fileUrls) {
message.error('没有附件');
return;
}
// 检查文件类型
const fileExtension = fileUrls.split('.').pop().toLowerCase();
router.push({
name: 'FileTemplatePreview',
query: {
src: encodeURIComponent(fileUrls),
type: fileExtension
}
})
}
</script>
<style lang="scss" scoped>
.upload-file-uploader {

View File

@ -26,7 +26,10 @@
<Icon class="mr-5px" icon="ep:refresh" />
重置
</el-button>
<el-button v-hasPermi="['crm:contact:create']" type="primary" @click="openForm('create')">
<Icon class="mr-5px" icon="ep:plus" />
新增联系人
</el-button>
</el-form-item>
</el-form>
@ -54,12 +57,16 @@
</template>
</Dialog>
<!-- 表单弹窗:添加/修改 -->
<ContactForm ref="formRef" @success="getList" />
</template>
<script setup lang="ts">
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
import * as ContactApi from '@/api/crm/contact'
import { dateFormatter } from '@/utils/formatTime'
import ContactForm from '@/views/crm/contact/ContactForm.vue'
import download from '@/utils/download'
import { BillTemplateApi, BillTemplateVO } from '@/api/crm/billtemplate'
@ -88,7 +95,6 @@ const exportLoading = ref(false) // 导出的加载中
const open = async (data: [], id) => {
dialogVisible.value = true
multipleSelection.value = data
console.log('%csrc/components/contact/index.vue:91 id', 'color: #007acc;', id);
queryParams.customerId = id
await getList()
await setSelections()
@ -100,6 +106,12 @@ const getRowKey = (row) => {
return row.id
}
/** 添加/修改操作 */
const formRef = ref()
const openForm = (type: string, id?: number) => {
formRef.value.open(type,id, queryParams.customerId)
}
/** 查询列表 */
const getList = async () => {
loading.value = true
@ -139,7 +151,7 @@ const submitForm = async () => {
const setSelections = async () => {
const selections = multipleSelection.value.map(item => {
return {
id: item.id
id: item.customerContactId
}
})
console.log('%csrc/components/product/index.vue:153 list.value', 'color: #007acc;', multipleSelection);

View File

@ -0,0 +1,171 @@
<template>
<Dialog title="添加客户" v-model="dialogVisible" width="50%">
<!-- 搜索工作栏 -->
<el-form
class="-mb-15px"
:model="queryParams"
ref="queryFormRef"
:inline="true"
label-width="100px"
>
<el-form-item label="客户名称" prop="name">
<el-input
v-model="queryParams.name"
class="!w-240px"
clearable
placeholder="请输入客户名称"
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item>
<el-button @click="handleQuery">
<Icon class="mr-5px" icon="ep:search" />
搜索
</el-button>
<el-button @click="resetQuery">
<Icon class="mr-5px" icon="ep:refresh" />
重置
</el-button>
<!-- <el-button v-hasPermi="['crm:customer:create']" type="primary" @click="openForm('create')">
<Icon class="mr-5px" icon="ep:plus" />
新增联系人
</el-button> -->
</el-form-item>
</el-form>
<!-- 列表 -->
<ContentWrap>
<el-table ref="multipleTableRef" v-loading="loading" :data="list" @selection-change="handleSelectionChange" :row-key="getRowKey" :stripe="true" :show-overflow-tooltip="true">
<el-table-column label="序号" align="center" type="selection" :reserve-selection="true" />
<el-table-column label="客户姓名" align="center" prop="name" />
<!-- <el-table-column align="center" label="电话" prop="telephone" width="130" />
<el-table-column align="center" label="职位" prop="post" width="120" /> -->
</el-table>
<!-- 分页 -->
<Pagination
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
</ContentWrap>
<template #footer>
<el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
<el-button @click="dialogVisible = false">取 消</el-button>
</template>
</Dialog>
<!-- 表单弹窗:添加/修改 -->
<CustomerForm ref="formRef" @success="getList" />
</template>
<script setup lang="ts">
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
import * as ContactApi from '@/api/crm/contact'
import { dateFormatter } from '@/utils/formatTime'
import CustomerForm from '@/views/crm/customer/CustomerForm.vue'
import download from '@/utils/download'
import { BillTemplateApi, BillTemplateVO } from '@/api/crm/billtemplate'
import * as CustomerApi from '@/api/crm/customer';
/** 票据模版 列表 */
defineOptions({ name: 'BillTemplate' })
const message = useMessage() // 消息弹窗
const { t } = useI18n() // 国际化
const loading = ref(true) // 列表的加载中
const list = ref<BillTemplateVO[]>([]) // 列表的数据
const total = ref(0) // 列表的总页数
const multipleSelection = ref([])
const dialogVisible = ref(false) // 弹窗的是否展示
const multipleTableRef = ref()
const selectedRowKeys = ref<number[]>([]);
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
sceneType: '1', // 默认和 activeName 相等
name: undefined,
customerId: undefined
})
const queryFormRef = ref() // 搜索的表单
const exportLoading = ref(false) // 导出的加载中
/** 打开弹窗 */
const open = async (data: []) => {
dialogVisible.value = true
multipleSelection.value = data
// queryParams.customerId = id
await getList()
await setSelections()
}
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
// 跨页唯一id
const getRowKey = (row) => {
return row.id
}
/** 添加/修改操作 */
const formRef = ref()
const openForm = (type: string, id?: number) => {
formRef.value.open(type,id, queryParams.customerId)
}
/** 查询列表 */
const getList = async () => {
loading.value = true
try {
const data = await CustomerApi.getCustomerPage(queryParams);
list.value = data.list
total.value = data.total
} finally {
loading.value = false
}
}
const handleSelectionChange = (val: []) => {
multipleSelection.value = val
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.pageNo = 1
getList()
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value.resetFields()
multipleTableRef.value.clearSelection()
handleQuery()
}
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
const submitForm = async () => {
// 提交请求
dialogVisible.value = false
// 发送操作成功的事件
emit('success', multipleSelection.value)
}
const setSelections = async () => {
const selections = multipleSelection.value.map(item => {
return {
id: item.id
}
})
if (selections && selections.length > 0) {
list.value.forEach((row: any) => {
if (selections.some(item => item.id === row.id)) {
multipleTableRef.value.toggleRowSelection(row, true); // 设置选择状态。
}
});
} else {
multipleTableRef.value.clearSelection(); // 如果没有保存的选择,则清空当前页的选择。
}
}
</script>

View File

@ -42,6 +42,8 @@ import Logger from '@/utils/Logger'
import VueDOMPurifyHTML from 'vue-dompurify-html' // 解决v-html 的安全隐患
import VueSignaturePad from "vue-signature-pad";
// 创建实例
const setupAll = async () => {
const app = createApp(App)
@ -65,6 +67,7 @@ const setupAll = async () => {
await router.isReady()
app.use(VueDOMPurifyHTML)
app.use(VueSignaturePad);
app.mount('#app')
}

View File

@ -512,12 +512,23 @@ const remainingRouter: AppRouteRecordRaw[] = [
},
component: () => import('@/views/crm/clue/detail/index.vue')
},
{
path: 'customer/HighSeasDetail/:id',
name: 'CrmCustomerHighSeasDetail',
meta: {
title: '客户公海详情',
noCache: false,
hidden: true,
activeMenu: '/crm/customer/pool'
},
component: () => import('@/views/crm/customer/detail/highSeasIndex.vue')
},
{
path: 'customer/detail/:id',
name: 'CrmCustomerDetail',
meta: {
title: '客户详情',
noCache: true,
noCache: false,
hidden: true,
activeMenu: '/crm/customer'
},
@ -614,7 +625,7 @@ const remainingRouter: AppRouteRecordRaw[] = [
component: () => import('@/views/crm/business/BusinessForm.vue')
},
{
path: 'contract/add',
path: 'contract/ContractForm',
name: 'CrmContractAdd',
meta: {
title: '合同新增',
@ -773,9 +784,183 @@ const remainingRouter: AppRouteRecordRaw[] = [
title: '合同预览',
activeMenu: '/crm/filetemplate'
}
},
{
path: 'filetemplate/preview',
component: () => import('@/components/IFrame/src/preview.vue'),
name: 'FileTemplatePreview',
meta: {
noCache: true,
hidden: true,
canTo: true,
title: '文件预览',
activeMenu: '/filetemplate/preview'
}
}
]
},
{
path: '/project',
component: Layout,
name: 'ProCenter',
meta: { hidden: true },
children: [
{
path: 'project/ProjectForm',
name: 'ProProAdd',
meta: {
title: '项目管理新增',
noCache: true,
hidden: true,
activeMenu: '/project/project'
},
component: () => import('@/views/project/project/ProjectForm.vue')
},
{
path: 'project/ProjectFormEdit',
name: 'ProProEdit',
meta: {
title: '项目管理编辑',
noCache: true,
hidden: true,
activeMenu: '/project/project'
},
component: () => import('@/views/project/project/ProjectForm.vue')
},
{
path: 'project/ProjectDetail',
name: 'ProProDetail',
meta: {
title: '项目管理详情',
noCache: true,
hidden: true,
activeMenu: '/project/project'
},
component: () => import('@/views/project/project/ProjectDetail.vue')
},
{
path: 'components/TaskDetail',
name: 'ProComDetail',
meta: {
title: '任务详情',
noCache: true,
hidden: true,
activeMenu: '/project/project'
},
component: () => import('@/views/project/project/components/TaskDetail.vue')
},
{
path: 'components/TaskAdd',
name: 'ProComAdd',
meta: {
title: '任务新增',
noCache: true,
hidden: true,
activeMenu: '/project/project'
},
component: () => import('@/views/project/project/components/TaskAdd.vue')
},
{
path: 'online/OnlineApplicationForm',
name: 'OnlineAppAdd',
meta: {
title: '上线申请新增',
noCache: true,
hidden: true,
activeMenu: '/project/online'
},
component: () => import('@/views/project/online/OnlineApplicationForm.vue')
},
{
path: 'online/OnlineApplicationEdit',
name: 'OnlineAppEdit',
meta: {
title: '上线申请编辑',
noCache: true,
hidden: true,
activeMenu: '/project/online'
},
component: () => import('@/views/project/online/OnlineApplicationForm.vue')
},
{
path: 'online/OnlineApplicationDetail',
name: 'OnlineAppDetail',
meta: {
title: '上线申请详情',
noCache: true,
hidden: true,
activeMenu: '/project/online'
},
component: () => import('@/views/project/online/OnlineApplicationDetail.vue')
},
{
path: 'briefWork/BriefWorkDetail',
name: 'DayReportDetail',
meta: {
title: '日报详情',
noCache: true,
hidden: true,
activeMenu: '/project/briefWork'
},
component: () => import('@/views/project/briefWork/BriefWorkDetail.vue')
},
{
path: 'briefWork/BriefWorkForm',
name: 'DayReportAdd',
meta: {
title: '日报新增',
noCache: true,
hidden: true,
activeMenu: '/project/briefWork'
},
component: () => import('@/views/project/briefWork/BriefWorkForm.vue')
},
{
path: 'briefWeekWork/BriefworkWeekDetail',
name: 'WeekReportDetail',
meta: {
title: '周报详情',
noCache: true,
hidden: true,
activeMenu: '/project/bridfWeekWork'
},
component: () => import('@/views/project/briefWeekWork/BriefWorkWeekDetail.vue')
},
{
path: 'briefWeekWork/BriefWorkWeekForm',
name: 'WeekReportAdd',
meta: {
title: '周报新增',
noCache: true,
hidden: true,
activeMenu: '/project/bridfWeekWork'
},
component: () => import('@/views/project/briefWeekWork/BriefWorkWeekForm.vue')
},
{
path: 'briefMonthWork/BriefWorkMonthDetail',
name: 'MonthReportDetail',
meta: {
title: '月报详情',
noCache: true,
hidden: true,
activeMenu: '/project/bridfWeekWork'
},
component: () => import('@/views/project/briefMonthWork/BriefMonthWorkDetail.vue')
},
{
path: 'briefMonthWork/BriefWorkMonthForm',
name: 'MonthReportAdd',
meta: {
title: '月报新增',
noCache: true,
hidden: true,
activeMenu: '/project/bridfWeekWork'
},
component: () => import('@/views/project/briefMonthWork/BriefWorkMonthForm.vue')
},
]
},
{
path: '/ai',
component: Layout,

View File

@ -72,23 +72,23 @@ export const useAppStore = defineStore('app', {
currentSize: wsCache.get('default') || 'default', // 组件尺寸
theme: wsCache.get(CACHE_KEY.THEME) || {
// 主题色
elColorPrimary: '#409eff',
elColorPrimary: '#F9AA25',
// 左侧菜单边框颜色
leftMenuBorderColor: 'inherit',
// 左侧菜单背景颜色
leftMenuBgColor: '#001529',
leftMenuBgColor: '#fff',
// 左侧菜单浅色背景颜色
leftMenuBgLightColor: '#0f2438',
leftMenuBgLightColor: '#fff',
// 左侧菜单选中背景颜色
leftMenuBgActiveColor: 'var(--el-color-primary)',
leftMenuBgActiveColor: '#fef5e5',
// 左侧菜单收起选中背景颜色
leftMenuCollapseBgActiveColor: 'var(--el-color-primary)',
// 左侧菜单字体颜色
leftMenuTextColor: '#bfcbd9',
leftMenuTextColor: 'inherit',
// 左侧菜单选中字体颜色
leftMenuTextActiveColor: '#fff',
leftMenuTextActiveColor: '#F9AA25',
// logo字体颜色
logoTitleTextColor: '#fff',
logoTitleTextColor: '#F9AA25',
// logo边框颜色
logoBorderColor: 'inherit',
// 头部背景颜色

View File

@ -202,6 +202,7 @@ export enum DICT_TYPE {
// ========== CRM - 客户管理模块 ==========
CRM_AUDIT_STATUS = 'crm_audit_status', // CRM 审批状态
CRM_CONFIRM_STATUS = 'crm_confirm_status', // CRM 客户确认状态
CRM_BIZ_TYPE = 'crm_biz_type', // CRM 业务类型
CRM_BUSINESS_END_STATUS_TYPE = 'crm_business_end_status_type', // CRM 商机结束状态类型
CRM_RECEIVABLE_RETURN_TYPE = 'crm_receivable_return_type', // CRM 回款的还款方式
@ -231,6 +232,19 @@ export enum DICT_TYPE {
// 商机账期
PAYMENT_TERM = "payment_term", // 投诉级别
CREDIT_CALC_CYCLE = "credit_calc_cycle",//授信计算周期
DOCKING_MODE="docking_mode",
CRM_PROGRESS="crm_progess", //进度
SYNCHRONIZATION_STATUS="synchronization_status", //同步状态
MATCHING_PRODUCT_TYPES="matching_product_types", //对接产品类型
CRM_SERVICE_FEE_COLLECTION_METHOD_TYPE = 'crm_service_fee_collection_method_type', // 服务费收取方式类型
// ========== ERP - 企业资源计划模块 ==========
// ========== TTS - 项目模块 ==========
TTS_PROJECT_LEVEL="tts_project_level", //项目级别
TTS_PROJECT_TYPE="tts_project_type", //项目类型
TTS_TASK_LEVEL = 'tts_task_level', //对接模式
TTS_APPLY_STATUS = 'tts_apply_status',
// ========== ERP - 企业资源计划模块 ==========
@ -266,4 +280,5 @@ export enum DICT_TYPE {
IOT_PLUGIN_TYPE = 'iot_plugin_type', // IOT 插件类型
IOT_DATA_BRIDGE_DIRECTION_ENUM = 'iot_data_bridge_direction_enum', // 桥梁方向
IOT_DATA_BRIDGE_TYPE_ENUM = 'iot_data_bridge_type_enum' // 桥梁类型
}

View File

@ -11,7 +11,7 @@
<el-input v-model="formData.name" placeholder="请输入分类名" />
</el-form-item>
<el-form-item label="分类标志" prop="code">
<el-input v-model="formData.code" placeholder="请输入分类标志" />
<el-input v-model="formData.code" :disabled="formType == 'update'" placeholder="请输入分类标志" />
</el-form-item>
<el-form-item label="分类描述" prop="description">
<el-input v-model="formData.description" type="textarea" placeholder="请输入分类描述" />

View File

@ -214,8 +214,9 @@ const handleProcessDetail = (row: ContractApi.ContractVO) => {
/** 打开合同详情 */
const { push } = useRouter()
const router = useRouter()
const openDetail = (id: number) => {
push({ name: 'CrmContractDetail', params: { id } })
router.push({ name: 'CrmContractDetail', query: { id: id } })
}
/** 打开客户详情 */

View File

@ -320,11 +320,11 @@ watch(
let totalOfflinePrice = 0;
for (const product of newProducts) {
if (typeof product.onlinePrice === 'number') {
totalOnlinePrice += product.onlinePrice;
if (typeof product.onlinePrice) {
totalOnlinePrice += Number(product.onlinePrice);
}
if (typeof product.offlinePrice === 'number') {
totalOfflinePrice += product.offlinePrice;
if (typeof product.offlinePrice) {
totalOfflinePrice += Number(product.offlinePrice);
}
}
formData.value.onlinePrice = totalOnlinePrice.toFixed(2)
@ -372,10 +372,10 @@ const setList = (newProducts) => {
// 改版账期
const changePayment = (val) => {
const currentDate = new Date(); // 获取当前日期
const currentMonth = currentDate.getMonth() + 1; // 获取当前月份(注意月份是从0开始的,所以需要加1)
const daysInMonth = new Date(currentDate.getFullYear(), currentMonth, 0).getDate(); // 获取指定月份的天数
formData.value.creditCalcCycle = (val > 2 ? parseInt(daysInMonth + Number(val)) : 0)
// const currentDate = new Date(); // 获取当前日期
// const currentMonth = currentDate.getMonth() + 1; // 获取当前月份(注意月份是从0开始的,所以需要加1)
// const daysInMonth = new Date(currentDate.getFullYear(), currentMonth, 0).getDate(); // 获取指定月份的天数
formData.value.creditCalcCycle = (val > 2 ? parseInt(30 + Number(val)) : 0)
formData.value.creditLimit = parseInt(formData.value.totalPrice / 365 * formData.value.creditCalcCycle)
}
const { push } = useRouter()

View File

@ -37,7 +37,7 @@
<dict-tag :type="DICT_TYPE.CRM_PRODUCT_UNIT" :value="scope.row.productUnit" />
</template>
</el-table-column>
<el-table-column label="线上价格(元)" fixed="right" min-width="100">
<el-table-column label="自助下单(元)" align="center" fixed="right" min-width="100">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.onlinePrice`" :rules="formRules.onlinePrice" class="mb-0px!" v-if="!disabled">
<el-input
@ -49,7 +49,7 @@
<div v-else>{{row.onlinePrice}}</div>
</template>
</el-table-column>
<el-table-column label="线下价格(元)" fixed="right" min-width="100">
<el-table-column label="客服代订(元)" align="center" fixed="right" min-width="100">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.offlinePrice`" :rules="formRules.offlinePrice" class="mb-0px!" v-if="!disabled">
<el-input
@ -61,7 +61,7 @@
<div v-else>{{row.offlinePrice}}</div>
</template>
</el-table-column>
<el-table-column label="合计(元)" prop="totalPrice" fixed="right" min-width="100">
<el-table-column label="合计(元)" prop="totalPrice" align="center" fixed="right" min-width="100">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.totalPrice`" class="mb-0px!">
{{row.totalPrice}}
@ -114,6 +114,7 @@ watch(
);
// 监听合同产品变化,计算合同产品总价
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
watch(
() => formData.value,
(val) => {
@ -123,11 +124,13 @@ watch(
// 循环处理
val.forEach((item) => {
if (item.offlinePrice != null && item.onlinePrice != null) {
item.totalPrice = item.offlinePrice + item.onlinePrice;
item.totalPrice = Number(item.offlinePrice) + Number(item.onlinePrice);
} else {
item.totalPrice = 0;
}
});
// emit('success', formData.value)
},
{ deep: true }
);
@ -149,7 +152,6 @@ watch(
// };
// formData.value.push(newRow);
// };
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
const getList = (val: []) => {
for(let i = formData.value.length - 1; i >= 0; i--) {
let obj = formData.value[i]

View File

@ -16,28 +16,28 @@
</el-button>
</BusinessDetailsHeader>
<el-col>
<el-tabs>
<el-tab-pane label="跟进记录">
<!-- <el-tabs> -->
<!-- <el-tab-pane label="跟进记录">
<FollowUpList :biz-id="businessId" :biz-type="BizTypeEnum.CRM_BUSINESS" />
</el-tab-pane>
<el-tab-pane label="详细资料">
</el-tab-pane> -->
<!-- <el-tab-pane label="详细资料"> -->
<BusinessDetailsInfo :business="business" />
</el-tab-pane>
<el-tab-pane label="联系人" lazy>
<!-- </el-tab-pane> -->
<!-- <el-tab-pane label="联系人" lazy>
<ContactList
:biz-id="business.id!"
:biz-type="BizTypeEnum.CRM_BUSINESS"
:business-id="business.id"
:customer-id="business.customerId"
/>
</el-tab-pane>
</el-tab-pane> -->
<!-- <el-tab-pane label="产品">
<BusinessProductList :business="business" />
</el-tab-pane> -->
<!-- <el-tab-pane label="合同" lazy>
<ContractList :biz-id="business.id!" :biz-type="BizTypeEnum.CRM_BUSINESS" />
</el-tab-pane> -->
<el-tab-pane label="操作日志">
<!-- <el-tab-pane label="操作日志">
<OperateLogV2 :log-list="logList" />
</el-tab-pane>
<el-tab-pane label="团队成员">
@ -48,8 +48,8 @@
:show-action="true"
@quit-team="close"
/>
</el-tab-pane>
</el-tabs>
</el-tab-pane> -->
<!-- </el-tabs> -->
</el-col>
<!-- 表单弹窗:添加/修改 -->

View File

@ -46,11 +46,11 @@
<!-- 列表 -->
<ContentWrap>
<el-tabs v-model="activeName" @tab-click="handleTabClick">
<!-- <el-tabs v-model="activeName" @tab-click="handleTabClick">
<el-tab-pane label="我负责的" name="1" />
<el-tab-pane label="我参与的" name="2" />
<el-tab-pane label="下属负责的" name="3" />
</el-tabs>
</el-tabs> -->
<el-table v-loading="loading" :data="list" :show-overflow-tooltip="true" :stripe="true">
<el-table-column align="center" fixed="left" label="商机名称" prop="name" width="200">
<template #default="scope">

View File

@ -4,7 +4,7 @@
ref="formRef"
:model="formData"
:rules="formRules"
label-width="130px"
label-width="100px"
v-loading="formLoading"
>
<el-form-item label="项目编码" prop="projectCode">
@ -13,7 +13,7 @@
<el-form-item label="结算账期" prop="paymentTerm">
<el-select v-model="formData.paymentTerm" placeholder="请选择结算账期">
<el-option
v-for="dict in getStrDictOptions(DICT_TYPE.PAYMENT_TERM)"
v-for="dict in getIntDictOptions(DICT_TYPE.PAYMENT_TERM)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
@ -41,7 +41,7 @@
</el-select>
</el-form-item>
<el-form-item label="计算周期" prop="creditCalcCycle">
<el-input v-model="formData.creditCalcCycle" placeholder="请输入授信额度计算周期" />
<el-input v-model="formData.creditCalcCycle" placeholder="请输入计算周期" />
</el-form-item>
<el-form-item label="出票周期" prop="ticketIssuanceCycle">
<el-input v-model="formData.ticketIssuanceCycle" placeholder="请输入出票周期" />
@ -50,7 +50,7 @@
<el-input v-model="formData.settlementCycle" placeholder="请输入结算周期" />
</el-form-item>
<el-form-item label="使用天数" prop="usageDays">
<el-input v-model="formData.usageDays" placeholder="请输入实际资金使用天数" />
<el-input v-model="formData.usageDays" placeholder="请输入使用天数" />
</el-form-item>
<el-form-item label="年利率" prop="annualInterestRate">
<el-input v-model="formData.annualInterestRate" placeholder="请输入年利率" />
@ -66,7 +66,7 @@
</Dialog>
</template>
<script setup lang="ts">
import { getIntDictOptions, getStrDictOptions, DICT_TYPE } from '@/utils/dict'
import { getIntDictOptions, DICT_TYPE } from '@/utils/dict'
import { CapitalCostApi, CapitalCostVO } from '@/api/crm/capitalcost'
/** 资金成本 表单 */
@ -102,7 +102,6 @@ const formRules = reactive({
settlementCycle: [{ required: true, message: '结算周期不能为空', trigger: 'blur' }],
usageDays: [{ required: true, message: '使用天数不能为空', trigger: 'blur' }],
annualInterestRate: [{ required: true, message: '年利率不能为空', trigger: 'blur' }],
// remark: [{ required: true, message: '备注不能为空', trigger: 'blur' }],
})
const formRef = ref() // 表单 Ref
@ -165,4 +164,4 @@ const resetForm = () => {
}
formRef.value?.resetFields()
}
</script>
</script>

View File

@ -25,7 +25,7 @@
class="!w-240px"
>
<el-option
v-for="dict in getStrDictOptions(DICT_TYPE.PAYMENT_TERM)"
v-for="dict in getIntDictOptions(DICT_TYPE.PAYMENT_TERM)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
@ -100,7 +100,7 @@
<!-- 列表 -->
<ContentWrap>
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
<!-- <el-table-column label="序号" align="center" prop="id" />-->
<el-table-column label="主键" align="center" prop="id" />
<el-table-column label="项目编码" align="center" prop="projectCode" />
<el-table-column label="结算账期" align="center" prop="paymentTerm">
<template #default="scope">
@ -120,7 +120,7 @@
<el-table-column label="计算周期" align="center" prop="creditCalcCycle" />
<el-table-column label="出票周期" align="center" prop="ticketIssuanceCycle" />
<el-table-column label="结算周期" align="center" prop="settlementCycle" />
<el-table-column label="资金使用天数" align="center" prop="usageDays" />
<el-table-column label="使用天数" align="center" prop="usageDays" />
<el-table-column label="年利率" align="center" prop="annualInterestRate" />
<el-table-column label="备注" align="center" prop="remark" />
<el-table-column
@ -165,7 +165,7 @@
</template>
<script setup lang="ts">
import { getIntDictOptions, getStrDictOptions, DICT_TYPE } from '@/utils/dict'
import { getIntDictOptions, DICT_TYPE } from '@/utils/dict'
import { dateFormatter } from '@/utils/formatTime'
import download from '@/utils/download'
import { CapitalCostApi, CapitalCostVO } from '@/api/crm/capitalcost'
@ -187,12 +187,6 @@ const queryParams = reactive({
paymentTerm: undefined,
creditMethod: undefined,
applicableProjects: undefined,
creditCalcCycle: undefined,
ticketIssuanceCycle: undefined,
settlementCycle: undefined,
usageDays: undefined,
annualInterestRate: undefined,
remark: undefined,
createTime: [],
})
const queryFormRef = ref() // 搜索的表单
@ -260,4 +254,4 @@ const handleExport = async () => {
onMounted(() => {
getList()
})
</script>
</script>

View File

@ -37,16 +37,23 @@
<el-select
:disabled="formData.customerDefault"
v-model="formData.customerId"
:loading="loading"
loading-text="数据加载中..."
filterable
placeholder="请选择客户"
class="w-1/1"
>
clearable
popper-class="event-select-poper"
v-el-select-loadmore="loadmore"
:placeholder="placeholder"
style="width: 100%"
:filter-method="filterOptions"
@change="handleEventsChange"
@visible-change="handleVisibleChange">
<el-option
v-for="item in customerList"
v-for="item in options"
:key="item.id"
:label="item.name"
:value="item.id"
/>
:value="item.id" />
</el-select>
</el-form-item>
</el-col>
@ -59,7 +66,7 @@
<el-row>
<el-col :span="12">
<el-form-item label="电话" prop="telephone">
<el-input v-model="formData.telephone" placeholder="请输入电话" />
<el-input v-model="formData.telephone" oninput = "value=value.replace(/[^\d]/g,'')" placeholder="请输入电话" />
</el-form-item>
</el-col>
<el-col :span="12">
@ -204,12 +211,26 @@ const formData = ref({
const formRules = reactive({
name: [{ required: true, message: '姓名不能为空', trigger: 'blur' }],
customerId: [{ required: true, message: '客户不能为空', trigger: 'blur' }],
ownerUserId: [{ required: true, message: '负责人不能为空', trigger: 'blur' }]
ownerUserId: [{ required: true, message: '负责人不能为空', trigger: 'blur' }],
email: [{ required: false, message: '电子邮箱不能为空', trigger: 'blur' } ,
{
pattern:
/^(([a-zA-Z0-9_\-\.]+)@([a-zA-Z0-9_\-\.]+)\.([a-zA-Z]{2,5}){1,25})$/,
message: "请输入正确的电子邮箱",
}
],
mobile: [{ required: false, message: '手机号不能为空', trigger: 'blur' } ,
{pattern: /^(1[3-9][0-9]|14[5|7]|15[0|1|2|3|4|5|6|7|8|9]|18[0|1|2|3|5|6|7|8|9])\d{8}$/, message: '请输入正确手机号'}
],
})
const formRef = ref() // 表单 Ref
const userOptions = ref<UserApi.UserVO[]>([]) // 用户列表
const customerList = ref<CustomerApi.CustomerVO[]>([]) // 客户列表
const contactList = ref<ContactApi.ContactVO[]>([]) // 联系人列表
let pageNo = ref(1)
let pageSize = ref(10)
let total = 0
const options = ref([]);
/** 打开弹窗 */
const open = async (type: string, id?: number, customerId?: number, businessId?: number) => {
@ -226,6 +247,7 @@ const open = async (type: string, id?: number, customerId?: number, businessId?:
formLoading.value = false
}
} else {
// console.log('%csrc/views/crm/contact/ContactForm.vue:229 customerId', 'color: #007acc;', customerId);
if (customerId) {
formData.value.customerId = customerId
formData.value.customerDefault = true // 默认客户的选择,不允许变
@ -238,7 +260,8 @@ const open = async (type: string, id?: number, customerId?: number, businessId?:
// 获得联系人列表
contactList.value = await ContactApi.getSimpleContactList()
// 获得客户列表
customerList.value = await CustomerApi.getCustomerSimpleList()
customerList.value = await CustomerApi.getSelfCustomerSimpleList()
options.value = customerList.value.slice(0, 10)
// 获得地区列表
areaList.value = await AreaApi.getAreaTree()
// 获得用户列表
@ -301,4 +324,81 @@ const resetForm = () => {
}
formRef.value?.resetFields()
}
const loading = ref(false);
const placeholder = ref('请选择');
const allFilterEvents = reactive([]);
const vElSelectLoadmore = {
beforeMount(el, binding) {
const selectDom = document.querySelector('.event-select-poper .el-select-dropdown__wrap');
const loadMores = function() {
const isBase = this.scrollHeight - this.scrollTop <= this.clientHeight + 20;
if (isBase) {
binding.value && binding.value();
}
};
el.selectDomInfo = selectDom;
el.selectLoadMore = loadMores;
selectDom?.addEventListener('scroll', loadMores.bind(selectDom));
},
beforeUnmount(el) {
if (el.selectLoadMore) {
el.selectDomInfo.removeEventListener('scroll', el.selectLoadMore);
delete el.selectDomInfo;
delete el.selectLoadMore;
}
}
};
watch(() => allFilterEvents, () => {
let startIndex = pageNo.value * pageSize.value - pageSize.value;
let endIndex = pageNo.value * pageSize.value;
options.value = allFilterEvents.slice(startIndex, endIndex);
}, {
immediate: true,
deep: true
});
const loadmore = () => {
if (customerList.value.length <= options.value.length) return;
pageNo.value++;
nextTick(() => {
loading.value = true;
let startIndex = pageNo.value * pageSize.value - pageSize.value;
let endIndex = pageNo.value * pageSize.value;
options.value = [
...options.value,
...customerList.value.slice(startIndex, endIndex)
];
loading.value = false;
});
};
const filterOptions = (query = '') => {
pageNo.value = 1;
nextTick(() => {
if (query === '') {
allFilterEvents.splice(0, allFilterEvents.length, ...customerList.value);
} else {
allFilterEvents.splice(0, allFilterEvents.length, ...customerList.value.filter(item => item.name.includes(query)));
}
});
};
const handleVisibleChange = (visible) => {
if (!visible) {
pageNo.value = 1;
nextTick(() => {
allFilterEvents.splice(0, allFilterEvents.length, ...customerList.value);
});
}
};
const handleEventsChange = (val) => {
console.log('Selected value:', val);
};
// onMounted(async() => {
// })
</script>

View File

@ -130,7 +130,7 @@ const handleQuery = () => {
/** 添加操作 */
const formRef = ref()
const openForm = () => {
formRef.value.open('create', undefined, props.customerId, props.businessId)
formRef.value.open('create', undefined, props.bizId, props.businessId)
}
/** 打开联系人详情 */

View File

@ -137,7 +137,7 @@ const resetQuery = () => {
/** 添加操作 */
const formRef = ref()
const openForm = () => {
formRef.value.open('create')
formRef.value.open('create', '', Number(props.customerId))
}
/** 关联联系人提交 */

View File

@ -3,9 +3,9 @@
<el-button v-if="permissionListRef?.validateWrite" @click="openForm('update', contact.id)">
编辑
</el-button>
<el-button v-if="permissionListRef?.validateOwnerUser" type="primary" @click="transfer">
<!-- <el-button v-if="permissionListRef?.validateOwnerUser" type="primary" @click="transfer">
转移
</el-button>
</el-button> -->
</ContactDetailsHeader>
<el-col>
<el-tabs>

View File

@ -106,7 +106,7 @@
<el-tabs v-model="activeName" @tab-click="handleTabClick">
<el-tab-pane label="我负责的" name="1" />
<el-tab-pane label="我参与的" name="2" />
<el-tab-pane label="下属负责的" name="3" />
<!-- <el-tab-pane label="下属负责的" name="3" /> -->
</el-tabs>
<el-table v-loading="loading" :data="list" :show-overflow-tooltip="true" :stripe="true">
<el-table-column align="center" fixed="left" label="联系人姓名" prop="name" width="160">

View File

@ -0,0 +1,473 @@
<template>
<ContentWrap>
<!-- 搜索工作栏 -->
<el-form
ref="queryFormRef"
:inline="true"
:model="queryParams"
class="-mb-15px"
label-width="68px"
>
<el-form-item label="合同编号" prop="no">
<el-input
v-model="queryParams.no"
class="!w-240px"
clearable
placeholder="请输入合同编号"
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="合同名称" prop="name">
<el-input
v-model="queryParams.name"
class="!w-240px"
clearable
placeholder="请输入合同名称"
@keyup.enter="handleQuery"
/>
<el-form-item label="客户" prop="customerId">
<el-select
v-model="queryParams.customerId"
class="!w-240px"
clearable
lable-key="name"
placeholder="请选择客户"
value-key="id"
@keyup.enter="handleQuery"
>
<el-option
v-for="item in customerList"
:key="item.id"
:label="item.name"
:value="item.id!"
/>
</el-select>
</el-form-item>
</el-form-item>
<el-form-item>
<el-button @click="handleQuery">
<Icon class="mr-5px" icon="ep:search" />
搜索
</el-button>
<el-button @click="resetQuery">
<Icon class="mr-5px" icon="ep:refresh" />
重置
</el-button>
<el-button v-hasPermi="['crm:contract:create']" type="primary" @click="handleChange">
<Icon class="mr-5px" icon="ep:plus" />
新增
</el-button>
<el-button
v-hasPermi="['crm:contract:export']"
:loading="exportLoading"
plain
type="success"
@click="handleExport"
>
<Icon class="mr-5px" icon="ep:download" />
导出
</el-button>
</el-form-item>
</el-form>
</ContentWrap>
<!-- 列表 -->
<ContentWrap>
<el-table v-loading="loading" :data="list" :show-overflow-tooltip="true" :stripe="true">
<el-table-column align="center" fixed="left" label="合同名称" prop="name" width="160">
<template #default="scope">
<el-link :underline="false" type="primary" @click="openDetail(scope.row)">
{{ scope.row.name }}
</el-link>
</template>
</el-table-column>
<el-table-column align="center" label="合同编号" prop="no" width="180" />
<el-table-column align="center" label="客户名称" prop="customerName" width="120">
<!-- <template #default="scope">
<el-link
:underline="false"
type="primary"
@click="openCustomerDetail(scope.row.customerId)"
>
{{ scope.row.customerName }}
</el-link>
</template> -->
</el-table-column>
<!-- <el-table-column align="center" label="商机名称" prop="businessName" width="130">
<template #default="scope">
<el-link
:underline="false"
type="primary"
@click="openBusinessDetail(scope.row.businessId)"
>
{{ scope.row.businessName }}
</el-link>
</template>
</el-table-column> -->
<el-table-column
align="center"
label="合同金额(元)"
prop="totalPrice"
width="140"
:formatter="erpPriceTableColumnFormatter"
/>
<!-- <el-table-column
align="center"
label="下单时间"
prop="orderDate"
width="120"
:formatter="dateFormatter2"
/>-->
<el-table-column
align="center"
label="合同开始时间"
prop="startTime"
width="120"
:formatter="dateFormatter2"
/>
<el-table-column
align="center"
label="合同结束时间"
prop="endTime"
width="120"
:formatter="dateFormatter2"
/>
<!-- <el-table-column align="center" label="客户签约人" prop="signContactName" width="130">
<template #default="scope">
<el-link
:underline="false"
type="primary"
@click="openContactDetail(scope.row.signContactId)"
>
{{ scope.row.signContactName }}
</el-link>
</template>
</el-table-column>-->
<!-- <el-table-column align="center" label="公司签约人" prop="signUserName" width="130" />-->
<!-- <el-table-column align="center" label="备注" prop="remark" width="200" />
<el-table-column
align="center"
label="已回款金额(元)"
prop="totalReceivablePrice"
width="140"
:formatter="erpPriceTableColumnFormatter"
/>
<el-table-column
align="center"
label="未回款金额(元)"
prop="totalReceivablePrice"
width="140"
:formatter="erpPriceTableColumnFormatter"
>
<template #default="scope">
{{ erpPriceInputFormatter(scope.row.totalPrice - scope.row.totalReceivablePrice) }}
</template>
</el-table-column>-->
<!-- <el-table-column
:formatter="dateFormatter"
align="center"
label="最后跟进时间"
prop="contactLastTime"
width="180px"
/>-->
<el-table-column align="center" label="负责人" prop="ownerUserName" width="120" />
<el-table-column align="center" label="所属部门" prop="ownerUserDeptName" width="100px" />
<el-table-column
:formatter="dateFormatter"
align="center"
label="更新时间"
prop="updateTime"
width="180px"
/>
<el-table-column
:formatter="dateFormatter"
align="center"
label="创建时间"
prop="createTime"
width="180px"
/>
<el-table-column align="center" label="创建人" prop="creatorName" width="120" />
<el-table-column align="center" fixed="right" label="合同状态" prop="auditStatus" width="120">
<template #default="scope">
<dict-tag :type="DICT_TYPE.CRM_AUDIT_STATUS" :value="scope.row.auditStatus" />
</template>
</el-table-column>
<el-table-column align="center" fixed="right" label="操作" width="180">
<template #default="scope">
<div style="display:flex">
<el-button
v-if="scope.row.auditStatus === 0"
v-hasPermi="['crm:contract:update']"
link
type="primary"
@click="openFormEdit(scope.row)"
>
编辑
</el-button>
<!-- <el-button
v-if="scope.row.auditStatus === 0"
v-hasPermi="['crm:contract:update']"
link
type="primary"
@click="handleSubmit(scope.row)"
>
提交审核
</el-button>
<el-button
v-if="scope.row.auditStatus === 0"
v-hasPermi="['crm:contract:update']"
link
type="primary"
@click="handleChange(scope.row)"
>
合同变更
</el-button> -->
<el-button
v-hasPermi="['crm:contract:query']"
link
type="primary"
@click="openDetail(scope.row)"
>
详情
</el-button>
<el-button
v-if="scope.row.auditStatus !== 0"
link
v-hasPermi="['crm:customer-suggestion:query']"
type="primary"
@click="handleProcessDetail(scope.row)"
>
进度
</el-button>
</div>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<Pagination
v-model:limit="queryParams.pageSize"
v-model:page="queryParams.pageNo"
:total="total"
@pagination="getList"
/>
<FileTemplateForm ref="formRef" @success="onPrintContract" />
</ContentWrap>
</template>
<script lang="ts" setup>
import { dateFormatter, dateFormatter2 } from '@/utils/formatTime'
import download from '@/utils/download'
import FileTemplateForm from '../contract/templateForm.vue'
import * as ContractApi from '@/api/crm/contract'
import { DICT_TYPE } from '@/utils/dict'
import { erpPriceInputFormatter, erpPriceTableColumnFormatter } from '@/utils'
import * as CustomerApi from '@/api/crm/customer'
import { checkPermi } from '@/utils/permission'
import { TabsPaneContext } from 'element-plus'
defineOptions({ name: 'ContractChangeRecord' })
const message = useMessage() // 消息弹窗
const { t } = useI18n() // 国际化
const loading = ref(true) // 列表的加载中
const total = ref(0) // 列表的总页数
const list = ref([]) // 列表的数据
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
sceneType: '1', // 默认和 activeName 相等
name: null,
customerId: null,
orderDate: [],
no: null
})
const queryFormRef = ref() // 搜索的表单
const exportLoading = ref(false) // 导出的加载中
const activeName = ref('1') // 列表 tab
const customerList = ref<CustomerApi.CustomerVO[]>([]) // 客户列表
/** 查询列表 */
const getList = async () => {
loading.value = true
try {
const data = await ContractApi.changeContractList(queryParams)
list.value = data.list
total.value = data.total
} finally {
loading.value = false
}
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.pageNo = 1
getList()
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value.resetFields()
handleQuery()
}
/** 添加/修改操作 */
const formRef = ref()
const router = useRouter() // 路由
const openFormEdit = (row: Object) => {
router.push({ name: 'CrmContractEdit', query: { id: row.id} })
}
const openFormAdd = () => {
push({ name: 'CrmContractAdd' })
}
const openFormDetail = (row: Object) => {
push({ name: 'CrmContractDetail', query: { id: row.id } })
}
const handleChange = (row) => {
router.push({ name: 'CrmContractChange', query: { id: row.id } })
}
/** 操作分发 */
const handleCommand = (command: string, row) => {
switch (command) {
case 'handleSubmit':
handleSubmit(row)
break
case 'handleChange':
handleChange(row)
break
case 'handlePrint':
onPrintContract(row)
break
case 'handleDelete':
handleDelete(row.id)
break
default:
break
}
}
/** 删除按钮操作 */
const handleDelete = async (id: number) => {
try {
// 删除的二次确认
await message.delConfirm()
// 发起删除
await ContractApi.deleteContract(id)
message.success(t('common.delSuccess'))
// 刷新列表
await getList()
} catch {}
}
/** 导出按钮操作 */
const handleExport = async () => {
try {
// 导出的二次确认
await message.exportConfirm()
// 发起导出
exportLoading.value = true
const data = await ContractApi.exportContract(queryParams)
download.excel(data, '合同.xls')
} catch {
} finally {
exportLoading.value = false
}
}
/** 提交审核 **/
const handleSubmit = async (row: ContractApi.ContractVO) => {
await message.confirm(`您确定提交【${row.name}】审核吗?`)
await ContractApi.submitContract(row.id)
message.success('提交审核成功!')
await getList()
}
const getPrintTemplate = (row) => {
formRef.value.open(row.id)
}
const onPrintContract = async (row: ContractApi.ContractVO) => {
try {
const res = await ContractApi.printContract(row.id)
if (!res) {
throw new Error('返回的合同文件为空')
}
// const contentType = res.type.toLowerCase()
// let fileType = 'application/msword' // 默认使用 .doc 的 MIME 类型
// let extension = '.doc'
// if (contentType.includes('wordprocessingml')) {
// fileType = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' // 对应 .docx 文件的 MIME 类型
// extension = '.docx'
// }
// // 创建 blob 对象并设置文件类型
// const blob = new Blob([res], { type: fileType })
// const url = window.URL.createObjectURL(blob)
// const link = document.createElement('a')
// link.href = url
// const disposition = res.headers['content-disposition']
// let fileName = '合同.docx'
// if (disposition && disposition.includes('filename=')) {
// const match = disposition.match(/filename="?([^"]+)"?/)
// if (match?.[1]) {
// fileName = decodeURIComponent(match[1])
// }
// }
// link.click()
// console.log('%csrc/views/crm/contract/index.vue:489 res.data', 'color: #007acc;', res);
window.open(res)
// message.success('提交审核成功!')
await getList()
} catch (error) {
console.error('合同下载失败', error)
}
}
/** 查看审批 */
const handleProcessDetail = (row) => {
router.push({ name: 'BpmProcessInstanceDetail', query: { id: row.processInstanceId } })
}
/** 打开合同详情 */
const { push } = useRouter()
const openDetail = (row: Object) => {
router.push({ name: 'CrmContractDetail', query: { id: row.id } })
}
/** 打开客户详情 */
const openCustomerDetail = (id: number) => {
router.push({ name: 'CrmCustomerDetail', query: { id } })
}
/** 打开联系人详情 */
const openContactDetail = (id: number) => {
push({ name: 'CrmContactDetail', params: { id } })
}
/** 打开商机详情 */
const openBusinessDetail = (id: number) => {
push({ name: 'CrmBusinessDetail', params: { id } })
}
/** 初始化 **/
onMounted(async () => {
await getList()
customerList.value = await CustomerApi.getCustomerSimpleList()
})
onActivated(()=>{
getList()
})
</script>

View File

@ -28,12 +28,13 @@
<el-form-item label="客户名称" prop="customerId">
<el-select
v-model="formData.customerId"
disabled=""
@change="changeCustomer"
:disabled="formType"
placeholder="请选择客户"
class="w-1/1"
>
<el-option
v-for="item in contactList"
v-for="item in customerList"
:key="item.id"
:label="item.name"
:value="item.id"
@ -41,16 +42,34 @@
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-col :span="8" v-if="!formType">
<el-form-item label="合同名称" prop="customerId">
<el-select
v-model="formData.processInstance"
:disabled="formType"
@change="getContractName"
placeholder="请选择合同名称"
class="w-1/1"
>
<el-option
v-for="item in contractList"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8" v-if="formType">
<el-form-item label="协议编码" prop="name">
<el-input v-model="formData.processInstanceId" disabled placeholder="协议编码" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="合同状态" prop="quotationId">
<el-select v-model="formData.auditStatus" disabled clearable placeholder="请选择合同状态">
<el-select v-model="formData.auditStatus" disabled clearable placeholder="">
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_AUDIT_STATUS)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
@ -65,7 +84,7 @@
disabled
type="date"
value-format="x"
placeholder="请选择协议截止日期"
placeholder=""
style="width: 100%"
/>
</el-form-item>
@ -108,7 +127,7 @@
<ContracExtension ref="contractExtensionFormRef" v-if="formData.changeItem == 3" :data="formData" />
<ContractStop ref="contractStopFormRef" :formData="formData" v-if="formData.changeItem == 4" />
<ContractAAuthorizedCompanyForm ref="contractAAuthorizedCompanyFormRef1" v-if="formData.changeItem == 5" :data="formData.contractAAuthorizedCompanys" />
<ContractForm ref="contractFormRef" :type="1" :formData="formData" v-if="formData.changeItem == 6" />
<ContractForm ref="contractFormRef" :type="1" :changeType="true" v-if="formData.changeItem == 6" />
</ContentWrap>
</el-form>
<div style="text-align: right">
@ -194,6 +213,7 @@ const userOptions = ref<UserApi.UserVO[]>([]) // 用户列表
const customerList = ref([]) // 客户列表的数据
const quotationList = ref<QuotationVO[]>([])
const contactList = ref<ContactApi.ContactVO[]>([])
const contractList = ref<ContractApi.ContractVO[]>([])
/** 子表的表单 */
const subTabsName = ref('limit')
@ -224,6 +244,9 @@ const open = async (type: string) => {
formLoading.value = true
try {
let data = await ContractApi.getContract(type)
if(!route.query.id) delete data.customerId
formData.value = Object.assign(formData.value, data)
} finally {
formLoading.value = false
@ -466,6 +489,17 @@ const getContactOptions = computed(() =>
contactList.value.filter((item) => item.customerId == formData.value.customerId)
)
const changeCustomer = async (val) => {
contractList.value = await ContractApi.getContractSimpleList(val)
}
const getContractName = (val) => {
open(val)
const arr = contractList.value.filter(v => v.id == val)
formData.value.processInstanceId = (arr.length ? arr[0]['no'] : '')
}
const route = useRoute();
onMounted(async () => {
formType.value = route.query.id;
@ -473,7 +507,7 @@ onMounted(async () => {
if (formType.value) open(formType.value)
// 获得客户列表
customerList.value = await CustomerApi.getCustomerSimpleList()
customerList.value = await CustomerApi.getSelfCustomerSimpleList()
// 获得用户列表
userOptions.value = await UserApi.getSimpleUserList()
// 获得报价列表
@ -484,6 +518,7 @@ onMounted(async () => {
// formData.value.ownerUserId = useUserStore().getUser.id
// }
// 获取联系人
contactList.value = await ContactApi.getSimpleContactList()
contactList.value = await CustomerApi.getCustomerSimpleList()
});
</script>

View File

@ -16,17 +16,14 @@
<el-input v-model="formData.name" placeholder="请输入合同名称" />
</el-form-item>
</el-col>
<el-col :span="8" v-if="formType">
<el-form-item label="合同编号" prop="no">
<el-input v-model="formData.no" placeholder="请输入合同编号" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="客户名称" prop="customerId">
<el-select
v-model="formData.customerId"
disabled
placeholder=""
placeholder="请选择客户名称"
:disabled="!!formType"
@change="onCustomerChange"
class="w-1/1"
>
<el-option
@ -38,13 +35,19 @@
</el-select>
</el-form-item>
</el-col>
<el-col :span="8" v-if="formType">
<el-form-item label="合同编号" prop="no">
<el-input disabled v-model="formData.no" placeholder="" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="报价单编号" prop="quotationId">
<el-select
@change="handleQuotationChange"
v-model="formData.quotationId"
placeholder="请选择报价单编号"
placeholder=""
class="w-1/1"
disabled
>
<el-option
v-for="item in quotationList"
@ -91,14 +94,7 @@
</el-col>
<el-col :span="8">
<el-form-item label="授信计算周期" prop="creditCalcCycle" >
<el-select v-model="formData.creditCalcCycle" placeholder="" disabled>
<el-option
v-for="dict in getIntDictOptions('credit_calc_cycle')"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
<el-input v-model="formData.creditCalcCycle" placeholder="" disabled />
</el-form-item>
</el-col>
<el-col :span="8">
@ -266,7 +262,18 @@
</el-col>
<el-col :span="8">
<el-form-item label="商务负责人" prop="ownerUserId">
<el-input v-model="formData.ownerUserId" placeholder="" disabled />
<el-select
v-model="formData.ownerUserId"
disabled
class="w-1/1"
>
<el-option
v-for="item in userOptions"
:key="item.id"
:label="item.nickname"
:value="item.id"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
@ -328,6 +335,7 @@
v-model="formData.startTime"
type="date"
value-format="x"
format="YYYY-MM-DD"
placeholder="选择合同签订日期"
style="width: 100%"
/>
@ -339,6 +347,7 @@
v-model="formData.endTime"
type="date"
value-format="x"
format="YYYY-MM-DD"
placeholder="选择合同结束时间"
style="width: 100%"
/>
@ -502,6 +511,7 @@ const { proxy }: any = getCurrentInstance();
const props = defineProps({
id: propTypes.number.def(undefined),
type: propTypes.number.def(undefined),
changeType: propTypes.number.def(undefined),//合同变更不回显数据
})
const templateOptions = ref([])
const dialogVisible = ref(false) // 弹窗的是否展示
@ -601,6 +611,7 @@ const open = async (type: string) => {
// formLoading.value = true
try {
formData.value = await ContractApi.getContract(type)
console.log('%csrc/views/crm/contract/ContractForm.vue:614 object', 'color: #007acc;', formData.value);
} finally {
formLoading.value = false
}
@ -753,7 +764,7 @@ const handleQuotationChange = async (quotationId: number) => {
const customerRes = await ContractApi.quotationTimes(quotation.id);
formData.value.quotationTimes = customerRes.quotationTimes;
await onCustomerChange(quotation.customerId);
// await onCustomerChange(quotation.customerId);
await onPartnerChange(quotation.partnerCompanyId);
} catch (err) {
} finally {
@ -767,7 +778,7 @@ const onCustomerChange = async (customerId: string) => {
try {
formLoading.value = true;
const customerRes = await CustomerApi.getCustomer(customerId);
const customerRes = await CustomerApi.getCustomerDetail(customerId);
formData.value.cooperationType = customerRes.cooperationType;
formData.value.companyType = customerRes.companyType;
formData.value.listingStatus = customerRes.listingStatus;
@ -781,11 +792,14 @@ const onCustomerChange = async (customerId: string) => {
formData.value.partAddress = customerRes.partAddress;
formData.value.listingStatus = customerRes.listingStatus;
formData.value.legalPersonName = customerRes.legalPersonName;
formData.value.creditCode = customerRes.creditCode;
formData.value.creditNo = customerRes.creditNo;
formData.value.regType = customerRes.regType;
formData.value.financingInfo = customerRes.financingInfo;
formData.value.socialEmployeeNum = customerRes.socialEmployeeNum;
formData.value.registeredAddress = customerRes.registeredAddress;
formData.value.quotationId = customerRes.quotationId;
await handleQuotationChange(customerRes.quotationId);
} catch (err) {
console.error('获取客户详情失败:', err);
} finally {
@ -818,15 +832,16 @@ const getContactOptions = computed(() =>
const route = useRoute();
onMounted(async () => {
formType.value = props.id || route.query.id
if (formType.value) await open(formType.value)
if(!props.changeType) {
console.log('%csrc/views/crm/contract/ContractForm.vue:835 props.id', 'color: #007acc;', props.id);
formType.value = props.id || route.query.id
if (formType.value) await open(formType.value)
}
// 获得客户列表
customerList.value = await CustomerApi.getCustomerSimpleList()
// 获得用户列表
userOptions.value = await UserApi.getSimpleUserList()
console.log('%csrc/views/crm/contract/ContractForm.vue:827 userOptions.value', 'color: #007acc;', userOptions.value);
// 获得报价列表
quotationList.value = await QuotationApi.getSelfSimpleQuotationList()
@ -845,7 +860,7 @@ onMounted(async () => {
// formData.value.ownerUserId = useUserStore().getUser.id
// }
// 获取联系人
contactList.value = await ContactApi.getSimpleContactList()
contactList.value = await CustomerApi.getSelfCustomerSimpleList()
const data = await ContractApi.getOrg({
pageNo: 1,
pageSize: 1000

View File

@ -13,13 +13,13 @@
v-model="formData.terminationDate"
type="date"
:disabled="!!type"
value-format="YYYY-MM-DD"
value-format="x"
placeholder="请选择终止日期"
style="width: 100%"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-col :span="16">
<el-form-item label="终止原因" prop="terminationReason">
<el-radio-group v-model="formData.terminationReason" :disabled="!!type">
<el-radio :label="1">合同到期</el-radio>
@ -74,58 +74,13 @@ const dialogTitle = ref('') // 弹窗的标题
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
const formType = ref('') // 表单的类型:create - 新增;update - 修改
const formData = ref({
no: undefined,
customerId: undefined,
businessId: undefined,
invoiceTemplateId: undefined,
processInstanceId: undefined,
auditStatus: undefined,
ownerUserId: undefined,
expanderUserId: undefined,
pricingUserId: undefined,
signUserId: undefined,
signPhoneNumber: undefined,
signEmail: undefined,
signWechat: undefined,
paymentTerm: undefined,
creditMethod: undefined,
creditCalcCycle: undefined,
creditLimit: undefined,
partnerCompanyId: undefined,
deptId: undefined,
creator: undefined,
createTime: undefined,
updater: undefined,
onlinePrice: undefined,
offlinePrice: undefined,
totalPrice: undefined,
cooperationType: undefined, // 合作类型
companyType: undefined, // 公司类型
listingStatus: undefined, // 上市情况
financingInfo: undefined, // 融资信息
paidInCapital: undefined, // 实缴资金
insuredCount: undefined, // 参保人数
establishmentDate: undefined, // 成立日期
enterpriseType: undefined, // 企业类型
businessStatus: undefined, // 营业状态
defendantRecord: undefined, // 被告记录
businessAbnormal: undefined, // 经营异常
equityPledge: undefined, // 股权出质
dishonestRecord: undefined, // 失信记录
financingRecord: undefined, // 融资记录
enforcementRecord: undefined, // 被执行记录
bBankName: undefined, // 开户行
bBankAccount: undefined, // 银行账号
bLegalRepresentative: undefined, // 法人代表
bBusinessLicenseNumber: undefined, // 营业执照号
bBusinessLicense: undefined // 营业执照
terminationDate: new Date(),
terminationReason: undefined,
note: undefined,
})
const formRules = reactive({
// cooperationType: [{ required: true, message: '合作类型不能为空', trigger: 'change' }],
// no: [{ required: true, message: '报价单编号不能为空', trigger: 'blur' }],
// customerId: [{ required: true, message: '客户id不能为空', trigger: 'change' }],
// businessId: [{ required: true, message: '商机编号不能为空', trigger: 'change' }],
// invoiceTemplateId: [{ required: true, message: '票据模板Id不能为空', trigger: 'change' }]
terminationReason: [{ required: true, message: '终止原因不能为空', trigger: 'change' }],
terminationDate: [{ required: true, message: '终止日期不能为空', trigger: 'blur' }],
})
const formRef = ref() // 表单 Ref

View File

@ -179,12 +179,13 @@ const handleAdd = () => {
}
const getList = (val: []) => {
console.log('%csrc/views/crm/contract/components/ContractAAuthorizedPersonForm.vue:182 val', 'color: #007acc;', val);
for(let i = val.length - 1; i >= 0; i--) {
let obj = val[i]
if(formData.value.some(v => v.id === obj.id)) val.splice(i, 1)
if(formData.value.some(v => v.customerContactId === obj.id)) val.splice(i, 1)
}
val.forEach(item => {
if(!formData.value.some(v => v.id === item.id)) {
if(!formData.value.some(v => v.customerContactId === item.id)) {
val.forEach(item => {
formData.value.push({
"customerContactId": item.id,

View File

@ -52,11 +52,11 @@
</el-table-column>
<el-table-column label="岗位" min-width="150">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.deptName`" :rules="formRules.deptName" class="mb-0px!" v-if="!type">
<el-input v-model="row.deptName" placeholder="请输入岗位" />
<el-form-item :prop="`${$index}.postName`" :rules="formRules.postName" class="mb-0px!" v-if="!type">
<el-input v-model="row.postName" placeholder="请输入岗位" />
</el-form-item>
<div v-else>
{{row.deptName}}
{{row.postName}}
</div>
</template>
</el-table-column>
@ -185,7 +185,7 @@ const handleAdd = () => {
userId: undefined,
name: undefined,
authType: undefined,
deptName: undefined,
postName: undefined,
userRank: undefined,
authDesc: undefined,
phoneNumber: undefined,
@ -207,8 +207,8 @@ const changeUser = (i, val) => {
let arr = props.userOptions.filter(v => v.id === val)
if(arr.length) {
formData.value[i].name = arr[0].nickname
formData.value[i].deptName = arr[0].deptName
formData.value[i].postId = arr[0].deptId
formData.value[i].postName = arr[0].postName
formData.value[i].postId = arr[0].postId
}
}
const getList = (val: []) => {
@ -223,7 +223,7 @@ const getList = (val: []) => {
"name": item.name,
"authType": item.post,
"postId": item.userRank,
"deptName": item.deptName,
"postName": item.postName,
"userRank": item.mobile,
"authDesc": item.authDesc,
"wechat":item. wechat,

View File

@ -3,7 +3,7 @@
ref="formRef"
:model="formData"
:rules="formRules"
label-width="130px"
label-width="142px"
v-loading="formLoading"
>
<el-row>
@ -24,7 +24,7 @@
<el-input v-model="formData.regType" disabled placeholder="自动获取" />
</el-form-item>
</el-col>
<el-col :span="8">
<!-- <el-col :span="8">
<el-form-item label="上市情况" prop="customerId">
<el-select v-model="formData.customerId" placeholder="请选择上市情况" disabled @change="onCustomerChange">
<el-option
@ -35,13 +35,13 @@
/>
</el-select>
</el-form-item>
</el-col>
</el-col> -->
<!-- <el-col :span="8">
<el-form-item label="合作类型" prop="cooperationType">
<el-input v-model="formData.cooperationType" disabled />
</el-form-item>
</el-col> -->
<el-col :span="8">
<!-- <el-col :span="8">
<el-form-item label="融资信息" prop="companyType">
<el-select v-model="formData.customerId" placeholder="请选择融资信息" disabled @change="onCustomerChange">
<el-option
@ -52,7 +52,7 @@
/>
</el-select>
</el-form-item>
</el-col>
</el-col> -->
<el-col :span="8">
<el-form-item label="实缴资金" prop="paidInCapital">
<el-input v-model="formData.actualCapital" disabled />
@ -67,48 +67,48 @@
<el-col :span="8">
<el-form-item label="被告记录" prop="defendantRecord">
<el-radio-group v-model="formData.defendantRecord" :disabled="true">
<el-radio :label="true">是</el-radio>
<el-radio :label="false">否</el-radio>
<el-radio :label="1">是</el-radio>
<el-radio :label="0">否</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="经营异常" prop="abnormalService">
<el-radio-group v-model="formData.abnormalService" :disabled="true">
<el-radio :label="true">是</el-radio>
<el-radio :label="false">否</el-radio>
<el-radio :label="1">是</el-radio>
<el-radio :label="0">否</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="股权出质" prop="equityPledge">
<el-radio-group v-model="formData.equityPledge" :disabled="true">
<el-radio :label="true">是</el-radio>
<el-radio :label="false">否</el-radio>
<el-radio :label="1">是</el-radio>
<el-radio :label="0">否</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="失信记录" prop="dishonestyRecord">
<el-radio-group v-model="formData.dishonestyRecord" :disabled="true">
<el-radio :label="true">是</el-radio>
<el-radio :label="false">否</el-radio>
<el-radio :label="1">是</el-radio>
<el-radio :label="0">否</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="融资记录" prop="financeRecord">
<el-radio-group v-model="formData.financeRecord" :disabled="true">
<el-radio :label="true">是</el-radio>
<el-radio :label="false">否</el-radio>
<el-radio :label="1">是</el-radio>
<el-radio :label="0">否</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="被执行记录" prop="judgmentRecord">
<el-radio-group v-model="formData.judgmentRecord" :disabled="true">
<el-radio :label="true">是</el-radio>
<el-radio :label="false">否</el-radio>
<el-radio :label="1">是</el-radio>
<el-radio :label="0">否</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
@ -118,12 +118,12 @@
<el-col :span="8">
<el-form-item label="初始额度" prop="creditLimit">
<el-input v-model="formData.creditLimit" placeholder="请输入初始额度" disabled />
<el-input v-model="formData.creditAmount" placeholder="请输入初始额度" disabled />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="目前额度" prop="creditLimit">
<el-input v-model="formData.creditLimit" placeholder="请输入目前额度" disabled />
<el-input v-model="formData.creditLimit" placeholder="" disabled />
</el-form-item>
</el-col>
<el-col :span="8">
@ -132,7 +132,7 @@
<el-option
v-for="dict in orgList"
:key="dict.id"
:label="dict.name"
:label="dict.orgName"
:value="dict.id"
/>
</el-select>
@ -140,12 +140,12 @@
</el-col>
<el-col :span="8">
<el-form-item label="调整金额(元)" prop="changeAmount">
<el-input v-model="formData.changeAmount" type="number" :disabled="!!type" placeholder="请输入调整金额(元)" @input="onChangeAmount" />
<el-input v-model="formData.changeAmount" :disabled="type" oninput="value=value.match(/^\d+(?:\.\d{0,2})?/)" placeholder="请输入调整金额(元)" @input="onChangeAmount" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="最终额度" prop="afterAmount">
<el-input v-model="formData.afterAmount" type="number" disabled placeholder="请输入最终额度" />
<el-input v-model="formData.afterAmount" type="number" disabled placeholder="" />
</el-form-item>
</el-col>
<el-col :span="8">
@ -154,7 +154,7 @@
v-model="formData.changeAmountDeadline"
value-format="YYYY-MM-DD"
type="date"
:disabled="!!type"
:disabled="type"
placeholder="请选择调整金额截止日期"
style="width: 100%"
/>
@ -262,7 +262,8 @@ const subTabsName = ref('quotationProduct')
const quotationProductFormRef = ref()
const props = defineProps<{
data: undefined
data: undefined,
type: undefined
}>()
watch(
@ -276,7 +277,7 @@ watch(
)
const onChangeAmount = async (amount: number) => {
formData.value.afterAmount = Number(formData.value.creditLimit) + Number(amount);
formData.value.afterAmount = Number(formData.value.creditLimit) + Number(Number(amount).toFixed(2));
}
const onCustomerChange = async (customerId: string) => {
@ -385,7 +386,7 @@ const resetForm = () => {
}
// const route = useRoute();
// onMounted(async () => {
onMounted(async () => {
// console.log('%csrc/views/crm/quotation/QuotationForm.vue:596 route', 'color: #007acc;', route);
// formType.value = route.query.id;
@ -404,10 +405,10 @@ const resetForm = () => {
// businessList.value = await BusinessApi.getSimpleBusinessList()
// // 获得部门树
// deptTree.value = handleTree(await DeptApi.getSimpleDeptList())
// const org = await ContractApi.getOrg({
// pageNo: 1,
// pageSize: 1000
// })
// orgList.value = org.list
// });
const org = await ContractApi.getOrg({
pageNo: 1,
pageSize: 1000
})
orgList.value = org.list
});
</script>

View File

@ -27,7 +27,7 @@
</el-row>
<el-row>
<el-col :span="8">
<!-- <el-col :span="8">
<el-form-item label="上市情况" prop="customerId">
<el-select v-model="formData.customerId" :disabled="!!type" placeholder="请选择上市情况" @change="onCustomerChange">
<el-option
@ -38,13 +38,13 @@
/>
</el-select>
</el-form-item>
</el-col>
</el-col> -->
<!-- <el-col :span="8">
<el-form-item label="合作类型" prop="cooperationType">
<el-input v-model="formData.cooperationType" disabled />
</el-form-item>
</el-col> -->
<el-col :span="8">
<!-- <el-col :span="8">
<el-form-item label="融资信息" prop="companyType">
<el-select v-model="formData.customerId" :disabled="!!type" placeholder="请选择融资信息" @change="onCustomerChange">
<el-option
@ -55,63 +55,63 @@
/>
</el-select>
</el-form-item>
</el-col>
</el-col> -->
<el-col :span="8">
<el-form-item label="实缴资金" prop="paidInCapital">
<el-input v-model="formData.paidInCapital" disabled />
<el-input v-model="formData.actualCapital" disabled />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="参保人数" prop="insuredCount">
<el-input v-model="formData.insuredCount" disabled />
<el-input v-model="formData.socialEmployeeNum" disabled />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="被告记录" prop="defendantRecord">
<el-radio-group v-model="formData.defendantRecord" :disabled="true">
<el-radio :label="true">是</el-radio>
<el-radio :label="false">否</el-radio>
<el-radio :label="1">是</el-radio>
<el-radio :label="0">否</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="经营异常" prop="businessAbnormal">
<el-radio-group v-model="formData.businessAbnormal" :disabled="true">
<el-radio :label="true">是</el-radio>
<el-radio :label="false">否</el-radio>
<el-form-item label="经营异常" prop="abnormalService">
<el-radio-group v-model="formData.abnormalService" :disabled="true">
<el-radio :label="1">是</el-radio>
<el-radio :label="0">否</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="股权出质" prop="equityPledge">
<el-radio-group v-model="formData.equityPledge" :disabled="true">
<el-radio :label="true">是</el-radio>
<el-radio :label="false">否</el-radio>
<el-radio :label="1">是</el-radio>
<el-radio :label="0">否</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="失信记录" prop="dishonestRecord">
<el-radio-group v-model="formData.dishonestRecord" :disabled="true">
<el-radio :label="true">是</el-radio>
<el-radio :label="false">否</el-radio>
<el-form-item label="失信记录" prop="dishonestyRecord">
<el-radio-group v-model="formData.dishonestyRecord" :disabled="true">
<el-radio :label="1">是</el-radio>
<el-radio :label="0">否</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="融资记录" prop="financingRecord">
<el-radio-group v-model="formData.financingRecord" :disabled="true">
<el-radio :label="true">是</el-radio>
<el-radio :label="false">否</el-radio>
<el-form-item label="融资记录" prop="financeRecord">
<el-radio-group v-model="formData.financeRecord" :disabled="true">
<el-radio :label="1">是</el-radio>
<el-radio :label="0">否</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="被执行记录" prop="enforcementRecord">
<el-radio-group v-model="formData.enforcementRecord" :disabled="true">
<el-radio :label="true">是</el-radio>
<el-radio :label="false">否</el-radio>
<el-form-item label="被执行记录" prop="judgmentRecord">
<el-radio-group v-model="formData.judgmentRecord" :disabled="true">
<el-radio :label="1">是</el-radio>
<el-radio :label="0">否</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
@ -120,12 +120,13 @@
<el-row>
<el-col :span="8">
<el-form-item label="协议截止日" prop="agreementDeadline">
<el-form-item label="协议截止日" prop="endTime">
<el-date-picker
:disabled="!!type"
v-model="formData.agreementDeadline"
type="date"
value-format="YYYY-MM-DD"
disabled
v-model="formData.endTime"
type="date"
@change="changeYear"
value-format="x"
placeholder="请选择协议截止日"
style="width: 100%"
/>
@ -133,7 +134,7 @@
</el-col>
<el-col :span="8">
<el-form-item label="顺延年限" prop="extensionYears">
<el-input v-model="formData.extensionYears" :disabled="!!type" placeholder="请输入顺延年限" />
<el-input v-model="formData.extensionYears" :disabled="!!type" placeholder="请输入顺延年限" @change="changeYear" />
</el-form-item>
</el-col>
<el-col :span="8">
@ -142,7 +143,7 @@
v-model="formData.extensionDate"
:disabled="!!type"
type="date"
value-format="YYYY-MM-DD"
value-format="x"
placeholder="调整金额截止日期"
style="width: 100%"
/>
@ -169,6 +170,7 @@ import * as UserApi from '@/api/system/user'
import * as DeptApi from '@/api/system/dept'
import * as BusinessApi from '@/api/crm/business'
import { propTypes } from '@/utils/propTypes'
import { number } from 'vue-types'
/** CRM 方案报价 表单 */
defineOptions({ name: 'QuotationForm' })
@ -333,6 +335,16 @@ const onPartnerChange = async (id: string) => {
}
}
const changeYear = (val) => {
if (formData.value.extensionYears && formData.value.endTime) {
let time = new Date(formData.value.endTime)
let year = time.getFullYear()
let month = time.getMonth() + 1
let day = time.getDate()
formData.value.extensionDate = Number(year) + Number(formData.value.extensionYears) + '-' + month + '-' + day
}
}
/** 打开弹窗 */
const open = async (type: string) => {
formType.value = type

View File

@ -118,8 +118,9 @@ const openForm = () => {
/** 打开合同详情 */
const { push } = useRouter()
const router = useRouter()
const openDetail = (id: number) => {
push({ name: 'CrmContractDetail', params: { id } })
router.push({ name: 'CrmContractDetail', query: { id: id } })
}
/** 监听打开的 bizId + bizType,从而加载最新的列表 */

View File

@ -28,8 +28,8 @@
<el-form-item label="客户名称" prop="customerId">
<el-select
v-model="formData.customerId"
disabled=""
placeholder="请选择客户"
disabled
class="w-1/1"
>
<el-option
@ -50,7 +50,7 @@
<el-form-item label="合同状态" prop="quotationId">
<el-select v-model="formData.auditStatus" disabled clearable placeholder="请选择合同状态">
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_AUDIT_STATUS)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
@ -72,47 +72,48 @@
</el-col>
<el-col :span="24">
<el-form-item label="变更项目" prop="endTime">
<el-radio-group v-model="formData.radio" disabled>
<el-radio-group v-model="formData.changeItem" disabled >
<el-radio :value="1">额度</el-radio>
<el-radio :value="2">授权人</el-radio>
<el-radio :value="3">延期</el-radio>
<el-radio :value="4">终止</el-radio>
<el-radio :value="5">甲方关联单位</el-radio>
<el-radio :value="5">关联</el-radio>
<el-radio :value="6">重签</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
</el-row>
<!-- 子表的表单 -->
<ContentWrap>
<ContractChangeLimit
v-if="formData.radio == 1"
:type="1"
v-if="formData.changeItem == 1"
ref="limitFormRef"
:formData="formData"
:disabled="disabled"
:data="formData"
:disabled="true"
/>
<div v-if="formData.radio == 2">
<div v-if="formData.changeItem == 2">
<ContentWrap style="border: none">
甲方现授权人信息
</ContentWrap>
<ContractAuthPersonInfo
:disabled="true"
ref="personDataRef"
:products="formData.contractAAuthorizedPersons"
/>
<!-- <ContentWrap style="border: none">
<ContentWrap style="border: none">
甲方授权人
</ContentWrap>
<ContractAAuthorizedCompanyForm ref="contractAAuthorizedCompanyFormRef" :contract-id="formData.id" /> -->
<ContractBAuthorizedPersonForm :type="1" ref="ContractBAuthorizedPersonFormRef" :customerId="formData.customerId" :contract-id="formData.id" />
</div>
<ContracExtension ref="contractExtensionFormRef" :type="1" :formData="formData" v-if="formData.radio == 3" />
<ContractStop ref="contractStopFormRef" :type="1" :formData="formData" v-if="formData.radio == 4" />
<ContractAAuthorizedCompanyForm :type="1" ref="contractAAuthorizedCompanyFormRef1" v-if="formData.radio == 5" :contract-id="formData.id" />
<ContractForm ref="contractFormRef" :type="1" :id="formType" :formData="formData" v-if="formData.radio == 6" />
<ContracExtension ref="contractExtensionFormRef" v-if="formData.changeItem == 3" :disabled="true" :data="formData" />
<ContractStop ref="contractStopFormRef" :formData="formData" :disabled="true" v-if="formData.changeItem == 4" />
<ContractAAuthorizedCompanyForm ref="contractAAuthorizedCompanyFormRef1" :type="true" v-if="formData.changeItem == 5" :data="formData.contractAAuthorizedCompanys" />
<ContractForm ref="contractFormRef" :type="1" :id="formData.id" v-if="formData.changeItem == 6" />
</ContentWrap>
</el-form>
</ContentWrap>
</template>
<script lang="ts" setup>
@ -130,6 +131,7 @@ import ContracExtension from '@/views/crm/contract/components/ContractExtension.
import ContractStop from '@/views/crm/contract/components/ContracStop.vue'
import ContractProductForm from '@/views/crm/contract/components/ContractProductForm.vue'
import ContractAAuthorizedCompanyForm from '../components/ContractAAuthorizedCompanyForm.vue'
import ContractBAuthorizedPersonForm from '../components/ContractAAuthorizedPersonForm.vue'
import ContractForm from './ContractDetail.vue'
import { propTypes } from '@/utils/propTypes'
@ -137,7 +139,7 @@ const { t } = useI18n() // 国际化
const message = useMessage() // 消息弹窗
const { proxy }: any = getCurrentInstance();
const props = defineProps({
id: propTypes.number.def(undefined)
id: propTypes.number.def(undefined),
})
const dialogVisible = ref(false) // 弹窗的是否展示
const dialogTitle = ref('') // 弹窗的标题
@ -180,6 +182,7 @@ const formData = ref({
partnerCompanyId: undefined,
deptId: undefined,
quotationTimes: undefined,
changeItem: 1,
products: []
})
const formRules = reactive({
@ -196,7 +199,7 @@ const contactList = ref<ContactApi.ContactVO[]>([])
/** 子表的表单 */
const subTabsName = ref('limit')
const contractAAuthorizedCompanyFormRef = ref()
const ContractBAuthorizedPersonFormRef = ref()
const contractAAuthorizedCompanyFormRef1 = ref()
const limitFormRef = ref()
const contractExtensionFormRef = ref()
@ -222,7 +225,16 @@ const open = async (type: string) => {
if (type) {
formLoading.value = true
try {
formData.value = await ContractApi.getChangeContract(type)
// formData.value = Object.assign(formData.value, data)
let data = await ContractApi.getChangeContract(type)
formData.value = data.contractDetail
formData.value.changeItem = data.changeItem
formData.value.changeAmount = data.changeAmount
formData.value.afterAmount = data.afterAmount
formData.value.changeAmountDeadline = data.changeAmountDeadline
formData.value.note = data.note
// formData.value.creditAmount = data.creditAmount
formData.value.creditLimit = data.beforeAmount
} finally {
formLoading.value = false
}
@ -245,10 +257,39 @@ const submitForm = async () => {
// const Authalid1 = await contractAAuthorizedCompanyFormRef1.value.validate()
// if (!Authalid1) return
// const limitValid = await limitFormRef.value.validate()
// if (!limitValid) return
// const extensionValid = await contractExtensionFormRef.value.validate()
// if (!extensionValid) return
if(ContractBAuthorizedPersonFormRef.value) {
const Authalid = await ContractBAuthorizedPersonFormRef.value.validate()
if (!Authalid) return
}
if(contractAAuthorizedCompanyFormRef1.value) {
const Authalid1 = await contractAAuthorizedCompanyFormRef1.value.validate()
if (!Authalid1) return
}
if(limitFormRef.value) {
const limitValid = await limitFormRef.value.validate()
if (!limitValid) return
}
if(contractExtensionFormRef.value) {
const extensionValid = await contractExtensionFormRef.value.validate()
if (!extensionValid) return
}
if(contractStopFormRef.value) {
const stopValid = await contractStopFormRef.value.validate()
if (!stopValid) return
}
if(contractFormRef.value) {
const contractValid = await contractFormRef.value.validate()
if (!contractValid) return
}
// const stopValid = await contractStopFormRef.value.validate()
// if (!stopValid) return
@ -257,19 +298,49 @@ const submitForm = async () => {
// if (!contractValid) return
// console.log('%csrc/views/crm/contract/ContractChange.vue:247 123', 'color: #007acc;', extensionValid,123);
// return
// 提交请求
formLoading.value = true
try {
const data = unref(JSON.parse(JSON.stringify(formData.value))) as unknown as ContractApi.ContractVO
// 拼接子表的数据
data.contractAAuthorizedCompanys = limitFormRef.value.getData()
data.contractAAuthorizedCompanys = contractExtensionFormRef.value.getData()
data.contractAAuthorizedCompanys = contractStopFormRef.value.getData()
data.contractAAuthorizedCompanys = contractFormRef.value.getData()
data.contractAAuthorizedCompanys = contractAAuthorizedCompanyFormRef.value.getData()
let proChangeObj = {
1: limitFormRef,
2: ContractBAuthorizedPersonFormRef,
3: contractExtensionFormRef,
4: contractStopFormRef,
5: contractAAuthorizedCompanyFormRef1,
6: contractFormRef
}
let dataFormList = proChangeObj[formData.value.changeItem].value.getData()
data[proChangeObj[formData.value.changeItem]] = dataFormList
if(formData.value.changeItem == 1) {
data.changeAmount = dataFormList.changeAmount
data.changeAmountDeadline = dataFormList.changeAmountDeadline
} else if(formData.value.changeItem == 2) {
let ids = []
dataFormList.map(item => {
ids.push(item.id)
})
data.afterAuthorizerId = ids.join(',')
} else if(formData.value.changeItem == 3) {
data.agreementDeadline = dataFormList.agreementDeadline
data.extensionYears = dataFormList.extensionYears
data.extensionDate = dataFormList.extensionDate
data.note = dataFormList.note
} else if(formData.value.changeItem == 4) {
data.terminationDate = dataFormList.terminationDate
data.terminationReason = dataFormList.terminationReason
data.note = dataFormList.note
} else if(formData.value.changeItem == 5) {
let ids = []
dataFormList.map(item => {
ids.push(item.id)
})
data.afterAffiliateCompanyId = ids.join(',')
} else if(formData.value.changeItem == 6) {
data.newContract = data
}
data.contractId = data.id
delete data.id
await ContractApi.changeContract(data)
@ -326,6 +397,7 @@ const resetForm = () => {
partnerCompanyId: undefined,
deptId: undefined,
quotationTimes: undefined,
changeItem: 1,
products: []
}
formRef.value?.resetFields()
@ -405,8 +477,7 @@ const getContactOptions = computed(() =>
)
const route = useRoute();
onBeforeMount(async () => {
console.log('%csrc/views/crm/contract/detail/ContractChangeDetail.vue:409 props.id,route.query.id', 'color: #007acc;', props.id,route.query.id);
onMounted(async () => {
formType.value = props.id || route.query.id;
if (formType.value) open(formType.value)
@ -423,6 +494,6 @@ onBeforeMount(async () => {
// formData.value.ownerUserId = useUserStore().getUser.id
// }
// 获取联系人
contactList.value = await ContactApi.getSimpleContactList()
contactList.value = await CustomerApi.getCustomerSimpleList()
});
</script>

View File

@ -16,6 +16,23 @@
<el-input v-model="formData.name" disabled placeholder="" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="客户名称" prop="customerId">
<el-select
v-model="formData.customerId"
disabled=""
placeholder=""
class="w-1/1"
>
<el-option
v-for="item in customerList"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8" v-if="formType">
<el-form-item label="合同编号" prop="no">
<el-input v-model="formData.no" disabled placeholder="" />
@ -39,23 +56,7 @@
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="客户名称" prop="customerId">
<el-select
v-model="formData.customerId"
disabled=""
placeholder=""
class="w-1/1"
>
<el-option
v-for="item in contactList"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="方案签约人" prop="pricingUserId">
<el-select
@ -94,14 +95,7 @@
</el-col>
<el-col :span="8">
<el-form-item label="授信计算周期" prop="creditCalcCycle" >
<el-select v-model="formData.creditCalcCycle" placeholder="" disabled>
<el-option
v-for="dict in getIntDictOptions('credit_calc_cycle')"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
<el-input v-model="formData.creditCalcCycle" placeholder="" disabled />
</el-form-item>
</el-col>
<el-col :span="8">
@ -209,11 +203,11 @@
<el-input v-model="formData.regType" placeholder="" disabled />
</el-form-item>
</el-col>
<el-col :span="8">
<!-- <el-col :span="8"> -->
<!-- <el-form-item label="融资信息" prop="financingInfo">
<el-input v-model="formData.financingInfo" placeholder="融资信息" disabled />
</el-form-item> -->
</el-col>
<!-- </el-col> -->
<el-col :span="8">
<el-form-item label="实缴资金" prop="actualCapital">
<el-input v-model="formData.actualCapital" placeholder="" disabled />
@ -224,11 +218,11 @@
<el-input v-model="formData.socialEmployeeNum" placeholder="" disabled />
</el-form-item>
</el-col>
<el-col :span="8">
<!-- <el-col :span="8"> -->
<!-- <el-form-item label="上市情况" prop="listingStatus">
<el-input v-model="formData.listingStatus" placeholder="上市情况" disabled />
</el-form-item> -->
</el-col>
<!-- </el-col> -->
<el-col :span="8">
<el-form-item label="注册地址" prop="registeredAddress">
<el-input v-model="formData.registeredAddress" placeholder="" disabled />
@ -268,10 +262,21 @@
</el-col>
<el-col :span="8">
<el-form-item label="商务负责人" prop="ownerUserId">
<el-input v-model="formData.ownerUserId" placeholder="" disabled />
<el-select
v-model="formData.ownerUserId"
disabled
class="w-1/1"
>
<el-option
v-for="item in userOptions"
:key="item.id"
:label="item.nickname"
:value="item.id"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<!-- <el-col :span="8">
<el-form-item label="手机号" prop="mobile">
<el-input
v-model="formData.ownerUserMobile"
@ -297,7 +302,7 @@
disabled
/>
</el-form-item>
</el-col>
</el-col> -->
<el-col :span="8">
<el-form-item label="归属部门" prop="deptId">
<el-tree-select
@ -856,7 +861,7 @@ onMounted(async () => {
// formData.value.ownerUserId = useUserStore().getUser.id
// }
// 获取联系人
contactList.value = await ContactApi.getSimpleContactList()
contactList.value = await CustomerApi.getSelfCustomerSimpleList()
const data = await ContractApi.getOrg({
pageNo: 1,
pageSize: 1000

View File

@ -1,14 +1,14 @@
<!-- 合同详情页面组件-->
<template>
<ContentWrap class="mt-10px">
<el-col>
<el-tabs>
<el-tab-pane label="跟进记录">
<!-- <ContentWrap class="mt-10px">
<el-col> -->
<!-- <el-tabs> -->
<!-- <el-tab-pane label="跟进记录">
<FollowUpList :biz-id="contract.id" :type="1" :biz-type="BizTypeEnum.CRM_CONTRACT" />
</el-tab-pane>
<el-tab-pane label="合同信息">
</el-tab-pane> -->
<!-- <el-tab-pane label="合同信息"> -->
<ContractDetailsInfo :contract="contract" :id="props.id" />
</el-tab-pane>
<!-- </el-tab-pane> -->
<!-- <el-tab-pane label="产品">
<ContractProductList :contract="contract" />
</el-tab-pane> -->
@ -26,7 +26,7 @@
:customer-id="contract.customerId"
/>
</el-tab-pane> -->
<el-tab-pane label="团队成员">
<!-- <el-tab-pane label="团队成员">
<PermissionList
ref="permissionListRef"
:type="1"
@ -38,10 +38,10 @@
</el-tab-pane>
<el-tab-pane label="操作日志">
<OperateLogV2 :log-list="logList" />
</el-tab-pane>
</el-tabs>
</el-col>
</ContentWrap>
</el-tab-pane> -->
<!-- </el-tabs> -->
<!-- </el-col>
</ContentWrap> -->
</template>
<script lang="ts" setup>

View File

@ -73,11 +73,11 @@
<!-- 列表 -->
<ContentWrap>
<el-tabs v-model="activeName" @tab-click="handleTabClick">
<!-- <el-tabs v-model="activeName" @tab-click="handleTabClick">
<el-tab-pane label="我负责的" name="1" />
<el-tab-pane label="我参与的" name="2" />
<el-tab-pane label="下属负责的" name="3" />
</el-tabs>
</el-tabs> -->
<el-table v-loading="loading" :data="list" :show-overflow-tooltip="true" :stripe="true">
<el-table-column align="center" fixed="left" label="合同名称" prop="name" width="160">
<template #default="scope">
@ -116,13 +116,13 @@
width="140"
:formatter="erpPriceTableColumnFormatter"
/>
<el-table-column
<!-- <el-table-column
align="center"
label="下单时间"
prop="orderDate"
width="120"
:formatter="dateFormatter2"
/>
/>-->
<el-table-column
align="center"
label="合同开始时间"
@ -137,44 +137,44 @@
width="120"
:formatter="dateFormatter2"
/>
<el-table-column align="center" label="客户签约人" prop="signContactName" width="130">
<!-- <template #default="scope">
<el-link
:underline="false"
type="primary"
@click="openContactDetail(scope.row.signContactId)"
>
{{ scope.row.signContactName }}
</el-link>
</template> -->
</el-table-column>
<el-table-column align="center" label="公司签约人" prop="signUserName" width="130" />
<el-table-column align="center" label="备注" prop="remark" width="200" />
<el-table-column
align="center"
label="已回款金额(元)"
prop="totalReceivablePrice"
width="140"
:formatter="erpPriceTableColumnFormatter"
/>
<el-table-column
align="center"
label="未回款金额(元)"
prop="totalReceivablePrice"
width="140"
:formatter="erpPriceTableColumnFormatter"
>
<template #default="scope">
{{ erpPriceInputFormatter(scope.row.totalPrice - scope.row.totalReceivablePrice) }}
</template>
</el-table-column>
<el-table-column
<!-- <el-table-column align="center" label="客户签约人" prop="signContactName" width="130">
<template #default="scope">
<el-link
:underline="false"
type="primary"
@click="openContactDetail(scope.row.signContactId)"
>
{{ scope.row.signContactName }}
</el-link>
</template>
</el-table-column>-->
<!-- <el-table-column align="center" label="公司签约人" prop="signUserName" width="130" />-->
<!-- <el-table-column align="center" label="备注" prop="remark" width="200" />
<el-table-column
align="center"
label="已回款金额(元)"
prop="totalReceivablePrice"
width="140"
:formatter="erpPriceTableColumnFormatter"
/>
<el-table-column
align="center"
label="未回款金额(元)"
prop="totalReceivablePrice"
width="140"
:formatter="erpPriceTableColumnFormatter"
>
<template #default="scope">
{{ erpPriceInputFormatter(scope.row.totalPrice - scope.row.totalReceivablePrice) }}
</template>
</el-table-column>-->
<!-- <el-table-column
:formatter="dateFormatter"
align="center"
label="最后跟进时间"
prop="contactLastTime"
width="180px"
/>
/>-->
<el-table-column align="center" label="负责人" prop="ownerUserName" width="120" />
<el-table-column align="center" label="所属部门" prop="ownerUserDeptName" width="100px" />
<el-table-column
@ -192,12 +192,12 @@
width="180px"
/>
<el-table-column align="center" label="创建人" prop="creatorName" width="120" />
<el-table-column align="center" label="合同状态" prop="auditStatus" width="120">
<el-table-column align="center" fixed="right" label="合同状态" prop="auditStatus" width="120">
<template #default="scope">
<dict-tag :type="DICT_TYPE.CRM_AUDIT_STATUS" :value="scope.row.auditStatus" />
</template>
</el-table-column>
<el-table-column fixed="right" label="操作" width="250">
<el-table-column align="center" fixed="right" label="操作" width="180">
<template #default="scope">
<div style="display:flex">
<el-button
@ -246,7 +246,7 @@
</el-button>
<el-button
v-if="scope.row.auditStatus !== 0 && scope.row.auditStatus !== 10"
v-if="scope.row.auditStatus !== 0 && scope.row.auditStatus !== 2 && scope.row.auditStatus !== 10"
v-hasPermi="['crm:contract:delete']"
link
type="danger"
@ -255,14 +255,16 @@
删除
</el-button>
<el-dropdown
v-if="scope.row.auditStatus === 0"
v-if="scope.row.auditStatus === 0 || scope.row.auditStatus === 2"
@command="(command) => handleCommand(command, scope.row)"
v-hasPermi="[
'crm:contract:update',
'crm:contract:delete'
'crm:contract:delete',
'crm:contract:print',
'crm:contract:change'
]"
>
<el-button type="primary" link><Icon icon="ep:d-arrow-right" /> 更多</el-button>
<el-button type="primary" link>更多<Icon icon="ep:d-arrow-right" /> </el-button>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item
@ -273,19 +275,19 @@
</el-dropdown-item>
<el-dropdown-item
command="handleChange"
v-if="checkPermi(['crm:contract:update']) && scope.row.auditStatus === 0"
v-if="checkPermi(['crm:contract:change']) && scope.row.auditStatus === 2"
>
合同变更
</el-dropdown-item>
<el-dropdown-item
command="handlePrint"
v-if="checkPermi(['crm:contract:update']) && scope.row.auditStatus === 0"
v-if="checkPermi(['crm:contract:print']) && scope.row.auditStatus === 2"
>
打印合同
合同下载
</el-dropdown-item>
<el-dropdown-item
command="handleDelete"
v-if="checkPermi(['crm:contract:delete']) && scope.row.auditStatus === 0"
v-if="checkPermi(['crm:contract:delete']) && (scope.row.auditStatus === 0 || scope.row.auditStatus === 2)"
>
删除
</el-dropdown-item>
@ -393,7 +395,7 @@ const handleChange = (row) => {
}
/** 操作分发 */
const handleCommand = (command: string, row: UserApi.UserVO) => {
const handleCommand = (command: string, row) => {
switch (command) {
case 'handleSubmit':
handleSubmit(row)
@ -402,7 +404,6 @@ const handleCommand = (command: string, row: UserApi.UserVO) => {
handleChange(row)
break
case 'handlePrint':
// getPrintTemplate(row)
onPrintContract(row)
break
case 'handleDelete':
@ -462,32 +463,33 @@ const onPrintContract = async (row: ContractApi.ContractVO) => {
throw new Error('返回的合同文件为空')
}
const contentType = res.type.toLowerCase()
let fileType = 'application/msword' // 默认使用 .doc 的 MIME 类型
let extension = '.doc'
// const contentType = res.type.toLowerCase()
// let fileType = 'application/msword' // 默认使用 .doc 的 MIME 类型
// let extension = '.doc'
if (contentType.includes('wordprocessingml')) {
fileType = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' // 对应 .docx 文件的 MIME 类型
extension = '.docx'
}
// 创建 blob 对象并设置文件类型
const blob = new Blob([res], { type: fileType })
// if (contentType.includes('wordprocessingml')) {
// fileType = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' // 对应 .docx 文件的 MIME 类型
// extension = '.docx'
// }
// // 创建 blob 对象并设置文件类型
// const blob = new Blob([res], { type: fileType })
const url = window.URL.createObjectURL(blob)
const link = document.createElement('a')
link.href = url
const disposition = res.headers['content-disposition']
let fileName = '合同.docx'
if (disposition && disposition.includes('filename=')) {
const match = disposition.match(/filename="?([^"]+)"?/)
if (match?.[1]) {
fileName = decodeURIComponent(match[1])
}
}
link.click()
window.URL.revokeObjectURL(url)
// const url = window.URL.createObjectURL(blob)
// const link = document.createElement('a')
// link.href = url
// const disposition = res.headers['content-disposition']
// let fileName = '合同.docx'
// if (disposition && disposition.includes('filename=')) {
// const match = disposition.match(/filename="?([^"]+)"?/)
// if (match?.[1]) {
// fileName = decodeURIComponent(match[1])
// }
// }
// link.click()
// console.log('%csrc/views/crm/contract/index.vue:489 res.data', 'color: #007acc;', res);
window.open(res)
message.success('提交审核成功!')
// message.success('提交审核成功!')
await getList()
} catch (error) {
console.error('合同下载失败', error)

View File

@ -259,7 +259,7 @@ onMounted(async () => {
await getApprovalDetail()
// 查询客户列表
customerList.value = await CustomerApi.getCustomerSimpleList()
customerList.value = await CustomerApi.getSelfCustomerSimpleList()
})
/** 审批相关:预测流程节点会因为输入的参数值而产生新的预测结果值,所以需重新预测一次, formData.value可改成实际业务中的特定字段

View File

@ -103,6 +103,7 @@ import * as ProcessInstanceApi from '@/api/bpm/processInstance'
import { CandidateStrategy, NodeId } from '@/components/SimpleProcessDesignerV2/src/consts'
import { ApprovalNodeInfo } from '@/api/bpm/processInstance'
import {defaultProps, handleTree} from "@/utils/tree";
import {getSelfCustomerSimpleList} from "@/api/crm/customer";
defineOptions({ name: 'CustomerSuggestionCreate' })
@ -249,7 +250,7 @@ onMounted(async () => {
await getApprovalDetail()
// 查询客户列表
customerList.value = await CustomerApi.getCustomerSimpleList()
customerList.value = await CustomerApi.getSelfCustomerSimpleList()
// 加载部门树
deptList.value = handleTree(await DeptApi.getSimpleDeptList())
})

View File

@ -266,13 +266,7 @@
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<el-form-item label="客户详情" prop="customerDetails">
<el-input v-model="formData.customerDetails" type="textarea" placeholder="请输入客户详情" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>

View File

@ -9,33 +9,32 @@
<el-descriptions-item label="客户名称">
{{ customer.name }}
</el-descriptions-item>
<el-descriptions-item label="信用统一代码">{{ customer.creditNo }}</el-descriptions-item>
<el-descriptions-item label="客户来源">
<dict-tag :type="DICT_TYPE.CRM_CUSTOMER_SOURCE" :value="customer.source" />
</el-descriptions-item>
<el-descriptions-item label="合作类型">{{ getName(getIntDictOptions(DICT_TYPE.CRM_COOPERATION_TYPE),customer.cooperationType) }}</el-descriptions-item>
<el-descriptions-item label="合作地区">{{ getName(getIntDictOptions(DICT_TYPE.CRM_COOPERATION_AREA),customer.cooperationArea) }}</el-descriptions-item>
<el-descriptions-item label="客户级别">
<dict-tag :type="DICT_TYPE.CRM_CUSTOMER_LEVEL" :value="customer.level" />
</el-descriptions-item>
<el-descriptions-item label="商务拓展人">{{ customer.developerName }}</el-descriptions-item>
<el-descriptions-item label="线索拓展人">{{ customer.clueDeveloperName }}</el-descriptions-item>
<el-descriptions-item label="协助人">{{ customer.assistName }}</el-descriptions-item>
<el-descriptions-item label="合作类型">{{ getName(getIntDictOptions(DICT_TYPE.CRM_COOPERATION_TYPE),customer.cooperationType) }}</el-descriptions-item>
<el-descriptions-item label="合作地区">{{ getName(getIntDictOptions(DICT_TYPE.CRM_COOPERATION_AREA),customer.cooperationArea) }}</el-descriptions-item>
<el-descriptions-item label="所属行业">{{ getName(getIntDictOptions(DICT_TYPE.CRM_CUSTOMER_INDUSTRY),customer.industryId) }}</el-descriptions-item>
<el-descriptions-item label="售后维护人">{{ customer.maintainerName }}</el-descriptions-item>
<el-descriptions-item label="商务签约人">{{ customer.contractorName }}</el-descriptions-item>
<el-descriptions-item label="信用统一代码">{{ customer.creditNo }}</el-descriptions-item>
<el-descriptions-item label="手机号">{{ customer.mobile }}</el-descriptions-item>
<el-descriptions-item label="电话">{{ customer.telephone }}</el-descriptions-item>
<el-descriptions-item label="邮箱">{{ customer.email }}</el-descriptions-item>
<el-descriptions-item label="地址">
{{ customer.areaName }} {{ customer.detailAddress }}
</el-descriptions-item>
<el-descriptions-item label="客户行业">
<dict-tag :type="DICT_TYPE.CRM_CUSTOMER_INDUSTRY" :value="customer.industryId" />
</el-descriptions-item>
<el-descriptions-item label="客户详情">{{ customer.customerDetails }}</el-descriptions-item>
<el-descriptions-item label=" 线索拓展人">{{ customer.developerName }}</el-descriptions-item>
<!-- <el-descriptions-item label="线索拓展人">{{ customer.clueDeveloperName }}</el-descriptions-item>-->
<el-descriptions-item label="协助人">{{ customer.assistName }}</el-descriptions-item>
=<el-descriptions-item label="售后维护人">{{ customer.maintainerName }}</el-descriptions-item>
<el-descriptions-item label="商务签约人">{{ customer.contractorName }}</el-descriptions-item>
<!-- <el-descriptions-item label="手机号">{{ customer.mobile }}</el-descriptions-item>
<el-descriptions-item label="电话">{{ customer.telephone }}</el-descriptions-item>
<el-descriptions-item label="邮箱">{{ customer.email }}</el-descriptions-item>-->
<el-descriptions-item label="地址">
{{ customer.areaName }} {{ customer.detailAddress }}
</el-descriptions-item>
<!-- <el-descriptions-item label="客户详情">{{ customer.customerDetails }}</el-descriptions-item>-->
<el-descriptions-item label="下次联系时间">
{{ formatDate(customer.contactNextTime) }}
</el-descriptions-item>

View File

@ -0,0 +1,234 @@
<template>
<CustomerDetailsHeader :customer="customer" :loading="loading">
<el-button
v-if="permissionListRef?.validateWrite"
v-hasPermi="['crm:customer:update']"
type="primary"
@click="openForm"
>
编辑
</el-button>
<!-- <el-button v-if="permissionListRef?.validateOwnerUser" type="primary" @click="transfer">
转移
</el-button> -->
<el-button v-if="permissionListRef?.validateWrite" @click="handleUpdateDealStatus">
更改成交状态
</el-button>
<el-button
v-if="customer.lockStatus && permissionListRef?.validateOwnerUser"
@click="handleUnlock"
>
解锁
</el-button>
<el-button
v-if="!customer.lockStatus && permissionListRef?.validateOwnerUser"
@click="handleLock"
>
锁定
</el-button>
<el-button v-if="!customer.ownerUserId" type="primary" @click="handleReceive"> 领取</el-button>
<el-button v-if="!customer.ownerUserId" type="primary" @click="handleDistributeForm">
分配
</el-button>
<el-button
v-if="customer.ownerUserId && permissionListRef?.validateOwnerUser"
@click="handlePutPool"
>
放入公海
</el-button>
</CustomerDetailsHeader>
<el-col>
<el-tabs>
<el-tab-pane label="跟进记录">
<FollowUpList :biz-id="customerId" :biz-type="BizTypeEnum.CRM_CUSTOMER" />
</el-tab-pane>
<el-tab-pane label="基本信息">
<CustomerDetailsInfo :customer="customer" />
</el-tab-pane>
<el-tab-pane label="工商信息">
<CompanyDetailsInfo :customer="customer" />
</el-tab-pane>
<el-tab-pane label="联系人" lazy>
<ContactList :biz-id="customer.id!" :biz-type="BizTypeEnum.CRM_CUSTOMER" />
</el-tab-pane>
<el-tab-pane label="团队成员">
<PermissionList
ref="permissionListRef"
:biz-id="customer.id!"
:biz-type="BizTypeEnum.CRM_CUSTOMER"
:show-action="!permissionListRef?.isPool || false"
@quit-team="close"
/>
</el-tab-pane>
<el-tab-pane label="商机" lazy>
<BusinessList :biz-id="customer.id!" :biz-type="BizTypeEnum.CRM_CUSTOMER" />
</el-tab-pane>
<el-tab-pane label="方案报价" lazy>
<QuotationList :biz-id="customer.id!" :biz-type="BizTypeEnum.CRM_CUSTOMER" />
</el-tab-pane>
<!-- <el-tab-pane label="合同" lazy>
<ContractList :biz-id="customer.id!" :biz-type="BizTypeEnum.CRM_CUSTOMER" />
</el-tab-pane>
<el-tab-pane label="回访" lazy>
<ReturnVisitrecord :biz-id="customerId" :biz-type="BizTypeEnum.CRM_CUSTOMER" />
</el-tab-pane>-->
<!-- <el-tab-pane label="回款" lazy>
<ReceivablePlanList :customer-id="customer.id!" @create-receivable="createReceivable" />
<ReceivableList ref="receivableListRef" :customer-id="customer.id!" />
</el-tab-pane> -->
<el-tab-pane label="操作日志">
<OperateLogV2 :log-list="logList" />
</el-tab-pane>
</el-tabs>
</el-col>
<!-- 表单弹窗:添加/修改 -->
<CustomerForm ref="formRef" @success="getCustomer" />
<CustomerDistributeForm ref="distributeForm" @success="getCustomer" />
<CrmTransferForm ref="transferFormRef" :biz-type="BizTypeEnum.CRM_CUSTOMER" @success="close" />
</template>
<script lang="ts" setup>
import { useTagsViewStore } from '@/store/modules/tagsView'
import * as CustomerApi from '@/api/crm/customer'
import CustomerForm from '@/views/crm/customer/CustomerForm.vue'
import CompanyDetailsInfo from './companyDetailsInfo.vue' // 工商信息
import CustomerDetailsInfo from './CustomerDetailsInfo.vue' // 客户明细 - 详细信息
import CustomerDetailsHeader from './CustomerDetailsHeader.vue' // 客户明细 - 头部
import ReturnVisitrecord from './returnvisitrecord/index.vue' // 回访
import ContactList from '@/views/crm/contact/components/ContactList.vue' // 联系人列表
import ContractList from '@/views/crm/contract/components/ContractList.vue' // 合同列表
import BusinessList from '@/views/crm/business/components/BusinessList.vue' // 商机列表
import QuotationList from '@/views/crm/contract/components/QuotationList.vue' // 报价列表
import ReceivableList from '@/views/crm/receivable/components/ReceivableList.vue' // 回款列表
import ReceivablePlanList from '@/views/crm/receivable/plan/components/ReceivablePlanList.vue' // 回款计划列表
import PermissionList from '@/views/crm/permission/components/PermissionList.vue' // 团队成员列表(权限)
import CrmTransferForm from '@/views/crm/permission/components/TransferForm.vue'
import FollowUpList from '@/views/crm/followup/index.vue'
import { BizTypeEnum } from '@/api/crm/permission'
import type { OperateLogVO } from '@/api/system/operatelog'
import { getOperateLogPage } from '@/api/crm/operateLog'
import CustomerDistributeForm from '@/views/crm/customer/pool/CustomerDistributeForm.vue'
defineOptions({ name: 'CrmCustomerDetail' })
const customerId = ref(0) // 客户编号
const loading = ref(true) // 加载中
const message = useMessage() // 消息弹窗
const { delView } = useTagsViewStore() // 视图操作
const { push, currentRoute } = useRouter() // 路由
const permissionListRef = ref<InstanceType<typeof PermissionList>>() // 团队成员列表 Ref
/** 获取详情 */
const customer = ref<CustomerApi.CustomerVO>({} as CustomerApi.CustomerVO) // 客户详情
const getCustomer = async () => {
loading.value = true
try {
customer.value = await CustomerApi.getCustomer(customerId.value)
await getOperateLog()
} finally {
loading.value = false
}
}
/** 编辑客户 */
const formRef = ref<InstanceType<typeof CustomerForm>>() // 客户表单 Ref
const openForm = () => {
formRef.value?.open('update', customerId.value)
}
/** 更新成交状态操作 */
const handleUpdateDealStatus = async () => {
const dealStatus = !customer.value.dealStatus
try {
// 更新状态的二次确认
await message.confirm(`确定更新成交状态为【${dealStatus ? '已成交' : '未成交'}】吗?`)
// 发起更新
await CustomerApi.updateCustomerDealStatus(customerId.value, dealStatus)
message.success(`更新成交状态成功`)
// 刷新数据
await getCustomer()
} catch {}
}
/** 客户转移 */
const transferFormRef = ref<InstanceType<typeof CrmTransferForm>>() // 客户转移表单 ref
const transfer = () => {
transferFormRef.value?.open(customerId.value)
}
/** 锁定客户 */
const handleLock = async () => {
await message.confirm(`确定锁定客户【${customer.value.name}】 吗?`)
await CustomerApi.lockCustomer(unref(customerId.value), true)
message.success(`锁定客户【${customer.value.name}】成功`)
await getCustomer()
}
/** 解锁客户 */
const handleUnlock = async () => {
await message.confirm(`确定解锁客户【${customer.value.name}】 吗?`)
await CustomerApi.lockCustomer(unref(customerId.value), false)
message.success(`解锁客户【${customer.value.name}】成功`)
await getCustomer()
}
/** 领取客户 */
const handleReceive = async () => {
await message.confirm(`确定领取客户【${customer.value.name}】 吗?`)
await CustomerApi.receiveCustomer([unref(customerId.value)])
message.success(`领取客户【${customer.value.name}】成功`)
await getCustomer()
}
/** 分配客户 */
const distributeForm = ref<InstanceType<typeof CustomerDistributeForm>>() // 分配客户表单 Ref
const handleDistributeForm = async () => {
distributeForm.value?.open(customerId.value)
}
/** 客户放入公海 */
const handlePutPool = async () => {
await message.confirm(`确定将客户【${customer.value.name}】放入公海吗?`)
await CustomerApi.putCustomerPool(unref(customerId.value))
message.success(`客户【${customer.value.name}】放入公海成功`)
// 加载
close()
}
/** 获取操作日志 */
const logList = ref<OperateLogVO[]>([]) // 操作日志列表
const getOperateLog = async () => {
if (!customerId.value) {
return
}
const data = await getOperateLogPage({
bizType: BizTypeEnum.CRM_CUSTOMER,
bizId: customerId.value
})
logList.value = data.list
}
/** 从回款计划创建回款 */
const receivableListRef = ref<InstanceType<typeof ReceivableList>>() // 回款列表 Ref
const createReceivable = (planData: any) => {
receivableListRef.value?.createReceivable(planData)
}
const close = () => {
delView(unref(currentRoute))
push({ name: 'CrmCustomer' })
}
/** 初始化 */
const { params } = useRoute()
onMounted(() => {
if (!params.id) {
message.warning('参数错误,客户不能为空!')
close()
return
}
customerId.value = params.id as unknown as number
getCustomer()
})
</script>

View File

@ -8,9 +8,9 @@
>
编辑
</el-button>
<el-button v-if="permissionListRef?.validateOwnerUser" type="primary" @click="transfer">
<!-- <el-button v-if="permissionListRef?.validateOwnerUser" type="primary" @click="transfer">
转移
</el-button>
</el-button> -->
<el-button v-if="permissionListRef?.validateWrite" @click="handleUpdateDealStatus">
更改成交状态
</el-button>
@ -31,7 +31,7 @@
分配
</el-button>
<el-button
v-if="customer.ownerUserId && permissionListRef?.validateOwnerUser"
v-if="customer.ownerUserId && permissionListRef?.validateOwnerUser && !customer.dealStatus"
@click="handlePutPool"
>
放入公海

View File

@ -59,7 +59,7 @@
</el-col>
<el-col :span="24">
<el-form-item label="附件" prop="fileUrls">
<UploadFile v-model="formData.fileUrls" />
<UploadFile v-model="formData.fileUrls" :limit="1" />
</el-form-item>
</el-col>
@ -162,6 +162,7 @@ const submitForm = async () => {
const data = {
...formData.value,
contactIds: formData.value.contacts.map((item) => item.id),
fileUrls: [formData.value.fileUrls]
} as unknown as ReturnVisitRecordVO
await ReturnVisitRecordApi.createReturnVisitRecord(data)
message.success(t('common.createSuccess'))

View File

@ -27,11 +27,22 @@
align="center"
prop="nextTime"
:formatter="dateFormatter"
width="180px"
/>
<el-table-column label="图片" align="center" prop="picUrls" />
width="180px" />
<el-table-column label="图片" align="center" prop="picUrls">
<template #default="scope">
<el-link
v-if="scope.row.picUrls"
:underline="false"
type="primary"
@click="previewPic(scope.row.picUrls)"
>
预览
</el-link>
<span v-else>无附件</span>
</template>
</el-table-column>
<el-table-column label="附件" align="center" prop="fileUrls">
<!-- <template #default="scope">
<template #default="scope">
<el-link
v-if="scope.row.fileUrls"
:underline="false"
@ -41,9 +52,9 @@
预览
</el-link>
<span v-else>无附件</span>
</template> -->
</template>
</el-table-column>
<el-table-column label="关联的联系人编号数组" align="center" prop="contactIds" />
<!-- <el-table-column label="关联的联系人编号数组" align="center" prop="contactIds" /> -->
<el-table-column label="创建者" align="center" prop="creator" >
<template #default="scope">
{{getName(userOptions, scope.row.creator)}}
@ -68,7 +79,7 @@
:formatter="dateFormatter"
width="180px"
/>
<el-table-column label="操作" align="center" min-width="120px">
<el-table-column label="操作" align="center" fixed="right" min-width="120px">
<template #default="scope">
<el-button
link
@ -88,6 +99,12 @@
@pagination="getList"
/>
</ContentWrap>
<el-image-viewer
v-if="show"
:url-list="urlList"
show-progress
@close="show = false"
/>
<!-- 表单弹窗:添加/修改 -->
<ReturnVisitRecordForm ref="formRef" @success="getList" />
@ -110,7 +127,8 @@ const props = defineProps<{
const message = useMessage() // 消息弹窗
const { t } = useI18n() // 国际化
const userOptions = ref<UserApi.UserVO[]>([]) // 用户列表
const show = ref(false)
const urlList = ref([])
const loading = ref(true) // 列表的加载中
const list = ref<ReturnVisitRecordVO[]>([]) // 列表的数据
const total = ref(0) // 列表的总页数
@ -137,6 +155,8 @@ const exportLoading = ref(false) // 导出的加载中
const getList = async () => {
loading.value = true
try {
// queryParams.bizType = props.bizType
queryParams.customerId = props.bizId
const data = await ReturnVisitRecordApi.getReturnVisitRecordPage(queryParams)
list.value = data.list
total.value = data.total
@ -190,9 +210,17 @@ const handleExport = async () => {
exportLoading.value = false
}
}
const router = useRouter() // 路由
const previewPic = (val) => {
show.value = true
urlList.value = val.split(',')
}
const previewFile = (fileUrls) => {
// 假设 fileUrls 是一个字符串,包含文件的完整 URL
// // 假设 fileUrls 是一个字符串,包含文件的完整 URL
if (!fileUrls) {
message.error('没有附件');
return;
@ -200,25 +228,13 @@ const previewFile = (fileUrls) => {
// 检查文件类型
const fileExtension = fileUrls.split('.').pop().toLowerCase();
switch (fileExtension) {
case 'pdf':
// 预览 PDF 文件
window.open(fileUrls, '_blank');
break;
case 'xlsx':
case 'xls':
// 预览 Excel 文件
window.open(fileUrls, '_blank');
break;
case 'doc':
case 'docx':
// 预览 Word 文件
window.open(fileUrls, '_blank');
break;
default:
message.error('不支持的文件类型');
break;
}
router.push({
name: 'FileTemplatePreview',
query: {
src: encodeURIComponent(fileUrls),
type: fileExtension
}
})
};
const getName = (opt, val) => {
const arr = opt.filter(v => v.id == val)

View File

@ -110,7 +110,7 @@
<el-tabs v-model="activeName" @tab-click="handleTabClick">
<el-tab-pane label="我负责的" name="1" />
<el-tab-pane label="我参与的" name="2" />
<el-tab-pane label="下属负责的" name="3" />
<!-- <el-tab-pane label="下属负责的" name="3" /> -->
</el-tabs>
<el-table v-loading="loading" :data="list" :show-overflow-tooltip="true" :stripe="true">
<el-table-column align="center" fixed="left" label="客户名称" prop="name" width="250">
@ -212,7 +212,7 @@
width="180px"
/>
<el-table-column align="center" label="创建人" prop="creatorName" width="100px" />
<el-table-column align="center" fixed="right" label="操作" min-width="150">
<el-table-column align="center" fixed="right" label="操作" min-width="150" v-if="activeName != 2">
<template #default="scope">
<el-button
v-hasPermi="['crm:customer:update']"

View File

@ -1,6 +1,4 @@
<template>
<doc-alert title="【客户】客户管理、公海客户" url="https://doc.iocoder.cn/crm/customer/" />
<doc-alert title="【通用】数据权限" url="https://doc.iocoder.cn/crm/permission/" />
<ContentWrap>
<!-- 搜索工作栏 -->
@ -271,7 +269,7 @@ const resetQuery = () => {
/** 打开客户详情 */
const { currentRoute, push } = useRouter()
const openDetail = (id: number) => {
push({ name: 'CrmCustomerDetail', params: { id } })
push({ name: 'CrmCustomerHighSeasDetail', params: { id } })
}
/** 领取客户 */

View File

@ -36,14 +36,14 @@
<el-input v-model="formData.content" :rows="3" type="textarea" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-col :span="24">
<el-form-item label="图片" prop="picUrls">
<UploadImgs v-model="formData.picUrls" class="min-w-80px" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-col :span="24">
<el-form-item label="附件" prop="fileUrls">
<UploadFile v-model="formData.fileUrls" class="min-w-80px" />
<UploadFile v-model="formData.fileUrls" :limit="1" class="min-w-80px" />
</el-form-item>
</el-col>
<el-col :span="24" v-if="formData.bizType == BizTypeEnum.CRM_CUSTOMER">
@ -137,7 +137,8 @@ const submitForm = async () => {
const data = {
...formData.value,
contactIds: formData.value.contacts.map((item) => item.id),
businessIds: formData.value.businesses.map((item) => item.id)
businessIds: formData.value.businesses.map((item) => item.id),
fileUrls: [formData.value.fileUrls]
} as unknown as FollowUpRecordVO
await FollowUpRecordApi.createFollowUpRecord(data)
message.success(t('common.createSuccess'))

View File

@ -24,6 +24,32 @@
</template>
</el-table-column>
<el-table-column align="center" label="跟进内容" prop="content" />
<el-table-column label="图片" align="center" prop="picUrls">
<template #default="scope">
<el-link
v-if="scope.row.picUrls"
:underline="false"
type="primary"
@click="previewPic(scope.row.picUrls)"
>
预览
</el-link>
<span v-else>无附件</span>
</template>
</el-table-column>
<el-table-column label="附件" align="center" prop="fileUrls">
<template #default="scope">
<el-link
v-if="scope.row.fileUrls"
:underline="false"
type="primary"
@click="previewFile(scope.row.fileUrls)"
>
预览
</el-link>
<span v-else>无附件</span>
</template>
</el-table-column>
<el-table-column
:formatter="dateFormatter"
align="center"
@ -84,6 +110,14 @@
/>
</ContentWrap>
<el-image-viewer
v-if="show"
:url-list="urlList"
show-progress
@close="show = false"
/>
<!-- 表单弹窗:添加/修改 -->
<FollowUpRecordForm ref="formRef" @success="getList" />
</template>
@ -104,7 +138,8 @@ const props = defineProps<{
}>()
const message = useMessage() // 消息弹窗
const { t } = useI18n() // 国际化
const show = ref(false)
const urlList = ref([])
const loading = ref(true) // 列表的加载中
const list = ref<FollowUpRecordVO[]>([]) // 列表的数据
const total = ref(0) // 列表的总页数
@ -127,6 +162,11 @@ const getList = async () => {
}
}
const previewPic = (val) => {
show.value = true
urlList.value = val
}
/** 添加/修改操作 */
const formRef = ref<InstanceType<typeof FollowUpRecordForm>>()
const openForm = () => {
@ -146,6 +186,27 @@ const handleDelete = async (id: number) => {
} catch {}
}
//预览
const router = useRouter() // 路由
const previewFile = (fileUrls) => {
// // 假设 fileUrls 是一个字符串,包含文件的完整 URL
fileUrls = fileUrls + ''
if (!fileUrls) {
message.error('没有附件');
return;
}
// 检查文件类型
const fileExtension = fileUrls.split('.').pop().toLowerCase();
router.push({
name: 'FileTemplatePreview',
query: {
src: encodeURIComponent(fileUrls),
type: fileExtension
}
})
};
/** 打开联系人详情 */
const { push } = useRouter()
const openContactDetail = (id: number) => {

View File

@ -1,60 +1,107 @@
<template>
<Dialog :title="dialogTitle" v-model="dialogVisible">
<Dialog :title="dialogTitle" v-model="dialogVisible" width="50%">
<el-form
ref="formRef"
:model="formData"
:rules="formRules"
label-width="100px"
label-width="164px"
v-loading="formLoading"
>
<el-form-item label="产品ID" prop="projectId">
<el-input v-model="formData.projectId" placeholder="请输入产品ID" />
</el-form-item>
<el-form-item label="产品编号" prop="projectCode">
<el-input v-model="formData.projectCode" placeholder="请输入产品编号" />
</el-form-item>
<el-form-item label="客单价" prop="customerPrice">
<el-input v-model="formData.customerPrice" placeholder="请输入客单价" />
</el-form-item>
<el-form-item label="采购成本" prop="purchaseCost">
<el-input v-model="formData.purchaseCost" placeholder="请输入采购成本" />
</el-form-item>
<el-form-item label="代理费前返比例/金额" prop="agentFeePreReturn">
<el-input v-model="formData.agentFeePreReturn" placeholder="请输入代理费前返比例/金额" />
</el-form-item>
<el-form-item label="代理费后返比例/金额" prop="agentFeePostReturn">
<el-input v-model="formData.agentFeePostReturn" placeholder="请输入代理费后返比例/金额" />
</el-form-item>
<el-form-item label="线上溢价率" prop="onlineMarkupRate">
<el-input v-model="formData.onlineMarkupRate" placeholder="请输入线上溢价率" />
</el-form-item>
<el-form-item label="线下溢价率" prop="offlineMarkupRate">
<el-input v-model="formData.offlineMarkupRate" placeholder="请输入线下溢价率" />
</el-form-item>
<el-form-item label="税费成本" prop="taxCost">
<el-input v-model="formData.taxCost" placeholder="请输入税费成本" />
</el-form-item>
<el-form-item label="管理成本" prop="managementCost">
<el-input v-model="formData.managementCost" placeholder="请输入管理成本" />
</el-form-item>
<el-form-item label="结算对账成本" prop="settlementReconciliationCost">
<el-input v-model="formData.settlementReconciliationCost" placeholder="请输入结算对账成本" />
</el-form-item>
<el-form-item label="结算开票成本" prop="settlementInvoiceCost">
<el-input v-model="formData.settlementInvoiceCost" placeholder="请输入结算开票成本" />
</el-form-item>
<el-form-item label="顾问订单成本" prop="consultantOrderCost">
<el-input v-model="formData.consultantOrderCost" placeholder="请输入顾问订单成本" />
</el-form-item>
<el-form-item label="溢价分佣" prop="markupCommission">
<el-input v-model="formData.markupCommission" placeholder="请输入溢价分佣" />
</el-form-item>
<el-form-item label="顾问管理" prop="consultantManagement">
<el-input v-model="formData.consultantManagement" placeholder="请输入顾问管理" />
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="formData.remark" placeholder="请输入备注" />
</el-form-item>
<el-row>
<el-col :span="12">
<el-form-item label="产品名称" prop="projectId">
<el-select
v-model="formData.projectId"
@change="changeProduct"
clearable
filterable
placeholder="请选择产品名称"
>
<el-option
v-for="item in productList"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="产品编号" prop="projectCode">
<el-input v-model="formData.projectCode" disabled placeholder="" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="客单价" prop="customerPrice">
<el-input v-model="formData.customerPrice" placeholder="请输入客单价" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="采购成本" prop="purchaseCost">
<el-input v-model="formData.purchaseCost" placeholder="请输入采购成本" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="代理费前返比例/金额" prop="agentFeePreReturn">
<el-input v-model="formData.agentFeePreReturn" placeholder="请输入代理费前返比例/金额" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="代理费后返比例/金额" prop="agentFeePostReturn">
<el-input v-model="formData.agentFeePostReturn" placeholder="请输入代理费后返比例/金额" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="线上溢价率" prop="onlineMarkupRate">
<el-input v-model="formData.onlineMarkupRate" placeholder="请输入线上溢价率" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="线下溢价率" prop="offlineMarkupRate">
<el-input v-model="formData.offlineMarkupRate" placeholder="请输入线下溢价率" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="税费成本" prop="taxCost">
<el-input v-model="formData.taxCost" placeholder="请输入税费成本" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="管理成本" prop="managementCost">
<el-input v-model="formData.managementCost" placeholder="请输入管理成本" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="结算对账成本" prop="settlementReconciliationCost">
<el-input v-model="formData.settlementReconciliationCost" placeholder="请输入结算对账成本" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="结算开票成本" prop="settlementInvoiceCost">
<el-input v-model="formData.settlementInvoiceCost" placeholder="请输入结算开票成本" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="顾问订单成本" prop="consultantOrderCost">
<el-input v-model="formData.consultantOrderCost" placeholder="请输入顾问订单成本" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="溢价分佣" prop="markupCommission">
<el-input v-model="formData.markupCommission" placeholder="请输入溢价分佣" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="顾问管理" prop="consultantManagement">
<el-input v-model="formData.consultantManagement" placeholder="请输入顾问管理" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="备注" prop="remark">
<el-input type="textarea" :rows="5" v-model="formData.remark" placeholder="请输入备注" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
@ -67,7 +114,9 @@ import { ProductProcurementCostApi, ProductProcurementCostVO } from '@/api/crm/p
/** 产品采购成本 表单 */
defineOptions({ name: 'ProductProcurementCostForm' })
import * as ProductApi from '@/api/crm/product'
const productList = ref<ProductApi.ProductVO[]>([]) // 产品列表
const { t } = useI18n() // 国际化
const message = useMessage() // 消息弹窗
@ -78,6 +127,9 @@ const formType = ref('') // 表单的类型:create - 新增;update - 修改
const formData = ref({
projectId: undefined,
projectCode: undefined,
category: undefined,
detailType: undefined,
unit: undefined,
customerPrice: undefined,
purchaseCost: undefined,
agentFeePreReturn: undefined,
@ -117,6 +169,7 @@ const open = async (type: string, id?: number) => {
dialogTitle.value = t('action.' + type)
formType.value = type
resetForm()
console.log('%csrc/views/crm/productprocurementcost/ProductProcurementCostForm.vue:172 id', 'color: #007acc;', id);
// 修改时,设置数据
if (id) {
formLoading.value = true
@ -129,6 +182,16 @@ const open = async (type: string, id?: number) => {
}
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
const changeProduct = (val) => {
let arr = productList.value.filter(v => v.id === val)
if (arr.length) {
formData.value.projectCode = arr[0]['no']
formData.value.category = arr[0]['category']
formData.value.detailType = arr[0]['detailType']
formData.value.unit = arr[0]['unit']
}
}
/** 提交表单 */
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
const submitForm = async () => {
@ -175,4 +238,8 @@ const resetForm = () => {
}
formRef.value?.resetFields()
}
onMounted( async() => {
productList.value = await ProductApi.getProductSimpleList()
})
</script>

View File

@ -8,10 +8,10 @@
:inline="true"
label-width="68px"
>
<el-form-item label="产品ID" prop="projectId">
<el-form-item label="产品名称" prop="projectId">
<el-input
v-model="queryParams.projectId"
placeholder="请输入产品ID"
placeholder="请选择产品"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
@ -64,9 +64,12 @@
<!-- 列表 -->
<ContentWrap>
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
<el-table-column label="产品ID" align="center" prop="projectId" />
<el-table-column label="产品名称" align="center" prop="projectId" />
<el-table-column label="产品编号" align="center" prop="projectCode" />
<el-table-column label="产品类型" align="center" prop="projectCode" />
<el-table-column label="明细类型" align="center" prop="projectCode" />
<el-table-column label="客单价" align="center" prop="customerPrice" />
<el-table-column label="单位" align="center" prop="projectCode" />
<el-table-column label="采购成本" align="center" prop="purchaseCost" />
<el-table-column label="代理费前返比例/金额" align="center" prop="agentFeePreReturn" />
<el-table-column label="代理费后返比例/金额" align="center" prop="agentFeePostReturn" />
@ -87,12 +90,12 @@
:formatter="dateFormatter"
width="180px"
/>
<el-table-column label="操作" align="center" min-width="120px">
<el-table-column label="操作" align="center" fixed="right" min-width="120px">
<template #default="scope">
<el-button
link
type="primary"
@click="openForm('update', scope.row.id)"
@click="openForm('update', scope.row.projectId)"
v-hasPermi="['crm:product-procurement-cost:update']"
>
编辑
@ -208,4 +211,4 @@ const handleExport = async () => {
onMounted(() => {
getList()
})
</script>
</script>

View File

@ -0,0 +1,103 @@
<template>
<Dialog :title="dialogTitle" v-model="dialogVisible">
<el-form
ref="formRef"
:model="formData"
:rules="formRules"
label-width="150px"
v-loading="formLoading"
>
<el-form-item label="项目难度" prop="projectDifficulty">
<el-input v-model="formData.projectDifficulty" placeholder="请输入项目难度" />
</el-form-item>
<el-form-item label="说明" prop="dockingMode">
<el-input v-model="formData.dockingMode" type="textarea" placeholder="请输入说明" />
</el-form-item>
<el-form-item label="对接工时(人天)" prop="dockingHours">
<el-input v-model="formData.dockingHours" placeholder="请输入对接工时(人天)" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
<el-button @click="dialogVisible = false">取 消</el-button>
</template>
</Dialog>
</template>
<script setup lang="ts">
import { ProjectDifficultyApi, ProjectDifficultyVO } from '@/api/crm/projectdifficulty'
/** 项目难度 表单 */
defineOptions({ name: 'ProjectDifficultyForm' })
const { t } = useI18n() // 国际化
const message = useMessage() // 消息弹窗
const dialogVisible = ref(false) // 弹窗的是否展示
const dialogTitle = ref('') // 弹窗的标题
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
const formType = ref('') // 表单的类型:create - 新增;update - 修改
const formData = ref({
id: undefined,
projectDifficulty: undefined,
dockingMode: undefined,
dockingHours: undefined,
})
const formRules = reactive({
projectDifficulty: [{ required: true, message: '项目难度不能为空', trigger: 'blur' }],
dockingMode: [{ required: true, message: '说明不能为空', trigger: 'blur' }],
})
const formRef = ref() // 表单 Ref
/** 打开弹窗 */
const open = async (type: string, id?: number) => {
dialogVisible.value = true
dialogTitle.value = t('action.' + type)
formType.value = type
resetForm()
// 修改时,设置数据
if (id) {
formLoading.value = true
try {
formData.value = await ProjectDifficultyApi.getProjectDifficulty(id)
} finally {
formLoading.value = false
}
}
}
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
/** 提交表单 */
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
const submitForm = async () => {
// 校验表单
await formRef.value.validate()
// 提交请求
formLoading.value = true
try {
const data = formData.value as unknown as ProjectDifficultyVO
if (formType.value === 'create') {
await ProjectDifficultyApi.createProjectDifficulty(data)
message.success(t('common.createSuccess'))
} else {
await ProjectDifficultyApi.updateProjectDifficulty(data)
message.success(t('common.updateSuccess'))
}
dialogVisible.value = false
// 发送操作成功的事件
emit('success')
} finally {
formLoading.value = false
}
}
/** 重置表单 */
const resetForm = () => {
formData.value = {
id: undefined,
projectDifficulty: undefined,
dockingMode: undefined,
dockingHours: undefined,
}
formRef.value?.resetFields()
}
</script>

View File

@ -0,0 +1,192 @@
<template>
<ContentWrap>
<!-- 搜索工作栏 -->
<el-form
class="-mb-15px"
:model="queryParams"
ref="queryFormRef"
:inline="true"
label-width="80px"
>
<el-form-item label="项目难度" prop="projectDifficulty">
<el-input
v-model="queryParams.projectDifficulty"
placeholder="请输入项目难度"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item>
<el-form-item label="创建时间" prop="createTime">
<el-date-picker
v-model="queryParams.createTime"
value-format="YYYY-MM-DD HH:mm:ss"
type="daterange"
start-placeholder="开始日期"
end-placeholder="结束日期"
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
class="!w-220px"
/>
</el-form-item>
<el-form-item>
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
<el-button
type="primary"
plain
@click="openForm('create')"
v-hasPermi="['crm:project-difficulty:create']"
>
<Icon icon="ep:plus" class="mr-5px" /> 新增
</el-button>
<el-button
type="success"
plain
@click="handleExport"
:loading="exportLoading"
v-hasPermi="['crm:project-difficulty:export']"
>
<Icon icon="ep:download" class="mr-5px" /> 导出
</el-button>
</el-form-item>
</el-form>
</ContentWrap>
<!-- 列表 -->
<ContentWrap>
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
<el-table-column label="主键ID" align="center" prop="id" />
<el-table-column label="项目难度" align="center" prop="projectDifficulty" />
<el-table-column label="说明" align="center" prop="dockingMode" />
<el-table-column label="对接工时(人天)" align="center" prop="dockingHours" />
<el-table-column
label="创建时间"
align="center"
prop="createTime"
:formatter="dateFormatter"
width="180px"
/>
<el-table-column label="操作" align="center" min-width="120px">
<template #default="scope">
<el-button
link
type="primary"
@click="openForm('update', scope.row.id)"
v-hasPermi="['crm:project-difficulty:update']"
>
编辑
</el-button>
<el-button
link
type="danger"
@click="handleDelete(scope.row.id)"
v-hasPermi="['crm:project-difficulty:delete']"
>
删除
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<Pagination
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
</ContentWrap>
<!-- 表单弹窗:添加/修改 -->
<ProjectDifficultyForm ref="formRef" @success="getList" />
</template>
<script setup lang="ts">
import { dateFormatter } from '@/utils/formatTime'
import download from '@/utils/download'
import { ProjectDifficultyApi, ProjectDifficultyVO } from '@/api/crm/projectdifficulty'
import ProjectDifficultyForm from './ProjectDifficultyForm.vue'
/** 项目难度 列表 */
defineOptions({ name: 'ProjectDifficulty' })
const message = useMessage() // 消息弹窗
const { t } = useI18n() // 国际化
const loading = ref(true) // 列表的加载中
const list = ref<ProjectDifficultyVO[]>([]) // 列表的数据
const total = ref(0) // 列表的总页数
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
projectDifficulty: undefined,
dockingMode: undefined,
dockingHours: undefined,
createTime: [],
})
const queryFormRef = ref() // 搜索的表单
const exportLoading = ref(false) // 导出的加载中
/** 查询列表 */
const getList = async () => {
loading.value = true
try {
const data = await ProjectDifficultyApi.getProjectDifficultyPage(queryParams)
list.value = data.list
total.value = data.total
} finally {
loading.value = false
}
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.pageNo = 1
getList()
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value.resetFields()
handleQuery()
}
/** 添加/修改操作 */
const formRef = ref()
const openForm = (type: string, id?: number) => {
formRef.value.open(type, id)
}
/** 删除按钮操作 */
const handleDelete = async (id: number) => {
try {
// 删除的二次确认
await message.delConfirm()
// 发起删除
await ProjectDifficultyApi.deleteProjectDifficulty(id)
message.success(t('common.delSuccess'))
// 刷新列表
await getList()
} catch {}
}
/** 导出按钮操作 */
const handleExport = async () => {
try {
// 导出的二次确认
await message.exportConfirm()
// 发起导出
exportLoading.value = true
const data = await ProjectDifficultyApi.exportProjectDifficulty(queryParams)
download.excel(data, '项目难度.xls')
} catch {
} finally {
exportLoading.value = false
}
}
/** 初始化 **/
onMounted(() => {
getList()
})
</script>

View File

@ -10,7 +10,7 @@
<el-row>
<el-col :span="8">
<el-col :span="8" v-if="!route.query.taskId">
<el-form-item label="选择商机" prop="businessId">
<el-select v-model="formData.businessId" disabled clearable @change="onBusinessChange">
<el-option
@ -173,6 +173,11 @@
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="统一信用代码" prop="creditNo">
<el-input v-model="formData.creditCode" placeholder="" disabled />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="开户行" prop="bankName">
<el-input v-model="formData.bankName" disabled />
@ -193,11 +198,11 @@
<el-input v-model="formData.bBusinessLicenseNumber" disabled />
</el-form-item>
</el-col> -->
<el-col :span="8">
<!-- <el-col :span="8">
<el-form-item label="营业执照" prop="businessLicense">
<el-input v-model="formData.businessLicense" disabled />
</el-form-item>
</el-col>
</el-col> -->
</el-row>
@ -564,7 +569,23 @@ const open = async (type: string) => {
if (type) {
formLoading.value = true
try {
let totalOnlinePrice = 0;
let totalOfflinePrice = 0;
formData.value = await QuotationApi.getQuotation(type)
for (const product of formData.value.products) {
if (typeof product.onlinePrice) {
totalOnlinePrice += Number(product.onlinePrice);
}
if (typeof product.offlinePrice) {
totalOfflinePrice += Number(product.offlinePrice);
}
}
formData.value.onlinePrice = totalOnlinePrice.toFixed(2)
formData.value.offlinePrice = (totalOfflinePrice + totalOnlinePrice).toFixed(2)
let all = totalOnlinePrice + totalOfflinePrice + totalOnlinePrice
formData.value.totalPrice = all.toFixed(2)
formData.value.creditLimit = ((formData.value.creditAmount || 0) + (formData.value.creditLimitNum || 0)).toFixed(2)
} finally {
formLoading.value = false

View File

@ -47,7 +47,7 @@
</el-col>
<el-col :span="8">
<el-form-item label="客户" prop="customerId">
<el-select v-model="formData.customerId" placeholder="请选择客户" disabled clearable @change="onCustomerChange">
<el-select v-model="formData.customerId" placeholder="" disabled clearable @change="onCustomerChange">
<el-option
v-for="item in customerList"
:key="item.id"
@ -172,6 +172,11 @@
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="统一信用代码" prop="creditCode">
<el-input v-model="formData.creditCode" placeholder="" disabled />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="开户行" prop="bankName">
@ -229,7 +234,7 @@
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<!-- <el-col :span="8">
<el-form-item label="拓展人" prop="expanderUserId">
<el-select v-model="formData.expanderUserId" placeholder="请选择拓展人">
<el-option
@ -240,10 +245,10 @@
/>
</el-select>
</el-form-item>
</el-col>
</el-col> -->
<el-col :span="8">
<el-form-item label="方案报价人" prop="pricingUserId">
<el-select v-model="formData.pricingUserId" placeholder="请选择方案报价人">
<el-select v-model="formData.pricingUserId" disabled placeholder="请选择方案报价人">
<el-option
v-for="dict in userOptions"
:key="dict.id"
@ -253,7 +258,7 @@
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<!-- <el-col :span="8">
<el-form-item label="报价签约人" prop="signUserId">
<el-select v-model="formData.signUserId" placeholder="请选择报价签约人">
<el-option
@ -264,7 +269,7 @@
/>
</el-select>
</el-form-item>
</el-col>
</el-col> -->
<!-- <el-col :span="8">
<el-form-item label="签约人联系电话" prop="signPhoneNumber">
<el-input v-model="formData.signPhoneNumber" placeholder="请输入签约人联系电话" />
@ -307,22 +312,19 @@
</el-col>
<el-col :span="8">
<el-form-item label="授信计算周期" prop="creditCalcCycle">
<el-select v-model="formData.creditCalcCycle" placeholder="请选择授信计算周期">
<el-input v-model="formData.creditCalcCycle" disabled placeholder="" />
<!-- <el-select v-model="formData.creditCalcCycle" placeholder="请选择授信计算周期">
<el-option
v-for="dict in getIntDictOptions('credit_calc_cycle')"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-select> -->
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="授信额度" prop="creditLimit">
<el-input v-model="formData.creditLimit" placeholder="请输入授信额度" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-col :span="8">
<el-form-item label="票据模板" prop="invoiceTemplateId">
<el-select v-model="formData.invoiceTemplateId" :disabled="!formData.businessId" @change="changeTemplate" placeholder="请选择票据模板">
<el-option
@ -334,12 +336,16 @@
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="第几次报价" prop="quotationTimes">
<el-input v-model="formData.quotationTimes" placeholder="请输入报价次数" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="第几次报价" prop="quotationTimes">
<el-input v-model="formData.quotationTimes" placeholder="请输入报价次数" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="授信额度" prop="creditLimit">
<el-input v-model="formData.creditLimit" placeholder="请输入授信额度" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="8">
@ -372,9 +378,12 @@
</el-row>
</el-form>
<!-- 子表的表单 -->
<el-tabs v-model="subTabsName">
<el-tabs v-model="subTabsName" @tab-Click="handleClick">
<el-tab-pane label="报价产品关联" name="quotationProduct">
<QuotationProductForm ref="quotationProductFormRef" :handleType="handleType" :quotation-id="formData.id" :business-id="formData.businessId" :billTemplateId="formData.invoiceTemplateId" :products="formData.products" />
<QuotationProductForm ref="quotationProductFormRef" :handleType="handleType" :quotation-id="formData.id" :business-id="formData.businessId" :billTemplateId="formData.invoiceTemplateId" :products="formData.products" @success="setSuccess" />
</el-tab-pane>
<el-tab-pane label="营收分析" name="RevenueAnalysis">
<RevenueAnalysis ref="RevenueAnalysisFormRef" :quotation-id="formData.id" :products="formData.products" />
</el-tab-pane>
</el-tabs>
<div style="text-align: right">
@ -389,12 +398,14 @@ import { QuotationApi, QuotationVO } from '@/api/crm/quotation'
import { defaultProps, handleTree } from '@/utils/tree'
import { BillTemplateApi, BillTemplateVO } from '@/api/crm/billtemplate'
import QuotationProductForm from './components/QuotationProductForm.vue'
import RevenueAnalysis from './components/RevenueAnalysis.vue'
import * as ContractApi from '@/api/crm/contract'
import * as CustomerApi from '@/api/crm/customer'
import * as UserApi from '@/api/system/user'
import * as DeptApi from '@/api/system/dept'
import * as BusinessApi from '@/api/crm/business'
import { propTypes } from '@/utils/propTypes'
import { useUserStore } from '@/store/modules/user';
/** CRM 方案报价 表单 */
defineOptions({ name: 'QuotationForm' })
@ -493,6 +504,64 @@ const formRef = ref() // 表单 Ref
/** 子表的表单 */
const subTabsName = ref('quotationProduct')
const quotationProductFormRef = ref()
watch(
() => formData.value.products,
(newProducts) => {
if (!Array.isArray(newProducts)) {
console.warn('formData.value.products is not an array');
return;
}
let totalOnlinePrice = 0;
let totalOfflinePrice = 0;
for (const product of newProducts) {
if (typeof product.onlinePrice) {
totalOnlinePrice += Number(product.onlinePrice);
}
if (typeof product.offlinePrice) {
totalOfflinePrice += Number(product.offlinePrice);
}
}
formData.value.onlinePrice = totalOnlinePrice.toFixed(2)
formData.value.offlinePrice = (totalOfflinePrice + totalOnlinePrice).toFixed(2)
let all = totalOnlinePrice + totalOfflinePrice + totalOnlinePrice
formData.value.totalPrice = all.toFixed(2)
},
{ deep: true }
);
const setSuccess = (val) => {
formData.value.products = val
}
//获取营收分析
const RevenueAnalysisFormRef = ref('')
const newProducts = ref([])
const handleClick = (val) => {
newProducts.value = []
console.log('%csrc/views/crm/quotation/QuotationForm.vue:540 val', 'color: #007acc;', subTabsName.value);
formData.value.products.forEach( item => {
newProducts.value.push({
id: item.productId,
onlinePrice: item.onlinePrice,
offlinePrice: item.offlinePrice,
productUnit: item.productUnit,
productName: item.productName,
category: item.category
})
})
let data = {
businessId: formData.value.businessId,
creditMethod: formData.value.creditMethod,
paymentTerm: formData.value.paymentTerm,
quotationProducts: newProducts.value
}
console.log('%csrc/views/crm/quotation/QuotationForm.vue:551 newProducts.value', 'color: #007acc;', newProducts.value);
RevenueAnalysisFormRef.value.getData(data)
}
const onBusinessChange = async (businessId: string) => {
if (!businessId) return
@ -512,7 +581,6 @@ const onBusinessChange = async (businessId: string) => {
formData.value.onlinePrice = res.onlinePrice;
formData.value.totalPrice = res.totalPrice;
formData.value.creditAmount = res.creditAmount;
console.log('%csrc/views/crm/quotation/QuotationForm.vue:516 (formData.value.creditAmount || 0) + (formData.value.creditLimitNum || 0)', 'color: #007acc;', (formData.value.creditAmount || 0) , (formData.value.creditLimitNum || 0));
// 🔁 自动加载客户详情
await onCustomerChange(res.customerId);
@ -562,6 +630,7 @@ const onPartnerChange = async (id: string) => {
formLoading.value = true;
const res = await ContractApi.getOrgInfo(id);
formData.value.bankName = res.bankName;
formData.value.creditCode = res.creditCode;
formData.value.bankAccount = res.bankAccount;
formData.value.legalRepresentative = res.legalRepresentative;
formData.value.businessLicenseNumber = res.businessLicenseNumber;
@ -677,6 +746,9 @@ onMounted(async () => {
if (formType.value) open(formType.value)
formData.value.pricingUserId = useUserStore().getUser.id;
// 获得客户列表
customerList.value = await CustomerApi.getSelfCustomerSimpleList()
// 获得用户列表

View File

@ -41,6 +41,11 @@
<template #default="{ row }">
{{row.offlinePrice}}
</template>
</el-table-column>
<el-table-column label="线下价格(元)" align="center" min-width="130">
<template #default="{ row }">
{{row.downlinePrice}}
</template>
</el-table-column>
<el-table-column label="总计价格" align="center" min-width="150">
<template #default="{ row }">
@ -111,8 +116,16 @@ watch(
return;
}
nextTick(() => {
// 循环处理
val.forEach((item) => {
if (item.offlinePrice != null && item.onlinePrice != null) {
item.downlinePrice = Number((Number(item.offlinePrice) + Number(item.onlinePrice)).toFixed(2)) || 0;
item.totalPrice = Number((Number(item.downlinePrice) + Number(item.onlinePrice)).toFixed(2)) || 0;
} else {
item.totalPrice = 0;
}
});
formData.value = val
console.log('%csrc/views/crm/quotation/components/QuotationProductDetail.vue:134 val', 'color: #007acc;', val);
})
},
{ immediate: true }

View File

@ -46,21 +46,29 @@
</el-form-item>
</template>
</el-table-column>
<el-table-column label="线上价格(元)" min-width="120">
<el-table-column label="线上价格(元)" min-width="130">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.onlinePrice`" :rules="formRules.onlinePrice" class="mb-0px!">
<el-input v-model="row.onlinePrice" placeholder="请输入线上价格" />
<el-input v-model="row.onlinePrice" oninput="value=value.match(/^\d+(?:\.\d{0,2})?/)" placeholder="" />
</el-form-item>
</template>
</el-table-column>
<el-table-column label="线下手续费(元)" min-width="120">
<el-table-column label="线下手续费(元)" min-width="140">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.offlinePrice`" :rules="formRules.offlinePrice" class="mb-0px!">
<el-input v-model="row.offlinePrice" placeholder="请输入线下价格" />
<el-input v-model="row.offlinePrice" oninput="value=value.match(/^\d+(?:\.\d{0,2})?/)" placeholder="" />
</el-form-item>
</template>
</el-table-column>
<el-table-column label="总计价格(元)" min-width="120">
<el-table-column label="线下价格(元)" min-width="130">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.downlinePrice`" :rules="formRules.onlinePrice" class="mb-0px!">
{{row.downlinePrice}}
</el-form-item>
</template>
</el-table-column>
<el-table-column label="总计价格(元)" min-width="130">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.totalPrice`" :rules="formRules.totalPrice" class="mb-0px!">
{{row.totalPrice}}
@ -166,7 +174,7 @@ const formRules = reactive({
})
const formRef = ref() // 表单 Ref
const emit = defineEmits(['update:handleType'])
const emit = defineEmits(['update:handleType', 'success'])
/** 监听主表的关联字段的变化,加载对应的子表数据 */
watch(
() => props.businessId,
@ -185,7 +193,12 @@ watch(
// formData.value = await QuotationApi.getQuotationProductListByQuotationId(val.quotationId)
// } else if(val.businessId) {
const business = await BusinessApi.getBusiness(val);
formData.value = business.products
let data = business.products
data.forEach(item => {
item.onlinePrice = ''
item.offlinePrice = ''
})
formData.value = data
emit('update:handleType', 0)
// }
} finally {
@ -251,11 +264,13 @@ watch(
// 循环处理
val.forEach((item) => {
if (item.offlinePrice != null && item.onlinePrice != null) {
item.totalPrice = Number((Number(item.offlinePrice) + Number(item.onlinePrice)).toFixed(2));
item.downlinePrice = Number((Number(item.offlinePrice) + Number(item.onlinePrice)).toFixed(2)) || 0;
item.totalPrice = Number((Number(item.downlinePrice) + Number(item.onlinePrice)).toFixed(2)) || 0;
} else {
item.totalPrice = 0;
}
});
emit('success', formData.value)
},
{ deep: true }
);

View File

@ -0,0 +1,87 @@
<template>
<el-table :data="formData" class="-mt-10px">
<el-table-column label="产品类型" align="center" min-width="150">
<template #default="{ row }">
<dict-tag :type="DICT_TYPE.CRM_PRODUCT_CATEGORY" :value="row.category" />
</template>
</el-table-column>
<el-table-column label="产品明细类型" align="center" min-width="190">
<template #default="{ row }">
<dict-tag :type="DICT_TYPE.CRM_PRODUCT_DETAIL_TYPE" :value="row.detailType" />
</template>
</el-table-column>
<el-table-column label="销售额" align="center" min-width="150">
<template #default="{ row }">
{{row.revenueAnalysisVO.salesVolume}}
</template>
</el-table-column>
<el-table-column label="订单量" align="center" min-width="150">
<template #default="{ row }">
{{row.revenueAnalysisVO.totalOrderVolume}}
</template>
</el-table-column>
<el-table-column label="营业收入" align="center" min-width="150">
<template #default="{ row }">
{{row.revenueAnalysisVO.totalIncome}}
</template>
</el-table-column>
<el-table-column label="收入利率" align="center" min-width="130">
<template #default="{ row }">
{{(row.revenueAnalysisVO.incomeInterestRate * 100).toFixed(2) + '%'}}
</template>
</el-table-column>
<el-table-column label="应扣成本" align="center" min-width="150">
<template #default="{ row }">
{{row.revenueAnalysisVO.deductibleCost}}
</template>
</el-table-column>
<el-table-column label="利润" align="center" min-width="150">
<template #default="{ row }">
{{row.revenueAnalysisVO.profit}}
</template>
</el-table-column>
<el-table-column label="利润率" align="center" min-width="250">
<template #default="{ row }">
{{(row.revenueAnalysisVO.profitRate * 100).toFixed(2) + '%'}}
</template>
</el-table-column>
<el-table-column label="您的分润比例" align="center" min-width="150">
<template #default="{ row }">
<dict-tag :type="DICT_TYPE.CRM_SERVICE_FEE_INVOICE" :value="row.serviceInvoice" />
</template>
</el-table-column>
<el-table-column label="您的分润收入" align="center" min-width="250">
<template #default="{ row }">
<dict-tag :type="DICT_TYPE.CRM_SERVICE_FEE_INVOICE_ITEMS" :value="row.serviceInvoiceItem" />
</template>
</el-table-column>
</el-table>
</template>
<script setup lang="ts">
import { QuotationApi, QuotationVO } from '@/api/crm/quotation'
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
import * as BusinessApi from '@/api/crm/business'
const props = defineProps<{
quotationId: undefined, // 报价编号(主表的关联字段)
products: undefined
}>()
const formLoading = ref(false) // 表单的加载中
const formData = ref([])
/** 表单值 */
const getData = async (data) => {
let list = await QuotationApi.getRevenueList(data)
console.log('%csrc/views/crm/quotation/components/RevenueAnalysis.vue:79 list.totalRevenueAnalysis', 'color: #007acc;', list.totalRevenueAnalysis);
formData.value = list.quotationProducts
}
const getName = (opt, val) => {
const arr = opt.filter(v => v.value == val)
return arr.length ? arr[0]['label'] : ''
}
defineExpose({ getData })
</script>

View File

@ -6,7 +6,7 @@
:model="queryParams"
ref="queryFormRef"
:inline="true"
label-width="90px"
label-width="110px"
>
<el-form-item label="报价单编号" prop="no">
<el-input
@ -47,6 +47,21 @@
/>
</el-select>
</el-form-item>
<el-form-item label="客户确认状态" prop="confirmStatus">
<el-select
v-model="queryParams.confirmStatus"
placeholder="请选择客户确认状态"
clearable
class="!w-240px"
>
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_CONFIRM_STATUS)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item label="创建时间" prop="createTime">
<el-date-picker
v-model="queryParams.createTime"
@ -84,11 +99,11 @@
<!-- 列表 -->
<ContentWrap>
<el-tabs v-model="activeName" @tab-click="handleTabClick">
<!-- <el-tabs v-model="activeName" @tab-click="handleTabClick">
<el-tab-pane label="我负责的" name="1" />
<el-tab-pane label="我参与的" name="2" />
<el-tab-pane label="下属负责的" name="3" />
</el-tabs>
</el-tabs> -->
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
<el-table-column label="报价单编号" align="center" prop="no" width="200" >
<template #default="scope">
@ -131,11 +146,11 @@
<dict-tag :type="'credit_method'" :value="scope.row.creditMethod" />
</template>
</el-table-column>
<el-table-column label="授信计算周期" align="center" prop="creditCalcCycle" width="120">
<template #default="scope">
<el-table-column label="授信计算周期" align="center" prop="creditCalcCycle" width="120" />
<!-- <template #default="scope">
<dict-tag :type="DICT_TYPE.CREDIT_CALC_CYCLE" :value="scope.row.creditCalcCycle" />
</template>
</el-table-column>
</el-table-column> -->
<el-table-column label="授信额度" align="center" prop="creditLimit" />
<el-table-column label="合作主体" align="center" prop="partnerCompanyName" />
<el-table-column label="审批状态" align="center" prop="auditStatus" width="100">
@ -143,6 +158,11 @@
<dict-tag :type="DICT_TYPE.CRM_AUDIT_STATUS" :value="scope.row.auditStatus" />
</template>
</el-table-column>
<el-table-column label="客户确认状态" align="center" prop="confirmStatus" width="110">
<template #default="scope">
<dict-tag :type="DICT_TYPE.CRM_CONFIRM_STATUS" :value="scope.row.confirmStatus" />
</template>
</el-table-column>
<el-table-column
label="创建时间"
align="center"
@ -150,7 +170,7 @@
:formatter="dateFormatter"
width="180px"
/>
<el-table-column label="操作" align="center" fixed="right" min-width="160px">
<el-table-column label="操作" align="center" fixed="right" min-width="180px">
<template #default="scope">
<div style="display:flex">
<el-button
@ -165,10 +185,30 @@
type="primary"
@click="openFormEdit(scope.row)"
v-hasPermi="['crm:quotation:update']"
v-if="scope.row.auditStatus == 0"
>
编辑
</el-button>
<el-button
link
type="primary"
@click="handleProcessDetail(scope.row)"
v-hasPermi="['crm:customer-suggestion:query']"
v-if="scope.row.auditStatus != 0"
>
进度
</el-button>
<el-button
link
type="primary"
@click="handleEnter(scope.row)"
v-hasPermi="['crm:quotation:customerConfirm']"
v-if="scope.row.auditStatus == 2 && scope.row.confirmStatus == 0"
>
客户确认
</el-button>
<el-dropdown
v-if="scope.row.auditStatus == 0"
@command="(command) => handleCommand(command, scope.row)"
v-hasPermi="[
'crm:quotation:delete',
@ -176,24 +216,30 @@
'system:permission:assign-user-role'
]"
>
<el-button type="primary" link><Icon icon="ep:d-arrow-right" /> 更多</el-button>
<el-button type="primary" link>更多<Icon icon="ep:d-arrow-right" /> </el-button>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item
command="handleSubmit"
v-if="checkPermi(['crm:contract:update']) && scope.row.auditStatus === 0"
>
提交审核
</el-dropdown-item>
command="handleSubmit"
v-if="checkPermi(['crm:contract:update']) && scope.row.auditStatus === 0"
>
提交审核
</el-dropdown-item>
<el-dropdown-item
command="handleEnter"
v-if="checkPermi(['crm:quotation:customerConfirm']) && scope.row.auditStatus == 2 && scope.row.confirmStatus == 0"
>
客户确认
</el-dropdown-item>
<el-dropdown-item
command="handleProcessDetail"
v-if="checkPermi(['crm:customer-suggestion:query'])"
v-if="checkPermi(['crm:customer-suggestion:query']) && scope.row.auditStatus != 0"
>
进度
</el-dropdown-item>
<el-dropdown-item
command="handleDelete"
v-if="checkPermi(['crm:quotation:delete'])"
v-if="checkPermi(['crm:quotation:delete']) && scope.row.auditStatus === 0"
>
删除
</el-dropdown-item>
@ -248,6 +294,7 @@ const queryParams = reactive({
customerId: undefined,
ownerUserId: undefined,
partnerCompanyId: undefined,
confirmStatus: undefined,
createTime: []
})
const queryFormRef = ref() // 搜索的表单
@ -314,6 +361,9 @@ const handleCommand = (command: string, row: UserApi.UserVO) => {
case 'handleProcessDetail':
handleProcessDetail(row)
break
case 'handleEnter':
handleEnter(row)
break
default:
break
}
@ -350,6 +400,13 @@ const handleProcessDetail = (row) => {
}
})
}
const handleEnter = async (row) => {
await QuotationApi.enterQuotation({id: row.id})
message.success('操作成功!')
getList()
}
const getName = (opt, val) => {
const arr = opt.filter(v => v.id == val)
return arr.length ? arr[0]['nickname'] : ''

View File

@ -0,0 +1,143 @@
<template>
<Dialog :title="dialogTitle" v-model="dialogVisible">
<el-form
ref="formRef"
:model="formData"
:rules="formRules"
label-width="100px"
v-loading="formLoading"
>
<el-form-item label="类型" prop="type">
<el-select v-model="formData.type" placeholder="请选择类型">
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_SERVICE_FEE_COLLECTION_METHOD_TYPE)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item label="产品明细" prop="productDetail">
<el-input v-model="formData.productDetail" placeholder="请输入产品明细" />
</el-form-item>
<el-form-item label="客单价" prop="customerPrice">
<el-input v-model="formData.customerPrice" placeholder="请输入客单价" />
</el-form-item>
<el-form-item label="单位" prop="unit">
<el-select v-model="formData.unit" placeholder="请选择单位">
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_PRODUCT_UNIT)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item label="采购成本" prop="procurementCost">
<el-input v-model="formData.procurementCost" placeholder="请输入采购成本" />
</el-form-item>
<el-form-item label="客单价 - 采购成本" prop="profit">
<el-input v-model="formData.profit" placeholder="请输入客单价 - 采购成本" />
</el-form-item>
<el-form-item label="说明" prop="describe">
<el-input v-model="formData.describe" placeholder="请输入说明" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
<el-button @click="dialogVisible = false">取 消</el-button>
</template>
</Dialog>
</template>
<script setup lang="ts">
import { getIntDictOptions, DICT_TYPE } from '@/utils/dict'
import { ServiceFeeCollectionMethodApi, ServiceFeeCollectionMethodVO } from '@/api/crm/servicefeecollectionmethod'
/** 服务费收取方式 表单 */
defineOptions({ name: 'ServiceFeeCollectionMethodForm' })
const { t } = useI18n() // 国际化
const message = useMessage() // 消息弹窗
const dialogVisible = ref(false) // 弹窗的是否展示
const dialogTitle = ref('') // 弹窗的标题
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
const formType = ref('') // 表单的类型:create - 新增;update - 修改
const formData = ref({
id: undefined,
type: undefined,
productDetail: undefined,
customerPrice: undefined,
unit: undefined,
procurementCost: undefined,
profit: undefined,
describe: undefined,
})
const formRules = reactive({
type: [{ required: true, message: '类型不能为空', trigger: 'change' }],
productDetail: [{ required: true, message: '产品明细不能为空', trigger: 'blur' }],
customerPrice: [{ required: true, message: '客单价不能为空', trigger: 'blur' }],
unit: [{ required: true, message: '单位不能为空', trigger: 'change' }],
procurementCost: [{ required: true, message: '采购成本不能为空', trigger: 'blur' }],
profit: [{ required: true, message: '客单价 - 采购成本不能为空', trigger: 'blur' }],
describe: [{ required: true, message: '说明不能为空', trigger: 'blur' }],
})
const formRef = ref() // 表单 Ref
/** 打开弹窗 */
const open = async (type: string, id?: number) => {
dialogVisible.value = true
dialogTitle.value = t('action.' + type)
formType.value = type
resetForm()
// 修改时,设置数据
if (id) {
formLoading.value = true
try {
formData.value = await ServiceFeeCollectionMethodApi.getServiceFeeCollectionMethod(id)
} finally {
formLoading.value = false
}
}
}
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
/** 提交表单 */
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
const submitForm = async () => {
// 校验表单
await formRef.value.validate()
// 提交请求
formLoading.value = true
try {
const data = formData.value as unknown as ServiceFeeCollectionMethodVO
if (formType.value === 'create') {
await ServiceFeeCollectionMethodApi.createServiceFeeCollectionMethod(data)
message.success(t('common.createSuccess'))
} else {
await ServiceFeeCollectionMethodApi.updateServiceFeeCollectionMethod(data)
message.success(t('common.updateSuccess'))
}
dialogVisible.value = false
// 发送操作成功的事件
emit('success')
} finally {
formLoading.value = false
}
}
/** 重置表单 */
const resetForm = () => {
formData.value = {
id: undefined,
type: undefined,
productDetail: undefined,
customerPrice: undefined,
unit: undefined,
procurementCost: undefined,
profit: undefined,
describe: undefined,
}
formRef.value?.resetFields()
}
</script>

View File

@ -0,0 +1,274 @@
<template>
<ContentWrap>
<!-- 搜索工作栏 -->
<el-form
class="-mb-15px"
:model="queryParams"
ref="queryFormRef"
:inline="true"
label-width="68px"
>
<el-form-item label="类型" prop="type">
<el-select
v-model="queryParams.type"
placeholder="请选择类型"
clearable
class="!w-240px"
>
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_SERVICE_FEE_COLLECTION_METHOD_TYPE)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item label="产品明细" prop="productDetail">
<el-input
v-model="queryParams.productDetail"
placeholder="请输入产品明细"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item>
<el-form-item label="客单价" prop="customerPrice">
<el-input
v-model="queryParams.customerPrice"
placeholder="请输入客单价"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item>
<el-form-item label="单位" prop="unit">
<el-select
v-model="queryParams.unit"
placeholder="请选择单位"
clearable
class="!w-240px"
>
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_PRODUCT_UNIT)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item label="采购成本" prop="procurementCost">
<el-input
v-model="queryParams.procurementCost"
placeholder="请输入采购成本"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item>
<el-form-item label="客单价 - 采购成本" prop="profit">
<el-input
v-model="queryParams.profit"
placeholder="请输入客单价 - 采购成本"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item>
<el-form-item label="说明" prop="describe">
<el-input
v-model="queryParams.describe"
placeholder="请输入说明"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item>
<el-form-item label="创建时间" prop="createTime">
<el-date-picker
v-model="queryParams.createTime"
value-format="YYYY-MM-DD HH:mm:ss"
type="daterange"
start-placeholder="开始日期"
end-placeholder="结束日期"
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
class="!w-220px"
/>
</el-form-item>
<el-form-item>
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
<el-button
type="primary"
plain
@click="openForm('create')"
v-hasPermi="['crm:service-fee-collection-method:create']"
>
<Icon icon="ep:plus" class="mr-5px" /> 新增
</el-button>
<el-button
type="success"
plain
@click="handleExport"
:loading="exportLoading"
v-hasPermi="['crm:service-fee-collection-method:export']"
>
<Icon icon="ep:download" class="mr-5px" /> 导出
</el-button>
</el-form-item>
</el-form>
</ContentWrap>
<!-- 列表 -->
<ContentWrap>
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
<el-table-column label="主键" align="center" prop="id" />
<el-table-column label="类型" align="center" prop="type">
<template #default="scope">
<dict-tag :type="DICT_TYPE.CRM_SERVICE_FEE_COLLECTION_METHOD_TYPE" :value="scope.row.type" />
</template>
</el-table-column>
<el-table-column label="产品明细" align="center" prop="productDetail" />
<el-table-column label="客单价" align="center" prop="customerPrice" />
<el-table-column label="单位" align="center" prop="unit">
<template #default="scope">
<dict-tag :type="DICT_TYPE.CRM_PRODUCT_UNIT" :value="scope.row.unit" />
</template>
</el-table-column>
<el-table-column label="采购成本" align="center" prop="procurementCost" />
<el-table-column label="客单价 - 采购成本" align="center" prop="profit" />
<el-table-column label="说明" align="center" prop="describe" />
<el-table-column
label="创建时间"
align="center"
prop="createTime"
:formatter="dateFormatter"
width="180px"
/>
<el-table-column label="操作" align="center" min-width="120px">
<template #default="scope">
<el-button
link
type="primary"
@click="openForm('update', scope.row.id)"
v-hasPermi="['crm:service-fee-collection-method:update']"
>
编辑
</el-button>
<el-button
link
type="danger"
@click="handleDelete(scope.row.id)"
v-hasPermi="['crm:service-fee-collection-method:delete']"
>
删除
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<Pagination
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
</ContentWrap>
<!-- 表单弹窗:添加/修改 -->
<ServiceFeeCollectionMethodForm ref="formRef" @success="getList" />
</template>
<script setup lang="ts">
import { getIntDictOptions, DICT_TYPE } from '@/utils/dict'
import { dateFormatter } from '@/utils/formatTime'
import download from '@/utils/download'
import { ServiceFeeCollectionMethodApi, ServiceFeeCollectionMethodVO } from '@/api/crm/servicefeecollectionmethod'
import ServiceFeeCollectionMethodForm from './ServiceFeeCollectionMethodForm.vue'
/** 服务费收取方式 列表 */
defineOptions({ name: 'ServiceFeeCollectionMethod' })
const message = useMessage() // 消息弹窗
const { t } = useI18n() // 国际化
const loading = ref(true) // 列表的加载中
const list = ref<ServiceFeeCollectionMethodVO[]>([]) // 列表的数据
const total = ref(0) // 列表的总页数
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
type: undefined,
productDetail: undefined,
customerPrice: undefined,
unit: undefined,
procurementCost: undefined,
profit: undefined,
describe: undefined,
createTime: [],
})
const queryFormRef = ref() // 搜索的表单
const exportLoading = ref(false) // 导出的加载中
/** 查询列表 */
const getList = async () => {
loading.value = true
try {
const data = await ServiceFeeCollectionMethodApi.getServiceFeeCollectionMethodPage(queryParams)
list.value = data.list
total.value = data.total
} finally {
loading.value = false
}
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.pageNo = 1
getList()
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value.resetFields()
handleQuery()
}
/** 添加/修改操作 */
const formRef = ref()
const openForm = (type: string, id?: number) => {
formRef.value.open(type, id)
}
/** 删除按钮操作 */
const handleDelete = async (id: number) => {
try {
// 删除的二次确认
await message.delConfirm()
// 发起删除
await ServiceFeeCollectionMethodApi.deleteServiceFeeCollectionMethod(id)
message.success(t('common.delSuccess'))
// 刷新列表
await getList()
} catch {}
}
/** 导出按钮操作 */
const handleExport = async () => {
try {
// 导出的二次确认
await message.exportConfirm()
// 发起导出
exportLoading.value = true
const data = await ServiceFeeCollectionMethodApi.exportServiceFeeCollectionMethod(queryParams)
download.excel(data, '服务费收取方式.xls')
} catch {
} finally {
exportLoading.value = false
}
}
/** 初始化 **/
onMounted(() => {
getList()
})
</script>

View File

@ -0,0 +1,227 @@
<template>
<Dialog :title="dialogTitle" v-model="dialogVisible">
<el-form
ref="formRef"
:model="formData"
:rules="formRules"
label-width="180px"
v-loading="formLoading"
>
<el-form-item label="平台名称" prop="name">
<el-input v-model="formData.name" placeholder="请输入平台名称" />
</el-form-item>
<el-form-item label="对接模式" prop="dockingMode">
<el-select v-model="formData.dockingMode" placeholder="请选择对接模式">
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.DOCKING_MODE)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item label="技术对接人力(人天)" prop="dockingHours">
<el-input v-model="formData.dockingHours" placeholder="请输入技术对接人力(人天)" />
</el-form-item>
<el-form-item label="单点登录" prop="singleSignOn">
<el-radio-group v-model="formData.singleSignOn">
<el-radio
v-for="dict in getIntDictOptions(DICT_TYPE.SYNCHRONIZATION_STATUS)"
:key="dict.value"
:label="dict.value"
>
{{ dict.label }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="架构同步" prop="architectureSynchronization">
<el-radio-group v-model="formData.architectureSynchronization">
<el-radio
v-for="dict in getIntDictOptions(DICT_TYPE.SYNCHRONIZATION_STATUS)"
:key="dict.value"
:label="dict.value"
>
{{ dict.label }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="出差单同步" prop="businessTripSynchronization">
<el-radio-group v-model="formData.businessTripSynchronization">
<el-radio
v-for="dict in getIntDictOptions(DICT_TYPE.SYNCHRONIZATION_STATUS)"
:key="dict.value"
:label="dict.value"
>
{{ dict.label }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="订单同步" prop="orderSynchronization">
<el-radio-group v-model="formData.orderSynchronization">
<el-radio
v-for="dict in getIntDictOptions(DICT_TYPE.SYNCHRONIZATION_STATUS)"
:key="dict.value"
:label="dict.value"
>
{{ dict.label }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="账单同步" prop="billSynchronization">
<el-radio-group v-model="formData.billSynchronization">
<el-radio
v-for="dict in getIntDictOptions(DICT_TYPE.SYNCHRONIZATION_STATUS)"
:key="dict.value"
:label="dict.value"
>
{{ dict.label }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="审批/消息同步" prop="approvalMessageSynchronization">
<el-radio-group v-model="formData.approvalMessageSynchronization">
<el-radio
v-for="dict in getIntDictOptions(DICT_TYPE.SYNCHRONIZATION_STATUS)"
:key="dict.value"
:label="dict.value"
>
{{ dict.label }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="对接产品类型" prop="dockingProductSynchronization">
<el-select v-model="formData.dockingProductSynchronization" placeholder="请选择对接产品类型">
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.MATCHING_PRODUCT_TYPES)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item label="进度" prop="progress">
<el-select v-model="formData.progress" placeholder="请选择进度">
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_PROGRESS)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item label="备注" prop="notes">
<el-input v-model="formData.notes" type="textarea" placeholder="请输入备注" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
<el-button @click="dialogVisible = false">取 消</el-button>
</template>
</Dialog>
</template>
<script setup lang="ts">
import { getIntDictOptions, DICT_TYPE } from '@/utils/dict'
import { TechnicalDockingChecklistApi, TechnicalDockingChecklistVO } from '@/api/crm/technicaldockingchecklist'
/** 技术对接清单 表单 */
defineOptions({ name: 'TechnicalDockingChecklistForm' })
const { t } = useI18n() // 国际化
const message = useMessage() // 消息弹窗
const dialogVisible = ref(false) // 弹窗的是否展示
const dialogTitle = ref('') // 弹窗的标题
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
const formType = ref('') // 表单的类型:create - 新增;update - 修改
const formData = ref({
id: undefined,
name: undefined,
dockingMode: undefined,
dockingHours: undefined,
singleSignOn: undefined,
architectureSynchronization: undefined,
businessTripSynchronization: undefined,
orderSynchronization: undefined,
billSynchronization: undefined,
approvalMessageSynchronization: undefined,
dockingProductSynchronization: undefined,
progress: undefined,
notes: undefined,
})
const formRules = reactive({
name: [{ required: true, message: '平台名称不能为空', trigger: 'blur' }],
dockingMode: [{ required: true, message: '对接模式不能为空', trigger: 'change' }],
dockingHours: [{ required: true, message: '技术对接人力(小时)不能为空', trigger: 'blur' }],
singleSignOn: [{ required: true, message: '单点登录不能为空', trigger: 'blur' }],
architectureSynchronization: [{ required: true, message: '架构同步不能为空', trigger: 'blur' }],
businessTripSynchronization: [{ required: true, message: '出差单同步不能为空', trigger: 'blur' }],
orderSynchronization: [{ required: true, message: '订单同步不能为空', trigger: 'blur' }],
billSynchronization: [{ required: true, message: '账单同步不能为空', trigger: 'blur' }],
approvalMessageSynchronization: [{ required: true, message: '审批/消息同步不能为空', trigger: 'blur' }],
dockingProductSynchronization: [{ required: true, message: '对接产品类型不能为空', trigger: 'change' }],
progress: [{ required: true, message: '进度不能为空', trigger: 'change' }],
})
const formRef = ref() // 表单 Ref
/** 打开弹窗 */
const open = async (type: string, id?: number) => {
dialogVisible.value = true
dialogTitle.value = t('action.' + type)
formType.value = type
resetForm()
// 修改时,设置数据
if (id) {
formLoading.value = true
try {
formData.value = await TechnicalDockingChecklistApi.getTechnicalDockingChecklist(id)
} finally {
formLoading.value = false
}
}
}
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
/** 提交表单 */
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
const submitForm = async () => {
// 校验表单
await formRef.value.validate()
// 提交请求
formLoading.value = true
try {
const data = formData.value as unknown as TechnicalDockingChecklistVO
if (formType.value === 'create') {
await TechnicalDockingChecklistApi.createTechnicalDockingChecklist(data)
message.success(t('common.createSuccess'))
} else {
await TechnicalDockingChecklistApi.updateTechnicalDockingChecklist(data)
message.success(t('common.updateSuccess'))
}
dialogVisible.value = false
// 发送操作成功的事件
emit('success')
} finally {
formLoading.value = false
}
}
/** 重置表单 */
const resetForm = () => {
formData.value = {
id: undefined,
name: undefined,
dockingMode: undefined,
dockingHours: undefined,
singleSignOn: undefined,
architectureSynchronization: undefined,
businessTripSynchronization: undefined,
orderSynchronization: undefined,
billSynchronization: undefined,
approvalMessageSynchronization: undefined,
dockingProductSynchronization: undefined,
progress: undefined,
notes: undefined,
}
formRef.value?.resetFields()
}
</script>

View File

@ -0,0 +1,251 @@
<template>
<ContentWrap>
<!-- 搜索工作栏 -->
<el-form
class="-mb-15px"
:model="queryParams"
ref="queryFormRef"
:inline="true"
label-width="68px"
>
<el-form-item label="平台名称" prop="name">
<el-input
v-model="queryParams.name"
placeholder="请输入平台名称"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item>
<el-form-item label="对接模式" prop="dockingMode">
<el-select
v-model="queryParams.dockingMode"
placeholder="请选择对接模式"
clearable
class="!w-240px"
>
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.DOCKING_MODE)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item label="创建时间" prop="createTime">
<el-date-picker
v-model="queryParams.createTime"
value-format="YYYY-MM-DD HH:mm:ss"
type="daterange"
start-placeholder="开始日期"
end-placeholder="结束日期"
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
class="!w-220px"
/>
</el-form-item>
<el-form-item>
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
<el-button
type="primary"
plain
@click="openForm('create')"
v-hasPermi="['crm:technical-docking-checklist:create']"
>
<Icon icon="ep:plus" class="mr-5px" /> 新增
</el-button>
<el-button
type="success"
plain
@click="handleExport"
:loading="exportLoading"
v-hasPermi="['crm:technical-docking-checklist:export']"
>
<Icon icon="ep:download" class="mr-5px" /> 导出
</el-button>
</el-form-item>
</el-form>
</ContentWrap>
<!-- 列表 -->
<ContentWrap>
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
<el-table-column label="主键ID" align="center" prop="id" />
<el-table-column label="平台名称" align="center" prop="name" />
<el-table-column label="对接模式" align="center" prop="dockingMode">
<template #default="scope">
<dict-tag :type="DICT_TYPE.DOCKING_MODE" :value="scope.row.dockingMode" />
</template>
</el-table-column>
<el-table-column label="技术对接人力(人天)" align="center" prop="dockingHours" />
<el-table-column label="单点登录" align="center" prop="singleSignOn">
<template #default="scope">
<dict-tag :type="DICT_TYPE.SYNCHRONIZATION_STATUS" :value="scope.row.singleSignOn" />
</template>
</el-table-column>
<el-table-column label="架构同步" align="center" prop="architectureSynchronization">
<template #default="scope">
<dict-tag :type="DICT_TYPE.SYNCHRONIZATION_STATUS" :value="scope.row.architectureSynchronization" />
</template>
</el-table-column>
<el-table-column label="出差单同步" align="center" prop="businessTripSynchronization">
<template #default="scope">
<dict-tag :type="DICT_TYPE.SYNCHRONIZATION_STATUS" :value="scope.row.businessTripSynchronization" />
</template>
</el-table-column>
<el-table-column label="订单同步" align="center" prop="orderSynchronization">
<template #default="scope">
<dict-tag :type="DICT_TYPE.SYNCHRONIZATION_STATUS" :value="scope.row.orderSynchronization" />
</template>
</el-table-column>
<el-table-column label="账单同步" align="center" prop="billSynchronization">
<template #default="scope">
<dict-tag :type="DICT_TYPE.SYNCHRONIZATION_STATUS" :value="scope.row.billSynchronization" />
</template>
</el-table-column>
<el-table-column label="审批/消息同步" align="center" prop="approvalMessageSynchronization">
<template #default="scope">
<dict-tag :type="DICT_TYPE.SYNCHRONIZATION_STATUS" :value="scope.row.approvalMessageSynchronization" />
</template>
</el-table-column>
<el-table-column label="对接产品类型" align="center" prop="dockingProductSynchronization">
<template #default="scope">
<dict-tag :type="DICT_TYPE.MATCHING_PRODUCT_TYPES" :value="scope.row.dockingProductSynchronization" />
</template>
</el-table-column>
<el-table-column label="进度" align="center" prop="progress">
<template #default="scope">
<dict-tag :type="DICT_TYPE.CRM_PROGRESS" :value="scope.row.progress" />
</template>
</el-table-column>
<el-table-column label="备注" align="center" prop="notes" />
<el-table-column
label="创建时间"
align="center"
prop="createTime"
:formatter="dateFormatter"
width="180px"
/>
<el-table-column label="操作" align="center" min-width="120px">
<template #default="scope">
<el-button
link
type="primary"
@click="openForm('update', scope.row.id)"
v-hasPermi="['crm:technical-docking-checklist:update']"
>
编辑
</el-button>
<el-button
link
type="danger"
@click="handleDelete(scope.row.id)"
v-hasPermi="['crm:technical-docking-checklist:delete']"
>
删除
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<Pagination
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
</ContentWrap>
<!-- 表单弹窗:添加/修改 -->
<TechnicalDockingChecklistForm ref="formRef" @success="getList" />
</template>
<script setup lang="ts">
import { getIntDictOptions, DICT_TYPE } from '@/utils/dict'
import { dateFormatter } from '@/utils/formatTime'
import download from '@/utils/download'
import { TechnicalDockingChecklistApi, TechnicalDockingChecklistVO } from '@/api/crm/technicaldockingchecklist'
import TechnicalDockingChecklistForm from './TechnicalDockingChecklistForm.vue'
/** 技术对接清单 列表 */
defineOptions({ name: 'TechnicalDockingChecklist' })
const message = useMessage() // 消息弹窗
const { t } = useI18n() // 国际化
const loading = ref(true) // 列表的加载中
const list = ref<TechnicalDockingChecklistVO[]>([]) // 列表的数据
const total = ref(0) // 列表的总页数
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
name: undefined,
dockingMode: undefined,
createTime: [],
})
const queryFormRef = ref() // 搜索的表单
const exportLoading = ref(false) // 导出的加载中
/** 查询列表 */
const getList = async () => {
loading.value = true
try {
const data = await TechnicalDockingChecklistApi.getTechnicalDockingChecklistPage(queryParams)
list.value = data.list
total.value = data.total
} finally {
loading.value = false
}
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.pageNo = 1
getList()
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value.resetFields()
handleQuery()
}
/** 添加/修改操作 */
const formRef = ref()
const openForm = (type: string, id?: number) => {
formRef.value.open(type, id)
}
/** 删除按钮操作 */
const handleDelete = async (id: number) => {
try {
// 删除的二次确认
await message.delConfirm()
// 发起删除
await TechnicalDockingChecklistApi.deleteTechnicalDockingChecklist(id)
message.success(t('common.delSuccess'))
// 刷新列表
await getList()
} catch {}
}
/** 导出按钮操作 */
const handleExport = async () => {
try {
// 导出的二次确认
await message.exportConfirm()
// 发起导出
exportLoading.value = true
const data = await TechnicalDockingChecklistApi.exportTechnicalDockingChecklist(queryParams)
download.excel(data, '技术对接清单.xls')
} catch {
} finally {
exportLoading.value = false
}
}
/** 初始化 **/
onMounted(() => {
getList()
})
</script>

View File

@ -0,0 +1,176 @@
<template>
<ContentWrap>
<el-form
ref="formRef"
:model="formData"
:rules="formRules"
label-width="120px"
v-loading="formLoading"
>
<el-row>
<el-col :span="8">
<el-form-item label="简报开始日期" prop="briefStartDate">
<el-date-picker
v-model="formData.briefStartDate"
type="date"
disabled
value-format="YYYY-MM-DD"
placeholder="选择简报开始日期"
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="简报结束日期" prop="briefEndDate">
<el-date-picker
disabled
v-model="formData.briefEndDate"
type="date"
value-format="YYYY-MM-DD"
placeholder="选择简报结束日期"
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="是否选择客户" prop="selectCustomer">
<el-radio-group v-model="formData.selectCustomer" disabled>
<el-radio :label="true">是</el-radio>
<el-radio :label="false">否</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="24">
<ContentWrap style="border: none;font-weight: 700" v-if="formData.selectCustomer">
选择客户
</ContentWrap>
<CustomerForm ref="customerFormRef" :userOptions="userOptions" :type="1" :dataList="formData.followUpRecords" v-if="formData.selectCustomer" />
<ContentWrap style="border: none;font-weight: 700" >
选择任务
</ContentWrap>
<TaskForm ref="taskFormRef" :type="1" :dataList="formData.tasks" />
<!-- <ContentWrap style="border: none;font-weight: 700" >
日报信息
</ContentWrap>
<DayReportForm :reportType="2" :type="1" ref="dayReportFormRef" :time="time" /> -->
</el-col>
<el-col :span="24">
<el-form-item label="本月完成工作" prop="workComplate">
<el-input type="textarea" disabled :rows="5" v-model="formData.workComplate" placeholder="请输入本日完成工作" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="下月工作计划" prop="workPlan">
<el-input type="textarea" disabled :rows="5" v-model="formData.workPlan" placeholder="请输入明日工作计划" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="工作困难及协助" prop="workAssistance">
<el-input type="textarea" disabled :rows="5" v-model="formData.workAssistance" placeholder="请输入工作困难及协助" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="附件" prop="attachment">
<UploadFile v-model="formData.attachment" :disabled="true" :limit="1" :fileType="['doc','docx']"/>
</el-form-item>
</el-col>
</el-row>
</el-form>
</ContentWrap>
</template>
<script setup lang="ts">
import { getIntDictOptions, getBoolDictOptions, DICT_TYPE } from '@/utils/dict'
import { BriefWorkApi, BriefWorkVO } from '@/api/task/briefWork'
import { useUserStore } from '@/store/modules/user'
import CustomerForm from '../briefWeekWork/components/selectCustomer.vue'
import TaskForm from '../briefWeekWork/components/selectTask.vue'
import DayReportForm from '../briefWeekWork/components/selectDayReport.vue'
import { propTypes } from '@/utils/propTypes'
import * as UserApi from '@/api/system/user'
/** 工作简报 表单 */
defineOptions({ name: 'BriefWorkForm' })
const { proxy }: any = getCurrentInstance();
const props = defineProps({
id: propTypes.number.def(undefined)
})
const userOptions = ref<UserApi.UserVO[]>([]) // 用户列表
const { t } = useI18n() // 国际化
const message = useMessage() // 消息弹窗
const dialogTitle = ref('') // 弹窗的标题
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
const formType = ref('') // 表单的类型:create - 新增;update - 修改
const time = ref([]) //
const formData = ref({
id: undefined,
briefType: 3,
selectCustomer: true,
briefCount: undefined,
briefStartDate: undefined,
briefEndDate: undefined,
workComplate: undefined,
workPlan: undefined,
workAssistance: undefined,
workExperience: undefined,
attachment: undefined,
processInstanceId: undefined,
auditStatus: undefined
})
const formRules = reactive({
briefStartDate: [{ required: true, message: '简报日期不能为空', trigger: 'blur' }],
workComplate: [{ required: true, message: '本月期完成工作不能为空', trigger: 'blur' }],
workPlan: [{ required: true, message: '下月工作计划不能为空', trigger: 'blur' }]
})
const formRef = ref() // 表单 Ref
const customerFormRef = ref() // 表单 Ref
const taskFormRef = ref() // 表单 Ref
const dayReportFormRef = ref() // 表单 Ref
const goBack = ()=> {
proxy.$router.go(-1)
}
const open = async (id?: number) => {
resetForm()
// 修改时,设置数据
if (id) {
formLoading.value = true
try {
formData.value = await BriefWorkApi.getBriefWork({id: id, briefType: 3})
time.value = [formData.value.briefStartDate + ' 00:00:00', formData.value.briefEndDate + ' 23:59:59']
} finally {
formLoading.value = false
}
}
}
/** 重置表单 */
const resetForm = () => {
formData.value = {
id: undefined,
briefType: 1,
briefCount: undefined,
briefStartDate: undefined,
briefEndDate: undefined,
selectCustomer: true,
workComplate: undefined,
workPlan: undefined,
workAssistance: undefined,
workExperience: undefined,
attachment: undefined,
processInstanceId: undefined,
auditStatus: undefined
}
formRef.value?.resetFields()
}
const route = useRoute();
onMounted(async () => {
formType.value = props.id || route.query.id
if (formType.value) await open(formType.value)
// 获得用户列表
userOptions.value = await UserApi.getSimpleUserList()
})
</script>

View File

@ -0,0 +1,225 @@
<template>
<ContentWrap>
<el-form
ref="formRef"
:model="formData"
:rules="formRules"
label-width="120px"
v-loading="formLoading"
>
<el-row>
<el-col :span="8">
<el-form-item label="简报开始日期" prop="briefStartDate">
<el-date-picker
v-model="formData.briefStartDate"
type="date"
disabled
value-format="YYYY-MM-DD"
placeholder="选择简报开始日期"
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="简报结束日期" prop="briefEndDate">
<el-date-picker
disabled
v-model="formData.briefEndDate"
type="date"
value-format="YYYY-MM-DD"
placeholder="选择简报结束日期"
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="是否选择客户" prop="selectCustomer">
<el-radio-group v-model="formData.selectCustomer">
<el-radio :label="true">是</el-radio>
<el-radio :label="false">否</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="24">
<ContentWrap style="border: none;font-weight: 700" v-if="formData.selectCustomer">
选择客户
</ContentWrap>
<CustomerForm ref="customerFormRef" :userOptions="userOptions" v-if="formData.selectCustomer" />
<ContentWrap style="border: none;font-weight: 700" >
选择任务
</ContentWrap>
<TaskForm ref="taskFormRef" />
<ContentWrap style="border: none;font-weight: 700" >
日报信息
</ContentWrap>
<DayReportForm :reportType="2" ref="dayReportFormRef" :time="time" />
</el-col>
<el-col :span="24">
<el-form-item label="本月完成工作" prop="workComplate">
<el-input type="textarea" :rows="5" v-model="formData.workComplate" placeholder="请输入本日完成工作" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="下月工作计划" prop="workPlan">
<el-input type="textarea" :rows="5" v-model="formData.workPlan" placeholder="请输入明日工作计划" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="工作困难及协助" prop="workAssistance">
<el-input type="textarea" :rows="5" v-model="formData.workAssistance" placeholder="请输入工作困难及协助" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="附件" prop="attachment">
<UploadFile v-model="formData.attachment" :disabled="!!formType" :limit="1" :fileType="['doc','docx']"/>
</el-form-item>
</el-col>
</el-row>
</el-form>
<div style="text-align: right">
<el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
<el-button @click="goBack = false">取 消</el-button>
</div>
</ContentWrap>
</template>
<script setup lang="ts">
import { getIntDictOptions, getBoolDictOptions, DICT_TYPE } from '@/utils/dict'
import { BriefWorkApi, BriefWorkVO } from '@/api/task/briefWork'
import { useUserStore } from '@/store/modules/user'
import CustomerForm from '../briefWeekWork/components/selectCustomer.vue'
import TaskForm from '../briefWeekWork/components/selectTask.vue'
import DayReportForm from '../briefWeekWork/components/selectDayReport.vue'
import moment from 'moment';
/** 工作简报 表单 */
defineOptions({ name: 'BriefWorkForm' })
const { proxy }: any = getCurrentInstance();
const { t } = useI18n() // 国际化
const message = useMessage() // 消息弹窗
const dialogTitle = ref('') // 弹窗的标题
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
const formType = ref('') // 表单的类型:create - 新增;update - 修改
const time = ref([]) //
const formData = ref({
id: undefined,
briefType: 3,
selectCustomer: true,
briefCount: undefined,
briefStartDate: undefined,
briefEndDate: undefined,
workComplate: undefined,
workPlan: undefined,
workAssistance: undefined,
workExperience: undefined,
attachment: undefined,
processInstanceId: undefined,
auditStatus: undefined
})
const formRules = reactive({
briefStartDate: [{ required: true, message: '简报日期不能为空', trigger: 'blur' }],
workComplate: [{ required: true, message: '本周期完成工作不能为空', trigger: 'blur' }],
workPlan: [{ required: true, message: '下周工作计划不能为空', trigger: 'blur' }]
})
const formRef = ref() // 表单 Ref
const customerFormRef = ref() // 表单 Ref
const taskFormRef = ref() // 表单 Ref
const dayReportFormRef = ref() // 表单 Ref
/** 提交表单 */
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
const submitForm = async () => {
// 校验表单
await formRef.value.validate()
// 提交请求
formLoading.value = true
try {
const data = formData.value as unknown as BriefWorkVO
if(customerFormRef.value) {
let customerData = customerFormRef.value.getData()
data.customerIds = customerData.map( v=> v.id)
}
let taskData = taskFormRef.value.getData()
let dayReportData = dayReportFormRef.value.getData()
data.taskIds = taskData.map( v=> v.id)
// data.taskIds = dayReportData.map( v=> v.id)
if (!formType.value) {
await BriefWorkApi.createBriefWork(data)
message.success(t('common.createSuccess'))
} else {
await BriefWorkApi.updateBriefWork(data)
message.success(t('common.updateSuccess'))
}
// 发送操作成功的事件
emit('success')
goBack()
} finally {
formLoading.value = false
}
}
const goBack = ()=> {
proxy.$router.go(-1)
}
/** 重置表单 */
const resetForm = () => {
formData.value = {
id: undefined,
briefType: 3,
briefCount: undefined,
briefStartDate: undefined,
briefEndDate: undefined,
selectCustomer: true,
workComplate: undefined,
workPlan: undefined,
workAssistance: undefined,
workExperience: undefined,
attachment: undefined,
processInstanceId: undefined,
auditStatus: undefined
}
formRef.value?.resetFields()
}
const getFirstAndLastDayOfMonth = () => {
let today = new Date(); // 当前日期
let firstDayOfMonth;
let lastDayOfMonth;
// 设置当前月份的第一天
firstDayOfMonth = new Date(today.getFullYear(), today.getMonth(), 1); // 设置日为1,自动调整为该月第一天
firstDayOfMonth.setHours(0, 0, 0, 0); // 确保时间为00:00:00
// 设置当前月份的最后一天
lastDayOfMonth = new Date(today.getFullYear(), today.getMonth() + 1, 0); // 设置日为下个月的第一天,然后减去1天
lastDayOfMonth.setHours(23, 59, 59); // 确保时间为23:59:59
return {
firstDayOfMonth,
lastDayOfMonth
};
}
const route = useRoute();
onMounted(async () => {
//获取当月第一天和最后一天
const { firstDayOfMonth, lastDayOfMonth } = getFirstAndLastDayOfMonth();
formData.value.briefStartDate = moment(firstDayOfMonth).format('YYYY-MM-DD'); // 输出格式为 YYYY-MM-DD
formData.value.briefEndDate = moment(lastDayOfMonth).format('YYYY-MM-DD'); // 输出格式为 YYYY-MM-DD
// formType.value = props.id || route.query.id
// if (formType.value) await open(formType.value)
time.value = [formData.value.briefStartDate, formData.value.briefEndDate]
// if (!formType.value) {
// formData.value.userId = useUserStore().getUser.id
// }
})
</script>

View File

@ -0,0 +1,281 @@
<template>
<ContentWrap>
<!-- 搜索工作栏 -->
<el-form
class="-mb-15px"
:model="queryParams"
ref="queryFormRef"
:inline="true"
label-width="68px"
>
<!-- <el-form-item label="审批状态" prop="auditStatus">
<el-select
v-model="queryParams.auditStatus"
placeholder="请选择审批状态"
clearable
class="!w-240px"
>
<el-option label="请选择字典生成" value="" />
</el-select>
</el-form-item> -->
<el-form-item label="填报人员" prop="creator">
<el-select v-model="queryParams.creator" placeholder="请选择申请人" class="!w-240px">
<el-option
v-for="dict in userOptions"
:key="dict.id"
:label="dict.nickname"
:value="dict.id"
/>
</el-select>
</el-form-item>
<el-form-item label="简报日期" prop="createTime">
<el-date-picker
v-model="queryParams.createTime"
value-format="YYYY-MM-DD HH:mm:ss"
type="daterange"
start-placeholder="开始日期"
end-placeholder="结束日期"
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
class="!w-220px"
/>
</el-form-item>
<el-form-item>
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
<el-button
type="primary"
plain
@click="openFormAdd('create')"
v-hasPermi="['tts:brief-month-work:create']"
>
<Icon icon="ep:plus" class="mr-5px" /> 新增
</el-button>
<el-button
type="success"
plain
@click="handleExport"
:loading="exportLoading"
v-hasPermi="['tts:brief-work:export']"
>
<Icon icon="ep:download" class="mr-5px" /> 导出
</el-button>
</el-form-item>
</el-form>
</ContentWrap>
<!-- 列表 -->
<ContentWrap>
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
<el-table-column
label="简报日期"
align="center"
prop="createTime"
:formatter="dateFormatter"
width="180px"
/>
<el-table-column label="填报人员" align="center" prop="creator">
<template #default="scope">
{{getName(userOptions, scope.row.creator, 'id', 'nickname')}}
</template>
</el-table-column>
<el-table-column label="本月完成工作" align="center" prop="workComplate" />
<el-table-column label="下月工作计划" align="center" prop="workPlan" />
<el-table-column label="工作困难及协助" align="center" prop="workAssistance" />
<el-table-column label="上传附件" align="center" prop="workAssistance">
<template #default="scope">
<el-link
v-if="scope.row.attachment"
:underline="false"
type="primary"
@click="previewFile(scope.row.attachment)"
>
预览
</el-link>
<span v-else>无附件</span>
</template>
</el-table-column>
<el-table-column label="审批状态" align="center" prop="auditStatus" width="110">
<template #default="scope">
<dict-tag :type="DICT_TYPE.TTS_APPLY_STATUS" :value="scope.row.auditStatus" />
</template>
</el-table-column>
<el-table-column label="操作" align="center" min-width="120px">
<template #default="scope">
<el-button
v-if="scope.row.auditStatus !== 0"
link
type="primary"
@click="handleProcessDetail(scope.row)"
>
进度
</el-button>
<el-button
link
type="primary"
@click="openFormDetail(scope.row)"
>
详情
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<Pagination
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
</ContentWrap>
<!-- 表单弹窗:添加/修改 -->
<!-- <BriefWorkForm ref="formRef" @success="getList" /> -->
</template>
<script setup lang="ts">
import { getIntDictOptions, getBoolDictOptions, DICT_TYPE } from '@/utils/dict'
import { dateFormatter } from '@/utils/formatTime'
import download from '@/utils/download'
import { BriefWorkApi, BriefWorkVO } from '@/api/task/briefWork'
// import BriefWorkForm from './BriefWorkForm.vue'
import * as UserApi from '@/api/system/user'
/** 工作简报 列表 */
defineOptions({ name: 'BriefWork' })
const message = useMessage() // 消息弹窗
const { t } = useI18n() // 国际化
const userOptions = ref<UserApi.UserVO[]>([]) // 用户列表
const loading = ref(true) // 列表的加载中
const list = ref<BriefWorkVO[]>([]) // 列表的数据
const total = ref(0) // 列表的总页数
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
briefType: 3,
briefCount: undefined,
briefStartDate: [],
briefEndDate: [],
selectCustomer: undefined,
workComplate: undefined,
workPlan: undefined,
workAssistance: undefined,
workExperience: undefined,
attachment: undefined,
processInstanceId: undefined,
auditStatus: undefined,
creator: undefined,
createTime: [],
updater: undefined,
updateTime: []
})
const queryFormRef = ref() // 搜索的表单
const exportLoading = ref(false) // 导出的加载中
/** 查询列表 */
const getList = async () => {
loading.value = true
try {
const data = await BriefWorkApi.getBriefWorkPage(queryParams)
list.value = data.list
total.value = data.total
} finally {
loading.value = false
}
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.pageNo = 1
getList()
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value.resetFields()
handleQuery()
}
/** 添加/修改操作 */
const formRef = ref()
const { push } = useRouter()
const router = useRouter() // 路
//新增
const openFormAdd = () => {
push({ name: 'MonthReportAdd' })
}
//详情
const openFormDetail = (row: Object) => {
router.push({ name: 'MonthReportDetail', query: { id: row.id} })
}
/** 查看审批 */
const handleProcessDetail = (row) => {
router.push({ name: 'BpmProcessInstanceDetail', query: { id: row.processInstanceId } })
}
/** 删除按钮操作 */
const handleDelete = async (id: number) => {
try {
// 删除的二次确认
await message.delConfirm()
// 发起删除
await BriefWorkApi.deleteBriefWork(id)
message.success(t('common.delSuccess'))
// 刷新列表
await getList()
} catch {}
}
/** 导出按钮操作 */
const handleExport = async () => {
try {
// 导出的二次确认
await message.exportConfirm()
// 发起导出
exportLoading.value = true
const data = await BriefWorkApi.exportBriefWork(queryParams)
download.excel(data, '月工作简报.xls')
} catch {
} finally {
exportLoading.value = false
}
}
//预览
const previewFile = (fileUrls) => {
// // 假设 fileUrls 是一个字符串,包含文件的完整 URL
if (!fileUrls) {
message.error('没有附件');
return;
}
// 检查文件类型
const fileExtension = fileUrls.split('.').pop().toLowerCase();
router.push({
name: 'FileTemplatePreview',
query: {
src: encodeURIComponent(fileUrls),
type: fileExtension
}
})
}
const getName = (opt, val, id, name) => {
const arr = opt.filter(v => v[id] == val)
return arr.length ? arr[0][name] : ''
}
/** 初始化 **/
onMounted(async() => {
// 获得用户列表
userOptions.value = await UserApi.getSimpleUserList()
getList()
})
onActivated(()=>{
getList()
})
</script>

View File

@ -0,0 +1,208 @@
<template>
<ContentWrap>
<el-form
ref="formRef"
:model="formData"
:rules="formRules"
label-width="120px"
v-loading="formLoading"
>
<el-row>
<el-col :span="8">
<el-form-item label="简报开始日期" prop="briefStartDate">
<el-date-picker
v-model="formData.briefStartDate"
type="date"
disabled
value-format="YYYY-MM-DD"
placeholder="选择简报开始日期"
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="简报结束日期" prop="briefEndDate">
<el-date-picker
disabled
v-model="formData.briefEndDate"
type="date"
value-format="YYYY-MM-DD"
placeholder="选择简报结束日期"
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="是否选择客户" prop="selectCustomer">
<el-radio-group v-model="formData.selectCustomer" disabled>
<el-radio :label="true">是</el-radio>
<el-radio :label="false">否</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="24">
<ContentWrap style="border: none;font-weight: 700" v-if="formData.selectCustomer">
选择客户
</ContentWrap>
<CustomerForm ref="customerFormRef" :userOptions="userOptions" :type="1" :dataList="formData.followUpRecords" v-if="formData.selectCustomer" />
<ContentWrap style="border: none;font-weight: 700" >
选择任务
</ContentWrap>
<TaskForm ref="taskFormRef" :type="1" :dataList="formData.tasks" />
<!-- <ContentWrap style="border: none;font-weight: 700" >
日报信息
</ContentWrap>
<DayReportForm :reportType="1" :type="1" ref="dayReportFormRef" :time="time" /> -->
</el-col>
<el-col :span="24">
<el-form-item label="本周完成工作" prop="workComplate">
<el-input type="textarea" disabled :rows="5" v-model="formData.workComplate" placeholder="请输入本日完成工作" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="下周工作计划" prop="workPlan">
<el-input type="textarea" disabled :rows="5" v-model="formData.workPlan" placeholder="请输入明日工作计划" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="工作困难及协助" prop="workAssistance">
<el-input type="textarea" disabled :rows="5" v-model="formData.workAssistance" placeholder="请输入工作困难及协助" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="附件" prop="attachment">
<UploadFile v-model="formData.attachment" :disabled="!!formType" :limit="1" :fileType="['doc','docx']"/>
</el-form-item>
</el-col>
</el-row>
</el-form>
</ContentWrap>
</template>
<script setup lang="ts">
import { getIntDictOptions, getBoolDictOptions, DICT_TYPE } from '@/utils/dict'
import { BriefWorkApi, BriefWorkVO } from '@/api/task/briefWork'
import { useUserStore } from '@/store/modules/user'
import CustomerForm from './components/selectCustomer.vue'
import TaskForm from './components/selectTask.vue'
import DayReportForm from './components/selectDayReport.vue'
import { propTypes } from '@/utils/propTypes'
import * as UserApi from '@/api/system/user'
/** 工作简报 表单 */
defineOptions({ name: 'BriefWorkForm' })
const { proxy }: any = getCurrentInstance();
const props = defineProps({
id: propTypes.number.def(undefined)
})
const userOptions = ref<UserApi.UserVO[]>([]) // 用户列表
const { t } = useI18n() // 国际化
const message = useMessage() // 消息弹窗
const dialogTitle = ref('') // 弹窗的标题
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
const formType = ref('') // 表单的类型:create - 新增;update - 修改
const time = ref([]) //
const formData = ref({
id: undefined,
briefType: 2,
selectCustomer: true,
briefCount: undefined,
briefStartDate: undefined,
briefEndDate: undefined,
workComplate: undefined,
workPlan: undefined,
workAssistance: undefined,
workExperience: undefined,
attachment: undefined,
processInstanceId: undefined,
auditStatus: undefined
})
const formRules = reactive({
briefStartDate: [{ required: true, message: '简报日期不能为空', trigger: 'blur' }],
workComplate: [{ required: true, message: '本周期完成工作不能为空', trigger: 'blur' }],
workPlan: [{ required: true, message: '下周工作计划不能为空', trigger: 'blur' }]
})
const open = async (id?: number) => {
resetForm()
// 修改时,设置数据
if (id) {
formLoading.value = true
try {
formData.value = await BriefWorkApi.getBriefWork({id: id, briefType: 2})
time.value = [formData.value.briefStartDate + ' 00:00:00', formData.value.briefEndDate + ' 23:59:59']
} finally {
formLoading.value = false
}
}
}
const formRef = ref() // 表单 Ref
const customerFormRef = ref() // 表单 Ref
const taskFormRef = ref() // 表单 Ref
const dayReportFormRef = ref() // 表单 Ref
/** 提交表单 */
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
const submitForm = async () => {
// 校验表单
await formRef.value.validate()
// 提交请求
formLoading.value = true
try {
const data = formData.value as unknown as BriefWorkVO
let customerData = customerFormRef.value.getData()
console.log('%csrc/views/task/briefWeekWork/BriefWorkWeekForm.vue:136 customerData', 'color: #007acc;', customerData);
let taskData = taskFormRef.value.getData()
let dayReportData = dayReportFormRef.value.getData()
data.customerIds = customerData.map( v=> v.id)
console.log('%csrc/views/task/briefWeekWork/BriefWorkWeekForm.vue:139 data', 'color: #007acc;', data.customerIds);
data.taskIds = taskData.map( v=> v.id)
// data.taskIds = dayReportData.map( v=> v.id)
if (!formType.value) {
await BriefWorkApi.createBriefWork(data)
message.success(t('common.createSuccess'))
} else {
await BriefWorkApi.updateBriefWork(data)
message.success(t('common.updateSuccess'))
}
// 发送操作成功的事件
emit('success')
goBack()
} finally {
formLoading.value = false
}
}
const goBack = ()=> {
proxy.$router.go(-1)
}
/** 重置表单 */
const resetForm = () => {
formData.value = {
id: undefined,
briefType: 1,
briefCount: undefined,
briefStartDate: undefined,
briefEndDate: undefined,
selectCustomer: true,
workComplate: undefined,
workPlan: undefined,
workAssistance: undefined,
workExperience: undefined,
attachment: undefined,
processInstanceId: undefined,
auditStatus: undefined
}
formRef.value?.resetFields()
}
const route = useRoute();
onMounted(async () => {
formType.value = props.id || route.query.id
if (formType.value) await open(formType.value)
// 获得用户列表
userOptions.value = await UserApi.getSimpleUserList()
})
</script>

View File

@ -0,0 +1,219 @@
<template>
<ContentWrap>
<el-form
ref="formRef"
:model="formData"
:rules="formRules"
label-width="120px"
v-loading="formLoading"
>
<el-row>
<el-col :span="8">
<el-form-item label="简报开始日期" prop="briefStartDate">
<el-date-picker
v-model="formData.briefStartDate"
type="date"
disabled
value-format="YYYY-MM-DD"
placeholder="选择简报开始日期"
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="简报结束日期" prop="briefEndDate">
<el-date-picker
disabled
v-model="formData.briefEndDate"
type="date"
value-format="YYYY-MM-DD"
placeholder="选择简报结束日期"
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="是否选择客户" prop="selectCustomer">
<el-radio-group v-model="formData.selectCustomer">
<el-radio :label="true">是</el-radio>
<el-radio :label="false">否</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="24">
<ContentWrap style="border: none;font-weight: 700" v-if="formData.selectCustomer">
选择客户
</ContentWrap>
<CustomerForm ref="customerFormRef" :userOptions="userOptions" v-if="formData.selectCustomer" />
<ContentWrap style="border: none;font-weight: 700" >
选择任务
</ContentWrap>
<TaskForm ref="taskFormRef" :dataList="formData.tasks" />
<ContentWrap style="border: none;font-weight: 700" >
日报信息
</ContentWrap>
<DayReportForm :reportType="1" ref="dayReportFormRef" :time="time" />
</el-col>
<el-col :span="24">
<el-form-item label="本周完成工作" prop="workComplate">
<el-input type="textarea" :rows="5" v-model="formData.workComplate" placeholder="请输入本日完成工作" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="下周工作计划" prop="workPlan">
<el-input type="textarea" :rows="5" v-model="formData.workPlan" placeholder="请输入明日工作计划" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="工作困难及协助" prop="workAssistance">
<el-input type="textarea" :rows="5" v-model="formData.workAssistance" placeholder="请输入工作困难及协助" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="附件" prop="attachment">
<UploadFile v-model="formData.attachment" :disabled="!!formType" :limit="1" :fileType="['doc','docx']"/>
</el-form-item>
</el-col>
</el-row>
</el-form>
<div style="text-align: right">
<el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
<el-button @click="goBack = false">取 消</el-button>
</div>
</ContentWrap>
</template>
<script setup lang="ts">
import { getIntDictOptions, getBoolDictOptions, DICT_TYPE } from '@/utils/dict'
import { BriefWorkApi, BriefWorkVO } from '@/api/task/briefWork'
import { useUserStore } from '@/store/modules/user'
import CustomerForm from './components/selectCustomer.vue'
import TaskForm from './components/selectTask.vue'
import DayReportForm from './components/selectDayReport.vue'
import * as UserApi from '@/api/system/user'
/** 工作简报 表单 */
defineOptions({ name: 'BriefWorkForm' })
const { proxy }: any = getCurrentInstance();
const { t } = useI18n() // 国际化
const message = useMessage() // 消息弹窗
const dialogTitle = ref('') // 弹窗的标题
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
const formType = ref('') // 表单的类型:create - 新增;update - 修改
const time = ref([]) //
const formData = ref({
id: undefined,
briefType: 2,
selectCustomer: true,
briefCount: undefined,
briefStartDate: undefined,
briefEndDate: undefined,
workComplate: undefined,
workPlan: undefined,
workAssistance: undefined,
workExperience: undefined,
attachment: undefined,
processInstanceId: undefined,
auditStatus: undefined
})
const formRules = reactive({
briefStartDate: [{ required: true, message: '简报日期不能为空', trigger: 'blur' }],
workComplate: [{ required: true, message: '本周期完成工作不能为空', trigger: 'blur' }],
workPlan: [{ required: true, message: '下周工作计划不能为空', trigger: 'blur' }]
})
const userOptions = ref<UserApi.UserVO[]>([]) // 用户列表
const formRef = ref() // 表单 Ref
const customerFormRef = ref() // 表单 Ref
const taskFormRef = ref() // 表单 Ref
const dayReportFormRef = ref() // 表单 Ref
/** 提交表单 */
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
const submitForm = async () => {
// 校验表单
await formRef.value.validate()
// 提交请求
formLoading.value = true
try {
const data = formData.value as unknown as BriefWorkVO
if(customerFormRef.value) {
let customerData = customerFormRef.value.getData()
data.customerIds = customerData.map( v=> v.id)
}
let taskData = taskFormRef.value.getData()
let dayReportData = dayReportFormRef.value.getData()
data.taskIds = taskData.map( v=> v.id)
// data.taskIds = dayReportData.map( v=> v.id)
if (!formType.value) {
await BriefWorkApi.createBriefWork(data)
message.success(t('common.createSuccess'))
} else {
await BriefWorkApi.updateBriefWork(data)
message.success(t('common.updateSuccess'))
}
// 发送操作成功的事件
emit('success')
goBack()
} finally {
formLoading.value = false
}
}
const goBack = ()=> {
proxy.$router.go(-1)
}
/** 重置表单 */
const resetForm = () => {
formData.value = {
id: undefined,
briefType: 1,
briefCount: undefined,
briefStartDate: undefined,
briefEndDate: undefined,
selectCustomer: true,
workComplate: undefined,
workPlan: undefined,
workAssistance: undefined,
workExperience: undefined,
attachment: undefined,
processInstanceId: undefined,
auditStatus: undefined
}
formRef.value?.resetFields()
}
const route = useRoute();
onMounted(async () => {
//获取当前时间的周一和周日
const now = new Date();
const dayOfWeek = now.getDay();
let monday = new Date(now);
if (dayOfWeek !== 0) {
monday.setDate(now.getDate() - dayOfWeek + 1);
} else {
monday.setDate(now.getDate() - 6);
}
let sunday = new Date(now);
const daysUntilSunday = (7 - dayOfWeek) % 7; // 计算到下一个周日的天数差
sunday.setDate(now.getDate() + daysUntilSunday);
formData.value.briefStartDate = monday.toISOString().slice(0, 10); // 输出格式为 YYYY-MM-DD
formData.value.briefEndDate = sunday.toISOString().slice(0, 10); // 输出格式为 YYYY-MM-DD
console.log('%csrc/views/project/briefWeekWork/BriefWorkWeekForm.vue:202 formData.value.briefStartDate ', 'color: #007acc;', formData.value.briefStartDate );
time.value = [formData.value.briefStartDate + ' 00:00:00', formData.value.briefEndDate + ' 23:59:59']
// 获得用户列表
userOptions.value = await UserApi.getSimpleUserList()
// formType.value = props.id || route.query.id
// if (formType.value) await open(formType.value)
// if (!formType.value) {
// formData.value.userId = useUserStore().getUser.id
// }
})
</script>

View File

@ -0,0 +1,194 @@
<template>
<!-- 列表 -->
<ContentWrap>
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
<!-- <el-table-column label="唯一主键" align="center" prop="id" /> -->
<!-- <el-table-column label="项目ID" align="center" prop="projectId" /> -->
<el-table-column label="单位名称" align="center" prop="name" />
<el-table-column label="跟进类型" align="center" prop="followUpStatus">
<template #default="scope">
<dict-tag :type="DICT_TYPE.CRM_FOLLOW_UP_TYPE" :value="scope.row.followUpType" />
</template>
</el-table-column>
<el-table-column label="跟进内容" align="center" prop="contactLastContent" />
<el-table-column label="跟进时间" align="center" prop="contactLastTime" :formatter="dateFormatter" />
<el-table-column label="跟进人" align="center" prop="followUpUserId">
<template #default="scope">
{{getName(userOptions, scope.row.followUpUserId)}}
</template>
</el-table-column>
<el-table-column label="操作" align="center" min-width="120px" v-if="!type">
<template #default="scope">
<el-button
link
type="danger"
@click="handleDelete(scope.$index)"
v-hasPermi="['task:selectCustomer:delete']"
>
删除
</el-button>
</template>
</el-table-column>
</el-table>
<el-row justify="center" class="mt-3" v-if="!type">
<el-button @click="openForm" round>+ 添加</el-button>
</el-row>
<!-- <el-button
type="primary"
plain
@click="openForm('create')"
v-hasPermi="['task:project-bonus:create']"
>
<Icon icon="ep:plus" class="mr-5px" /> 新增
</el-button> -->
<!-- 分页 -->
<!-- <Pagination
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/> -->
</ContentWrap>
<!-- 表单弹窗:添加/修改 -->
<CustomerForm ref="formRef" @success="getList" :id="id" />
</template>
<script setup lang="ts">
import { dateFormatter } from '@/utils/formatTime'
import download from '@/utils/download'
import { ProjectBonusApi, ProjectBonusVO } from '@/api/task/bonus'
import CustomerForm from '@/components/customer/index.vue'
import { propTypes } from '@/utils/propTypes'
import { DICT_TYPE } from '@/utils/dict'
import * as UserApi from '@/api/system/user'
/** 项目奖金 列表 */
defineOptions({ name: 'ProjectBonus' })
const message = useMessage() // 消息弹窗
const { t } = useI18n() // 国际化
const props = defineProps({
id: propTypes.number.def(undefined),
type: propTypes.number.def(undefined),
dataList: propTypes.object.def([]),
})
const userOptions = ref<UserApi.UserVO[]>([]) // 用户列表
const loading = ref(false) // 列表的加载中
const list = ref<ProjectBonusVO[]>([]) // 列表的数据
const total = ref(0) // 列表的总页数
const queryParams = reactive({
pageNo: 1,
pageSize: 999,
projectId: undefined,
userId: undefined,
bonusPercentage: undefined,
bonusAmount: undefined,
bonusIssueDate: [],
bonusState: undefined,
createTime: []
})
const queryFormRef = ref() // 搜索的表单
const exportLoading = ref(false) // 导出的加载中
/** 初始化设置产品项 */
watch(
() => props.dataList,
async (val) => {
list.value = val || []
},
{ immediate: true }
)
/** 查询列表 */
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
const getList = (val: []) => {
for(let i = val.length - 1; i >= 0; i--) {
let obj = val[i]
if(list.value.some(v => v.id === obj.id)) val.splice(i, 1)
}
val.forEach(item => {
if(!list.value.some(v => v.id === item.id)) {
console.log('%csrc/views/project/briefWeekWork/components/selectCustomer.vue:95 val', 'color: #007acc;', val);
val.forEach(item => {
list.value.push({
"name": item.name,
"followUpType": item.followUpType,
"contactLastTime": item.contactLastTime,
"contactLastContent": item.contactLastContent,
"followUpUserId": item.creator,
"id": item.id,
})
})
console.log('%csrc/views/project/briefWeekWork/components/selectCustomer.vue:122 list.value', 'color: #007acc;', list.value);
}
})
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.pageNo = 1
// getList()
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value.resetFields()
handleQuery()
}
/** 添加/修改操作 */
const formRef = ref()
const openForm = () => {
formRef.value.open(list.value)
}
/** 删除按钮操作 */
const handleDelete = async (i: number) => {
try {
// 删除的二次确认
await message.delConfirm()
// 发起删除
console.log('%csrc/views/task/briefWeekWork/components/selectCustomer.vue:132 i', 'color: #007acc;', i);
list.value.splice(i, 1)
message.success(t('common.delSuccess'))
// 刷新列表
// await getList()
} catch {}
}
/** 导出按钮操作 */
const handleExport = async () => {
try {
// 导出的二次确认
await message.exportConfirm()
// 发起导出
exportLoading.value = true
const data = await ProjectBonusApi.exportProjectBonus(queryParams)
download.excel(data, '项目奖金.xls')
} catch {
} finally {
exportLoading.value = false
}
}
const getName = (opt, val) => {
console.log('%csrc/views/project/briefWeekWork/components/selectCustomer.vue:176 opt,val', 'color: #007acc;', props.userOptions,val);
const arr = opt.filter(v => v.id == val)
return arr.length ? arr[0]['nickname'] : ''
}
const getData = (data) => {
return list.value
}
defineExpose({ getData })
/** 初始化 **/
onMounted(async() => {
// getList()
// 获得用户列表
userOptions.value = await UserApi.getSimpleUserList()
})
</script>

View File

@ -0,0 +1,197 @@
<template>
<!-- 列表 -->
<ContentWrap>
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
<el-table-column
label="填写时间"
align="center"
prop="createTime"
:formatter="dateFormatter"
width="180px"
/>
<el-table-column :label="reportType == 1 ? '今日完成工作' : '本周完成工作'" align="center" prop="workComplate">
<template #default="scope">
<div style="display: flex;justify-content: center;">
<span>
{{scope.row.workComplate}}
</span>
<el-button
v-if="!type && scope.row.workComplate"
link
type="primary"
@click="handleSelect(scope.row.workComplate)"
>
复制
</el-button>
</div>
</template>
</el-table-column>
<el-table-column :label="reportType == 1 ? '明日工作计划' : '下周工作计划'" align="center" prop="workPlan" />
<el-table-column label="工作困难及协助" align="center" prop="workAssistance">
<template #default="scope">
<div style="display: flex;justify-content: center;">
<span>
{{scope.row.workAssistance}}
</span>
<el-button
v-if="!type && scope.row.workAssistance"
link
type="primary"
@click="handleSelect(scope.row.workAssistance)"
>
复制
</el-button>
</div>
</template>
</el-table-column>
</el-table>
</ContentWrap>
<!-- 表单弹窗:添加/修改 -->
<!-- <BriefWorkForm ref="formRef" @success="getList" /> -->
</template>
<script setup lang="ts">
import { getIntDictOptions, getBoolDictOptions, DICT_TYPE } from '@/utils/dict'
import { dateFormatter } from '@/utils/formatTime'
import download from '@/utils/download'
import { BriefWorkApi, BriefWorkVO } from '@/api/task/briefWork'
// import BriefWorkForm from './BriefWorkForm.vue'
import * as UserApi from '@/api/system/user'
import { propTypes } from '@/utils/propTypes'
/** 工作简报 列表 */
defineOptions({ name: 'BriefWork' })
const props = defineProps({
time: [],
reportType: propTypes.number.def(undefined),
type: propTypes.number.def(undefined),
})
const message = useMessage() // 消息弹窗
const { t } = useI18n() // 国际化
const userOptions = ref<UserApi.UserVO[]>([]) // 用户列表
const loading = ref(true) // 列表的加载中
const list = ref<BriefWorkVO[]>([]) // 列表的数据
const total = ref(0) // 列表的总页数
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
briefType: props.reportType,
briefCount: undefined,
briefStartDate: [],
briefEndDate: [],
selectCustomer: undefined,
workComplate: undefined,
workPlan: undefined,
workAssistance: undefined,
workExperience: undefined,
attachment: undefined,
processInstanceId: undefined,
auditStatus: undefined,
creator: undefined,
createTime: [],
updater: undefined,
updateTime: []
})
const queryFormRef = ref() // 搜索的表单
const exportLoading = ref(false) // 导出的加载中
/** 查询列表 */
const getList = async () => {
loading.value = true
try {
queryParams.startDate = props.time[0]
queryParams.endDate = props.time[1]
const data = await BriefWorkApi.getBriefWorkTimePage(queryParams)
list.value = data.list
total.value = data.total
} finally {
loading.value = false
}
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.pageNo = 1
getList()
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value.resetFields()
handleQuery()
}
/** 添加/修改操作 */
const formRef = ref()
const { push } = useRouter()
const router = useRouter() // 路
//新增
const openFormAdd = () => {
push({ name: 'DayReportAdd' })
}
//详情
const openFormDetail = (row: Object) => {
router.push({ name: 'DayReportDetail', query: { id: row.id} })
}
/** 查看审批 */
const handleProcessDetail = (row) => {
router.push({ name: 'BpmProcessInstanceDetail', query: { id: row.processInstanceId } })
}
const handleSelect = (val) => {
navigator.clipboard.writeText(val)
}
/** 删除按钮操作 */
const handleDelete = async (id: number) => {
try {
// 删除的二次确认
await message.delConfirm()
// 发起删除
await BriefWorkApi.deleteBriefWork(id)
message.success(t('common.delSuccess'))
// 刷新列表
await getList()
} catch {}
}
/** 导出按钮操作 */
const handleExport = async () => {
try {
// 导出的二次确认
await message.exportConfirm()
// 发起导出
exportLoading.value = true
const data = await BriefWorkApi.exportBriefWork(queryParams)
download.excel(data, '工作简报.xls')
} catch {
} finally {
exportLoading.value = false
}
}
const getName = (opt, val, id, name) => {
const arr = opt.filter(v => v[id] == val)
return arr.length ? arr[0][name] : ''
}
const getData = (data) => {
return list.value
}
defineExpose({ getData })
/** 初始化 **/
onMounted(async() => {
// 获得用户列表
userOptions.value = await UserApi.getSimpleUserList()
getList()
})
onActivated(()=>{
getList()
})
</script>

View File

@ -0,0 +1,174 @@
<template>
<!-- 列表 -->
<ContentWrap>
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
<!-- <el-table-column label="唯一主键" align="center" prop="id" /> -->
<!-- <el-table-column label="项目ID" align="center" prop="projectId" /> -->
<el-table-column label="任务标题" align="center" prop="taskName" />
<el-table-column label="任务优先级" align="center" prop="taskLevel">
<template #default="scope">
<dict-tag :type="DICT_TYPE.TTS_TASK_LEVEL" :value="scope.row.taskLevel" />
</template>
</el-table-column>
<el-table-column label="任务负责人" align="center" prop="userId">
<template #default="scope">
{{getName(userOptions, scope.row.userId)}}
</template>
</el-table-column>
<el-table-column label="任务描述" align="center" prop="taskDescribe" />
<el-table-column label="操作" align="center" min-width="120px" v-if="!type">
<template #default="scope">
<el-button
link
type="danger"
@click="handleDelete(scope.$index)"
v-hasPermi="['task:selectTask:delete']"
>
删除
</el-button>
</template>
</el-table-column>
</el-table>
<el-row justify="center" class="mt-3" v-if="!type">
<el-button @click="openForm" round>+ 添加</el-button>
</el-row>
</ContentWrap>
<!-- 表单弹窗:添加/修改 -->
<TaskForm ref="formRef" @success="getList" :id="id" :userOptions="userOptions" />
</template>
<script setup lang="ts">
import { dateFormatter } from '@/utils/formatTime'
import download from '@/utils/download'
import { ProjectBonusApi, ProjectBonusVO } from '@/api/task/bonus'
import TaskForm from '@/components/Task/index.vue'
import { propTypes } from '@/utils/propTypes'
import { DICT_TYPE } from '@/utils/dict'
import * as UserApi from '@/api/system/user'
import { useUserStore } from '@/store/modules/user'
/** 项目奖金 列表 */
defineOptions({ name: 'ProjectBonus' })
const message = useMessage() // 消息弹窗
const userOptions = ref<UserApi.UserVO[]>([]) // 用户列表
const { t } = useI18n() // 国际化
const props = defineProps({
id: propTypes.number.def(undefined),
type: propTypes.number.def(undefined),
dataList: propTypes.object.def([]),
})
const loading = ref(false) // 列表的加载中
const list = ref<ProjectBonusVO[]>([]) // 列表的数据
const total = ref(0) // 列表的总页数
const queryParams = reactive({
pageNo: 1,
pageSize: 999,
projectId: undefined,
userId: useUserStore().getUser.id,
bonusPercentage: undefined,
bonusAmount: undefined,
bonusIssueDate: [],
bonusState: undefined,
createTime: []
})
const queryFormRef = ref() // 搜索的表单
const exportLoading = ref(false) // 导出的加载中
/** 初始化设置产品项 */
watch(
() => props.dataList,
async (val) => {
list.value = val || []
},
{ immediate: true }
)
/** 查询列表 */
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
const getList = (val: []) => {
for(let i = val.length - 1; i >= 0; i--) {
let obj = val[i]
if(list.value.some(v => v.id === obj.id)) val.splice(i, 1)
}
val.forEach(item => {
if(!list.value.some(v => v.id === item.id)) {
val.forEach(item => {
list.value.push({
"taskName": item.taskName,
"userId": item.userId,
"taskLevel": item.taskLevel,
"taskDescribe": item.taskDescribe,
"id": item.id,
})
})
}
})
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.pageNo = 1
// getList()
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value.resetFields()
handleQuery()
}
/** 添加/修改操作 */
const formRef = ref()
const openForm = () => {
formRef.value.open(list.value)
}
/** 删除按钮操作 */
const handleDelete = async (i: number) => {
try {
// 删除的二次确认
await message.delConfirm()
// 发起删除
list.value.splice(i, 1)
message.success(t('common.delSuccess'))
// 刷新列表
// await getList()
} catch {}
}
/** 导出按钮操作 */
const handleExport = async () => {
try {
// 导出的二次确认
await message.exportConfirm()
// 发起导出
exportLoading.value = true
const data = await ProjectBonusApi.exportProjectBonus(queryParams)
download.excel(data, '项目奖金.xls')
} catch {
} finally {
exportLoading.value = false
}
}
const getName = (opt, val) => {
const arr = opt.filter(v => v.id == val)
return arr.length ? arr[0]['nickname'] : ''
}
const getData = (data) => {
return list.value
}
defineExpose({ getData })
/** 初始化 **/
onMounted(async() => {
// getList() // 获得用户列表
userOptions.value = await UserApi.getSimpleUserList()
})
</script>

View File

@ -0,0 +1,282 @@
<template>
<ContentWrap>
<!-- 搜索工作栏 -->
<el-form
class="-mb-15px"
:model="queryParams"
ref="queryFormRef"
:inline="true"
label-width="68px"
>
<!-- <el-form-item label="审批状态" prop="auditStatus">
<el-select
v-model="queryParams.auditStatus"
placeholder="请选择审批状态"
clearable
class="!w-240px"
>
<el-option label="请选择字典生成" value="" />
</el-select>
</el-form-item> -->
<el-form-item label="填报人员" prop="creator">
<el-select v-model="queryParams.creator" placeholder="请选择申请人" class="!w-240px">
<el-option
v-for="dict in userOptions"
:key="dict.id"
:label="dict.nickname"
:value="dict.id"
/>
</el-select>
</el-form-item>
<el-form-item label="简报日期" prop="createTime">
<el-date-picker
v-model="queryParams.createTime"
value-format="YYYY-MM-DD HH:mm:ss"
type="daterange"
start-placeholder="开始日期"
end-placeholder="结束日期"
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
class="!w-220px"
/>
</el-form-item>
<el-form-item>
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
<el-button
type="primary"
plain
@click="openFormAdd('create')"
v-hasPermi="['tts:brief-work:create']"
>
<Icon icon="ep:plus" class="mr-5px" /> 新增
</el-button>
<el-button
type="success"
plain
@click="handleExport"
:loading="exportLoading"
v-hasPermi="['tts:brief-work:export']"
>
<Icon icon="ep:download" class="mr-5px" /> 导出
</el-button>
</el-form-item>
</el-form>
</ContentWrap>
<!-- 列表 -->
<ContentWrap>
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
<el-table-column
label="简报日期"
align="center"
prop="createTime"
:formatter="dateFormatter"
width="180px"
/>
<el-table-column label="填报人员" align="center" prop="creator">
<template #default="scope">
{{getName(userOptions, scope.row.creator, 'id', 'nickname')}}
</template>
</el-table-column>
<el-table-column label="本周完成工作" align="center" prop="workComplate" />
<el-table-column label="下周工作计划" align="center" prop="workPlan" />
<el-table-column label="工作困难及协助" align="center" prop="workAssistance" />
<el-table-column label="上传附件" align="center" prop="attachment">
<template #default="scope">
<el-link
v-if="scope.row.attachment"
:underline="false"
type="primary"
@click="previewFile(scope.row.attachment)"
>
预览
</el-link>
<span v-else>无附件</span>
</template>
</el-table-column>
<el-table-column label="审批状态" align="center" prop="auditStatus" width="110">
<template #default="scope">
<dict-tag :type="DICT_TYPE.TTS_APPLY_STATUS" :value="scope.row.auditStatus" />
</template>
</el-table-column>
<el-table-column label="操作" align="center" min-width="120px">
<template #default="scope">
<el-button
v-if="scope.row.auditStatus !== 0"
link
type="primary"
@click="handleProcessDetail(scope.row)"
>
进度
</el-button>
<el-button
link
type="primary"
@click="openFormDetail(scope.row)"
>
详情
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<Pagination
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
</ContentWrap>
<!-- 表单弹窗:添加/修改 -->
<!-- <BriefWorkForm ref="formRef" @success="getList" /> -->
</template>
<script setup lang="ts">
import { getIntDictOptions, getBoolDictOptions, DICT_TYPE } from '@/utils/dict'
import { dateFormatter } from '@/utils/formatTime'
import download from '@/utils/download'
import { BriefWorkApi, BriefWorkVO } from '@/api/task/briefWork'
// import BriefWorkForm from './BriefWorkForm.vue'
import * as UserApi from '@/api/system/user'
/** 工作简报 列表 */
defineOptions({ name: 'BriefWork' })
const message = useMessage() // 消息弹窗
const { t } = useI18n() // 国际化
const userOptions = ref<UserApi.UserVO[]>([]) // 用户列表
const loading = ref(true) // 列表的加载中
const list = ref<BriefWorkVO[]>([]) // 列表的数据
const total = ref(0) // 列表的总页数
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
briefType: 2,
briefCount: undefined,
briefStartDate: [],
briefEndDate: [],
selectCustomer: undefined,
workComplate: undefined,
workPlan: undefined,
workAssistance: undefined,
workExperience: undefined,
attachment: undefined,
processInstanceId: undefined,
auditStatus: undefined,
creator: undefined,
createTime: [],
updater: undefined,
updateTime: []
})
const queryFormRef = ref() // 搜索的表单
const exportLoading = ref(false) // 导出的加载中
/** 查询列表 */
const getList = async () => {
loading.value = true
try {
const data = await BriefWorkApi.getBriefWorkPage(queryParams)
list.value = data.list
total.value = data.total
} finally {
loading.value = false
}
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.pageNo = 1
getList()
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value.resetFields()
handleQuery()
}
/** 添加/修改操作 */
const formRef = ref()
const { push } = useRouter()
const router = useRouter() // 路
//新增
const openFormAdd = () => {
push({ name: 'WeekReportAdd' })
}
//详情
const openFormDetail = (row: Object) => {
router.push({ name: 'WeekReportDetail', query: { id: row.id} })
}
/** 查看审批 */
const handleProcessDetail = (row) => {
router.push({ name: 'BpmProcessInstanceDetail', query: { id: row.processInstanceId } })
}
/** 删除按钮操作 */
const handleDelete = async (id: number) => {
try {
// 删除的二次确认
await message.delConfirm()
// 发起删除
await BriefWorkApi.deleteBriefWork(id)
message.success(t('common.delSuccess'))
// 刷新列表
await getList()
} catch {}
}
/** 导出按钮操作 */
const handleExport = async () => {
try {
// 导出的二次确认
await message.exportConfirm()
// 发起导出
exportLoading.value = true
const data = await BriefWorkApi.exportBriefWork(queryParams)
download.excel(data, '周工作简报.xls')
} catch {
} finally {
exportLoading.value = false
}
}
//预览
const previewFile = (fileUrls) => {
// // 假设 fileUrls 是一个字符串,包含文件的完整 URL
if (!fileUrls) {
message.error('没有附件');
return;
}
// 检查文件类型
const fileExtension = fileUrls.split('.').pop().toLowerCase();
router.push({
name: 'FileTemplatePreview',
query: {
src: encodeURIComponent(fileUrls),
type: fileExtension
}
})
}
const getName = (opt, val, id, name) => {
const arr = opt.filter(v => v[id] == val)
return arr.length ? arr[0][name] : ''
}
const route = useRoute();
onMounted(async () => {
// 获得用户列表
userOptions.value = await UserApi.getSimpleUserList()
getList()
})
onActivated(()=>{
getList()
})
</script>

View File

@ -0,0 +1,164 @@
<template>
<ContentWrap>
<el-form
ref="formRef"
:model="formData"
:rules="formRules"
label-width="120px"
v-loading="formLoading"
>
<el-row>
<el-col :span="8">
<el-form-item label="简报日期" prop="briefStartDate">
<el-date-picker
disabled
v-model="formData.briefStartDate"
type="date"
value-format="YYYY-MM-DD"
placeholder="选择简报日期"
/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="本日完成工作" prop="workComplate">
<el-input type="textarea" disabled :rows="5" v-model="formData.workComplate" placeholder="请输入本日完成工作" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="明日工作计划" prop="workPlan">
<el-input type="textarea" disabled :rows="5" v-model="formData.workPlan" placeholder="请输入明日工作计划" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="工作困难及协助" prop="workAssistance">
<el-input type="textarea" disabled :rows="5" v-model="formData.workAssistance" placeholder="请输入工作困难及协助" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="附件" prop="attachment">
<UploadFile v-model="formData.attachment" :disabled="!!formType" :limit="1" :fileType="['doc','docx']"/>
</el-form-item>
</el-col>
</el-row>
</el-form>
</ContentWrap>
</template>
<script setup lang="ts">
import { getIntDictOptions, getBoolDictOptions, DICT_TYPE } from '@/utils/dict'
import { BriefWorkApi, BriefWorkVO } from '@/api/task/briefWork'
import { useUserStore } from '@/store/modules/user'
import { propTypes } from '@/utils/propTypes'
/** 工作简报 表单 */
defineOptions({ name: 'BriefWorkForm' })
const { proxy }: any = getCurrentInstance();
const { t } = useI18n() // 国际化
const message = useMessage() // 消息弹窗
const props = defineProps({
id: propTypes.number.def(undefined),
})
const dialogTitle = ref('') // 弹窗的标题
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
const formType = ref('') // 表单的类型:create - 新增;update - 修改
const formData = ref({
id: undefined,
briefType: 1,
selectCustomer: false,
briefCount: undefined,
briefStartDate: undefined,
briefEndDate: undefined,
workComplate: undefined,
workPlan: undefined,
workAssistance: undefined,
workExperience: undefined,
attachment: undefined,
processInstanceId: undefined,
auditStatus: undefined
})
const formRules = reactive({
briefStartDate: [{ required: true, message: '简报日期不能为空', trigger: 'blur' }],
workComplate: [{ required: true, message: '本日期完成工作不能为空', trigger: 'blur' }],
workPlan: [{ required: true, message: '明日工作计划不能为空', trigger: 'blur' }]
})
const formRef = ref() // 表单 Ref
/** 打开弹窗 */
const open = async (id?: number) => {
resetForm()
// 修改时,设置数据
if (id) {
formLoading.value = true
try {
formData.value = await BriefWorkApi.getBriefWork({id: id, briefType: 1})
} finally {
formLoading.value = false
}
}
}
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
/** 提交表单 */
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
const submitForm = async () => {
// 校验表单
await formRef.value.validate()
// 提交请求
formLoading.value = true
formData.value.briefEndDate = formData.value.briefStartDate
try {
const data = formData.value as unknown as BriefWorkVO
if (!formType.value) {
await BriefWorkApi.createBriefWork(data)
message.success(t('common.createSuccess'))
} else {
await BriefWorkApi.updateBriefWork(data)
message.success(t('common.updateSuccess'))
}
// 发送操作成功的事件
emit('success')
goBack()
} finally {
formLoading.value = false
}
}
const goBack = ()=> {
proxy.$router.go(-1)
}
/** 重置表单 */
const resetForm = () => {
formData.value = {
id: undefined,
briefType: 1,
briefCount: undefined,
briefStartDate: undefined,
briefEndDate: undefined,
selectCustomer: false,
workComplate: undefined,
workPlan: undefined,
workAssistance: undefined,
workExperience: undefined,
attachment: undefined,
processInstanceId: undefined,
auditStatus: undefined
}
formRef.value?.resetFields()
}
const route = useRoute();
onMounted(async () => {
formType.value = props.id || route.query.id
if (formType.value) await open(formType.value)
if (!formType.value) {
formData.value.userId = useUserStore().getUser.id
}
})
</script>

View File

@ -0,0 +1,154 @@
<template>
<ContentWrap>
<el-form
ref="formRef"
:model="formData"
:rules="formRules"
label-width="120px"
v-loading="formLoading"
>
<el-row>
<el-col :span="8">
<el-form-item label="简报日期" prop="briefStartDate">
<el-date-picker
v-model="formData.briefStartDate"
type="date"
value-format="YYYY-MM-DD"
placeholder="选择简报日期"
/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="本日完成工作" prop="workComplate">
<el-input type="textarea" :rows="5" v-model="formData.workComplate" placeholder="请输入本日完成工作" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="明日工作计划" prop="workPlan">
<el-input type="textarea" :rows="5" v-model="formData.workPlan" placeholder="请输入明日工作计划" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="工作困难及协助" prop="workAssistance">
<el-input type="textarea" :rows="5" v-model="formData.workAssistance" placeholder="请输入工作困难及协助" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="附件" prop="attachment">
<UploadFile v-model="formData.attachment" :disabled="!!formType" :limit="1" :fileType="['doc','docx']"/>
</el-form-item>
</el-col>
</el-row>
</el-form>
<div style="text-align: right">
<el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
<el-button @click="goBack = false">取 消</el-button>
</div>
</ContentWrap>
</template>
<script setup lang="ts">
import { getIntDictOptions, getBoolDictOptions, DICT_TYPE } from '@/utils/dict'
import { BriefWorkApi, BriefWorkVO } from '@/api/task/briefWork'
import { useUserStore } from '@/store/modules/user'
import { propTypes } from '@/utils/propTypes'
/** 工作简报 表单 */
defineOptions({ name: 'BriefWorkForm' })
const { proxy }: any = getCurrentInstance();
const props = defineProps({
id: propTypes.number.def(undefined),
type: propTypes.number.def(undefined),
})
const { t } = useI18n() // 国际化
const message = useMessage() // 消息弹窗
const dialogTitle = ref('') // 弹窗的标题
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
const formType = ref('') // 表单的类型:create - 新增;update - 修改
const formData = ref({
id: undefined,
briefType: 1,
selectCustomer: false,
briefCount: undefined,
briefStartDate: undefined,
briefEndDate: undefined,
workComplate: undefined,
workPlan: undefined,
workAssistance: undefined,
workExperience: undefined,
attachment: undefined,
processInstanceId: undefined,
auditStatus: undefined
})
const formRules = reactive({
briefStartDate: [{ required: true, message: '简报日期不能为空', trigger: 'blur' }],
workComplate: [{ required: true, message: '本日期完成工作不能为空', trigger: 'blur' }],
workPlan: [{ required: true, message: '明日工作计划不能为空', trigger: 'blur' }]
})
const formRef = ref() // 表单 Ref
/** 提交表单 */
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
const submitForm = async () => {
// 校验表单
await formRef.value.validate()
// 提交请求
formLoading.value = true
formData.value.briefEndDate = formData.value.briefStartDate
try {
const data = formData.value as unknown as BriefWorkVO
if (!formType.value) {
await BriefWorkApi.createBriefWork(data)
message.success(t('common.createSuccess'))
} else {
await BriefWorkApi.updateBriefWork(data)
message.success(t('common.updateSuccess'))
}
// 发送操作成功的事件
emit('success')
goBack()
} finally {
formLoading.value = false
}
}
const goBack = ()=> {
proxy.$router.go(-1)
}
/** 重置表单 */
const resetForm = () => {
formData.value = {
id: undefined,
briefType: 1,
briefCount: undefined,
briefStartDate: undefined,
briefEndDate: undefined,
selectCustomer: false,
workComplate: undefined,
workPlan: undefined,
workAssistance: undefined,
workExperience: undefined,
attachment: undefined,
processInstanceId: undefined,
auditStatus: undefined
}
formRef.value?.resetFields()
}
const route = useRoute();
onMounted(async () => {
formType.value = props.id || route.query.id
if (formType.value) await open(formType.value)
if (!formType.value) {
formData.value.userId = useUserStore().getUser.id
}
})
</script>

View File

@ -0,0 +1,281 @@
<template>
<ContentWrap>
<!-- 搜索工作栏 -->
<el-form
class="-mb-15px"
:model="queryParams"
ref="queryFormRef"
:inline="true"
label-width="68px"
>
<!-- <el-form-item label="审批状态" prop="auditStatus">
<el-select
v-model="queryParams.auditStatus"
placeholder="请选择审批状态"
clearable
class="!w-240px"
>
<el-option label="请选择字典生成" value="" />
</el-select>
</el-form-item> -->
<el-form-item label="填报人员" prop="creator">
<el-select v-model="queryParams.creator" placeholder="请选择申请人" class="!w-240px">
<el-option
v-for="dict in userOptions"
:key="dict.id"
:label="dict.nickname"
:value="dict.id"
/>
</el-select>
</el-form-item>
<el-form-item label="简报日期" prop="createTime">
<el-date-picker
v-model="queryParams.createTime"
value-format="YYYY-MM-DD HH:mm:ss"
type="daterange"
start-placeholder="开始日期"
end-placeholder="结束日期"
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
class="!w-220px"
/>
</el-form-item>
<el-form-item>
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
<el-button
type="primary"
plain
@click="openFormAdd('create')"
v-hasPermi="['tts:brief-work:create']"
>
<Icon icon="ep:plus" class="mr-5px" /> 新增
</el-button>
<el-button
type="success"
plain
@click="handleExport"
:loading="exportLoading"
v-hasPermi="['tts:brief-work:export']"
>
<Icon icon="ep:download" class="mr-5px" /> 导出
</el-button>
</el-form-item>
</el-form>
</ContentWrap>
<!-- 列表 -->
<ContentWrap>
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
<el-table-column
label="简报日期"
align="center"
prop="createTime"
:formatter="dateFormatter"
width="180px"
/>
<el-table-column label="填报人员" align="center" prop="creator">
<template #default="scope">
{{getName(userOptions, scope.row.creator, 'id', 'nickname')}}
</template>
</el-table-column>
<el-table-column label="今日完成工作" align="center" prop="workComplate" />
<el-table-column label="明日工作计划" align="center" prop="workPlan" />
<el-table-column label="工作困难及协助" align="center" prop="workAssistance" />
<el-table-column label="上传附件" align="center" prop="attachment" >
<template #default="scope">
<el-link
v-if="scope.row.attachment"
:underline="false"
type="primary"
@click="previewFile(scope.row.attachment)"
>
预览
</el-link>
<span v-else>无附件</span>
</template>
</el-table-column>
<el-table-column label="审批状态" align="center" prop="auditStatus" width="110">
<template #default="scope">
<dict-tag :type="DICT_TYPE.TTS_APPLY_STATUS" :value="scope.row.auditStatus" />
</template>
</el-table-column>
<el-table-column label="操作" align="center" min-width="120px">
<template #default="scope">
<el-button
v-if="scope.row.auditStatus !== 0"
link
type="primary"
@click="handleProcessDetail(scope.row)"
>
进度
</el-button>
<el-button
link
type="primary"
@click="openFormDetail(scope.row)"
>
详情
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<Pagination
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
</ContentWrap>
<!-- 表单弹窗:添加/修改 -->
<!-- <BriefWorkForm ref="formRef" @success="getList" /> -->
</template>
<script setup lang="ts">
import { getIntDictOptions, getBoolDictOptions, DICT_TYPE } from '@/utils/dict'
import { dateFormatter } from '@/utils/formatTime'
import download from '@/utils/download'
import { BriefWorkApi, BriefWorkVO } from '@/api/task/briefWork'
// import BriefWorkForm from './BriefWorkForm.vue'
import * as UserApi from '@/api/system/user'
/** 工作简报 列表 */
defineOptions({ name: 'BriefWork' })
const message = useMessage() // 消息弹窗
const { t } = useI18n() // 国际化
const userOptions = ref<UserApi.UserVO[]>([]) // 用户列表
const loading = ref(true) // 列表的加载中
const list = ref<BriefWorkVO[]>([]) // 列表的数据
const total = ref(0) // 列表的总页数
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
briefType: 1,
briefCount: undefined,
briefStartDate: [],
briefEndDate: [],
selectCustomer: undefined,
workComplate: undefined,
workPlan: undefined,
workAssistance: undefined,
workExperience: undefined,
attachment: undefined,
processInstanceId: undefined,
auditStatus: undefined,
creator: undefined,
createTime: [],
updater: undefined,
updateTime: []
})
const queryFormRef = ref() // 搜索的表单
const exportLoading = ref(false) // 导出的加载中
/** 查询列表 */
const getList = async () => {
loading.value = true
try {
const data = await BriefWorkApi.getBriefWorkPage(queryParams)
list.value = data.list
total.value = data.total
} finally {
loading.value = false
}
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.pageNo = 1
getList()
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value.resetFields()
handleQuery()
}
/** 添加/修改操作 */
const formRef = ref()
const { push } = useRouter()
const router = useRouter() // 路
//新增
const openFormAdd = () => {
push({ name: 'DayReportAdd' })
}
//详情
const openFormDetail = (row: Object) => {
router.push({ name: 'DayReportDetail', query: { id: row.id} })
}
/** 查看审批 */
const handleProcessDetail = (row) => {
router.push({ name: 'BpmProcessInstanceDetail', query: { id: row.processInstanceId } })
}
/** 删除按钮操作 */
const handleDelete = async (id: number) => {
try {
// 删除的二次确认
await message.delConfirm()
// 发起删除
await BriefWorkApi.deleteBriefWork(id)
message.success(t('common.delSuccess'))
// 刷新列表
await getList()
} catch {}
}
/** 导出按钮操作 */
const handleExport = async () => {
try {
// 导出的二次确认
await message.exportConfirm()
// 发起导出
exportLoading.value = true
const data = await BriefWorkApi.exportBriefWork(queryParams)
download.excel(data, '工作简报.xls')
} catch {
} finally {
exportLoading.value = false
}
}
//预览
const previewFile = (fileUrls) => {
// // 假设 fileUrls 是一个字符串,包含文件的完整 URL
if (!fileUrls) {
message.error('没有附件');
return;
}
// 检查文件类型
const fileExtension = fileUrls.split('.').pop().toLowerCase();
router.push({
name: 'FileTemplatePreview',
query: {
src: encodeURIComponent(fileUrls),
type: fileExtension
}
})
};
const getName = (opt, val, id, name) => {
const arr = opt.filter(v => v[id] == val)
return arr.length ? arr[0][name] : ''
}
/** 初始化 **/
onMounted(async() => {
// 获得用户列表
userOptions.value = await UserApi.getSimpleUserList()
getList()
})
onActivated(()=>{
getList()
})
</script>

View File

@ -0,0 +1,74 @@
<template>
<div class="signature-container">
<h5>请在下方框内签名</h5>
<div class="signature-pad-container">
<VueSignaturePad
width="300px"
height="150px"
ref="signaturePad"
/>
</div>
<div class="signature-actions">
<el-button type="button" @click="close">取消</el-button>
<el-button type="button" @click="clear">清除</el-button>
<el-button type="button" @click="save">保存签名</el-button>
</div>
<!-- <div v-if="signatureUrl" class="signature-preview">
<el-image :src="signatureUrl" alt="签名预览" />
</div> -->
</div>
</template>
<script setup>
import { ref } from 'vue';
const signaturePad = ref(null);
const signatureUrl = ref('');
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
const save = () => {
const { isEmpty, data } = signaturePad.value.saveSignature();
if (!isEmpty) {
console.log(data); // 输出签名的 Base64 数据
// signatureUrl.value = data;
emit('success', data)
} else {
alert('请先签名');
}
};
const close = () => {
emit('close')
}
const clear = () => {
signaturePad.value.clearSignature();
signatureUrl.value = '';
};
</script>
<style scoped>
.signature-container {
max-width: 800px;
margin: 0 auto;
}
.signature-pad-container {
border: 1px solid #dcdfe6;
border-radius: 4px;
margin-bottom: 20px;
}
.signature-actions {
display: flex;
justify-content: flex-end;
gap: 10px;
margin-bottom: 20px;
}
.signature-preview {
margin-top: 20px;
text-align: center;
}
</style>

View File

@ -0,0 +1,277 @@
<template>
<ContentWrap>
<el-form
ref="formRef"
:model="formData"
:rules="formRules"
label-width="130px"
v-loading="formLoading"
>
<el-row>
<el-col :span="8">
<el-form-item label="名称" prop="projectName">
<el-input v-model="formData.projectName" disabled placeholder="请输入名称" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="项目类型" prop="projectType">
<el-select v-model="formData.projectType" disabled placeholder="请选择项目类型">
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.TTS_PROJECT_TYPE)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="申请部门" prop="deptId">
<el-tree-select
disabled
v-model="formData.deptId"
:data="deptTree"
:props="defaultProps"
check-strictly
node-key="id"
placeholder=""
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="申请人" prop="userId">
<el-select v-model="formData.userId" disabled placeholder="请选择申请人">
<el-option
v-for="dict in userOptions"
:key="dict.id"
:label="dict.nickname"
:value="dict.id"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="测试完成日期" prop="testCompletionDate">
<el-date-picker
disabled
v-model="formData.testCompletionDate"
style="width: 100%"
type="date"
value-format="YYYY-MM-DD"
placeholder="选择测试完成日期"
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="申请上线日期" prop="onlineDate">
<el-date-picker
v-model="formData.onlineDate"
style="width: 100%"
disabled
type="date"
value-format="YYYY-MM-DD"
placeholder="选择申请上线日期"
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="涉及第三方系统" prop="externalSystem">
<el-radio-group v-model="formData.externalSystem" disabled>
<el-radio :label="true">是</el-radio>
<el-radio :label="false">否</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="24">
<OnlineApplicationList ref="onlineApplicationFormRef" :type="1" :onlineModuleInfo="formData.onlineModuleInfo" />
</el-col>
<el-col :span="24">
<el-form-item label="上线注意事项" prop="precautions">
<el-input type="textarea" disabled :rows="5" v-model="formData.precautions" placeholder="请输入上线注意事项" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="失败应急处理" prop="emergencyManagement">
<el-input type="textarea" disabled :rows="5" v-model="formData.emergencyManagement" placeholder="请输入失败应急处理" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="申请人签字" prop="applicantSign">
<div class="signature-preview">
<el-image class="image" v-if="formData.applicantSign" :src="formData.applicantSign" alt="签名预览" />
</div>
</el-form-item>
</el-col>
</el-row>
<el-form-item v-if="visible" >
<div>
<HandleSign @success="setSign" @close="visible = false" />
</div>
</el-form-item>
<!-- <el-form-item label="工作流编号" prop="processInstanceId">
<el-input v-model="formData.processInstanceId" placeholder="请输入工作流编号" />
</el-form-item>
<el-form-item label="审批状态" prop="auditStatus">
<el-radio-group v-model="formData.auditStatus">
<el-radio value="1">请选择字典生成</el-radio>
</el-radio-group>
</el-form-item> -->
</el-form>
</ContentWrap>
</template>
<script setup lang="ts">
import { getIntDictOptions, DICT_TYPE } from '@/utils/dict'
import { OnlineApplicationApi, OnlineApplicationVO } from '@/api/task/online'
import HandleSign from './HandleSign.vue'
import OnlineApplicationList from './components/index.vue'
import { defaultProps, handleTree } from '@/utils/tree'
import * as DeptApi from '@/api/system/dept'
import { propTypes } from '@/utils/propTypes'
import * as UserApi from '@/api/system/user'
/** 上线申请 表单 */
defineOptions({ name: 'OnlineApplicationForm' })
const { proxy }: any = getCurrentInstance();
const userOptions = ref<UserApi.UserVO[]>([]) // 用户列表
const { t } = useI18n() // 国际化
const message = useMessage() // 消息弹窗
const deptTree = ref() // 部门树形结构
const props = defineProps({
id: propTypes.number.def(undefined),
type: propTypes.number.def(undefined),
})
const dialogVisible = ref(false) // 弹窗的是否展示
const dialogTitle = ref('') // 弹窗的标题
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
const formType = ref('') // 表单的类型:create - 新增;update - 修改
const visible = ref(false)
const onlineApplicationFormRef = ref('')
const formData = ref({
id: undefined,
projectName: undefined,
projectType: undefined,
deptId: undefined,
userId: undefined,
testCompletionDate: undefined,
onlineDate: undefined,
externalSystem: undefined,
precautions: undefined,
emergencyManagement: undefined,
applicantSign: undefined,
processInstanceId: undefined,
auditStatus: undefined
})
const formRules = reactive({
projectName: [{ required: true, message: '产品名称不能为空', trigger: 'blur' }],
projectType: [{ required: true, message: '项目类型不能为空', trigger: 'change' }],
deptId: [{ required: true, message: '申请部门不能为空', trigger: 'blur' }],
userId: [{ required: true, message: '申请人不能为空', trigger: 'blur' }],
testCompletionDate: [{ required: true, message: '测试完成日期不能为空', trigger: 'blur' }],
onlineDate: [{ required: true, message: '申请上线日期不能为空', trigger: 'blur' }],
externalSystem: [{ required: true, message: '上线系统是否涉及第三方系统不能为空', trigger: 'blur' }],
precautions: [{ required: true, message: '上线注意事项不能为空', trigger: 'blur' }],
emergencyManagement: [{ required: true, message: '失败应急处理不能为空', trigger: 'blur' }],
applicantSign: [{ required: true, message: '申请人签字不能为空', trigger: 'blur' }]
})
const formRef = ref() // 表单 Ref
/** 打开弹窗 */
const open = async (id?: number) => {
dialogVisible.value = true
resetForm()
// 修改时,设置数据
if (id) {
formLoading.value = true
try {
formData.value = await OnlineApplicationApi.getOnlineApplication(id)
} finally {
formLoading.value = false
}
}
}
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
const setSign = (val) => {
console.log('%csrc/views/task/online/OnlineApplicationForm.vue:143 val', 'color: #007acc;', val);
formData.value.applicantSign = val
visible.value = false
}
/** 提交表单 */
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
const submitForm = async () => {
// 校验表单
await formRef.value.validate()
// 提交请求
formLoading.value = true
try {
const data = formData.value as unknown as OnlineApplicationVO
// await onlineApplicationFormRef.value.validate()
console.log('%csrc/views/task/online/OnlineApplicationForm.vue:214 onlineApplicationFormRef.value.getData()', 'color: #007acc;', onlineApplicationFormRef.value.getData());
data.onlineModuleInfo = onlineApplicationFormRef.value.getData()
if (!formType.value) {
await OnlineApplicationApi.createOnlineApplication(data)
message.success(t('common.createSuccess'))
} else {
await OnlineApplicationApi.updateOnlineApplication(data)
message.success(t('common.updateSuccess'))
}
dialogVisible.value = false
goBack()
// 发送操作成功的事件
emit('success')
} finally {
formLoading.value = false
}
}
const goBack = ()=> {
proxy.$router.go(-1)
}
/** 重置表单 */
const resetForm = () => {
formData.value = {
id: undefined,
projectName: undefined,
projectType: undefined,
deptId: undefined,
userId: undefined,
testCompletionDate: undefined,
onlineDate: undefined,
externalSystem: undefined,
precautions: undefined,
emergencyManagement: undefined,
applicantSign: undefined,
processInstanceId: undefined,
auditStatus: undefined
}
formRef.value?.resetFields()
}
const route = useRoute();
onMounted(async()=> {
formType.value = props.id || route.query.id
if (formType.value) await open(formType.value)
deptTree.value = handleTree(await DeptApi.getSimpleDeptList())
// 获得用户列表
userOptions.value = await UserApi.getSimpleUserList()
})
</script>
<style scoped>
.signature-preview {
display: flex;
flex-direction: column;
}
.image {
width: 200px;
margin-top: 5px;
}
</style>

View File

@ -0,0 +1,279 @@
<template>
<ContentWrap>
<el-form
ref="formRef"
:model="formData"
:rules="formRules"
label-width="130px"
v-loading="formLoading"
>
<el-row>
<el-col :span="8">
<el-form-item label="名称" prop="projectName">
<el-input v-model="formData.projectName" placeholder="请输入名称" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="项目类型" prop="projectType">
<el-select v-model="formData.projectType" placeholder="请选择项目类型">
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.TTS_PROJECT_TYPE)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="申请部门" prop="deptId">
<el-tree-select
v-model="formData.deptId"
:data="deptTree"
:props="defaultProps"
check-strictly
node-key="id"
placeholder="请选择申请部门"
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="申请人" prop="userId">
<el-select v-model="formData.userId" placeholder="请选择申请人">
<el-option
v-for="dict in userOptions"
:key="dict.id"
:label="dict.nickname"
:value="dict.id"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="测试完成日期" prop="testCompletionDate">
<el-date-picker
v-model="formData.testCompletionDate"
style="width: 100%"
type="date"
value-format="YYYY-MM-DD"
placeholder="选择测试完成日期"
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="申请上线日期" prop="onlineDate">
<el-date-picker
v-model="formData.onlineDate"
style="width: 100%"
type="date"
value-format="YYYY-MM-DD"
placeholder="选择申请上线日期"
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="涉及第三方系统" prop="externalSystem">
<el-radio-group v-model="formData.externalSystem">
<el-radio :label="true">是</el-radio>
<el-radio :label="false">否</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="24">
<OnlineApplicationList ref="onlineApplicationFormRef" :onlineModuleInfo="formData.onlineModuleInfo" />
</el-col>
<el-col :span="24">
<el-form-item label="上线注意事项" prop="precautions">
<el-input type="textarea" :rows="5" v-model="formData.precautions" placeholder="请输入上线注意事项" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="失败应急处理" prop="emergencyManagement">
<el-input type="textarea" :rows="5" v-model="formData.emergencyManagement" placeholder="请输入失败应急处理" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="申请人签字" prop="applicantSign">
<div class="signature-preview">
<el-button @click="visible = true">{{formData.applicantSign ? '重新签名' : '点击签名'}}</el-button>
<el-image class="image" v-if="formData.applicantSign" :src="formData.applicantSign" alt="签名预览" />
</div>
</el-form-item>
</el-col>
</el-row>
<el-form-item v-if="visible" >
<div>
<HandleSign @success="setSign" @close="visible = false" />
</div>
</el-form-item>
<!-- <el-form-item label="工作流编号" prop="processInstanceId">
<el-input v-model="formData.processInstanceId" placeholder="请输入工作流编号" />
</el-form-item>
<el-form-item label="审批状态" prop="auditStatus">
<el-radio-group v-model="formData.auditStatus">
<el-radio value="1">请选择字典生成</el-radio>
</el-radio-group>
</el-form-item> -->
</el-form>
<div style="text-align: right">
<el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
<el-button @click="goBack">取 消</el-button>
</div>
</ContentWrap>
</template>
<script setup lang="ts">
import { getIntDictOptions, DICT_TYPE } from '@/utils/dict'
import { OnlineApplicationApi, OnlineApplicationVO } from '@/api/task/online'
import HandleSign from './HandleSign.vue'
import OnlineApplicationList from './components/index.vue'
import { defaultProps, handleTree } from '@/utils/tree'
import * as DeptApi from '@/api/system/dept'
import { propTypes } from '@/utils/propTypes'
import * as UserApi from '@/api/system/user'
/** 上线申请 表单 */
defineOptions({ name: 'OnlineApplicationForm' })
const { proxy }: any = getCurrentInstance();
const userOptions = ref<UserApi.UserVO[]>([]) // 用户列表
const { t } = useI18n() // 国际化
const message = useMessage() // 消息弹窗
const deptTree = ref() // 部门树形结构
const props = defineProps({
id: propTypes.number.def(undefined),
type: propTypes.number.def(undefined),
})
const dialogVisible = ref(false) // 弹窗的是否展示
const dialogTitle = ref('') // 弹窗的标题
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
const formType = ref('') // 表单的类型:create - 新增;update - 修改
const visible = ref(false)
const onlineApplicationFormRef = ref('')
const formData = ref({
id: undefined,
projectName: undefined,
projectType: undefined,
deptId: undefined,
userId: undefined,
testCompletionDate: undefined,
onlineDate: undefined,
externalSystem: undefined,
precautions: undefined,
emergencyManagement: undefined,
applicantSign: undefined,
processInstanceId: undefined,
auditStatus: undefined
})
const formRules = reactive({
projectName: [{ required: true, message: '名称不能为空', trigger: 'blur' }],
projectType: [{ required: true, message: '项目类型不能为空', trigger: 'change' }],
deptId: [{ required: true, message: '申请部门不能为空', trigger: 'blur' }],
userId: [{ required: true, message: '申请人不能为空', trigger: 'blur' }],
testCompletionDate: [{ required: true, message: '测试完成日期不能为空', trigger: 'blur' }],
onlineDate: [{ required: true, message: '申请上线日期不能为空', trigger: 'blur' }],
externalSystem: [{ required: true, message: '上线系统是否涉及第三方系统不能为空', trigger: 'blur' }],
precautions: [{ required: true, message: '上线注意事项不能为空', trigger: 'blur' }],
emergencyManagement: [{ required: true, message: '失败应急处理不能为空', trigger: 'blur' }],
applicantSign: [{ required: true, message: '申请人签字不能为空', trigger: 'blur' }]
})
const formRef = ref() // 表单 Ref
/** 打开弹窗 */
const open = async (id?: number) => {
dialogVisible.value = true
resetForm()
// 修改时,设置数据
if (id) {
formLoading.value = true
try {
formData.value = await OnlineApplicationApi.getOnlineApplication(id)
} finally {
formLoading.value = false
}
}
}
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
const setSign = (val) => {
console.log('%csrc/views/task/online/OnlineApplicationForm.vue:143 val', 'color: #007acc;', val);
formData.value.applicantSign = val
visible.value = false
}
/** 提交表单 */
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
const submitForm = async () => {
// 校验表单
await formRef.value.validate()
// 提交请求
formLoading.value = true
try {
const data = formData.value as unknown as OnlineApplicationVO
// await onlineApplicationFormRef.value.validate()
console.log('%csrc/views/task/online/OnlineApplicationForm.vue:214 onlineApplicationFormRef.value.getData()', 'color: #007acc;', onlineApplicationFormRef.value.getData());
data.onlineModuleInfo = onlineApplicationFormRef.value.getData()
if (!formType.value) {
await OnlineApplicationApi.createOnlineApplication(data)
message.success(t('common.createSuccess'))
} else {
await OnlineApplicationApi.updateOnlineApplication(data)
message.success(t('common.updateSuccess'))
}
dialogVisible.value = false
goBack()
// 发送操作成功的事件
emit('success')
} finally {
formLoading.value = false
}
}
const goBack = ()=> {
proxy.$router.go(-1)
}
/** 重置表单 */
const resetForm = () => {
formData.value = {
id: undefined,
projectName: undefined,
projectType: undefined,
deptId: undefined,
userId: undefined,
testCompletionDate: undefined,
onlineDate: undefined,
externalSystem: undefined,
precautions: undefined,
emergencyManagement: undefined,
applicantSign: undefined,
processInstanceId: undefined,
auditStatus: undefined
}
formRef.value?.resetFields()
}
const route = useRoute();
onMounted(async()=> {
formType.value = props.id || route.query.id
if (formType.value) await open(formType.value)
deptTree.value = handleTree(await DeptApi.getSimpleDeptList())
// 获得用户列表
userOptions.value = await UserApi.getSimpleUserList()
})
</script>
<style scoped>
.signature-preview {
display: flex;
flex-direction: column;
}
.image {
width: 200px;
margin-top: 5px;
}
</style>

View File

@ -0,0 +1,110 @@
<template>
<Dialog :title="dialogTitle" v-model="dialogVisible">
<el-form
ref="formRef"
:model="formData"
:rules="formRules"
label-width="100px"
v-loading="formLoading"
>
<el-form-item label="上线申请ID" prop="onlineApplicationId">
<el-input v-model="formData.onlineApplicationId" placeholder="请输入上线申请ID" />
</el-form-item>
<el-form-item label="涉及模块" prop="involvingModule">
<el-input v-model="formData.involvingModule" placeholder="请输入涉及模块" />
</el-form-item>
<el-form-item label="版本" prop="version">
<el-input v-model="formData.version" placeholder="请输入版本" />
</el-form-item>
<el-form-item label="模块说明" prop="moduleDescription">
<Editor v-model="formData.moduleDescription" height="150px" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
<el-button @click="dialogVisible = false">取 消</el-button>
</template>
</Dialog>
</template>
<script setup lang="ts">
import { OnlineModuleInfoApi, OnlineModuleInfoVO } from '@/api/task/onlineModule'
/** 上线模块信息 表单 */
defineOptions({ name: 'OnlineModuleInfoForm' })
const { t } = useI18n() // 国际化
const message = useMessage() // 消息弹窗
const dialogVisible = ref(false) // 弹窗的是否展示
const dialogTitle = ref('') // 弹窗的标题
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
const formType = ref('') // 表单的类型:create - 新增;update - 修改
const formData = ref({
id: undefined,
onlineApplicationId: undefined,
involvingModule: undefined,
version: undefined,
moduleDescription: undefined
})
const formRules = reactive({
onlineApplicationId: [{ required: true, message: '上线申请ID不能为空', trigger: 'blur' }],
involvingModule: [{ required: true, message: '涉及模块不能为空', trigger: 'blur' }],
version: [{ required: true, message: '版本不能为空', trigger: 'blur' }],
moduleDescription: [{ required: true, message: '模块说明不能为空', trigger: 'blur' }]
})
const formRef = ref() // 表单 Ref
/** 打开弹窗 */
const open = async (type: string, id?: number) => {
dialogVisible.value = true
dialogTitle.value = t('action.' + type)
formType.value = type
resetForm()
// 修改时,设置数据
if (id) {
formLoading.value = true
try {
formData.value = await OnlineModuleInfoApi.getOnlineModuleInfo(id)
} finally {
formLoading.value = false
}
}
}
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
/** 提交表单 */
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
const submitForm = async () => {
// 校验表单
await formRef.value.validate()
// 提交请求
formLoading.value = true
try {
const data = formData.value as unknown as OnlineModuleInfoVO
if (formType.value === 'create') {
await OnlineModuleInfoApi.createOnlineModuleInfo(data)
message.success(t('common.createSuccess'))
} else {
await OnlineModuleInfoApi.updateOnlineModuleInfo(data)
message.success(t('common.updateSuccess'))
}
dialogVisible.value = false
// 发送操作成功的事件
emit('success')
} finally {
formLoading.value = false
}
}
/** 重置表单 */
const resetForm = () => {
formData.value = {
id: undefined,
onlineApplicationId: undefined,
involvingModule: undefined,
version: undefined,
moduleDescription: undefined
}
formRef.value?.resetFields()
}
</script>

View File

@ -0,0 +1,215 @@
<template>
<!-- 列表 -->
<ContentWrap>
<el-form
ref="contactRef"
:model="formData"
:rules="formRules"
v-loading="formLoading"
label-width="0px"
:inline-message="true"
>
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
<el-table-column label="涉及模块" align="center">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.involvingModule`" :rules="formRules.involvingModule" class="mb-0px!" v-if="!type">
<el-input v-model="row.involvingModule" placeholder="请输入涉及模块" />
</el-form-item>
<div v-else>
{{row.involvingModule}}
</div>
</template>
</el-table-column>
<el-table-column label="版本" align="center">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.version`" :rules="formRules.version" class="mb-0px!" v-if="!type">
<el-input v-model="row.version" placeholder="请输入版本" />
</el-form-item>
<div v-else>
{{row.version}}
</div>
</template>
</el-table-column>
<el-table-column label="模块说明" align="center">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.moduleDescription`" :rules="formRules.moduleDescription" class="mb-0px!" v-if="!type">
<el-input v-model="row.moduleDescription" placeholder="请输入模块说明" />
</el-form-item>
<div v-else>
{{row.moduleDescription}}
</div>
</template>
</el-table-column>
<el-table-column label="操作" align="center" width="120px" v-if="!type">
<template #default="scope">
<el-button
link
type="primary"
@click="openForm('update', scope.row.id)"
v-hasPermi="['tts:online-module-info:update']"
>
编辑
</el-button>
<el-button
link
type="danger"
@click="handleDelete(scope.row.id)"
v-hasPermi="['tts:online-module-info:delete']"
>
删除
</el-button>
</template>
</el-table-column>
</el-table>
</el-form>
<el-row justify="center" class="mt-3" v-if="!type">
<el-button @click="handleAdd" round>+ 添加</el-button>
</el-row>
<!-- 分页 -->
<!-- <Pagination
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/> -->
</ContentWrap>
<!-- 表单弹窗:添加/修改 -->
<OnlineModuleInfoForm ref="formRef" @success="getList" />
</template>
<script setup lang="ts">
import { dateFormatter } from '@/utils/formatTime'
import download from '@/utils/download'
import { OnlineModuleInfoApi, OnlineModuleInfoVO } from '@/api/task/onlineModule'
import OnlineModuleInfoForm from './OnlineModuleInfoForm.vue'
import { propTypes } from '@/utils/propTypes'
/** 上线模块信息 列表 */
defineOptions({ name: 'OnlineModuleInfo' })
const message = useMessage() // 消息弹窗
const { t } = useI18n() // 国际化
const props = defineProps({
onlineModuleInfo: [],
type: propTypes.number.def(undefined)
})
const loading = ref(false) // 列表的加载中
const list = ref<OnlineModuleInfoVO[]>([]) // 列表的数据
const total = ref(0) // 列表的总页数
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
onlineApplicationId: undefined,
involvingModule: undefined,
version: undefined,
moduleDescription: undefined,
createTime: []
})
const formRules = reactive({
involvingModule: [{ required: true, message: '涉及模块不能为空', trigger: 'blur' }],
version: [{ required: true, message: '版本不能为空', trigger: 'blur' }],
onlineApplicationId: [{ required: true, message: '模块说明不能为空', trigger: 'blur' }],
})
const queryFormRef = ref() // 搜索的表单
const exportLoading = ref(false) // 导出的加载中
watch(
() => props.onlineModuleInfo,
async (val) => {
if (!val || val.length === 0) {
return;
}
await nextTick(() => {
list.value = val
})
},
{ immediate: true },
);
/** 查询列表 */
const getList = async () => {
loading.value = true
try {
const data = await OnlineModuleInfoApi.getOnlineModuleInfoPage(queryParams)
list.value = data.list
total.value = data.total
} finally {
loading.value = false
}
}
const contactRef = ref() // 表单 Ref
/** 新增按钮操作 */
const handleAdd = () => {
const row = {
moduleDescription: undefined,
version: undefined,
involvingModule: undefined,
}
list.value.push(row)
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.pageNo = 1
getList()
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value.resetFields()
handleQuery()
}
/** 添加/修改操作 */
const formRef = ref()
const openForm = (type: string, id?: number) => {
formRef.value.open(type, id)
}
/** 删除按钮操作 */
const handleDelete = async (id: number) => {
try {
// 删除的二次确认
await message.delConfirm()
// 发起删除
await OnlineModuleInfoApi.deleteOnlineModuleInfo(id)
message.success(t('common.delSuccess'))
// 刷新列表
await getList()
} catch {}
}
/** 导出按钮操作 */
const handleExport = async () => {
try {
// 导出的二次确认
await message.exportConfirm()
// 发起导出
exportLoading.value = true
const data = await OnlineModuleInfoApi.exportOnlineModuleInfo(queryParams)
download.excel(data, '上线模块信息.xls')
} catch {
} finally {
exportLoading.value = false
}
}
/** 表单校验 */
const validate = () => {
return formRef.value.validate()
}
const getData = (data) => {
return list.value
}
defineExpose({ validate, getData })
/** 初始化 **/
onMounted(() => {
// getList()
})
</script>

View File

@ -0,0 +1,299 @@
<template>
<ContentWrap>
<!-- 搜索工作栏 -->
<el-form
class="-mb-15px"
:model="queryParams"
ref="queryFormRef"
:inline="true"
label-width="100px"
>
<el-form-item label="产品名称" prop="projectName">
<el-input
v-model="queryParams.projectName"
placeholder="请输入产品名称"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item>
<el-form-item label="申请部门" prop="deptId">
<el-tree-select
v-model="queryParams.deptId"
:data="deptTree"
:props="defaultProps"
check-strictly
class="!w-240px"
node-key="id"
placeholder="请选择申请部门"
/>
</el-form-item>
<el-form-item label="申请上线日期" prop="onlineDate">
<el-date-picker
v-model="queryParams.onlineDate"
value-format="YYYY-MM-DD"
type="daterange"
start-placeholder="开始日期"
end-placeholder="结束日期"
class="!w-240px"
/>
</el-form-item>
<el-form-item>
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
<el-button
type="primary"
plain
@click="openFormAdd('create')"
v-hasPermi="['tts:online-application:create']"
>
<Icon icon="ep:plus" class="mr-5px" /> 新增
</el-button>
<el-button
type="success"
plain
@click="handleExport"
:loading="exportLoading"
v-hasPermi="['tts:online-application:export']"
>
<Icon icon="ep:download" class="mr-5px" /> 导出
</el-button>
</el-form-item>
</el-form>
</ContentWrap>
<!-- 列表 -->
<ContentWrap>
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
<el-table-column label="唯一主键" align="center" prop="id" />
<el-table-column label="产品名称" align="center" prop="projectName" />
<!-- <el-table-column label="项目类型" align="center" prop="projectType">
<template #default="scope">
<dict-tag :type="DICT_TYPE.TTS_PROJECT_TYPE" :value="scope.row.projectType" />
</template>
</el-table-column> -->
<el-table-column label="申请人" align="center" prop="userId">
<template #default="scope">
{{getName(userOptions, scope.row.userId, 'id', 'nickname')}}
</template>
</el-table-column>
<el-table-column label="申请部门" align="center" prop="deptId">
<template #default="scope">
{{getName(deptTreeList, scope.row.deptId, 'id', 'name')}}
</template>
</el-table-column>
<el-table-column label="测试完成日期" align="center" prop="testCompletionDate" />
<el-table-column label="申请上线日期" align="center" prop="onlineDate" />
<el-table-column label="上线系统是否涉及第三方系统" align="center" prop="externalSystem" >
<template #default="scope">
{{scope.row.externalSystem ? '是' : '否'}}
</template>
</el-table-column>
<el-table-column label="审批状态" align="center" prop="auditStatus" width="110">
<template #default="scope">
<dict-tag :type="DICT_TYPE.TTS_APPLY_STATUS" :value="scope.row.auditStatus" />
</template>
</el-table-column>
<!-- <el-table-column label="上线注意事项" align="center" prop="precautions" />
<el-table-column label="失败应急处理" align="center" prop="emergencyManagement" />
<el-table-column label="申请人签字" align="center" prop="applicantSign" />
<el-table-column label="工作流编号" align="center" prop="processInstanceId" />
<el-table-column label="创建者" align="center" prop="creator" /> -->
<!-- <el-table-column
label="创建时间"
align="center"
prop="createTime"
:formatter="dateFormatter"
width="180px"
/>
<el-table-column label="更新者" align="center" prop="updater" />
<el-table-column
label="更新时间"
align="center"
prop="updateTime"
:formatter="dateFormatter"
width="180px"
/> -->
<el-table-column label="操作" fixed="right" align="center" min-width="120px">
<template #default="scope">
<!-- <el-button
link
type="primary"
@click="openFormEdit(scope.row)"
v-hasPermi="['tts:online-application:update']"
>
编辑
</el-button> -->
<el-button
v-if="scope.row.auditStatus !== 0"
link
type="primary"
@click="handleProcessDetail(scope.row)"
>
进度
</el-button>
<el-button
link
type="primary"
@click="openFormDetail(scope.row)"
>
详情
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<Pagination
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
</ContentWrap>
<!-- 表单弹窗:添加/修改 -->
<!-- <OnlineApplicationForm ref="formRef" @success="getList" /> -->
</template>
<script setup lang="ts">
import { getIntDictOptions, DICT_TYPE } from '@/utils/dict'
import { dateFormatter } from '@/utils/formatTime'
import download from '@/utils/download'
import { OnlineApplicationApi, OnlineApplicationVO } from '@/api/task/online'
import * as DeptApi from '@/api/system/dept'
import { defaultProps, handleTree } from '@/utils/tree'
import * as UserApi from '@/api/system/user'
/** 上线申请 列表 */
defineOptions({ name: 'OnlineApplication' })
const userOptions = ref<UserApi.UserVO[]>([]) // 用户列表
const message = useMessage() // 消息弹窗
const { t } = useI18n() // 国际化
const deptTree = ref() // 部门树形结构
const deptTreeList = ref() // 部门
const loading = ref(true) // 列表的加载中
const list = ref<OnlineApplicationVO[]>([]) // 列表的数据
const total = ref(0) // 列表的总页数
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
projectName: undefined,
projectType: undefined,
deptId: undefined,
userId: undefined,
testCompletionDate: [],
onlineDate: [],
externalSystem: undefined,
precautions: undefined,
emergencyManagement: undefined,
applicantSign: undefined,
processInstanceId: undefined,
auditStatus: undefined,
creator: undefined,
createTime: [],
updater: undefined,
updateTime: []
})
const queryFormRef = ref() // 搜索的表单
const exportLoading = ref(false) // 导出的加载中
/** 查询列表 */
const getList = async () => {
loading.value = true
try {
const data = await OnlineApplicationApi.getOnlineApplicationPage(queryParams)
list.value = data.list
total.value = data.total
} finally {
loading.value = false
}
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.pageNo = 1
getList()
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value.resetFields()
handleQuery()
}
/** 添加/修改操作 */
const { push } = useRouter()
const router = useRouter() // 路由
const formRef = ref()
//新增
const openFormAdd = () => {
push({ name: 'OnlineAppAdd' })
}
// 编辑
const openFormEdit = (row: Object) => {
router.push({ name: 'OnlineAppEdit', query: { id: row.id} })
}
const openFormDetail = (row: Object) => {
router.push({ name: 'OnlineAppDetail', query: { id: row.id} })
}
/** 查看审批 */
const handleProcessDetail = (row) => {
router.push({ name: 'BpmProcessInstanceDetail', query: { id: row.processInstanceId } })
}
/** 删除按钮操作 */
const handleDelete = async (id: number) => {
try {
// 删除的二次确认
await message.delConfirm()
// 发起删除
await OnlineApplicationApi.deleteOnlineApplication(id)
message.success(t('common.delSuccess'))
// 刷新列表
await getList()
} catch {}
}
/** 导出按钮操作 */
const handleExport = async () => {
try {
// 导出的二次确认
await message.exportConfirm()
// 发起导出
exportLoading.value = true
const data = await OnlineApplicationApi.exportOnlineApplication(queryParams)
download.excel(data, '上线申请.xls')
} catch {
} finally {
exportLoading.value = false
}
}
const getName = (opt, val, id, name) => {
const arr = opt.filter(v => v[id] == val)
return arr.length ? arr[0][name] : ''
}
/** 初始化 **/
onMounted(async() => {
deptTreeList.value = await DeptApi.getSimpleDeptList()
deptTree.value = handleTree(deptTreeList.value)
// 获得用户列表
userOptions.value = await UserApi.getSimpleUserList()
getList()
})
onActivated(()=>{
getList()
})
</script>

View File

@ -0,0 +1,367 @@
<template>
<ContentWrap>
<el-form
ref="formRef"
:model="formData"
:rules="formRules"
label-width="120px"
v-loading="formLoading"
>
<el-row>
<!-- <el-col :span="8">
<el-form-item label="项目编号" prop="projectCode">
<el-input v-model="formData.projectCode" placeholder="请输入项目编号" />
</el-form-item>
</el-col> -->
<el-col :span="8">
<el-form-item label="项目级别" prop="projectLevel">
<el-select v-model="formData.projectLevel" :disabled="formType" placeholder="请输入项目级别">
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.TTS_PROJECT_LEVEL)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="项目类型" prop="projectType">
<el-select v-model="formData.projectType" :disabled="formType" placeholder="请选择项目类型">
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.TTS_PROJECT_TYPE)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="项目名称" prop="name">
<el-input v-model="formData.name" :disabled="formType" placeholder="请输入项目名称" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="项目负责人" prop="userId">
<el-select
v-model="formData.userId"
:disabled="formType"
placeholder="请选择项目负责人"
filterable
>
<el-option
v-for="item in userOptions"
:key="item.id"
:label="item.nickname"
:value="item.id"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="项目开始日期" prop="startDate">
<el-date-picker
:disabled="formType"
v-model="formData.startDate"
style="width: 100%"
type="date"
value-format="YYYY-MM-DD"
placeholder="选择项目开始日期"
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="项目结束日期" prop="endDate">
<el-date-picker
:disabled="formType"
v-model="formData.endDate"
style="width: 100%"
type="date"
value-format="YYYY-MM-DD"
placeholder="选择项目结束日期"
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="归属部门" prop="deptId">
<el-tree-select
:disabled="formType"
v-model="formData.deptId"
:data="deptTree"
:props="defaultProps"
check-strictly
node-key="id"
placeholder="请选择归属部门"
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="立项奖金评估" prop="bonusEvaluation">
<el-radio-group v-model="formData.bonusEvaluation">
<el-switch
disabled
v-model="formData.bonusEvaluation"
class="ml-2"
/>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="8" v-if="formData.bonusEvaluation">
<el-form-item label="立项奖金金额" prop="bonusAmount">
<el-input v-model="formData.bonusAmount" disabled oninput="value=value.match(/^\d+(?:\.\d{0,2})?/)" placeholder="请输入奖金评估描述" />
</el-form-item>
</el-col>
<el-col :span="8" v-if="formData.bonusEvaluation">
<el-form-item label="项目进度(%)" prop="progress">
<el-input v-model="formData.progress" disabled oninput = "value=value.replace(/[^\d]/g,'')" placeholder="请输入奖金评估描述" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="项目附件" prop="attachment">
<UploadFile v-model="formData.attachment" :disabled="!!formType" :limit="1" :fileType="['doc','docx']"/>
</el-form-item>
</el-col>
</el-row>
<ContentWrap style="border: none;font-weight: 700" v-if="formData.auditStatus == 2">
项目信息
</ContentWrap>
<el-row>
<el-col :span="24">
<el-form-item label="项目背景" prop="background">
<el-input v-model="formData.background" type="textarea" :disabled="formType" :rows="5" placeholder="请输入项目背景" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="项目简介" prop="introduction">
<el-input v-model="formData.introduction" type="textarea" :disabled="formType" :rows="5" placeholder="请输入项目简介" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="项目预期" prop="expectation">
<el-input v-model="formData.expectation" type="textarea" :disabled="formType" :rows="5" placeholder="请输入项目预期" />
</el-form-item>
</el-col>
<!-- <el-col :span="8">
<el-form-item label="项目进度" prop="progress">
<el-input v-model="formData.progress" placeholder="请输入项目进度" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="工作流编号" prop="processInstanceId">
<el-input v-model="formData.processInstanceId" placeholder="请输入工作流编号" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="审批状态" prop="auditStatus">
<el-radio-group v-model="formData.auditStatus">
<el-radio value="1">请选择字典生成</el-radio>
</el-radio-group>
</el-form-item>
</el-col> -->
</el-row>
<ContentWrap style="border: none;font-weight: 700" v-if="formData.auditStatus == 2">
奖金信息
</ContentWrap>
<el-row v-if="formData.auditStatus == 2">
<el-col :span="8">
<el-form-item label="系数总和" prop="">
<el-input v-model="coefficient" disabled placeholder="系数总和" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="团队奖金" prop="">
<el-input v-model="teamAmount" disabled placeholder="团队奖金" />
</el-form-item>
</el-col>
</el-row>
<ContentWrap style="border: none;font-weight: 700" v-if="formData.auditStatus == 2">
团队成员
</ContentWrap>
<Bonus ref="formRef" v-if="formData.auditStatus == 2" :userOptions="userOptions" :type="formType" :id="formData.id" @success="getData" />
<ContentWrap style="border: none;font-weight: 700" v-if="formData.auditStatus == 2">
任务信息
</ContentWrap>
<Task ref="taskRef" v-if="formData.auditStatus == 2" :userOptions="userOptions" :type="formType" :id="formData.id" :userId="formData.userId" :name="formData.name" />
</el-form>
</ContentWrap>
</template>
<script setup lang="ts">
import { ProjectApi, ProjectVO } from '@/api/task/project'
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
import Bonus from './components/Bouns.vue'
import Task from './components/Task.vue'
import { defaultProps, handleTree } from '@/utils/tree'
import * as DeptApi from '@/api/system/dept'
import { propTypes } from '@/utils/propTypes'
import * as UserApi from '@/api/system/user'
import { useUserStore } from '@/store/modules/user'
const { proxy }: any = getCurrentInstance();
/** 项目管理 表单 */
defineOptions({ name: 'ProjectForm' })
const { t } = useI18n() // 国际化
const props = defineProps({
id: propTypes.number.def(undefined),
type: propTypes.number.def(undefined),
})
const userOptions = ref<UserApi.UserVO[]>([]) // 用户列表
const message = useMessage() // 消息弹窗
const deptTree = ref() // 部门树形结构
const routeId = ref('')
const teamAmount = ref('')
const coefficient = ref('')
const dialogVisible = ref(false) // 弹窗的是否展示
const dialogTitle = ref('') // 弹窗的标题
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
const formType = ref('') // 表单的类型:create - 新增;update - 修改
const validateProgress = (_: any, __: any, callback: any) => {
const num = Number(formData.value.progress)
if (num <= 0 || num >100) {
callback(new Error('请输入正确的项目进度'))
return
}
callback()
}
const formData = ref({
id: undefined,
projectCode: undefined,
name: undefined,
projectLevel: undefined,
projectType: undefined,
userId: undefined,
startDate: undefined,
endDate: undefined,
attachment: undefined,
bonusEvaluation: undefined,
bonusAmount: undefined,
bonusIssueDate: undefined,
background: undefined,
introduction: undefined,
expectation: undefined,
progress: undefined,
processInstanceId: undefined,
auditStatus: undefined
})
const formRules = reactive({
name: [{ required: true, message: '项目名称不能为空', trigger: 'blur' }],
projectLevel: [{ required: true, message: '项目级别不能为空', trigger: 'change' }],
projectType: [{ required: true, message: '项目类型不能为空', trigger: 'change' }],
userId: [{ required: true, message: '项目负责人ID不能为空', trigger: 'blur' }],
startDate: [{ required: true, message: '项目开始日期不能为空', trigger: 'blur' }],
endDate: [{ required: true, message: '项目结束日期不能为空', trigger: 'blur' }],
attachment: [{ required: true, message: '项目附件不能为空', trigger: 'blur' }],
background: [{ required: true, message: '项目背景不能为空', trigger: 'blur' }],
introduction: [{ required: true, message: '项目简介不能为空', trigger: 'blur' }],
progress: [{ validator: validateProgress, trigger: 'blur' }],
})
const formRef = ref() // 表单 Ref
/** 打开弹窗 */
const open = async (id?: number) => {
dialogVisible.value = true
resetForm()
// 修改时,设置数据
if (id) {
formLoading.value = true
try {
formData.value = await ProjectApi.getProject(id)
} finally {
formLoading.value = false
}
}
}
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
/** 提交表单 */
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
const submitForm = async () => {
// 校验表单
await formRef.value.validate()
// 提交请求
formLoading.value = true
try {
const data = formData.value as unknown as ProjectVO
if (!formType.value) {
await ProjectApi.createProject(data)
message.success(t('common.createSuccess'))
} else {
await ProjectApi.updateProject(data)
message.success(t('common.updateSuccess'))
}
dialogVisible.value = false
// 发送操作成功的事件
emit('success')
goBack()
} finally {
formLoading.value = false
}
}
const goBack = ()=> {
proxy.$router.go(-1)
}
const getData = (val) => {
let amount = 0
let coefficientNum = 0
val.forEach( item => {
amount = amount + item.bonusAmount
coefficientNum = coefficientNum + item.bonusPercentage
})
coefficient.value = (coefficientNum * 100).toFixed(2) + '%'
teamAmount.value = amount
}
/** 重置表单 */
const resetForm = () => {
formData.value = {
id: undefined,
projectCode: undefined,
name: undefined,
projectLevel: undefined,
projectType: undefined,
userId: undefined,
startDate: undefined,
endDate: undefined,
attachment: undefined,
bonusEvaluation: undefined,
bonusAmount: undefined,
bonusIssueDate: undefined,
background: undefined,
introduction: undefined,
expectation: undefined,
progress: undefined,
processInstanceId: undefined,
auditStatus: undefined
}
formRef.value?.resetFields()
}
const route = useRoute();
onMounted(async () => {
formType.value = props.id || route.query.id
if (formType.value) await open(formType.value)
deptTree.value = handleTree(await DeptApi.getSimpleDeptList())
// 获得用户列表
userOptions.value = await UserApi.getSimpleUserList()
if (!formType.value) {
formData.value.userId = useUserStore().getUser.id
}
})
</script>

View File

@ -0,0 +1,396 @@
<template>
<ContentWrap>
<el-form
ref="formRef"
:model="formData"
:rules="formRules"
label-width="120px"
v-loading="formLoading"
>
<el-row>
<!-- <el-col :span="8">
<el-form-item label="项目编号" prop="projectCode">
<el-input v-model="formData.projectCode" placeholder="请输入项目编号" />
</el-form-item>
</el-col> -->
<el-col :span="8">
<el-form-item label="项目级别" prop="projectLevel">
<el-select v-model="formData.projectLevel" :disabled="formType" placeholder="请输入项目级别">
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.TTS_PROJECT_LEVEL)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="项目类型" prop="projectType">
<el-select v-model="formData.projectType" :disabled="formType" placeholder="请选择项目类型">
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.TTS_PROJECT_TYPE)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="项目名称" prop="name">
<el-input v-model="formData.name" :disabled="formType" placeholder="请输入项目名称" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="项目负责人" prop="userId">
<el-select
v-model="formData.userId"
:disabled="formType"
placeholder="请选择项目负责人"
filterable
>
<el-option
v-for="item in userOptions"
:key="item.id"
:label="item.nickname"
:value="item.id"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="项目开始日期" prop="startDate">
<el-date-picker
:disabled="formType"
v-model="formData.startDate"
style="width: 100%"
type="date"
:disabled-date="disabledStartDate"
value-format="YYYY-MM-DD"
placeholder="选择项目开始日期"
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="项目结束日期" prop="endDate">
<el-date-picker
:disabled="formType"
v-model="formData.endDate"
style="width: 100%"
type="date"
:disabled-date="disabledEndDate"
value-format="YYYY-MM-DD"
placeholder="选择项目结束日期"
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="归属部门" prop="deptId">
<el-tree-select
:disabled="formType"
v-model="formData.deptId"
:data="deptTree"
:props="defaultProps"
check-strictly
node-key="id"
placeholder="请选择归属部门"
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="立项奖金评估" prop="bonusEvaluation">
<el-radio-group v-model="formData.bonusEvaluation">
<el-switch
v-model="formData.bonusEvaluation"
@change="formData.bonusAmount = ''"
class="ml-2"
/>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="8" v-if="formData.bonusEvaluation">
<el-form-item label="立项奖金金额" prop="bonusAmount">
<el-input v-model="formData.bonusAmount" oninput="value=value.match(/^\d+(?:\.\d{0,2})?/)" placeholder="请输入奖金评估描述" />
</el-form-item>
</el-col>
<el-col :span="8" v-if="formData.bonusEvaluation && formType">
<el-form-item label="项目进度(%)" prop="progress">
<el-input v-model="formData.progress" oninput = "value=value.replace(/[^\d]/g,'')" placeholder="请输入奖金评估描述" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="项目附件" prop="attachment">
<UploadFile v-model="formData.attachment" :disabled="!!formType" :limit="1" :fileType="['doc','docx']"/>
</el-form-item>
</el-col>
</el-row>
<ContentWrap style="border: none;font-weight: 700" v-if="formData.auditStatus == 2">
项目信息
</ContentWrap>
<el-row>
<el-col :span="24">
<el-form-item label="项目背景" prop="background">
<el-input v-model="formData.background" type="textarea" :disabled="formType" :rows="5" placeholder="请输入项目背景" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="项目简介" prop="introduction">
<el-input v-model="formData.introduction" type="textarea" :disabled="formType" :rows="5" placeholder="请输入项目简介" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="项目预期" prop="expectation">
<el-input v-model="formData.expectation" type="textarea" :disabled="formType" :rows="5" placeholder="请输入项目预期" />
</el-form-item>
</el-col>
<!-- <el-col :span="8">
<el-form-item label="项目进度" prop="progress">
<el-input v-model="formData.progress" placeholder="请输入项目进度" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="工作流编号" prop="processInstanceId">
<el-input v-model="formData.processInstanceId" placeholder="请输入工作流编号" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="审批状态" prop="auditStatus">
<el-radio-group v-model="formData.auditStatus">
<el-radio value="1">请选择字典生成</el-radio>
</el-radio-group>
</el-form-item>
</el-col> -->
</el-row>
<ContentWrap style="border: none;font-weight: 700" v-if="formData.auditStatus == 2">
奖金信息
</ContentWrap>
<el-row v-if="formData.auditStatus == 2">
<el-col :span="8">
<el-form-item label="系数总和" prop="">
<el-input v-model="coefficient" disabled placeholder="系数总和" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="团队奖金" prop="">
<el-input v-model="teamAmount" disabled placeholder="团队奖金" />
</el-form-item>
</el-col>
</el-row>
<ContentWrap style="border: none;font-weight: 700" v-if="formData.auditStatus == 2">
团队成员
</ContentWrap>
<Bonus v-if="formData.auditStatus == 2" :userOptions="userOptions" :id="formData.id" @success="getData" />
<ContentWrap style="border: none;font-weight: 700" v-if="formData.auditStatus == 2">
任务信息
</ContentWrap>
<Task ref="taskRef" v-if="formData.auditStatus == 2" :userOptions="userOptions" :id="formData.id" :userId="formData.userId" :name="formData.name" />
</el-form>
<div style="text-align: right" v-if="!type">
<el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
<el-button @click="goBack">取 消</el-button>
</div>
</ContentWrap>
</template>
<script setup lang="ts">
import { ProjectApi, ProjectVO } from '@/api/task/project'
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
import Bonus from './components/Bouns.vue'
import Task from './components/Task.vue'
import { defaultProps, handleTree } from '@/utils/tree'
import * as DeptApi from '@/api/system/dept'
import { propTypes } from '@/utils/propTypes'
import * as UserApi from '@/api/system/user'
import { useUserStore } from '@/store/modules/user'
const { proxy }: any = getCurrentInstance();
/** 项目管理 表单 */
defineOptions({ name: 'ProjectForm' })
const { t } = useI18n() // 国际化
const props = defineProps({
id: propTypes.number.def(undefined),
type: propTypes.number.def(undefined),
})
const pickerOptions = ref({
start: {
disabledDate(time) {
return time.getTime() < Date.now() - 86400000; // 禁用过去的时间(可选)
}
}
})
const userOptions = ref<UserApi.UserVO[]>([]) // 用户列表
const message = useMessage() // 消息弹窗
const deptTree = ref() // 部门树形结构
const routeId = ref('')
const teamAmount = ref('')
const coefficient = ref('')
const dialogVisible = ref(false) // 弹窗的是否展示
const dialogTitle = ref('') // 弹窗的标题
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
const formType = ref('') // 表单的类型:create - 新增;update - 修改
const validateProgress = (_: any, __: any, callback: any) => {
const num = Number(formData.value.progress)
if(!num) {
callback()
}
if (num <= 0 || num >100) {
callback(new Error('请输入正确的项目进度'))
return
}
callback()
}
const disabledStartDate = computed(() => {
return (time) => {
return formData.value.endDate ? time.getTime() > new Date(formData.value.endDate).getTime() - 60 * 60 * 24 * 1000 : false;
};
})
//结束时间限制
const disabledEndDate = computed(() => {
return (time) => {
return formData.value.startDate ? time.getTime() < new Date(formData.value.startDate).getTime() : false; // 开始时间存在时禁用之前及同一天的日期。
};
})
const formData = ref({
id: undefined,
projectCode: undefined,
name: undefined,
projectLevel: undefined,
projectType: undefined,
userId: undefined,
startDate: undefined,
endDate: undefined,
attachment: undefined,
bonusEvaluation: undefined,
bonusAmount: undefined,
bonusIssueDate: undefined,
background: undefined,
introduction: undefined,
expectation: undefined,
progress: undefined,
processInstanceId: undefined,
auditStatus: undefined
})
const formRules = reactive({
name: [{ required: true, message: '项目名称不能为空', trigger: 'blur' }],
projectLevel: [{ required: true, message: '项目级别不能为空', trigger: 'change' }],
projectType: [{ required: true, message: '项目类型不能为空', trigger: 'change' }],
userId: [{ required: true, message: '项目负责人ID不能为空', trigger: 'blur' }],
startDate: [{ required: true, message: '项目开始日期不能为空', trigger: 'blur' }],
endDate: [{ required: true, message: '项目结束日期不能为空', trigger: 'blur' }],
attachment: [{ required: true, message: '项目附件不能为空', trigger: 'blur' }],
background: [{ required: true, message: '项目背景不能为空', trigger: 'blur' }],
introduction: [{ required: true, message: '项目简介不能为空', trigger: 'blur' }],
progress: [{ validator: validateProgress, trigger: 'blur' }],
bonusAmount:[{ required: true, message: '立项奖金金额不能为空', trigger: 'blur' }],
})
const formRef = ref() // 表单 Ref
/** 打开弹窗 */
const open = async (id?: number) => {
dialogVisible.value = true
resetForm()
// 修改时,设置数据
if (id) {
formLoading.value = true
try {
formData.value = await ProjectApi.getProject(id)
} finally {
formLoading.value = false
}
}
}
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
/** 提交表单 */
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
const submitForm = async () => {
// 校验表单
await formRef.value.validate()
// 提交请求
formLoading.value = true
try {
const data = formData.value as unknown as ProjectVO
if (!formType.value) {
await ProjectApi.createProject(data)
message.success(t('common.createSuccess'))
} else {
await ProjectApi.updateProject(data)
message.success(t('common.updateSuccess'))
}
dialogVisible.value = false
// 发送操作成功的事件
emit('success')
goBack()
} finally {
formLoading.value = false
}
}
const goBack = ()=> {
proxy.$router.go(-1)
}
const getData = (val) => {
let amount = 0
let coefficientNum = 0
val.forEach( item => {
amount = amount + item.bonusAmount
coefficientNum = coefficientNum + item.bonusPercentage
})
coefficient.value = (coefficientNum * 100).toFixed(2) + '%'
teamAmount.value = amount
}
/** 重置表单 */
const resetForm = () => {
formData.value = {
id: undefined,
projectCode: undefined,
name: undefined,
projectLevel: undefined,
projectType: undefined,
userId: undefined,
startDate: undefined,
endDate: undefined,
attachment: undefined,
bonusEvaluation: undefined,
bonusAmount: undefined,
bonusIssueDate: undefined,
background: undefined,
introduction: undefined,
expectation: undefined,
progress: undefined,
processInstanceId: undefined,
auditStatus: undefined
}
formRef.value?.resetFields()
}
const route = useRoute();
onMounted(async () => {
formType.value = props.id || route.query.id
if (formType.value) await open(formType.value)
deptTree.value = handleTree(await DeptApi.getSimpleDeptList())
// 获得用户列表
userOptions.value = await UserApi.getSimpleUserList()
if (!formType.value) {
formData.value.userId = useUserStore().getUser.id
}
})
</script>

View File

@ -0,0 +1,160 @@
<template>
<!-- 列表 -->
<ContentWrap>
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
<el-table-column label="唯一主键" align="center" prop="id" />
<!-- <el-table-column label="项目ID" align="center" prop="projectId" /> -->
<el-table-column label="姓名" align="center" prop="userId">
<template #default="scope">
{{getName(userOptions, scope.row.userId)}}
</template>
</el-table-column>
<el-table-column label="系统占比" align="center" prop="bonusPercentage">
<template #default="scope">
{{(scope.row.bonusPercentage * 100).toFixed(2) + '%'}}
</template>
</el-table-column>
<el-table-column label="团队奖金" align="center" prop="bonusAmount" />
<el-table-column label="所得奖金" align="center" prop="bonusIssueDate" />
<el-table-column label="操作" align="center" min-width="120px" v-if="!type">
<template #default="scope">
<el-button
link
type="danger"
@click="handleDelete(scope.row.id)"
v-hasPermi="['task:project-bonus:delete']"
>
删除
</el-button>
</template>
</el-table-column>
</el-table>
<el-row justify="center" class="mt-3" v-if="!type">
<el-button @click="openForm" round>+ 添加</el-button>
</el-row>
<!-- <el-button
type="primary"
plain
@click="openForm('create')"
v-hasPermi="['task:project-bonus:create']"
>
<Icon icon="ep:plus" class="mr-5px" /> 新增
</el-button> -->
<!-- 分页 -->
<!-- <Pagination
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/> -->
</ContentWrap>
<!-- 表单弹窗:添加/修改 -->
<ProjectBonusForm ref="formRef" @success="getList" :id="id" :userOptions="userOptions" />
</template>
<script setup lang="ts">
import { dateFormatter } from '@/utils/formatTime'
import download from '@/utils/download'
import { ProjectBonusApi, ProjectBonusVO } from '@/api/task/bonus'
import ProjectBonusForm from './ProjectBonusForm.vue'
import { propTypes } from '@/utils/propTypes'
/** 项目奖金 列表 */
defineOptions({ name: 'ProjectBonus' })
const message = useMessage() // 消息弹窗
const { t } = useI18n() // 国际化
const props = defineProps({
id: propTypes.number.def(undefined),
type: propTypes.number.def(undefined),
userOptions: propTypes.object.def([]),
})
const loading = ref(true) // 列表的加载中
const list = ref<ProjectBonusVO[]>([]) // 列表的数据
const total = ref(0) // 列表的总页数
const queryParams = reactive({
pageNo: 1,
pageSize: 999,
projectId: undefined,
userId: undefined,
bonusPercentage: undefined,
bonusAmount: undefined,
bonusIssueDate: [],
bonusState: undefined,
createTime: []
})
const queryFormRef = ref() // 搜索的表单
const exportLoading = ref(false) // 导出的加载中
/** 查询列表 */
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
const getList = async () => {
loading.value = true
try {
queryParams.projectId = props.id
const data = await ProjectBonusApi.getProjectBonusPage(queryParams)
list.value = data.list
total.value = data.total
emit('success', list.value)
} finally {
loading.value = false
}
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.pageNo = 1
getList()
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value.resetFields()
handleQuery()
}
/** 添加/修改操作 */
const formRef = ref()
const openForm = (type: string, id?: number) => {
formRef.value.open(type, id)
}
/** 删除按钮操作 */
const handleDelete = async (id: number) => {
try {
// 删除的二次确认
await message.delConfirm()
// 发起删除
await ProjectBonusApi.deleteProjectBonus(id)
message.success(t('common.delSuccess'))
// 刷新列表
await getList()
} catch {}
}
/** 导出按钮操作 */
const handleExport = async () => {
try {
// 导出的二次确认
await message.exportConfirm()
// 发起导出
exportLoading.value = true
const data = await ProjectBonusApi.exportProjectBonus(queryParams)
download.excel(data, '项目奖金.xls')
} catch {
} finally {
exportLoading.value = false
}
}
const getName = (opt, val) => {
const arr = opt.filter(v => v.id == val)
return arr.length ? arr[0]['nickname'] : ''
}
/** 初始化 **/
onMounted(() => {
getList()
})
</script>

View File

@ -0,0 +1,121 @@
<template>
<Dialog title="添加成员" v-model="dialogVisible">
<el-form
ref="formRef"
:model="formData"
:rules="formRules"
label-width="110px"
v-loading="formLoading"
>
<el-form-item label="成员姓名" prop="userId">
<el-select v-model="formData.userId" placeholder="请选择成员姓名">
<el-option
v-for="dict in userOptions"
:key="dict.id"
:label="dict.nickname"
:value="dict.id"
/>
</el-select>
</el-form-item>
<el-form-item label="奖金系数占比" prop="bonusPercentage">
<el-input v-model="formData.bonusPercentage" oninput="value=value.match(/^\d+(?:\.\d{0,2})?/)" placeholder="请输入奖金系数占比" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
<el-button @click="dialogVisible = false">取 消</el-button>
</template>
</Dialog>
</template>
<script setup lang="ts">
import { ProjectBonusApi, ProjectBonusVO } from '@/api/task/bonus'
import * as UserApi from '@/api/system/user'
import { propTypes } from '@/utils/propTypes'
/** 项目奖金 表单 */
defineOptions({ name: 'ProjectBonusForm' })
// const userOptions = ref<UserApi.UserVO[]>([]) // 用户列表
const props = defineProps({
id: propTypes.number.def(undefined),
userOptions: [],
})
const { t } = useI18n() // 国际化
const message = useMessage() // 消息弹窗
const dialogVisible = ref(false) // 弹窗的是否展示
const dialogTitle = ref('') // 弹窗的标题
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
const formType = ref('') // 表单的类型:create - 新增;update - 修改
const validateMin = (_: any, __: any, callback: any) => {
const num = Number(formData.value.bonusPercentage)
if (!num) {
callback(new Error('请输入奖金系数占比'))
return
}
if (num < 0 || num >100) {
callback(new Error('请输入奖金系数占比'))
return
}
callback()
}
const formData = ref({
id: undefined,
projectId: undefined,
userId: undefined,
bonusPercentage: undefined,
bonusAmount: undefined,
bonusIssueDate: undefined,
bonusState: undefined,
})
const formRules = reactive({
projectId: [{ required: true, message: '项目ID不能为空', trigger: 'blur' }],
userId: [{ required: true, message: '员工ID不能为空', trigger: 'blur' }],
bonusPercentage: [{ required: true, validator: validateMin, trigger: 'blur' }],
})
const formRef = ref() // 表单 Ref
/** 打开弹窗 */
const open = async (id?: number) => {
dialogVisible.value = true
}
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
/** 提交表单 */
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
const submitForm = async () => {
// 校验表单
await formRef.value.validate()
// 提交请求
formLoading.value = true
try {
const data = JSON.parse(JSON.stringify(formData.value as unknown as ProjectBonusVO))
data.projectId = props.id
data.bonusPercentage = Number(data.bonusPercentage / 100).toFixed(4)
await ProjectBonusApi.createProjectBonus(data)
message.success(t('common.createSuccess'))
dialogVisible.value = false
// 发送操作成功的事件
emit('success')
} finally {
formLoading.value = false
}
}
/** 重置表单 */
const resetForm = () => {
formData.value = {
id: undefined,
projectId: undefined,
userId: undefined,
bonusPercentage: undefined,
bonusAmount: undefined,
bonusIssueDate: undefined,
bonusState: undefined
}
formRef.value?.resetFields()
}
</script>

View File

@ -0,0 +1,231 @@
<template>
<Dialog title="新增任务" v-model="dialogVisible" width="50%">
<el-form
ref="formRef"
:model="formData"
:rules="formRules"
label-width="100px"
v-loading="formLoading"
>
<el-row>
<el-col :span="12">
<el-form-item label="项目名称" prop="projectName">
<el-input v-model="formData.projectName" disabled placeholder="请输入任务名称" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="项目负责人" prop="projectUser">
<el-select v-model="formData.projectUser" disabled placeholder="">
<el-option
v-for="dict in userOptions"
:key="dict.id"
:label="dict.nickname"
:value="dict.id"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="任务标题" prop="taskName">
<el-input v-model="formData.taskName" placeholder="请输入任务名称" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="任务负责人" prop="userId">
<el-select v-model="formData.userId" placeholder="请选择成员姓名">
<el-option
v-for="dict in TaskOptions"
:key="dict.id"
:label="dict.userName"
:value="dict.id"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="开始日期" prop="startDate">
<el-date-picker
style="width: 100%"
v-model="formData.startDate"
type="date"
value-format="YYYY-MM-DD"
placeholder="选择开始日期"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="截止日期" prop="endDate">
<el-date-picker
v-model="formData.endDate"
style="width: 100%"
type="date"
value-format="YYYY-MM-DD"
placeholder="选择截止日期"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="任务优先级" prop="taskLevel">
<el-select v-model="formData.taskLevel" filterable clearable placeholder="请输入任务优先级" class="w-1/1">
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.TTS_TASK_LEVEL)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12" v-if="formType === 'update'">
<el-form-item label="任务进度" prop="taskProgress">
<el-input v-model="formData.taskProgress" oninput = "value=value.replace(/[^\d]/g,'')" placeholder="请输入任务进度" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="任务附件" prop="attachment">
<UploadFile v-model="formData.attachment" :limit="1" :fileType="['doc','docx']"/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="任务描述" prop="taskDescribe">
<el-input v-model="formData.taskDescribe" type="textarea" :rows="5" placeholder="请输入任务描述" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
<el-button @click="dialogVisible = false">取 消</el-button>
</template>
</Dialog>
</template>
<script setup lang="ts">
import { ProjectTaskApi, ProjectTaskVO } from '@/api/task/task'
import { getIntDictOptions, getStrDictOptions, DICT_TYPE } from '@/utils/dict'
import { propTypes } from '@/utils/propTypes'
import * as UserApi from '@/api/system/user'
/** 任务 表单 */
defineOptions({ name: 'ProjectTaskForm' })
const { t } = useI18n() // 国际化
// const userOptions = ref<UserApi.UserVO[]>([]) // 用户列表
const message = useMessage() // 消息弹窗
const props = defineProps({
userOptions: propTypes.object.def([]),
TaskOptions: propTypes.object.def([]),
})
const dialogVisible = ref(false) // 弹窗的是否展示
const dialogTitle = ref('') // 弹窗的标题
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
const validateProgress = (_: any, __: any, callback: any) => {
const num = Number(formData.value.taskProgress)
if (num <= 0 || num >100) {
callback(new Error('请输入正确的任务进度'))
return
}
callback()
}
const formType = ref('') // 表单的类型:create - 新增;update - 修改
const formData = ref({
id: undefined,
taskName: undefined,
userId: undefined,
taskLevel: undefined,
startDate: undefined,
endDate: undefined,
attachment: undefined,
taskDescribe: undefined,
taskProgress: undefined,
projectId: undefined,
taskState: undefined,
processInstanceId: undefined,
auditStatus: undefined,
projectUser: undefined,
projectName: undefined,
})
const formRules = reactive({
taskName: [{ required: true, message: '任务名称不能为空', trigger: 'blur' }],
userId: [{ required: true, message: '任务负责人不能为空', trigger: 'blur' }],
taskLevel: [{ required: true, message: '任务优先级不能为空', trigger: 'blur' }],
startDate: [{ required: true, message: '开始日期不能为空', trigger: 'blur' }],
endDate: [{ required: true, message: '截止日期不能为空', trigger: 'blur' }],
taskDescribe: [{ required: true, message: '任务描述不能为空', trigger: 'blur' }],
projectId: [{ required: true, message: '项目Id不能为空', trigger: 'blur' }],
taskProgress: [{ validator: validateProgress, trigger: 'blur' }],
})
const formRef = ref() // 表单 Ref
/** 打开弹窗 */
const open = async (data: Object, list: any) => {
dialogVisible.value = true
// userOptions.value = list
// formData.value.projectName = data.name
// formData.value.projectUser = data.userId
formType.value = data.type
let id = data.taskId
resetForm()
// // 修改时,设置数据
if (id) {
formLoading.value = true
try {
formData.value = await ProjectTaskApi.getProjectTask(id)
} finally {
formLoading.value = false
}
}
formData.value.projectName = data.name
formData.value.projectUser = data.userId
formData.value.projectId = data.id
}
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
/** 提交表单 */
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
const submitForm = async () => {
// 校验表单
await formRef.value.validate()
// 提交请求
formLoading.value = true
try {
const data = formData.value as unknown as ProjectTaskVO
if(formType.value === 'create') {
await ProjectTaskApi.createProjectTask(data)
message.success(t('common.createSuccess'))
} else {
data.taskProgress = (Number(data.taskProgress) / 100).toFixed(2)
await ProjectTaskApi.updateProjectTask(data)
message.success(t('common.updateSuccess'))
}
dialogVisible.value = false
// 发送操作成功的事件
emit('success')
} finally {
formLoading.value = false
}
}
/** 重置表单 */
const resetForm = () => {
formData.value = {
id: undefined,
taskName: undefined,
userId: undefined,
taskLevel: undefined,
startDate: undefined,
endDate: undefined,
attachment: undefined,
taskDescribe: undefined,
taskProgress: undefined,
projectId: undefined,
taskState: undefined,
processInstanceId: undefined,
auditStatus: undefined,
projectUser: undefined,
projectName: undefined,
}
formRef.value?.resetFields()
}
</script>

Some files were not shown because too many files have changed in this diff Show More