forked from a/lifeto-shop
lol
This commit is contained in:
parent
d4e2883ece
commit
b3dad219c6
File diff suppressed because it is too large
Load Diff
11
package.json
11
package.json
|
@ -8,7 +8,16 @@
|
||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"vue": "^3.2.25"
|
"@handsontable/vue3": "^12.0.1",
|
||||||
|
"@types/numbro": "^1.9.3",
|
||||||
|
"@types/qs": "^6.9.7",
|
||||||
|
"@vueuse/core": "^8.7.5",
|
||||||
|
"axios": "^0.27.2",
|
||||||
|
"handsontable": "^12.0.1",
|
||||||
|
"qs": "^6.10.5",
|
||||||
|
"typescript-cookie": "^1.0.3",
|
||||||
|
"vue": "^3.2.25",
|
||||||
|
"vue3-cookies": "^1.0.6"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vitejs/plugin-vue": "^2.3.3",
|
"@vitejs/plugin-vue": "^2.3.3",
|
||||||
|
|
22
src/App.vue
22
src/App.vue
|
@ -1,12 +1,13 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
// This starter template is using Vue 3 <script setup> SFCs
|
import CharacterInventory from "./components/CharacterInventory.vue"
|
||||||
// Check out https://vuejs.org/api/sfc-script-setup.html#script-setup
|
import Login from "./pages/login.vue"
|
||||||
import HelloWorld from './components/HelloWorld.vue'
|
import SessionDisplay from "./components/SessionDisplay.vue";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<img alt="Vue logo" src="./assets/logo.png" />
|
<SessionDisplay/>
|
||||||
<HelloWorld msg="Hello Vue 3 + TypeScript + Vite" />
|
<Login/>
|
||||||
|
<CharacterInventory/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
@ -18,4 +19,15 @@ import HelloWorld from './components/HelloWorld.vue'
|
||||||
color: #2c3e50;
|
color: #2c3e50;
|
||||||
margin-top: 60px;
|
margin-top: 60px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.handsontable td {
|
||||||
|
border-right: 0px !important;
|
||||||
|
border-left: 0px !important;
|
||||||
|
border-top: 1px white !important;
|
||||||
|
border-bottom: 1px white !important;
|
||||||
|
background-color: #F7F7F7 !important;
|
||||||
|
}
|
||||||
|
.handsontable tr {
|
||||||
|
border-radius: 10px !important;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -0,0 +1,62 @@
|
||||||
|
<template>
|
||||||
|
<input v-on:input="swapHotData" placeholder="separate by comma" value="Image, Name, Count, MoveCount, Move, MoveConfirm">
|
||||||
|
<hot-table
|
||||||
|
ref="hotTableComponent"
|
||||||
|
:settings="hotSettings"
|
||||||
|
></hot-table>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent, computed, PropType, defineProps, defineEmits} from 'vue';
|
||||||
|
import { HotTable, HotColumn} from '@handsontable/vue3';
|
||||||
|
import { registerAllModules } from 'handsontable/registry';
|
||||||
|
import { InventoryTable, TableRecipe } from '../lib/table';
|
||||||
|
import { Columns, ColumnByNames } from '../lib/columns';
|
||||||
|
import { TricksterItem, SampleData } from '../lib/trickster';
|
||||||
|
import Handsontable from 'handsontable';
|
||||||
|
|
||||||
|
// register Handsontable's modules
|
||||||
|
registerAllModules();
|
||||||
|
|
||||||
|
const _defaultColumn = [
|
||||||
|
Columns.Image,
|
||||||
|
Columns.Name,
|
||||||
|
Columns.Count,
|
||||||
|
Columns.MoveCount,
|
||||||
|
Columns.Move,
|
||||||
|
]
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
data() {
|
||||||
|
let cols = _defaultColumn
|
||||||
|
const it = new InventoryTable(SampleData.aysheBoyfriend,
|
||||||
|
...cols
|
||||||
|
);
|
||||||
|
const dat = it.BuildTable()
|
||||||
|
return {
|
||||||
|
hotSettings: {
|
||||||
|
data: dat.data,
|
||||||
|
...dat.settings,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
swapHotData: function(event:any) {
|
||||||
|
let trm = event.target.value.split(",").map((x:any)=>x.trim())
|
||||||
|
let cols = ColumnByNames(...(trm as any)).filter(x=>x!=undefined)
|
||||||
|
if(cols.length < 2) {
|
||||||
|
cols = _defaultColumn
|
||||||
|
}
|
||||||
|
const it = new InventoryTable(SampleData.aysheBoyfriend,
|
||||||
|
...cols)
|
||||||
|
const hot = ((this.$refs!.hotTableComponent as any).hotInstance as Handsontable)
|
||||||
|
hot.updateSettings(it.BuildTable().settings)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
HotTable,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style src="handsontable/dist/handsontable.full.css"></style>
|
|
@ -1,52 +0,0 @@
|
||||||
<script setup lang="ts">
|
|
||||||
import { ref } from 'vue'
|
|
||||||
|
|
||||||
defineProps<{ msg: string }>()
|
|
||||||
|
|
||||||
const count = ref(0)
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<h1>{{ msg }}</h1>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
Recommended IDE setup:
|
|
||||||
<a href="https://code.visualstudio.com/" target="_blank">VS Code</a>
|
|
||||||
+
|
|
||||||
<a href="https://github.com/johnsoncodehk/volar" target="_blank">Volar</a>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>See <code>README.md</code> for more information.</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
<a href="https://vitejs.dev/guide/features.html" target="_blank">
|
|
||||||
Vite Docs
|
|
||||||
</a>
|
|
||||||
|
|
|
||||||
<a href="https://v3.vuejs.org/" target="_blank">Vue 3 Docs</a>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<button type="button" @click="count++">count is: {{ count }}</button>
|
|
||||||
<p>
|
|
||||||
Edit
|
|
||||||
<code>components/HelloWorld.vue</code> to test hot module replacement.
|
|
||||||
</p>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
a {
|
|
||||||
color: #42b983;
|
|
||||||
}
|
|
||||||
|
|
||||||
label {
|
|
||||||
margin: 0 0.5em;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
code {
|
|
||||||
background-color: #eee;
|
|
||||||
padding: 2px 4px;
|
|
||||||
border-radius: 4px;
|
|
||||||
color: #304455;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
<template>
|
||||||
|
<button @click="refreshSessions">
|
||||||
|
Refresh
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent, computed, PropType, defineProps, defineEmits} from 'vue';
|
||||||
|
import { Session } from '../lib/session';
|
||||||
|
import { storage } from '../session_storage';
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
Sessions: {} as any,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
refreshSessions: function(event:any) {
|
||||||
|
this.Sessions = storage.GetSessions()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -0,0 +1,404 @@
|
||||||
|
import Handsontable from "handsontable"
|
||||||
|
import numbro from 'numbro';
|
||||||
|
import { textRenderer } from "handsontable/renderers"
|
||||||
|
import { TricksterInventory, TricksterItem } from "./trickster"
|
||||||
|
import Core from "handsontable/core";
|
||||||
|
|
||||||
|
|
||||||
|
type DefaultColumnName = "Image"|"Name"|"Count"
|
||||||
|
type ActionColumnName = "Move"|"MoveCount"|"ConfirmMove"
|
||||||
|
type TagColumName = "Equip"|"Drill"|"Card"|"Quest"|"Consume"
|
||||||
|
type StatColumnName = "AP"|"Gun AP"|"AC"|"DX"|"MP"|"MA"|"MD"|"WT"|"DA"|"LK"|"HP"|"DP"|"HV"
|
||||||
|
type EquipmentColumnName = "MinLvl"|"Slots"|"RefineNumber"|"RefineState"
|
||||||
|
type ColumnName = DefaultColumnName | TagColumName | StatColumnName | ActionColumnName | EquipmentColumnName
|
||||||
|
|
||||||
|
|
||||||
|
export interface ColumnInfo {
|
||||||
|
name: ColumnName
|
||||||
|
displayName:string
|
||||||
|
renderer?:any
|
||||||
|
filtering?:boolean
|
||||||
|
writable?:boolean
|
||||||
|
getter(item:TricksterItem):(string | number)
|
||||||
|
}
|
||||||
|
|
||||||
|
class Image implements ColumnInfo {
|
||||||
|
name:ColumnName = 'Image'
|
||||||
|
displayName = " "
|
||||||
|
renderer = coverRenderer
|
||||||
|
getter(item:TricksterItem):(string|number) {
|
||||||
|
return item.image ? item.image : ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function coverRenderer(instance:any, td:any, row:any, col:any, prop:any, value:any, cellProperties:any) {
|
||||||
|
const stringifiedValue = Handsontable.helper.stringify(value);
|
||||||
|
if (stringifiedValue.startsWith('http')) {
|
||||||
|
const img:any = document.createElement('IMG');
|
||||||
|
img.src = value;
|
||||||
|
Handsontable.dom.addEvent(img, 'mousedown', event =>{
|
||||||
|
event!.preventDefault();
|
||||||
|
});
|
||||||
|
Handsontable.dom.empty(td);
|
||||||
|
td.appendChild(img);
|
||||||
|
} else {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Name implements ColumnInfo {
|
||||||
|
name:ColumnName = "Name"
|
||||||
|
displayName = "Name"
|
||||||
|
filtering = true
|
||||||
|
getter(item:TricksterItem):(string|number){
|
||||||
|
return item.item_name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Count implements ColumnInfo {
|
||||||
|
name:ColumnName = "Count"
|
||||||
|
displayName = "Count"
|
||||||
|
renderer = "numeric"
|
||||||
|
filtering = true
|
||||||
|
getter(item:TricksterItem):(string|number){
|
||||||
|
return item.item_count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Move implements ColumnInfo {
|
||||||
|
name:ColumnName = "Move"
|
||||||
|
displayName = "Target"
|
||||||
|
writable = true
|
||||||
|
getter(item:TricksterItem):(string|number){
|
||||||
|
return "-"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MoveCount implements ColumnInfo {
|
||||||
|
name:ColumnName = "MoveCount"
|
||||||
|
displayName = "Move #"
|
||||||
|
renderer = moveCountRenderer
|
||||||
|
writable = true
|
||||||
|
getter(item:TricksterItem):(string|number){
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function moveCountRenderer(instance:Core, td:any, row:number, col:number, prop:any, value:any, cellProperties:any) {
|
||||||
|
let newValue = value;
|
||||||
|
|
||||||
|
if (Handsontable.helper.isNumeric(newValue)) {
|
||||||
|
const numericFormat = cellProperties.numericFormat;
|
||||||
|
const cellCulture = numericFormat && numericFormat.culture || '-';
|
||||||
|
const cellFormatPattern = numericFormat && numericFormat.pattern;
|
||||||
|
const className = cellProperties.className || '';
|
||||||
|
const classArr = className.length ? className.split(' ') : [];
|
||||||
|
|
||||||
|
if (typeof cellCulture !== 'undefined' && !numbro.languages()[cellCulture]) {
|
||||||
|
const shortTag:any = cellCulture.replace('-', '');
|
||||||
|
const langData = (numbro as any)[shortTag];
|
||||||
|
|
||||||
|
if (langData) {
|
||||||
|
numbro.registerLanguage(langData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const totalCount = Number(instance.getCell(row,col-1)?.innerHTML)
|
||||||
|
numbro.setLanguage(cellCulture);
|
||||||
|
const num = numbro(newValue)
|
||||||
|
if(totalCount < num.value()) {
|
||||||
|
const newNum = numbro(totalCount)
|
||||||
|
newValue = newNum.format(cellFormatPattern || '0');
|
||||||
|
}else {
|
||||||
|
newValue = num.format(cellFormatPattern || '0');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (classArr.indexOf('htLeft') < 0 && classArr.indexOf('htCenter') < 0 &&
|
||||||
|
classArr.indexOf('htRight') < 0 && classArr.indexOf('htJustify') < 0) {
|
||||||
|
classArr.push('htRight');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (classArr.indexOf('htNumeric') < 0) {
|
||||||
|
classArr.push('htNumeric');
|
||||||
|
}
|
||||||
|
|
||||||
|
cellProperties.className = classArr.join(' ');
|
||||||
|
|
||||||
|
td.dir = 'ltr';
|
||||||
|
newValue = newValue + "x"
|
||||||
|
}else {
|
||||||
|
newValue = ""
|
||||||
|
}
|
||||||
|
textRenderer(instance, td, row, col, prop, newValue, cellProperties);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class ConfirmMove implements ColumnInfo {
|
||||||
|
name:ColumnName = "ConfirmMove"
|
||||||
|
displayName = "Confirm"
|
||||||
|
writable = true
|
||||||
|
getter(item:TricksterItem):(string|number){
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Equip implements ColumnInfo {
|
||||||
|
name:ColumnName = "Equip"
|
||||||
|
displayName = "equip"
|
||||||
|
getter(item:TricksterItem):(string|number){
|
||||||
|
return item.is_equip ? 1 : 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Drill implements ColumnInfo {
|
||||||
|
name:ColumnName = "Drill"
|
||||||
|
displayName = "drill"
|
||||||
|
getter(item:TricksterItem):(string|number){
|
||||||
|
return item.is_drill ? 1 : 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Card implements ColumnInfo {
|
||||||
|
name:ColumnName = "Card"
|
||||||
|
displayName = "card"
|
||||||
|
getter(item:TricksterItem):(string|number){
|
||||||
|
return cardFilter(item) ? 1 : 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const cardFilter= (item:TricksterItem): boolean => {
|
||||||
|
return (item.item_name.endsWith(" Card") || item.item_name.startsWith("Star Card"))
|
||||||
|
}
|
||||||
|
|
||||||
|
class Quest implements ColumnInfo {
|
||||||
|
name:ColumnName = "Quest"
|
||||||
|
displayName = "quest"
|
||||||
|
getter(item:TricksterItem):(string|number){
|
||||||
|
return questFilter(item) ? 1 : 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const questFilter= (item:TricksterItem): boolean => {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
class Consume implements ColumnInfo {
|
||||||
|
name:ColumnName = "Consume"
|
||||||
|
displayName = "eat"
|
||||||
|
getter(item:TricksterItem):(string|number){
|
||||||
|
return consumeFilter(item) ? 1 : 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const consumeFilter= (item:TricksterItem): boolean => {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
class AP implements ColumnInfo {
|
||||||
|
name:ColumnName = "AP"
|
||||||
|
displayName = "AP"
|
||||||
|
getter(item:TricksterItem):(string|number){
|
||||||
|
return item.stats ? item.stats["AP"] : ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class GunAP implements ColumnInfo {
|
||||||
|
name:ColumnName = "Gun AP"
|
||||||
|
displayName = "Gun AP"
|
||||||
|
getter(item:TricksterItem):(string|number){
|
||||||
|
return item.stats ? item.stats["Gun AP"] : ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class AC implements ColumnInfo {
|
||||||
|
name:ColumnName = "AC"
|
||||||
|
displayName = "AC"
|
||||||
|
getter(item:TricksterItem):(string|number){
|
||||||
|
return item.stats ? item.stats["AC"] : ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DX implements ColumnInfo {
|
||||||
|
name:ColumnName = "DX"
|
||||||
|
displayName = "DX"
|
||||||
|
getter(item:TricksterItem):(string|number){
|
||||||
|
return item.stats ? item.stats["DX"] : ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MP implements ColumnInfo {
|
||||||
|
name:ColumnName = "MP"
|
||||||
|
displayName = "MP"
|
||||||
|
getter(item:TricksterItem):(string|number){
|
||||||
|
return item.stats ? item.stats["MP"] : ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MA implements ColumnInfo {
|
||||||
|
name:ColumnName = "MA"
|
||||||
|
displayName = "MA"
|
||||||
|
getter(item:TricksterItem):(string|number){
|
||||||
|
return item.stats ? item.stats["MA"] : ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MD implements ColumnInfo {
|
||||||
|
name:ColumnName = "MD"
|
||||||
|
displayName = "MD"
|
||||||
|
getter(item:TricksterItem):(string|number){
|
||||||
|
return item.stats ? item.stats["MD"] : ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class WT implements ColumnInfo {
|
||||||
|
name:ColumnName = "WT"
|
||||||
|
displayName = "WT"
|
||||||
|
getter(item:TricksterItem):(string|number){
|
||||||
|
return item.stats ? item.stats["WT"] : ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DA implements ColumnInfo {
|
||||||
|
name:ColumnName = "DA"
|
||||||
|
displayName = "DA"
|
||||||
|
getter(item:TricksterItem):(string|number){
|
||||||
|
return item.stats ? item.stats["DA"] : ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class LK implements ColumnInfo {
|
||||||
|
name:ColumnName = "LK"
|
||||||
|
displayName = "LK"
|
||||||
|
getter(item:TricksterItem):(string|number){
|
||||||
|
return item.stats ? item.stats["LK"] : ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class HP implements ColumnInfo {
|
||||||
|
name:ColumnName = "HP"
|
||||||
|
displayName = "HP"
|
||||||
|
getter(item:TricksterItem):(string|number){
|
||||||
|
return item.stats ? item.stats["HP"] : ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DP implements ColumnInfo {
|
||||||
|
name:ColumnName = "DP"
|
||||||
|
displayName = "DP"
|
||||||
|
getter(item:TricksterItem):(string|number){
|
||||||
|
return item.stats ? item.stats["DP"] : ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class HV implements ColumnInfo {
|
||||||
|
name:ColumnName = "HV"
|
||||||
|
displayName = "HV"
|
||||||
|
getter(item:TricksterItem):(string|number){
|
||||||
|
return item.stats ? item.stats["HV"] : ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MinLvl implements ColumnInfo {
|
||||||
|
name:ColumnName = "MinLvl"
|
||||||
|
displayName = "lvl"
|
||||||
|
getter(item:TricksterItem):(string|number){
|
||||||
|
//TODO:
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Slots implements ColumnInfo {
|
||||||
|
name:ColumnName = "Slots"
|
||||||
|
displayName = "slots"
|
||||||
|
getter(item:TricksterItem):(string|number){
|
||||||
|
//TODO:
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class RefineNumber implements ColumnInfo {
|
||||||
|
name:ColumnName = "RefineNumber"
|
||||||
|
displayName = "R#"
|
||||||
|
getter(item:TricksterItem):(string|number){
|
||||||
|
return item.refine_level ? item.refine_level : 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class RefineState implements ColumnInfo {
|
||||||
|
name:ColumnName = "RefineState"
|
||||||
|
displayName = "State"
|
||||||
|
getter(item:TricksterItem):(string|number){
|
||||||
|
return item.refine_state ? item.refine_state : 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export const ColumnByNames = (...n:ColumnName[]) => {
|
||||||
|
return n.map(ColumnByName)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export const ColumnByName = (n:ColumnName) => {
|
||||||
|
return Columns[n]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export const Columns = {
|
||||||
|
Image: new Image(),
|
||||||
|
Name: new Name(),
|
||||||
|
Count: new Count(),
|
||||||
|
Move: new Move(),
|
||||||
|
MoveCount: new MoveCount(),
|
||||||
|
ConfirmMove: new ConfirmMove(),
|
||||||
|
Equip: new Equip(),
|
||||||
|
Drill: new Drill(),
|
||||||
|
Card: new Card(),
|
||||||
|
Quest: new Quest(),
|
||||||
|
Consume: new Consume(),
|
||||||
|
AP: new AP(),
|
||||||
|
GunAP: new GunAP(),
|
||||||
|
'Gun AP': new GunAP(),
|
||||||
|
AC: new AC(),
|
||||||
|
DX: new DX(),
|
||||||
|
MP: new MP(),
|
||||||
|
MA: new MA(),
|
||||||
|
MD: new MD(),
|
||||||
|
WT: new WT(),
|
||||||
|
DA: new DA(),
|
||||||
|
LK: new LK(),
|
||||||
|
HP: new HP(),
|
||||||
|
DP: new DP(),
|
||||||
|
HV: new HV(),
|
||||||
|
MinLvl: new MinLvl(),
|
||||||
|
Slots: new Slots(),
|
||||||
|
RefineNumber: new RefineNumber(),
|
||||||
|
RefineState: new RefineState(),
|
||||||
|
image: new Image(),
|
||||||
|
name: new Name(),
|
||||||
|
count: new Count(),
|
||||||
|
move: new Move(),
|
||||||
|
movecount: new MoveCount(),
|
||||||
|
confirmmove: new ConfirmMove(),
|
||||||
|
equip: new Equip(),
|
||||||
|
drill: new Drill(),
|
||||||
|
card: new Card(),
|
||||||
|
quest: new Quest(),
|
||||||
|
consume: new Consume(),
|
||||||
|
ap: new AP(),
|
||||||
|
gunap: new GunAP(),
|
||||||
|
'gun ap': new GunAP(),
|
||||||
|
ac: new AC(),
|
||||||
|
dx: new DX(),
|
||||||
|
mp: new MP(),
|
||||||
|
ma: new MA(),
|
||||||
|
md: new MD(),
|
||||||
|
wt: new WT(),
|
||||||
|
da: new DA(),
|
||||||
|
lk: new LK(),
|
||||||
|
hp: new HP(),
|
||||||
|
dp: new DP(),
|
||||||
|
hv: new HV(),
|
||||||
|
minlvl: new MinLvl(),
|
||||||
|
slots: new Slots(),
|
||||||
|
refinenumber: new RefineNumber(),
|
||||||
|
refinestate: new RefineState(),
|
||||||
|
}
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,125 @@
|
||||||
|
import axios from "axios";
|
||||||
|
import qs from "qs";
|
||||||
|
import { getCookie, removeCookie } from "typescript-cookie";
|
||||||
|
|
||||||
|
export interface Session {
|
||||||
|
user:string
|
||||||
|
xsrf:string
|
||||||
|
csrf:string
|
||||||
|
lto:string
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export const API_ROOT = "/lifeto/"
|
||||||
|
|
||||||
|
const endpoint = (name:string)=>{
|
||||||
|
return API_ROOT + name
|
||||||
|
}
|
||||||
|
|
||||||
|
export class LoginHelper {
|
||||||
|
user:string
|
||||||
|
pass:string
|
||||||
|
csrf?:string
|
||||||
|
constructor(user:string, pass:string){
|
||||||
|
this.user = user;
|
||||||
|
this.pass = pass;
|
||||||
|
}
|
||||||
|
login = async ():Promise<TokenSession> =>{
|
||||||
|
removeCookie("XSRF-TOKEN")
|
||||||
|
removeCookie("lifeto_session", {path:'/'})
|
||||||
|
await sleep(1000)
|
||||||
|
return axios.get(endpoint("login"),{withCredentials:false})
|
||||||
|
.then(async (x)=>{
|
||||||
|
console.log(x)
|
||||||
|
if(x.data){
|
||||||
|
try{
|
||||||
|
this.csrf = x.data.split("csrf-token")[1].split('\">')[0].replace("\" content=\"",'')
|
||||||
|
}catch(e){
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return axios.post(endpoint("login"),{
|
||||||
|
login:this.user,
|
||||||
|
password:this.pass,
|
||||||
|
redirectTo:"lifeto"
|
||||||
|
},{withCredentials:false})
|
||||||
|
.then(async (x)=>{
|
||||||
|
await sleep(100)
|
||||||
|
let xsrf= getCookie("XSRF-TOKEN")
|
||||||
|
let lifeto = getCookie("lifeto_session")
|
||||||
|
return new TokenSession(this.user,this.csrf!, xsrf!, lifeto!)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
const sleep= async(ms:number)=> {
|
||||||
|
return new Promise(resolve => setTimeout(resolve, ms))
|
||||||
|
}
|
||||||
|
export class TokenSession {
|
||||||
|
csrf:string
|
||||||
|
xsrf:string
|
||||||
|
lto:string
|
||||||
|
user:string
|
||||||
|
constructor(name:string, csrf:string, xsrf: string, lifeto:string){
|
||||||
|
this.user = name
|
||||||
|
this.csrf = csrf
|
||||||
|
this.xsrf = xsrf;
|
||||||
|
this.lto = lifeto;
|
||||||
|
}
|
||||||
|
|
||||||
|
authed_request = async (verb:string,url:string,data:any) => {
|
||||||
|
let promise
|
||||||
|
switch (verb){
|
||||||
|
case "post":
|
||||||
|
promise = axios.post(endpoint(url),data,this.genHeaders())
|
||||||
|
break;
|
||||||
|
case "postraw":
|
||||||
|
const querystring = qs.stringify(data)
|
||||||
|
promise = axios.post(endpoint(url),querystring,this.genHeaders())
|
||||||
|
break;
|
||||||
|
case "get":
|
||||||
|
default:
|
||||||
|
promise = axios.get(endpoint(url),this.genHeaders())
|
||||||
|
}
|
||||||
|
return promise.then(x=>{
|
||||||
|
if(x.data){
|
||||||
|
try{
|
||||||
|
this.xsrf = x.data.split("xsrf-token")[1].split('\">')[0].replace("\" content=\"",'')
|
||||||
|
}catch(e){
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(x.headers['set-cookie']){
|
||||||
|
const cookies = x.headers['set-cookie'].map((y)=>{
|
||||||
|
return y.split("=")[1].split(";")[0];
|
||||||
|
})
|
||||||
|
this.xsrf = cookies[0]
|
||||||
|
this.lto = cookies[1]
|
||||||
|
}
|
||||||
|
return x
|
||||||
|
})
|
||||||
|
}
|
||||||
|
genHeaders = ()=>{
|
||||||
|
const out = {
|
||||||
|
headers:{
|
||||||
|
Cookie:`XSRF-TOKEN=${this.xsrf}; lifeto_session=${this.lto}`,
|
||||||
|
Accept: "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
|
||||||
|
Connection: "keep-alive",
|
||||||
|
"X-Requested-With":"XMLHttpRequest",
|
||||||
|
Host: "beta.lifeto.co",
|
||||||
|
"Alt-Used": "beta.lifeto.co",
|
||||||
|
"Update-Insecure-Requests": 1,
|
||||||
|
"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:90.0) Gecko/20100101 Firefox/90.0",
|
||||||
|
"Sec-Fetch-Mode":"navigate",
|
||||||
|
"Sec-Fetch-Dest":"document",
|
||||||
|
"Sec-Fetch-Site":"same-origin",
|
||||||
|
"Sec-Fetch-User":"?1",
|
||||||
|
"TE":"trailers"
|
||||||
|
},
|
||||||
|
withCredentials:true
|
||||||
|
}
|
||||||
|
if(this.xsrf){
|
||||||
|
(out.headers as any)["X-XSRF-TOKEN"] = this.xsrf.replace("%3D","=")
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,78 @@
|
||||||
|
import { HotTableProps } from "@handsontable/vue3/types"
|
||||||
|
import { TricksterInventory } from "./trickster"
|
||||||
|
import {ColumnInfo} from "./columns"
|
||||||
|
import { ColumnSettings } from "handsontable/settings"
|
||||||
|
import { PredefinedMenuItemKey } from "handsontable/plugins/contextMenu"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export class InventoryTable {
|
||||||
|
inventory:TricksterInventory
|
||||||
|
columns: ColumnInfo[]
|
||||||
|
|
||||||
|
constructor(inv:TricksterInventory, ...columns:ColumnInfo[]) {
|
||||||
|
this.inventory = inv
|
||||||
|
this.columns = columns
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
getTableColumnNames(): string[] {
|
||||||
|
return this.columns.map(x=>x.displayName)
|
||||||
|
}
|
||||||
|
|
||||||
|
getTableColumnSettings(): ColumnSettings[] {
|
||||||
|
return this.columns.map(x=>({
|
||||||
|
renderer: x.renderer ? x.renderer : "text",
|
||||||
|
dropdownMenu: x.filtering ? DefaultDropdownItems() : false,
|
||||||
|
readOnly: x.writable ? false : true,
|
||||||
|
selectionMode: x.writable ? "multiple" : "single"
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
getTableRows():any[][] {
|
||||||
|
return this.inventory.items.map((item)=>{
|
||||||
|
return this.columns.map(x=>x.getter(item))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
BuildTable():TableRecipe {
|
||||||
|
const s = DefaultSettings()
|
||||||
|
return {
|
||||||
|
data: this.getTableRows(),
|
||||||
|
settings: {
|
||||||
|
data: this.getTableRows(),
|
||||||
|
colHeaders:this.getTableColumnNames(),
|
||||||
|
columns:this.getTableColumnSettings(),
|
||||||
|
...s
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TableRecipe {
|
||||||
|
data: any[][]
|
||||||
|
settings: HotTableProps
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DefaultDropdownItems = ():PredefinedMenuItemKey[]=>['filter_by_condition' , 'filter_operators' ,'filter_by_condition2' , 'filter_by_value' , 'filter_action_bar']
|
||||||
|
export const DefaultSettings = ():HotTableProps=>{
|
||||||
|
return {
|
||||||
|
trimDropdown: true,
|
||||||
|
filters: true,
|
||||||
|
manualRowMove: false,
|
||||||
|
manualColumnMove: false,
|
||||||
|
allowInsertRow: false,
|
||||||
|
allowInsertColumn: false,
|
||||||
|
allowRemoveRow: false,
|
||||||
|
allowRemoveColumn: false,
|
||||||
|
allowHtml: true,
|
||||||
|
disableVisualSelection: false,
|
||||||
|
columnSorting: {
|
||||||
|
indicator: true,
|
||||||
|
headerAction: true,
|
||||||
|
},
|
||||||
|
contextMenu: false,
|
||||||
|
readOnlyCellClassName: "",
|
||||||
|
licenseKey:"non-commercial-and-evaluation",
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
import sampleAyshe from "./sample/ayshe.json"
|
||||||
|
|
||||||
|
|
||||||
|
export interface ItemExpireTime {
|
||||||
|
text: string
|
||||||
|
us: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TricksterItem {
|
||||||
|
unique_id: number;
|
||||||
|
item_name: string;
|
||||||
|
item_id: number;
|
||||||
|
item_count: number;
|
||||||
|
is_equip?: boolean;
|
||||||
|
is_drill?: boolean;
|
||||||
|
item_expire_time?: ItemExpireTime;
|
||||||
|
refine_level?: number;
|
||||||
|
refine_type?: number;
|
||||||
|
refine_state?: number;
|
||||||
|
image?: string;
|
||||||
|
stats?: {[key: string]:any}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TricksterWallet {
|
||||||
|
galders:number
|
||||||
|
state:number
|
||||||
|
job_img:string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TricksterInventory {
|
||||||
|
name:string
|
||||||
|
|
||||||
|
wallet?:TricksterWallet
|
||||||
|
items:TricksterItem[]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export const SampleData:{[key:string]:TricksterInventory} = {
|
||||||
|
aysheBoyfriend: {
|
||||||
|
name: sampleAyshe.characters[100047311].name,
|
||||||
|
items: Object.entries(sampleAyshe.items).map(([k, v]:[string, any])=>{
|
||||||
|
v.unique_id = k
|
||||||
|
return v
|
||||||
|
}),
|
||||||
|
wallet: {
|
||||||
|
galders: sampleAyshe.characters[100047311].galders,
|
||||||
|
job_img: sampleAyshe.characters[100047311].job_img,
|
||||||
|
state: sampleAyshe.characters[100047311].state
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
src/main.ts
11
src/main.ts
|
@ -1,4 +1,15 @@
|
||||||
import { createApp } from 'vue'
|
import { createApp } from 'vue'
|
||||||
import App from './App.vue'
|
import App from './App.vue'
|
||||||
|
|
||||||
|
|
||||||
|
import { globalCookiesConfig } from "vue3-cookies";
|
||||||
|
|
||||||
|
globalCookiesConfig({
|
||||||
|
expireTimes: "365d",
|
||||||
|
path: "/",
|
||||||
|
domain: "",
|
||||||
|
secure: true,
|
||||||
|
sameSite: "None",
|
||||||
|
});
|
||||||
|
|
||||||
createApp(App).mount('#app')
|
createApp(App).mount('#app')
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
<template>
|
||||||
|
<section class="login_modal">
|
||||||
|
<div class="login_field">
|
||||||
|
<label for="userName">Username: </label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="login-username"
|
||||||
|
v-model="username"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="login_field">
|
||||||
|
<label for="password">Password: </label>
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
id="login-password"
|
||||||
|
v-model="password"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="login_field">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
id="loginButton"
|
||||||
|
v-on:click="login()"
|
||||||
|
>Login</button>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { useVModel } from '@vueuse/core'
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
username:string
|
||||||
|
password:string
|
||||||
|
}>()
|
||||||
|
const data = {
|
||||||
|
username: useVModel(props, 'username', defineEmits(['update:username'])),
|
||||||
|
password:useVModel(props, 'password', defineEmits(['update:password']))
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent, computed, PropType, defineProps, defineEmits} from 'vue';
|
||||||
|
import { LoginHelper, Session } from '../lib/session';
|
||||||
|
import { storage } from '../session_storage';
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
methods: {
|
||||||
|
login: function() {
|
||||||
|
new LoginHelper(this.username, this.password).login()
|
||||||
|
.then((session)=>{
|
||||||
|
console.log(session, "adding to storage")
|
||||||
|
storage.AddSession(session)
|
||||||
|
}).catch((e)=>{
|
||||||
|
if(e.code == "ERR_BAD_REQUEST") {
|
||||||
|
alert("invalid username/password")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
alert("unknown error, please report")
|
||||||
|
console.log(e)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -0,0 +1,63 @@
|
||||||
|
import { getCookie, setCookie} from 'typescript-cookie'
|
||||||
|
import { Session } from './lib/session'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export const LIFETO_COOKIE_PREFIX="LIFETO_PANEL_"
|
||||||
|
|
||||||
|
export const nameCookie = (...s:string[]):string=>{
|
||||||
|
return LIFETO_COOKIE_PREFIX+s.join("_").toUpperCase()
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Storage {
|
||||||
|
GetSessions():{[key:string]:Session} {
|
||||||
|
const all_accounts = getCookie(nameCookie("all_accounts"))
|
||||||
|
const accounts = all_accounts?.split(",")
|
||||||
|
let out:{[key:string]:Session} = {};
|
||||||
|
for (const account in accounts) {
|
||||||
|
let tmp = {
|
||||||
|
user: account,
|
||||||
|
xsrf: getCookie(nameCookie("xsrf",account))!,
|
||||||
|
lto: getCookie(nameCookie("lto",account))!,
|
||||||
|
csrf: getCookie(nameCookie("csrf",account))!
|
||||||
|
}
|
||||||
|
out[account] = tmp
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
RemoveSessions(...s:Session[]) {
|
||||||
|
for(const v of s) {
|
||||||
|
this.RemoveSession(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RemoveSession(s:Session) {
|
||||||
|
const all_accounts = getCookie(nameCookie("all_accounts"))
|
||||||
|
let accounts = all_accounts?.split(",")
|
||||||
|
accounts = accounts ? accounts : []
|
||||||
|
accounts = [...new Set(accounts)]
|
||||||
|
accounts = accounts.filter(x=>x!=s.user)
|
||||||
|
|
||||||
|
setCookie(nameCookie("all_accounts"),accounts.join(","))
|
||||||
|
setCookie(nameCookie("lto",s.user), "")
|
||||||
|
setCookie(nameCookie("xsrf",s.user),"")
|
||||||
|
}
|
||||||
|
|
||||||
|
AddSessions(...s:Session[]) {
|
||||||
|
for(const v of s) {
|
||||||
|
this.AddSession(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AddSession(s:Session) {
|
||||||
|
const all_accounts = getCookie(nameCookie("all_accounts"))
|
||||||
|
let accounts = all_accounts?.split(",")
|
||||||
|
accounts = accounts ? accounts : []
|
||||||
|
accounts.push(s.user)
|
||||||
|
accounts = [...new Set(accounts)]
|
||||||
|
setCookie(nameCookie("lto",s.user), s.lto)
|
||||||
|
setCookie(nameCookie("xsrf",s.user),s.xsrf)
|
||||||
|
setCookie(nameCookie("csrf",s.user),s.csrf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const storage = new Storage()
|
|
@ -3,5 +3,20 @@ import vue from '@vitejs/plugin-vue'
|
||||||
|
|
||||||
// https://vitejs.dev/config/
|
// https://vitejs.dev/config/
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [vue()]
|
plugins: [vue()],
|
||||||
|
server: {
|
||||||
|
https: true,
|
||||||
|
proxy: {
|
||||||
|
// with options
|
||||||
|
'/lifeto': {
|
||||||
|
target: "https://beta.lifeto.co/",
|
||||||
|
changeOrigin: true,
|
||||||
|
rewrite: (path) => path.replace(/^\/lifeto/, ''),
|
||||||
|
hostRewrite: "localhost:3001/lifeto",
|
||||||
|
cookieDomainRewrite:{
|
||||||
|
"*":"",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in New Issue