| | |
| | | </template> |
| | | |
| | | <script> |
| | | import { getAttachmentList, uploadAttachmentFromWechat, deleteAttachment, getWechatAccessToken } from '@/api/task' |
| | | import { getAttachmentList, deleteAttachment } from '@/api/task' |
| | | import { formatDateTime } from '@/utils/common' |
| | | import { getToken } from '@/utils/auth' |
| | | import config from '@/config' |
| | | |
| | | export default { |
| | | name: 'AttachmentUpload', |
| | |
| | | { label: '系安全带', value: '6' } |
| | | ], |
| | | selectedCategoryIndex: 0, |
| | | tempImagePath: null, |
| | | isWechatMiniProgram: false |
| | | tempImagePath: null |
| | | } |
| | | }, |
| | | mounted() { |
| | | // 检测是否是微信小程序环境 |
| | | // #ifdef MP-WEIXIN |
| | | this.isWechatMiniProgram = true |
| | | // #endif |
| | | |
| | | // 自动加载附件列表 |
| | | if (this.autoLoad && this.taskId) { |
| | | this.loadAttachmentList() |
| | |
| | | const that = this |
| | | const category = this.categoryList[this.selectedCategoryIndex].value |
| | | |
| | | // 微信小程序环境:先获取AccessToken,再上传到微信服务器,最后提交mediaId到后端 |
| | | // #ifdef MP-WEIXIN |
| | | if (this.isWechatMiniProgram) { |
| | | uni.showLoading({ |
| | | title: '上传中...' |
| | | }) |
| | | |
| | | // 第一步:从后端获取AccessToken |
| | | getWechatAccessToken().then(tokenResponse => { |
| | | // 接口返回格式:{"msg":"token值","code":200} |
| | | console.log('获取AccessToken成功:', tokenResponse) |
| | | const accessToken = tokenResponse.msg || tokenResponse.data || tokenResponse |
| | | if (!accessToken) { |
| | | uni.hideLoading() |
| | | that.$modal.showToast('获取AccessToken失败') |
| | | console.error('获取AccessToken失败,响应数据:', tokenResponse) |
| | | return |
| | | } |
| | | |
| | | console.log('获取到AccessToken:', accessToken) |
| | | |
| | | // 第二步:上传到微信服务器 |
| | | const uploadUrl = `https://api.weixin.qq.com/cgi-bin/media/upload?access_token=${accessToken}&type=image` |
| | | |
| | | uni.uploadFile({ |
| | | url: uploadUrl, |
| | | filePath: that.tempImagePath, |
| | | name: 'media', |
| | | success: function(res) { |
| | | console.log('微信上传响应:', res) |
| | | try { |
| | | const data = JSON.parse(res.data) |
| | | if (data.media_id) { |
| | | // 第三步:提交mediaId到后端 |
| | | uploadAttachmentFromWechat(that.taskId, data.media_id, category).then(response => { |
| | | uni.hideLoading() |
| | | that.$modal.showToast('上传成功') |
| | | that.closeUploadDialog() |
| | | that.loadAttachmentList() |
| | | that.$emit('uploaded', response) |
| | | }).catch(error => { |
| | | uni.hideLoading() |
| | | console.error('提交mediaId失败:', error) |
| | | that.$modal.showToast('上传失败:' + (error.msg || '请重试')) |
| | | that.$emit('error', error) |
| | | }) |
| | | } else { |
| | | uni.hideLoading() |
| | | const errMsg = data.errmsg || '未知错误' |
| | | console.error('微信返回错误:', data) |
| | | that.$modal.showToast('微信上传失败:' + errMsg) |
| | | } |
| | | } catch (e) { |
| | | uni.hideLoading() |
| | | console.error('解析微信响应失败:', e, res.data) |
| | | that.$modal.showToast('上传失败:响应解析错误') |
| | | } |
| | | }, |
| | | fail: function(err) { |
| | | uni.hideLoading() |
| | | console.error('上传到微信失败:', err) |
| | | that.$modal.showToast('上传失败:' + (err.errMsg || '请检查网络')) |
| | | that.$emit('error', err) |
| | | } |
| | | }) |
| | | }).catch(error => { |
| | | uni.hideLoading() |
| | | console.error('获取AccessToken失败:', error) |
| | | that.$modal.showToast('获取AccessToken失败') |
| | | that.$emit('error', error) |
| | | }) |
| | | return |
| | | } |
| | | // #endif |
| | | |
| | | // 非微信小程序环境:直接上传到后端服务器 |
| | | // 统一直接上传到后端服务器 |
| | | uni.showLoading({ |
| | | title: '上传中...' |
| | | }) |
| | | |
| | | uni.uploadFile({ |
| | | url: that.$baseUrl + '/task/attachment/upload/' + that.taskId, |
| | | url: config.baseUrl + '/task/attachment/upload/' + that.taskId, |
| | | filePath: that.tempImagePath, |
| | | name: 'file', |
| | | formData: { |
| | | 'category': category |
| | | }, |
| | | header: { |
| | | 'Authorization': 'Bearer ' + uni.getStorageSync('token') |
| | | 'Authorization': 'Bearer ' + getToken() |
| | | }, |
| | | success: function(uploadRes) { |
| | | uni.hideLoading() |
| | |
| | | fail: function(err) { |
| | | uni.hideLoading() |
| | | console.error('上传失败:', err) |
| | | that.$modal.showToast('上传失败') |
| | | that.$modal.showToast('上传失败:' + (err.errMsg || '请检查网络')) |
| | | that.$emit('error', err) |
| | | } |
| | | }) |
| | |
| | | let imageUrl = item.fileUrl |
| | | // 如果没有fileUrl,则使用下载接口 |
| | | if (!imageUrl) { |
| | | imageUrl = this.$baseUrl + '/task/attachment/download/' + item.attachmentId |
| | | imageUrl = config.baseUrl + '/task/attachment/download/' + item.attachmentId |
| | | } |
| | | |
| | | // 微信小程序中预览图片 |
| | |
| | | } |
| | | // 使用下载接口 |
| | | if (item.attachmentId) { |
| | | return this.$baseUrl + '/task/attachment/download/' + item.attachmentId |
| | | return config.baseUrl + '/task/attachment/download/' + item.attachmentId |
| | | } |
| | | // 默认占位图 |
| | | return '/static/images/default-image.png' |
| | |
| | | // 是否需要设置 token |
| | | const isToken = (config.headers || {}).isToken === false |
| | | config.header = config.header || {} |
| | | if (getToken() && !isToken) { |
| | | config.header['Authorization'] = 'Bearer ' + getToken() |
| | | const token = getToken() |
| | | // 只有当token存在且不为空字符串时才添加到header |
| | | if (token && token.trim() !== '' && !isToken) { |
| | | config.header['Authorization'] = 'Bearer ' + token |
| | | } |
| | | // get请求映射params参数 |
| | | if (config.params) { |
| | |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | log.error("获取用户信息异常'{}'", e.getMessage()); |
| | | log.error("获取用户信息异常,token: [{}], 错误: {}", token.length() > 50 ? token.substring(0, 50) + "..." : token, e.getMessage()); |
| | | } |
| | | } |
| | | else |
| | | { |
| | | log.warn("请求未携带有效token,Header: {}", request.getHeader(header)); |
| | | } |
| | | return null; |
| | | } |
| | | |
| | |
| | | package com.ruoyi.system.domain; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.util.Date; |
| | | import com.fasterxml.jackson.annotation.JsonFormat; |
| | | import org.apache.commons.lang3.builder.ToStringBuilder; |
| | | import org.apache.commons.lang3.builder.ToStringStyle; |
| | | import com.ruoyi.common.annotation.Excel; |
| | | import com.ruoyi.common.core.domain.BaseEntity; |
| | | |
| | | /** |
| | | * 车辆里程统计对象 vehicle_mileage_stats |
| | | * |
| | | * @author ruoyi |
| | | * @date 2025-01-15 |
| | | */ |
| | | public class VehicleMileageStats extends BaseEntity |
| | | { |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | /** 统计ID */ |
| | | private Long statsId; |
| | | |
| | | /** 车辆ID */ |
| | | @Excel(name = "车辆ID") |
| | | private Long vehicleId; |
| | | |
| | | /** 统计日期 */ |
| | | @JsonFormat(pattern = "yyyy-MM-dd") |
| | | @Excel(name = "统计日期", width = 30, dateFormat = "yyyy-MM-dd") |
| | | private Date statsDate; |
| | | |
| | | /** 总里程(公里) */ |
| | | @Excel(name = "总里程(公里)") |
| | | private BigDecimal totalMileage; |
| | | |
| | | /** 任务里程(公里) */ |
| | | @Excel(name = "任务里程(公里)") |
| | | private BigDecimal taskMileage; |
| | | |
| | | /** 非任务里程(公里) */ |
| | | @Excel(name = "非任务里程(公里)") |
| | | private BigDecimal nonTaskMileage; |
| | | |
| | | /** 任务里程占比(%) */ |
| | | @Excel(name = "任务里程占比(%)") |
| | | private BigDecimal taskMileageRatio; |
| | | |
| | | /** GPS点数 */ |
| | | @Excel(name = "GPS点数") |
| | | private Integer gpsCount; |
| | | |
| | | public void setStatsId(Long statsId) |
| | | { |
| | | this.statsId = statsId; |
| | | } |
| | | |
| | | public Long getStatsId() |
| | | { |
| | | return statsId; |
| | | } |
| | | public void setVehicleId(Long vehicleId) |
| | | { |
| | | this.vehicleId = vehicleId; |
| | | } |
| | | |
| | | public Long getVehicleId() |
| | | { |
| | | return vehicleId; |
| | | } |
| | | public void setStatsDate(Date statsDate) |
| | | { |
| | | this.statsDate = statsDate; |
| | | } |
| | | |
| | | public Date getStatsDate() |
| | | { |
| | | return statsDate; |
| | | } |
| | | public void setTotalMileage(BigDecimal totalMileage) |
| | | { |
| | | this.totalMileage = totalMileage; |
| | | } |
| | | |
| | | public BigDecimal getTotalMileage() |
| | | { |
| | | return totalMileage; |
| | | } |
| | | public void setTaskMileage(BigDecimal taskMileage) |
| | | { |
| | | this.taskMileage = taskMileage; |
| | | } |
| | | |
| | | public BigDecimal getTaskMileage() |
| | | { |
| | | return taskMileage; |
| | | } |
| | | public void setNonTaskMileage(BigDecimal nonTaskMileage) |
| | | { |
| | | this.nonTaskMileage = nonTaskMileage; |
| | | } |
| | | |
| | | public BigDecimal getNonTaskMileage() |
| | | { |
| | | return nonTaskMileage; |
| | | } |
| | | public void setTaskMileageRatio(BigDecimal taskMileageRatio) |
| | | { |
| | | this.taskMileageRatio = taskMileageRatio; |
| | | } |
| | | |
| | | public BigDecimal getTaskMileageRatio() |
| | | { |
| | | return taskMileageRatio; |
| | | } |
| | | public void setGpsCount(Integer gpsCount) |
| | | { |
| | | this.gpsCount = gpsCount; |
| | | } |
| | | |
| | | public Integer getGpsCount() |
| | | { |
| | | return gpsCount; |
| | | } |
| | | |
| | | @Override |
| | | public String toString() { |
| | | return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) |
| | | .append("statsId", getStatsId()) |
| | | .append("vehicleId", getVehicleId()) |
| | | .append("statsDate", getStatsDate()) |
| | | .append("totalMileage", getTotalMileage()) |
| | | .append("taskMileage", getTaskMileage()) |
| | | .append("nonTaskMileage", getNonTaskMileage()) |
| | | .append("taskMileageRatio", getTaskMileageRatio()) |
| | | .append("gpsCount", getGpsCount()) |
| | | .append("createTime", getCreateTime()) |
| | | .append("updateTime", getUpdateTime()) |
| | | .toString(); |
| | | } |
| | | } |
| | |
| | | package com.ruoyi.system.mapper; |
| | | |
| | | import java.util.Date; |
| | | import java.util.List; |
| | | import com.ruoyi.system.domain.VehicleMileageStats; |
| | | import org.apache.ibatis.annotations.Param; |
| | | |
| | | /** |
| | | * 车辆里程统计Mapper接口 |
| | | * |
| | | * @author ruoyi |
| | | * @date 2025-01-15 |
| | | */ |
| | | public interface VehicleMileageStatsMapper |
| | | { |
| | | /** |
| | | * 查询车辆里程统计 |
| | | * |
| | | * @param statsId 车辆里程统计主键 |
| | | * @return 车辆里程统计 |
| | | */ |
| | | public VehicleMileageStats selectVehicleMileageStatsByStatsId(Long statsId); |
| | | |
| | | /** |
| | | * 查询车辆里程统计列表 |
| | | * |
| | | * @param vehicleMileageStats 车辆里程统计 |
| | | * @return 车辆里程统计集合 |
| | | */ |
| | | public List<VehicleMileageStats> selectVehicleMileageStatsList(VehicleMileageStats vehicleMileageStats); |
| | | |
| | | /** |
| | | * 新增车辆里程统计 |
| | | * |
| | | * @param vehicleMileageStats 车辆里程统计 |
| | | * @return 结果 |
| | | */ |
| | | public int insertVehicleMileageStats(VehicleMileageStats vehicleMileageStats); |
| | | |
| | | /** |
| | | * 修改车辆里程统计 |
| | | * |
| | | * @param vehicleMileageStats 车辆里程统计 |
| | | * @return 结果 |
| | | */ |
| | | public int updateVehicleMileageStats(VehicleMileageStats vehicleMileageStats); |
| | | |
| | | /** |
| | | * 删除车辆里程统计 |
| | | * |
| | | * @param statsId 车辆里程统计主键 |
| | | * @return 结果 |
| | | */ |
| | | public int deleteVehicleMileageStatsByStatsId(Long statsId); |
| | | |
| | | /** |
| | | * 批量删除车辆里程统计 |
| | | * |
| | | * @param statsIds 需要删除的数据主键集合 |
| | | * @return 结果 |
| | | */ |
| | | public int deleteVehicleMileageStatsByStatsIds(Long[] statsIds); |
| | | |
| | | /** |
| | | * 查询指定车辆和日期的统计记录 |
| | | * |
| | | * @param vehicleId 车辆ID |
| | | * @param statsDate 统计日期 |
| | | * @return 统计记录 |
| | | */ |
| | | public VehicleMileageStats selectByVehicleIdAndDate(@Param("vehicleId") Long vehicleId, @Param("statsDate") Date statsDate); |
| | | } |
| | |
| | | <?xml version="1.0" encoding="UTF-8" ?> |
| | | <!DOCTYPE mapper |
| | | PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" |
| | | "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> |
| | | <mapper namespace="com.ruoyi.system.mapper.VehicleMileageStatsMapper"> |
| | | |
| | | <resultMap type="VehicleMileageStats" id="VehicleMileageStatsResult"> |
| | | <result property="statsId" column="stats_id" /> |
| | | <result property="vehicleId" column="vehicle_id" /> |
| | | <result property="statsDate" column="stats_date" /> |
| | | <result property="totalMileage" column="total_mileage" /> |
| | | <result property="taskMileage" column="task_mileage" /> |
| | | <result property="nonTaskMileage" column="non_task_mileage" /> |
| | | <result property="taskMileageRatio" column="task_mileage_ratio" /> |
| | | <result property="gpsCount" column="gps_count" /> |
| | | <result property="createTime" column="create_time" /> |
| | | <result property="updateTime" column="update_time" /> |
| | | </resultMap> |
| | | |
| | | <sql id="selectVehicleMileageStatsVo"> |
| | | select stats_id, vehicle_id, stats_date, total_mileage, task_mileage, non_task_mileage, |
| | | task_mileage_ratio, gps_count, create_time, update_time |
| | | from vehicle_mileage_stats |
| | | </sql> |
| | | |
| | | <select id="selectVehicleMileageStatsList" parameterType="VehicleMileageStats" resultMap="VehicleMileageStatsResult"> |
| | | <include refid="selectVehicleMileageStatsVo"/> |
| | | <where> |
| | | <if test="vehicleId != null "> and vehicle_id = #{vehicleId}</if> |
| | | <if test="statsDate != null "> and stats_date = #{statsDate}</if> |
| | | </where> |
| | | order by stats_date desc, vehicle_id |
| | | </select> |
| | | |
| | | <select id="selectVehicleMileageStatsByStatsId" parameterType="Long" resultMap="VehicleMileageStatsResult"> |
| | | <include refid="selectVehicleMileageStatsVo"/> |
| | | where stats_id = #{statsId} |
| | | </select> |
| | | |
| | | <select id="selectByVehicleIdAndDate" resultMap="VehicleMileageStatsResult"> |
| | | <include refid="selectVehicleMileageStatsVo"/> |
| | | where vehicle_id = #{vehicleId} and stats_date = #{statsDate} |
| | | </select> |
| | | |
| | | <insert id="insertVehicleMileageStats" parameterType="VehicleMileageStats" useGeneratedKeys="true" keyProperty="statsId"> |
| | | insert into vehicle_mileage_stats |
| | | <trim prefix="(" suffix=")" suffixOverrides=","> |
| | | <if test="vehicleId != null">vehicle_id,</if> |
| | | <if test="statsDate != null">stats_date,</if> |
| | | <if test="totalMileage != null">total_mileage,</if> |
| | | <if test="taskMileage != null">task_mileage,</if> |
| | | <if test="nonTaskMileage != null">non_task_mileage,</if> |
| | | <if test="taskMileageRatio != null">task_mileage_ratio,</if> |
| | | <if test="gpsCount != null">gps_count,</if> |
| | | <if test="createTime != null">create_time,</if> |
| | | <if test="updateTime != null">update_time,</if> |
| | | </trim> |
| | | <trim prefix="values (" suffix=")" suffixOverrides=","> |
| | | <if test="vehicleId != null">#{vehicleId},</if> |
| | | <if test="statsDate != null">#{statsDate},</if> |
| | | <if test="totalMileage != null">#{totalMileage},</if> |
| | | <if test="taskMileage != null">#{taskMileage},</if> |
| | | <if test="nonTaskMileage != null">#{nonTaskMileage},</if> |
| | | <if test="taskMileageRatio != null">#{taskMileageRatio},</if> |
| | | <if test="gpsCount != null">#{gpsCount},</if> |
| | | <if test="createTime != null">#{createTime},</if> |
| | | <if test="updateTime != null">#{updateTime},</if> |
| | | </trim> |
| | | </insert> |
| | | |
| | | <update id="updateVehicleMileageStats" parameterType="VehicleMileageStats"> |
| | | update vehicle_mileage_stats |
| | | <trim prefix="SET" suffixOverrides=","> |
| | | <if test="vehicleId != null">vehicle_id = #{vehicleId},</if> |
| | | <if test="statsDate != null">stats_date = #{statsDate},</if> |
| | | <if test="totalMileage != null">total_mileage = #{totalMileage},</if> |
| | | <if test="taskMileage != null">task_mileage = #{taskMileage},</if> |
| | | <if test="nonTaskMileage != null">non_task_mileage = #{nonTaskMileage},</if> |
| | | <if test="taskMileageRatio != null">task_mileage_ratio = #{taskMileageRatio},</if> |
| | | <if test="gpsCount != null">gps_count = #{gpsCount},</if> |
| | | <if test="createTime != null">create_time = #{createTime},</if> |
| | | <if test="updateTime != null">update_time = #{updateTime},</if> |
| | | </trim> |
| | | where stats_id = #{statsId} |
| | | </update> |
| | | |
| | | <delete id="deleteVehicleMileageStatsByStatsId" parameterType="Long"> |
| | | delete from vehicle_mileage_stats where stats_id = #{statsId} |
| | | </delete> |
| | | |
| | | <delete id="deleteVehicleMileageStatsByStatsIds" parameterType="String"> |
| | | delete from vehicle_mileage_stats where stats_id in |
| | | <foreach item="statsId" collection="array" open="(" separator="," close=")"> |
| | | #{statsId} |
| | | </foreach> |
| | | </delete> |
| | | </mapper> |
| | |
| | | -- 车辆里程统计表 |
| | | DROP TABLE IF EXISTS `vehicle_mileage_stats`; |
| | | CREATE TABLE `vehicle_mileage_stats` ( |
| | | `stats_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '统计ID', |
| | | `vehicle_id` bigint(20) NOT NULL COMMENT '车辆ID', |
| | | `stats_date` date NOT NULL COMMENT '统计日期', |
| | | `total_mileage` decimal(10,2) DEFAULT '0.00' COMMENT '总里程(公里)', |
| | | `task_mileage` decimal(10,2) DEFAULT '0.00' COMMENT '任务里程(公里)', |
| | | `non_task_mileage` decimal(10,2) DEFAULT '0.00' COMMENT '非任务里程(公里)', |
| | | `task_mileage_ratio` decimal(5,2) DEFAULT '0.00' COMMENT '任务里程占比(%)', |
| | | `gps_count` int(11) DEFAULT '0' COMMENT 'GPS点数', |
| | | `create_time` datetime DEFAULT NULL COMMENT '创建时间', |
| | | `update_time` datetime DEFAULT NULL COMMENT '更新时间', |
| | | PRIMARY KEY (`stats_id`), |
| | | UNIQUE KEY `uk_vehicle_date` (`vehicle_id`,`stats_date`), |
| | | KEY `idx_stats_date` (`stats_date`), |
| | | KEY `idx_vehicle_id` (`vehicle_id`) |
| | | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='车辆里程统计表'; |
| | | |
| | | -- 车辆里程统计明细表 |
| | | DROP TABLE IF EXISTS `vehicle_mileage_stats_detail`; |
| | | CREATE TABLE `vehicle_mileage_stats_detail` ( |
| | | `detail_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '明细ID', |
| | | `stats_id` bigint(20) NOT NULL COMMENT '统计ID', |
| | | `vehicle_id` bigint(20) NOT NULL COMMENT '车辆ID', |
| | | `task_id` bigint(20) DEFAULT NULL COMMENT '任务ID(任务时段)', |
| | | `start_time` datetime NOT NULL COMMENT '开始时间', |
| | | `end_time` datetime NOT NULL COMMENT '结束时间', |
| | | `mileage` decimal(10,2) DEFAULT '0.00' COMMENT '里程(公里)', |
| | | `is_task_period` char(1) DEFAULT '0' COMMENT '是否任务时段(0否 1是)', |
| | | `gps_count` int(11) DEFAULT '0' COMMENT 'GPS点数', |
| | | `create_time` datetime DEFAULT NULL COMMENT '创建时间', |
| | | PRIMARY KEY (`detail_id`), |
| | | KEY `idx_stats_id` (`stats_id`), |
| | | KEY `idx_vehicle_id` (`vehicle_id`), |
| | | KEY `idx_task_id` (`task_id`) |
| | | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='车辆里程统计明细表'; |