// pages/penalty/penalty.js import request from '../../utils/request.js' const app = getApp() const MEIYE_TIAOSHU = 5 Page({ data: { zongshu: 0, daichuli: 0, yichuli: 0, shenfen: 0, chufaList: [], dangqianye: 1, haiyougengduo: true, jiazhaozhong: false, jiazhaigengduo: false, scrollHeight: 0, showXiangqing: false, xuanzhongChufa: {}, showShensuModal: false, shensuLiyou: '', shensuTupian: [], shensuTupianUrls: [], shangchuanJindu: 0, shangchuanZongshu: 0, jinduWidth: '0%', isShangchuanzhong: false, ossImageUrl: '', fangdouTimer: null }, onLoad(options) { this.registerNotificationComponent() this.initOSSUrl() this.jisuanGaodu() this.chushihuaShuju() }, onShow() { this.registerNotificationComponent() }, onUnload() { if (this.data.fangdouTimer) clearTimeout(this.data.fangdouTimer) }, onReachBottom() { this.shanglaShuaxin() }, registerNotificationComponent() { const notificationComp = this.selectComponent('#global-notification') if (notificationComp && notificationComp.showNotification) { app.globalData.globalNotification = { show: (data) => notificationComp.showNotification(data), hide: () => notificationComp.hideNotification() } } }, initOSSUrl() { const ossImageUrl = app.globalData.ossImageUrl || '' this.setData({ ossImageUrl }) }, jisuanGaodu() { const systemInfo = wx.getSystemInfoSync() const windowHeight = systemInfo.windowHeight const height = windowHeight - 200 - 100 - 40 this.setData({ scrollHeight: height > 0 ? height : 400 }) }, chushihuaShuju() { this.setData({ dangqianye: 1, chufaList: [], haiyougengduo: true }) this.jiazhuoquTongji() this.jiazhuoquChufaList() }, async jiazhuoquTongji() { try { const res = await request({ url: '/yonghu/dshqcfjl', method: 'POST', data: { qingqiu_tongji: true } }) if (res.statusCode === 200 && res.data.code === 0) { const data = res.data.data this.setData({ zongshu: data.zongshu || 0, daichuli: data.daichuli || 0, yichuli: data.yichuli || 0 }) } } catch (error) { console.error('获取统计信息失败:', error) } }, async jiazhuoquChufaList(isLoadMore = false) { if (this.data.jiazhaozhong || this.data.jiazhaigengduo) return if (isLoadMore) { if (!this.data.haiyougengduo) return this.setData({ jiazhaigengduo: true }) } else { this.setData({ jiazhaozhong: true }) } try { const params = { page: this.data.dangqianye, page_size: MEIYE_TIAOSHU } if (this.data.shenfen === 0) { params.zhuangtai = 0 } else { params.zhuangtai = 1 } const res = await request({ url: '/yonghu/dshqcfjl', method: 'POST', data: params }) //console.log('🔴【后端返回的数据】:', res.data) 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) { if (typeof item.create_time === 'number') { displayTime = String(item.create_time) } else if (typeof item.create_time === 'string') { displayTime = item.create_time } else { displayTime = String(item.create_time) } } // 处理显示状态 let displayStatus = '未知状态' let statusClass = 'zhuangtai-weizhi' const statusNum = Number(item.sqzhuangtai) if (!isNaN(statusNum)) { if (statusNum === 0) { displayStatus = '待处罚' statusClass = 'zhuangtai-daichuli' } else if (statusNum === 1) { displayStatus = '已处罚' statusClass = 'zhuangtai-yichufa' } else if (statusNum === 2) { displayStatus = '已驳回' statusClass = 'zhuangtai-yibohui' } else if (statusNum === 3) { displayStatus = '申诉中' statusClass = 'zhuangtai-shensuzhong' } } // 处理图片URL(在数据加载时就拼接完整URL) const fullZhengjuTupian = (item.zhengju_tupian || []).map(url => this.getFullImageUrl(url)) const fullShensuTupian = (item.shensu_tupian || []).map(url => this.getFullImageUrl(url)) return { ...item, display_time: displayTime, // 🔴 处理好的显示时间 display_status: displayStatus, // 🔴 处理好的显示状态 status_class: statusClass, // 🔴 处理好的状态样式类 zhengju_tupian: item.zhengju_tupian || [], shensu_tupian: item.shensu_tupian || [], full_zhengju_tupian: fullZhengjuTupian, // 🔴 完整图片URL full_shensu_tupian: fullShensuTupian // 🔴 完整图片URL } }) //console.log('🔴【处理后的数据】:', processedList) let newList if (isLoadMore) { newList = [...this.data.chufaList, ...processedList] } else { newList = processedList } const hasMore = data.has_more === true this.setData({ chufaList: newList, haiyougengduo: hasMore, dangqianye: this.data.dangqianye + 1 }) } else { this.setData({ haiyougengduo: false }) } } else { throw new Error(res.data?.msg || '加载处罚记录失败') } } catch (error) { console.error('加载处罚记录失败:', error) wx.showToast({ title: error.message || '加载失败', icon: 'none' }) this.setData({ haiyougengduo: false }) } finally { if (isLoadMore) { this.setData({ jiazhaigengduo: false }) } else { this.setData({ jiazhaozhong: false }) } } }, qiehuanShenfen(e) { const type = parseInt(e.currentTarget.dataset.type) if (this.data.shenfen === type) return this.setData({ shenfen: type, dangqianye: 1, chufaList: [], haiyougengduo: true }) this.jiazhuoquChufaList() }, shanglaShuaxin() { 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.jiazhuoquChufaList(true) }, 300) this.setData({ fangdouTimer: timer }) }, chakanXiangqing(e) { const item = e.currentTarget.dataset.item this.setData({ xuanzhongChufa: item, showXiangqing: true }) }, guanbiXiangqing() { this.setData({ showXiangqing: false, xuanzhongChufa: {} }) }, openShensuModal() { if (this.data.xuanzhongChufa.ssliyou || (this.data.xuanzhongChufa.shensu_tupian && this.data.xuanzhongChufa.shensu_tupian.length > 0)) { wx.showToast({ title: '您已经提交过申诉,请等待处理结果', icon: 'none' }) return } this.setData({ showShensuModal: true, shensuLiyou: '', shensuTupian: [], shensuTupianUrls: [], shangchuanJindu: 0, shangchuanZongshu: 0, jinduWidth: '0%' }) }, closeShensuModal() { this.setData({ showShensuModal: false, shensuLiyou: '', shensuTupian: [], shensuTupianUrls: [] }) }, onShensuLiyouInput(e) { const value = e.detail.value.slice(0, 500) this.setData({ shensuLiyou: value }) }, 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(file => file.tempFilePath)] this.setData({ shensuTupian: newTupian.slice(0, 9) }) } }) }, deleteShensuTupian(e) { const index = e.currentTarget.dataset.index const newTupian = [...this.data.shensuTupian] newTupian.splice(index, 1) this.setData({ shensuTupian: newTupian }) }, yulanShensuTupian(e) { const index = e.currentTarget.dataset.index const urls = this.data.shensuTupian if (!urls[index]) return wx.previewImage({ current: urls[index], urls: urls }) }, lianxiKefu() { const kefuLink = app.globalData.kefuConfig?.link || ''; const corpId = app.globalData.kefuConfig?.enterpriseId || ''; if (!kefuLink || !corpId) { wx.showToast({ title: '客服配置未加载', icon: 'none' }); return; } wx.openCustomerServiceChat({ extInfo: { url: kefuLink }, corpId: corpId, success: (res) => { console.log('跳转客服成功', res); }, fail: (err) => { console.error('跳转客服失败', err); wx.showToast({ title: '暂时无法连接客服', icon: 'none' }); } }); }, async getCOSZhengshu() { try { 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 } else { throw new Error(res?.data?.msg || '获取上传凭证失败') } } catch (error) { console.error('获取COS凭证失败:', error) throw error } }, initCOSClient(tokenData) { const COS = require('../../utils/cos-wx-sdk-v5.min.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: async (options, callback) => { const authParams = { 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) } callback(authParams) } }) return { cos, bucket, region } }, async shangchuanDanZhangTupian(filePath, cosKey, index, cosInstance, bucket, region) { return new Promise((resolve) => { const timeoutId = setTimeout(() => { resolve({ status: 'optimistic_success', key: cosKey }) }, 2000) cosInstance.uploadFile({ Bucket: bucket, Region: region, Key: cosKey, FilePath: filePath, onProgress: (progressInfo) => { const percent = Math.round(progressInfo.percent * 100) if (percent === 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 = '' // 方案1:直接从全局缓存获取 const uid = wx.getStorageSync('uid') if (uid) { yonghuid = String(uid).padStart(7, '0') //console.log('🔴【从uid获取yonghuid】', yonghuid) } // 方案2:如果还没有,从用户信息获取 if (!yonghuid && app.globalData.userInfo && app.globalData.userInfo.yonghuid) { yonghuid = String(app.globalData.userInfo.yonghuid).padStart(7, '0') //console.log('🔴【从userInfo获取yonghuid】', yonghuid) } // 方案3:如果还没有,直接提示并返回 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 const random = Math.floor(Math.random() * 10000) const fileExt = this.getFileExtension(this.data.shensuTupian[i]) const fileName = `${yonghuid}_${timestamp}_${random}${fileExt}` const cosKey = `a_long/chfajltp/dssstp/${fileName}` preGeneratedUrls.push(cosKey) } let cosClient, bucket, region try { const tokenData = await this.getCOSZhengshu() const { cos, bucket: cosBucket, region: cosRegion } = this.initCOSClient(tokenData) cosClient = cos bucket = cosBucket region = cosRegion } catch (error) { wx.showToast({ title: '上传初始化失败', icon: 'none' }) this.setData({ isShangchuanzhong: false }) return [] } const uploadTasks = [] for (let i = 0; i < total; i++) { const task = this.shangchuanDanZhangTupian( this.data.shensuTupian[i], preGeneratedUrls[i], i, cosClient, bucket, region ).then((result) => { const currentDone = i + 1 const jinduPercent = ((currentDone / total) * 100).toFixed(0) this.setData({ shangchuanJindu: currentDone, jinduWidth: `${jinduPercent}%` }) return result }) uploadTasks.push(task) } await Promise.all(uploadTasks) this.setData({ isShangchuanzhong: false }) return preGeneratedUrls }, getFileExtension(filePath) { const lastDotIndex = filePath.lastIndexOf('.') if (lastDotIndex === -1) return '.jpg' const ext = filePath.substring(lastDotIndex).toLowerCase() const allowedExts = ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp'] return allowedExts.includes(ext) ? ext : '.jpg' }, 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 tupianUrls = await this.piliangShangchuanShensuTupian() if (!tupianUrls || tupianUrls.length === 0) throw new Error('图片上传失败') await this.tijiaoShensuData(tupianUrls) } catch (error) { console.error('提交申诉失败:', error) wx.showToast({ title: error.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) { this.updateChufaRecord({ sqzhuangtai: 3, ssliyou: this.data.shensuLiyou, shensu_tupian: tupianUrls }) this.setData({ showShensuModal: false, showXiangqing: false, shensuLiyou: '', shensuTupian: [], xuanzhongChufa: {} }) wx.showToast({ title: '申诉提交成功,请等待处理', icon: 'success' }) setTimeout(() => { this.chushihuaShuju() }, 500) } else { throw new Error(res.data?.msg || '提交申诉失败') } } catch (error) { console.error('提交申诉数据失败:', error) wx.showToast({ title: error.message || '提交失败', icon: 'none' }) } }, updateChufaRecord(newData) { const newList = this.data.chufaList.map(item => { if (item.id === this.data.xuanzhongChufa.id) { const updatedItem = { ...item, ...newData } // 🔴 更新显示字段 if (newData.sqzhuangtai === 3) { updatedItem.display_status = '申诉中' updatedItem.status_class = 'zhuangtai-shensuzhong' } if (newData.shensu_tupian) { updatedItem.full_shensu_tupian = newData.shensu_tupian.map(url => this.getFullImageUrl(url)) } return updatedItem } return item }) this.setData({ chufaList: newList }) }, // 🔴【图片URL拼接 - 确保返回完整URL】 getFullImageUrl(relativeUrl) { //console.log('🔴【图片拼接】输入:', relativeUrl) if (!relativeUrl) return '' if (relativeUrl.startsWith('http')) { //console.log('🔴【已经是完整URL】返回:', relativeUrl) return relativeUrl } const ossUrl = this.data.ossImageUrl if (!ossUrl) { //console.log('🔴【OSS地址为空】返回原始URL:', relativeUrl) return relativeUrl } let fullUrl if (ossUrl.endsWith('/') && relativeUrl.startsWith('/')) { fullUrl = ossUrl + relativeUrl.substring(1) } else if (!ossUrl.endsWith('/') && !relativeUrl.startsWith('/')) { fullUrl = ossUrl + '/' + relativeUrl } else { fullUrl = ossUrl + relativeUrl } // console.log('🔴【拼接后URL】:', fullUrl) return fullUrl }, yulanTupian(e) { const currentUrl = e.currentTarget.dataset.url const urls = e.currentTarget.dataset.urls || [] if (!currentUrl) return const fullCurrentUrl = currentUrl.startsWith('http') ? currentUrl : this.getFullImageUrl(currentUrl) const fullUrls = urls.map(url => url.startsWith('http') ? url : this.getFullImageUrl(url)) wx.previewImage({ current: fullCurrentUrl, urls: fullUrls.length > 0 ? fullUrls : [fullCurrentUrl] }) }, fuzhiWenben(e) { const text = e.currentTarget.dataset.text if (!text) return wx.setClipboardData({ data: text, success: () => { wx.showToast({ title: '复制成功', icon: 'success' }) }, fail: () => { wx.showToast({ title: '复制失败', icon: 'none' }) } }) }, // 🔴【保留原函数但已不再使用(用于向后兼容)】 formatShijian(shijian) { if (!shijian) return '--' return String(shijian) }, getZhuangtaiText(zhuangtai) { const statusNum = Number(zhuangtai) if (statusNum === 0) return '待处罚' if (statusNum === 1) return '已处罚' if (statusNum === 2) return '已驳回' if (statusNum === 3) return '申诉中' return '未知状态' }, getZhuangtaiClass(zhuangtai) { const statusNum = Number(zhuangtai) if (statusNum === 0) return 'zhuangtai-daichuli' if (statusNum === 1) return 'zhuangtai-yichufa' if (statusNum === 2) return 'zhuangtai-yibohui' if (statusNum === 3) return 'zhuangtai-shensuzhong' return 'zhuangtai-weizhi' } })