8月10日的bug的修改

This commit is contained in:
2025-08-10 20:15:45 +08:00
parent d6f0ba4e6f
commit da9feb26c6
26 changed files with 743 additions and 112 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,4 +1,5 @@
<template> <template>
<div class="sidebar-container">
<a-menu <a-menu
v-model:selectedKeys="selectedKeys" v-model:selectedKeys="selectedKeys"
mode="inline" mode="inline"
@ -32,16 +33,16 @@
<!-- <a-menu-item key="/project">接单管理</a-menu-item>--> <!-- <a-menu-item key="/project">接单管理</a-menu-item>-->
<!-- </a-sub-menu>--> <!-- </a-sub-menu>-->
<a-sub-menu> <!-- <a-sub-menu>-->
<template #title> <!-- <template #title>-->
<span> <!-- <span>-->
<CommentOutlined /> <!-- <CommentOutlined />-->
<span>结算管理</span> <!-- <span>结算管理</span>-->
</span> <!-- </span>-->
</template> <!-- </template>-->
<a-menu-item key="/applicationRecord">推广码申请记录</a-menu-item> <!-- <a-menu-item key="/applicationRecord">推广码申请记录</a-menu-item>-->
<a-menu-item key="/withdrawalApplicationRecord">提现申请记录</a-menu-item> <!-- <a-menu-item key="/withdrawalApplicationRecord">提现申请记录</a-menu-item>-->
</a-sub-menu> <!-- </a-sub-menu>-->
<a-sub-menu> <a-sub-menu>
<template #title> <template #title>
<span> <span>
@ -74,12 +75,19 @@
<a-menu-item key="/employeePerformanceRanking">员工绩效排行</a-menu-item> <a-menu-item key="/employeePerformanceRanking">员工绩效排行</a-menu-item>
</a-sub-menu> </a-sub-menu>
<a-menu-item key="/employeeApplication"> <a-sub-menu>
<CommentOutlined /> <template #title>
<span>员工申请管理</span> <span>
</a-menu-item> <FieldTimeOutlined />
</a-menu> <span>申请管理</span>
</span>
</template>
<a-menu-item key="/employeeApplication">员工申请管理</a-menu-item>
<a-menu-item key="/applicationInstructions">员工申请须知</a-menu-item>
</a-sub-menu>
</a-menu>
</div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
@ -109,6 +117,29 @@ const handleClick = (item: any) => {
</script> </script>
<style scoped> <style scoped>
.sidebar-container {
height: 100vh;
overflow-y: auto;
background-color: #ffe7ba;
}
/* 自定义滚动条样式 */
.sidebar-container::-webkit-scrollbar {
width: 6px;
}
.sidebar-container::-webkit-scrollbar-track {
background: transparent;
}
.sidebar-container::-webkit-scrollbar-thumb {
background-color: rgba(0, 0, 0, 0.1);
border-radius: 3px;
}
.sidebar-container::-webkit-scrollbar-thumb:hover {
background-color: rgba(0, 0, 0, 0.2);
}
:deep(.ant-menu-item), :deep(.ant-menu-item),
:deep(.ant-menu-submenu-title) { :deep(.ant-menu-submenu-title) {

View File

@ -177,6 +177,11 @@ export const routes = [
name:'员工绩效排行', name:'员工绩效排行',
component: ()=> import("../view/performance/employeePerformanceRanking.vue") component: ()=> import("../view/performance/employeePerformanceRanking.vue")
}, },
{
path:'/applicationInstructions',
name:'员工申请须知',
component: ()=> import("../view/employeeApplication/applicationInstructions.vue")
},
] ]
}, },
] ]

View File

@ -2,7 +2,7 @@
<div id="login"> <div id="login">
<form> <form>
<div class="box" @submit.prevent> <div class="box" @submit.prevent>
<h2>欢迎登录青橙校园管理端</h2> <h2>欢迎登录丁香花校园管理端</h2>
<div class="input-box"> <div class="input-box">
<input type="text" placeholder="账号" v-model="userAccount"/> <input type="text" placeholder="账号" v-model="userAccount"/>

View File

@ -741,7 +741,7 @@ const handlePriceInput = (value: number, field: keyof CourseDetail) => {
border-radius: 0.75rem; border-radius: 0.75rem;
overflow: hidden; overflow: hidden;
transition: all 0.3s ease; transition: all 0.3s ease;
max-height: 500px; max-height: 2000px;
flex-grow: 1; flex-grow: 1;
padding: 10px; padding: 10px;
background: white; background: white;

View File

@ -504,4 +504,10 @@ const showDetails = (id: string) => {
background-color: #ff4d4f !important; background-color: #ff4d4f !important;
border-color: #ff4d4f !important; border-color: #ff4d4f !important;
} }
:deep(.custom-search .ant-input:focus),
:deep(.custom-search .ant-input:hover),
:deep(.custom-search .ant-input-focused) {
border-color: #ffa940 !important;
box-shadow: 0 0 0 2px rgba(255, 169, 64, 0.2) !important;
}
</style> </style>

View File

@ -82,6 +82,7 @@
v-if="record.orderStatus === '交易成功'" v-if="record.orderStatus === '交易成功'"
size="small" size="small"
primary primary
@click="showRefundConfirm(record)"
> >
退款 退款
</a-button> </a-button>
@ -94,9 +95,48 @@
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, ref } from "vue"; import { onMounted, ref } from "vue";
import myAxios from "../../api/myAxios.ts"; import myAxios from "../../api/myAxios.ts";
import { message} from "ant-design-vue"; import { message,Modal} from "ant-design-vue";
import {downLoadImage} from "../../api/ImageUrl.ts"; import {downLoadImage} from "../../api/ImageUrl.ts";
const handleRefund = async (orderId: number) => {
loading.value = true;
try {
const storedToken = localStorage.getItem('token');
if (!storedToken) throw new Error('未找到登录信息');
const res: any = await myAxios.post(
"/courseOrder/refund",
{ id: orderId },
{ headers: { Authorization: storedToken } }
);
if (res.code === 1 && res.data === true) {
message.success('退款成功');
// 刷新订单列表
await getOrderList();
} else {
message.error(res.message || '退款失败');
}
} catch (error) {
console.error("退款失败:", error);
message.error('退款操作失败');
} finally {
loading.value = false;
}
};
// 显示退款确认弹窗
const showRefundConfirm = (record: Order) => {
Modal.confirm({
title: '确认退款',
content: `确定要对订单 ${record.orderNumber} 执行退款操作吗?`,
okText: '确认退款',
cancelText: '取消',
centered: true,
onOk() {
return handleRefund(record.id);
}
});
};
const loading = ref(false); const loading = ref(false);
const selectedRowKeys = ref<number[]>([]); // 存储选中的行ID const selectedRowKeys = ref<number[]>([]); // 存储选中的行ID
@ -109,7 +149,7 @@ const searchParams = ref({
current: 1, current: 1,
pageSize: 10, pageSize: 10,
sortField: "id", sortField: "id",
sortOrder: "ascend", sortOrder: "descend",
orderNumber: "", orderNumber: "",
orderStatus: "" orderStatus: ""
}); });
@ -252,7 +292,7 @@ const handleTableChange = (pag: any, _filters: any, sorter: any) => {
} else { } else {
searchParams.value.sortField = "id"; searchParams.value.sortField = "id";
searchParams.value.sortOrder = "ascend"; searchParams.value.sortOrder = "descend";
} }
// 同步到分页组件 // 同步到分页组件
@ -306,7 +346,7 @@ const reset = () => {
current: 1, current: 1,
pageSize: 10, pageSize: 10,
sortField: "id", sortField: "id",
sortOrder: "ascend", sortOrder: "descend",
orderNumber: "", orderNumber: "",
orderStatus: "" orderStatus: ""
}; };
@ -347,4 +387,11 @@ const reset = () => {
background-color: #ffa940; background-color: #ffa940;
border-color: #ffa940; border-color: #ffa940;
} }
:deep(.custom-search .ant-input:focus),
:deep(.custom-search .ant-input:hover),
:deep(.custom-search .ant-input-focused) {
border-color: #ffa940 !important;
box-shadow: 0 0 0 2px rgba(255, 169, 64, 0.2) !important;
}
</style> </style>

View File

@ -0,0 +1,307 @@
<template>
<form @submit.prevent="handleSubmit" class="modern-form">
<h2 class="form-title">员工账号申请须知</h2>
<!-- 富文本编辑器区域 - 修改为50%宽度 -->
<div class="rich-text-container">
<div class="rich-text-columns">
<div class="rich-text-group">
<span class="label-text">须知详情</span>
<RichTextEditor
v-model="formData.detail"
:disable="false"
@content-change="(html:any) => formData.detail = html"
/>
</div>
</div>
</div>
<button type="submit" class="submit-button">
<span>立即创建</span>
<div class="button-sparkles"></div>
</button>
</form>
</template>
<script setup lang="ts">
import { reactive, ref } from 'vue';
import RichTextEditor from '../components/RichTextEditor.vue';
import '@vueup/vue-quill/dist/vue-quill.snow.css';
import myAxios from "../../api/myAxios.ts";
interface CourseForm {
name: string;
type: string;
image: string;
detail: string;
promoCodeDesc: string;
originPrice: number;
discountPrice: number;
firstLevelRate: number;
secondLevelRate: number;
}
const fileName = ref('');
// 新增价格错误状态
const priceError = ref('');
const formData = reactive<CourseForm>({
name: '',
type: '',
image: '',
detail: '',
promoCodeDesc: '',
originPrice: 0,
discountPrice: 0,
firstLevelRate: 0,
secondLevelRate: 0
});
const encode64 = (text: string): string => {
return btoa(
encodeURIComponent(text).replace(
/%([0-9A-F]{2})/g,
(_, p1) => String.fromCharCode(parseInt(p1, 16))
))
};
const encryptValue = (value: any, key: string): any => {
// 数值型字段直接返回,不加密
if (key === 'originPrice' || key === 'discountPrice' ||
key === 'image' || key ==='name'||key === 'type') {
return value;
}
try {
const valueString = typeof value === 'object'
? JSON.stringify(value)
: String(value);
return encode64(valueString);
} catch (error) {
console.error('Base64编码失败:', error);
return value;
}
};
const handleSubmit = async () => {
// 重置错误状态
priceError.value = '';
try {
const storedToken = localStorage.getItem('token');
const encryptedFormData: Record<string, any> = {};
Object.entries(formData).forEach(([key, value]) => {
if (value === null || value === undefined || value === '') return;
encryptedFormData[key] = encryptValue(value, key);
});
const res: any = await myAxios.post(`/course/add`, encryptedFormData, {
headers: {
'Content-Type': 'application/json',
'Authorization': storedToken
}
});
if (res.code === 1) {
alert('须知创建成功!');
Object.assign(formData, {
name: '',
type: '',
image: '',
detail: '',
promoCodeDesc: '',
originPrice: 0,
discountPrice: 0,
firstLevelRate: 0,
secondLevelRate: 0
});
fileName.value = '';
} else {
alert(`创建失败:${res.message}`);
}
} catch (error) {
console.error('请求失败:', error);
alert('提交失败,请检查控制台获取详细信息');
}
};
</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;
}
.form-title {
text-align: center;
font-size: 1.8rem;
color: #2c3e50;
margin-bottom: 2rem;
font-weight: 600;
letter-spacing: 0.5px;
}
.label-text {
display: block;
margin-bottom: 0.6rem;
color: #4a5568;
font-size: 0.9rem;
font-weight: 500;
}
.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;
}
.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); }
50% { transform: scale(1) translate(100px, -50px); }
100% { transform: scale(0) translate(200px, -100px); }
}
@media (max-width: 768px) {
.form-grid {
grid-template-columns: 1fr;
}
.modern-form {
padding: 1.5rem;
margin: 1rem;
}
}
.rich-text-container {
margin-top: 1.5rem;
width: 100%;
}
.rich-text-group {
width: 100%;
height: 100%;
border: 2px solid #e2e8f0;
border-radius: 0.75rem;
overflow: hidden;
transition: all 0.3s ease;
min-height: 320px;
flex-grow: 1;
}
@media (max-width: 768px) {
.rich-text-columns {
grid-template-columns: 1fr;
}
}
.rich-text-group:focus-within {
border-color: #6366f1;
box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.1);
}
.rich-text-group .label-text {
font-size: 0.95rem;
font-weight: 600;
color: #3b4151;
padding: 12px 12px 0;
}
.phone-section-content * {
max-width: 100%;
}
.phone-section-content img {
max-width: 100%;
height: auto;
border-radius: 8px;
margin: 10px 0;
}
.phone-section-content h1,
.phone-section-content h2,
.phone-section-content h3 {
color: #2c3e50;
margin-top: 15px;
margin-bottom: 10px;
}
.phone-section-content p {
margin-bottom: 10px;
}
.phone-section-content ul,
.phone-section-content ol {
margin-left: 20px;
margin-bottom: 10px;
}
</style>

View File

@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, reactive, onMounted, h } from "vue"; import { ref, reactive, onMounted, h ,nextTick} from "vue";
import { useRouter } from "vue-router"; import { useRouter } from "vue-router";
import { message } from "ant-design-vue"; import { message } from "ant-design-vue";
import type { TableProps } from 'ant-design-vue'; import type { TableProps } from 'ant-design-vue';
@ -21,8 +21,7 @@ const columns = [
key: 'name', key: 'name',
width: 120, width: 120,
fixed: 'left', fixed: 'left',
align: 'center', align: 'center'
sorter: true
}, },
{ {
title: '手机号', title: '手机号',
@ -255,6 +254,50 @@ onMounted(() => {
fetchAdvancementData(); fetchAdvancementData();
}); });
const handleNameInput = (event: Event) => {
const input = event.target as HTMLInputElement;
const originalValue = input.value;
// 过滤掉所有非中文字符(包括全角字符)
const filteredValue = originalValue.replace(/[^\u4e00-\u9fa5]/g, '');
// 更新输入框的值
if (originalValue !== filteredValue) {
searchName.value = filteredValue;
// 强制更新输入框值(解决某些情况下显示残留问题)
nextTick(() => {
input.value = filteredValue;
});
}
};
const handlePaste = (event: ClipboardEvent) => {
const clipboardData = event.clipboardData || (window as any).clipboardData;
const pastedText = clipboardData.getData('text');
// 检查粘贴内容是否包含非中文字符
if (/[^\u4e00-\u9fa5]/.test(pastedText)) {
event.preventDefault();
message.warning('只能粘贴中文字符');
}
};
const filterPhoneInput = (e: Event) => {
const input = e.target as HTMLInputElement;
let value = input.value;
// 1. 移除非数字字符
value = value.replace(/\D/g, '');
// 2. 限制最多11位数字
if (value.length > 11) {
value = value.slice(0, 11);
}
// 更新输入框值
input.value = value;
searchPhone.value = value;
};
</script> </script>
<template> <template>
@ -265,12 +308,14 @@ onMounted(() => {
<a-form-item label="申请人姓名"> <a-form-item label="申请人姓名">
<a-input-search <a-input-search
style="width: 300px" style="width: 300px"
placeholder="请输入姓名" placeholder="请输入姓名(仅中文)"
class="custom-search" class="custom-search"
enter-button enter-button
v-model:value="searchName" v-model:value="searchName"
@pressEnter="handleSearch" @pressEnter="handleSearch"
@search="handleSearch" @search="handleSearch"
@input="handleNameInput"
@paste="handlePaste"
/> />
</a-form-item> </a-form-item>
<a-form-item label="手机号"> <a-form-item label="手机号">
@ -282,6 +327,7 @@ onMounted(() => {
@search="handleSearch" @search="handleSearch"
enter-button enter-button
class="custom-search" class="custom-search"
@input="filterPhoneInput"
/> />
</a-form-item> </a-form-item>
<a-button @click="reset">重置</a-button> <a-button @click="reset">重置</a-button>
@ -379,38 +425,11 @@ onMounted(() => {
border-right-color: #ffa940; border-right-color: #ffa940;
} }
/* 审核人选择弹窗样式 */ :deep(.custom-search .ant-input:focus),
.supervisor-list { :deep(.custom-search .ant-input:hover),
max-height: 400px; :deep(.custom-search .ant-input-focused) {
overflow-y: auto; border-color: #ffa940 !important;
padding: 10px; box-shadow: 0 0 0 2px rgba(255, 169, 64, 0.2) !important;
}
.radio-group {
width: 100%;
}
.supervisor-item {
display: flex;
align-items: center;
padding: 8px 0;
width: 100%;
}
.supervisor-info {
display: flex;
flex-direction: column;
margin-left: 12px;
}
.supervisor-id {
font-size: 12px;
color: #666;
}
.supervisor-name {
font-size: 14px;
font-weight: 500;
} }
:deep(.ant-list-item) { :deep(.ant-list-item) {
@ -427,17 +446,4 @@ onMounted(() => {
align-items: flex-start; align-items: flex-start;
} }
/* 操作按钮样式 */
.action-btn {
margin: 0 4px;
font-size: 14px;
}
.action-btn.primary {
color: #1890ff;
}
.action-btn.danger {
color: #ff4d4f;
}
</style> </style>

View File

@ -124,6 +124,7 @@
:ok-text="'确认驳回'" :ok-text="'确认驳回'"
:cancel-text="'取消'" :cancel-text="'取消'"
okType="danger" okType="danger"
class="reject-modal"
> >
<a-alert type="warning" show-icon class="reject-alert"> <a-alert type="warning" show-icon class="reject-alert">
<template #message> <template #message>
@ -135,18 +136,19 @@
</a-alert> </a-alert>
<div class="reject-form"> <div class="reject-form">
<a-form layout="vertical"> <div class="reject-header">
<a-form-item label="驳回原因" required> <h4 class="reject-title">* 驳回原因</h4>
<div class="char-counter">
{{ rejectReason.length }} / 200
</div>
</div>
<a-textarea <a-textarea
v-model:value="rejectReason" v-model:value="rejectReason"
placeholder="请输入详细的驳回原因..." placeholder="请输入详细的驳回原因..."
:rows="4" :rows="4"
:maxlength="200" :maxlength="200"
show-count
class="reject-reason" class="reject-reason"
/> />
</a-form-item>
</a-form>
</div> </div>
</a-modal> </a-modal>
</div> </div>
@ -632,7 +634,11 @@ const submitReject = async () => {
padding: 24px; padding: 24px;
} }
/* 驳回弹窗样式 */ /* 驳回弹窗样式优化 */
.reject-modal :deep(.ant-modal-body) {
padding: 24px;
}
.reject-alert { .reject-alert {
margin-bottom: 20px; margin-bottom: 20px;
border-radius: 8px; border-radius: 8px;
@ -645,8 +651,37 @@ const submitReject = async () => {
font-weight: 500; font-weight: 500;
} }
.divider {
height: 1px;
background: #f0f0f0;
margin: 20px 0;
}
.reject-form { .reject-form {
padding: 10px; padding: 0 10px;
margin-bottom: 24px;
}
.reject-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
}
.reject-title {
margin: 0;
font-size: 14px;
font-weight: 500;
color: #333;
}
.char-counter {
font-size: 12px;
color: #999;
background: #f5f5f5;
padding: 2px 8px;
border-radius: 10px;
} }
.reject-reason { .reject-reason {
@ -655,6 +690,8 @@ const submitReject = async () => {
padding: 12px; padding: 12px;
border: 1px solid #d9d9d9; border: 1px solid #d9d9d9;
transition: all 0.3s; transition: all 0.3s;
width: 100%;
font-size: 14px;
} }
.reject-reason:focus { .reject-reason:focus {
@ -662,6 +699,13 @@ const submitReject = async () => {
box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2); box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
} }
.action-buttons {
display: flex;
justify-content: flex-end;
gap: 12px;
margin-top: 16px;
}
/* 空状态样式 */ /* 空状态样式 */
:deep(.ant-empty) { :deep(.ant-empty) {
margin: 40px 0; margin: 40px 0;

View File

@ -294,6 +294,7 @@ const updateProportionRate = async () => {
// 修复语法错误:移除命名参数 // 修复语法错误:移除命名参数
proportionRate.value = parseFloat(rateValue.toFixed(2)).toString(); proportionRate.value = parseFloat(rateValue.toFixed(2)).toString();
visible.value = false; visible.value = false;
fetchEmployeePerformance();
} else { } else {
message.error(response.message || "更新抽成比例失败"); message.error(response.message || "更新抽成比例失败");
} }
@ -525,4 +526,5 @@ const filterRateInput = (e: Event) => {
background-color: #ffa940; background-color: #ffa940;
border-color: #ffa940; border-color: #ffa940;
} }
</style> </style>

View File

@ -167,6 +167,33 @@ const handleDateChange = (_: any, dateStrings: [string, string]) => {
queryParams.startDate = dateStrings[0] || ''; queryParams.startDate = dateStrings[0] || '';
queryParams.endDate = dateStrings[1] || ''; queryParams.endDate = dateStrings[1] || '';
}; };
const handleSearchPhoneInput = (e: any) => {
let value = e.target.value;
// 只允许输入数字并截取前11位
const newValue = value.replace(/[^0-9]/g, '').slice(0, 11);
// 直接更新 queryParams.phoneNumber
queryParams.phoneNumber = newValue;
};
// 新增 onKeyDown 事件处理函数
const handleKeyDown = (e: KeyboardEvent) => {
const allowedKeys = [
'Backspace',
'Delete',
'ArrowLeft',
'ArrowRight',
'Tab',
];
// 只允许输入数字和控制键
if (!/[0-9]/.test(e.key) && !allowedKeys.includes(e.key)) {
e.preventDefault();
}
};
</script> </script>
<template> <template>
@ -212,6 +239,9 @@ const handleDateChange = (_: any, dateStrings: [string, string]) => {
placeholder="手机号" placeholder="手机号"
style="width: 180px; margin-right: 10px;" style="width: 180px; margin-right: 10px;"
allow-clear allow-clear
:maxlength="11"
@input="handleSearchPhoneInput"
@keydown="handleKeyDown"
/> />
<Input <Input

View File

@ -25,7 +25,7 @@ const columns = [
align: 'center' align: 'center'
}, },
{ {
title: '主管姓名', title: '学校名称',
dataIndex: 'nickName', dataIndex: 'nickName',
key: 'nickName', key: 'nickName',
width: 100, width: 100,
@ -290,6 +290,7 @@ const updateProportionRate = async () => {
message.success("抽成比例更新成功"); message.success("抽成比例更新成功");
proportionRate.value = rateValue.toFixed(2); proportionRate.value = rateValue.toFixed(2);
visible.value = false; visible.value = false;
fetchManagerPerformance()
} else { } else {
message.error(response.message || "更新抽成比例失败"); message.error(response.message || "更新抽成比例失败");
} }
@ -414,11 +415,11 @@ const settlement = async () => {
<!-- 搜索框 --> <!-- 搜索框 -->
<div class="search-box"> <div class="search-box">
<a-form layout="inline"> <a-form layout="inline">
<a-space> <div class="flex-container">
<a-form-item label="主管姓名"> <a-form-item label="学校名称">
<a-input <a-input
style="width: 300px;border: 1px solid #ffa940;" style="width: 300px;border: 1px solid #ffa940;"
placeholder="请输入主管姓名" placeholder="请输入学校名称"
v-model:value="searchName" v-model:value="searchName"
@click="handleSearch" @click="handleSearch"
@input="filterNameInput" @input="filterNameInput"
@ -434,12 +435,18 @@ const settlement = async () => {
@input="filterPhoneInput" @input="filterPhoneInput"
/> />
</a-form-item> </a-form-item>
<a-button @click="handleSearch" style="margin-right: 10px" class="custom-button">搜索</a-button>
<a-button @click="reset">重置</a-button> <!-- 将按钮和标签包裹在button-group中 -->
<div class="button-group">
<a-button @click="handleSearch" class="custom-button">搜索</a-button>
<a-button @click="reset" class="custom-button">重置</a-button>
<div class="proportion-tag">
抽成占比<a-tag color="orange">{{ proportionRate }}</a-tag> 抽成占比<a-tag color="orange">{{ proportionRate }}</a-tag>
<a-button @click="openRateModal" style="margin-left: 10px" class="custom-button">修改比例</a-button> </div>
<a-button @click="settlement" style="margin-left: 10px" class="custom-button">一键结算</a-button> <a-button @click="openRateModal" class="custom-button">修改比例</a-button>
</a-space> <a-button @click="settlement" class="custom-button">一键结算</a-button>
</div>
</div>
</a-form> </a-form>
</div> </div>
<!-- 修改比例弹窗 --> <!-- 修改比例弹窗 -->
@ -505,6 +512,45 @@ const settlement = async () => {
box-shadow: 0 2px 8px rgba(0,0,0,0.1); box-shadow: 0 2px 8px rgba(0,0,0,0.1);
} }
/* 使用弹性盒子布局并允许换行 */
.flex-container {
display: flex;
flex-wrap: wrap;
gap: 16px;
width: 100%;
}
/* 表单行样式 */
.search-box .ant-form-item {
margin-bottom: 0;
display: flex;
align-items: center;
flex: 1 1 auto;
}
/* 按钮组容器 */
.button-group {
display: flex;
flex-wrap: wrap;
gap: 10px;
align-items: center;
flex: 1 1 100%; /* 默认占据整行 */
}
/* 抽成比例标签容器 */
.proportion-tag {
display: flex;
align-items: center;
gap: 8px;
}
/* 在中等屏幕以上时调整按钮组布局 */
@media (min-width: 992px) {
.button-group {
flex: 0 1 auto; /* 根据内容自适应宽度 */
}
}
/* 搜索框输入框聚焦和悬浮状态边框为橙色 */ /* 搜索框输入框聚焦和悬浮状态边框为橙色 */
.search-box .custom-search :deep(.ant-input:hover), .search-box .custom-search :deep(.ant-input:hover),
.search-box .custom-search :deep(.ant-input:focus) { .search-box .custom-search :deep(.ant-input:focus) {
@ -517,6 +563,7 @@ const settlement = async () => {
background-color: #ffa940; background-color: #ffa940;
color: #fff; color: #fff;
border: 1px solid #ffa940; border: 1px solid #ffa940;
white-space: nowrap; /* 防止按钮文字换行 */
} }
.custom-button:hover, .custom-button:hover,
@ -542,4 +589,21 @@ const settlement = async () => {
.custom-search :deep(.ant-input) { .custom-search :deep(.ant-input) {
border-right-color: #ffa940; border-right-color: #ffa940;
} }
/* 响应式调整 */
@media (max-width: 1200px) {
.search-box .ant-form-item {
min-width: 200px;
}
}
@media (max-width: 768px) {
.search-box .ant-form-item {
width: 100%;
}
.search-box .ant-input {
width: 100% !important;
}
}
</style> </style>

View File

@ -67,7 +67,7 @@ const total = ref(0);
// 表格列定义 // 表格列定义
const columns = [ const columns = [
{ title: '用户昵称', dataIndex: 'nickName', key: 'nickName' }, { title: '学校名称', dataIndex: 'nickName', key: 'nickName' },
{ title: '手机号', dataIndex: 'phoneNumber', key: 'phoneNumber' }, { title: '手机号', dataIndex: 'phoneNumber', key: 'phoneNumber' },
{ title: '员工数量', dataIndex: 'empCount', key: 'empCount' }, { title: '员工数量', dataIndex: 'empCount', key: 'empCount' },
{ title: '推广人数', dataIndex: 'promoCount', key: 'promoCount' }, { title: '推广人数', dataIndex: 'promoCount', key: 'promoCount' },
@ -154,6 +154,10 @@ const resetSearch = () => {
queryParams.endDate = ''; queryParams.endDate = '';
queryParams.sortField = ''; queryParams.sortField = '';
queryParams.sortOrder = ''; queryParams.sortOrder = '';
// 重置当前页码为1
queryParams.current = 1;
fetchPerformanceRank(); fetchPerformanceRank();
}; };
@ -162,6 +166,40 @@ const handleDateChange = (_: any, dateStrings: [string, string]) => {
queryParams.startDate = dateStrings[0] || ''; queryParams.startDate = dateStrings[0] || '';
queryParams.endDate = dateStrings[1] || ''; queryParams.endDate = dateStrings[1] || '';
}; };
// 处理搜索操作(新增方法)
const handleSearch = () => {
// 重置当前页码为1
queryParams.current = 1;
fetchPerformanceRank();
};
const handleSearchPhoneInput = (e: any) => {
let value = e.target.value;
// 只允许输入数字并截取前11位
const newValue = value.replace(/[^0-9]/g, '').slice(0, 11);
// 直接更新 queryParams.phoneNumber
queryParams.phoneNumber = newValue;
};
// 新增 onKeyDown 事件处理函数
const handleKeyDown = (e: KeyboardEvent) => {
const allowedKeys = [
'Backspace',
'Delete',
'ArrowLeft',
'ArrowRight',
'Tab',
];
// 只允许输入数字和控制键
if (!/[0-9]/.test(e.key) && !allowedKeys.includes(e.key)) {
e.preventDefault();
}
};
</script> </script>
<template> <template>
@ -204,6 +242,9 @@ const handleDateChange = (_: any, dateStrings: [string, string]) => {
v-model:value="queryParams.phoneNumber" v-model:value="queryParams.phoneNumber"
placeholder="手机号" placeholder="手机号"
style="width: 150px; margin-right: 10px;" style="width: 150px; margin-right: 10px;"
:maxlength="11"
@input="handleSearchPhoneInput"
@keydown="handleKeyDown"
/> />
<Input <Input
@ -219,7 +260,8 @@ const handleDateChange = (_: any, dateStrings: [string, string]) => {
style="margin-right: 10px; width: 350px;" style="margin-right: 10px; width: 350px;"
/> />
<Button type="primary" @click="fetchPerformanceRank" style="margin-right: 10px;"> <!-- 修改这里使用新的handleSearch方法 -->
<Button type="primary" @click="handleSearch" style="margin-right: 10px;">
搜索 搜索
</Button> </Button>
@ -265,12 +307,11 @@ const handleDateChange = (_: any, dateStrings: [string, string]) => {
</template> </template>
<style scoped> <style scoped>
/* 原有样式保持不变 */
.performance-container { .performance-container {
padding: 20px; padding: 20px;
/* 温馨的乳白色背景,带一点暖色调 */
background: #fffaf0; background: #fffaf0;
border-radius: 12px; border-radius: 12px;
/* 轻柔的暖色阴影 */
box-shadow: 0 4px 12px rgba(255, 164, 64, 0.2); box-shadow: 0 4px 12px rgba(255, 164, 64, 0.2);
transition: transform 0.3s; transition: transform 0.3s;
} }
@ -288,7 +329,6 @@ const handleDateChange = (_: any, dateStrings: [string, string]) => {
h2 { h2 {
margin: 0; margin: 0;
/* 使用主题色 */
color: #ffa940; color: #ffa940;
font-size: 20px; font-size: 20px;
font-weight: 600; font-weight: 600;
@ -297,7 +337,6 @@ h2 {
.filter-container { .filter-container {
width: 100%; width: 100%;
margin-top: 15px; margin-top: 15px;
/* 柔和的卡片背景 */
background: #fff5e6; background: #fff5e6;
border: 1px solid #ffd591; border: 1px solid #ffd591;
border-radius: 8px; border-radius: 8px;
@ -326,8 +365,6 @@ h2 {
text-align: right; text-align: right;
} }
/* 单元格边框去掉横线,更显高级 */ /* 单元格边框去掉横线,更显高级 */
:deep(.ant-table-cell) { :deep(.ant-table-cell) {
border-bottom: none; border-bottom: none;

View File

@ -49,7 +49,7 @@
</template> </template>
</a-form-item> </a-form-item>
<a-form-item label="头像" name="userAvatar" required> <a-form-item label="头像" name="userAvatar">
<a-upload <a-upload
name="file" name="file"
list-type="picture-card" list-type="picture-card"
@ -216,7 +216,7 @@
</a-input> </a-input>
</a-form-item> </a-form-item>
<a-form-item label="头像" name="userAvatar" required> <a-form-item label="头像" name="userAvatar">
<a-upload <a-upload
name="file" name="file"
list-type="picture-card" list-type="picture-card"
@ -339,7 +339,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, ref,computed } from "vue"; import { onMounted, ref,computed,nextTick } from "vue";
import myAxios from "../../api/myAxios.ts"; import myAxios from "../../api/myAxios.ts";
import { message, Modal } from "ant-design-vue"; import { message, Modal } from "ant-design-vue";
import { PlusOutlined, LoadingOutlined } from '@ant-design/icons-vue'; import { PlusOutlined, LoadingOutlined } from '@ant-design/icons-vue';
@ -381,19 +381,19 @@ const handleUpload = async ({ file }: { file: File }) => {
uploadLoading.value = true; uploadLoading.value = true;
const storedToken = localStorage.getItem('token'); const storedToken = localStorage.getItem('token');
const res:any = await myAxios.post('/file/upload?biz=avatar', form, { const res:any = await myAxios.post('/file/upload?biz=avatar', form, {
headers: { headers: {
Authorization: storedToken, Authorization: storedToken,
} }
}); });
console.log(res)
if (res.code === 1) { if (res.code === 1) {
formData.value.userAvatar = res.data; formData.value.userAvatar = res.data;
previewImage.value = URL.createObjectURL(file); previewImage.value = URL.createObjectURL(file);
message.success('上传成功'); message.success('上传成功');
// 关键:清除整个表单的校验状态
formRef.value?.clearValidate();
} else { } else {
message.error(res.message || '上传失败'); message.error(res.message || '上传失败');
} }
@ -500,6 +500,13 @@ const formRules = {
}, },
trigger: ['input', 'blur'] trigger: ['input', 'blur']
} }
],
userAvatar: [
{
required: true,
message: '请上传头像',
trigger: 'change'
}
] ]
} }
@ -695,6 +702,13 @@ const editFormRules = {
}, },
trigger: ['input', 'blur'] trigger: ['input', 'blur']
} }
],
userAvatar: [
{
required: true,
message: '请上传头像',
trigger: 'change'
}
] ]
}; };
@ -844,7 +858,8 @@ const handlePhoneSearch = async () => {
{ {
current: 1, current: 1,
pageSize: 10, pageSize: 10,
phoneNumber: searchPhone.value phoneNumber: searchPhone.value,
userRole: "admin"
}, },
{ headers: { Authorization: storedToken } } { headers: { Authorization: storedToken } }
); );
@ -1012,7 +1027,7 @@ const editForm = ref({
userAccount: '', userAccount: '',
userPassword: '', userPassword: '',
invitationCode: '', invitationCode: '',
userRole: '', userRole: 'admin',
parentUserId: 0, parentUserId: 0,
SuperUserList: '' SuperUserList: ''
}); });
@ -1040,6 +1055,9 @@ const handleEditUpload = async ({ file }: { file: File }) => {
editForm.value.userAvatar = res.data; editForm.value.userAvatar = res.data;
editPreviewImage.value = URL.createObjectURL(file); editPreviewImage.value = URL.createObjectURL(file);
message.success('头像更新成功'); message.success('头像更新成功');
// 关键:清除整个编辑表单的校验状态
editFormRef.value?.clearValidate();
} else { } else {
message.error(res.message || '头像上传失败'); message.error(res.message || '头像上传失败');
} }
@ -1050,6 +1068,9 @@ const handleEditUpload = async ({ file }: { file: File }) => {
editUploadLoading.value = false; editUploadLoading.value = false;
} }
}; };
const showDrawer = (record: any) => { const showDrawer = (record: any) => {
selectedUser.value = record; selectedUser.value = record;
editForm.value = { editForm.value = {
@ -1162,6 +1183,7 @@ const formRef = ref();
const showModal = () => { const showModal = () => {
openUser.value = true; openUser.value = true;
formData.value = { formData.value = {
@ -1173,6 +1195,9 @@ const showModal = () => {
}; };
previewImage.value = ''; previewImage.value = '';
nextTick(() => {
formRef.value?.clearValidate();
});
}; };
const handleOk = (e: MouseEvent) => { const handleOk = (e: MouseEvent) => {
@ -1497,4 +1522,11 @@ const handleSubmit = async () => {
:deep(.ant-checkbox-wrapper-disabled) { :deep(.ant-checkbox-wrapper-disabled) {
display: none; display: none;
} }
:deep(.custom-search .ant-input:focus),
:deep(.custom-search .ant-input:hover),
:deep(.custom-search .ant-input-focused) {
border-color: #ffa940 !important;
box-shadow: 0 0 0 2px rgba(255, 169, 64, 0.2) !important;
}
</style> </style>

View File

@ -743,4 +743,10 @@ const showDrawer = (record: any) => {
:deep(.ant-checkbox-wrapper-disabled) { :deep(.ant-checkbox-wrapper-disabled) {
display: none; display: none;
} }
:deep(.custom-search .ant-input:focus),
:deep(.custom-search .ant-input:hover),
:deep(.custom-search .ant-input-focused) {
border-color: #ffa940 !important;
box-shadow: 0 0 0 2px rgba(255, 169, 64, 0.2) !important;
}
</style> </style>

View File

@ -804,4 +804,11 @@ const showDrawer = (record: any) => {
:deep(.ant-checkbox-wrapper-disabled) { :deep(.ant-checkbox-wrapper-disabled) {
display: none; display: none;
} }
:deep(.custom-search .ant-input:focus),
:deep(.custom-search .ant-input:hover),
:deep(.custom-search .ant-input-focused) {
border-color: #ffa940 !important;
box-shadow: 0 0 0 2px rgba(255, 169, 64, 0.2) !important;
}
</style> </style>

View File

@ -695,4 +695,11 @@ const handleTableChange = (pag: any, _: any, sorter: any) => {
:deep(.ant-checkbox-wrapper-disabled) { :deep(.ant-checkbox-wrapper-disabled) {
display: none; display: none;
} }
:deep(.custom-search .ant-input:focus),
:deep(.custom-search .ant-input:hover),
:deep(.custom-search .ant-input-focused) {
border-color: #ffa940 !important;
box-shadow: 0 0 0 2px rgba(255, 169, 64, 0.2) !important;
}
</style> </style>

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/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/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/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"}