2025-05-20 17:53:49 +08:00

1317 lines
50 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="page-manage" :class="pageClass">
<div ref="boxMenu" class="manage-box-menu">
<Dropdown
class="page-manage-menu-dropdown main-menu"
trigger="click"
@on-click="settingRoute"
@on-visible-change="menuVisibleChange">
<div :class="['manage-box-title', visibleMenu ? 'menu-visible' : '']">
<div class="manage-box-avatar">
<UserAvatar :userid="userId" :size="36"/>
</div>
<span>{{userInfo.nickname}}</span>
<Badge v-if="!!clientNewVersion" class="manage-box-top-report" dot/>
<div class="manage-box-arrow">
<Icon type="ios-arrow-up" />
<Icon type="ios-arrow-down" />
</div>
</div>
<DropdownMenu slot="list">
<template v-for="item in menu">
<!--最近打开的任务-->
<Dropdown
v-if="item.path === 'taskBrowse'"
transfer
transfer-class-name="page-manage-menu-dropdown"
placement="right-start">
<DropdownItem :divided="!!item.divided">
<div class="manage-menu-flex">
{{$L(item.name)}}
<Icon type="ios-arrow-forward"></Icon>
</div>
</DropdownItem>
<DropdownMenu slot="list" v-if="taskBrowseLists.length > 0">
<DropdownItem
v-for="(item, key) in taskBrowseLists"
v-if="item.id > 0 && key < 10"
:key="key"
class="task-title"
@click.native="openTask(item)"
:name="item.name">
<span v-if="item.flow_item_name" :class="item.flow_item_status">{{item.flow_item_name}}</span>
<div class="task-title-text">{{ item.name }}</div>
</DropdownItem>
</DropdownMenu>
<DropdownMenu v-else slot="list">
<DropdownItem style="color:darkgrey">{{ $L('暂无打开记录') }}</DropdownItem>
</DropdownMenu>
</Dropdown>
<!-- 团队管理 -->
<Dropdown
v-else-if="item.path === 'team'"
transfer
transfer-class-name="page-manage-menu-dropdown"
placement="right-start">
<DropdownItem :divided="!!item.divided">
<div class="manage-menu-flex">
{{$L(item.name)}}
<Icon type="ios-arrow-forward"></Icon>
</div>
</DropdownItem>
<DropdownMenu slot="list">
<DropdownItem name="allUser">{{$L('团队管理')}}</DropdownItem>
<DropdownItem name="exportTask">{{$L('导出任务统计')}}</DropdownItem>
<DropdownItem name="exportOverdueTask">{{$L('导出超期任务')}}</DropdownItem>
<DropdownItem name="exportApprove">{{$L('导出审批数据')}}</DropdownItem>
<DropdownItem name="exportCheckin">{{$L('导出签到数据')}}</DropdownItem>
</DropdownMenu>
</Dropdown>
<!-- 其他菜单 -->
<DropdownItem
v-else-if="item.visible !== false"
:divided="!!item.divided"
:name="item.path"
:style="item.style || {}">
<div class="manage-menu-flex">
{{$L(item.name)}}
<Badge
v-if="item.path === 'version'"
class="manage-menu-report-badge"
:text="clientNewVersion"/>
<Badge
v-else-if="item.path === 'workReport' && reportUnreadNumber > 0"
class="manage-menu-report-badge"
:count="reportUnreadNumber"/>
<Badge
v-else-if="item.path === 'approve' && approveUnreadNumber > 0"
class="manage-menu-report-badge"
:count="approveUnreadNumber"/>
</div>
</DropdownItem>
</template>
</DropdownMenu>
</Dropdown>
<Scrollbar class-name="manage-item" @on-scroll="operateVisible = false">
<div class="menu-base">
<ul>
<li @click="toggleRoute('dashboard')" :class="classNameRoute('dashboard')">
<i class="taskfont">&#xe6fb;</i>
<div class="menu-title">{{$L('仪表盘')}}</div>
<Badge v-if="dashboardTask.overdue_count > 0" class="menu-badge" type="error" :overflow-count="999" :count="dashboardTask.overdue_count"/>
<Badge v-else-if="dashboardTask.today_count > 0" class="menu-badge" type="info" :overflow-count="999" :count="dashboardTask.today_count"/>
<Badge v-else-if="dashboardTask.todo_count > 0" class="menu-badge" type="primary" :overflow-count="999" :count="dashboardTask.todo_count"/>
</li>
<li @click="toggleRoute('calendar')" :class="classNameRoute('calendar')">
<i class="taskfont">&#xe6f5;</i>
<div class="menu-title">{{$L('日历')}}</div>
</li>
<li @click="toggleRoute('messenger')" :class="classNameRoute('messenger')">
<i class="taskfont">&#xe6eb;</i>
<div class="menu-title">{{$L('消息')}}</div>
<Badge class="menu-badge" :overflow-count="999" :text="msgUnreadMention"/>
</li>
<li @click="toggleRoute('file')" :class="classNameRoute('file')">
<i class="taskfont">&#xe6f3;</i>
<div class="menu-title">{{$L('文件')}}</div>
</li>
<li @click="toggleRoute('application')" :class="classNameRoute('application')">
<i class="taskfont">&#xe60c;</i>
<div class="menu-title">{{$L('应用')}}</div>
<Badge class="menu-badge" :overflow-count="999" :text="String((reportUnreadNumber + approveUnreadNumber) || '')"/>
</li>
<li v-for="(item, key) in filterMicroAppsMenusMain" :key="key" @click="onTabbarClick('microApp', item)">
<div class="apply-icon no-dark-content" :style="{backgroundImage: `url(${item.icon})`}"></div>
<div class="menu-title">{{item.label}}</div>
</li>
</ul>
</div>
<div ref="menuProject" class="menu-project">
<ul v-longpress="handleLongpress">
<li
v-for="(item, key) in projectLists"
:ref="`project_${item.id}`"
:key="key"
:class="classNameProject(item)"
:data-id="item.id"
@pointerdown="handleOperation"
@click="toggleRoute('project', {projectId: item.id})">
<div class="project-h1">
<em @click.stop="toggleOpenMenu(item.id)"></em>
<div class="title">{{item.name}}</div>
<div v-if="item.top_at" class="icon-top"></div>
<div v-if="item.task_my_num - item.task_my_complete > 0" class="num">{{item.task_my_num - item.task_my_complete}}</div>
</div>
<div class="project-h2">
<p>
<em>{{$L('我的')}}:</em>
<span>{{item.task_my_complete}}/{{item.task_my_num}}</span>
<Progress :percent="item.task_my_percent" :stroke-width="6" />
</p>
<p>
<em>{{$L('全部')}}:</em>
<span>{{item.task_complete}}/{{item.task_num}}</span>
<Progress :percent="item.task_percent" :stroke-width="6" />
</p>
</div>
</li>
<li v-if="projectKeyLoading > 0" class="loading"><Loading/></li>
</ul>
</div>
</Scrollbar>
<div
v-transfer-dom
:data-transfer="true"
class="operate-position"
:style="operateStyles"
v-show="operateVisible">
<Dropdown
trigger="custom"
:placement="windowLandscape ? 'bottom' : 'top'"
:visible="operateVisible"
@on-clickoutside="operateVisible = false"
transfer>
<div :style="{userSelect:operateVisible ? 'none' : 'auto', height: operateStyles.height}"></div>
<DropdownMenu slot="list">
<DropdownItem @click.native="handleTopClick">
{{ $L(operateItem.top_at ? '取消置顶' : '置顶该项目') }}
</DropdownItem>
<DropdownItem @click.native="handleChatClick">
{{ $L('项目讨论') }}
</DropdownItem>
</DropdownMenu>
</Dropdown>
</div>
<div
v-if="projectKeyValue || ((projectSearchShow || projectTotal > 20) && windowHeight > 600)"
class="manage-project-search">
<div class="search-pre">
<Loading v-if="projectKeyLoading > 0"/>
<Icon v-else type="ios-search" />
</div>
<Form class="search-form" action="javascript:void(0)" @submit.native.prevent="$A.eeuiAppKeyboardHide">
<Input type="search" v-model="projectKeyValue" :placeholder="$L(`共${projectTotal || cacheProjects.length}个项目,搜索...`)" clearable/>
</Form>
</div>
<ButtonGroup class="manage-box-new-group">
<Button class="manage-box-new" type="primary" icon="md-add" @click="onAddShow">{{$L('新建项目')}}</Button>
<Dropdown @on-click="onAddMenu" trigger="click">
<Button type="primary">
<Icon type="ios-arrow-down"></Icon>
</Button>
<DropdownMenu slot="list">
<DropdownItem name="project">{{$L('新建项目')}} ({{mateName}}+B)</DropdownItem>
<DropdownItem name="task">{{$L('新建任务')}} ({{mateName}}+K)</DropdownItem>
<DropdownItem name="group">{{$L('创建群组')}} ({{mateName}}+U)</DropdownItem>
<DropdownItem name="createMeeting">{{$L('新会议')}} ({{mateName}}+J)</DropdownItem>
<DropdownItem name="joinMeeting">{{$L('加入会议')}}</DropdownItem>
</DropdownMenu>
</Dropdown>
</ButtonGroup>
</div>
<div class="manage-box-main">
<keep-alive>
<router-view class="manage-box-view" @on-click="onTabbarClick"></router-view>
</keep-alive>
</div>
<!--新建项目-->
<Modal
v-model="addShow"
:title="$L('新建项目')"
:mask-closable="false">
<Form
ref="addProject"
:model="addData"
:rules="addRule"
v-bind="formOptions"
@submit.native.prevent>
<FormItem prop="name" :label="$L('项目名称')">
<Input ref="projectName" type="text" v-model="addData.name"></Input>
</FormItem>
<FormItem v-if="addData.columns" :label="$L('任务列表')">
<TagInput v-model="addData.columns"/>
</FormItem>
<FormItem v-else :label="$L('项目模板')">
<Select :value="0" @on-change="selectChange" :placeholder="$L('请选择模板')">
<Option v-for="(item, index) in columns" :value="index" :key="index">{{ item.name }}</Option>
</Select>
</FormItem>
<FormItem prop="flow" :label="$L('开启工作流')">
<RadioGroup v-model="addData.flow">
<Radio label="open">{{$L('开启')}}</Radio>
<Radio label="close">{{$L('关闭')}}</Radio>
</RadioGroup>
</FormItem>
</Form>
<div slot="footer" class="adaption">
<Button type="default" @click="addShow=false">{{$L('取消')}}</Button>
<Button type="primary" :loading="loadIng > 0" @click="onAddProject">{{$L('添加')}}</Button>
</div>
</Modal>
<!--添加任务-->
<Modal
v-model="addTaskShow"
:mask-closable="false"
:styles="{
width: '90%',
maxWidth: '640px'
}"
footer-hide>
<TaskAdd ref="addTask" v-model="addTaskShow"/>
</Modal>
<!--创建群组-->
<Modal
v-model="createGroupShow"
:title="$L('创建群组')"
:mask-closable="false">
<Form :model="createGroupData" v-bind="formOptions" @submit.native.prevent>
<FormItem prop="avatar" :label="$L('群头像')">
<ImgUpload v-model="createGroupData.avatar" :num="1" :width="512" :height="512" whcut="cover"/>
</FormItem>
<FormItem prop="userids" :label="$L('群成员')">
<UserSelect v-model="createGroupData.userids" :uncancelable="createGroupData.uncancelable" :multiple-max="100" show-bot :title="$L('选择项目成员')"/>
</FormItem>
<FormItem prop="chat_name" :label="$L('群名称')">
<Input v-model="createGroupData.chat_name" :placeholder="$L('输入群名称(选填)')"/>
</FormItem>
</Form>
<div slot="footer" class="adaption">
<Button type="default" @click="createGroupShow=false">{{$L('取消')}}</Button>
<Button type="primary" :loading="createGroupLoad > 0" @click="submitCreateGroup">{{$L('创建')}}</Button>
</div>
</Modal>
<!--导出任务统计-->
<TaskExport v-model="exportTaskShow"/>
<!--导出签到数据-->
<CheckinExport v-model="exportCheckinShow"/>
<!--导出审批数据-->
<ApproveExport v-model="exportApproveShow"/>
<!--任务详情-->
<TaskModal ref="taskModal"/>
<!--聊天窗口-->
<DialogModal ref="dialogModal"/>
<!--搜索框-->
<SearchBox ref="searchBox"/>
<!--工作报告-->
<DrawerOverlay
v-model="workReportShow"
placement="right"
:size="1200">
<Report v-if="workReportShow" v-model="workReportTab" @on-read="$store.dispatch('getReportUnread', 1000)" />
</DrawerOverlay>
<!--查看所有团队-->
<DrawerOverlay
v-model="allUserShow"
placement="right"
:size="1380">
<TeamManagement v-if="allUserShow" @on-close="allUserShow=false"/>
</DrawerOverlay>
<!--查看所有项目-->
<DrawerOverlay
v-model="allProjectShow"
placement="right"
:size="1200">
<ProjectManagement v-if="allProjectShow"/>
</DrawerOverlay>
<!--举报投诉管理-->
<DrawerOverlay
v-model="complaintShow"
placement="right"
:size="1200">
<ComplaintManagement v-if="complaintShow"/>
</DrawerOverlay>
<!--查看归档项目-->
<DrawerOverlay
v-model="archivedProjectShow"
placement="right"
:size="1200">
<ProjectArchived v-if="archivedProjectShow"/>
</DrawerOverlay>
<!--移动端选项卡-->
<transition name="mobile-slide">
<MobileTabbar v-if="mobileTabbar" @on-click="onTabbarClick"/>
</transition>
<!--审批中心-->
<DrawerOverlay v-model="approveShow" placement="right" :size="1380" class="approve-drawer">
<Approve v-if="approveShow" @on-close="approveShow=false"/>
</DrawerOverlay>
<!--审批详情-->
<DrawerOverlay v-model="approveDetailsShow" placement="right" :size="600">
<ApproveDetails v-if="approveDetailsShow" :data="approveDetails" @onBack="approveDetailsShow=false"/>
</DrawerOverlay>
<!--应用详情-->
<MicroApps/>
</div>
</template>
<script>
import { mapState, mapGetters } from 'vuex'
import ProjectArchived from "./manage/components/ProjectArchived";
import TeamManagement from "./manage/components/TeamManagement";
import ProjectManagement from "./manage/components/ProjectManagement";
import DrawerOverlay from "../components/DrawerOverlay";
import MobileTabbar from "../components/Mobile/Tabbar";
import TaskAdd from "./manage/components/TaskAdd";
import Report from "./manage/components/Report";
import longpress from "../directives/longpress";
import TransferDom from "../directives/transfer-dom";
import DialogModal from "./manage/components/DialogModal";
import TaskModal from "./manage/components/TaskModal";
import CheckinExport from "./manage/components/CheckinExport";
import TaskExport from "./manage/components/TaskExport";
import ApproveExport from "./manage/components/ApproveExport";
import ComplaintManagement from "./manage/components/ComplaintManagement";
import MicroApps from "../components/MicroApps";
import UserSelect from "../components/UserSelect.vue";
import ImgUpload from "../components/ImgUpload.vue";
import Approve from "./manage/approve/index.vue";
import ApproveDetails from "./manage/approve/details.vue";
import notificationKoro from "notification-koro1";
import emitter from "../store/events";
import SearchBox from "../components/SearchBox.vue";
export default {
components: {
Approve,
SearchBox,
ApproveDetails,
ImgUpload,
UserSelect,
TaskExport,
CheckinExport,
ApproveExport,
TaskModal,
DialogModal,
MobileTabbar,
TaskAdd,
Report,
DrawerOverlay,
ProjectManagement,
TeamManagement,
ProjectArchived,
MicroApps,
ComplaintManagement
},
directives: {longpress, TransferDom},
data() {
return {
loadIng: 0,
mateName: /macintosh|mac os x/i.test(navigator.userAgent) ? '⌘' : 'Ctrl',
addShow: false,
addData: {
name: '',
columns: '',
flow: 'open',
},
addRule: {
name: [
{ required: true, message: this.$L('请填写项目名称!'), trigger: 'change' },
{ type: 'string', min: 2, message: this.$L('项目名称至少2个字'), trigger: 'change' }
]
},
addTaskShow: false,
createGroupShow: false,
createGroupData: {},
createGroupLoad: 0,
exportTaskShow: false,
exportCheckinShow: false,
exportApproveShow: false,
projectKeyValue: '',
projectKeyLoading: 0,
projectSearchShow: false,
openMenu: {},
visibleMenu: false,
allUserShow: false,
allProjectShow: false,
archivedProjectShow: false,
natificationReady: false,
notificationManage: null,
workReportShow: false,
workReportTab: "my",
operateStyles: {},
operateVisible: false,
operateItem: {},
complaintShow: false,
approveShow: false,
approveDetails: {id: 0},
approveDetailsShow: false,
}
},
mounted() {
this.notificationInit();
//
emitter.on('addTask', this.onAddTask);
emitter.on('createGroup', this.onCreateGroup);
emitter.on('dialogMsgPush', this.addDialogMsg);
emitter.on('approveDetails', this.openApproveDetails);
emitter.on('openReport', this.openReport);
//
document.addEventListener('keydown', this.shortcutEvent);
},
activated() {
this.$store.dispatch("getUserInfo").catch(_ => {})
this.$store.dispatch("getTaskPriority", 1000)
this.$store.dispatch("getReportUnread", 1000)
this.$store.dispatch("getApproveUnread", 1000)
},
beforeDestroy() {
emitter.off('addTask', this.onAddTask);
emitter.off('createGroup', this.onCreateGroup);
emitter.off('dialogMsgPush', this.addDialogMsg);
emitter.off('approveDetails', this.openApproveDetails);
emitter.off('openReport', this.openReport);
//
document.removeEventListener('keydown', this.shortcutEvent);
},
deactivated() {
this.addShow = false;
},
computed: {
...mapState([
'userInfo',
'userIsAdmin',
'cacheUserBasic',
'cacheTasks',
'cacheDialogs',
'cacheProjects',
'projectTotal',
'wsOpenNum',
'columnTemplate',
'clientNewVersion',
'cacheTaskBrowse',
'reportUnreadNumber',
'approveUnreadNumber',
'dialogIns',
'formOptions',
'mobileTabbar',
'longpressData',
]),
...mapGetters(['dashboardTask', "filterMicroAppsMenusMain"]),
/**
* page className
* @param mobileTabbar
* @param userId
* @returns {{"show-tabbar", "not-logged": boolean}}
*/
pageClass({mobileTabbar, userId}) {
return {
'show-tabbar': mobileTabbar,
'not-logged': userId <= 0
}
},
/**
* 综合数(未读、提及、待办)
* @returns {string|string}
*/
msgUnreadMention() {
let num = 0; // 未读
let mention = 0; // 提及
this.cacheDialogs.some(dialog => {
num += $A.getDialogUnread(dialog, false);
mention += $A.getDialogMention(dialog);
})
if (num > 999) {
num = "999+"
}
if (mention > 999) {
mention = "999+"
}
const todoNum = this.msgTodoTotal // 待办
if (todoNum) {
if (mention) {
return `@${mention}·${todoNum}`
}
if (num) {
return `${num}·${todoNum}`
}
return todoNum;
}
if (num) {
if (mention) {
return `${num}·@${mention}`
}
return String(num)
}
if (mention) {
return `@${mention}`
}
return "";
},
/**
* 未读消息数
* @returns {number}
*/
msgAllUnread() {
let num = 0;
this.cacheDialogs.some(dialog => {
num += $A.getDialogNum(dialog);
})
return num;
},
/**
* 待办消息数
* @returns {string|null}
*/
msgTodoTotal() {
let todoNum = this.cacheDialogs.reduce((total, current) => total + (current.todo_num || 0), 0)
if (todoNum > 0) {
if (todoNum > 99) {
todoNum = "99+"
} else if (todoNum === 1) {
todoNum = ""
}
return `${this.$L("待办")}${todoNum}`
}
return null;
},
/**
* 未读消息 + 逾期任务
* @returns {number|*}
*/
unreadAndOverdue() {
if (this.userId > 0) {
return this.msgAllUnread + this.dashboardTask.overdue_count
} else {
return 0
}
},
menu() {
const {userIsAdmin} = this;
const array = [
{path: 'taskBrowse', name: '最近打开的任务'}
];
if (userIsAdmin) {
array.push(...[
{path: 'personal', name: '个人设置', divided: true},
{path: 'system', name: '系统设置'},
{path: 'license', name: 'License Key'},
{path: 'version', name: '更新版本', divided: true, visible: !!this.clientNewVersion},
{path: 'allProject', name: '所有项目', divided: true},
{path: 'archivedProject', name: '已归档的项目'},
{path: 'team', name: '团队管理', divided: true},
{path: 'complaint', name: '举报管理'},
])
} else {
array.push(...[
{path: 'personal', name: '个人设置', divided: true},
{path: 'version', name: '更新版本', divided: true, visible: !!this.clientNewVersion},
{path: 'workReport', name: '工作报告', divided: true},
{path: 'archivedProject', name: '已归档的项目'},
])
}
array.push(...[
{path: 'clearCache', name: '清除缓存', divided: true},
{path: 'logout', name: '退出登录', style: {color: '#f40'}}
])
return array
},
columns() {
const array = $A.cloneJSON(this.columnTemplate);
array.unshift({
name: this.$L('空白模板'),
columns: [],
})
return array
},
projectLists() {
const {projectKeyValue, cacheProjects} = this;
const data = $A.cloneJSON(cacheProjects).sort((a, b) => {
if (a.top_at || b.top_at) {
return $A.sortDay(b.top_at, a.top_at);
}
return b.id - a.id;
});
if (projectKeyValue) {
return data.filter(item => $A.strExists(`${item.name} ${item.desc}`, projectKeyValue));
}
return data;
},
taskBrowseLists() {
const {cacheTasks, cacheTaskBrowse, userId} = this;
return cacheTaskBrowse.filter(({userid}) => userid === userId).map(({id}) => {
return cacheTasks.find(task => task.id === id) || {}
});
},
},
watch: {
'$route' () {
this.chackPass();
},
userInfo() {
this.chackPass();
},
projectKeyValue(val) {
if (val == '') {
return;
}
setTimeout(() => {
if (this.projectKeyValue == val) {
this.searchProject();
}
}, 600);
},
wsOpenNum(num) {
if (num <= 1) return
this.$store.dispatch("getBasicData", 600)
},
workReportShow(show) {
if (!show) return
this.$store.dispatch("getReportUnread", 0)
},
windowActive(active) {
if (!active) return
this.$store.dispatch("getProjectByQueue", 600);
},
'cacheProjects.length': {
handler() {
this.$nextTick(_ => {
const menuProject = this.$refs.menuProject
const lastEl = $A.last($A.getObject(menuProject, 'children.0.children'))
if (lastEl) {
const lastRect = lastEl.getBoundingClientRect()
const menuRect = menuProject.getBoundingClientRect()
if (lastRect.top > menuRect.top + menuRect.height) {
this.projectSearchShow = true
return
}
}
this.projectSearchShow = false
})
},
immediate: true
},
unreadAndOverdue: {
handler(val) {
if (this.$Electron) {
this.$Electron.sendMessage('setDockBadge', val);
}
},
immediate: true
},
},
methods: {
chackPass() {
if (this.userInfo.changepass === 1) {
this.goForward({name: 'manage-setting-password'});
}
},
async toggleRoute(path, params) {
const location = {name: 'manage-' + path, params: params || {}};
const fileFolderId = await $A.IDBInt("fileFolderId");
if (path === 'file' && fileFolderId > 0) {
location.params.folderId = fileFolderId
}
this.goForward(location);
},
toggleOpenMenu(id) {
this.$set(this.openMenu, id, !this.openMenu[id])
},
settingRoute(path) {
switch (path) {
case 'allUser':
this.allUserShow = true;
return;
case 'allProject':
this.allProjectShow = true;
return;
case 'archivedProject':
this.archivedProjectShow = true;
return;
case 'exportTask':
this.exportTaskShow = true;
return;
case 'exportOverdueTask':
this.exportOverdueTask();
return;
case 'exportCheckin':
this.exportCheckinShow = true;
return;
case 'exportApprove':
this.exportApproveShow = true;
return;
case 'workReport':
this.openReport(this.reportUnreadNumber > 0 ? 'receive' : 'my');
return;
case 'version':
emitter.emit('updateNotification', null);
return;
case 'clearCache':
$A.IDBSet("clearCache", "handle").then(_ => {
$A.reloadUrl()
});
return;
case 'approve':
if (this.menu.findIndex((m) => m.path == path) > -1) {
this.goForward({name: 'manage-approve'});
}
return;
case 'complaint':
this.complaintShow = true;
return;
case 'logout':
$A.modalConfirm({
title: '退出登录',
content: '你确定要登出系统吗?',
loading: true,
onOk: () => {
return new Promise(async resolve => {
await this.$store.dispatch("logout", false)
resolve()
})
}
});
return;
}
if (this.menu.findIndex((m) => m.path == path) > -1) {
this.toggleRoute('setting-' + path);
}
},
exportOverdueTask() {
$A.modalConfirm({
title: '导出任务',
content: '你确定要导出所有超期任务吗?',
loading: true,
onOk: () => {
return new Promise((resolve, reject) => {
this.$store.dispatch("call", {
url: 'project/task/exportoverdue',
}).then(({data}) => {
resolve();
this.$store.dispatch('downUrl', {
url: data.url
});
}).catch(({msg}) => {
reject(msg);
});
})
},
});
},
menuVisibleChange(visible) {
this.visibleMenu = visible
},
classNameRoute(path) {
let name = this.routeName
if (name == 'manage-approve') {
name = `manage-application`
}
return {
"active": name === `manage-${path}`,
};
},
classNameProject(item) {
return {
"active": this.routeName === 'manage-project' && this.$route.params.projectId == item.id,
"open-menu": this.openMenu[item.id] === true,
"operate": item.id == this.operateItem.id && this.operateVisible
};
},
onAddMenu(name) {
switch (name) {
case 'project':
this.onAddShow()
break;
case 'task':
this.onAddTask(0)
break;
case 'group':
this.onCreateGroup([this.userId])
break;
case 'createMeeting':
emitter.emit('addMeeting', {
type: 'create',
userids: [this.userId],
});
break;
case 'joinMeeting':
emitter.emit('addMeeting', {
type: 'join',
});
break;
}
},
onAddShow() {
this.$store.dispatch("getColumnTemplate").catch(() => {})
this.addShow = true;
this.$nextTick(() => {
this.$refs.projectName.focus();
})
},
onAddProject() {
this.$refs.addProject.validate((valid) => {
if (valid) {
this.loadIng++;
this.$store.dispatch("call", {
url: 'project/add',
data: this.addData,
}).then(({data, msg}) => {
$A.messageSuccess(msg);
this.addShow = false;
this.$refs.addProject.resetFields();
this.$store.dispatch("saveProject", data);
this.toggleRoute('project', {projectId: data.id})
}).catch(({msg}) => {
$A.modalError(msg);
}).finally(_ => {
this.loadIng--;
});
}
});
},
searchProject() {
setTimeout(() => {
this.projectKeyLoading++;
}, 1000)
this.$store.dispatch("getProjects", {
keys: {
name: this.projectKeyValue
}
}).finally(_ => {
this.projectKeyLoading--;
});
},
selectChange(index) {
this.$nextTick(() => {
this.$set(this.addData, 'columns', this.columns[index].columns.join(','));
})
},
shortcutEvent(e) {
if (e.metaKey || e.ctrlKey) {
switch (e.keyCode) {
case 66: // B - 新建项目
e.preventDefault();
this.onAddShow()
break;
case 70:
case 191: // F、/ - 搜索
e.preventDefault();
this.$refs.searchBox.onShow();
break;
case 75:
case 78: // K、N - 新建任务
e.preventDefault();
this.onAddMenu('task')
break;
case 85: // U - 创建群组
this.onCreateGroup([this.userId])
break;
case 74: // J - 新会议
e.preventDefault();
this.onAddMenu('createMeeting')
break;
case 83: // S - 保存任务
if (this.$refs.taskModal.checkUpdate()) {
e.preventDefault();
}
break;
case 188: // , - 进入设置
e.preventDefault();
this.toggleRoute('setting')
break;
}
}
},
onAddTask(params) {
this.addTaskShow = true
this.$nextTick(_ => {
let data = {
owner: [this.userId],
}
if ($A.isJson(params)) {
data = params
} else if (/^[1-9]\d*$/.test(params)) {
data.column_id = params
}
this.$refs.addTask.setData(data)
})
},
openTask(task) {
this.$store.dispatch("openTask", task)
},
onCreateGroup(userids) {
if (!$A.isArray(userids)) {
userids = []
}
this.createGroupData = {userids, uncancelable: [this.userId]}
this.createGroupShow = true
},
submitCreateGroup() {
this.createGroupLoad++;
this.$store.dispatch("call", {
url: 'dialog/group/add',
data: this.createGroupData
}).then(({data, msg}) => {
$A.messageSuccess(msg);
this.createGroupShow = false;
this.createGroupData = {};
this.$store.dispatch("saveDialog", data);
this.$store.dispatch('openDialog', data.id)
}).catch(({msg}) => {
$A.modalError(msg);
}).finally(_ => {
this.createGroupLoad--;
});
},
addDialogMsg(data) {
if (!this.natificationReady && !this.$isEEUIApp) {
return; // 通知未准备好不通知
}
if (this.windowActive && data.dialog_id === $A.last(this.dialogIns)?.dialog_id) {
return; // 窗口激活且最后打开的会话是通知的会话时不通知
}
//
const {id, dialog_id, dialog_type, userid} = data;
if (userid == this.userId) {
return; // 自己的消息不通知
}
const body = $A.getMsgSimpleDesc(data);
this.__notificationId = id;
//
const notificationFuncA = async (title) => {
const tempUser = await this.$store.dispatch("getUserData", userid).catch(_ => {});
if (dialog_type === 'group' && tempUser) {
title = `${title} (${tempUser.nickname})`
}
notificationFuncB(title, tempUser?.userimg)
}
const notificationFuncB = (title, userimg) => {
if (this.__notificationId === id) {
this.__notificationId = null
if (this.$isEEUIApp) {
emitter.emit('openMobileNotification', {
userid: userid,
title,
desc: body,
callback: () => {
this.$store.dispatch('openDialog', dialog_id)
}
});
} else if (this.$Electron) {
this.$Electron.sendMessage('openNotification', {
icon: userimg || $A.originUrl('images/logo.png'),
title,
body,
data,
tag: "dialog",
hasReply: true,
replyPlaceholder: this.$L('回复消息')
})
} else {
this.notificationManage.replaceOptions({
icon: userimg || $A.originUrl('images/logo.png'),
body: body,
data: data,
tag: "dialog",
// requireInteraction: true // true为通知不自动关闭
});
this.notificationManage.replaceTitle(title);
this.notificationManage.userAgreed();
}
}
}
const dialog = this.cacheDialogs.find((item) => item.id == dialog_id);
if (dialog) {
notificationFuncA(dialog.name)
} else {
this.$store.dispatch("getDialogOne", dialog_id).then(({data}) => notificationFuncA(data.name)).catch(() => {})
}
},
openApproveDetails(id) {
this.approveDetailsShow = true;
this.$nextTick(() => {
this.approveDetails = {id};
})
},
openReport(tab) {
this.workReportTab = tab;
this.workReportShow = true;
},
handleLongpress(event) {
const {type, data, element} = this.longpressData;
this.$store.commit("longpress/clear")
//
if (type !== 'manage') {
return
}
const projectItem = this.projectLists.find(item => item.id == data.projectId)
if (!projectItem) {
return
}
this.operateVisible = false;
this.operateItem = $A.isJson(projectItem) ? projectItem : {};
requestAnimationFrame(() => {
const rect = element.getBoundingClientRect();
this.operateStyles = {
left: `${event.clientX}px`,
top: `${rect.top}px`,
height: `${rect.height}px`,
}
this.operateVisible = true;
})
},
handleOperation({currentTarget}) {
this.$store.commit("longpress/set", {
type: 'manage',
data: {
projectId: $A.getAttr(currentTarget, 'data-id')
},
element: currentTarget
})
},
handleTopClick() {
this.$store.dispatch("call", {
url: 'project/top',
data: {
project_id: this.operateItem.id,
},
}).then(({data}) => {
this.$store.dispatch("saveProject", data);
this.$nextTick(() => {
const active = this.$refs.menuProject.querySelector(".active")
if (active) {
$A.scrollIntoViewIfNeeded(active);
}
});
}).catch(({msg}) => {
$A.modalError(msg);
});
},
handleChatClick() {
this.$store.dispatch("openDialog", this.operateItem.dialog_id).catch(({msg}) => {
$A.modalError(msg || this.$L('打开会话失败'))
})
},
onTabbarClick(act, params = '') {
switch (act) {
case 'approve':
this.approveShow = true
break;
case 'createGroup':
this.onAddMenu('group')
break;
case 'addTask':
this.onAddTask(0)
break;
case 'addProject':
this.onAddShow()
break;
case 'allUser':
case 'complaint':
case 'workReport':
this.settingRoute(act)
break;
case 'microApp':
this.$store.dispatch("openMicroApp", params);
break;
case 'appstore':
this.$store.dispatch("openMicroApp", {
name: 'appstore',
url: 'appstore/internal',
disableScopecss: true,
autoDarkTheme: false,
});
break;
}
},
/**
* 初始化通知
*/
notificationInit() {
this.notificationManage = new notificationKoro(this.$L("打开通知成功"));
if (this.notificationManage.support) {
this.notificationManage.notificationEvent({
onclick: ({target}) => {
console.log("[Notification] A Click", target);
this.notificationManage.close();
this.notificationClick(target)
window.focus()
},
});
this.notificationPermission();
}
//
if (this.$Electron) {
this.$Electron.listener('clickNotification', target => {
console.log("[Notification] B Click", target);
this.$Electron.sendMessage('mainWindowActive')
this.notificationClick(target)
})
this.$Electron.listener('replyNotification', target => {
console.log("[Notification] B Reply", target);
this.notificationReply(target)
})
}
},
/**
* 通知权限
*/
notificationPermission() {
const userSelectFn = msg => {
switch (msg) {
// 随时可以调用通知
case 'already granted':
case 'granted':
return this.natificationReady = true;
// 请求权限通知被关闭,再次调用
case 'close':
return this.notificationManage.initNotification(userSelectFn);
// 请求权限当前被拒绝 || 曾经被拒绝
case 'denied':
case 'already denied':
if (msg === "denied") {
console.log("您刚刚拒绝显示通知 请在设置中更改设置");
} else {
console.log("您曾级拒绝显示通知 请在设置中更改设置");
}
break;
}
};
this.notificationManage.initNotification(userSelectFn);
},
/**
* 点击通知(客户端)
* @param target
*/
notificationClick(target) {
const {tag, data} = target;
if (tag == 'dialog') {
if (!$A.isJson(data)) {
return;
}
this.$nextTick(_ => {
this.$store.dispatch('openDialog', data.dialog_id)
})
}
},
/**
* 回复通知(客户端)
* @param target
*/
notificationReply(target) {
const {tag, data, reply} = target;
if (tag == 'dialog' && reply) {
this.$store.dispatch("call", {
url: 'dialog/msg/sendtext',
data: {
dialog_id: data.dialog_id,
text: reply,
},
method: 'post',
}).then(({data}) => {
this.$store.dispatch("saveDialogMsg", data);
this.$store.dispatch("increaseTaskMsgNum", {id: data.dialog_id});
this.$store.dispatch("increaseMsgReplyNum", {id: data.reply_id});
this.$store.dispatch("updateDialogLastMsg", data);
}).catch(({msg}) => {
$A.modalError(msg)
});
}
},
}
}
</script>