第一次提交:微信小程序前端最新完整代码
This commit is contained in:
100
pages/cfss/cfss/cfss.js
Normal file
100
pages/cfss/cfss/cfss.js
Normal file
@@ -0,0 +1,100 @@
|
||||
// pages/cfss/cfss.js
|
||||
import request from '../../../utils/request.js'; // 向上4级就是根目录
|
||||
// 如果有 COS:
|
||||
|
||||
const app = getApp();
|
||||
|
||||
Page({
|
||||
data: {
|
||||
// 统计数字(由接口 /yonghu/cffktjhq 返回)
|
||||
stats: {
|
||||
fakuan: { total: 0, daijiaona: 0, shensuzhong: 0, yijiaona: 0, yibohui: 0 },
|
||||
jifen: { total: 0, daichuli: 0, yichuli: 0, daichuli_0: 0, shensuzhong_3: 0, yichufa_1: 0, yibohui_2: 0 }
|
||||
},
|
||||
|
||||
// 当前选中的主标签:'fakuan' 或 'jifen'
|
||||
activeTab: 'fakuan',
|
||||
|
||||
// 子标签状态值
|
||||
activeFakuanStatus: 1, // 罚款默认展示“待缴纳”
|
||||
activeJifenStatus: 0, // 积分默认展示“待处理”
|
||||
|
||||
// 搜索关键词(订单号)
|
||||
searchDingdan: '',
|
||||
|
||||
// 刷新触发器,传给子组件
|
||||
trigger: {
|
||||
fakuan: 0,
|
||||
jifen: 0
|
||||
}
|
||||
},
|
||||
|
||||
onLoad() {
|
||||
this.fetchTongji();
|
||||
},
|
||||
|
||||
onShow() {
|
||||
// 每次显示页面都重新拉取统计,确保数字最新
|
||||
this.fetchTongji();
|
||||
},
|
||||
|
||||
// 获取综合统计
|
||||
async fetchTongji() {
|
||||
try {
|
||||
const res = await request({
|
||||
url: '/yonghu/cffktjhq',
|
||||
method: 'POST'
|
||||
});
|
||||
if (res.statusCode === 200 && res.data.code === 0) {
|
||||
this.setData({ stats: res.data.data });
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取处罚统计失败', error);
|
||||
}
|
||||
},
|
||||
|
||||
// 切换主Tab(罚款 / 积分)
|
||||
switchMainTab(e) {
|
||||
const tab = e.currentTarget.dataset.tab;
|
||||
if (tab === this.data.activeTab) return;
|
||||
this.setData({ activeTab: tab });
|
||||
},
|
||||
|
||||
// 切换罚款状态子标签
|
||||
switchFakuanStatus(e) {
|
||||
const status = Number(e.currentTarget.dataset.status);
|
||||
if (status === this.data.activeFakuanStatus) return;
|
||||
this.setData({ activeFakuanStatus: status });
|
||||
this.bumpTrigger('fakuan'); // 通知罚款列表组件刷新
|
||||
},
|
||||
|
||||
// 切换积分状态子标签
|
||||
switchJifenStatus(e) {
|
||||
const status = Number(e.currentTarget.dataset.status);
|
||||
if (status === this.data.activeJifenStatus) return;
|
||||
this.setData({ activeJifenStatus: status });
|
||||
this.bumpTrigger('jifen'); // 通知积分列表组件刷新
|
||||
},
|
||||
|
||||
// 搜索输入
|
||||
onSearchInput(e) {
|
||||
this.setData({ searchDingdan: e.detail.value });
|
||||
},
|
||||
|
||||
// 执行搜索(按键盘搜索按钮或点击搜索图标)
|
||||
onSearchConfirm() {
|
||||
this.bumpTrigger(this.data.activeTab);
|
||||
},
|
||||
|
||||
// 清空搜索关键字
|
||||
clearSearch() {
|
||||
this.setData({ searchDingdan: '' });
|
||||
this.bumpTrigger(this.data.activeTab);
|
||||
},
|
||||
|
||||
// 递增触发器,子组件通过 observer 响应
|
||||
bumpTrigger(tab) {
|
||||
const key = `trigger.${tab}`;
|
||||
this.setData({ [key]: this.data.trigger[tab] + 1 });
|
||||
}
|
||||
});
|
||||
10
pages/cfss/cfss/cfss.json
Normal file
10
pages/cfss/cfss/cfss.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"usingComponents": {
|
||||
"fakuan-list": "/pages/cfss/components/fakuan-list/fakuan-list",
|
||||
"jifen-list": "/pages/cfss/components/jifen-list/jifen-list"
|
||||
},
|
||||
"navigationBarTitleText": "处罚中心",
|
||||
"navigationBarBackgroundColor": "#ffffff",
|
||||
"navigationBarTextStyle": "black",
|
||||
"backgroundColor": "#f7f8fa"
|
||||
}
|
||||
68
pages/cfss/cfss/cfss.wxml
Normal file
68
pages/cfss/cfss/cfss.wxml
Normal file
@@ -0,0 +1,68 @@
|
||||
<!-- pages/cfss/cfss.wxml -->
|
||||
<view class="page-container">
|
||||
<!-- 统计卡片 -->
|
||||
<view class="stats-card">
|
||||
<view class="stats-number-row">
|
||||
<view class="number-block">
|
||||
<text class="big-num green">{{ stats.fakuan.daijiaona }}</text>
|
||||
<text class="num-label">待缴罚款</text>
|
||||
</view>
|
||||
<view class="number-block">
|
||||
<text class="big-num orange">{{ stats.jifen.daichuli }}</text>
|
||||
<text class="num-label">待处理积分</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="stats-detail-row">
|
||||
<text>罚款:已缴{{ stats.fakuan.yijiaona }} · 申诉中{{ stats.fakuan.shensuzhong }} · 成功{{ stats.fakuan.yibohui }}</text>
|
||||
<text>积分:已处理{{ stats.jifen.yichuli }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 主Tab -->
|
||||
<view class="tab-row">
|
||||
<view class="tab-item {{ activeTab === 'fakuan' ? 'active' : '' }}" bindtap="switchMainTab" data-tab="fakuan">
|
||||
罚款
|
||||
</view>
|
||||
<view class="tab-item {{ activeTab === 'jifen' ? 'active' : '' }}" bindtap="switchMainTab" data-tab="jifen">
|
||||
积分处罚
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 子状态标签(横向滑动) -->
|
||||
<block wx:if="{{ activeTab === 'fakuan' }}">
|
||||
<scroll-view class="sub-tabs" scroll-x enable-flex>
|
||||
<view class="sub-tag {{ activeFakuanStatus === 1 ? 'sub-active' : '' }}" bindtap="switchFakuanStatus" data-status="1">待缴纳 {{ stats.fakuan.daijiaona }}</view>
|
||||
<view class="sub-tag {{ activeFakuanStatus === 3 ? 'sub-active' : '' }}" bindtap="switchFakuanStatus" data-status="3">申诉中 {{ stats.fakuan.shensuzhong }}</view>
|
||||
<view class="sub-tag {{ activeFakuanStatus === 2 ? 'sub-active' : '' }}" bindtap="switchFakuanStatus" data-status="2">已缴纳 {{ stats.fakuan.yijiaona }}</view>
|
||||
<view class="sub-tag {{ activeFakuanStatus === 4 ? 'sub-active' : '' }}" bindtap="switchFakuanStatus" data-status="4">申诉成功 {{ stats.fakuan.yibohui }}</view>
|
||||
</scroll-view>
|
||||
</block>
|
||||
<block wx:else>
|
||||
<scroll-view class="sub-tabs" scroll-x enable-flex>
|
||||
<view class="sub-tag {{ activeJifenStatus === 0 ? 'sub-active' : '' }}" bindtap="switchJifenStatus" data-status="0">待处理 {{ stats.jifen.daichuli }}</view>
|
||||
<view class="sub-tag {{ activeJifenStatus === 1 ? 'sub-active' : '' }}" bindtap="switchJifenStatus" data-status="1">已处理 {{ stats.jifen.yichuli }}</view>
|
||||
</scroll-view>
|
||||
</block>
|
||||
|
||||
<!-- 搜索栏 -->
|
||||
<view class="search-bar">
|
||||
<input class="search-input" placeholder="输入订单号搜索" value="{{ searchDingdan }}" bindinput="onSearchInput" bindconfirm="onSearchConfirm" confirm-type="search"/>
|
||||
<view class="search-btn" bindtap="onSearchConfirm">搜索</view>
|
||||
<view wx:if="{{ searchDingdan }}" class="clear-btn" bindtap="clearSearch">✕</view>
|
||||
</view>
|
||||
|
||||
<!-- 列表区域(动态显示对应组件) -->
|
||||
<view wx:if="{{ activeTab === 'fakuan' }}" class="list-area">
|
||||
<fakuan-list
|
||||
status="{{ activeFakuanStatus }}"
|
||||
searchDingdan="{{ searchDingdan }}"
|
||||
trigger="{{ trigger.fakuan }}"
|
||||
/>
|
||||
</view>
|
||||
<view wx:else class="list-area">
|
||||
<jifen-list
|
||||
status="{{ activeJifenStatus }}"
|
||||
trigger="{{ trigger.jifen }}"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
121
pages/cfss/cfss/cfss.wxss
Normal file
121
pages/cfss/cfss/cfss.wxss
Normal file
@@ -0,0 +1,121 @@
|
||||
/* pages/cfss/cfss.wxss */
|
||||
page { background: #f7f8fa; font-family: -apple-system, BlinkMacSystemFont, sans-serif; }
|
||||
|
||||
.page-container { padding-bottom: 30rpx; }
|
||||
|
||||
/* 统计卡片 */
|
||||
.stats-card {
|
||||
margin: 24rpx 28rpx;
|
||||
padding: 30rpx 24rpx;
|
||||
background: #fff;
|
||||
border-radius: 24rpx;
|
||||
box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.04);
|
||||
}
|
||||
.stats-number-row {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
.number-block {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
.big-num {
|
||||
font-size: 56rpx;
|
||||
font-weight: 700;
|
||||
}
|
||||
.green { color: #2E7D32; }
|
||||
.orange { color: #E67E22; }
|
||||
.num-label {
|
||||
font-size: 24rpx;
|
||||
color: #888;
|
||||
margin-top: 8rpx;
|
||||
}
|
||||
.stats-detail-row {
|
||||
border-top: 1rpx solid #eee;
|
||||
padding-top: 18rpx;
|
||||
}
|
||||
.stats-detail-row text {
|
||||
display: block;
|
||||
font-size: 22rpx;
|
||||
color: #aaa;
|
||||
margin-bottom: 4rpx;
|
||||
}
|
||||
|
||||
/* 主Tab */
|
||||
.tab-row {
|
||||
display: flex;
|
||||
margin: 16rpx 28rpx;
|
||||
background: #fff;
|
||||
border-radius: 18rpx;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 2rpx 6rpx rgba(0,0,0,0.02);
|
||||
}
|
||||
.tab-item {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
padding: 24rpx 0;
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
transition: 0.2s;
|
||||
}
|
||||
.tab-item.active {
|
||||
color: #2E7D32;
|
||||
font-weight: 600;
|
||||
background: #F0F9F0;
|
||||
}
|
||||
|
||||
/* 子状态标签 */
|
||||
.sub-tabs {
|
||||
white-space: nowrap;
|
||||
margin: 8rpx 28rpx 16rpx;
|
||||
}
|
||||
.sub-tag {
|
||||
display: inline-block;
|
||||
padding: 10rpx 26rpx;
|
||||
margin-right: 14rpx;
|
||||
background: #fff;
|
||||
border-radius: 30rpx;
|
||||
font-size: 24rpx;
|
||||
color: #888;
|
||||
box-shadow: 0 1rpx 4rpx rgba(0,0,0,0.02);
|
||||
}
|
||||
.sub-tag.sub-active {
|
||||
background: #E8F5E9;
|
||||
color: #2E7D32;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* 搜索栏 */
|
||||
.search-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 16rpx 28rpx;
|
||||
background: #fff;
|
||||
border-radius: 20rpx;
|
||||
padding: 12rpx 22rpx;
|
||||
box-shadow: 0 1rpx 6rpx rgba(0,0,0,0.02);
|
||||
}
|
||||
.search-input {
|
||||
flex: 1;
|
||||
font-size: 26rpx;
|
||||
color: #333;
|
||||
}
|
||||
.search-btn {
|
||||
margin-left: 18rpx;
|
||||
padding: 12rpx 30rpx;
|
||||
background: #2E7D32;
|
||||
color: #fff;
|
||||
border-radius: 16rpx;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
.clear-btn {
|
||||
margin-left: 16rpx;
|
||||
font-size: 30rpx;
|
||||
color: #aaa;
|
||||
}
|
||||
|
||||
.list-area {
|
||||
margin-top: 10rpx;
|
||||
}
|
||||
317
pages/cfss/components/fakuan-list/fakuan-list.js
Normal file
317
pages/cfss/components/fakuan-list/fakuan-list.js
Normal file
@@ -0,0 +1,317 @@
|
||||
// components/fakuan-list/fakuan-list.js
|
||||
import request from '../../../../utils/request.js';
|
||||
|
||||
const app = getApp();
|
||||
const MEIYE_TIAOSHU = 5;
|
||||
|
||||
Component({
|
||||
properties: {
|
||||
status: { type: Number, value: 1, observer: 'reload' },
|
||||
searchDingdan: { type: String, value: '', observer: 'reload' },
|
||||
trigger: { type: Number, value: 0, observer: 'reload' }
|
||||
},
|
||||
|
||||
data: {
|
||||
list: [],
|
||||
page: 1,
|
||||
hasMore: true,
|
||||
loading: false,
|
||||
loadingMore: false,
|
||||
ossImageUrl: app.globalData.ossImageUrl || '',
|
||||
|
||||
// 详情弹窗
|
||||
showDetail: false,
|
||||
detailItem: null,
|
||||
|
||||
// 申诉弹窗
|
||||
showShensu: false,
|
||||
shensuLiyou: '',
|
||||
shensuTupian: [],
|
||||
uploading: false,
|
||||
uploadProgress: { current: 0, total: 0 },
|
||||
uploadProgressWidth: '0%',
|
||||
|
||||
// 支付弹窗
|
||||
showPay: false,
|
||||
payFadan: null
|
||||
},
|
||||
|
||||
lifetimes: {
|
||||
attached() { this.reload(); }
|
||||
},
|
||||
|
||||
methods: {
|
||||
// ==================== 数据加载 ====================
|
||||
reload() {
|
||||
this.setData({ page: 1, list: [], hasMore: true });
|
||||
this.loadData();
|
||||
},
|
||||
|
||||
async loadData(isLoadMore = false) {
|
||||
// 防止重复请求
|
||||
if (this.data.loading || this.data.loadingMore) return;
|
||||
|
||||
if (!isLoadMore) {
|
||||
this.setData({ loading: true });
|
||||
} else {
|
||||
// 加载更多时,如果后端已经告知没有更多数据,不发送请求
|
||||
if (!this.data.hasMore) {
|
||||
wx.showToast({ title: '没有更多了', icon: 'none', duration: 1000 });
|
||||
return;
|
||||
}
|
||||
this.setData({ loadingMore: true });
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await request({
|
||||
url: '/yonghu/dsfklbhq',
|
||||
method: 'POST',
|
||||
data: {
|
||||
page: this.data.page,
|
||||
page_size: MEIYE_TIAOSHU,
|
||||
zhuangtai: this.data.status,
|
||||
sousuo_dingdan_id: this.data.searchDingdan
|
||||
}
|
||||
});
|
||||
if (res.statusCode === 200 && res.data.code === 0) {
|
||||
const data = res.data.data;
|
||||
const newList = isLoadMore ? this.data.list.concat(data.list || []) : (data.list || []);
|
||||
this.setData({
|
||||
list: newList,
|
||||
hasMore: data.has_more || false, // 以后端返回为准
|
||||
page: isLoadMore ? this.data.page + 1 : 2, // 首次加载后页码为2
|
||||
loading: false,
|
||||
loadingMore: false
|
||||
});
|
||||
} else {
|
||||
wx.showToast({ title: res.data?.msg || '加载失败', icon: 'none' });
|
||||
this.setData({ loading: false, loadingMore: false });
|
||||
}
|
||||
} catch (e) {
|
||||
wx.showToast({ title: '网络请求失败', icon: 'none' });
|
||||
this.setData({ loading: false, loadingMore: false });
|
||||
}
|
||||
},
|
||||
|
||||
loadMore() {
|
||||
if (this.data.list.length === 0) return;
|
||||
this.loadData(true);
|
||||
},
|
||||
|
||||
// ==================== 详情弹窗 ====================
|
||||
openDetail(e) {
|
||||
const item = e.currentTarget.dataset.item;
|
||||
this.setData({ showDetail: true, detailItem: item });
|
||||
},
|
||||
closeDetail() {
|
||||
this.setData({ showDetail: false, detailItem: null });
|
||||
},
|
||||
|
||||
// ==================== 申诉逻辑 ====================
|
||||
openShensu() {
|
||||
const item = this.data.detailItem;
|
||||
if (item.bohuiliyou) {
|
||||
wx.showToast({ title: '该罚单申诉已被驳回', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
const existingImgs = (item.shensu_tupian || []).map(url => this.getFullUrl(url));
|
||||
this.setData({
|
||||
showShensu: true,
|
||||
shensuLiyou: item.shensuliyou || '',
|
||||
shensuTupian: existingImgs,
|
||||
uploading: false,
|
||||
uploadProgress: { current: 0, total: 0 },
|
||||
uploadProgressWidth: '0%'
|
||||
});
|
||||
},
|
||||
closeShensu() {
|
||||
this.setData({ showShensu: false });
|
||||
},
|
||||
onShensuInput(e) {
|
||||
this.setData({ shensuLiyou: e.detail.value.slice(0, 500) });
|
||||
},
|
||||
|
||||
chooseImage() {
|
||||
const remain = 9 - this.data.shensuTupian.length;
|
||||
if (remain <= 0) return;
|
||||
wx.chooseMedia({
|
||||
count: remain,
|
||||
mediaType: ['image'],
|
||||
sourceType: ['album', 'camera'],
|
||||
success: (res) => {
|
||||
const newImgs = [...this.data.shensuTupian, ...res.tempFiles.map(f => f.tempFilePath)];
|
||||
this.setData({ shensuTupian: newImgs.slice(0, 9) });
|
||||
}
|
||||
});
|
||||
},
|
||||
deleteShensuImg(e) {
|
||||
const arr = this.data.shensuTupian;
|
||||
arr.splice(e.currentTarget.dataset.index, 1);
|
||||
this.setData({ shensuTupian: arr });
|
||||
},
|
||||
previewImage(e) {
|
||||
wx.previewImage({ current: e.currentTarget.dataset.url, urls: [e.currentTarget.dataset.url] });
|
||||
},
|
||||
previewLocalImg(e) {
|
||||
const idx = e.currentTarget.dataset.index;
|
||||
wx.previewImage({ current: this.data.shensuTupian[idx], urls: this.data.shensuTupian });
|
||||
},
|
||||
|
||||
// ==================== 提交流程 ====================
|
||||
async submitShensu() {
|
||||
if (!this.data.shensuLiyou.trim()) {
|
||||
wx.showToast({ title: '请输入申诉理由', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
wx.showLoading({ title: '提交中...' });
|
||||
try {
|
||||
const newLocalPaths = this.data.shensuTupian.filter(path => !path.startsWith('http'));
|
||||
let finalUrls = [];
|
||||
if (newLocalPaths.length > 0) {
|
||||
finalUrls = await this.piliangShangchuanShensuTupian(newLocalPaths);
|
||||
if (!finalUrls) {
|
||||
wx.hideLoading();
|
||||
return;
|
||||
}
|
||||
}
|
||||
const oldRemoteRelative = this.data.shensuTupian
|
||||
.filter(path => path.startsWith('http'))
|
||||
.map(url => url.replace(this.data.ossImageUrl, '').replace(/^\//, ''));
|
||||
const allUrls = oldRemoteRelative.concat(finalUrls);
|
||||
await this.doSubmitShensu(allUrls);
|
||||
} catch (err) {
|
||||
console.error('提交申诉失败', err);
|
||||
wx.showToast({ title: err.message || '提交失败', icon: 'none' });
|
||||
} finally {
|
||||
wx.hideLoading();
|
||||
}
|
||||
},
|
||||
|
||||
async doSubmitShensu(tupianUrls) {
|
||||
const res = await request({
|
||||
url: '/yonghu/fkss',
|
||||
method: 'POST',
|
||||
data: {
|
||||
fadan_id: this.data.detailItem.id,
|
||||
shensu_liyou: this.data.shensuLiyou,
|
||||
shensu_tupian_urls: tupianUrls
|
||||
}
|
||||
});
|
||||
if (res.statusCode === 200 && res.data.code === 0) {
|
||||
wx.showToast({ title: '申诉已提交', icon: 'success' });
|
||||
this.setData({ showShensu: false, showDetail: false });
|
||||
this.reload();
|
||||
} else {
|
||||
throw new Error(res.data?.msg || '提交失败');
|
||||
}
|
||||
},
|
||||
|
||||
// ==================== 图片上传到COS ====================
|
||||
async piliangShangchuanShensuTupian(localPaths) {
|
||||
const total = localPaths.length;
|
||||
if (!total) return [];
|
||||
this.setData({ uploading: true, uploadProgress: { current: 0, total: total }, uploadProgressWidth: '0%' });
|
||||
|
||||
let tokenData;
|
||||
try {
|
||||
const credRes = await request({
|
||||
url: '/dingdan/dsscpz',
|
||||
method: 'POST',
|
||||
data: {
|
||||
dingdan_id: this.data.detailItem.guanliandingdan_id || '',
|
||||
fadan_id: this.data.detailItem.id,
|
||||
yongtu: 'fakuan'
|
||||
}
|
||||
});
|
||||
if (credRes.data.code !== 0) throw new Error(credRes.data.msg || '凭证获取失败');
|
||||
tokenData = credRes.data.data;
|
||||
} catch (e) {
|
||||
wx.showToast({ title: '获取上传凭证失败', icon: 'none' });
|
||||
this.setData({ uploading: false });
|
||||
return null;
|
||||
}
|
||||
|
||||
const COS = require('../../../../utils/cos-wx-sdk-v5.js');
|
||||
const credentials = tokenData.credentials || tokenData;
|
||||
const cos = new COS({
|
||||
SimpleUploadMethod: 'putObject',
|
||||
getAuthorization: (options, callback) => {
|
||||
callback({
|
||||
TmpSecretId: credentials.tmpSecretId,
|
||||
TmpSecretKey: credentials.tmpSecretKey,
|
||||
SecurityToken: credentials.sessionToken || '',
|
||||
StartTime: tokenData.startTime,
|
||||
ExpiredTime: tokenData.expiredTime
|
||||
});
|
||||
}
|
||||
});
|
||||
const bucket = tokenData.bucket || 'julebu-1361527063';
|
||||
const region = tokenData.region || 'ap-shanghai';
|
||||
|
||||
const yonghuid = app.globalData.userInfo?.yonghuid || wx.getStorageSync('uid') || '0000000';
|
||||
const fadanId = this.data.detailItem.id;
|
||||
const keys = [];
|
||||
for (let i = 0; i < total; i++) {
|
||||
keys.push(`fakuan/dashoufakuan/dashoufakuanshensu/${yonghuid}_${fadanId}_${Date.now() + i}.jpg`);
|
||||
}
|
||||
|
||||
for (let i = 0; i < total; i++) {
|
||||
try {
|
||||
await new Promise((resolve, reject) => {
|
||||
const timeoutId = setTimeout(() => resolve(), 2000);
|
||||
cos.uploadFile({
|
||||
Bucket: bucket,
|
||||
Region: region,
|
||||
Key: keys[i],
|
||||
FilePath: localPaths[i],
|
||||
onProgress: (info) => {
|
||||
if (Math.round(info.percent * 100) === 100) {
|
||||
clearTimeout(timeoutId);
|
||||
resolve();
|
||||
}
|
||||
}
|
||||
}, (err) => {
|
||||
clearTimeout(timeoutId);
|
||||
if (err) reject(err);
|
||||
else resolve();
|
||||
});
|
||||
});
|
||||
} catch (err) {
|
||||
wx.showToast({ title: `第 ${i + 1} 张上传失败`, icon: 'none' });
|
||||
this.setData({ uploading: false });
|
||||
return null;
|
||||
}
|
||||
const done = i + 1;
|
||||
this.setData({
|
||||
uploadProgress: { current: done, total: total },
|
||||
uploadProgressWidth: `${((done / total) * 100).toFixed(0)}%`
|
||||
});
|
||||
}
|
||||
this.setData({ uploading: false });
|
||||
return keys;
|
||||
},
|
||||
|
||||
// ==================== 缴纳支付 ====================
|
||||
startPay() {
|
||||
this.setData({
|
||||
showDetail: false,
|
||||
showPay: true,
|
||||
payFadan: this.data.detailItem
|
||||
});
|
||||
},
|
||||
onPayClose() {
|
||||
this.setData({ showPay: false, payFadan: null });
|
||||
},
|
||||
onPaySuccess() {
|
||||
this.setData({ showPay: false, payFadan: null });
|
||||
this.reload();
|
||||
},
|
||||
|
||||
// ==================== 工具方法 ====================
|
||||
getFullUrl(url) {
|
||||
if (!url) return '';
|
||||
if (url.startsWith('http')) return url;
|
||||
return this.data.ossImageUrl + url;
|
||||
}
|
||||
}
|
||||
});
|
||||
6
pages/cfss/components/fakuan-list/fakuan-list.json
Normal file
6
pages/cfss/components/fakuan-list/fakuan-list.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"component": true,
|
||||
"usingComponents": {
|
||||
"fakuan-pay": "/pages/cfss/components/fakuan-pay/fakuan-pay"
|
||||
}
|
||||
}
|
||||
116
pages/cfss/components/fakuan-list/fakuan-list.wxml
Normal file
116
pages/cfss/components/fakuan-list/fakuan-list.wxml
Normal file
@@ -0,0 +1,116 @@
|
||||
<!-- components/fakuan-list/fakuan-list.wxml -->
|
||||
<view class="container">
|
||||
<scroll-view scroll-y class="scroll" bindscrolltolower="loadMore">
|
||||
<view wx:if="{{ loading && list.length === 0 }}" class="tip">加载中...</view>
|
||||
<block wx:for="{{ list }}" wx:key="id">
|
||||
<view class="card" bindtap="openDetail" data-item="{{ item }}">
|
||||
<view class="card-head">
|
||||
<text class="time">{{ item.create_time }}</text>
|
||||
<text class="tag {{ item.status_class }}">{{ item.display_status }}</text>
|
||||
</view>
|
||||
<text class="info">金额:¥{{ item.fakuanjine }}</text>
|
||||
<text class="info">理由:{{ item.chufaliyou }}</text>
|
||||
<text class="info" wx:if="{{ item.guanliandingdan_id }}">订单:{{ item.guanliandingdan_id }}</text>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<!-- ✅ 按钮始终显示,只要列表不为空且没有正在加载 -->
|
||||
<view wx:if="{{ list.length > 0 && !loadingMore }}" class="load-more-btn" bindtap="loadMore">
|
||||
<text>点击获取更多</text>
|
||||
</view>
|
||||
<view wx:if="{{ loadingMore }}" class="tip">加载中...</view>
|
||||
<view wx:if="{{ !hasMore && list.length > 0 }}" class="tip">—— 没有更多了 ——</view>
|
||||
<view wx:if="{{ !loading && list.length === 0 }}" class="empty">暂无罚款记录</view>
|
||||
</scroll-view>
|
||||
|
||||
<!-- 详情弹窗 -->
|
||||
<view wx:if="{{ showDetail }}" class="modal-mask">
|
||||
<view class="modal-panel">
|
||||
<view class="modal-head">
|
||||
<text class="modal-title">罚款详情</text>
|
||||
<view class="modal-close" bindtap="closeDetail">✕</view>
|
||||
</view>
|
||||
<scroll-view scroll-y class="modal-body">
|
||||
<view class="row"><text class="lbl">罚款金额</text><text class="val strong">¥{{ detailItem.fakuanjine }}</text></view>
|
||||
<view class="row"><text class="lbl">当前状态</text><text class="tag {{ detailItem.status_class }}">{{ detailItem.display_status }}</text></view>
|
||||
<view class="row"><text class="lbl">处罚理由</text><text class="val">{{ detailItem.chufaliyou }}</text></view>
|
||||
<view wx:if="{{ detailItem.guanliandingdan_id }}" class="row"><text class="lbl">关联订单</text><text class="val">{{ detailItem.guanliandingdan_id }}</text></view>
|
||||
<view wx:if="{{ detailItem.bohuiliyou }}" class="row"><text class="lbl">驳回理由</text><text class="val">{{ detailItem.bohuiliyou }}</text></view>
|
||||
<view wx:if="{{ detailItem.shensuliyou }}" class="row"><text class="lbl">申诉理由</text><text class="val">{{ detailItem.shensuliyou }}</text></view>
|
||||
|
||||
<!-- 证据图片 -->
|
||||
<view wx:if="{{ detailItem.zhengju_tupian && detailItem.zhengju_tupian.length }}" class="section">
|
||||
<text class="sec-title">证据图片</text>
|
||||
<view class="img-grid">
|
||||
<block wx:for="{{ detailItem.zhengju_tupian }}" wx:key="index">
|
||||
<view class="img-box" bindtap="previewImage" data-url="{{ ossImageUrl + item }}">
|
||||
<image class="img" src="{{ ossImageUrl + item }}" mode="aspectFill" />
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 申诉图片 -->
|
||||
<view wx:if="{{ detailItem.shensu_tupian && detailItem.shensu_tupian.length }}" class="section">
|
||||
<text class="sec-title">申诉图片</text>
|
||||
<view class="img-grid">
|
||||
<block wx:for="{{ detailItem.shensu_tupian }}" wx:key="index">
|
||||
<view class="img-box" bindtap="previewImage" data-url="{{ ossImageUrl + item }}">
|
||||
<image class="img" src="{{ ossImageUrl + item }}" mode="aspectFill" />
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
<view class="modal-foot">
|
||||
<view wx:if="{{ detailItem.zhuangtai === 1 && !detailItem.bohuiliyou }}" class="btn primary" bindtap="openShensu">申诉</view>
|
||||
<view wx:if="{{ detailItem.zhuangtai === 1 || detailItem.zhuangtai === 3 }}" class="btn primary" bindtap="startPay">立即缴纳</view>
|
||||
<view class="btn default" bindtap="closeDetail">关闭</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 申诉弹窗 -->
|
||||
<view wx:if="{{ showShensu }}" class="modal-mask">
|
||||
<view class="modal-panel">
|
||||
<view class="modal-head">
|
||||
<text class="modal-title">提交申诉</text>
|
||||
<view class="modal-close" bindtap="closeShensu">✕</view>
|
||||
</view>
|
||||
<scroll-view scroll-y class="modal-body">
|
||||
<view class="form-group">
|
||||
<text class="sec-title">申诉理由</text>
|
||||
<textarea class="textarea" placeholder="请输入申诉理由" value="{{ shensuLiyou }}" bindinput="onShensuInput" maxlength="500" />
|
||||
<text class="word-count">{{ shensuLiyou.length }}/500</text>
|
||||
</view>
|
||||
<view class="form-group">
|
||||
<text class="sec-title">上传凭证(最多9张)</text>
|
||||
<view class="img-grid">
|
||||
<block wx:for="{{ shensuTupian }}" wx:key="index">
|
||||
<view class="img-box">
|
||||
<image class="img" src="{{ item }}" mode="aspectFill" bindtap="previewLocalImg" data-index="{{ index }}" />
|
||||
<view class="img-del" catchtap="deleteShensuImg" data-index="{{ index }}">✕</view>
|
||||
</view>
|
||||
</block>
|
||||
<view wx:if="{{ shensuTupian.length < 9 }}" class="img-add" bindtap="chooseImage">+</view>
|
||||
</view>
|
||||
<view wx:if="{{ uploading }}" class="progress-box">
|
||||
<view class="progress-bar"><view class="progress-inner" style="width: {{ uploadProgressWidth }}"></view></view>
|
||||
<text class="progress-text">{{ uploadProgress.current }}/{{ uploadProgress.total }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
<view class="modal-foot">
|
||||
<view class="btn default" bindtap="closeShensu">取消</view>
|
||||
<view class="btn primary" bindtap="submitShensu">提交申诉</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 支付组件(缴纳罚款) -->
|
||||
<fakuan-pay
|
||||
visible="{{ showPay }}"
|
||||
fadan="{{ payFadan }}"
|
||||
bind:close="onPayClose"
|
||||
bind:success="onPaySuccess"
|
||||
/>
|
||||
</view>
|
||||
77
pages/cfss/components/fakuan-list/fakuan-list.wxss
Normal file
77
pages/cfss/components/fakuan-list/fakuan-list.wxss
Normal file
@@ -0,0 +1,77 @@
|
||||
/* 完全复用积分组件的样式 */
|
||||
page { background: #f5f5f5; }
|
||||
|
||||
.container { width: 100%; box-sizing: border-box; display: flex; flex-direction: column; }
|
||||
|
||||
.scroll { width: 100%; box-sizing: border-box; padding: 20rpx 24rpx; }
|
||||
|
||||
.tip { text-align: center; color: #999; font-size: 24rpx; padding: 30rpx 0; }
|
||||
.empty { display: flex; flex-direction: column; align-items: center; padding-top: 200rpx; color: #999; font-size: 28rpx; }
|
||||
|
||||
.card { width: 100%; box-sizing: border-box; background: #fff; border-radius: 16rpx; padding: 24rpx; margin-bottom: 16rpx; }
|
||||
.card-head { display: flex; justify-content: space-between; align-items: center; margin-bottom: 12rpx; }
|
||||
.time { font-size: 24rpx; color: #999; }
|
||||
.tag { font-size: 20rpx; padding: 2rpx 14rpx; border-radius: 16rpx; display: inline-block; }
|
||||
|
||||
/* 状态标签颜色(与积分组件完全相同) */
|
||||
.zhuangtai-daijiaona { background: #FFF3E0; color: #E65100; } /* 待缴纳 */
|
||||
.zhuangtai-yijiaona { background: #E8F5E9; color: #2E7D32; } /* 已缴纳 */
|
||||
.zhuangtai-shensuzhong { background: #E3F2FD; color: #1565C0; } /* 申诉中 */
|
||||
.zhuangtai-yibohui { background: #F3E5F5; color: #7B1FA2; } /* 申诉成功 */
|
||||
|
||||
.info { display: block; font-size: 26rpx; color: #333; margin-top: 8rpx; }
|
||||
|
||||
.modal-mask { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.5); z-index: 999; display: flex; align-items: flex-end; justify-content: center; }
|
||||
.modal-panel { width: 100%; box-sizing: border-box; max-height: 85vh; background: #fff; border-radius: 24rpx 24rpx 0 0; display: flex; flex-direction: column; overflow: hidden; }
|
||||
.modal-head { display: flex; justify-content: space-between; align-items: center; padding: 28rpx 30rpx 16rpx; border-bottom: 1px solid #eee; }
|
||||
.modal-title { font-size: 32rpx; font-weight: 600; }
|
||||
.modal-close { width: 44rpx; height: 44rpx; border-radius: 50%; background: #f5f5f5; display: flex; align-items: center; justify-content: center; font-size: 26rpx; color: #888; }
|
||||
|
||||
.modal-body { width: 100%; box-sizing: border-box; padding: 20rpx 30rpx; overflow-y: auto; }
|
||||
.row { display: flex; margin-bottom: 16rpx; }
|
||||
.lbl { width: 150rpx; font-size: 26rpx; color: #888; flex-shrink: 0; }
|
||||
.val { flex: 1; font-size: 26rpx; color: #333; word-break: break-all; }
|
||||
.strong { font-weight: 600; color: #E53935; }
|
||||
.section { margin: 20rpx 0; }
|
||||
.sec-title { font-size: 26rpx; color: #888; margin-bottom: 12rpx; display: block; }
|
||||
|
||||
.img-grid { width: 100%; box-sizing: border-box; display: flex; flex-wrap: wrap; gap: 12rpx; }
|
||||
.img-box { position: relative; width: calc((100% - 24rpx) / 3); height: 0; padding-bottom: calc((100% - 24rpx) / 3); }
|
||||
.img-box .img { position: absolute; top: 0; left: 0; width: 100%; height: 100%; border-radius: 10rpx; background: #f0f0f0; }
|
||||
.img-del { position: absolute; top: -6rpx; right: -6rpx; width: 34rpx; height: 34rpx; background: #e53935; border-radius: 50%; display: flex; align-items: center; justify-content: center; color: #fff; font-size: 18rpx; z-index: 2; }
|
||||
.img-add { width: calc((100% - 24rpx) / 3); height: 0; padding-bottom: calc((100% - 24rpx) / 3); border: 2rpx dashed #ccc; border-radius: 10rpx; display: flex; align-items: center; justify-content: center; font-size: 48rpx; color: #aaa; position: relative; box-sizing: border-box; }
|
||||
|
||||
.form-group { margin-bottom: 24rpx; }
|
||||
.textarea { width: 100%; box-sizing: border-box; height: 180rpx; background: #f9f9f9; border-radius: 10rpx; padding: 16rpx; font-size: 26rpx; border: 1px solid #eee; }
|
||||
.word-count { text-align: right; font-size: 22rpx; color: #bbb; margin-top: 6rpx; }
|
||||
.progress-box { margin-top: 12rpx; }
|
||||
.progress-bar { height: 10rpx; background: #eee; border-radius: 5rpx; }
|
||||
.progress-inner { height: 100%; background: #2e7d32; border-radius: 5rpx; }
|
||||
.progress-text { font-size: 22rpx; color: #2e7d32; text-align: right; margin-top: 4rpx; }
|
||||
|
||||
.modal-foot { width: 100%; box-sizing: border-box; display: flex; padding: 16rpx 30rpx 30rpx; gap: 20rpx; border-top: 1px solid #eee; }
|
||||
.btn { flex: 1; text-align: center; padding: 22rpx 0; border-radius: 16rpx; font-size: 28rpx; font-weight: 600; }
|
||||
.primary { background: #2e7d32; color: #fff; }
|
||||
.default { background: #f0f0f0; color: #555; }
|
||||
.btn:active { opacity: 0.8; }
|
||||
|
||||
|
||||
/* ===== 获取更多按钮 ===== */
|
||||
.load-more-btn {
|
||||
width: 80%;
|
||||
margin: 20rpx auto 30rpx;
|
||||
padding: 22rpx 0;
|
||||
text-align: center;
|
||||
background: #ffffff;
|
||||
border: 2rpx solid #e0e0e0;
|
||||
border-radius: 20rpx;
|
||||
color: #555;
|
||||
font-size: 26rpx;
|
||||
box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.02);
|
||||
transition: all 0.15s;
|
||||
}
|
||||
.load-more-btn:active {
|
||||
background: #f5f5f5;
|
||||
border-color: #2e7d32;
|
||||
color: #2e7d32;
|
||||
}
|
||||
178
pages/cfss/components/fakuan-pay/fakuan-pay.js
Normal file
178
pages/cfss/components/fakuan-pay/fakuan-pay.js
Normal file
@@ -0,0 +1,178 @@
|
||||
// components/fakuan-list/fakuan-pay/fakuan-pay.js
|
||||
import request from '../../../../utils/request.js'; // 路径保持您原来的样子
|
||||
|
||||
Component({
|
||||
properties: {
|
||||
visible: { type: Boolean, value: false, observer: 'onVisibleChange' },
|
||||
fadan: { type: Object, value: null } // { id, fakuanjine }
|
||||
},
|
||||
|
||||
data: {
|
||||
step: 'method', // method / balance / confirm / loading
|
||||
balanceOptions: [],
|
||||
selectedBalance: null,
|
||||
loadingText: '',
|
||||
payLoading: false,
|
||||
dingdanid: '' // 微信支付内部订单ID
|
||||
},
|
||||
|
||||
methods: {
|
||||
onVisibleChange(val) {
|
||||
if (val) {
|
||||
// 每次打开重置为支付方式选择
|
||||
this.setData({ step: 'method', balanceOptions: [], selectedBalance: null });
|
||||
}
|
||||
},
|
||||
|
||||
close() {
|
||||
this.setData({ visible: false });
|
||||
this.triggerEvent('close');
|
||||
},
|
||||
|
||||
// 选择支付方式
|
||||
async chooseMethod(e) {
|
||||
const method = e.currentTarget.dataset.method;
|
||||
if (method === 'wx') {
|
||||
this.wxPay();
|
||||
} else if (method === 'balance') {
|
||||
this.fetchBalanceOptions();
|
||||
}
|
||||
},
|
||||
|
||||
// ========== 微信支付流程 ==========
|
||||
async wxPay() {
|
||||
this.setData({ step: 'loading', loadingText: '发起支付中...' });
|
||||
try {
|
||||
const res = await request({
|
||||
url: '/yonghu/fkjn',
|
||||
method: 'POST',
|
||||
data: { fadan_id: this.properties.fadan.id }
|
||||
});
|
||||
if (res.data.code !== 200) throw new Error(res.data.msg || '下单失败');
|
||||
|
||||
// 🔥 唯一修改点:后端返回格式 { code:200, payParams:{...}, dingdanid:'...' }
|
||||
const payParams = res.data.payParams;
|
||||
const dingdanid = res.data.dingdanid;
|
||||
|
||||
this.setData({ dingdanid });
|
||||
await this.requestWxPayment(payParams);
|
||||
} catch (e) {
|
||||
this.setData({ step: 'method' });
|
||||
wx.showToast({ title: e.message, icon: 'none' });
|
||||
}
|
||||
},
|
||||
|
||||
async requestWxPayment(payParams) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!payParams || !payParams.timeStamp || !payParams.paySign) {
|
||||
wx.showToast({ title: '支付参数错误', icon: 'none' });
|
||||
reject(new Error('支付参数错误'));
|
||||
return;
|
||||
}
|
||||
wx.requestPayment({
|
||||
timeStamp: payParams.timeStamp,
|
||||
nonceStr: payParams.nonceStr,
|
||||
package: payParams.package,
|
||||
signType: payParams.signType || 'MD5',
|
||||
paySign: payParams.paySign,
|
||||
success: () => {
|
||||
this.pollPayStatus();
|
||||
resolve();
|
||||
},
|
||||
fail: (err) => {
|
||||
this.setData({ step: 'method' });
|
||||
this.notifyPayFail();
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
async pollPayStatus() {
|
||||
this.setData({ step: 'loading', loadingText: '确认支付结果...' });
|
||||
for (let i = 0; i < 10; i++) {
|
||||
try {
|
||||
const res = await request({
|
||||
url: '/yonghu/fkzffc',
|
||||
method: 'POST',
|
||||
data: { dingdanid: this.data.dingdanid }
|
||||
});
|
||||
if (res.data.code === 200) {
|
||||
this.triggerEvent('success');
|
||||
this.close();
|
||||
wx.showToast({ title: '缴纳成功', icon: 'success' });
|
||||
return;
|
||||
}
|
||||
} catch (e) {}
|
||||
await new Promise(r => setTimeout(r, 2000));
|
||||
}
|
||||
this.setData({ step: 'method' });
|
||||
wx.showToast({ title: '支付确认超时', icon: 'none' });
|
||||
},
|
||||
|
||||
async notifyPayFail() {
|
||||
try {
|
||||
await request({
|
||||
url: '/yonghu/fkzfsb',
|
||||
method: 'POST',
|
||||
data: { dingdanid: this.data.dingdanid }
|
||||
});
|
||||
} catch (e) {}
|
||||
},
|
||||
|
||||
// ========== 佣金抵扣流程 ==========
|
||||
async fetchBalanceOptions() {
|
||||
this.setData({ step: 'loading', loadingText: '获取可抵扣身份...' });
|
||||
try {
|
||||
const res = await request({
|
||||
url: '/shangpin/czhqdy',
|
||||
method: 'POST',
|
||||
data: {
|
||||
leixing: 4,
|
||||
fadan_id: this.properties.fadan.id,
|
||||
jine: this.properties.fadan.fakuanjine
|
||||
}
|
||||
});
|
||||
if (res.data.code !== 200) throw new Error(res.data.msg || '获取失败');
|
||||
this.setData({ balanceOptions: res.data.data, step: 'balance' });
|
||||
} catch (e) {
|
||||
this.setData({ step: 'method' });
|
||||
wx.showToast({ title: e.message, icon: 'none' });
|
||||
}
|
||||
},
|
||||
|
||||
selectBalance(e) {
|
||||
const id = e.currentTarget.dataset.id;
|
||||
const info = this.data.balanceOptions.find(opt => opt.id === id);
|
||||
if (!info) return;
|
||||
this.setData({ selectedBalance: info, step: 'confirm' });
|
||||
},
|
||||
|
||||
backToMethod() {
|
||||
this.setData({ step: 'method' });
|
||||
},
|
||||
|
||||
async confirmBalance() {
|
||||
if (!this.data.selectedBalance) return;
|
||||
this.setData({ step: 'loading', loadingText: '抵扣扣款中...' });
|
||||
try {
|
||||
const res = await request({
|
||||
url: '/shangpin/dsqrgmdh',
|
||||
method: 'POST',
|
||||
data: {
|
||||
leixing: 4,
|
||||
shenfen_id: this.data.selectedBalance.id,
|
||||
fadan_id: this.properties.fadan.id
|
||||
}
|
||||
});
|
||||
if (res.data.code !== 200) throw new Error(res.data.msg || '抵扣失败');
|
||||
this.triggerEvent('success');
|
||||
this.close();
|
||||
wx.showToast({ title: '缴纳成功', icon: 'success' });
|
||||
} catch (e) {
|
||||
this.setData({ step: 'method' });
|
||||
wx.showToast({ title: e.message, icon: 'none' });
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
3
pages/cfss/components/fakuan-pay/fakuan-pay.json
Normal file
3
pages/cfss/components/fakuan-pay/fakuan-pay.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"component": true
|
||||
}
|
||||
61
pages/cfss/components/fakuan-pay/fakuan-pay.wxml
Normal file
61
pages/cfss/components/fakuan-pay/fakuan-pay.wxml
Normal file
@@ -0,0 +1,61 @@
|
||||
<!-- 支付组件模板 -->
|
||||
<view wx:if="{{ visible }}" class="pay-mask" catchtouchmove="prevent">
|
||||
<!-- 支付方式选择 -->
|
||||
<view wx:if="{{ step === 'method' }}" class="pay-panel">
|
||||
<view class="pay-head">
|
||||
<text class="pay-title">选择支付方式</text>
|
||||
<view class="pay-close" bindtap="close">✕</view>
|
||||
</view>
|
||||
<view class="pay-body">
|
||||
<view class="method-item" bindtap="chooseMethod" data-method="wx">
|
||||
<text class="method-icon">💳</text>
|
||||
<text class="method-name">微信支付</text>
|
||||
</view>
|
||||
<view class="method-item" bindtap="chooseMethod" data-method="balance">
|
||||
<text class="method-icon">💰</text>
|
||||
<text class="method-name">佣金抵扣</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 余额抵扣身份选择 -->
|
||||
<view wx:if="{{ step === 'balance' }}" class="pay-panel">
|
||||
<view class="pay-head">
|
||||
<text class="pay-title">选择抵扣身份</text>
|
||||
<view class="pay-close" bindtap="close">✕</view>
|
||||
</view>
|
||||
<scroll-view scroll-y class="pay-body">
|
||||
<view wx:for="{{ balanceOptions }}" wx:key="id" class="balance-item" bindtap="selectBalance" data-id="{{ item.id }}">
|
||||
<text class="balance-name">{{ item.jieshao }}</text>
|
||||
<text class="balance-amount">需¥{{ item.xuyao }}</text>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
|
||||
<!-- 余额抵扣确认 -->
|
||||
<view wx:if="{{ step === 'confirm' }}" class="pay-panel">
|
||||
<view class="pay-head">
|
||||
<text class="pay-title">确认抵扣</text>
|
||||
<view class="pay-close" bindtap="close">✕</view>
|
||||
</view>
|
||||
<view class="pay-body">
|
||||
<view class="confirm-row">
|
||||
<text>抵扣身份:{{ selectedBalance.jieshao }}</text>
|
||||
</view>
|
||||
<view class="confirm-row">
|
||||
<text>需扣除:¥{{ selectedBalance.xuyao }}</text>
|
||||
</view>
|
||||
<text class="confirm-tip">确认后将从您的佣金/分红中扣除</text>
|
||||
</view>
|
||||
<view class="pay-foot">
|
||||
<view class="btn default" bindtap="backToMethod">取消</view>
|
||||
<view class="btn primary" bindtap="confirmBalance">确认</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 支付加载动画 -->
|
||||
<view wx:if="{{ step === 'loading' }}" class="pay-mask loading-box">
|
||||
<view class="spinner"></view>
|
||||
<text class="loading-text">{{ loadingText }}</text>
|
||||
</view>
|
||||
</view>
|
||||
59
pages/cfss/components/fakuan-pay/fakuan-pay.wxss
Normal file
59
pages/cfss/components/fakuan-pay/fakuan-pay.wxss
Normal file
@@ -0,0 +1,59 @@
|
||||
/* 支付组件样式,与罚款列表统一 */
|
||||
.pay-mask {
|
||||
position: fixed; top: 0; left: 0; right: 0; bottom: 0;
|
||||
background: rgba(0,0,0,0.5); z-index: 2000;
|
||||
display: flex; align-items: flex-end; justify-content: center;
|
||||
}
|
||||
.pay-panel {
|
||||
width: 100%; max-height: 85vh; background: #fff;
|
||||
border-radius: 24rpx 24rpx 0 0; display: flex; flex-direction: column; overflow: hidden;
|
||||
}
|
||||
.pay-head {
|
||||
display: flex; justify-content: space-between; align-items: center;
|
||||
padding: 28rpx 30rpx 16rpx; border-bottom: 1px solid #eee;
|
||||
}
|
||||
.pay-title { font-size: 32rpx; font-weight: 600; }
|
||||
.pay-close {
|
||||
width: 44rpx; height: 44rpx; background: #f5f5f5;
|
||||
border-radius: 50%; display: flex; align-items: center; justify-content: center;
|
||||
font-size: 26rpx; color: #888;
|
||||
}
|
||||
.pay-body { padding: 20rpx 30rpx; flex: 1; overflow-y: auto; }
|
||||
.pay-foot {
|
||||
display: flex; padding: 16rpx 30rpx 30rpx; gap: 20rpx; border-top: 1px solid #eee;
|
||||
}
|
||||
|
||||
.method-item {
|
||||
display: flex; flex-direction: column; align-items: center;
|
||||
padding: 40rpx 30rpx; background: #f9f9f9; border-radius: 20rpx; margin-bottom: 20rpx;
|
||||
}
|
||||
.method-icon { font-size: 48rpx; margin-bottom: 12rpx; }
|
||||
.method-name { font-size: 28rpx; color: #333; }
|
||||
|
||||
.balance-item {
|
||||
display: flex; justify-content: space-between; align-items: center;
|
||||
padding: 24rpx 0; border-bottom: 1rpx solid #f0f0f0;
|
||||
}
|
||||
.balance-name { font-size: 28rpx; color: #333; }
|
||||
.balance-amount { font-size: 28rpx; color: #E53935; }
|
||||
|
||||
.confirm-row { font-size: 28rpx; margin-bottom: 16rpx; }
|
||||
.confirm-tip { font-size: 24rpx; color: #888; display: block; margin-top: 20rpx; }
|
||||
|
||||
.loading-box {
|
||||
display: flex; flex-direction: column; align-items: center; justify-content: center;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
.spinner {
|
||||
width: 60rpx; height: 60rpx;
|
||||
border: 4rpx solid rgba(255,255,255,0.3);
|
||||
border-top-color: #fff; border-radius: 50%;
|
||||
animation: spin 1s linear infinite; margin-bottom: 20rpx;
|
||||
}
|
||||
@keyframes spin { to { transform: rotate(360deg); } }
|
||||
.loading-text { color: #fff; font-size: 26rpx; }
|
||||
|
||||
.btn { flex: 1; text-align: center; padding: 22rpx 0; border-radius: 16rpx; font-size: 28rpx; font-weight: 600; }
|
||||
.primary { background: #2e7d32; color: #fff; }
|
||||
.default { background: #f0f0f0; color: #555; }
|
||||
.btn:active { opacity: 0.8; }
|
||||
220
pages/cfss/components/jifen-list/jifen-list.js
Normal file
220
pages/cfss/components/jifen-list/jifen-list.js
Normal file
@@ -0,0 +1,220 @@
|
||||
// components/jifen-list/jifen-list.js
|
||||
import request from '../../../../utils/request.js';
|
||||
const app = getApp();
|
||||
const MEIYE_TIAOSHU = 5;
|
||||
|
||||
Component({
|
||||
properties: {
|
||||
status: { type: Number, value: 0, observer: 'reload' },
|
||||
trigger: { type: Number, value: 0, observer: 'reload' }
|
||||
},
|
||||
|
||||
data: {
|
||||
chufaList: [],
|
||||
dangqianye: 1,
|
||||
haiyougengduo: true,
|
||||
jiazhaozhong: false,
|
||||
jiazhaigengduo: false,
|
||||
ossImageUrl: app.globalData.ossImageUrl || '',
|
||||
|
||||
showXiangqing: false,
|
||||
xuanzhongChufa: {},
|
||||
|
||||
showShensuModal: false,
|
||||
shensuLiyou: '',
|
||||
shensuTupian: [],
|
||||
shangchuanJindu: 0,
|
||||
shangchuanZongshu: 0,
|
||||
jinduWidth: '0%',
|
||||
isShangchuanzhong: false,
|
||||
fangdouTimer: null
|
||||
},
|
||||
|
||||
lifetimes: {
|
||||
attached() { this.reload(); },
|
||||
detached() { if (this.data.fangdouTimer) clearTimeout(this.data.fangdouTimer); }
|
||||
},
|
||||
|
||||
methods: {
|
||||
reload() {
|
||||
this.setData({ dangqianye: 1, chufaList: [], haiyougengduo: true });
|
||||
this.loadJifenList();
|
||||
},
|
||||
|
||||
async loadJifenList(isLoadMore = false) {
|
||||
if (this.data.jiazhaozhong || this.data.jiazhaigengduo) return;
|
||||
if (isLoadMore && !this.data.haiyougengduo) return;
|
||||
if (isLoadMore) this.setData({ jiazhaigengduo: true });
|
||||
else this.setData({ jiazhaozhong: true });
|
||||
|
||||
try {
|
||||
const params = { page: this.data.dangqianye, page_size: MEIYE_TIAOSHU, zhuangtai: this.data.status };
|
||||
const res = await request({ url: '/yonghu/dshqcfjl', method: 'POST', data: params });
|
||||
if (res.statusCode === 200 && res.data.code === 0) {
|
||||
const data = res.data.data;
|
||||
if (data.list && Array.isArray(data.list)) {
|
||||
const processedList = data.list.map(item => {
|
||||
let displayTime = '--';
|
||||
if (item.create_time) displayTime = item.create_time;
|
||||
let displayStatus = '未知状态', statusClass = 'zhuangtai-weizhi';
|
||||
const n = Number(item.sqzhuangtai);
|
||||
if (!isNaN(n)) {
|
||||
if (n === 0) { displayStatus = '待处理'; statusClass = 'zhuangtai-daichuli'; }
|
||||
else if (n === 1) { displayStatus = '已处罚'; statusClass = 'zhuangtai-yichufa'; }
|
||||
else if (n === 2) { displayStatus = '处罚已撤销'; statusClass = 'zhuangtai-yibohui'; }
|
||||
else if (n === 3) { displayStatus = '申诉中'; statusClass = 'zhuangtai-shensuzhong'; }
|
||||
}
|
||||
return {
|
||||
...item,
|
||||
display_time: displayTime, display_status: displayStatus, status_class: statusClass,
|
||||
full_zhengju_tupian: (item.zhengju_tupian || []).map(url => this.getFullImageUrl(url)),
|
||||
full_shensu_tupian: (item.shensu_tupian || []).map(url => this.getFullImageUrl(url))
|
||||
};
|
||||
});
|
||||
const newList = isLoadMore ? [...this.data.chufaList, ...processedList] : processedList;
|
||||
this.setData({ chufaList: newList, haiyougengduo: data.has_more === true, dangqianye: this.data.dangqianye + 1 });
|
||||
} else this.setData({ haiyougengduo: false });
|
||||
}
|
||||
} catch (e) { wx.showToast({ title: '加载失败', icon: 'none' }); this.setData({ haiyougengduo: false }); }
|
||||
finally { if (isLoadMore) this.setData({ jiazhaigengduo: false }); else this.setData({ jiazhaozhong: false }); }
|
||||
},
|
||||
|
||||
onReachBottom() {
|
||||
if (this.data.fangdouTimer) clearTimeout(this.data.fangdouTimer);
|
||||
const timer = setTimeout(() => {
|
||||
if (this.data.jiazhaozhong || this.data.jiazhaigengduo) return;
|
||||
if (!this.data.haiyougengduo) return;
|
||||
this.loadJifenList(true);
|
||||
}, 300);
|
||||
this.setData({ fangdouTimer: timer });
|
||||
},
|
||||
|
||||
chakanXiangqing(e) { this.setData({ showXiangqing: true, xuanzhongChufa: e.currentTarget.dataset.item }); },
|
||||
guanbiXiangqing() { this.setData({ showXiangqing: false, xuanzhongChufa: {} }); },
|
||||
|
||||
openShensuModal() {
|
||||
const { xuanzhongChufa } = this.data;
|
||||
if (xuanzhongChufa.ssliyou || (xuanzhongChufa.shensu_tupian && xuanzhongChufa.shensu_tupian.length > 0)) {
|
||||
wx.showToast({ title: '您已经提交过申诉,请等待处理结果', icon: 'none' }); return;
|
||||
}
|
||||
this.setData({ showShensuModal: true, shensuLiyou: '', shensuTupian: [], shangchuanJindu: 0, shangchuanZongshu: 0, jinduWidth: '0%', isShangchuanzhong: false });
|
||||
},
|
||||
closeShensuModal() { this.setData({ showShensuModal: false }); },
|
||||
|
||||
onShensuLiyouInput(e) { this.setData({ shensuLiyou: e.detail.value.slice(0, 500) }); },
|
||||
|
||||
chooseShensuTupian() {
|
||||
if (this.data.isShangchuanzhong) return;
|
||||
const shengyu = 9 - this.data.shensuTupian.length;
|
||||
if (shengyu <= 0) { wx.showToast({ title: '最多只能上传9张图片', icon: 'none' }); return; }
|
||||
wx.chooseMedia({ count: shengyu, mediaType: ['image'], sourceType: ['album', 'camera'], success: (res) => {
|
||||
const newTupian = [...this.data.shensuTupian, ...res.tempFiles.map(f => f.tempFilePath)];
|
||||
this.setData({ shensuTupian: newTupian.slice(0, 9) });
|
||||
}});
|
||||
},
|
||||
deleteShensuTupian(e) { const arr = [...this.data.shensuTupian]; arr.splice(e.currentTarget.dataset.index, 1); this.setData({ shensuTupian: arr }); },
|
||||
yulanShensuTupian(e) {
|
||||
const idx = e.currentTarget.dataset.index;
|
||||
const urls = this.data.shensuTupian;
|
||||
if (!urls[idx]) return;
|
||||
wx.previewImage({ current: urls[idx], urls });
|
||||
},
|
||||
yulanTupian(e) {
|
||||
const current = e.currentTarget.dataset.url;
|
||||
const urls = e.currentTarget.dataset.urls || [];
|
||||
const fullCurrent = current.startsWith('http') ? current : this.getFullImageUrl(current);
|
||||
const fullUrls = urls.map(url => url.startsWith('http') ? url : this.getFullImageUrl(url));
|
||||
wx.previewImage({ current: fullCurrent, urls: fullUrls.length ? fullUrls : [fullCurrent] });
|
||||
},
|
||||
|
||||
/* ========== 图片上传(完全照抄原 cfss.js) ========== */
|
||||
// 【仅替换此方法,其余代码一个字别动】
|
||||
getFullImageUrl(relativeUrl) {
|
||||
if (!relativeUrl) return '';
|
||||
if (relativeUrl.startsWith('http')) return relativeUrl;
|
||||
const ossUrl = this.data.ossImageUrl;
|
||||
if (!ossUrl) return relativeUrl;
|
||||
// 确保拼接时只有一个斜杠
|
||||
const base = ossUrl.replace(/\/+$/, '');
|
||||
const rel = relativeUrl.replace(/^\/+/, '');
|
||||
return base + '/' + rel;
|
||||
},
|
||||
getFileExtension(filePath) {
|
||||
const lastDot = filePath.lastIndexOf('.'); if (lastDot === -1) return '.jpg';
|
||||
const ext = filePath.substring(lastDot).toLowerCase();
|
||||
return ['.jpg','.jpeg','.png','.gif','.bmp','.webp'].includes(ext) ? ext : '.jpg';
|
||||
},
|
||||
async getCOSZhengshu() {
|
||||
const res = await request({ url: '/dingdan/dsscpz', method: 'POST', data: { dingdan_id: this.data.xuanzhongChufa.dingdan_id || '', yongtu: 'chufa' } });
|
||||
if (res.statusCode === 200 && res.data.code === 0 && res.data.data) return res.data.data;
|
||||
throw new Error(res?.data?.msg || '获取上传凭证失败');
|
||||
},
|
||||
initCOSClient(tokenData) {
|
||||
const COS = require('../../../../utils/cos-wx-sdk-v5.js');
|
||||
const credentials = tokenData.credentials || tokenData;
|
||||
const bucket = tokenData.bucket || 'julebu-1361527063';
|
||||
const region = tokenData.region || 'ap-shanghai';
|
||||
const cos = new COS({ SimpleUploadMethod: 'putObject', getAuthorization: (options, callback) => { callback({ TmpSecretId: credentials.tmpSecretId, TmpSecretKey: credentials.tmpSecretKey, SecurityToken: credentials.sessionToken || '', StartTime: tokenData.startTime || Math.floor(Date.now() / 1000), ExpiredTime: tokenData.expiredTime || (Math.floor(Date.now() / 1000) + 7200) }); } });
|
||||
return { cos, bucket, region };
|
||||
},
|
||||
shangchuanDanZhangTupian(filePath, cosKey, cosClient, bucket, region) {
|
||||
return new Promise(resolve => {
|
||||
const timeoutId = setTimeout(() => resolve({ status: 'optimistic_success', key: cosKey }), 2000);
|
||||
cosClient.uploadFile({ Bucket: bucket, Region: region, Key: cosKey, FilePath: filePath, onProgress: (info) => { if (Math.round(info.percent * 100) === 100) { clearTimeout(timeoutId); resolve({ status: 'optimistic_success', key: cosKey }); } } });
|
||||
});
|
||||
},
|
||||
async piliangShangchuanShensuTupian() {
|
||||
const total = this.data.shensuTupian.length;
|
||||
if (total === 0) return [];
|
||||
this.setData({ shangchuanZongshu: total, shangchuanJindu: 0, jinduWidth: '0%', isShangchuanzhong: true });
|
||||
|
||||
let yonghuid = '';
|
||||
const uid = wx.getStorageSync('uid'); if (uid) yonghuid = String(uid).padStart(7, '0');
|
||||
else if (app.globalData.userInfo?.yonghuid) yonghuid = String(app.globalData.userInfo.yonghuid).padStart(7, '0');
|
||||
if (!yonghuid) { wx.showToast({ title: '请先登录', icon: 'none' }); this.setData({ isShangchuanzhong: false }); return []; }
|
||||
|
||||
const preGeneratedUrls = [];
|
||||
for (let i = 0; i < total; i++) {
|
||||
const timestamp = Date.now() + i, random = Math.floor(Math.random() * 10000);
|
||||
preGeneratedUrls.push(`a_long/chfajltp/dssstp/${yonghuid}_${timestamp}_${random}${this.getFileExtension(this.data.shensuTupian[i])}`);
|
||||
}
|
||||
|
||||
let cosClient, bucket, region;
|
||||
try { const tokenData = await this.getCOSZhengshu(); const c = this.initCOSClient(tokenData); cosClient = c.cos; bucket = c.bucket; region = c.region; }
|
||||
catch (e) { wx.showToast({ title: '上传初始化失败', icon: 'none' }); this.setData({ isShangchuanzhong: false }); return []; }
|
||||
|
||||
for (let i = 0; i < total; i++) {
|
||||
await this.shangchuanDanZhangTupian(this.data.shensuTupian[i], preGeneratedUrls[i], cosClient, bucket, region);
|
||||
const done = i + 1;
|
||||
this.setData({ shangchuanJindu: done, jinduWidth: `${((done / total) * 100).toFixed(0)}%` });
|
||||
}
|
||||
this.setData({ isShangchuanzhong: false });
|
||||
return preGeneratedUrls;
|
||||
},
|
||||
|
||||
async submitShensu() {
|
||||
if (!this.data.shensuLiyou.trim()) { wx.showToast({ title: '请输入申诉理由', icon: 'none' }); return; }
|
||||
if (this.data.shensuTupian.length === 0) { wx.showModal({ title: '提示', content: '未上传任何图片,确定要提交申诉吗?', success: async (res) => { if (res.confirm) await this.tijiaoShensuData([]); } }); return; }
|
||||
wx.showToast({ title: '开始上传图片...', icon: 'none' });
|
||||
try {
|
||||
const urls = await this.piliangShangchuanShensuTupian();
|
||||
if (!urls || urls.length === 0) throw new Error('图片上传失败');
|
||||
await this.tijiaoShensuData(urls);
|
||||
} catch (e) { wx.showToast({ title: e.message || '提交失败', icon: 'none' }); }
|
||||
},
|
||||
async tijiaoShensuData(tupianUrls) {
|
||||
wx.showToast({ title: '提交申诉中...', icon: 'none' });
|
||||
try {
|
||||
const res = await request({ url: '/yonghu/dscfss', method: 'POST', data: { chufa_id: this.data.xuanzhongChufa.id, shensu_liyou: this.data.shensuLiyou, shensu_tupian_urls: tupianUrls } });
|
||||
if (res.statusCode === 200 && res.data.code === 0) {
|
||||
const newList = this.data.chufaList.map(item => {
|
||||
if (item.id === this.data.xuanzhongChufa.id) { return { ...item, sqzhuangtai: 3, ssliyou: this.data.shensuLiyou, display_status: '申诉中', status_class: 'zhuangtai-shensuzhong', full_shensu_tupian: tupianUrls.map(url => this.getFullImageUrl(url)) }; }
|
||||
return item;
|
||||
});
|
||||
this.setData({ chufaList: newList, showShensuModal: false, showXiangqing: false });
|
||||
wx.showToast({ title: '申诉提交成功,请等待处理', icon: 'success' });
|
||||
} else throw new Error(res.data?.msg || '提交申诉失败');
|
||||
} catch (e) { wx.showToast({ title: e.message || '提交失败', icon: 'none' }); }
|
||||
}
|
||||
}
|
||||
});
|
||||
3
pages/cfss/components/jifen-list/jifen-list.json
Normal file
3
pages/cfss/components/jifen-list/jifen-list.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"component": true
|
||||
}
|
||||
105
pages/cfss/components/jifen-list/jifen-list.wxml
Normal file
105
pages/cfss/components/jifen-list/jifen-list.wxml
Normal file
@@ -0,0 +1,105 @@
|
||||
<view class="container">
|
||||
<scroll-view scroll-y class="scroll" bindscrolltolower="onReachBottom">
|
||||
<view wx:if="{{ jiazhaozhong && chufaList.length === 0 }}" class="tip">加载中...</view>
|
||||
<block wx:for="{{ chufaList }}" wx:key="id">
|
||||
<view class="card" bindtap="chakanXiangqing" data-item="{{ item }}">
|
||||
<view class="card-head">
|
||||
<text class="time">{{ item.display_time }}</text>
|
||||
<text class="tag {{ item.status_class }}">{{ item.display_status }}</text>
|
||||
</view>
|
||||
<text class="info">申请人:{{ item.qingqiuid || '--' }}</text>
|
||||
<text class="info">理由:{{ item.cfliyou || '无' }}</text>
|
||||
<text class="info" wx:if="{{ item.dingdan_id }}">订单:{{ item.dingdan_id }}</text>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<!-- ========== 底部:按钮 / 加载中 / 没有更多 ========== -->
|
||||
<view wx:if="{{ haiyougengduo && !jiazhaigengduo }}" class="load-more-btn" bindtap="onReachBottom">
|
||||
<text>点击获取更多</text>
|
||||
</view>
|
||||
<view wx:if="{{ jiazhaigengduo }}" class="tip">加载中...</view>
|
||||
<view wx:if="{{ !haiyougengduo && chufaList.length > 0 }}" class="tip">—— 没有更多了 ——</view>
|
||||
<view wx:if="{{ !jiazhaozhong && chufaList.length === 0 }}" class="empty">暂无积分处罚记录</view>
|
||||
</scroll-view>
|
||||
|
||||
<!-- 详情弹窗(不动) -->
|
||||
<view wx:if="{{ showXiangqing }}" class="modal-mask">
|
||||
<view class="modal-panel">
|
||||
<view class="modal-head">
|
||||
<text class="modal-title">处罚详情</text>
|
||||
<view class="modal-close" bindtap="guanbiXiangqing">✕</view>
|
||||
</view>
|
||||
<scroll-view scroll-y class="modal-body">
|
||||
<view class="row"><text class="lbl">订单编号</text><text class="val">{{ xuanzhongChufa.dingdan_id || '--' }}</text></view>
|
||||
<view class="row"><text class="lbl">申请人ID</text><text class="val">{{ xuanzhongChufa.qingqiuid || '--' }}</text></view>
|
||||
<view class="row"><text class="lbl">时间</text><text class="val">{{ xuanzhongChufa.display_time }}</text></view>
|
||||
<view class="row"><text class="lbl">状态</text><text class="val"><text class="tag {{ xuanzhongChufa.status_class }}">{{ xuanzhongChufa.display_status }}</text></text></view>
|
||||
<view class="row"><text class="lbl">处罚理由</text><text class="val">{{ xuanzhongChufa.cfliyou || '无' }}</text></view>
|
||||
|
||||
<view wx:if="{{ xuanzhongChufa.full_zhengju_tupian.length }}" class="section">
|
||||
<text class="sec-title">证据图片</text>
|
||||
<view class="img-grid">
|
||||
<block wx:for="{{ xuanzhongChufa.full_zhengju_tupian }}" wx:key="index">
|
||||
<view class="img-box" bindtap="yulanTupian" data-url="{{ item }}" data-urls="{{ xuanzhongChufa.full_zhengju_tupian }}">
|
||||
<image class="img" src="{{ item }}" mode="aspectFill" />
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view wx:if="{{ xuanzhongChufa.ssliyou || xuanzhongChufa.full_shensu_tupian.length }}" class="section">
|
||||
<text class="sec-title">我的申诉</text>
|
||||
<view wx:if="{{ xuanzhongChufa.ssliyou }}" class="row"><text class="lbl">理由</text><text class="val">{{ xuanzhongChufa.ssliyou }}</text></view>
|
||||
<view wx:if="{{ xuanzhongChufa.full_shensu_tupian.length }}" class="img-grid">
|
||||
<block wx:for="{{ xuanzhongChufa.full_shensu_tupian }}" wx:key="index">
|
||||
<view class="img-box" bindtap="yulanTupian" data-url="{{ item }}" data-urls="{{ xuanzhongChufa.full_shensu_tupian }}">
|
||||
<image class="img" src="{{ item }}" mode="aspectFill" />
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
<view class="modal-foot">
|
||||
<view wx:if="{{ xuanzhongChufa.sqzhuangtai === 0 }}" class="btn primary" bindtap="openShensuModal">申诉</view>
|
||||
<view class="btn default" bindtap="guanbiXiangqing">关闭</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 申诉弹窗(不动) -->
|
||||
<view wx:if="{{ showShensuModal }}" class="modal-mask">
|
||||
<view class="modal-panel">
|
||||
<view class="modal-head">
|
||||
<text class="modal-title">提交申诉</text>
|
||||
<view class="modal-close" bindtap="closeShensuModal">✕</view>
|
||||
</view>
|
||||
<scroll-view scroll-y class="modal-body">
|
||||
<view class="form-group">
|
||||
<text class="sec-title">申诉理由</text>
|
||||
<textarea class="textarea" placeholder="请输入申诉理由(最多500字)" value="{{ shensuLiyou }}" bindinput="onShensuLiyouInput" maxlength="500" />
|
||||
<text class="word-count">{{ shensuLiyou.length }}/500</text>
|
||||
</view>
|
||||
<view class="form-group">
|
||||
<text class="sec-title">上传凭证(最多9张)</text>
|
||||
<view class="img-grid">
|
||||
<block wx:for="{{ shensuTupian }}" wx:key="index">
|
||||
<view class="img-box">
|
||||
<image class="img" src="{{ item }}" mode="aspectFill" bindtap="yulanShensuTupian" data-index="{{ index }}" />
|
||||
<view class="img-del" catchtap="deleteShensuTupian" data-index="{{ index }}">✕</view>
|
||||
</view>
|
||||
</block>
|
||||
<view wx:if="{{ shensuTupian.length < 9 }}" class="img-add" bindtap="chooseShensuTupian">+</view>
|
||||
</view>
|
||||
<view wx:if="{{ shangchuanZongshu > 0 }}" class="progress-box">
|
||||
<view class="progress-bar"><view class="progress-inner" style="width: {{ jinduWidth }}"></view></view>
|
||||
<text class="progress-text">{{ shangchuanJindu }}/{{ shangchuanZongshu }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
<view class="modal-foot">
|
||||
<view class="btn default" bindtap="closeShensuModal">取消</view>
|
||||
<view class="btn primary" bindtap="submitShensu">提交申诉</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
86
pages/cfss/components/jifen-list/jifen-list.wxss
Normal file
86
pages/cfss/components/jifen-list/jifen-list.wxss
Normal file
@@ -0,0 +1,86 @@
|
||||
/* 页面与容器 */
|
||||
page { background: #f5f5f5; }
|
||||
|
||||
/* 🔥 组件自身必须占满宽度,且 padding 左右严格相等 */
|
||||
.container { width: 100%; box-sizing: border-box; display: flex; flex-direction: column; }
|
||||
|
||||
/* 🔥 滚动区 padding 左右必须相等,且容器用 box-sizing */
|
||||
.scroll { width: 100%; box-sizing: border-box; padding: 20rpx 24rpx; }
|
||||
|
||||
/* 提示文字 */
|
||||
.tip { text-align: center; color: #999; font-size: 24rpx; padding: 30rpx 0; }
|
||||
.empty { display: flex; flex-direction: column; align-items: center; padding-top: 200rpx; color: #999; font-size: 28rpx; }
|
||||
|
||||
/* 🔥 卡片 width:100% + box-sizing 使左右边距完全由父容器决定 */
|
||||
.card { width: 100%; box-sizing: border-box; background: #fff; border-radius: 16rpx; padding: 24rpx; margin-bottom: 16rpx; }
|
||||
.card-head { display: flex; justify-content: space-between; align-items: center; margin-bottom: 12rpx; }
|
||||
.time { font-size: 24rpx; color: #999; }
|
||||
.tag { font-size: 20rpx; padding: 2rpx 14rpx; border-radius: 16rpx; display: inline-block; }
|
||||
.zhuangtai-daichuli { background: #fff3e0; color: #e65100; }
|
||||
.zhuangtai-yichufa { background: #e8f5e9; color: #2e7d32; }
|
||||
.zhuangtai-yibohui { background: #f3e5f5; color: #7b1fa2; }
|
||||
.zhuangtai-shensuzhong { background: #e3f2fd; color: #1565c0; }
|
||||
.zhuangtai-weizhi { background: #f0f0f0; color: #888; }
|
||||
.info { display: block; font-size: 26rpx; color: #333; margin-top: 8rpx; }
|
||||
|
||||
/* 弹窗遮罩 */
|
||||
.modal-mask { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.5); z-index: 999; display: flex; align-items: flex-end; justify-content: center; }
|
||||
|
||||
/* 🔥 弹窗面板 width:100% + box-sizing */
|
||||
.modal-panel { width: 100%; box-sizing: border-box; max-height: 85vh; background: #fff; border-radius: 24rpx 24rpx 0 0; display: flex; flex-direction: column; overflow: hidden; }
|
||||
.modal-head { display: flex; justify-content: space-between; align-items: center; padding: 28rpx 30rpx 16rpx; border-bottom: 1px solid #eee; }
|
||||
.modal-title { font-size: 32rpx; font-weight: 600; }
|
||||
.modal-close { width: 44rpx; height: 44rpx; border-radius: 50%; background: #f5f5f5; display: flex; align-items: center; justify-content: center; font-size: 26rpx; color: #888; }
|
||||
|
||||
/* 🔥 内容区左右 padding 严格相等 */
|
||||
.modal-body { width: 100%; box-sizing: border-box; padding: 20rpx 30rpx; overflow-y: auto; }
|
||||
.row { display: flex; margin-bottom: 16rpx; }
|
||||
.lbl { width: 150rpx; font-size: 26rpx; color: #888; flex-shrink: 0; }
|
||||
.val { flex: 1; font-size: 26rpx; color: #333; word-break: break-all; }
|
||||
.section { margin: 20rpx 0; }
|
||||
.sec-title { font-size: 26rpx; color: #888; margin-bottom: 12rpx; display: block; }
|
||||
|
||||
/* 🔥 图片网格:gap 均等,无任何额外水平偏移 */
|
||||
.img-grid { width: 100%; box-sizing: border-box; display: flex; flex-wrap: wrap; gap: 12rpx; }
|
||||
.img { width: calc((100% - 24rpx) / 3); height: 0; padding-bottom: calc((100% - 24rpx) / 3); border-radius: 10rpx; background: #f0f0f0; display: block; position: relative; }
|
||||
.img-box { position: relative; width: calc((100% - 24rpx) / 3); height: 0; padding-bottom: calc((100% - 24rpx) / 3); }
|
||||
.img-box .img { position: absolute; top: 0; left: 0; width: 100%; height: 100%; padding-bottom: 0; }
|
||||
.img-del { position: absolute; top: -6rpx; right: -6rpx; width: 34rpx; height: 34rpx; background: #e53935; border-radius: 50%; display: flex; align-items: center; justify-content: center; color: #fff; font-size: 18rpx; z-index: 2; }
|
||||
.img-add { width: calc((100% - 24rpx) / 3); height: 0; padding-bottom: calc((100% - 24rpx) / 3); border: 2rpx dashed #ccc; border-radius: 10rpx; display: flex; align-items: center; justify-content: center; font-size: 52rpx; color: #aaa; box-sizing: border-box; position: relative; }
|
||||
|
||||
/* 表单 */
|
||||
.form-group { margin-bottom: 24rpx; }
|
||||
.textarea { width: 100%; box-sizing: border-box; height: 180rpx; background: #f9f9f9; border-radius: 10rpx; padding: 16rpx; font-size: 26rpx; border: 1px solid #eee; }
|
||||
.word-count { text-align: right; font-size: 22rpx; color: #bbb; margin-top: 6rpx; }
|
||||
.progress-box { margin-top: 12rpx; }
|
||||
.progress-bar { height: 10rpx; background: #eee; border-radius: 5rpx; }
|
||||
.progress-inner { height: 100%; background: #2e7d32; border-radius: 5rpx; }
|
||||
.progress-text { font-size: 22rpx; color: #2e7d32; text-align: right; margin-top: 4rpx; }
|
||||
|
||||
/* 🔥 底部按钮区左右 padding 严格均等 */
|
||||
.modal-foot { width: 100%; box-sizing: border-box; display: flex; padding: 16rpx 30rpx 30rpx; gap: 20rpx; border-top: 1px solid #eee; }
|
||||
.btn { flex: 1; text-align: center; padding: 22rpx 0; border-radius: 16rpx; font-size: 28rpx; font-weight: 600; }
|
||||
.primary { background: #2e7d32; color: #fff; }
|
||||
.default { background: #f0f0f0; color: #555; }
|
||||
.btn:active { opacity: 0.8; }
|
||||
|
||||
|
||||
/* ===== 获取更多按钮 ===== */
|
||||
.load-more-btn {
|
||||
width: 80%;
|
||||
margin: 20rpx auto 30rpx;
|
||||
padding: 22rpx 0;
|
||||
text-align: center;
|
||||
background: #ffffff;
|
||||
border: 2rpx solid #e0e0e0;
|
||||
border-radius: 20rpx;
|
||||
color: #555;
|
||||
font-size: 26rpx;
|
||||
box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.02);
|
||||
transition: all 0.15s;
|
||||
}
|
||||
.load-more-btn:active {
|
||||
background: #f5f5f5;
|
||||
border-color: #2e7d32;
|
||||
color: #2e7d32;
|
||||
}
|
||||
Reference in New Issue
Block a user