初始代码
This commit is contained in:
54
src/views/onlineDev/dataReport/Form.vue
Normal file
54
src/views/onlineDev/dataReport/Form.vue
Normal file
@@ -0,0 +1,54 @@
|
||||
<template>
|
||||
<BasicModal
|
||||
v-bind="$attrs"
|
||||
@register="registerModal"
|
||||
defaultFullscreen
|
||||
:footer="null"
|
||||
:closable="false"
|
||||
:keyboard="false"
|
||||
class="yunzhupaas-full-modal full-modal report-modal designer-modal">
|
||||
<iframe :src="state.url" width="100%" height="100%" frameborder="0" class="frame" />
|
||||
</BasicModal>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { reactive, onMounted } from 'vue';
|
||||
import { BasicModal, useModalInner } from '@/components/Modal';
|
||||
import { useGlobSetting } from '@/hooks/setting';
|
||||
import { getToken } from '@/utils/auth';
|
||||
|
||||
interface State {
|
||||
url: string;
|
||||
}
|
||||
|
||||
const emit = defineEmits(['register', 'reload']);
|
||||
const { report } = useGlobSetting();
|
||||
const [registerModal, { closeModal }] = useModalInner(init);
|
||||
const state = reactive<State>({
|
||||
url: '',
|
||||
});
|
||||
|
||||
function init(data) {
|
||||
state.url = `${report}/index.html?token=${getToken()}${data.id ? '&id=' + data.id : ''}`;
|
||||
}
|
||||
function handleMessage(e) {
|
||||
const data = e.data;
|
||||
if (data !== 'closeDialog') return;
|
||||
state.url = '';
|
||||
closeModal();
|
||||
emit('reload');
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
window.addEventListener('message', handleMessage);
|
||||
});
|
||||
</script>
|
||||
<style lang="less">
|
||||
.report-modal {
|
||||
.ant-modal-header {
|
||||
display: none !important;
|
||||
}
|
||||
.scrollbar {
|
||||
padding: 0 !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
70
src/views/onlineDev/dataReport/PreviewPopup.vue
Normal file
70
src/views/onlineDev/dataReport/PreviewPopup.vue
Normal file
@@ -0,0 +1,70 @@
|
||||
<template>
|
||||
<BasicPopup v-bind="$attrs" @register="registerPopup" class="full-popup report-popup">
|
||||
<iframe :src="state.url" width="100%" height="100%" frameborder="0" />
|
||||
</BasicPopup>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { reactive, onMounted } from 'vue';
|
||||
import { BasicPopup, usePopupInner } from '@/components/Popup';
|
||||
import { useGlobSetting } from '@/hooks/setting';
|
||||
import { getToken } from '@/utils/auth';
|
||||
import { getDataReportInfo } from '@/api/onlineDev/dataReport';
|
||||
interface State {
|
||||
url: string;
|
||||
}
|
||||
|
||||
defineEmits(['register']);
|
||||
const { report } = useGlobSetting();
|
||||
const [registerPopup, { closePopup }] = usePopupInner(init);
|
||||
const state = reactive<State>({
|
||||
url: '',
|
||||
});
|
||||
|
||||
function init(data) {
|
||||
let targetUrl = `${report}/preview.html?id=${data.id}&token=${getToken()}&page=1`;
|
||||
getDataReportInfo(data.id).then(res => {
|
||||
let item = {};
|
||||
if (res.data?.searchForm?.components && Array.isArray(res.data.searchForm.components)) {
|
||||
listQuery(res.data.searchForm.components, item);
|
||||
for (let key in item) {
|
||||
let item1 = '&' + key + '=' + item[key];
|
||||
targetUrl += item1;
|
||||
}
|
||||
}
|
||||
state.url = targetUrl;
|
||||
});
|
||||
}
|
||||
function listQuery(list, callback) {
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
let item = list[i];
|
||||
let arrayList = [];
|
||||
if (item.hasOwnProperty('cols') && Array.isArray(item.cols)) {
|
||||
arrayList = arrayList.concat(item.cols);
|
||||
}
|
||||
if (item.hasOwnProperty('children') && Array.isArray(item.children)) {
|
||||
arrayList = arrayList.concat(item.children);
|
||||
}
|
||||
if (item.bindParameter && item.defaultValue) {
|
||||
callback[item.bindParameter] = item.defaultValue;
|
||||
}
|
||||
listQuery(arrayList, callback);
|
||||
}
|
||||
}
|
||||
function handleMessage(e) {
|
||||
const data = e.data;
|
||||
if (data !== 'closeDialog') return;
|
||||
state.url = '';
|
||||
closePopup();
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
window.addEventListener('message', handleMessage);
|
||||
});
|
||||
</script>
|
||||
<style lang="less">
|
||||
.report-popup {
|
||||
.yunzhupaas-basic-popup-header {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
217
src/views/onlineDev/dataReport/index.vue
Normal file
217
src/views/onlineDev/dataReport/index.vue
Normal file
@@ -0,0 +1,217 @@
|
||||
<template>
|
||||
<div class="yunzhupaas-content-wrapper">
|
||||
<div class="yunzhupaas-content-wrapper-center">
|
||||
<div class="yunzhupaas-content-wrapper-content">
|
||||
<BasicTable @register="registerTable">
|
||||
<template #tableTitle>
|
||||
<a-button type="primary" preIcon="icon-ym icon-ym-btn-add" @click="addOrUpdateHandle()">{{ t('common.addText') }}</a-button>
|
||||
<yunzhupaas-upload-btn :url="reportServer + '/Data/Actions/Import'" accept=".json" @on-success="reload"></yunzhupaas-upload-btn>
|
||||
</template>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'enabledMark'">
|
||||
<a-tag :color="record.enabledMark === 1 ? 'success' : 'error'">{{ record.enabledMark == 1 ? '启用' : '禁用' }}</a-tag>
|
||||
</template>
|
||||
<template v-if="column.key === 'action'">
|
||||
<TableAction :actions="getTableActions(record)" :dropDownActions="getDropDownActions(record)" />
|
||||
</template>
|
||||
</template>
|
||||
</BasicTable>
|
||||
</div>
|
||||
</div>
|
||||
<PreviewModal @register="registerPreview" type="flow" @previewPc="previewPc" />
|
||||
<Form @register="registerForm" @reload="reload" />
|
||||
<PreviewPopup @register="registerPreviewPopup" />
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { getDataReportList, delDataReport, copy, release } from '@/api/onlineDev/dataReport';
|
||||
import { BasicTable, useTable, TableAction, BasicColumn, ActionItem } from '@/components/Table';
|
||||
import { useMessage } from '@/hooks/web/useMessage';
|
||||
import { useI18n } from '@/hooks/web/useI18n';
|
||||
import { useModal } from '@/components/Modal';
|
||||
import { usePopup } from '@/components/Popup';
|
||||
import { useBaseStore } from '@/store/modules/base';
|
||||
import { downloadByUrl } from '@/utils/file/download';
|
||||
import { useGlobSetting } from '@/hooks/setting';
|
||||
import { getToken } from '@/utils/auth';
|
||||
import { PreviewModal } from '@/components/CommonModal';
|
||||
import Form from './Form.vue';
|
||||
import PreviewPopup from './PreviewPopup.vue';
|
||||
|
||||
defineOptions({ name: 'onlineDev-dataReport' });
|
||||
|
||||
const { createMessage } = useMessage();
|
||||
const baseStore = useBaseStore();
|
||||
const { t } = useI18n();
|
||||
const [registerPreview, { openModal: openPreviewModal }] = useModal();
|
||||
const [registerForm, { openModal: openFormModal }] = useModal();
|
||||
const [registerPreviewPopup, { openPopup: openPreviewPopup }] = usePopup();
|
||||
|
||||
const columns: BasicColumn[] = [
|
||||
{ title: '名称', dataIndex: 'fullName', width: 200 },
|
||||
{ title: '编码', dataIndex: 'enCode', width: 200 },
|
||||
{ title: '分类', dataIndex: 'category', width: 150 },
|
||||
{ title: '创建人', dataIndex: 'creatorUser', width: 120 },
|
||||
{ title: '创建时间', dataIndex: 'creatorTime', width: 150, format: 'date|YYYY-MM-DD HH:mm:ss' },
|
||||
{ title: '最后修改时间', dataIndex: 'lastModifyTime', width: 150, format: 'date|YYYY-MM-DD HH:mm:ss' },
|
||||
{ title: '排序', dataIndex: 'sortCode', width: 70, align: 'center' },
|
||||
{ title: '状态', dataIndex: 'enabledMark', width: 70, align: 'center' },
|
||||
];
|
||||
const { reportServer } = useGlobSetting();
|
||||
const currRow = ref<any>({});
|
||||
const categoryList = ref<any[]>([]);
|
||||
const [registerTable, { reload, getForm }] = useTable({
|
||||
api: getDataReportList,
|
||||
columns,
|
||||
useSearchForm: true,
|
||||
immediate: false,
|
||||
formConfig: {
|
||||
schemas: [
|
||||
{
|
||||
field: 'keyword',
|
||||
label: t('common.keyword'),
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: t('common.enterKeyword'),
|
||||
submitOnPressEnter: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'category',
|
||||
label: '分类',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
placeholder: '请选择',
|
||||
showSearch: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'enabledMark',
|
||||
label: '状态',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
placeholder: '请选择',
|
||||
options: [
|
||||
{ fullName: '启用', id: 1 },
|
||||
{ fullName: '禁用', id: 0 },
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
actionColumn: {
|
||||
width: 150,
|
||||
title: '操作',
|
||||
dataIndex: 'action',
|
||||
},
|
||||
afterFetch: data => {
|
||||
const list = data.map(o => {
|
||||
let category = '';
|
||||
const arr = categoryList.value.filter(category => category.id == o.categoryId);
|
||||
if (arr.length) {
|
||||
const item = arr[0];
|
||||
category = item && item.fullName ? item.fullName : '';
|
||||
}
|
||||
return { ...o, category };
|
||||
});
|
||||
return list;
|
||||
},
|
||||
});
|
||||
function getTableActions(record): ActionItem[] {
|
||||
return [
|
||||
{
|
||||
label: t('common.editText'),
|
||||
onClick: addOrUpdateHandle.bind(null, record.id),
|
||||
},
|
||||
{
|
||||
label: t('common.delText'),
|
||||
color: 'error',
|
||||
modelConfirm: {
|
||||
onOk: handleDelete.bind(null, record.id),
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
function getDropDownActions(record): ActionItem[] {
|
||||
return [
|
||||
{
|
||||
label: t('common.previewText'),
|
||||
onClick: handlePreview.bind(null, record),
|
||||
},
|
||||
{
|
||||
label: t('common.copyText'),
|
||||
modelConfirm: {
|
||||
content: '您确定要复制该报表, 是否继续?',
|
||||
onOk: handleCopy.bind(null, record.id),
|
||||
},
|
||||
},
|
||||
{
|
||||
label: t('common.exportText'),
|
||||
modelConfirm: {
|
||||
content: '您确定要导出该报表, 是否继续?',
|
||||
onOk: handleExport.bind(null, record.id),
|
||||
},
|
||||
},
|
||||
{
|
||||
ifShow: !record.enabledMark,
|
||||
label: '启用',
|
||||
modelConfirm: {
|
||||
content: '此操作将启用该报表,是否继续?',
|
||||
onOk: handleRelease.bind(null, record),
|
||||
},
|
||||
},
|
||||
{
|
||||
ifShow: !!record.enabledMark,
|
||||
label: '禁用',
|
||||
modelConfirm: {
|
||||
content: '此操作将禁用该报表,是否继续?',
|
||||
onOk: handleRelease.bind(null, record),
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
function addOrUpdateHandle(id = '') {
|
||||
openFormModal(true, { id });
|
||||
}
|
||||
function handlePreview(record) {
|
||||
currRow.value = record;
|
||||
openPreviewModal(true, { type: 'report', id: record.id, fullName: record.fullName });
|
||||
}
|
||||
function previewPc() {
|
||||
openPreviewPopup(true, { id: currRow.value.id });
|
||||
}
|
||||
function handleDelete(id) {
|
||||
delDataReport(id).then(res => {
|
||||
createMessage.success(res.msg);
|
||||
reload();
|
||||
});
|
||||
}
|
||||
function handleCopy(id) {
|
||||
copy(id).then(res => {
|
||||
createMessage.success(res.msg);
|
||||
reload();
|
||||
});
|
||||
}
|
||||
function handleExport(id) {
|
||||
const token = getToken();
|
||||
const url = `${reportServer}/Data/${id}/Actions/Export?token=${token}`;
|
||||
downloadByUrl({ url });
|
||||
}
|
||||
async function getOptions() {
|
||||
const res = await baseStore.getDictionaryData('businessType');
|
||||
categoryList.value = res as any[];
|
||||
getForm().updateSchema({ field: 'category', componentProps: { options: res } });
|
||||
reload();
|
||||
}
|
||||
function handleRelease(record) {
|
||||
release(record.id).then(res => {
|
||||
createMessage.success(res.msg);
|
||||
reload();
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getOptions();
|
||||
});
|
||||
</script>
|
||||
120
src/views/onlineDev/integrate/Form.vue
Normal file
120
src/views/onlineDev/integrate/Form.vue
Normal file
@@ -0,0 +1,120 @@
|
||||
<template>
|
||||
<BasicModal v-bind="$attrs" @register="registerModal" :title="dataForm.id ? t('common.editText') : t('common.addText')" @ok="handleSubmit">
|
||||
<BasicForm @register="registerForm" />
|
||||
<template #appendFooter>
|
||||
<a-button type="primary" :loading="btnLoading" @click="handleSubmit(1)">确定并设计</a-button>
|
||||
</template>
|
||||
</BasicModal>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { toRefs, reactive } from 'vue';
|
||||
import { BasicModal, useModalInner } from '@/components/Modal';
|
||||
import { BasicForm, useForm } from '@/components/Form';
|
||||
import formValidate from '@/utils/formValidate';
|
||||
import { getInfo, update, create } from '@/api/onlineDev/integrate';
|
||||
import { useMessage } from '@/hooks/web/useMessage';
|
||||
import { useI18n } from '@/hooks/web/useI18n';
|
||||
|
||||
interface State {
|
||||
dataForm: any;
|
||||
btnLoading: boolean;
|
||||
}
|
||||
|
||||
const state = reactive<State>({
|
||||
dataForm: { id: '' },
|
||||
btnLoading: false,
|
||||
});
|
||||
const { dataForm, btnLoading } = toRefs(state);
|
||||
const { createMessage } = useMessage();
|
||||
const { t } = useI18n();
|
||||
const emit = defineEmits(['register', 'reload', 'design']);
|
||||
const [registerModal, { closeModal, changeLoading, changeOkLoading }] = useModalInner(init);
|
||||
const [registerForm, { setFieldsValue, resetFields, validate }] = useForm({
|
||||
labelWidth: 60,
|
||||
schemas: [
|
||||
{
|
||||
field: 'fullName',
|
||||
label: '名称',
|
||||
component: 'Input',
|
||||
componentProps: { placeholder: '请输入', maxlength: 100 },
|
||||
rules: [{ required: true, trigger: 'blur', message: '必填' }],
|
||||
},
|
||||
{
|
||||
field: 'enCode',
|
||||
label: '编码',
|
||||
component: 'Input',
|
||||
componentProps: { placeholder: '请输入', maxlength: 50 },
|
||||
rules: [
|
||||
{ required: true, trigger: 'blur', message: '必填' },
|
||||
{ validator: formValidate('enCode'), trigger: 'blur' },
|
||||
],
|
||||
},
|
||||
{
|
||||
field: 'type',
|
||||
label: '类型',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
options: [
|
||||
{ id: 1, fullName: '事件触发' },
|
||||
{ id: 2, fullName: '定时触发' },
|
||||
{ id: 3, fullName: 'webhook触发' },
|
||||
],
|
||||
disabled: true,
|
||||
},
|
||||
rules: [{ required: true, trigger: 'change', message: '必填', type: 'number' }],
|
||||
},
|
||||
{
|
||||
field: 'enabledMark',
|
||||
label: '状态',
|
||||
component: 'Switch',
|
||||
defaultValue: 1,
|
||||
},
|
||||
{
|
||||
field: 'description',
|
||||
label: '说明 ',
|
||||
component: 'Textarea',
|
||||
componentProps: { placeholder: '请输入' },
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
function init(data) {
|
||||
resetFields();
|
||||
state.dataForm.id = data.id || '';
|
||||
state.dataForm.type = data.type || 1;
|
||||
if (state.dataForm.id) {
|
||||
changeLoading(true);
|
||||
getInfo(state.dataForm.id)
|
||||
.then(res => {
|
||||
state.dataForm = res.data;
|
||||
setFieldsValue(state.dataForm);
|
||||
changeLoading(false);
|
||||
})
|
||||
.catch(() => {
|
||||
changeLoading(false);
|
||||
});
|
||||
} else {
|
||||
setFieldsValue({ type: state.dataForm.type });
|
||||
}
|
||||
}
|
||||
async function handleSubmit(type?) {
|
||||
const values = await validate();
|
||||
if (!values) return;
|
||||
type === 1 ? (state.btnLoading = true) : changeOkLoading(true);
|
||||
const query = { ...values, id: state.dataForm.id };
|
||||
const formMethod = state.dataForm.id ? update : create;
|
||||
formMethod(query)
|
||||
.then(res => {
|
||||
createMessage.success(res.msg);
|
||||
changeOkLoading(false);
|
||||
state.btnLoading = false;
|
||||
closeModal();
|
||||
emit('reload');
|
||||
if (type === 1) emit('design', state.dataForm.id || res.data);
|
||||
})
|
||||
.catch(() => {
|
||||
changeOkLoading(false);
|
||||
state.btnLoading = false;
|
||||
});
|
||||
}
|
||||
</script>
|
||||
29
src/views/onlineDev/integrate/components/ExecutionQueue.vue
Normal file
29
src/views/onlineDev/integrate/components/ExecutionQueue.vue
Normal file
@@ -0,0 +1,29 @@
|
||||
<template>
|
||||
<BasicDrawer v-bind="$attrs" @register="registerDrawer" title="执行队列" width="600px" class="full-drawer" destroy-on-close>
|
||||
<BasicTable @register="registerTable">
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'enabledMark'">
|
||||
<a-tag :color="record.state === 1 ? 'success' : ''">{{ record.state === 1 ? '执行中' : '等待' }}</a-tag>
|
||||
</template>
|
||||
</template>
|
||||
</BasicTable>
|
||||
</BasicDrawer>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { getQueueList } from '@/api/onlineDev/integrate';
|
||||
import { BasicDrawer, useDrawerInner } from '@/components/Drawer';
|
||||
import { BasicTable, useTable, BasicColumn } from '@/components/Table';
|
||||
|
||||
const [registerDrawer, {}] = useDrawerInner();
|
||||
const columns: BasicColumn[] = [
|
||||
{ title: '名称', dataIndex: 'fullName' },
|
||||
{ title: '执行时间', dataIndex: 'executionTime', width: 150, format: 'date|YYYY-MM-DD HH:mm:ss' },
|
||||
{ title: '状态', dataIndex: 'enabledMark', width: 70 },
|
||||
];
|
||||
const [registerTable] = useTable({
|
||||
api: getQueueList,
|
||||
columns,
|
||||
pagination: false,
|
||||
showTableSetting: false,
|
||||
});
|
||||
</script>
|
||||
157
src/views/onlineDev/integrate/components/Log.vue
Normal file
157
src/views/onlineDev/integrate/components/Log.vue
Normal file
@@ -0,0 +1,157 @@
|
||||
<template>
|
||||
<BasicPopup v-bind="$attrs" @register="registerPopup" :title="getTitle" class="full-popup">
|
||||
<BasicTable :columns="columns" @register="registerTable" class="yunzhupaas-sub-table yunzhupaas-sub-table-full" v-show="!showFlow">
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'processId'">
|
||||
{{ record.processId }}
|
||||
<template v-if="record.isRetry">
|
||||
<a-tag color="blue" class="!mx-8px">重试</a-tag>
|
||||
<BasicHelp :text="[`原实例ID:${record.parentId}`, `原实例执行时间:${dayjs(record.parentTime).format('YYYY-MM-DD HH:mm:ss')}`]">
|
||||
<template #default><i class="icon-ym icon-ym-generator-link" /></template>
|
||||
</BasicHelp>
|
||||
</template>
|
||||
</template>
|
||||
<template v-if="column.key === 'enabledMark'">
|
||||
<a-tag :color="record.resultType === 1 ? 'success' : 'error'">{{ record.resultType === 1 ? '成功' : '失败' }}</a-tag>
|
||||
</template>
|
||||
<template v-if="column.key === 'action'">
|
||||
<TableAction :actions="getTableActions(record)" />
|
||||
</template>
|
||||
</template>
|
||||
</BasicTable>
|
||||
<LogDetail @register="registerLogDetailModal" @updateNodes="updateNodes" />
|
||||
</BasicPopup>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { getTaskList, retryTask, delTask } from '@/api/onlineDev/integrate';
|
||||
import { ref, unref, computed, reactive } from 'vue';
|
||||
import { BasicPopup, usePopupInner } from '@/components/Popup';
|
||||
import { BasicTable, useTable, TableAction, BasicColumn, ActionItem } from '@/components/Table';
|
||||
import { useI18n } from '@/hooks/web/useI18n';
|
||||
import { useMessage } from '@/hooks/web/useMessage';
|
||||
import { useModal } from '@/components/Modal';
|
||||
import LogDetail from './LogDetail.vue';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
const { createMessage } = useMessage();
|
||||
const { t } = useI18n();
|
||||
const [registerPopup, { closePopup }] = usePopupInner(init);
|
||||
const emit = defineEmits(['register', 'updateNodes']);
|
||||
const title = ref('');
|
||||
const showFlow = ref(false);
|
||||
const columns: BasicColumn[] = [
|
||||
{ title: '实例ID', dataIndex: 'processId' },
|
||||
{ title: '执行结果', dataIndex: 'enabledMark', width: 150 },
|
||||
{ title: '执行时间', dataIndex: 'executionTime', width: 150, format: 'date|YYYY-MM-DD HH:mm:ss' },
|
||||
];
|
||||
const searchInfo = reactive({ integrateId: '' });
|
||||
const [registerLogDetailModal, { openModal: openLogDetailModal }] = useModal();
|
||||
const [registerTable, { reload }] = useTable({
|
||||
api: getTaskList,
|
||||
searchInfo: searchInfo,
|
||||
useSearchForm: true,
|
||||
formConfig: {
|
||||
baseColProps: { span: 6 },
|
||||
schemas: [
|
||||
{
|
||||
field: 'pickerVal',
|
||||
label: '执行时间',
|
||||
component: 'DateRange',
|
||||
componentProps: {
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
showTime: { defaultValue: [dayjs('00:00:00', 'HH:mm:ss'), dayjs('23:59:59', 'HH:mm:ss')] },
|
||||
placeholder: ['开始时间', '结束时间'],
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'resultType',
|
||||
label: '执行结果',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
placeholder: '请选择',
|
||||
options: [
|
||||
{ fullName: '成功', id: 1 },
|
||||
{ fullName: '失败', id: 0 },
|
||||
],
|
||||
showSearch: true,
|
||||
allowClear: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
fieldMapToTime: [['pickerVal', ['startTime', 'endTime']]],
|
||||
},
|
||||
immediate: false,
|
||||
tableSetting: { setting: false, redo: false },
|
||||
actionColumn: {
|
||||
width: 150,
|
||||
title: '操作',
|
||||
dataIndex: 'action',
|
||||
},
|
||||
});
|
||||
|
||||
const getTitle = computed(() => (unref(showFlow) ? '流程图' : unref(title)));
|
||||
|
||||
function init(data) {
|
||||
title.value = data.fullName;
|
||||
searchInfo.integrateId = data.id;
|
||||
reload();
|
||||
}
|
||||
function getTableActions(record): ActionItem[] {
|
||||
return [
|
||||
{
|
||||
label: t('common.detailText'),
|
||||
onClick: handleDetail.bind(null, record.id),
|
||||
},
|
||||
{
|
||||
label: t('common.delText'),
|
||||
color: 'error',
|
||||
modelConfirm: {
|
||||
onOk: handleDelete.bind(null, record.id),
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '重试',
|
||||
ifShow: record.resultType != 1,
|
||||
modelConfirm: {
|
||||
content: '确定将本实例进行重试,是否继续?',
|
||||
onOk: handleRedo.bind(null, record.id),
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
function handleDetail(id) {
|
||||
openLogDetailModal(true, { id });
|
||||
}
|
||||
function handleRedo(id) {
|
||||
retryTask(id).then(res => {
|
||||
createMessage.success(res.msg);
|
||||
reload();
|
||||
});
|
||||
}
|
||||
function handleDelete(id) {
|
||||
delTask(id).then(res => {
|
||||
createMessage.success(res.msg);
|
||||
reload();
|
||||
});
|
||||
}
|
||||
function updateNodes() {
|
||||
emit('updateNodes', searchInfo.integrateId);
|
||||
closePopup();
|
||||
}
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.process-preview {
|
||||
height: 100%;
|
||||
padding: 10px 0;
|
||||
|
||||
:deep(.process-flow-container) {
|
||||
.tips {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scale-slider {
|
||||
right: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
199
src/views/onlineDev/integrate/components/LogDetail.vue
Normal file
199
src/views/onlineDev/integrate/components/LogDetail.vue
Normal file
@@ -0,0 +1,199 @@
|
||||
<template>
|
||||
<BasicModal v-bind="$attrs" @register="registerModal" title="日志详情" width="800px" :footer="null" class="yunzhupaas-log-detail-modal">
|
||||
<div class="log-detail-board">
|
||||
<div class="left-board">
|
||||
<div class="item-box" :class="{ active: activeItem.id == item.id }" v-for="(item, index) in list" @click="handleClick(item, index)">
|
||||
<div class="top">
|
||||
<span class="fullName">{{ item.nodeName }}</span>
|
||||
<i class="icon icon-ym icon-ym-success" v-if="item.resultType" />
|
||||
<i class="icon icon-ym icon-ym-fail" v-else />
|
||||
</div>
|
||||
<div class="bottom">
|
||||
<i class="icon-ym icon-ym-clock pr-4px" v-if="item.type == 1" />
|
||||
<i class="icon-ym icon-ym-btn-refresh pr-4px" v-else />
|
||||
{{ dayjs(item.endTime).format('YYYY-MM-DD HH:mm:ss') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="center-board">
|
||||
<div class="top">
|
||||
<span>{{ activeItem.nodeName }}</span>
|
||||
<a-space v-if="!activeItem.resultType">
|
||||
<a-button @click="handleUpdateNodes">去修改节点</a-button>
|
||||
<a-button @click="handleRedoNodes" :disabled="!activeItem.isRetry" :loading="redoNodesLoading">
|
||||
{{ redoNodesLoading ? '正在修复中...' : '节点重试' }}
|
||||
</a-button>
|
||||
</a-space>
|
||||
</div>
|
||||
<div class="time-box">
|
||||
<span>开始时间:{{ dayjs(activeItem.startTime).format('YYYY-MM-DD HH:mm:ss') }}</span>
|
||||
<span>结束时间:{{ dayjs(activeItem.endTime).format('YYYY-MM-DD HH:mm:ss') }}</span>
|
||||
</div>
|
||||
<a-radio-group class="!mb-10px" v-model:value="activeKey" button-style="solid">
|
||||
<a-radio-button :value="1">输入</a-radio-button>
|
||||
<a-radio-button :value="2" v-if="!activeItem.resultType">错误</a-radio-button>
|
||||
</a-radio-group>
|
||||
<a-textarea v-model:value="getTextareaValue" :rows="17" readOnly />
|
||||
</div>
|
||||
</div>
|
||||
</BasicModal>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { getTaskInfo, nodeRetryTask } from '@/api/onlineDev/integrate';
|
||||
import { BasicModal, useModalInner } from '@/components/Modal';
|
||||
import { reactive, toRefs, computed } from 'vue';
|
||||
import { useMessage } from '@/hooks/web/useMessage';
|
||||
import { useI18n } from '@/hooks/web/useI18n';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
interface State {
|
||||
list: any[];
|
||||
activeKey: number;
|
||||
activeIndex: number;
|
||||
activeItem: any;
|
||||
id: string;
|
||||
msgInfo: string;
|
||||
redoNodesLoading: boolean;
|
||||
}
|
||||
|
||||
const emit = defineEmits(['register', 'updateNodes']);
|
||||
const { t } = useI18n();
|
||||
const { createMessage, createConfirm } = useMessage();
|
||||
const [registerModal, { closeModal, changeLoading }] = useModalInner(init);
|
||||
const state = reactive<State>({
|
||||
list: [],
|
||||
activeKey: 1,
|
||||
activeIndex: 0,
|
||||
activeItem: {},
|
||||
id: '',
|
||||
msgInfo: '',
|
||||
redoNodesLoading: false,
|
||||
});
|
||||
const { list, activeKey, activeItem, redoNodesLoading } = toRefs(state);
|
||||
|
||||
const getTextareaValue = computed(() => (state.activeKey === 1 ? state.msgInfo : state.activeItem.errorMsg));
|
||||
|
||||
function init(data) {
|
||||
state.activeKey = 1;
|
||||
state.list = [];
|
||||
state.activeItem = {};
|
||||
state.id = data.id;
|
||||
initData(true);
|
||||
}
|
||||
function initData(isInit?) {
|
||||
changeLoading(true);
|
||||
getTaskInfo(state.id).then(res => {
|
||||
changeLoading(false);
|
||||
state.list = res.data.list || [];
|
||||
state.msgInfo = res.data && res.data.data;
|
||||
if (state.list.length) state.activeItem = state.list[isInit ? 0 : state.activeIndex];
|
||||
});
|
||||
}
|
||||
function handleClick(item, index) {
|
||||
state.activeItem = item;
|
||||
state.activeIndex = index;
|
||||
state.activeKey = 1;
|
||||
}
|
||||
function handleUpdateNodes() {
|
||||
emit('updateNodes');
|
||||
closeModal();
|
||||
}
|
||||
function handleRedoNodes() {
|
||||
createConfirm({
|
||||
iconType: 'warning',
|
||||
title: t('common.tipTitle'),
|
||||
content: '确定将本节点进行重试?',
|
||||
onOk: () => {
|
||||
const query = {
|
||||
id: state.id,
|
||||
nodeId: state.activeItem.id,
|
||||
};
|
||||
state.redoNodesLoading = true;
|
||||
nodeRetryTask(query)
|
||||
.then(res => {
|
||||
state.redoNodesLoading = false;
|
||||
createMessage.success(res.msg);
|
||||
initData();
|
||||
})
|
||||
.catch(() => {
|
||||
state.redoNodesLoading = false;
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
</script>
|
||||
<style lang="less">
|
||||
.yunzhupaas-log-detail-modal {
|
||||
.log-detail-board {
|
||||
display: flex;
|
||||
height: 550px;
|
||||
.left-board {
|
||||
width: 270px;
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
border-right: 1px solid @border-color-base;
|
||||
.active {
|
||||
background-color: @tree-node-selected-bg !important;
|
||||
}
|
||||
.item-box {
|
||||
padding: 8px 20px;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background-color: @selected-hover-bg;
|
||||
}
|
||||
.top {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.fullName {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
font-size: 16px;
|
||||
}
|
||||
.icon {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border-radius: 50%;
|
||||
padding: 2px;
|
||||
text-align: center;
|
||||
color: #fff;
|
||||
transform: scale(0.65);
|
||||
}
|
||||
.icon-ym-fail {
|
||||
background-color: #ff4d4d;
|
||||
}
|
||||
.icon-ym-success {
|
||||
background-color: #55d187;
|
||||
}
|
||||
}
|
||||
.bottom {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.icon-ym-btn-refresh {
|
||||
color: @primary-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.center-board {
|
||||
width: 100%;
|
||||
padding: 15px 20px;
|
||||
.top {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.time-box {
|
||||
padding: 20px 0;
|
||||
display: flex;
|
||||
span {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
213
src/views/onlineDev/integrate/index.vue
Normal file
213
src/views/onlineDev/integrate/index.vue
Normal file
@@ -0,0 +1,213 @@
|
||||
<template>
|
||||
<div class="yunzhupaas-content-wrapper">
|
||||
<div class="yunzhupaas-content-wrapper-center">
|
||||
<div class="yunzhupaas-content-wrapper-content">
|
||||
<BasicTable @register="registerTable">
|
||||
<template #toolbar>
|
||||
<a-tooltip placement="top">
|
||||
<template #title>
|
||||
<span>执行队列</span>
|
||||
</template>
|
||||
<i class="icon-ym icon-ym-generator-slider cursor-pointer text-18px" @click="openDrawer(true)"></i>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<template #headerTop>
|
||||
<a-alert message="请使用【任务流程】功能来搭建业务编排,集成助手版块停止更新,后续将下架!" showIcon type="warning" class="mt-10px" />
|
||||
</template>
|
||||
<template #tableTitle>
|
||||
<a-dropdown>
|
||||
<template #overlay>
|
||||
<a-menu @click="handleAdd">
|
||||
<a-menu-item :key="item.id" v-for="item in typeList">{{ item.fullName }}</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
<a-button type="primary" preIcon="icon-ym icon-ym-btn-add">{{ t('common.addText') }}<DownOutlined /></a-button>
|
||||
</a-dropdown>
|
||||
<yunzhupaas-upload-btn url="/api/visualdev/Integrate/Actions/Import" accept=".bi" @on-success="reload"></yunzhupaas-upload-btn>
|
||||
</template>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'type'">
|
||||
{{ record.type === 1 ? '事件触发' : record.type === 2 ? '定时触发' : 'webhook触发' }}
|
||||
</template>
|
||||
<template v-if="column.key === 'enabledMark'">
|
||||
<a-tag :color="record.enabledMark === 1 ? 'success' : 'error'">{{ record.enabledMark == 1 ? '启用' : '禁用' }}</a-tag>
|
||||
</template>
|
||||
<template v-if="column.key === 'action'">
|
||||
<TableAction :actions="getTableActions(record)" :dropDownActions="getDropDownActions(record)" />
|
||||
</template>
|
||||
</template>
|
||||
</BasicTable>
|
||||
</div>
|
||||
</div>
|
||||
<Form @register="registerForm" @reload="reload" @design="handleDesign" />
|
||||
<IntegrateProcess @register="registerIntegrateProcess" @reload="reload" />
|
||||
<ExecutionQueue @register="registerDrawer" />
|
||||
<Log @register="registerLogPopup" @updateNodes="updateNodes" />
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { getIntegrateList, delIntegrate, copy, exportData, updateState } from '@/api/onlineDev/integrate';
|
||||
import { BasicTable, useTable, TableAction, BasicColumn, ActionItem } from '@/components/Table';
|
||||
import { useMessage } from '@/hooks/web/useMessage';
|
||||
import { useI18n } from '@/hooks/web/useI18n';
|
||||
import { useModal } from '@/components/Modal';
|
||||
import { usePopup } from '@/components/Popup/src/usePopup';
|
||||
import { IntegrateProcess } from '@/components/IntegrateProcess';
|
||||
import { downloadByUrl } from '@/utils/file/download';
|
||||
import { DownOutlined } from '@ant-design/icons-vue';
|
||||
import { useDrawer } from '@/components/Drawer';
|
||||
import Form from './Form.vue';
|
||||
import ExecutionQueue from './components/ExecutionQueue.vue';
|
||||
import Log from './components/Log.vue';
|
||||
|
||||
defineOptions({ name: 'onlineDev-integrate' });
|
||||
|
||||
const { createMessage } = useMessage();
|
||||
const { t } = useI18n();
|
||||
const [registerForm, { openModal: openFormModal }] = useModal();
|
||||
const [registerIntegrateProcess, { openModal: openIntegrateProcess }] = useModal();
|
||||
const [registerDrawer, { openDrawer }] = useDrawer();
|
||||
const [registerLogPopup, { openPopup: openLogPopup }] = usePopup();
|
||||
|
||||
const columns: BasicColumn[] = [
|
||||
{ title: '名称', dataIndex: 'fullName', width: 200 },
|
||||
{ title: '编码', dataIndex: 'enCode', width: 180 },
|
||||
{ title: '类型', dataIndex: 'type', width: 110, align: 'center' },
|
||||
{ title: '创建人', dataIndex: 'creatorUser', width: 120 },
|
||||
{ title: '创建时间', dataIndex: 'creatorTime', width: 150, format: 'date|YYYY-MM-DD HH:mm:ss' },
|
||||
{ title: '最后修改时间', dataIndex: 'lastModifyTime', width: 150, format: 'date|YYYY-MM-DD HH:mm:ss' },
|
||||
{ title: '状态', dataIndex: 'enabledMark', width: 80, align: 'center' },
|
||||
];
|
||||
const typeList = [
|
||||
{ fullName: '事件触发', id: 1 },
|
||||
{ fullName: '定时触发', id: 2 },
|
||||
{ fullName: 'webhook触发', id: 3 },
|
||||
];
|
||||
const [registerTable, { reload }] = useTable({
|
||||
api: getIntegrateList,
|
||||
columns,
|
||||
useSearchForm: true,
|
||||
formConfig: {
|
||||
schemas: [
|
||||
{
|
||||
field: 'keyword',
|
||||
label: t('common.keyword'),
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: t('common.enterKeyword'),
|
||||
submitOnPressEnter: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'type',
|
||||
label: '类型',
|
||||
component: 'Select',
|
||||
componentProps: { placeholder: '请选择', options: typeList },
|
||||
},
|
||||
{
|
||||
field: 'enabledMark',
|
||||
label: '状态',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
placeholder: '请选择',
|
||||
options: [
|
||||
{ fullName: '启用', id: 1 },
|
||||
{ fullName: '禁用', id: 0 },
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
actionColumn: {
|
||||
width: 180,
|
||||
title: '操作',
|
||||
dataIndex: 'action',
|
||||
},
|
||||
});
|
||||
function getTableActions(record): ActionItem[] {
|
||||
return [
|
||||
{
|
||||
label: t('common.editText'),
|
||||
onClick: addOrUpdateHandle.bind(null, record.id),
|
||||
},
|
||||
{
|
||||
label: record.enabledMark == 1 ? '禁用' : '启用',
|
||||
color: 'error',
|
||||
modelConfirm: {
|
||||
content: `此操作将${record.enabledMark == 1 ? '禁用' : '启用'}该集成助手,是否继续?`,
|
||||
onOk: handleRelease.bind(null, record.id),
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '设计',
|
||||
onClick: handleDesign.bind(null, record.id),
|
||||
},
|
||||
];
|
||||
}
|
||||
function getDropDownActions(record): ActionItem[] {
|
||||
return [
|
||||
{
|
||||
label: '日志',
|
||||
onClick: handleLog.bind(null, record.id, record.fullName),
|
||||
},
|
||||
{
|
||||
label: t('common.copyText'),
|
||||
modelConfirm: {
|
||||
content: '您确定要复制该集成助手, 是否继续?',
|
||||
onOk: handleCopy.bind(null, record.id),
|
||||
},
|
||||
},
|
||||
{
|
||||
label: t('common.exportText'),
|
||||
modelConfirm: {
|
||||
content: '您确定要导出该集成助手, 是否继续?',
|
||||
onOk: handleExport.bind(null, record.id),
|
||||
},
|
||||
},
|
||||
{
|
||||
label: t('common.delText'),
|
||||
modelConfirm: {
|
||||
onOk: handleDelete.bind(null, record.id),
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
function handleAdd({ key }) {
|
||||
addOrUpdateHandle('', key);
|
||||
}
|
||||
function addOrUpdateHandle(id = '', type) {
|
||||
openFormModal(true, { id, type });
|
||||
}
|
||||
function handleDelete(id) {
|
||||
delIntegrate(id).then(res => {
|
||||
createMessage.success(res.msg);
|
||||
reload();
|
||||
});
|
||||
}
|
||||
function handleDesign(id) {
|
||||
openIntegrateProcess(true, { id });
|
||||
}
|
||||
function handleCopy(id) {
|
||||
copy(id).then(res => {
|
||||
createMessage.success(res.msg);
|
||||
reload();
|
||||
});
|
||||
}
|
||||
function handleExport(id) {
|
||||
exportData(id).then(res => {
|
||||
downloadByUrl({ url: res.data.url });
|
||||
});
|
||||
}
|
||||
function handleLog(id, fullName) {
|
||||
openLogPopup(true, { id, fullName });
|
||||
}
|
||||
function handleRelease(id) {
|
||||
updateState(id).then(res => {
|
||||
createMessage.success(res.msg);
|
||||
reload();
|
||||
});
|
||||
}
|
||||
function updateNodes(id) {
|
||||
handleDesign(id);
|
||||
}
|
||||
</script>
|
||||
157
src/views/onlineDev/printDev/Form.vue
Normal file
157
src/views/onlineDev/printDev/Form.vue
Normal file
@@ -0,0 +1,157 @@
|
||||
<template>
|
||||
<BasicModal v-bind="$attrs" @register="registerModal" :title="dataForm.id ? t('common.editText') : t('common.addText')" @ok="handleSubmit()">
|
||||
<BasicForm @register="registerForm">
|
||||
<template #icon="{ model, field }">
|
||||
<div class="flex">
|
||||
<div class="flex-1 mr-10px">
|
||||
<yunzhupaas-icon-picker v-model:value="model[field]" placeholder="请选择" />
|
||||
</div>
|
||||
<a-form-item-rest>
|
||||
<yunzhupaas-color-picker v-model:value="state.iconBackground" size="small" :predefine="predefineList" name="iconBackground" />
|
||||
</a-form-item-rest>
|
||||
</div>
|
||||
</template>
|
||||
</BasicForm>
|
||||
<template #appendFooter>
|
||||
<a-button type="primary" :loading="btnLoading" @click="handleSubmit(1)">确定并设计</a-button>
|
||||
</template>
|
||||
</BasicModal>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { toRefs, reactive } from 'vue';
|
||||
import { BasicModal, useModalInner } from '@/components/Modal';
|
||||
import { BasicForm, useForm } from '@/components/Form';
|
||||
import formValidate from '@/utils/formValidate';
|
||||
import { getPrintDevInfo, updatePrintDev, createPrintDev } from '@/api/system/printDev';
|
||||
import { useMessage } from '@/hooks/web/useMessage';
|
||||
import { useI18n } from '@/hooks/web/useI18n';
|
||||
|
||||
interface State {
|
||||
dataForm: any;
|
||||
btnLoading: boolean;
|
||||
iconBackground: string;
|
||||
}
|
||||
|
||||
const predefineList = ['#008cff', '#35b8e0', '#00cc88', '#ff9d00', '#ff4d4d', '#5b69bc', '#ff8acc', '#3b3e47', '#282828'];
|
||||
const state = reactive<State>({
|
||||
dataForm: {},
|
||||
btnLoading: false,
|
||||
iconBackground: '',
|
||||
});
|
||||
const { dataForm, btnLoading } = toRefs(state);
|
||||
const { createMessage } = useMessage();
|
||||
const { t } = useI18n();
|
||||
const emit = defineEmits(['register', 'reload', 'design']);
|
||||
const [registerModal, { closeModal, changeLoading, changeOkLoading }] = useModalInner(init);
|
||||
const [registerForm, { setFieldsValue, resetFields, validate, updateSchema }] = useForm({
|
||||
schemas: [
|
||||
{
|
||||
field: 'fullName',
|
||||
label: '名称',
|
||||
component: 'Input',
|
||||
componentProps: { placeholder: '请输入', maxlength: 100 },
|
||||
rules: [{ required: true, trigger: 'blur', message: '必填' }],
|
||||
},
|
||||
{
|
||||
field: 'enCode',
|
||||
label: '编码',
|
||||
component: 'Input',
|
||||
componentProps: { placeholder: '请输入', maxlength: 50 },
|
||||
rules: [
|
||||
{ required: true, trigger: 'blur', message: '必填' },
|
||||
{ validator: formValidate('enCode'), trigger: 'blur' },
|
||||
],
|
||||
},
|
||||
{
|
||||
field: 'category',
|
||||
label: '分类',
|
||||
component: 'Select',
|
||||
componentProps: { placeholder: '请选择', showSearch: true },
|
||||
rules: [{ required: true, trigger: 'change', message: '必填' }],
|
||||
},
|
||||
{
|
||||
field: 'sortCode',
|
||||
label: '排序',
|
||||
defaultValue: 0,
|
||||
component: 'InputNumber',
|
||||
componentProps: { min: 0, max: 999999 },
|
||||
},
|
||||
{
|
||||
field: 'commonUse',
|
||||
label: '通用',
|
||||
helpMessage: '启用后,将该打印设计设为通用打印模板',
|
||||
defaultValue: 0,
|
||||
component: 'Switch',
|
||||
},
|
||||
{
|
||||
ifShow: ({ values }) => values.commonUse === 1,
|
||||
field: 'visibleType',
|
||||
label: '发布范围',
|
||||
defaultValue: 1,
|
||||
component: 'Radio',
|
||||
componentProps: {
|
||||
options: [
|
||||
{ fullName: '公开', id: 1 },
|
||||
{ fullName: '权限设置', id: 2 },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
ifShow: ({ values }) => values.commonUse === 1,
|
||||
field: 'icon',
|
||||
label: '图标',
|
||||
slot: 'icon',
|
||||
component: 'IconPicker',
|
||||
rules: [{ required: true, trigger: 'change', message: '必填' }],
|
||||
},
|
||||
{
|
||||
field: 'description',
|
||||
label: '说明 ',
|
||||
component: 'Textarea',
|
||||
componentProps: { placeholder: '请输入' },
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
function init(data) {
|
||||
resetFields();
|
||||
state.iconBackground = '#008cff';
|
||||
state.dataForm.id = data.id || '';
|
||||
updateSchema([{ field: 'category', componentProps: { options: data.categoryList || [] } }]);
|
||||
if (state.dataForm.id) {
|
||||
changeLoading(true);
|
||||
getPrintDevInfo(state.dataForm.id)
|
||||
.then(res => {
|
||||
state.dataForm = res.data;
|
||||
state.iconBackground = state.dataForm.iconBackground || '#008cff';
|
||||
state.dataForm.visibleType = state.dataForm.visibleType || 1;
|
||||
setFieldsValue(state.dataForm);
|
||||
changeLoading(false);
|
||||
})
|
||||
.catch(() => {
|
||||
changeLoading(false);
|
||||
});
|
||||
}
|
||||
}
|
||||
async function handleSubmit(type = 0) {
|
||||
const values = await validate();
|
||||
if (!values) return;
|
||||
type === 1 ? (state.btnLoading = true) : changeOkLoading(true);
|
||||
const query = { ...values, iconBackground: state.iconBackground, id: state.dataForm.id };
|
||||
const formMethod = state.dataForm.id ? updatePrintDev : createPrintDev;
|
||||
formMethod(query)
|
||||
.then(res => {
|
||||
createMessage.success(res.msg);
|
||||
changeOkLoading(false);
|
||||
state.btnLoading = false;
|
||||
closeModal();
|
||||
emit('reload');
|
||||
if (!state.dataForm.id) state.dataForm.id = res.data;
|
||||
if (type == 1) emit('design', { ...values, id: state.dataForm.id });
|
||||
})
|
||||
.catch(() => {
|
||||
changeOkLoading(false);
|
||||
state.btnLoading = false;
|
||||
});
|
||||
}
|
||||
</script>
|
||||
181
src/views/onlineDev/printDev/index.vue
Normal file
181
src/views/onlineDev/printDev/index.vue
Normal file
@@ -0,0 +1,181 @@
|
||||
<template>
|
||||
<div class="yunzhupaas-content-wrapper">
|
||||
<div class="yunzhupaas-content-wrapper-center">
|
||||
<div class="yunzhupaas-content-wrapper-content">
|
||||
<BasicTable @register="registerTable">
|
||||
<template #tableTitle>
|
||||
<a-button type="primary" preIcon="icon-ym icon-ym-btn-add" @click="addOrUpdateHandle()">{{ t('common.addText') }}</a-button>
|
||||
<yunzhupaas-upload-btn url="/api/system/printDev/Actions/Import" accept=".bp" @on-success="reload"></yunzhupaas-upload-btn>
|
||||
</template>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'state'">
|
||||
<a-tag :color="record.state == 1 ? 'success' : ''">{{ record.state == 1 ? '已发布' : '未发布' }}</a-tag>
|
||||
</template>
|
||||
<template v-if="column.key === 'action'">
|
||||
<TableAction :actions="getTableActions(record)" :dropDownActions="getDropDownActions(record)" />
|
||||
</template>
|
||||
</template>
|
||||
</BasicTable>
|
||||
</div>
|
||||
</div>
|
||||
<Form @register="registerForm" @reload="reload" @design="handleDesign" />
|
||||
<PrintDesign @register="registerPrintDesign" @reload="reload" />
|
||||
<Preview @register="registerPreview" />
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { BasicTable, useTable, TableAction, BasicColumn, ActionItem } from '@/components/Table';
|
||||
import { getPrintDevList, delPrintDev, copy, exportData } from '@/api/system/printDev';
|
||||
import { useModal } from '@/components/Modal';
|
||||
import { useI18n } from '@/hooks/web/useI18n';
|
||||
import { useMessage } from '@/hooks/web/useMessage';
|
||||
import { downloadByUrl } from '@/utils/file/download';
|
||||
import { useBaseStore } from '@/store/modules/base';
|
||||
import PrintDesign from '@/components/PrintDesign/index.vue';
|
||||
import Preview from '@/components/PrintDesign/Preview.vue';
|
||||
import Form from './Form.vue';
|
||||
|
||||
defineOptions({ name: 'onlineDev-printDev' });
|
||||
|
||||
const { t } = useI18n();
|
||||
const baseStore = useBaseStore();
|
||||
const { createMessage } = useMessage();
|
||||
const categoryList = ref<any[]>([]);
|
||||
const columns: BasicColumn[] = [
|
||||
{ title: '名称', dataIndex: 'fullName' },
|
||||
{ title: '编码', dataIndex: 'enCode', width: 200 },
|
||||
{ title: '分类', dataIndex: 'category', width: 150 },
|
||||
{ title: '创建人', dataIndex: 'creatorUser', width: 120 },
|
||||
{ title: '创建时间', dataIndex: 'creatorTime', width: 150, format: 'date|YYYY-MM-DD HH:mm:ss' },
|
||||
{ title: '最后修改时间', dataIndex: 'lastModifyTime', width: 150, format: 'date|YYYY-MM-DD HH:mm:ss' },
|
||||
{ title: '排序', dataIndex: 'sortCode', width: 70, align: 'center' },
|
||||
{ title: '状态', dataIndex: 'state', width: 80, align: 'center' },
|
||||
];
|
||||
const [registerTable, { reload, getForm }] = useTable({
|
||||
api: getPrintDevList,
|
||||
columns,
|
||||
useSearchForm: true,
|
||||
formConfig: {
|
||||
schemas: [
|
||||
{
|
||||
field: 'keyword',
|
||||
label: t('common.keyword'),
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: t('common.enterKeyword'),
|
||||
submitOnPressEnter: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'category',
|
||||
label: '分类',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
placeholder: '请选择',
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'state',
|
||||
label: '状态',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
placeholder: '请选择',
|
||||
options: [
|
||||
{ fullName: '未发布', id: 0 },
|
||||
{ fullName: '已发布', id: 1 },
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
actionColumn: {
|
||||
width: 150,
|
||||
title: '操作',
|
||||
dataIndex: 'action',
|
||||
},
|
||||
});
|
||||
const [registerForm, { openModal: openFormModal }] = useModal();
|
||||
const [registerPrintDesign, { openModal: openPrintDesign }] = useModal();
|
||||
const [registerPreview, { openModal: openPreviewModal }] = useModal();
|
||||
|
||||
function getTableActions(record): ActionItem[] {
|
||||
return [
|
||||
{
|
||||
label: t('common.editText'),
|
||||
onClick: addOrUpdateHandle.bind(null, record.id),
|
||||
},
|
||||
{
|
||||
label: '设计',
|
||||
color: 'error',
|
||||
onClick: handleDesign.bind(null, record),
|
||||
},
|
||||
];
|
||||
}
|
||||
function getDropDownActions(record): ActionItem[] {
|
||||
return [
|
||||
{
|
||||
label: t('common.previewText'),
|
||||
ifShow: !!record.state,
|
||||
onClick: handlePreview.bind(null, record.id, record.fullName),
|
||||
},
|
||||
{
|
||||
label: t('common.copyText'),
|
||||
ifShow: !!record.state,
|
||||
modelConfirm: {
|
||||
content: '您确定要复制该打印模板, 是否继续?',
|
||||
onOk: handleCopy.bind(null, record.id),
|
||||
},
|
||||
},
|
||||
{
|
||||
label: t('common.exportText'),
|
||||
ifShow: !!record.state,
|
||||
modelConfirm: {
|
||||
content: '您确定要导出该打印模板, 是否继续?',
|
||||
onOk: handleExport.bind(null, record.id),
|
||||
},
|
||||
},
|
||||
{
|
||||
label: t('common.delText'),
|
||||
color: 'error',
|
||||
modelConfirm: {
|
||||
onOk: handleDelete.bind(null, record.id),
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
function addOrUpdateHandle(id = '') {
|
||||
openFormModal(true, { id, categoryList: categoryList.value });
|
||||
}
|
||||
function handleExport(id) {
|
||||
exportData(id).then(res => {
|
||||
downloadByUrl({ url: res.data.url });
|
||||
});
|
||||
}
|
||||
function handleCopy(id) {
|
||||
copy(id).then(res => {
|
||||
createMessage.success(res.msg);
|
||||
reload();
|
||||
});
|
||||
}
|
||||
function handleDelete(id) {
|
||||
delPrintDev(id).then(res => {
|
||||
createMessage.success(res.msg);
|
||||
reload();
|
||||
});
|
||||
}
|
||||
function handleDesign(record) {
|
||||
openPrintDesign(true, record);
|
||||
}
|
||||
function handlePreview(id, fullName) {
|
||||
openPreviewModal(true, { id, fullName });
|
||||
}
|
||||
async function getOptions() {
|
||||
categoryList.value = (await baseStore.getDictionaryData('businessType')) as any[];
|
||||
getForm().updateSchema({ field: 'category', componentProps: { options: categoryList.value } });
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getOptions();
|
||||
});
|
||||
</script>
|
||||
127
src/views/onlineDev/report/CreateMenuModal.vue
Normal file
127
src/views/onlineDev/report/CreateMenuModal.vue
Normal file
@@ -0,0 +1,127 @@
|
||||
<template>
|
||||
<BasicModal v-bind="$attrs" @register="registerModal" title="生成菜单" @ok="handleSubmit" class="yunzhupaas-release-modal">
|
||||
<a-alert message="将该报表发布至应用菜单" type="warning" show-icon />
|
||||
<a-form class="release-main" :colon="false" :model="dataForm" :rules="rules" layout="vertical" ref="formElRef">
|
||||
<div class="release-item report-item-left">
|
||||
<a-form-item>
|
||||
<div class="top-item" :class="{ active: dataForm.pc === 1 }" @click="selectToggle('pc')">
|
||||
<i class="item-icon icon-ym icon-ym-pc"></i>
|
||||
<p class="item-title">桌面端</p>
|
||||
<div class="icon-checked">
|
||||
<check-outlined />
|
||||
</div>
|
||||
</div>
|
||||
</a-form-item>
|
||||
<a-form-item label="上级" name="pcModuleParentId" v-if="dataForm.pc">
|
||||
<YunzhupaasTreeSelect
|
||||
v-model:value="pcModuleParentId"
|
||||
:options="treeData"
|
||||
treeCheckStrictly
|
||||
multiple
|
||||
:dropdownMatchSelectWidth="false"
|
||||
@change="onPcChange" />
|
||||
</a-form-item>
|
||||
<a-form-item label="已发布菜单路径" v-if="record.pcIsRelease">
|
||||
<div class="released">{{ record.pcReleaseName }}</div>
|
||||
</a-form-item>
|
||||
</div>
|
||||
</a-form>
|
||||
</BasicModal>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { getReleaseMenu, createMenu } from '@/api/onlineDev/report';
|
||||
import { BasicModal, useModalInner } from '@/components/Modal';
|
||||
import { ref, reactive, toRefs, computed } from 'vue';
|
||||
import type { FormInstance } from 'ant-design-vue';
|
||||
import { CheckOutlined } from '@ant-design/icons-vue';
|
||||
import { getMenuSelectorFilter } from '@/api/system/menu';
|
||||
import { useMessage } from '@/hooks/web/useMessage';
|
||||
import { useI18n } from '@/hooks/web/useI18n';
|
||||
|
||||
interface State {
|
||||
dataForm: any;
|
||||
record: any;
|
||||
treeData: any[];
|
||||
pcModuleParentId: any[];
|
||||
}
|
||||
|
||||
const emit = defineEmits(['register', 'reload']);
|
||||
const { createMessage, createConfirm } = useMessage();
|
||||
const { t } = useI18n();
|
||||
const [registerModal, { changeOkLoading, closeModal }] = useModalInner(init);
|
||||
const formElRef = ref<FormInstance>();
|
||||
const state = reactive<State>({
|
||||
dataForm: {
|
||||
pc: 1,
|
||||
pcModuleParentId: [],
|
||||
},
|
||||
record: {},
|
||||
treeData: [],
|
||||
pcModuleParentId: [],
|
||||
});
|
||||
const { dataForm, record, treeData, pcModuleParentId } = toRefs(state);
|
||||
|
||||
const rules = computed(() => {
|
||||
let rules: any = {
|
||||
pcModuleParentId: [],
|
||||
};
|
||||
if (!state.record.pcIsRelease) rules.pcModuleParentId = [{ required: true, message: '必填', trigger: 'change', type: 'array' }];
|
||||
return rules;
|
||||
});
|
||||
|
||||
function init(data) {
|
||||
state.pcModuleParentId = [];
|
||||
getReleaseMenu(data.id).then(res => {
|
||||
state.record = res.data;
|
||||
const platformRelease = res.data.platformRelease ? JSON.parse(res.data.platformRelease) : {};
|
||||
state.dataForm = {
|
||||
pc: platformRelease.pc === 0 ? 0 : 1,
|
||||
pcModuleParentId: [],
|
||||
};
|
||||
formElRef.value?.clearValidate();
|
||||
});
|
||||
getMenuOptions(data.id);
|
||||
}
|
||||
function getMenuOptions(id) {
|
||||
getMenuSelectorFilter({ category: 'Web' }, id).then(res => {
|
||||
state.treeData = res.data.list;
|
||||
});
|
||||
}
|
||||
|
||||
function onPcChange(data) {
|
||||
state.dataForm.pcModuleParentId = data.map(o => o.value);
|
||||
}
|
||||
function selectToggle(key) {
|
||||
state.dataForm[key] = state.dataForm[key] === 1 ? 0 : 1;
|
||||
}
|
||||
async function handleSubmit() {
|
||||
try {
|
||||
if (!state.dataForm.pc) return createMessage.error('请选择发布类型');
|
||||
const values = await formElRef.value?.validate();
|
||||
if (!values) return;
|
||||
const platform = { pc: state.dataForm.pc };
|
||||
const query = { ...state.dataForm, platformRelease: JSON.stringify(platform) };
|
||||
const handleRelease = () => {
|
||||
changeOkLoading(true);
|
||||
createMenu(state.record.id, query)
|
||||
.then(res => {
|
||||
changeOkLoading(false);
|
||||
createMessage.success(res.msg);
|
||||
emit('reload');
|
||||
closeModal();
|
||||
})
|
||||
.catch(() => {
|
||||
changeOkLoading(false);
|
||||
});
|
||||
};
|
||||
if (!state.record.isRelease) return handleRelease();
|
||||
createConfirm({
|
||||
iconType: 'warning',
|
||||
title: t('common.tipTitle'),
|
||||
content: '发布确定后会覆盖当前线上版本且进行菜单同步,是否继续?',
|
||||
onOk: handleRelease,
|
||||
});
|
||||
} catch (_) {}
|
||||
}
|
||||
</script>
|
||||
|
||||
112
src/views/onlineDev/report/Form.vue
Normal file
112
src/views/onlineDev/report/Form.vue
Normal file
@@ -0,0 +1,112 @@
|
||||
<template>
|
||||
<BasicModal v-bind="$attrs" @register="registerModal" :title="dataForm.id ? t('common.editText') : t('common.addText')" @ok="handleSubmit()">
|
||||
<BasicForm @register="registerForm" />
|
||||
<template #appendFooter>
|
||||
<a-button type="primary" :loading="btnLoading" @click="handleSubmit(1)">确定并设计</a-button>
|
||||
</template>
|
||||
</BasicModal>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { toRefs, reactive } from 'vue';
|
||||
import { BasicModal, useModalInner } from '@/components/Modal';
|
||||
import { BasicForm, useForm } from '@/components/Form';
|
||||
import formValidate from '@/utils/formValidate';
|
||||
import { getReportInfo, updateReport, createReport } from '@/api/onlineDev/report';
|
||||
import { useMessage } from '@/hooks/web/useMessage';
|
||||
import { useI18n } from '@/hooks/web/useI18n';
|
||||
|
||||
interface State {
|
||||
dataForm: any;
|
||||
btnLoading: boolean;
|
||||
}
|
||||
|
||||
const state = reactive<State>({
|
||||
dataForm: {},
|
||||
btnLoading: false,
|
||||
});
|
||||
const { dataForm, btnLoading } = toRefs(state);
|
||||
const { createMessage } = useMessage();
|
||||
const { t } = useI18n();
|
||||
const emit = defineEmits(['register', 'reload', 'design']);
|
||||
const [registerModal, { closeModal, changeLoading, changeOkLoading }] = useModalInner(init);
|
||||
const [registerForm, { setFieldsValue, resetFields, validate, updateSchema }] = useForm({
|
||||
schemas: [
|
||||
{
|
||||
field: 'fullName',
|
||||
label: '名称',
|
||||
component: 'Input',
|
||||
componentProps: { placeholder: '请输入', maxlength: 100 },
|
||||
rules: [{ required: true, trigger: 'blur', message: '必填' }],
|
||||
},
|
||||
{
|
||||
field: 'enCode',
|
||||
label: '编码',
|
||||
component: 'Input',
|
||||
componentProps: { placeholder: '请输入', maxlength: 50 },
|
||||
rules: [
|
||||
{ required: true, trigger: 'blur', message: '必填' },
|
||||
{ validator: formValidate('enCode'), trigger: 'blur' },
|
||||
],
|
||||
},
|
||||
{
|
||||
field: 'category',
|
||||
label: '分类',
|
||||
component: 'Select',
|
||||
componentProps: { placeholder: '请选择', showSearch: true },
|
||||
rules: [{ required: true, trigger: 'change', message: '必填' }],
|
||||
},
|
||||
{
|
||||
field: 'sortCode',
|
||||
label: '排序',
|
||||
defaultValue: 0,
|
||||
component: 'InputNumber',
|
||||
componentProps: { min: 0, max: 999999 },
|
||||
},
|
||||
{
|
||||
field: 'description',
|
||||
label: '说明 ',
|
||||
component: 'Textarea',
|
||||
componentProps: { placeholder: '请输入' },
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
function init(data) {
|
||||
resetFields();
|
||||
state.dataForm.id = data.id || '';
|
||||
updateSchema([{ field: 'category', componentProps: { options: data.categoryList || [] } }]);
|
||||
if (state.dataForm.id) {
|
||||
changeLoading(true);
|
||||
getReportInfo(state.dataForm.id)
|
||||
.then(res => {
|
||||
state.dataForm = res.data;
|
||||
setFieldsValue(state.dataForm);
|
||||
changeLoading(false);
|
||||
})
|
||||
.catch(() => {
|
||||
changeLoading(false);
|
||||
});
|
||||
}
|
||||
}
|
||||
async function handleSubmit(type = 0) {
|
||||
const values = await validate();
|
||||
if (!values) return;
|
||||
type === 1 ? (state.btnLoading = true) : changeOkLoading(true);
|
||||
const query = { ...values, id: state.dataForm.id };
|
||||
const formMethod = state.dataForm.id ? updateReport : createReport;
|
||||
formMethod(query)
|
||||
.then(res => {
|
||||
createMessage.success(res.msg);
|
||||
changeOkLoading(false);
|
||||
state.btnLoading = false;
|
||||
closeModal();
|
||||
emit('reload');
|
||||
if (!state.dataForm.id) state.dataForm.id = res.data;
|
||||
if (type == 1) emit('design', { ...values, id: state.dataForm.id });
|
||||
})
|
||||
.catch(() => {
|
||||
changeOkLoading(false);
|
||||
state.btnLoading = false;
|
||||
});
|
||||
}
|
||||
</script>
|
||||
191
src/views/onlineDev/report/index.vue
Normal file
191
src/views/onlineDev/report/index.vue
Normal file
@@ -0,0 +1,191 @@
|
||||
<template>
|
||||
<div class="yunzhupaas-content-wrapper">
|
||||
<div class="yunzhupaas-content-wrapper-center">
|
||||
<div class="yunzhupaas-content-wrapper-content">
|
||||
<BasicTable @register="registerTable">
|
||||
<template #tableTitle>
|
||||
<a-button type="primary" preIcon="icon-ym icon-ym-btn-add" @click="addOrUpdateHandle()">{{ t('common.addText') }}</a-button>
|
||||
<yunzhupaas-upload-btn url="/api/Report/Actions/Import" accept=".rp" @on-success="reload" type="report" />
|
||||
</template>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'state'">
|
||||
<a-tag :color="record.enabledMark == 1 ? 'success' : ''">{{ record.enabledMark == 1 ? '已发布' : '未发布' }}</a-tag>
|
||||
</template>
|
||||
<template v-if="column.key === 'action'">
|
||||
<TableAction :actions="getTableActions(record)" :dropDownActions="getDropDownActions(record)" />
|
||||
</template>
|
||||
</template>
|
||||
</BasicTable>
|
||||
</div>
|
||||
</div>
|
||||
<Form @register="registerForm" @reload="reload" @design="handleDesign" />
|
||||
<Report @register="registerDesign" @reload="reload" />
|
||||
<ReportPreview @register="registerPreview" />
|
||||
<CreateMenuModal @register="registerCreateMenu" />
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { BasicTable, useTable, TableAction, BasicColumn, ActionItem } from '@/components/Table';
|
||||
import { getReportList, delReport, copy, exportData } from '@/api/onlineDev/report';
|
||||
import { useModal } from '@/components/Modal';
|
||||
import { useI18n } from '@/hooks/web/useI18n';
|
||||
import { useMessage } from '@/hooks/web/useMessage';
|
||||
import { downloadByUrlReport } from '@/utils/file/download';
|
||||
import { useBaseStore } from '@/store/modules/base';
|
||||
import { Report, ReportPreview } from '@/components/Report';
|
||||
import Form from './Form.vue';
|
||||
import CreateMenuModal from './CreateMenuModal.vue';
|
||||
|
||||
defineOptions({ name: 'onlineDev-report' });
|
||||
|
||||
const { t } = useI18n();
|
||||
const baseStore = useBaseStore();
|
||||
const { createMessage } = useMessage();
|
||||
const categoryList = ref<any[]>([]);
|
||||
const columns: BasicColumn[] = [
|
||||
{ title: '名称', dataIndex: 'fullName' },
|
||||
{ title: '编码', dataIndex: 'enCode', width: 200 },
|
||||
{ title: '分类', dataIndex: 'category', width: 150 },
|
||||
{ title: '创建人', dataIndex: 'creatorUser', width: 120 },
|
||||
{ title: '创建时间', dataIndex: 'creatorTime', width: 150, format: 'date|YYYY-MM-DD HH:mm:ss' },
|
||||
{ title: '最后修改时间', dataIndex: 'lastModifyTime', width: 150, format: 'date|YYYY-MM-DD HH:mm:ss' },
|
||||
{ title: '排序', dataIndex: 'sortCode', width: 70, align: 'center' },
|
||||
{ title: '状态', dataIndex: 'state', width: 80, align: 'center' },
|
||||
];
|
||||
const [registerTable, { reload, getForm }] = useTable({
|
||||
api: getReportList,
|
||||
columns,
|
||||
useSearchForm: true,
|
||||
formConfig: {
|
||||
schemas: [
|
||||
{
|
||||
field: 'keyword',
|
||||
label: t('common.keyword'),
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: t('common.enterKeyword'),
|
||||
submitOnPressEnter: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'category',
|
||||
label: '分类',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
placeholder: '请选择',
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'state',
|
||||
label: '状态',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
placeholder: '请选择',
|
||||
options: [
|
||||
{ fullName: '未发布', id: 0 },
|
||||
{ fullName: '已发布', id: 1 },
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
actionColumn: {
|
||||
width: 220,
|
||||
title: '操作',
|
||||
dataIndex: 'action',
|
||||
},
|
||||
});
|
||||
const [registerForm, { openModal: openFormModal }] = useModal();
|
||||
const [registerDesign, { openModal: openDesign }] = useModal();
|
||||
const [registerPreview, { openModal: openPreviewModal }] = useModal();
|
||||
const [registerCreateMenu, { openModal: openCreateMenuModal }] = useModal();
|
||||
function getTableActions(record): ActionItem[] {
|
||||
return [
|
||||
{
|
||||
label: t('common.editText'),
|
||||
onClick: addOrUpdateHandle.bind(null, record.id),
|
||||
},
|
||||
{
|
||||
label: '设计',
|
||||
color: 'error',
|
||||
onClick: handleDesign.bind(null, record),
|
||||
},
|
||||
{
|
||||
label: '生成菜单',
|
||||
disabled: !record.state,
|
||||
onClick: handleCreateMenu.bind(null, record),
|
||||
},
|
||||
];
|
||||
}
|
||||
function getDropDownActions(record): ActionItem[] {
|
||||
return [
|
||||
{
|
||||
label: t('common.previewText'),
|
||||
ifShow: !!record.state,
|
||||
onClick: handlePreview.bind(null, record.id),
|
||||
},
|
||||
{
|
||||
label: t('common.copyText'),
|
||||
ifShow: !!record.state,
|
||||
modelConfirm: {
|
||||
content: '您确定要复制该模板, 是否继续?',
|
||||
onOk: handleCopy.bind(null, record.id),
|
||||
},
|
||||
},
|
||||
{
|
||||
label: t('common.exportText'),
|
||||
ifShow: !!record.state,
|
||||
modelConfirm: {
|
||||
content: '您确定要导出该模板, 是否继续?',
|
||||
onOk: handleExport.bind(null, record.id),
|
||||
},
|
||||
},
|
||||
{
|
||||
label: t('common.delText'),
|
||||
color: 'error',
|
||||
modelConfirm: {
|
||||
onOk: handleDelete.bind(null, record.id),
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
function addOrUpdateHandle(id = '') {
|
||||
openFormModal(true, { id, categoryList: categoryList.value });
|
||||
}
|
||||
function handleExport(id) {
|
||||
exportData(id).then(res => {
|
||||
downloadByUrlReport({ url: res.data.url });
|
||||
});
|
||||
}
|
||||
function handleCopy(id) {
|
||||
copy(id).then(res => {
|
||||
createMessage.success(res.msg);
|
||||
reload();
|
||||
});
|
||||
}
|
||||
function handleDelete(id) {
|
||||
delReport(id).then(res => {
|
||||
createMessage.success(res.msg);
|
||||
reload();
|
||||
});
|
||||
}
|
||||
function handleDesign(record) {
|
||||
openDesign(true, record);
|
||||
}
|
||||
function handlePreview(id) {
|
||||
openPreviewModal(true, { id });
|
||||
}
|
||||
async function getOptions() {
|
||||
categoryList.value = (await baseStore.getDictionaryData('businessType')) as any[];
|
||||
getForm().updateSchema({ field: 'category', componentProps: { options: categoryList.value } });
|
||||
}
|
||||
|
||||
function handleCreateMenu(data) {
|
||||
openCreateMenuModal(true, data);
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getOptions();
|
||||
});
|
||||
</script>
|
||||
191
src/views/onlineDev/visualPortal/Form.vue
Normal file
191
src/views/onlineDev/visualPortal/Form.vue
Normal file
@@ -0,0 +1,191 @@
|
||||
<template>
|
||||
<BasicModal v-bind="$attrs" @register="registerModal" :title="state.dataForm.id ? t('common.editText') : t('common.addText')" @ok="handleSubmit">
|
||||
<a-alert message="新建成功后,前往【系统管理】>【应用菜单】中添加应用门户并授权。" type="warning" show-icon v-if="!dataForm.id" class="!mb-20px" />
|
||||
<BasicForm @register="registerForm" />
|
||||
<template #appendFooter>
|
||||
<a-button type="primary" :loading="btnLoading" @click="handleSubmit(1)" v-if="dataForm.type === 0">确定并设计</a-button>
|
||||
</template>
|
||||
</BasicModal>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { toRefs, reactive } from 'vue';
|
||||
import { BasicModal, useModalInner } from '@/components/Modal';
|
||||
import { BasicForm, useForm } from '@/components/Form';
|
||||
import formValidate from '@/utils/formValidate';
|
||||
import { getInfo, updatePortal, createPortal } from '@/api/onlineDev/portal';
|
||||
import { useMessage } from '@/hooks/web/useMessage';
|
||||
import { isUrl } from '@/utils/is';
|
||||
import { useI18n } from '@/hooks/web/useI18n';
|
||||
|
||||
const { t } = useI18n();
|
||||
const validateUrl = (_rule, value) => {
|
||||
if (value && getFieldsValue()?.linkType == 1 && !isUrl(value)) return Promise.reject('请输入正确的链接地址');
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
||||
interface State {
|
||||
dataForm: any;
|
||||
btnLoading: boolean;
|
||||
}
|
||||
|
||||
const state = reactive<State>({
|
||||
dataForm: {
|
||||
type: 0,
|
||||
},
|
||||
btnLoading: false,
|
||||
});
|
||||
const { dataForm, btnLoading } = toRefs(state);
|
||||
const { createMessage } = useMessage();
|
||||
const emit = defineEmits(['register', 'reload', 'design']);
|
||||
const [registerModal, { closeModal, changeLoading, changeOkLoading }] = useModalInner(init);
|
||||
const [registerForm, { setFieldsValue, getFieldsValue, resetFields, validate, clearValidate, updateSchema }] = useForm({
|
||||
labelWidth: 100,
|
||||
schemas: [
|
||||
{
|
||||
field: 'fullName',
|
||||
label: '名称',
|
||||
component: 'Input',
|
||||
componentProps: { placeholder: '请输入', maxlength: 100 },
|
||||
rules: [{ required: true, trigger: 'blur', message: '必填' }],
|
||||
},
|
||||
{
|
||||
field: 'enCode',
|
||||
label: '编码',
|
||||
component: 'Input',
|
||||
componentProps: { placeholder: '请输入', maxlength: 50 },
|
||||
rules: [
|
||||
{ required: true, trigger: 'blur', message: '必填' },
|
||||
{ validator: formValidate('enCode'), trigger: 'blur' },
|
||||
],
|
||||
},
|
||||
{
|
||||
field: 'category',
|
||||
label: '分类',
|
||||
component: 'Select',
|
||||
componentProps: { placeholder: '请选择', showSearch: true },
|
||||
rules: [{ required: true, trigger: 'change', message: '必填' }],
|
||||
},
|
||||
{
|
||||
field: 'type',
|
||||
label: '类型',
|
||||
defaultValue: 0,
|
||||
component: 'Radio',
|
||||
componentProps: {
|
||||
options: [
|
||||
{ id: 0, fullName: '门户设计' },
|
||||
{ id: 1, fullName: '配置路径' },
|
||||
],
|
||||
optionType: 'button',
|
||||
buttonStyle: 'solid',
|
||||
onChange: onTypeChange,
|
||||
},
|
||||
rules: [{ required: true, trigger: 'change', message: '必填', type: 'number' }],
|
||||
},
|
||||
{
|
||||
ifShow: ({ values }) => values.type === 1,
|
||||
field: 'linkType',
|
||||
label: '链接类型',
|
||||
defaultValue: 0,
|
||||
component: 'Radio',
|
||||
componentProps: {
|
||||
options: [
|
||||
{ id: 0, fullName: '页面' },
|
||||
{ id: 1, fullName: '外链' },
|
||||
],
|
||||
optionType: 'button',
|
||||
buttonStyle: 'solid',
|
||||
onChange: onLinkTypeChange,
|
||||
},
|
||||
rules: [{ required: true, trigger: 'change', message: '必填', type: 'number' }],
|
||||
},
|
||||
{
|
||||
ifShow: ({ values }) => values.type === 1,
|
||||
field: 'customUrl',
|
||||
label: '链接地址',
|
||||
component: 'Input',
|
||||
componentProps: { placeholder: '请输入' },
|
||||
helpMessage: '链接类型选择页面,只支持PC显示,不支持APP显示。',
|
||||
rules: [
|
||||
{ required: true, trigger: 'blur', message: '必填' },
|
||||
{ validator: validateUrl, trigger: 'blur' },
|
||||
],
|
||||
},
|
||||
{
|
||||
ifShow: ({ values }) => values.type === 0,
|
||||
field: 'enabledLock',
|
||||
label: '锁定',
|
||||
component: 'Switch',
|
||||
defaultValue: 1,
|
||||
helpMessage: '启用:不允许拖拽移动控件;禁用:允许用户在PC门户上拖拽大小及移动控件。',
|
||||
},
|
||||
{
|
||||
field: 'sortCode',
|
||||
label: '排序',
|
||||
defaultValue: 0,
|
||||
component: 'InputNumber',
|
||||
componentProps: { min: 0, max: 999999 },
|
||||
},
|
||||
{
|
||||
field: 'description',
|
||||
label: '说明 ',
|
||||
component: 'Textarea',
|
||||
componentProps: { placeholder: '请输入' },
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
function init(data) {
|
||||
resetFields();
|
||||
state.dataForm.id = data.id || '';
|
||||
updateSchema([{ field: 'category', componentProps: { options: data.categoryList || [] } }]);
|
||||
state.dataForm.type = 0;
|
||||
if (state.dataForm.id) {
|
||||
changeLoading(true);
|
||||
getInfo(state.dataForm.id)
|
||||
.then(res => {
|
||||
state.dataForm = res.data;
|
||||
onLinkTypeChange(state.dataForm.linkType);
|
||||
setFieldsValue(state.dataForm);
|
||||
changeLoading(false);
|
||||
})
|
||||
.catch(() => {
|
||||
changeLoading(false);
|
||||
});
|
||||
}
|
||||
}
|
||||
function onLinkTypeChange(val) {
|
||||
updateSchema([
|
||||
{ field: 'customUrl', componentProps: { addonBefore: val === 0 ? '@/views/' : '' }, helpMessage: val === 1 ? '地址以http://或https://为开头' : '' },
|
||||
]);
|
||||
setFieldsValue({ customUrl: '' });
|
||||
clearValidate('customUrl');
|
||||
}
|
||||
function onTypeChange(val) {
|
||||
state.dataForm.type = val;
|
||||
onLinkTypeChange(getFieldsValue()?.linkType);
|
||||
}
|
||||
async function handleSubmit(type?) {
|
||||
const values = await validate();
|
||||
if (!values) return;
|
||||
type === 1 ? (state.btnLoading = true) : changeOkLoading(true);
|
||||
const query = {
|
||||
...values,
|
||||
id: state.dataForm.id,
|
||||
};
|
||||
const formMethod = state.dataForm.id ? updatePortal : createPortal;
|
||||
formMethod(query)
|
||||
.then(res => {
|
||||
createMessage.success(res.msg);
|
||||
changeOkLoading(false);
|
||||
state.btnLoading = false;
|
||||
closeModal();
|
||||
emit('reload');
|
||||
if (!state.dataForm.id) state.dataForm.id = res.data;
|
||||
if (type == 1) emit('design', { ...values, id: state.dataForm.id });
|
||||
})
|
||||
.catch(() => {
|
||||
changeOkLoading(false);
|
||||
state.btnLoading = false;
|
||||
});
|
||||
}
|
||||
</script>
|
||||
269
src/views/onlineDev/visualPortal/components/ReleaseModal.vue
Normal file
269
src/views/onlineDev/visualPortal/components/ReleaseModal.vue
Normal file
@@ -0,0 +1,269 @@
|
||||
<template>
|
||||
<BasicModal v-bind="$attrs" @register="registerModal" title="发布" @ok="handleSubmit" class="yunzhupaas-release-modal">
|
||||
<a-form :colon="false" :model="dataForm" :rules="rules" layout="vertical" ref="formElRef">
|
||||
<a-tabs v-model:activeKey="activeKey">
|
||||
<a-tab-pane key="first" tab="主页门户">
|
||||
<a-alert message="将该门户发布至应用主页门户" type="warning" show-icon />
|
||||
<div class="release-main">
|
||||
<div class="release-item">
|
||||
<a-form-item>
|
||||
<div class="top-item" :class="{ active: dataForm.pcPortal === 1 }" @click="selectToggle('pcPortal')">
|
||||
<i class="item-icon icon-ym icon-ym-pc"></i>
|
||||
<p class="item-title">桌面端</p>
|
||||
<div class="icon-checked">
|
||||
<check-outlined />
|
||||
</div>
|
||||
</div>
|
||||
</a-form-item>
|
||||
<a-form-item label="应用" name="pcPortalSystemId" v-if="dataForm.pcPortal">
|
||||
<YunzhupaasSelect v-model:value="dataForm.pcPortalSystemId" :options="treeData" multiple allowClear />
|
||||
</a-form-item>
|
||||
<a-form-item label="已发布应用" v-if="record.pcPortalIsRelease">
|
||||
<div class="released">{{ record.pcPortalReleaseName }}</div>
|
||||
</a-form-item>
|
||||
</div>
|
||||
<div class="release-item">
|
||||
<a-form-item>
|
||||
<div class="top-item" :class="{ active: dataForm.appPortal === 1 }" @click="selectToggle('appPortal')">
|
||||
<i class="item-icon icon-ym icon-ym-mobile"></i>
|
||||
<p class="item-title">移动端</p>
|
||||
<div class="icon-checked">
|
||||
<check-outlined />
|
||||
</div>
|
||||
</div>
|
||||
</a-form-item>
|
||||
<a-form-item label="应用" name="appPortalSystemId" v-if="dataForm.appPortal">
|
||||
<YunzhupaasSelect v-model:value="dataForm.appPortalSystemId" :options="treeAppData" multiple allowClear />
|
||||
</a-form-item>
|
||||
<a-form-item label="已发布应用" v-if="record.appPortalIsRelease">
|
||||
<div class="released">{{ record.appPortalReleaseName }}</div>
|
||||
</a-form-item>
|
||||
</div>
|
||||
</div>
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="second" tab="应用菜单" force-render>
|
||||
<a-alert message="将该门户发布至应用菜单" type="warning" show-icon />
|
||||
<div class="release-main">
|
||||
<div class="release-item">
|
||||
<a-form-item>
|
||||
<div class="top-item" :class="{ active: dataForm.pc === 1 }" @click="selectToggle('pc')">
|
||||
<i class="item-icon icon-ym icon-ym-pc"></i>
|
||||
<p class="item-title">桌面端</p>
|
||||
<div class="icon-checked">
|
||||
<check-outlined />
|
||||
</div>
|
||||
</div>
|
||||
</a-form-item>
|
||||
<a-form-item label="上级" name="pcModuleParentId" v-if="dataForm.pc">
|
||||
<YunzhupaasTreeSelect
|
||||
v-model:value="pcModuleParentId"
|
||||
:options="menuTreeData"
|
||||
treeCheckStrictly
|
||||
multiple
|
||||
:dropdownMatchSelectWidth="false"
|
||||
@change="onPcChange" />
|
||||
</a-form-item>
|
||||
<a-form-item label="已发布菜单路径" v-if="record.pcIsRelease">
|
||||
<div class="released">{{ record.pcReleaseName }}</div>
|
||||
</a-form-item>
|
||||
</div>
|
||||
<div class="release-item">
|
||||
<a-form-item>
|
||||
<div class="top-item" :class="{ active: dataForm.app === 1 }" @click="selectToggle('app')">
|
||||
<i class="item-icon icon-ym icon-ym-mobile"></i>
|
||||
<p class="item-title">移动端</p>
|
||||
<div class="icon-checked">
|
||||
<check-outlined />
|
||||
</div>
|
||||
</div>
|
||||
</a-form-item>
|
||||
<a-form-item label="上级" name="appModuleParentId" v-if="dataForm.app">
|
||||
<YunzhupaasTreeSelect
|
||||
v-model:value="appModuleParentId"
|
||||
:options="appMenuTreeData"
|
||||
treeCheckStrictly
|
||||
multiple
|
||||
:dropdownMatchSelectWidth="false"
|
||||
@change="onAppChange" />
|
||||
</a-form-item>
|
||||
<a-form-item label="已发布菜单路径" v-if="record.appIsRelease">
|
||||
<div class="released">{{ record.appReleaseName }}</div>
|
||||
</a-form-item>
|
||||
</div>
|
||||
</div>
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
</a-form>
|
||||
</BasicModal>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { release, getSystemListFilter, getInfo } from '@/api/onlineDev/portal';
|
||||
import { BasicModal, useModalInner } from '@/components/Modal';
|
||||
import { ref, reactive, toRefs, computed } from 'vue';
|
||||
import type { FormInstance } from 'ant-design-vue';
|
||||
import { CheckOutlined } from '@ant-design/icons-vue';
|
||||
import { getMenuSelectorFilter } from '@/api/system/menu';
|
||||
import { useMessage } from '@/hooks/web/useMessage';
|
||||
import { useI18n } from '@/hooks/web/useI18n';
|
||||
|
||||
interface State {
|
||||
dataForm: any;
|
||||
record: any;
|
||||
treeData: any[];
|
||||
treeAppData: any[];
|
||||
menuTreeData: any[];
|
||||
appMenuTreeData: any[];
|
||||
pcModuleParentId: any[];
|
||||
appModuleParentId: any[];
|
||||
activeKey: String;
|
||||
}
|
||||
|
||||
const emit = defineEmits(['register', 'reload']);
|
||||
const { createMessage, createConfirm } = useMessage();
|
||||
const { t } = useI18n();
|
||||
const [registerModal, { changeOkLoading, closeModal }] = useModalInner(init);
|
||||
const formElRef = ref<FormInstance>();
|
||||
const state = reactive<State>({
|
||||
dataForm: {
|
||||
pc: 1,
|
||||
app: 1,
|
||||
pcModuleParentId: [],
|
||||
appModuleParentId: [],
|
||||
pcPortal: 1,
|
||||
appPortal: 1,
|
||||
pcPortalSystemId: [],
|
||||
appPortalSystemId: [],
|
||||
},
|
||||
record: {},
|
||||
treeData: [],
|
||||
treeAppData: [],
|
||||
menuTreeData: [],
|
||||
appMenuTreeData: [],
|
||||
pcModuleParentId: [],
|
||||
appModuleParentId: [],
|
||||
activeKey: 'first',
|
||||
});
|
||||
const { dataForm, record, treeData, treeAppData, menuTreeData, appMenuTreeData, activeKey, pcModuleParentId, appModuleParentId } = toRefs(state);
|
||||
|
||||
const rules = computed(() => {
|
||||
let rules: any = {
|
||||
pcModuleParentId: [],
|
||||
appModuleParentId: [],
|
||||
pcPortalSystemId: [],
|
||||
appPortalSystemId: [],
|
||||
};
|
||||
if (!state.record.pcPortalIsRelease) rules.pcPortalSystemId = [{ required: true, message: '必填', trigger: 'change', type: 'array' }];
|
||||
if (!state.record.appPortalIsRelease) rules.appPortalSystemId = [{ required: true, message: '必填', trigger: 'change', type: 'array' }];
|
||||
if (!state.record.pcIsRelease) rules.pcModuleParentId = [{ required: true, message: '必填', trigger: 'change', type: 'array' }];
|
||||
if (!state.record.appIsRelease) rules.appModuleParentId = [{ required: true, message: '必填', trigger: 'change', type: 'array' }];
|
||||
return rules;
|
||||
});
|
||||
function init(data) {
|
||||
state.activeKey = 'first';
|
||||
state.pcModuleParentId = [];
|
||||
state.appModuleParentId = [];
|
||||
getInfo(data.id).then(res => {
|
||||
state.record = res.data;
|
||||
const platformRelease = res.data.platformRelease ? JSON.parse(res.data.platformRelease) : {};
|
||||
state.dataForm = {
|
||||
pc: platformRelease.pc === 0 ? 0 : 1,
|
||||
app: platformRelease.app === 0 ? 0 : 1,
|
||||
pcModuleParentId: [],
|
||||
appModuleParentId: [],
|
||||
pcPortal: platformRelease.pcPortal === 0 ? 0 : 1,
|
||||
appPortal: platformRelease.appPortal === 0 ? 0 : 1,
|
||||
pcPortalSystemId: [],
|
||||
appPortalSystemId: [],
|
||||
};
|
||||
formElRef.value?.clearValidate();
|
||||
});
|
||||
getSystemOptions(data.id);
|
||||
getAppSystemOptions(data.id);
|
||||
getMenuOptions(data.id);
|
||||
getAppMenuOptions(data.id);
|
||||
}
|
||||
function getSystemOptions(id) {
|
||||
getSystemListFilter({ category: 'Web' }, id).then(res => {
|
||||
state.treeData = res.data.list || [];
|
||||
});
|
||||
}
|
||||
function getAppSystemOptions(id) {
|
||||
getSystemListFilter({ category: 'App' }, id).then(res => {
|
||||
state.treeAppData = res.data.list || [];
|
||||
});
|
||||
}
|
||||
function selectToggle(key) {
|
||||
state.dataForm[key] = state.dataForm[key] === 1 ? 0 : 1;
|
||||
}
|
||||
async function handleSubmit() {
|
||||
try {
|
||||
if (!state.dataForm.pc && !state.dataForm.app && !state.dataForm.pcPortal && !state.dataForm.appPortal)
|
||||
return createMessage.error('请至少选择一种发布类型');
|
||||
const values = await formElRef.value?.validate();
|
||||
if (!values) return;
|
||||
const platform = {
|
||||
pc: state.dataForm.pc,
|
||||
app: state.dataForm.app,
|
||||
pcPortal: state.dataForm.pcPortal,
|
||||
appPortal: state.dataForm.appPortal,
|
||||
};
|
||||
const query = { ...state.dataForm, platformRelease: JSON.stringify(platform) };
|
||||
const handleRelease = () => {
|
||||
changeOkLoading(true);
|
||||
release(state.record.id, query)
|
||||
.then(res => {
|
||||
changeOkLoading(false);
|
||||
createMessage.success(res.msg);
|
||||
emit('reload');
|
||||
closeModal();
|
||||
})
|
||||
.catch(() => {
|
||||
changeOkLoading(false);
|
||||
});
|
||||
};
|
||||
|
||||
if (!state.record.isRelease) return handleRelease();
|
||||
createConfirm({
|
||||
iconType: 'warning',
|
||||
title: t('common.tipTitle'),
|
||||
content: '发布确定后会覆盖当前线上版本且进行门户同步,是否继续?',
|
||||
onOk: handleRelease,
|
||||
});
|
||||
} catch (_) {
|
||||
if (
|
||||
(state.dataForm.pcPortalSystemId.length || !state.dataForm.pcPortal) &&
|
||||
(state.dataForm.appPortalSystemId.length || !state.dataForm.appPortal) &&
|
||||
!state.dataForm.pcModuleParentId.length &&
|
||||
!state.dataForm.appModuleParentId.length
|
||||
)
|
||||
return createMessage.error('应用菜单的上级必填');
|
||||
if (
|
||||
!state.dataForm.pcPortalSystemId.length &&
|
||||
!state.dataForm.appPortalSystemId.length &&
|
||||
(state.dataForm.pcModuleParentId.length || !state.dataForm.pc) &&
|
||||
(state.dataForm.appModuleParentId.length || !state.dataForm.app)
|
||||
)
|
||||
return createMessage.error('主页门户的应用必填');
|
||||
}
|
||||
}
|
||||
function getMenuOptions(id) {
|
||||
getMenuSelectorFilter({ category: 'Web' }, id).then(res => {
|
||||
state.menuTreeData = res.data.list || [];
|
||||
});
|
||||
}
|
||||
function getAppMenuOptions(id) {
|
||||
getMenuSelectorFilter({ category: 'App' }, id).then(res => {
|
||||
state.appMenuTreeData = res.data.list || [];
|
||||
for (let index = 0; index < state.appMenuTreeData.length; index++) {
|
||||
const item = state.appMenuTreeData[index];
|
||||
if (item.type == 0) item.disabled = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
function onPcChange(data) {
|
||||
state.dataForm.pcModuleParentId = data.map(o => o.value);
|
||||
}
|
||||
function onAppChange(data) {
|
||||
state.dataForm.appModuleParentId = data.map(o => o.value);
|
||||
}
|
||||
</script>
|
||||
238
src/views/onlineDev/visualPortal/index.vue
Normal file
238
src/views/onlineDev/visualPortal/index.vue
Normal file
@@ -0,0 +1,238 @@
|
||||
<template>
|
||||
<div class="yunzhupaas-content-wrapper">
|
||||
<div class="yunzhupaas-content-wrapper-center">
|
||||
<div class="yunzhupaas-content-wrapper-content">
|
||||
<BasicTable @register="registerTable">
|
||||
<template #tableTitle>
|
||||
<a-button type="primary" preIcon="icon-ym icon-ym-btn-add" @click="addOrUpdateHandle()">{{ t('common.addText') }}</a-button>
|
||||
<yunzhupaas-upload-btn url="/api/visualdev/Portal/Actions/Import" accept=".vp" @on-success="reload"></yunzhupaas-upload-btn>
|
||||
</template>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'isRelease'">
|
||||
<a-tag :color="record.isRelease === 1 ? 'success' : record.isRelease == 2 ? 'warning' : ''">
|
||||
{{ record.isRelease == 1 ? '已发布' : record.isRelease == 2 ? '已修改' : '未发布' }}
|
||||
</a-tag>
|
||||
</template>
|
||||
<template v-if="column.key === 'action'">
|
||||
<TableAction :actions="getTableActions(record)" :dropDownActions="getDropDownActions(record)" />
|
||||
</template>
|
||||
</template>
|
||||
</BasicTable>
|
||||
</div>
|
||||
</div>
|
||||
<Form @register="registerForm" @reload="reload" @design="handleDesignFun" />
|
||||
<PortalDesign @register="registerPortalDesign" @reload="reload" />
|
||||
<ReleaseModal @register="registerReleaseModal" @reload="reload" />
|
||||
<PreviewModal @register="registerPreviewModal" @previewPc="previewPc" />
|
||||
<Preview @register="registerPreview" />
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { getPortalList, delPortal, copyPortal, exportPortal } from '@/api/onlineDev/portal';
|
||||
import { BasicTable, useTable, TableAction, BasicColumn, ActionItem } from '@/components/Table';
|
||||
import { useMessage } from '@/hooks/web/useMessage';
|
||||
import { useI18n } from '@/hooks/web/useI18n';
|
||||
import { useModal } from '@/components/Modal';
|
||||
import { useBaseStore } from '@/store/modules/base';
|
||||
import Form from './Form.vue';
|
||||
import PortalDesign from '@/components/VisualPortal/Design/index.vue';
|
||||
import ReleaseModal from './components/ReleaseModal.vue';
|
||||
import { downloadByUrl } from '@/utils/file/download';
|
||||
import Preview from '@/components/VisualPortal/Design/components/Preview.vue';
|
||||
import { PreviewModal } from '@/components/CommonModal';
|
||||
|
||||
defineOptions({ name: 'onlineDev-visualPortal' });
|
||||
|
||||
const { createMessage } = useMessage();
|
||||
const baseStore = useBaseStore();
|
||||
const { t } = useI18n();
|
||||
const [registerForm, { openModal: openFormModal }] = useModal();
|
||||
const [registerPortalDesign, { openModal: openPortalDesign }] = useModal();
|
||||
const [registerReleaseModal, { openModal: openReleaseModal }] = useModal();
|
||||
const [registerPreviewModal, { openModal: openPreviewModal }] = useModal();
|
||||
const [registerPreview, { openModal: openPreview }] = useModal();
|
||||
|
||||
const columns: BasicColumn[] = [
|
||||
{ title: '名称', dataIndex: 'fullName', width: 200 },
|
||||
{ title: '编码', dataIndex: 'enCode', width: 180 },
|
||||
{ title: '分类', dataIndex: 'category', width: 100 },
|
||||
{ title: '类型', dataIndex: 'type', width: 100, align: 'center', customRender: ({ record }) => (record.type === 1 ? '配置路径' : '门户设计') },
|
||||
{
|
||||
title: '锁定',
|
||||
dataIndex: 'enabledLock',
|
||||
width: 100,
|
||||
align: 'center',
|
||||
customRender: ({ record }) => (record.type === 1 ? '' : record.enabledLock === 1 ? '是' : '否'),
|
||||
},
|
||||
{ title: '创建人', dataIndex: 'creatorUser', width: 120 },
|
||||
{ title: '创建时间', dataIndex: 'creatorTime', width: 150, format: 'date|YYYY-MM-DD HH:mm:ss' },
|
||||
{ title: '最后修改时间', dataIndex: 'lastModifyTime', width: 150, format: 'date|YYYY-MM-DD HH:mm:ss' },
|
||||
{ title: '排序', dataIndex: 'sortCode', width: 70, align: 'center' },
|
||||
{ title: '发布状态', dataIndex: 'isRelease', width: 80, align: 'center' },
|
||||
];
|
||||
const typeList = [
|
||||
{ fullName: '配置路径', id: 1 },
|
||||
{ fullName: '门户设计', id: 0 },
|
||||
];
|
||||
const enabledLockList = [
|
||||
{ fullName: '是', id: 1 },
|
||||
{ fullName: '否', id: 0 },
|
||||
];
|
||||
const categoryList = ref<any[]>([]);
|
||||
const [registerTable, { reload, getForm }] = useTable({
|
||||
api: getPortalList,
|
||||
columns,
|
||||
useSearchForm: true,
|
||||
formConfig: {
|
||||
schemas: [
|
||||
{
|
||||
field: 'keyword',
|
||||
label: t('common.keyword'),
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: t('common.enterKeyword'),
|
||||
submitOnPressEnter: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'category',
|
||||
label: '分类',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
placeholder: '请选择',
|
||||
showSearch: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'type',
|
||||
label: '类型',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
placeholder: '请选择',
|
||||
showSearch: true,
|
||||
options: typeList,
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'enabledLock',
|
||||
label: '锁定',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
placeholder: '请选择',
|
||||
showSearch: true,
|
||||
options: enabledLockList,
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'isRelease',
|
||||
label: '发布状态',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
placeholder: '请选择',
|
||||
options: [
|
||||
{ fullName: '未发布', id: 0 },
|
||||
{ fullName: '已发布', id: 1 },
|
||||
{ fullName: '已修改', id: 2 },
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
actionColumn: {
|
||||
width: 180,
|
||||
title: '操作',
|
||||
dataIndex: 'action',
|
||||
},
|
||||
});
|
||||
function getTableActions(record): ActionItem[] {
|
||||
return [
|
||||
{
|
||||
label: t('common.editText'),
|
||||
onClick: addOrUpdateHandle.bind(null, record.id),
|
||||
},
|
||||
{
|
||||
label: '发布',
|
||||
color: 'error',
|
||||
onClick: handleRelease.bind(null, record.id),
|
||||
},
|
||||
{
|
||||
label: '设计',
|
||||
disabled: record.type !== 0,
|
||||
onClick: handleDesign.bind(null, record),
|
||||
},
|
||||
];
|
||||
}
|
||||
function getDropDownActions(record): ActionItem[] {
|
||||
return [
|
||||
{
|
||||
label: t('common.previewText'),
|
||||
onClick: handlePreview.bind(null, record.id),
|
||||
},
|
||||
{
|
||||
label: t('common.copyText'),
|
||||
modelConfirm: {
|
||||
content: '您确定要复制该门户, 是否继续?',
|
||||
onOk: handleCopy.bind(null, record.id),
|
||||
},
|
||||
},
|
||||
{
|
||||
label: t('common.exportText'),
|
||||
modelConfirm: {
|
||||
content: '您确定要导出该门户, 是否继续?',
|
||||
onOk: handleExport.bind(null, record.id),
|
||||
},
|
||||
},
|
||||
{
|
||||
label: t('common.delText'),
|
||||
modelConfirm: {
|
||||
onOk: handleDelete.bind(null, record.id),
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
function addOrUpdateHandle(id = '') {
|
||||
openFormModal(true, { id, categoryList });
|
||||
}
|
||||
function handleDelete(id) {
|
||||
delPortal(id).then(res => {
|
||||
createMessage.success(res.msg);
|
||||
reload();
|
||||
});
|
||||
}
|
||||
function handleRelease(id) {
|
||||
openReleaseModal(true, { id });
|
||||
}
|
||||
function handleDesign(record) {
|
||||
openPortalDesign(true, record);
|
||||
}
|
||||
function handleDesignFun(item) {
|
||||
handleDesign(item);
|
||||
}
|
||||
function handlePreview(id) {
|
||||
openPreviewModal(true, { type: 'portal', id });
|
||||
}
|
||||
function previewPc({ id }) {
|
||||
openPreview(true, { id });
|
||||
}
|
||||
function handleCopy(id) {
|
||||
copyPortal(id).then(res => {
|
||||
createMessage.success(res.msg);
|
||||
reload();
|
||||
});
|
||||
}
|
||||
function handleExport(id) {
|
||||
exportPortal(id).then(res => {
|
||||
downloadByUrl({ url: res.data.url });
|
||||
});
|
||||
}
|
||||
async function getOptions() {
|
||||
const res = await baseStore.getDictionaryData('businessType');
|
||||
categoryList.value = res as any[];
|
||||
getForm().updateSchema({ field: 'category', componentProps: { options: res } });
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getOptions();
|
||||
});
|
||||
</script>
|
||||
578
src/views/onlineDev/webDesign/Form.vue
Normal file
578
src/views/onlineDev/webDesign/Form.vue
Normal file
@@ -0,0 +1,578 @@
|
||||
<template>
|
||||
<BasicModal
|
||||
v-bind="$attrs"
|
||||
@register="registerModal"
|
||||
defaultFullscreen
|
||||
:footer="null"
|
||||
:closable="false"
|
||||
:keyboard="false"
|
||||
class="yunzhupaas-full-modal full-modal designer-modal"
|
||||
destroy-on-close>
|
||||
<template #title>
|
||||
<div class="yunzhupaas-full-modal-header">
|
||||
<div class="header-title">
|
||||
<img src="@/assets/images/yunzhupaas.png" class="header-logo" />
|
||||
<span class="header-dot"></span>
|
||||
<p class="header-txt" v-if="!activeStep">在线开发</p>
|
||||
<a-tooltip :title="dataForm.fullName" v-else>
|
||||
<p class="header-txt">{{ dataForm.fullName }}</p>
|
||||
</a-tooltip>
|
||||
</div>
|
||||
<a-steps v-model:current="activeStep" type="navigation" size="small" @change="onStepChange" class="header-steps">
|
||||
<a-step title="基础设计" />
|
||||
<a-step title="表单设计" :disabled="activeStep <= 1" />
|
||||
<a-step title="列表设计" disabled v-if="maxStep >= 2" />
|
||||
</a-steps>
|
||||
<a-space class="options" :size="10">
|
||||
<a-button shape="round" type="warning" @click="toggleWebType(1)" v-show="activeStep == 2 && dataForm.webType == 2">
|
||||
{{ t('common.closeList') }}
|
||||
</a-button>
|
||||
<ValidatePopover ref="validatePopoverRef" :errorList="errorList" @select="handleSelect" v-if="activeStep == 1" />
|
||||
<a-space-compact block>
|
||||
<a-button shape="round" @click="handlePrev" :disabled="activeStep <= 0 || btnLoading">{{ t('common.prev') }}</a-button>
|
||||
<a-button shape="round" @click="handleNext" :disabled="activeStep >= maxStep || loading || btnLoading">{{ t('common.next') }} </a-button>
|
||||
</a-space-compact>
|
||||
<a-button shape="round" type="primary" @click="handleSubmit()" :disabled="loading" :loading="btnLoading">{{ t('common.saveText') }}</a-button>
|
||||
<a-button shape="round" @click="handleCancel()">{{ t('common.closeText') }}</a-button>
|
||||
</a-space>
|
||||
</div>
|
||||
</template>
|
||||
<a-row type="flex" justify="center" align="middle" class="basic-content" v-show="!activeStep">
|
||||
<a-col :span="12" :xxl="10" class="basic-form">
|
||||
<BasicForm @register="registerForm" />
|
||||
<a-table :data-source="tables" :columns="columns" size="small" :pagination="false" :scroll="{ x: 'max-content' }">
|
||||
<template #bodyCell="{ column, record, index }">
|
||||
<template v-if="column.key === 'typeId'">
|
||||
<a-tag color="processing" v-if="record.typeId == '1'">主表</a-tag>
|
||||
<a-tag color="warning" @click="changeTable(record)" v-else style="cursor: pointer" title="点击设置成主表">从表</a-tag>
|
||||
</template>
|
||||
<template v-if="column.key === 'table'">
|
||||
<span :title="record.tableName || record.table">{{ record.table }}</span>
|
||||
</template>
|
||||
<template v-if="column.key === 'tableField' && record.typeId !== '1'">
|
||||
<yunzhupaas-select
|
||||
v-model:value="record.tableField"
|
||||
placeholder="请选择"
|
||||
:options="record.fields"
|
||||
:field-names="{ value: 'field', label: 'field' }"
|
||||
showSearch
|
||||
class="!w-144px" />
|
||||
</template>
|
||||
<template v-if="column.key === 'relationField' && record.typeId !== '1'">
|
||||
<yunzhupaas-select
|
||||
v-model:value="record.relationField"
|
||||
placeholder="请选择"
|
||||
:options="mainTableFields"
|
||||
:field-names="{ value: 'field', label: 'field' }"
|
||||
showSearch
|
||||
class="!w-144px" />
|
||||
</template>
|
||||
<template v-if="column.key === 'action'">
|
||||
<a-button class="action-btn" type="link" color="error" @click="handleDelItem(record, index)" size="small">移除</a-button>
|
||||
</template>
|
||||
</template>
|
||||
<template #emptyText>
|
||||
<p class="ant-table__empty-text">点击“新增”可选择1条(单表)或2条以上(多表),未选择数据表时系统将会自动创建数据表</p>
|
||||
</template>
|
||||
</a-table>
|
||||
<div class="table-add-action" @click="openTableBox">
|
||||
<a-button type="link" preIcon="icon-ym icon-ym-btn-add">新增一行</a-button>
|
||||
</div>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<FormGenerator
|
||||
ref="generatorRef"
|
||||
:conf="formData"
|
||||
:formInfo="dataForm"
|
||||
:dbType="dbType"
|
||||
@showValidatePopover="showValidatePopover"
|
||||
v-if="activeStep == 1" />
|
||||
<BasicColumnDesign
|
||||
ref="columnDesignRef"
|
||||
:columnData="columnData"
|
||||
:appColumnData="appColumnData"
|
||||
:formInfo="dataForm"
|
||||
@toggleWebType="toggleWebType"
|
||||
v-if="activeStep == 2" />
|
||||
<TableModal @register="registerTableModal" @select="onTableSelect" />
|
||||
</BasicModal>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { getInfo, create, update } from '@/api/onlineDev/visualDev';
|
||||
import { getDataSourceSelector } from '@/api/systemData/dataSource';
|
||||
import { getDataModelFieldList } from '@/api/systemData/dataModel';
|
||||
import { ref, reactive, toRefs, unref, nextTick } from 'vue';
|
||||
import { BasicModal, useModal, useModalInner } from '@/components/Modal';
|
||||
import { BasicForm, useForm } from '@/components/Form';
|
||||
import { useMessage } from '@/hooks/web/useMessage';
|
||||
import { useI18n } from '@/hooks/web/useI18n';
|
||||
import { useGeneratorStore } from '@/store/modules/generator';
|
||||
import formValidate from '@/utils/formValidate';
|
||||
import TableModal from './components/TableModal.vue';
|
||||
import { FormGenerator } from '@/components/FormGenerator';
|
||||
import { BasicColumnDesign } from '@/components/ColumnDesign';
|
||||
import { ValidatePopover } from '@/components/CommonModal';
|
||||
|
||||
interface State {
|
||||
activeStep: number;
|
||||
maxStep: number;
|
||||
loading: boolean;
|
||||
btnLoading: boolean;
|
||||
relationTable: boolean;
|
||||
mainTableFields: any[];
|
||||
dbOptions: any[];
|
||||
tables: any[];
|
||||
defaultTable: any[];
|
||||
dataForm: Recordable;
|
||||
isReload: boolean;
|
||||
[prop: string]: any;
|
||||
errorList: any[];
|
||||
}
|
||||
interface ComType {
|
||||
getData: () => any;
|
||||
setVisible: (data) => any;
|
||||
activeFormItemById: (data) => any;
|
||||
setTabActiveKey: (data) => any;
|
||||
}
|
||||
|
||||
const emit = defineEmits(['register', 'reload']);
|
||||
const [registerForm, { setFieldsValue, getFieldsValue, resetFields, validate, updateSchema }] = useForm({
|
||||
schemas: [
|
||||
{
|
||||
field: 'fullName',
|
||||
label: '表单名称',
|
||||
component: 'Input',
|
||||
componentProps: { placeholder: '请输入', maxlength: 100 },
|
||||
rules: [{ required: true, trigger: 'blur', message: '必填' }],
|
||||
},
|
||||
{
|
||||
field: 'enCode',
|
||||
label: '表单编码',
|
||||
component: 'Input',
|
||||
componentProps: { placeholder: '请输入', maxlength: 50 },
|
||||
rules: [
|
||||
{ required: true, trigger: 'blur', message: '必填' },
|
||||
{ validator: formValidate('enCode'), trigger: 'blur' },
|
||||
],
|
||||
},
|
||||
{
|
||||
field: 'category',
|
||||
label: '表单分类',
|
||||
component: 'Select',
|
||||
componentProps: { placeholder: '请选择', showSearch: true },
|
||||
rules: [{ required: true, trigger: 'change', message: '必填' }],
|
||||
},
|
||||
{
|
||||
field: 'sortCode',
|
||||
label: '表单排序',
|
||||
defaultValue: 0,
|
||||
component: 'InputNumber',
|
||||
componentProps: { min: 0, max: 999999 },
|
||||
},
|
||||
{
|
||||
field: 'description',
|
||||
label: '表单说明',
|
||||
component: 'Textarea',
|
||||
componentProps: { placeholder: '请输入' },
|
||||
},
|
||||
{
|
||||
field: 'dbLinkId',
|
||||
label: '数据连接',
|
||||
defaultValue: '0',
|
||||
component: 'Select',
|
||||
componentProps: { placeholder: '请选择', allowClear: false, showSearch: true, fieldNames: { options: 'children' }, onChange: onDbChange },
|
||||
},
|
||||
],
|
||||
});
|
||||
const [registerTableModal, { openModal: openTableModal }] = useModal();
|
||||
const [registerModal, { closeModal, changeLoading }] = useModalInner(init);
|
||||
const { createMessage, createConfirm } = useMessage();
|
||||
const generatorStore = useGeneratorStore();
|
||||
const { t } = useI18n();
|
||||
const state = reactive<State>({
|
||||
activeStep: 0,
|
||||
maxStep: 2,
|
||||
loading: false,
|
||||
btnLoading: false,
|
||||
relationTable: false,
|
||||
mainTableFields: [],
|
||||
dbOptions: [],
|
||||
tables: [],
|
||||
defaultTable: [],
|
||||
dataForm: {
|
||||
id: '',
|
||||
fullName: '',
|
||||
enCode: '',
|
||||
type: 1,
|
||||
webType: 2,
|
||||
dbLinkId: '0',
|
||||
sortCode: 0,
|
||||
state: 1,
|
||||
category: '',
|
||||
description: '',
|
||||
tables: '',
|
||||
interfaceId: '',
|
||||
interfaceName: '',
|
||||
interfaceParam: '',
|
||||
},
|
||||
formData: null,
|
||||
columnData: null,
|
||||
appColumnData: null,
|
||||
dbType: 'MySQL',
|
||||
isReload: false,
|
||||
errorList: [],
|
||||
});
|
||||
const generatorRef = ref<Nullable<ComType>>(null);
|
||||
const columnDesignRef = ref<Nullable<ComType>>(null);
|
||||
const validatePopoverRef = ref<Nullable<ComType>>(null);
|
||||
const { activeStep, maxStep, loading, btnLoading, tables, mainTableFields, dbType, formData, columnData, appColumnData, dataForm, errorList } = toRefs(state);
|
||||
const columns = [
|
||||
{ title: '类别', dataIndex: 'typeId', key: 'typeId', width: 65 },
|
||||
{ title: '表名', dataIndex: 'table', key: 'table' },
|
||||
{ title: '外键字段', dataIndex: 'tableField', key: 'tableField', width: 160 },
|
||||
{ title: '关联主键', dataIndex: 'relationField', key: 'relationField', width: 160 },
|
||||
{ title: '操作', dataIndex: 'action', key: 'action', width: 50, fixed: 'right' },
|
||||
];
|
||||
function handleDelItem(record, index) {
|
||||
createConfirm({
|
||||
iconType: 'warning',
|
||||
title: t('common.tipTitle'),
|
||||
content: '确定要移除当前行?',
|
||||
onOk: () => {
|
||||
state.tables.splice(index, 1);
|
||||
if (record.typeId == '1' && state.tables.length) {
|
||||
state.tables[0].typeId = '1';
|
||||
state.tables[0].relationTable = '';
|
||||
state.tables[0].tableField = '';
|
||||
state.tables[0].relationField = '';
|
||||
state.tables[0].relationTable = '';
|
||||
state.mainTableFields = state.tables[0].fields;
|
||||
state.relationTable = state.tables[0].table;
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
function init(data) {
|
||||
state.isReload = false;
|
||||
state.activeStep = 0;
|
||||
state.loading = true;
|
||||
state.tables = [];
|
||||
state.defaultTable = [];
|
||||
state.errorList = [];
|
||||
state.formData = null;
|
||||
state.columnData = null;
|
||||
state.appColumnData = null;
|
||||
updateSchema([{ field: 'category', componentProps: { options: data.categoryList } }]);
|
||||
getDbOptions();
|
||||
changeLoading(true);
|
||||
resetFields();
|
||||
state.dataForm.id = data.id;
|
||||
if (state.dataForm.id) {
|
||||
getInfo(state.dataForm.id).then(res => {
|
||||
state.dataForm = res.data;
|
||||
state.maxStep = state.dataForm.webType == 4 ? 1 : 2;
|
||||
setFieldsValue(state.dataForm);
|
||||
state.formData = state.dataForm.formData && JSON.parse(state.dataForm.formData);
|
||||
state.columnData = state.dataForm.columnData && JSON.parse(state.dataForm.columnData);
|
||||
state.appColumnData = state.dataForm.appColumnData && JSON.parse(state.dataForm.appColumnData);
|
||||
state.tables = state.dataForm.tables ? JSON.parse(state.dataForm.tables) : [];
|
||||
state.defaultTable = state.dataForm.tables ? JSON.parse(state.dataForm.tables) : [];
|
||||
updateFields();
|
||||
changeLoading(false);
|
||||
});
|
||||
} else {
|
||||
state.dataForm.type = data.type;
|
||||
state.dataForm.webType = data.webType || 2;
|
||||
state.maxStep = state.dataForm.webType == 4 ? 1 : 2;
|
||||
state.loading = false;
|
||||
changeLoading(false);
|
||||
}
|
||||
}
|
||||
function toggleWebType(type) {
|
||||
createConfirm({
|
||||
iconType: 'warning',
|
||||
title: t('common.tipTitle'),
|
||||
content: type == '1' ? '关闭后,将切换为纯表单模式' : '开启后,将切换为表单+列表模式',
|
||||
onOk: () => {
|
||||
state.dataForm.webType = type;
|
||||
},
|
||||
});
|
||||
}
|
||||
async function updateFields() {
|
||||
if (!state.tables.length) {
|
||||
state.loading = false;
|
||||
nextTick(() => handleNext());
|
||||
return;
|
||||
}
|
||||
state.dataForm.dbLinkId = state.dataForm.dbLinkId || '0';
|
||||
const type = state.dataForm.type;
|
||||
const queryType = type == 3 || type == 4 || type == 5 ? '1' : '0';
|
||||
for (let i = 0; i < state.tables.length; i++) {
|
||||
const res = await getDataModelFieldList(state.dataForm.dbLinkId, state.tables[i].table, queryType);
|
||||
const fields = res.data.list;
|
||||
state.tables[i].fields = fields;
|
||||
if (state.tables[i].typeId == '1') {
|
||||
state.mainTableFields = state.tables[i].fields;
|
||||
state.relationTable = state.tables[i].table;
|
||||
}
|
||||
}
|
||||
state.loading = false;
|
||||
nextTick(() => handleNext());
|
||||
}
|
||||
function onDbChange() {
|
||||
state.tables = [];
|
||||
}
|
||||
function getDbOptions() {
|
||||
getDataSourceSelector().then(res => {
|
||||
let list = res.data.list || [];
|
||||
list = list.filter(o => o.children && o.children.length);
|
||||
if (list[0] && list[0].children && list[0].children.length) list[0] = list[0].children[0];
|
||||
delete list[0].children;
|
||||
state.dbOptions = list;
|
||||
updateSchema([{ field: 'dbLinkId', componentProps: { options: state.dbOptions } }]);
|
||||
});
|
||||
}
|
||||
function getDbType() {
|
||||
for (let i = 0; i < state.dbOptions.length; i++) {
|
||||
const item = state.dbOptions[i];
|
||||
if (state.dataForm.dbLinkId === item.id) {
|
||||
state.dbType = item.dbType;
|
||||
break;
|
||||
}
|
||||
const e = state.dbOptions[i].children || [];
|
||||
for (let j = 0; j < e.length; j++) {
|
||||
if (state.dataForm.dbLinkId === e[j].id) {
|
||||
state.dbType = e[j].dbType;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
function openTableBox() {
|
||||
const values = getFieldsValue();
|
||||
if (!values.dbLinkId) return createMessage.error('请先选择数据库');
|
||||
openTableModal(true, { dbLinkId: values.dbLinkId });
|
||||
}
|
||||
async function onTableSelect(data) {
|
||||
const values = getFieldsValue();
|
||||
const type = state.dataForm.type;
|
||||
const queryType = type == 3 || type == 4 || type == 5 ? '1' : '0';
|
||||
const checkList: any[] = [];
|
||||
if (!state.tables.length) {
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
const e = data[i];
|
||||
const relationTable = data[0].table;
|
||||
const typeId = i == 0 ? '1' : '0';
|
||||
const res = await getDataModelFieldList(values.dbLinkId, e.table, queryType);
|
||||
const fields = res.data.list;
|
||||
const item = {
|
||||
relationField: '',
|
||||
relationTable: i == 0 ? '' : relationTable,
|
||||
table: e.table,
|
||||
tableName: e.tableName,
|
||||
tableField: '',
|
||||
typeId,
|
||||
fields,
|
||||
};
|
||||
checkList.push(item);
|
||||
}
|
||||
state.relationTable = checkList[0].table;
|
||||
state.mainTableFields = checkList[0].fields;
|
||||
state.tables = checkList;
|
||||
} else {
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
const e = data[i];
|
||||
let boo = state.tables.some(o => o.table == e.table);
|
||||
if (!boo) {
|
||||
const res = await getDataModelFieldList(values.dbLinkId, e.table, queryType);
|
||||
const fields = res.data.list;
|
||||
const item = {
|
||||
relationField: '',
|
||||
relationTable: state.relationTable,
|
||||
table: e.table,
|
||||
tableName: e.tableName,
|
||||
tableField: '',
|
||||
typeId: '0',
|
||||
fields,
|
||||
};
|
||||
checkList.push(item);
|
||||
}
|
||||
}
|
||||
state.tables = [...state.tables, ...checkList];
|
||||
}
|
||||
state.loading = false;
|
||||
}
|
||||
function changeTable(record) {
|
||||
state.relationTable = record.table;
|
||||
state.mainTableFields = record.fields;
|
||||
for (let i = 0; i < state.tables.length; i++) {
|
||||
state.tables[i].typeId = state.tables[i].table === record.table ? '1' : '0';
|
||||
state.tables[i].relationTable = state.tables[i].table === record.table ? '' : state.relationTable;
|
||||
state.tables[i].relationField = '';
|
||||
state.tables[i].tableField = '';
|
||||
}
|
||||
}
|
||||
function handlePrev() {
|
||||
state.activeStep -= 1;
|
||||
if (state.activeStep == 0) updateTables();
|
||||
}
|
||||
async function handleNext() {
|
||||
if (state.activeStep < 1) {
|
||||
const values = await validate();
|
||||
if (!values) return;
|
||||
state.dataForm = { ...state.dataForm, ...values };
|
||||
getDbType();
|
||||
const type = state.dataForm.type;
|
||||
if (!state.tables.length) {
|
||||
if (state.defaultTable.length || type == 3 || type == 4) {
|
||||
createMessage.warning('请至少选择一个数据表');
|
||||
return;
|
||||
}
|
||||
generatorStore.setHasTable(false);
|
||||
generatorStore.setAllTable([]);
|
||||
generatorStore.setFormItemList([]);
|
||||
state.activeStep += 1;
|
||||
} else {
|
||||
if (!exist()) return;
|
||||
const subTable = state.tables.filter(o => o.typeId == '0');
|
||||
generatorStore.setHasTable(true);
|
||||
generatorStore.setAllTable(state.tables);
|
||||
generatorStore.setSubTable(subTable);
|
||||
generatorStore.setFormItemList(state.mainTableFields);
|
||||
state.activeStep += 1;
|
||||
}
|
||||
} else if (state.activeStep === 1) {
|
||||
(unref(generatorRef) as ComType)
|
||||
.getData()
|
||||
.then(res => {
|
||||
state.errorList = res.errorList || [];
|
||||
if (state.errorList.length) {
|
||||
setTimeout(() => {
|
||||
unref(validatePopoverRef)?.setVisible(true);
|
||||
}, 10);
|
||||
return;
|
||||
}
|
||||
state.formData = res.formData;
|
||||
state.dataForm.formData = state.formData ? JSON.stringify(state.formData) : null;
|
||||
state.activeStep += 1;
|
||||
})
|
||||
.catch(err => {
|
||||
err.msg && createMessage.warning(err.msg);
|
||||
});
|
||||
} else {
|
||||
(unref(columnDesignRef) as ComType)
|
||||
.getData()
|
||||
.then(res => {
|
||||
state.columnData = res.columnData;
|
||||
state.appColumnData = res.appColumnData;
|
||||
state.activeStep += 1;
|
||||
})
|
||||
.catch(err => {
|
||||
err.msg && createMessage.warning(err.msg);
|
||||
});
|
||||
}
|
||||
}
|
||||
function onStepChange(current) {
|
||||
if (current == 0) updateTables();
|
||||
}
|
||||
function updateTables() {
|
||||
state.tables = generatorStore.getAllTable;
|
||||
state.mainTableFields = generatorStore.getFormItemList;
|
||||
}
|
||||
function exist() {
|
||||
let isOk = true;
|
||||
for (let i = 0; i < state.tables.length; i++) {
|
||||
const e = state.tables[i];
|
||||
if (e.typeId == '0') {
|
||||
if (!e.tableField) {
|
||||
createMessage.warning(`表${e.table}外键字段不能为空`);
|
||||
isOk = false;
|
||||
break;
|
||||
}
|
||||
if (!e.relationField) {
|
||||
createMessage.warning(`表${e.table}关联主键不能为空`);
|
||||
isOk = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return isOk;
|
||||
}
|
||||
async function handleSubmit() {
|
||||
if (state.activeStep < 1) {
|
||||
const type = state.dataForm.type;
|
||||
if (!state.tables.length && (state.defaultTable.length || type == 3 || type == 4)) return createMessage.warning('请至少选择一个数据表');
|
||||
const values = await validate();
|
||||
if (!values) return;
|
||||
state.dataForm = { ...state.dataForm, ...values };
|
||||
handleRequest();
|
||||
} else if (state.activeStep === 1) {
|
||||
(unref(generatorRef) as ComType)
|
||||
.getData()
|
||||
.then(res => {
|
||||
state.errorList = res.errorList || [];
|
||||
if (state.errorList.length) {
|
||||
setTimeout(() => {
|
||||
unref(validatePopoverRef)?.setVisible(true);
|
||||
}, 10);
|
||||
return;
|
||||
}
|
||||
state.formData = res.formData;
|
||||
state.dataForm.formData = state.formData ? JSON.stringify(state.formData) : null;
|
||||
handleRequest();
|
||||
})
|
||||
.catch(err => {
|
||||
err.msg && createMessage.warning(err.msg);
|
||||
});
|
||||
} else {
|
||||
if (state.dataForm.webType == 1) return handleRequest();
|
||||
(unref(columnDesignRef) as ComType)
|
||||
.getData()
|
||||
.then(res => {
|
||||
state.columnData = res.columnData;
|
||||
state.appColumnData = res.appColumnData;
|
||||
handleRequest();
|
||||
})
|
||||
.catch(err => {
|
||||
err.msg && createMessage.warning(err.msg);
|
||||
});
|
||||
}
|
||||
}
|
||||
function handleRequest() {
|
||||
state.btnLoading = true;
|
||||
const query = {
|
||||
...state.dataForm,
|
||||
tables: JSON.stringify(state.tables),
|
||||
formData: state.formData ? JSON.stringify(state.formData) : null,
|
||||
columnData: state.columnData ? JSON.stringify(state.columnData) : null,
|
||||
appColumnData: state.appColumnData ? JSON.stringify(state.appColumnData) : null,
|
||||
};
|
||||
const formMethod = state.dataForm.id ? update : create;
|
||||
formMethod(query)
|
||||
.then(res => {
|
||||
createMessage.success(res.msg);
|
||||
state.btnLoading = false;
|
||||
state.isReload = true;
|
||||
if (!state.dataForm.id) state.dataForm.id = res.data;
|
||||
})
|
||||
.catch(() => {
|
||||
state.btnLoading = false;
|
||||
});
|
||||
}
|
||||
function handleCancel() {
|
||||
closeModal();
|
||||
if (state.isReload) emit('reload');
|
||||
}
|
||||
function handleSelect(id) {
|
||||
unref(generatorRef)?.setTabActiveKey(id == 'formAttr' ? 'form' : 'field');
|
||||
unref(generatorRef)?.activeFormItemById(id);
|
||||
}
|
||||
function showValidatePopover(list) {
|
||||
state.errorList = list || [];
|
||||
if (state.errorList.length) {
|
||||
setTimeout(() => {
|
||||
unref(validatePopoverRef)?.setVisible(true);
|
||||
}, 10);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
387
src/views/onlineDev/webDesign/ViewForm.vue
Normal file
387
src/views/onlineDev/webDesign/ViewForm.vue
Normal file
@@ -0,0 +1,387 @@
|
||||
<template>
|
||||
<BasicModal
|
||||
v-bind="$attrs"
|
||||
@register="registerModal"
|
||||
defaultFullscreen
|
||||
:footer="null"
|
||||
:closable="false"
|
||||
:keyboard="false"
|
||||
class="yunzhupaas-full-modal full-modal designer-modal">
|
||||
<template #title>
|
||||
<div class="yunzhupaas-full-modal-header">
|
||||
<div class="header-title">
|
||||
<img src="@/assets/images/yunzhupaas.png" class="header-logo" />
|
||||
<span class="header-dot"></span>
|
||||
<p class="header-txt" v-if="!activeStep">在线开发</p>
|
||||
<a-tooltip :title="dataForm.fullName" v-else>
|
||||
<p class="header-txt">{{ dataForm.fullName }}</p>
|
||||
</a-tooltip>
|
||||
</div>
|
||||
<a-steps v-model:current="activeStep" type="navigation" size="small" class="header-steps">
|
||||
<a-step title="基础设计" />
|
||||
<a-step title="列表设计" disabled />
|
||||
</a-steps>
|
||||
<a-space class="options" :size="10">
|
||||
<a-space-compact block>
|
||||
<a-button shape="round" @click="handlePrev" :disabled="activeStep <= 0 || btnLoading">{{ t('common.prev') }}</a-button>
|
||||
<a-button shape="round" @click="handleNext" :disabled="activeStep >= maxStep || loading || btnLoading">{{ t('common.next') }} </a-button>
|
||||
</a-space-compact>
|
||||
<a-button shape="round" type="primary" @click="handleSubmit()" :disabled="loading" :loading="btnLoading">{{ t('common.saveText') }}</a-button>
|
||||
<a-button shape="round" @click="handleCancel()">{{ t('common.closeText') }}</a-button>
|
||||
</a-space>
|
||||
</div>
|
||||
</template>
|
||||
<a-row type="flex" justify="center" align="middle" class="basic-content" v-show="!activeStep">
|
||||
<a-col :span="12" :xxl="10" class="basic-form">
|
||||
<BasicForm @register="registerForm">
|
||||
<template #interfaceId="{ model, field }">
|
||||
<interface-modal :value="model[field]" :title="dataForm.interfaceName" :sourceType="2" @change="onInterfaceChange" />
|
||||
</template>
|
||||
</BasicForm>
|
||||
<a-table :data-source="interfaceParam" :columns="templateJsonColumns" size="small" :pagination="false" v-if="interfaceParam && interfaceParam.length">
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'field'">
|
||||
<span class="required-sign">{{ record.required ? '*' : '' }}</span>
|
||||
{{ record.field }}{{ record.fieldName ? '(' + record.fieldName + ')' : '' }}
|
||||
</template>
|
||||
<template v-if="column.key === 'sourceType'">
|
||||
<yunzhupaas-select
|
||||
v-model:value="record.sourceType"
|
||||
:options="getSourceTypeOptions(record.required)"
|
||||
class="!w-100px"
|
||||
:disabled="record.disabled"
|
||||
@change="onSourceTypeChange(record)" />
|
||||
</template>
|
||||
<template v-if="column.key === 'relationField'">
|
||||
<template v-if="record.sourceType == 2">
|
||||
<yunzhupaas-input-number
|
||||
v-model:value="record.relationField"
|
||||
placeholder="请输入"
|
||||
allowClear
|
||||
:disabled="record.disabled"
|
||||
v-if="record.dataType === 'int' || record.dataType === 'decimal'" />
|
||||
<yunzhupaas-date-picker
|
||||
class="!w-full"
|
||||
v-model:value="record.relationField"
|
||||
placeholder="请选择"
|
||||
format="yyyy-MM-dd HH:mm:ss"
|
||||
allowClear
|
||||
:disabled="record.disabled"
|
||||
v-else-if="record.dataType === 'datetime'" />
|
||||
<a-input v-model:value="record.relationField" placeholder="请输入" allowClear :disabled="record.disabled" v-else />
|
||||
</template>
|
||||
<yunzhupaas-select
|
||||
v-model:value="record.relationField"
|
||||
placeholder="请选择"
|
||||
:options="systemFieldsOptions"
|
||||
:fieldNames="{ options: 'options1' }"
|
||||
allowClear
|
||||
class="!w-161px"
|
||||
:disabled="record.disabled"
|
||||
v-else-if="record.sourceType === 4" />
|
||||
</template>
|
||||
<template v-if="column.key === 'useSearch'">
|
||||
<a-checkbox v-model:checked="record.useSearch" @change="onUseSearchChange($event, record)" />
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<BasicColumnDesign
|
||||
ref="columnDesignRef"
|
||||
:columnData="columnData"
|
||||
:appColumnData="appColumnData"
|
||||
:formInfo="dataForm"
|
||||
:viewFields="viewFields"
|
||||
:interfaceParam="interfaceParam"
|
||||
:interfaceHasPage="interfaceHasPage"
|
||||
v-if="activeStep == 1" />
|
||||
</BasicModal>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { getInfo, create, update } from '@/api/onlineDev/visualDev';
|
||||
import { ref, reactive, toRefs, unref } from 'vue';
|
||||
import { BasicModal, useModalInner } from '@/components/Modal';
|
||||
import { BasicForm, useForm } from '@/components/Form';
|
||||
import { useMessage } from '@/hooks/web/useMessage';
|
||||
import { useI18n } from '@/hooks/web/useI18n';
|
||||
import formValidate from '@/utils/formValidate';
|
||||
import { BasicColumnDesign } from '@/components/ColumnDesign';
|
||||
import { InterfaceModal } from '@/components/CommonModal';
|
||||
import { getDataInterfaceInfo } from '@/api/systemData/dataInterface';
|
||||
import { sourceTypeOptions, interfaceSystemOptions } from '@/components/FlowProcess/src/helper/define';
|
||||
|
||||
interface State {
|
||||
activeStep: number;
|
||||
maxStep: number;
|
||||
loading: boolean;
|
||||
btnLoading: boolean;
|
||||
tables: any[];
|
||||
defaultTable: any[];
|
||||
dataForm: Recordable;
|
||||
viewFields: any[];
|
||||
interfaceParam: any[];
|
||||
interfaceHasPage: number;
|
||||
isReload: boolean;
|
||||
[prop: string]: any;
|
||||
}
|
||||
interface ComType {
|
||||
getData: () => any;
|
||||
}
|
||||
|
||||
const emit = defineEmits(['register', 'reload']);
|
||||
const [registerForm, { setFieldsValue, resetFields, validate, updateSchema, clearValidate }] = useForm({
|
||||
schemas: [
|
||||
{
|
||||
field: 'fullName',
|
||||
label: '视图名称',
|
||||
component: 'Input',
|
||||
componentProps: { placeholder: '请输入', maxlength: 100 },
|
||||
rules: [{ required: true, trigger: 'blur', message: '必填' }],
|
||||
},
|
||||
{
|
||||
field: 'enCode',
|
||||
label: '视图编码',
|
||||
component: 'Input',
|
||||
componentProps: { placeholder: '请输入', maxlength: 50 },
|
||||
rules: [
|
||||
{ required: true, trigger: 'blur', message: '必填' },
|
||||
{ validator: formValidate('enCode'), trigger: 'blur' },
|
||||
],
|
||||
},
|
||||
{
|
||||
field: 'category',
|
||||
label: '视图分类',
|
||||
component: 'Select',
|
||||
componentProps: { placeholder: '请选择', showSearch: true },
|
||||
rules: [{ required: true, trigger: 'change', message: '必填' }],
|
||||
},
|
||||
{
|
||||
field: 'sortCode',
|
||||
label: '视图排序',
|
||||
defaultValue: 0,
|
||||
component: 'InputNumber',
|
||||
componentProps: { min: 0, max: 999999 },
|
||||
},
|
||||
{
|
||||
field: 'description',
|
||||
label: '视图说明',
|
||||
component: 'Textarea',
|
||||
componentProps: { placeholder: '请输入' },
|
||||
},
|
||||
{
|
||||
field: 'interfaceId',
|
||||
label: '数据接口',
|
||||
slot: 'interfaceId',
|
||||
component: 'Select',
|
||||
rules: [{ required: true, trigger: 'change', message: '必填' }],
|
||||
},
|
||||
],
|
||||
});
|
||||
const [registerModal, { closeModal, changeLoading }] = useModalInner(init);
|
||||
const { createMessage } = useMessage();
|
||||
const { t } = useI18n();
|
||||
const state = reactive<State>({
|
||||
activeStep: 0,
|
||||
maxStep: 1,
|
||||
loading: false,
|
||||
btnLoading: false,
|
||||
tables: [],
|
||||
defaultTable: [],
|
||||
dataForm: {
|
||||
id: '',
|
||||
fullName: '',
|
||||
enCode: '',
|
||||
type: 1,
|
||||
webType: 4,
|
||||
dbLinkId: '0',
|
||||
sortCode: 0,
|
||||
state: 1,
|
||||
category: '',
|
||||
description: '',
|
||||
tables: '',
|
||||
interfaceId: '',
|
||||
interfaceName: '',
|
||||
interfaceParam: '',
|
||||
},
|
||||
formData: null,
|
||||
columnData: null,
|
||||
appColumnData: null,
|
||||
interfaceParam: [],
|
||||
viewFields: [],
|
||||
interfaceHasPage: 0,
|
||||
isReload: false,
|
||||
});
|
||||
const columnDesignRef = ref<Nullable<ComType>>(null);
|
||||
const { activeStep, maxStep, loading, btnLoading, columnData, appColumnData, dataForm, interfaceParam, viewFields, interfaceHasPage } = toRefs(state);
|
||||
const templateJsonColumns = [
|
||||
{ title: '序号', width: 50, align: 'center', customRender: ({ index }) => index + 1 },
|
||||
{ title: '参数名称', dataIndex: 'field', key: 'field' },
|
||||
{ title: '参数来源', dataIndex: 'sourceType', key: 'sourceType', width: 100 },
|
||||
{ title: '参数值', dataIndex: 'relationField', key: 'relationField', width: 180 },
|
||||
{ title: '作为查询字段', dataIndex: 'useSearch', key: 'useSearch', width: 110, align: 'center' },
|
||||
];
|
||||
const systemFieldsOptions = interfaceSystemOptions.filter(o => o.id != '@formId');
|
||||
function init(data) {
|
||||
state.isReload = false;
|
||||
state.activeStep = 0;
|
||||
state.loading = true;
|
||||
state.tables = [];
|
||||
state.defaultTable = [];
|
||||
state.interfaceParam = [];
|
||||
state.formData = null;
|
||||
state.columnData = null;
|
||||
state.appColumnData = null;
|
||||
state.dataForm.interfaceId = '';
|
||||
state.dataForm.interfaceName = '';
|
||||
state.dataForm.interfaceParam = '';
|
||||
updateSchema([{ field: 'category', componentProps: { options: data.categoryList } }]);
|
||||
changeLoading(true);
|
||||
resetFields();
|
||||
state.dataForm.id = data.id;
|
||||
if (state.dataForm.id) {
|
||||
getInfo(state.dataForm.id).then(res => {
|
||||
state.dataForm = res.data;
|
||||
state.maxStep = state.dataForm.webType == 4 ? 1 : 2;
|
||||
setFieldsValue(state.dataForm);
|
||||
state.formData = state.dataForm.formData && JSON.parse(state.dataForm.formData);
|
||||
state.columnData = state.dataForm.columnData && JSON.parse(state.dataForm.columnData);
|
||||
state.appColumnData = state.dataForm.appColumnData && JSON.parse(state.dataForm.appColumnData);
|
||||
state.tables = (state.dataForm.tables && JSON.parse(state.dataForm.tables)) || [];
|
||||
state.defaultTable = (state.dataForm.tables && JSON.parse(state.dataForm.tables)) || [];
|
||||
state.interfaceParam = state.dataForm.interfaceParam ? JSON.parse(state.dataForm.interfaceParam) : [];
|
||||
if (state.dataForm.interfaceId) initInterface();
|
||||
state.loading = false;
|
||||
changeLoading(false);
|
||||
});
|
||||
} else {
|
||||
state.dataForm.type = data.type;
|
||||
state.dataForm.webType = 4;
|
||||
state.maxStep = state.dataForm.webType == 4 ? 1 : 2;
|
||||
state.loading = false;
|
||||
changeLoading(false);
|
||||
}
|
||||
}
|
||||
function initInterface() {
|
||||
changeLoading(true);
|
||||
getDataInterfaceInfo(state.dataForm.interfaceId).then(res => {
|
||||
const data = res.data;
|
||||
state.dataForm.interfaceName = data.fullName;
|
||||
state.viewFields = data.fieldJson ? JSON.parse(data.fieldJson) : [];
|
||||
state.interfaceHasPage = data.hasPage;
|
||||
const parameterJson = (data.parameterJson ? JSON.parse(data.parameterJson) : []).map(o => ({ ...o, useSearch: false, disabled: false }));
|
||||
for (let i = 0; i < parameterJson.length; i++) {
|
||||
const findIndex = state.interfaceParam.findIndex(o => o.field === parameterJson[i].field);
|
||||
if (findIndex > -1) parameterJson[i] = state.interfaceParam[findIndex];
|
||||
}
|
||||
state.interfaceParam = parameterJson;
|
||||
changeLoading(false);
|
||||
});
|
||||
}
|
||||
function onInterfaceChange(val, row) {
|
||||
state.viewFields = [];
|
||||
if (!val) {
|
||||
state.dataForm.interfaceId = '';
|
||||
state.dataForm.interfaceName = '';
|
||||
state.interfaceParam = [];
|
||||
state.dataForm.interfaceParam = '';
|
||||
state.interfaceHasPage = 0;
|
||||
setFieldsValue({
|
||||
interfaceId: state.dataForm.interfaceId,
|
||||
interfaceName: state.dataForm.interfaceName,
|
||||
interfaceParam: state.dataForm.interfaceParam,
|
||||
});
|
||||
return;
|
||||
}
|
||||
state.viewFields = row.fieldJson ? JSON.parse(row.fieldJson) : [];
|
||||
state.interfaceHasPage = row.hasPage;
|
||||
if (state.dataForm.interfaceId === val) return;
|
||||
state.dataForm.interfaceId = val;
|
||||
state.dataForm.interfaceName = row.fullName;
|
||||
state.interfaceParam = (row.parameterJson ? JSON.parse(row.parameterJson) : []).map(o => ({
|
||||
...o,
|
||||
useSearch: false,
|
||||
sourceType: 2,
|
||||
relationField: '',
|
||||
disabled: false,
|
||||
}));
|
||||
state.dataForm.interfaceParam = JSON.stringify(state.interfaceParam);
|
||||
setFieldsValue({
|
||||
interfaceId: state.dataForm.interfaceId,
|
||||
interfaceName: state.dataForm.interfaceName,
|
||||
interfaceParam: state.dataForm.interfaceParam,
|
||||
});
|
||||
setTimeout(() => {
|
||||
clearValidate('interfaceId');
|
||||
}, 0);
|
||||
}
|
||||
function handlePrev() {
|
||||
state.activeStep -= 1;
|
||||
}
|
||||
async function handleNext() {
|
||||
if (state.activeStep < 1) {
|
||||
const values = await validate();
|
||||
if (!values) return;
|
||||
if (!state.viewFields.length) return createMessage.error('请先设置数据接口的字段列表!');
|
||||
state.dataForm = { ...state.dataForm, ...values };
|
||||
state.activeStep += 1;
|
||||
}
|
||||
}
|
||||
async function handleSubmit() {
|
||||
if (state.activeStep < 1) {
|
||||
const values = await validate();
|
||||
if (!values) return;
|
||||
if (!state.viewFields.length) return createMessage.error('请先设置数据接口的字段列表!');
|
||||
state.dataForm = { ...state.dataForm, ...values };
|
||||
handleRequest();
|
||||
} else if (state.activeStep === 1) {
|
||||
(unref(columnDesignRef) as ComType)
|
||||
.getData()
|
||||
.then(res => {
|
||||
state.columnData = res.columnData;
|
||||
state.appColumnData = res.appColumnData;
|
||||
handleRequest();
|
||||
})
|
||||
.catch(err => {
|
||||
err.msg && createMessage.warning(err.msg);
|
||||
});
|
||||
}
|
||||
}
|
||||
function handleRequest() {
|
||||
state.btnLoading = true;
|
||||
const query = {
|
||||
...state.dataForm,
|
||||
tables: JSON.stringify(state.tables),
|
||||
formData: state.formData ? JSON.stringify(state.formData) : null,
|
||||
columnData: state.columnData ? JSON.stringify(state.columnData) : null,
|
||||
appColumnData: state.appColumnData ? JSON.stringify(state.appColumnData) : null,
|
||||
interfaceParam: JSON.stringify(state.interfaceParam),
|
||||
};
|
||||
const formMethod = state.dataForm.id ? update : create;
|
||||
formMethod(query)
|
||||
.then(res => {
|
||||
createMessage.success(res.msg);
|
||||
state.btnLoading = false;
|
||||
state.isReload = true;
|
||||
if (!state.dataForm.id) state.dataForm.id = res.data;
|
||||
})
|
||||
.catch(() => {
|
||||
state.btnLoading = false;
|
||||
});
|
||||
}
|
||||
function onUseSearchChange(e, record) {
|
||||
record.sourceType = 2;
|
||||
record.relationField = undefined;
|
||||
record.disabled = e.target.checked;
|
||||
}
|
||||
function onSourceTypeChange(record) {
|
||||
record.relationField = record.sourceType == 4 ? systemFieldsOptions[0]?.id : '';
|
||||
}
|
||||
function getSourceTypeOptions(isRequired) {
|
||||
return isRequired ? sourceTypeOptions.filter(o => o.id != 3 && o.id != 1) : sourceTypeOptions.filter(o => o.id != 1);
|
||||
}
|
||||
function handleCancel() {
|
||||
closeModal();
|
||||
if (state.isReload) emit('reload');
|
||||
}
|
||||
</script>
|
||||
142
src/views/onlineDev/webDesign/components/AiCreateModal.vue
Normal file
142
src/views/onlineDev/webDesign/components/AiCreateModal.vue
Normal file
@@ -0,0 +1,142 @@
|
||||
<template>
|
||||
<BasicModal v-bind="getBindValue" @register="registerModal" @ok="handleSubmit" destroyOnClose>
|
||||
<div class="!pb-35px">
|
||||
<div class="text-tips">通过AI生成表单 ,根据文字描述智能生成推荐表单</div>
|
||||
<div class="ai-textarea">
|
||||
<yunzhupaas-textarea v-model:value="content" placeholder="输入你的需求描述" :maxlength="100" />
|
||||
<div class="ai-button">
|
||||
<loading-outlined class="mr-5px" v-if="loading" />
|
||||
<i class="ym-custom ym-custom-send cursor-pointer" :class="{ 'icon-selected': content }" v-else @click="handleAiSubmit" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<a-form :colon="false" :labelCol="{ style: { width: '77px' } }" :model="dataForm" :rules="rules" ref="formElRef" v-if="showForm">
|
||||
<div class="text-tips">表单已生成,请继续确认表单名称、编码及分类</div>
|
||||
<a-form-item label="表单名称" name="fullName">
|
||||
<yunzhupaas-input v-model:value="dataForm.fullName" placeholder="请输入" :maxlength="100" />
|
||||
</a-form-item>
|
||||
<a-form-item label="表单编码" name="enCode">
|
||||
<yunzhupaas-input v-model:value="dataForm.enCode" placeholder="请输入" :maxlength="50" />
|
||||
</a-form-item>
|
||||
<a-form-item label="表单分类" name="category">
|
||||
<yunzhupaas-select v-model:value="dataForm.category" :options="categoryOptions" showSearch />
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</BasicModal>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { reactive, ref, toRefs, nextTick, computed, unref } from 'vue';
|
||||
import { getAiInfo, create } from '@/api/onlineDev/visualDev';
|
||||
import { BasicModal, useModalInner } from '@/components/Modal';
|
||||
import type { FormInstance } from 'ant-design-vue';
|
||||
import { LoadingOutlined } from '@ant-design/icons-vue';
|
||||
import { useAttrs } from '@/hooks/core/useAttrs';
|
||||
import formValidate from '@/utils/formValidate';
|
||||
import { buildAiFormData } from '@/components/FormGenerator/src/helper/aiUtils';
|
||||
import { omit } from 'lodash-es';
|
||||
import { useMessage } from '@/hooks/web/useMessage';
|
||||
|
||||
interface State {
|
||||
dataForm: any;
|
||||
content: string;
|
||||
loading: boolean;
|
||||
categoryOptions: any[];
|
||||
showForm: boolean;
|
||||
}
|
||||
|
||||
const emit = defineEmits(['register', 'reload']);
|
||||
const { createMessage } = useMessage();
|
||||
const [registerModal, { changeOkLoading, closeModal }] = useModalInner(init);
|
||||
const formElRef = ref<FormInstance>();
|
||||
const state = reactive<State>({
|
||||
dataForm: {},
|
||||
content: '',
|
||||
loading: false,
|
||||
categoryOptions: [],
|
||||
showForm: false,
|
||||
});
|
||||
const { dataForm, content, loading, categoryOptions, showForm } = toRefs(state);
|
||||
const attrs = useAttrs({ excludeDefaultKeys: false });
|
||||
const rules = {
|
||||
fullName: [{ required: true, message: '必填', trigger: 'blur' }],
|
||||
enCode: [
|
||||
{ required: true, message: '必填', trigger: 'blur' },
|
||||
{ validator: formValidate('enCode'), trigger: 'blur' },
|
||||
],
|
||||
category: [{ required: true, message: '必填', trigger: 'change' }],
|
||||
};
|
||||
|
||||
const getBindValue = computed(() => {
|
||||
const attr: any = {
|
||||
...unref(attrs),
|
||||
title: 'AI建表',
|
||||
okButtonProps: {
|
||||
disabled: state.loading,
|
||||
},
|
||||
};
|
||||
if (!state.showForm) attr.footer = null;
|
||||
return attr;
|
||||
});
|
||||
|
||||
function init(data) {
|
||||
state.categoryOptions = data.categoryList || [];
|
||||
resetData();
|
||||
nextTick(() => {
|
||||
formElRef.value?.clearValidate();
|
||||
});
|
||||
}
|
||||
function resetData() {
|
||||
state.content = '';
|
||||
state.loading = false;
|
||||
state.showForm = false;
|
||||
state.dataForm = {};
|
||||
}
|
||||
function handleAiSubmit() {
|
||||
if (!state.content) return;
|
||||
state.loading = true;
|
||||
getAiInfo({ keyword: state.content })
|
||||
.then(res => {
|
||||
state.showForm = true;
|
||||
state.loading = false;
|
||||
state.dataForm = { ...res.data, category: state.dataForm.category };
|
||||
})
|
||||
.catch(() => {
|
||||
state.loading = false;
|
||||
});
|
||||
}
|
||||
async function handleSubmit() {
|
||||
try {
|
||||
const values = await formElRef.value?.validate();
|
||||
if (!values) return;
|
||||
state.dataForm = { ...buildAiFormData(state.dataForm.aiModelList || []), ...omit(state.dataForm, ['aiModelList']) };
|
||||
changeOkLoading(true);
|
||||
create(state.dataForm)
|
||||
.then(res => {
|
||||
changeOkLoading(false);
|
||||
closeModal();
|
||||
createMessage.success(res.msg);
|
||||
emit('reload', res.data);
|
||||
})
|
||||
.catch(() => {
|
||||
changeOkLoading(false);
|
||||
});
|
||||
} catch (_) {}
|
||||
}
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.ai-textarea {
|
||||
position: relative;
|
||||
.ai-button {
|
||||
position: absolute;
|
||||
bottom: 3px;
|
||||
right: 8px;
|
||||
}
|
||||
}
|
||||
.icon-selected {
|
||||
color: @primary-color!important;
|
||||
}
|
||||
.text-tips {
|
||||
margin: 10px 20px 20px 0;
|
||||
color: @text-color-label;
|
||||
}
|
||||
</style>
|
||||
169
src/views/onlineDev/webDesign/components/AliasModal.vue
Normal file
169
src/views/onlineDev/webDesign/components/AliasModal.vue
Normal file
@@ -0,0 +1,169 @@
|
||||
<template>
|
||||
<BasicModal
|
||||
v-bind="$attrs"
|
||||
@register="registerModal"
|
||||
defaultFullscreen
|
||||
:footer="null"
|
||||
:closable="false"
|
||||
:keyboard="false"
|
||||
class="yunzhupaas-full-modal full-modal designer-modal">
|
||||
<template #title>
|
||||
<div class="yunzhupaas-full-modal-header">
|
||||
<div class="header-title">
|
||||
<img src="@/assets/images/yunzhupaas.png" class="header-logo" />
|
||||
<span class="header-dot"></span>
|
||||
<a-tooltip :title="fullName">
|
||||
<p class="header-txt">{{ fullName }}</p>
|
||||
</a-tooltip>
|
||||
</div>
|
||||
<a-steps :current="1" type="navigation" size="small" class="header-steps tab-steps">
|
||||
<a-step title="命名规范" />
|
||||
</a-steps>
|
||||
<a-space class="options" :size="10">
|
||||
<a-button type="primary" @click="handleSubmit()" :disabled="loading" :loading="btnLoading">{{ t('common.saveText') }}</a-button>
|
||||
<a-button @click="closeModal()">{{ t('common.closeText') }}</a-button>
|
||||
</a-space>
|
||||
</div>
|
||||
</template>
|
||||
<div class="yunzhupaas-content-wrapper">
|
||||
<div class="yunzhupaas-content-wrapper-center">
|
||||
<div class="yunzhupaas-content-wrapper-content bg-white p-10px">
|
||||
<a-alert :message="tipTxt" :description="descTxt" type="warning" show-icon class="mb-10px" ref="alertElRef" />
|
||||
<a-table :data-source="list" v-bind="getTableBindValues" v-model:expandedRowKeys="expandedRowKeys" ref="tableElRef">
|
||||
<template #expandedRowRender="{ record }">
|
||||
<a-table :data-source="record.fields" v-bind="getChildTableBindValues">
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'field'">
|
||||
<a-input v-model:value="record.field" disabled detailed />
|
||||
</template>
|
||||
<template v-if="column.key === 'aliasName'">
|
||||
<a-input v-model:value="record.aliasName" placeholder="请输入" :maxlength="100" />
|
||||
</template>
|
||||
<template v-if="column.key === 'fieldName'">
|
||||
<a-input v-model:value="record.fieldName" disabled detailed />
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
</template>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'table'">
|
||||
<a-input v-model:value="record.table" disabled detailed />
|
||||
</template>
|
||||
<template v-if="column.key === 'aliasName'">
|
||||
<a-input v-model:value="record.aliasName" placeholder="请输入" :maxlength="100" />
|
||||
</template>
|
||||
<template v-if="column.key === 'comment'">
|
||||
<a-input v-model:value="record.comment" disabled detailed />
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</BasicModal>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { getAliasInfo, saveAlias } from '@/api/onlineDev/visualDev';
|
||||
import { ref, reactive, toRefs, computed, unref, nextTick } from 'vue';
|
||||
import { BasicModal, useModalInner } from '@/components/Modal';
|
||||
import { useI18n } from '@/hooks/web/useI18n';
|
||||
import { useMessage } from '@/hooks/web/useMessage';
|
||||
|
||||
interface State {
|
||||
id: string;
|
||||
fullName: string;
|
||||
list: any[];
|
||||
loading: boolean;
|
||||
btnLoading: boolean;
|
||||
expandedRowKeys: string[];
|
||||
}
|
||||
|
||||
defineEmits(['register']);
|
||||
const tipTxt = '规范名称命名规则:(1)只能由字母、数字、下划线组成,且不能以数字开头;(2)不能使用系统关键字或开发语言关键字';
|
||||
const descTxt =
|
||||
'系统关键字包含:tenantid、id、foreignid、flowid、flowtaskid、deleteuserid、deletetime、deletemark、version、tenant_id、foreign_id、flow_id、flow_task_id、delete_user_id、delete_time、delete_mark、f_tenant_id、f_id、f_foreign_id、f_flow_id、f_flow_task_id、f_delete_user_id、f_delete_time、f_delete_mark、f_version';
|
||||
const { t } = useI18n();
|
||||
const { createMessage } = useMessage();
|
||||
const [registerModal, { closeModal, changeLoading }] = useModalInner(init);
|
||||
const alertElRef = ref<any>(null);
|
||||
const tableElRef = ref<any>(null);
|
||||
const columns = [
|
||||
{ width: 50, title: '序号', dataIndex: 'index', key: 'index', align: 'center', customRender: ({ index }) => index + 1 },
|
||||
{ title: '表名', dataIndex: 'table', key: 'table' },
|
||||
{ title: '规范名称', dataIndex: 'aliasName', key: 'aliasName' },
|
||||
{ title: '说明', dataIndex: 'comment', key: 'comment' },
|
||||
];
|
||||
const childColumns = [
|
||||
{ width: 50, title: '序号', dataIndex: 'index', key: 'index', align: 'center', customRender: ({ index }) => index + 1 },
|
||||
{ title: '字段', dataIndex: 'field', key: 'field' },
|
||||
{ title: '规范名称', dataIndex: 'aliasName', key: 'aliasName' },
|
||||
{ title: '说明', dataIndex: 'fieldName', key: 'fieldName' },
|
||||
];
|
||||
const state = reactive<State>({
|
||||
id: '',
|
||||
fullName: '',
|
||||
list: [],
|
||||
loading: false,
|
||||
btnLoading: false,
|
||||
expandedRowKeys: [],
|
||||
});
|
||||
const { list, loading, btnLoading, expandedRowKeys, fullName } = toRefs(state);
|
||||
|
||||
const getHeaderHeight = computed(() => {
|
||||
const alertEl = alertElRef.value?.$el;
|
||||
if (!alertEl) return 120;
|
||||
return alertEl?.offsetHeight;
|
||||
});
|
||||
const getScrollY = computed(() => window.innerHeight - unref(getHeaderHeight) - 150);
|
||||
const getTableBindValues = computed(() => {
|
||||
return {
|
||||
columns,
|
||||
pagination: false,
|
||||
size: 'small',
|
||||
rowKey: 'table',
|
||||
scroll: { y: unref(getScrollY) },
|
||||
};
|
||||
});
|
||||
const getChildTableBindValues = computed(() => {
|
||||
return {
|
||||
columns: childColumns,
|
||||
pagination: false,
|
||||
size: 'small',
|
||||
rowKey: 'field',
|
||||
};
|
||||
});
|
||||
|
||||
function init(data) {
|
||||
state.id = data.id;
|
||||
state.fullName = data.fullName || '';
|
||||
state.loading = true;
|
||||
state.expandedRowKeys = [];
|
||||
changeLoading(true);
|
||||
getAliasInfo(data.id).then(res => {
|
||||
state.list = res.data;
|
||||
state.expandedRowKeys = res.data.map(e => e.table);
|
||||
nextTick(() => {
|
||||
const tableEl = tableElRef.value?.$el;
|
||||
let bodyEl = tableEl.querySelector('.ant-table-body');
|
||||
bodyEl!.style.height = `${unref(getScrollY)}px`;
|
||||
changeLoading(false);
|
||||
state.loading = false;
|
||||
});
|
||||
});
|
||||
}
|
||||
function handleSubmit() {
|
||||
state.btnLoading = true;
|
||||
changeLoading(true);
|
||||
saveAlias(state.id, { tableList: state.list })
|
||||
.then(res => {
|
||||
state.btnLoading = false;
|
||||
changeLoading(false);
|
||||
createMessage.success(res.msg);
|
||||
closeModal();
|
||||
})
|
||||
.catch(() => {
|
||||
state.btnLoading = false;
|
||||
changeLoading(false);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
173
src/views/onlineDev/webDesign/components/CreateMenuModal.vue
Normal file
173
src/views/onlineDev/webDesign/components/CreateMenuModal.vue
Normal file
@@ -0,0 +1,173 @@
|
||||
<template>
|
||||
<BasicModal v-bind="$attrs" @register="registerModal" title="生成菜单" @ok="handleSubmit" class="yunzhupaas-release-modal">
|
||||
<a-alert message="将该功能的按钮、列表、表单及数据权限发布至应用菜单" type="warning" show-icon />
|
||||
<a-form class="release-main" :colon="false" :model="dataForm" :rules="rules" layout="vertical" ref="formElRef">
|
||||
<div class="release-item">
|
||||
<a-form-item>
|
||||
<div class="top-item" :class="{ active: dataForm.pc === 1 }" @click="selectToggle('pc')">
|
||||
<i class="item-icon icon-ym icon-ym-pc"></i>
|
||||
<p class="item-title">桌面端</p>
|
||||
<div class="icon-checked">
|
||||
<check-outlined />
|
||||
</div>
|
||||
</div>
|
||||
</a-form-item>
|
||||
<a-form-item label="上级" name="pcModuleParentId" v-if="dataForm.pc">
|
||||
<YunzhupaasTreeSelect
|
||||
v-model:value="pcModuleParentId"
|
||||
:options="treeData"
|
||||
treeCheckStrictly
|
||||
multiple
|
||||
:dropdownMatchSelectWidth="false"
|
||||
@change="onPcChange" />
|
||||
</a-form-item>
|
||||
<a-form-item label="已发布菜单路径" v-if="record.pcIsRelease">
|
||||
<div class="released">{{ record.pcReleaseName }}</div>
|
||||
</a-form-item>
|
||||
</div>
|
||||
<div class="release-item">
|
||||
<a-form-item>
|
||||
<div class="top-item" :class="{ active: dataForm.app === 1 }" @click="selectToggle('app')">
|
||||
<i class="item-icon icon-ym icon-ym-mobile"></i>
|
||||
<p class="item-title">移动端</p>
|
||||
<div class="icon-checked">
|
||||
<check-outlined />
|
||||
</div>
|
||||
</div>
|
||||
</a-form-item>
|
||||
<a-form-item label="上级" name="appModuleParentId" v-if="dataForm.app">
|
||||
<YunzhupaasTreeSelect
|
||||
v-model:value="appModuleParentId"
|
||||
:options="appTreeData"
|
||||
treeCheckStrictly
|
||||
multiple
|
||||
:dropdownMatchSelectWidth="false"
|
||||
@change="onAppChange" />
|
||||
</a-form-item>
|
||||
<a-form-item label="已发布菜单路径" v-if="record.appIsRelease">
|
||||
<div class="released">{{ record.appReleaseName }}</div>
|
||||
</a-form-item>
|
||||
</div>
|
||||
</a-form>
|
||||
</BasicModal>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { getReleaseMenu, createMenu } from '@/api/onlineDev/visualDev';
|
||||
import { BasicModal, useModalInner } from '@/components/Modal';
|
||||
import { ref, reactive, toRefs, computed } from 'vue';
|
||||
import type { FormInstance } from 'ant-design-vue';
|
||||
import { CheckOutlined } from '@ant-design/icons-vue';
|
||||
import { getMenuSelectorFilter } from '@/api/system/menu';
|
||||
import { useMessage } from '@/hooks/web/useMessage';
|
||||
import { useI18n } from '@/hooks/web/useI18n';
|
||||
|
||||
interface State {
|
||||
dataForm: any;
|
||||
record: any;
|
||||
treeData: any[];
|
||||
appTreeData: any[];
|
||||
pcModuleParentId: any[];
|
||||
appModuleParentId: any[];
|
||||
}
|
||||
|
||||
const emit = defineEmits(['register', 'reload']);
|
||||
const { createMessage, createConfirm } = useMessage();
|
||||
const { t } = useI18n();
|
||||
const [registerModal, { changeOkLoading, closeModal }] = useModalInner(init);
|
||||
const formElRef = ref<FormInstance>();
|
||||
const state = reactive<State>({
|
||||
dataForm: {
|
||||
pc: 1,
|
||||
app: 1,
|
||||
pcModuleParentId: [],
|
||||
appModuleParentId: [],
|
||||
},
|
||||
record: {},
|
||||
treeData: [],
|
||||
appTreeData: [],
|
||||
pcModuleParentId: [],
|
||||
appModuleParentId: [],
|
||||
});
|
||||
const { dataForm, record, treeData, appTreeData, pcModuleParentId, appModuleParentId } = toRefs(state);
|
||||
|
||||
const rules = computed(() => {
|
||||
let rules: any = {
|
||||
pcModuleParentId: [],
|
||||
appModuleParentId: [],
|
||||
};
|
||||
if (!state.record.pcIsRelease) rules.pcModuleParentId = [{ required: true, message: '必填', trigger: 'change', type: 'array' }];
|
||||
if (!state.record.appIsRelease) rules.appModuleParentId = [{ required: true, message: '必填', trigger: 'change', type: 'array' }];
|
||||
return rules;
|
||||
});
|
||||
|
||||
function init(data) {
|
||||
state.pcModuleParentId = [];
|
||||
state.appModuleParentId = [];
|
||||
getReleaseMenu(data.id).then(res => {
|
||||
state.record = res.data;
|
||||
const platformRelease = res.data.platformRelease ? JSON.parse(res.data.platformRelease) : {};
|
||||
state.dataForm = {
|
||||
pc: platformRelease.pc === 0 ? 0 : 1,
|
||||
app: platformRelease.app === 0 ? 0 : 1,
|
||||
pcModuleParentId: [],
|
||||
appModuleParentId: [],
|
||||
};
|
||||
formElRef.value?.clearValidate();
|
||||
});
|
||||
getMenuOptions(data.id);
|
||||
getAppMenuOptions(data.id);
|
||||
}
|
||||
function getMenuOptions(id) {
|
||||
getMenuSelectorFilter({ category: 'Web' }, id).then(res => {
|
||||
state.treeData = res.data.list;
|
||||
});
|
||||
}
|
||||
function getAppMenuOptions(id) {
|
||||
getMenuSelectorFilter({ category: 'App' }, id).then(res => {
|
||||
let list = res.data.list || [];
|
||||
for (let index = 0; index < list.length; index++) {
|
||||
const item = list[index];
|
||||
if (item.type == 0) item.disabled = true;
|
||||
}
|
||||
state.appTreeData = list;
|
||||
});
|
||||
}
|
||||
function onPcChange(data) {
|
||||
state.dataForm.pcModuleParentId = data.map(o => o.value);
|
||||
}
|
||||
function onAppChange(data) {
|
||||
state.dataForm.appModuleParentId = data.map(o => o.value);
|
||||
}
|
||||
function selectToggle(key) {
|
||||
state.dataForm[key] = state.dataForm[key] === 1 ? 0 : 1;
|
||||
}
|
||||
async function handleSubmit() {
|
||||
try {
|
||||
if (!state.dataForm.pc && !state.dataForm.app) return createMessage.error('请至少选择一种发布类型');
|
||||
const values = await formElRef.value?.validate();
|
||||
if (!values) return;
|
||||
const platform = { pc: state.dataForm.pc, app: state.dataForm.app };
|
||||
const query = { ...state.dataForm, platformRelease: JSON.stringify(platform) };
|
||||
const handleRelease = () => {
|
||||
changeOkLoading(true);
|
||||
createMenu(state.record.id, query)
|
||||
.then(res => {
|
||||
changeOkLoading(false);
|
||||
createMessage.success(res.msg);
|
||||
emit('reload');
|
||||
closeModal();
|
||||
})
|
||||
.catch(() => {
|
||||
changeOkLoading(false);
|
||||
});
|
||||
};
|
||||
if (!state.record.isRelease) return handleRelease();
|
||||
createConfirm({
|
||||
iconType: 'warning',
|
||||
title: t('common.tipTitle'),
|
||||
content: '发布确定后会覆盖当前线上版本且进行菜单同步,是否继续?',
|
||||
onOk: handleRelease,
|
||||
});
|
||||
} catch (_) {}
|
||||
}
|
||||
</script>
|
||||
131
src/views/onlineDev/webDesign/components/DiffPreviewModal.vue
Normal file
131
src/views/onlineDev/webDesign/components/DiffPreviewModal.vue
Normal file
@@ -0,0 +1,131 @@
|
||||
<template>
|
||||
<BasicModal
|
||||
v-bind="$attrs"
|
||||
@register="registerModal"
|
||||
defaultFullscreen
|
||||
:footer="null"
|
||||
:closable="false"
|
||||
:keyboard="false"
|
||||
class="yunzhupaas-full-modal full-modal designer-modal">
|
||||
<template #title>
|
||||
<div class="yunzhupaas-full-modal-header">
|
||||
<div class="header-title">
|
||||
<img src="@/assets/images/yunzhupaas.png" class="header-logo" />
|
||||
<span class="header-dot"></span>
|
||||
<a-tooltip :title="fullName">
|
||||
<p class="header-txt">{{ fullName }}</p>
|
||||
</a-tooltip>
|
||||
</div>
|
||||
<a-steps :current="1" type="navigation" size="small" class="header-steps tab-steps">
|
||||
<a-step title="代码预览" />
|
||||
</a-steps>
|
||||
<a-space class="options" :size="10">
|
||||
<a-button @click="closeModal()">{{ t('common.closeText') }}</a-button>
|
||||
</a-space>
|
||||
</div>
|
||||
</template>
|
||||
<div class="yunzhupaas-content-wrapper">
|
||||
<div class="yunzhupaas-content-wrapper-left">
|
||||
<BasicLeftTree :showSearch="false" ref="leftTreeRef" :fieldNames="{ title: 'fileName' }" :treeData="treeData" @select="handleTreeSelect" />
|
||||
</div>
|
||||
<div class="yunzhupaas-content-wrapper-center">
|
||||
<div class="yunzhupaas-content-wrapper-content bg-white">
|
||||
<!-- <code-diff-->
|
||||
<!-- :old-string="oldFileContent"-->
|
||||
<!-- :new-string="currentContent"-->
|
||||
<!-- output-format="side-by-side"-->
|
||||
<!-- :language="editorLanguage"-->
|
||||
<!-- :theme="getThemeColor"-->
|
||||
<!-- :context="99999"-->
|
||||
<!-- :key="key" />-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</BasicModal>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { codePreview } from '@/api/onlineDev/visualDev';
|
||||
import { reactive, toRefs, nextTick, ref, unref, computed } from 'vue';
|
||||
import { BasicModal, useModalInner } from '@/components/Modal';
|
||||
import { useI18n } from '@/hooks/web/useI18n';
|
||||
import { BasicLeftTree, TreeActionType } from '@/components/Tree';
|
||||
// import { CodeDiff } from 'v-code-diff';
|
||||
|
||||
import { useAppStore } from '@/store/modules/app';
|
||||
|
||||
interface State {
|
||||
treeData: any[];
|
||||
currentId: string;
|
||||
oldFileContent: string;
|
||||
currentContent: string;
|
||||
editorLanguage: string;
|
||||
key: number;
|
||||
fullName: string;
|
||||
}
|
||||
|
||||
defineEmits(['register']);
|
||||
const { t } = useI18n();
|
||||
const leftTreeRef = ref<Nullable<TreeActionType>>(null);
|
||||
const appStore = useAppStore();
|
||||
const [registerModal, { closeModal, changeLoading }] = useModalInner(init);
|
||||
const state = reactive<State>({
|
||||
treeData: [],
|
||||
currentId: '',
|
||||
oldFileContent: '',
|
||||
currentContent: '',
|
||||
editorLanguage: 'html',
|
||||
key: +new Date(),
|
||||
fullName: '',
|
||||
});
|
||||
const { treeData, oldFileContent, currentContent, editorLanguage, key, fullName } = toRefs(state);
|
||||
|
||||
const getThemeColor = computed(() => appStore.getDarkMode);
|
||||
|
||||
function init(data) {
|
||||
state.fullName = data.fullName || '';
|
||||
state.key = +new Date();
|
||||
changeLoading(true);
|
||||
const query = {
|
||||
module: data.module || 'system',
|
||||
description: data.description,
|
||||
modulePackageName: data.modulePackageName || '',
|
||||
enableFlow: data.enableFlow,
|
||||
contrast: true,
|
||||
};
|
||||
codePreview(data.id, query).then(res => {
|
||||
state.treeData = res.data.list.map(o => ({ ...o, disabled: true }));
|
||||
state.currentId = state.treeData[0].children[0].id;
|
||||
state.currentContent = state.treeData[0].children[0].fileContent;
|
||||
state.oldFileContent = state.treeData[0].children[0].oldFileContent;
|
||||
state.editorLanguage = getLanguage(state.treeData[0].children[0]);
|
||||
nextTick(() => {
|
||||
const leftTree = unref(leftTreeRef);
|
||||
leftTree?.setSelectedKeys([state.currentId]);
|
||||
changeLoading(false);
|
||||
});
|
||||
});
|
||||
}
|
||||
function handleTreeSelect(id, node) {
|
||||
state.key = +new Date();
|
||||
state.currentId = id;
|
||||
state.currentContent = node.fileContent;
|
||||
state.oldFileContent = node.oldFileContent;
|
||||
state.editorLanguage = getLanguage(node);
|
||||
}
|
||||
function getLanguage(node) {
|
||||
return ['web', 'app'].includes(node.fileType) ? 'html' : ['js', 'ts'].includes(node.folderName) ? 'js' : 'java';
|
||||
}
|
||||
</script>
|
||||
<style lang="less">
|
||||
.code-diff-view {
|
||||
margin-top: unset !important;
|
||||
margin-bottom: unset !important;
|
||||
height: 100% !important;
|
||||
border: unset !important;
|
||||
background-color: @component-background!important;
|
||||
.file-header,
|
||||
.empty-cell {
|
||||
background-color: @component-background!important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
117
src/views/onlineDev/webDesign/components/DownloadModal.vue
Normal file
117
src/views/onlineDev/webDesign/components/DownloadModal.vue
Normal file
@@ -0,0 +1,117 @@
|
||||
<template>
|
||||
<BasicModal v-bind="$attrs" @register="registerModal" title="输出设置" okText="下载代码" @ok="handleSubmit">
|
||||
<template #insertFooter>
|
||||
<a-space :size="10" class="float-left">
|
||||
<a-button @click="handlePreview">代码预览</a-button>
|
||||
<a-button @click="handleAlias" v-if="webType != 4">命名规范</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
<a-alert message="注意:以下只能包含数字、字母、下划线、小圆点,不能用数字开头,不能是关键字或保留字。" type="warning" showIcon class="!mb-18px" />
|
||||
<a-form :colon="false" :labelCol="{ style: { width: '100px' } }" :model="dataForm" ref="formElRef">
|
||||
<a-form-item label="模块命名" name="module" v-if="type != 3" :rules="[{ required: true, message: '必填', trigger: 'change' }]">
|
||||
<yunzhupaas-select v-model:value="dataForm.module" :options="moduleOptions" placeholder="请选择" showSearch />
|
||||
</a-form-item>
|
||||
<a-form-item name="modulePackageName" :rules="[{ required: true, message: '必填', trigger: 'blur' }]" v-if="hasPackage">
|
||||
<template #label>模块包名<BasicHelp text="修改包名需要调整controller和mapper扫描配置" /></template>
|
||||
<a-input v-model:value="dataForm.modulePackageName" placeholder="请输入" />
|
||||
</a-form-item>
|
||||
<a-form-item label="功能描述" name="description" :rules="[{ required: true, message: '必填', trigger: 'blur' }]">
|
||||
<a-input v-model:value="dataForm.description" placeholder="请输入" />
|
||||
</a-form-item>
|
||||
<a-form-item label="流程模板" name="enableFlow" v-if="webType != 4">
|
||||
<yunzhupaas-switch v-model:value="dataForm.enableFlow" />
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</BasicModal>
|
||||
<PreviewModal @register="registerPreviewModal" />
|
||||
<AliasModal @register="registerAliasModal" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { reactive, ref, toRefs, nextTick } from 'vue';
|
||||
import { downloadCode } from '@/api/onlineDev/visualDev';
|
||||
import { BasicModal, useModal, useModalInner } from '@/components/Modal';
|
||||
import type { FormInstance } from 'ant-design-vue';
|
||||
import { downloadByUrl } from '@/utils/file/download';
|
||||
import { useBaseStore } from '@/store/modules/base';
|
||||
import PreviewModal from './PreviewModal.vue';
|
||||
import AliasModal from './AliasModal.vue';
|
||||
|
||||
interface State {
|
||||
dataForm: any;
|
||||
moduleOptions: any[];
|
||||
tables: any[];
|
||||
type: number;
|
||||
id: string;
|
||||
hasPackage: boolean;
|
||||
fullName: string;
|
||||
webType: number;
|
||||
}
|
||||
|
||||
defineEmits(['register']);
|
||||
const baseStore = useBaseStore();
|
||||
const [registerModal, { changeOkLoading, closeModal }] = useModalInner(init);
|
||||
const [registerPreviewModal, { openModal: openPreviewModal }] = useModal();
|
||||
const [registerAliasModal, { openModal: openAliasModal }] = useModal();
|
||||
const formElRef = ref<FormInstance>();
|
||||
const state = reactive<State>({
|
||||
dataForm: {
|
||||
module: '',
|
||||
modulePackageName: 'yunzhupaas',
|
||||
description: '',
|
||||
enableFlow: 0,
|
||||
},
|
||||
moduleOptions: [],
|
||||
tables: [],
|
||||
type: 0,
|
||||
id: '',
|
||||
hasPackage: false,
|
||||
fullName: '',
|
||||
webType: 2,
|
||||
});
|
||||
const { dataForm, type, moduleOptions, hasPackage, webType } = toRefs(state);
|
||||
|
||||
function init(data) {
|
||||
state.tables = data.tables ? JSON.parse(data.tables) : [];
|
||||
state.id = data.id;
|
||||
state.type = data.type || 0;
|
||||
state.hasPackage = !!data.hasPackage;
|
||||
state.fullName = data.fullName || '';
|
||||
state.webType = data.webType || 2;
|
||||
state.dataForm.description = '';
|
||||
state.dataForm.enableFlow = 0;
|
||||
getOptions();
|
||||
nextTick(() => {
|
||||
formElRef.value?.clearValidate();
|
||||
if (data.webType == 4) return;
|
||||
const mainTable = state.tables.length ? state.tables.filter(o => o.typeId == '1')[0].table : '';
|
||||
state.dataForm.description = mainTable;
|
||||
});
|
||||
}
|
||||
function getOptions() {
|
||||
baseStore.getDictionaryData('createModule').then(res => {
|
||||
state.moduleOptions = res as any;
|
||||
if (state.moduleOptions.length) state.dataForm.module = state.moduleOptions[0].id;
|
||||
});
|
||||
}
|
||||
async function handleSubmit() {
|
||||
try {
|
||||
const values = await formElRef.value?.validate();
|
||||
if (!values) return;
|
||||
changeOkLoading(true);
|
||||
downloadCode(state.id, state.dataForm)
|
||||
.then(res => {
|
||||
downloadByUrl({ url: res.data.url });
|
||||
closeModal();
|
||||
})
|
||||
.catch(() => {
|
||||
changeOkLoading(false);
|
||||
});
|
||||
} catch (_) {}
|
||||
}
|
||||
function handlePreview() {
|
||||
openPreviewModal(true, { id: state.id, fullName: state.fullName, ...state.dataForm });
|
||||
}
|
||||
function handleAlias() {
|
||||
openAliasModal(true, { id: state.id, fullName: state.fullName });
|
||||
}
|
||||
</script>
|
||||
116
src/views/onlineDev/webDesign/components/PreviewModal.vue
Normal file
116
src/views/onlineDev/webDesign/components/PreviewModal.vue
Normal file
@@ -0,0 +1,116 @@
|
||||
<template>
|
||||
<BasicModal
|
||||
v-bind="$attrs"
|
||||
@register="registerModal"
|
||||
defaultFullscreen
|
||||
:footer="null"
|
||||
:closable="false"
|
||||
:keyboard="false"
|
||||
class="yunzhupaas-full-modal full-modal designer-modal">
|
||||
<template #title>
|
||||
<div class="yunzhupaas-full-modal-header">
|
||||
<div class="header-title">
|
||||
<img src="@/assets/images/yunzhupaas.png" class="header-logo" />
|
||||
<span class="header-dot"></span>
|
||||
<a-tooltip :title="fullName">
|
||||
<p class="header-txt">{{ fullName }}</p>
|
||||
</a-tooltip>
|
||||
</div>
|
||||
<a-steps :current="1" type="navigation" size="small" class="header-steps tab-steps">
|
||||
<a-step title="代码预览" />
|
||||
</a-steps>
|
||||
<a-space class="options" :size="10">
|
||||
<a-button @click="openDiffPreviewModal(true, { ...data })">代码对比</a-button>
|
||||
<a-button @click="closeModal()">{{ t('common.closeText') }}</a-button>
|
||||
</a-space>
|
||||
</div>
|
||||
</template>
|
||||
<div class="yunzhupaas-content-wrapper">
|
||||
<div class="yunzhupaas-content-wrapper-left">
|
||||
<BasicLeftTree :showSearch="false" ref="leftTreeRef" :fieldNames="{ title: 'fileName' }" :treeData="treeData" @select="handleTreeSelect" />
|
||||
</div>
|
||||
<div class="yunzhupaas-content-wrapper-center">
|
||||
<div class="yunzhupaas-content-wrapper-content bg-white">
|
||||
<MonacoEditor v-model="currentContent" :options="editorOptions" :language="editorLanguage" :key="key" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</BasicModal>
|
||||
<DiffPreviewModal @register="registerDiffPreviewModal" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { codePreview } from '@/api/onlineDev/visualDev';
|
||||
import { reactive, toRefs, nextTick, ref, unref } from 'vue';
|
||||
import { BasicModal, useModal, useModalInner } from '@/components/Modal';
|
||||
import { useI18n } from '@/hooks/web/useI18n';
|
||||
import { BasicLeftTree, TreeActionType } from '@/components/Tree';
|
||||
import { MonacoEditor } from '@/components/CodeEditor';
|
||||
import DiffPreviewModal from './DiffPreviewModal.vue';
|
||||
|
||||
interface State {
|
||||
treeData: any[];
|
||||
currentId: string;
|
||||
currentContent: string;
|
||||
editorOptions: any;
|
||||
editorLanguage: string;
|
||||
key: number;
|
||||
fullName: string;
|
||||
data: any;
|
||||
}
|
||||
|
||||
defineEmits(['register']);
|
||||
const { t } = useI18n();
|
||||
const leftTreeRef = ref<Nullable<TreeActionType>>(null);
|
||||
const [registerModal, { closeModal, changeLoading }] = useModalInner(init);
|
||||
const state = reactive<State>({
|
||||
treeData: [],
|
||||
currentId: '',
|
||||
currentContent: '',
|
||||
editorOptions: {
|
||||
readOnly: true,
|
||||
scrollBeyondLastLine: true,
|
||||
minimap: {
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
editorLanguage: 'html',
|
||||
key: +new Date(),
|
||||
fullName: '',
|
||||
data: {},
|
||||
});
|
||||
const { treeData, currentContent, editorOptions, editorLanguage, key, fullName, data } = toRefs(state);
|
||||
const [registerDiffPreviewModal, { openModal: openDiffPreviewModal }] = useModal();
|
||||
|
||||
function init(data) {
|
||||
state.fullName = data.fullName || '';
|
||||
state.data = data;
|
||||
state.treeData = [];
|
||||
state.currentId = '';
|
||||
state.currentContent = '';
|
||||
state.key = +new Date();
|
||||
changeLoading(true);
|
||||
const query = {
|
||||
module: data.module || 'system',
|
||||
description: data.description,
|
||||
modulePackageName: data.modulePackageName || '',
|
||||
enableFlow: data.enableFlow,
|
||||
};
|
||||
codePreview(data.id, query).then(res => {
|
||||
state.treeData = res.data.list.map(o => ({ ...o, disabled: true }));
|
||||
state.currentId = state.treeData[0].children[0].id;
|
||||
state.currentContent = state.treeData[0].children[0].fileContent;
|
||||
state.editorLanguage = ['web', 'app'].includes(state.treeData[0].children[0].fileType) ? 'html' : 'java';
|
||||
nextTick(() => {
|
||||
const leftTree = unref(leftTreeRef);
|
||||
leftTree?.setSelectedKeys([state.currentId]);
|
||||
changeLoading(false);
|
||||
});
|
||||
});
|
||||
}
|
||||
function handleTreeSelect(id, node) {
|
||||
state.key = +new Date();
|
||||
state.currentId = id;
|
||||
state.currentContent = node.fileContent;
|
||||
state.editorLanguage = ['web', 'app'].includes(node.fileType) ? 'html' : 'java';
|
||||
}
|
||||
</script>
|
||||
380
src/views/onlineDev/webDesign/components/ShortLinkModal.vue
Normal file
380
src/views/onlineDev/webDesign/components/ShortLinkModal.vue
Normal file
@@ -0,0 +1,380 @@
|
||||
<template>
|
||||
<BasicModal v-bind="$attrs" @register="registerModal" title="表单外链配置" :width="800" @ok="handleSubmit" class="yunzhupaas-shortLink-modal">
|
||||
<a-form :colon="false" :model="dataForm" :rules="rules" :labelCol="{ style: { width: '100px' } }" ref="formElRef" hideRequiredMark class="h-500px">
|
||||
<a-form-item label="外链填单" name="formUse">
|
||||
<a-space :size="15">
|
||||
<YunzhupaasSwitch v-model:value="dataForm.formUse" />
|
||||
<span class="shortLink-tip">开启后,会生成表单填写链接,无需登录即可填写表单。</span>
|
||||
</a-space>
|
||||
</a-form-item>
|
||||
<template v-if="dataForm.formUse">
|
||||
<a-form-item label="外链地址">
|
||||
<a-space :size="15">
|
||||
<a-input v-model:value="dataForm.formLink" readonly class="!w-410px" />
|
||||
<template v-if="dataForm.alreadySave">
|
||||
<a-input-group compact>
|
||||
<a-button @click="openWindow(dataForm.formLink)">打开</a-button>
|
||||
<a-button type="primary" @click="handleCopy(dataForm.formLink)">复制</a-button>
|
||||
</a-input-group>
|
||||
<a-popover placement="bottomRight">
|
||||
<template #content>
|
||||
<p class="shortLink-tip">扫描二维码,分享此链接</p>
|
||||
<QrCode :value="dataForm.formLink" ref="qrRef" :width="154" :options="{ margin: 1 }" class="my-5px" />
|
||||
<a-button pre-icon="icon-ym icon-ym-download" block size="mini" @click="handleDownload(1)">下载</a-button>
|
||||
</template>
|
||||
<i class="ym-custom ym-custom-qrcode"></i>
|
||||
</a-popover>
|
||||
</template>
|
||||
<template v-else>
|
||||
<a-input-group compact>
|
||||
<a-tooltip placement="bottom">
|
||||
<template #title>请先保存,再打开</template>
|
||||
<a-button disabled>打开</a-button>
|
||||
</a-tooltip>
|
||||
<a-tooltip placement="bottom">
|
||||
<template #title>请先保存,再复制</template>
|
||||
<a-button type="primary" disabled>复制</a-button>
|
||||
</a-tooltip>
|
||||
</a-input-group>
|
||||
<a-tooltip placement="bottom">
|
||||
<template #title>请先保存,再下载二维码</template>
|
||||
<i class="ym-custom ym-custom-qrcode"></i>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
</a-space>
|
||||
</a-form-item>
|
||||
<a-row justify="start">
|
||||
<a-col>
|
||||
<a-form-item label="凭密码填写" name="formPassUse">
|
||||
<YunzhupaasSwitch v-model:value="dataForm.formPassUse" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col class="ml-15px">
|
||||
<a-form-item name="formPassword" v-if="dataForm.formPassUse">
|
||||
<a-input-password v-model:value="dataForm.formPassword" class="!w-200px" placeholder="请输入密码" :maxlength="20" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<div></div>
|
||||
</template>
|
||||
<template v-if="webType != '1'">
|
||||
<a-divider></a-divider>
|
||||
<a-form-item label="公开查询" name="columnUse">
|
||||
<a-space :size="15">
|
||||
<YunzhupaasSwitch v-model:value="dataForm.columnUse" />
|
||||
<span class="shortLink-tip">开启后,无需登录即可查询已有数据。</span>
|
||||
</a-space>
|
||||
</a-form-item>
|
||||
<template v-if="dataForm.columnUse">
|
||||
<a-form-item label="查询地址">
|
||||
<a-space :size="15">
|
||||
<a-input v-model:value="dataForm.columnLink" readonly class="!w-410px" />
|
||||
<template v-if="dataForm.alreadySave">
|
||||
<a-input-group compact>
|
||||
<a-button @click="openWindow(dataForm.columnLink)">打开</a-button>
|
||||
<a-button type="primary" @click="handleCopy(dataForm.columnLink)">复制</a-button>
|
||||
</a-input-group>
|
||||
<a-popover placement="bottomRight">
|
||||
<template #content>
|
||||
<p class="shortLink-tip">扫描二维码,分享此链接</p>
|
||||
<QrCode :value="dataForm.columnLink" ref="columnQrRef" :width="154" :options="{ margin: 1 }" class="my-5px" />
|
||||
<a-button pre-icon="icon-ym icon-ym-download" block size="mini" @click="handleDownload(2)">下载</a-button>
|
||||
</template>
|
||||
<i class="ym-custom ym-custom-qrcode"></i>
|
||||
</a-popover>
|
||||
</template>
|
||||
<template v-else>
|
||||
<a-input-group compact>
|
||||
<a-tooltip placement="bottom">
|
||||
<template #title>请先保存,再打开</template>
|
||||
<a-button disabled>打开</a-button>
|
||||
</a-tooltip>
|
||||
<a-tooltip placement="bottom">
|
||||
<template #title>请先保存,再复制</template>
|
||||
<a-button type="primary" disabled>复制</a-button>
|
||||
</a-tooltip>
|
||||
</a-input-group>
|
||||
<a-tooltip placement="bottom">
|
||||
<template #title>请先保存,再下载二维码</template>
|
||||
<i class="ym-custom ym-custom-qrcode"></i>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
</a-space>
|
||||
</a-form-item>
|
||||
<a-form-item label="查询条件" name="columnCondition">
|
||||
<YunzhupaasSelect
|
||||
v-model:value="columnCondition"
|
||||
placeholder="请选择"
|
||||
:options="searchOptions"
|
||||
:fieldNames="{ options: 'options1' }"
|
||||
allowClear
|
||||
showSearch
|
||||
multiple
|
||||
@change="onConditionChange" />
|
||||
</a-form-item>
|
||||
<a-form-item label="显示内容" name="columnText">
|
||||
<YunzhupaasSelect
|
||||
v-model:value="columnText"
|
||||
placeholder="请选择"
|
||||
:options="columnOptions"
|
||||
:fieldNames="{ options: 'options1' }"
|
||||
allowClear
|
||||
showSearch
|
||||
multiple
|
||||
@change="onColumnTextChange" />
|
||||
</a-form-item>
|
||||
<a-row justify="start">
|
||||
<a-col>
|
||||
<a-form-item label="凭密码填写" name="columnPassUse">
|
||||
<YunzhupaasSwitch v-model:value="dataForm.columnPassUse" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col class="ml-15px">
|
||||
<a-form-item name="columnPassword" v-if="dataForm.columnPassUse">
|
||||
<a-input-password v-model:value="dataForm.columnPassword" class="!w-200px" placeholder="请输入密码" :maxlength="20" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</template>
|
||||
</template>
|
||||
</a-form>
|
||||
</BasicModal>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { getShortLinkInfo, updateShortLink } from '@/api/onlineDev/shortLink';
|
||||
import { getInfo } from '@/api/onlineDev/visualDev';
|
||||
import { BasicModal, useModalInner } from '@/components/Modal';
|
||||
import { ref, reactive, toRefs, nextTick, unref } from 'vue';
|
||||
import type { FormInstance } from 'ant-design-vue';
|
||||
import { useMessage } from '@/hooks/web/useMessage';
|
||||
import { noColumnShowList, noSearchList } from '@/components/ColumnDesign/src/helper/config';
|
||||
import { QrCode, QrCodeActionType } from '@/components/Qrcode/index';
|
||||
import { openWindow } from '@/utils';
|
||||
import { useCopyToClipboard } from '@/hooks/web/useCopyToClipboard';
|
||||
|
||||
interface State {
|
||||
dataForm: any;
|
||||
record: any;
|
||||
rules: any;
|
||||
columnCondition: any[];
|
||||
columnText: any[];
|
||||
columnConditionData: any[];
|
||||
columnTextData: any[];
|
||||
columnOptions: any[];
|
||||
searchOptions: any[];
|
||||
webType: string;
|
||||
id: string;
|
||||
}
|
||||
|
||||
const validFieldsList = [
|
||||
'input',
|
||||
'textarea',
|
||||
'inputNumber',
|
||||
'switch',
|
||||
'datePicker',
|
||||
'timePicker',
|
||||
'colorPicker',
|
||||
'rate',
|
||||
'slider',
|
||||
'editor',
|
||||
'link',
|
||||
'text',
|
||||
'alert',
|
||||
'table',
|
||||
'collapse',
|
||||
'collapseItem',
|
||||
'tabItem',
|
||||
'tab',
|
||||
'row',
|
||||
'card',
|
||||
'groupTitle',
|
||||
'divider',
|
||||
'tableGrid',
|
||||
'tableGridTr',
|
||||
'tableGridTd',
|
||||
'location',
|
||||
'steps',
|
||||
'stepItem',
|
||||
];
|
||||
const selectFieldsList = ['radio', 'checkbox', 'select', 'treeSelect', 'cascader'];
|
||||
defineEmits(['register']);
|
||||
const { createMessage } = useMessage();
|
||||
const [registerModal, { changeLoading, changeOkLoading }] = useModalInner(init);
|
||||
const formElRef = ref<FormInstance>();
|
||||
const qrRef = ref<Nullable<QrCodeActionType>>(null);
|
||||
const columnQrRef = ref<Nullable<QrCodeActionType>>(null);
|
||||
const state = reactive<State>({
|
||||
dataForm: {
|
||||
id: '',
|
||||
formLink: '',
|
||||
formUse: 0,
|
||||
formPassUse: 0,
|
||||
formPassword: '',
|
||||
columnUse: 0,
|
||||
columnLink: '',
|
||||
columnPassUse: 0,
|
||||
columnPassword: '',
|
||||
columnCondition: '',
|
||||
columnText: '',
|
||||
alreadySave: false,
|
||||
},
|
||||
columnCondition: [],
|
||||
columnText: [],
|
||||
columnConditionData: [],
|
||||
columnTextData: [],
|
||||
record: {},
|
||||
rules: {
|
||||
formPassword: [{ required: true, message: '请输入密码', trigger: 'blur' }],
|
||||
columnPassword: [{ required: true, message: '请输入密码', trigger: 'blur' }],
|
||||
},
|
||||
columnOptions: [],
|
||||
searchOptions: [],
|
||||
webType: '1',
|
||||
id: '',
|
||||
});
|
||||
const { dataForm, rules, columnCondition, columnText, searchOptions, columnOptions, webType } = toRefs(state);
|
||||
|
||||
function init(data) {
|
||||
changeLoading(true);
|
||||
state.webType = '1';
|
||||
state.dataForm = {
|
||||
id: '',
|
||||
formLink: '',
|
||||
formUse: 0,
|
||||
formPassUse: 0,
|
||||
formPassword: '',
|
||||
columnUse: 0,
|
||||
columnLink: '',
|
||||
columnPassUse: 0,
|
||||
columnPassword: '',
|
||||
columnCondition: '',
|
||||
columnText: '',
|
||||
alreadySave: false,
|
||||
};
|
||||
state.columnCondition = [];
|
||||
state.columnText = [];
|
||||
state.id = data.id;
|
||||
getOptions(data.id);
|
||||
nextTick(() => {
|
||||
clearValidate();
|
||||
getShortLinkData(data.id);
|
||||
});
|
||||
}
|
||||
function getShortLinkData(id) {
|
||||
changeLoading(true);
|
||||
getShortLinkInfo(id).then(res => {
|
||||
state.dataForm = res.data || {};
|
||||
state.columnConditionData = state.dataForm.columnCondition ? JSON.parse(state.dataForm.columnCondition) : [];
|
||||
state.columnTextData = state.dataForm.columnText ? JSON.parse(state.dataForm.columnText) : [];
|
||||
state.columnCondition = state.columnConditionData.map(o => o.id);
|
||||
state.columnText = state.columnTextData.map(o => o.id);
|
||||
changeLoading(false);
|
||||
});
|
||||
}
|
||||
function getOptions(id) {
|
||||
getInfo(id).then(res => {
|
||||
state.webType = res.data.webType || '1';
|
||||
const formData = res.data.formData ? JSON.parse(res.data.formData) : {};
|
||||
let list: any[] = [];
|
||||
const loop = (data, parent?) => {
|
||||
if (!data) return;
|
||||
if (data.__config__ && data.__config__.children && Array.isArray(data.__config__.children)) {
|
||||
loop(data.__config__.children, data);
|
||||
}
|
||||
if (Array.isArray(data)) data.forEach(d => loop(d, parent));
|
||||
if (data.__config__ && data.__config__.yunzhupaasKey) {
|
||||
const visibility = !data.__config__.visibility || (Array.isArray(data.__config__.visibility) && data.__config__.visibility.includes('pc'));
|
||||
if (data.__config__.layout === 'colFormItem' && data.__vModel__ && visibility) {
|
||||
const isTableChild = parent && parent.__config__ && parent.__config__.yunzhupaasKey === 'table';
|
||||
list.push({
|
||||
id: isTableChild ? parent.__vModel__ + '-' + data.__vModel__ : data.__vModel__,
|
||||
fullName: isTableChild ? parent.__config__.label + '-' + data.__config__.label : data.__config__.label,
|
||||
...data,
|
||||
disabled: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
loop(formData.fields);
|
||||
list = list.filter(o => {
|
||||
if (!o.__config__ || !o.__config__.yunzhupaasKey) return true;
|
||||
const yunzhupaasKey = o.__config__.yunzhupaasKey;
|
||||
if (validFieldsList.includes(yunzhupaasKey) || (selectFieldsList.includes(yunzhupaasKey) && o.__config__.dataType === 'static')) return true;
|
||||
return false;
|
||||
});
|
||||
const columnOptions = list.filter(o => !noColumnShowList.includes(o.__config__.yunzhupaasKey) || o.isStorage);
|
||||
const searchOptions = list.filter(o => !noSearchList.includes(o.__config__.yunzhupaasKey));
|
||||
state.columnOptions = columnOptions.map(o => ({
|
||||
label: o.fullName,
|
||||
prop: o.id,
|
||||
fixed: 'none',
|
||||
align: 'left',
|
||||
yunzhupaasKey: o.__config__.yunzhupaasKey,
|
||||
sortable: false,
|
||||
width: null,
|
||||
...o,
|
||||
}));
|
||||
state.searchOptions = searchOptions.map(o => ({
|
||||
label: o.fullName,
|
||||
prop: o.id,
|
||||
yunzhupaasKey: o.__config__.yunzhupaasKey,
|
||||
value: '',
|
||||
...o,
|
||||
}));
|
||||
});
|
||||
}
|
||||
function clearValidate() {
|
||||
formElRef.value?.clearValidate();
|
||||
}
|
||||
function handleDownload(type) {
|
||||
const qrEl = type == 1 ? unref(qrRef) : unref(columnQrRef);
|
||||
if (!qrEl) return;
|
||||
qrEl.download('二维码');
|
||||
}
|
||||
function handleCopy(text) {
|
||||
const { isSuccessRef } = useCopyToClipboard(text);
|
||||
unref(isSuccessRef) && createMessage.success('复制成功');
|
||||
}
|
||||
function onConditionChange(_val, data) {
|
||||
state.columnConditionData = data || [];
|
||||
}
|
||||
function onColumnTextChange(_val, data) {
|
||||
state.columnTextData = data || [];
|
||||
}
|
||||
async function handleSubmit() {
|
||||
try {
|
||||
const values = await formElRef.value?.validate();
|
||||
if (!values) return;
|
||||
changeOkLoading(true);
|
||||
state.dataForm.columnCondition = JSON.stringify(state.columnConditionData);
|
||||
state.dataForm.columnText = JSON.stringify(state.columnTextData);
|
||||
updateShortLink(state.dataForm)
|
||||
.then(res => {
|
||||
changeOkLoading(false);
|
||||
createMessage.success(res.msg);
|
||||
getShortLinkData(state.id);
|
||||
})
|
||||
.catch(() => {
|
||||
changeOkLoading(false);
|
||||
});
|
||||
} catch (_) {}
|
||||
}
|
||||
</script>
|
||||
<style lang="less">
|
||||
.yunzhupaas-shortLink-modal {
|
||||
.shortLink-tip {
|
||||
color: @text-color-label;
|
||||
}
|
||||
.ym-custom-qrcode {
|
||||
font-size: 32px;
|
||||
line-height: 29px;
|
||||
height: 29px;
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
color: @text-color-label;
|
||||
}
|
||||
.ant-divider {
|
||||
margin-top: 0 !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
55
src/views/onlineDev/webDesign/components/TableModal.vue
Normal file
55
src/views/onlineDev/webDesign/components/TableModal.vue
Normal file
@@ -0,0 +1,55 @@
|
||||
<template>
|
||||
<BasicModal v-bind="$attrs" @register="registerModal" title="数据选择" @ok="handleSubmit" :width="800" class="yunzhupaas-list-modal">
|
||||
<BasicTable :searchInfo="searchInfo" @register="registerTable" class="yunzhupaas-sub-table"></BasicTable>
|
||||
</BasicModal>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { getDataModelList } from '@/api/systemData/dataModel';
|
||||
import { reactive } from 'vue';
|
||||
import { BasicModal, useModalInner } from '@/components/Modal';
|
||||
import { BasicTable, useTable } from '@/components/Table';
|
||||
import { useI18n } from '@/hooks/web/useI18n';
|
||||
|
||||
const emit = defineEmits(['register', 'select']);
|
||||
const { t } = useI18n();
|
||||
const [registerModal, { closeModal }] = useModalInner(init);
|
||||
const searchInfo = reactive<Recordable>({
|
||||
linkId: '0',
|
||||
});
|
||||
const [registerTable, { getForm, getSelectRows }] = useTable({
|
||||
api: getDataModelList,
|
||||
columns: [
|
||||
{ title: '表名', dataIndex: 'table' },
|
||||
{ title: '说明', dataIndex: 'tableName' },
|
||||
],
|
||||
useSearchForm: true,
|
||||
formConfig: {
|
||||
baseColProps: { span: 8 },
|
||||
schemas: [
|
||||
{
|
||||
field: 'keyword',
|
||||
label: t('common.keyword'),
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: t('common.enterKeyword'),
|
||||
submitOnPressEnter: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
tableSetting: { size: false, setting: false },
|
||||
isCanResizeParent: true,
|
||||
resizeHeightOffset: -73,
|
||||
immediate: false,
|
||||
rowSelection: { type: 'checkbox' },
|
||||
});
|
||||
function init(data) {
|
||||
searchInfo.linkId = data.dbLinkId ?? '0';
|
||||
getForm().resetFields();
|
||||
}
|
||||
function handleSubmit() {
|
||||
const selectedData = getSelectRows();
|
||||
emit('select', selectedData);
|
||||
closeModal();
|
||||
}
|
||||
</script>
|
||||
276
src/views/onlineDev/webDesign/index.vue
Normal file
276
src/views/onlineDev/webDesign/index.vue
Normal file
@@ -0,0 +1,276 @@
|
||||
<template>
|
||||
<div class="yunzhupaas-content-wrapper">
|
||||
<div class="yunzhupaas-content-wrapper-center">
|
||||
<div class="yunzhupaas-content-wrapper-content">
|
||||
<BasicTable @register="registerTable">
|
||||
<template #tableTitle>
|
||||
<a-dropdown>
|
||||
<template #overlay>
|
||||
<a-menu @click="handleAdd">
|
||||
<a-menu-item :key="item.id" v-for="item in webTypeOptions">{{ item.fullName }}</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
<a-button type="primary" preIcon="icon-ym icon-ym-btn-add">{{ t('common.addText') }}<DownOutlined /></a-button>
|
||||
</a-dropdown>
|
||||
<a-button type="primary" preIcon="icon-ym icon-ym-ai-form" @click="handleAiCreate">AI建表</a-button>
|
||||
<yunzhupaas-upload-btn url="/api/visualdev/OnlineDev/Actions/Import" accept=".vdd" @on-success="reload"></yunzhupaas-upload-btn>
|
||||
</template>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'isRelease'">
|
||||
<a-tag :color="record.isRelease == 1 ? 'success' : record.isRelease == 2 ? 'warning' : ''">
|
||||
{{ record.isRelease == 1 ? '已发布' : record.isRelease == 2 ? '已修改' : '未发布' }}
|
||||
</a-tag>
|
||||
</template>
|
||||
<template v-if="column.key === 'action'">
|
||||
<TableAction :actions="getTableActions(record)" :dropDownActions="getDropDownActions(record)" />
|
||||
</template>
|
||||
</template>
|
||||
</BasicTable>
|
||||
</div>
|
||||
</div>
|
||||
<Form @register="registerForm" @reload="reload" />
|
||||
<ViewForm @register="registerViewForm" @reload="reload" />
|
||||
<CreateMenuModal @register="registerCreateMenuModal" @reload="reload" />
|
||||
<ShortLinkModal @register="registerShortLink" @reload="reload" />
|
||||
<DownloadModal @register="registerDownloadModal" />
|
||||
<PreviewModal @register="registerPreview" />
|
||||
<AiCreateModal @register="registerAiCreateModal" @reload="handleAiReload" />
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { reactive, onMounted, ref } from 'vue';
|
||||
import { getVisualDevList, delVisualDev, copy, exportData, release } from '@/api/onlineDev/visualDev';
|
||||
import { BasicTable, useTable, TableAction, BasicColumn, ActionItem } from '@/components/Table';
|
||||
import { useMessage } from '@/hooks/web/useMessage';
|
||||
import { useI18n } from '@/hooks/web/useI18n';
|
||||
import { useModal } from '@/components/Modal';
|
||||
import { useBaseStore } from '@/store/modules/base';
|
||||
import Form from './Form.vue';
|
||||
import ViewForm from './ViewForm.vue';
|
||||
import CreateMenuModal from './components/CreateMenuModal.vue';
|
||||
import ShortLinkModal from './components/ShortLinkModal.vue';
|
||||
import DownloadModal from './components/DownloadModal.vue';
|
||||
import AiCreateModal from './components/AiCreateModal.vue';
|
||||
import { downloadByUrl } from '@/utils/file/download';
|
||||
import { PreviewModal } from '@/components/CommonModal';
|
||||
import { DownOutlined } from '@ant-design/icons-vue';
|
||||
import { useGo } from '@/hooks/web/usePage';
|
||||
|
||||
defineOptions({ name: 'onlineDev-webDesign' });
|
||||
|
||||
const { createMessage } = useMessage();
|
||||
const baseStore = useBaseStore();
|
||||
const { t } = useI18n();
|
||||
const [registerForm, { openModal: openFormModal }] = useModal();
|
||||
const [registerViewForm, { openModal: openViewFormModal }] = useModal();
|
||||
const [registerCreateMenuModal, { openModal: openCreateMenuModal }] = useModal();
|
||||
const [registerShortLink, { openModal: openShortLinkModal }] = useModal();
|
||||
const [registerPreview, { openModal: openPreviewModal }] = useModal();
|
||||
const [registerDownloadModal, { openModal: openDownloadModal }] = useModal();
|
||||
const [registerAiCreateModal, { openModal: openAiCreateModal }] = useModal();
|
||||
|
||||
const webTypeOptions: any[] = [
|
||||
{ fullName: '表单', id: 2 },
|
||||
{ fullName: '视图', id: 4 },
|
||||
];
|
||||
const columns: BasicColumn[] = [
|
||||
{ title: '名称', dataIndex: 'fullName', width: 200 },
|
||||
{ title: '编码', dataIndex: 'enCode', width: 200 },
|
||||
{ title: '分类', dataIndex: 'category', width: 150 },
|
||||
{ title: '类型', dataIndex: 'webType', width: 100, align: 'center', customRender: ({ record }) => (record.webType == 4 ? '视图' : '表单') },
|
||||
{ title: '创建人', dataIndex: 'creatorUser', width: 120 },
|
||||
{ title: '创建时间', dataIndex: 'creatorTime', width: 150, format: 'date|YYYY-MM-DD HH:mm:ss' },
|
||||
{ title: '最后修改时间', dataIndex: 'lastModifyTime', width: 150, format: 'date|YYYY-MM-DD HH:mm:ss' },
|
||||
{ title: '排序', dataIndex: 'sortCode', width: 70, align: 'center' },
|
||||
{ title: '状态', dataIndex: 'isRelease', width: 80, align: 'center' },
|
||||
];
|
||||
const categoryList = ref<any[]>([]);
|
||||
const searchInfo = reactive({
|
||||
type: 1,
|
||||
});
|
||||
const go = useGo();
|
||||
const [registerTable, { reload, getForm }] = useTable({
|
||||
api: getVisualDevList,
|
||||
columns,
|
||||
searchInfo,
|
||||
useSearchForm: true,
|
||||
formConfig: {
|
||||
schemas: [
|
||||
{
|
||||
field: 'keyword',
|
||||
label: t('common.keyword'),
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: t('common.enterKeyword'),
|
||||
submitOnPressEnter: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'category',
|
||||
label: '分类',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
placeholder: '请选择',
|
||||
showSearch: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'webType',
|
||||
label: '类型',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
placeholder: '请选择',
|
||||
options: webTypeOptions,
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'isRelease',
|
||||
label: '状态',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
placeholder: '请选择',
|
||||
options: [
|
||||
{ fullName: '未发布', id: 0 },
|
||||
{ fullName: '已发布', id: 1 },
|
||||
{ fullName: '已修改', id: 2 },
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
actionColumn: {
|
||||
width: 220,
|
||||
title: '操作',
|
||||
dataIndex: 'action',
|
||||
},
|
||||
});
|
||||
function getTableActions(record): ActionItem[] {
|
||||
return [
|
||||
{
|
||||
label: t('common.editText'),
|
||||
onClick: addOrUpdateHandle.bind(null, record.id, record.webType),
|
||||
},
|
||||
{
|
||||
label: '发布',
|
||||
color: 'error',
|
||||
modelConfirm: {
|
||||
title: '发布确认',
|
||||
content: '发布表单会覆盖当前线上版本, 是否继续?',
|
||||
onOk: handleRelease.bind(null, record.id),
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '代码生成',
|
||||
disabled: !record.isRelease,
|
||||
onClick: handleDownload.bind(null, record),
|
||||
},
|
||||
];
|
||||
}
|
||||
function getDropDownActions(record): ActionItem[] {
|
||||
return [
|
||||
{
|
||||
label: '预览表单',
|
||||
onClick: handlePreview.bind(null, record.id, record.isRelease),
|
||||
},
|
||||
{
|
||||
label: '生成菜单',
|
||||
ifShow: !!record.isRelease,
|
||||
onClick: handleCreateMenu.bind(null, record.id),
|
||||
},
|
||||
{
|
||||
label: '复制表单',
|
||||
modelConfirm: {
|
||||
content: '您确定要复制该功能表单, 是否继续?',
|
||||
onOk: handleCopy.bind(null, record.id),
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '导出表单',
|
||||
modelConfirm: {
|
||||
content: '您确定要导出该功能表单, 是否继续?',
|
||||
onOk: handleExport.bind(null, record.id),
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '外链设置',
|
||||
ifShow: !!record.isRelease && record.webType != 4,
|
||||
onClick: openShortLink.bind(null, record.id),
|
||||
},
|
||||
{
|
||||
label: '数据管理',
|
||||
ifShow: !!record.isRelease && record.webType == 2,
|
||||
onClick: openFormData.bind(null, record.id),
|
||||
},
|
||||
{
|
||||
label: '删除表单',
|
||||
modelConfirm: {
|
||||
onOk: handleDelete.bind(null, record.id),
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
function addOrUpdateHandle(id = '', webType) {
|
||||
if (webType == 4) return openViewFormModal(true, { id, ...searchInfo, categoryList });
|
||||
openFormModal(true, { id, ...searchInfo, categoryList });
|
||||
}
|
||||
function handleDelete(id) {
|
||||
delVisualDev(id).then(res => {
|
||||
createMessage.success(res.msg);
|
||||
reload();
|
||||
});
|
||||
}
|
||||
function handleDownload(record) {
|
||||
openDownloadModal(true, { tables: record.tables, id: record.id, hasPackage: record.hasPackage, fullName: record.fullName, webType: record.webType });
|
||||
}
|
||||
// 发布
|
||||
function handleRelease(id) {
|
||||
release(id).then(res => {
|
||||
createMessage.success(res.msg);
|
||||
reload();
|
||||
});
|
||||
}
|
||||
// 生成菜单
|
||||
function handleCreateMenu(id) {
|
||||
openCreateMenuModal(true, { id });
|
||||
}
|
||||
function handlePreview(id, isRelease) {
|
||||
openPreviewModal(true, { type: 'webDesign', id, isRelease });
|
||||
}
|
||||
function openShortLink(id) {
|
||||
openShortLinkModal(true, { id });
|
||||
}
|
||||
function openFormData(id) {
|
||||
if (!id) return;
|
||||
go(`/dataManage?isDataManage=1&id=${id}`);
|
||||
}
|
||||
function handleCopy(id) {
|
||||
copy(id).then(res => {
|
||||
createMessage.success(res.msg);
|
||||
reload();
|
||||
});
|
||||
}
|
||||
function handleExport(id) {
|
||||
exportData(id).then(res => {
|
||||
downloadByUrl({ url: res.data.url });
|
||||
});
|
||||
}
|
||||
async function getOptions() {
|
||||
const res = await baseStore.getDictionaryData('businessType');
|
||||
categoryList.value = res as any[];
|
||||
getForm().updateSchema({ field: 'category', componentProps: { options: res } });
|
||||
}
|
||||
function handleAdd({ key }) {
|
||||
addOrUpdateHandle('', key);
|
||||
}
|
||||
function handleAiCreate() {
|
||||
openAiCreateModal(true, { categoryList });
|
||||
}
|
||||
function handleAiReload(id) {
|
||||
addOrUpdateHandle(id, 2);
|
||||
reload();
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getOptions();
|
||||
});
|
||||
</script>
|
||||
Reference in New Issue
Block a user