Compare commits

..

No commits in common. 'developing' and 'master' have entirely different histories.

@ -1 +0,0 @@
dbfX2r5hEeBPV3qp

@ -1,21 +0,0 @@
all: frontend build tar
image: frontend docker-build save-image
TAG=v1.0.5
frontend:
cd ui && yarn run build
tar:
tar -czf cls-h5.$(TAG).tar.gz cls config
docker-build:
docker build -t k8s.dingshudata.com/cmcc-panoramic-application:$(TAG) .
save-image:
docker save -o cmcc.tar k8s.dingshudata.com/cmcc-panoramic-application
build:export CGO_ENABLED=0
build:export GOOS=linux
build:export GOARCH=amd64
build:
go build -ldflags -s -tags="jsoniter nomsgpack" .

BIN
cls

Binary file not shown.

Binary file not shown.

@ -1,4 +1,4 @@
package ui
package cls
import "embed"

File diff suppressed because it is too large Load Diff

@ -1,14 +1,14 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { PreloadAllModules, RouterModule, Routes } from '@angular/router';
const routes: Routes = [
// {
// path: '',
// redirectTo: 'wechat_pay-callback',
// pathMatch: 'full'
// },
{
path: 'home',
path: '',
redirectTo: 'home',
pathMatch: 'full'
},
{
path:'home',
loadChildren: () => import('./home/home.module').then(m => m.HomePageModule)
},
{
@ -16,13 +16,11 @@ const routes: Routes = [
loadChildren: () => import('./mine/mine.module').then(m => m.MinePageModule)
},
];
@NgModule({
imports: [
RouterModule.forRoot(routes)
RouterModule.forRoot(routes, { preloadingStrategy: PreloadAllModules })
],
exports: [RouterModule]
})

@ -1,7 +1,7 @@
<ion-app>
<ion-router-outlet></ion-router-outlet>
<ion-tabs>
<ion-tab-bar slot="bottom" *ngIf="showTabs">
<ion-tab-bar slot="bottom">
<ion-tab-button tab="home">
<ion-icon name="home"></ion-icon>
<ion-label>首页</ion-label>

@ -0,0 +1,21 @@
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { TestBed } from '@angular/core/testing';
import { AppComponent } from './app.component';
describe('AppComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [AppComponent],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
}).compileComponents();
});
it('should create the app', () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.componentInstance;
expect(app).toBeTruthy();
});
});

@ -0,0 +1,11 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: 'app.component.html',
styleUrls: ['app.component.scss'],
standalone: false,
})
export class AppComponent {
constructor() {}
}

@ -8,8 +8,6 @@ import { GoCaptchaModule } from 'go-captcha-angular';
import { AppComponent } from './app.component';
import { AppRoutingModule } from './app-routing.module';
import {AuthInterceptor} from "./core/interceptors/auth.interceptor";
import {CheckInterceptor} from "./core/interceptors/check.interceptor";
import {ErrorInterceptor} from "./core/interceptors/error.interceptor";
@NgModule({
declarations: [
@ -25,19 +23,15 @@ import {ErrorInterceptor} from "./core/interceptors/error.interceptor";
providers: [
{ provide: RouteReuseStrategy, useClass: IonicRouteStrategy },
{
provide: HTTP_INTERCEPTORS,
useClass: AuthInterceptor,
multi: true,
},
{
provide: HTTP_INTERCEPTORS,
useClass: ErrorInterceptor,
multi: true,
},
],
bootstrap: [AppComponent],
schemas: [CUSTOM_ELEMENTS_SCHEMA]
})
export class AppModule { }
// {
// provide: HTTP_INTERCEPTORS,
// useClass: AuthInterceptor,
// multi: true,
// },

@ -0,0 +1,61 @@
import { Injectable } from '@angular/core';
import {
HttpRequest,
HttpHandler,
HttpEvent,
HttpInterceptor,
HttpResponse
} from '@angular/common/http';
import { Observable, tap } from 'rxjs';
import { AuthConfigConsts, tokenNotExpired } from "../../mine/auth.jwt";
@Injectable()
export class AuthInterceptor implements HttpInterceptor {
constructor() {}
intercept(req: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
const token = sessionStorage.getItem('token');
console.log('当前 token:', token);
console.log('请求:', req.url);
let authHeader = req.headers;
if (token != null && tokenNotExpired()) {
const bearer = AuthConfigConsts.HEADER_PREFIX_BEARER + token;
authHeader = authHeader.set(AuthConfigConsts.DEFAULT_HEADER_NAME, bearer);
}
const authReq = req.clone({headers: authHeader});
return next.handle(authReq).pipe(
tap((event) => {
// 如果是 HTTP 响应
if (event instanceof HttpResponse) {
// 从响应头中获取 Authorization token
const authToken = event.headers.get('Authorization');
if (authToken && (!token || token !== authToken.replace(AuthConfigConsts.HEADER_PREFIX_BEARER, ''))) {
console.log('从响应中获取到新的 token:', authToken);
// 去掉 Bearer 前缀
const newToken = authToken.replace(AuthConfigConsts.HEADER_PREFIX_BEARER, '');
// 保存到 sessionStorage
sessionStorage.setItem('token', newToken);
}
}
})
);
}
private isGuestToken(token: string): boolean {
try {
// 解析 JWT token
const payload = JSON.parse(atob(token.split('.')[1]));
const isGuest = !payload.phone;
console.log('Token payload:', payload); // 添加日志
return isGuest;
} catch (error) {
console.error('Error parsing token:', error); // 添加错误日志
return true; // 解析失败时默认为游客
}
}
}

@ -2,9 +2,9 @@ import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { IonicModule } from '@ionic/angular';
import { ArticleDetailPage } from './article-detail.page';
import { ImagePreviewComponent } from '../component/image-preview/image-preview.component';
import {ArticleDetailPageRoutingModule} from "./article-detail-routing.module";
import {HomePageModule} from "../home.module";
import {ImagePreviewComponent} from "./image-preview/image-preview.component";
@NgModule({
imports: [

@ -0,0 +1,31 @@
<ion-header [translucent]="true">
<ion-toolbar>
<ion-buttons slot="start">
<ion-back-button defaultHref="/home"></ion-back-button>
</ion-buttons>
<ion-title>文章详情</ion-title>
</ion-toolbar>
</ion-header>
<ion-content [fullscreen]="true" class="ion-padding">
<!-- 文章标题 -->
<div class="article-title">
{{ article?.title }}
</div>
<!-- 发布时间 -->
<div class="article-meta">
<span class="date">{{ article?.releaseDate | date:'yyyy-MM-dd HH:mm' }}</span>
<span class="weekday">{{ weekday }}</span>
</div>
<!-- 文章简介 -->
<div class="article-brief">
<p>{{article?.brief}}</p>
</div>
<!-- 文章内容 -->
<div class="article-content" [innerHTML]="article!.content"></div>
<app-lc-fixed-bar></app-lc-fixed-bar>
</ion-content>

@ -3,9 +3,6 @@
--background: #fff;
}
}
ion-back-button {
--color: #333333;
}
.article-title {
font-size: 24px;
@ -60,53 +57,3 @@ ion-back-button {
}
}
}
.stocks-content {
display: flex;
flex-direction: row;
align-content: center;
margin-bottom: 24px;
.sc-title {
font-size: 12px;
color: #8B8D93;
font-weight: 500;
margin-right: 10px;
}
.sc-content {
font-size: 12px;
color: #1D1E22;
font-weight: 500;
.change {
margin-left: 4px;
font-weight: 500;
&.up {
color: #F93034;
}
&.down {
color: #07A50B;
}
}
}
}
.public-content {
display: flex;
flex-direction: row;
align-content: center;
margin-bottom: 24px;
span{
opacity: 0.75;
font-size: 12px;
color: #8B8D93;
font-weight: 400;
}
.pb-title {
width: 60px;
}
.pb-content {
}
}

@ -3,8 +3,7 @@ import { NavController } from '@ionic/angular';
import { Router } from '@angular/router';
import { ModalController } from '@ionic/angular';
import { Article } from "../../shared/model/article";
import {ImagePreviewComponent} from "./image-preview/image-preview.component";
import { HttpClient } from '@angular/common/http';
import { ImagePreviewComponent } from "../component/image-preview/image-preview.component";
@Component({
selector: 'app-article-detail',
@ -15,13 +14,11 @@ import { HttpClient } from '@angular/common/http';
export class ArticleDetailPage implements OnInit, AfterViewInit {
article?: Article;
weekday = "";
stockChanges: { [key: string]: number } = {};
constructor(
private navCtrl: NavController,
private router: Router,
private modalCtrl: ModalController,
private http: HttpClient
private modalCtrl: ModalController
) {
// 获取导航传递的数据
const navigation = this.router.getCurrentNavigation();
@ -29,7 +26,6 @@ export class ArticleDetailPage implements OnInit, AfterViewInit {
if (article) {
this.article = article;
this.addWeekday();
this.getStockChanges();
}
}
@ -81,42 +77,11 @@ export class ArticleDetailPage implements OnInit, AfterViewInit {
this.modalCtrl.create({
component: ImagePreviewComponent,
componentProps: {
imageUrl:imageUrl
imageUrl
},
cssClass: 'image-preview-modal'
}).then(modal => {
modal.present();
});
}
private getStockChanges() {
if (!this.article?.stocks) return;
const stockCodes = this.article.stocks.split(',').map(code => code.trim());
stockCodes.forEach(code => {
if (code) {
// 调用接口时使用纯数字代码
const numericCode = code.replace(/^(sz|sh)/, '');
this.http.get(`http://api.mairui.club/hsrl/ssjy/${numericCode}/13988152-0AB6-41A9-B2CC-DC9B50250113`)
.subscribe((data: any) => {
if (data && data.pc) {
this.stockChanges[code] = data.pc;
}
});
}
});
}
getStockChangeClass(code: string): string {
const change = this.stockChanges[code];
if (change > 0) return 'up';
if (change < 0) return 'down';
return '';
}
formatChange(change: number): string {
if (change > 0) return `+${change}%`;
return `${change}%`;
}
}
}

@ -0,0 +1,39 @@
<ion-segment (ionChange)="ionChange($event)">
<ion-segment-button value="new" content-id="new">
<ion-label>最新</ion-label>
</ion-segment-button>
<ion-segment-button value="unlock" content-id="unlock">
<ion-label>已解锁</ion-label>
</ion-segment-button>
<ion-segment-button value="free" content-id="free">
<ion-label>免费试读</ion-label>
</ion-segment-button>
</ion-segment>
<ion-segment-view>
<ion-segment-content id="new">
<ion-list>
<app-article-item [article]="item" *ngFor="let item of data" ></app-article-item>
</ion-list>
</ion-segment-content>
<ion-segment-content id="unlock">
<ion-list>
<app-article-item [article]="item" *ngFor="let item of data" ></app-article-item>
</ion-list>
</ion-segment-content>
<ion-segment-content id="free">
<ion-list>
<app-article-item [article]="item" *ngFor="let item of data" ></app-article-item>
</ion-list>
</ion-segment-content>
</ion-segment-view>
<ion-infinite-scroll (ionInfinite)="onIonInfinite($event)">
<ion-infinite-scroll-content
loadingText="加载中..."
loadingSpinner="bubbles">
</ion-infinite-scroll-content>
</ion-infinite-scroll>

@ -71,24 +71,3 @@ ion-segment {
transform: scaleX(1);
}
}
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 40px 20px;
text-align: center;
ion-icon {
font-size: 48px;
color: #999;
margin-bottom: 16px;
}
p {
color: #999;
font-size: 14px;
margin: 0;
}
}

@ -0,0 +1,91 @@
import {ChangeDetectorRef, Component, Input, OnInit} from '@angular/core';
import {Article} from "../../../shared/model/article";
import {InfiniteScrollCustomEvent} from "@ionic/angular";
import {HomeService} from "../../home.service";
@Component({
selector: 'app-article-content',
templateUrl: './article-content.component.html',
styleUrls: ['./article-content.component.scss'],
standalone:false,
})
export class ArticleContentComponent implements OnInit {
searchParams: { [param: string]: any } = {
page: 0,
size: 10,
};
hasMore: boolean = true;
@Input() className:string = ""
data:Article[] = []
currentSelect = "new"
constructor(private homeService:HomeService,
private cdr:ChangeDetectorRef) { }
ngOnInit() {
this.getData();
}
ionChange(e:any){
this.searchParams['page'] = 0
this.data = []
this.currentSelect = e.detail.value
this.getData()
}
getData() {
switch (this.currentSelect){
case "new":
return this.getNewData()
case "unlock":
return this.getUnLockData()
case "free":
return this.getFreeData()
}
}
getNewData() {
this.homeService.list(this.searchParams).subscribe(res=>{
if(res.items.length > 0) {
this.data = this.data.concat(res.items)
this.searchParams['page'] = this.searchParams['page']+1;
this.hasMore = res.items.length === this.searchParams['size'];
this.cdr.detectChanges();
}
})
}
getUnLockData() {
this.homeService.unlockList(this.searchParams).subscribe(res=>{
if(res.items.length > 0) {
this.data = this.data.concat(res.items)
this.searchParams['page'] = this.searchParams['page']+1;
this.hasMore = res.items.length === this.searchParams['size'];
this.cdr.detectChanges();
}
})
}
getFreeData() {
this.homeService.freeList(this.searchParams).subscribe(res=>{
if(res.items.length > 0) {
this.data = this.data.concat(res.items)
this.searchParams['page'] = this.searchParams['page']+1;
this.hasMore = res.items.length === this.searchParams['size'];
this.cdr.detectChanges();
}
})
}
onIonInfinite(event: InfiniteScrollCustomEvent) {
if (this.hasMore) {
this.getData();
setTimeout(() => {
event.target.complete();
}, 1000);
} else {
event.target.disabled = true;
}
}
}

@ -1,4 +1,4 @@
<div class="article-item" [class.locked]="!article.unlock" (click)="detail()">
<div class="article-item" [class.locked]="article.lock" (click)="detail()">
<div class="article-content">
<div class="article-header">
<div class="title-row">
@ -11,8 +11,8 @@
<span class="tag growth-board" *ngIf="article.growthBoard > 0">创业板 <span class="count" style="color:#077BE9">{{article.growthBoard}}只</span></span>
</div>
</div>
<div class="lock-mask" *ngIf="!article.unlock">
<button class="unlock-button" (click)="onUnlock()">立即解锁</button>
<button class="unlock-button" (click)="unlockArticle($event)" [class.noCount]="getFreeReadCount()<1" [disabled]="getFreeReadCount()<1">赠送({{getFreeReadCount()}}次)</button>
<div class="lock-mask" *ngIf="article.lock">
<button class="unlock-button">立即解锁</button>
<button class="unlock-button" [class.noCount]="freeReadCount==0" [disabled]="freeReadCount==0">赠送({{freeReadCount}}次)</button>
</div>
</div>

@ -3,8 +3,6 @@
background: #fff;
position: relative;
overflow: hidden;
margin-bottom: 20px;
border-bottom: #8B8D93 1px solid ;
}
.article-content {

@ -0,0 +1,73 @@
import {Component, Input, OnInit, Output, EventEmitter} from '@angular/core';
import {Article} from "../../../shared/model/article";
import {NavController} from "@ionic/angular";
import {getUser} from "../../../mine/mine.service";
@Component({
selector: 'app-article-item',
templateUrl: './article-item.component.html',
styleUrls: ['./article-item.component.scss'],
standalone:false,
})
export class ArticleItemComponent implements OnInit {
@Input() article!:Article
@Input() lock:boolean = true;
freeReadCount:number=0;
constructor(private navCtrl: NavController) {
getUser().subscribe((res)=>{
if(res.username != "") {
this.freeReadCount = parseInt(localStorage.getItem("giftCount")!)
}
})
}
ngOnInit() {}
onUnlock() {
}
detail(){
console.log("跳转")
if(!this.article.lock && this.article.stocks != "" && this.article.content != "") {
console.log("跳转")
this.navCtrl.navigateForward('/home/article-detail', {
state:{article:this.article}
});
}
}
formatReleaseTime(releaseDate: string, releaseTime: string): string {
const date = new Date(releaseDate);
const time = releaseTime || '00:00:00';
const today = new Date();
const yesterday = new Date(today);
yesterday.setDate(yesterday.getDate() - 1);
const dayBeforeYesterday = new Date(today);
dayBeforeYesterday.setDate(dayBeforeYesterday.getDate() - 2);
// 格式化日期为 YYYY-MM-DD
const formatDate = (d: Date) => {
return d.toISOString().split('T')[0];
};
// 格式化日期为 MM-DD
const formatShortDate = (d: Date) => {
return `${d.getMonth() + 1}${d.getDate()}`;
};
const dateStr = formatDate(date);
const todayStr = formatDate(today);
const yesterdayStr = formatDate(yesterday);
const dayBeforeYesterdayStr = formatDate(dayBeforeYesterday);
if (dateStr === todayStr) {
return time;
} else if (dateStr === yesterdayStr) {
return `昨天 ${time}`;
} else if (dateStr === dayBeforeYesterdayStr) {
return `前天 ${time}`;
} else {
return `${formatShortDate(date)} ${time}`;
}
}
}

@ -4,13 +4,13 @@
<div class="swiper-slide">
<ion-grid>
<ion-row>
<ion-col size="3" *ngFor="let icon of firstPageIcons" >
<ion-col size="3" *ngFor="let icon of firstPageIcons" (click)="columnDetail(icon.name)">
<div class="icon-item">
<div class="icon-circle" [ngClass]="icon.image.replace('.png', '')">
<img (click)="columnDetail(icon.name)" [src]="'assets/home/' + icon.image" [alt]="icon.name">
<img [src]="'assets/home/' + icon.image" [alt]="icon.name">
<span class="hot-tag" *ngIf="icon.remark!=''"> <img [src]="'assets/home/' + icon.remark" ></span>
</div>
<span class="icon-name" (click)="columnDetail(icon.name)">{{icon.name}}</span>
<span class="icon-name">{{icon.name}}</span>
</div>
</ion-col>
</ion-row>

@ -0,0 +1,18 @@
import { Component, Input } from '@angular/core';
import { ModalController } from '@ionic/angular';
@Component({
selector: 'app-image-preview',
templateUrl:'./image-preview.component.html',
styleUrls: ['./image-preview.component.scss'],
standalone: false
})
export class ImagePreviewComponent {
@Input() imageUrl: string = '';
constructor(private modalCtrl: ModalController) {}
dismiss() {
this.modalCtrl.dismiss();
}
}

@ -14,20 +14,7 @@ const routes: Routes = [
{
path: 'article-detail',
loadChildren: () => import('./article-detail/article-detail.module').then( m => m.ArticleDetailModule)
},
{
path: 'column-buy',
loadChildren: () => import('./column-buy/column-buy.module').then( m => m.ColumnBuyPageModule)
},
{
path: 'article-buy',
loadChildren: () => import('./article-buy/article-buy.module').then( m => m.ArticleBuyPageModule)
},
{
path: 'confirm-order',
loadChildren: () => import('./confirm-order/confirm-order.module').then(m => m.ConfirmOrderPageModule)
}
];
@NgModule({

@ -8,8 +8,6 @@ import { HomeIconsComponent } from './component/home-icons/home-icons.component'
import { ArticleItemComponent } from "./component/article-item/article-item.component";
import { ArticleContentComponent } from "./component/article-content/article-content.component";
import {LcFixedBarComponent} from "./component/lc-fixed-bar/lc-fixed-bar.component";
import {MoreDiscountsComponent} from "./component/more-discounts/more-discounts.component";
import {CouponListComponent} from "./component/coupon-list/coupon-list.component";
@NgModule({
imports: [
@ -24,14 +22,11 @@ import {CouponListComponent} from "./component/coupon-list/coupon-list.component
HomeIconsComponent,
ArticleItemComponent,
ArticleContentComponent,
LcFixedBarComponent,
MoreDiscountsComponent,
CouponListComponent,
LcFixedBarComponent
],
exports: [
LcFixedBarComponent,
ArticleContentComponent,
CouponListComponent
ArticleContentComponent
],
schemas: [CUSTOM_ELEMENTS_SCHEMA]
})

@ -12,5 +12,6 @@
</div>
<app-home-icons></app-home-icons>
<app-article-content></app-article-content>
<app-lc-fixed-bar></app-lc-fixed-bar>
</ion-content>

@ -0,0 +1,29 @@
import {ChangeDetectorRef, Component, OnInit} from '@angular/core';
import {InfiniteScrollCustomEvent} from "@ionic/angular";
import {Article} from "../shared/model/article";
import {HomeService} from "./home.service";
import {getUser} from "../mine/mine.service";
@Component({
selector: 'app-home',
templateUrl: 'home.page.html',
styleUrls: ['home.page.scss'],
standalone: false,
})
export class HomePage implements OnInit{
constructor(private homeService:HomeService,
private cdr:ChangeDetectorRef) {
getUser().subscribe((res)=>{
console.log(res)
})
}
ngOnInit() {
}
}

@ -0,0 +1,35 @@
import { Injectable } from '@angular/core';
import {HttpClient} from "@angular/common/http";
import {map, Observable} from "rxjs";
import {Article} from "../shared/model/article";
import {extractData, Page} from "../shared/model/page";
import {HttpUtils} from "../shared/until/http.utils";
@Injectable({
providedIn: 'root'
})
export class HomeService {
constructor(private http:HttpClient) { }
list(searchParams:{[key:string]:string}):Observable<Page<Article>>{
return this.http.get('/api/article/all',{params:HttpUtils.getSearchParams(searchParams)}).pipe(
map(response => extractData<Article>(response)),
)
}
unlockList(searchParams:{[key:string]:string}):Observable<Page<Article>>{
return this.http.get('/api/article/unlock',{params:HttpUtils.getSearchParams(searchParams)}).pipe(
map(response => extractData<Article>(response)),
)
}
freeList(searchParams:{[key:string]:string}):Observable<Page<Article>>{
return this.http.get('/api/article/free',{params:HttpUtils.getSearchParams(searchParams)}).pipe(
map(response => extractData<Article>(response)),
)
}
getAllClass():Observable<string[]>{
return this.http.get<string[]>('/api/article/class')
}
getArticleDetail(uid:number):Observable<Article>{
return this.http.get<Article>(`/api/article/detail/${uid}`)
}
}

@ -1,21 +1,17 @@
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { SpecialColumnPage } from './special-column.page';
const routes: Routes = [
{
path: '',
component: SpecialColumnPage
}, {
path: 'column-describe',
loadChildren: () => import('./column-describe/column-describe.module').then( m => m.ColumnDescribePageModule)
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { SpecialColumnPage } from './special-column.page';
const routes: Routes = [
{
path: '',
component: SpecialColumnPage
}
];
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
})
export class SpecialColumnPageRoutingModule {}
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
})
export class SpecialColumnPageRoutingModule {}

@ -0,0 +1,39 @@
<ion-header [translucent]="true">
<ion-toolbar>
<ion-title>{{name}}</ion-title>
</ion-toolbar>
</ion-header>
<ion-content [fullscreen]="true">
<div class="column-header">
<div class="logo-area">
<img src="assets/home/lc_white.png" class="lc-logo" alt="路诚数据">
<img src="assets/home/vip.png" class="vip-logo" alt="VIP">
</div>
<p class="slogan">为高净值用户提供精选信息</p>
</div>
<div class="column-info">
<div class="title-row">
<h1>{{name}}</h1>
<div class="tag">简介</div>
</div>
<div class="stats-row">
<div class="stat-item">
<span class="number">19.2w</span>
<span class="label">人关注</span>
</div>
<div class="stat-item">
<span class="number">19.2w</span>
<span class="label">人购买</span>
</div>
</div>
<div class="intro-section">
<h2>这是消息标题</h2>
<p>这是消息内容这是消息内容这是消息内容这是消息内容这是消息内容这是消息内容这是消息内容这是消息内容这是消息内容这是消息内容这是消息内容这是消息内容</p>
</div>
</div>
<app-article-content></app-article-content>
<app-lc-fixed-bar></app-lc-fixed-bar>
</ion-content>

@ -74,19 +74,16 @@
h1 {
margin: 0;
font-size: 20px;
color: #1D1E22;
letter-spacing: 0;
font-weight: 900;
font-weight: 600;
color: #2F3033;
}
.tag {
height: 20px;
background: #1D1E22;
font-size: 11px;
color: rgba(255,255,255,0.80);
font-weight: 400;
background: #F5F5F5;
color: #666;
font-size: 12px;
padding: 2px 8px;
border-radius: 2px;
border-radius: 4px;
}
}
@ -99,13 +96,16 @@
display: flex;
align-items: center;
gap: 4px;
font-size: 11px;
color: #78787A;
font-weight: 400;
.number {
font-size: 14px;
color: #2F3033;
font-weight: 500;
}
.label {
font-size: 14px;
color: #666;
}
}
}
@ -131,68 +131,3 @@
}
}
}
.footer-toolbar {
--padding-start: 0;
--padding-end: 0;
--padding-top: 0;
--padding-bottom: 0;
--background: linear-gradient(90deg, #474D5D 0%, #333742 75%);
}
.fixed-bottom {
display: flex;
justify-content: space-between;
align-items: center;
height: 60px;
padding-left: 16px;
.price {
text-align: center;
display: flex;
flex-direction: column;
align-items: flex-start;
.p1, .p2 {
margin: 0;
line-height: 1.2;
}
.p1 {
font-size: 16px;
font-weight: 500;
color: #fff;
}
.p2 {
font-size: 14px;
color: rgba(255, 255, 255, 0.8);
margin-top: 2px;
}
}
.buy-btn {
background: linear-gradient(180deg, #F54545 0%, #DC2D2D 100%);
color: white;
border: none;
padding: 8px 24px;
font-size: 16px;
color: #FFFFFF;
text-align: center;
font-weight: 400;
cursor: pointer;
height: 60px;
width: 40%;
&:active {
background: darken(#e74c3c, 10%);
}
}
}
//
ion-content {
--padding-bottom: 50px;
}
ion-back-button {
--color: #333333;
}

@ -0,0 +1,26 @@
import { Component, OnInit } from '@angular/core';
import {ModalController, NavController} from "@ionic/angular";
import {Router} from "@angular/router";
@Component({
selector: 'app-special-column',
templateUrl: './special-column.page.html',
styleUrls: ['./special-column.page.scss'],
standalone:false,
})
export class SpecialColumnPage implements OnInit {
name:string = ""
constructor(private navCtrl: NavController,
private router: Router,
) {
const navigation = this.router.getCurrentNavigation();
const name = navigation?.extras?.state?.['name'];
if (name) {
this.name = name;
}
}
ngOnInit() {
}
}

@ -112,7 +112,7 @@ export class JwtHelper {
*/
export function tokenNotExpired(tokenName = AuthConfigConsts.DEFAULT_TOKEN_NAME, jwt?: string): boolean {
const token: string | null = jwt || localStorage.getItem(tokenName);
const token: string | null = jwt || sessionStorage.getItem(tokenName);
const jwtHelper = new JwtHelper();

@ -20,10 +20,12 @@ export class CaptchaModalComponent {
}
captchaEvents = {
confirm: (point: SlidePoint, clear: (fn: Function) => void) => {
console.log('用户滑动位置:', point);
if(point.x < this.x+5 && point.x >this.x-5 ) {
this.modalCtrl.dismiss(this.x)
}
clear(() => {
console.log('滑动验证失败,清除验证码');
});
}
};

@ -7,7 +7,7 @@
<!-- 登录方式切换 -->
<div class="login-type-switch">
<span [class.active]="loginType === 'sms'" (click)="loginType = 'sms'">验证码登录</span>
<!-- <span [class.active]="loginType === 'password'" (click)="loginType = 'password'">密码登录</span>-->
<span [class.active]="loginType === 'password'" (click)="loginType = 'password'">密码登录</span>
</div>
<!-- 登录表单 -->
@ -43,12 +43,6 @@
<div class="register-hint">
未注册的手机号,登录时将自动注册
</div>
<!-- 隐私政策 -->
<div class="privacy-hint">
登录即代表您已同意
<a routerLink="/mine/privacy">《用户隐私政策》</a>
</div>
</form>
</div>
</div>

@ -127,16 +127,4 @@
height: auto;
display: block;
}
}
.privacy-hint {
text-align: center;
font-size: 12px;
color: #999;
margin-top: 16px;
a {
color: #ee0a24;
text-decoration: none;
}
}

@ -9,16 +9,14 @@ import {
Input
} from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import {NavigationEnd, Router} from '@angular/router';
import {ModalController, NavController, ToastController} from '@ionic/angular';
import {getUser, MineService} from '../mine.service';
import { Router } from '@angular/router';
import {ModalController, ToastController} from '@ionic/angular';
import { MineService } from '../mine.service';
import {
SlidePoint,
SlideRef
} from "go-captcha-angular";
import {CaptchaModalComponent} from "../component/captcha-modal/captcha-modal.component";
import {Subscription} from "rxjs";
@Component({
selector: 'app-login',
templateUrl: './login.page.html',
@ -26,7 +24,7 @@ import {Subscription} from "rxjs";
standalone: false,
})
export class LoginPage implements OnInit, OnDestroy,AfterViewInit {
loginForm: FormGroup;
loginForm: FormGroup ;
loginType: 'sms' | 'password' = 'sms';
canSendSms = true;
smsButtonText = '发送验证码';
@ -34,14 +32,11 @@ export class LoginPage implements OnInit, OnDestroy,AfterViewInit {
private countdown = 60;
private timer: any;
private verifyToken: string | null = null;
private returnUrl: string | null = null;
private routerSub!: Subscription;
constructor(
private fb: FormBuilder,
private mineService: MineService,
private router: Router,
private navCtrl: NavController,
private cdr:ChangeDetectorRef,
private toastCtrl: ToastController,
private modalCtrl: ModalController,
@ -51,40 +46,20 @@ export class LoginPage implements OnInit, OnDestroy,AfterViewInit {
password: [''],
smsCode: ['']
});
// 获取返回路径
const navigation = this.router.getCurrentNavigation();
if (navigation?.extras?.state?.['returnUrl']) {
this.returnUrl = navigation.extras.state['returnUrl'];
}
}
ngOnInit() {
this.createForm();
this.routerSub = this.router.events.subscribe(event => {
if (event instanceof NavigationEnd) {
if(event.url == "/mine/login") {
this.checkLoginStatus()
}
}
});
}
checkLoginStatus() {
const t = localStorage.getItem("token")
if(t!=""){
this.navCtrl.navigateBack("/mine/login")
}
}
ngAfterViewInit() {
}
ngOnDestroy() {
this.stopCountdown() ;
if (this.routerSub) {
this.routerSub.unsubscribe();
}
}
private createForm() {
// 根据登录类型动态设置验证器
this.loginForm.get('password')?.setValidators(
@ -97,6 +72,7 @@ export class LoginPage implements OnInit, OnDestroy,AfterViewInit {
// 显示滑动验证码
showSlideVerify(type: 'sms' | 'password') {
const phone = this.loginForm.get('phone')?.value;
if (!phone || !this.loginForm.get('phone')?.valid) {
@ -106,6 +82,7 @@ export class LoginPage implements OnInit, OnDestroy,AfterViewInit {
// 调用滑动验证服务
this.mineService.getSlideCaptcha(phone).subscribe( (res) => {
console.log("chuangjian")
this.slideConfigValid.x = res.thumbX;
this.slideConfigValid.y = res.thumbY;
res.thumbX = 0
@ -117,15 +94,20 @@ export class LoginPage implements OnInit, OnDestroy,AfterViewInit {
},
})
modal.catch((res)=>{
console.log(res)
})
modal.then((res)=>{
console.log(res)
res.present()
res.onDidDismiss().then((res)=>{
console.log(res)
if(res.data < this.slideConfigValid.x+5 && res.data >this.slideConfigValid.x-5 ) {
console.log("发送验证码")
this.sendSmsCode()
}
})
})
});
}
@ -140,13 +122,17 @@ export class LoginPage implements OnInit, OnDestroy,AfterViewInit {
return;
}
console.log('发送短信验证码,手机号:', phone);
// 直接发送短信验证码
this.mineService.sendSmsCaptcha(phone, '').subscribe({
next: (response) => {
console.log('短信验证码发送成功:', response);
this.startCountdown();
this.showToast('验证码已发送');
},
error: (error) => {
console.error('发送验证码失败:', error);
let errorMessage = '发送失败,请重试';
if (error && error.error) {
@ -156,10 +142,7 @@ export class LoginPage implements OnInit, OnDestroy,AfterViewInit {
errorMessage = error.error.error;
}
}
if(errorMessage == "openid为空") {
window.location.href = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx13bd75fbedf283e5&redirect_uri=http%3A%2F%2Ffamyun.com%2Fcallback&response_type=code&scope=snsapi_base&state=123#wechat_redirect";
return
}
this.showToast(errorMessage);
}
});
@ -179,17 +162,15 @@ export class LoginPage implements OnInit, OnDestroy,AfterViewInit {
loginRequest.subscribe({
next: (response) => {
console.log('登录成功:', response);
this.showToast('登录成功');
localStorage.setItem("token",response);
// 如果有返回路径,则导航到该路径
if (this.returnUrl) {
this.router.navigateByUrl(this.returnUrl);
} else {
this.navCtrl.back();
}
sessionStorage.setItem("token",response)
this.router.navigate(['/mine']);
},
error: (error) => {
console.error('登录失败:', error);
let errorMessage = '登录失败,请重试';
if (error && error.error) {
if (typeof error.error === 'string') {
errorMessage = error.error;
@ -197,9 +178,7 @@ export class LoginPage implements OnInit, OnDestroy,AfterViewInit {
errorMessage = error.error.error;
}
}
if(errorMessage == "openid为空") {
window.location.href = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx13bd75fbedf283e5&redirect_uri=http%3A%2F%2Ffamyun.com%2Fcallback&response_type=code&scope=snsapi_base&state=123#wechat_redirect";
}
this.showToast(errorMessage);
}
});

@ -1,27 +1,21 @@
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { MinePage } from './mine.page';
const routes: Routes = [
{
path: '',
component: MinePage
},
{
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { MinePage } from './mine.page';
const routes: Routes = [
{
path: '',
component: MinePage
}, {
path: 'login',
loadChildren: () => import('./login/login.module').then( m => m.LoginPageModule)
},
{
path:'privacy',
loadChildren:() => import('./privacy/privacy.module').then(m=>m.PrivacyPageModule)
},
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
})
export class MinePageRoutingModule {}
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
})
export class MinePageRoutingModule {}

@ -1,7 +1,7 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { HttpClientModule } from '@angular/common/http';
import { IonicModule } from '@ionic/angular';
import { MinePageRoutingModule } from './mine-routing.module';
@ -13,9 +13,9 @@ import { MinePage } from './mine.page';
CommonModule,
FormsModule,
ReactiveFormsModule,
HttpClientModule,
IonicModule,
MinePageRoutingModule
],
declarations: [MinePage]
})

@ -0,0 +1,47 @@
<ion-header>
<ion-toolbar>
<ion-title>个人中心</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
<div class="profile-wrapper" *ngIf="isLoggedIn">
<ion-list>
<ion-item class="profile-header">
<ion-avatar slot="start">
<img [src]="'assets/default-avatar.png'" alt="avatar">
</ion-avatar>
<ion-label>
<h2>{{userInfo?.username || '未设置昵称'}}</h2>
<p>{{userInfo?.phone || '未绑定手机号'}}</p>
</ion-label>
</ion-item>
<ion-item-divider>
<ion-label>我的服务</ion-label>
</ion-item-divider>
<ion-item button detail="true">
<ion-icon name="star" slot="start" color="warning"></ion-icon>
<ion-label>我的收藏</ion-label>
</ion-item>
<ion-item button detail="true">
<ion-icon name="time" slot="start" color="primary"></ion-icon>
<ion-label>浏览历史</ion-label>
</ion-item>
<ion-item button detail="true">
<ion-icon name="settings" slot="start" color="medium"></ion-icon>
<ion-label>设置</ion-label>
</ion-item>
</ion-list>
<div class="logout-button">
<ion-button expand="block" color="danger" (click)="logout()">
退出登录
</ion-button>
</div>
</div>
</ion-content>

@ -8,12 +8,11 @@ ion-content {
}
.login-wrapper {
min-height: 100%;
display: flex;
flex-direction: column;
align-items: center;
align-items: flex-start;
justify-content: center;
height: 100%;
padding: 32px 16px;
padding: 20px;
}
.login-container {
@ -107,28 +106,59 @@ ion-segment {
//
.profile-wrapper {
.profile-header {
--padding-start: 16px;
--inner-padding-end: 16px;
--background: transparent;
ion-list {
background: transparent;
padding: 0;
margin: 16px 0;
}
}
ion-label {
h2 {
font-size: 16px;
color: #333;
margin: 0;
}
}
.profile-header {
--background: var(--card-background);
border-radius: 8px;
margin-bottom: 16px;
ion-avatar {
width: 60px;
height: 60px;
}
h2 {
font-size: 18px;
font-weight: 500;
margin-bottom: 4px;
color: var(--ion-color-dark);
}
ion-item-divider {
--background: #f5f5f5;
--color: #999;
p {
font-size: 14px;
margin-top: 12px;
color: var(--ion-color-medium);
}
}
ion-item-divider {
--background: transparent;
--padding-start: 0;
--border-width: 0;
font-size: 16px;
font-weight: 500;
margin: 16px 0 8px;
}
ion-item[button] {
--background: var(--card-background);
border-radius: 8px;
margin-bottom: 8px;
ion-icon {
font-size: 20px;
}
}
.logout-button {
margin-top: 32px;
}
.image-captcha-item {
.image-captcha-wrapper {
display: flex;
@ -157,120 +187,3 @@ ion-item.image-captcha-item {
margin-bottom: 8px;
}
}
.login-wrapper {
.login-content {
text-align: center;
.login-tip {
color: #999;
font-size: 14px;
margin-bottom: 32px;
}
ion-button {
width: 200px;
}
}
}
.coupon-list {
padding: 16px;
border-radius: 100px;
.coupon-item {
display: flex;
background: #fff;
padding: 16px;
margin-bottom: 12px;
.coupon-left {
width: 100px;
display: flex;
flex-direction: column;
.amount {
color: #ff4d4f;
display: flex;
align-items: baseline;
.symbol {
font-size: 14px;
}
.value {
font-size: 24px;
font-weight: normal;
}
}
.condition {
font-size: 12px;
color: #999;
margin-top: 4px;
}
}
.coupon-right {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
.name {
font-size: 14px;
color: #333;
margin-bottom: 4px;
}
.date {
font-size: 12px;
color: #999;
}
}
}
}
.logout-button {
margin-top: 32px;
padding: 0 16px;
}
ion-footer {
ion-toolbar {
--padding-top: 8px;
--padding-bottom: 8px;
--border-top:none;
// --background: #fff;
}
.logout-button {
padding: 0 16px;
ion-button {
margin: 0;
}
}
}
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 40px 20px;
text-align: center;
ion-icon {
font-size: 48px;
color: #999;
margin-bottom: 16px;
}
p {
color: #999;
font-size: 14px;
margin: 0;
}
}

@ -0,0 +1,17 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { MinePage } from './mine.page';
describe('MinePage', () => {
let component: MinePage;
let fixture: ComponentFixture<MinePage>;
beforeEach(() => {
fixture = TestBed.createComponent(MinePage);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

@ -1,12 +1,10 @@
import {Component, OnInit, OnDestroy, AfterViewInit} from '@angular/core';
import { Component, OnInit, OnDestroy } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import {NavController, ToastController,ViewWillEnter, ViewDidEnter, ViewWillLeave, ViewDidLeave } from '@ionic/angular';
import { ToastController } from '@ionic/angular';
import {getUser, MineService} from './mine.service';
import {NavigationEnd, Router} from '@angular/router';
import {delay} from "rxjs";
import { Router } from '@angular/router';
import {UserInfo} from "../shared/model/user";
import {Subscription} from "rxjs";
import { HomeService } from '../home/home.service';
import { Coupon } from '../shared/model/coupon';
@Component({
@ -15,7 +13,7 @@ import { Coupon } from '../shared/model/coupon';
styleUrls: ['./mine.page.scss'],
standalone:false
})
export class MinePage implements OnInit, OnDestroy {
export class MinePage implements OnInit, OnDestroy {
isLoggedIn = false;
userInfo: UserInfo | null = null;
loginType: 'sms' | 'password' = 'sms';
@ -23,56 +21,43 @@ export class MinePage implements OnInit, OnDestroy {
countdown = 0;
canSendCode = false;
captchaImage: string = '';
private routerSub!: Subscription;
couponCount: number = 0;
coupons: Coupon[] = [];
constructor(
private mineService: MineService,
private fb: FormBuilder,
private router: Router,
private navCtrl: NavController,
private toastCtrl: ToastController,
private homeService: HomeService
) {
getUser().subscribe(res=>{
console.log(res)
if(res.username == "") {
this.isLoggedIn = false;
this.router.navigate(['/mine/login'])
} else {
this.isLoggedIn = true;
this.getUserInfo()
}
})
}
ngOnInit() {
this.routerSub = this.router.events.subscribe(event => {
if (event instanceof NavigationEnd) {
if(event.url == "/mine") {
this.checkLoginStatus()
}
}
});
this.checkLoginStatus();
}
ngOnDestroy() {
if (this.captchaImage) {
URL.revokeObjectURL(this.captchaImage);
}
if (this.routerSub) {
this.routerSub.unsubscribe();
}
}
checkLoginStatus() {
const t = localStorage.getItem("token")
if(!t){
this.navCtrl.navigateForward("/mine/login")
const token = localStorage.getItem('token');
if (token) {
this.isLoggedIn = true;
this.getUserInfo();
}
getUser().subscribe(res=>{
if(res.username == "") {
this.isLoggedIn = false;
this.navCtrl.navigateForward("/mine/login")
} else {
this.isLoggedIn = true;
this.getUserInfo()
this.getCouponList()
}
})
}
@ -85,7 +70,7 @@ export class MinePage implements OnInit, OnDestroy {
},
error: (error) => {
this.showToast(error?.message || '获取用户信息失败', 'danger');
// this.logout();
this.logout();
}
});
}
@ -103,18 +88,12 @@ export class MinePage implements OnInit, OnDestroy {
togglePassword() {
this.showPassword = !this.showPassword;
}
login(){
}
logout() {
localStorage.removeItem('token');
this.isLoggedIn = false;
this.userInfo = null;
this.coupons = [];
this.showToast('已退出登录', 'success');
localStorage.removeItem('token');
localStorage.removeItem("giftCount")
this.couponCount = 0;
this.navCtrl.navigateForward("/mine/login")
}
private showToast(message: string, color: 'success' | 'danger' = 'success') {
@ -126,16 +105,4 @@ export class MinePage implements OnInit, OnDestroy {
cssClass: 'ion-text-center'
}).then(toast => toast.present());
}
getCouponList() {
this.homeService.getCouponList().subscribe(coupons => {
if(coupons){
coupons.forEach(res=>{
res.minAmount /= 100
res.value /= 100
})
this.coupons = coupons;
}
});
}
}

@ -20,7 +20,7 @@ export interface LoginResponse {
providedIn: 'root'
})
export class MineService {
private apiUrl = environment.apiUrl;
constructor(private http: HttpClient, private modalController: ModalController) {}
@ -35,6 +35,7 @@ export class MineService {
* @returns Observable<void>
*/
sendSmsCaptcha(phone: string, verifyToken: string): Observable<void> {
console.log("发送短信验证码,手机号:", phone);
return this.http.post<void>(`/api/auth/sms-captcha`, {
phone
}, {
@ -69,54 +70,36 @@ export class MineService {
return this.http.get<UserInfo>('/api/user/profile', );
}
getGuestToken():Observable<string> {
return this.http.get<string>('/api/user/guest')
verifyImageCaptcha(phone: string, captchaCode: string): Observable<void> {
return this.http.post<void>('/api/auth/verify-image', { phone, captchaCode }, { withCredentials: true });
}
getWechatOpenId():Observable<string> {
console.log('request')
return this.http.get<string>('https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx13bd75fbedf283e5&redirect_uri=http%3A%2F%2Fwww.famyun.com%2Fmine%2Flogin&response_type=code&scope=snsapi_base&state=123#wechat_redirect');
verifySmsCaptcha(phone: string, captchaCode: string): Observable<void> {
return this.http.post<void>('/api/auth/verify-sms', { phone, captchaCode }, { withCredentials: true });
}
sendVerificationCode(phone: string): Observable<void> {
return this.http.post<void>('/api/auth/sms/code', { phone }, { withCredentials: true });
}
}
export const getLoggedIn = () => {
const token: string | null = localStorage.getItem('token');
const token: string | null = sessionStorage.getItem('token');
const jwtHelper = new JwtHelper();
return of(token != null && !jwtHelper.isTokenExpired(token));
};
export const getUser = (): Observable<UserDetail> => {
const jwtHelper = new JwtHelper();
const token = localStorage.getItem('token');
const token = sessionStorage.getItem('token');
if (token != null) {
const userObj = jwtHelper.decodeToken(token);
console.log(userObj)
return of({
username: userObj.username,
guest_id: userObj.guest_id,
openid:userObj.openid
} as UserDetail);
} else {
return EMPTY
}
};
export const getGiftCount = ():Observable<number> => {
if(localStorage.getItem("giftCount")){
return of(parseInt(localStorage.getItem("giftCount")!))
} else {
return of(0)
}
}
export const useGiftCount = () => {
if(localStorage.getItem("giftCount")){
let n = parseInt(localStorage.getItem("giftCount")!)
if(n < 1) {
localStorage.setItem("giftCount",0 +"")
} else {
localStorage.setItem("giftCount",n-1 +"")
}
}
}

@ -1,5 +1,5 @@
export interface Article{
eventId:number;
eventId:string;
title:string;
class:string;
releaseDate:string;
@ -9,6 +9,6 @@ export interface Article{
content:string;
mainBoard:number;
growthBoard:number;
unlock:boolean;
lock:boolean;
}

@ -1,7 +1,6 @@
export interface UserDetail {
username:string;
guest_id:string;
openid:string;
username:string,
guest_id:string
}
export interface UserInfo {

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

Before

Width:  |  Height:  |  Size: 9.8 KiB

After

Width:  |  Height:  |  Size: 9.8 KiB

Before

Width:  |  Height:  |  Size: 225 KiB

After

Width:  |  Height:  |  Size: 225 KiB

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Before

Width:  |  Height:  |  Size: 6.0 KiB

After

Width:  |  Height:  |  Size: 6.0 KiB

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Before

Width:  |  Height:  |  Size: 7.8 KiB

After

Width:  |  Height:  |  Size: 7.8 KiB

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

Before

Width:  |  Height:  |  Size: 9.7 KiB

After

Width:  |  Height:  |  Size: 9.7 KiB

Before

Width:  |  Height:  |  Size: 930 B

After

Width:  |  Height:  |  Size: 930 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

@ -84,15 +84,3 @@ go-captcha {
}
}
}
//
.more-discounts-modal {
--backdrop-opacity: 0.6;
&::part(content) {
--width: fit-content;
--height: fit-content;
--background: transparent;
--box-shadow: none;
}
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save