free-fe-core-modules 0.0.5 → 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.
@@ -9,12 +9,13 @@
9
9
  />
10
10
  <q-space></q-space>
11
11
  <q-btn v-if="meta.length > 0 && canBack"
12
- flat icon="keyboard_arrow_left" @click="back">{{backText}}</q-btn>
12
+ flat icon="keyboard_arrow_left" @click="back">{{$t(backText)}}</q-btn>
13
13
  </q-breadcrumbs>
14
14
  </template>
15
15
 
16
16
  <script>
17
17
  import { defineComponent } from 'vue';
18
+ import { useRouter, useRoute } from 'vue-router'
18
19
  import { mapWritableState } from 'pinia';
19
20
  import useAppStore from '@/stores/app';
20
21
 
@@ -24,6 +25,14 @@ export default defineComponent({
24
25
  canBack: { type: Boolean, default: true },
25
26
  backText: { type: String, default: '返回' },
26
27
  },
28
+ setup() {
29
+ const router = useRouter();
30
+
31
+ return {
32
+ router,
33
+ route: useRoute(),
34
+ };
35
+ },
27
36
  data() {
28
37
  return {
29
38
  nextFlag: false,
@@ -37,11 +46,11 @@ export default defineComponent({
37
46
  let mt = [];
38
47
  if (this.crumbs && this.crumbs.length) {
39
48
  mt = this.crumbs;
40
- } else if (this.$route.meta && this.$route.meta.length) {
41
- mt = this.$route.meta;
49
+ } else if (this.route.meta && this.route.meta.length) {
50
+ mt = this.route.meta;
42
51
  } else {
43
- for (let i = 0; i < this.$route.matched.length; i += 1) {
44
- const rm = this.$route.matched[i];
52
+ for (let i = 0; i < this.route.matched.length; i += 1) {
53
+ const rm = this.route.matched[i];
45
54
  if (rm.meta && rm.meta.length) mt = rm.meta;
46
55
  }
47
56
  }
@@ -65,14 +74,14 @@ export default defineComponent({
65
74
  return;
66
75
  }
67
76
 
68
- this.$router.push(this.meta[index].route);
77
+ this.router.push(this.meta[index].route);
69
78
  // const backIndex = this.meta.length - 1 - index;
70
79
  // window.history.go(-backIndex);
71
80
  },
72
81
  back() {
73
- const URL = this.$route.fullPath.indexOf('?') === -1
74
- ? this.$route.fullPath
75
- : this.$route.fullPath.split('?')[0];
82
+ const URL = this.route.fullPath.indexOf('?') === -1
83
+ ? this.route.fullPath
84
+ : this.route.fullPath.split('?')[0];
76
85
  if (this.whiteList.indexOf(URL) === -1) {
77
86
  window.history.go(-1);
78
87
  } else {
@@ -6,7 +6,7 @@
6
6
  v-if="!m.Sub || m.Sub.length <= 0"
7
7
  clickable
8
8
  class="simple"
9
- :class="group"
9
+ :class="`${group} level_${level || 0}`"
10
10
  :to="m.Route"
11
11
  expand-icon-class="simple-expand-icon"
12
12
  :ref="`menuItem_${group}_index`"
@@ -17,7 +17,7 @@
17
17
  <q-icon class="leaf-icon" :name="m.Icon"></q-icon>
18
18
  </q-item-section>
19
19
  <q-item-section>
20
- <div class="q-item__label leaf-label">{{$t(m.Label)}}</div>
20
+ <div class="q-item__label leaf-label">{{translate ? $t(m.Label) : m.Label}}</div>
21
21
  </q-item-section>
22
22
  <q-item-section side
23
23
  class="leaf-expand-icon">
@@ -32,10 +32,10 @@
32
32
 
33
33
  <q-expansion-item
34
34
  v-if="m.Sub && m.Sub.length > 0"
35
- :value="m.Sub.filter(s => $route.fullPath.startsWith(s.Route)).length > 0"
35
+ :modelValue="m.Sub.filter(s => $route.fullPath.startsWith(s.Route)).length > 0"
36
36
  exact
37
37
  class="expansion"
38
- :class="group"
38
+ :class="`${group} level_${level || 0}`"
39
39
  :dense-toggle="dense"
40
40
  expand-icon-class="expansion-icon"
41
41
  :expand-icon="expandIcon"
@@ -44,13 +44,14 @@
44
44
  :content-inset-level="inset ? insetLevel || (showIcon ? 0.6 : 0.4) : undefined"
45
45
  expand-separator
46
46
  :icon="(showIcon && m.Icon) ? m.Icon : undefined"
47
- :label="$t(m.Label)"
47
+ :label="translate ? $t(m.Label) : m.Label"
48
48
  :to="m.Route"
49
49
  :ref="`menuItem_${group}_index`"
50
50
  >
51
51
  <leveled-menus
52
52
  :menus="m.Sub"
53
53
  :group="`${group}_${index}`"
54
+ :level="level + 1"
54
55
  :showIcon="showIcon"
55
56
  :insetLevel="insetLevel"
56
57
  :dense="dense"
@@ -71,6 +72,7 @@ import { defineComponent } from 'vue';
71
72
  export default defineComponent({
72
73
  name: 'LeveledMenus',
73
74
  props: {
75
+ level: { type: Number, default: 0 },
74
76
  group: { type: String, default: 'DEFAULT' },
75
77
  menus: { type: Array },
76
78
  showIcon: { type: Boolean, default: true },
@@ -81,7 +83,13 @@ export default defineComponent({
81
83
  leafExpandedIcon: { type: String, default: undefined },
82
84
  expandIcon: { type: String, default: undefined },
83
85
  expandedIcon: { type: String, default: undefined },
86
+ translate: { type: Boolean, default: false },
84
87
  },
85
88
  });
86
89
  </script>
87
90
 
91
+ <style lang="sass">
92
+ .leveled-menu
93
+ .simple-expand-icon
94
+ display: none
95
+ </style>
@@ -76,6 +76,7 @@
76
76
 
77
77
  <script>
78
78
  import { defineComponent } from 'vue';
79
+ import { useRouter } from 'vue-router'
79
80
  import { useObjectData, objectDataProps } from '../../composible/useObjectData';
80
81
 
81
82
  export default defineComponent({
@@ -90,10 +91,12 @@ export default defineComponent({
90
91
  data,
91
92
  refreshData,
92
93
  } = useObjectData(props, ctx);
94
+ const router = useRouter();
93
95
 
94
96
  return {
95
- data,
97
+ data,
96
98
  refreshData,
99
+ router,
97
100
  };
98
101
  },
99
102
  created() {
@@ -150,7 +153,7 @@ export default defineComponent({
150
153
  // if (item.route_query) rParams.query = item.route_query;
151
154
  // if (item.route_params) rParams.params = item.route_params;
152
155
 
153
- this.$router.push(item.route);
156
+ this.router.push(item.route);
154
157
  }
155
158
  });
156
159
  },
@@ -66,6 +66,8 @@
66
66
  :Field="field"
67
67
  :values="fieldsData"
68
68
  ref="fieldsToValid"
69
+ @cancel="btnCancel"
70
+ @ok="onOKClick"
69
71
  @input="onInputFieldInput(field)"
70
72
  ></free-field>
71
73
  </div>
@@ -75,7 +77,7 @@
75
77
  <q-card-actions
76
78
  align="center"
77
79
  class="action-buttons full-width absolute"
78
- v-if="canOK || canCancel"
80
+ v-if="(canOK || canCancel) && !hideActions"
79
81
  >
80
82
  <q-btn
81
83
  :label="cancelText"
@@ -126,6 +128,7 @@ export default defineComponent({
126
128
  needText: { type: Boolean, default: false },
127
129
  text_label: { type: String, default: '' },
128
130
  max_text_length: { type: Number, default: 100 },
131
+ hideActions: { type: Boolean, default: false },
129
132
  canOK: { type: Boolean, default: true },
130
133
  canCancel: { type: Boolean, default: false },
131
134
  canClose: { type: Boolean, default: false },
@@ -205,7 +208,6 @@ export default defineComponent({
205
208
  // following method is REQUIRED
206
209
  // (don't change its name --> "show")
207
210
  show() {
208
- // debugger
209
211
  this.$refs.dialog.show();
210
212
  this.timeout_counter();
211
213
 
@@ -15,6 +15,7 @@
15
15
 
16
16
  <script>
17
17
  import { defineComponent } from 'vue';
18
+ import { useRouter } from 'vue-router'
18
19
 
19
20
  export default defineComponent({
20
21
  name: 'FloatingWindow',
@@ -24,6 +25,13 @@ export default defineComponent({
24
25
  onlyIn: { type: Array, default: () => [] },
25
26
  top: { type: Number, default: 0 },
26
27
  },
28
+ setup() {
29
+ const router = useRouter();
30
+
31
+ return {
32
+ router,
33
+ };
34
+ },
27
35
  data() {
28
36
  return {
29
37
  x: 150,
@@ -53,7 +61,7 @@ export default defineComponent({
53
61
  if (/^(http|https):\/\/.*/.test(this.url)) {
54
62
  window.open(this.url);
55
63
  } else {
56
- this.$router.push(this.url);
64
+ this.router.push(this.url);
57
65
  }
58
66
  }
59
67
  },
@@ -27,6 +27,7 @@
27
27
 
28
28
  <script>
29
29
  import { defineComponent } from 'vue';
30
+ import { useRouter } from 'vue-router'
30
31
  import { useObjectData, objectDataProps } from '../../composible/useObjectData';
31
32
 
32
33
  export default defineComponent({
@@ -48,9 +49,11 @@ export default defineComponent({
48
49
  refreshData,
49
50
  } = useObjectData(props, ctx);
50
51
 
52
+ const router = useRouter();
51
53
  return {
52
- data,
54
+ data,
53
55
  refreshData,
56
+ router,
54
57
  };
55
58
  },
56
59
  data() {
@@ -77,7 +80,7 @@ export default defineComponent({
77
80
  // window.location.href = l;
78
81
  window.open(carouse.url);
79
82
  } else {
80
- this.$router.push(carouse.url);
83
+ this.router.push(carouse.url);
81
84
  }
82
85
  }
83
86
  },
@@ -2,7 +2,7 @@
2
2
  <div
3
3
  v-if="visible"
4
4
  class="row full-width sliding-news justify-center"
5
- :class="(data && data.length) ? '' : 'empty'"
5
+ :class="(localData.length) ? '' : 'empty'"
6
6
  >
7
7
  <span class="sliding-news-label row items-center">
8
8
  <e-icon
@@ -26,23 +26,19 @@
26
26
  vertical
27
27
  >
28
28
  <q-carousel-slide
29
- v-for="(carouse, index) in data"
29
+ v-for="(carouse, index) in localData"
30
30
  :key="index"
31
31
  :name="index"
32
32
  @click="newsClicked(carouse)"
33
33
  style="cursor: pointer; padding: 0; margin: 0;"
34
34
  >
35
- <div class="row items-center justify-center full-height">
36
- <span class="sliding-news-title">{{carouse.Title}}</span>
35
+ <div class="row no-wrap items-center justify-center full-height">
36
+ <div class="sliding-news-title col ellipsis">{{carouse.title}}</div>
37
37
  <q-space />
38
- <span class="float-right sliding-news-right">
39
- <span
40
- class="sliding-news-date"
41
- :style="`line-height: ${heightString}`"
42
- >
38
+ <div class="sliding-news-right"
39
+ :style="`line-height: ${heightString}`">
43
40
  {{filter('normalDate',(carouse.PublishDate || carouse.LastUpdateDate))}}
44
- </span>
45
- </span>
41
+ </div>
46
42
  </div>
47
43
  </q-carousel-slide>
48
44
  </q-carousel>
@@ -65,6 +61,7 @@
65
61
  </template>
66
62
 
67
63
  <script>
64
+ import { useRouter, useRoute } from 'vue-router'
68
65
  import { defineComponent } from 'vue';
69
66
  import { useObjectData, objectDataProps } from '../../composible/useObjectData';
70
67
 
@@ -72,6 +69,13 @@ export default defineComponent({
72
69
  name: 'SlidingNews',
73
70
  props: {
74
71
  ...objectDataProps,
72
+ fields: {
73
+ type: Object,
74
+ default: () => ({
75
+ title: 'Title',
76
+ date: 'LastUpdateDate'
77
+ }),
78
+ },
75
79
  interval: { type: Number, default: 3000 },
76
80
  height: { type: String, default: '40px' },
77
81
  width: { type: String, default: '100%' },
@@ -88,10 +92,13 @@ export default defineComponent({
88
92
  data,
89
93
  refreshData,
90
94
  } = useObjectData(props, ctx);
95
+ const router = useRouter();
91
96
 
92
97
  return {
93
- data,
98
+ data,
94
99
  refreshData,
100
+ router,
101
+ route: useRoute(),
95
102
  };
96
103
  },
97
104
  data() {
@@ -113,24 +120,26 @@ export default defineComponent({
113
120
  return this.height;
114
121
  },
115
122
  },
116
- // watch: {
117
- // visible() {
118
- // if (!this.visible) {
119
- // clearInterval(this.timer);
120
- // }
121
- // },
122
- // },
123
- // created() {
124
- // this.timer = setInterval(this.carouselNext, this.interval);
125
- // },
123
+ localData() {
124
+ const fks = Object.keys(this.fields || {});
125
+ return (this.data || []).map((dd) => {
126
+ const ret = {};
127
+ for (let i = 0; i < fks.length; i += 1) {
128
+ const fk = fks[i];
129
+ ret[fk] = dd[this.fields[fk]];
130
+ }
131
+
132
+ return { ...dd, ...ret };
133
+ });
134
+ },
126
135
  methods: {
127
136
  newsClicked(news) {
128
137
  let url = '';
129
138
  if (news.url) url += news.url;
130
139
  else if (this.url) url += `${this.url}${news.id || ''}`;
131
140
 
132
- if (url && this.$route.fullPath !== url) {
133
- this.$router.push({ path: url });
141
+ if (url && this.route.fullPath !== url) {
142
+ this.router.push({ path: url });
134
143
  }
135
144
  },
136
145
  // carouselNext() {
@@ -1,4 +1,5 @@
1
1
  import { defineComponent, h } from 'vue';
2
+ import { freeFieldProps } from '../composible/useFreeField';
2
3
 
3
4
  export default defineComponent({
4
5
  name: 'InputFieldCategory',
@@ -9,7 +10,7 @@ export default defineComponent({
9
10
  Description: '',
10
11
  },
11
12
  props: {
12
- Field: { type: Object },
13
+ ...freeFieldProps,
13
14
  },
14
15
  methods: {
15
16
  },
@@ -76,6 +76,7 @@ export default defineComponent({
76
76
  if (!props.Field) return {};
77
77
 
78
78
  const { fieldData, setFieldData } = useFreeField(props);
79
+ fieldData.value = fieldData.value || false;
79
80
 
80
81
  const before = (props.Field.showLabel && !props.Field.dense && props.Field.Label !== void 0) ? () => h(freeFieldLabel, {
81
82
  Field: props.Field,
@@ -463,7 +463,8 @@ export default defineComponent({
463
463
  );
464
464
 
465
465
  const bodyCell = (slotProps) => {
466
- fieldsToValidate.value = slotProps.col?.List?.length > 1 ? slotProps.col.List.map((col) =>
466
+ const fields = ref([]);
467
+ fields.value = slotProps.col?.List?.length > 1 ? slotProps.col.List.map((col) =>
467
468
  h(FreeField, {
468
469
  Field: columnField(col, true, slotProps.col),
469
470
  values: slotProps.row,
@@ -478,7 +479,11 @@ export default defineComponent({
478
479
  onInput: cellChanged,
479
480
  }),
480
481
  ];
481
-
482
+
483
+ // add fields from the current cell to validate list
484
+ fieldsToValidate.value.push(...fields.value);
485
+
486
+
482
487
  if (slotProps.col.name === "listActions") {
483
488
  return h(QTd, null, {
484
489
  default: () =>
@@ -503,15 +508,13 @@ export default defineComponent({
503
508
  rowspan: slotProps.value ? slotProps.value.rowspan || "1" : "1",
504
509
  class: "items-center justify-center",
505
510
  },
506
- [
507
- h(
508
- "span",
509
- {
510
- class: "full-height full-width",
511
- },
512
- fieldsToValidate.value,
513
- )
514
- ]
511
+ () => h(
512
+ "span",
513
+ {
514
+ class: "full-height full-width",
515
+ },
516
+ fields.value,
517
+ )
515
518
  );
516
519
  }
517
520
  };
@@ -96,7 +96,7 @@
96
96
  </template>
97
97
 
98
98
  <script>
99
- import { defineComponent, ref, getCurrentInstance } from 'vue';
99
+ import { defineComponent, ref, getCurrentInstance, watch, watchEffect, computed } from 'vue';
100
100
  import { useFreeField, freeFieldProps } from '../composible/useFreeField';
101
101
  import { useFormValidator} from '../../composible/useFormValidator';
102
102
 
@@ -300,7 +300,7 @@ export default defineComponent({
300
300
  const objOptions = computed(() => props.Field.Options || undefined);
301
301
 
302
302
  const timeOptions = computed(() => {
303
- if (objOptions.values) return undefined;
303
+ if (objOptions.value) return undefined;
304
304
 
305
305
  if (!props.Field.MinValue && !props.Field.MaxValue) {
306
306
  return undefined;
@@ -349,6 +349,8 @@ export default defineComponent({
349
349
  })
350
350
 
351
351
  return {
352
+ min,
353
+ max,
352
354
  fieldData,
353
355
  locale,
354
356
 
@@ -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.5",
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>
@@ -8,6 +8,9 @@ import { defineComponent } from 'vue';
8
8
 
9
9
  export default defineComponent({
10
10
  name: 'mourningNode',
11
+ setup() {
12
+ return {};
13
+ },
11
14
  mounted() {
12
15
  const mourningStore = useMourningStore();
13
16
 
@@ -18,8 +21,9 @@ export default defineComponent({
18
21
  }
19
22
  mourningStore.mourning = inMourning;
20
23
 
21
- let classes = this.$root.$el.className.split(' ').filter((c) => !!c);
22
-
24
+ let bodyElem = document.getElementsByTagName('html')[0];
25
+ let classes = bodyElem.className.split(' ').filter((c) => !!c);
26
+
23
27
  if (inMourning) {
24
28
  classes.push('mourning-site');
25
29
  } else {
@@ -27,19 +31,20 @@ export default defineComponent({
27
31
  classes = classes.filter((c) => c.trim() !== 'mourning-site');
28
32
  }
29
33
 
30
- this.$root.$el.className = classes.join(' ');
34
+ bodyElem.className = classes.join(' ');
31
35
  });
32
36
  },
33
37
  });
34
38
  </script>
35
39
 
36
- <style lang="sass">
37
- .mourning-site
38
- -webkit-filter: grayscale(100%)
39
- -moz-filter: grayscale(100%)
40
- -ms-filter: grayscale(100%)
41
- -o-filter: grayscale(100%)
42
- filter: grayscale(100%)
43
- filter: progid:DXImageTransform.Microsoft.BasicImage(grayscale=1)
44
- filter: gray
40
+ <style lang="scss">
41
+ .mourning-site {
42
+ -webkit-filter: grayscale(100%);
43
+ -moz-filter: grayscale(100%);
44
+ -ms-filter: grayscale(100%);
45
+ -o-filter: grayscale(100%);
46
+ filter: grayscale(100%);
47
+ filter: progid:DXImageTransform.Microsoft.BasicImage(grayscale=1);
48
+ filter: gray;
49
+ }
45
50
  </style>