调整UI
@@ -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; }
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
|
Before Width: | Height: | Size: 341 KiB |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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) {
|
||||
|
||||
@@ -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('操作失败,请稍后重试');
|
||||
}
|
||||
});
|
||||
});
|
||||
})();
|
||||
@@ -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();
|
||||
})();
|
||||
@@ -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;
|
||||
})();
|
||||
|
||||
@@ -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();
|
||||
})();
|
||||
@@ -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();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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");
|
||||
});
|
||||
});
|
||||
}
|
||||
})();
|
||||
@@ -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) {}
|
||||
})();
|
||||
@@ -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]");
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
})();
|
||||
|
||||
@@ -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");
|
||||
});
|
||||
});
|
||||
}
|
||||
})();
|
||||
@@ -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) {}
|
||||
})();
|
||||
@@ -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);
|
||||
})();
|
||||
@@ -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();
|
||||
})();
|
||||
@@ -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 = '确认修改';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
})();
|
||||
@@ -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);
|
||||