Compare commits
No commits in common. 'developing' and 'master' have entirely different histories.
developing
...
master
@ -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" .
|
|
@ -1,4 +1,4 @@
|
|||||||
package ui
|
package cls
|
||||||
|
|
||||||
import "embed"
|
import "embed"
|
||||||
|
|
@ -1,7 +1,7 @@
|
|||||||
<ion-app>
|
<ion-app>
|
||||||
<ion-router-outlet></ion-router-outlet>
|
<ion-router-outlet></ion-router-outlet>
|
||||||
<ion-tabs>
|
<ion-tabs>
|
||||||
<ion-tab-bar slot="bottom" *ngIf="showTabs">
|
<ion-tab-bar slot="bottom">
|
||||||
<ion-tab-button tab="home">
|
<ion-tab-button tab="home">
|
||||||
<ion-icon name="home"></ion-icon>
|
<ion-icon name="home"></ion-icon>
|
||||||
<ion-label>首页</ion-label>
|
<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() {}
|
||||||
|
}
|
@ -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; // 解析失败时默认为游客
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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>
|
||||||
|
|
@ -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>
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -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}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
@ -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}`)
|
||||||
|
}
|
||||||
|
}
|
@ -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>
|
@ -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() {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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>
|
@ -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,7 +1,6 @@
|
|||||||
export interface UserDetail {
|
export interface UserDetail {
|
||||||
username:string;
|
username:string,
|
||||||
guest_id:string;
|
guest_id:string
|
||||||
openid:string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UserInfo {
|
export interface UserInfo {
|
After Width: | Height: | Size: 18 KiB |
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 |
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 |
After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |