feat:首页完成
This commit is contained in:
@@ -25,6 +25,10 @@ onLoad((options) => {
|
||||
}
|
||||
if (type === 'loginFailed') {
|
||||
pageType.value = 'loginFailed'
|
||||
return
|
||||
}
|
||||
if (type === 'submitSuccess') {
|
||||
pageType.value = 'submitSuccess'
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
185
src/pages/case-library/components/CaseCard.vue
Normal file
185
src/pages/case-library/components/CaseCard.vue
Normal file
@@ -0,0 +1,185 @@
|
||||
<script setup lang="ts">
|
||||
import type { CaseItem } from '@/types/case-library'
|
||||
|
||||
defineProps<{
|
||||
item: CaseItem
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
toggleFavorite: [id: string]
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="case-card">
|
||||
<view class="case-card__cover-wrap">
|
||||
<image
|
||||
class="case-card__cover"
|
||||
:src="item.cover"
|
||||
mode="aspectFill"
|
||||
/>
|
||||
<text class="case-card__tag">
|
||||
{{ item.category }}
|
||||
</text>
|
||||
<view
|
||||
class="case-card__favorite"
|
||||
@tap.stop="emit('toggleFavorite', item.id)"
|
||||
>
|
||||
<image
|
||||
class="case-card__favorite-icon"
|
||||
:src="item.favorite ? '/static/icons/icon-heart-filled.svg' : '/static/icons/icon-heart.svg'"
|
||||
mode="aspectFit"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<text class="case-card__title">
|
||||
{{ item.title }}
|
||||
</text>
|
||||
|
||||
<view class="case-card__meta">
|
||||
<view class="case-card__meta-row">
|
||||
<view class="case-card__meta-item">
|
||||
<text class="case-card__meta-label">
|
||||
客户类型
|
||||
</text>
|
||||
<text class="case-card__meta-value">
|
||||
{{ item.clientType }}
|
||||
</text>
|
||||
</view>
|
||||
<view class="case-card__meta-item">
|
||||
<text class="case-card__meta-label">
|
||||
所属行业
|
||||
</text>
|
||||
<text class="case-card__meta-value">
|
||||
{{ item.industry }}
|
||||
</text>
|
||||
</view>
|
||||
<view class="case-card__meta-item">
|
||||
<text class="case-card__meta-label">
|
||||
规模
|
||||
</text>
|
||||
<text class="case-card__meta-value">
|
||||
{{ item.scale }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="case-card__meta-row">
|
||||
<view class="case-card__meta-item">
|
||||
<text class="case-card__meta-label">
|
||||
项目周期
|
||||
</text>
|
||||
<text class="case-card__meta-value">
|
||||
{{ item.cycle }}
|
||||
</text>
|
||||
</view>
|
||||
<view class="case-card__meta-item">
|
||||
<text class="case-card__meta-label">
|
||||
服务团队
|
||||
</text>
|
||||
<text class="case-card__meta-value">
|
||||
{{ item.teamSize }}
|
||||
</text>
|
||||
</view>
|
||||
<view class="case-card__meta-item">
|
||||
<text class="case-card__meta-label">
|
||||
完成时间
|
||||
</text>
|
||||
<text class="case-card__meta-value">
|
||||
{{ item.completedAt }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import '@/styles/variables.scss';
|
||||
|
||||
.case-card {
|
||||
padding: 0 0 28rpx;
|
||||
margin-bottom: 24rpx;
|
||||
background: $color-bg-page;
|
||||
border-radius: 20rpx;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.case-card__cover-wrap {
|
||||
position: relative;
|
||||
height: 320rpx;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.case-card__cover {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.case-card__tag {
|
||||
position: absolute;
|
||||
top: 20rpx;
|
||||
left: 20rpx;
|
||||
padding: 6rpx 16rpx;
|
||||
font-size: 22rpx;
|
||||
color: #ffffff;
|
||||
background: $color-primary;
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
|
||||
.case-card__favorite {
|
||||
position: absolute;
|
||||
top: 20rpx;
|
||||
right: 20rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 56rpx;
|
||||
height: 56rpx;
|
||||
background: rgba(255, 255, 255, 0.92);
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.case-card__favorite-icon {
|
||||
width: 32rpx;
|
||||
height: 32rpx;
|
||||
}
|
||||
|
||||
.case-card__title {
|
||||
display: block;
|
||||
padding: 24rpx 24rpx 0;
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: $color-text-primary;
|
||||
}
|
||||
|
||||
.case-card__meta {
|
||||
padding: 20rpx 24rpx 0;
|
||||
}
|
||||
|
||||
.case-card__meta-row {
|
||||
display: flex;
|
||||
margin-bottom: 16rpx;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.case-card__meta-item {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.case-card__meta-label {
|
||||
font-size: 22rpx;
|
||||
color: $color-text-secondary;
|
||||
}
|
||||
|
||||
.case-card__meta-value {
|
||||
margin-top: 8rpx;
|
||||
font-size: 24rpx;
|
||||
color: $color-text-primary;
|
||||
}
|
||||
</style>
|
||||
526
src/pages/case-library/components/CaseFilterPanel.vue
Normal file
526
src/pages/case-library/components/CaseFilterPanel.vue
Normal file
@@ -0,0 +1,526 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, ref, watch } from 'vue'
|
||||
import {
|
||||
CASE_FILTER_INDUSTRIES,
|
||||
CASE_FILTER_SCALES,
|
||||
CASE_FILTER_TIME_RANGES,
|
||||
DEFAULT_CASE_FILTER,
|
||||
} from '@/constants/case-library'
|
||||
import type { CaseFilterValues, CaseItem } from '@/types/case-library'
|
||||
import {
|
||||
cloneCaseFilter,
|
||||
getSelectedFilterTags,
|
||||
matchCaseFilter,
|
||||
removeFilterTag,
|
||||
} from '@/utils/case-filter'
|
||||
|
||||
const props = defineProps<{
|
||||
visible: boolean
|
||||
cases: CaseItem[]
|
||||
value: CaseFilterValues
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
close: []
|
||||
confirm: [value: CaseFilterValues]
|
||||
}>()
|
||||
|
||||
const draft = ref<CaseFilterValues>(cloneCaseFilter(DEFAULT_CASE_FILTER))
|
||||
|
||||
watch(
|
||||
() => props.visible,
|
||||
(visible) => {
|
||||
if (visible)
|
||||
draft.value = cloneCaseFilter(props.value)
|
||||
},
|
||||
)
|
||||
|
||||
const selectedTags = computed(() => getSelectedFilterTags(draft.value))
|
||||
|
||||
const previewCount = computed(() => {
|
||||
return props.cases.filter(item => matchCaseFilter(item, draft.value)).length
|
||||
})
|
||||
|
||||
function isIndustryActive(item: string) {
|
||||
return draft.value.industries.includes(item)
|
||||
}
|
||||
|
||||
function isScaleActive(item: string) {
|
||||
if (item === '全部')
|
||||
return draft.value.scales.length === 0
|
||||
|
||||
return draft.value.scales.includes(item)
|
||||
}
|
||||
|
||||
function isTimeActive(item: string) {
|
||||
if (item === '全部')
|
||||
return draft.value.timeRanges.length === 0 && !draft.value.dateRange.start && !draft.value.dateRange.end
|
||||
|
||||
return draft.value.timeRanges.includes(item)
|
||||
}
|
||||
|
||||
function toggleIndustry(item: string) {
|
||||
const next = cloneCaseFilter(draft.value)
|
||||
|
||||
if (next.industries.includes(item))
|
||||
next.industries = next.industries.filter(value => value !== item)
|
||||
else
|
||||
next.industries.push(item)
|
||||
|
||||
draft.value = next
|
||||
}
|
||||
|
||||
function toggleScale(item: string) {
|
||||
const next = cloneCaseFilter(draft.value)
|
||||
|
||||
if (item === '全部') {
|
||||
next.scales = []
|
||||
draft.value = next
|
||||
return
|
||||
}
|
||||
|
||||
if (next.scales.includes(item))
|
||||
next.scales = next.scales.filter(value => value !== item)
|
||||
else
|
||||
next.scales.push(item)
|
||||
|
||||
draft.value = next
|
||||
}
|
||||
|
||||
function toggleTime(item: string) {
|
||||
const next = cloneCaseFilter(draft.value)
|
||||
|
||||
if (item === '全部') {
|
||||
next.timeRanges = []
|
||||
next.dateRange = { start: '', end: '' }
|
||||
draft.value = next
|
||||
return
|
||||
}
|
||||
|
||||
if (next.timeRanges.includes(item))
|
||||
next.timeRanges = next.timeRanges.filter(value => value !== item)
|
||||
else
|
||||
next.timeRanges.push(item)
|
||||
|
||||
draft.value = next
|
||||
}
|
||||
|
||||
function handleDateChange(type: 'start' | 'end', value: string) {
|
||||
const next = cloneCaseFilter(draft.value)
|
||||
next.dateRange[type] = value.replace(/-/g, '/')
|
||||
next.timeRanges = []
|
||||
draft.value = next
|
||||
}
|
||||
|
||||
function handleReset() {
|
||||
draft.value = cloneCaseFilter(DEFAULT_CASE_FILTER)
|
||||
}
|
||||
|
||||
function handleClearTags() {
|
||||
draft.value = cloneCaseFilter(DEFAULT_CASE_FILTER)
|
||||
}
|
||||
|
||||
function handleRemoveTag(key: string) {
|
||||
draft.value = removeFilterTag(draft.value, key)
|
||||
}
|
||||
|
||||
function handleCancel() {
|
||||
emit('close')
|
||||
}
|
||||
|
||||
function handleConfirm() {
|
||||
emit('confirm', cloneCaseFilter(draft.value))
|
||||
emit('close')
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view
|
||||
v-if="visible"
|
||||
class="case-filter-panel"
|
||||
>
|
||||
<view
|
||||
class="case-filter-panel__mask"
|
||||
@tap="handleCancel"
|
||||
/>
|
||||
|
||||
<view
|
||||
class="case-filter-panel__sheet"
|
||||
@tap.stop
|
||||
>
|
||||
<view class="case-filter-panel__header">
|
||||
<view class="case-filter-panel__title-wrap">
|
||||
<image
|
||||
class="case-filter-panel__title-icon"
|
||||
src="/static/icons/icon-filter.svg"
|
||||
mode="aspectFit"
|
||||
/>
|
||||
<text class="case-filter-panel__title">
|
||||
筛选条件
|
||||
</text>
|
||||
</view>
|
||||
<view
|
||||
class="case-filter-panel__reset"
|
||||
@tap="handleReset"
|
||||
>
|
||||
<text class="case-filter-panel__reset-icon">
|
||||
↺
|
||||
</text>
|
||||
<text class="case-filter-panel__reset-text">
|
||||
重置
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<scroll-view
|
||||
class="case-filter-panel__body"
|
||||
scroll-y
|
||||
:show-scrollbar="false"
|
||||
>
|
||||
<view class="case-filter-panel__section">
|
||||
<text class="case-filter-panel__section-title">
|
||||
行业
|
||||
</text>
|
||||
<view class="case-filter-panel__chips">
|
||||
<text
|
||||
v-for="item in CASE_FILTER_INDUSTRIES"
|
||||
:key="item"
|
||||
class="case-filter-panel__chip"
|
||||
:class="{ 'case-filter-panel__chip--active': isIndustryActive(item) }"
|
||||
@tap="toggleIndustry(item)"
|
||||
>
|
||||
{{ item }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="case-filter-panel__section">
|
||||
<text class="case-filter-panel__section-title">
|
||||
项目规模
|
||||
</text>
|
||||
<view class="case-filter-panel__chips">
|
||||
<text
|
||||
v-for="item in CASE_FILTER_SCALES"
|
||||
:key="item"
|
||||
class="case-filter-panel__chip"
|
||||
:class="{ 'case-filter-panel__chip--active': isScaleActive(item) }"
|
||||
@tap="toggleScale(item)"
|
||||
>
|
||||
{{ item }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="case-filter-panel__section">
|
||||
<text class="case-filter-panel__section-title">
|
||||
完成时间
|
||||
</text>
|
||||
<view class="case-filter-panel__chips">
|
||||
<text
|
||||
v-for="item in CASE_FILTER_TIME_RANGES"
|
||||
:key="item"
|
||||
class="case-filter-panel__chip"
|
||||
:class="{ 'case-filter-panel__chip--active': isTimeActive(item) }"
|
||||
@tap="toggleTime(item)"
|
||||
>
|
||||
{{ item }}
|
||||
</text>
|
||||
</view>
|
||||
|
||||
<view class="case-filter-panel__date-row">
|
||||
<picker
|
||||
mode="date"
|
||||
:value="draft.dateRange.start.replace(/\//g, '-')"
|
||||
@change="handleDateChange('start', $event.detail.value)"
|
||||
>
|
||||
<view class="case-filter-panel__date-field">
|
||||
<text :class="{ 'case-filter-panel__date-placeholder': !draft.dateRange.start }">
|
||||
{{ draft.dateRange.start || '开始时间' }}
|
||||
</text>
|
||||
</view>
|
||||
</picker>
|
||||
<text class="case-filter-panel__date-separator">
|
||||
—
|
||||
</text>
|
||||
<picker
|
||||
mode="date"
|
||||
:value="draft.dateRange.end.replace(/\//g, '-')"
|
||||
@change="handleDateChange('end', $event.detail.value)"
|
||||
>
|
||||
<view class="case-filter-panel__date-field">
|
||||
<text :class="{ 'case-filter-panel__date-placeholder': !draft.dateRange.end }">
|
||||
{{ draft.dateRange.end || '结束时间' }}
|
||||
</text>
|
||||
</view>
|
||||
</picker>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<view
|
||||
v-if="selectedTags.length > 0"
|
||||
class="case-filter-panel__selected"
|
||||
>
|
||||
<view class="case-filter-panel__selected-header">
|
||||
<text class="case-filter-panel__selected-title">
|
||||
已选条件 ({{ selectedTags.length }}个)
|
||||
</text>
|
||||
<text
|
||||
class="case-filter-panel__selected-clear"
|
||||
@tap="handleClearTags"
|
||||
>
|
||||
清空
|
||||
</text>
|
||||
</view>
|
||||
<view class="case-filter-panel__selected-tags">
|
||||
<view
|
||||
v-for="tag in selectedTags"
|
||||
:key="tag.key"
|
||||
class="case-filter-panel__tag"
|
||||
>
|
||||
<text class="case-filter-panel__tag-text">
|
||||
{{ tag.label }}
|
||||
</text>
|
||||
<text
|
||||
class="case-filter-panel__tag-close"
|
||||
@tap.stop="handleRemoveTag(tag.key)"
|
||||
>
|
||||
×
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="case-filter-panel__footer">
|
||||
<view
|
||||
class="case-filter-panel__btn case-filter-panel__btn--cancel"
|
||||
@tap="handleCancel"
|
||||
>
|
||||
取消
|
||||
</view>
|
||||
<view
|
||||
class="case-filter-panel__btn case-filter-panel__btn--confirm"
|
||||
@tap="handleConfirm"
|
||||
>
|
||||
确定 ({{ previewCount }}个结果)
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import '@/styles/variables.scss';
|
||||
|
||||
.case-filter-panel {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 300;
|
||||
}
|
||||
|
||||
.case-filter-panel__mask {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
background: rgba(0, 0, 0, 0.45);
|
||||
}
|
||||
|
||||
.case-filter-panel__sheet {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
max-height: 78vh;
|
||||
background: $color-bg-page;
|
||||
border-radius: 24rpx 24rpx 0 0;
|
||||
}
|
||||
|
||||
.case-filter-panel__header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
flex-shrink: 0;
|
||||
padding: 28rpx 32rpx 20rpx;
|
||||
}
|
||||
|
||||
.case-filter-panel__title-wrap,
|
||||
.case-filter-panel__reset {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.case-filter-panel__title-icon {
|
||||
width: 28rpx;
|
||||
height: 28rpx;
|
||||
margin-right: 10rpx;
|
||||
}
|
||||
|
||||
.case-filter-panel__title {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: $color-text-primary;
|
||||
}
|
||||
|
||||
.case-filter-panel__reset-icon {
|
||||
margin-right: 6rpx;
|
||||
font-size: 28rpx;
|
||||
color: $color-primary;
|
||||
}
|
||||
|
||||
.case-filter-panel__reset-text {
|
||||
font-size: 26rpx;
|
||||
color: $color-primary;
|
||||
}
|
||||
|
||||
.case-filter-panel__body {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
padding: 0 32rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.case-filter-panel__section {
|
||||
margin-bottom: 28rpx;
|
||||
}
|
||||
|
||||
.case-filter-panel__section-title {
|
||||
display: block;
|
||||
margin-bottom: 20rpx;
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: $color-text-primary;
|
||||
}
|
||||
|
||||
.case-filter-panel__chips {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.case-filter-panel__chip {
|
||||
padding: 14rpx 24rpx;
|
||||
font-size: 24rpx;
|
||||
color: $color-text-primary;
|
||||
background: $color-bg-section;
|
||||
border-radius: 999rpx;
|
||||
|
||||
&--active {
|
||||
color: $color-primary;
|
||||
background: $color-primary-light;
|
||||
}
|
||||
}
|
||||
|
||||
.case-filter-panel__date-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-top: 20rpx;
|
||||
}
|
||||
|
||||
.case-filter-panel__date-field {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-width: 220rpx;
|
||||
height: 72rpx;
|
||||
padding: 0 20rpx;
|
||||
background: $color-bg-section;
|
||||
border-radius: 999rpx;
|
||||
}
|
||||
|
||||
.case-filter-panel__date-placeholder {
|
||||
color: $color-text-placeholder;
|
||||
}
|
||||
|
||||
.case-filter-panel__date-separator {
|
||||
margin: 0 16rpx;
|
||||
font-size: 24rpx;
|
||||
color: $color-text-secondary;
|
||||
}
|
||||
|
||||
.case-filter-panel__selected {
|
||||
flex-shrink: 0;
|
||||
margin: 0 32rpx 20rpx;
|
||||
padding: 20rpx 24rpx;
|
||||
background: $color-bg-section;
|
||||
border-radius: 16rpx;
|
||||
}
|
||||
|
||||
.case-filter-panel__selected-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.case-filter-panel__selected-title {
|
||||
font-size: 24rpx;
|
||||
color: $color-text-secondary;
|
||||
}
|
||||
|
||||
.case-filter-panel__selected-clear {
|
||||
font-size: 24rpx;
|
||||
color: $color-primary;
|
||||
}
|
||||
|
||||
.case-filter-panel__selected-tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 12rpx;
|
||||
}
|
||||
|
||||
.case-filter-panel__tag {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 8rpx 16rpx;
|
||||
background: $color-primary-light;
|
||||
border-radius: 999rpx;
|
||||
}
|
||||
|
||||
.case-filter-panel__tag-text {
|
||||
font-size: 22rpx;
|
||||
color: $color-primary;
|
||||
}
|
||||
|
||||
.case-filter-panel__tag-close {
|
||||
margin-left: 8rpx;
|
||||
font-size: 24rpx;
|
||||
line-height: 1;
|
||||
color: $color-primary;
|
||||
}
|
||||
|
||||
.case-filter-panel__footer {
|
||||
display: flex;
|
||||
gap: 20rpx;
|
||||
flex-shrink: 0;
|
||||
padding: 16rpx 32rpx calc(24rpx + env(safe-area-inset-bottom));
|
||||
}
|
||||
|
||||
.case-filter-panel__btn {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 88rpx;
|
||||
font-size: 30rpx;
|
||||
font-weight: 600;
|
||||
border-radius: 44rpx;
|
||||
|
||||
&--cancel {
|
||||
color: $color-primary;
|
||||
background: #ffffff;
|
||||
border: 2rpx solid $color-primary;
|
||||
}
|
||||
|
||||
&--confirm {
|
||||
color: #ffffff;
|
||||
background: $color-primary;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
83
src/pages/case-library/components/LoadMoreFooter.vue
Normal file
83
src/pages/case-library/components/LoadMoreFooter.vue
Normal file
@@ -0,0 +1,83 @@
|
||||
<script setup lang="ts">
|
||||
defineProps<{
|
||||
loading: boolean
|
||||
hasMore: boolean
|
||||
finished: boolean
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
loadMore: []
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view
|
||||
v-if="hasMore || finished"
|
||||
class="load-more-footer"
|
||||
@tap="emit('loadMore')"
|
||||
>
|
||||
<view
|
||||
v-if="loading"
|
||||
class="load-more-footer__loading"
|
||||
>
|
||||
<view class="load-more-footer__spinner" />
|
||||
<text class="load-more-footer__text">
|
||||
加载更多...
|
||||
</text>
|
||||
</view>
|
||||
<text
|
||||
v-else-if="hasMore"
|
||||
class="load-more-footer__text"
|
||||
>
|
||||
加载更多
|
||||
</text>
|
||||
<text
|
||||
v-else-if="finished"
|
||||
class="load-more-footer__finished"
|
||||
>
|
||||
没有更多了
|
||||
</text>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import '@/styles/variables.scss';
|
||||
|
||||
.load-more-footer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 32rpx 0 16rpx;
|
||||
}
|
||||
|
||||
.load-more-footer__loading {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.load-more-footer__spinner {
|
||||
width: 32rpx;
|
||||
height: 32rpx;
|
||||
margin-right: 12rpx;
|
||||
border: 4rpx solid #d6e4ff;
|
||||
border-top-color: $color-primary;
|
||||
border-radius: 50%;
|
||||
animation: load-more-spin 0.8s linear infinite;
|
||||
}
|
||||
|
||||
.load-more-footer__text,
|
||||
.load-more-footer__finished {
|
||||
font-size: 24rpx;
|
||||
color: $color-text-secondary;
|
||||
}
|
||||
|
||||
@keyframes load-more-spin {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,41 +1,319 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, ref, watch } from 'vue'
|
||||
import AppTabBar from '@/components/AppTabBar.vue'
|
||||
import {
|
||||
CASE_CATEGORIES,
|
||||
CASE_LIBRARY_LIST,
|
||||
CASE_PAGE_SIZE,
|
||||
DEFAULT_CASE_FILTER,
|
||||
} from '@/constants/case-library'
|
||||
import type { CaseCategory, CaseFilterValues } from '@/types/case-library'
|
||||
import { cloneCaseFilter, matchCaseFilter } from '@/utils/case-filter'
|
||||
import CaseCard from './components/CaseCard.vue'
|
||||
import CaseFilterPanel from './components/CaseFilterPanel.vue'
|
||||
import LoadMoreFooter from './components/LoadMoreFooter.vue'
|
||||
|
||||
const LOAD_MORE_DELAY = 600
|
||||
|
||||
const keyword = ref('')
|
||||
const activeCategory = ref<CaseCategory>('全部')
|
||||
const caseList = ref(CASE_LIBRARY_LIST.map(item => ({ ...item })))
|
||||
const pageSize = ref(CASE_PAGE_SIZE)
|
||||
const loadingMore = ref(false)
|
||||
const filterVisible = ref(false)
|
||||
const appliedFilter = ref<CaseFilterValues>(cloneCaseFilter(DEFAULT_CASE_FILTER))
|
||||
|
||||
const filteredCases = computed(() => {
|
||||
const text = keyword.value.trim()
|
||||
|
||||
return caseList.value.filter((item) => {
|
||||
const matchCategory = activeCategory.value === '全部' || item.category === activeCategory.value
|
||||
const matchKeyword = !text || item.title.includes(text) || item.industry.includes(text) || item.category.includes(text)
|
||||
const matchFilter = matchCaseFilter(item, appliedFilter.value)
|
||||
return matchCategory && matchKeyword && matchFilter
|
||||
})
|
||||
})
|
||||
|
||||
const visibleCases = computed(() => filteredCases.value.slice(0, pageSize.value))
|
||||
|
||||
const hasMore = computed(() => pageSize.value < filteredCases.value.length)
|
||||
|
||||
const loadFinished = computed(() => !hasMore.value && visibleCases.value.length > 0)
|
||||
|
||||
watch([keyword, activeCategory, appliedFilter], () => {
|
||||
pageSize.value = CASE_PAGE_SIZE
|
||||
})
|
||||
|
||||
function handleSelectCategory(category: CaseCategory) {
|
||||
activeCategory.value = category
|
||||
}
|
||||
|
||||
function handleToggleFavorite(id: string) {
|
||||
caseList.value = caseList.value.map(item =>
|
||||
item.id === id ? { ...item, favorite: !item.favorite } : item,
|
||||
)
|
||||
}
|
||||
|
||||
function handleLoadMore() {
|
||||
if (loadingMore.value || !hasMore.value)
|
||||
return
|
||||
|
||||
loadingMore.value = true
|
||||
|
||||
setTimeout(() => {
|
||||
pageSize.value += CASE_PAGE_SIZE
|
||||
loadingMore.value = false
|
||||
}, LOAD_MORE_DELAY)
|
||||
}
|
||||
|
||||
function handleOpenFilter() {
|
||||
filterVisible.value = true
|
||||
}
|
||||
|
||||
function handleCloseFilter() {
|
||||
filterVisible.value = false
|
||||
}
|
||||
|
||||
function handleConfirmFilter(value: CaseFilterValues) {
|
||||
appliedFilter.value = cloneCaseFilter(value)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="placeholder-page">
|
||||
<text class="placeholder-page__title">
|
||||
案例库
|
||||
</text>
|
||||
<text class="placeholder-page__desc">
|
||||
功能开发中
|
||||
</text>
|
||||
<view class="case-library">
|
||||
<view class="case-library__header">
|
||||
<text class="case-library__title">
|
||||
案例库
|
||||
</text>
|
||||
|
||||
<view class="case-library__search-row">
|
||||
<view class="case-library__search">
|
||||
<image
|
||||
class="case-library__search-icon"
|
||||
src="/static/icons/icon-search.svg"
|
||||
mode="aspectFit"
|
||||
/>
|
||||
<input
|
||||
v-model="keyword"
|
||||
class="case-library__search-input"
|
||||
placeholder="搜索案例标题、描述、标签..."
|
||||
placeholder-class="case-library__search-placeholder"
|
||||
>
|
||||
</view>
|
||||
<view class="case-library__favorite-btn">
|
||||
<image
|
||||
class="case-library__favorite-btn-icon"
|
||||
src="/static/icons/icon-heart.svg"
|
||||
mode="aspectFit"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<scroll-view
|
||||
class="case-library__tabs"
|
||||
scroll-x
|
||||
:show-scrollbar="false"
|
||||
>
|
||||
<view class="case-library__tabs-inner">
|
||||
<text
|
||||
v-for="category in CASE_CATEGORIES"
|
||||
:key="category"
|
||||
class="case-library__tab"
|
||||
:class="{ 'case-library__tab--active': activeCategory === category }"
|
||||
@tap="handleSelectCategory(category)"
|
||||
>
|
||||
{{ category }}
|
||||
</text>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<view class="case-library__toolbar">
|
||||
<text class="case-library__count">
|
||||
共 {{ filteredCases.length }}
|
||||
</text>
|
||||
<view
|
||||
class="case-library__filter"
|
||||
@tap="handleOpenFilter"
|
||||
>
|
||||
<image
|
||||
class="case-library__filter-icon"
|
||||
src="/static/icons/icon-filter.svg"
|
||||
mode="aspectFit"
|
||||
/>
|
||||
<text class="case-library__filter-text">
|
||||
筛选
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<scroll-view
|
||||
class="case-library__scroll"
|
||||
scroll-y
|
||||
:show-scrollbar="false"
|
||||
:lower-threshold="120"
|
||||
@scrolltolower="handleLoadMore"
|
||||
>
|
||||
<view class="case-library__list">
|
||||
<CaseCard
|
||||
v-for="item in visibleCases"
|
||||
:key="item.id"
|
||||
:item="item"
|
||||
@toggle-favorite="handleToggleFavorite"
|
||||
/>
|
||||
|
||||
<LoadMoreFooter
|
||||
:loading="loadingMore"
|
||||
:has-more="hasMore"
|
||||
:finished="loadFinished"
|
||||
@load-more="handleLoadMore"
|
||||
/>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<AppTabBar active="case" />
|
||||
|
||||
<CaseFilterPanel
|
||||
:visible="filterVisible"
|
||||
:cases="caseList"
|
||||
:value="appliedFilter"
|
||||
@close="handleCloseFilter"
|
||||
@confirm="handleConfirmFilter"
|
||||
/>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import '@/styles/variables.scss';
|
||||
|
||||
.placeholder-page {
|
||||
.case-library {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 100vh;
|
||||
padding-bottom: calc(120rpx + env(safe-area-inset-bottom));
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
background: $color-bg-section;
|
||||
}
|
||||
|
||||
.case-library__header {
|
||||
flex-shrink: 0;
|
||||
padding: calc(var(--status-bar-height, 44px) + 12rpx) 24rpx 0;
|
||||
background: $color-bg-page;
|
||||
}
|
||||
|
||||
.placeholder-page__title {
|
||||
.case-library__title {
|
||||
display: block;
|
||||
font-size: 36rpx;
|
||||
font-weight: 600;
|
||||
color: $color-text-primary;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.placeholder-page__desc {
|
||||
margin-top: 16rpx;
|
||||
.case-library__search-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-top: 24rpx;
|
||||
}
|
||||
|
||||
.case-library__search {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
align-items: center;
|
||||
height: 72rpx;
|
||||
padding: 0 24rpx;
|
||||
background: $color-bg-section;
|
||||
border-radius: 36rpx;
|
||||
}
|
||||
|
||||
.case-library__search-icon {
|
||||
flex-shrink: 0;
|
||||
width: 32rpx;
|
||||
height: 32rpx;
|
||||
margin-right: 12rpx;
|
||||
}
|
||||
|
||||
.case-library__search-input {
|
||||
flex: 1;
|
||||
font-size: 26rpx;
|
||||
color: $color-text-primary;
|
||||
}
|
||||
|
||||
.case-library__search-placeholder {
|
||||
color: $color-text-placeholder;
|
||||
}
|
||||
|
||||
.case-library__favorite-btn {
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 72rpx;
|
||||
height: 72rpx;
|
||||
margin-left: 16rpx;
|
||||
}
|
||||
|
||||
.case-library__favorite-btn-icon {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
}
|
||||
|
||||
.case-library__tabs {
|
||||
margin-top: 24rpx;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.case-library__tabs-inner {
|
||||
display: inline-flex;
|
||||
gap: 32rpx;
|
||||
padding-right: 24rpx;
|
||||
}
|
||||
|
||||
.case-library__tab {
|
||||
flex-shrink: 0;
|
||||
padding-bottom: 12rpx;
|
||||
font-size: 28rpx;
|
||||
color: $color-text-secondary;
|
||||
|
||||
&--active {
|
||||
font-weight: 600;
|
||||
color: $color-text-primary;
|
||||
border-bottom: 4rpx solid $color-primary;
|
||||
}
|
||||
}
|
||||
|
||||
.case-library__toolbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 20rpx 0 24rpx;
|
||||
}
|
||||
|
||||
.case-library__count {
|
||||
font-size: 24rpx;
|
||||
color: $color-text-secondary;
|
||||
}
|
||||
|
||||
.case-library__filter {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.case-library__filter-icon {
|
||||
width: 28rpx;
|
||||
height: 28rpx;
|
||||
margin-right: 8rpx;
|
||||
}
|
||||
|
||||
.case-library__filter-text {
|
||||
font-size: 26rpx;
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
.case-library__scroll {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.case-library__list {
|
||||
padding: 0 24rpx calc(140rpx + env(safe-area-inset-bottom));
|
||||
}
|
||||
</style>
|
||||
|
||||
335
src/pages/home-index/business-detail/index.vue
Normal file
335
src/pages/home-index/business-detail/index.vue
Normal file
@@ -0,0 +1,335 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import { getBusinessDetail } from '@/constants/business-detail'
|
||||
import type { BusinessDetailContent } from '@/types/business-detail'
|
||||
import { navigateBackWithFallback } from '@/utils/navigate-back'
|
||||
import heroBg from '@/static/icons/background.png'
|
||||
|
||||
const detail = ref<BusinessDetailContent | null>(null)
|
||||
|
||||
onLoad((options) => {
|
||||
const id = (options?.id as string) || 'software'
|
||||
detail.value = getBusinessDetail(id) || null
|
||||
|
||||
if (!detail.value) {
|
||||
uni.showToast({ title: '业务详情不存在', icon: 'none' })
|
||||
setTimeout(() => uni.navigateBack(), 1500)
|
||||
}
|
||||
})
|
||||
|
||||
function handleBack() {
|
||||
navigateBackWithFallback('/pages/home-index/index')
|
||||
}
|
||||
|
||||
function handleSubmit() {
|
||||
if (!detail.value)
|
||||
return
|
||||
|
||||
uni.navigateTo({
|
||||
url: `/pages/home-index/requirement-submit/index?businessType=${encodeURIComponent(detail.value.title)}&detailId=${detail.value.id}`,
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view
|
||||
v-if="detail"
|
||||
class="business-detail"
|
||||
>
|
||||
<view class="business-detail__navbar">
|
||||
<view
|
||||
class="business-detail__back"
|
||||
@tap="handleBack"
|
||||
>
|
||||
<image
|
||||
class="business-detail__back-icon"
|
||||
src="/static/icons/icon-back.svg"
|
||||
mode="aspectFit"
|
||||
/>
|
||||
</view>
|
||||
<text class="business-detail__navbar-title">
|
||||
业务详情
|
||||
</text>
|
||||
<view class="business-detail__navbar-placeholder" />
|
||||
</view>
|
||||
|
||||
<scroll-view
|
||||
class="business-detail__scroll"
|
||||
scroll-y
|
||||
:show-scrollbar="false"
|
||||
>
|
||||
<view class="business-detail__hero">
|
||||
<image
|
||||
class="business-detail__hero-bg"
|
||||
:src="heroBg"
|
||||
mode="scaleToFill"
|
||||
/>
|
||||
<view class="business-detail__hero-inner">
|
||||
<image
|
||||
class="business-detail__hero-icon"
|
||||
:src="detail.icon"
|
||||
mode="aspectFit"
|
||||
/>
|
||||
<text class="business-detail__hero-title">
|
||||
{{ detail.title }}
|
||||
</text>
|
||||
<text class="business-detail__hero-subtitle">
|
||||
{{ detail.subtitle }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="business-detail__section">
|
||||
<text class="business-detail__section-title">
|
||||
服务类型
|
||||
</text>
|
||||
<view class="business-detail__type-row">
|
||||
<view
|
||||
v-for="type in detail.serviceTypes"
|
||||
:key="type.id"
|
||||
class="business-detail__type-item"
|
||||
>
|
||||
<image
|
||||
class="business-detail__type-icon"
|
||||
:src="type.icon"
|
||||
mode="aspectFit"
|
||||
/>
|
||||
<text class="business-detail__type-label">
|
||||
{{ type.label }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="business-detail__section">
|
||||
<text class="business-detail__section-title">
|
||||
{{ detail.contentTitle }}
|
||||
</text>
|
||||
|
||||
<view class="business-detail__video">
|
||||
<view class="business-detail__video-play">
|
||||
<text class="business-detail__video-play-icon">
|
||||
▶
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="business-detail__content">
|
||||
<text
|
||||
v-for="(paragraph, index) in detail.contentSections"
|
||||
:key="index"
|
||||
class="business-detail__paragraph"
|
||||
:class="{ 'business-detail__paragraph--title': paragraph.startsWith('一、') || paragraph.startsWith('二、') }"
|
||||
>
|
||||
{{ paragraph }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<view class="business-detail__footer">
|
||||
<view
|
||||
class="business-detail__submit-btn"
|
||||
@tap="handleSubmit"
|
||||
>
|
||||
立即提报
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import '@/styles/variables.scss';
|
||||
|
||||
.business-detail {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 100vh;
|
||||
background: $color-bg-section;
|
||||
}
|
||||
|
||||
.business-detail__navbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
flex-shrink: 0;
|
||||
padding: calc(var(--status-bar-height, 44px) + 12rpx) 24rpx 12rpx;
|
||||
background: $color-bg-page;
|
||||
}
|
||||
|
||||
.business-detail__back {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 64rpx;
|
||||
height: 64rpx;
|
||||
}
|
||||
|
||||
.business-detail__back-icon {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
}
|
||||
|
||||
.business-detail__navbar-title {
|
||||
font-size: 34rpx;
|
||||
font-weight: 600;
|
||||
color: $color-text-primary;
|
||||
}
|
||||
|
||||
.business-detail__navbar-placeholder {
|
||||
width: 64rpx;
|
||||
height: 64rpx;
|
||||
}
|
||||
|
||||
.business-detail__scroll {
|
||||
flex: 1;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.business-detail__hero {
|
||||
position: relative;
|
||||
margin: 20rpx 24rpx 0;
|
||||
overflow: hidden;
|
||||
border-radius: 20rpx;
|
||||
}
|
||||
|
||||
.business-detail__hero-bg {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.business-detail__hero-inner {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
padding: 24rpx 24rpx 28rpx;
|
||||
}
|
||||
|
||||
.business-detail__hero-icon {
|
||||
width: 96rpx;
|
||||
height: 96rpx;
|
||||
}
|
||||
|
||||
.business-detail__hero-title {
|
||||
display: block;
|
||||
margin-top: 16rpx;
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: $color-text-primary;
|
||||
}
|
||||
|
||||
.business-detail__hero-subtitle {
|
||||
display: block;
|
||||
margin-top: 8rpx;
|
||||
font-size: 24rpx;
|
||||
line-height: 1.5;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.business-detail__section {
|
||||
margin: 20rpx 24rpx 0;
|
||||
padding: 28rpx 24rpx 32rpx;
|
||||
background: $color-bg-page;
|
||||
border-radius: 20rpx;
|
||||
}
|
||||
|
||||
.business-detail__section-title {
|
||||
display: block;
|
||||
margin-bottom: 28rpx;
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: $color-text-primary;
|
||||
}
|
||||
|
||||
.business-detail__type-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.business-detail__type-item {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.business-detail__type-icon {
|
||||
width: 91rpx;
|
||||
height: 91rpx;
|
||||
}
|
||||
|
||||
.business-detail__type-label {
|
||||
margin-top: 12rpx;
|
||||
font-size: 22rpx;
|
||||
color: $color-text-primary;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.business-detail__video {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 360rpx;
|
||||
overflow: hidden;
|
||||
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%);
|
||||
border-radius: 16rpx;
|
||||
}
|
||||
|
||||
.business-detail__video-play {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
background: rgba(255, 255, 255, 0.25);
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.business-detail__video-play-icon {
|
||||
margin-left: 6rpx;
|
||||
font-size: 32rpx;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.business-detail__content {
|
||||
margin-top: 28rpx;
|
||||
}
|
||||
|
||||
.business-detail__paragraph {
|
||||
display: block;
|
||||
margin-bottom: 16rpx;
|
||||
font-size: 28rpx;
|
||||
line-height: 1.8;
|
||||
color: $color-text-primary;
|
||||
|
||||
&--title {
|
||||
margin-top: 8rpx;
|
||||
margin-bottom: 12rpx;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
.business-detail__footer {
|
||||
flex-shrink: 0;
|
||||
padding: 16rpx 32rpx calc(16rpx + env(safe-area-inset-bottom));
|
||||
background: $color-bg-page;
|
||||
border-top: 1rpx solid #f0f0f0;
|
||||
}
|
||||
|
||||
.business-detail__submit-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 88rpx;
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #ffffff;
|
||||
background: $color-primary;
|
||||
border-radius: 44rpx;
|
||||
}
|
||||
</style>
|
||||
112
src/pages/home-index/components/HomeFloatingActions.vue
Normal file
112
src/pages/home-index/components/HomeFloatingActions.vue
Normal file
@@ -0,0 +1,112 @@
|
||||
<script setup lang="ts">
|
||||
function handleQuickSubmit() {
|
||||
uni.navigateTo({
|
||||
url: '/pages/home-index/requirement-submit/index',
|
||||
})
|
||||
}
|
||||
|
||||
function handleCustomerService() {
|
||||
uni.showToast({
|
||||
title: '客服中心开发中',
|
||||
icon: 'none',
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="home-floating-actions">
|
||||
<view
|
||||
class="home-floating-actions__item"
|
||||
@tap="handleQuickSubmit"
|
||||
>
|
||||
<view class="home-floating-actions__icon home-floating-actions__icon--submit">
|
||||
<text class="home-floating-actions__plus">
|
||||
+
|
||||
</text>
|
||||
</view>
|
||||
<text class="home-floating-actions__label">
|
||||
快捷提报
|
||||
</text>
|
||||
</view>
|
||||
|
||||
<view
|
||||
class="home-floating-actions__item"
|
||||
@tap="handleCustomerService"
|
||||
>
|
||||
<view class="home-floating-actions__icon home-floating-actions__icon--service">
|
||||
<image
|
||||
class="home-floating-actions__service-icon"
|
||||
src="/static/icons/icon-float-service.png"
|
||||
mode="aspectFit"
|
||||
/>
|
||||
</view>
|
||||
<text class="home-floating-actions__label">
|
||||
客服中心
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import '@/styles/variables.scss';
|
||||
|
||||
.home-floating-actions {
|
||||
position: fixed;
|
||||
right: 24rpx;
|
||||
bottom: calc(160rpx + env(safe-area-inset-bottom));
|
||||
z-index: 120;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
gap: 20rpx;
|
||||
}
|
||||
|
||||
.home-floating-actions__item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 72rpx;
|
||||
padding: 0 28rpx 0 12rpx;
|
||||
background: #ffffff;
|
||||
border-radius: 999rpx;
|
||||
box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.home-floating-actions__icon {
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
border-radius: 50%;
|
||||
|
||||
&--submit {
|
||||
background: #22c55e;
|
||||
}
|
||||
|
||||
&--service {
|
||||
background: #e8f0ff;
|
||||
}
|
||||
}
|
||||
|
||||
.home-floating-actions__plus {
|
||||
font-size: 32rpx;
|
||||
font-weight: 500;
|
||||
line-height: 1;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.home-floating-actions__service-icon {
|
||||
width: 32rpx;
|
||||
height: 32rpx;
|
||||
}
|
||||
|
||||
.home-floating-actions__label {
|
||||
margin-left: 16rpx;
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
color: $color-text-primary;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -1,5 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import type { ProjectItem } from '@/types/home'
|
||||
import cardBg from '@/static/icons/new-card.png'
|
||||
|
||||
defineProps<{
|
||||
item: ProjectItem
|
||||
@@ -8,6 +9,12 @@ defineProps<{
|
||||
|
||||
<template>
|
||||
<view class="project-card">
|
||||
<image
|
||||
class="project-card__bg"
|
||||
:src="cardBg"
|
||||
mode="scaleToFill"
|
||||
/>
|
||||
<view class="project-card__inner">
|
||||
<view class="project-card__header">
|
||||
<text class="project-card__title">
|
||||
{{ item.title }}
|
||||
@@ -24,18 +31,14 @@ defineProps<{
|
||||
>
|
||||
{{ item.category }}
|
||||
</text>
|
||||
<text class="project-card__no">
|
||||
项目编号:{{ item.projectNo }}
|
||||
</text>
|
||||
</view>
|
||||
|
||||
<view class="project-card__progress-row">
|
||||
<text class="project-card__progress-label">
|
||||
进行中
|
||||
</text>
|
||||
<text class="project-card__progress-value">
|
||||
{{ item.progress }}%
|
||||
</text>
|
||||
<view class="project-card__meta-info">
|
||||
<text class="project-card__no">
|
||||
项目编号:{{ item.projectNo }}
|
||||
</text>
|
||||
<text class="project-card__progress-value">
|
||||
{{ item.progress }}%
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="project-card__progress-bar">
|
||||
@@ -53,6 +56,7 @@ defineProps<{
|
||||
查看详情 >
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
@@ -60,11 +64,25 @@ defineProps<{
|
||||
@import '@/styles/variables.scss';
|
||||
|
||||
.project-card {
|
||||
padding: 28rpx;
|
||||
position: relative;
|
||||
margin-top: 20rpx;
|
||||
background: $color-bg-page;
|
||||
overflow: hidden;
|
||||
border-radius: 20rpx;
|
||||
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
|
||||
.project-card__bg {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.project-card__inner {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
padding: 28rpx;
|
||||
}
|
||||
|
||||
.project-card__header {
|
||||
@@ -93,45 +111,52 @@ defineProps<{
|
||||
margin-top: 16rpx;
|
||||
}
|
||||
|
||||
.project-card__meta-info {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
min-width: 0;
|
||||
margin-left: 12rpx;
|
||||
}
|
||||
|
||||
.project-card__tag {
|
||||
flex-shrink: 0;
|
||||
padding: 4rpx 12rpx;
|
||||
font-size: 20rpx;
|
||||
border-radius: 6rpx;
|
||||
font-weight: 500;
|
||||
width: 100rpx;
|
||||
text-align: center;
|
||||
height: 20rpx;
|
||||
line-height: 20rpx;
|
||||
|
||||
&--orange {
|
||||
color: $color-tag-orange;
|
||||
background: $color-tag-orange-bg;
|
||||
}
|
||||
|
||||
&--peach {
|
||||
color: #e07b4a;
|
||||
background: #fff0e8;
|
||||
color: #875045;
|
||||
background: #FFF1EA;
|
||||
}
|
||||
}
|
||||
|
||||
.project-card__no {
|
||||
margin-left: 12rpx;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
font-size: 22rpx;
|
||||
color: $color-text-secondary;
|
||||
color: #666666;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.project-card__progress-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-top: 24rpx;
|
||||
}
|
||||
|
||||
.project-card__progress-label,
|
||||
.project-card__progress-value {
|
||||
flex-shrink: 0;
|
||||
margin-left: 12rpx;
|
||||
font-size: 24rpx;
|
||||
color: $color-text-secondary;
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
.project-card__progress-bar {
|
||||
height: 12rpx;
|
||||
margin-top: 12rpx;
|
||||
margin-top: 24rpx;
|
||||
overflow: hidden;
|
||||
background: #eef2ff;
|
||||
border-radius: 6rpx;
|
||||
@@ -152,11 +177,11 @@ defineProps<{
|
||||
|
||||
.project-card__time {
|
||||
font-size: 22rpx;
|
||||
color: $color-text-secondary;
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
.project-card__detail {
|
||||
font-size: 24rpx;
|
||||
color: $color-text-link;
|
||||
color: #666666;
|
||||
}
|
||||
</style>
|
||||
|
||||
244
src/pages/home-index/components/QuickEntryEditor.vue
Normal file
244
src/pages/home-index/components/QuickEntryEditor.vue
Normal file
@@ -0,0 +1,244 @@
|
||||
<script setup lang="ts">
|
||||
import type { QuickEntryItem } from '@/types/home'
|
||||
|
||||
defineProps<{
|
||||
visible: boolean
|
||||
pinnedEntries: QuickEntryItem[]
|
||||
otherEntries: QuickEntryItem[]
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
close: []
|
||||
remove: [id: string]
|
||||
add: [id: string]
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view
|
||||
v-if="visible"
|
||||
class="quick-entry-editor"
|
||||
>
|
||||
<view
|
||||
class="quick-entry-editor__mask"
|
||||
@tap="emit('close')"
|
||||
/>
|
||||
|
||||
<view
|
||||
class="quick-entry-editor__panel"
|
||||
@tap.stop
|
||||
>
|
||||
<view class="quick-entry-editor__header">
|
||||
<text class="quick-entry-editor__title">
|
||||
快捷入口
|
||||
</text>
|
||||
<text
|
||||
class="quick-entry-editor__close"
|
||||
@tap="emit('close')"
|
||||
>
|
||||
×
|
||||
</text>
|
||||
</view>
|
||||
|
||||
<scroll-view
|
||||
class="quick-entry-editor__body"
|
||||
scroll-y
|
||||
:show-scrollbar="false"
|
||||
>
|
||||
<view class="quick-entry-editor__grid quick-entry-editor__grid--pinned">
|
||||
<view
|
||||
v-for="item in pinnedEntries"
|
||||
:key="item.id"
|
||||
class="quick-entry-editor__item"
|
||||
>
|
||||
<view class="quick-entry-editor__icon-wrap">
|
||||
<image
|
||||
class="quick-entry-editor__icon"
|
||||
:src="item.icon"
|
||||
mode="aspectFit"
|
||||
/>
|
||||
<view
|
||||
class="quick-entry-editor__badge"
|
||||
@tap.stop="emit('remove', item.id)"
|
||||
>
|
||||
<img src="@/static/icons/delete.png" alt="close">
|
||||
</view>
|
||||
</view>
|
||||
<text class="quick-entry-editor__label">
|
||||
{{ item.label }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<text class="quick-entry-editor__section-title">
|
||||
其他入口
|
||||
</text>
|
||||
|
||||
<view class="quick-entry-editor__grid quick-entry-editor__grid--other">
|
||||
<view
|
||||
v-for="item in otherEntries"
|
||||
:key="item.id"
|
||||
class="quick-entry-editor__item"
|
||||
>
|
||||
<view class="quick-entry-editor__icon-wrap">
|
||||
<image
|
||||
class="quick-entry-editor__icon"
|
||||
:src="item.icon"
|
||||
mode="aspectFit"
|
||||
/>
|
||||
<view
|
||||
class="quick-entry-editor__badge"
|
||||
@tap.stop="emit('add', item.id)"
|
||||
>
|
||||
<img src="@/static/icons/add.png" alt="close">
|
||||
</view>
|
||||
</view>
|
||||
<text class="quick-entry-editor__label">
|
||||
{{ item.label }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import '@/styles/variables.scss';
|
||||
|
||||
.quick-entry-editor {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 200;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0 40rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.quick-entry-editor__mask {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
background: rgba(0, 0, 0, 0.45);
|
||||
}
|
||||
|
||||
.quick-entry-editor__panel {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
max-height: 80vh;
|
||||
overflow: hidden;
|
||||
background: $color-bg-page;
|
||||
border-radius: 24rpx;
|
||||
box-shadow: 0 16rpx 48rpx rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
.quick-entry-editor__header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
flex-shrink: 0;
|
||||
padding: 32rpx 32rpx 24rpx;
|
||||
}
|
||||
|
||||
.quick-entry-editor__title {
|
||||
font-size: 34rpx;
|
||||
font-weight: 600;
|
||||
color: $color-text-primary;
|
||||
}
|
||||
|
||||
.quick-entry-editor__close {
|
||||
width: 48rpx;
|
||||
font-size: 40rpx;
|
||||
line-height: 1;
|
||||
color: $color-text-secondary;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.quick-entry-editor__body {
|
||||
flex: 1;
|
||||
max-height: calc(80vh - 100rpx);
|
||||
padding: 0 24rpx 32rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.quick-entry-editor__section-title {
|
||||
display: block;
|
||||
margin: 32rpx 8rpx 24rpx;
|
||||
font-size: 30rpx;
|
||||
font-weight: 600;
|
||||
color: $color-text-primary;
|
||||
}
|
||||
|
||||
.quick-entry-editor__grid {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
&--pinned {
|
||||
justify-content: space-between;
|
||||
padding: 0 8rpx;
|
||||
}
|
||||
|
||||
&--other {
|
||||
row-gap: 28rpx;
|
||||
padding: 0 8rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.quick-entry-editor__item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
width: 20%;
|
||||
}
|
||||
|
||||
.quick-entry-editor__icon-wrap {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 88rpx;
|
||||
height: 88rpx;
|
||||
}
|
||||
|
||||
.quick-entry-editor__icon {
|
||||
width: 72rpx;
|
||||
height: 72rpx;
|
||||
}
|
||||
|
||||
.quick-entry-editor__badge {
|
||||
position: absolute;
|
||||
top: -4rpx;
|
||||
right: -4rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 32rpx;
|
||||
height: 32rpx;
|
||||
background: #c8cdd4;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.quick-entry-editor__badge-text {
|
||||
font-size: 22rpx;
|
||||
font-weight: 600;
|
||||
line-height: 1;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.quick-entry-editor__label {
|
||||
margin-top: 12rpx;
|
||||
font-size: 22rpx;
|
||||
color: $color-text-primary;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
@@ -1,9 +1,18 @@
|
||||
<script setup lang="ts">
|
||||
import type { ServiceItem } from '@/types/home'
|
||||
|
||||
defineProps<{
|
||||
const props = defineProps<{
|
||||
item: ServiceItem
|
||||
}>()
|
||||
|
||||
function handleTap() {
|
||||
if (!props.item.detailId)
|
||||
return
|
||||
|
||||
uni.navigateTo({
|
||||
url: `/pages/home-index/business-detail/index?id=${props.item.detailId}`,
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -12,7 +21,9 @@ defineProps<{
|
||||
:class="[
|
||||
`service-card--${item.theme}`,
|
||||
{ 'service-card--with-illus': item.illus },
|
||||
{ 'service-card--clickable': item.detailId },
|
||||
]"
|
||||
@tap="handleTap"
|
||||
>
|
||||
<image
|
||||
v-if="item.illus"
|
||||
@@ -63,6 +74,10 @@ defineProps<{
|
||||
&--orange {
|
||||
background: linear-gradient( 180deg, #F7D3C6 0%, #FFFFFF 100%);
|
||||
}
|
||||
|
||||
&--clickable {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.service-card__illus {
|
||||
|
||||
@@ -1,16 +1,28 @@
|
||||
<script setup lang="ts">
|
||||
import AppTabBar from '@/components/AppTabBar.vue'
|
||||
import { useGreeting } from '@/hooks/use-greeting'
|
||||
import type { ProjectItem, QuickEntryItem, ServiceItem } from '@/types/home'
|
||||
import { useQuickEntries } from '@/hooks/use-quick-entries'
|
||||
import type { ProjectItem, ServiceItem } from '@/types/home'
|
||||
import headerBg from '@/static/icons/1.png'
|
||||
import softwareIllus from '@/static/icons/software.png'
|
||||
import hardwareIllus from '@/static/icons/hardware.png'
|
||||
import iotIllus from '@/static/icons/iot.png'
|
||||
import productionIllus from '@/static/icons/production.png'
|
||||
import HomeFloatingActions from './components/HomeFloatingActions.vue'
|
||||
import ProjectCard from './components/ProjectCard.vue'
|
||||
import QuickEntryEditor from './components/QuickEntryEditor.vue'
|
||||
import ServiceCard from './components/ServiceCard.vue'
|
||||
|
||||
const greeting = useGreeting()
|
||||
const {
|
||||
pinnedEntries,
|
||||
otherEntries,
|
||||
editorVisible,
|
||||
openEditor,
|
||||
closeEditor,
|
||||
removeFromPinned,
|
||||
addToPinned,
|
||||
} = useQuickEntries()
|
||||
|
||||
const services: ServiceItem[] = [
|
||||
{
|
||||
@@ -19,6 +31,7 @@ const services: ServiceItem[] = [
|
||||
desc: 'Web/小程序/APP/二次开发/桌面工具等',
|
||||
theme: 'blue',
|
||||
illus: softwareIllus,
|
||||
detailId: 'software',
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
@@ -43,14 +56,6 @@ const services: ServiceItem[] = [
|
||||
},
|
||||
]
|
||||
|
||||
const quickEntries: QuickEntryItem[] = [
|
||||
{ id: '1', label: '项目中心', icon: '/static/icons/quick-project.png', bgColor: '#E8F0FF' },
|
||||
{ id: '2', label: '快捷提报', icon: '/static/icons/quick-report.png', bgColor: '#E8FAF4' },
|
||||
{ id: '3', label: '在线客服', icon: '/static/icons/quick-service.png', bgColor: '#F3E8FF' },
|
||||
{ id: '4', label: '合同管理', icon: '/static/icons/quick-contract.png', bgColor: '#FEE2E2' },
|
||||
{ id: '5', label: '财务管理', icon: '/static/icons/quick-finance.png', bgColor: '#E0F2FE' },
|
||||
]
|
||||
|
||||
const projects: ProjectItem[] = [
|
||||
{
|
||||
id: '1',
|
||||
@@ -70,7 +75,7 @@ const projects: ProjectItem[] = [
|
||||
progress: 65,
|
||||
status: '进行中',
|
||||
updateTime: '2026-8-13 11:04:12',
|
||||
tagTheme: 'peach',
|
||||
tagTheme: 'orange',
|
||||
},
|
||||
]
|
||||
</script>
|
||||
@@ -146,13 +151,16 @@ const projects: ProjectItem[] = [
|
||||
<text class="home__section-title">
|
||||
快捷入口
|
||||
</text>
|
||||
<text class="home__section-action">
|
||||
<text
|
||||
class="home__section-action"
|
||||
@tap="openEditor"
|
||||
>
|
||||
编辑
|
||||
</text>
|
||||
</view>
|
||||
<view class="home__quick-row">
|
||||
<view
|
||||
v-for="entry in quickEntries"
|
||||
v-for="entry in pinnedEntries"
|
||||
:key="entry.id"
|
||||
class="home__quick-item"
|
||||
>
|
||||
@@ -168,7 +176,7 @@ const projects: ProjectItem[] = [
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="home__section home__section--last">
|
||||
<view class="home__sectionss home__section--last">
|
||||
<view class="home__section-header">
|
||||
<text class="home__section-title">
|
||||
在办项目
|
||||
@@ -180,17 +188,27 @@ const projects: ProjectItem[] = [
|
||||
|
||||
<view class="home__stats-row">
|
||||
<view class="home__stat-card home__stat-card--blue">
|
||||
<text class="home__stat-value">
|
||||
3 个项目
|
||||
</text>
|
||||
<view class="home__stat-value">
|
||||
<text class="home__stat-num">
|
||||
3
|
||||
</text>
|
||||
<text class="home__stat-unit">
|
||||
个项目
|
||||
</text>
|
||||
</view>
|
||||
<text class="home__stat-label">
|
||||
进行中
|
||||
</text>
|
||||
</view>
|
||||
<view class="home__stat-card home__stat-card--orange">
|
||||
<text class="home__stat-value">
|
||||
3 项待办
|
||||
</text>
|
||||
<view class="home__stat-value">
|
||||
<text class="home__stat-num">
|
||||
3
|
||||
</text>
|
||||
<text class="home__stat-unit">
|
||||
项待办
|
||||
</text>
|
||||
</view>
|
||||
<text class="home__stat-label">
|
||||
待您处理
|
||||
</text>
|
||||
@@ -211,6 +229,17 @@ const projects: ProjectItem[] = [
|
||||
</scroll-view>
|
||||
|
||||
<AppTabBar active="home" />
|
||||
|
||||
<HomeFloatingActions />
|
||||
|
||||
<QuickEntryEditor
|
||||
:visible="editorVisible"
|
||||
:pinned-entries="pinnedEntries"
|
||||
:other-entries="otherEntries"
|
||||
@close="closeEditor"
|
||||
@remove="removeFromPinned"
|
||||
@add="addToPinned"
|
||||
/>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
@@ -361,6 +390,14 @@ const projects: ProjectItem[] = [
|
||||
padding-top: 20rpx
|
||||
}
|
||||
|
||||
.home__sectionss {
|
||||
margin-bottom: 20rpx;
|
||||
width: 711.83rpx;
|
||||
background: $color-bg-page;
|
||||
border-radius: 20rpx;
|
||||
padding-top: 20rpx
|
||||
}
|
||||
|
||||
.home__section--last {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
@@ -422,34 +459,52 @@ const projects: ProjectItem[] = [
|
||||
.home__stats-row {
|
||||
display: flex;
|
||||
gap: 20rpx;
|
||||
padding: 0 20rpx;
|
||||
}
|
||||
|
||||
.home__stat-card {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
padding: 28rpx 24rpx;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 32rpx 24rpx;
|
||||
border-radius: 16rpx;
|
||||
text-align: center;
|
||||
|
||||
&--blue {
|
||||
background: $color-primary-light;
|
||||
background: linear-gradient(180deg, #D3E1FC 0%, #FFFFFF 100%);
|
||||
}
|
||||
|
||||
&--orange {
|
||||
background: $color-stat-orange-bg;
|
||||
background: linear-gradient(180deg, #FFECE1 0%, #FFFFFF 100%);
|
||||
}
|
||||
}
|
||||
|
||||
.home__stat-value {
|
||||
font-size: 32rpx;
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.home__stat-num {
|
||||
font-size: 56rpx;
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
color: $color-text-primary;
|
||||
}
|
||||
|
||||
.home__stat-unit {
|
||||
margin-left: 4rpx;
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
color: $color-text-primary;
|
||||
}
|
||||
|
||||
.home__stat-label {
|
||||
margin-top: 8rpx;
|
||||
margin-top: 12rpx;
|
||||
font-size: 24rpx;
|
||||
color: $color-text-secondary;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.home__recent-title {
|
||||
|
||||
706
src/pages/home-index/requirement-submit/index.vue
Normal file
706
src/pages/home-index/requirement-submit/index.vue
Normal file
@@ -0,0 +1,706 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, ref } from 'vue'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import { BUSINESS_TYPE_DETAIL_ID, BUSINESS_TYPE_OPTIONS } from '@/constants/business-types'
|
||||
import { navigateBackWithFallback } from '@/utils/navigate-back'
|
||||
import { navigateToResultPage } from '@/utils/navigate-result'
|
||||
import deleteIcon from '@/static/icons/X.png'
|
||||
import pdfIcon from '@/static/icons/PDF.png'
|
||||
import pngIcon from '@/static/icons/PNG.png'
|
||||
|
||||
const businessType = ref('')
|
||||
const detailId = ref('software')
|
||||
const businessTypeDropdownVisible = ref(false)
|
||||
const businessTypeOptions = BUSINESS_TYPE_OPTIONS
|
||||
const projectName = ref('')
|
||||
const description = ref('')
|
||||
const budget = ref('')
|
||||
const cycle = ref('')
|
||||
const userName = ref('王永志')
|
||||
const userPhone = ref('186****9618')
|
||||
const remark = ref('')
|
||||
|
||||
const budgetOptions = ['1万以下', '1-5万', '5-10万', '10万+']
|
||||
|
||||
const attachments = ref([
|
||||
{ id: '1', name: '需求文档.pdf', type: 'pdf', icon: pdfIcon },
|
||||
{ id: '2', name: '参考图.png', type: 'png', icon: pdfIcon},
|
||||
{ id: '3', name: '合同模板.pdf', type: 'pdf', icon: pngIcon },
|
||||
])
|
||||
|
||||
const descLength = computed(() => description.value.length)
|
||||
|
||||
onLoad((options) => {
|
||||
if (options?.detailId)
|
||||
detailId.value = options.detailId as string
|
||||
|
||||
if (options?.businessType) {
|
||||
businessType.value = decodeURIComponent(options.businessType as string)
|
||||
if (!options.detailId)
|
||||
detailId.value = BUSINESS_TYPE_DETAIL_ID[businessType.value] || 'software'
|
||||
}
|
||||
})
|
||||
|
||||
function handleBack() {
|
||||
closeBusinessTypeDropdown()
|
||||
navigateBackWithFallback(`/pages/home-index/business-detail/index?id=${detailId.value}`)
|
||||
}
|
||||
|
||||
function toggleBusinessTypeDropdown() {
|
||||
businessTypeDropdownVisible.value = !businessTypeDropdownVisible.value
|
||||
}
|
||||
|
||||
function closeBusinessTypeDropdown() {
|
||||
businessTypeDropdownVisible.value = false
|
||||
}
|
||||
|
||||
function handleSelectBusinessType(option: string) {
|
||||
businessType.value = option
|
||||
closeBusinessTypeDropdown()
|
||||
}
|
||||
|
||||
function handleSelectBudget(value: string) {
|
||||
budget.value = value
|
||||
}
|
||||
|
||||
function handleRemoveAttachment(id: string) {
|
||||
attachments.value = attachments.value.filter(item => item.id !== id)
|
||||
}
|
||||
|
||||
function handleAddAttachment() {
|
||||
uni.showToast({ title: '附件上传开发中', icon: 'none' })
|
||||
}
|
||||
|
||||
function handleSaveDraft() {
|
||||
uni.showToast({ title: '草稿已保存', icon: 'none' })
|
||||
}
|
||||
|
||||
function handleReset() {
|
||||
projectName.value = ''
|
||||
description.value = ''
|
||||
budget.value = ''
|
||||
cycle.value = ''
|
||||
remark.value = ''
|
||||
}
|
||||
|
||||
function handleSubmit() {
|
||||
if (!businessType.value) {
|
||||
uni.showToast({ title: '请选择业务类型', icon: 'none' })
|
||||
return
|
||||
}
|
||||
if (!projectName.value.trim()) {
|
||||
uni.showToast({ title: '项目名称不能为空', icon: 'none' })
|
||||
return
|
||||
}
|
||||
if (!description.value.trim()) {
|
||||
uni.showToast({ title: '请填写核心需求描述', icon: 'none' })
|
||||
return
|
||||
}
|
||||
|
||||
navigateToResultPage('submitSuccess')
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="requirement-submit">
|
||||
<view class="requirement-submit__navbar">
|
||||
<view
|
||||
class="requirement-submit__back"
|
||||
@tap="handleBack"
|
||||
>
|
||||
<image
|
||||
class="requirement-submit__back-icon"
|
||||
src="/static/icons/icon-back.svg"
|
||||
mode="aspectFit"
|
||||
/>
|
||||
</view>
|
||||
<text class="requirement-submit__navbar-title">
|
||||
需求提报
|
||||
</text>
|
||||
<view class="requirement-submit__navbar-placeholder" />
|
||||
</view>
|
||||
|
||||
<scroll-view
|
||||
class="requirement-submit__scroll"
|
||||
scroll-y
|
||||
:show-scrollbar="false"
|
||||
>
|
||||
<view class="requirement-submit__card requirement-submit__card--dropdown">
|
||||
<view class="requirement-submit__field">
|
||||
<text class="requirement-submit__label">
|
||||
业务类型<text class="requirement-submit__required">
|
||||
*
|
||||
</text>
|
||||
</text>
|
||||
<view class="requirement-submit__select-wrap">
|
||||
<view
|
||||
class="requirement-submit__select"
|
||||
@tap.stop="toggleBusinessTypeDropdown"
|
||||
>
|
||||
<text
|
||||
class="requirement-submit__select-text"
|
||||
:class="{ 'requirement-submit__select-text--placeholder': !businessType }"
|
||||
>
|
||||
{{ businessType || '选择下方推荐的服务或手动输入服务名称' }}
|
||||
</text>
|
||||
<text
|
||||
class="requirement-submit__select-arrow"
|
||||
:class="{ 'requirement-submit__select-arrow--open': businessTypeDropdownVisible }"
|
||||
>
|
||||
▾
|
||||
</text>
|
||||
</view>
|
||||
<view
|
||||
v-if="businessTypeDropdownVisible"
|
||||
class="requirement-submit__dropdown"
|
||||
@tap.stop
|
||||
>
|
||||
<view
|
||||
v-for="option in businessTypeOptions"
|
||||
:key="option"
|
||||
class="requirement-submit__dropdown-item"
|
||||
:class="{ 'requirement-submit__dropdown-item--active': businessType === option }"
|
||||
@tap="handleSelectBusinessType(option)"
|
||||
>
|
||||
{{ option }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="requirement-submit__card">
|
||||
<view class="requirement-submit__field">
|
||||
<text class="requirement-submit__label">
|
||||
项目名称<text class="requirement-submit__required">
|
||||
*
|
||||
</text>
|
||||
</text>
|
||||
<input
|
||||
v-model="projectName"
|
||||
class="requirement-submit__input requirement-submit__input--box"
|
||||
placeholder="项目名称不能为空"
|
||||
placeholder-class="requirement-submit__placeholder"
|
||||
>
|
||||
</view>
|
||||
|
||||
<view class="requirement-submit__field">
|
||||
<text class="requirement-submit__label">
|
||||
核心需求描述<text class="requirement-submit__required">
|
||||
*
|
||||
</text>
|
||||
</text>
|
||||
<view class="requirement-submit__textarea-wrap">
|
||||
<textarea
|
||||
v-model="description"
|
||||
class="requirement-submit__textarea"
|
||||
placeholder="请详细描述您的核心需求,包括功能目标、使用场景、期望效果等"
|
||||
placeholder-class="requirement-submit__placeholder"
|
||||
maxlength="500"
|
||||
/>
|
||||
<text class="requirement-submit__counter">
|
||||
{{ descLength }}/500字
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="requirement-submit__field">
|
||||
<text class="requirement-submit__label">
|
||||
预算范围
|
||||
</text>
|
||||
<view class="requirement-submit__budget-row">
|
||||
<view
|
||||
v-for="option in budgetOptions"
|
||||
:key="option"
|
||||
class="requirement-submit__budget-item"
|
||||
:class="{ 'requirement-submit__budget-item--active': budget === option }"
|
||||
@tap="handleSelectBudget(option)"
|
||||
>
|
||||
{{ option }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="requirement-submit__field">
|
||||
<text class="requirement-submit__label">
|
||||
预计开发周期
|
||||
</text>
|
||||
<view class="requirement-submit__cycle-row">
|
||||
<input
|
||||
v-model="cycle"
|
||||
class="requirement-submit__input requirement-submit__input--box requirement-submit__input--cycle"
|
||||
placeholder="请输入预计开发周期"
|
||||
placeholder-class="requirement-submit__placeholder"
|
||||
>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="requirement-submit__field">
|
||||
<text class="requirement-submit__label">
|
||||
用户名称
|
||||
</text>
|
||||
<input
|
||||
v-model="userName"
|
||||
class="requirement-submit__input requirement-submit__input--box"
|
||||
placeholder="请输入用户名称"
|
||||
placeholder-class="requirement-submit__placeholder"
|
||||
>
|
||||
</view>
|
||||
|
||||
<view class="requirement-submit__field">
|
||||
<text class="requirement-submit__label">
|
||||
用户手机号
|
||||
</text>
|
||||
<input
|
||||
v-model="userPhone"
|
||||
class="requirement-submit__input requirement-submit__input--box"
|
||||
placeholder="请输入用户手机号"
|
||||
placeholder-class="requirement-submit__placeholder"
|
||||
>
|
||||
</view>
|
||||
|
||||
<view class="requirement-submit__field">
|
||||
<text class="requirement-submit__label">
|
||||
附件上传
|
||||
</text>
|
||||
<text class="requirement-submit__hint">
|
||||
支持图片/文档/压缩包
|
||||
</text>
|
||||
<view class="requirement-submit__attachment-row">
|
||||
<view
|
||||
v-for="file in attachments"
|
||||
:key="file.id"
|
||||
class="requirement-submit__attachment-item"
|
||||
>
|
||||
<view class="requirement-submit__attachment-thumb">
|
||||
<image
|
||||
class="requirement-submit__attachment-icon"
|
||||
:src="file.icon"
|
||||
mode="aspectFit"
|
||||
/>
|
||||
</view>
|
||||
<view
|
||||
class="requirement-submit__attachment-remove"
|
||||
@tap="handleRemoveAttachment(file.id)"
|
||||
>
|
||||
<image
|
||||
class="requirement-submit__attachment-remove-icon"
|
||||
:src="deleteIcon"
|
||||
mode="aspectFit"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
<view
|
||||
class="requirement-submit__attachment-add"
|
||||
@tap="handleAddAttachment"
|
||||
>
|
||||
<text class="requirement-submit__attachment-add-icon">
|
||||
+
|
||||
</text>
|
||||
<text class="requirement-submit__attachment-add-text">
|
||||
添加附件
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="requirement-submit__card">
|
||||
<view class="requirement-submit__field">
|
||||
<text class="requirement-submit__label">
|
||||
补充备注
|
||||
</text>
|
||||
<textarea
|
||||
v-model="remark"
|
||||
class="requirement-submit__textarea requirement-submit__textarea--remark"
|
||||
placeholder="其他需要说明的事项..."
|
||||
placeholder-class="requirement-submit__placeholder"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<view
|
||||
v-if="businessTypeDropdownVisible"
|
||||
class="requirement-submit__dropdown-mask"
|
||||
@tap="closeBusinessTypeDropdown"
|
||||
/>
|
||||
|
||||
<view class="requirement-submit__footer">
|
||||
<view
|
||||
class="requirement-submit__footer-btn requirement-submit__footer-btn--primary"
|
||||
@tap="handleSaveDraft"
|
||||
>
|
||||
保存草稿
|
||||
</view>
|
||||
<view
|
||||
class="requirement-submit__footer-btn requirement-submit__footer-btn--primary"
|
||||
@tap="handleReset"
|
||||
>
|
||||
重置
|
||||
</view>
|
||||
<view
|
||||
class="requirement-submit__footer-btn requirement-submit__footer-btn--primary"
|
||||
@tap="handleSubmit"
|
||||
>
|
||||
立即提报
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import '@/styles/variables.scss';
|
||||
|
||||
.requirement-submit {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 100vh;
|
||||
background: $color-bg-section;
|
||||
}
|
||||
|
||||
.requirement-submit__navbar {
|
||||
position: relative;
|
||||
z-index: 30;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
flex-shrink: 0;
|
||||
padding: calc(var(--status-bar-height, 44px) + 12rpx) 24rpx 12rpx;
|
||||
background: $color-bg-page;
|
||||
}
|
||||
|
||||
.requirement-submit__back {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 64rpx;
|
||||
height: 64rpx;
|
||||
}
|
||||
|
||||
.requirement-submit__back-icon {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
}
|
||||
|
||||
.requirement-submit__navbar-title {
|
||||
font-size: 34rpx;
|
||||
font-weight: 600;
|
||||
color: $color-text-primary;
|
||||
}
|
||||
|
||||
.requirement-submit__navbar-placeholder {
|
||||
width: 64rpx;
|
||||
height: 64rpx;
|
||||
}
|
||||
|
||||
.requirement-submit__scroll {
|
||||
flex: 1;
|
||||
height: 0;
|
||||
padding: 20rpx 24rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.requirement-submit__card {
|
||||
padding: 28rpx 24rpx;
|
||||
margin-bottom: 20rpx;
|
||||
background: $color-bg-page;
|
||||
border-radius: 20rpx;
|
||||
|
||||
&--dropdown {
|
||||
position: relative;
|
||||
z-index: 20;
|
||||
overflow: visible;
|
||||
}
|
||||
}
|
||||
|
||||
.requirement-submit__field {
|
||||
margin-bottom: 28rpx;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.requirement-submit__label {
|
||||
display: block;
|
||||
margin-bottom: 16rpx;
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
color: $color-text-primary;
|
||||
}
|
||||
|
||||
.requirement-submit__required {
|
||||
color: #ff4d4f;
|
||||
}
|
||||
|
||||
.requirement-submit__hint {
|
||||
display: block;
|
||||
margin-bottom: 16rpx;
|
||||
font-size: 24rpx;
|
||||
color: $color-text-secondary;
|
||||
}
|
||||
|
||||
.requirement-submit__input {
|
||||
width: 100%;
|
||||
font-size: 28rpx;
|
||||
color: $color-text-primary;
|
||||
|
||||
&--box {
|
||||
height: 80rpx;
|
||||
padding: 0 24rpx;
|
||||
background: $color-bg-page;
|
||||
border: 1rpx solid #e8eaef;
|
||||
border-radius: 12rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
&--cycle {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.requirement-submit__placeholder {
|
||||
color: $color-text-placeholder;
|
||||
}
|
||||
|
||||
.requirement-submit__select-wrap {
|
||||
position: relative;
|
||||
z-index: 20;
|
||||
}
|
||||
|
||||
.requirement-submit__select {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 80rpx;
|
||||
padding: 0 24rpx;
|
||||
background: $color-bg-page;
|
||||
border: 1rpx solid #e8eaef;
|
||||
border-radius: 12rpx;
|
||||
}
|
||||
|
||||
.requirement-submit__select-text {
|
||||
flex: 1;
|
||||
font-size: 28rpx;
|
||||
color: $color-text-primary;
|
||||
|
||||
&--placeholder {
|
||||
color: $color-text-placeholder;
|
||||
}
|
||||
}
|
||||
|
||||
.requirement-submit__select-arrow {
|
||||
flex-shrink: 0;
|
||||
margin-left: 12rpx;
|
||||
font-size: 24rpx;
|
||||
color: $color-text-secondary;
|
||||
transition: transform 0.2s ease;
|
||||
|
||||
&--open {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
}
|
||||
|
||||
.requirement-submit__dropdown {
|
||||
position: absolute;
|
||||
top: calc(100% + 8rpx);
|
||||
right: 0;
|
||||
left: 0;
|
||||
z-index: 21;
|
||||
overflow: hidden;
|
||||
background: $color-bg-page;
|
||||
border: 1rpx solid #e8eaef;
|
||||
border-radius: 12rpx;
|
||||
box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
.requirement-submit__dropdown-item {
|
||||
padding: 24rpx;
|
||||
font-size: 28rpx;
|
||||
color: $color-text-primary;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
&--active {
|
||||
color: $color-primary;
|
||||
background: $color-primary-light;
|
||||
}
|
||||
}
|
||||
|
||||
.requirement-submit__dropdown-mask {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 10;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.requirement-submit__textarea-wrap {
|
||||
position: relative;
|
||||
padding: 20rpx 24rpx 48rpx;
|
||||
background: $color-bg-page;
|
||||
border: 1rpx solid #e8eaef;
|
||||
border-radius: 12rpx;
|
||||
}
|
||||
|
||||
.requirement-submit__textarea {
|
||||
width: 100%;
|
||||
min-height: 180rpx;
|
||||
font-size: 28rpx;
|
||||
line-height: 1.6;
|
||||
color: $color-text-primary;
|
||||
|
||||
&--remark {
|
||||
min-height: 140rpx;
|
||||
padding: 20rpx 24rpx;
|
||||
background: $color-bg-page;
|
||||
border: 1rpx solid #e8eaef;
|
||||
border-radius: 12rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
|
||||
.requirement-submit__counter {
|
||||
position: absolute;
|
||||
right: 24rpx;
|
||||
bottom: 16rpx;
|
||||
font-size: 22rpx;
|
||||
color: $color-text-secondary;
|
||||
}
|
||||
|
||||
.requirement-submit__budget-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.requirement-submit__budget-item {
|
||||
padding: 14rpx 28rpx;
|
||||
font-size: 26rpx;
|
||||
color: $color-text-primary;
|
||||
background: $color-bg-page;
|
||||
border: 1rpx solid #e8eaef;
|
||||
border-radius: 12rpx;
|
||||
|
||||
&--active {
|
||||
color: $color-primary;
|
||||
background: $color-primary-light;
|
||||
border-color: $color-primary-light;
|
||||
}
|
||||
}
|
||||
|
||||
.requirement-submit__cycle-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.requirement-submit__cycle-unit {
|
||||
flex-shrink: 0;
|
||||
margin-left: 16rpx;
|
||||
font-size: 28rpx;
|
||||
color: $color-text-primary;
|
||||
}
|
||||
|
||||
.requirement-submit__attachment-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 20rpx;
|
||||
}
|
||||
|
||||
.requirement-submit__attachment-item {
|
||||
position: relative;
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
}
|
||||
|
||||
.requirement-submit__attachment-thumb {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: #f8f9fb;
|
||||
border: 1rpx solid #e8eaef;
|
||||
border-radius: 12rpx;
|
||||
}
|
||||
|
||||
.requirement-submit__attachment-icon {
|
||||
width: 72rpx;
|
||||
height: 72rpx;
|
||||
}
|
||||
|
||||
.requirement-submit__attachment-remove {
|
||||
position: absolute;
|
||||
top: -10rpx;
|
||||
right: -10rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 32rpx;
|
||||
height: 32rpx;
|
||||
}
|
||||
|
||||
.requirement-submit__attachment-remove-icon {
|
||||
width: 32rpx;
|
||||
height: 32rpx;
|
||||
}
|
||||
|
||||
.requirement-submit__attachment-add {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
background: #f8f9fb;
|
||||
border: 1rpx dashed #d8dde6;
|
||||
border-radius: 12rpx;
|
||||
}
|
||||
|
||||
.requirement-submit__attachment-add-icon {
|
||||
font-size: 40rpx;
|
||||
line-height: 1;
|
||||
color: $color-text-secondary;
|
||||
}
|
||||
|
||||
.requirement-submit__attachment-add-text {
|
||||
margin-top: 8rpx;
|
||||
font-size: 20rpx;
|
||||
color: $color-text-secondary;
|
||||
}
|
||||
|
||||
.requirement-submit__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;
|
||||
}
|
||||
|
||||
.requirement-submit__footer-btn {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 80rpx;
|
||||
font-size: 28rpx;
|
||||
border-radius: 12rpx;
|
||||
|
||||
&--outline {
|
||||
color: $color-primary;
|
||||
background: $color-primary-light;
|
||||
}
|
||||
|
||||
&--primary {
|
||||
font-weight: 600;
|
||||
color: #ffffff;
|
||||
background: $color-primary;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user