8月10日的bug的修改
This commit is contained in:
@ -1,4 +1,5 @@
|
||||
<template>
|
||||
<div class="sidebar-container">
|
||||
<a-menu
|
||||
v-model:selectedKeys="selectedKeys"
|
||||
mode="inline"
|
||||
@ -32,16 +33,16 @@
|
||||
<!-- <a-menu-item key="/project">接单管理</a-menu-item>-->
|
||||
<!-- </a-sub-menu>-->
|
||||
|
||||
<a-sub-menu>
|
||||
<template #title>
|
||||
<span>
|
||||
<CommentOutlined />
|
||||
<span>结算管理</span>
|
||||
</span>
|
||||
</template>
|
||||
<a-menu-item key="/applicationRecord">推广码申请记录</a-menu-item>
|
||||
<a-menu-item key="/withdrawalApplicationRecord">提现申请记录</a-menu-item>
|
||||
</a-sub-menu>
|
||||
<!-- <a-sub-menu>-->
|
||||
<!-- <template #title>-->
|
||||
<!-- <span>-->
|
||||
<!-- <CommentOutlined />-->
|
||||
<!-- <span>结算管理</span>-->
|
||||
<!-- </span>-->
|
||||
<!-- </template>-->
|
||||
<!-- <a-menu-item key="/applicationRecord">推广码申请记录</a-menu-item>-->
|
||||
<!-- <a-menu-item key="/withdrawalApplicationRecord">提现申请记录</a-menu-item>-->
|
||||
<!-- </a-sub-menu>-->
|
||||
<a-sub-menu>
|
||||
<template #title>
|
||||
<span>
|
||||
@ -74,12 +75,19 @@
|
||||
<a-menu-item key="/employeePerformanceRanking">员工绩效排行</a-menu-item>
|
||||
|
||||
</a-sub-menu>
|
||||
<a-menu-item key="/employeeApplication">
|
||||
<CommentOutlined />
|
||||
<span>员工申请管理</span>
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
<a-sub-menu>
|
||||
<template #title>
|
||||
<span>
|
||||
<FieldTimeOutlined />
|
||||
<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>
|
||||
|
||||
<script setup lang="ts">
|
||||
@ -109,6 +117,29 @@ const handleClick = (item: any) => {
|
||||
</script>
|
||||
|
||||
<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-submenu-title) {
|
||||
|
@ -177,6 +177,11 @@ export const routes = [
|
||||
name:'员工绩效排行',
|
||||
component: ()=> import("../view/performance/employeePerformanceRanking.vue")
|
||||
},
|
||||
{
|
||||
path:'/applicationInstructions',
|
||||
name:'员工申请须知',
|
||||
component: ()=> import("../view/employeeApplication/applicationInstructions.vue")
|
||||
},
|
||||
]
|
||||
},
|
||||
]
|
@ -2,7 +2,7 @@
|
||||
<div id="login">
|
||||
<form>
|
||||
<div class="box" @submit.prevent>
|
||||
<h2>欢迎登录青橙校园管理端</h2>
|
||||
<h2>欢迎登录丁香花校园管理端</h2>
|
||||
<div class="input-box">
|
||||
|
||||
<input type="text" placeholder="账号" v-model="userAccount"/>
|
||||
|
@ -741,7 +741,7 @@ const handlePriceInput = (value: number, field: keyof CourseDetail) => {
|
||||
border-radius: 0.75rem;
|
||||
overflow: hidden;
|
||||
transition: all 0.3s ease;
|
||||
max-height: 500px;
|
||||
max-height: 2000px;
|
||||
flex-grow: 1;
|
||||
padding: 10px;
|
||||
background: white;
|
||||
|
@ -504,4 +504,10 @@ const showDetails = (id: string) => {
|
||||
background-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>
|
@ -82,6 +82,7 @@
|
||||
v-if="record.orderStatus === '交易成功'"
|
||||
size="small"
|
||||
primary
|
||||
@click="showRefundConfirm(record)"
|
||||
>
|
||||
退款
|
||||
</a-button>
|
||||
@ -94,9 +95,48 @@
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, ref } from "vue";
|
||||
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";
|
||||
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 selectedRowKeys = ref<number[]>([]); // 存储选中的行ID
|
||||
|
||||
@ -109,7 +149,7 @@ const searchParams = ref({
|
||||
current: 1,
|
||||
pageSize: 10,
|
||||
sortField: "id",
|
||||
sortOrder: "ascend",
|
||||
sortOrder: "descend",
|
||||
orderNumber: "",
|
||||
orderStatus: ""
|
||||
});
|
||||
@ -252,7 +292,7 @@ const handleTableChange = (pag: any, _filters: any, sorter: any) => {
|
||||
} else {
|
||||
|
||||
searchParams.value.sortField = "id";
|
||||
searchParams.value.sortOrder = "ascend";
|
||||
searchParams.value.sortOrder = "descend";
|
||||
}
|
||||
|
||||
// 同步到分页组件
|
||||
@ -306,7 +346,7 @@ const reset = () => {
|
||||
current: 1,
|
||||
pageSize: 10,
|
||||
sortField: "id",
|
||||
sortOrder: "ascend",
|
||||
sortOrder: "descend",
|
||||
orderNumber: "",
|
||||
orderStatus: ""
|
||||
};
|
||||
@ -347,4 +387,11 @@ const reset = () => {
|
||||
background-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>
|
307
src/view/employeeApplication/applicationInstructions.vue
Normal file
307
src/view/employeeApplication/applicationInstructions.vue
Normal 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>
|
@ -1,5 +1,5 @@
|
||||
<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 { message } from "ant-design-vue";
|
||||
import type { TableProps } from 'ant-design-vue';
|
||||
@ -21,8 +21,7 @@ const columns = [
|
||||
key: 'name',
|
||||
width: 120,
|
||||
fixed: 'left',
|
||||
align: 'center',
|
||||
sorter: true
|
||||
align: 'center'
|
||||
},
|
||||
{
|
||||
title: '手机号',
|
||||
@ -255,6 +254,50 @@ onMounted(() => {
|
||||
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>
|
||||
|
||||
<template>
|
||||
@ -265,12 +308,14 @@ onMounted(() => {
|
||||
<a-form-item label="申请人姓名">
|
||||
<a-input-search
|
||||
style="width: 300px"
|
||||
placeholder="请输入姓名"
|
||||
placeholder="请输入姓名(仅中文)"
|
||||
class="custom-search"
|
||||
enter-button
|
||||
v-model:value="searchName"
|
||||
@pressEnter="handleSearch"
|
||||
@search="handleSearch"
|
||||
@input="handleNameInput"
|
||||
@paste="handlePaste"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="手机号">
|
||||
@ -282,6 +327,7 @@ onMounted(() => {
|
||||
@search="handleSearch"
|
||||
enter-button
|
||||
class="custom-search"
|
||||
@input="filterPhoneInput"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-button @click="reset">重置</a-button>
|
||||
@ -379,38 +425,11 @@ onMounted(() => {
|
||||
border-right-color: #ffa940;
|
||||
}
|
||||
|
||||
/* 审核人选择弹窗样式 */
|
||||
.supervisor-list {
|
||||
max-height: 400px;
|
||||
overflow-y: auto;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.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(.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;
|
||||
}
|
||||
|
||||
:deep(.ant-list-item) {
|
||||
@ -427,17 +446,4 @@ onMounted(() => {
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
/* 操作按钮样式 */
|
||||
.action-btn {
|
||||
margin: 0 4px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.action-btn.primary {
|
||||
color: #1890ff;
|
||||
}
|
||||
|
||||
.action-btn.danger {
|
||||
color: #ff4d4f;
|
||||
}
|
||||
</style>
|
@ -124,6 +124,7 @@
|
||||
:ok-text="'确认驳回'"
|
||||
:cancel-text="'取消'"
|
||||
okType="danger"
|
||||
class="reject-modal"
|
||||
>
|
||||
<a-alert type="warning" show-icon class="reject-alert">
|
||||
<template #message>
|
||||
@ -135,18 +136,19 @@
|
||||
</a-alert>
|
||||
|
||||
<div class="reject-form">
|
||||
<a-form layout="vertical">
|
||||
<a-form-item label="驳回原因" required>
|
||||
<a-textarea
|
||||
v-model:value="rejectReason"
|
||||
placeholder="请输入详细的驳回原因..."
|
||||
:rows="4"
|
||||
:maxlength="200"
|
||||
show-count
|
||||
class="reject-reason"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
<div class="reject-header">
|
||||
<h4 class="reject-title">* 驳回原因</h4>
|
||||
<div class="char-counter">
|
||||
{{ rejectReason.length }} / 200
|
||||
</div>
|
||||
</div>
|
||||
<a-textarea
|
||||
v-model:value="rejectReason"
|
||||
placeholder="请输入详细的驳回原因..."
|
||||
:rows="4"
|
||||
:maxlength="200"
|
||||
class="reject-reason"
|
||||
/>
|
||||
</div>
|
||||
</a-modal>
|
||||
</div>
|
||||
@ -632,7 +634,11 @@ const submitReject = async () => {
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
/* 驳回弹窗样式 */
|
||||
/* 驳回弹窗样式优化 */
|
||||
.reject-modal :deep(.ant-modal-body) {
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.reject-alert {
|
||||
margin-bottom: 20px;
|
||||
border-radius: 8px;
|
||||
@ -645,8 +651,37 @@ const submitReject = async () => {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.divider {
|
||||
height: 1px;
|
||||
background: #f0f0f0;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.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 {
|
||||
@ -655,6 +690,8 @@ const submitReject = async () => {
|
||||
padding: 12px;
|
||||
border: 1px solid #d9d9d9;
|
||||
transition: all 0.3s;
|
||||
width: 100%;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.reject-reason:focus {
|
||||
@ -662,6 +699,13 @@ const submitReject = async () => {
|
||||
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) {
|
||||
margin: 40px 0;
|
||||
|
@ -294,6 +294,7 @@ const updateProportionRate = async () => {
|
||||
// 修复语法错误:移除命名参数
|
||||
proportionRate.value = parseFloat(rateValue.toFixed(2)).toString();
|
||||
visible.value = false;
|
||||
fetchEmployeePerformance();
|
||||
} else {
|
||||
message.error(response.message || "更新抽成比例失败");
|
||||
}
|
||||
@ -525,4 +526,5 @@ const filterRateInput = (e: Event) => {
|
||||
background-color: #ffa940;
|
||||
border-color: #ffa940;
|
||||
}
|
||||
|
||||
</style>
|
@ -167,6 +167,33 @@ const handleDateChange = (_: any, dateStrings: [string, string]) => {
|
||||
queryParams.startDate = dateStrings[0] || '';
|
||||
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>
|
||||
|
||||
<template>
|
||||
@ -212,6 +239,9 @@ const handleDateChange = (_: any, dateStrings: [string, string]) => {
|
||||
placeholder="手机号"
|
||||
style="width: 180px; margin-right: 10px;"
|
||||
allow-clear
|
||||
:maxlength="11"
|
||||
@input="handleSearchPhoneInput"
|
||||
@keydown="handleKeyDown"
|
||||
/>
|
||||
|
||||
<Input
|
||||
|
@ -25,7 +25,7 @@ const columns = [
|
||||
align: 'center'
|
||||
},
|
||||
{
|
||||
title: '主管姓名',
|
||||
title: '学校名称',
|
||||
dataIndex: 'nickName',
|
||||
key: 'nickName',
|
||||
width: 100,
|
||||
@ -290,6 +290,7 @@ const updateProportionRate = async () => {
|
||||
message.success("抽成比例更新成功");
|
||||
proportionRate.value = rateValue.toFixed(2);
|
||||
visible.value = false;
|
||||
fetchManagerPerformance()
|
||||
} else {
|
||||
message.error(response.message || "更新抽成比例失败");
|
||||
}
|
||||
@ -414,11 +415,11 @@ const settlement = async () => {
|
||||
<!-- 搜索框 -->
|
||||
<div class="search-box">
|
||||
<a-form layout="inline">
|
||||
<a-space>
|
||||
<a-form-item label="主管姓名">
|
||||
<div class="flex-container">
|
||||
<a-form-item label="学校名称">
|
||||
<a-input
|
||||
style="width: 300px;border: 1px solid #ffa940;"
|
||||
placeholder="请输入主管姓名"
|
||||
placeholder="请输入学校名称"
|
||||
v-model:value="searchName"
|
||||
@click="handleSearch"
|
||||
@input="filterNameInput"
|
||||
@ -434,12 +435,18 @@ const settlement = async () => {
|
||||
@input="filterPhoneInput"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-button @click="handleSearch" style="margin-right: 10px" class="custom-button">搜索</a-button>
|
||||
<a-button @click="reset">重置</a-button>
|
||||
抽成占比<a-tag color="orange">{{ proportionRate }}</a-tag>
|
||||
<a-button @click="openRateModal" style="margin-left: 10px" class="custom-button">修改比例</a-button>
|
||||
<a-button @click="settlement" style="margin-left: 10px" class="custom-button">一键结算</a-button>
|
||||
</a-space>
|
||||
|
||||
<!-- 将按钮和标签包裹在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>
|
||||
</div>
|
||||
<a-button @click="openRateModal" class="custom-button">修改比例</a-button>
|
||||
<a-button @click="settlement" class="custom-button">一键结算</a-button>
|
||||
</div>
|
||||
</div>
|
||||
</a-form>
|
||||
</div>
|
||||
<!-- 修改比例弹窗 -->
|
||||
@ -505,6 +512,45 @@ const settlement = async () => {
|
||||
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:focus) {
|
||||
@ -517,6 +563,7 @@ const settlement = async () => {
|
||||
background-color: #ffa940;
|
||||
color: #fff;
|
||||
border: 1px solid #ffa940;
|
||||
white-space: nowrap; /* 防止按钮文字换行 */
|
||||
}
|
||||
|
||||
.custom-button:hover,
|
||||
@ -542,4 +589,21 @@ const settlement = async () => {
|
||||
.custom-search :deep(.ant-input) {
|
||||
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>
|
@ -67,7 +67,7 @@ const total = ref(0);
|
||||
|
||||
// 表格列定义
|
||||
const columns = [
|
||||
{ title: '用户昵称', dataIndex: 'nickName', key: 'nickName' },
|
||||
{ title: '学校名称', dataIndex: 'nickName', key: 'nickName' },
|
||||
{ title: '手机号', dataIndex: 'phoneNumber', key: 'phoneNumber' },
|
||||
{ title: '员工数量', dataIndex: 'empCount', key: 'empCount' },
|
||||
{ title: '推广人数', dataIndex: 'promoCount', key: 'promoCount' },
|
||||
@ -154,6 +154,10 @@ const resetSearch = () => {
|
||||
queryParams.endDate = '';
|
||||
queryParams.sortField = '';
|
||||
queryParams.sortOrder = '';
|
||||
|
||||
// 重置当前页码为1
|
||||
queryParams.current = 1;
|
||||
|
||||
fetchPerformanceRank();
|
||||
};
|
||||
|
||||
@ -162,6 +166,40 @@ const handleDateChange = (_: any, dateStrings: [string, string]) => {
|
||||
queryParams.startDate = dateStrings[0] || '';
|
||||
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>
|
||||
|
||||
<template>
|
||||
@ -204,6 +242,9 @@ const handleDateChange = (_: any, dateStrings: [string, string]) => {
|
||||
v-model:value="queryParams.phoneNumber"
|
||||
placeholder="手机号"
|
||||
style="width: 150px; margin-right: 10px;"
|
||||
:maxlength="11"
|
||||
@input="handleSearchPhoneInput"
|
||||
@keydown="handleKeyDown"
|
||||
/>
|
||||
|
||||
<Input
|
||||
@ -219,7 +260,8 @@ const handleDateChange = (_: any, dateStrings: [string, string]) => {
|
||||
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>
|
||||
|
||||
@ -265,12 +307,11 @@ const handleDateChange = (_: any, dateStrings: [string, string]) => {
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
/* 原有样式保持不变 */
|
||||
.performance-container {
|
||||
padding: 20px;
|
||||
/* 温馨的乳白色背景,带一点暖色调 */
|
||||
background: #fffaf0;
|
||||
border-radius: 12px;
|
||||
/* 轻柔的暖色阴影 */
|
||||
box-shadow: 0 4px 12px rgba(255, 164, 64, 0.2);
|
||||
transition: transform 0.3s;
|
||||
}
|
||||
@ -288,7 +329,6 @@ const handleDateChange = (_: any, dateStrings: [string, string]) => {
|
||||
|
||||
h2 {
|
||||
margin: 0;
|
||||
/* 使用主题色 */
|
||||
color: #ffa940;
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
@ -297,7 +337,6 @@ h2 {
|
||||
.filter-container {
|
||||
width: 100%;
|
||||
margin-top: 15px;
|
||||
/* 柔和的卡片背景 */
|
||||
background: #fff5e6;
|
||||
border: 1px solid #ffd591;
|
||||
border-radius: 8px;
|
||||
@ -326,8 +365,6 @@ h2 {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* 单元格边框去掉横线,更显高级 */
|
||||
:deep(.ant-table-cell) {
|
||||
border-bottom: none;
|
||||
@ -372,4 +409,4 @@ h2 {
|
||||
:deep(.ant-picker-focused) {
|
||||
box-shadow: 0 0 0 2px rgba(255, 164, 64, 0.2) !important;
|
||||
}
|
||||
</style>
|
||||
</style>
|
@ -49,7 +49,7 @@
|
||||
</template>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="头像" name="userAvatar" required>
|
||||
<a-form-item label="头像" name="userAvatar">
|
||||
<a-upload
|
||||
name="file"
|
||||
list-type="picture-card"
|
||||
@ -216,7 +216,7 @@
|
||||
</a-input>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="头像" name="userAvatar" required>
|
||||
<a-form-item label="头像" name="userAvatar">
|
||||
<a-upload
|
||||
name="file"
|
||||
list-type="picture-card"
|
||||
@ -339,7 +339,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, ref,computed } from "vue";
|
||||
import { onMounted, ref,computed,nextTick } from "vue";
|
||||
import myAxios from "../../api/myAxios.ts";
|
||||
import { message, Modal } from "ant-design-vue";
|
||||
import { PlusOutlined, LoadingOutlined } from '@ant-design/icons-vue';
|
||||
@ -381,19 +381,19 @@ const handleUpload = async ({ file }: { file: File }) => {
|
||||
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)
|
||||
|
||||
if (res.code === 1) {
|
||||
formData.value.userAvatar = res.data;
|
||||
previewImage.value = URL.createObjectURL(file);
|
||||
message.success('上传成功');
|
||||
|
||||
// 关键:清除整个表单的校验状态
|
||||
formRef.value?.clearValidate();
|
||||
} else {
|
||||
message.error(res.message || '上传失败');
|
||||
}
|
||||
@ -500,6 +500,13 @@ const formRules = {
|
||||
},
|
||||
trigger: ['input', 'blur']
|
||||
}
|
||||
],
|
||||
userAvatar: [
|
||||
{
|
||||
required: true,
|
||||
message: '请上传头像',
|
||||
trigger: 'change'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@ -695,6 +702,13 @@ const editFormRules = {
|
||||
},
|
||||
trigger: ['input', 'blur']
|
||||
}
|
||||
],
|
||||
userAvatar: [
|
||||
{
|
||||
required: true,
|
||||
message: '请上传头像',
|
||||
trigger: 'change'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
@ -844,7 +858,8 @@ const handlePhoneSearch = async () => {
|
||||
{
|
||||
current: 1,
|
||||
pageSize: 10,
|
||||
phoneNumber: searchPhone.value
|
||||
phoneNumber: searchPhone.value,
|
||||
userRole: "admin"
|
||||
},
|
||||
{ headers: { Authorization: storedToken } }
|
||||
);
|
||||
@ -1012,7 +1027,7 @@ const editForm = ref({
|
||||
userAccount: '',
|
||||
userPassword: '',
|
||||
invitationCode: '',
|
||||
userRole: '',
|
||||
userRole: 'admin',
|
||||
parentUserId: 0,
|
||||
SuperUserList: ''
|
||||
});
|
||||
@ -1040,6 +1055,9 @@ const handleEditUpload = async ({ file }: { file: File }) => {
|
||||
editForm.value.userAvatar = res.data;
|
||||
editPreviewImage.value = URL.createObjectURL(file);
|
||||
message.success('头像更新成功');
|
||||
|
||||
// 关键:清除整个编辑表单的校验状态
|
||||
editFormRef.value?.clearValidate();
|
||||
} else {
|
||||
message.error(res.message || '头像上传失败');
|
||||
}
|
||||
@ -1050,6 +1068,9 @@ const handleEditUpload = async ({ file }: { file: File }) => {
|
||||
editUploadLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
const showDrawer = (record: any) => {
|
||||
selectedUser.value = record;
|
||||
editForm.value = {
|
||||
@ -1162,6 +1183,7 @@ const formRef = ref();
|
||||
|
||||
|
||||
|
||||
|
||||
const showModal = () => {
|
||||
openUser.value = true;
|
||||
formData.value = {
|
||||
@ -1173,6 +1195,9 @@ const showModal = () => {
|
||||
};
|
||||
|
||||
previewImage.value = '';
|
||||
nextTick(() => {
|
||||
formRef.value?.clearValidate();
|
||||
});
|
||||
};
|
||||
|
||||
const handleOk = (e: MouseEvent) => {
|
||||
@ -1497,4 +1522,11 @@ const handleSubmit = async () => {
|
||||
:deep(.ant-checkbox-wrapper-disabled) {
|
||||
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>
|
@ -743,4 +743,10 @@ const showDrawer = (record: any) => {
|
||||
:deep(.ant-checkbox-wrapper-disabled) {
|
||||
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>
|
@ -804,4 +804,11 @@ const showDrawer = (record: any) => {
|
||||
:deep(.ant-checkbox-wrapper-disabled) {
|
||||
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>
|
@ -695,4 +695,11 @@ const handleTableChange = (pag: any, _: any, sorter: any) => {
|
||||
:deep(.ant-checkbox-wrapper-disabled) {
|
||||
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>
|
Reference in New Issue
Block a user