协议问题解决
This commit is contained in:
@@ -122,18 +122,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 上传按钮 -->
|
||||
<div v-if="selectedFiles.length > 0" class="upload-actions">
|
||||
<el-button
|
||||
type="primary"
|
||||
@click="handleUpload"
|
||||
:loading="uploading"
|
||||
:disabled="selectedFiles.length === 0"
|
||||
>
|
||||
{{ uploading ? '上传中...' : '确定上传' }}
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-dialog
|
||||
@@ -362,10 +350,8 @@ function handleFileSelect(event: Event) {
|
||||
// 清空 input,允许重复选择同一文件
|
||||
input.value = '';
|
||||
|
||||
// cover模式下选择文件后立即上传
|
||||
if (props.listType === 'cover') {
|
||||
handleUpload();
|
||||
}
|
||||
// 选择文件后立即上传
|
||||
handleUpload();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,9 +10,10 @@
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.blank-layout {
|
||||
width: 100vw;
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -24,6 +24,23 @@ export const routes: Array<RouteRecordRaw> = [
|
||||
requiresAuth: false,
|
||||
menuType: 3,
|
||||
}),
|
||||
// 协议预览页面
|
||||
{
|
||||
path: "/agreement/:type",
|
||||
component: () => import("@/layouts/BlankLayout.vue"),
|
||||
children: [
|
||||
{
|
||||
path: "",
|
||||
name: "Agreement",
|
||||
component: () => import("@/views/public/agreement/AgreementView.vue"),
|
||||
meta: {
|
||||
title: "协议",
|
||||
requiresAuth: false,
|
||||
menuType: 3,
|
||||
},
|
||||
}
|
||||
],
|
||||
},
|
||||
// 首页(显示在导航栏)
|
||||
// {
|
||||
// path: "/home",
|
||||
|
||||
@@ -47,6 +47,22 @@ export default {
|
||||
faviconUrl: (state: SystemState) => {
|
||||
const fileId = state.baseInfo?.favicon;
|
||||
return (fileId && fileId.trim()) ? `${FILE_DOWNLOAD_URL}${fileId}` : '/favicon.ico';
|
||||
},
|
||||
|
||||
// 协议文件URL
|
||||
platformAgreementUrl: (state: SystemState) => {
|
||||
const fileId = state.baseInfo?.platformAgreement;
|
||||
return (fileId && fileId.trim()) ? `${FILE_DOWNLOAD_URL}${fileId}` : '';
|
||||
},
|
||||
|
||||
userAgreementUrl: (state: SystemState) => {
|
||||
const fileId = state.baseInfo?.userAgreement;
|
||||
return (fileId && fileId.trim()) ? `${FILE_DOWNLOAD_URL}${fileId}` : '';
|
||||
},
|
||||
|
||||
privacyAgreementUrl: (state: SystemState) => {
|
||||
const fileId = state.baseInfo?.privacyAgreement;
|
||||
return (fileId && fileId.trim()) ? `${FILE_DOWNLOAD_URL}${fileId}` : '';
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -10,6 +10,11 @@ export interface SystemBaseInfo {
|
||||
adminLogo: string; // 管理后台Logo(fileId)
|
||||
favicon: string; // 网站图标(fileId)
|
||||
|
||||
// 登录协议文件ID
|
||||
platformAgreement: string; // 平台协议(fileId)
|
||||
userAgreement: string; // 用户协议(fileId)
|
||||
privacyAgreement: string; // 隐私协议(fileId)
|
||||
|
||||
// 登录开关(来自不同分组)
|
||||
smsLoginEnabled: boolean; // 短信登录开关(sms分组)
|
||||
emailLoginEnabled: boolean; // 邮箱登录开关(email分组)
|
||||
|
||||
@@ -122,6 +122,44 @@
|
||||
:tip="item.remark || '点击上传图片,支持jpg、png格式'"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 文件上传(协议文档等) -->
|
||||
<el-form-item
|
||||
v-else-if="getRenderType(item) === 'fileupload'"
|
||||
:label="item.configName || item.configKey"
|
||||
:prop="item.configKey"
|
||||
>
|
||||
<div class="file-upload-wrapper">
|
||||
<FileUpload
|
||||
:as-dialog="false"
|
||||
:multiple="false"
|
||||
accept=".pdf,.txt"
|
||||
:max-size="100"
|
||||
module="system"
|
||||
:tip="item.remark || '支持PDF、TXT格式,不超过100MB'"
|
||||
@success="(files) => handleFileUploadSuccess(files, group.groupKey, item.configKey)"
|
||||
/>
|
||||
<div v-if="configData[group.groupKey][item.configKey]" class="uploaded-file-info">
|
||||
<el-tag type="success" size="small">已上传</el-tag>
|
||||
<el-button
|
||||
type="primary"
|
||||
link
|
||||
size="small"
|
||||
@click="previewAgreementFile(configData[group.groupKey][item.configKey])"
|
||||
>
|
||||
预览文件
|
||||
</el-button>
|
||||
<el-button
|
||||
type="danger"
|
||||
link
|
||||
size="small"
|
||||
@click="configData[group.groupKey][item.configKey] = ''"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</template>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
@@ -149,7 +187,6 @@ import { ElMessage } from 'element-plus';
|
||||
import { configApi } from '@/apis/system';
|
||||
import type { ConfigItem } from '@/types/system/config';
|
||||
import FileUpload from '@/components/file/FileUpload.vue';
|
||||
|
||||
defineOptions({
|
||||
name: 'SystemConfigView'
|
||||
});
|
||||
@@ -358,6 +395,26 @@ function handleCoverUpdate(url: string, groupKey: string, configKey: string) {
|
||||
configData[groupKey][configKey] = url;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理文件上传成功(协议文档等)
|
||||
*/
|
||||
function handleFileUploadSuccess(files: any[], groupKey: string, configKey: string) {
|
||||
if (files && files.length > 0) {
|
||||
configData[groupKey][configKey] = files[0].fileID || '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 预览协议文件(新开tab页)
|
||||
*/
|
||||
function previewAgreementFile(fileId: string) {
|
||||
if (fileId) {
|
||||
// 使用文件下载URL在新标签页打开
|
||||
const url = `${import.meta.env.VITE_API_BASE_URL || ''}/api/file/preview/${fileId}`;
|
||||
window.open(url, '_blank');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存指定分组的配置
|
||||
*/
|
||||
@@ -440,5 +497,16 @@ async function saveConfig(groupKey: string) {
|
||||
color: #606266;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.file-upload-wrapper {
|
||||
width: 100%;
|
||||
|
||||
.uploaded-file-info {
|
||||
margin-top: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
230
schoolNewsWeb/src/views/public/agreement/AgreementView.vue
Normal file
230
schoolNewsWeb/src/views/public/agreement/AgreementView.vue
Normal file
@@ -0,0 +1,230 @@
|
||||
<template>
|
||||
<div class="agreement-container">
|
||||
<div class="agreement-header">
|
||||
<h1 class="agreement-title">{{ title }}</h1>
|
||||
</div>
|
||||
|
||||
<div class="agreement-content" v-loading="loading">
|
||||
<!-- PDF预览 -->
|
||||
<div v-if="fileType === 'pdf'" class="pdf-viewer">
|
||||
<VuePdfEmbed
|
||||
:source="pdfSource"
|
||||
:page="currentPage"
|
||||
@loaded="onPdfLoaded"
|
||||
@rendered="onPdfRendered"
|
||||
/>
|
||||
<!-- PDF分页控制 -->
|
||||
<div v-if="totalPages > 1" class="pdf-pagination">
|
||||
<el-button :disabled="currentPage <= 1" @click="currentPage--">上一页</el-button>
|
||||
<span class="page-info">{{ currentPage }} / {{ totalPages }}</span>
|
||||
<el-button :disabled="currentPage >= totalPages" @click="currentPage++">下一页</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- TXT预览 -->
|
||||
<div v-else-if="fileType === 'txt'" class="txt-viewer">
|
||||
<pre>{{ textContent }}</pre>
|
||||
</div>
|
||||
|
||||
<!-- 无内容 -->
|
||||
<div v-else-if="!loading" class="empty-content">
|
||||
<el-empty description="协议内容暂未上传" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { useStore } from 'vuex';
|
||||
import { FILE_DOWNLOAD_URL } from '@/config';
|
||||
import VuePdfEmbed from 'vue-pdf-embed';
|
||||
|
||||
const route = useRoute();
|
||||
const store = useStore();
|
||||
|
||||
const loading = ref(true);
|
||||
const textContent = ref('');
|
||||
const fileType = ref<'pdf' | 'txt' | ''>('');
|
||||
const pdfSource = ref<string>('');
|
||||
const currentPage = ref(1);
|
||||
const totalPages = ref(0);
|
||||
|
||||
// 协议类型对应的标题
|
||||
const titleMap: Record<string, string> = {
|
||||
platform: '红色思政智能体平台协议',
|
||||
user: '用户协议',
|
||||
privacy: '隐私协议'
|
||||
};
|
||||
|
||||
// 获取协议类型
|
||||
const agreementType = computed(() => route.params.type as string);
|
||||
|
||||
// 标题
|
||||
const title = computed(() => titleMap[agreementType.value] || '协议');
|
||||
|
||||
// 获取文件ID
|
||||
const fileId = computed(() => {
|
||||
const baseInfo = store.state.system.baseInfo;
|
||||
if (!baseInfo) return '';
|
||||
|
||||
switch (agreementType.value) {
|
||||
case 'platform':
|
||||
return baseInfo.platformAgreement || '';
|
||||
case 'user':
|
||||
return baseInfo.userAgreement || '';
|
||||
case 'privacy':
|
||||
return baseInfo.privacyAgreement || '';
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
});
|
||||
|
||||
// 文件URL
|
||||
const fileUrl = computed(() => {
|
||||
if (!fileId.value) return '';
|
||||
return `${FILE_DOWNLOAD_URL}${fileId.value}`;
|
||||
});
|
||||
|
||||
// PDF加载完成
|
||||
function onPdfLoaded(pdf: any) {
|
||||
totalPages.value = pdf.numPages;
|
||||
}
|
||||
|
||||
// PDF渲染完成
|
||||
function onPdfRendered() {
|
||||
loading.value = false;
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
// 确保系统配置已加载
|
||||
if (!store.state.system.baseInfo) {
|
||||
await store.dispatch('system/loadBaseInfo');
|
||||
}
|
||||
|
||||
if (!fileId.value) {
|
||||
loading.value = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// 根据文件URL判断类型并加载
|
||||
try {
|
||||
const response = await fetch(fileUrl.value);
|
||||
const contentType = response.headers.get('content-type') || '';
|
||||
const contentDisposition = response.headers.get('content-disposition') || '';
|
||||
|
||||
// 判断是否为PDF
|
||||
const isPdf = contentType.includes('pdf') ||
|
||||
contentDisposition.includes('.pdf') ||
|
||||
fileUrl.value.toLowerCase().includes('.pdf');
|
||||
|
||||
if (isPdf) {
|
||||
fileType.value = 'pdf';
|
||||
// 获取PDF的blob URL
|
||||
const blob = await response.blob();
|
||||
pdfSource.value = URL.createObjectURL(blob);
|
||||
} else {
|
||||
// 作为文本处理
|
||||
fileType.value = 'txt';
|
||||
textContent.value = await response.text();
|
||||
loading.value = false;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载协议文件失败:', error);
|
||||
loading.value = false;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.agreement-container {
|
||||
min-height: 100vh;
|
||||
background: #f5f5f5;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.agreement-header {
|
||||
background: #fff;
|
||||
padding: 20px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
|
||||
.agreement-title {
|
||||
margin: 0;
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.agreement-content {
|
||||
flex: 1;
|
||||
padding: 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.pdf-viewer {
|
||||
flex: 1;
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
|
||||
:deep(.vue-pdf-embed) {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
:deep(canvas) {
|
||||
max-width: 100%;
|
||||
height: auto !important;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.pdf-pagination {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
margin-top: 20px;
|
||||
padding-top: 20px;
|
||||
border-top: 1px solid #eee;
|
||||
|
||||
.page-info {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
|
||||
.txt-viewer {
|
||||
flex: 1;
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
padding: 24px;
|
||||
overflow: auto;
|
||||
|
||||
pre {
|
||||
margin: 0;
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
||||
font-size: 14px;
|
||||
line-height: 1.8;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
|
||||
.empty-content {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
}
|
||||
</style>
|
||||
@@ -181,8 +181,8 @@
|
||||
<div class="agreement-wrapper">
|
||||
<el-checkbox v-model="loginForm.agree" />
|
||||
<span class="agreement-text">
|
||||
登录即为同意 <span class="agreement-link">《红色思政智能体平台》</span><span class="agreement-link">《用户协议》</span>
|
||||
<span class="agreement-link">《隐私协议》</span>
|
||||
登录即为同意 <span class="agreement-link" @click="openAgreement('platform')">《红色思政智能体平台》</span><span class="agreement-link" @click="openAgreement('user')">《用户协议》</span>
|
||||
<span class="agreement-link" @click="openAgreement('privacy')">《隐私协议》</span>
|
||||
</span>
|
||||
</div>
|
||||
</el-form-item>
|
||||
@@ -519,6 +519,15 @@ function goToForgotPassword() {
|
||||
router.push('/forgot-password');
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开协议文件(新标签页)
|
||||
*/
|
||||
function openAgreement(type: 'platform' | 'user' | 'privacy') {
|
||||
// 跳转到协议预览页面
|
||||
const url = router.resolve({ path: `/agreement/${type}` }).href;
|
||||
window.open(url, '_blank');
|
||||
}
|
||||
|
||||
// 组件挂载时检查是否需要显示验证码
|
||||
onMounted(() => {
|
||||
// 可以根据需要决定是否默认显示验证码
|
||||
|
||||
@@ -181,9 +181,9 @@
|
||||
<div class="agreement-wrapper">
|
||||
<el-checkbox v-model="loginForm.agree" />
|
||||
<span class="agreement-text">
|
||||
登录即为同意 <span class="agreement-link">《红色思政智能体平台》</span>
|
||||
<span class="agreement-link">《用户协议》</span>
|
||||
<span class="agreement-link">《隐私协议》</span>
|
||||
登录即为同意 <span class="agreement-link" @click="openAgreement('platform')">《红色思政智能体平台》</span>
|
||||
<span class="agreement-link" @click="openAgreement('user')">《用户协议》</span>
|
||||
<span class="agreement-link" @click="openAgreement('privacy')">《隐私协议》</span>
|
||||
</span>
|
||||
</div>
|
||||
</el-form-item>
|
||||
@@ -521,6 +521,15 @@ function goToForgotPassword() {
|
||||
router.push('/forgot-password');
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开协议文件(新标签页)
|
||||
*/
|
||||
function openAgreement(type: 'platform' | 'user' | 'privacy') {
|
||||
// 跳转到协议预览页面
|
||||
const url = router.resolve({ path: `/agreement/${type}` }).href;
|
||||
window.open(url, '_blank');
|
||||
}
|
||||
|
||||
// 组件挂载时检查是否需要显示验证码
|
||||
onMounted(() => {
|
||||
// 可以根据需要决定是否默认显示验证码
|
||||
|
||||
@@ -167,8 +167,8 @@
|
||||
<div class="agreement-wrapper">
|
||||
<el-checkbox v-model="registerForm.agree" />
|
||||
<span class="agreement-text">
|
||||
注册即为同意 <span class="agreement-link">《红色思政智能体平台》</span><span class="agreement-link">《用户协议》</span>
|
||||
<span class="agreement-link">《隐私协议》</span>
|
||||
注册即为同意 <span class="agreement-link" @click="openAgreement('platform')">《红色思政智能体平台》</span><span class="agreement-link" @click="openAgreement('user')">《用户协议》</span>
|
||||
<span class="agreement-link" @click="openAgreement('privacy')">《隐私协议》</span>
|
||||
</span>
|
||||
</div>
|
||||
</el-form-item>
|
||||
@@ -515,6 +515,15 @@ onUnmounted(() => {
|
||||
clearInterval(emailTimer);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* 打开协议文件(新标签页)
|
||||
*/
|
||||
function openAgreement(type: 'platform' | 'user' | 'privacy') {
|
||||
// 跳转到协议预览页面
|
||||
const url = router.resolve({ path: `/agreement/${type}` }).href;
|
||||
window.open(url, '_blank');
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -174,8 +174,8 @@
|
||||
<div class="agreement-wrapper">
|
||||
<el-checkbox v-model="registerForm.agree" />
|
||||
<span class="agreement-text">
|
||||
注册即为同意 <span class="agreement-link">《红色思政智能体平台》</span><span class="agreement-link">《用户协议》</span>
|
||||
<span class="agreement-link">《隐私协议》</span>
|
||||
注册即为同意 <span class="agreement-link" @click="openAgreement('platform')">《红色思政智能体平台》</span><span class="agreement-link" @click="openAgreement('user')">《用户协议》</span>
|
||||
<span class="agreement-link" @click="openAgreement('privacy')">《隐私协议》</span>
|
||||
</span>
|
||||
</div>
|
||||
</el-form-item>
|
||||
@@ -522,6 +522,15 @@ function goToLogin() {
|
||||
router.push('/login');
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开协议文件(新标签页)
|
||||
*/
|
||||
function openAgreement(type: 'platform' | 'user' | 'privacy') {
|
||||
// 跳转到协议预览页面
|
||||
const url = router.resolve({ path: `/agreement/${type}` }).href;
|
||||
window.open(url, '_blank');
|
||||
}
|
||||
|
||||
// 组件卸载时清除定时器
|
||||
import { onUnmounted } from 'vue';
|
||||
onUnmounted(() => {
|
||||
|
||||
Reference in New Issue
Block a user