重构关于我们、商家申请和案例页面,优化布局和内容展示
This commit is contained in:
435
website/js/animal.js
Normal file
435
website/js/animal.js
Normal file
@@ -0,0 +1,435 @@
|
||||
// 动物认领功能JavaScript
|
||||
|
||||
class AnimalManager {
|
||||
constructor() {
|
||||
this.apiBaseUrl = 'https://api.jiebanke.com/api/animals';
|
||||
this.animalsContainer = document.getElementById('animals-container');
|
||||
this.init();
|
||||
}
|
||||
|
||||
async init() {
|
||||
await this.loadAnimals();
|
||||
this.bindFilterEvents();
|
||||
}
|
||||
|
||||
async loadAnimals() {
|
||||
try {
|
||||
// 模拟API调用,实际项目中替换为真实API
|
||||
const mockAnimals = [
|
||||
{
|
||||
id: 1,
|
||||
name: "小白",
|
||||
type: "sheep",
|
||||
typeName: "绵羊",
|
||||
age: "baby",
|
||||
ageText: "3个月",
|
||||
gender: "female",
|
||||
description: "温顺可爱的小绵羊,喜欢和人亲近,非常适合初次认领者。",
|
||||
status: "available",
|
||||
image: "images/sheep1.svg",
|
||||
price: 800,
|
||||
location: "绿野农场",
|
||||
health: "健康",
|
||||
personality: "温顺、亲人"
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: "小黑",
|
||||
type: "goat",
|
||||
typeName: "山羊",
|
||||
age: "young",
|
||||
ageText: "1岁",
|
||||
gender: "male",
|
||||
description: "活泼好动的小山羊,喜欢探索新环境,精力充沛。",
|
||||
status: "available",
|
||||
image: "images/goat1.svg",
|
||||
price: 600,
|
||||
location: "阳光牧场",
|
||||
health: "健康",
|
||||
personality: "活泼、好奇"
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: "雪球",
|
||||
type: "rabbit",
|
||||
typeName: "兔子",
|
||||
age: "baby",
|
||||
ageText: "2个月",
|
||||
gender: "female",
|
||||
description: "洁白如雪的小兔子,性格温和,非常适合家庭认领。",
|
||||
status: "reserved",
|
||||
image: "images/rabbit1.svg",
|
||||
price: 300,
|
||||
location: "爱心农场",
|
||||
health: "健康",
|
||||
personality: "温和、安静"
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: "金蛋",
|
||||
type: "chicken",
|
||||
typeName: "鸡",
|
||||
age: "adult",
|
||||
ageText: "2岁",
|
||||
gender: "female",
|
||||
description: "产蛋能力强的母鸡,每天都能提供新鲜鸡蛋。",
|
||||
status: "available",
|
||||
image: "images/chicken1.svg",
|
||||
price: 200,
|
||||
location: "丰收农场",
|
||||
health: "健康",
|
||||
personality: "勤劳、温顺"
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
name: "花花",
|
||||
type: "duck",
|
||||
typeName: "鸭子",
|
||||
age: "young",
|
||||
ageText: "8个月",
|
||||
gender: "female",
|
||||
description: "可爱的鸭子,喜欢在水中嬉戏,性格活泼开朗。",
|
||||
status: "available",
|
||||
image: "images/duck1.svg",
|
||||
price: 250,
|
||||
location: "水乡农场",
|
||||
health: "健康",
|
||||
personality: "活泼、友善"
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
name: "大角",
|
||||
type: "goat",
|
||||
typeName: "山羊",
|
||||
age: "adult",
|
||||
ageText: "3岁",
|
||||
gender: "male",
|
||||
description: "强壮的公山羊,有着漂亮的角,性格独立但温顺。",
|
||||
status: "available",
|
||||
image: "images/goat2.svg",
|
||||
price: 900,
|
||||
location: "山野牧场",
|
||||
health: "健康",
|
||||
personality: "独立、温顺"
|
||||
}
|
||||
];
|
||||
|
||||
this.displayAnimals(mockAnimals);
|
||||
} catch (error) {
|
||||
console.error('加载动物列表失败:', error);
|
||||
this.showError('加载动物列表失败,请刷新页面重试');
|
||||
}
|
||||
}
|
||||
|
||||
displayAnimals(animals) {
|
||||
if (animals.length === 0) {
|
||||
this.animalsContainer.innerHTML = `
|
||||
<div class="col-12 text-center">
|
||||
<div class="alert alert-info">
|
||||
<i class="fa fa-info-circle me-2"></i>
|
||||
暂无可用动物,请稍后再来查看
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
return;
|
||||
}
|
||||
|
||||
const animalsHTML = animals.map(animal => `
|
||||
<div class="col-md-6 col-lg-4 mb-4" data-aos="fade-up">
|
||||
<div class="card animal-card h-100">
|
||||
<img src="${animal.image}" class="card-img-top" alt="${animal.name}">
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between align-items-start mb-3">
|
||||
<h5 class="animal-name">${animal.name}</h5>
|
||||
<span class="status-badge status-${animal.status}">
|
||||
${animal.status === 'available' ? '可认领' : '已被认领'}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="animal-type">
|
||||
<i class="fa fa-paw me-2"></i>
|
||||
${animal.typeName}
|
||||
</div>
|
||||
|
||||
<div class="animal-age">
|
||||
<i class="fa fa-birthday-cake me-2"></i>
|
||||
${animal.ageText}
|
||||
</div>
|
||||
|
||||
<p class="animal-description">
|
||||
${animal.description}
|
||||
</p>
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<span class="text-primary fw-bold">¥${animal.price}</span>
|
||||
<small class="text-muted">${animal.location}</small>
|
||||
</div>
|
||||
|
||||
<div class="action-buttons">
|
||||
<button class="btn btn-primary btn-sm"
|
||||
onclick="animalManager.viewAnimalDetail(${animal.id})"
|
||||
${animal.status !== 'available' ? 'disabled' : ''}>
|
||||
${animal.status === 'available' ? '立即认领' : '已被认领'}
|
||||
</button>
|
||||
<button class="btn btn-outline-secondary btn-sm"
|
||||
onclick="animalManager.showAnimalInfo(${animal.id})">
|
||||
查看详情
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`).join('');
|
||||
|
||||
this.animalsContainer.innerHTML = animalsHTML;
|
||||
}
|
||||
|
||||
bindFilterEvents() {
|
||||
const filterType = document.getElementById('filter-type');
|
||||
const filterAge = document.getElementById('filter-age');
|
||||
const filterStatus = document.getElementById('filter-status');
|
||||
|
||||
if (filterType) filterType.addEventListener('change', () => this.filterAnimals());
|
||||
if (filterAge) filterAge.addEventListener('change', () => this.filterAnimals());
|
||||
if (filterStatus) filterStatus.addEventListener('change', () => this.filterAnimals());
|
||||
}
|
||||
|
||||
async filterAnimals() {
|
||||
const type = document.getElementById('filter-type').value;
|
||||
const age = document.getElementById('filter-age').value;
|
||||
const status = document.getElementById('filter-status').value;
|
||||
|
||||
try {
|
||||
// 模拟筛选功能
|
||||
const mockAnimals = await this.loadAnimals(); // 重新加载数据
|
||||
let filteredAnimals = mockAnimals;
|
||||
|
||||
if (type) {
|
||||
filteredAnimals = filteredAnimals.filter(animal => animal.type === type);
|
||||
}
|
||||
|
||||
if (age) {
|
||||
filteredAnimals = filteredAnimals.filter(animal => animal.age === age);
|
||||
}
|
||||
|
||||
if (status) {
|
||||
filteredAnimals = filteredAnimals.filter(animal => animal.status === status);
|
||||
}
|
||||
|
||||
this.displayAnimals(filteredAnimals);
|
||||
} catch (error) {
|
||||
console.error('筛选动物失败:', error);
|
||||
this.showError('筛选失败,请刷新页面重试');
|
||||
}
|
||||
}
|
||||
|
||||
viewAnimalDetail(animalId) {
|
||||
// 在实际项目中,这里可以跳转到详情页或显示认领表单
|
||||
console.log('查看动物详情:', animalId);
|
||||
|
||||
// 显示认领表单模态框
|
||||
this.showClaimForm(animalId);
|
||||
}
|
||||
|
||||
showAnimalInfo(animalId) {
|
||||
// 显示动物详细信息模态框
|
||||
console.log('显示动物信息:', animalId);
|
||||
|
||||
// 这里可以显示一个包含动物详细信息的模态框
|
||||
alert('动物详细信息功能开发中,即将上线');
|
||||
}
|
||||
|
||||
showClaimForm(animalId) {
|
||||
// 创建认领表单模态框
|
||||
const modalHTML = `
|
||||
<div class="modal fade" id="claimModal" tabindex="-1">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content animal-modal">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">认领申请</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form id="claimForm" class="claim-form">
|
||||
<input type="hidden" id="animalId" value="${animalId}">
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="fullName" class="form-label">姓名</label>
|
||||
<input type="text" class="form-control" id="fullName" required>
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="phone" class="form-label">手机号</label>
|
||||
<input type="tel" class="form-control" id="phone" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="email" class="form-label">邮箱</label>
|
||||
<input type="email" class="form-control" id="email" required>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="purpose" class="form-label">认领目的</label>
|
||||
<textarea class="form-control" id="purpose" rows="3" required></textarea>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="experience" class="form-label">饲养经验</label>
|
||||
<select class="form-select" id="experience" required>
|
||||
<option value="">请选择饲养经验</option>
|
||||
<option value="none">无经验</option>
|
||||
<option value="little">少量经验</option>
|
||||
<option value="rich">丰富经验</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-check mb-3">
|
||||
<input class="form-check-input" type="checkbox" id="agreeTerms" required>
|
||||
<label class="form-check-label" for="agreeTerms">
|
||||
我已阅读并同意<a href="#" onclick="animalManager.showTerms()">认领协议</a>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary btn-lg w-100">
|
||||
提交认领申请
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// 添加模态框到页面
|
||||
document.body.insertAdjacentHTML('beforeend', modalHTML);
|
||||
|
||||
// 显示模态框
|
||||
const modal = new bootstrap.Modal(document.getElementById('claimModal'));
|
||||
modal.show();
|
||||
|
||||
// 绑定表单提交事件
|
||||
document.getElementById('claimForm').addEventListener('submit', this.handleClaimSubmit.bind(this));
|
||||
|
||||
// 模态框关闭时清理
|
||||
document.getElementById('claimModal').addEventListener('hidden.bs.modal', function () {
|
||||
this.remove();
|
||||
});
|
||||
}
|
||||
|
||||
async handleClaimSubmit(event) {
|
||||
event.preventDefault();
|
||||
|
||||
const formData = {
|
||||
animalId: document.getElementById('animalId').value,
|
||||
fullName: document.getElementById('fullName').value,
|
||||
phone: document.getElementById('phone').value,
|
||||
email: document.getElementById('email').value,
|
||||
purpose: document.getElementById('purpose').value,
|
||||
experience: document.getElementById('experience').value
|
||||
};
|
||||
|
||||
// 表单验证
|
||||
if (!this.validateClaimForm(formData)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 模拟API调用
|
||||
console.log('提交认领申请:', formData);
|
||||
|
||||
// 显示成功消息
|
||||
this.showSuccess('认领申请提交成功!我们会尽快联系您');
|
||||
|
||||
// 关闭模态框
|
||||
bootstrap.Modal.getInstance(document.getElementById('claimModal')).hide();
|
||||
|
||||
// 重新加载动物列表
|
||||
await this.loadAnimals();
|
||||
|
||||
} catch (error) {
|
||||
console.error('提交认领申请失败:', error);
|
||||
this.showError('提交失败,请稍后重试');
|
||||
}
|
||||
}
|
||||
|
||||
validateClaimForm(data) {
|
||||
if (!data.fullName.trim()) {
|
||||
this.showError('请输入姓名');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!data.phone.trim() || !/^1[3-9]\d{9}$/.test(data.phone)) {
|
||||
this.showError('请输入正确的手机号');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!data.email.trim() || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(data.email)) {
|
||||
this.showError('请输入正确的邮箱地址');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!data.purpose.trim()) {
|
||||
this.showError('请输入认领目的');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!data.experience) {
|
||||
this.showError('请选择饲养经验');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!document.getElementById('agreeTerms').checked) {
|
||||
this.showError('请同意认领协议');
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
showTerms() {
|
||||
alert('认领协议功能开发中,即将上线');
|
||||
}
|
||||
|
||||
showSuccess(message) {
|
||||
this.showAlert(message, 'success');
|
||||
}
|
||||
|
||||
showError(message) {
|
||||
this.showAlert(message, 'danger');
|
||||
}
|
||||
|
||||
showAlert(message, type) {
|
||||
const alertDiv = document.createElement('div');
|
||||
alertDiv.className = `alert alert-${type} alert-dismissible fade show`;
|
||||
alertDiv.innerHTML = `
|
||||
${message}
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||
`;
|
||||
|
||||
// 插入到页面顶部
|
||||
const container = document.querySelector('.container');
|
||||
container.insertBefore(alertDiv, container.firstChild);
|
||||
|
||||
// 5秒后自动消失
|
||||
setTimeout(() => {
|
||||
if (alertDiv.parentNode) {
|
||||
alertDiv.remove();
|
||||
}
|
||||
}, 5000);
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化动物管理器
|
||||
const animalManager = new AnimalManager();
|
||||
|
||||
// 页面加载完成后初始化
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// AOS动画初始化
|
||||
if (typeof AOS !== 'undefined') {
|
||||
AOS.init({
|
||||
duration: 1000,
|
||||
once: true
|
||||
});
|
||||
}
|
||||
});
|
||||
447
website/js/flower.js
Normal file
447
website/js/flower.js
Normal file
@@ -0,0 +1,447 @@
|
||||
// 送花服务功能管理类
|
||||
class FlowerManager {
|
||||
constructor() {
|
||||
this.flowers = [];
|
||||
this.filteredFlowers = [];
|
||||
this.currentFilters = {
|
||||
category: '',
|
||||
price: '',
|
||||
occasion: ''
|
||||
};
|
||||
this.init();
|
||||
}
|
||||
|
||||
// 初始化方法
|
||||
init() {
|
||||
this.loadFlowers();
|
||||
this.bindEvents();
|
||||
AOS.init({
|
||||
duration: 1000,
|
||||
once: true
|
||||
});
|
||||
}
|
||||
|
||||
// 绑定事件
|
||||
bindEvents() {
|
||||
// 筛选器变化事件
|
||||
document.getElementById('filter-category').addEventListener('change', (e) => {
|
||||
this.currentFilters.category = e.target.value;
|
||||
});
|
||||
|
||||
document.getElementById('filter-price').addEventListener('change', (e) => {
|
||||
this.currentFilters.price = e.target.value;
|
||||
});
|
||||
|
||||
document.getElementById('filter-occasion').addEventListener('change', (e) => {
|
||||
this.currentFilters.occasion = e.target.value;
|
||||
});
|
||||
|
||||
// 筛选按钮点击事件
|
||||
document.querySelector('.flower-filter button').addEventListener('click', () => {
|
||||
this.filterFlowers();
|
||||
});
|
||||
|
||||
// 页面加载完成后显示鲜花
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
this.displayFlowers();
|
||||
});
|
||||
}
|
||||
|
||||
// 加载鲜花数据(模拟数据)
|
||||
loadFlowers() {
|
||||
this.flowers = [
|
||||
{
|
||||
id: 1,
|
||||
name: '浪漫红玫瑰',
|
||||
description: '99朵红玫瑰,象征永恒的爱情和浪漫',
|
||||
price: 299,
|
||||
originalPrice: 399,
|
||||
image: 'images/flower-rose.svg',
|
||||
category: 'love',
|
||||
occasion: 'valentine',
|
||||
tags: ['love', 'romantic'],
|
||||
inStock: true,
|
||||
rating: 4.8
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: '生日祝福花束',
|
||||
description: '多彩花束,包含康乃馨、百合和满天星',
|
||||
price: 199,
|
||||
originalPrice: 259,
|
||||
image: 'images/flower-birthday.svg',
|
||||
category: 'birthday',
|
||||
occasion: 'birthday',
|
||||
tags: ['birthday', 'celebration'],
|
||||
inStock: true,
|
||||
rating: 4.6
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: '商务庆典花篮',
|
||||
description: '大型花篮,适合开业、庆典等商务场合',
|
||||
price: 599,
|
||||
originalPrice: 699,
|
||||
image: 'images/flower-business.svg',
|
||||
category: 'celebration',
|
||||
occasion: 'anniversary',
|
||||
tags: ['business', 'celebration'],
|
||||
inStock: true,
|
||||
rating: 4.7
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: '温馨慰问花束',
|
||||
description: '淡雅花束,表达关怀和慰问之情',
|
||||
price: 159,
|
||||
originalPrice: 199,
|
||||
image: 'images/flower-sympathy.svg',
|
||||
category: 'sympathy',
|
||||
occasion: 'thanks',
|
||||
tags: ['sympathy', 'care'],
|
||||
inStock: true,
|
||||
rating: 4.5
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
name: '永恒爱意花盒',
|
||||
description: '精美礼盒装,包含玫瑰和配花,永久保存',
|
||||
price: 459,
|
||||
originalPrice: 599,
|
||||
image: 'images/flower-box.svg',
|
||||
category: 'love',
|
||||
occasion: 'anniversary',
|
||||
tags: ['love', 'premium'],
|
||||
inStock: true,
|
||||
rating: 4.9
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
name: '感恩花束',
|
||||
description: '向日葵为主的花束,表达感谢和敬意',
|
||||
price: 229,
|
||||
originalPrice: 279,
|
||||
image: 'images/flower-thanks.svg',
|
||||
category: 'celebration',
|
||||
occasion: 'thanks',
|
||||
tags: ['thanks', 'gratitude'],
|
||||
inStock: true,
|
||||
rating: 4.4
|
||||
}
|
||||
];
|
||||
this.filteredFlowers = [...this.flowers];
|
||||
}
|
||||
|
||||
// 筛选鲜花
|
||||
filterFlowers() {
|
||||
this.filteredFlowers = this.flowers.filter(flower => {
|
||||
let match = true;
|
||||
|
||||
// 分类筛选
|
||||
if (this.currentFilters.category && flower.category !== this.currentFilters.category) {
|
||||
match = false;
|
||||
}
|
||||
|
||||
// 价格筛选
|
||||
if (this.currentFilters.price) {
|
||||
const [min, max] = this.currentFilters.price.split('-');
|
||||
if (max === '+') {
|
||||
if (flower.price < parseInt(min)) {
|
||||
match = false;
|
||||
}
|
||||
} else {
|
||||
if (flower.price < parseInt(min) || flower.price > parseInt(max)) {
|
||||
match = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 场合筛选
|
||||
if (this.currentFilters.occasion && flower.occasion !== this.currentFilters.occasion) {
|
||||
match = false;
|
||||
}
|
||||
|
||||
return match;
|
||||
});
|
||||
|
||||
this.displayFlowers();
|
||||
}
|
||||
|
||||
// 显示鲜花列表
|
||||
displayFlowers() {
|
||||
const container = document.getElementById('flowers-container');
|
||||
|
||||
if (this.filteredFlowers.length === 0) {
|
||||
container.innerHTML = `
|
||||
<div class="col-12">
|
||||
<div class="flower-empty">
|
||||
<i class="fa fa-search"></i>
|
||||
<h4>没有找到符合条件的鲜花</h4>
|
||||
<p>请尝试调整筛选条件</p>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
return;
|
||||
}
|
||||
|
||||
container.innerHTML = this.filteredFlowers.map(flower => `
|
||||
<div class="col-md-4 mb-4" data-aos="fade-up">
|
||||
<div class="card flower-card h-100">
|
||||
${flower.originalPrice > flower.price ?
|
||||
`<span class="discount-badge">-${Math.round((1 - flower.price / flower.originalPrice) * 100)}%</span>` :
|
||||
''
|
||||
}
|
||||
<img src="${flower.image}" class="card-img-top" alt="${flower.name}"
|
||||
onerror="this.src='images/flower-placeholder.svg'">
|
||||
<div class="card-body">
|
||||
<div class="flower-tags">
|
||||
${flower.tags.map(tag => `<span class="flower-tag ${tag}">${this.getTagName(tag)}</span>`).join('')}
|
||||
</div>
|
||||
<h5 class="card-title">${flower.name}</h5>
|
||||
<p class="card-text text-muted">${flower.description}</p>
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<div>
|
||||
<span class="flower-price">¥${flower.price}</span>
|
||||
${flower.originalPrice > flower.price ?
|
||||
`<span class="flower-original-price ms-2">¥${flower.originalPrice}</span>` :
|
||||
''
|
||||
}
|
||||
</div>
|
||||
<div class="text-warning">
|
||||
<i class="fa fa-star"></i>
|
||||
<span>${flower.rating}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-grid">
|
||||
<button class="btn btn-flower" onclick="flowerManager.showOrderForm(${flower.id})">
|
||||
立即订购
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`).join('');
|
||||
}
|
||||
|
||||
// 获取标签名称
|
||||
getTagName(tag) {
|
||||
const tagNames = {
|
||||
'love': '爱情',
|
||||
'romantic': '浪漫',
|
||||
'birthday': '生日',
|
||||
'celebration': '庆典',
|
||||
'business': '商务',
|
||||
'sympathy': '慰问',
|
||||
'care': '关怀',
|
||||
'premium': '精品',
|
||||
'thanks': '感谢',
|
||||
'gratitude': '感恩'
|
||||
};
|
||||
return tagNames[tag] || tag;
|
||||
}
|
||||
|
||||
// 显示订购表单
|
||||
showOrderForm(flowerId) {
|
||||
const flower = this.flowers.find(f => f.id === flowerId);
|
||||
if (!flower) return;
|
||||
|
||||
// 创建模态框
|
||||
const modalHtml = `
|
||||
<div class="modal fade" id="orderModal" tabindex="-1">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">订购 ${flower.name}</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<img src="${flower.image}" class="img-fluid rounded" alt="${flower.name}"
|
||||
onerror="this.src='images/flower-placeholder.svg'">
|
||||
<div class="mt-3">
|
||||
<h4>${flower.name}</h4>
|
||||
<p class="text-muted">${flower.description}</p>
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<span class="flower-price">¥${flower.price}</span>
|
||||
<span class="badge bg-success">有货</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<form id="orderForm">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">收花人姓名</label>
|
||||
<input type="text" class="form-control" name="recipientName" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">收花人电话</label>
|
||||
<input type="tel" class="form-control" name="recipientPhone" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">配送地址</label>
|
||||
<textarea class="form-control" name="deliveryAddress" rows="3" required></textarea>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">配送时间</label>
|
||||
<input type="datetime-local" class="form-control" name="deliveryTime" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">祝福语</label>
|
||||
<textarea class="form-control" name="greeting" rows="2"
|
||||
placeholder="写下你的祝福..."></textarea>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">支付方式</label>
|
||||
<select class="form-select" name="paymentMethod" required>
|
||||
<option value="">请选择支付方式</option>
|
||||
<option value="wechat">微信支付</option>
|
||||
<option value="alipay">支付宝</option>
|
||||
<option value="bank">银行转账</option>
|
||||
</select>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
|
||||
<button type="button" class="btn btn-flower" onclick="flowerManager.submitOrder(${flower.id})">
|
||||
确认订购 (¥${flower.price})
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// 添加模态框到页面
|
||||
if (!document.getElementById('orderModal')) {
|
||||
document.body.insertAdjacentHTML('beforeend', modalHtml);
|
||||
}
|
||||
|
||||
// 显示模态框
|
||||
const modal = new bootstrap.Modal(document.getElementById('orderModal'));
|
||||
modal.show();
|
||||
}
|
||||
|
||||
// 提交订单
|
||||
submitOrder(flowerId) {
|
||||
const form = document.getElementById('orderForm');
|
||||
if (!form.checkValidity()) {
|
||||
form.reportValidity();
|
||||
return;
|
||||
}
|
||||
|
||||
const formData = new FormData(form);
|
||||
const orderData = {
|
||||
flowerId,
|
||||
recipientName: formData.get('recipientName'),
|
||||
recipientPhone: formData.get('recipientPhone'),
|
||||
deliveryAddress: formData.get('deliveryAddress'),
|
||||
deliveryTime: formData.get('deliveryTime'),
|
||||
greeting: formData.get('greeting'),
|
||||
paymentMethod: formData.get('paymentMethod'),
|
||||
timestamp: new Date().toISOString()
|
||||
};
|
||||
|
||||
// 模拟提交订单
|
||||
this.showOrderSuccess(orderData);
|
||||
}
|
||||
|
||||
// 显示订单成功
|
||||
showOrderSuccess(orderData) {
|
||||
// 关闭订购模态框
|
||||
const modal = bootstrap.Modal.getInstance(document.getElementById('orderModal'));
|
||||
modal.hide();
|
||||
|
||||
// 显示成功提示
|
||||
Swal.fire({
|
||||
icon: 'success',
|
||||
title: '订购成功!',
|
||||
html: `
|
||||
<div class="text-start">
|
||||
<p><strong>订单详情:</strong></p>
|
||||
<p>收花人:${orderData.recipientName}</p>
|
||||
<p>电话:${orderData.recipientPhone}</p>
|
||||
<p>配送地址:${orderData.deliveryAddress}</p>
|
||||
<p>配送时间:${new Date(orderData.deliveryTime).toLocaleString()}</p>
|
||||
<p class="mt-3">我们会在配送前与您确认,感谢您的订购!</p>
|
||||
</div>
|
||||
`,
|
||||
confirmButtonText: '确定',
|
||||
confirmButtonColor: '#ff6b6b'
|
||||
});
|
||||
}
|
||||
|
||||
// 搜索鲜花
|
||||
searchFlowers(query) {
|
||||
if (!query.trim()) {
|
||||
this.filteredFlowers = [...this.flowers];
|
||||
} else {
|
||||
this.filteredFlowers = this.flowers.filter(flower =>
|
||||
flower.name.toLowerCase().includes(query.toLowerCase()) ||
|
||||
flower.description.toLowerCase().includes(query.toLowerCase()) ||
|
||||
flower.tags.some(tag => tag.toLowerCase().includes(query.toLowerCase()))
|
||||
);
|
||||
}
|
||||
this.displayFlowers();
|
||||
}
|
||||
}
|
||||
|
||||
// 页面加载完成后初始化
|
||||
let flowerManager;
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
flowerManager = new FlowerManager();
|
||||
|
||||
// 添加搜索功能(如果需要)
|
||||
const searchInput = document.createElement('input');
|
||||
searchInput.type = 'text';
|
||||
searchInput.placeholder = '搜索鲜花...';
|
||||
searchInput.className = 'form-control mb-3';
|
||||
searchInput.style.maxWidth = '300px';
|
||||
searchInput.addEventListener('input', (e) => {
|
||||
flowerManager.searchFlowers(e.target.value);
|
||||
});
|
||||
|
||||
const searchContainer = document.createElement('div');
|
||||
searchContainer.className = 'd-flex justify-content-center';
|
||||
searchContainer.appendChild(searchInput);
|
||||
|
||||
const flowersSection = document.getElementById('flowers');
|
||||
if (flowersSection) {
|
||||
flowersSection.querySelector('.container').insertBefore(searchContainer, flowersSection.querySelector('.row'));
|
||||
}
|
||||
});
|
||||
|
||||
// 工具函数:格式化价格
|
||||
function formatPrice(price) {
|
||||
return '¥' + price.toFixed(2);
|
||||
}
|
||||
|
||||
// 工具函数:显示加载状态
|
||||
function showLoading() {
|
||||
const container = document.getElementById('flowers-container');
|
||||
container.innerHTML = `
|
||||
<div class="col-12">
|
||||
<div class="flower-loading">
|
||||
<div class="spinner-border" role="status">
|
||||
<span class="visually-hidden">加载中...</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
// 工具函数:显示错误信息
|
||||
function showError(message) {
|
||||
const container = document.getElementById('flowers-container');
|
||||
container.innerHTML = `
|
||||
<div class="col-12">
|
||||
<div class="alert alert-danger" role="alert">
|
||||
<i class="fa fa-exclamation-triangle me-2"></i>
|
||||
${message}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
@@ -32,6 +32,9 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
|
||||
// 初始化页面交互
|
||||
initPageInteractions();
|
||||
|
||||
// 初始化表单处理
|
||||
initForms();
|
||||
});
|
||||
|
||||
// 创建滚动进度条
|
||||
@@ -195,6 +198,50 @@ function initCallToAction() {
|
||||
});
|
||||
}
|
||||
|
||||
// 表单处理功能
|
||||
function initForms() {
|
||||
const forms = document.querySelectorAll('form');
|
||||
|
||||
forms.forEach(form => {
|
||||
form.addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
// 表单验证
|
||||
if (this.checkValidity()) {
|
||||
// 显示提交成功消息
|
||||
showFormSuccess(this);
|
||||
// 重置表单
|
||||
this.reset();
|
||||
} else {
|
||||
// 显示验证错误
|
||||
this.reportValidity();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// 显示表单提交成功消息
|
||||
function showFormSuccess(form) {
|
||||
// 创建成功消息
|
||||
const successMessage = document.createElement('div');
|
||||
successMessage.className = 'alert alert-success alert-dismissible fade show';
|
||||
successMessage.innerHTML = `
|
||||
<i class="fa fa-check-circle me-2"></i>
|
||||
提交成功!我们会尽快与您联系。
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||
`;
|
||||
|
||||
// 插入到表单前面
|
||||
form.parentNode.insertBefore(successMessage, form);
|
||||
|
||||
// 5秒后自动消失
|
||||
setTimeout(() => {
|
||||
if (successMessage.parentNode) {
|
||||
successMessage.remove();
|
||||
}
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
// 页面性能优化:图片懒加载
|
||||
function initLazyLoading() {
|
||||
if ('IntersectionObserver' in window) {
|
||||
|
||||
455
website/js/reward.js
Normal file
455
website/js/reward.js
Normal file
@@ -0,0 +1,455 @@
|
||||
// 推广奖励功能管理类
|
||||
class RewardManager {
|
||||
constructor() {
|
||||
this.promotionData = [];
|
||||
this.userStats = null;
|
||||
this.init();
|
||||
}
|
||||
|
||||
// 初始化方法
|
||||
init() {
|
||||
this.loadPromotionData();
|
||||
this.loadUserStats();
|
||||
this.bindEvents();
|
||||
AOS.init({
|
||||
duration: 1000,
|
||||
once: true
|
||||
});
|
||||
}
|
||||
|
||||
// 绑定事件
|
||||
bindEvents() {
|
||||
// 表单提交事件
|
||||
const promotionForm = document.getElementById('promotionForm');
|
||||
if (promotionForm) {
|
||||
promotionForm.addEventListener('submit', (e) => {
|
||||
e.preventDefault();
|
||||
this.handlePromotionApplication(e.target);
|
||||
});
|
||||
}
|
||||
|
||||
// 协议链接点击事件
|
||||
const agreeTerms = document.getElementById('agreeTerms');
|
||||
if (agreeTerms) {
|
||||
agreeTerms.addEventListener('change', (e) => {
|
||||
this.toggleAgreement(e.target.checked);
|
||||
});
|
||||
}
|
||||
|
||||
// 页面加载完成后显示数据
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
this.displayPromotionStats();
|
||||
});
|
||||
}
|
||||
|
||||
// 加载推广数据(模拟数据)
|
||||
loadPromotionData() {
|
||||
this.promotionData = [
|
||||
{
|
||||
id: 1,
|
||||
name: '旅行结伴推广',
|
||||
commissionRate: 15,
|
||||
totalEarnings: 12500,
|
||||
successfulReferrals: 84,
|
||||
conversionRate: 12.5
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: '动物认领推广',
|
||||
commissionRate: 20,
|
||||
totalEarnings: 8900,
|
||||
successfulReferrals: 45,
|
||||
conversionRate: 18.2
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: '送花服务推广',
|
||||
commissionRate: 12,
|
||||
totalEarnings: 6700,
|
||||
successfulReferrals: 56,
|
||||
conversionRate: 9.8
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: '新用户注册',
|
||||
commissionRate: 10,
|
||||
totalEarnings: 3200,
|
||||
successfulReferrals: 320,
|
||||
conversionRate: 25.4
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
// 加载用户统计数据(模拟数据)
|
||||
loadUserStats() {
|
||||
this.userStats = {
|
||||
totalEarnings: 31300,
|
||||
availableBalance: 5200,
|
||||
totalReferrals: 505,
|
||||
monthlyEarnings: 4200,
|
||||
joinDate: '2024-01-15',
|
||||
nextPayoutDate: '2024-03-15',
|
||||
performanceLevel: 'Gold',
|
||||
topPromotion: '旅行结伴推广'
|
||||
};
|
||||
}
|
||||
|
||||
// 显示推广统计数据
|
||||
displayPromotionStats() {
|
||||
// 这里可以添加实时统计数据显示逻辑
|
||||
console.log('推广数据加载完成:', this.promotionData);
|
||||
console.log('用户统计数据:', this.userStats);
|
||||
}
|
||||
|
||||
// 处理推广申请
|
||||
handlePromotionApplication(form) {
|
||||
if (!form.checkValidity()) {
|
||||
form.reportValidity();
|
||||
return;
|
||||
}
|
||||
|
||||
const formData = new FormData(form);
|
||||
const applicationData = {
|
||||
name: formData.get('name'),
|
||||
phone: formData.get('phone'),
|
||||
email: formData.get('email'),
|
||||
channel: formData.get('channel'),
|
||||
volume: formData.get('volume'),
|
||||
message: formData.get('message'),
|
||||
applicationDate: new Date().toISOString(),
|
||||
status: 'pending'
|
||||
};
|
||||
|
||||
// 模拟提交申请
|
||||
this.submitPromotionApplication(applicationData);
|
||||
}
|
||||
|
||||
// 提交推广申请
|
||||
submitPromotionApplication(data) {
|
||||
// 模拟API调用
|
||||
console.log('提交推广申请:', data);
|
||||
|
||||
// 显示成功提示
|
||||
this.showApplicationSuccess(data);
|
||||
|
||||
// 重置表单
|
||||
document.getElementById('promotionForm').reset();
|
||||
}
|
||||
|
||||
// 显示申请成功
|
||||
showApplicationSuccess(data) {
|
||||
// 使用SweetAlert或自定义模态框显示成功信息
|
||||
if (typeof Swal !== 'undefined') {
|
||||
Swal.fire({
|
||||
icon: 'success',
|
||||
title: '申请提交成功!',
|
||||
html: `
|
||||
<div class="text-start">
|
||||
<p><strong>申请详情:</strong></p>
|
||||
<p>姓名:${data.name}</p>
|
||||
<p>电话:${data.phone}</p>
|
||||
<p>邮箱:${data.email}</p>
|
||||
<p>推广渠道:${this.getChannelName(data.channel)}</p>
|
||||
<p class="mt-3">我们会在1-3个工作日内审核您的申请,并通过邮件和短信通知您结果。</p>
|
||||
</div>
|
||||
`,
|
||||
confirmButtonText: '确定',
|
||||
confirmButtonColor: '#6c5ce7'
|
||||
});
|
||||
} else {
|
||||
alert('推广申请提交成功!我们会在1-3个工作日内联系您。');
|
||||
}
|
||||
}
|
||||
|
||||
// 获取渠道名称
|
||||
getChannelName(channel) {
|
||||
const channelNames = {
|
||||
'wechat': '微信/朋友圈',
|
||||
'weibo': '微博',
|
||||
'douyin': '抖音',
|
||||
'website': '个人网站/博客',
|
||||
'other': '其他渠道'
|
||||
};
|
||||
return channelNames[channel] || channel;
|
||||
}
|
||||
|
||||
// 切换协议同意状态
|
||||
toggleAgreement(checked) {
|
||||
const submitButton = document.querySelector('#promotionForm button[type="submit"]');
|
||||
if (submitButton) {
|
||||
submitButton.disabled = !checked;
|
||||
}
|
||||
}
|
||||
|
||||
// 生成推广链接
|
||||
generatePromotionLink(userId, campaignType = 'general') {
|
||||
const baseUrl = window.location.origin;
|
||||
const affiliateId = this.generateAffiliateId(userId);
|
||||
|
||||
const campaignParams = {
|
||||
'travel': 'ref=travel_affiliate',
|
||||
'animal': 'ref=animal_affiliate',
|
||||
'flower': 'ref=flower_affiliate',
|
||||
'general': 'ref=general_affiliate'
|
||||
};
|
||||
|
||||
return `${baseUrl}/?${campaignParams[campaignType]}&affiliate=${affiliateId}`;
|
||||
}
|
||||
|
||||
// 生成推广ID
|
||||
generateAffiliateId(userId) {
|
||||
// 简单的ID生成逻辑,实际应用中应该更复杂
|
||||
return `aff_${userId}_${Date.now().toString(36)}`;
|
||||
}
|
||||
|
||||
// 生成推广二维码
|
||||
generatePromotionQRCode(link, elementId) {
|
||||
// 这里可以集成QRCode生成库
|
||||
console.log('生成二维码:', link);
|
||||
// 实际实现需要使用QRCode库
|
||||
}
|
||||
|
||||
// 复制推广链接
|
||||
copyPromotionLink(link) {
|
||||
navigator.clipboard.writeText(link).then(() => {
|
||||
this.showCopySuccess();
|
||||
}).catch(err => {
|
||||
console.error('复制失败:', err);
|
||||
this.showCopyError();
|
||||
});
|
||||
}
|
||||
|
||||
// 显示复制成功
|
||||
showCopySuccess() {
|
||||
if (typeof Swal !== 'undefined') {
|
||||
Swal.fire({
|
||||
icon: 'success',
|
||||
title: '复制成功!',
|
||||
text: '推广链接已复制到剪贴板',
|
||||
timer: 2000,
|
||||
showConfirmButton: false
|
||||
});
|
||||
} else {
|
||||
alert('推广链接已复制!');
|
||||
}
|
||||
}
|
||||
|
||||
// 显示复制错误
|
||||
showCopyError() {
|
||||
if (typeof Swal !== 'undefined') {
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: '复制失败',
|
||||
text: '请手动复制链接',
|
||||
timer: 2000,
|
||||
showConfirmButton: false
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 分享到社交媒体
|
||||
shareToSocialMedia(platform, link, title = '结伴客推广') {
|
||||
const shareUrls = {
|
||||
'wechat': `https://api.qrserver.com/v1/create-qr-code/?size=150x150&data=${encodeURIComponent(link)}`,
|
||||
'weibo': `http://service.weibo.com/share/share.php?url=${encodeURIComponent(link)}&title=${encodeURIComponent(title)}`,
|
||||
'qq': `http://connect.qq.com/widget/shareqq/index.html?url=${encodeURIComponent(link)}&title=${encodeURIComponent(title)}`
|
||||
};
|
||||
|
||||
if (shareUrls[platform]) {
|
||||
window.open(shareUrls[platform], '_blank');
|
||||
}
|
||||
}
|
||||
|
||||
// 计算预计收益
|
||||
calculateEstimatedEarnings(referrals, averageOrderValue, commissionRate) {
|
||||
return referrals * averageOrderValue * (commissionRate / 100);
|
||||
}
|
||||
|
||||
// 格式化金额
|
||||
formatCurrency(amount) {
|
||||
return new Intl.NumberFormat('zh-CN', {
|
||||
style: 'currency',
|
||||
currency: 'CNY'
|
||||
}).format(amount);
|
||||
}
|
||||
|
||||
// 获取性能等级
|
||||
getPerformanceLevel(conversionRate) {
|
||||
if (conversionRate >= 20) return 'Platinum';
|
||||
if (conversionRate >= 15) return 'Gold';
|
||||
if (conversionRate >= 10) return 'Silver';
|
||||
return 'Bronze';
|
||||
}
|
||||
|
||||
// 获取等级颜色
|
||||
getLevelColor(level) {
|
||||
const colors = {
|
||||
'Platinum': '#e5e4e2',
|
||||
'Gold': '#ffd700',
|
||||
'Silver': '#c0c0c0',
|
||||
'Bronze': '#cd7f32'
|
||||
};
|
||||
return colors[level] || '#6c757d';
|
||||
}
|
||||
}
|
||||
|
||||
// 页面加载完成后初始化
|
||||
let rewardManager;
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
rewardManager = new RewardManager();
|
||||
|
||||
// 添加实时统计显示(如果需要)
|
||||
addRealTimeStats();
|
||||
});
|
||||
|
||||
// 添加实时统计显示
|
||||
function addRealTimeStats() {
|
||||
const statsContainer = document.createElement('div');
|
||||
statsContainer.className = 'container mt-4';
|
||||
statsContainer.innerHTML = `
|
||||
<div class="row text-center">
|
||||
<div class="col-md-3 mb-3">
|
||||
<div class="reward-stat">
|
||||
<div class="reward-stat-number">¥31,300</div>
|
||||
<div class="reward-stat-label">总收益</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3 mb-3">
|
||||
<div class="reward-stat">
|
||||
<div class="reward-stat-number">505</div>
|
||||
<div class="reward-stat-label">总推荐数</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3 mb-3">
|
||||
<div class="reward-stat">
|
||||
<div class="reward-stat-number">¥5,200</div>
|
||||
<div class="reward-stat-label">可提现余额</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3 mb-3">
|
||||
<div class="reward-stat">
|
||||
<div class="reward-stat-number">Gold</div>
|
||||
<div class="reward-stat-label">性能等级</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
const joinSection = document.getElementById('join');
|
||||
if (joinSection) {
|
||||
joinSection.parentNode.insertBefore(statsContainer, joinSection);
|
||||
}
|
||||
}
|
||||
|
||||
// 工具函数:显示加载状态
|
||||
function showRewardLoading() {
|
||||
const container = document.getElementById('promotion-stats');
|
||||
if (container) {
|
||||
container.innerHTML = `
|
||||
<div class="reward-loading">
|
||||
<div class="spinner-border" role="status">
|
||||
<span class="visually-hidden">加载中...</span>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
// 工具函数:显示错误信息
|
||||
function showRewardError(message) {
|
||||
const container = document.getElementById('promotion-stats');
|
||||
if (container) {
|
||||
container.innerHTML = `
|
||||
<div class="alert alert-danger" role="alert">
|
||||
<i class="fa fa-exclamation-triangle me-2"></i>
|
||||
${message}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
// 工具函数:格式化日期
|
||||
function formatDate(dateString) {
|
||||
return new Date(dateString).toLocaleDateString('zh-CN', {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric'
|
||||
});
|
||||
}
|
||||
|
||||
// 工具函数:生成进度条HTML
|
||||
function createProgressBar(value, max = 100, type = 'primary') {
|
||||
const percentage = (value / max) * 100;
|
||||
return `
|
||||
<div class="progress mb-2">
|
||||
<div class="progress-bar progress-bar-${type}"
|
||||
role="progressbar"
|
||||
style="width: ${percentage}%"
|
||||
aria-valuenow="${value}"
|
||||
aria-valuemin="0"
|
||||
aria-valuemax="${max}">
|
||||
${percentage.toFixed(1)}%
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
// 推广链接生成器(示例)
|
||||
function setupPromotionLinkGenerator() {
|
||||
const generatorHtml = `
|
||||
<div class="card mt-4">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0">推广链接生成器</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label">推广类型</label>
|
||||
<select class="form-select" id="promotionType">
|
||||
<option value="general">通用推广</option>
|
||||
<option value="travel">旅行结伴</option>
|
||||
<option value="animal">动物认领</option>
|
||||
<option value="flower">送花服务</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label">用户ID</label>
|
||||
<input type="text" class="form-control" id="userId" placeholder="输入用户ID">
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">推广链接</label>
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control" id="generatedLink" readonly>
|
||||
<button class="btn btn-outline-secondary" type="button" onclick="copyGeneratedLink()">
|
||||
<i class="fa fa-copy"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<button class="btn btn-primary" onclick="generatePromotionLink()">
|
||||
生成链接
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
const joinSection = document.getElementById('join');
|
||||
if (joinSection) {
|
||||
joinSection.insertAdjacentHTML('beforebegin', generatorHtml);
|
||||
}
|
||||
}
|
||||
|
||||
// 全局函数供HTML调用
|
||||
function generatePromotionLink() {
|
||||
const type = document.getElementById('promotionType').value;
|
||||
const userId = document.getElementById('userId').value || 'demo_user';
|
||||
const link = rewardManager.generatePromotionLink(userId, type);
|
||||
document.getElementById('generatedLink').value = link;
|
||||
}
|
||||
|
||||
function copyGeneratedLink() {
|
||||
const linkInput = document.getElementById('generatedLink');
|
||||
if (linkInput.value) {
|
||||
rewardManager.copyPromotionLink(linkInput.value);
|
||||
}
|
||||
}
|
||||
266
website/js/travel.js
Normal file
266
website/js/travel.js
Normal file
@@ -0,0 +1,266 @@
|
||||
// 旅行结伴功能JavaScript
|
||||
|
||||
class TravelPlanManager {
|
||||
constructor() {
|
||||
this.apiBaseUrl = 'https://api.jiebanke.com/api/travel';
|
||||
this.plansContainer = document.getElementById('travel-plans-container');
|
||||
this.travelForm = document.getElementById('travel-plan-form');
|
||||
this.init();
|
||||
}
|
||||
|
||||
async init() {
|
||||
await this.loadTravelPlans();
|
||||
this.bindEvents();
|
||||
}
|
||||
|
||||
async loadTravelPlans() {
|
||||
try {
|
||||
// 模拟API调用,实际项目中替换为真实API
|
||||
const mockPlans = [
|
||||
{
|
||||
id: 1,
|
||||
destination: "西藏拉萨",
|
||||
travelDate: "2025-03-15",
|
||||
duration: 10,
|
||||
budget: "5000-10000",
|
||||
description: "寻找志同道合的伙伴一起探索西藏的神秘与美丽,计划游览布达拉宫、大昭寺、纳木错等景点。",
|
||||
requirements: "年龄25-35岁,有高原旅行经验者优先",
|
||||
participants: 3,
|
||||
maxParticipants: 6,
|
||||
creator: "旅行达人小李",
|
||||
createdAt: "2025-01-15"
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
destination: "云南大理",
|
||||
travelDate: "2025-02-20",
|
||||
duration: 7,
|
||||
budget: "2000-5000",
|
||||
description: "大理古城、洱海、苍山七日游,体验白族文化和自然风光。",
|
||||
requirements: "喜欢摄影、热爱自然",
|
||||
participants: 2,
|
||||
maxParticipants: 4,
|
||||
creator: "摄影师小王",
|
||||
createdAt: "2025-01-10"
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
destination: "海南三亚",
|
||||
travelDate: "2025-04-01",
|
||||
duration: 5,
|
||||
budget: "3000-6000",
|
||||
description: "阳光沙滩度假之旅,潜水、冲浪、海鲜美食一网打尽。",
|
||||
requirements: "性格开朗,喜欢水上运动",
|
||||
participants: 4,
|
||||
maxParticipants: 8,
|
||||
creator: "阳光少年小张",
|
||||
createdAt: "2025-01-08"
|
||||
}
|
||||
];
|
||||
|
||||
this.displayPlans(mockPlans);
|
||||
} catch (error) {
|
||||
console.error('加载旅行计划失败:', error);
|
||||
this.showError('加载旅行计划失败,请刷新页面重试');
|
||||
}
|
||||
}
|
||||
|
||||
displayPlans(plans) {
|
||||
if (plans.length === 0) {
|
||||
this.plansContainer.innerHTML = `
|
||||
<div class="col-12 text-center">
|
||||
<div class="alert alert-info">
|
||||
<i class="fa fa-info-circle me-2"></i>
|
||||
暂无旅行计划,快来发布第一个吧!
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
return;
|
||||
}
|
||||
|
||||
const plansHTML = plans.map(plan => `
|
||||
<div class="col-md-6 col-lg-4 mb-4" data-aos="fade-up">
|
||||
<div class="card travel-plan-card h-100">
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between align-items-start mb-3">
|
||||
<h5 class="destination">${plan.destination}</h5>
|
||||
<span class="budget-badge">${plan.budget}元</span>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<div class="travel-date mb-2">
|
||||
<i class="fa fa-calendar-alt me-2"></i>
|
||||
出发日期: ${new Date(plan.travelDate).toLocaleDateString('zh-CN')}
|
||||
</div>
|
||||
<div class="travel-date">
|
||||
<i class="fa fa-clock me-2"></i>
|
||||
行程天数: ${plan.duration}天
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class="card-text text-muted mb-3" style="font-size: 0.9rem;">
|
||||
${plan.description}
|
||||
</p>
|
||||
|
||||
<div class="mb-3">
|
||||
<small class="text-muted">
|
||||
<i class="fa fa-user me-1"></i>
|
||||
伙伴要求: ${plan.requirements || '无特殊要求'}
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div class="participants">
|
||||
<i class="fa fa-users"></i>
|
||||
<span>${plan.participants}/${plan.maxParticipants}人</span>
|
||||
</div>
|
||||
<small class="text-muted">发布者: ${plan.creator}</small>
|
||||
</div>
|
||||
|
||||
<div class="action-buttons mt-3">
|
||||
<button class="btn btn-primary btn-sm" onclick="travelManager.joinPlan(${plan.id})">
|
||||
加入计划
|
||||
</button>
|
||||
<button class="btn btn-outline-secondary btn-sm" onclick="travelManager.viewPlanDetail(${plan.id})">
|
||||
查看详情
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`).join('');
|
||||
|
||||
this.plansContainer.innerHTML = plansHTML;
|
||||
}
|
||||
|
||||
bindEvents() {
|
||||
if (this.travelForm) {
|
||||
this.travelForm.addEventListener('submit', this.handleFormSubmit.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
async handleFormSubmit(event) {
|
||||
event.preventDefault();
|
||||
|
||||
const formData = new FormData(this.travelForm);
|
||||
const planData = {
|
||||
destination: document.getElementById('destination').value,
|
||||
travelDate: document.getElementById('travel-date').value,
|
||||
duration: parseInt(document.getElementById('duration').value),
|
||||
budget: document.getElementById('budget').value,
|
||||
description: document.getElementById('description').value,
|
||||
requirements: document.getElementById('requirements').value
|
||||
};
|
||||
|
||||
// 表单验证
|
||||
if (!this.validateForm(planData)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 模拟API调用
|
||||
console.log('发布旅行计划:', planData);
|
||||
|
||||
// 显示成功消息
|
||||
this.showSuccess('旅行计划发布成功!');
|
||||
|
||||
// 清空表单
|
||||
this.travelForm.reset();
|
||||
|
||||
// 重新加载计划列表
|
||||
await this.loadTravelPlans();
|
||||
|
||||
} catch (error) {
|
||||
console.error('发布旅行计划失败:', error);
|
||||
this.showError('发布失败,请稍后重试');
|
||||
}
|
||||
}
|
||||
|
||||
validateForm(data) {
|
||||
if (!data.destination.trim()) {
|
||||
this.showError('请输入目的地');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!data.travelDate) {
|
||||
this.showError('请选择出发日期');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (data.duration < 1) {
|
||||
this.showError('行程天数必须大于0');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!data.budget) {
|
||||
this.showError('请选择预算范围');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!data.description.trim()) {
|
||||
this.showError('请输入行程描述');
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
async joinPlan(planId) {
|
||||
try {
|
||||
// 模拟加入计划
|
||||
console.log('加入旅行计划:', planId);
|
||||
this.showSuccess('已申请加入旅行计划,等待创建者确认');
|
||||
} catch (error) {
|
||||
console.error('加入计划失败:', error);
|
||||
this.showError('加入失败,请稍后重试');
|
||||
}
|
||||
}
|
||||
|
||||
viewPlanDetail(planId) {
|
||||
// 在实际项目中,这里可以跳转到详情页或显示模态框
|
||||
console.log('查看旅行计划详情:', planId);
|
||||
alert('功能开发中,即将上线');
|
||||
}
|
||||
|
||||
showSuccess(message) {
|
||||
this.showAlert(message, 'success');
|
||||
}
|
||||
|
||||
showError(message) {
|
||||
this.showAlert(message, 'danger');
|
||||
}
|
||||
|
||||
showAlert(message, type) {
|
||||
const alertDiv = document.createElement('div');
|
||||
alertDiv.className = `alert alert-${type} alert-dismissible fade show`;
|
||||
alertDiv.innerHTML = `
|
||||
${message}
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||
`;
|
||||
|
||||
// 插入到页面顶部
|
||||
const container = document.querySelector('.container');
|
||||
container.insertBefore(alertDiv, container.firstChild);
|
||||
|
||||
// 5秒后自动消失
|
||||
setTimeout(() => {
|
||||
if (alertDiv.parentNode) {
|
||||
alertDiv.remove();
|
||||
}
|
||||
}, 5000);
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化旅行计划管理器
|
||||
const travelManager = new TravelPlanManager();
|
||||
|
||||
// 页面加载完成后初始化
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// AOS动画初始化
|
||||
if (typeof AOS !== 'undefined') {
|
||||
AOS.init({
|
||||
duration: 1000,
|
||||
once: true
|
||||
});
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user