新增课程购买须知+轮播图的增删改查
This commit is contained in:
2
components.d.ts
vendored
2
components.d.ts
vendored
@ -13,6 +13,7 @@ declare module 'vue' {
|
|||||||
ABreadcrumbItem: typeof import('ant-design-vue/es')['BreadcrumbItem']
|
ABreadcrumbItem: typeof import('ant-design-vue/es')['BreadcrumbItem']
|
||||||
AButton: typeof import('ant-design-vue/es')['Button']
|
AButton: typeof import('ant-design-vue/es')['Button']
|
||||||
ACard: typeof import('ant-design-vue/es')['Card']
|
ACard: typeof import('ant-design-vue/es')['Card']
|
||||||
|
ACarousel: typeof import('ant-design-vue/es')['Carousel']
|
||||||
ACol: typeof import('ant-design-vue/es')['Col']
|
ACol: typeof import('ant-design-vue/es')['Col']
|
||||||
AConfigProvider: typeof import('ant-design-vue/es')['ConfigProvider']
|
AConfigProvider: typeof import('ant-design-vue/es')['ConfigProvider']
|
||||||
ADatePicker: typeof import('ant-design-vue/es')['DatePicker']
|
ADatePicker: typeof import('ant-design-vue/es')['DatePicker']
|
||||||
@ -53,6 +54,7 @@ declare module 'vue' {
|
|||||||
ATag: typeof import('ant-design-vue/es')['Tag']
|
ATag: typeof import('ant-design-vue/es')['Tag']
|
||||||
ATextarea: typeof import('ant-design-vue/es')['Textarea']
|
ATextarea: typeof import('ant-design-vue/es')['Textarea']
|
||||||
AUpload: typeof import('ant-design-vue/es')['Upload']
|
AUpload: typeof import('ant-design-vue/es')['Upload']
|
||||||
|
AUploadDragger: typeof import('ant-design-vue/es')['UploadDragger']
|
||||||
RouterLink: typeof import('vue-router')['RouterLink']
|
RouterLink: typeof import('vue-router')['RouterLink']
|
||||||
RouterView: typeof import('vue-router')['RouterView']
|
RouterView: typeof import('vue-router')['RouterView']
|
||||||
}
|
}
|
||||||
|
BIN
dist8月15日.zip
Normal file
BIN
dist8月15日.zip
Normal file
Binary file not shown.
@ -52,6 +52,7 @@
|
|||||||
</template>
|
</template>
|
||||||
<a-menu-item key="/courseManagement">课程管理</a-menu-item>
|
<a-menu-item key="/courseManagement">课程管理</a-menu-item>
|
||||||
<a-menu-item key="/courseOrder">课程订单</a-menu-item>
|
<a-menu-item key="/courseOrder">课程订单</a-menu-item>
|
||||||
|
<a-menu-item key="/coursePurchaseNotice">课程购买须知</a-menu-item>
|
||||||
</a-sub-menu>
|
</a-sub-menu>
|
||||||
<!-- <a-sub-menu>-->
|
<!-- <a-sub-menu>-->
|
||||||
<!-- <template #title>-->
|
<!-- <template #title>-->
|
||||||
@ -86,6 +87,10 @@
|
|||||||
<a-menu-item key="/applicationInstructions">员工申请须知</a-menu-item>
|
<a-menu-item key="/applicationInstructions">员工申请须知</a-menu-item>
|
||||||
|
|
||||||
</a-sub-menu>
|
</a-sub-menu>
|
||||||
|
<a-menu-item key="/carouselManagement">
|
||||||
|
<PieChartOutlined />
|
||||||
|
<span>轮播图管理</span>
|
||||||
|
</a-menu-item>
|
||||||
</a-menu>
|
</a-menu>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import {createRouter, createWebHashHistory} from "vue-router";
|
import {createRouter, createWebHashHistory} from "vue-router";
|
||||||
import {routes} from "./routes";
|
import {routes} from "./routes";
|
||||||
|
|
||||||
// 创建路由实例并传递 `routes` 配置
|
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
// 4. 内部提供了 history 模式的实现。为了简单起见,我们在这里使用 hash 模式。
|
|
||||||
history: createWebHashHistory(),
|
history: createWebHashHistory(),
|
||||||
routes, // `routes: routes` 的缩写
|
routes,
|
||||||
})
|
})
|
||||||
|
|
||||||
export default router
|
export default router
|
@ -1,6 +1,6 @@
|
|||||||
// 将路由规则 routes 导出
|
|
||||||
export const routes = [
|
export const routes = [
|
||||||
// 全局路由(无需嵌套上左右整体布局)
|
|
||||||
{
|
{
|
||||||
path: '/',
|
path: '/',
|
||||||
name: 'Login',
|
name: 'Login',
|
||||||
@ -11,12 +11,12 @@ export const routes = [
|
|||||||
name: '全局测试页面',
|
name: '全局测试页面',
|
||||||
component: () => import("../view/Test.vue"),
|
component: () => import("../view/Test.vue"),
|
||||||
},
|
},
|
||||||
// 管理端
|
|
||||||
{
|
{
|
||||||
path: '/manage',
|
path: '/manage',
|
||||||
component: () => import("../layout/ManageLayout.vue"),
|
component: () => import("../layout/ManageLayout.vue"),
|
||||||
children: [
|
children: [
|
||||||
// 首页
|
|
||||||
{
|
{
|
||||||
path: '/index',
|
path: '/index',
|
||||||
name: '首页',
|
name: '首页',
|
||||||
@ -182,6 +182,16 @@ export const routes = [
|
|||||||
name:'员工申请须知',
|
name:'员工申请须知',
|
||||||
component: ()=> import("../view/employeeApplication/applicationInstructions.vue")
|
component: ()=> import("../view/employeeApplication/applicationInstructions.vue")
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path:'/coursePurchaseNotice',
|
||||||
|
name:'课程购买须知',
|
||||||
|
component: ()=> import("../view/course/coursePurchaseNotice.vue")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path:'/carouselManagement',
|
||||||
|
name:'轮播图管理',
|
||||||
|
component: ()=> import("../view/carousel/carouselManagement.vue")
|
||||||
|
},
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
]
|
]
|
510
src/view/carousel/carouselManagement.vue
Normal file
510
src/view/carousel/carouselManagement.vue
Normal file
@ -0,0 +1,510 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<!-- 新增按钮 -->
|
||||||
|
<div class="add-button-container">
|
||||||
|
<button @click="showAddModal" class="add-button">
|
||||||
|
<span class="plus-icon">+</span> 新增轮播图
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 轮播图展示 -->
|
||||||
|
<a-carousel arrows class="carouselContainer">
|
||||||
|
<template #prevArrow>
|
||||||
|
<div class="custom-slick-arrow" style="left: 10px; z-index: 1">
|
||||||
|
<left-circle-outlined />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #nextArrow>
|
||||||
|
<div class="custom-slick-arrow" style="right: 10px">
|
||||||
|
<right-circle-outlined />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div v-for="(banner, index) in banners" :key="index">
|
||||||
|
<img
|
||||||
|
:src="getBannerUrl(banner)"
|
||||||
|
class="banner-image"
|
||||||
|
@click="showPreviewModal(banner, index)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</a-carousel>
|
||||||
|
|
||||||
|
<!-- 新增弹框 -->
|
||||||
|
<a-modal
|
||||||
|
v-model:visible="addModalVisible"
|
||||||
|
title="新增轮播图"
|
||||||
|
@ok="handleAddOk"
|
||||||
|
@cancel="handleAddCancel"
|
||||||
|
:confirm-loading="confirmLoading"
|
||||||
|
>
|
||||||
|
<!-- 图片上传 -->
|
||||||
|
<a-upload-dragger
|
||||||
|
name="file"
|
||||||
|
:multiple="false"
|
||||||
|
:action="uploadUrl"
|
||||||
|
:headers="uploadHeaders"
|
||||||
|
:data="uploadData"
|
||||||
|
@change="handleUploadChange"
|
||||||
|
:file-list="fileList"
|
||||||
|
:before-upload="beforeUpload"
|
||||||
|
accept="image/*"
|
||||||
|
class="upload-area"
|
||||||
|
>
|
||||||
|
<p class="ant-upload-drag-icon">
|
||||||
|
<inbox-outlined></inbox-outlined>
|
||||||
|
</p>
|
||||||
|
<p class="ant-upload-text">点击或拖拽图片到此区域上传</p>
|
||||||
|
<p class="ant-upload-hint">
|
||||||
|
支持单张图片上传,图片大小不超过5MB
|
||||||
|
</p>
|
||||||
|
</a-upload-dragger>
|
||||||
|
|
||||||
|
<!-- 上传状态提示 -->
|
||||||
|
<div v-if="uploadStatus" :class="['upload-status', uploadStatus.type]">
|
||||||
|
<loading-outlined v-if="uploadStatus.type === 'loading'" />
|
||||||
|
<check-circle-outlined v-if="uploadStatus.type === 'success'" />
|
||||||
|
<close-circle-outlined v-if="uploadStatus.type === 'error'" />
|
||||||
|
{{ uploadStatus.message }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 图片预览 -->
|
||||||
|
<div v-if="previewImage" class="preview-container">
|
||||||
|
<h4>图片预览:</h4>
|
||||||
|
<img :src="previewImage" class="preview-image" />
|
||||||
|
</div>
|
||||||
|
</a-modal>
|
||||||
|
|
||||||
|
<!-- 预览弹框 -->
|
||||||
|
<a-modal
|
||||||
|
v-model:visible="previewModalVisible"
|
||||||
|
:title="`轮播图预览 (ID: ${currentBannerId})`"
|
||||||
|
:footer="null"
|
||||||
|
width="80%"
|
||||||
|
>
|
||||||
|
<div class="preview-modal-content">
|
||||||
|
<img :src="currentBannerUrl" class="preview-modal-image" />
|
||||||
|
<div class="preview-actions">
|
||||||
|
<a-button type="danger" @click="handleDeleteBanner" :loading="deleteLoading">
|
||||||
|
<template #icon><delete-outlined /></template>
|
||||||
|
删除轮播图
|
||||||
|
</a-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a-modal>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref, reactive, onMounted } from 'vue';
|
||||||
|
import {
|
||||||
|
LeftCircleOutlined,
|
||||||
|
RightCircleOutlined,
|
||||||
|
InboxOutlined,
|
||||||
|
LoadingOutlined,
|
||||||
|
CheckCircleOutlined,
|
||||||
|
CloseCircleOutlined,
|
||||||
|
DeleteOutlined
|
||||||
|
} from '@ant-design/icons-vue';
|
||||||
|
import { message } from 'ant-design-vue';
|
||||||
|
import type { UploadChangeParam } from 'ant-design-vue';
|
||||||
|
import myAxios from "../../api/myAxios.ts";
|
||||||
|
|
||||||
|
// 轮播图数据
|
||||||
|
const banners = ref<string[]>([]);
|
||||||
|
// 弹框控制
|
||||||
|
const addModalVisible = ref(false);
|
||||||
|
const previewModalVisible = ref(false);
|
||||||
|
const confirmLoading = ref(false);
|
||||||
|
const deleteLoading = ref(false);
|
||||||
|
// 上传相关状态
|
||||||
|
const fileList = ref<any[]>([]);
|
||||||
|
const uploadData = reactive({ biz: 'avatar' });
|
||||||
|
const uploadStatus = ref<{ type: string; message: string } | null>(null);
|
||||||
|
// 上传后的图片标识
|
||||||
|
const uploadedImageId = ref('');
|
||||||
|
// 预览图片URL
|
||||||
|
const previewImage = ref('');
|
||||||
|
|
||||||
|
// 当前预览的轮播图信息
|
||||||
|
const currentBannerUrl = ref('');
|
||||||
|
const currentBannerId = ref('');
|
||||||
|
const currentBannerIndex = ref(-1);
|
||||||
|
|
||||||
|
// 获取上传URL和请求头
|
||||||
|
const uploadUrl = `${myAxios.defaults.baseURL}/file/upload`;
|
||||||
|
const uploadHeaders = {
|
||||||
|
Authorization: localStorage.getItem('token') || ''
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取轮播图完整URL
|
||||||
|
const getBannerUrl = (imageId: string) => {
|
||||||
|
return `https://www.chenxinzhi.top/file/download/${imageId}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 显示新增弹框
|
||||||
|
const showAddModal = () => {
|
||||||
|
addModalVisible.value = true;
|
||||||
|
fileList.value = [];
|
||||||
|
uploadStatus.value = null;
|
||||||
|
uploadedImageId.value = '';
|
||||||
|
previewImage.value = '';
|
||||||
|
};
|
||||||
|
|
||||||
|
// 关闭新增弹框
|
||||||
|
const handleAddCancel = () => {
|
||||||
|
addModalVisible.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 显示预览弹框
|
||||||
|
const showPreviewModal = (bannerId: string, index: number) => {
|
||||||
|
currentBannerId.value = bannerId;
|
||||||
|
currentBannerUrl.value = getBannerUrl(bannerId);
|
||||||
|
currentBannerIndex.value = index;
|
||||||
|
previewModalVisible.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 上传前校验
|
||||||
|
const beforeUpload = (file: File) => {
|
||||||
|
const isImage = file.type.startsWith('image/');
|
||||||
|
if (!isImage) {
|
||||||
|
message.error('只能上传图片文件!');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const isLt5M = file.size / 1024 / 1024 < 5;
|
||||||
|
if (!isLt5M) {
|
||||||
|
message.error('图片大小不能超过5MB!');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建预览URL
|
||||||
|
previewImage.value = URL.createObjectURL(file);
|
||||||
|
|
||||||
|
return isImage && isLt5M;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 处理上传变化
|
||||||
|
const handleUploadChange = (info: UploadChangeParam) => {
|
||||||
|
fileList.value = [...info.fileList];
|
||||||
|
|
||||||
|
if (info.file.status === 'uploading') {
|
||||||
|
uploadStatus.value = { type: 'loading', message: '图片上传中...' };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info.file.status === 'done') {
|
||||||
|
uploadStatus.value = { type: 'success', message: '图片上传成功!' };
|
||||||
|
|
||||||
|
// 保存上传后的图片标识
|
||||||
|
if (info.file.response && info.file.response.code === 1) {
|
||||||
|
uploadedImageId.value = info.file.response.data;
|
||||||
|
console.log('上传成功,图片标识:', uploadedImageId.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info.file.status === 'error') {
|
||||||
|
uploadStatus.value = {
|
||||||
|
type: 'error',
|
||||||
|
message: `上传失败: ${info.file.response?.message || '未知错误'}`
|
||||||
|
};
|
||||||
|
previewImage.value = '';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 处理新增确认
|
||||||
|
const handleAddOk = async () => {
|
||||||
|
if (!uploadedImageId.value) {
|
||||||
|
message.error('请先上传图片');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
confirmLoading.value = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response:any = await myAxios.post('/banner/add', {
|
||||||
|
templateString: uploadedImageId.value
|
||||||
|
}, {
|
||||||
|
headers: {
|
||||||
|
Authorization: localStorage.getItem('token') || '',
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.code === 1) {
|
||||||
|
message.success('轮播图添加成功!');
|
||||||
|
// 刷新轮播图列表
|
||||||
|
await fetchBanners();
|
||||||
|
addModalVisible.value = false;
|
||||||
|
} else {
|
||||||
|
message.error(`添加失败: ${response.data?.message || '未知错误'}`);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('添加轮播图失败:', error);
|
||||||
|
message.error('添加轮播图失败,请稍后重试');
|
||||||
|
} finally {
|
||||||
|
confirmLoading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 处理删除操作
|
||||||
|
const handleDeleteBanner = async () => {
|
||||||
|
if (currentBannerIndex.value === -1) {
|
||||||
|
message.error('未选择要删除的轮播图');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteLoading.value = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
const params = new URLSearchParams();
|
||||||
|
params.append('index', currentBannerIndex.value.toString());
|
||||||
|
|
||||||
|
const response:any = await myAxios.post('/banner/del', params, {
|
||||||
|
headers: {
|
||||||
|
Authorization: localStorage.getItem('token') || '',
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('删除响应:', response)
|
||||||
|
|
||||||
|
if (response.code === 1) {
|
||||||
|
message.success('轮播图删除成功!');
|
||||||
|
|
||||||
|
await fetchBanners();
|
||||||
|
previewModalVisible.value = false;
|
||||||
|
} else {
|
||||||
|
message.error(`删除失败: ${response.data?.message || '未知错误'}`);
|
||||||
|
}
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error('删除轮播图失败:', error);
|
||||||
|
|
||||||
|
|
||||||
|
let errorMsg = '删除轮播图失败,请稍后重试';
|
||||||
|
if (error.response) {
|
||||||
|
|
||||||
|
const data = error.response.data;
|
||||||
|
if (data && data.message) {
|
||||||
|
errorMsg = `删除失败: ${data.message}`;
|
||||||
|
} else if (data && data.error) {
|
||||||
|
errorMsg = `删除失败: ${data.error}`;
|
||||||
|
} else if (error.response.status === 400) {
|
||||||
|
errorMsg = '请求参数错误,请检查参数格式';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message.error(errorMsg);
|
||||||
|
} finally {
|
||||||
|
deleteLoading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取轮播图列表
|
||||||
|
const fetchBanners = async () => {
|
||||||
|
try {
|
||||||
|
const response:any = await myAxios.get('/banner/web/list', {
|
||||||
|
headers: {
|
||||||
|
Authorization: localStorage.getItem('token') || ''
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('轮播图列表响应:', response);
|
||||||
|
|
||||||
|
if (response.code === 1 && Array.isArray(response.data)) {
|
||||||
|
banners.value = response.data;
|
||||||
|
console.log('轮播图数据:', banners.value);
|
||||||
|
} else {
|
||||||
|
console.warn('轮播图列表格式错误:', response);
|
||||||
|
message.warning('获取轮播图数据格式错误');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取轮播图失败:', error);
|
||||||
|
message.error('获取轮播图失败');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 初始化时获取轮播图
|
||||||
|
onMounted(() => {
|
||||||
|
fetchBanners();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
/* 按钮样式 */
|
||||||
|
.add-button-container {
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-button {
|
||||||
|
background-color: #ff9f00;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
padding: 10px 20px;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: bold;
|
||||||
|
border-radius: 25px;
|
||||||
|
transition: background-color 0.3s;
|
||||||
|
cursor: pointer;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-button:hover {
|
||||||
|
background-color: #ff7f00;
|
||||||
|
}
|
||||||
|
|
||||||
|
.plus-icon {
|
||||||
|
font-size: 20px;
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 轮播图样式 */
|
||||||
|
.carouselContainer {
|
||||||
|
width: 80%;
|
||||||
|
margin: 0 auto;
|
||||||
|
aspect-ratio: 2/1;
|
||||||
|
}
|
||||||
|
:deep(.slick-slide) {
|
||||||
|
aspect-ratio: 2/1;
|
||||||
|
text-align: center;
|
||||||
|
line-height: normal;
|
||||||
|
background: #364d79;
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.banner-image {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
display: block;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: transform 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1200px) {
|
||||||
|
.carouselContainer {
|
||||||
|
width: 60%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.carouselContainer {
|
||||||
|
width: 80%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 480px) {
|
||||||
|
.carouselContainer {
|
||||||
|
width: 95%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.banner-image:hover {
|
||||||
|
transform: scale(1.02);
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.slick-arrow.custom-slick-arrow) {
|
||||||
|
width: 35px;
|
||||||
|
height: 35px;
|
||||||
|
font-size: 35px;
|
||||||
|
color: #fff;
|
||||||
|
background-color: rgba(31, 45, 61, 0.11);
|
||||||
|
transition: ease all 0.3s;
|
||||||
|
opacity: 0.5;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.slick-arrow.custom-slick-arrow:before) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.slick-arrow.custom-slick-arrow:hover) {
|
||||||
|
color: #fff;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 上传区域样式 */
|
||||||
|
.upload-area {
|
||||||
|
padding: 30px;
|
||||||
|
border-radius: 8px;
|
||||||
|
transition: border-color 0.3s;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-area:hover {
|
||||||
|
border-color: #1890ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-status {
|
||||||
|
margin-top: 15px;
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 4px;
|
||||||
|
text-align: center;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-status.loading {
|
||||||
|
background: #e6f7ff;
|
||||||
|
border: 1px solid #91d5ff;
|
||||||
|
color: #1890ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-status.success {
|
||||||
|
background: #f6ffed;
|
||||||
|
border: 1px solid #b7eb8f;
|
||||||
|
color: #52c41a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-status.error {
|
||||||
|
background: #fff2f0;
|
||||||
|
border: 1px solid #ffccc7;
|
||||||
|
color: #ff4d4f;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 图片预览样式 */
|
||||||
|
.preview-container {
|
||||||
|
margin-top: 20px;
|
||||||
|
padding: 15px;
|
||||||
|
border: 1px solid #e8e8e8;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: #fafafa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-container h4 {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-image {
|
||||||
|
max-width: 100%;
|
||||||
|
max-height: 200px;
|
||||||
|
display: block;
|
||||||
|
margin: 0 auto;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 预览弹框样式 */
|
||||||
|
.preview-modal-content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-modal-image {
|
||||||
|
max-width: 100%;
|
||||||
|
max-height: 60vh;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-actions {
|
||||||
|
margin-top: 20px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
</style>
|
@ -70,7 +70,7 @@ const editorConfig = {
|
|||||||
})
|
})
|
||||||
|
|
||||||
if (res.code === 1) {
|
if (res.code === 1) {
|
||||||
// 拼接完整 URL 地址再插入到富文本中
|
|
||||||
const imageUrl = 'https://www.chenxinzhi.top/file/download/' + res.data
|
const imageUrl = 'https://www.chenxinzhi.top/file/download/' + res.data
|
||||||
insertFn(imageUrl)
|
insertFn(imageUrl)
|
||||||
} else {
|
} else {
|
||||||
@ -119,6 +119,20 @@ onBeforeUnmount(() => {
|
|||||||
editorRef.value.destroy()
|
editorRef.value.destroy()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
// 添加清空方法
|
||||||
|
const clearContent = () => {
|
||||||
|
if (editorRef.value) {
|
||||||
|
editorRef.value.setHtml('');
|
||||||
|
valueHtml.value = '';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 暴露清空方法给父组件
|
||||||
|
defineExpose({
|
||||||
|
clearContent
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
291
src/view/course/coursePurchaseNotice.vue
Normal file
291
src/view/course/coursePurchaseNotice.vue
Normal file
@ -0,0 +1,291 @@
|
|||||||
|
<template>
|
||||||
|
<form @submit.prevent="handleSubmit" class="modern-form">
|
||||||
|
<h2 class="form-title">课程购买须知</h2>
|
||||||
|
|
||||||
|
<!-- 富文本编辑器 -->
|
||||||
|
<div class="rich-text-container">
|
||||||
|
<div class="rich-text-group">
|
||||||
|
<span class="label-text">购买详情</span>
|
||||||
|
<RichTextEditor
|
||||||
|
ref="editorRef"
|
||||||
|
:value="editorContent"
|
||||||
|
:disable="false"
|
||||||
|
@content-change="handleEditorChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 错误提示 -->
|
||||||
|
<div v-if="errorMessage" class="error-message">
|
||||||
|
{{ errorMessage }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button type="submit" class="submit-button">
|
||||||
|
<span>保存须知</span>
|
||||||
|
<div class="button-sparkles"></div>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
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 NoticeForm {
|
||||||
|
templateString: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const formData = reactive<NoticeForm>({
|
||||||
|
templateString: ''
|
||||||
|
});
|
||||||
|
|
||||||
|
const editorRef = ref<InstanceType<typeof RichTextEditor> | null>(null);
|
||||||
|
const errorMessage = ref('');
|
||||||
|
const editorContent = ref('');
|
||||||
|
|
||||||
|
// 增强Base64编码方法
|
||||||
|
const encode64 = (text: string): string => {
|
||||||
|
if (!text.trim()) return '';
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
return btoa(unescape(encodeURIComponent(text)));
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Base64编码失败:', error);
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
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);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
try {
|
||||||
|
await nextTick();
|
||||||
|
const storedToken = localStorage.getItem('token');
|
||||||
|
|
||||||
|
if (isContentEmpty(formData.templateString)) {
|
||||||
|
errorMessage.value = '须知内容不能为空';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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/courseDesc`,
|
||||||
|
payload,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Authorization': storedToken
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
console.log(res)
|
||||||
|
if (res.code === 1) {
|
||||||
|
alert('须知保存成功!');
|
||||||
|
|
||||||
|
if (editorRef.value) {
|
||||||
|
editorRef.value.clearContent(); // 调用组件的清空方法
|
||||||
|
}
|
||||||
|
formData.templateString = '';
|
||||||
|
editorContent.value = '';
|
||||||
|
errorMessage.value = '';
|
||||||
|
} else {
|
||||||
|
errorMessage.value = `保存失败:${res.message || '服务器错误'} (${res.code})`;
|
||||||
|
}
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error('请求失败:', error);
|
||||||
|
|
||||||
|
let detailedError = '提交失败:';
|
||||||
|
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 = '须知内容不能为空';
|
||||||
|
}
|
||||||
|
} 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;
|
||||||
|
padding: 2.5rem;
|
||||||
|
background: white;
|
||||||
|
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 {
|
||||||
|
text-align: center;
|
||||||
|
font-size: 1.8rem;
|
||||||
|
color: #2c3e50;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label-text {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 0.6rem;
|
||||||
|
padding: 10px;
|
||||||
|
color: #6366f1;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
.submit-button {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 100%;
|
||||||
|
padding: 1rem;
|
||||||
|
margin-top: 1.5rem;
|
||||||
|
background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
.submit-button:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 8px 20px rgba(99, 102, 241, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-sparkles {
|
||||||
|
position: absolute;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
background: rgba(255,255,255,0.4);
|
||||||
|
border-radius: 50%;
|
||||||
|
animation: sparkle 1.5s infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes sparkle {
|
||||||
|
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 {
|
||||||
|
margin-top: 1.5rem;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rich-text-group {
|
||||||
|
width: 100%;
|
||||||
|
border: 2px solid #e2e8f0;
|
||||||
|
border-radius: 0.75rem;
|
||||||
|
overflow: hidden;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
height: 420px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rich-text-group:focus-within {
|
||||||
|
border-color: #6366f1;
|
||||||
|
box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.editor-container) {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.toolbar) {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.w-e-text-container) {
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto !important;
|
||||||
|
min-height: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.w-e-text) {
|
||||||
|
min-height: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.modern-form {
|
||||||
|
padding: 1.5rem;
|
||||||
|
margin: 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
@ -33,7 +33,7 @@ import RichTextEditor from '../components/RichTextEditor.vue';
|
|||||||
import '@vueup/vue-quill/dist/vue-quill.snow.css';
|
import '@vueup/vue-quill/dist/vue-quill.snow.css';
|
||||||
import myAxios from "../../api/myAxios.ts";
|
import myAxios from "../../api/myAxios.ts";
|
||||||
|
|
||||||
// 定义表单数据结构
|
|
||||||
interface NoticeForm {
|
interface NoticeForm {
|
||||||
templateString: string;
|
templateString: string;
|
||||||
}
|
}
|
||||||
@ -44,14 +44,14 @@ const formData = reactive<NoticeForm>({
|
|||||||
|
|
||||||
const editorRef = ref<InstanceType<typeof RichTextEditor> | null>(null);
|
const editorRef = ref<InstanceType<typeof RichTextEditor> | null>(null);
|
||||||
const errorMessage = ref('');
|
const errorMessage = ref('');
|
||||||
const editorContent = ref(''); // 单独管理编辑器内容
|
const editorContent = ref('');
|
||||||
|
|
||||||
// 增强Base64编码方法
|
// 增强Base64编码方法
|
||||||
const encode64 = (text: string): string => {
|
const encode64 = (text: string): string => {
|
||||||
if (!text.trim()) return '';
|
if (!text.trim()) return '';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 使用更健壮的Base64编码
|
|
||||||
return btoa(unescape(encodeURIComponent(text)));
|
return btoa(unescape(encodeURIComponent(text)));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Base64编码失败:', error);
|
console.error('Base64编码失败:', error);
|
||||||
@ -59,25 +59,23 @@ const encode64 = (text: string): string => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 处理编辑器内容变化
|
|
||||||
const handleEditorChange = (html: string) => {
|
const handleEditorChange = (html: string) => {
|
||||||
formData.templateString = html;
|
formData.templateString = html;
|
||||||
editorContent.value = html;
|
editorContent.value = html;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 检查内容是否为空(去除所有HTML标签)
|
|
||||||
const isContentEmpty = (html: string): boolean => {
|
const isContentEmpty = (html: string): boolean => {
|
||||||
if (!html) return true;
|
if (!html) return true;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 创建临时元素解析HTML
|
|
||||||
const tempDiv = document.createElement('div');
|
const tempDiv = document.createElement('div');
|
||||||
tempDiv.innerHTML = html;
|
tempDiv.innerHTML = html;
|
||||||
|
|
||||||
// 获取纯文本内容
|
|
||||||
const plainText = tempDiv.textContent || '';
|
const plainText = tempDiv.textContent || '';
|
||||||
|
|
||||||
// 检查是否有实际内容
|
|
||||||
return plainText.trim() === '';
|
return plainText.trim() === '';
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('内容解析失败:', error);
|
console.error('内容解析失败:', error);
|
||||||
@ -90,7 +88,6 @@ const handleSubmit = async () => {
|
|||||||
await nextTick();
|
await nextTick();
|
||||||
const storedToken = localStorage.getItem('token');
|
const storedToken = localStorage.getItem('token');
|
||||||
|
|
||||||
// 检查内容是否为空
|
|
||||||
if (isContentEmpty(formData.templateString)) {
|
if (isContentEmpty(formData.templateString)) {
|
||||||
errorMessage.value = '须知内容不能为空';
|
errorMessage.value = '须知内容不能为空';
|
||||||
return;
|
return;
|
||||||
@ -98,7 +95,7 @@ const handleSubmit = async () => {
|
|||||||
|
|
||||||
const encryptedContent = encode64(formData.templateString);
|
const encryptedContent = encode64(formData.templateString);
|
||||||
|
|
||||||
// 检查加密结果是否有效
|
|
||||||
if (!encryptedContent) {
|
if (!encryptedContent) {
|
||||||
errorMessage.value = '内容加密失败,请检查输入内容';
|
errorMessage.value = '内容加密失败,请检查输入内容';
|
||||||
return;
|
return;
|
||||||
@ -124,6 +121,10 @@ const handleSubmit = async () => {
|
|||||||
console.log(res)
|
console.log(res)
|
||||||
if (res.code === 1) {
|
if (res.code === 1) {
|
||||||
alert('须知保存成功!');
|
alert('须知保存成功!');
|
||||||
|
|
||||||
|
if (editorRef.value) {
|
||||||
|
editorRef.value.clearContent();
|
||||||
|
}
|
||||||
formData.templateString = '';
|
formData.templateString = '';
|
||||||
editorContent.value = '';
|
editorContent.value = '';
|
||||||
errorMessage.value = '';
|
errorMessage.value = '';
|
||||||
@ -135,13 +136,13 @@ const handleSubmit = async () => {
|
|||||||
|
|
||||||
let detailedError = '提交失败:';
|
let detailedError = '提交失败:';
|
||||||
if (error.response) {
|
if (error.response) {
|
||||||
// 优先显示后端返回的错误信息
|
|
||||||
const serverMessage = error.response.data?.message ||
|
const serverMessage = error.response.data?.message ||
|
||||||
error.response.data?.error ||
|
error.response.data?.error ||
|
||||||
'无详细错误信息';
|
'无详细错误信息';
|
||||||
detailedError += `服务器响应错误 (${error.response.status}): ${serverMessage}`;
|
detailedError += `服务器响应错误 (${error.response.status}): ${serverMessage}`;
|
||||||
|
|
||||||
// 特殊处理40000错误(空字符串错误)
|
|
||||||
if (error.response.data?.code === 40000) {
|
if (error.response.data?.code === 40000) {
|
||||||
detailedError = '须知内容不能为空';
|
detailedError = '须知内容不能为空';
|
||||||
}
|
}
|
||||||
@ -162,7 +163,7 @@ onMounted(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
/* 精简后的样式 */
|
|
||||||
.modern-form {
|
.modern-form {
|
||||||
max-width: 90%;
|
max-width: 90%;
|
||||||
margin: 2rem auto;
|
margin: 2rem auto;
|
||||||
@ -248,12 +249,13 @@ onMounted(() => {
|
|||||||
|
|
||||||
.rich-text-group {
|
.rich-text-group {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
|
||||||
border: 2px solid #e2e8f0;
|
border: 2px solid #e2e8f0;
|
||||||
border-radius: 0.75rem;
|
border-radius: 0.75rem;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
max-height: 420px;
|
height: 420px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.rich-text-group:focus-within {
|
.rich-text-group:focus-within {
|
||||||
@ -261,6 +263,27 @@ onMounted(() => {
|
|||||||
box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.1);
|
box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:deep(.editor-container) {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.toolbar) {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.w-e-text-container) {
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto !important;
|
||||||
|
min-height: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.w-e-text) {
|
||||||
|
min-height: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.modern-form {
|
.modern-form {
|
||||||
padding: 1.5rem;
|
padding: 1.5rem;
|
||||||
|
@ -1 +1 @@
|
|||||||
{"root":["./src/main.ts","./src/vite-env.d.ts","./src/api/imageurl.ts","./src/api/myaxios.ts","./src/router/index.ts","./src/router/routes.ts","./src/store/index.ts","./src/store/userstore.ts","./src/types/wangeditor.d.ts","./src/app.vue","./src/layout/managelayout.vue","./src/layout/manage/manageheader.vue","./src/layout/manage/managesidebar.vue","./src/view/index.vue","./src/view/login.vue","./src/view/test.vue","./src/view/components/richtexteditor.vue","./src/view/course/addcourse.vue","./src/view/course/chapterdetail.vue","./src/view/course/coursedetail.vue","./src/view/course/coursemanagement.vue","./src/view/course/courseorder.vue","./src/view/employeeapplication/applicationinstructions.vue","./src/view/employeeapplication/employeeapplication.vue","./src/view/employeeapplication/employeedetail.vue","./src/view/performance/customerdetail.vue","./src/view/performance/customerorder.vue","./src/view/performance/employeeperformaince.vue","./src/view/performance/employeeperformanceranking.vue","./src/view/performance/performancemanagement.vue","./src/view/performance/supervisorperformanceranking.vue","./src/view/project/addproject.vue","./src/view/project/addprojectnotice.vue","./src/view/project/moneydetail.vue","./src/view/project/noticedetail.vue","./src/view/project/project.vue","./src/view/project/projectdetail.vue","./src/view/project/projectnotice.vue","./src/view/project/promotioncode.vue","./src/view/settlement/applicationrecord.vue","./src/view/settlement/moneyrecord.vue","./src/view/settlement/withdrawalapplicationrecord.vue","./src/view/userlist/adminlist.vue","./src/view/userlist/managerinformation.vue","./src/view/userlist/stafflist.vue","./src/view/userlist/supervisorlist.vue","./src/view/userlist/userlist.vue","./src/view/work/workdetail.vue","./src/view/work/worklist.vue"],"version":"5.6.3"}
|
{"root":["./src/main.ts","./src/vite-env.d.ts","./src/api/imageurl.ts","./src/api/myaxios.ts","./src/router/index.ts","./src/router/routes.ts","./src/store/index.ts","./src/store/userstore.ts","./src/types/wangeditor.d.ts","./src/app.vue","./src/layout/managelayout.vue","./src/layout/manage/manageheader.vue","./src/layout/manage/managesidebar.vue","./src/view/index.vue","./src/view/login.vue","./src/view/test.vue","./src/view/carousel/carouselmanagement.vue","./src/view/components/richtexteditor.vue","./src/view/course/addcourse.vue","./src/view/course/chapterdetail.vue","./src/view/course/coursedetail.vue","./src/view/course/coursemanagement.vue","./src/view/course/courseorder.vue","./src/view/course/coursepurchasenotice.vue","./src/view/employeeapplication/applicationinstructions.vue","./src/view/employeeapplication/employeeapplication.vue","./src/view/employeeapplication/employeedetail.vue","./src/view/performance/customerdetail.vue","./src/view/performance/customerorder.vue","./src/view/performance/employeeperformaince.vue","./src/view/performance/employeeperformanceranking.vue","./src/view/performance/performancemanagement.vue","./src/view/performance/supervisorperformanceranking.vue","./src/view/project/addproject.vue","./src/view/project/addprojectnotice.vue","./src/view/project/moneydetail.vue","./src/view/project/noticedetail.vue","./src/view/project/project.vue","./src/view/project/projectdetail.vue","./src/view/project/projectnotice.vue","./src/view/project/promotioncode.vue","./src/view/settlement/applicationrecord.vue","./src/view/settlement/moneyrecord.vue","./src/view/settlement/withdrawalapplicationrecord.vue","./src/view/userlist/adminlist.vue","./src/view/userlist/managerinformation.vue","./src/view/userlist/stafflist.vue","./src/view/userlist/supervisorlist.vue","./src/view/userlist/userlist.vue","./src/view/work/workdetail.vue","./src/view/work/worklist.vue"],"version":"5.6.3"}
|
Reference in New Issue
Block a user