377 lines
16 KiB
HTML
377 lines
16 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="zh">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>资源匹配 - 供需对接</title>
|
|
<script src="https://cdn.tailwindcss.com"></script>
|
|
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
|
|
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
|
|
<style>
|
|
:root {
|
|
--primary-color: {{ config.primary_color }};
|
|
--secondary-color: {{ config.secondary_color }};
|
|
--bg-color: {{ config.bg_color }};
|
|
--card-bg: rgba(12, 24, 50, 0.5); /* Glassy blue tint */
|
|
--text-color: #ffffff;
|
|
--text-muted: #b0c4de; /* Light blue-grey */
|
|
--accent-glow: rgba(0, 242, 255, 0.6);
|
|
}
|
|
|
|
* {
|
|
box-sizing: border-box;
|
|
/* margin: 0; padding: 0; - Tailwind resets this but good to keep in mind */
|
|
}
|
|
|
|
body {
|
|
font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif;
|
|
background-color: var(--bg-color);
|
|
color: var(--text-color);
|
|
background-image:
|
|
radial-gradient(circle at 50% 0%, #1a3a75 0%, #050814 60%),
|
|
radial-gradient(circle at 85% 30%, rgba(0, 242, 255, 0.1) 0%, transparent 40%),
|
|
radial-gradient(circle at 15% 70%, rgba(0, 102, 255, 0.15) 0%, transparent 40%);
|
|
background-attachment: fixed;
|
|
min-height: 100vh;
|
|
overflow-x: hidden;
|
|
}
|
|
|
|
body::before {
|
|
content: '';
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
background:
|
|
linear-gradient(rgba(255,255,255,0.03) 1px, transparent 1px),
|
|
linear-gradient(90deg, rgba(255,255,255,0.03) 1px, transparent 1px);
|
|
background-size: 50px 50px;
|
|
pointer-events: none;
|
|
z-index: -1;
|
|
}
|
|
|
|
.container {
|
|
width: 100%;
|
|
max-width: 600px;
|
|
margin: 0 auto;
|
|
padding: 20px;
|
|
position: relative;
|
|
z-index: 1;
|
|
}
|
|
|
|
.header-img {
|
|
width: 100%;
|
|
max-height: 220px;
|
|
object-fit: cover;
|
|
border-radius: 12px;
|
|
margin-bottom: 25px;
|
|
box-shadow: 0 0 20px rgba(0, 242, 255, 0.2);
|
|
border: 1px solid rgba(0, 242, 255, 0.3);
|
|
}
|
|
|
|
.glass-card {
|
|
background: var(--card-bg);
|
|
backdrop-filter: blur(20px);
|
|
-webkit-backdrop-filter: blur(20px);
|
|
border: 1px solid rgba(0, 242, 255, 0.2);
|
|
box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.5), inset 0 0 20px rgba(0, 242, 255, 0.05);
|
|
border-radius: 16px;
|
|
transition: transform 0.3s ease, box-shadow 0.3s ease;
|
|
}
|
|
|
|
h1, h2, h3 {
|
|
color: #fff;
|
|
}
|
|
|
|
h1 {
|
|
background: linear-gradient(180deg, #ffffff 0%, #d0eaff 100%);
|
|
-webkit-background-clip: text;
|
|
-webkit-text-fill-color: transparent;
|
|
text-shadow: 0 0 30px rgba(0, 242, 255, 0.4);
|
|
}
|
|
|
|
.text-primary { color: var(--primary-color); }
|
|
.text-muted { color: var(--text-muted); }
|
|
|
|
/* Custom Input Style to match index.html */
|
|
.custom-input {
|
|
width: 100%;
|
|
padding: 14px 16px;
|
|
background: rgba(0, 0, 0, 0.4);
|
|
border: 1px solid rgba(0, 242, 255, 0.3);
|
|
border-radius: 8px;
|
|
color: white;
|
|
font-size: 1rem;
|
|
outline: none;
|
|
transition: all 0.3s;
|
|
box-shadow: inset 0 2px 5px rgba(0,0,0,0.3);
|
|
}
|
|
.custom-input:focus {
|
|
border-color: var(--primary-color);
|
|
box-shadow: 0 0 15px rgba(0, 242, 255, 0.3), inset 0 2px 5px rgba(0,0,0,0.3);
|
|
background: rgba(0, 0, 0, 0.6);
|
|
}
|
|
|
|
/* Custom Button Style */
|
|
.custom-btn {
|
|
background: linear-gradient(90deg, #0051ff 0%, #00f2ff 100%);
|
|
color: #fff;
|
|
border: none;
|
|
box-shadow: 0 4px 15px rgba(0, 242, 255, 0.3);
|
|
text-shadow: 0 1px 2px rgba(0,0,0,0.2);
|
|
transition: all 0.3s;
|
|
}
|
|
.custom-btn:hover:not(:disabled) {
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 6px 20px rgba(0, 242, 255, 0.5);
|
|
background: linear-gradient(90deg, #0040cc 0%, #00d0dd 100%);
|
|
}
|
|
.custom-btn:disabled {
|
|
background: #2a3b55;
|
|
cursor: not-allowed;
|
|
opacity: 0.6;
|
|
box-shadow: none;
|
|
}
|
|
|
|
/* Tab Buttons */
|
|
.tab-btn {
|
|
background: rgba(255, 255, 255, 0.05);
|
|
color: var(--text-muted);
|
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
transition: all 0.3s;
|
|
}
|
|
.tab-btn.active {
|
|
background: rgba(0, 242, 255, 0.15);
|
|
color: #fff;
|
|
border-color: var(--primary-color);
|
|
box-shadow: 0 0 15px rgba(0, 242, 255, 0.2);
|
|
}
|
|
|
|
/* Avatar */
|
|
.avatar-circle {
|
|
background: linear-gradient(135deg, var(--secondary-color), var(--primary-color));
|
|
color: white;
|
|
box-shadow: 0 0 10px rgba(0, 242, 255, 0.3);
|
|
}
|
|
|
|
/* Tags */
|
|
.tag {
|
|
background: rgba(0, 242, 255, 0.1);
|
|
color: var(--primary-color);
|
|
border: 1px solid rgba(0, 242, 255, 0.2);
|
|
}
|
|
|
|
/* Mobile specific adjustments */
|
|
@media (max-width: 640px) {
|
|
.container { padding: 15px; }
|
|
h1 { font-size: 1.5rem; }
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div id="app" class="container pb-20">
|
|
<!-- Header Image -->
|
|
<img src="{{ config.header_image }}" alt="{{ config.event_title }}" class="header-img" onerror="this.style.display='none'">
|
|
|
|
<!-- Header Info -->
|
|
<div class="glass-card p-6 mb-6 text-center relative overflow-hidden">
|
|
<!-- Background Decoration -->
|
|
<div class="absolute top-0 left-0 w-full h-1 bg-gradient-to-r from-blue-600 to-cyan-400"></div>
|
|
|
|
<h1 class="text-2xl font-bold mb-2 tracking-wide">资源雷达 📡</h1>
|
|
<p class="text-sm text-muted">发现全场潜在商机与合作伙伴</p>
|
|
|
|
<!-- User Stats -->
|
|
<div v-if="user" class="mt-6 flex items-center justify-between bg-black/20 rounded-lg p-4 border border-white/10">
|
|
<div class="flex items-center text-left">
|
|
<div class="avatar-circle w-10 h-10 rounded-full flex items-center justify-center mr-3 text-lg font-bold shrink-0">
|
|
<i class="fas fa-user"></i>
|
|
</div>
|
|
<div>
|
|
<div class="font-bold text-white" v-text="user.name"></div>
|
|
<div class="text-xs text-muted truncate max-w-[120px]" v-text="user.industry_company"></div>
|
|
</div>
|
|
</div>
|
|
<div class="text-right">
|
|
<div class="text-xs text-muted mb-1">社交点数</div>
|
|
<div class="text-xl font-bold text-yellow-300 drop-shadow-md"><span v-text="user.points"></span> 💎</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Login / Search Form -->
|
|
<div v-if="!user">
|
|
<div class="glass-card p-8">
|
|
<h2 class="text-xl font-bold mb-6 text-center border-b border-white/10 pb-4">开启资源匹配</h2>
|
|
<div class="mb-6">
|
|
<label class="block text-primary text-sm font-bold mb-3 pl-1">手机号</label>
|
|
<input v-model="phone" type="tel" class="custom-input" placeholder="请输入您的手机号">
|
|
</div>
|
|
<button @click="login" :disabled="loading" class="w-full custom-btn py-3 px-4 rounded-lg font-bold flex items-center justify-center">
|
|
<span v-if="loading"><i class="fas fa-spinner fa-spin mr-2"></i>匹配中...</span>
|
|
<span v-else>开始匹配</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Results -->
|
|
<div v-else class="space-y-6">
|
|
|
|
<!-- Category Tabs -->
|
|
<div class="flex space-x-3 overflow-x-auto pb-2 scrollbar-hide">
|
|
<button @click="activeTab = 'customers'" :class="['px-5 py-2 rounded-full text-sm font-bold whitespace-nowrap tab-btn', activeTab === 'customers' ? 'active' : '']">
|
|
全场客户 (<span v-text="matches.customers.length"></span>)
|
|
</button>
|
|
<button @click="activeTab = 'partners'" :class="['px-5 py-2 rounded-full text-sm font-bold whitespace-nowrap tab-btn', activeTab === 'partners' ? 'active' : '']">
|
|
合作伙伴 (<span v-text="matches.partners.length"></span>)
|
|
</button>
|
|
<button @click="activeTab = 'peers'" :class="['px-5 py-2 rounded-full text-sm font-bold whitespace-nowrap tab-btn', activeTab === 'peers' ? 'active' : '']">
|
|
同行 (<span v-text="matches.peers.length"></span>)
|
|
</button>
|
|
</div>
|
|
|
|
<!-- List -->
|
|
<div class="space-y-4">
|
|
<div v-if="matches[activeTab].length === 0" class="text-center py-16 text-muted glass-card">
|
|
<i class="fas fa-satellite text-4xl mb-4 opacity-50"></i>
|
|
<p>暂无匹配结果</p>
|
|
</div>
|
|
|
|
<div v-for="(person, index) in matches[activeTab]" :key="index" class="glass-card p-5 relative overflow-hidden group hover:border-cyan-400/50 transition-colors">
|
|
<!-- Match Badge -->
|
|
<div class="absolute top-0 right-0 bg-gradient-to-bl from-yellow-500/20 to-transparent text-yellow-300 text-xs px-3 py-1 rounded-bl-lg font-bold border-l border-b border-yellow-500/20">
|
|
匹配度 <span v-text="Math.round(person.score * 100)"></span>%
|
|
</div>
|
|
|
|
<div class="flex items-start mb-4">
|
|
<div class="avatar-circle w-12 h-12 rounded-full flex items-center justify-center mr-4 text-xl font-bold shrink-0">
|
|
<span v-text="person.name[0]"></span>
|
|
</div>
|
|
<div class="flex-1 min-w-0">
|
|
<h3 class="font-bold text-lg text-white mb-1" v-text="person.name"></h3>
|
|
<p class="text-sm text-muted flex items-center truncate">
|
|
<i class="fas fa-building mr-2 opacity-70"></i>
|
|
<span v-text="person.company_name || '暂无公司信息'"></span>
|
|
</p>
|
|
<p class="text-xs text-muted/70 mt-1 pl-6 truncate" v-text="person.position || '职位未知'"></p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Tags -->
|
|
<div class="flex flex-wrap gap-2 mb-4">
|
|
<span class="tag text-xs px-2 py-1 rounded flex items-center">
|
|
<i class="fas fa-briefcase mr-1 opacity-70"></i><span v-text="person.industry"></span>
|
|
</span>
|
|
<span class="tag text-xs px-2 py-1 rounded flex items-center">
|
|
<i class="fas fa-bullseye mr-1 opacity-70"></i>
|
|
<span class="truncate max-w-[200px]" v-text="person.vision_2026 || '暂无愿景'"></span>
|
|
</span>
|
|
</div>
|
|
|
|
<!-- Contact Action -->
|
|
<div class="border-t border-white/10 pt-4 mt-2">
|
|
<div v-if="person.unlocked" class="bg-green-900/20 border border-green-500/30 p-3 rounded-lg text-sm text-green-400">
|
|
<p class="mb-1"><i class="fas fa-phone mr-2"></i><span v-text="person.phone"></span></p>
|
|
<p><i class="fas fa-map-marker-alt mr-2"></i><span v-text="person.location"></span></p>
|
|
</div>
|
|
<button v-else @click="unlockContact(person)" class="w-full bg-white/5 hover:bg-white/10 text-white text-sm py-2 rounded-lg transition border border-white/10 flex items-center justify-center group-hover:border-cyan-400/30">
|
|
<i class="fas fa-lock mr-2 text-muted group-hover:text-cyan-400"></i> 查看联系方式 (-1 💎)
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Toast -->
|
|
<div v-if="toast.show" class="fixed bottom-10 left-1/2 transform -translate-x-1/2 bg-gray-900 border border-cyan-500/50 text-white px-6 py-3 rounded-lg shadow-[0_0_20px_rgba(0,242,255,0.3)] text-sm transition-opacity duration-300 z-50 flex items-center" :class="{'opacity-0': !toast.show}">
|
|
<i class="fas fa-info-circle text-primary mr-2"></i>
|
|
<span v-text="toast.message"></span>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
const { createApp, ref, reactive } = Vue
|
|
|
|
createApp({
|
|
setup() {
|
|
const phone = ref('')
|
|
const user = ref(null)
|
|
const loading = ref(false)
|
|
const activeTab = ref('customers')
|
|
const matches = reactive({
|
|
customers: [],
|
|
partners: [],
|
|
peers: []
|
|
})
|
|
const toast = reactive({ show: false, message: '' })
|
|
|
|
const showToast = (msg) => {
|
|
toast.message = msg
|
|
toast.show = true
|
|
setTimeout(() => toast.show = false, 3000)
|
|
}
|
|
|
|
const login = async () => {
|
|
if (!phone.value) return showToast('请输入手机号')
|
|
loading.value = true
|
|
try {
|
|
const res = await fetch('/api/resource-match', {
|
|
method: 'POST',
|
|
headers: {'Content-Type': 'application/json'},
|
|
body: JSON.stringify({ phone: phone.value })
|
|
})
|
|
const data = await res.json()
|
|
if (data.success) {
|
|
user.value = data.user
|
|
matches.customers = data.matches.customers
|
|
matches.partners = data.matches.partners
|
|
matches.peers = data.matches.peers
|
|
} else {
|
|
showToast(data.message || '匹配失败')
|
|
}
|
|
} catch (e) {
|
|
showToast('网络错误')
|
|
} finally {
|
|
loading.value = false
|
|
}
|
|
}
|
|
|
|
const unlockContact = async (person) => {
|
|
if (user.value.points <= 0) {
|
|
return showToast('社交点数不足,请联系工作人员充值')
|
|
}
|
|
if (!confirm(`确定消耗1点数查看 ${person.name} 的联系方式吗?`)) return
|
|
|
|
try {
|
|
const res = await fetch('/api/unlock-contact', {
|
|
method: 'POST',
|
|
headers: {'Content-Type': 'application/json'},
|
|
body: JSON.stringify({
|
|
my_phone: user.value.phone,
|
|
target_id: person.new_id
|
|
})
|
|
})
|
|
const data = await res.json()
|
|
if (data.success) {
|
|
person.phone = data.contact.phone
|
|
person.location = data.contact.location
|
|
person.unlocked = true
|
|
user.value.points = data.remaining_points
|
|
showToast('解锁成功!')
|
|
} else {
|
|
showToast(data.message)
|
|
}
|
|
} catch (e) {
|
|
showToast('解锁失败')
|
|
}
|
|
}
|
|
|
|
return {
|
|
phone, user, loading, activeTab, matches, login, unlockContact, toast
|
|
}
|
|
}
|
|
}).mount('#app')
|
|
</script>
|
|
</body>
|
|
</html> |