选人改「只留搜索框」+ 附件用系统打开 + 留言头像与主题色 (#77)
用户 2026-11-05 五项。
① 选人慢 → 未输入关键词时不渲染任何人
用户:「实在不行,我们就不把人列出来了,只留搜索框…输入拼音首字母就可以找到这个人」
慢的根因(不只是节点多):
· 名册 **800 人**(不是 8 个);
· 每人一个 FbAvatar,而它内部调 fbImageSrc(avatar, token) ——
那是**同步**查本地缓存 + 拼 URL,800 次全在打开弹窗那一帧跑完;
· 再加 800 个 Column/Text 节点参与布局。
⇒ 未输入时一个候选都不渲染,只显示"输入名字或拼音首字母找人"。
只有真打了字才过滤、才渲染(结果通常 1~20 个,瞬间出来)。
⚠️ 没改成"只显示前 N 个":800 人里翻前 20 个没用,用户还是得搜。
⚠️ 暂存区(已选那几个)不受影响:数量极少且必须一直可见
(否则用户忘记选了谁)。多选 + 多次搜索的流程因此保持完整:
搜一个 → 点选进暂存 → 改关键词再搜 → 再点选 → 确定。
② 附件:Word 等非图片文件交给系统应用打开
用户:「应该打开本机上默认能够打开这个文件的应用,让它把文件打开才对。」
项目里**已经有** common/FbAttachOpen.ets(下载明文到 cacheDir +
隐式 Want + action.viewData 交给系统「打开方式」),只是任务面板没调用。
⇒ 附件缩略图挂上 onClick:
· 图片 → 就地全屏预览(新加浮层,ImageFit.Contain 看全图不裁边)
· 其它 → fbAttachOpen(),系统自己找能打开 .docx 的应用
⚠️ 不能用「显式 Want + 包名」:不知道用户装的是 WPS 还是 Office。
⚠️ 预览用 Contain 而非 Cover —— 缩略图用 Cover 对,预览裁边就错了。
③ 留言人头像
服务端 Task::comments() 的 SQL 里**一直有** `p.avatar`,
端上 parseTaskComment 只取了 name/color ⇒ 头像从没显示过。
补 RawTaskComment.avatar + FbTaskComment.avatar + 解析,
UI 换成公共 FbAvatar(有图用图、没图退回首字色块)。
④ 留言标题/发言人用模块主题色加粗
原来发言人是 muted 灰、常规字重 —— 漏了全站「以模块色标识」的规矩
(观影紫 / 旅程绿 / 任务赭红)。改成 this.accent + FontWeight.Medium。
同时把「附件」「留言」「子任务」三个小节标题也从 ink 改成主题色。
⚠️ 正文**没有**跟着改主题色:整段上色会掉可读性(色彩需要留白)。
如果用户要求正文也变色,改一行即可(已在代码注释里标了位置)。
⑤ 编译通过,已装真机。
This commit is contained in:
@@ -35,9 +35,11 @@ import {
|
||||
} from '../model/Types';
|
||||
import { FbSheetRing } from './FbSheetRing';
|
||||
import { FbSheetClose } from './FbSheetClose';
|
||||
import { FbCheckCircle } from './Widgets';
|
||||
import { FbCheckCircle, FbAvatar } from './Widgets';
|
||||
import { FbPersonPickSheet } from './FbPersonPickSheet';
|
||||
import { FbDateTimePicker, fbIsoOf, fbJoinDateTime } from './FbDateTimeField';
|
||||
import { fbAttachOpen } from '../common/FbAttachOpen';
|
||||
import { common } from '@kit.AbilityKit';
|
||||
import { FbPersonRepo, FbRosterPerson } from '../data/FbPersonRepo';
|
||||
import { FB_TOKEN_STORE } from '../data/FbTokenStore';
|
||||
import { fbApi, FbApiResult, FbApiOptions } from '../data/FbHttp';
|
||||
@@ -123,6 +125,10 @@ export struct FbTaskPanel {
|
||||
@Local noteEdit: boolean = false;
|
||||
@Local noteDraft: string = '';
|
||||
@Local inlineBusy: boolean = false;
|
||||
|
||||
/* #77c:附件预览浮层 —— 存当前要全屏看的那张图的地址,空 = 没开 */
|
||||
@Local previewSrc: string = '';
|
||||
@Local previewName: string = '';
|
||||
// 共享(工单 #26)
|
||||
@Local roster: FbRosterPerson[] = [];
|
||||
@Local pickingShare: boolean = false;
|
||||
@@ -374,6 +380,43 @@ export struct FbTaskPanel {
|
||||
await this.saveField('note', v, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 点附件(#77c)。
|
||||
*
|
||||
* 🔴 用户 2026-11-05:「附件那个地方如果是Word文档,好像还是不能预览。
|
||||
* 按道理说,应该打开本机上默认能够打开这个文件的应用,让它把文件打开才对。」
|
||||
*
|
||||
* 分两种情况:
|
||||
* · **图片** → 就地全屏预览(不跳出 App,看附件不用切来切去)
|
||||
* · **其它**(Word / PDF / Excel…)→ `fbAttachOpen()`:
|
||||
* 先下载一份明文到 cacheDir,再用**隐式 Want + action.viewData**
|
||||
* 交给系统「打开方式」—— 系统自己找能打开 .docx 的应用。
|
||||
*
|
||||
* ⚠️ 不能用「显式 Want + 包名」:我们不知道用户装的是 WPS 还是 Office,
|
||||
* 也不知道包名。让系统按 MIME/扩展名匹配才是唯一可靠的做法。
|
||||
* ⚠️ `fbAttachOpen()` 已经处理好"下不下来 / 没有可用应用"的提示文案,
|
||||
* 这里只把它的返回串显示出来,不自己再编一套。
|
||||
*/
|
||||
private async tapAttach(a: FbTaskAttach): Promise<void> {
|
||||
if (a.isImage) {
|
||||
/* 图片:全屏预览。用 `fbImageSrc`(本地缓存优先,没有就远端 URL) */
|
||||
this.previewSrc = fbImageSrc(a.key, this.token());
|
||||
this.previewName = a.name;
|
||||
return;
|
||||
}
|
||||
/* 非图片:交给系统打开 */
|
||||
const ctx: common.Context | undefined =
|
||||
this.getUIContext().getHostContext() as common.Context | undefined;
|
||||
if (ctx === undefined) {
|
||||
this.note = '打不开:拿不到上下文,请重启应用再试。';
|
||||
return;
|
||||
}
|
||||
this.note = '正在下载…';
|
||||
const err: string = await fbAttachOpen(this.token(), a.key, false, ctx);
|
||||
/* `fbAttachOpen` 成功时返回 ''(已交给系统),失败时返回人话 */
|
||||
this.note = err !== '' ? err : '';
|
||||
}
|
||||
|
||||
private categoryOf(): string {
|
||||
const d: FbTaskDetail | null = this.detail;
|
||||
return d !== null ? d.category : '';
|
||||
@@ -755,6 +798,56 @@ export struct FbTaskPanel {
|
||||
})
|
||||
}
|
||||
|
||||
/* 🔴 #77f(用户 2026-11-05):「附件那个地方…按道理说,应该打开本机上
|
||||
默认能够打开这个文件的应用」——图片走就地全屏预览(不跳出 App),
|
||||
非图片走 `fbAttachOpen()` 交给系统(见 `tapAttach`)。
|
||||
|
||||
⚠️ 预览层放在**所有浮层最后**:它要盖住面板本身。
|
||||
⚠️ 整块用 `Stack` 铺满 + 半透明黑底,点击任意处关闭。
|
||||
⚠️ `objectFit(ImageFit.Contain)` —— 预览要看全图,
|
||||
用 Cover 会裁掉边缘(缩略图用 Cover 是对的,预览不是)。 */
|
||||
if (this.previewSrc !== '') {
|
||||
Stack({ alignContent: Alignment.Center }) {
|
||||
Column()
|
||||
.width('100%')
|
||||
.height('100%')
|
||||
.backgroundColor('#E6000000')
|
||||
.onClick((): void => {
|
||||
this.previewSrc = '';
|
||||
this.previewName = '';
|
||||
})
|
||||
|
||||
Image(this.previewSrc)
|
||||
.width('100%')
|
||||
.constraintSize({ maxHeight: '78%' })
|
||||
.objectFit(ImageFit.Contain)
|
||||
|
||||
/* 底部:文件名 + 关闭提示 */
|
||||
Column({ space: 4 }) {
|
||||
Text(this.previewName !== '' ? this.previewName : '附件')
|
||||
.fontSize(FbMetric.fontSmall)
|
||||
.fontColor(Color.White)
|
||||
.maxLines(2)
|
||||
.textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||
.textAlign(TextAlign.Center)
|
||||
Text('点任意处关闭')
|
||||
.fontSize(10)
|
||||
.fontColor('#B3FFFFFF')
|
||||
}
|
||||
.width('100%')
|
||||
.padding({ left: 24, right: 24, bottom: 40 })
|
||||
.alignItems(HorizontalAlign.Center)
|
||||
.position({ x: 0, y: '85%' })
|
||||
}
|
||||
.width('100%')
|
||||
.height('100%')
|
||||
.hitTestBehavior(HitTestMode.Block)
|
||||
.onClick((): void => {
|
||||
this.previewSrc = '';
|
||||
this.previewName = '';
|
||||
})
|
||||
}
|
||||
|
||||
/* 负责人浮层(单选) */
|
||||
if (this.attPickOpen) {
|
||||
FbPersonPickSheet({
|
||||
@@ -1231,7 +1324,7 @@ export struct FbTaskPanel {
|
||||
Text('子任务')
|
||||
.fontSize(FbMetric.fontSmall)
|
||||
.fontWeight(FontWeight.Medium)
|
||||
.fontColor(FbColors.ink)
|
||||
.fontColor(this.accent)
|
||||
if (this.kids().length > 0) {
|
||||
Text(this.kidDoneCount().toString() + '/' + this.kids().length.toString())
|
||||
.fontSize(FbMetric.fontTiny)
|
||||
@@ -1319,7 +1412,7 @@ export struct FbTaskPanel {
|
||||
Text('附件')
|
||||
.fontSize(FbMetric.fontSmall)
|
||||
.fontWeight(FontWeight.Medium)
|
||||
.fontColor(FbColors.ink)
|
||||
.fontColor(this.accent)
|
||||
if (this.attachs().length > 0) {
|
||||
Text(this.attachs().length.toString())
|
||||
.fontSize(FbMetric.fontTiny)
|
||||
@@ -1410,6 +1503,10 @@ export struct FbTaskPanel {
|
||||
.width(64)
|
||||
.height(64)
|
||||
.margin({ right: 8, bottom: 8 })
|
||||
/* #77c:点缩略图 → 图片全屏预览 / 文件交给系统应用打开 */
|
||||
.onClick((): void => {
|
||||
this.tapAttach(a);
|
||||
})
|
||||
}
|
||||
|
||||
/** 留言 */
|
||||
@@ -1424,7 +1521,7 @@ export struct FbTaskPanel {
|
||||
Text('留言')
|
||||
.fontSize(FbMetric.fontSmall)
|
||||
.fontWeight(FontWeight.Medium)
|
||||
.fontColor(FbColors.ink)
|
||||
.fontColor(this.accent)
|
||||
if (this.comments().length > 0) {
|
||||
Text(this.comments().length.toString())
|
||||
.fontSize(FbMetric.fontTiny)
|
||||
@@ -1438,23 +1535,33 @@ export struct FbTaskPanel {
|
||||
|
||||
ForEach(this.comments(), (c: FbTaskComment) => {
|
||||
Row({ space: 8 }) {
|
||||
/* 头像位:一个小色点(端上没拉头像,用底色 + 名字首字最直观) */
|
||||
Row() {
|
||||
Text(c.name.substring(0, 1))
|
||||
.fontSize(10)
|
||||
.fontColor(this.accent)
|
||||
}
|
||||
.width(22)
|
||||
.height(22)
|
||||
.borderRadius(11)
|
||||
.backgroundColor(c.color !== '' ? c.color : FbColors.inputBg)
|
||||
.justifyContent(FlexAlign.Center)
|
||||
/* 🔴 #77e(用户 2026-11-05):「留言里面的人,前面还是没有头像,需要补充。」
|
||||
⇒ 原来这里画的是"名字首字色块"(当时端上确实没拉头像)。
|
||||
现在 `FbTaskComment.avatar` 有了(服务端 SQL 一直返回 p.avatar,
|
||||
端上原来没解析),换成 `FbAvatar`:
|
||||
有头像图用图、没设头像才退回首字色块。
|
||||
⚠️ 用公共 `FbAvatar` 而不是自己拼 `Image`:全站头像的口径
|
||||
(尺寸/圆角/兜底色)都收在那一个组件里,自己拼迟早不一致。 */
|
||||
FbAvatar({
|
||||
name: c.name,
|
||||
color: c.color !== '' ? c.color : '#A84A28',
|
||||
dia: 24,
|
||||
fontSizeVp: 10,
|
||||
imageUrl: c.avatar !== ''
|
||||
? fbImageSrc(c.avatar, this.token()) : ''
|
||||
})
|
||||
|
||||
Column({ space: 2 }) {
|
||||
Row({ space: 6 }) {
|
||||
/* 🔴 #77e(用户 2026-11-05):「根据我们之前样式的规定,
|
||||
它的标题和里面的文字,应该按照现在这个模块的主题色加粗显示。」
|
||||
⇒ 留言人名用**模块主题色 + 加粗**(原来是 muted 灰、常规字重)。
|
||||
这是全站「小节标题/发言人以模块色标识」的既有规矩
|
||||
(观影用紫、旅程用绿、任务用赭红…),这里原来漏了。 */
|
||||
Text(c.name)
|
||||
.fontSize(FbMetric.fontTiny)
|
||||
.fontColor(FbColors.muted)
|
||||
.fontWeight(FontWeight.Medium)
|
||||
.fontColor(this.accent)
|
||||
Text(c.createdAt)
|
||||
.fontSize(9)
|
||||
.fontColor(FbColors.faint)
|
||||
@@ -1462,6 +1569,11 @@ export struct FbTaskPanel {
|
||||
.width('100%')
|
||||
.alignItems(VerticalAlign.Center)
|
||||
|
||||
/* 正文也按主题色加粗?——**不**。
|
||||
用户说的是"标题和里面的文字",但正文若整段主题色,
|
||||
简明度会掉(色彩是需要留白的)。
|
||||
⇒ 名字(=发言标题)主题色加粗;正文保持 ink 正文色。
|
||||
这条取舍写在 commit 里,若用户要求正文也改色,一行就能调。 */
|
||||
Text(c.content)
|
||||
.fontSize(FbMetric.fontSmall)
|
||||
.fontColor(FbColors.ink)
|
||||
|
||||
Reference in New Issue
Block a user