update code
This commit is contained in:
129
resources/js/src/components/document/Attachments.vue
Normal file
129
resources/js/src/components/document/Attachments.vue
Normal file
@@ -0,0 +1,129 @@
|
||||
<template>
|
||||
<CCard>
|
||||
<CCardHeader>
|
||||
<strong>Tệp đính kèm</strong>
|
||||
</CCardHeader>
|
||||
<CCardBody>
|
||||
<CDataTable :loading="loading" :items="attachments" :fields="fields">
|
||||
<template #id="{item, index}">
|
||||
<td>
|
||||
<CButton size="sm" @click="downloadAttachment(item)" variant="outline" color="primary">
|
||||
<CIcon name="cil-cloud-download" />
|
||||
</CButton>
|
||||
<CButton
|
||||
size="sm"
|
||||
@click="deleteAttachment(item.id, index)"
|
||||
variant="outline"
|
||||
color="danger"
|
||||
>
|
||||
<CIcon name="cil-remove" />
|
||||
</CButton>
|
||||
</td>
|
||||
</template>
|
||||
</CDataTable>
|
||||
<CInputFile custom v-on:change="handleFileUpload" />
|
||||
</CCardBody>
|
||||
</CCard>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import services from "../../services/factory";
|
||||
|
||||
export default {
|
||||
name: "Attachments",
|
||||
props: {
|
||||
documentId: {
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: true,
|
||||
fields: [
|
||||
{ key: "name", label: "Tên", _classes: "w-50" },
|
||||
{ key: "size", label: "Kích thước (KB)" },
|
||||
{ key: "downloads", label: "Lượt tải" },
|
||||
{ key: "id", label: "Hành động" }
|
||||
],
|
||||
attachments: [
|
||||
{
|
||||
id: "",
|
||||
name: "",
|
||||
size: 0,
|
||||
downloads: 0
|
||||
}
|
||||
],
|
||||
file: {}
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
documentId: {
|
||||
handler() {
|
||||
this.init();
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.init();
|
||||
},
|
||||
methods: {
|
||||
init() {
|
||||
this.fetch();
|
||||
},
|
||||
async fetch() {
|
||||
const attachmentResponse = await services.attachment.all({
|
||||
search: `document_id:${this.documentId}`
|
||||
});
|
||||
this.attachments = this.formatKeys(attachmentResponse.data, {
|
||||
id: "id",
|
||||
name: "name"
|
||||
});
|
||||
this.loading = false;
|
||||
},
|
||||
downloadAttachment(item) {
|
||||
services.attachment
|
||||
.download(item.id, item.name)
|
||||
.then(response => {
|
||||
item.downloads++;
|
||||
this.$toast.success("Đã tải xuống");
|
||||
})
|
||||
.catch(error => {
|
||||
this.toastHttpError(error);
|
||||
});
|
||||
},
|
||||
deleteAttachment(id, index) {
|
||||
services.attachment
|
||||
.delete(id)
|
||||
.then(response => {
|
||||
this.attachments.splice(index, 1);
|
||||
this.$toast.success("Đã xóa");
|
||||
})
|
||||
.catch(error => {
|
||||
this.toastHttpError(error);
|
||||
});
|
||||
},
|
||||
handleFileUpload(files, e) {
|
||||
this.file = files[0];
|
||||
this.uploadAttachment();
|
||||
},
|
||||
uploadAttachment() {
|
||||
let formData = new FormData();
|
||||
formData.append("attachments", this.file);
|
||||
formData.append("document_id", this.documentId);
|
||||
services.attachment
|
||||
.create(formData, {
|
||||
headers: {
|
||||
"Content-Type": "multipart/form-data"
|
||||
}
|
||||
})
|
||||
.then(response => {
|
||||
this.attachments.push(response.data);
|
||||
this.$toast.success("Đã tải lên");
|
||||
})
|
||||
.catch(error => {
|
||||
this.toastHttpError(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
157
resources/js/src/components/document/CLinkDocument.vue
Normal file
157
resources/js/src/components/document/CLinkDocument.vue
Normal file
@@ -0,0 +1,157 @@
|
||||
<template>
|
||||
<CCard>
|
||||
<CCardHeader>
|
||||
<strong>Văn bản liên kết</strong>
|
||||
</CCardHeader>
|
||||
<CCardBody>
|
||||
<CRow class="form-group">
|
||||
<CCol v-if="isOutcome" sm="12">
|
||||
<label>Văn bản đến</label>
|
||||
<treeselect
|
||||
v-model="linkToDocument"
|
||||
:multiple="false"
|
||||
:options="documents"
|
||||
@search-change="fetchDocuments"
|
||||
@select="linkTo"
|
||||
@input="unLinkTo"
|
||||
>
|
||||
<div
|
||||
slot="option-label"
|
||||
slot-scope="{ node }"
|
||||
>{{ node.raw.label + ' - ' + node.raw.abstract || getSymbol(node.raw.id) }}</div>
|
||||
</treeselect>
|
||||
</CCol>
|
||||
<CCol v-else sm="12">
|
||||
<label>Danh sách đã liên kết</label>
|
||||
<treeselect
|
||||
v-model="documentsLinked"
|
||||
:options="linkedDocuments"
|
||||
:multiple="true"
|
||||
:openOnClick="false"
|
||||
:clearable="false"
|
||||
disabled
|
||||
placeholder
|
||||
>
|
||||
<CButton
|
||||
@click="redirectToDocument(node.raw.id)"
|
||||
class="btn-link p-0"
|
||||
slot="value-label"
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
slot-scope="{ node }"
|
||||
>{{ node.raw.label }}</CButton>
|
||||
</treeselect>
|
||||
</CCol>
|
||||
</CRow>
|
||||
</CCardBody>
|
||||
</CCard>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import services from "../../services/factory";
|
||||
|
||||
// import the component
|
||||
import { Treeselect, ASYNC_SEARCH } from "@riophae/vue-treeselect";
|
||||
// import the styles
|
||||
import "@riophae/vue-treeselect/dist/vue-treeselect.css";
|
||||
|
||||
export default {
|
||||
name: "CLinkDocument",
|
||||
props: {
|
||||
documentId: {
|
||||
required: true
|
||||
},
|
||||
isOutcome: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
components: { Treeselect },
|
||||
data() {
|
||||
return {
|
||||
// define the default value
|
||||
linkToDocument: null,
|
||||
documentsLinked: [],
|
||||
// define options
|
||||
documents: [],
|
||||
linkedDocuments: [],
|
||||
document: {}
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
documentId: {
|
||||
handler() {
|
||||
this.init();
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.init();
|
||||
},
|
||||
methods: {
|
||||
async init() {
|
||||
this.fetchDocuments();
|
||||
this.fetchDocument();
|
||||
},
|
||||
async fetchDocument() {
|
||||
const documentResponse = await services.document.get(this.documentId, {
|
||||
with: "linkTo;linked"
|
||||
});
|
||||
this.document = documentResponse.data;
|
||||
this.linkToDocument = this.document.link_to
|
||||
? this.document.link_to.id
|
||||
: null;
|
||||
this.documents.push(this.linkToDocument);
|
||||
this.linkedDocuments = this.formatKeys(this.document.linked, {
|
||||
id: "id",
|
||||
symbol: "label"
|
||||
});
|
||||
this.documentsLinked = this.document.linked.map(d => d.id);
|
||||
},
|
||||
fetchDocuments(query = "") {
|
||||
const documentsResponse = services.document
|
||||
.all({ search: `symbol:${query};book_id:1`, searchJoin: "and", orderBy: "created_at", sortedBy: "desc", })
|
||||
.then(response => {
|
||||
const documents = this.formatKeys(response.data.data, {
|
||||
id: "id",
|
||||
symbol: "label"
|
||||
});
|
||||
this.documents = documents;
|
||||
});
|
||||
},
|
||||
getSymbol(id) {
|
||||
this.fetchSymbol(id);
|
||||
return "Không có quyền truy cập";
|
||||
},
|
||||
async fetchSymbol(id) {
|
||||
const response = await services.document.get(id);
|
||||
if (!response.data.id) return;
|
||||
const document = {
|
||||
id: response.data.id,
|
||||
label: response.data.symbol
|
||||
};
|
||||
if (this.documents.map(item => item.id).includes(document.id)) return;
|
||||
this.documents.push(document);
|
||||
},
|
||||
linkTo(document) {
|
||||
services.document
|
||||
.update({ link_id: document.id }, this.documentId)
|
||||
.catch(error => {
|
||||
this.toastHttpError(error);
|
||||
});
|
||||
},
|
||||
unLinkTo(document) {
|
||||
if(document === undefined){
|
||||
services.document
|
||||
.update({ link_id: null }, this.documentId)
|
||||
.catch(error => {
|
||||
this.toastHttpError(error);
|
||||
});
|
||||
}
|
||||
},
|
||||
redirectToDocument(id) {
|
||||
this.$emit("redirectTo", id);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
305
resources/js/src/components/document/Detail.vue
Normal file
305
resources/js/src/components/document/Detail.vue
Normal file
@@ -0,0 +1,305 @@
|
||||
<template>
|
||||
<CCard>
|
||||
<CCardHeader>
|
||||
<strong v-if="documentId">Chi tiết văn bản</strong>
|
||||
<strong v-else>Tạo văn bản</strong>
|
||||
</CCardHeader>
|
||||
<CCardBody>
|
||||
<CForm>
|
||||
<CRow class="form-group">
|
||||
<CCol sm="6">
|
||||
<CSelect
|
||||
class="mb-0"
|
||||
label="Sổ văn bản"
|
||||
:options="books"
|
||||
:value.sync="document.book_id"
|
||||
placeholder="Please select"
|
||||
/>
|
||||
</CCol>
|
||||
<CCol sm="6">
|
||||
<CFormGroup class="form-group mb-0">
|
||||
<template #label>
|
||||
<slot name="label">
|
||||
<label>Loại văn bản</label>
|
||||
</slot>
|
||||
</template>
|
||||
<template #input>
|
||||
<treeselect
|
||||
v-model="document.type_id"
|
||||
:multiple="false"
|
||||
:options="types"
|
||||
:clearable="false"
|
||||
></treeselect>
|
||||
</template>
|
||||
</CFormGroup>
|
||||
</CCol>
|
||||
</CRow>
|
||||
<CRow class="form-group">
|
||||
<CCol sm="12">
|
||||
<CInput label="Số ký hiệu" :value.sync="document.symbol" class="mb-0" />
|
||||
</CCol>
|
||||
</CRow>
|
||||
<CRow class="form-group">
|
||||
<CCol sm="6">
|
||||
<CFormGroup class="form-group mb-0">
|
||||
<template #label>
|
||||
<slot name="label">
|
||||
<label>Người soạn</label>
|
||||
</slot>
|
||||
</template>
|
||||
<template #input>
|
||||
<treeselect
|
||||
v-model="document.writer_id"
|
||||
:multiple="false"
|
||||
:options="writers"
|
||||
@search-change="fetchWriters"
|
||||
@input="onClearWriter"
|
||||
>
|
||||
<label
|
||||
slot="option-label"
|
||||
slot-scope="{ node }"
|
||||
>{{ node.raw.department ? node.raw.label + ' - ' + node.raw.department.name : node.raw.label }}</label>
|
||||
</treeselect>
|
||||
</template>
|
||||
</CFormGroup>
|
||||
</CCol>
|
||||
<CCol sm="6">
|
||||
<CInput label="Người tạo" :value="document.creator.name" readonly class="mb-0" />
|
||||
</CCol>
|
||||
</CRow>
|
||||
<CTextarea
|
||||
label="Trích yếu"
|
||||
placeholder="Content..."
|
||||
rows="5"
|
||||
:value.sync="document.abstract"
|
||||
/>
|
||||
<CRow class="form-group">
|
||||
<CCol sm="6">
|
||||
<CFormGroup class="form-group mb-0">
|
||||
<template #label>
|
||||
<slot name="label">
|
||||
<label>Nơi ban hành</label>
|
||||
</slot>
|
||||
</template>
|
||||
<template #input>
|
||||
<treeselect
|
||||
v-model="document.publisher_id"
|
||||
:multiple="false"
|
||||
:options="publishers"
|
||||
:clearable="false"
|
||||
></treeselect>
|
||||
</template>
|
||||
</CFormGroup>
|
||||
</CCol>
|
||||
<CCol sm="6">
|
||||
<CInput
|
||||
:label="isIncome ? 'Ngày nhận' : 'Ngày ban hành'"
|
||||
type="date"
|
||||
:value.sync="document.effective_at"
|
||||
class="mb-0"
|
||||
/>
|
||||
</CCol>
|
||||
</CRow>
|
||||
<CRow class="form-group">
|
||||
<CCol sm="6">
|
||||
<CFormGroup class="form-group mb-0">
|
||||
<template #label>
|
||||
<slot name="label">
|
||||
<label>Người ký</label>
|
||||
</slot>
|
||||
</template>
|
||||
<template #input>
|
||||
<treeselect
|
||||
v-model="document.signer_id"
|
||||
:multiple="false"
|
||||
:options="signers"
|
||||
:clearable="false"
|
||||
>
|
||||
<label
|
||||
slot="option-label"
|
||||
slot-scope="{ node }"
|
||||
>{{ node.raw.description ? node.raw.label + ' - ' + node.raw.description : node.raw.label }}</label>
|
||||
</treeselect>
|
||||
</template>
|
||||
</CFormGroup>
|
||||
</CCol>
|
||||
<CCol sm="6">
|
||||
<CInput label="Ngày ký" type="date" :value.sync="document.sign_at" class="mb-0" />
|
||||
</CCol>
|
||||
</CRow>
|
||||
</CForm>
|
||||
</CCardBody>
|
||||
<CCardFooter>
|
||||
<CButton
|
||||
v-if="documentId"
|
||||
size="sm"
|
||||
@click="updateDocument"
|
||||
class="float-right"
|
||||
color="success"
|
||||
>
|
||||
<CIcon name="cil-check" />Lưu
|
||||
</CButton>
|
||||
<CButton v-else size="sm" @click="createDocument" class="float-right" color="success">
|
||||
<CIcon name="cil-plus" />Tạo
|
||||
</CButton>
|
||||
</CCardFooter>
|
||||
</CCard>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import services from "../../services/factory";
|
||||
// import the component
|
||||
import { Treeselect } from "@riophae/vue-treeselect";
|
||||
// import the styles
|
||||
import "@riophae/vue-treeselect/dist/vue-treeselect.css";
|
||||
|
||||
export default {
|
||||
name: "Detail",
|
||||
props: {
|
||||
documentId: {
|
||||
required: false
|
||||
}
|
||||
},
|
||||
components: { Treeselect },
|
||||
data() {
|
||||
return {
|
||||
books: [],
|
||||
types: [],
|
||||
signers: [],
|
||||
writers: [],
|
||||
publishers: [],
|
||||
document: {
|
||||
book_id: null,
|
||||
type_id: null,
|
||||
symbol: null,
|
||||
writer_id: null,
|
||||
abstract: null,
|
||||
publisher_id: null,
|
||||
effective_at: null,
|
||||
signer_id: null,
|
||||
sign_at: null,
|
||||
creator: {
|
||||
id: this.$store.state.auth.currentUser.id,
|
||||
name: this.$store.state.auth.currentUser.name
|
||||
},
|
||||
writer: {}
|
||||
}
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
documentId: {
|
||||
handler() {
|
||||
this.init();
|
||||
}
|
||||
},
|
||||
$route: {
|
||||
immediate: true,
|
||||
handler(route) {
|
||||
if (route.query && route.query.book) {
|
||||
this.document.book_id = Number.parseInt(route.query.book);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isIncome() {
|
||||
return this.document.book_id == 1;
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.init();
|
||||
},
|
||||
methods: {
|
||||
init() {
|
||||
!this.documentId || this.fetchDocument();
|
||||
this.fetchTypes();
|
||||
this.fetchBooks();
|
||||
this.fetchPublishers();
|
||||
this.fetchSigners();
|
||||
this.fetchWriters();
|
||||
},
|
||||
async fetchDocument() {
|
||||
const documentResponse = await services.document.get(this.documentId, {
|
||||
with: "creator;writer"
|
||||
});
|
||||
this.document = documentResponse.data;
|
||||
return documentResponse.data;
|
||||
},
|
||||
async fetchTypes() {
|
||||
const typeResponse = await services.documentType.all();
|
||||
this.types = this.formatKeys(typeResponse.data, {
|
||||
id: "id",
|
||||
name: "label"
|
||||
});
|
||||
return typeResponse;
|
||||
},
|
||||
async fetchBooks() {
|
||||
const bookResponse = await services.book.all();
|
||||
this.books = this.formatKeys(bookResponse.data);
|
||||
return bookResponse;
|
||||
},
|
||||
async fetchPublishers() {
|
||||
const publisherResponse = await services.publisher.all();
|
||||
this.publishers = this.formatKeys(publisherResponse.data, {
|
||||
id: "id",
|
||||
name: "label"
|
||||
});
|
||||
return publisherResponse;
|
||||
},
|
||||
async fetchSigners() {
|
||||
const signerResponse = await services.signer.all();
|
||||
this.signers = this.formatKeys(signerResponse.data, {
|
||||
id: "id",
|
||||
name: "label"
|
||||
});
|
||||
return signerResponse;
|
||||
},
|
||||
async fetchWriters(query = "") {
|
||||
const writersResponse = await services.user.all({
|
||||
search: `name:${query}`,
|
||||
with: "department"
|
||||
});
|
||||
this.writers = this.formatKeys(writersResponse.data.data, {
|
||||
id: "id",
|
||||
name: "label"
|
||||
});
|
||||
if (
|
||||
!this.writers.map(item => item.id).includes(this.document.writer_id) &&
|
||||
this.document.writer
|
||||
) {
|
||||
this.writers.push({
|
||||
id: this.document.writer.id,
|
||||
label: this.document.writer.name
|
||||
});
|
||||
}
|
||||
return writersResponse;
|
||||
},
|
||||
async updateDocument() {
|
||||
await services.document
|
||||
.update(this.document, this.documentId)
|
||||
.then(response => {
|
||||
this.$toast.success("Đã lưu");
|
||||
this.$emit("update", response.data);
|
||||
})
|
||||
.catch(error => {
|
||||
this.toastHttpError(error);
|
||||
});
|
||||
},
|
||||
createDocument() {
|
||||
services.document
|
||||
.create(this.document)
|
||||
.then(response => {
|
||||
this.$router.push({ path: `/documents/${response.data.id}` });
|
||||
this.$toast.success("Đã tạo văn bản");
|
||||
})
|
||||
.catch(error => {
|
||||
this.toastHttpError(error);
|
||||
});
|
||||
},
|
||||
onClearWriter(value) {
|
||||
if (value != undefined) return;
|
||||
this.document.writer_id = null;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
150
resources/js/src/components/document/Receivers.vue
Normal file
150
resources/js/src/components/document/Receivers.vue
Normal file
@@ -0,0 +1,150 @@
|
||||
<template>
|
||||
<CCard>
|
||||
<CCardHeader>
|
||||
<strong>Người nhận</strong>
|
||||
<CBadge
|
||||
color="success"
|
||||
class="float-right"
|
||||
v-c-tooltip="{ content: 'Đã xem' }"
|
||||
>{{ seenReceivers.length }}</CBadge
|
||||
>
|
||||
</CCardHeader>
|
||||
<CCardBody>
|
||||
<CRow class="form-group">
|
||||
<CCol sm="12">
|
||||
<!-- <label>Chọn xem</label> -->
|
||||
<treeselect
|
||||
@select="addViewer"
|
||||
@deselect="removeViewer"
|
||||
:normalizer="normalizer"
|
||||
v-model="viewers"
|
||||
:multiple="true"
|
||||
:options="viewerOptions"
|
||||
:clearable="false"
|
||||
>
|
||||
<div
|
||||
slot="value-label"
|
||||
slot-scope="{ node }"
|
||||
:class="seenStyle(node.raw.id)"
|
||||
>{{ node.raw.name }}</div>
|
||||
</treeselect>
|
||||
</CCol>
|
||||
</CRow>
|
||||
</CCardBody>
|
||||
</CCard>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import services from "../../services/factory";
|
||||
|
||||
// import the component
|
||||
import Treeselect from "@riophae/vue-treeselect";
|
||||
// import the styles
|
||||
import "@riophae/vue-treeselect/dist/vue-treeselect.css";
|
||||
|
||||
export default {
|
||||
name: "Receivers",
|
||||
props: {
|
||||
documentId: {
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
components: { Treeselect },
|
||||
data() {
|
||||
return {
|
||||
// define the default value
|
||||
viewers: [],
|
||||
// define options
|
||||
viewerOptions: [],
|
||||
seenReceivers: [],
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
documentId: {
|
||||
handler() {
|
||||
this.init();
|
||||
},
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.init();
|
||||
},
|
||||
methods: {
|
||||
async init() {
|
||||
this.fetchDepartments(), this.fetchViewers();
|
||||
},
|
||||
async fetchDepartments() {
|
||||
const departmentResponse = await services.department.all({
|
||||
with: "users",
|
||||
});
|
||||
const departments = departmentResponse.data;
|
||||
this.viewerOptions = departments;
|
||||
return departments;
|
||||
},
|
||||
async fetchViewers() {
|
||||
const receivers = await this.fetchReceivers();
|
||||
this.viewers = receivers.map((receiver) => receiver.id);
|
||||
this.seenReceivers = receivers
|
||||
.filter((receiver) => receiver.pivot.seen)
|
||||
.map((receiver) => receiver.id);
|
||||
return receivers;
|
||||
},
|
||||
async fetchReceivers() {
|
||||
const receiversResponse = await services.document.get(this.documentId, {
|
||||
with: "receivers",
|
||||
});
|
||||
const receivers = receiversResponse.data.receivers;
|
||||
this.handlerOptions = receivers;
|
||||
return receivers;
|
||||
},
|
||||
removeViewer(item) {
|
||||
var viewerIds = [item.id];
|
||||
if(item.users){
|
||||
viewerIds = item.users.map(u => u.id)
|
||||
}
|
||||
services.document
|
||||
.unassignReceivers(this.documentId, viewerIds)
|
||||
.then((response) => {
|
||||
this.fetchReceivers();
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toastHttpError(error);
|
||||
});
|
||||
},
|
||||
addViewer(item) {
|
||||
var viewerIds = [item.id];
|
||||
if(item.users){
|
||||
viewerIds = item.users.map(u => u.id)
|
||||
}
|
||||
services.document
|
||||
.assignReceivers(this.documentId, viewerIds)
|
||||
.then((response) => {
|
||||
this.fetchReceivers();
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toastHttpError(error);
|
||||
});
|
||||
},
|
||||
seenStyle(userId) {
|
||||
return this.seenReceivers.includes(userId) ? "seen" : null;
|
||||
},
|
||||
normalizer(node) {
|
||||
return {
|
||||
id: node.id,
|
||||
label: node.name,
|
||||
[node.users == undefined
|
||||
? ""
|
||||
: node.users.length > 0
|
||||
? "children"
|
||||
: ""]: node.users,
|
||||
};
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.seen {
|
||||
color: #2eb85c;
|
||||
}
|
||||
</style>
|
||||
94
resources/js/src/components/document/Recipients.vue
Normal file
94
resources/js/src/components/document/Recipients.vue
Normal file
@@ -0,0 +1,94 @@
|
||||
<template>
|
||||
<CCard>
|
||||
<CCardHeader>
|
||||
<strong>Nơi nhận</strong>
|
||||
</CCardHeader>
|
||||
<CCardBody>
|
||||
<CRow class="form-group">
|
||||
<CCol sm="12">
|
||||
<treeselect
|
||||
v-model="organizes"
|
||||
:multiple="true"
|
||||
:options="organizeOptions"
|
||||
:clearable="false"
|
||||
@select="addRecipient"
|
||||
@deselect="removeRecipient"
|
||||
></treeselect>
|
||||
</CCol>
|
||||
</CRow>
|
||||
</CCardBody>
|
||||
</CCard>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import services from "../../services/factory";
|
||||
|
||||
// import the component
|
||||
import Treeselect from "@riophae/vue-treeselect";
|
||||
// import the styles
|
||||
import "@riophae/vue-treeselect/dist/vue-treeselect.css";
|
||||
|
||||
export default {
|
||||
name: "Recipients",
|
||||
props: {
|
||||
documentId: {
|
||||
required: true
|
||||
}
|
||||
},
|
||||
components: { Treeselect },
|
||||
data() {
|
||||
return {
|
||||
// define the default value
|
||||
organizes: [],
|
||||
// define options
|
||||
organizeOptions: []
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
documentId: {
|
||||
handler() {
|
||||
this.init();
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.init();
|
||||
},
|
||||
methods: {
|
||||
async init() {
|
||||
this.fetchOrganizes();
|
||||
this.fetchRecipients();
|
||||
},
|
||||
async fetchOrganizes() {
|
||||
const organizesResponse = await services.publisher.all();
|
||||
const organizes = this.formatKeys(organizesResponse.data, {
|
||||
id: "id",
|
||||
name: "label"
|
||||
});
|
||||
this.organizeOptions = organizes;
|
||||
return organizes;
|
||||
},
|
||||
async fetchRecipients() {
|
||||
const recipientResponse = await services.document.get(this.documentId , {
|
||||
with: 'organizes'
|
||||
});
|
||||
this.organizes = recipientResponse.data.organizes.map(organize => organize.id);
|
||||
return recipientResponse;
|
||||
},
|
||||
addRecipient(item) {
|
||||
services.document
|
||||
.assignRecipients(this.documentId, [item.id])
|
||||
.catch(error => {
|
||||
this.toastHttpError(error);
|
||||
});
|
||||
},
|
||||
removeRecipient(item) {
|
||||
services.document
|
||||
.unassignRecipients(this.documentId, [item.id])
|
||||
.catch(error => {
|
||||
this.toastHttpError(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
Reference in New Issue
Block a user