diff --git a/src/constants/contract-sign.ts b/src/constants/contract-sign.ts new file mode 100644 index 0000000..7d271ed --- /dev/null +++ b/src/constants/contract-sign.ts @@ -0,0 +1,36 @@ +export const CONTRACT_SIGN_STEPS = [ + { key: 'party-a', label: '甲方已签', status: 'done' as const }, + { key: 'party-b', label: '待乙方签署', status: 'active' as const }, +] + +export const CONTRACT_SIGN_BODY = `软件定制开发合同 + +合同编号:【】 +签订地点:【】 +签订日期:【】年【】月【】日 + +甲方(委托方): +名称:【甲方公司全称】 +统一社会信用代码:【】 +地址:【】 +联系人:【】 +电话:【】 +电子邮箱:【】 + +乙方(开发方): +名称:【乙方公司全称】 +统一社会信用代码:【】 +地址:【】 +联系人:【】 +电话:【】 +电子邮箱:【】 + +鉴于甲方拟委托乙方进行软件定制开发服务,双方依据《中华人民共和国民法典》及相关法律法规,在平等、自愿、公平和诚实信用的基础上,经友好协商,达成如下协议……` + +export const CONTRACT_SIGN_SIGNATURE_STORAGE_KEY = 'contract-sign-signature-preview' + +export const CONTRACT_SIGN_PAYMENT_INFO = { + method: '微信支付', + paidAt: '2026-01-21 15:00:00', + transactionNo: 'T20260819153035678K2M001', +} diff --git a/src/constants/contract.ts b/src/constants/contract.ts index a7ab1f6..5b1960b 100644 --- a/src/constants/contract.ts +++ b/src/constants/contract.ts @@ -6,10 +6,23 @@ export const CONTRACT_TABS: Array<{ value: ContractTab, label: string }> = [ { value: 'signed', label: '已签署' }, ] +const CONTRACT_DETAIL_BASE = { + contractName: '社区康养智能服务系统开发合同', + contractType: '互联网软件开发', + contractNo: 'C8K2M202408001', + signDate: '2024-10-15', + effectiveDate: '2024-10-15', + expiryDate: '2024-10-15', + relatedProject: '社区康养智能服务系统', + contractStatus: '履行中', + projectId: '1', + fileName: '需求文档.pdf', +} + export const CONTRACT_LIST: ContractItem[] = [ { id: '1', - contractNo: 'C8K2M202408001', + contractNo: CONTRACT_DETAIL_BASE.contractNo, title: '社区康养智慧系统', type: '软件开发', relatedLabel: '关联合同', @@ -21,10 +34,11 @@ export const CONTRACT_LIST: ContractItem[] = [ ], showDownload: true, showSign: false, + ...CONTRACT_DETAIL_BASE, }, { id: '2', - contractNo: 'C8K2M202408001', + contractNo: CONTRACT_DETAIL_BASE.contractNo, title: '社区康养智慧系统', type: '软件开发', relatedLabel: '关联项目', @@ -35,10 +49,12 @@ export const CONTRACT_LIST: ContractItem[] = [ ], showDownload: false, showSign: true, + ...CONTRACT_DETAIL_BASE, + contractStatus: '待签署', }, { id: '3', - contractNo: 'C8K2M202408001', + contractNo: CONTRACT_DETAIL_BASE.contractNo, title: '社区康养智慧系统', type: '软件开发', relatedLabel: '关联项目', @@ -49,5 +65,10 @@ export const CONTRACT_LIST: ContractItem[] = [ ], showDownload: false, showSign: false, + ...CONTRACT_DETAIL_BASE, }, ] + +export function getContract(id: string): ContractItem | null { + return CONTRACT_LIST.find(item => item.id === id) || null +} diff --git a/src/constants/personal-center.ts b/src/constants/personal-center.ts index 0539a44..a0a227f 100644 --- a/src/constants/personal-center.ts +++ b/src/constants/personal-center.ts @@ -11,7 +11,7 @@ import iconInvoice from '@/static/icon/invoice.png' import iconMessagePush from '@/static/icon/message-push.png' import iconQualification from '@/static/icon/qualification.png' import iconSecurity from '@/static/icon/security.png' -import iconServiceHotline from '@/static/icon/service-hotline.svg' +import iconServiceHotline from '@/static/icon/service-hotline.png' export const PERSONAL_CENTER_PROFILE: PersonalCenterProfile = { nickname: '这里是昵称', diff --git a/src/pages.json b/src/pages.json index 60e8582..893cdcb 100644 --- a/src/pages.json +++ b/src/pages.json @@ -223,6 +223,24 @@ "navigationStyle": "custom" } }, + { + "path": "pages/contract-center/detail/index", + "style": { + "navigationStyle": "custom" + } + }, + { + "path": "pages/contract-center/sign/index", + "style": { + "navigationStyle": "custom" + } + }, + { + "path": "pages/contract-center/sign-success/index", + "style": { + "navigationStyle": "custom" + } + }, { "path": "pages/personal-center/service-hotline/index", "style": { diff --git a/src/pages/contract-center/detail/index.scss b/src/pages/contract-center/detail/index.scss new file mode 100644 index 0000000..553784d --- /dev/null +++ b/src/pages/contract-center/detail/index.scss @@ -0,0 +1,132 @@ +@import '@/styles/variables.scss'; + +.contract-detail { + display: flex; + flex-direction: column; + min-height: 100vh; + background: $color-bg-section; +} + +.contract-detail__navbar { + display: flex; + align-items: center; + justify-content: space-between; + flex-shrink: 0; + padding: calc(var(--status-bar-height, 44px) + 60rpx) 24rpx 20rpx; + background: $color-bg-page; +} + +.contract-detail__back { + display: flex; + align-items: center; + justify-content: center; + width: 64rpx; + height: 64rpx; +} + +.contract-detail__back-icon { + width: 40rpx; + height: 40rpx; +} + +.contract-detail__navbar-title { + font-size: 34rpx; + font-weight: 600; + color: $color-text-primary; +} + +.contract-detail__navbar-placeholder { + width: 64rpx; + height: 64rpx; +} + +.contract-detail__scroll { + flex: 1; + height: 0; +} + +.contract-detail__content { + padding: 20rpx 24rpx calc(32rpx + env(safe-area-inset-bottom)); +} + +.contract-detail__card { + padding: 28rpx 24rpx; + background: $color-bg-page; + border-radius: 20rpx; +} + +.contract-detail__section { + &--divider { + margin-top: 32rpx; + padding-top: 32rpx; + border-top: 1rpx solid #f0f0f0; + } +} + +.contract-detail__section-title { + display: block; + margin-bottom: 24rpx; + font-size: 30rpx; + font-weight: 600; + color: $color-text-primary; +} + +.contract-detail__info-row { + display: flex; + align-items: flex-start; + justify-content: space-between; + margin-top: 20rpx; + + &:first-child { + margin-top: 0; + } +} + +.contract-detail__info-label { + flex-shrink: 0; + font-size: 26rpx; + color: $color-text-secondary; +} + +.contract-detail__info-value { + flex: 1; + margin-left: 24rpx; + font-size: 26rpx; + color: $color-text-primary; + text-align: right; + word-break: break-all; +} + +.contract-detail__file { + display: flex; + flex-direction: column; + align-items: center; + width: 160rpx; + padding: 20rpx 16rpx; + background: $color-bg-section; + border-radius: 12rpx; +} + +.contract-detail__file-icon { + width: 64rpx; + height: 64rpx; +} + +.contract-detail__file-name { + margin-top: 12rpx; + font-size: 22rpx; + color: $color-text-primary; + text-align: center; + word-break: break-all; +} + +.contract-detail__link-row { + display: flex; + justify-content: flex-end; + margin-top: 24rpx; +} + +.contract-detail__link { + font-size: 26rpx; + color: $color-text-secondary; +} diff --git a/src/pages/contract-center/detail/index.vue b/src/pages/contract-center/detail/index.vue new file mode 100644 index 0000000..85c6540 --- /dev/null +++ b/src/pages/contract-center/detail/index.vue @@ -0,0 +1,144 @@ + + + + + diff --git a/src/pages/contract-center/index.vue b/src/pages/contract-center/index.vue index 21b6324..8efb16c 100644 --- a/src/pages/contract-center/index.vue +++ b/src/pages/contract-center/index.vue @@ -34,7 +34,9 @@ function badgeClass(type: ContractItem['badges'][number]['type']) { } function handleViewDetail(item: ContractItem) { - uni.showToast({ title: `查看${item.title}详情开发中`, icon: 'none' }) + uni.navigateTo({ + url: `/pages/contract-center/detail/index?id=${encodeURIComponent(item.id)}`, + }) } function handlePreview(item: ContractItem) { @@ -46,7 +48,9 @@ function handleDownload(item: ContractItem) { } function handleSign(item: ContractItem) { - uni.showToast({ title: `签署${item.contractNo}开发中`, icon: 'none' }) + uni.navigateTo({ + url: `/pages/contract-center/sign/index?id=${encodeURIComponent(item.id)}`, + }) } diff --git a/src/pages/contract-center/sign-success/index.scss b/src/pages/contract-center/sign-success/index.scss new file mode 100644 index 0000000..21790e7 --- /dev/null +++ b/src/pages/contract-center/sign-success/index.scss @@ -0,0 +1,171 @@ +@import '@/styles/variables.scss'; + +.sign-success { + display: flex; + flex-direction: column; + min-height: 100vh; + background: $color-bg-section; +} + +.sign-success__navbar { + display: flex; + align-items: center; + justify-content: space-between; + flex-shrink: 0; + padding: calc(var(--status-bar-height, 44px) + 60rpx) 24rpx 20rpx; + background: $color-bg-page; +} + +.sign-success__back { + display: flex; + align-items: center; + justify-content: center; + width: 64rpx; + height: 64rpx; +} + +.sign-success__back-icon { + width: 40rpx; + height: 40rpx; +} + +.sign-success__navbar-title { + font-size: 34rpx; + font-weight: 600; + color: $color-text-primary; +} + +.sign-success__navbar-placeholder { + width: 64rpx; + height: 64rpx; +} + +.sign-success__scroll { + flex: 1; + height: 0; +} + +.sign-success__content { + padding: 20rpx 24rpx calc(140rpx + env(safe-area-inset-bottom)); +} + +.sign-success__hero { + display: flex; + flex-direction: column; + align-items: center; + padding: 40rpx 24rpx 36rpx; + margin-bottom: 20rpx; + background: $color-bg-page; + border-radius: 20rpx; +} + +.sign-success__hero-icon { + width: 120rpx; + height: 120rpx; +} + +.sign-success__hero-title { + margin-top: 28rpx; + font-size: 36rpx; + font-weight: 600; + color: $color-text-primary; +} + +.sign-success__hero-subtitle { + margin-top: 16rpx; + font-size: 26rpx; + line-height: 1.5; + color: $color-text-secondary; + text-align: center; +} + +.sign-success__card { + padding: 28rpx 24rpx; + margin-bottom: 20rpx; + background: $color-bg-page; + border-radius: 20rpx; + + &--link { + padding: 32rpx 24rpx; + } + + &:last-child { + margin-bottom: 0; + } +} + +.sign-success__card-link-text { + font-size: 28rpx; + color: $color-text-primary; +} + +.sign-success__card-title { + display: block; + margin-bottom: 24rpx; + font-size: 30rpx; + font-weight: 600; + color: $color-text-primary; +} + +.sign-success__info-row { + display: flex; + align-items: flex-start; + justify-content: space-between; + margin-top: 20rpx; + + &:first-of-type { + margin-top: 0; + } +} + +.sign-success__info-label { + flex-shrink: 0; + font-size: 26rpx; + color: $color-text-secondary; +} + +.sign-success__info-value { + flex: 1; + margin-left: 24rpx; + font-size: 26rpx; + color: $color-text-primary; + text-align: right; + word-break: break-all; +} + +.sign-success__signature-preview { + display: flex; + align-items: center; + justify-content: center; + min-height: 200rpx; + background: #fafafa; + border: 1rpx solid #f0f0f0; + border-radius: 12rpx; +} + +.sign-success__signature-image { + width: 100%; + height: 200rpx; +} + +.sign-success__footer { + display: flex; + gap: 20rpx; + flex-shrink: 0; + padding: 16rpx 24rpx calc(16rpx + env(safe-area-inset-bottom)); + background: $color-bg-page; + border-top: 1rpx solid #f0f0f0; +} + +.sign-success__footer-btn { + display: flex; + flex: 1; + align-items: center; + justify-content: center; + height: 88rpx; + font-size: 28rpx; + font-weight: 600; + color: #ffffff; + background: $color-primary; + border-radius: 12rpx; +} diff --git a/src/pages/contract-center/sign-success/index.vue b/src/pages/contract-center/sign-success/index.vue new file mode 100644 index 0000000..f0dff90 --- /dev/null +++ b/src/pages/contract-center/sign-success/index.vue @@ -0,0 +1,184 @@ + + + + + diff --git a/src/pages/contract-center/sign/components/SignaturePad.scss b/src/pages/contract-center/sign/components/SignaturePad.scss new file mode 100644 index 0000000..4658092 --- /dev/null +++ b/src/pages/contract-center/sign/components/SignaturePad.scss @@ -0,0 +1,88 @@ +@import '@/styles/variables.scss'; + +.signature-pad { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 400; + display: flex; + flex-direction: column; + background: rgba(0, 0, 0, 0.65); +} + +.signature-pad__body { + display: flex; + flex: 1; + align-items: center; + justify-content: center; + padding: 48rpx 32rpx; + box-sizing: border-box; +} + +.signature-pad__actions { + display: flex; + flex-direction: column; + gap: 24rpx; + margin-right: 24rpx; +} + +.signature-pad__action-btn { + display: flex; + align-items: center; + justify-content: center; + width: 72rpx; + padding: 32rpx 16rpx; + font-size: 26rpx; + font-weight: 600; + line-height: 1.4; + color: #ffffff; + text-align: center; + background: $color-primary; + border-radius: 12rpx; + writing-mode: vertical-rl; +} + +.signature-pad__canvas-wrap { + position: relative; + flex: 1; + max-width: 520rpx; + height: 72vh; + background: $color-bg-page; + border-radius: 16rpx; + overflow: hidden; +} + +.signature-pad__canvas { + width: 100%; + height: 100%; +} + +.signature-pad__placeholder { + position: absolute; + top: 50%; + left: 50%; + font-size: 48rpx; + color: #e5e7eb; + transform: translate(-50%, -50%) rotate(90deg); + pointer-events: none; +} + +.signature-pad__close { + display: flex; + align-items: center; + justify-content: center; + align-self: flex-end; + width: 72rpx; + height: 72rpx; + margin: 0 32rpx 48rpx; + background: rgba(255, 255, 255, 0.2); + border-radius: 50%; +} + +.signature-pad__close-icon { + font-size: 40rpx; + line-height: 1; + color: #ffffff; +} diff --git a/src/pages/contract-center/sign/components/SignaturePad.vue b/src/pages/contract-center/sign/components/SignaturePad.vue new file mode 100644 index 0000000..d4af971 --- /dev/null +++ b/src/pages/contract-center/sign/components/SignaturePad.vue @@ -0,0 +1,168 @@ + + + + + diff --git a/src/pages/contract-center/sign/index.scss b/src/pages/contract-center/sign/index.scss new file mode 100644 index 0000000..87462ef --- /dev/null +++ b/src/pages/contract-center/sign/index.scss @@ -0,0 +1,198 @@ +@import '@/styles/variables.scss'; + +.contract-sign { + display: flex; + flex-direction: column; + min-height: 100vh; + background: $color-bg-section; +} + +.contract-sign__navbar { + display: flex; + align-items: center; + justify-content: space-between; + flex-shrink: 0; + padding: calc(var(--status-bar-height, 44px) + 60rpx) 24rpx 20rpx; + background: $color-bg-page; +} + +.contract-sign__back { + display: flex; + align-items: center; + justify-content: center; + width: 64rpx; + height: 64rpx; +} + +.contract-sign__back-icon { + width: 40rpx; + height: 40rpx; +} + +.contract-sign__navbar-title { + font-size: 34rpx; + font-weight: 600; + color: $color-text-primary; +} + +.contract-sign__navbar-placeholder { + width: 64rpx; + height: 64rpx; +} + +.contract-sign__header { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 16rpx; + padding: 0 24rpx 20rpx; + background: $color-bg-page; +} + +.contract-sign__contract-name { + flex: 1; + min-width: 0; + font-size: 30rpx; + font-weight: 600; + color: $color-text-primary; +} + +.contract-sign__status { + flex-shrink: 0; + padding: 4rpx 12rpx; + font-size: 22rpx; + color: $color-text-secondary; + background: #f3f4f6; + border-radius: 6rpx; +} + +.contract-sign__steps { + display: flex; + justify-content: space-between; + padding: 24rpx 48rpx 28rpx; + background: $color-bg-page; + border-bottom: 1rpx solid #f0f0f0; +} + +.contract-sign__step { + display: flex; + flex: 1; + flex-direction: column; + align-items: center; +} + +.contract-sign__step-track { + display: flex; + align-items: center; + width: 100%; +} + +.contract-sign__step-node { + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; + width: 40rpx; + height: 40rpx; + background: #e5e7eb; + border-radius: 50%; + + &--done, + &--active { + background: $color-primary; + } +} + +.contract-sign__step-check { + font-size: 22rpx; + font-weight: 700; + color: #ffffff; + line-height: 1; +} + +.contract-sign__step-line { + flex: 1; + height: 4rpx; + margin: 0 8rpx; + background: #e5e7eb; + + &--done { + background: $color-primary; + } +} + +.contract-sign__step-label { + margin-top: 12rpx; + font-size: 22rpx; + color: $color-text-secondary; + text-align: center; + + &--active { + color: $color-primary; + } +} + +.contract-sign__scroll { + flex: 1; + height: 0; +} + +.contract-sign__content { + padding: 24rpx 24rpx calc(140rpx + env(safe-area-inset-bottom)); + background: $color-bg-page; +} + +.contract-sign__body { + display: block; + font-size: 26rpx; + line-height: 1.8; + color: $color-text-primary; + white-space: pre-wrap; +} + +.contract-sign__signature-row { + display: flex; + gap: 24rpx; + margin-top: 40rpx; +} + +.contract-sign__signature-col { + flex: 1; +} + +.contract-sign__signature-label { + display: block; + margin-bottom: 16rpx; + font-size: 24rpx; + color: $color-text-primary; +} + +.contract-sign__signature-box { + height: 160rpx; + background: #f3f4f6; + border-radius: 12rpx; + + &--done { + background: #eef2ff; + border: 1rpx solid $color-primary-light; + } +} + +.contract-sign__footer { + flex-shrink: 0; + padding: 16rpx 24rpx calc(16rpx + env(safe-area-inset-bottom)); + background: $color-bg-page; + border-top: 1rpx solid #f0f0f0; +} + +.contract-sign__submit-btn { + display: flex; + align-items: center; + justify-content: center; + height: 88rpx; + font-size: 30rpx; + font-weight: 600; + color: #ffffff; + background: $color-primary; + border-radius: 12rpx; +} diff --git a/src/pages/contract-center/sign/index.vue b/src/pages/contract-center/sign/index.vue new file mode 100644 index 0000000..9ac9ef4 --- /dev/null +++ b/src/pages/contract-center/sign/index.vue @@ -0,0 +1,182 @@ + + + + + diff --git a/src/pages/home-index/index.vue b/src/pages/home-index/index.vue index a752e5b..a2a90b0 100644 --- a/src/pages/home-index/index.vue +++ b/src/pages/home-index/index.vue @@ -61,6 +61,10 @@ function handleQuickEntryTap(entry: QuickEntryItem) { navigateQuickEntry(entry.id, entry.label) } +function handleViewAllProjects() { + navigateQuickEntry('project', '项目中心') +} + const projects: ProjectItem[] = [ { id: '1', @@ -187,7 +191,10 @@ const projects: ProjectItem[] = [ 在办项目 - + 查看全部 diff --git a/src/types/contract.ts b/src/types/contract.ts index e80f807..ced9088 100644 --- a/src/types/contract.ts +++ b/src/types/contract.ts @@ -20,4 +20,13 @@ export interface ContractItem { badges: ContractBadge[] showDownload: boolean showSign: boolean + contractName: string + contractType: string + signDate: string + effectiveDate: string + expiryDate: string + relatedProject: string + contractStatus: string + projectId: string + fileName: string }