Commit f39293cd authored by 神楽坂玲奈's avatar 神楽坂玲奈

支付半成品

parent 4c95445a
......@@ -79,9 +79,10 @@ h2 {
object-fit: contain;
box-shadow: 0 0 4px #ccc;
}
.banner {
width: 120px;
height:45px;
height: 45px;
object-fit: cover;
}
......@@ -208,16 +209,19 @@ table th, table td {
flex-direction: column;
flex-grow: 1;
}
#game_info p {
flex-grow: 1;
}
#game_info_2 {
width: 160px;
flex-shrink: 0;
}
#tags {
font-size: 14px;
.tag {
font-size: 12px;
padding: 2px 5px;
}
#purchase-form .form-check {
......
......@@ -6,7 +6,9 @@
<!--应用未购买-->
<div *ngIf="!currentApp.isBought()">
<button i18n type="button" class="btn btn-primary btn-sm" data-toggle="modal" data-target="#purchase-modal">{{currentApp.price.cny | currency:'CNY':true}} 购买</button>
<button i18n type="button" (click)="updateInstallOption(currentApp)" class="btn btn-secondary btn-sm" data-toggle="modal" data-target="#install-modal">安装试玩版</button>
<!--<button i18n (click)="updateInstallOption(currentApp)" type="button" class="btn btn-primary btn-sm" data-toggle="modal" data-target="#install-modal">我已经购买过</button>-->
</div>
......@@ -108,7 +110,7 @@
<div class="panel" id="game_info">
<p [innerHTML]="currentApp.description"></p>
<div id="tags" *ngIf="currentApp.tags">
<div *ngFor="let tag of currentApp.tags" class="btn btn-xs btn-danger" style="padding:3px 10px; margin:2px">{{tags[tag] || tag}}</div>
<div *ngFor="let tag of currentApp.tags" class="btn btn-sm btn-danger tag">{{tags[tag] || tag}}</div>
</div>
</div>
<div class="panel" id="game_info_2">
......@@ -289,6 +291,16 @@
</button>
</div>
<div class="modal-body">
<!--<div class="form-group">-->
<!--<label for="exampleSelect1">国家/地区</label>-->
<!--<select class="form-control" id="exampleSelect1">-->
<!--<option>中国(¥)</option>-->
<!--<option>美国($)</option>-->
<!--<option>3</option>-->
<!--<option>4</option>-->
<!--<option>5</option>-->
<!--</select>-->
<!--</div>-->
<div class="d-flex justify-content-between">
<img *ngIf="currentApp.cover" [src]="currentApp.cover" class="banner">
<span class="p-2">{{currentApp.name}}</span>
......@@ -298,11 +310,11 @@
<!--<fieldset class="form-group">-->
<legend>支付方式</legend>
<div class="form-check">
<input id="alipay" type="radio" class="form-check-input" name="optionsRadios" value="alipay" checked>
<input [(ngModel)]="payment" id="alipay" type="radio" class="form-check-input" name="payment" value="alipay" checked>
<label for="alipay" class="form-check-label">支付宝</label>
</div>
<div class="form-check">
<input id="wechat" type="radio" class="form-check-input" name="optionsRadios" value="alipay" checked>
<input [(ngModel)]="payment" id="wechat" type="radio" class="form-check-input" name="payment" value="wechat" checked>
<label for="wechat" class="form-check-label">微信</label>
</div>
<!--<div class="form-check">-->
......@@ -314,8 +326,31 @@
</div>
<div class="modal-footer">
<button i18n type="button" class="btn btn-secondary" data-dismiss="modal">取消</button>
<button i18n type="submit" [disabled]="import_path && !theForm.form.valid" class="btn btn-primary">购买</button>
<button i18n class="btn btn-secondary" data-dismiss="modal">取消</button>
<button i18n [disabled]="creating_order" class="btn btn-primary" (click)="purchase()">购买</button>
</div>
</div>
</div>
</div>
<div class="modal fade" id="purchase-modal-alipay" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true" data-backdrop="static" *ngIf="!currentApp.isBought()">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 i18n class="modal-title">购买 {{currentApp.name}}</h5>
<!--<button type="button" class="close" data-dismiss="modal" aria-label="Close">-->
<!--<span>&times;</span>-->
<!--</button>-->
</div>
<div class="modal-body">
订单已经创建,请在新窗口中进行支付,支付成功后页面会自动跳转
若支付成功后没有自动跳转 请联系 thdod@mycard.moe
若支付失败,请返回并选择其他支付方式
</div>
<div class="modal-footer">
<button i18n type="button" class="btn btn-secondary" data-dismiss="modal">返回</button>
</div>
</div>
</div>
......
......@@ -9,7 +9,8 @@ import * as path from 'path';
import * as fs from 'fs';
import * as $ from 'jquery';
import {Points} from './ygopro.component';
import {Http} from '@angular/http';
import {Http, URLSearchParams} from '@angular/http';
import {LoginService} from './login.service';
declare const Notification: any;
......@@ -42,9 +43,12 @@ export class AppDetailComponent implements OnInit, OnChanges {
tags: {};
payment = 'alipay';
creating_order = false;
constructor(private appsService: AppsService, private settingsService: SettingsService,
private downloadService: DownloadService, private ref: ChangeDetectorRef, private el: ElementRef,
private http: Http) {
private http: Http, private loginService: LoginService) {
this.tags = this.settingsService.getLocale().startsWith('zh') ? {
'recommend': '推荐',
......@@ -62,6 +66,9 @@ export class AppDetailComponent implements OnInit, OnChanges {
}
async ngOnChanges(changes: SimpleChanges) {
if (this.currentApp.isBought()) {
$('#purchase-modal-alipay').modal('hide');
}
if (changes['currentApp']) {
if (this.currentApp.background) {
this.el.nativeElement.style.background = `url("${this.currentApp.background}") rgba(255,255,255,.8)`;
......@@ -262,4 +269,29 @@ export class AppDetailComponent implements OnInit, OnChanges {
onPoints(points: Points) {
this.points = points;
}
async purchase() {
this.creating_order = true;
let data = new URLSearchParams();
data.set('app_id', this.currentApp.id);
data.set('user_id', this.loginService.user.email);
data.set('currency', 'cny');
data.set('payment', this.payment);
try {
let {url} = await this.http.post('https://api.mycard.moe/orders', data).map(response => response.json()).toPromise();
open(url);
$('#purchase-modal').modal('hide');
$('#purchase-modal-alipay').modal('show');
} catch (error) {
console.log(error);
if (error.status === 409) {
alert('卖完了 /\\');
} else if (error.status === 403) {
alert('已经购买过 /\\');
} else {
alert('出错了 /\\');
}
}
this.creating_order = false;
}
}
......@@ -78,7 +78,8 @@ export class App {
cover: string;
background: string;
price: {[currency: string]: string}
price: {[currency: string]: string};
key?: string;
static downloadUrl(app: App, platform: string, locale: string): string {
if (app.id === 'ygopro') {
......@@ -108,8 +109,9 @@ export class App {
return `https://thief.mycard.moe/update/${app.id}/${app.version}`;
}
isBought() {
return !this.price;
isBought(): Boolean {
// 免费或有 Key
return !this.price || !!this.key;
}
isLanguage() {
......@@ -185,6 +187,7 @@ export class App {
this.background = app.background;
this.price = app.price;
this.key = app.key;
}
findDependencies(): App[] {
......
import {Injectable, ApplicationRef, EventEmitter, NgZone} from '@angular/core';
import {Http} from '@angular/http';
import {Http, URLSearchParams} from '@angular/http';
import * as crypto from 'crypto';
import {App, AppStatus, Action} from './app';
import {SettingsService} from './settings.sevices';
......@@ -18,6 +18,7 @@ import {DownloadService, DownloadStatus} from './download.service';
import {InstallOption} from './install-option';
import {ComparableSet} from './shared/ComparableSet';
import {Observable, Observer} from 'rxjs/Rx';
import {LoginService} from './login.service';
import Timer = NodeJS.Timer;
import ReadableStream = NodeJS.ReadableStream;
const sudo = require('electron-sudo');
......@@ -61,7 +62,7 @@ export class AppsService {
: 'bsdtar';
constructor(private http: Http, private settingsService: SettingsService, private ref: ApplicationRef,
private downloadService: DownloadService, private ngZone: NgZone) {
private downloadService: DownloadService, private ngZone: NgZone, private loginService: LoginService) {
}
get lastVisited(): App|undefined {
......@@ -80,15 +81,26 @@ export class AppsService {
async loadApps() {
let appsURL = 'https://api.mycard.moe/apps.json';
let keysURL = 'https://api.mycard.moe/keys';
try {
let params = new URLSearchParams();
params.set('user_id', this.loginService.user.email);
let data = await this.http.get(appsURL).map((response) => response.json()).toPromise();
let keys_data = await this.http.get(keysURL, {search: params}).map((response) => response.json()).toPromise();
for (let item of keys_data) {
let app = data.find((app: any) => app.id === item.app_id);
if (app) {
app.key = item.id;
}
}
localStorage.setItem('apps_json', JSON.stringify(data));
this.apps = this.loadAppsList(data);
} catch (e) {
let data = localStorage.getItem('apps_json');
if (data) {
this.apps = this.loadAppsList(data);
this.apps = this.loadAppsList(JSON.parse(data));
} else {
alert('读取游戏列表失败,可能是网络不通')
this.apps = new Map();
}
}
......
......@@ -7,6 +7,9 @@ import {LoginService} from './login.service';
import {App, Category} from './app';
import {shell} from 'electron';
import {SettingsService} from './settings.sevices';
import {URLSearchParams} from '@angular/http';
const ReconnectingWebSocket = require('reconnecting-websocket');
// import 'typeahead.js';
// import Options = Twitter.Typeahead.Options;
......@@ -28,11 +31,13 @@ export class LobbyComponent implements OnInit {
@ViewChild('search')
search: ElementRef;
constructor (private appsService: AppsService, private loginService: LoginService,
private settingsService: SettingsService, private ref: ChangeDetectorRef) {
private messages: WebSocket;
constructor(private appsService: AppsService, private loginService: LoginService,
private settingsService: SettingsService, private ref: ChangeDetectorRef) {
}
async ngOnInit () {
async ngOnInit() {
this.apps = await this.appsService.loadApps();
if (this.apps.size > 0) {
this.chooseApp(this.appsService.lastVisited || this.apps.get('ygopro')!);
......@@ -54,6 +59,18 @@ export class LobbyComponent implements OnInit {
}
this.ref.detectChanges();
let url = new URL('wss://api.mycard.moe:3100');
let params: URLSearchParams = url['searchParams'];
params.set('user_id', this.loginService.user.email);
this.messages = new ReconnectingWebSocket(url);
this.messages.onmessage = async(event) => {
let data = JSON.parse(event.data);
console.log(data);
this.apps = await this.appsService.loadApps();
this.currentApp = this.apps.get(this.currentApp.id)!;
};
// $(this.search.nativeElement).typeahead(<any>{
// minLength: 1,
// highlight: true
......@@ -92,7 +109,7 @@ export class LobbyComponent implements OnInit {
height = 230;
}
this.resizing.style.height = `${height}px`;
if ( $('#candy').attr('data-minormax') !== 'default') {
if ($('#candy').attr('data-minormax') !== 'default') {
$('#candy').attr('data-minormax', 'default');
$('#mobile-roster-icon').css('display', 'block');
$('#chat-toolbar').css('display', 'block');
......@@ -104,7 +121,7 @@ export class LobbyComponent implements OnInit {
$('#restore').hide();
$('#maximize').show();
}
}else if ( height <= 150) {
} else if (height <= 150) {
$('#candy').attr('data-minormax', 'min');
this.resizing.style.height = '31px';
$('#mobile-roster-icon').css('display', 'none');
......@@ -116,7 +133,7 @@ export class LobbyComponent implements OnInit {
$('#unminimize').show();
$('#restore').hide();
$('#maximize').show();
}else if ( main_height <= 180) {
} else if (main_height <= 180) {
$('#candy').attr('data-minormax', 'max');
this.resizing.style.height = 'calc( 100% - 180px )';
$('#minimize').show();
......@@ -132,7 +149,7 @@ export class LobbyComponent implements OnInit {
});
}
mousedown (event: MouseEvent) {
mousedown(event: MouseEvent) {
// console.log(()
document.body.classList.add('resizing');
this.resizing = <HTMLElement>(<HTMLElement>event.target).parentNode;
......@@ -143,13 +160,13 @@ export class LobbyComponent implements OnInit {
}
}
chooseApp (app: App) {
chooseApp(app: App) {
this.currentApp = app;
this.appsService.lastVisited = app;
}
get grouped_apps () {
let contains = ['game', 'music', 'book'].map((value) => Category[value]);
get grouped_apps() {
let contains = ['game', 'music', 'book', 'test'].map((value) => Category[value]);
let result = {runtime: []};
for (let app of this.apps.values()) {
let tag: string;
......@@ -175,7 +192,7 @@ export class LobbyComponent implements OnInit {
return result;
}
openExternal (url: string) {
openExternal(url: string) {
shell.openExternal(url);
}
}
......@@ -77,6 +77,7 @@ System.config({
'jquery': 'npm:jquery/dist/jquery.min.js',
'tether': 'npm:tether/dist/js/tether.min.js',
'bootstrap': 'npm:bootstrap/dist/js/bootstrap.min.js',
'reconnecting-websocket': 'npm:reconnecting-websocket/dist/index.js'
// 'typeahead.js': '@node/typeahead.js'
},
// packages tells the System loader how to load when no filename and/or no extension
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment