diff --git a/miniprogram/pages/super-article-editor/super-article-editor.js b/miniprogram/pages/super-article-editor/super-article-editor.js
index 2dfb28c2..b043a03a 100644
--- a/miniprogram/pages/super-article-editor/super-article-editor.js
+++ b/miniprogram/pages/super-article-editor/super-article-editor.js
@@ -4,19 +4,15 @@
const app = getApp()
const { uploadByApi } = require('../../utils/miniprogramUpload')
-const TEXT_EXTS = ['.txt', '.md', '.json', '.csv', '.log', '.yaml', '.yml']
-const IMAGE_EXTS = ['.jpg', '.jpeg', '.png', '.gif', '.webp']
-const MAX_TEXT_READ_BYTES = 1024 * 1024
-const MAX_REFERENCE_SEND = 28000
-
function newAttachId() {
return `f_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`
}
Page({
data: {
- /** 「AI 帮你写」卡片:参考图 + 提示词 + 生成 */
+ /** 「AI 帮你写」入口(弹窗内填写提示词并生成) */
showAiWriteSection: true,
+ aiAssistVisible: false,
statusBarHeight: 44,
auditMode: false,
title: '',
@@ -25,9 +21,10 @@ Page({
saving: false,
meNickname: '',
aiPrompt: '',
- attachedFiles: [],
generating: false,
editArticleId: '',
+ /** 正文光标,用于 @自己 插入 */
+ contentCursor: 0,
},
/** 遮挡层拦截滚动穿透 */
@@ -35,10 +32,33 @@ Page({
syncAuditMode() {
const audit = !!app.globalData.auditMode
- this.setData({ auditMode: audit })
+ const patch = { auditMode: audit }
+ if (audit) patch.aiAssistVisible = false
+ this.setData(patch)
return audit
},
+ openAiAssist() {
+ this.syncAuditMode()
+ if (this.data.auditMode || app.globalData.auditMode) {
+ wx.showToast({ title: '审核模式下不可用', icon: 'none' })
+ return
+ }
+ if (!this.data.showAiWriteSection) return
+ this.setData({ aiAssistVisible: true })
+ },
+
+ closeAiAssist() {
+ if (this.data.generating) {
+ wx.showToast({ title: '生成中,请稍候…', icon: 'none' })
+ return
+ }
+ this.setData({ aiAssistVisible: false })
+ },
+
+ /** 阻止弹层内容区点击冒泡到遮罩(部分机型兼容) */
+ noopModalTap() {},
+
onLoad(options) {
const editId = options && options.id != null ? String(options.id).trim() : ''
if (editId) {
@@ -114,6 +134,7 @@ Page({
title: '',
content: String(article.content || ''),
bodyImages,
+ contentCursor: String(article.content || '').length,
})
} catch (e) {
wx.showToast({ title: (e && e.message) || '加载失败', icon: 'none' })
@@ -130,21 +151,45 @@ Page({
},
onContentInput(e) {
- this.setData({ content: e.detail.value || '' })
+ const v = e.detail.value != null ? String(e.detail.value) : ''
+ let cursor = e.detail.cursor
+ if (typeof cursor !== 'number' || Number.isNaN(cursor)) cursor = v.length
+ cursor = Math.max(0, Math.min(cursor, v.length))
+ this.setData({ contentCursor: cursor })
+ },
+
+ onContentBlur(e) {
+ const v = e.detail.value != null ? String(e.detail.value) : ''
+ let cursor = e.detail.cursor
+ if (typeof cursor !== 'number' || Number.isNaN(cursor)) cursor = v.length
+ cursor = Math.max(0, Math.min(cursor, v.length))
+ this.setData({ contentCursor: cursor })
+ },
+
+ insertAtContentCursor(insert) {
+ if (this.data.auditMode) return false
+ const raw = this.data.content != null ? String(this.data.content) : ''
+ const seg = String(insert || '')
+ if (!seg) return false
+ let pos = this.data.contentCursor
+ if (typeof pos !== 'number' || Number.isNaN(pos)) pos = raw.length
+ pos = Math.max(0, Math.min(pos, raw.length))
+ const next = raw.slice(0, pos) + seg + raw.slice(pos)
+ const newCursor = pos + seg.length
+ this.setData({
+ content: next,
+ contentCursor: newCursor,
+ })
+ return true
},
insertMentionSelf() {
if (this.data.auditMode) return
const nick = this.data.meNickname || '我'
- this.setData({ content: `${this.data.content}@${nick} ` })
+ this.insertAtContentCursor(`@${nick} `)
},
- insertHashLink() {
- if (this.data.auditMode) return
- this.setData({ content: `${this.data.content}#链接(https://)` })
- },
-
- /** 编辑区配图(发布后展示在正文前),最多 9 张 */
+ /** 动态配图(发动态展示 + AI 生成多模态共用),最多 9 张 */
chooseArticleImages() {
this.syncAuditMode()
if (this.data.auditMode || app.globalData.auditMode) {
@@ -255,160 +300,11 @@ Page({
})
},
- /** 参考素材:仅从相册/相机选图片并上传(AI 侧按图理解) */
- chooseReferenceImages() {
- this.syncAuditMode()
- if (this.data.auditMode || app.globalData.auditMode) {
- wx.showToast({ title: '审核模式下不可用', icon: 'none' })
- return
- }
- this._pickReferenceImagesFromAlbum()
- },
-
- _pickReferenceImagesFromAlbum() {
- const mapFromChooseMedia = (res) => {
- const files = res.tempFiles || []
- return files.map((f, i) => ({
- path: f.tempFilePath,
- name: `参考图_${Date.now()}_${i}.jpg`,
- size: f.size || 0,
- }))
- }
- const mapFromChooseImage = (res) => {
- const paths = res.tempFilePaths || []
- const t = Date.now()
- return paths.map((path, i) => ({
- path,
- name: `参考图_${t}_${i}.jpg`,
- size: 0,
- }))
- }
- if (typeof wx.chooseMedia === 'function') {
- wx.chooseMedia({
- count: 9,
- mediaType: ['image'],
- sourceType: ['album', 'camera'],
- sizeType: ['compressed'],
- success: (res) => this.handlePickedFiles(mapFromChooseMedia(res)),
- fail: (err) => {
- const em = String((err && err.errMsg) || '')
- if (/cancel|取消/i.test(em)) return
- wx.showToast({ title: '选择图片失败', icon: 'none' })
- },
- })
- return
- }
- if (typeof wx.chooseImage === 'function') {
- wx.chooseImage({
- count: 9,
- sizeType: ['compressed'],
- sourceType: ['album', 'camera'],
- success: (res) => this.handlePickedFiles(mapFromChooseImage(res)),
- fail: (err) => {
- const em = String((err && err.errMsg) || '')
- if (/cancel|取消/i.test(em)) return
- wx.showToast({ title: '选择图片失败', icon: 'none' })
- },
- })
- return
- }
- wx.showToast({ title: '当前环境不支持选图片', icon: 'none' })
- },
-
_pickUploadUrl(up) {
if (!up || typeof up !== 'object') return ''
return String(up.url || up.fullUrl || up.data?.url || up.data?.fullUrl || '').trim()
},
- _patchAttach(id, patch) {
- const next = this.data.attachedFiles.map((x) =>
- x.id === id ? Object.assign({}, x, patch) : x,
- )
- this.setData({ attachedFiles: next })
- },
-
- _removeAttachById(id) {
- this.setData({
- attachedFiles: this.data.attachedFiles.filter((x) => x.id !== id),
- })
- },
-
- async handlePickedFiles(tempFiles) {
- if (!tempFiles.length) return
- for (const f of tempFiles) {
- const name = String(f.name || '未命名').trim() || '未命名'
- const filePath = f.path
- const size = f.size || 0
- const dot = name.lastIndexOf('.')
- const ext = dot >= 0 ? name.slice(dot).toLowerCase() : ''
- const id = newAttachId()
-
- if (size > 29 * 1024 * 1024) {
- wx.showToast({ title: `${name} 超过30MB上限`, icon: 'none' })
- continue
- }
-
- if (IMAGE_EXTS.includes(ext)) {
- this.setData({
- attachedFiles: this.data.attachedFiles.concat([
- { id, name, kind: 'image', url: '', uploading: true },
- ]),
- })
- try {
- const up = await uploadByApi({ filePath, folder: 'article-images' })
- const url = this._pickUploadUrl(up)
- if (!url) throw new Error('no url')
- this._patchAttach(id, { url, uploading: false })
- } catch (_) {
- this._removeAttachById(id)
- wx.showToast({ title: `${name} 上传失败`, icon: 'none' })
- }
- continue
- }
-
- if (TEXT_EXTS.includes(ext) && size <= MAX_TEXT_READ_BYTES && size >= 0) {
- try {
- const fs = wx.getFileSystemManager()
- const textContent = fs.readFileSync(filePath, 'utf8')
- this.setData({
- attachedFiles: this.data.attachedFiles.concat([
- { id, name, kind: 'text', textContent, uploading: false },
- ]),
- })
- } catch (_) {
- await this._uploadBinaryAttach(id, name, filePath)
- }
- continue
- }
-
- await this._uploadBinaryAttach(id, name, filePath)
- }
- },
-
- async _uploadBinaryAttach(id, name, filePath) {
- this.setData({
- attachedFiles: this.data.attachedFiles.concat([
- { id, name, kind: 'binary', url: '', uploading: true },
- ]),
- })
- try {
- const up = await uploadByApi({ filePath, folder: 'book-attachments' })
- const url = this._pickUploadUrl(up)
- if (!url) throw new Error('no url')
- this._patchAttach(id, { url, uploading: false })
- } catch (_) {
- this._removeAttachById(id)
- wx.showToast({ title: `${name} 上传失败`, icon: 'none' })
- }
- },
-
- removeAttachedFile(e) {
- if (this.data.auditMode) return
- const id = e.currentTarget.dataset.id
- if (!id) return
- this._removeAttachById(id)
- },
-
async generateArticle() {
if (this.data.auditMode || app.globalData.auditMode) return
if (this.data.generating) return
@@ -418,34 +314,20 @@ Page({
return
}
- if (this.data.attachedFiles.some((x) => x.uploading)) {
- wx.showToast({ title: '请等待图片上传完成', icon: 'none' })
+ if (this.data.bodyImages.some((x) => x.uploading)) {
+ wx.showToast({ title: '请等待配图上传完成', icon: 'none' })
return
}
const prompt = String(this.data.aiPrompt || '').trim()
- const parts = []
- for (const f of this.data.attachedFiles) {
- if (f.kind === 'text' && f.textContent) {
- parts.push(`--- ${f.name} ---\n${f.textContent}`)
- }
- }
- let referenceText = parts.join('\n\n').trim()
- if (referenceText.length > MAX_REFERENCE_SEND) {
- referenceText =
- referenceText.slice(0, MAX_REFERENCE_SEND) + '\n\n(本地参考文末已截断)'
- }
+ const referenceText = ''
+ const materialUrls = []
+ const imageUrls = this.data.bodyImages
+ .map((x) => String(x.url || '').trim())
+ .filter(Boolean)
- const materialUrls = this.data.attachedFiles
- .filter((x) => x.kind === 'binary' && x.url)
- .map((x) => x.url)
-
- const imageUrls = this.data.attachedFiles
- .filter((x) => x.kind === 'image' && x.url)
- .map((x) => x.url)
-
- if (!prompt && !referenceText && materialUrls.length === 0 && imageUrls.length === 0) {
- wx.showToast({ title: '请填写提示词或添加参考图', icon: 'none' })
+ if (!prompt && imageUrls.length === 0) {
+ wx.showToast({ title: '请填写提示词或添加配图', icon: 'none' })
return
}
@@ -470,7 +352,13 @@ Page({
if (title && merged) merged = `${title}\n\n${merged}`
else if (title) merged = title
if (!merged.trim()) throw new Error('生成结果不完整')
- this.setData({ title: '', content: merged.trim() })
+ const mergedStr = merged.trim()
+ this.setData({
+ title: '',
+ content: mergedStr,
+ contentCursor: mergedStr.length,
+ aiAssistVisible: false,
+ })
wx.showToast({ title: '已填入正文', icon: 'success' })
} catch (e) {
wx.showToast({
@@ -522,13 +410,13 @@ Page({
})
}
if (!res?.success) throw new Error(res?.error || '发布失败')
- if (editId) {
- wx.showToast({ title: '已提交审核', icon: 'success' })
- setTimeout(() => wx.navigateBack(), 450)
- } else {
+ if (!editId) {
this.setData({ title: '', content: '', bodyImages: [] })
- wx.showToast({ title: '已提交审核', icon: 'success' })
}
+ wx.showToast({ title: '已提交审核', icon: 'success' })
+ setTimeout(() => {
+ wx.redirectTo({ url: '/pages/super-article-mine/super-article-mine' })
+ }, 450)
} catch (e) {
wx.showToast({ title: e.message || '发布失败', icon: 'none' })
} finally {
diff --git a/miniprogram/pages/super-article-editor/super-article-editor.wxml b/miniprogram/pages/super-article-editor/super-article-editor.wxml
index ad20622e..2256dada 100644
--- a/miniprogram/pages/super-article-editor/super-article-editor.wxml
+++ b/miniprogram/pages/super-article-editor/super-article-editor.wxml
@@ -8,72 +8,89 @@
-
-
- AI 帮你写
- 参考图片(可选)
-
- + 添加图片
-
-
-
- {{item.name}}
- 上传中…
- 文本已载入
- 图片
- 附件
+
+
+
+
+ 配图
+ {{bodyImages.length}}/9
- 移除
-
- 从相册或相机添加参考图,可选(单次最多 9 张),上传后与提示词一并交给 AI 分析。单张建议不超 30MB。
-
- 提示词
-
-
+ 可选 · 弹窗内「AI 帮你写」会与下方配图共用 · 发布后展示在正文下方
+
+
+
+ 上传中…
+ ×
+
+
+ +
+
+
+ 单张建议不超 30MB
- {{generating ? '生成中…' : '生成'}}
+
+
+
+
+ 正文
+ 最多 5000 字
+
+
+ 快捷插入
+ @自己
+ ✨ AI 帮你写
+
+
+
+
+
-
-
-
- @自己
- #链接
-
-
-
-
-
-
-
-
- 上传中…
- ×
-
-
- +
-
-
- 配图 {{bodyImages.length}}/9 · 展示在文字下方 · 单张建议不超 30MB · 可直接输入 @昵称 或 #链接(https://xxx)
-
-
-
{{saving ? (editArticleId ? '提交中…' : '发布中…') : (editArticleId ? '提交审核' : '发布')}}
+
+
+
+
+
+ AI 帮你写
+ 关闭
+
+ 提示词 + 当前配图一并交给模型;生成后写入正文,可关闭弹窗继续编辑
+ 提示词
+
+
+
+
+ {{generating ? '生成中…' : '生成并填入正文'}}
+
+
+
+
开发中...
diff --git a/miniprogram/pages/super-article-editor/super-article-editor.wxss b/miniprogram/pages/super-article-editor/super-article-editor.wxss
index 8c349ec4..6b79bec7 100644
--- a/miniprogram/pages/super-article-editor/super-article-editor.wxss
+++ b/miniprogram/pages/super-article-editor/super-article-editor.wxss
@@ -1,248 +1,790 @@
.page {
+
min-height: 100vh;
- background: #0b1220;
+
+ background: linear-gradient(180deg, #080f18 0%, #0b1220 32%, #0b1220 100%);
+
color: #fff;
+
box-sizing: border-box;
- padding-bottom: calc(168rpx + env(safe-area-inset-bottom));
+
+ padding-bottom: calc(180rpx + env(safe-area-inset-bottom));
+
}
+
+
.nav-bar {
- position: fixed; left: 0; right: 0; top: 0; z-index: 10;
- display: flex; align-items: center; justify-content: space-between;
- height: 44px; padding: 0 24rpx; background: rgba(5, 11, 20, 0.9);
+
+ position: fixed;
+
+ left: 0;
+
+ right: 0;
+
+ top: 0;
+
+ z-index: 10;
+
+ display: flex;
+
+ align-items: center;
+
+ justify-content: space-between;
+
+ height: 44px;
+
+ padding: 0 24rpx;
+
+ background: rgba(5, 11, 20, 0.92);
+
+ border-bottom: 1rpx solid rgba(94, 234, 212, 0.08);
+
}
-.nav-back { width: 64rpx; flex-shrink: 0; display: flex; align-items: center; }
-.nav-placeholder { width: 64rpx; flex-shrink: 0; }
-.nav-title { flex: 1; text-align: center; font-size: 32rpx; font-weight: 700; }
+
+.nav-back {
+
+ width: 64rpx;
+
+ flex-shrink: 0;
+
+ display: flex;
+
+ align-items: center;
+
+}
+
+.nav-placeholder {
+
+ width: 64rpx;
+
+ flex-shrink: 0;
+
+}
+
+.nav-title {
+
+ flex: 1;
+
+ text-align: center;
+
+ font-size: 32rpx;
+
+ font-weight: 700;
+
+}
+
+
+
+/* ========== 主编曲面 ========== */
+
+.composer-sheet {
+
+ margin: 16rpx 24rpx 24rpx;
+
+ border-radius: 28rpx;
+
+ overflow: hidden;
+
+ background: rgba(15, 23, 42, 0.92);
+
+ border: 1rpx solid rgba(148, 163, 184, 0.22);
+
+ box-shadow: 0 24rpx 48rpx rgba(0, 0, 0, 0.35);
+
+}
+
+
+
+.sheet-section {
+
+ padding: 28rpx 28rpx 26rpx;
+
+}
+
+
+
+.sheet-section--body {
+
+ padding-top: 24rpx;
+
+ padding-bottom: 28rpx;
+
+}
+
+
+
+.sheet-section--ai {
+
+ padding-top: 24rpx;
+
+ padding-bottom: 30rpx;
+
+ background: linear-gradient(
+
+ 180deg,
+
+ rgba(8, 47, 73, 0.22) 0%,
+
+ rgba(15, 23, 42, 0.05) 100%
+
+ );
+
+}
+
+
+
+.section-divider {
+
+ height: 1rpx;
+
+ margin: 0 28rpx;
+
+ background: rgba(148, 163, 184, 0.14);
+
+}
+
+
+
+.section-head {
+
+ display: flex;
+
+ align-items: center;
+
+ justify-content: space-between;
+
+ gap: 16rpx;
+
+ margin-bottom: 12rpx;
+
+}
+
+
+
+.section-title-text {
+
+ font-size: 30rpx;
+
+ font-weight: 700;
+
+ color: #f1f5f9;
+
+ letter-spacing: 1rpx;
+
+}
+
+
+
+.section-badge {
+
+ font-size: 22rpx;
+
+ font-weight: 600;
+
+ color: #22d3ee;
+
+ padding: 6rpx 16rpx;
+
+ border-radius: 999rpx;
+
+ background: rgba(34, 211, 238, 0.12);
+
+ border: 1rpx solid rgba(34, 211, 238, 0.28);
+
+}
+
+
+
+.section-meta {
+
+ font-size: 22rpx;
+
+ color: #94a3b8;
+
+}
+
+
+
+.section-tag-ai {
+
+ font-size: 22rpx;
+
+ color: #a5f3fc;
+
+ padding: 4rpx 14rpx;
+
+ border-radius: 999rpx;
+
+ border: 1rpx solid rgba(94, 234, 212, 0.35);
+
+ background: rgba(34, 211, 238, 0.08);
+
+}
+
+
+
+.section-desc {
+
+ display: block;
+
+ font-size: 22rpx;
+
+ color: #94a3b8;
+
+ line-height: 1.55;
+
+ margin-bottom: 16rpx;
+
+}
+
+
+
+.hint-micro {
+
+ display: block;
+
+ margin-top: 14rpx;
+
+ font-size: 20rpx;
+
+ color: #64748b;
+
+}
+
+
+
+.inline-toolbar {
+
+ display: flex;
+
+ align-items: center;
+
+ flex-wrap: wrap;
+
+ gap: 12rpx;
+
+ margin-bottom: 14rpx;
+
+}
+
+
+
+.toolbar-label {
+
+ font-size: 22rpx;
+
+ color: #64748b;
+
+ margin-right: 4rpx;
+
+}
+
+
+
+.tool-btn {
+
+ padding: 12rpx 22rpx;
+
+ border-radius: 999rpx;
+
+ font-size: 24rpx;
+
+ font-weight: 600;
+
+ color: #67e8f9;
+
+ border: 1rpx solid rgba(34, 211, 238, 0.4);
+
+ background: rgba(8, 47, 73, 0.5);
+
+}
+
+
+
+.tool-btn-hover {
+
+ opacity: 0.82;
+
+}
+
+
+
+.label-ai {
+
+ display: block;
+
+ font-size: 24rpx;
+
+ color: #cbd5e1;
+
+ margin-bottom: 10rpx;
+
+ margin-top: 4rpx;
+
+}
+
+
+
+.textarea-wrap,
+
+.textarea-wrap-ai {
+
+ background: rgba(2, 6, 23, 0.78);
+
+ border-radius: 18rpx;
+
+ padding: 18rpx 20rpx;
+
+ border: 1rpx solid rgba(148, 163, 184, 0.12);
+
+}
+
+
+
+.textarea-wrap-ai {
+
+ margin-bottom: 16rpx;
+
+}
+
+
+
+.moment-textarea-wrap {
+
+ padding: 20rpx 22rpx;
+
+}
+
+
+
+.textarea {
+
+ width: 100%;
+
+ min-height: 320rpx;
+
+ color: #fff;
+
+ font-size: 30rpx;
+
+ line-height: 1.65;
+
+ background: transparent;
+
+}
+
+
+
+.moment-textarea {
+
+ min-height: 280rpx;
+
+}
+
+
+
+.prompt-textarea-inner {
+
+ min-height: 160rpx;
+
+ width: 100%;
+
+ font-size: 28rpx;
+
+ line-height: 1.55;
+
+ color: #f1f5f9;
+
+ background: transparent;
+
+}
+
+
+
+.generate-btn {
+
+ margin-top: 8rpx;
+
+ height: 88rpx;
+
+ border-radius: 18rpx;
+
+ border: 1rpx solid rgba(34, 211, 238, 0.5);
+
+ color: #ecfeff;
+
+ font-size: 28rpx;
+
+ font-weight: 700;
+
+ display: flex;
+
+ align-items: center;
+
+ justify-content: center;
+
+ background: linear-gradient(135deg, rgba(8, 47, 73, 0.85), rgba(15, 118, 110, 0.35));
+
+}
+
+
+
+.generate-btn-loading {
+
+ opacity: 0.65;
+
+ pointer-events: none;
+
+}
+
+
.bottom-publish-bar {
+
position: fixed;
+
left: 0;
+
right: 0;
+
bottom: 0;
+
z-index: 50;
+
padding: 20rpx 28rpx;
+
padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
+
background: rgba(11, 18, 32, 0.96);
+
border-top: 1rpx solid rgba(94, 234, 212, 0.12);
+
}
+
.bottom-publish-btn {
+
height: 92rpx;
+
border-radius: 18rpx;
+
background: linear-gradient(135deg, #22d3ee, #14b8a6);
+
color: #032b35;
+
font-size: 32rpx;
+
font-weight: 700;
+
display: flex;
+
align-items: center;
+
justify-content: center;
+
}
+
.bottom-publish-btn-disabled {
+
opacity: 0.65;
+
pointer-events: none;
-}
-.card {
- margin: 24rpx; padding: 28rpx; border-radius: 24rpx;
- background: rgba(15, 23, 42, 0.86); border: 1rpx solid rgba(148, 163, 184, 0.2);
-}
-.section-title {
- display: block; font-size: 26rpx; font-weight: 700; color: #e2e8f0;
- margin-bottom: 20rpx; letter-spacing: 2rpx;
-}
-.label { display: block; font-size: 24rpx; color: #cbd5e1; margin-bottom: 12rpx; }
-.input-wrap, .textarea-wrap { background: rgba(2, 6, 23, 0.75); border-radius: 16rpx; padding: 18rpx 20rpx; }
-.input { width: 100%; color: #fff; font-size: 28rpx; background: transparent; }
-.file-actions { margin-bottom: 12rpx; }
-.add-file-btn {
- display: inline-flex; align-items: center; justify-content: center;
- padding: 14rpx 28rpx; border-radius: 16rpx; font-size: 26rpx; font-weight: 600;
- color: #22d3ee; border: 1rpx solid rgba(34, 211, 238, 0.45);
- background: rgba(8, 47, 73, 0.45);
-}
-.file-row {
- display: flex; align-items: flex-start; justify-content: space-between;
- gap: 16rpx; padding: 16rpx 18rpx; margin-bottom: 12rpx;
- border-radius: 14rpx; background: rgba(2, 6, 23, 0.55); border: 1rpx solid rgba(148, 163, 184, 0.12);
-}
-.file-main { flex: 1; min-width: 0; }
-.file-name { display: block; font-size: 26rpx; color: #f1f5f9; word-break: break-all; line-height: 1.45; }
-.file-meta { display: block; font-size: 22rpx; color: #94a3b8; margin-top: 6rpx; }
-.file-meta.ok { color: #5eead4; }
-.file-remove {
- flex-shrink: 0; font-size: 24rpx; color: #f97316; padding: 8rpx 12rpx;
-}
-.body-img-grid {
- display: flex;
- flex-wrap: wrap;
- gap: 16rpx;
- margin-bottom: 12rpx;
-}
-.body-img-cell {
- position: relative;
- width: 160rpx;
- height: 160rpx;
- border-radius: 14rpx;
- overflow: hidden;
- background: rgba(2, 6, 23, 0.65);
- border: 1rpx solid rgba(148, 163, 184, 0.18);
-}
-.body-img-thumb {
- width: 100%;
- height: 100%;
- display: block;
-}
-.body-img-mask {
- position: absolute;
- left: 0;
- right: 0;
- top: 0;
- bottom: 0;
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 22rpx;
- color: #94a3b8;
- background: rgba(15, 23, 42, 0.72);
-}
-.body-img-remove {
- position: absolute;
- right: 6rpx;
- top: 4rpx;
- width: 44rpx;
- height: 44rpx;
- line-height: 40rpx;
- text-align: center;
- font-size: 32rpx;
- font-weight: 700;
- color: #fff;
- background: rgba(0, 0, 0, 0.45);
- border-radius: 999rpx;
- z-index: 2;
-}
-.prompt-textarea { min-height: 200rpx; }
-.generate-btn {
- margin-top: 20rpx; height: 88rpx; border-radius: 16rpx;
- border: 1rpx solid rgba(34, 211, 238, 0.55); color: #a5f3fc; font-size: 30rpx; font-weight: 700;
- display: flex; align-items: center; justify-content: center;
- background: rgba(8, 47, 73, 0.45);
-}
-.generate-btn-loading { opacity: 0.7; pointer-events: none; }
-.toolbar { margin-top: 12rpx; margin-bottom: 12rpx; display: flex; gap: 12rpx; }
-.tool-btn {
- padding: 10rpx 18rpx; border-radius: 999rpx; font-size: 22rpx;
- color: #67e8f9; border: 1rpx solid rgba(34, 211, 238, 0.35); background: rgba(8, 47, 73, 0.45);
-}
-.textarea {
- width: 100%; min-height: 380rpx; color: #fff; font-size: 28rpx; line-height: 1.65;
- background: transparent;
-}
-.hint { display: block; margin-top: 10rpx; color: #94a3b8; font-size: 22rpx; }
-/* 发动态 · 配图宫格(参考朋友圈) */
-.moment-card .toolbar {
- margin-top: 0;
-}
-.moment-textarea-wrap {
- margin-top: 8rpx;
- padding: 20rpx 22rpx;
-}
-.moment-textarea {
- min-height: 240rpx;
- width: 100%;
- font-size: 30rpx;
- line-height: 1.65;
}
+
+
+
+/* 配图宫格 */
+
.moment-photo-grid {
+
display: flex;
+
flex-wrap: wrap;
+
gap: 16rpx;
- margin-top: 20rpx;
-}
-.moment-photo-cell {
- position: relative;
- width: calc((100% - 32rpx) / 3);
- padding-bottom: calc((100% - 32rpx) / 3);
- height: 0;
- border-radius: 12rpx;
- overflow: hidden;
- background: rgba(2, 6, 23, 0.65);
- border: 1rpx solid rgba(148, 163, 184, 0.15);
- box-sizing: border-box;
-}
-.moment-photo-img {
- position: absolute;
- left: 0;
- top: 0;
- width: 100%;
- height: 100%;
- display: block;
-}
-.moment-photo-mask {
- position: absolute;
- left: 0;
- right: 0;
- top: 0;
- bottom: 0;
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 22rpx;
- color: #94a3b8;
- background: rgba(15, 23, 42, 0.72);
- z-index: 1;
-}
-.moment-photo-remove {
- position: absolute;
- right: 6rpx;
- top: 4rpx;
- width: 44rpx;
- height: 44rpx;
- line-height: 40rpx;
- text-align: center;
- font-size: 32rpx;
- font-weight: 700;
- color: #fff;
- background: rgba(0, 0, 0, 0.45);
- border-radius: 999rpx;
- z-index: 2;
-}
-.moment-photo-add {
- border: 2rpx dashed rgba(148, 163, 184, 0.35);
- background: rgba(15, 23, 42, 0.35);
- display: flex;
- align-items: center;
- justify-content: center;
-}
-.moment-photo-add-plus {
- position: absolute;
- left: 50%;
- top: 50%;
- transform: translate(-50%, -52%);
- font-size: 64rpx;
- font-weight: 300;
- color: rgba(148, 163, 184, 0.85);
- line-height: 1;
-}
-.moment-hint {
- margin-top: 16rpx;
+
}
-/* 审核模式:盖住整页(含自定义导航),高于 .nav-bar 的 z-index: 10 */
-.audit-mode-mask {
- position: fixed;
+.moment-photo-cell {
+
+ position: relative;
+
+ width: calc((100% - 32rpx) / 3);
+
+ padding-bottom: calc((100% - 32rpx) / 3);
+
+ height: 0;
+
+ border-radius: 14rpx;
+
+ overflow: hidden;
+
+ background: rgba(2, 6, 23, 0.65);
+
+ border: 1rpx solid rgba(148, 163, 184, 0.15);
+
+ box-sizing: border-box;
+
+}
+
+.moment-photo-img {
+
+ position: absolute;
+
left: 0;
- right: 0;
+
top: 0;
+
+ width: 100%;
+
+ height: 100%;
+
+ display: block;
+
+}
+
+.moment-photo-mask {
+
+ position: absolute;
+
+ left: 0;
+
+ right: 0;
+
+ top: 0;
+
bottom: 0;
- z-index: 10000;
+
display: flex;
+
align-items: center;
+
justify-content: center;
+
+ font-size: 22rpx;
+
+ color: #94a3b8;
+
+ background: rgba(15, 23, 42, 0.72);
+
+ z-index: 1;
+
+}
+
+.moment-photo-remove {
+
+ position: absolute;
+
+ right: 6rpx;
+
+ top: 4rpx;
+
+ width: 44rpx;
+
+ height: 44rpx;
+
+ line-height: 40rpx;
+
+ text-align: center;
+
+ font-size: 32rpx;
+
+ font-weight: 700;
+
+ color: #fff;
+
+ background: rgba(0, 0, 0, 0.45);
+
+ border-radius: 999rpx;
+
+ z-index: 2;
+
+}
+
+.moment-photo-add {
+
+ border: 2rpx dashed rgba(148, 163, 184, 0.35);
+
+ background: rgba(15, 23, 42, 0.35);
+
+ display: flex;
+
+ align-items: center;
+
+ justify-content: center;
+
+}
+
+.moment-photo-add-plus {
+
+ position: absolute;
+
+ left: 50%;
+
+ top: 50%;
+
+ transform: translate(-50%, -52%);
+
+ font-size: 64rpx;
+
+ font-weight: 300;
+
+ color: rgba(148, 163, 184, 0.85);
+
+ line-height: 1;
+
+}
+
+
+
+/* AI 底部弹层 */
+
+.ai-modal-root {
+
+ position: fixed;
+
+ left: 0;
+
+ right: 0;
+
+ top: 0;
+
+ bottom: 0;
+
+ z-index: 600;
+
+ display: flex;
+
+ flex-direction: column;
+
+ justify-content: flex-end;
+
+}
+
+.ai-modal-mask {
+
+ position: absolute;
+
+ left: 0;
+
+ right: 0;
+
+ top: 0;
+
+ bottom: 0;
+
+ background: rgba(0, 0, 0, 0.58);
+
+}
+
+.ai-modal-panel {
+
+ position: relative;
+
+ z-index: 1;
+
+ border-radius: 28rpx 28rpx 0 0;
+
+ padding: 28rpx 28rpx 32rpx;
+
+ padding-bottom: calc(32rpx + env(safe-area-inset-bottom));
+
+ max-height: 78vh;
+
+ overflow-y: auto;
+
+ background: rgba(15, 23, 42, 0.98);
+
+ border: 1rpx solid rgba(148, 163, 184, 0.22);
+
+ border-bottom: none;
+
+ box-shadow: 0 -12rpx 48rpx rgba(0, 0, 0, 0.45);
+
+}
+
+.ai-modal-head {
+
+ display: flex;
+
+ align-items: center;
+
+ justify-content: space-between;
+
+ margin-bottom: 12rpx;
+
+}
+
+.ai-modal-title {
+
+ font-size: 32rpx;
+
+ font-weight: 700;
+
+ color: #f1f5f9;
+
+}
+
+.ai-modal-close {
+
+ font-size: 26rpx;
+
+ color: #67e8f9;
+
+ padding: 12rpx 16rpx;
+
+}
+
+.ai-modal-desc {
+
+ display: block;
+
+ font-size: 22rpx;
+
+ color: #94a3b8;
+
+ line-height: 1.55;
+
+ margin-bottom: 16rpx;
+
+}
+
+
+
+.audit-mode-mask {
+
+ position: fixed;
+
+ left: 0;
+
+ right: 0;
+
+ top: 0;
+
+ bottom: 0;
+
+ z-index: 10000;
+
+ display: flex;
+
+ align-items: center;
+
+ justify-content: center;
+
background: rgba(11, 18, 32, 0.94);
+
}
+
.audit-mode-text {
+
font-size: 36rpx;
+
font-weight: 600;
+
color: rgba(255, 255, 255, 0.88);
+
letter-spacing: 4rpx;
+
}
+
diff --git a/miniprogram/pages/super-article-mine/super-article-mine.js b/miniprogram/pages/super-article-mine/super-article-mine.js
index a28e9e77..8daf5de6 100644
--- a/miniprogram/pages/super-article-mine/super-article-mine.js
+++ b/miniprogram/pages/super-article-mine/super-article-mine.js
@@ -78,11 +78,15 @@ function mapRows(rows) {
return (rows || []).map((r) => {
const st = String(r.auditStatus || 'approved').toLowerCase()
const thumb = buildThumbSlots(r.images)
+ const contentTrim = String(r.content || '').trim()
+ const titleTrim = String(r.title || '').trim()
return Object.assign({}, r, {
auditStatus: st === 'pending' || st === 'rejected' ? st : 'approved',
auditLabel: auditLabel(st),
timeText: relativeTime(r.createdAt),
preview: String(r.preview || '').trim(),
+ contentTrim,
+ titleTrim,
rejectReason: String(r.rejectReason || '').trim(),
showThumbRow: thumb.showThumbRow,
thumbSlots: thumb.thumbSlots,
diff --git a/miniprogram/pages/super-article-mine/super-article-mine.wxml b/miniprogram/pages/super-article-mine/super-article-mine.wxml
index b80844e4..e8b13fa5 100644
--- a/miniprogram/pages/super-article-mine/super-article-mine.wxml
+++ b/miniprogram/pages/super-article-mine/super-article-mine.wxml
@@ -31,9 +31,10 @@
{{item.auditLabel}}
{{item.timeText}}
- {{item.title}}
- {{item.preview}}
- 动态
+ {{item.contentTrim}}
+ {{item.titleTrim}}
+ {{item.preview}}
+ 动态
diff --git a/miniprogram/pages/super-article-mine/super-article-mine.wxss b/miniprogram/pages/super-article-mine/super-article-mine.wxss
index b8789aed..d8cd9791 100644
--- a/miniprogram/pages/super-article-mine/super-article-mine.wxss
+++ b/miniprogram/pages/super-article-mine/super-article-mine.wxss
@@ -146,14 +146,19 @@
}
.feed-title {
- display: block;
+ display: -webkit-box;
+ -webkit-box-orient: vertical;
+ -webkit-line-clamp: 3;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ word-break: break-word;
+ width: 100%;
+ box-sizing: border-box;
font-size: 16px;
font-weight: 600;
color: rgba(255, 255, 255, 0.92);
margin-bottom: 6px;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
+ line-height: 1.45;
}
.feed-title-fallback {
diff --git a/miniprogram/pages/super-moments/super-moments.js b/miniprogram/pages/super-moments/super-moments.js
index 18480cc8..9152b96b 100644
--- a/miniprogram/pages/super-moments/super-moments.js
+++ b/miniprogram/pages/super-moments/super-moments.js
@@ -96,6 +96,8 @@ Page({
hasMore: true,
loading: true,
loadingMore: false,
+ /** 超级个体:右下角发动态入口 */
+ showSuperPublishFab: false,
},
async onLoad() {
@@ -108,6 +110,7 @@ Page({
return
}
this.reloadFeed()
+ this.refreshSuperPublishFab()
},
async onShow() {
@@ -123,6 +126,29 @@ Page({
if (tabBar && tabBar.loadFeatureConfig) tabBar.loadFeatureConfig()
if (tabBar && tabBar.updateSelected) tabBar.updateSelected()
}
+ this.refreshSuperPublishFab()
+ },
+
+ /** 仅超级个体展示发动态悬浮按钮 */
+ async refreshSuperPublishFab() {
+ const userId = app.globalData.userInfo?.id
+ let show = false
+ if (userId && app.globalData.isLoggedIn) {
+ try {
+ const res = await app.request({
+ url: `/api/miniprogram/my/super-stats?userId=${encodeURIComponent(userId)}`,
+ silent: true,
+ })
+ show = !!(res?.success && res.data?.isSuperIndividual === true)
+ } catch (_) {
+ show = false
+ }
+ }
+ this.setData({ showSuperPublishFab: show })
+ },
+
+ goPublishMoment() {
+ wx.navigateTo({ url: '/pages/super-article-editor/super-article-editor' })
},
onReachBottom() {
@@ -139,6 +165,7 @@ Page({
}
try {
await this.reloadFeed()
+ await this.refreshSuperPublishFab()
} finally {
wx.stopPullDownRefresh()
}
diff --git a/miniprogram/pages/super-moments/super-moments.wxml b/miniprogram/pages/super-moments/super-moments.wxml
index f6074f65..ce3fdfa1 100644
--- a/miniprogram/pages/super-moments/super-moments.wxml
+++ b/miniprogram/pages/super-moments/super-moments.wxml
@@ -58,4 +58,15 @@
已经到底啦
+
+
+
+ +
+
diff --git a/miniprogram/pages/super-moments/super-moments.wxss b/miniprogram/pages/super-moments/super-moments.wxss
index 30ee52de..ed1b0d0f 100644
--- a/miniprogram/pages/super-moments/super-moments.wxss
+++ b/miniprogram/pages/super-moments/super-moments.wxss
@@ -166,3 +166,38 @@
font-size: 24rpx;
color: #64748b;
}
+
+/* 超级个体发动态:右下角圆钮(与读页分享 FAB、TabBar 品牌青一致) */
+.moments-fab-publish {
+ position: fixed;
+ right: 32rpx;
+ bottom: calc(156rpx + env(safe-area-inset-bottom));
+ width: 96rpx;
+ height: 96rpx;
+ border-radius: 50%;
+ background: linear-gradient(135deg, #00ced1 0%, #20b2aa 100%);
+ box-shadow: 0 8rpx 32rpx rgba(0, 206, 209, 0.42);
+ z-index: 1002;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ transition: transform 0.15s ease, opacity 0.15s ease;
+}
+
+.moments-fab-publish-hover {
+ opacity: 0.92;
+}
+
+.moments-fab-publish:active {
+ transform: scale(0.94);
+}
+
+/* 原生「+」,避免 icon 组件 SVG 在 FAB 内偶发不渲染 */
+.moments-fab-plus {
+ font-size: 56rpx;
+ font-weight: 300;
+ color: #ffffff;
+ line-height: 1;
+ margin-top: -4rpx;
+ text-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.35);
+}