fix(withdraw): resolve remote 404 route, db reason column patch and avatar tmp path fallback
这个提交包含在:
@@ -900,4 +900,96 @@ export async function updateExpertServiceStatus(expertId = 101, serviceId, statu
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户收藏的陪玩列表
|
||||
*/
|
||||
export async function getFavorites(userId = 1001) {
|
||||
try {
|
||||
const res = await http.get(`/favorites?userId=${userId}`);
|
||||
return res || [];
|
||||
} catch (err) {
|
||||
console.warn('[API] 获取收藏列表异常:', err.message);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 探测是否已收藏特定陪玩
|
||||
*/
|
||||
export async function checkFavorite(expertId, userId = 1001) {
|
||||
try {
|
||||
const res = await http.get(`/favorites/check?userId=${userId}&expertId=${expertId}`);
|
||||
return res && res.isFavorite;
|
||||
} catch (err) {
|
||||
console.warn('[API] 探测收藏状态异常:', err.message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加收藏
|
||||
*/
|
||||
export async function addFavorite(expertId, userId = 1001) {
|
||||
try {
|
||||
const res = await http.post('/favorites', { userId, expertId });
|
||||
return res;
|
||||
} catch (err) {
|
||||
console.warn('[API] 添加收藏异常:', err.message);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消收藏
|
||||
*/
|
||||
export async function removeFavorite(expertId, userId = 1001) {
|
||||
try {
|
||||
const res = await http.delete(`/favorites/${expertId}?userId=${userId}`);
|
||||
return res;
|
||||
} catch (err) {
|
||||
console.warn('[API] 取消收藏异常:', err.message);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取分销团队列表 (一级与二级成员)
|
||||
*/
|
||||
export async function getDistributionTeam(userId = 1001) {
|
||||
try {
|
||||
const res = await http.get(`/distribution/team?userId=${userId}`);
|
||||
return res || { directCount: 0, indirectCount: 0, totalTeamCount: 0, directList: [], indirectList: [] };
|
||||
} catch (err) {
|
||||
console.warn('[API] 获取分销团队异常:', err.message);
|
||||
return { directCount: 0, indirectCount: 0, totalTeamCount: 0, directList: [], indirectList: [] };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取分销返佣订单记录
|
||||
*/
|
||||
export async function getDistributionRecords(userId = 1001) {
|
||||
try {
|
||||
const res = await http.get(`/distribution/records?userId=${userId}`);
|
||||
return res || [];
|
||||
} catch (err) {
|
||||
console.warn('[API] 获取分销返佣记录异常:', err.message);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 佣金提现申请
|
||||
*/
|
||||
export async function withdrawCommission(amount, channel = 'WECHAT', userId = 1001) {
|
||||
try {
|
||||
const res = await http.post(`/users/${userId}/withdraw`, { amount, channel });
|
||||
return res;
|
||||
} catch (err) {
|
||||
console.warn('[API] 佣金提现申请异常:', err.message);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -168,6 +168,10 @@
|
||||
<image class="action-svg" :src="ICONS.chat" mode="aspectFit" />
|
||||
<text class="action-text">私聊</text>
|
||||
</view>
|
||||
<view class="icon-action btn-active" @click="toggleFavorite">
|
||||
<image class="action-svg" :src="isFav ? ICONS.starFilled : ICONS.starOutline" mode="aspectFit" />
|
||||
<text class="action-text" :class="{ 'fav-active': isFav }">{{ isFav ? '已收藏' : '收藏' }}</text>
|
||||
</view>
|
||||
<view class="icon-action btn-active" @click="openReportModal">
|
||||
<image class="action-svg" :src="ICONS.shield" mode="aspectFit" />
|
||||
<text class="action-text">举报</text>
|
||||
@@ -239,10 +243,11 @@
|
||||
import { ref, computed } from 'vue';
|
||||
import { onLoad } from '@dcloudio/uni-app';
|
||||
import SkeletonScreen from '@/components/SkeletonScreen.vue';
|
||||
import { getCompanionDetail, getExpertReviews, replyOrderReview, submitReport as submitReportApi } from '@/common/api.js';
|
||||
import { getCompanionDetail, getExpertReviews, replyOrderReview, submitReport as submitReportApi, checkFavorite, addFavorite as addFavApi, removeFavorite as removeFavApi } from '@/common/api.js';
|
||||
import { ICONS } from '@/common/icons.js';
|
||||
|
||||
const isLoading = ref(true);
|
||||
const isFav = ref(false);
|
||||
const expert = ref({
|
||||
id: '',
|
||||
nickname: '同城陪伴师',
|
||||
@@ -264,6 +269,23 @@ const expert = ref({
|
||||
const selectedServiceId = ref(null);
|
||||
const isPlayingVoice = ref(false);
|
||||
|
||||
const toggleFavorite = async () => {
|
||||
try {
|
||||
const targetId = Number(expert.value.id || 101);
|
||||
if (isFav.value) {
|
||||
await removeFavApi(targetId);
|
||||
isFav.value = false;
|
||||
uni.showToast({ title: '已取消收藏', icon: 'none' });
|
||||
} else {
|
||||
await addFavApi(targetId);
|
||||
isFav.value = true;
|
||||
uni.showToast({ title: '已收藏该陪玩', icon: 'success' });
|
||||
}
|
||||
} catch (err) {
|
||||
uni.showToast({ title: err.message || '操作失败', icon: 'none' });
|
||||
}
|
||||
};
|
||||
|
||||
const handleReplyReview = (rev) => {
|
||||
const defaultReplies = [
|
||||
'感谢老板的支持与五星好评!开黑配合非常默契,期待下次一起上大分!',
|
||||
@@ -302,6 +324,10 @@ onLoad(async (query) => {
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
isFav.value = await checkFavorite(targetId);
|
||||
} catch (_) {}
|
||||
|
||||
try {
|
||||
const liveReviews = await getExpertReviews(targetId);
|
||||
if (Array.isArray(liveReviews) && liveReviews.length > 0) {
|
||||
@@ -945,6 +971,11 @@ const submitReportTicket = async () => {
|
||||
font-size: 18rpx;
|
||||
color: #94A3B8;
|
||||
margin-top: 4rpx;
|
||||
|
||||
&.fav-active {
|
||||
color: #F59E0B;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,31 @@
|
||||
<template>
|
||||
<view class="index-container" :class="{ 'theme-warm': currentThemeKey === 'warm' }" :style="navStyle">
|
||||
<!-- 顶部搜索栏 (矢量SVG搜索图标,交互聚焦微动效) -->
|
||||
<!-- 顶部搜索栏 (带双向绑定、一键清空、热搜关键词胶囊) -->
|
||||
<view class="header-search">
|
||||
<view class="search-box">
|
||||
<image class="search-icon-img" :src="ICONS.search" mode="aspectFit" />
|
||||
<input class="search-input" placeholder="搜索同城陪玩、美食、KTV嗨唱" placeholder-class="placeholder-text" />
|
||||
<input
|
||||
class="search-input"
|
||||
v-model="searchKeyword"
|
||||
placeholder="搜索大神ID、昵称、游戏或生活陪伴"
|
||||
placeholder-class="placeholder-text"
|
||||
confirm-type="search"
|
||||
/>
|
||||
<view class="search-clear-btn btn-active" v-if="searchKeyword" @click="searchKeyword = ''">
|
||||
<image class="clear-svg" :src="ICONS.close" mode="aspectFit" />
|
||||
</view>
|
||||
</view>
|
||||
<!-- 热搜关键词推荐栏 -->
|
||||
<view class="hot-search-tags" v-if="!searchKeyword">
|
||||
<text class="hot-label">热搜:</text>
|
||||
<view
|
||||
v-for="word in hotSearchWords"
|
||||
:key="word"
|
||||
class="hot-tag btn-active"
|
||||
@click="searchKeyword = word"
|
||||
>
|
||||
{{ word }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -45,15 +66,51 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 陪玩大神瀑布流标题 -->
|
||||
<!-- 陪玩大神瀑布流标题 & 即时速配 -->
|
||||
<view class="section-title-wrap">
|
||||
<view class="section-title">
|
||||
<view class="title-line"></view>
|
||||
<text>精选在线大神</text>
|
||||
</view>
|
||||
<view class="section-more">
|
||||
<view class="section-more btn-active" @click="startInstantMatch">
|
||||
<view class="pulse-point"></view>
|
||||
<text>实时在线</text>
|
||||
<text class="instant-match-txt">雷达即时速配</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 多维度复合筛选工具栏 -->
|
||||
<view class="filter-toolbar">
|
||||
<view
|
||||
class="filter-pill btn-active"
|
||||
:class="{ active: filterOnlyOnline }"
|
||||
@click="filterOnlyOnline = !filterOnlyOnline"
|
||||
>
|
||||
<view class="filter-dot"></view>
|
||||
<text>仅看在线</text>
|
||||
</view>
|
||||
|
||||
<view
|
||||
class="filter-pill btn-active"
|
||||
:class="{ active: filterGender !== 'all' }"
|
||||
@click="toggleGenderFilter"
|
||||
>
|
||||
<text>{{ genderFilterLabel }}</text>
|
||||
</view>
|
||||
|
||||
<view
|
||||
class="filter-pill btn-active"
|
||||
:class="{ active: filterSort !== 'default' }"
|
||||
@click="toggleSortFilter"
|
||||
>
|
||||
<text>{{ sortFilterLabel }}</text>
|
||||
</view>
|
||||
|
||||
<view
|
||||
class="filter-pill btn-active"
|
||||
:class="{ active: filterPriceRange !== 'all' }"
|
||||
@click="showPriceModal = true"
|
||||
>
|
||||
<text>{{ priceFilterLabel }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -113,6 +170,14 @@
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 搜索/筛选无结果空状态 -->
|
||||
<view class="empty-search-box" v-if="filteredExperts.length === 0">
|
||||
<image class="empty-search-icon" :src="ICONS.search" mode="aspectFit" />
|
||||
<text class="empty-title">未匹配到符合条件的大神</text>
|
||||
<text class="empty-desc">尝试更换关键词或重置筛选条件</text>
|
||||
<button class="btn-reset-filters btn-active" @click="resetAllFilters">重置全部筛选</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部自定义导航栏 (发单人端:首页 / 活动中心 / 我要发单 / 友圈 / 个人中心) -->
|
||||
@@ -123,6 +188,80 @@
|
||||
|
||||
<!-- 首页运营全局活动营销弹窗 (配置化可控) -->
|
||||
<home-popup-modal />
|
||||
|
||||
<!-- 价格区间筛选弹窗 -->
|
||||
<view class="price-modal-mask" v-if="showPriceModal" @click="showPriceModal = false">
|
||||
<view class="price-modal-box" @click.stop>
|
||||
<view class="modal-title-row">
|
||||
<text class="modal-title">时薪价格区间筛选</text>
|
||||
<view class="btn-close btn-active" @click="showPriceModal = false">
|
||||
<image class="close-svg" :src="ICONS.close" mode="aspectFit" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="price-options-grid">
|
||||
<view
|
||||
v-for="opt in priceOptions"
|
||||
:key="opt.val"
|
||||
class="price-opt-btn btn-active"
|
||||
:class="{ active: filterPriceRange === opt.val }"
|
||||
@click="selectPriceOption(opt.val)"
|
||||
>
|
||||
{{ opt.label }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 雷达即时速配在线大神弹窗 -->
|
||||
<view class="price-modal-mask" v-if="showMatchModal" @click="closeMatchModal">
|
||||
<view class="price-modal-box match-modal-box" @click.stop>
|
||||
<view class="modal-title-row">
|
||||
<text class="modal-title">雷达即时派单匹配</text>
|
||||
<view class="btn-close btn-active" @click="closeMatchModal">
|
||||
<image class="close-svg" :src="ICONS.close" mode="aspectFit" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="match-content-body">
|
||||
<!-- 雷达扫描态 -->
|
||||
<view class="radar-scan-box" v-if="isMatching">
|
||||
<view class="radar-wave wave-1"></view>
|
||||
<view class="radar-wave wave-2"></view>
|
||||
<view class="radar-wave wave-3"></view>
|
||||
<view class="radar-center-icon">
|
||||
<image class="center-svg" :src="ICONS.flame" mode="aspectFit" />
|
||||
</view>
|
||||
<text class="radar-status-text">正在全网扫描空闲在线大神...</text>
|
||||
</view>
|
||||
|
||||
<!-- 匹配命中结果展示 -->
|
||||
<view class="match-result-box" v-else-if="matchedExpert">
|
||||
<view class="match-success-badge">首位接单大神命中</view>
|
||||
<view class="matched-expert-card">
|
||||
<image class="matched-avatar" :src="matchedExpert.avatar" mode="aspectFill" />
|
||||
<view class="matched-info">
|
||||
<view class="matched-name-row">
|
||||
<text class="matched-name">{{ matchedExpert.nickname }}</text>
|
||||
<text class="matched-online-tag">接单中</text>
|
||||
</view>
|
||||
<text class="matched-game">{{ matchedExpert.game }} · {{ matchedExpert.rankTag }}</text>
|
||||
<text class="matched-price">¥{{ matchedExpert.price }}/小时</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="match-actions-row">
|
||||
<button class="btn-match-action secondary btn-active" @click="reMatch">重新速配</button>
|
||||
<button class="btn-match-action primary btn-active" @click="confirmMatchedOrder">立即预约该大神</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 无在线大神匹配 -->
|
||||
<view class="match-empty-box" v-else>
|
||||
<text class="empty-txt">当前暂无空闲在线大神,请稍后再试</text>
|
||||
<button class="btn-match-action primary btn-active" @click="closeMatchModal">返回大厅</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
@@ -147,6 +286,48 @@ const playingId = ref(null);
|
||||
const currentThemeKey = ref(getCurrentThemeKey());
|
||||
const navInfo = ref(getNavBarInfo());
|
||||
|
||||
// 搜索与热搜
|
||||
const searchKeyword = ref('');
|
||||
const hotSearchWords = ['王者荣耀', '和平精英', '甜美陪伴', '同城微醺', 'KTV嗨唱'];
|
||||
|
||||
// 多维复合筛选与排序
|
||||
const filterOnlyOnline = ref(false);
|
||||
const filterGender = ref('all'); // all | female | male
|
||||
const filterSort = ref('default'); // default | price_asc | rating_desc | orders_desc
|
||||
const filterPriceRange = ref('all'); // all | under_50 | 50_80 | 80_120 | above_120
|
||||
const showPriceModal = ref(false);
|
||||
|
||||
const priceOptions = [
|
||||
{ val: 'all', label: '全部时薪' },
|
||||
{ val: 'under_50', label: '¥50 以下/时' },
|
||||
{ val: '50_80', label: '¥50 - 80/时' },
|
||||
{ val: '80_120', label: '¥80 - 120/时' },
|
||||
{ val: 'above_120', label: '¥120 以上/时' }
|
||||
];
|
||||
|
||||
const genderFilterLabel = computed(() => {
|
||||
if (filterGender.value === 'female') return '仅女生';
|
||||
if (filterGender.value === 'male') return '仅男生';
|
||||
return '性别不限';
|
||||
});
|
||||
|
||||
const sortFilterLabel = computed(() => {
|
||||
if (filterSort.value === 'price_asc') return '价格最低';
|
||||
if (filterSort.value === 'rating_desc') return '评分最高';
|
||||
if (filterSort.value === 'orders_desc') return '单量最多';
|
||||
return '综合推荐';
|
||||
});
|
||||
|
||||
const priceFilterLabel = computed(() => {
|
||||
const found = priceOptions.find(o => o.val === filterPriceRange.value);
|
||||
return found ? found.label : '时薪价格';
|
||||
});
|
||||
|
||||
// 雷达即时速配状态
|
||||
const showMatchModal = ref(false);
|
||||
const isMatching = ref(false);
|
||||
const matchedExpert = ref(null);
|
||||
|
||||
const navStyle = computed(() => {
|
||||
return {
|
||||
'--safe-header-gap': `${navInfo.value.safeHeaderGap}px`,
|
||||
@@ -246,21 +427,148 @@ onUnmounted(() => {
|
||||
});
|
||||
|
||||
const filteredExperts = computed(() => {
|
||||
if (currentCategory.value === 'all') return experts.value;
|
||||
const gameMap = {
|
||||
offline_meal: '美食陪吃',
|
||||
offline_drink: '下午茶陪喝',
|
||||
offline_sing: '同城陪唱',
|
||||
offline_play: '同城陪玩',
|
||||
wzry: '王者荣耀',
|
||||
hpjy: '和平精英',
|
||||
lol: '英雄联盟',
|
||||
val: '无畏契约'
|
||||
};
|
||||
const target = gameMap[currentCategory.value];
|
||||
return target ? experts.value.filter(e => e.game === target) : experts.value;
|
||||
let list = experts.value;
|
||||
|
||||
// 1. 关键词搜索 (ID、昵称、擅长游戏、城市、段位)
|
||||
if (searchKeyword.value.trim()) {
|
||||
const kw = searchKeyword.value.trim().toLowerCase();
|
||||
list = list.filter(e => {
|
||||
const matchId = String(e.id).toLowerCase().includes(kw);
|
||||
const matchName = (e.nickname || '').toLowerCase().includes(kw);
|
||||
const matchGame = (e.game || '').toLowerCase().includes(kw);
|
||||
const matchCity = (e.city || '').toLowerCase().includes(kw);
|
||||
const matchRank = (e.rankTag || '').toLowerCase().includes(kw);
|
||||
return matchId || matchName || matchGame || matchCity || matchRank;
|
||||
});
|
||||
}
|
||||
|
||||
// 2. 金刚区分类过滤
|
||||
if (currentCategory.value !== 'all') {
|
||||
const gameMap = {
|
||||
offline_meal: '美食陪吃',
|
||||
offline_drink: '下午茶陪喝',
|
||||
offline_sing: '同城陪唱',
|
||||
offline_play: '同城陪玩',
|
||||
wzry: '王者荣耀',
|
||||
hpjy: '和平精英',
|
||||
lol: '英雄联盟',
|
||||
val: '无畏契约'
|
||||
};
|
||||
const target = gameMap[currentCategory.value];
|
||||
if (target) {
|
||||
list = list.filter(e => e.game === target);
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 仅看当前在线
|
||||
if (filterOnlyOnline.value) {
|
||||
list = list.filter(e => e.isOnline);
|
||||
}
|
||||
|
||||
// 4. 性别筛选 (female: 女生, male: 男生)
|
||||
if (filterGender.value !== 'all') {
|
||||
list = list.filter(e => e.gender === filterGender.value);
|
||||
}
|
||||
|
||||
// 5. 价格区间过滤
|
||||
if (filterPriceRange.value === 'under_50') {
|
||||
list = list.filter(e => Number(e.price) < 50);
|
||||
} else if (filterPriceRange.value === '50_80') {
|
||||
list = list.filter(e => Number(e.price) >= 50 && Number(e.price) <= 80);
|
||||
} else if (filterPriceRange.value === '80_120') {
|
||||
list = list.filter(e => Number(e.price) > 80 && Number(e.price) <= 120);
|
||||
} else if (filterPriceRange.value === 'above_120') {
|
||||
list = list.filter(e => Number(e.price) > 120);
|
||||
}
|
||||
|
||||
// 6. 排序逻辑
|
||||
if (filterSort.value === 'price_asc') {
|
||||
list = [...list].sort((a, b) => Number(a.price) - Number(b.price));
|
||||
} else if (filterSort.value === 'rating_desc') {
|
||||
list = [...list].sort((a, b) => Number(b.rating || 5) - Number(a.rating || 5));
|
||||
} else if (filterSort.value === 'orders_desc') {
|
||||
list = [...list].sort((a, b) => Number(b.orderCount || 0) - Number(a.orderCount || 0));
|
||||
}
|
||||
|
||||
return list;
|
||||
});
|
||||
|
||||
// 筛选切换
|
||||
const toggleGenderFilter = () => {
|
||||
if (filterGender.value === 'all') {
|
||||
filterGender.value = 'female';
|
||||
} else if (filterGender.value === 'female') {
|
||||
filterGender.value = 'male';
|
||||
} else {
|
||||
filterGender.value = 'all';
|
||||
}
|
||||
};
|
||||
|
||||
const toggleSortFilter = () => {
|
||||
if (filterSort.value === 'default') {
|
||||
filterSort.value = 'price_asc';
|
||||
} else if (filterSort.value === 'price_asc') {
|
||||
filterSort.value = 'rating_desc';
|
||||
} else if (filterSort.value === 'rating_desc') {
|
||||
filterSort.value = 'orders_desc';
|
||||
} else {
|
||||
filterSort.value = 'default';
|
||||
}
|
||||
};
|
||||
|
||||
const selectPriceOption = (val) => {
|
||||
filterPriceRange.value = val;
|
||||
showPriceModal.value = false;
|
||||
};
|
||||
|
||||
const resetAllFilters = () => {
|
||||
searchKeyword.value = '';
|
||||
currentCategory.value = 'all';
|
||||
filterOnlyOnline.value = false;
|
||||
filterGender.value = 'all';
|
||||
filterSort.value = 'default';
|
||||
filterPriceRange.value = 'all';
|
||||
};
|
||||
|
||||
// 雷达即时速配逻辑
|
||||
let matchTimer = null;
|
||||
const startInstantMatch = () => {
|
||||
showMatchModal.value = true;
|
||||
isMatching.value = true;
|
||||
matchedExpert.value = null;
|
||||
|
||||
if (matchTimer) clearTimeout(matchTimer);
|
||||
matchTimer = setTimeout(() => {
|
||||
isMatching.value = false;
|
||||
// 优先匹配当前列表中在线的大神,若无则取首位
|
||||
const onlineList = experts.value.filter(e => e.isOnline);
|
||||
if (onlineList.length > 0) {
|
||||
matchedExpert.value = onlineList[Math.floor(Math.random() * onlineList.length)];
|
||||
} else if (experts.value.length > 0) {
|
||||
matchedExpert.value = experts.value[0];
|
||||
} else {
|
||||
matchedExpert.value = null;
|
||||
}
|
||||
}, 1200);
|
||||
};
|
||||
|
||||
const closeMatchModal = () => {
|
||||
showMatchModal.value = false;
|
||||
if (matchTimer) clearTimeout(matchTimer);
|
||||
};
|
||||
|
||||
const reMatch = () => {
|
||||
startInstantMatch();
|
||||
};
|
||||
|
||||
const confirmMatchedOrder = () => {
|
||||
if (matchedExpert.value) {
|
||||
const id = matchedExpert.value.id;
|
||||
closeMatchModal();
|
||||
goToExpert(id);
|
||||
}
|
||||
};
|
||||
|
||||
const toggleVoice = (id) => {
|
||||
if (playingId.value === id) {
|
||||
playingId.value = null;
|
||||
@@ -992,4 +1300,432 @@ const navTo = (url) => {
|
||||
0%, 100% { transform: scaleY(0.4); }
|
||||
50% { transform: scaleY(1); }
|
||||
}
|
||||
|
||||
.search-clear-btn {
|
||||
padding: 8rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.clear-svg {
|
||||
width: 28rpx;
|
||||
height: 28rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.hot-search-tags {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 12rpx;
|
||||
margin-top: 14rpx;
|
||||
|
||||
.hot-label {
|
||||
font-size: 20rpx;
|
||||
color: #64748B;
|
||||
}
|
||||
|
||||
.hot-tag {
|
||||
font-size: 20rpx;
|
||||
color: #94A3B8;
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||
padding: 4rpx 14rpx;
|
||||
border-radius: 20rpx;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
}
|
||||
|
||||
.instant-match-txt {
|
||||
font-size: 22rpx;
|
||||
font-weight: 700;
|
||||
color: #10B981;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.filter-toolbar {
|
||||
display: flex;
|
||||
gap: 12rpx;
|
||||
padding: 0 32rpx;
|
||||
margin-bottom: 24rpx;
|
||||
overflow-x: auto;
|
||||
white-space: nowrap;
|
||||
|
||||
.filter-pill {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8rpx;
|
||||
font-size: 22rpx;
|
||||
color: #94A3B8;
|
||||
background: rgba(255, 255, 255, 0.04);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
padding: 10rpx 20rpx;
|
||||
border-radius: 28rpx;
|
||||
transition: all 0.2s;
|
||||
|
||||
.filter-dot {
|
||||
width: 10rpx;
|
||||
height: 10rpx;
|
||||
border-radius: 50%;
|
||||
background: #64748B;
|
||||
}
|
||||
|
||||
&.active {
|
||||
color: #FFFFFF;
|
||||
background: rgba(99, 102, 241, 0.25);
|
||||
border-color: #6366F1;
|
||||
font-weight: 600;
|
||||
|
||||
.filter-dot {
|
||||
background: #10B981;
|
||||
box-shadow: 0 0 8rpx #10B981;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.empty-search-box {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 80rpx 32rpx;
|
||||
text-align: center;
|
||||
|
||||
.empty-search-icon {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
opacity: 0.3;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.empty-title {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #E2E8F0;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.empty-desc {
|
||||
font-size: 22rpx;
|
||||
color: #64748B;
|
||||
margin-bottom: 32rpx;
|
||||
}
|
||||
|
||||
.btn-reset-filters {
|
||||
font-size: 24rpx;
|
||||
color: #6366F1;
|
||||
background: rgba(99, 102, 241, 0.12);
|
||||
border: 1px solid rgba(99, 102, 241, 0.3);
|
||||
border-radius: 30rpx;
|
||||
height: 64rpx;
|
||||
line-height: 62rpx;
|
||||
padding: 0 36rpx;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.price-modal-mask {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background-color: rgba(0, 0, 0, 0.7);
|
||||
backdrop-filter: blur(8px);
|
||||
z-index: 999;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 32rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.price-modal-box {
|
||||
width: 100%;
|
||||
max-width: 560rpx;
|
||||
background: #151D30;
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
border-radius: 28rpx;
|
||||
padding: 32rpx;
|
||||
box-sizing: border-box;
|
||||
|
||||
.modal-title-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 24rpx;
|
||||
|
||||
.modal-title {
|
||||
font-size: 30rpx;
|
||||
font-weight: 700;
|
||||
color: #F8FAFC;
|
||||
}
|
||||
|
||||
.btn-close {
|
||||
width: 44rpx;
|
||||
height: 44rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.close-svg {
|
||||
width: 32rpx;
|
||||
height: 32rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.price-options-grid {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12rpx;
|
||||
|
||||
.price-opt-btn {
|
||||
text-align: center;
|
||||
padding: 18rpx 0;
|
||||
font-size: 24rpx;
|
||||
color: #94A3B8;
|
||||
background: rgba(255, 255, 255, 0.04);
|
||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||
border-radius: 14rpx;
|
||||
transition: all 0.2s;
|
||||
|
||||
&.active {
|
||||
color: #FFFFFF;
|
||||
background: rgba(99, 102, 241, 0.3);
|
||||
border-color: #6366F1;
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.match-modal-box {
|
||||
max-width: 600rpx;
|
||||
|
||||
.match-content-body {
|
||||
padding: 20rpx 0 10rpx;
|
||||
}
|
||||
|
||||
.radar-scan-box {
|
||||
position: relative;
|
||||
height: 320rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.radar-wave {
|
||||
position: absolute;
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
border-radius: 50%;
|
||||
border: 2px solid #6366F1;
|
||||
animation: radarRipples 2s infinite cubic-bezier(0.1, 0.2, 0.3, 1);
|
||||
|
||||
&.wave-2 {
|
||||
animation-delay: 0.6s;
|
||||
}
|
||||
&.wave-3 {
|
||||
animation-delay: 1.2s;
|
||||
}
|
||||
}
|
||||
|
||||
.radar-center-icon {
|
||||
width: 72rpx;
|
||||
height: 72rpx;
|
||||
border-radius: 36rpx;
|
||||
background: linear-gradient(135deg, #6366F1, #8B5CF6);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 2;
|
||||
|
||||
.center-svg {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.radar-status-text {
|
||||
position: absolute;
|
||||
bottom: 10rpx;
|
||||
font-size: 22rpx;
|
||||
color: #94A3B8;
|
||||
}
|
||||
}
|
||||
|
||||
.match-result-box {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
.match-success-badge {
|
||||
font-size: 20rpx;
|
||||
color: #10B981;
|
||||
background: rgba(16, 185, 129, 0.15);
|
||||
border: 1px solid rgba(16, 185, 129, 0.3);
|
||||
padding: 4rpx 16rpx;
|
||||
border-radius: 16rpx;
|
||||
font-weight: 600;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.matched-expert-card {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
gap: 16rpx;
|
||||
align-items: center;
|
||||
background: rgba(255, 255, 255, 0.04);
|
||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||
border-radius: 16rpx;
|
||||
padding: 16rpx;
|
||||
box-sizing: border-box;
|
||||
margin-bottom: 24rpx;
|
||||
|
||||
.matched-avatar {
|
||||
width: 100rpx;
|
||||
height: 100rpx;
|
||||
border-radius: 50rpx;
|
||||
border: 2rpx solid #10B981;
|
||||
}
|
||||
|
||||
.matched-info {
|
||||
flex: 1;
|
||||
|
||||
.matched-name-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10rpx;
|
||||
margin-bottom: 4rpx;
|
||||
|
||||
.matched-name {
|
||||
font-size: 28rpx;
|
||||
font-weight: 700;
|
||||
color: #F8FAFC;
|
||||
}
|
||||
|
||||
.matched-online-tag {
|
||||
font-size: 16rpx;
|
||||
color: #FFFFFF;
|
||||
background: #059669;
|
||||
padding: 2rpx 8rpx;
|
||||
border-radius: 6rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.matched-game {
|
||||
font-size: 20rpx;
|
||||
color: #94A3B8;
|
||||
display: block;
|
||||
margin-bottom: 4rpx;
|
||||
}
|
||||
|
||||
.matched-price {
|
||||
font-size: 26rpx;
|
||||
font-weight: 700;
|
||||
color: #10B981;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.match-actions-row {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
gap: 16rpx;
|
||||
|
||||
.btn-match-action {
|
||||
flex: 1;
|
||||
height: 72rpx;
|
||||
line-height: 70rpx;
|
||||
font-size: 24rpx;
|
||||
font-weight: 600;
|
||||
border-radius: 36rpx;
|
||||
margin: 0;
|
||||
|
||||
&.secondary {
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
color: #94A3B8;
|
||||
}
|
||||
|
||||
&.primary {
|
||||
background: linear-gradient(135deg, #10B981, #059669);
|
||||
color: #FFFFFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.match-empty-box {
|
||||
padding: 40rpx 0;
|
||||
text-align: center;
|
||||
|
||||
.empty-txt {
|
||||
font-size: 24rpx;
|
||||
color: #94A3B8;
|
||||
display: block;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.btn-match-action {
|
||||
height: 70rpx;
|
||||
line-height: 68rpx;
|
||||
font-size: 24rpx;
|
||||
font-weight: 600;
|
||||
border-radius: 35rpx;
|
||||
background: linear-gradient(135deg, #6366F1, #4F46E5);
|
||||
color: #FFFFFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes radarRipples {
|
||||
0% {
|
||||
transform: scale(0.6);
|
||||
opacity: 0.9;
|
||||
}
|
||||
100% {
|
||||
transform: scale(2.2);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
// 暖色模式补充
|
||||
.theme-warm {
|
||||
.hot-search-tags .hot-tag {
|
||||
background: #F5F2EC;
|
||||
border-color: #E7E5E4;
|
||||
color: #78716C;
|
||||
}
|
||||
|
||||
.filter-toolbar .filter-pill {
|
||||
background: #F5F2EC;
|
||||
border-color: #E7E5E4;
|
||||
color: #78716C;
|
||||
|
||||
&.active {
|
||||
background: #FFF7ED;
|
||||
border-color: #EA580C;
|
||||
color: #EA580C;
|
||||
}
|
||||
}
|
||||
|
||||
.price-modal-box {
|
||||
background: #FFFFFF;
|
||||
border-color: #E7E5E4;
|
||||
|
||||
.modal-title-row .modal-title {
|
||||
color: #1C1917;
|
||||
}
|
||||
|
||||
.price-options-grid .price-opt-btn {
|
||||
background: #FAFAF9;
|
||||
border-color: #E7E5E4;
|
||||
color: #78716C;
|
||||
|
||||
&.active {
|
||||
background: #FFF7ED;
|
||||
border-color: #EA580C;
|
||||
color: #EA580C;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
文件差异内容过多而无法显示
加载差异
在新工单中引用
屏蔽一个用户