394 lines
12 KiB
Vue
394 lines
12 KiB
Vue
<template>
|
|
<div class="mt-6" v-resize="onResize">
|
|
<div>
|
|
<v-btn depressed tile @click="revealedClicked">
|
|
{{ $ay.t("Attachments")
|
|
}}<v-icon v-text="reveal ? 'fa-eye-slash' : 'fa-eye'" right></v-icon
|
|
></v-btn>
|
|
</div>
|
|
|
|
<template v-if="reveal">
|
|
<div>
|
|
<template v-if="readonly">
|
|
<!-- Note: this is just a copy of the inner part of read/write version below
|
|
with the action taken out -->
|
|
<div class="mt-4" :style="cardTextStyle()">
|
|
<span v-if="!hasFiles()">{{ $ay.t("NoData") }}</span>
|
|
|
|
<v-list three-line>
|
|
<v-list-item
|
|
v-for="item in displayList"
|
|
:key="item.id"
|
|
:href="item.url"
|
|
target="_blank"
|
|
>
|
|
<v-list-item-avatar>
|
|
<v-icon v-text="item.icon"></v-icon>
|
|
</v-list-item-avatar>
|
|
|
|
<v-list-item-content>
|
|
<v-list-item-title v-text="item.name"></v-list-item-title>
|
|
|
|
<v-list-item-subtitle
|
|
v-text="item.date"
|
|
></v-list-item-subtitle>
|
|
|
|
<v-list-item-subtitle
|
|
v-text="item.notes"
|
|
></v-list-item-subtitle>
|
|
</v-list-item-content>
|
|
</v-list-item>
|
|
</v-list>
|
|
</div>
|
|
</template>
|
|
<template v-else>
|
|
<v-tabs v-model="tab" color="primary">
|
|
<v-tabs-slider></v-tabs-slider>
|
|
<v-tab key="list"><v-icon>fa-folder</v-icon></v-tab>
|
|
<v-tab key="attach"><v-icon>fa-paperclip</v-icon></v-tab>
|
|
<v-tabs-items v-model="tab">
|
|
<v-tab-item key="list">
|
|
<div
|
|
v-cloak
|
|
@drop.prevent="onDrop"
|
|
@dragover.prevent="onDragOver"
|
|
@dragleave="onDragEnd"
|
|
class="mt-4"
|
|
:style="cardTextStyle()"
|
|
id="dropDiv"
|
|
>
|
|
<v-list three-line>
|
|
<v-list-item
|
|
v-for="item in displayList"
|
|
:key="item.id"
|
|
:href="item.url"
|
|
target="_blank"
|
|
>
|
|
<v-list-item-avatar>
|
|
<v-icon v-text="item.icon"></v-icon>
|
|
</v-list-item-avatar>
|
|
|
|
<v-list-item-content>
|
|
<v-list-item-title
|
|
v-text="item.name"
|
|
></v-list-item-title>
|
|
|
|
<v-list-item-subtitle
|
|
v-text="item.date"
|
|
></v-list-item-subtitle>
|
|
|
|
<v-list-item-subtitle
|
|
v-text="item.notes"
|
|
></v-list-item-subtitle>
|
|
</v-list-item-content>
|
|
|
|
<v-list-item-action>
|
|
<v-btn large @click="openEditMenu(item, $event)" icon>
|
|
<v-icon>fa-edit</v-icon>
|
|
</v-btn>
|
|
</v-list-item-action>
|
|
</v-list-item>
|
|
</v-list>
|
|
</div>
|
|
<v-btn depressed tile @click="revealedClicked" class="mt-8">
|
|
{{ $ay.t("Attachments")
|
|
}}<v-icon
|
|
v-text="reveal ? 'fa-eye-slash' : 'fa-eye'"
|
|
right
|
|
></v-icon
|
|
></v-btn>
|
|
</v-tab-item>
|
|
<v-tab-item key="attach">
|
|
<div class="mt-8">
|
|
<v-file-input
|
|
v-model="uploadFiles"
|
|
:label="$ay.t('AttachFile')"
|
|
prepend-icon="fa-paperclip"
|
|
multiple
|
|
chips
|
|
></v-file-input>
|
|
|
|
<v-text-field
|
|
v-model="notes"
|
|
:label="$ay.t('AttachmentNotes')"
|
|
></v-text-field>
|
|
<v-btn color="primary" text @click="upload">{{
|
|
$ay.t("Upload")
|
|
}}</v-btn>
|
|
</div>
|
|
</v-tab-item>
|
|
</v-tabs-items>
|
|
</v-tabs>
|
|
<v-menu
|
|
min-width="360"
|
|
v-model="editMenu"
|
|
:close-on-content-click="false"
|
|
offset-y
|
|
:position-x="menuX"
|
|
:position-y="menuY"
|
|
absolute
|
|
>
|
|
<v-card>
|
|
<v-card-title>{{ $ay.t("FileAttachment") }}</v-card-title>
|
|
<div class="ma-6">
|
|
<v-text-field
|
|
v-model="editName"
|
|
:label="$ay.t('AttachmentFileName')"
|
|
></v-text-field>
|
|
<v-text-field
|
|
v-model="editNotes"
|
|
:label="$ay.t('AttachmentNotes')"
|
|
></v-text-field>
|
|
</div>
|
|
<v-card-actions>
|
|
<v-btn @click="remove()" text>
|
|
{{ $ay.t("Delete") }}
|
|
</v-btn>
|
|
<v-spacer></v-spacer>
|
|
<v-btn text @click="editMenu = false">{{
|
|
$ay.t("Cancel")
|
|
}}</v-btn>
|
|
<v-btn color="primary" text @click="saveEdit">{{
|
|
$ay.t("OK")
|
|
}}</v-btn>
|
|
</v-card-actions>
|
|
</v-card>
|
|
</v-menu>
|
|
</template>
|
|
</div>
|
|
</template>
|
|
</div>
|
|
</template>
|
|
<script>
|
|
export default {
|
|
data() {
|
|
return {
|
|
reveal: false,
|
|
height: 300,
|
|
displayList: [],
|
|
notes: null,
|
|
tab: null,
|
|
uploadFiles: [],
|
|
editMenu: false,
|
|
menuX: 10,
|
|
menuY: 10,
|
|
editNotes: null,
|
|
editName: null,
|
|
editId: null
|
|
};
|
|
},
|
|
props: {
|
|
ayaType: Number,
|
|
ayaId: Number,
|
|
readonly: Boolean
|
|
},
|
|
computed: {},
|
|
methods: {
|
|
revealedClicked() {
|
|
this.reveal = !this.reveal;
|
|
if (this.reveal) {
|
|
this.getList();
|
|
}
|
|
},
|
|
onResize() {
|
|
this.height = window.innerHeight * 0.8;
|
|
},
|
|
cardTextStyle() {
|
|
return "height: " + this.height + "px;overflow-y:auto;";
|
|
},
|
|
hasFiles() {
|
|
if (!this.displayList || this.displayList.length == 0) {
|
|
return false;
|
|
}
|
|
return true;
|
|
},
|
|
upload() {
|
|
//similar code in wiki-control
|
|
let vm = this;
|
|
let fileData = [];
|
|
for (let i = 0; i < vm.uploadFiles.length; i++) {
|
|
let f = vm.uploadFiles[i];
|
|
fileData.push({ name: f.name, lastModified: f.lastModified });
|
|
}
|
|
let at = {
|
|
ayaId: vm.ayaId,
|
|
ayaType: vm.ayaType,
|
|
files: vm.uploadFiles,
|
|
fileData: JSON.stringify(fileData), //note this is required for an array or it will come to the server as a string [object,object]
|
|
notes: vm.notes ? vm.notes : ""
|
|
};
|
|
|
|
window.$gz.api
|
|
.uploadAttachment(at)
|
|
.then(res => {
|
|
if (res.error) {
|
|
window.$gz.errorHandler.handleFormError(res.error);
|
|
} else {
|
|
vm.uploadFiles = [];
|
|
vm.updateDisplayList(res.data);
|
|
}
|
|
})
|
|
.catch(function handleUploadError(error) {
|
|
window.$gz.errorHandler.handleFormError(error);
|
|
});
|
|
},
|
|
remove() {
|
|
let vm = this;
|
|
window.$gz.dialog.confirmDelete().then(dialogResult => {
|
|
if (dialogResult == true) {
|
|
window.$gz.api
|
|
.remove("attachment/" + vm.editId)
|
|
.then(res => {
|
|
if (res.error) {
|
|
window.$gz.errorHandler.handleFormError(res.error);
|
|
} else {
|
|
vm.editMenu = false;
|
|
vm.editName = null;
|
|
vm.editNotes = null;
|
|
vm.editId = null;
|
|
vm.getList();
|
|
}
|
|
})
|
|
.catch(function handleUploadError(error) {
|
|
window.$gz.errorHandler.handleFormError(error);
|
|
});
|
|
}
|
|
});
|
|
},
|
|
getList() {
|
|
let vm = this;
|
|
window.$gz.api
|
|
.get("attachment/list?ayatype=" + vm.ayaType + "&ayaid=" + vm.ayaId)
|
|
.then(res => {
|
|
if (res.error) {
|
|
window.$gz.errorHandler.handleFormError(res.error);
|
|
} else {
|
|
vm.updateDisplayList(res.data);
|
|
}
|
|
})
|
|
.catch(function handleGetListError(error) {
|
|
window.$gz.errorHandler.handleFormError(error);
|
|
});
|
|
},
|
|
updateDisplayList(data) {
|
|
//{"data":[{"id":1,"concurrency":7733332,"contentType":"image/png","displayFileName":"Screen Shot 2020-01-09 at 10.50.24.png","lastModified":"0001-01-01T00:00:00Z","notes":"Here are notes"},{"id":4,"concurrency":7733354,"contentType":"text/plain","displayFileName":"TNT log file ayanova.txt","lastModified":"0001-01-01T00:00:00Z","notes":"Here are notes"},{"id":2,"concurrency":7733342,"contentType":"text/plain","displayFileName":"stack.txt","lastModified":"0001-01-01T00:00:00Z","notes":"Here are notes"},{"id":3,"concurrency":7733348,"contentType":"image/jpeg","displayFileName":"t2cx6sloffk41.jpg","lastModified":"0001-01-01T00:00:00Z","notes":"Here are notes"}]}
|
|
if (!data) {
|
|
data = [];
|
|
}
|
|
|
|
let timeZoneName = window.$gz.locale.getBrowserTimeZoneName();
|
|
let languageName = window.$gz.locale.getBrowserLanguages();
|
|
let hour12 = window.$gz.store.state.locale.hour12;
|
|
let ret = [];
|
|
for (let i = 0; i < data.length; i++) {
|
|
let o = data[i];
|
|
//http://localhost:7575/api/v8/attachment/download/100?t=sssss
|
|
ret.push({
|
|
id: o.id,
|
|
concurrency: o.concurrency,
|
|
url: window.$gz.api.attachmentDownloadUrl(o.id, o.contentType),
|
|
name: o.displayFileName,
|
|
date: window.$gz.locale.utcDateToShortDateAndTimeLocalized(
|
|
o.lastModified,
|
|
timeZoneName,
|
|
languageName,
|
|
hour12
|
|
),
|
|
notes: o.notes ? o.notes : "",
|
|
icon: window.$gz.util.iconForFile(o.displayFileName, o.contentType)
|
|
});
|
|
}
|
|
this.displayList = ret;
|
|
},
|
|
openEditMenu(item, e) {
|
|
e.preventDefault();
|
|
this.editMenu = false;
|
|
this.editName = item.name;
|
|
this.editNotes = item.notes;
|
|
this.editId = item.id;
|
|
|
|
this.menuX = e.clientX;
|
|
this.menuY = e.clientY;
|
|
this.$nextTick(() => {
|
|
this.editMenu = true;
|
|
});
|
|
},
|
|
saveEdit() {
|
|
let vm = this;
|
|
if (!vm.editName) {
|
|
//todo: some error here, name is required..
|
|
return;
|
|
}
|
|
//check if they differ first
|
|
let o = null;
|
|
let i = 0;
|
|
for (i = 0; i < vm.displayList.length; i++) {
|
|
if (vm.displayList[i].id == vm.editId) {
|
|
o = vm.displayList[i];
|
|
break;
|
|
}
|
|
}
|
|
//any changes?
|
|
if (o.name == vm.editName && o.notes == vm.editNotes) {
|
|
return;
|
|
}
|
|
|
|
//post to server
|
|
/*
|
|
public uint Concurrency { get; set; }
|
|
[Required]
|
|
public string DisplayFileName { get; set; }
|
|
public string Notes { get; set; }
|
|
*/
|
|
|
|
let p = {
|
|
concurrency: o.concurrency,
|
|
displayFileName: vm.editName,
|
|
notes: vm.editNotes
|
|
};
|
|
|
|
window.$gz.api
|
|
.upsert("attachment/" + vm.editId, p)
|
|
.then(res => {
|
|
if (res.error) {
|
|
window.$gz.errorHandler.handleFormError(res.error);
|
|
} else {
|
|
vm.editMenu = false;
|
|
vm.editName = null;
|
|
vm.editNotes = null;
|
|
vm.editId = null;
|
|
//due to fucking reactivity issues which never seem to resolve no matter what I'm returning a fresh list on update
|
|
vm.updateDisplayList(res.data);
|
|
}
|
|
})
|
|
.catch(function handleUploadError(error) {
|
|
window.$gz.errorHandler.handleFormError(error);
|
|
});
|
|
},
|
|
onDrop(ev) {
|
|
dropDiv.style.border = "none";
|
|
dropDiv = null;
|
|
//handle file drop
|
|
var files = Array.from(ev.dataTransfer.files);
|
|
if (files.length > 0) {
|
|
this.uploadFiles = files;
|
|
this.upload();
|
|
}
|
|
},
|
|
onDragOver(ev) {
|
|
if (!dropDiv) {
|
|
dropDiv = document.getElementById("dropDiv");
|
|
}
|
|
|
|
dropDiv.style.border = "4px dashed #00ff00";
|
|
},
|
|
onDragEnd(ev) {
|
|
dropDiv.style.border = "none";
|
|
dropDiv = null;
|
|
}
|
|
//-----
|
|
}
|
|
};
|
|
let dropDiv = null;
|
|
</script>
|