update code
This commit is contained in:
64
resources/js/src/components/SearchBox.vue
Normal file
64
resources/js/src/components/SearchBox.vue
Normal file
@@ -0,0 +1,64 @@
|
||||
<template>
|
||||
<CRow>
|
||||
<CCol sm="3">
|
||||
<CSelect :options="fields" @update:value="fieldChanged" />
|
||||
</CCol>
|
||||
<CCol sm="9">
|
||||
<CInput placeholder="Tìm kiếm" :value.sync="value" @update:value="valueChanged" />
|
||||
</CCol>
|
||||
</CRow>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "SearchBox",
|
||||
props: {
|
||||
fields: {
|
||||
type: Array,
|
||||
default: function () {
|
||||
return [{ value: "", label: "Tất cả" }];
|
||||
},
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
field: String,
|
||||
value: "",
|
||||
searching: {},
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
field: {
|
||||
handler(value) {
|
||||
this.$emit("fieldChanged", value);
|
||||
this.value = value.defaultValue;
|
||||
this.fireSearching();
|
||||
},
|
||||
},
|
||||
value: {
|
||||
handler(value) {
|
||||
this.$emit("valueChanged", value);
|
||||
this.fireSearching();
|
||||
},
|
||||
},
|
||||
searching: {
|
||||
handler(value) {
|
||||
this.$emit("searching", value);
|
||||
},
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
fieldChanged(field) {
|
||||
this.field = this.fields.find((e) => {
|
||||
return e.value == field;
|
||||
});
|
||||
},
|
||||
valueChanged(value) {
|
||||
this.value = value;
|
||||
},
|
||||
fireSearching(){
|
||||
this.searching = {field: this.field.value, value: this.value};
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
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>
|
||||
179
resources/js/src/components/form/List.vue
Normal file
179
resources/js/src/components/form/List.vue
Normal file
@@ -0,0 +1,179 @@
|
||||
<template>
|
||||
<div>
|
||||
<CCard>
|
||||
<CCardHeader>
|
||||
<CIcon name="cil-grid" />
|
||||
{{title}}
|
||||
<CButton
|
||||
v-if="canCreate"
|
||||
size="sm"
|
||||
class="float-right"
|
||||
color="primary"
|
||||
variant="outline"
|
||||
v-c-tooltip="'Tạo mới'"
|
||||
@click="showCreate"
|
||||
>
|
||||
<CIcon name="cil-plus" />
|
||||
</CButton>
|
||||
</CCardHeader>
|
||||
<CCardBody class="p-0">
|
||||
<CDataTable
|
||||
hover
|
||||
:loading="loading"
|
||||
:items="items"
|
||||
:fields="fields"
|
||||
@row-clicked="showDetail"
|
||||
clickable-rows
|
||||
sorter
|
||||
column-filter
|
||||
></CDataTable>
|
||||
</CCardBody>
|
||||
</CCard>
|
||||
<CModal :title="title" :show.sync="isShowDetail">
|
||||
<CInput
|
||||
v-for="field in fields"
|
||||
:label="field.label"
|
||||
:key="field.key"
|
||||
:value.sync="itemUpdating[field.key]"
|
||||
/>
|
||||
<slot v-if="!createMode" name="append-body"></slot>
|
||||
<template #footer>
|
||||
<CButton
|
||||
v-if="canCreate && createMode"
|
||||
size="sm"
|
||||
class="float-right"
|
||||
color="success"
|
||||
@click="onClickCreate"
|
||||
>
|
||||
<CIcon name="cil-plus" /> Tạo mới
|
||||
</CButton>
|
||||
<CButton
|
||||
v-if="canUpdate && !createMode"
|
||||
size="sm"
|
||||
class="float-right"
|
||||
color="success"
|
||||
@click="onClickSave"
|
||||
>
|
||||
<CIcon name="cil-check" /> Lưu
|
||||
</CButton>
|
||||
<CButton
|
||||
v-if="canDelete && !createMode"
|
||||
size="sm"
|
||||
class="float-right"
|
||||
color="danger"
|
||||
@click="onClickDelete"
|
||||
>
|
||||
<CIcon name="cil-x" /> Xóa
|
||||
</CButton>
|
||||
</template>
|
||||
</CModal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "List",
|
||||
props: {
|
||||
service: {
|
||||
required: true,
|
||||
type: Object
|
||||
},
|
||||
title: {
|
||||
required: false,
|
||||
type: String,
|
||||
default: "Danh sách"
|
||||
},
|
||||
canCreate: {
|
||||
required: false,
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
canUpdate: {
|
||||
required: false,
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
canDelete: {
|
||||
required: false,
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
fields: {
|
||||
required: true,
|
||||
type: Array
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
items: [],
|
||||
itemSelected: {},
|
||||
itemUpdating: {},
|
||||
isShowDetail: false,
|
||||
createMode: false
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.init();
|
||||
},
|
||||
methods: {
|
||||
init() {
|
||||
this.fetchList();
|
||||
},
|
||||
async fetchList() {
|
||||
this.loading = true;
|
||||
const response = await this.service.all();
|
||||
this.items = response.data;
|
||||
this.loading = false;
|
||||
},
|
||||
showDetail(item) {
|
||||
this.$emit("show", item);
|
||||
this.createMode = false;
|
||||
this.itemSelected = item;
|
||||
this.itemUpdating = _.clone(item);
|
||||
this.isShowDetail = true;
|
||||
},
|
||||
onClickSave() {
|
||||
this.service
|
||||
.update(this.itemUpdating, this.itemSelected.id)
|
||||
.then(response => {
|
||||
this.isShowDetail = false;
|
||||
this.$toast.success("Đã lưu");
|
||||
this.fetchList();
|
||||
})
|
||||
.catch(error => {
|
||||
this.toastHttpError(error);
|
||||
});
|
||||
},
|
||||
showCreate() {
|
||||
this.createMode = true;
|
||||
this.itemUpdating = {};
|
||||
this.isShowDetail = true;
|
||||
},
|
||||
onClickCreate() {
|
||||
this.service
|
||||
.create(this.itemUpdating)
|
||||
.then(response => {
|
||||
this.isShowDetail = false;
|
||||
this.$toast.success("Đã tạo");
|
||||
this.fetchList();
|
||||
})
|
||||
.catch(error => {
|
||||
this.toastHttpError(error);
|
||||
});
|
||||
},
|
||||
onClickDelete() {
|
||||
this.service
|
||||
.delete(this.itemSelected.id)
|
||||
.then(response => {
|
||||
this.isShowDetail = false;
|
||||
this.$toast.success("Đã xóa");
|
||||
this.fetchList();
|
||||
})
|
||||
.catch(error => {
|
||||
this.toastHttpError(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
117
resources/js/src/components/user/Info.vue
Normal file
117
resources/js/src/components/user/Info.vue
Normal file
@@ -0,0 +1,117 @@
|
||||
<template>
|
||||
<CCard>
|
||||
<CCardHeader>
|
||||
<strong>Thông tin</strong>
|
||||
</CCardHeader>
|
||||
<CCardBody>
|
||||
<CForm>
|
||||
<CInput label="Mã" :value.sync="user.id" horizontal :readonly="true" />
|
||||
<CInput
|
||||
placeholder="Let us know your full name."
|
||||
label="Tên"
|
||||
:value.sync="user.name"
|
||||
horizontal
|
||||
/>
|
||||
<CInput
|
||||
label="Email"
|
||||
placeholder="Enter your email"
|
||||
type="email"
|
||||
:value.sync="user.email"
|
||||
horizontal
|
||||
autocomplete="email"
|
||||
/>
|
||||
<CInput
|
||||
label="Số điện thoại"
|
||||
placeholder="Enter your tel"
|
||||
:value.sync="user.tel"
|
||||
horizontal
|
||||
autocomplete="tel"
|
||||
/>
|
||||
<CInput label="Ngày sinh" type="date" :value.sync="user.birthday" horizontal />
|
||||
<CSelect
|
||||
label="Chức danh"
|
||||
horizontal
|
||||
:value.sync="user.title_id"
|
||||
:options="titles"
|
||||
placeholder="Please select"
|
||||
/>
|
||||
<CSelect
|
||||
label="Phòng ban"
|
||||
horizontal
|
||||
:value.sync="user.department_id"
|
||||
:options="departments"
|
||||
placeholder="Please select"
|
||||
/>
|
||||
<CFormGroup class="form-group form-row">
|
||||
<template #label>
|
||||
<slot name="label">
|
||||
<label class="col-form-label col-sm-3">Kích hoạt</label>
|
||||
</slot>
|
||||
</template>
|
||||
<template #input>
|
||||
<CSwitch class="mx-1" color="success" :checked.sync="user.active" />
|
||||
</template>
|
||||
</CFormGroup>
|
||||
</CForm>
|
||||
</CCardBody>
|
||||
<CCardFooter>
|
||||
<CButton type="submit" size="sm" @click="updateInfo" class="float-right" color="success">
|
||||
<CIcon name="cil-check" />Lưu
|
||||
</CButton>
|
||||
</CCardFooter>
|
||||
</CCard>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import services from "../../services/factory";
|
||||
|
||||
export default {
|
||||
name: "Info",
|
||||
props: {
|
||||
userId: {
|
||||
required: true,
|
||||
},
|
||||
isMe: {
|
||||
required: false,
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
beforeRouteEnter(to, from, next) {
|
||||
next((vm) => {
|
||||
vm.usersOpened = from.fullPath.includes("users");
|
||||
});
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
usersOpened: null,
|
||||
user: [],
|
||||
titles: [],
|
||||
departments: [],
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.fetch();
|
||||
},
|
||||
methods: {
|
||||
async fetch() {
|
||||
const titleResponse = await services.title.all();
|
||||
this.titles = this.formatKeys(titleResponse.data);
|
||||
const departmentResponse = await services.department.all();
|
||||
this.departments = this.formatKeys(departmentResponse.data);
|
||||
const userResponse = await services.user.get(this.userId);
|
||||
this.user = userResponse.data;
|
||||
},
|
||||
async updateInfo() {
|
||||
return await (this.isMe ? services.auth : services.user)
|
||||
.update(this.user, this.userId)
|
||||
.then((response) => {
|
||||
this.$toast.success("Đã lưu");
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toastHttpError(error);
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
75
resources/js/src/components/user/Password.vue
Normal file
75
resources/js/src/components/user/Password.vue
Normal file
@@ -0,0 +1,75 @@
|
||||
<template>
|
||||
<CCard>
|
||||
<CCardHeader>
|
||||
<strong>Thay đổi mật khẩu</strong>
|
||||
</CCardHeader>
|
||||
<CCardBody>
|
||||
<CForm>
|
||||
<CInput placeholder="Nhập mật khẩu." label="Mật khẩu" type="password" horizontal />
|
||||
<CInput
|
||||
placeholder="Nhập mật khẩu mới."
|
||||
label="Mật khẩu mới"
|
||||
type="password"
|
||||
:value.sync="password.password"
|
||||
horizontal
|
||||
/>
|
||||
<CInput
|
||||
placeholder="Nhập lại mật khẩu mới."
|
||||
label="Xác nhận"
|
||||
type="password"
|
||||
:value.sync="password.password_confirmation"
|
||||
horizontal
|
||||
/>
|
||||
</CForm>
|
||||
</CCardBody>
|
||||
<CCardFooter>
|
||||
<CButton
|
||||
type="submit"
|
||||
@click="updatePassword"
|
||||
size="sm"
|
||||
class="float-right"
|
||||
color="success"
|
||||
>
|
||||
<CIcon name="cil-check" />Thay đổi
|
||||
</CButton>
|
||||
</CCardFooter>
|
||||
</CCard>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import services from "../../services/factory";
|
||||
|
||||
export default {
|
||||
name: "Password",
|
||||
props: {
|
||||
userId: {
|
||||
required: true,
|
||||
},
|
||||
isMe: {
|
||||
required: false,
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
password: {
|
||||
password_confirmation: "",
|
||||
password: "",
|
||||
},
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
async updatePassword() {
|
||||
return await (this.isMe ? services.auth : services.user)
|
||||
.update(this.password, this.userId)
|
||||
.then((response) => {
|
||||
this.$toast.success("Đã thay đổi");
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toastHttpError(error);
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
65
resources/js/src/components/user/Permission.vue
Normal file
65
resources/js/src/components/user/Permission.vue
Normal file
@@ -0,0 +1,65 @@
|
||||
<template>
|
||||
<CCard>
|
||||
<CCardHeader>
|
||||
<strong>Phân quyền</strong>
|
||||
</CCardHeader>
|
||||
<CCardBody>
|
||||
<CForm>
|
||||
<CRow form class="form-group" key="permission">
|
||||
<CCol sm="12">
|
||||
<CInputCheckbox
|
||||
v-for="(permission) in permissions"
|
||||
:key="permission.id"
|
||||
:label="permission.name"
|
||||
:value="permission.id"
|
||||
:checked="user.permissions.map(permission => permission.id).includes(permission.id)"
|
||||
@update:checked="givePermission"
|
||||
/>
|
||||
</CCol>
|
||||
</CRow>
|
||||
</CForm>
|
||||
</CCardBody>
|
||||
</CCard>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import services from "../../services/factory";
|
||||
|
||||
export default {
|
||||
name: "Permission",
|
||||
props: {
|
||||
userId: {
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
permissions: [],
|
||||
user: []
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.fetch();
|
||||
},
|
||||
methods: {
|
||||
async fetch() {
|
||||
const userResponse = await services.user.get(this.userId, {
|
||||
with: "permissions"
|
||||
});
|
||||
this.user = userResponse.data;
|
||||
const permissionResponse = await services.permission.all();
|
||||
this.permissions = permissionResponse.data;
|
||||
},
|
||||
async givePermission(checked, event) {
|
||||
const permissionId = event.target.value;
|
||||
const permissionRequest = (await checked)
|
||||
? services.user.givePermission(permissionId, this.userId)
|
||||
: services.user.revokePermission(permissionId, this.userId);
|
||||
permissionRequest.catch(error => {
|
||||
this.toastHttpError(error);
|
||||
});
|
||||
return permissionRequest;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
65
resources/js/src/components/user/Role.vue
Normal file
65
resources/js/src/components/user/Role.vue
Normal file
@@ -0,0 +1,65 @@
|
||||
<template>
|
||||
<CCard>
|
||||
<CCardHeader>
|
||||
<strong>Phân nhóm</strong>
|
||||
</CCardHeader>
|
||||
<CCardBody>
|
||||
<CForm>
|
||||
<CRow form class="form-group" key="role">
|
||||
<CCol sm="12">
|
||||
<CInputCheckbox
|
||||
v-for="(role) in roles"
|
||||
:key="role.id"
|
||||
:label="role.name"
|
||||
:value="role.id"
|
||||
:checked="user.roles.map(role => role.id).includes(role.id)"
|
||||
@update:checked="giveRole"
|
||||
/>
|
||||
</CCol>
|
||||
</CRow>
|
||||
</CForm>
|
||||
</CCardBody>
|
||||
</CCard>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import services from "../../services/factory";
|
||||
|
||||
export default {
|
||||
name: "Role",
|
||||
props: {
|
||||
userId: {
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
roles: [],
|
||||
user: []
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.fetch();
|
||||
},
|
||||
methods: {
|
||||
async fetch() {
|
||||
const userResponse = await services.user.get(this.userId, {
|
||||
with: "roles"
|
||||
});
|
||||
this.user = userResponse.data;
|
||||
const roleResponse = await services.role.all();
|
||||
this.roles = roleResponse.data;
|
||||
},
|
||||
async giveRole(checked, event) {
|
||||
const roleId = event.target.value;
|
||||
const roleRequest = (await checked)
|
||||
? services.user.giveRole(roleId, this.userId)
|
||||
: services.user.revokeRole(roleId, this.userId);
|
||||
roleRequest.catch(error => {
|
||||
this.toastHttpError(error);
|
||||
});
|
||||
return roleRequest;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
74
resources/js/src/components/user/TreePermission.vue
Normal file
74
resources/js/src/components/user/TreePermission.vue
Normal file
@@ -0,0 +1,74 @@
|
||||
<template>
|
||||
<CRow class="form-group">
|
||||
<CCol sm="12">
|
||||
<label>Phân theo chức năng</label>
|
||||
<treeselect
|
||||
@select="addPermission"
|
||||
@deselect="removePermission"
|
||||
v-model="permissions"
|
||||
:multiple="true"
|
||||
:options="permissionOptions"
|
||||
:clearable="false"
|
||||
></treeselect>
|
||||
</CCol>
|
||||
</CRow>
|
||||
</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: "TreePermission",
|
||||
props: {
|
||||
userId: {
|
||||
required: true
|
||||
}
|
||||
},
|
||||
components: { Treeselect },
|
||||
data() {
|
||||
return {
|
||||
permissionOptions: [],
|
||||
permissions: []
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.fetchPermissions();
|
||||
this.fetchCurrentPermissions();
|
||||
},
|
||||
methods: {
|
||||
async fetchPermissions() {
|
||||
const permissionResponse = await services.permission.all();
|
||||
this.permissionOptions = this.formatKeys(permissionResponse.data, {
|
||||
id: "id",
|
||||
name: "label"
|
||||
});
|
||||
return permissionResponse;
|
||||
},
|
||||
async fetchCurrentPermissions() {
|
||||
const userResponse = await services.user.get(this.userId, {
|
||||
with: "permissions"
|
||||
});
|
||||
this.permissions = userResponse.data.permissions.map(
|
||||
permission => permission.id
|
||||
);
|
||||
},
|
||||
addPermission(permission) {
|
||||
services.user.givePermission(permission.id, this.userId).catch(error => {
|
||||
this.toastHttpError(error);
|
||||
});
|
||||
},
|
||||
removePermission(permission) {
|
||||
services.user
|
||||
.revokePermission(permission.id, this.userId)
|
||||
.catch(error => {
|
||||
this.toastHttpError(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
70
resources/js/src/components/user/TreeRole.vue
Normal file
70
resources/js/src/components/user/TreeRole.vue
Normal file
@@ -0,0 +1,70 @@
|
||||
<template>
|
||||
<CRow class="form-group">
|
||||
<CCol sm="12">
|
||||
<label>Phân theo nhóm</label>
|
||||
<treeselect
|
||||
@select="addRole"
|
||||
@deselect="removeRole"
|
||||
v-model="roles"
|
||||
:multiple="true"
|
||||
:options="roleOptions"
|
||||
:clearable="false"
|
||||
></treeselect>
|
||||
</CCol>
|
||||
</CRow>
|
||||
</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: "TreeRole",
|
||||
props: {
|
||||
userId: {
|
||||
required: true
|
||||
}
|
||||
},
|
||||
components: { Treeselect },
|
||||
data() {
|
||||
return {
|
||||
roleOptions: [],
|
||||
roles: []
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.fetchRoles();
|
||||
this.fetchCurrentRoles();
|
||||
},
|
||||
methods: {
|
||||
async fetchRoles() {
|
||||
const roleResponse = await services.role.all();
|
||||
this.roleOptions = this.formatKeys(roleResponse.data, {
|
||||
id: "id",
|
||||
name: "label"
|
||||
});
|
||||
return roleResponse;
|
||||
},
|
||||
async fetchCurrentRoles() {
|
||||
const userResponse = await services.user.get(this.userId, {
|
||||
with: "roles"
|
||||
});
|
||||
this.roles = userResponse.data.roles.map(role => role.id);
|
||||
},
|
||||
addRole(role) {
|
||||
services.user.giveRole(role.id, this.userId).catch(error => {
|
||||
this.toastHttpError(error);
|
||||
});
|
||||
},
|
||||
removeRole(role) {
|
||||
services.user.revokeRole(role.id, this.userId).catch(error => {
|
||||
this.toastHttpError(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
Reference in New Issue
Block a user