Browse Source

承诺书

master
“wangzihua” 3 months ago
parent
commit
423bfcd07a
  1. 7
      src/api/business/letter/letter-api.js
  2. 5
      src/api/business/oa/notice-api.js
  3. 6
      src/components/support/file-upload/index.vue
  4. 161
      src/views/business/erp/letter/letter-list.vue
  5. 37
      src/views/system/home/components/agreement-modal.vue
  6. 189
      src/views/system/home/components/agreement.vue

7
src/api/business/letter/letter-api.js

@ -30,6 +30,13 @@ export const letterApi = {
return postRequest('/letter/update', param);
},
/**
* 查看承诺书详情 @author wzh
*/
isLetter: (letterId) => {
return getRequest(`/letter/detail/${letterId}`);
},
};

5
src/api/business/oa/notice-api.js

@ -67,6 +67,11 @@ export const noticeApi = {
return postRequest('/oa/notice/employee/query', param);
},
// 通知公告-员工-获取单个通知(用于承诺书展示) @author zhuoda
getOne(param) {
return postRequest('/oa/notice/employee/getOne', param);
},
// 【员工】通知公告-查询 查看记录 @author zhuoda
queryViewRecord(param) {
return postRequest('/oa/notice/employee/queryViewRecord', param);

6
src/components/support/file-upload/index.vue

@ -1,12 +1,6 @@
<!--
* 文件上传
*
* @Author: 1024创新实验室善逸
* @Date: 2022-08-12 20:19:39
* @Wechat: zhuda1024
* @Email: lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net Since 2012
*
-->
<template>
<div class="clearfix">

161
src/views/business/erp/letter/letter-list.vue

@ -9,16 +9,35 @@
<!---------- 查询表单form begin ----------->
<a-form class="smart-query-form">
<a-row class="smart-query-form-row">
<a-col :span="24" style="text-align: right;">
<a-col :span="8">
<a-form-item label="用户名称" class="smart-query-form-item">
<a-select
v-model:value="queryForm.userId"
style="width: 200px"
placeholder="请输入或选择用户"
:showSearch="true"
:allowClear="true"
:filterOption="false"
:notFoundContent="employeeSearchLoading ? '搜索中...' : '暂无数据'"
@search="handleEmployeeSearch"
@focus="handleEmployeeFocus"
>
<a-select-option v-for="item in filteredEmployeeList" :key="item.employeeId" :value="item.employeeId">
{{ item.actualName }}
<template v-if="item.departmentName"> {{ item.departmentName }} </template>
<template v-if="item.phone"> - {{ item.phone }} </template>
</a-select-option>
</a-select>
</a-form-item>
</a-col>
<a-col :span="16" style="text-align: right;">
<a-form-item class="smart-query-form-item">
<!--
<a-button @click="resetQuery" class="smart-margin-left10">
<template #icon>
<ReloadOutlined />
</template>
重置
</a-button>
-->
<a-button type="primary" @click="onSearch">
<template #icon>
<SearchOutlined />
@ -54,10 +73,9 @@
:pagination="false"
>
<template #bodyCell="{ text, record, column }">
<template v-if="column.dataIndex === 'action'">
<div class="smart-table-operate">
<a-button type="link" size="small" @click="viewDetail(record)">查看详情</a-button>
</div>
</template>
</template>
@ -81,18 +99,26 @@
</div>
<LetterForm ref="formRef" @reloadList="queryData"/>
<AgreementModal
v-if="showAgreementModal"
:letterId="currentLetterId"
@confirm="handleAgreementConfirm"
@cancel="handleAgreementCancel"
/>
</a-card>
</template>
<script setup>
import { reactive, ref, onMounted } from 'vue';
import { message, Modal } from 'ant-design-vue';
import { SmartLoading } from '/@/components/framework/smart-loading';
import { letterApi } from '/@/api/business/letter/letter-api';
import { PAGE_SIZE_OPTIONS } from '/@/constants/common-const';
import { smartSentry } from '/@/lib/smart-sentry';
import TableOperator from '/@/components/support/table-operator/index.vue';
import LetterForm from './letter-form.vue';
import { message, Modal } from 'ant-design-vue';
import { SmartLoading } from '/@/components/framework/smart-loading';
import { letterApi } from '/@/api/business/letter/letter-api';
import { employeeApi } from '/@/api/system/employee-api';
import { PAGE_SIZE_OPTIONS } from '/@/constants/common-const';
import { smartSentry } from '/@/lib/smart-sentry';
import TableOperator from '/@/components/support/table-operator/index.vue';
import LetterForm from './letter-form.vue';
import AgreementModal from '/@/views/system/home/components/agreement.vue';
// ---------------------------- ----------------------------
@ -112,16 +138,63 @@
dataIndex: 'createTime',
ellipsis: true,
},
{
title: '操作',
dataIndex: 'action',
width: 100,
align: 'center',
},
]);
// ---------------------------- ----------------------------
const showAgreementModal = ref(false);
const currentLetterId = ref(null);
//
async function viewDetail(record) {
try {
// letterId
currentLetterId.value = record.letterId;
showAgreementModal.value = true;
} catch (error) {
console.error('查看承诺书详情失败:', error);
//
Modal.error({
title: '错误',
content: '获取承诺书详情失败,请稍后重试',
okText: '确定',
});
}
}
function handleAgreementConfirm() {
showAgreementModal.value = false;
currentLetterId.value = null;
}
function handleAgreementCancel() {
showAgreementModal.value = false;
currentLetterId.value = null;
}
// ---------------------------- ----------------------------
const queryFormState = {
pageNum: 1,
pageSize: 10,
userId: undefined, // ID
};
// form
const queryForm = reactive({ ...queryFormState });
//
const employeeList = ref([]);
//
const filteredEmployeeList = ref([]);
//
const employeeSearchLoading = ref(false);
//
const searchKeyword = ref('');
// loading
const tableLoading = ref(false);
//
@ -129,6 +202,62 @@
//
const total = ref(0);
//
async function loadAllEmployees() {
if (employeeList.value.length > 0) {
return; //
}
try {
employeeSearchLoading.value = true;
const result = await employeeApi.queryAll();
if (result.data) {
employeeList.value = result.data;
filteredEmployeeList.value = result.data; //
}
} catch (error) {
console.error('加载员工列表失败:', error);
message.error('加载员工列表失败');
} finally {
employeeSearchLoading.value = false;
}
}
//
function handleEmployeeSearch(value) {
searchKeyword.value = value;
filterEmployeeList(value);
}
//
function handleEmployeeFocus() {
if (employeeList.value.length === 0) {
loadAllEmployees();
} else {
//
filterEmployeeList(searchKeyword.value);
}
}
//
function filterEmployeeList(keyword) {
if (!keyword || keyword.trim() === '') {
//
filteredEmployeeList.value = employeeList.value;
} else {
//
const lowerKeyword = keyword.toLowerCase();
filteredEmployeeList.value = employeeList.value.filter(employee => {
return (
(employee.actualName && employee.actualName.toLowerCase().includes(lowerKeyword)) ||
(employee.departmentName && employee.departmentName.toLowerCase().includes(lowerKeyword)) ||
(employee.phone && employee.phone.includes(keyword)) ||
(employee.loginName && employee.loginName.toLowerCase().includes(lowerKeyword))
);
});
}
}
//
function resetQuery() {
let pageSize = queryForm.pageSize;
@ -158,7 +287,13 @@
}
onMounted(queryData);
onMounted(() => {
queryData();
//
loadAllEmployees();
});
//
</script>

37
src/views/system/home/components/agreement-modal.vue

@ -109,32 +109,29 @@ function handleScroll(event) {
}
}
//
//
async function getFirstNoticeDetail() {
try {
loading.value = true;
// ID1
const queryForm = {
noticeTypeId: 1,
pageNum: 1,
pageSize: 1,
searchCount: false,
};
const result = await noticeApi.queryEmployeeNotice(queryForm);
if (result.data && result.data.list && result.data.list.length > 0) {
const firstNotice = result.data.list[0];
//
const detailResult = await noticeApi.view(firstNotice.noticeId);
if (detailResult.data) {
Object.assign(noticeDetail, detailResult.data);
}
// 使/oa/notice/employee/getOne
const result = await noticeApi.getOne({});
if (result.data) {
Object.assign(noticeDetail, result.data);
}
} catch (err) {
smartSentry.captureError(err);
console.error('获取公告详情失败:', err);
console.error('获取承诺书详情失败:', err);
//
Object.assign(noticeDetail, {
title: '用户协议与承诺书',
contentHtml: '',
author: '系统管理员',
source: '平台管理',
publishTime: new Date().toLocaleDateString()
});
} finally {
loading.value = false;
}

189
src/views/system/home/components/agreement.vue

@ -0,0 +1,189 @@
<template>
<a-modal
v-model:visible="visible"
:title="noticeDetail.title || '平台协议'"
width="800px"
:maskClosable="false"
:keyboard="false"
:closable="false"
>
<a-spin :spinning="loading">
<div class="agreement-content" @scroll="handleScroll">
<div v-if="noticeDetail.title" class="agreement-text">
<div class="content-header">
<h3>{{ noticeDetail.title }}</h3>
</div>
<div class="content-html" v-html="noticeDetail.contentHtml"></div>
<div v-if="!noticeDetail.contentHtml" class="default-content">
</div>
</div>
<div v-else class="agreement-text">
</div>
</div>
</a-spin>
<template #footer>
<a-button @click="handleCancel" size="large">关闭</a-button>
</template>
</a-modal>
</template>
<script setup>
import { ref, onMounted, reactive, watch } from 'vue';
import { letterApi } from '/@/api/business/letter/letter-api';
import { smartSentry } from '/@/lib/smart-sentry';
const props = defineProps({
// ID
letterId: {
type: [String, Number],
default: null
}
});
const emit = defineEmits(['confirm', 'cancel']);
const visible = ref(false);
const loading = ref(false);
//
const hasScrolledToBottom = ref(false);
//
const noticeDetail = reactive({
title: '',
contentHtml: '',
author: '',
source: '',
publishTime: ''
});
// letterId
watch(() => props.letterId, (newLetterId) => {
if (newLetterId) {
getLetterDetail(newLetterId);
}
}, { immediate: true });
onMounted(() => {
visible.value = true;
// letterId
if (props.letterId) {
getLetterDetail(props.letterId);
}
});
//
function handleScroll(event) {
const element = event.target;
//
if (element.scrollTop + element.clientHeight >= element.scrollHeight - 1) {
hasScrolledToBottom.value = true;
}
}
//
async function getLetterDetail(letterId) {
try {
loading.value = true;
// 使/letter/detail/{letterId}
const result = await letterApi.isLetter(letterId);
if (result.data) {
Object.assign(noticeDetail, result.data);
}
} catch (err) {
smartSentry.captureError(err);
console.error('获取承诺书详情失败:', err);
//
Object.assign(noticeDetail, {
title: '承诺书详情',
contentHtml: '',
author: '系统管理员',
source: '平台管理',
publishTime: new Date().toLocaleDateString()
});
} finally {
loading.value = false;
}
}
const handleConfirm = () => {
emit('confirm');
visible.value = false;
};
const handleCancel = () => {
emit('cancel');
visible.value = false;
};
</script>
<style scoped>
.agreement-content {
max-height: 400px;
overflow-y: auto;
padding: 0 10px;
}
.agreement-text {
line-height: 1.6;
}
.content-header {
border-bottom: 1px solid #e8e8e8;
padding-bottom: 15px;
margin-bottom: 20px;
}
.content-header h3 {
text-align: center;
color: #1e3a8a;
margin-bottom: 10px;
font-size: 18px;
}
.content-header-info {
text-align: center;
color: #666;
font-size: 12px;
}
.content-header-info span {
margin: 0 10px;
}
.content-html {
margin-bottom: 20px;
}
.content-html >>> p {
margin-bottom: 10px;
line-height: 1.6;
}
.content-html >>> h4 {
color: #333;
margin: 15px 0 10px 0;
}
.default-content h4 {
color: #333;
margin: 15px 0 10px 0;
}
.default-content p {
margin-bottom: 8px;
color: #666;
}
.important {
color: #ff4d4f !important;
font-weight: bold;
margin-top: 20px !important;
text-align: center;
}
</style>
Loading…
Cancel
Save