Browse Source

fix: 解决冲突

feat
song 4 years ago
parent
commit
a88c9331ce
  1. 102
      App.vue
  2. 1
      CHANGELOG.md
  3. 20
      apis/tall.js
  4. BIN
      common/img/weixinIcon.png
  5. 48
      common/js/dc-clipboard/clipboard.js
  6. 8
      common/styles/theme/default.scss
  7. 5
      common/styles/theme/index.scss
  8. 8
      common/styles/theme/test.scss
  9. 36
      components/Globals/Globals.vue
  10. 13
      components/Theme/Theme.vue
  11. 7
      components/TimeLine/component/Title.vue
  12. 4
      components/Title/components/CreateTask.vue
  13. 7
      hooks/theme/useTheme.js
  14. 1
      hooks/user/useGetToken.js
  15. 285
      hooks/user/userMixin - 副本.js
  16. 123
      hooks/user/userMixin.js
  17. 1
      package.json
  18. 36
      pages.json
  19. 266
      pages/index/index.vue
  20. 3
      pages/project/project.vue
  21. 94
      pages/user/accountLogin.vue
  22. 266
      pages/user/mixin.js
  23. 9
      plugins/p-deliver/p-deliver.vue
  24. 10
      plugins/p-task-title/p-task-title.vue
  25. 70
      store/index.js
  26. 2
      store/user/mutations.js

102
App.vue

@ -1,14 +1,26 @@
<script>
import useGetToken from "@/hooks/user/useGetToken";
// import { mapState } from 'vuex';
export default {
// computed: {
// ...mapState(['theme']),
// },
// watch: {
// theme(newTheme) {
// console.log('newTheme: ', newTheme);
// if (!newTheme) return;
// this.loadTheme();
// },
// },
async onLaunch(options) {
// this.loadTheme();
console.log('onLaunch options: ', options);
this.checkNetwork(); //
this.getSystemInfo(); //
this.syncLocalDataToStore(options.query.u); // localStoragestore
const token = await useGetToken();
await this.syncLocalDataToStore(options.query.u); // localStoragestore
const token = await this.getToken();
if (!token) {
this.$ui.showToast('获取用户信息失败, 请登录');
// TODO:
@ -19,29 +31,71 @@ export default {
},
methods: {
// loadTheme() {
// const path = this.theme.replace('-', '/');
// import(`./common/styles/${path}.scss`);
// },
async getToken() {
const { token } = this.$store.state.user;
const tokenIsAvailable = this.$store.getters['user/tokenIsAvailable'];
const userId = this.$store.getters['user/userId'];
if (token && tokenIsAvailable) {
// 1.1 storetoken 使storetoken
return token;
} else {
// 2. userIdtoken
if (userId) {
try {
const { token } = await this.$store.dispatch('user/getTokenByUserId', userId);
return token;
} catch (error) {
console.error('error: ', error);
return null;
}
} else {
return null;
}
}
},
/**
* 将localStorage里的数据同步到store里
* user, token, tokenExpiredTime
*/
syncLocalDataToStore(urlUserId) {
const localUser = uni.$storage.getStorageSync('user');
const localToken = uni.$storage.getStorageSync('anyringToken');
const tokenExpiredTime = uni.$storage.getStorageSync('tokenExpiredTime');
if (!this.$store.state.user.user && localUser) {
// user
const user = JSON.parse(localUser);
if (!urlUserId || user.id === urlUserId) {
this.$store.commit('user/setUser', user);
} else {
this.$store.commit('user/setUser', { id: urlUserId });
return new Promise((resolve, reject) => {
try {
const localUser = uni.$storage.getStorageSync('user');
const localToken = uni.$storage.getStorageSync('anyringToken');
const tokenExpiredTime = uni.$storage.getStorageSync('tokenExpiredTime');
if (!this.$store.state.user.user) {
if (localUser) {
// user
const user = JSON.parse(localUser);
if (!urlUserId || user.id === urlUserId) {
this.$store.commit('user/setUser', user);
} else {
this.$store.commit('user/setUser', { id: urlUserId });
}
} else {
this.$store.commit('user/setUser', { id: urlUserId });
}
}
if (this.$store.state.user.token && localToken) {
// token
this.$store.commit('user/setToken', localToken);
}
if (this.$store.state.user.tokenExpiredTime && tokenExpiredTime) {
// tokenExpiredTime
this.$store.commit('user/setTokenExpiredTime', +tokenExpiredTime);
}
resolve();
} catch (error) {
reject(error);
}
}
if (this.$store.state.user.token && localToken) { // token
this.$store.commit('user/setToken', localToken);
}
if (this.$store.state.user.tokenExpiredTime && tokenExpiredTime) { // tokenExpiredTime
this.$store.commit('user/setTokenExpiredTime', +tokenExpiredTime);
}
});
},
// store
@ -93,7 +147,8 @@ export default {
*/
async noPhone(phone) {
if (!phone) {
uni.navigateTo({ title: '/pages/phone-bind/phone-bind' });
// TODO:
// uni.navigateTo({ url: '/pages/phone-bind/phone-bind' });
}
},
},
@ -102,10 +157,11 @@ export default {
<style lang="scss">
/*每个页面公共css */
@import "@/uni_modules/vk-uview-ui/index.scss";
@import '@/uni_modules/vk-uview-ui/index.scss';
@import '@/common/styles/iconfont.scss';
@import '@/common/styles/app.scss';
@import '@/common/styles/tailwind.scss';
@import '@/common/styles/theme/index.scss';
page {
height: 100%;

1
CHANGELOG.md

@ -9,6 +9,7 @@
- | 使用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)
- | 时间轴展示 | [8b1b380](https://101.201.226.163:50022/TALL/TALL-MUI-4/commits/8b1b380)
- | 时间轴接口 | [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)

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
}

8
common/styles/theme/default.scss

@ -0,0 +1,8 @@
// 默认主题文件
.theme-default {
background-color: #333;
.u-card {
font-size: 24px !important;
color: #0f0;
}
}

5
common/styles/theme/index.scss

@ -0,0 +1,5 @@
// 整合所有主题
// 默认主题
@import './default.scss';
@import './test.scss';

8
common/styles/theme/test.scss

@ -0,0 +1,8 @@
// TODO: 测试用的scss主题样式
.theme-test {
background-color: #fff;
.u-card {
font-size: 24px !important;
background-color: #ff0 !important;
}
}

36
components/Globals/Globals.vue

@ -1,5 +1,5 @@
<template>
<view class="m-2" v-show="globals && globals.length">
<theme class="m-2" >
<u-card
@click="openCard"
:show-foot="false"
@ -7,14 +7,16 @@
:style="{ 'max-height': globalsHeight + 'px' }"
border-radius="25"
margin="0"
class="global-container"
>
<view slot="body">
<template v-slot:body>
<scroll-view :scrollY="true" :style="{ 'max-height': globalsHeight - 30 + 'px' }">
<skeleton :banner="false" :loading="!globals.length" :row="4" animate class="u-line-2 skeleton"></skeleton>
<skeleton :banner="false" :loading="!globals.length" :row="3" animate class="u-line-2 skeleton"></skeleton>
<view class="grid gap-2">
<block v-for="item in globals" :key="item.id">
<template v-if="item.plugins">
<block v-for="(pluginArr, i) in item.plugins" :key="i">
<view v-for="item in globals" :key="item.id">
<template v-if="item.plugins && item.plugins.length">
<view v-for="(pluginArr, i) in item.plugins" :key="i">
<template class="p-0 u-col-between" v-if="pluginArr.length">
<Plugin
:class="[`row-span-${plugin.row}`, `col-span-${plugin.col}`]"
@ -27,26 +29,32 @@
v-for="plugin in pluginArr"
/>
</template>
</block>
</view>
</template>
</block>
<!-- 任务名插件 -->
<p-task-title :task="item" v-else />
<!-- 交付物插件 -->
<p-deliver></p-deliver>
</view>
</view>
</scroll-view>
</view>
</template>
</u-card>
</view>
</theme>
</template>
<script setup>
import { computed } from 'vue';
import { useStore } from 'vuex';
import { useStore } from 'vuex';
import Skeleton from '@/components/Skeleton/Skeleton.vue';
const sysHeight = uni.getSystemInfoSync().screenHeight; //
const globalsHeight = Math.floor(((sysHeight - 44 - 30 - 10) / 5) * 4); //
const store = useStore();
const isShrink = computed(() => store.state.task.isShrink);
const sysHeight = uni.getSystemInfoSync().screenHeight;
const isShrink = computed(() => store.state.task.isShrink); //
const globals = computed(() => store.getters['task/globals']);
const globalsHeight = computed(() => [((sysHeight.value - 44 - 30 - 10) / 5) * 4]);
console.log('globals: ', globals.value);
//
function openCard() {

13
components/Theme/Theme.vue

@ -0,0 +1,13 @@
<template>
<view :class="[theme]">
<slot></slot>
</view>
</template>
<script setup>
import { computed } from 'vue';
import { useStore } from 'vuex';
const store = useStore();
const theme = computed(() => store.state.theme);
</script>

7
components/TimeLine/component/Title.vue

@ -1,7 +0,0 @@
<!--
* @Author: aBin
* @email: binbin0314@126.com
* @Date: 2021-07-19 15:40:02
* @LastEditors: aBin
* @LastEditTime: 2021-07-19 15:40:03
-->

4
components/Title/components/CreateTask.vue

@ -55,7 +55,7 @@
:key="roleIndex"
@click="change(roleIndex)"
>
<view v-model="role.id">{{ role.name }}</view>
<view>{{ role.name }}</view>
<u-icon v-if="role.dropdownShow" name="checkbox-mark" color="#2979ff" size="28"></u-icon>
</div>
</view>
@ -107,7 +107,7 @@
:key="Index"
@click="choose(Index)"
>
<view v-model="checkoutOption.value">{{ checkoutOption.name }}</view>
<view>{{ checkoutOption.name }}</view>
<u-icon v-if="checkoutOption.dropdownShow" name="checkbox-mark" color="#2979ff" size="28"></u-icon>
</div>
</view>

7
hooks/theme/useTheme.js

@ -0,0 +1,7 @@
import { computed } from 'vue';
import { useStore } from 'vuex';
export default function useTheme() {
const store = useStore();
const theme = computed(() => store.state.theme);
return theme;
}

1
hooks/user/useGetToken.js

@ -16,6 +16,7 @@ export default async function useGetToken() {
const token = computed(() => store.state.user.token);
const tokenIsAvailable = computed(() => store.getters['user/tokenIsAvailable']); // token是否可用
const userId = computed(() => store.getters['user/userId']);
debugger;
if (token.value && tokenIsAvailable.value) {
// 1.1 store里有token 且没过期直接:使用store的token
return token.value;

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

1
package.json

@ -53,6 +53,7 @@
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"cz": "npm run log && git add . && git cz",
"fix": "eslint --fix",
"log": "conventional-changelog --config ./node_modules/vue-cli-plugin-commitlint/lib/log -i CHANGELOG.md -s -r 0"
},
"author": "",

36
pages.json

@ -5,13 +5,20 @@
"style": {
"navigationBarText": "TALL"
}
},
{
"path": "pages/project/project",
"style": {
"navigationStyle": "custom",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/project/project",
"style": {
"navigationStyle": "custom",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/user/accountLogin",
"style": {
"navigationStyle": "custom",
"navigationBarTextStyle": "white"
}
}
],
"globalStyle": {
@ -19,12 +26,13 @@
"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"
}
},
"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"
}
}
}

266
pages/index/index.vue

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

3
pages/project/project.vue

@ -12,6 +12,9 @@
<!-- 定期任务面板 -->
<TimeLine @getTasks="getTasks" class="flex-1 overflow-hidden" ref="timeLine" />
<!-- TODO: DEBUG: -->
<u-button @click="$store.commit('setTheme', 'theme-test')">测试切换主题</u-button>
</view>
</view>
</template>

94
pages/user/accountLogin.vue

@ -0,0 +1,94 @@
<template>
<view class="u-p-l-50 u-p-r-50 u-p-t-30">
<u-form :model="model" ref="uForm" :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 uForm = ref(null);
const model = ref({
account: '',
password: ''
})
if (userInfo) {
user.value = JSON.parse(userInfo);
}
const submit = () => {
// uForm.value.validate().then().catch();
}
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`;
// },
// // }
// };

9
plugins/p-deliver/p-deliver.vue

@ -0,0 +1,9 @@
<template>
<view class="deliver-container">p-deliver</view>
</template>
<script setup>
</script>
<style lang="scss"></style>

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

@ -0,0 +1,10 @@
<template>
<!-- 任务名插件 -->
<theme>
<view>{{ task.name }}</view>
</theme>
</template>
<script setup>
defineProps({ task: { type: Object, default: () => {} } });
</script>

70
store/index.js

@ -1,48 +1,58 @@
import { createStore } from 'vuex';
import user from './user/index.js';
import socket from './socket/index.js';
import project from './project/index.js';
import role from './role/index.js';
import socket from './socket/index.js';
import task from './task/index.js';
import user from './user/index.js';
// 不属于具体模块的 应用级的 store内容
const state = {
networkConnected: true, // 网络是否连接
forceUseStorage: true, // 强制启用storage
systemInfo: null, // 系统设备信息
theme: 'theme-default',
networkConnected: true, // 网络是否连接
forceUseStorage: true, // 强制启用storage
systemInfo: null, // 系统设备信息
};
const getters = {
// 是否启用本地存储
// 设置了强制启用本地存储 或者 没有网络连接的时候
useStorage({ networkConnected, forceUseStorage }) {
return forceUseStorage || !networkConnected;
},
// 是否启用本地存储
// 设置了强制启用本地存储 或者 没有网络连接的时候
useStorage({ networkConnected, forceUseStorage }) {
return forceUseStorage || !networkConnected;
},
};
const mutations = {
/**
* 设置网络是否连接的变量
* @param {*} state
* @param {boolean} networkConnected
*/
setNetworkConnected(state, networkConnected) {
state.networkConnected = networkConnected;
},
/**
* 设置网络是否连接的变量
* @param {*} state
* @param {boolean} networkConnected
*/
setNetworkConnected(state, networkConnected) {
state.networkConnected = networkConnected;
},
/**
* 设置系统信息的数据
* @param {object} state
* @param {object | null} data 获取到的数据
*/
setSystemInfo(state, data) {
state.systemInfo = data;
},
/**
* 设置系统信息的数据
* @param {object} state
* @param {object | null} data 获取到的数据
*/
setSystemInfo(state, data) {
state.systemInfo = data;
},
/**
* 设置主题
* @param {object} state
* @param {string} theme 主题名称 默认theme-default
*/
setTheme(state, theme) {
state.theme = theme || 'theme-default';
},
};
export default createStore({
state,
getters,
mutations,
modules: {user, socket, project, role, task}
state,
getters,
mutations,
modules: { user, socket, project, role, task },
});

2
store/user/mutations.js

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

Loading…
Cancel
Save