Browse Source

refactor: 合并deliver分支到feat

test2
xuesinan 4 years ago
parent
commit
d173a4f834
  1. 149
      .drone.yml
  2. 1
      CHANGELOG.md
  3. 10
      apis/finance.js
  4. 28
      apis/mock.js
  5. 22
      apis/plugin.js
  6. 169
      common/styles/tailwind.scss
  7. 108
      common/styles/theme/default.scss
  8. 13
      components/DeliverLink/DeliverLink.vue
  9. 78
      components/Globals/Globals.vue
  10. 27
      components/Plugin/Plugin.vue
  11. 132
      components/PrettyExchange/PrettyExchange.vue
  12. 139
      components/Reviewer/Reviewer.vue
  13. 24
      components/TimeLine/component/TimeBox.vue
  14. 226
      components/zwp-ring-timing/zwp-ring-timing.vue
  15. 8
      config/deliver.js
  16. 20
      config/index.js
  17. 19
      main.js
  18. 107
      pages.json
  19. 5
      pages/404/404.vue
  20. 53
      pages/checkLog/checkLog.vue
  21. 25
      pages/detailWebview/detailWebview.vue
  22. 267
      pages/index/index.vue
  23. 386
      pages/project/project.vue
  24. 68
      pages/submitLog/submitLog.vue
  25. 26
      pages/submitlist/submitlist.vue
  26. 84
      plugins/p-deliver-check/check-form-modal.vue
  27. 135
      plugins/p-deliver-check/p-deliver-check.vue
  28. 225
      plugins/p-deliver-upload/p-deliver-upload.vue
  29. 213
      plugins/p-deliver/p-deliver.vue
  30. 18
      plugins/p-finance-audit/p-finance-audit.vue
  31. 73
      plugins/p-finance/p-finance.vue
  32. 11
      plugins/p-source-manage/p-source-manage.vue
  33. 21
      plugins/p-task-title/p-task-title.vue
  34. 108
      rest/deliver.http
  35. 18
      store/deliver/index.js
  36. 25
      store/finance/index.js
  37. 4
      store/index.js
  38. 128
      utils/ui.js
  39. 2
      utils/upload.js

149
.drone.yml

@ -1,149 +0,0 @@
---
kind: pipeline
type: docker
name: dev
# 挂载的主机卷,可以映射到docker容器中
volumes:
# maven构建缓存(宿主机目录)
- name: ssh_key
host:
path: /root/.ssh/
- name: cache
host:
path: /var/lib/cache
- name: data
host:
path: /var/lib/data
steps:
# - name: restore-cache
# image: drillster/drone-volume-cache
# volumes:
# - name: cache
# path: /cache
# settings:
# restore: true
# mount:
# - ./node_modules
- name: build
image: node:latest
pull: if-not-exists # default always
# volumes:
# - name: cache
# path: /root/.m2
commands:
- npm config set registry http://registry.npm.taobao.org
- npm i
- npm run test
# - name: rebuild-cache
# image: drillster/drone-volume-cache
# volumes:
# - name: cache
# path: /cache
# settings:
# rebuild: true
# mount:
# - ./node_modules
- name: deploy-scp
image: appleboy/drone-scp
pull: if-not-exists
volumes:
- name: ssh_key
path: /root/.ssh/
settings:
host: test.tall.wiki
port: 22
username: root
key_path: /root/.ssh/id_rsa
rm: true # true则会删除目标目录重建
target: /home/tall/v4.0.0
source: dist/*
strip_components: 1 # 去除的目录层数,如果没有该选项,则拷贝过去是 target/xxx.jar,1代表去除target
# - name: run-ssh
# image: appleboy/drone-ssh
# pull: if-not-exists
# volumes:
# - name: ssh_key
# path: /root/.ssh/
# settings:
# settings:
# host: test.tall.wiki
# port: 22
# username: root
# key_path: /root/.ssh/id_rsa
# script_stop: true # stop script after first failure
# #command_timeout: 30s # 30seconds, the maximum amount of time for the execute commands, default is 10 minutes.
# script:
# - cd /home/iacd-platform-drone
# - ./re.sh > /dev/null 2> /dev/null &
- name: notify-email
image: drillster/drone-email
pull: if-not-exists
settings:
host: smtp.qiye.aliyun.com #例如 smtp.qq.com
port: 465 #例如QQ邮箱端口465
username: devops@ccsens.com #邮箱用户名
password: #邮箱密码
from_secret: orgsecret_password_mail_devops
from: devops@ccsens.com
recipients: weizezhao@ccsens.com #收件人,多个用,隔开
when: #执行条件
status:
- success
- changed
- failure
- name: notify-wechatwork
image: fifsky/drone-wechat-work
pull: if-not-exists
settings:
url: https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=b2b93e9a-128b-41d4-8dce-12004e3f48b9
msgtype: markdown
content: |
{{if eq .Status "success" }}
#### 🎉 ${DRONE_REPO} 构建成功
> Commit: [${DRONE_COMMIT_MESSAGE}](${DRONE_COMMIT_LINK})
> Author: ${DRONE_COMMIT_AUTHOR}
> [点击查看](${DRONE_BUILD_LINK})
{{else}}
#### ❌ ${DRONE_REPO} 构建失败
> Commit: [${DRONE_COMMIT_MESSAGE}](${DRONE_COMMIT_LINK})
> Author: ${DRONE_COMMIT_AUTHOR}
> 请立即修复!!!
> [点击查看](${DRONE_BUILD_LINK})
{{end}}
when:
status:
- failure
- success
trigger:
branch: feat
# - name: notify-dingtalk
# image: lddsb/drone-dingtalk-message
# environment:
# PASSWORD:
# from_secret: password_mail_devops
# settings:
# token: your-dingtalk-robot-access-token
# type: markdown
# message_color: true
# message_pic: true
# sha_link: true
# -name: notify-slack
# image: plugins/slack
# webhook: https://hooks.slack.com/ www.dijiuyy.com services/xxx/xxx/xxx
# channel: dev
# template: >
# {{#success build.status}}
# build {{build.number}} succeeded. Good job.
# {{else}}
# build {{build.number}} failed. Fix me please.
# {{/success}}

1
CHANGELOG.md

@ -9,6 +9,7 @@
- | 插件面板分开显示 | [fb5e86b](https://101.201.226.163:50022/TALL/TALL-MUI-4/commits/fb5e86b)
- | 登录、日历页小绿点、二级项目列表 | [e676cf0](https://101.201.226.163:50022/TALL/TALL-MUI-4/commits/e676cf0)
- | 更新代码 | [392c8cc](https://101.201.226.163:50022/TALL/TALL-MUI-4/commits/392c8cc)
- | 广告页、引导页 | [1c89806](https://101.201.226.163:50022/TALL/TALL-MUI-4/commits/1c89806)
- | 获取手机唯一码 | [3f60cf8](https://101.201.226.163:50022/TALL/TALL-MUI-4/commits/3f60cf8)
- | 将时间轴改成swiper滑动 | [12384f9](https://101.201.226.163:50022/TALL/TALL-MUI-4/commits/12384f9)
- | 日历页首页 | [561c8e6](https://101.201.226.163:50022/TALL/TALL-MUI-4/commits/561c8e6)

10
apis/finance.js

@ -0,0 +1,10 @@
import store from '@/store/index';
const fullPath = store.getters['finance/fullPath'];
console.log('fullPath: ', fullPath);
export function setupFinance(app) {
uni.$u.api = { ...uni.$u.api } || {};
// 根据任务id获取财务条信息
uni.$u.api.getFinanceByTask = taskDetailId => uni.$u.post(`${fullPath}/getByTask`, { taskDetailId });
}

28
apis/mock.js

@ -0,0 +1,28 @@
import Config from '@/common/js/config.js';
const { apiUrl } = Config;
const defaultwbs = `${apiUrl}/defaultwbs`;
export function setupMock(app) {
uni.$u.api = { ...uni.$u.api } || {};
// 删除交付物
uni.$u.api.deleteDeliver = param =>
new Promise((resolve, reject) => {
setTimeout(() => {
resolve();
}, 100);
});
// uni.$u.api.getFinanceByTask = taskDetailId => new Promise((resolve, reject) => {
// setTimeout(() => {
// resolve({
// financeId: '123',
// budget: 1000 * 1000,
// bonus: 200 * 1000,
// projectExpend: 500 * 1000,
// dailyExpend: 500 * 1000,
// });
// }, 100);
// });
}

22
apis/plugin.js

@ -1,12 +1,13 @@
import Config from '@/common/js/config.js'
import Config from '@/common/js/config.js';
const apiUrl = Config.apiUrl;
const { apiUrl } = Config;
const defaultwbs = `${apiUrl}/defaultwbs`;
export function setupPlugin(app) {
uni.$u.api = { ...uni.$u.api } || {};
// 获取插件信息
uni.$u.api.getOtherPlugin = param => uni.$u.post(`${apiUrl}/pluginshop/plugin/query?pluginId=${param.pluginId}&styleType=${param.styleType}`);
uni.$u.api.getOtherPlugin = param =>
uni.$u.post(`${apiUrl}/pluginshop/plugin/query?pluginId=${param.pluginId}&styleType=${param.styleType}`);
// 查询子任务
uni.$u.api.findSonTask = param => uni.$u.post(`${defaultwbs}/task/findSonTask`, param);
@ -18,4 +19,17 @@ export function setupPlugin(app) {
uni.$u.api.queryDeliverOfTask = param => uni.$u.post(`${defaultwbs}/deliver/queryDeliverOfTask`, param);
// 检查交付物
uni.$u.api.checkDeliver = param => uni.$u.post(`${defaultwbs}/deliver/checkDeliver`, param);
};
// v4.0
// 根据任务id获取任务的交付物信息(已成功)
uni.$u.api.getDeliverByTaskId = param => uni.$u.post(`${defaultwbs}/deliver/getDeliver`, param);
// 提交交付物信息(已成功)
uni.$u.api.submitDeliverInfo = param => uni.$u.post(`${defaultwbs}/deliver/submitDeliver`, param);
// 查看交付物提交历史记录(已成功)
uni.$u.api.getDeliverHistory = param => uni.$u.post(`${defaultwbs}/deliver/queryRecord`, param);
// 修改交付物标题名称
uni.$u.api.editDeliverName = param => uni.$u.post(`${defaultwbs}/deliver/saveDeliver`, param);
// 检查交付物
uni.$u.api.checkDeliver = param => uni.$u.post(`${defaultwbs}/deliver/checkDeliver`, param);
// 查看检查记录
uni.$u.api.queryCheckLog = param => uni.$u.post(`${defaultwbs}/deliver/queryCheckLog`, param);
}

169
common/styles/tailwind.scss

@ -2198,9 +2198,9 @@
.-ml-px {
margin-left: -1px;
}
.border-none{
border-style: none;
.border-none {
border-style: none;
}
.box-border {
box-sizing: border-box;
@ -2213,16 +2213,22 @@
.border-l-2 {
border-left-width: 2px;
}
.border-b {
border-bottom-width: 1px;
}
.border-gray-400 {
--tw-border-opacity: 1;
border-color: rgba(156, 163, 175, var(--tw-border-opacity));
}
.border-gray-300 {
--tw-border-opacity: 1;
border-color: rgba(209, 213, 219, var(--tw-border-opacity));
}
.border{
border-width: 1px;
.border {
border-width: 1px;
}
.border-solid{
border-style: solid;
.border-solid {
border-style: solid;
}
.block {
display: block;
@ -2433,15 +2439,15 @@
grid-auto-flow: column dense;
}
.gap-1{
.gap-1 {
gap: 0.25rem;
}
.gap-2{
.gap-2 {
gap: 0.5rem;
}
.gap-3{
.gap-3 {
gap: 0.75rem;
}
@ -2704,11 +2710,11 @@
.overflow-hidden {
overflow: hidden;
}
.visible{
visibility: visible;
.visible {
visibility: visible;
}
.invisible{
visibility: hidden;
.invisible {
visibility: hidden;
}
.overflow-visible {
overflow: visible;
@ -4346,23 +4352,22 @@
}
.h-3 {
height: 0.75rem;
}
.h5{
height: 1.25rem;
height: 0.9rem;
}
.h-10{
height: 2.5rem;
.h5 {
height: 1.25rem;
}
.h-10 {
height: 2.5rem;
}
.h-full {
height: 100%;
}
.leading-10{
line-height: 2.5rem;
.leading-10 {
line-height: 2.5rem;
}
.bg-gray-50 {
--tw-bg-opacity: 1;
@ -4394,8 +4399,20 @@
.h-12 {
height: 3.15rem;
}
.h-8{
height: 2.2rem;
.h-8 {
height: 2.2rem;
}
.h-7 {
height: 2rem;
}
.h-4 {
height: 1rem;
}
.h-5 {
height: 1.25rem;
}
.h-6 {
height: 1.5rem;
}
.bg-white {
--tw-bg-opacity: 1;
@ -4405,13 +4422,23 @@
--tw-bg-opacity: 1;
background-color: rgba(219, 234, 254, var(--tw-bg-opacity));
}
.bg-blue-300 {
--tw-bg-opacity: 1;
background-color: rgba(147, 197, 253, var(--tw-bg-opacity));
}
.bg-blue-400 {
--tw-bg-opacity: 1;
background-color: rgba(96, 165, 250, var(--tw-bg-opacity));
}
.bg-blue-500 {
--tw-bg-opacity: 1;
background-color: rgba(59, 130, 246, var(--tw-bg-opacity));
}
.text-green-400 {
--tw-text-opacity: 1;
color: rgba(52, 211, 153, var(--tw-text-opacity));
.bg-green-400 {
--tw-bg-opacity: 1;
background-color: rgba(52, 211, 153, var(--tw-bg-opacity));
}
.text-black {
--tw-text-opacity: 1;
@ -4493,8 +4520,8 @@
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
}
.rounded-md{
border-radius: 0.375rem;
.rounded-md {
border-radius: 0.375rem;
}
.shadow-md {
@ -4527,34 +4554,68 @@ border-radius: 0.375rem;
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
}
.bg-red-500{
--tw-bg-opacity: 1;
background-color: rgba(239, 68, 68, var(--tw-bg-opacity));
.bg-red-500 {
--tw-bg-opacity: 1;
background-color: rgba(239, 68, 68, var(--tw-bg-opacity));
}
.bg-yellow-500{
--tw-bg-opacity: 1;
background-color: rgba(245, 158, 11, var(--tw-bg-opacity));
.bg-yellow-500 {
--tw-bg-opacity: 1;
background-color: rgba(245, 158, 11, var(--tw-bg-opacity));
}
.leading-12{
line-height: 3.15rem;
.bg-yellow-400 {
--tw-bg-opacity: 1;
background-color: rgba(251, 191, 36, var(--tw-bg-opacity));
}
.rounded-w-12{
border-radius: 1.5rem;
.bg-yellow-300 {
--tw-bg-opacity: 1;
background-color: rgba(252, 211, 77, var(--tw-bg-opacity));
}
.bg-opacity-100{
--tw-bg-opacity: 1;
.leading-12 {
line-height: 3.15rem;
}
.bg-grey{
background-color: rgba(0,0,0,0.5);
.leading-6 {
line-height: 1.5rem;
}
.rounded-md{
border-radius: 0.375rem;
.rounded-w-12 {
border-radius: 1.5rem;
}
.border-t{
border-top-width: 1px;
.bg-opacity-100 {
--tw-bg-opacity: 1;
}
.border-gray-200{
--tw-border-opacity: 1;
border-color: rgba(229, 231, 235, var(--tw-border-opacity));
.bg-grey {
background-color: rgba(0, 0, 0, 0.5);
}
.rounded-md {
border-radius: 0.375rem;
}
.border-t {
border-top-width: 1px;
}
.border-gray-200 {
--tw-border-opacity: 1;
border-color: rgba(229, 231, 235, var(--tw-border-opacity));
}
.w-64 {
width: 16rem;
}
.w-32 {
width: 8rem;
}
.w-58 {
width: 14.5rem;
}
.h-16 {
height: 4rem;
}
.break-all {
word-break: break-all;
}
.h-1-4 {
height: 1.4rem;
}
.leading-1-4 {
line-height: 1.4rem;
}
.h-screen {
height: 100vh;
}

108
common/styles/theme/default.scss

@ -1,38 +1,88 @@
// 默认主题文件
.theme-default {
background-color: #F3F3F3;
background-color: #007aff;
// color: #fff;
background-color: #f3f3f3;
.u-card {
font-size: 16px !important;
background-color: #F3F3F3 !important;
.deliverHead{
align-items: center;
}
.btns u-button{
margin: 0 !important;
}
.active{
background-color: #2979FF;
color: #FFFFFF;
}
.mask{
position:absolute;
top: 0;
left:0;
width: 100%;
height: 100%;
z-index: 100;
}
background-color: #f3f3f3 !important;
.mask {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 100;
}
}
.u-navbar {
background-color: #007aff !important;
color: #fff;
.uicon-nav-back {
color: #fff !important;
// 弹出层 内容盒子
.modal-content-wrap {
position: absolute;
left: 50rpx;
top: 50%;
width: 650rpx;
transform: translate3d(0, -50%, 0);
background-color: #fff;
.modal-content-head {
text-align: center;
margin-top: 40rpx;
margin-bottom: 20rpx;
font-size: 16px;
font-weight: 600;
}
.modal-content-body {
padding: 32rpx;
// 审核插件
.common-list {
height: 12.5rem;
overflow-y: scroll;
view {
border-bottom: 1px solid #e5e7eb;
&:last-child {
border-bottom: none;
}
}
}
}
.modal-content-foot {
display: flex;
border-top: 1px solid #d1d5db;
.cancel,
.confirm {
flex: 1;
text-align: center;
@extend .leading-12;
}
.cancel {
border-right: 1px solid #d1d5db;
}
.confirm {
@extend .text-blue-700;
}
// .leading-12 flex-1 text-center delete-modal-border
}
uni-textarea {
width: 596rpx;
height: 140rpx;
box-sizing: border-box !important;
}
}
button{
border: none!important;
.link-box {
:deep(.u-input__input) {
color: #60a5fa;
}
}
.progress-dot {
width: 50rpx;
height: 50rpx;
border-radius: 50%;
line-height: 50rpx;
background-color: #fa8c16;
}
}

13
components/DeliverLink/DeliverLink.vue

@ -0,0 +1,13 @@
<template>
<view @click="openLink" class="break-all text-blue-400 text-xs my-1"> {{ link }} </view>
</template>
<script setup>
const props = defineProps({ link: String });
function openLink() {
uni.navigateTo({
url: `/pages/detailWebview/detailWebview?url=${props.link}`,
});
}
</script>

78
components/Globals/Globals.vue

@ -1,39 +1,46 @@
<template>
<theme class="m-2">
<u-card @click="openCard" :show-foot="false" :show-head="false" :style="{ 'max-height': globalsHeight + 'px' }" border-radius="25" margin="0" class="global-container">
<template v-slot:body>
<scroll-view :scrollY="true" :style="{ 'max-height': globalsHeight - 30 + 'px' }">
<!-- <skeleton :banner="false" :loading="!globals.length" :row="3" animate class="u-line-2 skeleton"></skeleton> -->
<u-card
@click="openCard"
:show-foot="false"
:show-head="false"
:style="{ 'max-height': globalsHeight + 'px' }"
border-radius="25"
margin="0"
class="global-container"
>
<template v-slot:body>
<scroll-view :scrollY="true" :style="{ 'max-height': globalsHeight - 30 + 'px' }">
<!-- 骨架屏 -->
<skeleton :banner="false" :loading="showGlobalSkeleton" :row="3" animate class="u-line-2 skeleton"></skeleton>
<view class="grid gap-2">
<view v-for="item in globals" :key="item.id">
<template v-if="item.plugins && item.plugins.length">
<view v-for="(pluginArr, i) in item.plugins" :key="i">
<template class="p-0 u-col-between" v-if="pluginArr.length">
<Plugin
:class="[`row-span-${plugin.row}`, `col-span-${plugin.col}`]"
:task="item"
:key="plugin.pluginTaskId"
:plugin-task-id="plugin.pluginTaskId"
:plugin-id="plugin.pluginId"
:param="plugin.param"
:style-type="plugin.styleType || 0"
v-for="plugin in pluginArr"
/>
</template>
</view>
</template>
<view class="grid">
<template v-for="item in globals" :key="item.id">
<template v-if="item.plugins && item.plugins.length">
<view v-for="(pluginArr, i) in item.plugins" :key="i" class="pb-3">
<template class="p-0 u-col-between" v-if="pluginArr.length">
<Plugin
:class="[`row-span-${plugin.row}`, `col-span-${plugin.col}`]"
:task="item"
:key="plugin.pluginTaskId"
:plugin-task-id="plugin.pluginTaskId"
:plugin-id="plugin.pluginId"
:param="plugin.param"
:style-type="plugin.styleType || 0"
v-for="plugin in pluginArr"
/>
</template>
</view>
</template>
<!-- 任务名插件 -->
<p-task-title :task="item" v-else />
<!-- 交付物插件 -->
<p-deliver></p-deliver>
<!-- 任务名插件 -->
<view v-else class="pb-3">
<Plugin plugin-id="1" :task="item" />
</view>
</view>
</scroll-view>
</template>
</u-card>
</theme>
</template>
</view>
</scroll-view>
</template>
</u-card>
</template>
<script setup>
@ -44,9 +51,9 @@ import Skeleton from '@/components/Skeleton/Skeleton.vue';
defineProps({
globals: {
type: Array,
default: () => []
}
})
default: () => [],
},
});
const sysHeight = uni.getSystemInfoSync().screenHeight; //
const globalsHeight = Math.floor(((sysHeight - 44 - 30 - 10) / 5) * 4); //
@ -60,7 +67,6 @@ function openCard() {
store.commit('task/setShrink', false);
}
}
</script>
<style scoped lang="scss">

27
components/Plugin/Plugin.vue

@ -1,11 +1,9 @@
<template>
<view class="u-font-14" style="height: 100%">
<view @click="setStorage">
<Render :task="task" :pluginId="pluginId" :styleType="styleType" :pluginTaskId="pluginTaskId" :param="param" />
<!-- <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'" />
<view class="u-font-14 rounded-md bg-white p-2" style="height: 100%" @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" />
@ -17,14 +15,19 @@
<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>
<p-wbs-import :task="task" v-if="pluginId === '13' || pluginId === '14'" /> -->
<!-- 交付物插件 -->
<p-deliver v-else-if="pluginId === '15'" />
<p-source-manage v-else-if="pluginId === '16'" />
<p-finance-audit v-else-if="pluginId === '17'" />
<p-finance v-else-if="pluginId === '18'" />
<Render v-else :task="task" :pluginId="pluginId" :styleType="styleType" :pluginTaskId="pluginTaskId" :param="param" />
</view>
</template>
<script setup>
import { computed } from 'vue';
import { computed, provide } from 'vue';
import { useStore } from 'vuex';
const props = defineProps({
@ -35,6 +38,8 @@ const props = defineProps({
param: { type: String, default: '' },
});
provide('task', props.task);
const store = useStore();
const roleId = computed(() => store.state.role.roleId);
const token = computed(() => store.state.user.token);

132
components/PrettyExchange/PrettyExchange.vue

@ -3,28 +3,28 @@
<scroll-view scroll-y="true">
<view v-if="!data.changeEvent">
<view :id="'cu-' + index" :key="item.id" class="cu-item flex-col" v-for="(item, index) in data.itemList">
<ProjectItem
class="w-full"
:index="index"
:item="item"
:menuList="data.menuList"
<ProjectItem
class="w-full"
:index="index"
:item="item"
:menuList="data.menuList"
@setData="setData"
@openSubProject="openSubProject"
@openSubProject="openSubProject"
/>
</view>
</view>
<view v-else>
<view
:id="'cu-' + index"
:key="index"
<view
:id="'cu-' + index"
:key="index"
:style="{ 'background-color': item.color }"
@touchend="stops($event, index)"
@touchmove.stop.prevent="move"
@touchend="stops($event, index)"
@touchmove.stop.prevent="move"
@touchstart="start($event, index)"
class="cu-item flex-col"
v-for="(item, index) in data.itemList"
>
class="cu-item flex-col"
v-for="(item, index) in data.itemList"
>
<view class="border-100 bg-blue-500" v-if="item.showTopBorder"></view>
@ -59,14 +59,14 @@
<!-- 子项目 -->
<view class="ml-8" v-if="item.show">
<view
:id="'cu-' + index + '-' + subIndex"
<view
:id="'cu-' + index + '-' + subIndex"
:key="subIndex"
@touchend.stop.prevent="stops($event, index + '-' + subIndex, item.sonProjectList.length)"
@touchmove.stop.prevent="move($event, item.sonProjectList.length)"
@touchstart.stop.prevent="start($event, index + '-' + subIndex)" class="cu-item flex-col"
v-for="(subItem, subIndex) in item.sonProjectList"
>
v-for="(subItem, subIndex) in item.sonProjectList"
>
<view class="flex items-center justify-between p-3 w-full">
<u-icon class="mover" name="https://www.tall.wiki/staticrec/drag.svg" size="48"></u-icon>
@ -75,7 +75,7 @@
<view class="flex items-center">
<view class="mr-2">{{ subItem.name }}</view>
<!-- 状态 -->
<view
<view
:class="
subItem.status === 0
? 'text-blue-400 bg-blue-100'
@ -84,7 +84,7 @@
: subItem.status === 2
? 'text-red-400 bg-red-100'
: 'text-gray-400 bg-gray-100'
"
"
class="px-2 text-xs text-gray-400 bg-gray-100 rounded-full flex-shrink-0">
{{ subItem.status === 0 ? '未开始' : subItem.status === 1 ? '进行中' : subItem.status === 2 ? '暂停' : '已完成' }}
</view>
@ -130,7 +130,7 @@
// itemTop: 0,
// itemLeft: 0,
itemHeight: 0, //
itemWidth: 0, //
itemWidth: 0, //
subItemHeight: 0, //
showMoveImage: false,
moveItem: '', //
@ -154,7 +154,7 @@
});
const emit = defineEmits(['changeHeight', 'change']);
//
watch(projects, (val) => {
data.itemList = val;
@ -184,15 +184,15 @@
}
//
function getDate() {
const query = uni.createSelectorQuery().select(`#cu-0`).fields({
id: true,
dataset: true,
rect: true,
size: true
}, res => {
data.begintop = res.top;
data.beginleft = res.left;
function getDate() {
const query = uni.createSelectorQuery().select(`#cu-0`).fields({
id: true,
dataset: true,
rect: true,
size: true
}, res => {
data.begintop = res.top;
data.beginleft = res.left;
}).exec();
}
@ -243,44 +243,44 @@
}, 300);
if (isNumber(index)) { //
data.setSubItem = false;
const query = uni.createSelectorQuery().select(`#cu-${index}`).fields({
id: true,
dataset: true,
rect: true,
size: true
}, res => {
data.moveTop = res.top;
data.moveLeft = res.left;
data.moveItem = data.itemList[index];
data.itemWidth = res.width;
data.itemHeight = res.height;
data.setSubItem = false;
const query = uni.createSelectorQuery().select(`#cu-${index}`).fields({
id: true,
dataset: true,
rect: true,
size: true
}, res => {
data.moveTop = res.top;
data.moveLeft = res.left;
data.moveItem = data.itemList[index];
data.itemWidth = res.width;
data.itemHeight = res.height;
}).exec();
} else { //
let arr = index.split('-');
data.setSubItem = true;
const query = uni.createSelectorQuery();
query.select(`#cu-${arr[0] - 0}`).fields({
id: true,
dataset: true,
rect: true,
size: true
}, res => {
data.itemHeight = res.height;
}).exec();
query.select(`#cu-${index}`).fields({
id: true,
dataset: true,
rect: true,
size: true
}, res => {
data.moveTop = res.top;
data.moveLeft = res.left;
data.moveItem = data.itemList[arr[0] - 0].sonProjectList[arr[1] - 0];
data.itemWidth = res.width;
data.subItemHeight = res.height;
const query = uni.createSelectorQuery();
query.select(`#cu-${arr[0] - 0}`).fields({
id: true,
dataset: true,
rect: true,
size: true
}, res => {
data.itemHeight = res.height;
}).exec();
query.select(`#cu-${index}`).fields({
id: true,
dataset: true,
rect: true,
size: true
}, res => {
data.moveTop = res.top;
data.moveLeft = res.left;
data.moveItem = data.itemList[arr[0] - 0].sonProjectList[arr[1] - 0];
data.itemWidth = res.width;
data.subItemHeight = res.height;
}).exec();
}
}
@ -504,4 +504,4 @@
height: 2px;
margin-left: 30px;
}
</style>
</style>

139
components/Reviewer/Reviewer.vue

@ -1,91 +1,66 @@
<template>
<view class="deliverFoot border border-solid border-gray-300 rounded-md mt-3 p-2">
<view class="top flex justify-between">
<view class="mr-3">
审核人
</view>
<!-- 展示选择的审核人 -->
<view class="flex approver item-center truncate justify-end flex-1 text-sm" v-show="isUicon">
<view v-for="item in computedDelivers">
<view v-show="item.checked" class="mx-1">
{{item.name}}
</view>
</view>
</view>
<!-- 点击更换图标 -->
<view>
<u-icon v-if="isUicon" name="arrow-down" @click="changeIcon"></u-icon>
<u-icon v-else="isUicon" name="arrow-up" @click="changeIcon"></u-icon>
</view>
</view>
<!-- 隐藏的审核人选项 -->
<view v-show="!isUicon" class="foot mt-2 flex flex-wrap">
<u-button
v-for="item in delivers"
size="mini" class="my-1 mx-2"
@click="item.checked = !item.checked"
:class="item.checked ? 'active' : '' "
>
{{item.name}}
</u-button>
</view>
</view>
<view class="border border-solid border-gray-300 rounded-md mt-3 p-2" @click="collapsed = !collapsed">
<view class="top flex justify-between">
<view class="mr-3 text-sm">审核人</view>
<!-- 展示选择的审核人 -->
<view class="flex item-center justify-end flex-1 text-sm">
<view v-for="item in showCheckers" class="mx-1">
<!-- <u-badge :is-dot="true" is-center></u-badge> -->
{{ item.name }}
</view>
<view class="mx-1" v-show="checkedCheckers.length > 3">...</view>
</view>
<!-- 点击更换图标 -->
<u-icon :name="collapsed ? 'arrow-down' : 'arrow-up'"></u-icon>
</view>
<!-- 隐藏的审核人选项 -->
<view v-show="!collapsed" class="foot mt-2 flex flex-wrap">
<u-button
v-for="item in checkers"
:type="checkedCheckers.find(checker => checker.memberId === item.memberId) ? 'primary' : 'default'"
size="mini"
class="my-1 mx-2"
@click="handleSelectChecker(item)"
>
{{ item.name }}
</u-button>
</view>
</view>
</template>
<script setup>
import { ref, computed, defineExpose } from 'vue';
import { useStore } from 'vuex';
import {ref , reactive , computed }from 'vue'
//
let isUicon = ref('true')
//
const delivers = reactive([
{
checked:true,
name:'冯教授'
},
{
checked:false,
name:'陈历珺'
},
{
checked:false,
name:'张野'
},
{
checked:false,
name:'宋瑞芳'
},
{
checked:false,
name:'张斌'
},
{
checked:false,
name:'孙方圆'
}
])
const store = useStore();
//
const collapsed = ref(true);
//
const computedDelivers = computed(()=>{
let arr = [];
delivers.forEach((item)=>{
if(item.checked){
arr.push(item)
}
if(arr.length>3){
arr = arr.splice(0,3)
arr[3] = {checked:true,name:'...'}
}
})
return arr
})
// store
//
const checkers = computed(() => store.state.role.members);
//
function changeIcon(){
isUicon.value = !isUicon.value
}
</script>
//
const checkedCheckers = ref([]);
<style lang="scss">
//
const showCheckers = computed(() => (checkedCheckers.value.length > 3 ? checkedCheckers.value.slice(0, 3) : checkedCheckers.value));
</style>
defineExpose({ checkedCheckers, collapsed });
/**
* 点击成员 切换检查人的选中状态
* @param {object} member 成员对象
*/
function handleSelectChecker(member) {
const target = checkedCheckers.value.find(item => item.memberId === member.memberId);
if (target) {
//
checkedCheckers.value = checkedCheckers.value.filter(item => item.memberId !== member.memberId);
} else {
checkedCheckers.value.push(member);
}
}
</script>

24
components/TimeLine/component/TimeBox.vue

@ -17,25 +17,23 @@
<view class="h-3" v-if="task.process === 4"></view>
<!-- <view class="ml-3 overflow-hidden shadow-lg task-box"> -->
<view class="ml-3">
<u-card :show-foot="false" :show-head="false" :style="{ height: setHeight(task.panel) }" class="h-16" margin="0" v-if="showSkeleton">
<view slot="body">
<view><skeleton :banner="false" :loading="true" :row="4" animate class="mt-2 u-line-2 skeleton"></skeleton></view>
</view>
</u-card>
<!-- <u-card
@click="onClickTask(task.planStart - 0, task.id)"
:style="{ height: setHeight(task.panel) }"
<u-card
:show-foot="false"
:show-head="false"
:style="{ height: setHeight(task.panel) }"
class="h-16"
margin="0"
v-if="tasks && tasks.length && task.process !== 4 && !showSkeleton"
v-if="showSkeleton"
>
<template v-slot:body> -->
<view class="h-16" v-if="tasks && tasks.length && task.process !== 4 && !showSkeleton" @click="onClickTask(task.planStart - 0, task.id)">
<view slot="body">
<view><skeleton :banner="false" :loading="true" :row="4" animate class="mt-2 u-line-2 skeleton"></skeleton></view>
</view>
</u-card>
<view v-if="tasks && tasks.length && task.process !== 4 && !showSkeleton" @click="onClickTask(task.planStart - 0, task.id)">
<view class="p-0 u-col-between grid gap-3">
<view :key="pIndex" v-for="(row, pIndex) in task.plugins">
<view class="grid gap-2 grid-cols-1" v-if="row.length">
<view class="grid grid-cols-1" v-if="row.length">
<Plugin
:class="[`row-span-${plugin.row}`, `col-span-${plugin.col}`]"
:task="task"
@ -50,8 +48,6 @@
</view>
</view>
</view>
<!-- </template>
</u-card> -->
</view>
</view>
</view>

226
components/zwp-ring-timing/zwp-ring-timing.vue

@ -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>

8
config/deliver.js

@ -0,0 +1,8 @@
// 上传文件的扩展名
export const UPLOAD_EXTENSION = ['.xls', '.xlsx', '.zip', '.exe', '.pdf', '.doc', '.docx', '.ppt', '.pptx'];
// 审核的快捷用语
export const quickWords = {
RESOLVE: ['加油,再接再厉!', '很棒!', '不错,很详细!', '加油,再接再厉'], // 审核通过常用的审批语
REJECT: ['不详细', '还有需要改进的地方', '驳回审批1', '驳回审批2'], // 审核驳回常用的审批语
};

20
config/index.js

@ -0,0 +1,20 @@
// 环境 (development|production)
export const SCENE = 'development';
// 一些特殊的API区分生产测试环境
export const api = {
baseUrl: {
development: 'https://test.tall.wiki',
production: 'https://www.tall.wiki',
},
msgUrl: {
development: 'wss://test.tall.wiki/websocket/message/v4.0/ws',
production: 'wss://www.tall.wiki/websocket/message/v4.0/ws',
},
upload: {
development: 'https://test.tall.wiki/filedeal/file/upload/multiple',
production: 'http://101.201.226.21/filedeal/file/upload/multiple',
},
};
export const UPLOAD_URL = api.upload[SCENE]; // 多文件上传路径

19
main.js

@ -1,22 +1,24 @@
import { createSSRApp } from 'vue';
import App from './App';
import cache from '@/utils/cache.js';
import cacheAndRequest from '@/utils/cacheAndRequest.js';
import { createSSRApp } from 'vue';
import pluginConfig from '@/config/plugin';
import { setupDayjs } from '@/utils/dayjs.js';
import { setupFinance } from '@/apis/finance.js';
import { setupHttp } from '@/utils/request.js';
import { setupMock } from '@/apis/mock.js';
import { setupPlugin } from '@/apis/plugin.js';
import { setupProject } from '@/apis/project.js';
import { setupRole } from '@/apis/role.js';
import { setupTall } from '@/apis/tall.js';
import { setupTask } from '@/apis/task.js';
import { setupWbs } from '@/apis/wbs.js';
import { setupPlugin } from '@/apis/plugin.js';
import storage from '@/utils/storage.js';
import store from './store';
import task from '@/utils/task.js';
import taskConfig from '@/config/task';
import time from '@/utils/time.js';
import timeConfig from '@/config/time';
import taskConfig from '@/config/task';
import pluginConfig from '@/config/plugin';
import uView from './uni_modules/vk-uview-ui'; // 引入 uView UI
import ui from '@/utils/ui.js';
import upload from '@/utils/upload.js';
@ -56,8 +58,9 @@ export function createApp() {
setupTask(app);
setupWbs(app);
setupPlugin(app);
return {
app,
};
setupFinance(app);
if (import.meta.env.MODE === 'development') {
setupMock(app); // mock DEBUG:
}
return { app };
}

107
pages.json

@ -1,4 +1,4 @@
{
{
"pages": [ //pageshttps://uniapp.dcloud.io/collocation/pages
{
"path": "pages/guide/adv",
@ -13,13 +13,13 @@
"navigationBarText": "TALL",
"navigationStyle": "custom"
}
},
{
"path": "pages/index/index",
"style": {
},
{
"path": "pages/index/index",
"style": {
"navigationBarText": "TALL",
"navigationStyle": "custom"
}
"navigationStyle": "custom"
}
},
{
"path": "pages/business/business",
@ -27,21 +27,21 @@
"navigationBarText": "业务列表",
"navigationStyle": "custom"
}
},
{
"path": "pages/project/project",
"style": {
"navigationStyle": "custom",
"navigationBarTextStyle": "white"
}
},
//
{
"path": "pages/user/accountLogin",
"style": {
"navigationStyle": "custom",
"navigationBarTextStyle": "white"
}
{
"path": "pages/project/project",
"style": {
"navigationStyle": "custom",
"navigationBarTextStyle": "white"
}
},
//
{
"path": "pages/user/accountLogin",
"style": {
"navigationStyle": "custom",
"navigationBarTextStyle": "white"
}
},
//
{
@ -66,29 +66,44 @@
"navigationStyle": "custom",
"navigationBarTextStyle": "white"
}
}
,{
"path" : "pages/submitList/submitList",
"style" :
{
"navigationStyle": "historty",
"navigationBarTitleText": "历史交付物"
}
}
],
"globalStyle": {
"navigationBarTextStyle": "black",
"navigationBarTitleText": "TALL",
"navigationBarBackgroundColor": "#F8F8F8",
"backgroundColor": "#F8F8F8"
},
"easycom": {
"autoscan": true,
"custom": {
"^u-(.*)": "uview-ui/components/u-$1/u-$1.vue",
"^p-(.*)": "@/plugins/p-$1/p-$1.vue",
"theme": "@/components/Theme/Theme.vue"
}
}
},
{
"path": "pages/submitLog/submitLog",
"style": {
"navigationBarTitleText": "交付物上传记录"
}
},
{
"path": "pages/checkLog/checkLog",
"style": {
"navigationBarTitleText": "审核记录"
}
},
{
"path": "pages/detailWebview/detailWebview",
"style": {
"navigationBarTitleText": "详情页"
}
},
{
"path": "pages/404/404",
"style": {
"navigationBarTitleText": "404"
}
}
],
"globalStyle": {
"navigationBarTextStyle": "black",
"navigationBarTitleText": "TALL",
"navigationBarBackgroundColor": "#F8F8F8",
"backgroundColor": "#F8F8F8"
},
"easycom": {
"autoscan": true,
"custom": {
"^u-(.*)": "uview-ui/components/u-$1/u-$1.vue",
"^p-(.*)": "@/plugins/p-$1/p-$1.vue",
"theme": "@/components/Theme/Theme.vue"
}
}
}

5
pages/404/404.vue

@ -0,0 +1,5 @@
<template>
<div class="404">404</div>
</template>
<script setup></script>

53
pages/checkLog/checkLog.vue

@ -0,0 +1,53 @@
<template>
<theme class="pt-1 h-full">
<view class="h-full overflow-y-scroll bg-white rounded-md p-3 text-gray-400">
<view v-for="item in checkerList" class="flex justify-between">
<view>
<view class="mb-1 text-gray-800">
{{ item.checkerName }}
</view>
<view class="mb-1 text-xs">
{{ item.remark }}
</view>
<view class="text-xs" v-if="+item.checkTime > 0">
{{ dayjs(+item.checkTime).format('MM-DD HH:mm') }}
</view>
</view>
<view class="flex flex-col justify-center">
<view class="mb-1 text-green-600" v-if="item.status === 1"> 已通过 </view>
<view class="mb-1 text-red-600" v-if="item.status === 2"> 已驳回 </view>
<view v-if="+item.score > 0">
<zwp-ring-timing mode="chart" :value="item.score" active-color="#F59E0B" radius="30" bar-width="4">
<text class="text-yellow-500 font-medium">{{ item.score }}</text>
</zwp-ring-timing>
</view>
</view>
</view>
</view>
</theme>
</template>
<script setup>
import { ref } from 'vue';
import { onLoad } from '@dcloudio/uni-app';
import dayjs from 'dayjs';
const checkerList = ref([]);
onLoad(options => {
getQueryCheckLog(options);
});
async function getQueryCheckLog(options) {
try {
const param = { deliverRecordId: options.deliverRecordId };
const data = await uni.$u.api.queryCheckLog(param);
checkerList.value = data;
} catch (error) {
console.log('error: ', error);
uni.$ui.showToast('获取检查交付物历史失败');
}
}
</script>
<style lang="scss"></style>

25
pages/detailWebview/detailWebview.vue

@ -0,0 +1,25 @@
<template>
<web-view :src="src"></web-view>
</template>
<script setup>
import { ref } from 'vue';
import { onLoad } from '@dcloudio/uni-app';
const src = ref('');
onLoad(options => {
if (!options) {
uni.redirectTo({ url: '/pages/404/404' });
return;
}
if (options.url) {
src.value = options.url;
}
if (options.name) {
uni.setNavigationBarTitle({
title: options.name || '详情页',
});
}
});
</script>

267
pages/index/index.vue

@ -7,14 +7,9 @@
<!-- <view class="flex flex-col h-full bg-gray-50" @click="openAuth"> -->
<theme class="flex flex-col h-full bg-gray-50">
<view class="relative">
<!-- <view class="relative" @touchmove="onMove"> -->
<!-- <view class="relative" @touchmove="onMove"> -->
<!-- 日历 -->
<Calendar
@selected-change="onDateChange"
:show-back="true"
ref="calendar"
@handleFindPoint="handleFindPoint"
/>
<Calendar @selected-change="onDateChange" :show-back="true" ref="calendar" @handleFindPoint="handleFindPoint" />
<!-- 上传 导入wbs -->
<Upload @success="onUploadSuccess" @error="onUploadError" />
@ -30,141 +25,139 @@
</template>
<script setup>
import { reactive, computed, watchEffect, ref } from 'vue';
import { useStore } from 'vuex';
import dayjs from 'dayjs';
const store = useStore();
const token = computed(() => store.state.user.token);
const uTips = ref(null);
const data = reactive({
calendar: null,
// days: [],
});
getProjects();
handleFindPoint();
// token
// watchEffect(() => {
// if (!token.value) return;
// if (token.value) {
// getProjects();
// handleFindPoint();
// }
// });
//
function getProjects(start = dayjs().startOf('day').valueOf(), end = dayjs().endOf('day').valueOf()) {
uni.$catchReq.getProjects(start, end, (err, data) => {
if (err) {
console.error('err: ', err);
} else {
data.forEach(item => {
item.show = false;
});
store.commit('project/setProjects', data);
}
});
}
async function handleFindPoint(start, end) {
try {
const startTime = start || dayjs().startOf('month').valueOf();
const endTime = end || dayjs().endOf('month').valueOf();
const res = await uni.$u.api.findRedPoint(startTime, endTime);
store.commit('project/setDotList', res);
} catch (error) {
console.log('error: ', error);
import { reactive, computed, watchEffect, ref } from 'vue';
import { useStore } from 'vuex';
import dayjs from 'dayjs';
const store = useStore();
const token = computed(() => store.state.user.token);
const uTips = ref(null);
const data = reactive({
calendar: null,
// days: [],
});
getProjects();
handleFindPoint();
// token
// watchEffect(() => {
// if (!token.value) return;
// if (token.value) {
// getProjects();
// handleFindPoint();
// }
// });
//
function getProjects(start = dayjs().startOf('day').valueOf(), end = dayjs().endOf('day').valueOf()) {
uni.$catchReq.getProjects(start, end, (err, data) => {
if (err) {
console.error('err: ', err);
} else {
data.forEach(item => {
item.show = false;
});
store.commit('project/setProjects', data);
}
});
}
async function handleFindPoint(start, end) {
try {
const startTime = start || dayjs().startOf('month').valueOf();
const endTime = end || dayjs().endOf('month').valueOf();
const res = await uni.$u.api.findRedPoint(startTime, endTime);
store.commit('project/setDotList', res);
} catch (error) {
console.log('error: ', error);
}
//
const onDateChange = event => {
const day = dayjs(event.fullDate);
const start = day.startOf('date').valueOf();
const end = day.endOf('date').valueOf();
getProjects(start, end);
};
//
const onUploadSuccess = () => {
uni.$ui.showToast('导入成功,即将打开新项目', 3000);
// uTips.show({
// title: '',
// type: 'success',
// duration: '3000',
// });
};
//
const onUploadError = error => {
uni.$ui.showToast('导入失败', 6000);
// uTips.show({
// title: error || '',
// type: 'error',
// duration: '6000',
// });
};
// /
// function onMove(event) {
// const y = event.changedTouches[0].pageY;
// const prevY = 0;
// if (y - prevY > 0) {
// // weekMode=true weekMode=false
// data.calendar.weekMode && (data.calendar.weekMode = false);
// } else if (y - prevY < 0) {
// // weekMode=false weekMode=true
// !data.calendar.weekMode && (data.calendar.weekMode = true);
// }
// prevY = y;
// data.calendar.initDate();
// }
function toLogin() {
uni.navigateTo({
url: '/pages/user/login'
})
}
}
//
const onDateChange = event => {
const day = dayjs(event.fullDate);
const start = day.startOf('date').valueOf();
const end = day.endOf('date').valueOf();
getProjects(start, end);
};
//
const onUploadSuccess = () => {
uni.$ui.showToast('导入成功,即将打开新项目', 3000);
// uTips.show({
// title: '',
// type: 'success',
// duration: '3000',
// });
};
//
const onUploadError = error => {
uni.$ui.showToast('导入失败', 6000);
// uTips.show({
// title: error || '',
// type: 'error',
// duration: '6000',
// });
};
// /
// function onMove(event) {
// const y = event.changedTouches[0].pageY;
// const prevY = 0;
// if (y - prevY > 0) {
// // weekMode=true weekMode=false
// data.calendar.weekMode && (data.calendar.weekMode = false);
// } else if (y - prevY < 0) {
// // weekMode=false weekMode=true
// !data.calendar.weekMode && (data.calendar.weekMode = true);
// }
// prevY = y;
// data.calendar.initDate();
// }
function toLogin() {
uni.navigateTo({ url: '/pages/user/login' });
}
</script>
<style lang="scss" scoped>
.statbar {
width: 750rpx;
height: var(--status-bar-height);
.status_bar {
height: var(--status-bar-height);
width: 100%;
position: absolute;
}
}
.content {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.logo {
height: 200rpx;
width: 200rpx;
margin-top: 200rpx;
margin-left: auto;
margin-right: auto;
margin-bottom: 50rpx;
}
.statbar {
width: 750rpx;
height: var(--status-bar-height);
.text-area {
display: flex;
justify-content: center;
}
.title {
font-size: 36rpx;
color: #8f8f94;
.status_bar {
height: var(--status-bar-height);
width: 100%;
position: absolute;
}
}
.content {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.logo {
height: 200rpx;
width: 200rpx;
margin-top: 200rpx;
margin-left: auto;
margin-right: auto;
margin-bottom: 50rpx;
}
.text-area {
display: flex;
justify-content: center;
}
.title {
font-size: 36rpx;
color: #8f8f94;
}
</style>

386
pages/project/project.vue

@ -1,195 +1,195 @@
<template>
<theme :style="{ height: height }" class="flex flex-col overflow-hidden u-font-14">
<!-- 标题栏 -->
<Title />
<view class="container flex flex-col flex-1 mx-auto overflow-hidden bg-gray-100">
<!-- 角色栏 -->
<Roles />
<swiper :current="roleIndex" class="swiper container flex flex-col flex-1 mx-auto overflow-hidden bg-gray-100" :indicator-dots="false" :autoplay="false" :circular="false" @change="tabsChange">
<swiper-item v-for="(task,index) in allTasks" :key="index">
<!-- 日常任务面板 -->
<Globals :globals="task.global" />
<!-- 定期任务面板 -->
<TimeLine :tasks="task.task" @getTasks="getTasks" class="flex-1 overflow-hidden" />
</swiper-item>
</swiper>
<!-- TODO: DEBUG: -->
<u-button @click="$store.commit('setTheme', 'theme-test')">测试切换主题</u-button>
</view>
</theme>
</template>
<script setup>
import { ref, computed, watch, onMounted } from 'vue';
import { useStore } from 'vuex';
import useInit from '@/hooks/project/useInit';
import useGetTasks from '@/hooks/project/useGetTasks';
import dayjs from 'dayjs';
const initHook = useInit();
const getTasksHook = useGetTasks();
const store = useStore();
const projectId = computed(() => store.getters['project/projectId']);
const userId = computed(() => store.getters['user/userId']);
const roleId = computed(() => store.state.role.roleId); // id
const roleIndex = computed(() => store.state.role.roleIndex); //
const visibleRoles = computed(() => store.state.role.visibleRoles); //
const timeNode = computed(() => store.state.task.timeNode); //
const timeUnit = computed(() => store.state.task.timeUnit); //
const newProjectInfo = computed(() => store.state.task.newProjectInfo);
const showScrollTo = computed(() => store.state.task.showScrollTo); //
const allTasks = computed(() => store.state.task.allTasks); //
const globals = computed(() => store.getters['task/globals']); // +
const timeGranularity = computed(() => store.getters['task/timeGranularity']); // dayjs add
const height = ref(null); //
onMounted(() => {
const system = uni.getSystemInfoSync();
height.value = `${system.windowHeight}px`;
});
/**
* 当角色发生变化时
* 重新查询永久日常任务和普通日常任务
* 注意: 切换角色后 重新设置了时间基准点 时间基准点一定会变
* 所以监听时间基准点获取 可变日常任务即可 这里不用获取 避免重复获取
*/
watch(roleId, newValue => {
if (newValue) {
// allTasks
if (allTasks.value.length && allTasks.value[roleIndex.value].global && allTasks.value[roleIndex.value].task)
return;
console.log('当角色发生变化时', newValue);
store.commit('task/setTimeNode', Date.now());
//
const params = {
roleId: newValue,
projectId: projectId.value,
};
store.dispatch('task/getPermanent', params);
}
});
/**
* 当时间基准点发生变化时
* 重新根据时间和角色查询普通日常任务
* 永久日常任务不发生 改变
*/
watch(timeNode, newValue => {
if (newValue && roleId.value) {
console.log('当时间基准点发生变化时');
clearTasksData();
//
getGlobalData(); //
getTasksHook.initPlanTasks(); //
//
let timer = null;
timer = setInterval(() => {
if (showScrollTo.value) {
clearInterval(timer);
setScrollPosition();
}
}, 300);
}
});
/**
* 当收到打开新项目消息状态时
* 重新根据时间和角色查询普通日常任务
* 永久日常任务不发生改变
*/
watch(newProjectInfo, newValue => {
console.log('当收到打开新项目消息状态时');
if (newValue && newValue.value.projectId && newValue.value.url) {
uni.$u.route('/', {
u: userId.value,
p: newValue.value.projectId,
url: newValue.value.url,
});
clearTasksData();
store.commit('role/setRoleId', '');
const options = uni.$route.query;
initHook.init(options);
<template>
<theme :style="{ height: height }" class="flex flex-col overflow-hidden u-font-14">
<!-- 标题栏 -->
<Title />
<view class="container flex flex-col flex-1 mx-auto overflow-hidden bg-gray-100">
<!-- 角色栏 -->
<Roles />
<swiper
:current="roleIndex"
class="swiper container flex flex-col flex-1 mx-auto overflow-hidden bg-gray-100"
:indicator-dots="false"
:autoplay="false"
:circular="false"
@change="tabsChange"
>
<swiper-item v-for="(task, index) in allTasks" :key="index">
<!-- 日常任务面板 -->
<Globals :globals="task.global" />
<!-- 定期任务面板 -->
<TimeLine :tasks="task.task" @getTasks="getTasks" class="flex-1 overflow-hidden" />
</swiper-item>
</swiper>
<!-- TODO: DEBUG: -->
<!-- <u-button @click="$store.commit('setTheme', 'theme-test')">测试切换主题</u-button> -->
</view>
</theme>
</template>
<script setup>
import { ref, computed, watch, onMounted } from 'vue';
import { useStore } from 'vuex';
import dayjs from 'dayjs';
import useInit from '@/hooks/project/useInit';
import useGetTasks from '@/hooks/project/useGetTasks';
const initHook = useInit();
const getTasksHook = useGetTasks();
const store = useStore();
const projectId = computed(() => store.getters['project/projectId']);
const userId = computed(() => store.getters['user/userId']);
const roleId = computed(() => store.state.role.roleId); // id
const roleIndex = computed(() => store.state.role.roleIndex); //
const visibleRoles = computed(() => store.state.role.visibleRoles); //
const timeNode = computed(() => store.state.task.timeNode); //
const timeUnit = computed(() => store.state.task.timeUnit); //
const newProjectInfo = computed(() => store.state.task.newProjectInfo);
const showScrollTo = computed(() => store.state.task.showScrollTo); //
const allTasks = computed(() => store.state.task.allTasks); //
const globals = computed(() => store.getters['task/globals']); // +
const timeGranularity = computed(() => store.getters['task/timeGranularity']); // dayjs add
const height = ref(null); //
onMounted(() => {
const system = uni.getSystemInfoSync();
height.value = `${system.windowHeight}px`;
});
/**
* 当角色发生变化时
* 重新查询永久日常任务和普通日常任务
* 注意: 切换角色后 重新设置了时间基准点 时间基准点一定会变
* 所以监听时间基准点获取 可变日常任务即可 这里不用获取 避免重复获取
*/
watch(roleId, newValue => {
if (newValue) {
// allTasks
if (allTasks.value.length && allTasks.value[roleIndex.value].global && allTasks.value[roleIndex.value].task) return;
console.log('当角色发生变化时', newValue);
store.commit('task/setTimeNode', Date.now());
//
const params = {
roleId: newValue,
projectId: projectId.value,
};
store.dispatch('task/getPermanent', params);
}
});
/**
* 当时间基准点发生变化时
* 重新根据时间和角色查询普通日常任务
* 永久日常任务不发生 改变
*/
watch(timeNode, newValue => {
if (newValue && roleId.value) {
console.log('当时间基准点发生变化时');
clearTasksData();
//
getGlobalData(); //
getTasksHook.initPlanTasks(); //
//
let timer = null;
timer = setInterval(() => {
if (showScrollTo.value) {
clearInterval(timer);
setScrollPosition();
}
}, 300);
}
});
/**
* 当收到打开新项目消息状态时
* 重新根据时间和角色查询普通日常任务
* 永久日常任务不发生改变
*/
watch(newProjectInfo, newValue => {
console.log('当收到打开新项目消息状态时');
if (newValue && newValue.value.projectId && newValue.value.url) {
uni.$u.route('/', {
u: userId.value,
p: newValue.value.projectId,
url: newValue.value.url,
});
clearTasksData();
store.commit('role/setRoleId', '');
const options = uni.$route.query;
initHook.init(options);
}
});
//
function getGlobalData() {
if (!allTasks.value[roleIndex]) {
const param = {
roleId: roleId.value,
timeNode: timeNode.value,
timeUnit: timeUnit.value,
projectId: projectId.value,
};
store.dispatch('task/getGlobal', param);
}
// allTasks
const index = visibleRoles.value.findIndex(role => role.id === roleId.value);
const arr = [...allTasks.value];
arr[index].global = [...globals.value];
store.commit('task/setAllTasks', arr);
}
//
function clearTasksData() {
//
store.commit('task/setPermanents', []);
store.commit('task/setDailyTasks', []);
//
store.commit('task/clearTasks');
//
//
store.commit('task/clearEndFlag');
}
function getTasks(params) {
getTasksHook.initPlanTasks(params); //
}
// tabsswiper
function tabsChange(e) {
const { id } = visibleRoles.value[e.detail.current];
store.commit('role/setRoleIndex', e.detail.current);
store.commit('role/setRoleId', id);
}
//
function setScrollPosition() {
// storagetaskId id
const taskId = uni.$storage.getStorageSync('taskId');
if (taskId) {
store.commit('task/setScrollToTaskId', `a${taskId}`);
uni.$storage.setStorageSync('taskId', ''); //
} else {
const item = allTasks.value[roleIndex.value].task.find(task => task.detailId);
if (item) {
store.commit('task/setScrollToTaskId', `a${item.id}`);
} else {
// taskId
// 线id 线
const task = allTasks.value[roleIndex.value].task.find(item => dayjs(+item.planStart).isSame(timeNode.value, timeGranularity.value));
task && store.commit('task/setScrollToTaskId', `a${task.id}`); // task id
}
});
//
function getGlobalData() {
if (!allTasks.value[roleIndex]) {
const param = {
roleId: roleId.value,
timeNode: timeNode.value,
timeUnit: timeUnit.value,
projectId: projectId.value,
};
store.dispatch('task/getGlobal', param);
}
// allTasks
const index = visibleRoles.value.findIndex(role => role.id === roleId.value);
const arr = [...allTasks.value];
arr[index].global = [...globals.value];
store.commit('task/setAllTasks', arr);
}
//
function clearTasksData() {
//
store.commit('task/setPermanents', []);
store.commit('task/setDailyTasks', []);
//
store.commit('task/clearTasks');
//
//
store.commit('task/clearEndFlag');
}
function getTasks(params) {
getTasksHook.initPlanTasks(params); //
}
// tabsswiper
function tabsChange(e) {
const {
id
} = visibleRoles.value[e.detail.current];
store.commit('role/setRoleIndex', e.detail.current);
store.commit('role/setRoleId', id);
}
//
function setScrollPosition() {
// storagetaskId id
const taskId = uni.$storage.getStorageSync('taskId');
if (taskId) {
store.commit('task/setScrollToTaskId', `a${taskId}`);
uni.$storage.setStorageSync('taskId', ''); //
} else {
const item = allTasks.value[roleIndex.value].task.find(task => task.detailId);
if (item) {
store.commit('task/setScrollToTaskId', `a${item.id}`);
} else {
// taskId
// 线id 线
const task = allTasks.value[roleIndex.value].task.find(item => dayjs(+item.planStart).isSame(timeNode.value,
timeGranularity.value));
task && store.commit('task/setScrollToTaskId', `a${task.id}`); // task id
}
}
}
</script>
<style lang="scss" scoped>
.border-b {
border-bottom: 1px solid #e4e7ed;
}
}
}
</script>
<style lang="scss" scoped>
.border-b {
border-bottom: 1px solid #e4e7ed;
}
</style>

68
pages/submitLog/submitLog.vue

@ -0,0 +1,68 @@
<template>
<theme>
<view class="h-full w-full px-3 pt-1 overflow-y-scroll">
<view class="bg-white my-2 rounded-md p-3 text-gray-400" v-for="item in listRef">
<!-- 插件名称和提交时间显示 -->
<view class="flex justify-between mb-2">
<view class="text-gray-800">{{ deliverName }}</view>
<view class="text-xs">{{ dayjs(+item.submitTime).format('MM-DD HH:mm') }}</view>
</view>
<!-- 提交的链接 -->
<DeliverLink v-if="item.details[0]" :link="item.details[0]" />
<!-- 该插件物的审核人 -->
<view class="mb-1 mt-3">审核人</view>
<view class="flex justify-between mb-2" v-for="checkItem in item.checkerList">
<view>
<view class="mb-1 text-gray-800 font-semibold">
{{ checkItem.checkerName }}
</view>
<view class="mb-1 text-xs">
{{ checkItem.remark }}
</view>
<view class="mb-1 text-xs" v-if="+checkItem.checkTime > 0">
{{ dayjs(+checkItem.checkTime).format('MM-DD HH:mm') }}
</view>
</view>
<view class="text-center text-xs">
<view v-if="checkItem.status == null" class="text-gray-400">待审核</view>
<view v-else-if="checkItem.status === 1">
<view class="text-green-600 mb-1">已通过</view>
<zwp-ring-timing mode="chart" :value="checkItem.score" active-color="#F59E0B" radius="30" bar-width="4">
<text class="text-yellow-500 font-medium">{{ checkItem.score }}</text>
</zwp-ring-timing>
</view>
<view class="text-red-600" v-else>已驳回</view>
</view>
</view>
</view>
</view>
</theme>
</template>
<script setup>
import { ref } from 'vue';
import { onLoad } from '@dcloudio/uni-app';
import dayjs from 'dayjs';
const listRef = ref([]);
const deliverName = ref('');
onLoad(options => {
// id
(async function getHistory() {
try {
const param = { deliverId: options.deliverId };
const data = await uni.$u.api.getDeliverHistory(param);
deliverName.value = data.deliverName;
listRef.value = data.deliverRecordList;
} catch (error) {
console.log('error: ', error);
uni.$ui.showToast('获取交付物历史失败');
}
})();
});
</script>
<style lang="scss"></style>

26
pages/submitlist/submitlist.vue

@ -1,26 +0,0 @@
<template>
<theme class="h-full w-full pt-1">
<view class="bg-white m-5 rounded-md p-3">
<view class="flex justify-between text-gray-400 mb-2">
<view>插件名</view> <view>提交时间</view>
</view>
<view class="text-blue-400 mb-2">
链接
</view>
<view class="mb-2">审核人</view>
</view>
</theme>
</template>
<script>
export default {
data() {
return {
};
}
}
</script>
<style lang="scss">
</style>

84
plugins/p-deliver-check/check-form-modal.vue

@ -0,0 +1,84 @@
<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) {
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', param);
} catch (error) {
console.error('error: ', error);
uni.$ui.showToast('审核信息提交失败, 请稍后重试');
}
}
//
function handleHide() {
emits('hide');
//
score.value = 100;
commit.value = '';
}
</script>

135
plugins/p-deliver-check/p-deliver-check.vue

@ -1,70 +1,93 @@
<template>
<!-- 上传交付物 -->
<view class="box shadow-lg">
<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 class="p-3">
<!-- 交付物名称 -->
<view class="flex justify-between" @click="collapsed = !collapsed">
<!-- {{ deliverData ? deliverData.deliverName : '' }} -->
<text> 审核状态 </text>
<!-- 展开折叠按钮 -->
<u-icon :name="collapsed ? 'arrow-up' : 'arrow-down'"></u-icon>
</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 v-show="collapsed" class="mt-1">
<!-- 提交人和时间信息 -->
<view class="text-gray-400 text-xs">
上传人<text class="mr-4" v-if="deliverData.submitMemberName">{{ deliverData.submitMemberName }}</text>
上传时间
<text v-if="deliverData.submitTime"> {{ dayjs(+deliverData.submitTime).format('MM-DD HH:mm') }}</text>
</view>
<!-- 提交的链接信息 -->
<DeliverLink :link="deliverData.details[0]" v-if="deliverData.details && deliverData.details[0]" />
<!-- 审核人 标题 -->
<view class="text-gray-400 flex justify-between mt-3">
<text>审核</text>
<text class="text-blue-400 text-xs" @click="openMoreRecords">更多记录</text>
</view>
<!-- 审核人 列表 -->
<view v-if="deliverData.checkerList">
<!-- 遍历审核人信息 -->
<view class="mt-2 text-sm flex justify-between" v-for="item in deliverData.checkerList">
<view>
<view class="font-semibold">{{ item.checkerName }}</view>
<view class="text-xs text-gray-400">{{ item.remark }}</view>
<view class="text-xs text-gray-400" v-if="+item.checkTime > 0">{{ dayjs(+item.checkTime).format('MM-DD HH:mm') }}</view>
</view>
<!-- 不是自己 显示审核状态 -->
<view v-show="item.isMine !== 1" class="text-xs">
<text v-if="item.status === 1" class="text-green-600"> 已通过 </text>
<text v-else-if="item.status === 2" class="text-red-600"> 已驳回 </text>
<text v-else class="text-gray-400"> 待审核 </text>
</view>
<!-- 自己是当前审核人 且未审核状态 -->
<view v-show="item.isMine === 1 && item.status === null">
<u-button size="mini" shape="circle" class="mr-4 h-1-4 leading-1-4" type="primary" @click="checkModal.mode = 'RESOLVE'">
通过
</u-button>
<u-button size="mini" shape="circle" class="h-1-4 leading-1-4" type="error" @click="checkModal.mode = 'REJECT'">
驳回
</u-button>
</view>
<!-- 自己是审核人 且审核过 当前审核人的审核状态并展示得分情况 -->
<view v-show="item.isMine === 1 && item.status !== null" class="text-xs">
<view class="mb-1">
<text v-if="item.status === 1" class="text-green-600"> 已通过 </text>
<text v-else-if="item.status === 2" class="text-red-600"> 已驳回 </text>
</view>
<zwp-ring-timing mode="chart" :value="item.score" active-color="#F59E0B" radius="30" bar-width="4" v-if="item.score !== null">
<text class="text-yellow-500 font-medium">{{ item.score }}</text>
</zwp-ring-timing>
</view>
</view>
</view>
</view>
</view>
<checkFormModal :data="checkModal" @hide="checkModal.mode = 'HIDE'" @submit-end="$emit('check-success')" />
</view>
</template>
<script setup>
import { reactive, watchEffect } from 'vue';
import { ref, reactive, inject } from 'vue';
import dayjs from 'dayjs';
import checkFormModal from './check-form-modal.vue';
const data = reactive({
remark: '',
type: 'textarea',
border: true,
height: 100,
autoHeight: true,
wordNum: 0,
score: 0,
type1: 'number',
});
const emit = defineEmits(['submit']);
const deliverData = inject('deliver');
defineEmits(['check-success']);
const collapsed = ref(false);
watchEffect(() => {
if (remark) {
data.wordNum = remark.value.length;
}
if (score) {
data.score1 = score.value;
}
const checkModal = reactive({
mode: 'HIDE', // HIDE-> RESOLVE-> REJECT->
deliverRecordId: () => (deliverData.value ? deliverData.value.deliverRecordId : ''), // id
});
//
function submit() {
emit('submit', this.remark, this.score);
}
function sliderChange(e) {
data.score = e.detail.value;
}
function changeNumber(e) {
if (e > 100) {
data.score = 100;
}
//
function openMoreRecords() {
const { deliverRecordId } = deliverData.value;
uni.navigateTo({ url: `/pages/checkLog/checkLog?deliverRecordId=${deliverRecordId}` });
}
</script>
<style scoped lang="scss">
.box{
border-radius: 8px;
background: #fff;
padding: 16px;
overflow: hidden;
}
</style>

225
plugins/p-deliver-upload/p-deliver-upload.vue

@ -0,0 +1,225 @@
<template>
<!-- 是自己的任务 且该任务有交付物 才显示提交组件 -->
<view class="bg-white px-2 rounded-md relative" @longpress.prevent="showMask = true">
<!-- 插件名称输入和提交 -->
<view class="flex item-center justify-between py-2">
<view class="flex-1">
<text v-if="deliver.deliverName">
{{ deliver.deliverName }}
</text>
</view>
<!-- 提交 -->
<u-button
type="primary"
size="mini"
@click="submit"
class="self-center"
:disabled="submitState"
:ripple="true"
:loading="submitBtnLoading"
>
提交
</u-button>
<!-- 查看提交历史的按钮 -->
<u-icon name="arrow-right" class="ml-3" @click="openDeliverHistory"></u-icon>
</view>
<!-- 插件上传方式 -->
<view>
<view class="link-box">
<u-input v-model="linkValue" type="text" :border="true" placeholder="请输入交付物地址/链接" class="input"></u-input>
</view>
<view class="mt-3">
<u-button size="mini" :plain="true" type="primary" class="mr-3" @click="paste">粘贴</u-button>
<u-button size="mini" :plain="true" type="primary" class="mr-3" @click="uploadFile">文件</u-button>
<u-button size="mini" :plain="true" type="primary" class="mr-3" @click="uploadPhoto">拍照</u-button>
</view>
</view>
<!-- 编辑和删除的遮罩层 -->
<view class="mask flex items-center justify-center bg-grey" v-show="showMask" @click="showMask = false">
<view class="bg-yellow-500 text-white w-12 h-12 text-center leading-12 rounded-w-12 mx-8" @click.stop="showEditModal = true">
修改
</view>
<view class="bg-red-500 text-white w-12 h-12 text-center leading-12 rounded-w-12 mx-8" @click.stop="showDeleteModal = true">
删除
</view>
<!-- 删除的二次提示modal -->
<u-modal v-model="showDeleteModal" :content="content" :show-cancel-button="true" @confirm="confirmDelete"></u-modal>
</view>
<!-- 编辑交付物标题的modal -->
<u-mask :show="showEditModal" @click="showEditModal = false">
<view class="modal-content-wrap" @click.stop>
<view class="modal-content-head">交付物标题名称</view>
<view class="modal-content-body">
<u-input :border="true" placeholder="请输入交付物名称" v-model="newInputRef"></u-input>
</view>
<view class="modal-content-foot">
<view class="cancel" @click="showEditModal = false">取消</view>
<view class="confirm" @click="confirmEditDeliverName">确定</view>
</view>
</view>
</u-mask>
<!-- 插件审核人员选择 -->
<Reviewer ref="reviewerRef" />
</view>
</template>
<script setup>
import { ref, computed, inject } from 'vue';
import { useStore } from 'vuex';
import { UPLOAD_URL } from '@/config/index';
import { UPLOAD_EXTENSION } from '@/config/deliver';
const deliver = inject('deliver');
const task = inject('task');
const store = useStore();
const emits = defineEmits(['upload-success']);
const reviewerRef = ref(null);
const submitBtnLoading = ref(false);
const linkValue = ref(''); //
const showMask = ref(false); //
const showEditModal = ref(false); // modal
const newInputRef = ref(''); //
const showDeleteModal = ref(false); // modal
const content = '是否确定删除';
const showBadge = ref(false); // u-badge
//
const submitState = computed(() => !linkValue.value);
const projectId = computed(() => store.getters['project/projectId']);
//
function validateDeliverForm(checkedCheckers) {
const reg = /^http(s)?:\/\/([\w-]+\.)+[\w-]+(\/[\w- ./?%&=]*)?$/;
if (!reg.test(linkValue.value)) {
// toast
uni.$ui.showToast('请输入正确的链接');
return false;
}
//
if (!checkedCheckers || !checkedCheckers.length) {
uni.$ui.showToast('请选择检查人');
return false;
}
return true;
}
//
async function submit() {
const { checkedCheckers } = reviewerRef.value; //
//
if (!validateDeliverForm(checkedCheckers)) return;
submitBtnLoading.value = true; // loading
//
try {
const checkerList = [];
checkedCheckers.forEach(item => {
checkerList.push(item.memberId);
});
const param = {
projectId: projectId.value,
deliverId: deliver.value.deliverId,
fileList: [linkValue.value],
checkerList,
};
await uni.$u.api.submitDeliverInfo(param);
uni.$ui.showToast('提交交付物信息成功');
resetControlState(); //
emits('upload-success');
} catch (error) {
console.log('error: ', error);
uni.$ui.showToast('提交交付物信息失败');
}
}
//
function resetControlState() {
submitBtnLoading.value = false; // loading
linkValue.value = ''; //
reviewerRef.value.collapsed = true; //
}
//
function openDeliverHistory() {
const { deliverId } = deliver.value;
// console.log(deliverId)
uni.navigateTo({ url: `/pages/submitLog/submitLog?deliverId=${deliverId}` });
}
//
function paste() {
uni.getClipboardData({
success(res) {
linkValue.value = res.data;
},
});
}
//
async function uploadFile() {
try {
const data = await uni.$upload.chooseAndUpload(UPLOAD_URL, {}, UPLOAD_EXTENSION, 'files');
// console.log(data[0]);
linkValue.value = data[0].visitUrl;
} catch (error) {
console.error('error: ', error);
}
}
// TODO:
async function uploadPhoto() {
try {
const data = await uni.$upload.chooseAndUpload(UPLOAD_URL, {}, UPLOAD_EXTENSION, 'files');
// console.log(data[0])
linkValue.value = data[0].visitUrl;
} catch (error) {
console.error('error: ', error);
}
}
//
async function confirmEditDeliverName() {
// TODO: task
if (!newInputRef.value) {
uni.$ui.showToast('输入不能为空');
}
try {
const param = {
projectId: projectId.value,
taskId: task.id,
deliverName: newInputRef.value,
};
await uni.$u.api.editDeliverName(param);
// uni.$ui.showToast('');
//
showEditModal.value = false;
showMask.value = false;
showBadge.value = false;
newInputRef.value = '';
} catch (error) {
console.error('error: ', error);
uni.$ui.showToast('修改交付物名称失败');
}
}
//
async function confirmDelete() {
try {
showDeleteModal.value = true;
deliverRef.value = false;
await uni.$u.api.deleteDeliver();
uni.$ui.showToast('删除交付物成功');
} catch (error) {
console.error('error: ', error);
uni.$ui.showToast('删除交付物失败');
}
}
</script>

213
plugins/p-deliver/p-deliver.vue

@ -1,198 +1,35 @@
<template>
<!-- <view class="deliver-container">p-deliver</view> -->
<view>
<!-- 上传提交 -->
<p-deliver-upload v-if="isMine && deliver" @upload-success="getDeliverData"></p-deliver-upload>
<view class="my-2 bg-white p-2 rounded-md relative" @longpress="logoTime" v-if="deliverRef">
<!-- 插件名称输入和提交 -->
<view class="flex item-center justify-between py-3 pl-2" :class="inputRef">
<u-input v-model="iptValue" type="text" :border="false" placeholder="请编辑交付物名称" />
<view class="self-center" :class="viewRef">{{ iptValue }}</view>
<u-button type="primary" size="mini" @click="submit" class="self-center" :disabled="sbumitState">提交</u-button>
<u-icon v-show="historyIcon" name="arrow-right" class="ml-1" @click="historical"></u-icon>
</view>
<view :class="viewRef" class="py-3 pl-2">
<span class="relative px-1">
<u-badge :is-dot="true" is-center></u-badge>
{{ iptValue }}
</span>
</view>
<!-- 插件上传方式 -->
<view>
<u-input v-model="linkValue" type="text" :border="true" placeholder="请输入交付物地址/链接"> </u-input>
<view class="btns flexitems-start mt-3">
<u-button size="mini" :plain="true" style="color: #007aff" class="mr-3" @click="paste">粘贴</u-button>
<u-button size="mini" :plain="true" style="color: #007aff" class="mr-3" @click="getfile">文件</u-button>
<u-button size="mini" :plain="true" style="color: #007aff" class="mr-3" @click="photos">拍照</u-button>
</view>
</view>
<!-- 提示框 -->
<u-toast ref="tips" />
<!-- 取消和确定 -->
<u-mask :show="showRef" @click="showRef = false">
<view class="warp">
<view class="rect rounded-md" @tap.stop>
<view class="text-center my-7 font-semibold"> 交付物标题名称 </view>
<view class="">
<u-input :border="true" class="m-5" placeholder="请输入交付物名称" v-model="newInputRef" />
</view>
<view class="flex justify-around h-12 mt-7 justify-self-stretch" style="border-top: 1px solid #d1d5db">
<view class="leading-12 flex-1 text-center" style="border-right: 1px solid #d1d5db" @click="cancelClick"> 取消 </view>
<view class="text-blue-700 leading-12 flex-1 text-center" @click="sureClick"> 确定 </view>
</view>
</view>
</view>
</u-mask>
<!-- 编辑和删除的遮罩层 -->
<view class="mask flex items-center justify-center bg-grey" :class="maskRef" @click="close">
<view class="bg-yellow-500 text-white w-12 h-12 text-center leading-12 rounded-w-12 mx-8" @click="revisePlugin" @tap.stop>修改</view>
<view class="bg-red-500 text-white w-12 h-12 text-center leading-12 rounded-w-12 mx-8" @click="deletePlugin" @tap.stop>删除</view>
</view>
<!-- 插件审核人员选择 -->
<Reviewer />
</view>
<view class="box shadow-lg">
<view class="deliver-container">p-deliver</view>
<p-deliver-check v-if="deliver" @check-success="getDeliverData"></p-deliver-check>
</view>
</template>
<script setup>
import { ref, computed, reactive } from 'vue';
//
const deliverRef = ref(true);
const iptValue = ref('');
const linkValue = ref('');
const historyIcon = ref(false);
const tips = ref('');
const showRef = ref(false);
const maskRef = ref('hidden');
const inputRef = ref('block');
const viewRef = ref('hidden');
const newInputRef = ref('');
const submitHistory = reactive([]);
//
const sbumitState = computed(() => !(iptValue.value && linkValue.value));
//
function getTime() {
const MM = uni.$dayjs().$M + 1;
const DD = uni.$dayjs().$D;
const HH = uni.$dayjs().$H;
const mm = uni.$dayjs().$m;
const getTime = `${MM}/${DD} ${HH}:${mm < 10 ? `0${mm}` : mm}`;
return getTime;
}
//
function submit() {
const reg = /^http(s)?:\/\/([\w-]+\.)+[\w-]+(\/[\w- ./?%&=]*)?$/;
if (!reg.test(linkValue.value)) {
// toast
uni.$ui.showToast('请输入正确的链接');
} else {
inputRef.value = 'hidden';
viewRef.value = 'block';
const time = getTime();
const obj = {};
obj.name = iptValue.value;
obj.time = time;
obj.link = linkValue.value;
submitHistory.push(obj);
console.log(submitHistory);
import { useStore } from 'vuex';
import { ref, inject, provide, computed } from 'vue';
const store = useStore();
const task = inject('task');
const deliver = ref(null); //
const isMine = computed(() => store.getters['role/isMine']); //
provide('deliver', deliver);
// id
async function getDeliverData() {
try {
const { id: taskId } = task;
if (!taskId) return;
const param = { taskId };
const data = await uni.$u.api.getDeliverByTaskId(param);
deliver.value = data;
} catch (error) {
console.log('error: ', error);
}
}
//
function historical() {
uni.navigateTo({ url: '/pages/submitList/submitList' });
}
//
function paste() {
uni.getClipboardData({
success(res) {
linkValue.value = res.data;
},
});
}
//
function getfile() {
uni.chooseFile({
count: 1, // 100
extension: ['.zip', '.doc'],
success(res) {
linkValue.value = JSON.stringify(res.tempFilePaths);
},
});
}
//
function photos() {
uni.chooseImage({
count: 1, // 9
sizeType: ['original', 'compressed'], //
sourceType: ['album', 'camera'], //
success(res) {
linkValue.value = JSON.stringify(res.tempFilePaths);
},
});
}
function close() {
maskRef.value = 'hidden';
}
//
function logoTime() {
if (viewRef.value === 'block') {
maskRef.value = 'block';
}
}
//
function revisePlugin() {
showRef.value = true;
}
//
function cancelClick() {
showRef.value = false;
// maskRef.value = 'hidden'
}
//
function sureClick() {
iptValue.value = newInputRef.value;
newInputRef.value = '';
inputRef.value = 'block';
viewRef.value = 'hidden';
historyIcon.value = true;
showRef.value = false;
maskRef.value = 'hidden';
}
//
function deletePlugin() {
deliverRef.value = false;
}
getDeliverData();
</script>
<style lang="scss">
.warp {
display: flex;
align-items: center;
justify-content: center;
height: 80%;
}
.rect {
width: 80%;
height: 380rpx;
background-color: #fff;
}
.box {
border-radius: 8px;
background: #fff;
padding: 16px;
overflow: hidden;
}
</style>

18
plugins/p-finance-audit/p-finance-audit.vue

@ -0,0 +1,18 @@
<template>
<div class="flex justify-around">
<button class="text-xs bg-blue-500 text-white leading-6" style="width: 250rpx" @click="openAudit">财务审批</button>
<button class="text-xs bg-blue-500 text-white leading-6" style="width: 250rpx" @click="openStatistical">财务统计</button>
</div>
</template>
<script setup>
// DEBUG:
function openAudit() {
uni.$ui.openDetail({ url: 'https://www.taobao.com', name: '财务审批' });
}
// DEBUG:
function openStatistical() {
uni.$ui.openDetail({ url: 'https://www.taobao.com', name: '财务统计' });
}
</script>

73
plugins/p-finance/p-finance.vue

@ -0,0 +1,73 @@
<!-- 财务条内置组件 -->
<template>
<view class="finance-wrap" v-if="data" @click="openFinance">
<!-- 预算和奖金 -->
<view class="finance-row">
<view class="finance-item" :style="{ width: `${(data.budget * 100) / (data.budget + data.bonus)}%`, 'background-color': '#93C5FD' }">
预算{{ data.budget / 100 }}
</view>
<view class="finance-item" :style="{ width: `${(data.bonus * 100) / (data.budget + data.bonus)}%`, 'background-color': '#12c77e' }">
奖金{{ data.bonus / 100 }}
</view>
</view>
<!-- 项目采购日常采购 -->
<view class="finance-row">
<view
class="finance-item"
:style="{ width: `${(data.projectExpend * 100) / (data.projectExpend + data.dailyExpend)}%`, 'background-color': '#FBBF24' }"
>
项目采购{{ data.dailyExpend / 100 }}
</view>
<view
class="finance-item"
:style="{ width: `${(data.dailyExpend * 100) / (data.projectExpend + data.dailyExpend)}%`, 'background-color': '#a1fd93' }"
>
日常采购{{ data.dailyExpend / 100 }}
</view>
</view>
</view>
</template>
<script setup>
import { ref, inject } from 'vue';
const task = inject('task');
const data = ref(null);
//
async function getFinanceByTaskData() {
try {
console.log('task: ', task);
const detailId = task.detailId;
data.value = await uni.$u.api.getFinanceByTask(detailId);
} catch (error) {
console.log('getFinanceByTaskData error: ', error);
}
}
getFinanceByTaskData();
//
function openFinance() {
// DEBUG:
uni.$ui.openDetail({ url: 'http://www.baidu.com', name: '财务' });
}
</script>
<style lang="scss" scoped>
.finance-wrap {
.finance-row {
display: flex;
.finance-item {
margin: 3rpx 5rpx;
font-size: 12px;
text-align: center;
border-radius: 8rpx;
white-space: nowrap;
text-overflow: ellipsis;
overflow-x: visible;
}
}
}
</style>

11
plugins/p-source-manage/p-source-manage.vue

@ -0,0 +1,11 @@
<!-- 资源管理 -->
<template>
<button class="text-xs bg-blue-500 text-white leading-6" style="width: 500rpx" @click="openSourceManage">资源管理</button>
</template>
<script setup>
function openSourceManage() {
// DEBUG:
uni.$ui.openDetail({ url: 'http://m.jd.com', name: '资源管理' });
}
</script>

21
plugins/p-task-title/p-task-title.vue

@ -1,24 +1,13 @@
<template>
<!-- 任务名插件 -->
<theme class="my-2">
<view class="bg-white rounded-md h-10 leading-10 pl-2">{{ task.name }}</view>
</theme>
<view class="box shadow-lg">
<view>{{ task.name }}</view>
<view class="u-font-14">
{{ task.name }}
</view>
</template>
<script setup>
// import { inject } from 'vue';
// inject()
defineProps({ task: { type: Object, default: () => {} } });
</script>
<style scoped lang="scss">
.box {
border-radius: 8px;
background: #fff;
padding: 16px;
overflow: hidden;
}
</style>

108
rest/deliver.http

@ -0,0 +1,108 @@
@url=https://test.tall.wiki/gateway/defaultwbs
@type=content-type: application/json
@token=eyJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2NDI0MDU2ODcsInN1YiI6IjEyMTc2NDc2ODY1OTgxMzU4MDgiLCJhdXRoSWQiOiIxMTc3MTU4ODQ4ODg1MTY2MDgwIiwiZXhwIjoxNjQyNDkyMDg3fQ.GDHzvVN6kAF3mVanxYTxBBlZP1I08lCD4_zbazLX7AQ
### login
# @name login
POST https://test.tall.wiki/gateway/tall3/v3.0/users/signin
{{type}}
{
"client": 1,
"data": {
"credential": "123456",
"identifier": "zy11"
},
"type": 3
}
### 查看成员
POST {{url}}/deliver/queryChecker
{{type}}
Authorization: Bearer {{token}}
{
"param":{
"projectId": "1418109550141382656"
}
}
### 修改编辑交付物名称
POST {{url}}/deliver/saveDeliver
{{type}}
Authorization: Bearer {{token}}
{
"param":{
"projectId": "1418109550141382656",
"taskId": "1418109566541111296",
"deliverName":"测试交付物1"
}
}
### 查询交付物
POST {{url}}/deliver/getDeliver
{{type}}
Authorization: Bearer {{token}}
{
"param":{
"taskId": "1418109566541111296"
}
}
### 提交交付物信息
POST {{url}}/deliver/submitDeliver
{{type}}
Authorization: Bearer {{token}}
{
"param":{
"projectId": "1418109550141382656",
"deliverId": "1482975132598411264",
"fileList": [
"asdasd"
],
"checkerList":[
"1418109552909623296",
"1418109555648503808"
]
}
}
### 检查交付物
POST {{url}}/deliver/checkDeliver
{{type}}
Authorization: Bearer {{token}}
{
"param":{
"deliverRecordId": "1482980930758123520",
"projectId": "1418109550141382656",
"type": "2",
"remark": "我后悔了",
"score": "10.2"
}
}
### 查看交付物提交历史
POST {{url}}/deliver/queryRecord
{{type}}
Authorization: Bearer {{token}}
{
"param":{
"deliverId": "1482975132598411264"
}
}
### 查看检查记录
POST {{url}}/deliver/queryCheckLog
{{type}}
Authorization: Bearer {{token}}
{
"param":{
"deliverRecordId": "1482980930758123520"
}
}

18
store/deliver/index.js

@ -0,0 +1,18 @@
export default {
namespaced: true,
state: {
deliverRecordId: '', // 交付物记录id
},
getters: {},
mutations: {
/**
* 设置交付物记录id
* @param {object} state
* @param {string} recordId
*/
setDeliverRecordId(state, recordId) {
state.deliverRecordId = recordId;
},
},
actions: {},
};

25
store/finance/index.js

@ -0,0 +1,25 @@
import { SCENE, api } from '@/config/index';
export default {
namespaced: true,
state: {
apiPath: '/ptccsens/v1.0/finance', // 财务条apiPath
},
getters: {
// 完整路径
fullPath({ apiPath }) {
return `${api.baseUrl[SCENE]}${apiPath}`;
},
},
mutations: {
/**
* 设置apiPath
* @param {object} state
* @param {string} path 路径可能随着业务项目变化
*/
setApiPath(state, path) {
state.apiPath = path;
},
},
actions: {},
};

4
store/index.js

@ -1,4 +1,6 @@
import { createStore } from 'vuex';
import deliver from './deliver/index.js';
import finance from './finance/index.js';
import project from './project/index.js';
import role from './role/index.js';
import socket from './socket/index.js';
@ -76,5 +78,5 @@ export default createStore({
state,
getters,
mutations,
modules: { user, socket, project, role, task },
modules: { user, socket, project, role, task, deliver, finance },
});

128
utils/ui.js

@ -1,56 +1,74 @@
export default {
/**
* 显示toast
* @param {string} title 提示内容
* @param {number} duration 显示时间 默认2000
*/
showToast(title, duration = 2000) {
return uni.showToast({
title,
icon: 'none',
duration,
mask: true,
});
},
// 隐藏toast
hideToast() {
return uni.hideToast();
},
/**
* 显示加载雪花
* @param {string} title
*/
showLoading(title = '玩命加载中...') {
return uni.showLoading({
title,
mask: true,
});
},
// 隐藏loading
hideLoading() {
return uni.hideLoading();
},
/**
* 显示modal弹出框
* @param {string} title 标题
* @param {string} content 内容
* @param {boolean} showCancel 是否显示取消按钮 默认true
*/
showModal(title, content, showCancel = true) {
return new Promise(function(resolve, reject) {
uni.showModal({
title,
content,
showCancel,
success: ({ confirm, cancel }) => {
confirm && resolve();
cancel && reject();
},
});
});
},
import { stringify } from 'qs';
export default {
/**
* 显示toast
* @param {string} title 提示内容
* @param {number} duration 显示时间 默认2000
*/
showToast(title, duration = 2000) {
return uni.showToast({
title,
icon: 'none',
duration,
mask: true,
});
},
// 隐藏toast
hideToast() {
return uni.hideToast();
},
/**
* 显示加载雪花
* @param {string} title
*/
showLoading(title = '玩命加载中...') {
return uni.showLoading({
title,
mask: true,
});
},
// 隐藏loading
hideLoading() {
return uni.hideLoading();
},
/**
* 显示modal弹出框
* @param {string} title 标题
* @param {string} content 内容
* @param {boolean} showCancel 是否显示取消按钮 默认true
*/
showModal(title, content, showCancel = true) {
return new Promise(function (resolve, reject) {
uni.showModal({
title,
content,
showCancel,
success: ({ confirm, cancel }) => {
confirm && resolve();
cancel && reject();
},
});
});
},
/**
* 打开详情页
* @param {object} options
*/
openDetail(options) {
if (!options) {
throw new Error('缺少参数');
}
if (!options.url) {
throw new Error('缺少url参数');
}
const query = stringify(options);
console.log('query: ', query);
const path = `/pages/detailWebview/detailWebview?${query}`;
uni.navigateTo({ url: path });
},
};

2
utils/upload.js

@ -115,4 +115,4 @@ export default {
});
});
},
};
};

Loading…
Cancel
Save