优化个人中心页面

This commit is contained in:
冯辉
2025-11-10 15:27:34 +08:00
parent 1fcfcb0475
commit 1a204549c4
38 changed files with 3017 additions and 618 deletions

View File

@@ -0,0 +1,388 @@
<template>
<AppLayout>
<view class="skill-search-container">
<!-- 固定顶部区域 -->
<view class="fixed-header">
<!-- 搜索框 -->
<view class="search-box">
<input
class="search-input"
v-model="searchKeyword"
placeholder="请输入技能名称进行搜索"
@input="handleSearch"
confirm-type="search"
/>
<view class="search-icon" @click="handleSearch">搜索</view>
</view>
<!-- 已选技能提示 -->
<view class="selected-tip" v-if="selectedSkills.length > 0">
已选择 {{ selectedSkills.length }}/3 个技能
</view>
</view>
<!-- 可滚动内容区域 -->
<scroll-view class="scroll-content" scroll-y>
<view class="scroll-inner">
<!-- 搜索结果列表 -->
<view class="result-list" v-if="searchResults.length > 0">
<view
class="result-item"
v-for="(item, index) in searchResults"
:key="index"
@click="toggleSelect(item)"
>
<view class="item-content">
<text class="item-name">{{ item.name }}</text>
</view>
<view
class="item-checkbox"
:class="{ 'checked': isSelected(item) }"
>
<text v-if="isSelected(item)" class="check-icon"></text>
</view>
</view>
</view>
<!-- 空状态 -->
<view class="empty-state" v-if="!isSearching && searchResults.length === 0 && searchKeyword">
<text class="empty-text">未找到相关技能</text>
</view>
<!-- 初始提示 -->
<view class="empty-state" v-if="!isSearching && searchResults.length === 0 && !searchKeyword">
<text class="empty-text">请输入技能名称进行搜索</text>
</view>
</view>
</scroll-view>
<!-- 固定底部操作栏 -->
<view class="bottom-bar">
<view class="selected-skills" v-if="selectedSkills.length > 0">
<view
class="skill-tag"
v-for="(skill, index) in selectedSkills"
:key="index"
>
<text class="tag-text">{{ skill }}</text>
<text class="tag-close" @click.stop="removeSkill(index)">×</text>
</view>
</view>
<view class="action-buttons">
<button class="btn-cancel" @click="handleCancel">取消</button>
<button
class="btn-confirm"
:class="{ 'disabled': selectedSkills.length === 0 }"
@click="handleConfirm"
:disabled="selectedSkills.length === 0"
>
确定
</button>
</view>
</view>
</view>
</AppLayout>
</template>
<script setup>
import { ref, reactive, onMounted, onUnmounted } from 'vue';
import { onLoad } from '@dcloudio/uni-app';
import { inject } from 'vue';
const { $api } = inject('globalFunction');
const searchKeyword = ref('');
const searchResults = ref([]);
const selectedSkills = ref([]);
const isSearching = ref(false);
let searchTimer = null;
onLoad((options) => {
// 接收已选中的技能
if (options.selected) {
try {
const skills = JSON.parse(decodeURIComponent(options.selected));
if (Array.isArray(skills)) {
selectedSkills.value = skills;
}
} catch (e) {
console.error('解析已选技能失败:', e);
}
}
});
// 搜索处理(防抖)
function handleSearch() {
const keyword = searchKeyword.value.trim();
if (!keyword) {
searchResults.value = [];
return;
}
// 清除之前的定时器
if (searchTimer) {
clearTimeout(searchTimer);
}
// 防抖处理500ms后执行搜索
searchTimer = setTimeout(() => {
performSearch(keyword);
}, 500);
}
// 执行搜索
async function performSearch(keyword) {
if (!keyword.trim()) {
searchResults.value = [];
return;
}
isSearching.value = true;
try {
const response = await $api.createRequest('/cms/dict/jobCategory', { name: keyword }, 'GET');
// 处理接口返回的数据,支持多种可能的返回格式
let results = [];
if (response) {
if (Array.isArray(response)) {
// 如果直接返回数组
results = response;
} else if (response.data) {
// 如果返回 { data: [...] }
results = Array.isArray(response.data) ? response.data : [];
} else if (response.list) {
// 如果返回 { list: [...] }
results = Array.isArray(response.list) ? response.list : [];
}
}
// 确保每个结果都有name字段
searchResults.value = results.map(item => {
if (typeof item === 'string') {
return { name: item };
}
return item;
});
} catch (error) {
console.error('搜索技能失败:', error);
$api.msg('搜索失败,请稍后重试');
searchResults.value = [];
} finally {
isSearching.value = false;
}
}
// 判断技能是否已选中
function isSelected(item) {
return selectedSkills.value.includes(item.name);
}
// 切换技能选择状态
function toggleSelect(item) {
const skillName = item.name;
const index = selectedSkills.value.indexOf(skillName);
if (index > -1) {
// 已选中,取消选择
selectedSkills.value.splice(index, 1);
} else {
// 未选中,检查是否已达到最大数量
if (selectedSkills.value.length >= 3) {
$api.msg('最多只能选择3个技能');
return;
}
// 添加选择
selectedSkills.value.push(skillName);
}
}
// 移除技能
function removeSkill(index) {
selectedSkills.value.splice(index, 1);
}
// 取消操作
function handleCancel() {
uni.navigateBack();
}
// 确定操作
function handleConfirm() {
if (selectedSkills.value.length === 0) {
$api.msg('请至少选择一个技能');
return;
}
// 通过事件总线传递选中的技能技能字段值传name
uni.$emit('skillSelected', selectedSkills.value);
// 返回上一页
uni.navigateBack();
}
onUnmounted(() => {
// 清理定时器
if (searchTimer) {
clearTimeout(searchTimer);
}
});
</script>
<style lang="stylus" scoped>
.skill-search-container
display: flex
flex-direction: column
height: 100%
background-color: #f5f5f5
.fixed-header
flex-shrink: 0
background-color: #fff
border-bottom: 2rpx solid #ebebeb
.search-box
display: flex
align-items: center
padding: 24rpx
background-color: #fff
.search-input
flex: 1
height: 72rpx
padding: 0 24rpx
background-color: #f5f5f5
border-radius: 36rpx
font-size: 28rpx
color: #333
.search-icon
margin-left: 24rpx
padding: 16rpx 32rpx
background-color: #256bfa
color: #fff
border-radius: 36rpx
font-size: 28rpx
.selected-tip
padding: 16rpx 24rpx
background-color: #fff3cd
color: #856404
font-size: 24rpx
text-align: center
.scroll-content
flex: 1
overflow: hidden
height: 0 // 关键让flex布局正确计算高度
background-color: #fff
.scroll-inner
padding-bottom: 40rpx
.result-list
background-color: #fff
padding: 0 24rpx
.result-item
display: flex
align-items: center
justify-content: space-between
padding: 32rpx 0
border-bottom: 2rpx solid #ebebeb
.item-content
flex: 1
.item-name
font-size: 32rpx
color: #333
.item-checkbox
width: 48rpx
height: 48rpx
border: 2rpx solid #ddd
border-radius: 50%
display: flex
align-items: center
justify-content: center
margin-left: 24rpx
&.checked
background-color: #256bfa
border-color: #256bfa
.check-icon
color: #fff
font-size: 32rpx
font-weight: bold
.empty-state
display: flex
align-items: center
justify-content: center
min-height: 400rpx
.empty-text
font-size: 28rpx
color: #999
.bottom-bar
flex-shrink: 0
background-color: #fff
border-top: 2rpx solid #ebebeb
padding: 24rpx
box-shadow: 0 -4rpx 12rpx rgba(0, 0, 0, 0.05) // 添加阴影,让底部栏更明显
.selected-skills
display: flex
flex-wrap: wrap
margin-bottom: 24rpx
min-height: 60rpx
.skill-tag
display: inline-flex
align-items: center
padding: 12rpx 24rpx
margin-right: 16rpx
margin-bottom: 16rpx
background-color: #e8f4ff
border-radius: 32rpx
border: 2rpx solid #256bfa
.tag-text
font-size: 26rpx
color: #256bfa
margin-right: 12rpx
.tag-close
font-size: 32rpx
color: #256bfa
line-height: 1
cursor: pointer
.action-buttons
display: flex
gap: 24rpx
.btn-cancel, .btn-confirm
flex: 1
height: 88rpx
border-radius: 12rpx
font-size: 32rpx
border: none
.btn-cancel
background-color: #f5f5f5
color: #666
.btn-confirm
background-color: #256bfa
color: #fff
&.disabled
background-color: #ccc
color: #999
</style>