全面优化:小程序 + 后台管理

## 小程序优化
1. 我的页面:
   - 头像点击获取微信头像(button open-type="chooseAvatar")
   - 昵称点击直接修改
   - 去掉"创业伙伴"图标
   - ID优先显示微信号

2. 设置页面:
   - 一键获取微信手机号
   - 微信号/支付宝绑定优化

## 后台管理优化
1. 内容管理:
   - 章节编辑增加"免费章节"开关
   - 保存时自动去重标题(如#1.2重复)

2. 用户管理:
   - 修复绑定关系显示(优先查referral_bindings表)
This commit is contained in:
卡若
2026-01-29 11:58:07 +08:00
parent a228911170
commit d17150154c
8 changed files with 199 additions and 182 deletions

View File

@@ -41,6 +41,7 @@ interface EditingSection {
isNew?: boolean
partId?: string
chapterId?: string
isFree?: boolean // 是否免费章节
}
export default function ContentPage() {
@@ -129,14 +130,21 @@ export default function ContentPage() {
setIsSaving(true)
try {
// 自动去掉内容中的重复标题(如# 1.2 xxx
let content = editingSection.content || ''
// 匹配 # 数字.数字 开头的标题行并去掉
const titlePattern = new RegExp(`^#\\s*${editingSection.id}\\s+.*$`, 'm')
content = content.replace(titlePattern, '').trim()
const res = await fetch('/api/db/book', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
id: editingSection.id,
title: editingSection.title,
price: editingSection.price,
content: editingSection.content,
price: editingSection.isFree ? 0 : editingSection.price,
content: content,
isFree: editingSection.isFree || editingSection.price === 0,
saveToFile: true, // 同时保存到文件系统
})
})
@@ -760,7 +768,7 @@ export default function ContentPage() {
</DialogHeader>
{editingSection && (
<div className="space-y-4 py-4">
<div className="grid grid-cols-2 gap-4">
<div className="grid grid-cols-3 gap-4">
<div className="space-y-2">
<Label className="text-gray-300">ID</Label>
<Input
@@ -774,10 +782,29 @@ export default function ContentPage() {
<Input
type="number"
className="bg-[#0a1628] border-gray-700 text-white"
value={editingSection.price}
onChange={(e) => setEditingSection({ ...editingSection, price: Number(e.target.value) })}
value={editingSection.isFree ? 0 : editingSection.price}
onChange={(e) => setEditingSection({ ...editingSection, price: Number(e.target.value), isFree: Number(e.target.value) === 0 })}
disabled={editingSection.isFree}
/>
</div>
<div className="space-y-2">
<Label className="text-gray-300"></Label>
<div className="flex items-center h-10">
<label className="flex items-center cursor-pointer">
<input
type="checkbox"
checked={editingSection.isFree || editingSection.price === 0}
onChange={(e) => setEditingSection({
...editingSection,
isFree: e.target.checked,
price: e.target.checked ? 0 : 1
})}
className="w-5 h-5 rounded border-gray-600 bg-[#0a1628] text-[#38bdac] focus:ring-[#38bdac]"
/>
<span className="ml-2 text-gray-400 text-sm"></span>
</label>
</div>
</div>
</div>
<div className="space-y-2">
<Label className="text-gray-300"></Label>