6 changed files with 380 additions and 145 deletions
@ -0,0 +1,226 @@ |
|||
<template> |
|||
<view v-if="showViewToken" class="ring-timing" :style="containerStyles"> |
|||
<!-- #ifndef APP-NVUE --> |
|||
<view class="ring-timing-half ring-timing-left" :style="leftStyles" @transitionend="onTimingEnd" /> |
|||
<view class="ring-timing-half ring-timing-right" :style="rightStyles" /> |
|||
<!-- #endif --> |
|||
<!-- #ifdef APP-NVUE --> |
|||
<view :ref="`leftHalf${showViewToken}`" class="ring-timing-half ring-timing-left" :style="leftStyles" /> |
|||
<view :ref="`rightHalf${showViewToken}`" class="ring-timing-half ring-timing-right" :style="rightStyles" /> |
|||
<!-- #endif --> |
|||
<view class="ring-timing-center" :style="centerStyles"><slot /></view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
// #ifdef APP-NVUE |
|||
const animation = weex.requireModule('animation') |
|||
// #endif |
|||
|
|||
/** |
|||
* zwp-ring-timing 圆环计时器,不使用canvas和定时器,仅css |
|||
* @property {String} *mode 模式,可选值:timing定时器,chart当作图表用于展示数据(图表模式仅限看看,真要做图表还是用canvas好) |
|||
* @property {String} activeColor 进度条走过的颜色 |
|||
* @property {String} defaultColor 进度条底色 |
|||
* @property {String} centerBgColor 圆环中间区域的背景色 |
|||
* @property {Number} radius 圆形半径或整个组件的一半尺寸(包含了圆环的宽度) |
|||
* @property {Number} barWidth 圆环宽度 |
|||
* @property {Number} startDeg 进度开始的角度 |
|||
* @property {Number} duration timing模式的定时时间 |
|||
* @property {Number} value chart模式的值(1~100) |
|||
* @event {Function} timingEnd timing模式下定时完成的回调 |
|||
*/ |
|||
|
|||
export default { |
|||
props: { |
|||
mode: { |
|||
validator: value => ['timing', 'chart'].includes(value), |
|||
default: 'timing' |
|||
}, |
|||
activeColor: { |
|||
type: String, |
|||
default: '#42b983' |
|||
}, |
|||
defaultColor: { |
|||
type: String, |
|||
default: '#EEEEEE' |
|||
}, |
|||
centerBgColor: { |
|||
type: String, |
|||
default: '#FFFFFF' |
|||
}, |
|||
radius: { |
|||
type: Number, |
|||
default: 100 |
|||
}, |
|||
barWidth: { |
|||
type: Number, |
|||
default: 10 |
|||
}, |
|||
startDeg: { |
|||
type: Number, |
|||
default: 0 |
|||
}, |
|||
duration: { |
|||
type: Number, |
|||
default: 1 |
|||
}, |
|||
value: Number |
|||
}, |
|||
|
|||
data() { |
|||
return { |
|||
isStart: false, |
|||
showViewToken: Date.now() // nvue用来重置进度的 |
|||
} |
|||
}, |
|||
|
|||
computed: { |
|||
containerStyles() { |
|||
const { radius, startDeg, activeColor } = this |
|||
return { |
|||
borderRadius: `${radius}rpx`, |
|||
height: `${radius * 2}rpx`, |
|||
width: `${radius * 2}rpx`, |
|||
transform: `rotate(${startDeg}deg)`, |
|||
backgroundColor: activeColor |
|||
} |
|||
}, |
|||
leftStyles() { |
|||
const { mode, radius, defaultColor, isStart, duration, value } = this |
|||
return { |
|||
height: `${radius * 2}rpx`, |
|||
width: `${radius}rpx`, |
|||
backgroundColor: defaultColor, |
|||
borderTopLeftRadius: `${radius}rpx`, |
|||
borderBottomLeftRadius: `${radius}rpx`, |
|||
...(mode == 'timing' ? { |
|||
// #ifndef APP-NVUE |
|||
transitionDuration: `${isStart ? duration : 0}s`, |
|||
transform: `rotate(${isStart ? 180 : -180}deg)` |
|||
// #endif |
|||
} : { |
|||
transform: `rotate(${-180 + value * 3.6}deg)` |
|||
}) |
|||
} |
|||
}, |
|||
rightStyles() { |
|||
const { mode, radius, activeColor, defaultColor, isStart, duration, value } = this |
|||
return { |
|||
height: `${radius * 2}rpx`, |
|||
width: `${radius}rpx`, |
|||
backgroundColor: defaultColor, |
|||
borderTopRightRadius: `${radius}rpx`, |
|||
borderBottomRightRadius: `${radius}rpx`, |
|||
...(mode == 'timing' ? { |
|||
// #ifndef APP-NVUE |
|||
backgroundColor: isStart ? activeColor : defaultColor, |
|||
transitionDelay: `${isStart ? duration / 2 : 0}s`, |
|||
transform: `rotate(${isStart ? 0 : -180}deg)` |
|||
// #endif |
|||
} : { |
|||
backgroundColor: value >= 50 ? activeColor : defaultColor, |
|||
transform: `rotate(${value >= 50 ? 0 : -180}deg)`, |
|||
}) |
|||
} |
|||
}, |
|||
centerStyles() { |
|||
const { radius, centerBgColor, barWidth, startDeg } = this |
|||
return { |
|||
borderRadius: `${radius - barWidth}rpx`, |
|||
height: `${(radius - barWidth) * 2}rpx`, |
|||
width: `${(radius - barWidth) * 2}rpx`, |
|||
transform: `translate(-50%, -50%) rotate(-${startDeg}deg)`, |
|||
backgroundColor: centerBgColor, |
|||
left: `${radius}rpx`, |
|||
top: `${radius}rpx` |
|||
} |
|||
} |
|||
}, |
|||
|
|||
methods: { |
|||
// #ifdef APP-NVUE |
|||
createAnimation(direction, styles, callback = () => {}) { |
|||
let { showViewToken, duration } = this |
|||
let isLeft = direction == 'left' |
|||
animation.transition( |
|||
this.$refs[`${direction}Half${showViewToken}`], |
|||
{ |
|||
styles, |
|||
duration: isLeft ? duration * 1000 : 0, |
|||
delay: !isLeft ? (duration / 2) * 1000 : 0, |
|||
timingFunction: 'linear' |
|||
}, |
|||
callback |
|||
) |
|||
}, |
|||
// #endif |
|||
start() { |
|||
if (this.mode == 'chart') return |
|||
// #ifndef APP-NVUE |
|||
this.isStart = true |
|||
// #endif |
|||
// #ifdef APP-NVUE |
|||
const { createAnimation, activeColor, onTimingEnd } = this |
|||
createAnimation('left', { |
|||
transform: 'rotate(180deg)' |
|||
}, onTimingEnd) |
|||
createAnimation('right', { |
|||
backgroundColor: activeColor, |
|||
transform: 'rotate(0)' |
|||
}) |
|||
// #endif |
|||
}, |
|||
end() { |
|||
if (this.mode == 'chart') return |
|||
// #ifndef APP-NVUE |
|||
this.isStart = false |
|||
// #endif |
|||
// #ifdef APP-NVUE |
|||
this.showViewToken = 0 |
|||
this.$nextTick(() => { |
|||
this.showViewToken = Date.now() |
|||
}) |
|||
// #endif |
|||
}, |
|||
onTimingEnd() { |
|||
this.$emit('timingEnd') |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style scoped> |
|||
.ring-timing { |
|||
/* #ifndef APP-NVUE */ |
|||
display: flex; |
|||
/* #endif */ |
|||
flex-direction: row; |
|||
position: relative; |
|||
} |
|||
.ring-timing-half { |
|||
flex: 1; |
|||
/* #ifndef APP-NVUE */ |
|||
transition-property: transform, background-color; |
|||
transition-timing-function: linear; |
|||
/* #endif */ |
|||
/* #ifdef APP-NVUE */ |
|||
transform: rotate(-180deg); |
|||
/* #endif */ |
|||
} |
|||
.ring-timing-left { |
|||
transform-origin: right center; |
|||
} |
|||
.ring-timing-right { |
|||
transform-origin: left center; |
|||
} |
|||
.ring-timing-center { |
|||
/* #ifndef APP-NVUE */ |
|||
display: flex; |
|||
/* #endif */ |
|||
justify-content: center; |
|||
align-items: center; |
|||
overflow: hidden; |
|||
position: absolute; |
|||
} |
|||
</style> |
@ -1,2 +1,8 @@ |
|||
// 上传文件的扩展名
|
|||
export const UPLOAD_EXTENSION = ['.xls', '.xlsx', '.zip', '.exe', '.pdf', '.doc', '.docx', '.ppt', '.pptx']; |
|||
|
|||
// 审核的快捷用语
|
|||
export const quickWords = { |
|||
RESOLVE: ['加油,再接再厉!', '很棒!', '不错,很详细!', '加油,再接再厉'], // 审核通过常用的审批语
|
|||
REJECT: ['不详细', '还有需要改进的地方', '驳回审批1', '驳回审批2'], // 审核驳回常用的审批语
|
|||
}; |
|||
|
@ -0,0 +1,85 @@ |
|||
<template> |
|||
<!-- 审核通过的modal --> |
|||
<u-mask :show="data.mode !== 'HIDE'" @click="handleHide"> |
|||
<view class="modal-content-wrap" @click.stop> |
|||
<!-- 通过modal的标题 --> |
|||
<view class="modal-content-head"> {{ data.mode === 'RESOLVE' ? '审核通过' : '审核驳回' }} </view> |
|||
|
|||
<view class="modal-content-body"> |
|||
<!-- 评分 --> |
|||
<view class="flex justify-between mb-4" v-show="data.mode === 'RESOLVE'"> |
|||
<u-number-box v-model="score" size="30" input-width="50" :max="100" :min="0" :step="1"></u-number-box> |
|||
<view class="w-32 pt-4"> |
|||
<u-slider v-model="score" active-color="#34D399" :max="100" :min="0" :step="1"></u-slider> |
|||
</view> |
|||
</view> |
|||
|
|||
<u-input v-model="commit" type="textarea" :border="true" :auto-height="true" /> |
|||
|
|||
<view class="common-list"> |
|||
<view v-for="item in words" class="leading-12" @click="commit = item"> |
|||
{{ item }} |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="modal-content-foot"> |
|||
<view class="cancel" @click="handleHide"> 取消 </view> |
|||
<view class="confirm" @click="handleSubmit(data.mode)"> 确定 </view> |
|||
</view> |
|||
</view> |
|||
</u-mask> |
|||
</template> |
|||
|
|||
<script setup> |
|||
import { ref, computed } from 'vue'; |
|||
import { useStore } from 'vuex'; |
|||
import { quickWords } from '@/config/deliver'; |
|||
|
|||
const props = defineProps({ |
|||
data: { type: Object, default: {} }, |
|||
}); |
|||
const emits = defineEmits(['hide', 'submit-end']); |
|||
const store = useStore(); |
|||
const words = computed(() => quickWords[props.data.mode]); // 快捷用语 |
|||
const projectId = computed(() => store.getters['project/projectId']); |
|||
|
|||
const commit = ref(''); // 提交的信息 |
|||
const score = ref(100); // 评分 |
|||
|
|||
/** |
|||
* 提交评审信息 |
|||
* 提交成功后隐藏modal 重置表单控件 |
|||
* 给父组件信息 更新值 |
|||
* @param {string} mode 'RESOLVE'|'REJECT' |
|||
*/ |
|||
async function handleSubmit(mode) { |
|||
console.log('mode: ', mode); |
|||
try { |
|||
const deliverRecordId = props.data.deliverRecordId(); |
|||
const param = { |
|||
projectId: projectId.value, |
|||
deliverRecordId, |
|||
type: mode === 'RESOLVE' ? 1 : 2, |
|||
remark: commit.value, |
|||
score: mode === 'RESOLVE' ? score.value : '', |
|||
}; |
|||
await uni.$u.api.checkDeliver(param); |
|||
handleHide(); // 隐藏 + 重置 |
|||
uni.$ui.showToast('审核信息提交成功'); |
|||
// 通知父组件评审成功 更新信息 |
|||
emits('submit-end', mode); |
|||
} catch (error) { |
|||
console.error('error: ', error); |
|||
uni.$ui.showToast('审核信息提交失败, 请稍后重试'); |
|||
} |
|||
} |
|||
|
|||
// 隐藏及 重置 |
|||
function handleHide() { |
|||
emits('hide'); |
|||
// 重置相关数据 |
|||
score.value = 100; |
|||
commit.value = ''; |
|||
} |
|||
</script> |
Loading…
Reference in new issue