mirror of
https://gitee.com/lab1024/smart-admin.git
synced 2025-09-18 03:16:40 +08:00
前端首页的待办卡片中新增基于localStorage的待办添加与删除功能,并优化页面顶部的消息卡片
This commit is contained in:
parent
7d8879abcb
commit
694aa18452
@ -30,4 +30,6 @@ export default {
|
|||||||
HOME_QUICK_ENTRY: `${KEY_PREFIX}home_quick_entry`,
|
HOME_QUICK_ENTRY: `${KEY_PREFIX}home_quick_entry`,
|
||||||
// 通知信息已读
|
// 通知信息已读
|
||||||
NOTICE_READ: `${KEY_PREFIX}notice_read`,
|
NOTICE_READ: `${KEY_PREFIX}notice_read`,
|
||||||
|
// 待办
|
||||||
|
TO_BE_DONE: `${KEY_PREFIX}to_be_done`,
|
||||||
};
|
};
|
||||||
|
@ -37,7 +37,6 @@
|
|||||||
import { computed, ref, onMounted } from 'vue';
|
import { computed, ref, onMounted } from 'vue';
|
||||||
import { loginApi } from '/src/api/system/login-api';
|
import { loginApi } from '/src/api/system/login-api';
|
||||||
import { useUserStore } from '/@/store/modules/system/user';
|
import { useUserStore } from '/@/store/modules/system/user';
|
||||||
import { localClear } from '/@/utils/local-util';
|
|
||||||
import { smartSentry } from '/@/lib/smart-sentry';
|
import { smartSentry } from '/@/lib/smart-sentry';
|
||||||
import HeaderResetPassword from './header-reset-password-modal/index.vue';
|
import HeaderResetPassword from './header-reset-password-modal/index.vue';
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
@ -53,7 +52,6 @@
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
smartSentry.captureError(e);
|
smartSentry.captureError(e);
|
||||||
} finally {
|
} finally {
|
||||||
localClear();
|
|
||||||
useUserStore().logout();
|
useUserStore().logout();
|
||||||
location.reload();
|
location.reload();
|
||||||
}
|
}
|
||||||
|
@ -10,19 +10,21 @@
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<a-dropdown trigger="click" v-model:open="show">
|
<a-dropdown trigger="click" v-model:open="show">
|
||||||
<a-button type="text" @click="queryMessage" style="padding: 4px 5px">
|
<a-badge :count="unreadMessageCount + toBeDoneCount">
|
||||||
<a-badge :count="unreadMessageCount">
|
|
||||||
<div style="width: 26px; height: 26px">
|
<div style="width: 26px; height: 26px">
|
||||||
<BellOutlined :style="{ fontSize: '16px' }" />
|
<BellOutlined :style="{ fontSize: '16px' }" />
|
||||||
</div>
|
</div>
|
||||||
</a-badge>
|
</a-badge>
|
||||||
</a-button>
|
|
||||||
|
|
||||||
<template #overlay>
|
<template #overlay>
|
||||||
<a-card class="message-container" :bodyStyle="{ padding: 0 }">
|
<a-card class="message-container" :bodyStyle="{ padding: 0 }">
|
||||||
<a-spin :spinning="loading">
|
<a-spin :spinning="loading">
|
||||||
<a-tabs class="dropdown-tabs" centered :tabBarStyle="{ textAlign: 'center' }" style="width: 300px">
|
<a-tabs class="dropdown-tabs" centered :tabBarStyle="{ textAlign: 'center' }" style="width: 300px">
|
||||||
<a-tab-pane tab="未读消息" key="message">
|
<a-tab-pane key="message">
|
||||||
|
<template #tab>
|
||||||
|
未读消息
|
||||||
|
<a-badge :count="unreadMessageCount" showZero :offset="[0, -20]" />
|
||||||
|
</template>
|
||||||
<a-list class="tab-pane" size="small">
|
<a-list class="tab-pane" size="small">
|
||||||
<a-list-item v-for="item in messageList" :key="item.messageId">
|
<a-list-item v-for="item in messageList" :key="item.messageId">
|
||||||
<a-list-item-meta>
|
<a-list-item-meta>
|
||||||
@ -36,16 +38,34 @@
|
|||||||
</template>
|
</template>
|
||||||
</a-list-item-meta>
|
</a-list-item-meta>
|
||||||
</a-list-item>
|
</a-list-item>
|
||||||
|
<a-list-item v-if="unreadMessageCount !== 0">
|
||||||
|
<a-button type="text" @click="gotoMessage" style="padding: 4px 5px"> ... 查看更多 </a-button>
|
||||||
|
</a-list-item>
|
||||||
</a-list>
|
</a-list>
|
||||||
</a-tab-pane>
|
</a-tab-pane>
|
||||||
<a-tab-pane tab="待办工作" key="todo">
|
<a-tab-pane key="TO_BE_DONE">
|
||||||
<a-list class="tab-pane" />
|
<template #tab>
|
||||||
|
待办工作
|
||||||
|
<a-badge :count="toBeDoneCount" showZero :offset="[0, -20]" />
|
||||||
|
</template>
|
||||||
|
<a-list class="tab-pane" size="small" :locale="{ emptyText: '暂无待办' }">
|
||||||
|
<a-list-item v-for="(item, index) in toBeDoneList" :key="index">
|
||||||
|
<a-list-item-meta>
|
||||||
|
<template #title>
|
||||||
|
<a-badge status="error" />
|
||||||
|
<a-tag v-if="item.starFlag" color="red">重要</a-tag>
|
||||||
|
<span>{{ item.title }}</span>
|
||||||
|
</template>
|
||||||
|
</a-list-item-meta>
|
||||||
|
</a-list-item>
|
||||||
|
</a-list>
|
||||||
</a-tab-pane>
|
</a-tab-pane>
|
||||||
</a-tabs>
|
</a-tabs>
|
||||||
</a-spin>
|
</a-spin>
|
||||||
</a-card>
|
</a-card>
|
||||||
</template>
|
</template>
|
||||||
</a-dropdown>
|
</a-dropdown>
|
||||||
|
<MessageDetail ref="messageDetailRef" @refresh="queryMessage" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
@ -57,6 +77,9 @@
|
|||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import { theme } from 'ant-design-vue';
|
import { theme } from 'ant-design-vue';
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
|
import MessageDetail from '/@/views/system/account/components/message/components/message-detail.vue';
|
||||||
|
import localKey from '/@/constants/local-storage-key-const';
|
||||||
|
import { localRead } from '/@/utils/local-util';
|
||||||
|
|
||||||
const { useToken } = theme;
|
const { useToken } = theme;
|
||||||
const { token } = useToken();
|
const { token } = useToken();
|
||||||
@ -64,6 +87,8 @@
|
|||||||
defineExpose({ showMessage });
|
defineExpose({ showMessage });
|
||||||
|
|
||||||
function showMessage() {
|
function showMessage() {
|
||||||
|
queryMessage();
|
||||||
|
loadToBeDoneList();
|
||||||
show.value = true;
|
show.value = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,6 +115,8 @@
|
|||||||
readFlag: false,
|
readFlag: false,
|
||||||
});
|
});
|
||||||
messageList.value = responseModel.data.list;
|
messageList.value = responseModel.data.list;
|
||||||
|
// 若中途有新消息了 打开列表也能及时更新未读数量
|
||||||
|
useUserStore().queryUnreadMessageCount();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
smartSentry.captureError(e);
|
smartSentry.captureError(e);
|
||||||
} finally {
|
} finally {
|
||||||
@ -103,6 +130,30 @@
|
|||||||
router.push({ path: '/account', query: { menuId: 'message' } });
|
router.push({ path: '/account', query: { menuId: 'message' } });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ------------------------- 待办工作 -------------------------
|
||||||
|
|
||||||
|
// 待办工作数
|
||||||
|
const toBeDoneCount = computed(() => {
|
||||||
|
return useUserStore().toBeDoneCount;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 待办工作列表
|
||||||
|
const toBeDoneList = ref([]);
|
||||||
|
|
||||||
|
const loadToBeDoneList = async () => {
|
||||||
|
try {
|
||||||
|
loading.value = true;
|
||||||
|
let localToBeDoneList = localRead(localKey.TO_BE_DONE);
|
||||||
|
if (localToBeDoneList) {
|
||||||
|
toBeDoneList.value = JSON.parse(localToBeDoneList).filter((e) => !e.doneFlag);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
smartSentry.captureError(err);
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// ------------------------- 时间计算 -------------------------
|
// ------------------------- 时间计算 -------------------------
|
||||||
function timeago(dateStr) {
|
function timeago(dateStr) {
|
||||||
let dateTimeStamp = dayjs(dateStr).toDate().getTime();
|
let dateTimeStamp = dayjs(dateStr).toDate().getTime();
|
||||||
@ -189,4 +240,9 @@
|
|||||||
background-color: @base-bg-color;
|
background-color: @base-bg-color;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tab-pane {
|
||||||
|
height: 250px;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -18,7 +18,9 @@
|
|||||||
size="small"
|
size="small"
|
||||||
/>
|
/>
|
||||||
<!---消息通知--->
|
<!---消息通知--->
|
||||||
|
<a-button type="text" @click="showMessage" style="padding: 4px 5px">
|
||||||
<HeaderMessage ref="headerMessage" />
|
<HeaderMessage ref="headerMessage" />
|
||||||
|
</a-button>
|
||||||
<!---国际化--->
|
<!---国际化--->
|
||||||
<!-- <a-button type="text" @click="showSetting" class="operate-icon">
|
<!-- <a-button type="text" @click="showSetting" class="operate-icon">
|
||||||
<template #icon><switcher-outlined /></template>
|
<template #icon><switcher-outlined /></template>
|
||||||
|
@ -9,7 +9,8 @@
|
|||||||
*/
|
*/
|
||||||
import { message, Modal } from 'ant-design-vue';
|
import { message, Modal } from 'ant-design-vue';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { localClear, localRead } from '/@/utils/local-util';
|
import { localRead } from '/@/utils/local-util';
|
||||||
|
import { useUserStore } from '/@/store/modules/system/user';
|
||||||
import { decryptData, encryptData } from './encrypt';
|
import { decryptData, encryptData } from './encrypt';
|
||||||
import { DATA_TYPE_ENUM } from '../constants/common-const';
|
import { DATA_TYPE_ENUM } from '../constants/common-const';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
@ -25,7 +26,7 @@ const smartAxios = axios.create({
|
|||||||
|
|
||||||
// 退出系统
|
// 退出系统
|
||||||
function logout() {
|
function logout() {
|
||||||
localClear();
|
useUserStore.logout();
|
||||||
location.href = '/';
|
location.href = '/';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ import { PAGE_PATH_404, PAGE_PATH_LOGIN } from '/@/constants/common-const';
|
|||||||
import { HOME_PAGE_NAME } from '/@/constants/system/home-const';
|
import { HOME_PAGE_NAME } from '/@/constants/system/home-const';
|
||||||
import SmartLayout from '../layout/index.vue';
|
import SmartLayout from '../layout/index.vue';
|
||||||
import { useUserStore } from '/@/store/modules/system/user';
|
import { useUserStore } from '/@/store/modules/system/user';
|
||||||
import { localClear, localRead } from '/@/utils/local-util';
|
import { localRead } from '/@/utils/local-util';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import LocalStorageKeyConst from '/@/constants/local-storage-key-const.js';
|
import LocalStorageKeyConst from '/@/constants/local-storage-key-const.js';
|
||||||
|
|
||||||
@ -41,7 +41,7 @@ router.beforeEach(async (to, from, next) => {
|
|||||||
// 验证登录
|
// 验证登录
|
||||||
const token = localRead(LocalStorageKeyConst.USER_TOKEN);
|
const token = localRead(LocalStorageKeyConst.USER_TOKEN);
|
||||||
if (!token) {
|
if (!token) {
|
||||||
localClear();
|
useUserStore().logout();
|
||||||
next({ path: PAGE_PATH_LOGIN });
|
next({ path: PAGE_PATH_LOGIN });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -12,10 +12,10 @@ import { defineStore } from 'pinia';
|
|||||||
import localKey from '/@/constants/local-storage-key-const';
|
import localKey from '/@/constants/local-storage-key-const';
|
||||||
import { HOME_PAGE_NAME } from '/@/constants/system/home-const';
|
import { HOME_PAGE_NAME } from '/@/constants/system/home-const';
|
||||||
import { MENU_TYPE_ENUM } from '/@/constants/system/menu-const';
|
import { MENU_TYPE_ENUM } from '/@/constants/system/menu-const';
|
||||||
import { localClear, localRead, localSave } from '/@/utils/local-util';
|
|
||||||
import LocalStorageKeyConst from '/@/constants/local-storage-key-const';
|
|
||||||
import { messageApi } from '/@/api/support/message-api.js';
|
import { messageApi } from '/@/api/support/message-api.js';
|
||||||
import { smartSentry } from '/@/lib/smart-sentry.js';
|
import { smartSentry } from '/@/lib/smart-sentry.js';
|
||||||
|
import { localRead, localSave, localRemove } from '/@/utils/local-util';
|
||||||
|
|
||||||
|
|
||||||
export const useUserStore = defineStore({
|
export const useUserStore = defineStore({
|
||||||
id: 'userStore',
|
id: 'userStore',
|
||||||
@ -61,13 +61,15 @@ export const useUserStore = defineStore({
|
|||||||
keepAliveIncludes: [],
|
keepAliveIncludes: [],
|
||||||
// 未读消息数量
|
// 未读消息数量
|
||||||
unreadMessageCount: 0,
|
unreadMessageCount: 0,
|
||||||
|
// 待办工作数
|
||||||
|
toBeDoneCount: 0,
|
||||||
}),
|
}),
|
||||||
getters: {
|
getters: {
|
||||||
getToken(state) {
|
getToken(state) {
|
||||||
if (state.token) {
|
if (state.token) {
|
||||||
return state.token;
|
return state.token;
|
||||||
}
|
}
|
||||||
return localRead(LocalStorageKeyConst.USER_TOKEN);
|
return localRead(localKey.USER_TOKEN);
|
||||||
},
|
},
|
||||||
//是否初始化了 路由
|
//是否初始化了 路由
|
||||||
getMenuRouterInitFlag(state) {
|
getMenuRouterInitFlag(state) {
|
||||||
@ -115,7 +117,9 @@ export const useUserStore = defineStore({
|
|||||||
this.tagNav = [];
|
this.tagNav = [];
|
||||||
this.userInfo = {};
|
this.userInfo = {};
|
||||||
this.unreadMessageCount = 0;
|
this.unreadMessageCount = 0;
|
||||||
localClear();
|
localRemove(localKey.USER_TOKEN);
|
||||||
|
localRemove(localKey.USER_POINTS);
|
||||||
|
localRemove(localKey.USER_TAG_NAV);
|
||||||
},
|
},
|
||||||
// 查询未读消息数量
|
// 查询未读消息数量
|
||||||
async queryUnreadMessageCount() {
|
async queryUnreadMessageCount() {
|
||||||
@ -126,6 +130,16 @@ export const useUserStore = defineStore({
|
|||||||
smartSentry.captureError(e);
|
smartSentry.captureError(e);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
async queryToBeDoneList() {
|
||||||
|
try {
|
||||||
|
let localToBeDoneList = localRead(localKey.TO_BE_DONE);
|
||||||
|
if (localToBeDoneList) {
|
||||||
|
this.toBeDoneCount = JSON.parse(localToBeDoneList).filter((e) => !e.doneFlag).length;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
smartSentry.captureError(err);
|
||||||
|
}
|
||||||
|
},
|
||||||
//设置登录信息
|
//设置登录信息
|
||||||
setUserLoginInfo(data) {
|
setUserLoginInfo(data) {
|
||||||
// 用户基本信息
|
// 用户基本信息
|
||||||
@ -157,6 +171,8 @@ export const useUserStore = defineStore({
|
|||||||
|
|
||||||
// 获取用户未读消息
|
// 获取用户未读消息
|
||||||
this.queryUnreadMessageCount();
|
this.queryUnreadMessageCount();
|
||||||
|
// 获取待办工作数
|
||||||
|
this.queryToBeDoneList();
|
||||||
},
|
},
|
||||||
setToken(token) {
|
setToken(token) {
|
||||||
this.token = token;
|
this.token = token;
|
||||||
|
@ -19,3 +19,7 @@ export const localRead = (key) => {
|
|||||||
export const localClear = () => {
|
export const localClear = () => {
|
||||||
localStorage.clear();
|
localStorage.clear();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const localRemove = (key) => {
|
||||||
|
localStorage.removeItem(key);
|
||||||
|
};
|
||||||
|
@ -43,7 +43,7 @@
|
|||||||
</a-form>
|
</a-form>
|
||||||
|
|
||||||
<a-table size="small" :dataSource="tableData" :columns="columns" rowKey="messageId" :pagination="false" bordered>
|
<a-table size="small" :dataSource="tableData" :columns="columns" rowKey="messageId" :pagination="false" bordered>
|
||||||
<template #bodyCell="{ text, record, index, column }">
|
<template #bodyCell="{ text, record, column }">
|
||||||
<template v-if="column.dataIndex === 'messageType'">
|
<template v-if="column.dataIndex === 'messageType'">
|
||||||
<span>{{ $smartEnumPlugin.getDescByValue('MESSAGE_TYPE_ENUM', text) }}</span>
|
<span>{{ $smartEnumPlugin.getDescByValue('MESSAGE_TYPE_ENUM', text) }}</span>
|
||||||
</template>
|
</template>
|
||||||
|
@ -1,158 +0,0 @@
|
|||||||
<!--
|
|
||||||
* 已办/代办
|
|
||||||
*
|
|
||||||
* @Author: 1024创新实验室-主任:卓大
|
|
||||||
* @Date: 2022-09-12 22:34:00
|
|
||||||
* @Wechat: zhuda1024
|
|
||||||
* @Email: lab1024@163.com
|
|
||||||
* @Copyright 1024创新实验室 ( https://1024lab.net ),Since 2012
|
|
||||||
*
|
|
||||||
-->
|
|
||||||
<template>
|
|
||||||
<default-home-card icon="Star" title="已办待办">
|
|
||||||
<div style="height: 280px">
|
|
||||||
<div class="center column">
|
|
||||||
<a-space direction="vertical" style="width: 100%">
|
|
||||||
<div v-for="(item, index) in toDoList" :key="index" :class="['to-do', { done: item.doneFlag }]">
|
|
||||||
<a-checkbox v-model:checked="item.doneFlag">
|
|
||||||
<span class="task">{{ item.title }}</span>
|
|
||||||
</a-checkbox>
|
|
||||||
<div class="star-icon" @click="itemStar(item)">
|
|
||||||
<StarFilled v-if="item.starFlag" style="color: #ff8c00" />
|
|
||||||
<StarOutlined v-else style="color: #c0c0c0" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div v-for="(item, index) in doneList" :key="index" :class="['to-do', { done: item.doneFlag }]">
|
|
||||||
<a-checkbox v-model:checked="item.doneFlag">
|
|
||||||
<span class="task">{{ item.title }}</span>
|
|
||||||
</a-checkbox>
|
|
||||||
<div class="star-icon" @click="itemStar(item)">
|
|
||||||
<StarFilled v-if="item.starFlag" style="color: #ff8c00" />
|
|
||||||
<StarOutlined v-else style="color: #c0c0c0" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</a-space>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</default-home-card>
|
|
||||||
</template>
|
|
||||||
<script setup>
|
|
||||||
import DefaultHomeCard from '/@/views/system/home/components/default-home-card.vue';
|
|
||||||
import { computed, ref } from 'vue';
|
|
||||||
import dayjs from 'dayjs';
|
|
||||||
import { message } from 'ant-design-vue';
|
|
||||||
|
|
||||||
let taskList = ref([
|
|
||||||
{
|
|
||||||
title: '周五下班前需要提交周报',
|
|
||||||
doneFlag: true,
|
|
||||||
starFlag: true,
|
|
||||||
starTime: 0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '为SmartAdmin前端小组分配任务',
|
|
||||||
doneFlag: false,
|
|
||||||
starFlag: false,
|
|
||||||
starTime: 0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '跟进团建内容事宜',
|
|
||||||
doneFlag: false,
|
|
||||||
starFlag: true,
|
|
||||||
starTime: 0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '跟进客户定制一个软件平台',
|
|
||||||
doneFlag: false,
|
|
||||||
starFlag: false,
|
|
||||||
starTime: 0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '下个版本的需求确认',
|
|
||||||
doneFlag: false,
|
|
||||||
starFlag: false,
|
|
||||||
starTime: 0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '线上版本发布',
|
|
||||||
doneFlag: true,
|
|
||||||
starFlag: true,
|
|
||||||
starTime: dayjs().unix(),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '周一财务报销',
|
|
||||||
doneFlag: true,
|
|
||||||
starFlag: false,
|
|
||||||
starTime: 0,
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
|
|
||||||
let toDoList = computed(() => {
|
|
||||||
return taskList.value.filter((e) => !e.doneFlag).sort((a, b) => b.starTime - a.starTime);
|
|
||||||
});
|
|
||||||
|
|
||||||
let doneList = computed(() => {
|
|
||||||
return taskList.value.filter((e) => e.doneFlag);
|
|
||||||
});
|
|
||||||
|
|
||||||
function itemStar(item) {
|
|
||||||
item.starFlag = !item.starFlag;
|
|
||||||
if (item.starFlag) {
|
|
||||||
item.starTime = dayjs().unix();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//-------------------------任务新建-----------------------
|
|
||||||
let taskTitle = ref('');
|
|
||||||
function addTask() {
|
|
||||||
if (!taskTitle.value) {
|
|
||||||
message.warn('请输入任务标题');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let data = {
|
|
||||||
title: taskTitle.value,
|
|
||||||
doneFlag: false,
|
|
||||||
starFlag: false,
|
|
||||||
starTime: 0,
|
|
||||||
};
|
|
||||||
taskList.value.unshift(data);
|
|
||||||
taskTitle.value = '';
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
<style lang="less" scoped>
|
|
||||||
.center {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
height: 100%;
|
|
||||||
|
|
||||||
&.column {
|
|
||||||
flex-direction: column;
|
|
||||||
width: 100%;
|
|
||||||
padding: 0 10px;
|
|
||||||
justify-content: flex-start;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.to-do {
|
|
||||||
width: 100%;
|
|
||||||
border: 1px solid #d3d3d3;
|
|
||||||
border-radius: 4px;
|
|
||||||
padding: 4px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
.star-icon {
|
|
||||||
margin-left: auto;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.done {
|
|
||||||
text-decoration: line-through;
|
|
||||||
color: #8c8c8c;
|
|
||||||
|
|
||||||
.task {
|
|
||||||
color: #8c8c8c;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -0,0 +1,172 @@
|
|||||||
|
<!--
|
||||||
|
* 已办/代办
|
||||||
|
*
|
||||||
|
* @Author: 1024创新实验室-主任:卓大
|
||||||
|
* @Date: 2022-09-12 22:34:00
|
||||||
|
* @Copyright 1024创新实验室 ( https://1024lab.net ),Since 2012
|
||||||
|
*
|
||||||
|
-->
|
||||||
|
<template>
|
||||||
|
<default-home-card extra="添加" icon="StarTwoTone" title="待办工作" @extraClick="showAddToBeDone">
|
||||||
|
<div style="height: 280px">
|
||||||
|
<div class="center column">
|
||||||
|
<a-space direction="vertical" style="width: 100%">
|
||||||
|
<a-empty v-if="$lodash.isEmpty(toBeDoneList)" description="暂无待办工作" />
|
||||||
|
<div v-for="(item, index) in toDoList" :key="index" :class="['to-do', { done: item.doneFlag }]">
|
||||||
|
<a-checkbox v-model:checked="item.doneFlag" @change="handleCheckbox">
|
||||||
|
<span class="task">{{ item.title }}</span>
|
||||||
|
</a-checkbox>
|
||||||
|
<div v-if="!item.doneFlag" class="star-icon" @click="itemStar(item)">
|
||||||
|
<StarFilled v-if="item.starFlag" style="color: #ff8c00" />
|
||||||
|
<StarOutlined v-else style="color: #c0c0c0" />
|
||||||
|
</div>
|
||||||
|
<close-circle-outlined class="delete-icon" @click="toDelete(item)" />
|
||||||
|
</div>
|
||||||
|
<div v-for="(item, index) in doneList" :key="index" :class="['to-do', { done: item.doneFlag }]">
|
||||||
|
<a-checkbox v-model:checked="item.doneFlag" @change="handleCheckbox">
|
||||||
|
<span class="task">{{ item.title }}</span>
|
||||||
|
</a-checkbox>
|
||||||
|
<div v-if="!item.doneFlag" class="star-icon" @click="itemStar(item)">
|
||||||
|
<StarFilled v-if="item.starFlag" style="color: #ff8c00" />
|
||||||
|
<StarOutlined v-else style="color: #c0c0c0" />
|
||||||
|
</div>
|
||||||
|
<close-circle-outlined class="delete-icon" @click="toDelete(item)" />
|
||||||
|
</div>
|
||||||
|
</a-space>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</default-home-card>
|
||||||
|
<ToBeDoneModal ref="toBeDoneModalRef" @addToBeDone="addToBeDone" />
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import DefaultHomeCard from '/@/views/system/home/components/default-home-card.vue';
|
||||||
|
import ToBeDoneModal from './to-be-done-modal.vue';
|
||||||
|
import localKey from '/@/constants/local-storage-key-const';
|
||||||
|
import { localRead, localSave } from '/@/utils/local-util';
|
||||||
|
import { useUserStore } from '/@/store/modules/system/user.js';
|
||||||
|
import { computed, ref, onMounted } from 'vue';
|
||||||
|
import { Modal } from 'ant-design-vue';
|
||||||
|
|
||||||
|
let toBeDoneList = ref([]);
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
initTaskList();
|
||||||
|
});
|
||||||
|
|
||||||
|
function initTaskList() {
|
||||||
|
let localTaskList = localRead(localKey.TO_BE_DONE);
|
||||||
|
if (localTaskList) {
|
||||||
|
toBeDoneList.value = JSON.parse(localTaskList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let toDoList = computed(() => {
|
||||||
|
return toBeDoneList.value.filter((e) => !e.doneFlag);
|
||||||
|
});
|
||||||
|
|
||||||
|
let doneList = computed(() => {
|
||||||
|
return toBeDoneList.value.filter((e) => e.doneFlag);
|
||||||
|
});
|
||||||
|
|
||||||
|
function handleCheckbox(e) {
|
||||||
|
localSave(localKey.TO_BE_DONE, JSON.stringify(toBeDoneList.value));
|
||||||
|
}
|
||||||
|
|
||||||
|
function itemStar(data) {
|
||||||
|
data.starFlag = !data.starFlag;
|
||||||
|
const index = toBeDoneList.value.findIndex((item) => item.title === data.title);
|
||||||
|
toBeDoneList.value.splice(index, 1);
|
||||||
|
if (data.starFlag) {
|
||||||
|
toBeDoneList.value.unshift(data);
|
||||||
|
} else {
|
||||||
|
toBeDoneList.value.push(data);
|
||||||
|
}
|
||||||
|
localSave(localKey.TO_BE_DONE, JSON.stringify(toBeDoneList.value));
|
||||||
|
}
|
||||||
|
|
||||||
|
//-------------------------任务新建-----------------------
|
||||||
|
|
||||||
|
let toBeDoneModalRef = ref();
|
||||||
|
|
||||||
|
function showAddToBeDone() {
|
||||||
|
toBeDoneModalRef.value.showModal();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加待办工作
|
||||||
|
function addToBeDone(data) {
|
||||||
|
toBeDoneList.value.unshift(data);
|
||||||
|
useUserStore().toBeDoneCount = toBeDoneList.value.length;
|
||||||
|
localSave(localKey.TO_BE_DONE, JSON.stringify(toBeDoneList.value));
|
||||||
|
}
|
||||||
|
|
||||||
|
function toDelete(data) {
|
||||||
|
if (!data.doneFlag) {
|
||||||
|
Modal.confirm({
|
||||||
|
title: '提示',
|
||||||
|
content: '确定要删除吗?',
|
||||||
|
okText: '删除',
|
||||||
|
okType: 'danger',
|
||||||
|
onOk() {
|
||||||
|
deleteToBeDone(data);
|
||||||
|
},
|
||||||
|
cancelText: '取消',
|
||||||
|
onCancel() {},
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
deleteToBeDone(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除待办工作
|
||||||
|
function deleteToBeDone(data) {
|
||||||
|
const index = toBeDoneList.value.findIndex((item) => item.title === data.title);
|
||||||
|
toBeDoneList.value.splice(index, 1);
|
||||||
|
useUserStore().toBeDoneCount = toBeDoneList.value.length;
|
||||||
|
localSave(localKey.TO_BE_DONE, JSON.stringify(toBeDoneList.value));
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.center {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
height: 100%;
|
||||||
|
overflow-y: auto;
|
||||||
|
|
||||||
|
&.column {
|
||||||
|
flex-direction: column;
|
||||||
|
width: 100%;
|
||||||
|
padding: 0 10px;
|
||||||
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.to-do {
|
||||||
|
width: 100%;
|
||||||
|
border: 1px solid #d3d3d3;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 4px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
.star-icon {
|
||||||
|
margin-left: auto;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.done {
|
||||||
|
text-decoration: line-through;
|
||||||
|
color: #8c8c8c;
|
||||||
|
|
||||||
|
.task {
|
||||||
|
color: #8c8c8c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.delete-icon {
|
||||||
|
color: #f08080;
|
||||||
|
padding-left: 10px;
|
||||||
|
top: -5px;
|
||||||
|
right: -5px;
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
</style>
|
@ -0,0 +1,63 @@
|
|||||||
|
<template>
|
||||||
|
<a-modal v-model:open="visible" title="新建待办" @close="onClose">
|
||||||
|
<a-form ref="formRef" :model="form" :rules="rules">
|
||||||
|
<a-form-item label="标题" name="title">
|
||||||
|
<a-input v-model:value="form.title" placeholder="请输入标题" />
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
<template #footer>
|
||||||
|
<a-button @click="onClose">取消</a-button>
|
||||||
|
<a-button type="primary" @click="onSubmit">确定</a-button>
|
||||||
|
</template>
|
||||||
|
</a-modal>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { reactive, ref } from 'vue';
|
||||||
|
import { message } from 'ant-design-vue';
|
||||||
|
import _ from 'lodash';
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
showModal,
|
||||||
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits(['addToBeDone']);
|
||||||
|
|
||||||
|
// 组件ref
|
||||||
|
const formRef = ref();
|
||||||
|
|
||||||
|
const formDefault = {
|
||||||
|
title: undefined,
|
||||||
|
doneFlag: false,
|
||||||
|
starFlag: false,
|
||||||
|
starTime: 0,
|
||||||
|
};
|
||||||
|
let form = reactive({ ...formDefault });
|
||||||
|
const rules = {
|
||||||
|
title: [{ required: true, message: '标题不能为空' }],
|
||||||
|
};
|
||||||
|
|
||||||
|
const visible = ref(false);
|
||||||
|
|
||||||
|
function showModal() {
|
||||||
|
visible.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function onClose() {
|
||||||
|
Object.assign(form, formDefault);
|
||||||
|
visible.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function onSubmit() {
|
||||||
|
formRef.value
|
||||||
|
.validate()
|
||||||
|
.then(() => {
|
||||||
|
emit('addToBeDone', _.cloneDeep(form));
|
||||||
|
onClose();
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.log('error', error);
|
||||||
|
message.error('参数验证错误,请仔细填写表单数据!');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style lang="less" scoped></style>
|
@ -70,7 +70,7 @@
|
|||||||
import HomeNotice from './home-notice.vue';
|
import HomeNotice from './home-notice.vue';
|
||||||
import HomeQuickEntry from './components/quick-entry/home-quick-entry.vue';
|
import HomeQuickEntry from './components/quick-entry/home-quick-entry.vue';
|
||||||
import OfficialAccountCard from './components/official-account-card.vue';
|
import OfficialAccountCard from './components/official-account-card.vue';
|
||||||
import ToBeDoneCard from './components/to-be-done-card.vue';
|
import ToBeDoneCard from './components/to-be-done-card/home-to-be-done.vue';
|
||||||
import ChangelogCard from './components/changelog-card.vue';
|
import ChangelogCard from './components/changelog-card.vue';
|
||||||
import Gauge from './components/echarts/gauge.vue';
|
import Gauge from './components/echarts/gauge.vue';
|
||||||
import Category from './components/echarts/category.vue';
|
import Category from './components/echarts/category.vue';
|
||||||
|
Loading…
Reference in New Issue
Block a user