mrr.sj.front/pages/artisan/service-skills.vue

891 lines
27 KiB
Vue
Raw Normal View History

2026-03-24 11:45:13 +08:00
<template>
<view class="service-skills-page">
<!-- 顶部导航栏 -->
<custom-navbar :title="navTitle" :showBack="true"></custom-navbar>
<!-- 页面内容容器 -->
<view class="page-content" :style="{ paddingBottom: buttonAreaHeight + 'px' }">
<!-- 服务技能选择区域 -->
<view class="skills-container box-cont">
<view class="skills-header">
<view class="title-bar"></view>
<text class="skills-title">服务技能多选</text>
</view>
<view class="skills-subtitle" v-if="editable">请选择服务技能</view>
<!-- 显示当前生效的服务技能 -->
<view class="skills-list" v-if="!showPendingSkills">
<view class="skill-item"
v-for="(skill, index) in serviceList"
:key="skill.id"
:class="{ disabled: true }">
<view class="checkbox-wrapper">
<view class="checkbox" :class="{ checked: isCurrentSkillSelected(skill.id) }">
<image v-if="isCurrentSkillSelected(skill.id)"
class="check-icon"
2026-06-02 11:39:23 +08:00
src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/6948baf3-aad9-4d87-9483-9db0e462a3f7.png"
2026-03-24 11:45:13 +08:00
mode="aspectFit">
</image>
</view>
</view>
<text class="skill-name">{{ skill.title }}</text>
</view>
</view>
<!-- 显示待审核的服务技能 -->
<view class="skills-list" v-else>
<view class="skill-item"
v-for="(skill, index) in serviceList"
:key="skill.id"
@click="toggleSkillSelect(index)"
:class="{ disabled: !editable }">
<view class="checkbox-wrapper">
<view class="checkbox" :class="{ checked: skill.checked }">
<image v-if="skill.checked"
class="check-icon"
2026-06-02 11:39:23 +08:00
src="https://mrrplus.oss-cn-beijing.aliyuncs.com/photo/system/6948baf3-aad9-4d87-9483-9db0e462a3f7.png"
2026-03-24 11:45:13 +08:00
mode="aspectFit">
</image>
</view>
</view>
<text class="skill-name">{{ skill.title }}</text>
</view>
</view>
</view>
</view>
<!-- 底部按钮 -->
<view class="bottom-actions" ref="buttonArea">
<!-- 驳回原因提示 -->
<view class="reject-reason" v-if="shouldShowRejectReason">
<view class="reject-title">您的提交已驳回</view>
<view class="reject-detail">驳回原因{{ backText }}</view>
</view>
<!-- 审核中提示 -->
<view class="audit-tip" v-if="showAuditTip && !showPendingSkills">
<text class="audit-text">您的提交变更正在审核中...</text>
</view>
<!-- 去变更信息按钮 -->
<view class="action-btn change-info-btn" v-if="buttonType === 'change'" @click="startChange">
<text class="btn-text">去变更信息</text>
</view>
<!-- 取消和提交按钮 -->
<view class="action-buttons-row" v-if="buttonType === 'edit'">
<view class="action-btn cancel-btn" @click="cancelChange">
<text class="btn-text cancel-text">取消</text>
</view>
<view class="action-btn submit-btn" @click="submitChange">
<text class="btn-text">提交</text>
</view>
</view>
<!-- 查看我的变更按钮 -->
<view class="action-btn view-change-btn" v-if="buttonType === 'view'" @click="togglePendingSkills">
<text class="btn-text">{{ showPendingSkills ? '返回' : '查看我的变更' }}</text>
</view>
</view>
</view>
</template>
<script>
import request from '../../utils/request';
export default {
data() {
return {
serviceList: [], // 服务技能列表
selectedSkills: [], // 已选中的技能ID待审核的
originalSkills: [], // 原始选中的技能ID当前生效的
editable: false, // 是否可编辑
buttonType: 'change', // 按钮类型: change-去变更信息, edit-取消/提交, view-查看我的变更
showAuditTip: false, // 是否显示审核中提示
auditStatus: null, // 审核状态
applyId: null, // 申请ID
isLoading: true, // 添加加载状态
showPendingSkills: false, // 是否显示待审核的技能
currentSkills: [], // 当前生效的技能ID
navTitle: '服务技能', // 导航栏标题
buttonAreaHeight: 120, // 按钮区域高度用于计算底部padding
backText: '', // 驳回原因
identity: '', // 用户身份
lastApplyTime: null, //记录最后申请时间
}
},
computed: {
selectedSkillsText() {
const selectedTitles = this.serviceList
.filter(item => item.checked)
.map(item => item.title);
return selectedTitles.join('、');
},
hasChanges() {
// 检查是否有变更
const current = [...this.selectedSkills].sort().join(',');
const original = [...this.originalSkills].sort().join(',');
return current !== original;
},
// 检查是否有待处理的申请(基于时间)
hasPendingApply() {
if (!this.lastApplyTime) return false;
// 如果最后申请时间在驳回时间之后,说明有新的申请
return true;
},
// 控制驳回提示是否显示
shouldShowRejectReason() {
// 只有在审核驳回状态、不显示待审核技能、且没有待处理申请时才显示驳回提示
return this.auditStatus === 3 &&
!this.showPendingSkills &&
this.backText &&
!this.hasPendingApply;
}
},
onLoad() {
this.initPage();
},
onShow() {
// 页面显示时重新加载数据,确保显示最新状态
this.initPage();
},
onReady() {
// 获取按钮区域高度用于设置页面内容padding
this.getButtonAreaHeight();
},
methods: {
// 初始化页面
async initPage() {
this.isLoading = true;
try {
// 先加载服务技能列表
await this.loadServiceSkills();
// 然后加载申请详情
await this.loadApplyDetails();
} catch (error) {
console.error('初始化页面失败:', error);
} finally {
this.isLoading = false;
}
},
// 清理驳回技能存储的逻辑
async cleanupRejectedSkillsIfNeeded() {
try {
const rejectedSkills = uni.getStorageSync('rejectedSkills');
const lastApplyTime = uni.getStorageSync('lastApplyTime');
if (rejectedSkills && rejectedSkills.length > 0) {
// 获取当前生效技能
const currentSkills = await this.getCurrentEffectiveSkillIds();
const currentSorted = [...currentSkills].sort().join(',');
const rejectedSorted = [...rejectedSkills].sort().join(',');
// 如果当前生效技能与被驳回技能不一致,说明有新的申请已生效
if (currentSorted !== rejectedSorted) {
console.log('检测到技能已变更,清理驳回相关存储');
uni.removeStorageSync('rejectedSkills');
uni.removeStorageSync('lastRejectTime');
}
}
} catch (error) {
console.error('清理驳回技能存储失败:', error);
}
},
// 获取按钮区域高度
getButtonAreaHeight() {
// 使用setTimeout确保DOM已渲染
setTimeout(() => {
const query = uni.createSelectorQuery().in(this);
query.select('.bottom-actions').boundingClientRect(data => {
if (data) {
this.buttonAreaHeight = data.height + 20; // 增加一些边距
}
}).exec();
}, 100);
},
// 更新导航栏标题
updateNavTitle() {
if (this.editable || this.showPendingSkills) {
this.navTitle = '服务技能变更';
} else {
this.navTitle = '服务技能';
}
},
// 加载服务技能列表
async loadServiceSkills() {
try {
const res = await request.post('/user/firstclass');
this.serviceList = res.data.map(item => ({
...item,
checked: false
}));
console.log('服务技能列表:', this.serviceList);
} catch (error) {
console.error('加载服务技能失败:', error);
uni.showToast({
title: '加载服务技能失败',
icon: 'none'
});
}
},
// 加载申请详情 - 手艺人接口
async loadApplyDetails() {
try {
console.log('开始加载手艺人申请详情...');
const res = await request.post('/syr/userSyrAuth/details', {
apply_type: 2 // 服务技能变更
});
console.log('手艺人申请详情接口返回:', res);
if (res.code === 200 && res.data) {
const detail = res.data;
this.applyId = detail.id;
this.auditStatus = detail.apply_state;
this.backText = detail.back_text || ''; // 获取驳回原因
console.log('审核状态:', this.auditStatus);
console.log('原始servers_kill数据:', detail.servers_kill);
// 处理服务技能数据
if (detail.servers_kill) {
let skillIds = [];
if (Array.isArray(detail.servers_kill)) {
skillIds = detail.servers_kill.map(id => {
const num = parseInt(id);
return isNaN(num) ? null : num;
}).filter(id => id !== null);
} else if (typeof detail.servers_kill === 'string') {
skillIds = detail.servers_kill.split(',').map(id => {
const num = parseInt(id.trim());
return isNaN(num) ? null : num;
}).filter(id => id !== null);
}
console.log('解析后的技能ID:', skillIds);
// 根据审核状态正确处理数据
if (this.auditStatus === 1) { // 审核中
this.selectedSkills = [...skillIds];
console.log('审核中,待审核技能:', this.selectedSkills);
// 记录申请时间
uni.setStorageSync('lastApplyTime', new Date().getTime());
} else if (this.auditStatus === 3) { // 审核驳回
// 检查驳回数据是否是最新的
await this.handleRejectedSkills(skillIds);
} else if (this.auditStatus === 2) { // 审核通过
// 审核通过时,使用申请详情中的技能作为当前生效技能
this.currentSkills = [...skillIds];
this.originalSkills = [...skillIds];
this.selectedSkills = [...skillIds];
console.log('审核通过,设置当前生效技能:', this.currentSkills);
// 清理驳回相关存储
uni.removeStorageSync('rejectedSkills');
uni.removeStorageSync('lastRejectTime');
// 重要:审核通过后,强制重新加载当前生效技能以确保数据一致
await this.forceReloadCurrentSkills();
}
this.updateCheckedStatus();
}
// 如果审核状态不是审核通过,加载当前生效的技能
if (this.auditStatus !== 2) {
await this.loadCurrentEffectiveSkills();
}
} else {
console.log('申请详情返回数据为空或code不为200:', res);
// 清理可能的驳回存储
uni.removeStorageSync('rejectedSkills');
uni.removeStorageSync('lastRejectTime');
await this.loadCurrentEffectiveSkills();
}
// 更新页面状态
this.updatePageStatus();
} catch (error) {
console.error('加载申请详情失败:', error);
// 出错时也尝试加载当前生效的技能
await this.loadCurrentEffectiveSkills();
this.updatePageStatus();
}
},
// 新增方法:强制重新加载当前生效技能
async forceReloadCurrentSkills() {
try {
console.log('强制重新加载当前生效技能...');
const currentSkills = await this.getCurrentEffectiveSkillIds();
// 比较申请通过的数据和当前生效数据
const approvedSorted = [...this.currentSkills].sort().join(',');
const currentSorted = [...currentSkills].sort().join(',');
console.log('申请通过技能:', approvedSorted);
console.log('当前生效技能:', currentSorted);
if (approvedSorted !== currentSorted) {
console.log('数据不一致,使用申请通过的数据');
// 保持使用申请通过的数据
} else {
console.log('数据一致,使用当前生效数据');
this.currentSkills = [...currentSkills];
this.originalSkills = [...currentSkills];
this.selectedSkills = [...currentSkills];
}
this.updateCheckedStatus();
} catch (error) {
console.error('强制重新加载当前生效技能失败:', error);
}
},
// 处理驳回技能逻辑
async handleRejectedSkills(rejectedSkillIds) {
const storedRejectedSkills = uni.getStorageSync('rejectedSkills');
const lastRejectTime = uni.getStorageSync('lastRejectTime');
// 获取当前生效技能进行比较
const currentSkills = await this.getCurrentEffectiveSkillIds();
const rejectedSorted = [...rejectedSkillIds].sort().join(',');
const storedSorted = storedRejectedSkills ? [...storedRejectedSkills].sort().join(',') : '';
const currentSorted = [...currentSkills].sort().join(',');
console.log('驳回数据比较:', {
rejectedSorted,
storedSorted,
currentSorted
});
// 如果驳回的技能与当前生效技能相同,说明驳回数据过时
if (rejectedSorted === currentSorted) {
console.log('驳回数据与当前生效技能相同,可能数据过时,忽略本次驳回数据');
this.selectedSkills = [...currentSkills];
// 清理驳回存储
uni.removeStorageSync('rejectedSkills');
uni.removeStorageSync('lastRejectTime');
this.backText = ''; // 清空驳回原因
return;
}
// 如果驳回技能与存储的驳回技能不同,更新存储
if (rejectedSorted !== storedSorted) {
console.log('发现新的驳回数据,更新存储:', rejectedSkillIds);
uni.setStorageSync('rejectedSkills', rejectedSkillIds);
uni.setStorageSync('lastRejectTime', new Date().getTime());
}
this.selectedSkills = [...rejectedSkillIds];
console.log('审核驳回,被驳回技能:', this.selectedSkills);
},
// 获取当前生效技能ID
async getCurrentEffectiveSkillIds() {
try {
const params = {
type: 2, // 手艺人
_t: new Date().getTime() // 添加时间戳防止缓存
};
const res = await request.post('/user/getuser', params);
if (res.state === 1 && res.data && res.data.servers_kill) {
let skillIds = [];
if (Array.isArray(res.data.servers_kill)) {
skillIds = res.data.servers_kill.map(id => parseInt(id)).filter(id => !isNaN(id));
} else if (typeof res.data.servers_kill === 'string') {
skillIds = res.data.servers_kill.split(',').map(id => parseInt(id.trim())).filter(id => !isNaN(id));
}
console.log('获取到的当前生效技能ID:', skillIds);
return skillIds;
}
return [];
} catch (error) {
console.error('获取当前生效技能失败:', error);
return [];
}
},
// 加载当前生效的服务技能
async loadCurrentEffectiveSkills() {
try {
console.log('开始加载当前生效的服务技能...');
const currentSkills = await this.getCurrentEffectiveSkillIds();
this.currentSkills = [...currentSkills];
this.originalSkills = [...currentSkills];
console.log('当前生效的技能ID:', this.currentSkills);
// 只有在无申请状态或审核驳回状态时,才用当前技能更新选中技能
if (this.auditStatus === null || this.auditStatus === 3) {
this.selectedSkills = [...currentSkills];
}
// 审核中状态保持原有的selectedSkills不变
this.updateCheckedStatus();
} catch (error) {
console.error('加载当前生效技能失败:', error);
this.currentSkills = [];
this.originalSkills = [];
if (this.auditStatus !== 1) {
this.selectedSkills = [];
}
this.updateCheckedStatus();
}
},
// 检查技能是否在当前生效的技能中
isCurrentSkillSelected(skillId) {
return this.currentSkills.includes(skillId);
},
// 更新选中状态(用于待审核的技能)
updateCheckedStatus() {
console.log('更新选中状态,当前服务技能列表:', this.serviceList);
console.log('要选中的技能ID:', this.selectedSkills);
this.serviceList.forEach(item => {
item.checked = this.selectedSkills.includes(item.id);
});
},
// 根据审核状态更新页面状态
updatePageStatus() {
console.log('更新页面状态,审核状态:', this.auditStatus);
if (this.auditStatus === 1) { // 审核中
this.showAuditTip = true;
this.buttonType = 'view';
this.editable = false;
this.showPendingSkills = false;
} else if (this.auditStatus === 3) { // 审核驳回
// 检查驳回数据是否有效
const rejectedSkills = uni.getStorageSync('rejectedSkills');
const rejectedSorted = rejectedSkills ? [...rejectedSkills].sort().join(',') : '';
const currentSorted = [...this.currentSkills].sort().join(',');
console.log('驳回数据有效性检查:', {
rejectedSorted,
currentSorted
});
// 如果驳回技能与当前技能相同,说明驳回数据无效
if (rejectedSorted === currentSorted) {
console.log('驳回数据无效,按无申请状态处理');
this.showAuditTip = false;
this.buttonType = 'change';
this.editable = false;
this.showPendingSkills = false;
this.backText = ''; // 清空驳回原因
// 清理存储
uni.removeStorageSync('rejectedSkills');
uni.removeStorageSync('lastRejectTime');
} else {
this.showAuditTip = false;
this.buttonType = 'change';
this.editable = false;
this.showPendingSkills = false;
}
}else if (this.auditStatus === 2) { // 审核通过
// 审核通过时,确保显示正确的状态
this.showAuditTip = false;
this.buttonType = 'change';
this.editable = false;
this.showPendingSkills = false;
console.log('审核通过状态,当前生效技能:', this.currentSkills);
} else { // 无申请
this.showAuditTip = false;
this.buttonType = 'change';
this.editable = false;
this.showPendingSkills = false;
}
this.updateNavTitle();
console.log('最终页面状态:', {
showAuditTip: this.showAuditTip,
buttonType: this.buttonType,
editable: this.editable,
showPendingSkills: this.showPendingSkills,
currentSkills: this.currentSkills,
selectedSkills: this.selectedSkills,
backText: this.backText
});
},
// 切换技能选择状态(仅用于编辑模式)
toggleSkillSelect(index) {
if (!this.editable) return;
this.$set(this.serviceList[index], 'checked', !this.serviceList[index].checked);
// 更新选中的技能ID数组
this.selectedSkills = this.serviceList
.filter(item => item.checked)
.map(item => item.id);
console.log('当前选中的技能ID:', this.selectedSkills);
},
// 切换显示待审核的技能
togglePendingSkills() {
this.showPendingSkills = !this.showPendingSkills;
// 更新导航栏标题
this.updateNavTitle();
},
// 开始变更
startChange() {
this.editable = true;
this.buttonType = 'edit';
// 进入编辑模式时,显示待审核的技能
this.showPendingSkills = true;
// 更新导航栏标题
this.updateNavTitle();
},
// 取消变更
cancelChange() {
// 恢复原始选择
this.selectedSkills = [...this.originalSkills];
this.updateCheckedStatus();
this.editable = false;
this.buttonType = 'change';
this.showPendingSkills = false; // 取消后显示当前生效的技能
// 更新导航栏标题
this.updateNavTitle();
},
// 提交变更 - 手艺人接口(修改为数组格式)
async submitChange() {
if (this.selectedSkills.length === 0) {
uni.showToast({
title: '请至少选择一个服务技能',
icon: 'none'
});
return;
}
if (!this.hasChanges) {
uni.showToast({
title: '未检测到变更',
icon: 'none'
});
return;
}
try {
uni.showLoading({
title: '提交中...'
});
const params = {
apply_type: 2, // 服务技能变更
servers_kill: this.selectedSkills // 直接传递数组
};
console.log('提交的数据:', params);
const res = await request.post('/syr/userSyrAuth/apply', params);
uni.hideLoading();
if (res.code === 200) {
uni.showToast({
title: '提交成功',
icon: 'none'
});
// 清理驳回相关存储
uni.removeStorageSync('rejectedSkills');
uni.removeStorageSync('lastRejectTime');
// 记录申请时间
uni.setStorageSync('lastApplyTime', new Date().getTime());
// 更新页面状态为审核中
this.showAuditTip = true;
this.editable = false;
this.buttonType = 'view';
this.auditStatus = 1; // 审核中
this.showPendingSkills = false; // 提交后默认显示当前生效的技能
// 更新导航栏标题
this.updateNavTitle();
// 重要:提交后强制重新加载数据
setTimeout(async () => {
await this.loadApplyDetails();
}, 1000);
// 重新加载申请详情获取最新状态
setTimeout(() => {
this.loadApplyDetails();
}, 1000);
} else {
uni.showToast({
title: res.msg || '提交失败',
icon: 'none'
});
}
} catch (error) {
uni.hideLoading();
console.error('提交变更失败:', error);
uni.showToast({
title: '提交失败',
icon: 'none'
});
}
}
}
}
</script>
<style scoped>
.service-skills-page {
background-color: #f5f5f5;
min-height: 100vh;
box-sizing: border-box;
display: flex;
flex-direction: column;
}
/* 页面内容区域 */
.page-content {
flex: 1;
overflow-y: auto;
padding: 20rpx 0;
box-sizing: border-box;
}
/* 审核中提示 */
.audit-tip {
margin: 20rpx 24rpx 20rpx 24rpx;
border-radius: 12rpx;
}
.audit-text {
font-weight: 400;
font-size: 32rpx;
color: #333333;
text-align: center;
display: block;
}
/* 驳回原因样式 */
.reject-reason {
background: transparent;
padding: 24rpx;
}
.reject-title {
font-family: PingFangSC, PingFang SC;
font-weight: 500;
font-size: 32rpx;
color: #333333;
line-height: 45rpx;
text-align: center;
font-style: normal;
}
.reject-detail {
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 28rpx;
color: #999999;
line-height: 40rpx;
text-align: center;
font-style: normal;
margin-top: 12rpx;
}
.skills-header {
display: flex;
align-items: center;
margin-bottom: 20rpx;
}
.title-bar {
width: 6rpx;
height: 30rpx;
2026-06-02 11:39:23 +08:00
background-color: #FF4767;
2026-03-24 11:45:13 +08:00
margin-right: 10rpx;
border-radius: 5rpx;
}
.skills-title {
font-family: PingFangSC, PingFang SC;
font-weight: 500;
font-size: 32rpx;
color: #1D2129;
line-height: 48rpx;
text-align: left;
font-style: normal;
}
.skills-subtitle {
margin-left: 20rpx;
margin-bottom: 20rpx;
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 28rpx;
color: #333333;
line-height: 40rpx;
text-align: left;
font-style: normal;
}
.box-cont {
background: #ffffff;
border-radius: 20rpx;
margin: 4rpx 24rpx 24rpx 24rpx;
box-shadow: none;
}
.skills-container {
padding: 32rpx;
}
.skills-list {
display: flex;
flex-wrap: wrap;
margin: 0 4rpx;
}
.skill-item {
width: 100%;
display: flex;
align-items: center;
padding: 20rpx 16rpx;
box-sizing: border-box;
}
.checkbox-wrapper {
margin-right: 20rpx;
}
.checkbox {
width: 40rpx;
height: 40rpx;
border: 2rpx solid #DDDDDD;
border-radius: 8rpx;
display: flex;
align-items: center;
justify-content: center;
}
.checkbox.checked {
2026-06-02 11:39:23 +08:00
background-color: #FF4767;
border-color: #FF4767;
2026-03-24 11:45:13 +08:00
}
.check-icon {
width: 42rpx;
height: 42rpx;
}
.skill-name {
font-size: 28rpx;
color: #333333;
}
.bottom-actions {
position: fixed;
bottom: 0;
left: 0;
right: 0;
padding: 0rpx 24rpx 40rpx 24rpx;
background: #ffffff;
z-index: 100;
}
/* 单个按钮样式 */
.action-btn {
height: 88rpx;
border-radius: 43rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 32rpx;
font-weight: 400;
}
.change-info-btn {
margin-top: 20rpx;
2026-06-02 11:39:23 +08:00
background: linear-gradient(180deg, #ff8e9d 0%, #FF4767 100%);
2026-03-24 11:45:13 +08:00
}
.view-change-btn {
margin-top: 20rpx;
2026-06-02 11:39:23 +08:00
background: linear-gradient(180deg, #ff8e9d 0%, #FF4767 100%);
2026-03-24 11:45:13 +08:00
}
/* 双按钮行样式 */
.action-buttons-row {
display: flex;
justify-content: space-between;
margin: 20rpx 30rpx;
}
.action-buttons-row .action-btn {
width: 270rpx;
}
.cancel-btn {
background: #E5E5E5;
border-radius: 43rpx;
padding: 0 15rpx;
}
.submit-btn {
2026-06-02 11:39:23 +08:00
background: linear-gradient(180deg, #ff8e9d 0%, #FF4767 100%);
2026-03-24 11:45:13 +08:00
border-radius: 43rpx;
padding: 0 15rpx;
}
.btn-text {
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 36rpx;
color: #FFFFFF;
line-height: 50rpx;
text-align: left;
font-style: normal;
}
.cancel-text {
color: #666666;
}
/* 响应式适配 */
/* @media (max-width: 375px) {
.skill-item {
width: 100%;
}
.action-buttons-row .action-btn {
width: 260rpx;
}
}
@media (min-width: 750px) {
.skill-item {
width: 33.333%;
}
} */
</style>