提交 bbe0a36d 作者: wk

Merge branch 'feature-wk-jobSwitch' into dev

...@@ -3,7 +3,8 @@ ...@@ -3,7 +3,8 @@
globalData: { globalData: {
userInfo: {}, userInfo: {},
isReject: false, isReject: false,
authMenus: [] authMenus: [],
isInit: false
}, },
onLaunch: function() { onLaunch: function() {
console.log('App Launch') console.log('App Launch')
......
...@@ -156,6 +156,10 @@ const params = { ...@@ -156,6 +156,10 @@ const params = {
url: "/ydt/aArticleLibrary/getAArticleLibraryForm", url: "/ydt/aArticleLibrary/getAArticleLibraryForm",
method: "get" method: "get"
}, },
getCompanyNews: {
url: "/ydt/aArticleLibrary/getWithCompany",
method: "get"
},
getHomeComp: { getHomeComp: {
url: "/ydt/api/v1/pageTemplate/getByTemplateKey", url: "/ydt/api/v1/pageTemplate/getByTemplateKey",
method: "get" method: "get"
...@@ -292,6 +296,22 @@ const params = { ...@@ -292,6 +296,22 @@ const params = {
getAuthMenu: { getAuthMenu: {
url: "/api/v1/user/user/get_auths", url: "/api/v1/user/user/get_auths",
method: "get" method: "get"
},
getCompanyJobList: {
url: "/api/v1/org/o_comp_user/user_comp_job_list",
method: "get"
},
switchCompany: {
url: "/api/v1/user/user/switch_company",
method: "put"
},
switchJob: {
url: "/api/v2/user/user_v2/switch_job",
method: "put"
},
getAppOperateAuth: {
url: "/proxy/adminc/v1/system_manage/p_operation/queryAppOperations",
method: "get"
} }
} }
......
let configEnv = {}
const ENV = 'dev'; // dev:开发环境 | test:测试环境
if (ENV == "dev") {
//本地环境
console.log('本地环境')
configEnv = {
baseUrl: 'https://dev-console-api.hzdingmao.com',
gtrBase: "",
jyxyBase: "",
webUrl: "https://dev-console.hzdingmao.com", //开发
authCode: "477A0661-EF04-11F7-9D69-0BDD73F06500",
labourAppNo: "appletlabourboard",
jzgrAppNo: "", //建筑工人沟通日
jyxyAppNo: "", //教育学院
jfscAppNo: "63f05e5dc9d53f743030fa92"
}
} else {
if (ENV == 'test') {
//测试环境
console.log('测试环境')
configEnv = {
baseUrl: 'https://test-console-api.hzdingmao.com',
gtrBase: "https://cwcd-h5.huizhucloud.com",
jyxyBase: "https://edu.huizhucloud.com",
webUrl: "",
authCode: "07EC0FDA-0249-6DB3-7422-0AFFF34E0E90", //测试
labourAppNo: "",
jzgrAppNo: "", //建筑工人沟通日
jyxyAppNo: "", //教育学院
jfscAppNo: ""
}
} else {
//生产环境
console.log('生产环境')
configEnv = {
baseUrl: 'https://console-api.hibuilding.cn',
gtrBase: "https://gdt-front-mobile.hibuilding.cn",
jyxyBase: "https://wlxy.cnzgc.com",
webUrl: "https://console.hibuilding.cn", //生产
authCode: "D0539533-E169-5338-8ED7-3FAD554F4F62", //正式
labourAppNo: "20230207002", //劳务看板
jzgrAppNo: "63e1e639e7025e13c471a9d2", //建筑工人沟通日
jyxyAppNo: "63e1f98420836820ca5e3c02", //教育学院
jfscAppNo: "" //积分商城
}
}
}
export default configEnv
import configEnv from "../api/env.js"
const $config = { const $config = {
pwdTemplateNo: "industry-0001", pwdTemplateNo: "industry-0001",
loginTemplateNo: "industry-0001", loginTemplateNo: "industry-0001",
systemCode: "buildingSite", systemCode: "buildingSite",
/* 授权码 */ /* 授权码 */
// authCode: "477A0661-EF04-11F7-9D69-0BDD73F06500", authCode: configEnv.authCode,
// authCode: "07EC0FDA-0249-6DB3-7422-0AFFF34E0E90", //测试
authCode: "D0539533-E169-5338-8ED7-3FAD554F4F62", //正式
pageSize: 20, pageSize: 20,
isPage: 1, isPage: 1,
smsCfg: "industry", smsCfg: "industry",
/* api域名 */ /* api域名 */
// base: "https://dev-console-api.hzdingmao.com", base: configEnv.baseUrl,
// base: "https://test-console-api.hzdingmao.com",
base: "https://console-api.hibuilding.cn",
/* 工地沟通日 */ /* 工地沟通日 */
// gtrBase:"https://cwcd-h5.huizhucloud.com",//测试 gtrBase: configEnv.gtrBase, //生产
gtrBase: "https://gdt-front-mobile.hibuilding.cn", //生产
/* 教育培训 */ /* 教育培训 */
// jyxyBase: "https://edu.huizhucloud.com", //测试 jyxyBase: configEnv.jyxyBase, //生产
jyxyBase: "https://wlxy.cnzgc.com", //生产
/* 劳务看板 */ /* 劳务看板 */
// webUrl: "https://dev-console.hzdingmao.com", //开发 webUrl: configEnv.webUrl //生产
webUrl: "https://console.hibuilding.cn" //生产
} }
export default $config export default $config
<template> <template>
<view style="padding: 10rpx 20rpx;background-color: #FFFFFF;"> <view style="padding: 10rpx 0;background-color: #FFFFFF;">
<gi-title title="签到二维码"></gi-title> <view class="gi-title" style="margin-left: 20rpx;">
<image src="../../static/qrcode-ic.png" style="width: 96rpx;height: 96rpx;margin-top: 10px;" <text class="star" v-if="isMust">*</text>
<text class="text-black-normal" style="margin-left: 10rpx;flex: 1;">签到二维码</text>
</view>
<image src="../../static/qrcode-ic.png" style="width: 96rpx;height: 96rpx;margin-top: 10px;margin-left: 20rpx;"
@click="showQrcode"> @click="showQrcode">
</image> </image>
<view style="display: flex;flex-direction: column;" v-if="record_id!=''"> <view style="display: flex;flex-direction: column;" v-if="record_id!=''">
<view style="margin-top: 10px;">签到人员</view> <view style="margin-top: 10px;margin-left: 20rpx">签到人员</view>
<view style="display: flex;flex-direction: row;flex-wrap: wrap;"> <view style="display: flex;flex-direction: row;flex-wrap: wrap;margin-left: 20rpx">
<view v-for="(item,index) in users" :key="index"> <view v-for="(item,index) in users" :key="index">
<view class="sign-user-style"> <view class="sign-user-style">
{{item.value}} {{item.value}}
......
...@@ -321,8 +321,8 @@ ...@@ -321,8 +321,8 @@
} }
.ly-tree-node__icon { .ly-tree-node__icon {
width: 30rpx; width: 40rpx;
height: 30rpx; height: 40rpx;
overflow: hidden; overflow: hidden;
margin-right: 16rpx; margin-right: 16rpx;
} }
...@@ -334,8 +334,8 @@ ...@@ -334,8 +334,8 @@
} }
.ly-tree-node__label { .ly-tree-node__label {
font-size: 32rpx; font-size: 28rpx;
color: #333333; color: #606266;
width: 750rpx; width: 750rpx;
} }
......
<template> <template>
<view ref="node" <view ref="node" name="LyTreeNode" v-show="node.visible" class="ly-tree-node" :class="{
name="LyTreeNode"
v-show="node.visible"
class="ly-tree-node"
:class="{
'is-expanded': expanded, 'is-expanded': expanded,
'is-hidden': !node.visible, 'is-hidden': !node.visible,
'is-checked': !node.disabled && node.checked 'is-checked': !node.disabled && node.checked
}" }" role="treeitem" @tap.stop="handleClick">
role="treeitem" <view class="ly-tree-node__content" :class="{
@tap.stop="handleClick" >
<view class="ly-tree-node__content"
:class="{
'is-current': node.isCurrent && highlightCurrent 'is-current': node.isCurrent && highlightCurrent
}" }" :style="{
:style="{
'padding-left': (node.level - 1) * indent + 'px' 'padding-left': (node.level - 1) * indent + 'px'
}"> }">
<text <text @tap.stop="handleExpandIconClick" :class="[
@tap.stop="handleExpandIconClick"
:class="[
{ {
'is-leaf': node.isLeaf, 'is-leaf': node.isLeaf,
expanded: !node.isLeaf && node.expanded expanded: !node.isLeaf && node.expanded
...@@ -28,67 +18,50 @@ ...@@ -28,67 +18,50 @@
iconClass ? iconClass : 'ly-iconfont ly-icon-caret-right' iconClass ? iconClass : 'ly-iconfont ly-icon-caret-right'
]"> ]">
</text> </text>
<ly-checkbox v-if="checkboxVisible || radioVisible" <ly-checkbox v-if="checkboxVisible || radioVisible" :type="checkboxVisible ? 'checkbox' : 'radio'"
:type="checkboxVisible ? 'checkbox' : 'radio'" :checked="node.checked" :indeterminate="node.indeterminate" :disabled="!!node.disabled"
:checked="node.checked" @check="handleCheckChange(!node.checked)" />
:indeterminate="node.indeterminate"
:disabled="!!node.disabled" <text v-if="node.loading" class="ly-tree-node__loading-icon ly-iconfont ly-icon-loading">
@check="handleCheckChange(!node.checked)"/>
<text v-if="node.loading"
class="ly-tree-node__loading-icon ly-iconfont ly-icon-loading">
</text> </text>
<template v-if="node.icon && node.icon.length > 0"> <template v-if="node.icon && node.icon.length > 0">
<image <image v-if="node.icon.indexOf('/') !== -1" class="ly-tree-node__icon" mode="widthFix" :src="node.icon"
v-if="node.icon.indexOf('/') !== -1" @error="handleImageError">
class="ly-tree-node__icon"
mode="widthFix"
:src="node.icon"
@error="handleImageError"
>
</image> </image>
<text v-else <text v-else class="ly-tree-node__icon" :class="node.icon">
class="ly-tree-node__icon"
:class="node.icon">
</text> </text>
</template> </template>
<text class="ly-tree-node__label">{{node.label}}</text> <text class="ly-tree-node__label">{{node.label}}</text>
</view> </view>
<view v-if="!renderAfterExpand || childNodeRendered" <view v-if="!renderAfterExpand || childNodeRendered" v-show="expanded" class="ly-tree-node__children"
v-show="expanded"
class="ly-tree-node__children"
role="group"> role="group">
<ly-tree-node v-for="cNodeId in node.childNodesId" <ly-tree-node v-for="cNodeId in node.childNodesId" :nodeId="cNodeId"
:nodeId="cNodeId" :render-after-expand="renderAfterExpand" :show-checkbox="showCheckbox" :show-radio="showRadio"
:render-after-expand="renderAfterExpand" :check-only-leaf="checkOnlyLeaf" :key="getNodeKey(cNodeId)" :indent="indent" :icon-class="iconClass">
:show-checkbox="showCheckbox"
:show-radio="showRadio"
:check-only-leaf="checkOnlyLeaf"
:key="getNodeKey(cNodeId)"
:indent="indent"
:icon-class="iconClass">
</ly-tree-node> </ly-tree-node>
</view> </view>
</view> </view>
</template> </template>
<script> <script>
import {getNodeKey} from './tool/util.js'; import {
getNodeKey
} from './tool/util.js';
import lyCheckbox from './components/ly-checkbox.vue'; import lyCheckbox from './components/ly-checkbox.vue';
export default { export default {
name: 'LyTreeNode', name: 'LyTreeNode',
componentName: 'LyTreeNode', componentName: 'LyTreeNode',
components: { components: {
lyCheckbox lyCheckbox
}, },
props: { props: {
nodeId: [Number, String], nodeId: [Number, String],
renderAfterExpand: { renderAfterExpand: {
...@@ -110,7 +83,7 @@ ...@@ -110,7 +83,7 @@
indent: Number, indent: Number,
iconClass: String iconClass: String
}, },
data() { data() {
return { return {
node: { node: {
...@@ -125,26 +98,26 @@ ...@@ -125,26 +98,26 @@
highlightCurrent: false highlightCurrent: false
}; };
}, },
inject: ['tree'], inject: ['tree'],
computed: { computed: {
checkboxVisible() { checkboxVisible() {
if (this.checkOnlyLeaf) { if (this.checkOnlyLeaf) {
return this.showCheckbox && this.node.isLeaf; return this.showCheckbox && this.node.isLeaf;
} }
return this.showCheckbox; return this.showCheckbox;
}, },
radioVisible() { radioVisible() {
if (this.checkOnlyLeaf) { if (this.checkOnlyLeaf) {
return this.showRadio && this.node.isLeaf; return this.showRadio && this.node.isLeaf;
} }
return this.showRadio; return this.showRadio;
} }
}, },
watch: { watch: {
'node.indeterminate'(val) { 'node.indeterminate'(val) {
this.handleSelectChange(this.node.checked, val); this.handleSelectChange(this.node.checked, val);
...@@ -159,18 +132,18 @@ ...@@ -159,18 +132,18 @@
} }
} }
}, },
methods: { methods: {
getNodeKey(nodeId) { getNodeKey(nodeId) {
let node = this.tree.store.root.getChildNodes([nodeId])[0]; let node = this.tree.store.root.getChildNodes([nodeId])[0];
return getNodeKey(this.tree.nodeKey, node.data); return getNodeKey(this.tree.nodeKey, node.data);
}, },
handleSelectChange(checked, indeterminate) { handleSelectChange(checked, indeterminate) {
if (this.oldChecked !== checked && this.oldIndeterminate !== indeterminate) { if (this.oldChecked !== checked && this.oldIndeterminate !== indeterminate) {
if (this.checkOnlyLeaf && !this.node.isLeaf) return; if (this.checkOnlyLeaf && !this.node.isLeaf) return;
if (this.checkboxVisible) { if (this.checkboxVisible) {
const allNodes = this.tree.store._getAllNodes(); const allNodes = this.tree.store._getAllNodes();
this.tree.$emit('check-change', { this.tree.$emit('check-change', {
...@@ -189,15 +162,15 @@ ...@@ -189,15 +162,15 @@
}); });
} }
} }
if (!this.expanded && this.tree.expandOnCheckNode && checked) { if (!this.expanded && this.tree.expandOnCheckNode && checked) {
this.handleExpandIconClick(); this.handleExpandIconClick();
} }
this.oldChecked = checked; this.oldChecked = checked;
this.indeterminate = indeterminate; this.indeterminate = indeterminate;
}, },
handleClick() { handleClick() {
this.tree.store.setCurrentNode(this.node); this.tree.store.setCurrentNode(this.node);
this.tree.$emit('current-change', { this.tree.$emit('current-change', {
...@@ -206,43 +179,43 @@ ...@@ -206,43 +179,43 @@
currentNode: this.tree.store.currentNode currentNode: this.tree.store.currentNode
}); });
this.tree.currentNode = this.node; this.tree.currentNode = this.node;
if (this.tree.expandOnClickNode) { if (this.tree.expandOnClickNode) {
this.handleExpandIconClick(); this.handleExpandIconClick();
} }
if (this.tree.checkOnClickNode && !this.node.disabled) { if (this.tree.checkOnClickNode && !this.node.disabled) {
(this.checkboxVisible || this.radioVisible) && this.handleCheckChange(!this.node.checked); (this.checkboxVisible || this.radioVisible) && this.handleCheckChange(!this.node.checked);
} }
this.tree.$emit('node-click', this.node); this.tree.$emit('node-click', this.node);
}, },
handleExpandIconClick() { handleExpandIconClick() {
if (this.node.isLeaf) return; if (this.node.isLeaf) return;
if (this.expanded) { if (this.expanded) {
this.tree.$emit('node-collapse', this.node); this.tree.$emit('node-collapse', this.node);
this.node.collapse(); this.node.collapse();
} else { } else {
this.node.expand(); this.node.expand();
this.tree.$emit('node-expand', this.node); this.tree.$emit('node-expand', this.node);
if (this.tree.accordion) { if (this.tree.accordion) {
uni.$emit(`${this.tree.elId}-tree-node-expand`, this.node); uni.$emit(`${this.tree.elId}-tree-node-expand`, this.node);
} }
} }
}, },
handleCheckChange(checked) { handleCheckChange(checked) {
if (this.node.disabled) return; if (this.node.disabled) return;
if (this.checkboxVisible) { if (this.checkboxVisible) {
this.node.setChecked(checked, !(this.tree.checkStrictly || this.checkOnlyLeaf)); this.node.setChecked(checked, !(this.tree.checkStrictly || this.checkOnlyLeaf));
} else { } else {
this.node.setRadioChecked(checked); this.node.setRadioChecked(checked);
} }
this.$nextTick(() => { this.$nextTick(() => {
this.tree.$emit('check', { this.tree.$emit('check', {
node: this.node, node: this.node,
...@@ -254,20 +227,20 @@ ...@@ -254,20 +227,20 @@
}); });
}); });
}, },
handleImageError() { handleImageError() {
this.node.icon = this.tree.defaultNodeIcon; this.node.icon = this.tree.defaultNodeIcon;
} }
}, },
created() { created() {
if (!this.tree) { if (!this.tree) {
throw new Error('Can not find node\'s tree.'); throw new Error('Can not find node\'s tree.');
} }
this.node = this.tree.store.nodesMap[this.nodeId]; this.node = this.tree.store.nodesMap[this.nodeId];
this.highlightCurrent = this.tree.highlightCurrent; this.highlightCurrent = this.tree.highlightCurrent;
if (this.node.expanded) { if (this.node.expanded) {
this.expanded = true; this.expanded = true;
this.childNodeRendered = true; this.childNodeRendered = true;
...@@ -287,7 +260,7 @@ ...@@ -287,7 +260,7 @@
}); });
} }
}, },
beforeDestroy() { beforeDestroy() {
this.$parent = null; this.$parent = null;
} }
...@@ -299,33 +272,33 @@ ...@@ -299,33 +272,33 @@
white-space: nowrap; white-space: nowrap;
outline: 0 outline: 0
} }
.ly-tree-node__content { .ly-tree-node__content {
display: flex; display: flex;
align-items: center; align-items: center;
height: 70rpx; padding: 10rpx 0;
} }
.ly-tree-node__content.is-current { .ly-tree-node__content.is-current {
background-color: #F5F7FA; background-color: #F5F7FA;
} }
.ly-tree-node__content>.ly-tree-node__expand-icon { .ly-tree-node__content>.ly-tree-node__expand-icon {
padding: 12rpx; padding: 12rpx;
} }
.ly-tree-node__checkbox { .ly-tree-node__checkbox {
display: flex; display: flex;
margin-right: 16rpx; margin-right: 16rpx;
width: 40rpx; width: 40rpx;
height: 40rpx; height: 40rpx;
} }
.ly-tree-node__checkbox>image { .ly-tree-node__checkbox>image {
width: 40rpx; width: 40rpx;
height: 40rpx; height: 40rpx;
} }
.ly-tree-node__expand-icon { .ly-tree-node__expand-icon {
color: #C0C4CC; color: #C0C4CC;
font-size: 28rpx; font-size: 28rpx;
...@@ -336,27 +309,29 @@ ...@@ -336,27 +309,29 @@
transition: transform .3s ease-in-out; transition: transform .3s ease-in-out;
transition: transform .3s ease-in-out, -webkit-transform .3s ease-in-out transition: transform .3s ease-in-out, -webkit-transform .3s ease-in-out
} }
.ly-tree-node__expand-icon.expanded { .ly-tree-node__expand-icon.expanded {
-webkit-transform: rotate(90deg); -webkit-transform: rotate(90deg);
transform: rotate(90deg) transform: rotate(90deg)
} }
.ly-tree-node__expand-icon.is-leaf { .ly-tree-node__expand-icon.is-leaf {
color: transparent; color: transparent;
} }
.ly-tree-node__icon { .ly-tree-node__icon {
width: 34rpx; width: 34rpx;
height: 34rpx; height: 34rpx;
overflow: hidden; overflow: hidden;
margin-right: 16rpx; margin-right: 16rpx;
} }
.ly-tree-node__label { .ly-tree-node__label {
font-size: 28rpx font-size: 28rpx;
white-space: normal;
word-wrap: break-word;
} }
.ly-tree-node__loading-icon { .ly-tree-node__loading-icon {
margin-right: 16rpx; margin-right: 16rpx;
font-size: 28rpx; font-size: 28rpx;
...@@ -364,33 +339,34 @@ ...@@ -364,33 +339,34 @@
-webkit-animation: rotating 2s linear infinite; -webkit-animation: rotating 2s linear infinite;
animation: rotating 2s linear infinite animation: rotating 2s linear infinite
} }
.ly-tree-node>.ly-tree-node__children { .ly-tree-node>.ly-tree-node__children {
overflow: hidden; overflow: hidden;
background-color: transparent background-color: transparent
} }
.ly-tree-node>.ly-tree-node__children.collapse-transition { .ly-tree-node>.ly-tree-node__children.collapse-transition {
transition: height .3s ease-in-out; transition: height .3s ease-in-out;
} }
.ly-tree-node.is-expanded>.ly-tree-node__children { .ly-tree-node.is-expanded>.ly-tree-node__children {
display: block display: block
} }
.ly-tree-node_collapse { .ly-tree-node_collapse {
overflow: hidden; overflow: hidden;
padding-top: 0; padding-top: 0;
padding-bottom: 0; padding-bottom: 0;
} }
/* lyTree-end */ /* lyTree-end */
/* iconfont-start */ /* iconfont-start */
@font-face { @font-face {
font-family: "ly-iconfont"; font-family: "ly-iconfont";
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPsAAsAAAAACKwAAAOeAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHEIGVgCDBgqFDIQPATYCJAMMCwgABCAFhG0HQBtfB8gekiSCdAwUAKgCFMA5Hj7H0PeTlABUr57PVyGqugqzSWJnNwWoWJjx/9rUr4TPL1ZSQpU2mycqwoRwIN3p+MkqMqyEW+OtMBLPSUBb8v//XtWMKTavxYIUsT/Wy1qbQzkBDOYEKGB7dVpPyVqgCnJNwvMvhZl10nMCtQbFoPVhY8ZDncJfF4grbqpQ13AqE52hWqgcOFrEQ6hWnW5VfMCD7Pfjn4WoI6nI/K0bl0MNGPBz0qcflVqYnvCA4vNDPUXGPFCIw8HgtsqiOK9SrW2smm6sVITElWlpISMdVBn8wyMJopLfXg+myZ48KCrSkvj9g37U1ItbXYke4APwXxK3N4TuehyBfmM0I3zbNdt7uk3VnjPtzX0rnIl7z7bZvb/thHohsu9QuykKo+Cws4nL7LsPmI3n2qN9B9upZEIKd4hu0NCKi0rt7fNtdl+I1N25hOJMDQK6odS123tROR7Pg8toEhDaF+kR0TYjxW6M58F5+ZNQOxmZHtE2g+IYjxjlNy/yIRQpCmrgq5R4/3jx8PvT8Ha8d3/xiLnt4EGyaDnznzRv8vpyZ+9TFHf/ntX9e59A+b6+fPHd5+dy0wYHVvHOroWbnWe879O9DnL53bN/gUHuwm28b/n8i/V3ry4E3IoXNqS6Rvs0LhJxeNVjoUkM3LKosU+0a6rh45FVvLt+2oz7Zd53b4QOy7/9snDXHbqVu+A+f8r7PnM2H8kXrWm5c8/vLu7LqRee7HW637mz3kHc5U/RCXf25d7G8tkdgEfwIpzpkknGpaMw3ww55q9Mn9OQNyua/wB/49OOWydn4eL/6roCfjx6FMmcxfJStYRKfd3UwoHiML4rF4uMSK+SvYTuNxMHrpl8yd3Q6v32cAeo/KFaowBJlQHIqo3zi3geKtRZhErVlqDWnOGn67QRKkWpwaw1AkKza5A0egFZszf8In4HFTp9h0rNUQm1NqP1lXUmgyuDBVUlNYi2gHA98FnokUreOZaac1xV1JlMMZGKEs+QdCLVrgynPhUcO0pzzYyUjDAReGSYeBl13YCEIrCpLhOWlGE+mWRD35TQAw8UawRKJVEGQrMAwekCPpaMlpTOz49FmeZwqcREX1t3Ikoo4dMTaQmpBfzhRn9R30uZXTKXKUOSmLSKEQIeYhjqKZcrcIzhMLLRrJMSrA35UF4yGMaWGhPHm733dwJq+Z/NkSJHUXemCirjgpuWrHMD1eC+mQUAAAA=') format('woff2'); src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPsAAsAAAAACKwAAAOeAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHEIGVgCDBgqFDIQPATYCJAMMCwgABCAFhG0HQBtfB8gekiSCdAwUAKgCFMA5Hj7H0PeTlABUr57PVyGqugqzSWJnNwWoWJjx/9rUr4TPL1ZSQpU2mycqwoRwIN3p+MkqMqyEW+OtMBLPSUBb8v//XtWMKTavxYIUsT/Wy1qbQzkBDOYEKGB7dVpPyVqgCnJNwvMvhZl10nMCtQbFoPVhY8ZDncJfF4grbqpQ13AqE52hWqgcOFrEQ6hWnW5VfMCD7Pfjn4WoI6nI/K0bl0MNGPBz0qcflVqYnvCA4vNDPUXGPFCIw8HgtsqiOK9SrW2smm6sVITElWlpISMdVBn8wyMJopLfXg+myZ48KCrSkvj9g37U1ItbXYke4APwXxK3N4TuehyBfmM0I3zbNdt7uk3VnjPtzX0rnIl7z7bZvb/thHohsu9QuykKo+Cws4nL7LsPmI3n2qN9B9upZEIKd4hu0NCKi0rt7fNtdl+I1N25hOJMDQK6odS123tROR7Pg8toEhDaF+kR0TYjxW6M58F5+ZNQOxmZHtE2g+IYjxjlNy/yIRQpCmrgq5R4/3jx8PvT8Ha8d3/xiLnt4EGyaDnznzRv8vpyZ+9TFHf/ntX9e59A+b6+fPHd5+dy0wYHVvHOroWbnWe879O9DnL53bN/gUHuwm28b/n8i/V3ry4E3IoXNqS6Rvs0LhJxeNVjoUkM3LKosU+0a6rh45FVvLt+2oz7Zd53b4QOy7/9snDXHbqVu+A+f8r7PnM2H8kXrWm5c8/vLu7LqRee7HW637mz3kHc5U/RCXf25d7G8tkdgEfwIpzpkknGpaMw3ww55q9Mn9OQNyua/wB/49OOWydn4eL/6roCfjx6FMmcxfJStYRKfd3UwoHiML4rF4uMSK+SvYTuNxMHrpl8yd3Q6v32cAeo/KFaowBJlQHIqo3zi3geKtRZhErVlqDWnOGn67QRKkWpwaw1AkKza5A0egFZszf8In4HFTp9h0rNUQm1NqP1lXUmgyuDBVUlNYi2gHA98FnokUreOZaac1xV1JlMMZGKEs+QdCLVrgynPhUcO0pzzYyUjDAReGSYeBl13YCEIrCpLhOWlGE+mWRD35TQAw8UawRKJVEGQrMAwekCPpaMlpTOz49FmeZwqcREX1t3Ikoo4dMTaQmpBfzhRn9R30uZXTKXKUOSmLSKEQIeYhjqKZcrcIzhMLLRrJMSrA35UF4yGMaWGhPHm733dwJq+Z/NkSJHUXemCirjgpuWrHMD1eC+mQUAAAA=') format('woff2');
} }
.ly-iconfont { .ly-iconfont {
font-family: "ly-iconfont" !important; font-family: "ly-iconfont" !important;
font-size: 30rpx; font-size: 30rpx;
...@@ -398,27 +374,29 @@ ...@@ -398,27 +374,29 @@
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
} }
.ly-icon-caret-right:before { .ly-icon-caret-right:before {
content: "\e8ee"; content: "\e8ee";
} }
.ly-icon-loading:before { .ly-icon-loading:before {
content: "\e657"; content: "\e657";
} }
/* iconfont-end */ /* iconfont-end */
/* animate-start */ /* animate-start */
@keyframes rotating { @keyframes rotating {
0% { 0% {
-webkit-transform: rotateZ(0); -webkit-transform: rotateZ(0);
transform: rotateZ(0) transform: rotateZ(0)
} }
100% { 100% {
-webkit-transform: rotateZ(360deg); -webkit-transform: rotateZ(360deg);
transform: rotateZ(360deg) transform: rotateZ(360deg)
} }
} }
/* animate-end */ /* animate-end */
</style> </style>
\ No newline at end of file
...@@ -5,16 +5,26 @@ ...@@ -5,16 +5,26 @@
<view class="ly-loader-inner">加载中...</view> <view class="ly-loader-inner">加载中...</view>
</view> </view>
</template> </template>
<template v-else> <template v-else>
<view v-if="isEmpty || !visible" class="ly-empty"> <view v-if="isEmpty || !visible"
class="ly-empty">
{{emptyText}} {{emptyText}}
</view> </view>
<view :key="updateKey" class="ly-tree" :class="{'is-empty': isEmpty || !visible}" role="tree" <view :key="updateKey"
class="ly-tree"
:class="{'is-empty': isEmpty || !visible}"
role="tree"
name="LyTreeExpand"> name="LyTreeExpand">
<ly-tree-node v-for="nodeId in childNodesId" :nodeId="nodeId" :render-after-expand="renderAfterExpand" <ly-tree-node v-for="nodeId in childNodesId"
:show-checkbox="showCheckbox" :show-radio="showRadio" :check-only-leaf="checkOnlyLeaf" :nodeId="nodeId"
:key="getNodeKey(nodeId)" :indent="indent" :icon-class="iconClass"> :render-after-expand="renderAfterExpand"
:show-checkbox="showCheckbox"
:show-radio="showRadio"
:check-only-leaf="checkOnlyLeaf"
:key="getNodeKey(nodeId)"
:indent="indent"
:icon-class="iconClass">
</ly-tree-node> </ly-tree-node>
</view> </view>
</template> </template>
...@@ -24,20 +34,18 @@ ...@@ -24,20 +34,18 @@
<script> <script>
import Vue from 'vue' import Vue from 'vue'
import TreeStore from './model/tree-store.js'; import TreeStore from './model/tree-store.js';
import { import {getNodeKey} from './tool/util.js';
getNodeKey
} from './tool/util.js';
import LyTreeNode from './ly-tree-node.vue'; import LyTreeNode from './ly-tree-node.vue';
export default { export default {
name: 'LyTree', name: 'LyTree',
componentName: 'LyTree', componentName: 'LyTree',
components: { components: {
LyTreeNode LyTreeNode
}, },
data() { data() {
return { return {
updateKey: new Date().getTime(), // 数据更新的时候,重新渲染树 updateKey: new Date().getTime(), // 数据更新的时候,重新渲染树
...@@ -50,102 +58,102 @@ ...@@ -50,102 +58,102 @@
childNodesId: [] childNodesId: []
}; };
}, },
provide() { provide() {
return { return {
tree: this tree: this
} }
}, },
props: { props: {
// 展示数据 // 展示数据
treeData: Array, treeData: Array,
// 自主控制loading加载,避免数据还没获取到的空档出现“暂无数据”字样 // 自主控制loading加载,避免数据还没获取到的空档出现“暂无数据”字样
ready: { ready: {
type: Boolean, type: Boolean,
default: true default: true
}, },
// 内容为空的时候展示的文本 // 内容为空的时候展示的文本
emptyText: { emptyText: {
type: String, type: String,
default: '暂无数据' default: '暂无数据'
}, },
// 是否在第一次展开某个树节点后才渲染其子节点 // 是否在第一次展开某个树节点后才渲染其子节点
renderAfterExpand: { renderAfterExpand: {
type: Boolean, type: Boolean,
default: true default: true
}, },
// 每个树节点用来作为唯一标识的属性,整棵树应该是唯一的 // 每个树节点用来作为唯一标识的属性,整棵树应该是唯一的
nodeKey: String, nodeKey: String,
// 在显示复选框的情况下,是否严格的遵循父子不互相关联的做法,默认为 false // 在显示复选框的情况下,是否严格的遵循父子不互相关联的做法,默认为 false
checkStrictly: Boolean, checkStrictly: Boolean,
// 是否默认展开所有节点 // 是否默认展开所有节点
defaultExpandAll: Boolean, defaultExpandAll: Boolean,
// 切换全部展开、全部折叠 // 切换全部展开、全部折叠
toggleExpendAll: Boolean, toggleExpendAll: Boolean,
// 是否在点击节点的时候展开或者收缩节点, 默认值为 true,如果为 false,则只有点箭头图标的时候才会展开或者收缩节点 // 是否在点击节点的时候展开或者收缩节点, 默认值为 true,如果为 false,则只有点箭头图标的时候才会展开或者收缩节点
expandOnClickNode: { expandOnClickNode: {
type: Boolean, type: Boolean,
default: true default: true
}, },
// 选中的时候展开节点 // 选中的时候展开节点
expandOnCheckNode: { expandOnCheckNode: {
type: Boolean, type: Boolean,
default: true default: true
}, },
// 是否在点击节点的时候选中节点,默认值为 false,即只有在点击复选框时才会选中节点 // 是否在点击节点的时候选中节点,默认值为 false,即只有在点击复选框时才会选中节点
checkOnClickNode: Boolean, checkOnClickNode: Boolean,
checkDescendants: { checkDescendants: {
type: Boolean, type: Boolean,
default: false default: false
}, },
// 展开子节点的时候是否自动展开父节点 // 展开子节点的时候是否自动展开父节点
autoExpandParent: { autoExpandParent: {
type: Boolean, type: Boolean,
default: true default: true
}, },
// 默认勾选的节点的 key 的数组 // 默认勾选的节点的 key 的数组
defaultCheckedKeys: Array, defaultCheckedKeys: Array,
// 默认展开的节点的 key 的数组 // 默认展开的节点的 key 的数组
defaultExpandedKeys: Array, defaultExpandedKeys: Array,
// 是否展开当前节点的父节点 // 是否展开当前节点的父节点
expandCurrentNodeParent: Boolean, expandCurrentNodeParent: Boolean,
// 当前选中的节点 // 当前选中的节点
currentNodeKey: [String, Number], currentNodeKey: [String, Number],
// 是否最后一层叶子节点才显示单选/多选框 // 是否最后一层叶子节点才显示单选/多选框
checkOnlyLeaf: { checkOnlyLeaf: {
type: Boolean, type: Boolean,
default: false default: false
}, },
// 节点是否可被选择 // 节点是否可被选择
showCheckbox: { showCheckbox: {
type: Boolean, type: Boolean,
default: false default: false
}, },
// 节点单选 // 节点单选
showRadio: { showRadio: {
type: Boolean, type: Boolean,
default: false default: false
}, },
// 配置选项 // 配置选项
props: { props: {
type: [Object, Function], type: [Object, Function],
...@@ -157,76 +165,74 @@ ...@@ -157,76 +165,74 @@
}; };
} }
}, },
// 是否懒加载子节点,需与 load 方法结合使用 // 是否懒加载子节点,需与 load 方法结合使用
lazy: { lazy: {
type: Boolean, type: Boolean,
default: false default: false
}, },
// 是否高亮当前选中节点,默认值是 false // 是否高亮当前选中节点,默认值是 false
highlightCurrent: Boolean, highlightCurrent: Boolean,
// 加载子树数据的方法,仅当 lazy 属性为true 时生效 // 加载子树数据的方法,仅当 lazy 属性为true 时生效
load: Function, load: Function,
// 对树节点进行筛选时执行的方法,返回 true 表示这个节点可以显示,返回 false 则表示这个节点会被隐藏 // 对树节点进行筛选时执行的方法,返回 true 表示这个节点可以显示,返回 false 则表示这个节点会被隐藏
filterNodeMethod: Function, filterNodeMethod: Function,
// 搜索时是否展示匹配项的所有子节点 // 搜索时是否展示匹配项的所有子节点
childVisibleForFilterNode: { childVisibleForFilterNode: {
type: Boolean, type: Boolean,
default: false default: false
}, },
// 是否每次只打开一个同级树节点展开 // 是否每次只打开一个同级树节点展开
accordion: Boolean, accordion: Boolean,
// 相邻级节点间的水平缩进,单位为像素 // 相邻级节点间的水平缩进,单位为像素
indent: { indent: {
type: Number, type: Number,
default: 18 default: 18
}, },
// 自定义树节点的展开图标 // 自定义树节点的展开图标
iconClass: String, iconClass: String,
// 是否显示节点图标,如果配置为true,需要配置props中对应的图标属性名称 // 是否显示节点图标,如果配置为true,需要配置props中对应的图标属性名称
showNodeIcon: { showNodeIcon: {
type: Boolean, type: Boolean,
default: false default: false
}, },
// 当节点图标显示出错时,显示的默认图标 // 当节点图标显示出错时,显示的默认图标
defaultNodeIcon: { defaultNodeIcon: {
type: String, type: String,
default: 'https://img-cdn-qiniu.dcloud.net.cn/uniapp/doc/github.svg' default: 'https://img-cdn-qiniu.dcloud.net.cn/uniapp/doc/github.svg'
}, },
// 如果数据量较大,建议不要在node节点中添加parent属性,会造成性能损耗 // 如果数据量较大,建议不要在node节点中添加parent属性,会造成性能损耗
isInjectParentInNode: { isInjectParentInNode: {
type: Boolean, type: Boolean,
default: false default: false
} }
}, },
computed: { computed: {
isEmpty() { isEmpty() {
if (this.store.root) { if (this.store.root) {
const childNodes = this.store.root.getChildNodes(this.childNodesId); const childNodes = this.store.root.getChildNodes(this.childNodesId);
return !childNodes || childNodes.length === 0 || childNodes.every(({ return !childNodes || childNodes.length === 0 || childNodes.every(({visible}) => !visible);
visible
}) => !visible);
} }
return true; return true;
}, },
showLoading() { showLoading() {
return !(this.store.ready && this.ready); return !(this.store.ready && this.ready);
} }
}, },
watch: { watch: {
toggleExpendAll(newVal) { toggleExpendAll(newVal) {
this.store.toggleExpendAll(newVal); this.store.toggleExpendAll(newVal);
...@@ -247,7 +253,7 @@ ...@@ -247,7 +253,7 @@
'store.root.visible'(newVal) { 'store.root.visible'(newVal) {
this.visible = newVal; this.visible = newVal;
}, },
childNodesId() { childNodesId(){
this.$nextTick(() => { this.$nextTick(() => {
this.$emit('ly-tree-render-completed'); this.$emit('ly-tree-render-completed');
}); });
...@@ -260,233 +266,232 @@ ...@@ -260,233 +266,232 @@
deep: true deep: true
} }
}, },
methods: { methods: {
/* /*
* @description 对树节点进行筛选操作 * @description 对树节点进行筛选操作
* @method filter * @method filter
* @param {all} value 在 filter-node-method 中作为第一个参数 * @param {all} value 在 filter-node-method 中作为第一个参数
* @param {Object} data 搜索指定节点的节点数据,不传代表搜索所有节点,假如要搜索A节点下面的数据,那么nodeData代表treeData中A节点的数据 * @param {Object} data 搜索指定节点的节点数据,不传代表搜索所有节点,假如要搜索A节点下面的数据,那么nodeData代表treeData中A节点的数据
*/ */
filter(value, data) { filter(value, data) {
if (!this.filterNodeMethod) throw new Error('[Tree] filterNodeMethod is required when filter'); if (!this.filterNodeMethod) throw new Error('[Tree] filterNodeMethod is required when filter');
this.store.filter(value, data); this.store.filter(value, data);
}, },
/* /*
* @description 获取节点的唯一标识符 * @description 获取节点的唯一标识符
* @method getNodeKey * @method getNodeKey
* @param {String, Number} nodeId * @param {String, Number} nodeId
* @return {String, Number} 匹配到的数据中的某一项数据 * @return {String, Number} 匹配到的数据中的某一项数据
*/ */
getNodeKey(nodeId) { getNodeKey(nodeId) {
let node = this.store.root.getChildNodes([nodeId])[0]; let node = this.store.root.getChildNodes([nodeId])[0];
return getNodeKey(this.nodeKey, node.data); return getNodeKey(this.nodeKey, node.data);
}, },
/* /*
* @description 获取节点路径 * @description 获取节点路径
* @method getNodePath * @method getNodePath
* @param {Object} data 节点数据 * @param {Object} data 节点数据
* @return {Array} 路径数组 * @return {Array} 路径数组
*/ */
getNodePath(data) { getNodePath(data) {
return this.store.getNodePath(data); return this.store.getNodePath(data);
}, },
/* /*
* @description 若节点可被选择(即 show-checkbox 为 true),则返回目前被选中的节点所组成的数组 * @description 若节点可被选择(即 show-checkbox 为 true),则返回目前被选中的节点所组成的数组
* @method getCheckedNodes * @method getCheckedNodes
* @param {Boolean} leafOnly 是否只是叶子节点,默认false * @param {Boolean} leafOnly 是否只是叶子节点,默认false
* @param {Boolean} includeHalfChecked 是否包含半选节点,默认false * @param {Boolean} includeHalfChecked 是否包含半选节点,默认false
* @return {Array} 目前被选中的节点所组成的数组 * @return {Array} 目前被选中的节点所组成的数组
*/ */
getCheckedNodes(leafOnly, includeHalfChecked) { getCheckedNodes(leafOnly, includeHalfChecked) {
return this.store.getCheckedNodes(leafOnly, includeHalfChecked); return this.store.getCheckedNodes(leafOnly, includeHalfChecked);
}, },
/* /*
* @description 若节点可被选择(即 show-checkbox 为 true),则返回目前被选中的节点的 key 所组成的数组 * @description 若节点可被选择(即 show-checkbox 为 true),则返回目前被选中的节点的 key 所组成的数组
* @method getCheckedKeys * @method getCheckedKeys
* @param {Boolean} leafOnly 是否只是叶子节点,默认false,若为 true 则仅返回被选中的叶子节点的 keys * @param {Boolean} leafOnly 是否只是叶子节点,默认false,若为 true 则仅返回被选中的叶子节点的 keys
* @param {Boolean} includeHalfChecked 是否返回indeterminate为true的节点,默认false * @param {Boolean} includeHalfChecked 是否返回indeterminate为true的节点,默认false
* @return {Array} 目前被选中的节点所组成的数组 * @return {Array} 目前被选中的节点所组成的数组
*/ */
getCheckedKeys(leafOnly, includeHalfChecked) { getCheckedKeys(leafOnly, includeHalfChecked) {
return this.store.getCheckedKeys(leafOnly, includeHalfChecked); return this.store.getCheckedKeys(leafOnly, includeHalfChecked);
}, },
/* /*
* @description 获取当前被选中节点的 data,若没有节点被选中则返回 null * @description 获取当前被选中节点的 data,若没有节点被选中则返回 null
* @method getCurrentNode * @method getCurrentNode
* @return {Object} 当前被选中节点的 data,若没有节点被选中则返回 null * @return {Object} 当前被选中节点的 data,若没有节点被选中则返回 null
*/ */
getCurrentNode() { getCurrentNode() {
const currentNode = this.store.getCurrentNode(); const currentNode = this.store.getCurrentNode();
return currentNode ? currentNode.data : null; return currentNode ? currentNode.data : null;
}, },
/* /*
* @description 获取当前被选中节点的 key,若没有节点被选中则返回 null * @description 获取当前被选中节点的 key,若没有节点被选中则返回 null
* @method getCurrentKey * @method getCurrentKey
* @return {all} 当前被选中节点的 key, 若没有节点被选中则返回 null * @return {all} 当前被选中节点的 key, 若没有节点被选中则返回 null
*/ */
getCurrentKey() { getCurrentKey() {
const currentNode = this.getCurrentNode(); const currentNode = this.getCurrentNode();
return currentNode ? currentNode[this.nodeKey] : null; return currentNode ? currentNode[this.nodeKey] : null;
}, },
/* /*
* @description 设置全选/取消全选 * @description 设置全选/取消全选
* @method setCheckAll * @method setCheckAll
* @param {Boolean} isCheckAll 选中状态,默认为true * @param {Boolean} isCheckAll 选中状态,默认为true
*/ */
setCheckAll(isCheckAll = true) { setCheckAll(isCheckAll = true) {
if (this.showRadio) throw new Error('You set the "show-radio" property, so you cannot select all nodes'); if (this.showRadio) throw new Error('You set the "show-radio" property, so you cannot select all nodes');
if (!this.showCheckbox) console.warn( if (!this.showCheckbox) console.warn('You have not set the property "show-checkbox". Please check your settings');
'You have not set the property "show-checkbox". Please check your settings');
this.store.setCheckAll(isCheckAll); this.store.setCheckAll(isCheckAll);
}, },
/* /*
* @description 设置目前勾选的节点 * @description 设置目前勾选的节点
* @method setCheckedNodes * @method setCheckedNodes
* @param {Array} nodes 接收勾选节点数据的数组 * @param {Array} nodes 接收勾选节点数据的数组
* @param {Boolean} leafOnly 是否只是叶子节点, 若为 true 则仅设置叶子节点的选中状态,默认值为 false * @param {Boolean} leafOnly 是否只是叶子节点, 若为 true 则仅设置叶子节点的选中状态,默认值为 false
*/ */
setCheckedNodes(nodes, leafOnly) { setCheckedNodes(nodes, leafOnly) {
this.store.setCheckedNodes(nodes, leafOnly); this.store.setCheckedNodes(nodes, leafOnly);
}, },
/* /*
* @description 通过 keys 设置目前勾选的节点 * @description 通过 keys 设置目前勾选的节点
* @method setCheckedKeys * @method setCheckedKeys
* @param {Array} keys 勾选节点的 key 的数组 * @param {Array} keys 勾选节点的 key 的数组
* @param {Boolean} leafOnly 是否只是叶子节点, 若为 true 则仅设置叶子节点的选中状态,默认值为 false * @param {Boolean} leafOnly 是否只是叶子节点, 若为 true 则仅设置叶子节点的选中状态,默认值为 false
*/ */
setCheckedKeys(keys, leafOnly) { setCheckedKeys(keys, leafOnly) {
if (!this.nodeKey) throw new Error('[Tree] nodeKey is required in setCheckedKeys'); if (!this.nodeKey) throw new Error('[Tree] nodeKey is required in setCheckedKeys');
this.store.setCheckedKeys(keys, leafOnly); this.store.setCheckedKeys(keys, leafOnly);
}, },
/* /*
* @description 通过 key / data 设置某个节点的勾选状态 * @description 通过 key / data 设置某个节点的勾选状态
* @method setChecked * @method setChecked
* @param {all} data 勾选节点的 key 或者 data * @param {all} data 勾选节点的 key 或者 data
* @param {Boolean} checked 节点是否选中 * @param {Boolean} checked 节点是否选中
* @param {Boolean} deep 是否设置子节点 ,默认为 false * @param {Boolean} deep 是否设置子节点 ,默认为 false
*/ */
setChecked(data, checked, deep) { setChecked(data, checked, deep) {
this.store.setChecked(data, checked, deep); this.store.setChecked(data, checked, deep);
}, },
/* /*
* @description 若节点可被选择(即 show-checkbox 为 true),则返回目前半选中的节点所组成的数组 * @description 若节点可被选择(即 show-checkbox 为 true),则返回目前半选中的节点所组成的数组
* @method getHalfCheckedNodes * @method getHalfCheckedNodes
* @return {Array} 目前半选中的节点所组成的数组 * @return {Array} 目前半选中的节点所组成的数组
*/ */
getHalfCheckedNodes() { getHalfCheckedNodes() {
return this.store.getHalfCheckedNodes(); return this.store.getHalfCheckedNodes();
}, },
/* /*
* @description 若节点可被选择(即 show-checkbox 为 true),则返回目前半选中的节点的 key 所组成的数组 * @description 若节点可被选择(即 show-checkbox 为 true),则返回目前半选中的节点的 key 所组成的数组
* @method getHalfCheckedKeys * @method getHalfCheckedKeys
* @return {Array} 目前半选中的节点的 key 所组成的数组 * @return {Array} 目前半选中的节点的 key 所组成的数组
*/ */
getHalfCheckedKeys() { getHalfCheckedKeys() {
return this.store.getHalfCheckedKeys(); return this.store.getHalfCheckedKeys();
}, },
/* /*
* @description 通过 node 设置某个节点的当前选中状态 * @description 通过 node 设置某个节点的当前选中状态
* @method setCurrentNode * @method setCurrentNode
* @param {Object} node 待被选节点的 node * @param {Object} node 待被选节点的 node
*/ */
setCurrentNode(node) { setCurrentNode(node) {
if (!this.nodeKey) throw new Error('[Tree] nodeKey is required in setCurrentNode'); if (!this.nodeKey) throw new Error('[Tree] nodeKey is required in setCurrentNode');
this.store.setUserCurrentNode(node); this.store.setUserCurrentNode(node);
}, },
/* /*
* @description 通过 key 设置某个节点的当前选中状态 * @description 通过 key 设置某个节点的当前选中状态
* @method setCurrentKey * @method setCurrentKey
* @param {all} key 待被选节点的 key,若为 null 则取消当前高亮的节点 * @param {all} key 待被选节点的 key,若为 null 则取消当前高亮的节点
*/ */
setCurrentKey(key) { setCurrentKey(key) {
if (!this.nodeKey) throw new Error('[Tree] nodeKey is required in setCurrentKey'); if (!this.nodeKey) throw new Error('[Tree] nodeKey is required in setCurrentKey');
this.store.setCurrentNodeKey(key); this.store.setCurrentNodeKey(key);
}, },
/* /*
* @description 根据 data 或者 key 拿到 Tree 组件中的 node * @description 根据 data 或者 key 拿到 Tree 组件中的 node
* @method getNode * @method getNode
* @param {all} data 要获得 node 的 key 或者 data * @param {all} data 要获得 node 的 key 或者 data
*/ */
getNode(data) { getNode(data) {
return this.store.getNode(data); return this.store.getNode(data);
}, },
/* /*
* @description 删除 Tree 中的一个节点 * @description 删除 Tree 中的一个节点
* @method remove * @method remove
* @param {all} data 要删除的节点的 data 或者 node * @param {all} data 要删除的节点的 data 或者 node
*/ */
remove(data) { remove(data) {
this.store.remove(data); this.store.remove(data);
}, },
/* /*
* @description 为 Tree 中的一个节点追加一个子节点 * @description 为 Tree 中的一个节点追加一个子节点
* @method append * @method append
* @param {Object} data 要追加的子节点的 data * @param {Object} data 要追加的子节点的 data
* @param {Object} parentNode 子节点的 parent 的 data、key 或者 node * @param {Object} parentNode 子节点的 parent 的 data、key 或者 node
*/ */
append(data, parentNode) { append(data, parentNode) {
this.store.append(data, parentNode); this.store.append(data, parentNode);
}, },
/* /*
* @description 为 Tree 的一个节点的前面增加一个节点 * @description 为 Tree 的一个节点的前面增加一个节点
* @method insertBefore * @method insertBefore
* @param {Object} data 要增加的节点的 data * @param {Object} data 要增加的节点的 data
* @param {all} refNode 要增加的节点的后一个节点的 data、key 或者 node * @param {all} refNode 要增加的节点的后一个节点的 data、key 或者 node
*/ */
insertBefore(data, refNode) { insertBefore(data, refNode) {
this.store.insertBefore(data, refNode); this.store.insertBefore(data, refNode);
}, },
/* /*
* @description 为 Tree 的一个节点的后面增加一个节点 * @description 为 Tree 的一个节点的后面增加一个节点
* @method insertAfter * @method insertAfter
* @param {Object} data 要增加的节点的 data * @param {Object} data 要增加的节点的 data
* @param {all} refNode 要增加的节点的前一个节点的 data、key 或者 node * @param {all} refNode 要增加的节点的前一个节点的 data、key 或者 node
*/ */
insertAfter(data, refNode) { insertAfter(data, refNode) {
this.store.insertAfter(data, refNode); this.store.insertAfter(data, refNode);
}, },
/* /*
* @description 通过 keys 设置节点子元素 * @description 通过 keys 设置节点子元素
* @method updateKeyChildren * @method updateKeyChildren
* @param {String, Number} key 节点 key * @param {String, Number} key 节点 key
* @param {Object} data 节点数据的数组 * @param {Object} data 节点数据的数组
*/ */
updateKeyChildren(key, data) { updateKeyChildren(key, data) {
if (!this.nodeKey) throw new Error('[Tree] nodeKey is required in updateKeyChild'); if (!this.nodeKey) throw new Error('[Tree] nodeKey is required in updateKeyChild');
this.store.updateChildren(key, data); this.store.updateChildren(key, data);
} }
}, },
created() { created() {
this.isTree = true; this.isTree = true;
let props = this.props; let props = this.props;
if (typeof this.props === 'function') props = this.props(); if (typeof this.props === 'function') props = this.props();
if (typeof props !== 'object') throw new Error('props must be of object type.'); if (typeof props !== 'object') throw new Error('props must be of object type.');
this.store = new TreeStore({ this.store = new TreeStore({
key: this.nodeKey, key: this.nodeKey,
data: this.treeData, data: this.treeData,
...@@ -512,7 +517,7 @@ ...@@ -512,7 +517,7 @@
this.childNodesId = this.store.root.childNodesId; this.childNodesId = this.store.root.childNodesId;
}, },
beforeDestroy() { beforeDestroy() {
if (this.accordion) { if (this.accordion) {
uni.$off(`${this.elId}-tree-node-expand`) uni.$off(`${this.elId}-tree-node-expand`)
...@@ -526,14 +531,14 @@ ...@@ -526,14 +531,14 @@
position: relative; position: relative;
cursor: default; cursor: default;
background: #FFF; background: #FFF;
color: #333333; color: #606266;
padding: 20rpx; padding: 30rpx;
} }
.ly-tree.is-empty { .ly-tree.is-empty {
background: transparent; background: transparent;
} }
/* lyEmpty-start */ /* lyEmpty-start */
.ly-empty { .ly-empty {
width: 100%; width: 100%;
...@@ -541,9 +546,8 @@ ...@@ -541,9 +546,8 @@
justify-content: center; justify-content: center;
margin-top: 100rpx; margin-top: 100rpx;
} }
/* lyEmpty-end */ /* lyEmpty-end */
/* lyLoader-start */ /* lyLoader-start */
.ly-loader { .ly-loader {
margin-top: 100rpx; margin-top: 100rpx;
...@@ -551,7 +555,7 @@ ...@@ -551,7 +555,7 @@
align-items: center; align-items: center;
justify-content: center; justify-content: center;
} }
.ly-loader-inner, .ly-loader-inner,
.ly-loader-inner:before, .ly-loader-inner:before,
.ly-loader-inner:after { .ly-loader-inner:after {
...@@ -560,41 +564,39 @@ ...@@ -560,41 +564,39 @@
width: .5em; width: .5em;
height: 1em; height: 1em;
} }
.ly-loader-inner:before, .ly-loader-inner:before,
.ly-loader-inner:after { .ly-loader-inner:after {
position: absolute; position: absolute;
top: 0; top: 0;
content: ''; content: '';
} }
.ly-loader-inner:before { .ly-loader-inner:before {
left: -1em; left: -1em;
} }
.ly-loader-inner { .ly-loader-inner {
text-indent: -9999em; text-indent: -9999em;
position: relative; position: relative;
font-size: 22rpx; font-size: 22rpx;
animation-delay: 0.16s; animation-delay: 0.16s;
} }
.ly-loader-inner:after { .ly-loader-inner:after {
left: 1em; left: 1em;
animation-delay: 0.32s; animation-delay: 0.32s;
} }
/* lyLoader-end */ /* lyLoader-end */
@keyframes load { @keyframes load {
0%, 0%,
80%, 80%,
100% { 100% {
box-shadow: 0 0 #efefef; box-shadow: 0 0 #efefef;
height: 1em; height: 1em;
} }
40% { 40% {
box-shadow: 0 -1.5em #efefef; box-shadow: 0 -1.5em #efefef;
height: 1.5em; height: 1.5em;
......
...@@ -70,6 +70,7 @@ ...@@ -70,6 +70,7 @@
<style lang="scss"> <style lang="scss">
.navbar-home { .navbar-home {
.navbar-content { .navbar-content {
position: relative; position: relative;
top: 0; top: 0;
......
...@@ -73,6 +73,7 @@ ...@@ -73,6 +73,7 @@
margin-bottom: 16px; margin-bottom: 16px;
width: 100%; width: 100%;
background: linear-gradient(138.42deg, #59a3fe 0%, #1757ff 100%); background: linear-gradient(138.42deg, #59a3fe 0%, #1757ff 100%);
z-index: 999;
.navbar-v-navbar { .navbar-v-navbar {
display: flex; display: flex;
......
<template>
<view class="uni-calendar-item__weeks-box" :class="{
'uni-calendar-item--disable':weeks.disable,
'uni-calendar-item--before-checked-x':weeks.beforeMultiple,
'uni-calendar-item--multiple': weeks.multiple,
'uni-calendar-item--after-checked-x':weeks.afterMultiple,
}" @click="choiceDate(weeks)" @mouseenter="handleMousemove(weeks)">
<view class="uni-calendar-item__weeks-box-item" :class="{
'uni-calendar-item--checked':calendar.fullDate === weeks.fullDate && (calendar.userChecked || !checkHover),
'uni-calendar-item--checked-range-text': checkHover,
'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,
}">
<text v-if="selected&&weeks.extraInfo" class="uni-calendar-item__weeks-box-circle"></text>
<text class="uni-calendar-item__weeks-box-text uni-calendar-item__weeks-box-text-disable uni-calendar-item--checked-text">{{weeks.date}}</text>
</view>
<view :class="{'uni-calendar-item--isDay': weeks.isDay}"></view>
</view>
</template>
<script>
export default {
props: {
weeks: {
type: Object,
default () {
return {}
}
},
calendar: {
type: Object,
default: () => {
return {}
}
},
selected: {
type: Array,
default: () => {
return []
}
},
lunar: {
type: Boolean,
default: false
},
checkHover: {
type: Boolean,
default: false
}
},
methods: {
choiceDate(weeks) {
this.$emit('change', weeks)
},
handleMousemove(weeks) {
this.$emit('handleMouse', weeks)
}
}
}
</script>
<style lang="scss" >
$uni-primary: #007aff !default;
.uni-calendar-item__weeks-box {
flex: 1;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
justify-content: center;
align-items: center;
margin: 1px 0;
position: relative;
}
.uni-calendar-item__weeks-box-text {
font-size: 14px;
// font-family: Lato-Bold, Lato;
font-weight: bold;
color: darken($color: $uni-primary, $amount: 40%);
}
.uni-calendar-item__weeks-lunar-text {
font-size: 12px;
color: #333;
}
.uni-calendar-item__weeks-box-item {
position: relative;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
justify-content: center;
align-items: center;
width: 40px;
height: 40px;
/* #ifdef H5 */
cursor: pointer;
/* #endif */
}
.uni-calendar-item__weeks-box-circle {
position: absolute;
top: 5px;
right: 5px;
width: 8px;
height: 8px;
border-radius: 8px;
background-color: #dd524d;
}
.uni-calendar-item__weeks-box .uni-calendar-item--disable {
cursor: default;
}
.uni-calendar-item--disable .uni-calendar-item__weeks-box-text-disable {
color: #D1D1D1;
}
.uni-calendar-item--isDay {
position: absolute;
top: 10px;
right: 17%;
background-color: #dd524d;
width:6px;
height: 6px;
border-radius: 50%;
}
.uni-calendar-item--extra {
color: #dd524d;
opacity: 0.8;
}
.uni-calendar-item__weeks-box .uni-calendar-item--checked {
background-color: $uni-primary;
border-radius: 50%;
box-sizing: border-box;
border: 3px solid #fff;
}
.uni-calendar-item--checked .uni-calendar-item--checked-text {
color: #fff;
}
.uni-calendar-item--multiple .uni-calendar-item--checked-range-text {
color: #333;
}
.uni-calendar-item--multiple {
background-color: #F6F7FC;
// color: #fff;
}
.uni-calendar-item--multiple .uni-calendar-item--before-checked,
.uni-calendar-item--multiple .uni-calendar-item--after-checked {
background-color: $uni-primary;
border-radius: 50%;
box-sizing: border-box;
border: 3px solid #F6F7FC;
}
.uni-calendar-item--before-checked .uni-calendar-item--checked-text,
.uni-calendar-item--after-checked .uni-calendar-item--checked-text {
color: #fff;
}
.uni-calendar-item--before-checked-x {
border-top-left-radius: 50px;
border-bottom-left-radius: 50px;
box-sizing: border-box;
background-color: #F6F7FC;
}
.uni-calendar-item--after-checked-x {
border-top-right-radius: 50px;
border-bottom-right-radius: 50px;
background-color: #F6F7FC;
}
</style>
<template>
<view class="uni-calendar" @mouseleave="leaveCale">
<view v-if="!insert&&show" class="uni-calendar__mask" :class="{'uni-calendar--mask-show':aniMaskShow}"
@click="clean();maskClick()"></view>
<view v-if="insert || show" class="uni-calendar__content"
:class="{'uni-calendar--fixed':!insert,'uni-calendar--ani-show':aniMaskShow, 'uni-calendar__content-mobile': aniMaskShow}">
<view class="uni-calendar__header" :class="{'uni-calendar__header-mobile' :!insert}">
<view v-if="left" 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||'') + yearText + ( nowDate.month||'') + monthText}}</text>
</picker>
<view v-if="right" class="uni-calendar__header-btn-box" @click.stop="next">
<view class="uni-calendar__header-btn uni-calendar--right"></view>
</view>
<view v-if="!insert" class="dialog-close" @click="clean">
<view class="dialog-close-plus" data-id="close"></view>
<view class="dialog-close-plus dialog-close-rotate" data-id="close"></view>
</view>
</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" style="padding-bottom: 7px;">
<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" :checkHover="range" @change="choiceDate"
@handleMouse="handleMouse">
</calendar-item>
</view>
</view>
</view>
<view v-if="!insert && !range && typeHasTime" class="uni-date-changed uni-calendar--fixed-top"
style="padding: 0 80px;">
<view class="uni-date-changed--time-date">{{tempSingleDate ? tempSingleDate : selectDateText}}</view>
<time-picker type="time" :start="reactStartTime" :end="reactEndTime" v-model="time"
:disabled="!tempSingleDate" :border="false" :hide-second="hideSecond" class="time-picker-style">
</time-picker>
</view>
<view v-if="!insert && range && typeHasTime" class="uni-date-changed uni-calendar--fixed-top">
<view class="uni-date-changed--time-start">
<view class="uni-date-changed--time-date">{{tempRange.before ? tempRange.before : startDateText}}
</view>
<time-picker type="time" :start="reactStartTime" v-model="timeRange.startTime" :border="false"
:hide-second="hideSecond" :disabled="!tempRange.before" class="time-picker-style">
</time-picker>
</view>
<view style="line-height: 50px;">
<uni-icons type="arrowthinright" color="#999"></uni-icons>
</view>
<view class="uni-date-changed--time-end">
<view class="uni-date-changed--time-date">{{tempRange.after ? tempRange.after : endDateText}}</view>
<time-picker type="time" :end="reactEndTime" v-model="timeRange.endTime" :border="false"
:hide-second="hideSecond" :disabled="!tempRange.after" class="time-picker-style">
</time-picker>
</view>
</view>
<view v-if="!insert" class="uni-date-changed uni-date-btn--ok">
<view class="uni-datetime-picker--btn" @click="confirm">{{confirmText}}</view>
</view>
</view>
</view>
</template>
<script>
import Calendar from './util.js';
import calendarItem from './calendar-item.vue'
import timePicker from './time-picker.vue'
import { initVueI18n } from '@dcloudio/uni-i18n'
import i18nMessages from './i18n/index.js'
const { t } = initVueI18n(i18nMessages)
/**
* 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,
timePicker
},
props: {
date: {
type: String,
default: ''
},
defTime: {
type: [String, Object],
default: ''
},
selectableTimes: {
type: [Object],
default () {
return {}
}
},
selected: {
type: Array,
default () {
return []
}
},
lunar: {
type: Boolean,
default: false
},
startDate: {
type: String,
default: ''
},
endDate: {
type: String,
default: ''
},
startPlaceholder: {
type: String,
default: ''
},
endPlaceholder: {
type: String,
default: ''
},
range: {
type: Boolean,
default: false
},
typeHasTime: {
type: Boolean,
default: false
},
insert: {
type: Boolean,
default: true
},
showMonth: {
type: Boolean,
default: true
},
clearDate: {
type: Boolean,
default: true
},
left: {
type: Boolean,
default: true
},
right: {
type: Boolean,
default: true
},
checkHover: {
type: Boolean,
default: true
},
hideSecond: {
type: [Boolean],
default: false
},
pleStatus: {
type: Object,
default () {
return {
before: '',
after: '',
data: [],
fulldate: ''
}
}
}
},
data() {
return {
show: false,
weeks: [],
calendar: {},
nowDate: '',
aniMaskShow: false,
firstEnter: true,
time: '',
timeRange: {
startTime: '',
endTime: ''
},
tempSingleDate: '',
tempRange: {
before: '',
after: ''
}
}
},
watch: {
date: {
immediate: true,
handler(newVal) {
if (!this.range) {
this.tempSingleDate = newVal
setTimeout(() => {
this.init(newVal)
}, 100)
}
}
},
defTime: {
immediate: true,
handler(newVal) {
if (!this.range) {
this.time = newVal
} else {
this.timeRange.startTime = newVal.start
this.timeRange.endTime = newVal.end
}
}
},
startDate(val) {
// 字节小程序 watch 早于 created
if(!this.cale){
return
}
this.cale.resetSatrtDate(val)
this.cale.setDate(this.nowDate.fullDate)
this.weeks = this.cale.weeks
},
endDate(val) {
// 字节小程序 watch 早于 created
if(!this.cale){
return
}
this.cale.resetEndDate(val)
this.cale.setDate(this.nowDate.fullDate)
this.weeks = this.cale.weeks
},
selected(newVal) {
// 字节小程序 watch 早于 created
if(!this.cale){
return
}
this.cale.setSelectInfo(this.nowDate.fullDate, newVal)
this.weeks = this.cale.weeks
},
pleStatus: {
immediate: true,
handler(newVal) {
const {
before,
after,
fulldate,
which
} = newVal
this.tempRange.before = before
this.tempRange.after = after
setTimeout(() => {
if (fulldate) {
this.cale.setHoverMultiple(fulldate)
if (before && after) {
this.cale.lastHover = true
if (this.rangeWithinMonth(after, before)) return
this.setDate(before)
} else {
this.cale.setMultiple(fulldate)
this.setDate(this.nowDate.fullDate)
this.calendar.fullDate = ''
this.cale.lastHover = false
}
} else {
// 字节小程序 watch 早于 created
if(!this.cale){
return
}
this.cale.setDefaultMultiple(before, after)
if (which === 'left') {
this.setDate(before)
this.weeks = this.cale.weeks
} else {
this.setDate(after)
this.weeks = this.cale.weeks
}
this.cale.lastHover = true
}
}, 16)
}
}
},
computed: {
reactStartTime() {
const activeDate = this.range ? this.tempRange.before : this.calendar.fullDate
const res = activeDate === this.startDate ? this.selectableTimes.start : ''
return res
},
reactEndTime() {
const activeDate = this.range ? this.tempRange.after : this.calendar.fullDate
const res = activeDate === this.endDate ? this.selectableTimes.end : ''
return res
},
/**
* for i18n
*/
selectDateText() {
return t("uni-datetime-picker.selectDate")
},
startDateText() {
return this.startPlaceholder || t("uni-datetime-picker.startDate")
},
endDateText() {
return this.endPlaceholder || t("uni-datetime-picker.endDate")
},
okText() {
return t("uni-datetime-picker.ok")
},
yearText() {
return t("uni-datetime-picker.year")
},
monthText() {
return t("uni-datetime-picker.month")
},
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")
},
confirmText() {
return t("uni-calender.confirm")
},
},
created() {
// 获取日历方法实例
this.cale = new Calendar({
selected: this.selected,
startDate: this.startDate,
endDate: this.endDate,
range: this.range,
})
// 选中某一天
this.init(this.date)
},
methods: {
leaveCale() {
this.firstEnter = true
},
handleMouse(weeks) {
if (weeks.disable) return
if (this.cale.lastHover) return
let {
before,
after
} = this.cale.multipleStatus
if (!before) return
this.calendar = weeks
// 设置范围选
this.cale.setHoverMultiple(this.calendar.fullDate)
this.weeks = this.cale.weeks
// hover时,进入一个日历,更新另一个
if (this.firstEnter) {
this.$emit('firstEnterCale', this.cale.multipleStatus)
this.firstEnter = false
}
},
rangeWithinMonth(A, B) {
const [yearA, monthA] = A.split('-')
const [yearB, monthB] = B.split('-')
return yearA === yearB && monthA === monthB
},
// 取消穿透
clean() {
this.close()
},
// 蒙版点击事件
maskClick() {
this.$emit('maskClose')
},
clearCalender() {
if (this.range) {
this.timeRange.startTime = ''
this.timeRange.endTime = ''
this.tempRange.before = ''
this.tempRange.after = ''
this.cale.multipleStatus.before = ''
this.cale.multipleStatus.after = ''
this.cale.multipleStatus.data = []
this.cale.lastHover = false
} else {
this.time = ''
this.tempSingleDate = ''
}
this.calendar.fullDate = ''
this.setDate()
},
bindDateChange(e) {
const value = e.detail.value + '-1'
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,
time: this.time,
timeRange: this.timeRange,
fulldate: fullDate,
lunar,
extraInfo: extraInfo || {}
})
},
/**
* 选择天触发
* @param {Object} weeks
*/
choiceDate(weeks) {
if (weeks.disable) return
this.calendar = weeks
this.calendar.userChecked = true
// 设置多选
this.cale.setMultiple(this.calendar.fullDate, true)
this.weeks = this.cale.weeks
this.tempSingleDate = this.calendar.fullDate
const beforeDate = new Date(this.cale.multipleStatus.before).getTime()
const afterDate = new Date(this.cale.multipleStatus.after).getTime()
if (beforeDate > afterDate && afterDate) {
this.tempRange.before = this.cale.multipleStatus.after
this.tempRange.after = this.cale.multipleStatus.before
} else {
this.tempRange.before = this.cale.multipleStatus.before
this.tempRange.after = this.cale.multipleStatus.after
}
this.change()
},
/**
* 回到今天
*/
backtoday() {
let date = this.cale.getDate(new Date()).fullDate
// this.cale.setDate(date)
this.init(date)
this.change()
},
/**
* 比较时间大小
*/
dateCompare(startDate, endDate) {
// 计算截止时间
startDate = new Date(startDate.replace('-', '/').replace('-', '/'))
// 计算详细项的截止时间
endDate = new Date(endDate.replace('-', '/').replace('-', '/'))
if (startDate <= endDate) {
return true
} else {
return false
}
},
/**
* 上个月
*/
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" >
$uni-primary: #007aff !default;
.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: rgba(0, 0, 0, 0.4);
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__content-mobile {
border-top-left-radius: 10px;
border-top-right-radius: 10px;
box-shadow: 0px 0px 5px 3px rgba(0, 0, 0, 0.1);
}
.uni-calendar__header {
position: relative;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
justify-content: center;
align-items: center;
height: 50px;
}
.uni-calendar__header-mobile {
padding: 10px;
padding-bottom: 0;
}
.uni-calendar--fixed-top {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
justify-content: space-between;
border-top-color: rgba(0, 0, 0, 0.4);
border-top-style: solid;
border-top-width: 1px;
}
.uni-calendar--fixed-width {
width: 50px;
}
.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: #fff;
background-color: #f1f1f1;
}
.uni-calendar__header-text {
text-align: center;
width: 100px;
font-size: 15px;
color: #666;
}
.uni-calendar__button-text {
text-align: center;
width: 100px;
font-size: 14px;
color: $uni-primary;
/* #ifndef APP-NVUE */
letter-spacing: 3px;
/* #endif */
}
.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: 9px;
height: 9px;
border-left-color: #808080;
border-left-style: solid;
border-left-width: 1px;
border-top-color: #555555;
border-top-style: solid;
border-top-width: 1px;
}
.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: 40px;
border-bottom-color: #F5F5F5;
border-bottom-style: solid;
border-bottom-width: 1px;
}
.uni-calendar__weeks-day-text {
font-size: 12px;
color: #B2B2B2;
}
.uni-calendar__box {
position: relative;
// padding: 0 10px;
padding-bottom: 7px;
}
.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: #999;
opacity: 0.1;
text-align: center;
/* #ifndef APP-NVUE */
line-height: 1;
/* #endif */
}
.uni-date-changed {
padding: 0 10px;
// line-height: 50px;
text-align: center;
color: #333;
border-top-color: #DCDCDC;
;
border-top-style: solid;
border-top-width: 1px;
flex: 1;
}
.uni-date-btn--ok {
padding: 20px 15px;
}
.uni-date-changed--time-start {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
align-items: center;
}
.uni-date-changed--time-end {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
align-items: center;
}
.uni-date-changed--time-date {
color: #999;
line-height: 50px;
margin-right: 5px;
// opacity: 0.6;
}
.time-picker-style {
// width: 62px;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
justify-content: center;
align-items: center
}
.mr-10 {
margin-right: 10px;
}
.dialog-close {
position: absolute;
top: 0;
right: 0;
bottom: 0;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
align-items: center;
padding: 0 25px;
margin-top: 10px;
}
.dialog-close-plus {
width: 16px;
height: 2px;
background-color: #737987;
border-radius: 2px;
transform: rotate(45deg);
}
.dialog-close-rotate {
position: absolute;
transform: rotate(-45deg);
}
.uni-datetime-picker--btn {
border-radius: 100px;
height: 40px;
line-height: 40px;
background-color: $uni-primary;
color: #fff;
font-size: 16px;
letter-spacing: 2px;
}
/* #ifndef APP-NVUE */
.uni-datetime-picker--btn:active {
opacity: 0.7;
}
/* #endif */
</style>
{
"uni-datetime-picker.selectDate": "select date",
"uni-datetime-picker.selectTime": "select time",
"uni-datetime-picker.selectDateTime": "select datetime",
"uni-datetime-picker.startDate": "start date",
"uni-datetime-picker.endDate": "end date",
"uni-datetime-picker.startTime": "start time",
"uni-datetime-picker.endTime": "end time",
"uni-datetime-picker.ok": "ok",
"uni-datetime-picker.clear": "clear",
"uni-datetime-picker.cancel": "cancel",
"uni-datetime-picker.year": "-",
"uni-datetime-picker.month": "",
"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",
"uni-calender.confirm": "confirm"
}
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
}
{
"uni-datetime-picker.selectDate": "选择日期",
"uni-datetime-picker.selectTime": "选择时间",
"uni-datetime-picker.selectDateTime": "选择日期时间",
"uni-datetime-picker.startDate": "开始日期",
"uni-datetime-picker.endDate": "结束日期",
"uni-datetime-picker.startTime": "开始时间",
"uni-datetime-picker.endTime": "结束时间",
"uni-datetime-picker.ok": "确定",
"uni-datetime-picker.clear": "清除",
"uni-datetime-picker.cancel": "取消",
"uni-datetime-picker.year": "年",
"uni-datetime-picker.month": "月",
"uni-calender.SUN": "日",
"uni-calender.MON": "一",
"uni-calender.TUE": "二",
"uni-calender.WED": "三",
"uni-calender.THU": "四",
"uni-calender.FRI": "五",
"uni-calender.SAT": "六",
"uni-calender.confirm": "确认"
}
\ No newline at end of file
{
"uni-datetime-picker.selectDate": "選擇日期",
"uni-datetime-picker.selectTime": "選擇時間",
"uni-datetime-picker.selectDateTime": "選擇日期時間",
"uni-datetime-picker.startDate": "開始日期",
"uni-datetime-picker.endDate": "結束日期",
"uni-datetime-picker.startTime": "開始时间",
"uni-datetime-picker.endTime": "結束时间",
"uni-datetime-picker.ok": "確定",
"uni-datetime-picker.clear": "清除",
"uni-datetime-picker.cancel": "取消",
"uni-datetime-picker.year": "年",
"uni-datetime-picker.month": "月",
"uni-calender.SUN": "日",
"uni-calender.MON": "一",
"uni-calender.TUE": "二",
"uni-calender.WED": "三",
"uni-calender.THU": "四",
"uni-calender.FRI": "五",
"uni-calender.SAT": "六",
"uni-calender.confirm": "確認"
}
\ No newline at end of file
<template>
<view class="uni-datetime-picker">
<view @click="initTimePicker">
<slot>
<view class="uni-datetime-picker-timebox-pointer"
:class="{'uni-datetime-picker-disabled': disabled, 'uni-datetime-picker-timebox': border}">
<text class="uni-datetime-picker-text">{{time}}</text>
<view v-if="!time" class="uni-datetime-picker-time">
<text class="uni-datetime-picker-text">{{selectTimeText}}</text>
</view>
</view>
</slot>
</view>
<view v-if="visible" id="mask" class="uni-datetime-picker-mask" @click="tiggerTimePicker"></view>
<view v-if="visible" class="uni-datetime-picker-popup" :class="[dateShow && timeShow ? '' : 'fix-nvue-height']"
:style="fixNvueBug">
<view class="uni-title">
<text class="uni-datetime-picker-text">{{selectTimeText}}</text>
</view>
<view v-if="dateShow" class="uni-datetime-picker__container-box">
<picker-view class="uni-datetime-picker-view" :indicator-style="indicatorStyle" :value="ymd"
@change="bindDateChange">
<picker-view-column>
<view class="uni-datetime-picker-item" v-for="(item,index) in years" :key="index">
<text class="uni-datetime-picker-item">{{lessThanTen(item)}}</text>
</view>
</picker-view-column>
<picker-view-column>
<view class="uni-datetime-picker-item" v-for="(item,index) in months" :key="index">
<text class="uni-datetime-picker-item">{{lessThanTen(item)}}</text>
</view>
</picker-view-column>
<picker-view-column>
<view class="uni-datetime-picker-item" v-for="(item,index) in days" :key="index">
<text class="uni-datetime-picker-item">{{lessThanTen(item)}}</text>
</view>
</picker-view-column>
</picker-view>
<!-- 兼容 nvue 不支持伪类 -->
<text class="uni-datetime-picker-sign sign-left">-</text>
<text class="uni-datetime-picker-sign sign-right">-</text>
</view>
<view v-if="timeShow" class="uni-datetime-picker__container-box">
<picker-view class="uni-datetime-picker-view" :class="[hideSecond ? 'time-hide-second' : '']"
:indicator-style="indicatorStyle" :value="hms" @change="bindTimeChange">
<picker-view-column>
<view class="uni-datetime-picker-item" v-for="(item,index) in hours" :key="index">
<text class="uni-datetime-picker-item">{{lessThanTen(item)}}</text>
</view>
</picker-view-column>
<picker-view-column>
<view class="uni-datetime-picker-item" v-for="(item,index) in minutes" :key="index">
<text class="uni-datetime-picker-item">{{lessThanTen(item)}}</text>
</view>
</picker-view-column>
<picker-view-column v-if="!hideSecond">
<view class="uni-datetime-picker-item" v-for="(item,index) in seconds" :key="index">
<text class="uni-datetime-picker-item">{{lessThanTen(item)}}</text>
</view>
</picker-view-column>
</picker-view>
<!-- 兼容 nvue 不支持伪类 -->
<text class="uni-datetime-picker-sign" :class="[hideSecond ? 'sign-center' : 'sign-left']">:</text>
<text v-if="!hideSecond" class="uni-datetime-picker-sign sign-right">:</text>
</view>
<view class="uni-datetime-picker-btn">
<view @click="clearTime">
<text class="uni-datetime-picker-btn-text">{{clearText}}</text>
</view>
<view class="uni-datetime-picker-btn-group">
<view class="uni-datetime-picker-cancel" @click="tiggerTimePicker">
<text class="uni-datetime-picker-btn-text">{{cancelText}}</text>
</view>
<view @click="setTime">
<text class="uni-datetime-picker-btn-text">{{okText}}</text>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
import { initVueI18n } from '@dcloudio/uni-i18n'
import i18nMessages from './i18n/index.js'
const { t } = initVueI18n(i18nMessages)
/**
* DatetimePicker 时间选择器
* @description 可以同时选择日期和时间的选择器
* @tutorial https://ext.dcloud.net.cn/plugin?id=xxx
* @property {String} type = [datetime | date | time] 显示模式
* @property {Boolean} multiple = [true|false] 是否多选
* @property {String|Number} value 默认值
* @property {String|Number} start 起始日期或时间
* @property {String|Number} end 起始日期或时间
* @property {String} return-type = [timestamp | string]
* @event {Function} change 选中发生变化触发
*/
export default {
name: 'UniDatetimePicker',
data() {
return {
indicatorStyle: `height: 50px;`,
visible: false,
fixNvueBug: {},
dateShow: true,
timeShow: true,
title: '日期和时间',
// 输入框当前时间
time: '',
// 当前的年月日时分秒
year: 1920,
month: 0,
day: 0,
hour: 0,
minute: 0,
second: 0,
// 起始时间
startYear: 1920,
startMonth: 1,
startDay: 1,
startHour: 0,
startMinute: 0,
startSecond: 0,
// 结束时间
endYear: 2120,
endMonth: 12,
endDay: 31,
endHour: 23,
endMinute: 59,
endSecond: 59,
}
},
props: {
type: {
type: String,
default: 'datetime'
},
value: {
type: [String, Number],
default: ''
},
modelValue: {
type: [String, Number],
default: ''
},
start: {
type: [Number, String],
default: ''
},
end: {
type: [Number, String],
default: ''
},
returnType: {
type: String,
default: 'string'
},
disabled: {
type: [Boolean, String],
default: false
},
border: {
type: [Boolean, String],
default: true
},
hideSecond: {
type: [Boolean, String],
default: false
}
},
watch: {
// #ifndef VUE3
value: {
handler(newVal) {
if (newVal) {
this.parseValue(this.fixIosDateFormat(newVal)) //兼容 iOS、safari 日期格式
this.initTime(false)
} else {
this.time = ''
this.parseValue(Date.now())
}
},
immediate: true
},
// #endif
// #ifdef VUE3
modelValue: {
handler(newVal) {
if (newVal) {
this.parseValue(this.fixIosDateFormat(newVal)) //兼容 iOS、safari 日期格式
this.initTime(false)
} else {
this.time = ''
this.parseValue(Date.now())
}
},
immediate: true
},
// #endif
type: {
handler(newValue) {
if (newValue === 'date') {
this.dateShow = true
this.timeShow = false
this.title = '日期'
} else if (newValue === 'time') {
this.dateShow = false
this.timeShow = true
this.title = '时间'
} else {
this.dateShow = true
this.timeShow = true
this.title = '日期和时间'
}
},
immediate: true
},
start: {
handler(newVal) {
this.parseDatetimeRange(this.fixIosDateFormat(newVal), 'start') //兼容 iOS、safari 日期格式
},
immediate: true
},
end: {
handler(newVal) {
this.parseDatetimeRange(this.fixIosDateFormat(newVal), 'end') //兼容 iOS、safari 日期格式
},
immediate: true
},
// 月、日、时、分、秒可选范围变化后,检查当前值是否在范围内,不在则当前值重置为可选范围第一项
months(newVal) {
this.checkValue('month', this.month, newVal)
},
days(newVal) {
this.checkValue('day', this.day, newVal)
},
hours(newVal) {
this.checkValue('hour', this.hour, newVal)
},
minutes(newVal) {
this.checkValue('minute', this.minute, newVal)
},
seconds(newVal) {
this.checkValue('second', this.second, newVal)
}
},
computed: {
// 当前年、月、日、时、分、秒选择范围
years() {
return this.getCurrentRange('year')
},
months() {
return this.getCurrentRange('month')
},
days() {
return this.getCurrentRange('day')
},
hours() {
return this.getCurrentRange('hour')
},
minutes() {
return this.getCurrentRange('minute')
},
seconds() {
return this.getCurrentRange('second')
},
// picker 当前值数组
ymd() {
return [this.year - this.minYear, this.month - this.minMonth, this.day - this.minDay]
},
hms() {
return [this.hour - this.minHour, this.minute - this.minMinute, this.second - this.minSecond]
},
// 当前 date 是 start
currentDateIsStart() {
return this.year === this.startYear && this.month === this.startMonth && this.day === this.startDay
},
// 当前 date 是 end
currentDateIsEnd() {
return this.year === this.endYear && this.month === this.endMonth && this.day === this.endDay
},
// 当前年、月、日、时、分、秒的最小值和最大值
minYear() {
return this.startYear
},
maxYear() {
return this.endYear
},
minMonth() {
if (this.year === this.startYear) {
return this.startMonth
} else {
return 1
}
},
maxMonth() {
if (this.year === this.endYear) {
return this.endMonth
} else {
return 12
}
},
minDay() {
if (this.year === this.startYear && this.month === this.startMonth) {
return this.startDay
} else {
return 1
}
},
maxDay() {
if (this.year === this.endYear && this.month === this.endMonth) {
return this.endDay
} else {
return this.daysInMonth(this.year, this.month)
}
},
minHour() {
if (this.type === 'datetime') {
if (this.currentDateIsStart) {
return this.startHour
} else {
return 0
}
}
if (this.type === 'time') {
return this.startHour
}
},
maxHour() {
if (this.type === 'datetime') {
if (this.currentDateIsEnd) {
return this.endHour
} else {
return 23
}
}
if (this.type === 'time') {
return this.endHour
}
},
minMinute() {
if (this.type === 'datetime') {
if (this.currentDateIsStart && this.hour === this.startHour) {
return this.startMinute
} else {
return 0
}
}
if (this.type === 'time') {
if (this.hour === this.startHour) {
return this.startMinute
} else {
return 0
}
}
},
maxMinute() {
if (this.type === 'datetime') {
if (this.currentDateIsEnd && this.hour === this.endHour) {
return this.endMinute
} else {
return 59
}
}
if (this.type === 'time') {
if (this.hour === this.endHour) {
return this.endMinute
} else {
return 59
}
}
},
minSecond() {
if (this.type === 'datetime') {
if (this.currentDateIsStart && this.hour === this.startHour && this.minute === this.startMinute) {
return this.startSecond
} else {
return 0
}
}
if (this.type === 'time') {
if (this.hour === this.startHour && this.minute === this.startMinute) {
return this.startSecond
} else {
return 0
}
}
},
maxSecond() {
if (this.type === 'datetime') {
if (this.currentDateIsEnd && this.hour === this.endHour && this.minute === this.endMinute) {
return this.endSecond
} else {
return 59
}
}
if (this.type === 'time') {
if (this.hour === this.endHour && this.minute === this.endMinute) {
return this.endSecond
} else {
return 59
}
}
},
/**
* for i18n
*/
selectTimeText() {
return t("uni-datetime-picker.selectTime")
},
okText() {
return t("uni-datetime-picker.ok")
},
clearText() {
return t("uni-datetime-picker.clear")
},
cancelText() {
return t("uni-datetime-picker.cancel")
}
},
mounted() {
// #ifdef APP-NVUE
const res = uni.getSystemInfoSync();
this.fixNvueBug = {
top: res.windowHeight / 2,
left: res.windowWidth / 2
}
// #endif
},
methods: {
/**
* @param {Object} item
* 小于 10 在前面加个 0
*/
lessThanTen(item) {
return item < 10 ? '0' + item : item
},
/**
* 解析时分秒字符串,例如:00:00:00
* @param {String} timeString
*/
parseTimeType(timeString) {
if (timeString) {
let timeArr = timeString.split(':')
this.hour = Number(timeArr[0])
this.minute = Number(timeArr[1])
this.second = Number(timeArr[2])
}
},
/**
* 解析选择器初始值,类型可以是字符串、时间戳,例如:2000-10-02、'08:30:00'、 1610695109000
* @param {String | Number} datetime
*/
initPickerValue(datetime) {
let defaultValue = null
if (datetime) {
defaultValue = this.compareValueWithStartAndEnd(datetime, this.start, this.end)
} else {
defaultValue = Date.now()
defaultValue = this.compareValueWithStartAndEnd(defaultValue, this.start, this.end)
}
this.parseValue(defaultValue)
},
/**
* 初始值规则:
* - 用户设置初始值 value
* - 设置了起始时间 start、终止时间 end,并 start < value < end,初始值为 value, 否则初始值为 start
* - 只设置了起始时间 start,并 start < value,初始值为 value,否则初始值为 start
* - 只设置了终止时间 end,并 value < end,初始值为 value,否则初始值为 end
* - 无起始终止时间,则初始值为 value
* - 无初始值 value,则初始值为当前本地时间 Date.now()
* @param {Object} value
* @param {Object} dateBase
*/
compareValueWithStartAndEnd(value, start, end) {
let winner = null
value = this.superTimeStamp(value)
start = this.superTimeStamp(start)
end = this.superTimeStamp(end)
if (start && end) {
if (value < start) {
winner = new Date(start)
} else if (value > end) {
winner = new Date(end)
} else {
winner = new Date(value)
}
} else if (start && !end) {
winner = start <= value ? new Date(value) : new Date(start)
} else if (!start && end) {
winner = value <= end ? new Date(value) : new Date(end)
} else {
winner = new Date(value)
}
return winner
},
/**
* 转换为可比较的时间戳,接受日期、时分秒、时间戳
* @param {Object} value
*/
superTimeStamp(value) {
let dateBase = ''
if (this.type === 'time' && value && typeof value === 'string') {
const now = new Date()
const year = now.getFullYear()
const month = now.getMonth() + 1
const day = now.getDate()
dateBase = year + '/' + month + '/' + day + ' '
}
if (Number(value)) {
value = parseInt(value)
dateBase = 0
}
return this.createTimeStamp(dateBase + value)
},
/**
* 解析默认值 value,字符串、时间戳
* @param {Object} defaultTime
*/
parseValue(value) {
if (!value) {
return
}
if (this.type === 'time' && typeof value === "string") {
this.parseTimeType(value)
} else {
let defaultDate = null
defaultDate = new Date(value)
if (this.type !== 'time') {
this.year = defaultDate.getFullYear()
this.month = defaultDate.getMonth() + 1
this.day = defaultDate.getDate()
}
if (this.type !== 'date') {
this.hour = defaultDate.getHours()
this.minute = defaultDate.getMinutes()
this.second = defaultDate.getSeconds()
}
}
if (this.hideSecond) {
this.second = 0
}
},
/**
* 解析可选择时间范围 start、end,年月日字符串、时间戳
* @param {Object} defaultTime
*/
parseDatetimeRange(point, pointType) {
// 时间为空,则重置为初始值
if (!point) {
if (pointType === 'start') {
this.startYear = 1920
this.startMonth = 1
this.startDay = 1
this.startHour = 0
this.startMinute = 0
this.startSecond = 0
}
if (pointType === 'end') {
this.endYear = 2120
this.endMonth = 12
this.endDay = 31
this.endHour = 23
this.endMinute = 59
this.endSecond = 59
}
return
}
if (this.type === 'time') {
const pointArr = point.split(':')
this[pointType + 'Hour'] = Number(pointArr[0])
this[pointType + 'Minute'] = Number(pointArr[1])
this[pointType + 'Second'] = Number(pointArr[2])
} else {
if (!point) {
pointType === 'start' ? this.startYear = this.year - 60 : this.endYear = this.year + 60
return
}
if (Number(point)) {
point = parseInt(point)
}
// datetime 的 end 没有时分秒, 则不限制
const hasTime = /[0-9]:[0-9]/
if (this.type === 'datetime' && pointType === 'end' && typeof point === 'string' && !hasTime.test(
point)) {
point = point + ' 23:59:59'
}
const pointDate = new Date(point)
this[pointType + 'Year'] = pointDate.getFullYear()
this[pointType + 'Month'] = pointDate.getMonth() + 1
this[pointType + 'Day'] = pointDate.getDate()
if (this.type === 'datetime') {
this[pointType + 'Hour'] = pointDate.getHours()
this[pointType + 'Minute'] = pointDate.getMinutes()
this[pointType + 'Second'] = pointDate.getSeconds()
}
}
},
// 获取 年、月、日、时、分、秒 当前可选范围
getCurrentRange(value) {
const range = []
for (let i = this['min' + this.capitalize(value)]; i <= this['max' + this.capitalize(value)]; i++) {
range.push(i)
}
return range
},
// 字符串首字母大写
capitalize(str) {
return str.charAt(0).toUpperCase() + str.slice(1)
},
// 检查当前值是否在范围内,不在则当前值重置为可选范围第一项
checkValue(name, value, values) {
if (values.indexOf(value) === -1) {
this[name] = values[0]
}
},
// 每个月的实际天数
daysInMonth(year, month) { // Use 1 for January, 2 for February, etc.
return new Date(year, month, 0).getDate();
},
//兼容 iOS、safari 日期格式
fixIosDateFormat(value) {
if (typeof value === 'string') {
value = value.replace(/-/g, '/')
}
return value
},
/**
* 生成时间戳
* @param {Object} time
*/
createTimeStamp(time) {
if (!time) return
if (typeof time === "number") {
return time
} else {
time = time.replace(/-/g, '/')
if (this.type === 'date') {
time = time + ' ' + '00:00:00'
}
return Date.parse(time)
}
},
/**
* 生成日期或时间的字符串
*/
createDomSting() {
const yymmdd = this.year +
'-' +
this.lessThanTen(this.month) +
'-' +
this.lessThanTen(this.day)
let hhmmss = this.lessThanTen(this.hour) +
':' +
this.lessThanTen(this.minute)
if (!this.hideSecond) {
hhmmss = hhmmss + ':' + this.lessThanTen(this.second)
}
if (this.type === 'date') {
return yymmdd
} else if (this.type === 'time') {
return hhmmss
} else {
return yymmdd + ' ' + hhmmss
}
},
/**
* 初始化返回值,并抛出 change 事件
*/
initTime(emit = true) {
this.time = this.createDomSting()
if (!emit) return
if (this.returnType === 'timestamp' && this.type !== 'time') {
this.$emit('change', this.createTimeStamp(this.time))
this.$emit('input', this.createTimeStamp(this.time))
this.$emit('update:modelValue', this.createTimeStamp(this.time))
} else {
this.$emit('change', this.time)
this.$emit('input', this.time)
this.$emit('update:modelValue', this.time)
}
},
/**
* 用户选择日期或时间更新 data
* @param {Object} e
*/
bindDateChange(e) {
const val = e.detail.value
this.year = this.years[val[0]]
this.month = this.months[val[1]]
this.day = this.days[val[2]]
},
bindTimeChange(e) {
const val = e.detail.value
this.hour = this.hours[val[0]]
this.minute = this.minutes[val[1]]
this.second = this.seconds[val[2]]
},
/**
* 初始化弹出层
*/
initTimePicker() {
if (this.disabled) return
const value = this.fixIosDateFormat(this.time)
this.initPickerValue(value)
this.visible = !this.visible
},
/**
* 触发或关闭弹框
*/
tiggerTimePicker(e) {
this.visible = !this.visible
},
/**
* 用户点击“清空”按钮,清空当前值
*/
clearTime() {
this.time = ''
this.$emit('change', this.time)
this.$emit('input', this.time)
this.$emit('update:modelValue', this.time)
this.tiggerTimePicker()
},
/**
* 用户点击“确定”按钮
*/
setTime() {
this.initTime()
this.tiggerTimePicker()
}
}
}
</script>
<style lang="scss">
$uni-primary: #007aff !default;
.uni-datetime-picker {
/* #ifndef APP-NVUE */
/* width: 100%; */
/* #endif */
}
.uni-datetime-picker-view {
height: 130px;
width: 270px;
/* #ifndef APP-NVUE */
cursor: pointer;
/* #endif */
}
.uni-datetime-picker-item {
height: 50px;
line-height: 50px;
text-align: center;
font-size: 14px;
}
.uni-datetime-picker-btn {
margin-top: 60px;
/* #ifndef APP-NVUE */
display: flex;
cursor: pointer;
/* #endif */
flex-direction: row;
justify-content: space-between;
}
.uni-datetime-picker-btn-text {
font-size: 14px;
color: $uni-primary;
}
.uni-datetime-picker-btn-group {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
}
.uni-datetime-picker-cancel {
margin-right: 30px;
}
.uni-datetime-picker-mask {
position: fixed;
bottom: 0px;
top: 0px;
left: 0px;
right: 0px;
background-color: rgba(0, 0, 0, 0.4);
transition-duration: 0.3s;
z-index: 998;
}
.uni-datetime-picker-popup {
border-radius: 8px;
padding: 30px;
width: 270px;
/* #ifdef APP-NVUE */
height: 500px;
/* #endif */
/* #ifdef APP-NVUE */
width: 330px;
/* #endif */
background-color: #fff;
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
transition-duration: 0.3s;
z-index: 999;
}
.fix-nvue-height {
/* #ifdef APP-NVUE */
height: 330px;
/* #endif */
}
.uni-datetime-picker-time {
color: grey;
}
.uni-datetime-picker-column {
height: 50px;
}
.uni-datetime-picker-timebox {
border: 1px solid #E5E5E5;
border-radius: 5px;
padding: 7px 10px;
/* #ifndef APP-NVUE */
box-sizing: border-box;
cursor: pointer;
/* #endif */
}
.uni-datetime-picker-timebox-pointer {
/* #ifndef APP-NVUE */
cursor: pointer;
/* #endif */
}
.uni-datetime-picker-disabled {
opacity: 0.4;
/* #ifdef H5 */
cursor: not-allowed !important;
/* #endif */
}
.uni-datetime-picker-text {
font-size: 14px;
line-height: 50px
}
.uni-datetime-picker-sign {
position: absolute;
top: 53px;
/* 减掉 10px 的元素高度,兼容nvue */
color: #999;
/* #ifdef APP-NVUE */
font-size: 16px;
/* #endif */
}
.sign-left {
left: 86px;
}
.sign-right {
right: 86px;
}
.sign-center {
left: 135px;
}
.uni-datetime-picker__container-box {
position: relative;
display: flex;
align-items: center;
justify-content: center;
margin-top: 40px;
}
.time-hide-second {
width: 180px;
}
</style>
<template>
<view class="uni-date">
<view class="uni-date-editor" @click="show">
<slot>
<view
class="uni-date-editor--x"
:class="{'uni-date-editor--x__disabled': disabled,'uni-date-x--border': border}"
>
<view v-if="!isRange" class="uni-date-x uni-date-single">
<uni-icons class="icon-calendar" type="calendar" color="#c0c4cc" size="22"></uni-icons>
<input class="uni-date__x-input" type="text" v-model="displayValue"
:placeholder="singlePlaceholderText" :disabled="true" />
</view>
<view v-else class="uni-date-x uni-date-range">
<uni-icons class="icon-calendar" type="calendar" color="#c0c4cc" size="22"></uni-icons>
<input class="uni-date__x-input t-c" type="text" v-model="displayRangeValue.startDate"
:placeholder="startPlaceholderText" :disabled="true" />
<view class="range-separator">{{rangeSeparator}}</view>
<input class="uni-date__x-input t-c" type="text" v-model="displayRangeValue.endDate"
:placeholder="endPlaceholderText" :disabled="true" />
</view>
<view v-if="showClearIcon" class="uni-date__icon-clear" @click.stop="clear">
<uni-icons type="clear" color="#c0c4cc" size="22"></uni-icons>
</view>
</view>
</slot>
</view>
<view v-show="pickerVisible" class="uni-date-mask--pc" @click="close"></view>
<view v-if="!isPhone" v-show="pickerVisible" ref="datePicker" class="uni-date-picker__container">
<view v-if="!isRange" class="uni-date-single--x" :style="popover">
<view class="uni-popper__arrow"></view>
<view v-if="hasTime" class="uni-date-changed popup-x-header">
<input class="uni-date__input t-c" type="text" v-model="inputDate"
:placeholder="selectDateText" />
<time-picker type="time" v-model="pickerTime" :border="false" :disabled="!inputDate"
:start="reactStartTime" :end="reactEndTime" :hideSecond="hideSecond" style="width: 100%;">
<input class="uni-date__input t-c" type="text" v-model="pickerTime" :placeholder="selectTimeText"
:disabled="!inputDate" />
</time-picker>
</view>
<Calendar ref="pcSingle" :showMonth="false" :start-date="calendarRange.startDate"
:end-date="calendarRange.endDate" :date="calendarDate" @change="singleChange"
style="padding: 0 8px;" />
<view v-if="hasTime" class="popup-x-footer">
<text class="confirm" @click="confirmSingleChange">{{okText}}</text>
</view>
</view>
<view v-else class="uni-date-range--x" :style="popover">
<view class="uni-popper__arrow"></view>
<view v-if="hasTime" class="popup-x-header uni-date-changed">
<view class="popup-x-header--datetime">
<input class="uni-date__input uni-date-range__input" type="text" v-model="tempRange.startDate"
:placeholder="startDateText" />
<time-picker type="time" v-model="tempRange.startTime" :start="reactStartTime" :border="false"
:disabled="!tempRange.startDate" :hideSecond="hideSecond">
<input class="uni-date__input uni-date-range__input" type="text"
v-model="tempRange.startTime" :placeholder="startTimeText"
:disabled="!tempRange.startDate" />
</time-picker>
</view>
<uni-icons type="arrowthinright" color="#999" style="line-height: 40px;"></uni-icons>
<view class="popup-x-header--datetime">
<input class="uni-date__input uni-date-range__input" type="text" v-model="tempRange.endDate"
:placeholder="endDateText" />
<time-picker type="time" v-model="tempRange.endTime" :end="reactEndTime" :border="false"
:disabled="!tempRange.endDate" :hideSecond="hideSecond">
<input class="uni-date__input uni-date-range__input" type="text" v-model="tempRange.endTime"
:placeholder="endTimeText" :disabled="!tempRange.endDate" />
</time-picker>
</view>
</view>
<view class="popup-x-body">
<Calendar ref="left" :showMonth="false" :start-date="calendarRange.startDate"
:end-date="calendarRange.endDate" :range="true" @change="leftChange" :pleStatus="endMultipleStatus"
@firstEnterCale="updateRightCale" style="padding: 0 8px;" />
<Calendar ref="right" :showMonth="false" :start-date="calendarRange.startDate"
:end-date="calendarRange.endDate" :range="true" @change="rightChange"
:pleStatus="startMultipleStatus" @firstEnterCale="updateLeftCale"
style="padding: 0 8px;border-left: 1px solid #F1F1F1;" />
</view>
<view v-if="hasTime" class="popup-x-footer">
<text class="" @click="clear">{{clearText}}</text>
<text class="confirm" @click="confirmRangeChange">{{okText}}</text>
</view>
</view>
</view>
<Calendar v-if="isPhone" ref="mobile" :clearDate="false" :date="calendarDate" :defTime="reactMobDefTime"
:start-date="calendarRange.startDate" :end-date="calendarRange.endDate" :selectableTimes="mobSelectableTime"
:startPlaceholder="startPlaceholder" :endPlaceholder="endPlaceholder"
:pleStatus="endMultipleStatus" :showMonth="false" :range="isRange" :typeHasTime="hasTime" :insert="false"
:hideSecond="hideSecond" @confirm="mobileChange" @maskClose="close" />
</view>
</template>
<script>
/**
* DatetimePicker 时间选择器
* @description 同时支持 PC 和移动端使用日历选择日期和日期范围
* @tutorial https://ext.dcloud.net.cn/plugin?id=3962
* @property {String} type 选择器类型
* @property {String|Number|Array|Date} value 绑定值
* @property {String} placeholder 单选择时的占位内容
* @property {String} start 起始时间
* @property {String} end 终止时间
* @property {String} start-placeholder 范围选择时开始日期的占位内容
* @property {String} end-placeholder 范围选择时结束日期的占位内容
* @property {String} range-separator 选择范围时的分隔符
* @property {Boolean} border = [true|false] 是否有边框
* @property {Boolean} disabled = [true|false] 是否禁用
* @property {Boolean} clearIcon = [true|false] 是否显示清除按钮(仅PC端适用)
* @event {Function} change 确定日期时触发的事件
* @event {Function} maskClick 点击遮罩层触发的事件
* @event {Function} show 打开弹出层
* @event {Function} close 关闭弹出层
* @event {Function} clear 清除上次选中的状态和值
**/
import Calendar from './calendar.vue'
import TimePicker from './time-picker.vue'
import { initVueI18n } from '@dcloudio/uni-i18n'
import i18nMessages from './i18n/index.js'
export default {
name: 'UniDatetimePicker',
options: {
virtualHost: true
},
components: {
Calendar,
TimePicker
},
data() {
return {
isRange: false,
hasTime: false,
displayValue: '',
inputDate: '',
calendarDate: '',
pickerTime: '',
calendarRange: {
startDate: '',
startTime: '',
endDate: '',
endTime: ''
},
displayRangeValue: {
startDate: '',
endDate: '',
},
tempRange: {
startDate: '',
startTime: '',
endDate: '',
endTime: ''
},
// 左右日历同步数据
startMultipleStatus: {
before: '',
after: '',
data: [],
fulldate: ''
},
endMultipleStatus: {
before: '',
after: '',
data: [],
fulldate: ''
},
pickerVisible: false,
popover: null,
isEmitValue: false,
isPhone: false,
isFirstShow: true,
i18nT: () => {}
}
},
props: {
type: {
type: String,
default: 'datetime'
},
value: {
type: [String, Number, Array, Date],
default: ''
},
modelValue: {
type: [String, Number, Array, Date],
default: ''
},
start: {
type: [Number, String],
default: ''
},
end: {
type: [Number, String],
default: ''
},
returnType: {
type: String,
default: 'string'
},
placeholder: {
type: String,
default: ''
},
startPlaceholder: {
type: String,
default: ''
},
endPlaceholder: {
type: String,
default: ''
},
rangeSeparator: {
type: String,
default: '-'
},
border: {
type: [Boolean],
default: true
},
disabled: {
type: [Boolean],
default: false
},
clearIcon: {
type: [Boolean],
default: true
},
hideSecond: {
type: [Boolean],
default: false
}
},
watch: {
type: {
immediate: true,
handler(newVal) {
this.hasTime = newVal.indexOf('time') !== -1
this.isRange = newVal.indexOf('range') !== -1
}
},
// #ifndef VUE3
value: {
immediate: true,
handler(newVal) {
if (this.isEmitValue) {
this.isEmitValue = false
return
}
this.initPicker(newVal)
}
},
// #endif
// #ifdef VUE3
modelValue: {
immediate: true,
handler(newVal) {
if (this.isEmitValue) {
this.isEmitValue = false
return
}
this.initPicker(newVal)
}
},
// #endif
start: {
immediate: true,
handler(newVal) {
if (!newVal) return
const {
defDate,
defTime
} = this.parseDate(newVal)
this.calendarRange.startDate = defDate
if (this.hasTime) {
this.calendarRange.startTime = defTime
}
}
},
end: {
immediate: true,
handler(newVal) {
if (!newVal) return
const {
defDate,
defTime
} = this.parseDate(newVal)
this.calendarRange.endDate = defDate
if (this.hasTime) {
this.calendarRange.endTime = defTime
}
}
},
},
computed: {
reactStartTime() {
const activeDate = this.isRange ? this.tempRange.startDate : this.inputDate
const res = activeDate === this.calendarRange.startDate ? this.calendarRange.startTime : ''
return res
},
reactEndTime() {
const activeDate = this.isRange ? this.tempRange.endDate : this.inputDate
const res = activeDate === this.calendarRange.endDate ? this.calendarRange.endTime : ''
return res
},
reactMobDefTime() {
const times = {
start: this.tempRange.startTime,
end: this.tempRange.endTime
}
return this.isRange ? times : this.pickerTime
},
mobSelectableTime() {
return {
start: this.calendarRange.startTime,
end: this.calendarRange.endTime
}
},
datePopupWidth() {
// todo
return this.isRange ? 653 : 301
},
/**
* for i18n
*/
singlePlaceholderText() {
return this.placeholder || (this.type === 'date' ? this.selectDateText : this.selectDateTimeText)
},
startPlaceholderText() {
return this.startPlaceholder || this.startDateText
},
endPlaceholderText() {
return this.endPlaceholder || this.endDateText
},
selectDateText() {
return this.i18nT("uni-datetime-picker.selectDate")
},
selectDateTimeText() {
return this.i18nT("uni-datetime-picker.selectDateTime")
},
selectTimeText() {
return this.i18nT("uni-datetime-picker.selectTime")
},
startDateText() {
return this.startPlaceholder || this.i18nT("uni-datetime-picker.startDate")
},
startTimeText() {
return this.i18nT("uni-datetime-picker.startTime")
},
endDateText() {
return this.endPlaceholder || this.i18nT("uni-datetime-picker.endDate")
},
endTimeText() {
return this.i18nT("uni-datetime-picker.endTime")
},
okText() {
return this.i18nT("uni-datetime-picker.ok")
},
clearText() {
return this.i18nT("uni-datetime-picker.clear")
},
showClearIcon() {
return this.clearIcon && !this.disabled && (this.displayValue || (this.displayRangeValue.startDate && this.displayRangeValue.endDate))
}
},
created() {
this.initI18nT()
this.platform()
},
methods: {
initI18nT() {
const vueI18n = initVueI18n(i18nMessages)
this.i18nT = vueI18n.t
},
initPicker(newVal) {
if (!newVal || Array.isArray(newVal) && !newVal.length) {
this.$nextTick(() => {
this.clear(false)
})
return
}
if (!Array.isArray(newVal) && !this.isRange) {
const {
defDate,
defTime
} = this.parseDate(newVal)
this.displayValue = defDate
this.inputDate = defDate
this.calendarDate = defDate
if (this.hasTime) {
this.displayValue = defDate + ' ' + defTime
this.pickerTime = defTime
}
} else {
const [before, after] = newVal
if (!before && !after) return
const defBefore = this.parseDate(before)
const defAfter = this.parseDate(after)
const startDate = defBefore.defDate
const endDate = defAfter.defDate
this.displayRangeValue.startDate = this.tempRange.startDate = startDate
this.displayRangeValue.endDate = this.tempRange.endDate = endDate
if (this.hasTime) {
this.displayRangeValue.startDate = defBefore.defDate + ' ' + defBefore.defTime
this.displayRangeValue.endDate = defAfter.defDate + ' ' + defAfter.defTime
this.tempRange.startTime = defBefore.defTime
this.tempRange.endTime = defAfter.defTime
}
const defaultRange = {
before: defBefore.defDate,
after: defAfter.defDate
}
this.startMultipleStatus = Object.assign({}, this.startMultipleStatus, defaultRange, {
which: 'right'
})
this.endMultipleStatus = Object.assign({}, this.endMultipleStatus, defaultRange, {
which: 'left'
})
}
},
updateLeftCale(e) {
const left = this.$refs.left
// 设置范围选
left.cale.setHoverMultiple(e.after)
left.setDate(this.$refs.left.nowDate.fullDate)
},
updateRightCale(e) {
const right = this.$refs.right
// 设置范围选
right.cale.setHoverMultiple(e.after)
right.setDate(this.$refs.right.nowDate.fullDate)
},
platform() {
const { windowWidth } = uni.getSystemInfoSync()
this.isPhone = windowWidth <= 500
this.windowWidth = windowWidth
},
show() {
if (this.disabled) {
return
}
this.platform()
if (this.isPhone) {
this.$refs.mobile.open()
return
}
this.popover = {
top: '10px'
}
const dateEditor = uni.createSelectorQuery().in(this).select(".uni-date-editor")
dateEditor.boundingClientRect(rect => {
if (this.windowWidth - rect.left < this.datePopupWidth) {
this.popover.right = 0
}
}).exec()
setTimeout(() => {
this.pickerVisible = !this.pickerVisible
if (!this.isPhone && this.isRange && this.isFirstShow) {
this.isFirstShow = false
const {
startDate,
endDate
} = this.range
if (startDate && endDate) {
if (this.diffDate(startDate, endDate) < 30) {
this.$refs.right.next()
}
} else {
this.$refs.right.next()
this.$refs.right.cale.lastHover = false
}
}
}, 50)
},
close() {
setTimeout(() => {
this.pickerVisible = false
this.$emit('maskClick', this.value)
this.$refs.mobile && this.$refs.mobile.close()
}, 20)
},
setEmit(value) {
if (this.returnType === "timestamp" || this.returnType === "date") {
if (!Array.isArray(value)) {
if (!this.hasTime) {
value = value + ' ' + '00:00:00'
}
value = this.createTimestamp(value)
if (this.returnType === "date") {
value = new Date(value)
}
} else {
if (!this.hasTime) {
value[0] = value[0] + ' ' + '00:00:00'
value[1] = value[1] + ' ' + '00:00:00'
}
value[0] = this.createTimestamp(value[0])
value[1] = this.createTimestamp(value[1])
if (this.returnType === "date") {
value[0] = new Date(value[0])
value[1] = new Date(value[1])
}
}
}
this.$emit('change', value)
this.$emit('input', value)
this.$emit('update:modelValue', value)
this.isEmitValue = true
},
createTimestamp(date) {
date = this.fixIosDateFormat(date)
return Date.parse(new Date(date))
},
singleChange(e) {
this.calendarDate = this.inputDate = e.fulldate
if (this.hasTime) return
this.confirmSingleChange()
},
confirmSingleChange() {
if (!this.inputDate) {
this.pickerVisible = false
return
}
if(!this.checkDate(this.inputDate)){
this.inputDate = this.getDate(Date.now())
}
let startLaterInputDate = false
let startDate, startTime
if(this.start) {
let startString = this.start
if(typeof this.start === 'number'){
startString = this.getDateTime(this.start)
}
[startDate, startTime] = startString.split(' ')
if(this.start && !this.dateCompare(startDate, this.inputDate)) {
startLaterInputDate = true
this.inputDate = startDate
}
}
let endEarlierInputDate = false
let endDate, endTime
if(this.end) {
let endString = this.end
if(typeof this.end === 'number'){
endString = this.getDateTime(this.end)
}
[endDate, endTime] = endString.split(' ')
if(this.end && !this.dateCompare(this.inputDate, endDate)) {
endEarlierInputDate = true
this.inputDate = endDate
}
}
if (this.hasTime) {
if(startLaterInputDate){
this.pickerTime = startTime || this.getDefaultSecond()
}
if(endEarlierInputDate){
this.pickerTime = endTime || this.getDefaultSecond()
}
if(!this.pickerTime){
this.pickerTime = this.getTime(Date.now())
}
this.displayValue = `${this.inputDate} ${this.pickerTime}`
} else {
this.displayValue = this.inputDate
}
this.setEmit(this.displayValue)
this.pickerVisible = false
},
leftChange(e) {
const {
before,
after
} = e.range
this.rangeChange(before, after)
const obj = {
before: e.range.before,
after: e.range.after,
data: e.range.data,
fulldate: e.fulldate
}
this.startMultipleStatus = Object.assign({}, this.startMultipleStatus, obj)
},
rightChange(e) {
const {
before,
after
} = e.range
this.rangeChange(before, after)
const obj = {
before: e.range.before,
after: e.range.after,
data: e.range.data,
fulldate: e.fulldate
}
this.endMultipleStatus = Object.assign({}, this.endMultipleStatus, obj)
},
mobileChange(e) {
if (this.isRange) {
const {before, after} = e.range
if(!before || !after){
return
}
this.handleStartAndEnd(before, after, true)
if (this.hasTime) {
const {
startTime,
endTime
} = e.timeRange
this.tempRange.startTime = startTime
this.tempRange.endTime = endTime
}
this.confirmRangeChange()
} else {
if (this.hasTime) {
this.displayValue = e.fulldate + ' ' + e.time
} else {
this.displayValue = e.fulldate
}
this.setEmit(this.displayValue)
}
this.$refs.mobile.close()
},
rangeChange(before, after) {
if (!(before && after)) return
this.handleStartAndEnd(before, after, true)
if (this.hasTime) return
this.confirmRangeChange()
},
confirmRangeChange() {
if (!this.tempRange.startDate || !this.tempRange.endDate) {
this.pickerVisible = false
return
}
if(!this.checkDate(this.tempRange.startDate)){
this.tempRange.startDate = this.getDate(Date.now())
}
if(!this.checkDate(this.tempRange.endDate)){
this.tempRange.endDate = this.getDate(Date.now())
}
let start, end
let startDateLaterRangeStartDate = false
let startDateLaterRangeEndDate = false
let startDate, startTime
if(this.start) {
let startString = this.start
if(typeof this.start === 'number'){
startString = this.getDateTime(this.start)
}
[startDate,startTime] = startString.split(' ')
if(this.start && !this.dateCompare(this.start, this.tempRange.startDate)) {
startDateLaterRangeStartDate = true
this.tempRange.startDate = startDate
}
if(this.start && !this.dateCompare(this.start, this.tempRange.endDate)) {
startDateLaterRangeEndDate = true
this.tempRange.endDate = startDate
}
}
let endDateEarlierRangeStartDate = false
let endDateEarlierRangeEndDate = false
let endDate, endTime
if(this.end) {
let endString = this.end
if(typeof this.end === 'number'){
endString = this.getDateTime(this.end)
}
[endDate,endTime] = endString.split(' ')
if(this.end && !this.dateCompare(this.tempRange.startDate, this.end)) {
endDateEarlierRangeStartDate = true
this.tempRange.startDate = endDate
}
if(this.end && !this.dateCompare(this.tempRange.endDate, this.end)) {
endDateEarlierRangeEndDate = true
this.tempRange.endDate = endDate
}
}
if (!this.hasTime) {
start = this.displayRangeValue.startDate = this.tempRange.startDate
end = this.displayRangeValue.endDate = this.tempRange.endDate
} else {
if(startDateLaterRangeStartDate){
this.tempRange.startTime = startTime || this.getDefaultSecond()
}else if(endDateEarlierRangeStartDate){
this.tempRange.startTime = endTime || this.getDefaultSecond()
}
if(!this.tempRange.startTime){
this.tempRange.startTime = this.getTime(Date.now())
}
if(startDateLaterRangeEndDate){
this.tempRange.endTime = startTime || this.getDefaultSecond()
}else if(endDateEarlierRangeEndDate){
this.tempRange.endTime = endTime || this.getDefaultSecond()
}
if(!this.tempRange.endTime){
this.tempRange.endTime = this.getTime(Date.now())
}
start = this.displayRangeValue.startDate = `${this.tempRange.startDate} ${this.tempRange.startTime}`
end = this.displayRangeValue.endDate = `${this.tempRange.endDate} ${this.tempRange.endTime}`
}
if(!this.dateCompare(start,end)){
[start, end] = [end, start]
}
this.displayRangeValue.startDate = start
this.displayRangeValue.endDate = end
const displayRange = [start, end]
this.setEmit(displayRange)
this.pickerVisible = false
},
handleStartAndEnd(before, after, temp = false) {
if (!(before && after)) return
const type = temp ? 'tempRange' : 'range'
const isStartEarlierEnd = this.dateCompare(before, after)
this[type].startDate = isStartEarlierEnd ? before : after
this[type].endDate = isStartEarlierEnd ? after : before
},
/**
* 比较时间大小
*/
dateCompare(startDate, endDate) {
// 计算截止时间
startDate = new Date(startDate.replace('-', '/').replace('-', '/'))
// 计算详细项的截止时间
endDate = new Date(endDate.replace('-', '/').replace('-', '/'))
return startDate <= endDate
},
/**
* 比较时间差
*/
diffDate(startDate, endDate) {
// 计算截止时间
startDate = new Date(startDate.replace('-', '/').replace('-', '/'))
// 计算详细项的截止时间
endDate = new Date(endDate.replace('-', '/').replace('-', '/'))
const diff = (endDate - startDate) / (24 * 60 * 60 * 1000)
return Math.abs(diff)
},
clear(needEmit = true) {
if (!this.isRange) {
this.displayValue = ''
this.inputDate = ''
this.pickerTime = ''
if (this.isPhone) {
this.$refs.mobile && this.$refs.mobile.clearCalender()
} else {
this.$refs.pcSingle && this.$refs.pcSingle.clearCalender()
}
if (needEmit) {
this.$emit('change', '')
this.$emit('input', '')
this.$emit('update:modelValue', '')
}
} else {
this.displayRangeValue.startDate = ''
this.displayRangeValue.endDate = ''
this.tempRange.startDate = ''
this.tempRange.startTime = ''
this.tempRange.endDate = ''
this.tempRange.endTime = ''
if (this.isPhone) {
this.$refs.mobile && this.$refs.mobile.clearCalender()
} else {
this.$refs.left && this.$refs.left.clearCalender()
this.$refs.right && this.$refs.right.clearCalender()
this.$refs.right && this.$refs.right.next()
}
if (needEmit) {
this.$emit('change', [])
this.$emit('input', [])
this.$emit('update:modelValue', [])
}
}
},
parseDate(date) {
date = this.fixIosDateFormat(date)
const defVal = new Date(date)
const year = defVal.getFullYear()
const month = defVal.getMonth() + 1
const day = defVal.getDate()
const hour = defVal.getHours()
const minute = defVal.getMinutes()
const second = defVal.getSeconds()
const defDate = year + '-' + this.lessTen(month) + '-' + this.lessTen(day)
const defTime = this.lessTen(hour) + ':' + this.lessTen(minute) + (this.hideSecond ? '' : (':' + this
.lessTen(second)))
return {
defDate,
defTime
}
},
lessTen(item) {
return item < 10 ? '0' + item : item
},
//兼容 iOS、safari 日期格式
fixIosDateFormat(value) {
// #ifndef MP
if (typeof value === 'string') {
value = value.replace(/-/g, '/')
}
// #endif
return value
},
checkDate(date){
const dateReg = /((19|20)\d{2})(-|\/)\d{1,2}(-|\/)\d{1,2}/g
return date.match(dateReg)
},
getDateTime(date){
return `${this.getDate(date)} ${this.getTime(date)}`
},
getDate(date){
date = new Date(date)
const year = date.getFullYear()
const month = date.getMonth()+1
const day = date.getDate()
return `${year}-${this.addZero(month)}-${this.addZero(day)}`
},
getTime(date){
date = new Date(date)
const hour = date.getHours()
const minute = date.getMinutes()
const second = date.getSeconds()
return this.hideSecond ? `${this.addZero(hour)}:${this.addZero(minute)}` : `${this.addZero(hour)}:${this.addZero(minute)}:${this.addZero(second)}`
},
addZero(num) {
if(num < 10){
num = `0${num}`
}
return num
},
getDefaultSecond() {
return this.hideSecond ? '00:00' : '00:00:00'
}
}
}
</script>
<style lang="scss">
$uni-primary: #007aff !default;
.uni-date {
width: 100%;
flex: 1;
}
.uni-date-x {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
border-radius: 4px;
background-color: #fff;
color: #666;
font-size: 14px;
flex: 1;
.icon-calendar{
padding-left: 3px;
}
.range-separator{
padding: 0 2px;
}
}
.uni-date-x--border {
box-sizing: border-box;
border-radius: 4px;
border: 1px solid #e5e5e5;
}
.uni-date-editor--x {
display: flex;
align-items: center;
position: relative;
}
.uni-date-editor--x .uni-date__icon-clear {
padding-right: 3px;
display: flex;
align-items: center;
/* #ifdef H5 */
cursor: pointer;
/* #endif */
}
.uni-date__x-input {
width: auto;
position: relative;
overflow: hidden;
flex: 1;
line-height: 1;
font-size: 14px;
height: 35px;
}
.t-c {
text-align: center;
}
.uni-date__input {
height: 40px;
width: 100%;
line-height: 40px;
font-size: 14px;
}
.uni-date-range__input {
text-align: center;
max-width: 142px;
}
.uni-date-picker__container {
position: relative;
}
.uni-date-mask--pc {
position: fixed;
bottom: 0px;
top: 0px;
left: 0px;
right: 0px;
background-color: rgba(0, 0, 0, 0);
transition-duration: 0.3s;
z-index: 996;
}
.uni-date-single--x {
background-color: #fff;
position: absolute;
top: 0;
z-index: 999;
border: 1px solid #EBEEF5;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
border-radius: 4px;
}
.uni-date-range--x {
background-color: #fff;
position: absolute;
top: 0;
z-index: 999;
border: 1px solid #EBEEF5;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
border-radius: 4px;
}
.uni-date-editor--x__disabled {
opacity: 0.4;
cursor: default;
}
.uni-date-editor--logo {
width: 16px;
height: 16px;
vertical-align: middle;
}
/* 添加时间 */
.popup-x-header {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
}
.popup-x-header--datetime {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
flex: 1;
}
.popup-x-body {
display: flex;
}
.popup-x-footer {
padding: 0 15px;
border-top-color: #F1F1F1;
border-top-style: solid;
border-top-width: 1px;
line-height: 40px;
text-align: right;
color: #666;
}
.popup-x-footer text:hover {
color: $uni-primary;
cursor: pointer;
opacity: 0.8;
}
.popup-x-footer .confirm {
margin-left: 20px;
color: $uni-primary;
}
.uni-date-changed {
text-align: center;
color: #333;
border-bottom-color: #F1F1F1;
border-bottom-style: solid;
border-bottom-width: 1px;
}
.uni-date-changed--time text {
height: 50px;
line-height: 50px;
}
.uni-date-changed .uni-date-changed--time {
flex: 1;
}
.uni-date-changed--time-date {
color: #333;
opacity: 0.6;
}
.mr-50 {
margin-right: 50px;
}
/* picker 弹出层通用的指示小三角, todo:扩展至上下左右方向定位 */
.uni-popper__arrow,
.uni-popper__arrow::after {
position: absolute;
display: block;
width: 0;
height: 0;
border: 6px solid transparent;
border-top-width: 0;
}
.uni-popper__arrow {
filter: drop-shadow(0 2px 12px rgba(0, 0, 0, 0.03));
top: -6px;
left: 10%;
margin-right: 3px;
border-bottom-color: #EBEEF5;
}
.uni-popper__arrow::after {
content: " ";
top: 1px;
margin-left: -6px;
border-bottom-color: #fff;
}
</style>
<template>
<view class="uni-date">
<view class="uni-date-editor" @click="show">
<slot>
<view class="uni-date-editor--x"
:class="{'uni-date-editor--x__disabled': disabled,'uni-date-x--border': border}">
<view v-if="!isRange" class="uni-date-x uni-date-single">
<uni-icons class="icon-calendar" type="calendar" color="#c0c4cc" size="22"></uni-icons>
<input class="uni-date__x-input" type="text" v-model="displayValue"
:placeholder="singlePlaceholderText" :disabled="true" />
</view>
<view v-else class="uni-date-x uni-date-range">
<uni-icons class="icon-calendar" type="calendar" color="#c0c4cc" size="22"></uni-icons>
<input class="uni-date__x-input t-c" type="text" v-model="range.startDate"
:placeholder="startPlaceholderText" :disabled="true" />
<view class="range-separator">{{rangeSeparator}}</view>
<input class="uni-date__x-input t-c" type="text" v-model="range.endDate"
:placeholder="endPlaceholderText" :disabled="true" />
</view>
<view v-if="showClearIcon" class="uni-date__icon-clear" @click.stop="clear">
<uni-icons type="clear" color="#c0c4cc" size="22"></uni-icons>
</view>
</view>
</slot>
</view>
<view v-show="pickerVisible" class="uni-date-mask--pc" @click="close"></view>
<view v-if="!isPhone" v-show="pickerVisible" ref="datePicker" class="uni-date-picker__container">
<view v-if="!isRange" class="uni-date-single--x" :style="popover">
<view class="uni-popper__arrow"></view>
<view v-if="hasTime" class="uni-date-changed popup-x-header">
<input class="uni-date__input t-c" type="text" v-model="inputDate" :placeholder="selectDateText" />
<time-picker type="time" v-model="pickerTime" :border="false" :disabled="!inputDate"
:start="reactStartTime" :end="reactEndTime" :hideSecond="hideSecond" style="width: 100%;">
<input class="uni-date__input t-c" type="text" v-model="pickerTime"
:placeholder="selectTimeText" :disabled="!inputDate" />
</time-picker>
</view>
<calendar ref="pcSingle" :showMonth="false" :start-date="caleRange.startDate"
:end-date="caleRange.endDate" :date="calendarDate" @change="singleChange" style="padding: 0 8px;" />
<view v-if="hasTime" class="popup-x-footer">
<text class="confirm" @click="confirmSingleChange">{{okText}}</text>
</view>
</view>
<view v-else class="uni-date-range--x" :style="popover">
<view class="uni-popper__arrow"></view>
<view v-if="hasTime" class="popup-x-header uni-date-changed">
<view class="popup-x-header--datetime">
<input class="uni-date__input uni-date-range__input" type="text" v-model="tempRange.startDate"
:placeholder="startDateText" />
<time-picker type="time" v-model="tempRange.startTime" :start="reactStartTime" :border="false"
:disabled="!tempRange.startDate" :hideSecond="hideSecond">
<input class="uni-date__input uni-date-range__input" type="text"
v-model="tempRange.startTime" :placeholder="startTimeText"
:disabled="!tempRange.startDate" />
</time-picker>
</view>
<uni-icons type="arrowthinright" color="#999" style="line-height: 40px;"></uni-icons>
<view class="popup-x-header--datetime">
<input class="uni-date__input uni-date-range__input" type="text" v-model="tempRange.endDate"
:placeholder="endDateText" />
<time-picker type="time" v-model="tempRange.endTime" :end="reactEndTime" :border="false"
:disabled="!tempRange.endDate" :hideSecond="hideSecond">
<input class="uni-date__input uni-date-range__input" type="text" v-model="tempRange.endTime"
:placeholder="endTimeText" :disabled="!tempRange.endDate" />
</time-picker>
</view>
</view>
<view class="popup-x-body">
<calendar ref="left" :showMonth="false" :start-date="caleRange.startDate"
:end-date="caleRange.endDate" :range="true" @change="leftChange" :pleStatus="endMultipleStatus"
@firstEnterCale="updateRightCale" style="padding: 0 8px;" />
<calendar ref="right" :showMonth="false" :start-date="caleRange.startDate"
:end-date="caleRange.endDate" :range="true" @change="rightChange"
:pleStatus="startMultipleStatus" @firstEnterCale="updateLeftCale"
style="padding: 0 8px;border-left: 1px solid #F1F1F1;" />
</view>
<view v-if="hasTime" class="popup-x-footer">
<text class="" @click="clear">{{clearText}}</text>
<text class="confirm" @click="confirmRangeChange">{{okText}}</text>
</view>
</view>
</view>
<calendar v-if="isPhone" ref="mobile" :clearDate="false" :date="calendarDate" :defTime="reactMobDefTime"
:start-date="caleRange.startDate" :end-date="caleRange.endDate" :selectableTimes="mobSelectableTime"
:startPlaceholder="startPlaceholder" :endPlaceholder="endPlaceholder" :pleStatus="endMultipleStatus"
:showMonth="false" :range="isRange" :typeHasTime="hasTime" :insert="false" :hideSecond="hideSecond"
@confirm="mobileChange" @maskClose="close" />
</view>
</template>
<script>
/**
* DatetimePicker 时间选择器
* @description 同时支持 PC 和移动端使用日历选择日期和日期范围
* @tutorial https://ext.dcloud.net.cn/plugin?id=3962
* @property {String} type 选择器类型
* @property {String|Number|Array|Date} value 绑定值
* @property {String} placeholder 单选择时的占位内容
* @property {String} start 起始时间
* @property {String} end 终止时间
* @property {String} start-placeholder 范围选择时开始日期的占位内容
* @property {String} end-placeholder 范围选择时结束日期的占位内容
* @property {String} range-separator 选择范围时的分隔符
* @property {Boolean} border = [true|false] 是否有边框
* @property {Boolean} disabled = [true|false] 是否禁用
* @property {Boolean} clearIcon = [true|false] 是否显示清除按钮(仅PC端适用)
* @event {Function} change 确定日期时触发的事件
* @event {Function} maskClick 点击遮罩层触发的事件
* @event {Function} show 打开弹出层
* @event {Function} close 关闭弹出层
* @event {Function} clear 清除上次选中的状态和值
**/
import calendar from './calendar.vue'
import timePicker from './time-picker.vue'
import {
initVueI18n
} from '@dcloudio/uni-i18n'
import i18nMessages from './i18n/index.js'
export default {
name: 'UniDatetimePicker',
options: {
virtualHost: true
},
components: {
calendar,
timePicker
},
data() {
return {
isRange: false,
hasTime: false,
displayValue: '',
inputDate: '',
calendarDate: '',
pickerTime: '',
// 范围选
caleRange: {
startDate: '',
startTime: '',
endDate: '',
endTime: ''
},
range: {
startDate: '',
endDate: '',
},
tempRange: {
startDate: '',
startTime: '',
endDate: '',
endTime: ''
},
// 左右日历同步数据
startMultipleStatus: {
before: '',
after: '',
data: [],
fulldate: ''
},
endMultipleStatus: {
before: '',
after: '',
data: [],
fulldate: ''
},
pickerVisible: false,
popover: null,
isEmitValue: false,
isPhone: false,
isFirstShow: true,
i18nT: () => {}
}
},
props: {
type: {
type: String,
default: 'datetime'
},
value: {
type: [String, Number, Array, Date],
default: ''
},
modelValue: {
type: [String, Number, Array, Date],
default: ''
},
start: {
type: [Number, String],
default: ''
},
end: {
type: [Number, String],
default: ''
},
returnType: {
type: String,
default: 'string'
},
placeholder: {
type: String,
default: ''
},
startPlaceholder: {
type: String,
default: ''
},
endPlaceholder: {
type: String,
default: ''
},
rangeSeparator: {
type: String,
default: '-'
},
border: {
type: [Boolean],
default: true
},
disabled: {
type: [Boolean],
default: false
},
clearIcon: {
type: [Boolean],
default: false
},
hideSecond: {
type: [Boolean],
default: false
}
},
watch: {
type: {
immediate: true,
handler(newVal) {
this.hasTime = newVal.indexOf('time') !== -1
this.isRange = newVal.indexOf('range') !== -1
}
},
// #ifndef VUE3
value: {
immediate: true,
handler(newVal) {
if (this.isEmitValue) {
this.isEmitValue = false
return
}
this.initPicker(newVal)
}
},
// #endif
// #ifdef VUE3
modelValue: {
immediate: true,
handler(newVal) {
if (this.isEmitValue) {
this.isEmitValue = false
return
}
this.initPicker(newVal)
}
},
// #endif
start: {
immediate: true,
handler(newVal) {
if (!newVal) return
const {
defDate,
defTime
} = this.parseDate(newVal)
this.caleRange.startDate = defDate
if (this.hasTime) {
this.caleRange.startTime = defTime
}
}
},
end: {
immediate: true,
handler(newVal) {
if (!newVal) return
const {
defDate,
defTime
} = this.parseDate(newVal)
this.caleRange.endDate = defDate
if (this.hasTime) {
this.caleRange.endTime = defTime
}
}
},
},
computed: {
reactStartTime() {
const activeDate = this.isRange ? this.tempRange.startDate : this.inputDate
const res = activeDate === this.caleRange.startDate ? this.caleRange.startTime : ''
return res
},
reactEndTime() {
const activeDate = this.isRange ? this.tempRange.endDate : this.inputDate
const res = activeDate === this.caleRange.endDate ? this.caleRange.endTime : ''
return res
},
reactMobDefTime() {
const times = {
start: this.tempRange.startTime,
end: this.tempRange.endTime
}
return this.isRange ? times : this.pickerTime
},
mobSelectableTime() {
return {
start: this.caleRange.startTime,
end: this.caleRange.endTime
}
},
datePopupWidth() {
// todo
return this.isRange ? 653 : 301
},
/**
* for i18n
*/
singlePlaceholderText() {
return this.placeholder || (this.type === 'date' ? this.selectDateText : this.selectDateTimeText)
},
startPlaceholderText() {
return this.startPlaceholder || this.startDateText
},
endPlaceholderText() {
return this.endPlaceholder || this.endDateText
},
selectDateText() {
return this.i18nT("uni-datetime-picker.selectDate")
},
selectDateTimeText() {
return this.i18nT("uni-datetime-picker.selectDateTime")
},
selectTimeText() {
return this.i18nT("uni-datetime-picker.selectTime")
},
startDateText() {
return this.startPlaceholder || this.i18nT("uni-datetime-picker.startDate")
},
startTimeText() {
return this.i18nT("uni-datetime-picker.startTime")
},
endDateText() {
return this.endPlaceholder || this.i18nT("uni-datetime-picker.endDate")
},
endTimeText() {
return this.i18nT("uni-datetime-picker.endTime")
},
okText() {
return this.i18nT("uni-datetime-picker.ok")
},
clearText() {
return this.i18nT("uni-datetime-picker.clear")
},
showClearIcon() {
return this.clearIcon && !this.disabled && (this.displayValue || (this.range.startDate && this.range
.endDate))
}
},
created() {
this.initI18nT()
this.platform()
},
methods: {
initI18nT() {
const vueI18n = initVueI18n(i18nMessages)
this.i18nT = vueI18n.t
},
initPicker(newVal) {
if (!newVal || Array.isArray(newVal) && !newVal.length) {
this.$nextTick(() => {
this.clear(false)
})
return
}
if (!Array.isArray(newVal) && !this.isRange) {
const {
defDate,
defTime
} = this.parseDate(newVal)
this.displayValue = defDate
this.inputDate = defDate
this.calendarDate = defDate
if (this.hasTime) {
this.displayValue = defDate + ' ' + defTime
this.pickerTime = defTime
}
} else {
const [before, after] = newVal
if (!before && !after) return
const defBefore = this.parseDate(before)
const defAfter = this.parseDate(after)
const startDate = defBefore.defDate
const endDate = defAfter.defDate
this.range.startDate = this.tempRange.startDate = startDate
this.range.endDate = this.tempRange.endDate = endDate
if (this.hasTime) {
this.range.startDate = defBefore.defDate + ' ' + defBefore.defTime
this.range.endDate = defAfter.defDate + ' ' + defAfter.defTime
this.tempRange.startTime = defBefore.defTime
this.tempRange.endTime = defAfter.defTime
}
const defaultRange = {
before: defBefore.defDate,
after: defAfter.defDate
}
this.startMultipleStatus = Object.assign({}, this.startMultipleStatus, defaultRange, {
which: 'right'
})
this.endMultipleStatus = Object.assign({}, this.endMultipleStatus, defaultRange, {
which: 'left'
})
}
},
updateLeftCale(e) {
const left = this.$refs.left
// 设置范围选
left.cale.setHoverMultiple(e.after)
left.setDate(this.$refs.left.nowDate.fullDate)
},
updateRightCale(e) {
const right = this.$refs.right
// 设置范围选
right.cale.setHoverMultiple(e.after)
right.setDate(this.$refs.right.nowDate.fullDate)
},
platform() {
const {
windowWidth
} = uni.getSystemInfoSync()
this.isPhone = windowWidth <= 500
this.windowWidth = windowWidth
},
show() {
if (this.disabled) {
return
}
this.platform()
if (this.isPhone) {
this.$refs.mobile.open()
return
}
this.popover = {
top: '10px'
}
const dateEditor = uni.createSelectorQuery().in(this).select(".uni-date-editor")
dateEditor.boundingClientRect(rect => {
if (this.windowWidth - rect.left < this.datePopupWidth) {
this.popover.right = 0
}
}).exec()
setTimeout(() => {
this.pickerVisible = !this.pickerVisible
if (!this.isPhone && this.isRange && this.isFirstShow) {
this.isFirstShow = false
const {
startDate,
endDate
} = this.range
if (startDate && endDate) {
if (this.diffDate(startDate, endDate) < 30) {
this.$refs.right.next()
}
} else {
this.$refs.right.next()
this.$refs.right.cale.lastHover = false
}
}
}, 50)
},
close() {
setTimeout(() => {
this.pickerVisible = false
this.$emit('maskClick', this.value)
this.$refs.mobile && this.$refs.mobile.close()
}, 20)
},
setEmit(value) {
if (this.returnType === "timestamp" || this.returnType === "date") {
if (!Array.isArray(value)) {
if (!this.hasTime) {
value = value + ' ' + '00:00:00'
}
value = this.createTimestamp(value)
if (this.returnType === "date") {
value = new Date(value)
}
} else {
if (!this.hasTime) {
value[0] = value[0] + ' ' + '00:00:00'
value[1] = value[1] + ' ' + '00:00:00'
}
value[0] = this.createTimestamp(value[0])
value[1] = this.createTimestamp(value[1])
if (this.returnType === "date") {
value[0] = new Date(value[0])
value[1] = new Date(value[1])
}
}
}
this.$emit('change', value)
this.$emit('input', value)
this.$emit('update:modelValue', value)
this.isEmitValue = true
},
createTimestamp(date) {
date = this.fixIosDateFormat(date)
return Date.parse(new Date(date))
},
singleChange(e) {
this.calendarDate = this.inputDate = e.fulldate
if (this.hasTime) return
this.confirmSingleChange()
},
confirmSingleChange() {
if (!this.inputDate) {
this.pickerVisible = false
return
}
if (!this.checkDate(this.inputDate)) {
this.inputDate = this.getDate(Date.now())
}
let startLaterInputDate = false
let startDate, startTime
if (this.start) {
let startString = this.start
if (typeof this.start === 'number') {
startString = this.getDateTime(this.start)
}
[startDate, startTime] = startString.split(' ')
if (this.start && !this.dateCompare(startDate, this.inputDate)) {
startLaterInputDate = true
this.inputDate = startDate
}
}
let endEarlierInputDate = false
let endDate, endTime
if (this.end) {
let endString = this.end
if (typeof this.end === 'number') {
endString = this.getDateTime(this.end)
}
[endDate, endTime] = endString.split(' ')
if (this.end && !this.dateCompare(this.inputDate, endDate)) {
endEarlierInputDate = true
this.inputDate = endDate
}
}
if (this.hasTime) {
if (startLaterInputDate) {
this.pickerTime = startTime || this.getDefaultSecond()
}
if (endEarlierInputDate) {
this.pickerTime = endTime || this.getDefaultSecond()
}
if (!this.pickerTime) {
this.pickerTime = this.getTime(Date.now())
}
this.displayValue = `${this.inputDate} ${this.pickerTime}`
} else {
this.displayValue = this.inputDate
}
this.setEmit(this.displayValue)
this.pickerVisible = false
},
leftChange(e) {
const {
before,
after
} = e.range
this.rangeChange(before, after)
const obj = {
before: e.range.before,
after: e.range.after,
data: e.range.data,
fulldate: e.fulldate
}
this.startMultipleStatus = Object.assign({}, this.startMultipleStatus, obj)
},
rightChange(e) {
const {
before,
after
} = e.range
this.rangeChange(before, after)
const obj = {
before: e.range.before,
after: e.range.after,
data: e.range.data,
fulldate: e.fulldate
}
this.endMultipleStatus = Object.assign({}, this.endMultipleStatus, obj)
},
mobileChange(e) {
if (this.isRange) {
const {
before,
after
} = e.range
if (!before || !after) {
return
}
this.handleStartAndEnd(before, after, true)
if (this.hasTime) {
const {
startTime,
endTime
} = e.timeRange
this.tempRange.startTime = startTime
this.tempRange.endTime = endTime
}
this.confirmRangeChange()
} else {
if (this.hasTime) {
this.displayValue = e.fulldate + ' ' + e.time
} else {
this.displayValue = e.fulldate
}
this.setEmit(this.displayValue)
}
this.$refs.mobile.close()
},
rangeChange(before, after) {
if (!(before && after)) return
this.handleStartAndEnd(before, after, true)
if (this.hasTime) return
this.confirmRangeChange()
},
confirmRangeChange() {
if (!this.tempRange.startDate || !this.tempRange.endDate) {
this.pickerVisible = false
return
}
if (!this.checkDate(this.tempRange.startDate)) {
this.tempRange.startDate = this.getDate(Date.now())
}
if (!this.checkDate(this.tempRange.endDate)) {
this.tempRange.endDate = this.getDate(Date.now())
}
let start, end
let startDateLaterRangeStartDate = false
let startDateLaterRangeEndDate = false
let startDate, startTime
if (this.start) {
let startString = this.start
if (typeof this.start === 'number') {
startString = this.getDateTime(this.start)
}
[startDate, startTime] = startString.split(' ')
if (this.start && !this.dateCompare(this.start, this.tempRange.startDate)) {
startDateLaterRangeStartDate = true
this.tempRange.startDate = startDate
}
if (this.start && !this.dateCompare(this.start, this.tempRange.endDate)) {
startDateLaterRangeEndDate = true
this.tempRange.endDate = startDate
}
}
let endDateEarlierRangeStartDate = false
let endDateEarlierRangeEndDate = false
let endDate, endTime
if (this.end) {
let endString = this.end
if (typeof this.end === 'number') {
endString = this.getDateTime(this.end)
}
[endDate, endTime] = endString.split(' ')
if (this.end && !this.dateCompare(this.tempRange.startDate, this.end)) {
endDateEarlierRangeStartDate = true
this.tempRange.startDate = endDate
}
if (this.end && !this.dateCompare(this.tempRange.endDate, this.end)) {
endDateEarlierRangeEndDate = true
this.tempRange.endDate = endDate
}
}
if (!this.hasTime) {
start = this.range.startDate = this.tempRange.startDate
end = this.range.endDate = this.tempRange.endDate
} else {
if (startDateLaterRangeStartDate) {
this.tempRange.startTime = startTime || this.getDefaultSecond()
} else if (endDateEarlierRangeStartDate) {
this.tempRange.startTime = endTime || this.getDefaultSecond()
}
if (!this.tempRange.startTime) {
this.tempRange.startTime = this.getTime(Date.now())
}
if (startDateLaterRangeEndDate) {
this.tempRange.endTime = startTime || this.getDefaultSecond()
} else if (endDateEarlierRangeEndDate) {
this.tempRange.endTime = endTime || this.getDefaultSecond()
}
if (!this.tempRange.endTime) {
this.tempRange.endTime = this.getTime(Date.now())
}
start = this.range.startDate = `${this.tempRange.startDate} ${this.tempRange.startTime}`
end = this.range.endDate = `${this.tempRange.endDate} ${this.tempRange.endTime}`
}
if (!this.dateCompare(start, end)) {
[start, end] = [end, start]
}
this.range.startDate = start
this.range.endDate = end
const displayRange = [start, end]
this.setEmit(displayRange)
this.pickerVisible = false
},
handleStartAndEnd(before, after, temp = false) {
if (!(before && after)) return
const type = temp ? 'tempRange' : 'range'
const isStartEarlierEnd = this.dateCompare(before, after)
this[type].startDate = isStartEarlierEnd ? before : after
this[type].endDate = isStartEarlierEnd ? after : before
},
/**
* 比较时间大小
*/
dateCompare(startDate, endDate) {
// 计算截止时间
startDate = new Date(startDate.replace('-', '/').replace('-', '/'))
// 计算详细项的截止时间
endDate = new Date(endDate.replace('-', '/').replace('-', '/'))
return startDate <= endDate
},
/**
* 比较时间差
*/
diffDate(startDate, endDate) {
// 计算截止时间
startDate = new Date(startDate.replace('-', '/').replace('-', '/'))
// 计算详细项的截止时间
endDate = new Date(endDate.replace('-', '/').replace('-', '/'))
const diff = (endDate - startDate) / (24 * 60 * 60 * 1000)
return Math.abs(diff)
},
clear(needEmit = true) {
if (!this.isRange) {
this.displayValue = ''
this.inputDate = ''
this.pickerTime = ''
if (this.isPhone) {
this.$refs.mobile && this.$refs.mobile.clearCalender()
} else {
this.$refs.pcSingle && this.$refs.pcSingle.clearCalender()
}
if (needEmit) {
this.$emit('change', '')
this.$emit('input', '')
this.$emit('update:modelValue', '')
}
} else {
this.range.startDate = ''
this.range.endDate = ''
this.tempRange.startDate = ''
this.tempRange.startTime = ''
this.tempRange.endDate = ''
this.tempRange.endTime = ''
if (this.isPhone) {
this.$refs.mobile && this.$refs.mobile.clearCalender()
} else {
this.$refs.left && this.$refs.left.clearCalender()
this.$refs.right && this.$refs.right.clearCalender()
this.$refs.right && this.$refs.right.next()
}
if (needEmit) {
this.$emit('change', [])
this.$emit('input', [])
this.$emit('update:modelValue', [])
}
}
},
parseDate(date) {
date = this.fixIosDateFormat(date)
const defVal = new Date(date)
const year = defVal.getFullYear()
const month = defVal.getMonth() + 1
const day = defVal.getDate()
const hour = defVal.getHours()
const minute = defVal.getMinutes()
const second = defVal.getSeconds()
const defDate = year + '-' + this.lessTen(month) + '-' + this.lessTen(day)
const defTime = this.lessTen(hour) + ':' + this.lessTen(minute) + (this.hideSecond ? '' : (':' + this
.lessTen(second)))
return {
defDate,
defTime
}
},
lessTen(item) {
return item < 10 ? '0' + item : item
},
//兼容 iOS、safari 日期格式
fixIosDateFormat(value) {
// #ifndef MP
if (typeof value === 'string') {
value = value.replace(/-/g, '/')
}
// #endif
return value
},
checkDate(date) {
const dateReg = /((19|20)\d{2})(-|\/)\d{1,2}(-|\/)\d{1,2}/g
return date.match(dateReg)
},
getDateTime(date) {
return `${this.getDate(date)} ${this.getTime(date)}`
},
getDate(date) {
date = new Date(date)
const year = date.getFullYear()
const month = date.getMonth() + 1
const day = date.getDate()
return `${year}-${this.addZero(month)}-${this.addZero(day)}`
},
getTime(date) {
date = new Date(date)
const hour = date.getHours()
const minute = date.getMinutes()
const second = date.getSeconds()
return this.hideSecond ? `${this.addZero(hour)}:${this.addZero(minute)}` :
`${this.addZero(hour)}:${this.addZero(minute)}:${this.addZero(second)}`
},
addZero(num) {
if (num < 10) {
num = `0${num}`
}
return num
},
getDefaultSecond() {
return this.hideSecond ? '00:00' : '00:00:00'
}
}
}
</script>
<style lang="scss">
$uni-primary: #007aff !default;
.uni-date {
width: 100%;
flex: 1;
background-color: #fff;
}
.uni-date-x {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
border-radius: 4px;
background-color: #fff;
color: #666;
font-size: 14px;
flex: 1;
.icon-calendar {
padding-left: 3px;
}
.range-separator {
padding: 0 2px;
}
}
.uni-date-x--border {
box-sizing: border-box;
border-radius: 4px;
border: 1px solid #e5e5e5;
}
.uni-date-editor--x {
display: flex;
align-items: center;
position: relative;
}
.uni-date-editor--x .uni-date__icon-clear {
padding-right: 3px;
display: flex;
align-items: center;
/* #ifdef H5 */
cursor: pointer;
/* #endif */
}
.uni-date__x-input {
width: auto;
position: relative;
overflow: hidden;
flex: 1;
line-height: 1;
font-size: 14px;
height: 35px;
}
.t-c {
text-align: center;
}
.uni-date__input {
height: 40px;
width: 100%;
line-height: 40px;
font-size: 14px;
}
.uni-date-range__input {
text-align: center;
max-width: 142px;
}
.uni-date-picker__container {
position: relative;
}
.uni-date-mask--pc {
position: fixed;
bottom: 0px;
top: 0px;
left: 0px;
right: 0px;
background-color: rgba(0, 0, 0, 0);
transition-duration: 0.3s;
z-index: 996;
}
.uni-date-single--x {
background-color: #fff;
position: absolute;
top: 0;
z-index: 999;
border: 1px solid #EBEEF5;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
border-radius: 4px;
}
.uni-date-range--x {
background-color: #fff;
position: absolute;
top: 0;
z-index: 999;
border: 1px solid #EBEEF5;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
border-radius: 4px;
}
.uni-date-editor--x__disabled {
opacity: 0.4;
cursor: default;
}
.uni-date-editor--logo {
width: 16px;
height: 16px;
vertical-align: middle;
}
/* 添加时间 */
.popup-x-header {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
}
.popup-x-header--datetime {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
flex: 1;
}
.popup-x-body {
display: flex;
}
.popup-x-footer {
padding: 0 15px;
border-top-color: #F1F1F1;
border-top-style: solid;
border-top-width: 1px;
line-height: 40px;
text-align: right;
color: #666;
}
.popup-x-footer text:hover {
color: $uni-primary;
cursor: pointer;
opacity: 0.8;
}
.popup-x-footer .confirm {
margin-left: 20px;
color: $uni-primary;
}
.uni-date-changed {
text-align: center;
color: #333;
border-bottom-color: #F1F1F1;
border-bottom-style: solid;
border-bottom-width: 1px;
}
.uni-date-changed--time text {
height: 50px;
line-height: 50px;
}
.uni-date-changed .uni-date-changed--time {
flex: 1;
}
.uni-date-changed--time-date {
color: #333;
opacity: 0.6;
}
.mr-50 {
margin-right: 50px;
}
/* picker 弹出层通用的指示小三角, todo:扩展至上下左右方向定位 */
.uni-popper__arrow,
.uni-popper__arrow::after {
position: absolute;
display: block;
width: 0;
height: 0;
border: 6px solid transparent;
border-top-width: 0;
}
.uni-popper__arrow {
filter: drop-shadow(0 2px 12px rgba(0, 0, 0, 0.03));
top: -6px;
left: 10%;
margin-right: 3px;
border-bottom-color: #EBEEF5;
}
.uni-popper__arrow::after {
content: " ";
top: 1px;
margin-left: -6px;
border-bottom-color: #fff;
}
</style>
class Calendar {
constructor({
date,
selected,
startDate,
endDate,
range,
// multipleStatus
} = {}) {
// 当前日期
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)
// this.multipleStatus = multipleStatus
this.lastHover = false
}
/**
* 设置日期
* @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 && AddDayCount>0) {
dd.setDate(dd.getDate() + AddDayCount)
} else {
const preMonth = dd.getMonth()
dd.setMonth(preMonth + AddDayCount) // 获取AddDayCount天后的日期
const nextMonth = dd.getMonth()
// 处理 pre 切换月份目标月份为2月没有当前日(30 31) 切换错误问题
if(AddDayCount<0 && preMonth!==0 && nextMonth-preMonth>AddDayCount){
dd.setMonth(nextMonth+(nextMonth-preMonth+AddDayCount))
}
// 处理 next 切换月份目标月份为2月没有当前日(30 31) 切换错误问题
if(AddDayCount>0 && nextMonth-preMonth>AddDayCount){
dd.setMonth(nextMonth-(nextMonth-preMonth-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,
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.isLogicBefore(nowDate, this.multipleStatus.before, this.multipleStatus.after),
afterMultiple: this.isLogicAfter(nowDate, this.multipleStatus.before, this.multipleStatus.after),
month: full.month,
disable: !(disableBefore && disableAfter),
isDay,
userChecked: false
}
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,
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
}
}
/**
* 比较真实起始日期
*/
isLogicBefore(currentDay, before, after) {
let logicBefore = before
if (before && after) {
logicBefore = this.dateCompare(before, after) ? before : after
}
return this.dateEqual(logicBefore, currentDay)
}
isLogicAfter(currentDay, before, after) {
let logicAfter = after
if (before && after) {
logicAfter = this.dateCompare(before, after) ? after : before
}
return this.dateEqual(logicAfter, currentDay)
}
/**
* 获取日期范围内所有日期
* @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
}
/**
* 获取多选状态
*/
setMultiple(fullDate) {
let {
before,
after
} = this.multipleStatus
if (!this.range) return
if (before && after) {
if (!this.lastHover) {
this.lastHover = true
return
}
this.multipleStatus.before = fullDate
this.multipleStatus.after = ''
this.multipleStatus.data = []
this.multipleStatus.fulldate = ''
this.lastHover = false
} else {
if (!before) {
this.multipleStatus.before = fullDate
this.lastHover = false
} 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.lastHover = true
}
}
this._getWeek(fullDate)
}
/**
* 鼠标 hover 更新多选状态
*/
setHoverMultiple(fullDate) {
let {
before,
after
} = this.multipleStatus
if (!this.range) return
if (this.lastHover) return
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)
}
/**
* 更新默认值多选状态
*/
setDefaultMultiple(before, after) {
this.multipleStatus.before = before
this.multipleStatus.after = after
if (before && after) {
if (this.dateCompare(before, after)) {
this.multipleStatus.data = this.geDateAll(before, after);
this._getWeek(after)
} else {
this.multipleStatus.data = this.geDateAll(after, before);
this._getWeek(before)
}
}
}
/**
* 获取每周数据
* @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
...@@ -109,7 +109,7 @@ ...@@ -109,7 +109,7 @@
uni.scanCode({ uni.scanCode({
success(res) { success(res) {
let result = res.result let result = res.result
if (result.indexOf("?") != -1) { if (result.indexOf("?") != -1) { //一物一码
let param = result.split("?")[1] let param = result.split("?")[1]
let params = param.replace(/&/g, '","').replace(/=/g, '":"') let params = param.replace(/&/g, '","').replace(/=/g, '":"')
let paramJson = `{"${params}"}` let paramJson = `{"${params}"}`
...@@ -129,7 +129,8 @@ ...@@ -129,7 +129,8 @@
}) })
} }
} else { } else {
that.$toast.showToast("二维码格式不正确") that.orgCode(res)
// that.$toast.showToast("二维码格式不正确")
} }
}, },
fail() { fail() {
...@@ -137,6 +138,44 @@ ...@@ -137,6 +138,44 @@
} }
}) })
}, },
orgCode(res) {
try {
var role = getApp().globalData.userInfo.identity.code
let result = JSON.parse(res.result)
if (result && typeof result == 'object') {
let type = result.type
if (type == "company") {
this.code = result.text
uni.navigateTo({
url: `/pages/login/joinDepartment/joinDepartment?code=${this.code}&type=company`
})
} else if (type == "project") {
this.code = result.text
if (role == 'VISITOR') {
uni.navigateTo({
url: `/pages/login/joinDepartment/joinDepartment?code=${this.code}&type=project&projectId=${result.ext.id}`
})
} else {
uni.navigateTo({
url: `/pagesSub/mine/projectInfo/projectInfo?projectId=${result.ext.id}&from=qrcode`
})
}
} else if (type == "department") {
this.code = result.text
uni.navigateTo({
url: `/pages/login/joinDepartment/joinDepartment?code=${this.code}&type=department`
})
} else {
this.$toast.showToast("二维码格式不正确")
}
} else {
this.$toast.showToast("二维码格式不正确")
}
} catch (e) {
this.$toast.showToast("二维码格式不正确")
}
}
} }
} }
</script> </script>
......
...@@ -3,7 +3,7 @@ import App from './App' ...@@ -3,7 +3,7 @@ import App from './App'
// #ifndef VUE3 // #ifndef VUE3
import Vue from 'vue' import Vue from 'vue'
import tabBar from 'components/uni-tabbar/uni-tabbar.vue' import tabBar from './components/uni-tabbar/uni-tabbar.vue'
import $toast from './common/toast.js' import $toast from './common/toast.js'
import $config from './common/config.js' import $config from './common/config.js'
import $mta from "api/mta.js" import $mta from "api/mta.js"
......
...@@ -95,14 +95,6 @@ ...@@ -95,14 +95,6 @@
} }
}, { }, {
"path": "pages/score/score",
"style": {
"navigationBarTitleText": "",
"enablePullDownRefresh": false,
"navigationStyle": "custom"
}
}, {
"path": "pages/login/joinOrg/joinOrg", "path": "pages/login/joinOrg/joinOrg",
"style": { "style": {
"navigationBarTitleText": "", "navigationBarTitleText": "",
...@@ -111,14 +103,6 @@ ...@@ -111,14 +103,6 @@
} }
}, { }, {
"path": "pages/login/bindLogin/bindLogin",
"style": {
"navigationBarTitleText": "",
"enablePullDownRefresh": false,
"navigationStyle": "custom"
}
}, {
"path": "pages/login/userInfo/userInfo", "path": "pages/login/userInfo/userInfo",
"style": { "style": {
"navigationBarTitleText": "授权信息", "navigationBarTitleText": "授权信息",
...@@ -135,14 +119,6 @@ ...@@ -135,14 +119,6 @@
} }
}, { }, {
"path": "pages/mine/myScore/myScore",
"style": {
"navigationBarTitleText": "",
"enablePullDownRefresh": false,
"navigationStyle": "custom"
}
}, {
"path": "pages/login/createProject/createProject", "path": "pages/login/createProject/createProject",
"style": { "style": {
"navigationBarTitleText": "创建项目", "navigationBarTitleText": "创建项目",
...@@ -151,14 +127,6 @@ ...@@ -151,14 +127,6 @@
} }
}, { }, {
"path": "pages/mine/historyScore/historyScore",
"style": {
"navigationBarTitleText": "",
"enablePullDownRefresh": false,
"navigationStyle": "custom"
}
}, {
"path": "pages/index/siteNewList/siteNewList", "path": "pages/index/siteNewList/siteNewList",
"style": { "style": {
"navigationBarTitleText": "", "navigationBarTitleText": "",
...@@ -183,14 +151,6 @@ ...@@ -183,14 +151,6 @@
} }
}, { }, {
"path": "pages/mine/myApproval/myApproval",
"style": {
"navigationBarTitleText": "",
"enablePullDownRefresh": true,
"navigationStyle": "custom"
}
}, {
"path": "pages/gallery/message/message", "path": "pages/gallery/message/message",
"style": { "style": {
"navigationBarTitleText": "", "navigationBarTitleText": "",
...@@ -306,22 +266,6 @@ ...@@ -306,22 +266,6 @@
} }
}, { }, {
"path": "pages/mine/projectInfo/projectInfo",
"style": {
"navigationBarTitleText": "",
"enablePullDownRefresh": false,
"navigationStyle": "custom"
}
}, {
"path": "pages/mine/projectHistory/projectHistory",
"style": {
"navigationBarTitleText": "",
"enablePullDownRefresh": false,
"navigationStyle": "custom"
}
}, {
"path": "pages/index/bussConfig/bussConfig", "path": "pages/index/bussConfig/bussConfig",
"style": { "style": {
"navigationBarTitleText": "", "navigationBarTitleText": "",
...@@ -439,14 +383,6 @@ ...@@ -439,14 +383,6 @@
} }
}, { }, {
"path": "pages/mine/changeOrg/changeOrg",
"style": {
"navigationBarTitleText": "",
"enablePullDownRefresh": false,
"navigationStyle": "custom"
}
}, {
"path": "pages/gallery/app/editAloneForm/editAloneForm", "path": "pages/gallery/app/editAloneForm/editAloneForm",
"style": { "style": {
"navigationBarTitleText": "", "navigationBarTitleText": "",
...@@ -468,14 +404,6 @@ ...@@ -468,14 +404,6 @@
} }
}, { }, {
"path": "pages/mine/changeCompany/changeCompany",
"style": {
"navigationBarTitleText": "",
"enablePullDownRefresh": false,
"navigationStyle": "custom"
}
}, {
"path": "pages/gallery/messageDetail/messageDetail", "path": "pages/gallery/messageDetail/messageDetail",
"style": { "style": {
"navigationBarTitleText": "", "navigationBarTitleText": "",
...@@ -518,49 +446,37 @@ ...@@ -518,49 +446,37 @@
"enablePullDownRefresh": false "enablePullDownRefresh": false
} }
}, {
"path": "pages/user/accountLogin/accountLogin",
"style": {
"navigationBarTitleText": "",
"enablePullDownRefresh": false
}
}, {
"path": "pages/user/phoneLogin/phoneLogin",
"style": {
"navigationBarTitleText": "",
"enablePullDownRefresh": false
}
}, {
"path": "pages/gallery/outPage/gdtPage/gdtPage",
"style": {
"navigationBarTitleText": "",
"enablePullDownRefresh": false
}
} }
,{ ],
"path" : "pages/user/accountLogin/accountLogin",
"style" :
{
"navigationBarTitleText": "",
"enablePullDownRefresh": false
}
}
,{
"path" : "pages/user/phoneLogin/phoneLogin",
"style" :
{
"navigationBarTitleText": "",
"enablePullDownRefresh": false
}
}
,{
"path" : "pages/gallery/outPage/gdtPage/gdtPage",
"style" :
{
"navigationBarTitleText": "",
"enablePullDownRefresh": false
}
}
,{
"path" : "pages/questionAdd/questionAdd",
"style" :
{
"navigationBarTitleText": "",
"enablePullDownRefresh": false
}
}
],
"globalStyle": { "globalStyle": {
"navigationBarTextStyle": "white", "navigationBarTextStyle": "white",
"navigationBarTitleText": "智慧工地一点通", "navigationBarTitleText": "智慧工地一点通",
"navigationBarBackgroundColor": "#3B73FE", "navigationBarBackgroundColor": "#3B73FE",
"backgroundColor": "#3B73FE" "backgroundColor": "#3B73FE",
"usingComponents": {
"ly-tree-node": "/components/ly-tree/ly-tree-node"
}
}, },
"uniIdRouter": {}, "uniIdRouter": {},
"tabBar": { "tabBar": {
...@@ -579,5 +495,82 @@ ...@@ -579,5 +495,82 @@
"pagePath": "pages/mine/mine" "pagePath": "pages/mine/mine"
} }
] ]
} },
"subPackages": [{
"root": "pagesSub",
"pages": [{
"path": "mine/historyScoreDetail/historyScoreDetail",
"style": {
"navigationBarTitleText": "",
"enablePullDownRefresh": false,
"navigationStyle": "custom"
}
}, {
"path": "mine/myScore/myScore",
"style": {
"navigationBarTitleText": "",
"enablePullDownRefresh": false,
"navigationStyle": "custom"
}
}, {
"path": "mine/scoreDetail/scoreDetail",
"style": {
"navigationBarTitleText": "",
"enablePullDownRefresh": false,
"navigationStyle": "custom"
}
}, {
"path": "mine/historyScore/historyScore",
"style": {
"navigationBarTitleText": "",
"enablePullDownRefresh": false,
"navigationStyle": "custom"
}
}, {
"path": "mine/changeCompany/changeCompany",
"style": {
"navigationBarTitleText": "",
"enablePullDownRefresh": false,
"navigationStyle": "custom"
}
}, {
"path": "mine/changeOrg/changeOrg",
"style": {
"navigationBarTitleText": "",
"enablePullDownRefresh": false,
"navigationStyle": "custom"
}
}, {
"path": "mine/myApproval/myApproval",
"style": {
"navigationBarTitleText": "",
"enablePullDownRefresh": true,
"navigationStyle": "custom"
}
}, {
"path": "mine/projectHistory/projectHistory",
"style": {
"navigationBarTitleText": "",
"enablePullDownRefresh": false,
"navigationStyle": "custom"
}
}, {
"path": "mine/projectInfo/projectInfo",
"style": {
"navigationBarTitleText": "",
"enablePullDownRefresh": false,
"navigationStyle": "custom"
}
}]
}
]
} }
...@@ -37,7 +37,9 @@ ...@@ -37,7 +37,9 @@
projectId: "", projectId: "",
checkItemId: "", checkItemId: "",
inputValue: "", inputValue: "",
check_tpl_id: "" check_tpl_id: "",
appId: "",
auths: ['1', '2', '3', '4'] //操作权限对应增改查删
} }
}, },
onShow() { onShow() {
...@@ -51,6 +53,7 @@ ...@@ -51,6 +53,7 @@
this.check_tpl_id = option.check_tpl_id this.check_tpl_id = option.check_tpl_id
this.checkItemId = option.check_item_id this.checkItemId = option.check_item_id
this.projectId = option.project_id this.projectId = option.project_id
this.appId = option.appId
}, },
onPullDownRefresh() { onPullDownRefresh() {
...@@ -67,6 +70,18 @@ ...@@ -67,6 +70,18 @@
this.loadMore() this.loadMore()
}, },
methods: { methods: {
getOperateAuth() {
Api.getAppOperateAuth({
data: {
appId: this.appId
}
}).then((result) => {
console.log("按钮权限", result);
if (result.code == 0) {
this.auths = result.data
}
})
},
initData() { initData() {
this.loadData(true) this.loadData(true)
}, },
...@@ -80,6 +95,20 @@ ...@@ -80,6 +95,20 @@
this.getCheckList(refresh, 1) this.getCheckList(refresh, 1)
}, },
add() { add() {
var isAuth = this.auths.find(item => item == '1')
if (!isAuth) {
uni.showModal({
title: '提示',
content: '您没有新增权限,如需要请联系管理人员',
showCancel: false,
confirmText: "知道了",
confirmColor: '#3B73FE',
success: res => {
}
})
return
}
uni.navigateTo({ uni.navigateTo({
url: `/pages/gallery/app/createCheck/createCheck?check_item_id=${this.checkItemId}&title=${this.title}&check_tpl_id=${this.check_tpl_id}`, url: `/pages/gallery/app/createCheck/createCheck?check_item_id=${this.checkItemId}&title=${this.title}&check_tpl_id=${this.check_tpl_id}`,
}) })
...@@ -93,6 +122,20 @@ ...@@ -93,6 +122,20 @@
this.getCheckList(true, 0) this.getCheckList(true, 0)
}, },
itemClick(index) { itemClick(index) {
var isAuth = this.auths.find(item => item == '3')
if (!isAuth) {
uni.showModal({
title: '提示',
content: '您没有查看权限,如需要请联系管理人员',
showCancel: false,
confirmText: "知道了",
confirmColor: '#3B73FE',
success: res => {
}
})
return
}
let obj_no = this.objList[index].obj_no let obj_no = this.objList[index].obj_no
let check_obj_id = this.objList[index].check_obj_id let check_obj_id = this.objList[index].check_obj_id
uni.navigateTo({ uni.navigateTo({
......
...@@ -331,6 +331,7 @@ ...@@ -331,6 +331,7 @@
</template> </template>
<script> <script>
import Api from "../../../../api/api.js"
export default { export default {
data() { data() {
return { return {
...@@ -374,7 +375,8 @@ ...@@ -374,7 +375,8 @@
"align_type": "left" //对齐方式,可选参数:【'left','middle','right'】=》【'左对齐', '居中对齐', '右对齐'】 "align_type": "left" //对齐方式,可选参数:【'left','middle','right'】=》【'左对齐', '居中对齐', '右对齐'】
}, },
isSelectState: false, //是否选择状态 isSelectState: false, //是否选择状态
selectItems: [] //选中记录的recordId selectItems: [], //选中记录的recordId
auths: ['1', '2', '3', '4'] //操作权限对应增改查删
} }
}, },
onLoad: function(option) { onLoad: function(option) {
...@@ -383,6 +385,8 @@ ...@@ -383,6 +385,8 @@
this.businessType = option.business_type this.businessType = option.business_type
this.checkModuleId = option.check_module_id this.checkModuleId = option.check_module_id
this.appId = option.check_tpl_id this.appId = option.check_tpl_id
//真正的appId
var appId1 = option.appId
this.isModelEdit = option.is_edit this.isModelEdit = option.is_edit
this.loadProject() this.loadProject()
if (this.businessType == 3) { if (this.businessType == 3) {
...@@ -392,6 +396,7 @@ ...@@ -392,6 +396,7 @@
uni.setNavigationBarTitle({ uni.setNavigationBarTitle({
title: option.title title: option.title
}) })
this.getOperateAuth(appId1)
}, },
onShow: function() { onShow: function() {
uni.$once("refresh", res => { uni.$once("refresh", res => {
...@@ -418,7 +423,33 @@ ...@@ -418,7 +423,33 @@
} }
}, },
methods: { methods: {
getOperateAuth(appId) {
Api.getAppOperateAuth({
data: {
appId: appId
}
}).then((result) => {
console.log("按钮权限", result);
if (result.code == 0) {
this.auths = result.data
}
})
},
onDelete() { onDelete() {
var isAuth = this.auths.find(item => item == '4')
if (!isAuth) {
uni.showModal({
title: '提示',
content: '您没有删除权限,如需要请联系管理员',
showCancel: false,
confirmText: "知道了",
confirmColor: '#3B73FE',
success: res => {
}
})
return
}
if (this.selectItems.length == 0) { if (this.selectItems.length == 0) {
this.$toast.showToast("请选择需删除的记录") this.$toast.showToast("请选择需删除的记录")
return return
...@@ -511,6 +542,20 @@ ...@@ -511,6 +542,20 @@
this.getFormList() this.getFormList()
}, },
add: function() { add: function() {
var isAuth = this.auths.find(item => item == '1')
if (!isAuth) {
uni.showModal({
title: '提示',
content: '您没有新增权限,如需要请联系管理员',
showCancel: false,
confirmText: "知道了",
confirmColor: '#3B73FE',
success: res => {
}
})
return
}
if (this.businessType == 4) { if (this.businessType == 4) {
uni.navigateTo({ uni.navigateTo({
url: `/pages/gallery/app/addBusinessData/addBusinessData?check_tpl_id=${this.appId}&check_module_id=${this.checkModuleId}&bussness_type=${this.businessType}&project_id=${this.projectId}&title=${this.title}` url: `/pages/gallery/app/addBusinessData/addBusinessData?check_tpl_id=${this.appId}&check_module_id=${this.checkModuleId}&bussness_type=${this.businessType}&project_id=${this.projectId}&title=${this.title}`
...@@ -542,6 +587,20 @@ ...@@ -542,6 +587,20 @@
this.getFormList() this.getFormList()
}, },
toEdit: function(type, index) { toEdit: function(type, index) {
var isAuth = this.auths.find(item => item == '2')
if (!isAuth) {
uni.showModal({
title: '提示',
content: '您没有编辑权限,如需要请联系管理员',
showCancel: false,
confirmText: "知道了",
confirmColor: '#3B73FE',
success: res => {
}
})
return
}
let form = {} let form = {}
if (type == "left") { if (type == "left") {
form = this.leftList[index] form = this.leftList[index]
...@@ -561,6 +620,20 @@ ...@@ -561,6 +620,20 @@
} }
}, },
toEditDetail: function(type, index) { toEditDetail: function(type, index) {
var isAuth = this.auths.find(item => item == '3')
if (!isAuth) {
uni.showModal({
title: '提示',
content: '您没有查看权限,如需要请联系管理员',
showCancel: false,
confirmText: "知道了",
confirmColor: '#3B73FE',
success: res => {
}
})
return
}
let form = {} let form = {}
if (type == "left") { if (type == "left") {
form = this.leftList[index] form = this.leftList[index]
...@@ -574,6 +647,20 @@ ...@@ -574,6 +647,20 @@
}) })
}, },
toDelete: function(type, index) { toDelete: function(type, index) {
var isAuth = this.auths.find(item => item == '4')
if (!isAuth) {
uni.showModal({
title: '提示',
content: '您没有删除权限,如需要请联系管理员',
showCancel: false,
confirmText: "知道了",
confirmColor: '#3B73FE',
success: res => {
}
})
return
}
uni.showModal({ uni.showModal({
title: '提示', title: '提示',
content: '确定删除该内容?', content: '确定删除该内容?',
...@@ -613,6 +700,20 @@ ...@@ -613,6 +700,20 @@
}) })
}, },
toDetail: function(type, index) { toDetail: function(type, index) {
var isAuth = this.auths.find(item => item == '3')
if (!isAuth) {
uni.showModal({
title: '提示',
content: '您没有查看权限,如需要请联系管理员',
showCancel: false,
confirmText: "知道了",
confirmColor: '#3B73FE',
success: res => {
}
})
return
}
let form = {} let form = {}
if (type == "left") { if (type == "left") {
form = this.leftList[index] form = this.leftList[index]
......
...@@ -15,11 +15,13 @@ ...@@ -15,11 +15,13 @@
return { return {
appId: "", appId: "",
models: [], models: [],
appInfo: {} appInfo: {},
appId1: ""
} }
}, },
onLoad(option) { onLoad(option) {
this.appId = option.check_tpl_id this.appId = option.check_tpl_id
this.appId1 = option.appId
uni.setNavigationBarTitle({ uni.setNavigationBarTitle({
title: option.title title: option.title
}) })
...@@ -43,7 +45,7 @@ ...@@ -43,7 +45,7 @@
}, },
itemClick(index) { itemClick(index) {
uni.navigateTo({ uni.navigateTo({
url: `/pages/gallery/app/modeFormList/modeFormList?business_type=${this.appInfo.business_type}&check_tpl_id=${this.appId}&check_module_id=${this.models[index].check_module_id}&title=${this.models[index].module_name}&is_edit=${this.models[index].is_edit}&showCfg=${JSON.stringify(this.models[index].show_cfg)}` url: `/pages/gallery/app/modeFormList/modeFormList?business_type=${this.appInfo.business_type}&check_tpl_id=${this.appId}&check_module_id=${this.models[index].check_module_id}&title=${this.models[index].module_name}&is_edit=${this.models[index].is_edit}&showCfg=${JSON.stringify(this.models[index].show_cfg)}&appId=${this.appId1}`
}) })
} }
} }
......
...@@ -45,12 +45,14 @@ ...@@ -45,12 +45,14 @@
}, },
menu: [], menu: [],
appId: "", appId: "",
projectName: "" projectName: "",
appId1: ""
} }
}, },
onLoad(option) { onLoad(option) {
this.appId = option.check_tpl_id this.appId = option.check_tpl_id
this.title = option.title this.title = option.title
this.appId1 = option.appId
uni.setNavigationBarTitle({ uni.setNavigationBarTitle({
title: this.title title: this.title
}) })
...@@ -78,26 +80,6 @@ ...@@ -78,26 +80,6 @@
} else { } else {
this.projectSelect = this.projectList[0] this.projectSelect = this.projectList[0]
} }
// for (var i = 0; i < this.projectList.length; i++) {
// this.projectNames[i] = this.projectList[i].project_name
// }
// if (this.projectList.length > 0) {
// console.log("项目ID:", );
// let curId = uni.getStorageSync("project_id")
// if (curId.length == 0) {
// this.projectSelect = this.projectList[0].project_name
// uni.setStorageSync("project_id", this.projectList[0].project_id)
// uni.setStorageSync("project_name", this.projectList[0].project_name)
// } else {
// let project = this.projectList.find(item => item.project_id == curId)
// if (project) {
// this.projectSelect = uni.getStorageSync("project_name")
// } else {
// this.projectSelect = this.projectList[0].project_name
// }
// }
// }
} else { } else {
if (result.code === -1) { if (result.code === -1) {
uni.showToast({ uni.showToast({
...@@ -155,7 +137,7 @@ ...@@ -155,7 +137,7 @@
} = e.detail } = e.detail
let item = this.list[index] let item = this.list[index]
uni.navigateTo({ uni.navigateTo({
url: `/pages/gallery/app/checkObjList/checkObjList?check_item_name=${item.check_item_name}&check_item_id=${item.check_item_id}&check_tpl_id=${this.appId}&project_id=${this.projectSelect.project_id}`, url: `/pages/gallery/app/checkObjList/checkObjList?check_item_name=${item.check_item_name}&check_item_id=${item.check_item_id}&check_tpl_id=${this.appId}&project_id=${this.projectSelect.project_id}&appId=${this.appId1}`,
}) })
}, },
itemSelect(e) { itemSelect(e) {
......
...@@ -35,10 +35,11 @@ ...@@ -35,10 +35,11 @@
<text class="text">本周人气最佳:{{mostUsed.app_name||"暂无"}}</text> <text class="text">本周人气最佳:{{mostUsed.app_name||"暂无"}}</text>
</view> </view>
</view> </view>
<view class="app-content" :style="{'top':statusBarHeight+250+'px'}"> <titleBar title="欢迎您访问一点通" v-if="isTitleFix"></titleBar>
<view class="menu-content"> <view :class="isFix?'sticky-fixed':'menu-content'" :style="{'top':topValue()+'px'}">
<v-tabs :tabs="tabs" :scroll="true" :value="current" @change="tabChange"></v-tabs> <v-tabs :tabs="tabs" :scroll="true" :value="current" @change="tabChange"></v-tabs>
</view> </view>
<view class="app-content" :style="{'top':statusBarHeight+280+'px'}">
<view class="app-group-content"> <view class="app-group-content">
<view class="app-group-item" v-for="(item,index) in getMenuApps()" :key="index"> <view class="app-group-item" v-for="(item,index) in getMenuApps()" :key="index">
<view class="title">{{item.group_name||"其他"}}</view> <view class="title">{{item.group_name||"其他"}}</view>
...@@ -62,6 +63,7 @@ ...@@ -62,6 +63,7 @@
<script> <script>
import Api from "../../api/api.js" import Api from "../../api/api.js"
import StringUtils from "../../common/stringUtils.js" import StringUtils from "../../common/stringUtils.js"
import configEnv from "../../api/env.js"
export default { export default {
data() { data() {
return { return {
...@@ -94,19 +96,22 @@ ...@@ -94,19 +96,22 @@
}, },
orgName: "", orgName: "",
mostUsed: {}, mostUsed: {},
role: "PM" role: "PM",
isFix: false,
isTitleFix: false,
scrollTop: 0
} }
}, },
onLoad() { onLoad() {
const info = uni.getSystemInfoSync() const info = uni.getSystemInfoSync()
this.statusBarHeight = info.statusBarHeight this.statusBarHeight = info.statusBarHeight
this.role = getApp().globalData.userInfo.identity.code this.role = getApp().globalData.userInfo.identity.code
this.getAppGroup()
}, },
onShow() { onShow() {
this.getMessages() this.getMessages()
this.getNeedDo() this.getNeedDo()
uni.$once("refreshOrg", res => { this.getAppGroup()
uni.$on("refreshOrg", res => {
this.orgName = uni.getStorageSync("org_name") this.orgName = uni.getStorageSync("org_name")
}) })
if (!uni.getStorageSync("org_name")) { if (!uni.getStorageSync("org_name")) {
...@@ -117,8 +122,33 @@ ...@@ -117,8 +122,33 @@
} }
this.getMostUsed() this.getMostUsed()
}, },
onUnload() {
uni.$off("refreshOrg")
},
onPageScroll(e) {
//#ifdef H5
this.isFix = true
// #endif
// #ifndef H5
if (e.scrollTop > 50) {
this.isTitleFix = true
} else {
this.isTitleFix = false
}
if (this.statusBarHeight + 200 > e.scrollTop) {
this.isFix = false
} else {
this.scrollTop = e.scrollTop
this.isFix = true
}
// #endif
},
methods: { methods: {
topValue() {
return this.isFix ? (this.statusBarHeight + 40) : (this.statusBarHeight + 240)
},
getMenuApps() { getMenuApps() {
console.log("是否吸顶", this.isFix);
var menuApps = this.apps.find(item => item.app_menu == this.current + 1) var menuApps = this.apps.find(item => item.app_menu == this.current + 1)
if (menuApps) { if (menuApps) {
return menuApps.list return menuApps.list
...@@ -163,7 +193,7 @@ ...@@ -163,7 +193,7 @@
return return
} }
uni.navigateTo({ uni.navigateTo({
url: "/pages/mine/changeOrg/changeOrg" url: "/pagesSub/mine/changeOrg/changeOrg"
}) })
}, },
toIndex(model) { toIndex(model) {
...@@ -181,14 +211,14 @@ ...@@ -181,14 +211,14 @@
if (result.code == 0) { if (result.code == 0) {
if (!result.data.business_type) { if (!result.data.business_type) {
uni.navigateTo({ uni.navigateTo({
url: `/pages/gallery/app/originalIndex/originalIndex?check_tpl_id=${model.source_id}&title=${result.data.tpl_name}` url: `/pages/gallery/app/originalIndex/originalIndex?check_tpl_id=${model.source_id}&title=${result.data.tpl_name}&appId=${model.app_id}`
}) })
} else if (result.data.business_type == 2) { //独立模式 需要获取模块数量 如果只有一个模块 自动跳转表单页面 "体验嘛 呵呵" } else if (result.data.business_type == 2) { //独立模式 需要获取模块数量 如果只有一个模块 自动跳转表单页面 "体验嘛 呵呵"
this.toAlone(model, result.data) this.toAlone(model, result.data)
} else if (result.data.business_type == 3 || result.data.business_type == 4) { //流程模式 } else if (result.data.business_type == 3 || result.data.business_type == 4) { //流程模式
this.models = result.data.tpl_config this.models = result.data.tpl_config
uni.navigateTo({ uni.navigateTo({
url: `/pages/gallery/app/modeFormList/modeFormList?check_tpl_id=${model.source_id}&business_type=${result.data.business_type}&title=${result.data.tpl_name}&showCfg=${JSON.stringify(this.models[0].show_cfg)}` url: `/pages/gallery/app/modeFormList/modeFormList?check_tpl_id=${model.source_id}&business_type=${result.data.business_type}&title=${result.data.tpl_name}&showCfg=${JSON.stringify(this.models[0].show_cfg)}&appId=${model.app_id}`
}) })
} else if (result.data.business_type == 5) { } else if (result.data.business_type == 5) {
var source = "1" var source = "1"
...@@ -197,17 +227,21 @@ ...@@ -197,17 +227,21 @@
} else if (this.role == 'WORKER') { } else if (this.role == 'WORKER') {
source = 9 source = 9
} }
if ("63e1e639e7025e13c471a9d2" == result.data.check_tpl_id) { //建筑工人沟通日 if (configEnv.jzgrAppNo == result.data.check_tpl_id) { //建筑工人沟通日
this.project_id = uni.getStorageSync("project_id") this.project_id = uni.getStorageSync("project_id")
uni.navigateTo({ uni.navigateTo({
url: "/pages/gallery/outPage/gdtPage/gdtPage?url=" + url: "/pages/gallery/outPage/gdtPage/gdtPage?url=" +
`${this.$config.gtrBase}/#/loginByToken&projectId=${this.project_id}&source=${source}&token=${uni.getStorageSync("token")}&to=jzgrgtr` `${this.$config.gtrBase}/#/loginByToken&projectId=${this.project_id}&source=${source}&token=${uni.getStorageSync("token")}&to=jzgrgtr`
}) })
} else if ("63e1f98420836820ca5e3c02" == result.data.check_tpl_id) { } else if (configEnv.jyxyAppNo == result.data.check_tpl_id) {
uni.navigateTo({ uni.navigateTo({
url: "/pages/gallery/outPage/gdtPage/gdtPage?url=" + url: "/pages/gallery/outPage/gdtPage/gdtPage?url=" +
`${this.$config.jyxyBase}/dologin/workSite&workSiteToken=${uni.getStorageSync("token")}&to=jypx` `${this.$config.jyxyBase}/dologin/workSite&workSiteToken=${uni.getStorageSync("token")}&to=jypx`
}) })
} else if (configEnv.jfscAppNo == result.data.check_tpl_id) {
uni.navigateTo({
url: "/pagesSub/mine/myScore/myScore"
})
} else { } else {
this.toOutPage(result.data.tpl_config[0]) this.toOutPage(result.data.tpl_config[0])
} }
...@@ -264,11 +298,11 @@ ...@@ -264,11 +298,11 @@
if (this.models.length == 1) { if (this.models.length == 1) {
let is_model_edit = this.models[0].is_edit let is_model_edit = this.models[0].is_edit
uni.navigateTo({ uni.navigateTo({
url: `/pages/gallery/app/modeFormList/modeFormList?business_type=2&check_tpl_id=${model.source_id}&check_module_id=${this.models[0].check_module_id}&title=${this.models[0].module_name}&is_edit=${is_model_edit}&showCfg=${JSON.stringify(this.models[0].show_cfg)}` url: `/pages/gallery/app/modeFormList/modeFormList?business_type=2&check_tpl_id=${model.source_id}&check_module_id=${this.models[0].check_module_id}&title=${this.models[0].module_name}&is_edit=${is_model_edit}&showCfg=${JSON.stringify(this.models[0].show_cfg)}&appId=${model.app_id}`
}) })
} else { } else {
uni.navigateTo({ uni.navigateTo({
url: `/pages/gallery/app/modeList/modeList?check_tpl_id=${model.source_id}&title=${info.tpl_name}` url: `/pages/gallery/app/modeList/modeList?check_tpl_id=${model.source_id}&title=${info.tpl_name}&appId=${model.app_id}`
}) })
} }
}, },
...@@ -615,16 +649,24 @@ ...@@ -615,16 +649,24 @@
} }
.menu-content {
position: absolute;
width: 100%;
}
.sticky-fixed {
width: 100%;
position: fixed;
top: 0;
z-index: 999;
}
.app-content { .app-content {
position: absolute; position: absolute;
display: flex; display: flex;
padding-bottom: 85px; padding-bottom: 85px;
flex-direction: column; flex-direction: column;
.menu-content {
display: flex;
}
.app-group-content { .app-group-content {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
...@@ -652,6 +694,7 @@ ...@@ -652,6 +694,7 @@
.item { .item {
display: flex; display: flex;
margin-top: 20rpx;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
......
<template> <template>
<view class="content"> <view class="content">
<titleBar title="消息中心"></titleBar> <titleBar title="消息中心"></titleBar>
<v-tabs :tabs="tabs" :scroll="true" :value="current" @change="tabChange"></v-tabs> <view class="tab-content">
<v-tabs :tabs="tabs" :scroll="true" :value="current" @change="tabChange"></v-tabs>
<view class="t-allRead" @click="clickAllRead">
<image src="../../../static/brush-ic.png" class="image"></image>
<text class="text">一键已读</text>
</view>
</view>
<view class="list-content" v-if="list.length>0"> <view class="list-content" v-if="list.length>0">
<view v-for="(item,index) in list" :key="index" class="list-item" @click="itemClick(index,item)"> <view v-for="(item,index) in list" :key="index" class="list-item" @click="itemClick(index,item)">
<view class="red-dot" v-if="!item.readStatus"></view> <view class="red-dot" v-if="!item.readStatus"></view>
...@@ -56,14 +63,25 @@ ...@@ -56,14 +63,25 @@
// }) // })
}, },
setRead(item) { setRead(item) {
var list = []
list.push(item)
Api.setMessageRead({ Api.setMessageRead({
data: item data: list
}).then((result) => { }).then((result) => {
uni.navigateTo({ uni.navigateTo({
url: "/pages/gallery/messageDetail/messageDetail?message=" + JSON.stringify(item) url: "/pages/gallery/messageDetail/messageDetail?message=" + JSON.stringify(item)
}) })
}) })
}, },
clickAllRead() {
var array = this.list.filter(item => !item.readStatus).map(item => item.readStatus = true)
console.log("处理完数组", array);
Api.setMessageRead({
data: array
}).then((result) => {
this.getMessages()
})
},
tabChange(e) { tabChange(e) {
console.log("tab切换", e); console.log("tab切换", e);
this.current = e this.current = e
...@@ -100,6 +118,37 @@ ...@@ -100,6 +118,37 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
.tab-content {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
.t-allRead {
display: flex;
background-color: #FFFFFF;
flex-direction: row;
padding: 10rpx 20rpx;
margin-right: 30rpx;
border-radius: 24rpx;
align-items: center;
image {
width: 26rpx;
height: 26rpx;
}
text {
font-family: "PingFang SC";
font-weight: 400;
font-size: 12px;
margin-left: 10rpx;
text-align: center;
color: #666;
}
}
}
.list-content { .list-content {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
......
...@@ -32,7 +32,7 @@ ...@@ -32,7 +32,7 @@
<image src="../../../static/add-ic.png" class="del" @click="toAdd(app,index,appIndex)"> <image src="../../../static/add-ic.png" class="del" @click="toAdd(app,index,appIndex)">
</image> </image>
</view> </view>
<view class="text">{{nameLengthDeal(app.app_name)}}</view> <view class="text">{{app.app_name}}</view>
</view> </view>
</view> </view>
</view> </view>
...@@ -365,6 +365,7 @@ ...@@ -365,6 +365,7 @@
.app-list { .app-list {
display: flex; display: flex;
margin-top: 30rpx; margin-top: 30rpx;
flex-wrap: wrap;
flex-direction: row; flex-direction: row;
.menu-item { .menu-item {
...@@ -398,9 +399,19 @@ ...@@ -398,9 +399,19 @@
} }
.text { .text {
width: 120rpx;
margin-top: 20rpx;
text-align: center;
font-weight: 400;
font-size: 24rpx; font-size: 24rpx;
color: #333; color: #333;
margin-top: 10rpx; display: -webkit-box;
overflow: hidden;
text-overflow: ellipsis;
word-wrap: break-word;
white-space: normal !important;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
} }
} }
} }
......
...@@ -125,6 +125,7 @@ ...@@ -125,6 +125,7 @@
import qqmapsdk from "../../common/qqmap-wx-jssdk1.2/qqmap-wx-jssdk.js" import qqmapsdk from "../../common/qqmap-wx-jssdk1.2/qqmap-wx-jssdk.js"
import Api from "../../api/api.js" import Api from "../../api/api.js"
import StringUtils from "../../common/stringUtils.js" import StringUtils from "../../common/stringUtils.js"
import configEnv from "../../api/env.js"
export default { export default {
data() { data() {
return { return {
...@@ -160,8 +161,8 @@ ...@@ -160,8 +161,8 @@
} }
}, },
onLoad() { onLoad() {
getApp().globalData.isInit = true
const info = uni.getSystemInfoSync() const info = uni.getSystemInfoSync()
console.log("系统", info);
if (info.platform == 'windows') { if (info.platform == 'windows') {
this.statusBarHeight = info.safeArea.top this.statusBarHeight = info.safeArea.top
} else { } else {
...@@ -173,9 +174,7 @@ ...@@ -173,9 +174,7 @@
`${nowDate.getFullYear()}/${this.add0(nowDate.getMonth()+1)}/${this.add0(nowDate.getDate())}` `${nowDate.getFullYear()}/${this.add0(nowDate.getMonth()+1)}/${this.add0(nowDate.getDate())}`
}, },
onShow() { onShow() {
if (!getApp().globalData.isReject) { this.loginState()
this.loginState()
}
uni.$once("refreshApp", res => { uni.$once("refreshApp", res => {
if (this.isCommonSel) { if (this.isCommonSel) {
this.getCommonApp() this.getCommonApp()
...@@ -183,9 +182,19 @@ ...@@ -183,9 +182,19 @@
this.getRecentlyApp() this.getRecentlyApp()
} }
}) })
uni.$on("refreshOrg", res => { //组织切换了
this.project_id = uni.getStorageSync("project_id")
if (!this.project_id) {
this.getCompanyBanners()
this.getCompanyNews()
} else {
this.getNews()
this.getBanners()
}
})
}, },
onReady() { onUnload() {
uni.$off("refreshOrg")
}, },
methods: { methods: {
toJoinOrg() { toJoinOrg() {
...@@ -233,6 +242,32 @@ ...@@ -233,6 +242,32 @@
} }
}) })
}, },
getDeptProject() {
this.$http.get({
url: "/api/v1/project/project_list",
data: {
is_all: 1,
org_no: uni.getStorageSync("org_no")
},
header: {
"access_token": this.getToken()
}
}).then(res => {
let result = res.data
if (result.code === 0) {
if (result.data.list.length > 0) {
this.project = result.data.list[0]
this.project_id = this.project.project_id
uni.setStorageSync("org_no", this.project.org_no)
uni.setStorageSync("org_name", this.project.project_name)
uni.setStorageSync("project_id", this.project.project_id)
}
this.initData()
}
}, (err) => {
this.$toast.showToast(err.msg)
})
},
loadProject: function() { loadProject: function() {
let orgNo = uni.getStorageSync("org_no") let orgNo = uni.getStorageSync("org_no")
if (orgNo == '001') { if (orgNo == '001') {
...@@ -262,8 +297,8 @@ ...@@ -262,8 +297,8 @@
uni.setStorageSync("project_id", this.project.project_id) uni.setStorageSync("project_id", this.project.project_id)
} }
} }
this.initData()
} }
this.initData()
}, (err) => { }, (err) => {
this.$toast.showToast(err.msg) this.$toast.showToast(err.msg)
}) })
...@@ -304,8 +339,14 @@ ...@@ -304,8 +339,14 @@
if (this.role == 'VISITOR' || this.banners.length == 0) { if (this.role == 'VISITOR' || this.banners.length == 0) {
return return
} }
this.project_id = uni.getStorageSync("project_id")
var isComp = 0
if (!this.project_id) { //企业级
isComp = 1
}
uni.navigateTo({ uni.navigateTo({
url: `/pages/index/siteNewList/siteNewList?clientId=1&projectId=` + this.project_id url: `/pages/index/siteNewList/siteNewList?clientId=1&projectId=` + this.project_id +
"&isComp=" + isComp
}) })
}, },
toLinkOther() { toLinkOther() {
...@@ -407,7 +448,7 @@ ...@@ -407,7 +448,7 @@
Api.getNews({ Api.getNews({
data: { data: {
pageNum: 1, pageNum: 1,
pageSize: 20, pageSize: 10,
clientId: 2, clientId: 2,
projectId: this.project_id, projectId: this.project_id,
} }
...@@ -421,6 +462,63 @@ ...@@ -421,6 +462,63 @@
this.$toast.showToast(err.msg) this.$toast.showToast(err.msg)
}) })
}, },
getCompanyNews() {
Api.getCompanyNews({
data: {
pageNum: 1,
pageSize: 10,
clientId: 2,
orgId: uni.getStorageSync("org_id")
}
}).then((result) => {
if (result.code == 0) {
this.news = result.data.list
} else {
this.$toast.showToast(result.msg)
}
}, (err) => {
this.$toast.showToast(err.msg)
})
},
getCompanyBanners() {
Api.getCompanyNews({
data: {
pageNum: 1,
pageSize: 5,
clientId: 1,
orgId: uni.getStorageSync("org_id")
}
}).then((result) => {
if (result.code == 0) {
this.banners = []
if (result.data.list.length > 0) {
result.data.list.forEach((item) => {
if (!item.coverImg || item.coverImg == 'null') {
item.coverImg = "../../static/banner-content.png"
}
this.banners.push(item)
})
console.log("banner", this.banners);
} else {
if (this.role == 'VISITOR') {
this.banners.push({
"coverImg": "../../static/banner-authority.png"
})
} else {
this.banners.push({
"coverImg": "../../static/banner-content.png"
})
}
}
} else {
this.$toast.showToast(result.msg)
}
}, (err) => {
this.$toast.showToast(err.msg)
})
},
//获取常用应用 //获取常用应用
getCommonApp() { getCommonApp() {
Api.getNormalApp().then((result) => { Api.getNormalApp().then((result) => {
...@@ -446,12 +544,19 @@ ...@@ -446,12 +544,19 @@
}, },
toLinkScore() { toLinkScore() {
uni.navigateTo({ uni.navigateTo({
url: "/pages/mine/myScore/myScore" url: "/pagesSub/mine/myScore/myScore"
}) })
}, },
toLinkNew() { toLinkNew() {
this.project_id = uni.getStorageSync("project_id")
var isComp = 0
console.log("项目Id", this.project_id);
if (!this.project_id) { //企业级
isComp = 1
}
uni.navigateTo({ uni.navigateTo({
url: `/pages/index/siteNewList/siteNewList?clientId=2&projectId=` + this.project_id url: `/pages/index/siteNewList/siteNewList?clientId=2&projectId=` + this.project_id +
"&isComp=" + isComp
}) })
}, },
getMyScore() { getMyScore() {
...@@ -543,13 +648,10 @@ ...@@ -543,13 +648,10 @@
var app = this.menus.find(item => item.object_no == 'gdydt') var app = this.menus.find(item => item.object_no == 'gdydt')
if (app) { if (app) {
var mainAuthMenus = app.children_list var mainAuthMenus = app.children_list
// console.log("mainAuthMenus", mainAuthMenus);
if (mainAuthMenus && mainAuthMenus.length > 0) { if (mainAuthMenus && mainAuthMenus.length > 0) {
var mineMenus = mainAuthMenus.find(item => item.object_no == 'ydtsy') var mineMenus = mainAuthMenus.find(item => item.object_no == 'ydtsy')
// console.log("mineMenus", mineMenus);
if (mineMenus) { if (mineMenus) {
var childMenus = mineMenus.children_list var childMenus = mineMenus.children_list
console.log("childMenus", childMenus);
if (childMenus && childMenus.length > 0) { if (childMenus && childMenus.length > 0) {
var child = childMenus.find(item => item.object_no == obj) var child = childMenus.find(item => item.object_no == obj)
if (child) { if (child) {
...@@ -567,14 +669,41 @@ ...@@ -567,14 +669,41 @@
if (result.code == 0) { if (result.code == 0) {
getApp().globalData.userInfo = result.data getApp().globalData.userInfo = result.data
this.userInfo = result.data this.userInfo = result.data
// this.title = "欢迎" + this.userInfo.user.user_name + "访问一点通"
this.role = this.userInfo.identity.code this.role = this.userInfo.identity.code
this.project_id = uni.getStorageSync("project_id") let jobs = this.userInfo.user.job_list
console.log("当前组织", this.project_id); //如果是项目端 获取当前岗位
if (!this.project_id) { //如果是工人 获取工人当前项目
this.loadProject() if (this.role == 'PM') {
if (getApp().globalData.isInit) {
if (jobs) {
let currentJob = jobs[0]
uni.setStorageSync("org_no", currentJob.org_no)
uni.setStorageSync("org_id", currentJob.org_id)
//当前岗位 判断当前岗位是项目部还是其他
if (this.userInfo.user.dept_type == '2') { //项目级
uni.setStorageSync("org_name", currentJob.dept_name)
this.getDeptProject()
} else {
uni.setStorageSync("org_name", currentJob.org_name)
uni.setStorageSync("project_id", "")
this.initData()
}
} else {
uni.setStorageSync("org_no", "")
uni.setStorageSync("org_id", "")
uni.setStorageSync("org_name", "")
uni.setStorageSync("project_id", "")
this.initData()
}
getApp().globalData.isInit = false
}
} else { } else {
this.initData() this.project_id = uni.getStorageSync("project_id")
if (!this.project_id) {
this.loadProject()
} else {
this.initData()
}
} }
} else { } else {
this.$toast.showToast(result.msg) this.$toast.showToast(result.msg)
...@@ -595,8 +724,14 @@ ...@@ -595,8 +724,14 @@
this.city = currentProject.city || '杭州市' this.city = currentProject.city || '杭州市'
this.address = currentProject.province + currentProject.city + currentProject.area this.address = currentProject.province + currentProject.city + currentProject.area
} }
this.getNews() this.project_id = uni.getStorageSync("project_id")
this.getBanners() if (!this.project_id) {
this.getCompanyBanners()
this.getCompanyNews()
} else {
this.getNews()
this.getBanners()
}
this.getWeather() this.getWeather()
this.getAuthMenu() this.getAuthMenu()
}, },
...@@ -754,7 +889,7 @@ ...@@ -754,7 +889,7 @@
} else if (result.data.business_type == 3 || result.data.business_type == 4) { //流程模式 } else if (result.data.business_type == 3 || result.data.business_type == 4) { //流程模式
this.models = result.data.tpl_config this.models = result.data.tpl_config
uni.navigateTo({ uni.navigateTo({
url: `/pages/gallery/app/modeFormList/modeFormList?check_tpl_id=${model.source_id}&business_type=${result.data.business_type}&title=${result.data.tpl_name}&showCfg=${JSON.stringify(this.models[0].show_cfg)}` url: `/pages/gallery/app/modeFormList/modeFormList?check_tpl_id=${model.source_id}&business_type=${result.data.business_type}&title=${result.data.tpl_name}&showCfg=${JSON.stringify(this.models[0].show_cfg)}&appId=${model.app_id}`
}) })
} else if (result.data.business_type == 5) { } else if (result.data.business_type == 5) {
var source = "1" var source = "1"
...@@ -763,16 +898,20 @@ ...@@ -763,16 +898,20 @@
} else if (this.role == 'WORKER') { } else if (this.role == 'WORKER') {
source = 9 source = 9
} }
if ("63e1e639e7025e13c471a9d2" == result.data.check_tpl_id) { //建筑工人沟通日 if (configEnv.jzgrAppNo == result.data.check_tpl_id) { //建筑工人沟通日
uni.navigateTo({ uni.navigateTo({
url: "/pages/gallery/outPage/gdtPage/gdtPage?url=" + url: "/pages/gallery/outPage/gdtPage/gdtPage?url=" +
`${this.$config.gtrBase}/#/loginByToken&projectId=${this.project_id}&source=${source}&token=${uni.getStorageSync("token")}&to=jzgrgtr` `${this.$config.gtrBase}/#/loginByToken&projectId=${this.project_id}&source=${source}&token=${uni.getStorageSync("token")}&to=jzgrgtr`
}) })
} else if ("63e1f98420836820ca5e3c02" == result.data.check_tpl_id) { } else if (configEnv.jyxyAppNo == result.data.check_tpl_id) {
uni.navigateTo({ uni.navigateTo({
url: "/pages/gallery/outPage/gdtPage/gdtPage?url=" + url: "/pages/gallery/outPage/gdtPage/gdtPage?url=" +
`${this.$config.jyxyBase}/dologin/workSite&workSiteToken=${uni.getStorageSync("token")}&to=jypx` `${this.$config.jyxyBase}/dologin/workSite&workSiteToken=${uni.getStorageSync("token")}&to=jypx`
}) })
} else if (configEnv.jfscAppNo == result.data.check_tpl_id) {
uni.navigateTo({
url: "/pagesSub/mine/myScore/myScore"
})
} else { } else {
this.toOutPage(result.data.tpl_config[0]) this.toOutPage(result.data.tpl_config[0])
} }
...@@ -824,11 +963,11 @@ ...@@ -824,11 +963,11 @@
if (this.models.length == 1) { if (this.models.length == 1) {
let is_model_edit = this.models[0].is_edit let is_model_edit = this.models[0].is_edit
uni.navigateTo({ uni.navigateTo({
url: `/pages/gallery/app/modeFormList/modeFormList?business_type=2&check_tpl_id=${model.source_id}&check_module_id=${this.models[0].check_module_id}&title=${this.models[0].module_name}&is_edit=${is_model_edit}&showCfg=${JSON.stringify(this.models[0].show_cfg)}` url: `/pages/gallery/app/modeFormList/modeFormList?business_type=2&check_tpl_id=${model.source_id}&check_module_id=${this.models[0].check_module_id}&title=${this.models[0].module_name}&is_edit=${is_model_edit}&showCfg=${JSON.stringify(this.models[0].show_cfg)}&appId=${model.app_id}`
}) })
} else { } else {
uni.navigateTo({ uni.navigateTo({
url: `/pages/gallery/app/modeList/modeList?check_tpl_id=${model.source_id}&title=${info.tpl_name}` url: `/pages/gallery/app/modeList/modeList?check_tpl_id=${model.source_id}&title=${info.tpl_name}&appId=${model.app_id}`
}) })
} }
}, },
......
...@@ -44,12 +44,14 @@ ...@@ -44,12 +44,14 @@
}, },
id: "", id: "",
text: "", text: "",
clientId: "" clientId: "",
isComp: 0
} }
}, },
onLoad(options) { onLoad(options) {
this.id = options.id this.id = options.id
this.clientId = options.clientId this.clientId = options.clientId
this.isComp = options.isComp
this.getNewDetail() this.getNewDetail()
}, },
methods: { methods: {
...@@ -64,7 +66,7 @@ ...@@ -64,7 +66,7 @@
toLinkDetail(item) { toLinkDetail(item) {
uni.navigateTo({ uni.navigateTo({
url: "/pages/index/newDetail/newDetail?id=" + item.articleLibraryId + "&clientId=" + this url: "/pages/index/newDetail/newDetail?id=" + item.articleLibraryId + "&clientId=" + this
.clientId .clientId + "&isComp=" + this.isComp
}) })
}, },
onEditorReady(content) { onEditorReady(content) {
...@@ -95,12 +97,20 @@ ...@@ -95,12 +97,20 @@
return text return text
}, },
getNewDetail() { getNewDetail() {
Api.getNewDetail({ var data = {
data: { articleLibraryId: this.id,
projectId: uni.getStorageSync("project_id") || "",
clientId: this.clientId
}
if (this.isComp == 1) {
data = {
articleLibraryId: this.id, articleLibraryId: this.id,
projectId: uni.getStorageSync("project_id") || "", orgId: uni.getStorageSync("org_id") || "",
clientId: this.clientId clientId: this.clientId
} }
}
Api.getNewDetail({
data: data
}).then((result) => { }).then((result) => {
if (result.code == 0) { if (result.code == 0) {
this.list = [] this.list = []
......
...@@ -33,12 +33,15 @@ ...@@ -33,12 +33,15 @@
clientId: 2, clientId: 2,
key: "", key: "",
projectId: "", projectId: "",
title: "工地头条" title: "工地头条",
isComp: 0
} }
}, },
onLoad(option) { onLoad(option) {
this.clientId = option.clientId this.clientId = option.clientId
this.projectId = option.projectId this.projectId = option.projectId
this.isComp = option.isComp
console.log("是否企业级", this.isComp);
this.getNews() this.getNews()
if (this.clientId == 1) { if (this.clientId == 1) {
this.title = "新闻公告" this.title = "新闻公告"
...@@ -84,7 +87,7 @@ ...@@ -84,7 +87,7 @@
toLinkDetail(item) { toLinkDetail(item) {
uni.navigateTo({ uni.navigateTo({
url: "/pages/index/newDetail/newDetail?id=" + item.articleLibraryId + "&clientId=" + this url: "/pages/index/newDetail/newDetail?id=" + item.articleLibraryId + "&clientId=" + this
.clientId .clientId + "&isComp=" + this.isComp
}) })
}, },
search(e) { search(e) {
...@@ -93,31 +96,59 @@ ...@@ -93,31 +96,59 @@
this.getNews() this.getNews()
}, },
getNews() { getNews() {
Api.getNews({ if (this.isComp == 1) {
data: { Api.getCompanyNews({
pageNum: this.page, data: {
pageSize: this.pageSize, pageNum: this.page,
clientId: this.clientId, pageSize: this.pageSize,
title: this.key, clientId: this.clientId,
projectId: this.projectId title: this.key,
} orgId: uni.getStorageSync("org_id")
}).then((result) => { }
uni.stopPullDownRefresh() }).then((result) => {
if (result.code == 0) { uni.stopPullDownRefresh()
let list = result.data.list if (result.code == 0) {
if (list.length == this.pageSize) { let list = result.data.list
this.isHasMore = true if (list.length == this.pageSize) {
this.isHasMore = true
} else {
this.isHasMore = false
}
if (this.page == 1) {
this.list = []
}
this.list = this.list.concat(list)
} else { } else {
this.isHasMore = false this.$toast.showToast(result.msg)
} }
if (this.page == 1) { })
this.list = [] } else {
Api.getNews({
data: {
pageNum: this.page,
pageSize: this.pageSize,
clientId: this.clientId,
title: this.key,
projectId: this.projectId
} }
this.list = this.list.concat(list) }).then((result) => {
} else { uni.stopPullDownRefresh()
this.$toast.showToast(result.msg) if (result.code == 0) {
} let list = result.data.list
}) if (list.length == this.pageSize) {
this.isHasMore = true
} else {
this.isHasMore = false
}
if (this.page == 1) {
this.list = []
}
this.list = this.list.concat(list)
} else {
this.$toast.showToast(result.msg)
}
})
}
} }
} }
} }
......
<template>
<view class="login-parent">
<titleBar title="微信绑定"></titleBar>
<view class="login-content">
<view class="login-logo" v-if="false">
<image src="../../static/logo.png"></image>
</view>
<view class="login-app-name" v-if="false">
<text>{{appName}}</text>
</view>
<view class="login-layout">
<view class="login-account">
<text>账号</text>
<input placeholder="输入账号" type="text" v-model="account" />
</view>
<view class="login-password">
<text>密码</text>
<input placeholder="输入密码" password="true" v-model="password" />
</view>
</view>
<button @click="bindLogin">绑定</button>
<button @click="register">注册</button>
</view>
</view>
</template>
<script>
import Api from "../../../api/api.js"
export default {
data() {
return {
password: "",
account: "",
unique_token: "",
appName: uni.getStorageSync("application_name") === "" ? "" : uni.getStorageSync("application_name"),
appLogo: uni.getStorageSync("application_name") === "" ? "" : uni.getStorageSync("version_pic")
}
},
onLoad: function(option) {
this.unique_token = option.unique_token
},
methods: {
register: function() {
uni.navigateTo({
url: `/pages/login/register/register?unique_token=${this.unique_token}&type=bind`
})
},
bindLogin() {
if (this.account == "") {
uni.showToast({
title: "请输入账号",
duration: 1000,
icon: 'none',
})
return
}
if (this.password == "") {
uni.showToast({
title: "请输入密码",
duration: 1000,
icon: 'none',
})
return
}
Api.authLogin({
data: {
"unique_token": this.unique_token,
"user_no": this.isQuick ? this.phone : this.account,
"third_type": 1
},
}).then((result) => {
console.log("登录用户信息:", result);
if (result.code === 0) {
uni.setStorageSync('isCanUse', false); //记录是否第一次授权 false:表示不是第一次授权
uni.setStorageSync("short_token", result.data.short_token)
uni.setStorageSync("token", result.data.access_token)
uni.setStorageSync("user_info", result.data)
uni.showToast({
title: "授权登录成功",
icon: 'none'
})
uni.switchTab({
url: "/pages/index/index"
})
} else {
uni.showToast({
title: result.msg,
icon: 'none'
})
}
})
},
}
}
</script>
<style lang="scss">
.login-parent {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
.login-content {
width: 100%;
display: flex;
margin-top: 100px;
flex-direction: column;
align-items: center;
justify-content: center;
button {
background-color: #007AFF;
width: 70%;
color: #FFFFFF;
margin-top: 30px;
}
.login-logo {
image {
width: 80px;
height: 80px;
}
}
.login-app-name {
margin-top: 10px;
text {
font-size: 16px;
color: #333333;
}
}
.login-type {
display: flex;
height: 50px;
flex-direction: row;
padding: 20px;
align-items: center;
justify-content: center;
margin-top: 80px;
.line {
width: 20px;
display: flex;
align-items: center;
justify-content: center;
color: #E5E5E5;
}
.login-type-select {
font-size: 16px;
color: #1890FF;
}
.login-type-unselect {
font-size: 16px;
color: #333333;
}
}
.login-layout {
margin-top: 30px;
display: flex;
width: 70%;
flex-direction: column;
.login-account {
display: flex;
width: 100%;
align-items: center;
flex-direction: row;
flex-wrap: nowrap;
text {
font-size: 14px;
color: #333333;
}
input {
flex: 1;
padding: 5px;
margin-left: 10px;
font-size: 14px;
border-bottom: 1px solid #555555;
color: #333333;
}
}
.login-password {
margin-top: 20px;
display: flex;
align-items: center;
width: 100%;
flex-direction: row;
flex-wrap: nowrap;
input {
padding: 5px;
flex: 1;
margin-left: 10px;
font-size: 14px;
border-bottom: 1px solid #555555;
color: #333333;
}
text {
font-size: 14px;
color: #333333;
}
.login-code {
font-size: 12px;
color: #1890FF;
padding: 5px 10px;
border-radius: 2px;
}
}
}
}
}
</style>
<template>
<view class="content">
<titleBar title="切换组织"></titleBar>
<view class="org-content">
<view class="org-list">
<view class="org-item" v-for="(item,index) in list" :key="index">
<view class="org">
<view class="name">{{item.org_name}}</view>
<view class=" enter" @click="itemClick(item)">{{isCurrent(item)?"当前组织":"进入"}}</view>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
import Api from "../../../api/api.js"
export default {
data() {
return {
list: []
}
},
onLoad() {
this.getCompany()
},
methods: {
getCompany() {
Api.getCompany({
data: {
is_page: 0,
}
}).then((result) => {
console.log("单位列表", result);
if (result.code == 0) {
this.list = result.data.list
}
})
},
isCurrent(item) {
return item.company_id == uni.getStorageSync("company_id")
},
itemClick: function(item) {
if (this.isCurrent()) {
return
}
let that = this
uni.showModal({
title: '提示',
content: '确定切换到该单位?',
showCancel: true,
confirmText: "切换",
confirmColor: '#1890FF',
success: res => {
if (res.confirm) {
that.switchCompany(item)
}
}
})
},
switchCompany: function(company) {
this.$http.consolePut({
url: "/api/v1/user/user/switch_company",
data: {
company_id: company.company_id
},
header: {
access_token: this.getToken()
}
}).then((res) => {
let result = res.data
if (result.code == 0) {
uni.setStorageSync("token", result.data.access_token)
uni.setStorageSync("refresh_token", result.data.refresh_token)
uni.setStorageSync("company_id", company.company_id)
this.currentCompany = company
uni.setStorageSync("user_info", result.data)
this.getUserInfo()
} else {
this.$toast.showToast(result.msg)
}
})
},
getUserInfo() {
Api.getUserInfo().then((result) => {
if (result.code == 0) {
getApp().globalData.userInfo = result.data
uni.navigateBack({
delta: 1
})
} else {
this.$toast.showToast(result.msg)
}
})
},
}
}
</script>
<style lang="scss">
.content {
display: flex;
flex-direction: column;
.org-content {
display: flex;
flex-direction: column;
margin: 30rpx;
}
.org-item {
padding: 30rpx 0 30rpx 30rpx;
display: flex;
flex-direction: column;
border-radius: 30rpx;
background-color: #fff;
}
.org {
display: flex;
flex-direction: row;
align-items: center;
image {
width: 48rpx;
height: 24rpx;
}
.name {
font-size: 28rpx;
color: #333;
margin-left: 20rpx;
flex: 1;
font-weight: 500;
}
.enter {
font-size: 28rpx;
margin-right: 30rpx;
color: #3B73FE;
font-weight: 600;
}
}
}
</style>
...@@ -6,34 +6,35 @@ ...@@ -6,34 +6,35 @@
<view class="info-content" :style="{top:statusBarHeight+45+'px'}"> <view class="info-content" :style="{top:statusBarHeight+45+'px'}">
<image src="../../static/mine-bg.png" class="image"></image> <image src="../../static/mine-bg.png" class="image"></image>
<view class="company">{{currentJob.org_name||""}}</view> <view class="company">{{currentJob.org_name||""}}</view>
<view class="name">{{userInfo.user.user_name||""}} {{currentJob.job_name||""}}</view> <view class="name">{{userInfo.user.user_name||""}}</view>
<view class="job">{{currentJob.job_name||""}}</view>
<view class="project">{{currentJob.dept_name||""}}</view> <view class="project">{{currentJob.dept_name||""}}</view>
<image src="../../static/avatar-def.png" class="avatar"></image> <image src="../../static/avatar-def.png" class="avatar"></image>
</view> </view>
</view> </view>
<view class="func-content" :style="{top:statusBarHeight+230+'px'}"> <view class="func-content" :style="{top:statusBarHeight+250+'px'}">
<view class="item" v-if="isShow('项目信息')" @click="linkTo('/pages/mine/projectInfo/projectInfo')"> <view class="item" v-if="isShow('项目信息')" @click="linkTo('/pagesSub/mine/projectInfo/projectInfo')">
<view class="name">项目信息</view> <view class="name">项目信息</view>
<image src="../../static/right-arrow.png" class="image"></image> <image src="../../static/right-arrow.png" class="image"></image>
</view> </view>
<view class="line" v-if="isShow('项目信息')"></view> <view class="line" v-if="isShow('项目信息')"></view>
<view class="item" v-if="false" @click="linkTo('/pages/mine/projectHistory/projectHistory')"> <view class="item" v-if="false" @click="linkTo('/pagesSub/mine/projectHistory/projectHistory')">
<view class="name">项目过往</view> <view class="name">项目过往</view>
<image src="../../static/right-arrow.png" class="image"></image> <image src="../../static/right-arrow.png" class="image"></image>
</view> </view>
<view class="line" v-if="false"></view> <view class="line" v-if="false"></view>
<view class="item" @click="linkTo('/pages/mine/myApproval/myApproval')" v-if="isShow('我的审批')"> <view class="item" @click="linkTo('/pagesSub/mine/myApproval/myApproval')" v-if="isShow('我的审批')">
<view class="name">我的审批</view> <view class="name">我的审批</view>
<view class="red-dot" v-if="count>0"></view> <view class="red-dot" v-if="count>0"></view>
<image src="../../static/right-arrow.png" class="image"></image> <image src="../../static/right-arrow.png" class="image"></image>
</view> </view>
<view class="line" v-if="isShow('我的审批')"></view> <view class="line" v-if="isShow('我的审批')"></view>
<view class="item" @click="linkTo('/pages/mine/myScore/myScore')" v-if="isShow('我的积分')"> <view class="item" @click="linkTo('/pagesSub/mine/myScore/myScore')" v-if="isShow('我的积分')">
<view class="name">我的积分</view> <view class="name">我的积分</view>
<image src="../../static/right-arrow.png" class="image"></image> <image src="../../static/right-arrow.png" class="image"></image>
</view> </view>
<view class="line" v-if="isShow('我的积分')"></view> <view class="line" v-if="isShow('我的积分')"></view>
<view class="item" @click="linkTo('/pages/login/joinOrg/joinOrg')" v-if="isShow('加入组织')"> <view class="item" @click="linkTo('/pagesSub/login/joinOrg/joinOrg')" v-if="isShow('加入组织')">
<view class="name">加入组织</view> <view class="name">加入组织</view>
<image src="../../static/right-arrow.png" class="image"></image> <image src="../../static/right-arrow.png" class="image"></image>
</view> </view>
...@@ -41,7 +42,7 @@ ...@@ -41,7 +42,7 @@
<view class="version-content"> <view class="version-content">
<text class="text">当前版本:v1.0.0</text> <text class="text">当前版本:v1.0.0</text>
</view> </view>
<view class="btn-org" @click="linkTo('/pages/mine/changeCompany/changeCompany')" v-if="role!='VISITOR'"> <view class="btn-org" @click="linkTo('/pagesSub/mine/changeCompany/changeCompany')" v-if="role!='VISITOR'">
<text class="text">切换组织</text> <text class="text">切换组织</text>
</view> </view>
<view class="btn-logout" @click="toLinkLogin"> <view class="btn-logout" @click="toLinkLogin">
...@@ -96,6 +97,9 @@ ...@@ -96,6 +97,9 @@
return this.role == 'VISITOR' return this.role == 'VISITOR'
} }
if (name == '项目信息') { if (name == '项目信息') {
if (this.userInfo.user.dept_type != 2) { //企业级不显示项目
return false
}
if (this.role == 'PM') { if (this.role == 'PM') {
return true return true
} else if (this.role == 'WORKER') { } else if (this.role == 'WORKER') {
...@@ -250,17 +254,27 @@ ...@@ -250,17 +254,27 @@
font-weight: 500; font-weight: 500;
font-size: 28rpx; font-size: 28rpx;
left: 72rpx; left: 72rpx;
top: 100rpx; top: 80rpx;
text-align: center; text-align: center;
color: #333; color: #333;
} }
.name { .name {
position: absolute; position: absolute;
font-weight: 600;
font-size: 28rpx;
left: 72rpx;
top: 140rpx;
text-align: center;
color: #333;
}
.job {
position: absolute;
font-weight: 400; font-weight: 400;
font-size: 28rpx; font-size: 28rpx;
left: 72rpx; left: 72rpx;
top: 200rpx; top: 180rpx;
text-align: center; text-align: center;
color: #333; color: #333;
} }
...@@ -271,7 +285,7 @@ ...@@ -271,7 +285,7 @@
font-size: 28rpx; font-size: 28rpx;
width: 630rpx; width: 630rpx;
left: 72rpx; left: 72rpx;
top: 245rpx; top: 235rpx;
text-align: left; text-align: left;
color: #333; color: #333;
display: -webkit-box; display: -webkit-box;
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
</template> </template>
<script> <script>
import configEnv from "../../api/env.js"
export default { export default {
data() { data() {
return { return {
...@@ -96,7 +97,7 @@ ...@@ -96,7 +97,7 @@
if (res.data.code === 0) { if (res.data.code === 0) {
let list = res.data.data.list let list = res.data.data.list
list.forEach((item) => { list.forEach((item) => {
if (item.app_no == "20230207002") { if (item.app_no == configEnv.labourAppNo) {
this.apps.push(item) this.apps.push(item)
} }
}) })
...@@ -112,7 +113,7 @@ ...@@ -112,7 +113,7 @@
uni.navigateTo({ uni.navigateTo({
url: `/pages/monitor/zhgdMonitor/zhgdMonitor?type=${this.type}&project_id=${this.project_id}&project_name=${this.orgName}` url: `/pages/monitor/zhgdMonitor/zhgdMonitor?type=${this.type}&project_id=${this.project_id}&project_name=${this.orgName}`
}) })
} else if (app.app_no == "20230207002") { } else if (app.app_no == configEnv.labourAppNo) {
uni.navigateTo({ uni.navigateTo({
url: `/pages/monitor/labourMonitor/labourMonitor?type=${this.type}&project_id=${this.project_id}&project_name=${this.orgName}` url: `/pages/monitor/labourMonitor/labourMonitor?type=${this.type}&project_id=${this.project_id}&project_name=${this.orgName}`
}) })
......
<template>
<view>
</view>
</template>
<script>
export default {
data() {
return {
}
},
methods: {
}
}
</script>
<style>
</style>
<template>
<view class="content">
<titleBar title="切换组织"></titleBar>
<view class="org-content">
<view class="org-list">
<view class="org-item" v-for="(item,index) in list" :key="index">
<view class="org" @click="imgClici(item,index)">
<image :src="setImageSrc(item.isOpen)" :class="item.isOpen?'o-image':'c-image'"></image>
<view class="name">{{item.org_name}}</view>
<view class="enter">{{isCurrent(item)?"当前组织":""}}</view>
</view>
<view class="job-content" v-if="item.isOpen">
<view class="j-item" v-for="(job,jIndex) in item.job_list" :key="jIndex"
@click="jobSel(item,job)">
<view class="j-name">
{{job.dept_name}}-{{job.job_name}}
</view>
<image :src="setSelImage(job)" class="image"></image>
</view>
</view>
</view>
</view>
</view>
<view class="uni-group-button">
<view class="uni-blue-button" @click="toSwitch">确认切换该组织/岗位</view>
</view>
</view>
</template>
<script>
import Api from "../../../api/api.js"
export default {
data() {
return {
list: [],
currentJob: 0,
currentCompany: {}
}
},
onLoad() {
this.getCompany()
var userInfo = getApp().globalData.userInfo
this.currentCompany = {
company_id: userInfo.user.comp_id
}
var jobs = userInfo.user.job_list
this.currentJob = jobs[0]
},
methods: {
imgClici(item, index) {
item.isOpen = !item.isOpen
this.$set(this.list, index, item)
},
jobSel(item, job) {
this.currentCompany = item
this.currentJob = job
},
setSelImage(job) {
return this.isCurrentJob(job) ? '../../../static/sel-ic.png' : '../../../static/unselected-ic.png'
},
setImageSrc(isOpen) {
return isOpen ? '../../../static/icon_drop_arrow.png' : '../../../static/right-arrow.png'
},
getCompany() {
Api.getCompanyJobList({
data: {
is_page: 0,
}
}).then((result) => {
console.log("单位列表", result);
if (result.code == 0) {
var list = result.data.list
if (list) {
list.forEach((item) => {
if (this.isCurrent(item)) {
item.isOpen = true
} else {
item.isOpen = false
}
this.list.push(item)
})
}
}
})
},
isCurrent(item) {
var orgId = uni.getStorageSync("org_id")
console.log("当前org_id:" + orgId, "条目的org_id:" + item.org_id);
return item.org_id == orgId && item.company_id == this.currentCompany.company_id
},
isCurrentJob(job) {
return job.job_id == this.currentJob.job_id
},
toSwitch() {
var userInfo = getApp().globalData.userInfo
var jobs = userInfo.user.job_list
if (this.currentJob.job_id == jobs[0].job_id) { //没有任何变化 直接返回
uni.navigateBack({
delta: 1
})
} else {
let that = this
uni.showModal({
title: '提示',
content: '确定切换到该岗位',
showCancel: true,
confirmText: "切换",
confirmColor: '#3B73FE',
success: res => {
if (res.confirm) {
that.switchCompany()
}
}
})
}
},
switchCompany() {
this.$http.consolePut({
url: "/api/v1/user/user/switch_company",
data: {
company_id: this.currentCompany.company_id
},
header: {
access_token: this.getToken()
}
}).then((res) => {
let result = res.data
if (result.code == 0) {
// this.getUserInfo()
uni.setStorageSync("token", result.data.access_token)
uni.setStorageSync("short_token", result.data.short_token)
uni.setStorageSync("user_info", result.data)
uni.setStorageSync("company_id", result.data.comp_id)
uni.setStorageSync("org_no", result.data.org_no)
uni.setStorageSync("org_id", result.data.org_id)
if (this.currentJob.job_id != result.data.job_id) {
this.switchJob()
} else {
getApp().globalData.isInit = true
uni.switchTab({
url: "/pages/index/index"
})
}
} else {
this.$toast.showToast(result.msg)
}
})
},
switchJob() {
Api.switchJob({
data: {
job_id: this.currentJob.job_id
}
}).then((result) => {
console.log("切换岗位", result);
if (result.code == 0) {
uni.setStorageSync("token", result.data.access_token)
uni.setStorageSync("short_token", result.data.short_token)
uni.setStorageSync("user_info", result.data)
uni.setStorageSync("company_id", result.data.comp_id)
uni.setStorageSync("org_no", result.data.org_no)
uni.setStorageSync("org_id", result.data.org_id)
console.log("当前组织id", uni.getStorageSync("org_id"));
getApp().globalData.isInit = true
uni.switchTab({
url: "/pages/index/index"
})
} else {
this.$toast.showToast(result.msg)
}
})
},
getUserInfo() {
Api.getUserInfo().then((result) => {
if (result.code == 0) {
getApp().globalData.userInfo = result.data
uni.navigateBack({
delta: 1
})
} else {
this.$toast.showToast(result.msg)
}
})
},
}
}
</script>
<style lang="scss">
.content {
display: flex;
flex-direction: column;
.org-content {
display: flex;
flex-direction: column;
margin: 30rpx;
}
.org-item {
margin-bottom: 30rpx;
padding: 30rpx 0 30rpx 30rpx;
display: flex;
flex-direction: column;
border-radius: 30rpx;
background-color: #fff;
.org {
display: flex;
flex-direction: row;
align-items: center;
.c-image {
width: 15rpx;
height: 24rpx;
}
.o-image {
width: 48rpx;
height: 24rpx;
}
.name {
font-size: 28rpx;
color: #333;
margin-left: 20rpx;
flex: 1;
font-weight: 500;
}
.enter {
font-size: 28rpx;
margin-right: 30rpx;
color: #3B73FE;
font-weight: 600;
}
}
.job-content {
display: flex;
flex-direction: column;
margin-left: 40rpx;
.j-item {
padding: 30rpx 30rpx 0 30rpx;
display: flex;
align-items: center;
flex-direction: row;
.j-name {
width: 500rpx;
display: -webkit-box;
font-weight: 400;
font-size: 14px;
color: #333;
// overflow: hidden;
// white-space: nowrap;
// word-wrap: break-word;
// text-overflow: ellipsis;
// white-space: normal !important;
// -webkit-line-clamp: 1;
// -webkit-box-orient: vertical;
}
image {
margin-left: 30rpx;
width: 40rpx;
height: 40rpx;
}
}
}
}
}
</style>
...@@ -4,16 +4,25 @@ ...@@ -4,16 +4,25 @@
<view class="search"> <view class="search">
<view class="search-content"> <view class="search-content">
<image src="../../../static/ic-search.png" class="image"></image> <image src="../../../static/ic-search.png" class="image"></image>
<input placeholder="搜索" placeholder-class="placeholderStyle" @input="search" class="inputStyle" /> <input placeholder="搜索" placeholder-class="placeholderStyle" @input="search" class="inputStyle"
v-model="key" />
</view> </view>
</view> </view>
<view class="org-content"> <ly-tree :tree-data="trees" :props="treeProps" node-key="id" @node-expand="handleNodeExpand"
@node-click="handleNodeClick" :showRadio="true" ref="tree" :defaultCheckedKeys="currentKeys"
:defaultExpandedKeys="currentKeys" childVisibleForFilterNode="true" :filter-node-method="filterNode"
:checkOnClickNode="true" :childVisibleForFilterNode="true">
</ly-tree>
<view class="uni-group-button">
<view class="uni-blue-button" @click="toSwitch">确认切换该组织</view>
</view>
<!-- 暂时处理 -->
<view class="org-content" v-if="false">
<view class="org-list"> <view class="org-list">
<view class="org-item" v-for="(tree,index) in trees" :key="index"> <view class="org-item" v-for="(tree,index) in trees" :key="index">
<view class="org"> <view class="org">
<image <image v-if="isImgShow(tree)" :src="setImgSrc(tree)" class="image"
:src="tree.isOpen?'../../../static/icon_up_arrow.png':'../../../static/icon_drop_arrow.png'" @click="toNextLevel(tree,index)">
class="image" @click="toNextLevel(tree,index)">
</image> </image>
<view class="name" @click="toNextLevel(tree,index)">{{tree.name}}</view> <view class="name" @click="toNextLevel(tree,index)">{{tree.name}}</view>
<view class=" enter" @click="itemClick(tree)">进入</view> <view class=" enter" @click="itemClick(tree)">进入</view>
...@@ -21,10 +30,8 @@ ...@@ -21,10 +30,8 @@
<view v-if="tree.isOpen" style="margin-top: 20rpx;"> <view v-if="tree.isOpen" style="margin-top: 20rpx;">
<view class="org-item" v-for="(oneLevel,oneIndex) in tree.children_list" :key="oneIndex"> <view class="org-item" v-for="(oneLevel,oneIndex) in tree.children_list" :key="oneIndex">
<view class="org"> <view class="org">
<image <image v-if="isImgShow(oneLevel)" :src="setImgSrc(oneLevel)" class="image"
v-if="!oneLevel.project_id&&oneLevel.children_list&&oneLevel.children_list.length>0" @click="toNextChild(index,oneLevel,oneIndex)"></image>
:src="oneLevel.isOpen?'../../../static/icon_up_arrow.png':'../../../static/icon_drop_arrow.png'"
class="image" @click="toNextChild(index,oneLevel,oneIndex)"></image>
<view class="name" @click="toNextChild(index,oneLevel,oneIndex)">{{oneLevel.name}} <view class="name" @click="toNextChild(index,oneLevel,oneIndex)">{{oneLevel.name}}
</view> </view>
<view class="enter" @click="itemClick(oneLevel)">进入</view> <view class="enter" @click="itemClick(oneLevel)">进入</view>
...@@ -51,14 +58,74 @@ ...@@ -51,14 +58,74 @@
export default { export default {
data() { data() {
return { return {
key: "",
trees: [], trees: [],
temp: [] temp: [],
treeProps: function() {
return {
label: 'name', // 指把数据中的‘personName’当做label也就是节点名称
children: 'children_list' // 指
}
},
currentKeys: []
} }
}, },
onLoad() { onLoad() {
var currentId = uni.getStorageSync("org_id")
if (currentId) {
this.currentKeys.push(currentId)
}
console.log("当前选中", this.currentKey);
this.getOrgTree() this.getOrgTree()
}, },
watch: {
key(val) {
this.$refs.tree.filter(val);
}
},
methods: { methods: {
handleNodeClick(obj) {
// console.log('handleNodeClick', obj);
},
handleNodeExpand(obj) {
},
filterNode(value, data) {
if (!value) return true;
return data.name.indexOf(value) != -1;
},
toSwitch() {
var node = this.$refs.tree.getCheckedNodes()
if (node.length < 0) {
this.$toast.showToast("请选择切换的组织")
return
}
let that = this
uni.showModal({
title: '提示',
content: '确定切换到该组织?',
showCancel: true,
confirmText: "切换",
confirmColor: '#3B73FE',
success: res => {
if (res.confirm) {
that.switchOrg(node[0])
}
}
})
},
setImgSrc(item) {
return item.isOpen ? '../../../static/icon_up_arrow.png' : '../../../static/icon_drop_arrow.png'
},
isImgShow(level) {
if (level.project_id) {
return false
}
if (level.children_list && level.children_list.length > 0) {
return true
}
return false
},
dealProject(list) { dealProject(list) {
var projects = [] var projects = []
list.forEach((item) => { list.forEach((item) => {
...@@ -73,7 +140,8 @@ ...@@ -73,7 +140,8 @@
Api.getOrgTree({ Api.getOrgTree({
data: { data: {
company_id: uni.getStorageSync("company_id"), company_id: uni.getStorageSync("company_id"),
org_no: getApp().globalData.userInfo.user.org_no org_no: getApp().globalData.userInfo.user.org_no,
show_current_level: 1
} }
}).then((result) => { }).then((result) => {
console.log("组织树", result); console.log("组织树", result);
...@@ -85,11 +153,11 @@ ...@@ -85,11 +153,11 @@
item.dept_no == getApp().globalData.userInfo.user.dept_no item.dept_no == getApp().globalData.userInfo.user.dept_no
) )
} else { } else {
if (list[0].no == '001') { // if (list[0].no == '001') {
this.trees = list[0].children_list // this.trees = list[0].children_list
} else { // } else {
this.trees = list this.trees = list
} // }
} }
this.temp = list[0].children_list this.temp = list[0].children_list
} }
...@@ -155,65 +223,6 @@ ...@@ -155,65 +223,6 @@
delta: 1 delta: 1
}) })
}, },
search(e) {
var key = e.detail.value
if (key) {
var list = []
var sourceData = this.temp
console.log("数据源", sourceData);
sourceData.forEach((item) => {
if (item.name.indexOf(key) != -1) {
item.isOpen = true
list.push(item)
} else {
var children = item.children_list
var childrens = []
if (children) {
children.forEach((child) => {
if (child.name.indexOf(key) != -1) {
child.isOpen = true
var childs = child.children_list
var childList = []
if (childs) {
childs.forEach((childItem) => {
if (childItem.name.indexOf(key) != -1) {
childList.push(childItem)
}
})
}
child.children_list = childList
childrens.push(child)
} else {
var childs = child.children_list
var childList = []
if (childs) {
childs.forEach((childItem) => {
if (childItem.name.indexOf(key) != -1) {
childList.push(childItem)
}
})
}
if (childList.length > 0) {
child.children_list = childList
child.isOpen = true
childrens.push(child)
}
}
})
}
if (childrens.length > 0) {
item.isOpen = true
item.children_list = childrens
list.push(item)
}
}
})
this.trees = list
} else {
this.getOrgTree()
}
}
} }
} }
</script> </script>
...@@ -227,7 +236,7 @@ ...@@ -227,7 +236,7 @@
display: flex; display: flex;
flex-direction: row; flex-direction: row;
align-items: center; align-items: center;
margin: 30rpx 30rpx 0 30rpx; margin: 30rpx;
.search-content { .search-content {
display: flex; display: flex;
......
...@@ -69,6 +69,7 @@ ...@@ -69,6 +69,7 @@
uni.showModal({ uni.showModal({
title: '提示', title: '提示',
content: '是否取消兑换', content: '是否取消兑换',
confirmColor: '#3B73FE',
success: (res) => { success: (res) => {
if (res.confirm) { if (res.confirm) {
Api.cancelExchange({ Api.cancelExchange({
...@@ -94,6 +95,7 @@ ...@@ -94,6 +95,7 @@
uni.showModal({ uni.showModal({
title: '提示', title: '提示',
content: '是否确认领取', content: '是否确认领取',
confirmColor: '#3B73FE',
success: (res) => { success: (res) => {
if (res.confirm) { if (res.confirm) {
Api.confirmReceive({ Api.confirmReceive({
......
<template>
<view class="history-score">
<titleBar title="历史积分明细"></titleBar>
<view class="choose-date">
<uni-datetime-picker @change="toggleDate" returnType="string" v-model="requestParam.time" type="date">
</uni-datetime-picker>
</view>
<view class="current-score">
<view class="score">
<view class="date">
{{requestParam.time}}当日积分
</view>
<view class="value">
+{{scoreDetail.current_integral||'0'}}
</view>
</view>
<view class="date">
</view>
</view>
<view class="score-list">
<view class="score-list-item" v-for="(item,index) in scoreDetail.listData" :key="index">
<text class="time">{{item.time}}</text>
<text class="desc">{{item.item_name}}</text>
<text class="score">+{{item.integral}}</text>
</view>
</view>
</view>
</template>
<script>
import Api from "../../../api/api.js"
export default {
data() {
return {
userInfo: {},
project: {},
requestParam: {
project_id: '',
time: '',
worker_id: ''
},
scoreDetail: {
listData: []
}
}
},
onShow() { //监听页面显示
this.userInfo = getApp().globalData.userInfo
this.project_id = this.userInfo.currentProject.projectId
let date = new Date()
let year = date.getFullYear()
let month = date.getMonth() + 1
let day = date.getDate()
this.requestParam.time = year + '-' + month + '-' + day
this.requestParam.worker_id = this.userInfo.worker.workerId || ''
this.requestParam.project_id = this.project_id || ''
this.getScoreDetail() //暂时先注释掉02.16
},
methods: {
// 切换时间
toggleDate(date) {
this.requestParam.time = date
this.getScoreDetail()
},
// 获取积分数据
getScoreDetail() {
Api.getUserScore({
data: this.requestParam
}).then(res => {
if (res.code === 0) {
this.scoreDetail = res.data
}
}, (err) => {
this.$toast.showToast(err.msg)
})
},
}
}
</script>
<style lang="scss">
.history-score {
.current-score {
display: flex;
justify-content: space-between;
align-items: center;
padding: 30rpx;
margin: 0 32rpx;
background-color: #FAFAFA;
margin-top: 32rpx;
.score {
color: #666;
font-size: 24rpx;
}
.value {
color: #333;
font-size: 48rpx;
font-weight: bold;
}
.date {}
}
.choose-date {
padding: 0 30rpx;
margin: 30rpx 0 0 0;
}
.score-list {
display: flex;
flex-direction: column;
margin: 30rpx;
background-color: #FAFAFA;
.score-list-item {
display: flex;
justify-content: space-between;
align-items: center;
height: 104rpx;
padding: 0 30rpx;
position: relative;
border-bottom: 1px solid rgba($color: #999, $alpha: 0.5);
&:last-child {
.line {
display: none;
}
}
// .circle{
// border:1px solid #1890ff;
// border-radius: 50%;
// background-color: #FFFFFF;
// width: 20rpx;
// height: 20rpx;
// }
// .line{
// position: absolute;
// width: 2rpx;
// height:100rpx;
// background-color: #000;
// left: 10rpx;
// opacity: 0.5;
// bottom: -90rpx;
// }
.time {
font-size: 28rpx;
color: #666;
}
.desc {
font-size: 28rpx;
color: #666;
width: 250rpx;
}
.score {
font-size: 28rpx;
color: #333;
width: 60rpx;
text-align: right;
}
}
}
}
</style>
...@@ -7,7 +7,9 @@ ...@@ -7,7 +7,9 @@
<image src="../../../static/ic-coin.png" class="image"></image> <image src="../../../static/ic-coin.png" class="image"></image>
<view class="score-count-content"> <view class="score-count-content">
<view class="count">{{scoreDetail.integral||0}}</view> <view class="count">{{scoreDetail.integral||0}}</view>
<view class="unit">积分</view> <view class="s-detail" @click="toDetail">
<view class="unit">积分详情</view>
</view>
</view> </view>
<view class="history-content" @click="toHistory"> <view class="history-content" @click="toHistory">
<text class="text">兑换历史</text> <text class="text">兑换历史</text>
...@@ -89,6 +91,11 @@ ...@@ -89,6 +91,11 @@
this.getScoreDetail() this.getScoreDetail()
}, },
methods: { methods: {
toDetail() {
uni.navigateTo({
url: "/pagesSub/mine/scoreDetail/scoreDetail"
})
},
getScoreDetail() { getScoreDetail() {
var date = new Date() var date = new Date()
Api.getUserScore({ Api.getUserScore({
...@@ -129,7 +136,7 @@ ...@@ -129,7 +136,7 @@
}, },
toHistory() { toHistory() {
uni.navigateTo({ uni.navigateTo({
url: "/pages/mine/historyScore/historyScore" url: "/pagesSub/mine/historyScore/historyScore"
}) })
}, },
leftList() { leftList() {
...@@ -244,12 +251,24 @@ ...@@ -244,12 +251,24 @@
font-weight: bold; font-weight: bold;
} }
.unit { .s-detail {
font-size: 28rpx; width: 136rpx;
color: #fff; height: 48rpx;
display: flex;
align-items: center;
justify-content: center;
border-radius: 24rpx;
margin-left: 20rpx; margin-left: 20rpx;
font-weight: 400; background: #fff;
.unit {
font-size: 24rpx;
color: #3b73fe;
font-family: "PingFang SC";
font-weight: 400;
}
} }
} }
.history-content { .history-content {
......
...@@ -33,7 +33,7 @@ ...@@ -33,7 +33,7 @@
<view class="line"></view> <view class="line"></view>
<view class="item"> <view class="item">
<view class="item-name">详细地址:</view> <view class="item-name">详细地址:</view>
<view class="item-value" style="width: 488rpx;">{{project.address||""}}</view> <text class="item-value" style="width: 488rpx;">{{project.address||""}}</text>
</view> </view>
<view class="line"></view> <view class="line"></view>
<view class="item"> <view class="item">
...@@ -79,12 +79,17 @@ ...@@ -79,12 +79,17 @@
markers: [] markers: []
} }
}, },
onLoad() { onLoad(options) {
this.role = getApp().globalData.userInfo.identity.code var from = options.from
if (this.role == 'WORKER') { if (from && from == 'qrcode') {
this.projectId = getApp().globalData.userInfo.worker.projectId this.projectId = options.projectId
} else { } else {
this.projectId = uni.getStorageSync("project_id") this.role = getApp().globalData.userInfo.identity.code
if (this.role == 'WORKER') {
this.projectId = getApp().globalData.userInfo.worker.projectId
} else {
this.projectId = uni.getStorageSync("project_id")
}
} }
this.getProjectInfo() this.getProjectInfo()
}, },
...@@ -242,6 +247,7 @@ ...@@ -242,6 +247,7 @@
.item-value { .item-value {
font-weight: 400; font-weight: 400;
font-size: 28rpx; font-size: 28rpx;
text-align: right;
color: #333; color: #333;
} }
......
<template>
<view class="my-score-container">
<titleBar title="积分详情"></titleBar>
<view class="score-info">
<view class="my-score">
<view class="left">
<image src="../../../static/jf-s-ic.png"></image>
<view class="detail">
<view class="text">
当前积分
</view>
<text class="score">{{scoreDetail.integral || 0}}</text>
</view>
</view>
<view class="right">
<button @click="toScoreMall" type="primary" size="mini">我要兑换</button>
</view>
</view>
<view class="score-detail">
<view class="score-item" @click="toHistoryDetail">
<view class="s-left">
<text class="name">历史积分</text>
<text class="score">{{scoreDetail.his_integral || 0}}</text>
</view>
<image src="../../../static/s-arrow-left-ic.png" class="image"></image>
</view>
<view class="score-item" @click="toExcahngeDetail">
<view class="s-left">
<text class="name">已兑换积分</text>
<text class="score">{{scoreDetail.ex_integral || 0}}</text>
</view>
<image src="../../../static/s-arrow-left-ic.png" class="image"></image>
</view>
</view>
<view class="score-btn">
<view class="b-add-score" @click="toAddScore">
<image src="../../../static/jjf-ic.png" class="image"></image>
<text class="name">我要加积分</text>
</view>
<view class="b-add-score" @click="toShare">
<image src="../../../static/s-wechat-ic.png" class="image"></image>
<button open-type="share" class="button">微信分享</button>
</view>
</view>
</view>
<view class="choose-date">
<uni-datetime-picker @change="toggleDate" returnType="string" v-model="requestParam.time" type="date">
</uni-datetime-picker>
</view>
<view class="current-score">
<view class="score">
<view class="date">
{{requestParam.time}}当日积分
</view>
<view class="value">
+{{scoreDetail.current_integral||'0'}}
</view>
</view>
<view class="date">
明细
<!-- {{requestParam.time}} -->
</view>
</view>
<view class="score-list">
<view class="score-list-item" v-for="(item,index) in scoreDetail.listData" :key="index">
<text class="time">{{item.time}}</text>
<text class="desc">{{item.item_name}}</text>
<text class="score">+{{item.integral}}</text>
</view>
</view>
</view>
</template>
<script>
import Api from "../../../api/api.js"
export default {
data() {
return {
userInfo: {},
project: {},
requestParam: {
project_id: '',
time: '',
worker_id: ''
},
scoreDetail: {
listData: [{
time: '18:00',
item_name: '签到',
integral: '10'
},
{
time: '18:00',
item_name: '签到',
integral: '10'
},
{
time: '18:00',
item_name: '签到',
integral: '10'
},
{
time: '18:00',
item_name: '签到',
integral: '10'
},
{
time: '18:00',
item_name: '签到',
integral: '10'
},
]
},
projectList: []
}
},
onShow() {
this.userInfo = getApp().globalData.userInfo
this.project_id = this.userInfo.currentProject.projectId
let date = new Date()
let year = date.getFullYear()
let month = date.getMonth() + 1
let day = date.getDate()
this.requestParam.time = year + '-' + month + '-' + day
this.requestParam.worker_id = this.userInfo.worker.workerId || ''
this.requestParam.project_id = this.project_id || ''
this.getScoreDetail()
},
methods: {
toShare() {},
toHistoryDetail() {
uni.navigateTo({
url: "/pagesSub/mine/historyScoreDetail/historyScoreDetail"
})
},
toExcahngeDetail() {
uni.navigateTo({
url: "/pagesSub/mine/historyScore/historyScore"
})
},
toAddScore() {
uni.showToast({
title: '功能开发中',
icon: 'none'
})
},
// 切换时间
toggleDate(date) {
this.requestParam.time = date
this.getScoreDetail()
},
// 获取积分数据
getScoreDetail() {
Api.getUserScore({
data: this.requestParam
}).then(res => {
if (res.code === 0) {
this.scoreDetail = res.data
}
}, (err) => {
this.$toast.showToast(err.msg)
})
},
toScoreMall() {
uni.navigateTo({
url: `/pages/ScoreMall/ScoreMall`
})
},
}
}
</script>
<style lang="scss">
.my-score-container {
.score-info {
margin: 30rpx;
padding: 30rpx;
background-color: #FFF7E6;
background-image: url(../../../static/score-bg.png);
background-size: cover;
border-radius: 30rpx;
.my-score {
display: flex;
justify-content: space-between;
.left {
display: flex;
justify-content: space-between;
align-items: center;
image {
width: 80rpx;
height: 80rpx;
}
.detail {
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: flex-start;
margin-left: 16rpx;
.text {
font-size: 24rpx;
color: #FA2616;
}
.score {
font-size: 48rpx;
color: #FA2616;
font-weight: bold;
}
}
}
.right {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
button {
background: linear-gradient(230.81deg, #fa8c16 0%, #fa2616 100%);
width: 160rpx;
height: 64rpx;
border-radius: 32rpx;
color: #fff;
font-size: 28rpx;
padding: 0;
}
}
}
.score-detail {
display: flex;
justify-content: space-between;
align-items: center;
flex-direction: row;
margin-top: 32rpx;
.score-item {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
width: 300rpx;
border-radius: 16rpx;
background-color: #FFEDC2;
height: 108rpx;
.s-left {
display: flex;
align-items: center;
margin-left: 30rpx;
flex-direction: column;
.score {
font-size: 34rpx;
color: #FF7700;
font-weight: bold;
text-align: center;
}
.name {
text-align: center;
font-size: 24rpx;
color: #FA8C16;
}
}
image {
width: 36rpx;
margin-right: 30rpx;
height: 72rpx;
}
&.add-score-container {
align-items: center;
image {
width: 40rpx;
height: 40rpx;
}
}
.add-score {
color: #FA8C16;
font-size: 24rpx;
text-align: center;
}
}
}
.score-btn {
display: flex;
justify-content: space-between;
align-items: center;
flex-direction: row;
margin-top: 30rpx;
.b-add-score {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
width: 300rpx;
border-radius: 16rpx;
background-color: #FFF6E0;
height: 72rpx;
image {
width: 30rpx;
height: 30rpx;
}
.name {
text-align: center;
margin-left: 20rpx;
font-size: 28rpx;
color: #FF8800;
}
//js代码
.button {
background: rgba(0, 0, 0, 0); //设置背景色
text-align: center;
margin: 0rpx;
font-size: 28rpx;
color: #FF8800;
}
button::after {
border: none; //去除边框
}
}
}
}
.choose-date {
padding: 0 30rpx;
}
.current-score {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20rpx;
margin: 0 32rpx;
background-color: #FAFAFA;
margin-top: 32rpx;
.score {
color: #666;
font-size: 24rpx;
}
.value {
color: #333;
font-size: 48rpx;
font-weight: bold;
}
.date {}
}
.current-project {
padding: 32rpx;
display: flex;
justify-content: space-between;
align-items: center;
.name {
font-size: 34rpx;
width: 80%;
color: #333;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
.choose-project {
width: 40rpx;
height: 40rpx;
}
}
.score-list {
display: flex;
flex-direction: column;
margin: 0 32rpx;
background-color: #FAFAFA;
.score-list-item {
display: flex;
justify-content: space-between;
align-items: center;
height: 104rpx;
padding: 0 30rpx;
position: relative;
border-bottom: 1px solid rgba($color: #999, $alpha: 0.5);
&:last-child {
.line {
display: none;
}
}
// .circle{
// border:1px solid #1890ff;
// border-radius: 50%;
// background-color: #FFFFFF;
// width: 20rpx;
// height: 20rpx;
// }
// .line{
// position: absolute;
// width: 2rpx;
// height:100rpx;
// background-color: #000;
// left: 10rpx;
// opacity: 0.5;
// bottom: -90rpx;
// }
.time {
font-size: 28rpx;
color: #666;
}
.desc {
font-size: 28rpx;
color: #666;
width: 250rpx;
}
.score {
font-size: 28rpx;
color: #333;
width: 60rpx;
text-align: right;
}
}
}
}
</style>
## 1.2.3(2022-12-14)
修复外部不跳转页面修改current,组件不响应bug
## 1.2.2(2022-12-14)
新增click事件,click事件会优先于任何事件
## 1.2.1(2022-12-09)
修改文档使用说明,路由拦截有平台兼容性,暂时无法解决,平台原生不支持
## 1.2.0(2022-12-09)
新增插槽,可以自定义凸起导航,优化文档使用说明
## 1.1.9(2022-12-06)
修复iconPath图片路径为网络路径判断错误问题
## 1.1.7(2022-12-01)
优化部分机器计算错误问题
## 1.1.6(2022-12-01)
修改高度计算错误问题
## 1.1.5(2022-12-01)
修复各别设备上高度计算有误问题
## 1.1.4(2022-11-25)
修复vue2版本在微信小程序上不支持:style传入参数bug,优化参数错误逻辑
## 1.1.3(2022-11-24)
修复native模式borderStyle样式丢失问题
## 1.1.2(2022-11-24)
修改描述文档说明,修改插件描述信息
## 1.1.1(2022-11-24)
优化使用说明文档
## 1.1.0(2022-11-24)
极度简化native模式,页面只需要引入组件即可,任何操作都不需要
## 1.0.9(2022-11-24)
修复native模式下,fill忘记计算高度.
## 1.0.8(2022-11-24)
优化native模式,简化参数数量,使用更简单
## 1.0.7(2022-11-24)
新增native配置,可以兼容原生tabbar,新增beforeChange,可自行根据要求自己兼容路由守卫
## 1.0.6(2022-11-23)
修改文档描述错误
## 1.0.5(2022-11-23)
修复fill高度遗漏安全距离问题,文档使用说明优化更新
## 1.0.4(2022-11-23)
优化配置选项,提取当前选中项,新增fixed配置
## 1.0.3(2022-11-14)
添加上阴影效果,修复由于去除了上线条,造成如果内容如果是白色,tabbar会和内容高度重合的问题
## 1.0.2(2022-11-14)
修改说明文档,更加详细备注说明
## 1.0.1(2022-11-14)
新增当前选中项class名,方便用户直接样式覆盖
## 1.0.0(2022-11-14)
第一个自定义tabbar版本
<template>
<view class="m-tabbar-box" :style="tabbarBoxStyle">
<view v-if="fill || native" :style="tabbarFillStyle"></view>
<view id="m-tabbar" class="m-tabbar" :class="{'fixed': fixed || native}" :style="tabbarStyle">
<view class="m-tabbar__border" v-if="borderStyle === 'black' "></view>
<view class="m-tabbar__flex">
<view @click="tabChange(index)" v-for="(item, index) in tabbarList" :key="index" class="m-tabbar__item"
:class="{
'm-tabbar__item__active': index === currentIndex,
}">
<slot :name="`tabbar_index_${index}`">
<image :src="currentIndex === index ? item.selectedIconPath : item.iconPath"
class="m-tabbar__icon" />
<view class="m-tabbar__label"
:style="{'color': index === currentIndex ? tabbarConfig.selectedColor : tabbarConfig.color }">
{{ item.text }}
</view>
</slot>
</view>
</view>
</view>
</view>
</template>
<script>
const obj2strStyle = (obj) => {
let style = ''
for (let key in obj) {
style += `${key}:${obj[key]};`
}
return style
}
const padFirstSymbol = (str, smb) => {
if (str.startsWith(smb) || str.startsWith('http')) {
return str
}
return `/${str}`
}
const replaceTabbarList = (list) => {
if (!list.length > 0) {
return []
}
return list.map(item => {
if(item.iconPath){
item.iconPath = padFirstSymbol(item.iconPath, '/')
}
if(item.pagePath){
item.pagePath = padFirstSymbol(item.pagePath, '/')
}
if(item.selectedIconPath){
item.selectedIconPath = padFirstSymbol(item.selectedIconPath, '/')
}
return item
})
}
import PageConfig from '@/pages.json'
export default {
emits: ['change', 'click'],
props: {
current: {
type: [Number, String],
default: 0
},
tabbar: {
type: Object,
default () {
return {}
}
},
fixed: {
type: Boolean,
default: false
},
fill: {
type: Boolean,
default: false
},
zIndex: {
type: [Number, String],
default: 9999
},
native: {
type: Boolean,
default: false
},
safeBottom: {
type: Boolean,
default: true
},
beforeChange: {
type: Function,
default: null
}
},
data() {
return {
safeAreaInsetsBottom: 0,
currentIndex: 0,
tabbarHeight: 0,
beforeData: {},
}
},
watch: {
current(val) {
this.currentIndex = val * 1
}
},
computed: {
tabbarConfig() {
const {
native
} = this
if (native) {
const {
tabBar
} = PageConfig
if (!tabBar) {
console.error('Native mode, Pages.json no tabbar config')
return {
borderStyle: 'black',
list: []
}
}
return tabBar
}
return this.tabbar
},
tabbarList() {
const {
list
} = this.tabbarConfig
if(list){
return replaceTabbarList(list)
}
console.error('No tabbar config')
return []
},
borderStyle() {
const {
borderStyle
} = this.tabbarConfig
return borderStyle
},
tabbarBoxStyle() {
const {
zIndex
} = this
return obj2strStyle({
'z-index': zIndex,
})
},
tabbarFillStyle() {
const {
tabbarHeight,
safeAreaInsetsBottom
} = this
return obj2strStyle({
'height': `${tabbarHeight}rpx`,
'padding-bottom': `${safeAreaInsetsBottom}rpx`,
'opacity': 0
})
},
tabbarStyle() {
const {
safeAreaInsetsBottom
} = this
const {
backgroundColor
} = this.tabbarConfig
return obj2strStyle({
'background-color': backgroundColor || '#fff',
'padding-bottom': `${safeAreaInsetsBottom}rpx`
})
},
tabbarItemStyle() {
const {
currentIndex
} = this
const {
color,
selectedColor
} = this.tabbarConfig
return obj2strStyle({
'color': currentIndex ? selectedColor : color
})
}
},
mounted() {
const {
current,
safeBottom,
fill,
native,
tabbarList
} = this
this.currentIndex = current * 1
if (fill || native) {
this.getTabbarHeight()
}
if (native) {
const currentPage = getCurrentPages()[0].$page.fullPath
const currentIndex = tabbarList.findIndex(item => item.pagePath === currentPage)
this.currentIndex = currentIndex
if (tabbarList.length > 0) {
uni.hideTabBar()
}
}
},
methods: {
getTabbarHeight() {
const { safeBottom, native } = this
const {windowWidth, safeAreaInsets} = uni.getSystemInfoSync()
const ratio = 750 / windowWidth
if(safeBottom || native){
this.safeAreaInsetsBottom = safeAreaInsets.bottom * ratio
}
let query = wx.createSelectorQuery().in(this)
query.select('#m-tabbar').boundingClientRect(rect => {
if (rect) {
this.tabbarHeight = rect.height * ratio
}
}).exec()
},
tabChange(index) {
const {
currentIndex
} = this
this.$emit('click', index)
if (index === currentIndex) {
return
}
this.beforeData = {
newIndex: index,
oldIndex: currentIndex,
next: this.jumpPage
}
if (this.beforeChange) {
this.beforeChange(this.jumpPage)
} else {
this.jumpPage()
}
},
jumpPage() {
const {
native,
beforeData,
tabbarList: list
} = this
const {
newIndex: index
} = beforeData
if (list[index].pagePath) {
if (native) {
uni.switchTab({
url: list[index].pagePath
})
} else {
this.currentIndex = index
uni.reLaunch({
url: list[index].pagePath
})
}
}
this.$emit('change', index)
}
}
};
</script>
<style lang="scss" scoped>
.m-tabbar-box {
position: relative;
z-index: 9999;
}
.m-tabbar {
position: relative;
}
.m-tabbar.fixed {
position: fixed;
bottom: 0;
left: 0;
width: 100vw;
}
.m-tabbar__flex {
display: flex;
flex-direction: row;
}
.m-tabbar__border {
background-color: rgba(0, 0, 0, 0.33);
width: 100%;
height: 1rpx;
transform: scaleY(0.5);
}
.m-tabbar__item {
display: flex;
flex-direction: column;
align-items: center;
flex: 1;
padding: 4px 0 2px;
}
.m-tabbar__icon {
display: block;
width: 48rpx;
height: 48rpx;
margin-bottom: 6rpx;
}
.m-tabbar__label {
font-size: 24rpx;
}
</style>
{
"id": "m-tabbar",
"displayName": "自定义tabbar、tabbar路由守卫、零配置tabbar、凸起导航",
"version": "1.2.3",
"description": "自定义tabbar,超高还原原生配置模式,一行代码自定义导航,自带tabbar路由守卫功能",
"keywords": [
"自定义tabbar,tabbar,自定义标签栏,tabbar路由守卫,零配置tabbar,路由守卫、凸起导航"
],
"repository": "",
"engines": {
"HBuilderX": "^3.3.1"
},
"dcloudext": {
"type": "component-vue",
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": ""
},
"uni_modules": {
"dependencies": [],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"Vue": {
"vue2": "y",
"vue3": "y"
},
"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",
"钉钉": "y",
"快手": "y",
"飞书": "y",
"京东": "y"
},
"快应用": {
"华为": "y",
"联盟": "y"
}
}
}
}
}
\ No newline at end of file
# m-tabbar自定义
## 使用说明,注意事项(必看)
> 我配套上传了一个案例包,如果不会使用的,建议下载阅读使用
自定义tabbar的情况下,不建议在一个页面内通过几个组件,用v-if切换去模拟各个页面,会存在各种不可控bug
> 如果是`if切换组件`的话,就是一个页面控制多个组件显示隐藏来实现。 如果组件封装的有问题,会出现组件之间的协调问题,请看情况使用。 还有一些原生的交互没有办法达到预期,会影响到原生体验。 比如下拉刷新,滚动加载更多,切换tabbar后滚动位置不能固定等
推荐使用自带的tabbar系统,同时隐藏原生的tabbar, 再引入自定导航栏,这样可以保证原有性能,同时又能自定义tabbar
在pages.json中正常定义tabbar配置和字段,使用`native`模式,组件会自动加载pages.json配置项,并自动判断当前选中项
> native模式,你不需要传入任何配置,直接页面引入即结束
# 快速使用
## 方式一、Native模式使用(推荐)
```
// native模式,无需配置其他项
<m-tabbar native></m-tabbar>
```
在各个tabbar页面引入tabbar组件,传入属性`native``native`模式下无需任何配置
组件会默认自动通过`uni.hideTabBar()`隐藏系统tabbar
## 方式二、普通页面使用(current默认从0开始)
```
// 普通页面模式
<m-tabbar fixed fill current="1" :tabbar="tabbar"></m-tabbar>
```
配置选项和`uniapp`的配置完全相同,直接复制过来, 默认传入`pagePath`后,直接使用`reLaunch`跳转
如果不想使用自带跳转模式,可以不传入`pagePath`,自行接管事件`change`来达到当前页面tab任意切换
### 1、提取tabbar配置
新建文件config/tabbar.js(默认你有config目录,根据自己情况而定)
```
export default {
color: "#161616",
selectedColor: "#161616",
borderStyle: "black",
backgroundColor: "#ffffff",
list: [{
pagePath: "/pages/index/index",
iconPath: "/static/tabbar/index.png",
selectedIconPath: "/static/tabbar/index_active.png",
text: "首页"
}, {
pagePath: "/pages/shop/index",
iconPath: "/static/tabbar/shop.png",
selectedIconPath: "/static/tabbar/shop_active.png",
text: "门店"
}, {
pagePath: "/pages/my/index",
iconPath: "/static/tabbar/my.png",
selectedIconPath: "/static/tabbar/my_active.png",
text: "我的"
}]
}
```
### 2、引入tabbar
#### VUE2引入
```
import TabbarConfig from '@/config/tabbar.js'
export default {
data(){
return {
tabbar: TabbarConfig
}
},
onLoad(){
// 没有开启native模式下,使用reLaunch跳转,会存在首页标志,需要隐藏
#ifdef MP-JD || MP-WEIXIN
uni.hideHomeButton()
#endif
}
}
```
#### VUE3 setup引入
```
import TabbarConfig from '@/config/tabbar.js'
import { reactive } from 'vue'
// 没有开启native模式下,使用reLaunch跳转,会存在首页标志,需要隐藏
#ifdef MP-JD || MP-WEIXIN
uni.hideHomeButton()
#endif
const tabbar = reactive(TabbarConfig)
```
### 3、页面使用
```
<m-tabbar fixed fill current="1" :tabbar="tabbar"></m-tabbar>
```
## 高级用法(beforeChange)(路由守卫)
有些特殊需求,我们在点击一个tabbar其他一项的时候,可能需要判断权限是否可以进入,那么我们在切换前做一下路由拦截`beforeChange`,如果达到自己的预期,就进行跳转
> uniapp 微信小程序不支持$listeners,只能使用prop方式传入, 部分平台不支持prop传入方法,有平台限制,详细请看(问题解答)[https://ask.dcloud.net.cn/question/70659]
### 页面使用传入beforeChange
```
// native模式,无需传入 fixed fill
<m-tabbar native :beforeChange="onBeforeChange"></m-tabbar>
// 普通页面模式
<m-tabbar fixed fill current="1" :tabbar="tabbar" :beforeChange="onBeforeChange"></m-tabbar>
```
### 进行事件判断监听
函数必选参数 next,当判断逻辑执行完毕后,满足条件的情况下执行 `next()`
```
methods: {
onBeforeChange(next){
console.log('before page2 switch')
setTimeout(()=>{
console.log('switch page2 end')
next()
}, 1000)
}
}
```
## 自定义凸起导航(插槽使用)
```
<m-tabbar native>
<template v-slot:tabbar_index_1> //插槽详细看文档,样式你自己写
<view class="custom_style">
<view class="custom_style_icon">+</view>
</view>
</template>
</m-tabbar>
<style lang="scss">
.custom_style{
color: #fff;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: 24rpx;
&_icon{
background-color: #15357A;
font-size: 80rpx;
width: 120rpx;
height: 120rpx;
border-radius: 100%;
display: flex;
justify-content: center;
align-items: center;
margin-top: -40rpx;
}
}
</style>
```
### 属性说明
| 属性名 | 类型 | 默认值 | 必填 | 说明 |
| ------- | ------- | ------- | ------- | ------- |
| current | Number,String | 0 | true | 默认选中第几项,0开始(普通模式) |
| tabbar | Object | {} | true | tabbar配置项 (普通模式) |
| fixed | Boolean | false | false | 是否定位在底部(普通模式) |
| fill | Boolean | false | false | 是否填充底部高度(如果开启fixed后,会出现tabbar遮盖内容问题,开启此属性,会自动填充高度,可单独使用)(普通模式) |
| safeBottom | Boolean | true | false | 是否自动规避iphoneX\XR等底部安全距离(普通模式) |
| zIndex | Number,String | 999 | false | 当前处于z-index层级 |
| native | Boolean | false | false | native模式,当前页面是系统原生tabbar页面(pages.json里面配置了tabBar) |
| beforeChange | Function | null | false | 导航切换前事件hooks,用于判断点击tabbar的时候,可以先执行自己定义的事件,达到预期后在跳转(类似router的路由守卫),方法需要调用next参数回调,部分平台不支持,存在兼容性 |
### 方法说明
| 方法名 | 返回值说明 |
| ------- | ------- |
| click | 当前选中index,无论什么请看下都会先触发click事件,方便自由定制更多方法 |
| change | 当前选中index(beforeChange会在change之前执行,只有执行next才会返回) |
### 插槽
| 插槽名 | 返回值说明 |
| ------- | ------- |
| tabbar_index_{index} | 插槽名字为tabbar_index_你要变化的index, 可以做到任意控制自己的导航,比如中心凸起,比如你想让第一个变化,index就是0,比如你tabbarList里面有5个item,你想让中间的凸起,那么index就是2,取下标 |
### 插件写的时候,没办法照顾到所有平台,欢迎点评指正,如有问题欢迎给我留言
#### 例如:
```
设备:iphone13
系统: ios13
使用环境平台: 微信小程序、app
使用vue版本 :vue3
问题描述: 提示什么什么错误
```
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
Please register or to comment