266 lines
5.4 KiB
Vue
266 lines
5.4 KiB
Vue
<template>
|
||
<v-card class="market-card" elevation="0" :rounded="'lg'" @click="navigateToDetail">
|
||
<div class="market-card-content">
|
||
<!-- Top Section -->
|
||
<div class="top-section">
|
||
<!-- Market Image and Title -->
|
||
<div class="image-title-container">
|
||
<v-avatar class="market-image" :size="40" color="#f0f0f0" rounded="sm">
|
||
<v-img v-if="props.imageUrl" :src="props.imageUrl" cover />
|
||
</v-avatar>
|
||
<v-card-title class="market-title" :text="true" :shrink="true">
|
||
{{ marketTitle }}
|
||
</v-card-title>
|
||
</div>
|
||
|
||
<!-- Chance Container -->
|
||
<div class="chance-container">
|
||
<v-progress-circular
|
||
class="progress-bar"
|
||
:size="60"
|
||
:width="4"
|
||
:value="chanceValue"
|
||
:color="progressColor"
|
||
:background="'#e0e0e0'"
|
||
>
|
||
<template v-slot:default>
|
||
<span class="chance-value">{{ chanceValue }}%</span>
|
||
</template>
|
||
</v-progress-circular>
|
||
<span class="chance-label">chance</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Options Section -->
|
||
<div class="options-section">
|
||
<v-btn class="option-yes" :color="'#e6f9e6'" :rounded="'sm'" :text="true">
|
||
<span class="option-text-yes">Yes</span>
|
||
</v-btn>
|
||
<v-btn class="option-no" :color="'#ffe6e6'" :rounded="'sm'" :text="true">
|
||
<span class="option-text-no">No</span>
|
||
</v-btn>
|
||
</div>
|
||
|
||
<!-- Bottom Section -->
|
||
<div class="bottom-section">
|
||
<span class="market-info">{{ marketInfo }}</span>
|
||
<div class="icons-container">
|
||
<v-icon class="gift-icon" size="16">mdi-gift</v-icon>
|
||
<v-icon class="bookmark-icon" size="16">mdi-bookmark</v-icon>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</v-card>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { computed } from 'vue'
|
||
import { useRouter } from 'vue-router'
|
||
|
||
const router = useRouter()
|
||
|
||
const props = defineProps({
|
||
marketTitle: {
|
||
type: String,
|
||
default: 'Mamdan opens city-owned grocery store b...',
|
||
},
|
||
chanceValue: {
|
||
type: Number,
|
||
default: 17,
|
||
},
|
||
marketInfo: {
|
||
type: String,
|
||
default: '$155k Vol.',
|
||
},
|
||
id: {
|
||
type: String,
|
||
default: '1',
|
||
},
|
||
/** 市场图片 URL,由卡片传入供详情页展示 */
|
||
imageUrl: {
|
||
type: String,
|
||
default: '',
|
||
},
|
||
/** 分类标签,如 "Economy · World" */
|
||
category: {
|
||
type: String,
|
||
default: '',
|
||
},
|
||
/** 结算/到期日期,如 "Mar 31, 2026" */
|
||
expiresAt: {
|
||
type: String,
|
||
default: '',
|
||
},
|
||
})
|
||
|
||
// 计算进度条颜色,从红色(0%)到绿色(100%)
|
||
const progressColor = computed(() => {
|
||
// 红色在HSL中是0度,绿色是120度
|
||
const hue = (props.chanceValue / 100) * 120
|
||
return `hsl(${hue}, 100%, 50%)`
|
||
})
|
||
|
||
// 跳转到交易详情页面,将标题、图片等通过 query 传入
|
||
const navigateToDetail = () => {
|
||
router.push({
|
||
path: `/trade-detail/${props.id}`,
|
||
query: {
|
||
title: props.marketTitle,
|
||
imageUrl: props.imageUrl || undefined,
|
||
category: props.category || undefined,
|
||
marketInfo: props.marketInfo || undefined,
|
||
expiresAt: props.expiresAt || undefined,
|
||
chance: String(props.chanceValue),
|
||
},
|
||
})
|
||
}
|
||
</script>
|
||
|
||
<style scoped>
|
||
.market-card {
|
||
position: relative;
|
||
width: 310px;
|
||
height: 160px;
|
||
background-color: #ffffff;
|
||
border-radius: 8px;
|
||
border: 1px solid #e7e7e7;
|
||
padding: 12px;
|
||
cursor: pointer;
|
||
transition: all 0.2s ease;
|
||
}
|
||
|
||
.market-card:hover {
|
||
transform: translateY(-2px);
|
||
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.12);
|
||
border-color: #d0d0d0;
|
||
}
|
||
|
||
.market-card-content {
|
||
display: flex;
|
||
flex-direction: column;
|
||
height: 100%;
|
||
gap: 12px;
|
||
}
|
||
|
||
/* Top Section */
|
||
.top-section {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: flex-start;
|
||
}
|
||
|
||
.image-title-container {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
gap: 12px;
|
||
flex: 1;
|
||
min-width: 0;
|
||
}
|
||
|
||
.market-image {
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.market-title {
|
||
flex: 1;
|
||
min-width: 0;
|
||
font-family: 'Inter', sans-serif;
|
||
font-size: 14px;
|
||
font-weight: 500;
|
||
color: #000000;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
display: -webkit-box;
|
||
-webkit-line-clamp: 2;
|
||
-webkit-box-orient: vertical;
|
||
}
|
||
|
||
.chance-container {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
flex-shrink: 0;
|
||
width: 60px;
|
||
height: 60px;
|
||
}
|
||
|
||
.progress-bar {
|
||
margin-bottom: 2px;
|
||
}
|
||
|
||
.chance-value {
|
||
font-family: 'Inter', sans-serif;
|
||
font-size: 12px;
|
||
font-weight: 600;
|
||
color: #000000;
|
||
}
|
||
|
||
.chance-label {
|
||
font-family: 'Inter', sans-serif;
|
||
font-size: 10px;
|
||
font-weight: normal;
|
||
color: #808080;
|
||
margin-top: 4px;
|
||
}
|
||
|
||
/* Options Section */
|
||
.options-section {
|
||
display: flex;
|
||
gap: 12px;
|
||
}
|
||
|
||
.option-yes {
|
||
flex: 1;
|
||
background-color: #e6f9e6;
|
||
height: 40px;
|
||
}
|
||
|
||
.option-text-yes {
|
||
font-family: 'Inter', sans-serif;
|
||
font-size: 16px;
|
||
font-weight: 500;
|
||
color: #008000;
|
||
}
|
||
|
||
.option-no {
|
||
flex: 1;
|
||
background-color: #ffe6e6;
|
||
height: 40px;
|
||
}
|
||
|
||
.option-text-no {
|
||
font-family: 'Inter', sans-serif;
|
||
font-size: 16px;
|
||
font-weight: 500;
|
||
color: #ff0000;
|
||
}
|
||
|
||
/* Bottom Section */
|
||
.bottom-section {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-top: auto;
|
||
}
|
||
|
||
.market-info {
|
||
font-family: 'Inter', sans-serif;
|
||
font-size: 12px;
|
||
font-weight: normal;
|
||
color: #808080;
|
||
}
|
||
|
||
.icons-container {
|
||
display: flex;
|
||
gap: 16px;
|
||
}
|
||
|
||
.gift-icon {
|
||
color: #808080;
|
||
}
|
||
|
||
.bookmark-icon {
|
||
color: #808080;
|
||
}
|
||
</style>
|