feat:个人中心完成
This commit is contained in:
229
src/pages/contract-center/index.scss
Normal file
229
src/pages/contract-center/index.scss
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
178
src/pages/contract-center/index.vue
Normal file
178
src/pages/contract-center/index.vue
Normal file
@@ -0,0 +1,178 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, ref } from 'vue'
|
||||
import { CONTRACT_LIST, CONTRACT_TABS } from '@/constants/contract'
|
||||
import type { ContractItem, ContractTab } from '@/types/contract'
|
||||
import { navigateBackWithFallback } from '@/utils/navigate-back'
|
||||
|
||||
const keyword = ref('')
|
||||
const activeTab = ref<ContractTab>('all')
|
||||
const contractList = ref(CONTRACT_LIST.map(item => ({ ...item })))
|
||||
|
||||
const filteredContracts = computed(() => {
|
||||
const text = keyword.value.trim()
|
||||
|
||||
return contractList.value.filter((item) => {
|
||||
const matchTab = activeTab.value === 'all'
|
||||
|| (activeTab.value === 'pending' && item.signStatus === 'pending')
|
||||
|| (activeTab.value === 'signed' && item.signStatus === 'signed')
|
||||
const matchKeyword = !text
|
||||
|| item.title.includes(text)
|
||||
|| item.type.includes(text)
|
||||
|| item.contractNo.includes(text)
|
||||
|| item.relatedValue.includes(text)
|
||||
|
||||
return matchTab && matchKeyword
|
||||
})
|
||||
})
|
||||
|
||||
function handleBack() {
|
||||
navigateBackWithFallback('/pages/personal-center/index')
|
||||
}
|
||||
|
||||
function badgeClass(type: ContractItem['badges'][number]['type']) {
|
||||
return `contract-center__badge--${type}`
|
||||
}
|
||||
|
||||
function handleViewDetail(item: ContractItem) {
|
||||
uni.showToast({ title: `查看${item.title}详情开发中`, icon: 'none' })
|
||||
}
|
||||
|
||||
function handlePreview(item: ContractItem) {
|
||||
uni.showToast({ title: `预览${item.contractNo}开发中`, icon: 'none' })
|
||||
}
|
||||
|
||||
function handleDownload(item: ContractItem) {
|
||||
uni.showToast({ title: `下载${item.contractNo}开发中`, icon: 'none' })
|
||||
}
|
||||
|
||||
function handleSign(item: ContractItem) {
|
||||
uni.showToast({ title: `签署${item.contractNo}开发中`, icon: 'none' })
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="contract-center">
|
||||
<view class="contract-center__header">
|
||||
<view
|
||||
class="contract-center__back"
|
||||
@tap="handleBack"
|
||||
>
|
||||
<image
|
||||
class="contract-center__back-icon"
|
||||
src="/static/icons/icon-back.svg"
|
||||
mode="aspectFit"
|
||||
/>
|
||||
</view>
|
||||
<text class="contract-center__title">
|
||||
合同中心
|
||||
</text>
|
||||
<view class="contract-center__header-placeholder" />
|
||||
</view>
|
||||
|
||||
<view class="contract-center__toolbar">
|
||||
<view class="contract-center__search">
|
||||
<image
|
||||
class="contract-center__search-icon"
|
||||
src="/static/icons/icon-search.svg"
|
||||
mode="aspectFit"
|
||||
/>
|
||||
<input
|
||||
v-model="keyword"
|
||||
class="contract-center__search-input"
|
||||
placeholder="搜索项目标题、类型..."
|
||||
placeholder-class="contract-center__search-placeholder"
|
||||
>
|
||||
</view>
|
||||
|
||||
<view class="contract-center__tabs">
|
||||
<view
|
||||
v-for="tab in CONTRACT_TABS"
|
||||
:key="tab.value"
|
||||
class="contract-center__tab"
|
||||
:class="{ 'contract-center__tab--active': activeTab === tab.value }"
|
||||
@tap="activeTab = tab.value"
|
||||
>
|
||||
<text class="contract-center__tab-text">
|
||||
{{ tab.label }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<scroll-view
|
||||
class="contract-center__scroll"
|
||||
scroll-y
|
||||
:show-scrollbar="false"
|
||||
>
|
||||
<view class="contract-center__list">
|
||||
<view
|
||||
v-for="item in filteredContracts"
|
||||
:key="item.id"
|
||||
class="contract-center__card"
|
||||
>
|
||||
<view class="contract-center__card-top">
|
||||
<text class="contract-center__contract-no">
|
||||
合同编号: {{ item.contractNo }}
|
||||
</text>
|
||||
<view class="contract-center__badges">
|
||||
<text
|
||||
v-for="badge in item.badges"
|
||||
:key="badge.label"
|
||||
class="contract-center__badge"
|
||||
:class="badgeClass(badge.type)"
|
||||
>
|
||||
{{ badge.label }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="contract-center__title-row">
|
||||
<text class="contract-center__card-title">
|
||||
{{ item.title }}
|
||||
</text>
|
||||
<text
|
||||
class="contract-center__detail-link"
|
||||
@tap="handleViewDetail(item)"
|
||||
>
|
||||
查看详情
|
||||
</text>
|
||||
</view>
|
||||
|
||||
<text class="contract-center__related">
|
||||
{{ item.relatedLabel }}: {{ item.relatedValue }}
|
||||
</text>
|
||||
|
||||
<view
|
||||
class="contract-center__actions"
|
||||
:class="{ 'contract-center__actions--single': !item.showDownload && !item.showSign }"
|
||||
>
|
||||
<view
|
||||
class="contract-center__action-btn contract-center__action-btn--outline"
|
||||
@tap="handlePreview(item)"
|
||||
>
|
||||
预览
|
||||
</view>
|
||||
<view
|
||||
v-if="item.showDownload"
|
||||
class="contract-center__action-btn contract-center__action-btn--outline"
|
||||
@tap="handleDownload(item)"
|
||||
>
|
||||
下载
|
||||
</view>
|
||||
<view
|
||||
v-if="item.showSign"
|
||||
class="contract-center__action-btn contract-center__action-btn--primary"
|
||||
@tap="handleSign(item)"
|
||||
>
|
||||
立即签署
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import './index.scss';
|
||||
</style>
|
||||
@@ -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)"
|
||||
>
|
||||
<image
|
||||
class="home__quick-icon"
|
||||
|
||||
209
src/pages/personal-center/address/index.scss
Normal file
209
src/pages/personal-center/address/index.scss
Normal file
@@ -0,0 +1,209 @@
|
||||
@import '@/styles/variables.scss';
|
||||
|
||||
.address-page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 100vh;
|
||||
background: $color-bg-section;
|
||||
}
|
||||
|
||||
.address-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;
|
||||
}
|
||||
|
||||
.address-page__back {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 64rpx;
|
||||
height: 64rpx;
|
||||
}
|
||||
|
||||
.address-page__back-icon {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
}
|
||||
|
||||
.address-page__navbar-title {
|
||||
font-size: 34rpx;
|
||||
font-weight: 600;
|
||||
color: $color-text-primary;
|
||||
}
|
||||
|
||||
.address-page__navbar-placeholder {
|
||||
width: 64rpx;
|
||||
height: 64rpx;
|
||||
}
|
||||
|
||||
.address-page__scroll {
|
||||
flex: 1;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.address-page__content {
|
||||
padding: 20rpx 24rpx calc(140rpx + env(safe-area-inset-bottom));
|
||||
}
|
||||
|
||||
.address-page__card {
|
||||
padding: 28rpx 24rpx;
|
||||
margin-bottom: 20rpx;
|
||||
background: $color-bg-page;
|
||||
border-radius: 20rpx;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.address-page__card-top {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.address-page__name {
|
||||
font-size: 30rpx;
|
||||
font-weight: 600;
|
||||
color: $color-text-primary;
|
||||
}
|
||||
|
||||
.address-page__phone {
|
||||
margin-left: 24rpx;
|
||||
font-size: 30rpx;
|
||||
font-weight: 600;
|
||||
color: $color-text-primary;
|
||||
}
|
||||
|
||||
.address-page__address {
|
||||
display: block;
|
||||
margin-top: 16rpx;
|
||||
font-size: 26rpx;
|
||||
line-height: 1.5;
|
||||
color: #222222;
|
||||
}
|
||||
|
||||
.address-page__card-footer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-top: 28rpx;
|
||||
}
|
||||
|
||||
.address-page__default {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.address-page__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;
|
||||
}
|
||||
}
|
||||
|
||||
.address-page__radio-dot {
|
||||
width: 14rpx;
|
||||
height: 14rpx;
|
||||
background: $color-primary;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.address-page__default-text {
|
||||
margin-left: 12rpx;
|
||||
font-size: 24rpx;
|
||||
color: #222222;
|
||||
}
|
||||
|
||||
.address-page__actions {
|
||||
display: flex;
|
||||
gap: 32rpx;
|
||||
}
|
||||
|
||||
.address-page__action {
|
||||
font-size: 26rpx;
|
||||
color: $color-text-primary;
|
||||
}
|
||||
|
||||
.address-page__footer {
|
||||
flex-shrink: 0;
|
||||
padding: 16rpx 24rpx calc(16rpx + env(safe-area-inset-bottom));
|
||||
background: $color-bg-page;
|
||||
border-top: 1rpx solid #f0f0f0;
|
||||
}
|
||||
|
||||
.address-page__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;
|
||||
}
|
||||
|
||||
.address-page__sheet-title {
|
||||
display: block;
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: $color-text-primary;
|
||||
}
|
||||
|
||||
.address-page__form {
|
||||
margin-top: 8rpx;
|
||||
}
|
||||
|
||||
.address-page__form-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-height: 96rpx;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
|
||||
&--last {
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
|
||||
.address-page__form-label {
|
||||
flex-shrink: 0;
|
||||
width: 160rpx;
|
||||
font-size: 28rpx;
|
||||
color: $color-text-primary;
|
||||
}
|
||||
|
||||
.address-page__form-input {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
font-size: 28rpx;
|
||||
color: $color-text-primary;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.address-page__form-placeholder {
|
||||
color: $color-text-placeholder;
|
||||
}
|
||||
|
||||
.address-page__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;
|
||||
}
|
||||
284
src/pages/personal-center/address/index.vue
Normal file
284
src/pages/personal-center/address/index.vue
Normal file
@@ -0,0 +1,284 @@
|
||||
<script setup lang="ts">
|
||||
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<string | null>(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' })
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="address-page">
|
||||
<view class="address-page__navbar">
|
||||
<view
|
||||
class="address-page__back"
|
||||
@tap="handleBack"
|
||||
>
|
||||
<image
|
||||
class="address-page__back-icon"
|
||||
src="/static/icons/icon-back.svg"
|
||||
mode="aspectFit"
|
||||
/>
|
||||
</view>
|
||||
<text class="address-page__navbar-title">
|
||||
地址管理
|
||||
</text>
|
||||
<view class="address-page__navbar-placeholder" />
|
||||
</view>
|
||||
|
||||
<scroll-view
|
||||
class="address-page__scroll"
|
||||
scroll-y
|
||||
:show-scrollbar="false"
|
||||
>
|
||||
<view class="address-page__content">
|
||||
<view
|
||||
v-for="item in addressList"
|
||||
:key="item.id"
|
||||
class="address-page__card"
|
||||
>
|
||||
<view class="address-page__card-top">
|
||||
<text class="address-page__name">
|
||||
{{ item.name }}
|
||||
</text>
|
||||
<text class="address-page__phone">
|
||||
{{ item.phone }}
|
||||
</text>
|
||||
</view>
|
||||
<text class="address-page__address">
|
||||
{{ formatAddress(item) }}
|
||||
</text>
|
||||
<view class="address-page__card-footer">
|
||||
<view
|
||||
class="address-page__default"
|
||||
@tap="handleSetDefault(item)"
|
||||
>
|
||||
<view
|
||||
class="address-page__radio"
|
||||
:class="{ 'address-page__radio--checked': item.isDefault }"
|
||||
>
|
||||
<view
|
||||
v-if="item.isDefault"
|
||||
class="address-page__radio-dot"
|
||||
/>
|
||||
</view>
|
||||
<text class="address-page__default-text">
|
||||
默认地址
|
||||
</text>
|
||||
</view>
|
||||
<view class="address-page__actions">
|
||||
<text
|
||||
class="address-page__action"
|
||||
@tap="handleEdit(item)"
|
||||
>
|
||||
编辑
|
||||
</text>
|
||||
<text
|
||||
class="address-page__action"
|
||||
@tap="handleDelete(item)"
|
||||
>
|
||||
删除
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<view class="address-page__footer">
|
||||
<view
|
||||
class="address-page__add-btn"
|
||||
@tap="handleAdd"
|
||||
>
|
||||
+ 新增地址
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<BottomSheet
|
||||
:visible="sheetVisible"
|
||||
@close="closeSheet"
|
||||
>
|
||||
<text class="address-page__sheet-title">
|
||||
{{ sheetTitle }}
|
||||
</text>
|
||||
|
||||
<view class="address-page__form">
|
||||
<view class="address-page__form-row">
|
||||
<text class="address-page__form-label">
|
||||
收货人
|
||||
</text>
|
||||
<input
|
||||
v-model="formName"
|
||||
class="address-page__form-input"
|
||||
placeholder="请输入收货人姓名"
|
||||
placeholder-class="address-page__form-placeholder"
|
||||
>
|
||||
</view>
|
||||
<view class="address-page__form-row">
|
||||
<text class="address-page__form-label">
|
||||
手机号码
|
||||
</text>
|
||||
<input
|
||||
v-model="formPhone"
|
||||
class="address-page__form-input"
|
||||
type="number"
|
||||
placeholder="请输入联系电话"
|
||||
placeholder-class="address-page__form-placeholder"
|
||||
>
|
||||
</view>
|
||||
<view class="address-page__form-row">
|
||||
<text class="address-page__form-label">
|
||||
地区
|
||||
</text>
|
||||
<input
|
||||
v-model="formRegion"
|
||||
class="address-page__form-input"
|
||||
placeholder="请输入所在地区"
|
||||
placeholder-class="address-page__form-placeholder"
|
||||
>
|
||||
</view>
|
||||
<view class="address-page__form-row address-page__form-row--last">
|
||||
<text class="address-page__form-label">
|
||||
详细地址
|
||||
</text>
|
||||
<input
|
||||
v-model="formDetail"
|
||||
class="address-page__form-input"
|
||||
placeholder="请输入街道、小区门牌等详细地址"
|
||||
placeholder-class="address-page__form-placeholder"
|
||||
>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view
|
||||
class="address-page__sheet-submit"
|
||||
@tap="handleSubmit"
|
||||
>
|
||||
{{ sheetSubmitText }}
|
||||
</view>
|
||||
</BottomSheet>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import './index.scss';
|
||||
</style>
|
||||
@@ -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' })
|
||||
}
|
||||
</script>
|
||||
|
||||
254
src/pages/personal-center/invoice-list/index.scss
Normal file
254
src/pages/personal-center/invoice-list/index.scss
Normal file
@@ -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;
|
||||
}
|
||||
290
src/pages/personal-center/invoice-list/index.vue
Normal file
290
src/pages/personal-center/invoice-list/index.vue
Normal file
@@ -0,0 +1,290 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, ref } from 'vue'
|
||||
import BottomSheet from '@/components/BottomSheet.vue'
|
||||
import {
|
||||
INVOICE_HEADER_FORM_TYPE_OPTIONS,
|
||||
INVOICE_HEADER_TYPE_LABEL,
|
||||
} from '@/constants/invoice-list'
|
||||
import { useInvoiceHeaders } from '@/hooks/use-invoice-headers'
|
||||
import type { InvoiceHeaderItem } from '@/types/invoice-list'
|
||||
import { navigateBackWithFallback } from '@/utils/navigate-back'
|
||||
|
||||
const { invoiceHeaders: invoiceList } = useInvoiceHeaders()
|
||||
const sheetVisible = ref(false)
|
||||
const editingId = ref<string | null>(null)
|
||||
const headerType = ref<InvoiceHeaderItem['type']>('enterprise')
|
||||
const headerTitle = ref('')
|
||||
const taxNo = ref('')
|
||||
|
||||
const isEditing = computed(() => editingId.value !== null)
|
||||
const sheetTitle = computed(() => (isEditing.value ? '编辑抬头' : '新增抬头'))
|
||||
const sheetSubmitText = computed(() => (isEditing.value ? '保存' : '+ 新增抬头'))
|
||||
|
||||
const formTypeOptions = INVOICE_HEADER_FORM_TYPE_OPTIONS
|
||||
|
||||
function handleBack() {
|
||||
navigateBackWithFallback('/pages/personal-center/invoice/index')
|
||||
}
|
||||
|
||||
function typeLabel(type: InvoiceHeaderItem['type']) {
|
||||
return INVOICE_HEADER_TYPE_LABEL[type]
|
||||
}
|
||||
|
||||
function handleSetDefault(item: InvoiceHeaderItem) {
|
||||
invoiceList.value = invoiceList.value.map(row => ({
|
||||
...row,
|
||||
isDefault: row.id === item.id,
|
||||
}))
|
||||
}
|
||||
|
||||
function handleEdit(item: InvoiceHeaderItem) {
|
||||
editingId.value = item.id
|
||||
headerType.value = item.type
|
||||
headerTitle.value = item.title
|
||||
taxNo.value = item.taxNo
|
||||
sheetVisible.value = true
|
||||
}
|
||||
|
||||
function handleDelete(item: InvoiceHeaderItem) {
|
||||
uni.showModal({
|
||||
title: '删除抬头',
|
||||
content: `确认删除「${item.title}」吗?`,
|
||||
confirmText: '删除',
|
||||
confirmColor: '#2563EB',
|
||||
success: (res) => {
|
||||
if (!res.confirm)
|
||||
return
|
||||
|
||||
invoiceList.value = invoiceList.value.filter(row => row.id !== item.id)
|
||||
if (invoiceList.value.length && !invoiceList.value.some(row => row.isDefault))
|
||||
invoiceList.value[0].isDefault = true
|
||||
|
||||
uni.showToast({ title: '已删除', icon: 'none' })
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
function resetForm() {
|
||||
editingId.value = null
|
||||
headerType.value = 'enterprise'
|
||||
headerTitle.value = ''
|
||||
taxNo.value = ''
|
||||
}
|
||||
|
||||
function handleAdd() {
|
||||
resetForm()
|
||||
sheetVisible.value = true
|
||||
}
|
||||
|
||||
function closeSheet() {
|
||||
sheetVisible.value = false
|
||||
editingId.value = null
|
||||
}
|
||||
|
||||
function handleSelectType(type: InvoiceHeaderItem['type']) {
|
||||
headerType.value = type
|
||||
}
|
||||
|
||||
function handleSubmit() {
|
||||
if (!headerTitle.value.trim()) {
|
||||
uni.showToast({ title: '请填写发票抬头', icon: 'none' })
|
||||
return
|
||||
}
|
||||
if (!taxNo.value.trim()) {
|
||||
uni.showToast({ title: '请输入税号', icon: 'none' })
|
||||
return
|
||||
}
|
||||
|
||||
const payload = {
|
||||
title: headerTitle.value.trim(),
|
||||
type: headerType.value,
|
||||
taxNo: taxNo.value.trim(),
|
||||
}
|
||||
|
||||
if (editingId.value) {
|
||||
invoiceList.value = invoiceList.value.map(row => (
|
||||
row.id === editingId.value
|
||||
? { ...row, ...payload }
|
||||
: row
|
||||
))
|
||||
closeSheet()
|
||||
uni.showToast({ title: '保存成功', icon: 'none' })
|
||||
return
|
||||
}
|
||||
|
||||
const isFirst = invoiceList.value.length === 0
|
||||
invoiceList.value.push({
|
||||
id: `${Date.now()}`,
|
||||
...payload,
|
||||
isDefault: isFirst,
|
||||
})
|
||||
|
||||
closeSheet()
|
||||
uni.showToast({ title: '新增成功', icon: 'none' })
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="invoice-list">
|
||||
<view class="invoice-list__navbar">
|
||||
<view
|
||||
class="invoice-list__back"
|
||||
@tap="handleBack"
|
||||
>
|
||||
<image
|
||||
class="invoice-list__back-icon"
|
||||
src="/static/icons/icon-back.svg"
|
||||
mode="aspectFit"
|
||||
/>
|
||||
</view>
|
||||
<text class="invoice-list__navbar-title">
|
||||
抬头管理
|
||||
</text>
|
||||
<view class="invoice-list__navbar-placeholder" />
|
||||
</view>
|
||||
|
||||
<scroll-view
|
||||
class="invoice-list__scroll"
|
||||
scroll-y
|
||||
:show-scrollbar="false"
|
||||
>
|
||||
<view class="invoice-list__content">
|
||||
<view
|
||||
v-for="item in invoiceList"
|
||||
:key="item.id"
|
||||
class="invoice-list__card"
|
||||
>
|
||||
<view class="invoice-list__card-top">
|
||||
<text class="invoice-list__card-title">
|
||||
{{ item.title }}
|
||||
</text>
|
||||
<text
|
||||
class="invoice-list__tag"
|
||||
:class="`invoice-list__tag--${item.type}`"
|
||||
>
|
||||
{{ typeLabel(item.type) }}
|
||||
</text>
|
||||
</view>
|
||||
<text class="invoice-list__tax-no">
|
||||
{{ item.taxNo }}
|
||||
</text>
|
||||
<view class="invoice-list__card-footer">
|
||||
<view
|
||||
class="invoice-list__default"
|
||||
@tap="handleSetDefault(item)"
|
||||
>
|
||||
<view
|
||||
class="invoice-list__radio"
|
||||
:class="{ 'invoice-list__radio--checked': item.isDefault }"
|
||||
>
|
||||
<view
|
||||
v-if="item.isDefault"
|
||||
class="invoice-list__radio-dot"
|
||||
/>
|
||||
</view>
|
||||
<text class="invoice-list__default-text">
|
||||
默认抬头
|
||||
</text>
|
||||
</view>
|
||||
<view class="invoice-list__actions">
|
||||
<text
|
||||
class="invoice-list__action"
|
||||
@tap="handleEdit(item)"
|
||||
>
|
||||
编辑
|
||||
</text>
|
||||
<text
|
||||
class="invoice-list__action"
|
||||
@tap="handleDelete(item)"
|
||||
>
|
||||
删除
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<view class="invoice-list__footer">
|
||||
<view
|
||||
class="invoice-list__add-btn"
|
||||
@tap="handleAdd"
|
||||
>
|
||||
+ 新增抬头
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<BottomSheet
|
||||
:visible="sheetVisible"
|
||||
@close="closeSheet"
|
||||
>
|
||||
<text class="invoice-list__sheet-title">
|
||||
{{ sheetTitle }}
|
||||
</text>
|
||||
|
||||
<view class="invoice-list__sheet-field">
|
||||
<text class="invoice-list__sheet-label">
|
||||
抬头类型
|
||||
</text>
|
||||
<view class="invoice-list__type-row">
|
||||
<view
|
||||
v-for="option in formTypeOptions"
|
||||
:key="option.value"
|
||||
class="invoice-list__type-option"
|
||||
@tap="handleSelectType(option.value)"
|
||||
>
|
||||
<view
|
||||
class="invoice-list__type-radio"
|
||||
:class="{ 'invoice-list__type-radio--checked': headerType === option.value }"
|
||||
>
|
||||
<text
|
||||
v-if="headerType === option.value"
|
||||
class="invoice-list__type-check"
|
||||
>
|
||||
✓
|
||||
</text>
|
||||
</view>
|
||||
<text class="invoice-list__type-label">
|
||||
{{ option.label }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="invoice-list__sheet-field">
|
||||
<text class="invoice-list__sheet-label">
|
||||
发票抬头
|
||||
</text>
|
||||
<input
|
||||
v-model="headerTitle"
|
||||
class="invoice-list__sheet-input"
|
||||
placeholder="请填写发票抬头"
|
||||
placeholder-class="invoice-list__sheet-placeholder"
|
||||
>
|
||||
</view>
|
||||
|
||||
<view class="invoice-list__sheet-field">
|
||||
<text class="invoice-list__sheet-label">
|
||||
税号
|
||||
</text>
|
||||
<input
|
||||
v-model="taxNo"
|
||||
class="invoice-list__sheet-input"
|
||||
placeholder="请输入税号"
|
||||
placeholder-class="invoice-list__sheet-placeholder"
|
||||
>
|
||||
</view>
|
||||
|
||||
<view
|
||||
class="invoice-list__sheet-submit"
|
||||
@tap="handleSubmit"
|
||||
>
|
||||
{{ sheetSubmitText }}
|
||||
</view>
|
||||
</BottomSheet>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import './index.scss';
|
||||
</style>
|
||||
238
src/pages/personal-center/invoice/index.scss
Normal file
238
src/pages/personal-center/invoice/index.scss
Normal file
@@ -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;
|
||||
}
|
||||
175
src/pages/personal-center/invoice/index.vue
Normal file
175
src/pages/personal-center/invoice/index.vue
Normal file
@@ -0,0 +1,175 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, ref } from 'vue'
|
||||
import { INVOICE_RECORD_LIST, INVOICE_RECORD_TABS } from '@/constants/invoice'
|
||||
import { INVOICE_HEADER_TYPE_LABEL } from '@/constants/invoice-list'
|
||||
import { useInvoiceHeaders } from '@/hooks/use-invoice-headers'
|
||||
import type { InvoiceRecordStatus } from '@/types/invoice'
|
||||
import { navigateBackWithFallback } from '@/utils/navigate-back'
|
||||
|
||||
const { defaultHeader } = useInvoiceHeaders()
|
||||
|
||||
const keyword = ref('')
|
||||
const activeTab = ref<InvoiceRecordStatus>('all')
|
||||
const recordList = ref(INVOICE_RECORD_LIST.map(item => ({ ...item })))
|
||||
|
||||
const filteredRecords = computed(() => {
|
||||
const text = keyword.value.trim()
|
||||
|
||||
return recordList.value.filter((item) => {
|
||||
const matchTab = activeTab.value === 'all' || item.status === activeTab.value
|
||||
const matchKeyword = !text
|
||||
|| item.title.includes(text)
|
||||
|| item.relatedProject.includes(text)
|
||||
|| item.transactionNo.includes(text)
|
||||
|
||||
return matchTab && matchKeyword
|
||||
})
|
||||
})
|
||||
|
||||
function handleBack() {
|
||||
navigateBackWithFallback('/pages/personal-center/index')
|
||||
}
|
||||
|
||||
function handleManageHeader() {
|
||||
uni.navigateTo({
|
||||
url: '/pages/personal-center/invoice-list/index',
|
||||
})
|
||||
}
|
||||
|
||||
function formatAmount(amount: number) {
|
||||
return `¥ ${amount}`
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="invoice-page">
|
||||
<view class="invoice-page__navbar">
|
||||
<view
|
||||
class="invoice-page__back"
|
||||
@tap="handleBack"
|
||||
>
|
||||
<image
|
||||
class="invoice-page__back-icon"
|
||||
src="/static/icons/icon-back.svg"
|
||||
mode="aspectFit"
|
||||
/>
|
||||
</view>
|
||||
<text class="invoice-page__navbar-title">
|
||||
发票列表
|
||||
</text>
|
||||
<view class="invoice-page__navbar-placeholder" />
|
||||
</view>
|
||||
|
||||
<scroll-view
|
||||
class="invoice-page__scroll"
|
||||
scroll-y
|
||||
:show-scrollbar="false"
|
||||
>
|
||||
<view class="invoice-page__content">
|
||||
<view class="invoice-page__section">
|
||||
<view class="invoice-page__section-header">
|
||||
<text class="invoice-page__section-title">
|
||||
默认发票抬头
|
||||
</text>
|
||||
<view
|
||||
class="invoice-page__manage"
|
||||
@tap="handleManageHeader"
|
||||
>
|
||||
<text class="invoice-page__manage-text">
|
||||
管理抬头
|
||||
</text>
|
||||
<text class="invoice-page__manage-arrow">
|
||||
>
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view
|
||||
v-if="defaultHeader"
|
||||
class="invoice-page__header-card"
|
||||
>
|
||||
<view class="invoice-page__header-top">
|
||||
<text class="invoice-page__default-tag">
|
||||
默认
|
||||
</text>
|
||||
<text class="invoice-page__header-title">
|
||||
{{ defaultHeader.title }}
|
||||
</text>
|
||||
<text
|
||||
class="invoice-page__header-type"
|
||||
:class="`invoice-page__header-type--${defaultHeader.type}`"
|
||||
>
|
||||
{{ INVOICE_HEADER_TYPE_LABEL[defaultHeader.type] }}
|
||||
</text>
|
||||
</view>
|
||||
<text class="invoice-page__header-tax">
|
||||
{{ defaultHeader.taxNo }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="invoice-page__section">
|
||||
<text class="invoice-page__section-title">
|
||||
发票记录
|
||||
</text>
|
||||
|
||||
<view class="invoice-page__search">
|
||||
<image
|
||||
class="invoice-page__search-icon"
|
||||
src="/static/icons/icon-search.svg"
|
||||
mode="aspectFit"
|
||||
/>
|
||||
<input
|
||||
v-model="keyword"
|
||||
class="invoice-page__search-input"
|
||||
placeholder="搜索案例标题、描述、标签..."
|
||||
placeholder-class="invoice-page__search-placeholder"
|
||||
>
|
||||
</view>
|
||||
|
||||
<view class="invoice-page__tabs">
|
||||
<view
|
||||
v-for="tab in INVOICE_RECORD_TABS"
|
||||
:key="tab.value"
|
||||
class="invoice-page__tab"
|
||||
:class="{ 'invoice-page__tab--active': activeTab === tab.value }"
|
||||
@tap="activeTab = tab.value"
|
||||
>
|
||||
<text class="invoice-page__tab-text">
|
||||
{{ tab.label }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view
|
||||
v-for="item in filteredRecords"
|
||||
:key="item.id"
|
||||
class="invoice-page__record-card"
|
||||
>
|
||||
<view class="invoice-page__record-header">
|
||||
<text class="invoice-page__record-title">
|
||||
{{ item.title }} {{ item.ratio }}
|
||||
</text>
|
||||
<text class="invoice-page__record-amount">
|
||||
{{ formatAmount(item.amount) }}
|
||||
</text>
|
||||
</view>
|
||||
<text class="invoice-page__record-row">
|
||||
交易流水号:{{ item.transactionNo }}
|
||||
</text>
|
||||
<text class="invoice-page__record-row">
|
||||
关联项目:{{ item.relatedProject }}
|
||||
</text>
|
||||
<text class="invoice-page__record-row">
|
||||
开票时间:{{ item.invoiceTime }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import './index.scss';
|
||||
</style>
|
||||
131
src/pages/personal-center/qualification-list/index.scss
Normal file
131
src/pages/personal-center/qualification-list/index.scss
Normal file
@@ -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;
|
||||
}
|
||||
131
src/pages/personal-center/qualification-list/index.vue
Normal file
131
src/pages/personal-center/qualification-list/index.vue
Normal file
@@ -0,0 +1,131 @@
|
||||
<script setup lang="ts">
|
||||
import { QUALIFICATION_STATUS_LABEL } from '@/constants/qualification'
|
||||
import { useQualifications } from '@/hooks/use-qualifications'
|
||||
import type { QualificationItem } from '@/types/qualification'
|
||||
import { navigateBackWithFallback } from '@/utils/navigate-back'
|
||||
|
||||
const { qualifications } = useQualifications()
|
||||
|
||||
function handleBack() {
|
||||
navigateBackWithFallback('/pages/personal-center/index')
|
||||
}
|
||||
|
||||
function formatValidity(item: QualificationItem) {
|
||||
return `${item.validFrom}至${item.validTo}`
|
||||
}
|
||||
|
||||
function statusLabel(status: QualificationItem['status']) {
|
||||
return QUALIFICATION_STATUS_LABEL[status]
|
||||
}
|
||||
|
||||
function handleAdd() {
|
||||
uni.navigateTo({
|
||||
url: '/pages/personal-center/qualification-upload/index',
|
||||
})
|
||||
}
|
||||
|
||||
function handleEdit(item: QualificationItem) {
|
||||
uni.navigateTo({
|
||||
url: `/pages/personal-center/qualification-upload/index?id=${encodeURIComponent(item.id)}`,
|
||||
})
|
||||
}
|
||||
|
||||
function handleDelete(item: QualificationItem) {
|
||||
uni.showModal({
|
||||
title: '删除资质',
|
||||
content: `确认删除「${item.certificateName}」吗?`,
|
||||
confirmText: '删除',
|
||||
confirmColor: '#2563EB',
|
||||
success: (res) => {
|
||||
if (!res.confirm)
|
||||
return
|
||||
|
||||
qualifications.value = qualifications.value.filter(row => row.id !== item.id)
|
||||
uni.showToast({ title: '已删除', icon: 'none' })
|
||||
},
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="qualification-list">
|
||||
<view class="qualification-list__navbar">
|
||||
<view
|
||||
class="qualification-list__back"
|
||||
@tap="handleBack"
|
||||
>
|
||||
<image
|
||||
class="qualification-list__back-icon"
|
||||
src="/static/icons/icon-back.svg"
|
||||
mode="aspectFit"
|
||||
/>
|
||||
</view>
|
||||
<text class="qualification-list__navbar-title">
|
||||
资质列表
|
||||
</text>
|
||||
<view class="qualification-list__navbar-placeholder" />
|
||||
</view>
|
||||
|
||||
<scroll-view
|
||||
class="qualification-list__scroll"
|
||||
scroll-y
|
||||
:show-scrollbar="false"
|
||||
>
|
||||
<view class="qualification-list__content">
|
||||
<view
|
||||
v-for="item in qualifications"
|
||||
:key="item.id"
|
||||
class="qualification-list__card"
|
||||
>
|
||||
<view class="qualification-list__card-header">
|
||||
<text class="qualification-list__type">
|
||||
{{ item.type }}
|
||||
</text>
|
||||
<text class="qualification-list__status">
|
||||
{{ statusLabel(item.status) }}
|
||||
</text>
|
||||
</view>
|
||||
<text class="qualification-list__name">
|
||||
{{ item.certificateName }}
|
||||
</text>
|
||||
<text class="qualification-list__row">
|
||||
{{ item.certificateNo }}
|
||||
</text>
|
||||
<text class="qualification-list__row">
|
||||
{{ item.issuer }}
|
||||
</text>
|
||||
<text class="qualification-list__row">
|
||||
{{ formatValidity(item) }}
|
||||
</text>
|
||||
<view class="qualification-list__actions">
|
||||
<text
|
||||
class="qualification-list__action qualification-list__action--edit"
|
||||
@tap="handleEdit(item)"
|
||||
>
|
||||
编辑
|
||||
</text>
|
||||
<text
|
||||
class="qualification-list__action qualification-list__action--delete"
|
||||
@tap="handleDelete(item)"
|
||||
>
|
||||
删除
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<view class="qualification-list__footer">
|
||||
<view
|
||||
class="qualification-list__add-btn"
|
||||
@tap="handleAdd"
|
||||
>
|
||||
+ 新增资质
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import './index.scss';
|
||||
</style>
|
||||
248
src/pages/personal-center/qualification-upload/index.scss
Normal file
248
src/pages/personal-center/qualification-upload/index.scss
Normal file
@@ -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;
|
||||
}
|
||||
384
src/pages/personal-center/qualification-upload/index.vue
Normal file
384
src/pages/personal-center/qualification-upload/index.vue
Normal file
@@ -0,0 +1,384 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, ref } from 'vue'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import { QUALIFICATION_TYPE_OPTIONS } from '@/constants/qualification'
|
||||
import { useQualifications } from '@/hooks/use-qualifications'
|
||||
import type { QualificationItem, QualificationStatus } from '@/types/qualification'
|
||||
import deleteIcon from '@/static/icons/X.png'
|
||||
import pdfIcon from '@/static/icons/PDF.png'
|
||||
import pngIcon from '@/static/icons/PNG.png'
|
||||
import { navigateBackWithFallback } from '@/utils/navigate-back'
|
||||
|
||||
const { qualifications, getQualificationById } = useQualifications()
|
||||
|
||||
const editingId = ref<string | null>(null)
|
||||
const typeOptions = QUALIFICATION_TYPE_OPTIONS
|
||||
const typeIndex = ref(-1)
|
||||
const certificateName = ref('')
|
||||
const certificateNo = ref('')
|
||||
const issuer = ref('')
|
||||
const validFrom = ref('')
|
||||
const validTo = ref('')
|
||||
|
||||
const attachments = ref([
|
||||
{ id: '1', name: '需求文档.pdf', icon: pdfIcon },
|
||||
{ id: '2', name: '系统....pdf', icon: pdfIcon },
|
||||
{ id: '3', name: '参考图.png', icon: pngIcon },
|
||||
])
|
||||
|
||||
const selectedType = computed(() => {
|
||||
if (typeIndex.value < 0)
|
||||
return ''
|
||||
return typeOptions[typeIndex.value]
|
||||
})
|
||||
|
||||
onLoad((options) => {
|
||||
const rawId = options?.id
|
||||
if (!rawId)
|
||||
return
|
||||
|
||||
const id = decodeURIComponent(String(rawId))
|
||||
const item = getQualificationById(id)
|
||||
if (!item)
|
||||
return
|
||||
|
||||
editingId.value = id
|
||||
const index = typeOptions.indexOf(item.type)
|
||||
typeIndex.value = index >= 0 ? index : 0
|
||||
certificateName.value = item.certificateName
|
||||
certificateNo.value = item.certificateNo
|
||||
issuer.value = item.issuer
|
||||
validFrom.value = item.validFrom
|
||||
validTo.value = item.validTo
|
||||
})
|
||||
|
||||
function handleBack() {
|
||||
navigateBackWithFallback('/pages/personal-center/qualification-list/index')
|
||||
}
|
||||
|
||||
function handleTypeChange(event: { detail: { value: string } }) {
|
||||
typeIndex.value = Number(event.detail.value)
|
||||
}
|
||||
|
||||
function handleValidFromChange(event: { detail: { value: string } }) {
|
||||
validFrom.value = event.detail.value
|
||||
}
|
||||
|
||||
function handleValidToChange(event: { detail: { value: string } }) {
|
||||
validTo.value = event.detail.value
|
||||
}
|
||||
|
||||
function handleRemoveAttachment(id: string) {
|
||||
attachments.value = attachments.value.filter(item => item.id !== id)
|
||||
}
|
||||
|
||||
function handleAddAttachment() {
|
||||
uni.showToast({ title: '附件上传开发中', icon: 'none' })
|
||||
}
|
||||
|
||||
function resetForm() {
|
||||
if (!editingId.value) {
|
||||
typeIndex.value = -1
|
||||
certificateName.value = ''
|
||||
certificateNo.value = ''
|
||||
issuer.value = ''
|
||||
validFrom.value = ''
|
||||
validTo.value = ''
|
||||
}
|
||||
else {
|
||||
const item = getQualificationById(editingId.value)
|
||||
if (item) {
|
||||
const index = typeOptions.indexOf(item.type)
|
||||
typeIndex.value = index >= 0 ? index : 0
|
||||
certificateName.value = item.certificateName
|
||||
certificateNo.value = item.certificateNo
|
||||
issuer.value = item.issuer
|
||||
validFrom.value = item.validFrom
|
||||
validTo.value = item.validTo
|
||||
}
|
||||
}
|
||||
|
||||
attachments.value = [
|
||||
{ id: '1', name: '需求文档.pdf', icon: pdfIcon },
|
||||
{ id: '2', name: '系统....pdf', icon: pdfIcon },
|
||||
{ id: '3', name: '参考图.png', icon: pngIcon },
|
||||
]
|
||||
}
|
||||
|
||||
function validateForm() {
|
||||
if (!selectedType.value) {
|
||||
uni.showToast({ title: '请选择资质类型', icon: 'none' })
|
||||
return false
|
||||
}
|
||||
if (!certificateName.value.trim()) {
|
||||
uni.showToast({ title: '请输入证书名称', icon: 'none' })
|
||||
return false
|
||||
}
|
||||
if (!certificateNo.value.trim()) {
|
||||
uni.showToast({ title: '请输入证书编号', icon: 'none' })
|
||||
return false
|
||||
}
|
||||
if (!issuer.value.trim()) {
|
||||
uni.showToast({ title: '请输入发证单位', icon: 'none' })
|
||||
return false
|
||||
}
|
||||
if (!validFrom.value || !validTo.value) {
|
||||
uni.showToast({ title: '请选择有效期', icon: 'none' })
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
function buildPayload(status: QualificationStatus): Omit<QualificationItem, 'id'> {
|
||||
return {
|
||||
type: selectedType.value,
|
||||
certificateName: certificateName.value.trim(),
|
||||
certificateNo: certificateNo.value.trim(),
|
||||
issuer: issuer.value.trim(),
|
||||
validFrom: validFrom.value,
|
||||
validTo: validTo.value,
|
||||
status,
|
||||
}
|
||||
}
|
||||
|
||||
function upsertItem(status: QualificationStatus) {
|
||||
const payload = buildPayload(status)
|
||||
|
||||
if (editingId.value) {
|
||||
qualifications.value = qualifications.value.map(row => (
|
||||
row.id === editingId.value
|
||||
? { ...row, ...payload }
|
||||
: row
|
||||
))
|
||||
return
|
||||
}
|
||||
|
||||
qualifications.value.push({
|
||||
id: `${Date.now()}`,
|
||||
...payload,
|
||||
})
|
||||
}
|
||||
|
||||
function handleSaveDraft() {
|
||||
if (!validateForm())
|
||||
return
|
||||
|
||||
upsertItem('draft')
|
||||
uni.showToast({ title: '草稿已保存', icon: 'none' })
|
||||
setTimeout(() => {
|
||||
uni.navigateBack()
|
||||
}, 1200)
|
||||
}
|
||||
|
||||
function handleSubmit() {
|
||||
if (!validateForm())
|
||||
return
|
||||
|
||||
upsertItem('reviewing')
|
||||
uni.showToast({ title: '提交成功', icon: 'none' })
|
||||
setTimeout(() => {
|
||||
uni.navigateBack()
|
||||
}, 1200)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="qualification-upload">
|
||||
<view class="qualification-upload__navbar">
|
||||
<view
|
||||
class="qualification-upload__back"
|
||||
@tap="handleBack"
|
||||
>
|
||||
<image
|
||||
class="qualification-upload__back-icon"
|
||||
src="/static/icons/icon-back.svg"
|
||||
mode="aspectFit"
|
||||
/>
|
||||
</view>
|
||||
<text class="qualification-upload__navbar-title">
|
||||
资质上传
|
||||
</text>
|
||||
<view class="qualification-upload__navbar-placeholder" />
|
||||
</view>
|
||||
|
||||
<scroll-view
|
||||
class="qualification-upload__scroll"
|
||||
scroll-y
|
||||
:show-scrollbar="false"
|
||||
>
|
||||
<view class="qualification-upload__card">
|
||||
<view class="qualification-upload__field">
|
||||
<text class="qualification-upload__label">
|
||||
资质类型<text class="qualification-upload__required">*</text>
|
||||
</text>
|
||||
<picker
|
||||
mode="selector"
|
||||
:range="typeOptions"
|
||||
:value="typeIndex < 0 ? 0 : typeIndex"
|
||||
@change="handleTypeChange"
|
||||
>
|
||||
<view class="qualification-upload__picker">
|
||||
<text
|
||||
class="qualification-upload__picker-text"
|
||||
:class="{ 'qualification-upload__picker-text--placeholder': !selectedType }"
|
||||
>
|
||||
{{ selectedType || '请选择资质类型' }}
|
||||
</text>
|
||||
<text class="qualification-upload__picker-arrow">
|
||||
▾
|
||||
</text>
|
||||
</view>
|
||||
</picker>
|
||||
</view>
|
||||
|
||||
<view class="qualification-upload__field">
|
||||
<text class="qualification-upload__label">
|
||||
证书名称<text class="qualification-upload__required">*</text>
|
||||
</text>
|
||||
<input
|
||||
v-model="certificateName"
|
||||
class="qualification-upload__input"
|
||||
placeholder="请输入证书名称"
|
||||
placeholder-class="qualification-upload__placeholder"
|
||||
>
|
||||
</view>
|
||||
|
||||
<view class="qualification-upload__field">
|
||||
<text class="qualification-upload__label">
|
||||
证书编号<text class="qualification-upload__required">*</text>
|
||||
</text>
|
||||
<input
|
||||
v-model="certificateNo"
|
||||
class="qualification-upload__input"
|
||||
placeholder="请输入证书编号"
|
||||
placeholder-class="qualification-upload__placeholder"
|
||||
>
|
||||
</view>
|
||||
|
||||
<view class="qualification-upload__field">
|
||||
<text class="qualification-upload__label">
|
||||
发证单位<text class="qualification-upload__required">*</text>
|
||||
</text>
|
||||
<input
|
||||
v-model="issuer"
|
||||
class="qualification-upload__input"
|
||||
placeholder="请输入发证单位"
|
||||
placeholder-class="qualification-upload__placeholder"
|
||||
>
|
||||
</view>
|
||||
|
||||
<view class="qualification-upload__field">
|
||||
<text class="qualification-upload__label">
|
||||
有效期<text class="qualification-upload__required">*</text>
|
||||
</text>
|
||||
<view class="qualification-upload__date-row">
|
||||
<picker
|
||||
mode="date"
|
||||
:value="validFrom"
|
||||
@change="handleValidFromChange"
|
||||
>
|
||||
<view class="qualification-upload__date">
|
||||
<text
|
||||
class="qualification-upload__date-text"
|
||||
:class="{ 'qualification-upload__date-text--placeholder': !validFrom }"
|
||||
>
|
||||
{{ validFrom || '起始时间' }}
|
||||
</text>
|
||||
</view>
|
||||
</picker>
|
||||
<text class="qualification-upload__date-sep">
|
||||
至
|
||||
</text>
|
||||
<picker
|
||||
mode="date"
|
||||
:value="validTo"
|
||||
@change="handleValidToChange"
|
||||
>
|
||||
<view class="qualification-upload__date">
|
||||
<text
|
||||
class="qualification-upload__date-text"
|
||||
:class="{ 'qualification-upload__date-text--placeholder': !validTo }"
|
||||
>
|
||||
{{ validTo || '截止时间' }}
|
||||
</text>
|
||||
</view>
|
||||
</picker>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="qualification-upload__field">
|
||||
<view class="qualification-upload__label-row">
|
||||
<text class="qualification-upload__label">
|
||||
附件上传
|
||||
</text>
|
||||
<text class="qualification-upload__hint">
|
||||
支持图片/文档/压缩包
|
||||
</text>
|
||||
</view>
|
||||
<view class="qualification-upload__attachment-row">
|
||||
<view
|
||||
v-for="file in attachments"
|
||||
:key="file.id"
|
||||
class="qualification-upload__attachment-item"
|
||||
>
|
||||
<view
|
||||
class="qualification-upload__attachment-remove"
|
||||
@tap="handleRemoveAttachment(file.id)"
|
||||
>
|
||||
<image
|
||||
class="qualification-upload__attachment-remove-icon"
|
||||
:src="deleteIcon"
|
||||
mode="aspectFit"
|
||||
/>
|
||||
</view>
|
||||
<image
|
||||
class="qualification-upload__attachment-icon"
|
||||
:src="file.icon"
|
||||
mode="aspectFit"
|
||||
/>
|
||||
<text class="qualification-upload__attachment-name">
|
||||
{{ file.name }}
|
||||
</text>
|
||||
</view>
|
||||
<view
|
||||
class="qualification-upload__attachment-add"
|
||||
@tap="handleAddAttachment"
|
||||
>
|
||||
<text class="qualification-upload__attachment-add-icon">
|
||||
+
|
||||
</text>
|
||||
<text class="qualification-upload__attachment-add-text">
|
||||
添加附件
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<view class="qualification-upload__footer">
|
||||
<view
|
||||
class="qualification-upload__footer-btn"
|
||||
@tap="handleSaveDraft"
|
||||
>
|
||||
保存草稿
|
||||
</view>
|
||||
<view
|
||||
class="qualification-upload__footer-btn"
|
||||
@tap="resetForm"
|
||||
>
|
||||
重置
|
||||
</view>
|
||||
<view
|
||||
class="qualification-upload__footer-btn"
|
||||
@tap="handleSubmit"
|
||||
>
|
||||
提交认证
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import './index.scss';
|
||||
</style>
|
||||
94
src/pages/personal-center/service-hotline/index.scss
Normal file
94
src/pages/personal-center/service-hotline/index.scss
Normal file
@@ -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;
|
||||
}
|
||||
75
src/pages/personal-center/service-hotline/index.vue
Normal file
75
src/pages/personal-center/service-hotline/index.vue
Normal file
@@ -0,0 +1,75 @@
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
PERSONAL_CENTER_SERVICE_HOTLINE,
|
||||
PERSONAL_CENTER_SERVICE_HOTLINE_HOURS,
|
||||
} from '@/constants/personal-center'
|
||||
import serviceHotlineIcon from '@/static/icon/service-hotline.svg'
|
||||
import { navigateBackWithFallback } from '@/utils/navigate-back'
|
||||
|
||||
const hotlineDisplay = `专属客服热线-${PERSONAL_CENTER_SERVICE_HOTLINE}`
|
||||
const workHours = PERSONAL_CENTER_SERVICE_HOTLINE_HOURS
|
||||
const dialNumber = PERSONAL_CENTER_SERVICE_HOTLINE.replace(/-/g, '')
|
||||
|
||||
function handleBack() {
|
||||
navigateBackWithFallback('/pages/personal-center/index')
|
||||
}
|
||||
|
||||
function handleDial() {
|
||||
uni.makePhoneCall({
|
||||
phoneNumber: dialNumber,
|
||||
fail: () => {
|
||||
uni.showToast({ title: PERSONAL_CENTER_SERVICE_HOTLINE, icon: 'none' })
|
||||
},
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="service-hotline">
|
||||
<view class="service-hotline__navbar">
|
||||
<view
|
||||
class="service-hotline__back"
|
||||
@tap="handleBack"
|
||||
>
|
||||
<image
|
||||
class="service-hotline__back-icon"
|
||||
src="/static/icons/icon-back.svg"
|
||||
mode="aspectFit"
|
||||
/>
|
||||
</view>
|
||||
<text class="service-hotline__navbar-title">
|
||||
专属客服热线
|
||||
</text>
|
||||
<view class="service-hotline__navbar-placeholder" />
|
||||
</view>
|
||||
|
||||
<view class="service-hotline__body">
|
||||
<view class="service-hotline__card">
|
||||
<image
|
||||
class="service-hotline__icon"
|
||||
:src="serviceHotlineIcon"
|
||||
mode="aspectFit"
|
||||
/>
|
||||
<view class="service-hotline__info">
|
||||
<text class="service-hotline__title">
|
||||
{{ hotlineDisplay }}
|
||||
</text>
|
||||
<text class="service-hotline__hours">
|
||||
{{ workHours }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view
|
||||
class="service-hotline__dial-btn"
|
||||
@tap="handleDial"
|
||||
>
|
||||
点击拨号
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import './index.scss';
|
||||
</style>
|
||||
Reference in New Issue
Block a user