Browse Source

feat: 添加已测评页面,压缩代码

develop
song 4 years ago
parent
commit
551e5eae83
  1. 1
      .prettierignore
  2. 3
      CHANGELOG.md
  3. 3
      package.json
  4. BIN
      public/img/icons/android-chrome-192x192.png
  5. BIN
      public/img/icons/android-chrome-512x512.png
  6. BIN
      public/img/icons/android-chrome-maskable-192x192.png
  7. BIN
      public/img/icons/android-chrome-maskable-512x512.png
  8. BIN
      public/img/icons/apple-touch-icon-120x120.png
  9. BIN
      public/img/icons/apple-touch-icon-152x152.png
  10. BIN
      public/img/icons/apple-touch-icon-180x180.png
  11. BIN
      public/img/icons/apple-touch-icon-60x60.png
  12. BIN
      public/img/icons/apple-touch-icon-76x76.png
  13. BIN
      public/img/icons/apple-touch-icon.png
  14. BIN
      public/img/icons/favicon-16x16.png
  15. BIN
      public/img/icons/favicon-32x32.png
  16. BIN
      public/img/icons/msapplication-icon-144x144.png
  17. BIN
      public/img/icons/mstile-150x150.png
  18. 3
      public/img/icons/safari-pinned-tab.svg
  19. 28
      public/index.html
  20. 2
      public/robots.txt
  21. 13
      src/App.vue
  22. 7
      src/common/styles/app.scss
  23. 257
      src/common/styles/iconfont.css
  24. 59
      src/common/styles/tailwind.scss
  25. 17
      src/components/Evaluated/Evaluated.vue
  26. 4
      src/components/Globals/Globals.vue
  27. 12
      src/components/GuidePage/GuidePage.vue
  28. 10
      src/components/NotEvaluated/NotEvaluated.vue
  29. 149
      src/components/Plugin/Plugin.vue
  30. 2
      src/components/Projects/ProjectItem.vue
  31. 84
      src/components/Skeleton/READ_ME.md
  32. 10
      src/components/Test/TestMain.vue
  33. 10
      src/components/Test/TestTitle.vue
  34. 14
      src/components/TimeLine/component/TaskTools.vue
  35. 3
      src/components/TimeLine/component/TimeBox.vue
  36. 7
      src/components/Title/Title.vue
  37. 460
      src/components/Title/components/CreateTask.vue
  38. 12
      src/components/Upload/Upload.vue
  39. 12
      src/manifest.json
  40. 53
      src/pages.json
  41. 35
      src/pagesA/project-webview/project-webview.vue
  42. 2
      src/pagesProject/project/project.vue
  43. 2
      src/pagesUser/git-phone-power/git-phone-power.vue
  44. 0
      src/pagesUser/phone-bind/phone-bind.vue
  45. 0
      src/pagesYanyuan/add-info/add-info.vue
  46. 0
      src/pagesYanyuan/ascription/ascription.vue
  47. 43
      src/pagesYanyuan/assess/assess.vue
  48. 0
      src/pagesYanyuan/hold-all/hold-all.vue
  49. 0
      src/pagesYanyuan/scan-code/scan-code.vue
  50. 0
      src/pagesYanyuan/wifi-binding/wifi-binding.vue
  51. 64
      src/plugins/p-deliver-check/p-deliver-check.vue
  52. 140
      src/plugins/p-delivery-history/p-delivery-history.vue
  53. 7
      src/plugins/p-manage-member/p-manage-member.vue
  54. 7
      src/plugins/p-manage-project/p-manage-project.vue
  55. 7
      src/plugins/p-manage-role/p-manage-role.vue
  56. 7
      src/plugins/p-manage-task/p-manage-task.vue
  57. 60
      src/plugins/p-subproject/p-subproject.vue
  58. 39
      src/plugins/p-subtasks/p-subtasks.vue
  59. 20
      src/plugins/p-task-countdown/p-task-countdown.vue
  60. 16
      src/plugins/p-task-description/p-task-description.vue
  61. 34
      src/plugins/p-task-duration-delay/p-task-duration-delay.vue
  62. 23
      src/plugins/p-task-start-time-delay/p-task-start-time-delay.vue
  63. 16
      src/plugins/p-task-title/p-task-title.vue
  64. 94
      src/plugins/p-upload-deliverable/p-upload-deliverable.vue
  65. 85
      src/plugins/p-wbs-import/p-wbs-import.vue
  66. 9
      src/store/task/mutations.js
  67. 1
      src/store/task/state.js
  68. 49
      vue.config.js

1
.prettierignore

@ -7,3 +7,4 @@ babel.config.js
package.json
postcss.config.js
.eslint.js
*.scss

3
CHANGELOG.md

@ -1,4 +1,4 @@
# 0.1.0 (2021-11-08)
# 0.1.0 (2021-11-09)
### 🌟 新功能
范围|描述|commitId
@ -23,6 +23,7 @@
task status | 任务状态切换未完 | [7ffd135](https://dd.tall.wiki/gitea/binbin0314/yanyuan_js/commits/7ffd135)
- | ws storage | [21b3a06](https://dd.tall.wiki/gitea/binbin0314/yanyuan_js/commits/21b3a06)
- | 上传逻辑变化 | [3ff1dc2](https://dd.tall.wiki/gitea/binbin0314/yanyuan_js/commits/3ff1dc2)
- | 代码分包,压缩 | [f3b0afc](https://dd.tall.wiki/gitea/binbin0314/yanyuan_js/commits/f3b0afc)
- | 任务状态时间显示 | [56f5183](https://dd.tall.wiki/gitea/binbin0314/yanyuan_js/commits/56f5183)
- | 任务进行中状态数字 | [27b7326](https://dd.tall.wiki/gitea/binbin0314/yanyuan_js/commits/27b7326)
- | 修改config | [eb5f9c5](https://dd.tall.wiki/gitea/binbin0314/yanyuan_js/commits/eb5f9c5)

3
package.json

@ -86,7 +86,8 @@
"sass": "^1.38.2",
"sass-loader": "^8.0.2",
"vue-cli-plugin-commitlint": "~1.0.12",
"vue-template-compiler": "^2.6.11"
"vue-template-compiler": "^2.6.11",
"webpack-bundle-analyzer": "^4.5.0"
},
"browserslist": [
"Android >= 4",

BIN
public/img/icons/android-chrome-192x192.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.2 KiB

BIN
public/img/icons/android-chrome-512x512.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

BIN
public/img/icons/android-chrome-maskable-192x192.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.3 KiB

BIN
public/img/icons/android-chrome-maskable-512x512.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

BIN
public/img/icons/apple-touch-icon-120x120.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

BIN
public/img/icons/apple-touch-icon-152x152.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

BIN
public/img/icons/apple-touch-icon-180x180.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

BIN
public/img/icons/apple-touch-icon-60x60.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

BIN
public/img/icons/apple-touch-icon-76x76.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

BIN
public/img/icons/apple-touch-icon.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

BIN
public/img/icons/favicon-16x16.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 799 B

BIN
public/img/icons/favicon-32x32.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

BIN
public/img/icons/msapplication-icon-144x144.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

BIN
public/img/icons/mstile-150x150.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

3
public/img/icons/safari-pinned-tab.svg

@ -1,3 +0,0 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8.00251 14.9297L0 1.07422H6.14651L8.00251 4.27503L9.84583 1.07422H16L8.00251 14.9297Z" fill="black"/>
</svg>

Before

Width:  |  Height:  |  Size: 215 B

28
public/index.html

@ -1,28 +0,0 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>
<%= htmlWebpackPlugin.options.title %>
</title>
<script>
// document.addEventListener('DOMContentLoaded', function() {
// document.documentElement.style.fontSize = document.documentElement.clientWidth / 20 + 'px'
// })
var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') || CSS.supports('top: constant(a)'))
document.write('<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' + (coverSupport ? ', viewport-fit=cover' : '') + '" />')
</script>
<link rel="stylesheet" href="<%= BASE_URL %>static/index.<%= VUE_APP_INDEX_CSS_HASH %>.css" />
</head>
<body>
<noscript>
<strong>Please enable JavaScript to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>

2
public/robots.txt

@ -1,2 +0,0 @@
User-agent: *
Disallow:

13
src/App.vue

@ -79,7 +79,7 @@ export default {
*/
async noPhone(phone) {
if (!phone) {
this.$u.route('/pages/phone-bind/phone-bind');
this.$u.route('/pagesUser/phone-bind/phone-bind');
}
},
@ -99,9 +99,16 @@ export default {
</script>
<style lang="scss">
@import './common/styles/iconfont.scss';
// @import './common/styles/iconfont.scss';
@import 'uview-ui/index.scss';
@import './common/styles/app.scss';
.min-0 {
min-width: 0;
}
.flex-shrink-0 {
flex-shrink: 0;
}
</style>
<style>

7
src/common/styles/app.scss

@ -1,7 +0,0 @@
.min-0 {
min-width: 0;
}
.flex-shrink-0 {
flex-shrink: 0;
}

257
src/common/styles/iconfont.css

File diff suppressed because one or more lines are too long

59
src/common/styles/tailwind.scss

@ -1089,65 +1089,6 @@
color: rgba(59, 130, 246, var(--tw-text-opacity));
}
.opacity-0 {
opacity: 0;
}
.opacity-5 {
opacity: 0.05;
}
.opacity-10 {
opacity: 0.1;
}
.opacity-20 {
opacity: 0.2;
}
.opacity-25 {
opacity: 0.25;
}
.opacity-30 {
opacity: 0.3;
}
.opacity-40 {
opacity: 0.4;
}
.opacity-50 {
opacity: 0.5;
}
.opacity-60 {
opacity: 0.6;
}
.opacity-70 {
opacity: 0.7;
}
.opacity-75 {
opacity: 0.75;
}
.opacity-80 {
opacity: 0.8;
}
.opacity-90 {
opacity: 0.9;
}
.opacity-95 {
opacity: 0.95;
}
.opacity-100 {
opacity: 1;
}
.h-1 {
height: 0.25rem;
}

17
src/components/Evaluated/Evaluated.vue

@ -1,9 +1,22 @@
<template>
<view>已测评</view>
<view>
<view class="flex flex-nowrap items-center">
<view class="flex-1 flex flex-nowrap items-center">
<u-rate :count="count" v-model="count" :disabled="true" :size="26" :gutter="2" active-color="#333"></u-rate>
<text class="mx-2">数独</text>
<view class="rounded-full bg-blue-500 w-max u-font-xs text-white px-2 items-center">轻松完成</view>
</view>
<u-icon name="arrow-right" color="#909399"></u-icon>
</view>
</view>
</template>
<script>
export default {};
export default {
data() {
return { count: 3 };
},
};
</script>
<style></style>

4
src/components/Globals/Globals.vue

@ -11,7 +11,7 @@
<template v-if="item.plugins">
<block v-for="(pluginArr, i) in item.plugins" :key="i">
<template class="p-0 u-col-between" v-if="pluginArr.length">
<Plugin
<!-- <Plugin
:class="[`row-span-${plugin.row}`, `col-span-${plugin.col}`]"
:task="item"
:key="plugin.pluginTaskId"
@ -20,7 +20,7 @@
:param="plugin.param"
:style-type="plugin.styleType || 0"
v-for="plugin in pluginArr"
/>
/> -->
</template>
</block>
</template>

12
src/components/GuidePage/GuidePage.vue

@ -7,20 +7,8 @@
backgroundImage: 'url(' + item.image + ')',
}"
></view>
<!-- <img :src="item.image" class="w-full" /> -->
</swiper-item>
</swiper>
<!-- <view class="wrap aaaa">
<u-swiper
@change="changePic"
height="100%"
:autoplay="false"
:list="list"
img-mode="widthFix"
class="w-full h-full"
:circular="false"
></u-swiper>
</view> -->
</template>
<script>

10
src/components/NotEvaluated/NotEvaluated.vue

@ -10,7 +10,7 @@
身定制一套认知训练课程 这些问题有难有易请您别紧张因为老人的视力 不太好我们建议您在平板电脑上或者在台式电脑 上操作.
</view>
<view class="flex flex-nowrap my-2">
<u-button type="primary" size="mini" class="mx-0">开始测评</u-button>
<u-button type="primary" size="mini" class="mx-0" @click="startAssess">开始测评</u-button>
<view class="flex-1"></view>
</view>
<text class="text-xs text-gray-300">每两个周可进行一次测评</text>
@ -18,7 +18,13 @@
</template>
<script>
export default {};
export default {
methods: {
startAssess() {
uni.navigateTo({ url: '/pagesYanyuan/assess/assess' });
},
},
};
</script>
<style></style>

149
src/components/Plugin/Plugin.vue

@ -1,149 +0,0 @@
<template>
<view class="u-font-14" style="height: 100%">
<!-- <view v-if="pluginContent" @click="setStorage">
<view
: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-tid="task.id"
:data-tname="task.name"
:data-token="token"
:data-rstart="task.realStart"
:data-uid="userId"
style="height: 100%"
v-html="pluginContent"
></view>
</view> -->
<view @click="setStorage">
<!-- <plugin-default /> -->
<!-- <component :task="task" :is="pluginComponent"></component> -->
<p-task-title :task="task" v-if="pluginId === '1'" />
<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-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'" /> -->
<p-manage-project :task="task" v-if="pluginId === '9'" />
<p-manage-role :task="task" v-if="pluginId === '10'" />
<p-manage-member :task="task" v-if="pluginId === '11'" />
<p-manage-task :task="task" v-if="pluginId === '12'" />
<p-wbs-import :task="task" v-if="pluginId === '13' || pluginId === '14'" />
<p-deliver-check :task="task" v-if="pluginId === '15'" />
</view>
</view>
</template>
<script>
import { mapGetters, mapState, mapActions } from 'vuex';
export default {
name: 'Plugin',
props: {
task: { default: () => {}, type: Object },
pluginId: { default: '1', type: String },
styleType: { default: 0, type: Number },
pluginTaskId: { default: '', type: String },
param: { type: String, default: '' },
},
data() {
return { pluginContent: null };
},
computed: {
...mapState('role', ['roleId']),
...mapState('user', ['token']),
...mapGetters('user', ['userId']),
...mapGetters('project', ['projectId']),
...mapGetters('role', ['isMine']),
//
// pluginComponent() {
// const target = this.$t.plugin.defaults.find(item => item.id === +this.pluginId);
// if (!target) return '';
// return target.component;
// },
},
async created() {
if (this.pluginId === '5') {
// id
await this.getAllMembers({ projectId: this.projectId });
}
await this.getPlugin();
},
methods: {
...mapActions('role', ['getAllMembers']),
//
async getPlugin() {
const { pluginId, styleType } = this;
const params = { pluginId, styleType };
this.$t.$q.getOtherPlugin(params, (err, data) => {
if (err) {
console.error('err: ', err);
} else {
if (!data || !data.id) return;
const reg = /data-root=["|']?(\w+)["|']?/gi;
let uuid = '';
// FIXME: js, html
if (data.html) {
// data-root=xxx xxx pluginTaskId
if (reg.test(data.html)) {
uuid = RegExp.$1;
const str = data.html.replace(new RegExp(uuid, 'g'), `p${this.pluginTaskId}`);
this.pluginContent = str;
} else {
this.pluginContent = data.html;
}
const str = data.js.replace(new RegExp(uuid, 'g'), `p${this.pluginTaskId}`);
this.handleDom(str);
}
}
});
// if (data.js) {
// if (reg.test(data.js)) {
// const uuid = RegExp.$1;
// const str = data.js.replace(new RegExp(uuid, 'g'), `p${this.pluginTaskId}`);
// this.handleDom(str);
// } else {
// this.handleDom(data.js);
// }
// }
},
// script dom
handleDom(js) {
const { pluginTaskId } = this;
let domList = Array.from(document.getElementsByTagName('script'));
const index = domList.findIndex(item => item.id === `p${pluginTaskId}`);
if (index >= 0) {
document.body.removeChild(document.getElementById(`p${pluginTaskId}`));
}
const scriptDom = document.createElement('script');
scriptDom.id = `p${pluginTaskId}`;
scriptDom.setAttribute('data-type', 'plugin');
scriptDom.innerHTML = js;
this.$nextTick(() => {
document.body.append(scriptDom);
});
},
// storage
setStorage() {
this.$t.storage.setStorageSync('roleId', this.roleId);
},
},
};
</script>

2
src/components/Projects/ProjectItem.vue

@ -122,7 +122,7 @@ export default {
console.log('project: ', project);
const { name, id, url } = project;
url && (uni.$t.domain = url);
this.$u.route('/pagesA/project/project', {
this.$u.route('/pagesProject/project/project', {
u: this.userId,
p: id,
pname: name,

84
src/components/Skeleton/READ_ME.md

@ -1,84 +0,0 @@
# skeleton组件
### 1.描述
> 此组件用于加载数据时占位图显示,跟vant-ui骨架屏用法相似,但比vant-ui更灵活
### 2.用法
- 基本用法
代码:
```vue
//基本用法
<skeleton :row="3" animate :loading="loading" >
<view>
content
</view>
</skeleton>
```
- **显示 title ——通过 **title 属性显示title占位图
代码:
```vue
//显示 title——通过 title 属性显示title占位图
<skeleton :row="3" title animate :loading="loading">
<view>
content
</view>
</skeleton>
```
- 显示头像(上面)——通过avatar=‘top’让头像的占位图上面显示
代码:
```vue
<skeleton :avatar="top" avatarAlign="left" :row="3" animate :loading="loading" style="margin-top:24rpx;">
<view>
content
</view>
</skeleton>
```
- 显示头像(左边)——通过avatar=‘left’让头像的占位图左边显示
代码:
```vue
<skeleton title :avatar="left" :row="3" animate :loading="loading" style="margin-top:24rpx;">
<view>
content
</view>
</skeleton>
```
- 显示banner**——通过 **banner属性显示banner占位图(只显示banner,不显示内容占位图时设置row="0")
代码:
```vue
<skeleton banner :row="0" animate :loading="loading" style="margin-top:24rpx;">
<view>
content
</view>
</skeleton>
```
###
### 3. API
### Props
| **属性名** | **说明** | **类型** | **默认值** | 可取值 |
| --- | --- | --- | --- | --- |
| loading | 是否显示骨架屏 | Boolean | true | true/false |
| row | 段落行数 | Number | String | 3 | 0表示不展现 |
| rowWidth | 段落行宽度 | Boolean &#124; Number | '100%' | |
| title | 是否显示标题 | Boolean &#124; String | false | |
| banner | 是否显示banner | Boolean &#124; String | false | |
| animate | 是否开启动画 | Boolean &#124; String | false | |
| avatar | 头像位置 | Boolean &#124; String | ''空 | left/top |
| avatarSize | 头像大小 | String | - | |
| avatarShape | 头像形状 | String | circle | circle/round |

10
src/components/Test/TestMain.vue

@ -0,0 +1,10 @@
<template>
<view> 测试内容 </view>
</template>
<script>
export default {};
</script>
<style scoped lang="scss">
</style>

10
src/components/Test/TestTitle.vue

@ -0,0 +1,10 @@
<template>
<view> 1.请记住以下物品 </view>
</template>
<script>
export default {};
</script>
<style scoped lang="scss">
</style>

14
src/components/TimeLine/component/TaskTools.vue

@ -19,16 +19,6 @@
<!-- 遮罩 -->
<view class="mask" v-if="maskShow" @click="closeMask"></view>
<!-- 新建任务弹窗 -->
<CreateTask
:startTime="startTime"
:endTime="endTime"
:task="task"
@showTime="showTime"
@closeMask="closeMask"
class="thirdPopup flex transition-transform"
v-if="createTaskShow"
/>
<u-picker title="开始时间" mode="time" v-model="showStart" :params="params" @confirm="confirmStartTime"></u-picker>
<u-picker title="结束时间" mode="time" v-model="showEnd" :params="params" @confirm="confirmEndTime"></u-picker>
@ -36,11 +26,7 @@
</template>
<script>
import CreateTask from '../../Title/components/CreateTask.vue';
export default {
components: { CreateTask },
props: {
task: {
type: Object,

3
src/components/TimeLine/component/TimeBox.vue

@ -70,13 +70,12 @@ export default {
return {
currentComponent: '',
styleType: 0,
isEvaluated: false, //
};
},
computed: {
...mapState('role', ['roleId']),
...mapState('task', ['timeUnit', 'tasks', 'taskLoading', 'showSkeleton']),
...mapState('task', ['timeUnit', 'tasks', 'taskLoading', 'showSkeleton', 'isEvaluated']),
...mapGetters('task', ['startTimeFormat']),
},

7
src/components/Title/Title.vue

@ -71,13 +71,6 @@ export default {
onBack() {
// eslint-disable-next-line no-undef
uni.navigateBack();
// const pages = getCurrentPages(); //
// console.log('pages: ', pages.length);
// if (pages.length > 1) {
// } else {
// // this.$u.route('/', { u: this.userId });
// uni.webView.reLaunch({ url: `/pages/index/index?u=${this.userId}` });
// }
},
// LWBS

460
src/components/Title/components/CreateTask.vue

@ -1,460 +0,0 @@
<template>
<div class="new-projects-box">
<div class="form">
<!-- 项目名称 -->
<view class="mb-3 font-bold text-base flex justify-center">新建任务</view>
<div class="flex items-center mb-2">
<div>名称<span class="text-red-500">*</span></div>
<u-input max-length="5" v-model="name" :type="type" :border="border" />
</div>
<!-- 起止时间 -->
<div class="mb-2">
<div>起止时间</div>
<u-input placeholder="请选择起止时间" v-model="timeValue" :type="type" :border="border" @click="$emit('showTime')" />
</div>
<!-- 多选框 -->
<div class="flex justify-between items-center">
<div>负责人<span class="text-red-500">*</span></div>
<div class="flex-1" v-if="hasRole">{{ roleName }}</div>
<div label="负责人" class="flex-1" v-else>
<u-dropdown disabled ref="uDropdown" placeholder="请选择负责人">
<u-dropdown-item :title="roleList">
<view class="slot-content bg-white">
<div
class="flex flex-row justify-between mb-1 drop-item"
v-for="(role, roleIndex) in roleOptions"
:key="roleIndex"
@click="change(roleIndex)"
>
<view v-model="role.id">{{ role.name }}</view>
<u-icon v-if="role.dropdownShow" name="checkbox-mark" color="#2979ff" size="28"></u-icon>
</div>
</view>
</u-dropdown-item>
</u-dropdown>
</div>
</div>
<!-- 下拉图标 -->
<div class="flex justify-center my-6">
<u-icon v-if="arrow" name="arrow-down" size="28" @click="openDropdown"></u-icon>
<u-icon v-else name="arrow-up" size="28" @click="closeSecondDropdown"></u-icon>
</div>
<!-- 下拉框的内容 -->
<div v-if="show" class="mb-6">
<!-- 描述 -->
<div class="flex items-center mb-2">
<div>描述</div>
<u-input v-model="description" max-length="48" type="textarea" height="36" auto-height :border="border" />
</div>
<!-- 所属项目 -->
<div class="w flex items-center mb-2">
<div>所属项目<span class="text-red-500">*</span></div>
<div>{{ project.name }}</div>
</div>
<!-- 所属任务 -->
<div class="w flex items-center mb-2" v-if="task && task.id">
<div>所属任务</div>
<div>{{ task.name }}</div>
</div>
<!-- 上道工序 -->
<div class="flex items-center mb-2">
<div>上道工序</div>
<InputSearch
@searchPrevTask="searchPrevTask"
:dataSource="allTasks"
@select="handleChange"
@clearAllTasks="clearAllTasks"
placeholder="请输入上道工序"
/>
</div>
<!-- 检查人多选框 -->
<div class="flex justify-between items-center">
<div>检查人<span class="text-red-500">*</span></div>
<div label="检查人" class="flex-1">
<u-dropdown ref="dropdown">
<u-dropdown-item :title="checkerList">
<view class="slot-content bg-white">
<div
class="flex flex-row justify-between mb-1 drop-item"
v-for="(checkoutOption, Index) in checkoutOptions"
:key="Index"
@click="choose(Index)"
>
<view v-model="checkoutOption.value">{{ checkoutOption.name }}</view>
<u-icon v-if="checkoutOption.dropdownShow" name="checkbox-mark" color="#2979ff" size="28"></u-icon>
</div>
</view>
</u-dropdown-item>
</u-dropdown>
</div>
</div>
<!-- 是否是日常任务 -->
<div class="flex justify-between items-center mt-6">
是否是日常任务
<u-switch v-model="isGlobal" size="28"></u-switch>
</div>
<div class="mt-6">
<div>交付物</div>
<div v-for="(sort, sortIndex) in deliverSort" :key="sortIndex">
<u-input
@blur="addDeliverInput"
v-model="sort.name"
:placeholder="`交付物名称${sortIndex + 1}`"
:type="type"
:border="border"
/>
</div>
</div>
</div>
<div class="flex items-center mb-6">
<u-button type="primary" size="medium" @click="setParameters">提交</u-button>
</div>
</div>
</div>
</template>
<script>
import { mapState, mapGetters, mapMutations } from 'vuex';
export default {
props: {
startTime: {
type: String,
default: '',
},
endTime: {
type: String,
default: '',
},
task: {
type: Object,
default: null,
},
},
data() {
return {
arrow: true,
show: false,
isGlobal: false, //
name: '', //
showChooseTime: false,
timeValue: '', //
description: '', //
projectShow: false, //
processTaskId: '', //
type: 'text',
border: true,
roleList: undefined, //
checkerList: undefined, //
roleOptions: [], //
checkoutOptions: [], //
roleIdList: [], // id
checkerIdList: [], // id
deliverables: [], //
deliverSort: [{ name: '' }], //
allTasks: [],
roleName: '', //
hasRole: false, //
};
},
computed: {
...mapState('role', ['visibleRoles', 'roleId']),
...mapState('project', ['project']),
...mapState('task', ['tasks']),
...mapGetters('project', ['projectId']),
},
watch: {
endTime(val) {
if (val) {
this.timeValue = this.startTime + ' 至 ' + val;
}
},
},
mounted() {
//
if (this.visibleRoles.length) {
this.visibleRoles.forEach(role => {
role.dropdownShow = false;
role.status = false;
});
}
this.roleOptions = this.$u.deepClone(this.visibleRoles);
this.checkoutOptions = this.$u.deepClone(this.visibleRoles);
//
if (this.roleId) {
const item = this.visibleRoles.find(r => r.id === this.roleId);
if (item) {
this.roleName = item.name;
this.hasRole = true;
}
}
},
methods: {
...mapMutations('task', ['updateTasks']),
//
change(index) {
let arr = [...this.roleOptions];
//
arr[index].dropdownShow = !arr[index].dropdownShow;
//
this.roleList = arr[index].name;
let shows = '';
// arr
arr.map(val => {
if (val.dropdownShow === true) {
shows += val.name + ',';
this.roleIdList.push(val.id);
}
});
this.roleOptions = [...arr];
// ','
this.roleList = shows.slice(0, shows.length - 1);
},
//
choose(index) {
let arr = [...this.checkoutOptions];
//
arr[index].dropdownShow = !arr[index].dropdownShow;
//
this.checkerList = arr[index].name;
let shows = '';
// arr
arr.map(val => {
if (val.dropdownShow === true) {
shows += val.name + ',';
this.checkerIdList.push(val.id);
}
});
this.checkoutOptions = [...arr];
// ','
this.checkerList = shows.slice(0, shows.length - 1);
// this.roleList = arr[value - 1].name;
},
//
openDropdown() {
this.arrow = !this.arrow;
this.show = true;
},
//
closeSecondDropdown() {
this.arrow = !this.arrow;
this.show = false;
},
/**
* 模糊查询 查找项目下的任务
* @param name 任务名
* @param projectId 项目id
*/
async searchPrevTask(val) {
try {
const params = { name: val, projectId: this.projectId };
const data = await this.$u.api.queryTaskOfProject(params);
this.allTasks = data;
return data;
} catch (error) {
console.error('error: ', error);
}
},
//
handleChange(data) {
console.log('data', data);
this.processTaskId = data.detailId;
},
//
clearAllTasks() {
this.allTasks = [];
},
//
addDeliverInput() {
if (this.deliverSort[this.deliverSort.length - 1].name) {
this.deliverSort.push({ name: '' });
}
},
//
async setParameters() {
const {
projectId,
task,
name,
startTime,
endTime,
hasRole,
roleIdList,
roleId,
description,
processTaskId,
checkerIdList,
isGlobal,
} = this;
if (!name) {
this.$t.ui.showToast('请输入名称');
return;
}
if ((!roleIdList || !roleIdList.length) && !hasRole) {
this.$t.ui.showToast('请选择负责人');
return;
}
if (!checkerIdList || !checkerIdList.length) {
this.$t.ui.showToast('请选择检查人');
return;
}
const deliverList = [];
this.deliverSort.forEach(item => {
if (item.name) {
deliverList.push(item.name);
}
});
const params = {
name,
startTime: startTime ? this.$moment(startTime).format('x') - 0 : '',
endTime: endTime ? this.$moment(endTime).format('x') - 0 : '',
roleIdList: hasRole ? [roleId] : roleIdList,
description,
projectId,
parentTaskId: task && task.id ? task.id : '', //
processTaskId, // TODO
checkerIdList,
global: isGlobal ? 1 : 0,
deliverList,
};
await this.handleSubmit(params);
},
/**
* 新建任务
* @param name 任务名
* @param startTime 开始时间
* @param endTime 结束时间
* @param roleIdList 负责人id数组
* @param description 描述
* @param projectId 所属项目id
* @param parentTaskId 所属任务id
* @param processTaskId 上道工序任务id
* @param checkerIdList 检查人id数组
* @param global 是否日常任务 0 1
* @param deliverList 交付物名字数组
*/
async handleSubmit(params) {
try {
const data = await this.$u.api.saveTask(params);
// TODO or
this.$emit('closeMask');
const newTasks = {
data: data[0],
processTaskId: params.processTaskId,
};
// store
//
if (!this.task || !this.task.id) {
this.addNewTasks(newTasks);
}
} catch (error) {
this.$emit('closeMask');
console.error('error: ', error);
}
},
// tasks
addNewTasks(data) {
const oldTasks = this.$u.deepClone(this.tasks);
let res = data.data;
//
if (data.processTaskId) {
const index = oldTasks.find(item => item.detailId === data.processTaskId);
if (index) {
oldTasks.splice(index + 1, 0, res);
}
} else {
this.setAddPosition(res, oldTasks);
}
},
//
setAddPosition(res, oldTasks) {
if (res.planStart - 0 < oldTasks[0].planStart - 0) {
//
oldTasks.splice(0, 0, res);
} else if (res.planStart - 0 === oldTasks[0].planStart - 0) {
//
oldTasks.splice(1, 0, res);
} else if (res.planStart - 0 >= oldTasks[oldTasks.length - 1].planStart - 0) {
//
oldTasks.splice(-1, 0, res);
} else {
//
for (let i = 0; i < oldTasks.length; i++) {
const item = oldTasks[i];
if (res.planStart - 0 > item.planStart - 0) {
if (res.planStart - 0 <= oldTasks[i + 1].planStart - 0) {
oldTasks.splice(i + 1, 0, res);
console.log('res: ', res);
return;
}
}
}
}
// TODO:
console.log('oldTasks: ', oldTasks);
this.updateTasks([...oldTasks]);
},
},
};
</script>
<style lang="scss" scoped>
.form {
display: flex;
flex-direction: column;
width: 100%;
max-height: 400px;
overflow-y: scroll;
}
.drop-item {
border-bottom: 1px solid #f1f1f1;
padding: 16rpx;
}
::v-deep.u-input--border {
border: none;
border-radius: 0;
}
::v-deep.u-dropdown__menu__item > uni-view {
border: none !important;
padding: 5px;
}
.u-input {
border-bottom: 1px solid #dcdfe6;
}
.new-projects-box {
margin-top: 20px;
padding: 15px;
width: 100%;
overflow: hidden;
}
.w {
width: 300px;
height: 39px;
}
::v-deep .u-dropdown__menu__item .u-flex {
border: 0 !important;
border-bottom: 1px solid #dcdfe6 !important;
padding: 0 20rpx;
}
</style>

12
src/components/Upload/Upload.vue

@ -9,29 +9,29 @@
class="icons icon1"
size="76"
name="https://www.tall.wiki/staticrec/guide/icon1.png"
@click="openPage('/pagesB/scan-code/scan-code')"
@click="openPage('/pagesYanyuan/scan-code/scan-code')"
></u-icon>
<!-- @click="openPage('/pagesB/scan-code/scan-code')" -->
<!-- @click="openPage('/pagesYanyuan/scan-code/scan-code')" -->
<!-- @click="scanCode" -->
<u-icon
class="icons icon2"
size="76"
name="https://www.tall.wiki/staticrec/guide/icon2.png"
v-if="showAddInfo"
@click="openPage('/pagesB/add-info/add-info')"
@click="openPage('/pagesYanyuan/add-info/add-info')"
></u-icon>
<u-icon
class="icons icon3"
size="76"
:class="showAddInfo ? '' : 'icon3-top'"
name="https://www.tall.wiki/staticrec/guide/icon3.png"
@click="openPage('/pagesB/ascription/ascription')"
@click="openPage('/pagesYanyuan/ascription/ascription')"
></u-icon>
<u-icon
class="icons icon4"
size="76"
name="https://www.tall.wiki/staticrec/guide/icon4.png"
@click="openPage('/pagesB/hold-all/hold-all')"
@click="openPage('/pagesYanyuan/hold-all/hold-all')"
></u-icon>
</view>
</view>
@ -55,7 +55,7 @@ export default {
this.$emit('success');
data.url && (uni.$t.domain = data.url);
setTimeout(() => {
this.$u.route('/pagesA/project/project', {
this.$u.route('/pagesProject/project/project', {
u: this.userId,
p: data.id,
pname: data.pname,

12
src/manifest.json

@ -1,5 +1,5 @@
{
"name": "TALL",
"name": "燕园",
"appid": "wx733e229f00dc5c6f",
"description": "",
"versionName": "1.0.0",
@ -59,22 +59,24 @@
"mp-weixin": {
"appid": "wx733e229f00dc5c6f",
"setting": {
"urlCheck": false
"urlCheck": false,
"minified": true
},
"usingComponents": true
"usingComponents": true,
"optimization":{"subPackages":true}
},
"h5": {
"router": {
"base": "/tall/v3.2.0"
},
"title": "时物链条",
"title": "燕园",
"sdkConfigs": {
"maps": {}
},
"optimization": {
"treeShaking": {
"enable": false
"enable": true
}
}
}

53
src/pages.json

@ -6,25 +6,11 @@
"navigationBarText": "TALL",
"navigationStyle": "custom"
}
},
{
"path": "pages/phone-bind/phone-bind",
"style": {
"navigationBarTitleText": "绑定手机号",
"navigationStyle": "default"
}
},
{
"path": "pages/git-phone-power/git-phone-power",
"style": {
"navigationBarTitleText": "授权信息",
"navigationStyle": "custom"
}
}
}
],
"subPackages":[
{
"root": "pagesA",
"root": "pagesProject",
"pages": [
{
"path": "project/project",
@ -32,18 +18,11 @@
"navigationBarTitleText": "项目详情页",
"navigationStyle": "custom"
}
},
{
"path": "project-webview/project-webview",
"style": {
"navigationBarTitleText": "项目详情页",
"navigationStyle": "custom"
}
}
]
},
{
"root": "pagesB",
"root": "pagesYanyuan",
"pages": [
{
"path": "scan-code/scan-code",
@ -79,8 +58,34 @@
"navigationBarTitleText": "WIFI绑定",
"navigationStyle": "default"
}
},
{
"path": "assess/assess",
"style": {
"navigationBarTitleText": "脑力测评",
"navigationStyle": "default"
}
}
]
},
{
"root": "pagesUser",
"pages": [
{
"path": "phone-bind/phone-bind",
"style": {
"navigationBarTitleText": "绑定手机号",
"navigationStyle": "default"
}
},
{
"path": "git-phone-power/git-phone-power",
"style": {
"navigationBarTitleText": "授权信息",
"navigationStyle": "custom"
}
}
]
}
],
"globalStyle": {

35
src/pagesA/project-webview/project-webview.vue

@ -1,35 +0,0 @@
<template>
<web-view :src="src" />
</template>
<script>
export default {
data() {
return { src: '' };
},
onLoad(options) {
console.log('options: ', options);
if (!options) {
this.$t.ui.showModal('缺少参数, 请返回重试');
} else {
const { p, u, pname, url } = options;
if (pname) {
uni.setNavigationBarTitle({ title: pname });
}
if (!u) {
this.$t.ui.showToast('缺少用户信息, 请登录');
return;
}
if (!p || !url) {
this.$t.ui.showToast('缺少项目信息, 请重新打开');
return;
}
const baseUrl = process.env.VUE_APP_PROJECT_PATH;
this.src = `${baseUrl}/${this.$t.app.version}/#/?u=${u}&p=${p}&url=${url}&pname=${pname}`;
}
},
};
</script>

2
src/pagesA/project/project.vue → src/pagesProject/project/project.vue

@ -1,5 +1,5 @@
<template>
<view :style="{ height: height }" class="flex flex-col overflow-hidden u-font-14">
<view :style="{ height: height }" class="flex flex-col overflow-hidden u-font-16">
<!-- 标题栏 -->
<Title />

2
src/pages/git-phone-power/git-phone-power.vue → src/pagesUser/git-phone-power/git-phone-power.vue

@ -31,7 +31,7 @@ export default {
// id,,
async getphonenumber(e) {
// if (e.detail.errMsg === 'getPhoneNumber:fail user deny') {
// this.$u.route('/pages/phone-bind/phone-bind');
// this.$u.route('/pagesUser/phone-bind/phone-bind');
// } else {
//

0
src/pages/phone-bind/phone-bind.vue → src/pagesUser/phone-bind/phone-bind.vue

0
src/pagesB/add-info/add-info.vue → src/pagesYanyuan/add-info/add-info.vue

0
src/pagesB/ascription/ascription.vue → src/pagesYanyuan/ascription/ascription.vue

43
src/pagesYanyuan/assess/assess.vue

@ -0,0 +1,43 @@
<template>
<view class="flex flex-col p-3">
<TestTitle />
<u-line class="my-2" color="#c8c7cc" />
<TestMain />
<u-button class="my-10" type="primary">答题</u-button>
<u-button class="my-10" type="primary" @click="viewResults">查看测评结果</u-button>
</view>
</template>
<script>
import TestTitle from 'components/Test/TestTitle';
import TestMain from 'components/Test/TestMain';
import { mapState, mapGetters, mapMutations } from 'vuex';
export default {
components: { TestTitle, TestMain },
computed: {
...mapState('project', ['project']),
...mapGetters('user', ['userId']),
},
methods: {
...mapMutations('task', ['setIsEvaluated']),
viewResults() {
this.setIsEvaluated(true);
const { name, id, url } = this.project;
url && (uni.$t.domain = url);
this.$u.route('/pagesProject/project/project', {
u: this.userId,
p: id,
pname: name,
url: encodeURIComponent(url),
});
},
},
};
</script>
<style scoped lang="scss"></style>

0
src/pagesB/hold-all/hold-all.vue → src/pagesYanyuan/hold-all/hold-all.vue

0
src/pagesB/scan-code/scan-code.vue → src/pagesYanyuan/scan-code/scan-code.vue

0
src/pagesB/wifi-binding/wifi-binding.vue → src/pagesYanyuan/wifi-binding/wifi-binding.vue

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

@ -1,64 +0,0 @@
<template>
<!-- 上传交付物 -->
<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 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>
<script>
export default {
name: 'p-deliver-check',
data() {
return {
remark: '',
type: 'textarea',
border: true,
height: 100,
autoHeight: true,
wordNum: 0,
score: 0,
type1: 'number',
};
},
watch: {
remark(val) {
this.wordNum = val.length;
},
score(val) {
this.score1 = val;
},
},
methods: {
//
submit() {
this.$emit('submit', this.remark, this.score);
},
sliderChange(e) {
this.score = e.detail.value;
},
changeNumber(e) {
if (e > 100) {
this.score = 100;
}
},
},
};
</script>
<style></style>

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

@ -1,140 +0,0 @@
<template>
<!-- 交付物 -->
<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" v-for="checker in list.checkerList" class="mb-2">
<view class="flex justify-between">
<view class="font-bold">
{{ 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.checkId, 2)" class="mr-3" plain size="mini" type="error">驳回</u-button>
<u-button @click="showScore(checker.checkId, 1)" plain size="mini" type="primary">通过</u-button>
</view>
</view>
</view>
<view class="text-gray-400 text-xs mt-1">{{ checker.remark }}</view>
</view>
</view>
</view>
</view>
<u-empty icon-size="90" 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: [],
show: false,
options: null,
loading: true, //
};
},
computed: mapGetters('project', ['projectId']),
mounted() {
this.getDeliverOfTask();
},
methods: {
async getDeliverOfTask() {
try {
const { projectId, task } = this;
const params = { projectId, taskSubId: task.id };
const data = await this.$u.api.queryDeliverOfTask(params);
this.lists = data;
} catch (error) {
console.error('p-delivery-history.vue getDeliverOfTask error: ', error);
this.$t.ui.showToast(error.msg || '提交失败');
}
},
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');
},
async submit(remark, score) {
try {
await this.checkDeliver(remark, score);
this.closeScore();
} catch (error) {
console.error('error: ', error);
}
},
/**
* 检查交付物
* @param {string} checkId 检查记录id
* @param {string} projectId 项目id
* @param {string} remark 评论
* @param {number} score 分数
* @param {number} status 检查状态(1-通过,2-驳回)
*/
async checkDeliver(remark, score) {
try {
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;
}
},
},
};
</script>
<style></style>

7
src/plugins/p-manage-member/p-manage-member.vue

@ -1,7 +0,0 @@
<template>
<view>成员管理</view>
</template>
<script>
export default {};
</script>

7
src/plugins/p-manage-project/p-manage-project.vue

@ -1,7 +0,0 @@
<template>
<view>项目管理</view>
</template>
<script>
export default {};
</script>

7
src/plugins/p-manage-role/p-manage-role.vue

@ -1,7 +0,0 @@
<template>
<view>角色管理</view>
</template>
<script>
export default {};
</script>

7
src/plugins/p-manage-task/p-manage-task.vue

@ -1,7 +0,0 @@
<template>
<view>任务管理</view>
</template>
<script>
export default {};
</script>

60
src/plugins/p-subproject/p-subproject.vue

@ -1,60 +0,0 @@
<template>
<!-- 子项目插件 -->
<view>
<view v-for="item in sonProject" :key="item.detailId">
<span class="text-xs text-blue-500" @click="openProject(item)">{{ item.name }}</span>
</view>
</view>
</template>
<script>
import { mapGetters } from 'vuex';
export default {
name: 'p-subproject',
props: {
task: {
type: Object,
default: () => {},
},
},
data() {
return { sonProject: [] };
},
computed: mapGetters('project', ['projectId']),
mounted() {
this.getSonProject();
},
methods: {
async getSonProject() {
try {
const data = await this.$u.api.findSonProject({ projectId: this.task.detailId });
this.sonProject = data;
} catch (error) {
console.error('p-subproject.vue getSonProject error: ', error);
}
},
/**
* 打开项目
* @param {object} project 所点击的项目的信息
*/
openProject(project) {
const { name, id, url } = project;
url && (uni.$t.domain = url);
this.$u.route('/pagesA/project/project', {
u: this.userId,
p: id,
pname: name,
url: encodeURIComponent(url),
});
},
},
watch: {},
};
</script>
<style></style>

39
src/plugins/p-subtasks/p-subtasks.vue

@ -1,39 +0,0 @@
<template>
<view>
<view v-for="item in sonTask" :key="item.detailId">
<span class="text-xs text-gray-500">{{ item.name }}</span>
</view>
</view>
</template>
<script>
export default {
name: 'p-subtasks',
props: {
task: {
type: Object,
default: () => {},
},
},
data() {
return { sonTask: [] };
},
created() {
this.getSonTask();
},
methods: {
async getSonTask() {
try {
const data = await this.$u.api.findSonTask({ detailId: this.task.detailId });
this.sonTask = data;
} catch (error) {
console.error('p-subtasks.vue getSonTask error: ', error);
}
},
},
};
</script>
<style></style>

20
src/plugins/p-task-countdown/p-task-countdown.vue

@ -1,20 +0,0 @@
<template>
<!-- 任务倒计时插件 -->
<view>任务倒计时插件</view>
</template>
<script>
export default {
name: 'p-task-countdown',
props: { item: { type: Object, default: null } },
data() {
return {};
},
computed: {},
methods: {},
watch: {},
};
</script>
<style></style>

16
src/plugins/p-task-description/p-task-description.vue

@ -1,16 +0,0 @@
<template>
<!-- 任务描述 -->
<view>{{ task.description }}</view>
</template>
<script>
export default {
name: 'p-task-description',
props: {
task: {
type: Object,
default: () => {},
},
},
};
</script>

34
src/plugins/p-task-duration-delay/p-task-duration-delay.vue

@ -1,34 +0,0 @@
<template>
<view v-if="realDuration && planDuration">
<!-- 任务时长延迟插件 -->
<!-- 超时 -->
<span class="font-bold text-green-500" v-if="realDuration - 0 > planDuration - 0">
+{{ $t.time.formatDuration(realDuration - planDuration) }}
</span>
<!-- 延时 -->
<span class="font-bold text-red-500" v-if="realDuration - 0 < planDuration - 0">
-{{ $t.time.formatDuration(planDuration - realDuration) }}
</span>
</view>
</template>
<script>
export default {
name: 'p-task-duration-delay',
props: {
task: {
type: Object,
default: () => {},
},
},
computed: {
realDuration() {
return this.task.realDuration;
},
planDuration() {
return this.task.planDuration;
},
},
};
</script>

23
src/plugins/p-task-start-time-delay/p-task-start-time-delay.vue

@ -1,23 +0,0 @@
<template>
<!-- <view>任务开始时间延迟插件</view> -->
<view v-if="realStart && planStart">
<!-- 任务开始时间延迟插件 -->
<!-- 超时 -->
<span>{{ $t.time.formatDuration(+realStart - +planStart) }}</span>
</view>
</template>
<script>
export default {
name: 'p-task-start-time-delay',
props: { task: { type: Object, default: () => {} } },
computed: {
realStart() {
return this.task.realStart;
},
planStart() {
return this.task.planStart;
},
},
};
</script>

16
src/plugins/p-task-title/p-task-title.vue

@ -1,16 +0,0 @@
<template>
<!-- 任务名插件 -->
<view>{{ task.name }}</view>
</template>
<script>
export default {
name: 'p-task-title',
props: {
task: {
type: Object,
default: () => {},
},
},
};
</script>

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

@ -1,94 +0,0 @@
<template>
<!-- 上传交付物 -->
<view class="py-2">
<u-input :auto-height="autoHeight" :border="border" :height="height" :type="type" v-model="content" width="100" />
<!-- 选择检查人 -->
<ChooseChecker ref="checker" :checkerList="checkerList" @setCheckerList="setCheckerList"></ChooseChecker>
<view class="flex justify-between">
<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" />
</view>
</template>
<script>
import ChooseChecker from '@/components/ChooseChecker/ChooseChecker.vue';
import { mapState, mapGetters } from 'vuex';
export default {
name: 'p-upload-deliverable',
components: { ChooseChecker },
props: { task: { type: Object, default: null } },
data() {
return {
content: '',
type: 'textarea',
border: true,
height: 30,
autoHeight: true,
checkerList: [],
showHistory: false, //
};
},
computed: {
...mapState('role', ['members']),
...mapGetters('project', ['projectId']),
checkers() {
const arr = [];
if (this.members.length) {
this.members.forEach(member => {
const item = { value: member.memberId, label: member.name };
arr.push(item);
});
}
return arr;
},
},
methods: {
//
setCheckerList(checked, item) {
if (checked) {
this.checkerList.push(item.memberId);
} else {
const index = this.checkerList.findIndex(checker => checker === item.memberId);
this.checkerList.splice(index, 1);
}
},
//
changeShowHistory() {
this.showHistory = !this.showHistory;
},
//
async submit() {
try {
const { content, checkerList, projectId, task } = this;
if (!this.checkerList.length) {
this.$t.ui.showToast('请选择检查人');
return;
}
const params = { content, checkerList, projectId, taskSubId: task.id };
await this.$u.api.saveDeliver(params);
this.$t.ui.showToast('交付物提交成功');
this.content = '';
this.checkerList = [];
this.$refs.checker.clearChecked();
} catch (error) {
console.error('p-upload-deliverable.vue submit error: ', error);
this.$t.ui.showToast('交付物提交失败,请稍后重试');
}
},
},
};
</script>
<style scoped lang="scss"></style>

85
src/plugins/p-wbs-import/p-wbs-import.vue

@ -1,85 +0,0 @@
<template>
<view>
<view @click="handleUpload" v-if="task.name === '导入WBS新建项目'">{{ task.name }}</view>
<view @click="handleUpdate" v-if="task.name === '导入WBS更新项目'">{{ task.name }}</view>
<!-- 全局提示框 -->
<u-top-tips ref="uTips"></u-top-tips>
</view>
</template>
<script>
import { mapGetters, mapMutations } from 'vuex';
export default {
name: 'p-wbs-import',
props: {
task: {
type: Object,
default: () => {},
},
},
data() {
return {};
},
computed: {
...mapGetters('user', ['userId']),
...mapGetters('project', ['projectId']),
},
methods: {
...mapMutations('project', ['setShowAlert']),
// wbs
async handleUpload() {
try {
const data = await this.$u.api.import();
// WBS
//
this.onUploadSuccess();
setTimeout(() => {
this.$u.route('/pagesA/project/project', {
u: this.userId,
p: data.id,
pname: data.pname,
url: data.url,
});
}, 2000);
} catch (error) {
this.onUploadError(error);
}
},
//
// TODO:
async handleUpdate() {
try {
await this.$u.api.import({ projectId: this.projectId });
// WBS
//
this.onUploadSuccess();
} catch (error) {
this.onUploadError(error);
}
},
//
onUploadSuccess() {
this.$refs.uTips.show({
title: '导入成功,即将打开新项目',
type: 'success',
duration: '3000',
});
},
//
onUploadError(error) {
this.$refs.uTips.show({
title: error || '导入失败',
type: 'error',
duration: '6000',
});
},
},
};
</script>

9
src/store/task/mutations.js

@ -203,6 +203,15 @@ const mutations = {
setShowScrollTo(state, show) {
state.showScrollTo = show;
},
/**
* 设置是否已测评
* @param {Object} state
* @param {Boolean} show
*/
setIsEvaluated(state, show) {
state.isEvaluated = show;
},
};
export default mutations;

1
src/store/task/state.js

@ -20,6 +20,7 @@ const state = {
showSkeleton: false, // 定期任务骨架屏
newProjectInfo: {},
showScrollTo: false, // 是否可以设置时间轴自动滚动的位置
isEvaluated: false, // 是否已测评
};
export default state;

49
vue.config.js

@ -1,8 +1,8 @@
const path = require('path');
// const path = require('path');
// const CopyWebpackPlugin = require('copy-webpack-plugin'); // 最新版本copy-webpack-plugin插件暂不兼容,推荐v5.0.0
const CompressionWebpackPlugin = require('compression-webpack-plugin');
const productionGzipExtensions = /\.(js|css|json|txt|html|ico|svg|ttf|woff|woff2)(\?.*)?$/i;
const isPro = process.env.NODE_ENV === 'production';
// const CompressionWebpackPlugin = require('compression-webpack-plugin');
// const productionGzipExtensions = /\.(js|css|json|txt|html|ico|svg|ttf|woff|woff2)(\?.*)?$/i;
// const isPro = process.env.NODE_ENV === 'production';
module.exports = {
lintOnSave: process.env.NODE_ENV !== 'production',
@ -14,23 +14,32 @@ module.exports = {
// proxy: {}
},
configureWebpack: {
plugins: isPro
? [
new CompressionWebpackPlugin({
filename: '[path].gz[query]',
algorithm: 'gzip',
test: productionGzipExtensions,
threshold: 10240,
minRatio: 0.8,
deleteOriginalAssets: false,
}),
]
: [],
optimization: {
minimize: true,
},
// plugins: isPro
// ? [
// new CompressionWebpackPlugin({
// filename: '[path].gz[query]',
// algorithm: 'gzip',
// test: productionGzipExtensions,
// threshold: 10240,
// minRatio: 0.8,
// deleteOriginalAssets: false,
// }),
// ]
// : [],
},
// pluginOptions: {
// mock: { entry: './src/mock/mock.js', debug: true, disable: true },
// webpackBundleAnalyzer: {
// openAnalyzer: isPro,
// },
// },
pluginOptions: {
// mock: { entry: './src/mock/mock.js', debug: true, disable: true },
// webpackBundleAnalyzer: {
// openAnalyzer: isPro,
// },
webpackBundleAnalyzer: {
openAnalyzer: process.env.NODE_ENV === 'production',
},
},
};

Loading…
Cancel
Save