232 lines
9.7 KiB
Django/Jinja
232 lines
9.7 KiB
Django/Jinja
{% extends "nav" %}
|
|
{% block title %}交易{% endblock title %}
|
|
{% block container %}
|
|
<div class="">
|
|
<table class="table is-fullwidth is-bordered is-hoverable">
|
|
<thead>
|
|
<tr style="position: sticky; top: 0; background-color: white;">
|
|
<th style="width: fit-content">代码</th>
|
|
<th style="width: fit-content">名称</th>
|
|
<th>买入日期</th>
|
|
<th>买入价</th>
|
|
<th>买量</th>
|
|
<th><abbr title="买入金额+佣金+过户费等各种手续费">实际买入</abbr></th>
|
|
<th>卖出日期</th>
|
|
<th>卖出价</th>
|
|
<th>卖量</th>
|
|
<th><abbr title="卖出金额-佣金-各种手续费">实际卖出</abbr></th>
|
|
<th>收益: {{ gain|float|round(2) }} ({{ ((pct|float)*100)|round(2) }} %)</th>
|
|
<th>实际收益: {{ net_gain|float|round(2) }} ({{ ((net_pct|float)*100)|round(2) }} %)</th>
|
|
<th>操作</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for r in rows %}
|
|
{% if r.split_pieces %}
|
|
{% for p in r.split_pieces %}
|
|
<tr>
|
|
{% if loop.index == 1 %}
|
|
<td rowspan="{{ loop.length }}" style="vertical-align: middle;">{{ r.code|string|padLeft(6) }}</td>
|
|
<td rowspan="{{ loop.length }}" style="vertical-align: middle;"><a href="/?name={{ r.name }}">{{ r.name }}</a></td>
|
|
<td rowspan="{{ loop.length }}" style="vertical-align: middle;">{{ r.date }}</td>
|
|
<td rowspan="{{ loop.length }}" style="vertical-align: middle;">{{ r.buy|float|round(4) }}</td>
|
|
<td rowspan="{{ loop.length }}" style="vertical-align: middle;">{{ r.volume }}</td>
|
|
<td rowspan="{{ loop.length }}" style="vertical-align: middle;">{{ r.net_buy|float|round(2) }}</td>
|
|
<td class="{% if not r.is_done %}is-warning{% endif %}">{{ p.date or "" }}</td>
|
|
<td>{{ (p.sell or 0)|float|round(4) }}</td>
|
|
<td>{{ p.volume or "" }}</td>
|
|
<td>{{ (p.net_sell or 0)|float|round(2) }}</td>
|
|
<td rowspan="{{ loop.length }}" style="vertical-align: middle;" class="{% if r.gain|float > 0 %}is-success{% else %}is-danger{% endif %}">
|
|
{% if r.gain %}
|
|
{{ r.gain|float|round(2) }} ({{ ((r.gain|float)/(r.buy|float)/(r.volume|float)*100)|round(2) }}%)
|
|
{% else %}
|
|
-
|
|
{% endif %}
|
|
</td>
|
|
<td rowspan="{{ loop.length }}" style="vertical-align: middle;" class="{% if r.net_gain|float > 0 %}is-success{% else %}is-danger{% endif %}">
|
|
{% if r.net_gain %}
|
|
{{ r.net_gain|float|round(2) }} ({{ ((r.net_gain|float)/(r.net_buy|float)*100)|round(2) }}%)
|
|
{% else %}
|
|
-
|
|
{% endif %}
|
|
</td>
|
|
<td rowspan="{{ loop.length }}" style="vertical-align: middle;">
|
|
<a row-id="{{ r.id }}" class="modify-row">改</a> <a row-id="{{ r.id }}" class="delete-row has-text-danger">删</a>
|
|
</td>
|
|
{% else %}
|
|
<td>{{ p.date or "" }}</td>
|
|
<td>{{ p.sell or "" }}</td>
|
|
<td>{{ p.volume or "" }}</td>
|
|
<td>{{ p.net_sell or "" }}</td>
|
|
{% endif %}
|
|
</tr>
|
|
{% endfor %}
|
|
{% else %}
|
|
<tr>
|
|
<td>{{ r.code|string|padLeft(6) }}</td>
|
|
<td><a href="/?name={{ r.name }}">{{ r.name }}</a></td>
|
|
<td>{{ r.date }}</td>
|
|
<td>{{ r.buy|float|round(4) }}</td>
|
|
<td>{{ r.volume }}</td>
|
|
<td>{{ r.net_buy|float|round(2) }}</td>
|
|
<td class="is-warning"></td>
|
|
<td class="is-warning"></td>
|
|
<td class="is-warning"></td>
|
|
<td class="is-warning"></td>
|
|
<td class="is-warning">-</td>
|
|
<td class="is-warning">-</td>
|
|
<td><a row-id="{{ r.id }}" class="modify-row">改</a> <a row-id="{{ r.id }}" class="delete-row has-text-danger">删</a>
|
|
</td>
|
|
</tr>
|
|
{% endif %}
|
|
{% endfor %}
|
|
<tr class="is-light" style="position: sticky; bottom: 0;">
|
|
<td colspan="14" style="text-align: center">
|
|
<a id="addEntry">添加</a>
|
|
<span> </span>
|
|
<a id="filterEntry">高级筛选</a>
|
|
<span> </span>
|
|
<a id="ftx">快速记录</a>
|
|
<span> </span>
|
|
<a href="/?is_done=false">未清仓</a>
|
|
<span> </span>
|
|
<a href="/?is_done=true">已清仓</a>
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<div class="modal" id="modal">
|
|
<div class="modal-background"></div>
|
|
<div class="modal-content" id="modal-content" style="width: 50rem;">
|
|
<!-- Any other Bulma elements you want -->
|
|
</div>
|
|
<button class="modal-close is-large" aria-label="close"></button>
|
|
</div>
|
|
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
// Functions to open and close a modal
|
|
function openModal($el) {
|
|
$el.classList.add('is-active');
|
|
}
|
|
|
|
function closeModal($el) {
|
|
$el.classList.remove('is-active');
|
|
}
|
|
|
|
function closeAllModals() {
|
|
(document.querySelectorAll('.modal') || []).forEach(($modal) => {
|
|
closeModal($modal);
|
|
});
|
|
}
|
|
|
|
document.getElementById('addEntry').addEventListener('click', async () => {
|
|
const $target = document.getElementById("modal");
|
|
const response = await fetch("/add");
|
|
if (!response.ok) {
|
|
return;
|
|
}
|
|
document.getElementById('modal-content').innerHTML = await response.text();
|
|
openModal($target);
|
|
injectCodeEvent();
|
|
});
|
|
|
|
document.getElementById('ftx').addEventListener('click', async () => {
|
|
const $target = document.getElementById("modal");
|
|
const response = await fetch("/ftx");
|
|
if (!response.ok) {
|
|
return;
|
|
}
|
|
document.getElementById('modal-content').innerHTML = await response.text();
|
|
openModal($target);
|
|
injectCodeEvent();
|
|
});
|
|
|
|
(document.querySelectorAll('.modify-row') || []).forEach(($cl) => {
|
|
$cl.addEventListener('click', async () => {
|
|
const rowId = $cl.attributes.getNamedItem("row-id").value;
|
|
const $target = document.getElementById("modal");
|
|
const response = await fetch(`/tx/${rowId}`);
|
|
if (!response.ok) {
|
|
return;
|
|
}
|
|
document.getElementById('modal-content').innerHTML = await response.text();
|
|
openModal($target);
|
|
});
|
|
});
|
|
|
|
(document.querySelectorAll('.delete-row') || []).forEach(($cl) => {
|
|
$cl.addEventListener('click', async () => {
|
|
const rowId = $cl.attributes.getNamedItem("row-id").value;
|
|
if (confirm(`删除${$cl.parentElement.parentElement.innerText}吗?`) !== true) {
|
|
return;
|
|
}
|
|
const response = await fetch(`/tx/${rowId}`, {method: 'DELETE'});
|
|
alert(`${response.ok ? "OK" : "Fail"}: ${response.status}`);
|
|
if (response.ok) {
|
|
$cl.parentElement.parentElement.remove();
|
|
}
|
|
});
|
|
});
|
|
|
|
// Add a click event on various child elements to close the parent modal
|
|
(document.querySelectorAll('.modal-background, .modal-close, .modal-card-head .delete, .modal-card-foot .button') || []).forEach(($close) => {
|
|
const $target = $close.closest('.modal');
|
|
|
|
$close.addEventListener('click', () => {
|
|
closeModal($target);
|
|
});
|
|
});
|
|
|
|
// Add a keyboard event to close all modals
|
|
document.addEventListener('keydown', (event) => {
|
|
if (event.key === "Escape") {
|
|
closeAllModals();
|
|
}
|
|
});
|
|
|
|
});
|
|
function addRow() {
|
|
const this_row = document.getElementById('add-row-line');
|
|
this_row.parentElement.insertBefore(this_row.previousElementSibling.cloneNode(true), this_row);
|
|
for (let i = 0; i < this_row.parentElement.childElementCount-1; i++) {
|
|
let row = this_row.parentElement.children[i];
|
|
row.children[0].children[0].name = `split_pieces[${i}].date`;
|
|
row.children[1].children[0].name = `split_pieces[${i}].sell`;
|
|
row.children[2].children[0].name = `split_pieces[${i}].volume`;
|
|
row.children[3].children[0].name = `split_pieces[${i}].net_sell`;
|
|
}
|
|
}
|
|
|
|
function deleteRow(el) {
|
|
const this_row = el.parentElement.parentElement; // tr
|
|
const first_data_row = this_row.parentElement.firstElementChild;
|
|
const last_data_row = this_row.parentElement.lastElementChild.previousElementSibling; // last is add button
|
|
if (first_data_row === last_data_row) {
|
|
console.log("The only row");
|
|
} else {
|
|
this_row.parentElement.removeChild(this_row);
|
|
}
|
|
}
|
|
|
|
function injectCodeEvent() {
|
|
const $target = document.getElementById('code');
|
|
$target.addEventListener('blur', (event) => {
|
|
fetch(`/stock/${$target.value}`).then((resp) => {
|
|
return resp.text();
|
|
}).then((text) => {
|
|
if (text.trim().length > 0) {
|
|
const $update = document.getElementById('name');
|
|
$update.value = text;
|
|
}
|
|
})
|
|
});
|
|
}
|
|
|
|
|
|
</script>
|
|
|
|
{% endblock container %}
|