【V3.5.0】1、【新增】轻量级定时任务 SmartJob;2、【新增】站内信;3、【新增】个人中心;4、【新增】岗位管理;5、【优化】部门员工管理

This commit is contained in:
zhuoda
2024-07-16 00:20:02 +08:00
parent 23e8ea55e1
commit 716b6303e3
504 changed files with 59745 additions and 1110 deletions

View File

@@ -1,24 +1,28 @@
<!--
* 头像
*
* @Author: 1024创新实验室-主任卓大
* @Date: 2022-09-06 20:02:01
* @Wechat: zhuda1024
* @Email: lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net Since 2012
*
* @Author: 1024创新实验室-主任卓大
* @Date: 2022-09-06 20:02:01
* @Wechat: zhuda1024
* @Email: lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net Since 2012
-->
<template>
<a-dropdown class="header-trigger">
<div class="wrapper">
<a-avatar style="margin: 0 5px" :size="24" id="smartAdminAvatar">
<img class="avatar-image" :src="avatar" v-if="avatar" />
<a-avatar v-else style="margin: 0 5px" :size="20" id="smartAdminAvatar">
{{ avatarName }}
</a-avatar>
<span class="name">{{ actualName }}</span>
</div>
<template #overlay>
<a-menu :class="['avatar-menu']">
<a-menu-item @click="showUpdatePwdModal">
<a-menu-item @click="toAccount()">
<span>个人中心</span>
</a-menu-item>
<a-menu-item @click="toAccount(ACCOUNT_MENU.PASSWORD.menuId)">
<span>修改密码</span>
</a-menu-item>
<a-menu-item @click="onLogout">
@@ -36,6 +40,8 @@
import { localClear } from '/@/utils/local-util';
import { smartSentry } from '/@/lib/smart-sentry';
import HeaderResetPassword from './header-reset-password-modal/index.vue';
import { useRouter } from 'vue-router';
import { ACCOUNT_MENU } from '/@/views/system/account/account-menu.js';
// 头像背景颜色
const AVATAR_BACKGROUND_COLOR_ARRAY = ['#87d068', '#00B853', '#f56a00', '#1890ff'];
@@ -53,6 +59,15 @@
}
}
// ------------------------ 个人中心 ------------------------
const router = useRouter();
function toAccount(menuId) {
router.push({
path: '/account',
query: { menuId },
});
}
// ------------------------ 修改密码 ------------------------
const resetPasswordRef = ref();
@@ -63,7 +78,9 @@
// ------------------------ 以下是 头像和姓名 相关 ------------------------
const avatarName = ref('');
const avatar = computed(() => useUserStore().avatar);
const actualName = computed(() => useUserStore().actualName);
// 更新头像信息
function updateAvatar() {
if (useUserStore().actualName) {
@@ -98,7 +115,15 @@
cursor: pointer;
display: flex;
align-items: center;
.avatar-image {
width: 20px;
height: 20px;
object-fit: cover;
border-radius: 50%;
}
}
.header-trigger {
height: @header-user-height;
line-height: @header-user-height;

View File

@@ -1,77 +1,65 @@
<!--
* 消息通知
*
* @Author: 1024创新实验室-主任卓大
* @Date: 2022-09-06 20:17:18
* @Wechat: zhuda1024
* @Email: lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net Since 2012
*
* @Author: 1024创新实验室-主任卓大
* @Date: 2022-09-06 20:17:18
* @Wechat: zhuda1024
* @Email: lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net Since 2012
-->
<template>
<a-dropdown trigger="click" v-model:open="show">
<div @click="fetchMessage">
<a-badge count="12">
<a-button type="text" @click="queryMessage" style="padding: 4px 5px">
<a-badge :count="unreadMessageCount">
<div style="width: 26px; height: 26px">
<BellOutlined :style="{ fontSize: '18px' }" />
<BellOutlined :style="{ fontSize: '16px' }" />
</div>
</a-badge>
</div>
</a-button>
<template #overlay>
<div>
<a-card class="message-container" :bodyStyle="{ padding: 0 }">
<a-spin :spinning="loading">
<a-tabs class="dropdown-tabs" centered :tabBarStyle="{ textAlign: 'center' }" style="width: 300px">
<a-tab-pane tab="通知" key="1">
<a-tab-pane tab="未读消息" key="message">
<a-list class="tab-pane" size="small">
<a-list-item>
<a-list-item-meta description="7天前">
<a-list-item v-for="item in messageList" :key="item.messageId">
<a-list-item-meta>
<template #title>
<a href="#">今天洛阳天气39°c洛阳变各阳...</a>
<div class="title">
<a @click="gotoMessage">{{ item.title }}</a>
</div>
</template>
<template #avatar>
<a-avatar src="https://zhuoda.vip/images/blog/profile-128-128.jpg" />
</template>
</a-list-item-meta>
</a-list-item>
<a-list-item>
<a-list-item-meta description="7天前">
<template #title>
<a href="#">六月的雨 就是无情的你...</a>
</template>
<template #avatar>
<a-avatar src="https://zhuoda.vip/images/blog/profile-128-128.jpg" />
</template>
</a-list-item-meta>
</a-list-item>
<a-list-item>
<a-list-item-meta description="7天前">
<template #title>
<a href="#">今年河南天气炎热河南变可南...</a>
</template>
<template #avatar>
<a-avatar src="https://zhuoda.vip/images/blog/profile-128-128.jpg" />
<template #description>
<span> {{ timeago(item.createTime) }}</span>
</template>
</a-list-item-meta>
</a-list-item>
</a-list>
</a-tab-pane>
<a-tab-pane tab="消息" key="2">
<a-list class="tab-pane" />
</a-tab-pane>
<a-tab-pane tab="待办" key="3">
<a-tab-pane tab="待办工作" key="todo">
<a-list class="tab-pane" />
</a-tab-pane>
</a-tabs>
</a-spin>
</div>
</a-card>
</template>
</a-dropdown>
</template>
<script setup>
import { ref } from 'vue';
import { computed, ref } from 'vue';
import { BellOutlined } from '@ant-design/icons-vue';
import { useUserStore } from '/@/store/modules/system/user.js';
import { smartSentry } from '/@/lib/smart-sentry.js';
import { messageApi } from '/@/api/support/message-api.js';
import dayjs from 'dayjs';
import { theme } from 'ant-design-vue';
import { useRouter } from 'vue-router';
const { useToken } = theme;
const { token } = useToken();
defineExpose({ showMessage });
@@ -82,19 +70,97 @@
const loading = ref(false);
const show = ref(false);
const fetchMessage = () => {
if (loading.value) {
// ------------------------- 查询消息 -------------------------
// 未读消息
const unreadMessageCount = computed(() => {
return useUserStore().unreadMessageCount;
});
// 消息列表
const messageList = ref([]);
// 查询我的未读消息
async function queryMessage() {
try {
loading.value = true;
let responseModel = await messageApi.queryMessage({
pageNum: 1,
pageSize: 3,
readFlag: false,
});
messageList.value = responseModel.data.list;
} catch (e) {
smartSentry.captureError(e);
} finally {
loading.value = false;
return;
}
loading.value = true;
setTimeout(() => {
loading.value = false;
}, 700);
};
}
const router = useRouter();
function gotoMessage() {
show.value = false;
router.push({ path: '/account', query: { menuId: 'message' } });
}
// ------------------------- 时间计算 -------------------------
function timeago(dateStr) {
let dateTimeStamp = dayjs(dateStr).toDate().getTime();
let result = '';
let minute = 1000 * 60; //把分,时,天,周,半个月,一个月用毫秒表示
let hour = minute * 60;
let day = hour * 24;
let week = day * 7;
let month = day * 30;
let now = new Date().getTime(); //获取当前时间毫秒
let diffValue = now - dateTimeStamp; //时间差
if (diffValue < 0) {
return '刚刚';
}
let minC = diffValue / minute; //计算时间差的分,时,天,周,月
let hourC = diffValue / hour;
let dayC = diffValue / day;
let weekC = diffValue / week;
let monthC = diffValue / month;
if (monthC >= 1 && monthC <= 3) {
result = ' ' + parseInt(monthC) + '月前';
} else if (weekC >= 1 && weekC <= 3) {
result = ' ' + parseInt(weekC) + '周前';
} else if (dayC >= 1 && dayC <= 6) {
result = ' ' + parseInt(dayC) + '天前';
} else if (hourC >= 1 && hourC <= 23) {
result = ' ' + parseInt(hourC) + '小时前';
} else if (minC >= 1 && minC <= 59) {
result = ' ' + parseInt(minC) + '分钟前';
} else if (diffValue >= 0 && diffValue <= minute) {
result = '刚刚';
} else {
let datetime = new Date();
datetime.setTime(dateTimeStamp);
let year = datetime.getFullYear();
let month = datetime.getMonth() + 1 < 10 ? '0' + (datetime.getMonth() + 1) : datetime.getMonth() + 1;
let date = datetime.getDate() < 10 ? '0' + datetime.getDate() : datetime.getDate();
result = year + '-' + month + '-' + date;
}
return result;
}
</script>
<style lang="less">
<style lang="less" scoped>
@smart-page-tag-operate-width: 40px;
@color-primary: v-bind('token.colorPrimary');
.message-icon-div {
cursor: pointer;
height: 32px;
width: 42px;
padding-left: 10px;
}
.message-icon-div:hover {
background: @hover-bg-color !important;
}
.header-notice {
display: inline-block;
transition: all 0.3s;
@@ -108,6 +174,17 @@
}
}
.title {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
cursor: pointer;
}
.message-container {
border: #eeeeee solid 1px;
}
.dropdown-tabs {
background-color: @base-bg-color;
border-radius: 4px;

View File

@@ -11,8 +11,13 @@
<template>
<a-drawer :title="$t('setting.title')" placement="right" :open="visible" @close="close">
<a-form layout="horizontal" :label-col="{ span: 8 }">
<a-form-item label="语言/Language">
<a-select v-model:value="formState.language" @change="changeLanguage" style="width: 120px">
<a-select-option v-for="item in i18nList" :key="item.value" :value="item.value">{{ item.text }}</a-select-option>
</a-select>
</a-form-item>
<a-form-item :label="$t('setting.color')">
<div style="display: flex; align-items: center">
<div class="color-container">
<template v-for="(item, index) in themeColors">
<div v-if="index === formState.colorIndex" class="color">
<CheckSquareFilled :style="{ color: item.primaryColor, fontSize: '22px' }" />
@@ -38,6 +43,14 @@
<a-form-item :label="$t('setting.border.radius')">
<a-slider v-model:value="formState.borderRadius" :min="0" :max="6" @change="changeBorderRadius" />
</a-form-item>
<a-form-item :label="$t('setting.menu.width')" v-if="formState.layout === LAYOUT_ENUM.SIDE.value">
<a-input-number @change="changeSideMenuWidth" v-model:value="formState.sideMenuWidth" :min="1" />
像素px
</a-form-item>
<a-form-item :label="$t('setting.page.width')" v-if="formState.layout === LAYOUT_ENUM.TOP.value">
<a-input @change="changePageWidth" v-model:value="formState.pageWidth" />
像素px或者 百分比
</a-form-item>
<a-form-item :label="$t('setting.compact')">
<a-radio-group v-model:value="formState.compactFlag" button-style="solid" @change="changeCompactFlag">
<a-radio-button :value="false">默认</a-radio-button>
@@ -57,35 +70,39 @@
<a-radio-button value="light">Light</a-radio-button>
</a-radio-group>
</a-form-item>
<a-form-item :label="$t('setting.menu.width')" v-if="formState.layout === LAYOUT_ENUM.SIDE.value">
<a-input-number @change="changeSideMenuWidth" v-model:value="formState.sideMenuWidth" :min="1" />
像素px
</a-form-item>
<a-form-item :label="$t('setting.page.width')" v-if="formState.layout === LAYOUT_ENUM.TOP.value">
<a-input @change="changePageWidth" v-model:value="formState.pageWidth" />
像素px或者 百分比
</a-form-item>
<a-form-item label="语言/Language">
<a-select v-model:value="formState.language" @change="changeLanguage" style="width: 120px">
<a-select-option v-for="item in i18nList" :key="item.value" :value="item.value">{{ item.text }}</a-select-option>
</a-select>
</a-form-item>
<a-form-item :label="$t('setting.bread')">
<a-switch @change="changeBreadCrumbFlag" v-model:checked="formState.breadCrumbFlag" checked-children="显示" un-checked-children="隐藏" />
<a-form-item :label="$t('setting.pagetag.style')">
<a-radio-group v-model:value="formState.pageTagStyle" button-style="solid" @change="changePageTagStyle">
<a-radio-button value="default">默认</a-radio-button>
<a-radio-button value="antd">Ant Design</a-radio-button>
</a-radio-group>
</a-form-item>
<a-form-item :label="$t('setting.pagetag')">
<a-switch @change="changePageTagFlag" v-model:checked="formState.pageTagFlag" checked-children="显示" un-checked-children="隐藏" />
</a-form-item>
<a-form-item :label="$t('setting.bread')">
<a-switch @change="changeBreadCrumbFlag" v-model:checked="formState.breadCrumbFlag" checked-children="显示" un-checked-children="隐藏" />
</a-form-item>
<a-form-item :label="$t('setting.footer')">
<a-switch @change="changeFooterFlag" v-model:checked="formState.footerFlag" checked-children="显示" un-checked-children="隐藏" />
</a-form-item>
<a-form-item :label="$t('setting.helpdoc')">
<a-switch @change="changeHelpDocFlag" v-model:checked="formState.helpDocFlag" checked-children="显示" un-checked-children="隐藏" />
</a-form-item>
<a-form-item :label="$t('setting.watermark')">
<a-switch @change="changeWatermarkFlag" v-model:checked="formState.watermarkFlag" checked-children="显示" un-checked-children="隐藏" />
</a-form-item>
<a-form-item :label="$t('setting.helpdoc')">
<a-switch @change="changeHelpDocFlag" v-model:checked="formState.helpDocFlag" checked-children="显示" un-checked-children="隐藏" />
</a-form-item>
<a-form-item :label="$t('setting.helpdoc.expand')" v-if="formState.helpDocFlag">
<a-switch
@change="changeHelpDocExpandFlag"
v-model:checked="formState.helpDocExpandFlag"
checked-children="默认展开"
un-checked-children="默认不展开"
/>
</a-form-item>
<br />
<br />
</a-form>
<div class="footer">
<a-button style="margin-right: 8px" type="primary" @click="copy">复制配置信息</a-button>
<a-button type="block" danger @click="reset">恢复默认配置 </a-button>
@@ -176,12 +193,16 @@
borderRadius: appConfigStore.borderRadius,
// 标签页
pageTagFlag: appConfigStore.pageTagFlag,
// 标签页 样式
pageTagStyle: appConfigStore.pageTagStyle,
// 面包屑
breadCrumbFlag: appConfigStore.breadCrumbFlag,
// 页脚
footerFlag: appConfigStore.footerFlag,
// 帮助文档
helpDocFlag: appConfigStore.helpDocFlag,
// 帮助文档 默认展开
helpDocExpandFlag: appConfigStore.helpDocExpandFlag,
// 水印
watermarkFlag: appConfigStore.watermarkFlag,
};
@@ -250,6 +271,12 @@
});
}
function changePageTagStyle(e) {
appConfigStore.$patch({
pageTagStyle: e.target.value,
});
}
function changeFooterFlag(e) {
appConfigStore.$patch({
footerFlag: e,
@@ -262,6 +289,12 @@
});
}
function changeHelpDocExpandFlag(e) {
appConfigStore.$patch({
helpDocExpandFlag: e,
});
}
function changeWatermarkFlag(e) {
appConfigStore.$patch({
watermarkFlag: e,
@@ -281,9 +314,14 @@
z-index: 1;
}
.color-container {
display: flex;
justify-content: flex-start;
flex-wrap: wrap;
}
.color {
margin-left: 8px;
display: inline;
height: 26px;
width: 26px;
display: flex;

View File

@@ -1,11 +1,11 @@
<!--
* 头部一整行
*
* @Author: 1024创新实验室-主任卓大
* @Date: 2022-09-06 20:18:20
* @Wechat: zhuda1024
* @Email: lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net Since 2012
*
* @Author: 1024创新实验室-主任卓大
* @Date: 2022-09-06 20:18:20
* @Wechat: zhuda1024
* @Email: lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net Since 2012
-->
<template>
<a-space :size="10">
@@ -34,9 +34,9 @@
<HeaderAvatar />
</div>
<!---帮助文档--->
<div class="user-space-item" @click="showHelpDoc" v-if="!showHelpDocFlag">
<div class="user-space-item" @click="showHelpDoc" v-if="showHelpDocFlag">
<span>帮助文档</span>
<DoubleLeftOutlined v-if="!showHelpDocFlag" />
<DoubleLeftOutlined v-if="!helpDocExpandFlag" />
</div>
<HeaderSetting ref="headerSetting" />
@@ -72,6 +72,10 @@
return useAppConfigStore().helpDocFlag;
});
const helpDocExpandFlag = computed(() => {
return useAppConfigStore().helpDocExpandFlag;
});
//搜索
function search() {
window.open('https://1024lab.net');
@@ -100,7 +104,7 @@
.user-space-item:hover {
color: v-bind('token.colorPrimary');
background: @hover-bg-color;
background-color: @hover-bg-color !important;
}
.setting {