课程模块部分bug修复

This commit is contained in:
2025-07-02 09:43:17 +08:00
parent 927b200cf2
commit 06debc411c
24 changed files with 418 additions and 456 deletions

BIN
dist.zip Normal file

Binary file not shown.

View File

@ -6,7 +6,8 @@ import router from "../router";
const myAxios = axios.create({
withCredentials: true,
baseURL:'http://localhost:9091'
// baseURL:'http://localhost:9091'
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/'

View File

@ -76,14 +76,10 @@ const checkLoginStatus = () => {
// 检查store中的登录状态
if (store.loginUser.userRole === "notLogin") {
console.log("未检测到登录状态,跳转到登录页");
// 使用replace替换当前路由禁止返回
router.replace({ path: '/' });
}
};
// 生命周期钩子:在组件挂载前检查登录状态
onBeforeMount(() => {
checkLoginStatus();
});
@ -103,7 +99,6 @@ const logout = async () => {
try {
const token = localStorage.getItem('token');
// 严格遵循接口文档路径大小写
const res: any = await myAxios.get(
"/userInfo/logout",
{

View File

@ -69,19 +69,18 @@ import {useRoute, useRouter} from "vue-router";
const route = useRoute();
const router = useRouter();
// 选中侧边栏
const selectedKeys = ref<string[]>(['/userList']);
onMounted(() => {
setSelectedKey()
})
// 根据路由设置当前选中侧边栏
const setSelectedKey = () => {
selectedKeys.value = [route.path];
}
// 路由跳转
const handleClick = (item: any) => {
router.push(item.key)
}
@ -89,7 +88,7 @@ const handleClick = (item: any) => {
</script>
<style scoped>
/* 全局菜单项样式 */
:deep(.ant-menu-item),
:deep(.ant-menu-submenu-title) {
color: rgba(0, 0, 0, 0.85) !important; /* 未选中黑色字体 */
@ -97,31 +96,31 @@ const handleClick = (item: any) => {
transition: all 0.3s ease;
}
/* 选中项样式 */
:deep(.ant-menu-item-selected) {
background-color: #ffa940 !important;
color: white !important;
font-weight: 600 !important;
}
/* 鼠标悬停样式 */
:deep(.ant-menu-item:hover),
:deep(.ant-menu-submenu-title:hover) {
background-color: rgba(255, 169, 64, 0.1) !important;
}
/* 子菜单箭头颜色 */
:deep(.ant-menu-submenu-arrow::before),
:deep(.ant-menu-submenu-arrow::after) {
background: rgba(0, 0, 0, 0.65) !important;
}
/* 子菜单展开时标题样式 */
:deep(.ant-menu-submenu-selected .ant-menu-submenu-title) {
color: rgba(0, 0, 0, 0.95) !important;
}
/* 折叠状态下选中样式 */
:deep(.ant-menu-inline-collapsed .ant-menu-item-selected) {
background-color: #ffa940 !important;
}

View File

@ -103,7 +103,7 @@ const onLogin = async () => {
"/userInfo/login",
{
userAccount: userAccount.value,
userPassword: userPassword.value // 发送加密后的密码
userPassword: userPassword.value
},
{
headers: {

View File

@ -6,43 +6,5 @@
</template>
<style scoped>
.search-box {
margin-bottom: 20px;
padding: 16px;
background: #fff;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
.error-alert {
padding: 1rem;
background: #ffe3e3;
color: #ff4444;
border-radius: 6px;
display: flex;
align-items: center;
gap: 0.8rem;
margin-top: 1rem;
}
.error-icon {
display: inline-block;
width: 1.2rem;
height: 1.2rem;
border-radius: 50%;
background: #ff4444;
color: white;
text-align: center;
line-height: 1.2rem;
font-weight: bold;
}
:deep(.ant-table-thead > tr > th) {
background-color: #fafafa !important;
font-weight: 600;
}
:deep(.ant-table-row:hover) {
background-color: #fafafa !important;
}
</style>

View File

@ -68,16 +68,15 @@
class="select-field"
required
>
<option value="" disabled>请选择类型</option>
<option value="自媒体">自媒体</option>
<option value="">请选择课程类型</option>
<option value="考公考研">考公考研</option>
<option value="自媒体">自媒体</option>
<option value="财经">财经</option>
</select>
<div class="select-arrow"></div>
</div>
</label>
</div>
<div class="input-group">
<label class="input-label">
<span class="label-text">课程图片</span>
@ -108,6 +107,7 @@
class="input-field"
required
placeholder="请输入原价"
@input="validatePrices"
>
</label>
</div>
@ -122,6 +122,7 @@
class="input-field"
required
placeholder="请输入折扣价"
@input="validatePrices"
>
</label>
</div>
@ -134,11 +135,12 @@
<input
v-model.number="formData.firstLevelRate"
type="number"
min="0"
max="100"
min="1"
max="99"
class="input-field"
required
placeholder="0-100"
placeholder="1-99"
@input="validateRates"
>
</label>
</div>
@ -149,11 +151,12 @@
<input
v-model.number="formData.secondLevelRate"
type="number"
min="0"
max="100"
min="1"
max="99"
class="input-field"
required
placeholder="0-100"
placeholder="1-99"
@input="validateRates"
>
</label>
</div>
@ -161,6 +164,7 @@
</div>
</div>
<!-- 富文本编辑器区域 - 修改为50%宽度 -->
<div class="rich-text-container">
<div class="rich-text-columns">
<div class="rich-text-group">
@ -168,7 +172,7 @@
<RichTextEditor
v-model="formData.detail"
:disable="false"
@content-change="(html) => formData.detail = html"
@content-change="(html:any) => formData.detail = html"
/>
</div>
@ -177,7 +181,7 @@
<RichTextEditor
v-model="formData.promoCodeDesc"
:disable="false"
@content-change="(html) => formData.promoCodeDesc = html"
@content-change="(html:any) => formData.promoCodeDesc = html"
/>
</div>
</div>
@ -230,7 +234,7 @@ const handleFileUpload = async (event: Event) => {
'Authorization': storedToken
}
});
console.log(res)
if (res.code === 1) {
formData.image = res.data;
fileName.value = file.name;
@ -280,7 +284,41 @@ const encryptValue = (value: any, key: string): any => {
}
};
// 验证函数
const validatePrices = () => {
if (formData.discountPrice >= formData.originPrice) {
return false;
}
return true;
};
const validateRates = () => {
if (formData.secondLevelRate >= formData.firstLevelRate) {
return false;
}
return true;
};
const handleSubmit = async () => {
// 验证价格和佣金比例
const isPriceValid = validatePrices();
const isRateValid = validateRates();
let errorMessage = '';
if (!isPriceValid) {
errorMessage += '折扣价必须小于原价\n';
}
if (!isRateValid) {
errorMessage += '二级佣金比例必须小于一级佣金比例\n';
}
if (errorMessage) {
alert(errorMessage);
return;
}
try {
if (!validateForm()) {
alert('请填写所有必填字段');
@ -346,6 +384,7 @@ const validateForm = (): boolean => {
};
</script>
<style scoped>
.modern-form {
max-width: 90%;
@ -458,6 +497,73 @@ const validateForm = (): boolean => {
.select-wrapper {
position: relative;
border: 2px solid #e2e8f0;
border-radius: 0.75rem;
transition: all 0.3s ease;
background: white;
}
.select-wrapper:hover {
border-color: #cbd5e1;
}
.select-wrapper:focus-within {
border-color: #6366f1;
box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.1);
}
.select-field {
appearance: none;
width: 100%;
padding: 0.8rem 1.2rem;
border: none;
background: transparent;
font-size: 1rem;
color: #4a5568;
cursor: pointer;
outline: none;
border-radius: 0.75rem;
height: 44px; /* 与其他输入框高度一致 */
}
.select-field:focus {
outline: none;
}
.select-arrow {
position: absolute;
right: 1rem;
top: 50%;
transform: translateY(-50%);
color: #94a3b8;
pointer-events: none;
transition: transform 0.3s ease;
}
.select-wrapper:focus-within .select-arrow {
transform: translateY(-50%) rotate(180deg);
}
/* 下拉选项样式 */
.select-field option {
padding: 8px 12px;
background: white;
color: #4a5568;
}
.select-field option:hover {
background: #f1f5f9;
}
.select-field option:checked {
background: #6366f1;
color: white;
}
/* 当选择框有值时改变样式 */
.select-field:not([value=""]) {
color: #2c3e50;
font-weight: 500;
}
.select-field {
@ -477,11 +583,6 @@ const validateForm = (): boolean => {
pointer-events: none;
}
.textarea-field {
min-height: 100px;
resize: vertical;
}
.submit-button {
display: flex;
align-items: center;
@ -539,6 +640,7 @@ const validateForm = (): boolean => {
.rich-text-container {
margin-top: 1.5rem;
width: 100%;
}
.rich-text-columns {
@ -561,6 +663,12 @@ const validateForm = (): boolean => {
flex-grow: 1;
}
@media (max-width: 768px) {
.rich-text-columns {
grid-template-columns: 1fr;
}
}
.rich-text-group:focus-within {
border-color: #6366f1;
box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.1);
@ -742,4 +850,5 @@ const validateForm = (): boolean => {
margin-left: 20px;
margin-bottom: 10px;
}
</style>

View File

@ -108,13 +108,7 @@
>
编辑
</a-button>
<a-button
size="small"
type="link"
@click="previewVideo(record.videoView)"
>
预览
</a-button>
</a-space>
</template>
</template>
@ -714,7 +708,6 @@ const handleEditChapter = async () => {
const storedToken = localStorage.getItem('token');
if (!storedToken) throw new Error('未找到登录信息');
// 准备更新数据,修正字段映射问题
const updateData = {
id: editForm.value.id,
name: editForm.value.name,
@ -748,24 +741,15 @@ const handleEditChapter = async () => {
};
// 预览视频
const previewVideo = (videoView: string) => {
previewVideoUrl.value = downLoadImage + videoView;
videoPreviewVisible.value = true;
};
onMounted(() => {
// 从路由参数获取课程ID
courseId.value = route.query.id ? String(route.query.id) : "";
if (courseId.value) {
// 设置章节查询参数中的课程ID
searchParams.value.courseId = courseId.value;
// 获取课程详细信息
getCourseDetail();
// 获取章节列表
getChapterList();
} else {
message.error("未获取到课程ID");

View File

@ -121,7 +121,6 @@ const handleFileUpload = async (event: Event) => {
message.error('文件上传失败');
previewImage.value = '';
} finally {
// 重置文件输入,允许重复上传同一文件
if (input) input.value = '';
}
};
@ -250,7 +249,7 @@ const updateCourse = () => {
}
// 创建编辑数据的深拷贝副本
editData.value = JSON.parse(JSON.stringify(courseData.value));
previewImage.value = ''; // 进入编辑模式时重置预览图
previewImage.value = '';
isEditing.value = true;
};

View File

@ -92,15 +92,18 @@
{{ record.secondLevelRate }}%
</template>
<template v-if="column.key === 'isShelves'">
<a-tag :color="record.isShelves ? 'green' : 'red'">
{{ record.isShelves ? '已上架' : '已下架' }}
</a-tag>
</template>
<!-- 操作列 -->
<template v-if="column.key === 'action'">
<a-space :size="8">
<a-button
size="small"
:type="record.isShelves ? 'danger' : 'primary'"
@click="toggleShelves(record)"
>
{{ record.isShelves ? '下架' : '上架' }}
</a-button>
<a-button
size="small"
danger
@ -242,21 +245,44 @@ const columns = [
sorter: true,
sortDirections: ['ascend', 'descend']
},
{
title: '是否上架',
dataIndex: 'isShelves',
key: 'isShelves',
width: 65,
align: 'center'
},
{
title: '操作',
key: 'action',
fixed: 'right',
width: 75,
width: 105,
align: 'center'
}
];
// 添加上下架切换方法
const toggleShelves = async (record: Course) => {
Modal.confirm({
title: '确认操作',
content: `确定要${record.isShelves ? '下架' : '上架'}该课程吗?`,
okText: '确认',
cancelText: '取消',
onOk: async () => {
try {
const storedToken = localStorage.getItem('token');
const res: any = await myAxios.post(
"/course/isShelves",
{ id: record.id }, // 按照接口要求传递课程ID
{ headers: { Authorization: storedToken } }
);
if (res.code === 1) {
message.success(`${record.isShelves ? '下架' : '上架'}成功`);
// 刷新数据
await getCourseList();
} else {
message.error(res.message || '操作失败');
}
} catch (error) {
console.error('操作失败:', error);
message.error(`${record.isShelves ? '下架' : '上架'}操作失败`);
}
}
});
};
// 课程名称搜索方法
const handleCourseSearch = async () => {
@ -277,7 +303,7 @@ const getCourseList = async () => {
const res: any = await myAxios.post("/course/page", searchParams.value,
{ headers: { Authorization: storedToken } }
);
console.log(res)
if (res.code === 1 && res.data && Array.isArray(res.data.records)) {
tableData.value = res.data.records;
// 同步总条数到分页组件
@ -313,17 +339,13 @@ const handleTableChange = (pag: any, _filters: any, sorter: any) => {
// 处理排序
if (sorter && sorter.field) {
// 获取排序字段使用列的key而不是dataIndex
const sortField = sorter.field;
// 获取排序方向ascend/descend
const sortOrder = sorter.order ? sorter.order : '';
// 更新搜索参数中的排序字段和排序方向
searchParams.value.sortField = sortField;
searchParams.value.sortOrder = sortOrder;
} else {
// 如果没有排序信息,重置为默认排序
searchParams.value.sortField = "id";
searchParams.value.sortOrder = "ascend";
}
@ -465,7 +487,6 @@ const chapterDetails = (id: string) => {
</script>
<style scoped>
/* 原有样式保持不变 */
.search-box {
margin-bottom: 10px;
}

View File

@ -102,7 +102,6 @@ import { onMounted, ref } from "vue";
import myAxios from "../../api/myAxios.ts";
import { message, Modal } from "ant-design-vue";
import {downLoadImage} from "../../api/ImageUrl.ts";
import router from "../../router";
const loading = ref(false);
const selectedRowKeys = ref<number[]>([]); // 存储选中的行ID
@ -285,7 +284,6 @@ const pagination = ref({
pageSizeOptions: ['10', '20', '50', '100']
});
// 处理分页和排序变化
// 处理分页和排序变化
const handleTableChange = (pag: any, _filters: any, sorter: any) => {
searchParams.value.current = pag.current;
@ -293,17 +291,14 @@ const handleTableChange = (pag: any, _filters: any, sorter: any) => {
// 处理排序
if (sorter && sorter.field) {
// 获取排序字段使用列的key而不是dataIndex
const sortField = sorter.field;
// 获取排序方向ascend/descend
const sortOrder = sorter.order ? sorter.order : '';
// 更新搜索参数中的排序字段和排序方向
searchParams.value.sortField = sortField;
searchParams.value.sortOrder = sortOrder;
} else {
// 如果没有排序信息,重置为默认排序
searchParams.value.sortField = "id";
searchParams.value.sortOrder = "ascend";
}

View File

@ -291,18 +291,16 @@ onMounted(() => {
});
const encryptValue = (value: any, key: string): any => {
// 特定字段不加密
if (key === 'projectStatus') {
return value;
}
// 数值型字段直接返回,不加密
if (key === 'projectSettlementCycle' || key === 'maxPromoterCount' || key === 'projectImage' || key === 'projectName') {
return value;
}
try {
// 将值转换为字符串
const valueString = typeof value === 'object'
? JSON.stringify(value)
: String(value);
@ -325,12 +323,11 @@ const handleSubmit = async () => {
const storedToken = localStorage.getItem('token');
// 创建加密后的表单对象 - 保持原始数据结构
const encryptedFormData: Record<string, any> = {};
console.log("===== 字段Base64编码结果 =====");
Object.entries(formData).forEach(([key, value]) => {
// 跳过空值字段
if (value === null || value === undefined || value === '') return;
// 对字段值进行Base64编码
@ -625,16 +622,16 @@ const limitPromoterCount = (event: Event) => {
.rich-text-columns {
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr)); /* 修正列宽分配 */
gap: 1rem; /* 减小间距 */
align-items: start; /* 顶部对齐 */
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 1rem;
align-items: start;
}
.rich-text-group {
display: flex;
flex-direction: column;
gap: 0.5rem; /* 减小标签与编辑器间距 */
height: 100%; /* 保持等高布局 */
gap: 0.5rem;
height: 100%;
}
.rich-text-group:focus-within {
@ -652,7 +649,7 @@ const limitPromoterCount = (event: Event) => {
@media (max-width: 1280px) {
.rich-text-columns {
grid-template-columns: repeat(2, 1fr); /* 更早切换为2列 */
grid-template-columns: repeat(2, 1fr);
gap: 1.2rem;
}
}
@ -672,7 +669,7 @@ const limitPromoterCount = (event: Event) => {
min-height: 320px;
height: auto;
flex-grow: 1;
/* 新增焦点状态 */
&:focus-within {
border-color: #6366f1 !important;
box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.1);
@ -680,14 +677,13 @@ const limitPromoterCount = (event: Event) => {
}
/* 调整标签与编辑器间距 */
.rich-text-group {
display: flex;
flex-direction: column;
gap: 0.5rem;
height: 100%;
border-radius: 15px;
/* 新增标签焦点状态联动 */
&:focus-within .label-text {
color: #6366f1;
transition: color 0.3s ease;
@ -695,7 +691,6 @@ const limitPromoterCount = (event: Event) => {
}
}
/* 保持与其他输入组件的一致性 */
.rich-text-group .label-text {
font-size: 0.95rem;
font-weight: 600;
@ -705,7 +700,6 @@ const limitPromoterCount = (event: Event) => {
}
/* 手机预览样式 */
.phone-preview-container {
display: flex;
justify-content: center;

View File

@ -87,7 +87,7 @@ import RichTextEditor from '../components/RichTextEditor.vue';
import {useRoute} from "vue-router";
function encode64(text: string): string {
// 使用_代替未使用的match参数
return btoa(encodeURIComponent(text).replace(/%([0-9A-F]{2})/g, (_, p1) => {
return String.fromCharCode(parseInt(p1, 16));
}));
@ -102,10 +102,10 @@ interface NotificationForm {
const formData = reactive<NotificationForm>({
notificationTitle: '',
notificationContent: '',
projectId: 0 // 初始化值
projectId: 0
});
const route = useRoute(); // 获取路由参数
const route = useRoute();
onMounted(() => {
// 从路由参数中获取projectId并填充到表单
@ -118,7 +118,6 @@ const handleSubmit = async () => {
try {
const storedToken = localStorage.getItem('token');
// 创建要发送的数据副本
const payload = {
...formData,
notificationContent: encode64(formData.notificationContent)
@ -153,7 +152,6 @@ const handleSubmit = async () => {
</script>
<style scoped>
/* 保留原有样式,添加富文本编辑器样式 */
.phone-notification {
padding: 20px;
}

View File

@ -65,22 +65,22 @@ interface ProjectDetail {
id: number;
projectDetailName: string;
projectSettlementPrice: number;
projectMinSettlementPrice: number; // 修正接口字段
projectMinSettlementPrice: number;
maxCommissionRate: number;
projectId: number;
}
const route = useRoute();
const projectId = ref<string | number>("");
const tableData = ref<ProjectDetail[]>([]); // 改为数组存储
const tableData = ref<ProjectDetail[]>([]);
const loading = ref(false);
const error = ref("");
const searchId = ref(""); // 新增搜索ID绑定
const searchId = ref("");
// 新增变量保存原始数据
const originalTableData = ref<ProjectDetail[]>([]);
// 修改 getMoneyDetail 方法
const getMoneyDetail = async (id: string | number) => {
const storedToken = localStorage.getItem('token');
try {
@ -98,7 +98,7 @@ const getMoneyDetail = async (id: string | number) => {
console.log(response)
if (response.code === 1) {
tableData.value = response.data;
// 保存原始数据
originalTableData.value = response.data;
} else {
error.value = "获取项目详情失败";
@ -112,10 +112,10 @@ const getMoneyDetail = async (id: string | number) => {
}
};
// 修改搜索处理方法 - 前端过滤
const handleIdSearch = (value: string) => {
if (!value.trim()) {
// 如果搜索值为空,显示所有数据
tableData.value = [...originalTableData.value];
return;
}
@ -126,7 +126,7 @@ const handleIdSearch = (value: string) => {
return;
}
// 前端过滤逻辑
const filtered = originalTableData.value.filter(item => item.id === id);
if (filtered.length === 0) {
@ -137,10 +137,9 @@ const handleIdSearch = (value: string) => {
}
};
// 修改重置方法
const reset = () => {
searchId.value = "";
// 重置时显示所有原始数据
tableData.value = [...originalTableData.value];
};
@ -174,7 +173,7 @@ const queryDetailById = async (id: string | number) => {
}
};
// 初始化逻辑
if (typeof route.query.id === "string") {
projectId.value = route.query.id;
}
@ -188,9 +187,9 @@ onMounted(() => {
const deleteMoneyDetail = async (id: number) => {
try {
const storedToken = localStorage.getItem('token');
// 修正接口路径为项目明细的删除接口
const res: any = await myAxios.post(
"/projectDetail/delete", // 修改这里
"/projectDetail/delete",
{ id },
{
headers: {
@ -259,12 +258,12 @@ const formState = reactive({
// };
//
// 新增表单相关状态
const addDrawerVisible = ref(false);
const addFormState = reactive({
projectDetailName: '',
projectSettlementPrice: 0,
projectMinSettlementPrice: 0, // 注意字段名称需要与接口一致
projectMinSettlementPrice: 0,
maxCommissionRate: 0,
projectId: 0
});
@ -352,7 +351,7 @@ const handleSubmit = async () => {
const res:any = await myAxios.post(
"/projectDetail/update",
{
// 根据接口文档调整字段映射
id: formState.id,
projectDetailName: formState.projectDetailName,
projectSettlementPrice: formState.projectSettlementPrice,
@ -368,12 +367,12 @@ const handleSubmit = async () => {
}
);
console.log('接口响应:', res); // 添加详细日志
console.log('接口响应:', res);
if (res.code === 1) { // 注意响应结构层级
if (res.code === 1) {
message.success('更新成功');
drawerVisible.value = false;
// 根据当前查看模式刷新
if (searchId.value) {
await queryDetailById(formState.id);
} else {
@ -389,12 +388,10 @@ const handleSubmit = async () => {
};
//返回上一级
const router = useRouter();
// 返回上一级方法
const goBack = () => {
// router.go(-1); // 返回上一页
router.push('/project')
};
@ -526,7 +523,6 @@ const goBack = () => {
</a-form>
</a-drawer>
<!-- 修改新增抽屉的表单项 -->
<a-drawer
title="新增项目明细"
placement="right"
@ -625,7 +621,6 @@ const goBack = () => {
}
/*橙色按钮*/
.custom-button {
background-color: #ffa940;
@ -647,7 +642,6 @@ const goBack = () => {
color: #fff;
}
/* 危险按钮样式 */
.custom-button.ant-btn-dangerous {
background-color: #ff4d4f;
border-color: #ff4d4f;
@ -659,7 +653,6 @@ const goBack = () => {
border-color: #ff7875;
}
/* 保持原有的其他样式不变 */
.search-box {
margin-bottom: 10px;
}
@ -675,13 +668,11 @@ const goBack = () => {
border-color: #fa8c16;
}
/* 保持输入框原有样式不变 */
.custom-search :deep(.ant-input) {
border-right-color: #ffa940;
}
/* 调整分页器位置 */
:deep(.ant-table-pagination.ant-pagination) {
margin: 0;
}

View File

@ -74,9 +74,9 @@ function decode64(text: string): string {
}
}
interface NotificationDetail {
id: string; // 改为 string 类型
id: string;
notificationTitle: string;
projectId: string; // 改为 string 类型
projectId: string;
createTime: string;
notificationContent: string;
}
@ -99,7 +99,7 @@ const errorMessage = ref("");
// 进入编辑模式
const enterEditMode = () => {
// 创建可编辑数据的副本
editableNotice.value = {
...projectNotice.value
};
@ -140,7 +140,7 @@ const saveNotice = async () => {
if (res.code === 1) {
message.success('更新成功');
// 更新本地数据并退出编辑模式 - 使用解码后的内容
projectNotice.value = {
...editableNotice.value, // 这里包含的是用户编辑的原始内容(未编码)
};
@ -217,7 +217,6 @@ onMounted(() => {
</script>
<style scoped>
/* 基本样式保持不变 */
.notification-detail-container {
width: 100%;
padding: 24px;

View File

@ -124,14 +124,14 @@ import router from "../../router";
const loading = ref(false);
const total = ref(0);
const searchProjectName = ref(""); // 改为项目名称搜索参数
const searchProjectName = ref("");
const searchParams = ref({
current: 1,
pageSize: 10,
sortField: "id",
sortOrder: "ascend",
userRole: null,
projectName: "" // 新增项目名称查询参数
projectName: ""
});
@ -163,8 +163,8 @@ const columns = [
key: 'id',
fixed: 'left',
align: 'center',
sorter: true, // 添加排序功能
sortDirections: ['ascend', 'descend'] // 允许升序降序
sorter: true,
sortDirections: ['ascend', 'descend']
},
{
@ -189,8 +189,8 @@ const columns = [
width: 20,
key: 'projectPrice',
align: 'center',
sorter: true, // 添加排序功能
sortDirections: ['ascend', 'descend'] // 允许升序降序
sorter: true,
sortDirections: ['ascend', 'descend']
},
{
title: '当前推广人数',
@ -198,8 +198,8 @@ const columns = [
width: 20,
key: 'currentPromotionCount',
align: 'center',
sorter: true, // 添加排序功能
sortDirections: ['ascend', 'descend'] // 允许升序降序
sorter: true,
sortDirections: ['ascend', 'descend']
},
{
title: '结算周期',
@ -207,8 +207,8 @@ const columns = [
key: 'projectSettlementCycle',
width: 10,
align: 'center',
sorter: true, // 添加排序功能
sortDirections: ['ascend', 'descend'] // 允许升序降序
sorter: true,
sortDirections: ['ascend', 'descend']
},
{
title: '最大推广人数',
@ -216,8 +216,8 @@ const columns = [
key: 'maxPromoterCount',
width: 20,
align: 'center',
sorter: true, // 添加排序功能
sortDirections: ['ascend', 'descend'] // 允许升序降序
sorter: true,
sortDirections: ['ascend', 'descend']
},
{
title: '项目状态',
@ -255,9 +255,9 @@ const toggleShelves = async (record: any) => {
if (res.code === 1) {
message.success('状态更新成功');
// 使用 Object.assign 触发响应式更新
Object.assign(record, { isShelves: !record.isShelves });
// 或重新获取数据(推荐)
await getProjectList();
}else {
message.error(res.message || '操作失败');
@ -269,15 +269,14 @@ const toggleShelves = async (record: any) => {
};
interface ProjectRecord {
// 这里根据实际数据结构定义属性
superHostList?: string[];
// 其他属性...
}
// 项目名称搜索方法
const handleProjectSearch = async () => {
// 将搜索参数同步到分页查询参数
searchParams.value.projectName = searchProjectName.value;
searchParams.value.current = 1; // 重置到第一页
searchParams.value.current = 1;
await getProjectList();
};
//用户分页查询
@ -287,23 +286,28 @@ const getProjectList = async () => {
const storedToken = localStorage.getItem('token');
if (!storedToken) throw new Error('未找到登录信息');
const res:any = await myAxios.post("/project/page",
{
const params = {
...searchParams.value,
projectName: searchParams.value.projectName
},
fuzzyQuery: true,
fuzzyField: 'projectName'
};
const res:any = await myAxios.post("/project/page",
params,
{ headers: { Authorization: storedToken } }
);
console.log(res)
if (res.code === 1 && res.data && Array.isArray(res.data.records)) {
tableData.value = res.data.records.map((item: ProjectRecord) => ({
...item,
superUserList: item.superHostList? item.superHostList.join(', ') : '无'
}));
// 同步总条数到分页组件
total.value = res.data.total;
pagination.value.total = res.data.total; // 新增此行
pagination.value.current = searchParams.value.current; // 同步当前页
pagination.value.total = res.data.total;
pagination.value.current = searchParams.value.current;
} else {
message.error(res.message || '请求失败');
}
@ -318,7 +322,6 @@ const getProjectList = async () => {
onMounted(getProjectList);
//分页
// 分页配置
const pagination = ref({
current: 1,
pageSize: 10,
@ -329,9 +332,9 @@ const pagination = ref({
pageSizeOptions: ['10', '20', '50', '100']
});
const handleTableChange = (pag: any, _: any, sorter: any) => {
// 处理排序参数
let sortField = "id"; // 默认排序字段
let sortOrder = "ascend"; // 默认排序方式
let sortField = "id";
let sortOrder = "ascend";
if (sorter.field) {
sortField = sorter.field;
sortOrder = sorter.order;
@ -340,11 +343,11 @@ const handleTableChange = (pag: any, _: any, sorter: any) => {
...searchParams.value,
current: pag.current,
pageSize: pag.pageSize,
sortField: sortField, // 设置排序字段
sortField: sortField,
sortOrder: sortOrder
};
// 同步到分页组件
pagination.value = {
...pagination.value,
current: pag.current,
@ -364,13 +367,13 @@ interface Project {
projectStatus: string;
projectDescription: string;
settlementDesc: string;
// 其他可能存在的属性根据实际情况补充
}
const tableData = ref<Project[]>([]);
// 删除项目 - 添加确认弹窗
// 删除项目
const deleteProject = (id: number) => {
Modal.confirm({
title: '确认删除',
@ -403,7 +406,7 @@ const deleteProject = (id: number) => {
}
},
onCancel() {
// 用户点击取消,不做操作
},
});
};
@ -467,10 +470,8 @@ const promotionCode=(id:number)=>{
</script>
<style scoped>
.action-btn {
padding: 0 8px;
}
/* 分割线样式 */
:deep(.ant-divider-vertical) {
border-color: rgba(0, 0, 0, 0.15);
height: 1.2em;
@ -490,8 +491,6 @@ const promotionCode=(id:number)=>{
}
/* 角色颜色映射 */
:root {
--role-user: #87d068;
--role-admin: #f50;
@ -503,7 +502,6 @@ const promotionCode=(id:number)=>{
}
/*橙色按钮*/
.custom-button {
background-color: #ffa940;
@ -525,7 +523,7 @@ const promotionCode=(id:number)=>{
color: #fff;
}
/* 危险按钮样式 */
.custom-button.ant-btn-dangerous {
background-color: #ff4d4f;
border-color: #ff4d4f;
@ -537,7 +535,7 @@ const promotionCode=(id:number)=>{
border-color: #ff7875;
}
/* 保持原有的其他样式不变 */
.search-box {
margin-bottom: 10px;
}
@ -553,7 +551,7 @@ const promotionCode=(id:number)=>{
border-color: #fa8c16;
}
/* 保持输入框原有样式不变 */
.custom-search :deep(.ant-input) {
border-right-color: #ffa940;
}

View File

@ -125,7 +125,6 @@ const handleFileUpload = async (event: Event) => {
message.error('文件上传失败');
previewImage.value = '';
} finally {
// 重置文件输入,允许重复上传同一文件
if (input) input.value = '';
}
};
@ -199,7 +198,7 @@ const finishEditing = async () => {
message.error('项目更新失败');
} finally {
isEditing.value = false;
previewImage.value = ''; // 清空预览图
previewImage.value = '';
}
};
@ -257,9 +256,8 @@ const updateProject = () => {
message.warning('项目数据尚未加载完成,请稍后再试');
return;
}
// 创建编辑数据的深拷贝副本
editData.value = JSON.parse(JSON.stringify(projectData.value));
previewImage.value = ''; // 进入编辑模式时重置预览图
previewImage.value = '';
isEditing.value = true;
};
@ -279,13 +277,12 @@ const triggerFileInput = () => {
const handleNumberInput = (e: KeyboardEvent) => {
const allowedKeys = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'Backspace', 'Delete', 'ArrowLeft', 'ArrowRight', 'Tab'];
// 只允许数字和控制键
if (!allowedKeys.includes(e.key)) {
e.preventDefault();
}
};
// 添加范围限制方法 - 现在在输入时立即修正
const limitSettlementCycle = (value: number) => {
if (!editData.value) return;

View File

@ -64,7 +64,7 @@ const columns = [
}
];
// 修改接口数据类型
interface ProjectNotification {
id: number;
notificationTitle: string;
@ -75,14 +75,14 @@ interface ProjectNotification {
const route = useRoute();
const projectId = ref<string | number>("");
const originalTableData = ref<ProjectNotification[]>([]);
const searchedData = ref<ProjectNotification[]>([]); // 存储搜索结果
const displayData = ref<ProjectNotification[]>([]); // 实际显示的数据
const searchedData = ref<ProjectNotification[]>([]);
const displayData = ref<ProjectNotification[]>([]);
const loading = ref(false);
const error = ref("");
const searchId = ref("");
// 修改搜索处理方法,不再调用后端接口
const handleIdSearch = () => {
const value = searchId.value.trim();
@ -97,14 +97,14 @@ const handleIdSearch = () => {
return;
}
// 在原始数据中过滤
const result = originalTableData.value.filter(item => item.id === id);
if (result.length === 0) {
message.warning("未找到匹配的项目通知ID");
}
// 更新显示数据为搜索结果
searchedData.value = result;
displayData.value = result;
};
@ -208,16 +208,15 @@ const deleteNotification = async (id: number) => {
};
// 从路由参数中获取项目ID
if (typeof route.query.id === "string") {
projectId.value = route.query.id;
}
// 修改新增按钮的路由跳转传递projectId到新增页面
const goAddProjectNotice = () => {
router.push({
path: '/addprojectNotice',
query: { projectId: projectId.value } // 传递项目ID到新增页面
query: { projectId: projectId.value }
});
};
@ -298,8 +297,6 @@ const goBack = () => {
}
/*橙色按钮*/
.custom-button {
background-color: #ffa940;
border-color: #ffa940;
@ -320,7 +317,7 @@ const goBack = () => {
color: #fff;
}
/* 危险按钮样式 */
.custom-button.ant-btn-dangerous {
background-color: #ff4d4f;
border-color: #ff4d4f;
@ -332,7 +329,6 @@ const goBack = () => {
border-color: #ff7875;
}
/* 保持原有的其他样式不变 */
.search-box {
margin-bottom: 10px;
}
@ -348,13 +344,12 @@ const goBack = () => {
border-color: #fa8c16;
}
/* 保持输入框原有样式不变 */
.custom-search :deep(.ant-input) {
border-right-color: #ffa940;
}
/* 调整分页器位置 */
:deep(.ant-table-pagination.ant-pagination) {
margin: 0;
}

View File

@ -58,7 +58,7 @@ const columns = [
}
];
// 接口数据类型
interface PromoCode {
id: number;
promoCodeInfoKey: string;
@ -70,14 +70,14 @@ interface PromoCode {
const route = useRoute();
const projectId = ref<string | number>("");
const originalTableData = ref<PromoCode[]>([]); // 存储所有原始数据
const displayData = ref<PromoCode[]>([]); // 实际显示的数据
const originalTableData = ref<PromoCode[]>([]);
const displayData = ref<PromoCode[]>([]);
const loading = ref(false);
const searchId = ref("");
const previewVisible = ref(false);
const previewImage = ref("");
// 主查询方法 - 获取项目所有推广码
const getPromoCodes = async (id: string | number) => {
const storedToken = localStorage.getItem('token');
try {
@ -95,10 +95,9 @@ const getPromoCodes = async (id: string | number) => {
console.log(response);
if (response.code === 1) {
// 确保data是数组即使为null也转为空数组
const data = Array.isArray(response.data) ? response.data : [];
originalTableData.value = data;
displayData.value = data; // 初始显示所有数据
displayData.value = data;
} else {
message.error(response.message || "获取数据失败");
originalTableData.value = [];
@ -114,7 +113,7 @@ const getPromoCodes = async (id: string | number) => {
}
};
// 初始化逻辑
if (typeof route.query.id === "string") {
projectId.value = route.query.id;
}
@ -125,7 +124,7 @@ onMounted(() => {
}
});
// 修改搜索处理 - 前端过滤
const handleIdSearch = () => {
const value = searchId.value.trim();
@ -140,18 +139,18 @@ const handleIdSearch = () => {
return;
}
// 在前端原始数据中过滤
const result = originalTableData.value.filter(item => item.id === id);
if (result.length === 0) {
message.warning("未找到匹配的推广码ID");
}
// 更新显示数据
displayData.value = result;
};
// 重置搜索 - 显示所有数据
// 重置搜索
const reset = () => {
searchId.value = "";
displayData.value = originalTableData.value;
@ -181,14 +180,13 @@ const openAddDrawer = () => {
// 提交新增请求
const handleAddSubmit = async () => {
// 如果有文件需要上传
if (fileList.value.length > 0) {
const imageUrl = await handleUpload();
if (!imageUrl) {
message.error('图片上传失败,无法提交');
return;
}
// 更新表单中的图片URL
addFormState.promoCodeImage = imageUrl;
}
@ -215,7 +213,7 @@ const handleAddSubmit = async () => {
message.success('新增成功');
addDrawerVisible.value = false;
await getPromoCodes(projectId.value);
// 重置表单和文件列表
Object.assign(addFormState, {
promoCodeInfoKey: '',
promoCodeLink: '',
@ -309,14 +307,14 @@ const handleEdit = async (id: number) => {
};
const handleEditSubmit = async () => {
// 如果有文件需要上传
if (fileList.value.length > 0) {
const imageUrl = await handleUpload();
if (!imageUrl) {
message.error('图片上传失败,无法提交');
return;
}
// 更新表单中的图片URL
editFormState.promoCodeImage = imageUrl;
}
@ -343,7 +341,7 @@ const handleEditSubmit = async () => {
message.success('编辑成功');
editDrawerVisible.value = false;
await getPromoCodes(projectId.value);
// 重置文件列表
fileList.value = [];
} else {
message.error(res.message || '编辑失败');
@ -353,19 +351,19 @@ const handleEditSubmit = async () => {
}
};
//返回上一级
const router = useRouter();
// 返回上一级方法
const goBack = () => {
router.go(-1); // 返回上一页
router.go(-1);
};
// 添加文件上传相关状态
const fileList = ref<any[]>([]);
const uploading = ref(false);
// 图片上传处理
const handleUpload = async () => {
const formData = new FormData();
fileList.value.forEach(file => {
@ -382,7 +380,7 @@ const handleUpload = async () => {
{
headers: {
'Authorization': storedToken,
'AflatScript': 'required', // 添加AflatScript头部
'AflatScript': 'required',
'Content-Type': 'multipart/form-data'
}
}
@ -390,7 +388,7 @@ const handleUpload = async () => {
if (res.code === 1) {
message.success('上传成功');
// 将返回的文件路径保存到表单中
addFormState.promoCodeImage = res.data;
return res.data;
} else {
@ -405,23 +403,23 @@ const handleUpload = async () => {
uploading.value = false;
}
};
// 文件上传前的处理
const beforeUpload = (file: any) => {
// 检查文件类型
const isImage = file.type.includes('image');
if (!isImage) {
message.error('只能上传图片文件!');
return false;
}
// 检查文件大小 (限制为5MB)
const isLt5M = file.size / 1024 / 1024 < 5;
if (!isLt5M) {
message.error('图片大小不能超过5MB!');
return false;
}
// 添加到文件列表
fileList.value = [file];
return false; // 手动上传
};
@ -466,9 +464,9 @@ const batchDelete = async () => {
if (res.code === 1) {
message.success(`成功删除 ${selectedRowKeys.value.length} 条记录`);
// 刷新数据
await getPromoCodes(projectId.value);
// 清空选择
selectedRowKeys.value = [];
} else {
message.error(res.message || '批量删除失败');
@ -750,26 +748,13 @@ const batchDelete = async () => {
background-color: #fafafa !important;
}
/* 新增表格页脚样式 */
.table-footer {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 0;
}
.back-button {
margin-right: 16px;
}
/* 调整分页器位置 */
:deep(.ant-table-pagination.ant-pagination) {
margin: 0;
}
/*橙色按钮*/
.custom-button {
background-color: #ffa940;
@ -791,7 +776,7 @@ const batchDelete = async () => {
color: #fff;
}
/* 危险按钮样式 */
.custom-button.ant-btn-dangerous {
background-color: #ff4d4f;
border-color: #ff4d4f;
@ -803,7 +788,7 @@ const batchDelete = async () => {
border-color: #ff7875;
}
/* 保持原有的其他样式不变 */
.search-box {
margin-bottom: 10px;
}
@ -819,7 +804,6 @@ const batchDelete = async () => {
border-color: #fa8c16;
}
/* 保持输入框原有样式不变 */
.custom-search :deep(.ant-input) {
border-right-color: #ffa940;
}
@ -834,23 +818,5 @@ const batchDelete = async () => {
color: #666;
}
/*批量删除按钮操作*/
.batch-delete-button {
margin-left: 10px;
background-color: #ff4d4f;
border-color: #ff4d4f;
color: #fff;
}
.batch-delete-button:hover {
background-color: #ff7875;
border-color: #ff7875;
}
/* 添加选择计数样式 */
.selection-count {
margin-left: 10px;
font-size: 14px;
color: #ff4d4f;
}
</style>

View File

@ -11,7 +11,7 @@
>
<template #bodyCell="{ column, record }">
<template v-if="column.dataIndex === 'projectImage'">
<!-- 修复添加 alt 属性 -->
<img
:src="downLoadImage+record.projectImage"
alt="项目图片"
@ -19,7 +19,7 @@
/>
</template>
<template v-if="column.dataIndex === 'projectCodeImage'">
<!-- 修复添加 alt 属性 -->
<img
:src="downLoadImage+record.projectCodeImage"
alt="推广码"
@ -62,7 +62,7 @@ interface PromoRecord {
createTime: string;
}
// 列配置保持不变
const columns = ref([
{
title: 'ID',
@ -166,7 +166,7 @@ const columns = ref([
const dataSource = ref<PromoRecord[]>([]);
const loading = ref(false);
// 添加排序参数
const sortParams = reactive({
sortField: 'id',
sortOrder: 'ascend'
@ -180,7 +180,7 @@ const pagination = reactive({
showTotal: (total: number) => `${total}`,
});
// fetchData 方法保持不变
const fetchData = async () => {
const storedToken = localStorage.getItem('token');
try {
@ -208,7 +208,7 @@ const fetchData = async () => {
}
};
// 修复:简化排序处理逻辑
const handleTableChange: TableProps['onChange'] = (pag, _, sorter) => {
// 处理分页参数
if (pag) {
@ -216,16 +216,15 @@ const handleTableChange: TableProps['onChange'] = (pag, _, sorter) => {
pagination.pageSize = pag.pageSize!;
}
// 修复:简化排序处理
if (sorter) {
// 处理排序参数(只处理单列排序)
const { field, order } = sorter as { field?: string; order?: string };
if (field && order) {
sortParams.sortField = field;
sortParams.sortOrder = order;
} else {
// 取消排序时重置
sortParams.sortField = 'id';
sortParams.sortOrder = 'ascend';
}

View File

@ -124,7 +124,7 @@ import { useRoute } from 'vue-router';
import router from '../../router';
import dayjs from 'dayjs';
// 项目结算记录接口类型
interface SettlementRecord {
id: number;
projectDetailName: string;
@ -201,7 +201,7 @@ const loading = ref(false);
const searchId = ref(''); // 新增搜索ID绑定
const originalData = ref<SettlementRecord[]>([]); // 存储原始数据
// 分页配置
const pagination = reactive({
current: 1,
pageSize: 10,
@ -210,13 +210,13 @@ const pagination = reactive({
showTotal: (total: number) => `${total}`,
});
// 获取路由参数
const route = useRoute();
const idFromRoute = route.query.id as string | undefined;
const projectIdFromRoute = route.query.projectId as string | undefined;
const userIdFromRoute = route.query.userId as string | undefined;
// 计算属性:根据分页参数生成表格渲染用的分页数据
const paginatedData = computed(() => {
const start = (pagination.current - 1) * pagination.pageSize;
const end = start + pagination.pageSize;
@ -232,7 +232,7 @@ const fetchData = async () => {
const response: any = await myAxios.post(
'/projectSettlement/queryByPId',
{ id: idFromRoute }, // 使用从路由获取的id
{ id: idFromRoute },
{
headers: {
'Content-Type': 'application/json',
@ -246,7 +246,7 @@ const fetchData = async () => {
if (response.code === 1) {
allData.value = response.data;
originalData.value = [...response.data]; // 保存原始数据
originalData.value = [...response.data];
filteredData.value = [...response.data];
pagination.total = response.data.length;
} else {
@ -272,7 +272,7 @@ const fetchData = async () => {
const handleIdSearch = (value: string) => {
const idStr = value.trim();
if (!idStr) {
// 如果搜索值为空,显示所有数据
reset();
return;
}
@ -283,7 +283,7 @@ const handleIdSearch = (value: string) => {
return;
}
// 前端过滤 originalData 数据
const result = originalData.value.filter((record) => record.id === id);
if (result.length > 0) {
@ -302,21 +302,20 @@ const reset = () => {
searchId.value = '';
filteredData.value = [...originalData.value];
pagination.total = originalData.value.length;
pagination.current = 1; // 重置页码
revenueSourceFilter.value = 'all'; // 重置收益来源筛选
pagination.current = 1;
revenueSourceFilter.value = 'all';
applyFilters();
};
// 表格分页变更处理
const handleTableChange: TableProps['onChange'] = (pag) => {
if (pag) {
pagination.current = pag.current!;
pagination.pageSize = pag.pageSize!;
// 计算属性自动根据 pagination 变化更新,无需额外调用
}
};
// 返回按钮(修正拼写,保持语义清晰)
const goBack = () => {
router.push('/applicationRecord');
};
@ -328,7 +327,7 @@ const deleteRecord = async (id: number) => {
try {
const response: any = await myAxios.post(
'/projectSettlement/delete',
{ id }, // 要删除的记录ID
{ id },
{
headers: {
'Content-Type': 'application/json',
@ -342,13 +341,13 @@ const deleteRecord = async (id: number) => {
if (response.code === 1) {
message.success('删除成功');
// 重新加载数据
if (searchId.value) {
// 如果当前在搜索状态,重新搜索
handleIdSearch(searchId.value);
} else {
// 否则重新获取所有数据
await fetchData(); // 异步函数调用加 await
await fetchData();
}
} else {
message.error(response.message || '删除失败');
@ -370,7 +369,7 @@ const form = reactive({
settlementRevenue: '',
workTime: null,
settlementTime: null,
// 新增:自动填充路由参数
promoCodeApplyId: idFromRoute || 0,
projectId: projectIdFromRoute || 0,
userId: userIdFromRoute || 0,
@ -381,7 +380,7 @@ const form = reactive({
const showModal = () => {
visible.value = true;
// 重置非路由传递的字段(避免重复填充)
form.projectDetailName = '';
form.settlementQuantity = '';
form.settlementRevenue ='';
@ -435,13 +434,12 @@ const handleAdd = async () => {
}
};
const revenueSourceFilter = ref<string | boolean>('all'); // 收益来源筛选值
const revenueSourceFilter = ref<string | boolean>('all');
// 应用筛选条件
const applyFilters = () => {
let result = [...originalData.value];
// 应用ID筛选
if (searchId.value.trim() !== '') {
const id = Number(searchId.value.trim());
if (!isNaN(id)) {
@ -449,7 +447,7 @@ const applyFilters = () => {
}
}
// 应用收益来源筛选
if (revenueSourceFilter.value !== 'all') {
result = result.filter(record => record.revenueSource === revenueSourceFilter.value);
}
@ -473,7 +471,7 @@ const applyFilters = () => {
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
/* 橙色按钮样式 */
.custom-button {
background-color: #ffa940;
border-color: #ffa940;
@ -488,7 +486,7 @@ const applyFilters = () => {
opacity: 0.9;
}
/* 表格样式调整 */
:deep(.ant-table-thead) {
background-color: #fafafa;
font-weight: 600;
@ -498,7 +496,7 @@ const applyFilters = () => {
background-color: #f9f9f9 !important;
}
/* 搜索按钮样式 */
.custom-search :deep(.ant-input-search-button) {
background-color: #ffa940;
border-color: #ffa940;
@ -510,7 +508,7 @@ const applyFilters = () => {
border-color: #fa8c16;
}
/* 保持输入框原有样式不变 */
.custom-search :deep(.ant-input) {
border-right-color: #ffa940;
}

View File

@ -50,7 +50,7 @@ interface WithdrawalRecord {
withdrawalStatus: string;
}
// 修改后的列配置 - 添加排序功能
const columns = ref([
{
title: '提现申请记录ID',
@ -59,8 +59,8 @@ const columns = ref([
width: 80,
fixed: 'left',
align: 'center',
sorter: true, // 添加排序功能
sortDirections: ['ascend', 'descend'] // 允许升序降序
sorter: true,
sortDirections: ['ascend', 'descend']
},
{
title: '持卡人',
@ -68,8 +68,8 @@ const columns = ref([
key: 'cardHolder',
width: 120,
align: 'center',
sorter: true, // 添加排序功能
sortDirections: ['ascend', 'descend'] // 允许升序降序
sorter: true,
sortDirections: ['ascend', 'descend']
},
{
title: '身份证号',
@ -84,8 +84,8 @@ const columns = ref([
key: 'phoneNumber',
width: 140,
align: 'center',
sorter: true, // 添加排序功能
sortDirections: ['ascend', 'descend'] // 允许升序降序
sorter: true,
sortDirections: ['ascend', 'descend']
},
{
title: '银行卡号',
@ -107,8 +107,8 @@ const columns = ref([
key: 'withdrawnAmount',
width: 120,
align: 'center',
sorter: true, // 添加排序功能
sortDirections: ['ascend', 'descend'] // 允许升序降序
sorter: true,
sortDirections: ['ascend', 'descend']
},
{
title: '提取状态',
@ -129,7 +129,7 @@ const columns = ref([
const dataSource = ref<WithdrawalRecord[]>([]);
const loading = ref(false);
// 添加排序参数
const sortParams = reactive({
sortField: 'id',
sortOrder: 'ascend'
@ -143,7 +143,7 @@ const pagination = reactive({
showTotal: (total: number) => `${total}`,
});
// 状态映射函数
const getStatusText = (status: string) => {
const statusMap: Record<string, string> = {
'processing': '提现中',
@ -153,7 +153,7 @@ const getStatusText = (status: string) => {
return statusMap[status] || status;
};
// 状态颜色映射函数
const getStatusColor = (status: string) => {
const colorMap: Record<string, string> = {
'processing': 'blue',
@ -163,7 +163,7 @@ const getStatusColor = (status: string) => {
return colorMap[status] || 'default';
};
// 修改后的 fetchData 方法 - 添加排序参数
const fetchData = async () => {
const storedToken = localStorage.getItem('token');
try {
@ -171,8 +171,8 @@ const fetchData = async () => {
const response: any = await myAxios.post('/withdrawalApply/page', {
current: pagination.current,
pageSize: pagination.pageSize,
sortField: sortParams.sortField, // 添加排序字段
sortOrder: sortParams.sortOrder, // 添加排序方式
sortField: sortParams.sortField,
sortOrder: sortParams.sortOrder,
withdrawalStatus: 'processing'
}, {
headers: {
@ -193,7 +193,7 @@ const fetchData = async () => {
}
};
// 修改后的 handleTableChange 方法 - 处理排序
const handleTableChange: TableProps['onChange'] = (pag, _, sorter) => {
// 处理分页参数
if (pag) {
@ -209,7 +209,7 @@ const handleTableChange: TableProps['onChange'] = (pag, _, sorter) => {
sortParams.sortField = field;
sortParams.sortOrder = order;
} else {
// 取消排序时重置
sortParams.sortField = 'id';
sortParams.sortOrder = 'ascend';
}

View File

@ -20,11 +20,11 @@
/>
</a-form-item>
<a-button class="custom-button" @click="showModal">新增用户</a-button>
<a-button class="custom-button" @click="showModal">新增管理员</a-button>
<!--新增用户-->
<a-modal
v-model:open="openUser"
title="新增用户"
title="新增管理员"
@ok="handleOk"
:footer="null"
>
@ -375,22 +375,22 @@ const beforeUpload: UploadProps['beforeUpload'] = file => {
// 自定义上传处理
const handleUpload = async ({ file }: { file: File }) => {
const form = new FormData();
form.append('file', file); // 只保留file字段在formData中
form.append('file', file);
try {
uploadLoading.value = true;
const storedToken = localStorage.getItem('token');
// 根据接口文档修改请求地址和参数
const res:any = await myAxios.post('/file/upload?biz=avatar', form, {
headers: {
Authorization: storedToken,
// 保留由浏览器自动生成的内容类型
}
});
console.log(res)
// 根据响应结构调整假设返回data直接是URL
if (res.code === 1) { // 注意文档中响应状态码是200
if (res.code === 1) {
formData.value.userAvatar = res.data;
previewImage.value = URL.createObjectURL(file);
message.success('上传成功');
@ -405,29 +405,29 @@ const handleUpload = async ({ file }: { file: File }) => {
}
};
// 编辑表单实时处理函数
const handleEditNicknameInput = (e: any) => {
// 限制最大长度并实时提示
const trimmedValue = e.target.value.trim().slice(0, 6);
editForm.value.nickName = trimmedValue;
// 触发表单校验
editFormRef.value?.validateField('nickName');
};
const handleEditPhoneInput = (e: any) => {
// 过滤非数字字符并限制长度
const numericValue = e.target.value.replace(/\D/g, '').slice(0, 11);
editForm.value.phoneNumber = numericValue;
// 触发手机号格式校验
editFormRef.value?.validateField('phoneNumber');
};
const handleEditPasswordInput = (e: any) => {
// 限制密码长度
editForm.value.userPassword = e.target.value.slice(0, 10);
};
// 通用最大长度处理函数
const handleMaxLength = (max: number) => (e: KeyboardEvent) => {
if (e.target instanceof HTMLInputElement && e.target.value.length >= max) {
e.preventDefault();
@ -516,7 +516,7 @@ const handleSearchPhoneInput = (e: any) => {
// 新增 onKeyDown 事件处理函数
const handleKeyDown = (e: KeyboardEvent) => {
// 允许控制键(如删除、左右方向键等)
const allowedKeys = [
'Backspace',
'Delete',
@ -525,13 +525,13 @@ const handleKeyDown = (e: KeyboardEvent) => {
'Tab',
];
// 阻止非数字和非允许按键
if (!/[0-9]/.test(e.key) && !allowedKeys.includes(e.key)) {
e.preventDefault();
}
};
// 实时校验逻辑
const formData = ref({
nickName: '',
userAvatar: '',
@ -590,8 +590,8 @@ const columns = [
key: 'id',
fixed: 'left',
align: 'center',
sorter: true, // 添加排序功能
sortDirections: ['ascend', 'descend'] // 允许升序降序
sorter: true,
sortDirections: ['ascend', 'descend']
},
{
@ -630,8 +630,8 @@ const columns = [
key: 'phoneNumber',
width: 80,
align: 'center',
sorter: true, // 添加排序功能
sortDirections: ['ascend', 'descend'] // 允许升序降序
sorter: true,
sortDirections: ['ascend', 'descend']
},
{
title: '邀请码',
@ -639,8 +639,8 @@ const columns = [
key: 'invitationCode',
width: 70,
align: 'center',
sorter: true, // 添加排序功能
sortDirections: ['ascend', 'descend'] // 允许升序降序
sorter: true,
sortDirections: ['ascend', 'descend']
},
{
title: '上级ID',
@ -648,8 +648,8 @@ const columns = [
key: 'parentUserId',
width: 40,
align: 'center',
sorter: true, // 添加排序功能
sortDirections: ['ascend', 'descend'] // 允许升序降序
sorter: true,
sortDirections: ['ascend', 'descend']
},
{
title: '操作',
@ -670,7 +670,7 @@ const pagination = ref({
pageSizeOptions: ['10', '20', '50', '100']
});
//编辑用户表单
// 新增编辑表单校验规则
const editFormRules = {
nickName: [
{
@ -721,13 +721,13 @@ const editHasLower = computed(() => /[a-z]/.test(editForm.value.userPassword))
const editHasUpper = computed(() => /[A-Z]/.test(editForm.value.userPassword))
const editHasNumber = computed(() => /\d/.test(editForm.value.userPassword))
const editFormRef = ref(); // 添加表单引用
const editFormRef = ref();
// 修改后的分页处理函数
const handleTableChange = (pag: any, _: any, sorter: any) => {
// 处理排序参数
let sortField = "id"; // 默认排序字段
let sortOrder = "ascend"; // 默认排序方式
let sortField = "id";
let sortOrder = "ascend";
if (sorter.field) {
sortField = sorter.field;
@ -738,8 +738,8 @@ const handleTableChange = (pag: any, _: any, sorter: any) => {
...searchParams.value,
current: pag.current,
pageSize: pag.pageSize,
sortField: sortField, // 设置排序字段
sortOrder: sortOrder // 设置排序方式
sortField: sortField,
sortOrder: sortOrder
};
pagination.value.current = pag.current;
@ -749,7 +749,7 @@ const handleTableChange = (pag: any, _: any, sorter: any) => {
};
// 修改获取用户列表方法
const getUserList = async () => {
loading.value = true;
try {
@ -771,7 +771,7 @@ const getUserList = async () => {
superUserList: item.superHostList? item.superHostList.join(', ') : '无'
};
});
// 更新分页信息
pagination.value.total = res.data.total;
pagination.value.current = res.data.current;
pagination.value.pageSize = res.data.size;
@ -796,14 +796,11 @@ interface User {
parentUserId:number
}
const tableData = ref<User[]>([]); // 明确元素类型为User[]
const tableData = ref<User[]>([]);
const searchPhone = ref(""); // 手机号搜索参数
const searchPhone = ref("");
// 修改5: 删除原有的searchId变量
// const searchId = ref(""); // 删除这行
// 修改6: 替换原有的handleIdSearch方法
const handlePhoneSearch = async () => {
if (!searchPhone.value) {
message.warning('请输入手机号');
@ -821,7 +818,7 @@ const handlePhoneSearch = async () => {
const storedToken = localStorage.getItem('token');
if (!storedToken) throw new Error('未找到登录信息');
// 修改7: 使用phoneNumber作为查询参数
const res: { code: number; data: any; message: any } = await myAxios.post(
"/userInfo/page",
{
@ -857,9 +854,9 @@ const handlePhoneSearch = async () => {
}
};
// 修改8: 更新reset方法
const reset = () => {
searchPhone.value = ""; // 清空手机号搜索
searchPhone.value = "";
searchParams.value = {
current: 1,
pageSize: 10,
@ -868,7 +865,7 @@ const reset = () => {
userRole: null,
phoneNumber: null
};
getUserList(); // 重新加载列表
getUserList();
};
@ -882,13 +879,13 @@ const rowSelection = {
selectedRowKeys.value = selectedKeys;
},
selectedRowKeys: selectedRowKeys,
// 添加getCheckboxProps方法根据用户角色决定是否禁用复选框
getCheckboxProps: (record: User) => ({
disabled: record.userRole !== 'admin', // 只有admin用户可以选择
disabled: record.userRole !== 'admin',
name: record.id.toString(),
}),
};
// 修改删除用户方法,添加确认弹窗
const confirmDeleteUser = (id: number) => {
Modal.confirm({
title: '确认删除用户',
@ -897,7 +894,7 @@ const confirmDeleteUser = (id: number) => {
okType: 'danger',
cancelText: '取消',
onOk() {
return deleteUser(id); // 调用实际删除方法
return deleteUser(id);
},
onCancel() {
console.log('用户取消删除');
@ -905,7 +902,7 @@ const confirmDeleteUser = (id: number) => {
});
};
// 修改批量删除方法,添加确认弹窗
const batchDelete = async () => {
if (selectedRowKeys.value.length === 0) {
message.warning('请先选择要删除的用户');
@ -952,7 +949,7 @@ const batchDelete = async () => {
});
};
// 原有的deleteUser方法保持不变执行实际删除操作
const deleteUser = async (id: number) => {
try {
const storedToken = localStorage.getItem('token');
@ -1000,13 +997,11 @@ const editForm = ref({
SuperUserList: ''
});
// 修改showDrawer方法
// 编辑状态下的头像上传状态
const editUploadLoading = ref(false);
const editPreviewImage = ref('');
// 编辑时的自定义上传处理
const handleEditUpload = async ({ file }: { file: File }) => {
const form = new FormData();
form.append('file', file);
@ -1058,15 +1053,14 @@ const showDrawer = (record: any) => {
const toggleEditMode = () => {
isEditMode.value = !isEditMode.value;
};
//保存修改的用户信息
//保存修改的用户信息
const handleSave = async () => {
try {
// 检查密码是否修改且符合规则
if (editForm.value.userPassword) {
const password = editForm.value.userPassword;
// 验证密码规则:必须包含数字、大写字母和小写字母
const hasNumber = /\d/.test(password);
const hasUpperCase = /[A-Z]/.test(password);
const hasLowerCase = /[a-z]/.test(password);
@ -1080,19 +1074,19 @@ const handleSave = async () => {
const storedToken = localStorage.getItem('token');
if (!storedToken) throw new Error('未找到登录信息');
// 处理空值转换
const processValue = (value: any) => {
if (value === "" || value === "无") return null; // 将空字符串和"无"转为null
if (value === "" || value === "无") return null;
return value;
};
// 处理数字类型字段
const processNumber = (value: any) => {
const num = Number(value);
return isNaN(num) ? null : num;
};
// 构建请求体
const payload = {
id: editForm.value.id,
nickName: processValue(editForm.value.nickName),
@ -1110,7 +1104,7 @@ const handleSave = async () => {
: []
};
// 清理undefined字段
const cleanPayload = Object.fromEntries(
Object.entries(payload).filter(([_, v]) => v !== undefined)
);
@ -1129,7 +1123,7 @@ const handleSave = async () => {
await getUserList();
const updatedUser = tableData.value.find(item => item.id === editForm.value.id);
if (updatedUser) {
selectedUser.value = { ...updatedUser }; // 关键更新
selectedUser.value = { ...updatedUser };
}
isEditMode.value = false;
} else {
@ -1142,7 +1136,7 @@ const handleSave = async () => {
};
//新增用户
const openUser = ref<boolean>(false);
const formRef = ref();
@ -1157,7 +1151,7 @@ const showModal = () => {
userAccount: '',
userPassword: ''
};
// 重置预览图片
previewImage.value = '';
};
@ -1206,12 +1200,12 @@ const handleSubmit = async () => {
formRef.value?.resetFields();
getUserList();
} else {
// 使用提取函数处理错误信息
const errorMsg = extractErrorMessage(res.message);
message.error(`${errorMsg}`);
}
} catch (error: any) {
// 使用提取函数处理错误信息
let errorMsg = error.response?.message || error.message;
errorMsg = extractErrorMessage(errorMsg);
message.error(`${errorMsg}`);
@ -1225,7 +1219,7 @@ const handleSubmit = async () => {
.action-btn {
padding: 0 8px;
}
/* 分割线样式 */
:deep(.ant-divider-vertical) {
border-color: rgba(0, 0, 0, 0.15);
height: 1.2em;
@ -1316,46 +1310,14 @@ const handleSubmit = async () => {
color: #1890ff;
}
.code {
font-family: monospace;
background: #f5f5f5;
padding: 2px 6px;
border-radius: 4px;
}
.role-tag {
margin-left: 8px;
font-size: 12px;
}
.relation-item {
display: flex;
align-items: center;
padding: 12px 0;
}
.relation-icon {
font-size: 20px;
color: #52c41a;
margin-right: 16px;
}
.relation-group {
margin-bottom: 8px;
}
.relation-label {
display: inline-block;
width: 80px;
color: #666;
}
.relation-value {
color: #333;
font-weight: 500;
}
/* 角色颜色映射 */
:root {
--role-user: #87d068;
--role-admin: #f50;
@ -1507,7 +1469,7 @@ const handleSubmit = async () => {
border-color: #fa8c16;
}
/* 保持输入框原有样式不变 */
.custom-search :deep(.ant-input) {
border-right-color: #ffa940;
}

View File

@ -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/community/community.vue","./src/view/components/richtexteditor.vue","./src/view/course/linkedcourse.vue","./src/view/course/localcurriculum.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/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/community/community.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/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/userlist.vue","./src/view/work/workdetail.vue","./src/view/work/worklist.vue"],"version":"5.6.3"}