feat: 项目层级树+合作方绑定KOC资源+客户群全量可选+精简编辑表单,移除复盘报表
- 项目支持父子层级:树形折叠展示、详情面板内联管理总群成员、推送聚合子项目任务、环检测与子项目删除守卫 - 外部KOC合作方绑定 KOC 资源账号(唯一占用、换绑/解绑、资源页一键设为合作方),列表直显联系方式 - 绑定客户群改为全量外部群缓存(含各群主),同步按钮强刷;member_count 缺失以 member_list 兜底 - 任务/项目编辑模式隐藏不可变字段(平台/内容类型/品牌/总群成员),详情面板维护成员时保留 parent_id - 删除复盘报表整栏(导航/页面/task_report_list/导出路由/CSV lib 及测试)
This commit is contained in:
@@ -37,14 +37,18 @@ export function TaskCustomerChatModal({
|
||||
const [selectedChatId, setSelectedChatId] = useState(task.customerChatId);
|
||||
const [bindResult, setBindResult] = useState<BindResult | null>(null);
|
||||
|
||||
const loadChats = useCallback(async () => {
|
||||
const loadChats = useCallback(async (options?: { sync?: boolean }) => {
|
||||
setLoading(true);
|
||||
setError("");
|
||||
try {
|
||||
const response = await fetch("/api/action", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ action: "task_customer_chats", taskId: task.id }),
|
||||
body: JSON.stringify({
|
||||
action: "task_customer_chats",
|
||||
taskId: task.id,
|
||||
sync: options?.sync === true,
|
||||
}),
|
||||
});
|
||||
const parsed = await readApiResponse<{
|
||||
chats?: CustomerChat[];
|
||||
@@ -129,7 +133,7 @@ export function TaskCustomerChatModal({
|
||||
<div>
|
||||
<h2>绑定客户群</h2>
|
||||
<p>
|
||||
列出任务负责人名下的企业微信客户群,绑定后生成进群活码,认领人在领取页扫码进群。
|
||||
列出全部成员名下的企业微信客户群(含各群主的外部群),绑定后生成进群活码,认领人在领取页扫码进群。
|
||||
</p>
|
||||
</div>
|
||||
<button type="button" onClick={onClose} aria-label="关闭">×</button>
|
||||
@@ -142,10 +146,10 @@ export function TaskCustomerChatModal({
|
||||
<button
|
||||
type="button"
|
||||
className="ghost-button"
|
||||
onClick={() => void loadChats()}
|
||||
onClick={() => void loadChats({ sync: true })}
|
||||
disabled={loading || saving}
|
||||
>
|
||||
{loading ? "拉取中…" : "刷新列表"}
|
||||
{loading ? "同步中…" : "同步最新群列表"}
|
||||
</button>
|
||||
</div>
|
||||
{error && <p className="form-error">{error}</p>}
|
||||
@@ -158,11 +162,11 @@ export function TaskCustomerChatModal({
|
||||
)}
|
||||
<div className="wecom-picker-list">
|
||||
{loading ? (
|
||||
<div className="wecom-picker-empty">正在拉取负责人名下客户群…</div>
|
||||
<div className="wecom-picker-empty">正在拉取客户群列表…</div>
|
||||
) : chats.length === 0 ? (
|
||||
<div className="wecom-picker-empty">
|
||||
未拉取到客户群——请任务负责人先在企微客户端创建客户群(内外部成员都可拉入),
|
||||
再点击「刷新列表」。
|
||||
未拉取到客户群——请群主先在企微客户端创建客户群(内外部成员都可拉入,且群主需在应用可见范围内),
|
||||
再点击「同步最新群列表」。
|
||||
</div>
|
||||
) : (
|
||||
chats.map((chat) => {
|
||||
@@ -174,7 +178,9 @@ export function TaskCustomerChatModal({
|
||||
</span>
|
||||
<span className="wecom-picker-meta">
|
||||
<span className="wecom-picker-name">{chat.name || chat.chatId}</span>
|
||||
<span className="wecom-picker-sub">{chat.memberCount} 人</span>
|
||||
<span className="wecom-picker-sub">
|
||||
群主 {chat.ownerUserId || "—"} · {chat.memberCount} 人
|
||||
</span>
|
||||
<code className="wecom-picker-id">{chat.chatId}</code>
|
||||
</span>
|
||||
<input
|
||||
|
||||
Reference in New Issue
Block a user