1
0

FileEditable.vue 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. <script>
  2. import JSONAPI from '@api/json-axios'
  3. import REST from '@api/rest-axios'
  4. import { mapActions, mapState } from 'pinia'
  5. import { ConcernementsStore } from '@stores/concernements'
  6. import { UserStore } from '@stores/user'
  7. import ContentEditable from '@components/editable/ContentEditable.vue';
  8. import SvgIcon from '@jamescoyle/vue-icon';
  9. import { mdiTrashCanOutline } from '@mdi/js';
  10. import { mdiFilePlusOutline } from '@mdi/js';
  11. import { nextTick } from 'vue'
  12. export default {
  13. props: {
  14. can_update: Boolean,
  15. files: Object,
  16. data: Object,
  17. label: String,
  18. },
  19. emits: ['updated'],
  20. data(){
  21. return {
  22. mdiTrashCanOutline_path: mdiTrashCanOutline,
  23. mdiFilePlusOutline_path: mdiFilePlusOutline,
  24. }
  25. },
  26. computed: {
  27. ...mapState(UserStore,['csrf_token']),
  28. },
  29. created () {
  30. console.log('ImageEditable created')
  31. },
  32. mounted () {
  33. },
  34. methods: {
  35. ...mapActions(ConcernementsStore, ['reloadConcernements']),
  36. addFile(e){
  37. console.log('addFile', this.$refs);
  38. this.$refs.file_input.click();
  39. },
  40. onInputNewfile(e){
  41. console.log('onFileInput', e);
  42. let file = e.target.files[0];
  43. console.log('onFileInput file', file);
  44. this.saveNewFile(file);
  45. },
  46. saveNewFile(file){
  47. const filename = file.name.normalize('NFD').replace(/[\u0300-\u036f]/g, "").replace(/[^a-zA-Z0-9_\-\.]/g, "_"); // Normalisation Unicode et suppression des accents
  48. console.log('filename', filename);
  49. const configs = {
  50. headers: {
  51. 'X-CSRF-Token': this.csrf_token,
  52. 'Content-Type': 'application/octet-stream',
  53. 'Content-Disposition': `file; filename="${filename}"`,
  54. },
  55. };
  56. JSONAPI.post(`/${this.data.entitytype}/${this.data.bundle}/${this.data.uuid}/${this.data.field.field_name}`, file, configs)
  57. .then(({ data : { data } }) => {
  58. console.log('jsonapi post file', data)
  59. this.$emit('updated');
  60. })
  61. .catch(error => {
  62. console.warn('Issue with jsonapi post file', error)
  63. Promise.reject(error)
  64. })
  65. },
  66. onFocusOut(e){
  67. console.log('onfocusOut', e, this.$refs.file_list);
  68. this.buildFileListThenSaveList();
  69. },
  70. buildFileListThenSaveList(force_Save){
  71. console.log('buildLinkListThenSave files', this.files);
  72. // build links list
  73. let list = [];
  74. let tobesaved = false;
  75. for (let i = 0; i < this.$refs.file_list.children.length; i++) {
  76. const $li = this.$refs.file_list.children[i];
  77. console.log('$li', $li);
  78. if ($li.querySelector('input.description')) {
  79. let description = $li.querySelector('input.description').value;
  80. list.push({
  81. description: description,
  82. target_id: this.files[i].file.fid
  83. });
  84. tobesaved = (description !== this.files[i].description);
  85. }
  86. }
  87. if (!tobesaved) {
  88. tobesaved = this.files.length !== list.length;
  89. }
  90. if (tobesaved || force_Save) {
  91. this.saveList(list);
  92. }
  93. },
  94. saveList(list){
  95. console.log('saveList list', list);
  96. const params = {
  97. type: this.data.bundle,
  98. [this.data.field.field_name]: list
  99. };
  100. if (this.data.entitytype === 'node') {
  101. params.nid = [{"value":this.data.id}];
  102. } else {
  103. params.id = [{"value":this.data.id}];
  104. }
  105. // // we need additional values for image alt for example
  106. // // console.log('additional_values', this.data.field.additional_values);
  107. // if (this.data.field.additional_values) {
  108. // for (const key in this.data.field.additional_values) {
  109. // if (Object.hasOwnProperty.call(this.data.field.additional_values, key)) {
  110. // params[this.data.field.field_name][0][key] = this.data.field.additional_values[key]
  111. // }
  112. // }
  113. // }
  114. const configs = {
  115. headers: {'X-CSRF-Token': this.csrf_token}
  116. };
  117. // url is not the same between nodes and others entities
  118. let url_base = `/${this.data.entitytype === 'node' ? '' : 'entity/'}${this.data.entitytype}`;
  119. // call the api
  120. REST.patch(`${url_base}/${this.data.id}?_format=json`, params, configs)
  121. .then(({ data }) => {
  122. console.log('user REST post node data', data)
  123. this.$emit('updated');
  124. })
  125. .catch(error => {
  126. console.warn(`Issue with patch ${this.data.entitytype} ${this.data.bundle}`, error)
  127. Promise.reject(error)
  128. })
  129. },
  130. async onDeleteFile(i){
  131. console.log(`onDeleteLink ${i}`, this.$refs.file_list.children[i]);
  132. // this.links_nb = this.$refs.links_list.children.length;
  133. // this.$refs.links_list.children[i].remove();
  134. this.files.splice(i,1);
  135. await nextTick();
  136. this.buildFileListThenSaveList(true);
  137. },
  138. onUpdated(){
  139. this.$emit('updated');
  140. },
  141. fileMime(mime){
  142. return mime.replace('application/', '');
  143. },
  144. docLabel(doc){
  145. return doc.description ? doc.description : doc.file.filename;
  146. }
  147. },
  148. components: {
  149. SvgIcon,
  150. ContentEditable
  151. }
  152. }
  153. </script>
  154. <template>
  155. <label>{{ label }}</label>
  156. <ul ref="file_list" class="editable-files" :class="{'can-update': can_update}">
  157. <li v-for="(doc,d) in files" :key="d" class="file-editable">
  158. <template v-if="!can_update">
  159. <a
  160. :href="doc.file.url"
  161. target="_blank"
  162. >
  163. {{ docLabel(doc) }} [{{ fileMime(doc.file.filemime) }}]
  164. </a>
  165. </template>
  166. <template v-else>
  167. <input type="text" class="description" :value="doc.description" placeholder="Description du fichier" @focusout="onFocusOut" />
  168. <!-- <input type="text" class="url" :value="link.url" @focusout="onFocusOut" placeholder="Url du lien" /> -->
  169. <div class="filename">{{ doc.file.filename }}</div>
  170. <div @click="onDeleteFile(d)" class="delete-btn">
  171. <svg-icon type="mdi" :path="mdiTrashCanOutline_path" />
  172. </div>
  173. </template>
  174. </li>
  175. <li v-if="can_update">
  176. <div @click="addFile" class="file-btn">
  177. <svg-icon type="mdi" :path="mdiFilePlusOutline_path"/>
  178. </div>
  179. <input ref="file_input" type="file" accept="application/txt, application/pdf, application/doc, application/docx, application/obs, application/xls" @input="onInputNewfile"/>
  180. </li>
  181. </ul>
  182. </template>