Browse Source

Merge branch 'feat' of ssh://101.201.226.163:50022/TALL/TALL-MUI-4 into feat

feat
wally 4 years ago
parent
commit
e6c3b92d4d
  1. 86
      CHANGELOG.md
  2. 20
      apis/tall.js
  3. BIN
      common/img/weixinIcon.png
  4. 48
      common/js/dc-clipboard/clipboard.js
  5. 20
      common/styles/tailwind.scss
  6. 98
      components/Plugin/Plugin.vue
  7. 1
      components/Roles/Roles.vue
  8. 8
      components/TimeLine/TimeLine.vue
  9. 23
      components/TimeLine/component/TimeBox.vue
  10. 9
      components/TimeLine/component/TimeStatus.vue
  11. 2
      config/task.js
  12. 159
      hooks/project/useGetTasks.js
  13. 449
      hooks/project/useInit - 副本.js
  14. 16
      hooks/project/useInit.js
  15. 285
      hooks/user/userMixin - 副本.js
  16. 123
      hooks/user/userMixin.js
  17. 3
      main.js
  18. 66
      pages.json
  19. 434
      pages/project/project A.vue
  20. 18
      pages/project/project.vue
  21. 96
      pages/user/accountLogin.vue
  22. 266
      pages/user/mixin.js
  23. 2
      store/user/mutations.js
  24. 366
      utils/cacheAndRequest.js
  25. 7
      utils/task.js
  26. 47
      utils/time.js

86
CHANGELOG.md

@ -1,59 +1,67 @@
# 1.0.0 (2022-01-11)
### 🌟 新功能
范围|描述|commitId
--|--|--
- | app.vue | [970cf9a](https://101.201.226.163:50022/TALL/TALL-MUI-4/commits/970cf9a)
- | first commit | [8dc26de](https://101.201.226.163:50022/TALL/TALL-MUI-4/commits/8dc26de)
project | 日常任务面板添加 | [b3f16ff](https://101.201.226.163:50022/TALL/TALL-MUI-4/commits/b3f16ff)
theme | theme demo | [9175758](https://101.201.226.163:50022/TALL/TALL-MUI-4/commits/9175758)
- | vue3 | [12ed2ad](https://101.201.226.163:50022/TALL/TALL-MUI-4/commits/12ed2ad)
- | 使用uview完成api请求 | [1b3efd8](https://101.201.226.163:50022/TALL/TALL-MUI-4/commits/1b3efd8)
- | 日历页添加 | [1b46a91](https://101.201.226.163:50022/TALL/TALL-MUI-4/commits/1b46a91)
- | 日历页首页 | [561c8e6](https://101.201.226.163:50022/TALL/TALL-MUI-4/commits/561c8e6)
- | 时间轴接口 | [a95d005](https://101.201.226.163:50022/TALL/TALL-MUI-4/commits/a95d005)
- | 时间轴页面 | [e926b75](https://101.201.226.163:50022/TALL/TALL-MUI-4/commits/e926b75)
- | 更新代码 | [392c8cc](https://101.201.226.163:50022/TALL/TALL-MUI-4/commits/392c8cc)
- | 添加 timeline | [72dad2b](https://101.201.226.163:50022/TALL/TALL-MUI-4/commits/72dad2b)
- | 项目列表 | [a52e6d5](https://101.201.226.163:50022/TALL/TALL-MUI-4/commits/a52e6d5)
- | 项目操作面板 | [3beb05e](https://101.201.226.163:50022/TALL/TALL-MUI-4/commits/3beb05e)
| 范围 | 描述 | commitId |
| ------- | ------------------------ | ------------------------------------------------------------------------ |
| - | app.vue | [970cf9a](https://101.201.226.163:50022/TALL/TALL-MUI-4/commits/970cf9a) |
| - | first commit | [8dc26de](https://101.201.226.163:50022/TALL/TALL-MUI-4/commits/8dc26de) |
| project | 日常任务面板添加 | [b3f16ff](https://101.201.226.163:50022/TALL/TALL-MUI-4/commits/b3f16ff) |
| theme | theme demo | [9175758](https://101.201.226.163:50022/TALL/TALL-MUI-4/commits/9175758) |
| - | vue3 | [12ed2ad](https://101.201.226.163:50022/TALL/TALL-MUI-4/commits/12ed2ad) |
| - | 使用 uview 完成 api 请求 | [1b3efd8](https://101.201.226.163:50022/TALL/TALL-MUI-4/commits/1b3efd8) |
| - | 日历页添加 | [1b46a91](https://101.201.226.163:50022/TALL/TALL-MUI-4/commits/1b46a91) |
| - | 表单验证 | [8f3bc1e](https://101.201.226.163:50022/TALL/TALL-MUI-4/commits/8f3bc1e) |
| - | 更新代码 | [392c8cc](https://101.201.226.163:50022/TALL/TALL-MUI-4/commits/392c8cc) |
| - | 日历页首页 | [561c8e6](https://101.201.226.163:50022/TALL/TALL-MUI-4/commits/561c8e6) |
| - | 日历页添加 | [1b46a91](https://101.201.226.163:50022/TALL/TALL-MUI-4/commits/1b46a91) |
| - | 时间轴接口 | [a95d005](https://101.201.226.163:50022/TALL/TALL-MUI-4/commits/a95d005) |
| - | 时间轴页面 | [e926b75](https://101.201.226.163:50022/TALL/TALL-MUI-4/commits/e926b75) |
| - | 使用 uview 完成 api 请求 | [1b3efd8](https://101.201.226.163:50022/TALL/TALL-MUI-4/commits/1b3efd8) |
| - | 添加 timeline | [72dad2b](https://101.201.226.163:50022/TALL/TALL-MUI-4/commits/72dad2b) |
| - | 项目操作面板 | [3beb05e](https://101.201.226.163:50022/TALL/TALL-MUI-4/commits/3beb05e) |
| - | 项目列表 | [a52e6d5](https://101.201.226.163:50022/TALL/TALL-MUI-4/commits/a52e6d5) |
| - | 账户名密码登录 | [ebf456e](https://101.201.226.163:50022/TALL/TALL-MUI-4/commits/ebf456e) |
| - | app.vue | [970cf9a](https://101.201.226.163:50022/TALL/TALL-MUI-4/commits/970cf9a) |
| - | first commit | [8dc26de](https://101.201.226.163:50022/TALL/TALL-MUI-4/commits/8dc26de) |
| project | 日常任务面板添加 | [b3f16ff](https://101.201.226.163:50022/TALL/TALL-MUI-4/commits/b3f16ff) |
| theme | theme demo | [9175758](https://101.201.226.163:50022/TALL/TALL-MUI-4/commits/9175758) |
| - | vue3 | [12ed2ad](https://101.201.226.163:50022/TALL/TALL-MUI-4/commits/12ed2ad) |
### 🎨 代码样式
范围|描述|commitId
--|--|--
- | calender格式及细节调整 | [db9602b](https://101.201.226.163:50022/TALL/TALL-MUI-4/commits/db9602b)
- | 细节调整 | [bdd5f87](https://101.201.226.163:50022/TALL/TALL-MUI-4/commits/bdd5f87)
| 范围 | 描述 | commitId |
| ---- | ----------------------- | ------------------------------------------------------------------------ |
| - | 细节调整 | [bdd5f87](https://101.201.226.163:50022/TALL/TALL-MUI-4/commits/bdd5f87) |
| - | calender 格式及细节调整 | [db9602b](https://101.201.226.163:50022/TALL/TALL-MUI-4/commits/db9602b) |
### 🐛 Bug 修复
范围|描述|commitId
--|--|--
app.vue | 修复获取token报错的问题 | [9120d54](https://101.201.226.163:50022/TALL/TALL-MUI-4/commits/9120d54)
createTask | 修复createTask v-model的问题 | [b20d3f0](https://101.201.226.163:50022/TALL/TALL-MUI-4/commits/b20d3f0)
- | 修复一些内容 | [3cdb1ce](https://101.201.226.163:50022/TALL/TALL-MUI-4/commits/3cdb1ce)
| 范围 | 描述 | commitId |
| ---------- | ------------------------------ | ------------------------------------------------------------------------ |
| - | 修复一些内容 | [3cdb1ce](https://101.201.226.163:50022/TALL/TALL-MUI-4/commits/3cdb1ce) |
| app.vue | 修复获取 token 报错的问题 | [9120d54](https://101.201.226.163:50022/TALL/TALL-MUI-4/commits/9120d54) |
| createTask | 修复 createTask v-model 的问题 | [b20d3f0](https://101.201.226.163:50022/TALL/TALL-MUI-4/commits/b20d3f0) |
### 🔨 代码重构
范围|描述|commitId
--|--|--
- | project init 重构 | [2457a87](https://101.201.226.163:50022/TALL/TALL-MUI-4/commits/2457a87)
- | 重构project init 部分 | [c7bf2df](https://101.201.226.163:50022/TALL/TALL-MUI-4/commits/c7bf2df)
| 范围 | 描述 | commitId |
| ---- | ---------------------- | ------------------------------------------------------------------------ |
| - | 重构 project init 部分 | [c7bf2df](https://101.201.226.163:50022/TALL/TALL-MUI-4/commits/c7bf2df) |
| - | project init 重构 | [2457a87](https://101.201.226.163:50022/TALL/TALL-MUI-4/commits/2457a87) |
### 🚀 性能优化
范围|描述|commitId
--|--|--
- | 更新代码 | [0dd443b](https://101.201.226.163:50022/TALL/TALL-MUI-4/commits/0dd443b)
| 范围 | 描述 | commitId |
| ---- | -------- | ------------------------------------------------------------------------ |
| - | 更新代码 | [0dd443b](https://101.201.226.163:50022/TALL/TALL-MUI-4/commits/0dd443b) |
### chore
范围|描述|commitId
--|--|--
- | editorconfig update | [0c08089](https://101.201.226.163:50022/TALL/TALL-MUI-4/commits/0c08089)
| 范围 | 描述 | commitId |
| ---- | ------------------- | ------------------------------------------------------------------------ |
| - | editorconfig update | [0c08089](https://101.201.226.163:50022/TALL/TALL-MUI-4/commits/0c08089) |
范围|描述|commitId
--|--|--
- | Initial commit | [52b8f49](https://101.201.226.163:50022/TALL/TALL-MUI-4/commits/52b8f49)
| 范围 | 描述 | commitId |
| ---- | -------------- | ------------------------------------------------------------------------ |
| - | Initial commit | [52b8f49](https://101.201.226.163:50022/TALL/TALL-MUI-4/commits/52b8f49) |

20
apis/tall.js

@ -1,25 +1,13 @@
import Config from '@/common/js/config.js'
const apiUrl = Config.apiUrl;
const tall = `${apiUrl}/tall3/v3.0`;
// 登录
export const login = {
async index(params) {
try {
const data = await uni.$u.http.post(`${tall}/users/signin`, params);
return data;
} catch (error) {
throw new Error(error);
}
},
};
const tall = `${apiUrl}/tall3/v3.0`;
export function setupTall(app) {
uni.$u.api = { ...uni.$u.api } || {};
// 登录
uni.$u.api.signin = params => login.index(params);
// uni.$u.api.signin = params => login.index(params);
uni.$u.api.signin = params => uni.$u.http.post(`${tall}/users/signin`, params); // 登录
// 获取图片验证码
uni.$u.api.getImageCode = () => uni.$u.get(`${tall}/users/code`);
// 获取短信验证码
@ -42,4 +30,4 @@ export function setupTall(app) {
uni.$u.api.setProjectRelation = params => uni.$u.post(`${tall}/project/setProjectRelation`, params);
// 删除某个项目
uni.$u.api.delProject = projectId => uni.$u.post(`${tall}/project/deleteProject`, { projectId });
}
}

BIN
common/img/weixinIcon.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

48
common/js/dc-clipboard/clipboard.js

@ -0,0 +1,48 @@
/**
* 设置粘贴板数据
* @param {String} text 要设置的字符串
* 如果未设置参数则清空数据
*/
function setClipboardText(text) {
try {
var os = plus.os.name;
text = text || '';
if ('iOS' == os) {
// var UIPasteboard = plus.ios.importClass('UIPasteboard');
// var pasteboard = UIPasteboard.generalPasteboard();
// pasteboard.setValueforPasteboardType(text, 'public.utf8-plain-text');
var pasteboard = plus.ios.invoke('UIPasteboard', 'generalPasteboard');
plus.ios.invoke(pasteboard, 'setValue:forPasteboardType:', text, 'public.utf8-plain-text');
} else {
var main = plus.android.runtimeMainActivity();
// var Context = plus.android.importClass('android.content.Context');
// var clip = main.getSystemService(Context.CLIPBOARD_SERVICE);
var clip = main.getSystemService('clipboard');
plus.android.invoke(clip, 'setText', text);
}
} catch (e) {
console.error('error @setClipboardText!!');
}
}
function getClipboardText() {
try {
var os = plus.os.name;
if ('iOS' == os) {
var pasteboard = plus.ios.invoke('UIPasteboard', 'generalPasteboard');
return plus.ios.invoke(pasteboard, 'valueForPasteboardType:', 'public.utf8-plain-text')
} else {
var main = plus.android.runtimeMainActivity();
var clip = main.getSystemService('clipboard');
return plus.android.invoke(clip, 'getText');
}
} catch (e) {
console.error('error @getClipboardText!!');
}
}
export default {
setClipboardText,
getClipboardText
}

20
common/styles/tailwind.scss

@ -1,5 +1,5 @@
.container {
width: 100%;
width: 100%;
}
.fixed {
@ -2207,6 +2207,15 @@
box-sizing: content-box;
}
.border-l-2 {
border-left-width: 2px;
}
.border-gray-300 {
--tw-border-opacity: 1;
border-color: rgba(209, 213, 219, var(--tw-border-opacity));
}
.block {
display: block;
}
@ -4310,6 +4319,11 @@
.opacity-100 {
opacity: 1;
}
.h-3 {
height: 0.75rem;
}
.h-full {
height: 100%;
}
@ -4317,6 +4331,10 @@
--tw-bg-opacity: 1;
background-color: rgba(249, 250, 251, var(--tw-bg-opacity));
}
.bg-gray-100 {
--tw-bg-opacity: 1;
background-color: rgba(243, 244, 246, var(--tw-bg-opacity));
}
.w-12 {
width: 3rem;
}

98
components/Plugin/Plugin.vue

@ -1,6 +1,7 @@
<template>
<view class="u-font-14" style="height: 100%">
<view v-if="data.pluginContent" @click="setStorage">
插件面板
<!-- <view v-if="data.pluginContent" @click="setStorage">
<view
:data-did="task.detailId"
:data-param="param"
@ -17,9 +18,9 @@
style="height: 100%"
v-html="data.pluginContent"
></view>
</view>
</view> -->
<view v-else @click="setStorage">
<!-- <view v-else @click="setStorage"> -->
<!-- <plugin-default /> -->
<!-- <component :task="task" :is="pluginComponent"></component> -->
<!-- <p-task-title :task="task" v-if="pluginId === '1'" />
@ -37,7 +38,7 @@
<p-manage-task :task="task" v-if="pluginId === '12'" />
<p-wbs-import :task="task" v-if="pluginId === '13' || pluginId === '14'" />
<p-deliver-check :task="task" v-if="pluginId === '15'" /> -->
</view>
<!-- </view> -->
</view>
</template>
@ -53,14 +54,14 @@ const props = defineProps({
param: { type: String, default: '' },
});
const data = reactive({ pluginContent: null });
// const data = reactive({ pluginContent: null });
const store = useStore();
const roleId = computed(() => store.state.role.roleId);
const token = computed(() => store.state.user.token);
const userId = computed(() => store.getters['user/userId']);
// const token = computed(() => store.state.user.token);
// const userId = computed(() => store.getters['user/userId']);
const projectId = computed(() => store.getters['project/projectId']);
const isMine = computed(() => store.getters['role/isMine']);
// const isMine = computed(() => store.getters['role/isMine']);
//
// pluginComponent() {
@ -70,48 +71,48 @@ const isMine = computed(() => store.getters['role/isMine']);
// },
// script dom
function handleDom(js) {
const domList = Array.from(document.getElementsByTagName('script'));
const index = domList.findIndex(item => item.id === `p${props.pluginTaskId}`);
if (index >= 0) {
document.body.removeChild(document.getElementById(`p${props.pluginTaskId}`));
}
const scriptDom = document.createElement('script');
scriptDom.id = `p${props.pluginTaskId}`;
scriptDom.setAttribute('data-type', 'plugin');
scriptDom.innerHTML = js;
nextTick(() => {
document.body.append(scriptDom);
});
}
// function handleDom(js) {
// const domList = Array.from(document.getElementsByTagName('script'));
// const index = domList.findIndex(item => item.id === `p${props.pluginTaskId}`);
// if (index >= 0) {
// document.body.removeChild(document.getElementById(`p${props.pluginTaskId}`));
// }
// const scriptDom = document.createElement('script');
// scriptDom.id = `p${props.pluginTaskId}`;
// scriptDom.setAttribute('data-type', 'plugin');
// scriptDom.innerHTML = js;
// nextTick(() => {
// document.body.append(scriptDom);
// });
// }
//
async function getPlugin() {
const params = { pluginId: props.pluginId, styleType: props.styleType };
uni.$catchReq.getOtherPlugin(params, (err, data) => {
if (err) {
console.error('err: ', err);
} else {
if (!data || !data.id) return;
const reg = /data-root=["|']?(\w+)["|']?/gi;
let uuid = '';
// FIXME: js, html
if (data.html) {
// data-root=xxx xxx pluginTaskId
// async function getPlugin() {
// const params = { pluginId: props.pluginId, styleType: props.styleType };
// uni.$catchReq.getOtherPlugin(params, (err, data) => {
// if (err) {
// console.error('err: ', err);
// } else {
// if (!data || !data.id) return;
// const reg = /data-root=["|']?(\w+)["|']?/gi;
// let uuid = '';
// // FIXME: js, html
// if (data.html) {
// // data-root=xxx xxx pluginTaskId
if (reg.test(data.html)) {
uuid = RegExp.$1;
const str = data.html.replace(new RegExp(uuid, 'g'), `p${props.pluginTaskId}`);
data.pluginContent = str;
} else {
data.pluginContent = data.html;
}
// if (reg.test(data.html)) {
// uuid = RegExp.$1;
// const str = data.html.replace(new RegExp(uuid, 'g'), `p${props.pluginTaskId}`);
// data.pluginContent = str;
// } else {
// data.pluginContent = data.html;
// }
const str = data.js.replace(new RegExp(uuid, 'g'), `p${props.pluginTaskId}`);
handleDom(str);
}
}
});
// const str = data.js.replace(new RegExp(uuid, 'g'), `p${props.pluginTaskId}`);
// handleDom(str);
// }
// }
// });
// if (data.js) {
// if (reg.test(data.js)) {
@ -122,13 +123,14 @@ async function getPlugin() {
// this.handleDom(data.js);
// }
// }
}
// }
console.log('props.pluginId: ', props.pluginId);
if (props.pluginId === '5') {
// id
store.dispatch('role/getAllMembers', { projectId: projectId.value });
}
getPlugin();
// getPlugin();
// storage
async function setStorage() {

1
components/Roles/Roles.vue

@ -66,7 +66,6 @@ onMounted(() => {
query
.selectAll('.tab-children')
.boundingClientRect(res => {
console.log('data', res);
if (res.length) {
data.roleLeft = res[0].left;
}

8
components/TimeLine/TimeLine.vue

@ -15,14 +15,14 @@
<!-- 时间轴 -->
<!-- <u-divider bg-color="#f3f4f6" class="pt-5" fontSize="14px" v-if="topEnd">到顶啦</u-divider> -->
<!-- <TimeBox /> -->
<TimeBox />
<!-- <u-divider bg-color="#f3f4f6" class="pb-5" fontSize="14px" v-if="bottomEnd">到底啦</u-divider> -->
</scroll-view>
</template>
<script setup>
import { reactive, computed } from 'vue';
import { reactive, computed, defineExpose, defineEmits } from 'vue';
import { useStore } from 'vuex';
// import Barrier from './component/Barrier.vue';
import dayjs from 'dayjs';
@ -119,4 +119,8 @@ function setScrollPosition() {
}
}
}
defineExpose({
setScrollPosition
})
</script>

23
components/TimeLine/component/TimeBox.vue

@ -1,25 +1,25 @@
<template>
<view class="column" v-finger:pinch="pinchHandler">
<!-- v-if="tasks && tasks.length" -->
<view>
<!-- v-finger:pinch="pinchHandler" -->
<view class="column" >
<view v-if="tasks && tasks.length">
<view :key="task.id" v-for="task in tasks" :id="`a${task.id}`">
<view class="flex">
<!-- <TimeStatus :task="task" /> -->
<TimeStatus :task="task" />
<view class="flex items-center justify-between flex-1 ml-2 task-column">
<view v-if="task.process !== 4">{{ $moment(+task.planStart).format(startTimeFormat) }}</view>
<view v-else>{{ $moment(+task.planStart).format('D日') }}</view>
<!-- 任务功能菜单 -->
<!-- <TaskTools v-if="task.process !== 4" :task="task" /> -->
<TaskTools v-if="task.process !== 4" :task="task" />
</view>
</view>
<view class="border-l-2 border-gray-300 plugin">
<view class="plugin">
<view class="h-3" v-if="task.process === 4"></view>
<view class="ml-3 overflow-hidden shadow-lg task-box">
<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> -->
<skeleton :banner="false" :loading="true" :row="4" animate class="mt-2 u-line-2 skeleton"></skeleton>
</view>
</view>
</u-card>
@ -37,8 +37,8 @@
<view slot="body">
<view class="p-0 u-col-between">
<view :key="pIndex" v-for="(row, pIndex) in task.plugins">
<view class="grid gap-2 grid-cols-1" v-if="row.length">
<!-- <Plugin
<!-- <view class="grid gap-2 grid-cols-1" v-if="row.length">
<Plugin
:class="[`row-span-${plugin.row}`, `col-span-${plugin.col}`]"
:task="task"
:key="plugin.pluginTaskId"
@ -47,8 +47,8 @@
:param="plugin.param"
:style-type="data.styleType || 0"
v-for="plugin in row"
/> -->
</view>
/>
</view> -->
</view>
</view>
</view>
@ -120,6 +120,7 @@ function pinchHandler(evt) {
margin-top: 8px;
margin-bottom: 8px;
margin-left: 15px;
border-left: 2px solid #d1d5db;
}
::v-deep .ml-2 {
margin-left: 16px;

9
components/TimeLine/component/TimeStatus.vue

@ -59,6 +59,7 @@
<script setup>
import { reactive, onMounted, computed } from 'vue';
import { useStore } from 'vuex';
import dayjs from 'dayjs';
import CreateTask from '../../Title/components/CreateTask.vue';
const props = defineProps({ task: { type: Object, default: () => {} } });
@ -89,9 +90,9 @@ const data = reactive({
const store = useStore();
const tip = computed(() => store.state.task.tip);
const status = computed(() => [props.task ? props.task.process : 0]);
const taskName = computed(() => [props.task ? props.task.name : '']);
const taskId = computed(() => [props.task ? props.task.id : '']);
const status = computed(() => props.task ? props.task.process : 0);
const taskName = computed(() => props.task ? props.task.name : '');
const taskId = computed(() => props.task ? props.task.id : '');
//
function computeCyclePersent() {
@ -222,7 +223,7 @@ function confirmEndTime(e) {
// = realStart + planDuration - Date.now()
function computeDurationText() {
const { realStart, planDuration } = props.task;
const leftTime = +realStart + +planDuration - Date.now(); //
const leftTime = (realStart-0 || 0) + (planDuration-0 || 0) - Date.now(); //
const { num, time } = uni.$time.computeDurationText(leftTime);
if (num <= 0) {
clearInterval(data.timer);

2
config/task.js

@ -0,0 +1,2 @@
// 每页加载颗粒度的个数
export default { pageCount: 10 };

159
hooks/project/useGetTasks.js

@ -1,11 +1,10 @@
import { ref, computed, nextTick } from 'vue';
import { computed, nextTick } from 'vue';
import { useStore } from 'vuex';
import { flatten } from 'lodash';
import dayjs from 'dayjs';
export default function useGetTasks() {
const timeLine = ref(null);
const store = useStore();
const tasks = computed(() => store.state.task.tasks);
const showScrollTo = computed(() => store.state.task.showScrollTo);
const roleId = computed(() => store.state.role.roleId);
const timeNode = computed(() => store.state.task.timeNode);
const timeUnit = computed(() => store.state.task.timeUnit);
@ -17,67 +16,58 @@ export default function useGetTasks() {
setPrevPlaceholderTasks(); // 向上加载空数据
setNextPlaceholderTasks(); // 向下加载空数据
await getInitTasks(); // 获取初始数据
// 滚动到对应位置
// let timer = null;
// timer = setInterval(() => {
// if (showScrollTo.value) {
// clearInterval(timer);
// console.log('timeLine',timeLine)
// nextTick(() => timeLine.setScrollPosition());
// }
// }, 500);
}
// 切换了 颗粒度 || 角色时候 获取初始定期任务
function getInitTasks() {
// 预加载 上下的定期任务
// function preloadFn(that) {
// const detailId = tasks.value.findIndex(task => task.detailId);
// const arr = [];
// tasks.value.forEach(task => {
// if (task.detailId) {
// arr.push(task);
// }
// });
// if (detailId !== -1) {
// // 只要有1个真实的任务 就预加载上下周期的任务
// const {
// pageCount
// } = uni.$task;
// nextTick(() => {
// // 向上拿数据
// getTasks({
// timeNode: +tasks.value[detailId].planStart,
// queryType: 0,
// queryNum: pageCount
// });
// // 向下拿数据
// const nextQueryTime = +uni.$time.add(+arr[arr.length - 1].planStart, 1, timeGranularity.value);
// getTasks({
// timeNode: nextQueryTime,
// queryType: 1,
// queryNum: pageCount
// });
// });
// } else {
// // 没有任务 上下显示时间刻度
// // 向上加载
// setPrevPlaceholderTasks();
// // // 向下加载
// setNextPlaceholderTasks();
// }
// }
function preloadFn(that) {
const detailId = tasks.value.findIndex(task => task.detailId);
const arr = [];
tasks.value.forEach(task => {
if (task.detailId) {
arr.push(task);
}
});
if (detailId !== -1) {
// 只要有1个真实的任务 就预加载上下周期的任务
const { pageCount } = uni.$task;
nextTick(() => {
// 向上拿数据
getTasks({
timeNode: +tasks.value[detailId].planStart,
queryType: 0,
queryNum: pageCount,
});
// 向下拿数据
const nextQueryTime = +uni.$time.add(+arr[arr.length - 1].planStart, 1, timeGranularity.value);
getTasks({
timeNode: nextQueryTime,
queryType: 1,
queryNum: pageCount,
});
});
} else {
// 没有任务 上下显示时间刻度
// 向上加载
setPrevPlaceholderTasks();
// // 向下加载
setNextPlaceholderTasks();
}
}
// 根据时间基准点和角色查找定期任务
getTasks({
queryType: 0
queryType: 0,
}); // 向上获取定期任务数据
// 根据项目id获取角色列表
// 向下获取定期任务数据
// getTasks({
// queryType: 1
// }, preloadFn);
getTasks(
{
queryType: 1,
},
preloadFn,
);
}
/**
@ -105,8 +95,7 @@ export default function useGetTasks() {
* @param {number} query.queryType 0向上查找 1向下查找(默认) 下查包含自己上查不包含
*/
function getTasks(query, fn) {
// store.commit('task/setShowSkeleton', false);
console.log('根据时间基准点和角色查找定期任务')
store.commit('task/setShowSkeleton', false);
const params = generateGetTaskParam(query);
uni.$catchReq.getRegularTask(params, (err, data) => {
store.commit('task/setShowSkeleton', false);
@ -118,17 +107,58 @@ export default function useGetTasks() {
// 有数据用数据替换刻度
// 没有数据 继续加载刻度
if (data && data.length) {
// replacePrevData(data, params.queryType);
replacePrevData(data, params.queryType);
params.queryType === 0 ? store.commit('task/setTopEnd', false) : store.commit('task/setBottomEnd', false);
} else {
// TODO: 0 -> 向上 1 -> 向下
// params.queryType === 0 ? setPrevPlaceholderTasks() : setNextPlaceholderTasks();
params.queryType === 0 ? setPrevPlaceholderTasks() : setNextPlaceholderTasks();
}
// if (tasks.value.length && fn) {
// fn(this);
// }
if (tasks.value.length && fn) {
fn(this);
}
}
});
}
/**
* 用拿到的新数据 替换 时间刻度/旧数据
* 先对比 新旧数据的 始末时间 补齐刻度
* 再遍历对比 用任务替换刻度
* @param {array} data 服务端返回的新数据 上边已经处理过空值
* @param {number} type 0 -> 向上 1->向下
*/
function replacePrevData(data, type) {
const obj = { tasks: tasks.value, data, timeGranularity: timeGranularity.value };
let oldTasks = fillPlaceholderTask(obj); // 已经上下补齐时间刻度的
// 遍历对比 用任务替换刻度
// TODO: tasks越来越多 遍历越来越多 需要优化
oldTasks.forEach((taskItem, index) => {
const arr = data.filter(dataItem => dayjs(+dataItem.planStart).isSame(+taskItem.planStart, timeGranularity.value));
if (arr && arr.length) {
oldTasks.splice(index, 1, [...arr]); // 这里加入的数据是array类型的, [{},{},[],[],{}]
}
});
oldTasks = flatten(oldTasks); // 1维拍平
store.commit('task/clearTasks');
type === 0 ? store.commit('task/setUpTasks', oldTasks) : store.commit('task/setDownTasks', oldTasks);
}
/**
* 超出旧数据上下限 补齐时间刻度到新数据的起始时间颗粒度
*/
function fillPlaceholderTask(obj) {
const { prev, next } = uni.$task.computeFillPlaceholderTaskCount(obj);
if (prev) {
const newTasks = uni.$task.setPlaceholderTasks(+obj.tasks[0].planStart, true, obj.timeGranularity, prev);
store.commit('task/setUpTasks', newTasks);
}
if (next) {
const newTasks = uni.$task.setPlaceholderTasks(+obj.tasks[tasks.length - 1].planStart, false, obj.timeGranularity, next);
store.commit('task/setDownTasks', newTasks);
}
return tasks.value;
}
// 设置时间轴向上的空数据
@ -138,7 +168,7 @@ export default function useGetTasks() {
if (!tasks.value || !tasks.value.length) {
startTime = Date.now(); // 没有任务就应该是时间基准点
} else {
startTime = tasks[0].planStart - 0; // 有任务就是第一个任务的计划开始时间
startTime = tasks.value[0].planStart - 0; // 有任务就是第一个任务的计划开始时间
}
const placeholderTasks = uni.$task.setPlaceholderTasks(startTime, true, timeGranularity.value);
store.commit('task/setUpTasks', placeholderTasks);
@ -158,6 +188,7 @@ export default function useGetTasks() {
}
return {
initPlanTasks
}
initPlanTasks,
getTasks,
};
}

449
hooks/project/useInit - 副本.js

@ -1,449 +0,0 @@
import {
ref,
onMounted,
computed,
watch,
nextTick
} from 'vue';
import {
onLoad
} from '@dcloudio/uni-app';
import useGetUserIdFromLocal from '@/hooks/user/useGetUserIdFromLocal';
import {
useStore
} from 'vuex';
import {
flatten
} from 'lodash';
export default function useInit() {
const store = useStore();
const token = computed(() => store.state.user.token);
const userId = useGetUserIdFromLocal();
const roleId = computed(() => store.state.role.roleId);
const timeNode = computed(() => store.state.task.timeNode);
const timeUnit = computed(() => store.state.task.timeUnit);
const tasks = computed(() => store.state.task.tasks);
const newProjectInfo = computed(() => store.state.task.newProjectInfo);
const showScrollTo = computed(() => store.state.task.showScrollTo);
const timeGranularity = computed(() => store.getters['task/timeGranularity']);
const projectId = computed(() => store.getters['project/projectId']);
const height = ref(null);
const timeLine = ref(null);
onLoad(options => {
console.log('onLoad options: ', options);
if (options.share && options.share === '1') {
shareInit(options);
} else {
init(options);
}
});
/**
* 当时间基准点发生变化时
* 重新根据时间和角色查询普通日常任务
* 永久日常任务不发生 改变
*/
watch(timeNode, newValue => {
if (newValue && roleId.value) {
console.log('当时间基准点发生变化时')
clearTasksData();
getGlobalData(); // 查可变日常任务
initPlanTasks(); // 处理定期任务
}
});
/**
* 当角色发生变化时
* 重新查询永久日常任务和普通日常任务
* 注意: 切换角色后 重新设置了时间基准点 时间基准点一定会变
* 所以监听时间基准点获取 可变日常任务即可 这里不用获取 避免重复获取
*/
watch(roleId, newValue => {
if (newValue) {
console.log('当角色发生变化时')
store.commit('task/setTimeNode', Date.now());
// 根据角色查找永久的日常任务
const params = {
roleId: newValue.value,
projectId: projectId.value
};
store.dispatch('task/getPermanent', params);
}
});
/**
* 当时间基准点发生变化时
* 重新根据时间和角色查询普通日常任务
* 永久日常任务不发生改变
*/
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;
init(options);
}
});
onMounted(() => {
const system = uni.getSystemInfoSync();
height.value = `${system.windowHeight}px`;
});
/**
* 初始化
* @param {object | null} options
*/
function init(options) {
console.log('初始化init')
if (!token.value) {
// 不论有没有token都直接从userId获取token
// token有过期时间 从本地获取可能是过期 干脆直接从userId获取
if (!options || !options.u) {
uni.$ui.showToast('缺少用户信息参数'); // 参数里没有u (userId)提示
} else {
store.dispatch('user/getToken', options.u);
}
}
// 参数里有项目名称 就设置标题里的项目名称
options && options.pname && store.commit('project/setProjectName', options.pname);
if (!options || !options.p) {
uni.$ui.showToast('缺少项目信息参数'); // 没有项目id参数
} else {
if (options.p !== uni.$storage.getStorageSync('projectId')) {
console.log('切项目了');
uni.$storage.setStorageSync('roleId', '');
}
// 根据项目id获取项目信息
const params = {
projectId: options.p,
num: 0
}
getProjectById(params);
// 查询医院是否填写了调查问卷
// this.handleQueryNotWrite(options.p);
// 根据项目id获取成员列表
store.dispatch('role/getAllMembers', {
projectId: options.p
});
}
}
// 初始化 定期任务
async function initPlanTasks() {
setPrevPlaceholderTasks(); // 向上加载空数据
setNextPlaceholderTasks(); // 向下加载空数据
await getInitTasks(); // 获取初始数据
// 滚动到对应位置
let timer = null;
timer = setInterval(() => {
if (showScrollTo.value) {
clearInterval(timer);
// nextTick(() => timeLine.setScrollPosition());
}
}, 500);
}
// 切换了 颗粒度 || 角色时候 获取初始定期任务
function getInitTasks() {
// 预加载 上下的定期任务
function preloadFn(that) {
const detailId = tasks.value.findIndex(task => task.detailId);
const arr = [];
tasks.value.forEach(task => {
if (task.detailId) {
arr.push(task);
}
});
if (detailId !== -1) {
// 只要有1个真实的任务 就预加载上下周期的任务
const {
pageCount
} = uni.$task;
nextTick(() => {
// 向上拿数据
getTasks({
timeNode: +tasks.value[detailId].planStart,
queryType: 0,
queryNum: pageCount
});
// 向下拿数据
const nextQueryTime = +uni.$time.add(+arr[arr.length - 1].planStart, 1, timeGranularity.value);
getTasks({
timeNode: nextQueryTime,
queryType: 1,
queryNum: pageCount
});
});
} else {
// 没有任务 上下显示时间刻度
// 向上加载
setPrevPlaceholderTasks();
// // 向下加载
setNextPlaceholderTasks();
}
}
// 根据时间基准点和角色查找定期任务
getTasks({
queryType: 0
}); // 向上获取定期任务数据
// 根据项目id获取角色列表
getTasks({
queryType: 1
}, preloadFn); // 向下获取定期任务数据
}
/**
* 根据时间基准点和角色查找定期任务
* @param {object} query
* @param {string} query.roleId 角色id
* @param {string} query.timeNode 时间基准点 默认当前
* @param {string} query.timeUnit 时间颗粒度 默认天
* @param {string} query.queryNum 查找颗粒度数量 默认3个
* @param {number} query.queryType 0向上查找 1向下查找(默认) 下查包含自己上查不包含
*/
function getTasks(query, fn) {
store.commit('task/setShowSkeleton', false);
const params = generateGetTaskParam(query);
uni.$catchReq.getRegularTask(params, (err, data) => {
store.commit('task/setShowSkeleton', false);
if (err) {
// TODO: 提示错误
console.error('err: ', err);
} else {
store.commit('task/setShowScrollTo', true);
// 有数据用数据替换刻度
// 没有数据 继续加载刻度
if (data && data.length) {
replacePrevData(data, params.queryType);
params.queryType === 0 ? store.commit('task/setTopEnd', false) : store.commit('task/setBottomEnd', false);
} else {
// TODO: 0 -> 向上 1 -> 向下
params.queryType === 0 ? setPrevPlaceholderTasks() : setNextPlaceholderTasks();
}
if (tasks.value.length && fn) {
fn(this);
}
}
});
}
/**
* 生成getTasks所用的参数
* @param {object} query getTasks传递的参数
*/
function generateGetTaskParam(query) {
return {
roleId: roleId.value,
timeNode: query.timeNode || timeNode.value,
timeUnit: query.timeUnit || timeUnit.value,
queryNum: query.queryNum || 3,
queryType: query.queryType,
projectId: projectId.value,
};
}
// 设置时间轴向上的空数据
function setPrevPlaceholderTasks() {
store.commit('task/setTopEnd', true);
let startTime = '';
if (!tasks.value || !tasks.value.length) {
startTime = Date.now(); // 没有任务就应该是时间基准点
} else {
startTime = tasks[0].planStart - 0; // 有任务就是第一个任务的计划开始时间
}
const placeholderTasks = uni.$task.setPlaceholderTasks(startTime, true, timeGranularity.value);
store.commit('task/setUpTasks', placeholderTasks);
}
// 设置时间轴向下的空数据
function setNextPlaceholderTasks() {
store.commit('task/setBottomEnd', true);
let startTime = '';
if (!tasks.value || !tasks.value.length) {
startTime = Date.now();
} else {
startTime = +tasks.value[tasks.value.length - 1].planStart;
}
const initData = uni.$task.setPlaceholderTasks(startTime, false, timeGranularity.value);
store.commit('task/setDownTasks', initData);
}
/**
* 用拿到的新数据 替换 时间刻度/旧数据
* 先对比 新旧数据的 始末时间 补齐刻度
* 再遍历对比 用任务替换刻度
* @param {array} data 服务端返回的新数据 上边已经处理过空值
* @param {number} type 0 -> 向上 1->向下
*/
function replacePrevData(data, type) {
let oldTasks = fillPlaceholderTask({ tasks.value, data, timeGranularity.value }); // 已经上下补齐时间刻度的
// 遍历对比 用任务替换刻度
// TODO: tasks越来越多 遍历越来越多 需要优化
oldTasks.forEach((taskItem, index) => {
const arr = data.filter(dataItem => dayjs(+dataItem.planStart).isSame(+taskItem.planStart, timeGranularity
.value));
if (arr && arr.length) {
oldTasks.splice(index, 1, [...arr]); // 这里加入的数据是array类型的, [{},{},[],[],{}]
}
});
oldTasks = flatten(oldTasks); // 1维拍平
store.commit('task/clearTasks');
type === 0 ? store.commit('task/setUpTasks', oldTasks) : store.commit('task/setDownTasks', oldTasks);
}
/**
* 超出旧数据上下限 补齐时间刻度到新数据的起始时间颗粒度
*/
function fillPlaceholderTask({
tasks,
data,
timeGranularity
}) {
const {
prev,
next
} = uni.$task.computeFillPlaceholderTaskCount({
tasks,
data,
timeGranularity
});
if (prev) {
const newTasks = uni.$task.setPlaceholderTasks(+tasks[0].planStart, true, timeGranularity, prev);
store.commit('task/setUpTasks', newTasks);
}
if (next) {
const newTasks = uni.$task.setPlaceholderTasks(+tasks[tasks.length - 1].planStart, false, timeGranularity, next);
store.commit('task/setDownTasks', newTasks);
}
return tasks.value;
}
// 分享链接来的初始化
async function shareInit(options) {
console.log('分享链接来的初始化init')
const storageUser = uni.$storage.getStorageSync('user');
const user = storageUser ? JSON.parse(storageUser) : null;
if (user && user.id) {
await store.dispatch('user/getToken', user.id);
const res = await clickShare({
code: options.shareId
});
if (res && res.projectId) {
let query = {
...uni.$route.query
};
query = {
u: user.id,
p: res.projectId,
};
uni.$router.push({
path: uni.$route.path,
query
});
console.log('query',query)
init(query);
}
} else {
uni.$ui.showToast('缺少用户信息参数,请登录');
}
}
/**
* 点击分享连接
* @param {any} commit
* @param {object} param 请求参数
*/
async function clickShare(param) {
try {
const data = await uni.$catchReq.clickShare(param);
return data;
} catch (error) {
uni.$ui.showToast(error.msg || '获取失败');
}
}
/**
* 通过项目id获取项目信息
* @param {object} params 提交的参数
*/
async function getProjectById(params) {
try {
const data = await uni.$u.api.findProjectById(params);
store.commit('project/setProject', data);
// 根据项目id获取角色列表
getRoles(params);
} catch (error) {
console.log('error: ', error || '获取项目信息失败');
}
}
/**
* 通过项目id获取角色信息
* @param {string} projectId
* @param {object} params 提交的参数
*/
function getRoles(params) {
uni.$catchReq.findShowRole(params, (err, data) => {
if (err) {
console.error('err: ', err || '获取角色信息失败');
} else {
store.commit('role/setInvisibleRoles', data ? data.invisibleList : []);
store.commit('role/setVisibleRoles', data ? data.visibleList : []);
setInitialRoleId(data ? data.visibleList : []);
}
});
}
// 设置 初始显示角色信息
function setInitialRoleId(visibleList) {
if (!visibleList || !visibleList.length) return;
const index = visibleList.findIndex(item => +item.mine === 1);
const currentRole = index > 0 ? visibleList[index] : visibleList[0];
const storageRoleId = uni.$storage.getStorageSync('roleId');
const currentRoleId = storageRoleId || (currentRole ? currentRole.id : '');
store.commit('role/setRoleId', currentRoleId);
// 清空storage
uni.$storage.setStorageSync('roleId', '');
}
// 获取可变全局任务
function getGlobalData() {
const param = {
roleId: roleId.value,
timeNode: timeNode.value,
timeUnit: timeUnit.value,
projectId: projectId.value
};
store.dispatch('task/getGlobal', param);
}
// 清除已有的任务数据
function clearTasksData() {
// 清空日常任务的数据
store.commit('task/setPermanents', []);
store.commit('task/setDailyTasks', []);
// 清空定期任务数据
store.commit('task/clearTasks');
// 到顶的标志复位
// 到底的标志复位
store.commit('task/clearEndFlag');
}
}

16
hooks/project/useInit.js

@ -81,8 +81,6 @@ export default function useInit() {
// 根据项目id获取项目信息
const params = { projectId: options.p, num: 0 };
getProjectById(params);
// 查询医院是否填写了调查问卷
// this.handleQueryNotWrite(options.p);
// 根据项目id获取成员列表
store.dispatch('role/getAllMembers', { projectId: options.p });
}
@ -106,5 +104,19 @@ export default function useInit() {
}
}
/**
* 点击分享连接
* @param {any} commit
* @param {object} param 请求参数
*/
async function clickShare(param) {
try {
const data = await uni.$catchReq.clickShare(param);
return data;
} catch (error) {
uni.$ui.showToast(error.msg || '获取失败');
}
}
return { init };
}

285
hooks/user/userMixin - 副本.js

@ -0,0 +1,285 @@
import { ref, computed } from 'vue';
import { useStore } from 'vuex';
import clipboard from "@/common/js/dc-clipboard/clipboard.js"
export default function mixinInit {
const store = useStore();
const user = computed(() => store.state.user.user);
const rules = ref({
phone: [
{
required: true,
message: '请输入手机号',
trigger: ['change','blur'],
},
{
validator: (rule, value, callback) => {
// 调用uView自带的js验证规则,详见:https://www.uviewui.com/js/test.html
return this.$u.test.mobile(value);
},
message: '手机号码不正确',
// 触发器可以同时用blur和change,二者之间用英文逗号隔开
trigger: ['change','blur'],
}
],
verificationCodeValue: [
{
required: true,
message: '请输入图形验证码',
trigger: ['change','blur'],
},
{
type: 'number',
message: '图形验证码只能为数字',
trigger: ['change','blur'],
}
],
smsCode: [
{
required: true,
message: '请输入验证码',
trigger: ['change','blur'],
},
{
type: 'number',
message: '验证码只能为数字',
trigger: ['change','blur'],
}
],
account: [
{
required: true,
message: '请输入用户名',
trigger: ['change','blur'],
},
{
min: 2,
max: 20,
message: '用户名长度在2到20个字符',
trigger: ['change','blur'],
},
{
pattern: /^[a-zA-Z0-9._-]{2,20}$/,
message: '请输入2-20位字母、数字、汉字或字符"_ - ."',
trigger: ['change','blur'],
}
],
password: [
{
required: true,
message: '请输入密码',
trigger: ['change','blur'],
},
{
min: 6,
max: 20,
message: '密码长度在6到20个字符',
trigger: ['change','blur'],
},
{
// 正则不能含有两边的引号
pattern: /^[a-zA-Z0-9._-]{6,20}$/,
message: '请输入6-20位字母、数字、汉字或字符"_ - ."',
trigger: ['change','blur'],
}
],
});
const errorType = ref(['message']);
const labelPosition = ref('left');
const border = ref(false);
const smsCode = ref(''); // 短信验证码
const showInterval = ref(false);
const interval = ref(120);
const codeTimer = ref(null);
const showPaste = ref(false);
// 获取图形验证码
async function getImageCode() {
console.log('5555')
uni.$ui.showLoading();
try {
const data = await uni.$u.api.getImageCode();
const { imageBase64, verificationCodeId } = data;
imageBase64 = imageBase64 || '';
verificationCodeId = verificationCodeId || '';
uni.$ui.hideLoading();
} catch (error) {
uni.$ui.hideLoading();
uni.$ui.showToast(error);
}
}
return {
// errorType,
// rules,
// labelPosition,
// border,
getImageCode,
// hasvalue,
// getCode,
// getCodeInterval,
// checkRules,
// setCode,
// getClipboardContents,
// verifyPhone,
// verifyLoginname,
// handleWxLogin
}
}
// const mixin = {
// computed: mapState('user', ['user']),
// onReady() {
// this.$refs.uForm.setRules(this.rules);
// },
// methods: {
// ...mapActions('user', ['sendCode']),
// 获取图形验证码
// async getImageCode() {
// this.$util.showLoading();
// try {
// const data = await uni.$u.api.getImageCode();
// const { imageBase64, verificationCodeId } = data;
// this.imageBase64 = imageBase64 || '';
// this.verificationCodeId = verificationCodeId || '';
// uni.hideLoading();
// } catch (error) {
// uni.hideLoading();
// uni.$ui.showToast(error);
// }
// },
// //有图片验证码的值
// hasvalue() {
// if(this.model.smsCode || this.model.showPaste) return
// if (!this.verifyPhone(this.model.phone)) {
// uni.$ui.showToast('请输入正确的手机号');
// return;
// }
// if (!this.model.verificationCodeValue) {
// uni.$ui.showToast('请输入图形验证码');
// return;
// }
// this.getCode();
// },
// // 获取验证码
// async getCode() {
// try {
// const { phone, verificationCodeValue } = this.model;
// const { verificationCodeId } = this;
// if (!verificationCodeId || !verificationCodeValue) {
// uni.$ui.showToast('缺少图形验证码参数');
// return;
// }
// const params = {
// phone,
// verificationCodeId,
// verificationCodeValue,
// };
// const date = await store.dispatch('user/sendCode', params);
// getCodeInterval();
// showPaste.value = true;
// } catch (err) {
// throw err;
// }
// },
// // 获取验证码倒计时
// getCodeInterval() {
// this.showInterval = true;
// this.codeTimer = setInterval(() => {
// if (this.interval === 0) {
// clearInterval(this.codeTimer);
// this.codeTimer = null;
// this.showInterval = false;
// this.interval = 120;
// return;
// }
// this.interval = this.interval - 1;
// }, 1000);
// },
// // 验证信息
// checkRules() {
// // const { smsCode, phone, user } = this;
// if (!this.verifyPhone(phone.value)) {
// uni.$ui.showToast('请输入正确的手机号');
// return false;
// }
// if (!smsCode.value) {
// uni.$ui.showToast('验证码无效');
// return false;
// }
// if (phone.value === user.value.phone) {
// uni.$ui.showToast('新手机号不能与旧手机号相同');
// return;
// }
// return true;
// },
// // 粘贴
// setCode() {
// // 获取粘贴板内容
// // 小程序平台
// //#ifdef MP-WEIXIN
// var _this = this
// uni.getClipboardData({
// success (res) {
// _this.smsCode = res.data;
// }
// });
// //#endif
// // 非小程序平台
// //#ifndef MP-WEIXIN
// this.getClipboardContents()
// //#endif
// },
// // 非小程序平台粘贴
// async getClipboardContents() {
// try {
// const text = await navigator.clipboard.readText();
// this.smsCode = text;
// } catch (err) {
// console.error('Failed to read clipboard contents: ', err);
// }
// },
// /**
// * 验证手机号格式
// * @param {string} phone 手机号
// */
// verifyPhone(phone) {
// const phoneExg = /^1\d{10}$/;
// return phoneExg.test(phone);
// },
// /**
// * 验证账号/密码 格式
// * @param {string} account 账号
// */
// verifyLoginname(account) {
// const accountExg = /^[a-zA-Z0-9._-]{2,20}$/;
// return accountExg.test(account);
// },
// // 微信登录
// handleWxLogin() {
// const origin = 'https://test.tall.wiki/pt-mui'; // 测试
// const appid = 'wxd1842e073e0e6d91';
// const state = 'wx_web';
// const href = 'https://open.weixin.qq.com/connect/qrconnect';
// // eslint-disable-next-line
// window.location.href =
// `${href}?appid=${appid}&redirect_uri=${origin}&response_type=code&scope=snsapi_login&state=${state}#wechat_redirect`;
// },
// // }
// };

123
hooks/user/userMixin.js

@ -0,0 +1,123 @@
import { ref, computed } from 'vue';
import { useStore } from 'vuex';
import clipboard from "@/common/js/dc-clipboard/clipboard.js"
export default function userMixin() {
const store = useStore();
const user = computed(() => store.state.user.user);
const rules = ref({
phone: [{
required: true,
message: '请输入手机号',
trigger: ['change', 'blur'],
},
{
validator: (rule, value, callback) => {
// 调用uView自带的js验证规则,详见:https://www.uviewui.com/js/test.html
return this.$u.test.mobile(value);
},
message: '手机号码不正确',
// 触发器可以同时用blur和change,二者之间用英文逗号隔开
trigger: ['change', 'blur'],
}
],
verificationCodeValue: [{
required: true,
message: '请输入图形验证码',
trigger: ['change', 'blur'],
},
{
type: 'number',
message: '图形验证码只能为数字',
trigger: ['change', 'blur'],
}
],
smsCode: [{
required: true,
message: '请输入验证码',
trigger: ['change', 'blur'],
},
{
type: 'number',
message: '验证码只能为数字',
trigger: ['change', 'blur'],
}
],
account: [{
required: true,
message: '请输入用户名',
trigger: ['change', 'blur'],
},
{
min: 2,
max: 20,
message: '用户名长度在2到20个字符',
trigger: ['change', 'blur'],
},
{
pattern: /^[a-zA-Z0-9._-]{2,20}$/,
message: '请输入2-20位字母、数字、汉字或字符"_ - ."',
trigger: ['change', 'blur'],
}
],
password: [{
required: true,
message: '请输入密码',
trigger: ['change', 'blur'],
},
{
min: 6,
max: 20,
message: '密码长度在6到20个字符',
trigger: ['change', 'blur'],
},
{
// 正则不能含有两边的引号
pattern: /^[a-zA-Z0-9._-]{6,20}$/,
message: '请输入6-20位字母、数字、汉字或字符"_ - ."',
trigger: ['change', 'blur'],
}
],
});
const errorType = ['message'];
const labelPosition = 'left';
const border = false;
const smsCode = ref(''); // 短信验证码
const showInterval = ref(false);
const interval = ref(120);
const codeTimer = ref(null);
const showPaste = ref(false);
// 获取图形验证码
async function getImageCode() {
console.log('5555')
uni.$ui.showLoading();
try {
const data = await uni.$u.api.getImageCode();
const { imageBase64, verificationCodeId } = data;
imageBase64 = imageBase64 || '';
verificationCodeId = verificationCodeId || '';
uni.$ui.hideLoading();
} catch (error) {
uni.$ui.hideLoading();
uni.$ui.showToast(error);
}
}
return {
rules,
errorType,
labelPosition,
border,
getImageCode,
// hasvalue,
// getCode,
// getCodeInterval,
// checkRules,
// setCode,
// getClipboardContents,
// verifyPhone,
// verifyLoginname,
// handleWxLogin
}
}

3
main.js

@ -14,6 +14,7 @@ import store from './store';
import task from '@/utils/task.js';
import time from '@/utils/time.js';
import timeConfig from '@/config/time';
import taskConfig from '@/config/task';
import uView from './uni_modules/vk-uview-ui'; // 引入 uView UI
import ui from '@/utils/ui.js';
import upload from '@/utils/upload.js';
@ -29,6 +30,7 @@ export function createApp() {
app.config.globalProperties.$upload = upload;
app.config.globalProperties.$task = task;
app.config.globalProperties.$timeConfig = timeConfig;
app.config.globalProperties.$taskConfig = taskConfig;
uni.$cache = cache;
uni.$catchReq = cacheAndRequest;
@ -38,6 +40,7 @@ export function createApp() {
uni.$upload = upload;
uni.$task = task;
uni.$timeConfig = timeConfig;
uni.$taskConfig = taskConfig;
setupDayjs(app);
app.use(uView); // 使用 uView UI

66
pages.json

@ -1,32 +1,38 @@
{
"pages": [
//pageshttps://uniapp.dcloud.io/collocation/pages
{
"path": "pages/index/index",
"style": {
"navigationBarText": "TALL"
}
},
{
"path": "pages/project/project",
"style": {
"navigationStyle": "custom",
"navigationBarTextStyle": "white"
}
}
],
"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"
}
}
"pages": [ //pageshttps://uniapp.dcloud.io/collocation/pages
{
"path": "pages/index/index",
"style": {
"navigationBarText": "TALL"
}
},
{
"path": "pages/project/project",
"style": {
"navigationStyle": "custom",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/user/accountLogin",
"style": {
"navigationStyle": "custom",
"navigationBarTextStyle": "white"
}
}
],
"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"
}
}
}

434
pages/project/project A.vue

@ -1,434 +0,0 @@
<template>
<view :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 />
<!-- 日常任务面板 -->
<Globals />
<!-- 定期任务面板 -->
<!-- <TimeLine @getTasks="getTasks" class="flex-1 overflow-hidden" ref="timeLine" /> -->
</view>
</view>
</template>
<script>
import { defineComponent, ref, onMounted, computed, watch, nextTick } from 'vue';
import { useStore } from 'vuex';
import { flatten } from 'lodash';
export default defineComponent({
setup(options) {
const store = useStore();
const token = computed(() => store.state.user.token);
const userId = computed(() => store.getters['user/userId']);
const roleId = computed(() => store.state.role.roleId);
const timeNode = computed(() => store.state.task.timeNode);
const timeUnit = computed(() => store.state.task.timeUnit);
const tasks = computed(() => store.state.task.tasks);
const newProjectInfo = computed(() => store.state.task.newProjectInfo);
const showScrollTo = computed(() => store.state.task.showScrollTo);
const timeGranularity = computed(() => store.getters['task/timeGranularity']);
const projectId = computed(() => store.getters['project/projectId']);
const height = ref(null);
const timeLine = ref(null);
/**
* 当时间基准点发生变化时
* 重新根据时间和角色查询普通日常任务
* 永久日常任务不发生 改变
*/
watch(timeNode, newValue => {
if (newValue && roleId.value) {
clearTasksData();
getGlobalData(); //
initPlanTasks(); //
}
});
/**
* 当角色发生变化时
* 重新查询永久日常任务和普通日常任务
* 注意: 切换角色后 重新设置了时间基准点 时间基准点一定会变
* 所以监听时间基准点获取 可变日常任务即可 这里不用获取 避免重复获取
*/
watch(roleId, newValue => {
if (newValue) {
store.commit('task/setTimeNode', Date.now());
//
const params = { roleId: newValue, projectId: projectId.value };
store.dispatch('task/getPermanent', params);
}
});
/**
* 当时间基准点发生变化时
* 重新根据时间和角色查询普通日常任务
* 永久日常任务不发生改变
*/
watch(newProjectInfo, newValue => {
if (newValue && newValue.projectId && newValue.url) {
uni.$u.route('/', { u: userId.value, p: newValue.projectId, url: newValue.url });
clearTasksData();
store.commit('role/setRoleId', '');
const options = uni.$route.query;
init(options);
}
});
onMounted(() => {
const system = uni.getSystemInfoSync();
height.value = `${system.windowHeight}px`;
});
//
async function initPlanTasks() {
setPrevPlaceholderTasks(); //
setNextPlaceholderTasks(); //
await getInitTasks(); //
//
let timer = null;
timer = setInterval(() => {
if (showScrollTo.value) {
clearInterval(timer);
// nextTick(() => timeLine.setScrollPosition());
}
}, 500);
}
// ||
function getInitTasks() {
//
function preloadFn(that) {
const detailId = tasks.value.findIndex(task => task.detailId);
const arr = [];
tasks.value.forEach(task => {
if (task.detailId) {
arr.push(task);
}
});
if (detailId !== -1) {
// 1
const { pageCount } = uni.$task;
nextTick(() => {
//
getTasks({ timeNode: +tasks.value[detailId].planStart, queryType: 0, queryNum: pageCount });
//
const nextQueryTime = +uni.$time.add(+arr[arr.length - 1].planStart, 1, timeGranularity.value);
getTasks({ timeNode: nextQueryTime, queryType: 1, queryNum: pageCount });
});
} else {
//
//
setPrevPlaceholderTasks();
// //
setNextPlaceholderTasks();
}
}
//
getTasks({ queryType: 0 }); //
// id
getTasks({ queryType: 1 }, preloadFn); //
}
/**
* 根据时间基准点和角色查找定期任务
* @param {object} query
* @param {string} query.roleId 角色id
* @param {string} query.timeNode 时间基准点 默认当前
* @param {string} query.timeUnit 时间颗粒度 默认天
* @param {string} query.queryNum 查找颗粒度数量 默认3个
* @param {number} query.queryType 0向上查找 1向下查找(默认) 下查包含自己上查不包含
*/
function getTasks(query, fn) {
store.commit('task/setShowSkeleton', false);
const params = generateGetTaskParam(query);
uni.$catchReq.getRegularTask(params, (err, data) => {
store.commit('task/setShowSkeleton', false);
if (err) {
// TODO:
console.error('err: ', err);
} else {
store.commit('task/setShowScrollTo', true);
//
//
if (data && data.length) {
replacePrevData(data, params.queryType);
params.queryType === 0 ? store.commit('task/setTopEnd', false) : store.commit('task/setBottomEnd', false);
} else {
// TODO: 0 -> 1 ->
params.queryType === 0 ? setPrevPlaceholderTasks() : setNextPlaceholderTasks();
}
if (tasks.value.length && fn) {
fn(this);
}
}
});
}
/**
* 生成getTasks所用的参数
* @param {object} query getTasks传递的参数
*/
function generateGetTaskParam(query) {
return {
roleId: roleId.value,
timeNode: query.timeNode || timeNode.value,
timeUnit: query.timeUnit || timeUnit.value,
queryNum: query.queryNum || 3,
queryType: query.queryType,
projectId: projectId.value,
};
}
//
function setPrevPlaceholderTasks() {
store.commit('task/setTopEnd', true);
let startTime = '';
if (!tasks.value || !tasks.value.length) {
startTime = Date.now(); //
} else {
startTime = tasks[0].planStart - 0; //
}
const placeholderTasks = uni.$task.setPlaceholderTasks(startTime, true, timeGranularity.value);
store.commit('task/setUpTasks', placeholderTasks);
}
//
function setNextPlaceholderTasks() {
store.commit('task/setBottomEnd', true);
let startTime = '';
if (!tasks.value || !tasks.value.length) {
startTime = Date.now();
} else {
startTime = +tasks.value[tasks.value.length - 1].planStart;
}
const initData = uni.$task.setPlaceholderTasks(startTime, false, timeGranularity.value);
store.commit('task/setDownTasks', initData);
}
/**
* 用拿到的新数据 替换 时间刻度/旧数据
* 先对比 新旧数据的 始末时间 补齐刻度
* 再遍历对比 用任务替换刻度
* @param {array} data 服务端返回的新数据 上边已经处理过空值
* @param {number} type 0 -> 向上 1->向下
*/
function replacePrevData(data, type) {
let oldTasks = fillPlaceholderTask({ tasks.value, data, timeGranularity.value }); //
//
// TODO: tasks
oldTasks.forEach((taskItem, index) => {
const arr = data.filter(dataItem => dayjs(+dataItem.planStart).isSame(+taskItem.planStart, timeGranularity.value));
if (arr && arr.length) {
oldTasks.splice(index, 1, [...arr]); // array, [{},{},[],[],{}]
}
});
oldTasks = flatten(oldTasks); // 1
store.commit('task/clearTasks');
type === 0 ? store.commit('task/setUpTasks', oldTasks) : store.commit('task/setDownTasks', oldTasks);
}
/**
* 超出旧数据上下限 补齐时间刻度到新数据的起始时间颗粒度
*/
function fillPlaceholderTask({ tasks, data, timeGranularity }) {
const { prev, next } = uni.$task.computeFillPlaceholderTaskCount({ tasks, data, timeGranularity });
if (prev) {
const newTasks = uni.$task.setPlaceholderTasks(+tasks[0].planStart, true, timeGranularity, prev);
store.commit('task/setUpTasks', newTasks);
}
if (next) {
const newTasks = uni.$task.setPlaceholderTasks(+tasks[tasks.length - 1].planStart, false, timeGranularity, next);
store.commit('task/setDownTasks', newTasks);
}
return tasks.value;
}
/**
* 初始化
* @param {object | null} options
*/
function init(options) {
if (!token.value) {
// tokenuserIdtoken
// token userId
if (!options || !options.u) {
uni.$ui.showToast('缺少用户信息参数'); // u (userId)
} else {
store.dispatch('user/getToken', options.u);
}
}
//
options && options.pname && store.commit('project/setProjectName', options.pname);
if (!options || !options.p) {
uni.$ui.showToast('缺少项目信息参数'); // id
} else {
if (options.p !== uni.$storage.getStorageSync('projectId')) {
console.log('切项目了');
uni.$storage.setStorageSync('roleId', '');
}
// TODO
getProjectById({ projectId: options.p, num: 0 }); // id
//
// this.handleQueryNotWrite(options.p);
// id
store.dispatch('role/getAllMembers', { projectId: options.p });
}
}
//
async function shareInit(options) {
const storageUser = uni.$storage.getStorageSync('user');
const user = storageUser ? JSON.parse(storageUser) : null;
if (user && user.id) {
await store.dispatch('user/getToken', user.id);
const res = await clickShare({ code: options.shareId });
if (res && res.projectId) {
let query = { ...uni.$route.query };
query = {
u: user.id,
p: res.projectId,
};
uni.$router.push({ path: uni.$route.path, query });
init(query);
}
} else {
uni.$ui.showToast('缺少用户信息参数,请登录');
}
}
function setOptions(options) {
if (options.share && options.share === '1') {
shareInit(options);
} else {
init(options);
}
}
setOptions(options);
/**
* 点击分享连接
* @param {any} commit
* @param {object} param 请求参数
*/
async function clickShare(param) {
try {
const data = await uni.$catchReq.clickShare(param);
return data;
} catch (error) {
uni.$ui.showToast(error.msg || '获取失败');
}
}
/**
* 通过项目id获取项目信息
* @param {object} params 提交的参数
*/
async function getProjectById(params) {
try {
const data = await uni.$u.api.findProjectById(params);
store.commit('project/setProject', data);
// id
getRoles(params);
} catch (error) {
console.log('error: ', error || '获取项目信息失败');
}
}
/**
* 通过项目id获取角色信息
* @param {string} projectId
* @param {object} params 提交的参数
*/
function getRoles(params) {
uni.$catchReq.findShowRole(params, (err, data) => {
if (err) {
console.error('err: ', err || '获取角色信息失败');
} else {
store.commit('role/setInvisibleRoles', data ? data.invisibleList : []);
store.commit('role/setVisibleRoles', data ? data.visibleList : []);
setInitialRoleId(data ? data.visibleList : []);
}
});
}
//
function setInitialRoleId(visibleList) {
if (!visibleList || !visibleList.length) return;
const index = visibleList.findIndex(item => +item.mine === 1);
const currentRole = index > 0 ? visibleList[index] : visibleList[0];
const storageRoleId = uni.$storage.getStorageSync('roleId');
const currentRoleId = storageRoleId || (currentRole ? currentRole.id : '');
store.commit('role/setRoleId', currentRoleId);
// storage
uni.$storage.setStorageSync('roleId', '');
}
//
function getGlobalData() {
const param = { roleId: roleId.value, timeNode: timeNode.value, timeUnit: timeUnit.value, projectId: projectId.value };
store.dispatch('task/getGlobal', param);
}
//
function clearTasksData() {
//
store.commit('task/setPermanents', []);
store.commit('task/setDailyTasks', []);
//
store.commit('task/clearTasks');
//
//
store.commit('task/clearEndFlag');
}
return {
height,
timeLine,
roleId,
timeNode,
timeUnit,
tasks,
newProjectInfo,
showScrollTo,
timeGranularity,
projectId,
userId,
initPlanTasks,
getInitTasks,
getTasks,
generateGetTaskParam,
setPrevPlaceholderTasks,
setNextPlaceholderTasks,
replacePrevData,
fillPlaceholderTask,
init,
shareInit,
setOptions,
clickShare,
getProjectById,
getRoles,
setInitialRoleId,
getGlobalData,
clearTasksData,
};
},
});
</script>
<style lang="scss" scoped>
.border-b {
border-bottom: 1px solid #e4e7ed;
}
</style>

18
pages/project/project.vue

@ -11,8 +11,7 @@
<Globals />
<!-- 定期任务面板 -->
<!-- <TimeLine @getTasks="getTasks" class="flex-1 overflow-hidden" ref="timeLine" /> -->
<TimeLine class="flex-1 overflow-hidden" ref="timeLine" />
<TimeLine @getTasks="getTasks" class="flex-1 overflow-hidden" ref="timeLine" />
<!-- TODO: DEBUG: -->
<u-button @click="$store.commit('setTheme', 'theme-test')">测试切换主题</u-button>
@ -35,7 +34,9 @@ const timeUnit = computed(() => store.state.task.timeUnit);
const projectId = computed(() => store.getters['project/projectId']);
const userId = computed(() => store.getters['user/userId']);
const newProjectInfo = computed(() => store.state.task.newProjectInfo);
const showScrollTo = computed(() => store.state.task.showScrollTo);
const height = ref(null);
const timeLine = ref(null);
onMounted(() => {
const system = uni.getSystemInfoSync();
@ -77,6 +78,15 @@ watch(timeNode, newValue => {
getGlobalData(); //
// initPlanTasks(); //
getTasksHook.initPlanTasks(); //
//
let timer = null;
timer = setInterval(() => {
if (showScrollTo.value) {
clearInterval(timer);
timeLine.value.setScrollPosition();
}
}, 500);
}
});
@ -119,6 +129,10 @@ watch(newProjectInfo, newValue => {
initHook.init(options);
}
});
function getTasks(params) {
getTasksHook.initPlanTasks(params); //
}
</script>
<style lang="scss" scoped>

96
pages/user/accountLogin.vue

@ -0,0 +1,96 @@
<template>
<view class="u-p-l-50 u-p-r-50 u-p-t-30">
<u-form :model="model" ref="loginForm" :rules="mixinInit.rules" :error-type="mixinInit.errorType">
<u-form-item :label-position="mixinInit.labelPosition" label="用户名" prop="account" label-width="150">
<u-input :border="mixinInit.border" placeholder="请输入用户名" v-model="model.account" type="text"></u-input>
</u-form-item>
<u-form-item :label-position="mixinInit.labelPosition" label="密码" prop="password" label-width="150">
<u-input :password-icon="true" :border="mixinInit.border" type="password" v-model="model.password" placeholder="请输入密码">
</u-input>
</u-form-item>
<view class="flex flex-nowrap">
<view class="flex-sub"></view>
<view class="u-m-t-30 u-font-12 text-gray-400" @click="openPage('/pages/user/forgetPassword')">忘记密码</view>
</view>
</u-form>
<view class="u-m-t-50">
<u-button @click="submit" type="primary">立即登录</u-button>
</view>
<view class="flex justify-between">
<view class="u-m-t-30" style="color: #2885ED;" @click="openPage('/pages/user/rigister')"> 新用户注册</view>
<view class="u-m-t-30" style="color: #2885ED;" @click="openPage('/pages/user/login')">手机号登录 </view>
</view>
<view style="margin-top: 200rpx;text-align: center; color: #999999;font-size: 35rpx;">
快速登录
</view>
<view style="text-align: center; margin-top: 20rpx;">
<image src="/common/img/weixinIcon.png" mode="" style="width: 85rpx;height: 85rpx;"></image>
</view>
</view>
</template>
<script setup>
import { ref, computed } from 'vue';
import { useStore } from 'vuex';
import userMixin from '@/hooks/user/userMixin'
const store = useStore();
const mixinInit = userMixin();
const userInfo = uni.$storage.getStorageSync('user');
const user = ref({});
const loginForm = ref(null);
const model = ref({
account: '',
password: ''
})
if (userInfo) {
user.value = JSON.parse(userInfo);
}
const submit = () => {
loginForm.value.validate(data => {
console.log(data);
});
}
async function login() {
try {
uni.$ui.showLoading();
if (account.value === user.value.account) {
uni.$ui.showToast('当前账户已登录');
} else {
const params = ref({
client: 1,
data: {
identifier: account.value,
credential: password.value,
},
type: 3,
});
let res = await uni.$u.api.signin(params.value);
store.commit('user/setToken', res.token);
store.commit('user/setUser', res);
uni.$storage.setStorageSync('anyringToken', res.token || '');
uni.$storage.setStorageSync('user', JSON.stringify(res));
}
uni.navigateTo({
url: '/pages/index/index'
});
uni.$ui.hideLoading();
} catch (error) {
uni.$ui.hideLoading();
uni.$ui.showToast(error);
}
}
</script>
<style>
</style>

266
pages/user/mixin.js

@ -0,0 +1,266 @@
import { ref, computed } from 'vue';
import { useStore } from 'vuex';
import clipboard from "@/common/js/dc-clipboard/clipboard.js"
export default function mixinInit {
const store = useStore();
const user = computed(() => store.state.user.user);
const rules = ref({
phone: [
{
required: true,
message: '请输入手机号',
trigger: ['change','blur'],
},
{
validator: (rule, value, callback) => {
// 调用uView自带的js验证规则,详见:https://www.uviewui.com/js/test.html
return this.$u.test.mobile(value);
},
message: '手机号码不正确',
// 触发器可以同时用blur和change,二者之间用英文逗号隔开
trigger: ['change','blur'],
}
],
verificationCodeValue: [
{
required: true,
message: '请输入图形验证码',
trigger: ['change','blur'],
},
{
type: 'number',
message: '图形验证码只能为数字',
trigger: ['change','blur'],
}
],
smsCode: [
{
required: true,
message: '请输入验证码',
trigger: ['change','blur'],
},
{
type: 'number',
message: '验证码只能为数字',
trigger: ['change','blur'],
}
],
account: [
{
required: true,
message: '请输入用户名',
trigger: ['change','blur'],
},
{
min: 2,
max: 20,
message: '用户名长度在2到20个字符',
trigger: ['change','blur'],
},
{
pattern: /^[a-zA-Z0-9._-]{2,20}$/,
message: '请输入2-20位字母、数字、汉字或字符"_ - ."',
trigger: ['change','blur'],
}
],
password: [
{
required: true,
message: '请输入密码',
trigger: ['change','blur'],
},
{
min: 6,
max: 20,
message: '密码长度在6到20个字符',
trigger: ['change','blur'],
},
{
// 正则不能含有两边的引号
pattern: /^[a-zA-Z0-9._-]{6,20}$/,
message: '请输入6-20位字母、数字、汉字或字符"_ - ."',
trigger: ['change','blur'],
}
],
});
const errorType = ref(['message']);
const labelPosition = ref('left');
const border = ref(false);
const smsCode = ref(''); // 短信验证码
const showInterval = ref(false);
const interval = ref(120);
const codeTimer = ref(null);
const showPaste = ref(false);
return {
errorType,
// getImageCode,
// hasvalue,
// getCode,
// getCodeInterval,
// checkRules,
// setCode,
// getClipboardContents,
// verifyPhone,
// verifyLoginname,
// handleWxLogin
}
}
// const mixin = {
// computed: mapState('user', ['user']),
// onReady() {
// this.$refs.uForm.setRules(this.rules);
// },
// methods: {
// ...mapActions('user', ['sendCode']),
// 获取图形验证码
// async getImageCode() {
// this.$util.showLoading();
// try {
// const data = await uni.$u.api.getImageCode();
// const { imageBase64, verificationCodeId } = data;
// this.imageBase64 = imageBase64 || '';
// this.verificationCodeId = verificationCodeId || '';
// uni.hideLoading();
// } catch (error) {
// uni.hideLoading();
// uni.$ui.showToast(error);
// }
// },
// //有图片验证码的值
// hasvalue() {
// if(this.model.smsCode || this.model.showPaste) return
// if (!this.verifyPhone(this.model.phone)) {
// uni.$ui.showToast('请输入正确的手机号');
// return;
// }
// if (!this.model.verificationCodeValue) {
// uni.$ui.showToast('请输入图形验证码');
// return;
// }
// this.getCode();
// },
// // 获取验证码
// async getCode() {
// try {
// const { phone, verificationCodeValue } = this.model;
// const { verificationCodeId } = this;
// if (!verificationCodeId || !verificationCodeValue) {
// uni.$ui.showToast('缺少图形验证码参数');
// return;
// }
// const params = {
// phone,
// verificationCodeId,
// verificationCodeValue,
// };
// const date = await store.dispatch('user/sendCode', params);
// getCodeInterval();
// showPaste.value = true;
// } catch (err) {
// throw err;
// }
// },
// // 获取验证码倒计时
// getCodeInterval() {
// this.showInterval = true;
// this.codeTimer = setInterval(() => {
// if (this.interval === 0) {
// clearInterval(this.codeTimer);
// this.codeTimer = null;
// this.showInterval = false;
// this.interval = 120;
// return;
// }
// this.interval = this.interval - 1;
// }, 1000);
// },
// // 验证信息
// checkRules() {
// // const { smsCode, phone, user } = this;
// if (!this.verifyPhone(phone.value)) {
// uni.$ui.showToast('请输入正确的手机号');
// return false;
// }
// if (!smsCode.value) {
// uni.$ui.showToast('验证码无效');
// return false;
// }
// if (phone.value === user.value.phone) {
// uni.$ui.showToast('新手机号不能与旧手机号相同');
// return;
// }
// return true;
// },
// // 粘贴
// setCode() {
// // 获取粘贴板内容
// // 小程序平台
// //#ifdef MP-WEIXIN
// var _this = this
// uni.getClipboardData({
// success (res) {
// _this.smsCode = res.data;
// }
// });
// //#endif
// // 非小程序平台
// //#ifndef MP-WEIXIN
// this.getClipboardContents()
// //#endif
// },
// // 非小程序平台粘贴
// async getClipboardContents() {
// try {
// const text = await navigator.clipboard.readText();
// this.smsCode = text;
// } catch (err) {
// console.error('Failed to read clipboard contents: ', err);
// }
// },
// /**
// * 验证手机号格式
// * @param {string} phone 手机号
// */
// verifyPhone(phone) {
// const phoneExg = /^1\d{10}$/;
// return phoneExg.test(phone);
// },
// /**
// * 验证账号/密码 格式
// * @param {string} account 账号
// */
// verifyLoginname(account) {
// const accountExg = /^[a-zA-Z0-9._-]{2,20}$/;
// return accountExg.test(account);
// },
// // 微信登录
// handleWxLogin() {
// const origin = 'https://test.tall.wiki/pt-mui'; // 测试
// const appid = 'wxd1842e073e0e6d91';
// const state = 'wx_web';
// const href = 'https://open.weixin.qq.com/connect/qrconnect';
// // eslint-disable-next-line
// window.location.href =
// `${href}?appid=${appid}&redirect_uri=${origin}&response_type=code&scope=snsapi_login&state=${state}#wechat_redirect`;
// },
// // }
// };

2
store/user/mutations.js

@ -1,5 +1,3 @@
import storage from '@/utils/storage.js';
const mutations = {
/**
* 设置存储token

366
utils/cacheAndRequest.js

@ -1,178 +1,190 @@
import store from '@/store/index';
/**
* 等待token执行api
* 没有token 就延时执行自己 直到有了token在请求
* @param {function} requestFn 执行请求的函数
*/
export const waitTokenRequest = requestFn => {
if (!requestFn || typeof requestFn !== 'function') throw new Error(`requestFn must be a function`);
if (uni.$storage.getStorageSync('anyringToken')) {
requestFn();
} else {
setTimeout(() => waitTokenRequest(requestFn), 10);
}
};
export default {
/**
* 获取项目列表
* @param {number} startTime 起始时间
* @param {number} endTime 截止时间
*/
getProjects(startTime, endTime, fn) {
let remote = false;
if (store.getters.useStorage) {
// 有缓存 且 服务端数据未返回 就先返回缓存
uni.$cache.getProjectsByDay(startTime, endTime)
.then(data => {
!remote && fn(null, data);
})
.catch(err => !remote && fn(err));
}
waitTokenRequest(() => {
// 拿到api数据后 再用api的数据
uni.$u.api.getProjects(startTime, endTime)
.then(data => {
remote = true;
fn(null, data);
// 存api到cache里
uni.$cache.putProjects(data);
})
.catch(err => fn(err));
});
},
/**
* 通过项目id获取角色信息
* @param {object} params 提交的参数
*/
findShowRole(params, fn) {
let remote = false;
// 有缓存 且 服务端数据未返回 就先返回缓存
uni.$cache.getShowRole(params.projectId)
.then(data => {
!remote && fn(null, data);
})
.catch(err => !remote && fn(err));
waitTokenRequest(() => {
// 拿到api数据后 再用api的数据
uni.$u.api.findShowRole(params)
.then(data => {
remote = true;
fn(null, data);
// 存api到cache里
uni.$cache.putShowRole(params.projectId, data);
})
.catch(err => fn(err));
});
},
/**
* 根据时间基准点和角色查找定期任务
* @param {object} params 提交的参数
*/
getRegularTask(params, fn) {
let remote = false;
// 有缓存 且 服务端数据未返回 就先返回缓存
uni.$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;
fn(null, uni.$u.deepClone(data));
// 存api到cache里
uni.$cache.putStorageRegularTask(params, data);
})
.catch(err => fn(err));
});
},
/**
* 根据角色查找永久的日常任务
* @param {object} params 提交的参数
*/
getPermanent(params, fn) {
let remote = false;
// 有缓存 且 服务端数据未返回 就先返回缓存
uni.$cache.getStoragePermanent(params)
.then(data => {
!remote && fn(null, data);
})
.catch(err => !remote && fn(err));
waitTokenRequest(() => {
// 拿到api数据后 再用api的数据
uni.$u.api.getPermanent(params)
.then(data => {
remote = true;
fn(null, data);
// 存api到cache里
uni.$cache.putStoragePermanent(params, data);
})
.catch(err => fn(err));
});
},
/**
* 根据时间和角色查找日常任务
* @param {object} params 提交的参数
*/
getGlobal(params, fn) {
let remote = false;
// 有缓存 且 服务端数据未返回 就先返回缓存
uni.$cache.getDailyTask(params)
.then(data => {
!remote && fn(null, data);
})
.catch(err => !remote && fn(err));
waitTokenRequest(() => {
// 拿到api数据后 再用api的数据
uni.$u.api.getGlobal(params)
.then(data => {
remote = true;
fn(null, data);
// 存api到cache里
uni.$cache.putDailyTask(params, data);
})
.catch(err => fn(err));
});
},
/**
* 获取插件信息
* @param {object} params 提交的参数
*/
getOtherPlugin(params, fn) {
let remote = false;
// 有缓存 且 服务端数据未返回 就先返回缓存
uni.$cache.getPlugin(params.pluginId)
.then(data => {
!remote && fn(null, data);
})
.catch(err => !remote && fn(err));
waitTokenRequest(() => {
// 拿到api数据后 再用api的数据
uni.$u.api.getOtherPlugin(params)
.then(data => {
remote = true;
fn(null, data);
// 存api到cache里
uni.$cache.putPlugin(params.pluginId, data);
})
.catch(err => fn(err));
});
},
import store from '@/store/index';
/**
* 等待token执行api
* 没有token 就延时执行自己 直到有了token在请求
* @param {function} requestFn 执行请求的函数
*/
export const waitTokenRequest = requestFn => {
if (!requestFn || typeof requestFn !== 'function') throw new Error(`requestFn must be a function`);
if (uni.$storage.getStorageSync('anyringToken')) {
requestFn();
} else {
setTimeout(() => waitTokenRequest(requestFn), 10);
}
};
export default {
/**
* 获取项目列表
* @param {number} startTime 起始时间
* @param {number} endTime 截止时间
*/
getProjects(startTime, endTime, fn) {
let remote = false;
if (store.getters.useStorage) {
// 有缓存 且 服务端数据未返回 就先返回缓存
uni.$cache
.getProjectsByDay(startTime, endTime)
.then(data => {
!remote && fn(null, data);
})
.catch(err => !remote && fn(err));
}
waitTokenRequest(() => {
// 拿到api数据后 再用api的数据
uni.$u.api
.getProjects(startTime, endTime)
.then(data => {
remote = true;
fn(null, data);
// 存api到cache里
uni.$cache.putProjects(data);
})
.catch(err => fn(err));
});
},
/**
* 通过项目id获取角色信息
* @param {object} params 提交的参数
*/
findShowRole(params, fn) {
let remote = false;
// 有缓存 且 服务端数据未返回 就先返回缓存
uni.$cache
.getShowRole(params.projectId)
.then(data => {
!remote && fn(null, data);
})
.catch(err => !remote && fn(err));
waitTokenRequest(() => {
// 拿到api数据后 再用api的数据
uni.$u.api
.findShowRole(params)
.then(data => {
remote = true;
fn(null, data);
// 存api到cache里
uni.$cache.putShowRole(params.projectId, data);
})
.catch(err => fn(err));
});
},
/**
* 根据时间基准点和角色查找定期任务
* @param {object} params 提交的参数
*/
getRegularTask(params, fn) {
let remote = false;
// 有缓存 且 服务端数据未返回 就先返回缓存
uni.$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;
fn(null, uni.$u.deepClone(data));
// 存api到cache里
uni.$cache.putStorageRegularTask(params, data);
})
.catch(err => fn(err));
});
},
/**
* 根据角色查找永久的日常任务
* @param {object} params 提交的参数
*/
getPermanent(params, fn) {
let remote = false;
// 有缓存 且 服务端数据未返回 就先返回缓存
uni.$cache
.getStoragePermanent(params)
.then(data => {
!remote && fn(null, data);
})
.catch(err => !remote && fn(err));
waitTokenRequest(() => {
// 拿到api数据后 再用api的数据
uni.$u.api
.getPermanent(params)
.then(data => {
remote = true;
fn(null, data);
// 存api到cache里
uni.$cache.putStoragePermanent(params, data);
})
.catch(err => fn(err));
});
},
/**
* 根据时间和角色查找日常任务
* @param {object} params 提交的参数
*/
getGlobal(params, fn) {
let remote = false;
// 有缓存 且 服务端数据未返回 就先返回缓存
uni.$cache
.getDailyTask(params)
.then(data => {
!remote && fn(null, data);
})
.catch(err => !remote && fn(err));
waitTokenRequest(() => {
// 拿到api数据后 再用api的数据
uni.$u.api
.getGlobal(params)
.then(data => {
remote = true;
fn(null, data);
// 存api到cache里
uni.$cache.putDailyTask(params, data);
})
.catch(err => fn(err));
});
},
/**
* 获取插件信息
* @param {object} params 提交的参数
*/
getOtherPlugin(params, fn) {
let remote = false;
// 有缓存 且 服务端数据未返回 就先返回缓存
uni.$cache
.getPlugin(params.pluginId)
.then(data => {
!remote && fn(null, data);
})
.catch(err => !remote && fn(err));
waitTokenRequest(() => {
// 拿到api数据后 再用api的数据
uni.$u.api
.getOtherPlugin(params)
.then(data => {
remote = true;
fn(null, data);
// 存api到cache里
uni.$cache.putPlugin(params.pluginId, data);
})
.catch(err => fn(err));
});
},
};

7
utils/task.js

@ -9,7 +9,7 @@ import dayjs from 'dayjs';
*/
const setPlaceholderTasks = (startTime, isUp, timeGranularity, pageCount) => {
let result = [];
pageCount = pageCount || uni.$task.pageCount;
pageCount = pageCount || uni.$taskConfig.pageCount;
for (let i = 0; i < pageCount; i++) {
const delta = isUp ? `-${i + 1}` - 0 : i + 1;
let item = {
@ -33,7 +33,8 @@ const setPlaceholderTasks = (startTime, isUp, timeGranularity, pageCount) => {
* @param {array} option.data 新拿到的任务数据 空值已经过滤过了
* @param {string} option.timeGranularity 颗粒度
*/
const computeFillPlaceholderTaskCount = ({ tasks, data, timeGranularity }) => {
const computeFillPlaceholderTaskCount = obj => {
const { tasks, data, timeGranularity } = obj;
let result = { prev: 0, next: 0 };
// 新数据的开始时间 < 旧数据的开始时间
// 超出了上限 补上限的时间刻度
@ -54,5 +55,5 @@ const computeFillPlaceholderTaskCount = ({ tasks, data, timeGranularity }) => {
export default {
setPlaceholderTasks,
computeFillPlaceholderTaskCount
computeFillPlaceholderTaskCount,
};

47
utils/time.js

@ -12,7 +12,7 @@ import dayjs from 'dayjs';
* 格式化数字
* @param {*} n
*/
const formatNumber = (n) => {
const formatNumber = n => {
const str = n.toString();
return str[1] ? str : `0${str}`;
};
@ -21,7 +21,7 @@ const formatNumber = (n) => {
* 格式化时间
* @param {number} beginTime
*/
const formatTime = (beginTime) => {
const formatTime = beginTime => {
const date = new Date(beginTime);
const year = date.getFullYear();
const month = date.getMonth() + 1;
@ -49,7 +49,7 @@ const add = (time, num, cycle) => {
* @param {string} time
* @returns {{hours: number, minutes: number}}
*/
const convertTime = (time) => {
const convertTime = time => {
const arr = time.split(':');
return {
hours: parseInt(arr[0], 10),
@ -61,7 +61,7 @@ const convertTime = (time) => {
* 将秒 ->
* @param {number} seconds
*/
const secondToMinute = (seconds) => {
const secondToMinute = seconds => {
const minute = formatNumber(Math.floor(seconds / 60));
const second = formatNumber(parseInt(seconds % 60, 10));
return {
@ -75,7 +75,7 @@ const secondToMinute = (seconds) => {
* @param {Number} timestamp 时间戳
* @return date:2018/10/09 time: 12:59
*/
const setTimestampToStr = (timestamp) => {
const setTimestampToStr = timestamp => {
const timeObj = new Date(timestamp);
const year = timeObj.getFullYear();
const month = formatNumber(timeObj.getMonth() + 1);
@ -94,11 +94,10 @@ const setTimestampToStr = (timestamp) => {
* 检测时间(ms)是不是今天
* @param {Number} time 时间戳
*/
const validateTimeIsToday = (time) => {
const validateTimeIsToday = time => {
const timeDate = new Date(time);
const date = new Date();
return timeDate.getFullYear() === date.getFullYear() && timeDate.getMonth() === date.getMonth() && timeDate
.getDate() === date.getDate();
return timeDate.getFullYear() === date.getFullYear() && timeDate.getMonth() === date.getMonth() && timeDate.getDate() === date.getDate();
};
/**
@ -120,7 +119,7 @@ const isSame = (time, value, cycle) => {
* 如果不是今年 -> // :
* 否则 ** :
*/
const formatBeginTime = (timestamp) => {
const formatBeginTime = timestamp => {
const timeObj = new Date(timestamp);
const year = timeObj.getFullYear();
const month = formatNumber(timeObj.getMonth() + 1);
@ -134,7 +133,8 @@ const formatBeginTime = (timestamp) => {
if (validateTimeIsToday(timestamp)) {
// 今天
return `今天 ${time}`;
} if (currentYear !== year) {
}
if (currentYear !== year) {
// 不是今年
return `${date} ${time}`;
}
@ -149,7 +149,7 @@ const formatBeginTime = (timestamp) => {
* 其余的显示分钟
* 超过2小时 2 * 60 * 60 * 1000 ms 转换成小时 + 分钟数
*/
const formatDuration = (duration) => {
const formatDuration = duration => {
const minuteTime = 60 * 1000;
const hourTime = 60 * minuteTime;
const dayTime = 24 * hourTime;
@ -159,7 +159,8 @@ const formatDuration = (duration) => {
if (duration <= 60 * 1000) {
// 小于1分钟 返回几秒
return `${Math.floor(duration / 1000)}`;
} if (duration > dayTime) {
}
if (duration > dayTime) {
// 大于1天
if (minutes === 0) {
if (hours === 0) {
@ -171,7 +172,8 @@ const formatDuration = (duration) => {
}
// 分钟不是0 返回几天几时几分
return `${days}${hours}${minutes}`;
} if (duration > 2 * hourTime) {
}
if (duration > 2 * hourTime) {
// 大于2h
if (minutes === 0) {
// 分钟是0 返回几小时
@ -192,7 +194,7 @@ const formatDuration = (duration) => {
* 其余的显示分钟 { days: 0, hours: 0, minutes, seconds: 0 }
* 超过2小时 2 * 60 * 60 * 1000 ms 转换成{ days: 0, hours, minutes, seconds: 0 }
*/
const formatDurationToObject = (duration) => {
const formatDurationToObject = duration => {
const minuteTime = 60 * 1000;
const hourTime = 60 * minuteTime;
const dayTime = 24 * hourTime;
@ -247,14 +249,15 @@ const formatDurationToObject = (duration) => {
* @param {obj} 对象格式的时间 days, hours, minutes, seconds
* @return 时长的ms
*/
const formatObjectTimeToMs = (days = 0, hours = 0, minutes = 0, seconds = 0) => days * 24 * 60 * 60 * 1000 + hours * 60 * 60 * 1000 + minutes * 60 * 1000 + seconds * 1000;
const formatObjectTimeToMs = (days = 0, hours = 0, minutes = 0, seconds = 0) =>
days * 24 * 60 * 60 * 1000 + hours * 60 * 60 * 1000 + minutes * 60 * 1000 + seconds * 1000;
/**
* 计算过滤 周期
* @param {string} time 周期字符串
* @return {string} cycle 周期英文字符串
*/
const computeCycle = (time) => {
const computeCycle = time => {
// 加载下一个周期的任务
let cycle = 'day';
switch (time) {
@ -307,7 +310,7 @@ const formatStartTimeToCycleTime = (cycle, time) => {
* @param {number} leftTime 剩余时间ms
* @returns { num: 显示的数字, time: 演示器演示时长 }
*/
const computeDurationText = (leftTime) => {
const computeDurationText = leftTime => {
try {
if (leftTime < 0) {
return {
@ -315,15 +318,7 @@ const computeDurationText = (leftTime) => {
time: null,
};
}
const {
years,
months,
days,
hours,
minutes,
seconds,
milliseconds,
} = dayjs.duration(leftTime).$d;
const { years, months, days, hours, minutes, seconds, milliseconds } = dayjs.duration(leftTime).$d;
let num = 0;
let time = 1000;

Loading…
Cancel
Save