187 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
			
		
		
	
	
			187 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
| <template>
 | |
|   <modal-inner class="modal__inner-1--templates" aria-label="管理模板">
 | |
|     <div class="modal__content">
 | |
|       <div class="form-entry">
 | |
|         <label class="form-entry__label" for="template">模板</label>
 | |
|         <div class="form-entry__field">
 | |
|           <input v-if="isEditing" id="template" type="text" class="textfield" v-focus @blur="submitEdit()" @keydown.enter="submitEdit()" @keydown.esc.stop="submitEdit(true)" v-model="editingName">
 | |
|           <select v-else id="template" v-model="selectedId" class="textfield">
 | |
|             <option v-for="(template, id) in templates" :key="id" :value="id">
 | |
|               {{ template.name }}
 | |
|             </option>
 | |
|           </select>
 | |
|         </div>
 | |
|         <div class="form-entry__actions flex flex--row flex--end">
 | |
|           <button class="form-entry__button button" @click="create" v-title="'新建模板'">
 | |
|             <icon-file-plus></icon-file-plus>
 | |
|           </button>
 | |
|           <button class="form-entry__button button" @click="copy" v-title="'复制模板'">
 | |
|             <icon-file-multiple></icon-file-multiple>
 | |
|           </button>
 | |
|           <button v-if="!isReadOnly" class="form-entry__button button" @click="isEditing = true" v-title="'重命名模板'">
 | |
|             <icon-pen></icon-pen>
 | |
|           </button>
 | |
|           <button v-if="!isReadOnly" class="form-entry__button button" @click="remove" v-title="'删除模板'">
 | |
|             <icon-delete></icon-delete>
 | |
|           </button>
 | |
|         </div>
 | |
|       </div>
 | |
|       <div class="form-entry">
 | |
|         <label class="form-entry__label">值</label>
 | |
|         <div class="form-entry__field" v-for="(template, id) in templates" :key="id" v-if="id === selectedId">
 | |
|           <code-editor lang="handlebars" :value="template.value" :disabled="isReadOnly" @changed="template.value = $event"></code-editor>
 | |
|         </div>
 | |
|       </div>
 | |
|       <div v-if="!isReadOnly">
 | |
|         <a href="javascript:void(0)" v-if="!showHelpers" @click="showHelpers = true">添加帮助</a>
 | |
|         <div class="form-entry" v-else>
 | |
|           <br>
 | |
|           <label class="form-entry__label">帮助</label>
 | |
|           <div class="form-entry__field" v-for="(template, id) in templates" :key="id" v-if="id === selectedId">
 | |
|             <code-editor lang="javascript" :value="template.helpers" @changed="template.helpers = $event"></code-editor>
 | |
|           </div>
 | |
|         </div>
 | |
|       </div>
 | |
|     </div>
 | |
|     <div class="modal__button-bar">
 | |
|       <button class="button" @click="config.reject()">取消</button>
 | |
|       <button class="button button--resolve" @click="resolve()">确认</button>
 | |
|     </div>
 | |
|   </modal-inner>
 | |
| </template>
 | |
| 
 | |
| <script>
 | |
| import { mapGetters } from 'vuex';
 | |
| import utils from '../../services/utils';
 | |
| import badgeSvc from '../../services/badgeSvc';
 | |
| import ModalInner from './common/ModalInner';
 | |
| import CodeEditor from '../CodeEditor';
 | |
| import emptyTemplateValue from '../../data/empties/emptyTemplateValue.html';
 | |
| import emptyTemplateHelpers from '!raw-loader!../../data/empties/emptyTemplateHelpers.js'; // eslint-disable-line
 | |
| import store from '../../store';
 | |
| 
 | |
| const collator = new Intl.Collator(undefined, { sensitivity: 'base' });
 | |
| 
 | |
| function fillEmptyFields(template) {
 | |
|   if (template.value === '\n') {
 | |
|     template.value = emptyTemplateValue;
 | |
|   }
 | |
|   if (template.helpers === '\n') {
 | |
|     template.helpers = emptyTemplateHelpers;
 | |
|   }
 | |
| }
 | |
| 
 | |
| export default {
 | |
|   components: {
 | |
|     ModalInner,
 | |
|     CodeEditor,
 | |
|   },
 | |
|   data: () => ({
 | |
|     selectedId: '',
 | |
|     templates: {},
 | |
|     showHelpers: false,
 | |
|     isEditing: false,
 | |
|     editingName: '',
 | |
|   }),
 | |
|   computed: {
 | |
|     ...mapGetters('modal', [
 | |
|       'config',
 | |
|     ]),
 | |
|     isReadOnly() {
 | |
|       return this.templates[this.selectedId].isAdditional;
 | |
|     },
 | |
|   },
 | |
|   created() {
 | |
|     this.$watch(
 | |
|       () => store.getters['data/allTemplatesById'],
 | |
|       (allTemplatesById) => {
 | |
|         const templates = {};
 | |
|         // Sort templates by name
 | |
|         Object.entries(allTemplatesById)
 | |
|           .sort(([, template1], [, template2]) => collator.compare(template1.name, template2.name))
 | |
|           .forEach(([id, template]) => {
 | |
|             const templateClone = utils.deepCopy(template);
 | |
|             fillEmptyFields(templateClone);
 | |
|             templates[id] = templateClone;
 | |
|           });
 | |
|         this.templates = templates;
 | |
|         this.selectedId = this.config.selectedId;
 | |
|         if (!templates[this.selectedId]) {
 | |
|           [this.selectedId] = Object.keys(templates);
 | |
|         }
 | |
|         this.isEditing = false;
 | |
|       },
 | |
|       { immediate: true },
 | |
|     );
 | |
|     this.$watch('selectedId', (selectedId) => {
 | |
|       const template = this.templates[selectedId];
 | |
|       this.showHelpers = template.helpers !== emptyTemplateHelpers;
 | |
|       this.editingName = template.name;
 | |
|     }, { immediate: true });
 | |
|   },
 | |
|   methods: {
 | |
|     create() {
 | |
|       const template = {
 | |
|         name: 'New template',
 | |
|         value: '\n',
 | |
|         helpers: '\n',
 | |
|       };
 | |
|       fillEmptyFields(template);
 | |
|       this.selectedId = utils.uid();
 | |
|       this.templates[this.selectedId] = template;
 | |
|       this.isEditing = true;
 | |
|     },
 | |
|     copy() {
 | |
|       const template = utils.deepCopy(this.templates[this.selectedId]);
 | |
|       template.name += ' copy';
 | |
|       delete template.isAdditional;
 | |
|       this.selectedId = utils.uid();
 | |
|       this.templates[this.selectedId] = template;
 | |
|       this.isEditing = true;
 | |
|     },
 | |
|     remove() {
 | |
|       delete this.templates[this.selectedId];
 | |
|       [this.selectedId] = Object.keys(this.templates);
 | |
|     },
 | |
|     submitEdit(cancel) {
 | |
|       const template = this.templates[this.selectedId];
 | |
|       if (!cancel && this.editingName) {
 | |
|         template.name = utils.sanitizeName(this.editingName);
 | |
|       } else {
 | |
|         this.editingName = template.name;
 | |
|       }
 | |
|       setTimeout(() => { // For the form-entry to get the blur event
 | |
|         this.isEditing = false;
 | |
|       }, 1);
 | |
|     },
 | |
|     async resolve() {
 | |
|       const oldTemplateIds = Object.keys(store.getters['data/templatesById']);
 | |
|       await store.dispatch('data/setTemplatesById', this.templates);
 | |
|       const newTemplateIds = Object.keys(store.getters['data/templatesById']);
 | |
|       const createdCount = newTemplateIds
 | |
|         .filter(id => !oldTemplateIds.includes(id))
 | |
|         .length;
 | |
|       const removedCount = oldTemplateIds
 | |
|         .filter(id => !newTemplateIds.includes(id))
 | |
|         .length;
 | |
|       if (createdCount) {
 | |
|         badgeSvc.addBadge('addTemplate');
 | |
|       }
 | |
|       if (removedCount) {
 | |
|         badgeSvc.addBadge('removeTemplate');
 | |
|       }
 | |
|       this.config.resolve({
 | |
|         templates: this.templates,
 | |
|         selectedId: this.selectedId,
 | |
|       });
 | |
|     },
 | |
|   },
 | |
| };
 | |
| </script>
 | |
| 
 | |
| <style lang="scss">
 | |
| .modal__inner-1.modal__inner-1--templates {
 | |
|   max-width: 600px;
 | |
| }
 | |
| </style>
 | 
