| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475 |
- import { Injectable } from '@angular/core';
- import {
- AiSelectionStage,
- Activity,
- BriefScenario,
- Candidate,
- CandidateStatus,
- DeliveryVersion,
- FieldMapping,
- GapItem,
- InteractionLog,
- LearnedRule,
- NavItem,
- NoteDetail,
- NoteVerdict,
- Requirement,
- ResourceSummary,
- ResourceFile,
- ResourceIssue,
- Rule,
- RuleTestCase,
- SamplingTask,
- Step,
- TaskTimelineItem,
- TaskRecord,
- TemplateField,
- TemplateItem,
- ViewKey
- } from './models';
- function buildNotes(account: string, styles: string[], startMatch: number): NoteDetail[] {
- return Array.from({ length: 10 }, (_, index) => {
- const style = styles[index % styles.length];
- const match = Math.max(58, startMatch - index * 2 + (index % 3));
- return {
- id: `${account}-${index + 1}`,
- title: [
- '用了三周的空瓶真实复盘',
- '通勤包里的急救护肤三件套',
- '成分党也会喜欢的轻乳霜',
- '换季敏感肌别再乱叠加',
- '早八人的极简护肤流程',
- '这支面霜到底适合谁',
- '出差三天我只带这几样',
- '热门配方横评和真实感受',
- '编辑部最近在用什么',
- '本周值得看的护肤趋势'
- ][index],
- publishTime: `2026-04-${String(28 - index).padStart(2, '0')}`,
- format: index % 3 === 0 ? '视频' : '图文',
- style,
- match,
- verdict: match >= 82 ? '正向样本' : match >= 76 ? '需复核' : '负向样本',
- summary: `${account} 近10条内容样本之一,AI 用标题、正文关键词、图片场景和互动反馈判断是否贴合 Brief 的内容风格。`,
- visualCue:
- index % 3 === 0
- ? '封面为真人出镜或产品实拍,画面信息完整。'
- : '图片以生活场景和局部细节为主,弱化广告感。',
- textCue:
- match >= 80
- ? '正文中出现使用周期、肤感、场景、优缺点等真实体验词。'
- : '正文偏资讯清单或功效罗列,缺少连续使用体验。',
- mismatch: match >= 80 ? '暂无明显冲突' : '真实体验占比不足,可能和 Brief 的“不要硬广”要求冲突。',
- keywords: ['真实体验', style, index % 2 === 0 ? '通勤' : '成分', '非硬广'],
- metrics: {
- read: `${8 + index * 2}.${index}万`,
- like: `${1200 + index * 180}`,
- collect: `${620 + index * 90}`,
- comment: `${86 + index * 11}`
- },
- aiReason:
- match >= 80
- ? '内容表达自然,场景和产品体验占比高,可作为风格匹配证据。'
- : '资讯或清单感偏重,真实体验表达不足,建议进入人工复核。'
- };
- });
- }
- function getApiBaseUrl(): string {
- if (typeof window === 'undefined') {
- return '';
- }
- const isAngularDevServer =
- (window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1') &&
- window.location.port === '4200';
- return isAngularDevServer ? 'http://localhost:3000' : '';
- }
- function inferFanTier(fans: string): string {
- const value = Number.parseFloat(fans);
- if (fans.includes('万') && value >= 50) {
- return '50万以上';
- }
- if (fans.includes('万') && value >= 10) {
- return '10万-50万';
- }
- if (fans.includes('万') && value >= 1) {
- return '1万-10万';
- }
- return '1万以下';
- }
- function buildCandidate(input: {
- id: string;
- platform: string;
- name: string;
- category: string;
- fans: string;
- price: number;
- score: number;
- styleMatch: number;
- status: CandidateStatus;
- tags: string[];
- reason: string;
- risk: string;
- owner: string;
- supplier: string;
- dataFreshness: number;
- priceExpire: string;
- styles: string[];
- startMatch: number;
- readMedian: string;
- interactionMedian: string;
- viralRate: number;
- replyRate: number;
- fulfillmentRate: number;
- naturalTrafficRatio: number;
- cooperationNoteRatio: number;
- estimatedCpm: number;
- audienceProfile: string;
- contentForms: string[];
- recallSources: string[];
- serviceLevel: string;
- brandFit: string;
- selected?: boolean;
- }): Candidate {
- return {
- id: input.id,
- platform: input.platform,
- name: input.name,
- category: input.category,
- contentForms: input.contentForms,
- fanTier: inferFanTier(input.fans),
- fans: input.fans,
- price: input.price,
- score: input.score,
- styleMatch: input.styleMatch,
- status: input.status,
- tags: input.tags,
- reason: input.reason,
- risk: input.risk,
- quota: `${input.platform} 扩展池`,
- selected: input.selected ?? false,
- link: `https://demo.tihao.ai/${input.platform}/${input.id}`,
- owner: input.owner,
- supplier: input.supplier,
- dataFreshness: input.dataFreshness,
- priceExpire: input.priceExpire,
- audienceProfile: input.audienceProfile,
- readMedian: input.readMedian,
- interactionMedian: input.interactionMedian,
- viralRate: input.viralRate,
- replyRate: input.replyRate,
- fulfillmentRate: input.fulfillmentRate,
- naturalTrafficRatio: input.naturalTrafficRatio,
- cooperationNoteRatio: input.cooperationNoteRatio,
- estimatedCpm: input.estimatedCpm,
- cooperationHistory: '近 12 个月有 2-5 次同类客户合作记录',
- serviceLevel: input.serviceLevel,
- brandFit: input.brandFit,
- recallSources: input.recallSources,
- ruleHits: ['资源库召回', '内容标签匹配', input.serviceLevel, input.brandFit],
- scoreBreakdown: [
- { label: '需求匹配', value: Math.min(99, input.score + 1) },
- { label: '内容表现', value: Math.min(99, input.styleMatch + 2) },
- { label: '预算健康', value: input.price <= 8000 ? 92 : input.price <= 15000 ? 82 : 72 },
- { label: '服务履约', value: input.fulfillmentRate }
- ],
- recentNotes: buildNotes(input.name, input.styles, input.startMatch)
- };
- }
- @Injectable({ providedIn: 'root' })
- export class DemoStateService {
- activeView: ViewKey = 'workspaceFlow';
- currentStep = 1;
- simpleMode = true;
- showAdvancedWorkspace = false;
- uploadedFileName = '某品牌种草合作Brief.docx';
- resourcePath = 'D:\\公司优秀媒体资源库\\4月份资源库';
- feedbackText =
- '客户反馈:小红书希望减少泛美妆号,优先生活方式、通勤穿搭、真实测评风格;抖音达人预算再压低15%,保留爆文率高的账号。';
- exportState = '待生成';
- apiState = 'PostgreSQL/RAG 检索';
- tempRuleTitle = '母婴/护肤场景中,近30天评论含“真实有用”的账号加权';
- tempRuleImpact = '预计强推荐增加 6 个,需复核减少 2 个';
- selectedPlatform = '全部';
- selectedStatus: CandidateStatus | '全部' = '全部';
- budgetMax = 18000;
- creatorQuery = '护肤 真实测评 通勤生活';
- creatorPlatform = '全部';
- creatorCategory = '全部';
- creatorContentForm = '全部';
- creatorFanTier = '全部';
- creatorRisk = '全部';
- creatorBudgetMax = 15000;
- sortMode: '综合分' | '风格匹配' | '报价升序' = '综合分';
- selectedCandidateId = 'M-1087';
- selectedNoteId = '梨涡测评室-1';
- noteVerdictFilter: NoteVerdict | '全部' = '全部';
- compareOpen = false;
- previewAudience: '客户版' | '内部复核版' = '客户版';
- selectedResourcePlatform = '小红书';
- selectedTaskId = 'TH-202605-001';
- selectedRuleId = 'R-003';
- ruleSandboxText = '客户要求:真实体验、通勤生活,不要硬广,预算尽量控制在 1 万以内。';
- ruleSandboxWeight = 80;
- ruleSimulationState = '尚未试算';
- templatePreviewMode: '客户版' | '内部复核版' | '复盘版' = '客户版';
- briefingNote =
- '目标客户想看的是“工具能不能把经验留下来”,所以 Demo 里要突出可确认、可追溯、可学习,而不是只展示一张漂亮名单。';
- selectedLearningScope: '本任务' | '单客户' | '行业' | '长期规则' = '本任务';
- templateUploadState = '等待上传,当前使用系统内置推荐表模板';
- deliveryChecklist = [
- { label: '客户版隐藏供应商与内部风险', detail: '对客只展示平台、账号、报价、推荐理由和内容证据', done: true },
- { label: '需复核账号已处理', detail: '导出前强推荐和备选账号不能含未解释风险', done: false },
- { label: '报价与档期已由媒介确认', detail: '价格过期或未绑定供应商的账号只能进内部版', done: false },
- { label: '反馈学习入口已准备', detail: '客户驳回原因可回流到规则库,不丢失原文', done: true }
- ];
- learningScopes: Array<'本任务' | '单客户' | '行业' | '长期规则'> = ['本任务', '单客户', '行业', '长期规则'];
- noteVerdicts: Array<NoteVerdict | '全部'> = ['全部', '正向样本', '需复核', '负向样本'];
- feedbackPresets = [
- '客户临时要求:小红书主推生活方式,不要泛美妆资讯号。',
- '商务复核:报价超过预算上限的账号先放备选,除非爆文率明显高。',
- '媒介反馈:近期未更新价格的账号需要先联系供应商确认档期。',
- '客户偏好:保留真实空瓶、通勤场景、素人感强的内容样本。'
- ];
- creatorQuickPrompts = [
- '找小红书护肤生活方式博主,2k-8k,真实测评,不要硬广',
- '保留 B站专业背书,补 3 个长视频成分科普账号',
- '抖音预算压低 15%,只保留爆文率高且报价健康的账号',
- '参考梨涡测评室,扩展 10 个相似风格中腰部博主'
- ];
- aiSelectionStages: AiSelectionStage[] = [
- {
- title: '需求理解',
- subtitle: '从 Brief 和临时反馈抽取可执行条件',
- input: '客户、品牌、平台数量、预算、内容风格、排除项、参考博主',
- aiJudgement: '识别出“小红书主推真实测评/通勤生活,预算 2k-18k,硬广降级”。',
- humanControl: '商务可编辑需求点、关闭低置信度推断、追加临时需求。',
- output: '6 个确认需求点,2 个待复核推断',
- status: '完成',
- accounts: ['客户A / 新锐护肤品牌']
- },
- {
- title: '候选召回',
- subtitle: '像蒲公英一样从多入口找博主',
- input: '资源库、关键词、相似博主、历史采纳、平台筛选',
- aiJudgement: '合并 5 类召回来源,去重后形成 324 个候选账号。',
- humanControl: '媒介可指定参考账号、锁定平台比例、加入手工收藏账号。',
- output: '小红书 182、抖音 72、B站 28、微信 18、微博 24',
- status: '完成',
- accounts: ['梨涡测评室', '晚间护肤手账', '通勤包里有什么']
- },
- {
- title: '硬性过滤',
- subtitle: '先处理不能推荐或必须降级的账号',
- input: '黑名单、报价、供应商绑定、报价有效期、近期活跃、合作禁区',
- aiJudgement: '剔除黑名单和低活跃账号 31 个,报价过期账号进入备选。',
- humanControl: '媒介可临时放行,但必须填写放行理由。',
- output: '293 个账号进入评分池',
- status: '完成',
- accounts: ['美妆情报局', '护肤趋势观察']
- },
- {
- title: '内容采样',
- subtitle: '抽取近 10 条内容做风格和硬广判断',
- input: '标题、正文关键词、图片/视频场景、互动质量、评论反馈',
- aiJudgement: '真实体验、通勤、成分拆解命中率高的账号加权,资讯/硬广感降权。',
- humanControl: '单条笔记可标为正向、需复核或负向样本。',
- output: '已完成 14 个账号采样,4 个仍待媒介确认',
- status: '进行中',
- accounts: ['梨涡测评室', '敏感肌自救记录', '阿森护肤实验室']
- },
- {
- title: '多目标评分',
- subtitle: '把单账号质量变成可解释分数',
- input: '需求匹配、内容表现、粉丝画像、预算健康、数据新鲜度、服务履约、风险扣分',
- aiJudgement: '晚间护肤手账因风格和粉丝画像高匹配进入强推荐,微博资讯号降级。',
- humanControl: '可调整权重并记录为本次任务规则。',
- output: 'Top 20 推荐池和 Top 60 备选池',
- status: '进行中',
- accounts: ['晚间护肤手账', '配方师小简', '打工人变美清单']
- },
- {
- title: '组合配比',
- subtitle: '避免整份名单同质化',
- input: '平台比例、头腰尾比例、内容类目、价格带、主推/备选数量',
- aiJudgement: '控制小红书生活方式占比,保留 B站专业背书和低预算补充位。',
- humanControl: '可锁定账号、换一批、批量强推/备选/剔除。',
- output: '客户版 16 个主推 + 12 个备选',
- status: '待执行',
- accounts: ['通勤包里有什么', '配方师小简', '周末家居护肤']
- },
- {
- title: '交付与学习',
- subtitle: '输出客户版,并把反馈变成待确认规则',
- input: '最终名单、推荐理由、风险说明、客户反馈',
- aiJudgement: '客户反馈只生成待确认规则,不自动污染长期规则库。',
- humanControl: '商务确认后入库,可限制规则只对单客户生效。',
- output: '名单 v1.0 + 待确认规则 3 条',
- status: '待执行',
- accounts: ['反馈学习规则']
- }
- ];
- deliveryVersions: DeliveryVersion[] = [
- {
- version: 'v0.1',
- title: 'AI 初稿',
- owner: '系统',
- time: '2026-05-12 01:20',
- summary: '按 Brief 生成 104 个候选,输出 Top 20 初稿。',
- status: '已生成'
- },
- {
- version: 'v0.3',
- title: '媒介复核版',
- owner: '媒介-阿敏',
- time: '2026-05-12 02:05',
- summary: '剔除 4 个硬广风险账号,补入 B站专业背书账号。',
- status: '复核中'
- },
- {
- version: 'v1.0',
- title: '客户交付版',
- owner: '商务-王一',
- time: '待导出',
- summary: '隐藏内部风险字段,只保留推荐理由、报价、内容样本和排期建议。',
- status: '待交付'
- }
- ];
- deliveryFields = [
- { label: '平台', customer: true, internal: true, selected: true },
- { label: '账号昵称', customer: true, internal: true, selected: true },
- { label: '粉丝量', customer: true, internal: true, selected: true },
- { label: '报价', customer: true, internal: true, selected: true },
- { label: '推荐理由', customer: true, internal: true, selected: true },
- { label: '内容样本', customer: true, internal: true, selected: true },
- { label: '供应商', customer: false, internal: true, selected: this.previewAudience === '内部复核版' },
- { label: '风险提示', customer: false, internal: true, selected: this.previewAudience === '内部复核版' },
- { label: '规则命中', customer: false, internal: true, selected: this.previewAudience === '内部复核版' }
- ];
- resourceHealthItems = [
- { label: '报价有效率', value: 82, detail: '报价未过期且供应商可确认' },
- { label: '字段完整率', value: 78, detail: '昵称、链接、报价、互动、供应商字段齐全' },
- { label: '近30天更新率', value: 69, detail: '本地资源表最近一次更新时间' },
- { label: '可联系比例', value: 88, detail: '已绑定媒介负责人或供应商联系人' }
- ];
- resourceFiles: ResourceFile[] = [
- {
- platform: '小红书',
- name: '小红书4月优质种草资源.xlsx',
- updateTime: '2026-05-10 18:20',
- rows: 4286,
- owner: '媒介-阿敏',
- missingFields: ['报价有效期']
- },
- {
- platform: '抖音',
- name: '抖音星图护肤达人池.xlsx',
- updateTime: '2026-05-09 21:10',
- rows: 2130,
- owner: '媒介-阿宁',
- missingFields: ['供应商联系人', '档期']
- },
- {
- platform: 'B站',
- name: 'B站长视频评测UP主.xlsx',
- updateTime: '2026-05-08 11:30',
- rows: 684,
- owner: '媒介-六月',
- missingFields: ['近30天播放中位数']
- },
- {
- platform: '微信',
- name: '公众号图文资源库.xlsx',
- updateTime: '2026-04-29 16:45',
- rows: 516,
- owner: '媒介-小七',
- missingFields: ['主页链接', '供应商联系人']
- },
- {
- platform: '微博',
- name: '微博话题扩散账号.xlsx',
- updateTime: '2026-05-06 14:05',
- rows: 902,
- owner: '媒介-小满',
- missingFields: ['报价有效期', '历史配合度']
- }
- ];
- resourceIssues: ResourceIssue[] = [
- {
- id: 'RI-001',
- platform: '小红书',
- level: '中',
- title: '报价有效期缺失',
- detail: '部分资源只有图文报价,缺少报价有效期,导出前需要媒介确认。',
- action: '批量标为备选并提醒媒介确认',
- resolved: false
- },
- {
- id: 'RI-002',
- platform: '抖音',
- level: '高',
- title: '高报价账号需要预算复核',
- detail: '客户压预算后,部分抖音账号仍接近预算上限。',
- action: '进入预算沙盘,优先保留爆文率高账号',
- resolved: false
- },
- {
- id: 'RI-003',
- platform: '微信',
- level: '高',
- title: '主页链接不完整',
- detail: '缺主页链接会影响近10条图文采样和风格比对。',
- action: '要求资源库补齐链接后再进入强推荐',
- resolved: false
- }
- ];
- ruleConflicts = [
- {
- title: '低预算优先 vs 保留头部背书',
- detail: '客户压预算后,B站专业背书账号报价偏高,需要保留为备选并标注价值。',
- level: '中'
- },
- {
- title: '真实体验优先 vs 话题扩散',
- detail: '微博资讯号适合扩散,但和“不要硬广”存在冲突,客户版建议隐藏。',
- level: '高'
- }
- ];
- ruleTestCases: RuleTestCase[] = [
- {
- id: 'RTC-001',
- label: '命中真实体验',
- condition: '标题/正文出现空瓶、用了三周、真实反馈等词',
- action: '风格分 +8,推荐理由补充真实体验证据',
- enabled: true
- },
- {
- id: 'RTC-002',
- label: '命中硬广表达',
- condition: '正文偏优惠、强卖点、购买引导,缺少使用过程',
- action: '降级为需复核,客户版隐藏',
- enabled: true
- },
- {
- id: 'RTC-003',
- label: '预算保护',
- condition: '报价低于预算上限 60%,且互动中位数高于同价位',
- action: '综合分 +5,进入性价比候选',
- enabled: false
- }
- ];
- briefScenarios: BriefScenario[] = [
- {
- id: 'budget-down',
- label: '客户压预算 15%',
- description: '报价接近上限的账号降级,低预算高匹配账号加权。',
- selected: false,
- impact: '抖音高报价账号转备选,小红书中腰部账号上浮'
- },
- {
- id: 'life-style',
- label: '小红书偏生活方式',
- description: '通勤、生活方式、真实分享类账号优先。',
- selected: false,
- impact: '通勤/生活方式标签加权,泛美妆资讯降权'
- },
- {
- id: 'strict-style',
- label: '严格排除硬广',
- description: '近10条内容匹配低于80%的账号进入复核池。',
- selected: false,
- impact: '资讯感强、清单感强的账号不进入强推荐'
- },
- {
- id: 'keep-bilibili',
- label: '保留 B站专业背书',
- description: '即使数量少,也保留长视频成分科普账号。',
- selected: false,
- impact: 'B站成分科普账号进入备选或强推荐候选'
- }
- ];
- interactionLogs: InteractionLog[] = [
- { time: '10:12', action: 'Brief 解析', detail: '已识别平台、数量、预算和内容风格 6 个需求点。' },
- { time: '10:18', action: '规则确认', detail: '启用 5 条经验规则,保留 1 条同质化限制待确认。' },
- { time: '10:24', action: '账号复核', detail: '梨涡测评室被标记为强推荐,近10条样本 9 条符合。' }
- ];
- navItems: NavItem[] = [
- { key: 'workspaceFlow', label: '提号工作台', note: '上传Brief→导出推荐表' },
- { key: 'workspace', label: '提号流程(完整)', note: 'Brief 到推荐名单' },
- { key: 'creatorSearch', label: '找博主', note: '像蒲公英一样筛人' },
- { key: 'delivery', label: '名单交付', note: '客户版与内部版' },
- { key: 'tasks', label: '任务记录', note: '客户需求与版本' },
- { key: 'resources', label: '资源库', note: '本地媒体表管理' },
- { key: 'rules', label: '规则库', note: '经验与提炼规则' },
- { key: 'learning', label: '反馈学习', note: '复盘与自我提升' },
- { key: 'templates', label: '模板中心', note: 'Brief 与推荐表' },
- { key: 'progressBoard', label: '进度看板', note: '人×任务×进度实时' }
- ];
- simpleNavKeys: ViewKey[] = ['workspaceFlow', 'workspace', 'creatorSearch', 'delivery', 'tasks', 'progressBoard'];
- businessShortcuts = [
- {
- id: 'fast-list',
- title: '一键生成可看名单',
- detail: '自动确认需求、运行规则、完成采样,先拿到可讨论的名单。'
- },
- {
- id: 'review-risk',
- title: '只看待处理问题',
- detail: '跳到资源缺口和需复核账号,适合媒介补信息。'
- },
- {
- id: 'client-preview',
- title: '客户版预览',
- detail: '隐藏内部风险字段,查看对客话术和推荐理由。'
- },
- {
- id: 'learn-feedback',
- title: '反馈变规则',
- detail: '把客户反馈转成待确认规则,保留人工审核。'
- }
- ];
- businessChecklist = [
- { key: 'brief', label: 'Brief 已解析', detail: '客户、平台、预算、风格已抽取' },
- { key: 'demand', label: '需求已确认', detail: '低置信度内容可编辑' },
- { key: 'rule', label: '规则已选好', detail: '默认启用核心经验规则' },
- { key: 'sample', label: '内容已采样', detail: '近10条笔记用于风格比对' },
- { key: 'export', label: '名单可导出', detail: '客户版和内部版分开输出' }
- ];
- stepGuides = [
- {
- objective: '把客户 Brief 变成可解析任务',
- operator: '上传或替换 Brief 文件',
- output: '客户、平台、预算、风格字段'
- },
- {
- objective: '确认 AI 抽取是否准确',
- operator: '勾选、编辑、补充需求点',
- output: '可执行需求清单'
- },
- {
- objective: '决定本次提号怎么筛',
- operator: '启用经验规则或补临时规则',
- output: '本次任务规则组合'
- },
- {
- objective: '从资源库召回并评分',
- operator: '运行提号和近10条内容采样',
- output: '带证据的候选池'
- },
- {
- objective: '复核账号并形成名单',
- operator: '筛选、标记、查看账号详情',
- output: '客户版推荐名单'
- },
- {
- objective: '把反馈沉淀成能力',
- operator: '提炼、确认或驳回规则',
- output: '可复用规则与记忆'
- }
- ];
- steps: Step[] = [
- { label: '上传 Brief', status: 'active' },
- { label: '确认需求', status: 'todo' },
- { label: '选择规则', status: 'todo' },
- { label: '生成推荐', status: 'todo' },
- { label: '导出推荐表', status: 'todo' },
- { label: '反馈学习', status: 'todo' }
- ];
- templates: TemplateItem[] = [
- { name: '达人Brief.xlsx', type: 'Brief模板', useCase: '客户标准化需求录入', selected: false },
- { name: '某品牌种草合作Brief.docx', type: 'Brief模板', useCase: '非结构化需求解析', selected: false },
- { name: '某品牌达人推广规划.xlsx', type: '规划模板', useCase: '多平台组合拆解', selected: false },
- { name: '【机构名称】客户+品牌-需求推荐名单.xlsx', type: '推荐表模板', useCase: '对客输出名单', selected: true },
- { name: '内部复核名单-含风险字段.xlsx', type: '复核模板', useCase: '媒介确认报价、供应商、风险和档期', selected: false },
- { name: '客户反馈复盘表.xlsx', type: '复盘模板', useCase: '沉淀客户偏好、排除项和长期规则', selected: false }
- ];
- requirements: Requirement[] = [
- {
- label: '客户/品牌',
- value: '客户A / 新锐护肤品牌',
- confidence: 96,
- selected: true,
- editable: false,
- evidence: 'Brief 标题:新锐护肤品牌种草合作需求'
- },
- {
- label: '投放目标',
- value: '新品种草、真实测评、站内转化蓄水',
- confidence: 92,
- selected: true,
- editable: false,
- evidence: 'Brief 目标:新品首发,希望突出真实使用体验'
- },
- {
- label: '平台需求',
- value: '小红书 20人、抖音 8人、B站 4人',
- confidence: 90,
- selected: true,
- editable: false,
- evidence: '达人推广规划 Sheet:平台配比'
- },
- {
- label: '预算边界',
- value: '单号 2,000-18,000,优先性价比',
- confidence: 84,
- selected: true,
- editable: false,
- evidence: '预算列存在多个空值,AI 按已填区间推断'
- },
- {
- label: '内容风格',
- value: '真实体验、通勤生活、轻专业成分党',
- confidence: 88,
- selected: true,
- editable: false,
- evidence: 'Brief 原文:不要硬广,要像真实分享'
- },
- {
- label: '排除项',
- value: '硬广感强、近30天低活跃、黑名单供应商',
- confidence: 82,
- selected: true,
- editable: false,
- evidence: '规则库默认风控项 + Brief 排除描述'
- }
- ];
- rules: Rule[] = [
- {
- id: 'R-001',
- title: '按 Brief 平台优先提号',
- description: '先满足 Brief 明确指定的平台,再用相邻平台补足备选池。',
- category: '平台响应',
- source: '经验规则',
- selected: true,
- impact: '确保客户要求不被泛化'
- },
- {
- id: 'R-002',
- title: '默认推荐数量不少于需求 3 倍',
- description: '当 Brief 写明数量时,候选池按需求数量的 3 倍以上生成,便于商务二次筛选。',
- category: '数量策略',
- source: '经验规则',
- selected: true,
- impact: '小红书候选池 60 个,抖音 24 个,B站 12 个'
- },
- {
- id: 'R-003',
- title: '风格匹配低于 80% 自动降级',
- description: '需要风格比对时,采样账号近 10 篇内容,图文/标题/标签综合匹配不足 80% 标记为需复核。',
- category: '内容风格',
- source: '经验规则',
- selected: true,
- impact: '减少客户返工'
- },
- {
- id: 'R-004',
- title: '黑名单与低配合度剔除',
- description: '历史拉黑、配合度低、供应商关系未绑定的媒体默认不进入强推荐。',
- category: '风险控制',
- source: '经验规则',
- selected: true,
- impact: '降低执行风险'
- },
- {
- id: 'R-005',
- title: '优先近期采集有效价格',
- description: '报价有效期已过或采集超过 45 天的资源进入备选,并提示媒介复核。',
- category: '价格有效性',
- source: 'AI提炼',
- selected: true,
- impact: '避免报价失真'
- },
- {
- id: 'R-006',
- title: '同质化账号限额',
- description: '同一平台同一内容类目强推荐不超过 40%,避免名单看起来重复。',
- category: '组合均衡',
- source: '用户反馈',
- selected: false,
- impact: '提升名单丰富度'
- }
- ];
- candidates: Candidate[] = [
- {
- id: 'M-1087',
- platform: '小红书',
- name: '梨涡测评室',
- fans: '18.6万',
- price: 6800,
- score: 94,
- styleMatch: 91,
- status: '强推荐',
- tags: ['真实测评', '成分党', '图文强'],
- reason: '近10篇中 9 篇为真实体验或成分拆解,互动中位数高于同价位账号 28%。',
- risk: '视频内容偏少,可作为图文主推。',
- quota: '小红书 1/60',
- selected: true,
- link: 'https://www.xiaohongshu.com/user/profile/demo1087',
- owner: '媒介-阿敏',
- supplier: '杭州某MCN',
- dataFreshness: 92,
- priceExpire: '2026-05-31',
- ruleHits: ['平台优先', '风格>80%', '报价有效', '供应商已绑定'],
- scoreBreakdown: [
- { label: '需求匹配', value: 95 },
- { label: '历史表现', value: 89 },
- { label: '风险健康', value: 96 }
- ],
- recentNotes: buildNotes('梨涡测评室', ['真实测评', '通勤生活', '成分拆解'], 94)
- },
- {
- id: 'M-2214',
- platform: '小红书',
- name: '通勤包里有什么',
- fans: '9.4万',
- price: 3600,
- score: 89,
- styleMatch: 86,
- status: '强推荐',
- tags: ['生活方式', '通勤', '性价比'],
- reason: '内容场景和 Brief 的通勤生活需求贴合,历史合作反馈为配合度高。',
- risk: '粉丝量中腰部,需要搭配高粉账号。',
- quota: '小红书 2/60',
- selected: true,
- link: 'https://www.xiaohongshu.com/user/profile/demo2214',
- owner: '媒介-小徐',
- supplier: '上海生活方式MCN',
- dataFreshness: 87,
- priceExpire: '2026-05-25',
- ruleHits: ['数量池补足', '生活方式加权', '低预算友好'],
- scoreBreakdown: [
- { label: '需求匹配', value: 88 },
- { label: '历史表现', value: 82 },
- { label: '风险健康', value: 92 }
- ],
- recentNotes: buildNotes('通勤包里有什么', ['通勤生活', '真实分享', '生活方式'], 90)
- },
- {
- id: 'M-3309',
- platform: '抖音',
- name: '林小林认真护肤',
- fans: '42.1万',
- price: 12800,
- score: 87,
- styleMatch: 82,
- status: '备选',
- tags: ['口播', '测评', '转化'],
- reason: '爆文率和完播率表现稳定,预算接近上限,建议作为抖音头部备选。',
- risk: '单条报价较高,需确认档期。',
- quota: '抖音 1/24',
- selected: true,
- link: 'https://www.douyin.com/user/demo3309',
- owner: '媒介-阿宁',
- supplier: '星图服务商',
- dataFreshness: 78,
- priceExpire: '2026-05-18',
- ruleHits: ['平台优先', '爆文率保护', '预算接近上限'],
- scoreBreakdown: [
- { label: '需求匹配', value: 84 },
- { label: '历史表现', value: 91 },
- { label: '风险健康', value: 78 }
- ],
- recentNotes: buildNotes('林小林认真护肤', ['口播测评', '真实测评', '科普'], 84)
- },
- {
- id: 'M-4402',
- platform: 'B站',
- name: '阿澈的成分笔记',
- fans: '13.2万',
- price: 9800,
- score: 84,
- styleMatch: 88,
- status: '备选',
- tags: ['长视频', '成分科普', '深度测评'],
- reason: '长视频讲解能力强,适合承接品牌专业背书。',
- risk: '制作周期较长。',
- quota: 'B站 1/12',
- selected: true,
- link: 'https://space.bilibili.com/demo4402',
- owner: '媒介-六月',
- supplier: '独立UP主',
- dataFreshness: 74,
- priceExpire: '2026-06-05',
- ruleHits: ['B站补充背书', '成分党加权', '制作周期提示'],
- scoreBreakdown: [
- { label: '需求匹配', value: 85 },
- { label: '历史表现', value: 80 },
- { label: '风险健康', value: 84 }
- ],
- recentNotes: buildNotes('阿澈的成分笔记', ['成分科普', '深度测评', '科普'], 91)
- },
- {
- id: 'M-5520',
- platform: '微信',
- name: '美妆情报局',
- fans: '7.8万',
- price: 5200,
- score: 78,
- styleMatch: 73,
- status: '需复核',
- tags: ['图文', '资讯', '价格友好'],
- reason: '预算友好,但近10篇资讯感较重,和真实测评风格存在偏差。',
- risk: '风格匹配不足 80%,需人工确认。',
- quota: '补充平台',
- selected: false,
- link: 'https://mp.weixin.qq.com/demo5520',
- owner: '媒介-小七',
- supplier: '公众号资源组',
- dataFreshness: 61,
- priceExpire: '2026-05-12',
- ruleHits: ['价格友好', '风格不足80%', '需复核'],
- scoreBreakdown: [
- { label: '需求匹配', value: 72 },
- { label: '历史表现', value: 76 },
- { label: '风险健康', value: 69 }
- ],
- recentNotes: buildNotes('美妆情报局', ['资讯', '清单', '趋势'], 73)
- },
- buildCandidate({
- id: 'M-6101',
- platform: '小红书',
- name: '晚间护肤手账',
- category: '护肤',
- fans: '26.3万',
- price: 8200,
- score: 91,
- styleMatch: 93,
- status: '强推荐',
- tags: ['空瓶', '真实体验', '生活方式'],
- reason: '长期做空瓶和敏感肌日记,内容语气自然,适合新品真实种草。',
- risk: '档期较满,需要提前 10 天锁定。',
- owner: '媒介-小乔',
- supplier: '华东生活方式资源组',
- dataFreshness: 91,
- priceExpire: '2026-06-08',
- styles: ['空瓶复盘', '真实测评', '生活方式'],
- startMatch: 93,
- readMedian: '9.8万',
- interactionMedian: '4860',
- viralRate: 18,
- replyRate: 94,
- fulfillmentRate: 96,
- naturalTrafficRatio: 82,
- cooperationNoteRatio: 22,
- estimatedCpm: 84,
- audienceProfile: '女性 25-34 岁,一二线城市占比 58%,护肤消费兴趣高',
- contentForms: ['图文', '视频'],
- recallSources: ['关键词召回', '相似博主召回', '历史采纳召回'],
- serviceLevel: '高配合度',
- brandFit: '护肤新品高匹配',
- selected: true
- }),
- buildCandidate({
- id: 'M-6102',
- platform: '小红书',
- name: '打工人变美清单',
- category: '美妆',
- fans: '11.7万',
- price: 4800,
- score: 88,
- styleMatch: 87,
- status: '强推荐',
- tags: ['通勤', '清单', '性价比'],
- reason: '通勤场景稳定,报价健康,可补足中腰部生活方式账号。',
- risk: '清单型内容较多,需要脚本里强化单品体验。',
- owner: '媒介-小徐',
- supplier: '上海通勤内容组',
- dataFreshness: 89,
- priceExpire: '2026-06-01',
- styles: ['通勤生活', '清单', '真实分享'],
- startMatch: 88,
- readMedian: '6.2万',
- interactionMedian: '2510',
- viralRate: 14,
- replyRate: 91,
- fulfillmentRate: 94,
- naturalTrafficRatio: 76,
- cooperationNoteRatio: 28,
- estimatedCpm: 77,
- audienceProfile: '女性 22-30 岁,新一线/二线城市白领占比高',
- contentForms: ['图文'],
- recallSources: ['条件筛选召回', '关键词召回'],
- serviceLevel: '高配合度',
- brandFit: '预算友好'
- }),
- buildCandidate({
- id: 'M-6103',
- platform: '小红书',
- name: '敏感肌自救记录',
- category: '护肤',
- fans: '7.2万',
- price: 3900,
- score: 86,
- styleMatch: 90,
- status: '备选',
- tags: ['敏感肌', '真实测评', '素人感'],
- reason: '素人感强,评论区真实问题多,适合作为可信样本补充。',
- risk: '粉丝规模偏小,建议搭配高互动账号。',
- owner: '媒介-阿敏',
- supplier: '独立博主',
- dataFreshness: 84,
- priceExpire: '2026-05-29',
- styles: ['敏感肌', '真实测评', '素人分享'],
- startMatch: 89,
- readMedian: '4.1万',
- interactionMedian: '1790',
- viralRate: 12,
- replyRate: 86,
- fulfillmentRate: 88,
- naturalTrafficRatio: 88,
- cooperationNoteRatio: 12,
- estimatedCpm: 69,
- audienceProfile: '女性 20-32 岁,敏感肌与成分党标签集中',
- contentForms: ['图文', '视频'],
- recallSources: ['关键词召回', '内容样本召回'],
- serviceLevel: '稳定履约',
- brandFit: '风格高匹配'
- }),
- buildCandidate({
- id: 'M-6201',
- platform: '抖音',
- name: '七七成分开麦',
- category: '护肤',
- fans: '68.5万',
- price: 18500,
- score: 83,
- styleMatch: 85,
- status: '备选',
- tags: ['口播', '成分科普', '爆文率高'],
- reason: '完播和爆文率强,适合作为抖音声量位,但预算压力较大。',
- risk: '报价超过当前建议上限,需谈价或只保留备选。',
- owner: '媒介-阿宁',
- supplier: '星图服务商',
- dataFreshness: 86,
- priceExpire: '2026-05-26',
- styles: ['成分科普', '口播测评', '转化'],
- startMatch: 86,
- readMedian: '46万',
- interactionMedian: '1.8万',
- viralRate: 22,
- replyRate: 82,
- fulfillmentRate: 89,
- naturalTrafficRatio: 63,
- cooperationNoteRatio: 41,
- estimatedCpm: 146,
- audienceProfile: '女性 24-35 岁,护肤功效兴趣强,三线以上城市占比 64%',
- contentForms: ['视频'],
- recallSources: ['平台筛选召回', '历史表现召回'],
- serviceLevel: '稳定履约',
- brandFit: '专业背书'
- }),
- buildCandidate({
- id: 'M-6202',
- platform: '抖音',
- name: '小麦真实开箱',
- category: '美妆',
- fans: '23.9万',
- price: 7600,
- score: 85,
- styleMatch: 81,
- status: '备选',
- tags: ['开箱', '测评', '转化'],
- reason: '开箱测评节奏好,价格在预算中段,适合补转化素材。',
- risk: '护肤深度不足,需要提供产品体验脚本。',
- owner: '媒介-南南',
- supplier: '短视频资源组',
- dataFreshness: 79,
- priceExpire: '2026-05-20',
- styles: ['开箱', '口播测评', '真实分享'],
- startMatch: 82,
- readMedian: '18万',
- interactionMedian: '6900',
- viralRate: 16,
- replyRate: 84,
- fulfillmentRate: 91,
- naturalTrafficRatio: 68,
- cooperationNoteRatio: 35,
- estimatedCpm: 96,
- audienceProfile: '女性 18-28 岁,彩妆/护肤兴趣重叠',
- contentForms: ['视频'],
- recallSources: ['条件筛选召回', '预算策略召回'],
- serviceLevel: '稳定履约',
- brandFit: '转化补充'
- }),
- buildCandidate({
- id: 'M-6203',
- platform: '抖音',
- name: '阿森护肤实验室',
- category: '护肤',
- fans: '15.8万',
- price: 6800,
- score: 82,
- styleMatch: 78,
- status: '需复核',
- tags: ['实验', '功效', '男性视角'],
- reason: '专业表达强,但画面偏实验室感,和生活方式 Brief 有偏差。',
- risk: '风格匹配低于 80%,客户版需谨慎呈现。',
- owner: '媒介-六月',
- supplier: '独立达人',
- dataFreshness: 72,
- priceExpire: '2026-05-19',
- styles: ['实验测评', '成分科普', '功效'],
- startMatch: 79,
- readMedian: '11万',
- interactionMedian: '4200',
- viralRate: 11,
- replyRate: 78,
- fulfillmentRate: 86,
- naturalTrafficRatio: 71,
- cooperationNoteRatio: 32,
- estimatedCpm: 91,
- audienceProfile: '男女比例接近,功效护肤兴趣高',
- contentForms: ['视频'],
- recallSources: ['内容标签召回'],
- serviceLevel: '需沟通确认',
- brandFit: '需风格复核'
- }),
- buildCandidate({
- id: 'M-6301',
- platform: 'B站',
- name: '配方师小简',
- category: '护肤',
- fans: '31.4万',
- price: 16800,
- score: 90,
- styleMatch: 92,
- status: '强推荐',
- tags: ['长视频', '成分科普', '专业背书'],
- reason: '长视频解释能力强,适合承担品牌专业背书和搜索留存。',
- risk: '制作周期 14 天,需先锁定脚本窗口。',
- owner: '媒介-六月',
- supplier: '独立UP主',
- dataFreshness: 82,
- priceExpire: '2026-06-12',
- styles: ['成分科普', '长视频测评', '专业拆解'],
- startMatch: 92,
- readMedian: '13万',
- interactionMedian: '5300',
- viralRate: 13,
- replyRate: 88,
- fulfillmentRate: 92,
- naturalTrafficRatio: 79,
- cooperationNoteRatio: 24,
- estimatedCpm: 126,
- audienceProfile: '女性 23-35 岁,成分党、学生党与白领混合',
- contentForms: ['长视频', '图文'],
- recallSources: ['相似博主召回', '历史采纳召回'],
- serviceLevel: '稳定履约',
- brandFit: '专业背书高匹配',
- selected: true
- }),
- buildCandidate({
- id: 'M-6302',
- platform: 'B站',
- name: '护肤避坑研究员',
- category: '护肤',
- fans: '8.9万',
- price: 7200,
- score: 81,
- styleMatch: 84,
- status: '备选',
- tags: ['避坑', '深度测评', '学生党'],
- reason: '评论区信任度高,适合做备选口碑内容。',
- risk: '更新频率一般,需要确认排期。',
- owner: '媒介-小七',
- supplier: '独立UP主',
- dataFreshness: 69,
- priceExpire: '2026-05-30',
- styles: ['避坑', '深度测评', '成分科普'],
- startMatch: 85,
- readMedian: '5.7万',
- interactionMedian: '2100',
- viralRate: 9,
- replyRate: 80,
- fulfillmentRate: 84,
- naturalTrafficRatio: 83,
- cooperationNoteRatio: 18,
- estimatedCpm: 102,
- audienceProfile: '女性 18-26 岁,学生党与初入职场占比较高',
- contentForms: ['长视频'],
- recallSources: ['关键词召回', '平台策略召回'],
- serviceLevel: '需沟通确认',
- brandFit: '备选口碑'
- }),
- buildCandidate({
- id: 'M-6401',
- platform: '微信',
- name: '成分慢读',
- category: '护肤',
- fans: '12.5万',
- price: 9600,
- score: 79,
- styleMatch: 80,
- status: '备选',
- tags: ['公众号', '深度图文', '专业'],
- reason: '适合承接长图文解释,补足私域和搜索沉淀。',
- risk: '平台不在主需求中,需要客户确认是否保留。',
- owner: '媒介-小七',
- supplier: '公众号资源组',
- dataFreshness: 65,
- priceExpire: '2026-05-22',
- styles: ['深度图文', '成分科普', '趋势'],
- startMatch: 81,
- readMedian: '3.4万',
- interactionMedian: '980',
- viralRate: 6,
- replyRate: 76,
- fulfillmentRate: 87,
- naturalTrafficRatio: 74,
- cooperationNoteRatio: 29,
- estimatedCpm: 132,
- audienceProfile: '女性 28-40 岁,高线城市和高消费力用户偏多',
- contentForms: ['图文'],
- recallSources: ['资源库召回'],
- serviceLevel: '稳定履约',
- brandFit: '补充平台'
- }),
- buildCandidate({
- id: 'M-6501',
- platform: '微博',
- name: '护肤趋势观察',
- category: '美妆',
- fans: '54.6万',
- price: 11800,
- score: 76,
- styleMatch: 72,
- status: '需复核',
- tags: ['趋势', '话题扩散', '资讯'],
- reason: '适合话题扩散,但内容资讯感强,不适合作为主推真实测评。',
- risk: '硬广和资讯感风险,需要从客户版隐藏或降级。',
- owner: '媒介-阿宁',
- supplier: '微博资源组',
- dataFreshness: 70,
- priceExpire: '2026-06-02',
- styles: ['趋势', '资讯', '话题'],
- startMatch: 74,
- readMedian: '22万',
- interactionMedian: '3600',
- viralRate: 10,
- replyRate: 73,
- fulfillmentRate: 82,
- naturalTrafficRatio: 58,
- cooperationNoteRatio: 44,
- estimatedCpm: 112,
- audienceProfile: '泛美妆兴趣人群,话题扩散强但转化弱',
- contentForms: ['图文', '短内容'],
- recallSources: ['平台补充召回'],
- serviceLevel: '需沟通确认',
- brandFit: '话题扩散'
- }),
- buildCandidate({
- id: 'M-6601',
- platform: '小红书',
- name: '妈妈的浴室柜',
- category: '母婴',
- fans: '16.9万',
- price: 6200,
- score: 84,
- styleMatch: 83,
- status: '备选',
- tags: ['母婴', '家庭场景', '温和护肤'],
- reason: '家庭和温和护肤场景自然,适合敏感肌线索扩展。',
- risk: '主目标不是母婴,需要确认是否纳入多场景名单。',
- owner: '媒介-小乔',
- supplier: '母婴生活资源组',
- dataFreshness: 88,
- priceExpire: '2026-06-04',
- styles: ['家庭场景', '温和护肤', '真实分享'],
- startMatch: 84,
- readMedian: '7.3万',
- interactionMedian: '2900',
- viralRate: 13,
- replyRate: 89,
- fulfillmentRate: 93,
- naturalTrafficRatio: 81,
- cooperationNoteRatio: 19,
- estimatedCpm: 86,
- audienceProfile: '女性 25-38 岁,妈妈人群和敏感肌标签重叠',
- contentForms: ['图文', '视频'],
- recallSources: ['场景扩展召回'],
- serviceLevel: '高配合度',
- brandFit: '场景扩展'
- }),
- buildCandidate({
- id: 'M-6701',
- platform: '小红书',
- name: '周末家居护肤',
- category: '家居',
- fans: '6.4万',
- price: 3100,
- score: 80,
- styleMatch: 82,
- status: '备选',
- tags: ['家居', '生活方式', '低预算'],
- reason: '生活方式感强,价格友好,可作为低预算补充。',
- risk: '护肤内容占比不足,需要限定选题。',
- owner: '媒介-小徐',
- supplier: '生活方式资源组',
- dataFreshness: 83,
- priceExpire: '2026-05-28',
- styles: ['家居生活', '生活方式', '真实分享'],
- startMatch: 83,
- readMedian: '3.6万',
- interactionMedian: '1320',
- viralRate: 8,
- replyRate: 87,
- fulfillmentRate: 90,
- naturalTrafficRatio: 84,
- cooperationNoteRatio: 15,
- estimatedCpm: 72,
- audienceProfile: '女性 23-34 岁,家居和通勤生活兴趣高',
- contentForms: ['图文'],
- recallSources: ['相似场景召回'],
- serviceLevel: '稳定履约',
- brandFit: '低预算补充'
- })
- ];
- private candidateBaseline = new Map(
- this.candidates.map((candidate) => [
- candidate.id,
- {
- score: candidate.score,
- styleMatch: candidate.styleMatch,
- status: candidate.status,
- selected: candidate.selected,
- risk: candidate.risk,
- reason: candidate.reason,
- ruleHits: [...candidate.ruleHits]
- }
- ])
- );
- resources: ResourceSummary[] = [
- { platform: '小红书', total: 4286, fresh: 86, matched: 182, status: '主力平台' },
- { platform: '抖音', total: 2130, fresh: 79, matched: 72, status: '预算复核' },
- { platform: 'B站', total: 684, fresh: 73, matched: 28, status: '补充背书' },
- { platform: '微信', total: 516, fresh: 61, matched: 18, status: '待人工确认' },
- { platform: '微博', total: 902, fresh: 69, matched: 24, status: '话题扩散' }
- ];
- fieldMappings: FieldMapping[] = [
- { source: '达人昵称', target: 'accountName', confidence: 98, confirmed: true },
- { source: '平台ID/主页链接', target: 'platformId / link', confidence: 94, confirmed: true },
- { source: '报价-图文', target: 'imagePrice', confidence: 91, confirmed: true },
- { source: '近30天互动中位数', target: 'interactionMedian', confidence: 88, confirmed: false },
- { source: '供应商联系人', target: 'supplierContact', confidence: 84, confirmed: false },
- { source: '备注/特殊要求', target: 'riskNote', confidence: 71, confirmed: false }
- ];
- activities: Activity[] = [
- { title: '解析 Brief 表格/文档', detail: '识别客户、品牌、平台、数量、预算、内容风格与排除项', status: '完成' },
- { title: '读取本地媒体资源库', detail: '模拟扫描 D 盘资源库,多平台字段完成统一映射', status: '完成' },
- { title: '执行规则与评分', detail: '数量、价格、风格、风险、供应商关系进入综合评分', status: '进行中' },
- { title: '后台实时采样', detail: '需要登录平台采集近10篇内容时进入异步任务队列', status: '待执行' }
- ];
- samplingTasks: SamplingTask[] = [
- { account: '梨涡测评室', platform: '小红书', progress: 100, status: '完成', result: '9/10 篇符合真实测评或成分拆解' },
- { account: '通勤包里有什么', platform: '小红书', progress: 100, status: '完成', result: '8/10 篇符合通勤生活场景' },
- { account: '林小林认真护肤', platform: '抖音', progress: 70, status: '进行中', result: '已采样 7 条视频,风格暂定 82%' },
- { account: '美妆情报局', platform: '微信', progress: 40, status: '待执行', result: '等待公众号图文采样' }
- ];
- learnedRules: LearnedRule[] = [
- {
- title: '客户反馈中的明确偏好要先转成排除和加分项',
- description: '当反馈出现“减少/不要/优先/保留”等词时,提炼为可确认规则,不直接静默生效。',
- confidence: 91,
- action: '生成待确认规则',
- approved: false
- }
- ];
- gapItems: GapItem[] = [
- { title: '需求点需要可编辑和可追溯', current: '原 Demo 只有勾选,缺少原文证据和修正入口。', suggestion: '补充编辑状态、证据来源、手动新增需求。', done: true },
- { title: '资源库需要展示字段映射', current: '只展示各平台数量,客户看不到本地表如何被 AI 理解。', suggestion: '增加字段映射确认、资源目录检测和同步动作。', done: true },
- { title: '风格比对需要有过程', current: '只展示结果分,缺少近10篇内容采样的可解释过程。', suggestion: '增加后台采样队列、样本标题、风格命中率。', done: true },
- { title: '推荐名单需要二次筛选', current: '只展示卡片,缺少平台/状态/预算过滤和账号详情。', suggestion: '增加筛选、排序、详情侧栏、状态调整。', done: true },
- { title: '自我提升需要审核机制', current: '反馈提炼后直接加入规则,容易让客户担心 AI 乱学。', suggestion: '保留“待确认-入库”的人工审核链路。', done: true }
- ];
- taskRecords: TaskRecord[] = [
- { id: 'TH-202605-001', brand: '客户A / 新锐护肤品牌', status: '提号中', owner: '商务-王一', version: '名单 v0.3', lastAction: '已生成候选池,等待商务复核' },
- { id: 'TH-202604-028', brand: '客户B / 食饮新品', status: '已交付', owner: '商务-李想', version: '名单 v1.2', lastAction: '客户反馈已提炼 2 条规则' },
- { id: 'TH-202604-019', brand: '客户C / 家清系列', status: '待确认', owner: '商务-陈晨', version: '需求 v0.1', lastAction: '等待确认平台数量' },
- { id: 'TH-202604-033', brand: '客户D / 母婴洗护', status: '待复盘', owner: '商务-周周', version: '反馈 v0.2', lastAction: '客户要求减少硬广达人,规则待确认入库' },
- { id: 'TH-202605-006', brand: '客户E / 家居香氛', status: '资源同步', owner: '商务-林夕', version: '资源 v0.1', lastAction: '正在映射小红书和微信资源字段' },
- { id: 'TH-202605-009', brand: '客户F / 数码配件', status: '待采样', owner: '商务-阿树', version: '名单 v0.1', lastAction: 'B站长视频账号等待近10条内容采样' },
- { id: 'TH-202605-012', brand: '客户G / 轻食新品', status: '已交付', owner: '商务-小满', version: '名单 v1.0', lastAction: '客户版名单已导出,等待投放结果回填' }
- ];
- taskTimeline: TaskTimelineItem[] = [
- {
- taskId: 'TH-202605-001',
- time: '2026-05-12 09:18',
- title: 'Brief 上传并解析',
- detail: '识别客户、品牌、平台数量、预算和内容风格。'
- },
- {
- taskId: 'TH-202605-001',
- time: '2026-05-12 09:31',
- title: '生成候选池',
- detail: '从资源库召回 324 个账号,去重后 293 个进入评分池。'
- },
- {
- taskId: 'TH-202605-001',
- time: '2026-05-12 10:05',
- title: '媒介复核',
- detail: '剔除硬广风险账号 4 个,补入 B站专业背书账号。'
- },
- {
- taskId: 'TH-202604-028',
- time: '2026-04-28 16:40',
- title: '客户反馈回收',
- detail: '客户要求减少泛美食资讯号,已提炼 2 条行业偏好规则。'
- },
- {
- taskId: 'TH-202605-006',
- time: '2026-05-12 08:55',
- title: '资源同步',
- detail: '正在映射小红书和微信资源字段,等待供应商联系人补齐。'
- }
- ];
- templateFields: TemplateField[] = [
- { label: '平台', source: '资源库/Brief', customer: true, internal: true, required: true, selected: true },
- { label: '账号昵称', source: '资源库', customer: true, internal: true, required: true, selected: true },
- { label: '主页链接', source: '资源库', customer: true, internal: true, required: true, selected: true },
- { label: '粉丝量', source: '资源库', customer: true, internal: true, required: true, selected: true },
- { label: '图文/视频报价', source: '资源库', customer: true, internal: true, required: true, selected: true },
- { label: '推荐理由', source: 'AI生成+人工复核', customer: true, internal: true, required: true, selected: true },
- { label: '近10条样本', source: '后台采样', customer: true, internal: true, required: false, selected: true },
- { label: '供应商/联系人', source: '资源库', customer: false, internal: true, required: true, selected: true },
- { label: '风险提示', source: '规则库', customer: false, internal: true, required: false, selected: true },
- { label: '客户采纳结果', source: '复盘回填', customer: false, internal: true, required: false, selected: false }
- ];
- get selectedRulesCount(): number {
- return this.rules.filter((rule) => rule.selected).length;
- }
- get selectedCandidatesCount(): number {
- return this.filteredCandidates.filter((candidate) => candidate.selected).length;
- }
- get visibleNavItems(): NavItem[] {
- if (!this.simpleMode) {
- return this.navItems;
- }
- const simpleKeys = new Set<ViewKey>([...this.simpleNavKeys, this.activeView]);
- return this.navItems.filter((item) => simpleKeys.has(item.key));
- }
- get activeNavItem(): NavItem {
- return this.navItems.find((item) => item.key === this.activeView) ?? this.navItems[0];
- }
- get taskDisplayName(): string {
- return '客户A / 新锐护肤品牌种草合作';
- }
- get deliveryStatusLabel(): string {
- if (this.currentStep < 4) {
- return '名单待生成';
- }
- if (this.currentStep === 4) {
- return '推荐生成中';
- }
- return `交付准备度 ${this.exportReadiness}%`;
- }
- get nextAction(): { title: string; detail: string } {
- if (this.currentStep <= 1) {
- return { title: '解析 Brief', detail: '识别客户、平台、数量、预算、内容风格和排除项。' };
- }
- if (this.currentStep === 2) {
- return { title: '确认需求', detail: '保留已勾选需求,进入规则选择和提号。' };
- }
- if (this.currentStep === 3) {
- return { title: '生成推荐池', detail: '按已选规则从资源库召回账号,进入采样和评分。' };
- }
- if (this.currentStep === 4 && this.samplingTasks.some((task) => task.status !== '完成')) {
- return { title: '完成近10条采样', detail: '把风格比对证据补齐,降低客户返工风险。' };
- }
- if (this.currentStep === 4) {
- return { title: '查看推荐名单', detail: '进入账号复核和最近10条内容证据。' };
- }
- if (this.currentStep === 5) {
- return { title: '进入客户版交付', detail: '隐藏内部风险字段,预览对客版本并准备导出。' };
- }
- return { title: '提炼反馈规则', detail: '把客户反馈生成待确认规则,人工确认后入库。' };
- }
- get currentStepGuide(): { objective: string; operator: string; output: string } {
- return this.stepGuides[this.currentStep - 1] ?? this.stepGuides[0];
- }
- get checklistDoneCount(): number {
- return this.businessChecklist.filter((item) => this.isChecklistDone(item.key)).length;
- }
- get selectedCandidateList(): Candidate[] {
- return this.candidates.filter((candidate) => candidate.selected && candidate.status !== '已剔除');
- }
- get reviewQueueCandidates(): Candidate[] {
- return this.candidates.filter((candidate) => candidate.status === '需复核' || candidate.status === '已剔除').slice(0, 5);
- }
- get creatorShortlistCandidates(): Candidate[] {
- return this.creatorFilteredCandidates.slice(0, 8);
- }
- get compareCandidates(): Candidate[] {
- return this.selectedCandidateList.slice(0, 4);
- }
- get activeScenarioCount(): number {
- return this.briefScenarios.filter((scenario) => scenario.selected).length;
- }
- get exportReadiness(): number {
- const mapping = (this.confirmedMappingsCount / this.fieldMappings.length) * 25;
- const rules = (this.selectedRulesCount / this.rules.length) * 25;
- const candidates = Math.min(1, this.selectedCandidateList.length / 4) * 25;
- const sampling = (this.samplingTasks.filter((task) => task.status === '完成').length / this.samplingTasks.length) * 25;
- return Math.round(mapping + rules + candidates + sampling);
- }
- get strongRecommendationCount(): number {
- return this.candidates.filter((candidate) => candidate.status === '强推荐').length;
- }
- get totalMatchedCandidates(): number {
- return this.resources.reduce((sum, resource) => sum + resource.matched, 0);
- }
- get workspacePipelineStages(): AiSelectionStage[] {
- return this.aiSelectionStages.slice(1, 6);
- }
- get platforms(): string[] {
- return ['全部', ...Array.from(new Set(this.candidates.map((candidate) => candidate.platform)))];
- }
- get categories(): string[] {
- return ['全部', ...Array.from(new Set(this.candidates.map((candidate) => candidate.category ?? candidate.tags[0] ?? '未分类')))];
- }
- get contentForms(): string[] {
- const forms = this.candidates.flatMap((candidate) => candidate.contentForms ?? [candidate.platform === 'B站' ? '长视频' : '图文']);
- return ['全部', ...Array.from(new Set(forms))];
- }
- get fanTiers(): string[] {
- return ['全部', '1万以下', '1万-10万', '10万-50万', '50万以上'];
- }
- get creatorFilteredCandidates(): Candidate[] {
- const queryTokens = this.creatorQuery
- .split(/\s+/)
- .map((token) => token.trim())
- .filter(Boolean);
- return this.candidates
- .filter((candidate) => this.creatorPlatform === '全部' || candidate.platform === this.creatorPlatform)
- .filter((candidate) => this.creatorCategory === '全部' || candidate.category === this.creatorCategory || candidate.tags.includes(this.creatorCategory))
- .filter((candidate) => this.creatorContentForm === '全部' || (candidate.contentForms ?? []).includes(this.creatorContentForm))
- .filter((candidate) => this.creatorFanTier === '全部' || (candidate.fanTier ?? inferFanTier(candidate.fans)) === this.creatorFanTier)
- .filter((candidate) => candidate.price <= this.creatorBudgetMax)
- .filter((candidate) => {
- if (this.creatorRisk === '只看安全') {
- return candidate.status !== '需复核' && candidate.status !== '已剔除';
- }
- if (this.creatorRisk === '只看需复核') {
- return candidate.status === '需复核';
- }
- return true;
- })
- .filter((candidate) => {
- if (queryTokens.length === 0) {
- return true;
- }
- const haystack = [
- candidate.name,
- candidate.platform,
- candidate.category ?? '',
- candidate.reason,
- candidate.risk,
- candidate.audienceProfile ?? '',
- ...(candidate.tags ?? []),
- ...(candidate.recallSources ?? [])
- ].join(' ');
- return queryTokens.some((token) => haystack.includes(token));
- })
- .sort((a, b) => b.score - a.score);
- }
- get creatorAiSummary(): string {
- const count = this.creatorFilteredCandidates.length;
- const strong = this.creatorFilteredCandidates.filter((candidate) => candidate.status === '强推荐').length;
- const review = this.creatorFilteredCandidates.filter((candidate) => candidate.status === '需复核').length;
- return `命中 ${count} 个博主,其中强推荐 ${strong} 个、需复核 ${review} 个;AI 会优先保留风格匹配高、报价健康、服务履约稳定的账号。`;
- }
- get deliveryChecklistDoneCount(): number {
- return this.deliveryChecklist.filter((item) => item.done).length;
- }
- get selectedDeliveryFields(): string[] {
- return this.deliveryFields
- .filter((field) => field.selected)
- .filter((field) => this.previewAudience === '内部复核版' || field.customer)
- .map((field) => field.label);
- }
- get deliveryGaps(): string[] {
- const gaps: string[] = [];
- if (this.confirmedMappingsCount < this.fieldMappings.length) {
- gaps.push('还有字段映射未确认');
- }
- if (this.samplingTasks.some((task) => task.status !== '完成')) {
- gaps.push('近10条内容采样未全部完成');
- }
- if (this.selectedCandidateList.some((candidate) => candidate.status === '需复核')) {
- gaps.push('名单里仍有需复核账号');
- }
- if (this.selectedCandidateList.length < 8) {
- gaps.push('客户版主推账号数量偏少');
- }
- return gaps;
- }
- get resourceFixQueue(): ResourceIssue[] {
- return this.resourceIssues.filter((issue) => !issue.resolved).slice(0, 4);
- }
- get taskAttentionItems(): Array<{ title: string; detail: string; action: string; view: ViewKey }> {
- return [
- {
- title: '补齐微信主页链接',
- detail: '影响近10条内容采样,建议先到资源库处理高风险字段。',
- action: '去资源库',
- view: 'resources'
- },
- {
- title: '确认需复核账号',
- detail: `${this.reviewQueueCandidates.length} 个账号仍有风险或剔除原因,需要业务确认。`,
- action: '打开名单',
- view: 'workspace'
- },
- {
- title: '客户反馈待学习',
- detail: '把本次驳回原因转成待确认规则,后续同类客户可复用。',
- action: '去学习',
- view: 'learning'
- }
- ];
- }
- get learningScopeEffect(): string {
- if (this.selectedLearningScope === '本任务') {
- return '只影响当前推荐名单,适合临时口径或单次活动偏好。';
- }
- if (this.selectedLearningScope === '单客户') {
- return '后续同一客户 Brief 会默认带入,但仍保留人工确认。';
- }
- if (this.selectedLearningScope === '行业') {
- return '同品类客户会获得弱加权,避免把单个客户口味过度放大。';
- }
- return '进入全局规则库前需要二次确认,命中后会影响所有新任务。';
- }
- get filteredCandidates(): Candidate[] {
- return this.candidates
- .filter((candidate) => this.selectedPlatform === '全部' || candidate.platform === this.selectedPlatform)
- .filter((candidate) => this.selectedStatus === '全部' || candidate.status === this.selectedStatus)
- .filter((candidate) => candidate.price <= this.budgetMax)
- .sort((a, b) => {
- if (this.sortMode === '风格匹配') {
- return b.styleMatch - a.styleMatch;
- }
- if (this.sortMode === '报价升序') {
- return a.price - b.price;
- }
- return b.score - a.score;
- });
- }
- get selectedCandidate(): Candidate {
- return this.candidates.find((candidate) => candidate.id === this.selectedCandidateId) ?? this.candidates[0];
- }
- get selectedNote(): NoteDetail {
- return (
- this.selectedCandidate.recentNotes.find((note) => note.id === this.selectedNoteId) ??
- this.selectedCandidate.recentNotes[0]
- );
- }
- get filteredNotes(): NoteDetail[] {
- return this.selectedCandidate.recentNotes.filter(
- (note) => this.noteVerdictFilter === '全部' || note.verdict === this.noteVerdictFilter
- );
- }
- get confirmedMappingsCount(): number {
- return this.fieldMappings.filter((mapping) => mapping.confirmed).length;
- }
- get selectedResource(): ResourceSummary {
- return this.resources.find((resource) => resource.platform === this.selectedResourcePlatform) ?? this.resources[0];
- }
- get selectedResourceFiles(): ResourceFile[] {
- return this.resourceFiles.filter((file) => file.platform === this.selectedResourcePlatform);
- }
- get selectedResourceIssues(): ResourceIssue[] {
- return this.resourceIssues.filter((issue) => issue.platform === this.selectedResourcePlatform);
- }
- get selectedResourceCandidates(): Candidate[] {
- return this.candidates.filter((candidate) => candidate.platform === this.selectedResourcePlatform).slice(0, 6);
- }
- get selectedRule(): Rule {
- return this.rules.find((rule) => rule.id === this.selectedRuleId) ?? this.rules[0];
- }
- get enabledRuleTestCases(): RuleTestCase[] {
- return this.ruleTestCases.filter((item) => item.enabled);
- }
- get selectedTask(): TaskRecord {
- return this.taskRecords.find((task) => task.id === this.selectedTaskId) ?? this.taskRecords[0];
- }
- get selectedTaskTimeline(): TaskTimelineItem[] {
- const timeline = this.taskTimeline.filter((item) => item.taskId === this.selectedTaskId);
- if (timeline.length > 0) {
- return timeline;
- }
- return [
- {
- taskId: this.selectedTaskId,
- time: '待补充',
- title: '暂无详细时间线',
- detail: '正式版会记录 Brief、资源同步、规则调整、名单导出和客户反馈。'
- }
- ];
- }
- get selectedTemplate(): TemplateItem {
- return this.templates.find((template) => template.selected) ?? this.templates[0];
- }
- get visibleTemplateFields(): TemplateField[] {
- return this.templateFields.filter((field) => {
- if (this.templatePreviewMode === '客户版') {
- return field.customer && field.selected;
- }
- if (this.templatePreviewMode === '复盘版') {
- return field.selected || field.label.includes('采纳') || field.source.includes('复盘');
- }
- return field.internal && field.selected;
- });
- }
- isChecklistDone(key: string): boolean {
- if (key === 'brief') {
- return this.currentStep > 1;
- }
- if (key === 'demand') {
- return this.currentStep > 2 && this.requirements.some((item) => item.selected);
- }
- if (key === 'rule') {
- return this.currentStep > 3 && this.selectedRulesCount >= 3;
- }
- if (key === 'sample') {
- return this.currentStep > 4 && this.samplingTasks.every((task) => task.status === '完成');
- }
- if (key === 'export') {
- return this.currentStep >= 5 && (this.exportReadiness >= 80 || this.exportState.includes('导出'));
- }
- return false;
- }
- toggleSimpleMode(): void {
- this.simpleMode = !this.simpleMode;
- if (this.simpleMode && !this.simpleNavKeys.includes(this.activeView)) {
- this.activeView = 'workspace';
- }
- this.appendLog('视图模式', this.simpleMode ? '已切换为商务简洁模式。' : '已切换为完整业务模式。');
- }
- toggleAdvancedWorkspace(): void {
- this.showAdvancedWorkspace = !this.showAdvancedWorkspace;
- this.appendLog('工作台细节', this.showAdvancedWorkspace ? '已展开规则、采样和试算细节。' : '已收起高级业务细节。');
- }
- backToWorkflow(): void {
- this.activeView = 'workspace';
- this.appendLog('导航', `已返回提号流程 Step ${this.currentStep}。`);
- }
- runNextAction(): void {
- if (this.currentStep <= 1) {
- this.analyzeBrief();
- this.appendLog('下一步', '已解析 Brief。');
- return;
- }
- if (this.currentStep === 2) {
- this.confirmDemand();
- this.appendLog('下一步', '已确认需求。');
- return;
- }
- if (this.currentStep === 3) {
- this.generateRecommendations();
- this.appendLog('下一步', '已生成推荐名单。');
- return;
- }
- if (this.currentStep === 4 && this.samplingTasks.some((task) => task.status !== '完成')) {
- this.runSampling();
- this.currentStep = 5;
- this.updateSteps();
- this.appendLog('下一步', '已完成近10条内容采样。');
- return;
- }
- if (this.currentStep === 4) {
- this.currentStep = 5;
- this.updateSteps();
- this.appendLog('下一步', '已进入推荐名单复核。');
- return;
- }
- if (this.currentStep === 5) {
- this.previewAudience = '客户版';
- this.activeView = 'delivery';
- this.updateSteps();
- this.appendLog('下一步', '已进入客户版交付预览。');
- return;
- }
- this.extractRules();
- this.appendLog('下一步', '已从反馈中提炼待确认规则。');
- }
- runBusinessShortcut(id: string): void {
- if (id === 'fast-list') {
- this.analyzeBrief();
- this.confirmDemand();
- this.generateRecommendations();
- this.runSampling();
- this.currentStep = 5;
- this.previewAudience = '客户版';
- this.exportState = '已生成可讨论名单,等待客户版预览';
- this.showAdvancedWorkspace = false;
- this.updateSteps();
- this.appendLog('快捷操作', '已一键生成可看名单。');
- return;
- }
- if (id === 'review-risk') {
- this.selectedStatus = '需复核';
- this.selectedResourcePlatform = '微信';
- this.activeView = 'resources';
- this.appendLog('快捷操作', '已切到待处理问题和需复核账号。');
- return;
- }
- if (id === 'client-preview') {
- this.previewAudience = '客户版';
- this.templatePreviewMode = '客户版';
- this.currentStep = 5;
- this.activeView = 'delivery';
- this.updateSteps();
- this.appendLog('快捷操作', '已进入客户版名单预览。');
- return;
- }
- if (id === 'learn-feedback') {
- this.extractRules();
- this.activeView = 'learning';
- this.appendLog('快捷操作', '已进入反馈学习并提炼规则。');
- }
- }
- addVisibleCreatorsToPool(): void {
- const visibleIds = new Set(this.creatorShortlistCandidates.map((candidate) => candidate.id));
- this.candidates = this.candidates.map((candidate) =>
- visibleIds.has(candidate.id)
- ? {
- ...candidate,
- selected: true,
- status: candidate.status === '已剔除' ? '备选' : candidate.status
- }
- : candidate
- );
- this.exportState = `已将 ${visibleIds.size} 个当前筛选博主加入候选池`;
- this.appendLog('找博主', `已批量加入 ${visibleIds.size} 个当前筛选账号。`);
- }
- openRecommendationReview(candidate?: Candidate): void {
- if (candidate) {
- this.openCandidate(candidate);
- }
- this.currentStep = 5;
- this.activeView = 'workspace';
- this.updateSteps();
- this.appendLog('名单复核', candidate ? `已打开 ${candidate.name} 的内容证据。` : '已打开推荐名单复核视图。');
- }
- toggleDeliveryChecklist(item: { done: boolean; label: string }): void {
- item.done = !item.done;
- this.appendLog('交付检查', `${item.label} 已标记为${item.done ? '完成' : '待处理'}。`);
- }
- setLearningScope(scope: '本任务' | '单客户' | '行业' | '长期规则'): void {
- this.selectedLearningScope = scope;
- this.appendLog('学习范围', `反馈规则生效范围已设为${scope}。`);
- }
- uploadTemplateMock(): void {
- this.templateUploadState = '已上传《客户A-推荐名单字段模板.xlsx》,字段预览已刷新';
- this.templates = [
- {
- name: '客户A-推荐名单字段模板.xlsx',
- type: '推荐表模板',
- useCase: '客户A 专属字段顺序和对客话术',
- selected: true
- },
- ...this.templates.map((template) => ({ ...template, selected: false }))
- ];
- this.appendLog('模板上传', '已模拟上传客户A专属推荐表模板。');
- }
- useCreatorPrompt(prompt: string): void {
- this.creatorQuery = prompt;
- this.appendLog('找博主', `已按提示词更新搜索:${prompt}`);
- }
- setCreatorFilters(platform: string, category = '全部'): void {
- this.creatorPlatform = platform;
- this.creatorCategory = category;
- this.appendLog('找博主筛选', `平台 ${platform},类目 ${category}。`);
- }
- addCreatorToPool(candidate: Candidate): void {
- candidate.selected = true;
- candidate.status = candidate.status === '已剔除' ? '备选' : candidate.status;
- this.selectedCandidateId = candidate.id;
- this.selectedNoteId = candidate.recentNotes[0]?.id ?? '';
- this.exportState = `${candidate.name} 已加入候选池`;
- this.appendLog('候选池', `${candidate.name} 从找博主页加入本次名单。`);
- }
- toggleScenario(scenario: BriefScenario): void {
- scenario.selected = !scenario.selected;
- this.recalculateScenario();
- this.appendLog('情景试算', `${scenario.label}:${scenario.selected ? '已启用' : '已关闭'}。`);
- }
- selectVisibleCandidates(): void {
- const visibleIds = new Set(this.filteredCandidates.map((candidate) => candidate.id));
- this.candidates = this.candidates.map((candidate) => ({
- ...candidate,
- selected: visibleIds.has(candidate.id) ? true : candidate.selected
- }));
- this.appendLog('名单筛选', `已勾选当前筛选下的 ${visibleIds.size} 个账号。`);
- }
- clearSelectedCandidates(): void {
- this.candidates = this.candidates.map((candidate) => ({ ...candidate, selected: false }));
- this.appendLog('名单筛选', '已清空当前推荐名单勾选。');
- }
- bulkStatus(status: CandidateStatus): void {
- const count = this.selectedCandidateList.length;
- this.candidates = this.candidates.map((candidate) =>
- candidate.selected ? { ...candidate, status, selected: status !== '已剔除' } : candidate
- );
- this.appendLog('批量复核', `已将 ${count} 个勾选账号标记为${status}。`);
- }
- toggleCompare(): void {
- this.compareOpen = !this.compareOpen;
- this.appendLog('对比视图', this.compareOpen ? '已打开账号横向对比。' : '已收起账号横向对比。');
- }
- switchPreviewAudience(): void {
- this.previewAudience = this.previewAudience === '客户版' ? '内部复核版' : '客户版';
- this.appendLog('预览模式', `已切换为${this.previewAudience}。`);
- }
- setPreviewAudience(audience: '客户版' | '内部复核版'): void {
- if (this.previewAudience === audience) {
- return;
- }
- this.previewAudience = audience;
- this.appendLog('预览模式', `已切换为${this.previewAudience}。`);
- }
- setNoteVerdictFilter(verdict: NoteVerdict | '全部'): void {
- this.noteVerdictFilter = verdict;
- const nextNote = this.filteredNotes[0] ?? this.selectedCandidate.recentNotes[0];
- this.selectedNoteId = nextNote.id;
- }
- markNote(note: NoteDetail, verdict: NoteVerdict): void {
- note.verdict = verdict;
- if (verdict === '正向样本') {
- note.match = Math.min(99, note.match + 3);
- this.selectedCandidate.styleMatch = Math.min(99, this.selectedCandidate.styleMatch + 1);
- }
- if (verdict === '负向样本') {
- note.match = Math.max(45, note.match - 5);
- this.selectedCandidate.styleMatch = Math.max(50, this.selectedCandidate.styleMatch - 2);
- this.selectedCandidate.status = '需复核';
- }
- this.appendLog('笔记标注', `${this.selectedCandidate.name}《${note.title}》被标记为${verdict}。`);
- }
- addCandidateReview(text: string): void {
- this.feedbackText = `${this.feedbackText}\n商务复核:${this.selectedCandidate.name} - ${text}`;
- this.appendLog('复核意见', `${this.selectedCandidate.name}:${text}`);
- }
- appendFeedbackPreset(text: string): void {
- this.feedbackText = `${this.feedbackText}\n${text}`;
- this.appendLog('反馈补充', text);
- }
- selectResourcePlatform(platform: string): void {
- this.selectedResourcePlatform = platform;
- this.appendLog('资源库', `已查看 ${platform} 资源详情。`);
- }
- rescanSelectedResource(): void {
- this.resources = this.resources.map((resource) =>
- resource.platform === this.selectedResourcePlatform
- ? {
- ...resource,
- fresh: Math.min(99, resource.fresh + 6),
- matched: resource.matched + 9,
- status: '刚刚同步'
- }
- : resource
- );
- this.resourceHealthItems = this.resourceHealthItems.map((item, index) => ({
- ...item,
- value: Math.min(98, item.value + (index === 1 ? 5 : 3))
- }));
- this.appendLog('资源同步', `${this.selectedResourcePlatform} 已重新扫描,匹配数和字段完整率已更新。`);
- }
- resolveResourceIssue(issue: ResourceIssue): void {
- issue.resolved = true;
- this.appendLog('资源问题', `${issue.platform}:${issue.title} 已标记处理。`);
- }
- confirmMapping(mapping: FieldMapping): void {
- mapping.confirmed = true;
- mapping.confidence = Math.min(99, mapping.confidence + 4);
- this.appendLog('字段映射', `${mapping.source} -> ${mapping.target} 已确认。`);
- }
- selectRule(rule: Rule): void {
- this.selectedRuleId = rule.id;
- this.appendLog('规则沙盘', `已选中 ${rule.title}。`);
- }
- toggleRuleTestCase(item: RuleTestCase): void {
- item.enabled = !item.enabled;
- }
- runRuleSandbox(): void {
- const enabledCount = this.enabledRuleTestCases.length;
- const rule = this.selectedRule;
- const impact = Math.round((this.ruleSandboxWeight / 100) * (enabledCount + 1) * 3);
- this.ruleSimulationState = `${rule.title} 试算完成:预计强推荐 +${Math.max(1, impact)},需复核 -${Math.max(1, enabledCount)}。`;
- this.candidates = this.candidates.map((candidate, index) =>
- index < 4
- ? {
- ...candidate,
- score: Math.min(99, candidate.score + Math.max(1, enabledCount)),
- ruleHits: Array.from(new Set([...candidate.ruleHits, rule.category]))
- }
- : candidate
- );
- this.appendLog('规则试算', this.ruleSimulationState);
- }
- selectTask(task: TaskRecord): void {
- this.selectedTaskId = task.id;
- this.appendLog('任务查看', `已打开 ${task.id}:${task.brand}。`);
- }
- copyTask(task: TaskRecord): void {
- const nextId = `TH-202605-${String(this.taskRecords.length + 20).padStart(3, '0')}`;
- const copied: TaskRecord = {
- ...task,
- id: nextId,
- status: '待确认',
- owner: '商务-王一',
- version: '需求 v0.1',
- lastAction: `从 ${task.id} 复制行业规则和模板,等待上传新 Brief`
- };
- this.taskRecords = [copied, ...this.taskRecords];
- this.selectedTaskId = nextId;
- this.taskTimeline = [
- {
- taskId: nextId,
- time: '刚刚',
- title: '复制为新任务',
- detail: `复用 ${task.brand} 的规则组合、导出模板和复盘偏好。`
- },
- ...this.taskTimeline
- ];
- this.appendLog('任务复制', `${task.id} 已复制为 ${nextId}。`);
- }
- sendTaskFeedbackToLearning(): void {
- const text = `${this.selectedTask.brand}:${this.selectedTask.lastAction}`;
- this.feedbackText = `${this.feedbackText}\n任务复盘:${text}`;
- this.activeView = 'learning';
- this.appendLog('任务复盘', `${this.selectedTask.id} 的最近动作已加入反馈学习。`);
- }
- setTemplatePreviewMode(mode: '客户版' | '内部复核版' | '复盘版'): void {
- this.templatePreviewMode = mode;
- this.appendLog('模板预览', `已切换为${mode}。`);
- }
- toggleTemplateField(field: TemplateField): void {
- if (field.required) {
- field.selected = true;
- return;
- }
- field.selected = !field.selected;
- }
- applyTemplateFieldsToDelivery(): void {
- const active = new Set(this.templateFields.filter((field) => field.selected).map((field) => field.label));
- this.deliveryFields = this.deliveryFields.map((field) => ({
- ...field,
- selected: active.has(field.label) || field.selected
- }));
- this.exportState = `已应用 ${this.visibleTemplateFields.length} 个模板字段到导出配置`;
- this.appendLog('模板应用', `${this.selectedTemplate.name} 的字段配置已同步到名单交付。`);
- }
- setView(view: ViewKey): void {
- this.activeView = view;
- }
- goStep(step: number): void {
- this.currentStep = step;
- this.updateSteps();
- }
- onFileSelected(event: Event): void {
- const input = event.target as HTMLInputElement;
- const file = input.files?.[0];
- if (!file) {
- return;
- }
- this.uploadedFileName = file.name;
- this.currentStep = 1;
- this.exportState = 'Brief 已上传,等待解析';
- this.updateSteps();
- }
- analyzeBrief(): void {
- this.currentStep = 2;
- this.requirements = this.requirements.map((item, index) => ({
- ...item,
- confidence: Math.min(99, item.confidence + (index % 2 === 0 ? 2 : 1))
- }));
- this.activities[0] = { ...this.activities[0], status: '完成' };
- this.exportState = '已抽取 6 个关键需求点';
- this.updateSteps();
- }
- confirmDemand(): void {
- this.currentStep = 3;
- this.exportState = '需求已确认,等待规则选择';
- this.updateSteps();
- }
- toggleRequirementEdit(item: Requirement): void {
- item.editable = !item.editable;
- }
- addRequirement(): void {
- this.requirements = [
- ...this.requirements,
- {
- label: '临时补充',
- value: '优先选择历史合作中客户反馈好、执行配合度高的账号',
- confidence: 100,
- selected: true,
- editable: true,
- evidence: '商务手动补充'
- }
- ];
- }
- addTemporaryRule(): void {
- const next = String(this.rules.length + 1).padStart(3, '0');
- this.rules = [
- ...this.rules,
- {
- id: `R-${next}`,
- title: this.tempRuleTitle,
- description: '本次任务临时生效,可在复盘后保存为长期规则。',
- category: '临时策略',
- source: '临时补充',
- selected: true,
- impact: this.tempRuleImpact
- }
- ];
- this.exportState = '临时规则已加入本次提号';
- }
- runResourceSync(): void {
- this.resources = this.resources.map((resource, index) => ({
- ...resource,
- fresh: Math.min(96, resource.fresh + 4 + index),
- matched: resource.matched + 6 + index
- }));
- this.fieldMappings = this.fieldMappings.map((mapping, index) => ({
- ...mapping,
- confidence: Math.min(99, mapping.confidence + 3),
- confirmed: index < 4 ? true : mapping.confirmed
- }));
- this.activities[1] = { ...this.activities[1], status: '完成' };
- this.exportState = '资源库已重新同步并更新字段映射';
- }
- confirmAllMappings(): void {
- this.fieldMappings = this.fieldMappings.map((mapping) => ({ ...mapping, confirmed: true }));
- }
- generateRecommendations(): void {
- this.currentStep = 4;
- this.exportState = `已生成 ${this.totalMatchedCandidates} 个候选,当前展示 Top ${Math.min(12, this.candidates.length)}`;
- this.activities = this.activities.map((activity, index) => ({
- ...activity,
- status: index < 3 ? '完成' : '进行中'
- }));
- this.candidates = this.candidates.map((candidate, index) => ({
- ...candidate,
- score: Math.min(97, candidate.score + (index < 2 ? 2 : 0)),
- selected: index < 4,
- status: candidate.status === '已剔除' ? '备选' : candidate.status
- }));
- this.updateSteps();
- }
- runSampling(): void {
- this.currentStep = 4;
- this.samplingTasks = this.samplingTasks.map((task) => ({
- ...task,
- progress: 100,
- status: '完成',
- result: task.result.includes('等待') ? '已完成近10篇内容采样,等待商务复核' : task.result
- }));
- this.activities[3] = { ...this.activities[3], status: '完成' };
- this.candidates = this.candidates.map((candidate) => ({
- ...candidate,
- styleMatch: candidate.status === '需复核' ? Math.min(79, candidate.styleMatch + 4) : Math.min(96, candidate.styleMatch + 2)
- }));
- this.exportState = '近10篇内容采样已完成';
- this.updateSteps();
- }
- toggleRule(rule: Rule): void {
- rule.selected = !rule.selected;
- }
- toggleCandidate(candidate: Candidate): void {
- candidate.selected = !candidate.selected;
- }
- openCandidate(candidate: Candidate): void {
- this.selectedCandidateId = candidate.id;
- this.selectedNoteId = candidate.recentNotes[0]?.id ?? '';
- }
- selectNote(note: NoteDetail): void {
- this.selectedNoteId = note.id;
- }
- updateCandidateStatus(candidate: Candidate, status: CandidateStatus): void {
- candidate.status = status;
- candidate.selected = status !== '已剔除';
- }
- rotateCandidate(): void {
- this.candidates = this.candidates.map((candidate, index) => {
- if (candidate.status === '需复核') {
- return { ...candidate, status: '备选', score: candidate.score + 5, selected: true };
- }
- return index === 0 ? { ...candidate, score: Math.min(99, candidate.score + 1) } : candidate;
- });
- this.exportState = '已按当前规则换一批并补足备选';
- }
- extractRules(): void {
- const suggestions: LearnedRule[] = [
- {
- title: '小红书减少泛美妆号,优先生活方式和通勤场景',
- description: '当 Brief 或反馈中出现“真实测评、通勤、生活方式”时,生活方式标签加权 +12,泛美妆标签降权 -10。',
- confidence: 93,
- action: '建议加入长期规则',
- approved: false
- },
- {
- title: '抖音预算压低时保留爆文率高账号',
- description: '抖音预算下调时,先按报价降序剔除,但爆文率、完播率进入保护分。',
- confidence: 87,
- action: '建议加入预算策略',
- approved: false
- },
- {
- title: '客户明确不要硬广时,营销资讯类账号自动进入复核池',
- description: '当反馈出现“硬广、资讯感、种草不自然”等表述时,资讯类账号风格分扣 8 分。',
- confidence: 82,
- action: '建议加入风格风控',
- approved: false
- }
- ];
- this.learnedRules = suggestions;
- this.currentStep = 6;
- this.updateSteps();
- }
- approveLearnedRule(item: LearnedRule): void {
- item.approved = true;
- this.rules = [
- ...this.rules,
- {
- id: `R-${String(this.rules.length + 1).padStart(3, '0')}`,
- title: item.title,
- description: item.description,
- category: '反馈学习',
- source: 'AI提炼',
- selected: true,
- impact: `置信度 ${item.confidence}%`
- }
- ];
- this.exportState = '反馈规则已确认入库';
- }
- rejectLearnedRule(item: LearnedRule): void {
- this.learnedRules = this.learnedRules.filter((rule) => rule.title !== item.title);
- }
- selectTemplate(template: TemplateItem): void {
- this.templates = this.templates.map((item) => ({
- ...item,
- selected: item.name === template.name
- }));
- this.exportState = `已选择模板:${template.name}`;
- }
- async exportRecommendations(): Promise<void> {
- this.currentStep = 5;
- this.updateSteps();
- const selected = this.candidates.filter((candidate) => candidate.selected && candidate.status !== '已剔除');
- try {
- const response = await fetch(`${getApiBaseUrl()}/api/recommendations/export`, {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({ candidates: selected })
- });
- if (!response.ok) {
- throw new Error('API unavailable');
- }
- const blob = await response.blob();
- this.triggerDownload(blob, '客户A-新锐护肤品牌-需求推荐名单.xls');
- this.apiState = 'Node API 已生成导出文件';
- this.exportState = '推荐名单已导出';
- return;
- } catch {
- const html = this.buildExcelHtml(selected);
- const blob = new Blob([html], { type: 'application/vnd.ms-excel;charset=utf-8' });
- this.triggerDownload(blob, '客户A-新锐护肤品牌-需求推荐名单.xls');
- this.apiState = '前端兜底生成导出文件';
- this.exportState = '推荐名单已导出';
- }
- }
- private recalculateScenario(): void {
- const activeIds = new Set(this.briefScenarios.filter((scenario) => scenario.selected).map((scenario) => scenario.id));
- this.candidates = this.candidates.map((candidate) => {
- const base = this.candidateBaseline.get(candidate.id);
- if (!base) {
- return candidate;
- }
- let score = base.score;
- let styleMatch = base.styleMatch;
- let status = base.status;
- const ruleHits = [...base.ruleHits];
- let risk = base.risk;
- let reason = base.reason;
- let selected = candidate.selected;
- if (activeIds.has('budget-down')) {
- if (candidate.price > 12000) {
- score -= 6;
- status = '备选';
- risk = `${base.risk} 客户压预算后需谈价或替换。`;
- } else if (candidate.price <= 7000) {
- score += 4;
- ruleHits.push('预算压缩保护');
- }
- }
- if (activeIds.has('life-style')) {
- const lifestyleHit = candidate.tags.some((tag) => ['生活方式', '通勤', '真实测评'].includes(tag));
- if (lifestyleHit) {
- score += 5;
- styleMatch += 4;
- ruleHits.push('生活方式加权');
- } else if (candidate.platform === '微信') {
- score -= 5;
- status = '需复核';
- risk = '客户偏生活方式后,资讯号需人工复核。';
- }
- }
- if (activeIds.has('strict-style')) {
- if (styleMatch < 80) {
- score -= 8;
- status = '需复核';
- ruleHits.push('硬广风格复核');
- } else {
- score += 2;
- }
- }
- if (activeIds.has('keep-bilibili') && candidate.platform === 'B站') {
- score += 5;
- selected = true;
- status = status === '已剔除' ? '备选' : status;
- reason = `${base.reason} 当前情景要求保留专业背书账号。`;
- ruleHits.push('B站背书保留');
- }
- return {
- ...candidate,
- score: Math.max(50, Math.min(99, score)),
- styleMatch: Math.max(45, Math.min(99, styleMatch)),
- status,
- selected,
- risk,
- reason,
- ruleHits: Array.from(new Set(ruleHits))
- };
- });
- this.exportState =
- this.activeScenarioCount > 0
- ? `已按 ${this.activeScenarioCount} 个客户情景试算名单`
- : '已恢复默认提号策略';
- }
- private appendLog(action: string, detail: string): void {
- const now = new Date();
- const time = `${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}`;
- this.interactionLogs = [{ time, action, detail }, ...this.interactionLogs].slice(0, 6);
- }
- private updateSteps(): void {
- this.steps = this.steps.map((step, index) => {
- const position = index + 1;
- if (position < this.currentStep) {
- return { ...step, status: 'done' };
- }
- if (position === this.currentStep) {
- return { ...step, status: 'active' };
- }
- return { ...step, status: 'todo' };
- });
- }
- private triggerDownload(blob: Blob, filename: string): void {
- const url = URL.createObjectURL(blob);
- const link = document.createElement('a');
- link.href = url;
- link.download = filename;
- link.click();
- URL.revokeObjectURL(url);
- }
- private buildExcelHtml(candidates: Candidate[]): string {
- const rows = candidates
- .map(
- (candidate) => `
- <tr>
- <td>${candidate.platform}</td>
- <td>${candidate.name}</td>
- <td>${candidate.fans}</td>
- <td>${candidate.price}</td>
- <td>${candidate.score}</td>
- <td>${candidate.styleMatch}%</td>
- <td>${candidate.status}</td>
- <td>${candidate.owner}</td>
- <td>${candidate.supplier}</td>
- <td>${candidate.reason}</td>
- <td>${candidate.risk}</td>
- </tr>`
- )
- .join('');
- return `
- <html>
- <head><meta charset="UTF-8"></head>
- <body>
- <table border="1">
- <tr style="background:#1f8a70;color:#ffffff;font-weight:bold">
- <th>平台</th><th>账号</th><th>粉丝量</th><th>报价</th><th>综合分</th><th>风格匹配</th><th>推荐状态</th><th>责任媒介</th><th>供应商</th><th>推荐理由</th><th>风险提示</th>
- </tr>
- ${rows}
- </table>
- </body>
- </html>`;
- }
- }
|