Merge remote-tracking branch 'origin/master'

This commit is contained in:
2025-09-08 17:13:35 +08:00
4 changed files with 245 additions and 116 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

View File

@@ -24,19 +24,16 @@
<!-- 头部导航 -->
<div class="header">
<router-link
<div
v-for="(item, index) in headerItems"
:key="index"
:to="item.path"
class="header-item"
custom
v-slot="{ navigate }"
>
<div @click="navigate" class="header-item-content">
<div @click="item.text === '系统公告' ? handleSystemAnnouncementClick() : $router.push(item.path)" class="header-item-content">
<img :src="item.image" :alt="item.text" class="header-image" />
<div class="header-text">{{ item.text }}</div>
</div>
</router-link>
</div>
</div>
<!-- 修改后的操作区域 - 三个图片并排 -->
@@ -75,7 +72,7 @@
<el-dialog
v-model="showWelcomeDialog"
width="90%"
:style="{ height: '425px' }"
:style="{ height: isSystemAnnouncementClick ? '525px' : '425px' }"
:show-close="true"
:close-on-click-modal="true"
:close-on-press-escape="true"
@@ -87,10 +84,10 @@
<div class="welcome-icon">🎉</div>
<h3>融汇通更新</h3>
<div class="welcome-features">
<div class="announcements-container" v-if="unreadAnnouncements.length > 0">
<div class="announcements-container" v-if="displayAnnouncements.length > 0">
<div
class="announcement-item"
v-for="announcement in unreadAnnouncements"
v-for="announcement in displayAnnouncements"
:key="announcement.id"
>
<div class="announcement-header">
@@ -105,7 +102,7 @@
</div>
</div>
<div v-else class="no-announcements">
<span>暂无未读更新信息</span>
<span>{{ isSystemAnnouncementClick ? '暂无公告信息' : '暂无未读更新信息' }}</span>
</div>
</div>
</div>
@@ -203,6 +200,20 @@ export default {
return announcements.value.filter(announcement => !announcement.is_read);
});
// 标记是否通过系统公告按钮打开对话框
const isSystemAnnouncementClick = ref(false);
// 计算要显示的公告列表
const displayAnnouncements = computed(() => {
if (isSystemAnnouncementClick.value) {
// 如果是通过系统公告按钮打开,显示所有公告并按创建时间倒序排列(最新的在前)
return [...announcements.value].sort((a, b) => new Date(b.created_at) - new Date(a.created_at));
} else {
// 否则只显示未读公告
return unreadAnnouncements.value;
}
});
// 计算是否应该显示弹窗
const shouldShowDialog = computed(() => {
return unreadAnnouncements.value.length > 0;
@@ -290,8 +301,12 @@ export default {
// 关闭欢迎弹窗并标记所有未读公告为已读
const closeWelcomeDialog = async () => {
await markAllAnnouncementsAsRead();
// 只有在非系统公告点击时才标记为已读
if (!isSystemAnnouncementClick.value) {
await markAllAnnouncementsAsRead();
}
showWelcomeDialog.value = false;
isSystemAnnouncementClick.value = false; // 重置标志
};
// 点击已读按钮
@@ -299,6 +314,13 @@ export default {
showWelcomeDialog.value = false;
};
// 处理系统公告点击事件
const handleSystemAnnouncementClick = () => {
console.log('触发')
isSystemAnnouncementClick.value = true;
showWelcomeDialog.value = true; // 无条件显示对话框
};
return {
modules: [Autoplay, Pagination],
userPoints,
@@ -315,6 +337,9 @@ export default {
getUserPoints, // 如果需要外部调用可以暴露
closeWelcomeDialog,
handleReadClick,
handleSystemAnnouncementClick,
displayAnnouncements,
isSystemAnnouncementClick,
};
},
};

View File

@@ -6,10 +6,6 @@
<h2>用户登录</h2>
<p>欢迎来到炬融圈</p>
</div>
<div class="image">
<img src="/imgs/login.png" alt="炬融圈">
</div>
<el-form
ref="loginFormRef"
@@ -346,33 +342,6 @@ const handleRememberMe = () => {
font-size: 14px;
}
/* 图片容器样式 */
.image {
width: 375px; /* 固定宽度 */
height: 287px; /* 固定高度 */
margin: 0 auto 20px; /* 水平居中,底部留出间距 */
overflow: hidden; /* 隐藏超出容器的部分 */
border-radius: 8px; /* 可选:添加圆角增强视觉效果 */
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); /* 可选:添加轻微阴影 */
}
/* 图片填充样式 */
.image img {
width: 100%; /* 宽度充满容器 */
height: 100%; /* 高度充满容器 */
object-fit: contain;
display: block; /* 去除图片底部默认空白 */
}
/* 响应式适配(小屏设备自动缩放) */
@media (max-width: 375px) {
.image {
width: 100%; /* 在小于375px的屏幕上宽度自适应 */
height: auto; /* 高度按比例自动计算 */
aspect-ratio: 375 / 287; /* 保持原比例 */
}
}
.login-form {
margin-bottom: 20px;
}

View File

@@ -114,28 +114,16 @@
<el-form-item prop="agreement">
<el-checkbox
v-model="registerForm.agreement"
@change="handleAgreementChange"
@click.prevent="showAgreementDialog"
:disabled="true"
>
我已阅读并同意
<el-link
type="primary"
@click="showAgreement"
:class="{ viewed: agreementViewed }"
>
用户协议
</el-link>
<el-link
type="primary"
@click="showPrivacy"
:class="{ viewed: privacyViewed }"
>
隐私政策
</el-link>
<span @click="showAgreementDialog" class="agreement-text">
我已阅读并同意用户协议隐私政策
</span>
</el-checkbox>
<div v-if="!canCheckAgreement" class="agreement-hint">
<div class="agreement-hint">
<el-icon><InfoFilled /></el-icon>
<span>点击查看用户协议和隐私政策</span>
<span>请点击查看用户协议和隐私政策</span>
</div>
</el-form-item>
@@ -170,6 +158,64 @@
<div class="decoration-shape shape-3"></div>
<div class="decoration-shape shape-4"></div>
</div>
<!-- 协议弹窗 -->
<el-dialog
v-model="showAgreementDialogVisible"
title="用户协议和隐私政策"
width="90%"
:style="{ maxWidth: '600px' }"
:show-close="true"
:close-on-click-modal="false"
:close-on-press-escape="false"
center
class="agreement-dialog"
position="bottom"
>
<div class="agreement-content">
<el-tabs v-model="activeTab" @tab-change="handleTabChange">
<el-tab-pane name="agreement">
<template #label>
<span class="tab-label">用户协议</span>
<el-icon v-if="agreementViewed" class="check-icon"><Check /></el-icon>
</template>
<div class="agreement-text-content" ref="agreementContent">
<h3>用户协议</h3>
<p>1. 用户应当遵守法律法规不得发布违法违规内容</p>
<p>2. 用户对自己发布的内容承担全部责任</p>
<p>3. 平台有权对违规内容进行删除或限制</p>
<p>4. 用户应当保护好自己的账号安全</p>
<p>5. 平台保留修改本协议的权利</p>
</div>
</el-tab-pane>
<el-tab-pane name="privacy">
<template #label>
<span class="tab-label">隐私政策</span>
<el-icon v-if="privacyViewed" class="check-icon"><Check /></el-icon>
</template>
<div class="privacy-text-content" ref="privacyContent">
<h3>隐私政策</h3>
<p>1. 我们重视用户隐私保护</p>
<p>2. 我们只收集必要的用户信息</p>
<p>3. 用户信息仅用于提供服务</p>
<p>4. 我们不会向第三方泄露用户信息</p>
<p>5. 用户有权查看修改或删除个人信息</p>
</div>
</el-tab-pane>
</el-tabs>
</div>
<template #footer>
<div class="dialog-footer">
<div class="read-status">
</div>
<div class="dialog-buttons">
<el-button @click="closeAgreementDialog">取消</el-button>
<el-button type="primary" @click="confirmAgreement" :disabled="!(agreementViewed && privacyViewed)">确认已阅读</el-button>
</div>
</div>
</template>
</el-dialog>
</div>
</template>
@@ -187,6 +233,7 @@ import {
CreditCard,
Location,
InfoFilled,
Check,
} from '@element-plus/icons-vue';
import Captcha from '@/components/Captcha.vue';
import api from '@/utils/api'
@@ -218,9 +265,12 @@ const registerForm = reactive({
// 协议查看状态
const agreementViewed = ref(false);
const privacyViewed = ref(false);
const canCheckAgreement = computed(
() => agreementViewed.value && privacyViewed.value
);
// 协议弹窗相关状态
const showAgreementDialogVisible = ref(false);
const activeTab = ref('agreement');
const agreementContent = ref();
const privacyContent = ref();
// 短信验证码相关状态
const sendingSMS = ref(false);
@@ -477,62 +527,45 @@ const handleRegister = async () => {
}
};
// 显示用户协议
const showAgreement = () => {
ElMessageBox.alert(
`<div style="text-align: left; line-height: 1.6;">
<h3>用户协议</h3>
<p>1. 用户应当遵守法律法规,不得发布违法违规内容。</p>
<p>2. 用户对自己发布的内容承担全部责任。</p>
<p>3. 平台有权对违规内容进行删除或限制。</p>
<p>4. 用户应当保护好自己的账号安全。</p>
<p>5. 平台保留修改本协议的权利。</p>
</div>`,
'用户协议',
{
confirmButtonText: '我已了解',
dangerouslyUseHTMLString: true,
customClass: 'agreement-dialog',
}
).then(() => {
agreementViewed.value = true;
ElMessage.success('已查看用户协议');
});
// 显示协议弹窗
const showAgreementDialog = () => {
showAgreementDialogVisible.value = true;
activeTab.value = 'agreement';
// 弹窗打开时默认用户协议已读
agreementViewed.value = true;
};
// 显示隐私政策
const showPrivacy = () => {
ElMessageBox.alert(
`<div style="text-align: left; line-height: 1.6;">
<h3>隐私政策</h3>
<p>1. 我们重视用户隐私保护。</p>
<p>2. 我们只收集必要的用户信息。</p>
<p>3. 用户信息仅用于提供服务。</p>
<p>4. 我们不会向第三方泄露用户信息。</p>
<p>5. 用户有权查看、修改或删除个人信息。</p>
</div>`,
'隐私政策',
{
confirmButtonText: '我已了解',
dangerouslyUseHTMLString: true,
customClass: 'privacy-dialog',
}
).then(() => {
privacyViewed.value = true;
ElMessage.success('已查看隐私政策');
});
// 关闭协议弹窗
const closeAgreementDialog = () => {
showAgreementDialogVisible.value = false;
};
// 处理协议勾选
const handleAgreementChange = (value) => {
if (value && !canCheckAgreement.value) {
registerForm.agreement = false;
ElMessage.warning('请先查看用户协议和隐私政策后再勾选');
return false;
// 确认已阅读协议
const confirmAgreement = () => {
if (agreementViewed.value && privacyViewed.value) {
registerForm.agreement = true;
showAgreementDialogVisible.value = false;
ElMessage.success('已确认阅读用户协议和隐私政策');
} else {
ElMessage.warning('请先查看用户协议和隐私政策');
}
return true;
};
// 处理选项卡切换
const handleTabChange = (tabName) => {
if (tabName === 'agreement') {
// 切换到用户协议时,标记为已读
agreementViewed.value = true;
} else if (tabName === 'privacy') {
// 切换到隐私政策时,标记为已读
privacyViewed.value = true;
}
};
// 图片上传成功处理
const handleUploadSuccess = (response, field) => {
ElMessage.success('图片上传成功');
@@ -888,9 +921,111 @@ onMounted(() => {
font-size: 14px;
}
:deep(.el-link.viewed) {
color: #67c23a !important;
font-weight: 500;
.agreement-text {
color: #409eff;
cursor: pointer;
text-decoration: underline;
}
.agreement-text:hover {
color: #66b1ff;
}
/* 协议弹窗样式 */
:deep(.agreement-dialog) {
.el-dialog__body {
padding: 20px;
max-height: 60vh;
overflow: hidden;
}
}
.agreement-content {
height: 100%;
}
:deep(.agreement-dialog .el-tabs__nav) {
display: flex;
width: 100%;
}
:deep(.agreement-dialog .el-tabs__item) {
flex: 1;
text-align: center;
display: flex;
justify-content: center;
align-items: center;
}
.tab-label {
margin-right: 4px;
}
.agreement-text-content,
.privacy-text-content {
max-height: 400px;
overflow-y: auto;
padding: 20px;
line-height: 1.6;
font-size: 14px;
color: #606266;
border: 1px solid #e4e7ed;
border-radius: 8px;
background-color: #fafafa;
}
.agreement-text-content h3,
.privacy-text-content h3 {
color: #303133;
margin-bottom: 16px;
font-size: 18px;
font-weight: 600;
}
.agreement-text-content p,
.privacy-text-content p {
margin-bottom: 12px;
}
.agreement-text-content strong,
.privacy-text-content strong {
color: #409eff;
font-weight: 600;
}
.dialog-footer {
display: flex;
justify-content: space-between;
align-items: center;
padding-top: 20px;
}
.check-icon {
color: #67c23a;
font-size: 16px;
}
/* 滚动条样式 */
.agreement-text-content::-webkit-scrollbar,
.privacy-text-content::-webkit-scrollbar {
width: 6px;
}
.agreement-text-content::-webkit-scrollbar-track,
.privacy-text-content::-webkit-scrollbar-track {
background: #f1f1f1;
border-radius: 3px;
}
.agreement-text-content::-webkit-scrollbar-thumb,
.privacy-text-content::-webkit-scrollbar-thumb {
background: #c1c1c1;
border-radius: 3px;
}
.agreement-text-content::-webkit-scrollbar-thumb:hover,
.privacy-text-content::-webkit-scrollbar-thumb:hover {
background: #a8a8a8;
}
:deep(.el-link.viewed:hover) {