feat(aigc): add user agreement page and update consent dialog

Add new AIGC user agreement page with scrollable markdown rendering, loading and retry states.
Register the new route in pages.json.
Restructure AigcConsentDialog's content into styled sections, and update its rule click handler to navigate directly to the new agreement page.
This commit is contained in:
duanshuwen
2026-07-23 20:45:30 +08:00
parent 021d1fc77d
commit d71bda5e0b
5 changed files with 223 additions and 81 deletions

View File

@@ -0,0 +1,53 @@
<template>
<view class="aigc-agreement-page">
<TopNavBar title="智念AI用户规则" background="#ffffff" title-color="#172033" back-icon-color="#172033"
:align-with-menu-button="true" :z-index="3" />
<scroll-view class="aigc-agreement-scroll" scroll-y>
<view v-if="agreement" class="aigc-agreement-document">
<zero-markdown-view :markdown="agreement" />
</view>
<view v-else class="aigc-agreement-state">
<text>{{ loading ? "协议加载中..." : "协议内容加载失败" }}</text>
<button v-if="!loading" class="aigc-agreement-retry" @tap="fetchAgreement">
重新加载
</button>
</view>
</scroll-view>
</view>
</template>
<script setup>
import { ref } from "vue";
import { onLoad } from "@dcloudio/uni-app";
import TopNavBar from "@/components/TopNavBar/index.vue";
import { getUserAgreement } from "@/request/api/AigcApi.js";
const agreement = ref("");
const loading = ref(false);
const fetchAgreement = async () => {
if (loading.value) return;
loading.value = true;
try {
const res = await getUserAgreement();
agreement.value =
res?.code === 0 && typeof res.data === "string" ? res.data.trim() : "";
} catch (error) {
agreement.value = "";
console.error("获取智念AI用户规则失败", error);
} finally {
loading.value = false;
}
};
onLoad(() => {
fetchAgreement();
});
</script>
<style scoped lang="scss">
@import "./styles/index.scss";
</style>

View File

@@ -0,0 +1,54 @@
.aigc-agreement-page {
width: 100%;
height: 100vh;
display: flex;
flex-direction: column;
overflow: hidden;
background: #ffffff;
}
.aigc-agreement-scroll {
height: 0;
flex: 1 1 auto;
min-height: 0;
background: #ffffff;
}
.aigc-agreement-document {
min-height: 100%;
padding: 20px 16px calc(24px + env(safe-area-inset-bottom));
box-sizing: border-box;
}
.aigc-agreement-state {
min-height: 240px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 24px;
color: #7b8798;
font-size: 14px;
line-height: 22px;
box-sizing: border-box;
}
.aigc-agreement-retry {
width: 120px;
height: 42px;
display: flex;
align-items: center;
justify-content: center;
margin: 16px 0 0;
padding: 0;
border-radius: 999px;
background: #102942;
color: #ffffff;
font-size: 14px;
line-height: 20px;
font-weight: 800;
}
.aigc-agreement-retry::after {
border: 0;
}

View File

@@ -1,41 +1,37 @@
<template> <template>
<uni-popup <uni-popup ref="popupRef" type="center" background-color="transparent" mask-background-color="rgba(0, 0, 0, 0.58)"
ref="popupRef" :safe-area="false" :is-mask-click="false" @change="handlePopupChange">
type="center"
background-color="transparent"
mask-background-color="rgba(0, 0, 0, 0.58)"
:safe-area="false"
:is-mask-click="false"
@change="handlePopupChange"
>
<view class="aigc-consent-overlay" @touchmove.stop.prevent> <view class="aigc-consent-overlay" @touchmove.stop.prevent>
<view class="aigc-consent-dialog"> <view class="aigc-consent-dialog">
<text class="aigc-consent-title">温馨提示</text> <text class="aigc-consent-title">温馨提示</text>
<view class="aigc-consent-content"> <view class="aigc-consent-content">
<text v-if="content">{{ content }}</text> <text v-if="content" class="aigc-consent-paragraph">{{ content }}</text>
<template v-else> <template v-else>
<text>{{ defaultContentPrefix }}</text> <view class="aigc-consent-section">
<text class="aigc-consent-rule" @tap.stop="handleRuleClick"> <text class="aigc-consent-section-title">体验权益</text>
{{ ruleName }} <text class="aigc-consent-paragraph">{{ experienceContent }}</text>
</text> </view>
<text>{{ defaultContentSuffix }}</text>
<view class="aigc-consent-section">
<text class="aigc-consent-section-title">服务说明</text>
<text class="aigc-consent-paragraph">{{ serviceContent }}</text>
<view class="aigc-consent-paragraph">
<text>请您在使用前仔细阅读</text>
<text class="aigc-consent-rule" @tap.stop="handleRuleClick">
{{ ruleName }}
</text>
<text>{{ ruleConsentSuffix }}</text>
</view>
</view>
</template> </template>
</view> </view>
<view class="aigc-consent-actions"> <view class="aigc-consent-actions">
<button <button class="aigc-consent-button aigc-consent-cancel" type="default" @tap.stop="handleCancel">
class="aigc-consent-button aigc-consent-cancel"
type="default"
@tap.stop="handleCancel"
>
取消 取消
</button> </button>
<button <button class="aigc-consent-button aigc-consent-agree" type="default" @tap.stop="handleAgree">
class="aigc-consent-button aigc-consent-agree"
type="default"
@tap.stop="handleAgree"
>
我同意 我同意
</button> </button>
</view> </view>
@@ -66,16 +62,19 @@ const props = defineProps({
}, },
}); });
const emit = defineEmits(["update:visible", "cancel", "agree", "ruleClick"]); const emit = defineEmits(["update:visible", "cancel", "agree"]);
const popupRef = ref(null); const popupRef = ref(null);
const defaultContentPrefix = computed( const experienceContent =
"当前体验版本赠送 400 积分,可生成 1 张高清旅行图片和 1 段 5 秒内动效视频。如需更多生成额度,敬请期待后续版本上线。";
const serviceContent = computed(
() => () =>
`欢迎使用【${props.companyName}】提供的AI内容升级服务。本服务通过人工智能技术AIGC您上传的照片或视频与景区场景模板进行创意优化,为您生成具有特定风格的旅行内容。请您仔细阅读` `欢迎使用【${props.companyName}】提供的 AI 内容升级服务。本服务通过人工智能技术AIGC,对您上传的照片或视频进行处理,并结合景区场景模板,为您生成具有特定风格的旅行图片或视频内容。`
); );
const defaultContentSuffix = const ruleConsentSuffix =
",您点击“我同意”等进行下一步操作的行为均视为您同意受本规则约束。如您不同意,您可以直接关闭页面。"; "点击“我同意”并继续操作,即表示您已阅读、理解并同意遵守相关规则;如您不同意,可点击“取消”并退出本服务。";
const close = () => { const close = () => {
popupRef.value?.close(); popupRef.value?.close();
@@ -93,7 +92,15 @@ const handleAgree = () => {
}; };
const handleRuleClick = () => { const handleRuleClick = () => {
emit("ruleClick"); uni.navigateTo({
url: "/pages-aigc/agreement/agreement",
fail: () => {
uni.showToast({
title: "打开用户规则失败",
icon: "none",
});
},
});
}; };
const syncPopupVisible = async (visible) => { const syncPopupVisible = async (visible) => {

View File

@@ -31,6 +31,28 @@
text-align: justify; text-align: justify;
} }
.aigc-consent-section + .aigc-consent-section {
margin-top: 18px;
}
.aigc-consent-section-title,
.aigc-consent-paragraph {
display: block;
}
.aigc-consent-section-title {
margin-bottom: 6px;
color: #172033;
font-size: 16px;
line-height: 24px;
font-weight: 900;
text-align: left;
}
.aigc-consent-paragraph + .aigc-consent-paragraph {
margin-top: 10px;
}
.aigc-consent-rule { .aigc-consent-rule {
color: #18bd78; color: #18bd78;
font-weight: 900; font-weight: 900;

View File

@@ -158,56 +158,62 @@
{ {
"root": "pages-aigc", "root": "pages-aigc",
"pages": [ "pages": [
{ {
"path": "home/home", "path": "home/home",
"style": { "style": {
"navigationStyle": "custom" "navigationStyle": "custom"
} }
}, },
{ {
"path": "useTemplate/useTemplate", "path": "agreement/agreement",
"style": { "style": {
"navigationStyle": "custom" "navigationStyle": "custom"
} }
}, },
{ {
"path": "progress/progress", "path": "useTemplate/useTemplate",
"style": { "style": {
"navigationStyle": "custom" "navigationStyle": "custom"
} }
}, },
{ {
"path": "detail/detail", "path": "progress/progress",
"style": { "style": {
"navigationStyle": "custom" "navigationStyle": "custom"
} }
}, },
{ {
"path": "sharedWork/sharedWork", "path": "detail/detail",
"style": { "style": {
"navigationStyle": "custom" "navigationStyle": "custom"
} }
}, },
{ {
"path": "recharge/recharge", "path": "sharedWork/sharedWork",
"style": { "style": {
"navigationStyle": "custom" "navigationStyle": "custom"
} }
}, },
{ {
"path": "pointsDetails/pointsDetails", "path": "recharge/recharge",
"style": { "style": {
"navigationStyle": "custom" "navigationStyle": "custom"
} }
}, },
{ {
"path": "record/record", "path": "pointsDetails/pointsDetails",
"style": { "style": {
"navigationStyle": "custom" "navigationStyle": "custom"
} }
} },
] {
} "path": "record/record",
"style": {
"navigationStyle": "custom"
}
}
]
}
], ],
"globalStyle": { "globalStyle": {
"navigationBarTextstyle": "black", "navigationBarTextstyle": "black",