free-fe-core-modules 0.0.5 → 0.0.7
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.
- package/README.md +131 -6
- package/components/Basic/BreadCrumbs.vue +18 -9
- package/components/Basic/LeveledMenus.vue +13 -5
- package/components/Basic/SummaryHead.vue +5 -2
- package/components/Dialog/BasicDialog.vue +4 -2
- package/components/FloatingWindow/index.vue +9 -1
- package/components/SelectLocales/index.vue +18 -15
- package/components/SlidingCarousel/index.vue +5 -2
- package/components/SlidingNews/index.vue +33 -24
- package/free-field/Fields/Category.js +2 -1
- package/free-field/Fields/Check.js +1 -0
- package/free-field/Fields/DynamicList.js +14 -11
- package/free-field/Fields/TimeRange.vue +4 -2
- package/free-field/composible/freeFieldLabel.js +10 -3
- package/index.js +123 -78
- package/package.json +2 -2
- package/view/dict/index.vue +85 -1
- package/view/mourning/mourning.vue +17 -12
package/README.md
CHANGED
|
@@ -1,7 +1,132 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
<p align="center">
|
|
2
|
+
<a href="https://freeeis.aslancer.com" target="_blank">
|
|
3
|
+
<img
|
|
4
|
+
src="https://user-images.githubusercontent.com/33030594/227073920-03ed137f-c4f7-4ed7-ae05-d781dd1991f7.png"
|
|
5
|
+
alt="FreeEIS"
|
|
6
|
+
width="250"
|
|
7
|
+
/>
|
|
8
|
+
</a>
|
|
9
|
+
</p>
|
|
3
10
|
|
|
4
|
-
|
|
5
|
-
-
|
|
6
|
-
-
|
|
7
|
-
|
|
11
|
+
<p align="center">
|
|
12
|
+
<a href="https://www.npmjs.com/package/free-fe-starter-kit">
|
|
13
|
+
<img src="https://img.shields.io/npm/v/free-fe-starter-kit" alt="Version">
|
|
14
|
+
</a>
|
|
15
|
+
<a href="https://www.npmjs.com/package/free-fe-starter-kit">
|
|
16
|
+
<img src="https://img.shields.io/npm/dm/free-fe-starter-kit" alt="Download">
|
|
17
|
+
</a>
|
|
18
|
+
<a href="https://github.com/didi/LogicFlow/blob/master/LICENSE">
|
|
19
|
+
<img src="https://img.shields.io/npm/l/free-fe-starter-kit" alt="LICENSE">
|
|
20
|
+
</a>
|
|
21
|
+
</p>
|
|
22
|
+
|
|
23
|
+
简体中文 | [English](https://github.com/freeeis/.github/blob/main/profile/README.en-us.md)
|
|
24
|
+
|
|
25
|
+
[更多文档](https://freeeis.aslancer.com)(完善中……)
|
|
26
|
+
|
|
27
|
+
FreeEIS,是一种可扩展的、企业级系统统一开发框架。FreeEIS旨在解决越来越流行的远程协作开发中,系统拆分及组装的问题。并且通过积累越来越多的功能模块,使得系统的搭建更快捷。
|
|
28
|
+
|
|
29
|
+
## 特性
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
- 🛠 高扩展性
|
|
33
|
+
|
|
34
|
+
“一切”皆可为模块,从一个漂亮的按钮、一个简单的运算工具函数,到一套完备的ERP系统,都可以做为FreeEIS中的模块。模块拆分灵活,模块间可零耦合,模块即插即用。同时考虑模块自身的扩展性,可开发模块做为另一模块的“插件”。
|
|
35
|
+
|
|
36
|
+
- 🎯 聚焦业务逻辑
|
|
37
|
+
|
|
38
|
+
FreeEIS内核将与业务逻辑无关的功能预制,使得开发人员只需要考虑模块内部的业务逻辑,诸如模块加载、引用依赖、多语言支持、多样式支持、数据模型生成、权限管理与控制、路由生成、Mock功能等等都已经内置在FreeEIS内核中,模块开发过程中不需要再做考虑。特别是像数据模型生成、权限管理与控制,这类通常会消耗开发者大量精力的部分,也被剥离解耦,大大提高了开发效率。
|
|
39
|
+
|
|
40
|
+
- 🚀 快速集成
|
|
41
|
+
|
|
42
|
+
FreeEIS中所有的功能都被拆分成大大小小的模块,在需要整合成为一整套系统的时候,只需要配置所需要的模块,或将模块代码(目录)放到统一的位置,FreeEIS内核将自动加载所有需要的模块,并整合为一套完整的系统。这使得系统级的组装更加灵活,可以根据需要极速集成成为大小和功能完全不同的完整系统。
|
|
43
|
+
|
|
44
|
+
- 🍹 不改变开发习惯
|
|
45
|
+
|
|
46
|
+
FreeEIS尽力做到少封装,无SDK,避免改变现有的开发习惯。一个脚手架工程 + 一套简单的规范就可以实现快速的系统开发。
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
- 🚪 代码安全
|
|
50
|
+
|
|
51
|
+
在某些场景下,我们不希望所有的开发人员拿到很多甚至所有功能模块的代码才可以运行调试自己的功能模块。FreeEIS的设计使得开发人员只关注自己所开发的模块也只需要访问到自己开发的功能模块的代码库,可以在不影响开发的情况下确保代码安全。
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
## 使用
|
|
55
|
+
|
|
56
|
+
### 前提条件
|
|
57
|
+
|
|
58
|
+
当前版本中,前端使用了如下技术或框架,您需要已经对这些技术或框架有所了解甚至拥有相关经验:
|
|
59
|
+
- [VUE3](https://vuejs.org/)是我们整个前端部分的基础。
|
|
60
|
+
- [Quasar Framework](https://quasar.dev/)是一个基于VUE的框架,具有多种特性,如拥有丰富的组件,支持Material Design,支持跨平台等等。但FreeEIS中并禁止使用其他组件库,您依然可以根据自己的喜好选择,我们只是借助了Quasar Framework的脚手架和跨平台支持,甚至您也可以将FreeEIS放在与Quasar Framework无关的其他VUE工程中使用。
|
|
61
|
+
|
|
62
|
+
当前版本中,后端使用了如下技术或框架,您需要已经对这些技术或框架有所了解甚至拥有相关经验:
|
|
63
|
+
- [NodeJS](https://nodejs.org/)是基础!
|
|
64
|
+
- [ExpressJS](http://expressjs.com/)是基础之上的基础😉。
|
|
65
|
+
- [MongoDB](https://www.mongodb.com/)、[Mongoose](http://www.mongoosejs.net/), 目前已经实现的数据库操作模块基于Mongoose,但我们期望后续逐步添加更多数据库的支持。
|
|
66
|
+
- [Redis](https://redis.io/),建议使用Redis的docker镜像部署。
|
|
67
|
+
|
|
68
|
+
### 前端
|
|
69
|
+
|
|
70
|
+
```sh
|
|
71
|
+
# 安装前端脚手架
|
|
72
|
+
|
|
73
|
+
$ git clone https://github.com/freeeis/free-fe-starter-kit.git fe
|
|
74
|
+
|
|
75
|
+
# 安装依赖包
|
|
76
|
+
$ cd fe
|
|
77
|
+
$ yarn install
|
|
78
|
+
|
|
79
|
+
# 运行
|
|
80
|
+
$ yarn start
|
|
81
|
+
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
### 后端
|
|
86
|
+
|
|
87
|
+
在使用后端前,请先安装MongoDB和Redis,也可以使用远程部署的实例,FreeEIS的相关内置模块允许配置其地址和端口。另外,Redis不是必须的,如果无法连接到Redis,FreeEIS将使用[memory-cache](https://github.com/ptarjan/node-cache#readme)做为缓存,这不会影响开发或运行。
|
|
88
|
+
|
|
89
|
+
```sh
|
|
90
|
+
# 安装后端脚手架
|
|
91
|
+
|
|
92
|
+
$ git clone https://github.com/freeeis/free-be-starter-kit.git be
|
|
93
|
+
|
|
94
|
+
# 安装依赖包
|
|
95
|
+
$ cd be
|
|
96
|
+
$ yarn install
|
|
97
|
+
|
|
98
|
+
# 创建存放机密数据的文件(也可手动创建)
|
|
99
|
+
$ touch global.js
|
|
100
|
+
|
|
101
|
+
# 运行
|
|
102
|
+
$ yarn start
|
|
103
|
+
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### 访问系统
|
|
107
|
+
|
|
108
|
+
成功运行前后端工程后,通过链接[http://localhost:8080](http://localhost:8080/)来访问系统。也可通过修改配置,使前端运行后自动从浏览器中打开页面。
|
|
109
|
+
|
|
110
|
+
## 内置功能模块
|
|
111
|
+
|
|
112
|
+
我们期望通过提供一些内置的功能模块,来简化开发人员的工作,比如账号管理、权限控制这些在任何系统中都需要的功能,我们做成了模块,可以直接被引用。下面是一个列表,列出来目前我们所提供的内置功能模块,以及他们的功能。当然,根据我们的宗旨,我们不强制您使用任何一个内置模块,您可以根据自己的需要定制开发所有需要的模块,而只是使用FreeEIS的规范并用她来组装系统。并且我们鼓励您这么做,因为我们相信,在任何一个领域都有很多比我们更加专业的开发人员,我们希望有更多的人开发出越来越多的强大的功能模块供他人使用。
|
|
113
|
+
|
|
114
|
+
| 模块 | 前端模块 | 后端模块 | 功能 | 说明|
|
|
115
|
+
| :------- |------- |------- | ----|----: |
|
|
116
|
+
| 内核 | [free-fe-core](https://github.com/freeeis/free-fe-core) | [free-be-core](https://github.com/freeeis/free-be-core) | 加载其他模块|默认添加在脚手架中|
|
|
117
|
+
| 基础功能 | [free-fe-core-modules](https://github.com/freeeis/free-fe-core-modules) | [free-be-core-modules](https://github.com/freeeis/free-be-core-modules)| 数据字典、日志、菜单管理、系统配置、<br>错误代码管理、缓存管理、哀悼日、<br>文件处理、数据校验方法、<br>多语言支持、多皮肤支持等等 | 默认添加在脚手架中|
|
|
118
|
+
| 账号管理 | [free-fe-account](https://github.com/freeeis/free-fe-account) | [free-be-account](https://github.com/freeeis/free-be-account) |账号管理、组织结构管理、权限管理、<br>角色管理、权限控制| 默认添加在脚手架中|
|
|
119
|
+
| 数据库 | / | [free-be-mongodb](https://github.com/freeeis/free-be-mongodb) |数据库操作| 默认添加在脚手架中|
|
|
120
|
+
| 演示模块 | [free-fe-demo](https://github.com/freeeis/free-fe-demo) | [free-be-demo](https://github.com/freeeis/free-be-demo) |演示模块内部结构和使用方法| 默认添加在脚手架中|
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
## 联系我们
|
|
124
|
+
|
|
125
|
+
扫码添加微信加入交流群
|
|
126
|
+
|
|
127
|
+
<img width="120" alt="" style="margin-left:24px" src="https://user-images.githubusercontent.com/33030594/227093642-b38b7871-16eb-48b6-b96a-191433dc55c2.png">
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
## 贡献
|
|
131
|
+
|
|
132
|
+
FreeEIS虽然已经在多个大型项目中得到了验证,但做为开源项目她依然处于比较早期的阶段,需要各位大牛的帮助一起成长。我们欢迎任何建议或意见或贡献,小到一个字的修改,大到架构的调整建议。我们感谢您的任何捐赠、PR、Issue等等,有任何相关问题都可以发邮件给我们:【[freeeis@xixineis.com](mailto:freeeis@xixineis.com)】。感谢🙏🙏!!
|
|
@@ -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
|
|
41
|
-
mt = this
|
|
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
|
|
44
|
-
const rm = this
|
|
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
|
|
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
|
|
74
|
-
? this
|
|
75
|
-
: this
|
|
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
|
-
:
|
|
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
|
|
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
|
|
64
|
+
this.router.push(this.url);
|
|
57
65
|
}
|
|
58
66
|
}
|
|
59
67
|
},
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
</q-btn>
|
|
18
18
|
</template>
|
|
19
19
|
<script>
|
|
20
|
-
import { defineComponent } from 'vue';
|
|
20
|
+
import { defineComponent, getCurrentInstance, computed } from 'vue';
|
|
21
21
|
import useAppStore from '@/stores/app';
|
|
22
22
|
|
|
23
23
|
export default defineComponent({
|
|
@@ -25,22 +25,25 @@ export default defineComponent({
|
|
|
25
25
|
props: {
|
|
26
26
|
icon: { type: String, default: 'translate' },
|
|
27
27
|
},
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
},
|
|
32
|
-
},
|
|
33
|
-
created() {
|
|
28
|
+
setup() {
|
|
29
|
+
const { proxy:vm } = getCurrentInstance();
|
|
30
|
+
|
|
34
31
|
const appStore = useAppStore();
|
|
35
|
-
this.$i18n.locale = appStore.locale || (this.locales && (this.locales.length > 0) && this.locales[0]);
|
|
36
|
-
},
|
|
37
|
-
methods: {
|
|
38
|
-
localeChanged(l){
|
|
39
|
-
const store = useAppStore();
|
|
40
32
|
|
|
41
|
-
|
|
42
|
-
|
|
33
|
+
const localeChanged = (l) => {
|
|
34
|
+
vm.$i18n.locale = l;
|
|
35
|
+
appStore.SET_LOCALE(l);
|
|
43
36
|
}
|
|
44
|
-
|
|
37
|
+
|
|
38
|
+
const locales = computed(() => (vm.ctx.config.locales) || []);
|
|
39
|
+
const locale = appStore.locale || vm.ctx.config.defaultLocale || (locales.value && (locales.value.length > 0) && locales.value[0].locale) || 'zh-cn';
|
|
40
|
+
|
|
41
|
+
localeChanged(locale);
|
|
42
|
+
|
|
43
|
+
return {
|
|
44
|
+
locales,
|
|
45
|
+
localeChanged,
|
|
46
|
+
};
|
|
47
|
+
},
|
|
45
48
|
});
|
|
46
49
|
</script>
|
|
@@ -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
|
|
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="(
|
|
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
|
|
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
|
-
<
|
|
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
|
-
<
|
|
39
|
-
|
|
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
|
-
|
|
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
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
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
|
|
133
|
-
this
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
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.
|
|
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
|
|
11
|
-
|
|
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
|
-
|
|
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 =>
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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
|
-
|
|
104
|
-
|
|
122
|
+
const date1 = new Date();
|
|
123
|
+
const date2 = new Date(d);
|
|
105
124
|
|
|
106
|
-
let diff =
|
|
125
|
+
let diff = quasarDate.getDateDiff(date1, date2, 'seconds');
|
|
107
126
|
if (diff < 1) {
|
|
108
|
-
return diff +
|
|
109
|
-
}
|
|
110
|
-
return diff +
|
|
127
|
+
return diff + Vue.prototype.$t('justNow');
|
|
128
|
+
} if (diff < 60) {
|
|
129
|
+
return diff + Vue.prototype.$t('secondsAgo');
|
|
111
130
|
}
|
|
112
131
|
|
|
113
|
-
diff =
|
|
132
|
+
diff = quasarDate.getDateDiff(date1, date2, 'minutes');
|
|
114
133
|
if (diff < 60) {
|
|
115
|
-
return diff +
|
|
134
|
+
return diff + Vue.prototype.$t('minutesAgo');
|
|
116
135
|
}
|
|
117
136
|
|
|
118
137
|
if (diff < 24) {
|
|
119
|
-
return diff +
|
|
138
|
+
return diff + Vue.prototype.$t('hoursAgo');
|
|
120
139
|
}
|
|
121
140
|
|
|
122
|
-
diff =
|
|
141
|
+
diff = quasarDate.getDateDiff(date1, date2, 'days');
|
|
123
142
|
if (diff < 31) {
|
|
124
|
-
return diff +
|
|
143
|
+
return diff + Vue.prototype.$t('daysAgo');
|
|
125
144
|
}
|
|
126
145
|
|
|
127
|
-
diff =
|
|
146
|
+
diff = quasarDate.getDateDiff(date1, date2, 'months');
|
|
128
147
|
if (diff < 13) {
|
|
129
|
-
return diff +
|
|
148
|
+
return diff + Vue.prototype.$t('monthsAgo');
|
|
130
149
|
}
|
|
131
150
|
|
|
132
|
-
diff =
|
|
133
|
-
return diff +
|
|
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:
|
|
146
|
-
Label:
|
|
147
|
-
},
|
|
148
|
-
{
|
|
149
|
-
Name: "Index",
|
|
150
|
-
Label: "排序号",
|
|
151
|
-
Type: "Number",
|
|
166
|
+
Type: 'Category',
|
|
167
|
+
Label: '字典数据信息',
|
|
152
168
|
},
|
|
153
169
|
{
|
|
154
|
-
Name:
|
|
155
|
-
Label:
|
|
156
|
-
Type:
|
|
157
|
-
},
|
|
158
|
-
{
|
|
159
|
-
Name: "Label",
|
|
160
|
-
Label: "显示名称",
|
|
161
|
-
Type: "String",
|
|
170
|
+
Name: 'Index',
|
|
171
|
+
Label: '排序号',
|
|
172
|
+
Type: 'Number',
|
|
162
173
|
},
|
|
163
174
|
{
|
|
164
|
-
Name:
|
|
165
|
-
Label:
|
|
166
|
-
Type:
|
|
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:
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
Label: "普通类型",
|
|
175
|
-
Value: "String",
|
|
176
|
-
},
|
|
190
|
+
Name: 'Labels',
|
|
191
|
+
Type: 'Tabs',
|
|
192
|
+
Label: '显示内容',
|
|
193
|
+
DataType: 'Array',
|
|
194
|
+
Default: [
|
|
177
195
|
{
|
|
178
|
-
|
|
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:
|
|
185
|
-
Label:
|
|
186
|
-
Type:
|
|
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:
|
|
190
|
-
Label:
|
|
191
|
-
Type:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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 !==
|
|
340
|
+
d !== void 0 &&
|
|
296
341
|
d.length > 0 &&
|
|
297
|
-
(this.
|
|
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,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "free-fe-core-modules",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.7",
|
|
4
4
|
"main": "index.js",
|
|
5
|
-
"repository": "
|
|
5
|
+
"repository": "https://github.com/freeeis/free-fe-core-modules.git",
|
|
6
6
|
"author": "zhiquan",
|
|
7
7
|
"license": "UNLICENSED",
|
|
8
8
|
"dependencies": {
|
package/view/dict/index.vue
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
style="border-bottom: solid 1px grey;"
|
|
17
17
|
>
|
|
18
18
|
<div>
|
|
19
|
-
{{ prop.node
|
|
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
|
|
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
|
-
|
|
34
|
+
bodyElem.className = classes.join(' ');
|
|
31
35
|
});
|
|
32
36
|
},
|
|
33
37
|
});
|
|
34
38
|
</script>
|
|
35
39
|
|
|
36
|
-
<style lang="
|
|
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>
|