ts_aimz_uni/pages/copyright/applyRepair/applyRepair.vue

613 lines
18 KiB
Vue
Raw Normal View History

2025-05-19 12:05:53 +08:00
<template>
<view class="page-container">
<swiper indicator-dots style="height: 120rpx;" autoplay indicator-active-color="#fff">
<swiper-item>
<image :src="imgAssets+'/banner_1.png'" style="width: 100vw;height: 120rpx;"></image>
</swiper-item>
<swiper-item>
<image :src="imgAssets+'/banner_2.png'" style="width: 100vw;height: 120rpx;"></image>
</swiper-item>
</swiper>
<view class="apply-box">
<text class="label">补正信息</text>
<view class="apply-item-row mt-20 ml-10">
<view class="apply-title star" style="align-self: center;">补正软著</view>
<view class="apply-content" @click="showSelProDialog">
<view :class="selPro == null? 'text-hint':'text-sel'">
{{selPro==null? '请选择需要补正的软著' :selPro.projName}}
</view>
<view class="icon-arrow-solid"></view>
</view>
</view>
<view class="apply-item-row mt-20 ml-10">
<view class="apply-title star" style="align-self: center;">补正种类</view>
<view class="apply-content" @click="showSelTypeDialog">
<view :class="selType==null?'text-hint':'text-sel'">{{selType==null?'请选择种类':selType.title}}
</view>
<view class="icon-arrow-solid"></view>
</view>
</view>
<view class="apply-item-column mt-20 ml-10">
<view class="apply-title star">补正原因</view>
<textarea @input="inputRemark" :value="remark" placeholder="请输入补正原因"
placeholder-style="font-size:28rpx;color:$text-gray-hint-color"
class="reason-content mt-10"></textarea>
</view>
<view class="apply-item-column mt-20 ml-10">
<view class="apply-title star">补正凭证</view>
<view class="mt-10">
<uni-file-picker ref="imgPicker" :value="files" :del-icon="true" limit="4" fileMediatype="image"
@delete="delImgs" :auto-upload="false" :image-styles="imageStyles"
@select="uploadImgToNet"></uni-file-picker>
2025-05-19 12:05:53 +08:00
</view>
<view class="hint">
*上传完整的补正通知书或者完整的补正通知书的截图,要求右上方的流水号和右下方的补正通知书的日期都得完整显示
</view>
</view>
</view>
<view class="bottom-fixed-footer">
<view class="bottom-btn-green" @click="doApply">提交</view>
</view>
<!-- 可以补正的项目dialog -->
<uni-popup ref="proDialog" type="bottom" background-color="#fff" border-radius="15rpx 15rpx 0rpx 0rpx">
<view class="bottom-dialog-container">
<view class="dialog-title-box">
<view class="search-container">
<input placeholder="请输入项目名称" :value="proKeywords" @input="inputSearchPro" class="search-input"
type="text" @confirm="doSearchKeyWord" confirm-type="search" />
</view>
<view class="icon-close size-48" @click="closeDialog"></view>
</view>
<view style="height: 600rpx;" class="mt-10">
<containerLoadingVue :loadingVisible="loadingState">
<scroll-view scroll-y style="height: 580rpx;" :lower-threshold="10" @doRefresh="doRefreshList"
refresher-background="#FFFFFF00" @scrolltolower="doLoadMore">
<view class="pro-list-box">
<radio-group @change="bindChangeSelPro">
2025-05-19 12:05:53 +08:00
<view v-for="(item,index) in proList" :key="index" class="pro-list-item"
:data-value="item">
<radio :value="item.projId"
:checked="tempSelPro != null && tempSelPro.projId==item.projId">
</radio>
2025-05-19 12:05:53 +08:00
<view class="pro-list-item-left" @click="choosePro" :data-value="item">
<view class="pro-list-item-left-title">
<view class="pro-list-name single-line">{{item.projName}}</view>
<rich-text class="pro-list-item-left-count"
:nodes="'已通过补正<span style=color:red;font-size:28rpx;font-weight:bold;>'+ item.approvedCount+'</span>次'"></rich-text>
</view>
<view class="pro-list-item-left-footer">
2025-06-24 14:08:30 +08:00
<view class="pro-list-item-left-tag">{{item.gmtCreate}}</view>
<view class="pro-list-item-left-tag single-line">
2025-05-19 12:05:53 +08:00
{{item.authorName}}
</view>
</view>
</view>
</view>
</radio-group>
<uni-load-more :status="hasMore"></uni-load-more>
</view>
</scroll-view>
</containerLoadingVue>
</view>
<view class="bottom-btn-green mb-20" @click="confirmSelPro">确定</view>
2025-05-19 12:05:53 +08:00
</view>
</uni-popup>
<!-- 类型 -->
<uni-popup ref="typeDialog" type="bottom" background-color="#fff" border-radius="15rpx 15rpx 0rpx 0rpx">
<view class="bottom-dialog-container">
<view class="dialog-title-box">
<view class="icon-close size-48" @click="closeDialog"></view>
<view class="dialog-title-txt has-icon">补正种类</view>
</view>
<view style="height: 600rpx;" class="mt-10">
<view class="pro-list-box">
<radio-group>
<block v-for="(item,index) in typeList" :key="index">
<view class="pro-list-item">
<radio :checked="tempSelType != null&& tempSelType.value==item.value"></radio>
2025-05-19 12:05:53 +08:00
<view class="pro-list-item-left" @click="chooseType" :data-value="item">
<view class="pro-list-item-left-title">
<view class="pro-list-name">{{item.title}}</view>
</view>
</view>
</view>
</block>
</radio-group>
</view>
</view>
<view class="bottom-btn-green" @click="confirmSelType">确定</view>
</view>
</uni-popup>
<uni-popup type="message" ref="msg">
<uni-popup-message :type="msgType" :message="msgHint" :duration="2000"></uni-popup-message>
</uni-popup>
</view>
</template>
<script>
import ProApi from '@/common/js/net/projectApi.js'
import containerLoadingVue from '../../../components/container-loading.vue'
import {
kindList
} from '@/common/js/data.js'
import {
uploadImgUrl,
previewUrl
2025-05-19 12:05:53 +08:00
} from '../../../common/js/net/mainUrl'
import {
get
} from '@/common/js/cache/storage.js'
import {
inject
} from 'vue';
export default {
components: {
containerLoadingVue
},
setup() {
const globalData = inject('globalData')
return {
globalData
}
},
data() {
return {
imgAssets: this.globalData.imgAssetsUrl,
msgHint: '',
msgType: '',
msgShow: false,
selPro: null,
selType: null,
files: [],
proList: [],
tempSelPro: null,
typeList: kindList,
showSelPro: false,
showSelType: false,
tempSelType: null,
remark: '',
proKeywords: '',
proPageData: {
page: 1,
rows: 10,
keywords: ''
},
listRefreshTrig: false,
loadingState: 'loading',
hasMore: 'more',
imageStyles: {
width: 72,
height: 72,
border: {
color: "#F5F5F5",
width: 1,
style: 'solid',
radius: '2px'
}
},
primaryColor: this.globalData.primaryColor,
};
},
onLoad(options) {
uni.setNavigationBarTitle({
title: "补正申请",
});
uni.setNavigationBarColor({
frontColor: "#000000",
backgroundColor: "#F0F0F0",
animation: {
duration: 500,
timingFunc: "easeIn",
},
});
},
methods: {
inputSearchPro(e) {
this.proKeywords = e.detail.value
},
inputRemark(e) {
this.remark = e.detail.value
},
showSelProDialog() {
const _self = this
_self.doRefreshList()
_self.tempSelPro = _self.selPro
_self.$refs.proDialog.open()
},
showSelTypeDialog() {
this.tempSelType = this.selType
this.$refs.typeDialog.open()
},
chooseType(e) {
this.tempSelType = e.currentTarget.dataset.value
},
closeDialog() {
this.$refs.proDialog.close()
this.$refs.typeDialog.close()
},
confirmSelPro() {
if (this.tempSelPro == null) {
this.msgHint = '请选择补正项目'
2025-05-19 12:05:53 +08:00
this.msgType = 'error'
this.$refs.msg.open()
} else {
this.$refs.proDialog.close()
this.selPro = this.tempSelPro
this.tempSelPro = null
}
},
//确定种类
confirmSelType() {
this.selType = this.tempSelType
this.tempSelType = null
this.$refs.typeDialog.close()
},
doSearchKeyWord() {
this.doRefreshList()
},
//刷新
doRefreshList() {
const _self = this
_self.loadingState = 'loading'
_self.hasMore = 'more'
_self.isLoadMore = false
_self.proPageData.page = 1
_self.proPageData.keywords = _self.proKeywords
_self.getCanRefundList(true)
},
//加载更多
doLoadMore() {
//判断是否正在加载中 与是否存在更多数据
const _self = this
if (_self.isLoadMore || _self.hasMore == 'noMore') {
return
}
_self.isLoadMore = true
_self.hasMore = 'loading'
2025-05-19 12:05:53 +08:00
_self.proPageData.page = ++_self.proPageData.page
_self.proPageData.keywords = _self.proKeywords
_self.getCanRefundList(false)
},
getCanRefundList(isRefresh) {
const _self = this
_self.proList = isRefresh ? [] : _self.proList
_self.loadingState = isRefresh ? 'loading' : ''
ProApi.doGetCanProRepairList(_self.proPageData)
.then(res => {
console.log(res)
var status = 'success'
status = res.rows && res.rows.length > 0 ? 'success' : 'empty'
_self.loadingState = isRefresh ? status : ''
_self.proList = _self.proList.concat(res.rows)
_self.isLoadMore = false
_self.hasMore = _self.proList.length < res.total ? 'more' : 'noMore'
})
.catch(err => {
_self.loadingState = 'error'
_self.isLoadMore = false
_self.hasMore = 'more'
})
},
bindChangeSelPro(e) {
const id = e.detail.value;
const selectedItem = this.proList.find(item => item.projId === id);
this.tempSelPro = selectedItem
},
2025-05-19 12:05:53 +08:00
choosePro(e) {
this.tempSelPro = e.currentTarget.dataset.value
},
//删除图片
delImgs(e) {
console.log('删除凭证', e)
this.files.splice(e.index, 1)
},
//上传图片
async uploadImgToNet(e) {
uni.showLoading({
title: '上传中...'
})
const tempFilePaths = e.tempFilePaths;
const token = get('token');
const header = token ? {
Auth: `Bearer ${token}`
} : {};
const that = this;
try {
// 创建一个数组存储上传结果
const results = [];
// 串行上传(一个完成后再上传下一个)
for (let i = 0; i < tempFilePaths.length; i++) {
const filePath = tempFilePaths[i];
// 使用 Promise 封装 uni.uploadFile
const result = await new Promise((resolve, reject) => {
uni.uploadFile({
url: uploadImgUrl,
header: header,
filePath: filePath,
name: 'image',
success(res) {
if (res.statusCode === 200) {
let data = res.data;
if (typeof data === 'string') {
data = JSON.parse(data);
}
resolve(data.data);
} else {
reject(new Error('上传失败状态码非200'));
}
},
fail(err) {
reject(err);
}
});
});
// 按顺序添加到结果数组
result.url = previewUrl + result.fileId
result.name = result.fileName
let lastDot = result.fileName.lastIndexOf('.')
result.extname = result.fileName.substring(lastDot + 1);
2025-05-19 12:05:53 +08:00
results.push(result);
}
// 全部上传成功后更新文件列表
that.files = that.files.concat(results);
uni.hideLoading();
} catch (error) {
uni.hideLoading();
that.msgHint = '凭证上传失败,请稍后重试';
that.msgType = 'error';
that.$refs.msg.open();
// 清空已选择的文件
that.$refs.imgPicker.clearFiles();
console.error('上传错误:', error);
}
},
//提交申请
doApply() {
const legal = this.checkParams()
if (legal) {
const _self = this
uni.showLoading({
title: '提交中...',
})
const ids = _self.files.map(item => item.fileId).join(',');
const data = {
correctionReason: _self.remark,
correctionType: _self.selType.value,
correctionVoucher: ids,
projId: _self.selPro.projId
}
ProApi.doApplyProRepair(data)
.then(res => {
uni.hideLoading()
_self.msgHint = '提交操作已顺利完成,我们会尽快审核,还请耐心等待'
_self.msgType = 'success'
_self.$refs.msg.open()
2025-06-24 14:08:30 +08:00
setTimeout(()=>{
_self.backPageRefresh()
},1500)
2025-05-19 12:05:53 +08:00
})
.catch(err => {
uni.hideLoading()
_self.msgHint = err.msg ? err.msg : '提交失败,请稍后重试'
_self.msgType = 'error'
_self.$refs.msg.open()
})
}
},
checkParams() {
const _self = this
if (_self.selPro == null) {
_self.msgHint = '请选择补正软著'
_self.msgType = 'error'
_self.$refs.msg.open()
return false
}
if (_self.selType == null) {
_self.msgHint = '请选择补正种类'
_self.msgType = 'error'
_self.$refs.msg.open()
return false
}
if (_self.remark == '') {
_self.msgHint = '请输入补正原因'
_self.msgType = 'error'
_self.$refs.msg.open()
return false
}
if (_self.files.length <= 0) {
_self.msgHint = '请上传补正凭证'
_self.msgType = 'error'
_self.$refs.msg.open()
return false
}
return true
2025-06-24 14:08:30 +08:00
},
backPageRefresh() {
var pages = getCurrentPages()
let prevPage = pages[pages.length - 2];
prevPage.$vm.needRefresh = true
uni.navigateBack()
}
2025-05-19 12:05:53 +08:00
},
};
</script>
<style lang="scss" scoped>
.apply-box {
margin-top: 20rpx;
display: flex;
flex-direction: column;
padding: 30rpx;
background-color: $white-color;
border-radius: 10rpx;
}
.label {
font-size: 32rpx;
font-weight: bold;
}
.apply-item-row {
display: flex;
flex-direction: row;
justify-content: space-between;
}
.apply-item-column {
display: flex;
flex-direction: column;
}
.apply-title {
font-size: 28rpx;
color: $text-color;
flex: 0.3;
}
.apply-content {
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-end;
flex: 0.7;
background-color: $bg-gray-input-color;
padding: 10rpx 15rpx;
font-size: 28rpx;
border-radius: 15rpx;
}
.reason-content {
border-radius: 15rpx;
height: 120rpx;
background-color: $bg-gray-input-color;
font-size: 28rpx;
padding: 20rpx;
}
.text-hint {
color: $text-gray-hint-color;
}
.text-sel {
color: $text-color;
}
.hint {
margin-top: 10rpx;
font-size: 26rpx;
color: $red-color;
}
.pro-list-box {
display: flex;
flex-direction: column;
}
.pro-list-item {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
border-radius: 10rpx;
padding: 30rpx;
margin: 10rpx;
2025-06-24 14:08:30 +08:00
border-bottom: 1rpx solid $divider-color;
2025-05-19 12:05:53 +08:00
}
.pro-list-item:nth-of-type(n+2) {
margin-top: 20rpx;
}
2025-06-24 14:08:30 +08:00
.pro-list-item:last-of-type {
border-bottom: none;
}
2025-05-19 12:05:53 +08:00
.pro-list-item-left {
display: flex;
flex-direction: column;
flex: 1;
margin-left: 30rpx;
}
.pro-list-item-left-title {
display: flex;
flex-direction: row;
justify-content: space-between;
}
.pro-list-item-left-footer {
display: flex;
flex-direction: row;
justify-content: space-between;
margin-top: 20rpx;
}
.pro-list-item-left-count {
font-size: 24rpx;
color: $text-color;
flex-wrap: nowrap;
margin-top: 4rpx;
}
.pro-list-item-left-tag {
font-size: 24rpx;
color: $text-color;
flex: .5;
}
.pro-list-name {
font-size: 26rpx;
font-weight: bold;
flex: 1;
}
.search-container {
position: relative;
align-self: center;
border-radius: 5px;
background-color: $bg-gray-color;
padding: 5px;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
width: 100%;
margin-right: 20rpx;
}
.search-input {
box-sizing: border-box;
color: $text-color;
font-size: 28rpx;
text-align: center;
flex: 1;
background-color: $bg-gray-input-color;
border-radius: 10rpx;
}
.search-input::after {
content: '';
position: absolute;
left: 10px;
top: 50%;
transform: translateY(-50%);
width: 20px;
height: 20px;
margin-top: -1px;
background-size: cover;
background-image: url('data:image/svg+xml;charset=utf-8;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSI2NCA2NCA4OTYgODk2IiB3aWR0aD0iMTQiIGhlaWdodD0iMTkiIHN0eWxlPSIiIGZpbHRlcj0ibm9uZSI+CiAgICAKICAgIDxnPgogICAgPHBhdGggZD0iTTkwOS42IDg1NC41TDY0OS45IDU5NC44QzY5MC4yIDU0Mi43IDcxMiA0NzkgNzEyIDQxMmMwLTgwLjItMzEuMy0xNTUuNC04Ny45LTIxMi4xLTU2LjYtNTYuNy0xMzItODcuOS0yMTIuMS04Ny45cy0xNTUuNSAzMS4zLTIxMi4xIDg3LjlDMTQzLjIgMjU2LjUgMTEyIDMzMS44IDExMiA0MTJjMCA4MC4xIDMxLjMgMTU1LjUgODcuOSAyMTIuMUMyNTYuNSA2ODAuOCAzMzEuOCA3MTIgNDEyIDcxMmM2NyAwIDEzMC42LTIxLjggMTgyLjctNjJsMjU5LjcgMjU5LjZhOC4yIDguMiAwIDAgMCAxMS42IDBsNDMuNi00My41YTguMiA4LjIgMCAwIDAgMC0xMS42ek01NzAuNCA1NzAuNEM1MjggNjEyLjcgNDcxLjggNjM2IDQxMiA2MzZzLTExNi0yMy4zLTE1OC40LTY1LjZDMjExLjMgNTI4IDE4OCA0NzEuOCAxODggNDEyczIzLjMtMTE2LjEgNjUuNi0xNTguNEMyOTYgMjExLjMgMzUyLjIgMTg4IDQxMiAxODhzMTE2LjEgMjMuMiAxNTguNCA2NS42UzYzNiAzNTIuMiA2MzYgNDEycy0yMy4zIDExNi4xLTY1LjYgMTU4LjR6IiBmaWxsPSJyZ2JhKDIwNCwyMDQsMjA0LDEpIj48L3BhdGg+CiAgICA8L2c+CiAgPC9zdmc+');
}
.pro-list-item-left-count {
font-size: 24rpx;
color: $text-color;
flex-wrap: nowrap;
margin-top: 4rpx;
}
:deep(.file-picker__progress) {
display: none !important;
}
</style>