This commit is contained in:
谢宇宁
2026-09-29 15:30:33 +07:00
parent 0fbdba5ccf
commit 9abe0b1b76
181 changed files with 608 additions and 18501 deletions
File diff suppressed because one or more lines are too long
-72
View File
@@ -1,72 +0,0 @@
/* 与「我的收藏」一致的账户列表空状态。 */
/* 空态时仍保持与个人中心同宽的主栏,避免筛选条贴到视口左缘。 */
.account-empty {
display: flex;
flex-direction: column;
align-items: center;
gap: 16px;
width: 100%;
padding: 96px 16px;
font-size: 14px;
line-height: 22px;
text-align: center;
}
.account-empty[hidden] { display: none !important; }
.account-empty__icon {
display: flex;
align-items: center;
justify-content: center;
width: 96px;
height: 96px;
flex: 0 0 96px;
border: 1px solid var(--line);
border-radius: 50%;
background: #1b1b1f;
}
.account-empty__icon img { width: 40px; height: 40px; }
.account-empty__title {
margin: 0;
color: var(--text);
font-size: 20px;
font-weight: 600;
line-height: 28px;
}
.account-empty__description {
margin: 0;
color: var(--text-muted);
font-size: 14px;
line-height: 22px;
}
.account-empty__description-mobile { display: none; }
.account-empty__action {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 8px;
width: 147px;
height: 48px;
border-radius: 999px;
background: linear-gradient(105deg, #ff4a7b 0%, #ec237f 52%, #93226f 100%);
color: #fff;
font-size: 14px;
font-weight: 600;
line-height: 20px;
white-space: nowrap;
}
.account-empty__action img { width: 20px; height: 20px; }
.account-empty__action:focus-visible { outline: 2px solid var(--accent-hot); outline-offset: 3px; }
@media (max-width: 768px) {
.account-empty { padding: 48px 16px; }
.account-empty__description-desktop { display: none; }
.account-empty__description-mobile { display: inline; }
}
+32
View File
@@ -770,3 +770,35 @@ img {
white-space: nowrap !important;
border: 0 !important;
}
/* 卡片信息行:集数/状态前置为金色小标,标签随后(首页、列表、排行榜共用) */
.card__meta--ep {
display: flex;
align-items: center;
gap: 6px;
}
.card__meta-ep {
flex-shrink: 0;
padding: 0 6px;
border-radius: 4px;
color: var(--gold);
font-size: 12px;
line-height: 18px;
background: rgba(233, 201, 119, 0.12);
}
.card__meta-tags {
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
@media (max-width: 768px) {
.card__meta-ep {
padding: 0 4px;
font-size: 10px;
line-height: 16px;
}
}
-266
View File
@@ -1,266 +0,0 @@
/* 我的收藏 H5:以 Figma 375px 画板为基准。 */
@media (max-width: 768px) {
.favorites-main {
display: flex;
flex-direction: column;
gap: 12px;
width: 100%;
max-width: none;
min-width: 0;
margin: 0;
padding: 12px var(--profile-gutter, 16px) 20px;
}
.favorites-main.is-empty {
width: 100%;
gap: 0;
padding: 12px 0 20px;
}
.favorites-main.is-empty .favorites-toolbar {
width: 100%;
padding-inline: var(--profile-gutter, 16px);
}
.favorites-main.is-empty .favorites-recommendations {
width: auto;
margin-top: 8px;
padding: 0 var(--profile-gutter, 16px) 24px;
}
.favorites-toolbar {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
width: 100%;
min-width: 0;
min-height: 30px;
}
.favorites-filters {
display: flex;
flex: 1 1 auto;
flex-wrap: nowrap;
align-items: center;
gap: clamp(6px, 2.666667vw, 10px);
min-width: 0;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
scrollbar-width: none;
}
.favorites-filters::-webkit-scrollbar {
display: none;
}
.favorites-filter {
display: inline-flex;
flex: 0 0 auto;
align-items: center;
justify-content: center;
height: 30px;
padding: 0 clamp(11px, 4.266667vw, 16px);
border: 1px solid rgba(255, 255, 255, .12);
border-radius: 999px;
background: transparent;
color: #a1a1aa;
font-size: 14px;
font-weight: 500;
line-height: 20px;
white-space: nowrap;
}
.favorites-filter.is-active {
border-color: rgba(236, 35, 127, .3);
background: rgba(236, 35, 127, .1);
color: #ff4a7b;
}
.favorites-manage {
display: inline-flex;
flex: 0 0 auto;
align-items: center;
justify-content: center;
height: 30px;
padding: 0;
border: 0;
background: none;
color: #a1a1aa;
font-size: 12px;
font-weight: 400;
line-height: 18px;
white-space: nowrap;
}
.favorites-manage svg,
.favorites-manage img {
display: none;
}
.favorites-grid {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 16px var(--profile-card-gap, 8px);
width: 100%;
min-width: 0;
}
.favorites-card {
position: relative;
display: flex;
flex-direction: column;
gap: 6px;
min-width: 0;
}
.favorites-card .profile-card__cover {
width: 100%;
aspect-ratio: 109 / 145;
border-radius: 8px;
}
.favorites-card .profile-card__title {
overflow: hidden;
font-size: 14px;
font-weight: 600;
line-height: 20px;
text-overflow: ellipsis;
white-space: nowrap;
}
.favorites-card .profile-card__meta {
overflow: hidden;
font-size: 11px;
line-height: 16px;
text-overflow: ellipsis;
white-space: nowrap;
}
.favorites-card__desktop-meta {
display: none;
}
.favorites-card__mobile-meta {
display: inline;
}
.favorites-card__remove {
display: none;
}
.favorites-page.is-managing .favorites-card__remove {
position: absolute;
top: 6px;
left: 6px;
z-index: 3;
display: inline-flex;
align-items: center;
justify-content: center;
width: 24px;
height: 24px;
padding: 0;
border: 1px solid rgba(255, 255, 255, .5);
border-radius: 50%;
background: rgba(11, 11, 13, .8);
color: #f2f2f4;
}
.favorites-card .profile-card__episodes {
bottom: 10px;
left: 10px;
font-size: 11px;
line-height: 14px;
}
.favorites-card .profile-card__badge {
top: 4px;
right: 4px;
min-width: 30px;
height: 20px;
padding: 0 5px;
}
.favorites-load-more {
display: inline-flex;
align-items: center;
justify-content: center;
width: 100%;
height: 40px;
padding: 0 16px;
border: 1px solid rgba(255, 255, 255, .24);
border-radius: 999px;
background: transparent;
color: #f2f2f4;
font-size: 14px;
font-weight: 500;
line-height: 20px;
}
.favorites-pagination {
display: none;
}
.favorites-empty {
gap: 16px;
padding: 48px 16px;
}
.favorites-empty__description-desktop,
.favorites-recommendations__more-desktop {
display: none;
}
.favorites-empty__description-mobile,
.favorites-recommendations__more-mobile {
display: inline;
}
.favorites-recommendations {
width: 100%;
padding: 0 var(--profile-gutter, 16px) 20px;
}
.favorites-recommendations__head {
height: 26px;
margin-bottom: 14px;
}
.favorites-recommendations__head h2 {
font-size: 18px;
line-height: 26px;
}
.favorites-recommendations__head a {
font-size: 12px;
line-height: 18px;
}
.favorites-recommendations__head a img {
width: 14px;
height: 14px;
}
.favorites-recommendations__grid {
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 16px var(--profile-card-gap, 8px);
}
.favorites-card[hidden],
.favorites-empty[hidden],
.favorites-load-more[hidden] {
display: none !important;
}
}
@media (max-width: 359px) {
.favorites-filters {
flex-wrap: nowrap;
gap: 6px;
}
.favorites-filter {
padding-inline: 8px;
font-size: 12px;
}
}
-352
View File
@@ -1,352 +0,0 @@
/* 我的收藏:1440px 桌面稿,公共个人页头与封面卡复用 profile.css。 */
.favorites-page [hidden] {
display: none !important;
}
.favorites-main {
display: flex;
flex-direction: column;
gap: 24px;
width: min(calc(100% - 48px), var(--page-width));
margin-inline: auto;
padding: 24px 0 40px;
}
.favorites-main.is-empty {
width: 100%;
max-width: none;
gap: 24px;
margin: 0;
padding: 24px 0 40px;
}
.favorites-main.is-empty .favorites-toolbar {
width: min(calc(100% - 48px), var(--page-width));
margin-inline: auto;
}
.favorites-toolbar {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
min-height: 36px;
}
.favorites-filters {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 10px;
min-width: 0;
}
.favorites-filter,
.favorites-manage {
display: inline-flex;
flex: 0 0 auto;
align-items: center;
justify-content: center;
height: 36px;
border: 1px solid var(--line);
border-radius: 999px;
background: transparent;
font-size: 14px;
font-weight: 500;
line-height: 20px;
white-space: nowrap;
}
.favorites-filter {
padding-inline: 16px;
color: var(--text-muted);
}
.favorites-filter.is-active {
border-color: rgba(236, 35, 127, .3);
background: rgba(236, 35, 127, .1);
color: var(--accent-hot);
}
.favorites-manage {
gap: 6px;
padding-inline: 12px;
border-color: rgba(255, 255, 255, .24);
color: var(--text);
cursor: pointer;
}
.favorites-manage img,
.favorites-manage svg {
display: block;
width: 16px;
height: 16px;
flex: 0 0 16px;
}
.favorites-filter:hover,
.favorites-manage:hover {
border-color: rgba(255, 255, 255, .36);
color: var(--text);
}
.favorites-filter:focus-visible,
.favorites-manage:focus-visible,
.favorites-pagination button:focus-visible {
outline: 2px solid var(--accent-hot);
outline-offset: 3px;
}
.favorites-grid {
display: grid;
grid-template-columns: repeat(6, minmax(0, 1fr));
gap: 32px 24px;
min-width: 0;
}
.favorites-grid .favorites-card {
min-width: 0;
}
.favorites-card__mobile-meta,
.favorites-card__remove {
display: none;
}
.favorites-card__media {
position: relative;
min-width: 0;
}
.favorites-page.is-managing .favorites-card__remove {
position: absolute;
top: 6px;
left: 6px;
z-index: 3;
display: inline-flex;
align-items: center;
justify-content: center;
width: 26px;
height: 26px;
border: 1px solid rgba(255, 255, 255, .4);
border-radius: 50%;
background: rgba(11, 11, 13, .78);
color: #fff;
font-size: 18px;
line-height: 1;
}
.favorites-card__remove img {
display: block;
width: 14px;
height: 14px;
}
.favorites-page.is-managing .favorites-manage {
border-color: rgba(236, 35, 127, .3);
background: rgba(236, 35, 127, .1);
color: var(--accent-hot);
}
.favorites-load-more {
display: none;
}
.favorites-pagination,
.favorites-pagination__pages {
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
}
.favorites-pagination {
min-height: 36px;
}
.favorites-pagination button {
display: inline-flex;
align-items: center;
justify-content: center;
flex: 0 0 36px;
width: 36px;
height: 36px;
border: 1px solid var(--line-soft);
border-radius: 8px;
background: var(--bg-elevated);
color: var(--text-muted);
font-family: Inter, var(--font);
font-size: 16px;
font-weight: 600;
line-height: 20px;
}
.favorites-pagination button.is-active {
border-color: transparent;
background: linear-gradient(135deg, #ff4a7b 15%, #ec237f 51%, #93226f 85%);
color: #fff;
}
.favorites-pagination button:not(:disabled):not(.is-active):hover {
border-color: rgba(255, 255, 255, .24);
color: var(--text);
}
.favorites-pagination button:disabled {
cursor: default;
opacity: .45;
}
.favorites-pagination button img,
.favorites-pagination button svg {
display: block;
width: 16px;
height: 16px;
}
.favorites-pagination > .favorites-pagination__button:first-child img {
transform: rotate(180deg);
}
.favorites-pagination__total {
color: var(--text-dim);
font-size: 12px;
line-height: 18px;
white-space: nowrap;
}
.favorites-empty {
display: flex;
flex-direction: column;
align-items: center;
gap: 16px;
width: 100%;
padding: 96px 16px;
font-size: 14px;
line-height: 22px;
text-align: center;
}
.favorites-main.is-empty:has(.favorites-recommendations) .favorites-empty {
padding-bottom: 40px;
}
.favorites-main.is-empty .favorites-recommendations {
padding-top: 8px;
}
.favorites-filter-empty {
margin: 0;
padding: 56px 16px;
color: var(--text-dim);
font-size: 14px;
line-height: 22px;
text-align: center;
}
.favorites-empty__icon {
display: flex;
align-items: center;
justify-content: center;
width: 96px;
height: 96px;
flex: 0 0 96px;
border: 1px solid var(--line);
border-radius: 50%;
background: #1b1b1f;
}
.favorites-empty__icon img {
width: 40px;
height: 40px;
}
.favorites-empty__title {
margin: 0;
color: var(--text);
font-size: 20px;
font-weight: 600;
line-height: 28px;
}
.favorites-empty__description {
margin: 0;
color: var(--text-muted);
font-size: 14px;
font-weight: 400;
line-height: 22px;
}
.favorites-empty__description-mobile,
.favorites-recommendations__more-mobile {
display: none;
}
.favorites-empty__action {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 8px;
width: 147px;
height: 48px;
border-radius: 999px;
background: linear-gradient(105deg, #ff4a7b 0%, #ec237f 52%, #93226f 100%);
color: #fff;
font-size: 14px;
font-weight: 600;
line-height: 20px;
white-space: nowrap;
}
.favorites-empty__action img {
width: 20px;
height: 20px;
}
.favorites-empty__action:focus-visible,
.favorites-recommendations__head a:focus-visible {
outline: 2px solid var(--accent-hot);
outline-offset: 3px;
}
.favorites-recommendations {
width: min(calc(100% - 48px), var(--page-width));
margin-inline: auto;
padding-bottom: 40px;
}
.favorites-recommendations__head {
display: flex;
align-items: center;
justify-content: space-between;
height: 32px;
margin-bottom: 18px;
}
.favorites-recommendations__head h2 {
margin: 0;
color: var(--text);
font-size: 20px;
font-weight: 600;
line-height: 28px;
}
.favorites-recommendations__head a {
display: inline-flex;
align-items: center;
gap: 4px;
color: var(--text-muted);
font-size: 14px;
line-height: 22px;
}
.favorites-recommendations__head a img {
width: 16px;
height: 16px;
}
.favorites-recommendations__grid {
display: grid;
grid-template-columns: repeat(6, minmax(0, 1fr));
gap: 24px;
}
+96
View File
@@ -255,3 +255,99 @@
text-align: center;
}
}
/* 访问地址:横向地址卡片,窄屏改为纵向堆叠 */
.footer-addrs {
display: flex;
flex-direction: column;
gap: 14px;
}
.footer-addrs__head {
display: flex;
align-items: baseline;
gap: 12px;
}
.footer-addrs__head h3 {
margin: 0;
font-size: 14px;
font-weight: 500;
line-height: 20px;
}
.footer-addrs__head p {
margin: 0;
color: var(--text-dim);
font-size: 12px;
line-height: 18px;
}
.footer-addrs__list {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
gap: 10px;
}
.footer-addr {
display: flex;
align-items: center;
gap: 10px;
min-width: 0;
padding: 10px 14px;
border: 1px solid var(--line-soft);
border-left: 3px solid var(--accent);
border-radius: var(--radius-sm);
background: var(--bg-panel);
text-decoration: none;
}
.footer-addr:hover {
border-color: var(--line);
border-left-color: var(--accent-hot);
background: rgba(236, 35, 127, 0.08);
}
.footer-addr__label {
flex-shrink: 0;
color: var(--text-muted);
font-size: 12px;
line-height: 18px;
}
.footer-addr__url {
flex: 1;
min-width: 0;
color: var(--text);
font-size: 13px;
line-height: 18px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.footer-addr__go {
flex-shrink: 0;
opacity: 0.6;
}
@media (max-width: 768px) {
.footer-addrs {
width: 100%;
gap: 10px;
text-align: left;
}
.footer-addrs__head {
justify-content: space-between;
}
.footer-addrs__list {
grid-template-columns: 1fr;
gap: 8px;
}
.footer-addr {
padding: 9px 12px;
}
}
+10
View File
@@ -1194,3 +1194,13 @@
body.drawer-open {
overflow: hidden;
}
/* 手机页头:商务合作 Telegram 入口,与搜索按钮同尺寸,带品牌色底 */
.site-header__tg {
background: rgba(42, 171, 238, 0.14);
}
.site-header__tg .icon {
width: 18px;
height: 18px;
}
-256
View File
@@ -1,256 +0,0 @@
/* 观看历史 H5:对齐 Figma 375px 画板。 */
@media (max-width: 768px) {
.history-main {
display: flex;
flex-direction: column;
gap: 12px;
width: 100%;
max-width: none;
margin: 0;
padding: 12px 16px 20px;
}
.history-toolbar {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
min-width: 0;
min-height: 30px;
}
.history-filters {
display: flex;
flex: 1 1 auto;
align-items: center;
gap: 10px;
min-width: 0;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
scrollbar-width: none;
}
.history-filters::-webkit-scrollbar { display: none; }
.history-filter {
display: inline-flex;
flex: 0 0 auto;
align-items: center;
justify-content: center;
height: 30px;
padding: 0 16px;
border: 1px solid rgba(255, 255, 255, .12);
border-radius: 999px;
background: transparent;
color: #a1a1aa;
font-size: 14px;
font-weight: 500;
line-height: 20px;
white-space: nowrap;
cursor: pointer;
}
.history-filter.is-active {
border-color: rgba(236, 35, 127, .3);
background: rgba(236, 35, 127, .1);
color: #ff4a7b;
}
.history-clear {
display: inline-flex;
flex: 0 0 auto;
align-items: center;
justify-content: center;
height: auto;
padding: 0;
border: 0;
background: transparent;
color: #a1a1aa;
font-size: 12px;
font-weight: 400;
line-height: 18px;
white-space: nowrap;
cursor: pointer;
}
.history-clear img,
.history-clear__desktop { display: none; }
.history-clear__mobile { display: inline; }
.history-group,
.history-list {
display: flex;
flex-direction: column;
width: 100%;
min-width: 0;
}
.history-group__title {
margin: 0;
color: #7c7c86;
font-size: 12px;
font-weight: 500;
line-height: 16px;
}
.history-item {
display: flex;
align-items: center;
gap: 12px;
width: 100%;
min-width: 0;
padding: 12px 0;
border-bottom: 1px solid rgba(255, 255, 255, .07);
}
.history-item__cover {
display: block;
flex: 0 0 48px;
width: 48px;
height: 64px;
overflow: hidden;
border-radius: 6px;
background: #141417;
}
.history-item__image {
display: block;
width: 100%;
height: 100%;
object-fit: cover;
}
.history-item__body {
display: flex;
flex: 1 1 0;
flex-direction: column;
align-items: flex-start;
gap: 5px;
min-width: 0;
}
.history-item__title {
display: block;
align-self: stretch;
overflow: hidden;
margin: 0;
color: #f2f2f4;
font-size: 15px;
font-weight: 600;
line-height: 21px;
text-overflow: ellipsis;
white-space: nowrap;
}
.history-item__title a {
color: inherit;
text-decoration: none;
}
.history-item__details {
display: block;
align-self: stretch;
overflow: hidden;
color: #7c7c86;
font-size: 11px;
font-weight: 400;
line-height: 16px;
text-overflow: ellipsis;
white-space: nowrap;
}
.history-item__episodes,
.history-item__mobile-time {
display: inline;
}
.history-item__mobile-time::before {
content: " ";
white-space: pre;
}
.history-item__genre,
.history-item__time,
.history-item__resume-desktop {
display: none;
}
.history-item__progress {
display: block;
width: 140px;
max-width: 100%;
height: 3px;
overflow: hidden;
border-radius: 999px;
background: rgba(255, 255, 255, .12);
}
.history-item__progress-fill {
display: block;
height: 100%;
border-radius: inherit;
background: linear-gradient(90deg, #ff4a7b 0%, #ec237f 52%, #93226f 100%);
}
.history-item__resume {
display: inline-flex;
flex: 0 0 auto;
align-items: center;
justify-content: center;
gap: 8px;
width: auto;
min-width: 0;
height: 32px;
padding: 0 12px;
border: 1px solid rgba(255, 255, 255, .24);
border-radius: 999px;
background: transparent;
color: #f2f2f4;
font-size: 14px;
font-weight: 500;
line-height: 20px;
text-decoration: none;
white-space: nowrap;
}
.history-item__resume svg,
.history-item__resume img {
flex: 0 0 16px;
width: 16px;
height: 16px;
}
.history-item__resume-mobile {
display: inline;
}
.history-item__remove {
display: inline-flex;
flex: 0 0 14px;
align-items: center;
justify-content: center;
width: 14px;
height: 14px;
padding: 0;
border: 0;
background: transparent;
color: #7c7c86;
cursor: pointer;
}
.history-item__remove svg,
.history-item__remove img {
display: block;
width: 14px;
height: 14px;
}
.history-main .account-pagination {
margin-top: 4px;
}
.history-empty,
.history-main .account-empty {
padding: 48px 0;
}
}
-256
View File
@@ -1,256 +0,0 @@
/* 观看历史:1440px Figma 桌面画板,沿用个人中心公共页头与页签。 */
.history-page [hidden] { display: none !important; }
.history-main {
display: flex;
flex-direction: column;
gap: 24px;
width: min(calc(100% - 48px), var(--page-width));
margin-inline: auto;
padding: 24px 0 40px;
}
.history-toolbar {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
min-height: 36px;
}
.history-filters {
display: flex;
align-items: center;
gap: 10px;
}
.history-filter,
.history-clear,
.history-item__resume {
display: inline-flex;
flex: 0 0 auto;
align-items: center;
justify-content: center;
height: 36px;
padding: 0;
border: 1px solid var(--line);
border-radius: 999px;
background: transparent;
color: var(--text-muted);
font-size: 14px;
font-weight: 500;
line-height: 20px;
white-space: nowrap;
cursor: pointer;
}
.history-filter {
padding-inline: 16px;
}
.history-filter.is-active {
border-color: rgba(236, 35, 127, .3);
background: rgba(236, 35, 127, .1);
color: var(--accent-hot);
}
.history-clear {
gap: 6px;
padding-inline: 12px;
border-color: rgba(255, 255, 255, .24);
color: var(--text);
}
.history-clear__mobile { display: none; }
.history-clear svg,
.history-clear img,
.history-item__resume svg,
.history-item__resume img,
.history-item__remove svg,
.history-item__remove img {
display: block;
width: 16px;
height: 16px;
flex: 0 0 16px;
}
.history-filter:hover,
.history-clear:hover,
.history-item__resume:hover,
.history-item__remove:hover {
border-color: rgba(255, 255, 255, .36);
color: var(--text);
}
.history-filter:focus-visible,
.history-clear:focus-visible,
.history-item__resume:focus-visible,
.history-item__remove:focus-visible {
outline: 2px solid var(--accent-hot);
outline-offset: 3px;
}
.history-group {
min-width: 0;
}
.history-group__title {
margin: 0;
color: var(--text-dim);
font-size: 12px;
font-weight: 500;
line-height: 16px;
}
.history-list {
margin: 0;
padding: 0;
list-style: none;
}
.history-item {
display: grid;
grid-template-columns: 56px minmax(0, 1fr) 120px 106px 16px;
align-items: center;
gap: 16px;
min-width: 0;
min-height: 104px;
padding-block: 14px;
border-bottom: 1px solid var(--line-soft);
}
.history-item__cover {
display: block;
width: 56px;
height: 75px;
overflow: hidden;
border-radius: 6px;
background: var(--bg-panel);
}
.history-item__image {
display: block;
width: 100%;
height: 100%;
object-fit: cover;
}
.history-item__body {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 6px;
min-width: 0;
}
.history-item__title {
display: block;
max-width: 100%;
margin: 0;
overflow: hidden;
color: var(--text);
font-size: 16px;
font-weight: 600;
line-height: 22px;
text-overflow: ellipsis;
white-space: nowrap;
}
.history-item__title a {
color: inherit;
text-decoration: none;
}
.history-item__title a:hover {
color: var(--accent-hot);
}
.history-item__details {
display: flex;
align-items: center;
gap: 8px;
max-width: 100%;
overflow: hidden;
color: #a1a1aa;
font-size: 13px;
font-weight: 400;
line-height: 20px;
white-space: nowrap;
}
.history-item__episodes,
.history-item__genre {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.history-item__episodes {
flex: 0 0 auto;
}
.history-item__progress {
display: block;
width: 240px;
max-width: 100%;
height: 3px;
overflow: hidden;
border-radius: 999px;
background: var(--line);
}
.history-item__progress-fill {
display: block;
width: var(--progress, 0%);
height: 100%;
border-radius: inherit;
background: linear-gradient(90deg, #ff4a7b 0%, #ec237f 52%, #93226f 100%);
}
.history-item__time {
color: var(--text-dim);
font-size: 12px;
line-height: 18px;
white-space: nowrap;
}
.history-item__mobile-time,
.history-item__resume-mobile {
display: none;
}
.history-item__resume {
gap: 8px;
width: 106px;
padding-inline: 12px;
border-color: rgba(255, 255, 255, .24);
color: var(--text);
text-decoration: none;
}
.history-item__remove {
display: inline-flex;
align-items: center;
justify-content: center;
width: 16px;
height: 36px;
padding: 0;
border: 0;
background: transparent;
color: var(--text-dim);
cursor: pointer;
}
.history-group + .history-group {
margin-top: 0;
}
.history-main.is-empty {
gap: 24px;
padding: 24px 0 40px;
}
.history-main.is-empty .account-empty {
padding-block: 72px;
}
+75
View File
@@ -1364,3 +1364,78 @@
width: 100%;
}
}
/* 公告跑马灯:官方访问地址以金色描边胶囊展示 */
.announce__addrs {
display: inline-flex;
align-items: center;
gap: 8px;
}
.announce__addrs > span {
color: var(--gold);
}
.announce__addr {
display: inline-flex;
align-items: center;
height: 22px;
padding: 0 10px;
border: 1px solid rgba(233, 201, 119, 0.45);
border-radius: 999px;
color: var(--text);
font-size: 12px;
line-height: 20px;
text-decoration: none;
background: rgba(233, 201, 119, 0.08);
}
.announce__addr:hover {
border-color: var(--gold);
background: rgba(233, 201, 119, 0.16);
}
@media (max-width: 768px) {
.announce__addr {
height: 20px;
padding: 0 8px;
font-size: 11px;
line-height: 18px;
}
}
/* Banner 可读性(桌面端):背景海报保持原样,只在左侧文字区域加一道渐变 */
@media (min-width: 769px) {
.hero__shade--side {
background:
linear-gradient(90deg, #0b0b0d 0%, rgba(11, 11, 13, 0.78) 16%, rgba(11, 11, 13, 0.45) 36%, rgba(11, 11, 13, 0) 52%),
linear-gradient(270deg, #0b0b0d 0%, rgba(11, 11, 13, 0.72) 8%, rgba(11, 11, 13, 0) 24%);
}
.hero__title,
.hero__desc {
text-shadow: 0 2px 12px rgba(0, 0, 0, 0.6);
}
}
.hero__attrs {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 6px 12px;
}
@media (max-width: 768px) {
/* 手机端背景即主画面,不模糊;拉高底部渐变保证文字可读 */
.hero__shade--bottom {
background: linear-gradient(180deg, rgba(11, 11, 13, 0) 0%, rgba(11, 11, 13, 0.25) 28%, rgba(11, 11, 13, 0.82) 56%, #0b0b0d 78%);
}
.hero__desc {
-webkit-line-clamp: 2;
}
.hero__attrs {
gap: 4px 10px;
}
}
-283
View File
@@ -1,283 +0,0 @@
/* H5 登录页 */
body.page-login {
min-height: 100vh;
background: var(--bg);
}
.page-login .page-subheader {
display: flex;
border-bottom: 0;
}
.login-page {
min-height: 100vh;
padding: 56px 0 32px;
box-sizing: border-box;
}
.login-page__inner {
width: 100%;
max-width: 440px;
margin: 0 auto;
padding: 24px var(--h5-gutter);
display: flex;
flex-direction: column;
align-items: center;
gap: 24px;
box-sizing: border-box;
}
.login-page__brand {
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
}
.login-page__logo {
width: 64px;
height: 64px;
border-radius: 12px;
object-fit: cover;
box-shadow: 0 8px 24px rgba(236, 35, 127, 0.4);
}
.login-page__content {
align-self: stretch;
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 18px;
}
.login-page__intro {
align-self: stretch;
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 6px;
}
.login-page__title {
margin: 0;
color: var(--text);
font-size: 28px;
font-weight: 600;
line-height: 38px;
}
.login-page__desc {
margin: 0;
color: var(--text-muted);
font-size: 14px;
line-height: 22px;
}
.login-page__form {
align-self: stretch;
display: flex;
flex-direction: column;
gap: 16px;
}
.login-page__field {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 8px;
}
.login-page__label {
color: var(--text-muted);
font-size: 14px;
font-weight: 500;
line-height: 20px;
}
.login-page__control {
align-self: stretch;
height: 48px;
padding: 0 16px;
display: flex;
align-items: center;
gap: 10px;
background: rgba(255, 255, 255, 0.06);
border: 1px solid transparent;
border-radius: 12px;
outline: 1px solid transparent;
outline-offset: -1px;
}
.login-page__control:focus-within {
outline-color: rgba(236, 35, 127, 0.45);
}
.login-page__field.is-error .login-page__control {
outline-color: #ff5a5f;
}
.login-page__error {
display: inline-flex;
align-items: center;
gap: 6px;
color: #ff5a5f;
font-size: 12px;
font-weight: 400;
line-height: 18px;
}
.login-page__error[hidden] {
display: none !important;
}
.login-page__error-icon {
width: 14px;
height: 14px;
flex-shrink: 0;
}
.login-page__control > .icon {
width: 18px;
height: 18px;
flex-shrink: 0;
}
.login-page__input {
flex: 1 1 0;
min-width: 0;
height: 100%;
padding: 0;
border: 0;
outline: none;
background: transparent;
color: var(--text);
font: inherit;
font-size: 15px;
line-height: 24px;
}
.login-page__input::placeholder {
color: var(--text-dim);
}
.login-page__toggle {
width: 18px;
height: 18px;
flex-shrink: 0;
display: inline-flex;
align-items: center;
justify-content: center;
color: var(--text-dim);
}
.login-page__toggle .icon {
width: 18px;
height: 18px;
}
.login-page__meta {
align-self: stretch;
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
}
.login-page__remember {
display: inline-flex;
align-items: center;
gap: 8px;
color: var(--text-muted);
font-size: 13px;
line-height: 20px;
cursor: pointer;
}
.login-page__checkbox {
position: absolute;
opacity: 0;
width: 1px;
height: 1px;
pointer-events: none;
}
.login-page__check {
width: 18px;
height: 18px;
flex-shrink: 0;
display: inline-flex;
align-items: center;
justify-content: center;
border-radius: 4px;
background: rgba(255, 255, 255, 0.06);
border: 1px solid var(--line);
}
.login-page__checkbox:checked + .login-page__check {
border-color: transparent;
background: linear-gradient(135deg, #ff4a7b 0%, #ec237f 52%, #93226f 100%);
}
.login-page__checkbox:checked + .login-page__check::after {
content: "";
width: 10px;
height: 6px;
border-left: 1.5px solid #fff;
border-bottom: 1.5px solid #fff;
transform: translateY(-1px) rotate(-45deg);
}
.login-page__forgot {
color: #ff4a7b;
font-size: 13px;
line-height: 20px;
}
.login-page__submit {
align-self: stretch;
height: 52px;
padding: 0 28px;
font-size: 16px;
line-height: 24px;
}
.login-page__switch {
margin: 0;
align-self: stretch;
display: flex;
align-items: center;
justify-content: center;
gap: 6px;
color: var(--text-muted);
font-size: 14px;
line-height: 22px;
}
.login-page__switch a {
color: #ff4a7b;
font-size: 14px;
font-weight: 500;
line-height: 20px;
}
.login-page__rule {
align-self: stretch;
height: 1px;
background: var(--line-soft);
}
.login-page__legal {
margin: 0;
align-self: stretch;
text-align: center;
color: var(--text-dim);
font-size: 12px;
line-height: 18px;
}
.login-page__legal a {
color: inherit;
text-decoration: underline;
text-underline-offset: 2px;
}
-308
View File
@@ -1,308 +0,0 @@
/* PC 登录弹窗 */
.login-pop {
position: fixed;
inset: 0;
z-index: 290;
display: none;
align-items: center;
justify-content: center;
padding: 24px;
}
.login-pop.is-open {
display: flex;
}
.login-pop__mask {
position: absolute;
inset: 0;
background: rgba(0, 0, 0, 0.7);
}
.login-pop__panel {
position: relative;
z-index: 1;
width: 440px;
max-width: 100%;
padding: 32px;
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 20px;
background: #141417;
border: 1px solid var(--line);
border-radius: var(--radius-xl);
box-shadow: 0 24px 64px rgba(0, 0, 0, 0.55);
overflow: hidden;
}
.login-pop__close {
position: absolute;
top: 16px;
right: 16px;
width: 32px;
height: 32px;
display: inline-flex;
align-items: center;
justify-content: center;
background: rgba(255, 255, 255, 0.04);
border-radius: 999px;
}
.login-pop__close .icon {
width: 18px;
height: 18px;
}
.login-pop__intro {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 6px;
padding-right: 40px;
}
.login-pop__title {
margin: 0;
color: var(--text);
font-size: 28px;
font-weight: 600;
line-height: 38px;
}
.login-pop__desc {
margin: 0;
color: var(--text-muted);
font-size: 14px;
line-height: 22px;
}
.login-pop__form {
align-self: stretch;
display: flex;
flex-direction: column;
gap: 16px;
}
.login-pop__field {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 8px;
}
.login-pop__label {
color: var(--text-muted);
font-size: 14px;
font-weight: 500;
line-height: 20px;
}
.login-pop__control {
align-self: stretch;
height: 48px;
padding: 0 16px;
display: flex;
align-items: center;
gap: 10px;
background: rgba(255, 255, 255, 0.06);
border: 1px solid transparent;
border-radius: 12px;
outline: 1px solid transparent;
outline-offset: -1px;
}
.login-pop__control:focus-within {
outline-color: rgba(236, 35, 127, 0.45);
}
.login-pop__field.is-error .login-pop__control {
outline-color: #ff5a5f;
}
.login-pop__error {
display: inline-flex;
align-items: center;
gap: 6px;
color: #ff5a5f;
font-size: 12px;
font-weight: 400;
line-height: 18px;
}
.login-pop__error[hidden] {
display: none !important;
}
.login-pop__error-icon {
width: 14px;
height: 14px;
flex-shrink: 0;
}
.login-pop__control > .icon {
width: 18px;
height: 18px;
flex-shrink: 0;
color: var(--text-dim);
}
.login-pop__input {
flex: 1 1 0;
min-width: 0;
height: 100%;
padding: 0;
border: 0;
outline: none;
background: transparent;
color: var(--text);
font: inherit;
font-size: 15px;
line-height: 24px;
}
.login-pop__input::placeholder {
color: var(--text-dim);
}
.login-pop__toggle {
width: 18px;
height: 18px;
flex-shrink: 0;
display: inline-flex;
align-items: center;
justify-content: center;
color: var(--text-dim);
}
.login-pop__toggle .icon {
width: 18px;
height: 18px;
}
.login-pop__meta {
align-self: stretch;
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
}
.login-pop__remember {
display: inline-flex;
align-items: center;
gap: 8px;
color: var(--text-muted);
font-size: 13px;
line-height: 20px;
cursor: pointer;
}
.login-pop__checkbox {
position: absolute;
opacity: 0;
width: 1px;
height: 1px;
pointer-events: none;
}
.login-pop__check {
width: 18px;
height: 18px;
flex-shrink: 0;
display: inline-flex;
align-items: center;
justify-content: center;
border-radius: 4px;
background: rgba(255, 255, 255, 0.06);
border: 1px solid var(--line);
}
.login-pop__checkbox:checked + .login-pop__check {
border-color: transparent;
background: linear-gradient(135deg, #ff4a7b 0%, #ec237f 52%, #93226f 100%);
}
.login-pop__checkbox:checked + .login-pop__check::after {
content: "";
width: 10px;
height: 6px;
border-left: 1.5px solid #fff;
border-bottom: 1.5px solid #fff;
transform: translateY(-1px) rotate(-45deg);
}
.login-pop__submit {
align-self: stretch;
height: 52px;
padding: 0 28px;
font-size: 16px;
line-height: 24px;
}
.login-pop__submit.is-loading,
.login-pop__submit:disabled {
opacity: 0.72;
cursor: wait;
}
.login-pop__tip {
margin: 0;
align-self: stretch;
padding: 8px 12px;
border-radius: 8px;
font-size: 13px;
line-height: 20px;
background: rgba(255, 74, 123, 0.1);
color: #ff4a7b;
}
.login-pop__tip.is-success {
background: rgba(30, 120, 70, 0.12);
color: #1e7846;
}
.login-pop__tip[hidden] {
display: none !important;
}
.login-pop__rule {
align-self: stretch;
height: 1px;
background: var(--line-soft);
}
.login-pop__legal {
margin: 0;
align-self: stretch;
text-align: center;
color: var(--text-dim);
font-size: 12px;
line-height: 18px;
}
.login-pop__legal a {
color: inherit;
text-decoration: underline;
text-underline-offset: 2px;
}
@media (max-width: 768px) {
.login-pop {
padding: 16px;
}
.login-pop__panel {
max-height: calc(100vh - 32px);
max-height: calc(100dvh - 32px);
padding: 24px 20px;
border-radius: 18px;
overflow-y: auto;
}
.login-pop__title {
font-size: 24px;
line-height: 32px;
}
}
+44 -4
View File
@@ -980,10 +980,8 @@ a.play-meta__tag:focus-visible {
width: 12px;
height: 12px;
border-radius: 999px;
background:
url("/static/images/coin.png") center / 8px 8px no-repeat,
linear-gradient(135deg, #e9c977 0%, #d9a94a 100%);
border: 1px solid #e9c977;
background: url("/static/images/lock.svg") center / 8px 8px no-repeat, var(--accent-deep);
border: 1px solid var(--accent);
box-sizing: border-box;
}
@@ -1817,3 +1815,45 @@ a.play-meta__tag:focus-visible {
line-height: 18px;
}
}
/* 简介下方标签行 */
.play-synopsis__tags {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-top: 12px;
}
.play-synopsis__tag {
padding: 3px 10px;
border-radius: var(--radius-sm);
color: var(--text-muted);
font-size: 12px;
line-height: 18px;
text-decoration: none;
background: rgba(255, 255, 255, 0.06);
}
.play-synopsis__tag::before {
content: "#";
margin-right: 2px;
color: var(--accent);
}
a.play-synopsis__tag:hover {
color: var(--text);
background: rgba(236, 35, 127, 0.12);
}
@media (max-width: 768px) {
.play-synopsis__tags {
gap: 6px;
margin-top: 10px;
}
.play-synopsis__tag {
padding: 2px 8px;
font-size: 11px;
line-height: 16px;
}
}
-590
View File
@@ -1,590 +0,0 @@
/* 个人中心 H5:以 Figma 375px 画板为基准。 */
@media (max-width: 768px) {
.profile-page {
--profile-gutter: clamp(14px, 4.266667vw, 18px);
--profile-card-gap: clamp(7px, 2.133333vw, 9px);
width: 100%;
min-width: 0;
padding-top: 0;
background: #0b0b0d;
}
.profile-hero {
width: 100%;
padding-top: 56px; /* 公共导航固定在视口顶部 */
background: linear-gradient(180deg, #2a0d22 0%, #150a17 60%, #0b0b0d 100%);
}
.profile-hero::before {
top: -90px;
left: -60px;
width: 360px;
height: 220px;
filter: blur(55px);
opacity: 0.22;
}
.profile-hero__inner {
display: block;
grid-template-columns: none;
width: 100%;
max-width: none;
margin: 0;
padding: 0;
}
.profile-identity {
display: flex;
align-items: center;
gap: clamp(12px, 3.733333vw, 16px);
min-width: 0;
margin: 0;
padding: 12px var(--profile-gutter) 16px;
}
.profile-identity__avatar {
display: block;
flex: 0 0 clamp(55px, 17.066667vw, 73px);
width: clamp(55px, 17.066667vw, 73px);
height: clamp(55px, 17.066667vw, 73px);
border: 2px solid rgba(255, 255, 255, 0.24);
border-radius: 50%;
object-fit: cover;
}
.profile-identity__copy {
display: grid;
flex: 1 1 0;
grid-template-columns: minmax(0, 1fr) auto;
grid-template-rows: 26px 16px;
column-gap: 12px;
row-gap: 6px;
min-width: 0;
}
.profile-identity__name {
grid-column: 1;
grid-row: 1;
overflow: hidden;
color: #f2f2f4;
font-size: 18px;
font-weight: 600;
line-height: 26px;
text-overflow: ellipsis;
white-space: nowrap;
}
.profile-identity__meta {
grid-column: 1;
grid-row: 2;
overflow: hidden;
color: #a1a1aa;
font-size: 11px;
font-weight: 400;
line-height: 16px;
text-overflow: ellipsis;
white-space: nowrap;
}
.profile-identity__edit {
display: inline-flex;
grid-column: 2;
grid-row: 1 / 3;
align-self: center;
flex: 0 0 auto;
align-items: center;
justify-content: center;
gap: 8px;
height: 32px;
padding: 0 8px;
color: #f2f2f4;
border: 1px solid rgba(255, 255, 255, 0.24);
border-radius: 999px;
font-size: 0;
font-weight: 500;
line-height: 0;
white-space: nowrap;
}
.profile-identity__edit::after {
content: "编辑";
font-size: 14px;
line-height: 20px;
}
.profile-identity__edit .icon,
.profile-identity__edit img,
.profile-identity__edit svg {
width: 16px;
height: 16px;
}
.profile-wallet {
position: relative;
display: flex;
align-items: center;
justify-content: space-between;
gap: 10px;
min-height: 101px;
margin: 0 var(--profile-gutter) 16px;
padding: 14px;
background: #141417;
border: 1px solid rgba(255, 255, 255, 0.07);
border-radius: 12px;
}
.profile-wallet__label {
display: flex;
align-items: center;
gap: 6px;
color: #f2f2f4;
font-size: 15px;
font-weight: 600;
line-height: 21px;
}
.profile-wallet__amount {
display: block;
margin-top: 4px;
color: #e9c977;
font-family: Inter, var(--font);
font-size: 20px;
font-weight: 600;
line-height: 26px;
}
.profile-wallet__hint {
margin: 4px 0 0;
color: #7c7c86;
font-size: 11px;
line-height: 16px;
white-space: nowrap;
}
.profile-wallet__actions {
display: flex;
flex: 0 0 auto;
align-items: center;
}
.profile-wallet__recharge {
display: inline-flex;
align-items: center;
justify-content: center;
height: 32px;
padding: 0 12px;
color: #fff;
background: linear-gradient(135deg, #ff4a7b 0%, #ec237f 52%, #93226f 100%);
border: 0;
border-radius: 999px;
font-size: 14px;
font-weight: 500;
line-height: 20px;
white-space: nowrap;
}
.profile-wallet__details {
display: none;
}
.profile-stats {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
margin: 0;
min-height: 64px;
padding: 12px var(--profile-gutter) 14px;
border-top: 1px solid rgba(255, 255, 255, 0.07);
}
.profile-stat {
display: flex;
flex: 0 0 auto;
flex-direction: column;
align-items: center;
gap: 2px;
min-width: 0;
text-align: center;
}
.profile-stat__value {
color: #f2f2f4;
font-family: Inter, var(--font);
font-size: 16px;
font-weight: 600;
line-height: 20px;
}
.profile-stat__value span { display: none; }
.profile-stat__label {
color: #7c7c86;
font-size: 11px;
font-weight: 400;
line-height: 16px;
white-space: nowrap;
}
.profile-tabs {
width: 100%;
margin: 0;
padding-top: 0;
border-bottom: 1px solid rgba(255, 255, 255, 0.07);
}
.profile-tabs__inner {
display: flex;
align-items: flex-end;
gap: 24px;
width: 100%;
max-width: none;
min-height: 38px;
height: 38px;
margin: 0;
padding: 6px var(--profile-gutter) 0;
overflow-x: auto;
overflow-y: hidden;
scrollbar-width: none;
-webkit-overflow-scrolling: touch;
}
.profile-tabs__inner::-webkit-scrollbar { display: none; }
.profile-tabs__link {
position: relative;
display: inline-flex;
flex: 0 0 auto;
align-items: flex-start;
justify-content: center;
min-height: 31px;
padding: 0 0 10px;
color: #a1a1aa;
font-size: 15px;
font-weight: 600;
line-height: 21px;
white-space: nowrap;
}
.profile-tabs__link.is-active { color: #f2f2f4; }
.profile-tabs__link.is-active::after {
content: "";
position: absolute;
bottom: 0;
left: 50%;
width: 20px;
height: 2px;
background: linear-gradient(90deg, #ff4a7b 0%, #ec237f 52%, #93226f 100%);
transform: translateX(-50%);
}
.profile-main {
display: block;
width: 100%;
max-width: none;
min-width: 0;
margin: 0;
padding: 0;
}
.profile-section {
display: flex;
flex-direction: column;
gap: 14px;
width: 100%;
max-width: none;
margin: 0;
padding-right: var(--profile-gutter);
padding-left: var(--profile-gutter);
}
.profile-section--recent { padding-top: 16px; padding-bottom: 8px; }
.profile-section--favorites { padding-top: 12px; padding-bottom: 8px; }
.profile-section__head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
min-height: 26px;
}
.profile-section__heading {
display: flex;
align-items: baseline;
gap: 10px;
min-width: 0;
}
.profile-section__title {
margin: 0;
color: #f2f2f4;
font-size: 18px;
font-weight: 600;
line-height: 26px;
white-space: nowrap;
}
.profile-section__subtitle {
color: #7c7c86;
font-size: 12px;
line-height: 18px;
white-space: nowrap;
}
.profile-section--recent .profile-section__subtitle { display: none; }
.profile-section__more {
display: inline-flex;
flex: 0 0 auto;
align-items: center;
gap: 2px;
color: #a1a1aa;
font-size: 13px;
line-height: 20px;
white-space: nowrap;
}
.profile-section__more .icon,
.profile-section__more img,
.profile-section__more svg { width: 14px; height: 14px; }
.profile-grid {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 16px var(--profile-card-gap);
width: 100%;
min-width: 0;
}
.profile-section--recent .profile-card:nth-child(n + 4) { display: none; }
.profile-card {
display: flex;
flex-direction: column;
gap: 6px;
min-width: 0;
}
.profile-card__cover {
position: relative;
display: block;
width: 100%;
aspect-ratio: 109 / 145;
overflow: hidden;
background: #1b1b1f;
border: 1px solid rgba(255, 255, 255, 0.07);
border-radius: 8px;
}
.profile-card__cover::after {
content: "";
position: absolute;
inset: 0;
z-index: 1;
background: linear-gradient(180deg, transparent 58%, rgba(0, 0, 0, 0.7) 100%);
pointer-events: none;
}
.profile-card__image {
display: block;
width: 100%;
height: 100%;
object-fit: cover;
}
.profile-card__progress {
position: absolute;
right: 10px;
bottom: 6px;
left: 10px;
z-index: 2;
height: 3px;
overflow: hidden;
background: rgba(255, 255, 255, 0.12);
border-radius: 999px;
}
.profile-card__progress-fill {
display: block;
width: 60%;
height: 100%;
background: linear-gradient(90deg, #ff4a7b 0%, #ec237f 52%, #93226f 100%);
}
.profile-section--recent .profile-card:nth-child(1) .profile-card__progress-fill { width: 59.6%; }
.profile-section--recent .profile-card:nth-child(2) .profile-card__progress-fill { width: 24.7%; }
.profile-section--recent .profile-card:nth-child(3) .profile-card__progress-fill { width: 79.8%; }
.profile-card__episodes {
position: absolute;
bottom: 10px;
left: 10px;
z-index: 2;
color: #f2f2f4;
font-size: 11px;
font-weight: 500;
line-height: 14px;
white-space: nowrap;
}
.profile-card__badge {
position: absolute;
top: 4px;
right: 4px;
z-index: 2;
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 30px;
height: 20px;
padding: 0 5px;
color: #141414;
background: linear-gradient(135deg, #fadc44 15%, #f9c24e 85%);
border-radius: 6px;
font-size: 11px;
font-weight: 500;
line-height: 14px;
white-space: nowrap;
}
.profile-card__badge:not(.profile-card__badge--hot) {
color: #141414;
background: linear-gradient(135deg, #fadc44 15%, #f9c24e 85%);
}
.profile-card__badge--hot,
.profile-card__badge[data-badge="hot"] {
color: #fff;
background: linear-gradient(135deg, #f04050 15%, #d01030 85%);
}
.profile-card__title {
display: block;
overflow: hidden;
color: #f2f2f4;
font-size: 14px;
font-weight: 600;
line-height: 20px;
text-overflow: ellipsis;
white-space: nowrap;
}
.profile-card__meta {
display: block;
overflow: hidden;
color: #a1a1aa;
font-size: 11px;
font-weight: 400;
line-height: 16px;
text-overflow: ellipsis;
white-space: nowrap;
}
.profile-card__remaining { display: none; }
.profile-promo {
display: flex;
flex-direction: column;
gap: 8px;
width: 100%;
max-width: none;
margin: 0;
padding: 8px var(--profile-gutter);
}
.profile-ad {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 4px;
width: 100%;
aspect-ratio: 343 / 98;
min-height: 0;
background: #232328;
border: 1px solid rgba(255, 255, 255, 0.07);
border-radius: 12px;
text-align: center;
}
.profile-ad__icon { width: 20px; height: 20px; object-fit: contain; }
.profile-ad__title {
color: #a1a1aa;
font-size: 15px;
font-weight: 600;
line-height: 21px;
}
.profile-ad__mobile-size { display: inline; }
.profile-ad__desktop-size { display: none; }
.profile-ad__meta {
color: #7c7c86;
font-size: 11px;
line-height: 16px;
}
.profile-ad__badge {
position: absolute;
top: 8px;
right: 8px;
display: inline-flex;
align-items: center;
justify-content: center;
height: 20px;
padding: 0 6px;
color: #f2f2f4;
background: rgba(0, 0, 0, 0.6);
border-radius: 999px;
font-size: 11px;
font-weight: 500;
line-height: 14px;
}
/* The shared footer's compact profile arrangement in the mobile design. */
.profile-page .site-footer__brand { gap: 18px; }
.profile-page .site-footer__links {
flex-wrap: wrap;
gap: 12px 16px;
}
.profile-page .footer-col { flex-wrap: nowrap; }
.profile-page .footer-col:first-child,
.profile-page .footer-col:nth-child(2) a:last-child,
.profile-page .footer-col:nth-child(3) a:last-child { display: none; }
.profile-page .footer-download {
display: flex;
flex-basis: 100%;
justify-content: center;
padding: 0;
border: 0;
background: none;
}
.profile-page .footer-download .qr-box,
.profile-page .footer-download__copy strong,
.profile-page .footer-download__copy p,
.profile-page .footer-download__copy .icon { display: none; }
.profile-page .footer-download .btn {
height: 20px;
padding: 0;
color: var(--text-muted);
background: none;
font-size: 13px;
line-height: 20px;
}
.profile-page .site-footer__rule { display: none; }
.profile-page .site-footer__legal p:first-child {
order: 2;
width: 100%;
padding-top: 16px;
margin-top: 16px;
border-top: 1px solid var(--line-soft);
}
.profile-page .site-footer__legal p:last-child { order: 1; }
.profile-page .site-footer__adult { display: none; }
.profile-page .site-footer__rights { display: none; }
.profile-page .site-footer__mobile-adult { display: inline; }
}
-557
View File
@@ -1,557 +0,0 @@
/* Personal center: desktop layout based on the 1440px design frame. */
.profile-page {
min-height: 100vh;
padding-top: var(--site-header-height, 72px);
background: var(--bg);
}
.profile-hero {
position: relative;
overflow: hidden;
background: linear-gradient(180deg, #2a0d22 0%, #150a17 60%, #0b0b0d 100%);
}
.profile-hero::before {
position: absolute;
top: -140px;
left: -120px;
width: 620px;
height: 360px;
border-radius: 50%;
background: var(--accent);
filter: blur(80px);
opacity: .2;
content: "";
pointer-events: none;
}
.profile-hero__inner,
.profile-stats,
.profile-tabs,
.profile-main,
.profile-promo {
width: min(calc(100% - 48px), var(--page-width));
margin-inline: auto;
}
.profile-hero__inner {
position: relative;
display: flex;
align-items: center;
justify-content: space-between;
gap: 32px;
padding-block: 36px 28px;
}
.profile-identity {
display: flex;
align-items: center;
gap: 24px;
min-width: 0;
}
.profile-identity__avatar {
width: 96px;
height: 96px;
flex: 0 0 96px;
border: 3px solid rgba(255, 255, 255, .24);
border-radius: 50%;
object-fit: cover;
}
.profile-identity__copy {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 8px;
min-width: 0;
}
.profile-identity__name {
max-width: 100%;
margin: 0;
overflow: hidden;
color: var(--text);
font-size: 32px;
font-weight: 600;
line-height: 40px;
letter-spacing: -.01em;
text-overflow: ellipsis;
white-space: nowrap;
}
.profile-identity__meta {
margin: 0;
color: var(--text-muted);
font-size: 13px;
line-height: 18px;
letter-spacing: .06em;
}
.profile-identity__edit {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 8px;
height: 36px;
padding-inline: 12px;
border: 1px solid rgba(255, 255, 255, .24);
border-radius: 999px;
color: var(--text);
font-size: 14px;
font-weight: 500;
line-height: 20px;
white-space: nowrap;
}
.profile-identity__edit img,
.profile-wallet__label img {
width: 16px;
height: 16px;
flex: 0 0 16px;
}
.profile-wallet {
display: flex;
align-items: center;
gap: 28px;
min-height: 120px;
padding: 18px 24px;
border: 1px solid var(--line-soft);
border-radius: 16px;
background: var(--bg-elevated);
}
.profile-wallet__label {
display: flex;
align-items: center;
gap: 8px;
margin: 0;
color: var(--text);
font-size: 15px;
font-weight: 600;
line-height: 21px;
white-space: nowrap;
}
.profile-wallet__amount {
display: block;
margin: 4px 0 0;
color: var(--gold);
font-family: Inter, var(--font);
font-size: 28px;
font-weight: 600;
line-height: 34px;
}
.profile-wallet__hint {
margin: 2px 0 0;
color: var(--text-dim);
font-size: 12px;
line-height: 18px;
white-space: nowrap;
}
.profile-wallet__actions {
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
flex: 0 0 auto;
}
.profile-wallet__recharge {
display: inline-flex;
align-items: center;
justify-content: center;
height: 40px;
padding-inline: 16px;
border-radius: 999px;
background: linear-gradient(135deg, #ff4a7b 15%, #ec237f 51%, #93226f 85%);
color: #fff;
font-size: 14px;
font-weight: 500;
line-height: 20px;
white-space: nowrap;
}
.profile-wallet__details {
color: var(--text-muted);
font-size: 12px;
line-height: 18px;
white-space: nowrap;
text-decoration: none;
}
.profile-wallet__details:hover {
color: var(--text);
}
.profile-stats {
position: relative;
display: flex;
align-items: center;
gap: 40px;
padding: 18px 0 22px;
border-top: 1px solid var(--line-soft);
}
.profile-stat {
display: flex;
flex-direction: column;
gap: 2px;
}
.profile-stat__value {
color: var(--text);
font-family: Inter, var(--font);
font-size: 20px;
font-weight: 600;
line-height: 26px;
white-space: nowrap;
}
.profile-stat__label {
color: var(--text-dim);
font-size: 12px;
line-height: 18px;
}
.profile-tabs {
padding-top: 4px;
border-bottom: 1px solid var(--line-soft);
}
.profile-tabs__inner {
display: flex;
align-items: flex-start;
gap: 32px;
height: 38px;
}
.profile-tabs__link {
position: relative;
display: inline-flex;
flex-direction: column;
align-items: center;
height: 100%;
padding: 4px 0 0;
color: var(--text-muted);
font-size: 16px;
font-weight: 600;
line-height: 22px;
white-space: nowrap;
}
.profile-tabs__link::after {
position: absolute;
bottom: 0;
left: 50%;
width: 24px;
height: 2px;
transform: translateX(-50%);
background: linear-gradient(90deg, #ff4a7b 0%, #ec237f 52%, #93226f 100%);
content: "";
opacity: 0;
}
.profile-tabs__link.is-active {
color: var(--text);
}
.profile-tabs__link.is-active::after {
opacity: 1;
}
.profile-section {
display: flex;
flex-direction: column;
gap: 20px;
}
.profile-section--recent {
padding-block: 28px 20px;
}
.profile-section--favorites {
padding-block: 20px;
}
.profile-section__head,
.profile-section__heading {
display: flex;
align-items: center;
}
.profile-section__head {
justify-content: space-between;
gap: 20px;
}
.profile-section__heading {
gap: 14px;
min-width: 0;
}
.profile-section__title {
margin: 0;
color: var(--text);
font-size: 22px;
font-weight: 600;
line-height: 30px;
white-space: nowrap;
}
.profile-section__subtitle {
color: var(--text-dim);
font-size: 14px;
line-height: 22px;
white-space: nowrap;
}
.profile-section__more {
display: inline-flex;
align-items: center;
gap: 4px;
color: var(--text-muted);
font-size: 14px;
line-height: 22px;
white-space: nowrap;
}
.profile-section__more img {
width: 16px;
height: 16px;
}
.profile-grid {
display: grid;
grid-template-columns: repeat(6, minmax(0, 1fr));
gap: 32px 24px;
}
.profile-section__empty {
grid-column: 1 / -1;
margin: 8px 0 32px;
color: var(--text-muted);
}
.profile-card {
display: flex;
flex-direction: column;
gap: 10px;
min-width: 0;
}
.profile-card__cover {
position: relative;
display: block;
width: 100%;
aspect-ratio: 3 / 4;
overflow: hidden;
border: 1px solid var(--line-soft);
border-radius: 12px;
background: var(--bg-panel);
}
.profile-card__cover::after {
position: absolute;
inset: 58% 0 0;
z-index: 1;
background: linear-gradient(180deg, transparent, rgba(0, 0, 0, .7));
content: "";
pointer-events: none;
}
.profile-card__image {
width: 100%;
height: 100%;
object-fit: cover;
}
.profile-card__episodes {
position: absolute;
bottom: 10px;
left: 10px;
z-index: 2;
color: var(--text);
font-size: 12px;
font-weight: 500;
line-height: 16px;
text-shadow: 0 1px 4px #000;
}
.profile-card__progress {
position: absolute;
right: 10px;
bottom: 6px;
left: 10px;
z-index: 2;
height: 3px;
overflow: hidden;
border-radius: 999px;
background: rgba(255, 255, 255, .12);
}
.profile-card__progress-fill {
display: block;
width: var(--progress, 0%);
height: 100%;
background: linear-gradient(90deg, #ff4a7b 0%, #ec237f 52%, #93226f 100%);
}
.profile-card__badge {
position: absolute;
top: 4px;
right: 4px;
z-index: 2;
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 36px;
height: 20px;
padding-inline: 8px;
border-radius: 6px;
background: linear-gradient(135deg, #fadc44 15%, #f9c24e 85%);
color: #141414;
font-size: 11px;
font-weight: 500;
line-height: 14px;
white-space: nowrap;
}
.profile-card__badge--hot {
background: linear-gradient(135deg, #f04050 15%, #d01030 85%);
color: #fff;
}
.profile-card__title {
margin: 0;
overflow: hidden;
color: var(--text);
font-size: 16px;
font-weight: 600;
line-height: 22px;
text-overflow: ellipsis;
white-space: nowrap;
}
.profile-card__title a {
color: inherit;
text-decoration: none;
}
.profile-card__title a:hover {
color: var(--accent);
}
.profile-card__meta {
margin: 0;
overflow: hidden;
color: var(--text-muted);
font-size: 13px;
line-height: 20px;
text-overflow: ellipsis;
white-space: nowrap;
}
.profile-promo {
display: grid;
width: 100%;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 16px;
margin-inline: 0;
padding-block: 8px 40px;
}
.profile-ad {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 6px;
width: 100%;
aspect-ratio: 592 / 169;
border: 1px solid var(--line-soft);
border-radius: 12px;
background: #232328;
text-align: center;
}
.profile-ad__icon {
width: 24px;
height: 24px;
color: var(--text-muted);
}
.profile-ad__title {
margin: 0;
color: var(--text-muted);
font-size: 16px;
font-weight: 600;
line-height: 22px;
}
.profile-ad__mobile-size {
display: none;
}
.profile-ad__meta {
margin: 0;
color: var(--text-dim);
font-size: 12px;
line-height: 18px;
}
.profile-ad__badge {
position: absolute;
top: 8px;
right: 8px;
display: inline-flex;
align-items: center;
justify-content: center;
height: 20px;
padding-inline: 6px;
border-radius: 999px;
background: rgba(0, 0, 0, .6);
color: var(--text);
font-size: 11px;
font-weight: 500;
line-height: 14px;
}
@media (min-width: 901px) {
.profile-page .site-footer__inner {
gap: 32px;
padding-bottom: 31px;
}
.profile-page .site-footer__legal {
flex-direction: row;
justify-content: space-between;
gap: 24px;
}
.profile-page .site-footer__legal p:last-child {
text-align: right;
}
}
/* 账户列表使用上游总数进行服务端分页。 */
.account-pagination {
display: flex;
align-items: center;
justify-content: center;
gap: 20px;
margin: 24px 0;
color: var(--text-muted);
font-size: 13px;
}
.account-pagination a {
color: var(--text);
border: 1px solid var(--line);
border-radius: 20px;
padding: 7px 14px;
}
-152
View File
@@ -1,152 +0,0 @@
/* 已购剧集 H5:以 Figma 375px 画板为基准。 */
@media (max-width: 768px) {
.purchased-main {
display: flex;
flex-direction: column;
gap: 8px;
width: 100%;
max-width: none;
min-width: 0;
margin: 0;
padding: 12px var(--profile-gutter, 16px) 20px;
}
.purchased-note {
margin: 0;
color: #7c7c86;
font-size: 12px;
font-weight: 400;
line-height: 18px;
}
.purchased-list {
display: flex;
flex-direction: column;
width: 100%;
min-width: 0;
margin: 0;
padding: 0;
list-style: none;
}
.purchased-item {
display: flex;
align-items: center;
gap: clamp(8px, 3.2vw, 14px);
width: 100%;
min-width: 0;
min-height: 89px;
padding: 12px 0;
border-bottom: 1px solid rgba(255, 255, 255, .07);
}
.purchased-item__cover {
display: block;
flex: 0 0 clamp(41px, 12.8vw, 56px);
width: clamp(41px, 12.8vw, 56px);
height: clamp(55px, 17.066667vw, 75px);
overflow: hidden;
border-radius: 6px;
background: #141417;
}
.purchased-item__cover img {
display: block;
width: 100%;
height: 100%;
object-fit: cover;
}
.purchased-item__body {
display: flex;
flex: 1 1 0;
flex-direction: column;
align-items: flex-start;
gap: 5px;
min-width: 0;
}
.purchased-item__title {
display: block;
max-width: 100%;
overflow: hidden;
margin: 0;
color: #f2f2f4;
font-size: clamp(13px, 4vw, 15px);
font-weight: 600;
line-height: 21px;
text-overflow: ellipsis;
white-space: nowrap;
}
.purchased-item__info {
display: block;
max-width: 100%;
color: #7c7c86;
font-size: clamp(10px, 2.933333vw, 11px);
font-weight: 400;
line-height: 16px;
white-space: normal;
}
.purchased-item__mobile-date {
display: inline;
white-space: nowrap;
}
.purchased-item__date {
display: none;
}
.purchased-item__progress {
display: block;
width: min(140px, 100%);
height: 3px;
overflow: hidden;
border-radius: 999px;
background: rgba(255, 255, 255, .12);
}
.purchased-item__progress > span {
display: block;
height: 100%;
border-radius: inherit;
background: linear-gradient(90deg, #ff4a7b 0%, #ec237f 52%, #93226f 100%);
}
.purchased-item__resume {
display: inline-flex;
flex: 0 0 clamp(67px, 20.8vw, 88px);
align-items: center;
justify-content: center;
gap: 6px;
width: clamp(67px, 20.8vw, 88px);
height: 32px;
padding: 0 8px;
border: 1px solid rgba(255, 255, 255, .24);
border-radius: 999px;
background: transparent;
color: #f2f2f4;
font-size: 14px;
font-weight: 500;
line-height: 20px;
white-space: nowrap;
}
.purchased-item__resume svg,
.purchased-item__resume img {
display: block;
flex: 0 0 16px;
width: 16px;
height: 16px;
}
.purchased-item__resume-desktop { display: none; }
.purchased-item__resume-mobile { display: inline; }
}
@media (max-width: 359px) {
.purchased-page .profile-tabs__inner {
gap: 16px;
}
}
-148
View File
@@ -1,148 +0,0 @@
/* 已购剧集:1440px 桌面稿,沿用个人中心公共页头与页签。 */
.purchased-main {
display: flex;
flex-direction: column;
gap: 16px;
width: min(calc(100% - 48px), var(--page-width));
margin-inline: auto;
padding: 24px 0 40px;
}
.purchased-note {
margin: 0;
color: var(--text-dim);
font-size: 13px;
font-weight: 400;
line-height: 20px;
}
.purchased-list {
min-width: 0;
}
.purchased-item {
display: grid;
grid-template-columns: 56px minmax(0, 1fr) 120px 106px;
align-items: center;
gap: 16px;
min-width: 0;
min-height: 104px;
padding-block: 14px;
border-bottom: 1px solid var(--line-soft);
}
.purchased-item__cover {
display: block;
width: 56px;
height: 75px;
overflow: hidden;
border-radius: 6px;
background: var(--bg-panel);
}
.purchased-item__cover img {
display: block;
width: 100%;
height: 100%;
object-fit: cover;
}
.purchased-item__body {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 6px;
min-width: 0;
}
.purchased-item__title {
display: block;
max-width: 100%;
margin: 0;
overflow: hidden;
color: var(--text);
font-size: 16px;
font-weight: 600;
line-height: 22px;
text-overflow: ellipsis;
white-space: nowrap;
}
.purchased-item__info {
display: block;
max-width: 100%;
margin: 0;
overflow: hidden;
color: var(--text-muted);
font-size: 13px;
font-weight: 400;
line-height: 20px;
text-overflow: ellipsis;
white-space: nowrap;
}
.purchased-item__progress {
display: block;
width: 240px;
max-width: 100%;
height: 3px;
overflow: hidden;
border-radius: 999px;
background: var(--line);
}
.purchased-item__progress > span {
display: block;
width: var(--progress, 100%);
height: 100%;
border-radius: inherit;
background: linear-gradient(90deg, #ff4a7b 0%, #ec237f 52%, #93226f 100%);
}
.purchased-item__date {
color: var(--text-dim);
font-size: 12px;
font-weight: 400;
line-height: 18px;
white-space: nowrap;
}
.purchased-item__mobile-date,
.purchased-item__resume-mobile {
display: none;
}
.purchased-item__resume {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 8px;
width: 106px;
height: 36px;
padding-inline: 12px;
border: 1px solid rgba(255, 255, 255, .24);
border-radius: 999px;
color: var(--text);
font-size: 14px;
font-weight: 500;
line-height: 20px;
white-space: nowrap;
}
.purchased-item__resume img,
.purchased-item__resume svg {
display: block;
width: 16px;
height: 16px;
flex: 0 0 16px;
}
.purchased-item__resume:hover {
border-color: rgba(255, 255, 255, .36);
color: var(--text);
}
.purchased-item__resume:focus-visible {
outline: 2px solid var(--accent-hot);
outline-offset: 3px;
}
-117
View File
@@ -1,117 +0,0 @@
@media (max-width: 768px) {
.recharge-page {
--recharge-gutter: clamp(14px, 4.266667vw, 18px);
padding-top: 0;
}
.recharge-page .site-header { display: none; }
.subpage-header {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
height: 56px;
padding: 0 var(--recharge-gutter);
border-bottom: 1px solid var(--line-soft);
background: var(--bg);
}
.subpage-header__button { display: flex; align-items: center; justify-content: center; width: 28px; height: 36px; }
.subpage-header__button img { width: 22px; height: 22px; }
.subpage-header__title { color: var(--text); font-size: 16px; font-weight: 600; line-height: 24px; }
.recharge-main { width: 100%; margin: 0; padding: 16px 0 0; }
.recharge-breadcrumb,
.recharge-intro,
.recharge-balance__guide { display: none; }
.recharge-balance {
width: auto;
height: 110px;
margin: 0 var(--recharge-gutter);
padding: 18px;
border-radius: 16px;
}
.recharge-balance__main { gap: 14px; }
.recharge-balance__coin { width: 72px; height: 72px; flex-basis: 72px; }
.recharge-balance__label { font-size: 11px; line-height: 16px; }
.recharge-balance__copy strong { margin-top: 2px; font-size: 28px; line-height: 34px; }
.recharge-balance__copy small { font-size: 13px; line-height: 20px; }
.recharge-balance__copy p { margin-top: 0; font-size: 11px; line-height: 16px; }
.recharge-packages { margin-top: 20px; padding: 0 var(--recharge-gutter); }
.recharge-section-heading { gap: 10px; height: 22px; min-height: 22px; }
.recharge-section-heading h2,
.recharge-payment > h2 { font-size: 16px; line-height: 22px; }
.recharge-section-heading span { font-size: 12px; line-height: 18px; white-space: nowrap; }
.recharge-packages__grid { grid-template-columns: repeat(2,minmax(0,1fr)); gap: 10px; margin-top: 12px; }
.recharge-package { gap: 3px; height: 99px; padding: 12px; border-radius: 12px; }
.recharge-package__title { gap: 5px; font-size: 15px; line-height: 21px; white-space: nowrap; }
.recharge-package__title img { width: 14px; height: 14px; }
.recharge-package__hint { font-size: 11px; line-height: 16px; }
.recharge-package__price { font-size: 20px; line-height: 26px; }
.recharge-package__selected { top: 12px; right: 12px; width: 16px; height: 16px; }
.recharge-package__badge { top: -2px; right: 9px; height: 21px; padding-inline: 7px; font-size: 10px; }
.recharge-packages__note { display: none; }
.recharge-payment { margin-top: 16px; padding: 0 var(--recharge-gutter); }
.recharge-payment__grid { grid-template-columns: repeat(2,minmax(0,1fr)); gap: 12px; margin-top: 12px; }
.recharge-method { gap: 10px; padding: 0 12px; }
.recharge-payment__checkout { flex-direction: column; gap: 11px; margin-top: 11px; }
.recharge-payment__checkout button { width: 100%; height: 48px; font-size: 16px; }
.recharge-payment__checkout p { font-size: 11px; line-height: 18px; text-align: center; }
.recharge-payment__status { text-align: center; }
.recharge-history { margin-top: 22px; padding: 0 var(--recharge-gutter) 22px; }
.recharge-history__head { height: 26px; }
.recharge-history__head h2 { font-size: 18px; line-height: 26px; }
.recharge-history__filters,
.recharge-history__export { display: none; }
.recharge-history__more { display: flex; align-items: center; gap: 2px; margin-left: auto; padding: 0; border: 0; background: none; color: var(--text-muted); font-size: 12px; line-height: 18px; }
.recharge-history__more img { width: 14px; height: 14px; }
.recharge-history__table { height: auto; margin-top: 8px; padding: 0; border: 0; border-radius: 0; background: none; }
.recharge-history__columns { display: none; }
.recharge-history__row { display: grid; grid-template-columns: minmax(0,1fr) auto; grid-template-rows: 20px 16px; align-content: center; height: 64px; border-bottom: 1px solid var(--line-soft); font-size: 14px; line-height: 20px; }
.recharge-history__kind { grid-column: 1; grid-row: 1; }
.recharge-history__detail-line { display: flex; align-items: center; gap: 8px; grid-column: 1; grid-row: 2; min-width: 0; }
.recharge-history__description,
.recharge-history__time { color: var(--text-dim); font-size: 11px; line-height: 16px; white-space: nowrap; }
.recharge-history__description { overflow: hidden; text-overflow: ellipsis; }
.recharge-history__time { flex: 0 0 auto; }
.recharge-history__amount { align-self: center; grid-column: 2; grid-row: 1 / 3; font-size: 16px; line-height: 20px; }
.only-desktop { display: none; }
.only-mobile { display: inline; }
.recharge-pagination { display: none; }
.recharge-page .site-footer { background: #141417; }
.recharge-page .site-footer__inner { padding: 32px 16px 27px; gap: 18px; }
.recharge-page .site-footer__brand { gap: 18px; }
.recharge-page .site-footer__links { flex-wrap: wrap; gap: 12px 16px; }
.recharge-page .footer-col { flex-wrap: nowrap; }
.recharge-page .footer-col:first-child,
.recharge-page .footer-col:nth-child(2) a:last-child,
.recharge-page .footer-col:nth-child(3) a:last-child { display: none; }
.recharge-page .footer-download { display: flex; flex-basis: 100%; justify-content: center; padding: 0; border: 0; background: none; }
.recharge-page .footer-download .qr-box,
.recharge-page .footer-download__copy strong,
.recharge-page .footer-download__copy p,
.recharge-page .footer-download__copy .icon { display: none; }
.recharge-page .footer-download .btn { height: 20px; padding: 0; color: var(--text-muted); background: none; font-size: 13px; line-height: 20px; }
.recharge-page .site-footer__rule { display: none; }
.recharge-page .site-footer__legal { flex-direction: column; align-items: center; gap: 0; }
.recharge-page .site-footer__legal p:first-child { order: 2; width: 100%; padding-top: 16px; margin-top: 16px; border-top: 1px solid var(--line-soft); }
.recharge-page .site-footer__legal p:last-child { order: 1; }
.recharge-page .site-footer__adult,
.recharge-page .site-footer__rights { display: none; }
.recharge-page .site-footer__mobile-adult { display: inline; }
}
@media (max-width: 359px) {
.recharge-balance { height: 110px; padding: 14px; }
.recharge-balance__main { gap: 10px; }
.recharge-balance__coin { width: 64px; height: 64px; flex-basis: 64px; }
.recharge-balance__copy p { line-height: 14px; }
.recharge-balance__note-full { display: none; }
.recharge-balance__note-short { display: inline; }
.recharge-package__title { font-size: 14px; }
.recharge-package__badge { top: -4px; height: 18px; padding-inline: 5px; font-size: 9px; }
}
-516
View File
@@ -1,516 +0,0 @@
.recharge-modal[hidden] {
display: none !important;
}
.recharge-modal {
position: fixed;
inset: 0;
z-index: 320;
display: flex;
align-items: center;
justify-content: center;
padding: 24px;
}
.recharge-modal__backdrop {
position: absolute;
inset: 0;
background: rgba(0, 0, 0, 0.7);
}
.recharge-modal__panel {
position: relative;
z-index: 1;
display: flex;
flex-direction: column;
align-items: stretch;
gap: 16px;
width: 640px;
max-width: 100%;
max-height: calc(100dvh - 48px);
padding: 32px;
overflow-y: auto;
background: #141417;
border: 1px solid rgba(255, 255, 255, 0.12);
border-radius: 20px;
box-shadow: 0 24px 64px rgba(0, 0, 0, 0.55);
scrollbar-width: thin;
}
.recharge-modal__handle {
display: none;
}
.recharge-modal__head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
min-height: 54px;
}
.recharge-modal__heading {
min-width: 0;
display: flex;
flex-direction: column;
gap: 4px;
}
.recharge-modal__heading h2,
.recharge-modal__subtitle {
margin: 0;
}
.recharge-modal__heading h2 {
color: #f2f2f4;
font-size: 22px;
font-weight: 600;
line-height: 30px;
}
.recharge-modal__subtitle {
color: #a1a1aa;
font-size: 13px;
font-weight: 400;
line-height: 20px;
white-space: nowrap;
}
.recharge-modal__subtitle--mobile {
display: none;
}
.recharge-modal__balance {
display: inline-flex;
align-items: center;
gap: 6px;
flex: none;
padding: 6px 12px;
color: #e9c977;
background: rgba(217, 169, 74, 0.14);
border-radius: 999px;
}
.recharge-modal__balance strong {
font-family: Inter, var(--font);
font-size: 16px;
font-weight: 600;
line-height: 20px;
}
.recharge-modal__coin {
position: relative;
display: inline-flex;
align-items: center;
justify-content: center;
flex: 0 0 14px;
width: 14px;
height: 14px;
background: linear-gradient(135deg, #e9c977 15%, #d9a94a 85%);
border: 1px solid #e9c977;
border-radius: 50%;
}
.recharge-modal__coin::before {
content: "";
position: absolute;
inset: 2px;
border: 1px solid rgba(58, 42, 10, 0.45);
border-radius: 50%;
}
.recharge-modal__coin img {
position: relative;
width: 8px;
height: 8px;
}
.recharge-modal__coin--balance {
flex-basis: 16px;
width: 16px;
height: 16px;
}
.recharge-modal__close {
position: absolute;
top: 16px;
right: 16px;
display: inline-flex;
align-items: center;
justify-content: center;
width: 32px;
height: 32px;
background: rgba(255, 255, 255, 0.04);
border-radius: 50%;
}
.recharge-modal__close img {
width: 18px;
height: 18px;
}
.recharge-modal__packages {
display: grid;
grid-template-columns: repeat(3, 181px);
gap: 12px;
}
.recharge-modal__package {
position: relative;
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 4px;
width: 181px;
height: 99px;
padding: 12px;
text-align: left;
background: #141417;
border: 1px solid rgba(255, 255, 255, 0.07);
border-radius: 12px;
}
.recharge-modal__package.is-selected {
background: rgba(217, 169, 74, 0.14);
border: 1.5px solid #d9a94a;
}
.recharge-modal__package-top {
display: flex;
align-items: center;
justify-content: space-between;
gap: 4px;
width: 100%;
min-height: 21px;
}
.recharge-modal__package-name {
display: inline-flex;
align-items: center;
gap: 6px;
white-space: nowrap;
color: #f2f2f4;
font-size: 15px;
font-weight: 600;
line-height: 21px;
}
.recharge-modal__package.is-selected .recharge-modal__package-name,
.recharge-modal__package.is-selected .recharge-modal__package-price {
color: #e9c977;
}
.recharge-modal__selected-icon {
display: none;
flex: none;
width: 14px;
height: 14px;
}
.recharge-modal__package.is-selected .recharge-modal__selected-icon {
display: block;
}
.recharge-modal__package-detail {
color: #7c7c86;
font-size: 12px;
font-weight: 400;
line-height: 18px;
white-space: nowrap;
}
.recharge-modal__package-price {
color: #f2f2f4;
font-family: Inter, var(--font);
font-size: 20px;
font-weight: 600;
line-height: 26px;
}
.recharge-modal__package-badge {
position: absolute;
top: -1px;
right: 10px;
display: inline-flex;
align-items: center;
justify-content: center;
height: 20px;
padding: 0 8px;
color: #141414;
background: linear-gradient(135deg, #e9c977 15%, #d9a94a 85%);
border-radius: 6px;
font-size: 11px;
font-weight: 500;
line-height: 14px;
}
.recharge-modal__methods {
display: grid;
grid-template-columns: repeat(2, 278px);
gap: 12px;
}
.recharge-modal__method {
display: flex;
align-items: center;
gap: 10px;
width: 278px;
height: 52px;
padding: 0 16px;
text-align: left;
background: rgba(255, 255, 255, 0.06);
border: 1px solid rgba(255, 255, 255, 0.12);
border-radius: 12px;
}
.recharge-modal__method.is-selected {
background: rgba(236, 35, 127, 0.1);
border-color: rgba(236, 35, 127, 0.3);
}
.recharge-modal__method-check {
display: inline-flex;
align-items: center;
justify-content: center;
flex: none;
width: 18px;
height: 18px;
border: 1px solid rgba(255, 255, 255, 0.24);
border-radius: 50%;
}
.recharge-modal__method-check img {
display: none;
width: 12px;
height: 12px;
}
.recharge-modal__method.is-selected .recharge-modal__method-check {
border: 0;
background: linear-gradient(135deg, #ff4a7b 15%, #ec237f 51%, #93226f 85%);
}
.recharge-modal__method.is-selected .recharge-modal__method-check img {
display: block;
}
.recharge-modal__method-copy {
display: flex;
flex-direction: column;
gap: 1px;
min-width: 0;
color: #f2f2f4;
font-size: 14px;
font-weight: 500;
line-height: 20px;
}
.recharge-modal__method-copy small {
color: #7c7c86;
font-size: 12px;
font-weight: 400;
line-height: 18px;
}
.recharge-modal__pay {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 52px;
padding: 0 28px;
color: #fff;
background: linear-gradient(135deg, #ff4a7b 15%, #ec237f 51%, #93226f 85%);
border-radius: 999px;
font-size: 16px;
font-weight: 500;
line-height: 24px;
}
.recharge-modal__status {
margin: -8px 0 0;
color: #a1a1aa;
font-size: 12px;
line-height: 18px;
text-align: center;
}
.recharge-modal__status.is-ok {
color: #6ee7a8;
}
.recharge-modal__empty {
margin: 0;
grid-column: 1 / -1;
color: rgba(255, 255, 255, 0.56);
font-size: 13px;
line-height: 20px;
}
.recharge-modal__foot {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
color: #7c7c86;
font-size: 12px;
font-weight: 400;
line-height: 18px;
white-space: nowrap;
}
.recharge-modal__foot a {
display: inline-flex;
align-items: center;
gap: 5px;
color: #a1a1aa;
}
.recharge-modal__foot img {
width: 12px;
height: 12px;
}
.recharge-modal__close:focus-visible,
.recharge-modal__package:focus-visible,
.recharge-modal__method:focus-visible,
.recharge-modal__pay:focus-visible,
.recharge-modal__foot a:focus-visible {
outline: 2px solid #ff4a7b;
outline-offset: 2px;
}
@media (max-width: 768px) {
.recharge-modal {
align-items: flex-end;
justify-content: stretch;
padding: 0;
}
.recharge-modal__panel {
width: 100%;
max-width: none;
max-height: 100dvh;
padding: 12px 20px 24px;
gap: 14px;
border: 0;
border-radius: 20px 20px 0 0;
box-shadow: none;
}
.recharge-modal__handle {
display: flex;
justify-content: center;
align-items: center;
flex: none;
height: 4px;
}
.recharge-modal__handle::before {
content: "";
width: 36px;
height: 4px;
background: rgba(255, 255, 255, 0.24);
border-radius: 2px;
}
.recharge-modal__head {
min-height: 46px;
}
.recharge-modal__heading {
gap: 2px;
}
.recharge-modal__heading h2 {
font-size: 18px;
line-height: 26px;
}
.recharge-modal__subtitle {
font-size: 12px;
line-height: 18px;
}
.recharge-modal__subtitle--desktop,
.recharge-modal__balance {
display: none;
}
.recharge-modal__subtitle--mobile {
display: block;
}
.recharge-modal__close {
position: static;
flex: none;
}
.recharge-modal__packages {
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 10px;
}
.recharge-modal__package {
width: 100%;
}
.recharge-modal__package-detail {
font-size: clamp(10px, 3.2vw, 12px);
}
.recharge-modal__methods {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.recharge-modal__method {
width: 100%;
padding: 0 12px;
}
.recharge-modal__method-copy small {
font-size: 11px;
line-height: 16px;
}
.recharge-modal__pay {
height: 48px;
padding: 0 22px;
font-size: 15px;
line-height: 22px;
}
.recharge-modal__foot {
font-size: 11px;
line-height: 16px;
}
.recharge-modal__foot img {
width: 11px;
height: 11px;
}
}
@media (max-width: 340px) {
.recharge-modal__panel {
padding-inline: 16px;
}
.recharge-modal__package {
padding-inline: 9px;
}
.recharge-modal__package-name {
font-size: 13px;
}
.recharge-modal__package-badge {
top: -8px;
right: 4px;
}
.recharge-modal__method {
gap: 7px;
padding-inline: 9px;
}
}
-271
View File
@@ -1,271 +0,0 @@
.recharge-page {
min-height: 100vh;
padding-top: 72px;
background: var(--bg);
}
.recharge-page [hidden] { display: none !important; }
.subpage-header { display: none; }
.recharge-main {
width: min(calc(100% - 48px), var(--page-width));
margin-inline: auto;
padding: 24px 0 41px;
}
.recharge-breadcrumb {
display: flex;
align-items: center;
gap: 8px;
height: 20px;
color: var(--text-dim);
font-size: 13px;
line-height: 20px;
}
.recharge-breadcrumb span:last-child { color: var(--text-muted); }
.recharge-breadcrumb a:hover { color: var(--text); }
.recharge-intro {
display: flex;
flex-direction: column;
align-items: flex-start;
margin-top: 16px;
}
.recharge-intro__eyebrow {
color: var(--accent-hot);
font-size: 13px;
font-weight: 600;
line-height: 18px;
letter-spacing: .14em;
}
.recharge-intro__eyebrow::before {
display: inline-block;
width: 18px;
height: 2px;
margin: 0 10px 4px 0;
background: var(--accent);
content: "";
}
.recharge-intro h1 {
margin: 12px 0 0;
color: var(--text);
font-size: 28px;
font-weight: 600;
line-height: 36px;
}
.recharge-intro p {
margin: 12px 0 0;
color: var(--text-muted);
font-size: 15px;
line-height: 24px;
}
.recharge-balance {
display: flex;
align-items: center;
justify-content: space-between;
gap: 32px;
width: 100%;
height: 216px;
margin-top: 24px;
padding: 24px 32px;
border: 1px solid var(--line-soft);
border-radius: 20px;
background: linear-gradient(90deg, #2a0d22 0%, #150a17 55%, #141417 100%);
}
.recharge-balance__main {
display: flex;
align-items: center;
gap: 24px;
min-width: 0;
}
.recharge-balance__coin {
width: 120px;
height: 120px;
flex: 0 0 120px;
object-fit: contain;
}
.recharge-balance__copy { min-width: 0; }
.recharge-balance__label,
.recharge-balance__guide h2 {
color: var(--text-dim);
font-size: 12px;
font-weight: 500;
line-height: 18px;
}
.recharge-balance__copy strong {
display: block;
margin-top: 4px;
color: var(--gold);
font-family: Inter, var(--font);
font-size: 40px;
font-weight: 600;
line-height: 46px;
}
.recharge-balance__copy small {
color: var(--text);
font-family: var(--font);
font-size: 15px;
font-weight: 600;
line-height: 22px;
}
.recharge-balance__copy p {
margin: 3px 0 0;
color: var(--text-dim);
font-size: 12px;
line-height: 18px;
white-space: nowrap;
}
.recharge-balance__note-short { display: none; }
.recharge-balance__guide {
display: flex;
flex: 0 0 360px;
flex-direction: column;
gap: 9px;
}
.recharge-balance__guide h2 { margin: 0; }
.recharge-balance__tip { display: flex; align-items: flex-start; gap: 8px; }
.recharge-balance__tip > img { width: 16px; height: 16px; flex: 0 0 16px; }
.recharge-balance__tip > div { display: flex; flex-direction: column; gap: 2px; }
.recharge-balance__tip strong { color: var(--text); font-size: 13px; font-weight: 600; line-height: 18px; }
.recharge-balance__tip span { color: var(--text-dim); font-size: 12px; line-height: 18px; }
.recharge-packages { margin-top: 32px; }
.recharge-section-heading { display: flex; align-items: center; gap: 14px; min-height: 30px; }
.recharge-section-heading h2,
.recharge-payment > h2,
.recharge-history__head h2 {
margin: 0;
color: var(--text);
font-size: 20px;
font-weight: 600;
line-height: 26px;
}
.recharge-section-heading span { color: var(--text-dim); font-size: 13px; line-height: 20px; }
.recharge-packages__grid {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 16px;
margin-top: 20px;
}
.recharge-package {
position: relative;
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 5px;
min-width: 0;
height: 124px;
padding: 18px 20px;
border: 1px solid var(--line-soft);
border-radius: 16px;
background: var(--bg-elevated);
color: var(--text);
text-align: left;
cursor: pointer;
}
.recharge-package.is-selected {
border: 1.5px solid #d9a94a;
background: rgba(217, 169, 74, .14);
}
.recharge-package__title { display: flex; align-items: center; gap: 6px; font-size: 18px; font-weight: 600; line-height: 26px; }
.recharge-package__title img { width: 16px; height: 16px; }
.recharge-package__hint { color: var(--text-dim); font-size: 12px; line-height: 18px; white-space: nowrap; }
.recharge-package__price { margin-top: auto; color: var(--text); font-family: Inter, var(--font); font-size: 24px; font-weight: 600; line-height: 30px; }
.recharge-package.is-selected .recharge-package__title,
.recharge-package.is-selected .recharge-package__price { color: var(--gold); }
.recharge-package__selected { position: absolute; top: 18px; right: 20px; width: 18px; height: 18px; display: none; }
.recharge-package.is-selected .recharge-package__selected { display: block; }
.recharge-package__badge { position: absolute; top: -2px; right: 10px; display: flex; align-items: center; justify-content: center; height: 21px; padding: 0 8px; border-radius: 5px; background: linear-gradient(135deg, #ffe7a0, #e9c977); color: #382506; font-size: 11px; font-weight: 600; line-height: 16px; }
.recharge-packages__note { display: flex; align-items: center; gap: 8px; min-height: 24px; margin-top: 20px; }
.recharge-packages__note span { display: inline-flex; align-items: center; height: 24px; padding: 0 10px; border: 1px solid rgba(236,35,127,.34); border-radius: 999px; background: rgba(236,35,127,.08); color: var(--accent-hot); font-size: 12px; font-weight: 500; white-space: nowrap; }
.recharge-packages__note p { margin: 0; color: var(--text-dim); font-size: 12px; line-height: 18px; }
.recharge-data-empty { margin: 16px 0 0; color: var(--text-dim); font-size: 13px; line-height: 20px; }
.recharge-payment { margin-top: 28px; }
.recharge-payment__grid { display: grid; grid-template-columns: repeat(4, minmax(0,1fr)); gap: 12px; margin-top: 20px; }
.recharge-method { display: flex; align-items: center; gap: 10px; height: 52px; min-width: 0; padding: 0 14px; border: 1px solid var(--line); border-radius: 12px; background: #242427; color: var(--text); text-align: left; cursor: pointer; }
.recharge-method.is-selected { border-color: rgba(236,35,127,.45); background: rgba(236,35,127,.12); }
.recharge-method__radio { display: flex; align-items: center; justify-content: center; width: 18px; height: 18px; flex: 0 0 18px; border: 1px solid #68686d; border-radius: 50%; }
.recharge-method__radio img { display: none; width: 12px; height: 12px; }
.recharge-method.is-selected .recharge-method__radio { border-color: transparent; background: linear-gradient(135deg,#ff4a7b,#ec237f); }
.recharge-method.is-selected .recharge-method__radio img { display: block; }
.recharge-method > span:last-child { display: flex; flex-direction: column; }
.recharge-method strong { font-size: 14px; font-weight: 500; line-height: 20px; }
.recharge-method small { color: var(--text-dim); font-size: 12px; line-height: 18px; }
.recharge-payment__checkout { display: flex; align-items: center; gap: 16px; margin-top: 28px; }
.recharge-payment__checkout button { display: flex; align-items: center; justify-content: center; width: 320px; height: 56px; border: 0; border-radius: 999px; background: linear-gradient(105deg,#ff4a7b 0%,#ec237f 52%,#93226f 100%); color: #fff; font-size: 16px; font-weight: 600; line-height: 24px; cursor: pointer; }
.recharge-payment__checkout button:disabled { cursor: not-allowed; opacity: .5; }
.recharge-payment__checkout p { margin: 0; color: var(--text-dim); font-size: 12px; line-height: 18px; }
.recharge-payment__status { margin: 8px 0 0; color: var(--text-muted); font-size: 12px; line-height: 18px; }
.recharge-payment__status.is-success { color: #39c98a; }
.recharge-history { margin-top: 40px; scroll-margin-top: calc(var(--site-header-height, 72px) + 16px); }
.recharge-history__head { display: flex; align-items: center; gap: 14px; height: 30px; }
.recharge-history__filters { display: flex; align-items: center; gap: 18px; align-self: stretch; }
.recharge-history__filters button { position: relative; padding: 0; border: 0; background: none; color: var(--text-dim); font-size: 13px; font-weight: 500; cursor: pointer; }
.recharge-history__filters button.is-active { color: var(--text); }
.recharge-history__filters button.is-active::after { position: absolute; right: 0; bottom: -7px; left: 0; height: 2px; background: var(--accent); content: ""; }
.recharge-history__export { display: flex; align-items: center; gap: 3px; margin-left: auto; padding: 0; border: 0; background: none; color: var(--text-muted); font-size: 13px; cursor: pointer; }
.recharge-history__export img { width: 16px; height: 16px; }
.recharge-history__more { display: none; }
.recharge-history__table { height: 310px; margin-top: 16px; padding: 4px 24px; border: 1px solid var(--line-soft); border-radius: 16px; background: var(--bg-elevated); }
.recharge-history__columns,
.recharge-history__row { display: grid; grid-template-columns: 200px minmax(0,1fr) 200px 120px; align-items: center; }
.recharge-history__columns { height: 45px; border-bottom: 1px solid var(--line-soft); color: var(--text-dim); font-size: 12px; line-height: 18px; }
.recharge-history__columns span:last-child,
.recharge-history__amount { text-align: right; }
.recharge-history__row { height: 51px; border-bottom: 1px solid var(--line-soft); color: var(--text-muted); font-size: 13px; line-height: 20px; }
.recharge-history__row:last-child { border-bottom: 0; }
.recharge-history__kind { color: var(--text); }
.recharge-history__detail-line { display: contents; }
.recharge-history__time { color: var(--text-dim); }
.recharge-history__amount { color: #f35b69; font-family: Inter, var(--font); font-size: 15px; font-weight: 600; }
.recharge-history__amount.is-credit { color: #39c98a; }
.recharge-history__empty { margin: 0; padding: 36px 0; color: var(--text-dim); font-size: 13px; text-align: center; }
.only-mobile { display: none; }
.recharge-pagination,
.recharge-pagination [data-recharge-pages] { display: flex; align-items: center; justify-content: center; gap: 8px; }
.recharge-pagination { margin-top: 16px; }
.recharge-pagination button { display: flex; align-items: center; justify-content: center; width: 36px; height: 36px; padding: 0; border: 1px solid var(--line); border-radius: 8px; background: var(--bg-elevated); color: var(--text-muted); font-family: Inter, var(--font); font-size: 16px; font-weight: 600; cursor: pointer; }
.recharge-pagination button.is-active { border-color: transparent; background: linear-gradient(135deg,#ff4a7b,#ec237f 52%,#93226f); color: #fff; }
.recharge-pagination button:disabled { opacity: .45; cursor: default; }
.recharge-pagination button img { width: 16px; height: 16px; }
.recharge-pagination > button:first-child img { transform: rotate(180deg); }
.recharge-pagination__total { color: var(--text-dim); font-size: 12px; line-height: 18px; }
.recharge-page button:focus-visible,
.recharge-page a:focus-visible { outline: 2px solid var(--accent-hot); outline-offset: 3px; }
@media (max-width: 1080px) and (min-width: 769px) {
.recharge-balance__guide { flex-basis: 300px; }
.recharge-history__columns,
.recharge-history__row { grid-template-columns: 140px minmax(0,1fr) 175px 90px; }
}
@media (min-width: 901px) {
.recharge-page .site-footer__inner { gap: 32px; padding-bottom: 32px; }
.recharge-page .site-footer__legal { flex-direction: row; justify-content: space-between; gap: 24px; }
.recharge-page .site-footer__legal p:last-child { text-align: right; }
}
-255
View File
@@ -1,255 +0,0 @@
/* H5 注册页 */
body.page-register {
min-height: 100vh;
background: var(--bg);
}
.page-register .page-subheader {
display: flex;
border-bottom: 0;
}
.register-page {
min-height: 100vh;
padding: 56px 0 32px;
box-sizing: border-box;
}
.register-page__inner {
width: 100%;
max-width: 440px;
margin: 0 auto;
padding: 24px var(--h5-gutter);
display: flex;
flex-direction: column;
align-items: center;
gap: 24px;
box-sizing: border-box;
}
.register-page__brand {
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
}
.register-page__logo {
width: 64px;
height: 64px;
border-radius: 12px;
object-fit: cover;
box-shadow: 0 8px 24px rgba(236, 35, 127, 0.4);
}
.register-page__content {
align-self: stretch;
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 18px;
}
.register-page__intro {
align-self: stretch;
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 6px;
}
.register-page__title {
margin: 0;
color: var(--text);
font-size: 28px;
font-weight: 600;
line-height: 38px;
}
.register-page__desc {
margin: 0;
color: var(--text-muted);
font-size: 14px;
line-height: 22px;
}
.register-page__form {
align-self: stretch;
display: flex;
flex-direction: column;
gap: 16px;
}
.register-page__field {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 8px;
}
.register-page__label {
color: var(--text-muted);
font-size: 14px;
font-weight: 500;
line-height: 20px;
}
.register-page__control {
align-self: stretch;
height: 48px;
padding: 0 16px;
display: flex;
align-items: center;
gap: 10px;
background: rgba(255, 255, 255, 0.06);
border: 1px solid transparent;
border-radius: 12px;
outline: 1px solid transparent;
outline-offset: -1px;
}
.register-page__control:focus-within {
outline-color: rgba(236, 35, 127, 0.45);
}
.register-page__field.is-error .register-page__control {
outline-color: #ff5a5f;
}
.register-page__error {
display: inline-flex;
align-items: center;
gap: 6px;
color: #ff5a5f;
font-size: 12px;
font-weight: 400;
line-height: 18px;
}
.register-page__error[hidden] {
display: none !important;
}
.register-page__error-icon {
width: 14px;
height: 14px;
flex-shrink: 0;
}
.register-page__control > .icon {
width: 18px;
height: 18px;
flex-shrink: 0;
}
.register-page__input {
flex: 1 1 0;
min-width: 0;
height: 100%;
padding: 0;
border: 0;
outline: none;
background: transparent;
color: var(--text);
font: inherit;
font-size: 15px;
line-height: 24px;
}
.register-page__input::placeholder {
color: var(--text-dim);
}
.register-page__toggle {
width: 18px;
height: 18px;
flex-shrink: 0;
display: inline-flex;
align-items: center;
justify-content: center;
color: var(--text-dim);
}
.register-page__toggle .icon {
width: 18px;
height: 18px;
}
.register-page__agree {
display: flex;
align-items: flex-start;
gap: 10px;
color: var(--text-muted);
font-size: 13px;
line-height: 20px;
cursor: pointer;
}
.register-page__agree a {
color: inherit;
text-decoration: underline;
text-underline-offset: 2px;
}
.register-page__checkbox {
position: absolute;
opacity: 0;
width: 1px;
height: 1px;
pointer-events: none;
}
.register-page__check {
width: 18px;
height: 18px;
flex-shrink: 0;
margin-top: 1px;
display: inline-flex;
align-items: center;
justify-content: center;
border-radius: 4px;
background: transparent;
border: 1px solid rgba(255, 255, 255, 0.24);
}
.register-page__checkbox:checked + .register-page__check {
border-color: transparent;
background: linear-gradient(135deg, #ff4a7b 0%, #ec237f 52%, #93226f 100%);
}
.register-page__checkbox:checked + .register-page__check::after {
content: "";
width: 10px;
height: 6px;
border-left: 1.5px solid #fff;
border-bottom: 1.5px solid #fff;
transform: translateY(-1px) rotate(-45deg);
}
.register-page__submit {
align-self: stretch;
height: 52px;
padding: 0 28px;
font-size: 16px;
line-height: 24px;
}
.register-page__switch {
margin: 0;
align-self: stretch;
display: flex;
align-items: center;
justify-content: center;
gap: 6px;
color: var(--text-muted);
font-size: 14px;
line-height: 22px;
}
.register-page__switch a {
color: #ff4a7b;
font-size: 14px;
font-weight: 500;
line-height: 20px;
}
-304
View File
@@ -1,304 +0,0 @@
/* PC 注册弹窗 */
.register-pop {
position: fixed;
inset: 0;
z-index: 290;
display: none;
align-items: center;
justify-content: center;
padding: 24px;
}
.register-pop.is-open {
display: flex;
}
.register-pop__mask {
position: absolute;
inset: 0;
background: rgba(0, 0, 0, 0.7);
}
.register-pop__panel {
position: relative;
z-index: 1;
width: 440px;
max-width: 100%;
padding: 32px;
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 20px;
background: #141417;
border: 1px solid var(--line);
border-radius: var(--radius-xl);
box-shadow: 0 24px 64px rgba(0, 0, 0, 0.55);
overflow: hidden;
}
.register-pop__close {
position: absolute;
top: 16px;
right: 16px;
width: 32px;
height: 32px;
display: inline-flex;
align-items: center;
justify-content: center;
background: rgba(255, 255, 255, 0.04);
border-radius: 999px;
}
.register-pop__close .icon {
width: 18px;
height: 18px;
}
.register-pop__intro {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 6px;
padding-right: 40px;
}
.register-pop__title {
margin: 0;
color: var(--text);
font-size: 28px;
font-weight: 600;
line-height: 38px;
}
.register-pop__desc {
margin: 0;
color: var(--text-muted);
font-size: 14px;
line-height: 22px;
}
.register-pop__form {
align-self: stretch;
display: flex;
flex-direction: column;
gap: 16px;
}
.register-pop__field {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 8px;
}
.register-pop__label {
color: var(--text-muted);
font-size: 14px;
font-weight: 500;
line-height: 20px;
}
.register-pop__control {
align-self: stretch;
height: 48px;
padding: 0 16px;
display: flex;
align-items: center;
gap: 10px;
background: rgba(255, 255, 255, 0.06);
border: 1px solid transparent;
border-radius: 12px;
outline: 1px solid transparent;
outline-offset: -1px;
}
.register-pop__control:focus-within {
outline-color: rgba(236, 35, 127, 0.45);
}
.register-pop__field.is-error .register-pop__control {
outline-color: #ff5a5f;
}
.register-pop__error {
display: inline-flex;
align-items: center;
gap: 6px;
color: #ff5a5f;
font-size: 12px;
font-weight: 400;
line-height: 18px;
}
.register-pop__error[hidden] {
display: none !important;
}
.register-pop__error-icon {
width: 14px;
height: 14px;
flex-shrink: 0;
}
.register-pop__control > .icon {
width: 18px;
height: 18px;
flex-shrink: 0;
}
.register-pop__input {
flex: 1 1 0;
min-width: 0;
height: 100%;
padding: 0;
border: 0;
outline: none;
background: transparent;
color: var(--text);
font: inherit;
font-size: 15px;
line-height: 24px;
}
.register-pop__input::placeholder {
color: var(--text-dim);
}
.register-pop__toggle {
width: 18px;
height: 18px;
flex-shrink: 0;
display: inline-flex;
align-items: center;
justify-content: center;
color: var(--text-dim);
}
.register-pop__toggle .icon {
width: 18px;
height: 18px;
}
.register-pop__agree {
display: flex;
align-items: flex-start;
gap: 10px;
color: var(--text-muted);
font-size: 13px;
line-height: 20px;
cursor: pointer;
}
.register-pop__agree a {
color: inherit;
text-decoration: underline;
text-underline-offset: 2px;
}
.register-pop__checkbox {
position: absolute;
opacity: 0;
width: 1px;
height: 1px;
pointer-events: none;
}
.register-pop__check {
width: 18px;
height: 18px;
flex-shrink: 0;
margin-top: 1px;
display: inline-flex;
align-items: center;
justify-content: center;
border-radius: 4px;
background: transparent;
border: 1px solid rgba(255, 255, 255, 0.24);
}
.register-pop__checkbox:checked + .register-pop__check {
border-color: transparent;
background: linear-gradient(135deg, #ff4a7b 0%, #ec237f 52%, #93226f 100%);
}
.register-pop__checkbox:checked + .register-pop__check::after {
content: "";
width: 10px;
height: 6px;
border-left: 1.5px solid #fff;
border-bottom: 1.5px solid #fff;
transform: translateY(-1px) rotate(-45deg);
}
.register-pop__submit {
align-self: stretch;
height: 52px;
padding: 0 28px;
font-size: 16px;
line-height: 24px;
}
.register-pop__submit.is-loading,
.register-pop__submit:disabled {
opacity: 0.72;
cursor: wait;
}
.register-pop__tip {
margin: 0;
align-self: stretch;
padding: 8px 12px;
border-radius: 8px;
font-size: 13px;
line-height: 20px;
background: rgba(255, 74, 123, 0.1);
color: #ff4a7b;
}
.register-pop__tip.is-success {
background: rgba(30, 120, 70, 0.12);
color: #1e7846;
}
.register-pop__tip[hidden] {
display: none !important;
}
.register-pop__switch {
margin: 0;
align-self: stretch;
display: flex;
align-items: center;
justify-content: center;
gap: 6px;
color: var(--text-muted);
font-size: 14px;
line-height: 22px;
}
.register-pop__switch a {
color: #ff4a7b;
font-size: 14px;
font-weight: 500;
line-height: 20px;
}
@media (max-width: 768px) {
.register-pop {
padding: 16px;
}
.register-pop__panel {
max-height: calc(100vh - 32px);
max-height: calc(100dvh - 32px);
padding: 24px 20px;
border-radius: 18px;
overflow-y: auto;
}
.register-pop__title {
font-size: 24px;
line-height: 32px;
}
}
-65
View File
@@ -1,65 +0,0 @@
.setting-main { width: min(calc(100% - 48px), var(--page-width)); margin: 0 auto; padding: 24px 0 40px; }
.setting-layout { max-width: 900px; margin: 0 auto; }
.setting-card { display: flex; flex-direction: column; gap: 24px; min-width: 0; padding: 32px; border: 1px solid var(--line-soft); border-radius: 16px; background: #141417; }
.setting-section { display: flex; flex-direction: column; gap: 24px; scroll-margin-top: 90px; }
.setting-section__title { margin: 0; color: var(--text); font-size: 18px; font-weight: 600; line-height: 26px; }
.setting-avatar { display: flex; align-items: center; gap: 16px; }
.setting-avatar__image { flex: 0 0 72px; width: 72px; height: 72px; border-radius: 50%; object-fit: cover; }
.setting-avatar__button { display: inline-flex; align-items: center; justify-content: center; gap: 8px; height: 36px; padding: 0 12px; border: 1px solid rgba(255,255,255,.24); border-radius: 999px; color: var(--text); font-size: 14px; cursor: pointer; }
.setting-avatar__button img { width: 16px; height: 16px; }
.setting-avatar__file { position: absolute; width: 1px; height: 1px; opacity: 0; }
.setting-avatar__hint { margin: 8px 0 0; color: var(--text-dim); font-size: 12px; line-height: 18px; }
.setting-fields { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 20px 24px; }
.setting-field { display: flex; flex-direction: column; gap: 8px; min-width: 0; color: var(--text-muted); font-size: 14px; font-weight: 500; line-height: 20px; }
.setting-field input, .setting-field textarea, .setting-field__action { width: 100%; min-width: 0; min-height: 48px; padding: 12px 16px; border: 1px solid rgba(255,255,255,.12); border-radius: 12px; outline: none; background: rgba(255,255,255,.06); color: var(--text); font: 400 15px/24px var(--font); }
.setting-field textarea { min-height: 96px; resize: vertical; }
.setting-field input:focus, .setting-field textarea:focus { border-color: #ec237f; }
.setting-field input[readonly] { color: var(--text-muted); cursor: default; }
.setting-field small { color: var(--text-dim); font-size: 12px; font-weight: 400; line-height: 18px; }
.setting-field__action { color: #ff4a7b; text-align: right; cursor: pointer; }
.setting-field__value-action { position: relative; min-width: 0; }
.setting-field__value-action input { padding-right: 72px; }
.setting-field__inline-action { position: absolute; top: 50%; right: 16px; color: #ff4a7b; font-size: 14px; font-weight: 500; line-height: 20px; transform: translateY(-50%); cursor: pointer; }
.setting-field--intro { max-width: 100%; }
.setting-actions { display: flex; align-items: center; gap: 12px; padding-top: 24px; border-top: 1px solid var(--line-soft); }
.setting-actions button { min-height: 48px; padding: 0 22px; border-radius: 999px; color: #fff; font-size: 15px; font-weight: 500; cursor: pointer; }
.setting-actions__save { border: 0; background: linear-gradient(135deg, #ff4a7b 15%, #ec237f 51%, #93226f 85%); }
.setting-actions__logout { border: 1px solid rgba(255,255,255,.24); background: transparent; }
.setting-actions__unavailable { margin-left: auto; color: var(--text-dim); font-size: 13px; }
.setting-status { margin: -8px 0 0; min-height: 20px; color: #ff858b; font-size: 13px; line-height: 20px; }
.setting-status.is-success { color: #84d9a1; }
.setting-login-state { padding: 80px 16px; text-align: center; color: var(--text-muted); }
.setting-login-state h2 { color: var(--text); }
.setting-login-state a { color: #ff4a7b; }
.setting-password-pop { position: fixed; inset: 0; z-index: 295; display: flex; align-items: center; justify-content: center; padding: 24px; }
.setting-password-pop[hidden] { display: none !important; }
.setting-password-pop__mask { position: absolute; inset: 0; width: 100%; height: 100%; background: rgba(0,0,0,.72); cursor: default; }
.setting-password-pop__panel { position: relative; z-index: 1; width: 440px; max-width: 100%; padding: 32px; border: 1px solid var(--line); border-radius: var(--radius-xl); background: #141417; box-shadow: 0 24px 64px rgba(0,0,0,.55); }
.setting-password-pop__close { position: absolute; top: 16px; right: 16px; display: inline-flex; align-items: center; justify-content: center; width: 32px; height: 32px; border-radius: 50%; background: rgba(255,255,255,.04); }
.setting-password-pop__close .icon { width: 18px; height: 18px; }
.setting-password-pop__intro { padding-right: 40px; }
.setting-password-pop__intro h2 { margin: 0; color: var(--text); font-size: 28px; font-weight: 600; line-height: 38px; }
.setting-password-pop__intro p { margin: 6px 0 0; color: var(--text-muted); font-size: 14px; line-height: 22px; }
.setting-password-pop__form { display: flex; flex-direction: column; gap: 16px; margin-top: 20px; }
.setting-password-pop__status { min-height: 18px; margin: -4px 0 0; color: #ff858b; font-size: 12px; line-height: 18px; }
.setting-password-pop__status.is-success { color: #84d9a1; }
.setting-password-pop__submit { width: 100%; min-height: 52px; border: 0; border-radius: 999px; color: #fff; font-size: 16px; font-weight: 600; cursor: pointer; }
.setting-password-pop__submit:disabled { opacity: .72; cursor: wait; }
@media (max-width: 768px) {
.setting-main { width: 100%; padding: 0 16px 24px; }
.setting-layout { max-width: none; }
.setting-card { gap: 16px; padding: 16px 0 0; border: 0; border-radius: 0; background: transparent; }
.setting-section { gap: 16px; }
.setting-section__title { display: none; }
.setting-avatar__image { flex-basis: 56px; width: 56px; height: 56px; }
.setting-fields { grid-template-columns: minmax(0, 1fr); gap: 16px; }
.setting-actions { flex-direction: column; align-items: stretch; gap: 16px; padding-top: 16px; }
.setting-actions button { width: 100%; }
.setting-actions__unavailable { margin: 0; text-align: center; }
.setting-status { text-align: center; }
.setting-password-pop { padding: 16px; }
.setting-password-pop__panel { max-height: calc(100vh - 32px); max-height: calc(100dvh - 32px); padding: 24px 20px; border-radius: 18px; overflow-y: auto; }
.setting-password-pop__intro h2 { font-size: 24px; line-height: 32px; }
}
-3
View File
@@ -1,3 +0,0 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="0.5" y="0.5" width="17" height="17" rx="8.5" stroke="white" stroke-opacity="0.24"/>
</svg>

Before

Width:  |  Height:  |  Size: 196 B

-4
View File
@@ -1,4 +0,0 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M13.0725 13.0725C11.9313 13.9425 10.515 14.4375 9 14.4375C3.75 14.4375 0.75 9 0.75 9C1.6125 7.4025 2.835 6.075 4.275 5.115M7.4325 3.27C7.9425 3.1425 8.4675 3.075 9 3.075C14.25 3.075 17.25 9 17.25 9C16.7925 9.8475 16.2375 10.635 15.6075 11.34M0.75 0.75L17.25 17.25" stroke="#7C7C86" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M7.6125 7.61255C7.24773 7.97732 7.04276 8.47206 7.04276 8.98755C7.04276 9.50303 7.24773 9.99777 7.6125 10.3625C7.97727 10.7273 8.47201 10.9323 8.9875 10.9323C9.50298 10.9323 9.99772 10.7273 10.3625 10.3625" stroke="#7C7C86" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 763 B

-4
View File
@@ -1,4 +0,0 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0.75 9C0.75 9 3.75 3 9 3C14.25 3 17.25 9 17.25 9C17.25 9 14.25 15 9 15C3.75 15 0.75 9 0.75 9Z" stroke="#7C7C86" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9 11.25C10.2426 11.25 11.25 10.2426 11.25 9C11.25 7.75736 10.2426 6.75 9 6.75C7.75736 6.75 6.75 7.75736 6.75 9C6.75 10.2426 7.75736 11.25 9 11.25Z" stroke="#7C7C86" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 536 B

@@ -1,4 +0,0 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.16669 10H15.8334" stroke="white" stroke-width="1.66667" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M10 4.16602L15.8333 9.99935L10 15.8327" stroke="white" stroke-width="1.66667" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 356 B

@@ -1,3 +0,0 @@
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M31.6666 35L20 26.6667L8.33331 35V8.33333C8.33331 7.44928 8.6845 6.60143 9.30962 5.97631C9.93474 5.35119 10.7826 5 11.6666 5H28.3333C29.2174 5 30.0652 5.35119 30.6903 5.97631C31.3155 6.60143 31.6666 7.44928 31.6666 8.33333V35Z" stroke="#7C7C86" stroke-width="3.33333" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 429 B

-11
View File
@@ -1,11 +0,0 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M2.6665 14.0007V9.33398" stroke="#F2F2F4" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M2.6665 6.66667V2" stroke="#F2F2F4" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M8 14V8" stroke="#F2F2F4" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M8 5.33333V2" stroke="#F2F2F4" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M13.3335 13.9993V10.666" stroke="#F2F2F4" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M13.3335 8V2" stroke="#F2F2F4" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M0.666504 9.33398H4.6665" stroke="#F2F2F4" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M6 5.33398H10" stroke="#F2F2F4" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M11.3335 10.666H15.3335" stroke="#F2F2F4" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

-7
View File
@@ -1,7 +0,0 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M14.25 9V14.25C14.25 14.6478 14.092 15.0294 13.8107 15.3107C13.5294 15.592 13.1478 15.75 12.75 15.75H5.25C4.85218 15.75 4.47064 15.592 4.18934 15.3107C3.90804 15.0294 3.75 14.6478 3.75 14.25V9" stroke="#7C7C86" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M15 4.5H3C2.58579 4.5 2.25 4.83579 2.25 5.25V6.75C2.25 7.16421 2.58579 7.5 3 7.5H15C15.4142 7.5 15.75 7.16421 15.75 6.75V5.25C15.75 4.83579 15.4142 4.5 15 4.5Z" stroke="#7C7C86" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9 15.75V4.5" stroke="#7C7C86" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9 4.5H6.375C5.87772 4.5 5.40081 4.30246 5.04917 3.95083C4.69754 3.59919 4.5 3.12228 4.5 2.625C4.5 2.12772 4.69754 1.65081 5.04917 1.29917C5.40081 0.947544 5.87772 0.75 6.375 0.75C8.25 0.75 9 4.5 9 4.5Z" stroke="#7C7C86" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9 4.5H11.625C12.1223 4.5 12.5992 4.30246 12.9508 3.95083C13.3025 3.59919 13.5 3.12228 13.5 2.625C13.5 2.12772 13.3025 1.65081 12.9508 1.29917C12.5992 0.947544 12.1223 0.75 11.625 0.75C9.75 0.75 9 4.5 9 4.5Z" stroke="#7C7C86" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

-5
View File
@@ -1,5 +0,0 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M13.5 9.75V14.25C13.5 14.6478 13.342 15.0294 13.0607 15.3107C12.7794 15.592 12.3978 15.75 12 15.75H3.75C3.35218 15.75 2.97064 15.592 2.68934 15.3107C2.40804 15.0294 2.25 14.6478 2.25 14.25V6C2.25 5.60218 2.40804 5.22064 2.68934 4.93934C2.97064 4.65804 3.35218 4.5 3.75 4.5H8.25" stroke="#7C7C86" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M11.25 2.25H15.75V6.75" stroke="#7C7C86" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M7.5 10.5L15.75 2.25" stroke="#7C7C86" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 710 B

-4
View File
@@ -1,4 +0,0 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9 16.5C13.1421 16.5 16.5 13.1421 16.5 9C16.5 4.85786 13.1421 1.5 9 1.5C4.85786 1.5 1.5 4.85786 1.5 9C1.5 13.1421 4.85786 16.5 9 16.5Z" stroke="#A1A1AA" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9 4.5V9L12 10.5" stroke="#A1A1AA" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 445 B

-4
View File
@@ -1,4 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18" fill="none">
<circle cx="9" cy="5.25" r="3" stroke="#A1A1AA" stroke-width="1.5"/>
<path d="M3 15.75C3 12.85 5.69 10.5 9 10.5C12.31 10.5 15 12.85 15 15.75" stroke="#A1A1AA" stroke-width="1.5" stroke-linecap="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 310 B

-3
View File
@@ -1,3 +0,0 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9.75 1.5L2.25 10.5H9L8.25 16.5L15.75 7.5H9L9.75 1.5Z" stroke="#A1A1AA" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 252 B

-3
View File
@@ -1,3 +0,0 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M14.25 15.75L9 12L3.75 15.75V3.75C3.75 3.35218 3.90804 2.97064 4.18934 2.68934C4.47064 2.40804 4.85218 2.25 5.25 2.25H12.75C13.1478 2.25 13.5294 2.40804 13.8107 2.68934C14.092 2.97064 14.25 3.35218 14.25 3.75V15.75Z" stroke="#A1A1AA" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 414 B

@@ -1,5 +0,0 @@
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8.5 14.5H31.5L30.2 34H9.8L8.5 14.5Z" stroke="#7C7C86" stroke-width="2.8" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M14 15V11.5C14 8.18629 16.6863 5.5 20 5.5C23.3137 5.5 26 8.18629 26 11.5V15" stroke="#7C7C86" stroke-width="2.8" stroke-linecap="round"/>
<path d="M17.5 21L24.5 25.25L17.5 29.5V21Z" stroke="#7C7C86" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 517 B

-4
View File
@@ -1,4 +0,0 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7.99998 9.99935C10.5773 9.99935 12.6666 7.91001 12.6666 5.33268C12.6666 2.75535 10.5773 0.666016 7.99998 0.666016C5.42265 0.666016 3.33331 2.75535 3.33331 5.33268C3.33331 7.91001 5.42265 9.99935 7.99998 9.99935Z" stroke="#E9C977" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M5.47335 9.26057L4.66669 15.3339L8.00002 13.3339L11.3334 15.3339L10.5267 9.25391" stroke="#E9C977" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 595 B

@@ -1,4 +0,0 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M16.5 8.31039V9.00039C16.4991 10.6177 15.9754 12.1914 15.007 13.4868C14.0386 14.7821 12.6775 15.7297 11.1265 16.1883C9.57557 16.6469 7.91794 16.5918 6.40085 16.0313C4.88376 15.4708 3.58849 14.435 2.70822 13.0782C1.82795 11.7214 1.40984 10.1164 1.51626 8.50262C1.62267 6.88881 2.24791 5.35263 3.29871 4.12319C4.34951 2.89375 5.76959 2.03692 7.34714 1.6805C8.92469 1.32407 10.5752 1.48714 12.0525 2.14539" stroke="#E9C977" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M16.5 3L9 10.5075L6.75 8.2575" stroke="#E9C977" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 726 B

-3
View File
@@ -1,3 +0,0 @@
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10 3L4.5 8.5L2 6" stroke="white" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 195 B

-4
View File
@@ -1,4 +0,0 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M13.5 4.5L4.5 13.5" stroke="#A1A1AA" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M4.5 4.5L13.5 13.5" stroke="#A1A1AA" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 331 B

-3
View File
@@ -1,3 +0,0 @@
<svg width="8" height="8" viewBox="0 0 8 8" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.00008 0.666016L5.03008 2.75268L7.33342 3.08935L5.66675 4.71268L6.06008 7.00602L4.00008 5.92268L1.94008 7.00602L2.33341 4.71268L0.666748 3.08935L2.97008 2.75268L4.00008 0.666016Z" fill="#3A2A0A"/>
</svg>

Before

Width:  |  Height:  |  Size: 307 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 341 KiB

-5
View File
@@ -1,5 +0,0 @@
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10.5 7.5V9.5C10.5 9.76522 10.3946 10.0196 10.2071 10.2071C10.0196 10.3946 9.76522 10.5 9.5 10.5H2.5C2.23478 10.5 1.98043 10.3946 1.79289 10.2071C1.60536 10.0196 1.5 9.76522 1.5 9.5V7.5" stroke="#A1A1AA" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M3.5 5L6 7.5L8.5 5" stroke="#A1A1AA" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M6 7.5V1.5" stroke="#A1A1AA" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 547 B

-4
View File
@@ -1,4 +0,0 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12.6667 7.33398H3.33333C2.59695 7.33398 2 7.93094 2 8.66732V13.334C2 14.0704 2.59695 14.6673 3.33333 14.6673H12.6667C13.403 14.6673 14 14.0704 14 13.334V8.66732C14 7.93094 13.403 7.33398 12.6667 7.33398Z" stroke="#E9C977" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M4.66669 7.33398V4.66732C4.66669 3.78326 5.01788 2.93542 5.643 2.3103C6.26812 1.68517 7.11597 1.33398 8.00002 1.33398C8.88408 1.33398 9.73192 1.68517 10.357 2.3103C10.9822 2.93542 11.3334 3.78326 11.3334 4.66732V7.33398" stroke="#E9C977" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 726 B

-3
View File
@@ -1,3 +0,0 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8.66667 1.33398L2 9.33398H8L7.33333 14.6673L14 6.66732H8L8.66667 1.33398Z" stroke="#E9C977" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 277 B

-4
View File
@@ -1,4 +0,0 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M15 15.75V14.25C15 13.4544 14.6839 12.6913 14.1213 12.1287C13.5587 11.5661 12.7956 11.25 12 11.25H6C5.20435 11.25 4.44129 11.5661 3.87868 12.1287C3.31607 12.6913 3 13.4544 3 14.25V15.75" stroke="#7C7C86" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9 8.25C10.6569 8.25 12 6.90685 12 5.25C12 3.59315 10.6569 2.25 9 2.25C7.34315 2.25 6 3.59315 6 5.25C6 6.90685 7.34315 8.25 9 8.25Z" stroke="#7C7C86" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 611 B

-515
View File
@@ -1,515 +0,0 @@
/**
* 认证相关前端封装:注册 / 登录 / 游客登录走本站 BFF,token 等写入 cookie。
* 未注册用户自动游客登录(POST /api/auth/guest → 上游 /api/app/auth/guest)。
*/
(function (global) {
var UUID_KEY = "uuid";
var DEV_ID_KEY = "guest_dev_id";
var CHANNEL_KEY = "channel";
var GUEST_LOGOUT_FLAG_KEY = "guest_logout_manual";
var guestLoginPromise = null;
var volatileDevID = "";
/** 上游约定:token 失效需重新登录 */
var TOKEN_EXPIRED_CODE = 1000;
var tokenRecoveryPromise = null;
function toast(message, type) {
if (typeof global.showToast === "function") {
global.showToast(message, type || "info");
return;
}
// 调试兜底:无 toast 组件时用简易提示
try {
var tip = String(message || "").trim();
if (!tip) return;
var el = document.getElementById("auth-debug-toast");
if (!el) {
el = document.createElement("div");
el.id = "auth-debug-toast";
el.setAttribute("role", "status");
el.style.cssText =
"position:fixed;left:50%;bottom:48px;transform:translateX(-50%);z-index:99999;" +
"max-width:86vw;padding:10px 16px;border-radius:8px;font-size:14px;line-height:1.4;" +
"color:#fff;background:rgba(20,20,20,.88);box-shadow:0 8px 24px rgba(0,0,0,.25);";
document.body.appendChild(el);
}
el.textContent = tip;
el.style.background =
type === "error"
? "rgba(180,40,40,.92)"
: type === "success"
? "rgba(30,120,70,.92)"
: "rgba(20,20,20,.88)";
el.style.display = "block";
clearTimeout(el._hideTimer);
el._hideTimer = setTimeout(function () {
el.style.display = "none";
}, 2600);
} catch (e) {}
}
function getInviteCode() {
try {
var params = new URLSearchParams(global.location.search || "");
return (
params.get("inviteCode") ||
params.get("invite") ||
params.get("code") ||
""
).trim();
} catch (e) {
return "";
}
}
function hasSessionToken() {
return !!(global.CookieUtil && global.CookieUtil.getToken && global.CookieUtil.getToken());
}
// 与 91porn-h5-client generateUid 保持一致:16 位十六进制随机串 + 毫秒时间戳。
function generateUUID() {
var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
var uuid = [];
for (var i = 0; i < 16; i++) {
uuid[i] = chars.charAt(Math.floor(Math.random() * 16));
}
return uuid.join("") + Date.now();
}
function syncDevID(devID) {
if (!devID) return;
try {
localStorage.setItem(UUID_KEY, devID);
// 保留旧 key,兼容已上线页面和服务端游客 cookie。
localStorage.setItem(DEV_ID_KEY, devID);
} catch (e) {}
try {
if (global.CookieUtil && typeof global.CookieUtil.set === "function") {
global.CookieUtil.set(DEV_ID_KEY, devID, { days: 365 });
}
} catch (e2) {}
}
function getOrCreateDevID() {
var devID = "";
try {
devID = (localStorage.getItem(UUID_KEY) || "").trim();
} catch (e) {}
if (devID) {
syncDevID(devID);
return devID;
}
// 首屏 SSR 可能已生成同规则 UUID 并写入 cookie,优先复用以保持游客身份一致。
var fromCookie =
global.CookieUtil && typeof global.CookieUtil.get === "function"
? global.CookieUtil.get(DEV_ID_KEY)
: "";
if (fromCookie) {
syncDevID(fromCookie);
return fromCookie;
}
if (!volatileDevID) {
volatileDevID = generateUUID();
}
syncDevID(volatileDevID);
return volatileDevID;
}
function getChannel() {
if (
global.ChannelAttribution &&
typeof global.ChannelAttribution.get === "function"
) {
return global.ChannelAttribution.get();
}
var channel = "";
try {
channel = (localStorage.getItem(CHANNEL_KEY) || "").trim();
if (channel) {
if (global.CookieUtil && typeof global.CookieUtil.set === "function") {
global.CookieUtil.set(CHANNEL_KEY, channel, { days: 365 });
}
return channel;
}
} catch (e) {}
try {
channel =
global.CookieUtil && typeof global.CookieUtil.get === "function"
? String(global.CookieUtil.get(CHANNEL_KEY) || "").trim()
: "";
if (channel) {
try {
localStorage.setItem(CHANNEL_KEY, channel);
} catch (e2) {}
return channel;
}
} catch (e3) {}
try {
var params = new URLSearchParams(global.location.search || "");
channel = (params.get("dc") || params.get("ch") || params.get("pc") || "").trim();
if (channel) {
localStorage.setItem(CHANNEL_KEY, channel);
if (global.CookieUtil && typeof global.CookieUtil.set === "function") {
global.CookieUtil.set(CHANNEL_KEY, channel, { days: 365 });
}
}
} catch (e4) {}
return channel;
}
function clearGuestLogoutFlag() {
try {
sessionStorage.removeItem(GUEST_LOGOUT_FLAG_KEY);
} catch (e) {}
try {
if (global.CookieUtil && typeof global.CookieUtil.remove === "function") {
global.CookieUtil.remove("guest_skip_auto");
}
} catch (e2) {}
}
function markGuestLogout() {
try {
sessionStorage.setItem(GUEST_LOGOUT_FLAG_KEY, "1");
} catch (e) {}
try {
if (global.CookieUtil && typeof global.CookieUtil.set === "function") {
// 不设 days → session cookie,配合 SSR 跳过本次自动游客
global.CookieUtil.set("guest_skip_auto", "1");
}
} catch (e2) {}
}
function isGuestLogoutMarked() {
try {
if (sessionStorage.getItem(GUEST_LOGOUT_FLAG_KEY) === "1") return true;
} catch (e) {}
try {
if (
global.CookieUtil &&
typeof global.CookieUtil.get === "function" &&
global.CookieUtil.get("guest_skip_auto") === "1"
) {
return true;
}
} catch (e2) {}
return false;
}
function syncTrackerUid(user) {
if (!user || !global.tracker || typeof global.tracker.setUid !== "function") return;
var uid = user.userId != null ? user.userId : user.uid != null ? user.uid : user.id;
if (uid == null || String(uid) === "") return;
global.tracker.setUid(String(uid));
}
function applyAuthSession(res, failTip) {
if (!res || res.code !== 200 || !res.data) {
var err = new Error((res && (res.tip || res.msg)) || failTip || "请求失败");
err.code = res && res.code;
err.data = res;
throw err;
}
if (global.CookieUtil && typeof global.CookieUtil.setAuthSession === "function") {
global.CookieUtil.setAuthSession(res.data);
}
clearGuestLogoutFlag();
syncTrackerUid(res.data);
try {
document.dispatchEvent(
new CustomEvent("auth:login", { detail: { user: res.data } })
);
} catch (e) {}
return res;
}
function register(payload) {
var channel = getChannel();
var body = {
accountNo: (payload.accountNo || "").trim(),
password: payload.password || "",
devID: getOrCreateDevID(),
channel: channel,
};
var invite = (payload.agentInviteCode || getInviteCode() || "").trim();
if (invite) body.agentInviteCode = invite;
// 游客升级注册:携带当前游客 token,上游按 Authorization 绑定原游客账号。
var options = {};
var token =
global.CookieUtil && typeof global.CookieUtil.getToken === "function"
? global.CookieUtil.getToken()
: "";
var registered =
global.CookieUtil && typeof global.CookieUtil.isRegistered === "function"
? global.CookieUtil.isRegistered()
: false;
if (token && !registered) {
options.headers = { Authorization: token };
}
return global.HttpRequest.post("/api/auth/register", body, options).then(function (res) {
return applyAuthSession(res, "注册失败");
});
}
function login(payload) {
var body = {
account: (payload.account || "").trim(),
password: payload.password || "",
devID: getOrCreateDevID(),
channel: getChannel(),
};
return global.HttpRequest.post("/api/auth/login", body).then(function (res) {
return applyAuthSession(res, "登录失败");
});
}
function resetPassword(payload) {
return global.HttpRequest.post("/api/auth/resetPassword", {
accountNo: (payload.accountNo || "").trim(),
email: (payload.email || "").trim(),
newPassword: payload.newPassword || "",
});
}
function guestLogin() {
var devId = getOrCreateDevID();
return global.HttpRequest.post("/api/auth/guest", {
devID: devId,
deviceId: devId,
channel: getChannel(),
mac: "",
}).then(function (res) {
return applyAuthSession(res, "游客登录失败");
});
}
function isAuthEndpoint(url) {
return /\/api\/auth\/(guest|login|register|resetPassword)(\?|$)/.test(String(url || ""));
}
function isSafeRedirect(path) {
return typeof path === "string" && path.charAt(0) === "/" && path.charAt(1) !== "/";
}
/** PC 打开登录弹窗;H5 跳转登录页 */
function promptRegisteredRelogin() {
toast("登录已过期,请重新登录", "warning");
var isMobile =
global.matchMedia && global.matchMedia("(max-width: 768px)").matches;
if (!isMobile && typeof global.openLoginPop === "function") {
try {
global.openLoginPop();
return;
} catch (e) {}
}
if (typeof global.openLoginDialog === "function") {
try {
var opened = global.openLoginDialog();
if (opened && typeof opened.then === "function") {
opened.catch(function () {
global.location.href = "/login";
});
return;
}
return;
} catch (e2) {}
}
global.location.href = "/login";
}
/**
* 接口返回 code=1000(token 失效)时的统一处理。
* - 游客:清会话后走游客登录,成功后可重试原请求
* - 注册账号:清会话并打开登录弹窗 / 跳转登录页
* @returns {Promise<{ok:boolean, guest?:boolean, needLogin?:boolean, res?:any}>}
*/
function handleTokenExpired() {
if (tokenRecoveryPromise) {
return tokenRecoveryPromise;
}
var wasRegistered =
global.CookieUtil && typeof global.CookieUtil.isRegistered === "function"
? global.CookieUtil.isRegistered()
: false;
if (global.CookieUtil && typeof global.CookieUtil.clearAuth === "function") {
global.CookieUtil.clearAuth();
}
try {
document.dispatchEvent(
new CustomEvent("auth:logout", { detail: { reason: "token-expired" } })
);
} catch (e) {}
if (wasRegistered) {
var registeredJob = Promise.resolve().then(function () {
promptRegisteredRelogin();
return { ok: false, needLogin: true };
});
tokenRecoveryPromise = registeredJob;
registeredJob.finally(function () {
setTimeout(function () {
if (tokenRecoveryPromise === registeredJob) {
tokenRecoveryPromise = null;
}
}, 1200);
});
return registeredJob;
}
clearGuestLogoutFlag();
var guestJob = guestLogin()
.then(function (res) {
return { ok: true, guest: true, res: res };
})
.catch(function (err) {
toast("会话已失效,请刷新页面重试", "error");
throw err;
})
.finally(function () {
if (tokenRecoveryPromise === guestJob) {
tokenRecoveryPromise = null;
}
});
tokenRecoveryPromise = guestJob;
return guestJob;
}
/**
* 无 token(未自己登录)时自动游客登录。
* SSR 也会在首屏补游客;此处作客户端兜底。
* 手动退出后跳过本次;清掉标记后,下次进页再自动游客。
* BFF: POST /api/auth/guest → 上游 POST /api/app/auth/guest
*/
function ensureGuestSession() {
if (hasSessionToken()) {
return Promise.resolve({ ok: true, skipped: true });
}
if (isGuestLogoutMarked()) {
// 本次因手动退出跳过;清标记,下次进页恢复自动游客
clearGuestLogoutFlag();
return Promise.resolve({ ok: true, skipped: true, reason: "manual-logout" });
}
if (!global.HttpRequest) {
return Promise.resolve({ ok: false, skipped: true, reason: "no-http" });
}
if (!guestLoginPromise) {
guestLoginPromise = guestLogin()
.then(function (res) {
return { ok: true, res: res };
})
.catch(function (err) {
return { ok: false, error: err };
})
.finally(function () {
guestLoginPromise = null;
});
}
return guestLoginPromise;
}
function uploadImage(file) {
var fd = new FormData();
fd.append("file", file);
return global.HttpRequest.post("/api/upload/image", fd).then(function (res) {
if (!res || res.code !== 200 || !res.data || !res.data.url) {
var err = new Error((res && (res.tip || res.msg)) || "上传失败");
err.code = res && res.code;
err.data = res;
throw err;
}
return res.data;
});
}
function updateProfile(payload) {
var body = {};
if (payload.nickName != null && String(payload.nickName).trim() !== "") {
body.nickName = String(payload.nickName).trim();
}
if (payload.introduction != null) {
body.introduction = String(payload.introduction).trim();
}
if (payload.avatar) body.avatar = payload.avatar;
if (payload.oldPassword) body.oldPassword = payload.oldPassword;
if (payload.newPassword) body.newPassword = payload.newPassword;
if (typeof payload.privacyMode === "boolean") body.privacyMode = payload.privacyMode;
return global.HttpRequest.post("/api/auth/profile", body).then(function (res) {
if (!res || res.code !== 200) {
var err = new Error((res && (res.tip || res.msg)) || "保存失败");
err.code = res && res.code;
err.data = res;
throw err;
}
if (res.data && global.CookieUtil && typeof global.CookieUtil.setAuthSession === "function") {
global.CookieUtil.setAuthSession(res.data);
}
if (res.data) syncTrackerUid(res.data);
return res;
});
}
function isValidEmail(email) {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(String(email || "").trim());
}
/** 登录凭证:账号 4–16 位,或合法邮箱 */
function isValidLoginAccount(account) {
var v = String(account || "").trim();
if (!v) return false;
if (v.indexOf("@") >= 0) return isValidEmail(v);
return v.length >= 4 && v.length <= 16;
}
function logout() {
markGuestLogout();
if (global.CookieUtil && typeof global.CookieUtil.clearAuth === "function") {
global.CookieUtil.clearAuth();
}
try {
document.dispatchEvent(new CustomEvent("auth:logout", { detail: { reason: "manual" } }));
} catch (e) {}
if (!global.HttpRequest) {
return Promise.resolve({ code: 200 });
}
return global.HttpRequest.post("/api/auth/logout", {}).catch(function () {
return { code: 200 };
});
}
global.AuthAPI = {
TOKEN_EXPIRED_CODE: TOKEN_EXPIRED_CODE,
register: register,
login: login,
logout: logout,
resetPassword: resetPassword,
guestLogin: guestLogin,
ensureGuestSession: ensureGuestSession,
handleTokenExpired: handleTokenExpired,
isAuthEndpoint: isAuthEndpoint,
markGuestLogout: markGuestLogout,
hasSessionToken: hasSessionToken,
uploadImage: uploadImage,
updateProfile: updateProfile,
getInviteCode: getInviteCode,
getOrCreateDevID: getOrCreateDevID,
getChannel: getChannel,
isValidEmail: isValidEmail,
isValidLoginAccount: isValidLoginAccount,
toast: toast,
};
// 每次页面初始化都准备好持久设备 UUID,并捕获首次渠道。
getOrCreateDevID();
getChannel();
// 首屏无 token → 自动游客登录
ensureGuestSession();
})(typeof window !== "undefined" ? window : this);
-92
View File
@@ -1,92 +0,0 @@
/**
* 全站渠道首次归因。
* 与落地页约定一致,按 dc、ch、pc 顺序识别,并同步到 localStorage 与 cookie。
*/
(function (global) {
"use strict";
var STORAGE_KEY = "channel";
var QUERY_KEYS = ["dc", "ch", "pc"];
function clean(value) {
return String(value == null ? "" : value).trim();
}
function readStorage() {
try {
return clean(global.localStorage.getItem(STORAGE_KEY));
} catch (e) {
return "";
}
}
function writeStorage(channel) {
try {
global.localStorage.setItem(STORAGE_KEY, channel);
} catch (e) {}
}
function readCookie() {
try {
var match = document.cookie.match(/(?:^|;\s*)channel=([^;]*)/);
return match ? clean(decodeURIComponent(match[1])) : "";
} catch (e) {
return "";
}
}
function writeCookie(channel) {
try {
var secure = global.location && global.location.protocol === "https:" ? "; Secure" : "";
document.cookie =
STORAGE_KEY +
"=" +
encodeURIComponent(channel) +
"; Path=/; Max-Age=31536000; SameSite=Lax" +
secure;
} catch (e) {}
}
function readQuery() {
try {
var params = new URLSearchParams(global.location.search || "");
for (var i = 0; i < QUERY_KEYS.length; i += 1) {
var channel = clean(params.get(QUERY_KEYS[i]));
if (channel) return channel;
}
} catch (e) {}
return "";
}
function syncTracker(channel) {
try {
if (global.tracker && typeof global.tracker.setChannel === "function") {
global.tracker.setChannel(channel);
}
} catch (e) {}
}
function capture() {
// localStorage 与服务端 cookie 都属于已归因结果,必须先于当前 URL。
var channel = readStorage() || readCookie() || readQuery();
if (!channel) return "";
writeStorage(channel);
writeCookie(channel);
global.__CHANNEL_CODE__ = channel;
syncTracker(channel);
return channel;
}
global.ChannelAttribution = {
get: capture,
capture: capture,
};
var channel = capture();
try {
document.dispatchEvent(
new CustomEvent("channel:ready", { detail: { channel: channel } })
);
} catch (e) {}
})(window);
-204
View File
@@ -1,204 +0,0 @@
/**
* Cookie 读写(登录 token / sid 等统一落 cookie)
*/
(function (global) {
function encode(value) {
return encodeURIComponent(String(value == null ? "" : value));
}
function decode(value) {
try {
// 兼容服务端历史 QueryEscape:空格被编成 "+",decodeURIComponent 不会还原
return decodeURIComponent(String(value || "").replace(/\+/g, "%20"));
} catch (e) {
return value;
}
}
function getCookie(name) {
if (!name) return "";
var key = encodeURIComponent(name) + "=";
var parts = String(document.cookie || "").split(";");
for (var i = 0; i < parts.length; i++) {
var part = parts[i].replace(/^\s+/, "");
if (part.indexOf(key) === 0) {
return decode(part.slice(key.length));
}
}
return "";
}
/**
* @param {string} name
* @param {string} value
* @param {{ days?: number, path?: string, sameSite?: string, secure?: boolean }} [options]
*/
function setCookie(name, value, options) {
options = options || {};
var days = options.days;
var path = options.path || "/";
var sameSite = options.sameSite || "Lax";
var parts = [
encodeURIComponent(name) + "=" + encode(value),
"path=" + path,
"SameSite=" + sameSite,
];
if (typeof days === "number") {
var expires = new Date();
expires.setTime(expires.getTime() + days * 24 * 60 * 60 * 1000);
parts.push("expires=" + expires.toUTCString());
}
if (options.secure || (global.location && global.location.protocol === "https:")) {
parts.push("Secure");
}
document.cookie = parts.join("; ");
}
function removeCookie(name, path) {
setCookie(name, "", { days: -1, path: path || "/" });
}
var AUTH = {
TOKEN: "token",
SID: "sid",
S_TOKEN: "_s_token_",
USER_ID: "userId",
ACCOUNT_NO: "accountNo",
NICK_NAME: "nickName",
INTRODUCTION: "introduction",
AVATAR: "avatar",
EMAIL: "email",
INVITE_CODE: "inviteCode",
RECHARGE: "recharge",
VIP_TYPE: "vipType",
VIP_EXPIRE: "currentVipExpire",
SHOW_CARD_ID: "showCardId",
SHOW_CARD_NAME: "showCardName",
USER_TYPE: "userType",
IS_REGISTER: "isRegister",
};
var AUTH_KEYS = [
AUTH.TOKEN,
AUTH.USER_ID,
AUTH.ACCOUNT_NO,
AUTH.NICK_NAME,
AUTH.INTRODUCTION,
AUTH.AVATAR,
AUTH.EMAIL,
AUTH.INVITE_CODE,
AUTH.RECHARGE,
AUTH.VIP_TYPE,
AUTH.VIP_EXPIRE,
AUTH.SHOW_CARD_ID,
AUTH.SHOW_CARD_NAME,
AUTH.USER_TYPE,
AUTH.IS_REGISTER,
"_token_",
AUTH.SID,
AUTH.S_TOKEN,
];
function getToken() {
return getCookie(AUTH.TOKEN) || getCookie("_token_") || "";
}
function setToken(token, days) {
setCookie(AUTH.TOKEN, token || "", { days: typeof days === "number" ? days : 7 });
}
function getSid() {
return getCookie(AUTH.SID) || "";
}
function setSid(sid, days) {
setCookie(AUTH.SID, sid || "", { days: typeof days === "number" ? days : 7 });
}
function setAuthSession(user, days) {
if (!user) return;
var d = typeof days === "number" ? days : 7;
if (user.token) setToken(user.token, d);
if (user.userId != null) setCookie(AUTH.USER_ID, String(user.userId), { days: d });
if (user.accountNo) setCookie(AUTH.ACCOUNT_NO, user.accountNo, { days: d });
if (user.nickName) setCookie(AUTH.NICK_NAME, user.nickName, { days: d });
if (user.introduction != null) setCookie(AUTH.INTRODUCTION, user.introduction || "", { days: d });
if (user.avatar != null) setCookie(AUTH.AVATAR, user.avatar || "", { days: d });
if (user.email != null) setCookie(AUTH.EMAIL, user.email || "", { days: d });
if (user.inviteCode != null) setCookie(AUTH.INVITE_CODE, user.inviteCode || "", { days: d });
// 仅在响应显式带 recharge 时写入(含 0),避免局部更新把余额抹成 0
if (Object.prototype.hasOwnProperty.call(user, "recharge")) {
setCookie(AUTH.RECHARGE, String(Number(user.recharge) || 0), { days: d });
}
if (user.vipType != null) setCookie(AUTH.VIP_TYPE, String(user.vipType), { days: d });
if (user.currentVipExpire != null) {
setCookie(AUTH.VIP_EXPIRE, user.currentVipExpire || "", { days: d });
}
if (Object.prototype.hasOwnProperty.call(user, "showCardId")) {
setCookie(AUTH.SHOW_CARD_ID, String(user.showCardId != null ? user.showCardId : ""), { days: d });
}
if (Object.prototype.hasOwnProperty.call(user, "showCardName")) {
setCookie(AUTH.SHOW_CARD_NAME, user.showCardName || "", { days: d });
}
if (user.type != null) setCookie(AUTH.USER_TYPE, String(user.type), { days: d });
if (Object.prototype.hasOwnProperty.call(user, "isRegister") || user.type != null) {
var registered = !!(user.isRegister || Number(user.type) === 1);
setCookie(AUTH.IS_REGISTER, registered ? "1" : "0", { days: d });
}
}
function getAuthSession() {
var token = getToken();
if (!token) return null;
return {
token: token,
userId: getCookie(AUTH.USER_ID),
accountNo: getCookie(AUTH.ACCOUNT_NO),
nickName: getCookie(AUTH.NICK_NAME),
introduction: getCookie(AUTH.INTRODUCTION),
avatar: getCookie(AUTH.AVATAR),
email: getCookie(AUTH.EMAIL),
inviteCode: getCookie(AUTH.INVITE_CODE),
recharge: getCookie(AUTH.RECHARGE),
vipType: getCookie(AUTH.VIP_TYPE),
currentVipExpire: getCookie(AUTH.VIP_EXPIRE),
showCardId: getCookie(AUTH.SHOW_CARD_ID),
showCardName: getCookie(AUTH.SHOW_CARD_NAME),
type: getCookie(AUTH.USER_TYPE),
isRegister: getCookie(AUTH.IS_REGISTER) === "1",
};
}
function clearAuth() {
for (var i = 0; i < AUTH_KEYS.length; i++) {
removeCookie(AUTH_KEYS[i]);
}
}
function isLoggedIn() {
return !!getToken();
}
/** 是否正式注册账号(非游客) */
function isRegistered() {
if (!isLoggedIn()) return false;
if (getCookie(AUTH.IS_REGISTER) === "1") return true;
return Number(getCookie(AUTH.USER_TYPE)) === 1;
}
global.CookieUtil = {
get: getCookie,
set: setCookie,
remove: removeCookie,
AUTH: AUTH,
getToken: getToken,
setToken: setToken,
getSid: getSid,
setSid: setSid,
setAuthSession: setAuthSession,
getAuthSession: getAuthSession,
clearAuth: clearAuth,
isLoggedIn: isLoggedIn,
isRegistered: isRegistered,
};
})(typeof window !== "undefined" ? window : this);
+12 -234
View File
@@ -1,160 +1,10 @@
/**
* 前端请求封装。
* - 登录态走 cookie(credentials: include)
* - 浏览器无法改写 User-Agent;X-Real-IP 由服务端反代上游时注入
* - 同源业务请求携带 SDK 设备字段;短剧服务端事件额外携带完整公共字段快照
* - token 重试复用同一份 SDK Header、埋点上下文和 request_id
* - request_id 只用于请求关联;最终 event_id 由服务端按每条事件的最终字段生成
* - 直连上游时若需自定义头,请走本站 Go 代理接口
*/
(function (global) {
var DEFAULT_TIMEOUT_MS = 30000;
var TRACKER_CONTEXT_TIMEOUT_MS = 1500;
var DRAMA_SERVER_EVENT_ROUTES = {
"/api/playlet/chapter/like/set": "drama_like",
"/api/playlet/like/set": "drama_like",
"/api/playlet/chapter/collect/set": "drama_collect",
"/api/playlet/collect/set": "drama_collect",
"/api/comment/send": "drama_comment",
"/api/playlet/chapter/unlock": "drama_purchase",
};
function randomRequestId() {
var bytes = new Uint8Array(16);
if (global.crypto && typeof global.crypto.getRandomValues === "function") {
global.crypto.getRandomValues(bytes);
return Array.prototype.map.call(bytes, function (value) {
return (value + 256).toString(16).slice(-2);
}).join("");
}
var fallback = (Date.now().toString(16) + Math.random().toString(16).slice(2) + Math.random().toString(16).slice(2)).slice(0, 32);
while (fallback.length < 32) fallback += "0";
return fallback;
}
function cleanHeaderPart(value) {
return String(value == null ? "" : value).replace(/[^\x20-\x7E]|[;=]/g, " ").trim();
}
function sameOrigin(url) {
try {
return new URL(url, global.location.href).origin === global.location.origin;
} catch (error) {
return true;
}
}
function dramaServerEvent(url, method, options) {
if (method === "GET" || method === "HEAD" || !sameOrigin(url)) return "";
if (options.trackerEvent === false) return "";
if (typeof options.trackerEvent === "string") {
var explicit = options.trackerEvent.trim();
return /^(drama_like|drama_comment|drama_collect|drama_purchase)$/.test(explicit)
? explicit
: "";
}
try {
return DRAMA_SERVER_EVENT_ROUTES[new URL(url, global.location.href).pathname] || "";
} catch (error) {
return "";
}
}
function commonFields(options) {
if (options._trackerCommonFieldsPromise) return options._trackerCommonFieldsPromise;
var getter = global.tracker && typeof global.tracker.getCommonFields === "function"
? global.tracker.getCommonFields
: global.WebSDK && typeof global.WebSDK.getCommonFields === "function"
? global.WebSDK.getCommonFields.bind(global.WebSDK)
: null;
options._trackerCommonFieldsPromise = getter
? Promise.resolve().then(function () { return getter(); }).catch(function () { return {}; })
: Promise.resolve({});
return options._trackerCommonFieldsPromise;
}
function currentDramaContext(options) {
var context;
if (options && options.trackerContext && typeof options.trackerContext === "object") {
context = options.trackerContext;
} else if (global.tracker && typeof global.tracker.getDramaContext === "function") {
context = global.tracker.getDramaContext() || {};
} else {
context = {};
}
return {
media_id: String(context.media_id == null ? "" : context.media_id).trim(),
drama_id: String(context.drama_id == null ? "" : context.drama_id).trim(),
episode: Math.max(1, parseInt(context.episode, 10) || 1),
video_id: String(context.video_id == null ? "" : context.video_id).trim(),
recommend_trace_id: String(context.recommend_trace_id == null ? "" : context.recommend_trace_id).trim(),
};
}
function base64URLJSON(value) {
try {
var json = JSON.stringify(value);
var bytes = encodeURIComponent(json).replace(/%([0-9A-F]{2})/g, function (_, hex) {
return String.fromCharCode(parseInt(hex, 16));
});
return global.btoa(bytes).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
} catch (error) {
return "";
}
}
function trackerHeaders(url, method, options) {
var eventName = dramaServerEvent(url, method, options);
if (!eventName) return Promise.resolve({});
if (!options._trackerContextPromise) {
if (!options._trackerRequestId) options._trackerRequestId = randomRequestId();
var drama = currentDramaContext(options);
if (!drama || !String(drama.drama_id || "").trim()) {
options._trackerContextPromise = Promise.resolve({});
} else {
options._trackerContextPromise = commonFields(options).then(function (fields) {
var packedContext = base64URLJSON({
v: 1,
request_id: options._trackerRequestId,
event_name: eventName,
common: fields,
drama: drama,
});
if (!packedContext) return {};
var result = {
"X-Tracker-Request-Id": options._trackerRequestId,
"X-Tracker-Event-Name": eventName,
"X-DC-Request-Context": packedContext,
};
result["X-User-Agent"] = [
"DevID=" + cleanHeaderPart(fields.device_id),
"DevType=" + cleanHeaderPart(fields.device || "OTHER"),
"SysType=" + (fields.device === "Android" ? "h5_android" : fields.device === "iOS" ? "h5_ios" : "h5_pc"),
"Ver=" + cleanHeaderPart(fields.app_version || "1.0.0"),
"BuildID=web",
"device_brand=" + cleanHeaderPart(fields.device_brand),
"device_model=" + cleanHeaderPart(fields.device_model),
"sid=" + cleanHeaderPart(fields.sid),
].join(";");
return result;
});
}
}
return new Promise(function (resolve) {
var settled = false;
var timer = global.setTimeout(function () {
if (settled) return;
settled = true;
resolve({});
}, TRACKER_CONTEXT_TIMEOUT_MS);
options._trackerContextPromise.then(function (headers) {
if (settled) return;
settled = true;
global.clearTimeout(timer);
resolve(headers);
});
});
}
function buildQuery(params) {
if (!params) return "";
@@ -175,8 +25,6 @@
* params?: Record<string, any>,
* headers?: Record<string, string>,
* timeout?: number,
* trackerEvent?: false|string,
* trackerContext?: Record<string, any>,
* }} [options]
*/
function request(url, options) {
@@ -206,52 +54,20 @@
var timeoutMs =
typeof options.timeout === "number" ? options.timeout : DEFAULT_TIMEOUT_MS;
function sdkCommonHeader() {
if (headers["X-SDK-Common"] || options._skipSDKCommon || !sameOrigin(finalUrl)) {
return Promise.resolve("");
}
if (!options._sdkCommonHeaderPromise) {
options._sdkCommonHeaderPromise = Promise.resolve()
.then(function () {
if (
global.tracker &&
typeof global.tracker.getSDKCommonHeader === "function"
) {
return global.tracker.getSDKCommonHeader();
}
return "";
})
.catch(function () { return ""; });
}
return options._sdkCommonHeaderPromise;
var fetchOpts = {
method: method,
headers: headers,
credentials: "include",
signal: controller ? controller.signal : undefined,
};
if (body != null) fetchOpts.body = body;
if (controller && timeoutMs > 0) {
timer = setTimeout(function () {
controller.abort();
}, timeoutMs);
}
return Promise.all([
trackerHeaders(finalUrl, method, options).catch(function () { return {}; }),
sdkCommonHeader(),
])
.then(function (prepared) {
var tracking = prepared[0] || {};
Object.keys(tracking).forEach(function (key) {
if (headers[key] == null) headers[key] = tracking[key];
});
if (!headers["X-SDK-Common"] && prepared[1]) {
headers["X-SDK-Common"] = prepared[1];
}
var fetchOpts = {
method: method,
headers: headers,
credentials: "include",
signal: controller ? controller.signal : undefined,
};
if (body != null) fetchOpts.body = body;
if (controller && timeoutMs > 0) {
timer = setTimeout(function () {
controller.abort();
}, timeoutMs);
}
return fetch(finalUrl, fetchOpts);
})
return fetch(finalUrl, fetchOpts)
.then(function (res) {
var contentType = res.headers.get("content-type") || "";
var parse = contentType.indexOf("application/json") >= 0
@@ -270,49 +86,11 @@
return data;
});
})
.then(function (data) {
return handleTokenExpiredResponse(url, options, data);
})
.finally(function () {
if (timer) clearTimeout(timer);
});
}
/**
* 业务码 1000:token 失效。
* 游客 → 重新游客登录并重试一次;注册账号 → 打开登录弹窗/跳转登录页。
*/
function handleTokenExpiredResponse(url, options, data) {
options = options || {};
var expiredCode =
(global.AuthAPI && global.AuthAPI.TOKEN_EXPIRED_CODE) || 1000;
if (!data || Number(data.code) !== Number(expiredCode)) {
return data;
}
if (options._skipTokenRecovery || options._tokenRetried) {
return data;
}
if (
global.AuthAPI &&
typeof global.AuthAPI.isAuthEndpoint === "function" &&
global.AuthAPI.isAuthEndpoint(url)
) {
return data;
}
if (!global.AuthAPI || typeof global.AuthAPI.handleTokenExpired !== "function") {
return data;
}
return global.AuthAPI.handleTokenExpired().then(function (result) {
if (result && result.ok && result.guest) {
return request(
url,
Object.assign({}, options, { _tokenRetried: true })
);
}
return data;
});
}
global.HttpRequest = {
request: request,
get: function (url, options) {
-56
View File
@@ -1,56 +0,0 @@
(() => {
const root = document.querySelector('[data-favorites]');
const manage = root?.querySelector('[data-favorites-manage]');
if (!manage) return;
manage.addEventListener('click', () => {
const active = document.body.classList.toggle('is-managing');
manage.setAttribute('aria-pressed', String(active));
manage.querySelector('span').textContent = active ? '完成' : '管理';
});
root.querySelectorAll('[data-favorites-remove]').forEach(button => {
button.addEventListener('click', async (event) => {
event.preventDefault();
event.stopPropagation();
if (button.disabled) return;
button.disabled = true;
const kind = button.dataset.kind || 'short';
const id = Number(button.dataset.id);
let url = '/api/playlet/collect/set';
let body = {id, isCollect: false};
if (kind === 'chapter') {
url = '/api/playlet/chapter/collect/set';
body = {chapterId: id, isCollect: false};
} else if (kind === 'series') {
url = '/api/video/collect/set';
body = {id, isCollect: false};
}
const trackerContext = kind === 'series' ? null : {
media_id: '',
drama_id: String(button.dataset.dramaId || ''),
drama_title: String(button.dataset.dramaTitle || ''),
episode: Math.max(1, Number(button.dataset.episode) || 1),
total_episode: Math.max(1, Number(button.dataset.totalEpisode) || 1),
video_id: String(button.dataset.videoId || ''),
drama_content_type: 'short_drama',
recommend_trace_id: '',
};
try {
if (!window.HttpRequest || typeof window.HttpRequest.post !== 'function') {
throw new Error('请求服务未就绪');
}
const result = await window.HttpRequest.post(
url,
body,
trackerContext ? {trackerContext} : undefined,
);
if (!result || Number(result.code) !== 200) throw new Error((result && (result.tip || result.msg)) || '取消收藏失败');
window.location.reload();
} catch (_) {
button.disabled = false;
window.alert('操作失败,请稍后重试');
}
});
});
})();
-111
View File
@@ -1,111 +0,0 @@
(() => {
const root = document.querySelector('[data-favorites]');
if (!root) return;
const cards = Array.from(root.querySelectorAll('[data-favorites-card]'));
const filters = Array.from(root.querySelectorAll('[data-favorites-filter]'));
const manage = root.querySelector('[data-favorites-manage]');
const manageLabel = manage ? manage.querySelector('span') : null;
const toolbar = root.querySelector('[data-favorites-toolbar]');
const grid = root.querySelector('[data-favorites-grid]');
const empty = root.querySelector('[data-favorites-empty]');
const filterEmpty = root.querySelector('[data-favorites-filter-empty]');
const recommendations = root.querySelector('[data-favorites-recommendations]');
const loadMore = root.querySelector('[data-favorites-more]');
const pagination = root.querySelector('[data-favorites-pagination]');
const pages = root.querySelector('[data-favorites-pages]');
const previous = root.querySelector('[data-favorites-prev]');
const next = root.querySelector('[data-favorites-next]');
const count = root.querySelector('[data-favorites-count]');
const mobile = window.matchMedia('(max-width: 768px)');
let activeFilter = 'all';
let currentPage = 1;
let mobileVisible = 9;
function matches(card) {
if (card.dataset.removed === 'true') return false;
if (activeFilter === 'following') return card.dataset.following === 'true';
if (activeFilter === 'complete') return card.dataset.complete === 'true';
return activeFilter === 'all' || card.dataset.kind === activeFilter;
}
function render() {
const hasSavedWorks = cards.some(card => card.dataset.removed !== 'true');
const collectionEmpty = !hasSavedWorks;
const filtered = cards.filter(matches);
const pageCount = Math.ceil(filtered.length / 12);
currentPage = Math.min(currentPage, Math.max(1, pageCount));
const start = mobile.matches ? 0 : (currentPage - 1) * 12;
const end = mobile.matches ? mobileVisible : start + 12;
cards.forEach(card => { card.hidden = true; });
filtered.slice(start, end).forEach(card => { card.hidden = false; });
root.classList.toggle('is-empty', collectionEmpty);
toolbar.hidden = collectionEmpty;
grid.hidden = collectionEmpty;
empty.hidden = !collectionEmpty;
if (recommendations) recommendations.hidden = !collectionEmpty;
filterEmpty.hidden = collectionEmpty || filtered.length !== 0;
if (collectionEmpty) {
document.body.classList.remove('is-managing');
if (manage) manage.setAttribute('aria-pressed', 'false');
if (manageLabel) manageLabel.textContent = '管理';
}
loadMore.hidden = filtered.length === 0 || mobileVisible >= filtered.length;
pagination.hidden = filtered.length === 0;
count.textContent = `共 ${filtered.length} 部`;
previous.disabled = currentPage <= 1;
next.disabled = currentPage >= pageCount;
pages.replaceChildren();
for (let page = 1; page <= pageCount; page++) {
const button = document.createElement('button');
button.className = `favorites-pagination__button${page === currentPage ? ' is-active' : ''}`;
button.type = 'button';
button.textContent = String(page);
button.dataset.favoritesPage = String(page);
if (page === currentPage) button.setAttribute('aria-current', 'page');
button.addEventListener('click', () => {
currentPage = page;
render();
});
pages.append(button);
}
}
filters.forEach(filter => filter.addEventListener('click', () => {
activeFilter = filter.dataset.favoritesFilter;
currentPage = 1;
mobileVisible = 9;
filters.forEach(option => {
const selected = option === filter;
option.classList.toggle('is-active', selected);
option.setAttribute('aria-pressed', String(selected));
});
render();
}));
if (manage) manage.addEventListener('click', () => {
const managing = document.body.classList.toggle('is-managing');
manage.setAttribute('aria-pressed', String(managing));
manageLabel.textContent = managing ? '完成' : '管理';
});
root.querySelectorAll('[data-favorites-remove]').forEach(button => button.addEventListener('click', () => {
button.closest('[data-favorites-card]').dataset.removed = 'true';
render();
}));
loadMore.addEventListener('click', () => {
mobileVisible += 9;
render();
});
previous.addEventListener('click', () => {
if (currentPage > 1) { currentPage--; render(); }
});
next.addEventListener('click', () => {
if (currentPage < Math.ceil(cards.filter(matches).length / 12)) { currentPage++; render(); }
});
mobile.addEventListener('change', render);
render();
})();
-172
View File
@@ -2,7 +2,6 @@
var header = document.querySelector(".site-header");
var drawer = document.querySelector("[data-drawer]");
var openBtn = document.querySelector("[data-drawer-open]");
var userMenu = document.querySelector("[data-user-menu]");
if (header) {
var threshold = 48;
@@ -30,44 +29,6 @@
if ("ResizeObserver" in window) new ResizeObserver(measure).observe(header);
}
// 用户下拉菜单
if (userMenu) {
var toggle = userMenu.querySelector("[data-user-menu-toggle]");
function openUserMenu() {
userMenu.classList.add("is-open");
if (toggle) toggle.setAttribute("aria-expanded", "true");
}
function closeUserMenu() {
userMenu.classList.remove("is-open");
if (toggle) toggle.setAttribute("aria-expanded", "false");
}
function toggleUserMenu(e) {
e.stopPropagation();
if (userMenu.classList.contains("is-open")) {
closeUserMenu();
} else {
openUserMenu();
}
}
if (toggle) {
toggle.addEventListener("click", toggleUserMenu);
}
document.addEventListener("click", function (e) {
if (!userMenu.contains(e.target)) {
closeUserMenu();
}
});
document.addEventListener("keydown", function (e) {
if (e.key === "Escape") closeUserMenu();
});
}
// 左侧抽屉
if (drawer && openBtn) {
var closeEls = drawer.querySelectorAll("[data-drawer-close]");
@@ -99,35 +60,6 @@
});
}
function openAccountSwitch(event) {
if (event) event.preventDefault();
if (userMenu) {
userMenu.classList.remove("is-open");
var userToggle = userMenu.querySelector("[data-user-menu-toggle]");
if (userToggle) userToggle.setAttribute("aria-expanded", "false");
}
if (drawer) {
drawer.classList.remove("is-open");
document.body.classList.remove("drawer-open");
if (openBtn) openBtn.setAttribute("aria-expanded", "false");
}
if (typeof window.openAccountSwitchLogin === "function") {
window.openAccountSwitchLogin();
return;
}
if (typeof window.openLoginPop === "function") {
window.openLoginPop();
return;
}
window.location.href = "/login";
}
document.querySelectorAll("[data-account-switch]").forEach(function (btn) {
btn.addEventListener("click", openAccountSwitch);
});
// 搜索表单:走路径 /search/{q},不用 query;同时写入本地搜索历史
var SEARCH_HISTORY_KEY = "hgdj_search_history";
var SEARCH_HISTORY_MAX = 10;
@@ -526,108 +458,4 @@
closeDropdown();
});
});
var PLACEHOLDER = "/static/images/placeholder.png?v=2";
var DEFAULT_AVATAR = "/static/images/profile/avatar.png";
function usableAvatar(url) {
var u = String(url || "").trim();
if (!u) return "";
if (/^https?:\/\//i.test(u) || u.indexOf("/static/") === 0) return u;
return "";
}
function setAvatarImg(img, url) {
if (!img) return;
var src = usableAvatar(url);
img.removeAttribute("data-decrypted");
img.removeAttribute("data-decrypt-failed");
if (src && typeof window.shouldDecryptImage === "function" && window.shouldDecryptImage(src)) {
img.classList.add("lazy");
img.src = PLACEHOLDER;
img.setAttribute("data-src", src);
img.setAttribute("data-lazy-eager", "1");
} else {
img.classList.remove("lazy");
img.removeAttribute("data-src");
img.removeAttribute("data-lazy-eager");
img.src = src || DEFAULT_AVATAR;
}
}
function refreshAvatarDecrypt(root) {
var scope = root || document;
if (typeof window.initLazyImages === "function") {
window.initLazyImages(scope, { eager: true });
return;
}
if (typeof window.loadLazyNode !== "function") return;
scope
.querySelectorAll(
"img.user-chip__avatar.lazy[data-src], img.user-menu__avatar.lazy[data-src], img.drawer__avatar.lazy[data-src], img.play-composer__avatar-img.lazy[data-src]"
)
.forEach(function (img) {
window.loadLazyNode(img);
});
}
function formatRecharge(raw) {
var n = Math.floor(Number(raw));
if (!isFinite(n) || n < 0) n = 0;
var s = String(n);
if (s.length <= 3) return s;
var out = [];
var rem = s.length % 3;
if (rem) out.push(s.slice(0, rem));
for (var i = rem; i < s.length; i += 3) out.push(s.slice(i, i + 3));
return out.join(",");
}
function syncHeaderUser() {
var session =
window.CookieUtil && typeof CookieUtil.getAuthSession === "function"
? CookieUtil.getAuthSession()
: null;
if (!session) return;
var avatar = usableAvatar(session.avatar);
document.querySelectorAll("[data-header-avatar]").forEach(function (img) {
if (avatar) setAvatarImg(img, avatar);
});
var name = String(session.nickName || session.accountNo || "").trim();
if (name) {
document.querySelectorAll(".user-chip__name, .user-menu__meta strong, .drawer__meta strong").forEach(function (el) {
el.textContent = name;
});
}
if (session.userId) {
document.querySelectorAll(".user-menu__meta span, .drawer__meta span").forEach(function (el) {
if (/^ID\b/.test(String(el.textContent || "").trim())) {
el.textContent = "ID " + session.userId;
}
});
}
if (Object.prototype.hasOwnProperty.call(session, "recharge") && session.recharge !== "") {
var text = formatRecharge(session.recharge);
document.querySelectorAll("[data-header-recharge]").forEach(function (el) {
el.textContent = text;
});
}
refreshAvatarDecrypt(document);
}
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", syncHeaderUser);
} else {
syncHeaderUser();
}
document.addEventListener("auth:login", syncHeaderUser);
document.addEventListener("auth:logout", syncHeaderUser);
window.refreshAvatarDecrypt = refreshAvatarDecrypt;
window.setAvatarImg = setAvatarImg;
window.syncHeaderUser = syncHeaderUser;
})();
-115
View File
@@ -1,115 +0,0 @@
(() => {
const root = document.querySelector('[data-history]');
if (!root) return;
const filters = Array.from(root.querySelectorAll('[data-history-filter]'));
const groups = Array.from(root.querySelectorAll('[data-history-group]'));
const items = Array.from(root.querySelectorAll('[data-history-item]'));
const empty = root.querySelector('[data-history-empty]');
const clear = root.querySelector('[data-history-clear]');
let activeKind = 'all';
async function postJSON(url, body) {
if (window.HttpRequest && typeof window.HttpRequest.post === 'function') {
return window.HttpRequest.post(url, body || {});
}
const response = await fetch(url, {
method: 'POST',
credentials: 'same-origin',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(body || {}),
});
return response.json();
}
function update() {
root.dataset.activeKind = activeKind;
items.forEach(item => {
item.hidden = item.dataset.removed === 'true' || (activeKind !== 'all' && item.dataset.kind !== activeKind);
});
groups.forEach(group => {
group.hidden = !Array.from(group.querySelectorAll('[data-history-item]')).some(item =>
!item.hidden && getComputedStyle(item).display !== 'none'
);
});
const hasVisible = groups.some(group => !group.hidden);
if (empty) empty.hidden = hasVisible;
if (clear) {
clear.hidden = items.length === 0 || items.every(item => item.dataset.removed === 'true');
clear.disabled = clear.hidden;
}
filters.forEach(filter => {
const selected = filter.dataset.historyFilter === activeKind;
filter.classList.toggle('is-active', selected);
filter.setAttribute('aria-pressed', String(selected));
});
}
filters.forEach(filter => filter.addEventListener('click', () => {
activeKind = filter.dataset.historyFilter;
update();
}));
root.querySelectorAll('[data-history-remove]').forEach(button => button.addEventListener('click', async (event) => {
event.preventDefault();
event.stopPropagation();
if (button.disabled) return;
const item = button.closest('[data-history-item]');
if (!item) return;
const id = Number(item.dataset.id);
const kind = item.dataset.kind || 'short';
if (!id) {
item.dataset.removed = 'true';
update();
return;
}
button.disabled = true;
try {
let result;
if (kind === 'series') {
result = await postJSON('/api/video/history/delete', {videoId: id});
} else {
result = await postJSON('/api/playlet/history/delete', {playletId: id});
}
if (!result || Number(result.code) !== 200) {
throw new Error((result && (result.tip || result.msg)) || '删除失败');
}
item.dataset.removed = 'true';
update();
} catch (error) {
window.alert(error.message || '删除失败,请稍后重试');
button.disabled = false;
}
}));
if (clear) {
clear.addEventListener('click', async () => {
if (clear.disabled) return;
if (!window.confirm('确定清空全部观看历史?')) return;
clear.disabled = true;
try {
const [playletResult, videoResult] = await Promise.all([
postJSON('/api/playlet/history/clear', {}),
postJSON('/api/video/history/clear', {}),
]);
const playletOk = playletResult && Number(playletResult.code) === 200;
const videoOk = videoResult && Number(videoResult.code) === 200;
if (!playletOk && !videoOk) {
throw new Error(
(videoResult && (videoResult.tip || videoResult.msg)) ||
(playletResult && (playletResult.tip || playletResult.msg)) ||
'清空失败'
);
}
items.forEach(item => { item.dataset.removed = 'true'; });
update();
window.location.reload();
} catch (error) {
window.alert(error.message || '清空失败,请稍后重试');
clear.disabled = false;
}
});
}
window.addEventListener('resize', update);
update();
})();
-21
View File
@@ -75,21 +75,6 @@
var displayedIndex = -1;
var loadGeneration = 0;
function emitTrack(item, eventType) {
if (!item || eventType === "close") return;
if (window.tracker && typeof window.tracker.externalAd === "function") {
window.tracker.externalAd(item, eventType);
return;
}
try {
document.dispatchEvent(new CustomEvent("external-ad:track", {
detail: { element: item, eventType: eventType },
}));
} catch (error) {
// 埋点不可用时不影响广告展示与关闭。
}
}
function hideAllItems() {
items.forEach(function (item) {
item.hidden = true;
@@ -118,7 +103,6 @@
var item = displayedIndex >= 0 ? items[displayedIndex] : null;
if (item && !item.__dialogAdShowTracked) {
item.__dialogAdShowTracked = true;
emitTrack(item, "show");
}
}
@@ -239,18 +223,13 @@
if (!item || item.hidden || !mask.contains(item)) return;
var index = items.indexOf(item);
if (index < 0 || index !== displayedIndex) return;
if (event.target.closest("[data-dialog-ad-link]")) {
emitTrack(item, "click");
}
if (event.target.closest("[data-dialog-ad-close]")) {
emitTrack(item, "close");
showItem(index + 1);
}
});
document.addEventListener("keydown", function (event) {
if (event.key === "Escape" && mask.classList.contains("is-open") && displayedIndex >= 0) {
emitTrack(items[displayedIndex], "close");
showNextItem();
}
});
-4
View File
@@ -37,10 +37,6 @@
window.showToast(message, type);
return;
}
if (window.AuthAPI && typeof window.AuthAPI.toast === "function") {
window.AuthAPI.toast(message, type || "info");
return;
}
window.alert(message);
}
-137
View File
@@ -1,137 +0,0 @@
(function () {
var form = document.querySelector("[data-login-page-form]");
var password = document.querySelector("[data-login-page-password]");
var toggle = document.querySelector("[data-login-page-toggle]");
var toggleIcon = document.querySelector("[data-login-page-toggle-icon]");
var submitBtn = document.querySelector("[data-login-page-submit]") || (form && form.querySelector('button[type="submit"]'));
var root = form || document;
if (toggle && password && toggleIcon) {
toggle.addEventListener("click", function () {
var show = password.getAttribute("type") === "password";
password.setAttribute("type", show ? "text" : "password");
toggleIcon.setAttribute("src", show ? "/static/images/eye-off.svg" : "/static/images/eye.svg");
toggle.setAttribute("aria-label", show ? "隐藏密码" : "显示密码");
});
}
function clearFieldError(name) {
var field = root.querySelector('[data-field="' + name + '"]');
var err = root.querySelector('[data-field-error="' + name + '"]');
if (field) field.classList.remove("is-error");
if (err) {
err.hidden = true;
var text = err.querySelector("[data-field-error-text]");
if (text) text.textContent = "";
}
}
function clearAllErrors() {
root.querySelectorAll("[data-field]").forEach(function (el) {
el.classList.remove("is-error");
});
root.querySelectorAll("[data-field-error]").forEach(function (err) {
err.hidden = true;
var text = err.querySelector("[data-field-error-text]");
if (text) text.textContent = "";
});
}
function setFieldError(name, message) {
var field = root.querySelector('[data-field="' + name + '"]');
var err = root.querySelector('[data-field-error="' + name + '"]');
if (field) field.classList.add("is-error");
if (err) {
err.hidden = false;
var text = err.querySelector("[data-field-error-text]");
if (text) text.textContent = message || "";
}
}
function isLen4to16(value) {
var len = String(value || "").length;
return len >= 4 && len <= 16;
}
function validateForm() {
clearAllErrors();
var fd = new FormData(form);
var account = String(fd.get("account") || "").trim();
var pwd = String(fd.get("password") || "");
var valid = true;
if (!account) {
setFieldError("account", "请输入账号");
valid = false;
} else if (
!(window.AuthAPI && AuthAPI.isValidLoginAccount
? AuthAPI.isValidLoginAccount(account)
: isLen4to16(account) || account.indexOf("@") >= 0)
) {
setFieldError("account", "账号需为 4–16 位,或合法邮箱");
valid = false;
}
if (!pwd) {
setFieldError("password", "请输入密码");
valid = false;
} else if (!isLen4to16(pwd)) {
setFieldError("password", "密码需为 4–16 位");
valid = false;
}
if (!valid) return null;
return { account: account, password: pwd };
}
function mapServerError(err) {
var tip = (err && err.message) || "登录失败,请稍后重试";
if (/账号|账户|用户|不存在|邮箱/.test(tip) && !/密码/.test(tip)) {
setFieldError("account", tip);
return;
}
setFieldError("password", tip);
}
function setSubmitting(loading) {
if (!submitBtn) return;
submitBtn.disabled = !!loading;
submitBtn.classList.toggle("is-loading", !!loading);
submitBtn.textContent = loading ? "登录中…" : "登录";
}
if (form) {
form.querySelectorAll("input").forEach(function (input) {
input.addEventListener("input", function () {
var name = input.getAttribute("name");
if (name) clearFieldError(name);
});
});
form.addEventListener("submit", function (event) {
event.preventDefault();
if (!window.AuthAPI || !window.HttpRequest) {
setFieldError("password", "认证脚本未加载,请刷新页面");
return;
}
if (submitBtn && submitBtn.classList.contains("is-loading")) return;
var payload = validateForm();
if (!payload) return;
setSubmitting(true);
AuthAPI.login(payload)
.then(function (res) {
AuthAPI.toast((res && res.tip) || "登录成功", "success");
window.setTimeout(function () {
window.location.href = "/";
}, 400);
})
.catch(function (err) {
setSubmitting(false);
mapServerError(err);
AuthAPI.toast((err && err.message) || "登录失败", "error");
});
});
}
})();
-252
View File
@@ -1,252 +0,0 @@
(function () {
var root = document.querySelector("[data-login-pop]");
if (!root) return;
var form = root.querySelector("[data-login-form]");
var password = root.querySelector("[data-login-password]");
var toggle = root.querySelector("[data-login-toggle]");
var toggleIcon = root.querySelector("[data-login-toggle-icon]");
var title = root.querySelector("[data-login-title]");
var description = root.querySelector("[data-login-desc]");
var submitBtn = root.querySelector("[data-login-submit]") || (form && form.querySelector('button[type="submit"]'));
var previousOverflow = "";
var switchAccountMode = false;
function closeRegister() {
if (typeof window.closeRegisterPop === "function") {
window.closeRegisterPop();
return;
}
var register = document.querySelector("[data-register-pop]");
if (register) register.classList.remove("is-open");
}
function closeUserMenu() {
var menu = document.querySelector("[data-user-menu].is-open");
if (menu) {
menu.classList.remove("is-open");
var t = menu.querySelector("[data-user-menu-toggle]");
if (t) t.setAttribute("aria-expanded", "false");
}
}
function clearFieldError(name) {
var field = root.querySelector('[data-field="' + name + '"]');
var err = root.querySelector('[data-field-error="' + name + '"]');
if (field) field.classList.remove("is-error");
if (err) {
err.hidden = true;
var text = err.querySelector("[data-field-error-text]");
if (text) text.textContent = "";
}
}
function clearAllErrors() {
root.querySelectorAll("[data-field]").forEach(function (el) {
el.classList.remove("is-error");
});
root.querySelectorAll("[data-field-error]").forEach(function (err) {
err.hidden = true;
var text = err.querySelector("[data-field-error-text]");
if (text) text.textContent = "";
});
}
function setFieldError(name, message) {
var field = root.querySelector('[data-field="' + name + '"]');
var err = root.querySelector('[data-field-error="' + name + '"]');
if (field) field.classList.add("is-error");
if (err) {
err.hidden = false;
var text = err.querySelector("[data-field-error-text]");
if (text) text.textContent = message || "";
}
}
function isLen4to16(value) {
var len = String(value || "").length;
return len >= 4 && len <= 16;
}
function validateForm() {
clearAllErrors();
var fd = new FormData(form);
var account = String(fd.get("account") || "").trim();
var pwd = String(fd.get("password") || "");
var valid = true;
if (!account) {
setFieldError("account", "请输入账号");
valid = false;
} else if (
!(window.AuthAPI && AuthAPI.isValidLoginAccount
? AuthAPI.isValidLoginAccount(account)
: isLen4to16(account) || account.indexOf("@") >= 0)
) {
setFieldError("account", "账号需为 4–16 位,或合法邮箱");
valid = false;
}
if (!pwd) {
setFieldError("password", "请输入密码");
valid = false;
} else if (!isLen4to16(pwd)) {
setFieldError("password", "密码需为 4–16 位");
valid = false;
}
if (!valid) return null;
return { account: account, password: pwd };
}
function mapServerError(err) {
var tip = (err && err.message) || "登录失败,请稍后重试";
if (/账号|账户|用户|不存在|邮箱/.test(tip) && !/密码/.test(tip)) {
setFieldError("account", tip);
return;
}
setFieldError("password", tip);
}
function isMobile() {
return window.matchMedia && window.matchMedia("(max-width: 768px)").matches;
}
function open(input) {
var event = input && typeof input.preventDefault === "function" ? input : null;
var options = event ? {} : input || {};
if (isMobile() && !options.forcePopup) {
if (event) event.preventDefault();
window.location.href = "/login";
return;
}
if (event) event.preventDefault();
switchAccountMode = !!options.switchAccount;
closeRegister();
closeUserMenu();
clearAllErrors();
if (options.resetForm && form) form.reset();
if (password) password.setAttribute("type", "password");
if (toggle) toggle.setAttribute("aria-label", "显示密码");
if (toggleIcon) {
toggleIcon.setAttribute("src", "/static/images/eye.svg");
toggleIcon.setAttribute("data-src", "/static/images/eye.svg");
}
if (title) title.textContent = switchAccountMode ? "切换账号" : "登录黄果短剧";
if (description) {
description.textContent = switchAccountMode
? "请输入其他账号和密码完成切换"
: "登录后可收藏作品、解锁付费剧集并查看观看历史";
}
root.classList.add("is-open");
previousOverflow = document.body.style.overflow;
document.body.style.overflow = "hidden";
var first = root.querySelector(".login-pop__input");
if (first) {
window.setTimeout(function () {
first.focus();
}, 0);
}
}
function close() {
root.classList.remove("is-open");
document.body.style.overflow = previousOverflow;
clearAllErrors();
switchAccountMode = false;
}
window.openLoginPop = open;
window.openAccountSwitchLogin = function () {
open({ forcePopup: true, switchAccount: true, resetForm: true });
};
window.closeLoginPop = close;
document.addEventListener("click", function (event) {
var opener = event.target.closest("[data-login-open]");
if (opener) {
open(event);
return;
}
// PC:拦截站内 /login 链接,改为打开弹窗;H5 放行跳转登录页
var loginLink = event.target.closest('a[href="/login"], a[href="/login/"], a[href="#login"]');
if (loginLink && !isMobile()) {
open(event);
return;
}
if (!root.classList.contains("is-open")) return;
if (event.target.closest("[data-login-close]")) {
close();
}
});
if (toggle && password && toggleIcon) {
toggle.addEventListener("click", function () {
var show = password.getAttribute("type") === "password";
password.setAttribute("type", show ? "text" : "password");
toggleIcon.setAttribute("src", show ? "/static/images/eye-off.svg" : "/static/images/eye.svg");
toggle.setAttribute("aria-label", show ? "隐藏密码" : "显示密码");
});
}
function setSubmitting(loading) {
if (!submitBtn) return;
submitBtn.disabled = !!loading;
submitBtn.classList.toggle("is-loading", !!loading);
submitBtn.textContent = loading ? "登录中…" : "登录";
}
if (form) {
form.querySelectorAll("input").forEach(function (input) {
input.addEventListener("input", function () {
var name = input.getAttribute("name");
if (name) clearFieldError(name);
});
});
form.addEventListener("submit", function (event) {
event.preventDefault();
if (!window.AuthAPI || !window.HttpRequest) {
setFieldError("password", "认证脚本未加载,请刷新页面");
return;
}
if (submitBtn && submitBtn.classList.contains("is-loading")) return;
var payload = validateForm();
if (!payload) return;
setSubmitting(true);
AuthAPI.login(payload)
.then(function (res) {
AuthAPI.toast((res && res.tip) || "登录成功", "success");
window.setTimeout(function () {
close();
window.location.reload();
}, 400);
})
.catch(function (err) {
setSubmitting(false);
mapServerError(err);
AuthAPI.toast((err && err.message) || "登录失败", "error");
});
});
}
document.addEventListener("keydown", function (event) {
if (event.key === "Escape" && root.classList.contains("is-open")) {
close();
}
});
try {
var params = new URLSearchParams(window.location.search || "");
if (params.get("auth") === "login") {
params.delete("auth");
var qs = params.toString();
window.history.replaceState(null, "", window.location.pathname + (qs ? "?" + qs : "") + window.location.hash);
open();
}
} catch (e) {}
})();
+1 -782
View File
@@ -452,7 +452,6 @@
}
var playDp = null;
var lastWatchReportKey = "";
var currentPlayMode = "main";
var mainBadgePC = "";
var mainBadgeH5 = "";
@@ -515,32 +514,6 @@
if (label) label.textContent = mainCtaLabel || "立即播放";
}
function toIntAttr(v, fallback) {
var n = parseInt(v, 10);
return isNaN(n) ? fallback || 0 : n;
}
function reportChapterWatch() {
var root = document.querySelector("[data-play-player]");
if (!root) return;
var chapterId = toIntAttr(root.getAttribute("data-chapter-id"), 0);
var playletId = toIntAttr(root.getAttribute("data-playlet-id"), 0);
if (chapterId <= 0) return;
if (window.CookieUtil && typeof window.CookieUtil.isLoggedIn === "function") {
if (!window.CookieUtil.isLoggedIn()) return;
}
if (!window.HttpRequest || typeof window.HttpRequest.post !== "function") return;
var key = String(playletId || 0) + ":" + chapterId;
if (key === lastWatchReportKey) return;
lastWatchReportKey = key;
var body = { chapterId: chapterId };
if (playletId > 0) body.playletId = playletId;
window.HttpRequest.post("/api/playlet/chapter/watch/report", body)
.catch(function () {
lastWatchReportKey = "";
});
}
function hidePosterOverlay() {
var root = document.querySelector("[data-play-player]");
if (root) root.classList.add("is-playing");
@@ -571,20 +544,6 @@
}
}
function ensureComposerAvatarDecrypt() {
var avatar = document.querySelector(".play-composer__avatar-img.lazy[data-src]");
if (!avatar) return;
if (typeof window.loadLazyNode === "function") {
window.loadLazyNode(avatar);
return;
}
if (typeof window.refreshAvatarDecrypt === "function") {
window.refreshAvatarDecrypt(avatar.parentNode || document);
} else if (typeof window.initLazyImages === "function") {
window.initLazyImages(avatar.parentNode || document, { eager: true });
}
}
function initDPlayer(initialURL) {
if (typeof window.DPlayer === "undefined") return null;
var container = document.querySelector(".dplayer-video");
@@ -624,9 +583,6 @@
playDp.on("play", function () {
patchVideoInlinePlayback(playDp);
hidePosterOverlay();
if (currentPlayMode === "main") {
reportChapterWatch();
}
});
}
return playDp;
@@ -669,9 +625,6 @@
var root = playerRoot();
if (!root) return;
var next = mode === "preview" ? "preview" : "main";
if (window.tracker && typeof window.tracker.prepareDramaModeChange === "function") {
window.tracker.prepareDramaModeChange(next);
}
currentPlayMode = next;
root.setAttribute("data-play-mode", next);
root.classList.toggle("play-player--preview", next === "preview");
@@ -782,7 +735,6 @@
}
function bootPlayer() {
ensureComposerAvatarDecrypt();
var root = playerRoot();
if (!root) return;
@@ -888,7 +840,7 @@
Array.prototype.forEach.call(openers, function (btn) {
btn.addEventListener("click", function () {
// 预告片模式不弹充值;属性被临时移除时也不弹。
// 预告片模式不弹下载提示;属性被临时移除时也不弹。
if (!btn.hasAttribute("data-play-coin-open")) return;
if (isPreviewPlayingSurface()) return;
open();
@@ -906,123 +858,6 @@
});
})();
// 金币解锁:POST /api/playlet/chapter/unlock → upstream /api/app/playlet/chapter/unlock
(function () {
function toast(message, type) {
if (typeof window.showToast === "function") {
window.showToast(message, type);
return;
}
}
function toInt(v, fallback) {
var n = parseInt(v, 10);
return isNaN(n) ? fallback || 0 : n;
}
function isLoggedIn() {
var composer = document.querySelector("[data-play-composer]");
if (composer && composer.getAttribute("data-logged-in") === "1") return true;
if (window.CookieUtil && typeof window.CookieUtil.isLoggedIn === "function") {
return !!window.CookieUtil.isLoggedIn();
}
return false;
}
function openRecharge() {
var opener = document.querySelector("[data-play-coin-open]");
if (opener) {
opener.click();
return;
}
if (typeof window.openRechargeModal === "function") {
window.openRechargeModal();
return;
}
window.location.href = "/recharge";
}
function reloadAfterUnlockTracking() {
var tracking = Promise.resolve();
if (window.tracker && typeof window.tracker.dramaUnlock === "function") {
tracking = Promise.resolve(window.tracker.dramaUnlock()).catch(function () {
// 埋点失败不影响解锁结果。
});
if (typeof window.tracker.flush === "function") {
[50, 200, 500, 700].forEach(function (delay) {
window.setTimeout(function () {
Promise.resolve(window.tracker.flush()).catch(function () {
// 埋点失败不影响解锁结果。
});
}, delay);
});
}
}
var deadline = new Promise(function (resolve) {
window.setTimeout(resolve, 800);
});
return Promise.race([tracking, deadline]).then(function () {
window.location.reload();
});
}
document.addEventListener("click", function (event) {
var btn = window.domClosest(event.target, "[data-play-unlock]");
if (!btn) return;
event.preventDefault();
if (btn.disabled || btn.getAttribute("data-unlocking") === "1") return;
if (!isLoggedIn()) {
var loginBtn = document.querySelector("[data-login-open]");
if (loginBtn) loginBtn.click();
return;
}
if (!window.HttpRequest || typeof window.HttpRequest.post !== "function") {
toast("解锁服务未就绪", "error");
return;
}
var chapterId = toInt(btn.getAttribute("data-chapter-id"), 0);
var playletId = toInt(btn.getAttribute("data-playlet-id"), 0);
if (chapterId <= 0 || playletId <= 0) {
toast("缺少剧集信息", "error");
return;
}
var payload = { chapterId: chapterId, playletId: playletId };
btn.setAttribute("data-unlocking", "1");
btn.disabled = true;
window.HttpRequest.post("/api/playlet/chapter/unlock", payload)
.then(function (res) {
if (!res || Number(res.code) !== 200) {
var code = res && Number(res.code);
if (code === 401 || code === 1000) {
var login = document.querySelector("[data-login-open]");
if (login) login.click();
return;
}
// 常见余额不足码,转充值
if (code === 400 || /金币|余额|不足/.test(String((res && (res.tip || res.msg)) || ""))) {
openRecharge();
return;
}
throw new Error((res && (res.tip || res.msg)) || "解锁失败");
}
var data = res.data || {};
if (data.balance != null && window.CookieUtil && typeof window.CookieUtil.setAuthSession === "function") {
window.CookieUtil.setAuthSession({ recharge: data.balance });
if (typeof window.syncHeaderUser === "function") window.syncHeaderUser();
}
toast("购买成功", "success");
return reloadAfterUnlockTracking();
})
.catch(function (err) {
toast((err && err.message) || "解锁失败,请稍后重试", "error");
})
.finally(function () {
btn.removeAttribute("data-unlocking");
btn.disabled = false;
});
});
})();
(function () {
var btn = document.querySelector("[data-guess-refresh]");
var grid = document.querySelector("[data-guess-grid]");
@@ -1350,622 +1185,6 @@
});
})();
// 剧集点赞:POST /api/playlet/chapter/like/set → upstream /api/app/playlet/chapter/like/set
(function () {
function toast(message, type) {
if (typeof window.showToast === "function") {
window.showToast(message, type);
return;
}
}
function toInt(v, fallback) {
var n = parseInt(v, 10);
return isNaN(n) ? fallback || 0 : n;
}
function formatLikeCount(n) {
var v = Number(n) || 0;
if (v <= 0) return "0";
if (v < 10000) return String(v);
var wan = v / 10000;
if (wan >= 100) return Math.round(wan) + " 万";
return wan.toFixed(1) + " 万";
}
function isLoggedIn() {
var composer = document.querySelector("[data-play-composer]");
if (composer && composer.getAttribute("data-logged-in") === "1") return true;
if (window.CookieUtil && typeof window.CookieUtil.isLoggedIn === "function") {
return !!window.CookieUtil.isLoggedIn();
}
return false;
}
function applyChapterLikeUI(btn, isLike, countLike) {
if (!btn) return;
var liked = Number(isLike) === 1;
btn.classList.toggle("is-active", liked);
btn.setAttribute("data-liked", liked ? "1" : "2");
btn.setAttribute("aria-label", liked ? "取消点赞" : "点赞");
var label = btn.querySelector("[data-chapter-like-label]");
if (label) label.textContent = liked ? "已点赞" : "点赞";
var countEl = btn.querySelector("[data-chapter-like-count]");
if (countLike != null) {
btn.setAttribute("data-like-raw", String(countLike));
if (countEl) countEl.textContent = formatLikeCount(countLike);
}
if (countEl) countEl.hidden = liked;
}
function toggleChapterLike(btn) {
if (!btn || btn.disabled || btn.getAttribute("data-liking") === "1") return;
if (!isLoggedIn()) {
var loginBtn = document.querySelector("[data-login-open]");
if (loginBtn) loginBtn.click();
return;
}
if (!window.HttpRequest || typeof window.HttpRequest.post !== "function") {
toast("点赞服务未就绪", "error");
return;
}
var chapterId = toInt(btn.getAttribute("data-chapter-id"), 0);
if (chapterId <= 0) {
toast("缺少剧集 ID", "error");
return;
}
var likedNow = Number(btn.getAttribute("data-liked") || 2) === 1;
var nextLike = !likedNow;
var prevCount = toInt(btn.getAttribute("data-like-raw"), 0);
var nextCount = Math.max(0, prevCount + (nextLike ? 1 : -1));
btn.setAttribute("data-liking", "1");
btn.disabled = true;
applyChapterLikeUI(btn, nextLike ? 1 : 2, nextCount);
var payload = { chapterId: chapterId, isLike: nextLike };
window.HttpRequest.post("/api/playlet/chapter/like/set", payload)
.then(function (res) {
if (!res || Number(res.code) !== 200) {
throw new Error((res && (res.tip || res.msg)) || "操作失败");
}
var data = res.data || {};
applyChapterLikeUI(
btn,
data.isLike != null ? data.isLike : nextLike ? 1 : 2,
data.countLike != null ? data.countLike : nextCount
);
})
.catch(function (err) {
applyChapterLikeUI(btn, likedNow ? 1 : 2, prevCount);
toast((err && err.message) || "操作失败,请稍后重试", "error");
})
.finally(function () {
btn.removeAttribute("data-liking");
btn.disabled = false;
});
}
document.addEventListener("click", function (event) {
var btn = window.domClosest(event.target, "[data-chapter-like]");
if (!btn) return;
event.preventDefault();
toggleChapterLike(btn);
});
})();
// 短剧点赞:POST /api/playlet/like/set → upstream /api/app/playlet/like/set
(function () {
function toast(message, type) {
if (typeof window.showToast === "function") {
window.showToast(message, type);
return;
}
}
function toInt(v, fallback) {
var n = parseInt(v, 10);
return isNaN(n) ? fallback || 0 : n;
}
function formatLikeCount(n) {
var v = Number(n) || 0;
if (v <= 0) return "0";
if (v < 10000) return String(v);
var wan = v / 10000;
if (wan >= 100) return Math.round(wan) + " 万";
return wan.toFixed(1) + " 万";
}
function isLoggedIn() {
var composer = document.querySelector("[data-play-composer]");
if (composer && composer.getAttribute("data-logged-in") === "1") return true;
if (window.CookieUtil && typeof window.CookieUtil.isLoggedIn === "function") {
return !!window.CookieUtil.isLoggedIn();
}
return false;
}
function applyPlayletLikeUI(btn, isLike, countLike) {
if (!btn) return;
var liked = Number(isLike) === 1;
btn.classList.toggle("is-active", liked);
btn.setAttribute("data-liked", liked ? "1" : "2");
btn.setAttribute("aria-label", liked ? "取消点赞" : "点赞");
var label = btn.querySelector("[data-playlet-like-label]");
if (label) label.textContent = liked ? "已点赞" : "点赞";
var countEl = btn.querySelector("[data-playlet-like-count]");
if (countLike != null) {
btn.setAttribute("data-like-raw", String(countLike));
if (countEl) countEl.textContent = formatLikeCount(countLike);
}
if (countEl) countEl.hidden = liked;
}
function togglePlayletLike(btn) {
if (!btn || btn.disabled || btn.getAttribute("data-liking") === "1") return;
if (!isLoggedIn()) {
var loginBtn = document.querySelector("[data-login-open]");
if (loginBtn) loginBtn.click();
return;
}
if (!window.HttpRequest || typeof window.HttpRequest.post !== "function") {
toast("点赞服务未就绪", "error");
return;
}
var playletId = toInt(btn.getAttribute("data-playlet-id"), 0);
if (playletId <= 0) {
toast("缺少短剧 ID", "error");
return;
}
var likedNow = Number(btn.getAttribute("data-liked") || 2) === 1;
var nextLike = !likedNow;
var prevCount = toInt(btn.getAttribute("data-like-raw"), 0);
var nextCount = Math.max(0, prevCount + (nextLike ? 1 : -1));
btn.setAttribute("data-liking", "1");
btn.disabled = true;
applyPlayletLikeUI(btn, nextLike ? 1 : 2, nextCount);
var payload = { id: playletId, isLike: nextLike };
window.HttpRequest.post("/api/playlet/like/set", payload)
.then(function (res) {
if (!res || Number(res.code) !== 200) {
throw new Error((res && (res.tip || res.msg)) || "操作失败");
}
var data = res.data || {};
applyPlayletLikeUI(
btn,
data.isLike != null ? data.isLike : nextLike ? 1 : 2,
data.countLike != null ? data.countLike : nextCount
);
})
.catch(function (err) {
applyPlayletLikeUI(btn, likedNow ? 1 : 2, prevCount);
toast((err && err.message) || "操作失败,请稍后重试", "error");
})
.finally(function () {
btn.removeAttribute("data-liking");
btn.disabled = false;
});
}
document.addEventListener("click", function (event) {
var btn = window.domClosest(event.target, "[data-playlet-like]");
if (!btn) return;
event.preventDefault();
togglePlayletLike(btn);
});
})();
// 视频点赞:POST /api/video/like/set → upstream /api/app/video/like/set
(function () {
function toast(message, type) {
if (typeof window.showToast === "function") {
window.showToast(message, type);
return;
}
}
function toInt(v, fallback) {
var n = parseInt(v, 10);
return isNaN(n) ? fallback || 0 : n;
}
function formatLikeCount(n) {
var v = Number(n) || 0;
if (v <= 0) return "0";
if (v < 10000) return String(v);
var wan = v / 10000;
if (wan >= 100) return Math.round(wan) + " 万";
return wan.toFixed(1) + " 万";
}
function isLoggedIn() {
var composer = document.querySelector("[data-play-composer]");
if (composer && composer.getAttribute("data-logged-in") === "1") return true;
if (window.CookieUtil && typeof window.CookieUtil.isLoggedIn === "function") {
return !!window.CookieUtil.isLoggedIn();
}
return false;
}
function applyVideoLikeUI(btn, isLike, countLike) {
if (!btn) return;
var liked = Number(isLike) === 1;
btn.classList.toggle("is-active", liked);
btn.setAttribute("data-liked", liked ? "1" : "2");
btn.setAttribute("aria-label", liked ? "取消点赞" : "点赞");
var label = btn.querySelector("[data-video-like-label]");
if (label) label.textContent = liked ? "已点赞" : "点赞";
var countEl = btn.querySelector("[data-video-like-count]");
if (countLike != null) {
btn.setAttribute("data-like-raw", String(countLike));
if (countEl) countEl.textContent = formatLikeCount(countLike);
}
if (countEl) countEl.hidden = liked;
}
function toggleVideoLike(btn) {
if (!btn || btn.disabled || btn.getAttribute("data-liking") === "1") return;
if (!isLoggedIn()) {
var loginBtn = document.querySelector("[data-login-open]");
if (loginBtn) loginBtn.click();
return;
}
if (!window.HttpRequest || typeof window.HttpRequest.post !== "function") {
toast("点赞服务未就绪", "error");
return;
}
var videoId = toInt(btn.getAttribute("data-video-id"), 0);
if (videoId <= 0) {
toast("缺少视频 ID", "error");
return;
}
var likedNow = Number(btn.getAttribute("data-liked") || 2) === 1;
var nextLike = !likedNow;
var prevCount = toInt(btn.getAttribute("data-like-raw"), 0);
var nextCount = Math.max(0, prevCount + (nextLike ? 1 : -1));
btn.setAttribute("data-liking", "1");
btn.disabled = true;
applyVideoLikeUI(btn, nextLike ? 1 : 2, nextCount);
var payload = { id: videoId, isLike: nextLike };
window.HttpRequest.post("/api/video/like/set", payload)
.then(function (res) {
if (!res || Number(res.code) !== 200) {
throw new Error((res && (res.tip || res.msg)) || "操作失败");
}
var data = res.data || {};
applyVideoLikeUI(
btn,
data.isLike != null ? data.isLike : nextLike ? 1 : 2,
data.countLike != null ? data.countLike : nextCount
);
})
.catch(function (err) {
applyVideoLikeUI(btn, likedNow ? 1 : 2, prevCount);
toast((err && err.message) || "操作失败,请稍后重试", "error");
})
.finally(function () {
btn.removeAttribute("data-liking");
btn.disabled = false;
});
}
document.addEventListener("click", function (event) {
var btn = window.domClosest(event.target, "[data-video-like]");
if (!btn) return;
event.preventDefault();
toggleVideoLike(btn);
});
})();
// 视频收藏:POST /api/video/collect/set → upstream /api/app/video/collect/set
(function () {
function toast(message, type) {
if (typeof window.showToast === "function") {
window.showToast(message, type);
return;
}
}
function toInt(v, fallback) {
var n = parseInt(v, 10);
return isNaN(n) ? fallback || 0 : n;
}
function formatCount(n) {
var v = Number(n) || 0;
if (v <= 0) return "0";
if (v < 10000) return String(v);
var wan = v / 10000;
if (wan >= 100) return Math.round(wan) + " 万";
return wan.toFixed(1) + " 万";
}
function isLoggedIn() {
var composer = document.querySelector("[data-play-composer]");
if (composer && composer.getAttribute("data-logged-in") === "1") return true;
if (window.CookieUtil && typeof window.CookieUtil.isLoggedIn === "function") {
return !!window.CookieUtil.isLoggedIn();
}
return false;
}
function applyVideoCollectUI(btn, isCollect, countCollect) {
if (!btn) return;
var collected = Number(isCollect) === 1;
btn.classList.toggle("is-active", collected);
btn.setAttribute("data-collected", collected ? "1" : "2");
btn.setAttribute("aria-label", collected ? "取消收藏" : "收藏");
var label = btn.querySelector("[data-video-collect-label]");
if (label) label.textContent = collected ? "已收藏" : "收藏";
var countEl = btn.querySelector("[data-video-collect-count]");
if (countCollect != null) {
btn.setAttribute("data-collect-raw", String(countCollect));
if (countEl) countEl.textContent = formatCount(countCollect);
}
if (countEl) countEl.hidden = collected;
}
function toggleVideoCollect(btn) {
if (!btn || btn.disabled || btn.getAttribute("data-collecting") === "1") return;
if (!isLoggedIn()) {
var loginBtn = document.querySelector("[data-login-open]");
if (loginBtn) loginBtn.click();
return;
}
if (!window.HttpRequest || typeof window.HttpRequest.post !== "function") {
toast("收藏服务未就绪", "error");
return;
}
var videoId = toInt(btn.getAttribute("data-video-id"), 0);
if (videoId <= 0) {
toast("缺少视频 ID", "error");
return;
}
var collectedNow = Number(btn.getAttribute("data-collected") || 2) === 1;
var nextCollect = !collectedNow;
var prevCount = toInt(btn.getAttribute("data-collect-raw"), 0);
var nextCount = Math.max(0, prevCount + (nextCollect ? 1 : -1));
btn.setAttribute("data-collecting", "1");
btn.disabled = true;
applyVideoCollectUI(btn, nextCollect ? 1 : 2, nextCount);
var payload = { id: videoId, isCollect: nextCollect };
window.HttpRequest.post("/api/video/collect/set", payload)
.then(function (res) {
if (!res || Number(res.code) !== 200) {
throw new Error((res && (res.tip || res.msg)) || "操作失败");
}
var data = res.data || {};
applyVideoCollectUI(
btn,
data.isCollect != null ? data.isCollect : nextCollect ? 1 : 2,
data.countCollect != null ? data.countCollect : nextCount
);
})
.catch(function (err) {
applyVideoCollectUI(btn, collectedNow ? 1 : 2, prevCount);
toast((err && err.message) || "操作失败,请稍后重试", "error");
})
.finally(function () {
btn.removeAttribute("data-collecting");
btn.disabled = false;
});
}
document.addEventListener("click", function (event) {
var btn = window.domClosest(event.target, "[data-video-collect]");
if (!btn) return;
event.preventDefault();
toggleVideoCollect(btn);
});
})();
// 剧集收藏:POST /api/playlet/chapter/collect/set → upstream /api/app/playlet/chapter/collect/set
(function () {
function toast(message, type) {
if (typeof window.showToast === "function") {
window.showToast(message, type);
return;
}
}
function toInt(v, fallback) {
var n = parseInt(v, 10);
return isNaN(n) ? fallback || 0 : n;
}
function formatLikeCount(n) {
var v = Number(n) || 0;
if (v <= 0) return "0";
if (v < 10000) return String(v);
var wan = v / 10000;
if (wan >= 100) return Math.round(wan) + " 万";
return wan.toFixed(1) + " 万";
}
function isLoggedIn() {
var composer = document.querySelector("[data-play-composer]");
if (composer && composer.getAttribute("data-logged-in") === "1") return true;
if (window.CookieUtil && typeof window.CookieUtil.isLoggedIn === "function") {
return !!window.CookieUtil.isLoggedIn();
}
return false;
}
function applyChapterCollectUI(btn, isCollect, countCollect) {
if (!btn) return;
var collected = Number(isCollect) === 1;
btn.classList.toggle("is-active", collected);
btn.setAttribute("data-collected", collected ? "1" : "2");
btn.setAttribute("aria-label", collected ? "取消收藏" : "收藏");
var label = btn.querySelector("[data-chapter-collect-label]");
if (label) label.textContent = collected ? "已收藏" : "收藏";
var countEl = btn.querySelector("[data-chapter-collect-count]");
if (countCollect != null) {
btn.setAttribute("data-collect-raw", String(countCollect));
if (countEl) countEl.textContent = formatLikeCount(countCollect);
}
if (countEl) countEl.hidden = collected;
}
function toggleChapterCollect(btn) {
if (!btn || btn.disabled || btn.getAttribute("data-collecting") === "1") return;
if (!isLoggedIn()) {
var loginBtn = document.querySelector("[data-login-open]");
if (loginBtn) loginBtn.click();
return;
}
if (!window.HttpRequest || typeof window.HttpRequest.post !== "function") {
toast("收藏服务未就绪", "error");
return;
}
var chapterId = toInt(btn.getAttribute("data-chapter-id"), 0);
if (chapterId <= 0) {
toast("缺少剧集 ID", "error");
return;
}
var collectedNow = Number(btn.getAttribute("data-collected") || 2) === 1;
var nextCollect = !collectedNow;
var prevCount = toInt(btn.getAttribute("data-collect-raw"), 0);
var nextCount = Math.max(0, prevCount + (nextCollect ? 1 : -1));
btn.setAttribute("data-collecting", "1");
btn.disabled = true;
applyChapterCollectUI(btn, nextCollect ? 1 : 2, nextCount);
var payload = { chapterId: chapterId, isCollect: nextCollect };
window.HttpRequest.post("/api/playlet/chapter/collect/set", payload)
.then(function (res) {
if (!res || Number(res.code) !== 200) {
throw new Error((res && (res.tip || res.msg)) || "操作失败");
}
var data = res.data || {};
applyChapterCollectUI(
btn,
data.isCollect != null ? data.isCollect : nextCollect ? 1 : 2,
data.countCollect != null ? data.countCollect : nextCount
);
})
.catch(function (err) {
applyChapterCollectUI(btn, collectedNow ? 1 : 2, prevCount);
toast((err && err.message) || "操作失败,请稍后重试", "error");
})
.finally(function () {
btn.removeAttribute("data-collecting");
btn.disabled = false;
});
}
document.addEventListener("click", function (event) {
var btn = window.domClosest(event.target, "[data-chapter-collect]");
if (!btn) return;
event.preventDefault();
toggleChapterCollect(btn);
});
})();
// 短剧收藏:POST /api/playlet/collect/set → upstream /api/app/playlet/collect/set
(function () {
function toast(message, type) {
if (typeof window.showToast === "function") {
window.showToast(message, type);
return;
}
}
function toInt(v, fallback) {
var n = parseInt(v, 10);
return isNaN(n) ? fallback || 0 : n;
}
function formatLikeCount(n) {
var v = Number(n) || 0;
if (v <= 0) return "0";
if (v < 10000) return String(v);
var wan = v / 10000;
if (wan >= 100) return Math.round(wan) + " 万";
return wan.toFixed(1) + " 万";
}
function isLoggedIn() {
var composer = document.querySelector("[data-play-composer]");
if (composer && composer.getAttribute("data-logged-in") === "1") return true;
if (window.CookieUtil && typeof window.CookieUtil.isLoggedIn === "function") {
return !!window.CookieUtil.isLoggedIn();
}
return false;
}
function applyPlayletCollectUI(btn, isCollect, countCollect) {
if (!btn) return;
var collected = Number(isCollect) === 1;
btn.classList.toggle("is-active", collected);
btn.setAttribute("data-collected", collected ? "1" : "2");
btn.setAttribute("aria-label", collected ? "取消收藏" : "收藏");
var label = btn.querySelector("[data-playlet-collect-label]");
if (label) label.textContent = collected ? "已收藏" : "收藏";
var countEl = btn.querySelector("[data-playlet-collect-count]");
if (countCollect != null) {
btn.setAttribute("data-collect-raw", String(countCollect));
if (countEl) countEl.textContent = formatLikeCount(countCollect);
var stats = document.querySelectorAll("[data-playlet-collect-stat]");
Array.prototype.forEach.call(stats, function (el) {
el.textContent = formatLikeCount(countCollect);
});
}
if (countEl) countEl.hidden = collected;
}
function togglePlayletCollect(btn) {
if (!btn || btn.disabled || btn.getAttribute("data-collecting") === "1") return;
if (!isLoggedIn()) {
var loginBtn = document.querySelector("[data-login-open]");
if (loginBtn) loginBtn.click();
return;
}
if (!window.HttpRequest || typeof window.HttpRequest.post !== "function") {
toast("收藏服务未就绪", "error");
return;
}
var playletId = toInt(btn.getAttribute("data-playlet-id"), 0);
if (playletId <= 0) {
toast("缺少短剧 ID", "error");
return;
}
var collectedNow = Number(btn.getAttribute("data-collected") || 2) === 1;
var nextCollect = !collectedNow;
var prevCount = toInt(btn.getAttribute("data-collect-raw"), 0);
var nextCount = Math.max(0, prevCount + (nextCollect ? 1 : -1));
btn.setAttribute("data-collecting", "1");
btn.disabled = true;
applyPlayletCollectUI(btn, nextCollect ? 1 : 2, nextCount);
var payload = { id: playletId, isCollect: nextCollect };
window.HttpRequest.post("/api/playlet/collect/set", payload)
.then(function (res) {
if (!res || Number(res.code) !== 200) {
throw new Error((res && (res.tip || res.msg)) || "操作失败");
}
var data = res.data || {};
applyPlayletCollectUI(
btn,
data.isCollect != null ? data.isCollect : nextCollect ? 1 : 2,
data.countCollect != null ? data.countCollect : nextCount
);
})
.catch(function (err) {
applyPlayletCollectUI(btn, collectedNow ? 1 : 2, prevCount);
toast((err && err.message) || "操作失败,请稍后重试", "error");
})
.finally(function () {
btn.removeAttribute("data-collecting");
btn.disabled = false;
});
}
document.addEventListener("click", function (event) {
var btn = window.domClosest(event.target, "[data-playlet-collect]");
if (!btn) return;
event.preventDefault();
togglePlayletCollect(btn);
});
})();
// 简介展开:PC 超过 1 行、H5 超过 2 行才显示展开按钮
(function () {
var root = document.querySelector("[data-play-synopsis]");
+12 -351
View File
@@ -1,30 +1,19 @@
// 播放页评论:只读列表 + 「查看更多评论」追加翻页
(function () {
var root = document.querySelector("[data-play-composer]");
if (!root) return;
var commentsRoot = document.querySelector("[data-play-comments]");
if (!commentsRoot) return;
var commentsRoot = document.querySelector("[data-play-comments]") || root.closest(".play-comments");
var input = root.querySelector("[data-comment-input]");
var countEl = root.querySelector("[data-comment-count]");
var sendBtn = root.querySelector("[data-comment-send]");
var listEl = document.querySelector("[data-comment-list]");
var totalEl = document.querySelector("[data-comment-total-label]");
var moreBtn = document.querySelector("[data-comment-more]");
var sending = false;
var inlineSending = false;
var loadingMore = false;
var MAX_LEN = 200;
var PAGE_SIZE = 4;
var replyTarget = null;
function toast(message, type) {
if (typeof window.showToast === "function") {
window.showToast(message, type);
return;
}
if (window.AuthAPI && typeof window.AuthAPI.toast === "function") {
window.AuthAPI.toast(message, type || "info");
return;
}
}
function toInt(v, fallback) {
@@ -43,14 +32,14 @@
function getPageSize() {
return Math.max(
1,
toInt(commentsRoot && commentsRoot.getAttribute("data-comment-size"), PAGE_SIZE)
toInt(commentsRoot.getAttribute("data-comment-size"), PAGE_SIZE)
);
}
function getPage() {
return Math.max(
1,
toInt(commentsRoot && commentsRoot.getAttribute("data-comment-page"), 1)
toInt(commentsRoot.getAttribute("data-comment-page"), 1)
);
}
@@ -60,7 +49,6 @@
}
function setPagerState(page, loaded, total) {
if (!commentsRoot) return;
if (page != null) commentsRoot.setAttribute("data-comment-page", String(page));
if (loaded != null) commentsRoot.setAttribute("data-comment-loaded", String(loaded));
if (total != null) commentsRoot.setAttribute("data-comment-total", String(total));
@@ -80,12 +68,6 @@
}
}
function syncCount() {
if (!input || !countEl) return;
var len = Array.from(input.value || "").length;
countEl.textContent = len + " / " + MAX_LEN;
}
function formatTime(raw) {
var s = String(raw || "").trim();
if (!s) return "刚刚";
@@ -111,7 +93,6 @@
var text = escapeHtml(item.text || "");
var time = escapeHtml(formatTime(item.createdAt));
var like = toInt(item.countLike, 0);
var liked = Number(item.isLike) === 1;
var avatarHtml = avatar
? '<img class="play-comment__avatar lazy" src="/static/images/placeholder.png?v=2" data-src="' +
escapeHtml(avatar) +
@@ -135,7 +116,6 @@
})
.join("");
var commentId = escapeHtml(item.id || "");
var rawName = String(item.userName || "匿名用户");
return (
'<li class="play-comment" data-comment-id="' +
commentId +
@@ -155,27 +135,12 @@
"</p>" +
replies +
'<div class="play-comment__actions">' +
'<button class="play-comment__action' +
(liked ? " is-active" : "") +
'" type="button" aria-label="点赞" data-comment-like="' +
commentId +
'" data-liked="' +
(liked ? "1" : "2") +
'" data-like-raw="' +
escapeHtml(like) +
'">' +
'<span class="play-comment__action">' +
'<img class="icon" src="/static/images/placeholder.png?v=2" data-src="/static/images/heart.svg" alt="" loading="lazy" decoding="async">' +
'<span data-like-count>' +
"<span>" +
escapeHtml(like) +
"</span>" +
"</button>" +
'<button class="play-comment__action" type="button" data-comment-reply data-comment-id="' +
commentId +
'" data-parents-id="' +
commentId +
'" data-reply-name="' +
escapeHtml(rawName) +
'">回复</button>' +
"</span>" +
"</div></div></li>"
);
}
@@ -195,26 +160,6 @@
totalEl.textContent = n > 0 ? n + " 条" : "暂无评论";
}
function renderList(list, totalCount) {
if (!listEl) return;
list = Array.isArray(list) ? list : [];
clearReplyTarget();
if (!list.length) {
listEl.innerHTML =
'<li class="play-comment play-comment--empty"><p class="play-comment__text">暂无评论</p></li>';
} else {
listEl.innerHTML = list.map(renderCommentItem).join("");
if (typeof window.initLazyImages === "function") {
window.initLazyImages(listEl);
}
}
var total = typeof totalCount === "number" ? totalCount : toInt(commentsRoot && commentsRoot.getAttribute("data-comment-total"), 0);
if (typeof totalCount === "number") {
updateTotal(totalCount);
}
setPagerState(1, list.length, total);
}
// 下一页追加到现有列表底部,不替换已展示评论。
function appendList(list, totalCount) {
if (!listEl) return;
@@ -222,7 +167,7 @@
var total =
typeof totalCount === "number"
? totalCount
: toInt(commentsRoot && commentsRoot.getAttribute("data-comment-total"), 0);
: toInt(commentsRoot.getAttribute("data-comment-total"), 0);
if (typeof totalCount === "number") {
updateTotal(totalCount);
}
@@ -264,8 +209,8 @@
if (!window.HttpRequest || typeof window.HttpRequest.get !== "function") {
return Promise.reject(new Error("评论服务未就绪"));
}
var objectId = toInt(root.getAttribute("data-object-id"), 0);
var objectType = toInt(root.getAttribute("data-object-type"), 10);
var objectId = toInt(commentsRoot.getAttribute("data-object-id"), 0);
var objectType = toInt(commentsRoot.getAttribute("data-object-type"), 10);
if (objectId <= 0) return Promise.reject(new Error("缺少评论对象"));
var pageSize = getPageSize();
return window.HttpRequest.get("/api/comment/list", {
@@ -284,14 +229,6 @@
});
}
function refreshList() {
return fetchCommentPage(1)
.then(function (data) {
renderList(data.list || [], toInt(data.totalCount, 0));
})
.catch(function () {});
}
function loadMoreComments() {
if (loadingMore || !moreBtn) return;
var nextPage = getPage() + 1;
@@ -301,9 +238,7 @@
.then(function (data) {
var list = data.list || [];
var total = toInt(data.totalCount, 0);
if (commentsRoot) {
commentsRoot.setAttribute("data-comment-page", String(nextPage));
}
commentsRoot.setAttribute("data-comment-page", String(nextPage));
// 只追加,绝不 renderList 整表替换
appendList(list, total);
})
@@ -316,284 +251,10 @@
});
}
function removeInlineComposer() {
if (!listEl) return;
listEl.querySelectorAll("[data-comment-inline-composer]").forEach(function (el) {
el.remove();
});
}
function clearReplyTarget() {
replyTarget = null;
removeInlineComposer();
}
function applyLikeUI(btn, isLike, countLike) {
if (!btn) return;
var liked = Number(isLike) === 1;
btn.classList.toggle("is-active", liked);
btn.setAttribute("data-liked", liked ? "1" : "2");
if (countLike != null) {
btn.setAttribute("data-like-raw", String(countLike));
var countEl = btn.querySelector("[data-like-count]");
if (countEl) countEl.textContent = String(countLike);
}
}
function toggleCommentLike(btn) {
if (!btn || btn.getAttribute("data-liking") === "1") return;
if (root.getAttribute("data-logged-in") !== "1") {
var loginBtn = document.querySelector("[data-login-open]");
if (loginBtn) loginBtn.click();
return;
}
if (!window.HttpRequest || typeof window.HttpRequest.post !== "function") {
toast("点赞服务未就绪", "error");
return;
}
var commentId = toInt(btn.getAttribute("data-comment-like"), 0);
if (commentId <= 0) return;
var likedNow = Number(btn.getAttribute("data-liked") || 2) === 1;
var nextLike = !likedNow;
var prevCount = toInt(btn.getAttribute("data-like-raw"), 0);
var nextCount = Math.max(0, prevCount + (nextLike ? 1 : -1));
btn.setAttribute("data-liking", "1");
btn.disabled = true;
applyLikeUI(btn, nextLike ? 1 : 2, nextCount);
var payload = { commentId: commentId, isLike: nextLike };
window.HttpRequest.post("/api/comment/like/set", payload)
.then(function (res) {
if (!res || Number(res.code) !== 200) {
throw new Error((res && (res.tip || res.msg)) || "操作失败");
}
var data = res.data || {};
applyLikeUI(
btn,
data.isLike != null ? data.isLike : nextLike ? 1 : 2,
data.countLike != null ? data.countLike : nextCount
);
})
.catch(function (err) {
applyLikeUI(btn, likedNow ? 1 : 2, prevCount);
toast((err && err.message) || "操作失败,请稍后重试", "error");
})
.finally(function () {
btn.removeAttribute("data-liking");
btn.disabled = false;
});
}
function openInlineComposer(commentEl, target) {
if (!commentEl || !target) return;
removeInlineComposer();
replyTarget = target;
var body = commentEl.querySelector(".play-comment__body") || commentEl;
var actions = body.querySelector(".play-comment__actions");
var box = document.createElement("div");
box.className = "play-comment__inline";
box.setAttribute("data-comment-inline-composer", "");
var name = target.replyName || "用户";
box.innerHTML =
'<div class="play-comment__inline-hint">' +
"<span>回复 @" +
escapeHtml(name) +
"</span>" +
'<button type="button" data-comment-reply-cancel>取消</button>' +
"</div>" +
'<div class="play-comment__inline-row">' +
'<input class="play-comment__inline-input" type="text" maxlength="200" placeholder="回复 @' +
escapeHtml(name) +
'…" data-comment-inline-input autocomplete="off" />' +
'<button class="play-comment__inline-send" type="button" data-comment-inline-send>发送</button>' +
"</div>";
if (actions && actions.parentNode === body) {
if (actions.nextSibling) body.insertBefore(box, actions.nextSibling);
else body.appendChild(box);
} else {
body.appendChild(box);
}
var inlineInput = box.querySelector("[data-comment-inline-input]");
if (inlineInput) inlineInput.focus();
}
function setSending(on) {
sending = !!on;
if (sendBtn) sendBtn.disabled = !!on;
if (input) input.disabled = !!on;
}
function submitTopComment() {
if (sending) return;
if (root.getAttribute("data-logged-in") !== "1") {
var loginBtn = document.querySelector("[data-login-open]");
if (loginBtn) loginBtn.click();
return;
}
if (!window.HttpRequest || typeof window.HttpRequest.post !== "function") {
toast("评论服务未就绪", "error");
return;
}
var text = String((input && input.value) || "").trim();
if (!text) {
toast("请输入评论内容", "error");
return;
}
if (Array.from(text).length > MAX_LEN) {
toast("评论最多 200 字", "error");
return;
}
var objectId = toInt(root.getAttribute("data-object-id"), 0);
var objectType = toInt(root.getAttribute("data-object-type"), 10);
if (objectId <= 0) {
toast("缺少评论对象", "error");
return;
}
var payload = {
objectId: objectId,
objectType: objectType,
text: text,
parentsId: 0,
replyCommentId: 0,
};
setSending(true);
window.HttpRequest.post("/api/comment/send", payload)
.then(function (res) {
if (!res || Number(res.code) !== 200) {
throw new Error((res && (res.tip || res.msg)) || "发表失败");
}
if (input) input.value = "";
syncCount();
clearReplyTarget();
toast("发布成功,评论审核后才会展示", "success");
return refreshList();
})
.catch(function (err) {
toast((err && err.message) || "发表失败,请稍后重试", "error");
})
.finally(function () {
setSending(false);
});
}
function sendInlineReply(box) {
if (!box || inlineSending || !replyTarget) return;
if (!window.HttpRequest || typeof window.HttpRequest.post !== "function") {
toast("评论服务未就绪", "error");
return;
}
var inlineInput = box.querySelector("[data-comment-inline-input]");
var btn = box.querySelector("[data-comment-inline-send]");
if (!inlineInput) return;
var text = String(inlineInput.value || "").trim();
if (!text) {
toast("请输入评论内容", "error");
return;
}
if (Array.from(text).length > MAX_LEN) {
toast("评论最多 200 字", "error");
return;
}
var objectId = toInt(root.getAttribute("data-object-id"), 0);
var objectType = toInt(root.getAttribute("data-object-type"), 10);
if (objectId <= 0) {
toast("缺少评论对象", "error");
return;
}
inlineSending = true;
if (btn) btn.disabled = true;
inlineInput.disabled = true;
var payload = {
objectId: objectId,
objectType: objectType,
text: text,
parentsId: toInt(replyTarget.parentsId, 0),
replyCommentId: toInt(
replyTarget.replyCommentId || replyTarget.parentsId,
0
),
};
window.HttpRequest.post("/api/comment/send", payload)
.then(function (res) {
if (!res || Number(res.code) !== 200) {
throw new Error((res && (res.tip || res.msg)) || "发表失败");
}
clearReplyTarget();
toast("发布成功,评论审核后才会展示", "success");
return refreshList();
})
.catch(function (err) {
toast((err && err.message) || "发表失败,请稍后重试", "error");
})
.finally(function () {
inlineSending = false;
if (btn) btn.disabled = false;
if (inlineInput) inlineInput.disabled = false;
});
}
if (input) {
input.addEventListener("input", syncCount);
input.addEventListener("keydown", function (event) {
if (event.key === "Enter" && !event.shiftKey) {
event.preventDefault();
submitTopComment();
}
});
syncCount();
}
if (sendBtn) {
sendBtn.addEventListener("click", submitTopComment);
}
if (moreBtn) {
moreBtn.addEventListener("click", function (event) {
event.preventDefault();
loadMoreComments();
});
}
if (listEl) {
listEl.addEventListener("click", function (event) {
var likeBtn = window.domClosest(event.target, "[data-comment-like]");
if (likeBtn && listEl.contains(likeBtn)) {
event.preventDefault();
toggleCommentLike(likeBtn);
return;
}
var cancelBtn = window.domClosest(event.target, "[data-comment-reply-cancel]");
if (cancelBtn && listEl.contains(cancelBtn)) {
clearReplyTarget();
return;
}
var sendInlineBtn = window.domClosest(event.target, "[data-comment-inline-send]");
if (sendInlineBtn && listEl.contains(sendInlineBtn)) {
var box = sendInlineBtn.closest("[data-comment-inline-composer]");
sendInlineReply(box);
return;
}
var replyBtn = window.domClosest(event.target, "[data-comment-reply]");
if (!replyBtn || !listEl.contains(replyBtn)) return;
if (root.getAttribute("data-logged-in") !== "1") {
var loginBtn = document.querySelector("[data-login-open]");
if (loginBtn) loginBtn.click();
return;
}
var parentsId = toInt(replyBtn.getAttribute("data-parents-id"), 0);
var commentId = toInt(replyBtn.getAttribute("data-comment-id"), parentsId);
if (parentsId <= 0) return;
var commentEl = replyBtn.closest(".play-comment");
openInlineComposer(commentEl, {
parentsId: parentsId,
replyCommentId: commentId,
replyName: replyBtn.getAttribute("data-reply-name") || "用户",
});
});
listEl.addEventListener("keydown", function (event) {
if (event.key !== "Enter" || event.shiftKey) return;
var inlineInput = window.domClosest(event.target, "[data-comment-inline-input]");
if (!inlineInput || !listEl.contains(inlineInput)) return;
event.preventDefault();
var box = inlineInput.closest("[data-comment-inline-composer]");
sendInlineReply(box);
});
}
})();
-169
View File
@@ -1,169 +0,0 @@
(function () {
var form = document.querySelector("[data-register-page-form]");
var password = document.querySelector("[data-register-page-password]");
var confirm = document.querySelector("[data-register-page-password-confirm]");
var submitBtn = document.querySelector("[data-register-page-submit]") || (form && form.querySelector('button[type="submit"]'));
var root = form || document;
function bindToggle(key, input) {
var btn = document.querySelector('[data-register-page-toggle="' + key + '"]');
var icon = document.querySelector('[data-register-page-toggle-icon="' + key + '"]');
if (!btn || !input || !icon) return;
btn.addEventListener("click", function () {
var show = input.getAttribute("type") === "password";
input.setAttribute("type", show ? "text" : "password");
icon.setAttribute("src", show ? "/static/images/eye-off.svg" : "/static/images/eye.svg");
btn.setAttribute("aria-label", show ? "隐藏密码" : "显示密码");
});
}
bindToggle("password", password);
bindToggle("confirm", confirm);
function clearFieldError(name) {
var field = root.querySelector('[data-field="' + name + '"]');
var err = root.querySelector('[data-field-error="' + name + '"]');
if (field) field.classList.remove("is-error");
if (err) {
err.hidden = true;
var text = err.querySelector("[data-field-error-text]");
if (text) text.textContent = "";
}
}
function clearAllErrors() {
root.querySelectorAll("[data-field]").forEach(function (el) {
el.classList.remove("is-error");
});
root.querySelectorAll("[data-field-error]").forEach(function (err) {
err.hidden = true;
var text = err.querySelector("[data-field-error-text]");
if (text) text.textContent = "";
});
}
function setFieldError(name, message) {
var field = root.querySelector('[data-field="' + name + '"]');
var err = root.querySelector('[data-field-error="' + name + '"]');
if (field) field.classList.add("is-error");
if (err) {
err.hidden = false;
var text = err.querySelector("[data-field-error-text]");
if (text) text.textContent = message || "";
}
}
function isAccountNo(value) {
return /^[A-Za-z0-9]{6,20}$/.test(String(value || ""));
}
function isPassword(value) {
var pwd = String(value || "");
if (pwd.length < 8) return false;
return /[A-Za-z]/.test(pwd) && /[0-9]/.test(pwd);
}
function validateForm() {
clearAllErrors();
var fd = new FormData(form);
var account = String(fd.get("account") || "").trim();
var pwd = String(fd.get("password") || "");
var pwd2 = String(fd.get("passwordConfirm") || "");
var agree = form.querySelector('input[name="agree"]');
var valid = true;
if (!account) {
setFieldError("account", "请输入账号");
valid = false;
} else if (!isAccountNo(account)) {
setFieldError("account", "账号需为 6 到 20 位字母或数字");
valid = false;
}
if (!pwd) {
setFieldError("password", "请输入密码");
valid = false;
} else if (!isPassword(pwd)) {
setFieldError("password", "密码需 8 位以上,且同时包含字母与数字");
valid = false;
}
if (!pwd2) {
setFieldError("passwordConfirm", "请再次输入密码");
valid = false;
} else if (pwd2 !== pwd) {
setFieldError("passwordConfirm", "两次输入的密码不一致");
valid = false;
}
if (agree && !agree.checked) {
setFieldError("agree", "请先同意用户条款与隐私政策");
valid = false;
}
if (!valid) return null;
return { accountNo: account, password: pwd };
}
function mapServerError(err) {
var tip = (err && err.message) || "注册失败,请稍后重试";
var code = err && err.code;
if (code === 5005 || /已存在|重复|占用/.test(tip)) {
setFieldError("account", tip);
return;
}
if (/密码|password/i.test(tip)) {
setFieldError("password", tip);
return;
}
setFieldError("account", tip);
}
function setSubmitting(loading) {
if (!submitBtn) return;
submitBtn.disabled = !!loading;
submitBtn.classList.toggle("is-loading", !!loading);
submitBtn.textContent = loading ? "注册中…" : "注册";
}
if (form) {
form.querySelectorAll("input").forEach(function (input) {
input.addEventListener("input", function () {
var name = input.getAttribute("name");
if (name === "agree") clearFieldError("agree");
else if (name) clearFieldError(name);
});
if (input.type === "checkbox") {
input.addEventListener("change", function () {
clearFieldError("agree");
});
}
});
form.addEventListener("submit", function (event) {
event.preventDefault();
if (!window.AuthAPI || !window.HttpRequest) {
setFieldError("account", "认证脚本未加载,请刷新页面");
return;
}
if (submitBtn && submitBtn.classList.contains("is-loading")) return;
var payload = validateForm();
if (!payload) return;
setSubmitting(true);
AuthAPI.register(payload)
.then(function (res) {
AuthAPI.toast((res && res.tip) || "注册成功", "success");
window.setTimeout(function () {
window.location.href = "/";
}, 400);
})
.catch(function (err) {
setSubmitting(false);
mapServerError(err);
AuthAPI.toast((err && err.message) || "注册失败", "error");
});
});
}
})();
-256
View File
@@ -1,256 +0,0 @@
(function () {
var root = document.querySelector("[data-register-pop]");
if (!root) return;
var form = root.querySelector("[data-register-form]");
var password = root.querySelector("[data-register-password]");
var confirm = root.querySelector("[data-register-password-confirm]");
var submitBtn = root.querySelector("[data-register-submit]") || (form && form.querySelector('button[type="submit"]'));
var previousOverflow = "";
function closeLogin() {
if (typeof window.closeLoginPop === "function") {
window.closeLoginPop();
return;
}
var login = document.querySelector("[data-login-pop]");
if (login) login.classList.remove("is-open");
}
function closeUserMenu() {
var menu = document.querySelector("[data-user-menu].is-open");
if (menu) {
menu.classList.remove("is-open");
var t = menu.querySelector("[data-user-menu-toggle]");
if (t) t.setAttribute("aria-expanded", "false");
}
}
function clearFieldError(name) {
var field = root.querySelector('[data-field="' + name + '"]');
var err = root.querySelector('[data-field-error="' + name + '"]');
if (field) field.classList.remove("is-error");
if (err) {
err.hidden = true;
var text = err.querySelector("[data-field-error-text]");
if (text) text.textContent = "";
}
}
function clearAllErrors() {
root.querySelectorAll("[data-field]").forEach(function (el) {
el.classList.remove("is-error");
});
root.querySelectorAll("[data-field-error]").forEach(function (err) {
err.hidden = true;
var text = err.querySelector("[data-field-error-text]");
if (text) text.textContent = "";
});
}
function setFieldError(name, message) {
var field = root.querySelector('[data-field="' + name + '"]');
var err = root.querySelector('[data-field-error="' + name + '"]');
if (field) field.classList.add("is-error");
if (err) {
err.hidden = false;
var text = err.querySelector("[data-field-error-text]");
if (text) text.textContent = message || "";
}
}
function isAccountNo(value) {
return /^[A-Za-z0-9]{6,20}$/.test(String(value || ""));
}
function isPassword(value) {
var pwd = String(value || "");
if (pwd.length < 8) return false;
return /[A-Za-z]/.test(pwd) && /[0-9]/.test(pwd);
}
function bindingMessage(message, fallback) {
return String(message || fallback || "").replace(/注册/g, "绑定");
}
function validateForm() {
clearAllErrors();
var fd = new FormData(form);
var account = String(fd.get("account") || "").trim();
var pwd = String(fd.get("password") || "");
var pwd2 = String(fd.get("passwordConfirm") || "");
var agree = form.querySelector('input[name="agree"]');
var valid = true;
if (!account) {
setFieldError("account", "请输入账号");
valid = false;
} else if (!isAccountNo(account)) {
setFieldError("account", "账号需为 6 到 20 位字母或数字");
valid = false;
}
if (!pwd) {
setFieldError("password", "请输入密码");
valid = false;
} else if (!isPassword(pwd)) {
setFieldError("password", "密码需 8 位以上,且同时包含字母与数字");
valid = false;
}
if (!pwd2) {
setFieldError("passwordConfirm", "请再次输入密码");
valid = false;
} else if (pwd2 !== pwd) {
setFieldError("passwordConfirm", "两次输入的密码不一致");
valid = false;
}
if (agree && !agree.checked) {
setFieldError("agree", "请先同意用户条款与隐私政策");
valid = false;
}
if (!valid) return null;
return { accountNo: account, password: pwd };
}
function mapServerError(err) {
var tip = bindingMessage(err && err.message, "绑定失败,请稍后重试");
var code = err && err.code;
if (code === 5005 || /已存在|重复|占用/.test(tip)) {
setFieldError("account", tip);
return;
}
if (/密码|password/i.test(tip)) {
setFieldError("password", tip);
return;
}
setFieldError("account", tip);
}
function open(event) {
if (event) event.preventDefault();
closeLogin();
closeUserMenu();
clearAllErrors();
root.classList.add("is-open");
previousOverflow = document.body.style.overflow;
document.body.style.overflow = "hidden";
var first = root.querySelector(".register-pop__input");
if (first) {
window.setTimeout(function () {
first.focus();
}, 0);
}
}
function close() {
root.classList.remove("is-open");
document.body.style.overflow = previousOverflow;
clearAllErrors();
}
window.openRegisterPop = open;
window.closeRegisterPop = close;
function bindToggle(key, input) {
var btn = root.querySelector('[data-register-toggle="' + key + '"]');
var icon = root.querySelector('[data-register-toggle-icon="' + key + '"]');
if (!btn || !input || !icon) return;
btn.addEventListener("click", function () {
var show = input.getAttribute("type") === "password";
input.setAttribute("type", show ? "text" : "password");
icon.setAttribute("src", show ? "/static/images/eye-off.svg" : "/static/images/eye.svg");
btn.setAttribute("aria-label", show ? "隐藏密码" : "显示密码");
});
}
document.addEventListener("click", function (event) {
var opener = event.target.closest("[data-register-open]");
if (opener) {
open(event);
return;
}
// 站内注册入口统一使用绑定账号弹窗
var registerLink = event.target.closest('a[href="/register"], a[href="/register/"], a[href="#register"]');
if (registerLink) {
open(event);
return;
}
if (!root.classList.contains("is-open")) return;
if (event.target.closest("[data-register-close]")) {
close();
}
});
bindToggle("password", password);
bindToggle("confirm", confirm);
function setSubmitting(loading) {
if (!submitBtn) return;
submitBtn.disabled = !!loading;
submitBtn.classList.toggle("is-loading", !!loading);
submitBtn.textContent = loading ? "绑定中…" : "绑定";
}
if (form) {
form.querySelectorAll("input").forEach(function (input) {
input.addEventListener("input", function () {
var name = input.getAttribute("name");
if (name === "agree") clearFieldError("agree");
else if (name) clearFieldError(name);
});
if (input.type === "checkbox") {
input.addEventListener("change", function () {
clearFieldError("agree");
});
}
});
form.addEventListener("submit", function (event) {
event.preventDefault();
if (!window.AuthAPI || !window.HttpRequest) {
setFieldError("account", "认证脚本未加载,请刷新页面");
return;
}
if (submitBtn && submitBtn.classList.contains("is-loading")) return;
var payload = validateForm();
if (!payload) return;
setSubmitting(true);
AuthAPI.register(payload)
.then(function (res) {
AuthAPI.toast(bindingMessage(res && res.tip, "绑定成功"), "success");
window.setTimeout(function () {
close();
window.location.reload();
}, 400);
})
.catch(function (err) {
setSubmitting(false);
mapServerError(err);
AuthAPI.toast(bindingMessage(err && err.message, "绑定失败"), "error");
});
});
}
document.addEventListener("keydown", function (event) {
if (event.key === "Escape" && root.classList.contains("is-open")) {
close();
}
});
try {
var params = new URLSearchParams(window.location.search || "");
if (params.get("auth") === "register") {
params.delete("auth");
var qs = params.toString();
window.history.replaceState(null, "", window.location.pathname + (qs ? "?" + qs : "") + window.location.hash);
open();
}
} catch (e) {}
})();
-246
View File
@@ -1,246 +0,0 @@
(function () {
var root = document.querySelector("[data-recharge-modal]");
if (!root) return;
var panel = root.querySelector(".recharge-modal__panel");
var openers = document.querySelectorAll("[data-recharge-open]");
var closers = root.querySelectorAll("[data-recharge-close]");
var packagesRoot = root.querySelector("[data-recharge-packages]");
var methodsRoot = root.querySelector("[data-recharge-methods]");
var amount = root.querySelector("[data-recharge-amount]");
var pay = root.querySelector("[data-recharge-pay]");
var status = root.querySelector("[data-recharge-status]");
var previousOverflow = "";
var returnFocus = null;
var paying = false;
function packages() {
return root.querySelectorAll("[data-recharge-package]");
}
function methods() {
return root.querySelectorAll("[data-recharge-method]");
}
function showStatus(message, ok) {
if (!status) return;
status.textContent = message || "";
status.hidden = !message;
status.classList.toggle("is-ok", !!ok);
}
function open(trigger) {
if (!root.hidden) return;
var oldDialog = document.querySelector("[data-play-coin].is-open");
if (oldDialog) {
var oldClose = oldDialog.querySelector("button[data-play-coin-close]");
if (oldClose) oldClose.click();
}
returnFocus = trigger && oldDialog ? document.querySelector("[data-play-coin-open]") : trigger;
previousOverflow = document.body.style.overflow;
showStatus("");
root.hidden = false;
document.body.style.overflow = "hidden";
if (panel) panel.focus();
}
function close() {
if (root.hidden) return;
root.hidden = true;
document.body.style.overflow = previousOverflow;
if (returnFocus && typeof returnFocus.focus === "function") returnFocus.focus();
}
function parsePayMethods(raw) {
try {
var list = JSON.parse(raw || "[]");
return Array.isArray(list) ? list : [];
} catch (e) {
return [];
}
}
function renderMethods(list) {
if (!methodsRoot) return;
methodsRoot.replaceChildren();
if (!list.length) {
var empty = document.createElement("p");
empty.className = "recharge-modal__empty";
empty.textContent = "该套餐暂无可用支付方式";
methodsRoot.appendChild(empty);
return;
}
list.forEach(function (item, index) {
var type = String((item && item.type) || "").trim();
var name = String((item && item.typeName) || type || "支付").trim();
if (!type) return;
var btn = document.createElement("button");
btn.className = "recharge-modal__method" + (index === 0 ? " is-selected" : "");
btn.type = "button";
btn.setAttribute("data-recharge-method", type);
btn.setAttribute("aria-pressed", index === 0 ? "true" : "false");
btn.innerHTML =
'<span class="recharge-modal__method-check" aria-hidden="true"><img src="/static/images/placeholder.png?v=2" data-src="/static/images/recharge/check.svg" alt="" loading="lazy" decoding="async"></span>' +
'<span class="recharge-modal__method-copy"><span></span><small>即时到账</small></span>';
btn.querySelector(".recharge-modal__method-copy span").textContent = name;
methodsRoot.appendChild(btn);
});
if (typeof window.initLazyImages === "function") {
window.initLazyImages(methodsRoot);
}
}
function selectPackage(button) {
Array.prototype.forEach.call(packages(), function (item) {
var selected = item === button;
item.classList.toggle("is-selected", selected);
item.setAttribute("aria-pressed", String(selected));
});
if (amount) amount.textContent = "¥" + (button.getAttribute("data-price") || "0");
renderMethods(parsePayMethods(button.getAttribute("data-pay-methods")));
showStatus("");
}
function bindMethodClicks() {
if (!methodsRoot) return;
methodsRoot.addEventListener("click", function (event) {
var button = event.target.closest("[data-recharge-method]");
if (!button || !methodsRoot.contains(button)) return;
Array.prototype.forEach.call(methods(), function (item) {
var selected = item === button;
item.classList.toggle("is-selected", selected);
item.setAttribute("aria-pressed", String(selected));
});
showStatus("");
});
}
Array.prototype.forEach.call(openers, function (button) {
button.addEventListener("click", function (event) {
event.preventDefault();
open(button);
});
});
Array.prototype.forEach.call(closers, function (element) {
element.addEventListener("click", close);
});
if (packagesRoot) {
packagesRoot.addEventListener("click", function (event) {
var button = event.target.closest("[data-recharge-package]");
if (!button || !packagesRoot.contains(button)) return;
selectPackage(button);
});
}
bindMethodClicks();
if (pay) {
pay.addEventListener("click", function () {
if (paying) return;
var selectedPackage = root.querySelector("[data-recharge-package].is-selected");
var selectedMethod = root.querySelector("[data-recharge-method].is-selected");
var packageId = selectedPackage ? Number(selectedPackage.getAttribute("data-package-id") || 0) : 0;
var payMode = selectedMethod ? selectedMethod.getAttribute("data-recharge-method") : "";
if (!packageId) {
showStatus("请选择充值套餐");
return;
}
if (!payMode) {
showStatus("请选择支付方式");
return;
}
paying = true;
pay.disabled = true;
showStatus("正在创建支付订单…");
var payload = { packageId: packageId, payMode: payMode };
var req =
window.HttpRequest && typeof window.HttpRequest.post === "function"
? window.HttpRequest.post("/api/coin/buy", payload)
: fetch("/api/coin/buy", {
method: "POST",
credentials: "same-origin",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload),
}).then(function (r) {
return r.json().then(function (body) {
return body;
});
});
req
.then(function (result) {
if (!result || Number(result.code) !== 200 || !result.data || !result.data.payUrl) {
throw new Error((result && (result.tip || result.msg)) || "创建支付订单失败");
}
showStatus("订单创建成功,正在前往支付…", true);
openPayInNewWindow(result.data.payUrl);
paying = false;
pay.disabled = false;
})
.catch(function (error) {
showStatus((error && error.message) || "创建支付订单失败,请稍后重试");
paying = false;
pay.disabled = false;
});
});
}
function openPayInNewWindow(url) {
url = String(url || "").trim();
if (!url) return;
var opened = null;
try {
opened = window.open(url, "_blank", "noopener,noreferrer");
} catch (e) {
opened = null;
}
if (opened) {
try {
opened.opener = null;
} catch (e2) {}
showStatus("已在新窗口打开支付页面,请完成支付后返回本页", true);
return;
}
// 弹窗被拦截时用 <a target=_blank> 再试,仍不跳转当前页
var link = document.createElement("a");
link.href = url;
link.target = "_blank";
link.rel = "noopener noreferrer";
link.style.display = "none";
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
showStatus("若未打开支付页,请允许本站弹窗后重试", true);
}
document.addEventListener("keydown", function (event) {
if (root.hidden) return;
if (event.key === "Escape") {
event.preventDefault();
close();
return;
}
if (event.key !== "Tab") return;
var focusable = root.querySelectorAll("button:not([disabled]), a[href]");
if (!focusable.length) return;
var first = focusable[0];
var last = focusable[focusable.length - 1];
if (event.shiftKey && (document.activeElement === first || document.activeElement === panel)) {
event.preventDefault();
last.focus();
} else if (!event.shiftKey && document.activeElement === last) {
event.preventDefault();
first.focus();
}
});
window.openRechargeModal = function (trigger) {
open(trigger || null);
};
if (new URLSearchParams(window.location.search).get("recharge") === "1") open(null);
})();
-322
View File
@@ -1,322 +0,0 @@
(() => {
const root = document.querySelector('[data-recharge]');
if (!root) return;
const packages = Array.from(root.querySelectorAll('[data-recharge-package]'));
const methodsRoot = root.querySelector('[data-recharge-methods]');
const methodEmpty = root.querySelector('[data-recharge-method-empty]');
const payButton = root.querySelector('[data-recharge-pay]');
const price = root.querySelector('[data-recharge-price]');
const coins = root.querySelector('[data-recharge-coins]');
const status = root.querySelector('[data-recharge-status]');
const rowsRoot = root.querySelector('[data-recharge-rows]');
const filters = Array.from(root.querySelectorAll('[data-recharge-filter]'));
const pagination = root.querySelector('[data-recharge-pagination]');
const pageButtons = root.querySelector('[data-recharge-pages]');
const previous = root.querySelector('[data-recharge-prev]');
const next = root.querySelector('[data-recharge-next]');
const total = root.querySelector('[data-recharge-total]');
const empty = root.querySelector('[data-recharge-history-empty]');
const more = root.querySelector('[data-recharge-more]');
const exportBtn = root.querySelector('[data-recharge-export]');
const mobile = window.matchMedia('(max-width: 768px)');
let selectedPackage = null;
let selectedMethod = '';
let activeType = 0;
let currentPage = Number(pagination.dataset.page) || 1;
let pageSize = Number(pagination.dataset.pageSize) || 5;
let totalCount = Number(pagination.dataset.total) || 0;
let expanded = false;
function showStatus(message, success) {
status.textContent = message;
status.hidden = !message;
status.classList.toggle('is-success', Boolean(success));
}
function choose(button, choices) {
choices.forEach(choice => {
const isSelected = choice === button;
choice.classList.toggle('is-selected', isSelected);
choice.setAttribute('aria-checked', String(isSelected));
});
}
function parseMethods(button) {
try {
const parsed = JSON.parse(button.dataset.payMethods || '[]');
return Array.isArray(parsed) ? parsed : [];
} catch (_) {
return [];
}
}
function renderMethods(items) {
methodsRoot.replaceChildren();
selectedMethod = '';
items.forEach((item, index) => {
const type = String(item.type || '').trim();
if (!type) return;
const button = document.createElement('button');
button.className = 'recharge-method';
button.type = 'button';
button.setAttribute('role', 'radio');
button.dataset.rechargeMethod = type;
button.innerHTML = '<span class="recharge-method__radio"><img src="/static/images/placeholder.png?v=2" data-src="/static/images/recharge/check.svg" alt="" loading="lazy" decoding="async"></span><span><strong></strong><small>支付成功后到账</small></span>';
button.querySelector('strong').textContent = item.typeName || type;
button.addEventListener('click', () => {
const choices = Array.from(methodsRoot.querySelectorAll('[data-recharge-method]'));
choose(button, choices);
selectedMethod = type;
showStatus('');
});
methodsRoot.appendChild(button);
if (index === 0) {
button.classList.add('is-selected');
button.setAttribute('aria-checked', 'true');
selectedMethod = type;
} else {
button.setAttribute('aria-checked', 'false');
}
});
const hasMethod = Boolean(selectedMethod);
methodEmpty.hidden = hasMethod;
payButton.disabled = !selectedPackage || !hasMethod;
if (typeof window.initLazyImages === 'function') {
window.initLazyImages(methodsRoot);
}
}
function selectPackage(button) {
selectedPackage = button;
choose(button, packages);
price.textContent = button.dataset.price || '—';
coins.textContent = button.dataset.coins || '—';
renderMethods(parseMethods(button));
showStatus('');
}
packages.forEach(button => button.addEventListener('click', () => selectPackage(button)));
const initialPackage = packages.find(button => button.classList.contains('is-selected')) || packages[0];
if (initialPackage) selectPackage(initialPackage);
else payButton.disabled = true;
payButton.addEventListener('click', async () => {
if (!selectedPackage || !selectedMethod) {
showStatus('请选择套餐和支付方式');
return;
}
payButton.disabled = true;
showStatus('正在创建支付订单…');
const payload = {
packageId: Number(selectedPackage.dataset.packageId),
payMode: selectedMethod,
};
try {
let result;
if (window.HttpRequest && typeof window.HttpRequest.post === 'function') {
result = await window.HttpRequest.post('/api/coin/buy', payload);
} else {
const response = await fetch('/api/coin/buy', {
method: 'POST',
credentials: 'same-origin',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(payload),
});
result = await response.json();
}
if (!result || Number(result.code) !== 200 || !result.data || !result.data.payUrl) {
if (result && (Number(result.code) === 401 || Number(result.code) === 1000)) {
const login = document.querySelector('[data-login-open]');
if (login) login.click();
else window.location.href = '/login';
return;
}
throw new Error((result && (result.tip || result.msg)) || '创建支付订单失败');
}
showStatus('订单创建成功,正在前往支付…', true);
openPayInNewWindow(result.data.payUrl);
payButton.disabled = false;
} catch (error) {
showStatus(error.message || '创建支付订单失败,请稍后重试');
payButton.disabled = false;
}
});
function openPayInNewWindow(url) {
url = String(url || '').trim();
if (!url) return;
let opened = null;
try {
opened = window.open(url, '_blank', 'noopener,noreferrer');
} catch (e) {
opened = null;
}
if (opened) {
try {
opened.opener = null;
} catch (e2) {}
showStatus('已在新窗口打开支付页面,请完成支付后返回本页', true);
return;
}
const link = document.createElement('a');
link.href = url;
link.target = '_blank';
link.rel = 'noopener noreferrer';
link.style.display = 'none';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
showStatus('若未打开支付页,请允许本站弹窗后重试', true);
}
function appendText(parent, tag, className, value) {
const node = document.createElement(tag);
node.className = className;
node.textContent = value || '';
parent.appendChild(node);
return node;
}
function renderRows(items) {
rowsRoot.replaceChildren();
items.forEach(item => {
const row = document.createElement('div');
row.className = 'recharge-history__row';
row.dataset.rechargeRow = '';
row.dataset.category = item.category || '';
appendText(row, 'span', 'recharge-history__kind', item.kind);
const detail = document.createElement('span');
detail.className = 'recharge-history__detail-line';
appendText(detail, 'span', 'recharge-history__description', item.description);
const time = document.createElement('time');
time.className = 'recharge-history__time';
appendText(time, 'span', 'only-desktop', item.time);
appendText(time, 'span', 'only-mobile', item.mobileTime);
detail.appendChild(time);
row.appendChild(detail);
const amount = appendText(row, 'strong', 'recharge-history__amount', item.amount);
amount.classList.toggle('is-credit', Boolean(item.credit));
rowsRoot.appendChild(row);
});
empty.hidden = items.length > 0;
empty.textContent = '暂无记录';
}
function renderPagination() {
const pageCount = Math.ceil(totalCount / pageSize);
currentPage = Math.min(currentPage, Math.max(1, pageCount));
pagination.hidden = totalCount === 0;
more.hidden = totalCount <= 5 && !expanded;
previous.disabled = currentPage <= 1;
next.disabled = currentPage >= pageCount;
total.textContent = '共 ' + totalCount + ' 条';
pageButtons.replaceChildren();
const firstPage = Math.max(1, Math.min(currentPage - 1, Math.max(1, pageCount - 3)));
for (let page = firstPage; page <= Math.min(pageCount, firstPage + 3); page += 1) {
const button = document.createElement('button');
button.type = 'button';
button.textContent = String(page);
button.classList.toggle('is-active', page === currentPage);
if (page === currentPage) button.setAttribute('aria-current', 'page');
button.addEventListener('click', () => loadHistory(page));
pageButtons.appendChild(button);
}
}
async function loadHistory(page) {
empty.hidden = false;
empty.textContent = '加载中…';
try {
const query = new URLSearchParams({
page: String(page),
pageSize: String(pageSize),
type: String(activeType),
});
const url = '/api/coin/trans/list?' + query.toString();
let result;
if (window.HttpRequest && typeof window.HttpRequest.get === 'function') {
result = await window.HttpRequest.get(url);
} else {
const response = await fetch(url, {credentials: 'same-origin'});
result = await response.json();
}
if (!result || Number(result.code) !== 200 || !result.data) {
throw new Error((result && (result.tip || result.msg)) || '交易明细加载失败');
}
currentPage = Number(result.data.pageNum) || page;
totalCount = Number(result.data.totalCount) || 0;
renderRows(Array.isArray(result.data.list) ? result.data.list : []);
renderPagination();
} catch (error) {
rowsRoot.replaceChildren();
empty.hidden = false;
empty.textContent = error.message || '交易明细加载失败';
pagination.hidden = true;
}
}
async function exportHistory() {
if (!exportBtn) return;
exportBtn.disabled = true;
exportBtn.classList.add('is-loading');
try {
const query = new URLSearchParams({type: String(activeType)});
const url = '/api/coin/trans/export?' + query.toString();
const response = await fetch(url, {credentials: 'same-origin'});
const contentType = (response.headers.get('Content-Type') || '').toLowerCase();
if (contentType.includes('application/json')) {
const result = await response.json();
throw new Error((result && (result.tip || result.msg)) || '导出失败');
}
if (!response.ok) {
throw new Error('导出失败');
}
const blob = await response.blob();
const disposition = response.headers.get('Content-Disposition') || '';
let filename = 'coin-trans.csv';
const match = /filename\*?=(?:UTF-8''|")?([^\";]+)/i.exec(disposition);
if (match && match[1]) {
filename = decodeURIComponent(match[1].replace(/"/g, '').trim()) || filename;
}
const objectURL = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = objectURL;
link.download = filename;
document.body.appendChild(link);
link.click();
link.remove();
URL.revokeObjectURL(objectURL);
showStatus('导出成功', true);
} catch (error) {
showStatus(error.message || '导出失败', false);
} finally {
exportBtn.disabled = false;
exportBtn.classList.remove('is-loading');
}
}
filters.forEach(button => button.addEventListener('click', () => {
activeType = Number(button.dataset.rechargeFilter) || 0;
filters.forEach(option => {
const selected = option === button;
option.classList.toggle('is-active', selected);
option.setAttribute('aria-pressed', String(selected));
});
loadHistory(1);
}));
previous.addEventListener('click', () => { if (currentPage > 1) loadHistory(currentPage - 1); });
next.addEventListener('click', () => loadHistory(currentPage + 1));
if (exportBtn) {
exportBtn.addEventListener('click', () => exportHistory());
}
more.addEventListener('click', () => {
expanded = !expanded;
pageSize = expanded ? 100 : 5;
more.firstChild.textContent = expanded ? '收起 ' : '全部 ';
more.setAttribute('aria-expanded', String(expanded));
loadHistory(1);
});
renderPagination();
})();
-143
View File
@@ -1,143 +0,0 @@
(() => {
const form = document.querySelector('[data-setting-form]');
if (!form) return;
const status = form.querySelector('[data-setting-status]');
const save = form.querySelector('[data-setting-save]');
const passwordPop = document.querySelector('[data-setting-password-pop]');
const passwordForm = document.querySelector('[data-setting-password-form]');
const passwordOpen = document.querySelector('[data-setting-password-open]');
const passwordStatus = document.querySelector('[data-setting-password-status]');
const passwordSubmit = document.querySelector('[data-setting-password-submit]');
let previousOverflow = '';
function showStatus(message, success = false) {
status.textContent = message;
status.classList.toggle('is-success', success);
}
form.addEventListener('submit', async event => {
event.preventDefault();
const data = {};
const nickname = form.elements.nickName.value.trim();
const introduction = form.elements.introduction.value.trim();
if (!nickname || [...nickname].length > 40) {
showStatus('昵称须为 1 至 40 个字符');
form.elements.nickName.focus();
return;
}
if ([...introduction].length > 300) {
showStatus('简介不能超过 300 个字符');
return;
}
if (nickname !== form.elements.nickName.defaultValue) data.nickName = nickname;
if (introduction !== form.elements.introduction.defaultValue.trim()) data.introduction = introduction;
if (!Object.keys(data).length) {
showStatus('没有需要保存的修改');
return;
}
save.disabled = true;
showStatus('保存中…');
try {
const response = await fetch('/api/auth/profile', {
method: 'POST', credentials: 'same-origin',
headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data),
});
const result = await response.json();
if (!response.ok || result.code !== 200) throw new Error(result.tip || '保存失败,请稍后重试');
showStatus('保存成功', true);
window.setTimeout(() => window.location.reload(), 800);
} catch (error) {
showStatus(error.message || '保存失败,请稍后重试');
save.disabled = false;
}
});
function setPasswordStatus(message, success = false) {
if (!passwordStatus) return;
passwordStatus.textContent = message || '';
passwordStatus.classList.toggle('is-success', success);
}
function openPasswordPop() {
if (!passwordPop || !passwordForm) return;
passwordForm.reset();
setPasswordStatus('');
previousOverflow = document.body.style.overflow;
document.body.style.overflow = 'hidden';
passwordPop.hidden = false;
const first = passwordForm.elements.oldPassword;
if (first) window.setTimeout(() => first.focus(), 0);
}
function closePasswordPop() {
if (!passwordPop || passwordPop.hidden) return;
passwordPop.hidden = true;
document.body.style.overflow = previousOverflow;
if (passwordForm) passwordForm.reset();
setPasswordStatus('');
if (passwordOpen) passwordOpen.focus();
}
if (passwordOpen) passwordOpen.addEventListener('click', openPasswordPop);
if (passwordPop) {
passwordPop.querySelectorAll('[data-setting-password-close]').forEach(button => {
button.addEventListener('click', closePasswordPop);
});
}
document.addEventListener('keydown', event => {
if (event.key === 'Escape' && passwordPop && !passwordPop.hidden) closePasswordPop();
});
if (passwordForm) {
passwordForm.addEventListener('submit', async event => {
event.preventDefault();
const oldPassword = passwordForm.elements.oldPassword.value;
const newPassword = passwordForm.elements.newPassword.value;
const confirmPassword = passwordForm.elements.confirmPassword.value;
if (oldPassword.length < 4 || oldPassword.length > 16) {
setPasswordStatus('请输入 4–16 位当前密码');
passwordForm.elements.oldPassword.focus();
return;
}
if (newPassword.length < 4 || newPassword.length > 16) {
setPasswordStatus('请输入 4–16 位新密码');
passwordForm.elements.newPassword.focus();
return;
}
if (newPassword !== confirmPassword) {
setPasswordStatus('两次输入的新密码不一致');
passwordForm.elements.confirmPassword.focus();
return;
}
passwordSubmit.disabled = true;
passwordSubmit.textContent = '修改中…';
setPasswordStatus('');
try {
const response = await fetch('/api/auth/profile', {
method: 'POST',
credentials: 'same-origin',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ oldPassword, newPassword }),
});
const result = await response.json();
if (!response.ok || result.code !== 200) throw new Error(result.tip || '密码修改失败,请稍后重试');
setPasswordStatus('密码修改成功', true);
window.setTimeout(() => {
closePasswordPop();
window.location.reload();
}, 600);
} catch (error) {
setPasswordStatus(error.message || '密码修改失败,请稍后重试');
passwordSubmit.disabled = false;
passwordSubmit.textContent = '确认修改';
}
});
}
})();
-921
View File
@@ -1,921 +0,0 @@
/**
* Web SDK v1.2.0 接入层。
* 页面/点击/导航沿用 jha-1043 的口径,短剧使用最新版 dramaEvent/dramaStatusChange。
*/
(function (global) {
"use strict";
var state = {
initialized: false,
initializing: false,
queue: [],
page: { key: "internal_other", name: "站内其他页面" },
referrer: { key: "direct", name: "直接访问" },
userType: "normal",
pageStartedAt: Date.now(),
dramaViewSent: false,
adShown: {},
};
function stringValue(value, fallback) {
var result = value == null ? "" : String(value).trim();
return result || (fallback == null ? "" : String(fallback));
}
function integer(value, fallback) {
var result = parseInt(value, 10);
return isFinite(result) ? result : (fallback || 0);
}
function clamp(value, min, max) {
var result = Number(value);
if (!isFinite(result)) result = min;
return Math.max(min, Math.min(max, result));
}
function sdkAvailable() {
return !!(global.WebSDK && global.WebSDK.track);
}
function logError() {}
function invoke(method, payload) {
if (!state.initialized || !sdkAvailable()) {
return new Promise(function (resolve) {
state.queue.push([method, payload, resolve]);
});
}
try {
var fn = global.WebSDK.track[method];
if (typeof fn !== "function") {
var missing = new Error("SDK method does not exist: " + method);
logError(missing);
return Promise.resolve({ sent: false, ok: false, reason: "unknown_method" });
}
var result = fn(payload);
return Promise.resolve(result).then(function (response) {
if (response && response.ok === false) {
logError(method + " rejected: " + JSON.stringify(response));
}
return response;
}).catch(function (error) {
logError(error);
return { sent: false, ok: false, reason: "exception" };
});
} catch (error) {
logError(error);
return Promise.resolve({ sent: false, ok: false, reason: "exception" });
}
}
function flushQueue() {
var pending = state.queue.splice(0, state.queue.length);
pending.forEach(function (entry) {
Promise.resolve(invoke(entry[0], entry[1])).then(entry[2]);
});
}
function currentChannel(value) {
var channel = stringValue(value);
if (!channel && global.ChannelAttribution && typeof global.ChannelAttribution.get === "function") {
channel = stringValue(global.ChannelAttribution.get());
}
if (!channel) channel = stringValue(global.__CHANNEL_CODE__, "organic");
var normalized = channel.toLowerCase();
if (["self", "natural", "official", "channel", "channel://natural"].indexOf(normalized) >= 0) {
return "organic";
}
if (!channel || channel.length > 50 || !/^[A-Za-z0-9_.-]+$/.test(channel) ||
["{}", "0", "-", "unknown"].indexOf(normalized) >= 0) {
return "organic";
}
return channel;
}
function normalizeUid(value) {
var uid = stringValue(value);
if (uid === "0" || uid.length < 4 || uid.length > 32 || !/^[A-Za-z0-9_]+$/.test(uid) ||
["u_1001", "stress_user", "u_lab"].indexOf(uid.toLowerCase()) >= 0) return "";
return uid;
}
function inferPage(path) {
path = stringValue(path, "/").replace(/\/$/, "") || "/";
if (path === "/") return { key: "home", name: "首页" };
var rules = [
[/^\/series-play(?:\/|$)/, "series_play", "AI成人剧集播放页"],
[/^\/series(?:\/|$)/, "series", "AI成人剧集"],
[/^\/play(?:\/|$)/, "play", "短剧播放页"],
[/^\/detail(?:\/|$)/, "detail", "短剧详情"],
[/^\/list(?:\/|$)/, "list", "短剧列表"],
[/^\/rank(?:\/|$)/, "rank", "排行榜"],
[/^\/topic-detail(?:\/|$)/, "topic_detail", "专题详情"],
[/^\/topic(?:\/|$)/, "topic", "专题列表"],
[/^\/tags(?:\/|$)/, "tags", "标签详情"],
[/^\/search(?:-mobile)?(?:\/|$)/, "search", "搜索结果页"],
[/^\/history(?:\/|$)/, "history", "观看历史"],
[/^\/favorites(?:\/|$)/, "favorites", "我的收藏"],
[/^\/profile(?:\/|$)/, "profile", "个人中心"],
];
for (var i = 0; i < rules.length; i += 1) {
if (rules[i][0].test(path)) return { key: rules[i][1], name: rules[i][2] };
}
return { key: "internal_other", name: "站内其他页面" };
}
function readReferrer() {
try {
var saved = global.sessionStorage.getItem("__tracker_last_page");
if (saved) {
var parsed = JSON.parse(saved);
if (parsed && parsed.key) return parsed;
}
} catch (error) {}
try {
if (document.referrer) {
var url = new URL(document.referrer, global.location.href);
if (url.host === global.location.host) return inferPage(url.pathname);
return { key: "external", name: url.host || "外部站点" };
}
} catch (error) {}
return { key: "direct", name: "直接访问" };
}
function rememberPage() {
try { global.sessionStorage.setItem("__tracker_last_page", JSON.stringify(state.page)); } catch (error) {}
}
function init(options) {
if (state.initialized || state.initializing) return;
options = options || {};
var inferred = inferPage(global.location && global.location.pathname);
state.page = {
key: stringValue(options.pageKey, inferred.key),
name: stringValue(options.pageName, inferred.name),
};
state.userType = options.userType === "vip" ? "vip" : "normal";
state.referrer = readReferrer();
state.initializing = true;
var attempts = 0;
var timer = global.setInterval(function () {
attempts += 1;
if (!sdkAvailable() && attempts < 100) return;
global.clearInterval(timer);
if (!sdkAvailable()) {
state.initializing = false;
return;
}
try {
var result = global.WebSDK.init({
appId: stringValue(options.appId),
uid: normalizeUid(options.uid),
channel: currentChannel(options.channel),
encryptedConfig: stringValue(options.encryptedConfig),
appVersion: stringValue(options.appVersion, "1.0.0"),
disableAutoEvents: ["app_page_view", "page_click"],
});
Promise.resolve(result).then(function () {
state.initialized = true;
state.initializing = false;
__commonFieldsReady = null;
loadCommonFields();
flushQueue();
onReady();
}).catch(function (error) {
state.initializing = false;
logError(error);
});
} catch (error) {
state.initializing = false;
logError(error);
}
}, 50);
}
function whenDOMReady(callback) {
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", callback, { once: true });
} else {
callback();
}
}
function trackPageView(extra) {
extra = extra || {};
invoke("appPageView", {
user_type: stringValue(extra.user_type, state.userType),
page_key: state.page.key,
page_name: state.page.name,
referrer_page_key: state.referrer.key,
referrer_page_name: state.referrer.name,
current_page_key: state.page.key,
current_page_name: state.page.name,
page_load_time: pageLoadDuration(),
recommend_trace_id: stringValue(extra.recommend_trace_id),
});
rememberPage();
}
function pageLoadDuration() {
try {
if (global.performance && typeof global.performance.getEntriesByType === "function") {
var entries = global.performance.getEntriesByType("navigation");
if (entries && entries[0]) {
var navigation = entries[0];
var end = navigation.loadEventEnd || navigation.domContentLoadedEventEnd || global.performance.now();
if (isFinite(end)) return Math.max(0, Math.round(end));
}
}
if (global.performance && typeof global.performance.now === "function") {
return Math.max(0, Math.round(global.performance.now()));
}
} catch (error) {}
return Math.max(0, Date.now() - state.pageStartedAt);
}
function trackPageClick(event) {
var doc = document.documentElement || {};
var viewportWidth = global.innerWidth || doc.clientWidth || 0;
var viewportHeight = global.innerHeight || doc.clientHeight || 0;
var x = integer(event.clientX, 0);
var y = integer(event.clientY, 0);
invoke("pageClick", {
page_key: state.page.key,
page_name: state.page.name,
click_page_x: x,
click_page_y: y,
click_x_percent: viewportWidth ? Math.round(clamp(x / viewportWidth * 100, 0, 100)) : 0,
click_y_percent: viewportHeight ? Math.round(clamp(y / viewportHeight * 100, 0, 100)) : 0,
screen_width: integer(global.screen && global.screen.width, 0),
screen_height: integer(global.screen && global.screen.height, 0),
recommend_trace_id: "",
});
}
function trackNavigation(anchor) {
if (!anchor || (!anchor.hasAttribute("data-navigation-key") && !anchor.hasAttribute("data-nav-key")) ||
!anchor.getAttribute("href")) return;
var href = anchor.getAttribute("href");
if (!href || href.charAt(0) === "#" || /^javascript:/i.test(href)) return;
var label = stringValue(anchor.getAttribute("aria-label") || anchor.textContent).replace(/\s+/g, " ");
var navigationKey = stringValue(
anchor.getAttribute("data-navigation-key") || anchor.getAttribute("data-nav-key")
);
if (!/^[A-Za-z][A-Za-z0-9_]{0,63}$/.test(navigationKey)) {
navigationKey = "";
try {
var url = new URL(href, global.location.href);
if (url.origin !== global.location.origin) {
navigationKey = "nav_external";
} else {
var targetPage = inferPage(url.pathname);
if (targetPage.key !== "internal_other") {
navigationKey = "nav_" + targetPage.key;
} else {
var firstSegment = stringValue(url.pathname.split("/").filter(Boolean)[0])
.toLowerCase()
.replace(/[^a-z0-9]+/g, "_")
.replace(/^_+|_+$/g, "")
.slice(0, 48);
navigationKey = firstSegment ? "nav_" + firstSegment : "nav_home";
}
}
} catch (error) {
navigationKey = "nav_other";
}
}
invoke("navigation", {
navigation_key: navigationKey.toLowerCase(),
navigation_name: stringValue(
anchor.getAttribute("data-navigation-name") || anchor.getAttribute("data-nav-name"),
label || navigationKey
).slice(0, 255),
});
}
function adPayload(node) {
var id = stringValue(node && node.getAttribute("data-ad-id"));
if (!id) return null;
var adType = stringValue(node.getAttribute("data-ad-type"), "banner");
var allSlots = document.querySelectorAll('[data-track-ad="external"][data-ad-id]');
var slotPosition = Array.prototype.indexOf.call(allSlots, node) + 1;
if (slotPosition <= 0) slotPosition = 1;
var fallbackSlotKey = state.page.key + "_content_banner_" + slotPosition;
var fallbackSlotName = state.page.name + "内容横幅第" + slotPosition + "位";
if (adType === "interstitial") {
fallbackSlotKey = state.page.key + "_popup_" + slotPosition;
fallbackSlotName = state.page.name + "弹窗广告位";
}
return {
page_key: state.page.key,
page_name: state.page.name,
ad_slot_key: stringValue(
node.getAttribute("data-ad-slot-key"),
fallbackSlotKey
),
ad_slot_name: stringValue(
node.getAttribute("data-ad-slot-name"),
fallbackSlotName
),
ad_id: id,
creative_id: stringValue(node.getAttribute("data-creative-id")),
ad_type: adType,
};
}
function bindAds() {
var selector = '[data-track-ad="external"][data-ad-id]';
var nodes = document.querySelectorAll(selector);
function show(node) {
trackExternalAd(node, "show");
}
if (global.IntersectionObserver) {
var observer = new IntersectionObserver(function (entries) {
entries.forEach(function (entry) {
if (!entry.isIntersecting) return;
show(entry.target);
observer.unobserve(entry.target);
});
}, { threshold: 0.1 });
function observe(node) {
if (!node || node.__trackerAdObserved) return;
node.__trackerAdObserved = true;
observer.observe(node);
}
Array.prototype.forEach.call(nodes, observe);
if (global.MutationObserver) {
new MutationObserver(function (records) {
records.forEach(function (record) {
Array.prototype.forEach.call(record.addedNodes || [], function (node) {
if (!node || node.nodeType !== 1) return;
if (node.matches && node.matches(selector)) observe(node);
if (node.querySelectorAll) {
Array.prototype.forEach.call(node.querySelectorAll(selector), observe);
}
});
});
}).observe(document.documentElement, { childList: true, subtree: true });
}
} else {
Array.prototype.forEach.call(nodes, show);
}
}
function trackExternalAd(node, eventType) {
if (!node || (eventType !== "show" && eventType !== "click")) return;
var payload = adPayload(node);
if (!payload) return;
var key = payload.ad_slot_key + "|" + payload.ad_id;
if (!state.adShown[key]) {
state.adShown[key] = true;
invoke("adImpression", payload);
}
if (eventType === "click" && node.getAttribute("data-ad-clickable") === "1") {
return invoke("adClick", payload);
}
}
function bindExternalAdEvents() {
document.addEventListener("external-ad:track", function (event) {
var detail = event && event.detail ? event.detail : {};
trackExternalAd(detail.element, stringValue(detail.eventType));
});
}
function trackAdvertising(node, eventType) {
if (!node || ["show", "click", "close"].indexOf(eventType) < 0) return;
invoke("advertising", {
event_type: eventType,
advertising_key: stringValue(node.getAttribute("data-advertising-key"), "dialog_ad"),
advertising_name: stringValue(node.getAttribute("data-advertising-name"), "弹窗广告"),
advertising_id: stringValue(node.getAttribute("data-advertising-id") || node.getAttribute("data-dialog-ad-id")),
});
}
function bindAdvertisingEvents() {
document.addEventListener("dialog-ad:track", function (event) {
var detail = event && event.detail ? event.detail : {};
var node = detail.element;
if (!node || !node.getAttribute) return;
trackAdvertising(node, stringValue(detail.eventType));
});
}
function dramaRoot() {
return document.querySelector("[data-track-drama]");
}
function dramaContext(root) {
root = root || dramaRoot();
if (!root) return null;
var data = root.dataset || {};
var dramaID = stringValue(data.dramaId);
var videoID = stringValue(data.videoId);
if (!dramaID || !videoID) return null;
return {
media_id: stringValue(data.mediaId),
drama_id: dramaID,
drama_title: stringValue(data.dramaTitle, "未命名短剧"),
episode: Math.max(1, integer(data.episode, 1)),
total_episode: Math.max(1, integer(data.totalEpisode, 1)),
video_id: videoID,
drama_type_id: stringValue(data.dramaTypeId, "default"),
drama_type_name: stringValue(data.dramaTypeName, "默认分类"),
drama_content_type: "short_drama",
recommend_trace_id: stringValue(data.recommendTraceId),
drama_tag_key: stringValue(data.dramaTagKey, "default"),
drama_tag_name: stringValue(data.dramaTagName, "默认标签"),
drama_duration: Math.max(0, integer(data.dramaDuration, 0)),
};
}
var dramaBehaviorNames = {
ep_view: "展示",
ep_play: "播放",
ep_pause: "暂停",
ep_forward: "快进",
ep_rewind: "快退",
ep_complete: "播放完成",
ep_share: "分享",
};
function videoPlaybackState(video) {
if (!video) return null;
if (!video.__trackerPlaybackState) {
video.__trackerPlaybackState = {
startedAt: 0,
elapsedMs: 0,
seekFrom: null,
lastPosition: Number(video.currentTime) || 0,
};
}
return video.__trackerPlaybackState;
}
function beginPlayback(video) {
var playback = videoPlaybackState(video);
if (playback && !playback.startedAt) playback.startedAt = Date.now();
}
function settlePlayback(video) {
var playback = videoPlaybackState(video);
if (!playback || !playback.startedAt) return;
playback.elapsedMs += Math.max(0, Date.now() - playback.startedAt);
playback.startedAt = 0;
}
function playbackSeconds(video, keepDuration) {
var playback = videoPlaybackState(video);
if (!playback) return 0;
var now = Date.now();
var activeMs = playback.startedAt ? Math.max(0, now - playback.startedAt) : 0;
var seconds = Math.max(0, Math.round((playback.elapsedMs + activeMs) / 1000));
if (!keepDuration) {
playback.elapsedMs = 0;
if (playback.startedAt) playback.startedAt = now;
}
return seconds;
}
function videoMetrics(video, context, keepDuration) {
var duration = video && isFinite(video.duration) ? Math.round(video.duration) : context.drama_duration;
var current = video && isFinite(video.currentTime) ? Math.round(video.currentTime) : 0;
return {
duration: Math.max(0, duration),
current: Math.max(0, current),
played: playbackSeconds(video, keepDuration),
progress: duration > 0 ? Math.round(clamp(current / duration * 100, 0, 100)) : 0,
speed: Math.max(1, Math.round(((video && video.playbackRate) || 1) * 100)),
};
}
function trackDramaEvent(behaviorKey, video, options) {
var context = dramaContext();
if (!context) return;
options = options || {};
var metrics = videoMetrics(video, context, options.keepPlaybackDuration === true);
context.drama_duration = metrics.duration;
context.play_duration = metrics.played;
context.play_progress = options.forceProgress != null
? Math.round(clamp(options.forceProgress, 0, 100))
: behaviorKey === "ep_complete" ? 100 : metrics.progress;
context.play_speed = metrics.speed;
context.play_behavior_key = behaviorKey;
context.play_behavior_name = dramaBehaviorNames[behaviorKey] || behaviorKey;
return invoke("dramaEvent", context);
}
function trackDramaStatusChange(targetEpisode, targetVideoID, behaviorOverride) {
var context = dramaContext();
if (!context) return;
var nextEpisode = Math.max(1, integer(targetEpisode, context.episode));
var videoID = stringValue(targetVideoID, context.video_id);
var behavior = stringValue(behaviorOverride);
var names = {
ep_auto_play: "自动播放",
ep_previous: "上一集",
ep_next: "下一集",
ep_switch: "切换剧集",
ep_unlock: "解锁",
};
if (!behavior) {
if (nextEpisode === context.episode && videoID === context.video_id) return;
behavior = "ep_switch";
if (nextEpisode === context.episode + 1) behavior = "ep_next";
else if (nextEpisode === context.episode - 1) behavior = "ep_previous";
}
if (!names[behavior]) return;
return invoke("dramaStatusChange", {
media_id: context.media_id,
drama_id: context.drama_id,
drama_title: context.drama_title,
episode: nextEpisode,
video_id: videoID,
pre_episode: context.episode,
drama_behavior_key: behavior,
drama_behavior_name: names[behavior],
});
}
function trackDramaUnlock() {
var context = dramaContext();
if (!context) return Promise.resolve({ sent: false, ok: false, reason: "no_drama_context" });
return trackDramaStatusChange(context.episode, context.video_id, "ep_unlock");
}
function isMainDramaVideo(root, video) {
var mode = video && video.__trackerDramaMode;
if (!mode && root) {
mode = root.getAttribute("data-play-mode") === "preview" ? "preview" : "main";
}
return !!(
root &&
video &&
video.tagName === "VIDEO" &&
root.contains(video) &&
mode !== "preview"
);
}
function rememberDramaVideoMode(root, video) {
if (!root || !video || video.tagName !== "VIDEO" || !root.contains(video)) return;
video.__trackerDramaMode = root.getAttribute("data-play-mode") === "preview" ? "preview" : "main";
}
function prepareDramaModeChange(nextMode) {
var root = dramaRoot();
var video = root && root.querySelector("video");
if (!root || !video) return;
var previousMode = video.__trackerDramaMode ||
(root.getAttribute("data-play-mode") === "preview" ? "preview" : "main");
nextMode = nextMode === "preview" ? "preview" : "main";
if (previousMode === nextMode) return;
if (previousMode === "main") {
var playback = video.__trackerPlaybackState;
var wasPlaying = !!(playback && (playback.startedAt || playback.elapsedMs > 0));
settlePlayback(video);
if (wasPlaying) trackDramaEvent("ep_pause", video);
}
video.__trackerPlaybackState = null;
video.__trackerDramaMode = nextMode;
}
function bindDramaEvents() {
var root = dramaRoot();
if (!root) return;
if (!state.dramaViewSent) {
state.dramaViewSent = true;
trackDramaEvent("ep_view", null);
}
document.addEventListener("play", function (event) {
rememberDramaVideoMode(root, event.target);
if (isMainDramaVideo(root, event.target)) {
trackDramaEvent("ep_play", event.target);
}
}, true);
document.addEventListener("playing", function (event) {
rememberDramaVideoMode(root, event.target);
if (isMainDramaVideo(root, event.target)) beginPlayback(event.target);
}, true);
document.addEventListener("loadedmetadata", function (event) {
rememberDramaVideoMode(root, event.target);
}, true);
document.addEventListener("waiting", function (event) {
if (isMainDramaVideo(root, event.target)) settlePlayback(event.target);
}, true);
document.addEventListener("pause", function (event) {
if (isMainDramaVideo(root, event.target) && !event.target.ended) {
var playback = event.target.__trackerPlaybackState;
var wasPlaying = !!(playback && (playback.startedAt || playback.elapsedMs > 0));
settlePlayback(event.target);
if (event.target.seeking || !wasPlaying) return;
trackDramaEvent("ep_pause", event.target);
}
}, true);
document.addEventListener("timeupdate", function (event) {
if (!isMainDramaVideo(root, event.target) || event.target.seeking) return;
videoPlaybackState(event.target).lastPosition = Number(event.target.currentTime) || 0;
}, true);
document.addEventListener("seeking", function (event) {
if (!isMainDramaVideo(root, event.target)) return;
var playback = videoPlaybackState(event.target);
if (playback.seekFrom != null) return;
settlePlayback(event.target);
playback.seekFrom = playback.lastPosition;
}, true);
document.addEventListener("seeked", function (event) {
if (!isMainDramaVideo(root, event.target)) return;
var playback = videoPlaybackState(event.target);
if (playback.seekFrom == null) return;
var from = playback.seekFrom;
var target = Number(event.target.currentTime) || 0;
playback.seekFrom = null;
playback.lastPosition = target;
if (Math.abs(target - from) >= 0.5) {
trackDramaEvent(target > from ? "ep_forward" : "ep_rewind", event.target);
}
if (!event.target.paused && !event.target.ended) beginPlayback(event.target);
}, true);
document.addEventListener("ended", function (event) {
if (isMainDramaVideo(root, event.target)) {
settlePlayback(event.target);
trackDramaEvent("ep_complete", event.target, { keepPlaybackDuration: true });
trackDramaEvent("ep_play", event.target, { forceProgress: 100 });
}
}, true);
function reportPendingDramaProgress() {
var video = root.querySelector("video");
if (!isMainDramaVideo(root, video)) return false;
var playback = video.__trackerPlaybackState;
if (!playback || (!playback.startedAt && playback.elapsedMs <= 0)) return false;
var pendingMs = playback.elapsedMs +
(playback.startedAt ? Math.max(0, Date.now() - playback.startedAt) : 0);
if (pendingMs < 500) return false;
settlePlayback(video);
trackDramaEvent("ep_play", video);
return true;
}
function flushSDKAfterEnqueue() {
if (!global.WebSDK || typeof global.WebSDK.flush !== "function") return;
function flush() {
Promise.resolve(global.WebSDK.flush()).catch(logError);
}
Promise.resolve().then(function () {
return Promise.resolve();
}).then(flush);
global.setTimeout(flush, 50);
global.setTimeout(flush, 200);
}
document.addEventListener("visibilitychange", function () {
var video = root.querySelector("video");
if (document.visibilityState === "hidden") {
if (reportPendingDramaProgress()) flushSDKAfterEnqueue();
if (isMainDramaVideo(root, video) && !video.paused && !video.ended) beginPlayback(video);
return;
}
if (document.visibilityState === "visible" && isMainDramaVideo(root, video) &&
!video.paused && !video.ended) {
beginPlayback(video);
}
});
global.addEventListener("beforeunload", function () {
if (reportPendingDramaProgress()) flushSDKAfterEnqueue();
});
global.addEventListener("pagehide", function () {
if (reportPendingDramaProgress()) flushSDKAfterEnqueue();
});
}
function bindSearchEvents() {
var container = document.querySelector("[data-search-results]");
if (!container) return;
var keyword = stringValue(container.getAttribute("data-search-keyword"));
if (!keyword) return;
var grid = container.querySelector("[data-search-grid]");
var cards = grid ? Array.prototype.slice.call(grid.querySelectorAll("[data-search-item-id]")) : [];
var resultCount = Math.max(0, integer(container.getAttribute("data-search-result-count"), cards.length));
var pageNum = Math.max(1, integer(container.getAttribute("data-search-page"), 1));
var pageSize = Math.max(1, integer(container.getAttribute("data-search-page-size"), cards.length || 1));
if (pageNum === 1) {
invoke("keywordSearch", {
keyword: keyword,
search_result_count: resultCount,
search_content_type: "",
search_trace_id: "",
search_id: "",
});
}
if (!grid) return;
grid.addEventListener("click", function (event) {
var target = event.target && event.target.closest ? event.target : null;
var resultLink = target && target.closest("[data-search-result-click]");
var card = resultLink && resultLink.closest("[data-search-item-id]");
if (!card || !grid.contains(card)) return;
var position = cards.indexOf(card);
if (position < 0) return;
invoke("keywordClick", {
keyword: keyword,
click_item_id: stringValue(card.getAttribute("data-search-item-id")),
click_item_type_key: "short_drama",
click_item_type_name: "短剧",
click_position: (pageNum - 1) * pageSize + position + 1,
search_trace_id: "",
});
}, true);
}
function bindClicks() {
document.addEventListener("click", function (event) {
var target = event.target && event.target.closest ? event.target : null;
if (!target) return;
var popupClose = target.closest(
"[data-dialog-ad-close],[data-ad-close],[data-register-close]," +
"[data-download-close],[data-login-close],[data-recharge-close]," +
"[data-play-coin-close],[data-setting-password-close],[data-drawer-close]"
);
if (!popupClose) trackPageClick(event);
var switcher = target.closest("[data-drama-switch]");
if (switcher) {
trackDramaStatusChange(switcher.getAttribute("data-target-episode"), switcher.getAttribute("data-target-video-id"));
}
if (target.closest("[data-share-page]") && dramaRoot()) {
var root = dramaRoot();
var video = root.querySelector("video");
trackDramaEvent("ep_share", isMainDramaVideo(root, video) ? video : null);
}
var ad = target.closest('[data-track-ad="external"][data-ad-clickable="1"][data-ad-id]');
if (ad) {
trackExternalAd(ad, "click");
}
trackNavigation(target.closest("a[data-navigation-key],a[data-nav-key]"));
}, true);
}
function onReady() {
whenDOMReady(function () {
Promise.resolve(loadCommonFields()).then(function () {
trackPageView();
});
bindClicks();
bindAds();
bindExternalAdEvents();
bindAdvertisingEvents();
bindDramaEvents();
bindSearchEvents();
try {
global.dispatchEvent(new CustomEvent("tracker:ready", { detail: state.page }));
} catch (error) {}
});
}
var COMMON_FIELD_KEYS = [
"sid",
"device_id",
"device",
"device_fingerprint",
"fp_version",
];
var __commonFields = null;
var __commonFieldsReady = null;
function emptyCommonFields() {
var out = {};
for (var i = 0; i < COMMON_FIELD_KEYS.length; i++) {
out[COMMON_FIELD_KEYS[i]] = "";
}
return out;
}
function normalizeCommonFields(fields) {
var out = emptyCommonFields();
fields = fields || {};
for (var i = 0; i < COMMON_FIELD_KEYS.length; i++) {
var k = COMMON_FIELD_KEYS[i];
if (fields[k] != null) out[k] = String(fields[k]);
}
return out;
}
function publishCommonFields(fields) {
__commonFields = normalizeCommonFields(fields);
try {
global.__trackerCommonFields = __commonFields;
} catch (e) {}
try {
if (typeof CustomEvent === "function") {
global.dispatchEvent(
new CustomEvent("tracker:common-fields", { detail: __commonFields })
);
}
} catch (e) {}
return __commonFields;
}
function loadCommonFields() {
if (__commonFieldsReady) return __commonFieldsReady;
__commonFieldsReady = new Promise(function (resolve) {
function finish(fields) {
resolve(publishCommonFields(fields));
}
if (!global.WebSDK || typeof global.WebSDK.getCommonFields !== "function") {
finish(emptyCommonFields());
return;
}
try {
Promise.resolve(global.WebSDK.getCommonFields(COMMON_FIELD_KEYS.slice()))
.then(function (fields) {
finish(fields || emptyCommonFields());
})
.catch(function (err) {
logError(err);
finish(emptyCommonFields());
});
} catch (err) {
logError(err);
finish(emptyCommonFields());
}
});
return __commonFieldsReady;
}
function getCommonFieldsCached(keys) {
return loadCommonFields().then(function (all) {
if (!Array.isArray(keys) || !keys.length) return all;
var out = {};
for (var i = 0; i < keys.length; i++) {
var k = String(keys[i] || "").trim();
if (k) out[k] = Object.prototype.hasOwnProperty.call(all, k) ? all[k] : "";
}
return out;
});
}
function getCommonFieldCached(key) {
var k = String(key || "").trim();
if (!k) return Promise.resolve("");
return getCommonFieldsCached([k]).then(function (m) {
return m[k] || "";
});
}
function encodeSDKCommonHeader(fields) {
var payload = normalizeCommonFields(fields);
try {
return btoa(unescape(encodeURIComponent(JSON.stringify(payload))));
} catch (e) {
logError(e);
return "";
}
}
function getSDKCommonHeader() {
return loadCommonFields().then(function (fields) {
return encodeSDKCommonHeader(fields);
});
}
global.trackerInit = init;
global.tracker = {
init: init,
pageView: trackPageView,
dramaEvent: trackDramaEvent,
dramaStatusChange: trackDramaStatusChange,
dramaUnlock: trackDramaUnlock,
prepareDramaModeChange: prepareDramaModeChange,
advertising: function (payload) { return invoke("advertising", payload || {}); },
externalAd: trackExternalAd,
getDramaContext: function () {
var context = dramaContext();
return context ? Object.assign({}, context) : {};
},
keywordSearch: function (payload) { return invoke("keywordSearch", payload || {}); },
keywordClick: function (payload) { return invoke("keywordClick", payload || {}); },
getCommonFields: getCommonFieldsCached,
getCommonField: getCommonFieldCached,
getCommonFieldsSync: function () { return __commonFields; },
encodeSDKCommonHeader: encodeSDKCommonHeader,
getSDKCommonHeader: getSDKCommonHeader,
flush: function () {
if (!global.WebSDK || typeof global.WebSDK.flush !== "function") return Promise.resolve();
return Promise.resolve(global.WebSDK.flush()).catch(logError);
},
setUid: function (uid) {
if (global.WebSDK && global.WebSDK.setUid) global.WebSDK.setUid(normalizeUid(uid));
},
setChannel: function (channel) {
if (global.WebSDK && global.WebSDK.setChannel) global.WebSDK.setChannel(currentChannel(channel));
},
setContext: function (context) {
if (global.WebSDK && global.WebSDK.setContext) global.WebSDK.setContext(context || {});
},
};
init(global.__TRACKER_BOOT__ || {});
})(window);