diff --git a/.env.development b/.env.development index 6652e84..c2ca9a4 100644 --- a/.env.development +++ b/.env.development @@ -1,6 +1,6 @@ VUE_APP_NODE_ENV=development -VUE_APP_BASE_URL=https://test.tall.wiki -VUE_APP_API_URL=https://test.tall.wiki/gateway -VUE_APP_MSG_URL=wss://test.tall.wiki/websocket/message/v4.0/ws -VUE_APP_PROJECT_PATH=https://test.tall.wiki/tall-project +VUE_APP_BASE_URL=https://www.tall.wiki +VUE_APP_API_URL=https://www.tall.wiki/gateway +VUE_APP_MSG_URL=wss://www.tall.wiki/websocket/message/v4.0/ws +VUE_APP_PROJECT_PATH=https://www.tall.wiki/tall-project VUE_APP_VERSION=v3.2.0 diff --git a/CHANGELOG.md b/CHANGELOG.md index 8fef9b4..531aa2b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -# 0.1.0 (2021-10-25) +# 0.1.0 (2021-10-29) ### 🌟 新功能 范围|描述|commitId @@ -138,6 +138,7 @@ - | 提示信息显示bug及日常任务收缩问题 | f2f06c5 - | 跳转详情页返回路径修改 | c5e17c0 - | 下拉加载定期任务传参,时间格式化修改 | 0b95a0e + - | 项目操作按钮弹框显示问题 | 1d8d73e - | 项目操作弹框显示不对 | b55a915 项目列表排序 | 项目列表排序 | 402c563 - | 项目列表排序修改 | fd3c3ac diff --git a/package.json b/package.json index 6e70166..f0e6b92 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ "regenerator-runtime": "^0.12.1", "uview-ui": "^1.8.4", "vue": "^2.6.11", + "vue-clipboard2": "^0.3.3", "vuex": "^3.2.0" }, "devDependencies": { diff --git a/src/apis/plugin.js b/src/apis/plugin.js new file mode 100644 index 0000000..e01ef1e --- /dev/null +++ b/src/apis/plugin.js @@ -0,0 +1,20 @@ +// 插件的地址是固定的 +const url = process.env.VUE_APP_API_URL; + +const install = (Vue, vm) => { + vm.$u.api = { ...vm.$u.api } || {}; + // 获取插件信息 + vm.$u.api.getOtherPlugin = param => vm.$u.post(`${url}/pluginshop/plugin/query?pluginId=${param.pluginId}&styleType=${param.styleType}`); + // 查询子任务 + vm.$u.api.findSonTask = param => vm.$u.post(`${uni.$t.domain}/task/findSonTask`, param); + // 查询子项目 + vm.$u.api.findSonProject = param => vm.$u.post(`${uni.$t.domain}/project/findSonProject`, param); + // 提交交付物 + vm.$u.api.saveDeliver = param => vm.$u.post(`${uni.$t.domain}/deliver/save`, param); + // 查询任务的交付物历史记录 + vm.$u.api.queryDeliverOfTask = param => vm.$u.post(`${uni.$t.domain}/deliver/queryDeliverOfTask`, param); + // 检查交付物 + vm.$u.api.checkDeliver = param => vm.$u.post(`${uni.$t.domain}/deliver/checkDeliver`, param); +}; + +export default { install }; diff --git a/src/apis/project.js b/src/apis/project.js new file mode 100644 index 0000000..fd893a0 --- /dev/null +++ b/src/apis/project.js @@ -0,0 +1,16 @@ +const install = (Vue, vm) => { + vm.$u.api = { ...vm.$u.api } || {}; + //根据id获取项目信息 + vm.$u.api.findProjectById = param => vm.$u.post(`${uni.$t.domain}/project/findProjectById`, param); + + //创建分享连接 + vm.$u.api.createShare = param => vm.$u.post(`${uni.$t.domain}/share/create`, param); + + //点击分享连接 + vm.$u.api.clickShare = param => vm.$u.post(`${uni.$t.domain}/share/click`, param); + + //查询医院是否填写了调查问卷 + vm.$u.api.queryNotWrite = param => vm.$u.post(`${uni.$t.domain}/questionnaire/queryNotWrite`, param); +}; + +export default { install }; diff --git a/src/apis/role.js b/src/apis/role.js new file mode 100644 index 0000000..d0cc264 --- /dev/null +++ b/src/apis/role.js @@ -0,0 +1,9 @@ +const install = (Vue, vm) => { + vm.$u.api = { ...vm.$u.api } || {}; + //根据项目id查找角色 + vm.$u.api.findShowRole = param => vm.$u.post(`${uni.$t.domain}/role/show`, param); + //根据项目id查找所有成员 + vm.$u.api.queryChecker = param => vm.$u.post(`${uni.$t.domain}/deliver/queryChecker`, param); +}; + +export default { install }; diff --git a/src/apis/task.js b/src/apis/task.js new file mode 100644 index 0000000..b11d0a3 --- /dev/null +++ b/src/apis/task.js @@ -0,0 +1,17 @@ +const install = (Vue, vm) => { + vm.$u.api = { ...vm.$u.api } || {}; + vm.$u.api.getGlobal = param => vm.$u.post(`${uni.$t.domain}/task/global`, param); + vm.$u.api.getPermanent = param => vm.$u.post(`${uni.$t.domain}/task/permanent`, param); + //根据时间基准点和角色查找定期任务 + vm.$u.api.getRegularTask = param => vm.$u.post(`${uni.$t.domain}/task/regular`, param); + //修改任务状态 + vm.$u.api.updateTaskType = param => vm.$u.post(`${uni.$t.domain}/task/type`, param); + //新建任务 + vm.$u.api.saveTask = param => vm.$u.post(`${uni.$t.domain}/task/save`, param); + //克隆任务 + vm.$u.api.cloneTask = param => vm.$u.post(`${uni.$t.domain}/task/clone`, param); + //模糊查询 查找项目下的任务 + vm.$u.api.queryTaskOfProject = param => vm.$u.post(`${uni.$t.domain}/task/queryTaskOfProject`, param); +}; + +export default { install }; diff --git a/src/components/ChooseChecker/ChooseChecker.vue b/src/components/ChooseChecker/ChooseChecker.vue new file mode 100644 index 0000000..2a2f927 --- /dev/null +++ b/src/components/ChooseChecker/ChooseChecker.vue @@ -0,0 +1,87 @@ + + + + + + ... + + + + + + + + + + + diff --git a/src/components/Globals/Globals.vue b/src/components/Globals/Globals.vue new file mode 100644 index 0000000..a2af6da --- /dev/null +++ b/src/components/Globals/Globals.vue @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/components/InputSearch/InputSearch.vue b/src/components/InputSearch/InputSearch.vue new file mode 100644 index 0000000..133446b --- /dev/null +++ b/src/components/InputSearch/InputSearch.vue @@ -0,0 +1,101 @@ + + + + + {{ item.name }} + + + + + + + diff --git a/src/components/Plugin/Plugin.vue b/src/components/Plugin/Plugin.vue new file mode 100644 index 0000000..3917c72 --- /dev/null +++ b/src/components/Plugin/Plugin.vue @@ -0,0 +1,149 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/components/Projects/ProjectItem.vue b/src/components/Projects/ProjectItem.vue index 9d139cc..395b6bc 100644 --- a/src/components/Projects/ProjectItem.vue +++ b/src/components/Projects/ProjectItem.vue @@ -121,7 +121,7 @@ export default { openProject(project) { const { name, id, url } = project; url && (uni.$t.domain = url); - this.$u.route('pages/project-webview/project-webview', { + this.$u.route('pages/project/project', { u: this.userId, p: id, pname: name, diff --git a/src/components/Roles/Roles.vue b/src/components/Roles/Roles.vue new file mode 100644 index 0000000..09f6300 --- /dev/null +++ b/src/components/Roles/Roles.vue @@ -0,0 +1,252 @@ + + + + + + + + + {{ item.name }} + + + + + + + + + + + + + diff --git a/src/components/Skeleton/READ_ME.md b/src/components/Skeleton/READ_ME.md new file mode 100644 index 0000000..09ccf28 --- /dev/null +++ b/src/components/Skeleton/READ_ME.md @@ -0,0 +1,84 @@ +# skeleton组件 + +### 1.描述 +> 此组件用于加载数据时占位图显示,跟vant-ui骨架屏用法相似,但比vant-ui更灵活 + + + +### 2.用法 + +- 基本用法 + +代码: +```vue +//基本用法 + + + content + + +``` + + +- **显示 title ——通过 **title 属性显示title占位图 + +代码: +```vue +//显示 title——通过 title 属性显示title占位图 + + + content + + +``` + + +- 显示头像(上面)——通过avatar=‘top’让头像的占位图上面显示 + +代码: +```vue + + + content + + +``` + + +- 显示头像(左边)——通过avatar=‘left’让头像的占位图左边显示 + +代码: +```vue + + + content + + +``` + + +- 显示banner**——通过 **banner属性显示banner占位图(只显示banner,不显示内容占位图时设置row="0") + +代码: +```vue + + + content + + +``` +### +### 3. API +### Props +| **属性名** | **说明** | **类型** | **默认值** | 可取值 | +| --- | --- | --- | --- | --- | +| loading | 是否显示骨架屏 | Boolean | true | true/false | +| row | 段落行数 | Number | String | 3 | 0表示不展现 | +| rowWidth | 段落行宽度 | Boolean | Number | '100%' | | +| title | 是否显示标题 | Boolean | String | false | | +| banner | 是否显示banner | Boolean | String | false | | +| animate | 是否开启动画 | Boolean | String | false | | +| avatar | 头像位置 | Boolean | String | ''空 | left/top | +| avatarSize | 头像大小 | String | - | | +| avatarShape | 头像形状 | String | circle | circle/round | + diff --git a/src/components/Skeleton/Skeleton.vue b/src/components/Skeleton/Skeleton.vue new file mode 100644 index 0000000..6be25bb --- /dev/null +++ b/src/components/Skeleton/Skeleton.vue @@ -0,0 +1,187 @@ + + + + + + + + + + + + + + + + diff --git a/src/components/TimeLine/TimeLine.vue b/src/components/TimeLine/TimeLine.vue new file mode 100644 index 0000000..171b013 --- /dev/null +++ b/src/components/TimeLine/TimeLine.vue @@ -0,0 +1,127 @@ + + + + + + + + + + + + + diff --git a/src/components/TimeLine/component/Barrier.vue b/src/components/TimeLine/component/Barrier.vue new file mode 100644 index 0000000..590e4f9 --- /dev/null +++ b/src/components/TimeLine/component/Barrier.vue @@ -0,0 +1,42 @@ + + + + + + + 2021年30周 + + + + + diff --git a/src/components/TimeLine/component/TaskTools.vue b/src/components/TimeLine/component/TaskTools.vue new file mode 100644 index 0000000..a5d31fb --- /dev/null +++ b/src/components/TimeLine/component/TaskTools.vue @@ -0,0 +1,185 @@ + + + + + + + + + + + + 新建任务 + + + 克隆任务 + + + + + + + + + + + + + + + + + diff --git a/src/components/TimeLine/component/TimeBox.vue b/src/components/TimeLine/component/TimeBox.vue new file mode 100644 index 0000000..5b14178 --- /dev/null +++ b/src/components/TimeLine/component/TimeBox.vue @@ -0,0 +1,142 @@ + + + + + + + + + {{ $moment(+task.planStart).format(startTimeFormat) }} + {{ $moment(+task.planStart).format('D日') }} + + + + + + + + + + + + + + + + + + 任务面板插件 + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/components/TimeLine/component/TimeStatus.vue b/src/components/TimeLine/component/TimeStatus.vue new file mode 100644 index 0000000..7055166 --- /dev/null +++ b/src/components/TimeLine/component/TimeStatus.vue @@ -0,0 +1,231 @@ + + + + + + + + + + {{ durationText }} + + + + + + + + + + + + + {{ durationText }} + + + + + + + + + + diff --git a/src/components/TimeLine/component/Title.vue b/src/components/TimeLine/component/Title.vue new file mode 100644 index 0000000..fafec0b --- /dev/null +++ b/src/components/TimeLine/component/Title.vue @@ -0,0 +1,7 @@ + diff --git a/src/components/Tips/Tips.vue b/src/components/Tips/Tips.vue new file mode 100644 index 0000000..b485f10 --- /dev/null +++ b/src/components/Tips/Tips.vue @@ -0,0 +1,95 @@ + + + + {{ tip.text }} + + 取消 + 暂停 + 继续 + 重新开始 + 结束 + 确定 + + + + + + diff --git a/src/components/Title/Title.vue b/src/components/Title/Title.vue new file mode 100644 index 0000000..4b3b6c2 --- /dev/null +++ b/src/components/Title/Title.vue @@ -0,0 +1,233 @@ + + + + + + {{ project.name }} + + + + + + + + + + + + + + + + + + + diff --git a/src/components/Title/components/CreateTask.vue b/src/components/Title/components/CreateTask.vue new file mode 100644 index 0000000..536c360 --- /dev/null +++ b/src/components/Title/components/CreateTask.vue @@ -0,0 +1,460 @@ + + + + + 新建任务 + + 名称*: + + + + + 起止时间: + + + + + 负责人*: + {{ roleName }} + + + + + + {{ role.name }} + + + + + + + + + + + + + + + + + 描述: + + + + + 所属项目*: + {{ project.name }} + + + + 所属任务: + {{ task.name }} + + + + 上道工序: + + + + + 检查人*: + + + + + + {{ checkoutOption.name }} + + + + + + + + + + 是否是日常任务: + + + + 交付物: + + + + + + + 提交 + + + + + + + + diff --git a/src/components/Title/components/ShareProject.vue b/src/components/Title/components/ShareProject.vue new file mode 100644 index 0000000..ddb9a4b --- /dev/null +++ b/src/components/Title/components/ShareProject.vue @@ -0,0 +1,210 @@ + + + + 创建分享链接 + + 用户以什么角色加入项目 + + + + + + {{ allRolesName[index].name }} + + + + + + + + {{ links }} + + 复制链接 + + + + + + + + + + + + + + + + diff --git a/src/components/Upload/Upload.vue b/src/components/Upload/Upload.vue index f23e6e9..b268f46 100644 --- a/src/components/Upload/Upload.vue +++ b/src/components/Upload/Upload.vue @@ -19,7 +19,7 @@ export default { this.$emit('success'); data.url && (uni.$t.domain = data.url); setTimeout(() => { - this.$u.route('/pages/project-webview/project-webview', { + this.$u.route('/pages/project/project', { u: this.userId, p: data.id, pname: data.pname, diff --git a/src/components/uni-popup/message.js b/src/components/uni-popup/message.js new file mode 100644 index 0000000..577dd33 --- /dev/null +++ b/src/components/uni-popup/message.js @@ -0,0 +1,22 @@ +export default { + created() { + if (this.type === 'message') { + // 不显示遮罩 + this.maskShow = false; + // 获取子组件对象 + this.childrenMsg = null; + } + }, + methods: { + customOpen() { + if (this.childrenMsg) { + this.childrenMsg.open(); + } + }, + customClose() { + if (this.childrenMsg) { + this.childrenMsg.close(); + } + }, + }, +}; diff --git a/src/components/uni-popup/popup.js b/src/components/uni-popup/popup.js new file mode 100644 index 0000000..b646013 --- /dev/null +++ b/src/components/uni-popup/popup.js @@ -0,0 +1,23 @@ +import message from './message.js'; +// 定义 type 类型:弹出类型:top/bottom/center +const config = { + // 顶部弹出 + top: 'top', + // 底部弹出 + bottom: 'bottom', + // 居中弹出 + center: 'center', + // 消息提示 + message: 'top', + // 对话框 + dialog: 'center', + // 分享 + share: 'bottom', +}; + +export default { + data() { + return { config: config }; + }, + mixins: [message], +}; diff --git a/src/components/uni-popup/uni-popup-dialog.vue b/src/components/uni-popup/uni-popup-dialog.vue new file mode 100644 index 0000000..9102286 --- /dev/null +++ b/src/components/uni-popup/uni-popup-dialog.vue @@ -0,0 +1,246 @@ + + + + {{ title }} + + + {{ content }} + + + + + 取消 + + + 确定 + + + + + + + + diff --git a/src/components/uni-popup/uni-popup-message.vue b/src/components/uni-popup/uni-popup-message.vue new file mode 100644 index 0000000..fe401a7 --- /dev/null +++ b/src/components/uni-popup/uni-popup-message.vue @@ -0,0 +1,115 @@ + + + {{ message }} + + + + + diff --git a/src/components/uni-popup/uni-popup-share.vue b/src/components/uni-popup/uni-popup-share.vue new file mode 100644 index 0000000..224a9b7 --- /dev/null +++ b/src/components/uni-popup/uni-popup-share.vue @@ -0,0 +1,171 @@ + + + + {{ title }} + + + + + + {{ item.text }} + + + + + 取消 + + + + + + diff --git a/src/components/uni-popup/uni-popup.vue b/src/components/uni-popup/uni-popup.vue new file mode 100644 index 0000000..4d3b867 --- /dev/null +++ b/src/components/uni-popup/uni-popup.vue @@ -0,0 +1,289 @@ + + + + + + + + + + + + + diff --git a/src/components/uni-transition/uni-transition.vue b/src/components/uni-transition/uni-transition.vue new file mode 100644 index 0000000..9458e3b --- /dev/null +++ b/src/components/uni-transition/uni-transition.vue @@ -0,0 +1,276 @@ + + + + + + + + + diff --git a/src/config/db.js b/src/config/db.js new file mode 100644 index 0000000..2f0d965 --- /dev/null +++ b/src/config/db.js @@ -0,0 +1,3 @@ +export const db = null; // indexedDB 对象 +export const name = 'TALL_indexedDB'; // indexDB name +export const version = 1; // indexDB version diff --git a/src/config/plugin.js b/src/config/plugin.js new file mode 100644 index 0000000..a12856b --- /dev/null +++ b/src/config/plugin.js @@ -0,0 +1,97 @@ +// 定义插件相关信息 +/* eslint-disable */ +export default { + defaults: [ + { + id: 1, + name: 'TASK_NAME', + description: '任务名插件', + component: 'p-task-title', + }, + { + id: 2, + name: 'TASK_DESCRIPTION', + description: '任务描述插件', + component: 'p-task-description', + }, + { + id: 3, + name: 'TASK_DURATION_DELAY', + description: '任务时长延迟插件(+-1min)时间格式可设置', + component: 'p-task-duration-delay', + }, + { + id: 4, + name: 'TASK_START_TIME_DELAY', + description: '任务开始时间延迟插件(+-1hour)', + component: 'p-task-start-time-delay', + }, + { + id: 5, + name: 'DELIVERABLE', + description: '交付物插件(人 + 交付物)可配置【仅人】 or 【仅交付物】 or 【人+交付物】', + component: 'p-deliverable', + }, + { + id: 6, + name: 'SUBTASKS', + description: '子任务插件:显示子任务', + component: 'p-subtasks', + }, + { + id: 7, + name: 'SUB_PROJECT', + description: '子项目插件:显示子项目', + component: 'p-sub-project', + }, + { + id: 8, + name: 'TASK_COUNTDOWN', + description: '任务倒计时插件', + component: 'p-task-countdown', + }, + { + id: 9, + name: 'MANAGE_PROJECT', + description: '项目信息管理插件', + component: 'p-manage-project', + }, + + { + id: 10, + name: 'MANAGE_ROLE', + description: '角色信息管理插件', + component: 'p-manage-role', + }, + { + id: 11, + name: 'MANAGE_MEMBER', + description: '成员信息管理插件', + component: 'p-manage-member', + }, + { + id: 12, + name: 'MANAGE_TASK', + description: '任务信息管理插件', + component: 'p-manage-task', + }, + { + id: 13, + name: 'WBS_IMPORT', + description: '导入WBS新建项目', + component: 'p-wbs-import', + }, + { + id: 14, + name: 'WBS_IMPORT_UPDATE', + description: '导入WBS更新项目', + component: 'p-wbs-update', + }, + { + id: 15, + name: 'DELIVER_CHECK', + description: '交付物检查', + component: 'p-deliver-check', + }, + ], // 默认插件id列表 +}; diff --git a/src/config/task.js b/src/config/task.js new file mode 100644 index 0000000..95327d7 --- /dev/null +++ b/src/config/task.js @@ -0,0 +1,2 @@ +// 每页加载颗粒度的个数 +export default { pageCount: 10 }; diff --git a/src/config/time.js b/src/config/time.js new file mode 100644 index 0000000..27e412e --- /dev/null +++ b/src/config/time.js @@ -0,0 +1,17 @@ +export default { + timeUnits: [ + // 时间颗粒度 + { id: 0, value: '毫秒', format: 'x', cycle: 'YY-M-D HH:mm:ss', granularity: 'millisecond' }, + { id: 1, value: '秒', format: 'x', cycle: 'YY-M-D HH:mm:ss', granularity: 'second' }, + { id: 2, value: '分', format: 'ss', cycle: 'YY-M-D HH:mm', granularity: 'minute' }, + { id: 3, value: '时', format: 'mm', cycle: 'YY-M-D HH时', granularity: 'hour' }, + { id: 4, value: '天', format: 'D日 HH:mm', cycle: 'YY-M-D', granularity: 'day' }, + { id: 5, value: '周', format: 'D日 HH:mm', cycle: '', granularity: 'week' }, + { id: 6, value: '月', format: 'D日 H:m', cycle: 'YYYY年', granularity: 'month' }, + { id: 7, value: '季度', format: '', cycle: 'YYYY年', granularity: 'quarter' }, + { id: 8, value: '年', format: 'YYYY', cycle: '', granularity: 'year' }, + { id: 9, value: '年代', format: '', cycle: '', granularity: '' }, + { id: 10, value: '世纪', format: '', cycle: '', granularity: '' }, + { id: 11, value: '千年', format: '', cycle: '', granularity: '' }, + ], +}; diff --git a/src/main.js b/src/main.js index 6c35177..5fa4fac 100644 --- a/src/main.js +++ b/src/main.js @@ -1,10 +1,15 @@ import App from './App'; import Tall from '@/utils/tall'; import Vue from 'vue'; +import VueClipboard from 'vue-clipboard2'; import dayjs from 'dayjs'; +import plugin from '@/apis/plugin.js'; +import project from '@/apis/project.js'; import request from '@/utils/request.js'; +import role from '@/apis/role.js'; import store from './store'; import tall from '@/apis/tall.js'; +import task from '@/apis/task.js'; import uView from 'uview-ui'; import wbs from '@/apis/wbs.js'; @@ -18,6 +23,8 @@ import wbs from '@/apis/wbs.js'; // Vue.use(indexedDB); //#endif +Vue.use(VueClipboard); + Vue.config.productionTip = false; Vue.prototype.$moment = dayjs; Vue.use(uView); @@ -33,6 +40,10 @@ const app = new Vue({ ...App, store }); Vue.use(request, app); Vue.use(tall, app); +Vue.use(project, app); +Vue.use(task, app); +Vue.use(plugin, app); +Vue.use(role, app); Vue.use(wbs, app); app.$mount(); diff --git a/src/pages.json b/src/pages.json index d29817a..0367420 100644 --- a/src/pages.json +++ b/src/pages.json @@ -4,27 +4,28 @@ "path": "pages/index/index", "style": { "navigationBarText": "TALL", - //#ifdef H5 "navigationStyle": "custom" - //#endif } }, { "path": "pages/phone-bind/phone-bind", "style": { "navigationBarTitleText": "绑定手机号", - //#ifdef H5 "navigationStyle": "custom" - //#endif } }, { "path": "pages/project-webview/project-webview", "style": { "navigationBarTitleText": "项目详情页", - //#ifdef H5 "navigationStyle": "custom" - //#endif + } + }, + { + "path": "pages/project/project", + "style": { + "navigationBarTitleText": "项目详情页", + "navigationStyle": "custom" } } ], diff --git a/src/pages/project/project.vue b/src/pages/project/project.vue new file mode 100644 index 0000000..91c1e4e --- /dev/null +++ b/src/pages/project/project.vue @@ -0,0 +1,425 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/src/plugins/p-deliver-check/p-deliver-check.vue b/src/plugins/p-deliver-check/p-deliver-check.vue new file mode 100644 index 0000000..e923be5 --- /dev/null +++ b/src/plugins/p-deliver-check/p-deliver-check.vue @@ -0,0 +1,64 @@ + + + + + {{ wordNum }}/140 + + + + + + + + 提交 + 取消 + + + + + + + diff --git a/src/plugins/p-delivery-history/p-delivery-history.vue b/src/plugins/p-delivery-history/p-delivery-history.vue new file mode 100644 index 0000000..e1e10e8 --- /dev/null +++ b/src/plugins/p-delivery-history/p-delivery-history.vue @@ -0,0 +1,140 @@ + + + + + + + + {{ list.name }} + {{ $moment(+list.time).format('YYYY-MM-DD HH:mm:ss') }} + + + {{ list.content }} + {{ list.content }} + + + + + {{ checker.checkerName }} + (我) + + + 通过 + 驳回 + {{ checker.score }}分 + 未审核 + + 驳回 + 通过 + + + + {{ checker.remark }} + + + + + + + + + + + + + + + + diff --git a/src/plugins/p-manage-member/p-manage-member.vue b/src/plugins/p-manage-member/p-manage-member.vue new file mode 100644 index 0000000..e287a04 --- /dev/null +++ b/src/plugins/p-manage-member/p-manage-member.vue @@ -0,0 +1,7 @@ + + 成员管理 + + + diff --git a/src/plugins/p-manage-project/p-manage-project.vue b/src/plugins/p-manage-project/p-manage-project.vue new file mode 100644 index 0000000..ceb9371 --- /dev/null +++ b/src/plugins/p-manage-project/p-manage-project.vue @@ -0,0 +1,7 @@ + + 项目管理 + + + diff --git a/src/plugins/p-manage-role/p-manage-role.vue b/src/plugins/p-manage-role/p-manage-role.vue new file mode 100644 index 0000000..5eda40d --- /dev/null +++ b/src/plugins/p-manage-role/p-manage-role.vue @@ -0,0 +1,7 @@ + + 角色管理 + + + diff --git a/src/plugins/p-manage-task/p-manage-task.vue b/src/plugins/p-manage-task/p-manage-task.vue new file mode 100644 index 0000000..21d53f8 --- /dev/null +++ b/src/plugins/p-manage-task/p-manage-task.vue @@ -0,0 +1,7 @@ + + 任务管理 + + + diff --git a/src/plugins/p-subproject/p-subproject.vue b/src/plugins/p-subproject/p-subproject.vue new file mode 100644 index 0000000..602ccd8 --- /dev/null +++ b/src/plugins/p-subproject/p-subproject.vue @@ -0,0 +1,60 @@ + + + + + {{ item.name }} + + + + + + + diff --git a/src/plugins/p-subtasks/p-subtasks.vue b/src/plugins/p-subtasks/p-subtasks.vue new file mode 100644 index 0000000..8e55858 --- /dev/null +++ b/src/plugins/p-subtasks/p-subtasks.vue @@ -0,0 +1,39 @@ + + + + {{ item.name }} + + + + + + + diff --git a/src/plugins/p-task-countdown/p-task-countdown.vue b/src/plugins/p-task-countdown/p-task-countdown.vue new file mode 100644 index 0000000..f55572d --- /dev/null +++ b/src/plugins/p-task-countdown/p-task-countdown.vue @@ -0,0 +1,20 @@ + + + 任务倒计时插件 + + + + + diff --git a/src/plugins/p-task-description/p-task-description.vue b/src/plugins/p-task-description/p-task-description.vue new file mode 100644 index 0000000..e7db68f --- /dev/null +++ b/src/plugins/p-task-description/p-task-description.vue @@ -0,0 +1,16 @@ + + + {{ task.description }} + + + diff --git a/src/plugins/p-task-duration-delay/p-task-duration-delay.vue b/src/plugins/p-task-duration-delay/p-task-duration-delay.vue new file mode 100644 index 0000000..f435676 --- /dev/null +++ b/src/plugins/p-task-duration-delay/p-task-duration-delay.vue @@ -0,0 +1,34 @@ + + + + + + +{{ $t.time.formatDuration(realDuration - planDuration) }} + + + + -{{ $t.time.formatDuration(planDuration - realDuration) }} + + + + + diff --git a/src/plugins/p-task-start-time-delay/p-task-start-time-delay.vue b/src/plugins/p-task-start-time-delay/p-task-start-time-delay.vue new file mode 100644 index 0000000..f406ef3 --- /dev/null +++ b/src/plugins/p-task-start-time-delay/p-task-start-time-delay.vue @@ -0,0 +1,23 @@ + + + + + + {{ $t.time.formatDuration(+realStart - +planStart) }} + + + + diff --git a/src/plugins/p-task-title/p-task-title.vue b/src/plugins/p-task-title/p-task-title.vue new file mode 100644 index 0000000..dde14d0 --- /dev/null +++ b/src/plugins/p-task-title/p-task-title.vue @@ -0,0 +1,16 @@ + + + {{ task.name }} + + + diff --git a/src/plugins/p-upload-deliverable/p-upload-deliverable.vue b/src/plugins/p-upload-deliverable/p-upload-deliverable.vue new file mode 100644 index 0000000..0f19113 --- /dev/null +++ b/src/plugins/p-upload-deliverable/p-upload-deliverable.vue @@ -0,0 +1,94 @@ + + + + + + + + + + 提交 + + + + + + + + + + + diff --git a/src/plugins/p-wbs-import/p-wbs-import.vue b/src/plugins/p-wbs-import/p-wbs-import.vue new file mode 100644 index 0000000..3ca0ab6 --- /dev/null +++ b/src/plugins/p-wbs-import/p-wbs-import.vue @@ -0,0 +1,85 @@ + + + {{ task.name }} + {{ task.name }} + + + + + + diff --git a/src/store/db/actions.js b/src/store/db/actions.js new file mode 100644 index 0000000..5dfa3d4 --- /dev/null +++ b/src/store/db/actions.js @@ -0,0 +1,3 @@ +const actions = {}; + +export default actions; diff --git a/src/store/db/getters.js b/src/store/db/getters.js new file mode 100644 index 0000000..56c8c75 --- /dev/null +++ b/src/store/db/getters.js @@ -0,0 +1,3 @@ +const getters = {}; + +export default getters; diff --git a/src/store/db/index.js b/src/store/db/index.js new file mode 100644 index 0000000..d22f64a --- /dev/null +++ b/src/store/db/index.js @@ -0,0 +1,12 @@ +import state from './state'; +import getters from './getters'; +import mutations from './mutations'; +import actions from './actions'; + +export default { + namespaced: true, + state, + getters, + mutations, + actions, +}; diff --git a/src/store/db/mutations.js b/src/store/db/mutations.js new file mode 100644 index 0000000..ea2bcc2 --- /dev/null +++ b/src/store/db/mutations.js @@ -0,0 +1,3 @@ +const mutations = {}; + +export default mutations; diff --git a/src/store/db/state.js b/src/store/db/state.js new file mode 100644 index 0000000..51d33c8 --- /dev/null +++ b/src/store/db/state.js @@ -0,0 +1,7 @@ +const state = { + db: null, // indexedDB对象 + name: 'TALL_indexedDB', + version: 1, +}; + +export default state; diff --git a/src/store/index.js b/src/store/index.js index 8477da7..fb6e482 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -1,9 +1,11 @@ import Vue from 'vue'; import Vuex from 'vuex'; +import user from './user/index'; import messages from './messages/index'; -import project from './project/index'; import socket from './socket/index'; -import user from './user/index'; +import project from './project/index'; +import role from './role/index'; +import task from './task/index'; // 不属于具体模块的 应用级的 store内容 const state = { @@ -46,4 +48,4 @@ const mutations = { }; Vue.use(Vuex); -export default new Vuex.Store({ state, getters, mutations, modules: { user, messages, socket, project } }); +export default new Vuex.Store({ state, getters, mutations, modules: { user, messages, socket, project, role, task } }); diff --git a/src/store/role/actions.js b/src/store/role/actions.js new file mode 100644 index 0000000..6d279b0 --- /dev/null +++ b/src/store/role/actions.js @@ -0,0 +1,17 @@ +const actions = { + /** + * 根据项目id查找所有成员信息 + * @param {*} commit + * @param {object} params + */ + async getAllMembers({ commit }, params) { + try { + const data = await uni.$u.api.queryChecker(params); + commit('setMembers', data); + } catch (error) { + uni.$t.ui.showToast(error.msg || '成员查询失败'); + } + }, +}; + +export default actions; diff --git a/src/store/role/getters.js b/src/store/role/getters.js new file mode 100644 index 0000000..6552b4a --- /dev/null +++ b/src/store/role/getters.js @@ -0,0 +1,13 @@ +const getters = { + // 是不是负责人 + isMine({ roleId, invisibleRoles, visibleRoles }) { + if (!visibleRoles || !visibleRoles.length) return false; + const visible = visibleRoles.find(visible => visible.id === roleId); + if (visible) return visible.mine; + const invisible = invisibleRoles.find(invisible => invisible.id === roleId); + if (invisible) return visible.mine; + return false; + }, +}; + +export default getters; diff --git a/src/store/role/index.js b/src/store/role/index.js new file mode 100644 index 0000000..d22f64a --- /dev/null +++ b/src/store/role/index.js @@ -0,0 +1,12 @@ +import state from './state'; +import getters from './getters'; +import mutations from './mutations'; +import actions from './actions'; + +export default { + namespaced: true, + state, + getters, + mutations, + actions, +}; diff --git a/src/store/role/mutations.js b/src/store/role/mutations.js new file mode 100644 index 0000000..83bd215 --- /dev/null +++ b/src/store/role/mutations.js @@ -0,0 +1,39 @@ +const mutations = { + /** + * 设置不展示的角色信息 + * @param {Object} state + * @param {Array} data 服务端返回的模板数组 + */ + setInvisibleRoles(state, data) { + state.invisibleRoles = data || []; + }, + + /** + * 设置展示的角色信息 + * @param {Object} state + * @param {Array} data 服务端返回的模板数组 + */ + setVisibleRoles(state, data) { + state.visibleRoles = data || []; + }, + + /** + * 设置当前角色信息 + * @param {Object} state + * @param {string} roleId 当前正在展示的角色的id + */ + setRoleId(state, roleId) { + state.roleId = roleId; + }, + + /** + * 设置项目下所有成员信息 + * @param {Object} state + * @param {Array} data 服务端返回的模板数组 + */ + setMembers(state, data) { + state.members = data || []; + }, +}; + +export default mutations; diff --git a/src/store/role/state.js b/src/store/role/state.js new file mode 100644 index 0000000..1117de7 --- /dev/null +++ b/src/store/role/state.js @@ -0,0 +1,8 @@ +const state = { + invisibleRoles: [], // 不展示的角色信息 + visibleRoles: [], // 展示的角色信息 + roleId: '', // 当前展示查看的角色id + members: [], // 项目下所有成员 +}; + +export default state; diff --git a/src/store/task/actions.js b/src/store/task/actions.js new file mode 100644 index 0000000..40cdb31 --- /dev/null +++ b/src/store/task/actions.js @@ -0,0 +1,33 @@ +const actions = { + /** + * 根据角色查找永久的日常任务 + * @param {*} commit + * @param {string} roleId 角色id + */ + getPermanent({ commit }, param) { + uni.$t.$q.getPermanent(param, (err, data) => { + if (err) { + console.error('err: ', err); + } else { + commit('setPermanents', data); + } + }); + }, + + /** + * 根据时间和角色查找日常任务 + * @param {*} commit + * @param {object} param 请求参数 roleId, timeNode, timeUnit + */ + getGlobal({ commit }, param) { + uni.$t.$q.getGlobal(param, (err, data) => { + if (err) { + console.error('err: ', err); + } else { + commit('setDailyTasks', data); + } + }); + }, +}; + +export default actions; diff --git a/src/store/task/getters.js b/src/store/task/getters.js new file mode 100644 index 0000000..dcd7982 --- /dev/null +++ b/src/store/task/getters.js @@ -0,0 +1,23 @@ +const getters = { + // 所有的日常任务 永久 + 可变 日常任务 + globals({ dailyTasks, permanents }) { + return [...permanents, ...dailyTasks]; + }, + + unitConfig({ timeUnit }) { + const target = uni.$t.timeConfig.timeUnits.find(item => item.id === timeUnit); + return target; + }, + + // 计算任务开始时间的格式 + startTimeFormat(state, { unitConfig }) { + return unitConfig.format || 'D日 HH:mm'; + }, + + // 计算颗粒度 对应的 dayjs add 的单位 + timeGranularity(state, { unitConfig }) { + return unitConfig.granularity; + }, +}; + +export default getters; diff --git a/src/store/task/index.js b/src/store/task/index.js new file mode 100644 index 0000000..d22f64a --- /dev/null +++ b/src/store/task/index.js @@ -0,0 +1,12 @@ +import state from './state'; +import getters from './getters'; +import mutations from './mutations'; +import actions from './actions'; + +export default { + namespaced: true, + state, + getters, + mutations, + actions, +}; diff --git a/src/store/task/mutations.js b/src/store/task/mutations.js new file mode 100644 index 0000000..7e076d8 --- /dev/null +++ b/src/store/task/mutations.js @@ -0,0 +1,208 @@ +const mutations = { + /** + * 记录时间轴向上滚动的距离 + * @param { object } state + * @param { number } num + */ + setScrollTop(state, num) { + state.scrollTop = num; + }, + + /** + * 记录时间轴向上滚动的距离 + * @param { object } state + * @param {string} taskId + */ + setScrollToTaskId(state, taskId) { + state.scrollToTaskId = taskId; + }, + + /** + * 设置日常任务当前是否应该处于收缩状态 + * @param { object } state + * @param { boolean } data + */ + setShrink(state, data) { + state.isShrink = data; + }, + + /** + * 设置tip的值 + * @param {object} state + * @param {object} data + */ + setTip(state, data) { + if (!data) return; + state.tip = { ...data }; + }, + + /** + * 是否显示tips + * @param { object } state + * @param { boolean } show + */ + setTipShow(state, show) { + state.tip.show = show; + }, + + /** + * 是否显示tips + * @param { object } state + * @param { number } status + */ + setStatus(state, status) { + state.tip.status = status; + }, + + /** + * 设置时间基准点 + * @param { object } state + * @param { number } data + */ + setTimeNode(state, data) { + state.timeNode = data; + }, + + /** + * 设置时间颗粒度 + * @param { object } state + * @param { number } data + */ + setTimeUnit(state, data) { + state.timeUnit = data; + }, + + /** + * 设置向上查到的定期任务数据 + * @param {Object} state + * @param {Array} data 服务端返回的模板数组 + */ + setUpTasks(state, data) { + if (!state.tasks.length) { + state.tasks = [...data]; // 原来没有数据 + } else { + state.tasks = [...data, ...state.tasks]; + // state.tasks = [...data.concat(state.tasks)]; + } + }, + + /** + * 设置向下查到的定期任务数据 + * @param {Object} state + * @param {Array} data 服务端返回的模板数组 + */ + setDownTasks(state, data) { + console.log('setDownTasks: '); + if (!state.tasks && !state.tasks.length) { + state.tasks = [...data]; + } else { + state.tasks = [...state.tasks, ...data]; + // state.tasks = [...state.tasks.concat(data)]; + } + }, + + /** + * 添加任务后更新tasks + * @param {Object} state + * @param {Array} data 新添加的task + */ + updateTasks(state, data) { + console.log('updateTasks: '); + state.tasks = [...data]; + }, + + /** + * 设置添加任务的位置 + * @param {*} state + * @param {*} data + */ + setAddPosition(state, data) { + console.log('data: ', data); + }, + + /** + * 设置日常任务数据 + * @param {Object} state + * @param {Array} data 服务端返回的模板数组 + */ + setDailyTasks(state, data) { + state.dailyTasks = data || []; + }, + + /** + * 设置永久固定任务 + * @param {object} state + * @param {array} tasks 服务端查询到的永久日常任务书籍 + */ + setPermanents(state, tasks) { + state.permanents = tasks || []; + }, + + /** + * 设置时间轴是否继续向上查任务 + * @param {Object} state + * @param {Boolean} show + */ + setTopEnd(state, show) { + state.topEnd = show; + }, + + /** + * 设置时间轴是否继续向下查任务 + * @param {Object} state + * @param {Boolean} show + */ + setBottomEnd(state, show) { + state.bottomEnd = show; + }, + + // 清空标志位 如切换角色等使用 + clearEndFlag(state) { + state.topEnd = false; + state.bottomEnd = false; + }, + + // 清空定期任务 + clearTasks(state) { + state.tasks = []; + }, + + /** + * 收到消息设置任务状态 + * @param {Object} state + * @param {Array} data 服务端返回的模板数组 + */ + setTaskStatus(state, data) { + const item = state.tasks.find(i => i.id === data.id); + item.process = data.taskStatus; + }, + + /** + * 收到打开新项目消息状态 + * @param {Object} state + * @param {Array} data 服务端返回的模板数组 + */ + setNewProjectInfo(state, data) { + state.newProjectInfo = data; + }, + + /** + * 设置骨架屏是否显示 + * @param {Object} state + * @param {Boolean} show + */ + setShowSkeleton(state, show) { + state.showSkeleton = show; + }, + + /** + * 是否设置时间轴自动滚动的位置 + * @param {Object} state + * @param {Boolean} show + */ + setShowScrollTo(state, show) { + state.showScrollTo = show; + }, +}; + +export default mutations; diff --git a/src/store/task/state.js b/src/store/task/state.js new file mode 100644 index 0000000..023378d --- /dev/null +++ b/src/store/task/state.js @@ -0,0 +1,25 @@ +const state = { + scrollTop: 0, + scrollToTaskId: '', // 时间轴自动滚动的位置 + isShrink: false, // true: 收起, false:展开 + tip: { + taskId: '', // 当前正在修改状态的任务的id + show: false, + status: 0, // 所点击任务的当前状态码 + text: '', + left: 0, // 鼠标点击位置距离左边的距离 + top: 0, // 鼠标点击位置距离上边的距离 + }, + timeNode: new Date().getTime(), // 时间基准点 + timeUnit: 4, // 时间颗粒度 + topEnd: false, // 时间轴向上查任务到顶了 + bottomEnd: false, // 时间轴向下查任务到底了 + permanents: [], // 永久日常任务 + dailyTasks: [], // 日常任务 + tasks: [], // 所有的定期任务 + showSkeleton: false, // 定期任务骨架屏 + newProjectInfo: {}, + showScrollTo: false, // 是否可以设置时间轴自动滚动的位置 +}; + +export default state; diff --git a/src/test/util/task.test.js b/src/test/util/task.test.js new file mode 100644 index 0000000..75ee94a --- /dev/null +++ b/src/test/util/task.test.js @@ -0,0 +1,24 @@ +import { computeFillPlaceholderTaskCount } from '../../utils/task'; + +describe('computeFillPlaceholderTaskCount', () => { + // 2021/8/17 ~ 2021/8/21 + const tasks = [ + { id: '99724910037144221455', panel: {}, plugins: [], process: 4, planStart: 1629169800242 }, + { id: '65053357415671253512', panel: {}, plugins: [], process: 4, planStart: 1629256200242 }, + { id: '38735454515347179194', panel: {}, plugins: [], process: 4, planStart: 1629342600242 }, + { id: '49602681534756706607', panel: {}, plugins: [], process: 4, planStart: 1629429000242 }, + { id: '98860265376222512018', panel: {}, plugins: [], process: 4, planStart: 1629515400242 }, + { id: '44419041575700334936', panel: {}, plugins: [], process: 4, planStart: 1629601800242 }, + ]; + const timeGranularity = 'day'; + + it('超出上限 补齐', () => { + const data = [{ planStart: `${new Date('2021/8/10').getTime()}` }, { planStart: `${new Date('2021/8/11').getTime()}` }]; + expect(computeFillPlaceholderTaskCount({ tasks, data, timeGranularity }).prev.toString()).toMatch(/(7|8)/); + }); + + it('超出下限 补齐', () => { + const data = [{ planStart: `${new Date('2021/8/10').getTime()}` }, { planStart: `${new Date('2021/8/22').getTime()}` }]; + expect(computeFillPlaceholderTaskCount({ tasks, data, timeGranularity }).next.toString()).toMatch(/(1|2)/); + }); +}); diff --git a/src/test/util/time.test.js b/src/test/util/time.test.js new file mode 100644 index 0000000..11e64b2 --- /dev/null +++ b/src/test/util/time.test.js @@ -0,0 +1,46 @@ +import Time from '../../utils/time.js'; + +// 测试计算进行中剩余时长显示数值 +describe('utils/time.js computeDurationText function', () => { + const { computeDurationText } = Time; + // const leftTime = +realStart + +planDuration - Date.now(); // 剩余时间 + it ('leftTime is 60ms, num=60, time=16', () => { + expect(computeDurationText(60)).toEqual({ num: 60, time: 16 }) + }) + + it ('leftTime is 300ms, num=300, time=16', () => { + expect(computeDurationText(300)).toEqual({ num: 300, time: 16 }) + }) + + it ('leftTime is 10s20ms, num=10, time=1000', () => { + expect(computeDurationText(10*1000 + 20)).toEqual({ num: 10, time: 1000 }) + }) + + it ('leftTime is 8分钟10s20ms, num=8, time=1000', () => { + expect(computeDurationText(8*60*1000 + 10*1000 + 20)).toEqual({ num: 8, time: 1000 }) + }) + + it ('leftTime is 3小时8分钟10s20ms, num=3, time=1000', () => { + expect(computeDurationText(3*60*60*1000 + 8*60*1000 + 10*1000 + 20)).toEqual({ num: 3, time: 1000 }) + }) + + it ('leftTime is 11天3小时8分钟10s20ms, num=11, time=60 * 60 * 1000', () => { + expect(computeDurationText(11*24*60*60*1000 + 3*60*60*1000 + 8*60*1000 + 10*1000 + 20)).toEqual({ num: 11, time: 60 * 60 * 1000 }) + }) + + it ('leftTime is 2个月11天3小时8分钟10s20ms, num=2, time=60 * 60 * 1000', () => { + expect(computeDurationText(2*30*24*60*60*1000 + 11*24*60*60*1000 + 3*60*60*1000 + 8*60*1000 + 10*1000 + 20)).toEqual({ num: 2, time: 60 * 60 * 1000 }) + }) + + it ('leftTime is 7年2个月11天3小时8分钟10s20ms, num=7, time=60 * 60 * 1000', () => { + expect(computeDurationText(7*12*30*24*60*60*1000 + 2*30*24*60*60*1000 + 11*24*60*60*1000 + 3*60*60*1000 + 8*60*1000 + 10*1000 + 20)).toEqual({ num: 7, time: 60 * 60 * 1000 }) + }) + + it ('leftTime <=0, num=0, time=null', () => { + expect(computeDurationText(-10)).toEqual({ num: 0, time: null }) + }) + + it ('leftTime 不是数字, num=0, time=null', () => { + expect(computeDurationText('abc')).toEqual({ num: 0, time: null }) + }) +}) diff --git a/src/utils/cache.js b/src/utils/cache.js index f2b7dc8..078e08a 100644 --- a/src/utils/cache.js +++ b/src/utils/cache.js @@ -10,6 +10,111 @@ export const filter = { if (!data || !data.length) return []; return data.filter(item => start <= +item.endTime && end >= +item.startTime); }, + /** + * 角色 过滤获取到的数据 根据开始截止时间 + * @param {object} data 缓存拿到的数据 + * @returns + */ + roles(data) { + if (!data || !data.length) return []; + return data; + }, + + /** + * 定期任务 过滤获取到的数据 根据开始截止时间 + * @param {object} data 缓存拿到的数据 + * @param {number} timeNode 时间基准点 ms + * @param {number} queryNum 颗粒度数量 + * @param {number} timeUnit 时间颗粒度 + * @param {number} queryType 0向上查找 1向下查找(默认) 下查包含自己,上查不包含 + * @returns + */ + planTask(data, timeNode, queryNum, timeUnit, queryType) { + if (!data || !data.length) return []; + if (queryType === 0) { + // 计算颗粒度 对应的 dayjs add 的单位 + let target = uni.$t.timeConfig.timeUnits.find(item => item.id === timeUnit); + // TODO: 缺少通过时间颗粒度筛选数据 任务没有返回时间颗粒度标签 + let start = uni.$t.time.add(+timeNode, -queryNum, target.granularity).valueOf(); + let arr = []; + arr = data.filter(item => start <= +item.planStart && +timeNode > +item.planEnd); + + if (!arr || !arr.length) { + // 开始时间往前推 + let resultS = []; + let againStart = uni.$t.time.add(start, -1, target.granularity).valueOf(); + let againArr = data.filter(item => againStart >= +item.planStart); + if (againArr && againArr.length) { + let sTime = uni.$t.time.setTimestampToStr(+againArr[0].planStart); + data.forEach(item => { + if (uni.$t.time.isSame(uni.$moment(sTime.date).valueOf(), +item.planStart, target.granularity)) { + resultS.push(item); + } + }); + } + return resultS; + } else { + return arr; + } + } else { + // 计算颗粒度 对应的 dayjs add 的单位 + let target = uni.$t.timeConfig.timeUnits.find(item => item.id === timeUnit); + // TODO: 缺少通过时间颗粒度筛选数据 任务没有返回时间颗粒度标签 + let end = uni.$t.time.add(timeNode, +queryNum - 1, target.granularity).valueOf(); + let arr = []; + arr = data.filter(item => end >= +item.planEnd && +timeNode <= +item.planStart); + + if (!arr || !arr.length) { + // 结束时间往后推 + let resultE = []; + let againEnd = uni.$t.time.add(end, 1, target.granularity).valueOf(); + let againEndArr = data.filter(item => againEnd <= +item.planStart); + if (againEndArr) { + let eTime = uni.$t.time.setTimestampToStr(+againEndArr[againEndArr.length - 1].planStart); + data.forEach(item => { + if (uni.$t.time.isSame(uni.$moment(eTime.date).valueOf(), +item.planEnd, target.granularity)) { + resultE.push(item); + } + }); + } + return resultE; + } else { + return arr; + } + } + }, + + /** + * 永久日常任务 过滤获取到的数据 根据开始截止时间 + * @param {object} data 缓存拿到的数据 + * @returns + */ + fixedTasks(data) { + if (!data || !data.length) return []; + return data; + }, + + /** + * 日常任务 过滤获取到的数据 根据开始截止时间 + * @param {object} data 缓存拿到的数据 + * @param {number} timeNode 时间基准点 ms + * @returns + */ + dailyTask(data, timeNode) { + if (!data || !data.length) return []; + // TODO: 缺少通过时间颗粒度筛选数据 任务没有返回时间颗粒度标签 + return data.filter(item => timeNode <= +item.endTime && timeNode >= +item.startTime); + }, + + /** + * 插件 过滤获取到的数据 根据插件id + * @param {object} data 缓存拿到的数据 + * @returns + */ + plugin(data) { + if (!data || !data.id) return null; + return data; + }, }; export default { @@ -59,4 +164,240 @@ export default { uni.$t.storage.setStorage('projects', []); } }, + + /** + * 当前显示的角色信息 获取 + * @param {object} params + * @returns + */ + async getShowRole(projectId) { + try { + const data = await uni.$t.storage.getStorage(`roles_${projectId}`); + return filter.roles(JSON.parse(data)); + } catch (error) { + return null; + } + }, + + /** + * 当前显示的角色信息 存 + * @param {array} data + */ + putShowRole(projectId, data) { + try { + if (!data || !data.visibleList || !data.visibleList.length) return; // 服务端没数据不做操作 + let value = uni.$t.storage.getStorageSync(`roles_${projectId}`); + let locals = value ? JSON.parse(value) : null; + if (!locals || !locals.length) { + // 本地没数据 + locals = data || null; + } else { + // 本地有数据 + data.invisibleList.forEach(item => { + let invisibleListLocalData = locals.invisibleList.find(local => item.id === local.id); + if (invisibleListLocalData) { + // 有相同数据 就用新的data里的数据 + invisibleListLocalData = item; + } else { + // 没有就直接存本地 + locals.invisibleList.push(item); + } + }); + data.visibleList.forEach(item => { + let localData = locals.visibleList.find(local => item.id === local.id); + if (localData) { + // 有相同数据 就用新的data里的数据 + localData = item; + } else { + // 没有就直接存本地 + locals.visibleList.push(item); + } + }); + } + uni.$t.storage.setStorage(`roles_${projectId}`, locals); + } catch (error) { + console.error('error: ', error); + uni.$t.storage.setStorage(`roles_${projectId}`, []); + } + }, + + /** + * 定期任务 获取 + * @param {number} startTime + * @param {number} endTime + * @returns + */ + async getStorageRegularTask(params) { + try { + console.log('*********------------------------------------------'); + const data = await uni.$t.storage.getStorage(`plan_task_${params.projectId}_${params.roleId}`); + return filter.planTask(JSON.parse(data), params.timeNode, params.queryNum, params.timeUnit, params.queryType); + } catch (error) { + return []; + } + }, + + /** + * 定期任务 存 + * @param {array} data + */ + putStorageRegularTask(params, data) { + try { + if (!data || !data.length) return; // 服务端没数据不做操作 + let value = uni.$t.storage.getStorageSync(`plan_task_${params.projectId}_${params.roleId}`); + let locals = value ? JSON.parse(value) : []; + if (!locals || !locals.length) { + // 本地没数据 + locals = data || []; + } else { + // 本地有数据 + data.forEach(item => { + let localData = locals.find(local => item.id === local.id); + if (localData) { + // 有相同数据 就用新的data里的数据 + localData = item; + } else { + // 没有就直接存本地 + locals.push(item); + } + }); + } + uni.$t.storage.setStorage(`plan_task_${params.projectId}_${params.roleId}`, locals); + } catch (error) { + console.error('error: ', error); + uni.$t.storage.setStorage(`plan_task_${params.projectId}_${params.roleId}`, []); + } + }, + + /** + * 永久的日常任务 获取 + * @param {number} startTime + * @param {number} endTime + * @returns + */ + async getStoragePermanent(params) { + try { + const data = await uni.$t.storage.getStorage(`fixed_tasks_${params.projectId}_${params.roleId}`); + return filter.fixedTasks(JSON.parse(data)); + } catch (error) { + return []; + } + }, + + /** + * 永久的日常任务 存 + * @param {array} data + */ + putStoragePermanent(params, data) { + try { + if (!data || !data.length) return; // 服务端没数据不做操作 + let value = uni.$t.storage.getStorageSync(`fixed_tasks_${params.projectId}_${params.roleId}`); + let locals = value ? JSON.parse(value) : []; + if (!locals || !locals.length) { + // 本地没数据 + locals = data || []; + } else { + // 本地有数据 + data.forEach((item, index) => { + let localData = locals.find(local => item.detailId === local.detailId); + if (localData) { + // 有相同数据 就用新的data里的数据 + localData = item; + } else { + locals.splice(index, 1); + // 没有就直接存本地 + locals.push(item); + } + }); + } + uni.$t.storage.setStorage(`fixed_tasks_${params.projectId}_${params.roleId}`, locals); + } catch (error) { + console.error('error: ', error); + uni.$t.storage.setStorage(`fixed_tasks_${params.projectId}_${params.roleId}`, []); + } + }, + + /** + * 日常任务 获取 + * @param {number} timeNode + * @returns + */ + async getDailyTask(params) { + try { + const data = await uni.$t.storage.getStorage(`variable_tasks_${params.projectId}_${params.roleId}`); + return filter.dailyTask(JSON.parse(data), params.timeNode); + } catch (error) { + return []; + } + }, + + /** + * 日常任务 存 + * @param {array} data + */ + putDailyTask(params, data) { + try { + if (!data || !data.length) return; // 服务端没数据不做操作 + let value = uni.$t.storage.getStorageSync(`variable_tasks_${params.projectId}_${params.roleId}`); + let locals = value ? JSON.parse(value) : []; + if (!locals || !locals.length) { + // 本地没数据 + locals = data || []; + } else { + // 本地有数据 + data.forEach(item => { + let localData = locals.find(local => item.detailId === local.detailId); + if (localData) { + // 有相同数据 就用新的data里的数据 + localData = item; + } else { + // 没有就直接存本地 + locals.push(item); + } + }); + } + uni.$t.storage.setStorage(`variable_tasks_${params.projectId}_${params.roleId}`, locals); + } catch (error) { + console.error('error: ', error); + uni.$t.storage.setStorage(`variable_tasks_${params.projectId}_${params.roleId}`, []); + } + }, + + /** + * 插件信息 获取 + * @param {string} pluginId + * @returns + */ + async getPlugin(pluginId) { + try { + const data = await uni.$t.storage.getStorage(`plugin_${pluginId}`); + return filter.plugin(JSON.parse(data)); + } catch (error) { + return null; + } + }, + + /** + * 插件信息 存 + * @param {string} pluginId + * @param {object} data + */ + putPlugin(pluginId, data) { + try { + if (!data || !data.id) return; // 服务端没数据不做操作 + let value = uni.$t.storage.getStorageSync(`plugin_${pluginId}`); + let locals = value ? JSON.parse(value) : null; + if (!locals || !locals.length) { + // 本地没数据 + locals = data || null; + } else { + // 本地有数据 + locals = data; + } + uni.$t.storage.setStorage(`plugin_${pluginId}`, locals); + } catch (error) { + console.error('error: ', error); + uni.$t.storage.setStorage(`plugin_${pluginId}`, null); + } + }, }; diff --git a/src/utils/cacheAndRequest.js b/src/utils/cacheAndRequest.js index 32aa0f7..c4e60d6 100644 --- a/src/utils/cacheAndRequest.js +++ b/src/utils/cacheAndRequest.js @@ -78,24 +78,23 @@ export default { * @param {object} params 提交的参数 */ getRegularTask(params, fn) { - let remote = false; + // let remote = false; // 有缓存 且 服务端数据未返回 就先返回缓存 - uni.$t.cache - .getStorageRegularTask(params) - .then(data => { - console.log('cache data: ', data); - !remote && fn(null, data); - }) - .catch(err => !remote && fn(err)); + // uni.$t.cache + // .getStorageRegularTask(params) + // .then(data => { + // console.log('cache data: ', data); + // !remote && fn(null, data); + // }) + // .catch(err => !remote && fn(err)); waitTokenRequest(() => { // 拿到api数据后 再用api的数据 uni.$u.api .getRegularTask(params) .then(data => { - console.log('api data: ', uni.$u.deepClone(data)); - remote = true; - + // console.log('api data: ', uni.$u.deepClone(data)); + // remote = true; fn(null, uni.$u.deepClone(data)); // 存api到cache里 uni.$t.cache.putStorageRegularTask(params, data); diff --git a/src/utils/indexedDB.js b/src/utils/indexedDB.js new file mode 100644 index 0000000..30a0e90 --- /dev/null +++ b/src/utils/indexedDB.js @@ -0,0 +1,163 @@ +import { name } from '@/config/db'; +import { curry } from 'lodash'; + +// 创建表 +const createCollection = (Vue, db) => { + // projects项目表 + !db.objectStoreNames.contains('projects') && db.createObjectStore('projects', { keyPath: 'id' }); + // roles 角色表 + !db.objectStoreNames.contains('roles') && db.createObjectStore('roles', { keyPath: 'id' }); + // plan_tasks 定期任务 + !db.objectStoreNames.contains('plan_tasks') && db.createObjectStore('plan_tasks', { keyPath: 'id' }); + // fixed_tasks 固定全局任务 + Vue.prototype.$db.fixed_tasks = !db.objectStoreNames.contains('fixed_tasks') && db.createObjectStore('fixed_tasks', { keyPath: 'id' }); + // variable_tasks 可变全局任务 + Vue.prototype.$db.variable_tasks = + !db.objectStoreNames.contains('variable_tasks') && db.createObjectStore('variable_tasks', { keyPath: 'id' }); + // plugins 插件表 + Vue.prototype.$db.plugins = !db.objectStoreNames.contains('plugins') && db.createObjectStore('plugins', { keyPath: 'id' }); +}; + +/** + * 新增数据 + * + * @param {object} db 数据库database + * @param {string} collection 集合/表 + * @param {object} data 数据 + */ +const create = (db, collection, data) => { + return new Promise((resolve, reject) => { + const request = db.transaction([collection], 'readwrite').objectStore(collection).add(data); + request.onsuccess = () => resolve(); + + request.onerror = event => { + const { name, message } = event.target.error; + if (name === 'ConstraintError') { + reject('数据已存在'); + } else { + reject(message); + } + }; + }); +}; + +/** + * 找到1条数据 + * + * @param {object} db 数据库database + * @param {string} collection 集合/表 + * @param {string} key 索引关键字 一般是id + */ +const findOne = (db, collection, key) => { + return new Promise((resolve, reject) => { + const request = db.transaction([collection]).objectStore(collection).get(key); + request.onerror = event => reject(event.target.error.message); + request.onsuccess = event => resolve(event.target.result); + }); +}; + +/** + * 找到所有数据 + * + * @param {object} db 数据库database + * @param {string} collection 集合/表 + */ +const find = (db, collection) => { + return new Promise((resolve, reject) => { + const request = db.transaction(collection).objectStore(collection).openCursor(); + let result = []; + + request.onerror = event => reject(event.target.error.message); + request.onsuccess = event => { + const cursor = event.target.result; + if (cursor) { + result.push(cursor.value); + cursor.continue(); + } else { + resolve(result); + } + }; + }); +}; + +/** + * 更新数据 + * + * @param {object} db 数据库database + * @param {string} collection 集合/表 + * @param {object} newData 新数据 + */ +const update = (db, collection, newData) => { + return new Promise((resolve, reject) => { + const request = db.transaction([collection], 'readwrite').objectStore(collection).put(newData); + request.onerror = event => reject(event.target.error.message); + request.onsuccess = () => resolve(newData); + }); +}; + +/** + * 移除数据 通过关键字 + * + * @param {object} db 数据库database + * @param {string} collection 集合/表 + * @param {string} key 关键字 + */ +const remove = (db, collection, key) => { + return new Promise((resolve, reject) => { + const request = db.transaction([collection], 'readwrite').objectStore(collection).delete(key); + request.onerror = event => reject(event.target.error.message); + request.onsuccess = () => resolve(); + }); +}; + +/** + * 创建索引 + * + * @param {object} db 数据库database + * @param {string} collection 集合/表 + * @param {string} field 创建索引的字段名称 + * @param {string} key 关键字 + */ +const createIndexAndFind = (db, collection, field, key) => { + return new Promise((resolve, reject) => { + const store = db.transaction([collection], 'readonly').objectStore(collection); + store.createIndex(field, field); + const index = store.index(field); + const request = index.get(key); + request.onerror = event => reject(event.target.error.message); + request.onsuccess = event => resolve(event.target.result); + }); +}; + +const curriedCreate = curry(create); +export const curriedFindOne = curry(findOne); +export const curriedFind = curry(find); +export const curriedRemove = curry(remove); +export const curriedUpdate = curry(update); +export const curriedIndex = curry(createIndexAndFind); + +const install = Vue => { + uni.$db = Vue.prototype.$db = {}; + Vue.prototype.$db.indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB; + const request = Vue.prototype.$db.indexedDB.open(name, Date.now()); // IDBRequest 对象 + request.onerror = error => console.error('打开数据库失败', error); + request.onsuccess = event => { + console.log('INDEXED_DB OPEN SUCCESS'); + Vue.prototype.$db.db = event.target.result; + }; + request.onupgradeneeded = event => { + console.log('INDEXED_DB OPEN onupgradeneeded'); + Vue.prototype.$db.db = event.target.result; + // 创建表 + createCollection(Vue, Vue.prototype.$db.db); + + Vue.prototype.$db.create = curriedCreate(Vue.prototype.$db.db); // create 新增数据,颗粒化以后就不用再传db数据了 + Vue.prototype.$db.findOne = curriedFindOne(Vue.prototype.$db.db); // 查一条 + Vue.prototype.$db.find = curriedFind(Vue.prototype.$db.db); // 查集合里的所有数据 + Vue.prototype.$db.update = curriedUpdate(Vue.prototype.$db.db); // 更新某条数据 + Vue.prototype.$db.remove = curriedRemove(Vue.prototype.$db.db); // 删除某条数据 + // Vue.prototype.$db.createIndex = curriedIndex(Vue.prototype.$db.db); // 创建索引 + }; +}; + +export default { install }; diff --git a/src/utils/tall.js b/src/utils/tall.js index 39c0ca5..19c5b17 100644 --- a/src/utils/tall.js +++ b/src/utils/tall.js @@ -1,26 +1,32 @@ import app from '@/config/app.js'; import cache from '@/utils/cache.js'; import cacheAndRequest from '@/utils/cacheAndRequest.js'; +import plugin from '@/config/plugin.js'; import storage from '@/utils/storage.js'; import time from '@/utils/time.js'; +import timeConfig from '@/config/time'; import ui from '@/utils/ui.js'; import upload from '@/utils/upload.js'; import user from '@/config/user.js'; import zIndex from '@/config/zIndex.js'; +import task from '@/config/task.js'; const gateway = process.env.VUE_APP_API_URL; const $t = { zIndex, // 定位元素层级 app, // app级别的相关配置 + plugin, // 插件相关配置信息 storage, // 本地存储storage封装 time, // 时间处理 + timeConfig, // 时间相关配置 ui, // ui界面提示相关 chooseAndUpload: upload.chooseAndUpload, // 选择并上传单个文件相关的封装 domain: `${gateway}/defaultwbs`, cache, // 本地存储相关 $q: cacheAndRequest, user, // 用户相关配置 + task, // 任务相关配置 }; uni.$t = $t; diff --git a/src/utils/task.js b/src/utils/task.js new file mode 100644 index 0000000..17dd2f8 --- /dev/null +++ b/src/utils/task.js @@ -0,0 +1,53 @@ +import dayjs from 'dayjs'; + +/** + * 设置时间轴空数据 + * @param {number} startTime + * @param {boolean} isUp true 向上加载,false 向下加载 + * @param {string} timeGranularity 颗粒度 + * @param {number} pageCount 加载的颗粒度数量 默认值是10 + */ +export const setPlaceholderTasks = (startTime, isUp, timeGranularity, pageCount) => { + let result = []; + pageCount = pageCount || uni.$t.task.pageCount; + for (let i = 0; i < pageCount; i++) { + const delta = isUp ? `-${i + 1}` - 0 : i + 1; + let item = { + id: uni.$u.guid(20, false, 10), + panel: {}, + plugins: [], + process: 4, + planStart: uni.$moment(startTime).add(delta, timeGranularity).valueOf(), + }; + // console.log('isup: ', isUp, 'result:', new Date(item.planStart).toLocaleDateString()); + + isUp ? result.unshift(item) : result.push(item); + } + return result; +}; + +/** + * 超出旧数据上、下限 补齐时间刻度到新数据的起始时间颗粒度 + * @param {object} option + * @param {array} option.tasks 旧的已有的任务书籍 + * @param {array} option.data 新拿到的任务数据 空值已经过滤过了 + * @param {string} option.timeGranularity 颗粒度 + */ +export const computeFillPlaceholderTaskCount = ({ tasks, data, timeGranularity }) => { + const result = { prev: 0, next: 0 }; + // 新数据的开始时间 < 旧数据的开始时间 + // 超出了上限 补上限的时间刻度 + // 补上 新数据开始时间 到 旧数据开始时间 的刻度 + if (+data[0].planStart < +tasks[0].planStart) { + // 找出来需要补几组颗粒度 + result.prev = dayjs(+tasks[0].planStart).diff(+data[0].planStart, timeGranularity) + 1; + } + + // 新数据的结束时间 > 旧数据的结束时间 + // 超出了下线 补下限的时间刻度 + // 补上 旧数据截止时间 到 新数据截止时间 的刻度 + if (+data[data.length - 1].planStart > +tasks[tasks.length - 1].planStart) { + result.next = dayjs(+data[data.length - 1].planStart).diff(+tasks[tasks.length - 1].planStart, timeGranularity) + 1; + } + return result; +};