榆钱落尽槿花稀 251689fad9 1.日常考评菜单分类重写
2.增加月度,季度,年度考评评价
3.增加员工个人档案,和档案列表
4.增加考评评价管理
4.日常填报中增加考评列表,展示总加分和总减分,并且可以查看详情
2025-03-25 18:05:02 +08:00

471 lines
16 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>
<lay-container style="padding: 20px">
<lay-card>
<div style="padding: 10px">
<span style="font-size: 18px;vertical-align: center;margin-right: 20px">党风廉政教育</span>
<lay-button v-if="userStore.userInfo.level == 4" type="primary" @click="openNew()" size="sm">新增内容</lay-button>
</div>
<lay-table :page="page" height="600px" size="lg" @change="changePage" :columns="columns" :data-source="dataSource">
<!-- <template v-slot:content="{ data }">
<div v-html="data.content"></div>
</template> -->
<template v-slot:status="{ data }">
<span v-if="data.status == 1">已发布</span>
<span v-if="data.status == 0">已隐藏</span>
</template>
<template v-slot:operator="{ data }">
<lay-space size="lg">
<span style="color: #00A394;cursor: pointer" @click="viewDetail(data)">查看</span>
<span style="color: #00A394;cursor: pointer" @click="editShowMsd(data)">编辑</span>
<lay-popconfirm trigger="click" content="确定要删除吗?" @confirm="delShowMsd(data)">
<span style="color: #00A394;cursor: pointer">删除</span>
</lay-popconfirm>
</lay-space>
</template>
</lay-table>
</lay-card>
<lay-layer v-model="addShow" :title="addIsEdit == 1 ? '新增' : '编辑'" :type="4" :shade="true"
:area="['950px', '100%']" :btn="addButton">
<lay-container fluid="true" style="padding: 20px">
<lay-form :model="addData">
<lay-form-item required label="内容标题" prop="party_style_name">
<lay-input v-model="addData.party_style_name" placeholder="请输入教育标题"></lay-input>
</lay-form-item>
<lay-form-item required label="教育内容" prop="party_style_countent">
<div class="editor">
<quill-editor ref="editorRef" v-model:content="content" :options="options"
contentType="html"></quill-editor>
</div>
</lay-form-item>
</lay-form>
</lay-container>
</lay-layer>
<lay-layer v-model="detailShow" title="党风廉政教育详情" :type="4" :shade="true"
:area="['950px', '100%']" :btn="detailButton">
<lay-container fluid="true" style="padding: 20px">
<h2 style="text-align: center; margin-bottom: 20px;">{{detailData.party_style_name}}</h2>
<div class="detail-content" v-html="detailData.party_style_countent"></div>
</lay-container>
</lay-layer>
</lay-container>
</template>
<script setup lang="ts">
import {
uploadImage,
educationList,
educationCreate,
educationUpdate,
educationDelete
} from '../../api/module/home'
import { ref, onMounted, reactive, shallowRef, watch, nextTick, toRaw } from 'vue'
import { layer } from '@layui/layer-vue'
import { baseURL } from '@/api/http'
import { useUserStore } from '@/store/user'
const userStore = useUserStore()
// Update editor configuration
// 引入富文本编辑器与样式
import { Quill, QuillEditor } from '@vueup/vue-quill'
import '@vueup/vue-quill/dist/vue-quill.snow.css'
const addShow = ref(false);
const content = ref('')
const editorRef = ref<any>(null)
// 如果方法报错把imageHandler 方法放到options的上面
// 处理富文本图片上传
const imageHandler = () => {
// 创建一个文件输入元素
const input = document.createElement('input');
input.setAttribute('type', 'file');
input.setAttribute('accept', 'image/*');
// 模拟点击,打开文件选择对话框
input.click();
// 当用户选择文件后触发的事件
input.onchange = async () => {
// 获取用户选择的文件
const file = input.files ? input.files[0] : null;
if (file) {
// 创建一个 FormData 对象,用于文件上传
const formData = new FormData();
formData.append('file', file);
try {
/**
* @todo 可以选中图片然后把file文件给后端后端给存到文件服务器然后返回一个线上地址
* 这里的abc替换成你的请求接口方法,也可以使用 axios 发送 POST 请求
* */
// todo
// 使用 axios 发送 POST 请求,将文件上传到服务器,这里的abc替换成你的请求接口方法
// 可以选中图片然后把file文件给后端后端给存到文件服务器然后返回一个线上地址
const res = await uploadImage(formData);
// 确保获取到 Quill 编辑器实例
const quill = toRaw(editorRef.value).getQuill()
if (quill) {
// 获取当前光标位置
const range = quill.getSelection(true);
// 在当前光标位置插入上传的图片
quill.insertEmbed(range.index, 'image', res.data.fullurl);
}
} catch (error) {
alert('图片上传失败')
}
}
};
}
// 处理富文本图片上传
const videoHandler = () => {
// 创建一个文件输入元素
const input = document.createElement('input');
input.setAttribute('type', 'file');
input.setAttribute('accept', 'video/*');
// 模拟点击,打开文件选择对话框
input.click();
// 当用户选择文件后触发的事件
input.onchange = async () => {
// 获取用户选择的文件
const file = input.files ? input.files[0] : null;
if (file) {
// 创建一个 FormData 对象,用于文件上传
const formData = new FormData();
formData.append('file', file);
try {
/**
* @todo 可以选中图片然后把file文件给后端后端给存到文件服务器然后返回一个线上地址
* 这里的abc替换成你的请求接口方法,也可以使用 axios 发送 POST 请求
* */
// todo
// 使用 axios 发送 POST 请求,将文件上传到服务器,这里的abc替换成你的请求接口方法
// 可以选中图片然后把file文件给后端后端给存到文件服务器然后返回一个线上地址
const res = await uploadImage(formData);
// 确保获取到 Quill 编辑器实例
const quill = toRaw(editorRef.value).getQuill()
if (quill) {
// 获取当前光标位置
const range = quill.getSelection(true);
// 在当前光标位置插入上传的图片
quill.insertEmbed(range.index, 'video', res.data.fullurl);
}
} catch (error) {
alert('图片上传失败')
}
}
};
}
// 富文本配置
const options = ref({
theme: 'bubble', // 使用snow主题
modules: {
// 富文本头部栏的功能配置
toolbar: {
container: [
['bold', 'italic', 'underline', 'strike'], // 加粗 斜体 下划线 删除线
[{ color: [] }, { background: [] }], // 字体颜色、字体背景颜色
[{ align: [] }], // 对齐方式
[{ size: ['small', false, 'large', 'huge'] }], // 字体大小
[{ font: [] }], // 字体种类
[{ header: [1, 2, 3, 4, 5, 6, false] }], // 标题
[{ direction: 'ltl' }], // 文本方向
[{ direction: 'rtl' }], // 文本方向
[{ indent: '-1' }, { indent: '+1' }], // 缩进
[{ list: 'ordered' }, { list: 'bullet' }], // 有序、无序列表
[{ script: 'sub' }, { script: 'super' }], // 上标/下标
['blockquote', 'code-block'], // 引用 代码块
['clean'], // 清除文本格式
['link', 'image', 'video'], // 链接、图片、视频
],
handlers: {
image: imageHandler, // 点击图片触发事件
video: videoHandler, // 点击视频触发事件
},
},
}
})
// toolbar标题,划过富文本头部提示信息
const titleConfig = [
{ Choice: '.ql-insertMetric', title: '跳转配置' },
{ Choice: '.ql-bold', title: '加粗' },
{ Choice: '.ql-italic', title: '斜体' },
{ Choice: '.ql-underline', title: '下划线' },
{ Choice: '.ql-header', title: '段落格式' },
{ Choice: '.ql-strike', title: '删除线' },
{ Choice: '.ql-blockquote', title: '块引用' },
{ Choice: '.ql-code', title: '插入代码' },
{ Choice: '.ql-code-block', title: '插入代码段' },
{ Choice: '.ql-font', title: '字体' },
{ Choice: '.ql-size', title: '字体大小' },
{ Choice: '.ql-list[value="ordered"]', title: '编号列表' },
{ Choice: '.ql-list[value="bullet"]', title: '项目列表' },
{ Choice: '.ql-direction', title: '文本方向' },
{ Choice: '.ql-header[value="1"]', title: 'h1' },
{ Choice: '.ql-header[value="2"]', title: 'h2' },
{ Choice: '.ql-align', title: '对齐方式' },
{ Choice: '.ql-color', title: '字体颜色' },
{ Choice: '.ql-background', title: '背景颜色' },
{ Choice: '.ql-image', title: '图像' },
{ Choice: '.ql-video', title: '视频' },
{ Choice: '.ql-link', title: '添加链接' },
{ Choice: '.ql-formula', title: '插入公式' },
{ Choice: '.ql-clean', title: '清除字体格式' },
{ Choice: '.ql-script[value="sub"]', title: '下标' },
{ Choice: '.ql-script[value="super"]', title: '上标' },
{ Choice: '.ql-indent[value="-1"]', title: '向左缩进' },
{ Choice: '.ql-indent[value="+1"]', title: '向右缩进' },
{ Choice: '.ql-header .ql-picker-label', title: '标题大小' },
{ Choice: '.ql-header .ql-picker-item[data-value="1"]', title: '标题一' },
{ Choice: '.ql-header .ql-picker-item[data-value="2"]', title: '标题二' },
{ Choice: '.ql-header .ql-picker-item[data-value="3"]', title: '标题三' },
{ Choice: '.ql-header .ql-picker-item[data-value="4"]', title: '标题四' },
{ Choice: '.ql-header .ql-picker-item[data-value="5"]', title: '标题五' },
{ Choice: '.ql-header .ql-picker-item[data-value="6"]', title: '标题六' },
{ Choice: '.ql-header .ql-picker-item:last-child', title: '标准' },
{ Choice: '.ql-size .ql-picker-item[data-value="small"]', title: '小号' },
{ Choice: '.ql-size .ql-picker-item[data-value="large"]', title: '大号' },
{ Choice: '.ql-size .ql-picker-item[data-value="huge"]', title: '超大号' },
{ Choice: '.ql-size .ql-picker-item:nth-child(2)', title: '标准' },
{ Choice: '.ql-align .ql-picker-item:first-child', title: '居左对齐' },
{ Choice: '.ql-align .ql-picker-item[data-value="center"]', title: '居中对齐' },
{ Choice: '.ql-align .ql-picker-item[data-value="right"]', title: '居右对齐' },
{ Choice: '.ql-align .ql-picker-item[data-value="justify"]', title: '两端对齐' }
]
// 给富文本框工具栏加上鼠标悬浮中文提示
const initTitle = () => {
for (let item of titleConfig) {
// .editor 是富文本编辑器的类名
let tip = document.querySelector('.editor ' + item.Choice);
if (tip) {
tip.setAttribute('title', item.title);
}
}
}
const openNew = () => {
addShow.value = true;
}
// 自定义粘贴事件
const customPaste = (e: ClipboardEvent) => {
// Prevent default paste behavior
e.preventDefault()
const clipboardData = e.clipboardData
if (!clipboardData) return
const types = clipboardData.types
if (types.includes('Files')) {
Array.from(clipboardData.files).forEach(file => {
// Handle file paste
// Add your file handling logic here
})
}
}
// 分页变化
const changePage = (ppc: { current: number; limit: number }) => {
page.current = ppc.current;
page.limit = ppc.limit;
getNoticeList();
}
// 列表数据
const dataSource = ref([]);
// 1 add 2 edit
const addIsEdit = ref(1);
// 定义数据类型接口
interface EducationData {
id: number;
party_style_name: string;
party_style_countent: string;
status?: number; // 添加可选的状态字段,如果后端支持
}
// 修改分页参数定义
const page = reactive({
current: 1,
limit: 10,
total: 1
});
// 修改数据结构以匹配数据库字段
const addData = reactive<EducationData>({
id: 0,
party_style_name: '',
party_style_countent: ''
});
onMounted(() => {
getNoticeList()
// Wait for editor to be mounted and initialized
// nextTick(() => {
// // Add null check before accessing editor
// if (editorRef.value && editorRef.value.getQuill()) {
// // Initialize title tooltips
// initTitle()
// // Add paste event listener
// const editor = editorRef.value.getQuill()
// editor.root.addEventListener('paste', customPaste, false)
// }
// })
});
const getNoticeList = () => {
educationList({ page: page.current, size: page.limit }).then((res: any) => {
if (res.code == 1) {
dataSource.value = res.data.data;
page.total = res.data.count;
} else {
layer.msg(res.msg, { icon: 2 })
}
})
}
const editShowMsd = (data: EducationData) => {
addShow.value = true;
addData.id = data.id;
addData.party_style_name = data.party_style_name;
addData.party_style_countent = data.party_style_countent;
content.value = data.party_style_countent;
addIsEdit.value = 2;
}
// 监听内容变化
watch(content, (newValue) => {
addData.party_style_countent = newValue;
})
const delShowMsd = async (data: EducationData) => {
const res = await educationDelete({ id: data.id });
if (res.code == 1) {
layer.msg('删除成功!', { icon: 1 })
getNoticeList();
} else {
layer.msg(res.msg, { icon: 2 })
}
}
const columns = [
{
title: "内容标题",
width: "180px",
key: "party_style_name"
},
{
title: "发布时间",
width: "180px",
key: "createtime"
},
{
title: '操作',
width: '120px',
customSlot: 'operator',
key: 'operator',
align: 'center',
fixed: 'right'
}
]
const onCreated = (editor: any) => {
editorRef.value = Object.seal(editor);
}
const addButton = ref([
{
text: "确认",
callback: async () => {
// 取消调试用的return语句
// console.log(addData);
// return;
if (addData.party_style_name === '') {
layer.msg('教育标题不能为空!', { icon: 2 })
return;
}
if (addData.party_style_countent === '') {
layer.msg('教育内容不能为空!', { icon: 2 })
return;
}
const res = addIsEdit.value == 1
? await educationCreate(addData)
: await educationUpdate(addData);
if (res.code == 1) {
layer.msg('提交成功!', { icon: 1 })
addShow.value = false;
getNoticeList();
} else {
layer.msg(res.msg, { icon: 2 })
}
}
},
{
text: "取消",
callback: () => {
addShow.value = false;
}
}
])
// 详情查看相关
const detailShow = ref(false);
const detailData = reactive<EducationData>({
id: 0,
party_style_name: '',
party_style_countent: ''
});
const viewDetail = (data: EducationData) => {
detailShow.value = true;
detailData.id = data.id;
detailData.party_style_name = data.party_style_name;
detailData.party_style_countent = data.party_style_countent;
}
const detailButton = ref([
{
text: "关闭",
callback: () => {
detailShow.value = false;
}
}
])
</script>
<style scoped>
:deep(.w-e-text-container) {
min-height: 400px !important;
}
:deep(.w-e-toolbar) {
border: 1px solid #ddd !important;
border-bottom: none !important;
}
:deep(.w-e-text-container) {
border: 1px solid #ddd !important;
border-top: none !important;
}
:deep(.ql-editor) {
height: 300px;
width: 100%;
}
:deep(.layui-input-block){
max-width: 85%;
}
:deep(.detail-content) {
padding: 10px;
max-height: calc(100vh - 200px);
overflow-y: auto;
}
</style>