1
0
Files
coconut-leaf/frontend/src/views/Collection.vue

255 lines
8.4 KiB
Vue
Raw Normal View History

2026-06-30 14:08:51 +08:00
<script setup lang="ts">
import { computed, onMounted, ref } from 'vue';
import { useTokenStore } from '@/stores/token';
import MessageBox from '@/components/MessageBox.vue';
import {
addOwn as apiCollectionAddOwn,
getFullOwn as apiCollectionGetFullOwn,
getSharing as apiCollectionGetSharing,
getDetailOwn as apiCollectionGetDetailOwn,
type CollectionRow,
deleteOwn as apiCollectionDeleteOwn,
updateOwn as apiCollectionUpdateOwn,
addSharing as apiCollectionAddSharing,
deleteSharing as apiCollectionDeleteSharing
} from '@/api/collection';
import OwnedItem from '@/components/collection/OwnedItem.vue';
import SharingItem from '@/components/collection/SharingItem.vue';
const ownedItems = ref<Map<string, CollectionRow>>(new Map());
const sharingItems = ref<string[]>([]);
const sharingItemUuid = ref<string | null>(null);
const isSharingItem = computed(() => {
return typeof sharingItemUuid.value === 'string';
})
const pendingOwnedItemName = ref<string>("");
const pendingSharingItemUsername = ref<string>("");
const token = useTokenStore();
const messagebox = ref<InstanceType<typeof MessageBox>>();
onMounted(async () => {
await refreshOwnedItem();
})
const refreshOwnedItem = async () => {
// clean owned items
ownedItems.value.clear();
// Fetch data
let rv = await apiCollectionGetFullOwn(token.currentToken);
if (typeof rv === 'undefined') {
console.error("fail to fetch owned collection");
return;
}
// Add into dict for rendering
for (let item of rv) {
ownedItems.value.set(item[0], item);
}
// clean sharing status
sharingItemUuid.value = null;
await refreshSharingItem();
}
const addOwnedItem = async () => {
let new_name = pendingOwnedItemName.value;
if (new_name === '') {
return
}
// first add it
let rv = await apiCollectionAddOwn(token.currentToken, new_name);
if (typeof rv === 'undefined') {
messagebox.value?.show("An add operation failed. It may caused by wrong arguments. Refreshing page may fix system problem. Before refreshing page, please backup all your unsaved data.");
return;
}
// second get its detail
let rv2 = await apiCollectionGetDetailOwn(token.currentToken, rv);
if (typeof rv2 === 'undefined') {
messagebox.value?.show("A get operation failed. It may caused by server internal error or your limited permission. Refreshing page may fix system problem. Before refreshing page, please backup all your unsaved data.");
return;
}
// render to list
ownedItems.value.set(rv2[0], rv2);
}
const deleteOwnedItem = async (uuid: string) => {
let rv = await apiCollectionDeleteOwn(token.currentToken, uuid, ownedItems.value.get(uuid)![2]);
if (rv) {
messagebox.value?.show("A delete operation failed. It may caused by no matched item. Refreshing page may fix system problem. Before refreshing page, please backup all your unsaved data.");
return;
}
// remove from rendered list
ownedItems.value.delete(uuid);
// if we are editing sharing target of this item,
// we should escape this status
if (sharingItemUuid.value === uuid) {
sharingItemUuid.value = null;
await refreshSharingItem();
}
}
const shareOwnedItem = async (uuid: string) => {
// if there is no editing sharing target, set it.
// otherwise override it or toggle it according to its old value.
let old_value = sharingItemUuid.value;
if (typeof old_value === 'string') {
if (old_value === uuid) {
sharingItemUuid.value = null;
} else {
sharingItemUuid.value = uuid;
}
} else {
sharingItemUuid.value = uuid;
}
// refresh sharing list
await refreshSharingItem();
}
const updateOwnedItem = async (uuid: string, name: string) => {
let lastChange = ownedItems.value.get(uuid)![2];
let rv = await apiCollectionUpdateOwn(token.currentToken, uuid, name, lastChange);
if (typeof rv === 'undefined') {
messagebox.value?.show("An update operation failed. It may caused by wrong arguments or lost target. Refreshing page may fix system problem. Before refreshing page, please backup all your unsaved data.");
return;
}
// return value is new lastChange, so we update it.
let row = ownedItems.value.get(uuid)!;
row[1] = name;
row[2] = rv;
// if current item is sharing, update it at the same time
if (sharingItemUuid.value === uuid) {
await refreshSharingItem();
}
}
const refreshSharingItem = async () => {
// clear sharing item
sharingItems.value = [];
// if we are not in sharing mode, return directly
if (!isSharingItem.value) {
return;
}
// fetch data
let uuid = sharingItemUuid.value!;
let rv = await apiCollectionGetSharing(token.currentToken, uuid);
if (typeof rv === 'undefined') {
console.error(`fail to fetch sharing target for collection ${uuid}`);
return;
}
// render then
sharingItems.value = rv;
}
const addSharingItem = async () => {
let uuid = sharingItemUuid.value!;
let lastChange = ownedItems.value.get(uuid)![2];
let new_username = pendingSharingItemUsername.value;
if (new_username === '') {
return;
}
// order adding
let rv = await apiCollectionAddSharing(token.currentToken, uuid, new_username, lastChange);
if (typeof rv === 'undefined') {
messagebox.value?.show("An add operation failed. It may caused by wrong arguments. Refreshing page may fix system problem. Before refreshing page, please backup all your unsaved data.");
return;
}
// push new item in list
sharingItems.value.push(new_username);
// the return value is the new last change
ownedItems.value.get(uuid)![2] = rv;
}
const deleteSharingItem = async (username: string) => {
let uuid = sharingItemUuid.value!;
let lastChange = ownedItems.value.get(uuid)![2];
// order deleting
let rv = await apiCollectionDeleteSharing(token.currentToken, uuid, username, lastChange);
if (typeof rv === 'undefined') {
messagebox.value?.show("A delete operation failed. It may caused by no matched item. Refreshing page may fix system problem. Before refreshing page, please backup all your unsaved data.");
return;
}
// remove this item
sharingItems.value.splice(sharingItems.value.indexOf(username), 1);
// the return value is the new last change
ownedItems.value.get(uuid)![2] = rv;
}
</script>
2026-04-28 15:47:32 +08:00
<template>
2026-06-30 14:08:51 +08:00
<div class="container" style="display: flex; flex-flow: column; margin-top: 1.25rem;">
<h1 class="title">Owned</h1>
<div class="control-list">
<div class="field has-addons">
<div class="control">
<input v-model="pendingOwnedItemName" class="input" type="text">
</div>
<div class="control" @click="addOwnedItem">
<a class="button is-primary">
<span class="icon is-small"><font-awesome-icon icon="fas fa-plus"></font-awesome-icon></span>
</a>
</div>
</div>
<div class="control" @click="refreshOwnedItem">
<a class="button is-primary">
<span class="icon is-small"><font-awesome-icon icon="fas fa-sync"></font-awesome-icon></span>
</a>
</div>
</div>
<div v-for="item in ownedItems.values()" style="display: flex; flex-flow: column; margin-top: 1.25rem;">
<OwnedItem :uuid="item[0]" :name="item[1]" @delete="deleteOwnedItem" @share="shareOwnedItem"
@update="updateOwnedItem" />
</div>
</div>
<div v-show="isSharingItem" class="container" style="display: flex; flex-flow: column; margin-top: 1.25rem;">
<h1 class="title">Sharing target</h1>
<label class="label">
<span>Editing: </span>
<span>{{ isSharingItem ? ownedItems.get(sharingItemUuid!)![1] : "" }}</span>
</label>
<div class="control-list">
<div class="field has-addons">
<div class="control">
<input v-model="pendingSharingItemUsername" class="input" type="text">
</div>
<div class="control" @click="addSharingItem">
<a class="button is-primary">
<span class="icon is-small"><font-awesome-icon icon="fas fa-plus"></font-awesome-icon></span>
</a>
</div>
</div>
<div class="control" @click="refreshSharingItem">
<a class="button is-primary">
<span class="icon is-small"><font-awesome-icon icon="fas fa-sync"></font-awesome-icon></span>
</a>
</div>
</div>
<div v-for="item in sharingItems" style="display: flex; flex-flow: column; margin-top: 1.25rem;">
<SharingItem :username="item" @delete="deleteSharingItem" />
</div>
</div>
<MessageBox ref="messagebox" />
2026-04-28 15:47:32 +08:00
</template>
<style scoped></style>