pp
This commit is contained in:
377
templates/resource_match.html
Normal file
377
templates/resource_match.html
Normal file
@@ -0,0 +1,377 @@
|
||||
<!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: #00f2ff; /* Cyan from the AI sphere/text */
|
||||
--secondary-color: #0066ff; /* Rich blue */
|
||||
--bg-color: #050814; /* Deep space blue/black */
|
||||
--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="/static/image.jpg" alt="Header Image" 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>
|
||||
Reference in New Issue
Block a user