Files
QSY-lvjuxcx/components/contract-alert/contract-alert.vue
2026-06-29 09:42:53 +08:00

191 lines
4.2 KiB
Vue

<template>
<view v-if="activeAlert" class="contract-alert-float">
<view class="alert-panel" :class="{ 'is-collapsed': !isExpanded }" @click="handleBoxClick">
<view class="alert-icon-wrap">
<text class="tn-icon-edit-write-fill"></text>
</view>
<view class="alert-main">
<view class="alert-text">
<view class="alert-title">{{ activeAlert.title }}</view>
<view class="alert-desc">{{ activeAlert.desc }}</view>
</view>
<view class="alert-action" @click.stop="handlePrimaryAction">{{ activeAlert.actionText }}</view>
</view>
</view>
</view>
</template>
<script>
export default {
name: 'ContractAlert',
props: {
pendingContracts: {
type: Array,
default: () => []
}
},
data() {
return {
isExpanded: true,
collapseTimer: null
}
},
computed: {
activeAlert() {
if (!Array.isArray(this.pendingContracts) || !this.pendingContracts.length) {
return null
}
return {
title: this.t('pendingTitle'),
desc: this.t('pendingDesc', { count: this.pendingContracts.length }),
actionText: this.t('actionText')
}
}
},
watch: {
activeAlert: {
immediate: true,
handler(value) {
if (!value) {
this.clearCollapseTimer()
return
}
this.isExpanded = true
this.startCollapseTimer()
}
}
},
beforeDestroy() {
this.clearCollapseTimer()
},
methods: {
t(key, params) {
return this.$translate(this.i18n, key, params)
},
clearCollapseTimer() {
if (this.collapseTimer) {
clearTimeout(this.collapseTimer)
this.collapseTimer = null
}
},
startCollapseTimer() {
this.clearCollapseTimer()
this.collapseTimer = setTimeout(() => {
this.isExpanded = false
}, 3000)
},
handleBoxClick() {
if (!this.activeAlert) {
return
}
if (!this.isExpanded) {
this.isExpanded = true
this.startCollapseTimer()
return
}
this.handlePrimaryAction()
},
handlePrimaryAction() {
const contract = this.pendingContracts[0]
if (!contract) {
return
}
uni.navigateTo({
url: `/pkgService/contract_sign/contract_sign?no=${encodeURIComponent(contract.no || contract.contractNo || '')}&orderNo=${encodeURIComponent(contract.orderNo || '')}`
})
}
}
}
</script>
<style lang="scss" scoped>
.contract-alert-float {
position: fixed;
right: 0;
bottom: calc(180rpx + env(safe-area-inset-bottom));
z-index: 999;
}
.alert-panel {
display: flex;
align-items: center;
max-width: 530rpx;
padding: 16rpx 18rpx;
border-radius: 28rpx 0 0 28rpx;
background: rgba(255, 255, 255, 0.98);
border: 1px solid rgba($brand-primary, 0.12);
border-right: none;
box-shadow: 0 16rpx 36rpx rgba($brand-primary, 0.12);
backdrop-filter: blur(16px);
transition: all 0.35s ease;
}
.alert-panel.is-collapsed {
transform: translateX(calc(100% - 106rpx));
padding-right: 0;
}
.alert-panel.is-collapsed .alert-main {
width: 0;
margin-left: 0;
opacity: 0;
}
.alert-icon-wrap {
width: 68rpx;
height: 68rpx;
border-radius: 20rpx;
display: flex;
align-items: center;
justify-content: center;
margin-right: 16rpx;
flex-shrink: 0;
color: #ffffff;
background: linear-gradient(135deg, rgba($brand-primary, 0.9) 0%, $brand-primary 100%);
box-shadow: 0 6rpx 16rpx rgba($brand-primary, 0.2);
}
.alert-main {
display: flex;
align-items: center;
overflow: hidden;
opacity: 1;
transition: all 0.35s ease;
}
.alert-text {
margin-right: 18rpx;
max-width: 260rpx;
}
.alert-title {
font-size: 26rpx;
font-weight: 700;
color: #1f2d3d;
}
.alert-desc {
margin-top: 6rpx;
font-size: 22rpx;
color: #7c8da0;
white-space: nowrap;
}
.alert-action {
padding: 10rpx 28rpx;
border-radius: 30rpx;
font-size: 24rpx;
font-weight: 700;
color: #ffffff;
background: linear-gradient(135deg, rgba($brand-primary, 0.9) 0%, $brand-primary 100%);
box-shadow: 0 6rpx 16rpx rgba($brand-primary, 0.2);
white-space: nowrap;
transition: all 0.3s;
}
.alert-action:active {
transform: scale(0.96);
box-shadow: 0 4rpx 10rpx rgba($brand-primary, 0.15);
}
</style>