init
This commit is contained in:
535
src/views/lowdesign/formDesign/index.vue
Normal file
535
src/views/lowdesign/formDesign/index.vue
Normal file
@@ -0,0 +1,535 @@
|
||||
<template>
|
||||
<ContentWrap>
|
||||
<div class="flex gap-x-10px">
|
||||
<div class="flex-basis-180px flex-shrink-0">
|
||||
<avue-tree
|
||||
ref="treeRef"
|
||||
v-model="treeForm"
|
||||
:option="treeOption"
|
||||
:data="treeData"
|
||||
:permission="treePermission"
|
||||
:before-open="treeBeforeOpen"
|
||||
@node-contextmenu="treeNodeContextmenu"
|
||||
@node-click="treeNodeClick"
|
||||
@update="treeUpdate"
|
||||
@save="treeSave"
|
||||
@del="treeDel"
|
||||
>
|
||||
</avue-tree>
|
||||
</div>
|
||||
<avue-crud
|
||||
class="table-content flex-1"
|
||||
ref="crudRef"
|
||||
v-model="tableForm"
|
||||
v-model:page="tablePage"
|
||||
v-model:search="tableSearch"
|
||||
:table-loading="loading"
|
||||
:data="tableData"
|
||||
:option="tableOption"
|
||||
:permission="permission"
|
||||
:before-open="beforeOpen"
|
||||
@search-change="searchChange"
|
||||
@search-reset="resetChange"
|
||||
@row-save="rowSave"
|
||||
@row-update="rowUpdate"
|
||||
@row-del="rowDel"
|
||||
@refresh-change="getTableData"
|
||||
@size-change="sizeChange"
|
||||
@current-change="currentChange"
|
||||
@selection-change="selectionChange"
|
||||
>
|
||||
<template #menu-left="{ size }">
|
||||
<el-button
|
||||
:size="size"
|
||||
type="danger"
|
||||
:disabled="!tableSelect.length"
|
||||
v-hasPermi="['jeelowcode:desform:delete']"
|
||||
@click="rowDel(selectIds)"
|
||||
>
|
||||
<Icon :size="16" icon="mi:delete" class="mr-3px" />批量删除
|
||||
</el-button>
|
||||
</template>
|
||||
<template #menu="{ size, row }">
|
||||
<div class="flex justify-center flex-items-center">
|
||||
<el-button
|
||||
type="primary"
|
||||
text
|
||||
:size="size"
|
||||
@click="menuHandle({ type: 'form', row })"
|
||||
v-hasPermi="['jeelowcode:desform:update']"
|
||||
>
|
||||
<Icon :size="14" icon="ep:edit-pen"></Icon>
|
||||
<span>设计表单</span>
|
||||
</el-button>
|
||||
<span
|
||||
v-if="
|
||||
checkPermi(['jeelowcode:desform:update']) &&
|
||||
checkPermi([
|
||||
'jeelowcode:desform:create',
|
||||
'jeelowcode:desform:update',
|
||||
'jeelowcode:desform:delete'
|
||||
])
|
||||
"
|
||||
class="ml-8px mr-8px mt-2px inline-block h-16px w-1px bg-#e8e8e8 .dark:bg-[var(--el-border-color-dark)]"
|
||||
></span>
|
||||
<el-dropdown
|
||||
@command="menuHandle"
|
||||
v-if="
|
||||
checkPermi([
|
||||
'jeelowcode:desform:create',
|
||||
'jeelowcode:desform:update',
|
||||
'jeelowcode:desform:delete'
|
||||
])
|
||||
"
|
||||
>
|
||||
<div class="mt--2px cursor-pointer">
|
||||
<el-text :size="size" type="primary">
|
||||
更多
|
||||
<Icon :size="16" icon="iconamoon:arrow-down-2-light" />
|
||||
</el-text>
|
||||
</div>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item
|
||||
v-if="checkPermi(['jeelowcode:desform:update'])"
|
||||
:command="{ type: 'edit', row }"
|
||||
>
|
||||
编辑
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
:command="{ type: 'copy', row }"
|
||||
v-if="checkPermi(['jeelowcode:desform:create'])"
|
||||
>
|
||||
复制表
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item :command="{ type: 'routeAddress', row }">
|
||||
路由地址
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
v-if="row.isOpen == 'Y'"
|
||||
:command="{ type: 'externalAddress', row }"
|
||||
>
|
||||
外部访问链接
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
v-if="checkPermi(['jeelowcode:desform:update'])"
|
||||
:command="{ type: 'formUnlock', row }"
|
||||
>
|
||||
解除锁定
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
v-if="checkPermi(['jeelowcode:desform:delete'])"
|
||||
:command="{ type: 'del', row }"
|
||||
>
|
||||
删除
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</template>
|
||||
</avue-crud>
|
||||
</div>
|
||||
<DesignPopup v-model="designPop.dialog" v-bind="designPop.params" :isBodyFull="true">
|
||||
<template #default>
|
||||
<LowFormDesign :formDesignData="designPop.value" :isShow="designPop.dialog"></LowFormDesign>
|
||||
</template>
|
||||
</DesignPopup>
|
||||
</ContentWrap>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import * as FormApi from '@/api/design/form'
|
||||
import { ElLoading } from 'element-plus'
|
||||
import { LowFormDesign } from '@/components/LowFormDesign/index'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
import { useGroup } from '@/hooks/design/useGroup'
|
||||
import { checkPermi } from '@/utils/permission'
|
||||
import { ElButton } from 'element-plus'
|
||||
import useCopyText from '@/hooks/design/useCopyText'
|
||||
|
||||
defineOptions({ name: 'FormDesign' })
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { t } = useI18n() // 国际化
|
||||
const { getCurrPermi } = useCrudPermi()
|
||||
const { copyText } = useCopyText()
|
||||
|
||||
const loading = ref(true) // 列表的加载中
|
||||
//表格配置
|
||||
const tableOption = reactive({
|
||||
align: 'center',
|
||||
headerAlign: 'center',
|
||||
searchMenuSpan: 6,
|
||||
searchMenuPosition: 'left',
|
||||
labelSuffix: ' ',
|
||||
selection: true,
|
||||
dialogWidth: 800,
|
||||
dialogFullscreen: false,
|
||||
editBtn: false,
|
||||
delBtn: false,
|
||||
border: true,
|
||||
index: false,
|
||||
menuWidth: 180,
|
||||
span: 12,
|
||||
column: {
|
||||
id: {
|
||||
label: '表单id',
|
||||
display: false,
|
||||
search: true
|
||||
},
|
||||
desformName: {
|
||||
label: '表单名称',
|
||||
search: true,
|
||||
rules: [{ required: true, message: '请输入表单名称', trigger: 'blur' }]
|
||||
},
|
||||
groupDesformId: {
|
||||
label: '分组类型',
|
||||
type: 'tree',
|
||||
value: '',
|
||||
dicData: [],
|
||||
filterable: true,
|
||||
defaultExpandAll: true,
|
||||
props: { label: 'name', value: 'id' },
|
||||
hide: true
|
||||
},
|
||||
updateTime: {
|
||||
label: '修改时间',
|
||||
type: 'datetime',
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
valueFormat: 'x',
|
||||
display: false
|
||||
},
|
||||
isOpen: {
|
||||
label: '外部链接访问',
|
||||
type: 'select',
|
||||
width: 110,
|
||||
search: true,
|
||||
searchLabelWidth: 110,
|
||||
value: 'N',
|
||||
dicData: [
|
||||
{ label: '禁用', value: 'N' },
|
||||
{ label: '启用', value: 'Y' }
|
||||
]
|
||||
},
|
||||
isTemplate: {
|
||||
label: '是否作为模板',
|
||||
type: 'select',
|
||||
width: 110,
|
||||
search: true,
|
||||
searchLabelWidth: 110,
|
||||
value: 'N',
|
||||
dicData: [
|
||||
{ label: '是', value: 'Y' },
|
||||
{ label: '否', value: 'N' }
|
||||
]
|
||||
}
|
||||
}
|
||||
})
|
||||
const tableForm = ref<any>({})
|
||||
const tableData = ref([])
|
||||
const tableSearch = ref({})
|
||||
const tablePage = ref({
|
||||
currentPage: 1,
|
||||
pageSize: 10,
|
||||
total: 0
|
||||
})
|
||||
const tableSelect = ref([])
|
||||
const designPop = ref({
|
||||
value: {},
|
||||
dialog: false,
|
||||
params: {}
|
||||
})
|
||||
|
||||
const crudRef = ref()
|
||||
const treeRef = ref()
|
||||
|
||||
const isUnload = ref(false)
|
||||
|
||||
const selectIds = computed(() => {
|
||||
return tableSelect.value.map((item) => item['id'])
|
||||
})
|
||||
const permission = getCurrPermi(['jeelowcode:desform'])
|
||||
useCrudHeight(crudRef)
|
||||
const {
|
||||
treeForm,
|
||||
treeOption,
|
||||
treeData,
|
||||
groupValue,
|
||||
treePermission,
|
||||
treeBeforeOpen,
|
||||
treeNodeContextmenu,
|
||||
treeNodeClick,
|
||||
getTreeData,
|
||||
treeUpdate,
|
||||
treeSave,
|
||||
treeDel
|
||||
} = useGroup(treeRef, FormApi, () => resetChange())
|
||||
|
||||
const menuHandle = async ({ type, row }) => {
|
||||
if (type == 'edit') crudRef.value.rowEdit(row)
|
||||
else if (type == 'form') openFormDesing(row)
|
||||
else if (type == 'copy') copyForm(row)
|
||||
else if (type.indexOf('Address') != -1) showAddress(type, row)
|
||||
else if (type.indexOf('Unlock') !== -1) unlockForm(type, row)
|
||||
else if (type == 'del') rowDel(row)
|
||||
}
|
||||
|
||||
const showAddress = (type, row) => {
|
||||
let title = '菜单路由地址'
|
||||
let urlType = 'view'
|
||||
if (type == 'externalAddress') urlType = 'external'
|
||||
let htmlArr = [
|
||||
{ label: `表单填写(默认值启用)`, value: `form/${urlType}/fillout/${row.id}` },
|
||||
{ label: `表单填写(默认值禁用)`, value: `form/${urlType}/filloutNo/${row.id}` },
|
||||
{ label: `表单详情查看`, value: `form/${urlType}/detail/${row.id}` }
|
||||
]
|
||||
if (type == 'externalAddress') {
|
||||
htmlArr = htmlArr.map((item) => {
|
||||
item.value = `${window.location.origin}/${item.value}`
|
||||
return item
|
||||
})
|
||||
title = '外部链接访问地址'
|
||||
}
|
||||
let list: VNode[] = []
|
||||
htmlArr.forEach((item) => {
|
||||
list.push(
|
||||
h('div', { style: { marginBottom: '10px', border: ' 1px solid #eee', padding: '10px' } }, [
|
||||
h('div', [
|
||||
h('span', { style: { fontWeight: 600, fontSize: '14px' } }, item.label + ':'),
|
||||
h(
|
||||
ElButton,
|
||||
{ size: 'small', type: 'primary', onClick: () => copyText(item.value) },
|
||||
() => '复制'
|
||||
)
|
||||
]),
|
||||
h('div', { style: { fontSize: '12px' } }, item.value)
|
||||
])
|
||||
)
|
||||
})
|
||||
message.alert('', title, {
|
||||
message: () => {
|
||||
return h('div', { width: '100%' }, list)
|
||||
},
|
||||
confirmButtonText: '关闭',
|
||||
dangerouslyUseHTMLString: true,
|
||||
customStyle: { width: '100%' },
|
||||
customClass: 'form-design-address-box'
|
||||
})
|
||||
}
|
||||
|
||||
const openFormDesing = async (row) => {
|
||||
loading.value = true
|
||||
const data = await FormApi.getFormDetail({ desFormId: row.id, lock: true })
|
||||
loading.value = false
|
||||
if (data.timeStr) {
|
||||
message
|
||||
.confirm(
|
||||
`<div>
|
||||
<div style="font-size:16px"><span style="font-size:14px">当前操作人:</span>${data.userName}</div>
|
||||
<div style="font-size:16px"><span style="font-size:14px">开始操作时间:</span>${data.timeStr}</div>
|
||||
<div style="color:#E6A23C">注:如果确认没有人正在编辑,可在操作列更多里面解除锁定</div>
|
||||
</div>`,
|
||||
'当前表单已锁定',
|
||||
{
|
||||
dangerouslyUseHTMLString: true,
|
||||
cancelButtonText: '关闭',
|
||||
confirmButtonText: '强制解锁'
|
||||
}
|
||||
)
|
||||
.then(async () => menuHandle({ type: `formUnlockOpen`, row }))
|
||||
return
|
||||
}
|
||||
isUnload.value = true
|
||||
designPop.value = {
|
||||
value: data,
|
||||
dialog: true,
|
||||
params: {
|
||||
title: `表单设计 【${row.desformName}】(${row.id})`,
|
||||
fullscreen: true,
|
||||
width: '90%',
|
||||
handleClose: async (done) => {
|
||||
await FormApi.unlockForm(row.id)
|
||||
isUnload.value = false
|
||||
done()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
const copyForm = (row) => {
|
||||
message
|
||||
.prompt('新表单名称', '复制表单', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: '',
|
||||
inputValidator: (value) => {
|
||||
if (!value) return '请输入表单名称'
|
||||
}
|
||||
})
|
||||
.then(async ({ value }) => {
|
||||
loading.value = true
|
||||
const data = await FormApi.getFormDetail({ desFormId: row.id })
|
||||
data.desformName = value
|
||||
const oldId = data.id
|
||||
const loadingFun = async (id?) => {
|
||||
const hideLoading = () => {
|
||||
loading.value = false
|
||||
}
|
||||
if (id) {
|
||||
const desform = JSON.parse(data.desformJson)
|
||||
if (desform.scssEnhance) {
|
||||
const rexp = new RegExp(oldId, 'g')
|
||||
desform.scssEnhance = desform.scssEnhance.replace(rexp, id)
|
||||
rowUpdate(
|
||||
{ id, desformJson: JSON.stringify(desform) },
|
||||
row.$index,
|
||||
hideLoading,
|
||||
hideLoading,
|
||||
true
|
||||
)
|
||||
}
|
||||
} else hideLoading()
|
||||
}
|
||||
delete data.id
|
||||
rowSave(data, loadingFun, loadingFun, true)
|
||||
})
|
||||
}
|
||||
const unlockForm = (type, row) => {
|
||||
message.confirm('是否确认当前表单没有其他人在编辑', '提示').then(async () => {
|
||||
loading.value = true
|
||||
await FormApi.unlockForm(row.id)
|
||||
loading.value = false
|
||||
message.success('解锁成功')
|
||||
if (type.indexOf('Open') !== -1) menuHandle({ type: `form`, row })
|
||||
})
|
||||
}
|
||||
|
||||
const selectionChange = (data) => {
|
||||
tableSelect.value = data
|
||||
}
|
||||
|
||||
/** 查询列表 */
|
||||
const getTableData = async () => {
|
||||
loading.value = true
|
||||
let searchObj = {
|
||||
...tableSearch.value,
|
||||
pageNo: tablePage.value.currentPage,
|
||||
pageSize: tablePage.value.pageSize
|
||||
}
|
||||
if (groupValue.value) searchObj['groupDesformId'] = groupValue.value
|
||||
for (let key in searchObj) if (searchObj[key] === '') delete searchObj[key]
|
||||
try {
|
||||
const data = await FormApi.getFormList(searchObj)
|
||||
tableData.value = data.records
|
||||
tablePage.value.total = data.total
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const searchChange = (params, done) => {
|
||||
if (Object.keys(params).length && groupValue.value) {
|
||||
treeRef.value.setCurrentKey(0)
|
||||
groupValue.value = 0
|
||||
}
|
||||
tablePage.value.currentPage = 1
|
||||
getTableData().finally(() => {
|
||||
done()
|
||||
})
|
||||
}
|
||||
|
||||
/** 清空按钮操作 */
|
||||
const resetChange = () => {
|
||||
searchChange({}, () => {})
|
||||
}
|
||||
|
||||
const sizeChange = (pageSize) => {
|
||||
tablePage.value.pageSize = pageSize
|
||||
resetChange()
|
||||
}
|
||||
|
||||
const currentChange = (currentPage) => {
|
||||
tablePage.value.currentPage = currentPage
|
||||
getTableData()
|
||||
}
|
||||
|
||||
/** 表单打开前 */
|
||||
const beforeOpen = async (done, type) => {
|
||||
const groupData = cloneDeep(treeData.value)
|
||||
tableOption.column.groupDesformId.dicData = groupData[0].children
|
||||
if (['edit', 'view'].includes(type) && tableForm.value['id']) {
|
||||
loading.value = true
|
||||
const data = await FormApi.getFormDetail({ desFormId: tableForm.value['id'] })
|
||||
tableForm.value = data
|
||||
loading.value = false
|
||||
}
|
||||
if (type == 'add' && groupValue.value) {
|
||||
tableForm.value.groupDesformId = groupValue.value
|
||||
}
|
||||
done()
|
||||
}
|
||||
|
||||
/** 新增操作 */
|
||||
const rowSave = async (form: object, done, loading, isId?) => {
|
||||
const elLoading = ElLoading.service({ fullscreen: true })
|
||||
let bool = await FormApi.saveFormData(form).catch(() => false)
|
||||
if (bool) {
|
||||
message.success(t('common.createSuccess'))
|
||||
resetChange()
|
||||
if (isId) done(bool)
|
||||
else done()
|
||||
} else loading()
|
||||
elLoading.close()
|
||||
}
|
||||
|
||||
/** 编辑操作 */
|
||||
const rowUpdate = async (form, index, done, loading, onData?) => {
|
||||
let bool = await FormApi.updateFormData(form).catch(() => false)
|
||||
if (bool) {
|
||||
message.success(t('common.updateSuccess'))
|
||||
if (!onData) getTableData()
|
||||
done()
|
||||
} else loading()
|
||||
}
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const rowDel = async (data) => {
|
||||
try {
|
||||
// 删除的二次确认
|
||||
await message.delConfirm()
|
||||
// 发起删除
|
||||
await FormApi.deleteFormData(data instanceof Array ? data : [data.id])
|
||||
message.success(t('common.delSuccess'))
|
||||
// 刷新列表
|
||||
await getTableData()
|
||||
} catch {}
|
||||
}
|
||||
|
||||
const beforeUnload = (event) => {
|
||||
if (isUnload.value) return (event.returnValue = '您确定要关闭页面吗?')
|
||||
}
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(async () => {
|
||||
window.addEventListener('beforeunload', beforeUnload)
|
||||
getTableData()
|
||||
getTreeData()
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener('beforeunload', beforeUnload)
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.table-content {
|
||||
max-width: calc(100% - 190px);
|
||||
}
|
||||
</style>
|
||||
<style lang="scss">
|
||||
.form-design-address-box {
|
||||
.el-message-box__container {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
157
src/views/lowdesign/formView/index.vue
Normal file
157
src/views/lowdesign/formView/index.vue
Normal file
@@ -0,0 +1,157 @@
|
||||
<template>
|
||||
<div v-loading="loading">
|
||||
<div
|
||||
v-if="isOpen"
|
||||
class="form-external-box w-100% h-100vh py-20px box-border flex flex-col items-center"
|
||||
>
|
||||
<div
|
||||
v-if="isInit && isAuth"
|
||||
class="pos-relative w-80% b-1px b-[var(--el-border-color-light)] b-solid rd-4px bg-#fff"
|
||||
>
|
||||
<div
|
||||
class="external-title text-20px py-10px text-center b-b-1px b-b-[var(--el-border-color-light)] b-b-solid"
|
||||
>
|
||||
{{ externalTitle }}
|
||||
</div>
|
||||
<div class="max-h-[calc(100vh-90px)] overflow-y-auto">
|
||||
<div v-if="isInit" :class="formType !== 'view' ? 'pb-53px' : ''">
|
||||
<LowForm
|
||||
ref="formRef"
|
||||
:form-id="formId"
|
||||
:form-type="formType"
|
||||
:table-id="tableId"
|
||||
handle-type="default"
|
||||
:form-option="formOption"
|
||||
:default-is-open="isOpen"
|
||||
></LowForm>
|
||||
</div>
|
||||
<div v-else style="height: 200px"> </div>
|
||||
</div>
|
||||
<div
|
||||
v-if="formType !== 'view' && (btnObj.submit || btnObj.empty)"
|
||||
class="w-100% pos-absolute left-0% bottom-0 py-10px bg-#fff text-center b-t-1px b-t-[var(--el-border-color-light)] b-t-solid rd-b-4px"
|
||||
>
|
||||
<el-button type="primary" v-if="btnObj.submit" @click="handleBtnClick('submit')">
|
||||
<Icon icon="ep:check"></Icon>
|
||||
<span>提交</span>
|
||||
</el-button>
|
||||
<el-button v-if="btnObj.empty" @click="handleBtnClick('close')">
|
||||
<Icon icon="ep:close"></Icon>
|
||||
<span>清空</span>
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="text-20px mt-50px">无权限访问</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
<ContentWrap>
|
||||
<LowForm
|
||||
v-if="isInit"
|
||||
ref="formRef"
|
||||
class="py-10px!"
|
||||
:form-id="formId"
|
||||
:form-type="formType"
|
||||
:table-id="tableId"
|
||||
:form-option="formOption"
|
||||
:before-close="beforeClose"
|
||||
></LowForm>
|
||||
</ContentWrap>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import * as FormApi from '@/api/design/form'
|
||||
|
||||
const route = useRoute()
|
||||
const message = useMessage()
|
||||
const loading = ref(false)
|
||||
const isInit = ref(false)
|
||||
const isOpen = ref(false)
|
||||
const isAuth = ref(true)
|
||||
const formId = ref('')
|
||||
const formType = ref<any>('')
|
||||
const tableId = ref('')
|
||||
const formOption = ref({})
|
||||
const btnObj = { submit: false, empty: false }
|
||||
const externalTitle = ref('')
|
||||
|
||||
const formRef = ref()
|
||||
|
||||
const resetForm = () => {
|
||||
loading.value = true
|
||||
isInit.value = false
|
||||
setTimeout(() => {
|
||||
loading.value = false
|
||||
isInit.value = true
|
||||
}, 300)
|
||||
}
|
||||
const handleBtnClick = async (type) => {
|
||||
if (type == 'submit') {
|
||||
loading.value = true
|
||||
const data = await formRef.value.handleSubmit(true, () => {
|
||||
loading.value = false
|
||||
})
|
||||
if (data && tableId.value) beforeClose(type)
|
||||
} else formRef.value.handleClear()
|
||||
}
|
||||
|
||||
const beforeClose = (type, form?, loading?) => {
|
||||
if (loading) loading()
|
||||
if (type == 'submit') resetForm()
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
loading.value = true
|
||||
isOpen.value = route.path.indexOf('external') !== -1
|
||||
const pathList = route.path.split('/')
|
||||
const length = pathList.length - 1
|
||||
if (/\d$/.test(pathList[length])) {
|
||||
formId.value = pathList[length]
|
||||
switch (pathList[length - 1]) {
|
||||
case 'fillout':
|
||||
formType.value = 'add'
|
||||
break
|
||||
case 'filloutNo':
|
||||
formType.value = 'edit'
|
||||
break
|
||||
case 'detail':
|
||||
formType.value = 'view'
|
||||
break
|
||||
}
|
||||
const data = await FormApi[`${isOpen.value ? 'getOpenFormDetail' : 'getFormDetail'}`]({
|
||||
desFormId: formId.value
|
||||
}).catch(() => false)
|
||||
if (!data || (isOpen.value && data.isOpen != 'Y')) return (loading.value = false)
|
||||
const option = JSON.parse(data.desformJson || `{}`)
|
||||
option.i18nData = JSON.parse(data.i18nData || `{}`)
|
||||
tableId.value = option.tableDesignId || ''
|
||||
if (isOpen.value) {
|
||||
externalTitle.value = option.externalTitle || data.desformName
|
||||
btnObj.submit = option.submitBtn
|
||||
btnObj.empty = option.emptyBtn
|
||||
option.submitBtn = false
|
||||
option.emptyBtn = false
|
||||
}
|
||||
formOption.value = option
|
||||
loading.value = false
|
||||
isInit.value = true
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.form-external-box {
|
||||
background-attachment: scroll;
|
||||
background-color: rgb(236 239 244 / 100%);
|
||||
background-image: url('@/assets/svgs/external/external_bg.svg');
|
||||
background-position: center center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 103%;
|
||||
background-origin: border-box;
|
||||
|
||||
.external-title {
|
||||
font-family: '微软雅黑', sans-serif;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
108
src/views/lowdesign/general/components/TipView.vue
Normal file
108
src/views/lowdesign/general/components/TipView.vue
Normal file
@@ -0,0 +1,108 @@
|
||||
<template>
|
||||
<div class="control-word-content">
|
||||
<table cellspacing="0" cellpadding="0" style="width: 100%">
|
||||
<tbody v-for="(control, index) in tbodyList" :key="index">
|
||||
<tr>
|
||||
<td colspan="2" class="title">
|
||||
<span>{{ control.title }}</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-for="(tr, trIndex) in control.list" :key="trIndex">
|
||||
<template v-if="props.type == 'simple'">
|
||||
<td>
|
||||
<div class="flex flex-wrap items-center">
|
||||
<div class="value" v-if="tr.value">
|
||||
<el-tooltip v-if="tr.tip" :content="tr.tip">
|
||||
{{ valueStyle == 'simple' ? tr.value : `【 ${tr.value} 】` }}
|
||||
</el-tooltip>
|
||||
<template v-else>
|
||||
{{ valueStyle == 'simple' ? tr.value : `【 ${tr.value} 】` }}
|
||||
</template>
|
||||
</div>
|
||||
<div class="text-12px" :class="{ 'pl-5px': valueStyle == 'simple' }" v-if="tr.label">
|
||||
{{ tr.label }}
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</template>
|
||||
<template v-else>
|
||||
<td v-for="(td, tdIndex) in tr" :key="tdIndex" :colspan="td.colspan">
|
||||
<div class="flex flex-wrap">
|
||||
<span class="value" v-if="td.value">
|
||||
<el-tooltip v-if="td.tip" :content="td.tip">
|
||||
{{ valueStyle == 'simple' ? td.value : `【 ${td.value} 】` }}
|
||||
</el-tooltip>
|
||||
<template v-else>
|
||||
{{ valueStyle == 'simple' ? td.value : `【 ${td.value} 】` }}
|
||||
</template>
|
||||
</span>
|
||||
<span :class="{ 'pl-5px': valueStyle == 'simple' }" v-if="td.label">
|
||||
{{ td.label }}
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
</template>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import controlParams from '@/components/LowDesign/src/utils/tipView'
|
||||
|
||||
defineOptions({ name: 'TipView' })
|
||||
interface Props {
|
||||
type?: 'default' | 'simple'
|
||||
valueStyle?: 'default' | 'simple'
|
||||
tipKeyList?: Array<string>
|
||||
width?: string
|
||||
}
|
||||
const props = defineProps<Props>()
|
||||
const tbodyList = computed(() => {
|
||||
const list = [] as any
|
||||
if (props.tipKeyList) {
|
||||
props.tipKeyList.forEach((key) => {
|
||||
list.push(controlParams[key])
|
||||
})
|
||||
}
|
||||
return list
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.control-word-content {
|
||||
padding: 5px 10px;
|
||||
overflow-y: auto;
|
||||
box-sizing: border-box;
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
td {
|
||||
padding: 6px 8px;
|
||||
text-align: left;
|
||||
border: 1px solid black;
|
||||
|
||||
.value {
|
||||
font-size: 16px;
|
||||
color: #409eff;
|
||||
}
|
||||
|
||||
&.title {
|
||||
font-size: 16px;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
background-color: #409eff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dark .control-word-content {
|
||||
td {
|
||||
border-color: var(--el-border-color-dark) !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
450
src/views/lowdesign/general/components/VerifyOption.vue
Normal file
450
src/views/lowdesign/general/components/VerifyOption.vue
Normal file
@@ -0,0 +1,450 @@
|
||||
<template>
|
||||
<div class="verify-option">
|
||||
<el-container class="h-100%">
|
||||
<el-aside width="220px" class="left-tree">
|
||||
<div class="verify-title">
|
||||
<span>校验类型</span>
|
||||
</div>
|
||||
<div class="verify-draggable">
|
||||
<draggable
|
||||
class="verify-content"
|
||||
tag="div"
|
||||
:list="verifyTypeList"
|
||||
:group="{ name: 'config', pull: 'clone', put: false }"
|
||||
ghost-class="verify-ghost"
|
||||
:sort="false"
|
||||
item-key="value"
|
||||
>
|
||||
<template #item="{ element }">
|
||||
<div class="verify-item" @click="setOption(element)">
|
||||
<span>{{ element.label }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</draggable>
|
||||
</div>
|
||||
</el-aside>
|
||||
<el-main class="main-option">
|
||||
<div class="option-title table-item-row">
|
||||
<div class="row-item">
|
||||
<div class="cell">序号</div>
|
||||
</div>
|
||||
<div class="row-item text-center">
|
||||
<div class="cell">校验类型</div>
|
||||
</div>
|
||||
<div class="row-item">
|
||||
<div class="cell">校验表达式</div>
|
||||
</div>
|
||||
<div class="row-item">
|
||||
<div class="cell">启用状态</div>
|
||||
</div>
|
||||
<div class="row-item">
|
||||
<div class="cell">操作</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="option-content">
|
||||
<draggable
|
||||
class="content-draggable"
|
||||
:list="verifyList"
|
||||
:group="{ name: 'option', put: true }"
|
||||
ghost-class="option-ghost"
|
||||
:animation="300"
|
||||
handle=".move-box"
|
||||
item-key="prop"
|
||||
@add="handleAddColumn"
|
||||
>
|
||||
<template #item="{ element, index }">
|
||||
<div class="option-item table-item-row mt--2px">
|
||||
<div class="row-item move-box">
|
||||
<div class="cell">{{ index + 1 }}</div>
|
||||
</div>
|
||||
<div class="row-item move-box">
|
||||
<div class="cell">
|
||||
<avue-text-ellipsis
|
||||
:key="element.value"
|
||||
:text="element.label"
|
||||
:height="40"
|
||||
:width="140"
|
||||
use-tooltip
|
||||
placement="top"
|
||||
>
|
||||
<template #more>
|
||||
<small>...</small>
|
||||
</template>
|
||||
</avue-text-ellipsis>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row-item">
|
||||
<div class="cell">
|
||||
<template v-if="element.controlType == 'length'">
|
||||
<div class="flex items-center">
|
||||
<span>最小长度:</span>
|
||||
<avue-input-number
|
||||
class="flex-basis-90px flex-shrink-0"
|
||||
v-model="element.leng_min"
|
||||
></avue-input-number>
|
||||
<span class="ml-10px">最大长度:</span>
|
||||
<avue-input-number
|
||||
class="flex-basis-90px flex-shrink-0"
|
||||
v-model="element.leng_max"
|
||||
></avue-input-number>
|
||||
<span class="ml-10px">输入类型:</span>
|
||||
<avue-select
|
||||
class="flex-basis-100px flex-shrink-0"
|
||||
v-model="element.leng_type"
|
||||
:dic="lengTypeDic"
|
||||
></avue-select>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="element.controlType == 'regExp'">
|
||||
<div class="flex items-center">
|
||||
<span>正则:</span>
|
||||
<avue-input
|
||||
class="reg-exp-input flex-1"
|
||||
v-model="element.regExp"
|
||||
:placeholder="element.tip"
|
||||
prepend="/"
|
||||
append="/"
|
||||
></avue-input>
|
||||
<span class="ml-10px">失败提示语:</span>
|
||||
<avue-input
|
||||
class="flex-basis-120px flex-shrink-0"
|
||||
v-model="element.msg"
|
||||
></avue-input>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="element.controlType == 'MEditor'">
|
||||
<avue-input
|
||||
v-model="element.customStr"
|
||||
readonly
|
||||
@click="openEditor(index)"
|
||||
></avue-input>
|
||||
</template>
|
||||
<template v-else-if="patternObj[element.type] || element.tip">
|
||||
<div class="h-32px line-height-32px">
|
||||
{{
|
||||
patternObj[element.type] ||
|
||||
(isSub ? element.subTip || element.tip : element.tip)
|
||||
}}
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row-item">
|
||||
<div class="cell">
|
||||
<avue-switch v-model="element.display" :dic="switchDic"></avue-switch>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row-item">
|
||||
<div class="cell">
|
||||
<el-button type="danger" link text @click="delRow(element)"> 删除 </el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</draggable>
|
||||
</div>
|
||||
</el-main>
|
||||
</el-container>
|
||||
<DesignPopup v-model="MEDialog.value" v-bind="MEDialog.params" :isBodyFull="true">
|
||||
<template #default>
|
||||
<MonacoEditor v-model="MEData.value" v-bind="MEData.params"></MonacoEditor>
|
||||
</template>
|
||||
</DesignPopup>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import draggable from 'vuedraggable'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
import { rulesVerify, patternObj } from '@/components/LowDesign/src/utils/verifyOption'
|
||||
import useMEDialog from '@/hooks/design/useMEDialog'
|
||||
|
||||
defineOptions({ name: 'VerifyOption' })
|
||||
|
||||
interface Props {
|
||||
show: boolean
|
||||
isSub?: boolean
|
||||
filterKey?: string[]
|
||||
}
|
||||
const props = defineProps<Props>()
|
||||
const verifyStr = defineModel<string>({ default: '' })
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { MEDialog, MEData, openMEDialog } = useMEDialog()
|
||||
|
||||
const switchDic = [
|
||||
{ label: '', value: false },
|
||||
{ label: '', value: true }
|
||||
]
|
||||
const lengTypeDic = [
|
||||
{ label: '任意字符', value: 'all' },
|
||||
{ label: '数字', value: 'number' },
|
||||
{ label: '字母', value: 'alphabet' },
|
||||
{ label: '中文', value: 'chinese' }
|
||||
]
|
||||
|
||||
const verifyList = ref<any>([])
|
||||
|
||||
const verifyTypeList = computed(() => {
|
||||
if (props.filterKey) {
|
||||
return rulesVerify.filter((item) => !props.filterKey?.includes(item.type))
|
||||
}
|
||||
return rulesVerify
|
||||
})
|
||||
|
||||
const onlyKeyList = computed(() => {
|
||||
return verifyList.value
|
||||
.filter((item) => !['customRegExp', 'customRule'].includes(item.type) && item.prop)
|
||||
.map((item) => item.type)
|
||||
})
|
||||
|
||||
const openEditor = (index) => {
|
||||
openMEDialog(
|
||||
{
|
||||
prop: 'customStr',
|
||||
label: '自定义校验规则',
|
||||
params: {
|
||||
width: '50%'
|
||||
}
|
||||
},
|
||||
verifyList.value[index]
|
||||
)
|
||||
}
|
||||
|
||||
const initFun = () => {
|
||||
verifyList.value = []
|
||||
if (verifyStr.value) verifyList.value = JSON.parse(verifyStr.value)
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.show,
|
||||
(val) => {
|
||||
if (val) initFun()
|
||||
}
|
||||
)
|
||||
|
||||
const handleAddColumn = (e) => {
|
||||
const newIndex = e.newIndex
|
||||
const data = cloneDeep(verifyList.value[newIndex])
|
||||
if (onlyKeyList.value.includes(data.type)) {
|
||||
verifyList.value = verifyList.value.filter((item) => item.prop)
|
||||
message.info('该校验类型已存在')
|
||||
return
|
||||
}
|
||||
if (data.type == 'leng') data.leng_type = 'all'
|
||||
if (data.type == 'customRule')
|
||||
data.customStr = `return {
|
||||
validator: (rule, value, callback) => {
|
||||
if (!value) {
|
||||
callback(new Error('值不能为空')) //校验失败
|
||||
} else {
|
||||
callback() //校验成功
|
||||
}
|
||||
},
|
||||
trigger: 'blur'
|
||||
}`
|
||||
if (!data.prop) data.prop = `option_${Math.ceil(Math.random() * 9999999)}`
|
||||
data.display = true
|
||||
verifyList.value[newIndex] = data
|
||||
}
|
||||
const setOption = (row) => {
|
||||
verifyList.value.push(row)
|
||||
handleAddColumn({ newIndex: verifyList.value.length - 1 })
|
||||
setTimeout(() => {
|
||||
getOptionStr()
|
||||
}, 300)
|
||||
}
|
||||
|
||||
const delRow = (row) => {
|
||||
verifyList.value = verifyList.value.filter((item) => item.prop != row.prop)
|
||||
}
|
||||
|
||||
const getOptionStr = () => {
|
||||
if (verifyList.value.length) return JSON.stringify(verifyList.value)
|
||||
else ''
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
initFun()
|
||||
})
|
||||
|
||||
defineExpose({ getOptionStr })
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.verify-option {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin-top: -1px;
|
||||
|
||||
.left-tree {
|
||||
padding: 10px;
|
||||
border: 1px solid #f1f1f1;
|
||||
|
||||
.verify-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
cursor: pointer;
|
||||
|
||||
.el-icon {
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
|
||||
&:nth-last-of-type(1) {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.verify-draggable {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.verify-item {
|
||||
width: 160px;
|
||||
padding: 4px 8px;
|
||||
margin-bottom: 10px;
|
||||
font-size: 14px;
|
||||
cursor: move;
|
||||
background-color: #f4f6fc;
|
||||
border: 1px dashed #f4f6fc;
|
||||
|
||||
&:hover {
|
||||
color: var(--el-color-primary);
|
||||
border-color: var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.main-option {
|
||||
padding: 0;
|
||||
border: 1px solid #f1f1f1;
|
||||
border-left: 0;
|
||||
|
||||
.option-content {
|
||||
height: calc(100% - 41px);
|
||||
|
||||
.content-draggable {
|
||||
height: 100%;
|
||||
padding-bottom: 55px;
|
||||
overflow-y: auto;
|
||||
box-sizing: border-box;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
width: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.option-ghost {
|
||||
position: relative;
|
||||
width: 0 !important;
|
||||
height: 32px;
|
||||
min-width: 0 !important;
|
||||
padding: 0 !important;
|
||||
margin: 1px 2px 0;
|
||||
overflow: hidden;
|
||||
font-size: 0;
|
||||
background: white;
|
||||
border-left: 5px solid var(--el-color-primary);
|
||||
content: '';
|
||||
outline: none 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
|
||||
.table-item-row {
|
||||
display: flex;
|
||||
align-items: end;
|
||||
|
||||
.row-item {
|
||||
height: 100%;
|
||||
min-height: 32px;
|
||||
padding: 4px 0;
|
||||
font-size: 14px;
|
||||
line-height: 32px;
|
||||
border-bottom: 1px solid #dcdfe6;
|
||||
|
||||
.cell {
|
||||
padding: 0 12px;
|
||||
|
||||
& > div {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.reg-exp-input {
|
||||
::v-deep(.el-input) {
|
||||
.el-input-group__prepend,
|
||||
.el-input-group__append {
|
||||
padding: 0 8px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:nth-child(1) {
|
||||
flex-basis: 60px;
|
||||
text-align: center;
|
||||
border-right: 1px solid #dcdfe6;
|
||||
}
|
||||
|
||||
&:nth-child(2) {
|
||||
flex-basis: 170px;
|
||||
border-right: 1px solid #dcdfe6;
|
||||
}
|
||||
|
||||
&:nth-child(3) {
|
||||
flex: 1;
|
||||
line-height: normal;
|
||||
border-right: 1px solid #dcdfe6;
|
||||
}
|
||||
|
||||
&:nth-child(4) {
|
||||
flex-basis: 110px;
|
||||
text-align: center;
|
||||
border-right: 1px solid #dcdfe6;
|
||||
}
|
||||
|
||||
&:nth-child(5) {
|
||||
flex-basis: 110px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&.move-box {
|
||||
cursor: move;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.option-title {
|
||||
background-color: #fafafa;
|
||||
|
||||
.row-item:nth-child(3) {
|
||||
line-height: 32px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.right-custom {
|
||||
.custom-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 32px;
|
||||
padding: 4px 0;
|
||||
font-size: 14px;
|
||||
line-height: 32px;
|
||||
text-align: center;
|
||||
background-color: #fafafa;
|
||||
border: 1px solid #dcdfe6;
|
||||
}
|
||||
|
||||
.custom-content {
|
||||
width: 100%;
|
||||
height: calc(100% - 42px);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
3
src/views/lowdesign/general/components/index.ts
Normal file
3
src/views/lowdesign/general/components/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import TipView from './TipView.vue';
|
||||
import VerifyOption from './VerifyOption.vue';
|
||||
export { TipView, VerifyOption }
|
||||
259
src/views/lowdesign/general/components/useRenderVxeColumn.tsx
Normal file
259
src/views/lowdesign/general/components/useRenderVxeColumn.tsx
Normal file
@@ -0,0 +1,259 @@
|
||||
import { VxeUI } from 'vxe-table'
|
||||
import { tableInfoOption } from '../../tableDesign/designData';
|
||||
import { MonacoEditor } from '@/components/MonacoEditor/index'
|
||||
import { useAppStoreWithOut } from '@/store/modules/app'
|
||||
|
||||
const appStore = useAppStoreWithOut()
|
||||
|
||||
//表单开发校验
|
||||
const infoTableIsEdit = ({ row, column }, { verifyEdit, noStop }) => {
|
||||
let bool = true
|
||||
//判断如果是 需要编辑校验并且是系统字段 不可编辑
|
||||
if (verifyEdit && tableInfoOption.disabledArr.includes(row.fieldCode) && row.only) bool = false
|
||||
//判断是否可以绑定字典
|
||||
if (!['select', 'radio', 'checkbox', 'tree', 'cascader', 'dicTableSelect'].includes(row.controlType) && ['dictType', 'dictCode', 'dictTable', 'dictText', 'dictTextFormatter', 'dictTableColumn', 'dictTableSearch'].includes(column.field)) {
|
||||
if (!noStop) bool = false
|
||||
}
|
||||
if (row.controlType != 'dicTableSelect' && column.field == 'dictTableColumn') bool = false
|
||||
//判断如果是 系统字典 不可编辑
|
||||
if (row.dictType == 'dict' && ['dictTable', 'dictText', 'dictTableColumn', 'dictTableSearch'].includes(column.field)) bool = false
|
||||
//判断如果没有同步数据库 接口查询、外键 不可编辑
|
||||
if (row.isDb == 'N' && ['queryIsDb', 'mainTable', 'mainField'].includes(column.field)) bool = false
|
||||
//如果是树表pid的接口查询 不可编辑
|
||||
if (row.fieldCode == 'pid' && row.only && column.field == 'queryIsDb') bool = false
|
||||
//如果是id 接口查询 不可编辑
|
||||
if (row.fieldCode == 'id' && row.only && column.field == 'queryIsDb') bool = false
|
||||
//如果配置了外键 接口查询 不可编辑
|
||||
if (row.mainField && row.mainField && column.field == 'queryIsDb') bool = false
|
||||
//如果虚拟字段 处理方式为空 处理配置不能编辑
|
||||
if (!row.virtualType && ['virtual_java_str', 'virtual_sql_str', 'virtual_fun_str'].includes(column.field)) bool = false
|
||||
return bool
|
||||
}
|
||||
|
||||
const handleLowClickInput = (row, column) => {
|
||||
const prop = column.fieldProp || column.field
|
||||
let text = row[prop]
|
||||
if (text) {
|
||||
if (['summarySql', 'virtual_sql_str', 'virtual_java_str'].includes(prop)) {
|
||||
const obj = JSON.parse(text)
|
||||
text = obj[obj.valueType]
|
||||
if (prop == 'summarySql' && obj.valueType == 'group') {
|
||||
text = text.replace('#{jeelowcode_summary_table}', '当前表的数据源')
|
||||
}
|
||||
if (prop == 'virtual_java_str') {
|
||||
if (obj.valueType == 'group') {
|
||||
text = text.map(item => `${item.type == 'CALCULATE' ? '计算' : '拼接'}:${item.value} `).join(' ')
|
||||
} else if (obj.valueType == 'custom') {
|
||||
text = `JAVA类名/Sping Key:${text.javaPath}`
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return text
|
||||
}
|
||||
|
||||
watch(() => appStore.isDark, (val) => {
|
||||
if (val) VxeUI.setTheme('dark')
|
||||
else VxeUI.setTheme('light')
|
||||
}, { immediate: true })
|
||||
|
||||
export const useRenderVxeColumn = (useType = 'table') => {
|
||||
if (VxeUI.renderer.get('LowInput') !== null) return
|
||||
const stopIcon = <icon style="position: absolute; right: 4px; top: 2px; color: #f56c6c;" size={14} icon="uiw:stop-o" />
|
||||
const lowControl = {
|
||||
LowInput: {
|
||||
default: (renderOpts, { row, column, fieldProp }, isStop) => {
|
||||
const prop = fieldProp || column.field
|
||||
if (isStop) return (<div> <span>{row[prop]}</span> {stopIcon} </div>)
|
||||
return <span>{row[prop]}</span>
|
||||
},
|
||||
edit: (renderOpts, { row, column, fieldProp }) => {
|
||||
const { placeholder } = renderOpts
|
||||
const prop = fieldProp || column.field
|
||||
return <el-input class="my-cell" text="text" v-model={row[prop]} placeholder={placeholder ? placeholder : '请输入 ' + column.title} />
|
||||
}
|
||||
},
|
||||
LowNumber: {
|
||||
default: (renderOpts, { row, column }, isStop) => {
|
||||
if (isStop) return (<div> <span>{row[column.field]}</span> {stopIcon} </div>)
|
||||
return <span>{row[column.field]}</span>
|
||||
},
|
||||
edit: (renderOpts, { row, column }) => {
|
||||
return <div class="w-100% flex"><el-input-number v-model={row[column.field]} controls={false} placeholder={'请输入 ' + column.title} {...(renderOpts.params || {})} /></div>
|
||||
}
|
||||
},
|
||||
LowCheckbox: {
|
||||
default: (renderOpts, { row, column }, isStop) => {
|
||||
if (isStop) return (<div> <span>{row[column.field] == 'Y' ? '√' : ''}</span> {stopIcon} </div>)
|
||||
return <span>{row[column.field] == 'Y' ? '√' : ''}</span>
|
||||
},
|
||||
edit: (renderOpts, { row, column }) => {
|
||||
return <el-checkbox v-model={row[column.field]} true-value="Y" false-value="N" />
|
||||
}
|
||||
},
|
||||
LowSelect: {
|
||||
default: (renderOpts, { row, column }, isStop = false) => {
|
||||
if (column.field == 'dictType' && !row[column.field] && infoTableIsEdit({ row, column }, renderOpts)) {
|
||||
return <div class="c-#ccc text-12px">可配置</div>
|
||||
}
|
||||
const { typeKey } = renderOpts
|
||||
let dicObj = {}
|
||||
if (typeKey && row[typeKey]) {
|
||||
dicObj = renderOpts[`${row[typeKey]}DicObj`] || {}
|
||||
if (row[typeKey] == 'table' && column.field != 'dictTable' && row.dictTable) dicObj = dicObj[row.dictTable] || {}
|
||||
} else if (column.field == 'mainField') {
|
||||
dicObj = renderOpts.dicObj || {}
|
||||
if (row.mainTable) dicObj = dicObj[row.mainTable] || {}
|
||||
} else if (!typeKey) dicObj = renderOpts['dicObj'] || {}
|
||||
let value = row[column.field]
|
||||
if (value === '' || value === undefined || value === null) value = []
|
||||
if (typeof value == 'string') value = [value]
|
||||
if (value.length && Object.keys(dicObj).length) {
|
||||
const text: Array<string> = []
|
||||
value.forEach(key => text.push(dicObj[key] || key))
|
||||
value = text.join(' | ')
|
||||
}
|
||||
if (isStop) return (<div> <span>{value}</span> {stopIcon} </div>)
|
||||
return <span>{value}</span>
|
||||
},
|
||||
edit: (renderOpts, { row, rowIndex, column }) => {
|
||||
const { multiple, filterable, allowCreate, typeKey } = renderOpts
|
||||
let dicData = [] as any
|
||||
if (typeKey && row[typeKey]) {
|
||||
dicData = renderOpts[`${row[typeKey]}DicData`]
|
||||
if (row[typeKey] == 'table' && column.field != 'dictTable') {
|
||||
if (row.dictTable) dicData = dicData[row.dictTable]
|
||||
else dicData = [{ label: '请先选择 字典Table', value: '-1', disabled: true }]
|
||||
}
|
||||
} else if (column.field == 'mainField') {
|
||||
dicData = renderOpts.dicData
|
||||
if (row.mainTable) dicData = dicData[row.mainTable]
|
||||
else dicData = [{ label: '请先选择 外键-主表名', value: '-1', disabled: true }]
|
||||
} else if (!typeKey) dicData = renderOpts['dicData']
|
||||
|
||||
//树表
|
||||
if (column.field == 'dictTable' && ['tree', 'cascader'].includes(row.controlType) && row.dictType) {
|
||||
dicData = renderOpts['treeDicData']
|
||||
}
|
||||
|
||||
//设置禁用选择
|
||||
if (column.field == 'dictType') {
|
||||
dicData = dicData.map(item => {
|
||||
if (['tree', 'cascader', 'dicTableSelect'].includes(row.controlType) && item.value == 'dict') item.disabled = true
|
||||
else item.disabled = false
|
||||
return item
|
||||
})
|
||||
}
|
||||
return (
|
||||
<avue-select
|
||||
popper-class="vxe-table--ignore-clear"
|
||||
v-model={row[column.field]}
|
||||
placeholder={'请选择 ' + column.title}
|
||||
dic={dicData}
|
||||
multiple={multiple}
|
||||
filterable={filterable}
|
||||
allowCreate={allowCreate}
|
||||
onChange={() => renderOpts.events ? renderOpts.events.change(row, column.field, rowIndex) : ''}
|
||||
/>
|
||||
)
|
||||
}
|
||||
},
|
||||
LowSummaryBottomSql: {
|
||||
default: (renderOpts, { row, column }, isStop = false) => {
|
||||
const { dicObj } = renderOpts
|
||||
let value = row.summaryJson.sqlType
|
||||
if (value === '' || value === undefined || value === null) return ''
|
||||
if (value == 'custom') return <span>自定义SQL:<span style="color:#409EFF">{row.summaryJson.sqlValue}</span></span>
|
||||
if (Object.keys(dicObj).length) value = dicObj[value]
|
||||
if (isStop) return (<div> <span>{value}</span> {stopIcon} </div>)
|
||||
return <span>{value}</span>
|
||||
},
|
||||
edit: (renderOpts, { row, column }) => {
|
||||
const { dicData } = renderOpts
|
||||
const visible = row.summaryJson.sqlType == 'custom'
|
||||
return (
|
||||
<el-popover popper-class="low-summary-buttom-sql__popover" popper-style={{ width: 'auto', height: 'auto' }} visible={visible} placement='bottom-start' v-slots={{
|
||||
reference: () => (
|
||||
<avue-select
|
||||
popper-class="vxe-table--ignore-clear"
|
||||
v-model={row.summaryJson.sqlType}
|
||||
placeholder={'请选择 ' + column.title}
|
||||
dic={dicData}
|
||||
/>
|
||||
),
|
||||
default: () => (
|
||||
<div style={{ width: '400px', height: '200px' }} class="vxe-table--ignore-clear">
|
||||
<MonacoEditor v-model={row.summaryJson.sqlValue} language='mysql' editorOption={{ minimap: false }}></MonacoEditor>
|
||||
</div>
|
||||
),
|
||||
}}>
|
||||
</el-popover>
|
||||
)
|
||||
}
|
||||
},
|
||||
LowClickInput: {
|
||||
default: (renderOpts, { row, column, fieldProp }, isStop) => {
|
||||
const text = handleLowClickInput(row, { ...column, fieldProp })
|
||||
if (isStop) return (<div> <span>{text}</span> {stopIcon} </div>)
|
||||
return <span>{text}</span>
|
||||
},
|
||||
edit: (renderOpts, { row, rowIndex, column, fieldProp }) => {
|
||||
const prop = fieldProp || column.field
|
||||
const text = handleLowClickInput(row, { ...column, fieldProp })
|
||||
return <el-input class="my-cell" readonly onClick={() => renderOpts.events.click(row, prop, rowIndex)} text="text" value={text} placeholder={'请输入 ' + column.title} />
|
||||
}
|
||||
},
|
||||
virtualInput: {
|
||||
default: (renderOpts, { row, column }, isStop) => {
|
||||
if (!row.virtualType) isStop = true
|
||||
const option = { row, column, fieldProp: `virtual_${row.virtualType}_str` }
|
||||
if (row.virtualType == 'fun') return lowControl.LowInput.default(renderOpts, option, isStop)
|
||||
else return lowControl.LowClickInput.default(renderOpts, option, isStop)
|
||||
},
|
||||
edit: (renderOpts, { row, rowIndex, column }) => {
|
||||
const option = { row, column, rowIndex, fieldProp: `virtual_${row.virtualType}_str` }
|
||||
if (row.virtualType == 'fun') return lowControl.LowInput.edit(renderOpts, option)
|
||||
else return lowControl.LowClickInput.edit(renderOpts, option)
|
||||
}
|
||||
},
|
||||
LowMonacoEditorInput: {
|
||||
default: (renderOpts, { row, column, fieldProp }, isStop) => {
|
||||
const prop = fieldProp || column.field
|
||||
if (isStop) return (<div> <span>{row[prop]}</span> {stopIcon} </div>)
|
||||
return <span>{row[prop]}</span>
|
||||
},
|
||||
edit: (renderOpts, { row, column, fieldProp }) => {
|
||||
const prop = fieldProp || column.field
|
||||
return (
|
||||
<el-popover popper-style={{ width: 'auto', height: 'auto' }} placement='top' trigger='click' v-slots={{
|
||||
reference: () => (
|
||||
<el-input class="my-cell" text="text" v-model={row[prop]} clearable placeholder={'请输入 ' + column.title} onClick={() => renderOpts.events.click(row)} />
|
||||
),
|
||||
default: () => (
|
||||
<div>
|
||||
<div style={{ fontSize: '16px' }}>{row.fieldName}({row.fieldCode}) {column.title}</div>
|
||||
<div style={{ width: '600px', height: '200px' }} class="vxe-table--ignore-clear">
|
||||
<MonacoEditor v-model={row[prop]} language='javascript' editorOption={{ minimap: false }}></MonacoEditor>
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
}}>
|
||||
</el-popover>
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const key in lowControl) {
|
||||
const addObj = { renderDefault: lowControl[key].default, renderCell: lowControl[key].default }
|
||||
if (lowControl[key].edit) {
|
||||
addObj['renderEdit'] = (renderOpts: any, params) => {
|
||||
if (useType == 'table') {
|
||||
if (!infoTableIsEdit(params, renderOpts)) return lowControl[key].default(renderOpts, params, true)
|
||||
}
|
||||
return lowControl[key].edit(renderOpts, params)
|
||||
}
|
||||
}
|
||||
VxeUI.renderer.add(key, addObj)
|
||||
}
|
||||
}
|
||||
300
src/views/lowdesign/i18nConfig/ImportI18n.vue
Normal file
300
src/views/lowdesign/i18nConfig/ImportI18n.vue
Normal file
@@ -0,0 +1,300 @@
|
||||
<template>
|
||||
<div class="import-data-box pb-50px!">
|
||||
<div class="top-steps">
|
||||
<el-steps :active="stepValue" align-center finish-status="finish" process-status="finish">
|
||||
<el-step
|
||||
v-for="(title, index) in stepList"
|
||||
:key="title"
|
||||
:class="{ success: stepValue > index, active: stepValue == index }"
|
||||
>
|
||||
<template #title>
|
||||
<div class="text-14px"> {{ title }} </div>
|
||||
</template>
|
||||
<template #icon>
|
||||
<div class="step-icon"></div>
|
||||
</template>
|
||||
</el-step>
|
||||
</el-steps>
|
||||
</div>
|
||||
<div class="import-content">
|
||||
<div v-if="stepValue == 0">
|
||||
<div class="file-item flex" v-for="(item, key) in uploadList" :key="key">
|
||||
<div class="item-icon flex-basis-100px flex-shrink-0 bg-[var(--el-fill-color-light)]">
|
||||
<Icon color="#999" :icon="item.icon" :size="60"></Icon>
|
||||
</div>
|
||||
<div class="flex-1 pl-20px pt-10px pb-10px">
|
||||
<div class="text-16px">{{ item.title }}</div>
|
||||
<div class="text-14px pt-10px pb-10px">{{ item.tip }}</div>
|
||||
<div v-if="key == 'upload' && !files?.length">
|
||||
<el-button type="primary" :loading="item.loading" link @click="open()">
|
||||
{{ item.btnText }}
|
||||
</el-button>
|
||||
</div>
|
||||
<div v-if="key == 'upload' && files?.length" class="flex items-center">
|
||||
<Icon :size="14" icon="ep:link"></Icon>
|
||||
<div class="c-#409EFF pl-2px">{{ files[0].name }}</div>
|
||||
<div class="c-#999">({{ fileSizeFormatter(files[0], 'size', files[0].size) }})</div>
|
||||
<Icon :size="14" icon="ep:close" @click="reset" class="cursor-pointer"></Icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else-if="stepValue == 1" class="w-80% m-auto mt-40px mb-80px">
|
||||
<el-progress
|
||||
:text-inside="true"
|
||||
:stroke-width="18"
|
||||
:percentage="progress"
|
||||
:format="() => progress + '%'"
|
||||
/>
|
||||
<div class="text-center text-16px mt-10px">
|
||||
<span>
|
||||
{{ t('Avue.crud.importStep_3.playTip') }}
|
||||
({{ importInfo.handleCou }}/{{ importInfo.totalCou }})...
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else-if="stepValue == 2" class="mt-20px pb-40px">
|
||||
<div class="flex flex-col items-center">
|
||||
<Icon color="#67C23A" icon="mdi:success-circle-outline" :size="60"></Icon>
|
||||
<div class="text-20px pt-10px pb-10px">{{ t('Avue.crud.importStep_4.tip') }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="pos-absolute left-0 bottom-0 w-100% box-border px-20px py-10px flex justify-end bg-[var(--el-bg-color)] b-solid b-0px b-t-1px b-#f1f1f1 dark:b-[var(--el-border-color-dark)]"
|
||||
>
|
||||
<template v-if="stepValue == 0">
|
||||
<el-button
|
||||
type="primary"
|
||||
:disabled="!files || !files?.length"
|
||||
:loading="loading"
|
||||
@click="importData()"
|
||||
>
|
||||
<Icon v-if="!loading" icon="ep:view" :size="16"> </Icon>
|
||||
<span>开始导入数据</span>
|
||||
</el-button>
|
||||
</template>
|
||||
|
||||
<template v-if="stepValue == 2">
|
||||
<el-button type="default" @click="closeImport">
|
||||
<Icon icon="ep:close" :size="14" class="mr-3px"> </Icon>
|
||||
{{ t('dialog.close') }}
|
||||
</el-button>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import * as I18nApi from '@/api/design/i18n/index'
|
||||
import { fileSizeFormatter } from '@/utils/index'
|
||||
import { useFileDialog } from '@vueuse/core'
|
||||
defineOptions({ name: 'ImportData' })
|
||||
|
||||
interface Props {
|
||||
show: boolean
|
||||
localeMap: object[]
|
||||
isFull?: boolean
|
||||
}
|
||||
const props = defineProps<Props>()
|
||||
|
||||
const emit = defineEmits(['close-popup', 'reset-change'])
|
||||
const { t } = useI18n()
|
||||
|
||||
const stepList = [
|
||||
t('Avue.crud.importStep_1.title'),
|
||||
t('Avue.crud.importStep_3.title'),
|
||||
t('Avue.crud.importStep_4.title')
|
||||
]
|
||||
const uploadList = ref({
|
||||
upload: {
|
||||
icon: 'uiw:cloud-upload',
|
||||
title: '上传已经完善国际化的信息表',
|
||||
tip: t('Avue.crud.importStep_1.tip_2'),
|
||||
btnText: t('Avue.crud.importStep_1.btn_2'),
|
||||
loading: false
|
||||
}
|
||||
})
|
||||
const stepValue = ref(0)
|
||||
const loading = ref(false)
|
||||
const importInfo = ref({ batchCode: 0, handleCou: 0, totalCou: 0 })
|
||||
const timer = ref<any>(null)
|
||||
const isStop = ref(false)
|
||||
const viewRef = ref()
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { files, open, reset } = useFileDialog({
|
||||
multiple: false, //可选:是否可以多选文件
|
||||
accept: '.xls,.xlsx', //可选:自定义上传文件类型
|
||||
reset: true //可选:再次选择时是否把之前选的文件清除
|
||||
})
|
||||
|
||||
const progress = computed(() => {
|
||||
return Number(((importInfo.value.handleCou / importInfo.value.totalCou) * 100).toFixed(2))
|
||||
})
|
||||
|
||||
const init = () => {
|
||||
stepValue.value = 0
|
||||
isStop.value = false
|
||||
for (let key in importInfo.value) importInfo.value[key] = 0
|
||||
reset()
|
||||
}
|
||||
|
||||
const rollPoling = async (done?, type?) => {
|
||||
if (timer.value) clearTimeout(timer.value)
|
||||
const data = await I18nApi.getImportProgress(importInfo.value.batchCode).catch(() => false)
|
||||
console.log(data)
|
||||
if (data !== false) {
|
||||
importInfo.value.handleCou = data.handleCou
|
||||
importInfo.value.totalCou = data.totalCou
|
||||
if (type == 'init') {
|
||||
done()
|
||||
stepValue.value = 1
|
||||
}
|
||||
}
|
||||
|
||||
if (data.handleCou < data.totalCou) {
|
||||
timer.value = setTimeout(() => {
|
||||
rollPoling()
|
||||
}, 2000)
|
||||
} else stepValue.value = 2
|
||||
}
|
||||
|
||||
const importData = async () => {
|
||||
await message.confirm(t('Avue.crud.importTips.importDataBtnTip'))
|
||||
loading.value = true
|
||||
const file = files.value ? files.value[0] : ''
|
||||
const uploadRes = await I18nApi.uploadFile({ updateSupport: 1, file })
|
||||
const res = await I18nApi.uploadExcelData({
|
||||
fileUrl: uploadRes.data.fileUrl,
|
||||
exportLangParam: props.localeMap.map((item) => `${item['lang']}=${item['name']}`).join(','),
|
||||
file
|
||||
}).catch(() => false)
|
||||
const done = () => (loading.value = false)
|
||||
if (res) {
|
||||
importInfo.value.batchCode = res.data
|
||||
rollPoling(done, 'init')
|
||||
} else done()
|
||||
}
|
||||
|
||||
const handleClose = async (done) => {
|
||||
if (timer.value) clearTimeout(timer.value)
|
||||
emit('reset-change')
|
||||
done()
|
||||
}
|
||||
|
||||
const closeImport = () => {
|
||||
emit('reset-change')
|
||||
emit('close-popup')
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.show,
|
||||
(val) => {
|
||||
if (val && stepValue.value !== 1) {
|
||||
init()
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
watch(
|
||||
() => props?.isFull,
|
||||
(val) => {
|
||||
viewRef.value?.setCalcHeight(val ? 80 : 215)
|
||||
}
|
||||
)
|
||||
|
||||
defineExpose({ handleClose })
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.import-data-box {
|
||||
padding: 20px;
|
||||
|
||||
::v-deep(.el-steps) {
|
||||
.el-step__icon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.el-step__line {
|
||||
top: 7px;
|
||||
}
|
||||
|
||||
.step-icon {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 50%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.el-step__head {
|
||||
&.is-success {
|
||||
.step-icon {
|
||||
background-color: var(--el-color-success);
|
||||
}
|
||||
}
|
||||
|
||||
&.is-finish {
|
||||
.step-icon {
|
||||
background-color: var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
|
||||
&.is-wait {
|
||||
.step-icon {
|
||||
background-color: var(--el-text-color-placeholder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.el-step {
|
||||
&.active {
|
||||
.step-icon {
|
||||
background-color: var(--el-bg-color);
|
||||
border: 2px solid var(--el-color-primary);
|
||||
}
|
||||
|
||||
.el-step__line::before {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
width: 50%;
|
||||
height: 2px;
|
||||
background-color: var(--el-color-primary);
|
||||
content: '';
|
||||
}
|
||||
}
|
||||
|
||||
&.success {
|
||||
.el-step__line {
|
||||
background-color: var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.import-content {
|
||||
padding: 20px 40px;
|
||||
|
||||
.file-item {
|
||||
margin-bottom: 20px;
|
||||
border: var(--el-border);
|
||||
|
||||
.item-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 100px;
|
||||
border-right: var(--el-border);
|
||||
}
|
||||
}
|
||||
|
||||
.error-list {
|
||||
padding: 15px 20px;
|
||||
border: 1px solid #ece9e9;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
180
src/views/lowdesign/i18nConfig/index.vue
Normal file
180
src/views/lowdesign/i18nConfig/index.vue
Normal file
@@ -0,0 +1,180 @@
|
||||
<template>
|
||||
<ContentWrap>
|
||||
<avue-crud
|
||||
ref="crudRef"
|
||||
v-model="tableForm"
|
||||
v-model:page="tablePage"
|
||||
v-model:search="tableSearch"
|
||||
:table-loading="loading"
|
||||
:data="tableData"
|
||||
:option="tableOption"
|
||||
@refresh-change="getTableData"
|
||||
@size-change="sizeChange"
|
||||
@current-change="currentChange"
|
||||
>
|
||||
<template #menu-left="{ size }">
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
:size="size"
|
||||
@click="importDialog = true"
|
||||
:loading="exportLoading"
|
||||
v-hasPermi="['jeelowcode:i18n:import']"
|
||||
>
|
||||
<Icon icon="clarity:import-line"></Icon>
|
||||
<span>导入国际化配置</span>
|
||||
</el-button>
|
||||
<el-button
|
||||
type="success"
|
||||
plain
|
||||
:size="size"
|
||||
@click="handleExport"
|
||||
:loading="exportLoading"
|
||||
v-hasPermi="['jeelowcode:i18n:export']"
|
||||
>
|
||||
<Icon icon="clarity:export-line"></Icon>
|
||||
<span>导出国际化配置</span>
|
||||
</el-button>
|
||||
</template>
|
||||
<template #fileName="{ row }">
|
||||
<div
|
||||
class="cursor-pointer hover:c-#409EFF"
|
||||
@click="downloadByUrl({ url: row.fileUrl, fileName: row.fileName })"
|
||||
>
|
||||
{{ row.fileName }}
|
||||
</div>
|
||||
</template>
|
||||
</avue-crud>
|
||||
<!-- 导入 -->
|
||||
<DesignPopup
|
||||
v-model="importDialog"
|
||||
title="导入国际化配置"
|
||||
width="900px"
|
||||
:dialog-params="{ alignCenter: true }"
|
||||
:handleClose="importRef?.handleClose"
|
||||
>
|
||||
<template #default="{ isFull }">
|
||||
<ImportI18n
|
||||
ref="importRef"
|
||||
:show="importDialog"
|
||||
:isFull="isFull"
|
||||
:locale-map="localeStore.localeMap"
|
||||
@close-popup="importDialog = false"
|
||||
@reset-change="resetChange"
|
||||
></ImportI18n>
|
||||
</template>
|
||||
</DesignPopup>
|
||||
</ContentWrap>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import download from '@/utils/download'
|
||||
import { downloadByUrl } from '@/utils/filt'
|
||||
import * as I18nApi from '@/api/design/i18n'
|
||||
import { CommonStatusEnum } from '@/utils/constants'
|
||||
import ImportI18n from './ImportI18n.vue'
|
||||
import { useLocaleStoreWithOut } from '@/store/modules/locale'
|
||||
|
||||
defineOptions({ name: 'SystemPost' })
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { t } = useI18n() // 国际化
|
||||
const { getCurrPermi } = useCrudPermi()
|
||||
const localeStore = useLocaleStoreWithOut()
|
||||
|
||||
const loading = ref(true) // 列表的加载中
|
||||
const tableOption = reactive({
|
||||
align: 'center',
|
||||
headerAlign: 'center',
|
||||
addBtn: false,
|
||||
menu: false,
|
||||
column: {
|
||||
fileName: {
|
||||
label: '导入文件'
|
||||
},
|
||||
userName: {
|
||||
label: '操作人',
|
||||
dicData: getIntDictOptions(DICT_TYPE.COMMON_STATUS),
|
||||
rules: [{ required: true, message: '状态不能为空', trigger: 'blur' }],
|
||||
value: CommonStatusEnum.ENABLE
|
||||
},
|
||||
createTime: {
|
||||
label: '导入时间',
|
||||
display: false,
|
||||
type: 'datetime',
|
||||
startPlaceholder: '开始时间',
|
||||
endPlaceholder: '结束时间',
|
||||
formatter: (row, val, value, column) => {
|
||||
return dateFormatter(row, column, val)
|
||||
}
|
||||
}
|
||||
}
|
||||
}) //表格配置
|
||||
const tableForm = ref<{ id?: number }>({})
|
||||
const tableData = ref([])
|
||||
const tableSearch = ref({})
|
||||
const tablePage = ref({
|
||||
currentPage: 1,
|
||||
pageSize: 10,
|
||||
total: 0
|
||||
})
|
||||
const exportLoading = ref(false) // 导出的加载中
|
||||
const crudRef = ref()
|
||||
|
||||
const importDialog = ref(false)
|
||||
const importRef = ref()
|
||||
|
||||
useCrudHeight(crudRef)
|
||||
|
||||
/** 查询列表 */
|
||||
const getTableData = async () => {
|
||||
loading.value = true
|
||||
let searchObj = {
|
||||
pageNo: tablePage.value.currentPage,
|
||||
pageSize: tablePage.value.pageSize
|
||||
}
|
||||
try {
|
||||
const data = await I18nApi.getImportList(searchObj)
|
||||
tableData.value = data.records
|
||||
tablePage.value.total = data.total
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const resetChange = () => {
|
||||
tablePage.value.currentPage = 1
|
||||
getTableData()
|
||||
}
|
||||
|
||||
const sizeChange = (pageSize) => {
|
||||
tablePage.value.pageSize = pageSize
|
||||
resetChange()
|
||||
}
|
||||
|
||||
const currentChange = (currentPage) => {
|
||||
tablePage.value.currentPage = currentPage
|
||||
getTableData()
|
||||
}
|
||||
|
||||
/** 导出按钮操作 */
|
||||
const handleExport = async () => {
|
||||
try {
|
||||
// 导出的二次确认
|
||||
await message.exportConfirm()
|
||||
// 发起导出
|
||||
exportLoading.value = true
|
||||
const data = await I18nApi.exportExcelData({ param: localeStore.localeMap })
|
||||
download.excel(data, '国际化配置.xls')
|
||||
} catch {
|
||||
} finally {
|
||||
exportLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(async () => {
|
||||
await getTableData()
|
||||
})
|
||||
</script>
|
||||
922
src/views/lowdesign/moduleDesign/index.vue
Normal file
922
src/views/lowdesign/moduleDesign/index.vue
Normal file
@@ -0,0 +1,922 @@
|
||||
<template>
|
||||
<ContentWrap>
|
||||
<div class="flex gap-x-10px">
|
||||
<div class="flex-basis-180px flex-shrink-0">
|
||||
<avue-tree
|
||||
ref="treeRef"
|
||||
v-model="treeForm"
|
||||
:option="treeOption"
|
||||
:data="treeData"
|
||||
:permission="treePermission"
|
||||
:before-open="treeBeforeOpen"
|
||||
@node-contextmenu="treeNodeContextmenu"
|
||||
@node-click="treeNodeClick"
|
||||
@update="treeUpdate"
|
||||
@save="treeSave"
|
||||
@del="treeDel"
|
||||
>
|
||||
</avue-tree>
|
||||
</div>
|
||||
<avue-crud
|
||||
class="table-content flex-1"
|
||||
ref="crudRef"
|
||||
v-model="tableForm"
|
||||
v-model:page="tablePage"
|
||||
v-model:search="tableSearch"
|
||||
:table-loading="loading"
|
||||
:data="tableData"
|
||||
:option="tableOption"
|
||||
:permission="permission"
|
||||
:before-open="beforeOpen"
|
||||
@search-change="searchChange"
|
||||
@search-reset="resetChange"
|
||||
@row-save="rowSave"
|
||||
@row-update="rowUpdate"
|
||||
@row-del="rowDel"
|
||||
@refresh-change="getTableData"
|
||||
@size-change="sizeChange"
|
||||
@current-change="currentChange"
|
||||
@selection-change="selectionChange"
|
||||
>
|
||||
<template #menu-left="{ size }">
|
||||
<el-dropdown @command="menuLeftHandle" v-hasPermi="['jeelowcode:tab:js']">
|
||||
<el-button type="primary" :size="size" class="ml-10px">
|
||||
<Icon :size="14" icon="teenyicons:webpack-outline" />
|
||||
<span class="ml-3px!">前端增强</span>
|
||||
</el-button>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item command="js">JS增强</el-dropdown-item>
|
||||
<el-dropdown-item command="scss">SCSS增强</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</template>
|
||||
<template #menu="{ size, row, index }">
|
||||
<div class="flex justify-center flex-items-center">
|
||||
<el-button
|
||||
type="primary"
|
||||
text
|
||||
:size="size"
|
||||
@click="menuHandle({ type: 'edit', row, index })"
|
||||
v-hasPermi="['jeelowcode:tab:update']"
|
||||
>
|
||||
<Icon :size="14" icon="ep:edit-pen"></Icon>
|
||||
<span>编辑</span>
|
||||
</el-button>
|
||||
<span
|
||||
v-if="
|
||||
checkPermi(['jeelowcode:tab:update']) &&
|
||||
checkPermi([
|
||||
'jeelowcode:tab:create',
|
||||
'jeelowcode:tab:update',
|
||||
'jeelowcode:tab:delete'
|
||||
])
|
||||
"
|
||||
class="ml-8px mr-8px mt-2px inline-block h-16px w-1px bg-#e8e8e8 .dark:bg-[var(--el-border-color-dark)]"
|
||||
></span>
|
||||
<el-dropdown
|
||||
@command="menuHandle"
|
||||
v-if="
|
||||
checkPermi([
|
||||
'jeelowcode:tab:create',
|
||||
'jeelowcode:tab:update',
|
||||
'jeelowcode:tab:delete'
|
||||
])
|
||||
"
|
||||
>
|
||||
<div class="mt--2px cursor-pointer">
|
||||
<el-text :size="size" type="primary">
|
||||
更多
|
||||
<Icon :size="16" icon="iconamoon:arrow-down-2-light" />
|
||||
</el-text>
|
||||
</div>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item :command="{ type: 'test', row }"> 功能测试 </el-dropdown-item>
|
||||
<el-dropdown-item :command="{ type: 'routeAddress', row }">
|
||||
路由地址
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
v-if="checkPermi(['jeelowcode:tab:update'])"
|
||||
:command="{ type: 'jsUnlock', row }"
|
||||
>
|
||||
js增强解锁
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
v-if="checkPermi(['jeelowcode:tab:update'])"
|
||||
:command="{ type: 'scssUnlock', row }"
|
||||
>
|
||||
scss增强解锁
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
v-if="checkPermi(['jeelowcode:tab:delete'])"
|
||||
:command="{ type: 'del', row }"
|
||||
>
|
||||
删除
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</template>
|
||||
<template #lowSelectRadio="{ row }">
|
||||
<el-radio
|
||||
class="low-select-radio"
|
||||
v-model="radioValue"
|
||||
:label="row.id"
|
||||
@click="radioClick(row)"
|
||||
/>
|
||||
</template>
|
||||
<template #optionList-form>
|
||||
<avue-crud ref="moduleCrudRef" :option="moduleOption" :data="tableForm.optionList">
|
||||
<template #menu="{ size, index }">
|
||||
<el-button text :size="size" type="danger" @click="delModuleTableRow(index)">
|
||||
<span>删除</span>
|
||||
</el-button>
|
||||
</template>
|
||||
<template #darg="{ index }">
|
||||
<el-dropdown trigger="hover" placement="bottom" @command="dropdownCommand">
|
||||
<div>
|
||||
<Icon :size="20" icon="gg:menu-grid-r" />
|
||||
</div>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item :command="{ index: index, type: 'up' }" :disabled="index == 0"
|
||||
>向上移</el-dropdown-item
|
||||
>
|
||||
<el-dropdown-item
|
||||
:command="{ index: index, type: 'down' }"
|
||||
:disabled="index == tableForm.optionList?.length - 1"
|
||||
>向下移</el-dropdown-item
|
||||
>
|
||||
<el-dropdown-item :command="{ index: index, type: 'add' }"
|
||||
>插入一行</el-dropdown-item
|
||||
>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</template>
|
||||
<template #tabIcon="{ row }">
|
||||
<Icon v-if="row.tabIcon" :size="14" :icon="row.tabIcon" />
|
||||
</template>
|
||||
<template #tabIcon-form="{ row, column, size, disabled }">
|
||||
<IconSelectInput
|
||||
v-model="row.tabIcon"
|
||||
:prop="column.prop"
|
||||
:column="column"
|
||||
:size="size"
|
||||
:disabled="disabled"
|
||||
/>
|
||||
</template>
|
||||
<template #moduleValue-form="{ row }">
|
||||
<avue-select
|
||||
v-if="row.moduleType == 'table'"
|
||||
v-model="row.moduleValue"
|
||||
placeholder="请选择表单开发配置"
|
||||
:dic="tableDicData"
|
||||
:virtualize="true"
|
||||
:filterable="true"
|
||||
></avue-select>
|
||||
<avue-select
|
||||
v-else-if="row.moduleType == 'form'"
|
||||
v-model="row.moduleValue"
|
||||
placeholder="请选择自定义表单配置"
|
||||
:dic="formDicData"
|
||||
:virtualize="true"
|
||||
:filterable="true"
|
||||
></avue-select>
|
||||
<avue-select
|
||||
v-else-if="row.moduleType == 'report'"
|
||||
v-model="row.moduleValue"
|
||||
placeholder="请选择报表配置"
|
||||
:dic="reportDicData"
|
||||
:virtualize="true"
|
||||
:filterable="true"
|
||||
></avue-select>
|
||||
<avue-input
|
||||
v-else-if="row.moduleType == 'custom'"
|
||||
v-model="row.moduleValue"
|
||||
placeholder="模块所在文件路径"
|
||||
prepend="src/"
|
||||
>
|
||||
</avue-input>
|
||||
</template>
|
||||
<template #isLazy-header="{ column }">
|
||||
<span>{{ column.label }}</span>
|
||||
<div class="pos-absolute right-1px top-0">
|
||||
<el-tooltip
|
||||
class="box-item"
|
||||
effect="dark"
|
||||
content="第一次切换到当前标签页之前模块内容都不会加载"
|
||||
placement="top-start"
|
||||
>
|
||||
<Icon :size="14" icon="ep:info-filled"></Icon>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
<template #isLazy-form="{ row }">
|
||||
<el-checkbox v-model="row.isLazy" true-value="Y" false-value="N" />
|
||||
</template>
|
||||
<template #isRefresh-header="{ column }">
|
||||
<span>{{ column.label }}</span>
|
||||
<div class="pos-absolute right-1px top-0">
|
||||
<el-tooltip
|
||||
class="box-item"
|
||||
effect="dark"
|
||||
content="切换到当前标签页时,会刷新当前模块内容的数据"
|
||||
placement="top-start"
|
||||
>
|
||||
<Icon :size="14" icon="ep:info-filled"></Icon>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
<template #isRefresh-form="{ row }">
|
||||
<el-checkbox v-model="row.isRefresh" true-value="Y" false-value="N" />
|
||||
</template>
|
||||
<template #tabI18n-form="{ row }">
|
||||
<I18nInput v-model="row.tabI18n" :def-name="row.tabName"></I18nInput>
|
||||
</template>
|
||||
</avue-crud>
|
||||
</template>
|
||||
</avue-crud>
|
||||
</div>
|
||||
<DesignPopup v-model="MEDialog.value" v-bind="MEDialog.params" :isBodyFull="true">
|
||||
<template #default>
|
||||
<el-container class="h-100%">
|
||||
<el-main class="p-0!">
|
||||
<MonacoEditor
|
||||
class="flex-1"
|
||||
v-model="MEData.value"
|
||||
v-bind="MEData.params"
|
||||
></MonacoEditor>
|
||||
</el-main>
|
||||
<el-aside width="300px" v-if="MEDialog.otherParams">
|
||||
<TipView v-bind="MEDialog.otherParams"></TipView>
|
||||
</el-aside>
|
||||
</el-container>
|
||||
</template>
|
||||
</DesignPopup>
|
||||
<DesignPopup v-model="samplePopup" title="控件使用示例" :width="280">
|
||||
<template #default>
|
||||
<div class="text-center p-20px">
|
||||
<div class="b b-b-0 b-solid b-#f1f1f1 .dark:b-[var(--el-border-color-dark)]">
|
||||
<div
|
||||
v-for="(item, key) in controlPath"
|
||||
:key="key"
|
||||
class="h-50px flex items-center b-b b-b-solid b-#f1f1f1 .dark:b-[var(--el-border-color-dark)] pr-10px"
|
||||
>
|
||||
<div class="text-14px flex-1">{{ item.name }}</div>
|
||||
<el-button
|
||||
class="flex-basis-80px flex-shrink-0"
|
||||
size="small"
|
||||
type="primary"
|
||||
@click="copySampleStr(key)"
|
||||
>点击复制</el-button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</DesignPopup>
|
||||
</ContentWrap>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { TipView } from '../general/components/index'
|
||||
import * as ModuleApi from '@/api/design/module'
|
||||
import * as FormApi from '@/api/design/form'
|
||||
import * as TableApi from '@/api/design/table'
|
||||
import * as ReportApi from '@/api/design/report'
|
||||
import { ElLoading } from 'element-plus'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
import { useGroup } from '@/hooks/design/useGroup'
|
||||
import { checkPermi } from '@/utils/permission'
|
||||
import { ElButton } from 'element-plus'
|
||||
import { I18nInput } from '@/components/LowDesign/src/shareControl/index'
|
||||
import controlPath from '@/components/LowDesign/src/controlPath'
|
||||
import useCopyText from '@/hooks/design/useCopyText'
|
||||
import useMEDialog from '@/hooks/design/useMEDialog'
|
||||
import {
|
||||
moduleScssEnhanceExample,
|
||||
controlInitExample
|
||||
} from '@/components/LowDesign/src/utils/example'
|
||||
import { formattingLengStr } from '@/utils/lowDesign'
|
||||
defineOptions({ name: 'FormDesign' })
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { t } = useI18n() // 国际化
|
||||
const router = useRouter() // 路由
|
||||
const { getCurrPermi } = useCrudPermi()
|
||||
const { copyText } = useCopyText()
|
||||
const { MEDialog, MEData, openMEDialog } = useMEDialog()
|
||||
const loading = ref(true) // 列表的加载中
|
||||
//表格配置
|
||||
const tabsType = [
|
||||
{ label: '默认风格', value: 'default' },
|
||||
{ label: '卡片风格', value: 'card' },
|
||||
{ label: '带边框的卡片风格', value: 'border-card' }
|
||||
]
|
||||
const tabsPosition = [
|
||||
{ label: '上边', value: 'top' },
|
||||
{ label: '左边', value: 'left' },
|
||||
{ label: '右边', value: 'right' },
|
||||
{ label: '下边', value: 'bottom' }
|
||||
]
|
||||
const moduleType = [
|
||||
{ label: '表单开发', value: 'table' },
|
||||
{ label: '自定义表单', value: 'form' },
|
||||
{ label: '报表设计', value: 'report' },
|
||||
{ label: '自定义模块', value: 'custom' }
|
||||
]
|
||||
const getRules = (label, type?) => {
|
||||
return [
|
||||
{
|
||||
required: true,
|
||||
message: (type == 'select' ? '请选择' : '请输入') + label,
|
||||
trigger: type == 'select' ? 'change' : 'blur'
|
||||
}
|
||||
]
|
||||
}
|
||||
const tableOption = ref({
|
||||
align: 'center',
|
||||
headerAlign: 'center',
|
||||
searchMenuSpan: 6,
|
||||
searchMenuPosition: 'left',
|
||||
labelSuffix: ' ',
|
||||
dialogWidth: '90%',
|
||||
dialogFullscreen: true,
|
||||
dialogCustomClass: 'tabs-design-form',
|
||||
editBtn: false,
|
||||
delBtn: false,
|
||||
border: true,
|
||||
index: false,
|
||||
menuWidth: 180,
|
||||
span: 12,
|
||||
column: {
|
||||
lowSelectRadio: {
|
||||
label: '',
|
||||
display: false,
|
||||
width: 50,
|
||||
overHidden: false,
|
||||
fixed: true,
|
||||
showColumn: false
|
||||
},
|
||||
id: { label: '模块id', display: false, search: true, overHidden: true, width: 170 },
|
||||
moduleName: { label: '模块名称', display: false, search: true, overHidden: true },
|
||||
tabsType: { label: '风格类型', display: false, type: 'select', dicData: tabsType, width: 140 },
|
||||
tabsPosition: {
|
||||
label: '选项卡位置',
|
||||
display: false,
|
||||
type: 'select',
|
||||
dicData: tabsPosition,
|
||||
width: 120
|
||||
},
|
||||
updateTime: {
|
||||
label: '修改时间',
|
||||
type: 'datetime',
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
valueFormat: 'x',
|
||||
display: false,
|
||||
width: 160
|
||||
}
|
||||
},
|
||||
group: [
|
||||
{
|
||||
label: '基本信息',
|
||||
collapse: true,
|
||||
prop: 'group_info',
|
||||
column: {
|
||||
moduleName: { label: '模块名称', rules: getRules('模块名称') },
|
||||
groupModuleId: {
|
||||
label: '分组类型',
|
||||
type: 'tree',
|
||||
value: '',
|
||||
dicData: [],
|
||||
filterable: true,
|
||||
defaultExpandAll: true,
|
||||
props: { label: 'name', value: 'id' }
|
||||
},
|
||||
tabsType: {
|
||||
label: '风格类型',
|
||||
type: 'select',
|
||||
dicData: tabsType,
|
||||
value: 'border-card',
|
||||
clearable: false
|
||||
},
|
||||
tabsPosition: {
|
||||
label: '选项卡位置',
|
||||
type: 'select',
|
||||
dicData: tabsPosition,
|
||||
value: 'top',
|
||||
clearable: false
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '模块配置',
|
||||
collapse: true,
|
||||
prop: 'group_module',
|
||||
labelWidth: 20,
|
||||
column: {
|
||||
optionList: {
|
||||
hide: false,
|
||||
span: 24,
|
||||
dataType: 'array',
|
||||
value: [
|
||||
{
|
||||
$cellEdit: true,
|
||||
tabName: '',
|
||||
tabCode: '',
|
||||
moduleType: 'table',
|
||||
moduleValue: '',
|
||||
tabIcon: '',
|
||||
isLazy: false,
|
||||
isRefresh: false
|
||||
}
|
||||
],
|
||||
rules: [
|
||||
{
|
||||
validator: (rule, value, callback) => {
|
||||
if (value?.length) callback()
|
||||
else callback(new Error('请添加模块信息'))
|
||||
},
|
||||
trigger: 'blur'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
const tableForm = ref<any>({})
|
||||
const tableData = ref([])
|
||||
const tableSearch = ref({})
|
||||
const tablePage = ref({
|
||||
currentPage: 1,
|
||||
pageSize: 10,
|
||||
total: 0
|
||||
})
|
||||
const tableSelect = ref<any[]>([])
|
||||
const radioValue = ref('')
|
||||
const jsEnhanceData = ref({})
|
||||
const designPop = ref({
|
||||
value: {},
|
||||
dialog: false,
|
||||
params: {}
|
||||
})
|
||||
|
||||
const moduleOption = ref({
|
||||
addBtn: false,
|
||||
editBtn: false,
|
||||
addRowBtn: true,
|
||||
refreshBtn: false,
|
||||
columnBtn: false,
|
||||
addBtnText: '新增模块',
|
||||
menuWidth: 70,
|
||||
height: undefined,
|
||||
calcHeight: undefined,
|
||||
maxHeight: 300,
|
||||
index: true,
|
||||
column: {
|
||||
darg: { width: 45 },
|
||||
tabName: { label: '标签名称', cell: true, minWidth: 150, rules: getRules('标签名称') },
|
||||
tabCode: { label: '标签编码', cell: true, width: 150, rules: getRules('标签名称') },
|
||||
moduleType: {
|
||||
label: '模块类型',
|
||||
type: 'select',
|
||||
dicData: moduleType,
|
||||
value: 'table',
|
||||
cell: true,
|
||||
width: 140,
|
||||
clearable: false
|
||||
},
|
||||
moduleValue: {
|
||||
label: '模块内容',
|
||||
cell: true,
|
||||
minWidth: 180,
|
||||
rules: getRules('模块内容', 'select')
|
||||
},
|
||||
tabI18n: { label: '国际化配置', cell: true, width: 100 },
|
||||
tabIcon: { label: '标签图标', cell: true, width: 140 },
|
||||
isLazy: { label: '懒加载', cell: true, width: 70, value: 'N' },
|
||||
isRefresh: { label: '刷新数据', cell: true, width: 85, value: 'N' }
|
||||
}
|
||||
})
|
||||
|
||||
const tableDicData = ref([])
|
||||
const formDicData = ref([])
|
||||
const reportDicData = ref([])
|
||||
const samplePopup = ref(false)
|
||||
|
||||
const crudRef = ref()
|
||||
const treeRef = ref()
|
||||
|
||||
const isUnload = ref(false)
|
||||
|
||||
const permission = getCurrPermi(['jeelowcode:tab'])
|
||||
useCrudHeight(crudRef)
|
||||
const {
|
||||
treeForm,
|
||||
treeOption,
|
||||
treeData,
|
||||
groupValue,
|
||||
treePermission,
|
||||
treeBeforeOpen,
|
||||
treeNodeContextmenu,
|
||||
treeNodeClick,
|
||||
getTreeData,
|
||||
treeUpdate,
|
||||
treeSave,
|
||||
treeDel
|
||||
} = useGroup(treeRef, ModuleApi, () => resetChange())
|
||||
|
||||
const radioClick = (row) => {
|
||||
if (row.id == radioValue.value) {
|
||||
setTimeout(() => {
|
||||
radioValue.value = ''
|
||||
tableSelect.value = []
|
||||
}, 30)
|
||||
} else {
|
||||
tableSelect.value = [row]
|
||||
}
|
||||
}
|
||||
|
||||
const delModuleTableRow = (index) => {
|
||||
tableForm.value.optionList.splice(index, 1)
|
||||
}
|
||||
|
||||
const menuLeftHandle = (type) => {
|
||||
if (['js', 'scss'].includes(type) && tableSelect.value.length !== 1) {
|
||||
return message.warning('请勾选一条表格数据')
|
||||
}
|
||||
if (['js', 'scss'].includes(type)) openModuleEnhance(type)
|
||||
}
|
||||
|
||||
const dropdownCommand = ({ index, type }) => {
|
||||
if (type == 'up' || type == 'down') {
|
||||
const delItem = tableForm.value.optionList.splice(index, 1)[0]
|
||||
nextTick(() =>
|
||||
tableForm.value.optionList.splice(type == 'up' ? index - 1 : index + 1, 0, delItem)
|
||||
)
|
||||
} else if (type == 'add') {
|
||||
tableForm.value.optionList.splice(index + 1, 0, { $cellEdit: true, moduleType: 'table' })
|
||||
}
|
||||
}
|
||||
|
||||
const handleWebEnhance = (moduleId, enhanceType) => {
|
||||
return new Promise(async (resolve) => {
|
||||
let apiType = 'updateJsData'
|
||||
let apiData = {
|
||||
id: jsEnhanceData.value['id'],
|
||||
tabId: moduleId,
|
||||
jsType: enhanceType,
|
||||
jsJson: MEData.value.value
|
||||
}
|
||||
if (!apiData.id) {
|
||||
apiType = 'saveJsData'
|
||||
delete apiData.id
|
||||
}
|
||||
const data = await ModuleApi[apiType](apiData).catch(() => false)
|
||||
if (data) message.success('保存成功')
|
||||
resolve(data)
|
||||
})
|
||||
}
|
||||
|
||||
const getAllDic = () => {
|
||||
TableApi.getAllDbDicData({ onlyTableName: 'Y' }).then(async (res) => {
|
||||
tableDicData.value = res.map((item) => {
|
||||
return { label: `${item.tableDescribe}[${item.tableName}]`, value: item.tableId }
|
||||
})
|
||||
})
|
||||
FormApi.getFormList({}).then((res) => {
|
||||
formDicData.value = res.records.map((item) => {
|
||||
return { label: item.desformName, value: item.id }
|
||||
})
|
||||
})
|
||||
ReportApi.getDbList({}).then((res) => {
|
||||
reportDicData.value = res.records.map((item) => {
|
||||
return { label: item.reportName, value: item.reportCode }
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const menuHandle = async ({ type, row, index }) => {
|
||||
if (type == 'edit') crudRef.value.rowEdit(row, index)
|
||||
else if (type == 'test') router.push({ path: '/low/module/test/' + row.id })
|
||||
else if (type == 'routeAddress') showRouteAddress(row)
|
||||
else if (type.indexOf('Unlock') != -1) unlockEnhance(type, row)
|
||||
else if (type == 'del') rowDel(row)
|
||||
}
|
||||
|
||||
const showRouteAddress = (row) => {
|
||||
const url = `module/view/` + row.id
|
||||
message.alert(`路由地址:</br>${url}`, '菜单的路由地址', {
|
||||
confirmButtonText: '复制',
|
||||
dangerouslyUseHTMLString: true,
|
||||
callback: (action) => {
|
||||
if (action == 'confirm') copyText(url)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const unlockEnhance = (type, row) => {
|
||||
const enhanceType = type.split('Unlock')[0]
|
||||
message.confirm(`是否强制解除${enhanceType}增强锁定?`, '提示').then(async () => {
|
||||
await ModuleApi.unlockJs(row.id, enhanceType)
|
||||
message.success('解除锁定成功')
|
||||
if (type.indexOf('Open') !== -1 && tableSelect.value.length) menuLeftHandle(enhanceType)
|
||||
})
|
||||
}
|
||||
|
||||
const openModuleEnhance = async (enhanceType) => {
|
||||
loading.value = true
|
||||
jsEnhanceData.value = { jsJson: '' }
|
||||
const moduleId = tableSelect.value[0].id
|
||||
const detailData = await ModuleApi.getJsDetail({
|
||||
tabId: moduleId,
|
||||
type: enhanceType,
|
||||
lock: true
|
||||
}).catch(() => false)
|
||||
loading.value = false
|
||||
if (detailData === false) return
|
||||
if (detailData) {
|
||||
if (detailData.timeStr) {
|
||||
message
|
||||
.confirm(
|
||||
`<div>
|
||||
<div style="font-size:16px"><span style="font-size:14px">锁定操作人:</span>${detailData.userName}</div>
|
||||
<div style="font-size:16px"><span style="font-size:14px">开始锁定时间:</span>${detailData.timeStr}</div>
|
||||
<div style="color:#E6A23C">注:如果确认没有人正在编辑,可点击下方强制解锁</div>
|
||||
</div>`,
|
||||
'当前增强已锁定',
|
||||
{
|
||||
dangerouslyUseHTMLString: true,
|
||||
cancelButtonText: '关闭',
|
||||
confirmButtonText: '强制解锁'
|
||||
}
|
||||
)
|
||||
.then(() => {
|
||||
menuHandle({
|
||||
type: `${enhanceType}UnlockOpen`,
|
||||
row: tableSelect.value[0],
|
||||
index: tableSelect.value[0].$index
|
||||
})
|
||||
})
|
||||
return
|
||||
}
|
||||
if (detailData.jsJson) {
|
||||
jsEnhanceData.value = detailData
|
||||
}
|
||||
}
|
||||
let params = {
|
||||
title: `${enhanceType}增强 【${tableSelect.value[0].moduleName}】`,
|
||||
fullscreen: true,
|
||||
handleClose: async (done) => {
|
||||
await ModuleApi.unlockJs(moduleId, enhanceType)
|
||||
isUnload.value = false
|
||||
done()
|
||||
},
|
||||
footerBtn: [
|
||||
{
|
||||
params: { type: 'primary' },
|
||||
name: '保 存',
|
||||
icon: 'mingcute:save-line',
|
||||
loading: true,
|
||||
clickFun: async (loading) => {
|
||||
const id = await handleWebEnhance(moduleId, enhanceType)
|
||||
if (id) jsEnhanceData.value['id'] = id
|
||||
loading()
|
||||
}
|
||||
},
|
||||
{
|
||||
params: { type: 'success' },
|
||||
name: '保存并关闭',
|
||||
icon: 'mingcute:save-line',
|
||||
loading: true,
|
||||
clickFun: async (loading) => {
|
||||
const bool = await handleWebEnhance(moduleId, enhanceType)
|
||||
await ModuleApi.unlockJs(moduleId, enhanceType)
|
||||
loading()
|
||||
if (bool) {
|
||||
MEDialog.value.value = false
|
||||
isUnload.value = false
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
params: {},
|
||||
name: '关 闭',
|
||||
icon: 'material-symbols:close',
|
||||
clickFun: async () => {
|
||||
await message.confirm('是否确认关闭?')
|
||||
await ModuleApi.unlockJs(moduleId, enhanceType)
|
||||
MEDialog.value.value = false
|
||||
isUnload.value = false
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
if (enhanceType == 'js') {
|
||||
params['providerType'] = 'moduleJsEnhance'
|
||||
params['otherParams'] = { type: 'simple', width: '300px', tipKeyList: ['ModuleJsEnhance'] }
|
||||
params['language'] = 'javascript'
|
||||
params['headerBtn'] = [
|
||||
{
|
||||
params: { type: 'success' },
|
||||
name: '控件使用示例',
|
||||
icon: 'jam:code-sample',
|
||||
clickFun: async () => (samplePopup.value = true)
|
||||
}
|
||||
]
|
||||
if (!jsEnhanceData.value['jsJson']) {
|
||||
jsEnhanceData.value['jsJson'] = `return {
|
||||
|
||||
}`
|
||||
}
|
||||
} else if (enhanceType == 'scss') {
|
||||
params['language'] = 'scss'
|
||||
if (!jsEnhanceData.value['jsJson']) {
|
||||
jsEnhanceData.value['jsJson'] = moduleScssEnhanceExample(moduleId)
|
||||
}
|
||||
}
|
||||
|
||||
openMEDialog({ params, prop: 'jsJson' }, jsEnhanceData.value)
|
||||
isUnload.value = true
|
||||
}
|
||||
|
||||
const copySampleStr = (type) => {
|
||||
copyText(controlInitExample(type))
|
||||
samplePopup.value = false
|
||||
}
|
||||
|
||||
const selectionChange = (data) => {
|
||||
tableSelect.value = data
|
||||
}
|
||||
|
||||
/** 查询列表 */
|
||||
const getTableData = async () => {
|
||||
loading.value = true
|
||||
let searchObj = {
|
||||
...tableSearch.value,
|
||||
pageNo: tablePage.value.currentPage,
|
||||
pageSize: tablePage.value.pageSize
|
||||
}
|
||||
if (groupValue.value) searchObj['groupModuleId'] = groupValue.value
|
||||
for (let key in searchObj) if (searchObj[key] === '') delete searchObj[key]
|
||||
try {
|
||||
const data = await ModuleApi.getModuleList(searchObj)
|
||||
tableData.value = data.records
|
||||
tablePage.value.total = data.total
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const searchChange = (params, done) => {
|
||||
if (Object.keys(params).length && groupValue.value) {
|
||||
treeRef.value.setCurrentKey(0)
|
||||
groupValue.value = 0
|
||||
}
|
||||
tablePage.value.currentPage = 1
|
||||
getTableData().finally(() => {
|
||||
done()
|
||||
})
|
||||
}
|
||||
|
||||
/** 清空按钮操作 */
|
||||
const resetChange = () => {
|
||||
searchChange({}, () => {})
|
||||
}
|
||||
|
||||
const sizeChange = (pageSize) => {
|
||||
tablePage.value.pageSize = pageSize
|
||||
resetChange()
|
||||
}
|
||||
|
||||
const currentChange = (currentPage) => {
|
||||
tablePage.value.currentPage = currentPage
|
||||
getTableData()
|
||||
}
|
||||
|
||||
/** 表单打开前 */
|
||||
const beforeOpen = async (done, type) => {
|
||||
const groupData = cloneDeep(treeData.value)
|
||||
const groupModuleId = tableOption.value.group[0].column.groupModuleId
|
||||
if (groupModuleId) groupModuleId.dicData = groupData[0].children
|
||||
if (['edit', 'view'].includes(type) && tableForm.value['id']) {
|
||||
loading.value = true
|
||||
const data = await ModuleApi.getModuleDetail(tableForm.value['id'])
|
||||
tableForm.value = {
|
||||
...data.tab,
|
||||
optionList: data.optionList.map((item) => {
|
||||
if (type == 'edit') item.$cellEdit = true
|
||||
return item
|
||||
})
|
||||
}
|
||||
loading.value = false
|
||||
}
|
||||
if (type == 'add' && groupValue.value) {
|
||||
tableForm.value.groupModuleId = groupValue.value
|
||||
}
|
||||
done()
|
||||
}
|
||||
|
||||
const initI18nData = (list) => {
|
||||
if (!list?.length) return []
|
||||
return list.map((item) => {
|
||||
item.tabI18n = formattingLengStr(item.tabI18n, item.tabName)
|
||||
return item
|
||||
})
|
||||
}
|
||||
|
||||
/** 新增操作 */
|
||||
const rowSave = async (form: object, done, loading, isId?) => {
|
||||
const elLoading = ElLoading.service({ fullscreen: true })
|
||||
const saveData = { tab: { ...form }, optionList: initI18nData(form['optionList']) }
|
||||
delete saveData.tab['optionList']
|
||||
let bool = await ModuleApi.saveModuleData(saveData).catch(() => false)
|
||||
if (bool) {
|
||||
message.success(t('common.createSuccess'))
|
||||
resetChange()
|
||||
if (isId) done(bool)
|
||||
else done()
|
||||
} else loading()
|
||||
elLoading.close()
|
||||
}
|
||||
|
||||
/** 编辑操作 */
|
||||
const rowUpdate = async (form, index, done, loading, onData?) => {
|
||||
const editData = { tab: { ...form }, optionList: initI18nData(form['optionList']) }
|
||||
delete editData.tab['optionList']
|
||||
let bool = await ModuleApi.updateModuleData(editData).catch(() => false)
|
||||
if (bool) {
|
||||
message.success(t('common.updateSuccess'))
|
||||
if (!onData) getTableData()
|
||||
done()
|
||||
} else loading()
|
||||
}
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const rowDel = async (data) => {
|
||||
try {
|
||||
// 删除的二次确认
|
||||
await message.delConfirm()
|
||||
// 发起删除
|
||||
await ModuleApi.deleteModuleData(data instanceof Array ? data : [data.id])
|
||||
message.success(t('common.delSuccess'))
|
||||
// 刷新列表
|
||||
await getTableData()
|
||||
} catch {}
|
||||
}
|
||||
|
||||
const beforeUnload = (event) => {
|
||||
if (isUnload.value) return (event.returnValue = '您确定要关闭页面吗?')
|
||||
}
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(async () => {
|
||||
window.addEventListener('beforeunload', beforeUnload)
|
||||
getTableData()
|
||||
getTreeData()
|
||||
getAllDic()
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener('beforeunload', beforeUnload)
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.table-content {
|
||||
max-width: calc(100% - 190px);
|
||||
}
|
||||
</style>
|
||||
<style lang="scss">
|
||||
.form-design-address-box {
|
||||
.el-message-box__container {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<style lang="scss">
|
||||
.tabs-design-form {
|
||||
.el-dialog__body {
|
||||
padding-top: 0 !important;
|
||||
}
|
||||
|
||||
.avue-crud {
|
||||
.avue-crud__empty {
|
||||
padding: 0;
|
||||
|
||||
.el-empty {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.el-empty__description {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
70
src/views/lowdesign/moduleView/index.vue
Normal file
70
src/views/lowdesign/moduleView/index.vue
Normal file
@@ -0,0 +1,70 @@
|
||||
<template>
|
||||
<LowModule ref="lowTabsRef" v-if="moduleId" :moduleId="moduleId"></LowModule>
|
||||
<div v-else>无权限访问</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useTagsViewStore } from '@/store/modules/tagsView'
|
||||
import { CACHE_KEY, useCache } from '@/hooks/web/useCache'
|
||||
|
||||
const route = useRoute()
|
||||
const tagsViewStore = useTagsViewStore()
|
||||
const { wsCache } = useCache()
|
||||
const moduleId = ref('')
|
||||
const timer = ref<any>(null)
|
||||
|
||||
const lowTabsRef = ref()
|
||||
|
||||
const setTestTitle = () => {
|
||||
timer.value = setInterval(() => {
|
||||
const moduleName = lowTabsRef.value?.moduleInfo?.moduleName
|
||||
if (moduleName) {
|
||||
if (timer.value) clearInterval(timer.value)
|
||||
tagsViewStore.visitedViews = tagsViewStore.visitedViews.map((tag) => {
|
||||
if (tag.path == '/low/module/test/' + moduleId.value) {
|
||||
if (tag.meta) tag.meta.title = '功能测试:' + moduleName
|
||||
}
|
||||
return tag
|
||||
})
|
||||
}
|
||||
}, 100)
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (route.params.id) {
|
||||
//功能测试
|
||||
const menus = wsCache.get(CACHE_KEY.USER).menus
|
||||
const praentArr = route.meta.activeMenu?.split('/').filter((path) => path) || []
|
||||
let isPermission = false
|
||||
if (praentArr.length) {
|
||||
praentArr[0] = '/' + praentArr[0]
|
||||
let findIndex = 0
|
||||
const findPath = (menuList) => {
|
||||
for (const index in menuList) {
|
||||
if (menuList[index].path == praentArr[findIndex]) {
|
||||
if (findIndex == praentArr.length - 1) isPermission = true
|
||||
else findIndex++
|
||||
if (menuList[index].children) findPath(menuList[index].children)
|
||||
if (isPermission) break
|
||||
}
|
||||
}
|
||||
}
|
||||
findPath(menus)
|
||||
}
|
||||
if (isPermission && typeof route.params.id == 'string') moduleId.value = route.params.id
|
||||
setTestTitle()
|
||||
} else {
|
||||
const pathList = route.path.split('/')
|
||||
const length = pathList.length - 1
|
||||
if (/\d$/.test(pathList[length])) {
|
||||
moduleId.value = pathList[length]
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
if (timer.value) clearInterval(timer.value)
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
246
src/views/lowdesign/reportDesign/components/TableInfo.vue
Normal file
246
src/views/lowdesign/reportDesign/components/TableInfo.vue
Normal file
@@ -0,0 +1,246 @@
|
||||
<template>
|
||||
<avue-tabs ref="tabsRef" :option="tabsOption" @change="tabsHandleChange"></avue-tabs>
|
||||
<InfoVxeTopBtn
|
||||
v-show="tabsValue.edit"
|
||||
:selectNum="tabsValue.dataKey ? infoSelect[tabsValue.dataKey].length : 0"
|
||||
:tabItem="tabsValue"
|
||||
:size="size"
|
||||
@cell-add="cellAddData"
|
||||
@del-data="delTableInfoData"
|
||||
@order-data="setInfoOrder"
|
||||
/>
|
||||
<template v-for="tab in tabsOption.column" :key="tab.prop">
|
||||
<div v-show="tabsValue.prop === tab.prop">
|
||||
<InfoVxeTable
|
||||
v-model="infoData[tab.dataKey]"
|
||||
:ref="(el) => (tableRefObj[tab.prop] = el)"
|
||||
:tabItem="tab"
|
||||
:column="tableInfoOption.infoColumn[`${tab.key}Column`]"
|
||||
@selection-change="infoSelectionChange"
|
||||
@cell-click="cellClick"
|
||||
@dropdown-command="dorpdownHandleCommand"
|
||||
></InfoVxeTable>
|
||||
</div>
|
||||
</template>
|
||||
<DesignPopup
|
||||
v-model="optionsDialog.dialog.value"
|
||||
v-bind="optionsDialog.dialog"
|
||||
:isBodyFull="true"
|
||||
>
|
||||
<template #default>
|
||||
<template v-for="(control, key) in optionComponents" :key="key">
|
||||
<component
|
||||
:ref="(el) => (optionRef[key] = el)"
|
||||
v-if="key == optionsDialog.controlType"
|
||||
:is="control"
|
||||
v-model="optionsDialog.controlValue"
|
||||
v-bind="optionsDialog.controlData"
|
||||
:show="optionsDialog.dialog.value"
|
||||
></component>
|
||||
</template>
|
||||
</template>
|
||||
</DesignPopup>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { SqlOption, InfoVxeTable, InfoVxeTopBtn } from '../../tableDesign/components'
|
||||
import { MonacoEditor } from '@/components/MonacoEditor/index'
|
||||
import { tableInfoOption } from '../designData'
|
||||
import { formattingLengStr } from '@/utils/lowDesign'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
import * as DictDataApi from '@/api/system/dict/dict.type'
|
||||
|
||||
defineOptions({ name: 'TableInfo' })
|
||||
const message = useMessage()
|
||||
|
||||
interface Props {
|
||||
formType: string
|
||||
editInfoData: any
|
||||
size: any
|
||||
}
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
formType: ''
|
||||
})
|
||||
const tabsOption = ref({
|
||||
column: [{ label: '字段属性', prop: 'tab_field', key: 'field', dataKey: 'basics', edit: true }]
|
||||
})
|
||||
const tabsValue = ref(tabsOption.value.column[0])
|
||||
|
||||
const infoData = ref({
|
||||
basics: [] as any[]
|
||||
})
|
||||
const infoSelect = ref({
|
||||
basics: []
|
||||
})
|
||||
const tableInfoDefault = ref<any[]>([])
|
||||
const optionsDialog = ref<any>({ dialog: {}, controlData: {} })
|
||||
const tableRefObj = ref({})
|
||||
|
||||
const optionComponents = markRaw({
|
||||
sqlOption: SqlOption,
|
||||
meditor: MonacoEditor
|
||||
})
|
||||
const optionRef = ref({})
|
||||
const tabsRef = ref()
|
||||
|
||||
const fieldList = computed(() => {
|
||||
let dicData: Array<{ label: string; value: string; type: string }> = []
|
||||
infoData.value.basics.forEach((item) => {
|
||||
if (item.fieldCode && item.isDb == 'Y')
|
||||
dicData.push({
|
||||
label: `${item.fieldCode}${item.fieldName ? '(' + item.fieldName + ')' : ''}`,
|
||||
value: item.fieldCode,
|
||||
type: item.fieldType
|
||||
})
|
||||
})
|
||||
return dicData
|
||||
})
|
||||
|
||||
watch(
|
||||
() => fieldList.value,
|
||||
(dicData: any) => {
|
||||
const dicDataObj = {}
|
||||
dicData.forEach(({ value, label }) => (dicDataObj[value] = label))
|
||||
}
|
||||
)
|
||||
|
||||
const dorpdownHandleCommand = (command) => {
|
||||
let { dataKey } = tabsValue.value
|
||||
if (!dataKey) return
|
||||
let { index, type } = command
|
||||
if (type == 'up' || type == 'down') {
|
||||
const delItem = infoData.value[dataKey].splice(index, 1)[0]
|
||||
nextTick(() => infoData.value[dataKey].splice(type == 'up' ? index - 1 : index + 1, 0, delItem))
|
||||
} else if (type == 'add') cellAddData(index + 1)
|
||||
}
|
||||
const setInfoOrder = () => {
|
||||
let { dataKey } = tabsValue.value
|
||||
if (!dataKey) return
|
||||
message
|
||||
.prompt('请输入序号(格式:原序号/最终序号)', '调整排序', { inputValue: '/', type: '' })
|
||||
.then(({ value }) => {
|
||||
let orderArr = value.split('/')
|
||||
if (orderArr.length == 2) {
|
||||
const delItem = infoData.value[dataKey].splice(Number(orderArr[0].trim()) - 1, 1)[0]
|
||||
nextTick(() => infoData.value[dataKey].splice(Number(orderArr[1].trim()) - 1, 0, delItem))
|
||||
} else message.error('请输入正确的格式,如:2/3')
|
||||
})
|
||||
.catch(() => {})
|
||||
}
|
||||
|
||||
const infoSelectionChange = (selectObj) => {
|
||||
let { dataKey } = tabsValue.value
|
||||
if (!dataKey) return
|
||||
infoSelect.value[dataKey] = selectObj.records
|
||||
}
|
||||
|
||||
const delTableInfoData = () => {
|
||||
let { dataKey, prop } = tabsValue.value
|
||||
if (!dataKey) return
|
||||
const keyArr = infoSelect.value[dataKey].map((item) => item._X_ROW_KEY)
|
||||
infoData.value[dataKey] = infoData.value[dataKey].filter(
|
||||
(item) => !keyArr.includes(item._X_ROW_KEY)
|
||||
)
|
||||
tableRefObj.value[prop].vxeTableRef.clearCheckboxRow()
|
||||
infoSelect.value[dataKey] = []
|
||||
}
|
||||
|
||||
const tabsHandleChange = (column) => {
|
||||
tabsValue.value = column
|
||||
}
|
||||
|
||||
const setTabsValue = (prop) => {
|
||||
tabsOption.value.column.forEach((item, index) => {
|
||||
if (item.prop == prop) {
|
||||
tabsRef.value.changeTabs(index)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const cellAddData = (addIndex) => {
|
||||
let { key, dataKey } = tabsValue.value
|
||||
if (!dataKey) return
|
||||
let addData = cloneDeep(tableInfoOption.infoDefaultData[dataKey]) || {}
|
||||
let index = infoData.value[dataKey].length
|
||||
infoData.value[dataKey].splice(addIndex === undefined ? index : addIndex, 0, addData)
|
||||
tableScrollIndex(key, index, addIndex)
|
||||
}
|
||||
|
||||
const tableScrollIndex = (key, index, addIndex?) => {
|
||||
setTimeout(() => {
|
||||
const tableBodyDom = document.querySelector(`.${key}-vxe-table .vxe-table--body-wrapper`)
|
||||
if (tableBodyDom) {
|
||||
const bool = addIndex === undefined
|
||||
tableBodyDom.scrollTop =
|
||||
(bool ? index : addIndex) * 40 - (bool ? 0 : tableBodyDom['offsetHeight'] / 2)
|
||||
}
|
||||
tableRefObj.value[`tab_${key}`].vxeTableRef.setEditRow(
|
||||
tableRefObj.value[`tab_${key}`].vxeTableRef.getData(addIndex ? addIndex : index)
|
||||
)
|
||||
}, 300)
|
||||
}
|
||||
|
||||
const cellClick = ({ rowIndex }) => {
|
||||
let { prop } = tabsValue.value
|
||||
tableRefObj.value[prop].vxeTableRef.setEditRow(
|
||||
tableRefObj.value[prop].vxeTableRef.getData(rowIndex)
|
||||
)
|
||||
}
|
||||
|
||||
const initEditInfoData = () => {
|
||||
const data = tableInfoOption.formattingInitData(props.editInfoData)
|
||||
const fieldList: any[] = []
|
||||
tableInfoDefault.value = data.infoData.filter((item) => {
|
||||
fieldList.push(cloneDeep(item))
|
||||
return false
|
||||
})
|
||||
infoData.value.basics = fieldList
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
tableInfoDefault.value = []
|
||||
infoData.value.basics = []
|
||||
if (props.formType != 'add') initEditInfoData()
|
||||
const { fieldColumn } = tableInfoOption.infoColumn
|
||||
fieldColumn.labelI18n.editRender.events = {
|
||||
click: (row) => {
|
||||
nextTick(() => {
|
||||
tableRefObj.value['tab_field'].vxeTableRef.setRow(row, {
|
||||
labelI18n: formattingLengStr(row.labelI18n, row.fieldName)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
DictDataApi.getSimpleDictTypeList().then((dicData) => {
|
||||
const dicObj = {}
|
||||
dicData = dicData.map(({ type, name }) => {
|
||||
dicObj[type] = `${type}(${name})`
|
||||
return { label: dicObj[type], value: type }
|
||||
})
|
||||
Object.assign(fieldColumn.dictCode.editRender, { dicData, dicObj })
|
||||
})
|
||||
})
|
||||
|
||||
defineExpose({
|
||||
infoData,
|
||||
tableInfoDefault,
|
||||
fieldList,
|
||||
tableRefObj,
|
||||
setTabsValue,
|
||||
tableScrollIndex,
|
||||
initEditInfoData
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.low-summary-buttom-sql__popover {
|
||||
.el-popper__arrow {
|
||||
left: 20px !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<style lang="scss" scoped>
|
||||
::v-deep(.virtual-hide-row) {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
5
src/views/lowdesign/reportDesign/components/index.ts
Normal file
5
src/views/lowdesign/reportDesign/components/index.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import TableInfo from './TableInfo.vue'
|
||||
|
||||
export {
|
||||
TableInfo
|
||||
}
|
||||
222
src/views/lowdesign/reportDesign/designData.ts
Normal file
222
src/views/lowdesign/reportDesign/designData.ts
Normal file
@@ -0,0 +1,222 @@
|
||||
import { verifyReportCode } from '@/api/design/report';
|
||||
import { getAllDbDicData } from '@/api/design/table';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
// 数据字典
|
||||
export const dicObj = {
|
||||
dic_whether: [
|
||||
{ label: '是', value: 'Y' },
|
||||
{ label: '否', value: 'N' },
|
||||
],
|
||||
tableSelect: [
|
||||
{ label: '多选', value: 'multiple' },
|
||||
{ label: '单选', value: 'radio' },
|
||||
{ label: '禁用', value: 'disabled' },
|
||||
],
|
||||
dataConfig: [
|
||||
{ label: '分页', value: 'page' },
|
||||
|
||||
],
|
||||
dataConfigSelect: [
|
||||
{ label: '需登录', value: 'authFalse', desc: '访问该表接口:需登录' },
|
||||
{ label: '需登录、鉴权', value: 'authTrue', desc: '访问该表接口:需登录并且需要配置菜单权限' },
|
||||
{ label: '不登录可查询数据', value: 'authOpen', desc: '访问该表查询接口:不需要登录' },
|
||||
],
|
||||
tableConfig: [
|
||||
{ label: '固定表格高度', value: 'height' },
|
||||
{ label: '序号列', value: 'index' },
|
||||
{ label: '纵向边框', value: 'border' },
|
||||
{ label: '斑马纹样式', value: 'stripe' },
|
||||
{ label: '同步', value: 'sync' },
|
||||
],
|
||||
fieldType: [
|
||||
{ label: '字符串 String', value: 'String' },
|
||||
{ label: '整数 Integer', value: 'Integer' },
|
||||
{ label: '大整数 BigInt', value: 'BigInt' },
|
||||
{ label: '小数 BigDecimal', value: 'BigDecimal' },
|
||||
{ label: '日期 Date', value: 'Date' },
|
||||
{ label: '时间 Time', value: 'Time' },
|
||||
{ label: '日期时间 DateTime', value: 'DateTime' },
|
||||
{ label: '文本 Text', value: 'Text' },
|
||||
{ label: '大文本 LongText', value: 'LongText' },
|
||||
{ label: '二进制 Blob', value: 'Blob' },
|
||||
],
|
||||
cellWidthType: [
|
||||
{ label: '固定', value: 'fixed' },
|
||||
{ label: '最小', value: 'min' },
|
||||
],
|
||||
queryMode: [
|
||||
{ label: '精确查询', value: 'EQ' },
|
||||
{ label: '模糊查询', value: 'LIKE' },
|
||||
{ label: '范围查询(仅适用于日期、时间、数字)', value: 'RANGE' },
|
||||
{ label: '包含查询(in)', value: 'IN' },
|
||||
{ label: '不等于(!=)', value: 'NE' },
|
||||
],
|
||||
}
|
||||
export const getDicObj = (key) => {
|
||||
const obj = {}
|
||||
if (dicObj[key]) dicObj[key].forEach(item => obj[item.value] = item.label)
|
||||
return obj
|
||||
}
|
||||
|
||||
const rules_required = (label, type = '') => [{ required: true, message: `${['select'].includes(type) ? '请选择' : '请输入'} ${label}`, trigger: "blur" }]
|
||||
const reportCode_required = async (rule, value, callback) => {
|
||||
if (value === '') callback(new Error('请输入 报表编码'));
|
||||
else {
|
||||
const bool = await verifyReportCode(value)
|
||||
if (bool) callback(new Error('报表编码已存在,请修改'));
|
||||
else callback()
|
||||
}
|
||||
};
|
||||
|
||||
const dataSourcesCode_dicFormatter = (data) => {
|
||||
data = data.map(item => {
|
||||
if (item.id === 0) {
|
||||
item.dbCode = 'master'
|
||||
item.name = `${item.name}(本地数据源)`
|
||||
}
|
||||
else item.name = `${item.name}(${item.dbType})`
|
||||
return item
|
||||
})
|
||||
return data.filter(item => item.dbCode)
|
||||
}
|
||||
|
||||
const dataOriginDicObj = {}
|
||||
|
||||
export const getDataOriginDicData = (dbCode, isReacquire?) => {
|
||||
return new Promise(async resolve => {
|
||||
if (isReacquire) for (const key in dataOriginDicObj) delete dataOriginDicObj[key]
|
||||
if (dataOriginDicObj[dbCode]) return resolve(dataOriginDicObj[dbCode])
|
||||
let data = []
|
||||
if (dbCode == 'master') data = await getAllDbDicData({ systemFlag: 'Y' })
|
||||
else data = await getAllDbDicData({ dataSourcesCode: dbCode })
|
||||
dataOriginDicObj[dbCode] = dataOrigin_dicFormatter(data, dbCode)
|
||||
resolve(dataOriginDicObj[dbCode])
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
const dataOriginObj = {}
|
||||
const dataOrigin_dicFormatter = (data, dbCode) => {
|
||||
const sysList: any[] = []
|
||||
const dbList: any[] = []
|
||||
data.forEach(item => {
|
||||
const row = {
|
||||
label: `${item.tableName}(${item.tableDescribe})`,
|
||||
tableText: item.tableDescribe,
|
||||
tableName: item.tableName,
|
||||
value: item.tableId,
|
||||
type: 'table',
|
||||
fieldList: item.fieldModelList.map(child => {
|
||||
let label = child.fieldCode
|
||||
if (child.fieldName) label = `${label}(${child.fieldName})`
|
||||
return { label, value: child.fieldCode, tableName: item.tableName, type: 'field', fieldType: child.fieldType }
|
||||
})
|
||||
}
|
||||
if (item.tableId == item.tableName) sysList.push(row)
|
||||
else if (item.tableClassify !== 2) dbList.push(row)
|
||||
if (!dataOriginObj[dbCode]) dataOriginObj[dbCode] = {}
|
||||
dataOriginObj[dbCode][item.tableId] = row
|
||||
})
|
||||
const dicData: any[] = []
|
||||
if (sysList.length) dicData.push({ label: '系统表', value: 'sys', disabled: true, children: sysList })
|
||||
if (dbList.length) dicData.push({ label: '表单开发', value: 'dbForm', disabled: true, children: dbList })
|
||||
return dicData
|
||||
}
|
||||
|
||||
//表格配置
|
||||
const tableOptionColumn = {
|
||||
reportName: { label: '报表名称', display: false, search: true, minWidth: 140 },
|
||||
reportCode: { label: '报表编码', display: false, search: true, minWidth: 140, overHidden: true },
|
||||
javaConfig: { label: 'JAVA类路径', display: false, search: true, minWidth: 140, overHidden: true, searchLabelWidth: 100, },
|
||||
createTime: { label: '创建时间', type: 'datetime', format: 'YYYY-MM-DD HH:mm:ss', valueFormat: 'x', display: false, width: 160 },
|
||||
custom_form: { label: '', labelWidth: 0, span: 24, hide: true, showColumn: false },
|
||||
custom_info: { label: '', labelWidth: 0, span: 24, hide: true, showColumn: false }
|
||||
}
|
||||
// 表单配置
|
||||
const customFormColumn = {
|
||||
reportName: { label: '报表名称', rules: rules_required('报表名称') },
|
||||
reportCode: { label: '报表编码', rules: [...rules_required('报表编码'), { validator: reportCode_required, trigger: 'blur', required: true }] },
|
||||
groupReportId: { label: '分组类型', type: 'tree', value: '', dicData: [], filterable: true, defaultExpandAll: true, props: { label: 'name', value: 'id' } },
|
||||
dataSourcesCode: { label: '数据源', type: 'select', value: 'master', span: 6, clearable: false, dicUrl: '/infra/data-source-config/list', props: { label: 'name', value: 'dbCode' }, dicFormatter: dataSourcesCode_dicFormatter },
|
||||
dataOrigin: { label: '数据表', type: 'tree', value: '', span: 8, dataType: 'string', multiple: true, filterable: true, parent: false, defaultExpandAll: true, dicData: [] },
|
||||
originButton: { label: '', labelWidth: 0, },
|
||||
javaConfig: { label: 'JAVA类路径', span: 12 },
|
||||
dataConfig: { label: '数据配置', type: 'checkbox', span: 24, dicData: dicObj.dataConfig, dataType: 'string', value: ['page', 'sync','authFalse'] },
|
||||
tableConfig: { label: '表格配置', type: 'checkbox', span: 24, dicData: dicObj.tableConfig, dataType: 'string', value: ['height', 'header', 'menu', 'index', 'border'] },
|
||||
}
|
||||
|
||||
const infoColumn = {
|
||||
fieldColumn: {
|
||||
fieldCode: { title: '字段编码', minWidth: 120, editRender: { name: 'LowInput', verifyEdit: true } },
|
||||
fieldName: { title: '字段名称', minWidth: 120, editRender: { name: 'LowInput' } },
|
||||
labelI18n: { title: '国际化配置', width: 140, editRender: { name: 'LowMonacoEditorInput', events: {} } },
|
||||
fieldType: { title: '字段类型', minWidth: 100, editRender: { name: 'LowSelect', verifyEdit: true, dicData: dicObj.fieldType, dicObj: getDicObj('fieldType') } },
|
||||
queryIsDb: { title: '接口查询', width: 75, align: "center", editRender: { name: 'LowCheckbox' } },
|
||||
queryIsWeb: { title: '查询控件', width: 75, align: "center", editRender: { name: 'LowCheckbox' } },
|
||||
queryMode: { title: '查询模式', width: 130, editRender: { name: 'LowSelect', verifyEdit: true, dicData: dicObj.queryMode, dicObj: getDicObj('queryMode') } },
|
||||
dictCode: { title: '字典Code', width: 180, editRender: { name: 'LowSelect', verifyEdit: true, filterable: true, noStop: true, dicData: [] } },
|
||||
isExport: { title: '是否可导出', width: 90, align: "center", editRender: { name: 'LowCheckbox' } },
|
||||
isShowSort: { title: '是否排序', width: 75, align: "center", editRender: { name: 'LowCheckbox' } },
|
||||
},
|
||||
}
|
||||
|
||||
const infoApiKey = {}
|
||||
const apiKey = { fieldColumn: 'fieldList' }
|
||||
for (const key in infoColumn) {
|
||||
if (apiKey[key]) {
|
||||
const keys = Object.keys(infoColumn[key])
|
||||
if (key == 'fieldColumn') keys.push('sortNum')
|
||||
infoApiKey[apiKey[key]] = keys
|
||||
}
|
||||
}
|
||||
|
||||
//默认值
|
||||
const infoDefaultData = {
|
||||
basics: {
|
||||
fieldCode: '', fieldName: '', labelI18n: '', fieldType: 'String', queryIsDb: 'N', queryIsWeb: 'N', queryMode: 'LIKE', dictCode: '', isExport: 'Y', isShowSort: 'N',
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
//格式化接口初始数据
|
||||
const formattingInitData = (editInfoData) => {
|
||||
const optionObj = {}
|
||||
|
||||
for (const apiKey in infoApiKey) {
|
||||
const key = apiKey
|
||||
optionObj[key] = {}
|
||||
editInfoData[key]?.forEach(item => optionObj[key][item.fieldCode] = item)
|
||||
}
|
||||
|
||||
const infoData = [] as any
|
||||
editInfoData.fieldList.forEach(fieldItem => {
|
||||
const fieldCode = fieldItem.fieldCode
|
||||
const infoItem: any = {}
|
||||
|
||||
for (const apiKey in infoApiKey) {
|
||||
const dataKey = apiKey
|
||||
if (!optionObj[dataKey]) continue
|
||||
const editItem = optionObj[dataKey][fieldCode] || cloneDeep(infoDefaultData)
|
||||
|
||||
infoItem[`${apiKey}_id`] = editItem.id
|
||||
for (const i in infoApiKey[apiKey]) {
|
||||
const key = infoApiKey[apiKey][i]
|
||||
if (apiKey != 'fieldList' && ['fieldCode', 'fieldName'].includes(key)) continue
|
||||
infoItem[key] = editItem[key]
|
||||
}
|
||||
}
|
||||
infoData.push(infoItem)
|
||||
})
|
||||
return { infoData }
|
||||
}
|
||||
|
||||
export const pageOption = {
|
||||
tableOptionColumn, customFormColumn, dataOriginObj,
|
||||
infoApiKey,
|
||||
reportCode_required
|
||||
}
|
||||
export const tableInfoOption = {
|
||||
infoColumn,
|
||||
infoDefaultData,
|
||||
formattingInitData,
|
||||
}
|
||||
857
src/views/lowdesign/reportDesign/index.vue
Normal file
857
src/views/lowdesign/reportDesign/index.vue
Normal file
@@ -0,0 +1,857 @@
|
||||
<template>
|
||||
<ContentWrap>
|
||||
<div class="flex gap-x-10px">
|
||||
<div class="flex-basis-180px flex-shrink-0">
|
||||
<avue-tree
|
||||
ref="treeRef"
|
||||
v-model="treeForm"
|
||||
:option="treeOption"
|
||||
:data="treeData"
|
||||
:permission="treePermission"
|
||||
:before-open="treeBeforeOpen"
|
||||
@node-contextmenu="treeNodeContextmenu"
|
||||
@node-click="treeNodeClick"
|
||||
@update="treeUpdate"
|
||||
@save="treeSave"
|
||||
@del="treeDel"
|
||||
>
|
||||
</avue-tree>
|
||||
</div>
|
||||
<avue-crud
|
||||
class="table-content flex-1"
|
||||
ref="crudRef"
|
||||
v-model="tableForm"
|
||||
v-model:page="tablePage"
|
||||
v-model:search="tableSearch"
|
||||
:table-loading="loading"
|
||||
:data="tableData"
|
||||
:option="tableOption"
|
||||
:permission="permission"
|
||||
:before-open="beforeOpen"
|
||||
:before-close="beforeClose"
|
||||
@search-change="searchChange"
|
||||
@search-reset="resetChange"
|
||||
@row-save="rowSave"
|
||||
@row-update="rowUpdate"
|
||||
@row-del="rowDel"
|
||||
@refresh-change="getTableData"
|
||||
@size-change="sizeChange"
|
||||
@current-change="currentChange"
|
||||
>
|
||||
<template #menu="{ size, row }">
|
||||
<div class="flex justify-center flex-items-center">
|
||||
<el-button
|
||||
type="primary"
|
||||
text
|
||||
:size="size"
|
||||
v-hasPermi="['jeelowcode:report:update']"
|
||||
@click="menuHandle({ type: 'edit', row, index: row.$index })"
|
||||
>
|
||||
<Icon :size="14" icon="ep:edit-pen"></Icon>
|
||||
<span>编辑</span>
|
||||
</el-button>
|
||||
<span
|
||||
v-if="checkPermi(['jeelowcode:report:update'])"
|
||||
class="ml-8px mr-8px mt-2px inline-block h-16px w-1px bg-#e8e8e8 .dark:bg-[var(--el-border-color-dark)]"
|
||||
></span>
|
||||
<el-dropdown @command="menuHandle">
|
||||
<div class="mt--2px cursor-pointer">
|
||||
<el-text :size="size" type="primary">
|
||||
更多
|
||||
<Icon :size="16" icon="iconamoon:arrow-down-2-light" />
|
||||
</el-text>
|
||||
</div>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<template v-for="item in menuMoreList" :key="item.type">
|
||||
<el-dropdown-item
|
||||
v-if="!item.isShow || item.isShow()"
|
||||
:command="{ type: item.type, row }"
|
||||
>
|
||||
{{ item.label }}
|
||||
</el-dropdown-item>
|
||||
</template>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</template>
|
||||
<template #menu-form-before="{ disabled, size, type }">
|
||||
<el-button
|
||||
v-if="type == 'edit'"
|
||||
type="primary"
|
||||
:size="size"
|
||||
:disabled="disabled"
|
||||
:loading="saveLoading"
|
||||
@click="rowUpdate(tableForm)"
|
||||
>
|
||||
<Icon :size="14" icon="ep:circle-check"></Icon>
|
||||
<span>修改</span>
|
||||
</el-button>
|
||||
</template>
|
||||
<template #custom_form-form>
|
||||
<avue-form
|
||||
class="table-design-custom-form"
|
||||
ref="customFormRef"
|
||||
:option="customFormOption"
|
||||
v-model="tableForm"
|
||||
>
|
||||
<template #originButton>
|
||||
<div>
|
||||
<el-button
|
||||
type="primary"
|
||||
class="ml-2px"
|
||||
@click="openDataOrigin"
|
||||
:disabled="!tableForm.dataOrigin"
|
||||
>
|
||||
<Icon :size="14" icon="lucide:text-search"></Icon> <span>数据源SQL配置</span>
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
class="ml-2px"
|
||||
@click="analysisDataOrigin"
|
||||
:loading="analysisLoading"
|
||||
:disabled="!tableForm.originButton"
|
||||
>
|
||||
<Icon v-if="!analysisLoading" :size="14" icon="streamline:code-analysis"></Icon>
|
||||
<span>解析配置生成字段</span>
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
<template #dataConfig="scope">
|
||||
<div class="flex">
|
||||
<avue-checkbox
|
||||
class="flex-basis-60px flex-shrink-0"
|
||||
v-model="tableForm.dataConfig"
|
||||
:dic="scope.column.dicData"
|
||||
></avue-checkbox>
|
||||
<avue-select
|
||||
class="flex-basis-160px flex-shrink-0"
|
||||
v-model="authValue"
|
||||
:dic="dicObj.dataConfigSelect"
|
||||
:clearable="false"
|
||||
></avue-select>
|
||||
</div>
|
||||
</template>
|
||||
</avue-form>
|
||||
</template>
|
||||
|
||||
<template #custom_info-form="scope">
|
||||
<TableInfo
|
||||
v-if="isTableInfo"
|
||||
ref="tableInfoRef"
|
||||
:formType="scope.type"
|
||||
:editInfoData="editInfoData"
|
||||
:size="scope.size"
|
||||
></TableInfo>
|
||||
</template>
|
||||
</avue-crud>
|
||||
</div>
|
||||
|
||||
<DesignPopup
|
||||
v-model="dataOriginPopup.show"
|
||||
title="数据源SQL配置"
|
||||
width="90%"
|
||||
:isBodyFull="true"
|
||||
:dialogParams="{ top: '5vh' }"
|
||||
:handleClose="(done) => handlePopupClose(done, 'originButton')"
|
||||
>
|
||||
<template #default>
|
||||
<DataOriginOption
|
||||
:ref="(el) => (optionRef.originButton = el)"
|
||||
v-model="tableForm.originButton"
|
||||
:show="dataOriginPopup.show"
|
||||
:tableList="dataOriginPopup.tableList"
|
||||
:viewField="dataOriginPopup.viewField"
|
||||
:dbCode="tableForm.dataSourcesCode"
|
||||
></DataOriginOption>
|
||||
</template>
|
||||
</DesignPopup>
|
||||
</ContentWrap>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { TableInfo } from './components'
|
||||
import { DataOriginOption } from '../tableDesign/components'
|
||||
import * as TableApi from '@/api/design/table'
|
||||
import * as ReportApi from '@/api/design/report'
|
||||
import { pageOption, tableInfoOption, dicObj, getDataOriginDicData } from './designData'
|
||||
import { formattingLengStr } from '@/utils/lowDesign'
|
||||
import { ElMessage, ElButton, ElLoading } from 'element-plus'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
import useCopyText from '@/hooks/design/useCopyText'
|
||||
import { useRenderVxeColumn } from '../general/components/useRenderVxeColumn'
|
||||
import { useGroup } from '@/hooks/design/useGroup'
|
||||
import { checkPermi } from '@/utils/permission'
|
||||
|
||||
defineOptions({ name: 'TableDesign' })
|
||||
|
||||
const { copyText } = useCopyText()
|
||||
useRenderVxeColumn()
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const router = useRouter() // 路由
|
||||
const { t } = useI18n() // 国际化
|
||||
const { getCurrPermi } = useCrudPermi()
|
||||
|
||||
const loading = ref(true) // 列表的加载中
|
||||
const saveLoading = ref(false)
|
||||
const isTableInfo = ref(false)
|
||||
const analysisLoading = ref(false)
|
||||
|
||||
//表格配置
|
||||
const tableOption = reactive({
|
||||
align: 'center',
|
||||
headerAlign: 'center',
|
||||
searchMenuSpan: 6,
|
||||
searchMenuPosition: 'left',
|
||||
searchIndex: 3,
|
||||
searchIcon: true,
|
||||
labelSuffix: ' ',
|
||||
span: 8,
|
||||
dialogWidth: '100%',
|
||||
dialogFullscreen: true,
|
||||
editBtn: false,
|
||||
delBtn: false,
|
||||
border: true,
|
||||
index: true,
|
||||
menuWidth: 150,
|
||||
updateBtnText: '修改并关闭',
|
||||
column: pageOption.tableOptionColumn
|
||||
})
|
||||
|
||||
const tableForm = ref<any>({})
|
||||
const tableData = ref([])
|
||||
const authValue = ref('')
|
||||
const tableSearch = ref({})
|
||||
const tablePage = ref({
|
||||
currentPage: 1,
|
||||
pageSize: 10,
|
||||
total: 0
|
||||
})
|
||||
const customFormOption = ref({
|
||||
labelWidth: 120,
|
||||
span: 8,
|
||||
menuBtn: false,
|
||||
column: pageOption.customFormColumn
|
||||
})
|
||||
|
||||
const editInfoData = ref({})
|
||||
const apiDetailData = ref<any>({})
|
||||
const dataOriginPopup = ref({
|
||||
show: false,
|
||||
tableList: [] as any[],
|
||||
viewField: [] as any[]
|
||||
})
|
||||
|
||||
const crudRef = ref()
|
||||
const tableInfoRef = ref()
|
||||
const customFormRef = ref()
|
||||
const treeRef = ref()
|
||||
const optionRef = ref<any>({})
|
||||
const isUnload = ref(false)
|
||||
|
||||
const permission = getCurrPermi(['jeelowcode:report'])
|
||||
|
||||
const menuMoreList = [
|
||||
{ label: '功能测试', type: 'test' },
|
||||
{ label: '路由地址', type: 'address' },
|
||||
{ label: '接口地址', type: 'apiUrl' },
|
||||
{ label: '复制报表', type: 'copy', isShow: () => checkPermi(['jeelowcode:report:create']) },
|
||||
{ label: '删除', type: 'del', isShow: () => checkPermi(['jeelowcode:report:delete']) }
|
||||
]
|
||||
|
||||
useCrudHeight(crudRef)
|
||||
const {
|
||||
treeForm,
|
||||
treeOption,
|
||||
treeData,
|
||||
groupValue,
|
||||
treePermission,
|
||||
treeBeforeOpen,
|
||||
treeNodeContextmenu,
|
||||
treeNodeClick,
|
||||
getTreeData,
|
||||
treeUpdate,
|
||||
treeSave,
|
||||
treeDel
|
||||
} = useGroup(treeRef, ReportApi, () => resetChange())
|
||||
|
||||
const openDataOrigin = () => {
|
||||
dataOriginPopup.value.tableList = []
|
||||
const dataOriginObj = pageOption.dataOriginObj[tableForm.value.dataSourcesCode]
|
||||
tableForm.value.dataOrigin.split(',').forEach((key) => {
|
||||
const tableItem = dataOriginObj[key]
|
||||
if (tableItem) dataOriginPopup.value.tableList.push(tableItem)
|
||||
})
|
||||
const filedData = [...tableInfoRef.value.infoData.basics, ...tableInfoRef.value.tableInfoDefault]
|
||||
dataOriginPopup.value.viewField = filedData.map((item) => {
|
||||
return { label: item.fieldName, value: item.fieldCode }
|
||||
})
|
||||
dataOriginPopup.value.show = true
|
||||
}
|
||||
|
||||
watch(
|
||||
() => authValue.value,
|
||||
() => {
|
||||
if (tableForm.value.dataConfig) {
|
||||
tableForm.value.dataConfig = tableForm.value.dataConfig.filter(
|
||||
(key) => !['authFalse', 'authTrue', 'authOpen', ''].includes(key)
|
||||
)
|
||||
}
|
||||
if (authValue.value) tableForm.value.dataConfig.push(authValue.value)
|
||||
}
|
||||
)
|
||||
|
||||
watch(
|
||||
() => tableForm.value.dataSourcesCode,
|
||||
async (newVal, oldVal) => {
|
||||
if (newVal && oldVal) tableForm.value.dataOrigin = []
|
||||
if (newVal) {
|
||||
customFormRef.value?.updateDic('dataOrigin', [])
|
||||
const dicData = await getDataOriginDicData(newVal, oldVal === undefined)
|
||||
setTimeout(() => {
|
||||
customFormRef.value.updateDic('dataOrigin', dicData)
|
||||
}, 300)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
const tableFormVerify = (type) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
customFormRef.value.validate((bool, done, msg) => {
|
||||
done()
|
||||
if (!bool) return reject(msg)
|
||||
|
||||
let infoData = {}
|
||||
let errText = ''
|
||||
let fieldCodeArr: any[string] = []
|
||||
const filedData = [...tableInfoRef.value.infoData.basics]
|
||||
tableInfoRef.value.tableInfoDefault.forEach((item) => {
|
||||
filedData.splice(item.sortNum || 999, 0, item)
|
||||
})
|
||||
|
||||
for (const i in filedData) {
|
||||
const index = Number(i)
|
||||
const item = filedData[index]
|
||||
item.sortNum = index + 1
|
||||
|
||||
let messageText = ''
|
||||
let tabKey = 'mysql'
|
||||
if (!item.fieldCode || !item.fieldName) {
|
||||
messageText = `<div style="line-height:24px">
|
||||
<div>${!item.fieldCode ? '字段编码' : '字段名称'}必须填写</div>
|
||||
<div>序号:${index + 1}</div>
|
||||
</div>`
|
||||
}
|
||||
if (fieldCodeArr.includes(item.fieldCode)) {
|
||||
messageText = `<div style="line-height:24px">
|
||||
<div>
|
||||
<span>字段编码重复:</span>
|
||||
<span style="color:red">${item.fieldCode}</span>
|
||||
</div>
|
||||
<div>序号:${index + 1}</div>
|
||||
</div>`
|
||||
}
|
||||
fieldCodeArr.push(item.fieldCode)
|
||||
if (!/(^[a-zA-Z]{2}(_?[a-zA-Z0-9])*_?$)/.test(item.fieldCode)) {
|
||||
messageText = `<div style="line-height:24px">
|
||||
<div>
|
||||
<span>字段编码不符合规范</span>
|
||||
<span style="color:red">${item.fieldCode}</span>
|
||||
</div>
|
||||
<div>命名规则:只能由字母、数字、下划线组成;必须以字母开头;不能以单个字母加下滑线开头</div>
|
||||
<div>序号:${index + 1}</div>
|
||||
</div>`
|
||||
}
|
||||
|
||||
if (messageText) {
|
||||
handleVerifyError(tabKey, item._X_ROW_KEY, index)
|
||||
ElMessage({ dangerouslyUseHTMLString: true, message: messageText })
|
||||
errText = 'message'
|
||||
break
|
||||
}
|
||||
|
||||
for (let key in pageOption.infoApiKey) {
|
||||
if (!infoData[key]) infoData[key] = []
|
||||
let itemObj: any = {}
|
||||
pageOption.infoApiKey[key].forEach((prop) => {
|
||||
itemObj[prop] = item[prop] !== undefined ? item[prop] : ''
|
||||
})
|
||||
if (key == 'fieldList') {
|
||||
itemObj.labelI18n = formattingLengStr(itemObj.labelI18n, itemObj.fieldName)
|
||||
}
|
||||
if (type == 'edit' && item[`${key}_id`]) itemObj['id'] = item[`${key}_id`]
|
||||
infoData[key].push(itemObj)
|
||||
}
|
||||
}
|
||||
|
||||
if (errText) return reject(errText)
|
||||
|
||||
if (type == 'edit') {
|
||||
infoData['delIdVo'] = {}
|
||||
contrastEditData(infoData)
|
||||
}
|
||||
resolve(infoData)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const contrastEditData = (infoData) => {
|
||||
for (const key in infoData) {
|
||||
if (!apiDetailData.value[key] || key == 'delIdVo') break
|
||||
infoData.delIdVo[key] = cloneDeep(apiDetailData.value.delIdVo[key])
|
||||
infoData[key] = infoData[key].map((item) => {
|
||||
const contrastObj = apiDetailData.value[key][item.id]
|
||||
if (item.id) {
|
||||
const index = infoData.delIdVo[key].indexOf(item.id)
|
||||
if (index != -1) infoData.delIdVo[key].splice(index, 1)
|
||||
if (contrastObj) {
|
||||
for (const k in item) {
|
||||
if (item[k] != contrastObj[k]) {
|
||||
item.isModify = 'Y'
|
||||
// console.log(key, k, '修改了','原:'+contrastObj[k], '新:'+item[k], )
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return item
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const setApiDetailData = (data) => {
|
||||
apiDetailData.value = { delIdVo: {} }
|
||||
for (const key in data) {
|
||||
let listKey = key
|
||||
if (listKey != 'report') {
|
||||
if (!apiDetailData.value[listKey]) apiDetailData.value[listKey] = {}
|
||||
if (!apiDetailData.value.delIdVo[listKey]) apiDetailData.value.delIdVo[listKey] = []
|
||||
data[key]?.forEach((item) => {
|
||||
if (item.id) {
|
||||
apiDetailData.value[listKey][item.id] = item
|
||||
apiDetailData.value.delIdVo[listKey].push(item.id)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
for (const key in apiDetailData.value) {
|
||||
if (key == 'delIdVo') break
|
||||
if (!Object.keys(apiDetailData.value[key]).length) delete apiDetailData.value[key]
|
||||
}
|
||||
}
|
||||
|
||||
const handleVerifyError = (key, rowId, index) => {
|
||||
const tabKey = `tab_${key}`
|
||||
const vxeTableRef = tableInfoRef.value.tableRefObj[tabKey]?.vxeTableRef
|
||||
if (vxeTableRef) {
|
||||
tableInfoRef.value.setTabsValue(tabKey)
|
||||
setTimeout(() => {
|
||||
vxeTableRef.setEditRow(vxeTableRef.getRowById(rowId))
|
||||
tableInfoRef.value.tableScrollIndex(key, index, index)
|
||||
}, 300)
|
||||
}
|
||||
}
|
||||
|
||||
const menuHandle = async ({ type, row, index }) => {
|
||||
if (type == 'edit') crudRef.value.rowEdit(row, index)
|
||||
else if (type == 'test') router.push({ path: '/low/report/test/' + row.reportCode })
|
||||
else if (type == 'address') showAddress(row)
|
||||
else if (type == 'apiUrl') showApiUrl(row)
|
||||
else if (type == 'copy') copyReport(row)
|
||||
else if (type == 'del') rowDel(row)
|
||||
}
|
||||
|
||||
const showAddress = (row) => {
|
||||
const url = `report/view/` + row.reportCode
|
||||
message.alert(`路由地址:</br>${url}`, '菜单的路由地址', {
|
||||
confirmButtonText: '复制',
|
||||
dangerouslyUseHTMLString: true,
|
||||
callback: (action) => {
|
||||
if (action == 'confirm') copyText(url)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const showApiUrl = async (row) => {
|
||||
loading.value = true
|
||||
const detailData = await ReportApi.getDbDetail(row.id).finally(() => (loading.value = false))
|
||||
const isOpen = detailData.report.dataConfig.indexOf('authOpen') != -1
|
||||
const apiList = [
|
||||
{
|
||||
label: '获取报表数据[post]',
|
||||
value: `/jeelowcode/report-data/list/${row.reportCode}`
|
||||
},
|
||||
{
|
||||
label: '批量获取报表数据[post]',
|
||||
value: `/jeelowcode/report-data/batch/list/报表code,报表code,...`
|
||||
}
|
||||
]
|
||||
if (isOpen) {
|
||||
apiList.push({
|
||||
label: '未登录时获取报表数据[post]',
|
||||
value: `/jeelowcode/open/report/list/${row.reportCode}`
|
||||
})
|
||||
}
|
||||
|
||||
let list: VNode[] = []
|
||||
apiList.forEach((item) => {
|
||||
list.push(
|
||||
h('div', { style: { marginBottom: '10px', border: ' 1px solid #eee', padding: '10px' } }, [
|
||||
h('div', [
|
||||
h('span', { style: { fontWeight: 600, fontSize: '14px' } }, item.label + ':'),
|
||||
h(
|
||||
ElButton,
|
||||
{ size: 'small', type: 'primary', onClick: () => copyText(item.value) },
|
||||
() => '复制'
|
||||
)
|
||||
]),
|
||||
h('div', { style: { fontSize: '12px' } }, item.value)
|
||||
])
|
||||
)
|
||||
})
|
||||
message.alert('', '接口地址', {
|
||||
message: () => {
|
||||
return h('div', { width: '360px' }, list)
|
||||
},
|
||||
confirmButtonText: '关闭',
|
||||
dangerouslyUseHTMLString: true,
|
||||
customStyle: { width: '384px' }
|
||||
})
|
||||
}
|
||||
|
||||
const copyReport = (row) => {
|
||||
message
|
||||
.prompt('新报表编码', '复制报表', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: '',
|
||||
inputValidator: (value) => {
|
||||
if (!value) return '请输入报表编码'
|
||||
let RExp = /([a-zA-Z_$][a-zA-Z\d_$]*\.)*[a-zA-Z_$][a-zA-Z\d_$]*/
|
||||
if (!RExp.test(value)) return '报表编码格式错误!'
|
||||
}
|
||||
})
|
||||
.then(async ({ value }) => {
|
||||
loading.value = true
|
||||
const bool = await ReportApi.verifyReportCode(value)
|
||||
if (bool) {
|
||||
loading.value = false
|
||||
return message.info('报表编码已存在')
|
||||
}
|
||||
await ReportApi.copyReportData(row.reportCode, value)
|
||||
message.success('复制成功')
|
||||
resetChange()
|
||||
})
|
||||
}
|
||||
|
||||
const handlePopupClose = (done, prop) => {
|
||||
const str = optionRef.value[prop]?.getOptionStr() || ''
|
||||
done()
|
||||
setTimeout(() => {
|
||||
tableForm.value[prop] = str
|
||||
}, 30)
|
||||
}
|
||||
|
||||
const setInfoBasics = (infoData) => {
|
||||
const basics_id: any[] = []
|
||||
const basics_defaule: any[] = []
|
||||
tableInfoRef.value.infoData.basics.forEach((item) => {
|
||||
if (item.only) {
|
||||
if (item.fieldCode == 'id') basics_id.push(item)
|
||||
else basics_defaule.push(item)
|
||||
}
|
||||
})
|
||||
tableInfoRef.value.infoData.basics = [...basics_id, ...infoData, ...basics_defaule]
|
||||
}
|
||||
|
||||
const analysisDataOrigin = async () => {
|
||||
await message.confirm('解析后将会覆盖现有的字段配置,是否确定解析?')
|
||||
analysisLoading.value = true
|
||||
const dataOption = JSON.parse(tableForm.value.originButton)
|
||||
const apiData: any = {}
|
||||
if (dataOption.typeKey == 'custom' || tableForm.value.dataSourcesCode !== 'master') {
|
||||
apiData.explainSql =
|
||||
dataOption.typeKey == 'custom' ? dataOption.customSql : dataOption.executeSql
|
||||
apiData.dataSourcesCode = tableForm.value.dataSourcesCode
|
||||
} else {
|
||||
apiData.modelList = dataOption.optionObj.select
|
||||
}
|
||||
TableApi.viewDataOriginAnalysis(apiData)
|
||||
.then((analysisData) => {
|
||||
for (const key in analysisData) {
|
||||
analysisData[key] = analysisData[key]?.map((item) => {
|
||||
delete item.id
|
||||
delete item.dbformId
|
||||
return item
|
||||
})
|
||||
}
|
||||
let { infoData } = tableInfoOption.formattingInitData(analysisData)
|
||||
infoData = infoData.map((item) => {
|
||||
for (const key in item) {
|
||||
if (item[key] === null || item[key] === undefined || key == 'fieldList_id') {
|
||||
delete item[key]
|
||||
}
|
||||
}
|
||||
return { ...cloneDeep(tableInfoOption.infoDefaultData.basics), ...item }
|
||||
})
|
||||
setInfoBasics(infoData)
|
||||
message.success('解析成功')
|
||||
})
|
||||
.finally(() => (analysisLoading.value = false))
|
||||
}
|
||||
|
||||
/** 查询列表 */
|
||||
const getTableData = async () => {
|
||||
loading.value = true
|
||||
let searchObj = {
|
||||
...tableSearch.value,
|
||||
pageNo: tablePage.value.currentPage,
|
||||
pageSize: tablePage.value.pageSize
|
||||
}
|
||||
if (groupValue.value) searchObj['groupReportId'] = groupValue.value
|
||||
for (const key in searchObj) if (searchObj[key] === '') delete searchObj[key]
|
||||
try {
|
||||
const data = await ReportApi.getDbList(searchObj)
|
||||
|
||||
tableData.value = data.records
|
||||
tablePage.value.total = data.total
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const searchChange = (params, done) => {
|
||||
if (Object.keys(params).length && groupValue.value) {
|
||||
treeRef.value.setCurrentKey(0)
|
||||
groupValue.value = 0
|
||||
}
|
||||
tablePage.value.currentPage = 1
|
||||
getTableData().finally(() => {
|
||||
done()
|
||||
})
|
||||
}
|
||||
|
||||
/** 清空按钮操作 */
|
||||
const resetChange = () => {
|
||||
searchChange({}, () => {})
|
||||
}
|
||||
|
||||
const sizeChange = (pageSize) => {
|
||||
tablePage.value.pageSize = pageSize
|
||||
resetChange()
|
||||
}
|
||||
|
||||
const currentChange = (currentPage) => {
|
||||
tablePage.value.currentPage = currentPage
|
||||
getTableData()
|
||||
}
|
||||
|
||||
/** 表单打开前 */
|
||||
const beforeOpen = async (done, type) => {
|
||||
isTableInfo.value = false
|
||||
const groupData = cloneDeep(treeData.value)
|
||||
customFormOption.value.column.groupReportId.dicData = groupData[0].children
|
||||
const { reportCode } = customFormOption.value.column
|
||||
if (['edit', 'view'].includes(type) && tableForm.value.id) {
|
||||
loading.value = true
|
||||
const data = await ReportApi.getDbDetail(tableForm.value.id)
|
||||
setApiDetailData(cloneDeep(data))
|
||||
data.report.tableConfig = data.report.tableConfig?.split(',') || []
|
||||
data.report.dataConfig = data.report.dataConfig?.split(',') || []
|
||||
editInfoData.value = data
|
||||
tableForm.value = { ...data.report }
|
||||
reportCode['disabled'] = true
|
||||
reportCode['rules'] = []
|
||||
if (tableForm.value.dataSourcesConfig) {
|
||||
const dataSourcesConfig = JSON.parse(tableForm.value.dataSourcesConfig)
|
||||
delete tableForm.value.dataSourcesConfig
|
||||
tableForm.value.dataOrigin = dataSourcesConfig.dataOrigin
|
||||
tableForm.value.originButton = JSON.stringify({
|
||||
...dataSourcesConfig.optionData,
|
||||
executeSql: dataSourcesConfig.executeSql
|
||||
})
|
||||
}
|
||||
|
||||
const dataConfig = tableForm.value.dataConfig
|
||||
dicObj.dataConfigSelect.forEach((item) => {
|
||||
if (dataConfig.includes(item.value)) authValue.value = item.value
|
||||
})
|
||||
|
||||
loading.value = false
|
||||
} else {
|
||||
tableForm.value = { ...tableForm.value, dataConfig: ['page', 'authFalse'] }
|
||||
reportCode['disabled'] = false
|
||||
reportCode['rules'] = [{ validator: pageOption.reportCode_required, trigger: 'blur' }] as any
|
||||
if (groupValue.value) tableForm.value.groupReportId = groupValue.value
|
||||
authValue.value = ''
|
||||
}
|
||||
if (!authValue.value) authValue.value = 'authFalse'
|
||||
isUnload.value = ['edit', 'add'].includes(type)
|
||||
done()
|
||||
//延迟显示
|
||||
setTimeout(() => (isTableInfo.value = true), 300)
|
||||
}
|
||||
|
||||
const beforeClose = async (done, type) => {
|
||||
isUnload.value = false
|
||||
done()
|
||||
}
|
||||
|
||||
const handleApiFormData = (formData) => {
|
||||
const form = cloneDeep(formData)
|
||||
form.dataConfig = form.dataConfig.join(',')
|
||||
form.groupReportId = form.groupReportId || ''
|
||||
if (form.dataOrigin || form.originButton) {
|
||||
const dataSourcesConfig = {
|
||||
dataOrigin: form.dataOrigin || '',
|
||||
executeSql: '',
|
||||
optionData: {}
|
||||
}
|
||||
if (form.originButton) {
|
||||
const originButton = JSON.parse(form.originButton)
|
||||
if (form.dataOrigin) dataSourcesConfig.executeSql = originButton.executeSql
|
||||
delete originButton.executeSql
|
||||
dataSourcesConfig.optionData = originButton
|
||||
}
|
||||
delete form.dataOrigin
|
||||
delete form.originButton
|
||||
form.dataSourcesConfig = JSON.stringify(dataSourcesConfig)
|
||||
} else form.dataSourcesConfig = ''
|
||||
return form
|
||||
}
|
||||
|
||||
/** 新增操作 */
|
||||
const rowSave = async (formData, done, loading) => {
|
||||
const form = handleApiFormData(formData)
|
||||
tableFormVerify('add')
|
||||
.then(async (infoData: object) => {
|
||||
const elLoading = ElLoading.service({ fullscreen: true })
|
||||
let bool = await ReportApi.saveDbData({ report: { ...form }, ...infoData }).catch(() => false)
|
||||
if (bool) {
|
||||
message.success(t('common.createSuccess'))
|
||||
resetChange()
|
||||
done()
|
||||
} else loading()
|
||||
elLoading.close()
|
||||
})
|
||||
.catch((error) => {
|
||||
if (typeof error == 'object') {
|
||||
let key = Object.keys(error)[0]
|
||||
message.info(error[key][0].message)
|
||||
} else if (error !== 'message') {
|
||||
message.alert(error, '请修改', { dangerouslyUseHTMLString: true })
|
||||
}
|
||||
loading()
|
||||
})
|
||||
}
|
||||
|
||||
/** 编辑操作 */
|
||||
const rowUpdate = async (formData, index?, done?, loading?) => {
|
||||
let isGetDetail = false
|
||||
if (!loading || !done) {
|
||||
saveLoading.value = true
|
||||
loading = () => (saveLoading.value = false)
|
||||
done = () => (saveLoading.value = false)
|
||||
isGetDetail = true
|
||||
}
|
||||
const form = handleApiFormData(formData)
|
||||
tableFormVerify('edit')
|
||||
.then(async (infoData: object) => {
|
||||
let bool = await ReportApi.updateDbData({ report: { ...form }, ...infoData }).catch(
|
||||
() => false
|
||||
)
|
||||
if (bool) {
|
||||
if (isGetDetail) {
|
||||
const data = await ReportApi.getDbDetail(form.id)
|
||||
setApiDetailData(cloneDeep(data))
|
||||
editInfoData.value = data
|
||||
setTimeout(() => {
|
||||
tableInfoRef.value.initEditInfoData()
|
||||
}, 30)
|
||||
}
|
||||
message.success(t('common.updateSuccess'))
|
||||
getTableData()
|
||||
done()
|
||||
} else loading()
|
||||
})
|
||||
.catch((error) => {
|
||||
if (typeof error == 'object') {
|
||||
let key = Object.keys(error)[0]
|
||||
message.info(error[key][0].message)
|
||||
} else if (error !== 'message') {
|
||||
message.alert(error, '请修改', { dangerouslyUseHTMLString: true })
|
||||
}
|
||||
loading()
|
||||
})
|
||||
}
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const rowDel = async (form) => {
|
||||
try {
|
||||
// 删除的二次确认
|
||||
await message.delConfirm()
|
||||
loading.value = true
|
||||
// 发起删除
|
||||
await ReportApi.deleteDbData([form.id])
|
||||
message.success(t('common.delSuccess'))
|
||||
// 刷新列表
|
||||
await getTableData()
|
||||
} catch {}
|
||||
}
|
||||
|
||||
const beforeUnload = (event) => {
|
||||
if (isUnload.value) return (event.returnValue = '您确定要关闭页面吗?')
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
window.addEventListener('beforeunload', beforeUnload)
|
||||
getTableData()
|
||||
getTreeData()
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener('beforeunload', beforeUnload)
|
||||
})
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.table-content {
|
||||
max-width: calc(100% - 190px);
|
||||
}
|
||||
</style>
|
||||
<style lang="scss">
|
||||
.table-design-custom-form {
|
||||
.el-form-item {
|
||||
margin-bottom: 18px !important;
|
||||
|
||||
.el-form-item {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dark {
|
||||
.table-design-option {
|
||||
.view-field-content {
|
||||
.content-item {
|
||||
color: var(--el-text-color-seconda) !important;
|
||||
background-color: var(--el-fill-color-light) !important;
|
||||
}
|
||||
}
|
||||
|
||||
.integrality-content,
|
||||
.left-tree,
|
||||
.option-content,
|
||||
.view-field,
|
||||
.view-field .title,
|
||||
.alias-item {
|
||||
border-color: var(--el-border-color-dark) !important;
|
||||
}
|
||||
|
||||
.integrality-content > div {
|
||||
background-color: var(--el-fill-color-light) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
49
src/views/lowdesign/reportView/index.vue
Normal file
49
src/views/lowdesign/reportView/index.vue
Normal file
@@ -0,0 +1,49 @@
|
||||
<template>
|
||||
<ContentWrap>
|
||||
<LowReport v-if="reportCode" :reportCode="reportCode" :isPermi="isPermi"></LowReport>
|
||||
<div v-else>无权限访问</div>
|
||||
</ContentWrap>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { CACHE_KEY, useCache } from '@/hooks/web/useCache'
|
||||
|
||||
const route = useRoute()
|
||||
const { wsCache } = useCache()
|
||||
const reportCode = ref('')
|
||||
const isPermi = ref(false)
|
||||
|
||||
onMounted(() => {
|
||||
if (route.params.code) {
|
||||
//功能测试
|
||||
const menus = wsCache.get(CACHE_KEY.USER).menus
|
||||
const praentArr = route.meta.activeMenu?.split('/').filter((path) => path) || []
|
||||
let isPermission = false
|
||||
if (praentArr.length) {
|
||||
praentArr[0] = '/' + praentArr[0]
|
||||
let findIndex = 0
|
||||
const findPath = (menuList) => {
|
||||
for (const index in menuList) {
|
||||
if (menuList[index].path == praentArr[findIndex]) {
|
||||
if (findIndex == praentArr.length - 1) isPermission = true
|
||||
else findIndex++
|
||||
if (menuList[index].children) findPath(menuList[index].children)
|
||||
if (isPermission) break
|
||||
}
|
||||
}
|
||||
}
|
||||
findPath(menus)
|
||||
}
|
||||
if (isPermission && typeof route.params.code == 'string') reportCode.value = route.params.code
|
||||
} else {
|
||||
const pathList = route.path.split('/')
|
||||
const length = pathList.length - 1
|
||||
if (pathList[length]) {
|
||||
reportCode.value = pathList[length]
|
||||
isPermi.value = true
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
683
src/views/lowdesign/tableDesign/components/AuthConfig.vue
Normal file
683
src/views/lowdesign/tableDesign/components/AuthConfig.vue
Normal file
@@ -0,0 +1,683 @@
|
||||
<template>
|
||||
<DesignPopup
|
||||
v-model="popupShow"
|
||||
:title="`租户权限配置 【${tableDescribe}】(${tableName})`"
|
||||
:fullscreen="true"
|
||||
controlType="drawer"
|
||||
width="900px"
|
||||
>
|
||||
<div class="p-20px pt-0">
|
||||
<div class="flex items-center mt-10px mb-10px">
|
||||
<span class="flex-basis-50px flex-shrink-0 text-14px">租户:</span>
|
||||
<avue-select
|
||||
class="flex-basis-300px w-300px"
|
||||
v-model="currTenant"
|
||||
:dic="tenantDic"
|
||||
:props="{ label: 'tenantName', value: 'tenantId' }"
|
||||
@change="(data) => initData(data.value)"
|
||||
></avue-select>
|
||||
</div>
|
||||
<el-tabs v-model="tabValue" type="border-card" v-loading="loading">
|
||||
<el-tab-pane label="字段权限" name="field">
|
||||
<avue-crud ref="fieldCrudRef" :data="fieldData" :option="fieldOption">
|
||||
<template #enableState-header="{ column }">
|
||||
<div class="w-100% flex items-center justify-center gap-x-5px">
|
||||
<avue-switch
|
||||
v-model="fieldAllObj.open"
|
||||
@click="setAll('field', ['enableState'])"
|
||||
></avue-switch>
|
||||
<span>{{ column.label }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<template #listIsView-header="{ column }">
|
||||
<div class="w-100% flex items-center justify-center gap-x-5px" v-if="column">
|
||||
<el-checkbox
|
||||
v-model="fieldAllObj.list"
|
||||
:indeterminate="fieldAllObj.listHalf"
|
||||
@click="setAll('field', ['listIsView'])"
|
||||
>
|
||||
</el-checkbox>
|
||||
<span>{{ column.label }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<template #formConfig-header="{ column }">
|
||||
<div class="w-100% flex items-center justify-center gap-x-5px" v-if="column">
|
||||
<el-checkbox
|
||||
v-model="fieldAllObj.form"
|
||||
:indeterminate="fieldAllObj.formHalf"
|
||||
@click="setAll('field', ['formIsView', 'formIsEdit'])"
|
||||
>
|
||||
</el-checkbox>
|
||||
<span>{{ column.label }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<template #enableState-form="{ dic, column, row }">
|
||||
<avue-switch
|
||||
v-model="row[column.prop]"
|
||||
:dic="dic"
|
||||
:beforeChange="(done) => statusDeforeChange(row, 'field', done)"
|
||||
></avue-switch>
|
||||
</template>
|
||||
<template #listIsView-form="{ column, row }">
|
||||
<el-checkbox
|
||||
true-value="Y"
|
||||
false-value="N"
|
||||
v-model="row[column.prop]"
|
||||
:disabled="row.enableState == 'N'"
|
||||
@change="(val) => fieldCheckBoxChange(val, row, 'listIsView')"
|
||||
>
|
||||
可见
|
||||
</el-checkbox>
|
||||
</template>
|
||||
<template #formConfig-form="{ row }">
|
||||
<el-checkbox
|
||||
true-value="Y"
|
||||
false-value="N"
|
||||
:disabled="row.enableState == 'N'"
|
||||
v-model="row.formIsView"
|
||||
@change="(val) => fieldCheckBoxChange(val, row, 'formIsView')"
|
||||
>
|
||||
可见
|
||||
</el-checkbox>
|
||||
<el-checkbox
|
||||
true-value="Y"
|
||||
false-value="N"
|
||||
:disabled="row.enableState == 'N'"
|
||||
v-model="row.formIsEdit"
|
||||
@change="(val) => fieldCheckBoxChange(val, row, 'formIsEdit')"
|
||||
>
|
||||
可编辑
|
||||
</el-checkbox>
|
||||
</template>
|
||||
</avue-crud>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="按钮权限" name="btn">
|
||||
<avue-crud ref="btnCrudRef" :data="btnData" :option="btnOption">
|
||||
<template #enableState-header="{ column }">
|
||||
<div class="w-100% flex items-center justify-center gap-x-5px">
|
||||
<avue-switch v-model="btnAll" @click="setAll('btn')"></avue-switch>
|
||||
<span>{{ column.label }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<template #enableState-form="{ dic, column, row }">
|
||||
<avue-switch
|
||||
v-model="row[column.prop]"
|
||||
:dic="dic"
|
||||
:beforeChange="(done) => statusDeforeChange(row, 'btn', done)"
|
||||
></avue-switch>
|
||||
</template>
|
||||
<template #authText> 可见 </template>
|
||||
</avue-crud>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="数据权限" name="data">
|
||||
<avue-crud
|
||||
ref="dataCrudRef"
|
||||
v-model="dataForm"
|
||||
:data="dataData"
|
||||
:option="dataOption"
|
||||
:before-open="dataBeforeOpen"
|
||||
:table-loading="dataLoading"
|
||||
@row-save="(form, done, loading) => saveRuleData(form, done, loading)"
|
||||
@row-update="(form, index, done, loading) => saveRuleData(form, done, loading)"
|
||||
>
|
||||
<template #menu="{ row }">
|
||||
<el-button text type="danger" @click="dataRowDel(row)">
|
||||
<Icon :size="14" icon="ep:delete"></Icon>
|
||||
<span>删除</span>
|
||||
</el-button>
|
||||
</template>
|
||||
<template #enableState-header="{ column }">
|
||||
<div class="w-100% flex items-center justify-center gap-x-5px">
|
||||
<avue-switch v-model="dataAll" @click="setAll('data')"></avue-switch>
|
||||
<span>{{ column.label }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<template #enableState-form="{ dic, column, row }">
|
||||
<avue-switch
|
||||
v-model="row[column.prop]"
|
||||
:dic="dic"
|
||||
:beforeChange="(done) => statusDeforeChange(row, 'data', done)"
|
||||
></avue-switch>
|
||||
</template>
|
||||
<template #sqlRuleValue-form="{ column, size }">
|
||||
<avue-input
|
||||
v-model="dataForm[column.prop]"
|
||||
:size="size"
|
||||
readonly
|
||||
:placeholder="`请输入 ${column.label}`"
|
||||
@click="openMEDialog(column, dataForm)"
|
||||
></avue-input>
|
||||
</template>
|
||||
</avue-crud>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
</DesignPopup>
|
||||
<DesignPopup v-model="MEDialog.value" v-bind="MEDialog.params" :isBodyFull="true">
|
||||
<template #title>{{ MEDialog.title }}</template>
|
||||
<template #default>
|
||||
<MonacoEditor v-model="MEData.value" v-bind="MEData.params"></MonacoEditor>
|
||||
</template>
|
||||
</DesignPopup>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import useMEDialog from '@/hooks/design/useMEDialog'
|
||||
import * as Auth from '@/api/design/table/auth'
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
defineOptions({ name: 'JavaEnhance' })
|
||||
|
||||
const { MEDialog, MEData, openMEDialog } = useMEDialog()
|
||||
|
||||
interface Props {
|
||||
dbformId?: string
|
||||
tableName?: string
|
||||
tableDescribe?: string
|
||||
}
|
||||
const popupShow = defineModel({ default: false, type: Boolean })
|
||||
const props = defineProps<Props>()
|
||||
|
||||
const loading = ref(false)
|
||||
|
||||
const tabsOption = ref({
|
||||
type: 'border-card',
|
||||
column: [
|
||||
{ label: '字段权限', prop: 'field' },
|
||||
{ label: '按钮权限', prop: 'btn' },
|
||||
{ label: '数据权限', prop: 'data' }
|
||||
]
|
||||
})
|
||||
const tabValue = ref(tabsOption.value.column[0].prop)
|
||||
|
||||
const currTenant = ref('1')
|
||||
const tenantDic = ref<any[]>([])
|
||||
//字段权限
|
||||
const fieldAllObj = ref({
|
||||
open: true,
|
||||
list: false,
|
||||
form: false,
|
||||
listHalf: false,
|
||||
formHalf: false
|
||||
})
|
||||
const fieldOption = reactive({
|
||||
border: true,
|
||||
header: false,
|
||||
menu: false,
|
||||
rowKey: 'fieldCode',
|
||||
calcHeight: 185,
|
||||
column: {
|
||||
enableState: {
|
||||
label: '启用',
|
||||
type: 'switch',
|
||||
cell: true,
|
||||
width: 100,
|
||||
dicData: [
|
||||
{ label: '', value: 'N' },
|
||||
{ label: '', value: 'Y' }
|
||||
]
|
||||
},
|
||||
fieldCode: { label: '字段编码' },
|
||||
fieldName: { label: '字段名称' },
|
||||
listIsView: { label: '列表控制', cell: true },
|
||||
formConfig: { label: '表单控制', cell: true }
|
||||
}
|
||||
})
|
||||
const fieldData = ref<any>([])
|
||||
const filedAll = computed(() => {
|
||||
const obj = {
|
||||
open: true,
|
||||
list: true,
|
||||
form: true,
|
||||
listHalf: false,
|
||||
formHalf: false
|
||||
}
|
||||
if (fieldData.value.length) {
|
||||
for (let key in fieldData.value) {
|
||||
const item = fieldData.value[key]
|
||||
if (item.enableState == 'Y') {
|
||||
if (obj.list && item.listIsView == 'N') obj.list = false
|
||||
if (obj.form && (item.formIsView == 'N' || item.formIsEdit == 'N')) obj.form = false
|
||||
//设置半选
|
||||
if (!obj.listHalf && item.listIsView == 'Y') obj.listHalf = true
|
||||
if (!obj.formHalf && (item.formIsView == 'Y' || item.formIsEdit == 'Y')) obj.formHalf = true
|
||||
} else if (obj.open) obj.open = false
|
||||
}
|
||||
if (obj.list) obj.listHalf = false
|
||||
if (obj.form) obj.formHalf = false
|
||||
} else for (const key in obj) obj[key] = false
|
||||
return obj
|
||||
})
|
||||
watch(
|
||||
() => filedAll.value,
|
||||
(obj) => {
|
||||
fieldAllObj.value = obj
|
||||
}
|
||||
)
|
||||
//按钮权限
|
||||
const btnAll = ref(false)
|
||||
const btnOption = reactive({
|
||||
border: true,
|
||||
header: false,
|
||||
menu: false,
|
||||
rowKey: 'buttonCode',
|
||||
calcHeight: 185,
|
||||
column: {
|
||||
enableState: {
|
||||
label: '启用',
|
||||
type: 'switch',
|
||||
cell: true,
|
||||
dicData: [
|
||||
{ label: '', value: 'N' },
|
||||
{ label: '', value: 'Y' }
|
||||
]
|
||||
},
|
||||
buttonName: { label: '按钮名称' },
|
||||
buttonCode: { label: '按钮编码' },
|
||||
authText: { label: '权限控制' }
|
||||
}
|
||||
})
|
||||
const btnData = ref<any>([])
|
||||
watch(
|
||||
() => btnData.value,
|
||||
(list) => {
|
||||
let bool = true
|
||||
if (list.length) {
|
||||
list.forEach((item) => {
|
||||
if (item.enableState != 'Y') bool = false
|
||||
})
|
||||
} else bool = false
|
||||
btnAll.value = bool
|
||||
},
|
||||
{ deep: true }
|
||||
)
|
||||
|
||||
//数据权限
|
||||
const dataLoading = ref(false)
|
||||
const dataForm = ref<any>({ ruleCondition: '' })
|
||||
const isDataForm = ref(false)
|
||||
const dataAll = ref(false)
|
||||
const dataOption = reactive({
|
||||
border: true,
|
||||
menuWidth: 150,
|
||||
columnBtn: false,
|
||||
refreshBtn: false,
|
||||
calcHeight: 185,
|
||||
column: {
|
||||
enableState: {
|
||||
label: '启用',
|
||||
type: 'switch',
|
||||
cell: true,
|
||||
display: false,
|
||||
width: 100,
|
||||
dicData: [
|
||||
{ label: '', value: 'N' },
|
||||
{ label: '', value: 'Y' }
|
||||
]
|
||||
},
|
||||
ruleName: {
|
||||
label: '规则名称',
|
||||
width: 200,
|
||||
rules: [{ required: true, message: `请输入 规则名称`, trigger: 'blur' }]
|
||||
},
|
||||
ruleType: {
|
||||
label: '规则类型',
|
||||
type: 'radio',
|
||||
value: 'field',
|
||||
hide: true,
|
||||
dicData: [
|
||||
{ label: '字段选择', value: 'field' },
|
||||
{ label: '自定义SQL', value: 'sql' }
|
||||
],
|
||||
control: (val) => {
|
||||
const bool = val == 'field'
|
||||
return {
|
||||
ruleField: { display: bool },
|
||||
ruleCondition: { display: bool },
|
||||
ruleValue: { display: bool },
|
||||
sqlRuleValue: { display: !bool }
|
||||
}
|
||||
}
|
||||
},
|
||||
ruleField: {
|
||||
label: '规则字段',
|
||||
type: 'select',
|
||||
dicData: [],
|
||||
filterable: true,
|
||||
hide: true,
|
||||
rules: [{ required: true, message: `请选择 规则字段`, trigger: 'change' }]
|
||||
},
|
||||
ruleCondition: {
|
||||
label: '规则条件',
|
||||
type: 'select',
|
||||
rules: [{ required: true, message: `请选择 规则条件`, trigger: 'change' }],
|
||||
dicData: [
|
||||
{ label: '等于(=)', value: 'eq' },
|
||||
{ label: '不等于(!=)', value: 'ne' },
|
||||
{ label: `模糊(LIKE "%xxx%")`, value: 'like' },
|
||||
{ label: `左模糊(LIKE "%xxx")`, value: 'leftLike' },
|
||||
{ label: `右模糊(LIKE "xxx%")`, value: 'rightLike' },
|
||||
{ label: `包含(IN("xxx","xxx"...))`, value: 'in' },
|
||||
{ label: `不包含(NOT IN("xxx","xxx"...))`, value: 'notIn' },
|
||||
{ label: '为空(IS NULL)', value: 'isNull' },
|
||||
{ label: '不为空(IS NOT NULL)', value: 'isNotNull' }
|
||||
],
|
||||
hide: true,
|
||||
control: (val, form) => {
|
||||
let bool = !['isNull', 'isNotNull'].includes(val)
|
||||
if (form.ruleType == 'sql') bool = false
|
||||
return { ruleValue: { display: bool } }
|
||||
}
|
||||
},
|
||||
ruleValue: {
|
||||
label: '规则值',
|
||||
hide: true,
|
||||
type: 'select',
|
||||
allowCreate: true,
|
||||
filterable: true,
|
||||
multiple: false,
|
||||
dataType: 'string',
|
||||
dicData: [
|
||||
{ label: '其他值请直接输入(字符串请加"双引号")', value: '-', disabled: true },
|
||||
{ label: '获取当前人ID', value: '#{jeelowcode_user_id}' },
|
||||
{ label: '获取当前人名称', value: '#{jeelowcode_user_nickname}' },
|
||||
{ label: '获取当前人部门', value: '#{jeelowcode_user_dept}' },
|
||||
{ label: '获取当前人所有部门', value: '#{jeelowcode_user_all_dept}' },
|
||||
{ label: '获取当前人租户', value: '#{jeelowcode_tenant_id}' },
|
||||
{ label: '获取当前日期', value: '#{jeelowcode_date}' },
|
||||
{ label: '获取当前时间', value: '#{jeelowcode_time}' }
|
||||
],
|
||||
rules: [{ required: true, message: `请选择 规则值`, trigger: 'change' }]
|
||||
},
|
||||
sqlRuleValue: {
|
||||
label: '规则SQL',
|
||||
display: false,
|
||||
hide: true,
|
||||
rules: [{ required: true, message: `请输入 规则SQL`, trigger: 'blur' }]
|
||||
},
|
||||
ruleSql: {
|
||||
label: '规则描述',
|
||||
display: false
|
||||
}
|
||||
}
|
||||
})
|
||||
const dataData = ref<any>()
|
||||
watch(
|
||||
() => dataData.value,
|
||||
(list) => {
|
||||
let bool = true
|
||||
if (list.length) {
|
||||
list.forEach((item) => {
|
||||
if (item.enableState != 'Y') bool = false
|
||||
})
|
||||
} else bool = false
|
||||
dataAll.value = bool
|
||||
},
|
||||
{ deep: true }
|
||||
)
|
||||
|
||||
watch(
|
||||
() => dataForm.value.ruleCondition,
|
||||
(newVal, oldVal) => {
|
||||
const newBool = ['in', 'notIn'].includes(newVal)
|
||||
const oldBool = ['in', 'notIn'].includes(oldVal)
|
||||
if ((newBool && !oldBool) || (oldBool && !newBool)) {
|
||||
if (isDataForm.value) dataForm.value.ruleValue = newBool ? [] : ''
|
||||
dataOption.column.ruleValue.multiple = newBool
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
const dataCrudRef = ref()
|
||||
const btnCrudRef = ref()
|
||||
const fieldCrudRef = ref()
|
||||
|
||||
const { initTableLayout } = useCrudHeight([dataCrudRef, btnCrudRef, fieldCrudRef])
|
||||
|
||||
watch(
|
||||
() => popupShow.value,
|
||||
(val: boolean) => {
|
||||
tabValue.value = tabsOption.value.column[0].prop
|
||||
if (val) initData()
|
||||
}
|
||||
)
|
||||
|
||||
watch(
|
||||
() => tabValue.value,
|
||||
() => {
|
||||
initTableLayout()
|
||||
}
|
||||
)
|
||||
|
||||
//处理权限数据
|
||||
const handleSaveAuthData = (data, row, type) => {
|
||||
if (type == 'field') {
|
||||
data = {
|
||||
formIsEdit: row.formIsEdit == 'Y' ? null : 'N',
|
||||
formIsView: row.formIsView == 'Y' ? null : 'N',
|
||||
listIsView: row.listIsView == 'Y' ? null : 'N',
|
||||
enableState: row.enableState == 'Y' ? null : 'N',
|
||||
...data,
|
||||
fieldCode: row.fieldCode
|
||||
}
|
||||
} else if (type == 'btn') data.buttonCode = row.buttonCode
|
||||
else if (type == 'data') data.ruleId = row.id
|
||||
|
||||
data.tenantId = currTenant.value
|
||||
data.dbformId = props.dbformId
|
||||
return data
|
||||
}
|
||||
|
||||
//保存权限数据
|
||||
const apiType = { field: 'saveFieldAuth', btn: 'saveButtonAuth', data: 'saveDataAuth' }
|
||||
const saveAuthData = (data, row, type) => {
|
||||
data = handleSaveAuthData(data, row, type)
|
||||
return Auth[apiType[type]]([data]).catch(() => false)
|
||||
}
|
||||
|
||||
//修改所有的启用状态
|
||||
const statusDeforeChange = async (row, type, done) => {
|
||||
const value = row.enableState == 'Y' ? 'N' : 'Y'
|
||||
let data: any = { enableState: value == 'Y' ? null : value }
|
||||
const res = await saveAuthData(data, row, type)
|
||||
done(res ? true : false)
|
||||
}
|
||||
|
||||
//修改字段列表、表单控制
|
||||
const fieldCheckBoxChange = async (value, row, dataKey) => {
|
||||
let data: any = { [dataKey]: value == 'Y' ? null : value }
|
||||
const res = await saveAuthData(data, row, 'field')
|
||||
if (!res) {
|
||||
fieldData.value = fieldData.value.map((item) => {
|
||||
if (item.fieldCode == row.fieldCode) {
|
||||
item[dataKey] = item[dataKey] == 'Y' ? 'N' : 'Y'
|
||||
}
|
||||
return item
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
//全选控制
|
||||
const setAll = async (type, keyList?) => {
|
||||
const editData: any[] = []
|
||||
if (type == 'field') {
|
||||
const obj = { enableState: 'open', listIsView: 'list', formIsView: 'form', formIsEdit: 'form' }
|
||||
fieldData.value = fieldData.value.map((item) => {
|
||||
const editItem: any = {}
|
||||
let bool = false
|
||||
|
||||
keyList.forEach((prop) => {
|
||||
if (prop == 'enableState' || (prop != 'enableState' && item.enableState == 'Y')) {
|
||||
const value = fieldAllObj.value[obj[prop]] ? 'N' : 'Y'
|
||||
if (item[prop] !== value) {
|
||||
editItem[prop] = value == 'Y' ? null : 'N'
|
||||
bool = true
|
||||
}
|
||||
item[prop] = value
|
||||
}
|
||||
})
|
||||
if (bool) editData.push(handleSaveAuthData(editItem, item, type))
|
||||
return item
|
||||
})
|
||||
setTimeout(() => {
|
||||
fieldAllObj.value[obj[keyList[0]]] = !fieldAllObj.value[obj[keyList[0]]]
|
||||
}, 30)
|
||||
} else if (type == 'btn') {
|
||||
const value = btnAll.value ? 'N' : 'Y'
|
||||
btnData.value = btnData.value.map((item) => {
|
||||
if (item.enableState !== value) {
|
||||
editData.push(handleSaveAuthData({ enableState: value == 'Y' ? null : 'N' }, item, type))
|
||||
}
|
||||
item.enableState = value
|
||||
return item
|
||||
})
|
||||
setTimeout(() => {
|
||||
btnAll.value = !btnAll.value
|
||||
}, 30)
|
||||
} else if (type == 'data') {
|
||||
const value = dataAll.value ? 'N' : 'Y'
|
||||
dataData.value = dataData.value.map((item) => {
|
||||
if (item.enableState !== value) {
|
||||
editData.push(handleSaveAuthData({ enableState: value == 'Y' ? null : 'N' }, item, type))
|
||||
}
|
||||
item.enableState = value
|
||||
return item
|
||||
})
|
||||
setTimeout(() => {
|
||||
dataAll.value = !dataAll.value
|
||||
}, 30)
|
||||
}
|
||||
|
||||
if (editData.length) {
|
||||
loading.value = true
|
||||
await Auth[apiType[type]](editData).catch(() => false)
|
||||
if (type == 'field') await getFieldAuthData()
|
||||
else if (type == 'btn') await getButtonAuthData()
|
||||
else if (type == 'data') await getDataAuthData()
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const initData = async (tenantId?) => {
|
||||
loading.value = true
|
||||
if (tenantId) currTenant.value = tenantId
|
||||
else {
|
||||
currTenant.value = '1'
|
||||
tabValue.value = 'field'
|
||||
}
|
||||
const promiseArr = [getTenantList(), getFieldAuthData(), getButtonAuthData(), getDataAuthData()]
|
||||
await Promise.all(promiseArr)
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
//获取所有租户
|
||||
const getTenantList = () => {
|
||||
return Auth.getAllTenant()
|
||||
.then((data) => {
|
||||
tenantDic.value = data.map((item) => {
|
||||
if (item.tenantId == '1') item.tenantName = '所有租户默认权限'
|
||||
return item
|
||||
})
|
||||
})
|
||||
.catch(() => false)
|
||||
}
|
||||
|
||||
//获取字段权限列表
|
||||
const getFieldAuthData = () => {
|
||||
return Auth.getFieldAuth(currTenant.value, props.dbformId)
|
||||
.then((data) => {
|
||||
if (data) {
|
||||
fieldData.value = data.map((item) => {
|
||||
item.$cellEdit = true
|
||||
if (item.enableState == null) item.enableState = 'Y'
|
||||
if (item.formIsEdit == null) item.formIsEdit = 'Y'
|
||||
if (item.formIsView == null) item.formIsView = 'Y'
|
||||
if (item.listIsView == null) item.listIsView = 'Y'
|
||||
return item
|
||||
})
|
||||
}
|
||||
})
|
||||
.catch(() => false)
|
||||
}
|
||||
|
||||
//获取按钮权限列表
|
||||
const getButtonAuthData = () => {
|
||||
return Auth.getButtonAuth(currTenant.value, props.dbformId)
|
||||
.then((data) => {
|
||||
if (data) {
|
||||
btnData.value = data.map((item) => {
|
||||
item.$cellEdit = true
|
||||
if (item.enableState == null) item.enableState = 'Y'
|
||||
return item
|
||||
})
|
||||
}
|
||||
})
|
||||
.catch(() => false)
|
||||
}
|
||||
|
||||
//获取数据权限列表
|
||||
const getDataAuthData = () => {
|
||||
return Auth.getDataAuth(currTenant.value, props.dbformId)
|
||||
.then((data) => {
|
||||
dataData.value = (data || []).map((item) => {
|
||||
item.$cellEdit = true
|
||||
if (item.enableState == null) item.enableState = 'Y'
|
||||
if (item.ruleType == 'sql') {
|
||||
item.sqlRuleValue = item.ruleValue
|
||||
item.ruleValue = ''
|
||||
}
|
||||
return item
|
||||
})
|
||||
})
|
||||
.catch(() => false)
|
||||
}
|
||||
|
||||
//保存、修改数据权限
|
||||
const saveRuleData = async (form, done, loading) => {
|
||||
let conditionText = ''
|
||||
if (form.ruleCondition == 'eq') conditionText = `= ${form.ruleValue}`
|
||||
else if (form.ruleCondition == 'ne') conditionText = `!= ${form.ruleValue}`
|
||||
else if (form.ruleCondition == 'like') conditionText = `LIKE "%${form.ruleValue}%"`
|
||||
else if (form.ruleCondition == 'leftLike') conditionText = `LIKE "%${form.ruleValue}"`
|
||||
else if (form.ruleCondition == 'rightLike') conditionText = `LIKE "${form.ruleValue}%"`
|
||||
else if (form.ruleCondition == 'in') conditionText = `IN( ${form.ruleValue} )`
|
||||
else if (form.ruleCondition == 'notIn') conditionText = `NOT IN( ${form.ruleValue} )`
|
||||
else if (form.ruleCondition == 'isNull') conditionText = 'IS NULL'
|
||||
else if (form.ruleCondition == 'isNotNull') conditionText = 'IS NOT NULL'
|
||||
form.ruleSql = form.ruleType == 'sql' ? form.sqlRuleValue : `${form.ruleField} ${conditionText}`
|
||||
|
||||
const data = await Auth.saveRuleData({
|
||||
...form,
|
||||
dbformId: props.dbformId,
|
||||
ruleValue: form.ruleType == 'sql' ? form.sqlRuleValue : form.ruleValue
|
||||
}).catch(() => false)
|
||||
if (!data) return loading()
|
||||
done()
|
||||
dataLoading.value = true
|
||||
await getDataAuthData()
|
||||
dataLoading.value = false
|
||||
}
|
||||
|
||||
//删除数据权限
|
||||
const dataRowDel = async (row) => {
|
||||
await message.delConfirm()
|
||||
dataLoading.value = true
|
||||
await Auth.deleteRuleData(row.id).catch(() => false)
|
||||
await getDataAuthData()
|
||||
dataLoading.value = false
|
||||
}
|
||||
|
||||
const dataBeforeOpen = (done) => {
|
||||
isDataForm.value = false
|
||||
//设置规则字段
|
||||
dataOption.column.ruleField.dicData = fieldData.value.map((item) => {
|
||||
return { label: `${item.fieldName}(${item.fieldCode})`, value: item.fieldCode }
|
||||
})
|
||||
done()
|
||||
setTimeout(() => {
|
||||
isDataForm.value = true
|
||||
}, 300)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss"></style>
|
||||
476
src/views/lowdesign/tableDesign/components/ConfigOption.vue
Normal file
476
src/views/lowdesign/tableDesign/components/ConfigOption.vue
Normal file
@@ -0,0 +1,476 @@
|
||||
<template>
|
||||
<div class="config-option table-design-option">
|
||||
<el-container class="h-100%">
|
||||
<el-aside width="220px" class="left-tree">
|
||||
<template v-for="(config, index) in listData" :key="config.title">
|
||||
<div
|
||||
class="flex items-center mb-10px text-14px font-600 c-#333 cursor-pointer"
|
||||
@click="setCollapse(index)"
|
||||
>
|
||||
<Icon
|
||||
v-if="collapseObj[index] || collapseObj[index] === undefined"
|
||||
:size="14"
|
||||
icon="ep:caret-bottom"
|
||||
></Icon>
|
||||
<Icon v-else :size="14" icon="ep:caret-right"></Icon>
|
||||
<span class="ml-2px">{{ config.title }}</span>
|
||||
</div>
|
||||
<el-collapse-transition>
|
||||
<div
|
||||
class="ml-10px"
|
||||
v-show="collapseObj[index] === undefined ? true : collapseObj[index]"
|
||||
>
|
||||
<draggable
|
||||
class="config-content"
|
||||
tag="div"
|
||||
:list="config.list"
|
||||
:group="{ name: 'config', pull: 'clone', put: false }"
|
||||
ghost-class="config-ghost"
|
||||
:sort="false"
|
||||
item-key="value"
|
||||
>
|
||||
<template #item="{ element }">
|
||||
<div class="config-item" @click="setOption(element)">
|
||||
<span>{{ element.label }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</draggable>
|
||||
</div>
|
||||
</el-collapse-transition>
|
||||
</template>
|
||||
<div class="text-center pb-20px">
|
||||
<el-button @click="openEditor" class="w-100%">
|
||||
更多配置
|
||||
<span class="text-12px c-[var(--el-color-primary)]" v-if="customStr != defStr"
|
||||
>(已编写)</span
|
||||
>
|
||||
</el-button>
|
||||
</div>
|
||||
</el-aside>
|
||||
<el-main class="main-option">
|
||||
<div class="option-title table-item-row">
|
||||
<div class="row-item">
|
||||
<div class="cell">序号</div>
|
||||
</div>
|
||||
<div class="row-item text-center">
|
||||
<div class="cell">配置名称</div>
|
||||
</div>
|
||||
<div class="row-item">
|
||||
<div class="cell">配置值</div>
|
||||
</div>
|
||||
<div class="row-item">
|
||||
<div class="cell">操作</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="option-content">
|
||||
<draggable
|
||||
class="content-draggable"
|
||||
:list="optionList"
|
||||
:group="{ name: 'option', put: true }"
|
||||
ghost-class="option-ghost"
|
||||
:animation="300"
|
||||
handle=".move-box"
|
||||
item-key="prop"
|
||||
@add="handleAddColumn"
|
||||
>
|
||||
<template #item="{ element, index }">
|
||||
<div class="option-item table-item-row">
|
||||
<div class="row-item move-box">
|
||||
<div class="cell">{{ index + 1 }}</div>
|
||||
</div>
|
||||
<div class="row-item move-box">
|
||||
<div class="cell"
|
||||
><avue-text-ellipsis
|
||||
:key="element.value"
|
||||
:text="element.label"
|
||||
:height="40"
|
||||
:width="196"
|
||||
use-tooltip
|
||||
placement="top"
|
||||
>
|
||||
<template #more>
|
||||
<small>...</small>
|
||||
</template>
|
||||
</avue-text-ellipsis>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row-item">
|
||||
<div class="cell">
|
||||
<template v-if="element.type == 'input' || !element.type">
|
||||
<avue-input
|
||||
v-model="element.val"
|
||||
placeholder="请输入"
|
||||
v-bind="element.params || {}"
|
||||
></avue-input>
|
||||
</template>
|
||||
<template v-else-if="element.type == 'number'">
|
||||
<avue-input-number
|
||||
v-model="element.val"
|
||||
v-bind="element.params || {}"
|
||||
placeholder="请输入"
|
||||
></avue-input-number>
|
||||
</template>
|
||||
<template v-else-if="element.type == 'color'">
|
||||
<avue-input-color
|
||||
v-model="element.val"
|
||||
v-bind="element.params || {}"
|
||||
placeholder="请选择"
|
||||
></avue-input-color>
|
||||
</template>
|
||||
<template v-else-if="element.type == 'select'">
|
||||
<avue-select
|
||||
v-model="element.val"
|
||||
v-bind="element.params || {}"
|
||||
placeholder="请选择"
|
||||
:dic="element.dic"
|
||||
></avue-select>
|
||||
</template>
|
||||
<template v-else-if="element.type == 'radio'">
|
||||
<avue-radio
|
||||
v-model="element.val"
|
||||
:dic="element.dic"
|
||||
v-bind="element.params || {}"
|
||||
></avue-radio>
|
||||
</template>
|
||||
<template v-else-if="element.type == 'switch'">
|
||||
<avue-switch
|
||||
v-model="element.val"
|
||||
:dic="element.dic"
|
||||
v-bind="element.params || {}"
|
||||
></avue-switch>
|
||||
</template>
|
||||
<div class="tip" v-if="element.valTipShow == element.val && element.valTip">
|
||||
{{ element.valTip }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row-item">
|
||||
<div class="cell">
|
||||
<el-button type="danger" link text @click="delRow(element)"> 删除 </el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</draggable>
|
||||
</div>
|
||||
</el-main>
|
||||
</el-container>
|
||||
<DesignPopup v-model="MEDialog.value" v-bind="MEDialog.params" :isBodyFull="true">
|
||||
<template #default>
|
||||
<MonacoEditor v-model="MEData.value" v-bind="MEData.params"></MonacoEditor>
|
||||
</template>
|
||||
</DesignPopup>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import draggable from 'vuedraggable'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
import controlOption from '@/components/LowDesign/src/utils/controlOption'
|
||||
import { handleStrObj } from '@/utils/lowDesign'
|
||||
import useMEDialog from '@/hooks/design/useMEDialog'
|
||||
|
||||
defineOptions({ name: 'ConfigOption' })
|
||||
|
||||
interface Props {
|
||||
configKey: Array<string>
|
||||
show: boolean
|
||||
}
|
||||
const props = defineProps<Props>()
|
||||
const optionStr = defineModel<string>({ default: '' })
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { MEDialog, MEData, openMEDialog } = useMEDialog()
|
||||
|
||||
const collapseObj = ref({})
|
||||
|
||||
const listData = ref<any>([])
|
||||
const optionList = ref<any>([])
|
||||
let defStr = `return {
|
||||
|
||||
}`
|
||||
const customStr = ref(defStr)
|
||||
|
||||
const optionKey = computed(() => {
|
||||
return optionList.value.filter((item) => item.prop).map((item) => item.value)
|
||||
})
|
||||
|
||||
const openEditor = () => {
|
||||
openMEDialog(
|
||||
{
|
||||
prop: 'value',
|
||||
label: '更多配置编辑',
|
||||
params: {
|
||||
width: '50%',
|
||||
headerBtn: [
|
||||
{
|
||||
name: `配置参考地址`,
|
||||
clickFun: () => {
|
||||
window.open('https://avuejs.com/crud/crud-doc.html')
|
||||
},
|
||||
params: { size: 'small' }
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
customStr
|
||||
)
|
||||
}
|
||||
|
||||
const initFun = () => {
|
||||
listData.value = []
|
||||
optionList.value = []
|
||||
customStr.value = defStr
|
||||
if (props.configKey.length) {
|
||||
props.configKey.forEach((key) => {
|
||||
if (controlOption[key]) listData.value = [...listData.value, ...controlOption[key]]
|
||||
})
|
||||
for (const index in listData.value) collapseObj.value[index] = Number(index) == 0
|
||||
}
|
||||
if (listData.value.length) {
|
||||
if (optionStr.value && optionStr.value.indexOf('option_str')) {
|
||||
const data = cloneDeep(listData.value)
|
||||
const dataObj = {}
|
||||
data.forEach((typeItem) => typeItem.list.forEach((item) => (dataObj[item.value] = item)))
|
||||
const { custom_str, option_str } = handleStrObj(optionStr.value) as any
|
||||
if (custom_str) customStr.value = custom_str
|
||||
if (option_str) {
|
||||
const option = handleStrObj(option_str)
|
||||
for (let key in option) {
|
||||
if (dataObj[key])
|
||||
optionList.value.push({
|
||||
...dataObj[key],
|
||||
val: option[key],
|
||||
prop: `option_${Math.ceil(Math.random() * 9999999)}`
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.show,
|
||||
(val) => {
|
||||
if (val) initFun()
|
||||
}
|
||||
)
|
||||
const setCollapse = (index) => {
|
||||
for (let key in collapseObj.value) {
|
||||
collapseObj.value[key] = key == index ? !collapseObj.value[key] : false
|
||||
}
|
||||
}
|
||||
|
||||
const handleAddColumn = (e) => {
|
||||
const newIndex = e.newIndex
|
||||
const data = cloneDeep(optionList.value[newIndex])
|
||||
if (optionKey.value.includes(data.value)) {
|
||||
optionList.value = optionList.value.filter((item) => item.prop)
|
||||
message.info('该配置已存在')
|
||||
return
|
||||
}
|
||||
if (!data.prop) data.prop = `option_${Math.ceil(Math.random() * 9999999)}`
|
||||
delete data.icon
|
||||
optionList.value[newIndex] = data
|
||||
}
|
||||
const setOption = (row) => {
|
||||
optionList.value.push(row)
|
||||
handleAddColumn({ newIndex: optionList.value.length - 1 })
|
||||
setTimeout(() => {
|
||||
getOptionStr()
|
||||
}, 300)
|
||||
}
|
||||
|
||||
const delRow = (row) => {
|
||||
optionList.value = optionList.value.filter((item) => item.prop != row.prop)
|
||||
}
|
||||
|
||||
const getOptionStr = () => {
|
||||
if (optionList.value.length === 0 && customStr.value == defStr) {
|
||||
return ''
|
||||
}
|
||||
|
||||
let optionStr = 'return {'
|
||||
const setValue = (value) => {
|
||||
if (typeof value == 'string') value = `'${value}'`
|
||||
if (value instanceof Array) {
|
||||
let text = ''
|
||||
value.forEach((item, index) => {
|
||||
text = `${text}${index != 0 ? ',' : ''}${setValue(item)}`
|
||||
})
|
||||
return `[${text}]`
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
optionList.value.forEach((item) => {
|
||||
optionStr = `${optionStr}
|
||||
"${item.value}":${setValue(item.val)},`
|
||||
})
|
||||
optionStr = `${optionStr}
|
||||
}`
|
||||
return `return {
|
||||
option_str:${'`'}${optionStr}${'`'},
|
||||
custom_str: ${'`'}${customStr.value}${'`'},
|
||||
}`
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
initFun()
|
||||
})
|
||||
|
||||
defineExpose({ getOptionStr })
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.config-option {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin-top: -1px;
|
||||
|
||||
.left-tree {
|
||||
padding: 10px;
|
||||
border: 1px solid #f1f1f1;
|
||||
|
||||
.config-item {
|
||||
width: 160px;
|
||||
padding: 4px 8px;
|
||||
margin-bottom: 10px;
|
||||
font-size: 14px;
|
||||
cursor: move;
|
||||
background-color: #f4f6fc;
|
||||
border: 1px dashed #f4f6fc;
|
||||
|
||||
&:hover {
|
||||
color: var(--el-color-primary);
|
||||
border-color: var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.main-option {
|
||||
padding: 0;
|
||||
border: 1px solid #f1f1f1;
|
||||
border-left: 0;
|
||||
|
||||
.option-content {
|
||||
height: calc(100% - 41px);
|
||||
|
||||
.content-draggable {
|
||||
height: 100%;
|
||||
padding-bottom: 55px;
|
||||
overflow-y: auto;
|
||||
box-sizing: border-box;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
width: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.option-ghost {
|
||||
position: relative;
|
||||
width: 0 !important;
|
||||
height: 40px;
|
||||
min-width: 0 !important;
|
||||
padding: 0 !important;
|
||||
margin: 1px 2px 0;
|
||||
overflow: hidden;
|
||||
font-size: 0;
|
||||
background: white;
|
||||
border-left: 5px solid var(--el-color-primary);
|
||||
content: '';
|
||||
outline: none 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
|
||||
.table-item-row {
|
||||
display: flex;
|
||||
align-items: end;
|
||||
|
||||
.row-item {
|
||||
height: 100%;
|
||||
min-height: 32px;
|
||||
padding: 4px 0;
|
||||
font-size: 14px;
|
||||
line-height: 32px;
|
||||
border-bottom: 1px solid #dcdfe6;
|
||||
|
||||
.cell {
|
||||
position: relative;
|
||||
padding: 0 12px;
|
||||
|
||||
& > div {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.tip {
|
||||
position: absolute;
|
||||
bottom: -5px;
|
||||
left: 0;
|
||||
padding-right: 10px;
|
||||
font-size: 12px;
|
||||
color: #e6a23c;
|
||||
text-align: right;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
|
||||
&:nth-child(1) {
|
||||
flex-basis: 60px;
|
||||
text-align: center;
|
||||
border-right: 1px solid #dcdfe6;
|
||||
}
|
||||
|
||||
&:nth-child(2) {
|
||||
flex-basis: 260px;
|
||||
border-right: 1px solid #dcdfe6;
|
||||
}
|
||||
|
||||
&:nth-child(3) {
|
||||
flex: 1;
|
||||
line-height: normal;
|
||||
border-right: 1px solid #dcdfe6;
|
||||
}
|
||||
|
||||
&:nth-child(4) {
|
||||
flex-basis: 110px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&.move-box {
|
||||
cursor: move;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.option-title {
|
||||
background-color: #fafafa;
|
||||
|
||||
.row-item:nth-child(3) {
|
||||
line-height: 32px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.right-custom {
|
||||
.custom-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 32px;
|
||||
padding: 4px 0;
|
||||
font-size: 14px;
|
||||
line-height: 32px;
|
||||
text-align: center;
|
||||
background-color: #fafafa;
|
||||
border: 1px solid #dcdfe6;
|
||||
}
|
||||
|
||||
.custom-content {
|
||||
width: 100%;
|
||||
height: calc(100% - 42px);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
364
src/views/lowdesign/tableDesign/components/CustomButton.vue
Normal file
364
src/views/lowdesign/tableDesign/components/CustomButton.vue
Normal file
@@ -0,0 +1,364 @@
|
||||
<template>
|
||||
<DesignPopup
|
||||
v-model="popupShow"
|
||||
:title="`自定义按钮配置 【${tableDescribe}】(${tableName})`"
|
||||
:fullscreen="true"
|
||||
v-model:isFull="isFull"
|
||||
>
|
||||
<div class="p-20px">
|
||||
<avue-crud
|
||||
ref="crudRef"
|
||||
v-model="tableForm"
|
||||
v-model:page="tablePage"
|
||||
v-model:search="tableSearch"
|
||||
:data="tableData"
|
||||
:option="tableOption"
|
||||
:table-loading="loading"
|
||||
:before-open="beforeOpen"
|
||||
@search-change="searchChange"
|
||||
@search-reset="resetChange"
|
||||
@row-save="rowSave"
|
||||
@row-update="rowUpdate"
|
||||
@row-del="rowDel"
|
||||
@refresh-change="getTableData"
|
||||
@size-change="sizeChange"
|
||||
@current-change="currentChange"
|
||||
@selection-change="selectionChange"
|
||||
>
|
||||
<template #menu-left="{ size }">
|
||||
<el-button
|
||||
:size="size"
|
||||
type="danger"
|
||||
:disabled="!tableSelect.length"
|
||||
@click="rowDel(selectIds)"
|
||||
>
|
||||
<Icon :size="16" icon="mi:delete" class="mr-3px" />批量删除
|
||||
</el-button>
|
||||
</template>
|
||||
<template #buttonExp-form="scope">
|
||||
<avue-input
|
||||
v-model="tableForm[scope.column.prop]"
|
||||
:size="scope.size"
|
||||
readonly
|
||||
:placeholder="`请输入 ${scope.column.label}`"
|
||||
@click="openMEDialog(scope.column, tableForm)"
|
||||
></avue-input>
|
||||
</template>
|
||||
<template #buttonIcon-form="{ column, size, disabled }">
|
||||
<IconSelectInput
|
||||
v-model="tableForm.buttonIcon"
|
||||
:prop="column.prop"
|
||||
:column="column"
|
||||
:size="size"
|
||||
:disabled="disabled"
|
||||
/>
|
||||
</template>
|
||||
<template #buttonIcon="{ row }">
|
||||
<Icon :size="14" :icon="row.buttonIcon" />
|
||||
</template>
|
||||
<template #buttonI18n-form>
|
||||
<I18nInput v-model="tableForm.buttonI18n" :def-name="tableForm.buttonName"></I18nInput>
|
||||
</template>
|
||||
</avue-crud>
|
||||
</div>
|
||||
<DesignPopup v-model="MEDialog.value" v-bind="MEDialog.params" :isBodyFull="true">
|
||||
<template #title>{{ MEDialog.title }}</template>
|
||||
<template #default>
|
||||
<MonacoEditor
|
||||
class="bg-#1e1e1e"
|
||||
v-model="MEData.value"
|
||||
v-bind="MEData.params"
|
||||
></MonacoEditor>
|
||||
</template>
|
||||
</DesignPopup>
|
||||
</DesignPopup>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import * as ButtonApi from '@/api/design/table/customButton'
|
||||
import useMEDialog from '@/hooks/design/useMEDialog'
|
||||
import { I18nInput } from '@/components/LowDesign/src/shareControl/index'
|
||||
import { customButtonExample } from '@/components/LowDesign/src/utils/example'
|
||||
import useCopyText from '@/hooks/design/useCopyText'
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
defineOptions({ name: 'CustomButton' })
|
||||
interface Props {
|
||||
dbformId?: string
|
||||
tableName?: string
|
||||
tableDescribe?: string
|
||||
}
|
||||
const popupShow = defineModel({ default: false, type: Boolean })
|
||||
const props = defineProps<Props>()
|
||||
|
||||
const { copyText } = useCopyText()
|
||||
const { MEDialog, MEData, openMEDialog } = useMEDialog()
|
||||
|
||||
const loading = ref(true) // 列表的加载中消息弹窗
|
||||
const defaultBtn = [
|
||||
'addBtn',
|
||||
'editBtn',
|
||||
'viewBtn',
|
||||
'delBtn',
|
||||
'importBtn',
|
||||
'exportBtn',
|
||||
'batchDelBtn'
|
||||
]
|
||||
const tableOption = reactive({
|
||||
border: true,
|
||||
align: 'center',
|
||||
headerAlign: 'center',
|
||||
searchMenuSpan: 6,
|
||||
searchMenuPosition: 'left',
|
||||
labelSuffix: ' ',
|
||||
span: 12,
|
||||
calcHeight: 20,
|
||||
labelWidth: 100,
|
||||
dialogWidth: 900,
|
||||
selection: true,
|
||||
viewBtn: false,
|
||||
column: {
|
||||
buttonName: {
|
||||
label: '按钮名称',
|
||||
rules: [{ required: true, message: '请输入按钮名称', trigger: 'blur' }]
|
||||
},
|
||||
buttonCode: {
|
||||
label: '按钮编码',
|
||||
rules: [
|
||||
{ required: true, message: '请输入按钮编码', trigger: 'blur' },
|
||||
{
|
||||
validator: (rule, value, callback) => {
|
||||
if (defaultBtn.includes(value)) callback(`请不要以${value}作为按钮编码`)
|
||||
else callback()
|
||||
},
|
||||
trigger: 'blur'
|
||||
}
|
||||
]
|
||||
},
|
||||
buttonLocation: {
|
||||
label: '按钮位置',
|
||||
type: 'select',
|
||||
clearable: false,
|
||||
dicData: [
|
||||
{ label: '操作列按钮', value: 'menu' },
|
||||
{ label: '表格头部按钮', value: 'header' }
|
||||
],
|
||||
value: 'menu'
|
||||
},
|
||||
buttonType: {
|
||||
label: '按钮类型',
|
||||
type: 'select',
|
||||
clearable: false,
|
||||
dicData: [
|
||||
{ label: '默认', value: '' },
|
||||
{ label: '主要', value: 'primary' },
|
||||
{ label: '成功', value: 'success' },
|
||||
{ label: '信息', value: 'info' },
|
||||
{ label: '警告', value: 'warning' },
|
||||
{ label: '危险', value: 'danger' }
|
||||
],
|
||||
value: ''
|
||||
},
|
||||
buttonIcon: {
|
||||
label: '按钮图标'
|
||||
},
|
||||
buttonSort: {
|
||||
label: '按钮排序',
|
||||
value: '1'
|
||||
},
|
||||
buttonI18n: {
|
||||
label: '国际化配置',
|
||||
row: true,
|
||||
hide: true
|
||||
},
|
||||
buttonShow: {
|
||||
label: '是否启用',
|
||||
type: 'radio',
|
||||
dicData: [
|
||||
{ label: '启用', value: 'Y' },
|
||||
{ label: '停用', value: 'N' }
|
||||
],
|
||||
span: 12,
|
||||
value: 'Y'
|
||||
},
|
||||
buttonAuth: {
|
||||
label: '权限控制',
|
||||
type: 'radio',
|
||||
labelTip: '开启后可以通过菜单权限控制显隐',
|
||||
dicData: [
|
||||
{ label: '启用', value: 'Y' },
|
||||
{ label: '停用', value: 'N' }
|
||||
],
|
||||
span: 12,
|
||||
value: 'N'
|
||||
},
|
||||
|
||||
buttonExp: {
|
||||
label: '其他配置',
|
||||
span: 24,
|
||||
hide: true,
|
||||
params: {
|
||||
providerType: 'tableJsEnhance',
|
||||
width: '50%',
|
||||
headerBtn: [
|
||||
{
|
||||
name: `复制配置示例`,
|
||||
icon: 'solar:copy-outline',
|
||||
clickFun: () => {
|
||||
copyText(customButtonExample || '')
|
||||
},
|
||||
params: {
|
||||
size: 'small',
|
||||
type: 'primary'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}) //表格配置
|
||||
const tableForm = ref<any>({})
|
||||
const tableData = ref([])
|
||||
const tableSearch = ref({})
|
||||
const tablePage = ref({
|
||||
currentPage: 1,
|
||||
pageSize: 10,
|
||||
total: 0
|
||||
})
|
||||
const isFull = ref(true)
|
||||
const tableSelect = ref([])
|
||||
|
||||
const crudRef = ref()
|
||||
const { initTableLayout, windowSize } = useCrudHeight(crudRef)
|
||||
|
||||
const selectIds = computed(() => {
|
||||
return tableSelect.value.map((item) => item['id'])
|
||||
})
|
||||
|
||||
const calcHeight = computed(() => {
|
||||
return isFull.value ? 20 : windowSize.height.value * 0.1 + 70
|
||||
})
|
||||
|
||||
watch(
|
||||
() => popupShow.value,
|
||||
(val: boolean) => {
|
||||
if (val) getTableData()
|
||||
}
|
||||
)
|
||||
|
||||
watch(
|
||||
() => calcHeight.value,
|
||||
() => {
|
||||
tableOption.calcHeight = calcHeight.value
|
||||
}
|
||||
)
|
||||
|
||||
watch(
|
||||
() => isFull.value,
|
||||
() => {
|
||||
initTableLayout()
|
||||
}
|
||||
)
|
||||
|
||||
/** 查询列表 */
|
||||
const getTableData = async () => {
|
||||
loading.value = true
|
||||
let searchObj = {
|
||||
...tableSearch.value,
|
||||
pageNo: tablePage.value.currentPage,
|
||||
pageSize: tablePage.value.pageSize,
|
||||
dbformId: props.dbformId
|
||||
}
|
||||
for (let key in searchObj) if (searchObj[key] === '') delete searchObj[key]
|
||||
try {
|
||||
const data = await ButtonApi.getBtnList(searchObj)
|
||||
tableData.value = data.records
|
||||
tablePage.value.total = data.total
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const searchChange = (params, done) => {
|
||||
tablePage.value.currentPage = 1
|
||||
getTableData().finally(() => {
|
||||
done()
|
||||
})
|
||||
}
|
||||
|
||||
/** 清空按钮操作 */
|
||||
const resetChange = () => {
|
||||
searchChange({}, () => {})
|
||||
}
|
||||
|
||||
const sizeChange = (pageSize) => {
|
||||
tablePage.value.pageSize = pageSize
|
||||
resetChange()
|
||||
}
|
||||
|
||||
const currentChange = (currentPage) => {
|
||||
tablePage.value.currentPage = currentPage
|
||||
getTableData()
|
||||
}
|
||||
|
||||
const selectionChange = (data) => {
|
||||
tableSelect.value = data
|
||||
}
|
||||
|
||||
/** 表单打开前 */
|
||||
const beforeOpen = async (done, type) => {
|
||||
if (['edit', 'view'].includes(type) && tableForm.value.id) {
|
||||
tableForm.value = await ButtonApi.getBtnDetail(tableForm.value.id)
|
||||
}
|
||||
done()
|
||||
}
|
||||
|
||||
/** 新增操作 */
|
||||
const rowSave = async (form, done, loading) => {
|
||||
let bool = await ButtonApi.saveBtnData({ ...form, dbformId: props.dbformId }).catch(() => false)
|
||||
if (bool) {
|
||||
message.success(t('common.createSuccess'))
|
||||
resetChange()
|
||||
done()
|
||||
} else loading()
|
||||
}
|
||||
|
||||
/** 编辑操作 */
|
||||
const rowUpdate = async (form, index, done, loading) => {
|
||||
let bool = await ButtonApi.updateBtnData({ ...form, dbformId: props.dbformId }).catch(() => false)
|
||||
if (bool) {
|
||||
message.success(t('common.updateSuccess'))
|
||||
getTableData()
|
||||
done()
|
||||
} else loading()
|
||||
}
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const rowDel = async (data) => {
|
||||
try {
|
||||
// 删除的二次确认
|
||||
await message.delConfirm()
|
||||
// 发起删除
|
||||
await ButtonApi.deleteBtnData(data instanceof Array ? data : [data.id])
|
||||
message.success(t('common.delSuccess'))
|
||||
// 刷新列表
|
||||
await getTableData()
|
||||
} catch {}
|
||||
}
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(async () => {
|
||||
await getTableData()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.button-info-dialog {
|
||||
.el-dialog__body {
|
||||
padding: 20px;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
1632
src/views/lowdesign/tableDesign/components/DataOriginOption.vue
Normal file
1632
src/views/lowdesign/tableDesign/components/DataOriginOption.vue
Normal file
File diff suppressed because it is too large
Load Diff
452
src/views/lowdesign/tableDesign/components/FormattingOption.vue
Normal file
452
src/views/lowdesign/tableDesign/components/FormattingOption.vue
Normal file
@@ -0,0 +1,452 @@
|
||||
<template>
|
||||
<div class="formatting-option table-design-option">
|
||||
<el-tabs v-model="tabsValue" tab-position="top" type="border-card">
|
||||
<el-tab-pane name="group">
|
||||
<template #label>
|
||||
<el-radio v-model="tabsValue" label="group">JAVA表达式处理</el-radio>
|
||||
</template>
|
||||
<el-container class="h-100%">
|
||||
<el-aside width="180px" class="left-tree">
|
||||
<div class="formatting-title">
|
||||
<span>配置工具</span>
|
||||
</div>
|
||||
<div class="formatting-draggable">
|
||||
<draggable
|
||||
class="formatting-content"
|
||||
tag="div"
|
||||
:list="optionUtil"
|
||||
:group="{ name: 'config', pull: 'clone', put: false }"
|
||||
ghost-class="formatting-ghost"
|
||||
:sort="false"
|
||||
item-key="value"
|
||||
>
|
||||
<template #item="{ element }">
|
||||
<div class="formatting-item" @click="setOption(element)">
|
||||
<span>{{ element.label }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</draggable>
|
||||
</div>
|
||||
</el-aside>
|
||||
<el-main class="main-option">
|
||||
<div class="option-content">
|
||||
<draggable
|
||||
class="content-draggable"
|
||||
:list="formattingList"
|
||||
:group="{ name: 'option', put: true }"
|
||||
ghost-class="option-ghost"
|
||||
:animation="300"
|
||||
handle=".move-icon"
|
||||
item-key="prop"
|
||||
@add="handleAddColumn"
|
||||
>
|
||||
<template #item="{ element }">
|
||||
<div class="formatting-control">
|
||||
<template v-if="element.value == 'fieldSelect'">
|
||||
<avue-select
|
||||
class="w-200px"
|
||||
v-model="element.text"
|
||||
:dic="fieldList"
|
||||
:class="['select_' + element.prop + '_filed']"
|
||||
:style="{ width: element.width_filed || '80px' }"
|
||||
placeholder="请选择 字段"
|
||||
@change="() => setSelectWidth(element, 5, '_filed')"
|
||||
></avue-select>
|
||||
</template>
|
||||
<template v-if="element.value == 'modeType'">
|
||||
<avue-select
|
||||
class="w-100px"
|
||||
v-model="element.text"
|
||||
:dic="modeType"
|
||||
:class="['select_' + element.prop + '_mode']"
|
||||
:style="{ width: element.width_mode || '80px' }"
|
||||
placeholder="计算方式"
|
||||
@change="() => setSelectWidth(element, 5, '_mode')"
|
||||
></avue-select>
|
||||
</template>
|
||||
<template v-if="element.value == 'parenthesis'">
|
||||
<avue-select
|
||||
class="w-100px"
|
||||
v-model="element.text"
|
||||
:dic="parenthesis"
|
||||
:class="['select_' + element.prop + '_par']"
|
||||
:style="{ width: element.width_par || '80px' }"
|
||||
placeholder="括号运算符"
|
||||
@change="() => setSelectWidth(element, 5, '_par')"
|
||||
></avue-select>
|
||||
</template>
|
||||
<template v-if="element.value == 'modeNum'">
|
||||
<avue-input-number
|
||||
class="w-80px!"
|
||||
v-model="element.text"
|
||||
:controls="false"
|
||||
placeholder="计算值"
|
||||
></avue-input-number>
|
||||
</template>
|
||||
<template v-if="element.value == 'joinStr'">
|
||||
<avue-input
|
||||
class="w-120px"
|
||||
v-model="element.text"
|
||||
placeholder="拼接字符"
|
||||
></avue-input>
|
||||
</template>
|
||||
<div class="del-icon" @click="delRow(element)">
|
||||
<Icon color="#F56C6C" :size="22" icon="lets-icons:dell-fill" class="mr-3px" />
|
||||
</div>
|
||||
<div class="move-icon">
|
||||
<Icon :size="22" icon="mingcute:move-line" class="mr-3px" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</draggable>
|
||||
</div>
|
||||
<div class="formatting-result flex h-60px box-border p-10px">
|
||||
<div class="flex-basis-85px flex-shrink-0">输 出 内 容:</div>
|
||||
<div class="flex-1 flex flex-wrap gap-x-20px">
|
||||
<span v-for="(item, index) in valueText" :key="index">
|
||||
{{ item.type == 'CALCULATE' ? '计算' : '拼接' }}:<span class="c-#409EFF">{{
|
||||
item.value
|
||||
}}</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</el-main>
|
||||
</el-container>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane name="custom" class="pl-20px pr-20px pt-10px">
|
||||
<template #label>
|
||||
<el-radio v-model="tabsValue" label="custom">JAVA函数处理</el-radio>
|
||||
</template>
|
||||
<div class="custom-box">
|
||||
<div class="flex items-center mt-10px">
|
||||
<div class="flex-basis-140px">JAVA类名/Sping Key:</div>
|
||||
<avue-input
|
||||
class="flex-1"
|
||||
v-model="customValue.javaPath"
|
||||
placeholder="请输入 JAVA类名/Sping Key"
|
||||
></avue-input>
|
||||
</div>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import draggable from 'vuedraggable'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
|
||||
defineOptions({ name: 'FormattingOption' })
|
||||
|
||||
interface Props {
|
||||
show: boolean
|
||||
fieldList: Array<any>
|
||||
}
|
||||
interface TextList {
|
||||
value: string
|
||||
type: 'CALCULATE' | 'CONCAT'
|
||||
}
|
||||
const props = defineProps<Props>()
|
||||
const formattingStr = defineModel<string>({ default: '' })
|
||||
const formattingList = ref<any>([])
|
||||
|
||||
const tabsValue = ref('group')
|
||||
const customValue = ref({
|
||||
javaPath: ''
|
||||
})
|
||||
const modeType = [
|
||||
{ label: '加法', value: ' + ' },
|
||||
{ label: '减法', value: ' - ' },
|
||||
{ label: '乘法', value: ' * ' },
|
||||
{ label: '除法', value: ' / ' },
|
||||
{ label: '取余', value: ' % ' },
|
||||
{ label: '取模', value: ' mod ' }
|
||||
]
|
||||
const parenthesis = [
|
||||
{ label: '左括号', value: ' ( ' },
|
||||
{ label: '右括号', value: ' ) ' }
|
||||
]
|
||||
|
||||
const optionUtil = [
|
||||
{ label: '字段选择', value: 'fieldSelect', type: 'CALCULATE' },
|
||||
{ label: '计算方式', value: 'modeType', type: 'CALCULATE' },
|
||||
{ label: '括号运算符', value: 'parenthesis', type: 'CALCULATE' },
|
||||
{ label: '计算值', value: 'modeNum', type: 'CALCULATE' },
|
||||
{ label: '拼接字符', value: 'joinStr', tip: '拼接字符 例如:() - 等', type: 'CONCAT' }
|
||||
]
|
||||
|
||||
const valueText = computed(() => {
|
||||
let groupText = ''
|
||||
let lastType: 'CALCULATE' | 'CONCAT'
|
||||
let textList: TextList[] = []
|
||||
formattingList.value.forEach((item) => {
|
||||
let text = item.text
|
||||
if (item.value == 'fieldSelect' && text) text = '#{' + text + '}'
|
||||
if (item.value == 'modeNum' && text === null) text = ''
|
||||
if (!lastType || lastType == item.type) {
|
||||
groupText = `${groupText}${text}`
|
||||
if (!textList.length) textList.push({ value: groupText, type: item.type })
|
||||
else textList[textList.length - 1].value = groupText
|
||||
} else {
|
||||
groupText = text
|
||||
textList.push({ value: groupText, type: item.type })
|
||||
}
|
||||
lastType = item.type
|
||||
})
|
||||
return textList
|
||||
})
|
||||
|
||||
const initFun = () => {
|
||||
const optionObj = {}
|
||||
optionUtil.forEach((item) => (optionObj[item.value] = item))
|
||||
formattingList.value = []
|
||||
if (formattingStr.value) {
|
||||
const { list, custom, valueType } = JSON.parse(formattingStr.value)
|
||||
customValue.value = custom || { javaPath: '' }
|
||||
tabsValue.value = valueType || 'group'
|
||||
if (list) {
|
||||
formattingList.value = list.map((item) => {
|
||||
return {
|
||||
...optionObj[item.value],
|
||||
...item,
|
||||
prop: `option_${Math.ceil(Math.random() * 9999999)}`
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.show,
|
||||
(val) => {
|
||||
if (val) initFun()
|
||||
}
|
||||
)
|
||||
|
||||
const setSelectWidth = (element, num = 0, addClass = '') => {
|
||||
setTimeout(() => {
|
||||
const el = document.querySelector(
|
||||
`.main-option .select_${element.prop}${addClass} .el-select__selected-item.el-select__placeholder span`
|
||||
) as HTMLSpanElement
|
||||
if (!el) return
|
||||
const width = el.offsetWidth
|
||||
element[`width${addClass}`] = width + 40 + num + 'px'
|
||||
}, 30)
|
||||
}
|
||||
|
||||
const handleAddColumn = (e) => {
|
||||
const newIndex = e.newIndex
|
||||
const data = cloneDeep(formattingList.value[newIndex])
|
||||
if (!data.prop) data.prop = `option_${Math.ceil(Math.random() * 9999999)}`
|
||||
if (data.value == 'modeNum') data.text = null
|
||||
else data.text = ''
|
||||
if (data.value == 'parenthesis') {
|
||||
let valueIndex: number | null = null
|
||||
const groupText = valueText.value[valueText.value.length - 1].value || ''
|
||||
const lastLeft = groupText.lastIndexOf(' ( ')
|
||||
const lastRight = groupText.lastIndexOf(' ) ')
|
||||
if ((lastLeft == -1 && lastRight == -1) || lastRight > lastLeft) {
|
||||
valueIndex = 0
|
||||
} else valueIndex = 1
|
||||
if (valueIndex !== null) data.text = parenthesis[valueIndex].value
|
||||
}
|
||||
delete data.label
|
||||
|
||||
formattingList.value[newIndex] = data
|
||||
}
|
||||
const setOption = (row) => {
|
||||
formattingList.value.push(row)
|
||||
handleAddColumn({ newIndex: formattingList.value.length - 1 })
|
||||
setTimeout(() => {
|
||||
getOptionStr()
|
||||
}, 300)
|
||||
}
|
||||
|
||||
const delRow = (row) => {
|
||||
formattingList.value = formattingList.value.filter((item) => item.prop != row.prop)
|
||||
}
|
||||
|
||||
const getOptionStr = () => {
|
||||
if (formattingList.value.length || customValue.value)
|
||||
return JSON.stringify({
|
||||
list: formattingList.value.map((item) => {
|
||||
return { value: item.value, text: item.text }
|
||||
}),
|
||||
valueType: tabsValue.value,
|
||||
group: valueText.value.filter((item) => item.value !== ''),
|
||||
custom: customValue.value
|
||||
})
|
||||
else ''
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
initFun()
|
||||
})
|
||||
|
||||
defineExpose({ getOptionStr })
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.formatting-option {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
.left-tree {
|
||||
padding: 10px;
|
||||
border-right: 1px solid #f1f1f1;
|
||||
|
||||
.formatting-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
cursor: pointer;
|
||||
|
||||
.el-icon {
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
|
||||
&:nth-last-of-type(1) {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.formatting-draggable {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.formatting-item {
|
||||
width: 120px;
|
||||
padding: 4px 8px;
|
||||
margin-bottom: 10px;
|
||||
font-size: 14px;
|
||||
cursor: move;
|
||||
background-color: #f4f6fc;
|
||||
border: 1px dashed #f4f6fc;
|
||||
|
||||
&:hover {
|
||||
color: var(--el-color-primary);
|
||||
border-color: var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.main-option {
|
||||
padding: 0;
|
||||
border-left: 0;
|
||||
|
||||
.option-content {
|
||||
height: calc(100% - 60px);
|
||||
|
||||
.content-draggable {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
padding: 10px;
|
||||
padding-bottom: 55px;
|
||||
overflow-y: auto;
|
||||
box-sizing: border-box;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px 8px;
|
||||
align-content: flex-start;
|
||||
|
||||
.formatting-control {
|
||||
position: relative;
|
||||
|
||||
.del-icon {
|
||||
position: absolute;
|
||||
top: -9px;
|
||||
left: -8px;
|
||||
display: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.move-icon {
|
||||
position: absolute;
|
||||
right: -14px;
|
||||
bottom: -9px;
|
||||
display: none;
|
||||
cursor: move;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.del-icon {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.move-icon {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.option-ghost {
|
||||
position: relative;
|
||||
width: 0 !important;
|
||||
height: 32px;
|
||||
min-width: 0 !important;
|
||||
padding: 0 !important;
|
||||
overflow: hidden;
|
||||
font-size: 0;
|
||||
background: white;
|
||||
border-left: 5px solid var(--el-color-primary);
|
||||
content: '';
|
||||
outline: none 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
|
||||
.formatting-result {
|
||||
border-top: 1px solid #dcdfe6;
|
||||
}
|
||||
}
|
||||
|
||||
.right-custom {
|
||||
.custom-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 32px;
|
||||
padding: 4px 0;
|
||||
font-size: 14px;
|
||||
line-height: 32px;
|
||||
text-align: center;
|
||||
background-color: #fafafa;
|
||||
border: 1px solid #dcdfe6;
|
||||
}
|
||||
|
||||
.custom-content {
|
||||
width: 100%;
|
||||
height: calc(100% - 42px);
|
||||
}
|
||||
}
|
||||
|
||||
.custom-box {
|
||||
height: calc(100% - 51px);
|
||||
}
|
||||
|
||||
::v-deep(.el-tabs) {
|
||||
height: 100%;
|
||||
border: none;
|
||||
|
||||
.el-tabs__header {
|
||||
margin-right: 0;
|
||||
|
||||
.el-tabs__item {
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
.el-tabs__content {
|
||||
height: calc(100% - 39px);
|
||||
padding: 0;
|
||||
|
||||
.el-tab-pane {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
88
src/views/lowdesign/tableDesign/components/InfoVxeTable.vue
Normal file
88
src/views/lowdesign/tableDesign/components/InfoVxeTable.vue
Normal file
@@ -0,0 +1,88 @@
|
||||
<template>
|
||||
<vxe-table
|
||||
ref="vxeTableRef"
|
||||
:class="tabItem.key + '-vxe-table'"
|
||||
:border="true"
|
||||
size="small"
|
||||
show-overflow="tooltip"
|
||||
height="356"
|
||||
:row-config="{ isHover: true }"
|
||||
:edit-config="{ trigger: 'dblclick', mode: 'row', showIcon: false }"
|
||||
:data="tableData"
|
||||
:scroll-y="{ enabled: true }"
|
||||
:checkbox-config="checkboxConfig || {}"
|
||||
:row-class-name="rowClassName"
|
||||
@checkbox-all="(obj) => emit('selection-change', obj)"
|
||||
@checkbox-change="(obj) => emit('selection-change', obj)"
|
||||
@cell-click="(obj) => emit('cell-click', obj)"
|
||||
>
|
||||
<vxe-column v-if="tabItem.edit" type="checkbox" width="50" align="center"></vxe-column>
|
||||
<vxe-column type="seq" width="50" align="center"></vxe-column>
|
||||
<vxe-column v-if="tabItem.edit" key="customDrag" field="customDrag" width="50" align="center">
|
||||
<template #default="{ rowIndex }">
|
||||
<el-dropdown
|
||||
trigger="hover"
|
||||
placement="bottom"
|
||||
@command="(obj) => emit('dropdown-command', obj)"
|
||||
>
|
||||
<div>
|
||||
<Icon :size="20" icon="gg:menu-grid-r" />
|
||||
</div>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item :command="{ index: rowIndex, type: 'up' }" :disabled="rowIndex == 0"
|
||||
>向上移</el-dropdown-item
|
||||
>
|
||||
<el-dropdown-item
|
||||
:command="{ index: rowIndex, type: 'down' }"
|
||||
:disabled="rowIndex == tableData.length - 1"
|
||||
>向下移</el-dropdown-item
|
||||
>
|
||||
<el-dropdown-item :command="{ index: rowIndex, type: 'add' }"
|
||||
>插入一行</el-dropdown-item
|
||||
>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column v-for="(item, key) in column" :key="key" :field="key" v-bind="item"></vxe-column>
|
||||
</vxe-table>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
defineOptions({ name: 'InfoVxeTable' })
|
||||
|
||||
interface Props {
|
||||
tabItem: any
|
||||
column: object
|
||||
rowClassName?: string | Function
|
||||
checkboxConfig?: object
|
||||
}
|
||||
const props = defineProps<Props>()
|
||||
const tableData = defineModel<Array<any>>({ default: [] })
|
||||
const emit = defineEmits(['selection-change', 'cell-click', 'dropdown-command'])
|
||||
|
||||
const vxeTableRef = ref()
|
||||
|
||||
defineExpose({ vxeTableRef })
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.view-vxe-table,
|
||||
.exp-vxe-table {
|
||||
::v-deep(.vxe-table--header) {
|
||||
.vxe-header--column {
|
||||
position: relative;
|
||||
|
||||
.vxe-cell {
|
||||
.vxe-cell-title-suffix-icon {
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
right: -2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
26
src/views/lowdesign/tableDesign/components/InfoVxeTopBtn.vue
Normal file
26
src/views/lowdesign/tableDesign/components/InfoVxeTopBtn.vue
Normal file
@@ -0,0 +1,26 @@
|
||||
<template>
|
||||
<div class="mb-10px">
|
||||
<el-button :size="size" type="primary" icon="el-icon-plus" @click.stop="emit('cell-add')">
|
||||
新增
|
||||
</el-button>
|
||||
<el-button :size="size" type="danger" @click.stop="emit('del-data')" :disabled="!selectNum">
|
||||
<Icon :size="16" icon="mi:delete" class="mr-3px" />批量删除
|
||||
</el-button>
|
||||
<el-button @click.stop="emit('order-data')">调整排序</el-button>
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
defineOptions({ name: 'InfoVxeTopBtn' })
|
||||
|
||||
interface Props {
|
||||
selectNum: number
|
||||
tabItem: any
|
||||
size: string
|
||||
}
|
||||
const props = defineProps<Props>()
|
||||
const emit = defineEmits(['cell-add', 'del-data', 'order-data'])
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
379
src/views/lowdesign/tableDesign/components/JavaEnhance.vue
Normal file
379
src/views/lowdesign/tableDesign/components/JavaEnhance.vue
Normal file
@@ -0,0 +1,379 @@
|
||||
<template>
|
||||
<DesignPopup
|
||||
v-model="popupShow"
|
||||
:title="`Java增强 【${tableDescribe}】(${tableName})`"
|
||||
:fullscreen="true"
|
||||
v-model:isFull="isFull"
|
||||
>
|
||||
<div class="p-20px">
|
||||
<avue-crud
|
||||
ref="crudRef"
|
||||
v-model="tableForm"
|
||||
v-model:page="tablePage"
|
||||
v-model:search="tableSearch"
|
||||
:data="tableData"
|
||||
:option="tableOption"
|
||||
:table-loading="loading"
|
||||
:before-open="beforeOpen"
|
||||
@search-change="searchChange"
|
||||
@search-reset="resetChange"
|
||||
@row-save="rowSave"
|
||||
@row-update="rowUpdate"
|
||||
@row-del="rowDel"
|
||||
@refresh-change="getTableData"
|
||||
@size-change="sizeChange"
|
||||
@current-change="currentChange"
|
||||
@selection-change="selectionChange"
|
||||
@sort-change="sortChange"
|
||||
>
|
||||
<template #menu-left="{ size }">
|
||||
<el-button
|
||||
:size="size"
|
||||
type="danger"
|
||||
:disabled="!tableSelect.length"
|
||||
@click="rowDel(selectIds)"
|
||||
>
|
||||
<Icon :size="16" icon="mi:delete" class="mr-3px" />批量删除
|
||||
</el-button>
|
||||
</template>
|
||||
<template #onlineScript-form="{ size, column }">
|
||||
<avue-input
|
||||
type="textarea"
|
||||
:minRows="3"
|
||||
:maxRows="5"
|
||||
v-model="tableForm[column.prop]"
|
||||
:size="size"
|
||||
readonly
|
||||
:placeholder="`请输入 ${column.label}`"
|
||||
@click="openMEDialog(column, tableForm)"
|
||||
></avue-input>
|
||||
</template>
|
||||
</avue-crud>
|
||||
</div>
|
||||
<DesignPopup v-model="MEDialog.value" v-bind="MEDialog.params" :isBodyFull="true">
|
||||
<template #title>{{ MEDialog.title }}</template>
|
||||
<template #default>
|
||||
<MonacoEditor
|
||||
class="bg-#1e1e1e"
|
||||
v-model="MEData.value"
|
||||
v-bind="MEData.params"
|
||||
></MonacoEditor>
|
||||
</template>
|
||||
</DesignPopup>
|
||||
</DesignPopup>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import * as JavaApi from '@/api/design/table/javaEnhance'
|
||||
import useMEDialog from '@/hooks/design/useMEDialog'
|
||||
import { JavaDataExample } from '@/components/LowDesign/src/utils/example'
|
||||
import useCopyText from '@/hooks/design/useCopyText'
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
defineOptions({ name: 'JavaEnhance' })
|
||||
interface Props {
|
||||
dbformId?: string
|
||||
tableName?: string
|
||||
tableDescribe?: string
|
||||
}
|
||||
const popupShow = defineModel({ default: false, type: Boolean })
|
||||
const props = defineProps<Props>()
|
||||
|
||||
const { copyText } = useCopyText()
|
||||
const { MEDialog, MEData, openMEDialog } = useMEDialog()
|
||||
|
||||
const loading = ref(true) // 列表的加载中
|
||||
const tableOption = reactive({
|
||||
border: true,
|
||||
align: 'center',
|
||||
headerAlign: 'center',
|
||||
searchMenuSpan: 6,
|
||||
searchMenuPosition: 'left',
|
||||
labelSuffix: ' ',
|
||||
span: 12,
|
||||
calcHeight: 20,
|
||||
labelWidth: 120,
|
||||
dialogWidth: 900,
|
||||
selection: true,
|
||||
menuWidth: 180,
|
||||
column: {
|
||||
buttonCode: {
|
||||
label: '页面功能',
|
||||
type: 'select',
|
||||
rules: [{ required: true, message: '请选择 页面功能', trigger: 'blur' }],
|
||||
dicData: [
|
||||
{ label: '新增', value: 'add' },
|
||||
{ label: '编辑', value: 'edit' },
|
||||
{ label: '详情', value: 'detail' },
|
||||
{ label: '删除', value: 'delete' },
|
||||
{ label: '列表', value: 'list' },
|
||||
{ label: '导入', value: 'import' },
|
||||
{ label: '导出', value: 'export' },
|
||||
{ label: '统计', value: 'summary' }
|
||||
],
|
||||
sortable: true,
|
||||
editDisabled: true,
|
||||
width: 140,
|
||||
span: 12,
|
||||
control: (val) => {
|
||||
let bool = false
|
||||
if (typeObj.value[val] !== undefined && typeObj.value[val] >= 1) bool = true
|
||||
return {
|
||||
listResultHandleType: { display: bool }
|
||||
}
|
||||
}
|
||||
},
|
||||
listResultHandleType: {
|
||||
label: '结果处理类型',
|
||||
type: 'select',
|
||||
width: 120,
|
||||
rules: [{ required: true, message: '请选择 结果处理类型', trigger: 'blur' }],
|
||||
dicData: [
|
||||
{ label: '串行', value: 0 },
|
||||
{ label: '合集', value: 1 },
|
||||
{ label: '差集', value: 2 },
|
||||
{ label: '并集', value: 3 },
|
||||
{ label: '交集', value: 4 }
|
||||
],
|
||||
value: 0,
|
||||
span: 12,
|
||||
dataType: 'number',
|
||||
display: false
|
||||
},
|
||||
javaType: {
|
||||
label: 'Java类型',
|
||||
type: 'radio',
|
||||
dicData: [
|
||||
{ label: 'spring-key', value: 'spring' },
|
||||
{ label: 'java-class', value: 'class' },
|
||||
{ label: 'http-api', value: 'http' },
|
||||
{ label: '在线编辑', value: 'online_edit' }
|
||||
],
|
||||
value: 'spring',
|
||||
width: 140,
|
||||
span: 24,
|
||||
control: (val) => {
|
||||
const bool = val == 'online_edit'
|
||||
return {
|
||||
javaClassUrl: { display: !bool },
|
||||
onlineScript: { display: bool }
|
||||
}
|
||||
}
|
||||
},
|
||||
javaClassUrl: {
|
||||
label: 'java类路径',
|
||||
rules: [{ required: true, message: '请输入 java类路径', trigger: 'blur' }],
|
||||
span: 24
|
||||
},
|
||||
onlineScript: {
|
||||
label: '在线脚本',
|
||||
span: 24,
|
||||
display: false,
|
||||
rules: [{ required: true, message: '请输入 在线脚本', trigger: 'change' }],
|
||||
params: {
|
||||
language: 'java',
|
||||
otherParams: {
|
||||
type: 'simple',
|
||||
valueStyle: 'simple',
|
||||
tipKeyList: ['javaEnhance']
|
||||
},
|
||||
headerBtn: [
|
||||
{
|
||||
params: { type: 'primary', size: 'small' },
|
||||
name: '在线脚本模板',
|
||||
icon: 'mingcute:copy-line',
|
||||
clickFun: () => {
|
||||
copyText(JavaDataExample)
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
overHidden: true
|
||||
},
|
||||
activeStatus: {
|
||||
label: '状态',
|
||||
type: 'radio',
|
||||
dicData: [
|
||||
{ label: '启用', value: 'Y' },
|
||||
{ label: '禁用', value: 'N' }
|
||||
],
|
||||
value: 'Y',
|
||||
width: 100,
|
||||
span: 24
|
||||
},
|
||||
sort: {
|
||||
label: '排序',
|
||||
type: 'number',
|
||||
value: 1,
|
||||
span: 12,
|
||||
width: 100
|
||||
},
|
||||
remark: {
|
||||
label: '备注',
|
||||
span: 24
|
||||
}
|
||||
}
|
||||
}) //表格配置
|
||||
const tableForm = ref<{ id?: number; button_exp?: string }>({})
|
||||
const tableData = ref<any>([])
|
||||
const tableSearch = ref({})
|
||||
const tablePage = ref({
|
||||
currentPage: 1,
|
||||
pageSize: 10,
|
||||
total: 0
|
||||
})
|
||||
const tableSelect = ref([])
|
||||
const typeObj = ref({ list: 0, export: 0 })
|
||||
const tableSort = ref({ column: '', order: '' })
|
||||
|
||||
const isFull = ref(true)
|
||||
|
||||
const crudRef = ref()
|
||||
const { initTableLayout, windowSize } = useCrudHeight(crudRef)
|
||||
|
||||
const selectIds = computed(() => {
|
||||
return tableSelect.value.map((item) => item['id'])
|
||||
})
|
||||
|
||||
const calcHeight = computed(() => {
|
||||
return isFull.value ? 20 : windowSize.height.value * 0.1 + 70
|
||||
})
|
||||
|
||||
watch(
|
||||
() => popupShow.value,
|
||||
(val: boolean) => {
|
||||
if (val) getTableData()
|
||||
}
|
||||
)
|
||||
|
||||
watch(
|
||||
() => calcHeight.value,
|
||||
() => {
|
||||
tableOption.calcHeight = calcHeight.value
|
||||
}
|
||||
)
|
||||
|
||||
watch(
|
||||
() => isFull.value,
|
||||
() => {
|
||||
initTableLayout()
|
||||
}
|
||||
)
|
||||
|
||||
/** 查询列表 */
|
||||
const getTableData = async () => {
|
||||
loading.value = true
|
||||
let searchObj = {
|
||||
...tableSearch.value,
|
||||
pageNo: tablePage.value.currentPage,
|
||||
pageSize: tablePage.value.pageSize,
|
||||
dbformId: props.dbformId
|
||||
}
|
||||
if (tableSort.value.column) searchObj = { ...searchObj, ...tableSort.value }
|
||||
for (let key in searchObj) if (searchObj[key] === '') delete searchObj[key]
|
||||
try {
|
||||
const data = await JavaApi.getJavaList(searchObj)
|
||||
tableData.value = data.records
|
||||
tablePage.value.total = data.total
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const searchChange = (params, done) => {
|
||||
tablePage.value.currentPage = 1
|
||||
getTableData().finally(() => {
|
||||
done()
|
||||
})
|
||||
}
|
||||
|
||||
/** 清空按钮操作 */
|
||||
const resetChange = () => {
|
||||
searchChange({}, () => {})
|
||||
}
|
||||
|
||||
const sizeChange = (pageSize) => {
|
||||
tablePage.value.pageSize = pageSize
|
||||
resetChange()
|
||||
}
|
||||
|
||||
const currentChange = (currentPage) => {
|
||||
tablePage.value.currentPage = currentPage
|
||||
getTableData()
|
||||
}
|
||||
|
||||
const selectionChange = (data) => {
|
||||
tableSelect.value = data
|
||||
}
|
||||
|
||||
/** 表单打开前 */
|
||||
const beforeOpen = async (done, type) => {
|
||||
if (['edit', 'view'].includes(type) && tableForm.value.id) {
|
||||
tableForm.value = await JavaApi.getJavaDetail(tableForm.value.id)
|
||||
}
|
||||
|
||||
typeObj.value = { list: 0, export: 0 }
|
||||
tableData.value.forEach((item) => {
|
||||
if (tableForm.value.id != item.id && typeObj.value[item.buttonCode] !== undefined) {
|
||||
typeObj.value[item.buttonCode]++
|
||||
}
|
||||
})
|
||||
done()
|
||||
}
|
||||
|
||||
/** 新增操作 */
|
||||
const rowSave = async (form, done, loading) => {
|
||||
let bool = await JavaApi.saveJavaData({ ...form, dbformId: props.dbformId }).catch(() => false)
|
||||
if (bool) {
|
||||
message.success(t('common.createSuccess'))
|
||||
resetChange()
|
||||
done()
|
||||
} else loading()
|
||||
}
|
||||
|
||||
/** 编辑操作 */
|
||||
const rowUpdate = async (form, index, done, loading) => {
|
||||
let bool = await JavaApi.updateJavaData({ ...form, dbformId: props.dbformId }).catch(() => false)
|
||||
if (bool) {
|
||||
message.success(t('common.updateSuccess'))
|
||||
getTableData()
|
||||
done()
|
||||
} else loading()
|
||||
}
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const rowDel = async (data) => {
|
||||
try {
|
||||
// 删除的二次确认
|
||||
await message.delConfirm()
|
||||
// 发起删除
|
||||
await JavaApi.deleteJavaData(data instanceof Array ? data : [data.id])
|
||||
message.success(t('common.delSuccess'))
|
||||
// 刷新列表
|
||||
await getTableData()
|
||||
} catch {}
|
||||
}
|
||||
|
||||
const sortChange = ({ order, prop }) => {
|
||||
const sortKey = { ascending: 'asc', descending: 'desc' }
|
||||
tableSort.value = { order: sortKey[order], column: prop }
|
||||
if (tablePage.value) tablePage.value['currentPage'] = 1
|
||||
getTableData()
|
||||
}
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(async () => {
|
||||
await getTableData()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.button-info-dialog {
|
||||
.el-dialog__body {
|
||||
padding: 20px;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
317
src/views/lowdesign/tableDesign/components/JsEnhanceHistory.vue
Normal file
317
src/views/lowdesign/tableDesign/components/JsEnhanceHistory.vue
Normal file
@@ -0,0 +1,317 @@
|
||||
<template>
|
||||
<DesignPopup
|
||||
v-model="popupShow"
|
||||
:title="`JS增强历史版本【${tableDescribe}】(${tableName})`"
|
||||
:width="800"
|
||||
v-model:isFull="isFull"
|
||||
>
|
||||
<div class="p-20px">
|
||||
<avue-crud
|
||||
ref="crudRef"
|
||||
v-model="tableForm"
|
||||
v-model:page="tablePage"
|
||||
v-model:search="tableSearch"
|
||||
:data="tableData"
|
||||
:option="tableOption"
|
||||
:table-loading="loading"
|
||||
@search-change="searchChange"
|
||||
@search-reset="resetChange"
|
||||
@refresh-change="getTableData"
|
||||
@size-change="sizeChange"
|
||||
@current-change="currentChange"
|
||||
@selection-change="selectionChange"
|
||||
>
|
||||
<template #menu="{ size, row }">
|
||||
<el-button :size="size" text type="primary" @click="handleJs('view', row)">
|
||||
查看
|
||||
</el-button>
|
||||
<el-button :size="size" text type="primary" @click="handleJs('diff', row)">
|
||||
比较
|
||||
</el-button>
|
||||
</template>
|
||||
</avue-crud>
|
||||
</div>
|
||||
<DesignPopup
|
||||
v-model="MEDialog.value"
|
||||
v-bind="MEDialog.params"
|
||||
class="js-enhance-history-dialog"
|
||||
:isBodyFull="true"
|
||||
>
|
||||
<template #title>{{ MEDialog.title }}</template>
|
||||
<template #default>
|
||||
<div class="relative h-100%">
|
||||
<MonacoEditor
|
||||
v-if="currType == 'view'"
|
||||
v-model="MEData.value"
|
||||
v-bind="MEData.params"
|
||||
></MonacoEditor>
|
||||
<DeffEditor
|
||||
ref="deffRef"
|
||||
v-else
|
||||
v-model="MEData.value"
|
||||
v-bind="MEData.params"
|
||||
></DeffEditor>
|
||||
</div>
|
||||
</template>
|
||||
</DesignPopup>
|
||||
</DesignPopup>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import * as GeneralApi from '@/api/design/general'
|
||||
import * as JsApi from '@/api/design/table/jsEnhance'
|
||||
import useMEDialog from '@/hooks/design/useMEDialog'
|
||||
import { formatDate } from '@/utils/formatTime'
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
defineOptions({ name: 'JavaEnhance' })
|
||||
interface Props {
|
||||
dbformId?: string
|
||||
tableName?: string
|
||||
tableDescribe?: string
|
||||
}
|
||||
const popupShow = defineModel({ default: false, type: Boolean })
|
||||
const props = defineProps<Props>()
|
||||
|
||||
const { MEDialog, MEData, openMEDialog } = useMEDialog()
|
||||
|
||||
const loading = ref(true) // 列表的加载中
|
||||
const tableOption = reactive({
|
||||
align: 'center',
|
||||
headerAlign: 'center',
|
||||
searchMenuSpan: 8,
|
||||
searchMenuPosition: 'left',
|
||||
labelSuffix: ' ',
|
||||
span: 12,
|
||||
border: true,
|
||||
calcHeight: 130,
|
||||
labelWidth: 100,
|
||||
dialogWidth: 900,
|
||||
selection: false,
|
||||
header: false,
|
||||
editBtn: false,
|
||||
delBtn: false,
|
||||
column: {
|
||||
createUserName: { label: '修改人', search: true, searchSpan: 8 },
|
||||
createTime: {
|
||||
label: '修改时间',
|
||||
type: 'datetime',
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
valueFormat: 'x',
|
||||
search: true,
|
||||
searchType: 'date',
|
||||
searchSpan: 8,
|
||||
searchFormat: 'YYYY-MM-DD'
|
||||
}
|
||||
}
|
||||
}) //表格配置
|
||||
const tableForm = ref<{ id?: number; button_exp?: string }>({})
|
||||
const tableData = ref([])
|
||||
const tableSearch = ref({})
|
||||
const tablePage = ref({
|
||||
currentPage: 1,
|
||||
pageSize: 10,
|
||||
total: 0
|
||||
})
|
||||
const tableSelect = ref([])
|
||||
const newJsData = ref({})
|
||||
const currType = ref('')
|
||||
|
||||
const isFull = ref(true)
|
||||
|
||||
const crudRef = ref()
|
||||
const deffRef = ref()
|
||||
const { initTableLayout, windowSize } = useCrudHeight(crudRef)
|
||||
|
||||
const calcHeight = computed(() => {
|
||||
return isFull.value ? 20 : windowSize.height.value * 0.1 + 70
|
||||
})
|
||||
|
||||
watch(
|
||||
() => popupShow.value,
|
||||
async (val: boolean) => {
|
||||
if (val) getTableData('getDetail')
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
watch(
|
||||
() => calcHeight.value,
|
||||
() => {
|
||||
tableOption.calcHeight = calcHeight.value
|
||||
}
|
||||
)
|
||||
|
||||
watch(
|
||||
() => isFull.value,
|
||||
() => {
|
||||
initTableLayout()
|
||||
}
|
||||
)
|
||||
|
||||
const handleJs = async (type, row) => {
|
||||
currType.value = type
|
||||
const data = await GeneralApi.getHistoryDetail({ historyId: row.id, type: 'js' })
|
||||
let params = {}
|
||||
if (type == 'view') {
|
||||
params = {
|
||||
title: `js增强历史版本 【${row.createUserName}】(${formatDate(new Date(row.createTime))})`,
|
||||
fullscreen: true,
|
||||
providerType: 'tableJsEnhance',
|
||||
editorOption: { readOnly: true }
|
||||
}
|
||||
openMEDialog({ params, prop: 'jsJson' }, data)
|
||||
} else {
|
||||
params = {
|
||||
title: `js增强历史版本 【${row.createUserName}】(${formatDate(new Date(row.createTime))})`,
|
||||
fullscreen: true,
|
||||
providerType: 'tableJsEnhance',
|
||||
oldValue: data.jsJson || '',
|
||||
footerBtn: [
|
||||
{
|
||||
params: { type: 'primary' },
|
||||
name: '保 存',
|
||||
icon: 'mingcute:save-line',
|
||||
loading: true,
|
||||
clickFun: async (loading) => {
|
||||
const bool = await message
|
||||
.confirm('是否将【右侧】内容保存为最新的JS增强内容?')
|
||||
.catch(() => false)
|
||||
if (!bool) return loading()
|
||||
const data = await getNewJsDetail(true)
|
||||
let isLock = true
|
||||
if (data && data.timeStr) {
|
||||
isLock = false
|
||||
const confirm = await message
|
||||
.confirm(
|
||||
`<div>
|
||||
<div>当前增强已锁定</div>
|
||||
<div style="font-size:16px"><span style="font-size:14px">锁定操作人:</span>${data.userName}</div>
|
||||
<div style="font-size:16px"><span style="font-size:14px">开始锁定时间:</span>${data.timeStr}</div>
|
||||
<div style="color:#E6A23C">注:请确认没有其他人在编辑后再覆盖,避免增强被多次修改</div>
|
||||
</div>`,
|
||||
'是否强制覆盖?',
|
||||
{ dangerouslyUseHTMLString: true, confirmButtonText: '确认覆盖保存' }
|
||||
)
|
||||
.catch(() => false)
|
||||
if (confirm !== 'confirm') return loading()
|
||||
}
|
||||
const upadteData = await JsApi.updateJsData({
|
||||
id: data.id,
|
||||
dbformId: props.dbformId,
|
||||
jsType: 'js',
|
||||
jsJson: deffRef.value.getValue()
|
||||
}).catch(() => false)
|
||||
|
||||
if (upadteData !== false) {
|
||||
if (isLock) await JsApi.unlockJs(props.dbformId, 'js')
|
||||
message.success('保存成功')
|
||||
getTableData()
|
||||
}
|
||||
loading()
|
||||
}
|
||||
},
|
||||
{
|
||||
params: {},
|
||||
name: '关 闭',
|
||||
icon: 'material-symbols:close',
|
||||
clickFun: async () => {
|
||||
await message.confirm('是否确认关闭?')
|
||||
MEDialog.value.value = false
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
openMEDialog({ params, prop: 'jsJson' }, newJsData.value)
|
||||
}
|
||||
}
|
||||
|
||||
const getNewJsDetail = (lock = false) => {
|
||||
return new Promise(async (resolve) => {
|
||||
const data = await JsApi.getJsDetail({
|
||||
dbformId: props.dbformId,
|
||||
type: 'js',
|
||||
lock
|
||||
})
|
||||
resolve(data)
|
||||
}) as Promise<any>
|
||||
}
|
||||
|
||||
/** 查询列表 */
|
||||
const getTableData = async (type?) => {
|
||||
if (type == 'getDetail') newJsData.value = await getNewJsDetail()
|
||||
if (!newJsData.value) {
|
||||
tableData.value = []
|
||||
tablePage.value.total = 0
|
||||
loading.value = false
|
||||
return
|
||||
}
|
||||
loading.value = true
|
||||
let searchObj = {
|
||||
...tableSearch.value,
|
||||
pageNo: tablePage.value.currentPage,
|
||||
pageSize: tablePage.value.pageSize,
|
||||
type: 'js',
|
||||
id: newJsData.value['id']
|
||||
}
|
||||
for (let key in searchObj) if (searchObj[key] === '') delete searchObj[key]
|
||||
try {
|
||||
const data = await GeneralApi.getHistoryList(searchObj)
|
||||
tableData.value = data.records
|
||||
tablePage.value.total = data.total
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const searchChange = (params, done) => {
|
||||
tablePage.value.currentPage = 1
|
||||
getTableData().finally(() => {
|
||||
done()
|
||||
})
|
||||
}
|
||||
|
||||
/** 清空按钮操作 */
|
||||
const resetChange = () => {
|
||||
searchChange({}, () => {})
|
||||
}
|
||||
|
||||
const sizeChange = (pageSize) => {
|
||||
tablePage.value.pageSize = pageSize
|
||||
resetChange()
|
||||
}
|
||||
|
||||
const currentChange = (currentPage) => {
|
||||
tablePage.value.currentPage = currentPage
|
||||
getTableData()
|
||||
}
|
||||
|
||||
const selectionChange = (data) => {
|
||||
tableSelect.value = data
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.js-enhance-history-dialog {
|
||||
.monaco-scrollable-element::before {
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
right: 20px;
|
||||
z-index: 999;
|
||||
padding: 3px 10px;
|
||||
color: #f56c6c;
|
||||
border: 1px solid #f56c6c;
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
.editor.original .monaco-scrollable-element::before {
|
||||
content: '旧版增强';
|
||||
}
|
||||
|
||||
.editor.modified .monaco-scrollable-element::before {
|
||||
content: '最新增强(可修改)';
|
||||
}
|
||||
}
|
||||
</style>
|
||||
502
src/views/lowdesign/tableDesign/components/SearchOption.vue
Normal file
502
src/views/lowdesign/tableDesign/components/SearchOption.vue
Normal file
@@ -0,0 +1,502 @@
|
||||
<template>
|
||||
<div class="search-option table-design-option">
|
||||
<el-container class="search-content">
|
||||
<el-aside width="240px" class="left-tree">
|
||||
<div class="search-title">
|
||||
<span>字段名称</span>
|
||||
</div>
|
||||
<div class="search-draggable">
|
||||
<draggable
|
||||
class="search-content"
|
||||
tag="div"
|
||||
:list="filedList"
|
||||
:group="{ name: 'config', pull: 'clone', put: false }"
|
||||
ghost-class="search-ghost"
|
||||
:search="false"
|
||||
item-key="value"
|
||||
>
|
||||
<template #item="{ element }">
|
||||
<div class="search-item" @click="setOption(element)">
|
||||
<span>{{ element.label }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</draggable>
|
||||
</div>
|
||||
</el-aside>
|
||||
<el-main class="main-option">
|
||||
<div class="option-title table-item-row">
|
||||
<div class="row-item">
|
||||
<div class="cell">序号</div>
|
||||
</div>
|
||||
<div class="row-item text-center">
|
||||
<div class="cell">字段名称</div>
|
||||
</div>
|
||||
<div class="row-item">
|
||||
<div class="cell">类型</div>
|
||||
</div>
|
||||
<div class="row-item">
|
||||
<div class="cell">where条件</div>
|
||||
</div>
|
||||
<div class="row-item">
|
||||
<div class="cell">操作</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="option-content">
|
||||
<draggable
|
||||
class="content-draggable"
|
||||
:list="searchList"
|
||||
:group="{ name: 'option', put: true }"
|
||||
ghost-class="option-ghost"
|
||||
:animation="300"
|
||||
handle=".move-box"
|
||||
item-key="prop"
|
||||
@add="handleAddColumn"
|
||||
>
|
||||
<template #item="{ element, index }">
|
||||
<div class="option-item table-item-row mt--2px" v-if="element.textList">
|
||||
<div class="row-item move-box">
|
||||
<div class="cell">{{ index + 1 }}</div>
|
||||
</div>
|
||||
<div class="row-item">
|
||||
<div class="cell">
|
||||
<avue-input
|
||||
v-if="element.controlType != 'custom'"
|
||||
class="flex-1"
|
||||
v-model="element.textList[0].value"
|
||||
placeholder="字段名称"
|
||||
></avue-input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row-item">
|
||||
<div class="cell">
|
||||
<avue-select
|
||||
v-model="element.condition"
|
||||
:dic="conditionDic"
|
||||
@change="(data) => typeChange(data, index)"
|
||||
></avue-select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row-item">
|
||||
<div class="cell flex items-center gap-x-6px">
|
||||
<template v-if="['default', 'text'].includes(element.controlType)">
|
||||
<avue-input
|
||||
class="flex-1"
|
||||
v-if="element.controlType != 'text'"
|
||||
v-model="element.textList[2].value"
|
||||
placeholder="值"
|
||||
></avue-input>
|
||||
</template>
|
||||
<template v-else-if="element.controlType == 'between'">
|
||||
<avue-input
|
||||
class="flex-1"
|
||||
v-if="element.controlType != 'text'"
|
||||
v-model="element.textList[2].value"
|
||||
placeholder="值"
|
||||
></avue-input>
|
||||
<span class="flex-shrink-0">{{ element.textList[3].value }}</span>
|
||||
<avue-input
|
||||
class="flex-1"
|
||||
v-if="element.controlType != 'text'"
|
||||
v-model="element.textList[4].value"
|
||||
placeholder="值"
|
||||
></avue-input>
|
||||
</template>
|
||||
<template v-else-if="element.controlType == 'in'">
|
||||
<span class="flex-shrink-0">{{ element.textList[2].value }}</span>
|
||||
<avue-select
|
||||
v-model="element.textList[3].value"
|
||||
multiple
|
||||
filterable
|
||||
allowCreate
|
||||
:dic="[{ label: '请直接输入值', value: 'tip', disabled: true }]"
|
||||
></avue-select>
|
||||
<span class="flex-shrink-0">{{ element.textList[4].value }}</span>
|
||||
</template>
|
||||
<template v-else-if="element.controlType == 'custom'">
|
||||
<el-popover
|
||||
placement="bottom-start"
|
||||
:popperStyle="{ width: 'auto', height: 'auto' }"
|
||||
trigger="click"
|
||||
>
|
||||
<template #reference>
|
||||
<avue-input
|
||||
class="flex-1"
|
||||
v-model="element.textList[0].value"
|
||||
placeholder="自定义where"
|
||||
readonly
|
||||
></avue-input>
|
||||
</template>
|
||||
<div class="w-40vw h-200px">
|
||||
<MonacoEditor
|
||||
v-model="element.textList[0].value"
|
||||
language="mysql"
|
||||
:editorOption="{ minimap: false }"
|
||||
/>
|
||||
</div>
|
||||
</el-popover>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row-item">
|
||||
<div class="cell">
|
||||
<el-button type="danger" link text @click="delRow(element)"> 删除 </el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</draggable>
|
||||
</div>
|
||||
</el-main>
|
||||
</el-container>
|
||||
<div class="flex items-start pt-4px pb-4px pl-10px pr-10px">
|
||||
<div class="flex-shrink-0">输出的where条件:</div>
|
||||
<div class="flex-1 flex flex-wrap gap-x-6px">
|
||||
<template v-for="(item, index) in whereList" :key="index">
|
||||
<span>AND</span>
|
||||
<span class="c-#409EFF">{{ item }}</span>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import draggable from 'vuedraggable'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
|
||||
defineOptions({ name: 'SearchOption' })
|
||||
|
||||
interface Props {
|
||||
show: boolean
|
||||
filedList: Array<any>
|
||||
}
|
||||
const props = defineProps<Props>()
|
||||
const searchStr = defineModel<string>({ default: '' })
|
||||
|
||||
const searchList = ref<any>([])
|
||||
const conditionDic = [
|
||||
{ label: '>(大于)', value: 'GT' },
|
||||
{ label: '>=(大于等于)', value: 'GE' },
|
||||
{ label: '<(小于)', value: 'LT' },
|
||||
{ label: '<=(小于等于)', value: 'LE' },
|
||||
{ label: '=(等于)', value: 'EQ' },
|
||||
{ label: '!=(不等于)', value: 'NE' },
|
||||
{ label: 'BETWEEN(范围内)', value: 'BETWEEN' },
|
||||
{ label: 'NOT BETWEEN(不在范围内)', value: 'NOT_BETWEEN' },
|
||||
{ label: 'IN( 指定数组内)', value: 'IN' },
|
||||
{ label: 'NOT IN(不在指定数组内)', value: 'NOT_IN' },
|
||||
{ label: 'LIKE(模糊)', value: 'LIKE' },
|
||||
{ label: 'NOT LIKE(模糊取反)', value: 'NOT_LIKE' },
|
||||
{ label: 'IS NULL(为空)', value: 'IS_NULL', type: 'text' },
|
||||
{ label: 'IS NOT NULL(不为空)', value: 'IS_NOT_NULL', type: 'text' },
|
||||
{ label: '自定义', value: 'custom', type: 'custom' }
|
||||
]
|
||||
|
||||
const whereList = computed(() => {
|
||||
if (searchList.value.length) {
|
||||
const textList = getOptionStr('whereMap')
|
||||
return textList
|
||||
}
|
||||
return ''
|
||||
})
|
||||
|
||||
const initFun = () => {
|
||||
searchList.value = []
|
||||
if (searchStr.value) searchList.value = JSON.parse(searchStr.value).searchList
|
||||
}
|
||||
const typeChange = ({ value, item }, index) => {
|
||||
const row = searchList.value[index]
|
||||
const text = item.label.split('(')[0]
|
||||
const textList = [
|
||||
{ type: 'text', value: searchList.value[index].textList[0].value },
|
||||
{ type: 'text', value: text }
|
||||
]
|
||||
let type = ''
|
||||
if (['GT', 'GE', 'LT', 'LE', 'EQ', 'NE', 'LIKE', 'NOT_LIKE'].includes(value)) {
|
||||
textList.push({ type: 'input', value: '' })
|
||||
type = 'default'
|
||||
} else if (['IS_NULL', 'IS_NOT_NULL'].includes(value)) {
|
||||
type = 'text'
|
||||
} else if (['BETWEEN', 'NOT_BETWEEN'].includes(value)) {
|
||||
textList.push(
|
||||
{ type: 'input', value: '' },
|
||||
{ type: 'text', value: 'AND' },
|
||||
{ type: 'input', value: '' }
|
||||
)
|
||||
type = 'between'
|
||||
} else if (['IN', 'NOT_IN'].includes(value)) {
|
||||
textList.push(
|
||||
{ type: 'text', value: '(' },
|
||||
{ type: 'input', value: [] },
|
||||
{ type: 'text', value: ')' }
|
||||
)
|
||||
type = 'in'
|
||||
} else if (value == 'custom') {
|
||||
row.textList = [{ type: 'text', value: '' }]
|
||||
row.controlType = 'custom'
|
||||
return
|
||||
}
|
||||
row.textList = textList
|
||||
row.controlType = type
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.show,
|
||||
(val) => {
|
||||
if (val) initFun()
|
||||
}
|
||||
)
|
||||
|
||||
const handleAddColumn = (e) => {
|
||||
const newIndex = e.newIndex
|
||||
const data = cloneDeep(searchList.value[newIndex])
|
||||
if (!data.prop) data.prop = `option_${Math.ceil(Math.random() * 9999999)}`
|
||||
data.textList = [{ type: 'text', value: data.value }]
|
||||
delete data.label
|
||||
delete data.value
|
||||
searchList.value[newIndex] = data
|
||||
}
|
||||
const setOption = (row) => {
|
||||
searchList.value.push(row)
|
||||
handleAddColumn({ newIndex: searchList.value.length - 1 })
|
||||
}
|
||||
|
||||
const delRow = (row) => {
|
||||
searchList.value = searchList.value.filter((item) => item.prop != row.prop)
|
||||
}
|
||||
|
||||
const getOptionStr = (getType = 'str') => {
|
||||
if (searchList.value.length) {
|
||||
if (getType == 'str') {
|
||||
return JSON.stringify({
|
||||
searchList: searchList.value,
|
||||
whereList: getWhereList()
|
||||
})
|
||||
} else if (getType == 'whereMap') return getWhereList()
|
||||
} else ''
|
||||
}
|
||||
|
||||
const getWhereList = () => {
|
||||
const list: string[] = []
|
||||
searchList.value.forEach((item) => {
|
||||
let text = ''
|
||||
if (item.controlType == 'custom') text = item.textList[0].value
|
||||
else if (item.textList && item.textList.length) {
|
||||
item.textList.forEach((child) => {
|
||||
text = `${text}${text ? ' ' : ''}${
|
||||
child.type == 'text' ? child.value : handleValue(item.type, child.value)
|
||||
}`
|
||||
})
|
||||
}
|
||||
list.push(text)
|
||||
})
|
||||
return list
|
||||
}
|
||||
const handleValue = (filedType, value) => {
|
||||
let whereType = 'string'
|
||||
const valueType = value instanceof Array ? 'array' : 'string'
|
||||
|
||||
const setValueFormat = (val) => {
|
||||
let text = val
|
||||
if (whereType == 'string' && val.indexOf(`'`) == -1) {
|
||||
text = `'${val}'`
|
||||
}
|
||||
return text
|
||||
}
|
||||
if (['BigInt', 'BigDecimal', 'Integer'].includes(filedType)) whereType = 'number'
|
||||
if (valueType == 'string') value = [value]
|
||||
value = value.map((item) => setValueFormat(item)).join(',')
|
||||
return value
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
initFun()
|
||||
})
|
||||
|
||||
defineExpose({ getOptionStr })
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.search-option {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin-top: -1px;
|
||||
|
||||
.search-content {
|
||||
height: calc(100% - 60px);
|
||||
}
|
||||
|
||||
.left-tree {
|
||||
padding: 10px;
|
||||
border: 1px solid #f1f1f1;
|
||||
|
||||
.search-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
cursor: pointer;
|
||||
|
||||
.el-icon {
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
|
||||
&:nth-last-of-type(1) {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.search-draggable {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.search-item {
|
||||
width: 180px;
|
||||
padding: 4px 8px;
|
||||
margin-bottom: 10px;
|
||||
font-size: 14px;
|
||||
cursor: move;
|
||||
background-color: #f4f6fc;
|
||||
border: 1px dashed #f4f6fc;
|
||||
|
||||
&:hover {
|
||||
color: var(--el-color-primary);
|
||||
border-color: var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.main-option {
|
||||
padding: 0;
|
||||
border: 1px solid #f1f1f1;
|
||||
border-left: 0;
|
||||
|
||||
.option-content {
|
||||
height: calc(100% - 41px);
|
||||
|
||||
.content-draggable {
|
||||
height: 100%;
|
||||
padding-bottom: 55px;
|
||||
overflow-y: auto;
|
||||
box-sizing: border-box;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
width: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.option-ghost {
|
||||
position: relative;
|
||||
width: 0 !important;
|
||||
height: 40px;
|
||||
min-width: 0 !important;
|
||||
padding: 0 !important;
|
||||
margin: 1px 2px 0;
|
||||
overflow: hidden;
|
||||
font-size: 0;
|
||||
background: white;
|
||||
border-left: 5px solid var(--el-color-primary);
|
||||
content: '';
|
||||
outline: none 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
|
||||
.table-item-row {
|
||||
display: flex;
|
||||
|
||||
.row-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: auto;
|
||||
min-height: 32px;
|
||||
padding: 4px 0;
|
||||
font-size: 14px;
|
||||
line-height: 32px;
|
||||
border-bottom: 1px solid #dcdfe6;
|
||||
|
||||
.cell {
|
||||
flex: 1;
|
||||
padding: 0 12px;
|
||||
|
||||
& > div {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.reg-exp-input {
|
||||
::v-deep(.el-input) {
|
||||
.el-input-group__prepend,
|
||||
.el-input-group__append {
|
||||
padding: 0 8px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:nth-child(1) {
|
||||
flex-basis: 60px;
|
||||
text-align: center;
|
||||
border-right: 1px solid #dcdfe6;
|
||||
}
|
||||
|
||||
&:nth-child(2) {
|
||||
flex-basis: 230px;
|
||||
border-right: 1px solid #dcdfe6;
|
||||
}
|
||||
|
||||
&:nth-child(3) {
|
||||
flex-basis: 180px;
|
||||
line-height: normal;
|
||||
text-align: center;
|
||||
border-right: 1px solid #dcdfe6;
|
||||
}
|
||||
|
||||
&:nth-child(4) {
|
||||
flex: 1;
|
||||
border-right: 1px solid #dcdfe6;
|
||||
}
|
||||
|
||||
&:nth-child(5) {
|
||||
flex-basis: 110px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&.move-box {
|
||||
cursor: move;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.option-title {
|
||||
background-color: #fafafa;
|
||||
|
||||
.row-item:nth-child(4) {
|
||||
line-height: 32px;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.right-custom {
|
||||
.custom-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 32px;
|
||||
padding: 4px 0;
|
||||
font-size: 14px;
|
||||
line-height: 32px;
|
||||
text-align: center;
|
||||
background-color: #fafafa;
|
||||
border: 1px solid #dcdfe6;
|
||||
}
|
||||
|
||||
.custom-content {
|
||||
width: 100%;
|
||||
height: calc(100% - 42px);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
339
src/views/lowdesign/tableDesign/components/SortOption.vue
Normal file
339
src/views/lowdesign/tableDesign/components/SortOption.vue
Normal file
@@ -0,0 +1,339 @@
|
||||
<template>
|
||||
<div class="sort-option table-design-option">
|
||||
<el-container class="h-100%">
|
||||
<el-aside width="240px" class="left-tree">
|
||||
<div class="sort-title">
|
||||
<span>字段名称</span>
|
||||
</div>
|
||||
<div class="sort-draggable">
|
||||
<draggable
|
||||
class="sort-content"
|
||||
tag="div"
|
||||
:list="filedList"
|
||||
:group="{ name: 'config', pull: 'clone', put: false }"
|
||||
ghost-class="sort-ghost"
|
||||
:sort="false"
|
||||
item-key="value"
|
||||
>
|
||||
<template #item="{ element }">
|
||||
<div class="sort-item" @click="setOption(element)">
|
||||
<span>{{ element.label }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</draggable>
|
||||
</div>
|
||||
</el-aside>
|
||||
<el-main class="main-option">
|
||||
<div class="option-title table-item-row">
|
||||
<div class="row-item">
|
||||
<div class="cell">序号</div>
|
||||
</div>
|
||||
<div class="row-item text-center">
|
||||
<div class="cell">字段名称</div>
|
||||
</div>
|
||||
<div class="row-item">
|
||||
<div class="cell">排序方式</div>
|
||||
</div>
|
||||
<div class="row-item">
|
||||
<div class="cell">操作</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="option-content">
|
||||
<draggable
|
||||
class="content-draggable"
|
||||
:list="sortList"
|
||||
:group="{ name: 'option', put: true }"
|
||||
ghost-class="option-ghost"
|
||||
:animation="300"
|
||||
handle=".move-box"
|
||||
item-key="column"
|
||||
@add="handleAddColumn"
|
||||
>
|
||||
<template #item="{ element, index }">
|
||||
<div class="option-item table-item-row mt--2px">
|
||||
<div class="row-item move-box">
|
||||
<div class="cell">{{ index + 1 }}</div>
|
||||
</div>
|
||||
<div class="row-item move-box">
|
||||
<div class="cell">
|
||||
<avue-text-ellipsis
|
||||
:key="element.value"
|
||||
:text="filedObj[element.column]"
|
||||
:height="40"
|
||||
:width="220"
|
||||
use-tooltip
|
||||
placement="top"
|
||||
>
|
||||
<template #more>
|
||||
<small>...</small>
|
||||
</template>
|
||||
</avue-text-ellipsis>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row-item">
|
||||
<div class="cell">
|
||||
<avue-select v-model="element.order" :dic="dicObj.orderByType"></avue-select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row-item">
|
||||
<div class="cell">
|
||||
<el-button type="danger" link text @click="delRow(element)"> 删除 </el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</draggable>
|
||||
</div>
|
||||
</el-main>
|
||||
</el-container>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import draggable from 'vuedraggable'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
import { dicObj } from '@/views/lowdesign/tableDesign/designData'
|
||||
|
||||
defineOptions({ name: 'SortOption' })
|
||||
|
||||
interface Props {
|
||||
show: boolean
|
||||
filedList: Array<any>
|
||||
}
|
||||
const props = defineProps<Props>()
|
||||
const sortStr = defineModel<string>({ default: '' })
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
const sortList = ref<any>([])
|
||||
|
||||
const sortPropList = computed(() => {
|
||||
return sortList.value.filter((item) => item.column).map((item) => item.column)
|
||||
})
|
||||
const filedObj = computed(() => {
|
||||
const obj = {}
|
||||
props.filedList.forEach((item) => (obj[item.value] = item.label))
|
||||
return obj
|
||||
})
|
||||
|
||||
const initFun = () => {
|
||||
sortList.value = []
|
||||
if (sortStr.value) sortList.value = JSON.parse(sortStr.value)
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.show,
|
||||
(val) => {
|
||||
if (val) initFun()
|
||||
}
|
||||
)
|
||||
|
||||
const handleAddColumn = (e) => {
|
||||
const newIndex = e.newIndex
|
||||
const data = cloneDeep(sortList.value[newIndex])
|
||||
if (sortPropList.value.includes(data.value)) {
|
||||
sortList.value = sortList.value.filter((item) => item.column)
|
||||
message.info('该字段已存在')
|
||||
return
|
||||
}
|
||||
data.order = 'asc'
|
||||
data.column = data.value
|
||||
delete data.value
|
||||
delete data.label
|
||||
sortList.value[newIndex] = data
|
||||
}
|
||||
const setOption = (row) => {
|
||||
sortList.value.push(row)
|
||||
handleAddColumn({ newIndex: sortList.value.length - 1 })
|
||||
setTimeout(() => {
|
||||
getOptionStr()
|
||||
}, 300)
|
||||
}
|
||||
|
||||
const delRow = (row) => {
|
||||
sortList.value = sortList.value.filter((item) => item.column != row.column)
|
||||
}
|
||||
|
||||
const getOptionStr = () => {
|
||||
if (sortList.value.length) return JSON.stringify(sortList.value)
|
||||
else ''
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
initFun()
|
||||
})
|
||||
|
||||
defineExpose({ getOptionStr })
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.sort-option {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin-top: -1px;
|
||||
|
||||
.left-tree {
|
||||
padding: 10px;
|
||||
border: 1px solid #f1f1f1;
|
||||
|
||||
.sort-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
cursor: pointer;
|
||||
|
||||
.el-icon {
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
|
||||
&:nth-last-of-type(1) {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.sort-draggable {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.sort-item {
|
||||
width: 180px;
|
||||
padding: 4px 8px;
|
||||
margin-bottom: 10px;
|
||||
font-size: 14px;
|
||||
cursor: move;
|
||||
background-color: #f4f6fc;
|
||||
border: 1px dashed #f4f6fc;
|
||||
|
||||
&:hover {
|
||||
color: var(--el-color-primary);
|
||||
border-color: var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.main-option {
|
||||
padding: 0;
|
||||
border: 1px solid #f1f1f1;
|
||||
border-left: 0;
|
||||
|
||||
.option-content {
|
||||
height: calc(100% - 41px);
|
||||
|
||||
.content-draggable {
|
||||
height: 100%;
|
||||
padding-bottom: 55px;
|
||||
overflow-y: auto;
|
||||
box-sizing: border-box;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
width: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.option-ghost {
|
||||
position: relative;
|
||||
width: 0 !important;
|
||||
height: 40px;
|
||||
min-width: 0 !important;
|
||||
padding: 0 !important;
|
||||
margin: 1px 2px 0;
|
||||
overflow: hidden;
|
||||
font-size: 0;
|
||||
background: white;
|
||||
border-left: 5px solid var(--el-color-primary);
|
||||
content: '';
|
||||
outline: none 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
|
||||
.table-item-row {
|
||||
display: flex;
|
||||
align-items: end;
|
||||
|
||||
.row-item {
|
||||
height: 100%;
|
||||
min-height: 32px;
|
||||
padding: 4px 0;
|
||||
font-size: 14px;
|
||||
line-height: 32px;
|
||||
border-bottom: 1px solid #dcdfe6;
|
||||
|
||||
.cell {
|
||||
padding: 0 12px;
|
||||
|
||||
& > div {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.reg-exp-input {
|
||||
::v-deep(.el-input) {
|
||||
.el-input-group__prepend,
|
||||
.el-input-group__append {
|
||||
padding: 0 8px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:nth-child(1) {
|
||||
flex-basis: 60px;
|
||||
text-align: center;
|
||||
border-right: 1px solid #dcdfe6;
|
||||
}
|
||||
|
||||
&:nth-child(2) {
|
||||
flex-basis: 250px;
|
||||
border-right: 1px solid #dcdfe6;
|
||||
}
|
||||
|
||||
&:nth-child(3) {
|
||||
flex: 1;
|
||||
line-height: normal;
|
||||
border-right: 1px solid #dcdfe6;
|
||||
}
|
||||
|
||||
&:nth-child(4) {
|
||||
flex-basis: 110px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&.move-box {
|
||||
cursor: move;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.option-title {
|
||||
background-color: #fafafa;
|
||||
|
||||
.row-item:nth-child(3) {
|
||||
line-height: 32px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.right-custom {
|
||||
.custom-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 32px;
|
||||
padding: 4px 0;
|
||||
font-size: 14px;
|
||||
line-height: 32px;
|
||||
text-align: center;
|
||||
background-color: #fafafa;
|
||||
border: 1px solid #dcdfe6;
|
||||
}
|
||||
|
||||
.custom-content {
|
||||
width: 100%;
|
||||
height: calc(100% - 42px);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
366
src/views/lowdesign/tableDesign/components/SqlEnhance.vue
Normal file
366
src/views/lowdesign/tableDesign/components/SqlEnhance.vue
Normal file
@@ -0,0 +1,366 @@
|
||||
<template>
|
||||
<DesignPopup
|
||||
v-model="popupShow"
|
||||
:title="`SQL增强 【${tableDescribe}】(${tableName})`"
|
||||
:fullscreen="true"
|
||||
v-model:isFull="isFull"
|
||||
>
|
||||
<div class="p-20px">
|
||||
<avue-crud
|
||||
ref="crudRef"
|
||||
v-model="tableForm"
|
||||
v-model:page="tablePage"
|
||||
v-model:search="tableSearch"
|
||||
:data="tableData"
|
||||
:option="tableOption"
|
||||
:table-loading="loading"
|
||||
:before-open="beforeOpen"
|
||||
@search-change="searchChange"
|
||||
@search-reset="resetChange"
|
||||
@row-save="rowSave"
|
||||
@row-update="rowUpdate"
|
||||
@row-del="rowDel"
|
||||
@refresh-change="getTableData"
|
||||
@size-change="sizeChange"
|
||||
@current-change="currentChange"
|
||||
@selection-change="selectionChange"
|
||||
@sort-change="sortChange"
|
||||
>
|
||||
<template #menu-left="{ size }">
|
||||
<el-button
|
||||
:size="size"
|
||||
type="danger"
|
||||
:disabled="!tableSelect.length"
|
||||
@click="rowDel(selectIds)"
|
||||
>
|
||||
<Icon :size="16" icon="mi:delete" class="mr-3px" />批量删除
|
||||
</el-button>
|
||||
</template>
|
||||
<template #buttonCode-form="{ size, column }">
|
||||
<div class="flex">
|
||||
<avue-select
|
||||
v-model="tableForm[column.prop]"
|
||||
:size="size"
|
||||
:placeholder="`请选择 ${column.label}`"
|
||||
:dic="column.dicData"
|
||||
></avue-select>
|
||||
<div
|
||||
class="absolute left-110% whitespace-nowrap c-#be123c"
|
||||
v-if="tableForm[column.prop]"
|
||||
>
|
||||
<span v-if="['add', 'edit', 'delete', 'import'].includes(tableForm[column.prop])">
|
||||
支持新增、删除、修改sql语句
|
||||
</span>
|
||||
<span v-else>支持查询sql语句</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #executeSql-form="{ size, column }">
|
||||
<avue-input
|
||||
type="textarea"
|
||||
:minRows="3"
|
||||
:maxRows="5"
|
||||
v-model="tableForm[column.prop]"
|
||||
:size="size"
|
||||
readonly
|
||||
:placeholder="`请输入 ${column.label}`"
|
||||
@click="openMEDialog(column, tableForm)"
|
||||
></avue-input>
|
||||
</template>
|
||||
</avue-crud>
|
||||
</div>
|
||||
<DesignPopup v-model="MEDialog.value" v-bind="MEDialog.params" :isBodyFull="true">
|
||||
<template #title>{{ MEDialog.title }}</template>
|
||||
<template #default>
|
||||
<el-container class="h-100%">
|
||||
<el-main class="p-0!">
|
||||
<MonacoEditor
|
||||
class="bg-#1e1e1e"
|
||||
style="flex: 1"
|
||||
v-model="MEData.value"
|
||||
v-bind="MEData.params"
|
||||
></MonacoEditor>
|
||||
</el-main>
|
||||
<el-aside width="430px">
|
||||
<TipView v-bind="MEDialog.otherParams"></TipView>
|
||||
</el-aside>
|
||||
</el-container>
|
||||
</template>
|
||||
</DesignPopup>
|
||||
</DesignPopup>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { TipView } from '../../general/components/index'
|
||||
import * as SqlApi from '@/api/design/table/qslEnhance'
|
||||
import useMEDialog from '@/hooks/design/useMEDialog'
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
defineOptions({ name: 'SqlEnhance' })
|
||||
interface Props {
|
||||
dbformId?: string
|
||||
tableName?: string
|
||||
tableDescribe?: string
|
||||
}
|
||||
const popupShow = defineModel({ default: false, type: Boolean })
|
||||
const props = defineProps<Props>()
|
||||
|
||||
const { MEDialog, MEData, openMEDialog } = useMEDialog()
|
||||
|
||||
const loading = ref(true) // 列表的加载中
|
||||
const tableOption = reactive({
|
||||
border: true,
|
||||
align: 'center',
|
||||
headerAlign: 'center',
|
||||
searchMenuSpan: 6,
|
||||
searchMenuPosition: 'left',
|
||||
labelSuffix: ' ',
|
||||
span: 12,
|
||||
calcHeight: 20,
|
||||
labelWidth: 120,
|
||||
dialogWidth: 900,
|
||||
selection: true,
|
||||
column: {
|
||||
buttonCode: {
|
||||
label: '页面功能',
|
||||
type: 'select',
|
||||
rules: [{ required: true, message: '请选择 页面功能', trigger: 'blur' }],
|
||||
dicData: [
|
||||
{ label: '新增', value: 'add' },
|
||||
{ label: '编辑', value: 'edit' },
|
||||
{ label: '详情', value: 'detail' },
|
||||
{ label: '删除', value: 'delete' },
|
||||
{ label: '列表', value: 'list' },
|
||||
{ label: '导入', value: 'import' },
|
||||
{ label: '导出', value: 'export' }
|
||||
],
|
||||
sortable: true,
|
||||
editDisabled: true,
|
||||
width: 140,
|
||||
span: 12,
|
||||
row: true,
|
||||
control: (val) => {
|
||||
let bool = false
|
||||
if (typeObj.value[val] !== undefined && typeObj.value[val] >= 1) bool = true
|
||||
return {
|
||||
listResultHandleType: { display: bool }
|
||||
}
|
||||
}
|
||||
},
|
||||
listResultHandleType: {
|
||||
label: '结果处理类型',
|
||||
type: 'select',
|
||||
width: 120,
|
||||
rules: [{ required: true, message: '请选择 结果处理类型', trigger: 'blur' }],
|
||||
dicData: [
|
||||
{ label: '串行', value: 0 },
|
||||
{ label: '合集', value: 1 },
|
||||
{ label: '差集', value: 2 },
|
||||
{ label: '并集', value: 3 },
|
||||
{ label: '交集', value: 4 }
|
||||
],
|
||||
value: 0,
|
||||
span: 12,
|
||||
dataType: 'number',
|
||||
display: false
|
||||
},
|
||||
executeSql: {
|
||||
label: 'SQL增强',
|
||||
span: 24,
|
||||
rules: [{ required: true, message: '请输入 SQL增强', trigger: 'change' }],
|
||||
params: {
|
||||
language: 'mysql',
|
||||
otherParams: {
|
||||
type: 'simple',
|
||||
valueStyle: 'simple',
|
||||
tipKeyList: ['SqlEnhance']
|
||||
}
|
||||
},
|
||||
minWidth: 140,
|
||||
overHidden: true
|
||||
},
|
||||
activeStatus: {
|
||||
label: '状态',
|
||||
type: 'radio',
|
||||
dicData: [
|
||||
{ label: '启用', value: 'Y' },
|
||||
{ label: '禁用', value: 'N' }
|
||||
],
|
||||
value: 'Y',
|
||||
width: 100,
|
||||
span: 24
|
||||
},
|
||||
sort: {
|
||||
label: '排序',
|
||||
type: 'number',
|
||||
value: 1,
|
||||
span: 12,
|
||||
width: 100
|
||||
},
|
||||
remark: {
|
||||
label: '备注',
|
||||
span: 24,
|
||||
minWidth: 120
|
||||
}
|
||||
}
|
||||
}) //表格配置
|
||||
const tableForm = ref<{ id?: number; button_exp?: string }>({})
|
||||
const tableData = ref<any>([])
|
||||
const tableSearch = ref({})
|
||||
const tablePage = ref({
|
||||
currentPage: 1,
|
||||
pageSize: 10,
|
||||
total: 0
|
||||
})
|
||||
const tableSelect = ref([])
|
||||
const typeObj = ref({ list: 0, export: 0 })
|
||||
const tableSort = ref({ column: '', order: '' })
|
||||
const isFull = ref(true)
|
||||
|
||||
const crudRef = ref()
|
||||
const { initTableLayout, windowSize } = useCrudHeight(crudRef)
|
||||
|
||||
const selectIds = computed(() => {
|
||||
return tableSelect.value.map((item) => item['id'])
|
||||
})
|
||||
|
||||
const calcHeight = computed(() => {
|
||||
return isFull.value ? 20 : windowSize.height.value * 0.1 + 70
|
||||
})
|
||||
|
||||
watch(
|
||||
() => popupShow.value,
|
||||
(val: boolean) => {
|
||||
if (val) getTableData()
|
||||
}
|
||||
)
|
||||
|
||||
watch(
|
||||
() => calcHeight.value,
|
||||
() => {
|
||||
tableOption.calcHeight = calcHeight.value
|
||||
}
|
||||
)
|
||||
|
||||
watch(
|
||||
() => isFull.value,
|
||||
() => {
|
||||
initTableLayout()
|
||||
}
|
||||
)
|
||||
|
||||
/** 查询列表 */
|
||||
const getTableData = async () => {
|
||||
loading.value = true
|
||||
let searchObj = {
|
||||
...tableSearch.value,
|
||||
pageNo: tablePage.value.currentPage,
|
||||
pageSize: tablePage.value.pageSize,
|
||||
dbformId: props.dbformId
|
||||
}
|
||||
if (tableSort.value.column) searchObj = { ...searchObj, ...tableSort.value }
|
||||
for (let key in searchObj) if (searchObj[key] === '') delete searchObj[key]
|
||||
try {
|
||||
const data = await SqlApi.getSqlList(searchObj)
|
||||
tableData.value = data.records
|
||||
tablePage.value.total = data.total
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const searchChange = (params, done) => {
|
||||
tablePage.value.currentPage = 1
|
||||
getTableData().finally(() => {
|
||||
done()
|
||||
})
|
||||
}
|
||||
|
||||
/** 清空按钮操作 */
|
||||
const resetChange = () => {
|
||||
searchChange({}, () => {})
|
||||
}
|
||||
|
||||
const sizeChange = (pageSize) => {
|
||||
tablePage.value.pageSize = pageSize
|
||||
resetChange()
|
||||
}
|
||||
|
||||
const currentChange = (currentPage) => {
|
||||
tablePage.value.currentPage = currentPage
|
||||
getTableData()
|
||||
}
|
||||
|
||||
const selectionChange = (data) => {
|
||||
tableSelect.value = data
|
||||
}
|
||||
|
||||
/** 表单打开前 */
|
||||
const beforeOpen = async (done, type) => {
|
||||
if (['edit', 'view'].includes(type) && tableForm.value.id) {
|
||||
tableForm.value = await SqlApi.getSqlDetail(tableForm.value.id)
|
||||
}
|
||||
typeObj.value = { list: 0, export: 0 }
|
||||
tableData.value.forEach((item) => {
|
||||
if (tableForm.value.id != item.id && typeObj.value[item.buttonCode] !== undefined) {
|
||||
typeObj.value[item.buttonCode]++
|
||||
}
|
||||
})
|
||||
done()
|
||||
}
|
||||
|
||||
/** 新增操作 */
|
||||
const rowSave = async (form, done, loading) => {
|
||||
let bool = await SqlApi.saveSqlData({ ...form, dbformId: props.dbformId }).catch(() => false)
|
||||
if (bool) {
|
||||
message.success(t('common.createSuccess'))
|
||||
resetChange()
|
||||
done()
|
||||
} else loading()
|
||||
}
|
||||
|
||||
/** 编辑操作 */
|
||||
const rowUpdate = async (form, index, done, loading) => {
|
||||
let bool = await SqlApi.updateSqlData({ ...form, dbformId: props.dbformId }).catch(() => false)
|
||||
if (bool) {
|
||||
message.success(t('common.updateSuccess'))
|
||||
getTableData()
|
||||
done()
|
||||
} else loading()
|
||||
}
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const rowDel = async (data) => {
|
||||
try {
|
||||
// 删除的二次确认
|
||||
await message.delConfirm()
|
||||
// 发起删除
|
||||
await SqlApi.deleteSqlData(data instanceof Array ? data : [data.id])
|
||||
message.success(t('common.delSuccess'))
|
||||
// 刷新列表
|
||||
await getTableData()
|
||||
} catch {}
|
||||
}
|
||||
|
||||
const sortChange = ({ order, prop }) => {
|
||||
const sortKey = { ascending: 'asc', descending: 'desc' }
|
||||
tableSort.value = { order: sortKey[order], column: prop }
|
||||
if (tablePage.value) tablePage.value['currentPage'] = 1
|
||||
getTableData()
|
||||
}
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(async () => {
|
||||
await getTableData()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.button-info-dialog {
|
||||
.el-dialog__body {
|
||||
padding: 20px;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
886
src/views/lowdesign/tableDesign/components/SqlOption.vue
Normal file
886
src/views/lowdesign/tableDesign/components/SqlOption.vue
Normal file
@@ -0,0 +1,886 @@
|
||||
<template>
|
||||
<div class="summary-top-option table-design-option" :class="{ full: type == 'virtualSql' }">
|
||||
<div class="formatting-content flex items-center" v-if="type == 'summaryTop'">
|
||||
<div>统计控件:</div>
|
||||
<div class="flex-basis-90px mr-10px">
|
||||
<avue-select
|
||||
v-model="summaryControl"
|
||||
:dic="dicObj.summaryControl"
|
||||
:clearable="false"
|
||||
></avue-select>
|
||||
</div>
|
||||
<div class="flex-1 flex items-center" v-if="summaryControl == 'text'">
|
||||
<div class="flex-basis-145px">返回值格式化配置:</div>
|
||||
<el-popover placement="bottom-start" :width="400">
|
||||
<template #reference>
|
||||
<el-input
|
||||
@keydown.space="(e) => e.stopPropagation()"
|
||||
v-model="formattingObj.text"
|
||||
></el-input>
|
||||
</template>
|
||||
<div>
|
||||
<div class="text-16px">示例 </div>
|
||||
<div class="line-height-32px">
|
||||
<div>
|
||||
SQL返回值为:
|
||||
<span class="c-#409EFF">[{sex: "男", cou: 2}, {sex: "女", cou: 3}]</span>
|
||||
</div>
|
||||
<div>
|
||||
格式化配置为:
|
||||
<span class="c-#409EFF">#{sex}:#{cou}人</span>
|
||||
</div>
|
||||
<div>
|
||||
返回值会被格式化为:
|
||||
<span class="c-#409EFF">["男:2人","女:3人"]</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-popover>
|
||||
</div>
|
||||
<div class="flex-1 flex items-center" v-else-if="summaryControl == 'card'">
|
||||
<div class="flex-basis-70px flex-shrink-0">名称配置:</div>
|
||||
<el-popover placement="bottom-start" :width="400">
|
||||
<template #reference>
|
||||
<el-input
|
||||
@keydown.space="(e) => e.stopPropagation()"
|
||||
v-model="formattingObj.card_name"
|
||||
></el-input>
|
||||
</template>
|
||||
<div>
|
||||
<div class="text-16px">示例 </div>
|
||||
<div class="line-height-32px">
|
||||
<div>
|
||||
SQL返回值为:
|
||||
<span class="c-#409EFF">[{sex: "男", cou: 2}, {sex: "女", cou: 3}]</span>
|
||||
</div>
|
||||
<div>
|
||||
名称配置为:
|
||||
<span class="c-#409EFF">#{sex}生</span>
|
||||
</div>
|
||||
<div>
|
||||
名称显示为:
|
||||
<span class="c-#409EFF">男生 女生</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-popover>
|
||||
<div class="flex-basis-70px flex-shrink-0 ml-10px">值配置:</div>
|
||||
<el-popover placement="bottom-start" :width="400">
|
||||
<template #reference>
|
||||
<el-input
|
||||
@keydown.space="(e) => e.stopPropagation()"
|
||||
v-model="formattingObj.card_value"
|
||||
></el-input>
|
||||
</template>
|
||||
<div>
|
||||
<div class="text-16px">示例 </div>
|
||||
<div class="line-height-32px">
|
||||
<div>
|
||||
SQL返回值为:
|
||||
<span class="c-#409EFF">[{sex: "男", cou: 2}, {sex: "女", cou: 3}]</span>
|
||||
</div>
|
||||
<div>
|
||||
值配置为:
|
||||
<span class="c-#409EFF">#{cou}</span>
|
||||
</div>
|
||||
<div>
|
||||
值显示为:
|
||||
<span class="c-#409EFF">3 2</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-popover>
|
||||
</div>
|
||||
<div class="flex-1 flex items-center" v-else-if="['line', 'bar'].includes(summaryControl)">
|
||||
<div class="flex-basis-70px flex-shrink-0">X轴配置:</div>
|
||||
<el-popover placement="bottom-start" :width="400">
|
||||
<template #reference>
|
||||
<el-input
|
||||
@keydown.space="(e) => e.stopPropagation()"
|
||||
v-model="formattingObj[`${summaryControl}_x`]"
|
||||
></el-input>
|
||||
</template>
|
||||
<div>
|
||||
<div class="text-16px">示例 </div>
|
||||
<div class="line-height-32px">
|
||||
<div>
|
||||
SQL返回值为:
|
||||
<span class="c-#409EFF">[{sex: "男", cou: 2}, {sex: "女", cou: 3}]</span>
|
||||
</div>
|
||||
<div>
|
||||
X轴配置为:
|
||||
<span class="c-#409EFF">#{sex}生</span>
|
||||
</div>
|
||||
<div>
|
||||
X轴显示为:
|
||||
<span class="c-#409EFF">男生 女生</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-popover>
|
||||
<div class="flex-basis-70px flex-shrink-0 ml-10px">Y轴配置:</div>
|
||||
<el-popover placement="bottom-start" :width="400">
|
||||
<template #reference>
|
||||
<el-input
|
||||
@keydown.space="(e) => e.stopPropagation()"
|
||||
v-model="formattingObj[`${summaryControl}_y`]"
|
||||
></el-input>
|
||||
</template>
|
||||
<div>
|
||||
<div class="text-16px">示例 </div>
|
||||
<div class="line-height-32px">
|
||||
<div>
|
||||
SQL返回值为:
|
||||
<span class="c-#409EFF">[{sex: "男", cou: 2}, {sex: "女", cou: 3}]</span>
|
||||
</div>
|
||||
<div>
|
||||
Y轴配置为:
|
||||
<span class="c-#409EFF">#{cou}</span>
|
||||
</div>
|
||||
<div>
|
||||
Y轴显示为:
|
||||
<span class="c-#409EFF">3 2</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-popover>
|
||||
<div class="flex-basis-95px flex-shrink-0 ml-10px">Y轴单位名称:</div>
|
||||
<el-input
|
||||
class="flex-basis-80px flex-shrink-0"
|
||||
@keydown.space="(e) => e.stopPropagation()"
|
||||
v-model="formattingObj[`${summaryControl}_y_unit`]"
|
||||
></el-input>
|
||||
</div>
|
||||
<div class="flex-1 flex items-center" v-else-if="summaryControl == 'pie'">
|
||||
<div class="flex-basis-70px flex-shrink-0">名称配置:</div>
|
||||
<el-popover placement="bottom-start" :width="400">
|
||||
<template #reference>
|
||||
<el-input
|
||||
@keydown.space="(e) => e.stopPropagation()"
|
||||
v-model="formattingObj.pie_name"
|
||||
></el-input>
|
||||
</template>
|
||||
<div>
|
||||
<div class="text-16px">示例 </div>
|
||||
<div class="line-height-32px">
|
||||
<div>
|
||||
SQL返回值为:
|
||||
<span class="c-#409EFF">[{sex: "男", cou: 2}, {sex: "女", cou: 3}]</span>
|
||||
</div>
|
||||
<div>
|
||||
名称配置为:
|
||||
<span class="c-#409EFF">#{sex}生</span>
|
||||
</div>
|
||||
<div>
|
||||
名称显示为:
|
||||
<span class="c-#409EFF">男生 女生</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-popover>
|
||||
<div class="flex-basis-70px flex-shrink-0 ml-10px">值配置:</div>
|
||||
<el-popover placement="bottom-start" :width="400">
|
||||
<template #reference>
|
||||
<el-input
|
||||
@keydown.space="(e) => e.stopPropagation()"
|
||||
v-model="formattingObj.pie_value"
|
||||
></el-input>
|
||||
</template>
|
||||
<div>
|
||||
<div class="text-16px">示例 </div>
|
||||
<div class="line-height-32px">
|
||||
<div>
|
||||
SQL返回值为:
|
||||
<span class="c-#409EFF">[{sex: "男", cou: 2}, {sex: "女", cou: 3}]</span>
|
||||
</div>
|
||||
<div>
|
||||
值配置为:
|
||||
<span class="c-#409EFF">#{cou}</span>
|
||||
</div>
|
||||
<div>
|
||||
值显示为:
|
||||
<span class="c-#409EFF">3 2</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-popover>
|
||||
<div class="flex-basis-100px flex-shrink-0 ml-10px">值的单位名称:</div>
|
||||
<el-input
|
||||
class="flex-basis-80px flex-shrink-0"
|
||||
@keydown.space="(e) => e.stopPropagation()"
|
||||
v-model="formattingObj.pie_unit"
|
||||
></el-input>
|
||||
</div>
|
||||
</div>
|
||||
<el-tabs v-model="tabsValue" tab-position="top" type="border-card">
|
||||
<el-tab-pane name="group">
|
||||
<template #label>
|
||||
<el-radio v-model="tabsValue" label="group">{{ typeLabel }}SQL配置</el-radio>
|
||||
</template>
|
||||
<el-container class="h-100%">
|
||||
<el-aside width="190px" class="left-tree">
|
||||
<div class="summary-title">
|
||||
<span>配置工具</span>
|
||||
</div>
|
||||
<div class="summary-draggable">
|
||||
<draggable
|
||||
class="summary-content"
|
||||
tag="div"
|
||||
:list="optionUtil"
|
||||
:group="{ name: 'config', pull: 'clone', put: false }"
|
||||
ghost-class="summary-ghost"
|
||||
:sort="false"
|
||||
item-key="value"
|
||||
>
|
||||
<template #item="{ element }">
|
||||
<div class="summary-item">
|
||||
<span>{{ element.label }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</draggable>
|
||||
</div>
|
||||
</el-aside>
|
||||
<el-main class="main-option">
|
||||
<div
|
||||
v-for="(item, index) in optionsList"
|
||||
:key="index"
|
||||
:class="{ 'option-box': item.type != 'text' }"
|
||||
>
|
||||
<div v-if="item.type == 'text'" class="default-text">{{ item.value }}</div>
|
||||
<div v-else class="h-100%">
|
||||
<div class="option-content" :class="{ full: !sqlObj[item.type].length }">
|
||||
<draggable
|
||||
:class="`content-draggable-${item.type}`"
|
||||
:list="sqlObj[item.type]"
|
||||
:group="{ name: 'option', put: true }"
|
||||
ghost-class="option-ghost"
|
||||
:animation="300"
|
||||
handle=".move-icon"
|
||||
item-key="prop"
|
||||
@add="(e) => handleAddColumn(item.type, e)"
|
||||
>
|
||||
<template #item="{ element }">
|
||||
<div class="summary-control">
|
||||
<template v-if="element.value == 'fieldSelect'">
|
||||
<div class="summary-fun-content flex items-center">
|
||||
<avue-select
|
||||
:class="['select_' + element.prop]"
|
||||
:style="{ width: element.width || '120px' }"
|
||||
v-model="element.text"
|
||||
:dic="fieldList"
|
||||
placeholder="字段"
|
||||
@change="() => setSelectWidth(element, 5)"
|
||||
></avue-select>
|
||||
<div class="w-20px text-14px text-center" v-show="element.alias"
|
||||
>AS</div
|
||||
>
|
||||
<avue-input
|
||||
class="alias w-80px"
|
||||
v-model="element.alias"
|
||||
placeholder="别名"
|
||||
></avue-input>
|
||||
</div>
|
||||
</template>
|
||||
<template v-if="element.value == 'summaryFun'">
|
||||
<div class="summary-fun-content flex items-center">
|
||||
<avue-select
|
||||
:class="['select_' + element.prop + '_summary']"
|
||||
:style="{ width: element.width_summary || '100px' }"
|
||||
v-model="element.text"
|
||||
:dic="summaryType"
|
||||
placeholder="统计函数"
|
||||
filterable
|
||||
allowCreate
|
||||
@change="() => setSelectWidth(element, 3, '_summary')"
|
||||
></avue-select>
|
||||
<div
|
||||
class="flex items-center"
|
||||
v-show="element.text && summaryTypeValueList.includes(element.text)"
|
||||
>
|
||||
<div class="w-20px text-16px text-center mt--5px">(</div>
|
||||
<avue-select
|
||||
:class="['select_' + element.prop + '_filed']"
|
||||
:style="{ width: element.width_filed || '80px' }"
|
||||
class="summary_filed-select"
|
||||
v-model="element.params"
|
||||
:dic="fieldList"
|
||||
placeholder="字段"
|
||||
@change="() => setSelectWidth(element, -19, '_filed')"
|
||||
></avue-select>
|
||||
<div class="w-20px text-16px text-center mt--5px">)</div>
|
||||
<div class="w-20px text-14px text-center" v-show="element.alias"
|
||||
>AS</div
|
||||
>
|
||||
<avue-input
|
||||
class="alias w-80px"
|
||||
v-model="element.alias"
|
||||
placeholder="别名"
|
||||
></avue-input>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-for="control in controlList" :key="control.type">
|
||||
<template v-if="control.type == element.value">
|
||||
<div class="border-box flex items-center">
|
||||
<div class="pr-10px">{{ control.value }}</div>
|
||||
<el-popover
|
||||
placement="bottom-start"
|
||||
:popperStyle="{ width: 'auto', height: 'auto' }"
|
||||
trigger="click"
|
||||
>
|
||||
<template #reference>
|
||||
<div v-if="element.text" class="text-14px pr-10px cursor-pointer">
|
||||
{{ element.text }}
|
||||
</div>
|
||||
<div v-else class="text-12px c-#999">
|
||||
{{ control.tip }}
|
||||
</div>
|
||||
</template>
|
||||
<div class="w-40vw h-200px">
|
||||
<MonacoEditor
|
||||
v-model="element.text"
|
||||
language="mysql"
|
||||
:editorOption="{ minimap: false }"
|
||||
/>
|
||||
</div>
|
||||
</el-popover>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
<template v-if="element.value == 'custom'">
|
||||
<div class="border-box">
|
||||
<el-popover
|
||||
placement="bottom-start"
|
||||
:popperStyle="{ width: 'auto', height: 'auto' }"
|
||||
trigger="click"
|
||||
>
|
||||
<template #reference>
|
||||
<div v-if="element.text" class="text-14px pr-10px cursor-pointer">
|
||||
{{ element.text }}
|
||||
</div>
|
||||
<div v-else class="text-12px c-#999"> 点击输入自定义SQL</div>
|
||||
</template>
|
||||
<div class="w-40vw h-200px">
|
||||
<MonacoEditor
|
||||
v-model="element.text"
|
||||
language="mysql"
|
||||
:editorOption="{ minimap: false }"
|
||||
/>
|
||||
</div>
|
||||
</el-popover>
|
||||
</div>
|
||||
</template>
|
||||
<div class="del-icon" @click="delRow(item.type, element)">
|
||||
<Icon
|
||||
color="#F56C6C"
|
||||
:size="22"
|
||||
icon="lets-icons:dell-fill"
|
||||
class="mr-3px"
|
||||
/>
|
||||
</div>
|
||||
<div class="move-icon" @click="delRow(item.type, element)">
|
||||
<Icon :size="22" icon="mingcute:move-line" class="mr-3px" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</draggable>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="summary-result flex h-60px box-border p-10px mt-20px">
|
||||
<div class="flex-basis-85px flex-shrink-0">输 出 内 容:</div>
|
||||
<div class="flex-1 flex flex-wrap gap-x-10px">
|
||||
<div v-if="type == 'virtualSql'" class="c-#67C23A">SELECT (</div>
|
||||
<span
|
||||
v-for="item in valueText"
|
||||
:key="item.text"
|
||||
:class="[
|
||||
item.color ? `c-${item.color}` : `c-#333 .dark:c-[var(--el-text-color-regular)]`
|
||||
]"
|
||||
>{{ item.text }}</span
|
||||
>
|
||||
<div v-if="type == 'virtualSql'" class="c-#67C23A">
|
||||
) FROM {{ tableName }} tbl
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-main>
|
||||
</el-container>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane name="custom" class="pl-20px pr-20px pt-10px">
|
||||
<template #label>
|
||||
<el-radio v-model="tabsValue" label="custom">自定义{{ typeLabel }}SQL</el-radio>
|
||||
</template>
|
||||
<div class="pb-10px text-16px">
|
||||
<span>自定义{{ typeLabel }}SQL语句</span>
|
||||
<span class="text-14px c-#E6A23C ml-10px" v-if="type == 'summaryTop'">
|
||||
当前表的数据源:#{jeelowcode_summary_table}
|
||||
</span>
|
||||
</div>
|
||||
<div class="custom-box">
|
||||
<MonacoEditor v-model="customValue" language="mysql"></MonacoEditor>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import draggable from 'vuedraggable'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
import { dicObj } from '../designData'
|
||||
defineOptions({ name: 'SqlOption' })
|
||||
|
||||
interface Props {
|
||||
type: 'summaryTop' | 'virtualSql'
|
||||
show: boolean
|
||||
fieldList: Array<any>
|
||||
tableName?: string
|
||||
}
|
||||
const props = defineProps<Props>()
|
||||
const sqlStr = defineModel<string>({ default: '' })
|
||||
|
||||
const summaryControl = ref('text')
|
||||
const formattingObj = ref<any>({})
|
||||
const formattingKey = [
|
||||
//文本
|
||||
'text',
|
||||
//卡片
|
||||
'card_name',
|
||||
'card_value',
|
||||
//折线图
|
||||
'line_x',
|
||||
'line_y',
|
||||
'line_y_unit',
|
||||
//柱状图
|
||||
'bar_x',
|
||||
'bar_y',
|
||||
'bar_y_unit',
|
||||
//饼图
|
||||
'pie_value',
|
||||
'pie_name',
|
||||
'pie_unit'
|
||||
]
|
||||
formattingKey.forEach((key) => (formattingObj.value[key] = ''))
|
||||
const sqlObj = ref({
|
||||
top: [],
|
||||
buttom: []
|
||||
})
|
||||
const typeLabel = ref('')
|
||||
const optionUtil = [
|
||||
{ label: '字段选择', value: 'fieldSelect' },
|
||||
{ label: '过滤(WHERE)', value: 'where' },
|
||||
{ label: '统计函数', value: 'summaryFun' },
|
||||
{ label: '分组(GROUP BY)', value: 'groupBy' },
|
||||
{ label: '筛选(HAVING)', value: 'having' },
|
||||
{ label: '排序(ORDER BY)', value: 'orderBy' },
|
||||
{ label: '自定义SQL', value: 'custom' }
|
||||
]
|
||||
const summaryType = [
|
||||
{ label: 'COUNT', value: 'COUNT' },
|
||||
{ label: 'SUM', value: 'SUM' },
|
||||
{ label: 'AVG', value: 'AVG' },
|
||||
{ label: 'MAX', value: 'MAX' },
|
||||
{ label: 'MIN', value: 'MIN' },
|
||||
{ label: '其他函数请直接输入', value: '-1', disabled: true }
|
||||
]
|
||||
const controlList = [
|
||||
{ type: 'where', value: 'WHERE', tip: '点击输入WHERE过滤条件' },
|
||||
{ type: 'groupBy', value: 'GROUP BY', tip: '点击输入分组SQL' },
|
||||
{ type: 'having', value: 'HAVING', tip: '点击输入having条件' },
|
||||
{ type: 'orderBy', value: 'ORDER BY', tip: '点击输入排序SQL 例如:id DESC' }
|
||||
]
|
||||
const optionsList = ref([
|
||||
{ type: 'text', value: 'SELECT' },
|
||||
{ type: 'top' },
|
||||
{ type: 'text', value: 'FROM (当前表的数据源) tbl' },
|
||||
{ type: 'buttom' }
|
||||
])
|
||||
const summaryTypeValueList = summaryType.map((item) => item.value)
|
||||
|
||||
const tabsValue = ref('group')
|
||||
const customValue = ref('')
|
||||
|
||||
const valueText = computed(() => {
|
||||
const sqlList: any[] = [{ text: 'SELECT ' }]
|
||||
for (const key in sqlObj.value) {
|
||||
if (key == 'buttom') {
|
||||
if (props.type == 'summaryTop') {
|
||||
sqlList.push({
|
||||
text: ' FROM (当前表的数据源) tbl ',
|
||||
practical: ' FROM (#{jeelowcode_summary_table}) tbl '
|
||||
})
|
||||
} else if (props.type == 'virtualSql') sqlList.push({ text: ' FROM ' })
|
||||
}
|
||||
let lastType = ''
|
||||
let sqlText = ''
|
||||
sqlObj.value[key].forEach((item) => {
|
||||
const value = item.value
|
||||
const text = item.text === undefined ? '' : item.text
|
||||
const isComma = ['fieldSelect', 'summaryFun'].includes(lastType)
|
||||
if (isComma) sqlText = sqlText + ', '
|
||||
if (['fieldSelect', 'custom'].includes(value)) {
|
||||
sqlText = `${sqlText}${text}${item.alias ? ' AS ' + item.alias : ''}`
|
||||
} else if (value == 'summaryFun') {
|
||||
if (summaryTypeValueList.includes(text)) {
|
||||
sqlText =
|
||||
sqlText + `${text}(${item.params || ''})${item.alias ? ' AS ' + item.alias : ''}`
|
||||
} else sqlText = sqlText + text
|
||||
} else if (value == 'where') sqlText = sqlText + `WHERE ${item.text || ''} `
|
||||
else if (value == 'groupBy') sqlText = sqlText + `GROUP BY ${item.text || ''} `
|
||||
else if (value == 'orderBy') sqlText = sqlText + `ORDER BY ${item.text || ''} `
|
||||
else if (value == 'having') sqlText = sqlText + `HAVING ${item.text || ''} `
|
||||
lastType = item.value
|
||||
})
|
||||
sqlList.push({ text: sqlText, color: '#409EFF' })
|
||||
}
|
||||
return sqlList
|
||||
})
|
||||
|
||||
const initFun = () => {
|
||||
if (props.type == 'summaryTop') {
|
||||
typeLabel.value = '统计'
|
||||
summaryControl.value = 'text'
|
||||
formattingObj.value = {}
|
||||
formattingKey.forEach((key) => (formattingObj.value[key] = ''))
|
||||
} else if (props.type == 'virtualSql') {
|
||||
optionsList.value[2].value = 'FROM'
|
||||
typeLabel.value = '格式化'
|
||||
}
|
||||
sqlObj.value = { top: [], buttom: [] }
|
||||
if (sqlStr.value && sqlStr.value.indexOf('sqlObj') !== -1) {
|
||||
const data = JSON.parse(sqlStr.value)
|
||||
customValue.value = data.custom
|
||||
tabsValue.value = data.valueType
|
||||
if (props.type == 'summaryTop') {
|
||||
formattingObj.value = data.formattingObj
|
||||
summaryControl.value = data.summaryControl || 'text'
|
||||
}
|
||||
|
||||
for (let key in data.sqlObj) {
|
||||
data.sqlObj[key].forEach((item) => {
|
||||
sqlObj.value[key].push({
|
||||
...item,
|
||||
prop: `option_${Math.ceil(Math.random() * 9999999)}`
|
||||
})
|
||||
})
|
||||
}
|
||||
} else {
|
||||
customValue.value = ''
|
||||
tabsValue.value = 'group'
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.show,
|
||||
(val) => {
|
||||
if (val) initFun()
|
||||
}
|
||||
)
|
||||
|
||||
const setSelectWidth = (element, num = 0, addClass = '') => {
|
||||
setTimeout(() => {
|
||||
const el = document.querySelector(
|
||||
`.main-option .select_${element.prop}${addClass} .el-select__selected-item.el-select__placeholder span`
|
||||
) as HTMLSpanElement
|
||||
if (!el) return
|
||||
const width = el.offsetWidth
|
||||
element[`width${addClass}`] = width + 40 + num + 'px'
|
||||
}, 30)
|
||||
}
|
||||
|
||||
const handleAddColumn = (dataKey, e) => {
|
||||
const newIndex = e.newIndex
|
||||
const data = cloneDeep(sqlObj.value[dataKey][newIndex])
|
||||
if (!data.prop) data.prop = `option_${Math.ceil(Math.random() * 9999999)}`
|
||||
|
||||
if (data.value == 'groupBy') {
|
||||
const fieldArr = []
|
||||
sqlObj.value.top.forEach((item) => {
|
||||
if (item['value'] == 'fieldSelect' && item['text']) fieldArr.push(item['text'])
|
||||
})
|
||||
if (fieldArr.length) data.text = fieldArr.join(',')
|
||||
}
|
||||
delete data.label
|
||||
|
||||
sqlObj.value[dataKey][newIndex] = data
|
||||
}
|
||||
|
||||
const delRow = (dataKey, row) => {
|
||||
sqlObj.value[dataKey] = sqlObj.value[dataKey].filter((item) => item.prop != row.prop)
|
||||
}
|
||||
|
||||
const getOptionStr = () => {
|
||||
if (sqlObj.value.top.length || sqlObj.value.buttom.length || customValue.value) {
|
||||
const copySqlObj = cloneDeep(sqlObj.value)
|
||||
for (let key in copySqlObj) {
|
||||
copySqlObj[key] = copySqlObj[key].map((item) => {
|
||||
delete item.prop
|
||||
return item
|
||||
})
|
||||
}
|
||||
const obj: any = {
|
||||
sqlObj: copySqlObj,
|
||||
valueType: tabsValue.value,
|
||||
custom: customValue.value,
|
||||
group: valueText.value.map((item) => item.practical || item.text).join('')
|
||||
}
|
||||
if (props.type == 'summaryTop') {
|
||||
obj.formattingObj = formattingObj.value
|
||||
obj.summaryControl = summaryControl.value
|
||||
}
|
||||
return JSON.stringify(obj)
|
||||
} else ''
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
initFun()
|
||||
})
|
||||
|
||||
defineExpose({ getOptionStr })
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.summary-top-option {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
.formatting-content {
|
||||
padding: 5px 10px;
|
||||
background-color: var(--el-fill-color-light);
|
||||
border-bottom: 1px solid var(--el-border-color-light);
|
||||
}
|
||||
|
||||
.left-tree {
|
||||
padding: 10px;
|
||||
border-right: 1px solid #f1f1f1;
|
||||
|
||||
.summary-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
cursor: pointer;
|
||||
|
||||
.el-icon {
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
|
||||
&:nth-last-of-type(1) {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.summary-draggable {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.summary-item {
|
||||
width: 130px;
|
||||
padding: 4px 8px;
|
||||
margin-bottom: 10px;
|
||||
font-size: 14px;
|
||||
cursor: move;
|
||||
background-color: #f4f6fc;
|
||||
border: 1px dashed #f4f6fc;
|
||||
|
||||
&:hover {
|
||||
color: var(--el-color-primary);
|
||||
border-color: var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.main-option {
|
||||
padding: 0;
|
||||
border-left: 0;
|
||||
|
||||
.default-text {
|
||||
padding: 10px;
|
||||
font-size: 16px;
|
||||
color: #409eff;
|
||||
}
|
||||
|
||||
.option-box {
|
||||
height: calc((100% - 60px) / 2 - 60px);
|
||||
min-height: 100px;
|
||||
}
|
||||
|
||||
.option-content {
|
||||
position: relative;
|
||||
width: calc(100% - 20px);
|
||||
height: 100%;
|
||||
margin-left: 10px;
|
||||
border: 1px solid #f1f1f1;
|
||||
|
||||
&.full {
|
||||
&::before {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
left: 10px;
|
||||
font-size: 16px;
|
||||
color: #999;
|
||||
content: '拖拽左侧配置工具,配置统计SQL';
|
||||
}
|
||||
}
|
||||
|
||||
.content-draggable-top,
|
||||
.content-draggable-buttom {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
padding: 10px;
|
||||
padding-bottom: 40px;
|
||||
overflow-y: auto;
|
||||
box-sizing: border-box;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px 8px;
|
||||
align-content: flex-start;
|
||||
|
||||
.summary-control {
|
||||
position: relative;
|
||||
|
||||
.del-icon {
|
||||
position: absolute;
|
||||
top: -9px;
|
||||
left: -8px;
|
||||
display: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.move-icon {
|
||||
position: absolute;
|
||||
right: -14px;
|
||||
bottom: -9px;
|
||||
display: none;
|
||||
cursor: move;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.del-icon {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.move-icon {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.option-ghost {
|
||||
position: relative;
|
||||
width: 0 !important;
|
||||
height: 32px;
|
||||
min-width: 0 !important;
|
||||
padding: 0 !important;
|
||||
overflow: hidden;
|
||||
font-size: 0;
|
||||
background: white;
|
||||
border-left: 5px solid var(--el-color-primary);
|
||||
content: '';
|
||||
outline: none 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
|
||||
.summary-result {
|
||||
border-top: 1px solid #dcdfe6;
|
||||
}
|
||||
}
|
||||
|
||||
.right-custom {
|
||||
.custom-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 32px;
|
||||
padding: 4px 0;
|
||||
font-size: 14px;
|
||||
line-height: 32px;
|
||||
text-align: center;
|
||||
background-color: #fafafa;
|
||||
border: 1px solid #dcdfe6;
|
||||
}
|
||||
|
||||
.custom-content {
|
||||
width: 100%;
|
||||
height: calc(100% - 42px);
|
||||
}
|
||||
}
|
||||
|
||||
.custom-box {
|
||||
height: calc(100% - 51px);
|
||||
}
|
||||
|
||||
::v-deep(.el-tabs) {
|
||||
height: calc(100% - 43px);
|
||||
border: none;
|
||||
|
||||
.el-tabs__header {
|
||||
margin-right: 0;
|
||||
|
||||
.el-tabs__nav {
|
||||
.el-tabs__item {
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.el-tabs__content {
|
||||
height: calc(100% - 39px);
|
||||
padding: 0;
|
||||
|
||||
.el-tab-pane {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.border-box {
|
||||
min-height: 30px;
|
||||
padding: 1px 11px;
|
||||
line-height: 30px;
|
||||
border-radius: 5px;
|
||||
box-shadow: 0 0 0 1px var(--el-input-border-color, var(--el-border-color)) inset;
|
||||
}
|
||||
|
||||
.summary-fun-content {
|
||||
padding: 1px;
|
||||
border-radius: 5px;
|
||||
box-shadow: 0 0 0 1px var(--el-input-border-color, var(--el-border-color)) inset;
|
||||
|
||||
::v-deep(.el-input__wrapper),
|
||||
::v-deep(.el-select__wrapper) {
|
||||
height: 30px;
|
||||
padding: 0 11px;
|
||||
box-shadow: none;
|
||||
|
||||
.el-input__inner {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.summary_filed-select {
|
||||
::v-deep(.el-select__wrapper) {
|
||||
padding-right: 0;
|
||||
padding-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.alias {
|
||||
::v-deep(.el-input) {
|
||||
.el-input__inner {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.summary-top-option.full ::v-deep(.el-tabs) .el-tabs__content {
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
736
src/views/lowdesign/tableDesign/components/TableInfo.vue
Normal file
736
src/views/lowdesign/tableDesign/components/TableInfo.vue
Normal file
@@ -0,0 +1,736 @@
|
||||
<template>
|
||||
<avue-tabs ref="tabsRef" :option="tabsOption" @change="tabsHandleChange"></avue-tabs>
|
||||
<InfoVxeTopBtn
|
||||
v-show="tabsValue.edit"
|
||||
:selectNum="tabsValue.dataKey ? infoSelect[tabsValue.dataKey].length : 0"
|
||||
:tabItem="tabsValue"
|
||||
:size="size"
|
||||
@cell-add="cellAddData"
|
||||
@del-data="delTableInfoData"
|
||||
@order-data="setInfoOrder"
|
||||
>
|
||||
<template #default>
|
||||
<el-button @click.stop="setDefaultField" v-show="tabsValue.key == 'mysql'">
|
||||
<span>{{ viewDefaultField == 'Y' ? '隐藏' : '显示' }}系统字段</span>
|
||||
<el-tooltip effect="dark" content="仅影响当前界面显示,不影响系统字段的创建">
|
||||
<Icon :size="14" icon="ep:info-filled"></Icon>
|
||||
</el-tooltip>
|
||||
</el-button>
|
||||
</template>
|
||||
</InfoVxeTopBtn>
|
||||
<template v-for="tab in tabsOption.column" :key="tab.prop">
|
||||
<div v-show="tabsValue.prop === tab.prop">
|
||||
<div v-if="tab.prop == 'tab_exp'" class="flex items-center mb-8px ml-20px">
|
||||
<div class="flex-basis-160px flex-shrink-0">导入数据重复处理类型:</div>
|
||||
<avue-select class="w-120px!" v-model="tableFormData.importDuplicateType" :dic="dicObj.importDuplicateType"></avue-select>
|
||||
</div>
|
||||
<template v-if="tab.children">
|
||||
<el-tabs
|
||||
:class="{ 'h-430px': childTabsValue.edit, 'h-386px': !childTabsValue.edit }"
|
||||
v-model="childTabsValue.prop"
|
||||
tabPosition="left"
|
||||
type="border-card"
|
||||
@tab-change="childTabChange"
|
||||
>
|
||||
<InfoVxeTopBtn
|
||||
v-show="childTabsValue.edit"
|
||||
:selectNum="childTabsValue.dataKey ? infoSelect[childTabsValue.dataKey].length : 0"
|
||||
:tabItem="childTabsValue"
|
||||
:size="size"
|
||||
@cell-add="cellAddData"
|
||||
@del-data="delTableInfoData"
|
||||
@order-data="setInfoOrder"
|
||||
/>
|
||||
<el-tab-pane
|
||||
v-for="child in tab.children"
|
||||
:key="child.prop"
|
||||
:name="child.prop"
|
||||
:label="child.label"
|
||||
>
|
||||
<InfoVxeTable
|
||||
v-model="infoData[child.dataKey]"
|
||||
:ref="(el) => (tableRefObj[child.prop] = el)"
|
||||
:tabItem="child"
|
||||
:column="tableInfoOption.infoColumn[`${child.key}Column`]"
|
||||
@selection-change="infoSelectionChange"
|
||||
@cell-click="cellClick"
|
||||
@dropdown-command="dorpdownHandleCommand"
|
||||
></InfoVxeTable>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</template>
|
||||
<template v-else>
|
||||
<InfoVxeTable
|
||||
v-model="infoData[tab.dataKey]"
|
||||
:ref="(el) => (tableRefObj[tab.prop] = el)"
|
||||
:tabItem="tab"
|
||||
:column="tableInfoOption.infoColumn[`${tab.key}Column`]"
|
||||
:checkboxConfig="tab.key == 'mysql' ? { checkMethod: mysqlCheckMethod } : {}"
|
||||
:row-class-name="tab.key == 'virtual' ? virtualRowClassName : ''"
|
||||
@selection-change="infoSelectionChange"
|
||||
@cell-click="cellClick"
|
||||
@dropdown-command="dorpdownHandleCommand"
|
||||
></InfoVxeTable>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
<DesignPopup
|
||||
v-model="optionsDialog.dialog.value"
|
||||
v-bind="optionsDialog.dialog"
|
||||
:isBodyFull="true"
|
||||
>
|
||||
<template #default>
|
||||
<template v-for="(control, key) in optionComponents" :key="key">
|
||||
<component
|
||||
:ref="(el) => (optionRef[key] = el)"
|
||||
v-if="key == optionsDialog.controlType"
|
||||
:is="control"
|
||||
v-model="optionsDialog.controlValue"
|
||||
v-bind="optionsDialog.controlData"
|
||||
:show="optionsDialog.dialog.value"
|
||||
></component>
|
||||
</template>
|
||||
</template>
|
||||
</DesignPopup>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import ConfigOption from './ConfigOption.vue'
|
||||
import FormattingOption from './FormattingOption.vue'
|
||||
import SqlOption from './SqlOption.vue'
|
||||
import InfoVxeTable from './InfoVxeTable.vue'
|
||||
import InfoVxeTopBtn from './InfoVxeTopBtn.vue'
|
||||
import { MonacoEditor } from '@/components/MonacoEditor/index'
|
||||
import { VerifyOption } from '../../general/components/index'
|
||||
import { tableInfoOption, dicObj } from '../designData'
|
||||
import { formattingLengStr } from '@/utils/lowDesign'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
|
||||
import * as DictDataApi from '@/api/system/dict/dict.type'
|
||||
import * as TableApi from '@/api/design/table/index'
|
||||
|
||||
defineOptions({ name: 'TableInfo' })
|
||||
const message = useMessage()
|
||||
|
||||
interface Props {
|
||||
formType: string
|
||||
editInfoData: any
|
||||
showDef?: string
|
||||
size: any
|
||||
tableName: string
|
||||
}
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
formType: ''
|
||||
})
|
||||
const emit = defineEmits(['set-tree-label'])
|
||||
|
||||
const tableFormData = defineModel({ type: Object })
|
||||
|
||||
const tabsOption = ref({
|
||||
column: [
|
||||
{ label: '数据库属性', prop: 'tab_mysql', key: 'mysql', dataKey: 'basics', edit: true },
|
||||
{ label: '页面属性', prop: 'tab_view', key: 'view', dataKey: 'basics' },
|
||||
{ label: '查询属性', prop: 'tab_query', key: 'query', dataKey: 'basics' },
|
||||
{ label: '字典配置', prop: 'tab_dic', key: 'dic', dataKey: 'basics' },
|
||||
{ label: '导入/导出配置', prop: 'tab_exp', key: 'exp', dataKey: 'basics' },
|
||||
{
|
||||
label: '统计配置',
|
||||
prop: 'tab_summary',
|
||||
children: [
|
||||
{
|
||||
label: '表格底部统计',
|
||||
prop: 'tab_summary_bottom',
|
||||
key: 'summaryBottom',
|
||||
dataKey: 'basics'
|
||||
},
|
||||
{
|
||||
label: '表格顶部统计',
|
||||
prop: 'tab_summary_top',
|
||||
key: 'summaryTop',
|
||||
dataKey: 'summaryTop',
|
||||
edit: true
|
||||
}
|
||||
]
|
||||
},
|
||||
{ label: '虚拟字段格式化', prop: 'tab_virtual', key: 'virtual', dataKey: 'basics' },
|
||||
{ label: '外键', prop: 'tab_key', key: 'key', dataKey: 'basics' },
|
||||
{ label: '索引', prop: 'tab_index', key: 'index', dataKey: 'index', edit: true }
|
||||
]
|
||||
})
|
||||
const tabsValue = ref(tabsOption.value.column[0])
|
||||
const childTabsValue = ref<any>({})
|
||||
const viewDefaultField = ref('N')
|
||||
|
||||
const infoData = ref({
|
||||
basics: [] as any[],
|
||||
index: [],
|
||||
summaryTop: []
|
||||
})
|
||||
const infoSelect = ref({
|
||||
basics: [],
|
||||
index: [],
|
||||
summaryTop: []
|
||||
})
|
||||
const tableInfoDefault = ref<any[]>([])
|
||||
const optionsDialog = ref<any>({ dialog: {}, controlData: {} })
|
||||
const tableRefObj = ref({})
|
||||
|
||||
const optionComponents = markRaw({
|
||||
config: ConfigOption,
|
||||
verify: VerifyOption,
|
||||
formatting: FormattingOption,
|
||||
sqlOption: SqlOption,
|
||||
meditor: MonacoEditor
|
||||
})
|
||||
const optionRef = ref({})
|
||||
const tabsRef = ref()
|
||||
|
||||
const fieldList = computed(() => {
|
||||
let dicData: Array<{ label: string; value: string; type: string }> = []
|
||||
infoData.value.basics.forEach((item) => {
|
||||
if (item.fieldCode && item.isDb == 'Y')
|
||||
dicData.push({
|
||||
label: `${item.fieldCode}${item.fieldName ? '(' + item.fieldName + ')' : ''}`,
|
||||
value: item.fieldCode,
|
||||
type: item.fieldType
|
||||
})
|
||||
})
|
||||
if (viewDefaultField.value == 'N') {
|
||||
dicData = [...dicData, ...dicObj.indexFieldDefault]
|
||||
}
|
||||
return dicData
|
||||
})
|
||||
|
||||
const treeLabel = computed(() => {
|
||||
let dicData: Array<{ label: string; value: string }> = []
|
||||
infoData.value.basics.forEach((item) => {
|
||||
if (item.fieldCode && !item.only)
|
||||
dicData.push({
|
||||
label: `${item.fieldCode}${item.fieldName ? '(' + item.fieldName + ')' : ''}`,
|
||||
value: item.fieldCode
|
||||
})
|
||||
})
|
||||
return dicData
|
||||
})
|
||||
|
||||
watch(
|
||||
() => fieldList.value,
|
||||
(dicData: any) => {
|
||||
const dicDataObj = {}
|
||||
dicData.forEach(({ value, label }) => (dicDataObj[value] = label))
|
||||
Object.assign(tableInfoOption.infoColumn.indexColumn.indexFieldCodeList.editRender, {
|
||||
dicData: dicData,
|
||||
dicObj: dicDataObj
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
watch(
|
||||
() => treeLabel.value,
|
||||
(val: any) => {
|
||||
emit('set-tree-label', val)
|
||||
}
|
||||
)
|
||||
|
||||
const dorpdownHandleCommand = (command) => {
|
||||
let { dataKey, children } = tabsValue.value
|
||||
if (children) dataKey = childTabsValue.value.dataKey
|
||||
if (!dataKey) return
|
||||
let { index, type } = command
|
||||
if (type == 'up' || type == 'down') {
|
||||
const delItem = infoData.value[dataKey].splice(index, 1)[0]
|
||||
nextTick(() => infoData.value[dataKey].splice(type == 'up' ? index - 1 : index + 1, 0, delItem))
|
||||
} else if (type == 'add') cellAddData(index + 1)
|
||||
}
|
||||
const setInfoOrder = () => {
|
||||
let { dataKey, children } = tabsValue.value
|
||||
if (children) dataKey = childTabsValue.value.dataKey
|
||||
if (!dataKey) return
|
||||
message
|
||||
.prompt('请输入序号(格式:原序号/最终序号)', '调整排序', { inputValue: '/', type: '' })
|
||||
.then(({ value }) => {
|
||||
let orderArr = value.split('/')
|
||||
if (orderArr.length == 2) {
|
||||
const delItem = infoData.value[dataKey].splice(Number(orderArr[0].trim()) - 1, 1)[0]
|
||||
nextTick(() => infoData.value[dataKey].splice(Number(orderArr[1].trim()) - 1, 0, delItem))
|
||||
} else message.error('请输入正确的格式,如:2/3')
|
||||
})
|
||||
.catch(() => {})
|
||||
}
|
||||
const setDefaultField = () => {
|
||||
let defaultData = [] as any
|
||||
let basicsData = [] as any
|
||||
if (viewDefaultField.value == 'Y') {
|
||||
//隐藏
|
||||
infoData.value.basics.forEach((item, index) => {
|
||||
item.sortNum = index + 1
|
||||
if (
|
||||
tableInfoOption.disabledArr.includes(item.fieldCode) &&
|
||||
!['id', 'pid'].includes(item.fieldCode)
|
||||
) {
|
||||
defaultData.push(item)
|
||||
} else {
|
||||
basicsData.push(item)
|
||||
}
|
||||
})
|
||||
viewDefaultField.value = 'N'
|
||||
} else {
|
||||
basicsData = [...infoData.value.basics]
|
||||
tableInfoDefault.value.forEach((item) => {
|
||||
basicsData.splice(item.sortNum - 1, 0, item)
|
||||
})
|
||||
viewDefaultField.value = 'Y'
|
||||
}
|
||||
tableInfoDefault.value = defaultData
|
||||
infoData.value.basics = basicsData
|
||||
}
|
||||
|
||||
const mysqlCheckMethod = ({ row }) => {
|
||||
return !row.only
|
||||
}
|
||||
const virtualRowClassName = ({ row }) => {
|
||||
if (row.isDb == 'Y') return 'virtual-hide-row'
|
||||
}
|
||||
|
||||
const infoSelectionChange = (selectObj) => {
|
||||
let { dataKey, children } = tabsValue.value
|
||||
if (children) dataKey = childTabsValue.value.dataKey
|
||||
if (!dataKey) return
|
||||
infoSelect.value[dataKey] = selectObj.records
|
||||
}
|
||||
|
||||
const delTableInfoData = () => {
|
||||
let { dataKey, prop, children } = tabsValue.value
|
||||
if (children) {
|
||||
dataKey = childTabsValue.value.dataKey
|
||||
prop = childTabsValue.value.prop
|
||||
}
|
||||
if (!dataKey) return
|
||||
const keyArr = infoSelect.value[dataKey].map((item) => item._X_ROW_KEY)
|
||||
infoData.value[dataKey] = infoData.value[dataKey].filter(
|
||||
(item) => !keyArr.includes(item._X_ROW_KEY)
|
||||
)
|
||||
tableRefObj.value[prop].vxeTableRef.clearCheckboxRow()
|
||||
infoSelect.value[dataKey] = []
|
||||
}
|
||||
|
||||
const setTableInfoTree = (tableType) => {
|
||||
let isPid = false
|
||||
infoData.value.basics = infoData.value.basics.filter((item) => {
|
||||
if (item.fieldCode == 'pid') {
|
||||
if (item.only || isPid) return false
|
||||
isPid = true
|
||||
}
|
||||
return true
|
||||
})
|
||||
if (tableType == 2) {
|
||||
if (isPid) {
|
||||
infoData.value.basics = infoData.value.basics.map((item) => {
|
||||
if (item.fieldCode == 'pid') item.only = true
|
||||
return item
|
||||
})
|
||||
} else {
|
||||
infoData.value.basics.splice(1, 0, ...tableInfoOption.getDefaultMysqlField('tree'))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const tabsHandleChange = (column) => {
|
||||
tabsValue.value = column
|
||||
if (column.children?.length) childTabsValue.value = cloneDeep(column.children[0])
|
||||
}
|
||||
const childTabChange = (prop) => {
|
||||
if (tabsValue.value.children) {
|
||||
const index = tabsValue.value.children.findIndex((item) => item.prop == prop)
|
||||
childTabsValue.value = cloneDeep(tabsValue.value.children[index])
|
||||
} else childTabsValue.value = []
|
||||
}
|
||||
const setTabsValue = (prop) => {
|
||||
tabsOption.value.column.forEach((item, index) => {
|
||||
if (item.prop == prop) {
|
||||
tabsRef.value.changeTabs(index)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const cellAddData = (addIndex) => {
|
||||
let { prop, key, dataKey, children } = tabsValue.value
|
||||
if (children) {
|
||||
prop = childTabsValue.value.prop
|
||||
key = childTabsValue.value.key
|
||||
dataKey = childTabsValue.value.dataKey
|
||||
}
|
||||
if (!dataKey) return
|
||||
let addData = cloneDeep(tableInfoOption.infoDefaultData[dataKey]) || {}
|
||||
let index = infoData.value[dataKey].length
|
||||
if (key == 'mysql' && addIndex === undefined) {
|
||||
const indexList = [] as any
|
||||
infoData.value[dataKey].forEach((item, index) => {
|
||||
if (
|
||||
tableInfoOption.disabledArr.includes(item.fieldCode) &&
|
||||
!['id', 'pid'].includes(item.fieldCode)
|
||||
)
|
||||
indexList.push({ label: item.fieldCode, value: index })
|
||||
})
|
||||
indexList.sort((a, b) => a.value - b.value)
|
||||
if (indexList.length) {
|
||||
for (const i in indexList) {
|
||||
const currIndex = Number(i)
|
||||
if (indexList[currIndex + 1] === undefined) break
|
||||
if (indexList[currIndex].value != indexList[currIndex + 1].value - 1) {
|
||||
index = indexList[currIndex + 1].value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
infoData.value[dataKey].splice(addIndex === undefined ? index : addIndex, 0, addData)
|
||||
tableScrollIndex(key, index, addIndex)
|
||||
}
|
||||
|
||||
const tableScrollIndex = (key, index, addIndex?) => {
|
||||
setTimeout(() => {
|
||||
const tableBodyDom = document.querySelector(`.${key}-vxe-table .vxe-table--body-wrapper`)
|
||||
if (tableBodyDom) {
|
||||
const bool = addIndex === undefined
|
||||
tableBodyDom.scrollTop =
|
||||
(bool ? index : addIndex) * 40 - (bool ? 0 : tableBodyDom['offsetHeight'] / 2)
|
||||
}
|
||||
if (key == 'summaryTop') key = 'summary_top'
|
||||
tableRefObj.value[`tab_${key}`].vxeTableRef.setEditRow(
|
||||
tableRefObj.value[`tab_${key}`].vxeTableRef.getData(addIndex ? addIndex : index)
|
||||
)
|
||||
}, 300)
|
||||
}
|
||||
|
||||
const openOptions = (type, { row, dataKey, index, typeText, infoKey }, otherData = {}) => {
|
||||
const { viewColumn } = tableInfoOption.infoColumn
|
||||
const controlText = viewColumn.controlType.editRender.dicObj[row.controlType]
|
||||
let title = ''
|
||||
if (['virtual_sql_str', 'virtual_java_str'].includes(dataKey))
|
||||
title = `${row.fieldCode} ${typeText}`
|
||||
else if (!row.fieldCode) title = typeText
|
||||
else if (row.fieldCode) title = `${row.fieldCode} ${row.fieldName} ${typeText}(${controlText})`
|
||||
|
||||
optionsDialog.value = {
|
||||
controlValue: row[dataKey],
|
||||
controlType: type,
|
||||
controlData: { ...otherData },
|
||||
dialog: {
|
||||
value: true,
|
||||
title,
|
||||
handleClose: (done) => {
|
||||
let optionStr = ''
|
||||
const controlRef = optionRef.value[type]
|
||||
if (controlRef?.getOptionStr) optionStr = controlRef.getOptionStr()
|
||||
else if (controlRef?.getValue) optionStr = controlRef.getValue()
|
||||
done()
|
||||
setTimeout(() => {
|
||||
infoData.value[infoKey][index][dataKey] = optionStr
|
||||
if (infoKey == 'summaryTop' && dataKey == 'summarySql') {
|
||||
const data = JSON.parse(optionStr || '{}')
|
||||
infoData.value[infoKey][index].summaryControl = data.summaryControl || 'text'
|
||||
}
|
||||
}, 30)
|
||||
},
|
||||
dialogParams: { closeOnPressEscape: true, closeOnClickModal: true }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const lowClickInputClick = (row, dataKey, index, type) => {
|
||||
let typeText = ''
|
||||
let infoKey = 'basics'
|
||||
const otherData: any = {}
|
||||
if (['controlsConfig', 'queryConfig'].includes(dataKey)) {
|
||||
typeText = dataKey == 'controlsConfig' ? '控件配置' : '查询配置'
|
||||
otherData.configKey =
|
||||
dataKey == 'controlsConfig'
|
||||
? [row.controlType + 'Config', 'cellConfig', 'formConfig']
|
||||
: ['queryConfig']
|
||||
} else if (dataKey == 'verifyConfig') {
|
||||
typeText = '校验配置'
|
||||
} else if (dataKey == 'summarySql') {
|
||||
typeText = '统计SQL配置'
|
||||
infoKey = 'summaryTop'
|
||||
otherData.type = 'summaryTop'
|
||||
} else if (dataKey == 'virtual_sql_str') {
|
||||
typeText = '格式化配置处理'
|
||||
otherData.type = 'virtualSql'
|
||||
otherData.tableName = props.tableName
|
||||
}
|
||||
if (['summarySql', 'virtual_sql_str', 'virtual_java_str'].includes(dataKey)) {
|
||||
otherData.fieldList = fieldList.value
|
||||
}
|
||||
|
||||
openOptions(type, { row, dataKey, index, typeText, infoKey }, otherData)
|
||||
}
|
||||
|
||||
const cellClick = ({ rowIndex }) => {
|
||||
let { prop, children } = tabsValue.value
|
||||
if (children) prop = childTabsValue.value.prop
|
||||
tableRefObj.value[prop].vxeTableRef.setEditRow(
|
||||
tableRefObj.value[prop].vxeTableRef.getData(rowIndex)
|
||||
)
|
||||
}
|
||||
|
||||
const initEditInfoData = () => {
|
||||
const data = tableInfoOption.formattingInitData(props.editInfoData)
|
||||
if (viewDefaultField.value == 'Y') infoData.value.basics = data.infoData
|
||||
else {
|
||||
const fieldList: any[] = []
|
||||
tableInfoDefault.value = data.infoData.filter((item) => {
|
||||
if (
|
||||
tableInfoOption.disabledArr.includes(item.fieldCode) &&
|
||||
!['id', 'pid'].includes(item.fieldCode)
|
||||
) {
|
||||
return true
|
||||
}
|
||||
fieldList.push(cloneDeep(item))
|
||||
return false
|
||||
})
|
||||
infoData.value.basics = fieldList
|
||||
}
|
||||
infoData.value.index = data.indexData
|
||||
infoData.value.summaryTop = data.summaryTopData
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
tableInfoDefault.value = []
|
||||
infoData.value.basics = []
|
||||
infoData.value.index = []
|
||||
if (props.formType == 'add') {
|
||||
viewDefaultField.value = 'N'
|
||||
tableInfoDefault.value = tableInfoOption.getDefaultMysqlField().filter((item) => {
|
||||
if (item.fieldCode == 'id') {
|
||||
infoData.value.basics.push(item)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
} else {
|
||||
viewDefaultField.value = props.showDef || 'N'
|
||||
initEditInfoData()
|
||||
}
|
||||
|
||||
const { viewColumn, dicColumn, keyColumn } = tableInfoOption.infoColumn
|
||||
const clickOption = {
|
||||
viewColumn: ['controlsConfig', 'verifyConfig'],
|
||||
queryColumn: ['queryConfig'],
|
||||
summaryTopColumn: ['summarySql'],
|
||||
virtualColumn: ['virtualValue']
|
||||
}
|
||||
for (let key in clickOption) {
|
||||
clickOption[key].forEach((prop) => {
|
||||
let type = ''
|
||||
if (
|
||||
(key == 'viewColumn' && prop == 'controlsConfig') ||
|
||||
(key == 'queryColumn' && prop == 'queryConfig')
|
||||
)
|
||||
type = 'config'
|
||||
else if (key == 'viewColumn' && prop == 'verifyConfig') type = 'verify'
|
||||
else if (key == 'summaryTopColumn' && prop == 'summarySql') type = 'sqlOption'
|
||||
else if (key == 'virtualColumn' && prop == 'virtualValue') type = 'virtual'
|
||||
|
||||
tableInfoOption.infoColumn[key][prop].editRender.events = {
|
||||
click: (row, dataKey, index) => {
|
||||
if (type == 'virtual') {
|
||||
let controlType = ''
|
||||
if (row.virtualType == 'sql') controlType = 'sqlOption'
|
||||
else if (row.virtualType == 'java') controlType = 'formatting'
|
||||
if (controlType) return lowClickInputClick(row, dataKey, index, controlType)
|
||||
} else {
|
||||
return lowClickInputClick(row, dataKey, index, type)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
viewColumn.controlType.editRender.events = {
|
||||
change: (row) => {
|
||||
nextTick(() => {
|
||||
if (
|
||||
!['select', 'radio', 'checkbox', 'tree', 'cascader', 'dicTableSelect'].includes(
|
||||
row.controlType
|
||||
)
|
||||
) {
|
||||
tableRefObj.value['tab_dic'].vxeTableRef.setRow(row, {
|
||||
dictType: '',
|
||||
dictCode: '',
|
||||
dictTable: '',
|
||||
dictText: '',
|
||||
dictTableColumn: []
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
viewColumn.labelI18n.editRender.events = {
|
||||
click: (row) => {
|
||||
nextTick(() => {
|
||||
tableRefObj.value['tab_view'].vxeTableRef.setRow(row, {
|
||||
labelI18n: formattingLengStr(row.labelI18n, row.fieldName)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
dicColumn.dictType.editRender.events = {
|
||||
change: (row) => {
|
||||
nextTick(() => {
|
||||
if (row.dictType == 'table' && row.dictTable) return
|
||||
if (row.dictType == 'dict' && !row.dictTable) return
|
||||
tableRefObj.value['tab_dic'].vxeTableRef.setRow(row, {
|
||||
dictCode: '',
|
||||
dictTable: '',
|
||||
dictText: '',
|
||||
dictTableColumn: []
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
keyColumn.mainTable.editRender.events = {
|
||||
change: (row) => {
|
||||
nextTick(() => {
|
||||
if (!row.mainTable) tableRefObj.value['tab_key'].vxeTableRef.setRow(row, { mainField: '' })
|
||||
if (row.mainTable && row.mainField) {
|
||||
tableRefObj.value['tab_query'].vxeTableRef.setRow(row, { queryIsDb: 'Y' })
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
keyColumn.mainField.editRender.events = {
|
||||
change: (row) => {
|
||||
nextTick(() => {
|
||||
if (row.mainField) {
|
||||
tableRefObj.value['tab_mysql'].vxeTableRef.setRow(row, { fieldType: 'BigInt' })
|
||||
}
|
||||
if (row.mainTable && row.mainField) {
|
||||
tableRefObj.value['tab_query'].vxeTableRef.setRow(row, { queryIsDb: 'Y' })
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
dicColumn.dictTableSearch.editRender.events = {
|
||||
click: (row) => {
|
||||
nextTick(() => {
|
||||
tableRefObj.value['tab_dic'].vxeTableRef.setRow(row, {
|
||||
dictTableSearch: row.dictTableSearch || `return {\n\t\n}`
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
DictDataApi.getSimpleDictTypeList().then((dicData) => {
|
||||
const dicObj = {}
|
||||
dicData = dicData.map(({ type, name }) => {
|
||||
dicObj[type] = `${type}(${name})`
|
||||
return { label: dicObj[type], value: type }
|
||||
})
|
||||
Object.assign(dicColumn.dictCode.editRender, { dictDicData: dicData, dictDicObj: dicObj })
|
||||
})
|
||||
TableApi.getAllDbDicData().then((dbData) => {
|
||||
const dicObj = {}
|
||||
const tableDicData = {}
|
||||
const tableDicObj = {}
|
||||
const codeDicData = {}
|
||||
|
||||
const keyTableDicData: Array<object> = []
|
||||
const keyTreeTableDicData: Array<object> = []
|
||||
const keyTableDicObj = {}
|
||||
const keyFieldDicData = {}
|
||||
const keyFieldDicObj = {}
|
||||
|
||||
if (props.formType == 'edit') {
|
||||
dbData = dbData.filter((item) => item.tableName != props.editInfoData.dbForm.tableName)
|
||||
}
|
||||
const dicData = dbData.map(
|
||||
({ tableName, tableDescribe, tableId, tableType, fieldModelList }) => {
|
||||
tableDicData[tableId] = []
|
||||
codeDicData[tableId] = []
|
||||
tableDicObj[tableId] = {}
|
||||
|
||||
keyFieldDicData[tableName] = []
|
||||
keyFieldDicObj[tableName] = {}
|
||||
|
||||
tableDicData[tableId] = fieldModelList.map(({ fieldCode, fieldName }) => {
|
||||
tableDicObj[tableId][fieldCode] = `${fieldCode}(${fieldName})`
|
||||
keyFieldDicObj[tableName][fieldCode] = `${fieldCode}(${fieldName})`
|
||||
const currItem = { label: tableDicObj[tableId][fieldCode], value: fieldCode }
|
||||
codeDicData[tableId].push(currItem)
|
||||
if (fieldCode == 'id') {
|
||||
keyFieldDicData[tableName].push(currItem)
|
||||
}
|
||||
return currItem
|
||||
})
|
||||
dicObj[tableId] = `${tableName}(${tableDescribe})`
|
||||
const returnData = { label: dicObj[tableId], value: tableId }
|
||||
if (tableType == 3) {
|
||||
keyTableDicData.push({ label: dicObj[tableId], value: tableName })
|
||||
keyTableDicObj[tableName] = `${tableName}(${tableDescribe})`
|
||||
}
|
||||
if (tableType == 2) keyTreeTableDicData.push(returnData)
|
||||
if (codeDicData[tableId].length) {
|
||||
codeDicData[tableId] = [
|
||||
{
|
||||
label: '请确保绑定的字段在表内值是唯一的,否会导致数据异常(一般绑定id)',
|
||||
value: 'jeelowcode-tiop',
|
||||
disabled: true
|
||||
},
|
||||
...codeDicData[tableId]
|
||||
]
|
||||
}
|
||||
return returnData
|
||||
}
|
||||
)
|
||||
Object.assign(dicColumn.dictTable.editRender, {
|
||||
tableDicData: dicData,
|
||||
tableDicObj: dicObj,
|
||||
treeDicData: keyTreeTableDicData
|
||||
})
|
||||
Object.assign(dicColumn.dictCode.editRender, { tableDicData: codeDicData, tableDicObj })
|
||||
Object.assign(dicColumn.dictText.editRender, {
|
||||
dictDicData: [],
|
||||
dictDicObj: {},
|
||||
tableDicData,
|
||||
tableDicObj
|
||||
})
|
||||
Object.assign(dicColumn.dictTableColumn.editRender, {
|
||||
dictDicData: [],
|
||||
dictDicObj: {},
|
||||
tableDicData,
|
||||
tableDicObj
|
||||
})
|
||||
Object.assign(keyColumn.mainTable.editRender, {
|
||||
dicData: keyTableDicData,
|
||||
dicObj: keyTableDicObj
|
||||
})
|
||||
Object.assign(keyColumn.mainField.editRender, {
|
||||
dicData: keyFieldDicData,
|
||||
dicObj: keyFieldDicObj
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
defineExpose({
|
||||
infoData,
|
||||
tableInfoDefault,
|
||||
viewDefaultField,
|
||||
fieldList,
|
||||
tableRefObj,
|
||||
setTableInfoTree,
|
||||
setTabsValue,
|
||||
tableScrollIndex,
|
||||
initEditInfoData
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.low-summary-buttom-sql__popover {
|
||||
.el-popper__arrow {
|
||||
left: 20px !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<style lang="scss" scoped>
|
||||
::v-deep(.virtual-hide-row) {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
19
src/views/lowdesign/tableDesign/components/index.ts
Normal file
19
src/views/lowdesign/tableDesign/components/index.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import TableInfo from './TableInfo.vue'
|
||||
import InfoVxeTable from './InfoVxeTable.vue'
|
||||
import InfoVxeTopBtn from './InfoVxeTopBtn.vue'
|
||||
import CustomButton from './CustomButton.vue'
|
||||
import SqlEnhance from './SqlEnhance.vue'
|
||||
import JavaEnhance from './JavaEnhance.vue'
|
||||
import JsEnhanceHistory from './JsEnhanceHistory.vue';
|
||||
import AuthConfig from './AuthConfig.vue'
|
||||
import ConfigOption from './ConfigOption.vue';
|
||||
import SortOption from './SortOption.vue';
|
||||
import SearchOption from './SearchOption.vue';
|
||||
import FormattingOption from './FormattingOption.vue';
|
||||
import SqlOption from './SqlOption.vue';
|
||||
import DataOriginOption from './DataOriginOption.vue'
|
||||
export {
|
||||
TableInfo, InfoVxeTable, InfoVxeTopBtn,
|
||||
CustomButton, SqlEnhance, JavaEnhance, JsEnhanceHistory, AuthConfig,
|
||||
ConfigOption, SortOption, SearchOption, FormattingOption, SqlOption, DataOriginOption
|
||||
}
|
||||
587
src/views/lowdesign/tableDesign/designData.ts
Normal file
587
src/views/lowdesign/tableDesign/designData.ts
Normal file
@@ -0,0 +1,587 @@
|
||||
import { verifyDbName } from '@/api/design/table';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
|
||||
// 数据字典
|
||||
export const dicObj = {
|
||||
dic_whether: [
|
||||
{ label: '是', value: 'Y' },
|
||||
{ label: '否', value: 'N' },
|
||||
],
|
||||
tableType: [
|
||||
{ label: '单表', value: 1 },
|
||||
{ label: '树表', value: 2 },
|
||||
{ label: '主表', value: 3 },
|
||||
{ label: '附表', value: 4 },
|
||||
],
|
||||
tableClassify: [
|
||||
{ label: '业务表', value: 1 },
|
||||
{ label: '表视图', value: 2 },
|
||||
],
|
||||
is_db_sync: [
|
||||
{ label: '已同步', value: 'Y' },
|
||||
{ label: '未同步', value: 'N' },
|
||||
],
|
||||
tableIdType: [
|
||||
{ label: 'ID_WORKER(分布式自增)', value: 'NATIVE' },
|
||||
],
|
||||
themeTemplate: [
|
||||
{ label: '默认主题', value: 'normal' },
|
||||
{ label: 'ERP主题', value: 'erp' },
|
||||
{ label: '内嵌子表主题', value: 'innerTable' },
|
||||
],
|
||||
searchStyle: [
|
||||
{ label: '表格顶部', value: 'default' },
|
||||
{ label: '表格内嵌', value: 'inline' },
|
||||
],
|
||||
treeStyle: [
|
||||
{ label: '默认样式', value: 'default' },
|
||||
{ label: '左树右表', value: 'around' },
|
||||
],
|
||||
treeMode: [
|
||||
{ label: '默认', value: 'default' },
|
||||
{ label: '懒加载', value: 'treeLazy' },
|
||||
],
|
||||
tableSelect: [
|
||||
{ label: '多选', value: 'multiple' },
|
||||
{ label: '单选', value: 'radio' },
|
||||
{ label: '禁用', value: 'disabled' },
|
||||
],
|
||||
subTableMapping: [
|
||||
{ label: '一对多', value: 'many' },
|
||||
{ label: '一对一', value: 'one' },
|
||||
],
|
||||
operateMenuStyle: [
|
||||
{ label: '横向排列', value: 'normal' },
|
||||
{ label: '更多下拉', value: 'more' },
|
||||
],
|
||||
singleStyle: [
|
||||
{ label: '默认表格', value: 'default' },
|
||||
{ label: '可展开表格', value: 'expand' },
|
||||
{ label: '卡片表格', value: 'card' },
|
||||
],
|
||||
singleCardSpan: [
|
||||
{ label: '一列', value: 1 },
|
||||
{ label: '二列', value: 2 },
|
||||
{ label: '三列', value: 3 },
|
||||
{ label: '四列', value: 4 },
|
||||
{ label: '六列', value: 6 },
|
||||
{ label: '八列', value: 8 },
|
||||
{ label: '十二列', value: 12 },
|
||||
],
|
||||
expandMode: [
|
||||
{ label: '默认', value: 'default' },
|
||||
{ label: '手风琴', value: 'accordion' }
|
||||
],
|
||||
formStyle: [
|
||||
{ label: '一列', value: 1 },
|
||||
{ label: '二列', value: 2 },
|
||||
{ label: '三列', value: 3 },
|
||||
{ label: '四列', value: 4 },
|
||||
],
|
||||
dataConfig: [
|
||||
{ label: '分页', value: 'page' },
|
||||
{ label: '初始数据请求', value: 'initDataReq' },
|
||||
{ label: '数据权限过滤', value: 'dataAuth' },
|
||||
],
|
||||
dataConfigSelect: [
|
||||
{ label: '需登录', value: 'authFalse', desc: '访问该表接口:需登录' },
|
||||
{ label: '需登录、鉴权', value: 'authTrue', desc: '访问该表接口:需登录并且需要配置菜单权限' },
|
||||
{ label: '不登录可查询、新增数据', value: 'authOpen', desc: '访问该表查询、新增接口:不需要登录' },
|
||||
],
|
||||
tableConfig: [
|
||||
{ label: '固定表格高度', value: 'height' },
|
||||
{ label: '表格头部操作', value: 'header' },
|
||||
{ label: '操作列', value: 'menu' },
|
||||
{ label: '序号列', value: 'index' },
|
||||
{ label: '纵向边框', value: 'border' },
|
||||
{ label: '斑马纹样式', value: 'stripe' },
|
||||
{ label: '表格滚动条固定在底部', value: 'rollBottom' },
|
||||
],
|
||||
basicFunction: [
|
||||
{ label: '新增', value: 'addBtn' },
|
||||
{ label: '编辑', value: 'editBtn' },
|
||||
{ label: '查看', value: 'viewBtn' },
|
||||
{ label: '删除', value: 'delBtn' },
|
||||
{ label: '批量删除', value: 'batchDelBtn' },
|
||||
{ label: '导入', value: 'importBtn' },
|
||||
{ label: '导出', value: 'exportBtn' },
|
||||
],
|
||||
fieldType: [
|
||||
{ label: '字符串 String', value: 'String' },
|
||||
{ label: '整数 Integer', value: 'Integer' },
|
||||
{ label: '大整数 BigInt', value: 'BigInt' },
|
||||
{ label: '小数 BigDecimal', value: 'BigDecimal' },
|
||||
{ label: '日期 Date', value: 'Date' },
|
||||
{ label: '时间 Time', value: 'Time' },
|
||||
{ label: '日期时间 DateTime', value: 'DateTime' },
|
||||
{ label: '文本 Text', value: 'Text' },
|
||||
{ label: '大文本 LongText', value: 'LongText' },
|
||||
{ label: '二进制 Blob', value: 'Blob' },
|
||||
],
|
||||
cellWidthType: [
|
||||
{ label: '固定', value: 'fixed' },
|
||||
{ label: '最小', value: 'min' },
|
||||
],
|
||||
controlType: [
|
||||
{ label: '文本输入框', value: 'input' },
|
||||
{ label: '数字输入框', value: 'number' },
|
||||
{ label: '下拉选择框', value: 'select' },
|
||||
{ label: '单选框', value: 'radio' },
|
||||
{ label: '多选框', value: 'checkbox' },
|
||||
{ label: '日期', value: 'date' },
|
||||
{ label: '时间', value: 'time' },
|
||||
{ label: '开关', value: 'switch' },
|
||||
{ label: '文件', value: 'file' },
|
||||
{ label: '图片', value: 'image' },
|
||||
{ label: '树型选择框', value: 'tree' },
|
||||
{ label: 'Cascader级联选择框', value: 'cascader' },
|
||||
{ label: '表格选择框', value: 'dicTableSelect' },
|
||||
{ label: '用户选择框', value: 'userSelect' },
|
||||
{ label: '部门选择框', value: 'deptSelect' },
|
||||
{ label: '地区选择框', value: 'regionSelect' },
|
||||
{ label: '代码编辑器', value: 'monacoEditor' },
|
||||
{ label: '富文本', value: 'ueditor' },
|
||||
{ label: 'MarkDown编辑器', value: 'markDown' },
|
||||
{ label: '自定义控件', value: 'customControl' },
|
||||
],
|
||||
dictType: [
|
||||
{ label: '系统字典', value: 'dict' },
|
||||
{ label: '表格数据', value: 'table' },
|
||||
],
|
||||
dictTextFormatter: [
|
||||
{ label: '字典Code-字典Text', value: '{dicCode}-{dicText}' },
|
||||
{ label: '字典Text-字典Code', value: '{dicText}-{dicCode}' },
|
||||
{ label: '字典Text(字典Code)', value: '{dicText}({dicCode})' },
|
||||
{ label: '字典Text[字典Code]', value: '{dicText}[{dicCode}]' },
|
||||
{ label: '其他格式请自行输入', value: 'custom-tip-1', disabled: true },
|
||||
{ label: '例如:code:{dicCode},text:{dicText}', value: 'custom-tip-2', disabled: true },
|
||||
{ label: '效果:code:1001,text:小明', value: 'custom-tip-3', disabled: true },
|
||||
|
||||
],
|
||||
queryMode: [
|
||||
{ label: '精确查询(=)', value: 'EQ' },
|
||||
{ label: '模糊查询(like)', value: 'LIKE' },
|
||||
{ label: '范围查询(仅适用于控件类型为:日期、时间、数字)', value: 'RANGE' },
|
||||
{ label: '包含查询(in)', value: 'IN' },
|
||||
{ label: '不等于(!=)', value: 'NE' },
|
||||
],
|
||||
importDuplicateType: [
|
||||
{ label: '更新数据', value: 'update' },
|
||||
{ label: '先删后增', value: 'delete_update' },
|
||||
],
|
||||
indexType: [
|
||||
{ label: 'normal', value: 'normal' },
|
||||
{ label: 'unique', value: 'unique' },
|
||||
],
|
||||
summaryType: [
|
||||
{ label: '合计', value: 'sum' },
|
||||
{ label: '平均', value: 'avg' },
|
||||
{ label: '最大值', value: 'max' },
|
||||
{ label: '最小值', value: 'min' },
|
||||
{ label: '自定义SQL', value: 'custom' },
|
||||
],
|
||||
orderByType: [
|
||||
{ label: '升序', value: 'asc' },
|
||||
{ label: '降序', value: 'desc' },
|
||||
],
|
||||
summaryControl: [
|
||||
{ label: '文本', value: 'text' },
|
||||
{ label: '卡片', value: 'card' },
|
||||
{ label: '折线图', value: 'line' },
|
||||
{ label: '柱状图', value: 'bar' },
|
||||
{ label: '饼图', value: 'pie' },
|
||||
],
|
||||
virtualType: [
|
||||
{ label: 'SQL函数处理', value: 'fun' },
|
||||
{ label: 'SQL处理', value: 'sql' },
|
||||
{ label: 'JAVA处理', value: 'java' },
|
||||
],
|
||||
indexFieldDefault: [] as any[]
|
||||
}
|
||||
export const getDicObj = (key) => {
|
||||
const obj = {}
|
||||
if (dicObj[key]) dicObj[key].forEach(item => obj[item.value] = item.label)
|
||||
return obj
|
||||
}
|
||||
|
||||
const control_tableType = (val, form) => {
|
||||
dicObj.dataConfig = dicObj.dataConfig.map(item => {
|
||||
if (item.value == 'page') {
|
||||
item['disabled'] = val == 2
|
||||
}
|
||||
return item
|
||||
})
|
||||
dicObj.tableSelect = dicObj.tableSelect.map(item => {
|
||||
if (item.value == 'radio') {
|
||||
item['disabled'] = val == 2
|
||||
}
|
||||
return item
|
||||
})
|
||||
dicObj.basicFunction = dicObj.basicFunction.filter(item => {
|
||||
if (item.value == 'addChild') return false
|
||||
return true
|
||||
})
|
||||
if (val == 2) {
|
||||
dicObj.basicFunction.splice(1, 0, { label: '新增子级', value: 'addChild' })
|
||||
dicObj.basicFunction = [...dicObj.basicFunction]
|
||||
}
|
||||
if (val != 3 && form.themeTemplate != 'normal') form.themeTemplate = 'normal'
|
||||
return {
|
||||
themeTemplate: { disabled: val != 3 },
|
||||
treeLabelField: { display: val == 2 },
|
||||
subTableTitle: { display: val == 4 },
|
||||
subTableListStr: { display: val == 3 && form.subTableListStr },
|
||||
isDesForm: { display: val != 4 },
|
||||
dataConfig: { dicData: dicObj.dataConfig },
|
||||
tableSelect: { dicData: dicObj.tableSelect },
|
||||
basicFunction: { dicData: dicObj.basicFunction },
|
||||
}
|
||||
}
|
||||
|
||||
const control_tableClassify = (val) => {
|
||||
return {
|
||||
dataOrigin: { display: val == 2 },
|
||||
originButton: { display: val == 2 },
|
||||
}
|
||||
}
|
||||
|
||||
const control_isDesForm = (val) => {
|
||||
return {
|
||||
desformWebId: { display: val == 'Y' }
|
||||
}
|
||||
}
|
||||
const rules_required = (label, type = '') => [{ required: true, message: `${['select'].includes(type) ? '请选择' : '请输入'} ${label}`, trigger: "blur" }]
|
||||
const tableName_required = async (rule, value, callback) => {
|
||||
const regExp = /^[a-z][a-z0-9_]*$/
|
||||
if (value === '') callback(new Error('请输入 表名'));
|
||||
else if (!regExp.test(value)) callback(new Error('表名只能使用小写字母、数字、下划线,并以字母开头'));
|
||||
else {
|
||||
const bool = await verifyDbName(value)
|
||||
if (bool) callback(new Error('表名已存在,请修改'));
|
||||
else callback()
|
||||
}
|
||||
};
|
||||
|
||||
const dataOriginObj = {}
|
||||
const dataOrigin_dicFormatter = (data) => {
|
||||
const sysList: any[] = []
|
||||
const dbList: any[] = []
|
||||
data.forEach(item => {
|
||||
const row = {
|
||||
label: `${item.tableName}(${item.tableDescribe})`,
|
||||
tableText: item.tableDescribe,
|
||||
tableName: item.tableName,
|
||||
value: item.tableId,
|
||||
type: 'table',
|
||||
fieldList: item.fieldModelList.map(child => {
|
||||
let label = child.fieldCode
|
||||
if (child.fieldName) label = `${label}(${child.fieldName})`
|
||||
return { label, value: child.fieldCode, tableName: item.tableName, type: 'field', fieldType: child.fieldType }
|
||||
})
|
||||
}
|
||||
if (item.tableId == item.tableName) sysList.push(row)
|
||||
else if (item.tableClassify !== 2) dbList.push(row)
|
||||
dataOriginObj[item.tableId] = row
|
||||
})
|
||||
return [
|
||||
{ label: '系统表', value: 'sys', children: sysList },
|
||||
{ label: '表单开发', value: 'dbForm', children: dbList },
|
||||
]
|
||||
}
|
||||
|
||||
//表格配置
|
||||
const tableOptionColumn = {
|
||||
lowSelectRadio: { label: '', display: false, width: 50, overHidden: false, fixed: true, showColumn: false },
|
||||
id: { label: '表ID', display: false, search: true, width: 100, overHidden: true, type: 'number' },
|
||||
tableName: { label: '表名', display: false, search: true, minWidth: 140 },
|
||||
tableDescribe: { label: '表描述', display: false, search: true, minWidth: 180, overHidden: true },
|
||||
tableType: { label: '表类型', display: false, value: 1, search: true, type: 'select', dicData: dicObj.tableType, width: 70 },
|
||||
tableClassify: { label: '表分类', display: false, value: 1, search: true, type: 'select', dicData: dicObj.tableClassify, width: 70 },
|
||||
isDbSync: { label: '同步状态', display: false, type: 'select', search: true, dicData: dicObj.is_db_sync, width: 85 },
|
||||
optionCondition: { label: '配置情况', display: false, width: 180, className: 'option-condition-td' },
|
||||
custom_form: { label: '', labelWidth: 0, span: 24, hide: true, showColumn: false },
|
||||
custom_info: { label: '', labelWidth: 0, span: 24, hide: true, showColumn: false }
|
||||
}
|
||||
// 表单配置
|
||||
const customFormColumn = {
|
||||
tableName: { label: '表名', rules: [{ validator: tableName_required, trigger: 'blur', required: true }] },
|
||||
tableDescribe: { label: '表描述', rules: rules_required('表描述') },
|
||||
// tableIdType: { label: '主键类型', type: 'select', value: 'NATIVE', dicData: dicObj.tableIdType, clearable: false },
|
||||
groupDbformId: { label: '分组类型', type: 'tree', value: '', dicData: [], filterable: true, defaultExpandAll: true, props: { label: 'name', value: 'id' } },
|
||||
tableType: { label: '表类型', type: 'select', value: 1, dicData: dicObj.tableType, control: control_tableType, clearable: false },
|
||||
subTableTitle: { label: '附表-Tab标题', display: false },
|
||||
tableClassify: { label: '表分类', type: 'select', value: 1, dicData: dicObj.tableClassify, control: control_tableClassify, clearable: false },
|
||||
dataOrigin: { label: '数据来源', type: 'tree', value: '', display: false, span: 12, dataType: 'string', multiple: true, filterable: true, parent: false, defaultExpandAll: true, dicUrl: '/jeelowcode/dbform/get/all-table', dicQuery: { systemFlag: 'Y' }, dicFormatter: dataOrigin_dicFormatter },
|
||||
originButton: { label: '', labelWidth: 0, display: false },
|
||||
themeTemplate: { label: '主题模板', type: 'select', disabled: true, value: 'normal', dicData: dicObj.themeTemplate, clearable: false },
|
||||
treeLabelField: { label: '树表回显字段', display: false, value: '', type: 'select', dicData: [], clearable: false, rules: rules_required('树表回显字段', 'select'), },
|
||||
treeStyle: { label: '树表样式', display: false, value: 'default', type: 'select', dicData: dicObj.treeStyle, clearable: false },
|
||||
treeMode: { label: '树表模式', display: false, value: 'default', type: 'select', dicData: dicObj.treeMode, clearable: false },
|
||||
tableSelect: { label: '表格选择', type: 'select', value: 'multiple', display: false, dicData: dicObj.tableSelect },
|
||||
operateMenuStyle: { label: '操作栏样式', type: 'select', value: 'more', display: false, dicData: dicObj.operateMenuStyle, clearable: false },
|
||||
maxMenuNum: { label: '操作栏最大横排数', type: 'number', value: 1, display: false },
|
||||
tableStyle: { label: '单表样式', display: false },
|
||||
formStyle: { label: '表单风格', type: 'select', value: 2, dicData: dicObj.formStyle, clearable: false },
|
||||
isDesForm: { label: '默认表单', type: 'select', value: 'N', control: control_isDesForm, clearable: false, dicData: [{ label: '是', value: 'N' }, { label: '否', value: 'Y' }] },
|
||||
desformWebId: { label: '自定义表单', display: false, type: 'select', filterable: true, rules: rules_required('自定义表单', 'select'), dicUrl: '/jeelowcode/desform/page', dicMethod: 'post', virtualize: true, props: { label: 'desformName', value: 'id' }, dicFormatter: (data) => data.records },
|
||||
subTableListStr: { label: '关联的附表', display: false, span: 24, disabled: true, placeholder: '关联的附表需通过[附表的外键配置]绑定当前主表,当前字段仅作显示用途。' },
|
||||
dataConfig: { label: '数据配置', type: 'checkbox', span: 24, dicData: dicObj.dataConfig, dataType: 'string', value: ['page', 'initDataReq', 'authFalse'] },
|
||||
tableConfig: { label: '表格配置', type: 'checkbox', span: 24, dicData: dicObj.tableConfig, dataType: 'string', value: ['height', 'header', 'menu', 'index', 'border', 'rollBottom'] },
|
||||
basicFunction: { label: '基础功能', type: 'checkbox', span: 18, dicData: dicObj.basicFunction, dataType: 'string', value: 'addBtn,editBtn,viewBtn,delBtn,batchDelBtn,importBtn,exportBtn' },
|
||||
basicConfig: { label: '', labelWidth: 0, span: 6, params: { otherParams: { type: 'default', width: '620px', tipKeyList: ['tableOption'] }, dialogParams: { closeOnClickModal: true, closeOnPressEscape: true } } },
|
||||
importDuplicateType: { label: '导入数据重复处理类型', display: false, dicData: dicObj.importDuplicateType, value: 'update' }
|
||||
}
|
||||
|
||||
const infoColumn = {
|
||||
mysqlColumn: {
|
||||
fieldCode: { title: '字段编码', minWidth: 120, editRender: { name: 'LowInput', verifyEdit: true } },
|
||||
fieldName: { title: '字段名称', minWidth: 120, editRender: { name: 'LowInput' } },
|
||||
fieldType: { title: '字段类型', minWidth: 100, editRender: { name: 'LowSelect', verifyEdit: true, dicData: dicObj.fieldType, dicObj: getDicObj('fieldType') } },
|
||||
fieldDefaultVal: { title: '默认值', minWidth: 120, editRender: { name: 'LowInput', verifyEdit: true } },
|
||||
fieldLen: { title: '字段长度', minWidth: 80, editRender: { name: 'LowNumber', verifyEdit: true } },
|
||||
fieldPointLen: { title: '小数位数', minWidth: 80, editRender: { name: 'LowNumber', verifyEdit: true } },
|
||||
fieldRemark: { title: '备注', minWidth: 120, editRender: { name: 'LowInput' } },
|
||||
isPrimaryKey: { title: '是否主键', width: 54, align: "center", editRender: { name: 'LowCheckbox', verifyEdit: true } },
|
||||
isNull: { title: '是否为空', width: 54, align: "center", editRender: { name: 'LowCheckbox', verifyEdit: true } },
|
||||
isDb: { title: '同步数据库', width: 64, align: "center", editRender: { name: 'LowCheckbox', verifyEdit: true } },
|
||||
},
|
||||
viewColumn: {
|
||||
fieldCode: { title: '字段编码', width: 140 },
|
||||
fieldName: { title: '字段名称', width: 140 },
|
||||
labelI18n: { title: '国际化配置', width: 140, editRender: { name: 'LowMonacoEditorInput', events: {} } },
|
||||
isShowList: { title: '列表显示', width: 54, align: "center", editRender: { name: 'LowCheckbox' }, titleSuffix: { content: '控制表格列是否显示' } },
|
||||
isDbSelect: { title: '字段显示', width: 54, align: "center", editRender: { name: 'LowCheckbox', verifyEdit: true, }, titleSuffix: { content: '控制接口是否返回该字段' } },
|
||||
isShowForm: { title: '表单显示', width: 54, align: "center", editRender: { name: 'LowCheckbox', verifyEdit: true, } },
|
||||
isRequired: { title: '是否必填', width: 54, align: "center", editRender: { name: 'LowCheckbox', verifyEdit: true, } },
|
||||
cellWidthType: { title: '列宽类型', width: 100, align: "center", editRender: { name: 'LowSelect', dicData: dicObj.cellWidthType, dicObj: getDicObj('cellWidthType') } },
|
||||
cellWidth: { title: '列宽', width: 80, align: "center", editRender: { name: 'LowInput', placeholder: '120' } },
|
||||
controlType: { title: '控件类型', width: 140, editRender: { name: 'LowSelect', verifyEdit: true, filterable: true, dicData: dicObj.controlType, dicObj: getDicObj('controlType'), events: {} } },
|
||||
controlsConfig: { title: '控件配置', minWidth: 100, editRender: { name: 'LowClickInput', events: {} } },
|
||||
verifyConfig: { title: '校验配置', minWidth: 100, editRender: { name: 'LowClickInput', events: {} } },
|
||||
isShowSort: { title: '是否排序', width: 54, align: "center", editRender: { name: 'LowCheckbox' } },
|
||||
isShowColumn: { title: '是否可控', width: 54, align: "center", editRender: { name: 'LowCheckbox' } },
|
||||
},
|
||||
queryColumn: {
|
||||
fieldCode: { title: '字段编码', width: 140 },
|
||||
fieldName: { title: '字段名称', width: 140 },
|
||||
isShowList: { title: '列表显示', width: 54, align: "center", cellRender: { name: 'LowCheckbox' } },
|
||||
queryIsDb: { title: '接口查询', width: 54, align: "center", editRender: { name: 'LowCheckbox' } },
|
||||
filterAuth: { title: '权限过滤', width: 54, align: "center", editRender: { name: 'LowCheckbox' } },
|
||||
queryIsWeb: { title: '查询控件', width: 54, align: "center", editRender: { name: 'LowCheckbox' } },
|
||||
queryDefaultVal: { title: '查询控件默认值', width: 160, editRender: { name: 'LowInput' } },
|
||||
queryMode: { title: '查询模式', width: 130, editRender: { name: 'LowSelect', dicData: dicObj.queryMode, dicObj: getDicObj('queryMode') } },
|
||||
queryConfig: { title: '查询配置', minWidth: 100, editRender: { name: 'LowClickInput', events: {} } },
|
||||
},
|
||||
dicColumn: {
|
||||
fieldCode: { title: '字段编码', width: 140 },
|
||||
fieldName: { title: '字段名称', width: 140 },
|
||||
dictType: { title: '字典类型', width: 140, editRender: { name: 'LowSelect', verifyEdit: true, events: {}, dicData: dicObj.dictType, dicObj: getDicObj('dictType') } },
|
||||
dictCode: { title: '字典Code', width: 180, editRender: { name: 'LowSelect', verifyEdit: true, filterable: true, typeKey: 'dictType', dicData: [] } },
|
||||
dictTable: { title: '字典Table', width: 230, editRender: { name: 'LowSelect', verifyEdit: true, filterable: true, typeKey: 'dictType', dicData: [] } },
|
||||
dictText: { title: '字典Text', width: 180, editRender: { name: 'LowSelect', verifyEdit: true, filterable: true, typeKey: 'dictType', dicData: [] } },
|
||||
dictTextFormatter: { title: '字典Text格式化', width: 180, editRender: { name: 'LowSelect', verifyEdit: true, filterable: true, allowCreate: true, dicData: dicObj.dictTextFormatter, dicObj: getDicObj('dictTextFormatter') } },
|
||||
dictTableColumn: { title: '字典显示列', editRender: { name: 'LowSelect', verifyEdit: true, multiple: true, filterable: true, typeKey: 'dictType', dicData: [] } },
|
||||
dictTableSearch: { title: '额外查询条件', editRender: { name: 'LowMonacoEditorInput', verifyEdit: true, events: {} } },
|
||||
},
|
||||
expColumn: {
|
||||
fieldCode: { title: '字段编码', width: 140 },
|
||||
fieldName: { title: '字段名称', width: 140 },
|
||||
isShowList: { title: '列表显示', width: 74, align: "center", cellRender: { name: 'LowCheckbox' } },
|
||||
isExport: { title: '可导出', width: 70, align: "center", editRender: { name: 'LowCheckbox' } },
|
||||
isImport: { title: '可导入', width: 70, align: "center", editRender: { name: 'LowCheckbox' } },
|
||||
isImportDuplicate: { title: '导入去重', width: 80, align: "center", editRender: { name: 'LowCheckbox' }, titleSuffix: { content: '开启后导入将校验表内数据中对应的字段是否重复,勾选多个字段以(AND)判断是否重复' } },
|
||||
importExampleTxt: { title: '导入模板示例文本', minWidth: 120, editRender: { name: 'LowInput' } },
|
||||
},
|
||||
keyColumn: {
|
||||
fieldCode: { title: '字段编码', width: 140 },
|
||||
fieldName: { title: '字段名称', width: 140 },
|
||||
mainTable: { title: '外键-主表名', width: 230, editRender: { name: 'LowSelect', verifyEdit: true, filterable: true, dicData: [], events: {} } },
|
||||
mainField: { title: '外键-主键字段', editRender: { name: 'LowSelect', verifyEdit: true, dicData: [], events: {} } },
|
||||
},
|
||||
summaryBottomColumn: {
|
||||
fieldCode: { title: '字段编码', width: 140 },
|
||||
fieldName: { title: '字段名称', width: 140 },
|
||||
summaryShow: { title: '显示统计', width: 80, align: "center", editRender: { name: 'LowCheckbox' } },
|
||||
summaryLabel: { title: '统计名称', width: 160, editRender: { name: 'LowInput' } },
|
||||
summarySql: { title: '统计执行sql', minWidth: 120, editRender: { name: 'LowSummaryBottomSql', dicData: dicObj.summaryType, dicObj: getDicObj('summaryType') } },
|
||||
},
|
||||
summaryTopColumn: {
|
||||
summaryShow: { title: '显示统计', width: 80, align: "center", editRender: { name: 'LowCheckbox' } },
|
||||
summarySpan: { title: '统计栅格', width: 80, align: 'center', editRender: { name: 'LowNumber', params: { min: 1, max: 24 } } },
|
||||
summaryLabel: { title: '统计名称', width: 160, editRender: { name: 'LowInput' } },
|
||||
summaryControl: { title: '统计控件类型', width: 120, align: 'center', cellRender: { name: 'LowSelect', dicData: dicObj.summaryControl, dicObj: getDicObj('summaryControl') } },
|
||||
summarySql: { title: '统计SQL配置', minWidth: 100, editRender: { name: 'LowClickInput', events: {} } },
|
||||
},
|
||||
virtualColumn: {
|
||||
fieldCode: { title: '字段编码', width: 140 },
|
||||
fieldName: { title: '字段名称', width: 140 },
|
||||
virtualType: { title: '处理方式', width: 140, align: 'center', editRender: { name: 'LowSelect', dicData: dicObj.virtualType, dicObj: getDicObj('virtualType') } },
|
||||
virtualValue: { title: '处理配置', minWidth: 140, editRender: { name: 'virtualInput', events: {} } },
|
||||
},
|
||||
indexColumn: {
|
||||
indexName: { title: '索引名称', width: 180, editRender: { name: 'LowInput' } },
|
||||
indexType: { title: '索引类型', width: 180, editRender: { name: 'LowSelect', dicData: dicObj.indexType } },
|
||||
indexFieldCodeList: { title: '索引字段列表', editRender: { name: 'LowSelect', multiple: true, filterable: true, dicData: [] } }
|
||||
},
|
||||
}
|
||||
|
||||
const infoApiKey = {}
|
||||
const indexApiKey = Object.keys(infoColumn.indexColumn)
|
||||
const apiKey = { mysqlColumn: 'fieldList', viewColumn: 'webList', queryColumn: 'queryList', dicColumn: 'dictList', expColumn: 'exportList', keyColumn: 'foreignkeyList', summaryBottomColumn: 'summaryList' }
|
||||
for (const key in infoColumn) {
|
||||
if (apiKey[key]) {
|
||||
const keys = Object.keys(infoColumn[key])
|
||||
if (key == 'mysqlColumn') keys.push('sortNum')
|
||||
infoApiKey[apiKey[key]] = keys
|
||||
}
|
||||
}
|
||||
|
||||
const disabledArr = ['id', 'tenant_id', 'create_user', 'create_time', 'create_dept', 'update_user', 'update_time', 'pid', 'is_deleted']
|
||||
|
||||
//默认值
|
||||
const infoDefaultData = {
|
||||
basics: {
|
||||
fieldCode: '', fieldName: '', fieldLen: 128, fieldPointLen: 0, fieldDefaultVal: '', fieldType: 'String', fieldRemark: '', isPrimaryKey: 'N', isNull: 'Y', isDb: 'Y',
|
||||
labelI18n: '', isShowList: 'Y', isDbSelect: 'Y', isShowForm: 'Y', isRequired: 'N', cellWidthType: 'min', controlType: 'input', isShowSort: 'N', isShowColumn: 'Y',
|
||||
queryIsDb: 'N', queryIsWeb: 'N', queryMode: 'EQ', queryConfig: '',
|
||||
dictType: '', dictCode: '', dictTable: '', dictText: '', dictTextFormatter: '', dictTableColumn: [], dictTableSearch: '',
|
||||
isImport: 'Y', isExport: 'Y', importRemoveRepet: 'N', importExampleTxt: '',
|
||||
mainTable: '', mainField: '',
|
||||
summaryShow: 'N', summaryLabel: '', summarySql: '', summaryJson: { sqlType: '', sqlValue: '' },
|
||||
virtualType: '', virtual_java_str: '', virtual_sql_str: '', virtual_fun_str: '', virtualValue: ''
|
||||
},
|
||||
index: { indexName: '', indexFieldCodeList: [], indexType: 'normal' },
|
||||
summaryTop: { summaryShow: 'Y', summarySpan: 8, summaryControl: 'text', summaryLabel: '', summarySql: '', }
|
||||
}
|
||||
|
||||
//获取默认字段配置
|
||||
const getDefaultMysqlField = (type?: string) => {
|
||||
let fieldObj = {}
|
||||
if (type == 'tree') {
|
||||
fieldObj = {
|
||||
pid: { fieldName: '父级节点', fieldType: 'BigInt', fieldDefaultVal: 0, isShowList: 'N', isShowForm: 'Y', only: true, is_tree_field: true, queryIsDb: 'Y' },
|
||||
}
|
||||
} else {
|
||||
fieldObj = {
|
||||
id: { fieldName: '主键', fieldType: 'BigInt', isPrimaryKey: 'Y', isNull: 'N', isShowList: 'N', isShowForm: 'N', isImport: 'N', isExport: 'N', queryIsDb: 'Y', isShowColumn: 'N', dictType: '', only: true },
|
||||
tenant_id: { fieldName: '租户编号', fieldType: 'BigInt', isShowList: 'N', isShowForm: 'N', isImport: 'N', isExport: 'N', isShowColumn: 'N', dictType: '', only: true },
|
||||
create_user: { fieldName: '创建人', fieldType: 'BigInt', isShowList: 'N', isShowForm: 'N', controlType: 'userSelect', isImport: 'N', isExport: 'N', queryIsDb: 'Y', isShowColumn: 'N', dictType: '', only: true },
|
||||
create_time: { fieldName: '创建时间', fieldType: 'DateTime', isShowList: 'N', isShowForm: 'N', controlType: 'date', isImport: 'N', isExport: 'N', queryIsDb: 'Y', isShowColumn: 'N', dictType: '', only: true },
|
||||
create_dept: { fieldName: '创建部门id', fieldType: 'BigInt', isShowList: 'N', isShowForm: 'N', controlType: 'deptSelect', isImport: 'N', isExport: 'N', queryIsDb: 'Y', isShowColumn: 'N', dictType: '', only: true },
|
||||
update_user: { fieldName: '更新人', fieldType: 'BigInt', isShowList: 'N', isShowForm: 'N', controlType: 'userSelect', isImport: 'N', isExport: 'N', queryIsDb: 'Y', isShowColumn: 'N', dictType: '', only: true },
|
||||
update_time: { fieldName: '更新时间', fieldType: 'DateTime', isShowList: 'N', isShowForm: 'N', controlType: 'date', isImport: 'N', isExport: 'N', queryIsDb: 'Y', isShowColumn: 'N', dictType: '', only: true },
|
||||
is_deleted: { fieldName: '是否删除', fieldType: 'Integer', fieldLen: 2, fieldDefaultVal: 0, isShowList: 'N', isShowForm: 'N', isShowColumn: 'N', isImport: 'N', isExport: 'N', queryIsDb: 'Y', dictType: '', only: true },
|
||||
}
|
||||
}
|
||||
const defaultFieldData: any[] = []
|
||||
for (const key in fieldObj) defaultFieldData.push({ ...cloneDeep(infoDefaultData.basics), ...fieldObj[key], fieldCode: key, })
|
||||
return defaultFieldData
|
||||
}
|
||||
|
||||
getDefaultMysqlField().forEach(item => {
|
||||
if (item.fieldCode == 'id') return
|
||||
dicObj.indexFieldDefault.push({ label: `${item.fieldCode}(${item.fieldName})`, value: item.fieldCode, type: item.fieldType })
|
||||
})
|
||||
|
||||
//格式化接口初始数据
|
||||
const formattingInitData = (editInfoData) => {
|
||||
const optionObj = {}
|
||||
const infoData = [] as any
|
||||
const indexData = [] as any
|
||||
const summaryTopData = [] as any
|
||||
for (const apiKey in infoApiKey) {
|
||||
let key = apiKey
|
||||
if (apiKey == 'summaryList') key = 'summaryBottomList'
|
||||
optionObj[key] = {}
|
||||
editInfoData[key]?.forEach(item => optionObj[key][item.fieldCode] = item)
|
||||
}
|
||||
|
||||
editInfoData.fieldList.forEach(fieldItem => {
|
||||
const fieldCode = fieldItem.fieldCode
|
||||
const infoItem: any = {}
|
||||
for (const apiKey in infoApiKey) {
|
||||
let dataKey = apiKey
|
||||
if (apiKey == 'summaryList') dataKey = 'summaryBottomList'
|
||||
if (!optionObj[dataKey]) continue
|
||||
|
||||
const editItem = optionObj[dataKey][fieldCode] || cloneDeep(infoDefaultData)
|
||||
infoItem[`${apiKey}_id`] = editItem.id
|
||||
|
||||
if (apiKey == 'summaryList') infoItem.summaryJson = editItem.summaryJson ? JSON.parse(editItem.summaryJson) : cloneDeep(infoDefaultData.basics.summaryJson)
|
||||
if (apiKey == 'webList') {
|
||||
if (editItem.formatConfig) {
|
||||
const formatConfig = JSON.parse(editItem.formatConfig)
|
||||
infoItem.virtualType = formatConfig.formatType
|
||||
infoItem.virtual_sql_str = JSON.stringify(formatConfig.formatJson.sql || {})
|
||||
infoItem.virtual_java_str = JSON.stringify(formatConfig.formatJson.java || {})
|
||||
infoItem.virtual_fun_str = formatConfig.formatJson.fun || ''
|
||||
infoItem.virtualValue = ''
|
||||
} else {
|
||||
infoItem.virtualType = ''
|
||||
infoItem.virtual_sql_str = ''
|
||||
infoItem.virtual_java_str = ''
|
||||
infoItem.virtual_fun_str = ''
|
||||
infoItem.virtualValue = ''
|
||||
}
|
||||
}
|
||||
for (const i in infoApiKey[apiKey]) {
|
||||
const key = infoApiKey[apiKey][i]
|
||||
if (apiKey != 'fieldList' && ['fieldCode', 'fieldName'].includes(key)) continue
|
||||
if (['queryList', 'exportList'].includes(apiKey) && key == 'isShowList') continue
|
||||
infoItem[key] = editItem[key]
|
||||
|
||||
if (key == 'dictTableColumn' && !(infoItem[key] instanceof Array)) {
|
||||
infoItem[key] = infoItem[key] ? infoItem[key].split(',') : []
|
||||
}
|
||||
if (key == 'fieldCode' && disabledArr.includes(infoItem[key])) {
|
||||
infoItem.only = true
|
||||
if (infoItem[key] === 'pid' && editInfoData.dbForm.tableType !== 2) {
|
||||
delete infoItem.only
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
infoData.push(infoItem)
|
||||
})
|
||||
|
||||
if (editInfoData.indexList) {
|
||||
const indexKeyList = Object.keys(infoColumn.indexColumn)
|
||||
editInfoData.indexList.forEach(item => {
|
||||
const indexItem = { id: item.id }
|
||||
indexKeyList.forEach(prop => {
|
||||
if (prop == 'indexFieldCodeList') indexItem[prop] = item[prop] ? item[prop].split(',') : []
|
||||
else indexItem[prop] = item[prop]
|
||||
})
|
||||
indexData.push(indexItem)
|
||||
})
|
||||
}
|
||||
if (editInfoData.summaryTopList) {
|
||||
editInfoData.summaryTopList.forEach(item => {
|
||||
const topObj = JSON.parse(item.summaryJson)
|
||||
summaryTopData.push({
|
||||
id: item.id,
|
||||
summaryLabel: item.summaryLabel,
|
||||
summaryShow: item.summaryShow,
|
||||
summarySpan: topObj.summarySpan,
|
||||
summaryControl: topObj.summaryControl,
|
||||
summarySql: JSON.stringify(topObj.summarySql),
|
||||
})
|
||||
})
|
||||
}
|
||||
return { infoData, indexData, summaryTopData }
|
||||
}
|
||||
|
||||
|
||||
|
||||
export const pageOption = {
|
||||
tableOptionColumn, customFormColumn, dataOriginObj,
|
||||
infoApiKey, indexApiKey,
|
||||
tableName_required
|
||||
}
|
||||
export const tableInfoOption = {
|
||||
infoColumn,
|
||||
infoDefaultData,
|
||||
disabledArr,
|
||||
getDefaultMysqlField,
|
||||
formattingInitData,
|
||||
}
|
||||
1872
src/views/lowdesign/tableDesign/index.vue
Normal file
1872
src/views/lowdesign/tableDesign/index.vue
Normal file
File diff suppressed because it is too large
Load Diff
74
src/views/lowdesign/tableView/index.vue
Normal file
74
src/views/lowdesign/tableView/index.vue
Normal file
@@ -0,0 +1,74 @@
|
||||
<template>
|
||||
<ContentWrap>
|
||||
<LowTable ref="lowTableRef" v-if="tableId" :tableId="tableId" :isPermi="isPermi"></LowTable>
|
||||
<div v-else>无权限访问</div>
|
||||
</ContentWrap>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useTagsViewStore } from '@/store/modules/tagsView'
|
||||
import { CACHE_KEY, useCache } from '@/hooks/web/useCache'
|
||||
|
||||
const route = useRoute()
|
||||
const tagsViewStore = useTagsViewStore()
|
||||
const { wsCache } = useCache()
|
||||
const tableId = ref('')
|
||||
const isPermi = ref(false)
|
||||
const timer = ref<any>(null)
|
||||
|
||||
const lowTableRef = ref()
|
||||
|
||||
const setTestTitle = () => {
|
||||
timer.value = setInterval(() => {
|
||||
const tableDescribe = lowTableRef.value?.tableInfo?.tableDescribe
|
||||
if (tableDescribe) {
|
||||
if (timer.value) clearInterval(timer.value)
|
||||
tagsViewStore.visitedViews = tagsViewStore.visitedViews.map((tag) => {
|
||||
if (tag.path == '/low/table/test/' + tableId.value) {
|
||||
if (tag.meta) tag.meta.title = '功能测试:' + tableDescribe
|
||||
}
|
||||
return tag
|
||||
})
|
||||
}
|
||||
}, 100)
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (route.params.id) {
|
||||
//功能测试
|
||||
const menus = wsCache.get(CACHE_KEY.USER).menus
|
||||
const praentArr = route.meta.activeMenu?.split('/').filter((path) => path) || []
|
||||
let isPermission = false
|
||||
if (praentArr.length) {
|
||||
praentArr[0] = '/' + praentArr[0]
|
||||
let findIndex = 0
|
||||
const findPath = (menuList) => {
|
||||
for (const index in menuList) {
|
||||
if (menuList[index].path == praentArr[findIndex]) {
|
||||
if (findIndex == praentArr.length - 1) isPermission = true
|
||||
else findIndex++
|
||||
if (menuList[index].children) findPath(menuList[index].children)
|
||||
if (isPermission) break
|
||||
}
|
||||
}
|
||||
}
|
||||
findPath(menus)
|
||||
}
|
||||
if (isPermission && typeof route.params.id == 'string') tableId.value = route.params.id
|
||||
setTestTitle()
|
||||
} else {
|
||||
const pathList = route.path.split('/')
|
||||
const length = pathList.length - 1
|
||||
if (/\d$/.test(pathList[length])) {
|
||||
tableId.value = pathList[length]
|
||||
isPermi.value = true
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
if (timer.value) clearInterval(timer.value)
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
Reference in New Issue
Block a user