`)
11 | G6.registerNode(this.shape, {
12 | draw(item: any) {
13 | const group = item.getGraphicGroup()
14 | return group.addShape('dom', {
15 | attrs: {
16 | x: 0,
17 | y: 0,
18 | width: option.width,
19 | height: option.height,
20 | html
21 | }
22 | })
23 | },
24 | anchor: [[0.5, 0], [0.5, 1]]
25 | })
26 | return html
27 | }
28 |
29 | createDom(model: any, graph: any) {
30 | console.log(' createDom, model =', model)
31 | let g6Node = graph.add('node', {
32 | id: model.id,
33 | x: model.x,
34 | y: model.y,
35 | shape: this.shape
36 | })
37 | console.log(' 创建 dom,添加 g6节点,g6Node =', g6Node)
38 | return g6Node
39 | }
40 |
41 | /**
42 | * 渲染 vnode 到创建好的 dom 上
43 | *
44 | * @param h 渲染函数
45 | * @param handler 回调函数集合
46 | */
47 | mountNode(h: CreateElement, html: any, node: FlowNode, handler: any) {
48 | console.log(' 渲染 vnode 到创建好的 dom 上,node =', node)
49 | let vueNode = Vue.extend({
50 | props: ['node'],
51 | render: () => {
52 | return new NodeCard().render(h, node, handler)
53 | }
54 | })
55 | new vueNode({
56 | propsData: {
57 | node
58 | }
59 | }).$mount(html)
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/web/src/components/flow/NodeCard.tsx:
--------------------------------------------------------------------------------
1 | import { CreateElement, RenderContext, VNode } from 'vue'
2 | import { FlowNode } from '.'
3 |
4 | export default class NodeCard {
5 | render(h: CreateElement, node: FlowNode, handler: any): VNode {
6 | return (
7 |
8 |
9 |
10 | {node.nodeName}
11 | handler.add(node)} trigger="click" style="float: right;">
12 |
13 |
14 | 任务节点
15 | 审核节点
16 | 分支节点
17 |
18 |
19 | handler.delete(node)}
24 | />
25 | {/* handler.edit(node)}
30 | /> */}
31 |
32 |
33 | 处理人:{node.handlerName}
34 |
35 |
36 | )
37 | }
38 |
39 | isActive(node: any) {
40 | console.log('is active node = ', node)
41 | if (node.active) {
42 | return 'active'
43 | } else {
44 | return ''
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/web/src/components/flow/index.ts:
--------------------------------------------------------------------------------
1 | // G6 node,前两个是 g6 属性
2 | export interface ConfigModel {
3 | // 当前节点 g6 model
4 | model: any
5 | // 节点流程属性
6 | node: FlowNode
7 | // 当前配置栏标签页
8 | activeTab: string
9 | // 流程名称
10 | title: any
11 | // todo 详情展示哪个 task 卡片
12 | todoShow: any
13 | }
14 |
15 | // 节点
16 | export class FlowNode {
17 | // 节点 id
18 | id: any
19 | // 所属流程 id
20 | processId?: number
21 | // 节点名称
22 | nodeName?: string
23 | // 节点类型
24 | nodeType?: string
25 | // 节点状态
26 | status?: FlowStatus
27 | // g6 model
28 | model?: any
29 | // 处理人 id
30 | handlerId?: number
31 | // 处理人 name
32 | handlerName?: string
33 | // 处理人所在组 id
34 | handlerGroupId?: number
35 | // 处理人所在组 name
36 | handlerGroupName?: string
37 | // 关联表单
38 | formId?: number
39 | // 下一节点
40 | nextNodeId?: number
41 | // 是否可流回
42 | canFlowBack?: Boolean
43 | // 是否激活
44 | active?: any
45 | }
46 |
47 | // 流程动作,即流程图中的连线
48 | export interface FlowAction {}
49 |
50 | // 流程节点的状态
51 | export interface FlowStatus {}
52 |
--------------------------------------------------------------------------------
/web/src/components/form/FormConfig.tsx:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import { Prop, Component } from 'vue-property-decorator'
3 | import InputConfig from './configs/InputConfig.vue'
4 | import SelectConfig from './configs/SelectConfig.vue'
5 |
6 | @Component({ components: { InputConfig } })
7 | export default class FormConfig extends Vue {
8 | @Prop()
9 | item!: any
10 |
11 | render() {
12 | console.log('=== 渲染表单配置,item =', this.item)
13 |
14 | if (!this.item.itemType) return
15 |
16 | let config = this.getConfig()
17 | console.debug(' get config =', config)
18 |
19 | return (
20 |
26 | )
27 | }
28 |
29 | // 获取组件
30 | getConfig() {
31 | switch (this.item.itemType) {
32 | case 'el-input':
33 | return InputConfig
34 | case 'el-select':
35 | return SelectConfig
36 | }
37 | }
38 |
39 | // 把事件丢给上一层
40 | updateItem(value: any) {
41 | this.$emit('updateItem', value)
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/web/src/components/form/FormItem.tsx:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import { Prop, Component } from 'vue-property-decorator'
3 | import Input from './items/Input.vue'
4 | import Select from './items/Select.vue'
5 | import { Item } from '.'
6 |
7 | @Component({ components: { Input } })
8 | export default class FormItem extends Vue {
9 | // prop
10 | @Prop() item!: Item
11 | @Prop() curItem!: any
12 |
13 | render() {
14 | console.log('=== 渲染表单元素,item =', this.item)
15 |
16 | let component = this.getItem()
17 | console.debug(' get item =', component)
18 |
19 | return (
20 |
21 | this.$emit('setActiveItem', this.item.uuid)}
28 | >
29 |
30 |
31 |
32 | )
33 | }
34 |
35 | isActive() {
36 | let active = this.curItem ? this.item.uuid == this.curItem.uuid : false
37 | return { active }
38 | }
39 |
40 | getItem() {
41 | switch (this.item.itemType) {
42 | case 'el-input':
43 | return Input
44 | case 'el-select':
45 | return Select
46 | default:
47 | break
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/web/src/components/form/configs/InputConfig.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
37 |
--------------------------------------------------------------------------------
/web/src/components/form/configs/SelectConfig.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
17 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
58 |
--------------------------------------------------------------------------------
/web/src/components/form/display/CardView.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{cardName}}
6 |
7 |
8 | {{item.label}}:
9 | {{item.value}}
10 |
11 |
12 |
13 |
14 |
15 |
27 |
28 |
35 |
--------------------------------------------------------------------------------
/web/src/components/form/index.ts:
--------------------------------------------------------------------------------
1 | export class Item {
2 | idx: number = 0
3 | uuid: string = ''
4 | prop: string = ''
5 | value: string = ''
6 | itemType: string = ''
7 | label: string = ''
8 | inputType: string = ''
9 | placeholder: string = ''
10 | icon: string = ''
11 | prefixIcon: string = ''
12 | }
13 |
14 | export class BaseItem {
15 | static form = {
16 | inline: false
17 | }
18 | static items = [
19 | {
20 | itemType: 'el-input',
21 | prop: 'input',
22 | label: '文本框',
23 | icon: 'el-icon-edit',
24 | type: 'text'
25 | },
26 | {
27 | itemType: 'el-input',
28 | prop: 'input',
29 | label: '多行文本',
30 | icon: 'el-icon-tickets',
31 | type: 'textarea'
32 | },
33 | {
34 | itemType: 'el-select',
35 | prop: 'input',
36 | label: '下拉框',
37 | icon: 'el-icon-arrow-down',
38 | options: [
39 | {
40 | label: '西瓜',
41 | value: 'watermelon'
42 | },
43 | {
44 | label: '可乐',
45 | value: 'cola'
46 | }
47 | ]
48 | },
49 | {
50 | itemType: 'el-date-picker',
51 | prop: 'input',
52 | label: '日期选择器',
53 | icon: 'el-icon-time'
54 | }
55 | ]
56 | }
57 |
58 | export class FormConfigData {
59 | title: string = ''
60 | }
61 |
--------------------------------------------------------------------------------
/web/src/components/form/items/Input.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
14 |
--------------------------------------------------------------------------------
/web/src/components/form/items/Select.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
10 |
11 |
12 |
21 |
--------------------------------------------------------------------------------
/web/src/components/report/AppReport.vue:
--------------------------------------------------------------------------------
1 |
2 | this is app report
3 |
4 |
5 |
--------------------------------------------------------------------------------
/web/src/main.ts:
--------------------------------------------------------------------------------
1 | Vue.config.productionTip = false
2 |
3 | // 主页面
4 | import App from "./App.vue"
5 |
6 | // vue
7 | import Vue from "vue"
8 |
9 | // router
10 | import router from "./routes/router"
11 |
12 | // vuex
13 | import store from "./stores/store"
14 |
15 | // element
16 | import ElementUI from "element-ui"
17 | import "element-ui/lib/theme-chalk/index.css"
18 | Vue.use(ElementUI, {
19 | size: "mini"
20 | })
21 |
22 | // 拖拽插件
23 | import draggable from "vuedraggable"
24 | Vue.use(draggable)
25 |
26 | // 外部样式
27 | import './styles/index.scss'
28 |
29 |
30 |
31 | new Vue({
32 | router,
33 | store,
34 | render: h => h(App)
35 | }).$mount("#app")
36 |
--------------------------------------------------------------------------------
/web/src/routes/router.ts:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Router from 'vue-router'
3 | Vue.use(Router)
4 |
5 | const routes = [
6 | {
7 | path: '/',
8 | name: 'home',
9 | component: () => import('@/views/Home.vue')
10 | },
11 | {
12 | path: '/formEditor',
13 | name: 'formEditor',
14 | component: () => import('@/views/FormEditor.vue')
15 | },
16 | {
17 | path: '/flowEditor',
18 | name: 'flowEditor',
19 | props: true,
20 | component: () => import('@/views/FlowEditor.vue')
21 | },
22 | {
23 | path: '/appRoom',
24 | name: 'AppRoom',
25 | component: () => import('@/views/AppRoom.vue')
26 | },
27 | {
28 | path: '/appAdmin',
29 | name: 'AppAdmin',
30 | component: () => import('@/components/admin/AdminLayout.vue'),
31 | children: [
32 | {
33 | path: 'appInfo',
34 | name: 'AppInfo',
35 | component: () => import('@/components/admin/AppInfo.vue')
36 | },
37 | {
38 | path: 'userAdmin',
39 | name: 'UserAdmin',
40 | component: () => import('@/components/admin/UserAdmin.vue')
41 | },
42 | {
43 | path: 'formAdmin',
44 | name: 'FormAdmin',
45 | component: () => import('@/components/admin/FormAdmin.vue')
46 | },
47 | {
48 | path: 'flowAdmin',
49 | name: 'FlowAdmin',
50 | component: () => import('@/components/admin/FlowAdmin.vue')
51 | }
52 | ]
53 | },
54 | {
55 | path: '/appReport',
56 | name: 'AppReport',
57 | component: () => import('@/components/report/AppReport.vue')
58 | },
59 | {
60 | path: '/appClient',
61 | name: 'AppClient',
62 | props: true,
63 | component: () => import('@/components/client/ClientLayout.vue'),
64 | children: [
65 | {
66 | path: 'start',
67 | name: 'Start',
68 | props: true,
69 | component: () => import('@/components/client/Start.vue')
70 | },
71 | {
72 | path: 'created',
73 | name: 'Created',
74 | props: true,
75 | component: () => import('@/components/client/Created.vue')
76 | },
77 | {
78 | path: 'todo',
79 | name: 'Todo',
80 | props: true,
81 | component: () => import('@/components/client/Todo.vue')
82 | },
83 | {
84 | path: 'done',
85 | name: 'Done',
86 | props: true,
87 | component: () => import('@/components/client/Done.vue')
88 | }
89 | ]
90 | }
91 | ]
92 |
93 | export default new Router({ routes })
94 |
--------------------------------------------------------------------------------
/web/src/shims-tsx.d.ts:
--------------------------------------------------------------------------------
1 | import Vue, { VNode } from 'vue'
2 |
3 | declare global {
4 | namespace JSX {
5 | // tslint:disable no-empty-interface
6 | interface Element extends VNode {}
7 | // tslint:disable no-empty-interface
8 | interface ElementClass extends Vue {}
9 | interface IntrinsicElements {
10 | [elem: string]: any
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/web/src/shims-vue.d.ts:
--------------------------------------------------------------------------------
1 | declare module '*.vue' {
2 | import Vue from 'vue'
3 | export default Vue
4 | }
5 |
--------------------------------------------------------------------------------
/web/src/stores/store.ts:
--------------------------------------------------------------------------------
1 | import Vue from "vue"
2 | import Vuex, { StoreOptions } from "vuex"
3 | Vue.use(Vuex)
4 |
5 | interface FormState {
6 | activeMenu: any // 当前激活的导航菜单
7 | activeIdx: any
8 | meta: any
9 | data: any
10 | arch: any // 存档,缓存创建好的表单和流程结构数据
11 | }
12 |
13 | const store: StoreOptions
= {
14 | state: {
15 | meta: {
16 | form: {
17 | inline: false
18 | },
19 | items: [
20 | {
21 | id: null,
22 | prop: "input", // 字段
23 | value: "", // 值
24 | itemType: "el-input", // 类型
25 | label: "输入框", // 名称
26 | placeholder: "请输入...", // 占位值
27 | icon: "el-icon-edit", // 左侧栏图标
28 | prefixIcon: "", // 输入框前图标
29 | inputType: "text"
30 | },
31 | {
32 | id: null,
33 | itemType: "el-input",
34 | prop: "text",
35 | label: "文本域",
36 | icon: "el-icon-tickets",
37 | inputType: "textarea"
38 | },
39 | {
40 | id: null,
41 | itemType: "el-select",
42 | prop: "select",
43 | label: "下拉框",
44 | icon: "el-icon-arrow-down",
45 | options: [
46 | {
47 | value: "k1",
48 | label: "选项一"
49 | },
50 | {
51 | value: "k2",
52 | label: "选项二"
53 | }
54 | ]
55 | },
56 | {
57 | id: null,
58 | itemType: "el-date-picker",
59 | prop: "date",
60 | label: "日期选择器",
61 | placeholder: "请选择...zhanweifu...",
62 | icon: "el-icon-time"
63 | }
64 | ]
65 | },
66 | data: {
67 | form: {},
68 | formItems: [],
69 | result: {},
70 | table: [],
71 | flow: {}
72 | },
73 | activeMenu: "/",
74 | activeIdx: -1,
75 | arch: {}
76 | },
77 | mutations: {
78 | active(state, idx) {
79 | state.activeIdx = idx
80 | },
81 |
82 | // 设置当前激活导航菜单
83 | setActiveMenu(state, menu) {
84 | state.activeMenu = menu
85 | },
86 |
87 | // 更新表单结构数据
88 | updateFormItems(state, items) {
89 | state.data.formItems = items
90 | },
91 |
92 | // 根据顺序id更新
93 | updateByIdx(state, { idx, item }) {
94 | let target = state.data.formItems[idx]
95 | // 判断是否有该字段,动态添加
96 | // 有点麻烦,不如在初始化时就指定数据
97 | // if (target.type == "el-input")
98 | // Vue.set(target, "prefixIcon", item.prefixIcon)
99 | Object.assign(target, item)
100 | },
101 |
102 | removeByIdx(state, idx) {
103 | state.data.formItems.splice(idx, 1)
104 | },
105 |
106 | // 表单填值后,更新结果值
107 | updateResult(state, { field, value }) {
108 | Vue.set(state.data.result, field, value)
109 | },
110 |
111 | commitTable(state) {
112 | let { result, table } = state.data
113 | table.push(JSON.parse(JSON.stringify(result)))
114 | result = {}
115 | }
116 | }
117 | }
118 |
119 | export default new Vuex.Store(store)
120 |
--------------------------------------------------------------------------------
/web/src/styles/app-admin.scss:
--------------------------------------------------------------------------------
1 | .app-admin {
2 | // 设置父容器与页面等高
3 | height: calc(100% - 36px);
4 | padding-top: 36px;
5 |
6 | .el-container {
7 | height: 100%;
8 | }
9 |
10 | // logo: Form Flow
11 | .logo-box {
12 | margin-top: 20px;
13 | margin-bottom: 10px;
14 | // border-bottom: solid 1px #e6e6e6;
15 | }
16 |
17 | .el-aside {
18 | height: 100%;
19 | width: 100%;
20 | }
21 |
22 | .el-menu {
23 | height: calc(100% - 160px);
24 | border: 0;
25 | .el-menu-item {
26 | height: 50px !important;
27 | line-height: 50px !important;
28 | }
29 | }
30 |
31 | .menu-bar {
32 | border-right: solid 1px #e6e6e6;
33 | width: 200px;
34 | }
35 |
36 | .admin-box {
37 | width: 100%;
38 | height: 100%;
39 | // 顶部工具栏
40 | .tool-bar {
41 | height: 50px !important;
42 | border-bottom: solid 1px #e6e6e6;
43 | // 标题
44 | .tool-bar-title {
45 | margin-top: 18px;
46 | float: left;
47 | }
48 | // 按钮组
49 | .tool-bar-button {
50 | margin-top: 11px;
51 | float: right;
52 | }
53 | }
54 | // 主页面
55 | .main-box {
56 | // .el-form-item {
57 | // height: 29px;
58 | // }
59 | .search-box {
60 | float: right;
61 | .el-input {
62 | width: 150px;
63 | padding-right: 10px;
64 | }
65 | }
66 | .list-box {
67 | .el-table {
68 | padding-top: 20px;
69 | width: 99%;
70 | height: 100%;
71 | .el-icon-delete {
72 | color: #f56c6c;
73 | }
74 | .el-icon-view {
75 | color: #409eff;
76 | }
77 | }
78 | }
79 | }
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/web/src/styles/app-client.scss:
--------------------------------------------------------------------------------
1 | .app-client {
2 | // 设置父容器与页面等高
3 | height: calc(100% - 36px);
4 |
5 | padding-top: 36px;
6 |
7 | .el-container {
8 | height: 100%;
9 | }
10 |
11 | .el-aside {
12 | height: 100%;
13 | }
14 |
15 | .el-menu {
16 | border: 0;
17 | }
18 |
19 | // 左侧菜单容器
20 | .left-aside {
21 | position: fixed;
22 | top: 36px;
23 | width: 60px;
24 | height: 100%;
25 | border-right: solid 1px #f5f5f5;
26 | background-color: #545c64;
27 | // 菜单高度
28 | .el-menu-item {
29 | height: 50px !important;
30 | }
31 | // 菜单宽度
32 | .el-menu--collapse {
33 | width: 100%;
34 | }
35 | // 菜单图标
36 | .el-tooltip {
37 | display: block !important;
38 | padding: 0 30% !important;
39 | [class^='el-icon-'] {
40 | font-size: 22px;
41 | }
42 | }
43 | // 头像框
44 | .avatar-box {
45 | height: 40px;
46 | padding-top: 20px;
47 | display: flex;
48 | align-items: center;
49 | justify-content: center;
50 | img {
51 | width: 60%;
52 | }
53 | }
54 | .name-box {
55 | height: 40px;
56 | text-align: center;
57 | color: aliceblue;
58 | }
59 | .setting-box {
60 | height: 60px;
61 | display: flex;
62 | align-items: center;
63 | justify-content: center;
64 | }
65 | }
66 | .app-box {
67 | width: 100%;
68 | }
69 |
70 | // 右侧主界面容器
71 | .main-box {
72 | margin-left: 410px;
73 | width: 100%;
74 | .el-select {
75 | width: 100%;
76 | }
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/web/src/styles/app-store.scss:
--------------------------------------------------------------------------------
1 | .app-store {
2 | padding-top: 36px;
3 | // 卡片
4 | .app-card {
5 | $elCardHeight: 180px;
6 | .el-button {
7 | float: right;
8 | width: 50px;
9 | padding: 3px 0;
10 | margin-left: 3px;
11 | }
12 | .new-button {
13 | height: $elCardHeight - 20px * 2;
14 | display: flex;
15 | align-items: center;
16 | justify-content: center;
17 | .el-button {
18 | font-size: 200%;
19 | width: 100%;
20 | height: 100%;
21 | }
22 | }
23 | .el-card {
24 | height: $elCardHeight;
25 | margin: 10px 10px;
26 | }
27 | }
28 |
29 | // 弹窗
30 | }
31 |
--------------------------------------------------------------------------------
/web/src/styles/app-task.scss:
--------------------------------------------------------------------------------
1 | // 中间功能列表容器
2 | .tool-box {
3 | position: fixed;
4 | left: 60px;
5 | width: 350px;
6 | height: 100%;
7 | border-right: solid 1px #f5f5f5;
8 | // 搜索框
9 | .search-box {
10 | .el-input {
11 | width: 84%;
12 | padding: 20px 5px 20px 10px;
13 | }
14 | .el-button--mini {
15 | padding: 7px 7px;
16 | }
17 | }
18 | // 流程列表
19 | .task-list {
20 | height: calc(100% - 68px);
21 | .el-table {
22 | height: 100%;
23 | // background-color: #f5f5f5;
24 | // 选中行的颜色
25 | .el-table__body tr.current-row > td {
26 | // background-color: #ebe9e7;
27 | }
28 | // 正常行的颜色
29 | .el-table__row {
30 | height: 50px;
31 | // background-color: #f5f5f5;
32 | }
33 | }
34 | }
35 | }
36 |
37 | .todo-box {
38 | margin-left: 410px;
39 | width: 100%;
40 | .content {
41 | .el-collapse {
42 | margin-bottom: 30px;
43 | }
44 | }
45 | }
46 |
47 | .task-card {
48 | .item {
49 | padding: 10px 0px;
50 | }
51 | .el-tag {
52 | margin-right: 5px;
53 | }
54 | .el-card {
55 | min-height: 65px;
56 | margin-bottom: 20px;
57 | }
58 | .el-icon-question {
59 | margin-right: 5px;
60 | color: #E6A23C;
61 | }
62 | .el-icon-success {
63 | margin-right: 5px;
64 | color: #67C23A;
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/web/src/styles/flow-editor.scss:
--------------------------------------------------------------------------------
1 | .flow-editor {
2 | height: calc(100% - 36px);
3 |
4 | padding-top: 36px;
5 |
6 | .flow-item-container {
7 | .el-button {
8 | display: block;
9 | border-color: #c6e2ff;
10 | width: 80%;
11 | margin: 15px 10%;
12 | text-align: left;
13 | cursor: move;
14 | }
15 | }
16 |
17 | .el-container {
18 | height: 100%;
19 | }
20 |
21 | .el-aside {
22 | width: 20%;
23 | height: 100%;
24 | }
25 |
26 | .el-main {
27 | border-left: 1px solid #e6e6e6;
28 | border-right: 1px solid #e6e6e6;
29 | .flow-page {
30 | .card-container {
31 | height: 67px;
32 | width: 196px;
33 | .el-button {
34 | margin: 0;
35 | }
36 | .el-card__header {
37 | padding: 5px 10px;
38 | font-size: 12px;
39 | .el-dropdown {
40 | height: 12px;
41 | font-size: 6px;
42 | .el-button {
43 | padding: 1px 1px;
44 | vertical-align: middle;
45 | }
46 | }
47 | }
48 | .el-card__body {
49 | font-size: 12px;
50 | height: 40px;
51 | padding: 10px;
52 | }
53 | }
54 | .active {
55 | border: 1px solid #409eff;
56 | }
57 | }
58 | }
59 |
60 | // 中间,图
61 | .flow-page {
62 | background-color: #f9f9f9;
63 | min-height: 100%;
64 | }
65 |
66 | // 右侧配置
67 | .flow-config {
68 | height: 100%;
69 | .el-select {
70 | width: 100%;
71 | }
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/web/src/styles/form-editor.scss:
--------------------------------------------------------------------------------
1 | .form-editor {
2 | .el-aside {
3 | width: 20%;
4 | height: 100%;
5 | }
6 |
7 | padding-top: 36px;
8 |
9 | .el-main {
10 | border-left: 1px solid #e6e6e6;
11 | border-right: 1px solid #e6e6e6;
12 | }
13 |
14 | height: calc(100% - 36px);
15 | .el-container {
16 | height: 100%;
17 | }
18 |
19 | .el-form {
20 | height: 100%;
21 | min-height: 100%;
22 | }
23 |
24 | .pad {
25 | min-height: 100%;
26 | }
27 |
28 | .metas-panel {
29 | .el-button {
30 | display: block;
31 | color: #409eff;
32 | border-color: #c6e2ff;
33 | background-color: #ecf5ff;
34 | width: 80%;
35 | margin: 15px 10%;
36 | text-align: left;
37 | cursor: move;
38 | }
39 | }
40 |
41 | .dynamic-item {
42 | .el-form-item {
43 | padding: 1%;
44 | margin: 0px 5px 0px 0px;
45 | }
46 | .active {
47 | border: 1px solid #409eff;
48 | }
49 | .el-form-item__label {
50 | cursor: move;
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/web/src/styles/home.scss:
--------------------------------------------------------------------------------
1 | .home {
2 | height: 80%;
3 | padding-top: 36px;
4 | display: flex;
5 | align-items: center;
6 | justify-content: center;
7 | img {
8 | cursor: pointer;
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/web/src/styles/index.scss:
--------------------------------------------------------------------------------
1 | @import "home.scss";
2 | @import "app-store.scss";
3 | @import "app-admin.scss";
4 | @import "app-client.scss";
5 | @import "app-task.scss";
6 | @import "form-editor.scss";
7 | @import "flow-editor.scss";
8 |
9 | .el-dialog__header {
10 | border-bottom: 1px solid #ebeef5
11 | }
12 | .el-dialog__body {
13 | padding: 20px;
14 | }
15 | .el-dialog__footer {
16 | padding: 20px;
17 | padding-top: 0px;
18 | }
--------------------------------------------------------------------------------
/web/src/typings/index.d.ts:
--------------------------------------------------------------------------------
1 | declare module 'vuedraggable'
2 | declare module '@antv/g6-editor'
3 | declare module '@antv/g6'
--------------------------------------------------------------------------------
/web/src/util/DateUtil.ts:
--------------------------------------------------------------------------------
1 | import dayjs from 'dayjs'
2 |
3 | export default class DateUtil {
4 | static format(row: any, column: any, cellValue: any, index: any) {
5 | return dayjs(cellValue).format('YYYY-MM-DD HH:mm:ss')
6 | }
7 | static formatTime(time: string) {
8 | return dayjs(time).format('YYYY.MM.DD HH:mm')
9 | }
10 | static formatDay(time: string) {
11 | return dayjs(time).format('MM.DD')
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/web/src/util/UuidUtil.ts:
--------------------------------------------------------------------------------
1 | export default class UuidUtil {
2 | static uuid() {
3 | var s: any = []
4 |
5 | var hexDigits = '0123456789abcdef'
6 |
7 | for (var i = 0; i < 36; i++) {
8 | s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1)
9 | }
10 |
11 | // bits 12-15 of the time_hi_and_version field to 0010
12 | s[14] = '4'
13 |
14 | // bits 6-7 of the clock_seq_hi_and_reserved to 01
15 | s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1)
16 |
17 | s[8] = s[13] = s[18] = s[23] = '-'
18 |
19 | var uuid = s.join('')
20 | return uuid
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/web/src/views/AppRoom.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/web/src/views/FlowEditor.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
439 |
--------------------------------------------------------------------------------
/web/src/views/FormEditor.vue:
--------------------------------------------------------------------------------
1 |
2 |
67 |
68 |
69 |
188 |
--------------------------------------------------------------------------------
/web/src/views/Home.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |

4 |
5 |
6 |
7 |
21 |
--------------------------------------------------------------------------------
/web/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "esnext",
4 | "module": "esnext",
5 | "strict": true,
6 | "jsx": "preserve",
7 | "importHelpers": true,
8 | "moduleResolution": "node",
9 | "experimentalDecorators": true,
10 | "esModuleInterop": true,
11 | "allowSyntheticDefaultImports": true,
12 | "sourceMap": true,
13 | "baseUrl": ".",
14 | "types": [
15 | "webpack-env"
16 | ],
17 | "paths": {
18 | "@/*": [
19 | "src/*"
20 | ]
21 | },
22 | "lib": [
23 | "esnext",
24 | "dom",
25 | "dom.iterable",
26 | "scripthost"
27 | ]
28 | },
29 | "include": [
30 | "src/**/*.ts",
31 | "src/**/*.tsx",
32 | "src/**/*.vue",
33 | "tests/**/*.ts",
34 | "tests/**/*.tsx"
35 | ],
36 | "exclude": [
37 | "node_modules"
38 | ]
39 | }
40 |
--------------------------------------------------------------------------------