chore: add-vue3-performance-demo

This commit is contained in:
Petar Todorov 2022-07-05 17:38:50 +03:00
Родитель 91b002b490
Коммит 9dc23f2e44
21 изменённых файлов: 1315 добавлений и 0 удалений

23
vue3-grid-performance/.gitignore поставляемый Normal file
Просмотреть файл

@ -0,0 +1,23 @@
.DS_Store
node_modules
/dist
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

Просмотреть файл

@ -0,0 +1,24 @@
# vue3-grid-performance
## Project setup
```
npm install
```
### Compiles and hot-reloads for development
```
npm run serve
```
### Compiles and minifies for production
```
npm run build
```
### Lints and fixes files
```
npm run lint
```
### Customize configuration
See [Configuration Reference](https://cli.vuejs.org/config/).

Просмотреть файл

@ -0,0 +1,5 @@
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset'
]
}

Просмотреть файл

@ -0,0 +1,48 @@
{
"name": "vue3-grid-performance",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
"dependencies": {
"@progress/kendo-data-query": "^1.6.0",
"@progress/kendo-theme-default": "^5.5.0",
"@progress/kendo-vue-grid": "^3.4.1",
"@progress/kendo-vue-inputs": "^3.4.1",
"@progress/kendo-vue-intl": "^3.4.1",
"core-js": "^3.8.3",
"vue": "^3.2.13"
},
"devDependencies": {
"@babel/core": "^7.12.16",
"@babel/eslint-parser": "^7.12.16",
"@vue/cli-plugin-babel": "~5.0.0",
"@vue/cli-plugin-eslint": "~5.0.0",
"@vue/cli-service": "~5.0.0",
"eslint": "^7.32.0",
"eslint-plugin-vue": "^8.0.3"
},
"eslintConfig": {
"root": true,
"env": {
"node": true
},
"extends": [
"plugin:vue/vue3-essential",
"eslint:recommended"
],
"parserOptions": {
"parser": "@babel/eslint-parser"
},
"rules": {}
},
"browserslist": [
"> 1%",
"last 2 versions",
"not dead",
"not ie 11"
]
}

Двоичные данные
vue3-grid-performance/public/favicon.ico Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 4.2 KiB

Просмотреть файл

@ -0,0 +1,18 @@
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>

Просмотреть файл

@ -0,0 +1,26 @@
<template>
<img alt="Vue logo" src="./assets/logo.png">
<HelloWorld msg="Welcome to Your Vue.js App"/>
</template>
<script>
import HelloWorld from './components/HelloWorld.vue'
export default {
name: 'App',
components: {
HelloWorld
}
}
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>

Просмотреть файл

@ -0,0 +1,31 @@
<template>
<span :class="[currentTrends.negative ? 'red' : 'green']">
{{ formattedNumber }}
</span>
</template>
<script>
import { provideIntlService } from '@progress/kendo-vue-intl';
import { trends } from './utils';
export default {
inject: {
kendoIntlService: { default: null },
},
props: {
dataItem: Object,
field: String,
},
computed: {
currentTrends() {
return trends(this.dataItem);
},
formattedNumber: function () {
return provideIntlService(this).formatNumber(
this.dataItem[this.field],
this.dataItem[this.field] === 'Change' ? 'n5' : '0.##'
);
},
},
};
</script>

Просмотреть файл

@ -0,0 +1,290 @@
<template>
<div>
<Grid ref="grid" :style="{ height: '650px', width: '1300px' }" :data-items="processedData" :columns="columns"
:column-menu="showColumnMenu" :groupable="groupable" :group="group"
:column-virtualization="enableColumnVirtualization" :scrollable="'virtual'" :skip="skip" :take="take"
:total="processedData.length" @datastatechange="onDataStateChange" :data-item-key="'ID'"
:key="enableColumnVirtualization" :row-height="40">
<template v-slot:tickerTemplate="{ props }">
<td>
<ticker :data-item="props.dataItem" :field="props.field"></ticker>
</td>
</template>
<template v-slot:priceTemplate="{ props }">
<td class="text-center">
<price :data-item="props.dataItem" :field="props.field"></price>
</td>
</template>
<template v-slot:pricechangepercentageTemplate="{ props }">
<td class="text-center">
<pricechange :data-item="props.dataItem" :field="props.field"></pricechange>
</td>
</template>
<template v-slot:pricechangeTemplate="{ props }">
<td class="text-center">
<pricechange :data-item="props.dataItem" :field="props.field"></pricechange>
</td>
</template>
<template v-slot:highpriceTemplate="{ props }">
<td class="text-center">
<price :data-item="props.dataItem" :field="props.field"></price>
</td>
</template>
<template v-slot:lowpriceTemplate="{ props }">
<td class="text-center">
<price :data-item="props.dataItem" :field="props.field"></price>
</td>
</template>
<template v-slot:volumeTemplate="{ props }">
<td class="text-center">
<volume :data-item="props.dataItem" :field="props.field"></volume>
</td>
</template>
<template v-slot:ratingTemplate="{ props }">
<td class="text-center">
<rating :data-item="props.dataItem" :field="props.field"></rating>
</td>
</template>
</Grid>
</div>
</template>
<script>
import { Grid } from '@progress/kendo-vue-grid';
import { process } from '@progress/kendo-data-query';
import TickerComponent from './TickerComponent.vue';
import PriceComponent from './PriceComponent.vue';
import ChangePriceComponent from './ChangePriceComponent.vue';
import VolumeComponent from './VolumeComponent.vue';
import RatingComponent from './RatingComponent.vue';
const columnsData = [
{
field: 'Stock',
width: 110,
},
{
field: 'Price',
title: 'LAST',
width: 120,
cell: 'priceTemplate',
},
{
field: 'Change(%)',
title: 'CHG %',
width: 90,
cell: 'pricechangepercentageTemplate',
},
{
field: 'Change',
title: 'CHG',
width: 90,
cell: 'pricechangeTemplate',
},
{
field: 'High(D)',
title: 'High',
width: 110,
cell: 'highpriceTemplate',
},
{
field: 'Low(D)',
title: 'LOW',
width: 110,
cell: 'lowpriceTemplate',
},
{
field: 'Volume',
title: 'VOL',
width: 90,
cell: 'volumeTemplate',
},
{
title: 'RATING',
cell: 'ratingTemplate',
width: 130,
},
{ field: 'Country', width: 110 },
{ field: 'City', width: 100 },
{ field: 'Category', width: 120 },
{ field: 'Type', width: 100 },
{ field: 'Settlement', width: 100 },
{ field: 'Industry', width: 100, filterable: false },
{ field: 'Sector', width: 120, filterable: false, resizable: true },
{ field: 'SubSector', width: 100, filterable: false },
{ field: 'SectorType', width: 90, filterable: false },
{ field: 'SecurityCode', width: 170, filterable: false, resizable: true },
{ field: 'FullName', width: 60, filterable: false },
{ field: 'FitchRating', width: 60, filterable: false },
{ field: 'DBRSRating', width: 60, filterable: false },
{ field: 'Address', width: 90, filterable: false },
{ field: 'Currency', width: 60, filterable: false },
{ field: 'SecurityCode', width: 120, filterable: false },
{ field: 'SectorCode', width: 80, filterable: false },
{ field: 'Phone', width: 100, filterable: false },
{ field: 'Ticker', width: 60, filterable: false },
{ field: 'Cpn', width: 80, filterable: false },
{ field: 'Maturity', width: 120, filterable: false },
{ field: 'KRD_10YR', width: 110, filterable: false },
{ field: 'CUSIP', width: 90, filterable: false },
{ field: 'KRD_5YR', width: 50, filterable: false },
{ field: 'KRD_1YR', width: 80, filterable: false },
{ field: 'Industry', width: 100, filterable: false },
{ field: 'Sector', width: 120, filterable: false, resizable: true },
{ field: 'SubSector', width: 100, filterable: false },
{ field: 'SectorType', width: 90, filterable: false },
{ field: 'SecurityCode', width: 170, filterable: false, resizable: true },
{ field: 'FullName', width: 60, filterable: false },
{ field: 'FitchRating', width: 60, filterable: false },
{ field: 'DBRSRating', width: 60, filterable: false },
{ field: 'Address', width: 90, filterable: false },
{ field: 'Currency', width: 60, filterable: false },
{ field: 'SecurityCode', width: 120, filterable: false },
{ field: 'SectorCode', width: 80, filterable: false },
{ field: 'Phone', width: 100, filterable: false },
{ field: 'Ticker', width: 60, filterable: false },
{ field: 'Cpn', width: 80, filterable: false },
{ field: 'Maturity', width: 120, filterable: false },
{ field: 'KRD_10YR', width: 110, filterable: false },
{ field: 'CUSIP', width: 90, filterable: false },
{ field: 'KRD_5YR', width: 50, filterable: false },
{ field: 'KRD_1YR', width: 80, filterable: false },
{ field: 'Industry', width: 100, filterable: false },
{ field: 'Sector', width: 120, filterable: false, resizable: true },
{ field: 'SubSector', width: 100, filterable: false },
{ field: 'SectorType', width: 90, filterable: false },
{ field: 'SecurityCode', width: 170, filterable: false, resizable: true },
{ field: 'FullName', width: 60, filterable: false },
{ field: 'FitchRating', width: 60, filterable: false },
{ field: 'DBRSRating', width: 60, filterable: false },
{ field: 'Address', width: 90, filterable: false },
{ field: 'Currency', width: 60, filterable: false },
{ field: 'SecurityCode', width: 120, filterable: false },
{ field: 'SectorCode', width: 80, filterable: false },
{ field: 'Phone', width: 100, filterable: false },
{ field: 'Ticker', width: 60, filterable: false },
{ field: 'Cpn', width: 80, filterable: false },
{ field: 'Maturity', width: 120, filterable: false },
{ field: 'KRD_10YR', width: 110, filterable: false },
{ field: 'CUSIP', width: 90, filterable: false },
{ field: 'KRD_5YR', width: 50, filterable: false },
{ field: 'KRD_1YR', width: 80, filterable: false },
{ field: 'Industry', width: 100, filterable: false },
{ field: 'Sector', width: 120, filterable: false, resizable: true },
{ field: 'SubSector', width: 100, filterable: false },
{ field: 'SectorType', width: 90, filterable: false },
{ field: 'SecurityCode', width: 170, filterable: false, resizable: true },
{ field: 'FullName', width: 60, filterable: false },
{ field: 'FitchRating', width: 60, filterable: false },
{ field: 'DBRSRating', width: 60, filterable: false },
{ field: 'Address', width: 90, filterable: false },
{ field: 'Currency', width: 60, filterable: false },
{ field: 'SecurityCode', width: 120, filterable: false },
{ field: 'SectorCode', width: 80, filterable: false },
{ field: 'Phone', width: 100, filterable: false },
{ field: 'Ticker', width: 60, filterable: false },
{ field: 'Cpn', width: 80, filterable: false },
{ field: 'Maturity', width: 120, filterable: false },
{ field: 'KRD_10YR', width: 110, filterable: false },
{ field: 'CUSIP', width: 90, filterable: false },
{ field: 'KRD_5YR', width: 50, filterable: false },
{ field: 'KRD_1YR', width: 80, filterable: false },
{ field: 'Industry', width: 100, filterable: false },
{ field: 'Sector', width: 120, filterable: false, resizable: true },
{ field: 'SubSector', width: 100, filterable: false },
{ field: 'SectorType', width: 90, filterable: false },
{ field: 'SecurityCode', width: 170, filterable: false, resizable: true },
{ field: 'FullName', width: 60, filterable: false },
{ field: 'FitchRating', width: 60, filterable: false },
{ field: 'DBRSRating', width: 60, filterable: false },
{ field: 'Address', width: 90, filterable: false },
{ field: 'Currency', width: 60, filterable: false },
{ field: 'SecurityCode', width: 120, filterable: false },
{ field: 'SectorCode', width: 80, filterable: false },
{ field: 'Phone', width: 100, filterable: false },
{ field: 'Ticker', width: 60, filterable: false },
{ field: 'Cpn', width: 80, filterable: false },
{ field: 'Maturity', width: 120, filterable: false },
{ field: 'KRD_10YR', width: 110, filterable: false },
{ field: 'CUSIP', width: 90, filterable: false },
{ field: 'KRD_5YR', width: 50, filterable: false },
{ field: 'KRD_1YR', width: 80, filterable: false },
];
export default {
name: 'App',
components: {
Grid: Grid,
price: PriceComponent,
pricechange: ChangePriceComponent,
ticker: TickerComponent,
volume: VolumeComponent,
rating: RatingComponent,
},
props: {
gridData: Array,
showColumnMenu: Boolean,
groupable: Boolean,
enableColumnVirtualization: Boolean
},
mounted() {
this.columns = columnsData;
this.processedData = this.processData();
},
watch: {
gridData() {
this.processedData = this.processData();
}
},
data() {
return {
columns: [],
skip: 0,
take: 25,
group: [],
processedData: []
};
},
methods: {
pageChange(event) {
this.skip = event.page.skip;
this.take = event.page.take;
},
onDataStateChange(event) {
console.log("DataStateChange")
this.createAppState(event.data);
},
createAppState(dataState) {
this.group = dataState.group;
this.take = dataState.take;
this.skip = dataState.skip;
this.processedData = this.processData();
},
processData() {
return process(this.gridData, {
take: this.take,
skip: this.skip,
group: this.group,
});
}
},
};
</script>
<style>
td.text-center {
text-align: center;
}
.red {
color: #d9534f;
}
.green {
color: #37b400;
}
.text-bold {
font-weight: 600;
}
</style>

Просмотреть файл

@ -0,0 +1,168 @@
<template>
<div>
<div class='row m-3'>
<div class='col-3'>
<div :style="{ minWidth: '140px' }">
<Switch @change="onColumnMenuChange" :checked="showColumnMenu" />
&nbsp;Column Menu
</div>
</div>
<div class='col-3'>
<div :style="{ minWidth: '140px' }">
<Switch @change="onColumnVirtualizationChange" :checked="enableColumnVirtualization" />
&nbsp;Column Virtualization
</div>
</div>
<div class='col-3'>
<div :style="{ minWidth: '140px' }">
<Switch @change="onGroupingChange" />
&nbsp;Grouping
</div>
</div>
</div>
<div class='row m-3'>
<div class='col-3'>
<label>
Records: {{ currentValue }} <br />
<Slider :min="0" :max="1000000" :step="VOLUME_STEP" :defaultValue="VOLUME_INITIAL" @change="onVolumeChange"
:disabled="liveUpdating" />
</label>
</div>
<div class='col-3'>
<label>
Frequency: {{ currentFrequency }} ms <br />
<Slider :min="100" :max="3000" :step="10" :defaultValue="FREQUENCY_STEP" @change="onFrequencyChange"
:disabled="liveUpdating" />
</label>
</div>
</div>
<div class='row m-3'>
<div class='col'>
<ButtonGroup>
<KButton v-for="(btn, key) in buttonsData" :toggable="true" :icon="btn.icon" :key="key"
:style="{ width: '200px' }" :name="key" @click="onButtonGroupClick"> {{ btn.title }}</KButton>
</ButtonGroup>
</div>
</div>
</div>
<grid :grid-data="data" :show-column-menu="showColumnMenu" :groupable="groupable"
:enable-column-virtualization="enableColumnVirtualization"></grid>
</template>
<script>
// import { RadioGroup } from '@progress/kendo-vue-inputs';
import Grid from './Grid.vue';
import { Slider, Switch } from "@progress/kendo-vue-inputs";
import { Button, ButtonGroup } from "@progress/kendo-vue-buttons";
import { FinancialData } from './financial-data';
import { updateRandomPrices, updateAllPrices } from './services';
export default {
components: {
Grid,
Slider, Switch,
KButton: Button,
ButtonGroup
},
data() {
return {
data: [],
intervalRef: null,
showColumnMenu: true,
enableColumnVirtualization: true,
liveUpdating: false,
VOLUME_STEP: 1000,
VOLUME_INITIAL: 10000,
FREQUENCY_STEP: 100,
currentFrequency: 100,
currentValue: 100,
groupable: false,
buttonsData: {
live: {
title: 'Live Prices',
icon: 'refresh'
},
liveAll: {
title: 'Live All Prices',
icon: 'refresh'
},
stop: {
title: 'Stop',
icon: 'stop'
}
},
}
},
created() {
this.generateNewData();
},
methods: {
generateNewData() {
const financialData = new FinancialData();
this.data = financialData.generateData(this.currentValue);
},
onFrequencyChange(e) {
this.currentFrequency =
Math.floor(e.value / this.FREQUENCY_STEP) * this.FREQUENCY_STEP
},
startDataGeneration() {
this.dataReset();
this.startLiveUpdate(this.currentFrequency);
},
dataReset() {
clearInterval(this.intervalRef);
},
startLiveUpdate(interval) {
clearInterval(this.intervalRef);
this.intervalRef = setInterval(() => {
this.gridData = updateRandomPrices(this.data);
}, interval);
},
startLiveUpdateAll(interval) {
console.log(this.intervalRef)
clearInterval(this.intervalRef);
this.intervalRef = setInterval(() => {
this.gridData = updateAllPrices(this.data);
}, interval);
},
stopLiveUpdate() {
clearInterval(this.intervalRef);
},
onColumnMenuChange() {
this.showColumnMenu = !this.showColumnMenu;
},
onColumnVirtualizationChange() {
this.enableColumnVirtualization = !this.enableColumnVirtualization;
},
onGroupingChange() {
this.groupable = !this.groupable;
},
onVolumeChange(e) {
this.currentValue = Math.floor(e.value / this.VOLUME_STEP) * this.VOLUME_STEP;
this.generateNewData();
},
onButtonGroupClick(event) {
const currentButton = event.currentTarget.name;
this.selectedButton = currentButton;
switch (currentButton) {
case "live":
this.startLiveUpdate(this.currentFrequency);
this.liveUpdating = true;
break;
case "liveAll":
this.startLiveUpdateAll(this.currentFrequency);
this.liveUpdating = true;
break;
case "stop":
this.stopLiveUpdate();
this.liveUpdating = false;
break;
default:
}
}
},
};
</script>

Просмотреть файл

@ -0,0 +1,25 @@
<template>
<span>{{ formattedNumber }} </span>
</template>
<script>
import { provideIntlService } from '@progress/kendo-vue-intl';
export default {
inject: {
kendoIntlService: { default: null },
},
props: {
dataItem: Object,
field: String,
},
computed: {
formattedNumber: function () {
return provideIntlService(this).formatNumber(
this.dataItem[this.field],
'n5'
);
},
},
};
</script>

Просмотреть файл

@ -0,0 +1,36 @@
<template>
<div
:class="[
rating === 'Buy' || rating === 'Strong Buy'
? 'green text-bold'
: 'red text-bold',
]"
>
{{ rating }}
</div>
</template>
<script>
import { trends } from './utils';
export default {
props: {
dataItem: Object,
field: String,
},
computed: {
rating() {
const currentTrends = trends(this.$props.dataItem);
return currentTrends.strongNegative
? 'Strong Sell'
: currentTrends.negative
? 'Sell'
: currentTrends.positive
? 'Buy'
: currentTrends.strongPositive
? 'Strong Buy'
: 'Neutral';
},
},
};
</script>

Просмотреть файл

@ -0,0 +1,24 @@
<template>
<div>
<img
:alt="`${dataItem.Ticker} icon`"
:src="`./${dataItem.Ticker}.png`"
/><span
:style="{
color: '#4B5FFA',
marginLeft: '5px',
fontWeight: 'bold',
}"
>{{ dataItem[field] }}</span
>
</div>
</template>
<script>
export default {
props: {
dataItem: Object,
field: String,
},
};
</script>

Просмотреть файл

@ -0,0 +1,21 @@
<template>
<span> {{ formattedNumber }} </span>
</template>
<script>
export default {
inject: {
kendoIntlService: { default: null },
},
props: {
dataItem: Object,
field: String,
},
computed: {
formattedNumber: function () {
return this.dataItem[this.field];
},
},
};
</script>

Двоичные данные
vue3-grid-performance/src/assets/logo.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 6.7 KiB

Просмотреть файл

@ -0,0 +1,58 @@
<template>
<div class="hello">
<h1>{{ msg }}</h1>
<p>
For a guide and recipes on how to configure / customize this project,<br>
check out the
<a href="https://cli.vuejs.org" target="_blank" rel="noopener">vue-cli documentation</a>.
</p>
<h3>Installed CLI Plugins</h3>
<ul>
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel" target="_blank" rel="noopener">babel</a></li>
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint" target="_blank" rel="noopener">eslint</a></li>
</ul>
<h3>Essential Links</h3>
<ul>
<li><a href="https://vuejs.org" target="_blank" rel="noopener">Core Docs</a></li>
<li><a href="https://forum.vuejs.org" target="_blank" rel="noopener">Forum</a></li>
<li><a href="https://chat.vuejs.org" target="_blank" rel="noopener">Community Chat</a></li>
<li><a href="https://twitter.com/vuejs" target="_blank" rel="noopener">Twitter</a></li>
<li><a href="https://news.vuejs.org" target="_blank" rel="noopener">News</a></li>
</ul>
<h3>Ecosystem</h3>
<ul>
<li><a href="https://router.vuejs.org" target="_blank" rel="noopener">vue-router</a></li>
<li><a href="https://vuex.vuejs.org" target="_blank" rel="noopener">vuex</a></li>
<li><a href="https://github.com/vuejs/vue-devtools#vue-devtools" target="_blank" rel="noopener">vue-devtools</a></li>
<li><a href="https://vue-loader.vuejs.org" target="_blank" rel="noopener">vue-loader</a></li>
<li><a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">awesome-vue</a></li>
</ul>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
props: {
msg: String
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h3 {
margin: 40px 0 0;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
</style>

Просмотреть файл

@ -0,0 +1,422 @@
/* tslint:disable */
export const COUNTRIES = [
{
"Country": "America",
"Cities": ["New York", "Chicago", "Dallas", "Los Angeles", "Boston", "Austin", "Florida"]
},
{
"Country": "Germany",
"Cities": ["Dortmund", "Stuttgard", "München", "Berlin", "Hamburg"]
},
{
"Country": "United Kingdom",
"Cities": ["Manchester", "Liverpool", "Sheffield", "Leeds", "Birmingham", "London"]
},
{
"Country": "Italy",
"Cities": ["Milano", "Firenze", "Roma", "Napoli", "Catania", "Palermo", "Venezia"]
},
{
"Country": "France",
"Cities": ["Nantes", "Paris", "Toulouse", "Montpellier", "Marseille", "Nice", "Lyon"]
},
{
"Country": "Spain",
"Cities": ["Madrid", "Zaragoza", "València", "Murcia", "Málaga", "Barcelona"]
},
]
export const DealType = [
"Buy", "Sell"
]
export const Stock = [
"OX", "USD", "SHIB", "TETHERUS", "XEC", "18C", "BITCOIN", "ETHERIUM", "INCH", "BUSD", "GBP", "EUR"
]
export const Settlement = [
"Deliverable", "Cash"
]
export const MOCKFINANCEDATA = [
{
"Category": "Test Data",
"Industry": "Informational technology",
"Sector": "Technology",
"SubSector": "Machine learning",
"SectorType": "PRIVATE",
// tslint:disable-next-line:object-literal-sort-keys
"Type": "DEFAULT",
"FullName": "Progress Software",
"Location": "Boston",
"FitchRating": "Private",
"DBRSRating": "Private",
"Currency": "USD",
"SecurityCode": "12345678910 IT",
"SectorCode": "IT",
"CUSIP": "987654321",
"Ticker": "PGRS",
"Cpn": "40.875",
"Maturity": "1981",
"KRD_5YR": 2.00006,
"Address": "Bedford",
"Phone": "+1 111 111 111",
"Number": 28.302,
"KRD_10YR": 0,
"KRD_1YR": -0.00187,
"SpecialCode": null
}];
export const DATA = [
{
"Ticker": "OX",
"Spread": 0.01,
"Open Price": 1281.10,
"Price": 1280.7317,
"Buy": 1280.7267,
"Sell": 1280.7367,
"Change": -0.3683,
"Change(%)": -0.0287,
"Volume": 48387,
"High(D)": 1289.50,
"Low(D)": 1279.10,
"High(Y)": 1306,
"Low(Y)": 1047.20,
"Start(Y)": 1176.60,
"Change On Year(%)": 8.8502
},
{
"Ticker": "USD",
"Type": "Silver",
"Spread": 0.01,
"Open Price": 17.43,
"Price": 17.42,
"Buy": 17.43,
"Sell": 17.43,
"Change": -0.01,
"Change(%)": -0.0574,
"Volume": 11720,
"High(D)": 17.51,
"Low(D)": 17.37,
"High(Y)": 18.06,
"Low(Y)": 13.73,
"Start(Y)": 15.895,
"Change On Year(%)": 9.5942
},
{
"Ticker": "SHIB",
"Type": "Copper",
"Spread": 0.02,
"Open Price": 2.123,
"Price": 2.113,
"Buy": 2.123,
"Sell": 2.123,
"Change": -0.01,
"Change(%)": -0.471,
"Volume": 28819,
"High(D)": 2.16,
"Low(D)": 2.11,
"High(Y)": 2.94,
"Low(Y)": 1.96,
"Start(Y)": 2.45,
"Change On Year(%)": -13.7551
},
{
"Ticker": "TETHERUS",
"Type": "Platinum",
"Spread": 0.01,
"Open Price": 1071.60,
"Price": 1071.0993,
"Buy": 1071.0943,
"Sell": 1071.1043,
"Change": -0.5007,
"Change(%)": -0.0467,
"Volume": 3039,
"High(D)": 1081.20,
"Low(D)": 1070.50,
"High(Y)": 1120.60,
"Low(Y)": 812.40,
"Start(Y)": 966.50,
"Change On Year(%)": 10.8225
},
{
"Ticker": "XEC",
"Type": "Palladium",
"Spread": 0.01,
"Open Price": 600.55,
"Price": 601.0005,
"Buy": 600.9955,
"Sell": 601.0055,
"Change": 0.4505,
"Change(%)": 0.075,
"Volume": 651,
"High(D)": 607.20,
"Low(D)": 598.40,
"High(Y)": 690,
"Low(Y)": 458.6,
"Start(Y)": 574.3,
"Change On Year(%)": 4.6492
},
{
"Ticker": "18C",
"Type": "Oil",
"Spread": 0.015,
"Open Price": 45.54,
"Price": 45.7899,
"Buy": 45.7824,
"Sell": 45.7974,
"Change": 0.2499,
"Change(%)": 0.5487,
"Volume": 107196,
"High(D)": 45.94,
"Low(D)": 45.00,
"High(Y)": 65.28,
"Low(Y)": 30.79,
"Start(Y)": 48.035,
"Change On Year(%)": -4.6739
},
{
"Ticker": "BITCOIN",
"Type": "Brent",
"Spread": 0.01,
"Open Price": 46.06,
"Price": 46.05,
"Buy": 46.06,
"Sell": 46.06,
"Change": -0.01,
"Change(%)": -0.0217,
"Volume": 59818,
"High(D)": 46.48,
"Low(D)": 45.60,
"High(Y)": 71.14,
"Low(Y)": 30.02,
"Start(Y)": 50.58,
"Change On Year(%)": -8.9561
},
{
"Ticker": "ETHERIUM",
"Type": "Natural Gas",
"Spread": 0.02,
"Open Price": 2.094,
"Price": 2.104,
"Buy": 2.094,
"Sell": 2.094,
"Change": 0.01,
"Change(%)": 0.4776,
"Volume": 2783,
"High(D)": 2.11,
"Low(D)": 2.09,
"High(Y)": 3.20,
"Low(Y)": 1.84,
"Start(Y)": 2.52,
"Change On Year(%)": -16.5079
},
{
"Ticker": "INCH",
"Type": "RBOB Gas",
"Spread": 0.015,
"Open Price": 1.5086,
"Price": 1.9532,
"Buy": 1.9457,
"Sell": 1.9607,
"Change": 0.4446,
"Change(%)": 29.4686,
"Volume": 2646,
"High(D)": 1.9532,
"Low(D)": 1.50,
"High(Y)": 2.05,
"Low(Y)": 1.15,
"Start(Y)": 1.60,
"Change On Year(%)": 22.0727
},
{
"Ticker": "BUSD",
"Type": "Diesel",
"Spread": 0.015,
"Open Price": 1.3474,
"Price": 1.3574,
"Buy": 1.3474,
"Sell": 1.3474,
"Change": 0.01,
"Change(%)": 0.7422,
"Volume": 2971,
"High(D)": 1.36,
"Low(D)": 1.34,
"High(Y)": 2.11,
"Low(Y)": 0.92,
"Start(Y)": 1.515,
"Change On Year(%)": -10.4026
},
{
"Ticker": "GBP",
"Type": "Ethanol",
"Spread": 0.01,
"Open Price": 1.512,
"Price": 2.7538,
"Buy": 2.7488,
"Sell": 2.7588,
"Change": 1.2418,
"Change(%)": 82.1323,
"Volume": 14,
"High(D)": 2.7538,
"Low(D)": 1.1168,
"High(Y)": 2.7538,
"Low(Y)": 1.1168,
"Start(Y)": 1.475,
"Change On Year(%)": 86.7011
},
{
"Ticker": "EUR",
"Type": "Uranium",
"Spread": 0.02,
"Open Price": 27.55,
"Price": 27.58,
"Buy": 27.55,
"Sell": 27.55,
"Change": 0.03,
"Change(%)": 0.1089,
"Volume": 12,
"High(D)": 27.55,
"Low(D)": 27.55,
"High(Y)": 29.32,
"Low(Y)": 21.28,
"Start(Y)": 25.30,
"Change On Year(%)": 9.0119
}
];
/* tslint:enable */
export class FinancialData {
generateData(count) {
console.time('generateData');
const currData = [];
for (let i = 0; i < count; i++) {
const rand = Math.floor(Math.random() * Math.floor(DATA.length));
const dataObj = Object.assign({}, DATA[rand]);
dataObj.Settlement = Settlement[this.generateRandomNumber(0, 1)];
dataObj.Stock = Stock[this.generateRandomNumber(0, 11)];
const country = COUNTRIES[this.generateRandomNumber(0, 5)];
dataObj.Country = country.Country;
dataObj.City = this.randomizeCity(country);
// for (let y = 0; y < 80; y++) {
// dataObj["Text" + y] = "Text";
// }
for (const mockData of MOCKFINANCEDATA) {
for (const prop in mockData) {
if (Object.prototype.hasOwnProperty.call(mockData, prop)) {
dataObj[prop] = mockData[prop];
}
}
}
dataObj.ID = i;
this.randomizeObjectData(dataObj);
currData.push(dataObj);
}
console.timeEnd('generateData');
return currData;
}
updateAllPrices(data) {
const currData = [];
for (const dataRow of data) {
const dataObj = Object.assign({}, dataRow);
this.randomizeObjectData(dataObj);
currData.push(dataObj);
}
return currData;
}
updateRandomPrices(data) {
const currData = data.slice(0, data.length + 1);
// let y = 0;
for (let i = Math.round(Math.random() * 10); i < data.length; i += Math.round(Math.random() * 10)) {
const dataObj = Object.assign({}, data[i]);
this.randomizeObjectData(dataObj);
currData[i] = dataObj;
// y++;
}
// return {data: currData, recordsUpdated: y };
return currData;
}
updateRandomPrices2(data) {
const currData = data.slice(0, data.length + 1);
let y = 0;
for (let i = Math.round(Math.random() * 10); i < data.length; i += Math.round(Math.random() * 10)) {
const dataObj = Object.assign({}, data[i]);
this.randomizeObjectData(dataObj);
currData[i] = dataObj;
y++;
}
return { data: currData, recordsUpdated: y };
}
randomizeObjectData(dataObj) {
const changeP = 'Change(%)';
const res = this.generateNewPrice(dataObj.Price);
dataObj.Change = res.Price - dataObj.Price;
dataObj.Price = res.Price;
dataObj[changeP] = res.ChangePercent;
}
generateNewPrice(oldPrice) {
const rnd = parseFloat(Math.random().toFixed(2));
const volatility = 2;
let newPrice = 0;
let changePercent = 2 * volatility * rnd;
if (changePercent > volatility) {
changePercent -= (2 * volatility);
}
const changeAmount = oldPrice * (changePercent / 100);
newPrice = oldPrice + changeAmount;
const result = { Price: 0, ChangePercent: 0 };
result.Price = parseFloat(newPrice.toFixed(2));
result.ChangePercent = parseFloat(changePercent.toFixed(2));
return result;
}
generateRandomNumber(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
randomizeCity(country) {
let city;
switch (country.Country) {
case 'America': {
city = country.Cities[this.generateRandomNumber(0, 6)];
break;
}
case 'Germany': {
city = country.Cities[this.generateRandomNumber(0, 4)];
break;
}
case 'United Kingdom': {
city = country.Cities[this.generateRandomNumber(0, 5)];
break;
}
case 'Italy': {
city = country.Cities[this.generateRandomNumber(0, 6)];
break;
}
case 'France': {
city = country.Cities[this.generateRandomNumber(0, 6)];
break;
}
case 'Spain': {
city = country.Cities[this.generateRandomNumber(0, 5)];
break;
}
default:
break;
}
return city;
}
}

Просмотреть файл

@ -0,0 +1,5 @@
import { createApp } from 'vue'
import App from './MainComponent.vue'
import '@progress/kendo-theme-default'
createApp(App).mount('#app')

Просмотреть файл

@ -0,0 +1,52 @@
export const updateRandomPrices = (data) => {
const newData = data.slice();
for (
let i = Math.round(Math.random() * 10);
i < newData.length;
i += Math.round(Math.random() * 10)
) {
randomizeObjectData(newData[i]);
}
return newData;
};
export const updateAllPrices = (data) => {
const newData = data.slice();
for (const dataRow of newData) {
randomizeObjectData(dataRow);
}
return newData;
};
export const randomizeObjectData = (dataObj) => {
const changeP = 'Change(%)';
const res = generateNewPrice(dataObj.Price);
dataObj.Change = res.Price - dataObj.Price;
dataObj.Price = res.Price;
dataObj[changeP] = res.ChangePercent;
};
export const generateNewPrice = (oldPrice) => {
let rnd = Math.random();
rnd = Math.round(rnd * 100) / 100;
const volatility = 2;
let newPrice = 0;
let changePercent = 2 * volatility * rnd;
if (changePercent > volatility) {
changePercent -= 2 * volatility;
}
const changeAmount = oldPrice * (changePercent / 100);
newPrice = oldPrice + changeAmount;
newPrice = Math.round(newPrice * 100) / 100;
const result = {
Price: 0,
ChangePercent: 0,
};
changePercent = Math.round(changePercent * 100) / 100;
result.Price = newPrice;
result.ChangePercent = changePercent;
return result;
};

Просмотреть файл

@ -0,0 +1,18 @@
export const negative = (rowData) => {
return rowData['Change(%)'] < 0;
};
export const positive = (rowData) => {
return rowData['Change(%)'] > 0;
};
export const changeNegative = (rowData) => {
return rowData['Change(%)'] < 0 && rowData['Change(%)'] > -1;
};
export const changePositive = (rowData) => {
return rowData['Change(%)'] > 0 && rowData['Change(%)'] < 1;
};
export const strongPositive = (rowData) => {
return rowData['Change(%)'] >= 1;
};
export const strongNegative = (rowData) => {
return rowData['Change(%)'] <= -1;
};

Просмотреть файл

@ -0,0 +1,21 @@
import {
changeNegative,
changePositive,
negative,
positive,
strongNegative,
strongPositive,
} from './trends';
export const trends = (dataItem) => {
return {
changeNegative: changeNegative(dataItem),
changePositive: changePositive(dataItem),
negative: negative(dataItem),
positive: positive(dataItem),
strongNegative: strongNegative(dataItem),
strongPositive: strongPositive(dataItem),
};
};