新增员工申请须知功能

This commit is contained in:
2025-08-15 00:11:02 +08:00
parent da9feb26c6
commit 09b105b9e5
10 changed files with 167 additions and 187 deletions

BIN
dist8月12日.zip Normal file

Binary file not shown.

BIN
dist9092.zip Normal file

Binary file not shown.

BIN
distTOP.zip Normal file

Binary file not shown.

View File

@ -1,8 +1,9 @@
// export const downLoadImage = 'http://1.94.237.210:3457/file/download/'
// export const downLoadImage = 'http://27.30.77.229:9092/file/download/'
export const downLoadImage = 'http://160.202.242.36:9092/file/download/'
// export const downLoadImage = 'http://160.202.242.36:9092/file/download/'
// export const downLoadImage = 'http://160.202.242.36:9091/file/download/'
export const downLoadImage = 'https://www.chenxinzhi.top/file/download/'

View File

@ -14,8 +14,8 @@ const myAxios = axios.create({
//baseURL:'http://27.30.77.229:9092/'
// baseURL:'http://160.202.242.36:9091/'
// baseURL:'http://160.202.242.36:9092/'
baseURL:'http://160.202.242.36:9092'
// baseURL:'http://160.202.242.36:9092'
baseURL:'https://www.chenxinzhi.top'
});
myAxios.interceptors.request.use(function (config) {

View File

@ -88,7 +88,7 @@ onMounted(() => {
<ul>
<li><span>主管数量</span><strong>{{ dashboardData.superCount }}</strong></li>
<li><span>员工数量</span><strong>{{ dashboardData.empCount }}</strong></li>
<li><span>户数量</span><strong>{{ dashboardData.promoCount }}</strong></li>
<li><span>户数量</span><strong>{{ dashboardData.promoCount }}</strong></li>
</ul>
</div>
<!-- 今日统计 -->

View File

@ -71,7 +71,7 @@ const editorConfig = {
if (res.code === 1) {
// 拼接完整 URL 地址再插入到富文本中
const imageUrl = 'http://160.202.242.36:9092/file/download/' + res.data
const imageUrl = 'https://www.chenxinzhi.top/file/download/' + res.data
insertFn(imageUrl)
} else {
console.error('上传失败:', res.message)

View File

@ -2,140 +2,167 @@
<form @submit.prevent="handleSubmit" class="modern-form">
<h2 class="form-title">员工账号申请须知</h2>
<!-- 富文本编辑器区域 - 修改为50%宽度 -->
<!-- 富文本编辑器 -->
<div class="rich-text-container">
<div class="rich-text-columns">
<div class="rich-text-group">
<span class="label-text">须知详情</span>
<RichTextEditor
v-model="formData.detail"
ref="editorRef"
:value="editorContent"
:disable="false"
@content-change="(html:any) => formData.detail = html"
@content-change="handleEditorChange"
/>
</div>
</div>
<!-- 错误提示 -->
<div v-if="errorMessage" class="error-message">
{{ errorMessage }}
</div>
<button type="submit" class="submit-button">
<span>立即创建</span>
<span>保存须知</span>
<div class="button-sparkles"></div>
</button>
</form>
</template>
<script setup lang="ts">
import { reactive, ref } from 'vue';
import { reactive, ref, nextTick, onMounted } from 'vue';
import RichTextEditor from '../components/RichTextEditor.vue';
import '@vueup/vue-quill/dist/vue-quill.snow.css';
import myAxios from "../../api/myAxios.ts";
interface CourseForm {
name: string;
type: string;
image: string;
detail: string;
promoCodeDesc: string;
originPrice: number;
discountPrice: number;
firstLevelRate: number;
secondLevelRate: number;
// 定义表单数据结构
interface NoticeForm {
templateString: string;
}
const fileName = ref('');
// 新增价格错误状态
const priceError = ref('');
const formData = reactive<CourseForm>({
name: '',
type: '',
image: '',
detail: '',
promoCodeDesc: '',
originPrice: 0,
discountPrice: 0,
firstLevelRate: 0,
secondLevelRate: 0
const formData = reactive<NoticeForm>({
templateString: ''
});
const encode64 = (text: string): string => {
return btoa(
encodeURIComponent(text).replace(
/%([0-9A-F]{2})/g,
(_, p1) => String.fromCharCode(parseInt(p1, 16))
))
};
const editorRef = ref<InstanceType<typeof RichTextEditor> | null>(null);
const errorMessage = ref('');
const editorContent = ref(''); // 单独管理编辑器内容
const encryptValue = (value: any, key: string): any => {
// 数值型字段直接返回,不加密
if (key === 'originPrice' || key === 'discountPrice' ||
key === 'image' || key ==='name'||key === 'type') {
return value;
}
// 增强Base64编码方法
const encode64 = (text: string): string => {
if (!text.trim()) return '';
try {
const valueString = typeof value === 'object'
? JSON.stringify(value)
: String(value);
return encode64(valueString);
// 使用更健壮的Base64编码
return btoa(unescape(encodeURIComponent(text)));
} catch (error) {
console.error('Base64编码失败:', error);
return value;
return '';
}
};
// 处理编辑器内容变化
const handleEditorChange = (html: string) => {
formData.templateString = html;
editorContent.value = html;
};
const handleSubmit = async () => {
// 重置错误状态
priceError.value = '';
// 检查内容是否为空去除所有HTML标签
const isContentEmpty = (html: string): boolean => {
if (!html) return true;
try {
// 创建临时元素解析HTML
const tempDiv = document.createElement('div');
tempDiv.innerHTML = html;
// 获取纯文本内容
const plainText = tempDiv.textContent || '';
// 检查是否有实际内容
return plainText.trim() === '';
} catch (error) {
console.error('内容解析失败:', error);
return true;
}
};
const handleSubmit = async () => {
try {
await nextTick();
const storedToken = localStorage.getItem('token');
const encryptedFormData: Record<string, any> = {};
Object.entries(formData).forEach(([key, value]) => {
if (value === null || value === undefined || value === '') return;
encryptedFormData[key] = encryptValue(value, key);
});
// 检查内容是否为空
if (isContentEmpty(formData.templateString)) {
errorMessage.value = '须知内容不能为空';
return;
}
const res: any = await myAxios.post(`/course/add`, encryptedFormData, {
const encryptedContent = encode64(formData.templateString);
// 检查加密结果是否有效
if (!encryptedContent) {
errorMessage.value = '内容加密失败,请检查输入内容';
return;
}
const payload = {
templateString: encryptedContent
};
console.log('提交内容:', formData.templateString);
console.log('加密内容:', encryptedContent);
const res: any = await myAxios.post(
`/userInfo/modify/applyNotice`,
payload,
{
headers: {
'Content-Type': 'application/json',
'Authorization': storedToken
}
});
if (res.code === 1) {
alert('须知创建成功!');
Object.assign(formData, {
name: '',
type: '',
image: '',
detail: '',
promoCodeDesc: '',
originPrice: 0,
discountPrice: 0,
firstLevelRate: 0,
secondLevelRate: 0
});
fileName.value = '';
} else {
alert(`创建失败:${res.message}`);
}
} catch (error) {
);
console.log(res)
if (res.code === 1) {
alert('须知保存成功!');
formData.templateString = '';
editorContent.value = '';
errorMessage.value = '';
} else {
errorMessage.value = `保存失败:${res.message || '服务器错误'} (${res.code})`;
}
} catch (error: any) {
console.error('请求失败:', error);
alert('提交失败,请检查控制台获取详细信息');
let detailedError = '提交失败:';
if (error.response) {
// 优先显示后端返回的错误信息
const serverMessage = error.response.data?.message ||
error.response.data?.error ||
'无详细错误信息';
detailedError += `服务器响应错误 (${error.response.status}): ${serverMessage}`;
// 特殊处理40000错误空字符串错误
if (error.response.data?.code === 40000) {
detailedError = '须知内容不能为空';
}
} else if (error.request) {
detailedError += '请求已发出但无响应';
} else {
detailedError += error.message || '未知错误';
}
errorMessage.value = detailedError;
}
};
// 初始化时清空错误信息
onMounted(() => {
errorMessage.value = '';
});
</script>
<style scoped>
/* 精简后的样式 */
.modern-form {
max-width: 90%;
margin: 2rem auto;
@ -144,6 +171,7 @@ const handleSubmit = async () => {
border-radius: 1.5rem;
box-shadow: 0 10px 30px rgba(0,0,0,0.1);
font-family: 'Segoe UI', system-ui, sans-serif;
position: relative;
}
.form-title {
@ -152,39 +180,28 @@ const handleSubmit = async () => {
color: #2c3e50;
margin-bottom: 2rem;
font-weight: 600;
letter-spacing: 0.5px;
}
.label-text {
display: block;
margin-bottom: 0.6rem;
color: #4a5568;
font-size: 0.9rem;
padding: 10px;
color: #6366f1;
font-size: 18px;
font-weight: 500;
}
.select-wrapper:focus-within .select-arrow {
transform: translateY(-50%) rotate(180deg);
.error-message {
color: #e53e3e;
background: #fff5f5;
padding: 0.8rem;
border-radius: 0.5rem;
margin-top: 1rem;
border: 1px solid #fc8181;
font-size: 0.9rem;
white-space: pre-wrap;
}
/* 下拉选项样式 */
.select-field option {
padding: 8px 12px;
background: white;
color: #4a5568;
}
.select-field option:hover {
background: #f1f5f9;
}
.select-field option:checked {
background: #6366f1;
color: white;
}
.submit-button {
display: flex;
align-items: center;
@ -219,21 +236,9 @@ const handleSubmit = async () => {
}
@keyframes sparkle {
0% { transform: scale(0) translate(0,0); }
50% { transform: scale(1) translate(100px, -50px); }
100% { transform: scale(0) translate(200px, -100px); }
}
@media (max-width: 768px) {
.form-grid {
grid-template-columns: 1fr;
}
.modern-form {
padding: 1.5rem;
margin: 1rem;
}
0% { transform: scale(0) translate(0,0); opacity: 1; }
50% { transform: scale(1) translate(100px, -50px); opacity: 0.8; }
100% { transform: scale(0) translate(200px, -100px); opacity: 0; }
}
.rich-text-container {
@ -241,8 +246,6 @@ const handleSubmit = async () => {
width: 100%;
}
.rich-text-group {
width: 100%;
height: 100%;
@ -250,14 +253,7 @@ const handleSubmit = async () => {
border-radius: 0.75rem;
overflow: hidden;
transition: all 0.3s ease;
min-height: 320px;
flex-grow: 1;
}
@media (max-width: 768px) {
.rich-text-columns {
grid-template-columns: 1fr;
}
max-height: 420px;
}
.rich-text-group:focus-within {
@ -265,43 +261,11 @@ const handleSubmit = async () => {
box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.1);
}
.rich-text-group .label-text {
font-size: 0.95rem;
font-weight: 600;
color: #3b4151;
padding: 12px 12px 0;
@media (max-width: 768px) {
.modern-form {
padding: 1.5rem;
margin: 1rem;
}
.phone-section-content * {
max-width: 100%;
}
.phone-section-content img {
max-width: 100%;
height: auto;
border-radius: 8px;
margin: 10px 0;
}
.phone-section-content h1,
.phone-section-content h2,
.phone-section-content h3 {
color: #2c3e50;
margin-top: 15px;
margin-bottom: 10px;
}
.phone-section-content p {
margin-bottom: 10px;
}
.phone-section-content ul,
.phone-section-content ol {
margin-left: 20px;
margin-bottom: 10px;
}
</style>

View File

@ -313,7 +313,8 @@ const filterNameInput = (e: Event) => {
// 使用正则表达式过滤非中文字符
// value = value.replace(/[^\u4e00-\u9fa5]/g, '');
value = value.replace(/[^a-zA-Z\u4e00-\u9fa5]/g, '');
// value = value.replace(/[^a-zA-Z\u4e00-\u9fa5]/g, '');
value = value.replace(/[^\u4e00-\u9fa50-9]/g, '');
// 更新输入框值
input.value = value;
searchName.value = value;

View File

@ -200,6 +200,19 @@ const handleKeyDown = (e: KeyboardEvent) => {
}
};
// 新增学校名称输入过滤函数
const filterSchoolInput = (e: Event) => {
const input = e.target as HTMLInputElement;
let value = input.value;
// 只允许中文和数字
value = value.replace(/[^\u4e00-\u9fa50-9]/g, '');
// 更新输入框值
input.value = value;
queryParams.nickName = value;
};
</script>
<template>
@ -249,8 +262,9 @@ const handleKeyDown = (e: KeyboardEvent) => {
<Input
v-model:value="queryParams.nickName"
placeholder="用户昵称"
placeholder="学校"
style="width: 150px; margin-right: 10px;"
@input="filterSchoolInput"
/>
<DatePicker.RangePicker