From 21569b181f1545b3ffafffec81b156396940f793 Mon Sep 17 00:00:00 2001 From: leiyuee Date: Fri, 17 Jul 2026 16:33:51 +0800 Subject: [PATCH 1/2] no message --- src/api/api.ts | 2 - src/components/subscribe-plan-list/index.scss | 225 +++++++++--------- src/components/subscribe-plan-list/index.tsx | 95 +++++--- src/locales/en-US.json | 7 +- src/locales/zh-CN.json | 7 +- src/locales/zh-TW.json | 7 +- 6 files changed, 192 insertions(+), 151 deletions(-) diff --git a/src/api/api.ts b/src/api/api.ts index b3e9062..87f5839 100644 --- a/src/api/api.ts +++ b/src/api/api.ts @@ -25,7 +25,6 @@ type ListApiResponse = { data?: ListApiData; }; export type ProductPlanCustomInfo = { - rightTags?: string; discount_info?: string; point_amount?: string; illustrator?: string; @@ -40,7 +39,6 @@ export type ProductPlan = ApiParams & { price_amount?: number | string; original_price_amount?: number | string; level?: number | string; - recommended?: boolean; coins?: number | string; pointsGrantMode?: string; custom_info?: ProductPlanCustomInfo; diff --git a/src/components/subscribe-plan-list/index.scss b/src/components/subscribe-plan-list/index.scss index 113e13c..0dbfb67 100644 --- a/src/components/subscribe-plan-list/index.scss +++ b/src/components/subscribe-plan-list/index.scss @@ -114,15 +114,15 @@ .plan-grid { display: grid; - grid-template-columns: repeat(4, 357px); + grid-template-columns: repeat(4, 313px); align-items: start; justify-content: center; - gap: 10px; + gap: 12px; width: 100%; } .plan_grid_item { - width: 357px; + width: 313px; min-width: 0; } @@ -131,6 +131,7 @@ flex-direction: column; width: 100%; min-width: 0; + gap: 0; } .plan_item__header { @@ -138,9 +139,9 @@ z-index: 2; display: flex; flex-direction: column; - gap: 10px; - min-height: 287px; - padding: 24px 20px; + gap: 16px; + min-height: 266px; + padding: 20px; border-radius: 26px; box-sizing: border-box; background: #ffffff; @@ -149,10 +150,10 @@ .plan_item__title-row { display: flex; - align-items: flex-start; + align-items: center; justify-content: space-between; gap: 8px; - min-height: 28px; + min-height: 20px; } .plan_item__title { @@ -160,17 +161,9 @@ align-items: center; min-width: 0; color: #333333; - font-size: 20px; - font-weight: 500; - line-height: 28px; - } - - .plan_item__crown { - flex: 0 0 auto; - width: 24px; - height: 24px; - margin-right: 8px; - display: block; + font-size: 16px; + font-weight: 700; + line-height: 20px; } .plan_item__title span { @@ -180,39 +173,27 @@ white-space: nowrap; } - .plan_item__title-badges { - display: flex; - flex: 0 0 auto; - flex-wrap: wrap; - justify-content: flex-end; - gap: 6px; - max-width: 124px; - } - - .plan_item__title-badge { + .plan_item__discount-badge { display: inline-flex; + flex: 0 0 auto; align-items: center; justify-content: center; - height: 28px; - padding: 0 15px; - border-radius: 26px; + min-width: 66px; + max-width: 96px; + height: 20px; + padding: 0 10px; + overflow: hidden; + border-radius: 100px; box-sizing: border-box; + background: #f3efff; + color: #6f47f5; font-size: 12px; - font-weight: 500; - line-height: 26px; + font-weight: 400; + line-height: 20px; + text-overflow: ellipsis; white-space: nowrap; } - .plan_item__title-badge_recommended { - color: #e55936; - background: #ffe3c3; - } - - .plan_item__title-badge_top { - color: #6f47f5; - background: #ebe5ff; - } - .plan_item__price-block { display: flex; flex-direction: column; @@ -224,7 +205,6 @@ display: flex; align-items: flex-start; min-height: 44px; - padding-right: 72px; box-sizing: border-box; } @@ -240,7 +220,7 @@ margin-left: 2px; color: #333333; font-size: 36px; - font-weight: 500; + font-weight: 700; line-height: 44px; } @@ -252,7 +232,7 @@ margin-top: 18px; margin-left: 4px; color: #666666; - font-size: 16px; + font-size: 12px; font-weight: 400; line-height: 24px; white-space: nowrap; @@ -263,35 +243,16 @@ margin-top: 18px; margin-left: 4px; color: #999999; - font-size: 16px; - font-weight: 500; + font-size: 12px; + font-weight: 400; line-height: 24px; text-decoration: line-through; white-space: nowrap; } - .plan_item__price-discount { - position: absolute; - top: 20px; - right: 0; - display: inline-flex; - align-items: center; - justify-content: center; - height: 20px; - padding: 0 10px; - border-radius: 9px; - box-sizing: border-box; - background: #ebe5ff; - color: #333333; - font-size: 12px; - font-weight: 500; - line-height: 20px; - white-space: nowrap; - } - .plan_item__price-subtitle { margin: 0; - color: #666666; + color: #999999; font-size: 12px; font-weight: 400; line-height: 24px; @@ -301,7 +262,7 @@ position: relative; display: flex; align-items: center; - width: 314px; + width: 273px; max-width: 100%; height: 49px; padding: 0 12px; @@ -378,7 +339,7 @@ } .plan_item__cta.arco-btn:not(.arco-btn-disabled) { - background: linear-gradient(90deg, #333333 0%, #6f47f5 100%) !important; + background: #333333 !important; } .plan_item__cta.arco-btn-disabled, @@ -392,7 +353,7 @@ .plan_tier_selector { width: 100%; - max-width: 314px; + max-width: 273px; height: 44px; margin: 0 auto; box-sizing: border-box; @@ -489,7 +450,7 @@ .plan_commission { position: relative; - width: 314px; + width: 273px; max-width: 100%; height: 35px; padding: 0; @@ -548,16 +509,75 @@ .plan_item__features { position: relative; z-index: 1; - min-height: 510px; - margin-top: -10px; - padding: 46px 20px 24px; + min-height: 1028px; + margin-top: -58px; + padding: 70px 20px 20px; border-radius: 26px; box-sizing: border-box; - background: linear-gradient(0deg, #ffffff 70%, #f4f4f5 100%); - box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); + background: #ffffff; + box-shadow: 0 0 5px rgba(0, 0, 0, 0.1); } - .plan_item__feature-list { + .plan_goal { + display: flex; + flex-direction: column; + align-items: flex-start; + width: 100%; + min-height: 102px; + padding: 20px; + border-radius: 26px; + box-sizing: border-box; + background: #f9f6ff; + } + + .plan_goal__title { + display: flex; + align-items: center; + gap: 5px; + max-width: 100%; + color: #6f47f5; + font-size: 14px; + font-weight: 700; + line-height: 28px; + } + + .plan_goal__icon { + display: block; + flex: 0 0 auto; + width: 15px; + height: 15px; + object-fit: contain; + } + + .plan_goal__title span { + overflow: hidden; + min-width: 0; + text-overflow: ellipsis; + white-space: nowrap; + } + + .plan_goal__desc { + display: flex; + flex-direction: column; + margin: 6px 0 0; + color: #333333; + font-size: 12px; + font-weight: 400; + line-height: 18px; + } + + .plan_description { + width: 100%; + } + + .plan_description__divider { + width: 100%; + height: 1px; + margin: 20px 0; + background: #eeeeee; + } + + .plan_description__list { margin: 0; padding: 0; list-style: none; @@ -567,8 +587,8 @@ display: flex; align-items: flex-start; min-width: 0; - margin: 0 0 10px; - color: #666666; + margin: 0; + color: #333333; font-size: 12px; font-weight: 400; line-height: 24px; @@ -582,10 +602,10 @@ .plan_feature.plan_feature_title { display: block; margin: 0 0 10px; - color: #666666; - font-size: 12px; - font-weight: 500; - line-height: 24px; + color: #333333; + font-size: 14px; + font-weight: 700; + line-height: 28px; } .plan_feature__check { @@ -600,15 +620,15 @@ .plan_feature__text { flex: 1 1 auto; min-width: 0; - color: #666666; + color: #333333; font-size: 12px; - font-weight: 500; + font-weight: 400; line-height: 24px; } .desc_highlight { color: #6f47f5; - font-weight: 500; + font-weight: 700; } } @@ -629,36 +649,13 @@ } .plan-grid { - grid-template-columns: repeat(4, 320px); + grid-template-columns: repeat(4, 313px); width: max-content; justify-content: flex-start; } .plan_grid_item { - width: 320px; - } - - .plan_item__header { - min-height: 287px; - } - - .plan_item__title-row { - flex-direction: column; - } - - .plan_item__title-badges { - max-width: 100%; - justify-content: flex-start; - } - - .plan_item__price { - padding-right: 0; - } - - .plan_item__price-discount { - position: static; - margin-top: 20px; - margin-left: 8px; + width: 313px; } } } @@ -786,4 +783,4 @@ width: 14px; height: 14px; object-fit: cover; -} \ No newline at end of file +} diff --git a/src/components/subscribe-plan-list/index.tsx b/src/components/subscribe-plan-list/index.tsx index f1ce412..64a65bc 100644 --- a/src/components/subscribe-plan-list/index.tsx +++ b/src/components/subscribe-plan-list/index.tsx @@ -38,10 +38,13 @@ type PlanGroup = { const PLAN_INLINE_TAG_RE = /<\/?(?:mark|title|section)>/gi; const HIGHLIGHT_CLASS_NAME = "desc_highlight"; const SUBSCRIBE_PLAN_ASSETS = { - crown: new URL("@/assets/images/subscription/crown.png", import.meta.url).href, check: new URL("@/assets/images/home/feature-check-icon.png", import.meta.url).href, pointsIcon: new URL("@/assets/images/points/points-active.png", import.meta.url).href, }; +const FIGMA_TEMP_ASSETS = { + // Figma temporary asset. Replace with a local exported icon if the design asset is added to the repo. + goal: "https://www.figma.com/api/mcp/asset/b627a414-493d-430e-bbba-2d862a46e127", +}; function getPlanKey(plan: ProductPlan, index: number): string { if (plan.id != null && plan.id !== "") { @@ -65,6 +68,27 @@ function formatPlanTierLabel(plan: ProductPlan): string { return String(coins); } +function formatCurrencyAmount(value: number): string { + if (!Number.isFinite(value)) { + return "0"; + } + + if (Number.isInteger(value)) { + return String(value); + } + + return value.toFixed(2).replace(/\.?0+$/, ""); +} + +function formatPointPriceValue(plan: ProductPlan, price: number): string | null { + const coins = Number(plan.coins); + if (!Number.isFinite(price) || !Number.isFinite(coins) || price <= 0 || coins <= 0) { + return null; + } + + return formatCurrencyAmount((price / coins) * 100); +} + /** 去掉 mark/title/section 标签,保留纯文本 */ function stripPlanInlineTags(text: string): string { return text.replace(PLAN_INLINE_TAG_RE, "").trim(); @@ -282,9 +306,6 @@ export default function SubscribePlanList({ embedded = false, onBillingCycleChan const price = Number(plan.price ?? 0) / 100; // 价格单位为分,需要除以100转换为元 const original = Number(plan.original_price_amount) > 0 ? Number(plan.original_price_amount) : null; // 原价 const level = Number(plan.level ?? -1); // 会员等级 - const titleBadges: Array<{ type: string; text: string }> = []; // - if (plan.recommended) titleBadges.push({ type: "recommended", text: t("subscribe.recommended_plan") }); - if (plan.custom_info?.rightTags) titleBadges.push({ type: "top", text: plan.custom_info.rightTags }); const isLevel4Plan = level === 4; const isFreePlan = price <= 0; const isPurchasedLevel4Plan = isLevel4Plan && userMemberLevel === 4; // 已购买4级会员 Future Creator 课程 @@ -295,7 +316,7 @@ export default function SubscribePlanList({ embedded = false, onBillingCycleChan ? t("subscribe.current_plan") : userMemberLevel > level // 当前会员等级大于计划等级 ? t("subscribe.already_member_level", { level: t(memberLevelMap[userMemberLevel] ?? memberLevelMap[0]) }) - : t("subscribe.subscribe_now"); // 订阅按钮文本 + : t("subscribe.plan_cta_goal"); // 订阅按钮文本 // 点击CTA按钮 const handleCtaClick = () => { @@ -309,6 +330,10 @@ export default function SubscribePlanList({ embedded = false, onBillingCycleChan // 渲染描述段落 const priceUnit = isLevel4Plan ? t("subscribe.per_100_days") : plan.planCategory === "yearly" ? t("subscribe.per_year") : t("subscribe.per_month"); const pointsPeriod = isLevel4Plan ? t("subscribe.points_granted_once") : plan.planCategory === "yearly" ? (plan.pointsGrantMode === "monthly" ? t("subscribe.points_period_monthly") : t("subscribe.points_period_yearly")) : t("subscribe.points_period_monthly"); + const pointPriceValue = formatPointPriceValue(plan, price); + const pointPriceText = plan.custom_info?.point_amount ?? (pointPriceValue ? t("subscribe.plan_point_price", { value: pointPriceValue }) : ""); + const discountText = price > 0 ? (plan.custom_info?.discount_info ?? t("subscribe.plan_default_discount")) : ""; + const descriptionLines = splitPlanDescriptionLines(plan.description || ""); const renderDescSegments = (line: string) => getPlanDescriptionSegments(line).map((seg, segIdx) => ( @@ -322,29 +347,19 @@ export default function SubscribePlanList({ embedded = false, onBillingCycleChan
- {price > 0 ? : null} {plan.title}
- {titleBadges.length > 0 ? ( -
- {titleBadges.map((badge) => ( - - {badge.text} - - ))} -
- ) : null} + {discountText ? {discountText} : null}
- {price} + {formatCurrencyAmount(price)} {priceUnit} {original != null && original > price ? ¥{original} : null} - {plan.custom_info?.discount_info && price > 0 ? {plan.custom_info.discount_info} : null}
- {plan.custom_info?.point_amount ?

{plan.custom_info.point_amount}

: null} + {pointPriceText ?

{pointPriceText}

: null}
{price > 0 && plan.coins != null ? ( @@ -429,20 +444,36 @@ export default function SubscribePlanList({ embedded = false, onBillingCycleChan
-
    - {splitPlanDescriptionLines(plan.description || "").map((line, lineIdx) => - isSectionLine(line) ? ( -
  • - {renderDescSegments(line)} -
  • - ) : ( -
  • - - {renderDescSegments(line)} -
  • - ), - )} -
+
+
+ + {t("subscribe.plan_goal_title")} +
+

+ {t("subscribe.plan_goal_line1")} + {t("subscribe.plan_goal_line2")} +

+
+ + {descriptionLines.length > 0 ? ( +
+ + ) : null}
diff --git a/src/locales/en-US.json b/src/locales/en-US.json index eae957a..a0a1206 100644 --- a/src/locales/en-US.json +++ b/src/locales/en-US.json @@ -564,7 +564,6 @@ "subscribe.promo_carousel_label": "Membership benefit campaigns", "subscribe.billing_badge_hot": "HOT", "subscribe.billing_badge_value": "Best value", - "subscribe.recommended_plan": "Recommended", "subscribe.discount_off_8": "OFF 8%", "subscribe.discount_off_15": "OFF 15%", "subscribe.other_cycle_unavailable": "Other billing cycles are not available yet. Please choose single purchase", @@ -589,6 +588,12 @@ "subscribe.buy_now": "Buy now", "subscribe.points_package": "Points package", "subscribe.already_member_level": "You are already \"{{level}}\"", + "subscribe.plan_cta_goal": "Finish the first work", + "subscribe.plan_default_discount": "30% off limited offer", + "subscribe.plan_point_price": "100 points ≈ ¥{{value}}", + "subscribe.plan_goal_title": "More trials / steadier output", + "subscribe.plan_goal_line1": "Combine images, characters, videos,", + "subscribe.plan_goal_line2": "and shots into the first publishable work", "subscribe.qa_title": "FAQ", "subscribe.qa_subtitle": "Popi points and content rights", "subscribe.qa_footnote_line1": "Note: The rules above are for FAQ display on the official site. Actual terms follow", diff --git a/src/locales/zh-CN.json b/src/locales/zh-CN.json index 1d54ed3..40ca65b 100644 --- a/src/locales/zh-CN.json +++ b/src/locales/zh-CN.json @@ -564,7 +564,6 @@ "subscribe.promo_carousel_label": "会员权益活动", "subscribe.billing_badge_hot": "HOT", "subscribe.billing_badge_value": "性价比", - "subscribe.recommended_plan": "主推计划", "subscribe.discount_off_8": "OFF 8%", "subscribe.discount_off_15": "OFF 15%", "subscribe.other_cycle_unavailable": "其他周期暂未开放,请选择单次购买", @@ -589,6 +588,12 @@ "subscribe.buy_now": "立即购买", "subscribe.points_package": "积分包", "subscribe.already_member_level": "你已是{{level}}", + "subscribe.plan_cta_goal": "完成第一条作品", + "subscribe.plan_default_discount": "限时7折", + "subscribe.plan_point_price": "每100积分≈¥{{value}}元", + "subscribe.plan_goal_title": "多次试错 / 稳定出作品", + "subscribe.plan_goal_line1": "把图片、角色、视频和镜头", + "subscribe.plan_goal_line2": "组合成第一条可发布作品", "subscribe.qa_title": "常见问题", "subscribe.qa_subtitle": "Popi 积分与内容权益规则", "subscribe.qa_footnote_line1": "注:以上规则用于官网常见问题展示,具体执行以", diff --git a/src/locales/zh-TW.json b/src/locales/zh-TW.json index 49fe959..62ee338 100644 --- a/src/locales/zh-TW.json +++ b/src/locales/zh-TW.json @@ -565,7 +565,6 @@ "subscribe.promo_carousel_label": "會員權益活動", "subscribe.billing_badge_hot": "HOT", "subscribe.billing_badge_value": "性價比", - "subscribe.recommended_plan": "主推計劃", "subscribe.discount_off_8": "OFF 8%", "subscribe.discount_off_15": "OFF 15%", "subscribe.other_cycle_unavailable": "其他週期暫未開放,請選擇單次購買", @@ -590,6 +589,12 @@ "subscribe.buy_now": "立即購買", "subscribe.points_package": "積分包", "subscribe.already_member_level": "你已是{{level}}", + "subscribe.plan_cta_goal": "完成第一條作品", + "subscribe.plan_default_discount": "限時7折", + "subscribe.plan_point_price": "每100積分≈¥{{value}}元", + "subscribe.plan_goal_title": "多次試錯 / 穩定出作品", + "subscribe.plan_goal_line1": "把圖片、角色、影片和鏡頭", + "subscribe.plan_goal_line2": "組合成第一條可發布作品", "subscribe.qa_title": "常見問題", "subscribe.qa_subtitle": "Popi 積分與內容權益規則", "subscribe.qa_footnote_line1": "註:以上規則用於官網常見問題展示,具體執行以", From 42a52b7997c3d2f20d1d634a87c15ccaddc40894 Mon Sep 17 00:00:00 2001 From: leiyuee Date: Sat, 18 Jul 2026 14:40:58 +0800 Subject: [PATCH 2/2] =?UTF-8?q?fix=EF=BC=9A=E5=88=9D=E7=89=88=E6=9A=82?= =?UTF-8?q?=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/api.ts | 8 +- .../subscribe-plan-feature-title-icon.png | Bin 0 -> 1504 bytes .../subscription/subscribe-plan-goal-icon.png | Bin 0 -> 2235 bytes src/components/subscribe-plan-list/index.scss | 320 +++++++-------- src/components/subscribe-plan-list/index.tsx | 379 +++++++++--------- src/dialog/pay-subscribe/index.tsx | 69 +--- src/locales/en-US.json | 13 +- src/locales/zh-CN.json | 13 +- src/locales/zh-TW.json | 13 +- src/pages/subscribe/index.scss | 21 +- src/pages/subscribe/index.tsx | 2 +- src/utils/index.ts | 1 - src/utils/openLatestPrivacyPolicyLink.ts | 2 - 13 files changed, 379 insertions(+), 462 deletions(-) create mode 100644 src/assets/images/subscription/subscribe-plan-feature-title-icon.png create mode 100644 src/assets/images/subscription/subscribe-plan-goal-icon.png diff --git a/src/api/api.ts b/src/api/api.ts index 87f5839..eed8706 100644 --- a/src/api/api.ts +++ b/src/api/api.ts @@ -27,7 +27,12 @@ type ListApiResponse = { export type ProductPlanCustomInfo = { discount_info?: string; point_amount?: string; - illustrator?: string; + buttonText?: string; + bonus_gift?: string; + goal_title?: string; + goal_description?: string; + feature_title?: string; + new_user?: string | boolean; }; export type ProductPlan = ApiParams & { id?: EntityId; @@ -35,6 +40,7 @@ export type ProductPlan = ApiParams & { name?: string; description?: string; planCategory?: string; + recommended?: boolean; price?: number | string; price_amount?: number | string; original_price_amount?: number | string; diff --git a/src/assets/images/subscription/subscribe-plan-feature-title-icon.png b/src/assets/images/subscription/subscribe-plan-feature-title-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..3f4f7ef67f3c529d56fe5a425bf47df88ea82482 GIT binary patch literal 1504 zcmV<61t0o}P)@~0drDELIAGL9O(c600d`2O+f$vv5yPEdg+*cuL`9 z7$dTk&z1t z&hh=35YSN(QnyKN{&xPR}FZW3FXy zii}hwD1^I3d`2KOzS?sYj!j3#)^^kVXXh4DLW=}#;nC~Di#^xv<7d!>J{j-^(PGUQ zf+mzm;0Ge%dqoHzcW={VB$B`$A133DYNA7G--S}Sormx%&ti~_SQ4~jJ<<&!ohs_W zIsRIs8@7myn&K%^G9HnTF4oV(U6II>j2Q_RN0U<$-hJxAFN4VtVvGHit$4}`89oUY zN23-ZwB_d`mA%vEQxJX`LBeLWRLP)Y+kd?;UGg>tp5ObJdOv3DnU1TXGRU%OnDOPa%q2)W;1m8GWSOrY$9aE8f!X!D6Q?Ruj5cQvZ}X{CyiyXvo7(DK9dzl&T@z&JE$OKj zeM%%m4#(vHobVBRTGBji9@!(q=Wb^ipkE6N}|QWH|pbSPRu~!_1WpswO>L; zWYz@tzdc_0IdJOtjsUBX5Jf^08NZ^3Z!SB|Xl;XpuYnZg^n~E9P@n%Ivx6+i%^W!S zAVKJ%JV_K0CnW4iWFlP2^Be;SuQJDAq}h|JPYB=c85J)s6)$g-a*NPJ&HzRgr8L)I zr^|?AoSDe+SJDbL5m+art>E9Bb*@p-!EmU70lM_t%T&vw%W6OtPLr&^#@- zK?B+(gnb3phIJ0n2pMV(71n~%p>9^0ZiLS-maro^EEh6#x=CEohS{nXQWxrByl*KS znu-klaY3poQxcwWqE%};M2QSd;lVcnT4+t90kbPWMg-yU2a&OLnuUy1!sj$Sli!rP zHW`APmql++3?WFss-T4YM~CwN-eE>YVhODclM{31zI@T2K#fHa@~0drDELIAGL9O(c600d`2O+f$vv5yPqbAokFfH^_L!veK0F(<%W z04pcJoB)vv;G6*RvIW@(yeEirf@n{Gcf*fEV)b^9gltJ8YsMBKUzGvNNHgC|Pxse7 zV*!r@w}o{WjhcYtCW2U_OHF#!=`};h};gbJGtaxufR1Zz>6;L7+ z$-A32f_zO~ac2FmsNS!7XRAZ7Z6hWt>hZHGzpQ(IJ|CFe{|riiyM5;%;sM^>I^h^% z@lE%&2K_YZWZaI1tit%aWBROx2**S*aKH*;U^*jlTX|#^lr?!K&zXxb5p59B6BD9B zfKL-ncavk zA{)19xQ?x6xU+S8MC0k^jMGqv;24%xJnu|cJ0l(P$qKfV283QegyA|iBFx^G91=B* zP0h!p6IE@_ASl2=n?C;cOWFvFSQFdl%&fy%sp4IPw*Y=9e& zMK*$;zMvx;0t=iJTM4#k0FTI0Kf7DEu9mR7eccYdaRMRPzgDaSgEa!^XRfg(*FcEM zo)E~I+{jrX;6cWu^VThy@y;X9Y>c$23J`C+i{+Uf*xml((3)=2Kfgx)tu=L_F=-4A zAWfDX)DI%7+5`6fQ(XdhU33qtH}7J_gQeOIXqE*i3k`M>gn5+c;q64@6nZG^bYTIg zEXoSxWD?NEe{DNa2-W)@T8;_Ai1H-+7_kR)fE(Z6ygT4fL7liFGd(fBIlGSQoTxxS zdqrgVO-xv=0i7w&p@ms*lM?z&3amR7Purn0EJoD1gd-xqvYSWRlq_Q+Jh0M9=CQ)3 zR}U;PQ*R3!>B%8yS~^0TNVmyiIJOQbwsLYZfTg8@DOuG8{tI+46wg>b3oSBh53;iLL-wNJLwMuNaH#YBN8g@%yWn+nfAudK$f9Q8WDCO z)k&MR%WIPg0%@UQt+(^Ya)0RgIk7c?LUANr=c=AsD}=zfq8Kg*aR8Z4;+{=7%O|S_ zZR7Z&wn}8y1>hT6hpY+G{=i5HK7*xa&1}uB(g#!I36=Ae9vEUa*~y?qWca027pIZ5 zC>_;)06i1gOC~LyR5QMfIZx-Lll#js<0M%frUt+mlJw_Vq9MRfhWCQI`Ckh%vuiF? z79l$?BtnpM1j1s(L?xwhm38zD?tBum3`s)0k+_29;s8^@UWow%JQZ06&2XxGBhd$Q zG?Mowvm70sk}N}$9!d-Omx_x8_i_k)O0tYn_LdgGW0MUx4wNXOa;}D>ygfutOIsZL^YqLAeAQh-7V>G=eYF zou|&l{s|{U=2($`j!##yk86@A)A!g z%HC8hj6{`GTNX()Oh++tLdU6Q94>gng5Y=!8QF?fSsY}xh-6>F=zCU5BDTO3qwnkm zld}wXw7-3~3v)t+l1#kjl9TL#F|tJsSQ-r^&k}X9KYiV#4*UQ$%Yd2ch`^x7RZFmh zQet5!Nj{IlnUy)~iW+CDLluW9G^I2!nPuy$sJak?3*Z8?=EhY^-jFkKWc)wuh4Yn| zd43(5v%aViDy^^IdhqM>qr~T<=e!QPUitXGvsCWZ7uxQW9Vh{0 z+Q(!)Rt`|NH^vt1UDT9cilrruWO^Oeqgl3sck#mIdRFLU=EhLf#4O1&f*|0v+elm# zGWH3HYPoingm|&svBHH>o4gB%N^2>*JiEN2ynS#ADb*avR zn|>SBl&295Qd@O|OF>e$SouWx_k%+mhFTtJhaS&}=wOk9F;QO=NSB;4`yrZH=1sP) z%>mA*fVQxp**(dmhrFu}DIvk(rA&=nHr&mdmQJYXc26>d*%Wm`g4V{V`^U9$CiSDB zjyMUODeHxb_Kb%T5|Gx&w4TS}@@FEQcb-dyvmBxF(;ZtfMR<~TdW<*DYFZHpMw?g^o;bJFdP4RGS zS%2DIk=-cjOCjM&LjjqOzWl>sOGX2SvO}|EUJJ888ARh}L5YY=-Jw5sQ(2ca;xCF+YrQhRT2LzC zX@p l.trim() !== ""); + return description.split(/\n|\\n/).filter((l) => l.trim() !== ""); } /** 套餐描述行:解析 mark 片段;分组标题行解析外壳内的 mark */ @@ -180,12 +190,10 @@ function getPlanDescriptionSegments(line: string): PlanDescriptionSegment[] { export default function SubscribePlanList({ embedded = false, onBillingCycleChange }: SubscribePlanListProps) { const { t } = useTranslation(); const user = useUserStore((s) => s.user); - const serviceQrCode = useParamConfigStore((s) => s.config?.systemConfig?.serviceQrCode ?? ""); const [billingCycle, setBillingCycleState] = useState("monthly"); const [planList, setPlanList] = useState([]); const [selectedPlanKeyByGroup, setSelectedPlanKeyByGroup] = useState>({}); - const [futureCreatorModalOpen, setFutureCreatorModalOpen] = useState(false); const userMemberLevel = Number(user?.memberLevel ?? 0); // 拉取套餐列表 @@ -245,13 +253,7 @@ export default function SubscribePlanList({ embedded = false, onBillingCycleChan void openPaySubscribeDialog({ type: "subscribe", plan }); }, []); - const openFutureCreatorModal = useCallback(() => { - setFutureCreatorModalOpen(true); - }, []); - const joinCls = (...parts: Array) => parts.filter(Boolean).join(" "); - const closeIconSrc = new URL("@/assets/images/common/icons/close.png", import.meta.url).href; - const wechatIconSrc = new URL("@/assets/images/common/payments/wxpay-logo.png", import.meta.url).href; return (
@@ -306,34 +308,36 @@ export default function SubscribePlanList({ embedded = false, onBillingCycleChan const price = Number(plan.price ?? 0) / 100; // 价格单位为分,需要除以100转换为元 const original = Number(plan.original_price_amount) > 0 ? Number(plan.original_price_amount) : null; // 原价 const level = Number(plan.level ?? -1); // 会员等级 - const isLevel4Plan = level === 4; const isFreePlan = price <= 0; - const isPurchasedLevel4Plan = isLevel4Plan && userMemberLevel === 4; // 已购买4级会员 Future Creator 课程 - const ctaDisabled = isPurchasedLevel4Plan ? false : userMemberLevel === level || userMemberLevel > level; // 按钮是否禁用 - const ctaLabel = isPurchasedLevel4Plan - ? t("subscribe.contact_service") - : userMemberLevel === level // 当前会员等级 + const ctaDisabled = userMemberLevel === level || userMemberLevel > level; // 按钮是否禁用 + const clickableSubscribeText = plan.custom_info?.buttonText?.trim() || t("subscribe.subscribe_now"); + const ctaLabel = + userMemberLevel === level // 当前会员等级 ? t("subscribe.current_plan") : userMemberLevel > level // 当前会员等级大于计划等级 ? t("subscribe.already_member_level", { level: t(memberLevelMap[userMemberLevel] ?? memberLevelMap[0]) }) - : t("subscribe.plan_cta_goal"); // 订阅按钮文本 + : clickableSubscribeText; // 仅可点击的开通按钮使用服务端文案 // 点击CTA按钮 const handleCtaClick = () => { - if (isPurchasedLevel4Plan) { - openFutureCreatorModal(); - return; - } showPaySubscribe(plan); }; // 渲染描述段落 - const priceUnit = isLevel4Plan ? t("subscribe.per_100_days") : plan.planCategory === "yearly" ? t("subscribe.per_year") : t("subscribe.per_month"); - const pointsPeriod = isLevel4Plan ? t("subscribe.points_granted_once") : plan.planCategory === "yearly" ? (plan.pointsGrantMode === "monthly" ? t("subscribe.points_period_monthly") : t("subscribe.points_period_yearly")) : t("subscribe.points_period_monthly"); + const priceUnit = plan.planCategory === "yearly" ? t("subscribe.per_year") : t("subscribe.per_month"); + const pointsPeriod = plan.planCategory === "yearly" ? (plan.pointsGrantMode === "monthly" ? t("subscribe.points_period_monthly") : t("subscribe.points_period_yearly")) : t("subscribe.points_period_monthly"); const pointPriceValue = formatPointPriceValue(plan, price); const pointPriceText = plan.custom_info?.point_amount ?? (pointPriceValue ? t("subscribe.plan_point_price", { value: pointPriceValue }) : ""); const discountText = price > 0 ? (plan.custom_info?.discount_info ?? t("subscribe.plan_default_discount")) : ""; const descriptionLines = splitPlanDescriptionLines(plan.description || ""); + const goalTitle = plan.custom_info?.goal_title?.trim() ?? ""; + const goalDescriptionLines = splitPlanDescriptionLines(plan.custom_info?.goal_description ?? ""); + const showPlanGoal = goalTitle !== "" || goalDescriptionLines.length > 0; + const featureTitle = plan.custom_info?.feature_title?.trim() ?? ""; + const newUserLabel = getNewUserLabel(plan.custom_info?.new_user, t("subscribe.plan_new_user")); + const isNewUserPlan = newUserLabel !== ""; + const recommendedLabel = plan.recommended === true && !isNewUserPlan ? t("subscribe.plan_recommended") : ""; + const isRecommendedPlan = recommendedLabel !== ""; const renderDescSegments = (line: string) => getPlanDescriptionSegments(line).map((seg, segIdx) => ( @@ -341,194 +345,173 @@ export default function SubscribePlanList({ embedded = false, onBillingCycleChan )); - return ( -
-
-
-
-
- {plan.title} -
- {discountText ? {discountText} : null} + const planContent = ( + <> +
+
+
+ {plan.title}
+ {discountText ? {discountText} : null} +
-
-
- - {formatCurrencyAmount(price)} - {priceUnit} - {original != null && original > price ? ¥{original} : null} -
- {pointPriceText ?

{pointPriceText}

: null} +
+
+ + {formatCurrencyAmount(price)} + {priceUnit} + {original != null && original > price ? ¥{original} : null}
+ {pointPriceText ?

{pointPriceText}

: null} +
- {price > 0 && plan.coins != null ? ( -
- - - {plan.coins} - - {t("subscribe.points_unit")} - {pointsPeriod} - + {price > 0 && plan.coins != null ? ( +
+ + -
- ) : null} - - - - {planGroup.plans.length > 1 ? ( -
-
- {planGroup.plans.map((item) => { - const active = item.key === selectedPlanItem.key; - - return ( - - ); - })} -
-
-
-
- ) : null} - - {plan.custom_info?.illustrator ? ( -
- - {t("subscribe.commission_highlight")} - {plan.custom_info.illustrator} + {plan.coins} + + {t("subscribe.points_unit")} + {pointsPeriod} - {t("subscribe.limited_time_badge")} + +
+ ) : null} + + + + {planGroup.plans.length > 1 ? ( +
+
+ {planGroup.plans.map((item) => { + const active = item.key === selectedPlanItem.key; + + return ( + + ); + })}
- ) : null} -
- -
-
-
- - {t("subscribe.plan_goal_title")} +
+
-

- {t("subscribe.plan_goal_line1")} - {t("subscribe.plan_goal_line2")} -

+ ) : null} + + {plan.custom_info?.bonus_gift ? ( +
+ + {t("subscribe.bonus_gift_prefix")} + {plan.custom_info.bonus_gift} + +
+ ) : null} +
- {descriptionLines.length > 0 ? ( -
- + ) : null} +
+ + ); + const highlightLabel = isNewUserPlan ? newUserLabel : recommendedLabel; + const highlightClassName = isNewUserPlan ? "plan_item__new-user-label" : isRecommendedPlan ? "plan_item__recommended-label" : ""; + + return ( +
+
+ {highlightLabel ? ( +
+
{highlightLabel}
+
{planContent}
+
+ ) : ( + planContent + )}
); })}
- - {futureCreatorModalOpen - ? createPortal( -
{ - setFutureCreatorModalOpen(false); - }} - > - , - document.body, - ) - : null}
); } diff --git a/src/dialog/pay-subscribe/index.tsx b/src/dialog/pay-subscribe/index.tsx index 7e4f0da..4b30808 100644 --- a/src/dialog/pay-subscribe/index.tsx +++ b/src/dialog/pay-subscribe/index.tsx @@ -11,7 +11,7 @@ import { getToken } from "@/utils/auth"; import { useUserStore } from "@/store/useUserStore"; import { checkGatewayPayment, checkGatewayWxPayment, checkPointsPayment, checkPointsWxPayment, createPointsOrder, createPointsOrderByWxPay, createSubscriptionOrder, createSubscriptionOrderByWxPay } from "@/api/api"; import type { PaymentOrderData, PaymentStatusData, ProductPlan } from "@/api/api"; -import { openLatestPrivacyPolicyLink, PRIVACY_POLICY_KEY_FUTURE_CREATOR, PRIVACY_POLICY_KEY_PAYMENT_TERMS } from "@/utils/openLatestPrivacyPolicyLink"; +import { openLatestPrivacyPolicyLink, PRIVACY_POLICY_KEY_PAYMENT_TERMS } from "@/utils/openLatestPrivacyPolicyLink"; import "./index.scss"; type PayType = "subscribe" | "points"; @@ -152,7 +152,6 @@ export default function PaySubscribe({ onClose, type, plan, afterPaySuccess }: P }, [payType, plan?.name, plan?.title, t]); const originalPrice = Number(plan?.original_price_amount ?? 0); const discountInfo = plan?.custom_info?.discount_info; - const isFutureCreatorPlan = Number(plan?.level ?? 0) === 4; /** 支付宝始终蒙层;微信仅首次同意前蒙层 */ const showAgreementOverlay = paymentMethod === "ALIPAY" || !wxAgreed; @@ -541,29 +540,16 @@ export default function PaySubscribe({ onClose, type, plan, afterPaySuccess }: P {t("subscribe.read_before_pay")}
- {isFutureCreatorPlan ? ( - { - e.preventDefault(); - void openLatestPrivacyPolicyLink(PRIVACY_POLICY_KEY_FUTURE_CREATOR); - }} - > - {t("subscribe.future_creator_payment_terms")} - - ) : ( - { - e.preventDefault(); - void openLatestPrivacyPolicyLink(PRIVACY_POLICY_KEY_PAYMENT_TERMS); - }} - > - {t("subscribe.payment_terms")} - - )} + { + e.preventDefault(); + void openLatestPrivacyPolicyLink(PRIVACY_POLICY_KEY_PAYMENT_TERMS); + }} + > + {t("subscribe.payment_terms")} +

); diff --git a/src/locales/en-US.json b/src/locales/en-US.json index a0a1206..a08c6df 100644 --- a/src/locales/en-US.json +++ b/src/locales/en-US.json @@ -535,19 +535,13 @@ "subscribe.creator_member": "Creator", "subscribe.per_month": "/month", "subscribe.per_year": "/year", - "subscribe.per_100_days": "100 days", "subscribe.current_plan": "Current plan", "subscribe.subscribe_now": "Sponsor now", - "subscribe.contact_service": "Purchased — contact support", - "subscribe.future_creator_title": "Popi Future Creator Plan", - "subscribe.future_creator_success": "Activated!", - "subscribe.future_creator_wechat_tip": "Scan with WeChat to contact support and join the course", "subscribe.scan_to_pay": "Scan to pay", "subscribe.scan_code_tip": "Please scan to pay", "subscribe.yuan": " yuan", "subscribe.read_before_pay": "Please read before payment", "subscribe.payment_terms": "'Popi.art paid service agreement '", - "subscribe.future_creator_payment_terms": "Popi Future Creator Plan User Service Agreement", "subscribe.agree_and_pay": "Agree and Pay", "subscribe.please_complete_wechat_payment": "Please complete payment in WeChat", "subscribe.please_complete_alipay_payment": "Please complete payment in Alipay", @@ -570,10 +564,7 @@ "subscribe.points_unit": "points /", "subscribe.points_period_monthly": "month", "subscribe.points_period_yearly": "year", - "subscribe.points_granted_once": "Granted in full once", - "subscribe.commission_highlight": "1-on-1 artist commission", - "subscribe.commission_suffix": ": character art ×1", - "subscribe.limited_time_badge": "Limited", + "subscribe.bonus_gift_prefix": "Bonus: ", "subscribe.pay_result_timeout": "Payment check timed out. Please check your order or account later", "subscribe.pay_notice_timeout_title": "Result not confirmed yet", "subscribe.pay_notice_missing_plan_title": "Cannot start payment", @@ -591,6 +582,8 @@ "subscribe.plan_cta_goal": "Finish the first work", "subscribe.plan_default_discount": "30% off limited offer", "subscribe.plan_point_price": "100 points ≈ ¥{{value}}", + "subscribe.plan_new_user": "Best for beginners", + "subscribe.plan_recommended": "Recommended", "subscribe.plan_goal_title": "More trials / steadier output", "subscribe.plan_goal_line1": "Combine images, characters, videos,", "subscribe.plan_goal_line2": "and shots into the first publishable work", diff --git a/src/locales/zh-CN.json b/src/locales/zh-CN.json index 40ca65b..bc04a77 100644 --- a/src/locales/zh-CN.json +++ b/src/locales/zh-CN.json @@ -535,19 +535,13 @@ "subscribe.creator_member": "Creator", "subscribe.per_month": "每月", "subscribe.per_year": "每年", - "subscribe.per_100_days": "100天", "subscribe.current_plan": "当前计划", "subscribe.subscribe_now": "立即开通", - "subscribe.contact_service": "已购买,联系客服", - "subscribe.future_creator_title": "Popi Future Creator 计划", - "subscribe.future_creator_success": "开通成功!", - "subscribe.future_creator_wechat_tip": "请使用微信扫码联系客服加入课程", "subscribe.scan_to_pay": "扫码支付", "subscribe.scan_code_tip": "请扫码支付", "subscribe.yuan": "元", "subscribe.read_before_pay": "支付前请阅读", "subscribe.payment_terms": "《“Popi.art”付费服务协议》", - "subscribe.future_creator_payment_terms": "Popi Future Creator 计划用户服务协议", "subscribe.agree_and_pay": "同意并支付", "subscribe.please_complete_wechat_payment": "请微信扫码完成支付", "subscribe.please_complete_alipay_payment": "请在跳转页面扫码完成支付", @@ -570,10 +564,7 @@ "subscribe.points_unit": "积分/", "subscribe.points_period_monthly": "每月", "subscribe.points_period_yearly": "每年", - "subscribe.points_granted_once": "一次全额获得", - "subscribe.commission_highlight": "画师一对一约稿", - "subscribe.commission_suffix": ":角色图×1", - "subscribe.limited_time_badge": "限时", + "subscribe.bonus_gift_prefix": "附赠:", "subscribe.pay_result_timeout": "支付结果查询超时,请稍后在订单或账户中查看", "subscribe.pay_notice_timeout_title": "尚未确认结果", "subscribe.pay_notice_missing_plan_title": "无法发起支付", @@ -591,6 +582,8 @@ "subscribe.plan_cta_goal": "完成第一条作品", "subscribe.plan_default_discount": "限时7折", "subscribe.plan_point_price": "每100积分≈¥{{value}}元", + "subscribe.plan_new_user": "新人首选", + "subscribe.plan_recommended": "官方推荐", "subscribe.plan_goal_title": "多次试错 / 稳定出作品", "subscribe.plan_goal_line1": "把图片、角色、视频和镜头", "subscribe.plan_goal_line2": "组合成第一条可发布作品", diff --git a/src/locales/zh-TW.json b/src/locales/zh-TW.json index 62ee338..89a6fcf 100644 --- a/src/locales/zh-TW.json +++ b/src/locales/zh-TW.json @@ -536,19 +536,13 @@ "subscribe.creator_member": "Creator", "subscribe.per_month": "每月", "subscribe.per_year": "每年", - "subscribe.per_100_days": "100天", "subscribe.current_plan": "目前方案", "subscribe.subscribe_now": "立即贊助", - "subscribe.contact_service": "已購買,聯絡客服", - "subscribe.future_creator_title": "Popi Future Creator 計劃", - "subscribe.future_creator_success": "開通成功!", - "subscribe.future_creator_wechat_tip": "請使用微信掃碼聯絡客服加入課程", "subscribe.scan_to_pay": "掃碼支付", "subscribe.scan_code_tip": "請掃碼支付", "subscribe.yuan": "元", "subscribe.read_before_pay": "支付前請閱讀", "subscribe.payment_terms": "《“Popi.art”付費服務協議》", - "subscribe.future_creator_payment_terms": "Popi Future Creator 計劃用戶服務協議", "subscribe.agree_and_pay": "同意並支付", "subscribe.please_complete_wechat_payment": "請微信掃碼完成支付", "subscribe.please_complete_alipay_payment": "請在跳轉頁面掃碼完成支付", @@ -571,10 +565,7 @@ "subscribe.points_unit": "積分/", "subscribe.points_period_monthly": "每月", "subscribe.points_period_yearly": "每年", - "subscribe.points_granted_once": "一次全額獲得", - "subscribe.commission_highlight": "畫師一對一約稿", - "subscribe.commission_suffix": ":角色圖×1", - "subscribe.limited_time_badge": "限時", + "subscribe.bonus_gift_prefix": "附贈:", "subscribe.pay_result_timeout": "支付結果查詢超時,請稍後在訂單或帳戶中查看", "subscribe.pay_notice_timeout_title": "尚未確認結果", "subscribe.pay_notice_missing_plan_title": "無法發起支付", @@ -592,6 +583,8 @@ "subscribe.plan_cta_goal": "完成第一條作品", "subscribe.plan_default_discount": "限時7折", "subscribe.plan_point_price": "每100積分≈¥{{value}}元", + "subscribe.plan_new_user": "新人首選", + "subscribe.plan_recommended": "官方推薦", "subscribe.plan_goal_title": "多次試錯 / 穩定出作品", "subscribe.plan_goal_line1": "把圖片、角色、影片和鏡頭", "subscribe.plan_goal_line2": "組合成第一條可發布作品", diff --git a/src/pages/subscribe/index.scss b/src/pages/subscribe/index.scss index 34a9662..821fc57 100644 --- a/src/pages/subscribe/index.scss +++ b/src/pages/subscribe/index.scss @@ -50,9 +50,9 @@ .subscribe-overview-card { position: relative; display: grid; - grid-template-columns: 146px minmax(0, 518px); + grid-template-columns: 313px minmax(0, 1fr); align-items: start; - gap: 77px; + gap: 20px; height: 132px; padding: 20px 30px; border-radius: 26px; @@ -102,6 +102,7 @@ } .user-plan__meta { + flex: 1 1 auto; min-width: 0; } @@ -109,13 +110,15 @@ display: flex; align-items: center; gap: 5px; + width: 100%; min-width: 0; height: 20px; } .user-plan__name { + flex: 0 0 220px; overflow: hidden; - max-width: 72px; + width: 220px; color: #333333; font-size: 16px; font-weight: 400; @@ -126,6 +129,7 @@ .user-plan__tag { display: inline-flex; + flex: 0 0 auto; align-items: center; justify-content: center; min-width: 39px; @@ -411,8 +415,8 @@ } .subscribe-overview-card { - grid-template-columns: 146px minmax(0, 1fr); - gap: 42px; + grid-template-columns: 313px minmax(0, 1fr); + gap: 20px; } .subscribe-promo-card.subscribe-promo-card_membership { @@ -456,6 +460,11 @@ margin-top: 0; } + .user-plan__name { + flex: 1 1 auto; + width: auto; + } + .points-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; @@ -487,4 +496,4 @@ } } } -} \ No newline at end of file +} diff --git a/src/pages/subscribe/index.tsx b/src/pages/subscribe/index.tsx index 74b0238..f257872 100644 --- a/src/pages/subscribe/index.tsx +++ b/src/pages/subscribe/index.tsx @@ -67,7 +67,7 @@ export default function Subscribe() {
- {userName} +
{userName}
{memberLevel !== 0 ? t("subscribe.vip_badge") : t(memberLevelKey)}
diff --git a/src/utils/index.ts b/src/utils/index.ts index 9c956d0..b1ef470 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -12,7 +12,6 @@ export { openLatestPrivacyPolicyLink, PRIVACY_POLICY_KEY_AI_USE_NOTICE, PRIVACY_POLICY_KEY_EVIDENCE_PRESERVATION, - PRIVACY_POLICY_KEY_FUTURE_CREATOR, PRIVACY_POLICY_KEY_MUSIC_USE_NOTICE, PRIVACY_POLICY_KEY_PAYMENT_TERMS, PRIVACY_POLICY_KEY_PRIVACY_POLICY, diff --git a/src/utils/openLatestPrivacyPolicyLink.ts b/src/utils/openLatestPrivacyPolicyLink.ts index 578c48c..aaa8ac0 100644 --- a/src/utils/openLatestPrivacyPolicyLink.ts +++ b/src/utils/openLatestPrivacyPolicyLink.ts @@ -14,8 +14,6 @@ export const PRIVACY_POLICY_KEY_PAYMENT_TERMS = "web.paymentTerms"; export const PRIVACY_POLICY_KEY_EVIDENCE_PRESERVATION = "web.popiartEvidencePreservationAgreement"; /** 内容协议 key:音乐功能使用须知 */ export const PRIVACY_POLICY_KEY_MUSIC_USE_NOTICE = "web.musicUseNotice"; -/** 内容协议 key:Popi Future Creator 计划用户服务协议 */ -export const PRIVACY_POLICY_KEY_FUTURE_CREATOR = "web.futureCreatorUserServiceAgreement"; interface PrivacyPolicyItem { id: number;