细节修改
This commit is contained in:
parent
181ffdae66
commit
74de21b28f
|
|
@ -4,7 +4,7 @@
|
|||
VITE_APP_TITLE=相宜相亲后台管理系统(开发)
|
||||
|
||||
# API基础地址
|
||||
VITE_API_BASE_URL=http://localhost:5000/api
|
||||
VITE_API_BASE_URL=http://localhost:5001/api
|
||||
|
||||
# 静态资源服务器地址(AppApi,用于图片等静态资源)
|
||||
VITE_STATIC_BASE_URL=http://localhost:5001
|
||||
# 静态资源服务器地址(所有图片统一从AppApi访问)
|
||||
VITE_STATIC_BASE_URL=http://localhost:5000
|
||||
|
|
|
|||
|
|
@ -20,3 +20,31 @@ export function setDefaultAvatar(avatarUrl: string) {
|
|||
export function getAllConfigs() {
|
||||
return request.get('/admin/config/all')
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户协议
|
||||
*/
|
||||
export function getUserAgreement() {
|
||||
return request.get('/admin/config/userAgreement')
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置用户协议
|
||||
*/
|
||||
export function setUserAgreement(content: string) {
|
||||
return request.post('/admin/config/userAgreement', { content })
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取隐私协议
|
||||
*/
|
||||
export function getPrivacyPolicy() {
|
||||
return request.get('/admin/config/privacyPolicy')
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置隐私协议
|
||||
*/
|
||||
export function setPrivacyPolicy(content: string) {
|
||||
return request.post('/admin/config/privacyPolicy', { content })
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,8 +60,8 @@ import { ElMessage } from 'element-plus'
|
|||
import type { UploadFile, UploadFiles, UploadRawFile, UploadUserFile } from 'element-plus'
|
||||
import { getToken } from '@/utils/auth'
|
||||
|
||||
// 获取静态资源服务器地址(AppApi,用于图片等静态资源)
|
||||
const STATIC_BASE = import.meta.env.VITE_STATIC_BASE_URL || 'http://localhost:5001'
|
||||
// 获取静态资源服务器地址(所有图片统一从AppApi访问)
|
||||
const STATIC_BASE = import.meta.env.VITE_STATIC_BASE_URL || 'http://localhost:5000'
|
||||
|
||||
// 处理图片URL,将相对路径转换为完整URL
|
||||
function getFullImageUrl(url: string): string {
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@
|
|||
* 图片URL处理工具
|
||||
*/
|
||||
|
||||
// 获取静态资源服务器地址(AppApi,用于图片等静态资源)
|
||||
const STATIC_BASE = import.meta.env.VITE_STATIC_BASE_URL || 'http://localhost:5001'
|
||||
// 静态资源服务器地址(所有图片统一从AppApi访问)
|
||||
const STATIC_BASE = import.meta.env.VITE_STATIC_BASE_URL || 'http://localhost:5000'
|
||||
|
||||
/**
|
||||
* 处理图片URL,将相对路径转换为完整URL
|
||||
|
|
|
|||
|
|
@ -7,6 +7,9 @@
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<el-tabs v-model="activeTab" class="config-tabs">
|
||||
<!-- 基础配置 -->
|
||||
<el-tab-pane label="基础配置" name="basic">
|
||||
<el-form :model="configForm" label-width="120px" class="config-form">
|
||||
<!-- 默认头像设置 -->
|
||||
<el-form-item label="默认头像">
|
||||
|
|
@ -32,11 +35,51 @@
|
|||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="saveConfig" :loading="saving">
|
||||
<el-button type="primary" @click="saveBasicConfig" :loading="saving">
|
||||
保存配置
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-tab-pane>
|
||||
|
||||
<!-- 用户协议 -->
|
||||
<el-tab-pane label="用户协议" name="userAgreement">
|
||||
<div class="agreement-editor">
|
||||
<div class="editor-toolbar">
|
||||
<span class="toolbar-title">用户协议内容</span>
|
||||
<el-button type="primary" @click="saveUserAgreement" :loading="savingAgreement">
|
||||
保存协议
|
||||
</el-button>
|
||||
</div>
|
||||
<el-input
|
||||
v-model="agreementForm.userAgreement"
|
||||
type="textarea"
|
||||
:rows="20"
|
||||
placeholder="请输入用户协议内容,支持HTML格式"
|
||||
class="agreement-textarea"
|
||||
/>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
|
||||
<!-- 隐私协议 -->
|
||||
<el-tab-pane label="隐私协议" name="privacyPolicy">
|
||||
<div class="agreement-editor">
|
||||
<div class="editor-toolbar">
|
||||
<span class="toolbar-title">隐私协议内容</span>
|
||||
<el-button type="primary" @click="savePrivacyPolicy" :loading="savingPolicy">
|
||||
保存协议
|
||||
</el-button>
|
||||
</div>
|
||||
<el-input
|
||||
v-model="agreementForm.privacyPolicy"
|
||||
type="textarea"
|
||||
:rows="20"
|
||||
placeholder="请输入隐私协议内容,支持HTML格式"
|
||||
class="agreement-textarea"
|
||||
/>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -45,7 +88,14 @@
|
|||
import { ref, onMounted, computed } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { Plus } from '@element-plus/icons-vue'
|
||||
import { getDefaultAvatar, setDefaultAvatar } from '@/api/config'
|
||||
import {
|
||||
getDefaultAvatar,
|
||||
setDefaultAvatar,
|
||||
getUserAgreement,
|
||||
setUserAgreement,
|
||||
getPrivacyPolicy,
|
||||
setPrivacyPolicy
|
||||
} from '@/api/config'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
|
||||
const userStore = useUserStore()
|
||||
|
|
@ -53,11 +103,20 @@ const apiBaseUrl = import.meta.env.VITE_API_BASE_URL || 'http://localhost:5000/a
|
|||
// 去掉 /api 后缀获取服务器根地址
|
||||
const serverUrl = apiBaseUrl.replace(/\/api$/, '')
|
||||
|
||||
const activeTab = ref('basic')
|
||||
|
||||
const configForm = ref({
|
||||
defaultAvatar: ''
|
||||
})
|
||||
|
||||
const agreementForm = ref({
|
||||
userAgreement: '',
|
||||
privacyPolicy: ''
|
||||
})
|
||||
|
||||
const saving = ref(false)
|
||||
const savingAgreement = ref(false)
|
||||
const savingPolicy = ref(false)
|
||||
|
||||
const uploadUrl = computed(() => `${apiBaseUrl}/admin/upload`)
|
||||
|
||||
|
|
@ -83,6 +142,25 @@ const loadConfig = async () => {
|
|||
}
|
||||
}
|
||||
|
||||
const loadAgreements = async () => {
|
||||
try {
|
||||
const [userAgreementRes, privacyPolicyRes] = await Promise.all([
|
||||
getUserAgreement(),
|
||||
getPrivacyPolicy()
|
||||
])
|
||||
|
||||
if (userAgreementRes) {
|
||||
agreementForm.value.userAgreement = userAgreementRes.content || ''
|
||||
}
|
||||
|
||||
if (privacyPolicyRes) {
|
||||
agreementForm.value.privacyPolicy = privacyPolicyRes.content || ''
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载协议失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
const handleAvatarSuccess = (response) => {
|
||||
if (response.code === 0 && response.data) {
|
||||
configForm.value.defaultAvatar = response.data.url
|
||||
|
|
@ -107,7 +185,7 @@ const beforeAvatarUpload = (file) => {
|
|||
return true
|
||||
}
|
||||
|
||||
const saveConfig = async () => {
|
||||
const saveBasicConfig = async () => {
|
||||
if (!configForm.value.defaultAvatar) {
|
||||
ElMessage.warning('请先上传默认头像')
|
||||
return
|
||||
|
|
@ -126,8 +204,43 @@ const saveConfig = async () => {
|
|||
}
|
||||
}
|
||||
|
||||
const saveUserAgreement = async () => {
|
||||
if (!agreementForm.value.userAgreement.trim()) {
|
||||
ElMessage.warning('请输入用户协议内容')
|
||||
return
|
||||
}
|
||||
|
||||
savingAgreement.value = true
|
||||
try {
|
||||
await setUserAgreement(agreementForm.value.userAgreement)
|
||||
ElMessage.success('用户协议保存成功')
|
||||
} catch (error) {
|
||||
console.error('保存用户协议失败:', error)
|
||||
} finally {
|
||||
savingAgreement.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const savePrivacyPolicy = async () => {
|
||||
if (!agreementForm.value.privacyPolicy.trim()) {
|
||||
ElMessage.warning('请输入隐私协议内容')
|
||||
return
|
||||
}
|
||||
|
||||
savingPolicy.value = true
|
||||
try {
|
||||
await setPrivacyPolicy(agreementForm.value.privacyPolicy)
|
||||
ElMessage.success('隐私协议保存成功')
|
||||
} catch (error) {
|
||||
console.error('保存隐私协议失败:', error)
|
||||
} finally {
|
||||
savingPolicy.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadConfig()
|
||||
loadAgreements()
|
||||
})
|
||||
</script>
|
||||
|
||||
|
|
@ -137,7 +250,7 @@ onMounted(() => {
|
|||
}
|
||||
|
||||
.config-card {
|
||||
max-width: 800px;
|
||||
max-width: 1200px;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
|
|
@ -145,6 +258,10 @@ onMounted(() => {
|
|||
font-weight: 600;
|
||||
}
|
||||
|
||||
.config-tabs {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.config-form {
|
||||
padding: 20px 0;
|
||||
}
|
||||
|
|
@ -199,4 +316,29 @@ onMounted(() => {
|
|||
.avatar-tip p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.agreement-editor {
|
||||
padding: 20px 0;
|
||||
}
|
||||
|
||||
.editor-toolbar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 16px;
|
||||
padding-bottom: 12px;
|
||||
border-bottom: 1px solid var(--el-border-color-light);
|
||||
}
|
||||
|
||||
.toolbar-title {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: var(--el-text-color-primary);
|
||||
}
|
||||
|
||||
.agreement-textarea :deep(.el-textarea__inner) {
|
||||
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
||||
font-size: 14px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
56
docs/用户协议.txt
Normal file
56
docs/用户协议.txt
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
<h2>相宜相亲用户服务协议</h2>
|
||||
|
||||
<p><strong>更新日期:2026年1月6日</strong></p>
|
||||
<p><strong>生效日期:2026年1月6日</strong></p>
|
||||
|
||||
<p>欢迎您使用相宜相亲小程序(以下简称"本平台")!在使用本平台服务之前,请您仔细阅读并充分理解本协议的全部内容。</p>
|
||||
|
||||
<h3>一、服务说明</h3>
|
||||
<p>1. 本平台是一个为用户提供相亲交友服务的社交平台,旨在帮助用户寻找合适的另一半。</p>
|
||||
<p>2. 本平台提供的服务包括但不限于:资料填写、信息浏览、用户沟通、会员服务、消息推送等功能。</p>
|
||||
<p>3. 用户需年满18周岁方可使用本平台服务。</p>
|
||||
|
||||
<h3>二、用户注册与账号管理</h3>
|
||||
<p>1. 用户在注册时应提供真实、准确、完整的个人信息,并在信息发生变化时及时更新。</p>
|
||||
<p>2. 用户应妥善保管账号和密码,因用户原因导致的账号安全问题由用户自行承担。</p>
|
||||
<p>3. 用户不得将账号转让、出借给他人使用。</p>
|
||||
|
||||
<h3>三、用户行为规范</h3>
|
||||
<p>1. 用户应遵守国家法律法规,不得利用本平台从事违法违规活动。</p>
|
||||
<p>2. 用户不得发布虚假、欺诈、骚扰、侮辱、诽谤等不良信息。</p>
|
||||
<p>3. 用户不得上传含有色情、暴力、恐怖等违法违规内容的图片或信息。</p>
|
||||
<p>4. 用户应尊重其他用户的隐私和合法权益。</p>
|
||||
|
||||
<h3>四、相亲资料管理</h3>
|
||||
<p>1. 用户填写的相亲资料应真实、准确,不得提供虚假信息。</p>
|
||||
<p>2. 用户上传的照片应为本人或其子女的真实照片,不得使用他人照片。</p>
|
||||
<p>3. 用户可选择是否公开照片,平台将根据用户选择进行展示。</p>
|
||||
|
||||
<h3>五、会员服务</h3>
|
||||
<p>1. 本平台提供多种会员服务,包括限时会员(1299元)、诚意会员(1999元)、诚意会员家庭版(2999元)。</p>
|
||||
<p>2. 会员服务的具体权益以平台公示为准。</p>
|
||||
<p>3. 会员费用一经支付,除法律规定的情形外,不予退还。</p>
|
||||
|
||||
<h3>六、实名认证</h3>
|
||||
<p>1. 用户可选择进行实名认证,认证通过后将获得"已实名"标识。</p>
|
||||
<p>2. 实名认证需支付88元认证费用,并提供真实有效的身份证信息。</p>
|
||||
<p>3. 平台将对用户提交的身份信息进行严格保密。</p>
|
||||
|
||||
<h3>七、联系方式解锁</h3>
|
||||
<p>1. 用户解锁其他用户的联系方式需消耗联系次数。</p>
|
||||
<p>2. 新用户注册时赠送2次免费联系次数。</p>
|
||||
<p>3. 联系次数用完后,需开通会员获取更多次数。</p>
|
||||
|
||||
<h3>八、免责声明</h3>
|
||||
<p>1. 本平台仅提供信息展示和沟通渠道,不对用户之间的交往结果承担责任。</p>
|
||||
<p>2. 用户应自行判断其他用户信息的真实性,谨慎交往。</p>
|
||||
<p>3. 因不可抗力导致的服务中断,本平台不承担责任。</p>
|
||||
|
||||
<h3>九、协议修改</h3>
|
||||
<p>1. 本平台有权根据需要修改本协议内容。</p>
|
||||
<p>2. 协议修改后,将在平台公示,用户继续使用即视为同意修改后的协议。</p>
|
||||
|
||||
<h3>十、联系方式</h3>
|
||||
<p>如您对本协议有任何疑问,请通过平台内的"管家指导"功能联系我们。</p>
|
||||
|
||||
<p><strong>相宜相亲运营团队</strong></p>
|
||||
68
docs/隐私协议.txt
Normal file
68
docs/隐私协议.txt
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
<h2>相宜相亲隐私政策</h2>
|
||||
|
||||
<p><strong>更新日期:2026年1月6日</strong></p>
|
||||
<p><strong>生效日期:2026年1月6日</strong></p>
|
||||
|
||||
<p>相宜相亲(以下简称"我们")深知个人信息对您的重要性,我们将按照法律法规的规定,保护您的个人信息及隐私安全。</p>
|
||||
|
||||
<h3>一、我们收集的信息</h3>
|
||||
<p>为了向您提供相亲交友服务,我们可能会收集以下信息:</p>
|
||||
|
||||
<p><strong>1. 基本信息</strong></p>
|
||||
<p>• 微信授权信息(昵称、头像)</p>
|
||||
<p>• 手机号码(用于账号验证和联系)</p>
|
||||
<p>• 微信号(用于用户间交换联系方式)</p>
|
||||
|
||||
<p><strong>2. 相亲资料信息</strong></p>
|
||||
<p>• 个人基本信息:姓名、性别、出生年份、身高、体重、学历、职业、月收入等</p>
|
||||
<p>• 家庭信息:与孩子的关系、家乡城市、现居城市等</p>
|
||||
<p>• 婚姻状况:婚姻状态、房产情况、车辆情况等</p>
|
||||
<p>• 照片:用户上传的个人或子女照片</p>
|
||||
<p>• 相亲要求:期望的另一半条件</p>
|
||||
|
||||
<p><strong>3. 实名认证信息</strong></p>
|
||||
<p>• 身份证照片(正反面)</p>
|
||||
<p>• 身份证号码</p>
|
||||
<p>• 真实姓名</p>
|
||||
|
||||
<p><strong>4. 使用信息</strong></p>
|
||||
<p>• 浏览记录、收藏记录、解锁记录</p>
|
||||
<p>• 聊天记录</p>
|
||||
<p>• 设备信息、IP地址、位置信息</p>
|
||||
|
||||
<h3>二、信息的使用</h3>
|
||||
<p>我们收集的信息将用于以下目的:</p>
|
||||
<p>1. 提供相亲交友服务,包括资料展示、用户匹配、消息通知等</p>
|
||||
<p>2. 验证用户身份,保障账号安全</p>
|
||||
<p>3. 进行实名认证,提升平台可信度</p>
|
||||
<p>4. 改进和优化我们的服务</p>
|
||||
<p>5. 向您推送相关通知和消息</p>
|
||||
|
||||
<h3>三、信息的保护</h3>
|
||||
<p>1. 我们采用业界标准的安全技术措施保护您的个人信息。</p>
|
||||
<p>2. 您的身份证信息仅用于实名认证,认证完成后将进行加密存储。</p>
|
||||
<p>3. 您的照片可选择是否公开,未公开的照片将进行模糊处理展示。</p>
|
||||
<p>4. 我们不会将您的个人信息出售给第三方。</p>
|
||||
|
||||
<h3>四、信息的共享</h3>
|
||||
<p>1. 在您同意的情况下,您的部分信息将展示给其他用户(如相亲资料)。</p>
|
||||
<p>2. 当您与其他用户交换联系方式时,相关信息将被共享。</p>
|
||||
<p>3. 为完成实名认证,我们可能会将您的身份信息提交给第三方认证服务商。</p>
|
||||
<p>4. 根据法律法规要求,我们可能需要向有关部门提供您的信息。</p>
|
||||
|
||||
<h3>五、您的权利</h3>
|
||||
<p>1. 您可以随时查看和修改您的个人资料。</p>
|
||||
<p>2. 您可以选择是否公开您的照片。</p>
|
||||
<p>3. 您可以申请删除您的账号和相关数据。</p>
|
||||
<p>4. 您可以拒绝接收推送消息。</p>
|
||||
|
||||
<h3>六、未成年人保护</h3>
|
||||
<p>本平台仅面向18周岁及以上用户,我们不会故意收集未成年人的个人信息。</p>
|
||||
|
||||
<h3>七、隐私政策的更新</h3>
|
||||
<p>我们可能会不时更新本隐私政策,更新后的政策将在平台公示。</p>
|
||||
|
||||
<h3>八、联系我们</h3>
|
||||
<p>如您对本隐私政策有任何疑问,请通过平台内的"管家指导"功能联系我们。</p>
|
||||
|
||||
<p><strong>相宜相亲运营团队</strong></p>
|
||||
15
miniapp/api/agreement.js
Normal file
15
miniapp/api/agreement.js
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
import { get } from './request.js'
|
||||
|
||||
/**
|
||||
* 获取用户协议
|
||||
*/
|
||||
export const getUserAgreement = () => {
|
||||
return get('/config/userAgreement')
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取隐私协议
|
||||
*/
|
||||
export const getPrivacyPolicy = () => {
|
||||
return get('/config/privacyPolicy')
|
||||
}
|
||||
|
|
@ -6,10 +6,10 @@
|
|||
import { get, post, del } from './request'
|
||||
import { getToken } from '../utils/storage'
|
||||
|
||||
// API 基础地址
|
||||
const BASE_URL = 'http://localhost:5001/api/app'
|
||||
// 静态资源服务器地址
|
||||
const STATIC_URL = 'http://localhost:5001'
|
||||
// API 基础地址(AppApi)
|
||||
const BASE_URL = 'http://localhost:5000/api/app'
|
||||
// 静态资源服务器地址(AppApi)
|
||||
const STATIC_URL = 'http://localhost:5000'
|
||||
|
||||
/**
|
||||
* 提交/更新用户资料
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
import { getToken, removeToken, removeUserInfo } from '../utils/storage'
|
||||
|
||||
// API 基础地址
|
||||
const BASE_URL = 'http://localhost:5001/api/app'
|
||||
const BASE_URL = 'http://localhost:5000/api/app'
|
||||
|
||||
// 请求状态
|
||||
let isRefreshing = false
|
||||
|
|
|
|||
|
|
@ -126,6 +126,13 @@
|
|||
"navigationStyle": "custom",
|
||||
"navigationBarTitleText": "登录"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/agreement/index",
|
||||
"style": {
|
||||
"navigationStyle": "custom",
|
||||
"navigationBarTitleText": "协议"
|
||||
}
|
||||
}
|
||||
],
|
||||
"globalStyle": {
|
||||
|
|
|
|||
327
miniapp/pages/agreement/index.vue
Normal file
327
miniapp/pages/agreement/index.vue
Normal file
|
|
@ -0,0 +1,327 @@
|
|||
<template>
|
||||
<view class="agreement-page">
|
||||
<!-- 页面加载状态 -->
|
||||
<Loading type="page" :loading="pageLoading" />
|
||||
|
||||
<!-- 顶部背景图 -->
|
||||
<view class="top-bg">
|
||||
<image src="/static/title_bg.png" mode="aspectFill" class="bg-img" />
|
||||
</view>
|
||||
|
||||
<!-- 自定义导航栏 -->
|
||||
<view class="custom-navbar" :style="{ paddingTop: statusBarHeight + 'px' }">
|
||||
<view class="navbar-content">
|
||||
<view class="navbar-back" @click="handleBack">
|
||||
<text class="back-icon">‹</text>
|
||||
</view>
|
||||
<text class="navbar-title">{{ pageTitle }}</text>
|
||||
<view class="navbar-placeholder"></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 可滚动内容区域 -->
|
||||
<scroll-view
|
||||
class="content-scroll"
|
||||
scroll-y
|
||||
:style="{
|
||||
top: (statusBarHeight + 44) + 'px',
|
||||
height: 'calc(100vh - ' + (statusBarHeight + 44) + 'px)'
|
||||
}"
|
||||
>
|
||||
<view class="agreement-content">
|
||||
<view class="content-wrapper">
|
||||
<rich-text
|
||||
v-if="agreementContent && agreementContent.trim()"
|
||||
:nodes="formatContent(agreementContent)"
|
||||
class="rich-content"
|
||||
/>
|
||||
<view v-else-if="!pageLoading" class="empty-content">
|
||||
<image src="/static/ic_empty.png" mode="aspectFit" class="empty-icon" />
|
||||
<text class="empty-text">暂无{{ agreementType === 'user' ? '用户协议' : '隐私协议' }}内容</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { getUserAgreement, getPrivacyPolicy } from '@/api/agreement.js'
|
||||
import Loading from '@/components/Loading/index.vue'
|
||||
|
||||
export default {
|
||||
name: 'AgreementPage',
|
||||
components: {
|
||||
Loading
|
||||
},
|
||||
setup() {
|
||||
// 状态栏高度
|
||||
const statusBarHeight = ref(20)
|
||||
|
||||
// 页面状态
|
||||
const pageLoading = ref(true)
|
||||
const agreementType = ref('user') // user: 用户协议, privacy: 隐私协议
|
||||
const agreementContent = ref('')
|
||||
|
||||
// 获取系统信息
|
||||
const getSystemInfo = () => {
|
||||
uni.getSystemInfo({
|
||||
success: (res) => {
|
||||
statusBarHeight.value = res.statusBarHeight || 20
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 返回上一页
|
||||
const handleBack = () => {
|
||||
uni.navigateBack()
|
||||
}
|
||||
|
||||
// 计算属性
|
||||
const pageTitle = computed(() => {
|
||||
return agreementType.value === 'user' ? '用户协议' : '隐私协议'
|
||||
})
|
||||
|
||||
/**
|
||||
* 格式化协议内容
|
||||
*/
|
||||
const formatContent = (content) => {
|
||||
if (!content) return ''
|
||||
|
||||
// 如果内容包含HTML标签,直接返回
|
||||
if (content.includes('<')) {
|
||||
return content
|
||||
}
|
||||
|
||||
// 否则将换行符转换为HTML格式
|
||||
return content.replace(/\n/g, '<br/>')
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取协议内容
|
||||
*/
|
||||
const getAgreementContent = async () => {
|
||||
try {
|
||||
let res
|
||||
if (agreementType.value === 'user') {
|
||||
res = await getUserAgreement()
|
||||
} else {
|
||||
res = await getPrivacyPolicy()
|
||||
}
|
||||
|
||||
console.log('协议接口返回:', res)
|
||||
|
||||
// 兼容两种返回格式
|
||||
if (res) {
|
||||
if (res.data && res.data.content) {
|
||||
// 格式: { code: 0, data: { content: "..." } }
|
||||
agreementContent.value = res.data.content
|
||||
} else if (res.content) {
|
||||
// 格式: { content: "..." }
|
||||
agreementContent.value = res.content
|
||||
} else if (typeof res === 'string') {
|
||||
// 直接返回字符串
|
||||
agreementContent.value = res
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取协议内容失败:', error)
|
||||
uni.showToast({
|
||||
title: '获取协议内容失败',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化页面
|
||||
*/
|
||||
const initPage = async () => {
|
||||
pageLoading.value = true
|
||||
try {
|
||||
getSystemInfo()
|
||||
|
||||
// 获取页面参数
|
||||
const pages = getCurrentPages()
|
||||
const currentPage = pages[pages.length - 1]
|
||||
const options = currentPage.options || {}
|
||||
|
||||
agreementType.value = options.type || 'user'
|
||||
|
||||
await getAgreementContent()
|
||||
} catch (error) {
|
||||
console.error('初始化页面失败:', error)
|
||||
} finally {
|
||||
pageLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
initPage()
|
||||
})
|
||||
|
||||
return {
|
||||
statusBarHeight,
|
||||
pageLoading,
|
||||
agreementType,
|
||||
agreementContent,
|
||||
pageTitle,
|
||||
handleBack,
|
||||
formatContent
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.agreement-page {
|
||||
height: 100vh;
|
||||
background-color: #f8f8f8;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
// 顶部背景图
|
||||
.top-bg {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 400rpx;
|
||||
z-index: 0;
|
||||
|
||||
.bg-img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
// 自定义导航栏
|
||||
.custom-navbar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 200;
|
||||
|
||||
.navbar-content {
|
||||
position: relative;
|
||||
height: 44px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 24rpx;
|
||||
|
||||
.navbar-back {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.back-icon {
|
||||
font-size: 56rpx;
|
||||
color: #333;
|
||||
font-weight: 300;
|
||||
}
|
||||
}
|
||||
|
||||
.navbar-title {
|
||||
font-size: 34rpx;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.navbar-placeholder {
|
||||
width: 80rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 可滚动内容区域
|
||||
.content-scroll {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
// 协议内容
|
||||
.agreement-content {
|
||||
padding: 20rpx 30rpx 40rpx;
|
||||
|
||||
.content-wrapper {
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
padding: 40rpx 30rpx;
|
||||
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05);
|
||||
|
||||
.rich-content {
|
||||
line-height: 1.8;
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
|
||||
:deep(p) {
|
||||
margin-bottom: 20rpx;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(h1), :deep(h2), :deep(h3) {
|
||||
font-weight: 600;
|
||||
margin: 30rpx 0 20rpx;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
:deep(h1) {
|
||||
font-size: 36rpx;
|
||||
}
|
||||
|
||||
:deep(h2) {
|
||||
font-size: 32rpx;
|
||||
}
|
||||
|
||||
:deep(h3) {
|
||||
font-size: 30rpx;
|
||||
}
|
||||
|
||||
:deep(ul), :deep(ol) {
|
||||
padding-left: 40rpx;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
:deep(li) {
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
|
||||
:deep(strong) {
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
:deep(em) {
|
||||
font-style: italic;
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
|
||||
.empty-content {
|
||||
text-align: center;
|
||||
padding: 100rpx 0;
|
||||
|
||||
.empty-icon {
|
||||
width: 200rpx;
|
||||
height: 200rpx;
|
||||
margin-bottom: 30rpx;
|
||||
}
|
||||
|
||||
.empty-text {
|
||||
font-size: 28rpx;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -579,7 +579,7 @@ const handleCopyWeChat = (wechatNo) => {
|
|||
const previewPhotos = (photos, index) => {
|
||||
uni.previewImage({
|
||||
urls: photos,
|
||||
current: index
|
||||
current: photos[index] || photos[0]
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -257,12 +257,12 @@ export default {
|
|||
|
||||
// 用户协议
|
||||
const handleUserAgreement = () => {
|
||||
uni.showToast({ title: '功能开发中', icon: 'none' })
|
||||
uni.navigateTo({ url: '/pages/agreement/index?type=user' })
|
||||
}
|
||||
|
||||
// 隐私协议
|
||||
const handlePrivacyPolicy = () => {
|
||||
uni.showToast({ title: '功能开发中', icon: 'none' })
|
||||
uni.navigateTo({ url: '/pages/agreement/index?type=privacy' })
|
||||
}
|
||||
|
||||
// 退出登录
|
||||
|
|
|
|||
|
|
@ -486,7 +486,7 @@ const previewPhoto = (index) => {
|
|||
const urls = displayPhotos.value.map(p => p.photoUrl)
|
||||
uni.previewImage({
|
||||
urls,
|
||||
current: index
|
||||
current: urls[index] || urls[0]
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1276,8 +1276,8 @@ const loadProfile = async () => {
|
|||
formData.introduction = profile.introduction || ''
|
||||
formData.weChatNo = profile.weChatNo || ''
|
||||
|
||||
// 照片 - 需要拼接完整URL
|
||||
const STATIC_URL = 'http://localhost:5001'
|
||||
// 照片 - 需要拼接完整URL(使用AppApi地址)
|
||||
const STATIC_URL = 'http://localhost:5000'
|
||||
if (profile.photos && profile.photos.length > 0) {
|
||||
formData.photos = profile.photos.map(p => ({
|
||||
id: p.id,
|
||||
|
|
|
|||
|
|
@ -114,10 +114,10 @@ export default {
|
|||
uni.showLoading({ title: '上传中...' })
|
||||
|
||||
try {
|
||||
// 上传图片
|
||||
// 上传图片(使用AppApi地址)
|
||||
const uploadRes = await new Promise((resolve, reject) => {
|
||||
uni.uploadFile({
|
||||
url: `${import.meta.env.VITE_API_BASE_URL || 'http://localhost:5001/api'}/app/upload`,
|
||||
url: `${import.meta.env.VITE_API_BASE_URL || 'http://localhost:5000/api'}/app/upload`,
|
||||
filePath: tempFilePath,
|
||||
name: 'file',
|
||||
header: {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
*/
|
||||
|
||||
// 后端服务器基础地址(图片资源 - 使用AppApi,因为图片存储在AppApi的wwwroot下)
|
||||
const IMAGE_BASE_URL = 'http://localhost:5001'
|
||||
const IMAGE_BASE_URL = 'http://localhost:5000'
|
||||
|
||||
/**
|
||||
* 获取完整的图片URL
|
||||
|
|
|
|||
|
|
@ -61,6 +61,62 @@ public class AdminConfigController : ControllerBase
|
|||
var configs = await _configService.GetAllConfigsAsync();
|
||||
return ApiResponse<Dictionary<string, string>>.Success(configs);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取用户协议
|
||||
/// </summary>
|
||||
[HttpGet("userAgreement")]
|
||||
public async Task<ApiResponse<AgreementResponse>> GetUserAgreement()
|
||||
{
|
||||
var content = await _configService.GetUserAgreementAsync();
|
||||
return ApiResponse<AgreementResponse>.Success(new AgreementResponse
|
||||
{
|
||||
Content = content ?? ""
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置用户协议
|
||||
/// </summary>
|
||||
[HttpPost("userAgreement")]
|
||||
public async Task<ApiResponse> SetUserAgreement([FromBody] SetAgreementRequest request)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(request.Content))
|
||||
{
|
||||
return ApiResponse.Error(40001, "协议内容不能为空");
|
||||
}
|
||||
|
||||
var result = await _configService.SetUserAgreementAsync(request.Content);
|
||||
return result ? ApiResponse.Success("设置成功") : ApiResponse.Error(40001, "设置失败");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取隐私协议
|
||||
/// </summary>
|
||||
[HttpGet("privacyPolicy")]
|
||||
public async Task<ApiResponse<AgreementResponse>> GetPrivacyPolicy()
|
||||
{
|
||||
var content = await _configService.GetPrivacyPolicyAsync();
|
||||
return ApiResponse<AgreementResponse>.Success(new AgreementResponse
|
||||
{
|
||||
Content = content ?? ""
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置隐私协议
|
||||
/// </summary>
|
||||
[HttpPost("privacyPolicy")]
|
||||
public async Task<ApiResponse> SetPrivacyPolicy([FromBody] SetAgreementRequest request)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(request.Content))
|
||||
{
|
||||
return ApiResponse.Error(40001, "协议内容不能为空");
|
||||
}
|
||||
|
||||
var result = await _configService.SetPrivacyPolicyAsync(request.Content);
|
||||
return result ? ApiResponse.Success("设置成功") : ApiResponse.Error(40001, "设置失败");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -84,3 +140,25 @@ public class SetDefaultAvatarRequest
|
|||
/// </summary>
|
||||
public string AvatarUrl { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 协议响应
|
||||
/// </summary>
|
||||
public class AgreementResponse
|
||||
{
|
||||
/// <summary>
|
||||
/// 协议内容
|
||||
/// </summary>
|
||||
public string Content { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置协议请求
|
||||
/// </summary>
|
||||
public class SetAgreementRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// 协议内容
|
||||
/// </summary>
|
||||
public string Content { get; set; } = string.Empty;
|
||||
}
|
||||
|
|
|
|||
13
server/src/XiangYi.AdminApi/Properties/launchSettings.json
Normal file
13
server/src/XiangYi.AdminApi/Properties/launchSettings.json
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"profiles": {
|
||||
"XiangYi.AdminApi": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": false,
|
||||
"applicationUrl": "http://localhost:5001",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -37,7 +37,7 @@
|
|||
"Storage": {
|
||||
"Provider": "Local",
|
||||
"Local": {
|
||||
"BasePath": "wwwroot/uploads",
|
||||
"BasePath": "../XiangYi.AppApi/wwwroot/uploads",
|
||||
"BaseUrl": "/uploads"
|
||||
},
|
||||
"TencentCos": {
|
||||
|
|
|
|||
|
|
@ -14,11 +14,16 @@ namespace XiangYi.AppApi.Controllers;
|
|||
public class ConfigController : ControllerBase
|
||||
{
|
||||
private readonly IConfigService _configService;
|
||||
private readonly ISystemConfigService _systemConfigService;
|
||||
private readonly ILogger<ConfigController> _logger;
|
||||
|
||||
public ConfigController(IConfigService configService, ILogger<ConfigController> logger)
|
||||
public ConfigController(
|
||||
IConfigService configService,
|
||||
ISystemConfigService systemConfigService,
|
||||
ILogger<ConfigController> logger)
|
||||
{
|
||||
_configService = configService;
|
||||
_systemConfigService = systemConfigService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
|
|
@ -57,4 +62,45 @@ public class ConfigController : ControllerBase
|
|||
var result = await _configService.GetAllPopupConfigsAsync();
|
||||
return ApiResponse<List<PopupConfigResponse>>.Success(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取用户协议
|
||||
/// </summary>
|
||||
/// <returns>用户协议内容</returns>
|
||||
[HttpGet("userAgreement")]
|
||||
[AllowAnonymous]
|
||||
public async Task<ApiResponse<AgreementContentResponse>> GetUserAgreement()
|
||||
{
|
||||
var content = await _systemConfigService.GetUserAgreementAsync();
|
||||
return ApiResponse<AgreementContentResponse>.Success(new AgreementContentResponse
|
||||
{
|
||||
Content = content ?? "暂无用户协议内容"
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取隐私协议
|
||||
/// </summary>
|
||||
/// <returns>隐私协议内容</returns>
|
||||
[HttpGet("privacyPolicy")]
|
||||
[AllowAnonymous]
|
||||
public async Task<ApiResponse<AgreementContentResponse>> GetPrivacyPolicy()
|
||||
{
|
||||
var content = await _systemConfigService.GetPrivacyPolicyAsync();
|
||||
return ApiResponse<AgreementContentResponse>.Success(new AgreementContentResponse
|
||||
{
|
||||
Content = content ?? "暂无隐私协议内容"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 协议内容响应
|
||||
/// </summary>
|
||||
public class AgreementContentResponse
|
||||
{
|
||||
/// <summary>
|
||||
/// 协议内容
|
||||
/// </summary>
|
||||
public string Content { get; set; } = string.Empty;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
"dotnetRunMessages": true,
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "swagger",
|
||||
"applicationUrl": "http://localhost:5001",
|
||||
"applicationUrl": "http://localhost:5000",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
"dotnetRunMessages": true,
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "swagger",
|
||||
"applicationUrl": "https://localhost:7001;http://localhost:5001",
|
||||
"applicationUrl": "https://localhost:7000;http://localhost:5000",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,4 +29,24 @@ public interface ISystemConfigService
|
|||
/// 获取所有配置
|
||||
/// </summary>
|
||||
Task<Dictionary<string, string>> GetAllConfigsAsync();
|
||||
|
||||
/// <summary>
|
||||
/// 获取用户协议内容
|
||||
/// </summary>
|
||||
Task<string?> GetUserAgreementAsync();
|
||||
|
||||
/// <summary>
|
||||
/// 设置用户协议内容
|
||||
/// </summary>
|
||||
Task<bool> SetUserAgreementAsync(string content);
|
||||
|
||||
/// <summary>
|
||||
/// 获取隐私协议内容
|
||||
/// </summary>
|
||||
Task<string?> GetPrivacyPolicyAsync();
|
||||
|
||||
/// <summary>
|
||||
/// 设置隐私协议内容
|
||||
/// </summary>
|
||||
Task<bool> SetPrivacyPolicyAsync(string content);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,16 @@ public class SystemConfigService : ISystemConfigService
|
|||
/// </summary>
|
||||
public const string DefaultAvatarKey = "default_avatar";
|
||||
|
||||
/// <summary>
|
||||
/// 用户协议配置键
|
||||
/// </summary>
|
||||
public const string UserAgreementKey = "user_agreement";
|
||||
|
||||
/// <summary>
|
||||
/// 隐私协议配置键
|
||||
/// </summary>
|
||||
public const string PrivacyPolicyKey = "privacy_policy";
|
||||
|
||||
public SystemConfigService(
|
||||
IRepository<SystemConfig> configRepository,
|
||||
ILogger<SystemConfigService> logger)
|
||||
|
|
@ -92,4 +102,28 @@ public class SystemConfigService : ISystemConfigService
|
|||
var configs = await _configRepository.GetListAsync(c => true);
|
||||
return configs.ToDictionary(c => c.ConfigKey, c => c.ConfigValue);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<string?> GetUserAgreementAsync()
|
||||
{
|
||||
return await GetConfigValueAsync(UserAgreementKey);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<bool> SetUserAgreementAsync(string content)
|
||||
{
|
||||
return await SetConfigValueAsync(UserAgreementKey, content, "用户协议内容");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<string?> GetPrivacyPolicyAsync()
|
||||
{
|
||||
return await GetConfigValueAsync(PrivacyPolicyKey);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<bool> SetPrivacyPolicyAsync(string content)
|
||||
{
|
||||
return await SetConfigValueAsync(PrivacyPolicyKey, content, "隐私协议内容");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user