diff --git a/admin/package.json b/admin/package.json index 3b00f13..c0a51fa 100644 --- a/admin/package.json +++ b/admin/package.json @@ -75,7 +75,8 @@ "vuex": "3.1.0", "wechat-jssdk": "^5.0.4", "xlsx": "0.14.1", - "xml-js": "1.6.11" + "xml-js": "1.6.11", + "quill": "1.3.7" }, "devDependencies": { "@babel/core": "7.0.0", diff --git a/admin/src/api/workflow/category.js b/admin/src/api/workflow/category.js index d40d7d1..9b53b95 100644 --- a/admin/src/api/workflow/category.js +++ b/admin/src/api/workflow/category.js @@ -21,7 +21,7 @@ export function listAllCategory(query) { // 查询流程分类详细 export function getCategory(categoryId) { return request({ - url: '/workflow/category/' + categoryId, + url: '/workflow/category/info/' + categoryId, method: 'get' }) } @@ -29,7 +29,7 @@ export function getCategory(categoryId) { // 新增流程分类 export function addCategory(data) { return request({ - url: '/workflow/category', + url: '/workflow/category/save', method: 'post', data: data }) @@ -38,16 +38,17 @@ export function addCategory(data) { // 修改流程分类 export function updateCategory(data) { return request({ - url: '/workflow/category', - method: 'put', + url: '/workflow/category/update', + method: 'post', data: data }) } // 删除流程分类 -export function delCategory(categoryId) { +export function delCategory(ids) { return request({ - url: '/workflow/category/' + categoryId, - method: 'delete' + url: '/workflow/category/delete', + method: 'post', + data: typeof ids === 'object' ? ids : [ids] }) } diff --git a/admin/src/api/workflow/deploy.js b/admin/src/api/workflow/deploy.js index 3a8ff76..1d0beb0 100644 --- a/admin/src/api/workflow/deploy.js +++ b/admin/src/api/workflow/deploy.js @@ -29,7 +29,7 @@ export function getBpmnXml(definitionId) { export function changeState(params) { return request({ url: '/workflow/deploy/changeState', - method: 'put', + method: 'post', params: params }) } @@ -37,8 +37,9 @@ export function changeState(params) { // 删除流程部署 export function delDeploy(deployIds) { return request({ - url: '/workflow/deploy/' + deployIds, - method: 'delete' + url: '/workflow/deploy/delete', + method: 'post', + data: typeof deployIds === 'object' ? deployIds : [deployIds] }) } diff --git a/admin/src/api/workflow/form.js b/admin/src/api/workflow/form.js index e903d4c..e22b2d0 100644 --- a/admin/src/api/workflow/form.js +++ b/admin/src/api/workflow/form.js @@ -12,7 +12,7 @@ export function listForm(query) { // 查询流程表单详细 export function getForm(formId) { return request({ - url: '/workflow/form/' + formId, + url: '/workflow/form/info/' + formId, method: 'get' }) } @@ -20,7 +20,7 @@ export function getForm(formId) { // 新增流程表单 export function addForm(data) { return request({ - url: '/workflow/form', + url: '/workflow/form/save', method: 'post', data: data }) @@ -29,7 +29,7 @@ export function addForm(data) { // 修改流程表单 export function updateForm(data) { return request({ - url: '/workflow/form', + url: '/workflow/form/update', method: 'put', data: data }) @@ -46,7 +46,8 @@ export function addDeployForm(data) { // 删除流程表单 export function delForm(formId) { return request({ - url: '/workflow/form/' + formId, - method: 'delete' + url: '/workflow/form/delete', + method: 'post', + data: typeof formId === 'object' ? formId : [formId] }) } diff --git a/admin/src/api/workflow/instance.js b/admin/src/api/workflow/instance.js index 8f41795..a37a638 100644 --- a/admin/src/api/workflow/instance.js +++ b/admin/src/api/workflow/instance.js @@ -3,7 +3,7 @@ import request from '@/utils/request' // 查询流程实例详情信息 export function getDetailInstance(query) { return request({ - url: '/workflow/instance/detail', + url: '/workflow/instance/info', method: 'get', params: query }) diff --git a/admin/src/api/workflow/model.js b/admin/src/api/workflow/model.js index 683672f..81b3270 100644 --- a/admin/src/api/workflow/model.js +++ b/admin/src/api/workflow/model.js @@ -20,7 +20,7 @@ export function historyModel(query) { export function getModel(modelId) { return request({ - url: '/workflow/model/' + modelId, + url: '/workflow/model/info/' + modelId, method: 'get' }) } @@ -28,7 +28,7 @@ export function getModel(modelId) { // 新增模型信息 export function addModel(data) { return request({ - url: '/workflow/model', + url: '/workflow/model/save', method: 'post', data: data }) @@ -37,8 +37,8 @@ export function addModel(data) { // 修改模型信息 export function updateModel(data) { return request({ - url: '/workflow/model', - method: 'put', + url: '/workflow/model/update', + method: 'post', data: data }) } @@ -46,7 +46,7 @@ export function updateModel(data) { // 保存流程模型 export function saveModel(data) { return request({ - url: '/workflow/model/save', + url: '/workflow/model/saveModel', method: 'post', data: data }) @@ -62,8 +62,9 @@ export function latestModel(params) { export function delModel(modelIds) { return request({ - url: '/workflow/model/' + modelIds, - method: 'delete' + url: '/workflow/model/delete', + method: 'post', + data: typeof modelIds === 'object' ? modelIds : [ modelIds ] }) } diff --git a/admin/src/api/workflow/process.js b/admin/src/api/workflow/process.js index a6a8862..73b7be0 100644 --- a/admin/src/api/workflow/process.js +++ b/admin/src/api/workflow/process.js @@ -30,8 +30,9 @@ export function startProcess(processDefId, data) { // 删除流程实例 export function delProcess(ids) { return request({ - url: '/workflow/process/instance/' + ids, - method: 'delete' + url: '/workflow/process/instance/delete', + method: 'post', + data: typeof ids === 'object' ? ids : [ids] }) } @@ -45,7 +46,7 @@ export function getBpmnXml(processDefId) { export function detailProcess(query) { return request({ - url: '/workflow/process/detail', + url: '/workflow/process/info', method: 'get', params: query }) diff --git a/admin/src/components/Editor/index.vue b/admin/src/components/Editor/index.vue new file mode 100644 index 0000000..1f2e15e --- /dev/null +++ b/admin/src/components/Editor/index.vue @@ -0,0 +1,272 @@ + + + + + diff --git a/admin/src/components/FormGenerator/index/RightPanel.vue b/admin/src/components/FormGenerator/index/RightPanel.vue index e066acc..b74d8ed 100644 --- a/admin/src/components/FormGenerator/index/RightPanel.vue +++ b/admin/src/components/FormGenerator/index/RightPanel.vue @@ -639,6 +639,7 @@ import { } from '@/components/FormGenerator/components/generator/config' import { saveFormConf } from '../utils/db' import Templates from "../../../views/appSetting/wxAccount/wxTemplate/index"; +import draggable from 'vuedraggable' const dateTimeFormat = { date: 'yyyy-MM-dd', @@ -655,7 +656,8 @@ export default { components: { Templates, TreeNodeDialog, - IconsDialog + IconsDialog, + draggable }, props: ['showField', 'activeData', 'formConf'], data() { diff --git a/admin/src/main.js b/admin/src/main.js index 9441e7e..ad21ecf 100644 --- a/admin/src/main.js +++ b/admin/src/main.js @@ -68,6 +68,8 @@ import DictTag from '@/components/DictTag' import Pagination from "@/components/Pagination"; // 自定义表格工具组件 import RightToolbar from "@/components/RightToolbar" +// 富文本组件 +import Editor from "@/components/Editor" Vue.use(VueLazyload, { preLoad: 1.3, @@ -89,6 +91,7 @@ DictData.install() Vue.component('DictTag', DictTag) Vue.component('Pagination', Pagination) Vue.component('RightToolbar', RightToolbar) +Vue.component('Editor', Editor) Vue.component('attrFrom', attrFrom) Vue.component('UploadIndex', UploadIndex) Vue.component('SelfUpload', SelfUpload) diff --git a/admin/src/plugins/package/penal/form/ElementForm.vue b/admin/src/plugins/package/penal/form/ElementForm.vue index d15fd5e..0002e33 100644 --- a/admin/src/plugins/package/penal/form/ElementForm.vue +++ b/admin/src/plugins/package/penal/form/ElementForm.vue @@ -217,7 +217,7 @@ export default { methods: { /** 查询表单列表 */ getFormList() { - listForm().then(response => this.formOptions = response.rows) + listForm().then(response => this.formOptions = response.list) }, resetFormList() { this.bpmnELement = window.bpmnInstances.bpmnElement; diff --git a/admin/src/router/modules/flow.js b/admin/src/router/modules/flow.js index 248c97d..b6b32b6 100644 --- a/admin/src/router/modules/flow.js +++ b/admin/src/router/modules/flow.js @@ -35,7 +35,13 @@ const flowRouter = { path: 'detail/:procInsId([\\w|\\-]+)', component: () => import('@/views/workflow/work/detail'), name: 'WorkDetail', - meta: { title: '流程详情', activeMenu: '/workflow/work/own' } + meta: { title: '流程详情', activeMenu: '/flow/workflow/work/own' } + }, + { + path: 'form/build', + component: () => import('@/views/tool/build/index'), + name: 'FormBuild', + meta: { title: '编辑表单', activeMenu: '/flow/workflow/form/index' } } ] } diff --git a/admin/src/utils/db.js b/admin/src/utils/db.js new file mode 100644 index 0000000..2409f3d --- /dev/null +++ b/admin/src/utils/db.js @@ -0,0 +1,54 @@ +const DRAWING_ITEMS = 'drawingItems' +const DRAWING_ITEMS_VERSION = '1.2' +const DRAWING_ITEMS_VERSION_KEY = 'DRAWING_ITEMS_VERSION' +const DRAWING_ID = 'idGlobal' +const TREE_NODE_ID = 'treeNodeId' +const FORM_CONF = 'formConf' + +export function getDrawingList() { + // 加入缓存版本的概念,保证缓存数据与程序匹配 + const version = localStorage.getItem(DRAWING_ITEMS_VERSION_KEY) + if (version !== DRAWING_ITEMS_VERSION) { + localStorage.setItem(DRAWING_ITEMS_VERSION_KEY, DRAWING_ITEMS_VERSION) + saveDrawingList([]) + return null + } + + const str = localStorage.getItem(DRAWING_ITEMS) + if (str) return JSON.parse(str) + return null +} + +export function saveDrawingList(list) { + localStorage.setItem(DRAWING_ITEMS, JSON.stringify(list)) +} + +export function getIdGlobal() { + const str = localStorage.getItem(DRAWING_ID) + if (str) return parseInt(str, 10) + return 100 +} + +export function saveIdGlobal(id) { + localStorage.setItem(DRAWING_ID, `${id}`) +} + +export function getTreeNodeId() { + const str = localStorage.getItem(TREE_NODE_ID) + if (str) return parseInt(str, 10) + return 100 +} + +export function saveTreeNodeId(id) { + localStorage.setItem(TREE_NODE_ID, `${id}`) +} + +export function getFormConf() { + const str = localStorage.getItem(FORM_CONF) + if (str) return JSON.parse(str) + return null +} + +export function saveFormConf(obj) { + localStorage.setItem(FORM_CONF, JSON.stringify(obj)) +} diff --git a/admin/src/utils/index.js b/admin/src/utils/index.js index 07a6ae5..684502e 100644 --- a/admin/src/utils/index.js +++ b/admin/src/utils/index.js @@ -445,4 +445,59 @@ export function mergeRecursive(source, target) { } } return source; -}; \ No newline at end of file +}; + +export const beautifierConf = { + html: { + indent_size: '2', + indent_char: ' ', + max_preserve_newlines: '-1', + preserve_newlines: false, + keep_array_indentation: false, + break_chained_methods: false, + indent_scripts: 'separate', + brace_style: 'end-expand', + space_before_conditional: true, + unescape_strings: false, + jslint_happy: false, + end_with_newline: true, + wrap_line_length: '110', + indent_inner_html: true, + comma_first: false, + e4x: true, + indent_empty_lines: true + }, + js: { + indent_size: '2', + indent_char: ' ', + max_preserve_newlines: '-1', + preserve_newlines: false, + keep_array_indentation: false, + break_chained_methods: false, + indent_scripts: 'normal', + brace_style: 'end-expand', + space_before_conditional: true, + unescape_strings: false, + jslint_happy: true, + end_with_newline: true, + wrap_line_length: '110', + indent_inner_html: true, + comma_first: false, + e4x: true, + indent_empty_lines: true + } +} + +// 首字母大小 +export function titleCase(str) { + return str.replace(/( |^)[a-z]/g, L => L.toUpperCase()) +} + +// 下划转驼峰 +export function camelCase(str) { + return str.replace(/_[a-z]/g, str1 => str1.substr(-1).toUpperCase()) +} + +export function isNumberStr(str) { + return /^[+-]?(0|([1-9]\d*))(\.\d+)?$/g.test(str) +} \ No newline at end of file diff --git a/admin/src/utils/loadBeautifier.js b/admin/src/utils/loadBeautifier.js new file mode 100644 index 0000000..cec9ccd --- /dev/null +++ b/admin/src/utils/loadBeautifier.js @@ -0,0 +1,28 @@ +import loadScript from './loadScript' +import ELEMENT from 'element-ui' +import pluginsConfig from './pluginsConfig' + +let beautifierObj + +export default function loadBeautifier(cb) { + const { beautifierUrl } = pluginsConfig + if (beautifierObj) { + cb(beautifierObj) + return + } + + const loading = ELEMENT.Loading.service({ + fullscreen: true, + lock: true, + text: '格式化资源加载中...', + spinner: 'el-icon-loading', + background: 'rgba(255, 255, 255, 0.5)' + }) + + loadScript(beautifierUrl, () => { + loading.close() + // eslint-disable-next-line no-undef + beautifierObj = beautifier + cb(beautifierObj) + }) +} diff --git a/admin/src/utils/loadMonaco.js b/admin/src/utils/loadMonaco.js new file mode 100644 index 0000000..9bb64f1 --- /dev/null +++ b/admin/src/utils/loadMonaco.js @@ -0,0 +1,40 @@ +import loadScript from './loadScript' +import ELEMENT from 'element-ui' +import pluginsConfig from './pluginsConfig' + +// monaco-editor单例 +let monacoEidtor + +/** + * 动态加载monaco-editor cdn资源 + * @param {Function} cb 回调,必填 + */ +export default function loadMonaco(cb) { + if (monacoEidtor) { + cb(monacoEidtor) + return + } + + const { monacoEditorUrl: vs } = pluginsConfig + + // 使用element ui实现加载提示 + const loading = ELEMENT.Loading.service({ + fullscreen: true, + lock: true, + text: '编辑器资源初始化中...', + spinner: 'el-icon-loading', + background: 'rgba(255, 255, 255, 0.5)' + }) + + !window.require && (window.require = {}) + !window.require.paths && (window.require.paths = {}) + window.require.paths.vs = vs + + loadScript(`${vs}/loader.js`, () => { + window.require(['vs/editor/editor.main'], () => { + loading.close() + monacoEidtor = window.monaco + cb(monacoEidtor) + }) + }) +} diff --git a/admin/src/utils/loadScript.js b/admin/src/utils/loadScript.js new file mode 100644 index 0000000..18112fd --- /dev/null +++ b/admin/src/utils/loadScript.js @@ -0,0 +1,60 @@ +const callbacks = {} + +/** + * 加载一个远程脚本 + * @param {String} src 一个远程脚本 + * @param {Function} callback 回调 + */ +function loadScript(src, callback) { + const existingScript = document.getElementById(src) + const cb = callback || (() => {}) + if (!existingScript) { + callbacks[src] = [] + const $script = document.createElement('script') + $script.src = src + $script.id = src + $script.async = 1 + document.body.appendChild($script) + const onEnd = 'onload' in $script ? stdOnEnd.bind($script) : ieOnEnd.bind($script) + onEnd($script) + } + + callbacks[src].push(cb) + + function stdOnEnd(script) { + script.onload = () => { + this.onerror = this.onload = null + callbacks[src].forEach(item => { + item(null, script) + }) + delete callbacks[src] + } + script.onerror = () => { + this.onerror = this.onload = null + cb(new Error(`Failed to load ${src}`), script) + } + } + + function ieOnEnd(script) { + script.onreadystatechange = () => { + if (this.readyState !== 'complete' && this.readyState !== 'loaded') return + this.onreadystatechange = null + callbacks[src].forEach(item => { + item(null, script) + }) + delete callbacks[src] + } + } +} + +/** + * 顺序加载一组远程脚本 + * @param {Array} list 一组远程脚本 + * @param {Function} cb 回调 + */ +export function loadScriptQueue(list, cb) { + const first = list.shift() + list.length ? loadScript(first, () => loadScriptQueue(list, cb)) : loadScript(first, cb) +} + +export default loadScript diff --git a/admin/src/utils/loadTinymce.js b/admin/src/utils/loadTinymce.js new file mode 100644 index 0000000..e2455fc --- /dev/null +++ b/admin/src/utils/loadTinymce.js @@ -0,0 +1,29 @@ +import loadScript from './loadScript' +import ELEMENT from 'element-ui' +import pluginsConfig from './pluginsConfig' + +let tinymceObj + +export default function loadTinymce(cb) { + const { tinymceUrl } = pluginsConfig + + if (tinymceObj) { + cb(tinymceObj) + return + } + + const loading = ELEMENT.Loading.service({ + fullscreen: true, + lock: true, + text: '富文本资源加载中...', + spinner: 'el-icon-loading', + background: 'rgba(255, 255, 255, 0.5)' + }) + + loadScript(tinymceUrl, () => { + loading.close() + // eslint-disable-next-line no-undef + tinymceObj = tinymce + cb(tinymceObj) + }) +} diff --git a/admin/src/utils/pluginsConfig.js b/admin/src/utils/pluginsConfig.js new file mode 100644 index 0000000..e7f4882 --- /dev/null +++ b/admin/src/utils/pluginsConfig.js @@ -0,0 +1,13 @@ +const CDN = 'https://lib.baomitu.com/' // CDN Homepage: https://cdn.baomitu.com/ +const publicPath = process.env.BASE_URL + +function splicingPluginUrl(PluginName, version, fileName) { + return `${CDN}${PluginName}/${version}/${fileName}` +} + +export default { + beautifierUrl: splicingPluginUrl('js-beautify', '1.13.5', 'beautifier.min.js'), + // monacoEditorUrl: splicingPluginUrl('monaco-editor', '0.19.3', 'min/vs'), // 使用 monaco-editor CDN 链接 + monacoEditorUrl: `${publicPath}libs/monaco-editor/vs`, // 使用 monaco-editor 本地代码 + tinymceUrl: splicingPluginUrl('tinymce', '5.7.0', 'tinymce.min.js') +} diff --git a/admin/src/views/tool/build/CodeTypeDialog.vue b/admin/src/views/tool/build/CodeTypeDialog.vue new file mode 100644 index 0000000..b5c2e2e --- /dev/null +++ b/admin/src/views/tool/build/CodeTypeDialog.vue @@ -0,0 +1,106 @@ + + diff --git a/admin/src/views/tool/build/DraggableItem.vue b/admin/src/views/tool/build/DraggableItem.vue new file mode 100644 index 0000000..30655f2 --- /dev/null +++ b/admin/src/views/tool/build/DraggableItem.vue @@ -0,0 +1,120 @@ + diff --git a/admin/src/views/tool/build/FormDrawer.vue b/admin/src/views/tool/build/FormDrawer.vue new file mode 100644 index 0000000..0373f1a --- /dev/null +++ b/admin/src/views/tool/build/FormDrawer.vue @@ -0,0 +1,332 @@ +