统一排行榜对齐星雀UI,页面资源后台配置实时刷新

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
XingQue
2026-06-27 01:26:11 +08:00
parent 1ab2e2080a
commit 21112173f2
292 changed files with 64010 additions and 81 deletions

View File

@@ -0,0 +1,461 @@
// pages/kaohe_jinpai/kaohe_jinpai.js
const app = getApp();
import request from '../../utils/request.js';
Page({
data: {
activeTab: 'all_tags',
plateList: [], // 第一个接口返回的所有板块及标签(含 fee_list
myTags: [],
recordList: [],
recordPage: 1,
recordHasMore: true,
recordLoading: false,
recordFilter: 'pending',
showApplyModal: false,
selectedTag: null,
applyGameNick: '',
applyRemark: '',
applyFee: 0,
applyJiluId: '',
isReapply: false,
payLoading: false,
ossImageUrl: app.globalData.ossImageUrl || '',
// 新增:考核官相关
kaoheguanList: [], // 所有可选的考核官
showKaoheguanModal: false, // 考核官选择弹窗
selectedKaoheguanId: '', // 已选择的考核官ID
selectedKaoheguanInfo: null, // 已选择的考核官信息(用于展示)
searchKeyword: '', // 搜索关键词
filteredKaoheguanList: [], // 搜索过滤后的列表
},
onLoad() {
this.fetchAllTags();
this.fetchMyRecords(true);
},
switchTab(e) {
const tab = e.currentTarget.dataset.tab;
if (tab === this.data.activeTab) return;
this.setData({ activeTab: tab });
if (tab === 'my_records') this.fetchMyRecords(true);
},
// 获取所有板块及标签(含费用列表),缓存到 plateList同时获取考核官列表
async fetchAllTags() {
try {
const res = await request({ url: '/dengji/dskhhq', method: 'POST' });
if (res.data.code === 0) {
const { plate_list, my_tags, kaoheguan_list } = res.data.data;
// 拼接考核官头像为完整URL
const ossUrl = this.data.ossImageUrl;
const processedKgList = (kaoheguan_list || []).map(kg => ({
...kg,
avatar: kg.avatar && !kg.avatar.startsWith('http') ? ossUrl + kg.avatar : kg.avatar,
}));
this.setData({
plateList: plate_list || [],
myTags: my_tags || [],
kaoheguanList: processedKgList // 使用拼接后的列表
});
}
} catch (e) {}
},
// 获取我的考核记录,并根据 plateList 计算本次/预计下次费用
async fetchMyRecords(isRefresh = false) {
if (this.data.recordLoading) return;
const page = isRefresh ? 1 : this.data.recordPage;
this.setData({ recordLoading: true });
try {
const res = await request({
url: '/dengji/dskhjl',
method: 'POST',
data: { status: this.data.recordFilter, page, page_size: 5 }
});
if (res.data.code === 0) {
const { list, has_more } = res.data.data;
// 从 plateList 构建标签ID -> 完整标签对象的映射(用于精确匹配费用)
const tagMap = {};
this.data.plateList.forEach(plate => {
(plate.tags || []).forEach(tag => {
tagMap[tag.id] = tag; // tag 包含 fee_list
});
});
// 处理每条记录:优先使用 tag_obj.id 匹配,其次 chenghao_id
const processed = list.map(item => {
// 尝试多种方式获取标签ID
const tagId = (item.tag_obj && item.tag_obj.id) || item.chenghao_id;
const tag = tagMap[tagId] || item.tag_obj || {};
const feeList = tag.fee_list || [];
let currentFee = 0;
let nextFee = 0;
if (feeList.length > 0) {
const cishu = item.cishu;
const cur = feeList.find(f => f.cishu === cishu);
currentFee = cur ? cur.feiyong : feeList[feeList.length - 1].feiyong;
const nextCishu = cishu + 1;
const nxt = feeList.find(f => f.cishu === nextCishu);
nextFee = nxt ? nxt.feiyong : feeList[feeList.length - 1].feiyong;
}
return {
...item,
current_fee: currentFee,
next_fee: nextFee,
tag_obj: tag // 使用匹配到的完整标签对象,保证后续“再次考核”能拿到费用
};
});
const newList = isRefresh ? processed : [...this.data.recordList, ...processed];
this.setData({
recordList: newList,
recordPage: page + 1,
recordHasMore: has_more,
recordLoading: false
});
} else {
this.setData({ recordLoading: false });
}
} catch (e) {
this.setData({ recordLoading: false });
}
},
filterRecord(e) {
const filter = e.currentTarget.dataset.filter;
if (filter === this.data.recordFilter) return;
this.setData({ recordFilter: filter, recordList: [], recordPage: 1, recordHasMore: true });
this.fetchMyRecords(true);
},
onReachBottom() {
if (this.data.activeTab === 'my_records' && this.data.recordHasMore) {
this.fetchMyRecords(false);
}
},
// 打开申请考核弹窗,费用完全基于 plateList 中的标签数据
openApplyModal(e) {
const { tag, jiluid, reapply } = e.currentTarget.dataset;
// 查找完整标签对象,保证 fee_list 存在
let fullTag = tag;
if (fullTag && fullTag.fee_list === undefined) {
const found = this.findTagInPlateList(fullTag.id);
if (found) fullTag = found;
}
if (!fullTag) {
const found = this.findTagInPlateList(tag);
if (found) fullTag = found;
}
let fee = 0;
if (fullTag) {
let nextCishu = 0;
// 再次申请时,基于原记录次数计算下一次考核次数
if (reapply && jiluid) {
const record = this.data.recordList.find(r => r.jilu_id === jiluid);
if (record) {
nextCishu = record.cishu + 1;
} else {
nextCishu = (fullTag.my_cishu || 0) + 1;
}
} else {
nextCishu = (fullTag.my_cishu || 0) + 1;
}
const feeList = fullTag.fee_list || [];
const exact = feeList.find(f => f.cishu === nextCishu);
if (exact) {
fee = exact.feiyong;
} else if (feeList.length > 0) {
fee = feeList[feeList.length - 1].feiyong;
}
}
// 检查再次申请时原记录是否有考核官,若有则不允许指定
let canSpecify = true;
let originalShenheguanId = '';
if (reapply && jiluid) {
const record = this.data.recordList.find(r => r.jilu_id === jiluid);
if (record && record.shenheguan_id) {
canSpecify = false; // 已有考核官,不能指定新的
originalShenheguanId = record.shenheguan_id;
}
}
this.setData({
showApplyModal: true,
selectedTag: fullTag || null,
applyGameNick: '',
applyRemark: '',
applyFee: fee,
applyJiluId: jiluid || '',
isReapply: reapply || false,
selectedKaoheguanId: '', // 重置
selectedKaoheguanInfo: null,
canSpecify: canSpecify, // 新增
originalShenheguanId: originalShenheguanId, // 新增
});
},
// 辅助方法:从 plateList 查找指定 id 的标签
findTagInPlateList(tagId) {
if (!tagId) return null;
for (const plate of this.data.plateList) {
for (const t of (plate.tags || [])) {
if (t.id === tagId) return t;
}
}
return null;
},
closeApplyModal() {
this.setData({ showApplyModal: false });
},
// 实时更新游戏昵称
onNickInput(e) {
this.setData({ applyGameNick: e.detail.value });
},
// 实时更新备注
onRemarkInput(e) {
this.setData({ applyRemark: e.detail.value });
},
// 新增:打开考核官选择弹窗
openKaoheguanModal() {
if (!this.data.canSpecify) {
wx.showToast({ title: '已有考核官,无法重新指定', icon: 'none' });
return;
}
// 根据费用筛选可选考核官
let all = [...this.data.kaoheguanList];
if (this.data.applyFee > 0) {
all = all.filter(kg => kg.is_renzheng);
}
this.setData({
showKaoheguanModal: true,
filteredKaoheguanList: all,
searchKeyword: '',
});
},
// 关闭考核官选择弹窗
closeKaoheguanModal() {
this.setData({ showKaoheguanModal: false });
},
// 搜索输入
onSearchInput(e) {
const value = e.detail.value.trim();
this.setData({ searchKeyword: value });
this.filterKaoheguanList(value);
},
// 过滤考核官列表
filterKaoheguanList(keyword) {
let all = [...this.data.kaoheguanList];
if (this.data.applyFee > 0) {
all = all.filter(kg => kg.is_renzheng);
}
if (keyword) {
all = all.filter(kg =>
kg.yonghuid.includes(keyword) ||
(kg.dashou_nick && kg.dashou_nick.includes(keyword))
);
}
this.setData({ filteredKaoheguanList: all });
},
// 选择一个考核官
selectKaoheguan(e) {
const kg = e.currentTarget.dataset.kg;
if (kg.shenhe_zhuangtai !== 0) {
wx.showToast({ title: '该考核官正忙,无法指定', icon: 'none' });
return;
}
this.setData({
selectedKaoheguanId: kg.yonghuid,
selectedKaoheguanInfo: kg,
showKaoheguanModal: false,
});
},
// 取消已选择的考核官
clearSelectedKaoheguan() {
this.setData({
selectedKaoheguanId: '',
selectedKaoheguanInfo: null,
});
},
// 提交申请
async submitApply() {
const { applyGameNick, applyRemark, applyFee, selectedTag, isReapply, applyJiluId, selectedKaoheguanId } = this.data;
if (!applyGameNick || !applyGameNick.trim()) {
wx.showToast({ title: '请输入游戏昵称', icon: 'none' });
return;
}
if (!applyRemark || !applyRemark.trim()) {
wx.showToast({ title: '请填写备注', icon: 'none' });
return;
}
if (applyFee > 0) {
await this.startPayProcess();
} else {
await this.doFreeApply();
}
},
// 支付流程
async startPayProcess() {
this.setData({ payLoading: true });
try {
const res = await request({
url: '/yonghu/khzf',
method: 'POST',
data: {
tag_id: this.data.selectedTag.id,
game_nick: this.data.applyGameNick,
remark: this.data.applyRemark,
reapply: this.data.isReapply,
jilu_id: this.data.applyJiluId,
shenheguan_id: this.data.selectedKaoheguanId, // 新增
}
});
if (res.data.code !== 200) throw new Error(res.data.message || '下单失败');
const payParams = res.data.payParams;
const dingdanid = res.data.dingdanid;
wx.requestPayment({
timeStamp: payParams.timeStamp,
nonceStr: payParams.nonceStr,
package: payParams.package,
signType: payParams.signType || 'MD5',
paySign: payParams.paySign,
success: async () => {
await this.pollPayStatus(dingdanid);
},
fail: () => {
wx.showToast({ title: '支付取消或失败', icon: 'none' });
request({ url: '/yonghu/khzfsb', method: 'POST', data: { dingdanid } }).catch(()=>{});
}
});
} catch (e) {
wx.showToast({ title: e.message || '支付异常', icon: 'none' });
} finally {
this.setData({ payLoading: false });
}
},
// 轮询支付结果
async pollPayStatus(dingdanid) {
for (let i = 0; i < 10; i++) {
try {
const res = await request({ url: '/yonghu/khzffc', method: 'POST', data: { dingdanid } });
if (res.data.code === 200) {
wx.showToast({ title: '申请成功', icon: 'success' });
this.closeApplyModal();
this.fetchAllTags();
this.setData({ recordList: [], recordPage: 1 });
this.fetchMyRecords(true);
return;
}
} catch (e) {}
await new Promise(r => setTimeout(r, 2000));
}
wx.showToast({ title: '支付确认超时', icon: 'none' });
},
// 免费申请
async doFreeApply() {
try {
const res = await request({
url: '/dengji/dssqkh',
method: 'POST',
data: {
tag_id: this.data.selectedTag.id,
game_nick: this.data.applyGameNick,
remark: this.data.applyRemark,
reapply: this.data.isReapply,
jilu_id: this.data.applyJiluId,
shenheguan_id: this.data.selectedKaoheguanId, // 新增
}
});
if (res.data.code === 0) {
wx.showToast({ title: '申请成功', icon: 'success' });
this.closeApplyModal();
this.fetchAllTags();
this.setData({ recordList: [], recordPage: 1 });
this.fetchMyRecords(true);
} else {
wx.showToast({ title: res.data.msg || '申请失败', icon: 'none' });
}
} catch (e) {
wx.showToast({ title: '网络错误', icon: 'none' });
}
},
// 联系考核官(私聊)
contactKaoheguan(e) {
const item = e.currentTarget.dataset.item;
if (!item) return;
if (!item.shenheguan_id) {
wx.showToast({ title: '暂无考核官信息', icon: 'none' });
return;
}
const uid = wx.getStorageSync('uid');
if (!uid) {
wx.showToast({ title: '用户信息缺失', icon: 'none' });
return;
}
if (app.globalData.currentRole !== 'dashou') {
app.globalData.currentRole = 'dashou';
wx.setStorageSync('currentRole', 'dashou');
}
if (app.connectForCurrentRole) {
app.connectForCurrentRole();
} else if (app.ensureConnection) {
app.ensureConnection();
}
const ossImageUrl = this.data.ossImageUrl || app.globalData.ossImageUrl || '';
let fullAvatar = item.shenheguan_avatar || '';
if (fullAvatar && !fullAvatar.startsWith('http')) {
fullAvatar = ossImageUrl + fullAvatar;
}
if (!fullAvatar) {
fullAvatar = ossImageUrl + (app.globalData.morentouxiang || 'avatar/default.jpg');
}
const param = {
toUserId: 'Kh' + item.shenheguan_id,
toName: '考核官' + item.shenheguan_id,
toAvatar: fullAvatar
};
wx.navigateTo({
url: '/pages/liaotian/liaotian?data=' + encodeURIComponent(JSON.stringify(param)),
fail: (err) => {
wx.showToast({ title: '打开聊天失败', icon: 'none' });
}
});
},
showTagDetail(e) {
const tag = e.currentTarget.dataset.tag;
const feeStr = this.formatFeeList(tag.fee_list);
wx.showModal({
title: tag.name,
content: `规则:${tag.rule || '暂无'}\n${tag.jinpai ? '【可同时开启金牌】' : ''}\n费用:${feeStr}`,
showCancel: false,
confirmText: '知道了'
});
},
formatFeeList(feeList) {
if (!feeList || feeList.length === 0) return '暂无';
let str = feeList.map(f => `${f.cishu}次:¥${f.feiyong}`).join('');
const last = feeList[feeList.length - 1];
str += `;第${last.cishu}次及以上:¥${last.feiyong}`;
return str;
},
});

View File

@@ -0,0 +1,9 @@
{
"usingComponents": {
"chenghao-tag": "/components/chenghao-tag/chenghao-tag",
"global-notification": "/components/global-notification/global-notification"
},
"navigationBarTitleText": "考核金牌",
"navigationBarBackgroundColor": "#0F0A1E",
"navigationBarTextStyle": "white"
}

View File

@@ -0,0 +1,135 @@
<!-- pages/kaohe_jinpai/kaohe_jinpai.wxml -->
<view class="page-root">
<!-- 顶部Tab切换 -->
<view class="tab-bar">
<view class="tab-item {{activeTab === 'all_tags' ? 'active' : ''}}" data-tab="all_tags" bindtap="switchTab">考核标签</view>
<view class="tab-item {{activeTab === 'my_records' ? 'active' : ''}}" data-tab="my_records" bindtap="switchTab">我的考核</view>
</view>
<!-- 考核标签面板 -->
<view class="content-area" wx:if="{{activeTab === 'all_tags'}}">
<scroll-view scroll-y="true" class="scroll-view">
<view wx:for="{{plateList}}" wx:key="bankuai_id" class="plate-group">
<view class="plate-title">{{item.mingcheng}}</view>
<view wx:for="{{item.tags}}" wx:key="id" class="tag-card" bindtap="showTagDetail" data-tag="{{item}}">
<view class="tag-header">
<chenghao-tag mingcheng="{{item.name}}" texiaoJson="{{item.texiao_json}}" />
<text wx:if="{{item.jinpai}}" class="jinpai-badge">金牌</text>
</view>
<view class="fee-line">
<text wx:for="{{item.fee_list}}" wx:key="cishu" class="fee-item">{{item.cishu}}次 ¥{{item.feiyong}}</text>
<text wx:if="{{item.fee_list.length > 0}}" class="fee-item">≥{{item.fee_list.length}}次 ¥{{item.fee_list[item.fee_list.length-1].feiyong}}</text>
</view>
<view wx:if="{{!myTags.includes(item.name)}}" class="apply-btn" data-tag="{{item}}" bindtap="openApplyModal">申请考核</view>
<view wx:else class="owned-tip">已拥有</view>
</view>
</view>
</scroll-view>
</view>
<!-- 我的考核面板 -->
<view class="content-area" wx:if="{{activeTab === 'my_records'}}">
<view class="filter-row">
<view class="filter-tag {{recordFilter === 'pending' ? 'active' : ''}}" data-filter="pending" bindtap="filterRecord">进行中/未通过</view>
<view class="filter-tag {{recordFilter === 'passed' ? 'active' : ''}}" data-filter="passed" bindtap="filterRecord">已通过</view>
</view>
<scroll-view scroll-y="true" class="scroll-view" bindscrolltolower="onReachBottom">
<view wx:for="{{recordList}}" wx:key="jilu_id" class="record-card">
<view class="card-top">
<text class="tag-name">{{item.chenghao_name}}</text>
<text class="status-text" style="color: {{item.statusColor}}">{{item.statusText}}</text>
</view>
<view class="card-info">
<text>板块:{{item.bankuai_name}}</text>
<text>考核官:{{item.shenheguan_id || '待分配'}}</text>
<!-- 联系考核官:纯文字蓝色下划线 -->
<view wx:if="{{item.shenheguan_id}}" class="contact-text-link" data-item="{{item}}" bindtap="contactKaoheguan">联系考核官</view>
<text>游戏ID{{item.dashou_youxi_id}}</text>
<text>备注:{{item.dashou_beizhu}}</text>
<text>第{{item.cishu}}次考核 · 共缴 ¥{{item.total_fee}}</text>
<!-- 进行中/未通过时显示本次及预计下次费用 -->
<view wx:if="{{item.zhuangtai !== 1}}" class="fee-predict">
<text>本次考核费用:¥{{item.current_fee}}</text>
<text>预计下次费用:¥{{item.next_fee}}</text>
</view>
<text wx:if="{{item.last_fail_reason}}">上次拒绝原因:{{item.last_fail_reason}}</text>
</view>
<!-- 未通过时显示“再次考核”按钮,传递 tag_obj 以便计算费用 -->
<view wx:if="{{item.zhuangtai === 2}}" class="reapply-btn" data-tag="{{item.tag_obj}}" data-jiluid="{{item.jilu_id}}" data-reapply="{{true}}" bindtap="openApplyModal">再次考核</view>
</view>
<view wx:if="{{recordLoading}}" class="loading-more">加载中...</view>
<view wx:if="{{!recordHasMore && recordList.length > 0}}" class="no-more">—— 没有更多了 ——</view>
</scroll-view>
</view>
<!-- 申请考核弹窗 -->
<view wx:if="{{showApplyModal}}" class="modal-mask" bindtap="closeApplyModal">
<view class="modal-dialog" catchtap="noop">
<view class="modal-header">申请考核 - {{selectedTag.name}}</view>
<view class="form-group">
<text class="form-label">游戏昵称</text>
<input class="form-input" value="{{applyGameNick}}" bindinput="onNickInput" placeholder="请输入游戏昵称" />
</view>
<view class="form-group">
<text class="form-label">备注</text>
<textarea class="form-textarea" value="{{applyRemark}}" bindinput="onRemarkInput" placeholder="区服、时间段等(请勿填写无关内容,违者封号)" maxlength="100" />
</view>
<!-- 指定考核官区域 -->
<view class="form-group">
<text class="form-label">指定考核官(可选)</text>
<view wx:if="{{!canSpecify}}" class="kg-disabled-tip">
<text>已有考核官:{{originalShenheguanId}},不可重新指定</text>
</view>
<view wx:else>
<view wx:if="{{!selectedKaoheguanInfo}}" class="select-kg-btn" bindtap="openKaoheguanModal">选择考核官</view>
<view wx:else class="selected-kg-card">
<image class="kg-avatar" src="{{selectedKaoheguanInfo.avatar}}" mode="aspectFill" />
<view class="kg-info">
<text class="kg-uid">UID {{selectedKaoheguanInfo.yonghuid}}</text>
<text class="kg-nick" wx:if="{{selectedKaoheguanInfo.dashou_nick}}">{{selectedKaoheguanInfo.dashou_nick}}</text>
<text class="kg-status {{selectedKaoheguanInfo.is_renzheng ? 'renzheng' : 'not-renzheng'}}">{{selectedKaoheguanInfo.is_renzheng ? '已认证' : '未认证'}}</text>
</view>
<view class="clear-kg-btn" bindtap="clearSelectedKaoheguan">取消</view>
</view>
</view>
</view>
<view wx:if="{{applyFee > 0}}" class="fee-info">本次需缴纳:<text class="fee-amount">¥{{applyFee}}</text></view>
<view class="modal-actions">
<view class="btn-cancel" bindtap="closeApplyModal">取消</view>
<view class="btn-confirm" bindtap="submitApply">确认申请{{applyFee > 0 ? '并支付' : ''}}</view>
</view>
</view>
</view>
<!-- 考核官选择弹窗 -->
<view wx:if="{{showKaoheguanModal}}" class="modal-mask" bindtap="closeKaoheguanModal">
<view class="modal-dialog" catchtap="noop">
<view class="modal-header">选择考核官</view>
<view class="search-box">
<input class="search-input" value="{{searchKeyword}}" bindinput="onSearchInput" placeholder="搜索用户ID或昵称" />
</view>
<scroll-view scroll-y="true" class="kg-scroll">
<view wx:for="{{filteredKaoheguanList}}" wx:key="yonghuid" class="kg-item" data-kg="{{item}}" bindtap="selectKaoheguan">
<image class="kg-avatar" src="{{item.avatar}}" mode="aspectFill" />
<view class="kg-info">
<text class="kg-uid">UID {{item.yonghuid}}</text>
<text wx:if="{{item.dashou_nick}}" class="kg-nick">{{item.dashou_nick}}</text>
<text class="kg-status {{item.is_renzheng ? 'renzheng' : 'not-renzheng'}}">{{item.is_renzheng ? '已认证' : '未认证'}}</text>
<text class="kg-state {{item.shenhe_zhuangtai === 0 ? 'free' : 'busy'}}">{{item.shenhe_zhuangtai === 0 ? '空闲' : '忙碌'}}</text>
</view>
</view>
<view wx:if="{{filteredKaoheguanList.length === 0}}" class="empty-tip">暂无符合条件的考核官</view>
</scroll-view>
<view class="modal-actions">
<view class="btn-cancel" bindtap="closeKaoheguanModal">取消</view>
</view>
</view>
</view>
<global-notification id="global-notification" />
</view>

View File

@@ -0,0 +1,385 @@
/* pages/kaohe_jinpai/kaohe_jinpai.wxss - 晨雾微光风格 */
.page-root {
min-height: 100vh;
background: linear-gradient(135deg, #F5F7FA 0%, #E4E8F0 100%);
display: flex;
flex-direction: column;
overflow: hidden;
}
.tab-bar {
display: flex;
padding: 30rpx 50rpx;
gap: 60rpx;
background: rgba(255, 255, 255, 0.65);
backdrop-filter: blur(15px);
border-bottom: 1rpx solid rgba(0, 0, 0, 0.04);
}
.tab-item {
font-size: 32rpx;
color: #6B7280;
padding-bottom: 10rpx;
border-bottom: 4rpx solid transparent;
transition: all 0.3s;
}
.tab-item.active {
color: #4C5BF0;
border-bottom-color: #4C5BF0;
font-weight: 600;
}
.content-area {
flex: 1;
overflow: hidden;
}
.scroll-view {
height: 100%;
padding: 30rpx 0;
}
.plate-group {
margin-bottom: 50rpx;
}
.plate-title {
font-size: 34rpx;
font-weight: 700;
color: #1F2937;
margin-bottom: 20rpx;
padding-left: 30rpx;
border-left: 6rpx solid #4C5BF0;
margin-left: 30rpx;
margin-right: 30rpx;
}
.tag-card {
background: #FFFFFF;
border-radius: 28rpx;
padding: 30rpx;
margin: 0 30rpx 20rpx 30rpx;
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.03);
border: 1rpx solid rgba(0, 0, 0, 0.04);
}
.tag-header {
display: flex;
align-items: center;
gap: 16rpx;
margin-bottom: 18rpx;
}
.jinpai-badge {
font-size: 22rpx;
color: #F59E0B;
background: rgba(245, 158, 11, 0.1);
padding: 4rpx 14rpx;
border-radius: 20rpx;
font-weight: 600;
}
.fee-line {
display: flex;
flex-wrap: wrap;
gap: 16rpx;
margin-bottom: 20rpx;
}
.fee-item {
font-size: 24rpx;
color: #4B5563;
background: #F3F4F6;
padding: 6rpx 16rpx;
border-radius: 20rpx;
}
.apply-btn {
display: flex;
align-items: center;
justify-content: center;
height: 80rpx;
background: linear-gradient(135deg, #4C5BF0, #6D7AF5);
color: #FFFFFF;
border-radius: 34rpx;
font-size: 28rpx;
font-weight: 600;
letter-spacing: 2rpx;
transition: all 0.2s;
}
.apply-btn:active {
opacity: 0.85;
transform: scale(0.98);
}
.owned-tip {
display: flex;
align-items: center;
justify-content: center;
height: 80rpx;
color: #10B981;
font-size: 28rpx;
font-weight: 600;
}
/* 记录卡片 */
.record-card {
background: #FFFFFF;
border-radius: 28rpx;
padding: 30rpx;
margin: 0 30rpx 20rpx 30rpx;
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.03);
border: 1rpx solid rgba(0, 0, 0, 0.04);
}
.card-top {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16rpx;
}
.tag-name { font-size: 32rpx; font-weight: 600; color: #1F2937; }
.status-text { font-size: 26rpx; }
.card-info {
display: flex;
flex-direction: column;
gap: 10rpx;
font-size: 26rpx;
color: #4B5563;
}
/* 联系考核官:蓝色下划线 */
.contact-text-link {
color: #4C5BF0;
text-decoration: underline;
font-size: 26rpx;
}
.fee-predict {
background: #F3F4F6;
padding: 12rpx 16rpx;
border-radius: 12rpx;
margin-top: 10rpx;
display: flex;
flex-direction: column;
gap: 6rpx;
font-size: 24rpx;
color: #4B5563;
}
.reapply-btn {
display: flex;
align-items: center;
justify-content: center;
height: 80rpx;
margin-top: 20rpx;
background: #F59E0B;
color: #FFFFFF;
border-radius: 34rpx;
font-size: 28rpx;
font-weight: 600;
}
.reapply-btn:active {
opacity: 0.85;
transform: scale(0.98);
}
.filter-row {
display: flex;
padding: 20rpx 30rpx;
gap: 40rpx;
}
.filter-tag {
font-size: 28rpx;
color: #6B7280;
padding-bottom: 8rpx;
border-bottom: 3rpx solid transparent;
}
.filter-tag.active {
color: #4C5BF0;
border-bottom-color: #4C5BF0;
}
/* 弹窗 */
.modal-mask {
position: fixed; top:0; left:0; width:100%; height:100%;
background: rgba(0, 0, 0, 0.4);
display: flex; align-items: center; justify-content: center;
z-index: 1000;
}
.modal-dialog {
width: 85%;
background: #FFFFFF;
border-radius: 36rpx;
padding: 40rpx;
box-shadow: 0 20rpx 40rpx rgba(0, 0, 0, 0.15);
}
.modal-header { font-size: 34rpx; font-weight: 700; color: #1F2937; margin-bottom: 30rpx; }
.form-group { margin-bottom: 25rpx; }
.form-label { font-size: 28rpx; color: #4B5563; margin-bottom: 10rpx; display: block; }
.form-input, .form-textarea {
width: 100%;
background: #F9FAFB;
border-radius: 14rpx;
padding: 16rpx;
font-size: 28rpx;
color: #1F2937;
border: 1rpx solid #E5E7EB;
}
.form-textarea { height: 150rpx; }
.fee-info { text-align: center; font-size: 30rpx; margin: 20rpx 0; color: #F59E0B; }
.fee-amount { font-weight: 700; }
.modal-actions { display: flex; gap: 20rpx; margin-top: 30rpx; }
.btn-cancel, .btn-confirm {
flex: 1;
height: 80rpx;
display: flex;
align-items: center;
justify-content: center;
border-radius: 40rpx;
font-size: 30rpx;
font-weight: 600;
transition: all 0.2s;
}
.btn-cancel { background: #F3F4F6; color: #4B5563; }
.btn-cancel:active { background: #E5E7EB; }
.btn-confirm { background: linear-gradient(135deg, #4C5BF0, #6D7AF5); color: #FFFFFF; }
.btn-confirm:active { opacity: 0.85; transform: scale(0.98); }
.loading-more, .no-more { text-align: center; padding: 30rpx; color: #9CA3AF; font-size: 26rpx; }
.kg-avatar {
width: 40rpx;
height: 40rpx;
border-radius: 50%;
margin-right: 8rpx;
vertical-align: middle;
}
/* 原有样式保留,以下是新增样式 */
/* 指定考核官选择按钮 */
.select-kg-btn {
display: flex;
align-items: center;
justify-content: center;
height: 80rpx;
background: #F3F4F6;
color: #4B5563;
border-radius: 34rpx;
font-size: 28rpx;
margin-top: 10rpx;
}
.select-kg-btn:active {
background: #E5E7EB;
}
/* 已选择的考核官卡片 */
.selected-kg-card {
display: flex;
align-items: center;
background: #F9FAFB;
border-radius: 20rpx;
padding: 20rpx;
margin-top: 10rpx;
border: 1rpx solid #E5E7EB;
}
.kg-avatar {
width: 64rpx;
height: 64rpx;
border-radius: 50%;
margin-right: 16rpx;
}
.kg-info {
flex: 1;
display: flex;
flex-direction: column;
gap: 4rpx;
}
.kg-uid {
font-size: 28rpx;
font-weight: 600;
color: #1F2937;
}
.kg-nick {
font-size: 24rpx;
color: #6B7280;
}
.kg-status {
font-size: 22rpx;
padding: 2rpx 12rpx;
border-radius: 20rpx;
display: inline-block;
width: fit-content;
}
.kg-status.renzheng {
background: rgba(16, 185, 129, 0.1);
color: #10B981;
}
.kg-status.not-renzheng {
background: rgba(245, 158, 11, 0.1);
color: #F59E0B;
}
.clear-kg-btn {
color: #EF4444;
font-size: 26rpx;
padding: 0 10rpx;
}
.clear-kg-btn:active {
opacity: 0.7;
}
/* 禁用指定提示 */
.kg-disabled-tip {
background: #FEF3C7;
padding: 16rpx;
border-radius: 12rpx;
font-size: 24rpx;
color: #92400E;
margin-top: 10rpx;
}
/* 考核官选择弹窗内搜索 */
.search-box {
margin-bottom: 20rpx;
}
.search-input {
width: 100%;
background: #F9FAFB;
border-radius: 14rpx;
padding: 16rpx;
font-size: 28rpx;
border: 1rpx solid #E5E7EB;
}
/* 考核官滚动列表 */
.kg-scroll {
max-height: 500rpx;
margin-bottom: 20rpx;
}
/* 单个考核官项 */
.kg-item {
display: flex;
align-items: center;
padding: 20rpx;
background: #FFFFFF;
border-radius: 16rpx;
margin-bottom: 12rpx;
border: 1rpx solid #F3F4F6;
}
.kg-item:active {
background: #F9FAFB;
}
.kg-item .kg-state {
font-size: 22rpx;
margin-top: 4rpx;
}
.kg-state.free {
color: #10B981;
}
.kg-state.busy {
color: #EF4444;
}
.empty-tip {
text-align: center;
padding: 30rpx;
color: #9CA3AF;
font-size: 26rpx;
}