8 changed files with 1563 additions and 496 deletions
-
614package-lock.json
-
2package.json
-
588src/assets/PrizePanel1.vue
-
6src/views/choujiang/index.vue
-
87src/views/choujiang/lottery/CardItem.vue
-
428src/views/choujiang/lottery/Lottery3D.vue
-
252src/views/choujiang/lottery/PrizePanel.vue
-
78src/views/choujiang/lottery/dataManager.js
614
package-lock.json
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,588 @@ |
|||||
|
<template> |
||||
|
<div class="prize-panel-root"> |
||||
|
<div class="prize-panel-list" v-if="prizes && prizes.length" :style="containerStyle"> |
||||
|
<div |
||||
|
class="prize-panel-item" |
||||
|
v-for="(prize, idx) in prizes" |
||||
|
:key="prize.type || idx" |
||||
|
:class="{ |
||||
|
'revealed-highlight': idx === lastRevealedIdx, |
||||
|
'winner-mode-highlight': showWinnerList && idx === lastRevealedIdx |
||||
|
}" |
||||
|
@click="showWinnerList ? null : handleReveal(idx)" |
||||
|
:style="{ cursor: showWinnerList ? 'default' : 'pointer' }" |
||||
|
:ref="el => setPrizeRef(el, idx)" |
||||
|
v-show="!shouldHideOtherPrizes || idx === lastRevealedIdx" |
||||
|
> |
||||
|
<div v-if="isRevealed(idx)" class="prize-card"> |
||||
|
<div class="prize-img-wrap"> |
||||
|
<img class="prize-img" :src="prize.img" :alt="prize.title" /> |
||||
|
</div> |
||||
|
<div class="prize-info"> |
||||
|
<div class="prize-row prize-row-top"> |
||||
|
<span class="prize-level">{{ prize.title }}</span> |
||||
|
<span class="prize-name">{{ prize.text }}</span> |
||||
|
</div> |
||||
|
<div class="prize-row prize-row-bottom"> |
||||
|
<div class="progress-bar-bg"> |
||||
|
<div |
||||
|
class="progress-bar-fill" |
||||
|
:style="{ width:getProgressPercent(prize) + '%' }" |
||||
|
></div> |
||||
|
<span class="progress-bar-text"> |
||||
|
{{ prize.count-getLeftCount(prize) }}/{{ prize.count }} |
||||
|
</span> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div v-else class="prize-card prize-card-mask"> |
||||
|
<img src="../../../assets/daijiemi.png" alt="待揭秘" class="prize-mask-img" /> |
||||
|
</div> |
||||
|
</div> |
||||
|
<!-- 隐藏占位div --> |
||||
|
<div v-show="!shouldHideOtherPrizes"></div> |
||||
|
<div v-show="!shouldHideOtherPrizes"></div> |
||||
|
<div v-show="!shouldHideOtherPrizes"></div> |
||||
|
<div v-show="!shouldHideOtherPrizes"></div> |
||||
|
<!-- 动态定位的获奖名单按钮 --> |
||||
|
<div |
||||
|
class="prize-panel-footer" |
||||
|
:class="{ 'winner-mode': shouldHideOtherPrizes }" |
||||
|
:style="winnerBtnStyle" |
||||
|
> |
||||
|
<div class="arrow-up" @click="openWinnerList"></div> |
||||
|
<button ref="winnerBtnRef" class="winner-btn" @click="toggleWinnerList"> |
||||
|
{{ showWinnerList ? '关闭名单' : '获奖名单' }} |
||||
|
</button> |
||||
|
<div |
||||
|
v-if="showWinnerList" |
||||
|
class="winner-modal-mask" |
||||
|
@click="closeWinnerList" |
||||
|
> |
||||
|
<div |
||||
|
class="winner-modal" |
||||
|
:style="{ position: 'absolute', left: modalLeft + 'px', top: modalTop + 'px' }" |
||||
|
@click.stop |
||||
|
> |
||||
|
<div class="winner-modal-header"> |
||||
|
<div class="winner-modal-title">Homily ID</div> |
||||
|
<div class="winner-modal-close" @click="closeWinnerList">×</div> |
||||
|
</div> |
||||
|
<ul class="winner-list"> |
||||
|
<li v-for="(user, idx) in fakeWinners" :key="idx"> |
||||
|
<!-- <span>{{ user.id }}</span> - <span>{{ user.name }}</span> - --> |
||||
|
<span>{{ user.id }}</span> |
||||
|
<span>{{ user.prize }}</span> |
||||
|
</li> |
||||
|
</ul> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script setup> |
||||
|
import { ref, computed, nextTick, watch } from "vue"; |
||||
|
const props = defineProps({ |
||||
|
prizes: Array, |
||||
|
}); |
||||
|
// 新增:控制已揭秘奖品数量 |
||||
|
const revealedCount = ref(0); |
||||
|
// 新增:记录最新揭秘的奖品索引 |
||||
|
const lastRevealedIdx = ref(-1); |
||||
|
// 新增:奖品引用数组 |
||||
|
const prizeRefs = ref([]); |
||||
|
// 新增:获奖名单按钮样式 |
||||
|
const winnerBtnStyle = ref({ |
||||
|
position: 'absolute', |
||||
|
left: '0', |
||||
|
bottom: '0', |
||||
|
width: '100%' |
||||
|
}); |
||||
|
|
||||
|
// 新增:容器样式计算属性 |
||||
|
const containerStyle = computed(() => { |
||||
|
if (shouldHideOtherPrizes.value) { |
||||
|
return { |
||||
|
justifyContent: 'flex-start', |
||||
|
alignItems: 'flex-start', |
||||
|
paddingTop: '20px' |
||||
|
}; |
||||
|
} |
||||
|
return {}; |
||||
|
}); |
||||
|
|
||||
|
// 设置奖品引用 |
||||
|
function setPrizeRef(el, idx) { |
||||
|
if (el) { |
||||
|
prizeRefs.value[idx] = el; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 计算哪些奖品已揭秘 |
||||
|
const isRevealed = idx => idx >= (props.prizes?.length || 0) - revealedCount.value; |
||||
|
// 允许点击的卡片index |
||||
|
const nextRevealIdx = computed(() => (props.prizes?.length || 0) - revealedCount.value - 1); |
||||
|
// 卡片点击事件 |
||||
|
function handleReveal(idx) { |
||||
|
if (idx === nextRevealIdx.value) { |
||||
|
revealedCount.value++; |
||||
|
lastRevealedIdx.value = idx; // 记录最新揭秘的索引 |
||||
|
} |
||||
|
} |
||||
|
// 计算未抽取数量 |
||||
|
function getLeftCount(prize) { |
||||
|
// 这里假设奖品有 type 字段,且 dataManager.state.basicData.luckyUsers 可用 |
||||
|
// 由于本组件无 luckyUsers 数据,建议父组件传入或全局可访问 |
||||
|
// 这里用 window.dataManager 兼容演示 |
||||
|
let luckyUsers = |
||||
|
(window.dataManager && window.dataManager.state.basicData.luckyUsers) || {}; |
||||
|
let got = luckyUsers[prize.type]?.length || 0; |
||||
|
return prize.count - got; |
||||
|
} |
||||
|
|
||||
|
// 新增部分 |
||||
|
const showWinnerList = ref(false); |
||||
|
const fakeWinners = ref([ |
||||
|
{ id: "90044065", name: "张三", prize: "六等奖" }, |
||||
|
{ id: "90044066", name: "李四", prize: "六等奖" }, |
||||
|
{ id: "90044067", name: "王五", prize: "六等奖" }, |
||||
|
{ id: "90044068", name: "赵六", prize: "六等奖" }, |
||||
|
{ id: "90044069", name: "小明", prize: "六等奖" }, |
||||
|
]); |
||||
|
|
||||
|
// 新增:控制是否隐藏其他奖品 |
||||
|
const shouldHideOtherPrizes = computed(() => { |
||||
|
return showWinnerList.value && lastRevealedIdx.value >= 0; |
||||
|
}); |
||||
|
|
||||
|
// 新增:定位获奖名单按钮到高亮奖品下方 |
||||
|
function positionWinnerBtn() { |
||||
|
if (lastRevealedIdx.value >= 0 && prizeRefs.value[lastRevealedIdx.value]) { |
||||
|
const highlightedPrize = prizeRefs.value[lastRevealedIdx.value]; |
||||
|
|
||||
|
// 当显示获奖名单且有高亮奖品时,进行特殊定位 |
||||
|
if (shouldHideOtherPrizes.value) { |
||||
|
const rect = highlightedPrize.getBoundingClientRect(); |
||||
|
const containerRect = highlightedPrize.parentElement.getBoundingClientRect(); |
||||
|
|
||||
|
// 计算相对于容器的位置,考虑奖品高度和间距 |
||||
|
const relativeTop = rect.bottom - containerRect.top + 18; // 18px间距 |
||||
|
|
||||
|
winnerBtnStyle.value = { |
||||
|
position: 'absolute', |
||||
|
left: '0', |
||||
|
top: relativeTop + 'px', |
||||
|
width: '100%', |
||||
|
zIndex: '20' |
||||
|
}; |
||||
|
} else { |
||||
|
// 正常模式下,按钮保持在底部,不进行特殊定位 |
||||
|
winnerBtnStyle.value = { |
||||
|
position: 'absolute', |
||||
|
left: '0', |
||||
|
bottom: '0', |
||||
|
width: '100%' |
||||
|
}; |
||||
|
} |
||||
|
} else { |
||||
|
// 如果没有高亮奖品,回到默认位置 |
||||
|
winnerBtnStyle.value = { |
||||
|
position: 'absolute', |
||||
|
left: '0', |
||||
|
bottom: '0', |
||||
|
width: '100%' |
||||
|
}; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function openWinnerList() { |
||||
|
showWinnerList.value = true; |
||||
|
// 只有在有高亮奖品时才重新定位按钮 |
||||
|
if (lastRevealedIdx.value >= 0) { |
||||
|
nextTick(() => { |
||||
|
positionWinnerBtn(); |
||||
|
}); |
||||
|
} else { |
||||
|
// 如果没有高亮奖品,按钮保持在底部 |
||||
|
winnerBtnStyle.value = { |
||||
|
position: 'absolute', |
||||
|
left: '0', |
||||
|
bottom: '0', |
||||
|
width: '100%' |
||||
|
}; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function closeWinnerList() { |
||||
|
showWinnerList.value = false; |
||||
|
// 关闭获奖名单时,按钮回到底部 |
||||
|
nextTick(() => { |
||||
|
winnerBtnStyle.value = { |
||||
|
position: 'absolute', |
||||
|
left: '0', |
||||
|
bottom: '0', |
||||
|
width: '100%' |
||||
|
}; |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
const winnerBtnRef = ref(null); |
||||
|
const modalLeft = ref(0); |
||||
|
const modalTop = ref(0); |
||||
|
|
||||
|
function toggleWinnerList() { |
||||
|
showWinnerList.value = !showWinnerList.value; |
||||
|
if (showWinnerList.value) { |
||||
|
nextTick(() => { |
||||
|
const btn = winnerBtnRef.value; |
||||
|
if (btn) { |
||||
|
const rect = btn.getBoundingClientRect(); |
||||
|
modalLeft.value = rect.left-22; |
||||
|
modalTop.value = rect.bottom + 20; // 4px间距 |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 监听高亮奖品变化,自动重新定位按钮 |
||||
|
watch(lastRevealedIdx, () => { |
||||
|
nextTick(() => { |
||||
|
// 只有在获奖名单模式下才重新定位按钮 |
||||
|
if (lastRevealedIdx.value >= 0 && showWinnerList.value) { |
||||
|
positionWinnerBtn(); |
||||
|
} else if (lastRevealedIdx.value >= 0) { |
||||
|
// 正常模式下,按钮保持在底部 |
||||
|
winnerBtnStyle.value = { |
||||
|
position: 'absolute', |
||||
|
left: '0', |
||||
|
bottom: '0', |
||||
|
width: '100%' |
||||
|
}; |
||||
|
} |
||||
|
}); |
||||
|
}); |
||||
|
|
||||
|
function getProgressPercent(prize) { |
||||
|
const total = prize.count || 1; |
||||
|
const left = getLeftCount(prize); |
||||
|
const got = total - left; |
||||
|
return Math.round((got / total) * 100); |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style scoped> |
||||
|
.prize-panel-list { |
||||
|
position: absolute; |
||||
|
top: 20px; |
||||
|
left: 20px; |
||||
|
background: none; |
||||
|
z-index: 10; |
||||
|
min-width: 320px; |
||||
|
text-align: left; |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
gap: 18px; |
||||
|
/* 新增:支持flexbox排序 */ |
||||
|
align-items: flex-start; |
||||
|
/* 新增:支持滚动和定位 */ |
||||
|
max-height: calc(100vh - 40px); |
||||
|
/* overflow-y: auto; */ |
||||
|
/* 新增:当显示获奖名单时的特殊样式 */ |
||||
|
transition: all 0.3s ease; |
||||
|
} |
||||
|
.prize-panel-item { |
||||
|
background: #ffd283; |
||||
|
border-radius: 6px 6px 6px 6px; |
||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08); |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
min-width: 300px; |
||||
|
transition: opacity 0.3s ease, transform 0.3s ease; |
||||
|
} |
||||
|
.prize-card { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
width: 100%; |
||||
|
padding: 10px 18px; |
||||
|
|
||||
|
} |
||||
|
.prize-img-wrap { |
||||
|
width: 64px; |
||||
|
height: 64px; |
||||
|
border-radius: 50%; |
||||
|
background: #fff; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
overflow: hidden; |
||||
|
margin-right: 18px; |
||||
|
border: 2px solid #fff3e0; |
||||
|
} |
||||
|
.prize-img { |
||||
|
width: 60px; |
||||
|
height: 60px; |
||||
|
object-fit: contain; |
||||
|
} |
||||
|
.prize-info { |
||||
|
flex: 1; |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
justify-content: center; |
||||
|
} |
||||
|
.prize-row { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
background: #ffffff; |
||||
|
border-radius: 93px 93px 93px 93px; |
||||
|
} |
||||
|
.prize-row-top { |
||||
|
margin-bottom: 8px; |
||||
|
} |
||||
|
.prize-level { |
||||
|
background: linear-gradient(90deg, #ff9800 0%, #ff5722 100%); |
||||
|
color: #fff; |
||||
|
border-radius: 15.71px 15.71px 15.71px 15.71px; |
||||
|
padding: 2px 18px; |
||||
|
font-size: 18px; |
||||
|
font-weight: bold; |
||||
|
margin-right: 12px; |
||||
|
} |
||||
|
.prize-name { |
||||
|
font-size: 18px; |
||||
|
color: #d84315; |
||||
|
font-weight: 500; |
||||
|
} |
||||
|
/* .prize-row-bottom { |
||||
|
background: linear-gradient(90deg, #ff9800 0%, #ff5722 100%); |
||||
|
background: #8a3500; |
||||
|
border-radius: 16px; |
||||
|
color: #fff; |
||||
|
font-size: 20px; |
||||
|
font-weight: bold; |
||||
|
padding: 2px 0 2px 0; |
||||
|
justify-content: center; |
||||
|
min-width: 80px; |
||||
|
} */ |
||||
|
.prize-count { |
||||
|
font-size: 20px; |
||||
|
font-weight: bold; |
||||
|
} |
||||
|
.prize-divider { |
||||
|
margin: 0 4px; |
||||
|
font-size: 20px; |
||||
|
} |
||||
|
.prize-total { |
||||
|
font-size: 20px; |
||||
|
font-weight: bold; |
||||
|
} |
||||
|
/* 新增:获奖名单按钮容器样式调整 */ |
||||
|
.prize-panel-footer { |
||||
|
position: absolute; |
||||
|
left: 0; |
||||
|
width: 100%; |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
align-items: center; |
||||
|
z-index: 20; |
||||
|
transition: all 0.3s ease; |
||||
|
/* 移除 bottom 定位,改为动态定位 */ |
||||
|
} |
||||
|
|
||||
|
/* 新增:获奖名单模式下的按钮样式 */ |
||||
|
.prize-panel-footer.winner-mode { |
||||
|
position: relative; |
||||
|
margin-top: 18px; |
||||
|
} |
||||
|
.arrow-up { |
||||
|
width: 36px; |
||||
|
height: 24px; |
||||
|
background: url("@/assets/arrow-up.svg") no-repeat center/contain; |
||||
|
margin-bottom: 4px; |
||||
|
cursor: pointer; |
||||
|
} |
||||
|
.winner-btn { |
||||
|
background: rgba(255, 210, 131, 0.8); |
||||
|
color: #fff; |
||||
|
border: #fff; |
||||
|
border-radius: 8px; |
||||
|
padding: 15px 79px; |
||||
|
font-size: 20px; |
||||
|
font-weight: bold; |
||||
|
cursor: pointer; |
||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08); |
||||
|
transition: all 0.3s ease; |
||||
|
} |
||||
|
|
||||
|
.winner-btn:hover { |
||||
|
background: rgba(255, 210, 131, 1); |
||||
|
transform: translateY(-2px); |
||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); |
||||
|
} |
||||
|
.winner-modal-mask { |
||||
|
position: fixed; |
||||
|
top: 0; |
||||
|
left: 0; |
||||
|
width: 100vw; |
||||
|
height: 100vh; |
||||
|
background: rgba(0,0,0,0.01); |
||||
|
z-index: 1000; |
||||
|
display: flex; |
||||
|
align-items: flex-start; |
||||
|
justify-content: center; |
||||
|
} |
||||
|
.winner-modal { |
||||
|
background:rgba(255, 210, 131, 0.8); |
||||
|
border-radius: 12px; |
||||
|
/* margin-top: 2vh; */ |
||||
|
padding-top: 12px; |
||||
|
min-width: 280px; |
||||
|
max-width: 90vw; |
||||
|
box-shadow: 0 4px 24px rgba(0, 0, 0, 0.12); |
||||
|
position: relative; |
||||
|
} |
||||
|
.winner-modal-header { |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
align-items: center; |
||||
|
margin-bottom: 18px; |
||||
|
padding: 0 12px; |
||||
|
} |
||||
|
.winner-modal-title { |
||||
|
font-size: 22px; |
||||
|
font-weight: bold; |
||||
|
text-align: center; |
||||
|
flex: 1; |
||||
|
} |
||||
|
.winner-modal-close { |
||||
|
font-size: 24px; |
||||
|
color: #d84315; |
||||
|
cursor: pointer; |
||||
|
width: 30px; |
||||
|
height: 30px; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
border-radius: 50%; |
||||
|
background: rgba(255, 255, 255, 0.8); |
||||
|
transition: all 0.2s ease; |
||||
|
} |
||||
|
|
||||
|
.winner-modal-close:hover { |
||||
|
background: rgba(255, 255, 255, 1); |
||||
|
transform: scale(1.1); |
||||
|
} |
||||
|
.winner-list { |
||||
|
max-height: 260px; |
||||
|
/* background: rgba(255, 210, 131, 0.8);/ */ |
||||
|
overflow-y: auto; |
||||
|
padding: 0; |
||||
|
margin: 0; |
||||
|
list-style: none; |
||||
|
} |
||||
|
.winner-list li { |
||||
|
padding: 8px 0; |
||||
|
/* border-bottom: 1px solid #f2f2f2; */ |
||||
|
font-size: 17px; |
||||
|
color: #d84315; |
||||
|
display: flex; |
||||
|
gap: 12px; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
text-align: center; |
||||
|
} |
||||
|
.progress-bar-bg { |
||||
|
position: relative; |
||||
|
width: 220px; |
||||
|
height: 28px; |
||||
|
background: #E9620E; |
||||
|
border-radius: 16px; |
||||
|
overflow: hidden; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
margin: 0 auto; |
||||
|
border: #E13726; |
||||
|
} |
||||
|
.progress-bar-fill { |
||||
|
position: absolute; |
||||
|
left: 0; |
||||
|
top: 0; |
||||
|
height: 100%; |
||||
|
/* background: linear-gradient(90deg, #ff9800 0%, #8a3500 100%); */ |
||||
|
background: #8a3500; |
||||
|
border-radius: 16px; |
||||
|
transition: width 0.4s; |
||||
|
z-index: 1; |
||||
|
} |
||||
|
.progress-bar-text { |
||||
|
position: relative; |
||||
|
width: 100%; |
||||
|
text-align: center; |
||||
|
color: #ffffff; |
||||
|
font-size: 18px; |
||||
|
font-weight: bold; |
||||
|
z-index: 2; |
||||
|
letter-spacing: 1px; |
||||
|
} |
||||
|
.prize-card-mask { |
||||
|
position: relative; |
||||
|
width: 342px; |
||||
|
height: 88px; |
||||
|
display: flex; |
||||
|
/* align-items: center; |
||||
|
justify-content: center; */ |
||||
|
padding: 0; |
||||
|
overflow: hidden; |
||||
|
} |
||||
|
.prize-mask-img { |
||||
|
object-fit: cover; |
||||
|
position: absolute; |
||||
|
width: 100%; |
||||
|
height: 98%; |
||||
|
object-fit: cover; |
||||
|
left: 0; |
||||
|
top: 0; |
||||
|
border-radius: 8px 8px 8px 8px; |
||||
|
|
||||
|
} |
||||
|
.prize-panel-item.revealed-highlight { |
||||
|
border: 3px solid #ff9800; |
||||
|
box-shadow: 0 0 16px 4px #ff9800aa; |
||||
|
transform: scale(1.05); |
||||
|
z-index: 2; |
||||
|
transition: all 0.3s; |
||||
|
/* 确保高亮奖品在顶部 */ |
||||
|
position: relative; |
||||
|
} |
||||
|
|
||||
|
/* 新增:只有在获奖名单模式下才上移 */ |
||||
|
.prize-panel-item.revealed-highlight.winner-mode-highlight { |
||||
|
order: -1; |
||||
|
margin-bottom: 18px; |
||||
|
/* 确保在左侧顶部显示 */ |
||||
|
align-self: flex-start; |
||||
|
width: 100%; |
||||
|
} |
||||
|
|
||||
|
/* 新增:获奖名单模式下的高亮样式 */ |
||||
|
.prize-panel-item.winner-mode-highlight { |
||||
|
transform: scale(1.05); |
||||
|
box-shadow: 0 0 24px 8px #ff9800dd; |
||||
|
border: 4px solid #ff9800; |
||||
|
animation: winnerPulse 2s ease-in-out infinite; |
||||
|
/* 确保在左侧顶部显示 */ |
||||
|
margin-top: 0; |
||||
|
margin-bottom: 18px; |
||||
|
} |
||||
|
|
||||
|
@keyframes winnerPulse { |
||||
|
0%, 100% { |
||||
|
box-shadow: 0 0 24px 8px #ff9800dd; |
||||
|
} |
||||
|
50% { |
||||
|
box-shadow: 0 0 32px 12px #ff9800ff; |
||||
|
} |
||||
|
} |
||||
|
</style> |
Write
Preview
Loading…
Cancel
Save
Reference in new issue