no message

This commit is contained in:
kuaifan 2022-05-30 14:27:28 +08:00
parent 72f7cc927c
commit cb2ad20e36
30 changed files with 581 additions and 429 deletions

View File

@ -1,7 +1,7 @@
<template> <template>
<div id="app"> <div id="app">
<keep-alive> <keep-alive>
<router-view class="child-view" :class="{'view-768': $store.state.windowMax768}"></router-view> <router-view class="child-view"></router-view>
</keep-alive> </keep-alive>
<Spinner/> <Spinner/>
<RightBottom/> <RightBottom/>
@ -58,7 +58,7 @@ export default {
}, },
computed: { computed: {
...mapState(['ws', 'userId', 'userToken']), ...mapState(['ws', 'userId', 'userToken', 'windowMax768']),
}, },
watch: { watch: {

View File

@ -6,7 +6,6 @@
</div> </div>
<ul ref="ganttItem" <ul ref="ganttItem"
class="gantt-item" class="gantt-item"
:style="ganttItemStyle"
@scroll="itemScrollListener" @scroll="itemScrollListener"
@mouseenter="mouseType='item'"> @mouseenter="mouseType='item'">
<li v-for="(item, key) in lists" :key="key"> <li v-for="(item, key) in lists" :key="key">
@ -103,7 +102,6 @@ export default {
} }
}, },
computed: { computed: {
...mapState(['touchBackInProgress']),
monthNum() { monthNum() {
const {ganttWidth, dateWidth} = this; const {ganttWidth, dateWidth} = this;
return Math.floor(ganttWidth / dateWidth / 30) + 2 return Math.floor(ganttWidth / dateWidth / 30) + 2
@ -232,13 +230,6 @@ export default {
return customStyle return customStyle
} }
}, },
ganttItemStyle() {
const style = {}
if (this.touchBackInProgress) {
style.overflow = 'hidden !important';
}
return style
},
}, },
methods: { methods: {
itemScrollListener(e) { itemScrollListener(e) {
@ -376,265 +367,3 @@ export default {
} }
} }
</script> </script>
<style lang="scss" scoped>
.common-gantt {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: flex;
flex-direction: row;
align-items: self-start;
color: #747a81;
* {
box-sizing: border-box;
}
.gantt-left {
flex-grow:0;
flex-shrink:0;
height: 100%;
background-color: #ffffff;
position: relative;
display: flex;
flex-direction: column;
&:after {
content: "";
position: absolute;
top: 0;
right: 0;
bottom: 0;
width: 1px;
background-color: rgba(237, 241, 242, 0.75);
}
.gantt-title {
height: 76px;
flex-grow: 0;
flex-shrink: 0;
background-color: #F9FAFB;
padding-left: 12px;
overflow: hidden;
.gantt-title-text {
line-height: 100px;
max-width: 200px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-weight: 600;
}
}
.gantt-item {
transform: translateZ(0);
max-height: 100%;
overflow: auto;
-ms-overflow-style: none;
&::-webkit-scrollbar {
display: none;
}
> li {
height: 40px;
border-bottom: 1px solid rgba(237, 241, 242, 0.75);
position: relative;
display: flex;
align-items: center;
padding-left: 12px;
&:hover {
.item-icon {
display: flex;
}
}
.item-overdue {
flex-grow:0;
flex-shrink:0;
color: #ffffff;
margin-right: 4px;
background-color: #ff0000;
padding: 1px 3px;
border-radius: 3px;
font-size: 12px;
line-height: 18px;
}
.item-title {
flex: 1;
padding-right: 12px;
cursor: default;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
&.complete {
text-decoration: line-through;
}
&.overdue {
font-weight: 600;
}
}
.item-icon {
display: none;
align-items: center;
justify-content: center;
width: 32px;
margin-right: 2px;
font-size: 16px;
color: #888888;
}
}
}
}
.gantt-right {
flex: 1;
height: 100%;
background-color: #ffffff;
position: relative;
overflow: hidden;
.gantt-chart {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
transform: translateZ(0);
.gantt-month {
display: flex;
align-items: center;
position: absolute;
top: 0;
left: 0;
right: 0;
z-index: 1;
height: 26px;
line-height: 20px;
font-size: 14px;
background-color: #F9FAFB;
> li {
flex-grow: 0;
flex-shrink: 0;
height: 100%;
position: relative;
overflow: hidden;
&:after {
content: "";
position: absolute;
top: 0;
right: 0;
width: 1px;
height: 100%;
background-color: rgba(237, 241, 242, 0.75);
}
.month-format {
overflow: hidden;
white-space: nowrap;
padding: 6px 6px 0;
}
}
}
.gantt-date {
display: flex;
align-items: center;
position: absolute;
top: 26px;
left: 0;
right: 0;
bottom: 0;
z-index: 2;
cursor: move;
&:before {
content: "";
position: absolute;
top: 0;
left: 0;
right: 0;
height: 50px;
background-color: #F9FAFB;
}
> li {
flex-grow: 0;
flex-shrink: 0;
height: 100%;
position: relative;
overflow: hidden;
&:after {
content: "";
position: absolute;
top: 0;
right: 0;
width: 1px;
height: 100%;
background-color: rgba(237, 241, 242, 0.75);
}
.date-format {
overflow: hidden;
white-space: nowrap;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 44px;
.format-day {
line-height: 28px;
font-size: 18px;
}
.format-week {
line-height: 16px;
font-weight: 300;
font-size: 13px;
}
}
}
}
.gantt-timeline {
position: absolute;
top: 76px;
left: 0;
right: 0;
bottom: 0;
z-index: 3;
overflow-x: hidden;
overflow-y: auto;
> li {
cursor: default;
height: 40px;
border-bottom: 1px solid rgba(237, 241, 242, 0.75);
position: relative;
.timeline-item {
position: absolute;
top: 0;
touch-action: none;
pointer-events: auto;
padding: 4px;
margin-top: 4px;
background: #e74c3c;
border-radius: 18px;
color: #fff;
display: flex;
align-items: center;
will-change: contents;
height: 32px;
.timeline-title {
touch-action: none;
flex-grow: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
margin-left: 4px;
margin-right: 10px;
}
.timeline-resizer {
height: 22px;
touch-action: none;
width: 8px;
background: rgba(255,255,255,0.1);
cursor: ew-resize;
flex-shrink: 0;
will-change: visibility;
position: absolute;
top: 5px;
right: 5px;
}
}
}
}
}
}
}
</style>

View File

@ -50,8 +50,13 @@ export default {
}, },
watch: { watch: {
show(s) { show(state) {
this.$store.state.touchBackInProgress = s; if (state) {
document.body.classList.add("touch-back");
} else {
document.body.classList.remove("touch-back");
}
this.$store.state.touchBackInProgress = state;
} }
}, },

View File

@ -26,7 +26,7 @@
</div> </div>
<div v-if="$Platform === 'mac'" class="notification-tip">{{$L('离最新版本只有一步之遥了重新启动应用即可完成更新')}}</div> <div v-if="$Platform === 'mac'" class="notification-tip">{{$L('离最新版本只有一步之遥了重新启动应用即可完成更新')}}</div>
</div> </div>
<MarkdownPreview class="notification-body overlay-y" :initialValue="updateNote"/> <MarkdownPreview class="notification-body scrollbar-overlay" :initialValue="updateNote"/>
<div slot="footer" class="adaption"> <div slot="footer" class="adaption">
<Button type="default" @click="updateShow=false">{{$L('稍后')}}</Button> <Button type="default" @click="updateShow=false">{{$L('稍后')}}</Button>
<Button type="primary" :loading="updateIng" @click="updateQuitAndInstall">{{$L($Platform === 'mac' ? '重新启动' : '立即升级')}}</Button> <Button type="primary" :loading="updateIng" @click="updateQuitAndInstall">{{$L($Platform === 'mac' ? '重新启动' : '立即升级')}}</Button>

View File

@ -0,0 +1,70 @@
const isSupportTouch = "ontouchend" in document;
// 长按指令
const longpress = {
bind: function (el, binding) {
if (!isSupportTouch) {
return
}
let delay = 500,
callback = binding.value;
if ($A.isJson(binding.value)) {
delay = binding.value.delay || 500;
callback = binding.value.callback;
}
if (typeof callback !== 'function') {
throw 'callback must be a function'
}
// 定义变量
let pressTimer = null
let isCall = false
// 创建计时器( 500秒后执行函数
el.__longpressStart__ = (e) => {
if (e.type === 'click' && e.button !== 0) {
return
}
isCall = false
if (pressTimer === null) {
pressTimer = setTimeout(() => {
isCall = true
callback(e, el)
}, delay)
}
}
// 取消计时器
el.__longpressCancel__ = (e) => {
if (pressTimer !== null) {
clearTimeout(pressTimer)
pressTimer = null
}
}
// 点击拦截
el.__longpressClick__ = (e) => {
if (isCall) {
e.preventDefault()
e.stopPropagation()
}
el.__longpressCancel__(e)
}
// 添加事件监听器
el.addEventListener('touchstart', el.__longpressStart__)
// 取消计时器
el.addEventListener('click', el.__longpressClick__)
el.addEventListener('touchmove', el.__longpressCancel__);
el.addEventListener('touchend', el.__longpressCancel__)
el.addEventListener('touchcancel', el.__longpressCancel__)
},
// 指令与元素解绑的时候,移除事件绑定
unbind(el) {
el.removeEventListener('touchstart', el.__longpressStart__)
el.removeEventListener('click', el.__longpressClick__)
el.removeEventListener('touchmove', el.__longpressCancel__)
el.removeEventListener('touchend', el.__longpressCancel__)
el.removeEventListener('touchcancel', el.__longpressCancel__)
delete el.__longpressStart__;
delete el.__longpressClick__;
delete el.__longpressCancel__;
}
}
export default longpress

View File

@ -18,17 +18,29 @@ export default {
binding.value("up"); binding.value("up");
} }
}; };
el.addEventListener(isSupportTouch ? 'touchstart' : 'mousedown', el.__touchMouseDown__); if (isSupportTouch) {
document.addEventListener(isSupportTouch ? 'touchmove' : 'mousemove', el.__touchMouseMove__); el.addEventListener('touchstart', el.__touchMouseDown__);
document.addEventListener(isSupportTouch ? 'touchend' : 'mouseup', el.__touchMouseUp__); el.addEventListener('touchmove', el.__touchMouseMove__);
el.addEventListener('touchend', el.__touchMouseUp__);
} else {
el.addEventListener('mousedown', el.__touchMouseDown__);
document.addEventListener('mousemove', el.__touchMouseMove__);
document.addEventListener('mouseup', el.__touchMouseUp__);
}
}, },
update () { update () {
}, },
unbind (el) { unbind (el) {
el.removeEventListener(isSupportTouch ? 'touchstart' : 'mousedown', el.__touchMouseDown__); if (isSupportTouch) {
document.removeEventListener(isSupportTouch ? 'touchmove' : 'mousemove', el.__touchMouseMove__); el.removeEventListener('touchstart', el.__touchMouseDown__);
document.removeEventListener(isSupportTouch ? 'touchend' : 'mouseup', el.__touchMouseUp__); el.removeEventListener('touchmove', el.__touchMouseMove__);
el.removeEventListener('touchend', el.__touchMouseUp__);
} else {
el.removeEventListener('mousedown', el.__touchMouseDown__);
document.removeEventListener('mousemove', el.__touchMouseMove__);
document.removeEventListener('mouseup', el.__touchMouseUp__);
}
delete el.__touchMouseDown__; delete el.__touchMouseDown__;
delete el.__touchMouseMove__; delete el.__touchMouseMove__;
delete el.__touchMouseUp__; delete el.__touchMouseUp__;

View File

@ -1077,6 +1077,17 @@
return {width: parseInt(tempWidth), height: parseInt(tempHeight)}; return {width: parseInt(tempWidth), height: parseInt(tempHeight)};
} }
return {width, height}; return {width, height};
},
/**
* 获取元素属性
* @param el
* @param attrName
* @param def
* @returns {Property<any>|string|string}
*/
getAttr(el, attrName, def = "") {
return el ? el.getAttribute(attrName) : def;
} }
}); });

View File

@ -135,7 +135,7 @@
</template> </template>
</DropdownMenu> </DropdownMenu>
</Dropdown> </Dropdown>
<ul :class="overlayClass" @scroll="handleClickTopOperateOutside"> <ul :class="listClassName" @scroll="operateVisible = false">
<li @click="toggleRoute('dashboard')" :class="classNameRoute('dashboard')"> <li @click="toggleRoute('dashboard')" :class="classNameRoute('dashboard')">
<i class="taskfont">&#xe6fb;</i> <i class="taskfont">&#xe6fb;</i>
<div class="menu-title">{{$L('仪表盘')}}</div> <div class="menu-title">{{$L('仪表盘')}}</div>
@ -156,14 +156,15 @@
<i class="taskfont">&#xe6f3;</i> <i class="taskfont">&#xe6f3;</i>
<div class="menu-title">{{$L('文件')}}</div> <div class="menu-title">{{$L('文件')}}</div>
</li> </li>
<li ref="projectWrapper" class="menu-project"> <li class="menu-project">
<ul :class="overlayClass" @scroll="handleClickTopOperateOutside"> <ul ref="projectWrapper" :class="listClassName" @scroll="operateVisible = false">
<li <li
v-for="(item, key) in projectLists" v-for="(item, key) in projectLists"
:ref="`project_${item.id}`"
:key="key" :key="key"
:class="classNameProject(item)" :class="classNameProject(item)"
@click="toggleRoute('project', {projectId: item.id})" @click="toggleRoute('project', {projectId: item.id})"
@contextmenu.prevent.stop="handleRightClick($event, item)"> @contextmenu.prevent.stop="handleContextmenu($event, item)">
<div class="project-h1"> <div class="project-h1">
<em @click.stop="toggleOpenMenu(item.id)"></em> <em @click.stop="toggleOpenMenu(item.id)"></em>
<div class="title">{{item.name}}</div> <div class="title">{{item.name}}</div>
@ -183,24 +184,24 @@
</p> </p>
</div> </div>
</li> </li>
<li v-if="loadIng > 0" class="loading"><Loading/></li>
</ul> </ul>
<Loading v-if="loadIng > 0"/> </li>
<div class="top-operate" :style="topOperateStyles"> </ul>
<div class="operate-position" :style="operateStyles">
<Dropdown <Dropdown
trigger="custom" trigger="custom"
:visible="topOperateVisible" :visible="operateVisible"
transfer-class-name="page-file-dropdown-menu" @on-clickoutside="operateVisible = false"
@on-clickoutside="handleClickTopOperateOutside"
transfer> transfer>
<div :style="{height: operateStyles.height}"></div>
<DropdownMenu slot="list"> <DropdownMenu slot="list">
<DropdownItem @click.native="handleTopClick"> <DropdownItem @click.native="handleTopClick">
{{ $L(topOperateItem.top_at ? '取消置顶' : '置顶该项目') }} {{ $L(operateItem.top_at ? '取消置顶' : '置顶该项目') }}
</DropdownItem> </DropdownItem>
</DropdownMenu> </DropdownMenu>
</Dropdown> </Dropdown>
</div> </div>
</li>
</ul>
<div <div
v-if="projectTotal > 20" v-if="projectTotal > 20"
class="manage-project-search"> class="manage-project-search">
@ -226,7 +227,7 @@
<div class="manage-box-main"> <div class="manage-box-main">
<keep-alive> <keep-alive>
<router-view class="manage-box-view overlay"></router-view> <router-view class="manage-box-view"></router-view>
</keep-alive> </keep-alive>
</div> </div>
@ -437,9 +438,9 @@ export default {
reportTabs: "my", reportTabs: "my",
reportUnreadNumber: 0, reportUnreadNumber: 0,
topOperateStyles: {}, operateStyles: {},
topOperateVisible: false, operateVisible: false,
topOperateItem: {}, operateItem: {},
} }
}, },
@ -620,10 +621,10 @@ export default {
return data; return data;
}, },
overlayClass() { listClassName() {
return { return {
'overlay-y': true, 'scrollbar-overlay': true,
'overlay-none': this.topOperateVisible === true, 'scrollbar-hidden': this.operateVisible === true,
} }
}, },
@ -805,7 +806,7 @@ export default {
return { return {
"active": this.routeName === 'manage-project' && this.$route.params.projectId == item.id, "active": this.routeName === 'manage-project' && this.$route.params.projectId == item.id,
"open-menu": this.openMenu[item.id] === true, "open-menu": this.openMenu[item.id] === true,
"operate": item.id == this.topOperateItem.id && this.topOperateVisible "operate": item.id == this.operateItem.id && this.operateVisible
}; };
}, },
@ -963,34 +964,31 @@ export default {
}, typeof timeout === "number" ? timeout : 1000) }, typeof timeout === "number" ? timeout : 1000)
}, },
handleRightClick(event, item) { handleContextmenu(event, item) {
this.handleClickTopOperateOutside(); this.operateVisible = false;
this.topOperateItem = item; this.operateItem = $A.isJson(item) ? item : {};
this.$nextTick(() => { this.$nextTick(() => {
const projectWrap = this.$refs.projectWrapper; const dialogRect = this.$refs[`project_${item.id}`][0].getBoundingClientRect();
const projectBounding = projectWrap.getBoundingClientRect(); const wrapRect = this.$refs.projectWrapper.getBoundingClientRect();
this.topOperateStyles = { this.operateStyles = {
left: `${event.clientX - projectBounding.left}px`, left: `${event.clientX - wrapRect.left}px`,
top: `${event.clientY - projectBounding.top}px` top: `${dialogRect.top}px`,
}; height: dialogRect.height + 'px',
this.topOperateVisible = true; }
this.operateVisible = true;
}) })
}, },
handleClickTopOperateOutside() {
this.topOperateVisible = false;
},
handleTopClick() { handleTopClick() {
this.$store.dispatch("call", { this.$store.dispatch("call", {
url: 'project/top', url: 'project/top',
data: { data: {
project_id: this.topOperateItem.id, project_id: this.operateItem.id,
}, },
}).then(({data}) => { }).then(({data}) => {
this.$store.dispatch("saveProject", data); this.$store.dispatch("saveProject", data);
this.$nextTick(() => { this.$nextTick(() => {
let active = this.$refs.projectWrapper.querySelector(".active") const active = this.$refs.projectWrapper.querySelector(".active")
if (active) { if (active) {
$A.scrollToView(active, { $A.scrollToView(active, {
behavior: 'instant', behavior: 'instant',

View File

@ -1,6 +1,6 @@
<template> <template>
<div class="chat-emoji-wrapper"> <div class="chat-emoji-wrapper">
<ul class="chat-emoji-box overlay-y" :class="[type, 'no-dark-content']"> <ul class="chat-emoji-box scrollbar-overlay" :class="[type, 'no-dark-content']">
<li v-for="item in list" @click="onSelect(item)"> <li v-for="item in list" @click="onSelect(item)">
<img v-if="item.type === 'emoticon'" :src="item.src" :title="item.name" :alt="item.name"/> <img v-if="item.type === 'emoticon'" :src="item.src" :title="item.name" :alt="item.name"/>
<span v-else v-html="item.html" :title="item.name"></span> <span v-else v-html="item.html" :title="item.name"></span>

View File

@ -65,13 +65,13 @@
popper-class="dialog-wrapper-read-poptip" popper-class="dialog-wrapper-read-poptip"
placement="left-end"> placement="left-end">
<div class="read-poptip-content"> <div class="read-poptip-content">
<ul class="read overlay-y"> <ul class="read scrollbar-overlay">
<li class="read-title"><em>{{ readList.length }}</em>{{ $L('已读') }}</li> <li class="read-title"><em>{{ readList.length }}</em>{{ $L('已读') }}</li>
<li v-for="item in readList"> <li v-for="item in readList">
<UserAvatar :userid="item.userid" :size="26" showName/> <UserAvatar :userid="item.userid" :size="26" showName/>
</li> </li>
</ul> </ul>
<ul class="unread overlay-y"> <ul class="unread scrollbar-overlay">
<li class="read-title"><em>{{ unreadList.length }}</em>{{ $L('未读') }}</li> <li class="read-title"><em>{{ unreadList.length }}</em>{{ $L('未读') }}</li>
<li v-for="item in unreadList"> <li v-for="item in unreadList">
<UserAvatar :userid="item.userid" :size="26" showName/> <UserAvatar :userid="item.userid" :size="26" showName/>

View File

@ -65,10 +65,7 @@
</slot> </slot>
<DynamicScroller <DynamicScroller
ref="scroller" ref="scroller"
class="dialog-scroller" class="dialog-scroller scrollbar-overlay"
:class="{
'overlay-y': !touchBackInProgress
}"
:disabled="touchBackInProgress" :disabled="touchBackInProgress"
:items="allMsgs" :items="allMsgs"
:min-item-size="58" :min-item-size="58"

View File

@ -92,7 +92,7 @@
</div> </div>
</div> </div>
</div> </div>
<div v-if="tabTypeActive === 'column'" class="project-column" :style="columnStyle"> <div v-if="tabTypeActive === 'column'" class="project-column">
<Draggable <Draggable
:list="columnList" :list="columnList"
:animation="150" :animation="150"
@ -138,7 +138,7 @@
<Icon class="last" type="md-add" @click="addTopShow(column.id, true)" /> <Icon class="last" type="md-add" @click="addTopShow(column.id, true)" />
</div> </div>
</div> </div>
<div :ref="'column_' + column.id" class="column-task overlay-y"> <div :ref="'column_' + column.id" class="column-task scrollbar-overlay">
<div v-if="!!columnTopShow[column.id]" class="task-item additem"> <div v-if="!!columnTopShow[column.id]" class="task-item additem">
<TaskAddSimple <TaskAddSimple
:column-id="column.id" :column-id="column.id"
@ -230,7 +230,7 @@
</li> </li>
</Draggable> </Draggable>
</div> </div>
<div v-else-if="tabTypeActive === 'table'" class="project-table overlay-y" :style="columnStyle"> <div v-else-if="tabTypeActive === 'table'" class="project-table scrollbar-overlay">
<div class="project-table-head"> <div class="project-table-head">
<Row class="task-row"> <Row class="task-row">
<Col span="12"># {{$L('任务名称')}}</Col> <Col span="12"># {{$L('任务名称')}}</Col>
@ -538,7 +538,6 @@ export default {
computed: { computed: {
...mapState([ ...mapState([
'windowWidth', 'windowWidth',
'touchBackInProgress',
'userId', 'userId',
'cacheDialogs', 'cacheDialogs',
@ -575,14 +574,6 @@ export default {
return style return style
}, },
columnStyle() {
const style = {}
if (this.touchBackInProgress) {
style.overflow = 'hidden !important';
}
return style
},
userWaitRemove() { userWaitRemove() {
const {userids, useridbak} = this.userData; const {userids, useridbak} = this.userData;
if (!userids) { if (!userids) {

View File

@ -27,7 +27,7 @@
<div class="taskflow-config-table"> <div class="taskflow-config-table">
<div class="taskflow-config-table-left-container"> <div class="taskflow-config-table-left-container">
<div class="taskflow-config-table-column-header left-header">{{$L('配置项')}}</div> <div class="taskflow-config-table-column-header left-header">{{$L('配置项')}}</div>
<div :ref="`overlay_${data.id}`" class="taskflow-config-table-column-body overlay-y"> <div :ref="`overlay_${data.id}`" class="taskflow-config-table-column-body scrollbar-overlay">
<div class="taskflow-config-table-block"> <div class="taskflow-config-table-block">
<div class="taskflow-config-table-block-title">{{$L('设置状态为')}}</div> <div class="taskflow-config-table-block-title">{{$L('设置状态为')}}</div>
<div class="taskflow-config-table-block-item"> <div class="taskflow-config-table-block-item">
@ -113,7 +113,7 @@
</EDropdown> </EDropdown>
</div> </div>
</div> </div>
<div :ref="`overlay_${data.id}`" class="taskflow-config-table-column-body overlay-y"> <div :ref="`overlay_${data.id}`" class="taskflow-config-table-column-body scrollbar-overlay">
<div class="taskflow-config-table-block"> <div class="taskflow-config-table-block">
<div class="taskflow-config-table-block-title"></div> <div class="taskflow-config-table-block-title"></div>
<RadioGroup v-model="item.status"> <RadioGroup v-model="item.status">

View File

@ -135,7 +135,7 @@
</div> </div>
</div> </div>
</div> </div>
<div class="scroller overlay-y"> <div class="scroller scrollbar-overlay">
<div class="title"> <div class="title">
<Input <Input
v-model="taskDetail.name" v-model="taskDetail.name"

View File

@ -32,7 +32,7 @@
</div> </div>
</li> </li>
</ul> </ul>
<div class="dashboard-list overlay-y"> <div class="dashboard-list scrollbar-overlay">
<template <template
v-for="column in columns" v-for="column in columns"
v-if="column.list.length > 0"> v-if="column.list.length > 0">

View File

@ -27,7 +27,7 @@
<ScrollerY <ScrollerY
ref="list" ref="list"
class="messenger-list" class="messenger-list"
:class="overlayClass" :class="listClassName"
@on-scroll="listScroll" @on-scroll="listScroll"
static> static>
<ul <ul
@ -45,11 +45,13 @@
:class="{ :class="{
top: dialog.top_at, top: dialog.top_at,
active: dialog.id == dialogId, active: dialog.id == dialogId,
operate: dialog.id == topOperateItem.id && topOperateVisible, operate: dialog.id == operateItem.id && operateVisible,
completed: $A.dialogCompleted(dialog) completed: $A.dialogCompleted(dialog)
}" }"
:data-id="dialog.id"
@click="openDialog(dialog.id)" @click="openDialog(dialog.id)"
@contextmenu.prevent.stop="handleRightClick($event, dialog)"> v-longpress="handleLongpress"
@contextmenu.prevent.stop="handleContextmenu($event, dialog)">
<template v-if="dialog.type=='group'"> <template v-if="dialog.type=='group'">
<i v-if="dialog.group_type=='project'" class="taskfont icon-avatar project">&#xe6f9;</i> <i v-if="dialog.group_type=='project'" class="taskfont icon-avatar project">&#xe6f9;</i>
<i v-else-if="dialog.group_type=='task'" class="taskfont icon-avatar task">&#xe6f4;</i> <i v-else-if="dialog.group_type=='task'" class="taskfont icon-avatar task">&#xe6f4;</i>
@ -99,21 +101,22 @@
<li class="loaded">{{$L('共' + contactsFilter.length + '位联系人')}}</li> <li class="loaded">{{$L('共' + contactsFilter.length + '位联系人')}}</li>
</template> </template>
</ul> </ul>
<div class="top-operate" :style="topOperateStyles"> <div class="operate-position" :style="operateStyles">
<Dropdown <Dropdown
trigger="custom" trigger="custom"
:visible="topOperateVisible" :transfer="true"
transfer-class-name="page-file-dropdown-menu" :placement="$isDesktop ? 'bottom' : 'top'"
@on-clickoutside="handleClickTopOperateOutside" :visible="operateVisible"
transfer> @on-clickoutside="operateVisible = false">
<div :style="{userSelect:operateVisible ? 'none' : 'auto', height: operateStyles.height}"></div>
<DropdownMenu slot="list"> <DropdownMenu slot="list">
<DropdownItem @click.native="handleTopClick"> <DropdownItem @click.native="handleTopClick">
{{ $L(topOperateItem.top_at ? '取消置顶' : '置顶该聊天') }} {{ $L(operateItem.top_at ? '取消置顶' : '置顶该聊天') }}
</DropdownItem> </DropdownItem>
<DropdownItem @click.native="updateRead('read')" v-if="$A.getDialogUnread(topOperateItem) > 0"> <DropdownItem @click.native="handleReadClick('read')" v-if="$A.getDialogUnread(operateItem) > 0">
{{ $L('标记已读') }} {{ $L('标记已读') }}
</DropdownItem> </DropdownItem>
<DropdownItem @click.native="updateRead('unread')" v-else> <DropdownItem @click.native="handleReadClick('unread')" v-else>
{{ $L('标记未读') }} {{ $L('标记未读') }}
</DropdownItem> </DropdownItem>
</DropdownMenu> </DropdownMenu>
@ -149,9 +152,11 @@
import {mapState} from "vuex"; import {mapState} from "vuex";
import DialogWrapper from "./components/DialogWrapper"; import DialogWrapper from "./components/DialogWrapper";
import ScrollerY from "../../components/ScrollerY"; import ScrollerY from "../../components/ScrollerY";
import longpress from "../../directives/longpress";
export default { export default {
components: {ScrollerY, DialogWrapper}, components: {ScrollerY, DialogWrapper},
directives: {longpress},
data() { data() {
return { return {
tabActive: 'dialog', tabActive: 'dialog',
@ -171,9 +176,9 @@ export default {
contactsCurrentPage: 1, contactsCurrentPage: 1,
contactsHasMorePages: false, contactsHasMorePages: false,
topOperateStyles: {}, operateItem: {},
topOperateVisible: false, operateStyles: {},
topOperateItem: {}, operateVisible: false,
} }
}, },
@ -300,10 +305,10 @@ export default {
} }
}, },
overlayClass() { listClassName() {
return { return {
'overlay-y': true, 'scrollbar-overlay': true,
'overlay-none': this.topOperateVisible === true, 'scrollbar-hidden': this.operateVisible === true,
} }
} }
}, },
@ -371,7 +376,7 @@ export default {
} }
break; break;
} }
this.topOperateVisible = false; this.operateVisible = false;
}, },
onActive(type) { onActive(type) {
@ -389,6 +394,9 @@ export default {
}, },
openDialog(dialogId) { openDialog(dialogId) {
if (this.operateVisible) {
return
}
if (dialogId > 0) { if (dialogId > 0) {
this.goForward({name: 'manage-messenger', params: {dialogId}}); this.goForward({name: 'manage-messenger', params: {dialogId}});
} else { } else {
@ -507,18 +515,18 @@ export default {
scrollIntoActive() { scrollIntoActive() {
this.$nextTick(() => { this.$nextTick(() => {
if (this.$isDesktop && this.$refs.list) { if (this.$isDesktop && this.$refs.list) {
let active = this.$refs.list.querySelector(".active") const active = this.$refs.list.querySelector(".active")
if (active) { if (active) {
$A.scrollToView(active, { $A.scrollToView(active, {
behavior: 'instant', behavior: 'instant',
scrollMode: 'if-needed', scrollMode: 'if-needed',
}); });
} else { } else {
let dialog = this.cacheDialogs.find(({id}) => id == this.dialogId) const dialog = this.cacheDialogs.find(({id}) => id == this.dialogId)
if (dialog && this.dialogActive) { if (dialog && this.dialogActive) {
this.dialogActive = ''; this.dialogActive = '';
this.$nextTick(() => { this.$nextTick(() => {
let active = this.$refs.list.querySelector(".active") const active = this.$refs.list.querySelector(".active")
if (active) { if (active) {
$A.scrollToView(active, { $A.scrollToView(active, {
behavior: 'instant', behavior: 'instant',
@ -532,29 +540,44 @@ export default {
}) })
}, },
handleRightClick(event, item) { handleLongpress(touchEvent, el) {
this.handleClickTopOperateOutside(); if (this.$isDesktop) {
this.topOperateItem = $A.isJson(item) ? item : {}; return
this.$nextTick(() => { }
const dialogWrap = this.$refs.dialogWrapper; const dialogId = $A.getAttr(el, 'data-id')
const dialogBounding = dialogWrap.getBoundingClientRect(); const dialogItem = this.dialogList.find(item => item.id == dialogId)
this.topOperateStyles = { if (dialogItem) {
left: `${event.clientX - dialogBounding.left}px`, this.handleTopOperateShow(touchEvent.touches[0], dialogItem)
top: `${event.clientY - dialogBounding.top + 100 - this.$refs.list.scrollInfo().scrollY}px` }
};
this.topOperateVisible = true;
})
}, },
handleClickTopOperateOutside() { handleContextmenu(event, dialog) {
this.topOperateVisible = false; if (!this.$isDesktop) {
return
}
this.handleTopOperateShow(event, dialog);
},
handleTopOperateShow(event, dialog) {
this.operateVisible = false;
this.operateItem = $A.isJson(dialog) ? dialog : {};
this.$nextTick(() => {
const dialogRect = this.$refs[`dialog_${dialog.id}`][0].getBoundingClientRect();
const wrapRect = this.$refs.dialogWrapper.getBoundingClientRect();
this.operateStyles = {
left: `${event.clientX - wrapRect.left}px`,
top: `${dialogRect.top}px`,
height: dialogRect.height + 'px',
}
this.operateVisible = true;
})
}, },
handleTopClick() { handleTopClick() {
this.$store.dispatch("call", { this.$store.dispatch("call", {
url: 'dialog/top', url: 'dialog/top',
data: { data: {
dialog_id: this.topOperateItem.id, dialog_id: this.operateItem.id,
}, },
}).then(({data}) => { }).then(({data}) => {
this.$store.dispatch("saveDialog", data); this.$store.dispatch("saveDialog", data);
@ -564,11 +587,11 @@ export default {
}); });
}, },
updateRead(type) { handleReadClick(type) {
this.$store.dispatch("call", { this.$store.dispatch("call", {
url: 'dialog/msg/mark', url: 'dialog/msg/mark',
data: { data: {
dialog_id: this.topOperateItem.id, dialog_id: this.operateItem.id,
type: type type: type
}, },
}).then(({data}) => { }).then(({data}) => {

View File

@ -33,7 +33,9 @@
</div> </div>
<div class="setting-content"> <div class="setting-content">
<div class="setting-content-title">{{$L(titleNameRoute)}}</div> <div class="setting-content-title">{{$L(titleNameRoute)}}</div>
<div class="setting-content-view"><router-view class="setting-router-view"></router-view></div> <div class="setting-content-view">
<router-view class="setting-router-view"></router-view>
</div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -9,4 +9,3 @@
@import "pages/_"; @import "pages/_";
@import "dark"; @import "dark";

View File

@ -1,6 +1,7 @@
@import "auto-tip"; @import "auto-tip";
@import "circle"; @import "circle";
@import "drawer-overlay"; @import "drawer-overlay";
@import "gantt-view";
@import "img-update"; @import "img-update";
@import "loading"; @import "loading";
@import "mobile"; @import "mobile";

View File

@ -0,0 +1,290 @@
.common-gantt {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: flex;
flex-direction: row;
align-items: self-start;
color: #747a81;
* {
box-sizing: border-box;
}
.gantt-left {
flex-grow: 0;
flex-shrink: 0;
height: 100%;
background-color: #ffffff;
position: relative;
display: flex;
flex-direction: column;
&:after {
content: "";
position: absolute;
top: 0;
right: 0;
bottom: 0;
width: 1px;
background-color: rgba(237, 241, 242, 0.75);
}
.gantt-title {
height: 76px;
flex-grow: 0;
flex-shrink: 0;
background-color: #F9FAFB;
padding-left: 12px;
overflow: hidden;
.gantt-title-text {
line-height: 100px;
max-width: 200px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-weight: 600;
}
}
.gantt-item {
transform: translateZ(0);
max-height: 100%;
overflow: auto;
&::-webkit-scrollbar {
display: none;
}
> li {
height: 40px;
border-bottom: 1px solid rgba(237, 241, 242, 0.75);
position: relative;
display: flex;
align-items: center;
padding-left: 12px;
&:hover {
.item-icon {
display: flex;
}
}
.item-overdue {
flex-grow: 0;
flex-shrink: 0;
color: #ffffff;
margin-right: 4px;
background-color: #ff0000;
padding: 1px 3px;
border-radius: 3px;
font-size: 12px;
line-height: 18px;
}
.item-title {
flex: 1;
padding-right: 12px;
cursor: default;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
&.complete {
text-decoration: line-through;
}
&.overdue {
font-weight: 600;
}
}
.item-icon {
display: none;
align-items: center;
justify-content: center;
width: 32px;
margin-right: 2px;
font-size: 16px;
color: #888888;
}
}
}
}
.gantt-right {
flex: 1;
height: 100%;
background-color: #ffffff;
position: relative;
overflow: hidden;
.gantt-chart {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
transform: translateZ(0);
.gantt-month {
display: flex;
align-items: center;
position: absolute;
top: 0;
left: 0;
right: 0;
z-index: 1;
height: 26px;
line-height: 20px;
font-size: 14px;
background-color: #F9FAFB;
> li {
flex-grow: 0;
flex-shrink: 0;
height: 100%;
position: relative;
overflow: hidden;
&:after {
content: "";
position: absolute;
top: 0;
right: 0;
width: 1px;
height: 100%;
background-color: rgba(237, 241, 242, 0.75);
}
.month-format {
overflow: hidden;
white-space: nowrap;
padding: 6px 6px 0;
}
}
}
.gantt-date {
display: flex;
align-items: center;
position: absolute;
top: 26px;
left: 0;
right: 0;
bottom: 0;
z-index: 2;
cursor: move;
&:before {
content: "";
position: absolute;
top: 0;
left: 0;
right: 0;
height: 50px;
background-color: #F9FAFB;
}
> li {
flex-grow: 0;
flex-shrink: 0;
height: 100%;
position: relative;
overflow: hidden;
&:after {
content: "";
position: absolute;
top: 0;
right: 0;
width: 1px;
height: 100%;
background-color: rgba(237, 241, 242, 0.75);
}
.date-format {
overflow: hidden;
white-space: nowrap;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 44px;
.format-day {
line-height: 28px;
font-size: 18px;
}
.format-week {
line-height: 16px;
font-weight: 300;
font-size: 13px;
}
}
}
}
.gantt-timeline {
position: absolute;
top: 76px;
left: 0;
right: 0;
bottom: 0;
z-index: 3;
overflow-x: hidden;
overflow-y: auto;
> li {
cursor: default;
height: 40px;
border-bottom: 1px solid rgba(237, 241, 242, 0.75);
position: relative;
.timeline-item {
position: absolute;
top: 0;
touch-action: none;
pointer-events: auto;
padding: 4px;
margin-top: 4px;
background: #e74c3c;
border-radius: 18px;
color: #fff;
display: flex;
align-items: center;
will-change: contents;
height: 32px;
.timeline-title {
touch-action: none;
flex-grow: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
margin-left: 4px;
margin-right: 10px;
}
.timeline-resizer {
height: 22px;
touch-action: none;
width: 8px;
background: rgba(255, 255, 255, 0.1);
cursor: ew-resize;
flex-shrink: 0;
will-change: visibility;
position: absolute;
top: 5px;
right: 5px;
}
}
}
}
}
}
}

View File

@ -46,7 +46,6 @@
.notification-body { .notification-body {
max-height: 210px; max-height: 210px;
overflow-x: hidden; overflow-x: hidden;
overflow-y: auto;
margin-bottom: 16px; margin-bottom: 16px;
.markdown-preview { .markdown-preview {
margin: -20px -12px; margin: -20px -12px;

View File

@ -6,7 +6,7 @@
bottom: 0; bottom: 0;
overflow-x: hidden; overflow-x: hidden;
overflow-y: auto; overflow-y: auto;
-webkit-overflow-scrolling: touch; overflow-y: overlay;
.app-scroller-bottom { .app-scroller-bottom {
height: 0; height: 0;
margin: 0; margin: 0;

View File

@ -1,6 +1,17 @@
body { body {
overflow: hidden; overflow: hidden;
&.touch-back {
.dialog-wrapper .vue-recycle-scroller.direction-vertical:not(.page-mode),
.common-gantt .gantt-left .gantt-item,
.project-panel .project-column,
.project-panel .project-table,
.scrollbar-overlay,
.ivu-modal-wrap {
overflow: hidden;
}
}
.form-tip { .form-tip {
color: #999999; color: #999999;
line-height: 22px; line-height: 22px;

View File

@ -9,6 +9,10 @@
background-color: #ffffff; background-color: #ffffff;
z-index: 1; z-index: 1;
.vue-recycle-scroller.direction-vertical:not(.page-mode) {
overflow-y: overlay;
}
.dialog-nav { .dialog-nav {
display: flex; display: flex;
align-items: center; align-items: center;
@ -823,7 +827,6 @@
.unread { .unread {
flex: 1; flex: 1;
max-height: 300px; max-height: 300px;
overflow: auto;
> li { > li {
min-height: 26px; min-height: 26px;

View File

@ -380,7 +380,6 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
overflow-x: hidden; overflow-x: hidden;
overflow-y: auto;
.task-list { .task-list {
> div:last-child { > div:last-child {
margin-bottom: 16px; margin-bottom: 16px;
@ -614,7 +613,7 @@
.project-table { .project-table {
height: 100%; height: 100%;
margin-top: 18px; margin-top: 18px;
overflow: auto; overflow-x: auto;
.task-row { .task-row {
background-color: #ffffff; background-color: #ffffff;
border-bottom: 1px solid #F4F4F5; border-bottom: 1px solid #F4F4F5;
@ -1082,7 +1081,6 @@
} }
} }
.project-table { .project-table {
overflow-x: auto;
.project-table-head, .project-table-head,
.project-table-body { .project-table-body {
min-width: 768px; min-width: 768px;

View File

@ -152,7 +152,6 @@
padding-left: 8px; padding-left: 8px;
padding-right: 36px; padding-right: 36px;
overflow-x: hidden; overflow-x: hidden;
overflow-y: auto;
.title { .title {
margin-top: 18px; margin-top: 18px;
.ivu-input { .ivu-input {

View File

@ -105,7 +105,6 @@
width: 100%; width: 100%;
margin-top: 48px; margin-top: 48px;
padding-bottom: 6%; padding-bottom: 6%;
overflow: auto;
.dashboard-ref { .dashboard-ref {
height: 0; height: 0;
} }
@ -298,8 +297,8 @@
} }
} }
.dashboard-list { .dashboard-list {
overflow: visible !important;
padding-bottom: 2px; padding-bottom: 2px;
overflow: visible;
.dashboard-ul { .dashboard-ul {
margin-bottom: 36px; margin-bottom: 36px;
user-select: none; user-select: none;

View File

@ -22,7 +22,7 @@
margin-top: 16px; margin-top: 16px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
overflow: hidden !important; overflow: hidden;
> li { > li {
flex-shrink: 0; flex-shrink: 0;
display: flex; display: flex;
@ -67,7 +67,6 @@
width: 100%; width: 100%;
> ul { > ul {
width: 100%; width: 100%;
overflow: auto;
> li { > li {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@ -174,22 +173,30 @@
&.operate { &.operate {
border-color: $primary-color; border-color: $primary-color;
} }
} &.loading {
} display: flex;
align-items: center;
justify-content: center;
padding: 6px;
.common-loading { .common-loading {
margin: 6px; margin: 6px;
width: 22px; width: 22px;
height: 22px; height: 22px;
} }
.top-operate { }
}
}
}
}
}
.operate-position {
position: absolute; position: absolute;
top: 0; top: 0;
right: 0; right: 0;
width: 1px;
opacity: 0; opacity: 0;
display: flex; visibility: hidden;
} pointer-events: none;
}
}
} }
.manage-project-search { .manage-project-search {
width: 80%; width: 80%;
@ -400,14 +407,14 @@
.page-manage { .page-manage {
.manage-box-menu { .manage-box-menu {
> ul { > ul {
overflow: auto !important; overflow: auto;
&.overlay-y { &.scrollbar-overlay {
overflow-y: overlay !important; overflow-y: overlay;
} }
> li { > li {
&.menu-project { &.menu-project {
> ul { > ul {
overflow: visible !important; overflow: visible;
} }
} }
} }

View File

@ -89,7 +89,6 @@
height: 0; height: 0;
width: 100%; width: 100%;
overflow-x: hidden; overflow-x: hidden;
overflow-y: auto;
> ul { > ul {
&.dialog { &.dialog {
> li { > li {
@ -336,12 +335,14 @@
} }
} }
} }
.top-operate { .operate-position {
position: absolute; position: absolute;
top: 0; top: 0;
right: 0; right: 0;
width: 1px;
opacity: 0; opacity: 0;
display: flex; visibility: hidden;
pointer-events: none;
} }
} }
.messenger-menu { .messenger-menu {
@ -447,15 +448,23 @@
opacity: 0; opacity: 0;
} }
.messenger-list { .messenger-list {
flex: 1;
height: 0;
width: 100%;
overflow-x: hidden;
overflow-y: auto;
> ul { > ul {
user-select: none; user-select: none;
&.dialog { &.dialog {
> li { > li {
.user-avatar {
.common-avatar {
&:after {
content: "";
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 1;
}
}
}
.dialog-split { .dialog-split {
display: block; display: block;
position: absolute; position: absolute;

View File

@ -1,5 +1,6 @@
.overlay-y { .scrollbar-overlay {
overflow-y: overlay !important; overflow-y: auto;
overflow-y: overlay;
/* 滚动条美化 */ /* 滚动条美化 */
&::-webkit-scrollbar { &::-webkit-scrollbar {
@ -38,11 +39,9 @@
background: rgba(0, 0, 0, 0); background: rgba(0, 0, 0, 0);
} }
} }
.overlay-none {
.scrollbar-hidden {
&::-webkit-scrollbar { &::-webkit-scrollbar {
display: none; display: none;
} }
} }
.overlay-hidden {
overflow: hidden !important;
}