显示员工申请须知+显示课程购买须知

This commit is contained in:
2025-08-16 22:37:28 +08:00
parent 4ec8f8e997
commit b0b985bc53
8 changed files with 409 additions and 63 deletions

BIN
dist---TOP.zip Normal file

Binary file not shown.

BIN
dist160-9092.zip Normal file

Binary file not shown.

BIN
dist8月15日9092.zip Normal file

Binary file not shown.

View File

@ -7,7 +7,7 @@ import router from "../router";
const myAxios = axios.create({
withCredentials: true,
// baseURL:'http://localhost:9091'
// baseURL:'http://localhost:9092'
baseURL:'http://localhost:9092'
// baseURL:'http://1.94.237.210:3457'
//baseURL:'http://1.94.237.210:8088'
//baseURL:'http://27.30.77.229:9091/'
@ -15,7 +15,7 @@ const myAxios = axios.create({
// baseURL:'http://160.202.242.36:9091/'
// baseURL:'http://160.202.242.36:9092/'
// baseURL:'http://160.202.242.36:9092'
baseURL:'https://www.chenxinzhi.top'
// baseURL:'https://www.chenxinzhi.top'
});
myAxios.interceptors.request.use(function (config) {

View File

@ -7,8 +7,22 @@
</button>
</div>
<!-- 加载状态 -->
<div v-if="loadingBanners" class="loading-container">
<a-spin size="large" />
<p class="loading-text">轮播图加载中...</p>
</div>
<!-- 错误提示 -->
<div v-if="bannerError" class="error-container">
<a-alert type="error" :message="bannerError" show-icon />
<a-button type="primary" @click="fetchBanners" class="retry-button">
重新加载
</a-button>
</div>
<!-- 轮播图展示 -->
<a-carousel arrows class="carouselContainer">
<a-carousel v-if="!loadingBanners && !bannerError && banners.length > 0" arrows class="carouselContainer">
<template #prevArrow>
<div class="custom-slick-arrow" style="left: 10px; z-index: 1">
<left-circle-outlined />
@ -28,6 +42,13 @@
</div>
</a-carousel>
<!-- 空状态提示 -->
<div v-if="!loadingBanners && !bannerError && banners.length === 0" class="empty-container">
<a-empty description="暂无轮播图数据">
<a-button type="primary" @click="showAddModal">添加轮播图</a-button>
</a-empty>
</div>
<!-- 新增弹框 -->
<a-modal
v-model:visible="addModalVisible"
@ -104,7 +125,7 @@ import {
CloseCircleOutlined,
DeleteOutlined
} from '@ant-design/icons-vue';
import { message } from 'ant-design-vue';
import { message, Spin as ASpin, Alert as AAlert, Empty as AEmpty } from 'ant-design-vue';
import type { UploadChangeParam } from 'ant-design-vue';
import myAxios from "../../api/myAxios.ts";
@ -129,6 +150,11 @@ const currentBannerUrl = ref('');
const currentBannerId = ref('');
const currentBannerIndex = ref(-1);
// 新增:加载状态和错误状态
const loadingBanners = ref(true);
const bannerError = ref('');
// 获取上传URL和请求头
const uploadUrl = `${myAxios.defaults.baseURL}/file/upload`;
const uploadHeaders = {
@ -302,6 +328,8 @@ const handleDeleteBanner = async () => {
// 获取轮播图列表
const fetchBanners = async () => {
loadingBanners.value = true;
bannerError.value = '';
try {
const response:any = await myAxios.get('/banner/web/list', {
headers: {
@ -321,6 +349,8 @@ const fetchBanners = async () => {
} catch (error) {
console.error('获取轮播图失败:', error);
message.error('获取轮播图失败');
}finally {
loadingBanners.value = false;
}
};
@ -331,6 +361,53 @@ onMounted(() => {
</script>
<style scoped>
/* 加载状态样式 */
.loading-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 40px;
height: 300px;
background: rgba(255, 255, 255, 0.8);
border-radius: 8px;
margin: 20px auto;
}
.loading-text {
margin-top: 16px;
font-size: 16px;
color: #1890ff;
}
/* 错误状态样式 */
.error-container {
display: flex;
flex-direction: column;
align-items: center;
padding: 30px;
background: #fff1f0;
border-radius: 8px;
margin: 20px auto;
max-width: 80%;
}
.retry-button {
margin-top: 20px;
}
/* 空状态样式 */
.empty-container {
display: flex;
flex-direction: column;
align-items: center;
padding: 40px;
background: #fafafa;
border-radius: 8px;
margin: 20px auto;
max-width: 80%;
}
/* 按钮样式 */
.add-button-container {
margin-top: 10px;

View File

@ -70,7 +70,7 @@ const editorConfig = {
})
if (res.code === 1) {
// 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 {

View File

@ -1,7 +1,16 @@
<template>
<form @submit.prevent="handleSubmit" class="modern-form">
<div class="modern-form">
<h2 class="form-title">课程购买须知</h2>
<!-- 只读模式 -->
<div v-if="!editing" class="readonly-container">
<div class="notice-content" v-html="decodedNotice"></div>
<button @click="startEditing" class="edit-button">
<span>修改须知</span>
<div class="button-sparkles"></div>
</button>
</div>
<!-- 编辑模式 -->
<form v-else @submit.prevent="handleSubmit" class="editing-form">
<!-- 富文本编辑器 -->
<div class="rich-text-container">
<div class="rich-text-group">
@ -19,12 +28,17 @@
<div v-if="errorMessage" class="error-message">
{{ errorMessage }}
</div>
<div class="button-group">
<button type="button" @click="cancelEditing" class="cancel-button">
取消
</button>
<button type="submit" class="submit-button">
<span>保存须知</span>
<div class="button-sparkles"></div>
</button>
</div>
</form>
</div>
</template>
<script setup lang="ts">
@ -45,6 +59,20 @@ const formData = reactive<NoticeForm>({
const editorRef = ref<InstanceType<typeof RichTextEditor> | null>(null);
const errorMessage = ref('');
const editorContent = ref('');
const decodedNotice = ref('<p>加载课程须知中...</p>');
const editing = ref(false);
// Base64解码函数
const decode64 = (base64Text: string): string => {
if (!base64Text.trim()) return '';
try {
return decodeURIComponent(escape(atob(base64Text)));
} catch (error) {
console.error('Base64解码失败:', error);
return '内容解析错误';
}
};
// 增强Base64编码方法
const encode64 = (text: string): string => {
@ -83,6 +111,46 @@ const isContentEmpty = (html: string): boolean => {
}
};
// 获取申请须知
const fetchNotice = async () => {
try {
const storedToken = localStorage.getItem('token');
const res: any = await myAxios.post(
`/userInfo/query/courseDesc`,
{},
{
headers: {
'Content-Type': 'application/json',
'Authorization': storedToken
}
}
);
if (res.code === 1 && res.data) {
decodedNotice.value = decode64(res.data);
} else {
decodedNotice.value = `<p class="error-text">获取购买须知失败: ${res.message || '未知错误'}</p>`;
}
} catch (error: any) {
console.error('获取须知失败:', error);
decodedNotice.value = `<p class="error-text">网络错误,请稍后重试</p>`;
}
};
// 开始编辑
const startEditing = () => {
editing.value = true;
// 将当前内容加载到编辑器
editorContent.value = decodedNotice.value;
formData.templateString = decodedNotice.value;
};
// 取消编辑
const cancelEditing = () => {
editing.value = false;
errorMessage.value = '';
};
const handleSubmit = async () => {
try {
await nextTick();
@ -119,6 +187,9 @@ const handleSubmit = async () => {
);
console.log(res)
if (res.code === 1) {
decodedNotice.value = formData.templateString;
editing.value = false;
errorMessage.value = '';
alert('须知保存成功!');
if (editorRef.value) {
@ -155,12 +226,12 @@ const handleSubmit = async () => {
// 初始化时清空错误信息
onMounted(() => {
fetchNotice();
errorMessage.value = '';
});
</script>
<style scoped>
.modern-form {
max-width: 90%;
margin: 2rem auto;
@ -180,6 +251,52 @@ onMounted(() => {
font-weight: 600;
}
.readonly-container {
border: 1px solid #e2e8f0;
border-radius: 0.75rem;
padding: 1.5rem;
margin-bottom: 1.5rem;
background-color: #f9fafb;
}
.notice-content {
min-height: 300px;
padding: 1rem;
line-height: 1.6;
}
.notice-content :deep(p) {
margin-bottom: 1rem;
}
.notice-content :deep(.error-text) {
color: #e53e3e;
}
.edit-button {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
padding: 1rem;
margin-top: 1.5rem;
background: linear-gradient(135deg, #10b981 0%, #059669 100%);
color: white;
border: none;
border-radius: 0.75rem;
font-size: 1rem;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
position: relative;
overflow: hidden;
}
.edit-button:hover {
transform: translateY(-2px);
box-shadow: 0 8px 20px rgba(16, 185, 129, 0.3);
}
.label-text {
display: block;
margin-bottom: 0.6rem;
@ -200,13 +317,18 @@ onMounted(() => {
white-space: pre-wrap;
}
.button-group {
display: flex;
gap: 1rem;
margin-top: 1.5rem;
}
.submit-button {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
flex: 1;
padding: 1rem;
margin-top: 1.5rem;
background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%);
color: white;
border: none;
@ -219,6 +341,26 @@ onMounted(() => {
overflow: hidden;
}
.cancel-button {
display: flex;
align-items: center;
justify-content: center;
flex: 1;
padding: 1rem;
background: #e2e8f0;
color: #4a5568;
border: none;
border-radius: 0.75rem;
font-size: 1rem;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
}
.cancel-button:hover {
background: #cbd5e0;
}
.submit-button:hover {
transform: translateY(-2px);
box-shadow: 0 8px 20px rgba(99, 102, 241, 0.3);
@ -286,6 +428,8 @@ onMounted(() => {
padding: 1.5rem;
margin: 1rem;
}
.button-group {
flex-direction: column;
}
}
</style>

View File

@ -1,8 +1,18 @@
<template>
<form @submit.prevent="handleSubmit" class="modern-form">
<h2 class="form-title">员工账号申请须知</h2>
<div class="modern-form">
<h2 class="form-title">员工申请须知</h2>
<!-- 富文本编辑器 -->
<!-- 只读模式 -->
<div v-if="!editing" class="readonly-container">
<div class="notice-content" v-html="decodedNotice"></div>
<button @click="startEditing" class="edit-button">
<span>修改须知</span>
<div class="button-sparkles"></div>
</button>
</div>
<!-- 编辑模式 -->
<form v-else @submit.prevent="handleSubmit" class="editing-form">
<div class="rich-text-container">
<div class="rich-text-group">
<span class="label-text">须知详情</span>
@ -15,16 +25,21 @@
</div>
</div>
<!-- 错误提示 -->
<div v-if="errorMessage" class="error-message">
{{ errorMessage }}
</div>
<div class="button-group">
<button type="button" @click="cancelEditing" class="cancel-button">
取消
</button>
<button type="submit" class="submit-button">
<span>保存须知</span>
<div class="button-sparkles"></div>
</button>
</div>
</form>
</div>
</template>
<script setup lang="ts">
@ -33,7 +48,6 @@ import RichTextEditor from '../components/RichTextEditor.vue';
import '@vueup/vue-quill/dist/vue-quill.snow.css';
import myAxios from "../../api/myAxios.ts";
interface NoticeForm {
templateString: string;
}
@ -45,13 +59,26 @@ const formData = reactive<NoticeForm>({
const editorRef = ref<InstanceType<typeof RichTextEditor> | null>(null);
const errorMessage = ref('');
const editorContent = ref('');
const decodedNotice = ref('<p>加载申请须知中...</p>');
const editing = ref(false);
// 增强Base64编码方法
// Base64解码函数
const decode64 = (base64Text: string): string => {
if (!base64Text.trim()) return '';
try {
return decodeURIComponent(escape(atob(base64Text)));
} catch (error) {
console.error('Base64解码失败:', error);
return '内容解析错误';
}
};
// Base64编码函数
const encode64 = (text: string): string => {
if (!text.trim()) return '';
try {
return btoa(unescape(encodeURIComponent(text)));
} catch (error) {
console.error('Base64编码失败:', error);
@ -59,23 +86,18 @@ const encode64 = (text: string): string => {
}
};
const handleEditorChange = (html: string) => {
formData.templateString = html;
editorContent.value = html;
};
const isContentEmpty = (html: string): boolean => {
if (!html) return true;
try {
const tempDiv = document.createElement('div');
tempDiv.innerHTML = html;
const plainText = tempDiv.textContent || '';
return plainText.trim() === '';
} catch (error) {
console.error('内容解析失败:', error);
@ -83,6 +105,46 @@ const isContentEmpty = (html: string): boolean => {
}
};
// 获取申请须知
const fetchNotice = async () => {
try {
const storedToken = localStorage.getItem('token');
const res: any = await myAxios.post(
`/userInfo/query/applyNotice`,
{},
{
headers: {
'Content-Type': 'application/json',
'Authorization': storedToken
}
}
);
if (res.code === 1 && res.data) {
decodedNotice.value = decode64(res.data);
} else {
decodedNotice.value = `<p class="error-text">获取申请须知失败: ${res.message || '未知错误'}</p>`;
}
} catch (error: any) {
console.error('获取须知失败:', error);
decodedNotice.value = `<p class="error-text">网络错误,请稍后重试</p>`;
}
};
// 开始编辑
const startEditing = () => {
editing.value = true;
// 将当前内容加载到编辑器
editorContent.value = decodedNotice.value;
formData.templateString = decodedNotice.value;
};
// 取消编辑
const cancelEditing = () => {
editing.value = false;
errorMessage.value = '';
};
const handleSubmit = async () => {
try {
await nextTick();
@ -95,7 +157,6 @@ const handleSubmit = async () => {
const encryptedContent = encode64(formData.templateString);
if (!encryptedContent) {
errorMessage.value = '内容加密失败,请检查输入内容';
return;
@ -105,9 +166,6 @@ const handleSubmit = async () => {
templateString: encryptedContent
};
console.log('提交内容:', formData.templateString);
console.log('加密内容:', encryptedContent);
const res: any = await myAxios.post(
`/userInfo/modify/applyNotice`,
payload,
@ -118,31 +176,26 @@ const handleSubmit = async () => {
}
}
);
console.log(res)
if (res.code === 1) {
alert('须知保存成功!');
if (editorRef.value) {
editorRef.value.clearContent();
}
formData.templateString = '';
editorContent.value = '';
if (res.code === 1) {
// 更新本地内容并退出编辑模式
decodedNotice.value = formData.templateString;
editing.value = false;
errorMessage.value = '';
alert('须知保存成功!');
} else {
errorMessage.value = `保存失败:${res.message || '服务器错误'} (${res.code})`;
}
} catch (error: any) {
console.error('请求失败:', error);
let detailedError = '提交失败:';
if (error.response) {
if (error.response) {
const serverMessage = error.response.data?.message ||
error.response.data?.error ||
'无详细错误信息';
detailedError += `服务器响应错误 (${error.response.status}): ${serverMessage}`;
if (error.response.data?.code === 40000) {
detailedError = '须知内容不能为空';
}
@ -156,14 +209,13 @@ const handleSubmit = async () => {
}
};
// 初始化时清空错误信息
// 初始化时获取申请须知
onMounted(() => {
errorMessage.value = '';
fetchNotice();
});
</script>
<style scoped>
.modern-form {
max-width: 90%;
margin: 2rem auto;
@ -183,6 +235,52 @@ onMounted(() => {
font-weight: 600;
}
.readonly-container {
border: 1px solid #e2e8f0;
border-radius: 0.75rem;
padding: 1.5rem;
margin-bottom: 1.5rem;
background-color: #f9fafb;
}
.notice-content {
min-height: 300px;
padding: 1rem;
line-height: 1.6;
}
.notice-content :deep(p) {
margin-bottom: 1rem;
}
.notice-content :deep(.error-text) {
color: #e53e3e;
}
.edit-button {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
padding: 1rem;
margin-top: 1.5rem;
background: linear-gradient(135deg, #10b981 0%, #059669 100%);
color: white;
border: none;
border-radius: 0.75rem;
font-size: 1rem;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
position: relative;
overflow: hidden;
}
.edit-button:hover {
transform: translateY(-2px);
box-shadow: 0 8px 20px rgba(16, 185, 129, 0.3);
}
.label-text {
display: block;
margin-bottom: 0.6rem;
@ -203,13 +301,18 @@ onMounted(() => {
white-space: pre-wrap;
}
.button-group {
display: flex;
gap: 1rem;
margin-top: 1.5rem;
}
.submit-button {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
flex: 1;
padding: 1rem;
margin-top: 1.5rem;
background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%);
color: white;
border: none;
@ -222,6 +325,26 @@ onMounted(() => {
overflow: hidden;
}
.cancel-button {
display: flex;
align-items: center;
justify-content: center;
flex: 1;
padding: 1rem;
background: #e2e8f0;
color: #4a5568;
border: none;
border-radius: 0.75rem;
font-size: 1rem;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
}
.cancel-button:hover {
background: #cbd5e0;
}
.submit-button:hover {
transform: translateY(-2px);
box-shadow: 0 8px 20px rgba(99, 102, 241, 0.3);
@ -289,6 +412,8 @@ onMounted(() => {
padding: 1.5rem;
margin: 1rem;
}
.button-group {
flex-direction: column;
}
}
</style>