Browse Source

feat: 检查交付物

pull/1/head
song 4 years ago
parent
commit
9d92be5aec
  1. 3
      CHANGELOG.md
  2. 23
      src/components/Plugin/Plugin.vue
  3. 28
      src/components/ld-select/ld-select.vue
  4. 22
      src/components/uni-popup/message.js
  5. 23
      src/components/uni-popup/popup.js
  6. 246
      src/components/uni-popup/uni-popup-dialog.vue
  7. 115
      src/components/uni-popup/uni-popup-message.vue
  8. 171
      src/components/uni-popup/uni-popup-share.vue
  9. 289
      src/components/uni-popup/uni-popup.vue
  10. 276
      src/components/uni-transition/uni-transition.vue
  11. 40
      src/plugins/p-deliver-check/p-deliver-check.vue
  12. 95
      src/plugins/p-delivery-history/p-delivery-history.vue
  13. 18
      src/plugins/p-upload-deliverable/p-upload-deliverable.vue
  14. 8
      src/store/role/getters.js

3
CHANGELOG.md

@ -1,4 +1,4 @@
# 0.1.0 (2021-08-27)
# 0.1.0 (2021-08-30)
### 🌟 新功能
范围|描述|commitId
@ -44,6 +44,7 @@
- | 标题栏变化 | [c0fcd9d](https://dd.tall.wiki/gitea/wally/tall-mui-3-project/commits/c0fcd9d)
- | 标题栏角色栏全局任务组件新建 | [0500cb4](https://dd.tall.wiki/gitea/wally/tall-mui-3-project/commits/0500cb4)
- | 模拟接口测试 | [69e7931](https://dd.tall.wiki/gitea/wally/tall-mui-3-project/commits/69e7931)
- | 添加内置插件-交付物 | [6e0bc46](https://dd.tall.wiki/gitea/wally/tall-mui-3-project/commits/6e0bc46)
- | 添加子任务插件 子项目插件 | [7bda7e2](https://dd.tall.wiki/gitea/wally/tall-mui-3-project/commits/7bda7e2)
- | 添加时间轴上下滚动 | [2b81bbc](https://dd.tall.wiki/gitea/wally/tall-mui-3-project/commits/2b81bbc)
- | 添加项目排序 | [a0b491b](https://dd.tall.wiki/gitea/wally/tall-mui-3-project/commits/a0b491b)

23
src/components/Plugin/Plugin.vue

@ -2,19 +2,19 @@
<view class="u-font-14" style="height: 100%">
<view v-if="pluginContent">
<view
style="height: 100%"
:data-uid="userId"
:data-token="token"
:data-pid="projectId"
:data-did="task.detailId"
:data-param="param"
:data-pdu="task.planDuration"
:data-pid="projectId"
:data-pstart="task.planStart"
:data-rdu="task.realDuration"
:data-rid="roleId"
:data-rstart="task.realStart"
:data-tid="task.id"
:data-tname="task.name"
:data-pstart="task.planStart"
:data-rstart="task.realStart"
:data-pdu="task.planDuration"
:data-rdu="task.realDuration"
:data-param="param"
:data-token="token"
:data-uid="userId"
style="height: 100%"
v-html="pluginContent"
></view>
</view>
@ -26,9 +26,8 @@
<p-task-description :task="task" v-if="pluginId === '2'" />
<p-task-duration-delay :task="task" v-if="pluginId === '3'" />
<p-task-start-time-delay :task="task" v-if="pluginId === '4'" />
<!-- <p-upload-deliverable :task="task" v-if="pluginId === '5' && isMine" /> -->
<p-upload-deliverable :task="task" v-if="pluginId === '5'" />
<!-- <p-delivery-history :task="task" v-if="pluginId === '5' && !isMine" /> -->
<p-upload-deliverable :task="task" v-if="pluginId === '5' && isMine" />
<p-delivery-history :task="task" v-if="pluginId === '5' && !isMine" />
<p-subtasks :task="task" v-if="pluginId === '6'" />
<p-subproject :task="task" v-if="pluginId === '7'" />
<!-- <p-task-countdown :task="task" v-if="pluginId === '8'" /> -->

28
src/components/ld-select/ld-select.vue

@ -19,7 +19,7 @@
@click="select(item)"
>
<view class="title">{{ getLabelKeyValue(item) }}</view>
<text class="selectIcon icongou" v-if="valueIndexOf(item)"></text>
<u-icon name="checkbox-mark" v-if="valueIndexOf(item)"></u-icon>
</view>
</view>
</view>
@ -207,30 +207,8 @@ export default {
font-family: 'selectIcon';
src: url('//at.alicdn.com/t/font_1833441_ycfzdhg2u3.eot?t=1590375117208'); /* IE9 */
src: url('//at.alicdn.com/t/font_1833441_ycfzdhg2u3.eot?t=1590375117208#iefix') format('embedded-opentype'),
/* IE6-IE8 */
/* url('data:application/x-font-woff2;charset=utf-8;base64,
d09GMgABAAAAAAMEAAsAAAAABvQAAAK4AAEAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAHEIGVgCDBgqBRIFCATYCJAMMCwgABCAFhQUHNRsfBsg+QCa3uoO0oAJTMwhx
Vu965keqWBy1hkbwtfzWb2Z279/shRhJisKF6FApKLI7oyBbpAaHo3w24k+ca9
EUJbDmjaeznUdZ/FOUlkWdJ33rizZY/Pw6J5Xw0qKYxHTMesePHVT6EFpaC4zV
70sKi2bYgNPc1w0WHnDVC/e/UnNTgyP+4Jq6BBpIHoisgypLaIAFEtU0wgeaIG
8Yu4nAIZwnUK1QgFfOT6nUUoBpgXjj2lqplTMpiuXtCW3N2iK+aPTS2/Qdnzny8
d+5IEiaDMy99exklra//FrKnX48pChmgrq5QcYRQCEe17ruqgqLAKv8Wntwqwhp
Lms/nB5yW/iHRxJEC0QOgT3NnfgF01NBKvOuIzNoZdh5gJuAeGrsozE8vOJ7u5D
832oz55039W5G+S52K0H+zNf1TJz07k26kqoQybRfwVFV4rjDS/K8EXUyuF1cX
nT3weKS9Rvdm/xe7h8oA1hLwOR18R+Y4n4zwpr4z5SU089Vc+cpfWL+mn5APmT3
Z39jeOs/GbWjK+DnmsuL/u6ehMX4j4yedSVkAUUuPh3TY022MtKZUEOtPqCb8Bkv
nr5XT6imU0gGrEJW7aAL/gw0OhegVV2F6pC7uTOppirKIA4MFQhTrpCM+AbZlDu6
4L/QmAkQWlMhQXU75D07O9Gtl0PUYjTBLyAzOLNQYtypIEEjvsXtBLQTooV2nrQr
GEau2gKmZlR4L8gwnGtBJbUn1diCOOQUnEkTkRAOeci9KHOQxvFro+tx3ZcGAael
jstCSBNDJuArgIyBYyy6OdZxAhHIELu1IC9AtgShCVtLltEKrSff1XoHJo3RC33h
M63o3j6pSNkmqmIWEAtxFHB2OwoRBAfyeqE3r2ogHeF42dBhs7gvf7CukH5MmlU
GOCpHihxFfs6TehDyKCqVAA==')
format('woff2'),
url('//at.alicdn.com/t/font_1833441_ycfzdhg2u3.woff?t=1590375117208') format('woff'),
url('//at.alicdn.com/t/font_1833441_ycfzdhg2u3.ttf?t=1590375117208') format('truetype'), */
/* chrome, firefox, opera, Safari, Android, iOS 4.2+ */
url('//at.alicdn.com/t/font_1833441_ycfzdhg2u3.svg?t=1590375117208#selectIcon') format('svg'); /* iOS 4.1- */
/* chrome, firefox, opera, Safari, Android, iOS 4.2+ */ url('//at.alicdn.com/t/font_1833441_ycfzdhg2u3.svg?t=1590375117208#selectIcon')
format('svg'); /* iOS 4.1- */
}
.selectIcon {

22
src/components/uni-popup/message.js

@ -0,0 +1,22 @@
export default {
created() {
if (this.type === 'message') {
// 不显示遮罩
this.maskShow = false;
// 获取子组件对象
this.childrenMsg = null;
}
},
methods: {
customOpen() {
if (this.childrenMsg) {
this.childrenMsg.open();
}
},
customClose() {
if (this.childrenMsg) {
this.childrenMsg.close();
}
},
},
};

23
src/components/uni-popup/popup.js

@ -0,0 +1,23 @@
import message from './message.js';
// 定义 type 类型:弹出类型:top/bottom/center
const config = {
// 顶部弹出
top: 'top',
// 底部弹出
bottom: 'bottom',
// 居中弹出
center: 'center',
// 消息提示
message: 'top',
// 对话框
dialog: 'center',
// 分享
share: 'bottom',
};
export default {
data() {
return { config: config };
},
mixins: [message],
};

246
src/components/uni-popup/uni-popup-dialog.vue

@ -0,0 +1,246 @@
<template>
<view class="uni-popup-dialog">
<view class="uni-dialog-title">
<text class="uni-dialog-title-text" :class="['uni-popup__' + dialogType]">{{ title }}</text>
</view>
<view class="uni-dialog-content">
<text class="uni-dialog-content-text" v-if="mode === 'base'">{{ content }}</text>
<input v-else class="uni-dialog-input" v-model="val" type="text" :placeholder="placeholder" :focus="focus" />
</view>
<view class="uni-dialog-button-group">
<view class="uni-dialog-button" @click="close">
<text class="uni-dialog-button-text">取消</text>
</view>
<view class="uni-dialog-button uni-border-left" @click="onOk">
<text class="uni-dialog-button-text uni-button-color">确定</text>
</view>
</view>
</view>
</template>
<script>
/**
* PopUp 弹出层-对话框样式
* @description 弹出层-对话框样式
* @tutorial https://ext.dcloud.net.cn/plugin?id=329
* @property {String} value input 模式下的默认值
* @property {String} placeholder input 模式下输入提示
* @property {String} type = [success|warning|info|error] 主题样式
* @value success 成功
* @value warning 提示
* @value info 消息
* @value error 错误
* @property {String} mode = [base|input] 模式
* @value base 基础对话框
* @value input 可输入对话框
* @property {String} content 对话框内容
* @property {Boolean} beforeClose 是否拦截取消事件
* @event {Function} confirm 点击确认按钮触发
* @event {Function} close 点击取消按钮触发
*/
export default {
name: 'uniPopupDialog',
props: {
value: {
type: [String, Number],
default: '',
},
placeholder: {
type: [String, Number],
default: '请输入内容',
},
/**
* 对话框主题 success/warning/info/error 默认 success
*/
type: {
type: String,
default: 'error',
},
/**
* 对话框模式 base/input
*/
mode: {
type: String,
default: 'base',
},
/**
* 对话框标题
*/
title: {
type: String,
default: '提示',
},
/**
* 对话框内容
*/
content: {
type: String,
default: '',
},
/**
* 拦截取消事件 如果拦截取消事件必须监听close事件执行 done()
*/
beforeClose: {
type: Boolean,
default: false,
},
},
data() {
return {
dialogType: 'error',
focus: false,
val: '',
};
},
inject: ['popup'],
watch: {
type(val) {
this.dialogType = val;
},
mode(val) {
if (val === 'input') {
this.dialogType = 'info';
}
},
value(val) {
this.val = val;
},
},
created() {
//
this.popup.mkclick = false;
if (this.mode === 'input') {
this.dialogType = 'info';
this.val = this.value;
} else {
this.dialogType = this.type;
}
},
mounted() {
this.focus = true;
},
methods: {
/**
* 点击确认按钮
*/
onOk() {
this.$emit(
'confirm',
() => {
this.popup.close();
if (this.mode === 'input') this.val = this.value;
},
this.mode === 'input' ? this.val : '',
);
},
/**
* 点击取消按钮
*/
close() {
if (this.beforeClose) {
this.$emit('close', () => {
this.popup.close();
});
return;
}
this.popup.close();
},
},
};
</script>
<style lang="scss" scoped>
.uni-popup-dialog {
width: 300px;
border-radius: 15px;
background-color: #fff;
}
.uni-dialog-title {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
justify-content: center;
padding-top: 15px;
padding-bottom: 5px;
}
.uni-dialog-title-text {
font-size: 16px;
font-weight: 500;
}
.uni-dialog-content {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
justify-content: center;
align-items: center;
padding: 5px 15px 15px 15px;
}
.uni-dialog-content-text {
font-size: 14px;
color: #6e6e6e;
}
.uni-dialog-button-group {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
border-top-color: #f5f5f5;
border-top-style: solid;
border-top-width: 1px;
}
.uni-dialog-button {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex: 1;
flex-direction: row;
justify-content: center;
align-items: center;
height: 45px;
}
.uni-border-left {
border-left-color: #f0f0f0;
border-left-style: solid;
border-left-width: 1px;
}
.uni-dialog-button-text {
font-size: 14px;
}
.uni-button-color {
color: $uni-color-primary;
}
.uni-dialog-input {
flex: 1;
font-size: 14px;
}
.uni-popup__success {
color: $uni-color-success;
}
.uni-popup__warn {
color: $uni-color-warning;
}
.uni-popup__error {
color: $uni-color-error;
}
.uni-popup__info {
color: #909399;
}
</style>

115
src/components/uni-popup/uni-popup-message.vue

@ -0,0 +1,115 @@
<template>
<view class="uni-popup-message" :class="'uni-popup__' + [type]">
<text class="uni-popup-message-text" :class="'uni-popup__' + [type] + '-text'">{{ message }}</text>
</view>
</template>
<script>
/**
* PopUp 弹出层-消息提示
* @description 弹出层-消息提示
* @tutorial https://ext.dcloud.net.cn/plugin?id=329
* @property {String} type = [success|warning|info|error] 主题样式
* @value success 成功
* @value warning 提示
* @value info 消息
* @value error 错误
* @property {String} message 消息提示文字
* @property {String} duration 显示时间设置为 0 则不会自动关闭
*/
export default {
name: 'UniPopupMessage',
props: {
/**
* 主题 success/warning/info/error 默认 success
*/
type: {
type: String,
default: 'success',
},
/**
* 消息文字
*/
message: {
type: String,
default: '',
},
/**
* 显示时间设置为 0 则不会自动关闭
*/
duration: {
type: Number,
default: 3000,
},
},
inject: ['popup'],
data() {
return {};
},
created() {
this.popup.childrenMsg = this;
},
methods: {
open() {
if (this.duration === 0) return;
clearTimeout(this.popuptimer);
this.popuptimer = setTimeout(() => {
this.popup.close();
}, this.duration);
},
close() {
clearTimeout(this.popuptimer);
},
},
};
</script>
<style lang="scss" scoped>
.uni-popup-message {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
background-color: #e1f3d8;
padding: 10px 15px;
border-color: #eee;
border-style: solid;
border-width: 1px;
}
.uni-popup-message-text {
font-size: 14px;
padding: 0;
}
.uni-popup__success {
background-color: #e1f3d8;
}
.uni-popup__success-text {
color: #67c23a;
}
.uni-popup__warn {
background-color: #faecd8;
}
.uni-popup__warn-text {
color: #e6a23c;
}
.uni-popup__error {
background-color: #fde2e2;
}
.uni-popup__error-text {
color: #f56c6c;
}
.uni-popup__info {
background-color: #f2f6fc;
}
.uni-popup__info-text {
color: #909399;
}
</style>

171
src/components/uni-popup/uni-popup-share.vue

@ -0,0 +1,171 @@
<template>
<view class="uni-popup-share">
<view class="uni-share-title">
<text class="uni-share-title-text">{{ title }}</text>
</view>
<view class="uni-share-content">
<view class="uni-share-content-box">
<view class="uni-share-content-item" v-for="(item, index) in bottomData" :key="index" @click.stop="select(item, index)">
<image class="uni-share-image" :src="item.icon" mode="aspectFill"></image>
<text class="uni-share-text">{{ item.text }}</text>
</view>
</view>
</view>
<view class="uni-share-button-box">
<button class="uni-share-button" @click="close">取消</button>
</view>
</view>
</template>
<script>
export default {
name: 'UniPopupShare',
props: {
title: {
type: String,
default: '分享到',
},
},
inject: ['popup'],
data() {
return {
bottomData: [
{
text: '微信',
icon: 'https://img-cdn-qiniu.dcloud.net.cn/uni-ui/grid-2.png',
name: 'wx',
},
{
text: '支付宝',
icon: 'https://img-cdn-qiniu.dcloud.net.cn/uni-ui/grid-8.png',
name: 'wx',
},
{
text: 'QQ',
icon: 'https://img-cdn-qiniu.dcloud.net.cn/uni-ui/gird-3.png',
name: 'qq',
},
{
text: '新浪',
icon: 'https://img-cdn-qiniu.dcloud.net.cn/uni-ui/grid-1.png',
name: 'sina',
},
{
text: '百度',
icon: 'https://img-cdn-qiniu.dcloud.net.cn/uni-ui/grid-7.png',
name: 'copy',
},
{
text: '其他',
icon: 'https://img-cdn-qiniu.dcloud.net.cn/uni-ui/grid-5.png',
name: 'more',
},
],
};
},
created() {},
methods: {
/**
* 选择内容
*/
select(item, index) {
this.$emit(
'select',
{
item,
index,
},
() => {
this.popup.close();
},
);
},
/**
* 关闭窗口
*/
close() {
this.popup.close();
},
},
};
</script>
<style lang="scss" scoped>
.uni-popup-share {
background-color: #fff;
}
.uni-share-title {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
align-items: center;
justify-content: center;
height: 40px;
}
.uni-share-title-text {
font-size: 14px;
color: #666;
}
.uni-share-content {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
justify-content: center;
padding-top: 10px;
}
.uni-share-content-box {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
flex-wrap: wrap;
width: 360px;
}
.uni-share-content-item {
width: 90px;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
justify-content: center;
padding: 10px 0;
align-items: center;
}
.uni-share-content-item:active {
background-color: #f5f5f5;
}
.uni-share-image {
width: 30px;
height: 30px;
}
.uni-share-text {
margin-top: 10px;
font-size: 14px;
color: #3b4144;
}
.uni-share-button-box {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
padding: 10px 15px;
}
.uni-share-button {
flex: 1;
border-radius: 50px;
color: #666;
font-size: 16px;
}
.uni-share-button::after {
border-radius: 50px;
}
</style>

289
src/components/uni-popup/uni-popup.vue

@ -0,0 +1,289 @@
<template>
<view v-if="showPopup" class="uni-popup" :class="[popupstyle]" @touchmove.stop.prevent="clear">
<uni-transition v-if="maskShow" :mode-class="['fade']" :styles="maskClass" :duration="duration" :show="showTrans" @click="onTap" />
<uni-transition :mode-class="ani" :styles="transClass" :duration="duration" :show="showTrans" @click="onTap">
<view class="uni-popup__wrapper-box" @click.stop="clear">
<slot />
</view>
</uni-transition>
</view>
</template>
<script>
import uniTransition from '../uni-transition/uni-transition.vue';
import popup from './popup.js';
/**
* PopUp 弹出层
* @description 弹出层组件为了解决遮罩弹层的问题
* @tutorial https://ext.dcloud.net.cn/plugin?id=329
* @property {String} type = [top|center|bottom] 弹出方式
* @value top 顶部弹出
* @value center 中间弹出
* @value bottom 底部弹出
* @value message 消息提示
* @value dialog 对话框
* @value share 底部分享示例
* @property {Boolean} animation = [ture|false] 是否开启动画
* @property {Boolean} maskClick = [ture|false] 蒙版点击是否关闭弹窗
* @event {Function} change 打开关闭弹窗触发e={show: false}
*/
export default {
name: 'UniPopup',
components: { uniTransition },
props: {
//
animation: {
type: Boolean,
default: true,
},
// top: bottomcenter
// message: ; dialog :
type: {
type: String,
default: 'center',
},
// maskClick
maskClick: {
type: Boolean,
default: true,
},
},
provide() {
return { popup: this };
},
mixins: [popup],
watch: {
/**
* 监听type类型
*/
type: {
handler: function (newVal) {
this[this.config[newVal]]();
},
immediate: true,
},
/**
* 监听遮罩是否可点击
* @param {Object} val
*/
maskClick(val) {
this.mkclick = val;
},
},
data() {
return {
duration: 300,
ani: [],
showPopup: false,
showTrans: false,
maskClass: {
position: 'fixed',
bottom: 0,
top: 0,
left: 0,
right: 0,
backgroundColor: 'rgba(0, 0, 0, 0.4)',
},
transClass: {
position: 'fixed',
left: 0,
right: 0,
},
maskShow: true,
mkclick: true,
popupstyle: 'top',
};
},
created() {
this.mkclick = this.maskClick;
if (this.animation) {
this.duration = 300;
} else {
this.duration = 0;
}
},
methods: {
clear(e) {
// TODO nvue
e.stopPropagation();
},
open() {
this.showPopup = true;
this.$nextTick(() => {
new Promise(resolve => {
clearTimeout(this.timer);
this.timer = setTimeout(() => {
this.showTrans = true;
// fixed by mehaotian app
this.$nextTick(() => {
resolve();
});
}, 50);
}).then(res => {
console.log('res: ', res);
//
clearTimeout(this.msgtimer);
this.msgtimer = setTimeout(() => {
this.customOpen && this.customOpen();
}, 100);
this.$emit('change', {
show: true,
type: this.type,
});
});
});
},
close(type) {
this.showTrans = false;
this.$nextTick(() => {
this.$emit('change', {
show: false,
type,
});
clearTimeout(this.timer);
//
this.customOpen && this.customClose();
this.timer = setTimeout(() => {
this.showPopup = false;
}, 300);
});
},
onTap() {
if (!this.mkclick) return;
this.close();
},
/**
* 顶部弹出样式处理
*/
top() {
this.popupstyle = 'top';
this.ani = ['slide-top'];
this.transClass = {
position: 'fixed',
left: 0,
right: 0,
};
},
/**
* 底部弹出样式处理
*/
bottom() {
this.popupstyle = 'bottom';
this.ani = ['slide-bottom'];
this.transClass = {
position: 'fixed',
left: 0,
right: 0,
bottom: 0,
};
},
/**
* 中间弹出样式处理
*/
center() {
this.popupstyle = 'center';
this.ani = ['zoom-out', 'fade'];
this.transClass = {
position: 'fixed',
/* #ifndef APP-NVUE */
display: 'flex',
flexDirection: 'column',
/* #endif */
bottom: 0,
left: 0,
right: 0,
top: 0,
justifyContent: 'center',
alignItems: 'center',
};
},
},
};
</script>
<style lang="scss" scoped>
.uni-popup {
position: fixed;
/* #ifndef APP-NVUE */
z-index: 99;
/* #endif */
}
.uni-popup__mask {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
background-color: $uni-bg-color-mask;
opacity: 0;
}
.mask-ani {
transition-property: opacity;
transition-duration: 0.2s;
}
.uni-top-mask {
opacity: 1;
}
.uni-bottom-mask {
opacity: 1;
}
.uni-center-mask {
opacity: 1;
}
.uni-popup__wrapper {
/* #ifndef APP-NVUE */
display: block;
/* #endif */
position: absolute;
}
.top {
/* #ifdef H5 */
top: var(--window-top);
/* #endif */
/* #ifndef H5 */
top: 0;
/* #endif */
}
.bottom {
bottom: 0;
}
.uni-popup__wrapper-box {
/* #ifndef APP-NVUE */
display: block;
/* #endif */
position: relative;
/* iphonex 等安全区设置,底部安全区适配 */
/* #ifndef APP-NVUE */
padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
/* #endif */
}
.content-ani {
// transition: transform 0.3s;
transition-property: transform, opacity;
transition-duration: 0.2s;
}
.uni-top-content {
transform: translateY(0);
}
.uni-bottom-content {
transform: translateY(0);
}
.uni-center-content {
transform: scale(1);
opacity: 1;
}
</style>

276
src/components/uni-transition/uni-transition.vue

@ -0,0 +1,276 @@
<template>
<view
v-if="isShow"
ref="ani"
class="uni-transition"
:class="[ani.in]"
:style="'transform:' + transform + ';' + stylesObject"
@click="change"
>
<slot></slot>
</view>
</template>
<script>
// #ifdef APP-NVUE
const animation = uni.requireNativePlugin('animation');
// #endif
/**
* Transition 过渡动画
* @description 简单过渡动画组件
* @tutorial https://ext.dcloud.net.cn/plugin?id=985
* @property {Boolean} show = [false|true] 控制组件显示或隐藏
* @property {Array} modeClass = [fade|slide-top|slide-right|slide-bottom|slide-left|zoom-in|zoom-out] 过渡动画类型
* @value fade 渐隐渐出过渡
* @value slide-top 由上至下过渡
* @value slide-right 由右至左过渡
* @value slide-bottom 由下至上过渡
* @value slide-left 由左至右过渡
* @value zoom-in 由小到大过渡
* @value zoom-out 由大到小过渡
* @property {Number} duration 过渡动画持续时间
* @property {Object} styles 组件样式 css 样式注意带-连接符的属性需要使用小驼峰写法如`backgroundColor:red`
*/
export default {
name: 'uniTransition',
props: {
show: {
type: Boolean,
default: false,
},
modeClass: {
type: Array,
default() {
return [];
},
},
duration: {
type: Number,
default: 300,
},
styles: {
type: Object,
default() {
return {};
},
},
},
data() {
return {
isShow: false,
transform: '',
ani: { in: '', active: '' },
};
},
watch: {
show: {
handler(newVal) {
if (newVal) {
this.open();
} else {
this.close();
}
},
immediate: true,
},
},
computed: {
stylesObject() {
let styles = {
...this.styles,
'transition-duration': this.duration / 1000 + 's',
};
let transfrom = '';
for (let i in styles) {
let line = this.toLine(i);
transfrom += line + ':' + styles[i] + ';';
}
return transfrom;
},
},
created() {
// this.timer = null
// this.nextTick = (time = 50) => new Promise(resolve => {
// clearTimeout(this.timer)
// this.timer = setTimeout(resolve, time)
// return this.timer
// });
},
methods: {
change() {
this.$emit('click', { detail: this.isShow });
},
open() {
clearTimeout(this.timer);
this.isShow = true;
this.transform = '';
this.ani.in = '';
for (let i in this.getTranfrom(false)) {
if (i === 'opacity') {
this.ani.in = 'fade-in';
} else {
this.transform += `${this.getTranfrom(false)[i]} `;
}
}
this.$nextTick(() => {
setTimeout(() => {
this._animation(true);
}, 50);
});
},
close() {
clearTimeout(this.timer);
this._animation(false);
},
_animation(type) {
let styles = this.getTranfrom(type);
// #ifdef APP-NVUE
if (!this.$refs['ani']) return;
animation.transition(
this.$refs['ani'].ref,
{
styles,
duration: this.duration, //ms
timingFunction: 'ease',
needLayout: false,
delay: 0, //ms
},
() => {
if (!type) {
this.isShow = false;
}
this.$emit('change', { detail: this.isShow });
},
);
// #endif
// #ifndef APP-NVUE
this.transform = '';
for (let i in styles) {
if (i === 'opacity') {
this.ani.in = `fade-${type ? 'out' : 'in'}`;
} else {
this.transform += `${styles[i]} `;
}
}
this.timer = setTimeout(() => {
if (!type) {
this.isShow = false;
}
this.$emit('change', { detail: this.isShow });
}, this.duration);
// #endif
},
getTranfrom(type) {
let styles = { transform: '' };
this.modeClass.forEach(mode => {
switch (mode) {
case 'fade':
styles.opacity = type ? 1 : 0;
break;
case 'slide-top':
styles.transform += `translateY(${type ? '0' : '-100%'}) `;
break;
case 'slide-right':
styles.transform += `translateX(${type ? '0' : '100%'}) `;
break;
case 'slide-bottom':
styles.transform += `translateY(${type ? '0' : '100%'}) `;
break;
case 'slide-left':
styles.transform += `translateX(${type ? '0' : '-100%'}) `;
break;
case 'zoom-in':
styles.transform += `scale(${type ? 1 : 0.8}) `;
break;
case 'zoom-out':
styles.transform += `scale(${type ? 1 : 1.2}) `;
break;
}
});
return styles;
},
_modeClassArr(type) {
let mode = this.modeClass;
if (typeof mode !== 'string') {
let modestr = '';
mode.forEach(item => {
modestr += item + '-' + type + ',';
});
return modestr.substr(0, modestr.length - 1);
} else {
return mode + '-' + type;
}
},
// getEl(el) {
// console.log(el || el.ref || null);
// return el || el.ref || null
// },
toLine(name) {
return name.replace(/([A-Z])/g, '-$1').toLowerCase();
},
},
};
</script>
<style>
.uni-transition {
transition-timing-function: ease;
transition-duration: 0.3s;
transition-property: transform, opacity;
}
.fade-in {
opacity: 0;
}
.fade-active {
opacity: 1;
}
.slide-top-in {
/* transition-property: transform, opacity; */
transform: translateY(-100%);
}
.slide-top-active {
transform: translateY(0);
/* opacity: 1; */
}
.slide-right-in {
transform: translateX(100%);
}
.slide-right-active {
transform: translateX(0);
}
.slide-bottom-in {
transform: translateY(100%);
}
.slide-bottom-active {
transform: translateY(0);
}
.slide-left-in {
transform: translateX(-100%);
}
.slide-left-active {
transform: translateX(0);
opacity: 1;
}
.zoom-in-in {
transform: scale(0.8);
}
.zoom-out-active {
transform: scale(1);
}
.zoom-out-in {
transform: scale(1.2);
}
</style>

40
src/plugins/p-deliver-check/p-deliver-check.vue

@ -1,11 +1,17 @@
<template>
<!-- 上传交付物 -->
<view class="py-2">
<u-input v-model="content" :type="type" :border="border" :height="height" :auto-height="autoHeight" />
<view class="px-3 py-6 bg-white">
<u-input :auto-height="autoHeight" :border="border" :height="height" :type="type" placeholder="输入备注" v-model="remark" />
<view class="flex flex-row-reverse text-xs text-gray-400 mt-2">{{ wordNum }}/140</view>
<!-- 评分 -->
<view class="flex justify-between mt-3">
<slider :value="score" @change="sliderChange" max="100" min="0" show-value style="width: 60%" />
<u-input :border="border" :type="type1" @input="changeNumber" maxlength="100" placeholder="输入分数" v-model="score" />
</view>
<view class="flex justify-between">
<u-button type="primary" size="mini" class="m-0" @click="submit">提交</u-button>
<view class="flex flex-col justify-center mt-5">
<u-button @click="submit" size="medium" type="primary">提交</u-button>
<u-button @click="$emit('closeScore')" class="mt-2" size="medium">取消</u-button>
</view>
</view>
</template>
@ -15,28 +21,40 @@ export default {
name: 'p-deliver-check',
data() {
return {
content: '',
remark: '',
type: 'textarea',
border: true,
height: 100,
autoHeight: true,
wordNum: 0,
score: 0,
type1: 'number',
};
},
watch: {
content(val) {
remark(val) {
this.wordNum = val.length;
},
score(val) {
this.score1 = val;
},
},
methods: {
//
async submit() {
try {
console.log('检查');
} catch (error) {
console.error('p-deliver-check.vue submit error: ', error);
submit() {
this.$emit('submit', this.remark, this.score);
},
sliderChange(e) {
this.score = e.detail.value;
},
changeNumber(e) {
if (e > 100) {
this.score = 100;
}
},
},

95
src/plugins/p-delivery-history/p-delivery-history.vue

@ -1,37 +1,61 @@
<template>
<!-- 交付物 -->
<view class="p-3 shadow">
<view v-for="list in lists" :key="list.id">
<view class="text-gray-400 pb-2">
<span class="mr-4">{{ list.name }}</span>
<span>{{ $moment(+list.time).format('YYYY-MM-DD HH:mm:ss') }}</span>
</view>
<view class="pb-2"> {{ list.content }} </view>
<view class="flex justify-between" v-for="checker in list.checkerList" :key="checker.checkerId">
<view>{{ checker.checkerName }}<span v-if="checker.isMine">()</span></view>
<view v-if="!checker.isMine">
<span class="text-blue-500" v-if="checker.status === 1">通过</span>
<span class="text-red-500" v-if="checker.status === 2">驳回</span>
<span class="text-gray-400" v-if="checker.status === 0 && !checker.isMine">未审核</span>
<view v-if="checker.status === 0 && checker.isMine">
<u-button plain type="error" size="mini" @click="checkDeliver(checker.checkerId, 2)">驳回</u-button>
<u-button plain type="primary" size="mini" @click="checkDeliver(checker.checkerId, 2)">通过</u-button>
<view class="mt-3">
<view v-if="lists && lists.length">
<view :key="list.id" v-for="list in lists">
<view class="p-3 mt-3 shadow">
<view class="text-gray-400 pb-2">
<span class="mr-4">{{ list.name }}</span>
<span>{{ $moment(+list.time).format('YYYY-MM-DD HH:mm:ss') }}</span>
</view>
<view class="pb-2 flex flex-wrap overflow-hidden" v-if="list.content">
<a :href="list.content" class="text-blue-500" target="_blank" v-if="CheckUrl(list.content)">{{ list.content }}</a>
<span v-else>{{ list.content }}</span>
</view>
<view :key="checker.checkerId" class="flex justify-between" v-for="checker in list.checkerList">
<view>
{{ checker.checkerName }}
<span v-if="checker.isMine">()</span>
</view>
<view>
<span class="text-blue-500" v-if="checker.status === 1">通过</span>
<span class="text-red-500" v-if="checker.status === 2">驳回</span>
<span class="ml-4" v-if="checker.status !== 0">{{ checker.score }}</span>
<span class="text-gray-400" v-if="checker.status === 0 && !checker.isMine">未审核</span>
<view v-if="checker.status === 0 && checker.isMine">
<u-button @click="showScore(checker.checkerId, 2)" class="mr-3" plain size="mini" type="error">驳回</u-button>
<u-button @click="showScore(checker.checkerId, 2)" plain size="mini" type="primary">通过</u-button>
</view>
</view>
</view>
<span class="ml-4">{{ checker.score }}</span>
</view>
</view>
</view>
<u-empty mode="history" text="暂无历史记录" v-else></u-empty>
<!-- 评分 -->
<uni-popup :maskClick="false" background-color="#fff" ref="popup" type="bottom">
<PDeliverCheck @closeScore="closeScore" @submit="submit"></PDeliverCheck>
</uni-popup>
</view>
</template>
<script>
import { mapGetters } from 'vuex';
import UniPopup from '../../components/uni-popup/uni-popup.vue';
import PDeliverCheck from '../p-deliver-check/p-deliver-check.vue';
export default {
name: 'p-delivery-history',
props: { task: { type: Object, default: null } },
components: { PDeliverCheck, UniPopup },
data() {
return { lists: [] };
return {
lists: [],
show: false,
options: null,
loading: true, //
};
},
computed: mapGetters('project', ['projectId']),
@ -53,6 +77,20 @@ export default {
}
},
showScore(checkId, status) {
// refuni-popup , type ['top','left','bottom','right','center']
this.$refs.popup.open('bottom');
this.options = { checkId, status };
},
closeScore() {
this.$refs.popup.close('bottom');
},
submit(remark, score) {
this.checkDeliver(remark, score);
},
/**
* 检查交付物
* @param {string} checkId 检查记录id
@ -61,15 +99,30 @@ export default {
* @param {number} score 分数
* @param {number} status 检查状态(1-通过,2-驳回)
*/
async checkDeliver(checkId, status) {
async checkDeliver(remark, score) {
try {
const { projectId } = this;
const params = { checkId, projectId, status };
this.show = true;
const { projectId, options } = this;
const { checkId, status } = options;
const params = { checkId, projectId, status, remark, score };
await this.$u.api.checkDeliver(params);
this.$t.ui.showToast('交付物检查成功');
this.options = null;
this.getDeliverOfTask();
} catch (error) {
console.error('p-delivery-history.vue checkDeliver error: ', error);
this.$t.ui.showToast('交付物检查失败,请稍后重试');
this.options = null;
}
},
//
CheckUrl(url) {
var reg = /^([hH][tT]{2}[pP]:\/\/|[hH][tT]{2}[pP][sS]:\/\/)(.)+$/;
if (!reg.test(url)) {
return false;
} else {
return true;
}
},
},

18
src/plugins/p-upload-deliverable/p-upload-deliverable.vue

@ -1,25 +1,25 @@
<template>
<!-- 上传交付物 -->
<view class="py-2">
<u-input v-model="content" :type="type" :border="border" :height="height" :auto-height="autoHeight" />
<u-input :auto-height="autoHeight" :border="border" :height="height" :type="type" v-model="content" width="100" />
<view class="flex flex-row-reverse text-xs text-gray-400 mt-2">{{ wordNum }}/140</view>
<ld-select
:multiple="true"
:list="checkers"
:multiple="true"
@change="selectChange2"
class="my-3"
clearable
label-key="label"
value-key="value"
placeholder="选择检查人"
clearable
v-model="checkerList"
@change="selectChange2"
class="my-3"
value-key="value"
></ld-select>
<view class="flex justify-between">
<u-button type="primary" size="mini" class="m-0" @click="submit">提交</u-button>
<u-icon name="arrow-up" v-if="showHistory" @click="changeShowHistory"></u-icon>
<u-icon name="arrow-down" v-else @click="changeShowHistory"></u-icon>
<u-button @click="submit" class="m-0" size="mini" type="primary">提交</u-button>
<u-icon @click="changeShowHistory" name="arrow-up" v-if="showHistory"></u-icon>
<u-icon @click="changeShowHistory" name="arrow-down" v-else></u-icon>
</view>
<p-delivery-history :task="task" v-if="showHistory" />

8
src/store/role/getters.js

@ -1,10 +1,10 @@
const getters = {
// 是不是负责人
isMine({ roleId, invisibleRoles, visibleList }) {
if (!visibleList || !visibleList.length) return false;
const visible = visibleList.find(visible => +visible.id === roleId);
isMine({ roleId, invisibleRoles, visibleRoles }) {
if (!visibleRoles || !visibleRoles.length) return false;
const visible = visibleRoles.find(visible => visible.id === roleId);
if (visible) return visible.mine;
const invisible = invisibleRoles.find(invisible => +invisible.id === roleId);
const invisible = invisibleRoles.find(invisible => invisible.id === roleId);
if (invisible) return visible.mine;
return false;
},

Loading…
Cancel
Save