mirror of
https://github.com/1024-lab/smart-admin.git
synced 2026-06-27 08:04:24 +00:00
v3.9.0【优化】typescript版本;【优化】App端消息;【优化】弹出层z-index;
This commit is contained in:
+307
@@ -0,0 +1,307 @@
|
||||
<!--
|
||||
* 通知 表单
|
||||
*
|
||||
* @Author: 1024创新实验室-主任:卓大
|
||||
* @Date: 2022-08-21 19:52:43
|
||||
* @Wechat: zhuda1024
|
||||
* @Email: lab1024@163.com
|
||||
* @Copyright 1024创新实验室 ( https://1024lab.net ),Since 2012
|
||||
-->
|
||||
<template>
|
||||
<a-drawer
|
||||
:title="formData.noticeId ? '编辑' : '新建'"
|
||||
:open="visibleFlag"
|
||||
:width="1000"
|
||||
:footerStyle="{ textAlign: 'right' }"
|
||||
@close="onClose"
|
||||
:destroyOnClose="true"
|
||||
>
|
||||
<a-form ref="formRef" :model="formData" :rules="formRules" :label-col="{ span: 3 }" :wrapper-col="{ span: 20 }">
|
||||
<a-form-item label="公告标题" name="title">
|
||||
<a-input v-model:value="formData.title" placeholder="请输入公告标题" />
|
||||
</a-form-item>
|
||||
<a-form-item label="分类" name="noticeTypeId">
|
||||
<a-select v-model:value="formData.noticeTypeId" style="width: 100%" :showSearch="true" :allowClear="true">
|
||||
<a-select-option v-for="item in noticeTypeList" :key="item.noticeTypeId" :value="item.noticeTypeId">
|
||||
{{ item.noticeTypeName }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label="文号">
|
||||
<a-input v-model:value="formData.documentNumber" placeholder="文号,如:1024创新实验室发〔2022〕字第36号" />
|
||||
</a-form-item>
|
||||
<a-form-item label="作者" name="author">
|
||||
<a-input v-model:value="formData.author" placeholder="请输入作者" />
|
||||
</a-form-item>
|
||||
<a-form-item label="来源" name="source">
|
||||
<a-input v-model:value="formData.source" placeholder="请输入来源" />
|
||||
</a-form-item>
|
||||
<a-form-item label="可见范围" name="allVisibleFlag">
|
||||
<a-select v-model:value="formData.allVisibleFlag" placeholder="请选择可见范围">
|
||||
<a-select-option :value="1">全部可见</a-select-option>
|
||||
<a-select-option :value="0">部分可见</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item v-show="!formData.allVisibleFlag" label="可见员工/部门">
|
||||
<a-button type="primary" @click="showNoticeVisibleModal">选择</a-button>
|
||||
<div class="visible-list">
|
||||
<div class="visible-item" v-for="(item, index) in formData.visibleRangeList" :key="item.dataId">
|
||||
<a-tag>
|
||||
<span>{{ item.dataName }}</span>
|
||||
<close-outlined @click="removeVisibleItem(index)" />
|
||||
</a-tag>
|
||||
</div>
|
||||
</div>
|
||||
</a-form-item>
|
||||
<a-form-item label="定时发布">
|
||||
<a-switch
|
||||
v-model:checked="formData.scheduledPublishFlag"
|
||||
checked-children="开"
|
||||
un-checked-children="关"
|
||||
@change="changesSheduledPublishFlag"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item v-show="formData.scheduledPublishFlag" label="发布时间">
|
||||
<a-date-picker
|
||||
v-model:value="releaseTime"
|
||||
:format="timeFormat"
|
||||
showTime
|
||||
:allowClear="false"
|
||||
placeholder="请选择发布时间"
|
||||
style="width: 200px"
|
||||
@change="changeTime"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="公告内容" name="contentHtml">
|
||||
<SmartWangeditor ref="contentRef" :modelValue="formData.contentHtml" :height="300" />
|
||||
</a-form-item>
|
||||
<a-form-item label="附件">
|
||||
<Upload
|
||||
:defaultFileList="defaultFileList"
|
||||
:maxUploadSize="10"
|
||||
:folder="FILE_FOLDER_TYPE_ENUM.NOTICE.value"
|
||||
buttonText="上传附件"
|
||||
listType="text"
|
||||
extraMsg="最多上传10个附件"
|
||||
@change="changeAttachment"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
|
||||
<template #footer>
|
||||
<a-space>
|
||||
<a-button @click="onClose">取消</a-button>
|
||||
<a-button type="primary" @click="onSubmit">保存</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
</a-drawer>
|
||||
|
||||
<!-- 选择可见范围弹窗 -->
|
||||
<NoticeFormVisibleModal ref="noticeFormVisibleModal" @selectedFinish="finishCanSelectedVisibleRange" />
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive, ref, onMounted, watch, computed, nextTick } from 'vue';
|
||||
import { message, Modal } from 'ant-design-vue';
|
||||
import _ from 'lodash';
|
||||
import dayjs, { Dayjs } from 'dayjs';
|
||||
import { SmartLoading } from '/@/components/framework/smart-loading';
|
||||
import { FILE_FOLDER_TYPE_ENUM } from '/@/constants/support/file-const';
|
||||
import { noticeApi } from '/@/api/business/oa/notice-api';
|
||||
import SmartWangeditor from '/@/components/framework/wangeditor/index.vue';
|
||||
import Upload from '/@/components/support/file-upload/index.vue';
|
||||
import NoticeFormVisibleModal from './notice-form-visible-modal.vue';
|
||||
import { smartSentry } from '/@/lib/smart-sentry';
|
||||
|
||||
const emits = defineEmits(['reloadList']);
|
||||
|
||||
// ------------------ 显示,关闭 ------------------
|
||||
// 显示
|
||||
const visibleFlag = ref(false);
|
||||
function showModal(noticeId) {
|
||||
Object.assign(formData, defaultFormData);
|
||||
releaseTime.value = null;
|
||||
defaultFileList.value = [];
|
||||
queryNoticeTypeList();
|
||||
if (noticeId) {
|
||||
getNoticeUpdate(noticeId);
|
||||
}
|
||||
|
||||
visibleFlag.value = true;
|
||||
nextTick(() => {
|
||||
formRef.value.clearValidate();
|
||||
});
|
||||
}
|
||||
|
||||
// 关闭
|
||||
function onClose() {
|
||||
visibleFlag.value = false;
|
||||
}
|
||||
|
||||
// ------------------ 表单 ------------------
|
||||
|
||||
const formRef = ref();
|
||||
const contentRef = ref();
|
||||
const noticeFormVisibleModal = ref();
|
||||
|
||||
const defaultFormData = {
|
||||
noticeId: undefined,
|
||||
noticeTypeId: undefined,
|
||||
title: undefined, // 标题
|
||||
categoryId: undefined, // 分类
|
||||
source: undefined, // 来源
|
||||
documentNumber: undefined, // 文号
|
||||
author: undefined, // 作者
|
||||
allVisibleFlag: 1, // 是否全部可见
|
||||
visibleRangeList: [], // 可见范围
|
||||
scheduledPublishFlag: false, // 是否定时发布
|
||||
publishTime: undefined, // 发布时间
|
||||
attachment: [], // 附件
|
||||
contentHtml: '', // html内容
|
||||
contentText: '', // 纯文本内容
|
||||
};
|
||||
|
||||
const formData = reactive({ ...defaultFormData });
|
||||
|
||||
const formRules = {
|
||||
title: [{ required: true, message: '请输入' }],
|
||||
noticeTypeId: [{ required: true, message: '请选择分类' }],
|
||||
allVisibleFlag: [{ required: true, message: '请选择' }],
|
||||
source: [{ required: true, message: '请输入来源' }],
|
||||
author: [{ required: true, message: '请输入作者' }],
|
||||
contentHtml: [{ required: true, message: '请输入内容' }],
|
||||
};
|
||||
|
||||
// 查询详情
|
||||
async function getNoticeUpdate(noticeId) {
|
||||
try {
|
||||
SmartLoading.show();
|
||||
const result = await noticeApi.getUpdateNoticeInfo(noticeId);
|
||||
const attachment = result.data.attachment;
|
||||
if (!_.isEmpty(attachment)) {
|
||||
defaultFileList.value = attachment;
|
||||
} else {
|
||||
defaultFileList.value = [];
|
||||
}
|
||||
Object.assign(formData, result.data);
|
||||
formData.allVisibleFlag = formData.allVisibleFlag ? 1 : 0;
|
||||
|
||||
releaseTime.value = dayjs(result.data.publishTime);
|
||||
visibleFlag.value = true;
|
||||
} catch (err) {
|
||||
smartSentry.captureError(err);
|
||||
} finally {
|
||||
SmartLoading.hide();
|
||||
}
|
||||
}
|
||||
|
||||
// 点击确定,验证表单
|
||||
async function onSubmit() {
|
||||
try {
|
||||
formData.contentHtml = contentRef.value.getHtml();
|
||||
formData.contentText = contentRef.value.getText();
|
||||
await formRef.value.validateFields();
|
||||
save();
|
||||
} catch (err) {
|
||||
message.error('参数验证错误,请仔细填写表单数据!');
|
||||
}
|
||||
}
|
||||
|
||||
// 新建、编辑API
|
||||
async function save() {
|
||||
try {
|
||||
SmartLoading.show();
|
||||
if (formData.allVisibleFlag) {
|
||||
formData.visibleRangeList = [];
|
||||
}
|
||||
if (!formData.publishTime) {
|
||||
formData.publishTime = dayjs().format(timeFormat);
|
||||
}
|
||||
if (formData.noticeId) {
|
||||
await noticeApi.updateNotice(formData);
|
||||
} else {
|
||||
await noticeApi.addNotice(formData);
|
||||
}
|
||||
message.success('保存成功');
|
||||
emits('reloadList');
|
||||
onClose();
|
||||
} catch (err) {
|
||||
smartSentry.captureError(err);
|
||||
} finally {
|
||||
SmartLoading.hide();
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------ 通知分类 ------------------
|
||||
|
||||
// 查询分类列表
|
||||
const noticeTypeList = ref([]);
|
||||
async function queryNoticeTypeList() {
|
||||
try {
|
||||
const result = await noticeApi.getAllNoticeTypeList();
|
||||
noticeTypeList.value = result.data;
|
||||
if (noticeTypeList.value.length > 0 && !formData.noticeId) {
|
||||
formData.noticeTypeId = noticeTypeList.value[0].noticeTypeId;
|
||||
}
|
||||
} catch (err) {
|
||||
smartSentry.captureError(err);
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------- 可见员工/部门 ----------------------------
|
||||
// 点击显示选择可见员工/部门
|
||||
function showNoticeVisibleModal() {
|
||||
const visibleRangeList = formData.visibleRangeList || [];
|
||||
noticeFormVisibleModal.value.showModal(visibleRangeList);
|
||||
}
|
||||
|
||||
// 选择完成回调
|
||||
function finishCanSelectedVisibleRange(selectedList) {
|
||||
formData.visibleRangeList = selectedList;
|
||||
}
|
||||
|
||||
// 移除某个员工/部门
|
||||
function removeVisibleItem(index) {
|
||||
Modal.confirm({
|
||||
title: '提示',
|
||||
content: '确定移除吗?',
|
||||
onOk() {
|
||||
formData.visibleRangeList.splice(index, 1);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// ----------------------- 发布时间 ----------------------------
|
||||
const timeFormat = 'YYYY-MM-DD HH:mm:ss';
|
||||
const releaseTime = ref(null);
|
||||
function changeTime(date, dateString) {
|
||||
formData.publishTime = dateString;
|
||||
}
|
||||
function changesSheduledPublishFlag(checked) {
|
||||
releaseTime.value = checked ? dayjs() : null;
|
||||
formData.publishTime = checked ? dayjs().format(timeFormat) : null;
|
||||
}
|
||||
|
||||
// ----------------------- 上传附件 ----------------------------
|
||||
// 已上传的附件列表
|
||||
const defaultFileList = ref([]);
|
||||
function changeAttachment(fileList) {
|
||||
defaultFileList.value = fileList;
|
||||
formData.attachment = _.isEmpty(fileList) ? [] : fileList;
|
||||
}
|
||||
|
||||
// ----------------------- 以下是暴露的方法内容 ------------------------
|
||||
defineExpose({
|
||||
showModal,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.visible-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
.visible-item {
|
||||
padding-top: 8px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
<!--
|
||||
* 通知 可见范围
|
||||
*
|
||||
* @Author: 1024创新实验室-主任:卓大
|
||||
* @Date: 2022-08-21 19:52:43
|
||||
* @Wechat: zhuda1024
|
||||
* @Email: lab1024@163.com
|
||||
* @Copyright 1024创新实验室 ( https://1024lab.net ),Since 2012
|
||||
-->
|
||||
<template>
|
||||
<a-modal title="选择部门" v-model:open="visibleFlag" :maskClosable="false" :width="768" @ok="onSubmit" @cancel="onClose">
|
||||
<a-tabs v-model:activeKey="activeKey">
|
||||
<a-tab-pane :key="1" tab="选择员工">
|
||||
<NoticeFormVisibleTransferEmployee :employeeList="employeeList" @onChange="onChangeEmployee" />
|
||||
</a-tab-pane>
|
||||
<a-tab-pane :key="2" tab="选择部门">
|
||||
<NoticeFormVisibleTransferDepartment :departmentList="departmentList" @onChange="onChangeDepartment" />
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive, ref, onMounted, watch } from 'vue';
|
||||
import { NOTICE_VISIBLE_RANGE_DATA_TYPE_ENUM } from '/@/constants/business/oa/notice-const';
|
||||
import NoticeFormVisibleTransferDepartment from './notice-form-visible-transfer-department.vue';
|
||||
import NoticeFormVisibleTransferEmployee from './notice-form-visible-transfer-employee.vue';
|
||||
|
||||
const emits = defineEmits('selectedFinish');
|
||||
const visibleFlag = ref(false);
|
||||
function onClose() {
|
||||
visibleFlag.value = false;
|
||||
}
|
||||
|
||||
const activeKey = ref(1);
|
||||
// 已选的员工列表
|
||||
const employeeList = ref([]);
|
||||
// 已选的部门列表
|
||||
const departmentList = ref([]);
|
||||
|
||||
// 显示弹窗
|
||||
function showModal(visibleRangeList = []) {
|
||||
employeeList.value = visibleRangeList.filter((item) => item.dataType === NOTICE_VISIBLE_RANGE_DATA_TYPE_ENUM.EMPLOYEE.value);
|
||||
departmentList.value = visibleRangeList.filter((item) => item.dataType === NOTICE_VISIBLE_RANGE_DATA_TYPE_ENUM.DEPARTMENT.value);
|
||||
activeKey.value = 1;
|
||||
visibleFlag.value = true;
|
||||
}
|
||||
|
||||
function onSubmit() {
|
||||
const selectedList = [...employeeList.value, ...departmentList.value];
|
||||
emits('selectedFinish', selectedList);
|
||||
onClose();
|
||||
}
|
||||
|
||||
// 选择员工改变
|
||||
function onChangeEmployee({ selectedList }) {
|
||||
employeeList.value = selectedList;
|
||||
}
|
||||
|
||||
// 选择部门改变
|
||||
function onChangeDepartment({ selectedList }) {
|
||||
departmentList.value = selectedList;
|
||||
}
|
||||
|
||||
// ----------------------- 以下是暴露的方法内容 ------------------------
|
||||
defineExpose({
|
||||
showModal,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped></style>
|
||||
+204
@@ -0,0 +1,204 @@
|
||||
<!--
|
||||
* 通知 可见范围 选择部门
|
||||
*
|
||||
* @Author: 1024创新实验室-主任:卓大
|
||||
* @Date: 2022-08-21 19:52:43
|
||||
* @Wechat: zhuda1024
|
||||
* @Email: lab1024@163.com
|
||||
* @Copyright 1024创新实验室 ( https://1024lab.net ),Since 2012
|
||||
-->
|
||||
<template>
|
||||
<div class="wrapper">
|
||||
<div class="sider-fl">
|
||||
<a-tree :tree-data="treeData" :fieldNames="{ title: 'name', key: 'departmentId' }" :selectable="false" v-model:expandedKeys="expandedKeys">
|
||||
<template #switcherIcon="{ switcherCls }">
|
||||
<caret-down-outlined :class="switcherCls" />
|
||||
</template>
|
||||
<template #title="{ name, departmentId }">
|
||||
<div class="list-item" :class="{ active: checkExists(departmentId) }">
|
||||
<div class="list-item-title">{{ name }}</div>
|
||||
<check-circle-filled class="check-icon-style" @click="onSelectAdd(name, departmentId)" />
|
||||
</div>
|
||||
</template>
|
||||
</a-tree>
|
||||
</div>
|
||||
<div class="sider-fr">
|
||||
<div class="selected-list">
|
||||
<div class="list-item" v-for="(item, index) in selectedList" :key="item.dataId">
|
||||
<div class="list-item-title">{{ item.dataName }}</div>
|
||||
<close-circle-two-tone @click="onRemove(index)" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive, ref, onMounted, watch, computed, nextTick } from 'vue';
|
||||
import _ from 'lodash';
|
||||
import { NOTICE_VISIBLE_RANGE_DATA_TYPE_ENUM } from '/@/constants/business/oa/notice-const';
|
||||
import { departmentApi } from '/@/api/system/department-api';
|
||||
import { smartSentry } from '/@/lib/smart-sentry';
|
||||
|
||||
const props = defineProps({
|
||||
// 已选择的部门数据列表
|
||||
departmentList: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
});
|
||||
|
||||
const emits = defineEmits(['onChange']);
|
||||
|
||||
const treeData = ref([]);
|
||||
async function queryDepartmentTree() {
|
||||
try {
|
||||
const result = await departmentApi.queryDepartmentTree();
|
||||
if (!_.isEmpty(result.data)) {
|
||||
treeData.value = result.data;
|
||||
setExpanded();
|
||||
}
|
||||
} catch (err) {
|
||||
smartSentry.captureError(err);
|
||||
}
|
||||
}
|
||||
|
||||
// 设置默认展开的节点
|
||||
const expandedKeys = ref([]);
|
||||
function setExpanded() {
|
||||
expandedKeys.value = [treeData.value[0].departmentId];
|
||||
}
|
||||
|
||||
// 选择的部门列表数据
|
||||
const selectedList = ref([]);
|
||||
|
||||
// 选择的部门列表Ids
|
||||
const selectedIds = computed(() => {
|
||||
return selectedList.value.map((item) => item.dataId);
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.departmentList,
|
||||
(newVal) => {
|
||||
selectedList.value = newVal;
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
// 检查是否已选
|
||||
function checkExists(dataId) {
|
||||
return selectedIds.value.includes(dataId);
|
||||
}
|
||||
|
||||
// 点击左边添加
|
||||
function onSelectAdd(name, departmentId) {
|
||||
if (checkExists(departmentId)) {
|
||||
return;
|
||||
}
|
||||
selectedList.value.push({
|
||||
dataName: name,
|
||||
dataId: departmentId,
|
||||
dataType: NOTICE_VISIBLE_RANGE_DATA_TYPE_ENUM.DEPARTMENT.value,
|
||||
});
|
||||
onChangeEmit();
|
||||
}
|
||||
|
||||
// 点击右边移除
|
||||
function onRemove(index) {
|
||||
selectedList.value.splice(index, 1);
|
||||
onChangeEmit();
|
||||
}
|
||||
|
||||
function onChangeEmit() {
|
||||
emits('onChange', { selectedList: selectedList.value, selectedIds: selectedIds.value });
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
queryDepartmentTree();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
:deep(.ant-tree-list-holder-inner) {
|
||||
display: block !important;
|
||||
.ant-tree-treenode {
|
||||
align-items: center;
|
||||
padding-bottom: 0;
|
||||
&:hover {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
.ant-tree-switcher {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
.ant-tree-switcher-icon {
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
.ant-tree-node-content-wrapper {
|
||||
display: block;
|
||||
flex: 1;
|
||||
&:hover {
|
||||
cursor: auto;
|
||||
}
|
||||
.ant-tree-title {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
display: flex;
|
||||
.sider-fl,
|
||||
.sider-fr {
|
||||
flex: 1;
|
||||
height: 500px;
|
||||
border: 1px solid #d9d9d9;
|
||||
overflow: hidden;
|
||||
overflow-y: auto;
|
||||
&::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
&::-webkit-scrollbar-track-piece {
|
||||
background-color: #ededed;
|
||||
}
|
||||
&::-webkit-scrollbar-thumb {
|
||||
height: 50px;
|
||||
background-color: #a1a1a1;
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
.sider-fr {
|
||||
margin-left: 15px;
|
||||
.list-item {
|
||||
padding-left: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.list-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 14px 0 0;
|
||||
height: 32px;
|
||||
&:hover {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
&.active {
|
||||
.check-icon-style {
|
||||
cursor: auto;
|
||||
color: @primary-color;
|
||||
}
|
||||
}
|
||||
.list-item-title {
|
||||
flex: 1;
|
||||
margin-right: 10px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.check-icon-style {
|
||||
color: #d9d9d9;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
+252
@@ -0,0 +1,252 @@
|
||||
<!--
|
||||
* 通知 可见范围 选择员工
|
||||
*
|
||||
* @Author: 1024创新实验室-主任:卓大
|
||||
* @Date: 2022-08-21 19:52:43
|
||||
* @Wechat: zhuda1024
|
||||
* @Email: lab1024@163.com
|
||||
* @Copyright 1024创新实验室 ( https://1024lab.net ),Since 2012
|
||||
-->
|
||||
<template>
|
||||
<div class="wrapper">
|
||||
<div class="sider-left">
|
||||
<a-tree :tree-data="treeData" :fieldNames="{ title: 'name' }" :selectable="false" v-model:expandedKeys="expandedKeys">
|
||||
<template #switcherIcon="{ switcherCls }">
|
||||
<caret-down-outlined :class="switcherCls" />
|
||||
</template>
|
||||
<template #title="{ name, id, dataType }">
|
||||
<div class="list-item" :class="{ active: checkExists(id) }">
|
||||
<div class="list-item-title">
|
||||
<user-outlined v-if="dataType === NOTICE_VISIBLE_RANGE_DATA_TYPE_ENUM.EMPLOYEE.value" />
|
||||
{{ name }}
|
||||
</div>
|
||||
<check-circle-filled
|
||||
v-if="dataType === NOTICE_VISIBLE_RANGE_DATA_TYPE_ENUM.EMPLOYEE.value"
|
||||
class="check-icon-style"
|
||||
@click="onSelectAdd(name, id, dataType)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</a-tree>
|
||||
</div>
|
||||
<div class="sider-right">
|
||||
<div class="selected-list">
|
||||
<div class="list-item" v-for="(item, index) in selectedList" :key="item.id">
|
||||
<div class="list-item-title">{{ item.dataName }}</div>
|
||||
<close-circle-two-tone @click="onRemove(index)" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, nextTick, onMounted, ref, watch } from 'vue';
|
||||
import _ from 'lodash';
|
||||
import { NOTICE_VISIBLE_RANGE_DATA_TYPE_ENUM } from '/@/constants/business/oa/notice-const';
|
||||
import { departmentApi } from '/@/api/system/department-api';
|
||||
import { employeeApi } from '/@/api/system/employee-api';
|
||||
import { smartSentry } from '/@/lib/smart-sentry';
|
||||
|
||||
const props = defineProps({
|
||||
// 已选择的员工数据列表
|
||||
employeeList: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
});
|
||||
|
||||
const emits = defineEmits(['onChange']);
|
||||
|
||||
const treeData = ref([]);
|
||||
|
||||
// 查询部门树形
|
||||
async function queryDepartmentTree() {
|
||||
try {
|
||||
const departmentResult = await departmentApi.queryDepartmentTree();
|
||||
const employeeResult = await employeeApi.queryAll();
|
||||
const departmentTree = departmentResult.data;
|
||||
buildDepartmentEmployeeTree(departmentTree, employeeResult.data);
|
||||
|
||||
if (!_.isEmpty(departmentTree)) {
|
||||
treeData.value = departmentTree;
|
||||
console.log(treeData.value);
|
||||
nextTick(() => {
|
||||
setExpanded();
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
smartSentry.captureError(err);
|
||||
}
|
||||
}
|
||||
|
||||
// 递归构建部门员工树
|
||||
function buildDepartmentEmployeeTree(departmentTree, employeeList) {
|
||||
for (const department of departmentTree) {
|
||||
if (department.dataType && department.dataType === NOTICE_VISIBLE_RANGE_DATA_TYPE_ENUM.EMPLOYEE.value) {
|
||||
continue;
|
||||
}
|
||||
|
||||
department.id = department.departmentId;
|
||||
department.key = 'department_' + department.departmentId;
|
||||
department.dataType = NOTICE_VISIBLE_RANGE_DATA_TYPE_ENUM.DEPARTMENT.value;
|
||||
let employeeChildren = employeeList
|
||||
.filter((e) => e.departmentId === department.departmentId)
|
||||
.map((e) =>
|
||||
Object.assign(
|
||||
{},
|
||||
{
|
||||
id: e.employeeId,
|
||||
key: 'employee_' + e.employeeId,
|
||||
name: e.actualName,
|
||||
dataType: NOTICE_VISIBLE_RANGE_DATA_TYPE_ENUM.EMPLOYEE.value,
|
||||
}
|
||||
)
|
||||
);
|
||||
if (!department.children) {
|
||||
department.children = [];
|
||||
}
|
||||
department.children.push(...employeeChildren);
|
||||
buildDepartmentEmployeeTree(department.children, employeeList);
|
||||
}
|
||||
}
|
||||
|
||||
// 设置默认展开的节点
|
||||
const expandedKeys = ref([]);
|
||||
function setExpanded() {
|
||||
expandedKeys.value = [treeData.value[0].key];
|
||||
}
|
||||
|
||||
// 选择的员工列表数据
|
||||
const selectedList = ref([]);
|
||||
|
||||
// 选择的员工列表Ids
|
||||
const selectedIds = computed(() => {
|
||||
return selectedList.value.map((item) => item.dataId);
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.employeeList,
|
||||
(newVal) => {
|
||||
selectedList.value = newVal;
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
// 检查是否已选
|
||||
function checkExists(id) {
|
||||
return selectedIds.value.includes(id);
|
||||
}
|
||||
|
||||
// 点击左边添加
|
||||
function onSelectAdd(name, id, dataType) {
|
||||
if (checkExists(id)) {
|
||||
return;
|
||||
}
|
||||
selectedList.value.push({
|
||||
dataName: name,
|
||||
dataId: id,
|
||||
dataType: NOTICE_VISIBLE_RANGE_DATA_TYPE_ENUM.EMPLOYEE.value,
|
||||
});
|
||||
onChangeEmit();
|
||||
}
|
||||
|
||||
// 点击右边移除
|
||||
function onRemove(index) {
|
||||
selectedList.value.splice(index, 1);
|
||||
onChangeEmit();
|
||||
}
|
||||
|
||||
function onChangeEmit() {
|
||||
emits('onChange', { selectedList: selectedList.value, selectedIds: selectedIds.value });
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
queryDepartmentTree();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
:deep(.ant-tree-list-holder-inner) {
|
||||
display: block !important;
|
||||
.ant-tree-treenode {
|
||||
align-items: center;
|
||||
padding-bottom: 0;
|
||||
&:hover {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
.ant-tree-switcher {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
.ant-tree-switcher-icon {
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
.ant-tree-node-content-wrapper {
|
||||
display: block;
|
||||
flex: 1;
|
||||
&:hover {
|
||||
cursor: auto;
|
||||
}
|
||||
.ant-tree-title {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
display: flex;
|
||||
.sider-left,
|
||||
.sider-right {
|
||||
flex: 1;
|
||||
height: 500px;
|
||||
border: 1px solid #d9d9d9;
|
||||
overflow: hidden;
|
||||
overflow-y: auto;
|
||||
&::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
&::-webkit-scrollbar-track-piece {
|
||||
background-color: #ededed;
|
||||
}
|
||||
&::-webkit-scrollbar-thumb {
|
||||
height: 50px;
|
||||
background-color: #a1a1a1;
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
.sider-right {
|
||||
margin-left: 15px;
|
||||
.list-item {
|
||||
padding-left: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.list-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 14px 0 0;
|
||||
height: 32px;
|
||||
&:hover {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
&.active {
|
||||
.check-icon-style {
|
||||
cursor: auto;
|
||||
color: @primary-color;
|
||||
}
|
||||
}
|
||||
.list-item-title {
|
||||
flex: 1;
|
||||
margin-right: 10px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.check-icon-style {
|
||||
color: #d9d9d9;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
+161
@@ -0,0 +1,161 @@
|
||||
<!--
|
||||
* 通知 查看记录
|
||||
*
|
||||
* @Author: 1024创新实验室-主任:卓大
|
||||
* @Date: 2022-08-21 19:52:43
|
||||
* @Wechat: zhuda1024
|
||||
* @Email: lab1024@163.com
|
||||
* @Copyright 1024创新实验室 ( https://1024lab.net ),Since 2012
|
||||
-->
|
||||
<template>
|
||||
<div>
|
||||
<a-form class="smart-query-form">
|
||||
<a-row class="smart-query-form-row">
|
||||
<a-form-item label="部门" class="smart-query-form-item" style="width: 280px; margin-right: 20px">
|
||||
<DepartmentTreeSelect v-model:value="queryForm.departmentId" width="100%" />
|
||||
</a-form-item>
|
||||
<a-form-item label="关键字" class="smart-query-form-item" style="width: 280px">
|
||||
<a-input v-model:value="queryForm.keywords" placeholder="姓名/IP/设备" />
|
||||
</a-form-item>
|
||||
<a-form-item class="smart-query-form-item smart-margin-left10">
|
||||
<a-button-group>
|
||||
<a-button type="primary" @click="onSearch">
|
||||
<template #icon>
|
||||
<SearchOutlined />
|
||||
</template>
|
||||
查询
|
||||
</a-button>
|
||||
<a-button @click="resetQuery">
|
||||
<template #icon>
|
||||
<ReloadOutlined />
|
||||
</template>
|
||||
重置
|
||||
</a-button>
|
||||
</a-button-group>
|
||||
</a-form-item>
|
||||
</a-row>
|
||||
</a-form>
|
||||
<a-table rowKey="employeeId" :columns="tableColumns" :dataSource="tableData" :pagination="false" :loading="tableLoading" size="small" bordered>
|
||||
<template #bodyCell="{ column, record, text }">
|
||||
<template v-if="column.dataIndex === 'employeeName'"> {{ text }}({{ record.departmentName }}) </template>
|
||||
<template v-if="column.dataIndex === 'firstIp'"> {{ text }} ({{ record.firstDevice }}) </template>
|
||||
<template v-if="column.dataIndex === 'lastIp'"> {{ text }} ({{ record.lastDevice }}) </template>
|
||||
</template>
|
||||
</a-table>
|
||||
<div class="smart-query-table-page">
|
||||
<a-pagination
|
||||
showSizeChanger
|
||||
showQuickJumper
|
||||
show-less-items
|
||||
:pageSizeOptions="PAGE_SIZE_OPTIONS"
|
||||
:defaultPageSize="queryForm.pageSize"
|
||||
v-model:current="queryForm.pageNum"
|
||||
v-model:pageSize="queryForm.pageSize"
|
||||
:total="total"
|
||||
@change="queryViewRecord"
|
||||
@showSizeChange="queryViewRecord"
|
||||
:show-total="(total) => `共${total}条`"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import { reactive, ref } from 'vue';
|
||||
import { noticeApi } from '/@/api/business/oa/notice-api';
|
||||
import { PAGE_SIZE, PAGE_SIZE_OPTIONS } from '/@/constants/common-const';
|
||||
import DepartmentTreeSelect from '/@/components/system/department-tree-select/index.vue';
|
||||
import uaparser from 'ua-parser-js';
|
||||
|
||||
const props = defineProps({
|
||||
noticeId: {
|
||||
type: [Number, String],
|
||||
},
|
||||
});
|
||||
|
||||
defineExpose({
|
||||
onSearch,
|
||||
});
|
||||
|
||||
const tableColumns = [
|
||||
{
|
||||
title: '姓名',
|
||||
dataIndex: 'employeeName',
|
||||
},
|
||||
{
|
||||
title: '查看次数',
|
||||
dataIndex: 'pageViewCount',
|
||||
},
|
||||
{
|
||||
title: '首次查看设备',
|
||||
dataIndex: 'firstIp',
|
||||
},
|
||||
{
|
||||
title: '首次查看时间',
|
||||
dataIndex: 'createTime',
|
||||
},
|
||||
{
|
||||
title: '最后一次查看设备',
|
||||
dataIndex: 'lastIp',
|
||||
},
|
||||
{
|
||||
title: '最后一次查看时间',
|
||||
dataIndex: 'updateTime',
|
||||
with: 80,
|
||||
},
|
||||
];
|
||||
|
||||
const tableData = ref([]);
|
||||
const total = ref(0);
|
||||
const tableLoading = ref(false);
|
||||
|
||||
const defaultQueryForm = {
|
||||
noticeId: props.noticeId,
|
||||
departmentId: null,
|
||||
keywords: '',
|
||||
pageNum: 1,
|
||||
pageSize: PAGE_SIZE,
|
||||
};
|
||||
|
||||
const queryForm = reactive({ ...defaultQueryForm });
|
||||
|
||||
function buildDeviceInfo(userAgent) {
|
||||
if (!userAgent) {
|
||||
return '';
|
||||
}
|
||||
|
||||
let ua = uaparser(userAgent);
|
||||
let browser = ua.browser.name;
|
||||
let os = ua.os.name;
|
||||
return browser + '/' + os + '/' + (ua.device.vendor ? ua.device.vendor + ua.device.model : '');
|
||||
}
|
||||
|
||||
async function queryViewRecord() {
|
||||
try {
|
||||
tableLoading.value = true;
|
||||
const result = await noticeApi.queryViewRecord(queryForm);
|
||||
|
||||
for (const e of result.data.list) {
|
||||
e.firstDevice = buildDeviceInfo(e.firstUserAgent);
|
||||
e.lastDevice = buildDeviceInfo(e.lastUserAgent);
|
||||
}
|
||||
|
||||
tableData.value = result.data.list;
|
||||
total.value = result.data.total;
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
} finally {
|
||||
tableLoading.value = false;
|
||||
}
|
||||
}
|
||||
// 点击查询
|
||||
function onSearch() {
|
||||
queryForm.pageNum = 1;
|
||||
queryViewRecord();
|
||||
}
|
||||
|
||||
// 点击重置
|
||||
function resetQuery() {
|
||||
Object.assign(queryForm, defaultQueryForm);
|
||||
queryViewRecord();
|
||||
}
|
||||
</script>
|
||||
Reference in New Issue
Block a user