feat(miniapp): add privacy consent and legal pages
Show privacy popup on launch, gate login on legal consent, and link user agreement and privacy policy pages in the mini program. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -22,7 +22,18 @@ The H5 APIs are reused as-is. Native Mini Program login posts the `wx.login` cod
|
||||
- `utils/config.js` 默认 `LOCAL_DEV = false`,API 指向 **`https://m.tkmind.cn`**。
|
||||
- `project.config.json` 中 `urlCheck: true`;DevTools 需配置真实小程序 AppID(非 tourist)。
|
||||
- 微信公众平台需配置 request / web-view 合法域名:`m.tkmind.cn`。
|
||||
- 已启用 `__usePrivacyCheck__`,登录页含协议勾选(默认不勾选)、协议全文页与微信隐私弹窗组件。
|
||||
- 首次登录须主动勾选协议;登录成功后本地保存 cookie 与同意记录,下次自动登录不再展示协议区。
|
||||
- 此配置**不会**触发 103 发版;仅小程序客户端连线上 Portal。
|
||||
|
||||
### 微信公众平台后台(提审必配)
|
||||
|
||||
1. **设置 → 服务内容声明 → 用户隐私保护指引**
|
||||
- 声明收集:用户账号、邮箱、微信登录标识、聊天记录、设备信息
|
||||
- 用途:账号登录验证、身份识别、提供 AI 对话与会话服务
|
||||
2. **设置 → 基本设置 → 服务内容声明**
|
||||
- 补充用户服务协议与隐私政策说明(协议已内置在小程序 `pages/legal/`)
|
||||
3. 提审备注可写:「登录页已增加协议勾选与隐私政策全文,未同意前不提交用户信息;已接入微信隐私保护检测」
|
||||
- **微信一键登录**还需 Portal 侧配置 `H5_WECHAT_MINIAPP_APP_ID` / `H5_WECHAT_MINIAPP_APP_SECRET` 并实现 `/auth/wechat-miniapp/login`;未部署前可先用账号密码登录。
|
||||
|
||||
### 开发者工具仍显示「游客模式」时
|
||||
|
||||
+4
-1
@@ -1,12 +1,14 @@
|
||||
const { getStoredSession, setApiBaseUrl, getMiniProgramAppId, isTouristMode, getApiBaseUrl } = require('./utils/api');
|
||||
const { API_BASE_URL } = require('./utils/config');
|
||||
const { getPrivacySetting } = require('./utils/privacy');
|
||||
|
||||
App({
|
||||
globalData: {
|
||||
user: null,
|
||||
privacySetting: null,
|
||||
},
|
||||
|
||||
onLaunch() {
|
||||
async onLaunch() {
|
||||
setApiBaseUrl(API_BASE_URL);
|
||||
const appId = getMiniProgramAppId();
|
||||
console.info('[TKMind miniapp] API_BASE_URL =', getApiBaseUrl() || API_BASE_URL);
|
||||
@@ -18,5 +20,6 @@ App({
|
||||
if (session?.user) {
|
||||
this.globalData.user = session.user;
|
||||
}
|
||||
this.globalData.privacySetting = await getPrivacySetting();
|
||||
},
|
||||
});
|
||||
|
||||
+5
-1
@@ -4,8 +4,12 @@
|
||||
"pages/chat/index",
|
||||
"pages/sessions/index",
|
||||
"pages/space/index",
|
||||
"pages/webview/index"
|
||||
"pages/webview/index",
|
||||
"pages/legal/index/index",
|
||||
"pages/legal/privacy/index",
|
||||
"pages/legal/terms/index"
|
||||
],
|
||||
"__usePrivacyCheck__": true,
|
||||
"window": {
|
||||
"navigationBarTitleText": "TKMind",
|
||||
"navigationBarBackgroundColor": "#101820",
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
const { openPrivacyContract } = require('../../utils/privacy');
|
||||
|
||||
Component({
|
||||
data: {
|
||||
show: false,
|
||||
privacyContractName: '《用户隐私保护指引》',
|
||||
},
|
||||
|
||||
lifetimes: {
|
||||
attached() {
|
||||
if (typeof wx.onNeedPrivacyAuthorization !== 'function') {
|
||||
return;
|
||||
}
|
||||
wx.onNeedPrivacyAuthorization((resolve) => {
|
||||
this._privacyResolve = resolve;
|
||||
this.setData({ show: true });
|
||||
});
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
openPrivacyContract() {
|
||||
openPrivacyContract();
|
||||
},
|
||||
|
||||
handleDisagree() {
|
||||
this.setData({ show: false });
|
||||
if (this._privacyResolve) {
|
||||
this._privacyResolve({ event: 'disagree' });
|
||||
this._privacyResolve = null;
|
||||
}
|
||||
},
|
||||
|
||||
handleAgree() {
|
||||
this.setData({ show: false });
|
||||
if (this._privacyResolve) {
|
||||
this._privacyResolve({
|
||||
buttonId: 'agree-privacy-btn',
|
||||
event: 'agree',
|
||||
});
|
||||
this._privacyResolve = null;
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"component": true,
|
||||
"usingComponents": {}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<view wx:if="{{show}}" class="privacy-mask">
|
||||
<view class="privacy-panel">
|
||||
<text class="privacy-title">用户隐私保护提示</text>
|
||||
<view class="privacy-body">
|
||||
<text>在使用 TKMind 前,请阅读</text>
|
||||
<text class="privacy-link" bindtap="openPrivacyContract">{{privacyContractName}}</text>
|
||||
<text>。点击「同意」即表示你已阅读并同意该指引。</text>
|
||||
</view>
|
||||
<view class="privacy-actions">
|
||||
<button class="privacy-decline" bindtap="handleDisagree">拒绝</button>
|
||||
<button
|
||||
id="agree-privacy-btn"
|
||||
class="privacy-accept"
|
||||
open-type="agreePrivacyAuthorization"
|
||||
bindagreeprivacyauthorization="handleAgree"
|
||||
>
|
||||
同意
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -0,0 +1,65 @@
|
||||
.privacy-mask {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 2000;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 40rpx;
|
||||
background: rgba(15, 23, 42, 0.55);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.privacy-panel {
|
||||
width: 100%;
|
||||
max-width: 640rpx;
|
||||
padding: 36rpx;
|
||||
border-radius: 16rpx;
|
||||
background: #ffffff;
|
||||
box-shadow: 0 10rpx 30rpx rgba(15, 23, 42, 0.12);
|
||||
}
|
||||
|
||||
.privacy-title {
|
||||
display: block;
|
||||
font-size: 34rpx;
|
||||
font-weight: 800;
|
||||
color: #101820;
|
||||
}
|
||||
|
||||
.privacy-body {
|
||||
margin-top: 24rpx;
|
||||
font-size: 26rpx;
|
||||
color: #334155;
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
.privacy-link {
|
||||
color: #0f766e;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.privacy-actions {
|
||||
display: flex;
|
||||
gap: 16rpx;
|
||||
margin-top: 32rpx;
|
||||
}
|
||||
|
||||
.privacy-decline,
|
||||
.privacy-accept {
|
||||
flex: 1;
|
||||
height: 80rpx;
|
||||
border-radius: 12rpx;
|
||||
font-size: 28rpx;
|
||||
font-weight: 700;
|
||||
line-height: 80rpx;
|
||||
}
|
||||
|
||||
.privacy-decline {
|
||||
background: #f1f5f9;
|
||||
color: #475569;
|
||||
}
|
||||
|
||||
.privacy-accept {
|
||||
background: #0f766e;
|
||||
color: #ffffff;
|
||||
}
|
||||
@@ -206,6 +206,10 @@ Page({
|
||||
this.setData({ sessionId: '', messages: [], draft: '', error: '' });
|
||||
},
|
||||
|
||||
openLegalCenter() {
|
||||
wx.navigateTo({ url: '/pages/legal/index/index' });
|
||||
},
|
||||
|
||||
openLink(event) {
|
||||
let url = String(event.currentTarget.dataset.url || '').trim();
|
||||
if (!url) {
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
{
|
||||
"navigationBarTitleText": "TKMind 聊天"
|
||||
"navigationBarTitleText": "TKMind 聊天",
|
||||
"usingComponents": {
|
||||
"privacy-popup": "/components/privacy-popup/index"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
</view>
|
||||
<view class="header-actions">
|
||||
<button class="header-button" bindtap="startNewChat">新聊天</button>
|
||||
<button class="header-button" bindtap="openLegalCenter">隐私</button>
|
||||
<button class="header-button" bindtap="handleLogout">退出</button>
|
||||
</view>
|
||||
</view>
|
||||
@@ -71,3 +72,5 @@
|
||||
<button class="send" loading="{{loading}}" disabled="{{loading}}" bindtap="sendMessage">发送</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<privacy-popup />
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
const { openPrivacyContract } = require('../../../utils/privacy');
|
||||
|
||||
Page({
|
||||
openTerms() {
|
||||
wx.navigateTo({ url: '/pages/legal/terms/index' });
|
||||
},
|
||||
|
||||
openPrivacy() {
|
||||
wx.navigateTo({ url: '/pages/legal/privacy/index' });
|
||||
},
|
||||
|
||||
openWechatPrivacy() {
|
||||
openPrivacyContract();
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"navigationBarTitleText": "隐私与协议"
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
<view class="page legal-hub-page">
|
||||
<view class="panel legal-hub-card">
|
||||
<text class="legal-hub-title">隐私与协议</text>
|
||||
<text class="legal-hub-desc">
|
||||
你可以随时查看 TKMind 的用户服务协议与隐私政策,了解我们如何收集、使用与保护你的个人信息。
|
||||
</text>
|
||||
|
||||
<view class="legal-hub-item" bindtap="openTerms">
|
||||
<text class="legal-hub-item-title">用户服务协议</text>
|
||||
<text class="legal-hub-item-desc">服务范围、账号使用规范与免责声明</text>
|
||||
</view>
|
||||
|
||||
<view class="legal-hub-item" bindtap="openPrivacy">
|
||||
<text class="legal-hub-item-title">隐私政策</text>
|
||||
<text class="legal-hub-item-desc">个人信息收集目的、方式、用途与您的权利</text>
|
||||
</view>
|
||||
|
||||
<view class="legal-hub-item" bindtap="openWechatPrivacy">
|
||||
<text class="legal-hub-item-title">微信用户隐私保护指引</text>
|
||||
<text class="legal-hub-item-desc">查看微信平台提供的隐私保护说明</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -0,0 +1,44 @@
|
||||
.legal-hub-page {
|
||||
padding-top: 24rpx;
|
||||
}
|
||||
|
||||
.legal-hub-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 24rpx;
|
||||
padding: 32rpx;
|
||||
}
|
||||
|
||||
.legal-hub-title {
|
||||
font-size: 40rpx;
|
||||
font-weight: 800;
|
||||
color: #101820;
|
||||
}
|
||||
|
||||
.legal-hub-desc {
|
||||
font-size: 26rpx;
|
||||
color: #475569;
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
.legal-hub-item {
|
||||
padding: 24rpx;
|
||||
border: 1rpx solid #dbe3eb;
|
||||
border-radius: 12rpx;
|
||||
background: #f8fafc;
|
||||
}
|
||||
|
||||
.legal-hub-item-title {
|
||||
display: block;
|
||||
font-size: 30rpx;
|
||||
font-weight: 700;
|
||||
color: #0f766e;
|
||||
}
|
||||
|
||||
.legal-hub-item-desc {
|
||||
display: block;
|
||||
margin-top: 8rpx;
|
||||
font-size: 24rpx;
|
||||
color: #64748b;
|
||||
line-height: 1.6;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
Page({});
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"navigationBarTitleText": "隐私政策"
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
<view class="page legal-page">
|
||||
<view class="panel legal-card">
|
||||
<text class="legal-title">TKMind 隐私政策</text>
|
||||
<text class="legal-meta">更新日期:2026年7月12日 · 生效日期:2026年7月12日</text>
|
||||
|
||||
<view class="legal-section">
|
||||
<text class="legal-heading">引言</text>
|
||||
<text class="legal-paragraph">
|
||||
TKMind(以下简称「我们」)重视您的个人信息保护。本隐私政策说明我们在您使用 TKMind 微信小程序(以下简称「本小程序」)时,如何收集、使用、存储与保护您的个人信息,以及您享有的相关权利。
|
||||
</text>
|
||||
<text class="legal-paragraph">
|
||||
在您点击同意本政策并使用登录功能前,我们不会收集您的账号、邮箱、密码或微信登录凭证。请您在使用本小程序前仔细阅读并充分理解本政策。
|
||||
</text>
|
||||
</view>
|
||||
|
||||
<view class="legal-section">
|
||||
<text class="legal-heading">一、我们收集的信息</text>
|
||||
<text class="legal-paragraph">
|
||||
为实现账号登录与核心功能,我们将在您同意本政策后,收集以下个人信息:
|
||||
</text>
|
||||
<text class="legal-item">1. 账号与邮箱信息:您主动填写的用户名、邮箱地址,用于账号识别、登录验证与账号找回。</text>
|
||||
<text class="legal-item">2. 登录凭证:您输入的登录密码仅用于身份验证,经加密传输,不在客户端明文保存。</text>
|
||||
<text class="legal-item">3. 微信登录信息:当您选择「微信一键登录」时,我们会通过微信提供的临时登录凭证(code)换取您的微信用户标识(如 openid),用于完成身份验证与账号绑定。</text>
|
||||
<text class="legal-item">4. 会话与业务数据:您与 AI 的对话内容、会话记录、MindSpace 页面访问记录,以及您主动提交的业务数据。</text>
|
||||
<text class="legal-item">5. 设备与日志信息:为保障服务安全与稳定,我们可能记录设备型号、操作系统版本、网络类型、操作时间、接口请求日志等必要技术信息。</text>
|
||||
</view>
|
||||
|
||||
<view class="legal-section">
|
||||
<text class="legal-heading">二、信息的使用目的与方式</text>
|
||||
<text class="legal-paragraph">我们仅在以下目的范围内使用您的个人信息:</text>
|
||||
<text class="legal-item">1. 身份验证:核验您的登录身份,建立并维持您的账号会话。</text>
|
||||
<text class="legal-item">2. 提供服务:向您提供 AI 对话、会话管理、MindSpace 页面浏览等核心功能。</text>
|
||||
<text class="legal-item">3. 安全保障:识别异常登录、防范欺诈与滥用,保障系统与数据安全。</text>
|
||||
<text class="legal-item">4. 服务改进:在符合法律法规的前提下,用于故障排查、性能优化与体验改进。</text>
|
||||
<text class="legal-paragraph">
|
||||
我们不会将您的个人信息用于与本政策所述目的无关的用途。如需用于其他目的,我们将另行征得您的同意。
|
||||
</text>
|
||||
</view>
|
||||
|
||||
<view class="legal-section">
|
||||
<text class="legal-heading">三、信息的存储与保护</text>
|
||||
<text class="legal-item">1. 您的个人信息将存储于中华人民共和国境内的安全服务器,并采取访问控制、传输加密等合理安全措施。</text>
|
||||
<text class="legal-item">2. 我们仅在实现本政策所述目的所必需的期限内保留您的个人信息,法律法规另有规定的除外。</text>
|
||||
<text class="legal-item">3. 如发生安全事件,我们将依法及时告知您并采取补救措施。</text>
|
||||
</view>
|
||||
|
||||
<view class="legal-section">
|
||||
<text class="legal-heading">四、信息的共享与披露</text>
|
||||
<text class="legal-paragraph">
|
||||
我们不会向第三方出售您的个人信息。仅在以下情形中,我们可能会共享或披露相关信息:
|
||||
</text>
|
||||
<text class="legal-item">1. 事先获得您的明确同意。</text>
|
||||
<text class="legal-item">2. 根据法律法规、司法机关或政府主管部门的强制性要求。</text>
|
||||
<text class="legal-item">3. 为履行本政策所述服务目的,向受严格保密义务约束的服务提供方(如基础设施托管方)提供必要信息。</text>
|
||||
</view>
|
||||
|
||||
<view class="legal-section">
|
||||
<text class="legal-heading">五、您的权利</text>
|
||||
<text class="legal-paragraph">您依法享有以下权利:</text>
|
||||
<text class="legal-item">1. 查询与访问:您可登录后查看账号基本信息及会话数据。</text>
|
||||
<text class="legal-item">2. 更正与补充:如发现信息有误,可通过应用内功能或联系我们进行更正。</text>
|
||||
<text class="legal-item">3. 删除与注销:您可申请删除特定数据或注销账号;注销后我们将停止提供服务并依法删除或匿名化处理相关个人信息。</text>
|
||||
<text class="legal-item">4. 撤回同意:您可通过停止使用本小程序、退出登录等方式撤回授权;撤回后我们将不再处理相应个人信息,但不影响撤回前基于您同意已进行的处理。</text>
|
||||
</view>
|
||||
|
||||
<view class="legal-section">
|
||||
<text class="legal-heading">六、未成年人保护</text>
|
||||
<text class="legal-paragraph">
|
||||
若您为未成年人,请在监护人陪同下阅读本政策,并在取得监护人同意后使用本小程序。我们不会主动面向未满十四周岁的未成年人收集个人信息。
|
||||
</text>
|
||||
</view>
|
||||
|
||||
<view class="legal-section">
|
||||
<text class="legal-heading">七、政策更新</text>
|
||||
<text class="legal-paragraph">
|
||||
我们可能适时修订本政策。重大变更时,我们将通过小程序内提示等方式通知您。若您继续使用本小程序,即表示您同意更新后的政策。
|
||||
</text>
|
||||
</view>
|
||||
|
||||
<view class="legal-section">
|
||||
<text class="legal-heading">八、联系我们</text>
|
||||
<text class="legal-paragraph">
|
||||
如您对本隐私政策或个人信息处理有任何疑问、意见或投诉,请通过 TKMind 官方渠道(m.tkmind.cn)或小程序内反馈入口与我们联系,我们将在合理期限内予以答复。
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -0,0 +1,47 @@
|
||||
.legal-page {
|
||||
padding-bottom: 48rpx;
|
||||
}
|
||||
|
||||
.legal-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 28rpx;
|
||||
padding: 32rpx;
|
||||
}
|
||||
|
||||
.legal-title {
|
||||
font-size: 40rpx;
|
||||
font-weight: 800;
|
||||
color: #101820;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.legal-meta {
|
||||
font-size: 24rpx;
|
||||
color: #64748b;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.legal-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.legal-heading {
|
||||
font-size: 30rpx;
|
||||
font-weight: 700;
|
||||
color: #132028;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.legal-paragraph,
|
||||
.legal-item {
|
||||
font-size: 26rpx;
|
||||
color: #334155;
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
.legal-item {
|
||||
padding-left: 8rpx;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
Page({});
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"navigationBarTitleText": "用户服务协议"
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
<view class="page legal-page">
|
||||
<view class="panel legal-card">
|
||||
<text class="legal-title">TKMind 用户服务协议</text>
|
||||
<text class="legal-meta">更新日期:2026年7月12日 · 生效日期:2026年7月12日</text>
|
||||
|
||||
<view class="legal-section">
|
||||
<text class="legal-heading">一、协议范围</text>
|
||||
<text class="legal-paragraph">
|
||||
本协议是您与 TKMind 运营方之间,就您使用 TKMind 微信小程序(以下简称「本小程序」)所订立的法律协议。请您在注册、登录或使用本小程序前仔细阅读本协议。您点击同意或实际使用本小程序,即视为您已阅读并同意受本协议约束。
|
||||
</text>
|
||||
</view>
|
||||
|
||||
<view class="legal-section">
|
||||
<text class="legal-heading">二、服务内容</text>
|
||||
<text class="legal-paragraph">本小程序向您提供包括但不限于以下服务:</text>
|
||||
<text class="legal-item">1. AI 智能对话与助手服务;</text>
|
||||
<text class="legal-item">2. 会话历史管理与查看;</text>
|
||||
<text class="legal-item">3. MindSpace 页面浏览与预览;</text>
|
||||
<text class="legal-item">4. 微信一键登录及 H5 账号登录等身份验证服务。</text>
|
||||
<text class="legal-paragraph">
|
||||
我们有权根据业务发展需要调整服务内容,并将通过合理方式告知您。
|
||||
</text>
|
||||
</view>
|
||||
|
||||
<view class="legal-section">
|
||||
<text class="legal-heading">三、账号注册与使用</text>
|
||||
<text class="legal-item">1. 您应使用真实、合法、有效的账号信息完成登录,并妥善保管账号与密码,不得将账号转让、出借或供他人使用。</text>
|
||||
<text class="legal-item">2. 您应对使用本账号进行的所有操作行为负责。如发现账号被盗用或存在安全风险,请立即联系我们。</text>
|
||||
<text class="legal-item">3. 使用微信一键登录时,您授权我们依据微信平台规则获取完成登录所必需的用户标识信息。</text>
|
||||
</view>
|
||||
|
||||
<view class="legal-section">
|
||||
<text class="legal-heading">四、用户行为规范</text>
|
||||
<text class="legal-paragraph">您在使用本小程序时,不得从事以下行为:</text>
|
||||
<text class="legal-item">1. 发布、传播违反法律法规、公序良俗或侵犯他人合法权益的内容;</text>
|
||||
<text class="legal-item">2. 利用本小程序从事欺诈、骚扰、网络攻击或其他危害网络安全的行为;</text>
|
||||
<text class="legal-item">3. 未经授权访问、干扰或破坏本小程序及相关系统;</text>
|
||||
<text class="legal-item">4. 利用技术手段批量注册、滥用接口或干扰服务正常运行。</text>
|
||||
<text class="legal-paragraph">
|
||||
如您违反上述规定,我们有权采取警告、限制功能、暂停或终止服务等措施,并依法保留追究法律责任的权利。
|
||||
</text>
|
||||
</view>
|
||||
|
||||
<view class="legal-section">
|
||||
<text class="legal-heading">五、个人信息保护</text>
|
||||
<text class="legal-paragraph">
|
||||
我们重视您的个人信息保护。关于个人信息的收集、使用、存储方式及您的权利,请详见《隐私政策》。您使用本小程序即表示您已阅读并同意《隐私政策》。
|
||||
</text>
|
||||
</view>
|
||||
|
||||
<view class="legal-section">
|
||||
<text class="legal-heading">六、知识产权</text>
|
||||
<text class="legal-paragraph">
|
||||
本小程序的软件、界面设计、商标及相关内容的知识产权归 TKMind 运营方或相关权利人所有。未经授权,您不得复制、修改、传播或用于商业用途。
|
||||
</text>
|
||||
<text class="legal-paragraph">
|
||||
您通过本小程序生成或上传的内容,其权利归属依照相关法律法规及您与我们的约定确定。您应确保对所提交内容拥有合法权利,不侵犯第三方权益。
|
||||
</text>
|
||||
</view>
|
||||
|
||||
<view class="legal-section">
|
||||
<text class="legal-heading">七、免责声明</text>
|
||||
<text class="legal-item">1. AI 生成内容仅供参考,不构成专业建议(包括但不限于医疗、法律、投资等领域建议),您应自行判断并承担使用风险。</text>
|
||||
<text class="legal-item">2. 因不可抗力、网络故障、第三方服务中断或您自身原因导致的服务中断或数据损失,我们在法律允许范围内不承担责任。</text>
|
||||
<text class="legal-item">3. 我们将尽力保障服务稳定,但不保证服务持续无中断、无错误或完全满足您的特定需求。</text>
|
||||
</view>
|
||||
|
||||
<view class="legal-section">
|
||||
<text class="legal-heading">八、协议变更与终止</text>
|
||||
<text class="legal-paragraph">
|
||||
我们有权根据法律法规或业务需要修订本协议,并通过小程序内公告等方式通知您。若您不同意变更内容,可停止使用本小程序;若您继续使用,则视为接受变更后的协议。
|
||||
</text>
|
||||
<text class="legal-paragraph">
|
||||
您可随时停止使用本小程序。我们亦可在您严重违反本协议时终止向您提供服务。
|
||||
</text>
|
||||
</view>
|
||||
|
||||
<view class="legal-section">
|
||||
<text class="legal-heading">九、适用法律与争议解决</text>
|
||||
<text class="legal-paragraph">
|
||||
本协议的订立、执行与解释适用中华人民共和国法律。因本协议引起的争议,双方应友好协商解决;协商不成的,任何一方可向 TKMind 运营方所在地有管辖权的人民法院提起诉讼。
|
||||
</text>
|
||||
</view>
|
||||
|
||||
<view class="legal-section">
|
||||
<text class="legal-heading">十、联系我们</text>
|
||||
<text class="legal-paragraph">
|
||||
如您对本协议有任何疑问,请通过 TKMind 官方渠道(m.tkmind.cn)或小程序内反馈入口与我们联系。
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -0,0 +1 @@
|
||||
@import '../privacy/index.wxss';
|
||||
@@ -3,9 +3,19 @@ const {
|
||||
loginWithPassword,
|
||||
loginWithWechatCode,
|
||||
isTouristMode,
|
||||
getApiBaseUrl
|
||||
getApiBaseUrl,
|
||||
getStoredSession
|
||||
} = require('../../utils/api');
|
||||
const { MINIAPP_LOGIN_PATH } = require('../../utils/config');
|
||||
const {
|
||||
saveLegalConsent,
|
||||
shouldSkipLoginConsent
|
||||
} = require('../../utils/legal-consent');
|
||||
|
||||
function hasStoredSessionCookie() {
|
||||
const session = getStoredSession();
|
||||
return Boolean(session?.cookie);
|
||||
}
|
||||
|
||||
Page({
|
||||
data: {
|
||||
@@ -15,10 +25,18 @@ Page({
|
||||
wechatLoading: false,
|
||||
passwordLoading: false,
|
||||
touristWarning: '',
|
||||
envHint: ''
|
||||
envHint: '',
|
||||
agreedToLegal: false,
|
||||
showLegalConsent: true,
|
||||
showConsentModal: false
|
||||
},
|
||||
|
||||
onLoad() {
|
||||
const skipConsent = hasStoredSessionCookie() && shouldSkipLoginConsent();
|
||||
this.setData({
|
||||
agreedToLegal: false,
|
||||
showLegalConsent: !skipConsent
|
||||
});
|
||||
this.refreshEnvHints();
|
||||
this.tryAutoLogin();
|
||||
},
|
||||
@@ -40,6 +58,9 @@ Page({
|
||||
},
|
||||
|
||||
async tryAutoLogin() {
|
||||
if (!hasStoredSessionCookie()) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const status = await checkAuth();
|
||||
if (status?.authenticated) {
|
||||
@@ -62,10 +83,61 @@ Page({
|
||||
this.setData({ password: event.detail.value, error: '' });
|
||||
},
|
||||
|
||||
handleConsentChange(event) {
|
||||
const agreedToLegal = (event.detail.value || []).includes('agreed');
|
||||
this.setData({ agreedToLegal, error: '' });
|
||||
},
|
||||
|
||||
openTerms() {
|
||||
wx.navigateTo({ url: '/pages/legal/terms/index' });
|
||||
},
|
||||
|
||||
openPrivacy() {
|
||||
wx.navigateTo({ url: '/pages/legal/privacy/index' });
|
||||
},
|
||||
|
||||
openLegalCenter() {
|
||||
wx.navigateTo({ url: '/pages/legal/index/index' });
|
||||
},
|
||||
|
||||
ensureLegalConsent() {
|
||||
if (!this.data.showLegalConsent || this.data.agreedToLegal) {
|
||||
return true;
|
||||
}
|
||||
this.setData({
|
||||
error: '请先阅读并同意《用户服务协议》和《隐私政策》后再登录',
|
||||
showConsentModal: true
|
||||
});
|
||||
return false;
|
||||
},
|
||||
|
||||
acceptConsentModal() {
|
||||
this.setData({
|
||||
agreedToLegal: true,
|
||||
showConsentModal: false,
|
||||
error: ''
|
||||
});
|
||||
},
|
||||
|
||||
declineConsentModal() {
|
||||
this.setData({ showConsentModal: false });
|
||||
wx.showToast({
|
||||
title: '需同意协议后方可使用',
|
||||
icon: 'none'
|
||||
});
|
||||
},
|
||||
|
||||
persistConsentAfterLogin() {
|
||||
saveLegalConsent();
|
||||
},
|
||||
|
||||
async handleWechatLogin() {
|
||||
if (!this.ensureLegalConsent()) return;
|
||||
|
||||
this.setData({ wechatLoading: true, error: '' });
|
||||
try {
|
||||
await loginWithWechatCode(MINIAPP_LOGIN_PATH);
|
||||
this.persistConsentAfterLogin();
|
||||
wx.switchTab({ url: '/pages/chat/index' });
|
||||
} catch (error) {
|
||||
this.setData({
|
||||
@@ -79,6 +151,8 @@ Page({
|
||||
},
|
||||
|
||||
async handlePasswordLogin() {
|
||||
if (!this.ensureLegalConsent()) return;
|
||||
|
||||
const username = this.data.username.trim();
|
||||
const password = this.data.password;
|
||||
if (!username || !password) {
|
||||
@@ -88,6 +162,7 @@ Page({
|
||||
this.setData({ passwordLoading: true, error: '' });
|
||||
try {
|
||||
await loginWithPassword(username, password);
|
||||
this.persistConsentAfterLogin();
|
||||
wx.switchTab({ url: '/pages/chat/index' });
|
||||
} catch (error) {
|
||||
this.setData({ error: error?.message || '登录失败,请重试' });
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
{
|
||||
"navigationBarTitleText": "登录 TKMind"
|
||||
"navigationBarTitleText": "登录 TKMind",
|
||||
"usingComponents": {
|
||||
"privacy-popup": "/components/privacy-popup/index"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,11 @@
|
||||
<view wx:if="{{touristWarning}}" class="warn-box">{{touristWarning}}</view>
|
||||
<view wx:if="{{envHint}}" class="info-box">{{envHint}}</view>
|
||||
|
||||
<button class="primary-button" loading="{{wechatLoading}}" bindtap="handleWechatLogin">
|
||||
<button
|
||||
class="primary-button"
|
||||
loading="{{wechatLoading}}"
|
||||
bindtap="handleWechatLogin"
|
||||
>
|
||||
微信一键登录
|
||||
</button>
|
||||
|
||||
@@ -16,10 +20,62 @@
|
||||
|
||||
<input class="field" placeholder="用户名 / 邮箱" value="{{username}}" bindinput="handleUsername" />
|
||||
<input class="field" placeholder="密码" password value="{{password}}" bindinput="handlePassword" />
|
||||
<button class="secondary-button" loading="{{passwordLoading}}" bindtap="handlePasswordLogin">
|
||||
<button
|
||||
class="secondary-button"
|
||||
loading="{{passwordLoading}}"
|
||||
bindtap="handlePasswordLogin"
|
||||
>
|
||||
账号登录
|
||||
</button>
|
||||
|
||||
<view wx:if="{{showLegalConsent}}" class="consent-box">
|
||||
<checkbox-group bindchange="handleConsentChange">
|
||||
<label class="consent-row">
|
||||
<checkbox value="agreed" checked="{{agreedToLegal}}" color="#0f766e" />
|
||||
<view class="consent-text">
|
||||
<text>我已阅读并同意</text>
|
||||
<text class="consent-link" catchtap="openTerms">《用户服务协议》</text>
|
||||
<text>和</text>
|
||||
<text class="consent-link" catchtap="openPrivacy">《隐私政策》</text>
|
||||
<text>,并授权 TKMind 为账号登录、身份验证及提供 AI 对话服务,收集和使用我的用户名、邮箱、登录密码(加密传输)及微信登录标识。</text>
|
||||
</view>
|
||||
</label>
|
||||
</checkbox-group>
|
||||
</view>
|
||||
|
||||
<text wx:if="{{error}}" class="error">{{error}}</text>
|
||||
</view>
|
||||
|
||||
<view class="legal-footer">
|
||||
<text class="consent-link" bindtap="openTerms">用户服务协议</text>
|
||||
<text class="legal-sep">|</text>
|
||||
<text class="consent-link" bindtap="openPrivacy">隐私政策</text>
|
||||
<text class="legal-sep">|</text>
|
||||
<text class="consent-link" bindtap="openLegalCenter">隐私与协议</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view wx:if="{{showConsentModal}}" class="consent-modal-mask">
|
||||
<view class="consent-modal panel">
|
||||
<text class="consent-modal-title">个人信息收集说明</text>
|
||||
<text class="consent-modal-body">
|
||||
为完成账号登录并提供 AI 对话、会话管理等服务,TKMind 将收集以下信息:用户名/邮箱、登录密码(加密传输)、微信登录标识、会话与聊天记录。
|
||||
</text>
|
||||
<text class="consent-modal-body">
|
||||
收集目的:账号验证、身份识别、提供服务与安全保障。收集方式:由您主动填写或授权微信登录后获取。使用范围:仅用于本小程序及相关服务,不会出售给第三方。
|
||||
</text>
|
||||
<text class="consent-modal-body">
|
||||
请阅读
|
||||
<text class="consent-link" bindtap="openTerms">《用户服务协议》</text>
|
||||
和
|
||||
<text class="consent-link" bindtap="openPrivacy">《隐私政策》</text>
|
||||
后勾选同意。仅在您同意后,我们才会处理相关个人信息。
|
||||
</text>
|
||||
<view class="consent-modal-actions">
|
||||
<button class="consent-decline-button" bindtap="declineConsentModal">暂不同意</button>
|
||||
<button class="consent-accept-button" bindtap="acceptConsentModal">我已阅读并同意</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<privacy-popup />
|
||||
|
||||
@@ -70,3 +70,98 @@
|
||||
color: #1d4f91;
|
||||
border: 1rpx solid #c7ddff;
|
||||
}
|
||||
|
||||
.consent-box {
|
||||
padding: 8rpx 0;
|
||||
}
|
||||
|
||||
.consent-row {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 12rpx;
|
||||
}
|
||||
|
||||
.consent-text {
|
||||
flex: 1;
|
||||
font-size: 24rpx;
|
||||
color: #475569;
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
.consent-link {
|
||||
color: #0f766e;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.consent-modal-mask {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 1000;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 40rpx;
|
||||
background: rgba(15, 23, 42, 0.55);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.consent-modal {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 24rpx;
|
||||
width: 100%;
|
||||
max-width: 640rpx;
|
||||
padding: 36rpx;
|
||||
}
|
||||
|
||||
.consent-modal-title {
|
||||
font-size: 34rpx;
|
||||
font-weight: 800;
|
||||
color: #101820;
|
||||
}
|
||||
|
||||
.consent-modal-body {
|
||||
font-size: 26rpx;
|
||||
color: #334155;
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
.consent-modal-actions {
|
||||
display: flex;
|
||||
gap: 16rpx;
|
||||
margin-top: 8rpx;
|
||||
}
|
||||
|
||||
.consent-decline-button,
|
||||
.consent-accept-button {
|
||||
flex: 1;
|
||||
height: 80rpx;
|
||||
border-radius: 12rpx;
|
||||
font-size: 28rpx;
|
||||
font-weight: 700;
|
||||
line-height: 80rpx;
|
||||
}
|
||||
|
||||
.consent-decline-button {
|
||||
background: #f1f5f9;
|
||||
color: #475569;
|
||||
}
|
||||
|
||||
.consent-accept-button {
|
||||
background: #0f766e;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.legal-footer {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 12rpx;
|
||||
font-size: 24rpx;
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
.legal-sep {
|
||||
color: #cbd5e1;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
const SESSION_KEY = 'tkmind_session';
|
||||
const { createUuid, SESSION_COOKIE_NAME } = require('./config');
|
||||
const { clearLegalConsent } = require('./legal-consent');
|
||||
|
||||
let apiBaseUrl = '';
|
||||
let cookie = '';
|
||||
@@ -225,6 +226,7 @@ async function logout() {
|
||||
await portalRequest('/auth/logout', { method: 'POST' });
|
||||
} finally {
|
||||
clearSession();
|
||||
clearLegalConsent();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
const CONSENT_KEY = 'tkmind_legal_consent';
|
||||
const CONSENT_VERSION = '1.0';
|
||||
|
||||
function getStoredConsent() {
|
||||
try {
|
||||
const stored = wx.getStorageSync(CONSENT_KEY);
|
||||
if (stored && typeof stored === 'object') {
|
||||
return stored;
|
||||
}
|
||||
} catch {
|
||||
// Ignore storage read errors.
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function hasAcceptedLegalConsent() {
|
||||
const stored = getStoredConsent();
|
||||
return stored?.accepted === true && stored?.version === CONSENT_VERSION;
|
||||
}
|
||||
|
||||
function saveLegalConsent() {
|
||||
wx.setStorageSync(CONSENT_KEY, {
|
||||
accepted: true,
|
||||
version: CONSENT_VERSION,
|
||||
acceptedAt: new Date().toISOString(),
|
||||
});
|
||||
}
|
||||
|
||||
function clearLegalConsent() {
|
||||
wx.removeStorageSync(CONSENT_KEY);
|
||||
}
|
||||
|
||||
function shouldSkipLoginConsent() {
|
||||
return hasAcceptedLegalConsent();
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
CONSENT_VERSION,
|
||||
getStoredConsent,
|
||||
hasAcceptedLegalConsent,
|
||||
saveLegalConsent,
|
||||
clearLegalConsent,
|
||||
shouldSkipLoginConsent,
|
||||
};
|
||||
@@ -0,0 +1,51 @@
|
||||
function canUsePrivacyApis() {
|
||||
return typeof wx.getPrivacySetting === 'function';
|
||||
}
|
||||
|
||||
function getPrivacySetting() {
|
||||
return new Promise((resolve) => {
|
||||
if (!canUsePrivacyApis()) {
|
||||
resolve({ needAuthorization: false, privacyContractName: '' });
|
||||
return;
|
||||
}
|
||||
wx.getPrivacySetting({
|
||||
success(res) {
|
||||
resolve({
|
||||
needAuthorization: Boolean(res?.needAuthorization),
|
||||
privacyContractName: String(res?.privacyContractName || ''),
|
||||
});
|
||||
},
|
||||
fail() {
|
||||
resolve({ needAuthorization: false, privacyContractName: '' });
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function openPrivacyContract() {
|
||||
if (typeof wx.openPrivacyContract !== 'function') {
|
||||
wx.showToast({ title: '请升级微信版本后查看', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
wx.openPrivacyContract({
|
||||
fail() {
|
||||
wx.showToast({ title: '暂时无法打开隐私指引', icon: 'none' });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function registerPrivacyAuthorizationListener(handler) {
|
||||
if (typeof wx.onNeedPrivacyAuthorization !== 'function') {
|
||||
return;
|
||||
}
|
||||
wx.onNeedPrivacyAuthorization((resolve) => {
|
||||
handler(resolve);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
canUsePrivacyApis,
|
||||
getPrivacySetting,
|
||||
openPrivacyContract,
|
||||
registerPrivacyAuthorizationListener,
|
||||
};
|
||||
Reference in New Issue
Block a user