chore: initial import
This commit is contained in:
674
pkgService/my_application/my_application.vue
Normal file
674
pkgService/my_application/my_application.vue
Normal file
@@ -0,0 +1,674 @@
|
||||
<template>
|
||||
<view class="my-application-page tn-safe-area-inset-bottom">
|
||||
<tn-nav-bar :isBack="true" :bottomShadow="false" backgroundColor="transparent">
|
||||
<view class="tn-text-bold tn-text-xl tn-color-black">{{ t('navTitle') }}</view>
|
||||
</tn-nav-bar>
|
||||
|
||||
<view class="page-main tn-padding" :style="{ paddingTop: vuex_custom_bar_height + 'px', paddingBottom: '48rpx' }">
|
||||
<scroll-view class="filter-scroll" scroll-x :show-scrollbar="false">
|
||||
<view class="filter-row tn-margin-bottom-lg">
|
||||
<view
|
||||
class="filter-chip"
|
||||
v-for="item in statusOptions"
|
||||
:key="item.value"
|
||||
:class="{ 'filter-chip--active': status === item.value }"
|
||||
@click="changeStatus(item.value)"
|
||||
>
|
||||
{{ item.label }}
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<view class="toolbar-row tn-margin-bottom-lg">
|
||||
<view class="toolbar-summary">{{ t('toolbarSummary', { total }) }}</view>
|
||||
<view
|
||||
class="refresh-btn"
|
||||
:class="{ 'refresh-btn--loading': loading }"
|
||||
@click="refreshStatus"
|
||||
>
|
||||
<text
|
||||
class="tn-text-bold tn-flex tn-flex-row-center tn-flex-col-center"
|
||||
>
|
||||
<text v-if="loading" class="tn-icon-loading tn-margin-right-xs spin"></text>
|
||||
{{ loading ? t('refreshing') : t('refresh') }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-if="list.length">
|
||||
<view
|
||||
v-for="item in list"
|
||||
:key="getApplicationKey(item)"
|
||||
class="application-card tn-margin-bottom-lg"
|
||||
@click="goToApplicationDetail(item)"
|
||||
>
|
||||
<view class="card-head tn-flex tn-flex-row-between tn-flex-col-center">
|
||||
<view class="card-title-wrap">
|
||||
<view class="card-title">{{ getApplicationTitle(item) }}</view>
|
||||
<view class="card-code tn-margin-top-xs">{{ getApplicationCode(item) }}</view>
|
||||
</view>
|
||||
<view :class="['status-badge', 'status-' + resolveStatusType(item.status)]">
|
||||
{{ getDisplayStatusText(item) }}
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="card-body tn-margin-top">
|
||||
<view class="info-row tn-flex tn-flex-row-between">
|
||||
<text class="info-label">{{ t('apartmentLabel') }}</text>
|
||||
<text class="info-value">{{ item.intendedAreaName || '--' }}</text>
|
||||
</view>
|
||||
<view class="info-row tn-flex tn-flex-row-between">
|
||||
<text class="info-label">{{ t('occupantLabel') }}</text>
|
||||
<text class="info-value">{{ item.userName || item.phone || '--' }}</text>
|
||||
</view>
|
||||
<view class="info-row tn-flex tn-flex-row-between">
|
||||
<text class="info-label">{{ item.isViewingApplication ? t('viewingTimeLabel') : t('leaseTermLabel') }}</text>
|
||||
<text class="info-value">{{ formatDateRange(item.start, item.end) }}</text>
|
||||
</view>
|
||||
<view class="info-row tn-flex tn-flex-row-between">
|
||||
<text class="info-label">{{ t('submittedAtLabel') }}</text>
|
||||
<text class="info-value">{{ formatDateTime(item.createTime) }}</text>
|
||||
</view>
|
||||
<view v-if="item.remark" class="remark-panel tn-margin-top">
|
||||
<view class="remark-label">{{ t('remarkLabel') }}</view>
|
||||
<view class="remark-text tn-margin-top-xs">{{ item.remark }}</view>
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="canViewBill(item)" class="card-footer tn-margin-top-lg">
|
||||
<view
|
||||
class="card-action-btn card-action-btn--ghost"
|
||||
:data-reference-type-no="resolveReferenceTypeNo(item)"
|
||||
@click="goToBillPage"
|
||||
>
|
||||
<text class="tn-text-bold">{{ t('viewBill') }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="load-state">
|
||||
<text v-if="loadingMore">{{ t('loadingMore') }}</text>
|
||||
<text v-else-if="!hasMore">{{ t('noMore') }}</text>
|
||||
<text v-else>{{ t('pullUpMore') }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-else-if="!loading" class="empty-panel tn-padding-lg">
|
||||
<view class="empty-title">{{ t('emptyTitle') }}</view>
|
||||
<view class="empty-desc tn-margin-top-sm">{{ t('emptyDesc') }}</view>
|
||||
<view class="action-btn tn-margin-top-lg" @click="goSelectRoom">
|
||||
<text class="tn-text-bold">{{ t('goSelectRoom') }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-if="loading && !list.length" class="empty-panel tn-padding-lg">
|
||||
<view class="empty-title">{{ t('loadingTitle') }}</view>
|
||||
<view class="empty-desc tn-margin-top-sm">{{ t('loadingDesc') }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { applicationFormMePage } from '@/api/index.js'
|
||||
import { saveActiveApplicationCode, saveMyApplicationList } from '@/utils/app-state'
|
||||
|
||||
function formatPageResponse(response) {
|
||||
const payload = response && response.data ? response.data : response
|
||||
return payload && typeof payload === 'object' ? payload : {}
|
||||
}
|
||||
|
||||
function normalizeApplicationList(list = []) {
|
||||
if (!Array.isArray(list)) {
|
||||
return []
|
||||
}
|
||||
return list
|
||||
.filter(item => item && typeof item === 'object')
|
||||
.map((item) => {
|
||||
const remark = item && item.remark ? String(item.remark) : ''
|
||||
const isViewingApplication = /预约看房申请|看房时间[::]|意向房型[::]|意向区域[::]|viewing request|viewing time[::]|preferred room type[::]|preferred area[::]/i.test(remark)
|
||||
return {
|
||||
...item,
|
||||
isViewingApplication
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export default {
|
||||
i18n: {
|
||||
zh: {
|
||||
navTitle: '我的申请',
|
||||
toolbarSummary: '共 {total} 条申请',
|
||||
refreshing: '刷新中...',
|
||||
refresh: '刷新状态',
|
||||
apartmentLabel: '所属公寓',
|
||||
occupantLabel: '入住人',
|
||||
viewingTimeLabel: '预约时间',
|
||||
leaseTermLabel: '预计租期',
|
||||
submittedAtLabel: '提交时间',
|
||||
remarkLabel: '补充说明',
|
||||
viewBill: '查看关联账单',
|
||||
loadingMore: '加载中...',
|
||||
noMore: '没有更多申请了',
|
||||
pullUpMore: '上拉加载更多',
|
||||
emptyTitle: '暂无申请记录',
|
||||
emptyDesc: '您还没有提交任何申请,去选房后可发起入住申请或预约看房。',
|
||||
goSelectRoom: '去选房',
|
||||
loadingTitle: '申请列表加载中',
|
||||
loadingDesc: '正在同步您的入住申请记录,请稍候。',
|
||||
statusAll: '全部',
|
||||
statusPending: '待审核',
|
||||
statusApproved: '已通过',
|
||||
statusRejected: '已拒绝',
|
||||
statusFinished: '已结束',
|
||||
statusProcessing: '处理中',
|
||||
refreshSuccess: '申请状态已刷新',
|
||||
loadError: '申请记录加载失败',
|
||||
viewingApplicationTitle: '预约看房申请',
|
||||
stayApplicationTitle: '入住申请',
|
||||
missingBillCode: '申请单编号缺失',
|
||||
missingApplicationCode: '申请编号缺失',
|
||||
dateRangeJoiner: ' 至 '
|
||||
},
|
||||
en: {
|
||||
navTitle: 'My Applications',
|
||||
toolbarSummary: '{total} applications',
|
||||
refreshing: 'Refreshing...',
|
||||
refresh: 'Refresh Status',
|
||||
apartmentLabel: 'Apartment',
|
||||
occupantLabel: 'Resident',
|
||||
viewingTimeLabel: 'Viewing Time',
|
||||
leaseTermLabel: 'Lease Term',
|
||||
submittedAtLabel: 'Submitted At',
|
||||
remarkLabel: 'Remarks',
|
||||
viewBill: 'View Related Bill',
|
||||
loadingMore: 'Loading...',
|
||||
noMore: 'No more applications',
|
||||
pullUpMore: 'Pull up to load more',
|
||||
emptyTitle: 'No applications yet',
|
||||
emptyDesc: 'You have not submitted any application yet. Go select a room to start a stay or viewing application.',
|
||||
goSelectRoom: 'Select Room',
|
||||
loadingTitle: 'Loading applications',
|
||||
loadingDesc: 'Syncing your application records. Please wait.',
|
||||
statusAll: 'All',
|
||||
statusPending: 'Pending',
|
||||
statusApproved: 'Approved',
|
||||
statusRejected: 'Rejected',
|
||||
statusFinished: 'Closed',
|
||||
statusProcessing: 'Processing',
|
||||
refreshSuccess: 'Application status refreshed',
|
||||
loadError: 'Failed to load applications',
|
||||
viewingApplicationTitle: 'Viewing Request',
|
||||
stayApplicationTitle: 'Stay Application',
|
||||
missingBillCode: 'Missing application bill number',
|
||||
missingApplicationCode: 'Missing application number',
|
||||
dateRangeJoiner: ' to '
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
status: '',
|
||||
pageIndex: 1,
|
||||
pageSize: 10,
|
||||
total: 0,
|
||||
list: [],
|
||||
loading: false,
|
||||
loadingMore: false,
|
||||
hasMore: true,
|
||||
statusOptions: []
|
||||
}
|
||||
},
|
||||
onShow() {
|
||||
this.reloadList()
|
||||
},
|
||||
onPullDownRefresh() {
|
||||
this.reloadList().finally(() => {
|
||||
uni.stopPullDownRefresh()
|
||||
})
|
||||
},
|
||||
onReachBottom() {
|
||||
this.loadMore()
|
||||
},
|
||||
created() {
|
||||
this.syncStatusOptions()
|
||||
},
|
||||
watch: {
|
||||
language() {
|
||||
this.syncStatusOptions()
|
||||
this.reloadList(true)
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
language() {
|
||||
return this.$getLanguage()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
t(key, params) {
|
||||
return this.$tm('pkgService.my_application.my_application', key, params)
|
||||
},
|
||||
syncStatusOptions() {
|
||||
this.statusOptions = [
|
||||
{ label: this.t('statusAll'), value: '' },
|
||||
{ label: this.t('statusPending'), value: 0 },
|
||||
{ label: this.t('statusApproved'), value: 10 },
|
||||
{ label: this.t('statusRejected'), value: 20 },
|
||||
{ label: this.t('statusFinished'), value: 99 }
|
||||
]
|
||||
},
|
||||
getStatusText(status) {
|
||||
const map = {
|
||||
0: this.t('statusPending'),
|
||||
10: this.t('statusApproved'),
|
||||
20: this.t('statusRejected'),
|
||||
99: this.t('statusFinished')
|
||||
}
|
||||
return map[status] || this.t('statusProcessing')
|
||||
},
|
||||
getDisplayStatusText(item = {}) {
|
||||
if (this.language !== 'en' && item.statusDesc) {
|
||||
return item.statusDesc
|
||||
}
|
||||
return this.getStatusText(item.status)
|
||||
},
|
||||
resolveStatusType(status) {
|
||||
if (status === 10) {
|
||||
return 'approved'
|
||||
}
|
||||
if (status === 20) {
|
||||
return 'rejected'
|
||||
}
|
||||
if (status === 99) {
|
||||
return 'finished'
|
||||
}
|
||||
return 'pending'
|
||||
},
|
||||
formatDateTime(value) {
|
||||
if (!value) {
|
||||
return '--'
|
||||
}
|
||||
const text = String(value).replace('T', ' ')
|
||||
return text.length > 16 ? text.slice(0, 16) : text
|
||||
},
|
||||
formatDate(value) {
|
||||
if (!value) {
|
||||
return '--'
|
||||
}
|
||||
return String(value).slice(0, 10)
|
||||
},
|
||||
formatDateRange(start, end) {
|
||||
if (!start && !end) {
|
||||
return '--'
|
||||
}
|
||||
if (start && end) {
|
||||
const startText = this.formatDateTime(start)
|
||||
const endText = this.formatDateTime(end)
|
||||
if (String(start).includes('T') || String(end).includes('T')) {
|
||||
return `${startText}${this.t('dateRangeJoiner')}${endText}`
|
||||
}
|
||||
}
|
||||
return `${this.formatDate(start)}${this.t('dateRangeJoiner')}${this.formatDate(end)}`
|
||||
},
|
||||
async refreshStatus() {
|
||||
if (this.loading || this.loadingMore) {
|
||||
return
|
||||
}
|
||||
await this.reloadList()
|
||||
if (this.list.length || this.total === 0) {
|
||||
uni.showToast({
|
||||
title: this.t('refreshSuccess'),
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
},
|
||||
async reloadList() {
|
||||
this.pageIndex = 1
|
||||
this.hasMore = true
|
||||
this.list = []
|
||||
await this.fetchList(true)
|
||||
},
|
||||
async loadMore() {
|
||||
if (!this.hasMore || this.loading || this.loadingMore) {
|
||||
return
|
||||
}
|
||||
this.pageIndex += 1
|
||||
await this.fetchList(false)
|
||||
},
|
||||
async fetchList(isReload = false) {
|
||||
if (isReload) {
|
||||
this.loading = true
|
||||
} else {
|
||||
this.loadingMore = true
|
||||
}
|
||||
|
||||
const params = {
|
||||
PageIndex: this.pageIndex,
|
||||
PageSize: this.pageSize
|
||||
}
|
||||
if (this.status !== '' && this.status !== null && typeof this.status !== 'undefined') {
|
||||
params.Status = this.status
|
||||
}
|
||||
|
||||
await applicationFormMePage(params, this.language)
|
||||
.then((response) => {
|
||||
const pageData = formatPageResponse(response)
|
||||
console.log('pageData', pageData)
|
||||
const nextList = normalizeApplicationList(pageData.data || [])
|
||||
const total = Number(pageData.total || 0)
|
||||
|
||||
this.total = total
|
||||
this.list = normalizeApplicationList(isReload ? nextList : this.list.concat(nextList))
|
||||
this.hasMore = this.list.length < total && nextList.length > 0
|
||||
saveMyApplicationList(this.list)
|
||||
|
||||
const activeCode = this.resolvePreferredReferenceTypeNo(this.list)
|
||||
if (activeCode) {
|
||||
saveActiveApplicationCode(activeCode)
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
if (!isReload) {
|
||||
this.pageIndex = Math.max(1, this.pageIndex - 1)
|
||||
}
|
||||
uni.showToast({
|
||||
title: (error && (error.message || error.msg)) || this.t('loadError'),
|
||||
icon: 'none'
|
||||
})
|
||||
})
|
||||
.finally(() => {
|
||||
this.loading = false
|
||||
this.loadingMore = false
|
||||
})
|
||||
},
|
||||
changeStatus(value) {
|
||||
if (this.status === value) {
|
||||
return
|
||||
}
|
||||
this.status = value
|
||||
this.reloadList()
|
||||
},
|
||||
getApplicationCode(item = {}) {
|
||||
return item.detailCode || '--'
|
||||
},
|
||||
getApplicationKey(item = {}) {
|
||||
return item.detailCode || item.formId || `${item.createTime || 'application'}-${item.phone || item.userName || 'unknown'}`
|
||||
},
|
||||
getApplicationTitle(item = {}) {
|
||||
if (item.isViewingApplication) {
|
||||
return this.t('viewingApplicationTitle')
|
||||
}
|
||||
return item.intendedRoomTypeName || this.t('stayApplicationTitle')
|
||||
},
|
||||
resolveReferenceTypeNo(item = {}) {
|
||||
return item.detailCode || ''
|
||||
},
|
||||
canViewBill(item = {}) {
|
||||
return !item.isViewingApplication && this.resolveReferenceTypeNo(item) && this.resolveStatusType(item.status) === 'pending'
|
||||
},
|
||||
resolvePreferredReferenceTypeNo(list = []) {
|
||||
const sortedList = [...list]
|
||||
.filter(item => !item.isViewingApplication && this.resolveReferenceTypeNo(item))
|
||||
.sort((a, b) => new Date(b.createTime || 0).getTime() - new Date(a.createTime || 0).getTime())
|
||||
|
||||
if (!sortedList.length) {
|
||||
return ''
|
||||
}
|
||||
|
||||
const preferredItem = sortedList.find(item => Number(item.status) !== 99) || sortedList[0]
|
||||
return this.resolveReferenceTypeNo(preferredItem)
|
||||
},
|
||||
goToBillPage(event) {
|
||||
const dataset = event && event.currentTarget ? event.currentTarget.dataset || {} : {}
|
||||
const referenceTypeNo = dataset.referenceTypeNo || dataset.referenceTypeNo === 0
|
||||
? String(dataset.referenceTypeNo)
|
||||
: ''
|
||||
if (!referenceTypeNo) {
|
||||
uni.showToast({
|
||||
title: this.t('missingBillCode'),
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
saveActiveApplicationCode(referenceTypeNo)
|
||||
uni.navigateTo({
|
||||
url: `/pkgService/utility_recharge/utility_recharge?referenceTypeNo=${encodeURIComponent(referenceTypeNo)}`
|
||||
})
|
||||
},
|
||||
goToApplicationDetail(item = {}) {
|
||||
const code = item.detailCode || ''
|
||||
if (!code) {
|
||||
uni.showToast({
|
||||
title: this.t('missingApplicationCode'),
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
uni.navigateTo({
|
||||
url: `/pkgService/application_detail/application_detail?code=${encodeURIComponent(code)}&payload=${encodeURIComponent(JSON.stringify(item))}`
|
||||
})
|
||||
},
|
||||
goSelectRoom() {
|
||||
uni.reLaunch({
|
||||
url: '/pages/index/index?index=0&pageId=select-room'
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.my-application-page {
|
||||
min-height: 100vh;
|
||||
background: linear-gradient(180deg, #F0F6FF 0%, #F8FAFF 100%);
|
||||
}
|
||||
|
||||
.application-card,
|
||||
.empty-panel {
|
||||
background: #FFFFFF;
|
||||
border-radius: 24rpx;
|
||||
padding: 32rpx;
|
||||
box-shadow: 0 8rpx 20rpx rgba(15, 23, 42, 0.04);
|
||||
border: 1px solid rgba($brand-primary, 0.05);
|
||||
}
|
||||
|
||||
.empty-title,
|
||||
.card-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: 700;
|
||||
color: #1A2233;
|
||||
}
|
||||
|
||||
.empty-desc,
|
||||
.card-code,
|
||||
.info-label,
|
||||
.remark-label,
|
||||
.load-state {
|
||||
color: #6A7A99;
|
||||
}
|
||||
|
||||
.filter-scroll {
|
||||
width: 100%;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.filter-row {
|
||||
display: inline-flex;
|
||||
flex-wrap: nowrap;
|
||||
gap: 16rpx;
|
||||
padding: 4rpx;
|
||||
}
|
||||
|
||||
.toolbar-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 24rpx;
|
||||
}
|
||||
|
||||
.toolbar-summary {
|
||||
flex: 1;
|
||||
font-size: 24rpx;
|
||||
color: #6A7A99;
|
||||
}
|
||||
|
||||
.refresh-btn {
|
||||
min-width: 188rpx;
|
||||
height: 72rpx;
|
||||
padding: 0 24rpx;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 999rpx;
|
||||
background: #FFFFFF;
|
||||
border: 1rpx solid rgba($brand-primary, 0.18);
|
||||
color: $brand-primary;
|
||||
box-shadow: 0 4rpx 12rpx rgba($brand-primary, 0.06);
|
||||
}
|
||||
|
||||
.refresh-btn--loading {
|
||||
opacity: 0.88;
|
||||
}
|
||||
|
||||
.filter-chip {
|
||||
flex-shrink: 0;
|
||||
padding: 14rpx 28rpx;
|
||||
border-radius: 999rpx;
|
||||
background: #FFFFFF;
|
||||
border: 1rpx solid rgba($brand-primary, 0.15);
|
||||
color: #6A7A99;
|
||||
font-size: 24rpx;
|
||||
box-shadow: 0 4rpx 12rpx rgba($brand-primary, 0.05);
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.filter-chip--active {
|
||||
background: #FFFFFF;
|
||||
border-color: $brand-primary;
|
||||
color: $brand-primary;
|
||||
font-weight: bold;
|
||||
box-shadow: 0 4rpx 16rpx rgba($brand-primary, 0.1);
|
||||
}
|
||||
|
||||
.card-code {
|
||||
font-size: 22rpx;
|
||||
}
|
||||
|
||||
.status-badge {
|
||||
padding: 8rpx 20rpx;
|
||||
border-radius: 999rpx;
|
||||
font-size: 22rpx;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.status-pending {
|
||||
background: rgba(245, 158, 11, 0.12);
|
||||
color: #B45309;
|
||||
}
|
||||
|
||||
.status-approved {
|
||||
background: rgba($brand-primary, 0.12);
|
||||
color: $brand-primary;
|
||||
}
|
||||
|
||||
.status-rejected {
|
||||
background: rgba(239, 68, 68, 0.12);
|
||||
color: #B91C1C;
|
||||
}
|
||||
|
||||
.status-finished {
|
||||
background: rgba(100, 116, 139, 0.12);
|
||||
color: #475569;
|
||||
}
|
||||
|
||||
.info-row {
|
||||
padding: 16rpx 0;
|
||||
border-bottom: 1px dashed rgba($brand-primary, 0.15);
|
||||
}
|
||||
|
||||
.info-row:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.info-value {
|
||||
max-width: 62%;
|
||||
text-align: right;
|
||||
color: #1A2233;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.remark-panel {
|
||||
padding: 24rpx;
|
||||
border-radius: 20rpx;
|
||||
background: rgba($brand-primary, 0.02);
|
||||
border: 1rpx solid rgba($brand-primary, 0.1);
|
||||
}
|
||||
|
||||
.remark-text {
|
||||
color: #1A2233;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.card-footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.card-action-btn {
|
||||
min-width: 220rpx;
|
||||
height: 84rpx;
|
||||
padding: 0 28rpx;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 18rpx;
|
||||
font-size: 26rpx;
|
||||
}
|
||||
|
||||
.card-action-btn--ghost {
|
||||
background: #ffffff;
|
||||
color: $brand-primary;
|
||||
border: 1rpx solid rgba($brand-primary, 0.3);
|
||||
box-shadow: 0 4rpx 12rpx rgba($brand-primary, 0.05);
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
width: 100%;
|
||||
height: 96rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 999rpx;
|
||||
background: linear-gradient(135deg, rgba($brand-primary, 0.9) 0%, $brand-primary 100%);
|
||||
color: #FFFFFF;
|
||||
box-shadow: 0 12rpx 32rpx rgba($brand-primary, 0.25);
|
||||
}
|
||||
|
||||
.load-state {
|
||||
text-align: center;
|
||||
font-size: 24rpx;
|
||||
padding: 12rpx 0 24rpx;
|
||||
color: #6A7A99;
|
||||
}
|
||||
|
||||
.spin {
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
/* 隐藏 scroll-view 滚动条 */
|
||||
::-webkit-scrollbar {
|
||||
display: none;
|
||||
width: 0 !important;
|
||||
height: 0 !important;
|
||||
-webkit-appearance: none;
|
||||
background: transparent;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user