free-fe-core-modules 0.0.4 → 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/components/Basic/BreadCrumbs.vue +18 -9
  2. package/components/Basic/LeveledMenus.vue +13 -5
  3. package/components/Basic/SummaryHead.vue +5 -2
  4. package/components/Dialog/BasicDialog.vue +14 -3
  5. package/components/FloatingWindow/index.vue +9 -1
  6. package/components/SlidingCarousel/index.vue +5 -2
  7. package/components/SlidingNews/index.vue +33 -24
  8. package/composible/useFormValidator.js +56 -0
  9. package/free-field/Fields/AgreementCheck.js +6 -2
  10. package/free-field/Fields/Category.js +2 -1
  11. package/free-field/Fields/Check.js +1 -0
  12. package/free-field/Fields/Date.js +13 -5
  13. package/free-field/Fields/DateRange.js +14 -8
  14. package/free-field/Fields/DynamicList.js +36 -33
  15. package/free-field/Fields/FixedList.vue +9 -5
  16. package/free-field/Fields/Labels.vue +7 -1
  17. package/free-field/Fields/MixedTable.vue +9 -3
  18. package/free-field/Fields/Number.js +12 -6
  19. package/free-field/Fields/Password.js +12 -6
  20. package/free-field/Fields/Search.vue +8 -2
  21. package/free-field/Fields/Select.vue +4 -1
  22. package/free-field/Fields/SelectionChain.vue +8 -2
  23. package/free-field/Fields/String.js +11 -5
  24. package/free-field/Fields/Text.js +12 -6
  25. package/free-field/Fields/Time.vue +8 -2
  26. package/free-field/Fields/TimeRange.vue +13 -5
  27. package/free-field/Fields/Year.js +12 -6
  28. package/free-field/Fields/YearRange.vue +9 -6
  29. package/free-field/composible/fieldWrapper.js +59 -51
  30. package/free-field/composible/freeFieldLabel.js +10 -3
  31. package/index.js +123 -78
  32. package/package.json +1 -1
  33. package/view/dict/index.vue +85 -1
  34. package/view/menu/index.vue +15 -11
  35. package/view/mourning/mourning.vue +17 -12
@@ -19,7 +19,7 @@
19
19
  :options="minYearOptions"
20
20
  :readonly="Field.ReadOnly"
21
21
  @input="rangeChanged"
22
- :ref="`input_field_validator_first`"
22
+ ref="input_field_validator_first"
23
23
  >
24
24
  <template v-slot:before v-if="Field.Label !== void 0">
25
25
  <span
@@ -39,7 +39,7 @@
39
39
  :options="maxYearOptions"
40
40
  :readonly="Field.ReadOnly"
41
41
  @input="rangeChanged"
42
- :ref="`input_field_validator_second`"
42
+ ref="input_field_validator_second"
43
43
  />
44
44
  </span>
45
45
 
@@ -48,8 +48,9 @@
48
48
  </template>
49
49
 
50
50
  <script>
51
- import { defineComponent, ref, getCurrentInstance } from 'vue';
51
+ import { defineComponent, ref } from 'vue';
52
52
  import { useFreeField, freeFieldProps } from '../composible/useFreeField';
53
+ import { useFormValidator} from '../../composible/useFormValidator';
53
54
 
54
55
  export default defineComponent({
55
56
  name: 'InputFieldYearRange',
@@ -85,11 +86,9 @@ export default defineComponent({
85
86
  ...freeFieldProps,
86
87
  },
87
88
  emits:['input'],
88
- setup(props, { emit }) {
89
+ setup(props, { emit, expose }) {
89
90
  if (!props.Field) return () => null;
90
91
 
91
- const { proxy:vm } = getCurrentInstance();
92
-
93
92
  const { fieldData, setFieldData } = useFreeField(props);
94
93
 
95
94
  const min = ref('');
@@ -163,6 +162,10 @@ export default defineComponent({
163
162
  return options;
164
163
  });
165
164
 
165
+ const { validate } = useFormValidator('input_field_validator_first', 'input_field_validator_second');
166
+ expose ({
167
+ validate,
168
+ })
166
169
 
167
170
  return {
168
171
  fieldData,
@@ -3,7 +3,7 @@ import {
3
3
  getCurrentInstance,
4
4
  h,
5
5
  ref,
6
- reactive,
6
+ shallowRef,
7
7
  watchEffect,
8
8
  computed,
9
9
  onMounted,
@@ -11,6 +11,8 @@ import {
11
11
  isRef,
12
12
  } from "vue";
13
13
  import { freeFieldProps } from "./useFreeField";
14
+ import { useFormValidator } from '../../composible/useFormValidator';
15
+
14
16
 
15
17
  export default defineComponent({
16
18
  name: "FreeFieldWrapper",
@@ -111,7 +113,7 @@ export default defineComponent({
111
113
  return props.Field.Hidden;
112
114
  });
113
115
 
114
- let realComponent = reactive({});
116
+ let realComponent = shallowRef(null);
115
117
 
116
118
  watchEffect(() => {
117
119
  const fComponents = vm.ctx.FieldComponents || {};
@@ -131,7 +133,7 @@ export default defineComponent({
131
133
  }
132
134
  }
133
135
 
134
- realComponent = field;
136
+ realComponent.value = field;
135
137
  });
136
138
 
137
139
  if (props.Field && props.Field.Info && props.Field.Info.KeepChanged) {
@@ -153,20 +155,23 @@ export default defineComponent({
153
155
  const warningSlot = (!props.noWarning && localField.value.Warning)
154
156
  && h('span', {
155
157
  class: 'input-field-warning no-wrap',
156
- },[
158
+ },
159
+ [
157
160
  h('span', { class: 'input-field-warning-icon' }),
158
161
  h('span', { class: 'input-field-warning-icon-sign-top' }),
159
162
  h('span', { class: 'input-field-warning-icon-sign-dot' }),
160
163
  h('span', { class: 'input-field-warning-text ellipsis' }, localField.value.Warning),
161
- ]);
164
+ ]
165
+ );
162
166
 
163
- if (realComponent) {
164
- const compEmits = {};
165
- (realComponent.emits || []).forEach((em) => {
166
- if (!em || em === 'input') return;
167
167
 
168
+ const compEmits = ref({});
169
+ watchEffect(() => {
170
+ (realComponent?.value?.emits || []).forEach((em) => {
171
+ if (!em || em === 'input') return;
172
+
168
173
  const captEm = `${em[0].toUpperCase()}${em.substring(1)}`;
169
- compEmits[`on${captEm}`] = (...args) => {
174
+ compEmits.value[`on${captEm}`] = (...args) => {
170
175
  // should not emit event directly as we were not inlucde these events in the emits list
171
176
  // but we could get any matched handller from the parent component and then call that
172
177
  // handller directly
@@ -176,46 +181,49 @@ export default defineComponent({
176
181
  outerHandller(...args);
177
182
  }
178
183
  };
179
- })
180
-
181
- const emitsRef = ref(realComponent.emits);
182
-
183
- expose ({
184
- emits: emitsRef,
185
- })
186
-
187
-
188
- return () =>
189
- h(
190
- "div",
191
- {
192
- class: wrapperClass,
193
- },
194
- [
195
- h(
196
- realComponent,
197
- {
198
- Field: localField.value,
199
- values: props.values,
200
- style: shouldHide.value ? "display:none;" : "",
201
- class: [
202
- props.Field.ReadOnly ? "free-field--readonly" : "",
203
- !shouldHide.value && hasError.value ? "hasError" : "",
204
- ],
205
- onInput: () => {
206
- emit("input", props.Field);
207
- },
208
- ...compEmits,
209
- },
210
- {
211
- ...slots,
212
- warning: slots.warning ? slots.warning : () => warningSlot,
213
- }
214
- ),
215
- ]
216
- );
217
- } else {
218
- return () => null;
219
- }
184
+ });
185
+ })
186
+
187
+ const readComp = computed(() => realComponent.value && h(
188
+ realComponent.value,
189
+ {
190
+ Field: localField.value,
191
+ values: props.values,
192
+ style: shouldHide.value ? "display:none;" : "",
193
+ class: [
194
+ props.Field.ReadOnly ? "free-field--readonly" : "",
195
+ !shouldHide.value && hasError.value ? "hasError" : "",
196
+ ],
197
+ onInput: () => {
198
+ emit("input", props.Field);
199
+ },
200
+ ...compEmits,
201
+ },
202
+ {
203
+ ...slots,
204
+ warning: slots.warning ? slots.warning : () => warningSlot,
205
+ }
206
+ ));
207
+
208
+ // const emitsRef = computed(() => realComponent?.value?.emits);
209
+
210
+ const {
211
+ validate,
212
+ } = useFormValidator(readComp);
213
+
214
+ expose ({
215
+ // emits: emitsRef.value,
216
+ validate: () => validate.value(props.Field.Name),
217
+ })
218
+
219
+ return realComponent.value ? () => h(
220
+ "div",
221
+ {
222
+ class: wrapperClass,
223
+ },
224
+ [
225
+ readComp.value,
226
+ ]
227
+ ) : () => null;
220
228
  },
221
229
  });
@@ -1,4 +1,5 @@
1
1
  import { defineComponent, h } from 'vue';
2
+ import { QTooltip } from 'quasar';
2
3
 
3
4
  export default defineComponent({
4
5
  name: 'FreeFieldLabel',
@@ -6,13 +7,19 @@ export default defineComponent({
6
7
  Field: { type: Object },
7
8
  },
8
9
  setup(props){
10
+ if (!props.Field) return () => null;
11
+
9
12
  return () => (props.Field?.Label === void 0) ? null : h('span', {
10
- class:`field-label field-label-readonly ${(props.Field.Label && props.Field.Label.trim().length)
11
- ? '' : 'field-label-empty'} ${props.Field.Required ? 'required' : ''}`,
13
+ class: {
14
+ 'field-label': true,
15
+ 'field-label-readonly': props.Field.ReadOnly,
16
+ 'field-label-empty': props.Field.Label?.trim().length <= 0,
17
+ required: props.Field.Required,
18
+ },
12
19
  }, [
13
20
  props.Field.Description && h(QTooltip, {
14
21
  anchor: "top right",
15
- }, props.Field.Description),
22
+ }, () => props.Field.Description),
16
23
  props.Field.Label || '',
17
24
  props.Field.Required && h('span', {
18
25
  class: 'required-mark',
package/index.js CHANGED
@@ -1,5 +1,7 @@
1
- import { date } from 'quasar';
1
+
2
+ import { date as quasarDate } from 'quasar';
2
3
  import config from '@/config';
4
+ import useAppStore from '@/stores/app';
3
5
  import MsgDialog from './components/Dialog/index';
4
6
 
5
7
  import EIcon from './components/Basic/EIcon.vue';
@@ -20,12 +22,29 @@ import routers from './router';
20
22
 
21
23
  // global filters
22
24
  const filters = {
23
- serverImage: url => (url ? `${config.imageUrlBase}${url}` : ''),
24
- serverVideo: url => (url ? `${config.videoUrlBase}${url}` : ''),
25
- serverThumb: url => (url ? `${config.thumbUrlBase}${url}` : ''),
26
- serverDocument: url => (url ? `${config.documentUrlBase}${url}` : ''),
25
+ serverImage: (url) => {
26
+ if (typeof url === 'string' && url.startsWith('@')) return url.substring(1);
27
+
28
+ return url ? `${config.imageUrlBase}${url}` : '';
29
+ },
30
+ serverVideo: (url) => {
31
+ if (typeof url === 'string' && url.startsWith('@')) return url.substring(1);
32
+
33
+ return url ? `${config.videoUrlBase}${url}` : '';
34
+ },
35
+ serverThumb: (url) => {
36
+ if (typeof url === 'string' && url.startsWith('@')) return url.substring(1);
37
+
38
+ return url ? `${config.thumbUrlBase}${url}` : '';
39
+ },
40
+ serverDocument: (url) => {
41
+ if (typeof url === 'string' && url.startsWith('@')) return url.substring(1);
42
+
43
+ return url ? `${config.documentUrlBase}${url}` : '';
44
+ },
27
45
  serverPath: (url) => {
28
46
  if (!url) return '';
47
+ if (typeof url === 'string' && url.startsWith('@')) return url.substring(1);
29
48
 
30
49
  const dotIndex = url.lastIndexOf('.');
31
50
  const ext = url.substring(dotIndex, url.length).toLowerCase();
@@ -100,117 +119,143 @@ const filters = {
100
119
  return filters.padding((date.getDate()));
101
120
  },
102
121
  ago: (d) => {
103
- let date1 = new Date();
104
- let date2 = new Date(d);
122
+ const date1 = new Date();
123
+ const date2 = new Date(d);
105
124
 
106
- let diff = date.getDateDiff(date1, date2, 'seconds');
125
+ let diff = quasarDate.getDateDiff(date1, date2, 'seconds');
107
126
  if (diff < 1) {
108
- return diff + this.$t('justNow');
109
- } else if (diff < 60) {
110
- return diff + this.$t('secondsAgo');
127
+ return diff + Vue.prototype.$t('justNow');
128
+ } if (diff < 60) {
129
+ return diff + Vue.prototype.$t('secondsAgo');
111
130
  }
112
131
 
113
- diff = date.getDateDiff(date1, date2, 'minutes');
132
+ diff = quasarDate.getDateDiff(date1, date2, 'minutes');
114
133
  if (diff < 60) {
115
- return diff + this.$t('minutesAgo');
134
+ return diff + Vue.prototype.$t('minutesAgo');
116
135
  }
117
136
 
118
137
  if (diff < 24) {
119
- return diff + this.$t('hoursAgo');
138
+ return diff + Vue.prototype.$t('hoursAgo');
120
139
  }
121
140
 
122
- diff = date.getDateDiff(date1, date2, 'days');
141
+ diff = quasarDate.getDateDiff(date1, date2, 'days');
123
142
  if (diff < 31) {
124
- return diff + this.$t('daysAgo');
143
+ return diff + Vue.prototype.$t('daysAgo');
125
144
  }
126
145
 
127
- diff = date.getDateDiff(date1, date2, 'months');
146
+ diff = quasarDate.getDateDiff(date1, date2, 'months');
128
147
  if (diff < 13) {
129
- return diff + this.$t('monthsAgo');
148
+ return diff + Vue.prototype.$t('monthsAgo');
130
149
  }
131
150
 
132
- diff = date.getDateDiff(date1, date2, 'years');
133
- return diff + this.$t('yearsAgo');
151
+ diff = quasarDate.getDateDiff(date1, date2, 'years');
152
+ return diff + Vue.prototype.$t('yearsAgo');
134
153
  },
135
154
  };
136
155
 
137
156
  export default (app, root) => {
138
157
  root.use(MsgDialog);
139
158
 
159
+ const appStore = useAppStore();
160
+
140
161
  return {
141
162
  config: {
142
163
  backendDependencies: ["core-modules"],
143
164
  dictFields: [
144
165
  {
145
- Type: "Category",
146
- Label: "字典数据信息",
147
- },
148
- {
149
- Name: "Index",
150
- Label: "排序号",
151
- Type: "Number",
166
+ Type: 'Category',
167
+ Label: '字典数据信息',
152
168
  },
153
169
  {
154
- Name: "Name",
155
- Label: "数据名称",
156
- Type: "String",
157
- },
158
- {
159
- Name: "Label",
160
- Label: "显示名称",
161
- Type: "String",
170
+ Name: 'Index',
171
+ Label: '排序号',
172
+ Type: 'Number',
162
173
  },
163
174
  {
164
- Name: "Description",
165
- Label: "说明",
166
- Type: "Text",
175
+ Name: 'Name',
176
+ Label: '数据名称',
177
+ Type: 'String',
167
178
  },
179
+ // {
180
+ // Name: 'Label',
181
+ // Label: '显示名称',
182
+ // Type: 'String',
183
+ // },
184
+ // {
185
+ // Name: 'Description',
186
+ // Label: '说明',
187
+ // Type: 'Text',
188
+ // },
168
189
  {
169
- Name: "Type",
170
- Label: "类型",
171
- Type: "Select",
172
- Options: [
173
- {
174
- Label: "普通类型",
175
- Value: "String",
176
- },
190
+ Name: 'Labels',
191
+ Type: 'Tabs',
192
+ Label: '显示内容',
193
+ DataType: 'Array',
194
+ Default: [
177
195
  {
178
- Label: "文件",
179
- Value: "File",
196
+ Locale: appStore.locale || app.config.defaultLocale,
180
197
  },
181
198
  ],
199
+ Options: {
200
+ Dense: true,
201
+ LabelField: 'Name',
202
+ ValueField: 'Locale',
203
+ List: [
204
+ {
205
+ Name: 'Locale',
206
+ Label: '语言',
207
+ Type: 'String',
208
+ ReadOnly: true,
209
+ },
210
+ {
211
+ Name: 'Label',
212
+ Label: '显示名称',
213
+ Type: 'String',
214
+ },
215
+ {
216
+ Name: 'Description',
217
+ Label: '说明',
218
+ Type: 'Text',
219
+ },
220
+ ],
221
+ },
182
222
  },
183
223
  {
184
- Name: "Value",
185
- Label: "值",
186
- Type: "String",
224
+ Name: 'Type',
225
+ Label: '类型',
226
+ Type: 'Select',
227
+ Options: [{
228
+ Label: '普通类型',
229
+ Value: 'String',
230
+ }, {
231
+ Label: '文件',
232
+ Value: 'File',
233
+ }],
187
234
  },
188
235
  {
189
- Name: "Image",
190
- Label: "图片/图标/文件",
191
- Type: "File",
192
- MaxValue: "100m",
193
- Options: {
194
- Dense: false,
195
- AsLink: false,
196
- Ext: "jpg,png,pdf,doc,docx,zip",
197
- },
198
- Tips: [
199
- {
200
- Text: "文件不可超过100M。格式支持:PNG、JPG、PDF、DOC、DOCX、ZIP。",
201
- },
202
- ],
236
+ Name: 'Value',
237
+ Label: '值',
238
+ Type: 'String',
203
239
  },
204
240
  {
205
- Type: "Category",
206
- Label: "高级设置",
241
+ Name: 'Image',
242
+ Label: '图片/图标/文件',
243
+ Type: 'File',
244
+ MaxValue: '100m',
245
+ Options: { Dense: false, AsLink: false, Ext: 'jpg,png,pdf,doc,docx,zip' },
246
+ Tips: [{
247
+ Text: '文件不可超过100M。格式支持:PNG、JPG、PDF、DOC、DOCX、ZIP。',
248
+ }],
207
249
  },
208
250
  {
209
- Name: "Info",
210
- Label: "附加信息",
211
- Type: "Text",
251
+ Type: 'Category',
252
+ Label: '高级设置',
212
253
  },
213
- ],
254
+ {
255
+ Name: 'Info',
256
+ Label: '附加信息',
257
+ Type: 'Text',
258
+ }],
214
259
  menuFields: [
215
260
  {
216
261
  Type: "Category",
@@ -275,29 +320,29 @@ export default (app, root) => {
275
320
  LeveledMenus,
276
321
  BreadCrumbs,
277
322
  ThemeSwitch,
278
- ...FieldComponents.components,
279
323
  Mourning,
324
+ ...FieldComponents.components,
280
325
  },
281
326
  fieldComponents: FieldComponents.fieldComponents,
282
327
 
283
328
  validators: {
284
329
  validatorNotEmpty: (d) =>
285
- d !== undefined && d.length > 0 && d.trim().length > 0,
330
+ d && d.length > 0 && d.trim().length > 0,
286
331
  validatorMobilePhone: (d) =>
287
- /^(0|86|17951)?(13[0-9]|14[0-9]|15[0-9]|16[0-9]|17[0-9]|18[0-9]|19[0-9])[0-9]{8}$/.test(
332
+ !d || /^(0|86|17951)?(13[0-9]|14[0-9]|15[0-9]|16[0-9]|17[0-9]|18[0-9]|19[0-9])[0-9]{8}$/.test(
288
333
  d
289
334
  ),
290
335
  validatorEmail: (d) =>
291
- /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/.test(
336
+ !d || /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/.test(
292
337
  d
293
338
  ),
294
339
  validatorPhoneOrEmail: (d) =>
295
- d !== undefined &&
340
+ d !== void 0 &&
296
341
  d.length > 0 &&
297
- (this.validatorPhone(d) || this.validatorEmail(d)),
342
+ (this.validatorMobilePhone(d) || this.validatorEmail(d)),
298
343
  // validatorMinLength: (d, len = 0) => d !== undefined && d.length >= len,
299
344
  validatorChinaIDNumber: (d) =>
300
- /^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/.test(
345
+ !d || /^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/.test(
301
346
  d
302
347
  ),
303
348
  // validatorSame: (d, to) => d === to,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "free-fe-core-modules",
3
- "version": "0.0.4",
3
+ "version": "0.0.6",
4
4
  "main": "index.js",
5
5
  "repository": "git@gitlab.com:eis-base/modules/free-fe-core-modules.git",
6
6
  "author": "zhiquan",
@@ -16,7 +16,7 @@
16
16
  style="border-bottom: solid 1px grey;"
17
17
  >
18
18
  <div>
19
- {{ prop.node.Label }}
19
+ {{ dictLabel(prop.node) }}
20
20
  <span
21
21
  v-if="prop.node.level === 1 && prop.node.Name"
22
22
  class="dictionary-data-name"
@@ -78,11 +78,29 @@
78
78
  </div>
79
79
  </template>
80
80
  </q-tree>
81
+
82
+ <div class="row items-center justify-center q-my-md" v-if="canImport">
83
+ <q-btn flat @click="exportTranslates" class="btn-primary q-mr-md">导出翻译</q-btn>
84
+ <q-btn flat @click="importTranslates" class="btn-primary">导入翻译</q-btn>
85
+
86
+ <div class="row full-width q-mt-md">
87
+ <q-input v-if="showImportTextArea"
88
+ class="full-width"
89
+ type="textarea"
90
+ autogrow
91
+ v-model="importText"
92
+ placeholder="请输入要导入的内容(tab键分割),如:
93
+ xxx类型 类型一 en-us Type One
94
+ xxx类型 类型二 en-us Type Two"></q-input>
95
+ </div>
96
+ </div>
81
97
  </div>
82
98
  </template>
83
99
 
84
100
  <script>
85
101
  import { defineComponent } from 'vue';
102
+ import { copyToClipboard } from 'quasar';
103
+ import { requests } from '@/boot/axios';
86
104
  import { useObjectData, objectDataProps } from '../../composible/useObjectData';
87
105
 
88
106
  export default defineComponent({
@@ -98,6 +116,9 @@ export default defineComponent({
98
116
  selectedDictNode: {},
99
117
  editingDict: {},
100
118
  dictFields: [],
119
+ canImport: false,
120
+ importText: '',
121
+ showImportTextArea: false,
101
122
  };
102
123
  },
103
124
  setup(props, ctx) {
@@ -124,9 +145,26 @@ export default defineComponent({
124
145
  }
125
146
  },
126
147
  },
148
+ beforeCreate() {
149
+ requests.canI('dict/import').then((r) => {
150
+ if (r) {
151
+ this.canImport = true;
152
+ }
153
+ });
154
+ },
127
155
  created() {
128
156
  this.dictFields = this.getModule('core-modules').config.dictFields;
129
157
  },
158
+ computed: {
159
+ dictLabel() {
160
+ return (d) => {
161
+ if (!d || !d.Labels) return '';
162
+
163
+ const lb = d.Labels.find((l) => l.Locale === this.$i18n.locale );
164
+ return lb && lb.Label;
165
+ }
166
+ }
167
+ },
130
168
  methods: {
131
169
  loadSubDicts({ key, done, node /* , fail */ }) {
132
170
  this.GetData(key, node.level)
@@ -153,6 +191,27 @@ export default defineComponent({
153
191
  }
154
192
  },
155
193
  editNode(n) {
194
+ if (!n) return;
195
+
196
+ n.Labels = n.Labels || [];
197
+ // check labels according to the locales
198
+ const locales = this.ctx.config.locales || [];
199
+ for(let i = 0; i < locales.length; i += 1) {
200
+ const locale = locales[i];
201
+ const existsLabel = n.Labels.find((l) => l.Locale === locale.locale);
202
+
203
+ if (!existsLabel) {
204
+ n.Labels.push({
205
+ Label: '',
206
+ Locale: locale.locale,
207
+ Description: '',
208
+ Name: locale.name,
209
+ });
210
+ } else {
211
+ existsLabel.Name = locale.name;
212
+ }
213
+ }
214
+
156
215
  if (this.selectedDictNode && this.selectedDictNode.id === n.id) {
157
216
  this.selectedDictNode = {};
158
217
  this.editingDict = {};
@@ -276,6 +335,31 @@ export default defineComponent({
276
335
  this.onCancelClick();
277
336
  }
278
337
  },
338
+ importTranslates() {
339
+ if (!this.showImportTextArea) {
340
+ this.showImportTextArea = true;
341
+ this.importText = '';
342
+ } else {
343
+ // do the i
344
+ this.postRequest('/dict/import/trans', {c: this.importText}).then((d) => {
345
+ const data = d && d.data;
346
+ if (d && d.msg === 'OK') {
347
+ this.$q.notify('导入成功!');
348
+ }
349
+ });
350
+ }
351
+ },
352
+ exportTranslates() {
353
+ this.showImportTextArea = false;
354
+ this.getRequest('/dict/export/trans').then((d) => {
355
+ const data = (d && d.data) || {};
356
+
357
+ if (d.data.c) {
358
+ copyToClipboard(d.data.c);
359
+ this.$q.notify('已拷贝到剪切板,可直接粘贴至excel等工具!');
360
+ }
361
+ });
362
+ },
279
363
  },
280
364
  });
281
365
  </script>