50 changed files with 1596 additions and 916 deletions
@ -0,0 +1,102 @@ |
|||
{ |
|||
"name": "io.sc.platform.system.frontend", |
|||
"version": "8.1.38", |
|||
"description": "", |
|||
"private": false, |
|||
"keywords": [], |
|||
"author": "", |
|||
"license": "ISC", |
|||
"scripts": { |
|||
"dev": "nodemon", |
|||
"serve": "node ./util-components-generator.cjs && cross-env NODE_ENV=development webpack serve --config webpack.env.serve.cjs", |
|||
"build": "node ./util-components-generator.cjs && cross-env NODE_ENV=development webpack --config webpack.env.build.cjs", |
|||
"prod": "node ./util-components-generator.cjs && cross-env NODE_ENV=production webpack --config webpack.env.prod.cjs", |
|||
"sync": "platform sync", |
|||
"clean": "rm -rf ./node_modules && rm -rf pnpm-lock.yaml" |
|||
}, |
|||
"engines": { |
|||
"node": ">=18", |
|||
"pnpm": ">=7" |
|||
}, |
|||
"publishConfig": { |
|||
"registry": "http://nexus.sc.io:8000/repository/npm-releases/", |
|||
"access": "public" |
|||
}, |
|||
"devDependencies": { |
|||
"@babel/core": "7.23.7", |
|||
"@babel/preset-env": "7.23.7", |
|||
"@babel/preset-typescript": "7.23.3", |
|||
"@babel/plugin-transform-class-properties": "7.23.3", |
|||
"@babel/plugin-transform-object-rest-spread": "7.23.4", |
|||
"@quasar/app-webpack": "3.12.1", |
|||
"@quasar/cli": "2.3.0", |
|||
"@types/mockjs": "1.0.10", |
|||
"@types/node": "20.10.6", |
|||
"@typescript-eslint/eslint-plugin": "6.17.0", |
|||
"@typescript-eslint/parser": "6.17.0", |
|||
"@vue/compiler-sfc": "3.4.3", |
|||
"@webpack-cli/serve": "2.0.5", |
|||
"autoprefixer": "10.4.16", |
|||
"babel-loader": "9.1.3", |
|||
"clean-webpack-plugin": "4.0.0", |
|||
"copy-webpack-plugin": "11.0.0", |
|||
"cross-env": "7.0.3", |
|||
"css-loader": "6.8.1", |
|||
"eslint": "8.56.0", |
|||
"eslint-config-prettier": "9.1.0", |
|||
"eslint-plugin-prettier": "5.1.2", |
|||
"eslint-plugin-vue": "9.19.2", |
|||
"eslint-webpack-plugin": "4.0.1", |
|||
"html-webpack-plugin": "5.6.0", |
|||
"json5": "2.2.3", |
|||
"mini-css-extract-plugin": "2.7.6", |
|||
"nodemon": "3.0.2", |
|||
"postcss": "8.4.32", |
|||
"postcss-import": "16.0.0", |
|||
"postcss-loader": "7.3.4", |
|||
"postcss-preset-env": "9.3.0", |
|||
"prettier": "3.1.1", |
|||
"sass": "1.69.7", |
|||
"sass-loader": "13.3.3", |
|||
"typescript": "5.3.3", |
|||
"vue-loader": "17.4.2", |
|||
"webpack": "5.89.0", |
|||
"webpack-bundle-analyzer": "4.10.1", |
|||
"webpack-cli": "5.1.4", |
|||
"webpack-dev-server": "4.15.1", |
|||
"webpack-merge": "5.10.0", |
|||
"@vue/babel-plugin-jsx": "1.1.5" |
|||
}, |
|||
"dependencies": { |
|||
"@codemirror/autocomplete": "6.11.1", |
|||
"@codemirror/commands": "6.3.3", |
|||
"@codemirror/lang-html": "6.4.7", |
|||
"@codemirror/lang-java": "6.0.1", |
|||
"@codemirror/lang-javascript": "6.2.1", |
|||
"@codemirror/lang-json": "6.0.1", |
|||
"@codemirror/lang-sql": "6.5.4", |
|||
"@codemirror/lang-xml": "6.0.2", |
|||
"@codemirror/language": "6.10.0", |
|||
"@codemirror/search": "6.5.5", |
|||
"@codemirror/state": "6.4.0", |
|||
"@codemirror/view": "6.23.0", |
|||
"@maxgraph/core": "0.9.0", |
|||
"@quasar/extras": "1.16.9", |
|||
"@vueuse/core": "10.7.1", |
|||
"axios": "1.6.3", |
|||
"codemirror": "6.0.1", |
|||
"dayjs": "1.11.10", |
|||
"echarts": "5.4.3", |
|||
"exceljs": "4.4.0", |
|||
"file-saver": "2.0.5", |
|||
"luckyexcel": "1.0.1", |
|||
"mockjs": "1.1.0", |
|||
"pinia": "2.1.7", |
|||
"quasar": "2.14.5", |
|||
"tailwindcss": "3.4.0", |
|||
"vue": "3.4.3", |
|||
"vue-dompurify-html": "5.0.1", |
|||
"vue-i18n": "9.8.0", |
|||
"vue-router": "4.2.5" |
|||
} |
|||
} |
@ -0,0 +1,157 @@ |
|||
<template> |
|||
<div> |
|||
<div class="row justify-end q-gutter-md q-py-md"> |
|||
<q-select |
|||
v-model="selectedTheme" |
|||
:label="$t('theme')" |
|||
:options="themeOptions" |
|||
option-label="name" |
|||
option-value="id" |
|||
emit-value |
|||
map-options |
|||
dense |
|||
outlined |
|||
style="min-width: 200px" |
|||
@update:model-value="themeChanged" |
|||
> |
|||
<template #option="scope"> |
|||
<q-item v-bind="scope.itemProps"> |
|||
<q-item-section> |
|||
<q-item-label>{{ scope.opt.name }}</q-item-label> |
|||
</q-item-section> |
|||
<q-item-section v-if="scope.opt.active" avatar> |
|||
<q-icon name="bi-check" /> |
|||
</q-item-section> |
|||
<q-item-section v-else avatar> |
|||
<w-icon-empty></w-icon-empty> |
|||
</q-item-section> |
|||
</q-item> |
|||
</template> |
|||
</q-select> |
|||
<q-btn :label="$t('theme.action.setDefaultTheme')" icon="bi-heart" no-caps outline dense padding="2px 10px" @click="defaultTheme" /> |
|||
<q-btn :label="$t('add')" icon="add" no-caps outline dense padding="2px 10px" @click="prompt = true" /> |
|||
<q-btn :label="$t('delete')" icon="delete" no-caps outline dense padding="2px 10px" @click="removeTheme" /> |
|||
<q-btn :label="$t('save')" icon="save" no-caps outline dense padding="2px 10px" @click="saveTheme" /> |
|||
</div> |
|||
</div> |
|||
</template> |
|||
<script setup lang="ts"> |
|||
import { ref } from 'vue'; |
|||
import { useI18n } from 'vue-i18n'; |
|||
import { useQuasar } from 'quasar'; |
|||
import { Environment, axios, Tools, NotifyManager } from 'platform-core'; |
|||
import Brand from './theme/Brand.vue'; |
|||
import LoadingBar from './theme/LoadingBar.vue'; |
|||
import Topper from './theme/Topper.vue'; |
|||
import Sider from './theme/Sider.vue'; |
|||
import TagViewBar from './theme/TagViewBar.vue'; |
|||
import Main from './theme/Main.vue'; |
|||
import Footer from './theme/Footer.vue'; |
|||
import Login from './theme/Login.vue'; |
|||
import Scroller from './theme/Scroller.vue'; |
|||
import Notifier from './theme/Notifier.vue'; |
|||
import Grid from './theme/Grid.vue'; |
|||
|
|||
const { t } = useI18n(); |
|||
const quasar = useQuasar(); |
|||
|
|||
const selectedTab = ref('brand'); |
|||
const selectedTheme = ref(''); |
|||
const themeOptions = ref([]); |
|||
const prompt = ref(false); |
|||
const configureName = ref(''); |
|||
|
|||
let configureMap = new Map<string, object>(); |
|||
|
|||
const newTheme = () => { |
|||
axios.post(Environment.apiContextPath('/api/lcdp/configure'), { name: configureName.value }).then((response) => { |
|||
NotifyManager.info(t('success')); |
|||
refresh(() => { |
|||
selectedTheme.value = response.data.id; |
|||
configureName.value = ''; |
|||
}); |
|||
}); |
|||
}; |
|||
|
|||
const themeChanged = (value) => { |
|||
const option = configureMap.get(value); |
|||
if (option) { |
|||
Tools.mergeObject(Environment.getConfigure(), option); |
|||
} |
|||
}; |
|||
|
|||
const defaultTheme = () => { |
|||
quasar |
|||
.dialog({ |
|||
title: t('confirm'), |
|||
message: t('theme.action.setDefaultTheme.confirm', { themeName: configureMap.get(selectedTheme.value).name }), |
|||
cancel: true, |
|||
persistent: true, |
|||
}) |
|||
.onOk(() => { |
|||
axios.post(Environment.apiContextPath('/api/lcdp/configure/activeConfigure/') + selectedTheme.value).then((response) => { |
|||
NotifyManager.info(t('success')); |
|||
refresh(); |
|||
}); |
|||
}); |
|||
}; |
|||
|
|||
const removeTheme = () => { |
|||
quasar |
|||
.dialog({ |
|||
title: t('confirm'), |
|||
message: t('theme.action.delete.confirm', { themeName: configureMap.get(selectedTheme.value).name }), |
|||
cancel: true, |
|||
persistent: true, |
|||
}) |
|||
.onOk(() => { |
|||
axios.delete(Environment.apiContextPath('/api/lcdp/configure/') + selectedTheme.value).then((response) => { |
|||
NotifyManager.info(t('success')); |
|||
refresh(); |
|||
selectedTheme.value = ''; |
|||
}); |
|||
}); |
|||
}; |
|||
|
|||
const saveTheme = () => { |
|||
quasar |
|||
.dialog({ |
|||
title: t('confirm'), |
|||
message: t('theme.action.save.confirm', { themeName: configureMap.get(selectedTheme.value).name }), |
|||
cancel: true, |
|||
persistent: true, |
|||
}) |
|||
.onOk(() => { |
|||
const data = {}; |
|||
Tools.mergeObject(data, configureMap.get(selectedTheme.value)); |
|||
data.theme = Environment.getConfigure().theme; |
|||
|
|||
axios.put(Environment.apiContextPath('/api/lcdp/configure/') + selectedTheme.value, data).then((response) => { |
|||
NotifyManager.info(t('success')); |
|||
}); |
|||
}); |
|||
}; |
|||
|
|||
const refresh = (callback?: any) => { |
|||
axios.get(Environment.apiContextPath('/api/lcdp/configure')).then((response) => { |
|||
themeOptions.value.splice(0, themeOptions.value.length); |
|||
const items = response.data.content; |
|||
if (items && items.length > 0) { |
|||
const options = []; |
|||
for (const item of items) { |
|||
themeOptions.value.push(item); |
|||
configureMap.set(item.id, item); |
|||
if (item.active) { |
|||
selectedTheme.value = item.id; |
|||
} |
|||
} |
|||
//themeOptions.value = options; |
|||
if (callback) { |
|||
callback(); |
|||
} |
|||
} |
|||
}); |
|||
}; |
|||
|
|||
refresh(); |
|||
</script> |
@ -1,25 +1,160 @@ |
|||
<template> |
|||
<div class="w-full border rounded-md" style="height: 100%"> |
|||
<ToolBar></ToolBar> |
|||
<q-splitter :model-value="20" unit="%" separator-style="width:2px;" style="height: calc(100% - 50px)"> |
|||
<template #before> |
|||
<SideBar></SideBar> |
|||
</template> |
|||
<template #after> |
|||
<q-splitter :model-value="65" unit="%" separator-style="width:2px;" style="height: 100%"> |
|||
<template #before><Editor></Editor></template> |
|||
<template #after><Properties></Properties></template> |
|||
</q-splitter> |
|||
</template> |
|||
</q-splitter> |
|||
<div> |
|||
<div class="row justify-end q-gutter-md q-py-md"> |
|||
<q-select |
|||
v-model="selectedTheme" |
|||
:label="$t('theme')" |
|||
:options="themeOptions" |
|||
option-label="name" |
|||
option-value="id" |
|||
emit-value |
|||
map-options |
|||
dense |
|||
outlined |
|||
style="min-width: 200px" |
|||
@update:model-value="themeChanged" |
|||
> |
|||
<template #option="scope"> |
|||
<q-item v-bind="scope.itemProps"> |
|||
<q-item-section> |
|||
<q-item-label>{{ scope.opt.name }}</q-item-label> |
|||
</q-item-section> |
|||
<q-item-section v-if="scope.opt.active" avatar> |
|||
<q-icon name="bi-check" /> |
|||
</q-item-section> |
|||
<q-item-section v-else avatar> |
|||
<w-icon-empty></w-icon-empty> |
|||
</q-item-section> |
|||
</q-item> |
|||
</template> |
|||
</q-select> |
|||
<q-btn :label="$t('theme.action.setDefaultTheme')" icon="bi-heart" no-caps outline dense padding="2px 10px" @click="defaultTheme" /> |
|||
<q-btn :label="$t('add')" icon="add" no-caps outline dense padding="2px 10px" @click="prompt = true" /> |
|||
<q-btn :label="$t('delete')" icon="delete" no-caps outline dense padding="2px 10px" @click="removeTheme" /> |
|||
<q-btn :label="$t('save')" icon="save" no-caps outline dense padding="2px 10px" @click="saveTheme" /> |
|||
</div> |
|||
<q-dialog v-model="prompt" persistent> |
|||
<q-card style="min-width: 350px"> |
|||
<q-card-section class="q-pt-md"> |
|||
<q-input v-model="configureName" :label="$t('name')" outlined dense autofocus @keyup.enter="prompt = false" /> |
|||
</q-card-section> |
|||
|
|||
<q-card-actions align="right" class="text-primary"> |
|||
<q-btn v-close-popup flat :label="$t('cancel')" /> |
|||
<q-btn v-close-popup flat :label="$t('confirm')" @click="newTheme" /> |
|||
</q-card-actions> |
|||
</q-card> |
|||
</q-dialog> |
|||
</div> |
|||
</template> |
|||
|
|||
<script setup lang="ts"> |
|||
import { ref, onMounted } from 'vue'; |
|||
import { type CellStyle, Graph, InternalEvent } from '@maxgraph/core'; |
|||
import ToolBar from './ToolBar.vue'; |
|||
import SideBar from './SideBar.vue'; |
|||
import Editor from './Editor.vue'; |
|||
import Properties from './Properties.vue'; |
|||
import { ref } from 'vue'; |
|||
import { useI18n } from 'vue-i18n'; |
|||
import { useQuasar } from 'quasar'; |
|||
import { Environment, axios, Tools, NotifyManager } from '@/platform'; |
|||
|
|||
const { t } = useI18n(); |
|||
const quasar = useQuasar(); |
|||
|
|||
const selectedTab = ref('brand'); |
|||
const selectedTheme = ref(''); |
|||
const themeOptions = ref([]); |
|||
const prompt = ref(false); |
|||
const configureName = ref(''); |
|||
|
|||
let configureMap = new Map<string, object>(); |
|||
|
|||
const newTheme = () => { |
|||
axios.post(Environment.apiContextPath('/api/lcdp/configure'), { name: configureName.value }).then((response) => { |
|||
NotifyManager.info(t('success')); |
|||
refresh(() => { |
|||
selectedTheme.value = response.data.id; |
|||
configureName.value = ''; |
|||
}); |
|||
}); |
|||
}; |
|||
|
|||
const themeChanged = (value) => { |
|||
const option = configureMap.get(value); |
|||
if (option) { |
|||
Tools.mergeObject(Environment.getConfigure(), option); |
|||
} |
|||
}; |
|||
|
|||
const defaultTheme = () => { |
|||
quasar |
|||
.dialog({ |
|||
title: t('confirm'), |
|||
message: t('theme.action.setDefaultTheme.confirm', { themeName: configureMap.get(selectedTheme.value).name }), |
|||
cancel: true, |
|||
persistent: true, |
|||
}) |
|||
.onOk(() => { |
|||
axios.post(Environment.apiContextPath('/api/lcdp/configure/activeConfigure/') + selectedTheme.value).then((response) => { |
|||
NotifyManager.info(t('success')); |
|||
refresh(); |
|||
}); |
|||
}); |
|||
}; |
|||
|
|||
const removeTheme = () => { |
|||
quasar |
|||
.dialog({ |
|||
title: t('confirm'), |
|||
message: t('theme.action.delete.confirm', { themeName: configureMap.get(selectedTheme.value).name }), |
|||
cancel: true, |
|||
persistent: true, |
|||
}) |
|||
.onOk(() => { |
|||
axios.delete(Environment.apiContextPath('/api/lcdp/configure/') + selectedTheme.value).then((response) => { |
|||
NotifyManager.info(t('success')); |
|||
refresh(); |
|||
selectedTheme.value = ''; |
|||
}); |
|||
}); |
|||
}; |
|||
|
|||
const saveTheme = () => { |
|||
quasar |
|||
.dialog({ |
|||
title: t('confirm'), |
|||
message: t('theme.action.save.confirm', { themeName: configureMap.get(selectedTheme.value).name }), |
|||
cancel: true, |
|||
persistent: true, |
|||
}) |
|||
.onOk(() => { |
|||
const data = {}; |
|||
Tools.mergeObject(data, configureMap.get(selectedTheme.value)); |
|||
data.theme = Environment.getConfigure().theme; |
|||
|
|||
axios.put(Environment.apiContextPath('/api/lcdp/configure/') + selectedTheme.value, data).then((response) => { |
|||
NotifyManager.info(t('success')); |
|||
}); |
|||
}); |
|||
}; |
|||
|
|||
const refresh = (callback?: any) => { |
|||
axios.get(Environment.apiContextPath('/api/lcdp/configure')).then((response) => { |
|||
configureMap.clear(); |
|||
const options = []; |
|||
selectedTheme.value = ''; |
|||
|
|||
const items = response.data.content; |
|||
if (items && items.length > 0) { |
|||
for (const item of items) { |
|||
options.push(item); |
|||
configureMap.set(item.id, item); |
|||
if (item.active) { |
|||
selectedTheme.value = item.id; |
|||
} |
|||
} |
|||
} |
|||
themeOptions.value = options; |
|||
if (callback) { |
|||
callback(); |
|||
} |
|||
}); |
|||
}; |
|||
|
|||
refresh(); |
|||
</script> |
|||
|
@ -0,0 +1,157 @@ |
|||
<template> |
|||
<div> |
|||
<div class="row justify-end q-gutter-md q-py-md"> |
|||
<q-select |
|||
v-model="selectedTheme" |
|||
:label="$t('theme')" |
|||
:options="themeOptions" |
|||
option-label="name" |
|||
option-value="id" |
|||
emit-value |
|||
map-options |
|||
dense |
|||
outlined |
|||
style="min-width: 200px" |
|||
@update:model-value="themeChanged" |
|||
> |
|||
<template #option="scope"> |
|||
<q-item v-bind="scope.itemProps"> |
|||
<q-item-section> |
|||
<q-item-label>{{ scope.opt.name }}</q-item-label> |
|||
</q-item-section> |
|||
<q-item-section v-if="scope.opt.active" avatar> |
|||
<q-icon name="bi-check" /> |
|||
</q-item-section> |
|||
<q-item-section v-else avatar> |
|||
<w-icon-empty></w-icon-empty> |
|||
</q-item-section> |
|||
</q-item> |
|||
</template> |
|||
</q-select> |
|||
<q-btn :label="$t('theme.action.setDefaultTheme')" icon="bi-heart" no-caps outline dense padding="2px 10px" @click="defaultTheme" /> |
|||
<q-btn :label="$t('add')" icon="add" no-caps outline dense padding="2px 10px" @click="prompt = true" /> |
|||
<q-btn :label="$t('delete')" icon="delete" no-caps outline dense padding="2px 10px" @click="removeTheme" /> |
|||
<q-btn :label="$t('save')" icon="save" no-caps outline dense padding="2px 10px" @click="saveTheme" /> |
|||
</div> |
|||
</div> |
|||
</template> |
|||
<script setup lang="ts"> |
|||
import { ref } from 'vue'; |
|||
import { useI18n } from 'vue-i18n'; |
|||
import { useQuasar } from 'quasar'; |
|||
import { Environment, axios, Tools, NotifyManager } from 'platform-core'; |
|||
import Brand from './theme/Brand.vue'; |
|||
import LoadingBar from './theme/LoadingBar.vue'; |
|||
import Topper from './theme/Topper.vue'; |
|||
import Sider from './theme/Sider.vue'; |
|||
import TagViewBar from './theme/TagViewBar.vue'; |
|||
import Main from './theme/Main.vue'; |
|||
import Footer from './theme/Footer.vue'; |
|||
import Login from './theme/Login.vue'; |
|||
import Scroller from './theme/Scroller.vue'; |
|||
import Notifier from './theme/Notifier.vue'; |
|||
import Grid from './theme/Grid.vue'; |
|||
|
|||
const { t } = useI18n(); |
|||
const quasar = useQuasar(); |
|||
|
|||
const selectedTab = ref('brand'); |
|||
const selectedTheme = ref(''); |
|||
const themeOptions = ref([]); |
|||
const prompt = ref(false); |
|||
const configureName = ref(''); |
|||
|
|||
let configureMap = new Map<string, object>(); |
|||
|
|||
const newTheme = () => { |
|||
axios.post(Environment.apiContextPath('/api/lcdp/configure'), { name: configureName.value }).then((response) => { |
|||
NotifyManager.info(t('success')); |
|||
refresh(() => { |
|||
selectedTheme.value = response.data.id; |
|||
configureName.value = ''; |
|||
}); |
|||
}); |
|||
}; |
|||
|
|||
const themeChanged = (value) => { |
|||
const option = configureMap.get(value); |
|||
if (option) { |
|||
Tools.mergeObject(Environment.getConfigure(), option); |
|||
} |
|||
}; |
|||
|
|||
const defaultTheme = () => { |
|||
quasar |
|||
.dialog({ |
|||
title: t('confirm'), |
|||
message: t('theme.action.setDefaultTheme.confirm', { themeName: configureMap.get(selectedTheme.value).name }), |
|||
cancel: true, |
|||
persistent: true, |
|||
}) |
|||
.onOk(() => { |
|||
axios.post(Environment.apiContextPath('/api/lcdp/configure/activeConfigure/') + selectedTheme.value).then((response) => { |
|||
NotifyManager.info(t('success')); |
|||
refresh(); |
|||
}); |
|||
}); |
|||
}; |
|||
|
|||
const removeTheme = () => { |
|||
quasar |
|||
.dialog({ |
|||
title: t('confirm'), |
|||
message: t('theme.action.delete.confirm', { themeName: configureMap.get(selectedTheme.value).name }), |
|||
cancel: true, |
|||
persistent: true, |
|||
}) |
|||
.onOk(() => { |
|||
axios.delete(Environment.apiContextPath('/api/lcdp/configure/') + selectedTheme.value).then((response) => { |
|||
NotifyManager.info(t('success')); |
|||
refresh(); |
|||
selectedTheme.value = ''; |
|||
}); |
|||
}); |
|||
}; |
|||
|
|||
const saveTheme = () => { |
|||
quasar |
|||
.dialog({ |
|||
title: t('confirm'), |
|||
message: t('theme.action.save.confirm', { themeName: configureMap.get(selectedTheme.value).name }), |
|||
cancel: true, |
|||
persistent: true, |
|||
}) |
|||
.onOk(() => { |
|||
const data = {}; |
|||
Tools.mergeObject(data, configureMap.get(selectedTheme.value)); |
|||
data.theme = Environment.getConfigure().theme; |
|||
|
|||
axios.put(Environment.apiContextPath('/api/lcdp/configure/') + selectedTheme.value, data).then((response) => { |
|||
NotifyManager.info(t('success')); |
|||
}); |
|||
}); |
|||
}; |
|||
|
|||
const refresh = (callback?: any) => { |
|||
axios.get(Environment.apiContextPath('/api/lcdp/configure')).then((response) => { |
|||
themeOptions.value.splice(0, themeOptions.value.length); |
|||
const items = response.data.content; |
|||
if (items && items.length > 0) { |
|||
const options = []; |
|||
for (const item of items) { |
|||
themeOptions.value.push(item); |
|||
configureMap.set(item.id, item); |
|||
if (item.active) { |
|||
selectedTheme.value = item.id; |
|||
} |
|||
} |
|||
//themeOptions.value = options; |
|||
if (callback) { |
|||
callback(); |
|||
} |
|||
} |
|||
}); |
|||
}; |
|||
|
|||
refresh(); |
|||
</script> |
@ -1,25 +1,160 @@ |
|||
<template> |
|||
<div class="w-full border rounded-md" style="height: 100%"> |
|||
<ToolBar></ToolBar> |
|||
<q-splitter :model-value="20" unit="%" separator-style="width:2px;" style="height: calc(100% - 50px)"> |
|||
<template #before> |
|||
<SideBar></SideBar> |
|||
</template> |
|||
<template #after> |
|||
<q-splitter :model-value="65" unit="%" separator-style="width:2px;" style="height: 100%"> |
|||
<template #before><Editor></Editor></template> |
|||
<template #after><Properties></Properties></template> |
|||
</q-splitter> |
|||
</template> |
|||
</q-splitter> |
|||
<div> |
|||
<div class="row justify-end q-gutter-md q-py-md"> |
|||
<q-select |
|||
v-model="selectedTheme" |
|||
:label="$t('theme')" |
|||
:options="themeOptions" |
|||
option-label="name" |
|||
option-value="id" |
|||
emit-value |
|||
map-options |
|||
dense |
|||
outlined |
|||
style="min-width: 200px" |
|||
@update:model-value="themeChanged" |
|||
> |
|||
<template #option="scope"> |
|||
<q-item v-bind="scope.itemProps"> |
|||
<q-item-section> |
|||
<q-item-label>{{ scope.opt.name }}</q-item-label> |
|||
</q-item-section> |
|||
<q-item-section v-if="scope.opt.active" avatar> |
|||
<q-icon name="bi-check" /> |
|||
</q-item-section> |
|||
<q-item-section v-else avatar> |
|||
<w-icon-empty></w-icon-empty> |
|||
</q-item-section> |
|||
</q-item> |
|||
</template> |
|||
</q-select> |
|||
<q-btn :label="$t('theme.action.setDefaultTheme')" icon="bi-heart" no-caps outline dense padding="2px 10px" @click="defaultTheme" /> |
|||
<q-btn :label="$t('add')" icon="add" no-caps outline dense padding="2px 10px" @click="prompt = true" /> |
|||
<q-btn :label="$t('delete')" icon="delete" no-caps outline dense padding="2px 10px" @click="removeTheme" /> |
|||
<q-btn :label="$t('save')" icon="save" no-caps outline dense padding="2px 10px" @click="saveTheme" /> |
|||
</div> |
|||
<q-dialog v-model="prompt" persistent> |
|||
<q-card style="min-width: 350px"> |
|||
<q-card-section class="q-pt-md"> |
|||
<q-input v-model="configureName" :label="$t('name')" outlined dense autofocus @keyup.enter="prompt = false" /> |
|||
</q-card-section> |
|||
|
|||
<q-card-actions align="right" class="text-primary"> |
|||
<q-btn v-close-popup flat :label="$t('cancel')" /> |
|||
<q-btn v-close-popup flat :label="$t('confirm')" @click="newTheme" /> |
|||
</q-card-actions> |
|||
</q-card> |
|||
</q-dialog> |
|||
</div> |
|||
</template> |
|||
|
|||
<script setup lang="ts"> |
|||
import { ref, onMounted } from 'vue'; |
|||
import { type CellStyle, Graph, InternalEvent } from '@maxgraph/core'; |
|||
import ToolBar from './ToolBar.vue'; |
|||
import SideBar from './SideBar.vue'; |
|||
import Editor from './Editor.vue'; |
|||
import Properties from './Properties.vue'; |
|||
import { ref } from 'vue'; |
|||
import { useI18n } from 'vue-i18n'; |
|||
import { useQuasar } from 'quasar'; |
|||
import { Environment, axios, Tools, NotifyManager } from '@/platform'; |
|||
|
|||
const { t } = useI18n(); |
|||
const quasar = useQuasar(); |
|||
|
|||
const selectedTab = ref('brand'); |
|||
const selectedTheme = ref(''); |
|||
const themeOptions = ref([]); |
|||
const prompt = ref(false); |
|||
const configureName = ref(''); |
|||
|
|||
let configureMap = new Map<string, object>(); |
|||
|
|||
const newTheme = () => { |
|||
axios.post(Environment.apiContextPath('/api/lcdp/configure'), { name: configureName.value }).then((response) => { |
|||
NotifyManager.info(t('success')); |
|||
refresh(() => { |
|||
selectedTheme.value = response.data.id; |
|||
configureName.value = ''; |
|||
}); |
|||
}); |
|||
}; |
|||
|
|||
const themeChanged = (value) => { |
|||
const option = configureMap.get(value); |
|||
if (option) { |
|||
Tools.mergeObject(Environment.getConfigure(), option); |
|||
} |
|||
}; |
|||
|
|||
const defaultTheme = () => { |
|||
quasar |
|||
.dialog({ |
|||
title: t('confirm'), |
|||
message: t('theme.action.setDefaultTheme.confirm', { themeName: configureMap.get(selectedTheme.value).name }), |
|||
cancel: true, |
|||
persistent: true, |
|||
}) |
|||
.onOk(() => { |
|||
axios.post(Environment.apiContextPath('/api/lcdp/configure/activeConfigure/') + selectedTheme.value).then((response) => { |
|||
NotifyManager.info(t('success')); |
|||
refresh(); |
|||
}); |
|||
}); |
|||
}; |
|||
|
|||
const removeTheme = () => { |
|||
quasar |
|||
.dialog({ |
|||
title: t('confirm'), |
|||
message: t('theme.action.delete.confirm', { themeName: configureMap.get(selectedTheme.value).name }), |
|||
cancel: true, |
|||
persistent: true, |
|||
}) |
|||
.onOk(() => { |
|||
axios.delete(Environment.apiContextPath('/api/lcdp/configure/') + selectedTheme.value).then((response) => { |
|||
NotifyManager.info(t('success')); |
|||
refresh(); |
|||
selectedTheme.value = ''; |
|||
}); |
|||
}); |
|||
}; |
|||
|
|||
const saveTheme = () => { |
|||
quasar |
|||
.dialog({ |
|||
title: t('confirm'), |
|||
message: t('theme.action.save.confirm', { themeName: configureMap.get(selectedTheme.value).name }), |
|||
cancel: true, |
|||
persistent: true, |
|||
}) |
|||
.onOk(() => { |
|||
const data = {}; |
|||
Tools.mergeObject(data, configureMap.get(selectedTheme.value)); |
|||
data.theme = Environment.getConfigure().theme; |
|||
|
|||
axios.put(Environment.apiContextPath('/api/lcdp/configure/') + selectedTheme.value, data).then((response) => { |
|||
NotifyManager.info(t('success')); |
|||
}); |
|||
}); |
|||
}; |
|||
|
|||
const refresh = (callback?: any) => { |
|||
axios.get(Environment.apiContextPath('/api/lcdp/configure')).then((response) => { |
|||
configureMap.clear(); |
|||
const options = []; |
|||
selectedTheme.value = ''; |
|||
|
|||
const items = response.data.content; |
|||
if (items && items.length > 0) { |
|||
for (const item of items) { |
|||
options.push(item); |
|||
configureMap.set(item.id, item); |
|||
if (item.active) { |
|||
selectedTheme.value = item.id; |
|||
} |
|||
} |
|||
} |
|||
themeOptions.value = options; |
|||
if (callback) { |
|||
callback(); |
|||
} |
|||
}); |
|||
}; |
|||
|
|||
refresh(); |
|||
</script> |
|||
|
@ -1,203 +0,0 @@ |
|||
<template> |
|||
<div> |
|||
<div class="row py-1 q-col-gutter-sm"> |
|||
<div class="col-2"> |
|||
<q-select |
|||
v-model="valueReactive.datasource" |
|||
:label="$t('developer.backend.export.liquibase.datasource')" |
|||
outlined |
|||
dense |
|||
emit-value |
|||
map-options |
|||
:options="datasourceOptionsRef" |
|||
@update:model-value=" |
|||
(value) => { |
|||
datasourceChanged(value); |
|||
} |
|||
" |
|||
></q-select> |
|||
</div> |
|||
<div class="col-2"> |
|||
<q-select |
|||
v-model="valueReactive.schema" |
|||
:label="$t('developer.backend.export.liquibase.schema')" |
|||
outlined |
|||
dense |
|||
emit-value |
|||
map-options |
|||
:options="schemaOptionsRef" |
|||
@update:model-value=" |
|||
(value) => { |
|||
schemaChanged(valueReactive.datasource, value); |
|||
} |
|||
" |
|||
></q-select> |
|||
</div> |
|||
<div class="col-8"> |
|||
<q-select |
|||
v-model="valueReactive.tables" |
|||
:label="$t('developer.backend.export.liquibase.tables')" |
|||
input-class="bg-red-200" |
|||
outlined |
|||
dense |
|||
emit-value |
|||
map-options |
|||
multiple |
|||
use-chips |
|||
:options="tablesOptionsRef" |
|||
@update:model-value=" |
|||
(value) => { |
|||
if (value) { |
|||
if (value.length > 1) { |
|||
valueReactive.sql = 'select * from ${table}'; |
|||
} else { |
|||
valueReactive.sql = 'select * from ' + value; |
|||
} |
|||
} |
|||
} |
|||
" |
|||
> |
|||
<template #append> |
|||
<q-btn |
|||
:title="$t('selectAll')" |
|||
icon="bi-check-square" |
|||
flat |
|||
dense |
|||
:disable="!(tablesOptionsRef?.length > 0)" |
|||
@click.stop.prevent=" |
|||
() => { |
|||
const selecteds = []; |
|||
if (tablesOptionsRef) { |
|||
for (const table of tablesOptionsRef) { |
|||
selecteds.push(table.value); |
|||
} |
|||
} |
|||
valueReactive.tables = selecteds; |
|||
} |
|||
" |
|||
/> |
|||
<q-btn |
|||
:title="$t('unSelectAll')" |
|||
icon="bi-square" |
|||
flat |
|||
dense |
|||
:disable="!(tablesOptionsRef?.length > 0)" |
|||
@click.stop.prevent=" |
|||
() => { |
|||
valueReactive.tables = []; |
|||
} |
|||
" |
|||
/> |
|||
</template> |
|||
</q-select> |
|||
</div> |
|||
</div> |
|||
<div class="row py-1 q-col-gutter-sm"> |
|||
<div class="col-12"> |
|||
<w-code-mirror v-model="valueReactive.sql" :label="$t('sql')" :rows="5" lang="sql"></w-code-mirror> |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="row justify-center q-gutter-md py-2"> |
|||
<WProgressBtn |
|||
ref="progressBtnRef" |
|||
icon="bi-database-down" |
|||
:label="$t('export')" |
|||
data-url="/api/jdbc/data/traceExporterExecuteProgress" |
|||
@click="exportData" |
|||
@success=" |
|||
(progressInfo) => { |
|||
Downloader.get(Environment.apiContextPath('/api/mvc/download?filePath=' + encodeURIComponent(progressInfo.result))); |
|||
} |
|||
" |
|||
></WProgressBtn> |
|||
</div> |
|||
</div> |
|||
</template> |
|||
<script setup lang="ts"> |
|||
import { ref, reactive, onMounted, onUpdated } from 'vue'; |
|||
import { useI18n } from 'vue-i18n'; |
|||
import { axios, blobAxios, Environment, DialogManager, Downloader } from '@/platform'; |
|||
import WProgressBtn from '../platform/components/progress/WProgressBtn.vue'; |
|||
|
|||
const { t } = useI18n(); |
|||
const progressBtnRef = ref(); |
|||
const datasourceOptionsRef = ref([]); |
|||
const schemaOptionsRef = ref([]); |
|||
const tablesOptionsRef = ref([]); |
|||
|
|||
const valueReactive = reactive({ |
|||
datasource: undefined, |
|||
schema: undefined, |
|||
tables: undefined, |
|||
sql: undefined, |
|||
}); |
|||
|
|||
const loadDatasource = () => { |
|||
axios.get(Environment.apiContextPath('/api/system/datasource?pageable=false&sortBy=name')).then((response) => { |
|||
const data = response?.data.content; |
|||
if (data && data.length > 0) { |
|||
datasourceOptionsRef.value.splice(0, datasourceOptionsRef.value.length); |
|||
for (let item of data) { |
|||
datasourceOptionsRef.value.push({ label: item.name, value: item.name }); |
|||
} |
|||
} |
|||
}); |
|||
}; |
|||
|
|||
const datasourceChanged = (datasource: string) => { |
|||
datasource = datasource || ''; |
|||
axios.get(Environment.apiContextPath('/api/jdbc/metadata/getSchemas?datasource=' + datasource)).then((response) => { |
|||
const data = response?.data; |
|||
if (data && data.length > 0) { |
|||
schemaOptionsRef.value.splice(0, schemaOptionsRef.value.length); |
|||
tablesOptionsRef.value.splice(0, tablesOptionsRef.value.length); |
|||
for (let item of data) { |
|||
schemaOptionsRef.value.push({ label: item.name, value: item.name }); |
|||
} |
|||
} |
|||
}); |
|||
}; |
|||
|
|||
const schemaChanged = (datasource: string, schema: string) => { |
|||
datasource = datasource || ''; |
|||
schema = schema || ''; |
|||
axios.get(Environment.apiContextPath('/api/jdbc/metadata/getTables?datasource=' + datasource + '&schema=' + schema)).then((response) => { |
|||
const data = response?.data; |
|||
if (data && data.length > 0) { |
|||
tablesOptionsRef.value.splice(0, tablesOptionsRef.value.length); |
|||
for (let item of data) { |
|||
tablesOptionsRef.value.push({ label: item.name, value: item.name }); |
|||
} |
|||
} |
|||
}); |
|||
}; |
|||
|
|||
const exportData = (e) => { |
|||
DialogManager.confirm(t('developer.backend.export.liquibase.export.tip'), () => { |
|||
const data = valueReactive; |
|||
const config = { |
|||
datasource: data.datasource, |
|||
schema: data.schema, |
|||
tables: [], |
|||
}; |
|||
const length = data.tables.length; |
|||
const sql = length === 1 ? data.sql : ''; |
|||
for (let i = 0; i < length; i++) { |
|||
config.tables[i] = { name: data.tables[i], sql: sql ? sql : 'select * from ' + data.tables[i] }; |
|||
} |
|||
axios.post(Environment.apiContextPath('/api/jdbc/data/exportData'), config).then((response) => { |
|||
progressBtnRef.value.start(); |
|||
}); |
|||
}); |
|||
}; |
|||
|
|||
onMounted(() => { |
|||
loadDatasource(); |
|||
datasourceChanged(''); |
|||
}); |
|||
|
|||
onUpdated(() => { |
|||
loadDatasource(); |
|||
}); |
|||
</script> |
@ -0,0 +1,103 @@ |
|||
{ |
|||
"name": "io.sc.platform.lcdp.frontend", |
|||
"version": "8.1.38", |
|||
"description": "", |
|||
"private": false, |
|||
"keywords": [], |
|||
"author": "", |
|||
"license": "ISC", |
|||
"scripts": { |
|||
"clean": "rm -rf ./node_modules && rm -rf pnpm-lock.yaml", |
|||
"dev": "nodemon", |
|||
"serve": "node ./util-components-generator.cjs && cross-env NODE_ENV=development webpack serve --config webpack.env.serve.cjs", |
|||
"build": "node ./util-components-generator.cjs && cross-env NODE_ENV=development webpack --config webpack.env.build.cjs", |
|||
"prod": "node ./util-components-generator.cjs && cross-env NODE_ENV=production webpack --config webpack.env.prod.cjs", |
|||
"sync": "platform sync" |
|||
}, |
|||
"engines": { |
|||
"node": ">=18", |
|||
"pnpm": ">=7" |
|||
}, |
|||
"publishConfig": { |
|||
"registry": "http://nexus.sc.io:8000/repository/npm-releases/", |
|||
"access": "public" |
|||
}, |
|||
"devDependencies": { |
|||
"@babel/core": "7.23.7", |
|||
"@babel/preset-env": "7.23.7", |
|||
"@babel/preset-typescript": "7.23.3", |
|||
"@babel/plugin-transform-class-properties": "7.23.3", |
|||
"@babel/plugin-transform-object-rest-spread": "7.23.4", |
|||
"@quasar/app-webpack": "3.12.1", |
|||
"@quasar/cli": "2.3.0", |
|||
"@types/mockjs": "1.0.10", |
|||
"@types/node": "20.10.6", |
|||
"@typescript-eslint/eslint-plugin": "6.17.0", |
|||
"@typescript-eslint/parser": "6.17.0", |
|||
"@vue/compiler-sfc": "3.4.3", |
|||
"@webpack-cli/serve": "2.0.5", |
|||
"autoprefixer": "10.4.16", |
|||
"babel-loader": "9.1.3", |
|||
"clean-webpack-plugin": "4.0.0", |
|||
"copy-webpack-plugin": "11.0.0", |
|||
"cross-env": "7.0.3", |
|||
"css-loader": "6.8.1", |
|||
"eslint": "8.56.0", |
|||
"eslint-config-prettier": "9.1.0", |
|||
"eslint-plugin-prettier": "5.1.2", |
|||
"eslint-plugin-vue": "9.19.2", |
|||
"eslint-webpack-plugin": "4.0.1", |
|||
"html-webpack-plugin": "5.6.0", |
|||
"json5": "2.2.3", |
|||
"mini-css-extract-plugin": "2.7.6", |
|||
"nodemon": "3.0.2", |
|||
"postcss": "8.4.32", |
|||
"postcss-import": "16.0.0", |
|||
"postcss-loader": "7.3.4", |
|||
"postcss-preset-env": "9.3.0", |
|||
"prettier": "3.1.1", |
|||
"sass": "1.69.7", |
|||
"sass-loader": "13.3.3", |
|||
"typescript": "5.3.3", |
|||
"vue-loader": "17.4.2", |
|||
"webpack": "5.89.0", |
|||
"webpack-bundle-analyzer": "4.10.1", |
|||
"webpack-cli": "5.1.4", |
|||
"webpack-dev-server": "4.15.1", |
|||
"webpack-merge": "5.10.0", |
|||
"@vue/babel-plugin-jsx": "1.1.5" |
|||
}, |
|||
"dependencies": { |
|||
"@codemirror/autocomplete": "6.11.1", |
|||
"@codemirror/commands": "6.3.3", |
|||
"@codemirror/lang-html": "6.4.7", |
|||
"@codemirror/lang-java": "6.0.1", |
|||
"@codemirror/lang-javascript": "6.2.1", |
|||
"@codemirror/lang-json": "6.0.1", |
|||
"@codemirror/lang-sql": "6.5.4", |
|||
"@codemirror/lang-xml": "6.0.2", |
|||
"@codemirror/language": "6.10.0", |
|||
"@codemirror/search": "6.5.5", |
|||
"@codemirror/state": "6.4.0", |
|||
"@codemirror/view": "6.23.0", |
|||
"@maxgraph/core": "0.9.0", |
|||
"@quasar/extras": "1.16.9", |
|||
"@vueuse/core": "10.7.1", |
|||
"axios": "1.6.3", |
|||
"codemirror": "6.0.1", |
|||
"dayjs": "1.11.10", |
|||
"echarts": "5.4.3", |
|||
"exceljs": "4.4.0", |
|||
"file-saver": "2.0.5", |
|||
"luckyexcel": "1.0.1", |
|||
"mockjs": "1.1.0", |
|||
"pinia": "2.1.7", |
|||
"platform-core": "8.1.188", |
|||
"quasar": "2.14.5", |
|||
"tailwindcss": "3.4.0", |
|||
"vue": "3.4.3", |
|||
"vue-dompurify-html": "5.0.1", |
|||
"vue-i18n": "9.8.0", |
|||
"vue-router": "4.2.5" |
|||
} |
|||
} |
Loading…
Reference in new issue