diff --git a/src/components/BottomSheet.scss b/src/components/BottomSheet.scss
new file mode 100644
index 0000000..fa065c3
--- /dev/null
+++ b/src/components/BottomSheet.scss
@@ -0,0 +1,46 @@
+@import '@/styles/variables.scss';
+
+.bottom-sheet {
+ position: fixed;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ z-index: 300;
+ display: flex;
+ flex-direction: column;
+ justify-content: flex-end;
+}
+
+.bottom-sheet__mask {
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ background: rgba(0, 0, 0, 0.45);
+ opacity: 0;
+ transition: opacity 0.3s ease;
+}
+
+.bottom-sheet__mask--active {
+ opacity: 1;
+}
+
+.bottom-sheet__panel {
+ position: relative;
+ z-index: 1;
+ padding-bottom: env(safe-area-inset-bottom);
+ background: $color-bg-page;
+ border-radius: 24rpx 24rpx 0 0;
+ transform: translateY(100%);
+ transition: transform 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
+}
+
+.bottom-sheet__panel--active {
+ transform: translateY(0);
+}
+
+.bottom-sheet__body {
+ padding: 32rpx;
+}
diff --git a/src/components/BottomSheet.vue b/src/components/BottomSheet.vue
new file mode 100644
index 0000000..cb27efb
--- /dev/null
+++ b/src/components/BottomSheet.vue
@@ -0,0 +1,65 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/constants/address.ts b/src/constants/address.ts
new file mode 100644
index 0000000..c2994df
--- /dev/null
+++ b/src/constants/address.ts
@@ -0,0 +1,20 @@
+import type { AddressItem } from '@/types/address'
+
+export const ADDRESS_LIST: AddressItem[] = [
+ {
+ id: '1',
+ name: '张三',
+ phone: '15588888000',
+ region: '山东省日照市东港区',
+ detail: '科技园南路99号',
+ isDefault: true,
+ },
+ {
+ id: '2',
+ name: '张三',
+ phone: '15588888000',
+ region: '山东省日照市东港区',
+ detail: '科技园南路99号',
+ isDefault: false,
+ },
+]
diff --git a/src/constants/contract.ts b/src/constants/contract.ts
new file mode 100644
index 0000000..a7ab1f6
--- /dev/null
+++ b/src/constants/contract.ts
@@ -0,0 +1,53 @@
+import type { ContractItem, ContractTab } from '@/types/contract'
+
+export const CONTRACT_TABS: Array<{ value: ContractTab, label: string }> = [
+ { value: 'all', label: '全部' },
+ { value: 'pending', label: '待签署' },
+ { value: 'signed', label: '已签署' },
+]
+
+export const CONTRACT_LIST: ContractItem[] = [
+ {
+ id: '1',
+ contractNo: 'C8K2M202408001',
+ title: '社区康养智慧系统',
+ type: '软件开发',
+ relatedLabel: '关联合同',
+ relatedValue: '20260814ZZZ151245',
+ signStatus: 'signed',
+ badges: [
+ { type: 'signed', label: '已签署' },
+ { type: 'in_progress', label: '履行中' },
+ ],
+ showDownload: true,
+ showSign: false,
+ },
+ {
+ id: '2',
+ contractNo: 'C8K2M202408001',
+ title: '社区康养智慧系统',
+ type: '软件开发',
+ relatedLabel: '关联项目',
+ relatedValue: '社区康养智慧系统',
+ signStatus: 'pending',
+ badges: [
+ { type: 'pending_sign', label: '待签署' },
+ ],
+ showDownload: false,
+ showSign: true,
+ },
+ {
+ id: '3',
+ contractNo: 'C8K2M202408001',
+ title: '社区康养智慧系统',
+ type: '软件开发',
+ relatedLabel: '关联项目',
+ relatedValue: '社区康养智慧系统',
+ signStatus: 'signed',
+ badges: [
+ { type: 'signed', label: '已签署' },
+ ],
+ showDownload: false,
+ showSign: false,
+ },
+]
diff --git a/src/constants/invoice-list.ts b/src/constants/invoice-list.ts
new file mode 100644
index 0000000..1cd0634
--- /dev/null
+++ b/src/constants/invoice-list.ts
@@ -0,0 +1,31 @@
+import type { InvoiceHeaderItem } from '@/types/invoice-list'
+
+export const INVOICE_HEADER_LIST: InvoiceHeaderItem[] = [
+ {
+ id: '1',
+ title: '山东日照有限公司',
+ type: 'enterprise',
+ taxNo: '344683434513543543545535',
+ isDefault: true,
+ },
+ {
+ id: '2',
+ title: '山东日照有限公司',
+ type: 'personal',
+ taxNo: '344683434513543543545535',
+ isDefault: false,
+ },
+]
+
+export const INVOICE_HEADER_TYPE_LABEL: Record = {
+ enterprise: '企业',
+ personal: '个人',
+}
+
+export const INVOICE_HEADER_FORM_TYPE_OPTIONS: Array<{
+ value: InvoiceHeaderItem['type']
+ label: string
+}> = [
+ { value: 'enterprise', label: '企业单位' },
+ { value: 'personal', label: '非企业单位' },
+]
diff --git a/src/constants/invoice.ts b/src/constants/invoice.ts
new file mode 100644
index 0000000..d22affa
--- /dev/null
+++ b/src/constants/invoice.ts
@@ -0,0 +1,47 @@
+import type { InvoiceRecordItem, InvoiceRecordStatus } from '@/types/invoice'
+
+export const INVOICE_RECORD_TABS: Array<{ value: InvoiceRecordStatus, label: string }> = [
+ { value: 'all', label: '全部' },
+ { value: 'invoiced', label: '已开票' },
+ { value: 'not_invoiced', label: '未开票' },
+ { value: 'invoicing', label: '开票中' },
+]
+
+export const INVOICE_RECORD_STATUS_LABEL: Record = {
+ invoiced: '已开票',
+ not_invoiced: '未开票',
+ invoicing: '开票中',
+}
+
+export const INVOICE_RECORD_LIST: InvoiceRecordItem[] = [
+ {
+ id: '1',
+ title: '第一期·预付款',
+ ratio: '比例33%',
+ amount: 20100,
+ transactionNo: 'T20260819153035678K2M001',
+ relatedProject: '社区康养智慧系统',
+ invoiceTime: '2026-08-14 15:00',
+ status: 'invoiced',
+ },
+ {
+ id: '2',
+ title: '第一期·预付款',
+ ratio: '比例33%',
+ amount: 20100,
+ transactionNo: 'T20260819153035678K2M002',
+ relatedProject: '社区康养智慧系统',
+ invoiceTime: '2026-08-14 15:00',
+ status: 'invoiced',
+ },
+ {
+ id: '3',
+ title: '第一期·预付款',
+ ratio: '比例33%',
+ amount: 20100,
+ transactionNo: 'T20260819153035678K2M003',
+ relatedProject: '社区康养智慧系统',
+ invoiceTime: '2026-08-14 15:00',
+ status: 'not_invoiced',
+ },
+]
diff --git a/src/constants/personal-center.ts b/src/constants/personal-center.ts
index 960fb13..0539a44 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.png'
+import iconServiceHotline from '@/static/icon/service-hotline.svg'
export const PERSONAL_CENTER_PROFILE: PersonalCenterProfile = {
nickname: '这里是昵称',
@@ -23,15 +23,17 @@ export const PERSONAL_CENTER_MENU: PersonalCenterMenuItem[] = [
{ key: 'security', label: '账号安全', icon: iconSecurity, path: '/pages/personal-center/account-security/index' },
{ key: 'message-push', label: '消息推送', icon: iconMessagePush },
{ key: 'feedback', label: '意见反馈', icon: iconFeedback, path: '/pages/personal-center/feedback/index' },
- { key: 'contract-list', label: '合同列表', icon: iconContractList },
+ { key: 'contract-list', label: '合同列表', icon: iconContractList, path: '/pages/contract-center/index' },
{ key: 'finance', label: '财务中心', icon: iconFinance, path: '/pages/personal-center/finance/index' },
- { key: 'invoice', label: '发票列表', icon: iconInvoice },
- { key: 'address', label: '地址管理', icon: iconAddress },
+ { key: 'invoice', label: '发票列表', icon: iconInvoice, path: '/pages/personal-center/invoice/index' },
+ { key: 'address', label: '地址管理', icon: iconAddress, path: '/pages/personal-center/address/index' },
{ key: 'favorites', label: '收藏列表', icon: iconFavorites, path: '/pages/case-library/favorites/index' },
- { key: 'qualification', label: '资质列表', icon: iconQualification },
+ { key: 'qualification', label: '资质列表', icon: iconQualification, path: '/pages/personal-center/qualification-list/index' },
{ key: 'help', label: '帮助中心', icon: iconHelp },
{ key: 'about', label: '关于我们', icon: iconAbout },
- { key: 'service-hotline', label: '专属客服热线', icon: iconServiceHotline },
+ { key: 'service-hotline', label: '专属客服热线', icon: iconServiceHotline, path: '/pages/personal-center/service-hotline/index' },
]
export const PERSONAL_CENTER_SERVICE_HOTLINE = '400-000-0000'
+
+export const PERSONAL_CENTER_SERVICE_HOTLINE_HOURS = '工作日 8:00-12:00 14:00-18:00'
diff --git a/src/constants/qualification.ts b/src/constants/qualification.ts
new file mode 100644
index 0000000..cf5a28d
--- /dev/null
+++ b/src/constants/qualification.ts
@@ -0,0 +1,38 @@
+import type { QualificationItem, QualificationStatus } from '@/types/qualification'
+
+export const QUALIFICATION_TYPE_OPTIONS = [
+ '营业执照',
+ '高新技术企业证书',
+ 'ISO 质量管理体系认证',
+ '软件企业认定证书',
+]
+
+export const QUALIFICATION_STATUS_LABEL: Record = {
+ reviewing: '审核中',
+ approved: '已通过',
+ rejected: '已驳回',
+ draft: '草稿',
+}
+
+export const QUALIFICATION_LIST: QualificationItem[] = [
+ {
+ id: '1',
+ type: '营业执照',
+ certificateName: '山东日照有限公司',
+ certificateNo: '344683434513543543545535',
+ issuer: '山东省政务局',
+ validFrom: '2024-10-10',
+ validTo: '2027-10-10',
+ status: 'reviewing',
+ },
+ {
+ id: '2',
+ type: '营业执照',
+ certificateName: '山东日照有限公司',
+ certificateNo: '344683434513543543545535',
+ issuer: '山东省政务局',
+ validFrom: '2024-10-10',
+ validTo: '2027-10-10',
+ status: 'reviewing',
+ },
+]
diff --git a/src/hooks/use-invoice-headers.ts b/src/hooks/use-invoice-headers.ts
new file mode 100644
index 0000000..bd22e09
--- /dev/null
+++ b/src/hooks/use-invoice-headers.ts
@@ -0,0 +1,16 @@
+import { computed, ref } from 'vue'
+import { INVOICE_HEADER_LIST } from '@/constants/invoice-list'
+import type { InvoiceHeaderItem } from '@/types/invoice-list'
+
+const invoiceHeaders = ref(INVOICE_HEADER_LIST.map(item => ({ ...item })))
+
+export function useInvoiceHeaders() {
+ const defaultHeader = computed(() =>
+ invoiceHeaders.value.find(item => item.isDefault) || invoiceHeaders.value[0] || null,
+ )
+
+ return {
+ invoiceHeaders,
+ defaultHeader,
+ }
+}
diff --git a/src/hooks/use-qualifications.ts b/src/hooks/use-qualifications.ts
new file mode 100644
index 0000000..04c1941
--- /dev/null
+++ b/src/hooks/use-qualifications.ts
@@ -0,0 +1,16 @@
+import { ref } from 'vue'
+import { QUALIFICATION_LIST } from '@/constants/qualification'
+import type { QualificationItem } from '@/types/qualification'
+
+const qualifications = ref(QUALIFICATION_LIST.map(item => ({ ...item })))
+
+export function useQualifications() {
+ function getQualificationById(id: string) {
+ return qualifications.value.find(item => item.id === id) || null
+ }
+
+ return {
+ qualifications,
+ getQualificationById,
+ }
+}
diff --git a/src/pages.json b/src/pages.json
index 84dd4bf..60e8582 100644
--- a/src/pages.json
+++ b/src/pages.json
@@ -198,6 +198,48 @@
"style": {
"navigationStyle": "custom"
}
+ },
+ {
+ "path": "pages/personal-center/invoice/index",
+ "style": {
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/personal-center/invoice-list/index",
+ "style": {
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/personal-center/address/index",
+ "style": {
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/contract-center/index",
+ "style": {
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/personal-center/service-hotline/index",
+ "style": {
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/personal-center/qualification-list/index",
+ "style": {
+ "navigationStyle": "custom"
+ }
+ },
+ {
+ "path": "pages/personal-center/qualification-upload/index",
+ "style": {
+ "navigationStyle": "custom"
+ }
}
],
"globalStyle": {
diff --git a/src/pages/contract-center/index.scss b/src/pages/contract-center/index.scss
new file mode 100644
index 0000000..22d66ca
--- /dev/null
+++ b/src/pages/contract-center/index.scss
@@ -0,0 +1,229 @@
+@import '@/styles/variables.scss';
+
+.contract-center {
+ display: flex;
+ flex-direction: column;
+ min-height: 100vh;
+ background: $color-bg-section;
+}
+
+.contract-center__header {
+ 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-center__back {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 64rpx;
+ height: 64rpx;
+}
+
+.contract-center__back-icon {
+ width: 40rpx;
+ height: 40rpx;
+}
+
+.contract-center__title {
+ font-size: 34rpx;
+ font-weight: 600;
+ color: $color-text-primary;
+}
+
+.contract-center__header-placeholder {
+ width: 64rpx;
+ height: 64rpx;
+}
+
+.contract-center__toolbar {
+ flex-shrink: 0;
+ padding: 0 24rpx 24rpx;
+ background: $color-bg-page;
+}
+
+.contract-center__search {
+ display: flex;
+ align-items: center;
+ height: 72rpx;
+ padding: 0 24rpx;
+ background: $color-bg-section;
+ border-radius: 36rpx;
+}
+
+.contract-center__search-icon {
+ flex-shrink: 0;
+ width: 32rpx;
+ height: 32rpx;
+ margin-right: 12rpx;
+}
+
+.contract-center__search-input {
+ flex: 1;
+ font-size: 26rpx;
+ color: $color-text-primary;
+}
+
+.contract-center__search-placeholder {
+ color: $color-text-placeholder;
+}
+
+.contract-center__tabs {
+ display: flex;
+ margin-top: 24rpx;
+ overflow: hidden;
+ border: 2rpx solid $color-primary;
+ border-radius: 12rpx;
+}
+
+.contract-center__tab {
+ display: flex;
+ flex: 1;
+ align-items: center;
+ justify-content: center;
+ height: 72rpx;
+ background: $color-bg-page;
+ border-right: 2rpx solid $color-primary;
+
+ &:last-child {
+ border-right: none;
+ }
+
+ &--active {
+ background: $color-primary;
+ }
+}
+
+.contract-center__tab-text {
+ font-size: 28rpx;
+ color: $color-primary;
+}
+
+.contract-center__tab--active .contract-center__tab-text {
+ font-weight: 600;
+ color: #ffffff;
+}
+
+.contract-center__scroll {
+ flex: 1;
+ height: 0;
+}
+
+.contract-center__list {
+ padding: 0 24rpx calc(32rpx + env(safe-area-inset-bottom));
+}
+
+.contract-center__card {
+ padding: 28rpx 24rpx;
+ margin-top: 24rpx;
+ background: $color-bg-page;
+ border-radius: 20rpx;
+ box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.04);
+}
+
+.contract-center__card-top {
+ display: flex;
+ align-items: flex-start;
+ justify-content: space-between;
+ gap: 16rpx;
+}
+
+.contract-center__contract-no {
+ flex: 1;
+ min-width: 0;
+ font-size: 22rpx;
+ color: $color-text-secondary;
+}
+
+.contract-center__badges {
+ display: flex;
+ flex-shrink: 0;
+ gap: 12rpx;
+}
+
+.contract-center__badge {
+ padding: 4rpx 12rpx;
+ font-size: 20rpx;
+ border-radius: 6rpx;
+
+ &--signed {
+ color: #16a34a;
+ background: #ecfdf3;
+ }
+
+ &--in_progress {
+ color: $color-primary;
+ background: $color-primary-light;
+ }
+
+ &--pending_sign {
+ color: $color-stat-orange;
+ background: $color-stat-orange-bg;
+ }
+}
+
+.contract-center__title-row {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 16rpx;
+ margin-top: 20rpx;
+}
+
+.contract-center__card-title {
+ flex: 1;
+ min-width: 0;
+ font-size: 30rpx;
+ font-weight: 600;
+ color: $color-text-primary;
+}
+
+.contract-center__detail-link {
+ flex-shrink: 0;
+ font-size: 24rpx;
+ color: $color-text-secondary;
+}
+
+.contract-center__related {
+ display: block;
+ margin-top: 12rpx;
+ font-size: 24rpx;
+ color: $color-text-secondary;
+}
+
+.contract-center__actions {
+ display: flex;
+ gap: 20rpx;
+ margin-top: 28rpx;
+
+ &--single {
+ .contract-center__action-btn {
+ flex: 1;
+ }
+ }
+}
+
+.contract-center__action-btn {
+ display: flex;
+ flex: 1;
+ align-items: center;
+ justify-content: center;
+ height: 80rpx;
+ font-size: 28rpx;
+ border-radius: 12rpx;
+
+ &--outline {
+ color: $color-text-primary;
+ background: $color-bg-page;
+ border: 1rpx solid #d8dde6;
+ }
+
+ &--primary {
+ color: #ffffff;
+ background: $color-primary;
+ }
+}
diff --git a/src/pages/contract-center/index.vue b/src/pages/contract-center/index.vue
new file mode 100644
index 0000000..21b6324
--- /dev/null
+++ b/src/pages/contract-center/index.vue
@@ -0,0 +1,178 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ tab.label }}
+
+
+
+
+
+
+
+
+
+
+ 合同编号: {{ item.contractNo }}
+
+
+
+ {{ badge.label }}
+
+
+
+
+
+
+ {{ item.title }}
+
+
+ 查看详情
+
+
+
+
+ {{ item.relatedLabel }}: {{ item.relatedValue }}
+
+
+
+
+ 预览
+
+
+ 下载
+
+
+ 立即签署
+
+
+
+
+
+
+
+
+
diff --git a/src/pages/home-index/index.vue b/src/pages/home-index/index.vue
index 114f0e5..a752e5b 100644
--- a/src/pages/home-index/index.vue
+++ b/src/pages/home-index/index.vue
@@ -2,7 +2,8 @@
import AppTabBar from '@/components/AppTabBar.vue'
import { useGreeting } from '@/hooks/use-greeting'
import { useQuickEntries } from '@/hooks/use-quick-entries'
-import type { ProjectItem, ServiceItem } from '@/types/home'
+import type { ProjectItem, QuickEntryItem, ServiceItem } from '@/types/home'
+import { navigateQuickEntry } from '@/utils/quick-entry-navigate'
import headerBg from '@/static/icons/1.png'
import softwareIllus from '@/static/icons/software.png'
import hardwareIllus from '@/static/icons/hardware.png'
@@ -56,6 +57,10 @@ const services: ServiceItem[] = [
},
]
+function handleQuickEntryTap(entry: QuickEntryItem) {
+ navigateQuickEntry(entry.id, entry.label)
+}
+
const projects: ProjectItem[] = [
{
id: '1',
@@ -163,6 +168,7 @@ const projects: ProjectItem[] = [
v-for="entry in pinnedEntries"
:key="entry.id"
class="home__quick-item"
+ @tap="handleQuickEntryTap(entry)"
>
+import { computed, ref } from 'vue'
+import BottomSheet from '@/components/BottomSheet.vue'
+import { ADDRESS_LIST } from '@/constants/address'
+import type { AddressItem } from '@/types/address'
+import { navigateBackWithFallback } from '@/utils/navigate-back'
+
+const addressList = ref(ADDRESS_LIST.map(item => ({ ...item })))
+const sheetVisible = ref(false)
+const editingId = ref(null)
+const formName = ref('')
+const formPhone = ref('')
+const formRegion = ref('')
+const formDetail = ref('')
+
+const isEditing = computed(() => editingId.value !== null)
+const sheetTitle = computed(() => (isEditing.value ? '编辑地址' : '新增地址'))
+const sheetSubmitText = computed(() => (isEditing.value ? '保存' : '+ 新增地址'))
+
+function formatAddress(item: AddressItem) {
+ return `${item.region}${item.detail}`
+}
+
+function handleBack() {
+ navigateBackWithFallback('/pages/personal-center/index')
+}
+
+function handleSetDefault(item: AddressItem) {
+ addressList.value = addressList.value.map(row => ({
+ ...row,
+ isDefault: row.id === item.id,
+ }))
+}
+
+function handleEdit(item: AddressItem) {
+ editingId.value = item.id
+ formName.value = item.name
+ formPhone.value = item.phone
+ formRegion.value = item.region
+ formDetail.value = item.detail
+ sheetVisible.value = true
+}
+
+function handleDelete(item: AddressItem) {
+ uni.showModal({
+ title: '删除地址',
+ content: `确认删除「${item.name}」的地址吗?`,
+ confirmText: '删除',
+ confirmColor: '#2563EB',
+ success: (res) => {
+ if (!res.confirm)
+ return
+
+ addressList.value = addressList.value.filter(row => row.id !== item.id)
+ if (addressList.value.length && !addressList.value.some(row => row.isDefault))
+ addressList.value[0].isDefault = true
+
+ uni.showToast({ title: '已删除', icon: 'none' })
+ },
+ })
+}
+
+function resetForm() {
+ editingId.value = null
+ formName.value = ''
+ formPhone.value = ''
+ formRegion.value = ''
+ formDetail.value = ''
+}
+
+function handleAdd() {
+ resetForm()
+ sheetVisible.value = true
+}
+
+function closeSheet() {
+ sheetVisible.value = false
+ editingId.value = null
+}
+
+function handleSubmit() {
+ if (!formName.value.trim()) {
+ uni.showToast({ title: '请输入收货人姓名', icon: 'none' })
+ return
+ }
+ if (!formPhone.value.trim()) {
+ uni.showToast({ title: '请输入联系电话', icon: 'none' })
+ return
+ }
+ if (!formRegion.value.trim()) {
+ uni.showToast({ title: '请输入所在地区', icon: 'none' })
+ return
+ }
+ if (!formDetail.value.trim()) {
+ uni.showToast({ title: '请输入详细地址', icon: 'none' })
+ return
+ }
+
+ const payload = {
+ name: formName.value.trim(),
+ phone: formPhone.value.trim(),
+ region: formRegion.value.trim(),
+ detail: formDetail.value.trim(),
+ }
+
+ if (editingId.value) {
+ addressList.value = addressList.value.map(row => (
+ row.id === editingId.value
+ ? { ...row, ...payload }
+ : row
+ ))
+ closeSheet()
+ uni.showToast({ title: '保存成功', icon: 'none' })
+ return
+ }
+
+ const isFirst = addressList.value.length === 0
+ addressList.value.push({
+ id: `${Date.now()}`,
+ ...payload,
+ isDefault: isFirst,
+ })
+
+ closeSheet()
+ uni.showToast({ title: '新增成功', icon: 'none' })
+}
+
+
+
+
+
+
+
+
+
+ 地址管理
+
+
+
+
+
+
+
+
+
+ {{ item.name }}
+
+
+ {{ item.phone }}
+
+
+
+ {{ formatAddress(item) }}
+
+
+
+
+
+
+
+
+
+
+ {{ sheetTitle }}
+
+
+
+
+
+ 收货人
+
+
+
+
+
+ 手机号码
+
+
+
+
+
+ 地区
+
+
+
+
+
+ 详细地址
+
+
+
+
+
+
+ {{ sheetSubmitText }}
+
+
+
+
+
+
diff --git a/src/pages/personal-center/index.vue b/src/pages/personal-center/index.vue
index 170d302..b20ab87 100644
--- a/src/pages/personal-center/index.vue
+++ b/src/pages/personal-center/index.vue
@@ -3,7 +3,6 @@ import AppTabBar from '@/components/AppTabBar.vue'
import {
PERSONAL_CENTER_MENU,
PERSONAL_CENTER_PROFILE,
- PERSONAL_CENTER_SERVICE_HOTLINE,
} from '@/constants/personal-center'
import type { PersonalCenterMenuItem } from '@/types/personal-center'
import editIcon from '@/static/icon/edit.svg'
@@ -21,16 +20,6 @@ function handleMenuTap(item: PersonalCenterMenuItem) {
return
}
- if (item.key === 'service-hotline') {
- uni.makePhoneCall({
- phoneNumber: PERSONAL_CENTER_SERVICE_HOTLINE.replace(/-/g, ''),
- fail: () => {
- uni.showToast({ title: PERSONAL_CENTER_SERVICE_HOTLINE, icon: 'none' })
- },
- })
- return
- }
-
uni.showToast({ title: '功能开发中', icon: 'none' })
}
diff --git a/src/pages/personal-center/invoice-list/index.scss b/src/pages/personal-center/invoice-list/index.scss
new file mode 100644
index 0000000..249cf81
--- /dev/null
+++ b/src/pages/personal-center/invoice-list/index.scss
@@ -0,0 +1,254 @@
+@import '@/styles/variables.scss';
+
+.invoice-list {
+ display: flex;
+ flex-direction: column;
+ min-height: 100vh;
+ background: $color-bg-section;
+}
+
+.invoice-list__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;
+}
+
+.invoice-list__back {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 64rpx;
+ height: 64rpx;
+}
+
+.invoice-list__back-icon {
+ width: 40rpx;
+ height: 40rpx;
+}
+
+.invoice-list__navbar-title {
+ font-size: 34rpx;
+ font-weight: 600;
+ color: $color-text-primary;
+}
+
+.invoice-list__navbar-placeholder {
+ width: 64rpx;
+ height: 64rpx;
+}
+
+.invoice-list__scroll {
+ flex: 1;
+ height: 0;
+}
+
+.invoice-list__content {
+ padding: 20rpx 24rpx calc(140rpx + env(safe-area-inset-bottom));
+}
+
+.invoice-list__card {
+ padding: 28rpx 24rpx;
+ margin-bottom: 20rpx;
+ background: $color-bg-page;
+ border-radius: 20rpx;
+
+ &:last-child {
+ margin-bottom: 0;
+ }
+}
+
+.invoice-list__card-top {
+ display: flex;
+ align-items: center;
+}
+
+.invoice-list__card-title {
+ flex: 1;
+ min-width: 0;
+ font-size: 30rpx;
+ font-weight: 600;
+ color: $color-text-primary;
+}
+
+.invoice-list__tag {
+ flex-shrink: 0;
+ margin-left: 16rpx;
+ padding: 4rpx 14rpx;
+ font-size: 22rpx;
+ border-radius: 6rpx;
+
+ &--enterprise {
+ color: $color-primary;
+ border: 1rpx solid $color-primary;
+ }
+
+ &--personal {
+ color: $color-stat-orange;
+ border: 1rpx solid $color-stat-orange;
+ }
+}
+
+.invoice-list__tax-no {
+ display: block;
+ margin-top: 16rpx;
+ font-size: 24rpx;
+ color: #222222;
+ word-break: break-all;
+}
+
+.invoice-list__card-footer {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ margin-top: 28rpx;
+}
+
+.invoice-list__default {
+ display: flex;
+ align-items: center;
+}
+
+.invoice-list__radio {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 28rpx;
+ height: 28rpx;
+ border: 2rpx solid #d8dde6;
+ border-radius: 50%;
+
+ &--checked {
+ border-color: $color-primary;
+ }
+}
+
+.invoice-list__radio-dot {
+ width: 14rpx;
+ height: 14rpx;
+ background: $color-primary;
+ border-radius: 50%;
+}
+
+.invoice-list__default-text {
+ margin-left: 12rpx;
+ font-size: 24rpx;
+ color: #222222;
+}
+
+.invoice-list__actions {
+ display: flex;
+ gap: 32rpx;
+}
+
+.invoice-list__action {
+ font-size: 26rpx;
+ color: $color-text-primary;
+}
+
+.invoice-list__footer {
+ flex-shrink: 0;
+ padding: 16rpx 24rpx calc(16rpx + env(safe-area-inset-bottom));
+ background: $color-bg-page;
+ border-top: 1rpx solid #f0f0f0;
+}
+
+.invoice-list__add-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;
+}
+
+.invoice-list__sheet-title {
+ display: block;
+ font-size: 32rpx;
+ font-weight: 600;
+ color: $color-text-primary;
+}
+
+.invoice-list__sheet-field {
+ margin-top: 32rpx;
+}
+
+.invoice-list__sheet-label {
+ display: block;
+ margin-bottom: 16rpx;
+ font-size: 28rpx;
+ font-weight: 500;
+ color: $color-text-primary;
+}
+
+.invoice-list__type-row {
+ display: flex;
+ gap: 48rpx;
+}
+
+.invoice-list__type-option {
+ display: flex;
+ align-items: center;
+}
+
+.invoice-list__type-radio {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 32rpx;
+ height: 32rpx;
+ border: 2rpx solid #d8dde6;
+ border-radius: 50%;
+
+ &--checked {
+ background: $color-primary;
+ border-color: $color-primary;
+ }
+}
+
+.invoice-list__type-check {
+ font-size: 20rpx;
+ font-weight: 700;
+ color: #ffffff;
+ line-height: 1;
+}
+
+.invoice-list__type-label {
+ margin-left: 12rpx;
+ font-size: 28rpx;
+ color: $color-text-primary;
+}
+
+.invoice-list__sheet-input {
+ width: 100%;
+ height: 80rpx;
+ padding: 0 24rpx;
+ font-size: 28rpx;
+ color: $color-text-primary;
+ background: $color-bg-page;
+ border: 1rpx solid #e8eaef;
+ border-radius: 12rpx;
+ box-sizing: border-box;
+}
+
+.invoice-list__sheet-placeholder {
+ color: $color-text-placeholder;
+}
+
+.invoice-list__sheet-submit {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ height: 88rpx;
+ margin-top: 40rpx;
+ font-size: 30rpx;
+ font-weight: 600;
+ color: #ffffff;
+ background: $color-primary;
+ border-radius: 12rpx;
+}
diff --git a/src/pages/personal-center/invoice-list/index.vue b/src/pages/personal-center/invoice-list/index.vue
new file mode 100644
index 0000000..91b6737
--- /dev/null
+++ b/src/pages/personal-center/invoice-list/index.vue
@@ -0,0 +1,290 @@
+
+
+
+
+
+
+
+
+
+ 抬头管理
+
+
+
+
+
+
+
+
+
+ {{ item.title }}
+
+
+ {{ typeLabel(item.type) }}
+
+
+
+ {{ item.taxNo }}
+
+
+
+
+
+
+
+
+
+
+ {{ sheetTitle }}
+
+
+
+
+ 抬头类型
+
+
+
+
+
+ ✓
+
+
+
+ {{ option.label }}
+
+
+
+
+
+
+
+ 发票抬头
+
+
+
+
+
+
+ 税号
+
+
+
+
+
+ {{ sheetSubmitText }}
+
+
+
+
+
+
diff --git a/src/pages/personal-center/invoice/index.scss b/src/pages/personal-center/invoice/index.scss
new file mode 100644
index 0000000..28c5067
--- /dev/null
+++ b/src/pages/personal-center/invoice/index.scss
@@ -0,0 +1,238 @@
+@import '@/styles/variables.scss';
+
+.invoice-page {
+ display: flex;
+ flex-direction: column;
+ min-height: 100vh;
+ background: $color-bg-section;
+}
+
+.invoice-page__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;
+}
+
+.invoice-page__back {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 64rpx;
+ height: 64rpx;
+}
+
+.invoice-page__back-icon {
+ width: 40rpx;
+ height: 40rpx;
+}
+
+.invoice-page__navbar-title {
+ font-size: 34rpx;
+ font-weight: 600;
+ color: $color-text-primary;
+}
+
+.invoice-page__navbar-placeholder {
+ width: 64rpx;
+ height: 64rpx;
+}
+
+.invoice-page__scroll {
+ flex: 1;
+ height: 0;
+}
+
+.invoice-page__content {
+ padding: 20rpx 24rpx calc(32rpx + env(safe-area-inset-bottom));
+}
+
+.invoice-page__section {
+ margin-bottom: 24rpx;
+}
+
+.invoice-page__section-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ margin-bottom: 16rpx;
+}
+
+.invoice-page__section-title {
+ font-size: 30rpx;
+ font-weight: 600;
+ color: $color-text-primary;
+}
+
+.invoice-page__manage {
+ display: flex;
+ align-items: center;
+}
+
+.invoice-page__manage-text {
+ font-size: 26rpx;
+ color: $color-text-secondary;
+}
+
+.invoice-page__manage-arrow {
+ margin-left: 4rpx;
+ font-size: 26rpx;
+ color: $color-text-secondary;
+}
+
+.invoice-page__header-card {
+ padding: 28rpx 24rpx;
+ background: $color-bg-page;
+ border-radius: 20rpx;
+}
+
+.invoice-page__header-top {
+ display: flex;
+ align-items: center;
+}
+
+.invoice-page__default-tag {
+ flex-shrink: 0;
+ padding: 4rpx 12rpx;
+ font-size: 22rpx;
+ color: $color-stat-orange;
+ background: $color-stat-orange-bg;
+ border-radius: 6rpx;
+}
+
+.invoice-page__header-title {
+ flex: 1;
+ min-width: 0;
+ margin-left: 12rpx;
+ font-size: 30rpx;
+ font-weight: 600;
+ color: $color-text-primary;
+}
+
+.invoice-page__header-type {
+ flex-shrink: 0;
+ margin-left: 16rpx;
+ padding: 4rpx 14rpx;
+ font-size: 22rpx;
+ border-radius: 6rpx;
+
+ &--enterprise {
+ color: $color-primary;
+ border: 1rpx solid $color-primary;
+ }
+
+ &--personal {
+ color: $color-stat-orange;
+ border: 1rpx solid $color-stat-orange;
+ }
+}
+
+.invoice-page__header-tax {
+ display: block;
+ margin-top: 16rpx;
+ font-size: 24rpx;
+ color: #222222;
+ word-break: break-all;
+}
+
+.invoice-page__search {
+ display: flex;
+ align-items: center;
+ height: 72rpx;
+ margin-top: 20rpx;
+ padding: 0 24rpx;
+ background: $color-bg-page;
+ border-radius: 36rpx;
+}
+
+.invoice-page__search-icon {
+ flex-shrink: 0;
+ width: 32rpx;
+ height: 32rpx;
+ margin-right: 12rpx;
+}
+
+.invoice-page__search-input {
+ flex: 1;
+ font-size: 26rpx;
+ color: $color-text-primary;
+}
+
+.invoice-page__search-placeholder {
+ color: $color-text-placeholder;
+}
+
+.invoice-page__tabs {
+ display: flex;
+ margin-top: 24rpx;
+ background: $color-bg-page;
+ border-radius: 20rpx;
+}
+
+.invoice-page__tab {
+ position: relative;
+ display: flex;
+ flex: 1;
+ align-items: center;
+ justify-content: center;
+ height: 80rpx;
+}
+
+.invoice-page__tab-text {
+ font-size: 28rpx;
+ color: $color-text-secondary;
+}
+
+.invoice-page__tab--active .invoice-page__tab-text {
+ font-weight: 600;
+ color: $color-primary;
+}
+
+.invoice-page__tab--active::after {
+ content: '';
+ position: absolute;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ height: 4rpx;
+ background: $color-primary;
+ border-radius: 2rpx;
+}
+
+.invoice-page__record-card {
+ padding: 28rpx 24rpx;
+ margin-top: 20rpx;
+ background: $color-bg-page;
+ border-radius: 20rpx;
+}
+
+.invoice-page__record-header {
+ display: flex;
+ align-items: flex-start;
+ justify-content: space-between;
+ gap: 16rpx;
+}
+
+.invoice-page__record-title {
+ flex: 1;
+ min-width: 0;
+ font-size: 28rpx;
+ font-weight: 600;
+ color: $color-text-primary;
+}
+
+.invoice-page__record-amount {
+ flex-shrink: 0;
+ font-size: 30rpx;
+ font-weight: 600;
+ color: $color-text-primary;
+}
+
+.invoice-page__record-row {
+ display: block;
+ margin-top: 12rpx;
+ font-size: 24rpx;
+ color: $color-text-secondary;
+}
diff --git a/src/pages/personal-center/invoice/index.vue b/src/pages/personal-center/invoice/index.vue
new file mode 100644
index 0000000..cc188e7
--- /dev/null
+++ b/src/pages/personal-center/invoice/index.vue
@@ -0,0 +1,175 @@
+
+
+
+
+
+
+
+
+
+ 发票列表
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 发票记录
+
+
+
+
+
+
+
+
+
+
+ {{ tab.label }}
+
+
+
+
+
+
+
+ 交易流水号:{{ item.transactionNo }}
+
+
+ 关联项目:{{ item.relatedProject }}
+
+
+ 开票时间:{{ item.invoiceTime }}
+
+
+
+
+
+
+
+
+
diff --git a/src/pages/personal-center/qualification-list/index.scss b/src/pages/personal-center/qualification-list/index.scss
new file mode 100644
index 0000000..8b26519
--- /dev/null
+++ b/src/pages/personal-center/qualification-list/index.scss
@@ -0,0 +1,131 @@
+@import '@/styles/variables.scss';
+
+.qualification-list {
+ display: flex;
+ flex-direction: column;
+ min-height: 100vh;
+ background: $color-bg-section;
+}
+
+.qualification-list__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;
+}
+
+.qualification-list__back {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 64rpx;
+ height: 64rpx;
+}
+
+.qualification-list__back-icon {
+ width: 40rpx;
+ height: 40rpx;
+}
+
+.qualification-list__navbar-title {
+ font-size: 34rpx;
+ font-weight: 600;
+ color: $color-text-primary;
+}
+
+.qualification-list__navbar-placeholder {
+ width: 64rpx;
+ height: 64rpx;
+}
+
+.qualification-list__scroll {
+ flex: 1;
+ height: 0;
+}
+
+.qualification-list__content {
+ padding: 20rpx 24rpx calc(140rpx + env(safe-area-inset-bottom));
+}
+
+.qualification-list__card {
+ padding: 28rpx 24rpx;
+ margin-bottom: 20rpx;
+ background: $color-bg-page;
+ border-radius: 20rpx;
+
+ &:last-child {
+ margin-bottom: 0;
+ }
+}
+
+.qualification-list__card-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+}
+
+.qualification-list__type {
+ font-size: 24rpx;
+ color: $color-text-secondary;
+}
+
+.qualification-list__status {
+ font-size: 24rpx;
+ color: $color-stat-orange;
+}
+
+.qualification-list__name {
+ display: block;
+ margin-top: 12rpx;
+ font-size: 32rpx;
+ font-weight: 600;
+ color: $color-text-primary;
+}
+
+.qualification-list__row {
+ display: block;
+ margin-top: 12rpx;
+ font-size: 24rpx;
+ color: #222222;
+ word-break: break-all;
+}
+
+.qualification-list__actions {
+ display: flex;
+ justify-content: flex-end;
+ gap: 32rpx;
+ margin-top: 24rpx;
+}
+
+.qualification-list__action {
+ font-size: 26rpx;
+
+ &--edit {
+ color: $color-primary;
+ }
+
+ &--delete {
+ color: #ef4444;
+ }
+}
+
+.qualification-list__footer {
+ flex-shrink: 0;
+ padding: 16rpx 24rpx calc(16rpx + env(safe-area-inset-bottom));
+ background: $color-bg-page;
+ border-top: 1rpx solid #f0f0f0;
+}
+
+.qualification-list__add-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/personal-center/qualification-list/index.vue b/src/pages/personal-center/qualification-list/index.vue
new file mode 100644
index 0000000..964ce52
--- /dev/null
+++ b/src/pages/personal-center/qualification-list/index.vue
@@ -0,0 +1,131 @@
+
+
+
+
+
+
+
+
+
+ 资质列表
+
+
+
+
+
+
+
+
+
+ {{ item.certificateName }}
+
+
+ {{ item.certificateNo }}
+
+
+ {{ item.issuer }}
+
+
+ {{ formatValidity(item) }}
+
+
+
+ 编辑
+
+
+ 删除
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/pages/personal-center/qualification-upload/index.scss b/src/pages/personal-center/qualification-upload/index.scss
new file mode 100644
index 0000000..6af372a
--- /dev/null
+++ b/src/pages/personal-center/qualification-upload/index.scss
@@ -0,0 +1,248 @@
+@import '@/styles/variables.scss';
+
+.qualification-upload {
+ display: flex;
+ flex-direction: column;
+ min-height: 100vh;
+ background: $color-bg-section;
+}
+
+.qualification-upload__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;
+}
+
+.qualification-upload__back {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 64rpx;
+ height: 64rpx;
+}
+
+.qualification-upload__back-icon {
+ width: 40rpx;
+ height: 40rpx;
+}
+
+.qualification-upload__navbar-title {
+ font-size: 34rpx;
+ font-weight: 600;
+ color: $color-text-primary;
+}
+
+.qualification-upload__navbar-placeholder {
+ width: 64rpx;
+ height: 64rpx;
+}
+
+.qualification-upload__scroll {
+ flex: 1;
+ height: 0;
+ padding: 20rpx 24rpx;
+ box-sizing: border-box;
+}
+
+.qualification-upload__card {
+ padding: 28rpx 24rpx;
+ background: $color-bg-page;
+ border-radius: 20rpx;
+}
+
+.qualification-upload__field {
+ margin-bottom: 28rpx;
+
+ &:last-child {
+ margin-bottom: 0;
+ }
+}
+
+.qualification-upload__label {
+ display: block;
+ margin-bottom: 16rpx;
+ font-size: 28rpx;
+ font-weight: 500;
+ color: $color-text-primary;
+}
+
+.qualification-upload__required {
+ color: $color-text-primary;
+}
+
+.qualification-upload__label-row {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ margin-bottom: 16rpx;
+}
+
+.qualification-upload__hint {
+ font-size: 24rpx;
+ color: $color-text-secondary;
+}
+
+.qualification-upload__input,
+.qualification-upload__picker {
+ height: 80rpx;
+ padding: 0 24rpx;
+ background: $color-bg-page;
+ border: 1rpx solid #e8eaef;
+ border-radius: 12rpx;
+ box-sizing: border-box;
+}
+
+.qualification-upload__input {
+ width: 100%;
+ font-size: 28rpx;
+ color: $color-text-primary;
+}
+
+.qualification-upload__picker {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+}
+
+.qualification-upload__picker-text {
+ font-size: 28rpx;
+ color: $color-text-primary;
+
+ &--placeholder {
+ color: $color-text-placeholder;
+ }
+}
+
+.qualification-upload__picker-arrow {
+ font-size: 24rpx;
+ color: $color-text-secondary;
+}
+
+.qualification-upload__placeholder {
+ color: $color-text-placeholder;
+}
+
+.qualification-upload__date-row {
+ display: flex;
+ align-items: center;
+ gap: 16rpx;
+}
+
+.qualification-upload__date {
+ display: flex;
+ flex: 1;
+ align-items: center;
+ justify-content: center;
+ height: 80rpx;
+ background: $color-bg-page;
+ border: 1rpx solid #e8eaef;
+ border-radius: 12rpx;
+}
+
+.qualification-upload__date-text {
+ font-size: 28rpx;
+ color: $color-text-primary;
+
+ &--placeholder {
+ color: $color-text-placeholder;
+ }
+}
+
+.qualification-upload__date-sep {
+ flex-shrink: 0;
+ font-size: 28rpx;
+ color: $color-text-primary;
+}
+
+.qualification-upload__attachment-row {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 20rpx;
+ padding: 10rpx 10rpx 0 0;
+}
+
+.qualification-upload__attachment-item {
+ position: relative;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ width: 160rpx;
+ padding: 20rpx 16rpx;
+ background: $color-bg-section;
+ border-radius: 12rpx;
+}
+
+.qualification-upload__attachment-remove {
+ position: absolute;
+ top: -10rpx;
+ right: -10rpx;
+ width: 32rpx;
+ height: 32rpx;
+}
+
+.qualification-upload__attachment-remove-icon {
+ width: 32rpx;
+ height: 32rpx;
+}
+
+.qualification-upload__attachment-icon {
+ width: 64rpx;
+ height: 64rpx;
+}
+
+.qualification-upload__attachment-name {
+ margin-top: 12rpx;
+ font-size: 22rpx;
+ color: $color-text-primary;
+ text-align: center;
+ word-break: break-all;
+}
+
+.qualification-upload__attachment-add {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ width: 160rpx;
+ height: 160rpx;
+ background: #f8f9fb;
+ border: 1rpx dashed #d8dde6;
+ border-radius: 12rpx;
+}
+
+.qualification-upload__attachment-add-icon {
+ font-size: 40rpx;
+ line-height: 1;
+ color: $color-text-secondary;
+}
+
+.qualification-upload__attachment-add-text {
+ margin-top: 8rpx;
+ font-size: 20rpx;
+ color: $color-text-secondary;
+}
+
+.qualification-upload__footer {
+ display: flex;
+ gap: 16rpx;
+ flex-shrink: 0;
+ padding: 16rpx 24rpx calc(16rpx + env(safe-area-inset-bottom));
+ background: $color-bg-page;
+ border-top: 1rpx solid #f0f0f0;
+}
+
+.qualification-upload__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/personal-center/qualification-upload/index.vue b/src/pages/personal-center/qualification-upload/index.vue
new file mode 100644
index 0000000..05b35ce
--- /dev/null
+++ b/src/pages/personal-center/qualification-upload/index.vue
@@ -0,0 +1,384 @@
+
+
+
+
+
+
+
+
+
+ 资质上传
+
+
+
+
+
+
+
+
+ 资质类型*
+
+
+
+
+ {{ selectedType || '请选择资质类型' }}
+
+
+ ▾
+
+
+
+
+
+
+
+ 证书名称*
+
+
+
+
+
+
+ 证书编号*
+
+
+
+
+
+
+ 发证单位*
+
+
+
+
+
+
+ 有效期*
+
+
+
+
+
+ {{ validFrom || '起始时间' }}
+
+
+
+
+ 至
+
+
+
+
+ {{ validTo || '截止时间' }}
+
+
+
+
+
+
+
+
+
+ 附件上传
+
+
+ 支持图片/文档/压缩包
+
+
+
+
+
+
+
+
+
+ {{ file.name }}
+
+
+
+
+ +
+
+
+ 添加附件
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/pages/personal-center/service-hotline/index.scss b/src/pages/personal-center/service-hotline/index.scss
new file mode 100644
index 0000000..7936bd9
--- /dev/null
+++ b/src/pages/personal-center/service-hotline/index.scss
@@ -0,0 +1,94 @@
+@import '@/styles/variables.scss';
+
+.service-hotline {
+ display: flex;
+ flex-direction: column;
+ min-height: 100vh;
+ background: $color-bg-section;
+}
+
+.service-hotline__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;
+}
+
+.service-hotline__back {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 64rpx;
+ height: 64rpx;
+}
+
+.service-hotline__back-icon {
+ width: 40rpx;
+ height: 40rpx;
+}
+
+.service-hotline__navbar-title {
+ font-size: 34rpx;
+ font-weight: 600;
+ color: $color-text-primary;
+}
+
+.service-hotline__navbar-placeholder {
+ width: 64rpx;
+ height: 64rpx;
+}
+
+.service-hotline__body {
+ padding: 24rpx;
+}
+
+.service-hotline__card {
+ display: flex;
+ align-items: center;
+ padding: 32rpx 28rpx;
+ background: $color-bg-page;
+ border-radius: 20rpx;
+ box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.04);
+}
+
+.service-hotline__icon {
+ flex-shrink: 0;
+ width: 96rpx;
+ height: 96rpx;
+}
+
+.service-hotline__info {
+ flex: 1;
+ min-width: 0;
+ margin-left: 24rpx;
+}
+
+.service-hotline__title {
+ display: block;
+ font-size: 30rpx;
+ font-weight: 600;
+ color: $color-text-primary;
+ word-break: break-all;
+}
+
+.service-hotline__hours {
+ display: block;
+ margin-top: 12rpx;
+ font-size: 24rpx;
+ color: $color-text-secondary;
+}
+
+.service-hotline__dial-btn {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ height: 88rpx;
+ margin-top: 32rpx;
+ font-size: 30rpx;
+ font-weight: 600;
+ color: #ffffff;
+ background: $color-primary;
+ border-radius: 12rpx;
+}
diff --git a/src/pages/personal-center/service-hotline/index.vue b/src/pages/personal-center/service-hotline/index.vue
new file mode 100644
index 0000000..8a24936
--- /dev/null
+++ b/src/pages/personal-center/service-hotline/index.vue
@@ -0,0 +1,75 @@
+
+
+
+
+
+
+
+
+
+ 专属客服热线
+
+
+
+
+
+
+
+
+
+ {{ hotlineDisplay }}
+
+
+ {{ workHours }}
+
+
+
+
+
+ 点击拨号
+
+
+
+
+
+
diff --git a/src/static/icon/service-hotline.svg b/src/static/icon/service-hotline.svg
new file mode 100644
index 0000000..2042334
--- /dev/null
+++ b/src/static/icon/service-hotline.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/types/address.ts b/src/types/address.ts
new file mode 100644
index 0000000..9b9806c
--- /dev/null
+++ b/src/types/address.ts
@@ -0,0 +1,8 @@
+export interface AddressItem {
+ id: string
+ name: string
+ phone: string
+ region: string
+ detail: string
+ isDefault: boolean
+}
diff --git a/src/types/contract.ts b/src/types/contract.ts
new file mode 100644
index 0000000..e80f807
--- /dev/null
+++ b/src/types/contract.ts
@@ -0,0 +1,23 @@
+export type ContractTab = 'all' | 'pending' | 'signed'
+
+export type ContractSignStatus = 'signed' | 'pending'
+
+export type ContractBadgeType = 'signed' | 'in_progress' | 'pending_sign'
+
+export interface ContractBadge {
+ type: ContractBadgeType
+ label: string
+}
+
+export interface ContractItem {
+ id: string
+ contractNo: string
+ title: string
+ type: string
+ relatedLabel: string
+ relatedValue: string
+ signStatus: ContractSignStatus
+ badges: ContractBadge[]
+ showDownload: boolean
+ showSign: boolean
+}
diff --git a/src/types/invoice-list.ts b/src/types/invoice-list.ts
new file mode 100644
index 0000000..fae36cb
--- /dev/null
+++ b/src/types/invoice-list.ts
@@ -0,0 +1,9 @@
+export type InvoiceHeaderType = 'enterprise' | 'personal'
+
+export interface InvoiceHeaderItem {
+ id: string
+ title: string
+ type: InvoiceHeaderType
+ taxNo: string
+ isDefault: boolean
+}
diff --git a/src/types/invoice.ts b/src/types/invoice.ts
new file mode 100644
index 0000000..40b4eee
--- /dev/null
+++ b/src/types/invoice.ts
@@ -0,0 +1,14 @@
+export type InvoiceRecordStatus = 'all' | 'invoiced' | 'not_invoiced' | 'invoicing'
+
+export type InvoiceRecordItemStatus = Exclude
+
+export interface InvoiceRecordItem {
+ id: string
+ title: string
+ ratio: string
+ amount: number
+ transactionNo: string
+ relatedProject: string
+ invoiceTime: string
+ status: InvoiceRecordItemStatus
+}
diff --git a/src/types/qualification.ts b/src/types/qualification.ts
new file mode 100644
index 0000000..b871fba
--- /dev/null
+++ b/src/types/qualification.ts
@@ -0,0 +1,12 @@
+export type QualificationStatus = 'reviewing' | 'approved' | 'rejected' | 'draft'
+
+export interface QualificationItem {
+ id: string
+ type: string
+ certificateName: string
+ certificateNo: string
+ issuer: string
+ validFrom: string
+ validTo: string
+ status: QualificationStatus
+}
diff --git a/src/utils/quick-entry-navigate.ts b/src/utils/quick-entry-navigate.ts
new file mode 100644
index 0000000..abe0b76
--- /dev/null
+++ b/src/utils/quick-entry-navigate.ts
@@ -0,0 +1,39 @@
+const TAB_BAR_PATHS = new Set([
+ '/pages/home-index/index',
+ '/pages/case-library/index',
+ '/pages/message-center/index',
+ '/pages/project-center/index',
+ '/pages/personal-center/index',
+])
+
+const QUICK_ENTRY_ROUTES: Record = {
+ project: '/pages/project-center/index',
+ report: '/pages/home-index/requirement-submit/index',
+ service: '/pages/message-center/customer-service/index',
+ contract: '/pages/contract-center/index',
+ finance: '/pages/personal-center/finance/index',
+ personal: '/pages/personal-center/index',
+ message: '/pages/message-center/index',
+ security: '/pages/personal-center/account-security/index',
+ feedback: '/pages/personal-center/feedback/index',
+ 'contract-list': '/pages/contract-center/index',
+ invoice: '/pages/personal-center/invoice/index',
+ address: '/pages/personal-center/address/index',
+ favorites: '/pages/case-library/favorites/index',
+ qualification: '/pages/personal-center/qualification-list/index',
+}
+
+export function navigateQuickEntry(id: string, label: string) {
+ const path = QUICK_ENTRY_ROUTES[id]
+ if (!path) {
+ uni.showToast({ title: `${label}开发中`, icon: 'none' })
+ return
+ }
+
+ if (TAB_BAR_PATHS.has(path)) {
+ uni.redirectTo({ url: path })
+ return
+ }
+
+ uni.navigateTo({ url: path })
+}