134 changed files with 17706 additions and 543 deletions
@ -0,0 +1,148 @@ |
|||||
|
--- |
||||
|
kind: pipeline |
||||
|
type: docker |
||||
|
name: dev |
||||
|
|
||||
|
# 挂载的主机卷,可以映射到docker容器中 |
||||
|
volumes: |
||||
|
# maven构建缓存(宿主机目录) |
||||
|
- name: ssh_key |
||||
|
host: |
||||
|
path: /root/.ssh/ |
||||
|
- name: cache |
||||
|
host: |
||||
|
path: /var/lib/cache |
||||
|
- name: data |
||||
|
host: |
||||
|
path: /var/lib/data |
||||
|
|
||||
|
steps: |
||||
|
- name: restore-cache |
||||
|
image: drillster/drone-volume-cache |
||||
|
volumes: |
||||
|
- name: cache |
||||
|
path: /cache |
||||
|
settings: |
||||
|
restore: true |
||||
|
mount: |
||||
|
- ./node_modules |
||||
|
|
||||
|
- name: build |
||||
|
image: node:latest |
||||
|
pull: if-not-exists # default always |
||||
|
volumes: |
||||
|
- name: cache |
||||
|
path: /root/.m2 |
||||
|
commands: |
||||
|
- npm i |
||||
|
- npm run build:dev |
||||
|
|
||||
|
- name: rebuild-cache |
||||
|
image: drillster/drone-volume-cache |
||||
|
volumes: |
||||
|
- name: cache |
||||
|
path: /cache |
||||
|
settings: |
||||
|
rebuild: true |
||||
|
mount: |
||||
|
- ./node_modules |
||||
|
|
||||
|
- name: deploy-scp |
||||
|
image: appleboy/drone-scp |
||||
|
pull: if-not-exists |
||||
|
volumes: |
||||
|
- name: ssh_key |
||||
|
path: /root/.ssh/ |
||||
|
settings: |
||||
|
host: test.tall.wiki |
||||
|
port: 22 |
||||
|
username: root |
||||
|
key_path: /root/.ssh/id_rsa |
||||
|
rm: true # true则会删除目标目录重建 |
||||
|
target: /home/tall/v4.0.0 |
||||
|
source: dist/**/* |
||||
|
strip_components: 1 # 去除的目录层数,如果没有该选项,则拷贝过去是 target/xxx.jar,1代表去除target |
||||
|
|
||||
|
# - name: run-ssh |
||||
|
# image: appleboy/drone-ssh |
||||
|
# pull: if-not-exists |
||||
|
# volumes: |
||||
|
# - name: ssh_key |
||||
|
# path: /root/.ssh/ |
||||
|
# settings: |
||||
|
# settings: |
||||
|
# host: test.tall.wiki |
||||
|
# port: 22 |
||||
|
# username: root |
||||
|
# key_path: /root/.ssh/id_rsa |
||||
|
# script_stop: true # stop script after first failure |
||||
|
# #command_timeout: 30s # 30seconds, the maximum amount of time for the execute commands, default is 10 minutes. |
||||
|
# script: |
||||
|
# - cd /home/iacd-platform-drone |
||||
|
# - ./re.sh > /dev/null 2> /dev/null & |
||||
|
|
||||
|
- name: notify-email |
||||
|
image: drillster/drone-email |
||||
|
pull: if-not-exists |
||||
|
settings: |
||||
|
host: smtp.qiye.aliyun.com #例如 smtp.qq.com |
||||
|
port: 465 #例如QQ邮箱端口465 |
||||
|
username: devops@ccsens.com #邮箱用户名 |
||||
|
password: #邮箱密码 |
||||
|
from_secret: orgsecret_password_mail_devops |
||||
|
from: devops@ccsens.com |
||||
|
recipients: weizezhao@ccsens.com #收件人,多个用,隔开 |
||||
|
when: #执行条件 |
||||
|
status: |
||||
|
- success |
||||
|
- changed |
||||
|
- failure |
||||
|
|
||||
|
- name: notify-wechatwork |
||||
|
image: fifsky/drone-wechat-work |
||||
|
pull: if-not-exists |
||||
|
settings: |
||||
|
url: https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=b2b93e9a-128b-41d4-8dce-12004e3f48b9 |
||||
|
msgtype: markdown |
||||
|
content: | |
||||
|
{{if eq .Status "success" }} |
||||
|
#### 🎉 ${DRONE_REPO} 构建成功 |
||||
|
> Commit: [${DRONE_COMMIT_MESSAGE}](${DRONE_COMMIT_LINK}) |
||||
|
> Author: ${DRONE_COMMIT_AUTHOR} |
||||
|
> [点击查看](${DRONE_BUILD_LINK}) |
||||
|
{{else}} |
||||
|
#### ❌ ${DRONE_REPO} 构建失败 |
||||
|
> Commit: [${DRONE_COMMIT_MESSAGE}](${DRONE_COMMIT_LINK}) |
||||
|
> Author: ${DRONE_COMMIT_AUTHOR} |
||||
|
> 请立即修复!!! |
||||
|
> [点击查看](${DRONE_BUILD_LINK}) |
||||
|
{{end}} |
||||
|
when: |
||||
|
status: |
||||
|
- failure |
||||
|
- success |
||||
|
|
||||
|
trigger: |
||||
|
branch: develop |
||||
|
# - name: notify-dingtalk |
||||
|
# image: lddsb/drone-dingtalk-message |
||||
|
# environment: |
||||
|
# PASSWORD: |
||||
|
# from_secret: password_mail_devops |
||||
|
# settings: |
||||
|
# token: your-dingtalk-robot-access-token |
||||
|
# type: markdown |
||||
|
# message_color: true |
||||
|
# message_pic: true |
||||
|
# sha_link: true |
||||
|
|
||||
|
# -name: notify-slack |
||||
|
# image: plugins/slack |
||||
|
# webhook: https://hooks.slack.com/ www.dijiuyy.com services/xxx/xxx/xxx |
||||
|
# channel: dev |
||||
|
# template: > |
||||
|
# {{#success build.status}} |
||||
|
# build {{build.number}} succeeded. Good job. |
||||
|
# {{else}} |
||||
|
# build {{build.number}} failed. Fix me please. |
||||
|
# {{/success}} |
||||
@ -1,21 +1,41 @@ |
|||||
module.exports = { |
module.exports = { |
||||
"env": { |
env: { |
||||
"browser": true, |
browser: true, |
||||
"es2021": true |
es2021: true, |
||||
}, |
}, |
||||
"extends": [ |
extends: [ |
||||
"plugin:vue/essential", |
'plugin:vue/essential', |
||||
"airbnb-base" |
'airbnb-base', |
||||
|
], |
||||
|
parserOptions: { |
||||
|
ecmaVersion: 13, |
||||
|
parser: '@typescript-eslint/parser', |
||||
|
sourceType: 'module', |
||||
|
}, |
||||
|
plugins: [ |
||||
|
'vue', |
||||
|
'@typescript-eslint', |
||||
|
], |
||||
|
rules: { |
||||
|
'vue/html-self-closing': 'off', |
||||
|
'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off', |
||||
|
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off', |
||||
|
'no-param-reassign': 'off', |
||||
|
'max-len': ['error', { code: 140, tabWidth: 2 }], |
||||
|
'object-curly-newline': ['error', { multiline: true }], |
||||
|
'arrow-parens': ['error', 'as-needed'], |
||||
|
'linebreak-style': 'off', |
||||
|
'vue/attributes-order': 'off', |
||||
|
'vue/singleline-html-element-content-newline': 'off', |
||||
|
'vue/max-attributes-per-line': 'off', |
||||
|
'vue/multiline-html-element-content-newline': 'off', |
||||
|
'vue/html-indent': 'off', |
||||
|
'vue/html-closing-bracket-newline': [ |
||||
|
'error', |
||||
|
{ |
||||
|
singleline: 'never', |
||||
|
multiline: 'always', |
||||
|
}, |
||||
], |
], |
||||
"parserOptions": { |
}, |
||||
"ecmaVersion": 13, |
|
||||
"parser": "@typescript-eslint/parser", |
|
||||
"sourceType": "module" |
|
||||
}, |
|
||||
"plugins": [ |
|
||||
"vue", |
|
||||
"@typescript-eslint" |
|
||||
], |
|
||||
"rules": { |
|
||||
} |
|
||||
}; |
}; |
||||
|
|||||
@ -1,36 +1,169 @@ |
|||||
<script> |
<script> |
||||
// import { |
// import { mapState } from 'vuex'; |
||||
// ref, |
|
||||
// computed |
export default { |
||||
// } from 'vue'; |
// computed: { |
||||
// import { |
// ...mapState(['theme']), |
||||
// useStore |
// }, |
||||
// } from 'vuex'; |
|
||||
|
// watch: { |
||||
// const store = useStore(); |
// theme(newTheme) { |
||||
|
// console.log('newTheme: ', newTheme); |
||||
// onLaunch(() => { |
// if (!newTheme) return; |
||||
// // checkNetwork(); // 监听网络状态 |
// this.loadTheme(); |
||||
// }); |
// }, |
||||
|
// }, |
||||
// 检查网络状态 设置store里的网络状态变量 |
|
||||
// 网络连接 且 不是2g 不是3g 才算是网络连接; 否则不是 将启用本地存储 |
async onLaunch(options) { |
||||
// function checkNetwork() { |
// this.loadTheme(); |
||||
// uni.getNetworkType({ |
console.log('onLaunch options: ', options); |
||||
// success: ({ networkType }) => { |
this.checkNetwork(); // 监听网络状态 |
||||
// this.setNetworkConnected(!(networkType === 'none' || networkType === '2g' || networkType === '3g')); |
this.getSystemInfo(); // 获取系统设备信息、 |
||||
// }, |
await this.syncLocalDataToStore(options.query.u); // 将localStorage里的数据同步到store里 |
||||
// }); |
const token = await this.getToken(); |
||||
// // 监听网络状态的变化 |
if (!token) { |
||||
// uni.onNetworkStatusChange(({ isConnected, networkType }) => { |
this.$ui.showToast('获取用户信息失败, 请登录'); |
||||
// this.setNetworkConnected(isConnected && !(networkType === '2g' || networkType === '3g')); |
// TODO: 跳转登录界面 |
||||
// }); |
return; |
||||
// } |
} |
||||
|
this.noPhone(this.$store.state.user.phone); |
||||
|
this.$store.dispatch('socket/initSocket'); |
||||
|
}, |
||||
|
|
||||
|
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 store里有token 且没过期直接:使用store的token |
||||
|
return token; |
||||
|
} else { |
||||
|
// 2. 根据userId获取token |
||||
|
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) { |
||||
|
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); |
||||
|
} |
||||
|
}); |
||||
|
}, |
||||
|
|
||||
|
// 检查网络状态 设置store里的网络状态变量 |
||||
|
// 网络连接 且 不是2g 不是3g 才算是网络连接; 否则不是 将启用本地存储 |
||||
|
checkNetwork() { |
||||
|
uni.getNetworkType({ |
||||
|
success: ({ networkType }) => { |
||||
|
this.$store.commit('setNetworkConnected', !(networkType === 'none' || networkType === '2g' || networkType === '3g')); |
||||
|
}, |
||||
|
}); |
||||
|
// 监听网络状态的变化 |
||||
|
uni.onNetworkStatusChange(({ isConnected, networkType }) => { |
||||
|
this.$store.commit('setNetworkConnected', isConnected && !(networkType === '2g' || networkType === '3g')); |
||||
|
}); |
||||
|
}, |
||||
|
|
||||
|
// 获取系统设备信息 |
||||
|
getSystemInfo() { |
||||
|
uni.getSystemInfo({ |
||||
|
success: result => { |
||||
|
this.$store.commit('setSystemInfo', result); |
||||
|
}, |
||||
|
fail: error => { |
||||
|
console.error('getSystemInfo fail:', error); |
||||
|
}, |
||||
|
}); |
||||
|
}, |
||||
|
|
||||
|
// 登录 |
||||
|
async signin() { |
||||
|
try { |
||||
|
const data = await uni.$u.api.signin(); |
||||
|
if (data && data.token) { |
||||
|
this.$store.commit('user/setUser', data); |
||||
|
this.$store.commit('user/setToken', data.token); |
||||
|
noPhone(data.phone); |
||||
|
} else { |
||||
|
uni.$ui.showToast('返回数据异常'); |
||||
|
} |
||||
|
} catch (error) { |
||||
|
console.error('error: ', error); |
||||
|
uni.$ui.showToast(error || '登录失败'); |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 没有手机号 跳转绑定手机号的界面 |
||||
|
* @param {string} phone |
||||
|
*/ |
||||
|
async noPhone(phone) { |
||||
|
if (!phone) { |
||||
|
// TODO: |
||||
|
// uni.navigateTo({ url: '/pages/phone-bind/phone-bind' }); |
||||
|
} |
||||
|
}, |
||||
|
}, |
||||
|
}; |
||||
</script> |
</script> |
||||
|
|
||||
<style lang="scss"> |
<style lang="scss"> |
||||
/*每个页面公共css */ |
/*每个页面公共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/iconfont.scss'; |
||||
@import '@/common/styles/app.scss'; |
@import '@/common/styles/app.scss'; |
||||
|
@import '@/common/styles/tailwind.scss'; |
||||
|
@import '@/common/styles/theme/index.scss'; |
||||
|
|
||||
|
page { |
||||
|
height: 100%; |
||||
|
} |
||||
</style> |
</style> |
||||
|
|||||
@ -1,6 +1,68 @@ |
|||||
# 1.0.0 (2021-12-31) |
# 1.0.0 (2022-01-11) |
||||
|
|
||||
|
### 🌟 新功能 |
||||
范围|描述|commitId |
范围|描述|commitId |
||||
--|--|-- |
--|--|-- |
||||
- | Initial commit | 52b8f49 |
- | 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) |
||||
|
- | 时间轴展示 | [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) |
||||
|
- | 添加 timeline | [72dad2b](https://101.201.226.163:50022/TALL/TALL-MUI-4/commits/72dad2b) |
||||
|
- | 表单验证 | [8f3bc1e](https://101.201.226.163:50022/TALL/TALL-MUI-4/commits/8f3bc1e) |
||||
|
- | 账户名密码登录 | [ebf456e](https://101.201.226.163:50022/TALL/TALL-MUI-4/commits/ebf456e) |
||||
|
- | 项目列表 | [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 |
||||
|
--|--|-- |
||||
|
- | 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) |
||||
|
|
||||
|
|
||||
|
### 🐛 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 |
||||
|
--|--|-- |
||||
|
- | 添加drone.yml | [9fbae89](https://101.201.226.163:50022/TALL/TALL-MUI-4/commits/9fbae89) |
||||
|
|
||||
|
|
||||
|
### 🔨 代码重构 |
||||
|
范围|描述|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 |
||||
|
--|--|-- |
||||
|
- | 更新代码 | [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 |
||||
|
--|--|-- |
||||
|
- | Initial commit | [52b8f49](https://101.201.226.163:50022/TALL/TALL-MUI-4/commits/52b8f49) |
||||
|
|
||||
|
|||||
@ -0,0 +1,17 @@ |
|||||
|
import Config from '@/common/js/config.js' |
||||
|
|
||||
|
const apiUrl = Config.apiUrl; |
||||
|
const defaultwbs = `${apiUrl}/defaultwbs`; |
||||
|
|
||||
|
export function setupProject(app) { |
||||
|
uni.$u.api = { ...uni.$u.api } || {}; |
||||
|
//根据id获取项目信息
|
||||
|
uni.$u.api.findProjectById = param => uni.$u.post(`${defaultwbs}/project/findProjectById`, param); |
||||
|
|
||||
|
//创建分享连接
|
||||
|
uni.$u.api.createShare = param => uni.$u.post(`${defaultwbs}/share/create`, param); |
||||
|
|
||||
|
//点击分享连接
|
||||
|
uni.$u.api.clickShare = param => uni.$u.post(`${defaultwbs}/share/click`, param); |
||||
|
}; |
||||
|
|
||||
@ -0,0 +1,12 @@ |
|||||
|
import Config from '@/common/js/config.js' |
||||
|
|
||||
|
const apiUrl = Config.apiUrl; |
||||
|
const defaultwbs = `${apiUrl}/defaultwbs`; |
||||
|
|
||||
|
export function setupRole(app) { |
||||
|
uni.$u.api = { ...uni.$u.api } || {}; |
||||
|
//根据项目id查找角色
|
||||
|
uni.$u.api.findShowRole = param => uni.$u.post(`${defaultwbs}/role/show`, param); |
||||
|
//根据项目id查找所有成员
|
||||
|
uni.$u.api.queryChecker = param => uni.$u.post(`${defaultwbs}/deliver/queryChecker`, param); |
||||
|
}; |
||||
@ -0,0 +1,33 @@ |
|||||
|
import Config from '@/common/js/config.js' |
||||
|
|
||||
|
const apiUrl = Config.apiUrl; |
||||
|
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 => uni.$u.http.post(`${tall}/users/signin`, params); // 登录
|
||||
|
// 获取图片验证码
|
||||
|
uni.$u.api.getImageCode = () => uni.$u.get(`${tall}/users/code`); |
||||
|
// 获取短信验证码
|
||||
|
uni.$u.api.getSmsCode = params => uni.$u.get(`${tall}/users/smscode`, params); |
||||
|
// 根据userId获取token
|
||||
|
uni.$u.api.getToken = userId => uni.$u.get(`${tall}/users/userId`, { userId }); |
||||
|
// 绑定手机号
|
||||
|
uni.$u.api.phoneBind = (phone, smsCode) => uni.$u.http.post(`${tall}/users/binding`, { phone, smsCode }); |
||||
|
// 是否合并账号
|
||||
|
uni.$u.api.phoneMerge = (phone, isMerge) => uni.$u.http.post(`${tall}/users/merge`, { phone, isMerge }); |
||||
|
// 修改用户信息
|
||||
|
uni.$u.api.updateUserInfo = params => uni.$u.http.post(`${tall}/users/userInfo`, params); |
||||
|
// 获取项目列表
|
||||
|
uni.$u.api.getProjects = (startTime, endTime) => uni.$u.post(`${tall}/project/query`, { startTime, endTime }); |
||||
|
// 查询日历是否有小红点
|
||||
|
uni.$u.api.findRedPoint = (startTime, endTime) => uni.$u.post(`${tall}/project/day`, { startTime, endTime }); |
||||
|
// 设置项目顺序
|
||||
|
uni.$u.api.setProjectSort = params => uni.$u.post(`${tall}/project/setProjectSort`, params); |
||||
|
// 设置项目父子结构
|
||||
|
uni.$u.api.setProjectRelation = params => uni.$u.post(`${tall}/project/setProjectRelation`, params); |
||||
|
// 删除某个项目
|
||||
|
uni.$u.api.delProject = projectId => uni.$u.post(`${tall}/project/deleteProject`, { projectId }); |
||||
|
} |
||||
@ -0,0 +1,20 @@ |
|||||
|
import Config from '@/common/js/config.js' |
||||
|
|
||||
|
const apiUrl = Config.apiUrl; |
||||
|
const defaultwbs = `${apiUrl}/defaultwbs`; |
||||
|
|
||||
|
export function setupTask(app) { |
||||
|
uni.$u.api = { ...uni.$u.api } || {}; |
||||
|
uni.$u.api.getGlobal = param => uni.$u.post(`${defaultwbs}/task/global`, param); |
||||
|
uni.$u.api.getPermanent = param => uni.$u.post(`${defaultwbs}/task/permanent`, param); |
||||
|
//根据时间基准点和角色查找定期任务
|
||||
|
uni.$u.api.getRegularTask = param => uni.$u.post(`${defaultwbs}/task/regular`, param); |
||||
|
//修改任务状态
|
||||
|
uni.$u.api.updateTaskType = param => uni.$u.post(`${defaultwbs}/task/type`, param); |
||||
|
//新建任务
|
||||
|
uni.$u.api.saveTask = param => uni.$u.post(`${defaultwbs}/task/save`, param); |
||||
|
//克隆任务
|
||||
|
uni.$u.api.cloneTask = param => uni.$u.post(`${defaultwbs}/task/clone`, param); |
||||
|
//模糊查询 查找项目下的任务
|
||||
|
uni.$u.api.queryTaskOfProject = param => uni.$u.post(`${defaultwbs}/task/queryTaskOfProject`, param); |
||||
|
}; |
||||
@ -0,0 +1,7 @@ |
|||||
|
import Config from "@/common/js/config.js" |
||||
|
|
||||
|
export function setupWbs(app) { |
||||
|
uni.$u.api = { ...uni.$u.api } || {}; |
||||
|
// 导入wbs
|
||||
|
uni.$u.api.import = formData => this.$upload.chooseAndUpload(`${Config.apiUrl}/wbs`, formData); |
||||
|
} |
||||
|
After Width: | Height: | Size: 8.6 KiB |
@ -0,0 +1,15 @@ |
|||||
|
var config = { |
||||
|
baseUrl: 'https://test.tall.wiki', |
||||
|
apiUrl: 'https://test.tall.wiki/gateway', |
||||
|
msgUrl: 'wss://test.tall.wiki/websocket/message/v4.0/ws', |
||||
|
projectPath: 'https://test.tall.wiki/tall-project', |
||||
|
|
||||
|
// baseUrl: 'https://www.tall.wiki',
|
||||
|
// apiUrl: 'https://www.tall.wiki/gateway',
|
||||
|
// msgUrl: 'wss://www.tall.wiki/websocket/message/v4.0/ws';
|
||||
|
// projectPath: 'https://www.tall.wiki/tall-project',
|
||||
|
|
||||
|
version: 'v4.0.0' |
||||
|
}; |
||||
|
|
||||
|
export default config; |
||||
@ -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 |
||||
|
} |
||||
File diff suppressed because it is too large
@ -0,0 +1,8 @@ |
|||||
|
// 默认主题文件 |
||||
|
.theme-default { |
||||
|
background-color: #333; |
||||
|
.u-card { |
||||
|
font-size: 24px !important; |
||||
|
color: #0f0; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,5 @@ |
|||||
|
// 整合所有主题 |
||||
|
|
||||
|
// 默认主题 |
||||
|
@import './default.scss'; |
||||
|
@import './test.scss'; |
||||
@ -0,0 +1,8 @@ |
|||||
|
// TODO: 测试用的scss主题样式 |
||||
|
.theme-test { |
||||
|
background-color: #fff; |
||||
|
.u-card { |
||||
|
font-size: 24px !important; |
||||
|
background-color: #ff0 !important; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,451 @@ |
|||||
|
<template> |
||||
|
<view class="zzx-calendar"> |
||||
|
<view class="calendar-header">{{ timeStr }}</view> |
||||
|
<!-- 星期几标题 --> |
||||
|
<view class="calendar-weeks"> |
||||
|
<view |
||||
|
class="calendar-week" |
||||
|
:class="{ 'text-red-500': week === '六' || week === '日' }" |
||||
|
v-for="(week, index) in data.weeks" |
||||
|
:key="index" |
||||
|
>{{ week }}</view> |
||||
|
</view> |
||||
|
|
||||
|
<view class="calendar-content"> |
||||
|
<swiper |
||||
|
class="calendar-swiper" |
||||
|
:style="{ |
||||
|
width: '100%', |
||||
|
height: calenderHeight, |
||||
|
}" |
||||
|
:indicator-dots="false" |
||||
|
:autoplay="false" |
||||
|
:duration="500" |
||||
|
:current="data.current" |
||||
|
@change="changeSwp" |
||||
|
:circular="true" |
||||
|
> |
||||
|
<swiper-item class="calendar-item" v-for="sItem in data.swiper" :key="sItem"> |
||||
|
<view class="calendar-days"> |
||||
|
<!-- 当前的 --> |
||||
|
<template v-if="sItem === data.current"> |
||||
|
<view |
||||
|
class="calendar-day" |
||||
|
v-for="(item, index) in data.days" |
||||
|
:key="index" |
||||
|
:class="{ 'day-hidden': !item.show }" |
||||
|
@click="clickItem(item)" |
||||
|
> |
||||
|
<view |
||||
|
class="date" |
||||
|
:class="[item.isToday ? 'is-today' : '', item.fullDate === data.selectedDate ? 'is-checked' : '']" |
||||
|
>{{ item.time.getDate() }}</view> |
||||
|
<view class="dot-show" v-if="item.info === '0'" :style="dotStyle"></view> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<template v-else> |
||||
|
<!-- 下一个月/周 --> |
||||
|
<template v-if="data.current - sItem === 1 || data.current - sItem === -2"> |
||||
|
<view |
||||
|
class="calendar-day" |
||||
|
v-for="(item, index) in preDays" |
||||
|
:key="index" |
||||
|
:class="{ |
||||
|
'day-hidden': !item.show, |
||||
|
}" |
||||
|
> |
||||
|
<view class="date" :class="[item.isToday ? 'is-today' : '']">{{ item.time.getDate() }}</view> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<!-- 上一个月/周 --> |
||||
|
<template v-else> |
||||
|
<view |
||||
|
class="calendar-day" |
||||
|
v-for="(item, index) in nextDays" |
||||
|
:key="index" |
||||
|
:class="{ |
||||
|
'day-hidden': !item.show, |
||||
|
}" |
||||
|
> |
||||
|
<view class="date" :class="[item.isToday ? 'is-today' : '']">{{ item.time.getDate() }}</view> |
||||
|
</view> |
||||
|
</template> |
||||
|
</template> |
||||
|
</view> |
||||
|
</swiper-item> |
||||
|
</swiper> |
||||
|
|
||||
|
<!-- <view class="mode-change" @click="changeMode"> |
||||
|
<view :class="weekMode ? 'mode-arrow-bottom' : 'mode-arrow-top'"> </view> |
||||
|
</view>--> |
||||
|
</view> |
||||
|
|
||||
|
<view class="flex justify-center u-font-18" style="color: #3b82f6" @click="goToday">今日</view> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script setup> |
||||
|
import { reactive, computed, watchEffect } from 'vue'; |
||||
|
import { useStore } from 'vuex'; |
||||
|
import dayjs from 'dayjs'; |
||||
|
import { generateDates, formatDate } from './generateDates.js'; |
||||
|
|
||||
|
const props = defineProps({ |
||||
|
// 打点日期的自定义样式 |
||||
|
dotStyle: { |
||||
|
type: Object, |
||||
|
default: () => ({ background: '#4ade80' }), |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
const emit = defineEmits(['handleFindPoint', 'handleFindPoint', 'days-change', 'selected-change']); |
||||
|
|
||||
|
const data = reactive({ |
||||
|
weeks: ['日', '一', '二', '三', '四', '五', '六'], // 周 |
||||
|
current: 1, |
||||
|
currentYear: '', |
||||
|
currentMonth: '', |
||||
|
currentDate: '', |
||||
|
days: [], |
||||
|
weekMode: false, // false -> 月 true -> 显示周 |
||||
|
swiper: [0, 1, 2], |
||||
|
selectedDate: formatDate(new Date(), 'yyyy-MM-dd'), // 当前选中的日期 |
||||
|
start: dayjs().startOf('month').valueOf(), |
||||
|
end: dayjs().endOf('month').valueOf(), |
||||
|
}); |
||||
|
const store = useStore(); |
||||
|
const dotList = computed(() => store.state.project.dotList); |
||||
|
const calenderHeight = computed(() => { |
||||
|
// 根据年月判断有多少行 |
||||
|
// 判断该月有多少天 |
||||
|
let h = '35px'; |
||||
|
if (!data.weekMode) { |
||||
|
const d = new Date(data.currentYear, data.currentMonth, 0); |
||||
|
const days = d.getDate(); // 判断本月有多少天 |
||||
|
const day = new Date(d.setDate(1)).getDay(); |
||||
|
// if (day === 0) { |
||||
|
// day = 7; |
||||
|
// } |
||||
|
const pre = 8 - day; |
||||
|
const rows = Math.ceil((days - pre) / 7) + 1; |
||||
|
h = `${35 * rows}px`; |
||||
|
} |
||||
|
return h; |
||||
|
}); |
||||
|
// 当前日期 年月 |
||||
|
const timeStr = computed(() => { |
||||
|
let str = ''; |
||||
|
const d = new Date(data.currentYear, data.currentMonth - 1, data.currentDate); |
||||
|
const y = d.getFullYear(); |
||||
|
const m = d.getMonth() + 1 <= 9 ? `0${d.getMonth() + 1}` : d.getMonth() + 1; |
||||
|
str = `${y}年${m}月`; |
||||
|
return str; |
||||
|
}); |
||||
|
// 上一周期的days书籍 |
||||
|
const preDays = computed(() => { |
||||
|
let pres = []; |
||||
|
if (data.weekMode) { |
||||
|
// 周模式 |
||||
|
const d = new Date(data.currentYear, data.currentMonth - 1, data.currentDate); |
||||
|
d.setDate(d.getDate() - 7); |
||||
|
pres = generateDates(d, 'week'); |
||||
|
} else { |
||||
|
// 月模式 |
||||
|
const d = new Date(data.currentYear, data.currentMonth - 2, 1); |
||||
|
pres = generateDates(d, 'month'); |
||||
|
} |
||||
|
return pres; |
||||
|
}); |
||||
|
// 下一周期的days书籍 |
||||
|
const nextDays = computed(() => { |
||||
|
let next = []; |
||||
|
if (data.weekMode) { |
||||
|
// 周模式 |
||||
|
const d = new Date(data.currentYear, data.currentMonth - 1, data.currentDate); |
||||
|
d.setDate(d.getDate() + 7); |
||||
|
next = generateDates(d, 'week'); |
||||
|
} else { |
||||
|
// 月模式 |
||||
|
const d = new Date(data.currentYear, data.currentMonth, 1); |
||||
|
next = generateDates(d, 'month'); |
||||
|
} |
||||
|
return next; |
||||
|
}); |
||||
|
|
||||
|
watchEffect(() => { |
||||
|
const days = data.days.slice(0); |
||||
|
const index = days.findIndex(day => day.show); |
||||
|
days.forEach((day, i) => { |
||||
|
dotList.value.forEach((item, j) => { |
||||
|
if (i - index === j) { |
||||
|
day.info = item; |
||||
|
} |
||||
|
}); |
||||
|
}); |
||||
|
data.days = days; |
||||
|
}); |
||||
|
|
||||
|
// 初始化日历的方法 |
||||
|
const initDate = cur => { |
||||
|
let date = ''; |
||||
|
if (cur) { |
||||
|
date = new Date(cur); |
||||
|
} else { |
||||
|
date = new Date(); |
||||
|
} |
||||
|
data.currentDate = date.getDate(); // 今日几号 |
||||
|
data.currentYear = date.getFullYear(); // 当前年份 |
||||
|
data.currentMonth = date.getMonth() + 1; // 当前月份 |
||||
|
data.currentWeek = date.getDay() === 0 ? 7 : date.getDay(); // 1...6,0 星期几 |
||||
|
// const nowY = new Date().getFullYear(); // 当前年份 |
||||
|
// const nowM = new Date().getMonth() + 1; |
||||
|
// const nowD = new Date().getDate(); // 今日日期 几号 |
||||
|
// const nowW = new Date().getDay(); |
||||
|
// this.selectedDate = formatDate(new Date(), 'yyyy-MM-dd') |
||||
|
data.days = []; |
||||
|
let days = []; |
||||
|
if (data.weekMode) { |
||||
|
days = generateDates(date, 'week'); |
||||
|
// this.selectedDate = days[0].fullDate; |
||||
|
} else { |
||||
|
days = generateDates(date, 'month'); |
||||
|
|
||||
|
// const sel = new Date(this.selectedDate.replace('-', '/').replace('-', '/')); |
||||
|
// const isMonth = sel.getFullYear() === this.currentYear && (sel.getMonth() + 1) === this.currentMonth; |
||||
|
// if(!isMonth) { |
||||
|
// this.selectedDate = formatDate(new Date(this.currentYear, this.currentMonth-1,1), 'yyyy-MM-dd') |
||||
|
// } |
||||
|
} |
||||
|
|
||||
|
// 设置小红点 |
||||
|
days.forEach((day, i) => { |
||||
|
dotList.value.forEach((item, j) => { |
||||
|
if (i === j) { |
||||
|
day.info = item; |
||||
|
} |
||||
|
}); |
||||
|
}); |
||||
|
|
||||
|
data.days = days; |
||||
|
// 派发事件,时间发生改变 |
||||
|
const obj = { |
||||
|
start: '', |
||||
|
end: '', |
||||
|
}; |
||||
|
if (data.weekMode) { |
||||
|
obj.start = data.days[0].time; |
||||
|
obj.end = data.days[6].time; |
||||
|
} else { |
||||
|
const start = new Date(data.currentYear, data.currentMonth - 1, 1); |
||||
|
const end = new Date(data.currentYear, data.currentMonth, 0); |
||||
|
obj.start = start; |
||||
|
obj.end = end; |
||||
|
} |
||||
|
emit('days-change', obj); |
||||
|
}; |
||||
|
|
||||
|
initDate(); |
||||
|
|
||||
|
// 上一个 |
||||
|
const daysPre = () => { |
||||
|
if (data.weekMode) { |
||||
|
const d = new Date(data.currentYear, data.currentMonth - 1, data.currentDate); |
||||
|
d.setDate(d.getDate() - 7); |
||||
|
initDate(d); |
||||
|
} else { |
||||
|
const d = new Date(data.currentYear, data.currentMonth - 2, 1); |
||||
|
initDate(d); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
// 下一个 |
||||
|
const daysNext = () => { |
||||
|
if (data.weekMode) { |
||||
|
const d = new Date(data.currentYear, data.currentMonth - 1, data.currentDate); |
||||
|
d.setDate(d.getDate() + 7); |
||||
|
initDate(d); |
||||
|
} else { |
||||
|
const d = new Date(data.currentYear, data.currentMonth, 1); |
||||
|
initDate(d); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
/** |
||||
|
* 滑动切换上下周期 |
||||
|
* 根据前一个减去目前的值我们可以判断是下一个月/周还是上一个月/周 |
||||
|
* current - pre === 1, -2 下一个月/周 |
||||
|
* current - pre === -1, 2 上一个月或者上一周 |
||||
|
*/ |
||||
|
const changeSwp = e => { |
||||
|
const pre = data.current; |
||||
|
const { current } = e.detail; |
||||
|
data.current = current; |
||||
|
if (current - pre === 1 || current - pre === -2) { |
||||
|
// 下一个月 或 下一周 |
||||
|
daysNext(); |
||||
|
const arr = data.days.filter(s => s.show); |
||||
|
const end = `${arr[arr.length - 1].fullDate} 23:59:59`; |
||||
|
data.start = dayjs(arr[0].fullDate).valueOf(); |
||||
|
data.end = dayjs(end).valueOf(); |
||||
|
emit('handleFindPoint', data.start, data.end); |
||||
|
} else { |
||||
|
// 上一个月 或 上一周 |
||||
|
daysPre(); |
||||
|
const arr = data.days.filter(s => s.show); |
||||
|
const end = `${arr[arr.length - 1].fullDate} 23:59:59`; |
||||
|
data.start = dayjs(arr[0].fullDate).valueOf(); |
||||
|
data.end = dayjs(end).valueOf(); |
||||
|
emit('handleFindPoint', data.start, data.end); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
// 切换模式 |
||||
|
// const changeMode = () => { |
||||
|
// const premode = this.weekMode; |
||||
|
// let isweek = false; |
||||
|
// if (premode) { |
||||
|
// isweek = !!this.days.find((item) => item.fullDate === this.selectedDate); |
||||
|
// } |
||||
|
// this.weekMode = !this.weekMode; |
||||
|
// let d = new Date(this.currentYear, this.currentMonth - 1, this.currentDate); |
||||
|
// const sel = new Date(this.selectedDate.replace('-', '/').replace('-', '/')); |
||||
|
// const isMonth = sel.getFullYear() === this.currentYear && sel.getMonth() + 1 === this.currentMonth; |
||||
|
// if ((this.selectedDate && isMonth) || isweek) { |
||||
|
// d = new Date(this.selectedDate.replace('-', '/').replace('-', '/')); |
||||
|
// } |
||||
|
// this.initDate(d); |
||||
|
// }; |
||||
|
|
||||
|
// 点击日期 |
||||
|
const clickItem = e => { |
||||
|
data.selectedDate = e.fullDate; |
||||
|
emit('selected-change', e); |
||||
|
}; |
||||
|
|
||||
|
// 返回 |
||||
|
const goToday = () => { |
||||
|
const d = new Date(); |
||||
|
initDate(d); |
||||
|
}; |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss" scoped> |
||||
|
.zzx-calendar { |
||||
|
width: 100%; |
||||
|
height: auto; |
||||
|
background-color: #fff; |
||||
|
padding-bottom: 10px; |
||||
|
|
||||
|
.calendar-header { |
||||
|
text-align: center; |
||||
|
padding: 16px 0; |
||||
|
position: relative; |
||||
|
font-size: 15px; |
||||
|
} |
||||
|
|
||||
|
.calendar-weeks { |
||||
|
width: 100%; |
||||
|
display: flex; |
||||
|
flex-flow: row nowrap; |
||||
|
margin-bottom: 10px; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
font-size: 12px; |
||||
|
color: #9ca3af; |
||||
|
font-weight: bold; |
||||
|
|
||||
|
.calendar-week { |
||||
|
width: calc(100% / 7); |
||||
|
height: 100%; |
||||
|
text-align: center; |
||||
|
} |
||||
|
} |
||||
|
swiper { |
||||
|
width: 100%; |
||||
|
height: 60upx; |
||||
|
} |
||||
|
.calendar-content { |
||||
|
min-height: 30px; |
||||
|
} |
||||
|
.calendar-swiper { |
||||
|
min-height: 35px; |
||||
|
transition: height ease-out 0.3s; |
||||
|
} |
||||
|
.calendar-item { |
||||
|
margin: 0; |
||||
|
padding: 0; |
||||
|
height: 100%; |
||||
|
} |
||||
|
.calendar-days { |
||||
|
display: flex; |
||||
|
flex-flow: row wrap; |
||||
|
width: 100%; |
||||
|
height: 100%; |
||||
|
overflow: hidden; |
||||
|
font-size: 14px; |
||||
|
|
||||
|
.calendar-day { |
||||
|
width: calc(100% / 7); |
||||
|
height: 35px; |
||||
|
text-align: center; |
||||
|
display: flex; |
||||
|
flex-flow: column nowrap; |
||||
|
justify-content: flex-start; |
||||
|
align-items: center; |
||||
|
position: relative; |
||||
|
} |
||||
|
} |
||||
|
.day-hidden { |
||||
|
visibility: hidden; |
||||
|
} |
||||
|
|
||||
|
.mode-change { |
||||
|
display: flex; |
||||
|
justify-content: center; |
||||
|
margin-top: 5px; |
||||
|
|
||||
|
.mode-arrow-top { |
||||
|
width: 0; |
||||
|
height: 0; |
||||
|
border-left: 6px solid transparent; |
||||
|
border-right: 6px solid transparent; |
||||
|
border-bottom: 5px solid #ff6633; |
||||
|
} |
||||
|
.mode-arrow-bottom { |
||||
|
width: 0; |
||||
|
height: 0; |
||||
|
border-left: 6px solid transparent; |
||||
|
border-right: 6px solid transparent; |
||||
|
border-top: 5px solid #ff6633; |
||||
|
} |
||||
|
} |
||||
|
.is-today { |
||||
|
background: #ffffff; |
||||
|
border: 1upx solid #ff6633; |
||||
|
border-radius: 50%; |
||||
|
color: #ff6633; |
||||
|
} |
||||
|
.is-checked { |
||||
|
background: #ff6633; |
||||
|
color: #ffffff; |
||||
|
} |
||||
|
.date { |
||||
|
width: 25px; |
||||
|
height: 25px; |
||||
|
line-height: 25px; |
||||
|
margin: 0 auto; |
||||
|
border-radius: 25px; |
||||
|
} |
||||
|
.dot-show { |
||||
|
width: 6px; |
||||
|
height: 6px; |
||||
|
// background: red; |
||||
|
border-radius: 5px; |
||||
|
position: absolute; |
||||
|
top: 2px; |
||||
|
right: 10px; |
||||
|
} |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,136 @@ |
|||||
|
/* |
||||
|
*此函数的作用是根据传入的一个日期,返回这一周的日期或者这一个月的日期, |
||||
|
* 如果是月的话注意还包含上个月和下个月的日期,月的话总共数据有 6 * 7 = 42个 |
||||
|
* |
||||
|
*/ |
||||
|
/* |
||||
|
* 时间格式化函数 |
||||
|
* 重要提示,微信小程序new Date('2020-04-16')在ios中无法获取时间对象 |
||||
|
* 解决方式: 建议将时间都格式化成'2020/04/16 00:00:00'的格式 |
||||
|
* 函数示例: formatDate(new Date(), 'YYYY/MM/dd hh:mm:ss') |
||||
|
*/ |
||||
|
export const formatDate = (date, fmt) => { |
||||
|
if (/(y+)/.test(fmt)) { |
||||
|
fmt = fmt.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length)); |
||||
|
} |
||||
|
let o = { |
||||
|
'M+': date.getMonth() + 1, |
||||
|
'd+': date.getDate(), |
||||
|
'h+': date.getHours(), |
||||
|
'm+': date.getMinutes(), |
||||
|
's+': date.getSeconds(), |
||||
|
}; |
||||
|
for (let k in o) { |
||||
|
if (new RegExp(`(${k})`).test(fmt)) { |
||||
|
let str = o[k] + ''; |
||||
|
fmt = fmt.replace(RegExp.$1, RegExp.$1.length === 1 ? str : padLeftZero(str)); |
||||
|
} |
||||
|
} |
||||
|
return fmt; |
||||
|
}; |
||||
|
const padLeftZero = str => { |
||||
|
return ('00' + str).substr(str.length); |
||||
|
}; |
||||
|
|
||||
|
// 判断是不是date对象
|
||||
|
export const judgeType = s => { |
||||
|
// 函数返回数据的具体类型
|
||||
|
return Object.prototype.toString.call(s).slice(8, -1); |
||||
|
}; |
||||
|
|
||||
|
export const equalDate = (d1, d2) => { |
||||
|
let result = false; |
||||
|
if (d1.getFullYear() === d2.getFullYear() && d1.getMonth() === d2.getMonth() && d1.getDate() === d2.getDate()) { |
||||
|
result = true; |
||||
|
} |
||||
|
return result; |
||||
|
}; |
||||
|
|
||||
|
/* 比较时间,时间格式为2020-04-04 |
||||
|
*/ |
||||
|
export const dateEqual = (before, after) => { |
||||
|
before = new Date(before.replace('-', '/').replace('-', '/')); |
||||
|
after = new Date(after.replace('-', '/').replace('-', '/')); |
||||
|
if (before.getTime() - after.getTime() === 0) { |
||||
|
return true; |
||||
|
} else { |
||||
|
return false; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
export const generateDates = (date = new Date(), type = 'week') => { |
||||
|
const result = []; |
||||
|
if (judgeType(date) === 'Date') { |
||||
|
// 年,月,日
|
||||
|
const y = date.getFullYear(); |
||||
|
const m = date.getMonth(); |
||||
|
const d = date.getDate(); |
||||
|
const days = new Date(y, m + 1, 0).getDate(); |
||||
|
// 获取日期是星期几
|
||||
|
// let weekIndex = date.getDay() === 0 ? 7 : date.getDay();
|
||||
|
let weekIndex = date.getDay(); |
||||
|
if (type === 'month') { |
||||
|
const dobj = new Date(y, m, 1); |
||||
|
// weekIndex = dobj.getDay() === 0 ? 7 : dobj.getDay();
|
||||
|
weekIndex = dobj.getDay(); |
||||
|
} |
||||
|
if (type === 'week') { |
||||
|
for (let i = weekIndex; i > 0; i--) { |
||||
|
const dtemp = new Date(y, m, d); |
||||
|
dtemp.setDate(dtemp.getDate() - i); |
||||
|
result.push({ |
||||
|
time: dtemp, |
||||
|
show: true, |
||||
|
fullDate: formatDate(dtemp, 'yyyy-MM-dd'), |
||||
|
isToday: equalDate(new Date(), dtemp), |
||||
|
}); |
||||
|
} |
||||
|
for (let i = 0; i <= 7 - weekIndex; i++) { |
||||
|
const dtemp = new Date(y, m, d); |
||||
|
dtemp.setDate(dtemp.getDate() + i); |
||||
|
result.push({ |
||||
|
time: dtemp, |
||||
|
show: true, |
||||
|
fullDate: formatDate(dtemp, 'yyyy-MM-dd'), |
||||
|
isToday: equalDate(new Date(), dtemp), |
||||
|
}); |
||||
|
} |
||||
|
} else if (type === 'month') { |
||||
|
// 上个月
|
||||
|
for (let i = weekIndex; i > 0; i--) { |
||||
|
const dtemp = new Date(y, m, 1); |
||||
|
dtemp.setDate(dtemp.getDate() - i); |
||||
|
result.push({ |
||||
|
time: dtemp, |
||||
|
show: false, |
||||
|
fullDate: formatDate(dtemp, 'yyyy-MM-dd'), |
||||
|
isToday: equalDate(new Date(), dtemp), |
||||
|
}); |
||||
|
} |
||||
|
// 这个月的日期
|
||||
|
for (let i = 0; i < days; i++) { |
||||
|
const dtemp = new Date(y, m, 1); |
||||
|
dtemp.setDate(dtemp.getDate() + i); |
||||
|
result.push({ |
||||
|
time: dtemp, |
||||
|
show: true, |
||||
|
fullDate: formatDate(dtemp, 'yyyy-MM-dd'), |
||||
|
isToday: equalDate(new Date(), dtemp), |
||||
|
}); |
||||
|
} |
||||
|
const len = 42 - result.length; |
||||
|
// 下个月的日期
|
||||
|
for (let i = 1; i <= len; i++) { |
||||
|
const dtemp = new Date(y, m + 1, 0); |
||||
|
dtemp.setDate(dtemp.getDate() + i); |
||||
|
result.push({ |
||||
|
time: dtemp, |
||||
|
show: false, |
||||
|
fullDate: formatDate(dtemp, 'yyyy-MM-dd'), |
||||
|
isToday: equalDate(new Date(), dtemp), |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
return result; |
||||
|
}; |
||||
@ -1,8 +1,86 @@ |
|||||
<template> |
<template> |
||||
|
<theme class="m-2" > |
||||
|
<u-card |
||||
|
@click="openCard" |
||||
|
:show-foot="false" |
||||
|
:show-head="false" |
||||
|
:style="{ 'max-height': globalsHeight + 'px' }" |
||||
|
border-radius="25" |
||||
|
margin="0" |
||||
|
class="global-container" |
||||
|
> |
||||
|
<template v-slot:body> |
||||
|
<scroll-view :scrollY="true" :style="{ 'max-height': globalsHeight - 30 + 'px' }"> |
||||
|
<skeleton :banner="false" :loading="!globals.length" :row="3" animate class="u-line-2 skeleton"></skeleton> |
||||
|
|
||||
|
<view class="grid gap-2"> |
||||
|
<view v-for="item in globals" :key="item.id"> |
||||
|
<template v-if="item.plugins && item.plugins.length"> |
||||
|
<view v-for="(pluginArr, i) in item.plugins" :key="i"> |
||||
|
<template class="p-0 u-col-between" v-if="pluginArr.length"> |
||||
|
<Plugin |
||||
|
:class="[`row-span-${plugin.row}`, `col-span-${plugin.col}`]" |
||||
|
:task="item" |
||||
|
:key="plugin.pluginTaskId" |
||||
|
:plugin-task-id="plugin.pluginTaskId" |
||||
|
:plugin-id="plugin.pluginId" |
||||
|
:param="plugin.param" |
||||
|
:style-type="plugin.styleType || 0" |
||||
|
v-for="plugin in pluginArr" |
||||
|
/> |
||||
|
</template> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<!-- 任务名插件 --> |
||||
|
<p-task-title :task="item" v-else /> |
||||
|
<!-- 交付物插件 --> |
||||
|
<p-deliver></p-deliver> |
||||
|
</view> |
||||
|
</view> |
||||
|
</scroll-view> |
||||
|
</template> |
||||
|
</u-card> |
||||
|
</theme> |
||||
</template> |
</template> |
||||
|
|
||||
<script> |
<script setup> |
||||
|
import { computed } from 'vue'; |
||||
|
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 globals = computed(() => store.getters['task/globals']); |
||||
|
console.log('globals: ', globals.value); |
||||
|
|
||||
|
// 手动展开日常任务 |
||||
|
function openCard() { |
||||
|
if (isShrink.value) { |
||||
|
store.commit('task/setShrink', false); |
||||
|
} |
||||
|
} |
||||
</script> |
</script> |
||||
|
|
||||
<style> |
<style scoped lang="scss"> |
||||
|
.u-card-wrap { |
||||
|
background-color: $u-bg-color; |
||||
|
padding: 1px; |
||||
|
} |
||||
|
|
||||
|
.u-body-item { |
||||
|
font-size: 32rpx; |
||||
|
color: #333; |
||||
|
padding: 20rpx 10rpx; |
||||
|
} |
||||
|
|
||||
|
.u-body-item image { |
||||
|
width: 120rpx; |
||||
|
flex: 0 0 120rpx; |
||||
|
height: 120rpx; |
||||
|
border-radius: 8rpx; |
||||
|
margin-left: 12rpx; |
||||
|
} |
||||
</style> |
</style> |
||||
|
|||||
@ -0,0 +1,139 @@ |
|||||
|
<template> |
||||
|
<view class="u-font-14" style="height: 100%"> |
||||
|
插件面板 |
||||
|
<!-- <view v-if="data.pluginContent" @click="setStorage"> |
||||
|
<view |
||||
|
:data-did="task.detailId" |
||||
|
:data-param="param" |
||||
|
:data-pdu="task.planDuration" |
||||
|
:data-pid="projectId" |
||||
|
:data-pstart="task.planStart" |
||||
|
:data-rdu="task.realDuration" |
||||
|
:data-rid="roleId" |
||||
|
:data-tid="task.id" |
||||
|
:data-tname="task.name" |
||||
|
:data-token="token" |
||||
|
:data-rstart="task.realStart" |
||||
|
:data-uid="userId" |
||||
|
style="height: 100%" |
||||
|
v-html="data.pluginContent" |
||||
|
></view> |
||||
|
</view> --> |
||||
|
|
||||
|
<!-- <view v-else @click="setStorage"> --> |
||||
|
<!-- <plugin-default /> --> |
||||
|
<!-- <component :task="task" :is="pluginComponent"></component> --> |
||||
|
<!-- <p-task-title :task="task" v-if="pluginId === '1'" /> |
||||
|
<p-task-description :task="task" v-if="pluginId === '2'" /> |
||||
|
<p-task-duration-delay :task="task" v-if="pluginId === '3'" /> |
||||
|
<p-task-start-time-delay :task="task" v-if="pluginId === '4'" /> |
||||
|
<p-upload-deliverable :task="task" v-if="pluginId === '5' && isMine" /> |
||||
|
<p-delivery-history :task="task" v-if="pluginId === '5' && !isMine" /> |
||||
|
<p-subtasks :task="task" v-if="pluginId === '6'" /> |
||||
|
<p-subproject :task="task" v-if="pluginId === '7'" /> |
||||
|
<p-task-countdown :task="task" v-if="pluginId === '8'" /> |
||||
|
<p-manage-project :task="task" v-if="pluginId === '9'" /> |
||||
|
<p-manage-role :task="task" v-if="pluginId === '10'" /> |
||||
|
<p-manage-member :task="task" v-if="pluginId === '11'" /> |
||||
|
<p-manage-task :task="task" v-if="pluginId === '12'" /> |
||||
|
<p-wbs-import :task="task" v-if="pluginId === '13' || pluginId === '14'" /> |
||||
|
<p-deliver-check :task="task" v-if="pluginId === '15'" /> --> |
||||
|
<!-- </view> --> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script setup> |
||||
|
import { reactive, nextTick, computed } from 'vue'; |
||||
|
import { useStore } from 'vuex'; |
||||
|
|
||||
|
const props = defineProps({ |
||||
|
task: { default: () => {}, type: Object }, |
||||
|
pluginId: { default: '1', type: String }, |
||||
|
styleType: { default: 0, type: Number }, |
||||
|
pluginTaskId: { default: '', type: String }, |
||||
|
param: { type: String, default: '' }, |
||||
|
}); |
||||
|
|
||||
|
// 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 projectId = computed(() => store.getters['project/projectId']); |
||||
|
// const isMine = computed(() => store.getters['role/isMine']); |
||||
|
|
||||
|
// 插件名称 |
||||
|
// pluginComponent() { |
||||
|
// const target = this.$t.plugin.defaults.find(item => item.id === +this.pluginId); |
||||
|
// if (!target) return ''; |
||||
|
// return target.component; |
||||
|
// }, |
||||
|
|
||||
|
// 创建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); |
||||
|
// }); |
||||
|
// } |
||||
|
|
||||
|
// 获取插件信息 |
||||
|
// 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; |
||||
|
// } |
||||
|
|
||||
|
// const str = data.js.replace(new RegExp(uuid, 'g'), `p${props.pluginTaskId}`); |
||||
|
// handleDom(str); |
||||
|
// } |
||||
|
// } |
||||
|
// }); |
||||
|
|
||||
|
// if (data.js) { |
||||
|
// if (reg.test(data.js)) { |
||||
|
// const uuid = RegExp.$1; |
||||
|
// const str = data.js.replace(new RegExp(uuid, 'g'), `p${this.pluginTaskId}`); |
||||
|
// this.handleDom(str); |
||||
|
// } else { |
||||
|
// this.handleDom(data.js); |
||||
|
// } |
||||
|
// } |
||||
|
// } |
||||
|
|
||||
|
console.log('props.pluginId: ', props.pluginId); |
||||
|
if (props.pluginId === '5') { |
||||
|
// 根据项目id获取成员列表 |
||||
|
store.dispatch('role/getAllMembers', { projectId: projectId.value }); |
||||
|
} |
||||
|
// getPlugin(); |
||||
|
|
||||
|
// 点击时存储 storage |
||||
|
async function setStorage() { |
||||
|
uni.$storage.setStorageSync('roleId', roleId.value); |
||||
|
} |
||||
|
</script> |
||||
@ -0,0 +1,480 @@ |
|||||
|
<template> |
||||
|
<view> |
||||
|
<scroll-view scroll-y="true"> |
||||
|
<view v-if="!data.changeEvent"> |
||||
|
<view :id="'cu-' + index" :key="item.id" class="cu-item flex-col" v-for="(item, index) in data.itemList"> |
||||
|
<ProjectItem |
||||
|
class="w-full" |
||||
|
:index="index" |
||||
|
:item="item" |
||||
|
:menuList="data.menuList" |
||||
|
@setData="setData" |
||||
|
@openSubProject="openSubProject" |
||||
|
/> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<view v-else> |
||||
|
<view |
||||
|
:id="'cu-' + index" |
||||
|
:key="index" |
||||
|
:style="{ 'background-color': item.color }" |
||||
|
@touchend="stops($event, index)" |
||||
|
@touchmove.stop.prevent="move" |
||||
|
@touchstart="start($event, index)" |
||||
|
class="cu-item flex-col" v-for="(item, index) in data.itemList" |
||||
|
> |
||||
|
<view class="border-100 bg-blue-500" v-if="item.showTopBorder"></view> |
||||
|
|
||||
|
<!-- 内容区 --> |
||||
|
<!-- 父项目 --> |
||||
|
<view class="w-full"> |
||||
|
<view class="flex items-center justify-between p-3"> |
||||
|
<u-icon class="mover" name="https://www.tall.wiki/staticrec/drag.svg" size="48"></u-icon> |
||||
|
|
||||
|
<view class="flex-1 px-3"> |
||||
|
<view class="flex items-center mb-1"> |
||||
|
<view class="mr-2">{{ item.name }}</view> |
||||
|
<!-- 状态 TODO:--> |
||||
|
<view class="px-2 text-xs text-green-400 bg-green-100 rounded-full flex-shrink-0">进行中</view> |
||||
|
</view> |
||||
|
|
||||
|
<view class="flex items-center text-xs text-gray-400"> |
||||
|
<view class="pr-2">{{ dayjs(+item.startTime).format('MM-DD HH:mm') }}</view> |
||||
|
至 |
||||
|
<view class="pl-2">{{ dayjs(+item.endTime).format('MM-DD HH:mm') }}</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<!-- 箭头 --> |
||||
|
<view v-if="item.sonProjectList && item.sonProjectList.length"> |
||||
|
<u-icon @click="openSubProject(item.sonProjectList.length, index)" class="text-gray-400" name="arrow-up" size="14px" v-if="item.show"></u-icon> |
||||
|
<u-icon @click="openSubProject(item.sonProjectList.length, index)" class="text-gray-400" name="arrow-down" size="14px" v-else></u-icon> |
||||
|
</view> |
||||
|
<u-icon class="text-gray-400" name="arrow-right" size="14px" v-else></u-icon> |
||||
|
</view> |
||||
|
<!-- 父项目 end --> |
||||
|
|
||||
|
<!-- 子项目 --> |
||||
|
<view class="ml-8" v-if="item.show"> |
||||
|
<view :id="'cu-' + index + '-' + subIndex" :key="subIndex" |
||||
|
@touchend.stop.prevent="stops($event, index + '-' + subIndex, item.sonProjectList.length)" |
||||
|
@touchmove.stop.prevent="move($event, item.sonProjectList.length)" |
||||
|
@touchstart.stop.prevent="start($event, index + '-' + subIndex)" |
||||
|
class="cu-item flex-col" v-for="(subItem, subIndex) in item.sonProjectList"> |
||||
|
<view class="flex items-center justify-between p-3 w-full"> |
||||
|
<u-icon class="mover" name="https://www.tall.wiki/staticrec/drag.svg" size="48"> |
||||
|
</u-icon> |
||||
|
|
||||
|
<view class="flex-1 px-3"> |
||||
|
<view class="flex items-center"> |
||||
|
<view class="mr-2">{{ subItem.name }}</view> |
||||
|
<!-- 状态 --> |
||||
|
<view |
||||
|
:class=" |
||||
|
subItem.status === 0 |
||||
|
? 'text-blue-400 bg-blue-100' |
||||
|
: subItem.status === 1 |
||||
|
? 'text-green-400 bg-green-100' |
||||
|
: subItem.status === 2 |
||||
|
? 'text-red-400 bg-red-100' |
||||
|
: 'text-gray-400 bg-gray-100' |
||||
|
" |
||||
|
class="px-2 text-xs text-gray-400 bg-gray-100 rounded-full flex-shrink-0"> |
||||
|
{{ subItem.status === 0 ? '未开始' : subItem.status === 1 ? '进行中' : subItem.status === 2 ? '暂停' : '已完成' }} |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<!-- 箭头 --> |
||||
|
<u-icon class="text-gray-400" name="arrow-right" size="14px"></u-icon> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
<!-- 内容区 end --> |
||||
|
|
||||
|
<view class="border-100 bg-blue-500" v-if="item.showBorder"></view> |
||||
|
<view class="border-80 bg-blue-500" v-if="item.showSubBorder"></view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</scroll-view> |
||||
|
|
||||
|
<!-- 移动悬浮 begin --> |
||||
|
<view v-if="data.showMoveImage"> |
||||
|
<view :style="{ left: moveLeft + 'px', top: moveTop + 'px' }" class="cu-item absolute"> |
||||
|
<ProjectItem class="w-full" :item="moveItem" /> |
||||
|
</view> |
||||
|
</view> |
||||
|
<!-- 移动悬浮 end --> |
||||
|
|
||||
|
<!-- 项目操作面板 --> |
||||
|
<u-action-sheet :list="data.menuList" :tips="data.tips" @click="chooseAction" v-model="data.showMenu"></u-action-sheet> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script setup> |
||||
|
import { ref, onMounted, watch, computed } from 'vue'; |
||||
|
import ProjectItem from '@/components/Projects/ProjectItem.vue'; |
||||
|
import { useStore } from 'vuex'; |
||||
|
import dayjs from 'dayjs'; |
||||
|
|
||||
|
const store = useStore(); |
||||
|
const projects = computed(() => store.state.project.projects); |
||||
|
const data = ref({ |
||||
|
itemTop: 0, |
||||
|
itemLeft: 0, |
||||
|
itemHeight: 0, // 移动元素的高度 |
||||
|
subItemHeight: 0, // 移动子元素的高度 |
||||
|
itemWidth: 0, // 移动元素的宽度 |
||||
|
showMoveImage: false, |
||||
|
moveItem: '', |
||||
|
moveLeft: 0, |
||||
|
moveTop: 0, |
||||
|
deltaLeft: 0, |
||||
|
deltaTop: 0, |
||||
|
beginleft: 0, |
||||
|
begintop: 0, |
||||
|
itemList: [], |
||||
|
setSubItem: false, |
||||
|
changeEvent: false, |
||||
|
|
||||
|
showMenu: false, |
||||
|
tips: { |
||||
|
text: '', |
||||
|
color: '#909399', |
||||
|
fontSize: 28, |
||||
|
}, |
||||
|
projectId: 0, |
||||
|
menuList: [{ text: '复制' }, { text: '编辑' }, { text: '删除' }, { text: '置顶' }, { text: '排序' }], |
||||
|
// show: false, |
||||
|
border: 'border border-blue-500 shadow rounded-md', |
||||
|
showBorder: false, |
||||
|
showItemIndex: undefined, |
||||
|
}); |
||||
|
|
||||
|
watch(projects, (val) => { |
||||
|
data.value.itemList = val; |
||||
|
data.value.itemList.forEach(item => { |
||||
|
item.showBorder = false; |
||||
|
item.showSubBorder = false; |
||||
|
item.showTopBorder = false; |
||||
|
}); |
||||
|
}) |
||||
|
|
||||
|
onMounted(() => { |
||||
|
data.value.itemList = projects.value; |
||||
|
data.value.itemList.forEach(item => { |
||||
|
item.showBorder = false; |
||||
|
item.showSubBorder = false; |
||||
|
item.showTopBorder = false; |
||||
|
}); |
||||
|
}); |
||||
|
|
||||
|
// 展开子项目 |
||||
|
function openSubProject(length, index) { |
||||
|
setProjectItemShow({ index, show: data.value.itemList[index].show ? false : true }); |
||||
|
if (length && index) { |
||||
|
this.$emit('changeHeight', length, index); |
||||
|
} |
||||
|
data.value.showItemIndex = index; |
||||
|
} |
||||
|
|
||||
|
// 获取项目列表距离顶部的距离 |
||||
|
function getDate() { |
||||
|
const query = uni.createSelectorQuery().in(this); |
||||
|
query.select(`#cu-0`).boundingClientRect(res => { |
||||
|
console.log('data: ', res); |
||||
|
data.value.begintop = res.top; |
||||
|
data.value.beginleft = res.left; |
||||
|
}).exec(); |
||||
|
} |
||||
|
|
||||
|
function setData(flag, projectId, tips) { |
||||
|
data.value.showMenu = flag; |
||||
|
data.value.projectId = projectId; |
||||
|
data.value.tips = tips; |
||||
|
} |
||||
|
|
||||
|
function chooseAction(e) { |
||||
|
let obj = { index: e, projectId: data.value.projectId }; |
||||
|
// this.$emit('chooseAction', data); |
||||
|
actionFun(obj); |
||||
|
} |
||||
|
|
||||
|
// 操作 |
||||
|
function actionFun(obj) { |
||||
|
let action = data.value.menuList[obj.index].text; |
||||
|
if (action === '排序') { |
||||
|
data.value.changeEvent = true; |
||||
|
uni.$ui.showToast('请移动进行排序'); |
||||
|
} |
||||
|
|
||||
|
if (action === '删除') { |
||||
|
data.value.changeEvent = false; |
||||
|
delProject(obj.projectId); |
||||
|
} |
||||
|
|
||||
|
if (data.value.showItemIndex !== undefined) { |
||||
|
setProjectItemShow({ index: data.value.showItemIndex, show: true }); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function isNumber(val) { |
||||
|
return val === +val; |
||||
|
} |
||||
|
|
||||
|
function start(e, index) { |
||||
|
console.log('开始', e); |
||||
|
setTimeout(() => { |
||||
|
getDate(); |
||||
|
}, 300); |
||||
|
|
||||
|
if (isNumber(index)) { |
||||
|
data.value.setSubItem = false; |
||||
|
const query = uni.createSelectorQuery().in(this); |
||||
|
console.log('2222', query) |
||||
|
query.select(`#cu-${index}`).boundingClientRect(res => { |
||||
|
data.value.moveTop = res.top; |
||||
|
data.value.moveLeft = res.left; |
||||
|
data.value.moveItem = data.value.itemList[index]; |
||||
|
data.value.itemWidth = res.width; |
||||
|
data.value.itemHeight = res.height; |
||||
|
}).exec(); |
||||
|
} else { |
||||
|
let arr = index.split('-'); |
||||
|
data.value.setSubItem = true; |
||||
|
const query = uni.createSelectorQuery().in(this); |
||||
|
query.select(`#cu-${arr[0] - 0}`).boundingClientRect(res => { |
||||
|
data.value.itemHeight = res.height; |
||||
|
}).exec(); |
||||
|
|
||||
|
query.select(`#cu-${index}`).boundingClientRect(res => { |
||||
|
data.value.moveTop = res.top; |
||||
|
data.value.moveLeft = res.left; |
||||
|
data.value.moveItem = data.value.itemList[arr[0] - 0].sonProjectList[arr[1] - 0]; |
||||
|
data.value.itemWidth = res.width; |
||||
|
data.value.subItemHeight = res.height; |
||||
|
}).exec(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function move(e, length) { |
||||
|
console.log('移动'); |
||||
|
data.value.showMoveImage = true; //悬浮开始 |
||||
|
const touch = e.touches[0]; |
||||
|
if (data.value.deltaLeft == 0) { |
||||
|
// 获得本身的移动 |
||||
|
data.value.deltaLeft = touch.pageX - data.value.moveLeft; |
||||
|
data.value.deltaTop = touch.pageY - data.value.moveTop; |
||||
|
} |
||||
|
data.value.moveLeft = touch.pageX - data.value.deltaLeft; |
||||
|
data.value.moveTop = touch.pageY - data.value.deltaTop; |
||||
|
|
||||
|
let lastIndex = (lastIndex = findOverIndex(touch.pageY, length)); |
||||
|
console.log('111111', lastIndex); |
||||
|
// 显示下划线 |
||||
|
for (let i = 0; i < data.value.itemList.length; i++) { |
||||
|
if (data.value.moveLeft > 35) { |
||||
|
data.value.itemList[i].showBorder = false; |
||||
|
data.value.itemList[i].showTopBorder = false; |
||||
|
if (i === lastIndex) { |
||||
|
data.value.itemList[i].showSubBorder = true; |
||||
|
} else { |
||||
|
data.value.itemList[i].showSubBorder = false; |
||||
|
} |
||||
|
} else { |
||||
|
if (lastIndex === -1) { |
||||
|
data.value.itemList[0].showTopBorder = true; |
||||
|
data.value.itemList[i].showSubBorder = false; |
||||
|
data.value.itemList[i].showBorder = false; |
||||
|
} else { |
||||
|
data.value.itemList[i].showSubBorder = false; |
||||
|
data.value.itemList[i].showTopBorder = false; |
||||
|
if (i === lastIndex) { |
||||
|
data.value.itemList[i].showBorder = true; |
||||
|
} else { |
||||
|
data.value.itemList[i].showBorder = false; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function stops(e, index, length) { |
||||
|
console.log('结束'); |
||||
|
const touch = e.mp.changedTouches[0]; |
||||
|
let lastIndex = (lastIndex = findOverIndex(touch.pageY, length)); |
||||
|
|
||||
|
// 交换两个值 |
||||
|
for (let i = 0; i < data.value.itemList.length; i++) { |
||||
|
// 插入顶部 |
||||
|
if (data.value.itemList[i].showTopBorder) { |
||||
|
if (isNumber(index)) { |
||||
|
let Value = data.value.itemList[index]; |
||||
|
data.value.itemList.unshift(Value); |
||||
|
data.value.itemList.splice(index + 1, 1); |
||||
|
} else { |
||||
|
let arr = index.split('-'); |
||||
|
let Value = data.value.itemList[arr[0] - 0].sonProjectList[arr[1] - 0]; |
||||
|
data.value.itemList.unshift(Value); |
||||
|
data.value.itemList[arr[0] - 0].sonProjectList.splice([arr[1] - 0], 1); |
||||
|
const options = { |
||||
|
id: Value.id, |
||||
|
parentId: 0, |
||||
|
}; |
||||
|
this.$emit('change', options); |
||||
|
} |
||||
|
// 清空 |
||||
|
clearSet(i); |
||||
|
this.$emit('change', data.value.itemList); |
||||
|
return; |
||||
|
} |
||||
|
// 插入一级项目 |
||||
|
if (data.value.itemList[i].showBorder) { |
||||
|
if (isNumber(index)) { |
||||
|
let Value = data.value.itemList[index]; |
||||
|
data.value.itemList.splice(i + 1, 0, Value); |
||||
|
if (i < index) { |
||||
|
data.value.itemList.splice(index + 1, 1); |
||||
|
} else { |
||||
|
data.value.itemList.splice(index, 1); |
||||
|
} |
||||
|
} else { |
||||
|
let arr = index.split('-'); |
||||
|
let Value = data.value.itemList[arr[0] - 0].sonProjectList[arr[1] - 0]; |
||||
|
data.value.itemList.splice(i + 1, 0, Value); |
||||
|
data.value.itemList[arr[0] - 0].sonProjectList.splice([arr[1] - 0], 1); |
||||
|
const options = { |
||||
|
id: Value.id, |
||||
|
parentId: 0, |
||||
|
}; |
||||
|
this.$emit('change', options); |
||||
|
} |
||||
|
// 清空 |
||||
|
clearSet(i); |
||||
|
this.$emit('change', data.value.itemList); |
||||
|
return; |
||||
|
} |
||||
|
// 插入二级项目 |
||||
|
if (data.value.itemList[i].showSubBorder) { |
||||
|
if (isNumber(index)) { |
||||
|
let Value = data.value.itemList[index]; |
||||
|
if (data.value.itemList[lastIndex - 1].sonProjectList && data.value.itemList[lastIndex - 1].sonProjectList.length) { |
||||
|
data.value.itemList[lastIndex - 1].sonProjectList.push(Value); |
||||
|
} else { |
||||
|
data.value.itemList[lastIndex].sonProjectList = [Value]; |
||||
|
} |
||||
|
data.value.itemList.splice(index, 1); |
||||
|
// 清空 |
||||
|
clearSet(i); |
||||
|
const options = { |
||||
|
id: Value.id, |
||||
|
parentId: data.value.itemList[lastIndex - 1].id, |
||||
|
}; |
||||
|
this.$emit('change', options); |
||||
|
} else { |
||||
|
let arr = index.split('-'); |
||||
|
let Value = data.value.itemList[arr[0] - 0].sonProjectList[arr[1] - 0]; |
||||
|
if (data.value.itemList[lastIndex].sonProjectList && data.value.itemList[lastIndex].sonProjectList.length) { |
||||
|
data.value.itemList[lastIndex].sonProjectList.push(Value); |
||||
|
} else { |
||||
|
data.value.itemList[lastIndex].sonProjectList = [Value]; |
||||
|
} |
||||
|
data.value.itemList[arr[0] - 0].sonProjectList.splice([arr[1] - 0], 1); |
||||
|
// 清空 |
||||
|
clearSet(i); |
||||
|
const options = { |
||||
|
id: Value.id, |
||||
|
parentId: data.value.itemList[lastIndex].id, |
||||
|
}; |
||||
|
this.$emit('change', options); |
||||
|
|
||||
|
const options1 = { |
||||
|
id: Value.id, |
||||
|
parentId: 0, |
||||
|
}; |
||||
|
this.$emit('change', options1); |
||||
|
} |
||||
|
return; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 还原初始数据 |
||||
|
function clearSet(i) { |
||||
|
data.value.itemList[i].showBorder = false; |
||||
|
data.value.itemList[i].showSubBorder = false; |
||||
|
data.value.itemList[i].showTopBorder = false; |
||||
|
data.value.deltaLeft == 0; |
||||
|
data.value.showMoveImage = false; |
||||
|
data.value.setSubItem = false; |
||||
|
data.value.changeEvent = false; |
||||
|
data.value.showItemIndex = undefined; |
||||
|
} |
||||
|
|
||||
|
// 找到停下的元素的下标 |
||||
|
function findOverIndex(posY) { |
||||
|
// 如果有子项目展开着 |
||||
|
let leng = data.value.itemList.length * data.value.itemHeight; // 最后一个元素距离顶部的距离 |
||||
|
if (posY < data.value.begintop) { |
||||
|
return -1; |
||||
|
} |
||||
|
for (var i = 0; i < data.value.itemList.length; i++) { |
||||
|
let begin = data.value.itemHeight * i + data.value.begintop; |
||||
|
let end = data.value.itemHeight * i + data.value.begintop + data.value.itemHeight; |
||||
|
if (begin <= posY && end >= posY) { |
||||
|
return i; |
||||
|
} |
||||
|
} |
||||
|
if (posY > leng) { |
||||
|
// 交换最后一个 |
||||
|
return data.value.itemList.length - 1; |
||||
|
} else if (posY < data.value.begintop) { |
||||
|
return 0; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 删除项目 |
||||
|
function delProject(id) { |
||||
|
uni.showModal({ |
||||
|
title: '', |
||||
|
content: '是否删除项目?', |
||||
|
showCancel: true, |
||||
|
success: async ({ confirm }) => { |
||||
|
if (confirm) { |
||||
|
await this.$u.api.delProject(id); |
||||
|
let flag_index = 0; |
||||
|
data.value.itemList.forEach((item, index) => { |
||||
|
if (item.id == id) { |
||||
|
flag_index = index; |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
data.value.itemList.splice(flag_index, 1); |
||||
|
setProjects(data.value.itemList); |
||||
|
} |
||||
|
}, |
||||
|
}); |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss" scoped> |
||||
|
.cu-item { |
||||
|
width: 100%; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
font-size: 14px; |
||||
|
} |
||||
|
|
||||
|
.border-100 { |
||||
|
width: 92%; |
||||
|
height: 4rpx; |
||||
|
} |
||||
|
|
||||
|
.border-80 { |
||||
|
width: 84%; |
||||
|
height: 2px; |
||||
|
margin-left: 30px; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,134 @@ |
|||||
|
<template> |
||||
|
<view class="w-full"> |
||||
|
<!-- 有子项目 --> |
||||
|
<view class="flex items-center justify-between p-3"> |
||||
|
<u-icon @click="openMenu(item)" class="mover" name="https://www.tall.wiki/staticrec/drag.svg" size="48"></u-icon> |
||||
|
|
||||
|
<view @click="openProject(item)" class="flex-1 px-3"> |
||||
|
<view class="flex items-center mb-1"> |
||||
|
<view class="mr-2">{{ item.name }}</view> |
||||
|
<!-- 状态 TODO:--> |
||||
|
<view class="px-2 text-xs text-green-400 bg-green-100 rounded-full flex-shrink-0">进行中</view> |
||||
|
</view> |
||||
|
|
||||
|
<view class="flex items-center text-xs text-gray-400"> |
||||
|
<view class="pr-2">{{ dayjs(item.startTime).format('MM-DD HH:mm') }}</view> |
||||
|
至 |
||||
|
<view class="pl-2">{{ dayjs(item.endTime).format('MM-DD HH:mm') }}</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<!-- 箭头 --> |
||||
|
<view v-if="item.sonProjectList && item.sonProjectList.length"> |
||||
|
<u-icon @click="$emit('openSubProject', item.sonProjectList.length, index)" class="text-gray-400" name="arrow-up" size="14px" v-if="item.show"></u-icon> |
||||
|
<u-icon @click="$emit('openSubProject', item.sonProjectList.length, index)" class="text-gray-400" name="arrow-down" size="14px" v-else></u-icon> |
||||
|
</view> |
||||
|
<u-icon @click="openProject(item)" class="text-gray-400" name="arrow-right" size="14px" v-else></u-icon> |
||||
|
</view> |
||||
|
|
||||
|
<!-- 有子项目 --> |
||||
|
<view class="ml-8" v-if="item.show"> |
||||
|
<view :id="'cu-' + index + '-' + subIndex" :key="subIndex" class="cu-item flex-col" v-for="(subItem, subIndex) in item.sonProjectList"> |
||||
|
<!-- <view :key="subItem.id" v-for="subItem in item.sonProjectList"> --> |
||||
|
<view class="flex items-center justify-between p-3"> |
||||
|
<u-icon @click="openMenu(subItem)" class="mover" name="https://www.tall.wiki/staticrec/drag.svg" size="48"></u-icon> |
||||
|
|
||||
|
<view @click="openProject(subItem)" class="flex-1 px-3"> |
||||
|
<view class="flex items-center"> |
||||
|
<view class="mr-2">{{ subItem.name }}</view> |
||||
|
<!-- 状态 --> |
||||
|
<view |
||||
|
:class=" |
||||
|
subItem.status === 0 |
||||
|
? 'text-blue-400 bg-blue-100' |
||||
|
: subItem.status === 1 |
||||
|
? 'text-green-400 bg-green-100' |
||||
|
: subItem.status === 2 |
||||
|
? 'text-red-400 bg-red-100' |
||||
|
: 'text-gray-400 bg-gray-100' |
||||
|
" |
||||
|
class="px-2 text-xs text-gray-400 bg-gray-100 rounded-full flex-shrink-0" |
||||
|
> |
||||
|
{{ subItem.status === 0 ? '未开始' : subItem.status === 1 ? '进行中' : subItem.status === 2 ? '暂停' : '已完成' }} |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<!-- 箭头 --> |
||||
|
<u-icon @click="openProject(subItem)" class="text-gray-400" name="arrow-right" size="14px"></u-icon> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script setup> |
||||
|
import { ref, computed } from 'vue'; |
||||
|
import dayjs from 'dayjs'; |
||||
|
import { useStore } from 'vuex'; |
||||
|
import config from '@/common/js/config.js'; |
||||
|
|
||||
|
defineProps({ |
||||
|
item: { |
||||
|
type: Object, |
||||
|
default: () => {}, |
||||
|
}, |
||||
|
index: { |
||||
|
type: Number, |
||||
|
default: 0, |
||||
|
}, |
||||
|
// menuList: { |
||||
|
// type: Array, |
||||
|
// default: () => [], |
||||
|
// }, |
||||
|
}); |
||||
|
const emit = defineEmits(['setData']); |
||||
|
|
||||
|
const store = useStore(); |
||||
|
const userId = computed(() => store.getters['user/userId']); |
||||
|
|
||||
|
const data = ref({ |
||||
|
showMenu: false, |
||||
|
tips: { |
||||
|
text: '', |
||||
|
color: '#909399', |
||||
|
fontSize: 28, |
||||
|
}, |
||||
|
// show: false, |
||||
|
// border: 'border border-blue-500 shadow rounded-md', |
||||
|
// showBorder: false, |
||||
|
projectId: 0, |
||||
|
}); |
||||
|
|
||||
|
// 打开项目详情 |
||||
|
function openProject(project) { |
||||
|
const gateway = config.apiUrl; |
||||
|
const url = `${gateway}/defaultwbs`; |
||||
|
const { name, id } = project; |
||||
|
uni.navigateTo({ url: `/pages/project/project?u=${userId.value}&p=${id}&pname=${name}&url=${encodeURIComponent(url)}` }); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 弹出项目操作面板 |
||||
|
*/ |
||||
|
function openMenu(project) { |
||||
|
data.showMenu = true; |
||||
|
data.projectId = project.id; |
||||
|
data.tips.text = project.name; |
||||
|
|
||||
|
emit('setData', data.showMenu, data.projectId, data.tips); |
||||
|
// this.$emit('setData', data.showMenu, data.projectId, data.tips); |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss" scoped> |
||||
|
.border-100 { |
||||
|
height: 4rpx; |
||||
|
margin: 0 20rpx; |
||||
|
} |
||||
|
|
||||
|
.border-80 { |
||||
|
height: 4rpx; |
||||
|
margin: 0 20rpx 0 90rpx; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,57 @@ |
|||||
|
<template> |
||||
|
<view class="py-3 mt-4 bg-white u-font-15"> |
||||
|
<PrettyExchange @change="change" /> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script setup> |
||||
|
function change(options) { |
||||
|
if (options instanceof Array) { |
||||
|
let projectIdList = []; |
||||
|
let arr = []; |
||||
|
options.forEach(item => { |
||||
|
projectIdList.push(item.id); |
||||
|
arr.push(item.name); |
||||
|
}); |
||||
|
setProjectSort(projectIdList); |
||||
|
} else { |
||||
|
setProjectRelation(options); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 设置项目顺序 |
||||
|
* @param { Array } projectIdList 项目id |
||||
|
*/ |
||||
|
async function setProjectSort(projectIdList) { |
||||
|
try { |
||||
|
const params = { projectIdList }; |
||||
|
await uni.$u.api.setProjectSort(params); |
||||
|
uni.$ui.showToast('排序修改成功'); |
||||
|
} catch (error) { |
||||
|
console.log('error: ', error); |
||||
|
uni.$ui.showToast(error.msg || '排序修改失败'); |
||||
|
} |
||||
|
this.$emit('getProjects'); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 设置项目父子结构 |
||||
|
* @param { string } id 当前移动的项目的id |
||||
|
* @param { string } parentId 父项目的id |
||||
|
*/ |
||||
|
async function setProjectRelation(options) { |
||||
|
try { |
||||
|
const params = options; |
||||
|
await uni.$u.api.setProjectRelation(params); |
||||
|
uni.$ui.showToast('排序修改成功'); |
||||
|
} catch (error) { |
||||
|
console.error('error: ', error); |
||||
|
uni.$ui.showToast(error.msg || '排序修改失败'); |
||||
|
} |
||||
|
this.$emit('getProjects'); |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss" scoped> |
||||
|
</style> |
||||
@ -1,8 +1,236 @@ |
|||||
<template> |
<template> |
||||
|
<view class="px-2 bg-white wrap"> |
||||
|
<view class="home-box u-skeleton"> |
||||
|
<scroll-view :enable-flex="true" :scroll-left="data.scrollLeft" :throttle="false" scroll-with-animation scroll-x @scroll="scroll"> |
||||
|
<view class="tab-box"> |
||||
|
<!-- 角色项 |
||||
|
default-tab-choice 是 我的角色 && 当前展示 |
||||
|
default-tab-item 是 我的角色 && 当前不展示 |
||||
|
tab-choice 不是我的 && 当前展示 |
||||
|
--> |
||||
|
<view |
||||
|
:class="{ |
||||
|
'default-tab-choice': item.mine == 1 && roleId === item.id, |
||||
|
'default-tab-item': item.mine == 1 && roleId !== item.id, |
||||
|
'tab-choice': item.mine == 0 && roleId === item.id, |
||||
|
}" |
||||
|
:key="index" |
||||
|
@click="changeRole(item.id, index)" |
||||
|
class="tab-item" |
||||
|
v-for="(item, index) in data.roles" |
||||
|
> |
||||
|
<view class="tab-children u-skeleton-fillet u-font-14">{{ item.name }}</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</scroll-view> |
||||
|
</view> |
||||
|
<!-- 骨架屏 --> |
||||
|
<u-skeleton :animation="true" :loading="data.loading" bg-color="#fff"></u-skeleton> |
||||
|
</view> |
||||
</template> |
</template> |
||||
|
|
||||
<script> |
<script setup> |
||||
|
import { reactive, computed, watchEffect, onMounted, nextTick } from 'vue'; |
||||
|
import { useStore } from 'vuex'; |
||||
|
|
||||
|
const data = reactive({ |
||||
|
tabIndex: 0, // 当前访问的 index 默认为0 |
||||
|
tabList: [], // tab dom节点集合 |
||||
|
scrollLeft: 0, // scrollview需要滚动的距离 |
||||
|
loading: false, // 是否显示骨架屏组件 |
||||
|
roles: [{ id: 1, name: '项目经理', mine: 0, pm: 1, sequence: 1 }, { id: 2, name: '运维', mine: 0, pm: 0, sequence: 2 }], |
||||
|
roleLeft: 0, |
||||
|
}); |
||||
|
|
||||
|
const store = useStore(); |
||||
|
const visibleRoles = computed(() => store.state.role.visibleRoles); |
||||
|
const roleId = computed(() => store.state.role.roleId); |
||||
|
const tasks = computed(() => store.state.task.tasks); |
||||
|
|
||||
|
watchEffect(() => { |
||||
|
if (visibleRoles.value && visibleRoles.value.length) { |
||||
|
data.roles = visibleRoles.value; |
||||
|
data.loading = false; |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
onMounted(() => { |
||||
|
if (!visibleRoles.value || !visibleRoles.value.length) { |
||||
|
data.loading = true; |
||||
|
} else { |
||||
|
data.roles = visibleRoles.value; |
||||
|
} |
||||
|
|
||||
|
nextTick(() => { |
||||
|
const query = uni.createSelectorQuery().in(this); |
||||
|
query |
||||
|
.selectAll('.tab-children') |
||||
|
.boundingClientRect(res => { |
||||
|
if (res.length) { |
||||
|
data.roleLeft = res[0].left; |
||||
|
} |
||||
|
}) |
||||
|
.exec(); |
||||
|
}); |
||||
|
}); |
||||
|
|
||||
|
function scroll(e) { |
||||
|
data.scrollLeft = e.detail.scrollLeft; |
||||
|
} |
||||
|
|
||||
|
// 设置滚动位置 |
||||
|
function setCurrentRole(index) { |
||||
|
const query = uni.createSelectorQuery().in(this); |
||||
|
query |
||||
|
.selectAll('.tab-children') |
||||
|
.boundingClientRect(res => { |
||||
|
res.forEach(item => { |
||||
|
data.tabList.push({ width: item.width }); |
||||
|
}); |
||||
|
}) |
||||
|
.exec(); |
||||
|
|
||||
|
const system = uni.getSystemInfoSync(); // 获取系统信息 |
||||
|
// 屏幕宽度 |
||||
|
const screenWidth = system.windowWidth; |
||||
|
// 当前滚动的位置 |
||||
|
let left = 0; |
||||
|
for (let i = 0; i < index; i++) { |
||||
|
left += data.tabList[i].width + (data.roleLeft - 8) * 2; |
||||
|
} |
||||
|
|
||||
|
left += (data.tabList[index].width + (data.roleLeft - 8) * 2) / 2; |
||||
|
if (left > screenWidth) { |
||||
|
data.scrollLeft = left - screenWidth + screenWidth / 2; |
||||
|
} else if (left > screenWidth / 2) { |
||||
|
data.scrollLeft = left - screenWidth / 2; |
||||
|
} else if (left < screenWidth / 2) { |
||||
|
data.scrollLeft = 0; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 清除插件script |
||||
|
function clearPluginScript() { |
||||
|
try { |
||||
|
const scripts = document.querySelectorAll('script[data-type=plugin]'); |
||||
|
for (let i = 0; i < scripts.length; i++) { |
||||
|
document.body.removeChild(scripts[i]); |
||||
|
} |
||||
|
} catch (error) { |
||||
|
console.error('clearPluginScript error: ', error); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 切换角色 |
||||
|
// 查任务这里不用管 project监听了roleId的变化 |
||||
|
// 时间基准点不用管 project监听了roleId 里处理了 |
||||
|
function changeRole(id, index) { |
||||
|
try { |
||||
|
// 清除多余的script |
||||
|
clearPluginScript(); |
||||
|
nextTick(() => { |
||||
|
store.commit('role/setRoleId', id); |
||||
|
// 改变index 即手动点击切换 我在此时将当前元素赋值给左边距 实现自动滚动 |
||||
|
setCurrentRole(index); |
||||
|
}); |
||||
|
} catch (error) { |
||||
|
console.error('role.vue changeRole error: ', error); |
||||
|
} |
||||
|
} |
||||
</script> |
</script> |
||||
|
|
||||
<style> |
<style lang="scss" scoped> |
||||
|
.home-box { |
||||
|
// 对此盒子进行 sticky 粘性定位 |
||||
|
position: sticky; |
||||
|
top: 0; |
||||
|
background: #fff; // 设置背景 否则会透明 |
||||
|
|
||||
|
/* #ifdef H5 */ |
||||
|
//粘性定位 在h5下 加 原生头部高度 44px |
||||
|
top: 88rpx; |
||||
|
/* #endif */ |
||||
|
|
||||
|
.tab-box { |
||||
|
position: relative; |
||||
|
white-space: nowrap; |
||||
|
// height: 84rpx; |
||||
|
|
||||
|
.tab-item { |
||||
|
// width: 20%; |
||||
|
// height: 84rpx; |
||||
|
padding: 20rpx 24rpx; |
||||
|
position: relative; |
||||
|
display: inline-block; |
||||
|
text-align: center; |
||||
|
font-size: 30rpx; |
||||
|
transition-property: background-color, width; |
||||
|
} |
||||
|
|
||||
|
.default-tab-item { |
||||
|
color: $roleChoiceColor; |
||||
|
} |
||||
|
|
||||
|
.default-tab-choice { |
||||
|
//当前选中 基于此类名给与底部选中框 |
||||
|
position: relative; |
||||
|
color: $roleChoiceColor; |
||||
|
font-weight: 600; |
||||
|
} |
||||
|
|
||||
|
.default-tab-choice:before { |
||||
|
content: ''; |
||||
|
position: absolute; |
||||
|
left: 0; |
||||
|
bottom: -20rpx; |
||||
|
width: 100%; |
||||
|
height: 6rpx; |
||||
|
border-radius: 2rpx; |
||||
|
background: $roleChoiceColor; |
||||
|
} |
||||
|
|
||||
|
.tab-choice { |
||||
|
//当前选中 基于此类名给与底部选中框 |
||||
|
position: relative; |
||||
|
color: $uni-color-primary; |
||||
|
font-weight: 600; |
||||
|
} |
||||
|
|
||||
|
.tab-choice:before { |
||||
|
content: ''; |
||||
|
position: absolute; |
||||
|
left: 0; |
||||
|
bottom: -20rpx; |
||||
|
width: 100%; |
||||
|
height: 6rpx; |
||||
|
border-radius: 2rpx; |
||||
|
background: $uni-color-primary; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// // 删除 底部滚动条 |
||||
|
/* #ifndef APP-NVUE */ |
||||
|
::-webkit-scrollbar, |
||||
|
::-webkit-scrollbar, |
||||
|
::-webkit-scrollbar { |
||||
|
display: none; |
||||
|
width: 0 !important; |
||||
|
height: 0 !important; |
||||
|
-webkit-appearance: none; |
||||
|
background: transparent; |
||||
|
} |
||||
|
|
||||
|
/* #endif */ |
||||
|
/* #ifdef H5 */ |
||||
|
// 通过样式穿透,隐藏H5下,scroll-view下的滚动条 |
||||
|
scroll-view ::v-deep ::-webkit-scrollbar { |
||||
|
display: none; |
||||
|
} |
||||
|
|
||||
|
/* #endif */ |
||||
|
|
||||
|
.skeleton { |
||||
|
height: 44rpx; |
||||
|
} |
||||
</style> |
</style> |
||||
|
|||||
@ -0,0 +1,173 @@ |
|||||
|
<template> |
||||
|
<view> |
||||
|
<view :class="[avatarClass, animationClass]" class="lx-skeleton" v-show="loading"> |
||||
|
<view :class="[avatarShapeClass, bannerClass]" :style="{ width: avatarSize, height: avatarSize }" class="avatar-class"></view> |
||||
|
<view :style="{ width: rowWidth }" class="row"> |
||||
|
<view class="row-class lx-skeleton_title" v-if="title"></view> |
||||
|
<view :key="index" class="row-class" v-for="(item, index) in row"></view> |
||||
|
</view> |
||||
|
</view> |
||||
|
<slot v-if="!loading"></slot> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script setup> |
||||
|
import { computed } from 'vue'; |
||||
|
/** |
||||
|
* skeleton 骨架屏 |
||||
|
* @description 用于加载数据时占位图显示,跟Vant-UI用法相似,但比Vant-UI更灵活 |
||||
|
* @property {Boolean} loading 是否显示骨架屏,默认为true |
||||
|
* @property {Number | String} row 段落行数,默认为3 |
||||
|
* @property {Boolean | Number} rowWidth 段落行宽度,默认为100% |
||||
|
* @property {Boolean | String} title 是否显示标题,默认为false |
||||
|
* @property {Boolean | String} banner 是否显示banner,默认为false |
||||
|
* @property {Boolean | String} animate 是否开启动画,默认为false |
||||
|
* @property {Boolean | String} avatar 头像位置 |
||||
|
* @property {String} avatarSize 头像大小 |
||||
|
* @property {String} avatarShape 头像形状,默认为circle |
||||
|
* |
||||
|
* */ |
||||
|
const props = defineProps({ |
||||
|
loading: { |
||||
|
type: Boolean, |
||||
|
default: true, |
||||
|
}, |
||||
|
row: { |
||||
|
type: Number, |
||||
|
default: 3, |
||||
|
}, |
||||
|
title: { |
||||
|
type: String, |
||||
|
default: '', |
||||
|
}, |
||||
|
avatar: { |
||||
|
type: String, |
||||
|
default: '', |
||||
|
}, |
||||
|
animate: { |
||||
|
type: Boolean, |
||||
|
default: false, |
||||
|
}, |
||||
|
avatarSize: { type: String }, |
||||
|
rowWidth: { |
||||
|
type: String, |
||||
|
default: '100%', |
||||
|
}, |
||||
|
avatarShape: { |
||||
|
type: String, |
||||
|
default: 'circle', |
||||
|
}, |
||||
|
banner: { |
||||
|
type: Boolean, |
||||
|
default: false, |
||||
|
}, |
||||
|
// avator-size:{ |
||||
|
// type: String, |
||||
|
// defualt: '32px' |
||||
|
// } |
||||
|
}); |
||||
|
|
||||
|
const avatarClass = computed(() => { |
||||
|
if (props.avatar === 'top') { |
||||
|
return ['lx-skeleton_avator__top']; |
||||
|
} |
||||
|
if (props.avatar === 'left') { |
||||
|
return ['lx-skeleton_avator__left']; |
||||
|
} return ''; |
||||
|
}); |
||||
|
const animationClass = computed(() => [props.animate ? 'lx-skeleton_animation' : '']); |
||||
|
const slotClass = computed(() => [!props.loading ? 'show' : 'hide']); |
||||
|
const avatarShapeClass = computed(() => [props.avatarShape == 'round' ? 'lx-skeleton_avator__round' : '']); |
||||
|
const bannerClass = computed(() => [props.banner ? 'lx-skeleton_banner' : '']); |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss" scoped> |
||||
|
.lx-skeleton { |
||||
|
background-color: #fff; |
||||
|
padding: 12px; |
||||
|
} |
||||
|
|
||||
|
.lx-skeleton_avator__left { |
||||
|
display: flex; |
||||
|
width: 100%; |
||||
|
} |
||||
|
|
||||
|
.lx-skeleton_avator__left .avatar-class, |
||||
|
.lx-skeleton_avator__top .avatar-class { |
||||
|
background-color: #f2f3f5; |
||||
|
border-radius: 50%; |
||||
|
width: 32px; |
||||
|
height: 32px; |
||||
|
} |
||||
|
|
||||
|
.lx-skeleton_avator__left .avatar-class.lx-skeleton_avator__round, |
||||
|
.lx-skeleton_avator__top .avatar-class.lx-skeleton_avator__round { |
||||
|
border-radius: 0; |
||||
|
width: 32px; |
||||
|
height: 32px; |
||||
|
} |
||||
|
|
||||
|
.lx-skeleton_avator__left .avatar-class { |
||||
|
margin-right: 16px; |
||||
|
} |
||||
|
|
||||
|
.lx-skeleton_avator__top .avatar-class { |
||||
|
margin: 0 auto 12px auto; |
||||
|
} |
||||
|
|
||||
|
.row-class { |
||||
|
width: 100%; |
||||
|
height: 16px; |
||||
|
background-color: #f2f3f5; |
||||
|
margin-top: 12px; |
||||
|
} |
||||
|
|
||||
|
.row-class:first-child { |
||||
|
margin-top: 0; |
||||
|
} |
||||
|
|
||||
|
.row { |
||||
|
flex: 1; |
||||
|
} |
||||
|
|
||||
|
.lx-skeleton_avator__left .row { |
||||
|
width: calc(100% - 48px); |
||||
|
} |
||||
|
|
||||
|
.row-class:last-child { |
||||
|
width: 60%; |
||||
|
} |
||||
|
|
||||
|
.lx-skeleton_animation .row-class { |
||||
|
animation-duration: 1.5s; |
||||
|
animation-name: blink; |
||||
|
animation-timing-function: ease-in-out; |
||||
|
animation-iteration-count: infinite; |
||||
|
} |
||||
|
|
||||
|
@keyframes blink { |
||||
|
50% { |
||||
|
opacity: 0.6; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.lx-skeleton_title { |
||||
|
width: 40%; |
||||
|
} |
||||
|
|
||||
|
.show { |
||||
|
display: block; |
||||
|
} |
||||
|
|
||||
|
.hide { |
||||
|
display: none; |
||||
|
} |
||||
|
|
||||
|
.lx-skeleton .lx-skeleton_banner { |
||||
|
width: 92%; |
||||
|
margin: 10px auto; |
||||
|
height: 64px; |
||||
|
border-radius: 0; |
||||
|
background-color: #f2f3f5; |
||||
|
} |
||||
|
</style> |
||||
@ -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> |
||||
@ -1,8 +1,126 @@ |
|||||
<template> |
<template> |
||||
|
<!-- 时间间隔栏 --> |
||||
|
<!-- <Barrier /> --> |
||||
|
|
||||
|
<scroll-view |
||||
|
:lower-threshold="300" |
||||
|
scroll-y="true" |
||||
|
:upper-threshold="300" |
||||
|
:scroll-into-view="scrollToTaskId" |
||||
|
@scroll="scroll" |
||||
|
@scrolltolower="handleScrollBottom" |
||||
|
@scrolltoupper="handleScrollTop" |
||||
|
id="scroll" |
||||
|
> |
||||
|
<!-- 时间轴 --> |
||||
|
<!-- <u-divider bg-color="#f3f4f6" class="pt-5" fontSize="14px" v-if="topEnd">到顶啦</u-divider> --> |
||||
|
|
||||
|
<TimeBox /> |
||||
|
|
||||
|
<!-- <u-divider bg-color="#f3f4f6" class="pb-5" fontSize="14px" v-if="bottomEnd">到底啦</u-divider> --> |
||||
|
</scroll-view> |
||||
</template> |
</template> |
||||
|
|
||||
<script> |
<script setup> |
||||
</script> |
import { reactive, computed, defineExpose, defineEmits } from 'vue'; |
||||
|
import { useStore } from 'vuex'; |
||||
|
// import Barrier from './component/Barrier.vue'; |
||||
|
import dayjs from 'dayjs'; |
||||
|
import TimeBox from './component/TimeBox.vue'; |
||||
|
|
||||
|
const store = useStore(); |
||||
|
// const visibleRoles = computed(() => store.state.role.visibleRoles); |
||||
|
// const scrollTop = computed(() => store.state.task.scrollTop); |
||||
|
const tasks = computed(() => store.state.task.tasks); |
||||
|
const topEnd = computed(() => store.state.task.topEnd); |
||||
|
const bottomEnd = computed(() => store.state.task.bottomEnd); |
||||
|
const showSkeleton = computed(() => store.state.task.showSkeleton); |
||||
|
const timeNode = computed(() => store.state.task.timeNode); |
||||
|
const scrollToTaskId = computed(() => store.state.task.scrollToTaskId); |
||||
|
const timeGranularity = computed(() => store.getters['task/timeGranularity']); |
||||
|
const emit = defineEmits(['getTasks']); |
||||
|
|
||||
|
const data = reactive({ top: 0 }); |
||||
|
|
||||
<style> |
// 滚动 |
||||
</style> |
function scroll(e) { |
||||
|
data.top = e.detail.scrollTop; |
||||
|
store.commit('task/setShrink', data.top > data.scrollTop); |
||||
|
store.commit('task/setScrollTop', data.top); |
||||
|
} |
||||
|
|
||||
|
// 滚动到顶部 |
||||
|
async function handleScrollTop() { |
||||
|
if (!tasks.value || !tasks.value.length || showSkeleton.value) return; |
||||
|
const startTime = tasks.value[0].planStart - 0; |
||||
|
if (topEnd.value) { |
||||
|
// 没有数据时 自动加载数据 |
||||
|
console.warn('滚动到顶部没有数据时: '); |
||||
|
const addTasks = uni.$task.setPlaceholderTasks(startTime, true, timeGranularity.value); |
||||
|
store.commit('task/setUpTasks', addTasks); |
||||
|
} else { |
||||
|
// 有数据时 |
||||
|
console.warn('滚动到顶部有数据时: '); |
||||
|
const detailId = tasks.value.findIndex(task => task.detailId); |
||||
|
const timeNodeValue = tasks.value[detailId].planStart - 0; |
||||
|
const upQuery = { |
||||
|
timeNode: timeNodeValue, |
||||
|
queryType: 0, |
||||
|
queryNum: 6, |
||||
|
}; |
||||
|
await emit('getTasks', upQuery); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 滚动到底部 |
||||
|
async function handleScrollBottom() { |
||||
|
if (!tasks.value || !tasks.value.length || showSkeleton.value) return; |
||||
|
const startTime = tasks.value[tasks.value.length - 1].planStart - 0; |
||||
|
if (bottomEnd.value) { |
||||
|
// 没有数据时 自动加载数据 |
||||
|
console.warn('滚动到底部没有数据时: '); |
||||
|
const addTasks = uni.$task.setPlaceholderTasks(startTime, false, timeGranularity.value); |
||||
|
store.commit('task/setDownTasks', addTasks); |
||||
|
} else { |
||||
|
// 时间基准点=最后一个任务的开始时间+当前时间颗粒度 |
||||
|
console.warn('滚动到底部有数据时: '); |
||||
|
const arr = []; |
||||
|
tasks.value.forEach(task => { |
||||
|
if (task.detailId) { |
||||
|
arr.push(task); |
||||
|
} |
||||
|
}); |
||||
|
const nextQueryTime = +uni.$time.add(+arr[arr.length - 1].planStart, 1, timeGranularity.value); |
||||
|
const downQuery = { |
||||
|
timeNode: nextQueryTime, |
||||
|
queryType: 1, |
||||
|
queryNum: 6, |
||||
|
}; |
||||
|
await emit('getTasks', downQuery); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 设置自动滚动位置 |
||||
|
function setScrollPosition() { |
||||
|
// 如果storage里有taskId 滚动到这个id的任务 |
||||
|
const taskId = uni.$storage.getStorageSync('taskId'); |
||||
|
if (taskId) { |
||||
|
store.commit('task/setScrollToTaskId', `a${taskId}`); |
||||
|
uni.$storage.setStorageSync('taskId', ''); // 记录后即刻清除本地存储 |
||||
|
} else { |
||||
|
const item = tasks.value.find(task => task.detailId); |
||||
|
if (item) { |
||||
|
store.commit('task/setScrollToTaskId', `a${item.id}`); |
||||
|
} else { |
||||
|
// 没有本地记录的taskId |
||||
|
// 找到当前时间基准线的任务id 记录 并滚动到当前时间基准线 |
||||
|
const task = tasks.value.find(item => dayjs(+item.planStart).isSame(timeNode.value, timeGranularity.value)); |
||||
|
task && store.commit('task/setScrollToTaskId', `a${task.id}`); // 有这个task 就记录他的id |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
defineExpose({ |
||||
|
setScrollPosition |
||||
|
}) |
||||
|
</script> |
||||
|
|||||
@ -0,0 +1,38 @@ |
|||||
|
<!-- |
||||
|
* @Author: aBin |
||||
|
* @email: binbin0314@126.com |
||||
|
* @Date: 2021-07-19 14:22:54 |
||||
|
* @LastEditors: aBin |
||||
|
* @LastEditTime: 2021-07-20 11:46:04 |
||||
|
--> |
||||
|
<template> |
||||
|
<view class> |
||||
|
<!-- :class="{ active: cycleTasks.time.start === filter.startTime }" --> |
||||
|
<view class="cycle-time active"> |
||||
|
<!-- {{ $util.formatStartTimeToCycleTime(filter.time, cycleTasks.time.start) }} --> |
||||
|
2021年30周 |
||||
|
</view> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script setup> |
||||
|
|
||||
|
</script> |
||||
|
<style scoped lang="scss"> |
||||
|
.cycle-time { |
||||
|
padding: 8rpx 16rpx; |
||||
|
margin-bottom: 16rpx; |
||||
|
background: #fafafc; |
||||
|
color: $uni-text-color; |
||||
|
font-size: 28rpx; |
||||
|
position: sticky; |
||||
|
top: -1px; |
||||
|
left: 0; |
||||
|
z-index: 99; |
||||
|
|
||||
|
&.active { |
||||
|
background: $uni-color-primary; |
||||
|
color: $uni-text-color-inverse; |
||||
|
} |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,177 @@ |
|||||
|
<template> |
||||
|
<view> |
||||
|
<view class="flex justify-between" style="min-width: 90px; position: relative"> |
||||
|
<u-icon custom-prefix="custom-icon" name="C-bxl-redux" size="17px"></u-icon> |
||||
|
<u-icon custom-prefix="custom-icon" name="attachment" size="21px"></u-icon> |
||||
|
<!-- <u-icon custom-prefix="custom-icon" name="moneycollect" size="20px"></u-icon> --> |
||||
|
<u-icon name="xuanxiang" custom-prefix="custom-icon" size="21px" @click="operation"></u-icon> |
||||
|
|
||||
|
<!-- 右上角 ... 弹窗 --> |
||||
|
<view class="popup border shadow-md" v-if="data.show"> |
||||
|
<!-- <view class="flex justify-center pb-3 border-b-1"> |
||||
|
<span>添加插件</span> |
||||
|
</view> --> |
||||
|
<view class="flex justify-center pb-3 border-b-1"> |
||||
|
<span @click="createTask">新建任务</span> |
||||
|
</view> |
||||
|
<view class="flex pt-3 justify-center"> |
||||
|
<span>克隆任务</span> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<!-- 遮罩 --> |
||||
|
<view class="mask" v-if="data.maskShow" @click="closeMask"></view> |
||||
|
<!-- 新建任务弹窗 --> |
||||
|
<CreateTask |
||||
|
:startTime="data.startTime" |
||||
|
:endTime="data.endTime" |
||||
|
:task="task" |
||||
|
:source="'regular'" |
||||
|
@showTime="showTime" |
||||
|
@closeMask="closeMask" |
||||
|
class="thirdPopup flex transition-transform" |
||||
|
v-if="data.createTaskShow" |
||||
|
/> |
||||
|
|
||||
|
<u-picker title="开始时间" mode="time" v-model="data.showStart" :params="data.params" @confirm="confirmStartTime"></u-picker> |
||||
|
<u-picker title="结束时间" mode="time" v-model="data.showEnd" :params="data.params" @confirm="confirmEndTime"></u-picker> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script setup> |
||||
|
import { reactive } from 'vue'; |
||||
|
import CreateTask from '@/components/Title/components/CreateTask.vue'; |
||||
|
|
||||
|
defineProps({ task: { type: Object, default: () => {} } }); |
||||
|
|
||||
|
const data = reactive({ |
||||
|
show: false, // 右上角 ... 显示 |
||||
|
createTaskShow: false, // 新建项目显示 |
||||
|
secondShow: false, // 分享项目显示 |
||||
|
maskShow: false, // 遮罩显示 |
||||
|
showStart: false, |
||||
|
showEnd: false, |
||||
|
startTime: '', // 新建任务的开始时间 |
||||
|
endTime: '', // 新建任务的截止时间 |
||||
|
params: { |
||||
|
year: true, |
||||
|
month: true, |
||||
|
day: true, |
||||
|
hour: true, |
||||
|
minute: true, |
||||
|
second: true, |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
// 操作 |
||||
|
function operation() { |
||||
|
// this.$t.ui.showToast('操作'); |
||||
|
data.show = !data.show; |
||||
|
} |
||||
|
|
||||
|
// 新建项目 |
||||
|
function createTask() { |
||||
|
// 关闭 ... 弹窗 |
||||
|
data.show = false; |
||||
|
// 打开遮罩 |
||||
|
data.maskShow = true; |
||||
|
// 打开新建项目弹窗 |
||||
|
data.createTaskShow = true; |
||||
|
} |
||||
|
|
||||
|
// 点击遮罩,关闭弹窗 |
||||
|
function closeMask() { |
||||
|
// 关闭遮罩 |
||||
|
data.maskShow = false; |
||||
|
// 关闭分享项目弹窗 |
||||
|
data.secondShow = false; |
||||
|
// 关闭新建项目弹窗 |
||||
|
data.createTaskShow = false; |
||||
|
} |
||||
|
|
||||
|
function showTime() { |
||||
|
data.showStart = !data.showStart; |
||||
|
} |
||||
|
|
||||
|
// 选择开始时间 |
||||
|
function confirmStartTime(e) { |
||||
|
data.startTime = `${e.year}-${e.month}-${e.day} ${e.hour}:${e.minute}:${e.second}`; |
||||
|
data.showEnd = true; |
||||
|
} |
||||
|
|
||||
|
// 选择结束时间 |
||||
|
function confirmEndTime(e) { |
||||
|
data.endTime = `${e.year}-${e.month}-${e.day} ${e.hour}:${e.minute}:${e.second}`; |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss" scoped> |
||||
|
.mask { |
||||
|
width: 100%; |
||||
|
height: 100vh; |
||||
|
z-index: 21; |
||||
|
position: fixed; |
||||
|
top: 0; |
||||
|
left: 0; |
||||
|
background: rgba(0, 0, 0, 0.3); |
||||
|
} |
||||
|
|
||||
|
.thirdPopup { |
||||
|
background: #ffffff; |
||||
|
position: fixed; |
||||
|
left: 50%; |
||||
|
top: 50%; |
||||
|
transform: translate(-50%, -50%); |
||||
|
z-index: 33; |
||||
|
border-radius: 5px; |
||||
|
width: 90%; |
||||
|
} |
||||
|
|
||||
|
.popup { |
||||
|
width: 110px; |
||||
|
background: #fff; |
||||
|
position: absolute; |
||||
|
right: 0; |
||||
|
top: 35px; |
||||
|
z-index: 99; |
||||
|
padding: 15px 0; |
||||
|
color: black; |
||||
|
animation: opacity 1s ease-in; |
||||
|
} |
||||
|
|
||||
|
@keyframes opacity { |
||||
|
0% { |
||||
|
opacity: 0; |
||||
|
} |
||||
|
50% { |
||||
|
opacity: 0.8; |
||||
|
} |
||||
|
100% { |
||||
|
opacity: 1; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
::v-deep .u-slot-content { |
||||
|
min-width: 0; |
||||
|
} |
||||
|
::v-deep .u-dropdown__content { |
||||
|
min-height: 120px !important; |
||||
|
height: auto !important; |
||||
|
overflow-y: auto; |
||||
|
background: #fff !important; |
||||
|
transition: none !important; |
||||
|
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); |
||||
|
} |
||||
|
::v-deep .u-dropdown__menu__item .u-flex { |
||||
|
justify-content: space-between; |
||||
|
width: 100%; |
||||
|
height: 100%; |
||||
|
flex-wrap: nowrap; |
||||
|
border: 1px solid #afbed1; |
||||
|
padding: 0 8px; |
||||
|
} |
||||
|
::v-deep .u-dropdown__content__mask { |
||||
|
display: none; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,132 @@ |
|||||
|
<template> |
||||
|
<!-- 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" /> |
||||
|
<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" /> |
||||
|
</view> |
||||
|
</view> |
||||
|
<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> |
||||
|
</view> |
||||
|
</view> |
||||
|
</u-card> |
||||
|
|
||||
|
<u-card |
||||
|
@click="onClickTask(task.planStart - 0, task.id)" |
||||
|
:show-foot="false" |
||||
|
:show-head="false" |
||||
|
:style="{ height: setHeight(task.panel) }" |
||||
|
class="h-16" |
||||
|
margin="0" |
||||
|
v-if="tasks && tasks.length && task.process !== 4 && !showSkeleton" |
||||
|
> |
||||
|
<!-- 任务面板插件 --> |
||||
|
<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 |
||||
|
:class="[`row-span-${plugin.row}`, `col-span-${plugin.col}`]" |
||||
|
:task="task" |
||||
|
:key="plugin.pluginTaskId" |
||||
|
:plugin-task-id="plugin.pluginTaskId" |
||||
|
:plugin-id="plugin.pluginId" |
||||
|
:param="plugin.param" |
||||
|
:style-type="data.styleType || 0" |
||||
|
v-for="plugin in row" |
||||
|
/> |
||||
|
</view> --> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</u-card> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
<!-- 局部弹框操作栏 --> |
||||
|
<!-- <Tips /> --> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script setup> |
||||
|
import { useStore } from 'vuex'; |
||||
|
import { computed, reactive } from 'vue'; |
||||
|
import TimeStatus from './TimeStatus.vue'; |
||||
|
import TaskTools from './TaskTools.vue'; |
||||
|
|
||||
|
// const data = reactive({ |
||||
|
// currentComponent: '', |
||||
|
// styleType: 0, |
||||
|
// }); |
||||
|
|
||||
|
const store = useStore(); |
||||
|
const roleId = computed(() => store.state.role.roleId); |
||||
|
const timeUnit = computed(() => store.state.task.timeUnit); |
||||
|
const tasks = computed(() => store.state.task.tasks); |
||||
|
const showSkeleton = computed(() => store.state.task.showSkeleton); |
||||
|
const startTimeFormat = computed(() => store.getters['task/startTimeFormat']); |
||||
|
|
||||
|
// 设置任务面板高度 |
||||
|
function setHeight(panel) { |
||||
|
if (panel && panel.height) { |
||||
|
return `${panel.height}px`; |
||||
|
} |
||||
|
return 'auto'; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 点击了定期任务的面板 更新可变的日常任务 |
||||
|
* @param {number} planStart 任务计划开始时间 |
||||
|
* @param {string} taskId 任务id |
||||
|
*/ |
||||
|
function onClickTask(planStart, taskId) { |
||||
|
const param = { roleId: roleId.value, timeNode: planStart, timeUnit: timeUnit.value }; |
||||
|
store.dispatch('task/getGlobal', param); |
||||
|
uni.$storage.setStorageSync('taskId', taskId); |
||||
|
uni.$storage.setStorageSync('roleId', roleId.value); |
||||
|
} |
||||
|
|
||||
|
function pinchHandler(evt) { |
||||
|
// evt.scale代表两个手指缩放的比例 |
||||
|
console.log(`缩放:${evt.zoom}`); |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style scoped lang="scss"> |
||||
|
.task-box { |
||||
|
border-radius: 24rpx; |
||||
|
} |
||||
|
.column { |
||||
|
padding: 24px 14px; |
||||
|
} |
||||
|
.task-column { |
||||
|
height: 33px; |
||||
|
} |
||||
|
.plugin { |
||||
|
margin-top: 8px; |
||||
|
margin-bottom: 8px; |
||||
|
margin-left: 15px; |
||||
|
border-left: 2px solid #d1d5db; |
||||
|
} |
||||
|
::v-deep .ml-2 { |
||||
|
margin-left: 16px; |
||||
|
} |
||||
|
|
||||
|
::v-deep .ml-3 { |
||||
|
margin-left: 20px; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,317 @@ |
|||||
|
<template> |
||||
|
<view class="u-font-14"> |
||||
|
<view |
||||
|
class="flex items-center justify-center rounded-full icon-column" |
||||
|
:style="{ color: orderStyle.color }" |
||||
|
@click="changeStatus(task.process, $event)" |
||||
|
> |
||||
|
<!-- 1进行中 2暂停中 3已完成 --> |
||||
|
<u-circle-progress |
||||
|
:percent="orderStyle.persent - 0" |
||||
|
:active-color="orderStyle.color" |
||||
|
bg-color="rgba(255,255,255,0)" |
||||
|
border-width="4" |
||||
|
:width="task.process !== 4 ? 66 : 50" |
||||
|
v-if="task.process === 1 || task.process === 2 || task.process === 3" |
||||
|
> |
||||
|
<view class="u-progress-content"> |
||||
|
<view class="u-progress-dot"></view> |
||||
|
<view class="u-progress-info"> |
||||
|
<u-icon :name="orderStyle.icon" v-if="orderStyle.icon" size="15px"></u-icon> |
||||
|
<template v-else>{{ data.durationText }}</template> |
||||
|
</view> |
||||
|
</view> |
||||
|
</u-circle-progress> |
||||
|
<!-- 0未开始 4添加任务 --> |
||||
|
<view class="flex items-center justify-center rounded-full progress-box" v-else :class="task.process === 4 ? 'progress-box-4' : ''"> |
||||
|
<view class="u-progress-content"> |
||||
|
<view class="u-progress-dot"></view> |
||||
|
<view class="u-progress-info"> |
||||
|
<span v-if="orderStyle.icon"> |
||||
|
<u-icon :name="orderStyle.icon" v-if="task.process !== 4" size="15px"></u-icon> |
||||
|
<u-icon :name="orderStyle.icon" v-else size="15px"></u-icon> |
||||
|
</span> |
||||
|
<template v-else>{{ data.durationText }}</template> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<!-- 遮罩 --> |
||||
|
<view class="mask" v-if="data.maskShow" @click="closeMask"></view> |
||||
|
<!-- 新建任务弹窗 --> |
||||
|
<CreateTask |
||||
|
:startTime="data.startTime" |
||||
|
:endTime="data.endTime" |
||||
|
:task="task" |
||||
|
:source="'regular'" |
||||
|
@showTime="showTime" |
||||
|
@closeMask="closeMask" |
||||
|
class="thirdPopup flex transition-transform" |
||||
|
v-if="data.createTaskShow" |
||||
|
/> |
||||
|
|
||||
|
<u-picker title="开始时间" mode="time" v-model="data.showStart" :params="data.params" @confirm="confirmStartTime"></u-picker> |
||||
|
<u-picker title="结束时间" mode="time" v-model="data.showEnd" :params="data.params" @confirm="confirmEndTime"></u-picker> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<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: () => {} } }); |
||||
|
|
||||
|
const data = reactive({ |
||||
|
time: '', |
||||
|
start: [{ text: '确认开始任务', color: 'blue' }], |
||||
|
pause: [{ text: '继续' }, { text: '重新开始任务', color: 'blue' }, { text: '结束' }], |
||||
|
proceed: [{ text: '暂停' }, { text: '重新开始任务', color: 'blue' }, { text: '结束' }], |
||||
|
again: [{ text: '重新开始任务', color: 'blue' }], |
||||
|
timer: null, |
||||
|
durationText: 0, |
||||
|
maskShow: false, // 遮罩显示 |
||||
|
createTaskShow: false, // 新建项目显示 |
||||
|
startTime: '', // 新建任务的开始时间 |
||||
|
endTime: '', // 新建任务的截止时间 |
||||
|
showStart: false, |
||||
|
showEnd: false, |
||||
|
params: { |
||||
|
year: true, |
||||
|
month: true, |
||||
|
day: true, |
||||
|
hour: true, |
||||
|
minute: true, |
||||
|
second: true, |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
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 : ''); |
||||
|
|
||||
|
// 计算圆环的弧度百分比 |
||||
|
function computeCyclePersent() { |
||||
|
if (!props.task || !props.task.realStart || !props.task.planDuration) return 100; |
||||
|
const { realStart, planDuration } = props.task; |
||||
|
return (((Date.now() - +realStart) * 100) / +planDuration).toFixed(2); |
||||
|
} |
||||
|
|
||||
|
const orderStyle = computed(() => { |
||||
|
// 图标文本颜色 |
||||
|
// 任务状态 0未开始 1进行中 2暂停中 3已完成 |
||||
|
let color = '#9CA3AF'; |
||||
|
let icon = 'play-right-fill'; |
||||
|
let persent = 100; |
||||
|
switch (status.value) { |
||||
|
case 1: // 进行中 |
||||
|
color = '#60A5FA'; |
||||
|
icon = ''; |
||||
|
if (+computeCyclePersent() > 100) { |
||||
|
persent = 96; |
||||
|
} else { |
||||
|
persent = computeCyclePersent(); |
||||
|
} |
||||
|
break; |
||||
|
case 2: // 暂停中 |
||||
|
color = '#F87171'; |
||||
|
icon = 'pause'; |
||||
|
persent = 50; // TODO: 暂时这样 暂停状态没有计算剩余多少时间 |
||||
|
break; |
||||
|
case 3: // 已结束 |
||||
|
color = '#34D399'; |
||||
|
icon = 'checkmark'; |
||||
|
persent = 100; |
||||
|
break; |
||||
|
case 4: // 添加任务 |
||||
|
color = '#60A5FA'; |
||||
|
icon = 'plus'; |
||||
|
persent = 100; |
||||
|
break; |
||||
|
default: |
||||
|
// 未开始 |
||||
|
color = '#9CA3AF'; |
||||
|
icon = 'play-right'; |
||||
|
persent = 100; |
||||
|
break; |
||||
|
} |
||||
|
return { color, icon, persent }; |
||||
|
}); |
||||
|
|
||||
|
// unMounted(() => { |
||||
|
// if (data.timer) { |
||||
|
// clearInterval(data.timer); |
||||
|
// data.timer = null; |
||||
|
// } |
||||
|
// }); |
||||
|
|
||||
|
/** |
||||
|
* 计算tip的标题内容 |
||||
|
*/ |
||||
|
function genetateTips(type, content) { |
||||
|
if (type === 0) { |
||||
|
return `确认开始任务"${content}"吗?`; |
||||
|
} |
||||
|
if (type === 3) { |
||||
|
return '是否要重新开始此任务'; |
||||
|
} |
||||
|
return '请选择要执行的操作'; |
||||
|
} |
||||
|
|
||||
|
// 新建任务 |
||||
|
function addTask() { |
||||
|
// uni.$ui.showToast('新建任务'); |
||||
|
// 打开遮罩 |
||||
|
data.maskShow = true; |
||||
|
// 打开新建项目弹窗 |
||||
|
data.createTaskShow = true; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 点击了图标 修改任务状态 |
||||
|
* @param {object} event |
||||
|
*/ |
||||
|
function changeStatus(process, event) { |
||||
|
if (process === 4) { |
||||
|
addTask(); |
||||
|
return; |
||||
|
} |
||||
|
// return false; |
||||
|
tip.status = status; |
||||
|
tip.taskId = taskId; |
||||
|
tip.left = event.target.x; |
||||
|
tip.top = event.target.y; |
||||
|
tip.show = true; |
||||
|
tip.text = genetateTips(status, taskName); |
||||
|
store.commit('task/setTip', tip); |
||||
|
} |
||||
|
|
||||
|
// 点击遮罩,关闭弹窗 |
||||
|
function closeMask() { |
||||
|
// 关闭遮罩 |
||||
|
data.maskShow = false; |
||||
|
// 关闭新建项目弹窗 |
||||
|
data.createTaskShow = false; |
||||
|
} |
||||
|
|
||||
|
function showTime(type) { |
||||
|
if (type === 1) { |
||||
|
data.showStart = !data.showStart; |
||||
|
} else { |
||||
|
data.showEnd = !data.showEnd; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 选择开始时间 |
||||
|
function confirmStartTime(e) { |
||||
|
data.startTime = `${e.year}-${e.month}-${e.day} ${e.hour}:${e.minute}:${e.second}`; |
||||
|
data.showEnd = true; |
||||
|
} |
||||
|
|
||||
|
// 选择结束时间 |
||||
|
function confirmEndTime(e) { |
||||
|
data.endTime = `${e.year}-${e.month}-${e.day} ${e.hour}:${e.minute}:${e.second}`; |
||||
|
} |
||||
|
|
||||
|
// 计算进行中状态剩余时间 |
||||
|
// 预计结束时间 = realStart(实际开始) + planDuration(计划时长) |
||||
|
// 剩余时间 = 预计结束时间 - 当前时间 |
||||
|
// 剩余时间 = realStart + planDuration - Date.now() |
||||
|
function computeDurationText() { |
||||
|
const { realStart, planDuration } = props.task; |
||||
|
const leftTime = (realStart-0 || 0) + (planDuration-0 || 0) - Date.now(); // 剩余时间 |
||||
|
const { num, time } = uni.$time.computeDurationText(leftTime); |
||||
|
if (num <= 0) { |
||||
|
clearInterval(data.timer); |
||||
|
data.timer = null; |
||||
|
} |
||||
|
data.durationText = num; |
||||
|
return time; |
||||
|
} |
||||
|
|
||||
|
function updateDurationText(time) { |
||||
|
if (data.timer) { |
||||
|
clearInterval(data.timer); |
||||
|
data.timer = null; |
||||
|
} |
||||
|
if (!time) return; |
||||
|
setInterval(() => { |
||||
|
computeDurationText(); |
||||
|
}, time); |
||||
|
} |
||||
|
|
||||
|
onMounted(() => { |
||||
|
// TODO: 计算在不在窗口内显示 |
||||
|
const time = computeDurationText(); |
||||
|
updateDurationText(time); |
||||
|
}); |
||||
|
</script> |
||||
|
|
||||
|
<style scoped lang="scss"> |
||||
|
.icon-column { |
||||
|
height: 33px; |
||||
|
width: 33px; |
||||
|
} |
||||
|
.one { |
||||
|
height: 33px; |
||||
|
width: 33px; |
||||
|
} |
||||
|
|
||||
|
.progress-box { |
||||
|
background: rgba(255, 255, 255, 0); |
||||
|
width: 33px; |
||||
|
height: 33px; |
||||
|
border: 2px solid #9ca3af; |
||||
|
} |
||||
|
|
||||
|
.progress-box-4 { |
||||
|
width: 25px; |
||||
|
height: 25px; |
||||
|
border: 2px solid #60a5fa; |
||||
|
} |
||||
|
|
||||
|
.mask { |
||||
|
width: 100%; |
||||
|
height: 100vh; |
||||
|
z-index: 21; |
||||
|
position: fixed; |
||||
|
top: 0; |
||||
|
left: 0; |
||||
|
background: rgba(0, 0, 0, 0.3); |
||||
|
} |
||||
|
|
||||
|
.thirdPopup { |
||||
|
background: #ffffff; |
||||
|
position: fixed; |
||||
|
left: 50%; |
||||
|
top: 50%; |
||||
|
transform: translate(-50%, -50%); |
||||
|
z-index: 33; |
||||
|
border-radius: 5px; |
||||
|
width: 90%; |
||||
|
} |
||||
|
|
||||
|
::v-deep .u-dropdown__content { |
||||
|
min-height: 120px !important; |
||||
|
height: auto !important; |
||||
|
overflow-y: auto; |
||||
|
background: #fff !important; |
||||
|
transition: none !important; |
||||
|
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); |
||||
|
} |
||||
|
::v-deep .u-dropdown__menu__item .u-flex { |
||||
|
justify-content: space-between; |
||||
|
width: 100%; |
||||
|
height: 100%; |
||||
|
flex-wrap: nowrap; |
||||
|
border: 1px solid #afbed1; |
||||
|
padding: 0 8px; |
||||
|
} |
||||
|
::v-deep .u-dropdown__content__mask { |
||||
|
display: none; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,81 @@ |
|||||
|
<template> |
||||
|
<view |
||||
|
class="fixed shadow-2xl" |
||||
|
style="z-index: 1000" |
||||
|
:style="{ |
||||
|
left: tip.left + 'px', |
||||
|
top: data.height - tip.top > 110 ? tip.top + 'px' : '', |
||||
|
bottom: data.height - tip.top > 110 ? '' : '10px', |
||||
|
}" |
||||
|
id="u-icard" |
||||
|
> |
||||
|
<u-card :title="title" style="width: 500rpx; margin: 0 !important" v-if="tip.show" titleSize="28" :headStyle="data.headStyle" :footStyle="data.footStyle"> |
||||
|
<view class="" slot="body">{{ tip.text }}</view> |
||||
|
<view class="flex justify-end" slot="foot"> |
||||
|
<u-button size="mini" @click="onCancel">取消</u-button> |
||||
|
<u-button v-if="tip.status === 1" size="mini" @click="onChangeStatus(1)">暂停</u-button> |
||||
|
<u-button v-if="tip.status === 2" size="mini" @click="onChangeStatus(2)">继续</u-button> |
||||
|
<u-button v-if="tip.status === 1 || tip.status === 2" size="mini" @click="onChangeStatus(0)">重新开始</u-button> |
||||
|
<u-button v-if="tip.status === 1 || tip.status === 2" type="primary" size="mini" @click="onChangeStatus(3)">结束</u-button> |
||||
|
<u-button v-if="tip.status === 0 || tip.status === 3" type="primary" size="mini" @click="onChangeStatus(0)">确定</u-button> |
||||
|
</view> |
||||
|
</u-card> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script setup> |
||||
|
import { useStore, onMounted, computed, reactive } from 'vuex'; |
||||
|
|
||||
|
defineProps({ title: { default: '提示', type: String } }); |
||||
|
|
||||
|
const store = useStore(); |
||||
|
const tip = computed(() => store.state.task.tip); |
||||
|
|
||||
|
const data = reactive({ |
||||
|
footStyle: { padding: '4px 15px' }, |
||||
|
headStyle: { paddingTop: '8px', paddingBottom: '8px' }, |
||||
|
height: 0, |
||||
|
}); |
||||
|
|
||||
|
onMounted(() => { |
||||
|
const system = uni.getSystemInfoSync(); |
||||
|
data.height = system.windowHeight; |
||||
|
}); |
||||
|
|
||||
|
/** |
||||
|
* 执行修改任务状态的动作 |
||||
|
* @param {number} type 状态码 0开始 1暂停 2继续 3完成 默认0 |
||||
|
*/ |
||||
|
async function onChangeStatus(type) { |
||||
|
try { |
||||
|
const param = { id: data.tip.taskId, type }; |
||||
|
await uni.$u.api.updateTaskType(param); |
||||
|
if (type === 0) { |
||||
|
uni.$ui.showToast('项目已重新开始'); |
||||
|
} else if (type === 1) { |
||||
|
uni.$ui.showToast('项目已暂停'); |
||||
|
} else if (type === 2) { |
||||
|
uni.$ui.showToast('项目继续'); |
||||
|
} else if (type === 3) { |
||||
|
uni.$ui.showToast('项目结束'); |
||||
|
} |
||||
|
data.tip.show = false; |
||||
|
// TODO: 更新界面 不要整体刷新 |
||||
|
// location.reload(); |
||||
|
// this.$router.go(0); |
||||
|
} catch (error) { |
||||
|
console.error(error); |
||||
|
uni.$ui.showToast(error.msg || '操作失败'); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 点击了取消 |
||||
|
function onCancel() { |
||||
|
store.commit('task/setTipShow', false); |
||||
|
} |
||||
|
|
||||
|
// 点击了确认 |
||||
|
// function onConfirm() { |
||||
|
// onCancel(); |
||||
|
// } |
||||
|
</script> |
||||
@ -1,8 +1,225 @@ |
|||||
<template> |
<template> |
||||
|
<view> |
||||
|
<!-- :is-back="false" --> |
||||
|
<u-navbar :custom-back="onBack" class="overflow-hidden"> |
||||
|
<view class="flex justify-start flex-1 px-3 font-bold min-0"> |
||||
|
<view class="truncate">{{ project.name }}</view> |
||||
|
</view> |
||||
|
<view class="mr-2" slot="right"> |
||||
|
<u-icon class="m-1" name="xuanzhong2" custom-prefix="custom-icon" size="20px" @click="lwbs"></u-icon> |
||||
|
<u-icon class="m-1" name="shuaxin1" custom-prefix="custom-icon" size="20px" @click="projectOverview"></u-icon> |
||||
|
<u-icon class="m-1" name="home" custom-prefix="custom-icon" size="20px" @click="openIndex"></u-icon> |
||||
|
<u-icon class="m-1" name="xuanxiang" custom-prefix="custom-icon" size="20px" @click="operation"></u-icon> |
||||
|
</view> |
||||
|
</u-navbar> |
||||
|
<view |
||||
|
class="mask" |
||||
|
v-if="data.maskShow" |
||||
|
@click="closeMask" |
||||
|
style="width: 100%; height: 100vh; z-index: 21; position: fixed; background: rgba(0, 0, 0, 0.3)" |
||||
|
></view> |
||||
|
<!-- 右上角 ... 弹窗 --> |
||||
|
<view class="popup border shadow-md" v-if="data.show"> |
||||
|
<view class="flex pb-3 border-b-1"> |
||||
|
<u-icon name="plus-circle" size="36" style="margin: 0 15px 3px 0"></u-icon> |
||||
|
<view @click="createTask">新建任务</view> |
||||
|
<!-- <view>新建任务</view> --> |
||||
|
</view> |
||||
|
<view class="flex pt-3"> |
||||
|
<u-icon name="share" size="32" style="margin: 0 15px 3px 0"></u-icon> |
||||
|
<view @click="share">分享项目</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
<!-- 分享项目弹窗 --> |
||||
|
<ShareProject v-if="data.secondShow" class="second-popup" /> |
||||
|
<!-- 新建任务弹窗 --> |
||||
|
<CreateTask |
||||
|
:startTime="data.startTime" |
||||
|
:endTime="data.endTime" |
||||
|
@showTime="showTime" |
||||
|
@closeMask="closeMask" |
||||
|
class="third-popup flex transition-transform" |
||||
|
v-if="data.createTaskShow" |
||||
|
/> |
||||
|
|
||||
|
<u-picker title="开始时间" mode="time" v-model="data.showStart" :params="data.params" @confirm="confirmStartTime"></u-picker> |
||||
|
<u-picker title="结束时间" mode="time" v-model="data.showEnd" :params="data.params" @confirm="confirmEndTime"></u-picker> |
||||
|
</view> |
||||
</template> |
</template> |
||||
|
|
||||
<script> |
<script setup> |
||||
|
import { reactive, computed } from 'vue'; |
||||
|
import { useStore } from 'vuex'; |
||||
|
import CreateTask from './components/CreateTask.vue'; |
||||
|
import ShareProject from './components/ShareProject.vue'; |
||||
|
|
||||
|
const data = reactive({ |
||||
|
show: false, // 右上角 ... 显示 |
||||
|
createTaskShow: false, // 新建项目显示 |
||||
|
secondShow: false, // 分享项目显示 |
||||
|
maskShow: false, // 遮罩显示 |
||||
|
showStart: false, |
||||
|
showEnd: false, |
||||
|
startTime: '', // 新建任务的开始时间 |
||||
|
endTime: '', // 新建任务的截止时间 |
||||
|
params: { |
||||
|
year: true, |
||||
|
month: true, |
||||
|
day: true, |
||||
|
hour: true, |
||||
|
minute: true, |
||||
|
second: true, |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
const store = useStore(); |
||||
|
const project = computed(() => store.state.project.project); |
||||
|
const userId = computed(() => store.getters['user/userId']); |
||||
|
|
||||
|
function showTime() { |
||||
|
data.showStart = !data.showStart; |
||||
|
} |
||||
|
|
||||
|
// 选择开始时间 |
||||
|
function confirmStartTime(e) { |
||||
|
data.startTime = `${e.year}-${e.month}-${e.day} ${e.hour}:${e.minute}:${e.second}`; |
||||
|
data.showEnd = true; |
||||
|
} |
||||
|
|
||||
|
// 选择结束时间 |
||||
|
function confirmEndTime(e) { |
||||
|
data.endTime = `${e.year}-${e.month}-${e.day} ${e.hour}:${e.minute}:${e.second}`; |
||||
|
} |
||||
|
|
||||
|
// 点击返回按钮 |
||||
|
function onBack() { |
||||
|
// eslint-disable-next-line no-undef |
||||
|
const pages = getCurrentPages(); // 获取页面栈数组 |
||||
|
console.log('历史pages: ', pages.length); |
||||
|
if (pages.length > 1) { |
||||
|
uni.navigateBack(); |
||||
|
} else { |
||||
|
// this.$u.route('/', { u: this.userId }); |
||||
|
uni.reLaunch({ url: `/pages/index/index?u=${userId.value}` }); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// LWBS提示 |
||||
|
function lwbs() { |
||||
|
// uni.$ui.showToast('LWBS'); |
||||
|
} |
||||
|
// 项目概览 |
||||
|
function projectOverview() { |
||||
|
// uni.$ui.showToast('项目概览'); |
||||
|
} |
||||
|
// 回到首页 |
||||
|
function openIndex() { |
||||
|
uni.webView.reLaunch({ url: `/pages/index/index?u=${this.userId}` }); |
||||
|
} |
||||
|
|
||||
|
// 操作 |
||||
|
function operation() { |
||||
|
// uni.$ui.showToast('操作'); |
||||
|
data.show = !data.show; |
||||
|
} |
||||
|
|
||||
|
// 新建项目 |
||||
|
function createTask() { |
||||
|
// 关闭 ... 弹窗 |
||||
|
data.show = false; |
||||
|
// 打开遮罩 |
||||
|
data.maskShow = true; |
||||
|
// 打开新建项目弹窗 |
||||
|
data.createTaskShow = true; |
||||
|
} |
||||
|
|
||||
|
// 分享项目 |
||||
|
function share() { |
||||
|
// 关闭 ... 弹窗 |
||||
|
data.show = false; |
||||
|
// 打开遮罩 |
||||
|
data.maskShow = true; |
||||
|
// 打开分享项目弹窗 |
||||
|
data.secondShow = true; |
||||
|
} |
||||
|
|
||||
|
// 点击遮罩,关闭弹窗 |
||||
|
function closeMask() { |
||||
|
// 关闭遮罩 |
||||
|
data.maskShow = false; |
||||
|
// 关闭分享项目弹窗 |
||||
|
data.secondShow = false; |
||||
|
// 关闭新建项目弹窗 |
||||
|
data.createTaskShow = false; |
||||
|
} |
||||
</script> |
</script> |
||||
|
|
||||
<style> |
<style lang="scss" scoped> |
||||
|
.second-popup { |
||||
|
background: #ffffff; |
||||
|
position: fixed; |
||||
|
left: 50%; |
||||
|
top: 50%; |
||||
|
transform: translate(-50%, -50%); |
||||
|
z-index: 33; |
||||
|
border-radius: 5px; |
||||
|
width: 90%; |
||||
|
} |
||||
|
|
||||
|
.third-popup { |
||||
|
background: #ffffff; |
||||
|
position: fixed; |
||||
|
left: 50%; |
||||
|
top: 50%; |
||||
|
transform: translate(-50%, -50%); |
||||
|
z-index: 33; |
||||
|
border-radius: 5px; |
||||
|
width: 90%; |
||||
|
} |
||||
|
|
||||
|
.popup { |
||||
|
width: 40%; |
||||
|
background: #fff; |
||||
|
position: absolute; |
||||
|
right: 0; |
||||
|
z-index: 99; |
||||
|
padding: 15px; |
||||
|
color: black; |
||||
|
animation: opacity 0.5s ease-in; |
||||
|
} |
||||
|
|
||||
|
@keyframes opacity { |
||||
|
0% { |
||||
|
opacity: 0; |
||||
|
} |
||||
|
50% { |
||||
|
opacity: 0.8; |
||||
|
} |
||||
|
100% { |
||||
|
opacity: 1; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
::v-deep .u-slot-content { |
||||
|
min-width: 0; |
||||
|
} |
||||
|
::v-deep .u-dropdown__content { |
||||
|
min-height: 120px !important; |
||||
|
height: auto !important; |
||||
|
overflow-y: auto; |
||||
|
background: #fff !important; |
||||
|
transition: none !important; |
||||
|
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); |
||||
|
} |
||||
|
::v-deep .u-dropdown__menu__item .u-flex { |
||||
|
justify-content: space-between; |
||||
|
width: 100%; |
||||
|
height: 100%; |
||||
|
flex-wrap: nowrap; |
||||
|
border: 1px solid #afbed1; |
||||
|
padding: 0 8px; |
||||
|
} |
||||
|
::v-deep .u-dropdown__content__mask { |
||||
|
display: none; |
||||
|
} |
||||
</style> |
</style> |
||||
|
|||||
@ -0,0 +1,466 @@ |
|||||
|
<template> |
||||
|
<div class="new-projects-box"> |
||||
|
<div class="form"> |
||||
|
<!-- 项目名称 --> |
||||
|
<view class="mb-3 font-bold text-base flex justify-center text-black">新建任务</view> |
||||
|
<div class="flex items-center mb-2"> |
||||
|
<div>名称<span class="text-red-500">*</span>:</div> |
||||
|
<u-input max-length="5" v-model="name" :type="type" :border="border" /> |
||||
|
</div> |
||||
|
<!-- 起止时间 --> |
||||
|
<div class="mb-2"> |
||||
|
<div>起止时间:</div> |
||||
|
<u-input placeholder="请选择起止时间" v-model="timeValue" :type="type" :border="border" @click="$emit('showTime')" /> |
||||
|
</div> |
||||
|
<!-- 多选框 --> |
||||
|
<div class="flex justify-between items-center"> |
||||
|
<div>负责人<span class="text-red-500">*</span>:</div> |
||||
|
<div class="flex-1" v-if="hasRole">{{ roleName }}</div> |
||||
|
<div label="负责人" class="flex-1" v-else> |
||||
|
<u-dropdown disabled ref="uDropdown" placeholder="请选择负责人"> |
||||
|
<u-dropdown-item :title="roleList"> |
||||
|
<view class="slot-content bg-white"> |
||||
|
<div |
||||
|
class="flex flex-row justify-between mb-1 drop-item" |
||||
|
v-for="(role, roleIndex) in roleOptions" |
||||
|
:key="roleIndex" |
||||
|
@click="change(roleIndex)" |
||||
|
> |
||||
|
<view v-model="role.id">{{ role.name }}</view> |
||||
|
<u-icon v-if="role.dropdownShow" name="checkbox-mark" color="#2979ff" size="28"></u-icon> |
||||
|
</div> |
||||
|
</view> |
||||
|
</u-dropdown-item> |
||||
|
</u-dropdown> |
||||
|
</div> |
||||
|
</div> |
||||
|
<!-- 下拉图标 --> |
||||
|
<div class="flex justify-center my-6"> |
||||
|
<u-icon v-if="arrow" name="arrow-down" size="28" @click="openDropdown"></u-icon> |
||||
|
<u-icon v-else name="arrow-up" size="28" @click="closeSecondDropdown"></u-icon> |
||||
|
</div> |
||||
|
<!-- 下拉框的内容 --> |
||||
|
<div v-if="show" class="mb-6"> |
||||
|
<!-- 描述 --> |
||||
|
<div class="flex items-center mb-2"> |
||||
|
<div>描述:</div> |
||||
|
<u-input v-model="description" max-length="48" type="textarea" height="36" auto-height :border="border" /> |
||||
|
</div> |
||||
|
<!-- 所属项目 --> |
||||
|
<div class="w flex items-center mb-2"> |
||||
|
<div>所属项目<span class="text-red-500">*</span>:</div> |
||||
|
<div>{{ project.name }}</div> |
||||
|
</div> |
||||
|
<!-- 所属任务 --> |
||||
|
<div class="w flex items-center mb-2" v-if="task && task.id"> |
||||
|
<div>所属任务:</div> |
||||
|
<div>{{ task.name }}</div> |
||||
|
</div> |
||||
|
<!-- 上道工序 --> |
||||
|
<div class="flex items-center mb-2"> |
||||
|
<div>上道工序:</div> |
||||
|
<InputSearch |
||||
|
@searchPrevTask="searchPrevTask" |
||||
|
:dataSource="allTasks" |
||||
|
@select="handleChange" |
||||
|
@clearAllTasks="clearAllTasks" |
||||
|
placeholder="请输入上道工序" |
||||
|
/> |
||||
|
</div> |
||||
|
<!-- 检查人多选框 --> |
||||
|
<div class="flex justify-between items-center"> |
||||
|
<div>检查人<span class="text-red-500">*</span>:</div> |
||||
|
<div label="检查人" class="flex-1"> |
||||
|
<u-dropdown ref="dropdown"> |
||||
|
<u-dropdown-item :title="checkerList"> |
||||
|
<view class="slot-content bg-white"> |
||||
|
<div |
||||
|
class="flex flex-row justify-between mb-1 drop-item" |
||||
|
v-for="(checkoutOption, Index) in checkoutOptions" |
||||
|
:key="Index" |
||||
|
@click="choose(Index)" |
||||
|
> |
||||
|
<view v-model="checkoutOption.value">{{ checkoutOption.name }}</view> |
||||
|
<u-icon v-if="checkoutOption.dropdownShow" name="checkbox-mark" color="#2979ff" size="28"></u-icon> |
||||
|
</div> |
||||
|
</view> |
||||
|
</u-dropdown-item> |
||||
|
</u-dropdown> |
||||
|
</div> |
||||
|
</div> |
||||
|
<!-- 是否是日常任务 --> |
||||
|
<div class="flex justify-between items-center mt-6"> |
||||
|
是否是日常任务: |
||||
|
<u-switch v-model="isGlobal" size="28"></u-switch> |
||||
|
</div> |
||||
|
<div class="mt-6"> |
||||
|
<div>交付物:</div> |
||||
|
<div v-for="(sort, sortIndex) in deliverSort" :key="sortIndex"> |
||||
|
<u-input |
||||
|
@blur="addDeliverInput" |
||||
|
v-model="sort.name" |
||||
|
:placeholder="`交付物名称${sortIndex + 1}`" |
||||
|
:type="type" |
||||
|
:border="border" |
||||
|
/> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="flex items-center mb-6"> |
||||
|
<u-button type="primary" size="medium" @click="setParameters">提交</u-button> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import { mapState, mapGetters, mapMutations, mapActions } from 'vuex'; |
||||
|
|
||||
|
export default { |
||||
|
props: { |
||||
|
startTime: { |
||||
|
type: String, |
||||
|
default: '', |
||||
|
}, |
||||
|
endTime: { |
||||
|
type: String, |
||||
|
default: '', |
||||
|
}, |
||||
|
task: { |
||||
|
type: Object, |
||||
|
default: null, |
||||
|
}, |
||||
|
}, |
||||
|
data() { |
||||
|
return { |
||||
|
arrow: true, |
||||
|
show: false, |
||||
|
isGlobal: false, //是否日常任务 |
||||
|
name: '', //名称 |
||||
|
showChooseTime: false, |
||||
|
timeValue: '', //起止时间 |
||||
|
description: '', //描述 |
||||
|
projectShow: false, //所属项目模糊搜索展示 |
||||
|
processTaskId: '', //上道工序 |
||||
|
type: 'text', |
||||
|
border: true, |
||||
|
roleList: undefined, //负责人默认多选 |
||||
|
checkerList: undefined, //检查人默认多选 |
||||
|
roleOptions: [], // 负责人下拉多选列表 |
||||
|
checkoutOptions: [], // 检查人下拉多选列表 |
||||
|
roleIdList: [], // 选中的负责人id |
||||
|
checkerIdList: [], // 选中的检查人id |
||||
|
deliverables: [], // 交付物 |
||||
|
deliverSort: [{ name: '' }], // 交付物排序 |
||||
|
allTasks: [], |
||||
|
roleName: '', // 负责人名字 |
||||
|
hasRole: false, // 有没有负责人 |
||||
|
}; |
||||
|
}, |
||||
|
|
||||
|
computed: { |
||||
|
...mapState('role', ['visibleRoles', 'roleId']), |
||||
|
...mapState('project', ['project']), |
||||
|
...mapState('task', ['tasks']), |
||||
|
...mapGetters('project', ['projectId']), |
||||
|
}, |
||||
|
|
||||
|
watch: { |
||||
|
endTime(val) { |
||||
|
if (val) { |
||||
|
this.timeValue = this.startTime + ' 至 ' + val; |
||||
|
} |
||||
|
}, |
||||
|
}, |
||||
|
|
||||
|
mounted() { |
||||
|
// 获取负责人和检查人列表 |
||||
|
if (this.visibleRoles.length) { |
||||
|
this.visibleRoles.forEach(role => { |
||||
|
role.dropdownShow = false; |
||||
|
role.status = false; |
||||
|
}); |
||||
|
} |
||||
|
this.roleOptions = this.$u.deepClone(this.visibleRoles); |
||||
|
this.checkoutOptions = this.$u.deepClone(this.visibleRoles); |
||||
|
|
||||
|
// 判断有没有负责人 是不是添加子任务 |
||||
|
if (this.roleId) { |
||||
|
const item = this.visibleRoles.find(r => r.id === this.roleId); |
||||
|
if (item) { |
||||
|
this.roleName = item.name; |
||||
|
this.hasRole = true; |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
methods: { |
||||
|
...mapMutations('task', ['updateTasks']), |
||||
|
...mapActions('task', ['getPermanent']), |
||||
|
|
||||
|
// 负责人下拉多选选中 |
||||
|
change(index) { |
||||
|
let arr = [...this.roleOptions]; |
||||
|
// 选择多选项图标的展示 |
||||
|
arr[index].dropdownShow = !arr[index].dropdownShow; |
||||
|
// 多选展示的改变 |
||||
|
this.roleList = arr[index].name; |
||||
|
let shows = ''; |
||||
|
// 遍历arr,如果选中,添加到多选展示框上 |
||||
|
arr.map(val => { |
||||
|
if (val.dropdownShow === true) { |
||||
|
shows += val.name + ','; |
||||
|
this.roleIdList.push(val.id); |
||||
|
} |
||||
|
}); |
||||
|
this.roleOptions = [...arr]; |
||||
|
// 删除最后的',' |
||||
|
this.roleList = shows.slice(0, shows.length - 1); |
||||
|
}, |
||||
|
|
||||
|
// 检查人下拉多选选中 |
||||
|
choose(index) { |
||||
|
let arr = [...this.checkoutOptions]; |
||||
|
// 选择多选项图标的展示 |
||||
|
arr[index].dropdownShow = !arr[index].dropdownShow; |
||||
|
// 多选展示的改变 |
||||
|
this.checkerList = arr[index].name; |
||||
|
let shows = ''; |
||||
|
// 遍历arr,如果选中,添加到多选展示框上 |
||||
|
arr.map(val => { |
||||
|
if (val.dropdownShow === true) { |
||||
|
shows += val.name + ','; |
||||
|
this.checkerIdList.push(val.id); |
||||
|
} |
||||
|
}); |
||||
|
this.checkoutOptions = [...arr]; |
||||
|
// 删除最后的',' |
||||
|
this.checkerList = shows.slice(0, shows.length - 1); |
||||
|
// this.roleList = arr[value - 1].name; |
||||
|
}, |
||||
|
|
||||
|
// 打开下拉框 |
||||
|
openDropdown() { |
||||
|
this.arrow = !this.arrow; |
||||
|
this.show = true; |
||||
|
}, |
||||
|
|
||||
|
// 关闭下拉框 |
||||
|
closeSecondDropdown() { |
||||
|
this.arrow = !this.arrow; |
||||
|
this.show = false; |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 模糊查询 查找项目下的任务 |
||||
|
* @param name 任务名 |
||||
|
* @param projectId 项目id |
||||
|
*/ |
||||
|
async searchPrevTask(val) { |
||||
|
try { |
||||
|
const params = { name: val, projectId: this.projectId }; |
||||
|
const data = await this.$u.api.queryTaskOfProject(params); |
||||
|
this.allTasks = data; |
||||
|
return data; |
||||
|
} catch (error) { |
||||
|
console.error('error: ', error); |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
//用户点击获取的数据 |
||||
|
handleChange(data) { |
||||
|
console.log('data', data); |
||||
|
this.processTaskId = data.detailId; |
||||
|
}, |
||||
|
// 清空模糊查询信息 |
||||
|
clearAllTasks() { |
||||
|
this.allTasks = []; |
||||
|
}, |
||||
|
|
||||
|
// 数组最后一项有值 添加一条交付物输入框 |
||||
|
addDeliverInput() { |
||||
|
if (this.deliverSort[this.deliverSort.length - 1].name) { |
||||
|
this.deliverSort.push({ name: '' }); |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
// 设置提交参数 |
||||
|
async setParameters() { |
||||
|
const { |
||||
|
projectId, |
||||
|
task, |
||||
|
name, |
||||
|
startTime, |
||||
|
endTime, |
||||
|
hasRole, |
||||
|
roleIdList, |
||||
|
roleId, |
||||
|
description, |
||||
|
processTaskId, |
||||
|
checkerIdList, |
||||
|
isGlobal, |
||||
|
} = this; |
||||
|
if (!name) { |
||||
|
uni.$uiowToast('请输入名称'); |
||||
|
return; |
||||
|
} |
||||
|
if ((!roleIdList || !roleIdList.length) && !hasRole) { |
||||
|
uni.$uiowToast('请选择负责人'); |
||||
|
return; |
||||
|
} |
||||
|
if (!checkerIdList || !checkerIdList.length) { |
||||
|
uni.$uiowToast('请选择检查人'); |
||||
|
return; |
||||
|
} |
||||
|
const deliverList = []; |
||||
|
this.deliverSort.forEach(item => { |
||||
|
if (item.name) { |
||||
|
deliverList.push(item.name); |
||||
|
} |
||||
|
}); |
||||
|
const params = { |
||||
|
name, |
||||
|
startTime: startTime ? this.$moment(startTime).format('x') - 0 : '', |
||||
|
endTime: endTime ? this.$moment(endTime).format('x') - 0 : '', |
||||
|
roleIdList: hasRole ? [roleId] : roleIdList, |
||||
|
description, |
||||
|
projectId, |
||||
|
parentTaskId: task && task.id ? task.id : '', // 父任务 |
||||
|
processTaskId, // 上道工序 TODO |
||||
|
checkerIdList, |
||||
|
global: isGlobal ? 1 : 0, |
||||
|
deliverList, |
||||
|
}; |
||||
|
await this.handleSubmit(params); |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 新建任务 |
||||
|
* @param name 任务名 |
||||
|
* @param startTime 开始时间 |
||||
|
* @param endTime 结束时间 |
||||
|
* @param roleIdList 负责人id(数组) |
||||
|
* @param description 描述 |
||||
|
* @param projectId 所属项目id |
||||
|
* @param parentTaskId 所属任务id |
||||
|
* @param processTaskId 上道工序(任务id) |
||||
|
* @param checkerIdList 检查人id(数组) |
||||
|
* @param global 是否日常任务 0否 1是 |
||||
|
* @param deliverList 交付物名字(数组) |
||||
|
*/ |
||||
|
async handleSubmit(params) { |
||||
|
try { |
||||
|
const data = await this.$u.api.saveTask(params); |
||||
|
// TODO 任务新建成功 继续 or 取消 |
||||
|
this.$emit('closeMask'); |
||||
|
const newTasks = { |
||||
|
data: data[0], |
||||
|
processTaskId: params.processTaskId, |
||||
|
}; |
||||
|
// 将新加的任务加到store |
||||
|
// 判断不是子任务 |
||||
|
if (!this.task || !this.task.id) { |
||||
|
this.addNewTasks(newTasks); |
||||
|
} |
||||
|
} catch (error) { |
||||
|
this.$emit('closeMask'); |
||||
|
console.error('error: ', error); |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
// 添加任务后更新tasks |
||||
|
addNewTasks(data) { |
||||
|
const oldTasks = this.$u.deepClone(this.tasks); |
||||
|
let res = data.data; |
||||
|
console.log('添加任务后更新tasks', data); |
||||
|
// 判断有没有选择上道工序 |
||||
|
if (data.processTaskId) { |
||||
|
const index = oldTasks.find(item => item.detailId === data.processTaskId); |
||||
|
if (index) { |
||||
|
oldTasks.splice(index + 1, 0, res); |
||||
|
} |
||||
|
} else { |
||||
|
this.setAddPosition(res, oldTasks); |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
// 设置添加位置 |
||||
|
setAddPosition(res, oldTasks) { |
||||
|
console.log('设置添加位置', res, oldTasks); |
||||
|
|
||||
|
if (res.planStart - 0 < oldTasks[0].planStart - 0) { |
||||
|
// 开始时间小于列表的第一个 插入最前面 |
||||
|
oldTasks.splice(0, 0, res); |
||||
|
} else if (res.planStart - 0 === oldTasks[0].planStart - 0) { |
||||
|
// 开始时间等于列表的第一个 插入第二 |
||||
|
oldTasks.splice(1, 0, res); |
||||
|
} else if (res.planStart - 0 >= oldTasks[oldTasks.length - 1].planStart - 0) { |
||||
|
// 开始时间大等于列表的最后一个 插入最后 |
||||
|
oldTasks.splice(-1, 0, res); |
||||
|
} else { |
||||
|
// 判断开始时间在列表中间的哪个位置 |
||||
|
for (let i = 0; i < oldTasks.length; i++) { |
||||
|
const item = oldTasks[i]; |
||||
|
if (res.planStart - 0 > item.planStart - 0) { |
||||
|
if (res.planStart - 0 <= oldTasks[i + 1].planStart - 0) { |
||||
|
oldTasks.splice(i + 1, 0, res); |
||||
|
// console.log('res: ', res); |
||||
|
// return; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
// TODO: 不能全更新 |
||||
|
console.log('oldTasks: ', oldTasks); |
||||
|
this.updateTasks([...oldTasks]); |
||||
|
const params = { roleId: this.roleId, projectId: this.projectId }; |
||||
|
this.getPermanent(params); |
||||
|
}, |
||||
|
}, |
||||
|
}; |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss" scoped> |
||||
|
.form { |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
width: 100%; |
||||
|
max-height: 400px; |
||||
|
overflow-y: scroll; |
||||
|
} |
||||
|
|
||||
|
.drop-item { |
||||
|
border-bottom: 1px solid #f1f1f1; |
||||
|
padding: 16rpx; |
||||
|
} |
||||
|
|
||||
|
::v-deep.u-input--border { |
||||
|
border: none; |
||||
|
border-radius: 0; |
||||
|
} |
||||
|
::v-deep.u-dropdown__menu__item > uni-view { |
||||
|
border: none !important; |
||||
|
padding: 5px; |
||||
|
} |
||||
|
.u-input { |
||||
|
border-bottom: 1px solid #dcdfe6; |
||||
|
} |
||||
|
|
||||
|
.new-projects-box { |
||||
|
margin-top: 20px; |
||||
|
padding: 15px; |
||||
|
width: 100%; |
||||
|
overflow: hidden; |
||||
|
} |
||||
|
|
||||
|
.w { |
||||
|
width: 300px; |
||||
|
height: 39px; |
||||
|
} |
||||
|
|
||||
|
::v-deep .u-dropdown__menu__item .u-flex { |
||||
|
border: 0 !important; |
||||
|
border-bottom: 1px solid #dcdfe6 !important; |
||||
|
padding: 0 20rpx; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,583 @@ |
|||||
|
<template> |
||||
|
<div class="new-projects-box"> |
||||
|
<div class="form"> |
||||
|
<!-- 项目名称 --> |
||||
|
<view class="new-projects-title font-bold text-base flex justify-center items-center text-black">新建任务</view> |
||||
|
|
||||
|
<div class="form-item flex items-center"> |
||||
|
<div class="mr-4">名称<span class="text-red-500">*</span></div> |
||||
|
<u-input :maxlength="8" v-model="name" type="text" :inputAlign="'right'" :clearable="false" placeholder="请输入任务名称" /> |
||||
|
</div> |
||||
|
|
||||
|
<!-- 开始时间 --> |
||||
|
<div class="form-item flex items-center"> |
||||
|
<div class="mr-4">开始时间<span class="text-red-500" v-if="source === 'regular'">*</span></div> |
||||
|
<div class="flex justify-end items-center flex-1"> |
||||
|
<u-input |
||||
|
placeholder="请选择开始时间" |
||||
|
v-model="startTime" |
||||
|
type="text" |
||||
|
:inputAlign="'right'" |
||||
|
:clearable="false" |
||||
|
@click="$emit('showTime', 1)" |
||||
|
/> |
||||
|
<u-icon class="ml-1" name="arrow-right" color="#969799" size="28"></u-icon> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<!-- 结束时间 --> |
||||
|
<div class="form-item flex items-center"> |
||||
|
<div class="mr-4">结束时间<span class="text-red-500" v-if="source === 'regular'">*</span></div> |
||||
|
<div class="flex justify-end items-center flex-1"> |
||||
|
<u-input |
||||
|
placeholder="请选择结束时间" |
||||
|
v-model="endTime" |
||||
|
type="text" |
||||
|
:inputAlign="'right'" |
||||
|
:clearable="false" |
||||
|
@click="$emit('showTime', 2)" |
||||
|
/> |
||||
|
<u-icon class="ml-1" name="arrow-right" color="#969799" size="28"></u-icon> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<!-- 多选框 --> |
||||
|
<div class="form-item flex justify-between items-center"> |
||||
|
<div class="mr-4">负责人<span class="text-red-500">*</span></div> |
||||
|
<div class="flex-1 text-right" v-if="hasRole">{{ roleName }}</div> |
||||
|
<div label="负责人" class="flex-1" v-else> |
||||
|
<u-dropdown disabled ref="uDropdown" placeholder="请选择负责人"> |
||||
|
<u-dropdown-item :title="roleList"> |
||||
|
<view class="slot-content bg-white"> |
||||
|
<div |
||||
|
class="flex flex-row justify-between mb-1 drop-item" |
||||
|
v-for="(role, roleIndex) in roleOptions" |
||||
|
:key="roleIndex" |
||||
|
@click="change(roleIndex)" |
||||
|
> |
||||
|
<view>{{ role.name }}</view> |
||||
|
<u-icon v-if="role.dropdownShow" name="checkbox-mark" color="#2979ff" size="28"></u-icon> |
||||
|
</div> |
||||
|
</view> |
||||
|
</u-dropdown-item> |
||||
|
</u-dropdown> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<!-- 描述 --> |
||||
|
<div class="form-item flex items-center"> |
||||
|
<div class="mr-4">描述</div> |
||||
|
<u-input v-model="description" :maxlength="48" type="text" :inputAlign="'right'" :clearable="false" /> |
||||
|
</div> |
||||
|
|
||||
|
<!-- 所属项目 --> |
||||
|
<div class="form-item flex items-center"> |
||||
|
<div class="mr-4">所属项目<span class="text-red-500">*</span></div> |
||||
|
<div class="flex-1 text-right">{{ project.name }}</div> |
||||
|
</div> |
||||
|
|
||||
|
<!-- 所属任务 --> |
||||
|
<div class="form-item flex items-center" v-if="task && task.id"> |
||||
|
<div class="mr-4">所属任务</div> |
||||
|
<div class="flex-1 text-right">{{ task.name }}</div> |
||||
|
</div> |
||||
|
|
||||
|
<!-- 上道工序 --> |
||||
|
<div class="form-item flex items-center"> |
||||
|
<div class="mr-4">上道工序</div> |
||||
|
<InputSearch |
||||
|
@searchPrevTask="searchPrevTask" |
||||
|
:dataSource="allTasks" |
||||
|
@select="handleChange" |
||||
|
@clearAllTasks="clearAllTasks" |
||||
|
placeholder="请输入上道工序" |
||||
|
/> |
||||
|
</div> |
||||
|
|
||||
|
<!-- 检查人多选框 --> |
||||
|
<div class="form-item flex justify-between items-center"> |
||||
|
<div class="mr-4 flex-shrink-0">检查人<span class="text-red-500">*</span></div> |
||||
|
<div label="检查人" class="flex-1" style="width: calc(100% - 65px)"> |
||||
|
<u-dropdown ref="dropdown"> |
||||
|
<u-dropdown-item :title="checkerList"> |
||||
|
<view class="slot-content bg-white"> |
||||
|
<div |
||||
|
class="flex flex-row justify-between mb-1 drop-item" |
||||
|
v-for="(checkoutOption, Index) in checkoutOptions" |
||||
|
:key="Index" |
||||
|
@click="choose(Index)" |
||||
|
> |
||||
|
<view>{{ checkoutOption.name }}</view> |
||||
|
<u-icon v-if="checkoutOption.dropdownShow" name="checkbox-mark" color="#2979ff" size="28"></u-icon> |
||||
|
</div> |
||||
|
</view> |
||||
|
</u-dropdown-item> |
||||
|
</u-dropdown> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<!-- 是否是日常任务 --> |
||||
|
<div class="form-item flex justify-between items-center" v-if="!source"> |
||||
|
是否是日常任务 |
||||
|
<u-switch v-model="isGlobal" size="28"></u-switch> |
||||
|
</div> |
||||
|
|
||||
|
<div class="form-item flex justify-between items-center" v-for="(sort, sortIndex) in deliverSort" :key="sortIndex"> |
||||
|
<div class="mr-4">{{ `交付物${sortIndex + 1}` }}</div> |
||||
|
<div class="flex-1"> |
||||
|
<u-input |
||||
|
@blur="addDeliverInput" |
||||
|
v-model="sort.name" |
||||
|
:placeholder="`交付物名称${sortIndex + 1}`" |
||||
|
type="text" |
||||
|
:inputAlign="'right'" |
||||
|
:clearable="false" |
||||
|
/> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<!-- <div class="form-item flex items-center"> |
||||
|
<div class="mr-4">添加插件</div> |
||||
|
<u-input :maxlength="8" v-model="name" type="text" :inputAlign="'right'" :clearable="false" placeholder="选择插件" /> |
||||
|
</div> --> |
||||
|
|
||||
|
<div class="form-btn flex items-center mt-4"> |
||||
|
<u-button type="primary" size="medium" @click="setParameters">提交</u-button> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import { mapState, mapGetters, mapMutations, mapActions } from 'vuex'; |
||||
|
|
||||
|
export default { |
||||
|
props: { |
||||
|
startTime: { |
||||
|
type: String, |
||||
|
default: '', |
||||
|
}, |
||||
|
endTime: { |
||||
|
type: String, |
||||
|
default: '', |
||||
|
}, |
||||
|
task: { |
||||
|
type: Object, |
||||
|
default: null, |
||||
|
}, |
||||
|
source: { |
||||
|
type: String, |
||||
|
default: '', |
||||
|
}, |
||||
|
}, |
||||
|
data() { |
||||
|
return { |
||||
|
// arrow: true, |
||||
|
// show: false, |
||||
|
isGlobal: false, //是否日常任务 |
||||
|
name: '', //名称 |
||||
|
// showChooseTime: false, |
||||
|
// timeValue: '', //起止时间 |
||||
|
// taskStartTime: '', // 新建任务开始时间 |
||||
|
// taskEndTime: '', // 新建任务结束时间 |
||||
|
description: '', //描述 |
||||
|
// projectShow: false, //所属项目模糊搜索展示 |
||||
|
processTaskId: '', //上道工序 |
||||
|
// type: 'text', |
||||
|
// border: true, |
||||
|
roleList: undefined, //负责人默认多选 |
||||
|
checkerList: undefined, //检查人默认多选 |
||||
|
roleOptions: [], // 负责人下拉多选列表 |
||||
|
checkoutOptions: [], // 检查人下拉多选列表 |
||||
|
roleIdList: [], // 选中的负责人id |
||||
|
checkerIdList: [], // 选中的检查人id |
||||
|
deliverables: [], // 交付物 |
||||
|
deliverSort: [{ name: '' }], // 交付物排序 |
||||
|
allTasks: [], |
||||
|
roleName: '', // 负责人名字 |
||||
|
hasRole: false, // 有没有负责人 |
||||
|
}; |
||||
|
}, |
||||
|
|
||||
|
computed: { |
||||
|
...mapState('role', ['visibleRoles', 'roleId']), |
||||
|
...mapState('project', ['project']), |
||||
|
...mapState('task', ['tasks']), |
||||
|
...mapGetters('project', ['projectId']), |
||||
|
}, |
||||
|
|
||||
|
// watch: { |
||||
|
// endTime(val) { |
||||
|
// if (val) { |
||||
|
// this.timeValue = this.startTime + ' 至 ' + val; |
||||
|
// } |
||||
|
// }, |
||||
|
// }, |
||||
|
|
||||
|
mounted() { |
||||
|
// 获取负责人和检查人列表 |
||||
|
if (this.visibleRoles.length) { |
||||
|
this.visibleRoles.forEach(role => { |
||||
|
role.dropdownShow = false; |
||||
|
role.status = false; |
||||
|
}); |
||||
|
} |
||||
|
this.roleOptions = this.$u.deepClone(this.visibleRoles); |
||||
|
this.checkoutOptions = this.$u.deepClone(this.visibleRoles); |
||||
|
|
||||
|
// 判断有没有负责人 是不是添加子任务 |
||||
|
if (this.roleId) { |
||||
|
const item = this.visibleRoles.find(r => r.id === this.roleId); |
||||
|
if (item) { |
||||
|
this.roleName = item.name; |
||||
|
this.hasRole = true; |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
methods: { |
||||
|
...mapMutations('task', ['updateTasks']), |
||||
|
...mapActions('task', ['getPermanent']), |
||||
|
|
||||
|
// 负责人下拉多选选中 |
||||
|
change(index) { |
||||
|
let arr = [...this.roleOptions]; |
||||
|
// 选择多选项图标的展示 |
||||
|
arr[index].dropdownShow = !arr[index].dropdownShow; |
||||
|
// 多选展示的改变 |
||||
|
this.roleList = arr[index].name; |
||||
|
let shows = ''; |
||||
|
// 遍历arr,如果选中,添加到多选展示框上 |
||||
|
arr.map(val => { |
||||
|
if (val.dropdownShow === true) { |
||||
|
shows += val.name + ','; |
||||
|
this.roleIdList.push(val.id); |
||||
|
} |
||||
|
}); |
||||
|
this.roleOptions = [...arr]; |
||||
|
// 删除最后的',' |
||||
|
this.roleList = shows.slice(0, shows.length - 1); |
||||
|
}, |
||||
|
|
||||
|
// 检查人下拉多选选中 |
||||
|
choose(index) { |
||||
|
let arr = [...this.checkoutOptions]; |
||||
|
// 选择多选项图标的展示 |
||||
|
arr[index].dropdownShow = !arr[index].dropdownShow; |
||||
|
// 多选展示的改变 |
||||
|
this.checkerList = arr[index].name; |
||||
|
let shows = ''; |
||||
|
// 遍历arr,如果选中,添加到多选展示框上 |
||||
|
arr.map(val => { |
||||
|
if (val.dropdownShow === true) { |
||||
|
shows += val.name + ','; |
||||
|
this.checkerIdList.push(val.id); |
||||
|
} |
||||
|
}); |
||||
|
this.checkoutOptions = [...arr]; |
||||
|
// 删除最后的',' |
||||
|
this.checkerList = shows.slice(0, shows.length - 1); |
||||
|
// this.roleList = arr[value - 1].name; |
||||
|
}, |
||||
|
|
||||
|
// 打开下拉框 |
||||
|
// openDropdown() { |
||||
|
// this.arrow = !this.arrow; |
||||
|
// this.show = true; |
||||
|
// }, |
||||
|
|
||||
|
// // 关闭下拉框 |
||||
|
// closeSecondDropdown() { |
||||
|
// this.arrow = !this.arrow; |
||||
|
// this.show = false; |
||||
|
// }, |
||||
|
|
||||
|
/** |
||||
|
* 模糊查询 查找项目下的任务 |
||||
|
* @param name 任务名 |
||||
|
* @param projectId 项目id |
||||
|
*/ |
||||
|
async searchPrevTask(val) { |
||||
|
try { |
||||
|
const params = { name: val, projectId: this.projectId }; |
||||
|
const data = await this.$u.api.queryTaskOfProject(params); |
||||
|
this.allTasks = data; |
||||
|
return data; |
||||
|
} catch (error) { |
||||
|
console.error('error: ', error); |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
//用户点击获取的数据 |
||||
|
handleChange(data) { |
||||
|
console.log('data', data); |
||||
|
this.processTaskId = data.detailId; |
||||
|
}, |
||||
|
// 清空模糊查询信息 |
||||
|
clearAllTasks() { |
||||
|
this.allTasks = []; |
||||
|
}, |
||||
|
|
||||
|
// 数组最后一项有值 添加一条交付物输入框 |
||||
|
addDeliverInput() { |
||||
|
if (this.deliverSort[this.deliverSort.length - 1].name) { |
||||
|
this.deliverSort.push({ name: '' }); |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
// 设置提交参数 |
||||
|
async setParameters() { |
||||
|
if (this.source) { |
||||
|
this.isGlobal = 0; |
||||
|
} |
||||
|
|
||||
|
const { |
||||
|
projectId, |
||||
|
task, |
||||
|
name, |
||||
|
startTime, |
||||
|
endTime, |
||||
|
hasRole, |
||||
|
roleIdList, |
||||
|
roleId, |
||||
|
description, |
||||
|
processTaskId, |
||||
|
checkerIdList, |
||||
|
isGlobal, |
||||
|
} = this; |
||||
|
if (!name) { |
||||
|
uni.$ui.showToast('请输入任务名称'); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
if (!isGlobal) { |
||||
|
if (!startTime) { |
||||
|
uni.$ui.showToast('定期任务时间不能为空'); |
||||
|
return; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (startTime && endTime) { |
||||
|
let start = startTime ? this.$moment(startTime).format('x') - 0 : ''; |
||||
|
let end = endTime ? this.$moment(endTime).format('x') - 0 : ''; |
||||
|
if (start > end) { |
||||
|
uni.$ui.showToast('结束时间不能小于开始时间'); |
||||
|
return; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if ((!roleIdList || !roleIdList.length) && !hasRole) { |
||||
|
uni.$ui.showToast('请选择负责人'); |
||||
|
return; |
||||
|
} |
||||
|
if (!checkerIdList || !checkerIdList.length) { |
||||
|
uni.$ui.showToast('请选择检查人'); |
||||
|
return; |
||||
|
} |
||||
|
const deliverList = []; |
||||
|
this.deliverSort.forEach(item => { |
||||
|
if (item.name) { |
||||
|
deliverList.push(item.name); |
||||
|
} |
||||
|
}); |
||||
|
const params = { |
||||
|
name, |
||||
|
startTime: startTime ? this.$moment(startTime).format('x') - 0 : '', |
||||
|
endTime: endTime ? this.$moment(endTime).format('x') - 0 : '', |
||||
|
roleIdList: hasRole ? [roleId] : roleIdList, |
||||
|
description, |
||||
|
projectId, |
||||
|
parentTaskId: task && task.process !== 4 && task.id ? task.id : '', // 父任务 |
||||
|
processTaskId, // 上道工序 TODO |
||||
|
checkerIdList, |
||||
|
global: isGlobal ? 1 : 0, |
||||
|
deliverList, |
||||
|
}; |
||||
|
await this.handleSubmit(params); |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 新建任务 |
||||
|
* @param name 任务名 |
||||
|
* @param startTime 开始时间 |
||||
|
* @param endTime 结束时间 |
||||
|
* @param roleIdList 负责人id(数组) |
||||
|
* @param description 描述 |
||||
|
* @param projectId 所属项目id |
||||
|
* @param parentTaskId 所属任务id |
||||
|
* @param processTaskId 上道工序(任务id) |
||||
|
* @param checkerIdList 检查人id(数组) |
||||
|
* @param global 是否日常任务 0否 1是 |
||||
|
* @param deliverList 交付物名字(数组) |
||||
|
*/ |
||||
|
async handleSubmit(params) { |
||||
|
try { |
||||
|
const data = await this.$u.api.saveTask(params); |
||||
|
// TODO 任务新建成功 继续 or 取消 |
||||
|
this.$emit('closeMask'); |
||||
|
const newTasks = { |
||||
|
data: data[0], |
||||
|
processTaskId: params.processTaskId, |
||||
|
}; |
||||
|
// 将新加的任务加到store |
||||
|
// 判断不是子任务 -- 暂无子任务,暂不判断 |
||||
|
// if (!this.task || !this.task.id || this.task.process === 4) { |
||||
|
// this.addNewTasks(newTasks); |
||||
|
// } |
||||
|
|
||||
|
this.addNewTasks(newTasks); |
||||
|
} catch (error) { |
||||
|
this.$emit('closeMask'); |
||||
|
console.error('error: ', error); |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
// 添加任务后更新tasks |
||||
|
addNewTasks(data) { |
||||
|
const oldTasks = this.$u.deepClone(this.tasks); |
||||
|
let res = data.data; |
||||
|
|
||||
|
// 判断有没有选择上道工序 |
||||
|
if (data.processTaskId) { |
||||
|
const index = oldTasks.find(item => item.detailId === data.processTaskId); |
||||
|
if (index) { |
||||
|
oldTasks.splice(index + 1, 0, res); |
||||
|
} |
||||
|
} else { |
||||
|
this.setAddPosition(res, oldTasks); |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
// 设置添加位置 |
||||
|
setAddPosition(res, oldTasks) { |
||||
|
if (res.planStart - 0 < oldTasks[0].planStart - 0) { |
||||
|
// 开始时间小于列表的第一个 插入最前面 |
||||
|
oldTasks.splice(0, 0, res); |
||||
|
} else if (res.planStart - 0 === oldTasks[0].planStart - 0) { |
||||
|
// 开始时间等于列表的第一个 插入第二 |
||||
|
oldTasks.splice(1, 0, res); |
||||
|
} else if (res.planStart - 0 >= oldTasks[oldTasks.length - 1].planStart - 0) { |
||||
|
// 开始时间大等于列表的最后一个 插入最后 |
||||
|
oldTasks.splice(-1, 0, res); |
||||
|
} else { |
||||
|
// 判断开始时间在列表中间的哪个位置 |
||||
|
for (let i = 0; i < oldTasks.length; i++) { |
||||
|
const item = oldTasks[i]; |
||||
|
if (res.planStart - 0 > item.planStart - 0) { |
||||
|
if (res.planStart - 0 <= oldTasks[i + 1].planStart - 0) { |
||||
|
oldTasks.splice(i + 1, 0, res); |
||||
|
// console.log('res: ', res); |
||||
|
// return; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
// TODO: 不能全更新 |
||||
|
console.log('oldTasks: ', oldTasks); |
||||
|
this.updateTasks([...oldTasks]); |
||||
|
const params = { roleId: this.roleId, projectId: this.projectId }; |
||||
|
this.getPermanent(params); |
||||
|
}, |
||||
|
}, |
||||
|
}; |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss" scoped> |
||||
|
.form { |
||||
|
// display: flex; |
||||
|
// flex-direction: column; |
||||
|
width: 100%; |
||||
|
max-height: 400px; |
||||
|
overflow-y: scroll; |
||||
|
} |
||||
|
|
||||
|
.drop-item { |
||||
|
border-bottom: 1px solid #f1f1f1; |
||||
|
padding: 16rpx 32rpx; |
||||
|
} |
||||
|
|
||||
|
// ::v-deep.u-input--border { |
||||
|
// border: none; |
||||
|
// border-radius: 0; |
||||
|
// padding: 0 !important; |
||||
|
// } |
||||
|
::v-deep.u-dropdown__menu__item > uni-view { |
||||
|
border: none !important; |
||||
|
padding: 5px; |
||||
|
} |
||||
|
// .u-input { |
||||
|
// border-bottom: 1px solid #dcdfe6; |
||||
|
// } |
||||
|
|
||||
|
.new-projects-box { |
||||
|
margin-top: 20px; |
||||
|
// padding: 15px; |
||||
|
width: 100%; |
||||
|
overflow: hidden; |
||||
|
color: #595959; |
||||
|
} |
||||
|
|
||||
|
.new-projects-title { |
||||
|
height: 60px; |
||||
|
} |
||||
|
|
||||
|
.form-item { |
||||
|
padding: 0 16px; |
||||
|
height: 48px; |
||||
|
border-bottom: 1px solid rgba(0, 0, 0, 0.1); |
||||
|
} |
||||
|
|
||||
|
.form-item ::v-deep .input-group uni-input { |
||||
|
border: 0; |
||||
|
font-size: 14px; |
||||
|
text-align: right; |
||||
|
} |
||||
|
|
||||
|
.form-btn { |
||||
|
height: 60px; |
||||
|
} |
||||
|
|
||||
|
.form-btn .u-btn { |
||||
|
width: 156px; |
||||
|
height: 32px; |
||||
|
font-size: 16px; |
||||
|
border-radius: 4px; |
||||
|
} |
||||
|
|
||||
|
.w { |
||||
|
width: 300px; |
||||
|
height: 39px; |
||||
|
} |
||||
|
|
||||
|
::v-deep .u-dropdown__menu { |
||||
|
height: 48px !important; |
||||
|
} |
||||
|
|
||||
|
::v-deep .u-dropdown__menu__item { |
||||
|
width: 100%; |
||||
|
} |
||||
|
|
||||
|
::v-deep .u-dropdown__menu__item .u-flex { |
||||
|
border: 0 !important; |
||||
|
// border-bottom: 1px solid #dcdfe6 !important; |
||||
|
padding: 0 !important; |
||||
|
} |
||||
|
|
||||
|
::v-deep .u-dropdown__menu__item__arrow { |
||||
|
margin-left: 10px; |
||||
|
flex-shrink: 0; |
||||
|
} |
||||
|
|
||||
|
::v-deep .u-dropdown__menu__item__text { |
||||
|
width: calc(100% - 23px); |
||||
|
text-align: right; |
||||
|
overflow: hidden; |
||||
|
text-overflow: ellipsis; |
||||
|
white-space: nowrap; |
||||
|
} |
||||
|
|
||||
|
::v-deep .u-dropdown__content { |
||||
|
top: 48px !important; |
||||
|
box-shadow: 0 4px 6px 1px rgba(0, 0, 0, 0.1) !important; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,210 @@ |
|||||
|
<template> |
||||
|
<view class="flex justify-center"> |
||||
|
<view class="content p-3 pb-8"> |
||||
|
<view class="mb-3 font-bold text-base flex justify-center">创建分享链接</view> |
||||
|
<view class="flex flex-col"> |
||||
|
<view class="mb-1">用户以什么角色加入项目</view> |
||||
|
<!-- 下拉多选 --> |
||||
|
<view class="uni-list"> |
||||
|
<view class="uni-list-cell"> |
||||
|
<view class="uni-list-cell-db ml-2" v-if="rolesArray.length"> |
||||
|
<picker @change="changeRole" :value="index" :range="rolesArray"> |
||||
|
<view class="uni-input">{{ allRolesName[index].name }}</view> |
||||
|
</picker> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<!-- 复制链接 --> |
||||
|
<view class="link flex items-center mt-4"> |
||||
|
<view class="link-url">{{ links }}</view> |
||||
|
<u-button |
||||
|
style="border-radius: 0; height: 100%" |
||||
|
type="primary" |
||||
|
v-clipboard:copy="copyText" |
||||
|
v-clipboard:success="copySuccess" |
||||
|
v-clipboard:error="copyError" |
||||
|
> |
||||
|
复制链接 |
||||
|
</u-button> |
||||
|
</view> |
||||
|
<view @click="select"> |
||||
|
<!-- 全选按钮 --> |
||||
|
<!-- <view class="flex mt-4"> |
||||
|
<view> |
||||
|
<u-checkbox-group> |
||||
|
<u-checkbox v-model="checked" @change="checkedAll"></u-checkbox> |
||||
|
</u-checkbox-group> |
||||
|
</view> |
||||
|
<view>已选择({{ this.quantity }})</view> |
||||
|
<view style="color: #f37378; margin-left: 20px">批量删除</view> |
||||
|
</view> --> |
||||
|
<!-- 多选框 --> |
||||
|
<!-- <view> |
||||
|
<u-checkbox-group class="checkboxs flex flex-1 items-center mt-4" v-for="(item, index) in list" :key="index"> |
||||
|
<div class="flex-1 flex items-center"> |
||||
|
<u-checkbox v-model="item.checked"></u-checkbox> |
||||
|
<u-avatar :src="item.src" size="55" style="background: #d8dce0; margin-right: 10px"></u-avatar> |
||||
|
<div style="width: 60%; font-size: 12px"> |
||||
|
<div style="color: gray">{{ item.name }}</div> |
||||
|
<div style="color: #c4d0e1">{{ item.joinMethod }}</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</u-checkbox-group> |
||||
|
</view> --> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import { mapGetters, mapState } from 'vuex'; |
||||
|
|
||||
|
export default { |
||||
|
data() { |
||||
|
return { |
||||
|
rolesArray: [], |
||||
|
allRolesName: [], |
||||
|
index: 0, |
||||
|
links: '', //复制的链接 |
||||
|
copyText: '', |
||||
|
checked: false, //全选按钮是否选中 |
||||
|
roleName: '观众', |
||||
|
// 多选框列表 |
||||
|
list: [ |
||||
|
{ |
||||
|
name: '冯老师', |
||||
|
src: '', |
||||
|
joinMethod: '文件创建者', |
||||
|
role: '观众', |
||||
|
checked: false, |
||||
|
disabled: false, |
||||
|
}, |
||||
|
{ |
||||
|
name: '马壮', |
||||
|
src: '', |
||||
|
joinMethod: '通过链接加入', |
||||
|
role: '干系人', |
||||
|
checked: false, |
||||
|
disabled: false, |
||||
|
}, |
||||
|
{ |
||||
|
name: '张野', |
||||
|
src: '', |
||||
|
joinMethod: '通过链接加入', |
||||
|
role: '观众', |
||||
|
checked: false, |
||||
|
disabled: false, |
||||
|
}, |
||||
|
], |
||||
|
quantity: 0, //多选里面已选择的数量 |
||||
|
path: '', |
||||
|
}; |
||||
|
}, |
||||
|
|
||||
|
computed: { |
||||
|
...mapState('role', ['visibleRoles', 'invisibleRoles']), |
||||
|
...mapState('project', ['project']), |
||||
|
...mapGetters('project', ['projectId']), |
||||
|
}, |
||||
|
|
||||
|
mounted() { |
||||
|
this.$nextTick(() => { |
||||
|
this.path = window.location.href.split('?')[0]; |
||||
|
const { path, projectId } = this; |
||||
|
const params = { path: `${path}`, projectId, roleId: '0' }; |
||||
|
this.creatShare(params); |
||||
|
}); |
||||
|
|
||||
|
if (this.visibleRoles.length || this.invisibleRoles.length) { |
||||
|
const arr = this.visibleRoles.concat(this.invisibleRoles); |
||||
|
arr.forEach(role => { |
||||
|
let item = { id: '', name: '' }; |
||||
|
item.id = role.id; |
||||
|
item.name = role.name; |
||||
|
this.allRolesName.push(item); |
||||
|
this.rolesArray.push(role.name); |
||||
|
}); |
||||
|
const firstItem = { id: '0', name: '观众' }; |
||||
|
this.allRolesName.unshift(firstItem); |
||||
|
this.rolesArray.unshift('观众'); |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
methods: { |
||||
|
// 选择角色 |
||||
|
async changeRole(e) { |
||||
|
this.index = e.target.value; |
||||
|
this.roleName = this.allRolesName[this.index].name; |
||||
|
const { path, projectId } = this; |
||||
|
const params = { path, projectId, roleId: this.allRolesName[this.index].id }; |
||||
|
await this.creatShare(params); |
||||
|
}, |
||||
|
|
||||
|
// 复制成功 |
||||
|
copySuccess() { |
||||
|
uni.$uiowToast('复制成功'); |
||||
|
}, |
||||
|
// 复制失败 |
||||
|
copyError() { |
||||
|
uni.$uiowToast('复制失败,请稍后重试'); |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 创建分享链接 |
||||
|
* @param path 路径前缀 |
||||
|
* @param projectId 项目id |
||||
|
* @param roleId 角色id |
||||
|
*/ |
||||
|
async creatShare(params) { |
||||
|
try { |
||||
|
const data = await this.$u.api.createShare(params); |
||||
|
this.links = data.path; |
||||
|
this.copyText = `邀请您加入${this.project.name}的项目,角色为${this.roleName},链接为${data.path}&url=${this.$t.domain}`; |
||||
|
} catch (error) { |
||||
|
console.error('error: ', error); |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
//已选择 |
||||
|
select() { |
||||
|
this.quantity = 0; |
||||
|
this.list.forEach(val => { |
||||
|
if (val.checked == true) { |
||||
|
this.quantity++; |
||||
|
} |
||||
|
}); |
||||
|
}, |
||||
|
|
||||
|
// 全选 |
||||
|
checkedAll() { |
||||
|
this.list.map(val => { |
||||
|
val.checked = !this.checked; |
||||
|
}); |
||||
|
}, |
||||
|
}, |
||||
|
}; |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss" scoped> |
||||
|
.content { |
||||
|
width: 100%; |
||||
|
max-height: 400px; |
||||
|
} |
||||
|
|
||||
|
.link { |
||||
|
height: 40px; |
||||
|
border: 1px solid #afbed1; |
||||
|
} |
||||
|
|
||||
|
.link-url { |
||||
|
color: #afbed1; |
||||
|
width: 80%; |
||||
|
line-height: 40px; |
||||
|
margin-left: 5px; |
||||
|
overflow: hidden; |
||||
|
white-space: nowrap; |
||||
|
text-overflow: ellipsis; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,43 @@ |
|||||
|
<template> |
||||
|
<view class="upload"> |
||||
|
<u-icon name="plus" size="24px" class="flex justify-center w-12 h-12 bg-blue-100 rounded-full shadow-md" @click="handleUpload"></u-icon> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script setup> |
||||
|
import { computed } from 'vue'; |
||||
|
import { useStore } from 'vuex'; |
||||
|
|
||||
|
const emit = defineEmits(['success', 'error']); |
||||
|
|
||||
|
const store = useStore(); |
||||
|
const userId = computed(() => store.getters['user/userId']); |
||||
|
|
||||
|
// 导入wbs |
||||
|
const handleUpload = async cur => { |
||||
|
try { |
||||
|
const res = await uni.$u.api.import(); |
||||
|
// 导入WBS成功后 |
||||
|
// 直接打开导入的项目 |
||||
|
emit('success'); |
||||
|
res.url && (uni.$t.domain = res.url); |
||||
|
setTimeout(() => { |
||||
|
uni.navigateTo({ url: `/pages/project/project?u=${userId.value}&p=${res.id}&pname=${res.pname}&url=${res.url}` }); |
||||
|
}, 2000); |
||||
|
} catch (error) { |
||||
|
console.error('error: ', error); |
||||
|
emit('error', error); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss" scoped> |
||||
|
.upload { |
||||
|
position: absolute; |
||||
|
right: 10px; |
||||
|
bottom: 0; |
||||
|
transform: translate3d(0, 50%, 0); |
||||
|
color: $uni-color-primary !important; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,2 @@ |
|||||
|
// 每页加载颗粒度的个数
|
||||
|
export default { pageCount: 10 }; |
||||
@ -0,0 +1,17 @@ |
|||||
|
export default { |
||||
|
timeUnits: [ |
||||
|
// 时间颗粒度
|
||||
|
{ id: 0, value: '毫秒', format: 'x', cycle: 'YY-M-D HH:mm:ss', granularity: 'millisecond' }, |
||||
|
{ id: 1, value: '秒', format: 'x', cycle: 'YY-M-D HH:mm:ss', granularity: 'second' }, |
||||
|
{ id: 2, value: '分', format: 'ss', cycle: 'YY-M-D HH:mm', granularity: 'minute' }, |
||||
|
{ id: 3, value: '时', format: 'mm', cycle: 'YY-M-D HH时', granularity: 'hour' }, |
||||
|
{ id: 4, value: '天', format: 'D日 HH:mm', cycle: 'YY-M-D', granularity: 'day' }, |
||||
|
{ id: 5, value: '周', format: 'D日 HH:mm', cycle: '', granularity: 'week' }, |
||||
|
{ id: 6, value: '月', format: 'D日 H:m', cycle: 'YYYY年', granularity: 'month' }, |
||||
|
{ id: 7, value: '季度', format: '', cycle: 'YYYY年', granularity: 'quarter' }, |
||||
|
{ id: 8, value: '年', format: 'YYYY', cycle: '', granularity: 'year' }, |
||||
|
{ id: 9, value: '年代', format: '', cycle: '', granularity: '' }, |
||||
|
{ id: 10, value: '世纪', format: '', cycle: '', granularity: '' }, |
||||
|
{ id: 11, value: '千年', format: '', cycle: '', granularity: '' }, |
||||
|
], |
||||
|
}; |
||||
@ -0,0 +1,194 @@ |
|||||
|
import { computed, nextTick } from 'vue'; |
||||
|
import { useStore } from 'vuex'; |
||||
|
import { flatten } from 'lodash'; |
||||
|
import dayjs from 'dayjs'; |
||||
|
export default function useGetTasks() { |
||||
|
const store = useStore(); |
||||
|
const tasks = computed(() => store.state.task.tasks); |
||||
|
const roleId = computed(() => store.state.role.roleId); |
||||
|
const timeNode = computed(() => store.state.task.timeNode); |
||||
|
const timeUnit = computed(() => store.state.task.timeUnit); |
||||
|
const projectId = computed(() => store.getters['project/projectId']); |
||||
|
const timeGranularity = computed(() => store.getters['task/timeGranularity']); |
||||
|
|
||||
|
// 初始化 定期任务
|
||||
|
async function initPlanTasks() { |
||||
|
setPrevPlaceholderTasks(); // 向上加载空数据
|
||||
|
setNextPlaceholderTasks(); // 向下加载空数据
|
||||
|
await getInitTasks(); // 获取初始数据
|
||||
|
} |
||||
|
|
||||
|
// 切换了 颗粒度 || 角色时候 获取初始定期任务
|
||||
|
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, |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 生成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, |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 根据时间基准点和角色查找定期任务 |
||||
|
* @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); |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 用拿到的新数据 替换 时间刻度/旧数据 |
||||
|
* 先对比 新旧数据的 始末时间 补齐刻度 |
||||
|
* 再遍历对比 用任务替换刻度 |
||||
|
* @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; |
||||
|
} |
||||
|
|
||||
|
// 设置时间轴向上的空数据
|
||||
|
function setPrevPlaceholderTasks() { |
||||
|
store.commit('task/setTopEnd', true); |
||||
|
let startTime = ''; |
||||
|
if (!tasks.value || !tasks.value.length) { |
||||
|
startTime = Date.now(); // 没有任务就应该是时间基准点
|
||||
|
} else { |
||||
|
startTime = tasks.value[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); |
||||
|
} |
||||
|
|
||||
|
return { |
||||
|
initPlanTasks, |
||||
|
getTasks, |
||||
|
}; |
||||
|
} |
||||
@ -0,0 +1,122 @@ |
|||||
|
import { computed } from 'vue'; |
||||
|
import { onLoad } from '@dcloudio/uni-app'; |
||||
|
import { useStore } from 'vuex'; |
||||
|
|
||||
|
export default function useInit() { |
||||
|
const store = useStore(); |
||||
|
const token = computed(() => store.state.user.token); |
||||
|
|
||||
|
onLoad(options => { |
||||
|
if (options.share && options.share === '1') { |
||||
|
shareInit(options); |
||||
|
} else { |
||||
|
init(options); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
// onMounted(() => {
|
||||
|
// const system = uni.getSystemInfoSync();
|
||||
|
// height.value = `${system.windowHeight}px`;
|
||||
|
// });
|
||||
|
|
||||
|
/** |
||||
|
* 通过项目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', ''); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 初始化 |
||||
|
* @param {object | null} options |
||||
|
*/ |
||||
|
function init(options) { |
||||
|
// 参数里有项目名称 就设置标题里的项目名称
|
||||
|
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); |
||||
|
// 根据项目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('缺少用户信息参数,请登录'); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 点击分享连接 |
||||
|
* @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 }; |
||||
|
} |
||||
@ -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; |
||||
|
} |
||||
@ -0,0 +1,37 @@ |
|||||
|
import { computed } from 'vue'; |
||||
|
import { useStore } from 'vuex'; |
||||
|
|
||||
|
/** |
||||
|
* 初始化 |
||||
|
* token 及 userId处理: |
||||
|
* 1.1 store里有token 且没过期直接:使用store的token √ |
||||
|
* 1.2 store里的token不可用 查localStorage |
||||
|
* 因为一开始就将local的数据同步到了store里所以不用管local的数据了 |
||||
|
* 2. store里token不可用 查userId 通过store里的userId获取token |
||||
|
* url 与 local的userId 一开始就同步到了store里所以不用考虑 |
||||
|
* @param {object | null} options |
||||
|
*/ |
||||
|
export default async function useGetToken() { |
||||
|
const store = useStore(); |
||||
|
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; |
||||
|
} else { |
||||
|
// 2. 根据userId获取token
|
||||
|
if (userId.value) { |
||||
|
try { |
||||
|
const { token } = await store.dispatch('user/getTokenByUserId', userId.value); |
||||
|
return token; |
||||
|
} catch (error) { |
||||
|
console.error('error: ', error); |
||||
|
return null; |
||||
|
} |
||||
|
} else { |
||||
|
return null; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,10 @@ |
|||||
|
// 获取本地localStorage里的userId
|
||||
|
export default function useGetUserIdFromLocal() { |
||||
|
try { |
||||
|
const userLocal = uni.$storage.getStorageSync('user'); |
||||
|
const user = JSON.parse(userLocal); |
||||
|
return user.id; |
||||
|
} catch (error) { |
||||
|
return null; |
||||
|
} |
||||
|
} |
||||
@ -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`;
|
||||
|
// },
|
||||
|
// // }
|
||||
|
// };
|
||||
@ -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,23 +1,58 @@ |
|||||
import App from './App' |
import App from './App'; |
||||
|
import cache from '@/utils/cache.js'; |
||||
|
import cacheAndRequest from '@/utils/cacheAndRequest.js'; |
||||
|
import { createSSRApp } from 'vue'; |
||||
|
import { setupDayjs } from '@/utils/dayjs.js'; |
||||
|
import { setupHttp } from '@/utils/request.js'; |
||||
|
import { setupProject } from '@/apis/project.js'; |
||||
|
import { setupRole } from '@/apis/role.js'; |
||||
|
import { setupTall } from '@/apis/tall.js'; |
||||
|
import { setupTask } from '@/apis/task.js'; |
||||
|
import { setupWbs } from '@/apis/wbs.js'; |
||||
|
import storage from '@/utils/storage.js'; |
||||
|
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 uView from './uni_modules/vk-uview-ui'; // 引入 uView UI
|
||||
|
import ui from '@/utils/ui.js'; |
||||
|
import upload from '@/utils/upload.js'; |
||||
|
|
||||
// #ifndef VUE3
|
|
||||
import Vue from 'vue' |
|
||||
Vue.config.productionTip = false |
|
||||
App.mpType = 'app' |
|
||||
const app = new Vue({ |
|
||||
...App |
|
||||
}) |
|
||||
app.$mount() |
|
||||
// #endif
|
|
||||
|
|
||||
// #ifdef VUE3
|
|
||||
import { createSSRApp } from 'vue' |
|
||||
export function createApp() { |
export function createApp() { |
||||
const app = createSSRApp(App) |
const app = createSSRApp(App); |
||||
app.use(uView) // 使用 uView UI
|
|
||||
|
app.config.globalProperties.$cache = cache; |
||||
|
app.config.globalProperties.$catchReq = cacheAndRequest; |
||||
|
app.config.globalProperties.$storage = storage; |
||||
|
app.config.globalProperties.$time = time; |
||||
|
app.config.globalProperties.$ui = ui; |
||||
|
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; |
||||
|
uni.$storage = storage; |
||||
|
uni.$time = time; |
||||
|
uni.$ui = ui; |
||||
|
uni.$upload = upload; |
||||
|
uni.$task = task; |
||||
|
uni.$timeConfig = timeConfig; |
||||
|
uni.$taskConfig = taskConfig; |
||||
|
|
||||
|
setupDayjs(app); |
||||
|
app.use(uView); // 使用 uView UI
|
||||
|
app.use(store); |
||||
|
setupHttp(app); |
||||
|
setupTall(app); |
||||
|
setupProject(app); |
||||
|
setupRole(app); |
||||
|
setupTask(app); |
||||
|
setupWbs(app); |
||||
|
|
||||
return { |
return { |
||||
app |
app, |
||||
} |
}; |
||||
} |
} |
||||
// #endif
|
|
||||
@ -1,54 +1,61 @@ |
|||||
{ |
{ |
||||
"name": "tall-4", |
"name": "tall-4", |
||||
"version": "1.0.0", |
"version": "1.0.0", |
||||
"description": "", |
"description": "", |
||||
"main": "main.js", |
"main": "main.js", |
||||
"dependencies": {}, |
"dependencies": { |
||||
"devDependencies": { |
"axios": "^0.24.0", |
||||
"@typescript-eslint/eslint-plugin": "^5.8.1", |
"dayjs": "^1.10.7", |
||||
"@typescript-eslint/parser": "^5.8.1", |
"lodash": "^4.17.21", |
||||
"commitizen": "^4.2.4", |
"qs": "^6.10.2" |
||||
"commitlint": "^16.0.1", |
}, |
||||
"conventional-changelog": "^3.1.25", |
"devDependencies": { |
||||
"conventional-changelog-cli": "^2.2.2", |
"@dcloudio/uni-app": "^3.0.0-alpha-3000020210521001", |
||||
"eslint": "^7.32.0", |
"@typescript-eslint/eslint-plugin": "^5.8.1", |
||||
"eslint-config-airbnb-base": "^15.0.0", |
"@typescript-eslint/parser": "^5.8.1", |
||||
"eslint-plugin-import": "^2.25.3", |
"commitizen": "^4.2.4", |
||||
"eslint-plugin-prettier": "^4.0.0", |
"commitlint": "^16.0.1", |
||||
"eslint-plugin-vue": "^8.2.0", |
"conventional-changelog": "^3.1.25", |
||||
"husky": "^7.0.4", |
"conventional-changelog-cli": "^2.2.2", |
||||
"lint-staged": "^12.1.4", |
"eslint": "^7.32.0", |
||||
"prettier": "^2.5.1", |
"eslint-config-airbnb-base": "^15.0.0", |
||||
"right-pad": "^1.0.1", |
"eslint-plugin-import": "^2.25.3", |
||||
"vue-cli-plugin-commitlint": "^1.0.12" |
"eslint-plugin-prettier": "^4.0.0", |
||||
}, |
"eslint-plugin-vue": "^8.2.0", |
||||
"browserslist": [ |
"husky": "^7.0.4", |
||||
"Android >= 4", |
"lint-staged": "^12.1.4", |
||||
"ios >= 8" |
"prettier": "^2.5.1", |
||||
], |
"right-pad": "^1.0.1", |
||||
"config": { |
"vue-cli-plugin-commitlint": "^1.0.12" |
||||
"commitizen": { |
}, |
||||
"path": "./node_modules/vue-cli-plugin-commitlint/lib/cz" |
"browserslist": [ |
||||
} |
"Android >= 4", |
||||
}, |
"ios >= 8" |
||||
"husky": { |
], |
||||
"hooks": { |
"config": { |
||||
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS", |
"commitizen": { |
||||
"pre-commit": "lint-staged" |
"path": "./node_modules/vue-cli-plugin-commitlint/lib/cz" |
||||
} |
} |
||||
}, |
}, |
||||
"lint-staged": { |
"husky": { |
||||
"src/**/*.{js,json,css,vue}": [ |
"hooks": { |
||||
"eslint --fix", |
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS", |
||||
"git add" |
"pre-commit": "lint-staged" |
||||
], |
} |
||||
"*.js": "eslint --cache --fix" |
}, |
||||
}, |
"lint-staged": { |
||||
"scripts": { |
"src/**/*.{js,json,css,vue}": [ |
||||
"test": "echo \"Error: no test specified\" && exit 1", |
"eslint --fix", |
||||
"cz": "npm run log && git add . && git cz", |
"git add" |
||||
"log": "conventional-changelog --config ./node_modules/vue-cli-plugin-commitlint/lib/log -i CHANGELOG.md -s -r 0" |
], |
||||
}, |
"*.js": "eslint --cache --fix" |
||||
"author": "", |
}, |
||||
"license": "ISC" |
"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": "", |
||||
|
"license": "ISC" |
||||
|
} |
||||
|
|||||
@ -1,66 +1,135 @@ |
|||||
<template> |
<template> |
||||
<view :style="{ height: height }" class="flex flex-col overflow-hidden u-font-14"> |
<!-- <view class="flex flex-col h-full bg-gray-50" @click="openAuth"> --> |
||||
<!-- 标题栏 --> |
<view class="flex flex-col h-full bg-gray-50"> |
||||
<Title /> |
<view class="relative" @touchmove="onMove"> |
||||
|
<!-- 日历 --> |
||||
<view class="container flex flex-col flex-1 mx-auto overflow-hidden bg-gray-100"> |
<Calendar @selected-change="onDateChange" :show-back="true" ref="calendar" @handleFindPoint="handleFindPoint" /> |
||||
<!-- 角色栏 --> |
<!-- 上传 导入wbs --> |
||||
<Roles /> |
<Upload @success="onUploadSuccess" @error="onUploadError" /> |
||||
|
</view> |
||||
|
|
||||
<!-- 日常任务面板 --> |
<!-- 项目列表 --> |
||||
<Globals /> |
<Projects @getProjects="getProjects" class="flex-1 overflow-y-auto" /> |
||||
|
|
||||
<!-- 定期任务面板 --> |
<!-- 全局提示框 --> |
||||
<TimeLine @getTasks="getTasks" class="flex-1 overflow-hidden" ref="timeLine" /> |
<u-top-tips ref="uTips"></u-top-tips> |
||||
</view> |
|
||||
</view> |
</view> |
||||
</template> |
</template> |
||||
|
|
||||
<script setup> |
<script setup> |
||||
import { |
import { reactive, computed, watchEffect, ref } from 'vue'; |
||||
ref, onMounted |
import { useStore } from 'vuex'; |
||||
} from 'vue'; |
import dayjs from 'dayjs'; |
||||
import Navbar from '@/components/Title/Title.vue'; |
|
||||
import Roles from '@/components/Roles/Roles.vue'; |
const store = useStore(); |
||||
import Globals from '@/components/Globals/Globals.vue'; |
const token = computed(() => store.state.user.token); |
||||
import TimeLine from '@/components/TimeLine/TimeLine.vue'; |
const uTips = ref(null); |
||||
|
|
||||
|
const data = reactive({ |
||||
|
calendar: null, |
||||
|
days: [], |
||||
|
}); |
||||
|
|
||||
|
// 监听token |
||||
|
watchEffect(() => { |
||||
|
if (!token.value) return; |
||||
|
if (token.value) { |
||||
|
getProjects(); |
||||
|
handleFindPoint(); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
let height = ref(null); |
// 获取项目列表 |
||||
|
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); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
onMounted(() => { |
async function handleFindPoint(start, end) { |
||||
const system = uni.getSystemInfoSync(); |
try { |
||||
height.value = system.windowHeight + 'px'; |
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); |
||||
|
} |
||||
|
} |
||||
|
|
||||
function getTasks() { |
// 点击了某个日期 |
||||
|
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> |
</script> |
||||
|
|
||||
<style> |
<style> |
||||
.content { |
.content { |
||||
display: flex; |
display: flex; |
||||
flex-direction: column; |
flex-direction: column; |
||||
align-items: center; |
align-items: center; |
||||
justify-content: center; |
justify-content: center; |
||||
} |
} |
||||
|
|
||||
.logo { |
.logo { |
||||
height: 200rpx; |
height: 200rpx; |
||||
width: 200rpx; |
width: 200rpx; |
||||
margin-top: 200rpx; |
margin-top: 200rpx; |
||||
margin-left: auto; |
margin-left: auto; |
||||
margin-right: auto; |
margin-right: auto; |
||||
margin-bottom: 50rpx; |
margin-bottom: 50rpx; |
||||
} |
} |
||||
|
|
||||
.text-area { |
.text-area { |
||||
display: flex; |
display: flex; |
||||
justify-content: center; |
justify-content: center; |
||||
} |
} |
||||
|
|
||||
.title { |
.title { |
||||
font-size: 36rpx; |
font-size: 36rpx; |
||||
color: #8f8f94; |
color: #8f8f94; |
||||
} |
} |
||||
</style> |
</style> |
||||
|
|||||
@ -0,0 +1,142 @@ |
|||||
|
<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" /> |
||||
|
|
||||
|
<!-- TODO: DEBUG: --> |
||||
|
<u-button @click="$store.commit('setTheme', 'theme-test')">测试切换主题</u-button> |
||||
|
</view> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script setup> |
||||
|
import { ref, computed, watch, onMounted } from 'vue'; |
||||
|
import { useStore } from 'vuex'; |
||||
|
import useInit from '@/hooks/project/useInit'; |
||||
|
import useGetTasks from '@/hooks/project/useGetTasks'; |
||||
|
|
||||
|
const initHook = useInit(); |
||||
|
const getTasksHook = useGetTasks(); |
||||
|
const store = useStore(); |
||||
|
const roleId = computed(() => store.state.role.roleId); |
||||
|
const timeNode = computed(() => store.state.task.timeNode); |
||||
|
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(); |
||||
|
height.value = `${system.windowHeight}px`; |
||||
|
}); |
||||
|
|
||||
|
// 获取可变全局任务 |
||||
|
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'); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 当时间基准点发生变化时 |
||||
|
* 重新根据时间和角色查询普通日常任务 |
||||
|
* 永久日常任务不发生 改变 |
||||
|
*/ |
||||
|
watch(timeNode, newValue => { |
||||
|
if (newValue && roleId.value) { |
||||
|
console.log('当时间基准点发生变化时'); |
||||
|
clearTasksData(); |
||||
|
getGlobalData(); // 查可变日常任务 |
||||
|
// initPlanTasks(); // 处理定期任务 |
||||
|
getTasksHook.initPlanTasks(); // 处理定期任务 |
||||
|
|
||||
|
// 滚动到对应位置 |
||||
|
let timer = null; |
||||
|
timer = setInterval(() => { |
||||
|
if (showScrollTo.value) { |
||||
|
clearInterval(timer); |
||||
|
timeLine.value.setScrollPosition(); |
||||
|
} |
||||
|
}, 500); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
/** |
||||
|
* 当角色发生变化时 |
||||
|
* 重新查询永久日常任务和普通日常任务 |
||||
|
* 注意: 切换角色后 重新设置了时间基准点 时间基准点一定会变 |
||||
|
* 所以监听时间基准点获取 可变日常任务即可 这里不用获取 避免重复获取 |
||||
|
*/ |
||||
|
watch(roleId, newValue => { |
||||
|
if (newValue) { |
||||
|
console.log('当角色发生变化时', newValue); |
||||
|
store.commit('task/setTimeNode', Date.now()); |
||||
|
// 根据角色查找永久的日常任务 |
||||
|
const params = { |
||||
|
roleId: newValue, |
||||
|
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; |
||||
|
|
||||
|
initHook.init(options); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
function getTasks(params) { |
||||
|
getTasksHook.initPlanTasks(params); // 处理定期任务 |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss" scoped> |
||||
|
.border-b { |
||||
|
border-bottom: 1px solid #e4e7ed; |
||||
|
} |
||||
|
</style> |
||||
@ -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> |
||||
@ -0,0 +1,8 @@ |
|||||
|
<template> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
</script> |
||||
|
|
||||
|
<style> |
||||
|
</style> |
||||
@ -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`;
|
||||
|
// },
|
||||
|
// // }
|
||||
|
// };
|
||||
@ -0,0 +1,9 @@ |
|||||
|
<template> |
||||
|
<view class="deliver-container">p-deliver</view> |
||||
|
</template> |
||||
|
|
||||
|
<script setup> |
||||
|
|
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss"></style> |
||||
@ -0,0 +1,10 @@ |
|||||
|
<template> |
||||
|
<!-- 任务名插件 --> |
||||
|
<theme> |
||||
|
<view>{{ task.name }}</view> |
||||
|
</theme> |
||||
|
</template> |
||||
|
|
||||
|
<script setup> |
||||
|
defineProps({ task: { type: Object, default: () => {} } }); |
||||
|
</script> |
||||
@ -1,3 +0,0 @@ |
|||||
const actions = {}; |
|
||||
|
|
||||
export default actions; |
|
||||
@ -1,3 +0,0 @@ |
|||||
const getters = {}; |
|
||||
|
|
||||
export default getters; |
|
||||
@ -1,12 +0,0 @@ |
|||||
import state from './state'; |
|
||||
import getters from './getters'; |
|
||||
import mutations from './mutations'; |
|
||||
import actions from './actions'; |
|
||||
|
|
||||
export default { |
|
||||
namespaced: true, |
|
||||
state, |
|
||||
getters, |
|
||||
mutations, |
|
||||
actions, |
|
||||
}; |
|
||||
@ -1,3 +0,0 @@ |
|||||
const mutations = {}; |
|
||||
|
|
||||
export default mutations; |
|
||||
@ -1,7 +0,0 @@ |
|||||
const state = { |
|
||||
db: null, // indexedDB对象
|
|
||||
name: 'TALL_indexedDB', |
|
||||
version: 1, |
|
||||
}; |
|
||||
|
|
||||
export default state; |
|
||||
@ -1,3 +0,0 @@ |
|||||
const getters = {}; |
|
||||
|
|
||||
export default getters; |
|
||||
@ -1,4 +0,0 @@ |
|||||
import state from './state'; |
|
||||
import mutations from './mutations'; |
|
||||
|
|
||||
export default { namespaced: true, state, mutations }; |
|
||||
@ -1,85 +0,0 @@ |
|||||
import storage from '@/utils/storage'; |
|
||||
const { setStorageSync, getStorageSync, removeStorageSync } = storage; |
|
||||
|
|
||||
const mutations = { |
|
||||
/** |
|
||||
* 初始化消息栈 |
|
||||
* @param {object} state |
|
||||
* @param {string} type |
|
||||
* type: |
|
||||
* syncMessages 同步消息栈 |
|
||||
* faultMessages 故障消息 未处理消息栈 |
|
||||
* faults 所有的故障消息栈 |
|
||||
*/ |
|
||||
messagesInit(state, type) { |
|
||||
const messages = getStorageSync(type) ? JSON.parse(getStorageSync(type)) : []; |
|
||||
state[type] = messages; |
|
||||
}, |
|
||||
|
|
||||
/** |
|
||||
* 将新 消息添加到 消息栈 最前边 |
|
||||
* @param { object } state |
|
||||
* @param { object } data |
|
||||
* data: message, type |
|
||||
* message 消息对象 |
|
||||
* type: |
|
||||
* syncMessages 同步消息栈 |
|
||||
* faultMessages 故障消息 未处理消息栈 |
|
||||
* faults 所有的故障消息栈 |
|
||||
* game 游戏的消息 |
|
||||
* |
|
||||
* cache: boolean true 本地存储 false 不存储 |
|
||||
*/ |
|
||||
messagesAdd(state, data) { |
|
||||
const messages = state[data.type]; |
|
||||
if (messages.length > 0) { |
|
||||
const result = messages.find(msg => msg.id === data.message.id); |
|
||||
if (result) return; |
|
||||
} |
|
||||
messages.unshift(data.message); |
|
||||
// eslint-disable-next-line no-param-reassign
|
|
||||
state[data.type] = messages; |
|
||||
if (data.cache) { |
|
||||
setStorageSync(data.type, JSON.stringify(messages)); |
|
||||
} |
|
||||
}, |
|
||||
|
|
||||
/** |
|
||||
* 通过消息id移除指定 同步 消息 |
|
||||
* @param { object } state |
|
||||
* @param { object } data |
|
||||
* data: messageId, type |
|
||||
* messageId: 要移除的消息的messageId |
|
||||
* type: |
|
||||
* syncMessages 同步消息栈 |
|
||||
* faultMessages 故障消息 未处理消息栈 |
|
||||
* faults 所有的故障消息栈 |
|
||||
* cache: boolean true 本地存储 false 不存储 |
|
||||
*/ |
|
||||
messagesRemoveById(state, data) { |
|
||||
const messages = state[data.type]; |
|
||||
const index = messages.findIndex(msg => msg.id === data.messageId); |
|
||||
if (index < 0) return; |
|
||||
messages.splice(index, 1); |
|
||||
// eslint-disable-next-line no-param-reassign
|
|
||||
state[data.type] = messages; |
|
||||
if (data.cache) { |
|
||||
setStorageSync(data.type, JSON.stringify(messages)); |
|
||||
} |
|
||||
}, |
|
||||
|
|
||||
/** |
|
||||
* 清除指定type的消息 |
|
||||
* @param {any} state |
|
||||
* @param {object} data |
|
||||
* data: type, cache |
|
||||
*/ |
|
||||
messagesClear(state, data) { |
|
||||
state[data.type] = []; |
|
||||
if (data.cache) { |
|
||||
removeStorageSync(data.type); |
|
||||
} |
|
||||
}, |
|
||||
}; |
|
||||
|
|
||||
export default mutations; |
|
||||
@ -1,8 +0,0 @@ |
|||||
const state = { |
|
||||
syncMessages: [], // 同步消息
|
|
||||
faultMessages: [], // 新收到的未处理的 故障消息
|
|
||||
faults: [], // 所有的故障消息
|
|
||||
game: [], // 游戏的消息
|
|
||||
}; |
|
||||
|
|
||||
export default state; |
|
||||
@ -1,154 +1,211 @@ |
|||||
const WS_BASE_URL = process.env.VUE_APP_MSG_URL; |
const WS_BASE_URL = 'wss://test.tall.wiki/websocket/message/v4.0/ws'; // 测试
|
||||
|
// const WS_BASE_URL = 'wss://www.tall.wiki/websocket/message/v4.0/ws'; // 生产
|
||||
let prevTime = 0; |
|
||||
let socketMsgQueue = []; // socket消息队列
|
let prevTime = 0; |
||||
let sendHeartTimer = null; |
let socketMsgQueue = []; // socket消息队列
|
||||
|
let sendHeartTimer = null; |
||||
const actions = { |
|
||||
// 初始化socket
|
const actions = { |
||||
initSocket({ commit, dispatch, state, rootState }) { |
// 初始化socket
|
||||
if (state.lockSocket) return; |
initSocket({ commit, dispatch, state, rootState }) { |
||||
const { token } = rootState.user; |
if (state.lockSocket) return; |
||||
if (!token) return; |
const { |
||||
commit('setLockSocket', true); |
token |
||||
commit('setSocket', uni.connectSocket({ url: WS_BASE_URL, complete: () => {} })); |
} = rootState.user; |
||||
dispatch('onSocketOpen'); |
if (!token) return; |
||||
dispatch('onSocketMessage'); |
commit('setLockSocket', true); |
||||
dispatch('onSocketClose'); |
commit('setSocket', uni.connectSocket({ |
||||
state.socket.onError(errMsg => console.error(errMsg)); |
url: WS_BASE_URL, |
||||
commit('setLockSocket', false); |
complete: () => {} |
||||
}, |
})); |
||||
|
dispatch('onSocketOpen'); |
||||
// 监听ws打开
|
dispatch('onSocketMessage'); |
||||
onSocketOpen({ dispatch, commit, state }) { |
dispatch('onSocketClose'); |
||||
// eslint-disable-next-line no-unused-vars
|
state.socket.onError(errMsg => console.error(errMsg)); |
||||
state.socket.onOpen(res => { |
commit('setLockSocket', false); |
||||
// console.log('ws open: ', res);
|
}, |
||||
commit('setConnected', true); |
|
||||
prevTime = Date.now(); |
// 监听ws打开
|
||||
// this.auth();
|
onSocketOpen({ |
||||
dispatch('auth'); |
dispatch, |
||||
for (let i = 0; i < socketMsgQueue.length; i++) { |
commit, |
||||
dispatch('sendSocketMessage', socketMsgQueue[i]); |
state |
||||
} |
}) { |
||||
socketMsgQueue = []; |
// eslint-disable-next-line no-unused-vars
|
||||
}); |
state.socket.onOpen(res => { |
||||
}, |
// console.log('ws open: ', res);
|
||||
|
commit('setConnected', true); |
||||
// 监听收到的ws消息
|
prevTime = Date.now(); |
||||
onSocketMessage({ dispatch, state }) { |
// this.auth();
|
||||
state.socket.onMessage(res => { |
dispatch('auth'); |
||||
// console.log('收到消息:', res);
|
for (let i = 0; i < socketMsgQueue.length; i++) { |
||||
prevTime = Date.now(); |
dispatch('sendSocketMessage', socketMsgQueue[i]); |
||||
if (!res || !res.data || !JSON.parse(res.data)) return; |
} |
||||
const resData = JSON.parse(res.data); |
socketMsgQueue = []; |
||||
const { messageSet, ackId } = resData; |
}); |
||||
// 处理消息体对象
|
}, |
||||
messageSet.forEach(item => dispatch('handleMessagesData', item)); |
|
||||
ackId && dispatch('sendSocketMessage', { type: 'Ack', data: { ackId } }); |
// 监听收到的ws消息
|
||||
}); |
onSocketMessage({ |
||||
}, |
dispatch, |
||||
|
state |
||||
/** |
}) { |
||||
* 处理收到的消息内容 |
state.socket.onMessage(res => { |
||||
* @param {object} item 单个消息体对象 |
// console.log('收到消息:', res);
|
||||
*/ |
prevTime = Date.now(); |
||||
handleMessagesData({ dispatch, commit }, item) { |
if (!res || !res.data || !JSON.parse(res.data)) return; |
||||
const data = JSON.parse(item.data); |
const resData = JSON.parse(res.data); |
||||
switch (data.type) { |
const { |
||||
case 'Sync': // 开始某个节点
|
messageSet, |
||||
commit('messages/messagesAdd', { message: data, type: 'syncMessages' }, { root: true }); |
ackId |
||||
break; |
} = resData; |
||||
case 'taskStatus': // 任务状态修改相关消息
|
// 处理消息体对象
|
||||
commit('task/setTaskStatus', data.data, { root: true }); |
messageSet.forEach(item => dispatch('handleMessagesData', item)); |
||||
break; |
ackId && dispatch('sendSocketMessage', { |
||||
case 'switchoverProject': // 打开新项目消息
|
type: 'Ack', |
||||
commit('task/setNewProjectInfo', data.data, { root: true }); |
data: { |
||||
break; |
ackId |
||||
// case 'Chrome': // !收到开始游戏的消息
|
} |
||||
// console.log('handleMessagesData', data);
|
}); |
||||
// // @ts-ignore
|
}); |
||||
// util.openGameApp({
|
}, |
||||
// type: data.data.type,
|
|
||||
// projectId: data.data.projectId,
|
/** |
||||
// id: data.data.recordId,
|
* 处理收到的消息内容 |
||||
// token: rootState.user.token,
|
* @param {object} item 单个消息体对象 |
||||
// });
|
*/ |
||||
// break;
|
handleMessagesData({ |
||||
// case 'Deliver': // 交付物相关消息
|
dispatch, |
||||
// commit('messages/messagesAdd', { type: 'checkMessages', message: data }, { root: true });
|
commit |
||||
// break;
|
}, item) { |
||||
case 'ChannelStatus': |
const data = JSON.parse(item.data); |
||||
dispatch('handleAuthMessage', data); |
switch (data.type) { |
||||
break; |
case 'Sync': // 开始某个节点
|
||||
// case 'switchoverProject': // 康复相关消息
|
commit('messages/messagesAdd', { |
||||
// dispatch('home/getProjectById', data.data.projectId, { root: true });
|
message: data, |
||||
// break;
|
type: 'syncMessages' |
||||
// case 'startDrill': // 康复开始训练相关消息
|
}, { |
||||
// console.log('setStartDrillInfo', data.data);
|
root: true |
||||
// commit('home/setStartDrillMessages', data.data, { root: true });
|
}); |
||||
// break;
|
break; |
||||
default: |
case 'taskStatus': // 任务状态修改相关消息
|
||||
break; |
commit('task/setTaskStatus', data.data, { |
||||
} |
root: true |
||||
}, |
}); |
||||
|
break; |
||||
// 发送消息
|
// case 'Chrome': // !收到开始游戏的消息
|
||||
sendSocketMessage({ state }, data) { |
// console.log('handleMessagesData', data);
|
||||
if (state.connected) { |
// // @ts-ignore
|
||||
const msg = JSON.stringify({ toDomain: 'Server', data: JSON.stringify(data) }); |
// util.openGameApp({
|
||||
state.socket.send({ data: msg }); |
// type: data.data.type,
|
||||
} else { |
// projectId: data.data.projectId,
|
||||
socketMsgQueue.push(data); |
// id: data.data.recordId,
|
||||
} |
// token: rootState.user.token,
|
||||
}, |
// });
|
||||
|
// break;
|
||||
// 监听关闭事件
|
// case 'Deliver': // 交付物相关消息
|
||||
onSocketClose({ dispatch, commit, state }) { |
// commit('messages/messagesAdd', { type: 'checkMessages', message: data }, { root: true });
|
||||
// console.log('onSocketClose');
|
// break;
|
||||
state.socket.onClose(() => { |
case 'ChannelStatus': |
||||
commit('setConnected', false); |
dispatch('handleAuthMessage', data); |
||||
if (sendHeartTimer) clearInterval(sendHeartTimer); |
break; |
||||
setTimeout(() => { |
// case 'switchoverProject': // 康复相关消息
|
||||
dispatch('initSocket'); |
// dispatch('home/getProjectById', data.data.projectId, { root: true });
|
||||
}, 300); |
// break;
|
||||
}); |
// case 'startDrill': // 康复开始训练相关消息
|
||||
}, |
// console.log('setStartDrillInfo', data.data);
|
||||
|
// commit('home/setStartDrillMessages', data.data, { root: true });
|
||||
// websocket发送channelId进行认证
|
// break;
|
||||
auth({ dispatch, rootState }) { |
default: |
||||
const { token } = rootState.user; |
break; |
||||
if (!token) return; |
} |
||||
const data = { type: 'Auth', data: { token } }; |
}, |
||||
dispatch('sendSocketMessage', data); |
|
||||
}, |
// 发送消息
|
||||
|
sendSocketMessage({ |
||||
// 心跳检测
|
state |
||||
sendHeart({ dispatch, state }) { |
}, data) { |
||||
if (sendHeartTimer) clearInterval(sendHeartTimer); |
if (state.connected) { |
||||
sendHeartTimer = setInterval(() => { |
const msg = JSON.stringify({ |
||||
if (Date.now() - prevTime >= 15000) { |
toDomain: 'Server', |
||||
dispatch('sendSocketMessage', { type: 'Ping' }); |
data: JSON.stringify(data) |
||||
if (Date.now() - prevTime >= 20000) { |
}); |
||||
state.socket.close(); |
state.socket.send({ |
||||
} |
data: msg |
||||
} |
}); |
||||
}, 5000); |
} else { |
||||
}, |
socketMsgQueue.push(data); |
||||
|
} |
||||
/** |
}, |
||||
* 处理auth认证返回的ChannelStatus消息 |
|
||||
* @param {object} data 消息内容对象 |
// 监听关闭事件
|
||||
*/ |
onSocketClose({ |
||||
handleAuthMessage({ commit, dispatch }, data) { |
dispatch, |
||||
if (data.data.authed) { |
commit, |
||||
dispatch('sendHeart'); |
state |
||||
} else { |
}) { |
||||
uni.$u.toast('消息系统认证失败, 请退出重新登录'); |
// console.log('onSocketClose');
|
||||
uni.$t.removeStorageSync('anyringToken'); |
state.socket.onClose(() => { |
||||
commit('setSocket', null); |
commit('setConnected', false); |
||||
} |
if (sendHeartTimer) clearInterval(sendHeartTimer); |
||||
}, |
setTimeout(() => { |
||||
}; |
dispatch('initSocket'); |
||||
|
}, 300); |
||||
|
}); |
||||
|
}, |
||||
|
|
||||
|
// websocket发送channelId进行认证
|
||||
|
auth({ |
||||
|
dispatch, |
||||
|
rootState |
||||
|
}) { |
||||
|
const { |
||||
|
token |
||||
|
} = rootState.user; |
||||
|
if (!token) return; |
||||
|
const data = { |
||||
|
type: 'Auth', |
||||
|
data: { |
||||
|
token |
||||
|
} |
||||
|
}; |
||||
|
dispatch('sendSocketMessage', data); |
||||
|
}, |
||||
|
|
||||
|
// 心跳检测
|
||||
|
sendHeart({ |
||||
|
dispatch, |
||||
|
state |
||||
|
}) { |
||||
|
if (sendHeartTimer) clearInterval(sendHeartTimer); |
||||
|
sendHeartTimer = setInterval(() => { |
||||
|
if (Date.now() - prevTime >= 15000) { |
||||
|
dispatch('sendSocketMessage', { |
||||
|
type: 'Ping' |
||||
|
}); |
||||
|
if (Date.now() - prevTime >= 20000) { |
||||
|
state.socket.close(); |
||||
|
} |
||||
|
} |
||||
|
}, 5000); |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 处理auth认证返回的ChannelStatus消息 |
||||
|
* @param {object} data 消息内容对象 |
||||
|
*/ |
||||
|
handleAuthMessage({ |
||||
|
commit, |
||||
|
dispatch |
||||
|
}, data) { |
||||
|
if (data.data.authed) { |
||||
|
dispatch('sendHeart'); |
||||
|
} else { |
||||
|
uni.$u.toast('消息系统认证失败, 请退出重新登录'); |
||||
|
uni.$t.removeStorageSync('anyringToken'); |
||||
|
commit('setSocket', null); |
||||
|
} |
||||
|
}, |
||||
|
}; |
||||
|
|
||||
export default actions; |
export default actions; |
||||
|
|||||
@ -1,5 +1,10 @@ |
|||||
import state from './state'; |
import state from './state'; |
||||
import mutations from './mutations'; |
import mutations from './mutations'; |
||||
import actions from './actions'; |
import actions from './actions'; |
||||
|
|
||||
export default { namespaced: true, state, mutations, actions }; |
export default { |
||||
|
namespaced: true, |
||||
|
state, |
||||
|
mutations, |
||||
|
actions |
||||
|
}; |
||||
|
|||||
@ -1,26 +1,26 @@ |
|||||
const mutations = { |
const mutations = { |
||||
// 设置socket实例
|
// 设置socket实例
|
||||
setSocket(state, socket) { |
setSocket(state, socket) { |
||||
state.socket = socket; |
state.socket = socket; |
||||
}, |
}, |
||||
|
|
||||
/** |
/** |
||||
* 设置socket连接状态 |
* 设置socket连接状态 |
||||
* @param {Object} state |
* @param {Object} state |
||||
* @param {boolean} connected 是否连接 true -> 连接 |
* @param {boolean} connected 是否连接 true -> 连接 |
||||
*/ |
*/ |
||||
setConnected(state, connected) { |
setConnected(state, connected) { |
||||
state.connected = connected; |
state.connected = connected; |
||||
}, |
}, |
||||
|
|
||||
/** |
/** |
||||
* 设置连接锁 正在连接中 锁上 避免多个连接同时发出 |
* 设置连接锁 正在连接中 锁上 避免多个连接同时发出 |
||||
* @param {Object} state |
* @param {Object} state |
||||
* @param {boolean} lockSocket 是否正在连接的过程中 |
* @param {boolean} lockSocket 是否正在连接的过程中 |
||||
*/ |
*/ |
||||
setLockSocket(state, lockSocket) { |
setLockSocket(state, lockSocket) { |
||||
state.lockSocket = lockSocket; |
state.lockSocket = lockSocket; |
||||
}, |
}, |
||||
}; |
}; |
||||
|
|
||||
export default mutations; |
export default mutations; |
||||
|
|||||
@ -1,7 +1,7 @@ |
|||||
const state = { |
const state = { |
||||
socket: null, // websocket实例
|
socket: null, // websocket实例
|
||||
connected: false, // 是否处于连接状态
|
connected: false, // 是否处于连接状态
|
||||
lockSocket: false, // 是否正在连接状态
|
lockSocket: false, // 是否正在连接状态
|
||||
}; |
}; |
||||
|
|
||||
export default state; |
export default state; |
||||
|
|||||
@ -1,12 +1,12 @@ |
|||||
import state from './state'; |
import state from './state'; |
||||
import getters from './getters'; |
import getters from './getters'; |
||||
import mutations from './mutations'; |
import mutations from './mutations'; |
||||
import actions from './actions'; |
import actions from './actions'; |
||||
|
|
||||
export default { |
export default { |
||||
namespaced: true, |
namespaced: true, |
||||
state, |
state, |
||||
getters, |
getters, |
||||
mutations, |
mutations, |
||||
actions, |
actions, |
||||
}; |
}; |
||||
|
|||||
@ -1,5 +1,8 @@ |
|||||
|
import { dayjs } from '@/utils/dayjs'; |
||||
|
|
||||
const state = { |
const state = { |
||||
token: '', |
token: '', |
||||
|
tokenExpiredTime: dayjs().add('1', 'day'), // DEBUG:
|
||||
user: null, |
user: null, |
||||
}; |
}; |
||||
export default state; |
export default state; |
||||
|
|||||
@ -0,0 +1,12 @@ |
|||||
|
## 1.4.3(2021-09-22) |
||||
|
- 修复 startDate、 endDate 属性失效的 bug |
||||
|
## 1.4.2(2021-08-24) |
||||
|
- 新增 支持国际化 |
||||
|
## 1.4.1(2021-08-05) |
||||
|
- 修复 弹出层被 tabbar 遮盖 bug |
||||
|
## 1.4.0(2021-07-30) |
||||
|
- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) |
||||
|
## 1.3.16(2021-05-12) |
||||
|
- 新增 组件示例地址 |
||||
|
## 1.3.15(2021-02-04) |
||||
|
- 调整为uni_modules目录规范 |
||||
@ -0,0 +1,546 @@ |
|||||
|
/** |
||||
|
* @1900-2100区间内的公历、农历互转 |
||||
|
* @charset UTF-8 |
||||
|
* @github https://github.com/jjonline/calendar.js
|
||||
|
* @Author Jea杨(JJonline@JJonline.Cn) |
||||
|
* @Time 2014-7-21 |
||||
|
* @Time 2016-8-13 Fixed 2033hex、Attribution Annals |
||||
|
* @Time 2016-9-25 Fixed lunar LeapMonth Param Bug |
||||
|
* @Time 2017-7-24 Fixed use getTerm Func Param Error.use solar year,NOT lunar year |
||||
|
* @Version 1.0.3 |
||||
|
* @公历转农历:calendar.solar2lunar(1987,11,01); //[you can ignore params of prefix 0]
|
||||
|
* @农历转公历:calendar.lunar2solar(1987,09,10); //[you can ignore params of prefix 0]
|
||||
|
*/ |
||||
|
/* eslint-disable */ |
||||
|
var calendar = { |
||||
|
|
||||
|
/** |
||||
|
* 农历1900-2100的润大小信息表 |
||||
|
* @Array Of Property |
||||
|
* @return Hex |
||||
|
*/ |
||||
|
lunarInfo: [0x04bd8, 0x04ae0, 0x0a570, 0x054d5, 0x0d260, 0x0d950, 0x16554, 0x056a0, 0x09ad0, 0x055d2, // 1900-1909
|
||||
|
0x04ae0, 0x0a5b6, 0x0a4d0, 0x0d250, 0x1d255, 0x0b540, 0x0d6a0, 0x0ada2, 0x095b0, 0x14977, // 1910-1919
|
||||
|
0x04970, 0x0a4b0, 0x0b4b5, 0x06a50, 0x06d40, 0x1ab54, 0x02b60, 0x09570, 0x052f2, 0x04970, // 1920-1929
|
||||
|
0x06566, 0x0d4a0, 0x0ea50, 0x06e95, 0x05ad0, 0x02b60, 0x186e3, 0x092e0, 0x1c8d7, 0x0c950, // 1930-1939
|
||||
|
0x0d4a0, 0x1d8a6, 0x0b550, 0x056a0, 0x1a5b4, 0x025d0, 0x092d0, 0x0d2b2, 0x0a950, 0x0b557, // 1940-1949
|
||||
|
0x06ca0, 0x0b550, 0x15355, 0x04da0, 0x0a5b0, 0x14573, 0x052b0, 0x0a9a8, 0x0e950, 0x06aa0, // 1950-1959
|
||||
|
0x0aea6, 0x0ab50, 0x04b60, 0x0aae4, 0x0a570, 0x05260, 0x0f263, 0x0d950, 0x05b57, 0x056a0, // 1960-1969
|
||||
|
0x096d0, 0x04dd5, 0x04ad0, 0x0a4d0, 0x0d4d4, 0x0d250, 0x0d558, 0x0b540, 0x0b6a0, 0x195a6, // 1970-1979
|
||||
|
0x095b0, 0x049b0, 0x0a974, 0x0a4b0, 0x0b27a, 0x06a50, 0x06d40, 0x0af46, 0x0ab60, 0x09570, // 1980-1989
|
||||
|
0x04af5, 0x04970, 0x064b0, 0x074a3, 0x0ea50, 0x06b58, 0x05ac0, 0x0ab60, 0x096d5, 0x092e0, // 1990-1999
|
||||
|
0x0c960, 0x0d954, 0x0d4a0, 0x0da50, 0x07552, 0x056a0, 0x0abb7, 0x025d0, 0x092d0, 0x0cab5, // 2000-2009
|
||||
|
0x0a950, 0x0b4a0, 0x0baa4, 0x0ad50, 0x055d9, 0x04ba0, 0x0a5b0, 0x15176, 0x052b0, 0x0a930, // 2010-2019
|
||||
|
0x07954, 0x06aa0, 0x0ad50, 0x05b52, 0x04b60, 0x0a6e6, 0x0a4e0, 0x0d260, 0x0ea65, 0x0d530, // 2020-2029
|
||||
|
0x05aa0, 0x076a3, 0x096d0, 0x04afb, 0x04ad0, 0x0a4d0, 0x1d0b6, 0x0d250, 0x0d520, 0x0dd45, // 2030-2039
|
||||
|
0x0b5a0, 0x056d0, 0x055b2, 0x049b0, 0x0a577, 0x0a4b0, 0x0aa50, 0x1b255, 0x06d20, 0x0ada0, // 2040-2049
|
||||
|
/** Add By JJonline@JJonline.Cn**/ |
||||
|
0x14b63, 0x09370, 0x049f8, 0x04970, 0x064b0, 0x168a6, 0x0ea50, 0x06b20, 0x1a6c4, 0x0aae0, // 2050-2059
|
||||
|
0x0a2e0, 0x0d2e3, 0x0c960, 0x0d557, 0x0d4a0, 0x0da50, 0x05d55, 0x056a0, 0x0a6d0, 0x055d4, // 2060-2069
|
||||
|
0x052d0, 0x0a9b8, 0x0a950, 0x0b4a0, 0x0b6a6, 0x0ad50, 0x055a0, 0x0aba4, 0x0a5b0, 0x052b0, // 2070-2079
|
||||
|
0x0b273, 0x06930, 0x07337, 0x06aa0, 0x0ad50, 0x14b55, 0x04b60, 0x0a570, 0x054e4, 0x0d160, // 2080-2089
|
||||
|
0x0e968, 0x0d520, 0x0daa0, 0x16aa6, 0x056d0, 0x04ae0, 0x0a9d4, 0x0a2d0, 0x0d150, 0x0f252, // 2090-2099
|
||||
|
0x0d520], // 2100
|
||||
|
|
||||
|
/** |
||||
|
* 公历每个月份的天数普通表 |
||||
|
* @Array Of Property |
||||
|
* @return Number |
||||
|
*/ |
||||
|
solarMonth: [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31], |
||||
|
|
||||
|
/** |
||||
|
* 天干地支之天干速查表 |
||||
|
* @Array Of Property trans["甲","乙","丙","丁","戊","己","庚","辛","壬","癸"] |
||||
|
* @return Cn string |
||||
|
*/ |
||||
|
Gan: ['\u7532', '\u4e59', '\u4e19', '\u4e01', '\u620a', '\u5df1', '\u5e9a', '\u8f9b', '\u58ec', '\u7678'], |
||||
|
|
||||
|
/** |
||||
|
* 天干地支之地支速查表 |
||||
|
* @Array Of Property |
||||
|
* @trans["子","丑","寅","卯","辰","巳","午","未","申","酉","戌","亥"] |
||||
|
* @return Cn string |
||||
|
*/ |
||||
|
Zhi: ['\u5b50', '\u4e11', '\u5bc5', '\u536f', '\u8fb0', '\u5df3', '\u5348', '\u672a', '\u7533', '\u9149', '\u620c', '\u4ea5'], |
||||
|
|
||||
|
/** |
||||
|
* 天干地支之地支速查表<=>生肖 |
||||
|
* @Array Of Property |
||||
|
* @trans["鼠","牛","虎","兔","龙","蛇","马","羊","猴","鸡","狗","猪"] |
||||
|
* @return Cn string |
||||
|
*/ |
||||
|
Animals: ['\u9f20', '\u725b', '\u864e', '\u5154', '\u9f99', '\u86c7', '\u9a6c', '\u7f8a', '\u7334', '\u9e21', '\u72d7', '\u732a'], |
||||
|
|
||||
|
/** |
||||
|
* 24节气速查表 |
||||
|
* @Array Of Property |
||||
|
* @trans["小寒","大寒","立春","雨水","惊蛰","春分","清明","谷雨","立夏","小满","芒种","夏至","小暑","大暑","立秋","处暑","白露","秋分","寒露","霜降","立冬","小雪","大雪","冬至"] |
||||
|
* @return Cn string |
||||
|
*/ |
||||
|
solarTerm: ['\u5c0f\u5bd2', '\u5927\u5bd2', '\u7acb\u6625', '\u96e8\u6c34', '\u60ca\u86f0', '\u6625\u5206', '\u6e05\u660e', '\u8c37\u96e8', '\u7acb\u590f', '\u5c0f\u6ee1', '\u8292\u79cd', '\u590f\u81f3', '\u5c0f\u6691', '\u5927\u6691', '\u7acb\u79cb', '\u5904\u6691', '\u767d\u9732', '\u79cb\u5206', '\u5bd2\u9732', '\u971c\u964d', '\u7acb\u51ac', '\u5c0f\u96ea', '\u5927\u96ea', '\u51ac\u81f3'], |
||||
|
|
||||
|
/** |
||||
|
* 1900-2100各年的24节气日期速查表 |
||||
|
* @Array Of Property |
||||
|
* @return 0x string For splice |
||||
|
*/ |
||||
|
sTermInfo: ['9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bcf97c3598082c95f8c965cc920f', |
||||
|
'97bd0b06bdb0722c965ce1cfcc920f', 'b027097bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', |
||||
|
'97bcf97c359801ec95f8c965cc920f', '97bd0b06bdb0722c965ce1cfcc920f', 'b027097bd097c36b0b6fc9274c91aa', |
||||
|
'97b6b97bd19801ec9210c965cc920e', '97bcf97c359801ec95f8c965cc920f', '97bd0b06bdb0722c965ce1cfcc920f', |
||||
|
'b027097bd097c36b0b6fc9274c91aa', '9778397bd19801ec9210c965cc920e', '97b6b97bd19801ec95f8c965cc920f', |
||||
|
'97bd09801d98082c95f8e1cfcc920f', '97bd097bd097c36b0b6fc9210c8dc2', '9778397bd197c36c9210c9274c91aa', |
||||
|
'97b6b97bd19801ec95f8c965cc920e', '97bd09801d98082c95f8e1cfcc920f', '97bd097bd097c36b0b6fc9210c8dc2', |
||||
|
'9778397bd097c36c9210c9274c91aa', '97b6b97bd19801ec95f8c965cc920e', '97bcf97c3598082c95f8e1cfcc920f', |
||||
|
'97bd097bd097c36b0b6fc9210c8dc2', '9778397bd097c36c9210c9274c91aa', '97b6b97bd19801ec9210c965cc920e', |
||||
|
'97bcf97c3598082c95f8c965cc920f', '97bd097bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', |
||||
|
'97b6b97bd19801ec9210c965cc920e', '97bcf97c3598082c95f8c965cc920f', '97bd097bd097c35b0b6fc920fb0722', |
||||
|
'9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bcf97c359801ec95f8c965cc920f', |
||||
|
'97bd097bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', |
||||
|
'97bcf97c359801ec95f8c965cc920f', '97bd097bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', |
||||
|
'97b6b97bd19801ec9210c965cc920e', '97bcf97c359801ec95f8c965cc920f', '97bd097bd07f595b0b6fc920fb0722', |
||||
|
'9778397bd097c36b0b6fc9210c8dc2', '9778397bd19801ec9210c9274c920e', '97b6b97bd19801ec95f8c965cc920f', |
||||
|
'97bd07f5307f595b0b0bc920fb0722', '7f0e397bd097c36b0b6fc9210c8dc2', '9778397bd097c36c9210c9274c920e', |
||||
|
'97b6b97bd19801ec95f8c965cc920f', '97bd07f5307f595b0b0bc920fb0722', '7f0e397bd097c36b0b6fc9210c8dc2', |
||||
|
'9778397bd097c36c9210c9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bd07f1487f595b0b0bc920fb0722', |
||||
|
'7f0e397bd097c36b0b6fc9210c8dc2', '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', |
||||
|
'97bcf7f1487f595b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', |
||||
|
'97b6b97bd19801ec9210c965cc920e', '97bcf7f1487f595b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', |
||||
|
'9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bcf7f1487f531b0b0bb0b6fb0722', |
||||
|
'7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', |
||||
|
'97bcf7f1487f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', |
||||
|
'97b6b97bd19801ec9210c9274c920e', '97bcf7f0e47f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722', |
||||
|
'9778397bd097c36b0b6fc9210c91aa', '97b6b97bd197c36c9210c9274c920e', '97bcf7f0e47f531b0b0bb0b6fb0722', |
||||
|
'7f0e397bd07f595b0b0bc920fb0722', '9778397bd097c36b0b6fc9210c8dc2', '9778397bd097c36c9210c9274c920e', |
||||
|
'97b6b7f0e47f531b0723b0b6fb0722', '7f0e37f5307f595b0b0bc920fb0722', '7f0e397bd097c36b0b6fc9210c8dc2', |
||||
|
'9778397bd097c36b0b70c9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721', '7f0e37f1487f595b0b0bb0b6fb0722', |
||||
|
'7f0e397bd097c35b0b6fc9210c8dc2', '9778397bd097c36b0b6fc9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721', |
||||
|
'7f0e27f1487f595b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', |
||||
|
'97b6b7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', |
||||
|
'9778397bd097c36b0b6fc9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', |
||||
|
'7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721', |
||||
|
'7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722', '9778397bd097c36b0b6fc9274c91aa', |
||||
|
'97b6b7f0e47f531b0723b0787b0721', '7f0e27f0e47f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722', |
||||
|
'9778397bd097c36b0b6fc9210c91aa', '97b6b7f0e47f149b0723b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722', |
||||
|
'7f0e397bd07f595b0b0bc920fb0722', '9778397bd097c36b0b6fc9210c8dc2', '977837f0e37f149b0723b0787b0721', |
||||
|
'7f07e7f0e47f531b0723b0b6fb0722', '7f0e37f5307f595b0b0bc920fb0722', '7f0e397bd097c35b0b6fc9210c8dc2', |
||||
|
'977837f0e37f14998082b0787b0721', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e37f1487f595b0b0bb0b6fb0722', |
||||
|
'7f0e397bd097c35b0b6fc9210c8dc2', '977837f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', |
||||
|
'7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', '977837f0e37f14998082b0787b06bd', |
||||
|
'7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', |
||||
|
'977837f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', |
||||
|
'7f0e397bd07f595b0b0bc920fb0722', '977837f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', |
||||
|
'7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722', '977837f0e37f14998082b0787b06bd', |
||||
|
'7f07e7f0e47f149b0723b0787b0721', '7f0e27f0e47f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722', |
||||
|
'977837f0e37f14998082b0723b06bd', '7f07e7f0e37f149b0723b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722', |
||||
|
'7f0e397bd07f595b0b0bc920fb0722', '977837f0e37f14898082b0723b02d5', '7ec967f0e37f14998082b0787b0721', |
||||
|
'7f07e7f0e47f531b0723b0b6fb0722', '7f0e37f1487f595b0b0bb0b6fb0722', '7f0e37f0e37f14898082b0723b02d5', |
||||
|
'7ec967f0e37f14998082b0787b0721', '7f07e7f0e47f531b0723b0b6fb0722', '7f0e37f1487f531b0b0bb0b6fb0722', |
||||
|
'7f0e37f0e37f14898082b0723b02d5', '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', |
||||
|
'7f0e37f1487f531b0b0bb0b6fb0722', '7f0e37f0e37f14898082b072297c35', '7ec967f0e37f14998082b0787b06bd', |
||||
|
'7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e37f0e37f14898082b072297c35', |
||||
|
'7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', |
||||
|
'7f0e37f0e366aa89801eb072297c35', '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f149b0723b0787b0721', |
||||
|
'7f0e27f1487f531b0b0bb0b6fb0722', '7f0e37f0e366aa89801eb072297c35', '7ec967f0e37f14998082b0723b06bd', |
||||
|
'7f07e7f0e47f149b0723b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722', '7f0e37f0e366aa89801eb072297c35', |
||||
|
'7ec967f0e37f14998082b0723b06bd', '7f07e7f0e37f14998083b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722', |
||||
|
'7f0e37f0e366aa89801eb072297c35', '7ec967f0e37f14898082b0723b02d5', '7f07e7f0e37f14998082b0787b0721', |
||||
|
'7f07e7f0e47f531b0723b0b6fb0722', '7f0e36665b66aa89801e9808297c35', '665f67f0e37f14898082b0723b02d5', |
||||
|
'7ec967f0e37f14998082b0787b0721', '7f07e7f0e47f531b0723b0b6fb0722', '7f0e36665b66a449801e9808297c35', |
||||
|
'665f67f0e37f14898082b0723b02d5', '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', |
||||
|
'7f0e36665b66a449801e9808297c35', '665f67f0e37f14898082b072297c35', '7ec967f0e37f14998082b0787b06bd', |
||||
|
'7f07e7f0e47f531b0723b0b6fb0721', '7f0e26665b66a449801e9808297c35', '665f67f0e37f1489801eb072297c35', |
||||
|
'7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722'], |
||||
|
|
||||
|
/** |
||||
|
* 数字转中文速查表 |
||||
|
* @Array Of Property |
||||
|
* @trans ['日','一','二','三','四','五','六','七','八','九','十'] |
||||
|
* @return Cn string |
||||
|
*/ |
||||
|
nStr1: ['\u65e5', '\u4e00', '\u4e8c', '\u4e09', '\u56db', '\u4e94', '\u516d', '\u4e03', '\u516b', '\u4e5d', '\u5341'], |
||||
|
|
||||
|
/** |
||||
|
* 日期转农历称呼速查表 |
||||
|
* @Array Of Property |
||||
|
* @trans ['初','十','廿','卅'] |
||||
|
* @return Cn string |
||||
|
*/ |
||||
|
nStr2: ['\u521d', '\u5341', '\u5eff', '\u5345'], |
||||
|
|
||||
|
/** |
||||
|
* 月份转农历称呼速查表 |
||||
|
* @Array Of Property |
||||
|
* @trans ['正','一','二','三','四','五','六','七','八','九','十','冬','腊'] |
||||
|
* @return Cn string |
||||
|
*/ |
||||
|
nStr3: ['\u6b63', '\u4e8c', '\u4e09', '\u56db', '\u4e94', '\u516d', '\u4e03', '\u516b', '\u4e5d', '\u5341', '\u51ac', '\u814a'], |
||||
|
|
||||
|
/** |
||||
|
* 返回农历y年一整年的总天数 |
||||
|
* @param lunar Year |
||||
|
* @return Number |
||||
|
* @eg:var count = calendar.lYearDays(1987) ;//count=387
|
||||
|
*/ |
||||
|
lYearDays: function (y) { |
||||
|
var i; var sum = 348 |
||||
|
for (i = 0x8000; i > 0x8; i >>= 1) { sum += (this.lunarInfo[y - 1900] & i) ? 1 : 0 } |
||||
|
return (sum + this.leapDays(y)) |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 返回农历y年闰月是哪个月;若y年没有闰月 则返回0 |
||||
|
* @param lunar Year |
||||
|
* @return Number (0-12) |
||||
|
* @eg:var leapMonth = calendar.leapMonth(1987) ;//leapMonth=6
|
||||
|
*/ |
||||
|
leapMonth: function (y) { // 闰字编码 \u95f0
|
||||
|
return (this.lunarInfo[y - 1900] & 0xf) |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 返回农历y年闰月的天数 若该年没有闰月则返回0 |
||||
|
* @param lunar Year |
||||
|
* @return Number (0、29、30) |
||||
|
* @eg:var leapMonthDay = calendar.leapDays(1987) ;//leapMonthDay=29
|
||||
|
*/ |
||||
|
leapDays: function (y) { |
||||
|
if (this.leapMonth(y)) { |
||||
|
return ((this.lunarInfo[y - 1900] & 0x10000) ? 30 : 29) |
||||
|
} |
||||
|
return (0) |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 返回农历y年m月(非闰月)的总天数,计算m为闰月时的天数请使用leapDays方法 |
||||
|
* @param lunar Year |
||||
|
* @return Number (-1、29、30) |
||||
|
* @eg:var MonthDay = calendar.monthDays(1987,9) ;//MonthDay=29
|
||||
|
*/ |
||||
|
monthDays: function (y, m) { |
||||
|
if (m > 12 || m < 1) { return -1 }// 月份参数从1至12,参数错误返回-1
|
||||
|
return ((this.lunarInfo[y - 1900] & (0x10000 >> m)) ? 30 : 29) |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 返回公历(!)y年m月的天数 |
||||
|
* @param solar Year |
||||
|
* @return Number (-1、28、29、30、31) |
||||
|
* @eg:var solarMonthDay = calendar.leapDays(1987) ;//solarMonthDay=30
|
||||
|
*/ |
||||
|
solarDays: function (y, m) { |
||||
|
if (m > 12 || m < 1) { return -1 } // 若参数错误 返回-1
|
||||
|
var ms = m - 1 |
||||
|
if (ms == 1) { // 2月份的闰平规律测算后确认返回28或29
|
||||
|
return (((y % 4 == 0) && (y % 100 != 0) || (y % 400 == 0)) ? 29 : 28) |
||||
|
} else { |
||||
|
return (this.solarMonth[ms]) |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 农历年份转换为干支纪年 |
||||
|
* @param lYear 农历年的年份数 |
||||
|
* @return Cn string |
||||
|
*/ |
||||
|
toGanZhiYear: function (lYear) { |
||||
|
var ganKey = (lYear - 3) % 10 |
||||
|
var zhiKey = (lYear - 3) % 12 |
||||
|
if (ganKey == 0) ganKey = 10// 如果余数为0则为最后一个天干
|
||||
|
if (zhiKey == 0) zhiKey = 12// 如果余数为0则为最后一个地支
|
||||
|
return this.Gan[ganKey - 1] + this.Zhi[zhiKey - 1] |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 公历月、日判断所属星座 |
||||
|
* @param cMonth [description] |
||||
|
* @param cDay [description] |
||||
|
* @return Cn string |
||||
|
*/ |
||||
|
toAstro: function (cMonth, cDay) { |
||||
|
var s = '\u9b54\u7faf\u6c34\u74f6\u53cc\u9c7c\u767d\u7f8a\u91d1\u725b\u53cc\u5b50\u5de8\u87f9\u72ee\u5b50\u5904\u5973\u5929\u79e4\u5929\u874e\u5c04\u624b\u9b54\u7faf' |
||||
|
var arr = [20, 19, 21, 21, 21, 22, 23, 23, 23, 23, 22, 22] |
||||
|
return s.substr(cMonth * 2 - (cDay < arr[cMonth - 1] ? 2 : 0), 2) + '\u5ea7'// 座
|
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 传入offset偏移量返回干支 |
||||
|
* @param offset 相对甲子的偏移量 |
||||
|
* @return Cn string |
||||
|
*/ |
||||
|
toGanZhi: function (offset) { |
||||
|
return this.Gan[offset % 10] + this.Zhi[offset % 12] |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 传入公历(!)y年获得该年第n个节气的公历日期 |
||||
|
* @param y公历年(1900-2100);n二十四节气中的第几个节气(1~24);从n=1(小寒)算起 |
||||
|
* @return day Number |
||||
|
* @eg:var _24 = calendar.getTerm(1987,3) ;//_24=4;意即1987年2月4日立春
|
||||
|
*/ |
||||
|
getTerm: function (y, n) { |
||||
|
if (y < 1900 || y > 2100) { return -1 } |
||||
|
if (n < 1 || n > 24) { return -1 } |
||||
|
var _table = this.sTermInfo[y - 1900] |
||||
|
var _info = [ |
||||
|
parseInt('0x' + _table.substr(0, 5)).toString(), |
||||
|
parseInt('0x' + _table.substr(5, 5)).toString(), |
||||
|
parseInt('0x' + _table.substr(10, 5)).toString(), |
||||
|
parseInt('0x' + _table.substr(15, 5)).toString(), |
||||
|
parseInt('0x' + _table.substr(20, 5)).toString(), |
||||
|
parseInt('0x' + _table.substr(25, 5)).toString() |
||||
|
] |
||||
|
var _calday = [ |
||||
|
_info[0].substr(0, 1), |
||||
|
_info[0].substr(1, 2), |
||||
|
_info[0].substr(3, 1), |
||||
|
_info[0].substr(4, 2), |
||||
|
|
||||
|
_info[1].substr(0, 1), |
||||
|
_info[1].substr(1, 2), |
||||
|
_info[1].substr(3, 1), |
||||
|
_info[1].substr(4, 2), |
||||
|
|
||||
|
_info[2].substr(0, 1), |
||||
|
_info[2].substr(1, 2), |
||||
|
_info[2].substr(3, 1), |
||||
|
_info[2].substr(4, 2), |
||||
|
|
||||
|
_info[3].substr(0, 1), |
||||
|
_info[3].substr(1, 2), |
||||
|
_info[3].substr(3, 1), |
||||
|
_info[3].substr(4, 2), |
||||
|
|
||||
|
_info[4].substr(0, 1), |
||||
|
_info[4].substr(1, 2), |
||||
|
_info[4].substr(3, 1), |
||||
|
_info[4].substr(4, 2), |
||||
|
|
||||
|
_info[5].substr(0, 1), |
||||
|
_info[5].substr(1, 2), |
||||
|
_info[5].substr(3, 1), |
||||
|
_info[5].substr(4, 2) |
||||
|
] |
||||
|
return parseInt(_calday[n - 1]) |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 传入农历数字月份返回汉语通俗表示法 |
||||
|
* @param lunar month |
||||
|
* @return Cn string |
||||
|
* @eg:var cnMonth = calendar.toChinaMonth(12) ;//cnMonth='腊月'
|
||||
|
*/ |
||||
|
toChinaMonth: function (m) { // 月 => \u6708
|
||||
|
if (m > 12 || m < 1) { return -1 } // 若参数错误 返回-1
|
||||
|
var s = this.nStr3[m - 1] |
||||
|
s += '\u6708'// 加上月字
|
||||
|
return s |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 传入农历日期数字返回汉字表示法 |
||||
|
* @param lunar day |
||||
|
* @return Cn string |
||||
|
* @eg:var cnDay = calendar.toChinaDay(21) ;//cnMonth='廿一'
|
||||
|
*/ |
||||
|
toChinaDay: function (d) { // 日 => \u65e5
|
||||
|
var s |
||||
|
switch (d) { |
||||
|
case 10: |
||||
|
s = '\u521d\u5341'; break |
||||
|
case 20: |
||||
|
s = '\u4e8c\u5341'; break |
||||
|
break |
||||
|
case 30: |
||||
|
s = '\u4e09\u5341'; break |
||||
|
break |
||||
|
default : |
||||
|
s = this.nStr2[Math.floor(d / 10)] |
||||
|
s += this.nStr1[d % 10] |
||||
|
} |
||||
|
return (s) |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 年份转生肖[!仅能大致转换] => 精确划分生肖分界线是“立春” |
||||
|
* @param y year |
||||
|
* @return Cn string |
||||
|
* @eg:var animal = calendar.getAnimal(1987) ;//animal='兔'
|
||||
|
*/ |
||||
|
getAnimal: function (y) { |
||||
|
return this.Animals[(y - 4) % 12] |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 传入阳历年月日获得详细的公历、农历object信息 <=>JSON |
||||
|
* @param y solar year |
||||
|
* @param m solar month |
||||
|
* @param d solar day |
||||
|
* @return JSON object |
||||
|
* @eg:console.log(calendar.solar2lunar(1987,11,01)); |
||||
|
*/ |
||||
|
solar2lunar: function (y, m, d) { // 参数区间1900.1.31~2100.12.31
|
||||
|
// 年份限定、上限
|
||||
|
if (y < 1900 || y > 2100) { |
||||
|
return -1// undefined转换为数字变为NaN
|
||||
|
} |
||||
|
// 公历传参最下限
|
||||
|
if (y == 1900 && m == 1 && d < 31) { |
||||
|
return -1 |
||||
|
} |
||||
|
// 未传参 获得当天
|
||||
|
if (!y) { |
||||
|
var objDate = new Date() |
||||
|
} else { |
||||
|
var objDate = new Date(y, parseInt(m) - 1, d) |
||||
|
} |
||||
|
var i; var leap = 0; var temp = 0 |
||||
|
// 修正ymd参数
|
||||
|
var y = objDate.getFullYear() |
||||
|
var m = objDate.getMonth() + 1 |
||||
|
var d = objDate.getDate() |
||||
|
var offset = (Date.UTC(objDate.getFullYear(), objDate.getMonth(), objDate.getDate()) - Date.UTC(1900, 0, 31)) / 86400000 |
||||
|
for (i = 1900; i < 2101 && offset > 0; i++) { |
||||
|
temp = this.lYearDays(i) |
||||
|
offset -= temp |
||||
|
} |
||||
|
if (offset < 0) { |
||||
|
offset += temp; i-- |
||||
|
} |
||||
|
|
||||
|
// 是否今天
|
||||
|
var isTodayObj = new Date() |
||||
|
var isToday = false |
||||
|
if (isTodayObj.getFullYear() == y && isTodayObj.getMonth() + 1 == m && isTodayObj.getDate() == d) { |
||||
|
isToday = true |
||||
|
} |
||||
|
// 星期几
|
||||
|
var nWeek = objDate.getDay() |
||||
|
var cWeek = this.nStr1[nWeek] |
||||
|
// 数字表示周几顺应天朝周一开始的惯例
|
||||
|
if (nWeek == 0) { |
||||
|
nWeek = 7 |
||||
|
} |
||||
|
// 农历年
|
||||
|
var year = i |
||||
|
var leap = this.leapMonth(i) // 闰哪个月
|
||||
|
var isLeap = false |
||||
|
|
||||
|
// 效验闰月
|
||||
|
for (i = 1; i < 13 && offset > 0; i++) { |
||||
|
// 闰月
|
||||
|
if (leap > 0 && i == (leap + 1) && isLeap == false) { |
||||
|
--i |
||||
|
isLeap = true; temp = this.leapDays(year) // 计算农历闰月天数
|
||||
|
} else { |
||||
|
temp = this.monthDays(year, i)// 计算农历普通月天数
|
||||
|
} |
||||
|
// 解除闰月
|
||||
|
if (isLeap == true && i == (leap + 1)) { isLeap = false } |
||||
|
offset -= temp |
||||
|
} |
||||
|
// 闰月导致数组下标重叠取反
|
||||
|
if (offset == 0 && leap > 0 && i == leap + 1) { |
||||
|
if (isLeap) { |
||||
|
isLeap = false |
||||
|
} else { |
||||
|
isLeap = true; --i |
||||
|
} |
||||
|
} |
||||
|
if (offset < 0) { |
||||
|
offset += temp; --i |
||||
|
} |
||||
|
// 农历月
|
||||
|
var month = i |
||||
|
// 农历日
|
||||
|
var day = offset + 1 |
||||
|
// 天干地支处理
|
||||
|
var sm = m - 1 |
||||
|
var gzY = this.toGanZhiYear(year) |
||||
|
|
||||
|
// 当月的两个节气
|
||||
|
// bugfix-2017-7-24 11:03:38 use lunar Year Param `y` Not `year`
|
||||
|
var firstNode = this.getTerm(y, (m * 2 - 1))// 返回当月「节」为几日开始
|
||||
|
var secondNode = this.getTerm(y, (m * 2))// 返回当月「节」为几日开始
|
||||
|
|
||||
|
// 依据12节气修正干支月
|
||||
|
var gzM = this.toGanZhi((y - 1900) * 12 + m + 11) |
||||
|
if (d >= firstNode) { |
||||
|
gzM = this.toGanZhi((y - 1900) * 12 + m + 12) |
||||
|
} |
||||
|
|
||||
|
// 传入的日期的节气与否
|
||||
|
var isTerm = false |
||||
|
var Term = null |
||||
|
if (firstNode == d) { |
||||
|
isTerm = true |
||||
|
Term = this.solarTerm[m * 2 - 2] |
||||
|
} |
||||
|
if (secondNode == d) { |
||||
|
isTerm = true |
||||
|
Term = this.solarTerm[m * 2 - 1] |
||||
|
} |
||||
|
// 日柱 当月一日与 1900/1/1 相差天数
|
||||
|
var dayCyclical = Date.UTC(y, sm, 1, 0, 0, 0, 0) / 86400000 + 25567 + 10 |
||||
|
var gzD = this.toGanZhi(dayCyclical + d - 1) |
||||
|
// 该日期所属的星座
|
||||
|
var astro = this.toAstro(m, d) |
||||
|
|
||||
|
return { 'lYear': year, 'lMonth': month, 'lDay': day, 'Animal': this.getAnimal(year), 'IMonthCn': (isLeap ? '\u95f0' : '') + this.toChinaMonth(month), 'IDayCn': this.toChinaDay(day), 'cYear': y, 'cMonth': m, 'cDay': d, 'gzYear': gzY, 'gzMonth': gzM, 'gzDay': gzD, 'isToday': isToday, 'isLeap': isLeap, 'nWeek': nWeek, 'ncWeek': '\u661f\u671f' + cWeek, 'isTerm': isTerm, 'Term': Term, 'astro': astro } |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 传入农历年月日以及传入的月份是否闰月获得详细的公历、农历object信息 <=>JSON |
||||
|
* @param y lunar year |
||||
|
* @param m lunar month |
||||
|
* @param d lunar day |
||||
|
* @param isLeapMonth lunar month is leap or not.[如果是农历闰月第四个参数赋值true即可] |
||||
|
* @return JSON object |
||||
|
* @eg:console.log(calendar.lunar2solar(1987,9,10)); |
||||
|
*/ |
||||
|
lunar2solar: function (y, m, d, isLeapMonth) { // 参数区间1900.1.31~2100.12.1
|
||||
|
var isLeapMonth = !!isLeapMonth |
||||
|
var leapOffset = 0 |
||||
|
var leapMonth = this.leapMonth(y) |
||||
|
var leapDay = this.leapDays(y) |
||||
|
if (isLeapMonth && (leapMonth != m)) { return -1 }// 传参要求计算该闰月公历 但该年得出的闰月与传参的月份并不同
|
||||
|
if (y == 2100 && m == 12 && d > 1 || y == 1900 && m == 1 && d < 31) { return -1 }// 超出了最大极限值
|
||||
|
var day = this.monthDays(y, m) |
||||
|
var _day = day |
||||
|
// bugFix 2016-9-25
|
||||
|
// if month is leap, _day use leapDays method
|
||||
|
if (isLeapMonth) { |
||||
|
_day = this.leapDays(y, m) |
||||
|
} |
||||
|
if (y < 1900 || y > 2100 || d > _day) { return -1 }// 参数合法性效验
|
||||
|
|
||||
|
// 计算农历的时间差
|
||||
|
var offset = 0 |
||||
|
for (var i = 1900; i < y; i++) { |
||||
|
offset += this.lYearDays(i) |
||||
|
} |
||||
|
var leap = 0; var isAdd = false |
||||
|
for (var i = 1; i < m; i++) { |
||||
|
leap = this.leapMonth(y) |
||||
|
if (!isAdd) { // 处理闰月
|
||||
|
if (leap <= i && leap > 0) { |
||||
|
offset += this.leapDays(y); isAdd = true |
||||
|
} |
||||
|
} |
||||
|
offset += this.monthDays(y, i) |
||||
|
} |
||||
|
// 转换闰月农历 需补充该年闰月的前一个月的时差
|
||||
|
if (isLeapMonth) { offset += day } |
||||
|
// 1900年农历正月一日的公历时间为1900年1月30日0时0分0秒(该时间也是本农历的最开始起始点)
|
||||
|
var stmap = Date.UTC(1900, 1, 30, 0, 0, 0) |
||||
|
var calObj = new Date((offset + d - 31) * 86400000 + stmap) |
||||
|
var cY = calObj.getUTCFullYear() |
||||
|
var cM = calObj.getUTCMonth() + 1 |
||||
|
var cD = calObj.getUTCDate() |
||||
|
|
||||
|
return this.solar2lunar(cY, cM, cD) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export default calendar |
||||
@ -0,0 +1,12 @@ |
|||||
|
{ |
||||
|
"uni-calender.ok": "ok", |
||||
|
"uni-calender.cancel": "cancel", |
||||
|
"uni-calender.today": "today", |
||||
|
"uni-calender.MON": "MON", |
||||
|
"uni-calender.TUE": "TUE", |
||||
|
"uni-calender.WED": "WED", |
||||
|
"uni-calender.THU": "THU", |
||||
|
"uni-calender.FRI": "FRI", |
||||
|
"uni-calender.SAT": "SAT", |
||||
|
"uni-calender.SUN": "SUN" |
||||
|
} |
||||
@ -0,0 +1,8 @@ |
|||||
|
import en from './en.json' |
||||
|
import zhHans from './zh-Hans.json' |
||||
|
import zhHant from './zh-Hant.json' |
||||
|
export default { |
||||
|
en, |
||||
|
'zh-Hans': zhHans, |
||||
|
'zh-Hant': zhHant |
||||
|
} |
||||
@ -0,0 +1,12 @@ |
|||||
|
{ |
||||
|
"uni-calender.ok": "确定", |
||||
|
"uni-calender.cancel": "取消", |
||||
|
"uni-calender.today": "今日", |
||||
|
"uni-calender.SUN": "日", |
||||
|
"uni-calender.MON": "一", |
||||
|
"uni-calender.TUE": "二", |
||||
|
"uni-calender.WED": "三", |
||||
|
"uni-calender.THU": "四", |
||||
|
"uni-calender.FRI": "五", |
||||
|
"uni-calender.SAT": "六" |
||||
|
} |
||||
@ -0,0 +1,12 @@ |
|||||
|
{ |
||||
|
"uni-calender.ok": "確定", |
||||
|
"uni-calender.cancel": "取消", |
||||
|
"uni-calender.today": "今日", |
||||
|
"uni-calender.SUN": "日", |
||||
|
"uni-calender.MON": "一", |
||||
|
"uni-calender.TUE": "二", |
||||
|
"uni-calender.WED": "三", |
||||
|
"uni-calender.THU": "四", |
||||
|
"uni-calender.FRI": "五", |
||||
|
"uni-calender.SAT": "六" |
||||
|
} |
||||
@ -0,0 +1,181 @@ |
|||||
|
<template> |
||||
|
<view class="uni-calendar-item__weeks-box" :class="{ |
||||
|
'uni-calendar-item--disable':weeks.disable, |
||||
|
'uni-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay, |
||||
|
'uni-calendar-item--checked':(calendar.fullDate === weeks.fullDate && !weeks.isDay) , |
||||
|
'uni-calendar-item--before-checked':weeks.beforeMultiple, |
||||
|
'uni-calendar-item--multiple': weeks.multiple, |
||||
|
'uni-calendar-item--after-checked':weeks.afterMultiple, |
||||
|
}" |
||||
|
@click="choiceDate(weeks)"> |
||||
|
<view class="uni-calendar-item__weeks-box-item"> |
||||
|
<text v-if="selected&&weeks.extraInfo" class="uni-calendar-item__weeks-box-circle"></text> |
||||
|
<text class="uni-calendar-item__weeks-box-text" :class="{ |
||||
|
'uni-calendar-item--isDay-text': weeks.isDay, |
||||
|
'uni-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay, |
||||
|
'uni-calendar-item--checked':calendar.fullDate === weeks.fullDate && !weeks.isDay, |
||||
|
'uni-calendar-item--before-checked':weeks.beforeMultiple, |
||||
|
'uni-calendar-item--multiple': weeks.multiple, |
||||
|
'uni-calendar-item--after-checked':weeks.afterMultiple, |
||||
|
'uni-calendar-item--disable':weeks.disable, |
||||
|
}">{{weeks.date}}</text> |
||||
|
<text v-if="!lunar&&!weeks.extraInfo && weeks.isDay" class="uni-calendar-item__weeks-lunar-text" :class="{ |
||||
|
'uni-calendar-item--isDay-text':weeks.isDay, |
||||
|
'uni-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay, |
||||
|
'uni-calendar-item--checked':calendar.fullDate === weeks.fullDate && !weeks.isDay, |
||||
|
'uni-calendar-item--before-checked':weeks.beforeMultiple, |
||||
|
'uni-calendar-item--multiple': weeks.multiple, |
||||
|
'uni-calendar-item--after-checked':weeks.afterMultiple, |
||||
|
}">{{todayText}}</text> |
||||
|
<text v-if="lunar&&!weeks.extraInfo" class="uni-calendar-item__weeks-lunar-text" :class="{ |
||||
|
'uni-calendar-item--isDay-text':weeks.isDay, |
||||
|
'uni-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay, |
||||
|
'uni-calendar-item--checked':calendar.fullDate === weeks.fullDate && !weeks.isDay, |
||||
|
'uni-calendar-item--before-checked':weeks.beforeMultiple, |
||||
|
'uni-calendar-item--multiple': weeks.multiple, |
||||
|
'uni-calendar-item--after-checked':weeks.afterMultiple, |
||||
|
'uni-calendar-item--disable':weeks.disable, |
||||
|
}">{{weeks.isDay ? todayText : (weeks.lunar.IDayCn === '初一'?weeks.lunar.IMonthCn:weeks.lunar.IDayCn)}}</text> |
||||
|
<text v-if="weeks.extraInfo&&weeks.extraInfo.info" class="uni-calendar-item__weeks-lunar-text" :class="{ |
||||
|
'uni-calendar-item--extra':weeks.extraInfo.info, |
||||
|
'uni-calendar-item--isDay-text':weeks.isDay, |
||||
|
'uni-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay, |
||||
|
'uni-calendar-item--checked':calendar.fullDate === weeks.fullDate && !weeks.isDay, |
||||
|
'uni-calendar-item--before-checked':weeks.beforeMultiple, |
||||
|
'uni-calendar-item--multiple': weeks.multiple, |
||||
|
'uni-calendar-item--after-checked':weeks.afterMultiple, |
||||
|
'uni-calendar-item--disable':weeks.disable, |
||||
|
}">{{weeks.extraInfo.info}}</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import { |
||||
|
initVueI18n |
||||
|
} from '@dcloudio/uni-i18n' |
||||
|
import messages from './i18n/index.js' |
||||
|
const { t } = initVueI18n(messages) |
||||
|
export default { |
||||
|
emits:['change'], |
||||
|
props: { |
||||
|
weeks: { |
||||
|
type: Object, |
||||
|
default () { |
||||
|
return {} |
||||
|
} |
||||
|
}, |
||||
|
calendar: { |
||||
|
type: Object, |
||||
|
default: () => { |
||||
|
return {} |
||||
|
} |
||||
|
}, |
||||
|
selected: { |
||||
|
type: Array, |
||||
|
default: () => { |
||||
|
return [] |
||||
|
} |
||||
|
}, |
||||
|
lunar: { |
||||
|
type: Boolean, |
||||
|
default: false |
||||
|
} |
||||
|
}, |
||||
|
computed: { |
||||
|
todayText() { |
||||
|
return t("uni-calender.today") |
||||
|
}, |
||||
|
}, |
||||
|
methods: { |
||||
|
choiceDate(weeks) { |
||||
|
this.$emit('change', weeks) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss" scoped> |
||||
|
.uni-calendar-item__weeks-box { |
||||
|
flex: 1; |
||||
|
/* #ifndef APP-NVUE */ |
||||
|
display: flex; |
||||
|
/* #endif */ |
||||
|
flex-direction: column; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
} |
||||
|
|
||||
|
.uni-calendar-item__weeks-box-text { |
||||
|
font-size: $uni-font-size-base; |
||||
|
color: $uni-text-color; |
||||
|
} |
||||
|
|
||||
|
.uni-calendar-item__weeks-lunar-text { |
||||
|
font-size: $uni-font-size-sm; |
||||
|
color: $uni-text-color; |
||||
|
} |
||||
|
|
||||
|
.uni-calendar-item__weeks-box-item { |
||||
|
position: relative; |
||||
|
/* #ifndef APP-NVUE */ |
||||
|
display: flex; |
||||
|
/* #endif */ |
||||
|
flex-direction: column; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
width: 100rpx; |
||||
|
height: 100rpx; |
||||
|
} |
||||
|
|
||||
|
.uni-calendar-item__weeks-box-circle { |
||||
|
position: absolute; |
||||
|
top: 5px; |
||||
|
right: 5px; |
||||
|
width: 8px; |
||||
|
height: 8px; |
||||
|
border-radius: 8px; |
||||
|
background-color: $uni-color-error; |
||||
|
|
||||
|
} |
||||
|
|
||||
|
.uni-calendar-item--disable { |
||||
|
background-color: rgba(249, 249, 249, $uni-opacity-disabled); |
||||
|
color: $uni-text-color-disable; |
||||
|
} |
||||
|
|
||||
|
.uni-calendar-item--isDay-text { |
||||
|
color: $uni-color-primary; |
||||
|
} |
||||
|
|
||||
|
.uni-calendar-item--isDay { |
||||
|
background-color: $uni-color-primary; |
||||
|
opacity: 0.8; |
||||
|
color: #fff; |
||||
|
} |
||||
|
|
||||
|
.uni-calendar-item--extra { |
||||
|
color: $uni-color-error; |
||||
|
opacity: 0.8; |
||||
|
} |
||||
|
|
||||
|
.uni-calendar-item--checked { |
||||
|
background-color: $uni-color-primary; |
||||
|
color: #fff; |
||||
|
opacity: 0.8; |
||||
|
} |
||||
|
|
||||
|
.uni-calendar-item--multiple { |
||||
|
background-color: $uni-color-primary; |
||||
|
color: #fff; |
||||
|
opacity: 0.8; |
||||
|
} |
||||
|
.uni-calendar-item--before-checked { |
||||
|
background-color: #ff5a5f; |
||||
|
color: #fff; |
||||
|
} |
||||
|
.uni-calendar-item--after-checked { |
||||
|
background-color: #ff5a5f; |
||||
|
color: #fff; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,551 @@ |
|||||
|
<template> |
||||
|
<view class="uni-calendar"> |
||||
|
<view v-if="!insert&&show" class="uni-calendar__mask" :class="{'uni-calendar--mask-show':aniMaskShow}" @click="clean"></view> |
||||
|
<view v-if="insert || show" class="uni-calendar__content" :class="{'uni-calendar--fixed':!insert,'uni-calendar--ani-show':aniMaskShow}"> |
||||
|
<view v-if="!insert" class="uni-calendar__header uni-calendar--fixed-top"> |
||||
|
<view class="uni-calendar__header-btn-box" @click="close"> |
||||
|
<text class="uni-calendar__header-text uni-calendar--fixed-width">{{cancelText}}</text> |
||||
|
</view> |
||||
|
<view class="uni-calendar__header-btn-box" @click="confirm"> |
||||
|
<text class="uni-calendar__header-text uni-calendar--fixed-width">{{okText}}</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
<view class="uni-calendar__header"> |
||||
|
<view class="uni-calendar__header-btn-box" @click.stop="pre"> |
||||
|
<view class="uni-calendar__header-btn uni-calendar--left"></view> |
||||
|
</view> |
||||
|
<picker mode="date" :value="date" fields="month" @change="bindDateChange"> |
||||
|
<text class="uni-calendar__header-text">{{ (nowDate.year||'') +' / '+( nowDate.month||'')}}</text> |
||||
|
</picker> |
||||
|
<view class="uni-calendar__header-btn-box" @click.stop="next"> |
||||
|
<view class="uni-calendar__header-btn uni-calendar--right"></view> |
||||
|
</view> |
||||
|
<text class="uni-calendar__backtoday" @click="backtoday">{{todayText}}</text> |
||||
|
|
||||
|
</view> |
||||
|
<view class="uni-calendar__box"> |
||||
|
<view v-if="showMonth" class="uni-calendar__box-bg"> |
||||
|
<text class="uni-calendar__box-bg-text">{{nowDate.month}}</text> |
||||
|
</view> |
||||
|
<view class="uni-calendar__weeks"> |
||||
|
<view class="uni-calendar__weeks-day"> |
||||
|
<text class="uni-calendar__weeks-day-text">{{SUNText}}</text> |
||||
|
</view> |
||||
|
<view class="uni-calendar__weeks-day"> |
||||
|
<text class="uni-calendar__weeks-day-text">{{monText}}</text> |
||||
|
</view> |
||||
|
<view class="uni-calendar__weeks-day"> |
||||
|
<text class="uni-calendar__weeks-day-text">{{TUEText}}</text> |
||||
|
</view> |
||||
|
<view class="uni-calendar__weeks-day"> |
||||
|
<text class="uni-calendar__weeks-day-text">{{WEDText}}</text> |
||||
|
</view> |
||||
|
<view class="uni-calendar__weeks-day"> |
||||
|
<text class="uni-calendar__weeks-day-text">{{THUText}}</text> |
||||
|
</view> |
||||
|
<view class="uni-calendar__weeks-day"> |
||||
|
<text class="uni-calendar__weeks-day-text">{{FRIText}}</text> |
||||
|
</view> |
||||
|
<view class="uni-calendar__weeks-day"> |
||||
|
<text class="uni-calendar__weeks-day-text">{{SATText}}</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
<view class="uni-calendar__weeks" v-for="(item,weekIndex) in weeks" :key="weekIndex"> |
||||
|
<view class="uni-calendar__weeks-item" v-for="(weeks,weeksIndex) in item" :key="weeksIndex"> |
||||
|
<calendar-item class="uni-calendar-item--hook" :weeks="weeks" :calendar="calendar" :selected="selected" :lunar="lunar" @change="choiceDate"></calendar-item> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import Calendar from './util.js'; |
||||
|
import calendarItem from './uni-calendar-item.vue' |
||||
|
import { |
||||
|
initVueI18n |
||||
|
} from '@dcloudio/uni-i18n' |
||||
|
import messages from './i18n/index.js' |
||||
|
const { t } = initVueI18n(messages) |
||||
|
/** |
||||
|
* Calendar 日历 |
||||
|
* @description 日历组件可以查看日期,选择任意范围内的日期,打点操作。常用场景如:酒店日期预订、火车机票选择购买日期、上下班打卡等 |
||||
|
* @tutorial https://ext.dcloud.net.cn/plugin?id=56 |
||||
|
* @property {String} date 自定义当前时间,默认为今天 |
||||
|
* @property {Boolean} lunar 显示农历 |
||||
|
* @property {String} startDate 日期选择范围-开始日期 |
||||
|
* @property {String} endDate 日期选择范围-结束日期 |
||||
|
* @property {Boolean} range 范围选择 |
||||
|
* @property {Boolean} insert = [true|false] 插入模式,默认为false |
||||
|
* @value true 弹窗模式 |
||||
|
* @value false 插入模式 |
||||
|
* @property {Boolean} clearDate = [true|false] 弹窗模式是否清空上次选择内容 |
||||
|
* @property {Array} selected 打点,期待格式[{date: '2019-06-27', info: '签到', data: { custom: '自定义信息', name: '自定义消息头',xxx:xxx... }}] |
||||
|
* @property {Boolean} showMonth 是否选择月份为背景 |
||||
|
* @event {Function} change 日期改变,`insert :ture` 时生效 |
||||
|
* @event {Function} confirm 确认选择`insert :false` 时生效 |
||||
|
* @event {Function} monthSwitch 切换月份时触发 |
||||
|
* @example <uni-calendar :insert="true":lunar="true" :start-date="'2019-3-2'":end-date="'2019-5-20'"@change="change" /> |
||||
|
*/ |
||||
|
export default { |
||||
|
components: { |
||||
|
calendarItem |
||||
|
}, |
||||
|
emits:['close','confirm','change','monthSwitch'], |
||||
|
props: { |
||||
|
date: { |
||||
|
type: String, |
||||
|
default: '' |
||||
|
}, |
||||
|
selected: { |
||||
|
type: Array, |
||||
|
default () { |
||||
|
return [] |
||||
|
} |
||||
|
}, |
||||
|
lunar: { |
||||
|
type: Boolean, |
||||
|
default: false |
||||
|
}, |
||||
|
startDate: { |
||||
|
type: String, |
||||
|
default: '' |
||||
|
}, |
||||
|
endDate: { |
||||
|
type: String, |
||||
|
default: '' |
||||
|
}, |
||||
|
range: { |
||||
|
type: Boolean, |
||||
|
default: false |
||||
|
}, |
||||
|
insert: { |
||||
|
type: Boolean, |
||||
|
default: true |
||||
|
}, |
||||
|
showMonth: { |
||||
|
type: Boolean, |
||||
|
default: true |
||||
|
}, |
||||
|
clearDate: { |
||||
|
type: Boolean, |
||||
|
default: true |
||||
|
} |
||||
|
}, |
||||
|
data() { |
||||
|
return { |
||||
|
show: false, |
||||
|
weeks: [], |
||||
|
calendar: {}, |
||||
|
nowDate: '', |
||||
|
aniMaskShow: false |
||||
|
} |
||||
|
}, |
||||
|
computed:{ |
||||
|
/** |
||||
|
* for i18n |
||||
|
*/ |
||||
|
|
||||
|
okText() { |
||||
|
return t("uni-calender.ok") |
||||
|
}, |
||||
|
cancelText() { |
||||
|
return t("uni-calender.cancel") |
||||
|
}, |
||||
|
todayText() { |
||||
|
return t("uni-calender.today") |
||||
|
}, |
||||
|
monText() { |
||||
|
return t("uni-calender.MON") |
||||
|
}, |
||||
|
TUEText() { |
||||
|
return t("uni-calender.TUE") |
||||
|
}, |
||||
|
WEDText() { |
||||
|
return t("uni-calender.WED") |
||||
|
}, |
||||
|
THUText() { |
||||
|
return t("uni-calender.THU") |
||||
|
}, |
||||
|
FRIText() { |
||||
|
return t("uni-calender.FRI") |
||||
|
}, |
||||
|
SATText() { |
||||
|
return t("uni-calender.SAT") |
||||
|
}, |
||||
|
SUNText() { |
||||
|
return t("uni-calender.SUN") |
||||
|
}, |
||||
|
}, |
||||
|
watch: { |
||||
|
date(newVal) { |
||||
|
// this.cale.setDate(newVal) |
||||
|
this.init(newVal) |
||||
|
}, |
||||
|
startDate(val){ |
||||
|
this.cale.resetSatrtDate(val) |
||||
|
this.cale.setDate(this.nowDate.fullDate) |
||||
|
this.weeks = this.cale.weeks |
||||
|
}, |
||||
|
endDate(val){ |
||||
|
this.cale.resetEndDate(val) |
||||
|
this.cale.setDate(this.nowDate.fullDate) |
||||
|
this.weeks = this.cale.weeks |
||||
|
}, |
||||
|
selected(newVal) { |
||||
|
this.cale.setSelectInfo(this.nowDate.fullDate, newVal) |
||||
|
this.weeks = this.cale.weeks |
||||
|
} |
||||
|
}, |
||||
|
created() { |
||||
|
// 获取日历方法实例 |
||||
|
this.cale = new Calendar({ |
||||
|
// date: new Date(), |
||||
|
selected: this.selected, |
||||
|
startDate: this.startDate, |
||||
|
endDate: this.endDate, |
||||
|
range: this.range, |
||||
|
}) |
||||
|
// 选中某一天 |
||||
|
// this.cale.setDate(this.date) |
||||
|
this.init(this.date) |
||||
|
// this.setDay |
||||
|
}, |
||||
|
methods: { |
||||
|
// 取消穿透 |
||||
|
clean() {}, |
||||
|
bindDateChange(e) { |
||||
|
const value = e.detail.value + '-1' |
||||
|
console.log(this.cale.getDate(value)); |
||||
|
this.init(value) |
||||
|
}, |
||||
|
/** |
||||
|
* 初始化日期显示 |
||||
|
* @param {Object} date |
||||
|
*/ |
||||
|
init(date) { |
||||
|
this.cale.setDate(date) |
||||
|
this.weeks = this.cale.weeks |
||||
|
this.nowDate = this.calendar = this.cale.getInfo(date) |
||||
|
}, |
||||
|
/** |
||||
|
* 打开日历弹窗 |
||||
|
*/ |
||||
|
open() { |
||||
|
// 弹窗模式并且清理数据 |
||||
|
if (this.clearDate && !this.insert) { |
||||
|
this.cale.cleanMultipleStatus() |
||||
|
// this.cale.setDate(this.date) |
||||
|
this.init(this.date) |
||||
|
} |
||||
|
this.show = true |
||||
|
this.$nextTick(() => { |
||||
|
setTimeout(() => { |
||||
|
this.aniMaskShow = true |
||||
|
}, 50) |
||||
|
}) |
||||
|
}, |
||||
|
/** |
||||
|
* 关闭日历弹窗 |
||||
|
*/ |
||||
|
close() { |
||||
|
this.aniMaskShow = false |
||||
|
this.$nextTick(() => { |
||||
|
setTimeout(() => { |
||||
|
this.show = false |
||||
|
this.$emit('close') |
||||
|
}, 300) |
||||
|
}) |
||||
|
}, |
||||
|
/** |
||||
|
* 确认按钮 |
||||
|
*/ |
||||
|
confirm() { |
||||
|
this.setEmit('confirm') |
||||
|
this.close() |
||||
|
}, |
||||
|
/** |
||||
|
* 变化触发 |
||||
|
*/ |
||||
|
change() { |
||||
|
if (!this.insert) return |
||||
|
this.setEmit('change') |
||||
|
}, |
||||
|
/** |
||||
|
* 选择月份触发 |
||||
|
*/ |
||||
|
monthSwitch() { |
||||
|
let { |
||||
|
year, |
||||
|
month |
||||
|
} = this.nowDate |
||||
|
this.$emit('monthSwitch', { |
||||
|
year, |
||||
|
month: Number(month) |
||||
|
}) |
||||
|
}, |
||||
|
/** |
||||
|
* 派发事件 |
||||
|
* @param {Object} name |
||||
|
*/ |
||||
|
setEmit(name) { |
||||
|
let { |
||||
|
year, |
||||
|
month, |
||||
|
date, |
||||
|
fullDate, |
||||
|
lunar, |
||||
|
extraInfo |
||||
|
} = this.calendar |
||||
|
this.$emit(name, { |
||||
|
range: this.cale.multipleStatus, |
||||
|
year, |
||||
|
month, |
||||
|
date, |
||||
|
fulldate: fullDate, |
||||
|
lunar, |
||||
|
extraInfo: extraInfo || {} |
||||
|
}) |
||||
|
}, |
||||
|
/** |
||||
|
* 选择天触发 |
||||
|
* @param {Object} weeks |
||||
|
*/ |
||||
|
choiceDate(weeks) { |
||||
|
if (weeks.disable) return |
||||
|
this.calendar = weeks |
||||
|
// 设置多选 |
||||
|
this.cale.setMultiple(this.calendar.fullDate) |
||||
|
this.weeks = this.cale.weeks |
||||
|
this.change() |
||||
|
}, |
||||
|
/** |
||||
|
* 回到今天 |
||||
|
*/ |
||||
|
backtoday() { |
||||
|
console.log(this.cale.getDate(new Date()).fullDate); |
||||
|
let date = this.cale.getDate(new Date()).fullDate |
||||
|
// this.cale.setDate(date) |
||||
|
this.init(date) |
||||
|
this.change() |
||||
|
}, |
||||
|
/** |
||||
|
* 上个月 |
||||
|
*/ |
||||
|
pre() { |
||||
|
const preDate = this.cale.getDate(this.nowDate.fullDate, -1, 'month').fullDate |
||||
|
this.setDate(preDate) |
||||
|
this.monthSwitch() |
||||
|
|
||||
|
}, |
||||
|
/** |
||||
|
* 下个月 |
||||
|
*/ |
||||
|
next() { |
||||
|
const nextDate = this.cale.getDate(this.nowDate.fullDate, +1, 'month').fullDate |
||||
|
this.setDate(nextDate) |
||||
|
this.monthSwitch() |
||||
|
}, |
||||
|
/** |
||||
|
* 设置日期 |
||||
|
* @param {Object} date |
||||
|
*/ |
||||
|
setDate(date) { |
||||
|
this.cale.setDate(date) |
||||
|
this.weeks = this.cale.weeks |
||||
|
this.nowDate = this.cale.getInfo(date) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss" scoped> |
||||
|
.uni-calendar { |
||||
|
/* #ifndef APP-NVUE */ |
||||
|
display: flex; |
||||
|
/* #endif */ |
||||
|
flex-direction: column; |
||||
|
} |
||||
|
|
||||
|
.uni-calendar__mask { |
||||
|
position: fixed; |
||||
|
bottom: 0; |
||||
|
top: 0; |
||||
|
left: 0; |
||||
|
right: 0; |
||||
|
background-color: $uni-bg-color-mask; |
||||
|
transition-property: opacity; |
||||
|
transition-duration: 0.3s; |
||||
|
opacity: 0; |
||||
|
/* #ifndef APP-NVUE */ |
||||
|
z-index: 99; |
||||
|
/* #endif */ |
||||
|
} |
||||
|
|
||||
|
.uni-calendar--mask-show { |
||||
|
opacity: 1 |
||||
|
} |
||||
|
|
||||
|
.uni-calendar--fixed { |
||||
|
position: fixed; |
||||
|
bottom: calc(var(--window-bottom)); |
||||
|
left: 0; |
||||
|
right: 0; |
||||
|
transition-property: transform; |
||||
|
transition-duration: 0.3s; |
||||
|
transform: translateY(460px); |
||||
|
/* #ifndef APP-NVUE */ |
||||
|
z-index: 99; |
||||
|
/* #endif */ |
||||
|
} |
||||
|
|
||||
|
.uni-calendar--ani-show { |
||||
|
transform: translateY(0); |
||||
|
} |
||||
|
|
||||
|
.uni-calendar__content { |
||||
|
background-color: #fff; |
||||
|
} |
||||
|
|
||||
|
.uni-calendar__header { |
||||
|
position: relative; |
||||
|
/* #ifndef APP-NVUE */ |
||||
|
display: flex; |
||||
|
/* #endif */ |
||||
|
flex-direction: row; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
height: 50px; |
||||
|
border-bottom-color: $uni-border-color; |
||||
|
border-bottom-style: solid; |
||||
|
border-bottom-width: 1px; |
||||
|
} |
||||
|
|
||||
|
.uni-calendar--fixed-top { |
||||
|
/* #ifndef APP-NVUE */ |
||||
|
display: flex; |
||||
|
/* #endif */ |
||||
|
flex-direction: row; |
||||
|
justify-content: space-between; |
||||
|
border-top-color: $uni-border-color; |
||||
|
border-top-style: solid; |
||||
|
border-top-width: 1px; |
||||
|
} |
||||
|
|
||||
|
.uni-calendar--fixed-width { |
||||
|
width: 50px; |
||||
|
// padding: 0 15px; |
||||
|
} |
||||
|
|
||||
|
.uni-calendar__backtoday { |
||||
|
position: absolute; |
||||
|
right: 0; |
||||
|
top: 25rpx; |
||||
|
padding: 0 5px; |
||||
|
padding-left: 10px; |
||||
|
height: 25px; |
||||
|
line-height: 25px; |
||||
|
font-size: 12px; |
||||
|
border-top-left-radius: 25px; |
||||
|
border-bottom-left-radius: 25px; |
||||
|
color: $uni-text-color; |
||||
|
background-color: $uni-bg-color-hover; |
||||
|
} |
||||
|
|
||||
|
.uni-calendar__header-text { |
||||
|
text-align: center; |
||||
|
width: 100px; |
||||
|
font-size: $uni-font-size-base; |
||||
|
color: $uni-text-color; |
||||
|
} |
||||
|
|
||||
|
.uni-calendar__header-btn-box { |
||||
|
/* #ifndef APP-NVUE */ |
||||
|
display: flex; |
||||
|
/* #endif */ |
||||
|
flex-direction: row; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
width: 50px; |
||||
|
height: 50px; |
||||
|
} |
||||
|
|
||||
|
.uni-calendar__header-btn { |
||||
|
width: 10px; |
||||
|
height: 10px; |
||||
|
border-left-color: $uni-text-color-placeholder; |
||||
|
border-left-style: solid; |
||||
|
border-left-width: 2px; |
||||
|
border-top-color: $uni-color-subtitle; |
||||
|
border-top-style: solid; |
||||
|
border-top-width: 2px; |
||||
|
} |
||||
|
|
||||
|
.uni-calendar--left { |
||||
|
transform: rotate(-45deg); |
||||
|
} |
||||
|
|
||||
|
.uni-calendar--right { |
||||
|
transform: rotate(135deg); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
.uni-calendar__weeks { |
||||
|
position: relative; |
||||
|
/* #ifndef APP-NVUE */ |
||||
|
display: flex; |
||||
|
/* #endif */ |
||||
|
flex-direction: row; |
||||
|
} |
||||
|
|
||||
|
.uni-calendar__weeks-item { |
||||
|
flex: 1; |
||||
|
} |
||||
|
|
||||
|
.uni-calendar__weeks-day { |
||||
|
flex: 1; |
||||
|
/* #ifndef APP-NVUE */ |
||||
|
display: flex; |
||||
|
/* #endif */ |
||||
|
flex-direction: column; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
height: 45px; |
||||
|
border-bottom-color: #F5F5F5; |
||||
|
border-bottom-style: solid; |
||||
|
border-bottom-width: 1px; |
||||
|
} |
||||
|
|
||||
|
.uni-calendar__weeks-day-text { |
||||
|
font-size: 14px; |
||||
|
} |
||||
|
|
||||
|
.uni-calendar__box { |
||||
|
position: relative; |
||||
|
} |
||||
|
|
||||
|
.uni-calendar__box-bg { |
||||
|
/* #ifndef APP-NVUE */ |
||||
|
display: flex; |
||||
|
/* #endif */ |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
position: absolute; |
||||
|
top: 0; |
||||
|
left: 0; |
||||
|
right: 0; |
||||
|
bottom: 0; |
||||
|
} |
||||
|
|
||||
|
.uni-calendar__box-bg-text { |
||||
|
font-size: 200px; |
||||
|
font-weight: bold; |
||||
|
color: $uni-text-color-grey; |
||||
|
opacity: 0.1; |
||||
|
text-align: center; |
||||
|
/* #ifndef APP-NVUE */ |
||||
|
line-height: 1; |
||||
|
/* #endif */ |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,354 @@ |
|||||
|
import CALENDAR from './calendar.js' |
||||
|
|
||||
|
class Calendar { |
||||
|
constructor({ |
||||
|
date, |
||||
|
selected, |
||||
|
startDate, |
||||
|
endDate, |
||||
|
range |
||||
|
} = {}) { |
||||
|
// 当前日期
|
||||
|
this.date = this.getDate(new Date()) // 当前初入日期
|
||||
|
// 打点信息
|
||||
|
this.selected = selected || []; |
||||
|
// 范围开始
|
||||
|
this.startDate = startDate |
||||
|
// 范围结束
|
||||
|
this.endDate = endDate |
||||
|
this.range = range |
||||
|
// 多选状态
|
||||
|
this.cleanMultipleStatus() |
||||
|
// 每周日期
|
||||
|
this.weeks = {} |
||||
|
// this._getWeek(this.date.fullDate)
|
||||
|
} |
||||
|
/** |
||||
|
* 设置日期 |
||||
|
* @param {Object} date |
||||
|
*/ |
||||
|
setDate(date) { |
||||
|
this.selectDate = this.getDate(date) |
||||
|
this._getWeek(this.selectDate.fullDate) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 清理多选状态 |
||||
|
*/ |
||||
|
cleanMultipleStatus() { |
||||
|
this.multipleStatus = { |
||||
|
before: '', |
||||
|
after: '', |
||||
|
data: [] |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 重置开始日期 |
||||
|
*/ |
||||
|
resetSatrtDate(startDate) { |
||||
|
// 范围开始
|
||||
|
this.startDate = startDate |
||||
|
|
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 重置结束日期 |
||||
|
*/ |
||||
|
resetEndDate(endDate) { |
||||
|
// 范围结束
|
||||
|
this.endDate = endDate |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取任意时间 |
||||
|
*/ |
||||
|
getDate(date, AddDayCount = 0, str = 'day') { |
||||
|
if (!date) { |
||||
|
date = new Date() |
||||
|
} |
||||
|
if (typeof date !== 'object') { |
||||
|
date = date.replace(/-/g, '/') |
||||
|
} |
||||
|
const dd = new Date(date) |
||||
|
switch (str) { |
||||
|
case 'day': |
||||
|
dd.setDate(dd.getDate() + AddDayCount) // 获取AddDayCount天后的日期
|
||||
|
break |
||||
|
case 'month': |
||||
|
if (dd.getDate() === 31) { |
||||
|
dd.setDate(dd.getDate() + AddDayCount) |
||||
|
} else { |
||||
|
dd.setMonth(dd.getMonth() + AddDayCount) // 获取AddDayCount天后的日期
|
||||
|
} |
||||
|
break |
||||
|
case 'year': |
||||
|
dd.setFullYear(dd.getFullYear() + AddDayCount) // 获取AddDayCount天后的日期
|
||||
|
break |
||||
|
} |
||||
|
const y = dd.getFullYear() |
||||
|
const m = dd.getMonth() + 1 < 10 ? '0' + (dd.getMonth() + 1) : dd.getMonth() + 1 // 获取当前月份的日期,不足10补0
|
||||
|
const d = dd.getDate() < 10 ? '0' + dd.getDate() : dd.getDate() // 获取当前几号,不足10补0
|
||||
|
return { |
||||
|
fullDate: y + '-' + m + '-' + d, |
||||
|
year: y, |
||||
|
month: m, |
||||
|
date: d, |
||||
|
day: dd.getDay() |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 获取上月剩余天数 |
||||
|
*/ |
||||
|
_getLastMonthDays(firstDay, full) { |
||||
|
let dateArr = [] |
||||
|
for (let i = firstDay; i > 0; i--) { |
||||
|
const beforeDate = new Date(full.year, full.month - 1, -i + 1).getDate() |
||||
|
dateArr.push({ |
||||
|
date: beforeDate, |
||||
|
month: full.month - 1, |
||||
|
lunar: this.getlunar(full.year, full.month - 1, beforeDate), |
||||
|
disable: true |
||||
|
}) |
||||
|
} |
||||
|
return dateArr |
||||
|
} |
||||
|
/** |
||||
|
* 获取本月天数 |
||||
|
*/ |
||||
|
_currentMonthDys(dateData, full) { |
||||
|
let dateArr = [] |
||||
|
let fullDate = this.date.fullDate |
||||
|
for (let i = 1; i <= dateData; i++) { |
||||
|
let isinfo = false |
||||
|
let nowDate = full.year + '-' + (full.month < 10 ? |
||||
|
full.month : full.month) + '-' + (i < 10 ? |
||||
|
'0' + i : i) |
||||
|
// 是否今天
|
||||
|
let isDay = fullDate === nowDate |
||||
|
// 获取打点信息
|
||||
|
let info = this.selected && this.selected.find((item) => { |
||||
|
if (this.dateEqual(nowDate, item.date)) { |
||||
|
return item |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
// 日期禁用
|
||||
|
let disableBefore = true |
||||
|
let disableAfter = true |
||||
|
if (this.startDate) { |
||||
|
// let dateCompBefore = this.dateCompare(this.startDate, fullDate)
|
||||
|
// disableBefore = this.dateCompare(dateCompBefore ? this.startDate : fullDate, nowDate)
|
||||
|
disableBefore = this.dateCompare(this.startDate, nowDate) |
||||
|
} |
||||
|
|
||||
|
if (this.endDate) { |
||||
|
// let dateCompAfter = this.dateCompare(fullDate, this.endDate)
|
||||
|
// disableAfter = this.dateCompare(nowDate, dateCompAfter ? this.endDate : fullDate)
|
||||
|
disableAfter = this.dateCompare(nowDate, this.endDate) |
||||
|
} |
||||
|
let multiples = this.multipleStatus.data |
||||
|
let checked = false |
||||
|
let multiplesStatus = -1 |
||||
|
if (this.range) { |
||||
|
if (multiples) { |
||||
|
multiplesStatus = multiples.findIndex((item) => { |
||||
|
return this.dateEqual(item, nowDate) |
||||
|
}) |
||||
|
} |
||||
|
if (multiplesStatus !== -1) { |
||||
|
checked = true |
||||
|
} |
||||
|
} |
||||
|
let data = { |
||||
|
fullDate: nowDate, |
||||
|
year: full.year, |
||||
|
date: i, |
||||
|
multiple: this.range ? checked : false, |
||||
|
beforeMultiple: this.dateEqual(this.multipleStatus.before, nowDate), |
||||
|
afterMultiple: this.dateEqual(this.multipleStatus.after, nowDate), |
||||
|
month: full.month, |
||||
|
lunar: this.getlunar(full.year, full.month, i), |
||||
|
disable: !(disableBefore && disableAfter), |
||||
|
isDay |
||||
|
} |
||||
|
if (info) { |
||||
|
data.extraInfo = info |
||||
|
} |
||||
|
|
||||
|
dateArr.push(data) |
||||
|
} |
||||
|
return dateArr |
||||
|
} |
||||
|
/** |
||||
|
* 获取下月天数 |
||||
|
*/ |
||||
|
_getNextMonthDays(surplus, full) { |
||||
|
let dateArr = [] |
||||
|
for (let i = 1; i < surplus + 1; i++) { |
||||
|
dateArr.push({ |
||||
|
date: i, |
||||
|
month: Number(full.month) + 1, |
||||
|
lunar: this.getlunar(full.year, Number(full.month) + 1, i), |
||||
|
disable: true |
||||
|
}) |
||||
|
} |
||||
|
return dateArr |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取当前日期详情 |
||||
|
* @param {Object} date |
||||
|
*/ |
||||
|
getInfo(date) { |
||||
|
if (!date) { |
||||
|
date = new Date() |
||||
|
} |
||||
|
const dateInfo = this.canlender.find(item => item.fullDate === this.getDate(date).fullDate) |
||||
|
return dateInfo |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 比较时间大小 |
||||
|
*/ |
||||
|
dateCompare(startDate, endDate) { |
||||
|
// 计算截止时间
|
||||
|
startDate = new Date(startDate.replace('-', '/').replace('-', '/')) |
||||
|
// 计算详细项的截止时间
|
||||
|
endDate = new Date(endDate.replace('-', '/').replace('-', '/')) |
||||
|
if (startDate <= endDate) { |
||||
|
return true |
||||
|
} else { |
||||
|
return false |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 比较时间是否相等 |
||||
|
*/ |
||||
|
dateEqual(before, after) { |
||||
|
// 计算截止时间
|
||||
|
before = new Date(before.replace('-', '/').replace('-', '/')) |
||||
|
// 计算详细项的截止时间
|
||||
|
after = new Date(after.replace('-', '/').replace('-', '/')) |
||||
|
if (before.getTime() - after.getTime() === 0) { |
||||
|
return true |
||||
|
} else { |
||||
|
return false |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 获取日期范围内所有日期 |
||||
|
* @param {Object} begin |
||||
|
* @param {Object} end |
||||
|
*/ |
||||
|
geDateAll(begin, end) { |
||||
|
var arr = [] |
||||
|
var ab = begin.split('-') |
||||
|
var ae = end.split('-') |
||||
|
var db = new Date() |
||||
|
db.setFullYear(ab[0], ab[1] - 1, ab[2]) |
||||
|
var de = new Date() |
||||
|
de.setFullYear(ae[0], ae[1] - 1, ae[2]) |
||||
|
var unixDb = db.getTime() - 24 * 60 * 60 * 1000 |
||||
|
var unixDe = de.getTime() - 24 * 60 * 60 * 1000 |
||||
|
for (var k = unixDb; k <= unixDe;) { |
||||
|
k = k + 24 * 60 * 60 * 1000 |
||||
|
arr.push(this.getDate(new Date(parseInt(k))).fullDate) |
||||
|
} |
||||
|
return arr |
||||
|
} |
||||
|
/** |
||||
|
* 计算阴历日期显示 |
||||
|
*/ |
||||
|
getlunar(year, month, date) { |
||||
|
return CALENDAR.solar2lunar(year, month, date) |
||||
|
} |
||||
|
/** |
||||
|
* 设置打点 |
||||
|
*/ |
||||
|
setSelectInfo(data, value) { |
||||
|
this.selected = value |
||||
|
this._getWeek(data) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取多选状态 |
||||
|
*/ |
||||
|
setMultiple(fullDate) { |
||||
|
let { |
||||
|
before, |
||||
|
after |
||||
|
} = this.multipleStatus |
||||
|
|
||||
|
if (!this.range) return |
||||
|
if (before && after) { |
||||
|
this.multipleStatus.before = '' |
||||
|
this.multipleStatus.after = '' |
||||
|
this.multipleStatus.data = [] |
||||
|
} else { |
||||
|
if (!before) { |
||||
|
this.multipleStatus.before = fullDate |
||||
|
} else { |
||||
|
this.multipleStatus.after = fullDate |
||||
|
if (this.dateCompare(this.multipleStatus.before, this.multipleStatus.after)) { |
||||
|
this.multipleStatus.data = this.geDateAll(this.multipleStatus.before, this.multipleStatus.after); |
||||
|
} else { |
||||
|
this.multipleStatus.data = this.geDateAll(this.multipleStatus.after, this.multipleStatus.before); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
this._getWeek(fullDate) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取每周数据 |
||||
|
* @param {Object} dateData |
||||
|
*/ |
||||
|
_getWeek(dateData) { |
||||
|
const { |
||||
|
fullDate, |
||||
|
year, |
||||
|
month, |
||||
|
date, |
||||
|
day |
||||
|
} = this.getDate(dateData) |
||||
|
let firstDay = new Date(year, month - 1, 1).getDay() |
||||
|
let currentDay = new Date(year, month, 0).getDate() |
||||
|
let dates = { |
||||
|
lastMonthDays: this._getLastMonthDays(firstDay, this.getDate(dateData)), // 上个月末尾几天
|
||||
|
currentMonthDys: this._currentMonthDys(currentDay, this.getDate(dateData)), // 本月天数
|
||||
|
nextMonthDays: [], // 下个月开始几天
|
||||
|
weeks: [] |
||||
|
} |
||||
|
let canlender = [] |
||||
|
const surplus = 42 - (dates.lastMonthDays.length + dates.currentMonthDys.length) |
||||
|
dates.nextMonthDays = this._getNextMonthDays(surplus, this.getDate(dateData)) |
||||
|
canlender = canlender.concat(dates.lastMonthDays, dates.currentMonthDys, dates.nextMonthDays) |
||||
|
let weeks = {} |
||||
|
// 拼接数组 上个月开始几天 + 本月天数+ 下个月开始几天
|
||||
|
for (let i = 0; i < canlender.length; i++) { |
||||
|
if (i % 7 === 0) { |
||||
|
weeks[parseInt(i / 7)] = new Array(7) |
||||
|
} |
||||
|
weeks[parseInt(i / 7)][i % 7] = canlender[i] |
||||
|
} |
||||
|
this.canlender = canlender |
||||
|
this.weeks = weeks |
||||
|
} |
||||
|
|
||||
|
//静态方法
|
||||
|
// static init(date) {
|
||||
|
// if (!this.instance) {
|
||||
|
// this.instance = new Calendar(date);
|
||||
|
// }
|
||||
|
// return this.instance;
|
||||
|
// }
|
||||
|
} |
||||
|
|
||||
|
|
||||
|
export default Calendar |
||||
@ -0,0 +1,88 @@ |
|||||
|
{ |
||||
|
"id": "uni-calendar", |
||||
|
"displayName": "uni-calendar 日历", |
||||
|
"version": "1.4.3", |
||||
|
"description": "日历组件", |
||||
|
"keywords": [ |
||||
|
"uni-ui", |
||||
|
"uniui", |
||||
|
"日历", |
||||
|
"", |
||||
|
"打卡", |
||||
|
"日历选择" |
||||
|
], |
||||
|
"repository": "https://github.com/dcloudio/uni-ui", |
||||
|
"engines": { |
||||
|
"HBuilderX": "" |
||||
|
}, |
||||
|
"directories": { |
||||
|
"example": "../../temps/example_temps" |
||||
|
}, |
||||
|
"dcloudext": { |
||||
|
"category": [ |
||||
|
"前端组件", |
||||
|
"通用组件" |
||||
|
], |
||||
|
"sale": { |
||||
|
"regular": { |
||||
|
"price": "0.00" |
||||
|
}, |
||||
|
"sourcecode": { |
||||
|
"price": "0.00" |
||||
|
} |
||||
|
}, |
||||
|
"contact": { |
||||
|
"qq": "" |
||||
|
}, |
||||
|
"declaration": { |
||||
|
"ads": "无", |
||||
|
"data": "无", |
||||
|
"permissions": "无" |
||||
|
}, |
||||
|
"npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" |
||||
|
}, |
||||
|
"uni_modules": { |
||||
|
"dependencies": [], |
||||
|
"encrypt": [], |
||||
|
"platforms": { |
||||
|
"cloud": { |
||||
|
"tcb": "y", |
||||
|
"aliyun": "y" |
||||
|
}, |
||||
|
"client": { |
||||
|
"App": { |
||||
|
"app-vue": "y", |
||||
|
"app-nvue": "y" |
||||
|
}, |
||||
|
"H5-mobile": { |
||||
|
"Safari": "y", |
||||
|
"Android Browser": "y", |
||||
|
"微信浏览器(Android)": "y", |
||||
|
"QQ浏览器(Android)": "y" |
||||
|
}, |
||||
|
"H5-pc": { |
||||
|
"Chrome": "y", |
||||
|
"IE": "y", |
||||
|
"Edge": "y", |
||||
|
"Firefox": "y", |
||||
|
"Safari": "y" |
||||
|
}, |
||||
|
"小程序": { |
||||
|
"微信": "y", |
||||
|
"阿里": "y", |
||||
|
"百度": "y", |
||||
|
"字节跳动": "y", |
||||
|
"QQ": "y" |
||||
|
}, |
||||
|
"快应用": { |
||||
|
"华为": "u", |
||||
|
"联盟": "u" |
||||
|
}, |
||||
|
"Vue": { |
||||
|
"vue2": "y", |
||||
|
"vue3": "y" |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,103 @@ |
|||||
|
|
||||
|
|
||||
|
## Calendar 日历 |
||||
|
> **组件名:uni-calendar** |
||||
|
> 代码块: `uCalendar` |
||||
|
|
||||
|
|
||||
|
日历组件 |
||||
|
|
||||
|
> **注意事项** |
||||
|
> 为了避免错误使用,给大家带来不好的开发体验,请在使用组件前仔细阅读下面的注意事项,可以帮你避免一些错误。 |
||||
|
> - 本组件农历转换使用的js是 [@1900-2100区间内的公历、农历互转](https://github.com/jjonline/calendar.js) |
||||
|
> - 仅支持自定义组件模式 |
||||
|
> - `date`属性传入的应该是一个 String ,如: 2019-06-27 ,而不是 new Date() |
||||
|
> - 通过 `insert` 属性来确定当前的事件是 @change 还是 @confirm 。理应合并为一个事件,但是为了区分模式,现使用两个事件,这里需要注意 |
||||
|
> - 弹窗模式下无法阻止后面的元素滚动,如有需要阻止,请在弹窗弹出后,手动设置滚动元素为不可滚动 |
||||
|
|
||||
|
|
||||
|
### 安装方式 |
||||
|
|
||||
|
本组件符合[easycom](https://uniapp.dcloud.io/collocation/pages?id=easycom)规范,`HBuilderX 2.5.5`起,只需将本组件导入项目,在页面`template`中即可直接使用,无需在页面中`import`和注册`components`。 |
||||
|
|
||||
|
如需通过`npm`方式使用`uni-ui`组件,另见文档:[https://ext.dcloud.net.cn/plugin?id=55](https://ext.dcloud.net.cn/plugin?id=55) |
||||
|
|
||||
|
### 基本用法 |
||||
|
|
||||
|
在 ``template`` 中使用组件 |
||||
|
|
||||
|
```html |
||||
|
<view> |
||||
|
<uni-calendar |
||||
|
:insert="true" |
||||
|
:lunar="true" |
||||
|
:start-date="'2019-3-2'" |
||||
|
:end-date="'2019-5-20'" |
||||
|
@change="change" |
||||
|
/> |
||||
|
</view> |
||||
|
``` |
||||
|
|
||||
|
### 通过方法打开日历 |
||||
|
|
||||
|
需要设置 `insert` 为 `false` |
||||
|
|
||||
|
```html |
||||
|
<view> |
||||
|
<uni-calendar |
||||
|
ref="calendar" |
||||
|
:insert="false" |
||||
|
@confirm="confirm" |
||||
|
/> |
||||
|
<button @click="open">打开日历</button> |
||||
|
</view> |
||||
|
``` |
||||
|
|
||||
|
```javascript |
||||
|
|
||||
|
export default { |
||||
|
data() { |
||||
|
return {}; |
||||
|
}, |
||||
|
methods: { |
||||
|
open(){ |
||||
|
this.$refs.calendar.open(); |
||||
|
}, |
||||
|
confirm(e) { |
||||
|
console.log(e); |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
``` |
||||
|
|
||||
|
|
||||
|
## API |
||||
|
|
||||
|
### Calendar Props |
||||
|
|
||||
|
| 属性名 | 类型 | 默认值| 说明 | |
||||
|
| | | |
||||
|
| date | String |- | 自定义当前时间,默认为今天 | |
||||
|
| lunar | Boolean | false | 显示农历 | |
||||
|
| startDate | String |- | 日期选择范围-开始日期 | |
||||
|
| endDate | String |- | 日期选择范围-结束日期 | |
||||
|
| range | Boolean | false | 范围选择 | |
||||
|
| insert | Boolean | false | 插入模式,可选值,ture:插入模式;false:弹窗模式;默认为插入模式 | |
||||
|
|clearDate |Boolean |true |弹窗模式是否清空上次选择内容 | |
||||
|
| selected | Array |- | 打点,期待格式[{date: '2019-06-27', info: '签到', data: { custom: '自定义信息', name: '自定义消息头',xxx:xxx... }}] | |
||||
|
|showMonth | Boolean | true | 是否显示月份为背景 | |
||||
|
|
||||
|
### Calendar Events |
||||
|
|
||||
|
| 事件名 | 说明 |返回值| |
||||
|
| | | | |
||||
|
| open | 弹出日历组件,`insert :false` 时生效|- | |
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
## 组件示例 |
||||
|
|
||||
|
点击查看:[https://hellouniapp.dcloud.net.cn/pages/extUI/calendar/calendar](https://hellouniapp.dcloud.net.cn/pages/extUI/calendar/calendar) |
||||
@ -0,0 +1,16 @@ |
|||||
|
## 1.3.2(2021-12-01) |
||||
|
- 优化 示例可复制图标名称 |
||||
|
## 1.3.1(2021-11-23) |
||||
|
- 优化 兼容旧组件 type 值 |
||||
|
## 1.3.0(2021-11-19) |
||||
|
- 新增 更多图标 |
||||
|
- 优化 自定义图标使用方式 |
||||
|
- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) |
||||
|
- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-icons](https://uniapp.dcloud.io/component/uniui/uni-icons) |
||||
|
## 1.1.7(2021-11-08) |
||||
|
## 1.2.0(2021-07-30) |
||||
|
- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) |
||||
|
## 1.1.5(2021-05-12) |
||||
|
- 新增 组件示例地址 |
||||
|
## 1.1.4(2021-02-05) |
||||
|
- 调整为uni_modules目录规范 |
||||
File diff suppressed because it is too large
@ -0,0 +1,89 @@ |
|||||
|
<template> |
||||
|
<!-- #ifdef APP-NVUE --> |
||||
|
<text :style="{ color: color, 'font-size': size + 'px' }" class="uni-icons" @click="_onClick">{{unicode}}</text> |
||||
|
<!-- #endif --> |
||||
|
<!-- #ifndef APP-NVUE --> |
||||
|
<text :style="{ color: color, 'font-size': size + 'px' }" class="uni-icons" :class="['uniui-'+type,customPrefix,customPrefix?type:'']" @click="_onClick"></text> |
||||
|
<!-- #endif --> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import icons from './icons.js'; |
||||
|
// #ifdef APP-NVUE |
||||
|
var domModule = weex.requireModule('dom'); |
||||
|
import iconUrl from './uniicons.ttf' |
||||
|
domModule.addRule('fontFace', { |
||||
|
'fontFamily': "uniicons", |
||||
|
'src': "url('"+iconUrl+"')" |
||||
|
}); |
||||
|
// #endif |
||||
|
|
||||
|
/** |
||||
|
* Icons 图标 |
||||
|
* @description 用于展示 icons 图标 |
||||
|
* @tutorial https://ext.dcloud.net.cn/plugin?id=28 |
||||
|
* @property {Number} size 图标大小 |
||||
|
* @property {String} type 图标图案,参考示例 |
||||
|
* @property {String} color 图标颜色 |
||||
|
* @property {String} customPrefix 自定义图标 |
||||
|
* @event {Function} click 点击 Icon 触发事件 |
||||
|
*/ |
||||
|
export default { |
||||
|
name: 'UniIcons', |
||||
|
emits:['click'], |
||||
|
props: { |
||||
|
type: { |
||||
|
type: String, |
||||
|
default: '' |
||||
|
}, |
||||
|
color: { |
||||
|
type: String, |
||||
|
default: '#333333' |
||||
|
}, |
||||
|
size: { |
||||
|
type: [Number, String], |
||||
|
default: 16 |
||||
|
}, |
||||
|
customPrefix:{ |
||||
|
type: String, |
||||
|
default: '' |
||||
|
} |
||||
|
}, |
||||
|
data() { |
||||
|
return { |
||||
|
icons: icons.glyphs |
||||
|
} |
||||
|
}, |
||||
|
computed:{ |
||||
|
unicode(){ |
||||
|
let code = this.icons.find(v=>v.font_class === this.type) |
||||
|
if(code){ |
||||
|
return unescape(`%u${code.unicode}`) |
||||
|
} |
||||
|
return '' |
||||
|
} |
||||
|
}, |
||||
|
methods: { |
||||
|
_onClick() { |
||||
|
this.$emit('click') |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss"> |
||||
|
/* #ifndef APP-NVUE */ |
||||
|
@import './uniicons.css'; |
||||
|
@font-face { |
||||
|
font-family: uniicons; |
||||
|
src: url('./uniicons.ttf') format('truetype'); |
||||
|
} |
||||
|
|
||||
|
/* #endif */ |
||||
|
.uni-icons { |
||||
|
font-family: uniicons; |
||||
|
text-decoration: none; |
||||
|
text-align: center; |
||||
|
} |
||||
|
|
||||
|
</style> |
||||
@ -0,0 +1,663 @@ |
|||||
|
.uniui-color:before { |
||||
|
content: "\e6cf"; |
||||
|
} |
||||
|
|
||||
|
.uniui-wallet:before { |
||||
|
content: "\e6b1"; |
||||
|
} |
||||
|
|
||||
|
.uniui-settings-filled:before { |
||||
|
content: "\e6ce"; |
||||
|
} |
||||
|
|
||||
|
.uniui-auth-filled:before { |
||||
|
content: "\e6cc"; |
||||
|
} |
||||
|
|
||||
|
.uniui-shop-filled:before { |
||||
|
content: "\e6cd"; |
||||
|
} |
||||
|
|
||||
|
.uniui-staff-filled:before { |
||||
|
content: "\e6cb"; |
||||
|
} |
||||
|
|
||||
|
.uniui-vip-filled:before { |
||||
|
content: "\e6c6"; |
||||
|
} |
||||
|
|
||||
|
.uniui-plus-filled:before { |
||||
|
content: "\e6c7"; |
||||
|
} |
||||
|
|
||||
|
.uniui-folder-add-filled:before { |
||||
|
content: "\e6c8"; |
||||
|
} |
||||
|
|
||||
|
.uniui-color-filled:before { |
||||
|
content: "\e6c9"; |
||||
|
} |
||||
|
|
||||
|
.uniui-tune-filled:before { |
||||
|
content: "\e6ca"; |
||||
|
} |
||||
|
|
||||
|
.uniui-calendar-filled:before { |
||||
|
content: "\e6c0"; |
||||
|
} |
||||
|
|
||||
|
.uniui-notification-filled:before { |
||||
|
content: "\e6c1"; |
||||
|
} |
||||
|
|
||||
|
.uniui-wallet-filled:before { |
||||
|
content: "\e6c2"; |
||||
|
} |
||||
|
|
||||
|
.uniui-medal-filled:before { |
||||
|
content: "\e6c3"; |
||||
|
} |
||||
|
|
||||
|
.uniui-gift-filled:before { |
||||
|
content: "\e6c4"; |
||||
|
} |
||||
|
|
||||
|
.uniui-fire-filled:before { |
||||
|
content: "\e6c5"; |
||||
|
} |
||||
|
|
||||
|
.uniui-refreshempty:before { |
||||
|
content: "\e6bf"; |
||||
|
} |
||||
|
|
||||
|
.uniui-location-filled:before { |
||||
|
content: "\e6af"; |
||||
|
} |
||||
|
|
||||
|
.uniui-person-filled:before { |
||||
|
content: "\e69d"; |
||||
|
} |
||||
|
|
||||
|
.uniui-personadd-filled:before { |
||||
|
content: "\e698"; |
||||
|
} |
||||
|
|
||||
|
.uniui-back:before { |
||||
|
content: "\e6b9"; |
||||
|
} |
||||
|
|
||||
|
.uniui-forward:before { |
||||
|
content: "\e6ba"; |
||||
|
} |
||||
|
|
||||
|
.uniui-arrow-right:before { |
||||
|
content: "\e6bb"; |
||||
|
} |
||||
|
|
||||
|
.uniui-arrowthinright:before { |
||||
|
content: "\e6bb"; |
||||
|
} |
||||
|
|
||||
|
.uniui-arrow-left:before { |
||||
|
content: "\e6bc"; |
||||
|
} |
||||
|
|
||||
|
.uniui-arrowthinleft:before { |
||||
|
content: "\e6bc"; |
||||
|
} |
||||
|
|
||||
|
.uniui-arrow-up:before { |
||||
|
content: "\e6bd"; |
||||
|
} |
||||
|
|
||||
|
.uniui-arrowthinup:before { |
||||
|
content: "\e6bd"; |
||||
|
} |
||||
|
|
||||
|
.uniui-arrow-down:before { |
||||
|
content: "\e6be"; |
||||
|
} |
||||
|
|
||||
|
.uniui-arrowthindown:before { |
||||
|
content: "\e6be"; |
||||
|
} |
||||
|
|
||||
|
.uniui-bottom:before { |
||||
|
content: "\e6b8"; |
||||
|
} |
||||
|
|
||||
|
.uniui-arrowdown:before { |
||||
|
content: "\e6b8"; |
||||
|
} |
||||
|
|
||||
|
.uniui-right:before { |
||||
|
content: "\e6b5"; |
||||
|
} |
||||
|
|
||||
|
.uniui-arrowright:before { |
||||
|
content: "\e6b5"; |
||||
|
} |
||||
|
|
||||
|
.uniui-top:before { |
||||
|
content: "\e6b6"; |
||||
|
} |
||||
|
|
||||
|
.uniui-arrowup:before { |
||||
|
content: "\e6b6"; |
||||
|
} |
||||
|
|
||||
|
.uniui-left:before { |
||||
|
content: "\e6b7"; |
||||
|
} |
||||
|
|
||||
|
.uniui-arrowleft:before { |
||||
|
content: "\e6b7"; |
||||
|
} |
||||
|
|
||||
|
.uniui-eye:before { |
||||
|
content: "\e651"; |
||||
|
} |
||||
|
|
||||
|
.uniui-eye-filled:before { |
||||
|
content: "\e66a"; |
||||
|
} |
||||
|
|
||||
|
.uniui-eye-slash:before { |
||||
|
content: "\e6b3"; |
||||
|
} |
||||
|
|
||||
|
.uniui-eye-slash-filled:before { |
||||
|
content: "\e6b4"; |
||||
|
} |
||||
|
|
||||
|
.uniui-info-filled:before { |
||||
|
content: "\e649"; |
||||
|
} |
||||
|
|
||||
|
.uniui-reload:before { |
||||
|
content: "\e6b2"; |
||||
|
} |
||||
|
|
||||
|
.uniui-micoff-filled:before { |
||||
|
content: "\e6b0"; |
||||
|
} |
||||
|
|
||||
|
.uniui-map-pin-ellipse:before { |
||||
|
content: "\e6ac"; |
||||
|
} |
||||
|
|
||||
|
.uniui-map-pin:before { |
||||
|
content: "\e6ad"; |
||||
|
} |
||||
|
|
||||
|
.uniui-location:before { |
||||
|
content: "\e6ae"; |
||||
|
} |
||||
|
|
||||
|
.uniui-starhalf:before { |
||||
|
content: "\e683"; |
||||
|
} |
||||
|
|
||||
|
.uniui-star:before { |
||||
|
content: "\e688"; |
||||
|
} |
||||
|
|
||||
|
.uniui-star-filled:before { |
||||
|
content: "\e68f"; |
||||
|
} |
||||
|
|
||||
|
.uniui-calendar:before { |
||||
|
content: "\e6a0"; |
||||
|
} |
||||
|
|
||||
|
.uniui-fire:before { |
||||
|
content: "\e6a1"; |
||||
|
} |
||||
|
|
||||
|
.uniui-medal:before { |
||||
|
content: "\e6a2"; |
||||
|
} |
||||
|
|
||||
|
.uniui-font:before { |
||||
|
content: "\e6a3"; |
||||
|
} |
||||
|
|
||||
|
.uniui-gift:before { |
||||
|
content: "\e6a4"; |
||||
|
} |
||||
|
|
||||
|
.uniui-link:before { |
||||
|
content: "\e6a5"; |
||||
|
} |
||||
|
|
||||
|
.uniui-notification:before { |
||||
|
content: "\e6a6"; |
||||
|
} |
||||
|
|
||||
|
.uniui-staff:before { |
||||
|
content: "\e6a7"; |
||||
|
} |
||||
|
|
||||
|
.uniui-vip:before { |
||||
|
content: "\e6a8"; |
||||
|
} |
||||
|
|
||||
|
.uniui-folder-add:before { |
||||
|
content: "\e6a9"; |
||||
|
} |
||||
|
|
||||
|
.uniui-tune:before { |
||||
|
content: "\e6aa"; |
||||
|
} |
||||
|
|
||||
|
.uniui-auth:before { |
||||
|
content: "\e6ab"; |
||||
|
} |
||||
|
|
||||
|
.uniui-person:before { |
||||
|
content: "\e699"; |
||||
|
} |
||||
|
|
||||
|
.uniui-email-filled:before { |
||||
|
content: "\e69a"; |
||||
|
} |
||||
|
|
||||
|
.uniui-phone-filled:before { |
||||
|
content: "\e69b"; |
||||
|
} |
||||
|
|
||||
|
.uniui-phone:before { |
||||
|
content: "\e69c"; |
||||
|
} |
||||
|
|
||||
|
.uniui-email:before { |
||||
|
content: "\e69e"; |
||||
|
} |
||||
|
|
||||
|
.uniui-personadd:before { |
||||
|
content: "\e69f"; |
||||
|
} |
||||
|
|
||||
|
.uniui-chatboxes-filled:before { |
||||
|
content: "\e692"; |
||||
|
} |
||||
|
|
||||
|
.uniui-contact:before { |
||||
|
content: "\e693"; |
||||
|
} |
||||
|
|
||||
|
.uniui-chatbubble-filled:before { |
||||
|
content: "\e694"; |
||||
|
} |
||||
|
|
||||
|
.uniui-contact-filled:before { |
||||
|
content: "\e695"; |
||||
|
} |
||||
|
|
||||
|
.uniui-chatboxes:before { |
||||
|
content: "\e696"; |
||||
|
} |
||||
|
|
||||
|
.uniui-chatbubble:before { |
||||
|
content: "\e697"; |
||||
|
} |
||||
|
|
||||
|
.uniui-upload-filled:before { |
||||
|
content: "\e68e"; |
||||
|
} |
||||
|
|
||||
|
.uniui-upload:before { |
||||
|
content: "\e690"; |
||||
|
} |
||||
|
|
||||
|
.uniui-weixin:before { |
||||
|
content: "\e691"; |
||||
|
} |
||||
|
|
||||
|
.uniui-compose:before { |
||||
|
content: "\e67f"; |
||||
|
} |
||||
|
|
||||
|
.uniui-qq:before { |
||||
|
content: "\e680"; |
||||
|
} |
||||
|
|
||||
|
.uniui-download-filled:before { |
||||
|
content: "\e681"; |
||||
|
} |
||||
|
|
||||
|
.uniui-pyq:before { |
||||
|
content: "\e682"; |
||||
|
} |
||||
|
|
||||
|
.uniui-sound:before { |
||||
|
content: "\e684"; |
||||
|
} |
||||
|
|
||||
|
.uniui-trash-filled:before { |
||||
|
content: "\e685"; |
||||
|
} |
||||
|
|
||||
|
.uniui-sound-filled:before { |
||||
|
content: "\e686"; |
||||
|
} |
||||
|
|
||||
|
.uniui-trash:before { |
||||
|
content: "\e687"; |
||||
|
} |
||||
|
|
||||
|
.uniui-videocam-filled:before { |
||||
|
content: "\e689"; |
||||
|
} |
||||
|
|
||||
|
.uniui-spinner-cycle:before { |
||||
|
content: "\e68a"; |
||||
|
} |
||||
|
|
||||
|
.uniui-weibo:before { |
||||
|
content: "\e68b"; |
||||
|
} |
||||
|
|
||||
|
.uniui-videocam:before { |
||||
|
content: "\e68c"; |
||||
|
} |
||||
|
|
||||
|
.uniui-download:before { |
||||
|
content: "\e68d"; |
||||
|
} |
||||
|
|
||||
|
.uniui-help:before { |
||||
|
content: "\e679"; |
||||
|
} |
||||
|
|
||||
|
.uniui-navigate-filled:before { |
||||
|
content: "\e67a"; |
||||
|
} |
||||
|
|
||||
|
.uniui-plusempty:before { |
||||
|
content: "\e67b"; |
||||
|
} |
||||
|
|
||||
|
.uniui-smallcircle:before { |
||||
|
content: "\e67c"; |
||||
|
} |
||||
|
|
||||
|
.uniui-minus-filled:before { |
||||
|
content: "\e67d"; |
||||
|
} |
||||
|
|
||||
|
.uniui-micoff:before { |
||||
|
content: "\e67e"; |
||||
|
} |
||||
|
|
||||
|
.uniui-closeempty:before { |
||||
|
content: "\e66c"; |
||||
|
} |
||||
|
|
||||
|
.uniui-clear:before { |
||||
|
content: "\e66d"; |
||||
|
} |
||||
|
|
||||
|
.uniui-navigate:before { |
||||
|
content: "\e66e"; |
||||
|
} |
||||
|
|
||||
|
.uniui-minus:before { |
||||
|
content: "\e66f"; |
||||
|
} |
||||
|
|
||||
|
.uniui-image:before { |
||||
|
content: "\e670"; |
||||
|
} |
||||
|
|
||||
|
.uniui-mic:before { |
||||
|
content: "\e671"; |
||||
|
} |
||||
|
|
||||
|
.uniui-paperplane:before { |
||||
|
content: "\e672"; |
||||
|
} |
||||
|
|
||||
|
.uniui-close:before { |
||||
|
content: "\e673"; |
||||
|
} |
||||
|
|
||||
|
.uniui-help-filled:before { |
||||
|
content: "\e674"; |
||||
|
} |
||||
|
|
||||
|
.uniui-paperplane-filled:before { |
||||
|
content: "\e675"; |
||||
|
} |
||||
|
|
||||
|
.uniui-plus:before { |
||||
|
content: "\e676"; |
||||
|
} |
||||
|
|
||||
|
.uniui-mic-filled:before { |
||||
|
content: "\e677"; |
||||
|
} |
||||
|
|
||||
|
.uniui-image-filled:before { |
||||
|
content: "\e678"; |
||||
|
} |
||||
|
|
||||
|
.uniui-locked-filled:before { |
||||
|
content: "\e668"; |
||||
|
} |
||||
|
|
||||
|
.uniui-info:before { |
||||
|
content: "\e669"; |
||||
|
} |
||||
|
|
||||
|
.uniui-locked:before { |
||||
|
content: "\e66b"; |
||||
|
} |
||||
|
|
||||
|
.uniui-camera-filled:before { |
||||
|
content: "\e658"; |
||||
|
} |
||||
|
|
||||
|
.uniui-chat-filled:before { |
||||
|
content: "\e659"; |
||||
|
} |
||||
|
|
||||
|
.uniui-camera:before { |
||||
|
content: "\e65a"; |
||||
|
} |
||||
|
|
||||
|
.uniui-circle:before { |
||||
|
content: "\e65b"; |
||||
|
} |
||||
|
|
||||
|
.uniui-checkmarkempty:before { |
||||
|
content: "\e65c"; |
||||
|
} |
||||
|
|
||||
|
.uniui-chat:before { |
||||
|
content: "\e65d"; |
||||
|
} |
||||
|
|
||||
|
.uniui-circle-filled:before { |
||||
|
content: "\e65e"; |
||||
|
} |
||||
|
|
||||
|
.uniui-flag:before { |
||||
|
content: "\e65f"; |
||||
|
} |
||||
|
|
||||
|
.uniui-flag-filled:before { |
||||
|
content: "\e660"; |
||||
|
} |
||||
|
|
||||
|
.uniui-gear-filled:before { |
||||
|
content: "\e661"; |
||||
|
} |
||||
|
|
||||
|
.uniui-home:before { |
||||
|
content: "\e662"; |
||||
|
} |
||||
|
|
||||
|
.uniui-home-filled:before { |
||||
|
content: "\e663"; |
||||
|
} |
||||
|
|
||||
|
.uniui-gear:before { |
||||
|
content: "\e664"; |
||||
|
} |
||||
|
|
||||
|
.uniui-smallcircle-filled:before { |
||||
|
content: "\e665"; |
||||
|
} |
||||
|
|
||||
|
.uniui-map-filled:before { |
||||
|
content: "\e666"; |
||||
|
} |
||||
|
|
||||
|
.uniui-map:before { |
||||
|
content: "\e667"; |
||||
|
} |
||||
|
|
||||
|
.uniui-refresh-filled:before { |
||||
|
content: "\e656"; |
||||
|
} |
||||
|
|
||||
|
.uniui-refresh:before { |
||||
|
content: "\e657"; |
||||
|
} |
||||
|
|
||||
|
.uniui-cloud-upload:before { |
||||
|
content: "\e645"; |
||||
|
} |
||||
|
|
||||
|
.uniui-cloud-download-filled:before { |
||||
|
content: "\e646"; |
||||
|
} |
||||
|
|
||||
|
.uniui-cloud-download:before { |
||||
|
content: "\e647"; |
||||
|
} |
||||
|
|
||||
|
.uniui-cloud-upload-filled:before { |
||||
|
content: "\e648"; |
||||
|
} |
||||
|
|
||||
|
.uniui-redo:before { |
||||
|
content: "\e64a"; |
||||
|
} |
||||
|
|
||||
|
.uniui-images-filled:before { |
||||
|
content: "\e64b"; |
||||
|
} |
||||
|
|
||||
|
.uniui-undo-filled:before { |
||||
|
content: "\e64c"; |
||||
|
} |
||||
|
|
||||
|
.uniui-more:before { |
||||
|
content: "\e64d"; |
||||
|
} |
||||
|
|
||||
|
.uniui-more-filled:before { |
||||
|
content: "\e64e"; |
||||
|
} |
||||
|
|
||||
|
.uniui-undo:before { |
||||
|
content: "\e64f"; |
||||
|
} |
||||
|
|
||||
|
.uniui-images:before { |
||||
|
content: "\e650"; |
||||
|
} |
||||
|
|
||||
|
.uniui-paperclip:before { |
||||
|
content: "\e652"; |
||||
|
} |
||||
|
|
||||
|
.uniui-settings:before { |
||||
|
content: "\e653"; |
||||
|
} |
||||
|
|
||||
|
.uniui-search:before { |
||||
|
content: "\e654"; |
||||
|
} |
||||
|
|
||||
|
.uniui-redo-filled:before { |
||||
|
content: "\e655"; |
||||
|
} |
||||
|
|
||||
|
.uniui-list:before { |
||||
|
content: "\e644"; |
||||
|
} |
||||
|
|
||||
|
.uniui-mail-open-filled:before { |
||||
|
content: "\e63a"; |
||||
|
} |
||||
|
|
||||
|
.uniui-hand-down-filled:before { |
||||
|
content: "\e63c"; |
||||
|
} |
||||
|
|
||||
|
.uniui-hand-down:before { |
||||
|
content: "\e63d"; |
||||
|
} |
||||
|
|
||||
|
.uniui-hand-up-filled:before { |
||||
|
content: "\e63e"; |
||||
|
} |
||||
|
|
||||
|
.uniui-hand-up:before { |
||||
|
content: "\e63f"; |
||||
|
} |
||||
|
|
||||
|
.uniui-heart-filled:before { |
||||
|
content: "\e641"; |
||||
|
} |
||||
|
|
||||
|
.uniui-mail-open:before { |
||||
|
content: "\e643"; |
||||
|
} |
||||
|
|
||||
|
.uniui-heart:before { |
||||
|
content: "\e639"; |
||||
|
} |
||||
|
|
||||
|
.uniui-loop:before { |
||||
|
content: "\e633"; |
||||
|
} |
||||
|
|
||||
|
.uniui-pulldown:before { |
||||
|
content: "\e632"; |
||||
|
} |
||||
|
|
||||
|
.uniui-scan:before { |
||||
|
content: "\e62a"; |
||||
|
} |
||||
|
|
||||
|
.uniui-bars:before { |
||||
|
content: "\e627"; |
||||
|
} |
||||
|
|
||||
|
.uniui-cart-filled:before { |
||||
|
content: "\e629"; |
||||
|
} |
||||
|
|
||||
|
.uniui-checkbox:before { |
||||
|
content: "\e62b"; |
||||
|
} |
||||
|
|
||||
|
.uniui-checkbox-filled:before { |
||||
|
content: "\e62c"; |
||||
|
} |
||||
|
|
||||
|
.uniui-shop:before { |
||||
|
content: "\e62f"; |
||||
|
} |
||||
|
|
||||
|
.uniui-headphones:before { |
||||
|
content: "\e630"; |
||||
|
} |
||||
|
|
||||
|
.uniui-cart:before { |
||||
|
content: "\e631"; |
||||
|
} |
||||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue