This commit is contained in:
sjk
2026-01-06 19:37:06 +08:00
parent 19942144fb
commit 25d362c1d7
24 changed files with 0 additions and 1894 deletions

View File

@@ -1,4 +0,0 @@
{
"navigationBarTitleText": "文章详情",
"usingComponents": {}
}

View File

@@ -1,91 +0,0 @@
// pages/article-detail/article-detail.ts
import { formatDate, getStatusInfo, getChannelInfo, getCoverColor, getCoverIcon } from '../../utils/util'
import { EmployeeService } from '../../services/employee'
Page({
data: {
article: {} as any,
copyId: 0, // 文案ID
productId: 0, // 产品ID
showClaimButton: true // 是否显示领取按钮
},
onLoad(options: any) {
const copyId = parseInt(options.id);
const productId = parseInt(options.productId || '0');
this.setData({
copyId,
productId
});
// 如果有copyId显示文案详情
if (copyId) {
this.setData({
article: {
id: copyId,
title: '正在加载...',
content: ''
}
});
}
},
// 领取文案
async claimCopy() {
const { copyId, productId } = this.data;
if (!copyId || !productId) {
wx.showToast({
title: '参数错误',
icon: 'none'
});
return;
}
try {
const response = await EmployeeService.claimCopy(copyId, productId);
if (response.code === 200 && response.data) {
wx.showToast({
title: '领取成功',
icon: 'success'
});
// 隐藏领取按钮
this.setData({
showClaimButton: false
});
// 延迟后跳转到发布页面,传递领取信息
setTimeout(() => {
const copy = response.data!.copy;
wx.redirectTo({
url: `/pages/article-generate/article-generate?copyId=${copyId}&claimId=${response.data!.claim_id}&productId=${productId}&productName=${encodeURIComponent(copy.title)}&title=${encodeURIComponent(copy.title)}&content=${encodeURIComponent(copy.content)}`
});
}, 1500);
}
} catch (error) {
console.error('领取文案失败:', error);
}
},
// 分享功能
onShareAppMessage() {
const article = this.data.article;
return {
title: article.title || '精彩种草文案',
path: `/pages/article-detail/article-detail?id=${this.data.copyId}&productId=${this.data.productId}`,
imageUrl: '' // 可以设置文章封面图
};
},
// 分享到朋友圈
onShareTimeline() {
const article = this.data.article;
return {
title: article.title || '精彩种草文案',
imageUrl: '' // 可以设置文章封面图
};
}
});

View File

@@ -1,115 +0,0 @@
<!--pages/article-detail/article-detail.wxml-->
<scroll-view class="container" scroll-y>
<view class="article-cover" style="background-color: {{article.coverColor}}">
<text class="cover-icon">{{article.coverIcon}}</text>
</view>
<view class="detail-content">
<view class="detail-title">{{article.title}}</view>
<view class="article-meta-info">
<view class="meta-item">
<text class="meta-label">批次ID:</text>
<text class="meta-value">{{article.batch_id}}</text>
</view>
<view class="meta-item">
<text class="meta-label">话题:</text>
<text class="meta-value">{{article.topic}}</text>
</view>
<view class="meta-item">
<text class="meta-label">部门:</text>
<text class="meta-value">{{article.department_name || article.department}}</text>
</view>
<view class="meta-item">
<text class="meta-label">渠道:</text>
<text class="channel-tag channel-{{article.channel}}">{{article.channelText}}</text>
</view>
<view class="meta-item full-width" wx:if="{{article.coze_tag}}">
<text class="meta-label">标签:</text>
<text class="meta-value">{{article.coze_tag}}</text>
</view>
<view class="meta-item full-width" wx:if="{{article.review_comment}}">
<text class="meta-label">审核意见:</text>
<text class="meta-value">{{article.review_comment}}</text>
</view>
</view>
<view class="detail-author">
<view class="detail-author-avatar" style="background-color: {{article.coverColor}}">
{{article.author_name ? article.author_name[0] : 'A'}}
</view>
<view class="author-info">
<view class="author-name">{{article.author_name || '匿名'}}</view>
<view class="author-time">创建于 {{article.created_at}}</view>
</view>
<view class="article-status status-{{article.status}}">{{article.statusText}}</view>
</view>
<view class="detail-text">{{article.content}}</view>
<view class="detail-stats">
<view class="detail-stat">
<view class="detail-stat-value">{{article.word_count}}</view>
<view class="detail-stat-label">字数</view>
</view>
<view class="detail-stat">
<view class="detail-stat-value">{{article.image_count}}</view>
<view class="detail-stat-label">图片</view>
</view>
<view class="detail-stat">
<view class="detail-stat-value">{{article.timeText}}</view>
<view class="detail-stat-label">创建时间</view>
</view>
</view>
<view class="action-buttons" wx:if="{{showActions}}">
<button
class="action-btn approve-btn"
wx:if="{{article.status === 'pending_review'}}"
bindtap="handleApprove"
>
✓ 通过审核
</button>
<button
class="action-btn reject-btn"
wx:if="{{article.status === 'pending_review'}}"
bindtap="handleReject"
>
✕ 驳回
</button>
<button
class="action-btn publish-btn"
wx:if="{{article.status === 'approved'}}"
bindtap="handlePublish"
>
📤 发布文章
</button>
<button
class="action-btn publish-btn disabled"
wx:if="{{article.status === 'published'}}"
disabled
>
✓ 已发布
</button>
</view>
<view class="review-section" wx:if="{{showReviewSection}}">
<view class="review-title">审核意见</view>
<textarea
class="review-textarea"
placeholder="{{reviewAction === 'reject' ? '请输入驳回原因(必填)' : '请输入审核意见(可选)'}}"
value="{{reviewComment}}"
bindinput="onCommentInput"
maxlength="500"
></textarea>
<view class="action-buttons">
<button class="action-btn approve-btn" bindtap="confirmReview">
✓ 确认提交
</button>
<button class="action-btn cancel-btn" bindtap="cancelReview">
✕ 取消
</button>
</view>
</view>
</view>
</scroll-view>

View File

@@ -1,341 +0,0 @@
/* pages/article-detail/article-detail.wxss */
page {
height: 100vh;
width: 100vw;
background: linear-gradient(to bottom, #fff 0%, #f8f9fa 100%);
overflow: hidden;
}
.container {
height: 100vh;
width: 100vw;
overflow-y: auto;
overflow-x: hidden;
-webkit-overflow-scrolling: touch;
}
.article-cover {
width: 100%;
height: 450rpx;
display: flex;
align-items: center;
justify-content: center;
position: relative;
overflow: hidden;
}
.article-cover::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(180deg, rgba(0,0,0,0) 0%, rgba(0,0,0,0.15) 100%);
}
.cover-icon {
font-size: 150rpx;
position: relative;
z-index: 1;
filter: drop-shadow(0 8rpx 24rpx rgba(0, 0, 0, 0.15));
}
.detail-content {
width: 100%;
padding: 50rpx 40rpx;
background: white;
border-radius: 40rpx 40rpx 0 0;
margin-top: -40rpx;
position: relative;
z-index: 10;
box-shadow: 0 -8rpx 32rpx rgba(0, 0, 0, 0.06);
box-sizing: border-box;
}
.detail-title {
font-size: 44rpx;
font-weight: bold;
color: #1a1a1a;
margin-bottom: 35rpx;
line-height: 1.5;
letter-spacing: 1rpx;
word-break: break-all;
}
.article-meta-info {
display: flex;
flex-wrap: wrap;
gap: 24rpx;
margin-bottom: 35rpx;
padding: 35rpx;
background: linear-gradient(135deg, #f8f9fa 0%, #fff 100%);
border-radius: 20rpx;
border: 1rpx solid #f0f0f0;
box-sizing: border-box;
}
.meta-item {
flex: 0 0 auto;
font-size: 28rpx;
}
.meta-item.full-width {
flex: 1 1 100%;
}
.meta-label {
color: #999;
margin-right: 12rpx;
font-weight: 500;
}
.meta-value {
color: #333;
font-weight: 600;
}
.channel-tag {
padding: 6rpx 20rpx;
border-radius: 50rpx;
font-size: 24rpx;
font-weight: 600;
letter-spacing: 0.5rpx;
}
.channel-1 {
background-color: #e6f7ff;
color: #1890ff;
}
.channel-2 {
background-color: #fff7e6;
color: #fa8c16;
}
.channel-3 {
background-color: #f0f9ff;
color: #07c160;
}
.detail-author {
display: flex;
align-items: center;
margin-bottom: 40rpx;
padding: 30rpx;
background: linear-gradient(135deg, #f8f9fa 0%, #fff 100%);
border-radius: 20rpx;
border: 1rpx solid #f0f0f0;
}
.detail-author-avatar {
width: 88rpx;
height: 88rpx;
border-radius: 50%;
margin-right: 24rpx;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-weight: bold;
font-size: 36rpx;
box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.15);
}
.author-info {
flex: 1;
}
.author-name {
font-size: 34rpx;
font-weight: 600;
margin-bottom: 10rpx;
color: #1a1a1a;
}
.author-time {
font-size: 26rpx;
color: #999;
}
.article-status {
padding: 10rpx 28rpx;
border-radius: 50rpx;
font-size: 26rpx;
font-weight: 600;
letter-spacing: 0.5rpx;
}
.status-topic {
background-color: #f0f5ff;
color: #2f54eb;
}
.status-cover_image {
background-color: #e6f7ff;
color: #1890ff;
}
.status-generate {
background-color: #fff7e6;
color: #fa8c16;
}
.status-generate_failed {
background-color: #fff1f0;
color: #f5222d;
}
.status-draft {
background-color: #f5f5f5;
color: #8c8c8c;
}
.status-pending_review {
background-color: #fff7e6;
color: #fa8c16;
}
.status-approved {
background-color: #f6ffed;
color: #52c41a;
}
.status-rejected {
background-color: #fff1f0;
color: #f5222d;
}
.status-published_review {
background-color: #e6fffb;
color: #13c2c2;
}
.status-published {
background-color: #f6ffed;
color: #52c41a;
}
.status-failed {
background-color: #fff1f0;
color: #f5222d;
}
.detail-text {
font-size: 32rpx;
line-height: 2;
color: #444;
margin-bottom: 40rpx;
white-space: pre-wrap;
padding: 30rpx;
background: #fafafa;
border-radius: 20rpx;
border-left: 4rpx solid #07c160;
word-break: break-all;
box-sizing: border-box;
}
.detail-stats {
display: flex;
justify-content: space-around;
margin-bottom: 50rpx;
padding: 40rpx 0;
background: linear-gradient(135deg, #f8f9fa 0%, #fff 100%);
border-radius: 20rpx;
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.04);
}
.detail-stat {
text-align: center;
flex: 1;
}
.detail-stat-value {
font-size: 48rpx;
font-weight: bold;
color: #07c160;
display: block;
margin-bottom: 12rpx;
}
.detail-stat-label {
font-size: 26rpx;
color: #999;
font-weight: 500;
}
.action-buttons {
display: flex;
gap: 24rpx;
margin-top: 40rpx;
flex-wrap: wrap;
}
.action-btn {
flex: 1;
min-width: 200rpx;
padding: 32rpx;
border-radius: 20rpx;
font-size: 32rpx;
font-weight: bold;
border: none;
color: white;
box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.12);
transition: all 0.3s;
box-sizing: border-box;
}
.action-btn:active {
transform: scale(0.95);
}
.approve-btn {
background: linear-gradient(135deg, #52c41a 0%, #73d13d 100%);
}
.reject-btn {
background: linear-gradient(135deg, #ff4d4f 0%, #ff7875 100%);
}
.publish-btn {
background: #07c160;
}
.publish-btn.disabled {
background: #d9d9d9;
color: #999;
box-shadow: none;
}
.cancel-btn {
background: linear-gradient(135deg, #d9d9d9 0%, #e8e8e8 100%);
color: #666;
}
.review-section {
margin-top: 50rpx;
padding: 40rpx;
background: linear-gradient(135deg, #fff5f5 0%, #fff 100%);
border-radius: 20rpx;
border: 2rpx solid #ffebee;
}
.review-title {
font-size: 34rpx;
font-weight: bold;
margin-bottom: 30rpx;
color: #1a1a1a;
}
.review-textarea {
width: 100%;
min-height: 220rpx;
padding: 28rpx;
border: 2rpx solid #f0f0f0;
border-radius: 16rpx;
font-size: 30rpx;
box-sizing: border-box;
margin-bottom: 30rpx;
background: white;
line-height: 1.8;
}

View File

@@ -1,5 +0,0 @@
{
"navigationBarTitleText": "AI生成种草文章",
"navigationBarBackgroundColor": "#ff2442",
"navigationBarTextStyle": "white"
}

View File

@@ -1,231 +0,0 @@
// pages/article-generate/article-generate.ts
import { EmployeeService } from '../../services/employee';
interface Article {
title: string;
content: string;
tags: string[];
images: string[];
}
Page({
data: {
productId: 0,
productName: '',
copyId: 0, // 领取的文案ID
claimId: 0, // 领取记录ID
article: {
title: '',
content: '',
tags: [],
images: []
} as Article,
generating: false,
isFromClaim: false // 是否来自领取的文案
},
onLoad(options: any) {
const { productId, productName, copyId, claimId, title, content } = options;
this.setData({
productId: parseInt(productId || '0'),
productName: decodeURIComponent(productName || ''),
copyId: parseInt(copyId || '0'),
claimId: parseInt(claimId || '0'),
isFromClaim: !!copyId
});
// 如果有copyId说明是从领取文案过来的
if (this.data.copyId && this.data.claimId) {
// 如果有传递文案内容,直接显示
if (title && content) {
this.setData({
article: {
title: decodeURIComponent(title),
content: decodeURIComponent(content),
tags: ['种草分享', '好物推荐'],
images: []
}
});
} else {
// 否则生成模拟文案
this.generateArticle();
}
} else {
// 生成文章
this.generateArticle();
}
},
// 生成文章
generateArticle() {
this.setData({ generating: true });
wx.showLoading({
title: '生成中...',
mask: true
});
// 模拟AI生成文章
setTimeout(() => {
const mockArticles = [
{
title: `【种草分享】${this.data.productName}使用体验💕`,
content: `姐妹们!今天必须来跟大家分享一下我最近入手的宝藏好物——${this.data.productName}
✨使用感受:
用了一段时间真的太爱了!质感超级好,完全超出我的预期。包装也非常精致,送人自用都很合适。
🌟推荐理由:
1. 品质优秀,性价比超高
2. 使用体验一级棒
3. 颜值在线,拿出来超有面子
💰价格也很美丽,趁着活动入手真的很划算!强烈推荐给大家,绝对不会踩雷!`,
tags: ['种草分享', '好物推荐', '必买清单', '真实测评'],
images: [
'https://picsum.photos/id/237/600/400',
'https://picsum.photos/id/152/600/400'
]
},
{
title: `真香警告!${this.data.productName}实测分享`,
content: `集美们看过来!今天给大家带来${this.data.productName}的真实使用感受~
🎯第一印象:
收到货的那一刻就被惊艳到了!包装精美,细节满满,完全是高端货的质感。
💫使用体验:
用了几天下来,真的是越用越喜欢!质量很好,用起来特别顺手,完全就是我想要的样子!
⭐️总结:
这个价位能买到这么好的产品,真的是太值了!强烈安利给大家,闭眼入不会错!`,
tags: ['真实测评', '使用心得', '好物安利', '值得入手'],
images: [
'https://picsum.photos/id/292/600/400',
'https://picsum.photos/id/365/600/400',
'https://picsum.photos/id/180/600/400'
]
}
];
const randomArticle = mockArticles[Math.floor(Math.random() * mockArticles.length)];
this.setData({
article: randomArticle,
generating: false
});
wx.hideLoading();
}, 2000);
},
// 删除图片
deleteImage(e: any) {
const index = e.currentTarget.dataset.index;
const images = this.data.article.images;
images.splice(index, 1);
this.setData({
'article.images': images
});
},
// 返回上一页
goBack() {
wx.navigateBack();
},
// 重新生成文章
regenerateArticle() {
if (this.data.generating) return;
this.generateArticle();
},
// 发布文章
async publishArticle() {
wx.showModal({
title: '确认发布',
content: '确定要发布这篇种草文章吗?',
confirmText: '发布',
confirmColor: '#ff2442',
success: async (res) => {
if (res.confirm) {
// 如果是从领取的文案调用后端API
if (this.data.isFromClaim && this.data.copyId) {
try {
const response = await EmployeeService.publish({
copy_id: this.data.copyId,
title: this.data.article.title,
content: this.data.article.content,
publish_link: '', // 可以后续添加发布链接输入
xhs_note_id: '' // 小红书笔记ID
});
if (response.code === 200) {
wx.showToast({
title: '发布成功',
icon: 'success',
duration: 2000
});
// 保存到本地
const articles = wx.getStorageSync('myArticles') || [];
articles.unshift({
id: (response.data && response.data.record_id) || Date.now(),
productName: this.data.productName,
title: this.data.article.title,
content: this.data.article.content,
tags: this.data.article.tags,
createTime: new Date().toISOString(),
status: 'published'
});
wx.setStorageSync('myArticles', articles);
// 2秒后返回首页
setTimeout(() => {
wx.navigateBack();
}, 2000);
}
} catch (error) {
console.error('发布失败:', error);
}
} else {
// 模拟发布(非领取文案的情况)
wx.showLoading({
title: '发布中...',
mask: true
});
setTimeout(() => {
wx.hideLoading();
wx.showToast({
title: '发布成功',
icon: 'success',
duration: 2000
});
// 保存到本地(模拟)
const articles = wx.getStorageSync('myArticles') || [];
articles.unshift({
id: Date.now(),
productName: this.data.productName,
title: this.data.article.title,
content: this.data.article.content,
tags: this.data.article.tags,
createTime: new Date().toISOString(),
status: 'published'
});
wx.setStorageSync('myArticles', articles);
// 2秒后返回首页
setTimeout(() => {
wx.navigateBack();
}, 2000);
}, 1500);
}
}
}
});
}
});

View File

@@ -1,52 +0,0 @@
<!--pages/article-generate/article-generate.wxml-->
<view class="page-container">
<!-- 顶部标题 -->
<view class="page-header">
<view class="header-left" bindtap="goBack">
<view class="back-icon"></view>
</view>
<text class="page-title">生成具体内容</text>
<view class="header-right"></view>
</view>
<!-- 文章内容 -->
<scroll-view class="article-container" scroll-y enable-flex>
<view class="article-wrapper">
<!-- 图片列表 -->
<view class="article-images" wx:if="{{article.images.length > 0}}">
<view
class="image-item"
wx:for="{{article.images}}"
wx:key="index"
>
<image
class="article-image"
src="{{item}}"
mode="aspectFill"
/>
<view class="delete-icon" bindtap="deleteImage" data-index="{{index}}">
<text class="delete-text">×</text>
</view>
</view>
</view>
<!-- 标题和内容 -->
<view class="article-header">
<text class="article-title">{{article.title}}</text>
</view>
<view class="article-content">
<text class="content-text">{{article.content}}</text>
</view>
</view>
</scroll-view>
<!-- 底部操作栏 -->
<view class="action-bar">
<button class="action-btn secondary" bindtap="regenerateArticle">
<text class="btn-text">换一换</text>
</button>
<button class="action-btn primary" bindtap="publishArticle">
<text class="btn-text">一键发布</text>
</button>
</view>
</view>

View File

@@ -1,184 +0,0 @@
/* pages/article-generate/article-generate.wxss */
page {
background: white;
height: 100%;
}
.page-container {
height: 100vh;
display: flex;
flex-direction: column;
box-sizing: border-box;
padding-bottom: calc(136rpx + env(safe-area-inset-bottom));
}
/* 页面头部 */
.page-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 20rpx 30rpx;
background: white;
border-bottom: 1rpx solid #f0f0f0;
}
.header-left,
.header-right {
width: 80rpx;
}
.back-icon {
width: 40rpx;
height: 40rpx;
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="%231a1a1a"><path d="M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z"/></svg>');
background-size: contain;
background-repeat: no-repeat;
background-position: center;
}
.page-title {
font-size: 32rpx;
font-weight: 600;
color: #1a1a1a;
}
/* 文章容器 */
.article-container {
flex: 1;
overflow-y: auto;
overflow-x: hidden;
background: white;
}
.article-wrapper {
padding: 30rpx;
}
/* 图片列表 */
.article-images {
display: flex;
flex-wrap: nowrap;
gap: 16rpx;
margin-bottom: 30rpx;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}
.article-images::-webkit-scrollbar {
display: none;
}
.image-item {
flex-shrink: 0;
width: 200rpx;
height: 200rpx;
position: relative;
border-radius: 12rpx;
overflow: hidden;
}
.article-image {
width: 100%;
height: 100%;
background: #f5f5f5;
}
.delete-icon {
position: absolute;
top: 8rpx;
right: 8rpx;
width: 40rpx;
height: 40rpx;
background: rgba(0, 0, 0, 0.6);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
}
.delete-text {
font-size: 36rpx;
color: white;
line-height: 1;
font-weight: 300;
}
/* 标题区域 */
.article-header {
margin-bottom: 24rpx;
}
.article-title {
font-size: 36rpx;
color: #1a1a1a;
font-weight: bold;
line-height: 1.4;
}
/* 内容区域 */
.article-content {
margin-bottom: 24rpx;
}
.content-text {
font-size: 28rpx;
color: #333;
line-height: 1.8;
white-space: pre-line;
}
/* 操作栏 */
.action-bar {
position: fixed;
bottom: 0;
left: 0;
right: 0;
display: flex;
gap: 20rpx;
padding: 20rpx 30rpx;
background: white;
box-shadow: 0 -4rpx 20rpx rgba(0, 0, 0, 0.08);
border-top: 1rpx solid #f0f0f0;
padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
}
.action-btn {
flex: 1;
height: 96rpx;
border: none;
border-radius: 48rpx;
font-size: 32rpx;
font-weight: 600;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s;
}
.action-btn::after {
border: none;
}
.action-btn.secondary {
background: white;
color: #07c160;
border: 2rpx solid #07c160;
}
.action-btn.secondary:active {
background: #f0f9f4;
}
.action-btn.primary {
background: #07c160;
color: white;
box-shadow: 0 4rpx 16rpx rgba(7, 193, 96, 0.3);
}
.action-btn.primary:active {
transform: scale(0.98);
}
.btn-text {
font-size: 32rpx;
}

View File

@@ -1,4 +0,0 @@
{
"usingComponents": {
}
}

View File

@@ -1,54 +0,0 @@
// index.ts
// 获取应用实例
const app = getApp<IAppOption>()
const defaultAvatarUrl = 'https://mmbiz.qpic.cn/mmbiz/icTdbqWNOwNRna42FI242Lcia07jQodd2FJGIYQfG0LAJGFxM4FbnQP6yfMxBgJ0F3YRqJCJ1aPAK2dQagdusBZg/0'
Component({
data: {
motto: 'Hello World',
userInfo: {
avatarUrl: defaultAvatarUrl,
nickName: '',
},
hasUserInfo: false,
canIUseGetUserProfile: wx.canIUse('getUserProfile'),
canIUseNicknameComp: wx.canIUse('input.type.nickname'),
},
methods: {
// 事件处理函数
bindViewTap() {
wx.navigateTo({
url: '../logs/logs',
})
},
onChooseAvatar(e: any) {
const { avatarUrl } = e.detail
const { nickName } = this.data.userInfo
this.setData({
"userInfo.avatarUrl": avatarUrl,
hasUserInfo: nickName && avatarUrl && avatarUrl !== defaultAvatarUrl,
})
},
onInputChange(e: any) {
const nickName = e.detail.value
const { avatarUrl } = this.data.userInfo
this.setData({
"userInfo.nickName": nickName,
hasUserInfo: nickName && avatarUrl && avatarUrl !== defaultAvatarUrl,
})
},
getUserProfile() {
// 推荐使用wx.getUserProfile获取用户信息开发者每次通过该接口获取用户个人信息均需用户确认开发者妥善保管用户快速填写的头像昵称避免重复弹窗
wx.getUserProfile({
desc: '展示用户信息', // 声明获取用户个人信息后的用途,后续会展示在弹窗中,请谨慎填写
success: (res) => {
console.log(res)
this.setData({
userInfo: res.userInfo,
hasUserInfo: true
})
}
})
},
},
})

View File

@@ -1,27 +0,0 @@
<!--index.wxml-->
<scroll-view class="scrollarea" scroll-y type="list">
<view class="container">
<view class="userinfo">
<block wx:if="{{canIUseNicknameComp && !hasUserInfo}}">
<button class="avatar-wrapper" open-type="chooseAvatar" bind:chooseavatar="onChooseAvatar">
<image class="avatar" src="{{userInfo.avatarUrl}}"></image>
</button>
<view class="nickname-wrapper">
<text class="nickname-label">昵称</text>
<input type="nickname" class="nickname-input" placeholder="请输入昵称" bind:change="onInputChange" />
</view>
</block>
<block wx:elif="{{!hasUserInfo}}">
<button wx:if="{{canIUseGetUserProfile}}" bindtap="getUserProfile"> 获取头像昵称 </button>
<view wx:else> 请使用2.10.4及以上版本基础库 </view>
</block>
<block wx:else>
<image bindtap="bindViewTap" class="userinfo-avatar" src="{{userInfo.avatarUrl}}" mode="cover"></image>
<text class="userinfo-nickname">{{userInfo.nickName}}</text>
</block>
</view>
<view class="usermotto">
<text class="user-motto">{{motto}}</text>
</view>
</view>
</scroll-view>

View File

@@ -1,62 +0,0 @@
/**index.wxss**/
page {
height: 100vh;
display: flex;
flex-direction: column;
}
.scrollarea {
flex: 1;
overflow-y: hidden;
}
.userinfo {
display: flex;
flex-direction: column;
align-items: center;
color: #aaa;
width: 80%;
}
.userinfo-avatar {
overflow: hidden;
width: 128rpx;
height: 128rpx;
margin: 20rpx;
border-radius: 50%;
}
.usermotto {
margin-top: 200px;
}
.avatar-wrapper {
padding: 0;
width: 56px !important;
border-radius: 8px;
margin-top: 40px;
margin-bottom: 40px;
}
.avatar {
display: block;
width: 56px;
height: 56px;
}
.nickname-wrapper {
display: flex;
width: 100%;
padding: 16px;
box-sizing: border-box;
border-top: .5px solid rgba(0, 0, 0, 0.1);
border-bottom: .5px solid rgba(0, 0, 0, 0.1);
color: black;
}
.nickname-label {
width: 105px;
}
.nickname-input {
flex: 1;
}

View File

@@ -1,5 +0,0 @@
{
"navigationBarTitleText": "环境切换",
"navigationBarBackgroundColor": "#667eea",
"navigationBarTextStyle": "white"
}

View File

@@ -1,62 +0,0 @@
<!--pages/profile/env-switch/env-switch.wxml-->
<view class="container">
<!-- 当前环境 -->
<view class="current-env">
<view class="env-label">当前环境</view>
<view class="env-value">
<text class="env-tag env-tag-{{currentEnv}}">{{currentEnv === 'dev' ? '开发环境' : currentEnv === 'test' ? '测试环境' : '生产环境'}}</text>
</view>
</view>
<!-- 环境列表 -->
<view class="env-list">
<view
class="env-item {{currentEnv === item.key ? 'active' : ''}}"
wx:for="{{envList}}"
wx:key="key"
bindtap="switchEnvironment"
data-env="{{item.key}}"
>
<view class="env-header">
<view class="env-name">
<text class="env-icon" style="background-color: {{item.color}}"></text>
<text>{{item.name}}</text>
</view>
<view class="env-status">
<text wx:if="{{currentEnv === item.key}}" class="current-tag">当前</text>
<text wx:else class="switch-text">切换</text>
</view>
</view>
<view class="env-info">
<view class="info-item">
<text class="info-label">主服务:</text>
<text class="info-value" bindtap="copyURL" catchtap="copyURL" data-url="{{configs[item.key].baseURL}}">
{{configs[item.key].baseURL}}
</text>
</view>
<view class="info-item" wx:if="{{configs[item.key].pythonURL}}">
<text class="info-label">Python:</text>
<text class="info-value" bindtap="copyURL" catchtap="copyURL" data-url="{{configs[item.key].pythonURL}}">
{{configs[item.key].pythonURL}}
</text>
</view>
</view>
</view>
</view>
<!-- 说明 -->
<view class="tips">
<view class="tips-title">⚠️ 温馨提示</view>
<view class="tips-item">• 切换环境后会清除登录状态,需要重新登录</view>
<view class="tips-item">• 开发环境用于本地开发调试</view>
<view class="tips-item">• 测试环境用于服务器功能测试</view>
<view class="tips-item">• 生产环境为正式线上环境</view>
<view class="tips-item">• 点击地址可以复制</view>
</view>
<!-- 操作按钮 -->
<view class="actions">
<button class="action-btn" bindtap="restartApp">重启小程序</button>
</view>
</view>

View File

@@ -328,20 +328,6 @@ Page({
});
},
// 数据统计
goToStats() {
wx.navigateTo({
url: '/pages/profile/stats/stats'
});
},
// 我的文章
goToMyArticles() {
wx.navigateTo({
url: '/pages/profile/my-articles/my-articles'
});
},
// 已发布文章
goToPublished() {
wx.navigateTo({
@@ -349,34 +335,6 @@ Page({
});
},
// 收藏夹
goToFavorites() {
wx.navigateTo({
url: '/pages/profile/favorites/favorites'
});
},
// 消息通知
goToNotifications() {
wx.navigateTo({
url: '/pages/profile/notifications/notifications'
});
},
// 个人资料
goToUserInfo() {
wx.navigateTo({
url: '/pages/profile/user-info/user-info'
});
},
// 社交账号绑定
goToSocialBinding() {
wx.navigateTo({
url: '/pages/profile/social-binding/social-binding'
});
},
// 意见反馈
goToFeedback() {
wx.navigateTo({

View File

@@ -1,5 +0,0 @@
{
"navigationBarTitleText": "社交账号绑定",
"navigationBarBackgroundColor": "#ff2442",
"navigationBarTextStyle": "white"
}

View File

@@ -1,174 +0,0 @@
// pages/profile/social-binding/social-binding.ts
import { EmployeeService } from '../../../services/employee';
Page({
data: {
// 小红书
xiaohongshuBinded: false,
xiaohongshuPhone: '',
xiaohongshuAccount: '',
xiaohongshuCookieExpired: false, // Cookie是否失效
xiaohongshuStatusText: '去绑定',
xiaohongshuStatusClass: '',
// 微博
weiboBinded: false,
weiboPhone: '',
// 抖音
douyinBinded: false,
douyinPhone: ''
},
onLoad() {
this.loadBindingStatus();
},
onShow() {
// 每次显示时重新加载绑定状态
this.loadBindingStatus();
// 同时从后端加载
this.loadUserProfile();
},
// 从BACKEND加载用户信息包含小红书绑定状态
async loadUserProfile() {
try {
const response = await EmployeeService.getProfile();
if (response.code === 200 && response.data) {
const userInfo = response.data;
const isBound = userInfo.is_bound_xhs === 1;
// 使用has_xhs_cookie字段判断而不是xhs_cookie
const hasCookie = userInfo.has_xhs_cookie === true;
// 判断状态
let statusText = '去绑定';
let statusClass = '';
let cookieExpired = false;
if (isBound) {
if (hasCookie) {
// 已绑定且Cookie有效
statusText = '已绑定';
statusClass = 'binded';
} else {
// 已绑定但Cookie失效
statusText = '已失效';
statusClass = 'expired';
cookieExpired = true;
}
}
this.setData({
xiaohongshuBinded: isBound,
xiaohongshuAccount: userInfo.xhs_account || '',
xiaohongshuPhone: userInfo.xhs_phone || '',
xiaohongshuCookieExpired: cookieExpired,
xiaohongshuStatusText: statusText,
xiaohongshuStatusClass: statusClass
});
// 更新本地存储
if (isBound) {
const bindings = wx.getStorageSync('socialBindings') || {};
bindings.xiaohongshu = {
phone: userInfo.xhs_phone,
xhs_account: userInfo.xhs_account,
bindTime: userInfo.bound_at || new Date().getTime(),
cookieExpired: cookieExpired
};
wx.setStorageSync('socialBindings', bindings);
}
}
} catch (error) {
console.error('加载用户信息失败:', error);
}
},
// 加载绑定状态
loadBindingStatus() {
const bindings = wx.getStorageSync('socialBindings') || {};
this.setData({
xiaohongshuBinded: !!bindings.xiaohongshu,
xiaohongshuPhone: (bindings.xiaohongshu && bindings.xiaohongshu.phone) || '',
weiboBinded: !!bindings.weibo,
weiboPhone: (bindings.weibo && bindings.weibo.phone) || '',
douyinBinded: !!bindings.douyin,
douyinPhone: (bindings.douyin && bindings.douyin.phone) || ''
});
},
// 跳转到平台绑定页面
goToPlatformBind(e: any) {
const platform = e.currentTarget.dataset.platform;
// 如果是小红书
if (platform === 'xiaohongshu') {
// Cookie失效直接跳转重新绑定
if (this.data.xiaohongshuCookieExpired) {
wx.navigateTo({
url: '/pages/profile/xhs-login/xhs-login'
});
return;
}
// 已绑定且Cookie有效显示解绑确认
if (this.data.xiaohongshuBinded && !this.data.xiaohongshuCookieExpired) {
this.handleUnbindXHS();
return;
}
// 未绑定,跳转到绑定页
wx.navigateTo({
url: '/pages/profile/xhs-login/xhs-login'
});
return;
}
wx.navigateTo({
url: `/pages/profile/platform-bind/platform-bind?platform=${platform}`
});
},
// 解绑小红书
handleUnbindXHS() {
wx.showModal({
title: '确认解绑',
content: '确定要解绑小红书账号吗?',
confirmText: '解绑',
confirmColor: '#FF6B6B',
success: async (res) => {
if (res.confirm) {
try {
const response = await EmployeeService.unbindXHS();
if (response.code === 200) {
wx.showToast({
title: '解绑成功',
icon: 'success'
});
// 清除本地存储
const bindings = wx.getStorageSync('socialBindings') || {};
delete bindings.xiaohongshu;
wx.setStorageSync('socialBindings', bindings);
// 刷新页面
this.setData({
xiaohongshuBinded: false,
xiaohongshuPhone: '',
xiaohongshuAccount: ''
});
}
} catch (error) {
console.error('解绑失败:', error);
}
}
}
});
}
});

View File

@@ -1,51 +0,0 @@
<!--pages/profile/social-binding/social-binding.wxml-->
<view class="page-container">
<scroll-view class="content-scroll" scroll-y>
<!-- 小红书 -->
<view class="platform-item" bindtap="goToPlatformBind" data-platform="xiaohongshu">
<view class="platform-left">
<view class="platform-icon xiaohongshu"></view>
<view class="platform-info">
<text class="platform-name">小红书</text>
<text class="platform-desc">{{xiaohongshuBinded ? xiaohongshuPhone : '未绑定'}}</text>
</view>
</view>
<view class="platform-right">
<view class="bind-status {{xiaohongshuStatusClass}}">{{xiaohongshuStatusText}}</view>
<text class="item-arrow"></text>
</view>
</view>
<!-- 微博 -->
<view class="platform-item" bindtap="goToPlatformBind" data-platform="weibo">
<view class="platform-left">
<view class="platform-icon weibo"></view>
<view class="platform-info">
<text class="platform-name">微博</text>
<text class="platform-desc">{{weiboBinded ? weiboPhone : '未绑定'}}</text>
</view>
</view>
<view class="platform-right">
<view class="bind-status {{weiboBinded ? 'binded' : ''}}">{{weiboBinded ? '已绑定' : '去绑定'}}</view>
<text class="item-arrow"></text>
</view>
</view>
<!-- 抖音 -->
<view class="platform-item" bindtap="goToPlatformBind" data-platform="douyin">
<view class="platform-left">
<view class="platform-icon douyin"></view>
<view class="platform-info">
<text class="platform-name">抖音</text>
<text class="platform-desc">{{douyinBinded ? douyinPhone : '未绑定'}}</text>
</view>
</view>
<view class="platform-right">
<view class="bind-status {{douyinBinded ? 'binded' : ''}}">{{douyinBinded ? '已绑定' : '去绑定'}}</view>
<text class="item-arrow"></text>
</view>
</view>
</scroll-view>
</view>

View File

@@ -1,112 +0,0 @@
/* pages/profile/social-binding/social-binding.wxss */
page {
background: #f8f8f8;
height: 100%;
}
.page-container {
height: 100vh;
width: 100%;
display: flex;
flex-direction: column;
box-sizing: border-box;
}
.content-scroll {
flex: 1;
width: 100%;
padding: 20rpx;
box-sizing: border-box;
}
/* 平台列表项 */
.platform-item {
background: white;
border-radius: 16rpx;
padding: 32rpx;
margin-bottom: 20rpx;
display: flex;
align-items: center;
justify-content: space-between;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);
transition: all 0.3s ease;
}
.platform-item:active {
transform: scale(0.98);
background: #fafafa;
}
.platform-left {
display: flex;
align-items: center;
gap: 20rpx;
flex: 1;
}
.platform-icon {
width: 88rpx;
height: 88rpx;
border-radius: 16rpx;
flex-shrink: 0;
}
.platform-icon.xiaohongshu {
background: #07c160;
}
.platform-icon.weibo {
background: linear-gradient(135deg, #ff8200 0%, #ffb84d 100%);
}
.platform-icon.douyin {
background: linear-gradient(135deg, #000000 0%, #333333 100%);
}
.platform-info {
display: flex;
flex-direction: column;
gap: 8rpx;
flex: 1;
}
.platform-name {
font-size: 32rpx;
font-weight: 600;
color: #1a1a1a;
}
.platform-desc {
font-size: 24rpx;
color: #999;
}
.platform-right {
display: flex;
align-items: center;
gap: 12rpx;
}
.bind-status {
padding: 8rpx 20rpx;
border-radius: 20rpx;
font-size: 24rpx;
background: #f5f5f5;
color: #999;
}
.bind-status.binded {
background: #e8f5e9;
color: #4caf50;
}
.bind-status.expired {
background: #fff3e0;
color: #ff9800;
}
.item-arrow {
font-size: 40rpx;
color: #d0d0d0;
font-weight: 300;
}

View File

@@ -1,5 +0,0 @@
{
"navigationBarTitleText": "个人资料",
"navigationBarBackgroundColor": "#ff2442",
"navigationBarTextStyle": "white"
}

View File

@@ -1,26 +0,0 @@
// pages/profile/user-info/user-info.ts
Page({
data: {
username: '管理员'
},
onLoad() {
const username = wx.getStorageSync('username') || '管理员';
this.setData({ username });
},
handleSave() {
wx.showLoading({
title: '保存中...',
mask: true
});
setTimeout(() => {
wx.hideLoading();
wx.showToast({
title: '保存成功',
icon: 'success'
});
}, 1000);
}
});

View File

@@ -1,49 +0,0 @@
<!--pages/profile/user-info/user-info.wxml-->
<view class="page-container">
<view class="info-section">
<view class="info-item">
<view class="item-label">头像</view>
<view class="item-value">
<view class="avatar-preview">
<text class="avatar-icon"></text>
</view>
<text class="item-arrow"></text>
</view>
</view>
<view class="info-item">
<view class="item-label">昵称</view>
<view class="item-value">
<text class="value-text">{{username}}</text>
<text class="item-arrow"></text>
</view>
</view>
<view class="info-item">
<view class="item-label">手机号</view>
<view class="item-value">
<text class="value-text">138****8888</text>
<text class="item-arrow"></text>
</view>
</view>
<view class="info-item">
<view class="item-label">邮箱</view>
<view class="item-value">
<text class="value-text">user@example.com</text>
<text class="item-arrow"></text>
</view>
</view>
<view class="info-item">
<view class="item-label">角色</view>
<view class="item-value">
<text class="value-text role">内容管理员</text>
</view>
</view>
</view>
<view class="save-section">
<button class="save-btn" bindtap="handleSave">保存修改</button>
</view>
</view>

View File

@@ -1,99 +0,0 @@
/* pages/profile/user-info/user-info.wxss */
page {
background: #f8f8f8;
}
.page-container {
min-height: 100vh;
padding-bottom: 120rpx;
}
.info-section {
width: 100%;
background: white;
margin-top: 20rpx;
}
.info-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 32rpx 30rpx;
border-bottom: 1rpx solid #f0f0f0;
}
.info-item:last-child {
border-bottom: none;
}
.item-label {
font-size: 28rpx;
color: #333;
font-weight: 500;
}
.item-value {
display: flex;
align-items: center;
gap: 16rpx;
}
.value-text {
font-size: 28rpx;
color: #666;
}
.value-text.role {
color: #07c160;
font-weight: 600;
}
.item-arrow {
font-size: 36rpx;
color: #ccc;
}
.avatar-preview {
width: 100rpx;
height: 100rpx;
border-radius: 50%;
background: linear-gradient(135deg, #fff5f7 0%, #ffe8ec 100%);
display: flex;
align-items: center;
justify-content: center;
}
.avatar-icon {
width: 50rpx;
height: 50rpx;
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="%23ff2442"><path d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"/></svg>');
background-size: contain;
background-repeat: no-repeat;
background-position: center;
}
.save-section {
width: 100%;
padding: 40rpx 30rpx;
}
.save-btn {
width: 100%;
background: #07c160;
color: white;
border: none;
border-radius: 16rpx;
padding: 32rpx;
font-size: 32rpx;
font-weight: 600;
box-shadow: 0 8rpx 24rpx rgba(255, 36, 66, 0.3);
}
.save-btn::after {
border: none;
}
.save-btn:active {
opacity: 0.9;
transform: translateY(2rpx);
}

View File

@@ -1,94 +0,0 @@
<!--pages/profile/xhs-publish/xhs-publish.wxml-->
<view class="page-container">
<!-- 渐变红背景 + 动画效果 -->
<view class="background">
<view class="gradient-bg"></view>
<view class="circle circle1"></view>
<view class="circle circle2"></view>
<view class="circle circle3"></view>
</view>
<!-- 发布卡片 - 毛玻璃效果 -->
<view class="publish-card">
<view class="card-header">
<text class="header-title">✨ 发布小红书笔记</text>
<text class="header-subtitle">分享你的精彩时刻</text>
</view>
<!-- 标题输入 -->
<view class="input-group">
<view class="input-label">
<text class="label-icon">📝</text>
<text class="label-text">标题</text>
</view>
<input
class="input-field"
type="text"
placeholder="给你的笔记起个吸引人的标题"
value="{{title}}"
bindinput="onTitleInput"
maxlength="30"
/>
<view class="char-count">{{title.length}}/30</view>
</view>
<!-- 内容输入 -->
<view class="input-group">
<view class="input-label">
<text class="label-icon">✍️</text>
<text class="label-text">正文内容</text>
</view>
<textarea
class="textarea-field"
placeholder="分享你的故事、经验或想法..."
value="{{content}}"
bindinput="onContentInput"
maxlength="1000"
auto-height
></textarea>
<view class="char-count">{{content.length}}/1000</view>
</view>
<!-- 话题标签 -->
<view class="input-group">
<view class="input-label">
<text class="label-icon">🏷️</text>
<text class="label-text">话题标签</text>
<text class="label-tip">(可选)</text>
</view>
<input
class="input-field"
type="text"
placeholder="输入话题后按空格分隔,如:旅行 美食 生活"
value="{{topicsText}}"
bindinput="onTopicsInput"
/>
<view class="topics-preview" wx:if="{{topics.length > 0}}">
<block wx:for="{{topics}}" wx:key="index">
<view class="topic-tag">#{{item}}#</view>
</block>
</view>
</view>
<!-- 提示信息 -->
<view class="tips-box">
<text class="tips-icon">💡</text>
<text class="tips-text">发布前请确保已登录小红书账号</text>
</view>
<!-- 发布按钮 -->
<button
class="publish-btn {{publishing ? 'disabled' : ''}}"
bindtap="handlePublish"
disabled="{{publishing}}"
>
<text wx:if="{{!publishing}}">🚀 立即发布</text>
<text wx:else>发布中...</text>
</button>
</view>
<!-- Toast提示 -->
<view class="toast {{showToast ? 'show' : ''}}" wx:if="{{toastMessage}}">
<text>{{toastMessage}}</text>
</view>
</view>